From 7fde2daeed593684120d75de07598154f3ddaf2c Mon Sep 17 00:00:00 2001 From: William Joye Date: Mon, 17 Oct 2016 11:22:52 -0400 Subject: Squashed 'ast/' content from commit 8cff38d git-subtree-dir: ast git-subtree-split: 8cff38d86266dbbd3409f987ca15f85458fc6891 --- .gitignore | 17 + AST_to_do | 23 + COPYING | 674 + COPYING.LESSER | 165 + COPYING.LIB | 481 + Ers.h | 245 + GRF_PAR | 124 + Makefile.am | 801 + Notes | 105 + STARLINK_BRANCHES | 46 + addcopyright | 2 + addlinks | 163 + addversion.in | 10 + ast-for-wcslib/astTester.c | 85 + ast-for-wcslib/loader.c | 35 + ast-for-wcslib/makefile | 31 + ast-for-wcslib/matrixmap.h | 7 + ast-for-wcslib/plot.h | 2 + ast-for-wcslib/wcslib-instructions | 112 + ast.news | 1063 + ast_cpp.in | 11 + ast_dev | 86 + ast_err.msg | 211 + ast_link.in | 463 + ast_link_adam.in | 406 + ast_par.source | 720 + ast_test.c | 115 + ast_tester/README | 33 + ast_tester/a20070718_00010_02_cube.ast | 339 + ast_tester/a20070718_00010_02_cube.fits-wcs | 61 + ast_tester/aitoff.attr | 1 + ast_tester/aitoff.box | 1 + ast_tester/aitoff.head | 13 + ast_tester/aitoff.ps | 1598 + ast_tester/ast_tester | 230 + ast_tester/brad.map | 58 + ast_tester/brad.simp | 49 + ast_tester/car1.attr | 1 + ast_tester/car1.box | 1 + ast_tester/car1.fattr | 1 + ast_tester/car1.head | 32 + ast_tester/car1.ps | 684 + ast_tester/car2.attr | 1 + ast_tester/car2.box | 1 + ast_tester/car2.fattr | 1 + ast_tester/car2.head | 32 + ast_tester/car2.ps | 557 + ast_tester/car3.attr | 1 + ast_tester/car3.box | 1 + ast_tester/car3.head | 8 + ast_tester/car3.ps | 839 + ast_tester/car4.attr | 0 ast_tester/car4.box | 1 + ast_tester/car4.fattr | 1 + ast_tester/car4.head | 38 + ast_tester/car4.ps | 647 + ast_tester/car5.attr | 0 ast_tester/car5.box | 1 + ast_tester/car5.head | 38 + ast_tester/car5.ps | 670 + ast_tester/car6.attr | 0 ast_tester/car6.box | 1 + ast_tester/car6.head | 39 + ast_tester/car6.ps | 1084 + ast_tester/cobe.attr | 1 + ast_tester/cobe.box | 1 + ast_tester/cobe.head | 61 + ast_tester/cobe.ps | 934 + ast_tester/degen1.ast | 318 + ast_tester/degen1.fits-wcs | 40 + ast_tester/doplot | 53 + ast_tester/dss.ast | 178 + ast_tester/dss.dss | 58 + ast_tester/dss.fits-dss | 108 + ast_tester/dss.fits-wcs | 39 + ast_tester/hpx.attr | 1 + ast_tester/hpx.box | 1 + ast_tester/hpx.head | 10 + ast_tester/hpx.ps | 1688 + ast_tester/joye_car_headers/CAR_model.head | 47 + ast_tester/joye_car_headers/CHIPASS_Equ.head | 39 + ast_tester/joye_car_headers/car1.fattr | 1 + ast_tester/joye_car_headers/car1.head | 32 + ast_tester/joye_car_headers/car2.fattr | 1 + ast_tester/joye_car_headers/car2.head | 32 + ast_tester/joye_car_headers/car3.head | 8 + ast_tester/joye_car_headers/car4.fattr | 1 + ast_tester/joye_car_headers/car4.head | 38 + ast_tester/joye_car_headers/car5.head | 38 + ast_tester/joye_car_headers/cmap_3years_GP_D2.head | 162 + ast_tester/joye_car_headers/doit | 17 + ast_tester/joye_car_headers/total_hi.head | 35 + ast_tester/longslit.fits-pc | 18 + ast_tester/longslit.fits-wcs | 22 + ast_tester/makeplot | 47 + ast_tester/maketest | 35 + ast_tester/origin.attr | 1 + ast_tester/origin.box | 1 + ast_tester/origin.head | 18 + ast_tester/origin.ps | 903 + ast_tester/plot3d-test1.ast | 340 + ast_tester/plotter.f | 230 + ast_tester/polco.attr | 1 + ast_tester/polco.box | 1 + ast_tester/polco.head | 86 + ast_tester/polco.ps | 888 + ast_tester/polco2.attr | 1 + ast_tester/polco2.box | 1 + ast_tester/polco2.head | 86 + ast_tester/polco2.ps | 664 + ast_tester/regression.current | 7583 ++++ ast_tester/regression.f | 1515 + ast_tester/regression.out | 7630 ++++ ast_tester/rigby.map | 240 + ast_tester/rigby.simp | 201 + ast_tester/scp.attr | 1 + ast_tester/scp.box | 1 + ast_tester/scp.head | 204 + ast_tester/scp.ps | 641 + ast_tester/serpens.attr | 1 + ast_tester/serpens.box | 1 + ast_tester/serpens.head | 160 + ast_tester/serpens.ps | 705 + ast_tester/simplify.f | 123 + ast_tester/sip.head | 288 + ast_tester/sparse.ast | 392 + ast_tester/specflux.ast | 29 + ast_tester/specflux.attr | 1 + ast_tester/specflux.box | 2 + ast_tester/specflux.head | 28 + ast_tester/specflux.ps | 769 + ast_tester/splittest1.ast | 185 + ast_tester/stcschan-test1-doc3-props.ast | 126 + ast_tester/stcschan-test1-doc3.ast | 272 + ast_tester/testchannel.f | 88 + ast_tester/testcmpmap.f | 149 + ast_tester/testconvert.c | 232 + ast_tester/testfitschan.f | 918 + ast_tester/testfitstable.f | 590 + ast_tester/testflux.f | 353 + ast_tester/testframeset.f | 391 + ast_tester/testkeymap.f | 1350 + ast_tester/testlutmap.f | 177 + ast_tester/testnormmap.f | 76 + ast_tester/testobject.c | 40 + ast_tester/testplot3d.f | 1357 + ast_tester/testpolymap.f | 246 + ast_tester/testrate.f | 344 + ast_tester/testratemap.f | 148 + ast_tester/testrebin.f | 4176 ++ ast_tester/testrebinseq.f | 1580 + ast_tester/testregions.f | 4032 ++ ast_tester/testskyframe.f | 70 + ast_tester/testspecflux.f | 331 + ast_tester/testspecframe.f | 249 + ast_tester/teststc.f | 1857 + ast_tester/teststc_com | 13 + ast_tester/teststc_eg1 | 71 + ast_tester/teststc_eg10 | 18 + ast_tester/teststc_eg2 | 114 + ast_tester/teststc_eg3 | 113 + ast_tester/teststc_eg4 | 55 + ast_tester/teststc_eg5 | 109 + ast_tester/teststc_eg6 | 49 + ast_tester/teststc_eg7 | 52 + ast_tester/teststc_eg8 | 52 + ast_tester/teststc_eg9 | 49 + ast_tester/teststcschan.f | 654 + ast_tester/testswitchmap.f | 792 + ast_tester/testtable.f | 537 + ast_tester/testtime.f | 888 + ast_tester/testtrangrid.f | 276 + ast_tester/testxmlchan.f | 246 + ast_tester/testxmlchan_com | 13 + ast_tester/testzoommap.f | 91 + ast_tester/timeplot.attr | 1 + ast_tester/timeplot.box | 1 + ast_tester/timeplot.head | 34 + ast_tester/timeplot.ps | 687 + ast_tester/timj.ast | 234 + ast_tester/timj.fits-aips | 11 + ast_tester/timj.fits-iraf | 13 + ast_tester/timj.fits-pc | 16 + ast_tester/timj.fits-wcs | 39 + ast_tester/timj.native | 217 + ast_tester/tnx.attr | 1 + ast_tester/tnx.box | 1 + ast_tester/tnx.head | 180 + ast_tester/tnx.ps | 688 + ast_tester/tsc.attr | 1 + ast_tester/tsc.box | 1 + ast_tester/tsc.head | 116 + ast_tester/tsc.ps | 835 + ast_tester/wcsconverter.f | 222 + ast_tester/zpn.attr | 1 + ast_tester/zpn.box | 1 + ast_tester/zpn.head | 136 + ast_tester/zpn.ps | 6407 +++ ast_tester/zpx.attr | 1 + ast_tester/zpx.head | 153 + ast_tester/zpx.ps | 949 + astbad.c | 181 + axis.c | 3444 ++ axis.h | 621 + bootstrap | 134 + box.c | 5062 +++ box.h | 234 + builddocs.in | 144 + buildhyperdocs | 11 + c2f77.c | 125 + c2f77.h | 166 + cexpand | 32 + cexpand.pl | 22 + channel.c | 6458 +++ channel.h | 687 + circle.c | 2900 ++ circle.h | 241 + cminpack/CopyrightMINPACK.txt | 52 + cminpack/README.md | 128 + cminpack/cminpack.h | 370 + cminpack/cminpackP.h | 62 + cminpack/dpmpar.c | 201 + cminpack/enorm.c | 157 + cminpack/lmder.c | 526 + cminpack/lmder1.c | 167 + cminpack/lmpar.c | 338 + cminpack/qrfac.c | 285 + cminpack/qrsolv.c | 218 + cmpframe.c | 10654 +++++ cmpframe.h | 428 + cmpframe.pdf | Bin 0 -> 7432 bytes cmpmap.c | 4739 +++ cmpmap.h | 300 + cmpregion.c | 5127 +++ cmpregion.h | 251 + complex.pdf | Bin 0 -> 15323 bytes component.xml | 44 + component.xml.in | 44 + configure.ac | 215 + devtools/make_simtest | 4 + devtools/simtest.c | 864 + doincludes | 22 + dsbspecframe.c | 3266 ++ dsbspecframe.h | 298 + dssmap.c | 2283 + dssmap.h | 401 + ellipse.c | 3055 ++ ellipse.h | 244 + ems.h | 185 + erfa.h | 72 + erfa/INFO | 19 + erfa/LICENSE | 53 + erfa/Makefile.am | 46 + erfa/README.rst | 77 + erfa/RELEASE.rst | 177 + erfa/a2af.c | 129 + erfa/a2tf.c | 125 + erfa/ab.c | 137 + erfa/af2a.c | 116 + erfa/anp.c | 91 + erfa/anpm.c | 91 + erfa/apcg.c | 181 + erfa/apcg13.c | 184 + erfa/apci.c | 190 + erfa/apci13.c | 202 + erfa/apco.c | 264 + erfa/apco13.c | 287 + erfa/apcs.c | 233 + erfa/apcs13.c | 191 + erfa/aper.c | 162 + erfa/aper13.c | 181 + erfa/apio.c | 213 + erfa/apio13.c | 259 + erfa/atci13.c | 159 + erfa/atciq.c | 154 + erfa/atciqn.c | 191 + erfa/atciqz.c | 153 + erfa/atco13.c | 243 + erfa/atic13.c | 152 + erfa/aticq.c | 199 + erfa/aticqn.c | 237 + erfa/atio13.c | 222 + erfa/atioq.c | 244 + erfa/atoc13.c | 233 + erfa/atoi13.c | 228 + erfa/atoiq.c | 260 + erfa/bi00.c | 125 + erfa/bp00.c | 181 + erfa/bp06.c | 152 + erfa/bpn2xy.c | 109 + erfa/c2i00a.c | 148 + erfa/c2i00b.c | 148 + erfa/c2i06a.c | 145 + erfa/c2ibpn.c | 151 + erfa/c2ixy.c | 140 + erfa/c2ixys.c | 132 + erfa/c2s.c | 105 + erfa/c2t00a.c | 163 + erfa/c2t00b.c | 159 + erfa/c2t06a.c | 161 + erfa/c2tcio.c | 131 + erfa/c2teqx.c | 131 + erfa/c2tpe.c | 176 + erfa/c2txy.c | 168 + erfa/cal2jd.c | 148 + erfa/cp.c | 89 + erfa/cpv.c | 91 + erfa/cr.c | 92 + erfa/d2dtf.c | 245 + erfa/d2tf.c | 169 + erfa/dat.c | 298 + erfa/dtdb.c | 1222 + erfa/dtf2d.c | 212 + erfa/ee00.c | 137 + erfa/ee00a.c | 144 + erfa/ee00b.c | 150 + erfa/ee06a.c | 131 + erfa/eect00.c | 291 + erfa/eform.c | 155 + erfa/eo06a.c | 140 + erfa/eors.c | 117 + erfa/epb.c | 100 + erfa/epb2jd.c | 100 + erfa/epj.c | 102 + erfa/epj2jd.c | 100 + erfa/epv00.c | 2598 ++ erfa/eqeq94.c | 141 + erfa/era00.c | 145 + erfa/erfa.h | 504 + erfa/erfam.h | 208 + erfa/fad03.c | 112 + erfa/fae03.c | 111 + erfa/faf03.c | 115 + erfa/faju03.c | 111 + erfa/fal03.c | 112 + erfa/falp03.c | 112 + erfa/fama03.c | 111 + erfa/fame03.c | 111 + erfa/fane03.c | 108 + erfa/faom03.c | 113 + erfa/fapa03.c | 112 + erfa/fasa03.c | 111 + erfa/faur03.c | 108 + erfa/fave03.c | 111 + erfa/fk52h.c | 152 + erfa/fk5hip.c | 135 + erfa/fk5hz.c | 169 + erfa/fw2m.c | 143 + erfa/fw2xy.c | 130 + erfa/gc2gd.c | 143 + erfa/gc2gde.c | 208 + erfa/gd2gc.c | 142 + erfa/gd2gce.c | 146 + erfa/gmst00.c | 154 + erfa/gmst06.c | 145 + erfa/gmst82.c | 160 + erfa/gst00a.c | 147 + erfa/gst00b.c | 155 + erfa/gst06.c | 149 + erfa/gst06a.c | 140 + erfa/gst94.c | 140 + erfa/h2fk5.c | 157 + erfa/hfk5z.c | 184 + erfa/ir.c | 92 + erfa/jd2cal.c | 164 + erfa/jdcalf.c | 170 + erfa/ld.c | 161 + erfa/ldn.c | 183 + erfa/ldsun.c | 105 + erfa/num00a.c | 130 + erfa/num00b.c | 130 + erfa/num06a.c | 134 + erfa/numat.c | 118 + erfa/nut00a.c | 2056 + erfa/nut00b.c | 381 + erfa/nut06a.c | 162 + erfa/nut80.c | 334 + erfa/nutm80.c | 126 + erfa/obl06.c | 127 + erfa/obl80.c | 127 + erfa/p06e.c | 330 + erfa/p2pv.c | 92 + erfa/p2s.c | 100 + erfa/pap.c | 148 + erfa/pas.c | 105 + erfa/pb06.c | 153 + erfa/pdp.c | 93 + erfa/pfw06.c | 174 + erfa/plan94.c | 523 + erfa/pm.c | 85 + erfa/pmat00.c | 127 + erfa/pmat06.c | 131 + erfa/pmat76.c | 150 + erfa/pmp.c | 94 + erfa/pmpx.c | 153 + erfa/pmsafe.c | 206 + erfa/pn.c | 118 + erfa/pn00.c | 186 + erfa/pn00a.c | 172 + erfa/pn00b.c | 172 + erfa/pn06.c | 196 + erfa/pn06a.c | 162 + erfa/pnm00a.c | 130 + erfa/pnm00b.c | 130 + erfa/pnm06a.c | 133 + erfa/pnm80.c | 135 + erfa/pom00.c | 124 + erfa/ppp.c | 94 + erfa/ppsp.c | 103 + erfa/pr00.c | 151 + erfa/prec76.c | 157 + erfa/pv2p.c | 90 + erfa/pv2s.c | 153 + erfa/pvdpv.c | 111 + erfa/pvm.c | 95 + erfa/pvmpv.c | 96 + erfa/pvppv.c | 96 + erfa/pvstar.c | 216 + erfa/pvtob.c | 162 + erfa/pvu.c | 102 + erfa/pvup.c | 97 + erfa/pvxpv.c | 116 + erfa/pxp.c | 103 + erfa/refco.c | 262 + erfa/rm2v.c | 120 + erfa/rv2m.c | 127 + erfa/rx.c | 119 + erfa/rxp.c | 108 + erfa/rxpv.c | 95 + erfa/rxr.c | 108 + erfa/ry.c | 119 + erfa/rz.c | 119 + erfa/s00.c | 380 + erfa/s00a.c | 152 + erfa/s00b.c | 152 + erfa/s06.c | 377 + erfa/s06a.c | 154 + erfa/s2c.c | 94 + erfa/s2p.c | 97 + erfa/s2pv.c | 112 + erfa/s2xpv.c | 96 + erfa/sepp.c | 114 + erfa/seps.c | 102 + erfa/sp00.c | 127 + erfa/starpm.c | 214 + erfa/starpv.c | 273 + erfa/sxp.c | 93 + erfa/sxpv.c | 94 + erfa/t_erfa_c.c | 9311 ++++ erfa/taitt.c | 119 + erfa/taiut1.c | 120 + erfa/taiutc.c | 168 + erfa/tcbtdb.c | 141 + erfa/tcgtt.c | 118 + erfa/tdbtcb.c | 146 + erfa/tdbtt.c | 130 + erfa/tf2a.c | 116 + erfa/tf2d.c | 116 + erfa/tr.c | 102 + erfa/trxp.c | 102 + erfa/trxpv.c | 102 + erfa/tttai.c | 119 + erfa/tttcg.c | 121 + erfa/tttdb.c | 130 + erfa/ttut1.c | 119 + erfa/ut1tai.c | 120 + erfa/ut1tt.c | 119 + erfa/ut1utc.c | 202 + erfa/utctai.c | 186 + erfa/utcut1.c | 156 + erfa/xy06.c | 2767 ++ erfa/xys00a.c | 142 + erfa/xys00b.c | 142 + erfa/xys06a.c | 142 + erfa/zp.c | 86 + erfa/zpv.c | 88 + erfa/zr.c | 92 + erfa2ast.h | 248 + erfam.h | 61 + err.h | 66 + err_drama.c | 122 + err_ems.c | 108 + err_null.c | 108 + error.c | 1024 + error.h | 327 + f77.h.in | 1096 + fbox.c | 110 + fchannel.c | 473 + fcircle.c | 128 + fcmpframe.c | 104 + fcmpmap.c | 106 + fcmpregion.c | 107 + fdsbspecframe.c | 100 + fdssmap.c | 75 + fellipse.c | 136 + ferror.c | 58 + fetch | 5 + ffitschan.c | 1022 + ffitstable.c | 234 + ffluxframe.c | 104 + fframe.c | 494 + fframeset.c | 214 + fgrismmap.c | 99 + finterval.c | 109 + fintramap.c | 332 + fitschan.c | 42623 +++++++++++++++++++ fitschan.h | 871 + fitstable.c | 3006 ++ fitstable.h | 235 + fkeymap.c | 1400 + flutmap.c | 107 + fluxframe.c | 4490 ++ fluxframe.h | 267 + fmapping.c | 771 + fmathmap.c | 122 + fmatrixmap.c | 110 + fnormmap.c | 101 + fnullregion.c | 104 + fobject.c | 674 + fpcdmap.c | 103 + fpermmap.c | 110 + fplot.c | 683 + fplot3d.c | 107 + fpointlist.c | 117 + fpolygon.c | 226 + fpolymap.c | 140 + fprism.c | 105 + frame.c | 15859 +++++++ frame.f | 71 + frame.h | 1431 + frames.pdf | Bin 0 -> 7196 bytes frameset.c | 13018 ++++++ frameset.h | 709 + frameset.pdf | Bin 0 -> 28443 bytes fratemap.c | 106 + fregion.c | 297 + fronta.pdf | Bin 0 -> 22646 bytes fronta_bw.pdf | Bin 0 -> 22635 bytes frontb.pdf | Bin 0 -> 66281 bytes frontb_bw.pdf | Bin 0 -> 66247 bytes frontc.pdf | Bin 0 -> 43197 bytes frontc_bw.pdf | Bin 0 -> 43186 bytes fsalign.pdf | Bin 0 -> 51413 bytes fsconvert.pdf | Bin 0 -> 18693 bytes fselectormap.c | 115 + fsexample.pdf | Bin 0 -> 17716 bytes fshiftmap.c | 103 + fskyframe.c | 112 + fslamap.c | 120 + fsmerge.pdf | Bin 0 -> 47115 bytes fspecfluxframe.c | 104 + fspecframe.c | 134 + fspecmap.c | 122 + fsphmap.c | 99 + fsremap.pdf | Bin 0 -> 24970 bytes fstc.c | 114 + fstccatalogentrylocation.c | 117 + fstcobsdatalocation.c | 117 + fstcresourceprofile.c | 118 + fstcschan.c | 131 + fstcsearchlocation.c | 117 + fswitchmap.c | 118 + ftable.c | 330 + ftemplateclass.c | 109 + ftimeframe.c | 114 + ftimemap.c | 120 + ftranmap.c | 104 + funitmap.c | 101 + fwcsmap.c | 108 + fwinmap.c | 110 + fxmlchan.c | 130 + fzoommap.c | 103 + getatt | 163 + getnewversion | 15 + globals.c | 251 + globals.h | 243 + grf.h | 110 + grf3d.c | 102 + grf3d.h | 69 + grf3d_pgplot.c | 3196 ++ grf_2.0.c | 101 + grf_3.2.c | 74 + grf_5.6.c | 77 + grf_null.c | 98 + grf_pgplot.c | 1494 + gridplot.pdf | Bin 0 -> 50536 bytes gridplot_bw.pdf | Bin 0 -> 50506 bytes grismmap.c | 2596 ++ grismmap.h | 353 + interval.c | 4686 ++ interval.h | 236 + intramap.c | 2942 ++ intramap.h | 344 + keymap.c | 10768 +++++ keymap.h | 566 + leap-seconds.png | Bin 0 -> 47101 bytes loader.c | 192 + loader.h | 49 + lutmap.c | 2629 ++ lutmap.h | 335 + makeh | 313 + mapping.c | 24616 +++++++++++ mapping.h | 856 + mapping.pdf | Bin 0 -> 8013 bytes mathmap.c | 7421 ++++ mathmap.h | 410 + matrixmap.c | 5501 +++ matrixmap.h | 318 + memory.c | 5483 +++ memory.h | 347 + normmap.c | 1664 + normmap.h | 217 + nullregion.c | 2093 + nullregion.h | 221 + object.c | 8657 ++++ object.f | 70 + object.h.in | 1934 + overgrid.pdf | Bin 0 -> 26963 bytes overgrid_bw.pdf | Bin 0 -> 25865 bytes pal.h | 578 + pal/pal.h | 549 + pal/pal1sofa.h | 142 + pal/palAddet.c | 112 + pal/palAmpqk.c | 128 + pal/palCaldj.c | 99 + pal/palDat.c | 95 + pal/palDe2h.c | 142 + pal/palDeuler.c | 141 + pal/palDh2e.c | 133 + pal/palDjcal.c | 97 + pal/palDmat.c | 182 + pal/palDrange.c | 77 + pal/palDs2tp.c | 127 + pal/palDtp2s.c | 95 + pal/palDtps2c.c | 151 + pal/palDtt.c | 77 + pal/palEcmat.c | 82 + pal/palEqgal.c | 118 + pal/palEtrms.c | 106 + pal/palEvp.c | 110 + pal/palFk45z.c | 186 + pal/palFk524.c | 259 + pal/palFk54z.c | 113 + pal/palGaleq.c | 118 + pal/palGalsup.c | 116 + pal/palGeoc.c | 83 + pal/palMappa.c | 129 + pal/palMapqkz.c | 127 + pal/palOne2One.c | 277 + pal/palPrebn.c | 98 + pal/palPrec.c | 107 + pal/palPrenut.c | 111 + pal/palPvobs.c | 108 + pal/palRvgalc.c | 111 + pal/palRvlg.c | 106 + pal/palRvlsrd.c | 116 + pal/palRvlsrk.c | 116 + pal/palSubet.c | 112 + pal/palSupgal.c | 116 + pal/palmac.h | 136 + pal2ast.h | 134 + palwrap.c | 301 + parallel.pdf | Bin 0 -> 10441 bytes pcdmap.c | 3218 ++ pcdmap.h | 357 + permmap.c | 3199 ++ permmap.h | 322 + pg3d.h | 68 + plot.c | 32074 ++++++++++++++ plot.f | 71 + plot.h | 1417 + plot3d.c | 8587 ++++ plot3d.h | 258 + pointlist.c | 3407 ++ pointlist.h | 239 + pointset.c | 3207 ++ pointset.h | 707 + polygon.c | 7087 +++ polygon.h | 353 + polymap.c | 5506 +++ polymap.h | 354 + prepare_all | 41 + prepare_docs | 16 + prepare_hyperdocs | 37 + prepare_release | 39 + prism.c | 4448 ++ prism.h | 238 + proj.c | 4840 +++ proj.h | 181 + ratemap.c | 2011 + ratemap.h | 276 + region.c | 13226 ++++++ region.h | 515 + selectfc | 29 + selectormap.c | 1838 + selectormap.h | 277 + series.pdf | Bin 0 -> 10149 bytes shiftmap.c | 1617 + shiftmap.h | 290 + simpexamp.pdf | Bin 0 -> 7842 bytes skyaxis.c | 4962 +++ skyaxis.h | 428 + skyframe.c | 12440 ++++++ skyframe.h | 505 + slamap.c | 5007 +++ slamap.h | 330 + specfluxframe.c | 2189 + specfluxframe.h | 215 + specframe.c | 7437 ++++ specframe.h | 430 + specmap.c | 4671 ++ specmap.h | 282 + sphmap.c | 2052 + sphmap.h | 374 + stc.c | 3703 ++ stc.h | 240 + stccatalogentrylocation.c | 804 + stccatalogentrylocation.h | 223 + stcobsdatalocation.c | 1051 + stcobsdatalocation.h | 236 + stcresourceprofile.c | 807 + stcresourceprofile.h | 223 + stcs-ex1.txt | 13 + stcschan-demo1.c | 288 + stcschan-demo2.c | 263 + stcschan-demo3.c | 435 + stcschan-demo4.c | 262 + stcschan-demo5.c | 300 + stcschan.c | 8732 ++++ stcschan.h | 308 + stcsearchlocation.c | 806 + stcsearchlocation.h | 222 + sun210_figures/cmpframe.pdf | Bin 0 -> 7432 bytes sun210_figures/complex.pdf | Bin 0 -> 15323 bytes sun210_figures/frames.pdf | Bin 0 -> 7196 bytes sun210_figures/frameset.pdf | Bin 0 -> 28443 bytes sun210_figures/fronta.pdf | Bin 0 -> 22394 bytes sun210_figures/fronta_bw.pdf | Bin 0 -> 22635 bytes sun210_figures/frontb.pdf | Bin 0 -> 57273 bytes sun210_figures/frontb_bw.pdf | Bin 0 -> 66247 bytes sun210_figures/frontc.pdf | Bin 0 -> 57629 bytes sun210_figures/frontc_bw.pdf | Bin 0 -> 43186 bytes sun210_figures/fsalign.pdf | Bin 0 -> 51413 bytes sun210_figures/fsconvert.pdf | Bin 0 -> 18693 bytes sun210_figures/fsexample.pdf | Bin 0 -> 17716 bytes sun210_figures/fsmerge.pdf | Bin 0 -> 47115 bytes sun210_figures/fsremap.pdf | Bin 0 -> 24970 bytes sun210_figures/gridplot.pdf | Bin 0 -> 50536 bytes sun210_figures/gridplot_bw.pdf | Bin 0 -> 20421 bytes sun210_figures/mapping.pdf | Bin 0 -> 8013 bytes sun210_figures/overgrid.pdf | Bin 0 -> 26963 bytes sun210_figures/overgrid_bw.pdf | Bin 0 -> 25865 bytes sun210_figures/parallel.pdf | Bin 0 -> 10441 bytes sun210_figures/series.pdf | Bin 0 -> 10149 bytes sun210_figures/simpexamp.pdf | Bin 0 -> 7842 bytes sun211_figures/cmpframe.pdf | Bin 0 -> 7432 bytes sun211_figures/complex.pdf | Bin 0 -> 15323 bytes sun211_figures/frames.pdf | Bin 0 -> 7196 bytes sun211_figures/frameset.pdf | Bin 0 -> 28443 bytes sun211_figures/fronta.pdf | Bin 0 -> 22394 bytes sun211_figures/fronta_bw.pdf | Bin 0 -> 22635 bytes sun211_figures/frontb.pdf | Bin 0 -> 57273 bytes sun211_figures/frontb_bw.pdf | Bin 0 -> 66247 bytes sun211_figures/frontc.pdf | Bin 0 -> 57629 bytes sun211_figures/frontc_bw.pdf | Bin 0 -> 43186 bytes sun211_figures/fsalign.pdf | Bin 0 -> 51413 bytes sun211_figures/fsconvert.pdf | Bin 0 -> 18693 bytes sun211_figures/fsexample.pdf | Bin 0 -> 17716 bytes sun211_figures/fsmerge.pdf | Bin 0 -> 47115 bytes sun211_figures/fsremap.pdf | Bin 0 -> 24962 bytes sun211_figures/gridplot.pdf | Bin 0 -> 50536 bytes sun211_figures/gridplot_bw.pdf | Bin 0 -> 20421 bytes sun211_figures/mapping.pdf | Bin 0 -> 8013 bytes sun211_figures/overgrid.pdf | Bin 0 -> 26963 bytes sun211_figures/overgrid_bw.pdf | Bin 0 -> 25865 bytes sun211_figures/parallel.pdf | Bin 0 -> 10441 bytes sun211_figures/series.pdf | Bin 0 -> 10149 bytes sun211_figures/simpexamp.pdf | Bin 0 -> 7842 bytes sun_master.tex | 21676 ++++++++++ switchmap.c | 2875 ++ switchmap.h | 289 + table.c | 5246 +++ table.h | 309 + templateclass.README | 29 + templateclass.c | 1483 + templateclass.h | 216 + timeframe.c | 7481 ++++ timeframe.h | 324 + timemap.c | 5187 +++ timemap.h | 285 + tpn.c | 393 + tranmap.c | 2327 + tranmap.h | 276 + unit.c | 6218 +++ unit.h | 83 + unitmap.c | 1425 + unitmap.h | 288 + version.h.in | 73 + wcsmap.c | 6063 +++ wcsmap.h | 591 + wcsmath.h | 67 + wcstrig.c | 189 + wcstrig.h | 63 + winmap.c | 4389 ++ winmap.h | 300 + xml.c | 7119 ++++ xml.h | 392 + xmlchan.c | 14128 ++++++ xmlchan.h | 300 + zoommap.c | 2074 + zoommap.h | 322 + 811 files changed, 631993 insertions(+) create mode 100644 .gitignore create mode 100644 AST_to_do create mode 100644 COPYING create mode 100644 COPYING.LESSER create mode 100644 COPYING.LIB create mode 100644 Ers.h create mode 100644 GRF_PAR create mode 100644 Makefile.am create mode 100644 Notes create mode 100644 STARLINK_BRANCHES create mode 100755 addcopyright create mode 100755 addlinks create mode 100644 addversion.in create mode 100644 ast-for-wcslib/astTester.c create mode 100644 ast-for-wcslib/loader.c create mode 100644 ast-for-wcslib/makefile create mode 100644 ast-for-wcslib/matrixmap.h create mode 100644 ast-for-wcslib/plot.h create mode 100644 ast-for-wcslib/wcslib-instructions create mode 100644 ast.news create mode 100644 ast_cpp.in create mode 100644 ast_dev create mode 100644 ast_err.msg create mode 100644 ast_link.in create mode 100644 ast_link_adam.in create mode 100644 ast_par.source create mode 100644 ast_test.c create mode 100644 ast_tester/README create mode 100644 ast_tester/a20070718_00010_02_cube.ast create mode 100644 ast_tester/a20070718_00010_02_cube.fits-wcs create mode 100644 ast_tester/aitoff.attr create mode 100644 ast_tester/aitoff.box create mode 100644 ast_tester/aitoff.head create mode 100644 ast_tester/aitoff.ps create mode 100755 ast_tester/ast_tester create mode 100644 ast_tester/brad.map create mode 100644 ast_tester/brad.simp create mode 100644 ast_tester/car1.attr create mode 100644 ast_tester/car1.box create mode 100644 ast_tester/car1.fattr create mode 100644 ast_tester/car1.head create mode 100644 ast_tester/car1.ps create mode 100644 ast_tester/car2.attr create mode 100644 ast_tester/car2.box create mode 100644 ast_tester/car2.fattr create mode 100644 ast_tester/car2.head create mode 100644 ast_tester/car2.ps create mode 100644 ast_tester/car3.attr create mode 100644 ast_tester/car3.box create mode 100644 ast_tester/car3.head create mode 100644 ast_tester/car3.ps create mode 100644 ast_tester/car4.attr create mode 100644 ast_tester/car4.box create mode 100644 ast_tester/car4.fattr create mode 100644 ast_tester/car4.head create mode 100644 ast_tester/car4.ps create mode 100644 ast_tester/car5.attr create mode 100644 ast_tester/car5.box create mode 100644 ast_tester/car5.head create mode 100644 ast_tester/car5.ps create mode 100644 ast_tester/car6.attr create mode 100644 ast_tester/car6.box create mode 100644 ast_tester/car6.head create mode 100644 ast_tester/car6.ps create mode 100644 ast_tester/cobe.attr create mode 100644 ast_tester/cobe.box create mode 100644 ast_tester/cobe.head create mode 100644 ast_tester/cobe.ps create mode 100644 ast_tester/degen1.ast create mode 100644 ast_tester/degen1.fits-wcs create mode 100755 ast_tester/doplot create mode 100644 ast_tester/dss.ast create mode 100644 ast_tester/dss.dss create mode 100644 ast_tester/dss.fits-dss create mode 100644 ast_tester/dss.fits-wcs create mode 100644 ast_tester/hpx.attr create mode 100644 ast_tester/hpx.box create mode 100644 ast_tester/hpx.head create mode 100644 ast_tester/hpx.ps create mode 100644 ast_tester/joye_car_headers/CAR_model.head create mode 100644 ast_tester/joye_car_headers/CHIPASS_Equ.head create mode 100644 ast_tester/joye_car_headers/car1.fattr create mode 100644 ast_tester/joye_car_headers/car1.head create mode 100644 ast_tester/joye_car_headers/car2.fattr create mode 100644 ast_tester/joye_car_headers/car2.head create mode 100644 ast_tester/joye_car_headers/car3.head create mode 100644 ast_tester/joye_car_headers/car4.fattr create mode 100644 ast_tester/joye_car_headers/car4.head create mode 100644 ast_tester/joye_car_headers/car5.head create mode 100644 ast_tester/joye_car_headers/cmap_3years_GP_D2.head create mode 100755 ast_tester/joye_car_headers/doit create mode 100644 ast_tester/joye_car_headers/total_hi.head create mode 100644 ast_tester/longslit.fits-pc create mode 100644 ast_tester/longslit.fits-wcs create mode 100755 ast_tester/makeplot create mode 100755 ast_tester/maketest create mode 100644 ast_tester/origin.attr create mode 100644 ast_tester/origin.box create mode 100644 ast_tester/origin.head create mode 100644 ast_tester/origin.ps create mode 100644 ast_tester/plot3d-test1.ast create mode 100644 ast_tester/plotter.f create mode 100644 ast_tester/polco.attr create mode 100644 ast_tester/polco.box create mode 100644 ast_tester/polco.head create mode 100644 ast_tester/polco.ps create mode 100644 ast_tester/polco2.attr create mode 100644 ast_tester/polco2.box create mode 100644 ast_tester/polco2.head create mode 100644 ast_tester/polco2.ps create mode 100644 ast_tester/regression.current create mode 100644 ast_tester/regression.f create mode 100644 ast_tester/regression.out create mode 100644 ast_tester/rigby.map create mode 100644 ast_tester/rigby.simp create mode 100644 ast_tester/scp.attr create mode 100644 ast_tester/scp.box create mode 100644 ast_tester/scp.head create mode 100644 ast_tester/scp.ps create mode 100644 ast_tester/serpens.attr create mode 100644 ast_tester/serpens.box create mode 100644 ast_tester/serpens.head create mode 100644 ast_tester/serpens.ps create mode 100644 ast_tester/simplify.f create mode 100644 ast_tester/sip.head create mode 100644 ast_tester/sparse.ast create mode 100644 ast_tester/specflux.ast create mode 100644 ast_tester/specflux.attr create mode 100644 ast_tester/specflux.box create mode 100644 ast_tester/specflux.head create mode 100644 ast_tester/specflux.ps create mode 100644 ast_tester/splittest1.ast create mode 100644 ast_tester/stcschan-test1-doc3-props.ast create mode 100644 ast_tester/stcschan-test1-doc3.ast create mode 100644 ast_tester/testchannel.f create mode 100644 ast_tester/testcmpmap.f create mode 100644 ast_tester/testconvert.c create mode 100644 ast_tester/testfitschan.f create mode 100644 ast_tester/testfitstable.f create mode 100644 ast_tester/testflux.f create mode 100644 ast_tester/testframeset.f create mode 100644 ast_tester/testkeymap.f create mode 100644 ast_tester/testlutmap.f create mode 100644 ast_tester/testnormmap.f create mode 100644 ast_tester/testobject.c create mode 100644 ast_tester/testplot3d.f create mode 100644 ast_tester/testpolymap.f create mode 100644 ast_tester/testrate.f create mode 100644 ast_tester/testratemap.f create mode 100644 ast_tester/testrebin.f create mode 100644 ast_tester/testrebinseq.f create mode 100644 ast_tester/testregions.f create mode 100644 ast_tester/testskyframe.f create mode 100644 ast_tester/testspecflux.f create mode 100644 ast_tester/testspecframe.f create mode 100644 ast_tester/teststc.f create mode 100644 ast_tester/teststc_com create mode 100644 ast_tester/teststc_eg1 create mode 100644 ast_tester/teststc_eg10 create mode 100644 ast_tester/teststc_eg2 create mode 100644 ast_tester/teststc_eg3 create mode 100644 ast_tester/teststc_eg4 create mode 100644 ast_tester/teststc_eg5 create mode 100644 ast_tester/teststc_eg6 create mode 100644 ast_tester/teststc_eg7 create mode 100644 ast_tester/teststc_eg8 create mode 100644 ast_tester/teststc_eg9 create mode 100755 ast_tester/teststcschan.f create mode 100644 ast_tester/testswitchmap.f create mode 100644 ast_tester/testtable.f create mode 100644 ast_tester/testtime.f create mode 100644 ast_tester/testtrangrid.f create mode 100644 ast_tester/testxmlchan.f create mode 100644 ast_tester/testxmlchan_com create mode 100644 ast_tester/testzoommap.f create mode 100644 ast_tester/timeplot.attr create mode 100644 ast_tester/timeplot.box create mode 100644 ast_tester/timeplot.head create mode 100644 ast_tester/timeplot.ps create mode 100644 ast_tester/timj.ast create mode 100644 ast_tester/timj.fits-aips create mode 100644 ast_tester/timj.fits-iraf create mode 100644 ast_tester/timj.fits-pc create mode 100644 ast_tester/timj.fits-wcs create mode 100644 ast_tester/timj.native create mode 100644 ast_tester/tnx.attr create mode 100644 ast_tester/tnx.box create mode 100644 ast_tester/tnx.head create mode 100644 ast_tester/tnx.ps create mode 100644 ast_tester/tsc.attr create mode 100644 ast_tester/tsc.box create mode 100644 ast_tester/tsc.head create mode 100644 ast_tester/tsc.ps create mode 100644 ast_tester/wcsconverter.f create mode 100644 ast_tester/zpn.attr create mode 100644 ast_tester/zpn.box create mode 100644 ast_tester/zpn.head create mode 100644 ast_tester/zpn.ps create mode 100644 ast_tester/zpx.attr create mode 100644 ast_tester/zpx.head create mode 100644 ast_tester/zpx.ps create mode 100644 astbad.c create mode 100644 axis.c create mode 100644 axis.h create mode 100755 bootstrap create mode 100644 box.c create mode 100644 box.h create mode 100644 builddocs.in create mode 100755 buildhyperdocs create mode 100644 c2f77.c create mode 100644 c2f77.h create mode 100755 cexpand create mode 100755 cexpand.pl create mode 100644 channel.c create mode 100644 channel.h create mode 100644 circle.c create mode 100644 circle.h create mode 100644 cminpack/CopyrightMINPACK.txt create mode 100644 cminpack/README.md create mode 100644 cminpack/cminpack.h create mode 100644 cminpack/cminpackP.h create mode 100644 cminpack/dpmpar.c create mode 100644 cminpack/enorm.c create mode 100644 cminpack/lmder.c create mode 100644 cminpack/lmder1.c create mode 100644 cminpack/lmpar.c create mode 100644 cminpack/qrfac.c create mode 100644 cminpack/qrsolv.c create mode 100644 cmpframe.c create mode 100644 cmpframe.h create mode 100644 cmpframe.pdf create mode 100644 cmpmap.c create mode 100644 cmpmap.h create mode 100644 cmpregion.c create mode 100644 cmpregion.h create mode 100644 complex.pdf create mode 100644 component.xml create mode 100644 component.xml.in create mode 100644 configure.ac create mode 100644 devtools/make_simtest create mode 100644 devtools/simtest.c create mode 100755 doincludes create mode 100644 dsbspecframe.c create mode 100644 dsbspecframe.h create mode 100644 dssmap.c create mode 100644 dssmap.h create mode 100644 ellipse.c create mode 100644 ellipse.h create mode 100644 ems.h create mode 100644 erfa.h create mode 100644 erfa/INFO create mode 100644 erfa/LICENSE create mode 100644 erfa/Makefile.am create mode 100644 erfa/README.rst create mode 100644 erfa/RELEASE.rst create mode 100644 erfa/a2af.c create mode 100644 erfa/a2tf.c create mode 100644 erfa/ab.c create mode 100644 erfa/af2a.c create mode 100644 erfa/anp.c create mode 100644 erfa/anpm.c create mode 100644 erfa/apcg.c create mode 100644 erfa/apcg13.c create mode 100644 erfa/apci.c create mode 100644 erfa/apci13.c create mode 100644 erfa/apco.c create mode 100644 erfa/apco13.c create mode 100644 erfa/apcs.c create mode 100644 erfa/apcs13.c create mode 100644 erfa/aper.c create mode 100644 erfa/aper13.c create mode 100644 erfa/apio.c create mode 100644 erfa/apio13.c create mode 100644 erfa/atci13.c create mode 100644 erfa/atciq.c create mode 100644 erfa/atciqn.c create mode 100644 erfa/atciqz.c create mode 100644 erfa/atco13.c create mode 100644 erfa/atic13.c create mode 100644 erfa/aticq.c create mode 100644 erfa/aticqn.c create mode 100644 erfa/atio13.c create mode 100644 erfa/atioq.c create mode 100644 erfa/atoc13.c create mode 100644 erfa/atoi13.c create mode 100644 erfa/atoiq.c create mode 100644 erfa/bi00.c create mode 100644 erfa/bp00.c create mode 100644 erfa/bp06.c create mode 100644 erfa/bpn2xy.c create mode 100644 erfa/c2i00a.c create mode 100644 erfa/c2i00b.c create mode 100644 erfa/c2i06a.c create mode 100644 erfa/c2ibpn.c create mode 100644 erfa/c2ixy.c create mode 100644 erfa/c2ixys.c create mode 100644 erfa/c2s.c create mode 100644 erfa/c2t00a.c create mode 100644 erfa/c2t00b.c create mode 100644 erfa/c2t06a.c create mode 100644 erfa/c2tcio.c create mode 100644 erfa/c2teqx.c create mode 100644 erfa/c2tpe.c create mode 100644 erfa/c2txy.c create mode 100644 erfa/cal2jd.c create mode 100644 erfa/cp.c create mode 100644 erfa/cpv.c create mode 100644 erfa/cr.c create mode 100644 erfa/d2dtf.c create mode 100644 erfa/d2tf.c create mode 100644 erfa/dat.c create mode 100644 erfa/dtdb.c create mode 100644 erfa/dtf2d.c create mode 100644 erfa/ee00.c create mode 100644 erfa/ee00a.c create mode 100644 erfa/ee00b.c create mode 100644 erfa/ee06a.c create mode 100644 erfa/eect00.c create mode 100644 erfa/eform.c create mode 100644 erfa/eo06a.c create mode 100644 erfa/eors.c create mode 100644 erfa/epb.c create mode 100644 erfa/epb2jd.c create mode 100644 erfa/epj.c create mode 100644 erfa/epj2jd.c create mode 100644 erfa/epv00.c create mode 100644 erfa/eqeq94.c create mode 100644 erfa/era00.c create mode 100644 erfa/erfa.h create mode 100644 erfa/erfam.h create mode 100644 erfa/fad03.c create mode 100644 erfa/fae03.c create mode 100644 erfa/faf03.c create mode 100644 erfa/faju03.c create mode 100644 erfa/fal03.c create mode 100644 erfa/falp03.c create mode 100644 erfa/fama03.c create mode 100644 erfa/fame03.c create mode 100644 erfa/fane03.c create mode 100644 erfa/faom03.c create mode 100644 erfa/fapa03.c create mode 100644 erfa/fasa03.c create mode 100644 erfa/faur03.c create mode 100644 erfa/fave03.c create mode 100644 erfa/fk52h.c create mode 100644 erfa/fk5hip.c create mode 100644 erfa/fk5hz.c create mode 100644 erfa/fw2m.c create mode 100644 erfa/fw2xy.c create mode 100644 erfa/gc2gd.c create mode 100644 erfa/gc2gde.c create mode 100644 erfa/gd2gc.c create mode 100644 erfa/gd2gce.c create mode 100644 erfa/gmst00.c create mode 100644 erfa/gmst06.c create mode 100644 erfa/gmst82.c create mode 100644 erfa/gst00a.c create mode 100644 erfa/gst00b.c create mode 100644 erfa/gst06.c create mode 100644 erfa/gst06a.c create mode 100644 erfa/gst94.c create mode 100644 erfa/h2fk5.c create mode 100644 erfa/hfk5z.c create mode 100644 erfa/ir.c create mode 100644 erfa/jd2cal.c create mode 100644 erfa/jdcalf.c create mode 100644 erfa/ld.c create mode 100644 erfa/ldn.c create mode 100644 erfa/ldsun.c create mode 100644 erfa/num00a.c create mode 100644 erfa/num00b.c create mode 100644 erfa/num06a.c create mode 100644 erfa/numat.c create mode 100644 erfa/nut00a.c create mode 100644 erfa/nut00b.c create mode 100644 erfa/nut06a.c create mode 100644 erfa/nut80.c create mode 100644 erfa/nutm80.c create mode 100644 erfa/obl06.c create mode 100644 erfa/obl80.c create mode 100644 erfa/p06e.c create mode 100644 erfa/p2pv.c create mode 100644 erfa/p2s.c create mode 100644 erfa/pap.c create mode 100644 erfa/pas.c create mode 100644 erfa/pb06.c create mode 100644 erfa/pdp.c create mode 100644 erfa/pfw06.c create mode 100644 erfa/plan94.c create mode 100644 erfa/pm.c create mode 100644 erfa/pmat00.c create mode 100644 erfa/pmat06.c create mode 100644 erfa/pmat76.c create mode 100644 erfa/pmp.c create mode 100644 erfa/pmpx.c create mode 100644 erfa/pmsafe.c create mode 100644 erfa/pn.c create mode 100644 erfa/pn00.c create mode 100644 erfa/pn00a.c create mode 100644 erfa/pn00b.c create mode 100644 erfa/pn06.c create mode 100644 erfa/pn06a.c create mode 100644 erfa/pnm00a.c create mode 100644 erfa/pnm00b.c create mode 100644 erfa/pnm06a.c create mode 100644 erfa/pnm80.c create mode 100644 erfa/pom00.c create mode 100644 erfa/ppp.c create mode 100644 erfa/ppsp.c create mode 100644 erfa/pr00.c create mode 100644 erfa/prec76.c create mode 100644 erfa/pv2p.c create mode 100644 erfa/pv2s.c create mode 100644 erfa/pvdpv.c create mode 100644 erfa/pvm.c create mode 100644 erfa/pvmpv.c create mode 100644 erfa/pvppv.c create mode 100644 erfa/pvstar.c create mode 100644 erfa/pvtob.c create mode 100644 erfa/pvu.c create mode 100644 erfa/pvup.c create mode 100644 erfa/pvxpv.c create mode 100644 erfa/pxp.c create mode 100644 erfa/refco.c create mode 100644 erfa/rm2v.c create mode 100644 erfa/rv2m.c create mode 100644 erfa/rx.c create mode 100644 erfa/rxp.c create mode 100644 erfa/rxpv.c create mode 100644 erfa/rxr.c create mode 100644 erfa/ry.c create mode 100644 erfa/rz.c create mode 100644 erfa/s00.c create mode 100644 erfa/s00a.c create mode 100644 erfa/s00b.c create mode 100644 erfa/s06.c create mode 100644 erfa/s06a.c create mode 100644 erfa/s2c.c create mode 100644 erfa/s2p.c create mode 100644 erfa/s2pv.c create mode 100644 erfa/s2xpv.c create mode 100644 erfa/sepp.c create mode 100644 erfa/seps.c create mode 100644 erfa/sp00.c create mode 100644 erfa/starpm.c create mode 100644 erfa/starpv.c create mode 100644 erfa/sxp.c create mode 100644 erfa/sxpv.c create mode 100644 erfa/t_erfa_c.c create mode 100644 erfa/taitt.c create mode 100644 erfa/taiut1.c create mode 100644 erfa/taiutc.c create mode 100644 erfa/tcbtdb.c create mode 100644 erfa/tcgtt.c create mode 100644 erfa/tdbtcb.c create mode 100644 erfa/tdbtt.c create mode 100644 erfa/tf2a.c create mode 100644 erfa/tf2d.c create mode 100644 erfa/tr.c create mode 100644 erfa/trxp.c create mode 100644 erfa/trxpv.c create mode 100644 erfa/tttai.c create mode 100644 erfa/tttcg.c create mode 100644 erfa/tttdb.c create mode 100644 erfa/ttut1.c create mode 100644 erfa/ut1tai.c create mode 100644 erfa/ut1tt.c create mode 100644 erfa/ut1utc.c create mode 100644 erfa/utctai.c create mode 100644 erfa/utcut1.c create mode 100644 erfa/xy06.c create mode 100644 erfa/xys00a.c create mode 100644 erfa/xys00b.c create mode 100644 erfa/xys06a.c create mode 100644 erfa/zp.c create mode 100644 erfa/zpv.c create mode 100644 erfa/zr.c create mode 100644 erfa2ast.h create mode 100644 erfam.h create mode 100644 err.h create mode 100644 err_drama.c create mode 100644 err_ems.c create mode 100644 err_null.c create mode 100644 error.c create mode 100644 error.h create mode 100644 f77.h.in create mode 100644 fbox.c create mode 100644 fchannel.c create mode 100644 fcircle.c create mode 100644 fcmpframe.c create mode 100644 fcmpmap.c create mode 100644 fcmpregion.c create mode 100644 fdsbspecframe.c create mode 100644 fdssmap.c create mode 100644 fellipse.c create mode 100644 ferror.c create mode 100755 fetch create mode 100644 ffitschan.c create mode 100644 ffitstable.c create mode 100644 ffluxframe.c create mode 100644 fframe.c create mode 100644 fframeset.c create mode 100644 fgrismmap.c create mode 100644 finterval.c create mode 100644 fintramap.c create mode 100644 fitschan.c create mode 100644 fitschan.h create mode 100644 fitstable.c create mode 100644 fitstable.h create mode 100644 fkeymap.c create mode 100644 flutmap.c create mode 100644 fluxframe.c create mode 100644 fluxframe.h create mode 100644 fmapping.c create mode 100644 fmathmap.c create mode 100644 fmatrixmap.c create mode 100644 fnormmap.c create mode 100644 fnullregion.c create mode 100644 fobject.c create mode 100644 fpcdmap.c create mode 100644 fpermmap.c create mode 100644 fplot.c create mode 100644 fplot3d.c create mode 100644 fpointlist.c create mode 100644 fpolygon.c create mode 100644 fpolymap.c create mode 100644 fprism.c create mode 100644 frame.c create mode 100644 frame.f create mode 100644 frame.h create mode 100644 frames.pdf create mode 100644 frameset.c create mode 100644 frameset.h create mode 100644 frameset.pdf create mode 100644 fratemap.c create mode 100644 fregion.c create mode 100644 fronta.pdf create mode 100644 fronta_bw.pdf create mode 100644 frontb.pdf create mode 100644 frontb_bw.pdf create mode 100644 frontc.pdf create mode 100644 frontc_bw.pdf create mode 100644 fsalign.pdf create mode 100644 fsconvert.pdf create mode 100644 fselectormap.c create mode 100644 fsexample.pdf create mode 100644 fshiftmap.c create mode 100644 fskyframe.c create mode 100644 fslamap.c create mode 100644 fsmerge.pdf create mode 100644 fspecfluxframe.c create mode 100644 fspecframe.c create mode 100644 fspecmap.c create mode 100644 fsphmap.c create mode 100644 fsremap.pdf create mode 100644 fstc.c create mode 100644 fstccatalogentrylocation.c create mode 100644 fstcobsdatalocation.c create mode 100644 fstcresourceprofile.c create mode 100644 fstcschan.c create mode 100644 fstcsearchlocation.c create mode 100644 fswitchmap.c create mode 100644 ftable.c create mode 100644 ftemplateclass.c create mode 100644 ftimeframe.c create mode 100644 ftimemap.c create mode 100644 ftranmap.c create mode 100644 funitmap.c create mode 100644 fwcsmap.c create mode 100644 fwinmap.c create mode 100644 fxmlchan.c create mode 100644 fzoommap.c create mode 100755 getatt create mode 100644 getnewversion create mode 100644 globals.c create mode 100644 globals.h create mode 100644 grf.h create mode 100644 grf3d.c create mode 100644 grf3d.h create mode 100644 grf3d_pgplot.c create mode 100644 grf_2.0.c create mode 100644 grf_3.2.c create mode 100644 grf_5.6.c create mode 100644 grf_null.c create mode 100644 grf_pgplot.c create mode 100644 gridplot.pdf create mode 100644 gridplot_bw.pdf create mode 100644 grismmap.c create mode 100644 grismmap.h create mode 100644 interval.c create mode 100644 interval.h create mode 100644 intramap.c create mode 100644 intramap.h create mode 100644 keymap.c create mode 100644 keymap.h create mode 100644 leap-seconds.png create mode 100644 loader.c create mode 100644 loader.h create mode 100644 lutmap.c create mode 100644 lutmap.h create mode 100644 makeh create mode 100644 mapping.c create mode 100644 mapping.h create mode 100644 mapping.pdf create mode 100644 mathmap.c create mode 100644 mathmap.h create mode 100644 matrixmap.c create mode 100644 matrixmap.h create mode 100644 memory.c create mode 100644 memory.h create mode 100644 normmap.c create mode 100644 normmap.h create mode 100644 nullregion.c create mode 100644 nullregion.h create mode 100644 object.c create mode 100644 object.f create mode 100644 object.h.in create mode 100644 overgrid.pdf create mode 100644 overgrid_bw.pdf create mode 100644 pal.h create mode 100644 pal/pal.h create mode 100644 pal/pal1sofa.h create mode 100644 pal/palAddet.c create mode 100644 pal/palAmpqk.c create mode 100644 pal/palCaldj.c create mode 100644 pal/palDat.c create mode 100644 pal/palDe2h.c create mode 100644 pal/palDeuler.c create mode 100644 pal/palDh2e.c create mode 100644 pal/palDjcal.c create mode 100644 pal/palDmat.c create mode 100644 pal/palDrange.c create mode 100644 pal/palDs2tp.c create mode 100644 pal/palDtp2s.c create mode 100644 pal/palDtps2c.c create mode 100644 pal/palDtt.c create mode 100644 pal/palEcmat.c create mode 100644 pal/palEqgal.c create mode 100644 pal/palEtrms.c create mode 100644 pal/palEvp.c create mode 100644 pal/palFk45z.c create mode 100644 pal/palFk524.c create mode 100644 pal/palFk54z.c create mode 100644 pal/palGaleq.c create mode 100644 pal/palGalsup.c create mode 100644 pal/palGeoc.c create mode 100644 pal/palMappa.c create mode 100644 pal/palMapqkz.c create mode 100644 pal/palOne2One.c create mode 100644 pal/palPrebn.c create mode 100644 pal/palPrec.c create mode 100644 pal/palPrenut.c create mode 100644 pal/palPvobs.c create mode 100644 pal/palRvgalc.c create mode 100644 pal/palRvlg.c create mode 100644 pal/palRvlsrd.c create mode 100644 pal/palRvlsrk.c create mode 100644 pal/palSubet.c create mode 100644 pal/palSupgal.c create mode 100644 pal/palmac.h create mode 100644 pal2ast.h create mode 100644 palwrap.c create mode 100644 parallel.pdf create mode 100644 pcdmap.c create mode 100644 pcdmap.h create mode 100644 permmap.c create mode 100644 permmap.h create mode 100644 pg3d.h create mode 100644 plot.c create mode 100644 plot.f create mode 100644 plot.h create mode 100644 plot3d.c create mode 100644 plot3d.h create mode 100644 pointlist.c create mode 100644 pointlist.h create mode 100644 pointset.c create mode 100644 pointset.h create mode 100644 polygon.c create mode 100644 polygon.h create mode 100644 polymap.c create mode 100644 polymap.h create mode 100755 prepare_all create mode 100755 prepare_docs create mode 100755 prepare_hyperdocs create mode 100755 prepare_release create mode 100644 prism.c create mode 100644 prism.h create mode 100644 proj.c create mode 100644 proj.h create mode 100644 ratemap.c create mode 100644 ratemap.h create mode 100644 region.c create mode 100644 region.h create mode 100755 selectfc create mode 100644 selectormap.c create mode 100644 selectormap.h create mode 100644 series.pdf create mode 100644 shiftmap.c create mode 100644 shiftmap.h create mode 100644 simpexamp.pdf create mode 100644 skyaxis.c create mode 100644 skyaxis.h create mode 100644 skyframe.c create mode 100644 skyframe.h create mode 100644 slamap.c create mode 100644 slamap.h create mode 100644 specfluxframe.c create mode 100644 specfluxframe.h create mode 100644 specframe.c create mode 100644 specframe.h create mode 100644 specmap.c create mode 100644 specmap.h create mode 100644 sphmap.c create mode 100644 sphmap.h create mode 100644 stc.c create mode 100644 stc.h create mode 100644 stccatalogentrylocation.c create mode 100644 stccatalogentrylocation.h create mode 100644 stcobsdatalocation.c create mode 100644 stcobsdatalocation.h create mode 100644 stcresourceprofile.c create mode 100644 stcresourceprofile.h create mode 100644 stcs-ex1.txt create mode 100644 stcschan-demo1.c create mode 100644 stcschan-demo2.c create mode 100644 stcschan-demo3.c create mode 100644 stcschan-demo4.c create mode 100644 stcschan-demo5.c create mode 100644 stcschan.c create mode 100644 stcschan.h create mode 100644 stcsearchlocation.c create mode 100644 stcsearchlocation.h create mode 100644 sun210_figures/cmpframe.pdf create mode 100644 sun210_figures/complex.pdf create mode 100644 sun210_figures/frames.pdf create mode 100644 sun210_figures/frameset.pdf create mode 100644 sun210_figures/fronta.pdf create mode 100644 sun210_figures/fronta_bw.pdf create mode 100644 sun210_figures/frontb.pdf create mode 100644 sun210_figures/frontb_bw.pdf create mode 100644 sun210_figures/frontc.pdf create mode 100644 sun210_figures/frontc_bw.pdf create mode 100644 sun210_figures/fsalign.pdf create mode 100644 sun210_figures/fsconvert.pdf create mode 100644 sun210_figures/fsexample.pdf create mode 100644 sun210_figures/fsmerge.pdf create mode 100644 sun210_figures/fsremap.pdf create mode 100644 sun210_figures/gridplot.pdf create mode 100644 sun210_figures/gridplot_bw.pdf create mode 100644 sun210_figures/mapping.pdf create mode 100644 sun210_figures/overgrid.pdf create mode 100644 sun210_figures/overgrid_bw.pdf create mode 100644 sun210_figures/parallel.pdf create mode 100644 sun210_figures/series.pdf create mode 100644 sun210_figures/simpexamp.pdf create mode 100644 sun211_figures/cmpframe.pdf create mode 100644 sun211_figures/complex.pdf create mode 100644 sun211_figures/frames.pdf create mode 100644 sun211_figures/frameset.pdf create mode 100644 sun211_figures/fronta.pdf create mode 100644 sun211_figures/fronta_bw.pdf create mode 100644 sun211_figures/frontb.pdf create mode 100644 sun211_figures/frontb_bw.pdf create mode 100644 sun211_figures/frontc.pdf create mode 100644 sun211_figures/frontc_bw.pdf create mode 100644 sun211_figures/fsalign.pdf create mode 100644 sun211_figures/fsconvert.pdf create mode 100644 sun211_figures/fsexample.pdf create mode 100644 sun211_figures/fsmerge.pdf create mode 100644 sun211_figures/fsremap.pdf create mode 100644 sun211_figures/gridplot.pdf create mode 100644 sun211_figures/gridplot_bw.pdf create mode 100644 sun211_figures/mapping.pdf create mode 100644 sun211_figures/overgrid.pdf create mode 100644 sun211_figures/overgrid_bw.pdf create mode 100644 sun211_figures/parallel.pdf create mode 100644 sun211_figures/series.pdf create mode 100644 sun211_figures/simpexamp.pdf create mode 100644 sun_master.tex create mode 100644 switchmap.c create mode 100644 switchmap.h create mode 100644 table.c create mode 100644 table.h create mode 100644 templateclass.README create mode 100644 templateclass.c create mode 100644 templateclass.h create mode 100644 timeframe.c create mode 100644 timeframe.h create mode 100644 timemap.c create mode 100644 timemap.h create mode 100644 tpn.c create mode 100644 tranmap.c create mode 100644 tranmap.h create mode 100644 unit.c create mode 100644 unit.h create mode 100644 unitmap.c create mode 100644 unitmap.h create mode 100644 version.h.in create mode 100644 wcsmap.c create mode 100644 wcsmap.h create mode 100644 wcsmath.h create mode 100644 wcstrig.c create mode 100644 wcstrig.h create mode 100644 winmap.c create mode 100644 winmap.h create mode 100644 xml.c create mode 100644 xml.h create mode 100644 xmlchan.c create mode 100644 xmlchan.h create mode 100644 zoommap.c create mode 100644 zoommap.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..437071a --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +/AST_PAR +/addversion +/ast.h +/astbad +/ast_cpp +/ast_link +/ast_link_adam +/ast_test +/builddocs +/f77.h +/object.h +/sun210.tex +/sun210.pdf +/sun211.tex +/sun211.pdf +/version.h +/*.trs diff --git a/AST_to_do b/AST_to_do new file mode 100644 index 0000000..a1faf93 --- /dev/null +++ b/AST_to_do @@ -0,0 +1,23 @@ +Things to do in AST (not necesarily priority order): + +Added 17/1/05: + +Done 17/1/05 - Add Prism class to CVS +Done 17/1/05 - Correct void * arithmetic in keymap.c +- Changing version in configure.ac and then doing make does not result in + version.h being re-made. +Done 29/6/05 - Complete STC classes +Done 29/6/05 - Integrate TimeFrame +- Move observer position attributes into Frame class +- Extend FluxFrame to describe magnitudes and antenna temperature. +- Write SOAP service to show off STC functionality +- Add AzEl system to SkyFrame +- Flux conservation in Mapping resample routines +- Write proper tutorial documentation for new classes (regions, STC, fluxframe, etc). +- Add support for FITS-WCS paper III "-TAB" algorithm code to FitsChan +- Add support for HEALPIX ?? (e.g. see http://www.spacebanter.com/showthread.php?p=336715#post336715) +- Pure Java version +- Monitor progresss on FITS-WCS paper IV (and eventually paper V) +- Extend ATOOLS to cover Regions, TimeFrame & STC +- Speed up the STC facilities of XmlCHan and Stc (astSimplify is particularly + slow) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..eb685a5 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Ers.h b/Ers.h new file mode 100644 index 0000000..a66fa82 --- /dev/null +++ b/Ers.h @@ -0,0 +1,245 @@ +#ifndef ERSINC +#define ERSINC +#ifdef __cplusplus +extern "C" { +#endif + + +/* E r s . h + + * Module name: + Ers.h + + * Function: + Function header for the Ers routines + + * Description: + Should be included by all files using the Ers routines. + + * Language: + C + + * Support: Tony Farrell, AAO + + * Copyright (c) Anglo-Australian Telescope Board, 1995. + Not to be used for commercial purposes without AATB permission. + + * @(#) $Id: Ers.h,v 1.3 2005/05/17 22:21:19 rkackley Exp $ + + + * History: + 04-Aug-1992 - TJF - Original version + 25-Sep-1992 - TJF - Update comments + 06-Oct-1992 - TJF - Rewrite for complete Ers package. + 04-Aug-1993 - TJF - maxsize argument to ErsSPrintf needs a type + 28-Sep-1993 - TJF - Use GNUC attribute to flag the ers calls + as printf style. Use drama.h for configuration + stuff. + + 29-Sep-1993 - TJF - Add Sccs id + 06-Mar-1994 - TJF - Add Task Id stuff. + 05-Feb-1995 - TJF - Add BROADCAST flag + 06-Aug-1996 - TJF - Add const to strings arguments of ErsVSPrintf + 30-May-2001 - TJF - Add ErsSetLogRoutine. + 15-Jun-2001 - TJF - Add ErsGetTaskId. + {@change entry@} + + + */ + +#ifdef ERS_STANDALONE +/* + * DRAMA macros and types used by Ers. They are defined here when we + * are building ers standalone. + */ +#define DVOID void +#define DVOIDP void * +#define DPUBLIC extern +#define DPRIVATE static +#define DCONSTV const +#define DCONSTR const +#define STATUS__OK 0 +#define DPROTOTYPES_OK +#define DFLOAT_OK +#define DCONST_I +typedef long int StatusType; +#define StatusOkP(_value_) (*(_value_) == STATUS__OK) +#else +/* + * Include the drama.h file for configuration macros. + */ + +#include "drama.h" + +#include "status.h" /* For StatusType etc */ +#endif + +/* + * Get around problems in Sparc include files, they are not ANSI compatible + */ +#if defined(__sparc__) && !defined(sparc) +#define sparc 1 +#endif + +/* + * Floating point stuff. Only used in ErsVSPrintf. + */ +#ifdef DFLOAT_OK +/* + * These values taken from bsd floatio.h + */ +# define ERS_MAXEXP 308 +# define ERS_MAXFRACT 39 +#endif + +/* + * Constants + */ + +#define ERS_C_LEN 200 /* Maximum length of reported messages */ +#define ERS_C_MAXMSG 30 /* Maximum number of reported messages */ + +#define ERS_M_NOFMT (1<<0) /* Message flag masks */ +#define ERS_M_HIGHLIGHT (1<<1) +#define ERS_M_BELL (1<<2) +#define ERS_M_ALARM (1<<3) +#define ERS_M_BROADCAST (1<<4) + + +/* + * This structure is used to store details of a message + */ +typedef struct { + StatusType mesStatus; /* Status of message */ + unsigned int context; /* Context message was written at */ + int flags; /* Message flags */ + char message[ERS_C_LEN]; /* The formated message */ + } ErsMessageType; + +typedef DVOIDP ErsTaskIdType; + +#ifdef DPROTOTYPES_OK +/* + * This type is that required for log routines - called on each call to + * ErsRep with details of a single message. + * + * The argument "logArg" is a user value supplied when ErsStart is called. + * It enables the user to pass any appropriate value to the log routine. + */ +typedef DVOID (*ErsLogRoutineType)( + DVOIDP logArg, /* Supplied to ErsStart */ + DCONSTV ErsMessageType * message,/* The message */ + StatusType * status); +/* + * The type is that requried for the output routine - called to output + * the messages to the user. An array of message may be output by one + * call, with count being the number of message to output. + * + * The argument "outArg" is a user value supplied when ErsStart is called. + * It enables the user to pass any appropriate value to the log routine. + */ +typedef DVOID (*ErsOutRoutineType)( + DVOIDP outArg, /* Supplied to ErsStart */ + unsigned int count, /* Number of messages */ + DCONSTV ErsMessageType messages[],/* Array of messages */ + StatusType * status); + + +/* + * Function prototypes. + * + * + * We can't define these prorotype in the Ers main module unless we have + * stdarg.h. + */ +#if !defined(ERS_MAIN) || defined(DSTDARG__OK) + DPUBLIC DVOID ErsRep(DCONSTV int flags, StatusType * status, + DCONSTV char * string , ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))) +#endif + ; + DPUBLIC DVOID ErsOut(DCONSTV int flags, StatusType * status, + DCONSTV char * string, ...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))) +#endif + ; + DPUBLIC int ErsSPrintf(DCONSTV int maxLength, + char *string, + DCONSTV char * fmt,...) +#ifdef __GNUC__ + __attribute__ ((format (printf, 3, 4))) +#endif + ; + +#endif /* DSTDARG_OK */ + +DPUBLIC ErsTaskIdType ErsStart( + ErsOutRoutineType outRoutine, + DVOIDP outArg, + ErsLogRoutineType logRoutine, + DVOIDP logArg, + StatusType * status); +DPUBLIC DVOID ErsStop(StatusType * status); +DPUBLIC DVOID ErsPush(void); +DPUBLIC DVOID ErsAnnul(StatusType * status); +DPUBLIC DVOID ErsFlush(StatusType * status); +DPUBLIC DVOID ErsClear(StatusType * status); +DPUBLIC DVOID ErsPop(void); +DPUBLIC DVOID ErsSetLogRoutine( + ErsLogRoutineType logRoutine, + DVOIDP logArg, + ErsLogRoutineType *oldLogRoutine, + DVOIDP *oldLogArg, + StatusType * status); + +DPUBLIC ErsTaskIdType ErsGetTaskId(StatusType *status); +DPUBLIC DVOID ErsEnableTask(ErsTaskIdType TaskId, + ErsTaskIdType * SavedTaskId); +DPUBLIC DVOID ErsRestoreTask(ErsTaskIdType TaskId); + + +#ifdef DSTDARG_OK +# include +#else +# include +#endif +DPUBLIC int ErsVSPrintf( + int maxLength, + char *string , + DCONSTV char * fmt0, + va_list ap); +#else +/* Don't use prorotypes */ +typedef DVOID (*ErsLogRoutineType)(); +typedef DVOID (*ErsOutRoutineType)(); + +DPUBLIC DVOID ErsRep(); +DPUBLIC DVOID ErsOut(); + +DPUBLIC DVOID ErsStart(); +DPUBLIC DVOID ErsStop(); +DPUBLIC DVOID ErsPush(); +DPUBLIC DVOID ErsPop(); +DPUBLIC DVOID ErsAnnul(); +DPUBLIC DVOID ErsFlush(); +DPUBLIC DVOID ErsClear(); +DPUBLIC DVOID ErsSetLogRoutine(); +DPUBLIC ErsTaskIdType ErsGetTaskId(); + +DPUBLIC int ErsVSPrintf(); +DPUBLIC int ErsSPrintf(); + +DPUBLIC DVOID ErsEnableTask(); +DPUBLIC DVOID ErsRestoreTask(); + + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/GRF_PAR b/GRF_PAR new file mode 100644 index 0000000..0c1d9aa --- /dev/null +++ b/GRF_PAR @@ -0,0 +1,124 @@ +*+ +* Name: +* GRF_PAR + +* Purpose: +* Define the constants needed to implement Fortran GRF routines. + +* Language: +* Fortran 77 + +* Type of Module: +* Include file. + +* Description: +* This file contains definitions which are required by Fortran 77 +* programs which implement their own grf routines (routines for +* drawing graphics primitive used by the AST Plot class). + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 13-JUN-2001 (DSB): +* Original version. +*- + +* Values identifying different graphics attributes. + INTEGER GRF__STYLE + PARAMETER ( GRF__STYLE = 0 ) + + INTEGER GRF__WIDTH + PARAMETER ( GRF__WIDTH = 1 ) + + INTEGER GRF__SIZE + PARAMETER ( GRF__SIZE = 2 ) + + INTEGER GRF__FONT + PARAMETER ( GRF__FONT = 3 ) + + INTEGER GRF__COLOUR + PARAMETER ( GRF__COLOUR = 4 ) + +* Values identifying different graphics primatives. + INTEGER GRF__TEXT + PARAMETER ( GRF__TEXT = 0 ) + + INTEGER GRF__LINE + PARAMETER ( GRF__LINE = 1 ) + + INTEGER GRF__MARK + PARAMETER ( GRF__MARK = 2 ) + +* The number of different graphics attributes. + INTEGER GRF__NATTR + PARAMETER ( GRF__NATTR = 5 ) + +* Values identifying capabilities. + INTEGER GRF__ESC + PARAMETER ( GRF__ESC = 0 ) + + INTEGER GRF__MJUST + PARAMETER ( GRF__MJUST = 1 ) + + INTEGER GRF__SCALES + PARAMETER ( GRF__SCALES = 2 ) + +* Values identifying types of graphics escape sequence + INTEGER GRF__ESPER + PARAMETER ( GRF__ESPER = 1 ) + + INTEGER GRF__ESSUP + PARAMETER ( GRF__ESSUP = 2 ) + + INTEGER GRF__ESSUB + PARAMETER ( GRF__ESSUB = 3 ) + + INTEGER GRF__ESGAP + PARAMETER ( GRF__ESGAP = 4 ) + + INTEGER GRF__ESBAC + PARAMETER ( GRF__ESBAC = 5 ) + + INTEGER GRF__ESSIZ + PARAMETER ( GRF__ESSIZ = 6 ) + + INTEGER GRF__ESWID + PARAMETER ( GRF__ESWID = 7 ) + + INTEGER GRF__ESFON + PARAMETER ( GRF__ESFON = 8 ) + + INTEGER GRF__ESCOL + PARAMETER ( GRF__ESCOL = 9 ) + + INTEGER GRF__ESSTY + PARAMETER ( GRF__ESSTY = 10 ) + + INTEGER GRF__ESPOP + PARAMETER ( GRF__ESPOP = 11 ) + + INTEGER GRF__ESPSH + PARAMETER ( GRF__ESPSH = 12 ) + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3b28f63 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,801 @@ +## Process this file with automake to produce Makefile.in + +# First declare various groups of files. These were initially extracted +# from the grp.make file, as constructed by the SDT newdev command +GRP_C_ROUTINES = \ + axis.c \ + box.c \ + channel.c \ + circle.c \ + cmpframe.c \ + cmpmap.c \ + cmpregion.c \ + dsbspecframe.c \ + dssmap.c \ + ellipse.c \ + error.c \ + fitschan.c \ + fitstable.c \ + fluxframe.c \ + frame.c \ + frameset.c \ + globals.c \ + grismmap.c \ + interval.c \ + intramap.c \ + keymap.c \ + loader.c \ + lutmap.c \ + mapping.c \ + mathmap.c \ + matrixmap.c \ + memory.c \ + normmap.c \ + nullregion.c \ + object.c \ + pcdmap.c \ + permmap.c \ + plot.c \ + plot3d.c \ + pointlist.c \ + pointset.c \ + polygon.c \ + polymap.c \ + prism.c \ + ratemap.c \ + region.c \ + selectormap.c \ + shiftmap.c \ + skyaxis.c \ + skyframe.c \ + slamap.c \ + specfluxframe.c \ + specframe.c \ + specmap.c \ + sphmap.c \ + stc.c \ + stccatalogentrylocation.c \ + stcobsdatalocation.c \ + stcresourceprofile.c \ + stcschan.c \ + stcsearchlocation.c \ + switchmap.c \ + table.c \ + timeframe.c \ + timemap.c \ + tranmap.c \ + unit.c \ + unitmap.c \ + wcsmap.c \ + winmap.c \ + xml.c \ + xmlchan.c \ + zoommap.c + + +# The C source files required for the Fortran interface +if !NOFORTRAN +F_C_ROUTINES = \ + c2f77.c \ + fbox.c \ + fchannel.c \ + fcircle.c \ + fcmpframe.c \ + fcmpmap.c \ + fcmpregion.c \ + fdsbspecframe.c \ + fdssmap.c \ + fellipse.c \ + ferror.c \ + ffitschan.c \ + ffitstable.c \ + ffluxframe.c \ + fframe.c \ + fframeset.c \ + fgrismmap.c \ + finterval.c \ + fintramap.c \ + fkeymap.c \ + flutmap.c \ + fmapping.c \ + fmathmap.c \ + fmatrixmap.c \ + fnormmap.c \ + fnullregion.c \ + fobject.c \ + fpcdmap.c \ + fpermmap.c \ + fplot.c \ + fplot3d.c \ + fpointlist.c \ + fpolygon.c \ + fpolymap.c \ + fprism.c \ + fratemap.c \ + fregion.c \ + fselectormap.c \ + fshiftmap.c \ + fskyframe.c \ + fslamap.c \ + fspecfluxframe.c \ + fspecframe.c \ + fspecmap.c \ + fsphmap.c \ + fstc.c \ + fstccatalogentrylocation.c \ + fstcobsdatalocation.c \ + fstcresourceprofile.c \ + fstcschan.c \ + fstcsearchlocation.c \ + fswitchmap.c \ + ftable.c \ + ftimeframe.c \ + ftimemap.c \ + ftranmap.c \ + funitmap.c \ + fwcsmap.c \ + fwinmap.c \ + fxmlchan.c \ + fzoommap.c +else +F_C_ROUTINES = +endif + +# Header files which contribute to the "ast.h" file, organised to correspond +# with the class hierarchy. +AST_H_FILES = \ + xml.h \ + wcstrig.h \ + proj.h \ + memory.h \ + error.h \ + globals.h \ + unit.h \ + ast_err.h \ + version.h \ + object.h \ + keymap.h \ + table.h \ + fitstable.h \ + pointset.h \ + axis.h \ + skyaxis.h \ + mapping.h \ + cmpmap.h \ + dssmap.h \ + grismmap.h \ + intramap.h \ + lutmap.h \ + mathmap.h \ + matrixmap.h \ + pcdmap.h \ + permmap.h \ + polymap.h \ + ratemap.h \ + normmap.h \ + shiftmap.h \ + slamap.h \ + specmap.h \ + sphmap.h \ + timemap.h \ + selectormap.h \ + switchmap.h \ + tranmap.h \ + unitmap.h \ + wcsmap.h \ + winmap.h \ + zoommap.h \ + frame.h \ + cmpframe.h \ + specfluxframe.h \ + fluxframe.h \ + frameset.h \ + plot.h \ + plot3d.h \ + skyframe.h \ + specframe.h \ + dsbspecframe.h \ + region.h \ + box.h \ + circle.h \ + cmpregion.h \ + ellipse.h \ + interval.h \ + nullregion.h \ + pointlist.h \ + polygon.h \ + prism.h \ + stc.h \ + stcresourceprofile.h \ + stcsearchlocation.h \ + stccatalogentrylocation.h \ + stcobsdatalocation.h \ + timeframe.h \ + channel.h \ + fitschan.h \ + stcschan.h \ + xmlchan.h + +# All the (C) include files required to build the library. +GRP_C_INCLUDE_FILES = \ + $(AST_H_FILES) \ + ems.h \ + err.h \ + Ers.h \ + f77.h \ + grf.h \ + grf3d.h \ + pg3d.h \ + loader.h \ + pal2ast.h \ + skyaxis.h \ + erfa2ast.h \ + stc.h \ + stcresourceprofile.h \ + stcsearchlocation.h \ + stccatalogentrylocation.h \ + stcobsdatalocation.h \ + wcsmath.h \ + wcstrig.h \ + xmlchan.h + +if !NOFORTRAN +F_C_INCLUDE_FILES = \ + c2f77.h + +# The following list should include AST_PAR, but that must not be +# distributed, and so it is listed separately in +# nodist_libast_la_SOURCES below. +GRP_F_INCLUDE_FILES = \ + GRF_PAR \ + AST_ERR + +else +F_C_INCLUDE_FILES = +GRP_F_INCLUDE_FILES = +endif + +## Following declaration isn't used +## LATEX_DOCUMENTATION_FILES = \ +## sun210.tex \ +## sun211.tex + +DOCUMENTATION_PRODUCTS = $(PAPER_DOCUMENTATION) $(HYPER_DOCUMENTATION) +PAPER_DOCUMENTATION = sun210.tex sun211.tex sun210.pdf sun211.pdf +HYPER_DOCUMENTATION = sun210.htx_tar sun211.htx_tar + +PDF_FIGURES = \ + cmpframe.pdf \ + complex.pdf \ + frames.pdf \ + frameset.pdf \ + fronta.pdf \ + fronta_bw.pdf \ + frontb.pdf \ + frontb_bw.pdf \ + frontc.pdf \ + frontc_bw.pdf \ + fsalign.pdf \ + fsconvert.pdf \ + fsexample.pdf \ + fsmerge.pdf \ + fsremap.pdf \ + gridplot.pdf \ + gridplot_bw.pdf \ + mapping.pdf \ + overgrid.pdf \ + overgrid_bw.pdf \ + parallel.pdf \ + series.pdf \ + simpexamp.pdf + +WCSLIB_FILES = \ + proj.c \ + tpn.c \ + proj.h \ + wcstrig.c \ + wcsmath.h \ + wcstrig.h + +STAR_PAL_FILES = \ + pal/pal.h \ + pal/palAddet.c \ + pal/palAmpqk.c \ + pal/palCaldj.c \ + pal/palDat.c \ + pal/palDe2h.c \ + pal/palDeuler.c \ + pal/palDh2e.c \ + pal/palDjcal.c \ + pal/palDmat.c \ + pal/palDrange.c \ + pal/palDs2tp.c \ + pal/palDtp2s.c \ + pal/palDtps2c.c \ + pal/palDtt.c \ + pal/palEcmat.c \ + pal/palEqgal.c \ + pal/palEtrms.c \ + pal/palEvp.c \ + pal/palFk45z.c \ + pal/palFk524.c \ + pal/palFk54z.c \ + pal/palGaleq.c \ + pal/palGalsup.c \ + pal/palMappa.c \ + pal/palMapqkz.c \ + pal/palOne2One.c \ + pal/palPrebn.c \ + pal/palPrec.c \ + pal/palPrenut.c \ + pal/palPvobs.c \ + pal/palRvgalc.c \ + pal/palRvlg.c \ + pal/palRvlsrd.c \ + pal/palRvlsrk.c \ + pal/palSubet.c \ + pal/palSupgal.c \ + pal/pal1sofa.h \ + pal/palmac.h + +ERFA_FILES = \ + erfa/00READ.ME \ + erfa/erfa.h \ + erfa/erfam.h \ + erfa/a2af.c \ + erfa/a2tf.c \ + erfa/af2a.c \ + erfa/anp.c \ + erfa/anpm.c \ + erfa/bi00.c \ + erfa/bp00.c \ + erfa/bp06.c \ + erfa/bpn2xy.c \ + erfa/c2i00a.c \ + erfa/c2i00b.c \ + erfa/c2i06a.c \ + erfa/c2ibpn.c \ + erfa/c2ixy.c \ + erfa/c2ixys.c \ + erfa/c2s.c \ + erfa/c2t00a.c \ + erfa/c2t00b.c \ + erfa/c2t06a.c \ + erfa/c2tcio.c \ + erfa/c2teqx.c \ + erfa/c2tpe.c \ + erfa/c2txy.c \ + erfa/cal2jd.c \ + erfa/cp.c \ + erfa/cpv.c \ + erfa/cr.c \ + erfa/d2dtf.c \ + erfa/d2tf.c \ + erfa/dat.c \ + erfa/dtdb.c \ + erfa/dtf2d.c \ + erfa/ee00.c \ + erfa/ee00a.c \ + erfa/ee00b.c \ + erfa/ee06a.c \ + erfa/eect00.c \ + erfa/eform.c \ + erfa/eo06a.c \ + erfa/eors.c \ + erfa/epb.c \ + erfa/epb2jd.c \ + erfa/epj.c \ + erfa/epj2jd.c \ + erfa/epv00.c \ + erfa/eqeq94.c \ + erfa/era00.c \ + erfa/fad03.c \ + erfa/fae03.c \ + erfa/faf03.c \ + erfa/faju03.c \ + erfa/fal03.c \ + erfa/falp03.c \ + erfa/fama03.c \ + erfa/fame03.c \ + erfa/fane03.c \ + erfa/faom03.c \ + erfa/fapa03.c \ + erfa/fasa03.c \ + erfa/faur03.c \ + erfa/fave03.c \ + erfa/fk52h.c \ + erfa/fk5hip.c \ + erfa/fk5hz.c \ + erfa/fw2m.c \ + erfa/fw2xy.c \ + erfa/gc2gd.c \ + erfa/gc2gde.c \ + erfa/gd2gc.c \ + erfa/gd2gce.c \ + erfa/gmst00.c \ + erfa/gmst06.c \ + erfa/gmst82.c \ + erfa/gst00a.c \ + erfa/gst00b.c \ + erfa/gst06.c \ + erfa/gst06a.c \ + erfa/gst94.c \ + erfa/h2fk5.c \ + erfa/hfk5z.c \ + erfa/ir.c \ + erfa/jd2cal.c \ + erfa/jdcalf.c \ + erfa/num00a.c \ + erfa/num00b.c \ + erfa/num06a.c \ + erfa/numat.c \ + erfa/nut00a.c \ + erfa/nut00b.c \ + erfa/nut06a.c \ + erfa/nut80.c \ + erfa/nutm80.c \ + erfa/obl06.c \ + erfa/obl80.c \ + erfa/p06e.c \ + erfa/p2pv.c \ + erfa/p2s.c \ + erfa/pap.c \ + erfa/pas.c \ + erfa/pb06.c \ + erfa/pdp.c \ + erfa/pfw06.c \ + erfa/plan94.c \ + erfa/pm.c \ + erfa/pmat00.c \ + erfa/pmat06.c \ + erfa/pmat76.c \ + erfa/pmp.c \ + erfa/pn.c \ + erfa/pn00.c \ + erfa/pn00a.c \ + erfa/pn00b.c \ + erfa/pn06.c \ + erfa/pn06a.c \ + erfa/pnm00a.c \ + erfa/pnm00b.c \ + erfa/pnm06a.c \ + erfa/pnm80.c \ + erfa/pom00.c \ + erfa/ppp.c \ + erfa/ppsp.c \ + erfa/pr00.c \ + erfa/prec76.c \ + erfa/pv2p.c \ + erfa/pv2s.c \ + erfa/pvdpv.c \ + erfa/pvm.c \ + erfa/pvmpv.c \ + erfa/pvppv.c \ + erfa/pvstar.c \ + erfa/pvu.c \ + erfa/pvup.c \ + erfa/pvxpv.c \ + erfa/pxp.c \ + erfa/refco.c \ + erfa/rm2v.c \ + erfa/rv2m.c \ + erfa/rx.c \ + erfa/rxp.c \ + erfa/rxpv.c \ + erfa/rxr.c \ + erfa/ry.c \ + erfa/rz.c \ + erfa/s00.c \ + erfa/s00a.c \ + erfa/s00b.c \ + erfa/s06.c \ + erfa/s06a.c \ + erfa/s2c.c \ + erfa/s2p.c \ + erfa/s2pv.c \ + erfa/s2xpv.c \ + erfa/sepp.c \ + erfa/seps.c \ + erfa/sp00.c \ + erfa/starpm.c \ + erfa/starpv.c \ + erfa/sxp.c \ + erfa/sxpv.c \ + erfa/taitt.c \ + erfa/taiut1.c \ + erfa/taiutc.c \ + erfa/tcbtdb.c \ + erfa/tcgtt.c \ + erfa/tdbtcb.c \ + erfa/tdbtt.c \ + erfa/tf2a.c \ + erfa/tf2d.c \ + erfa/tr.c \ + erfa/trxp.c \ + erfa/trxpv.c \ + erfa/tttai.c \ + erfa/tttcg.c \ + erfa/tttdb.c \ + erfa/ttut1.c \ + erfa/ut1tai.c \ + erfa/ut1tt.c \ + erfa/ut1utc.c \ + erfa/utctai.c \ + erfa/utcut1.c \ + erfa/xy06.c \ + erfa/xys00a.c \ + erfa/xys00b.c \ + erfa/xys06a.c \ + erfa/zp.c \ + erfa/zpv.c \ + erfa/zr.c + +PAL_FILES = \ + palwrap.c \ + pal.h \ + erfa.h \ + erfam.h + +CMINPACK_FILES = \ + cminpack/cminpack.h \ + cminpack/cminpackP.h \ + cminpack/lmder1.c \ + cminpack/lmder.c \ + cminpack/dpmpar.c \ + cminpack/enorm.c \ + cminpack/qrfac.c \ + cminpack/lmpar.c \ + cminpack/qrsolv.c + +bin_SCRIPTS = ast_link +dist_bin_SCRIPTS = ast_link_adam +noinst_SCRIPTS = ast_cpp +dist_noinst_SCRIPTS = makeh +# Scripts are not distributed by default (since they might be derived objects) +# Add these to the distribution below. In fact, it would be useful +# and straightforward to make ast_link{,_adam} derived, since they +# could then have installation directories painlessly edited in to +# them. This might be a requirement for scripts which supported +# linking against shared libraries. + +# Headers required by library users. Both of the following lines +# indicate headers which are installed. +include_HEADERS = GRF_PAR grf.h grf3d.h +# Following are generated, so should not be distributed. +nodist_include_HEADERS = ast.h AST_PAR +include_MESSAGES = AST_ERR ast_err.h + +if EXTERNAL_PAL +PAL_LIB = +else +PAL_LIB = libast_pal.la +endif + +lib_LTLIBRARIES = \ + $(PAL_LIB) \ + libast.la \ + libast_err.la \ + libast_ems.la \ + libast_drama.la \ + libast_grf3d.la \ + libast_grf_2.0.la \ + libast_grf_3.2.la \ + libast_grf_5.6.la \ + libast_pgplot.la \ + libast_pgplot3d.la + +stardocs_DATA = @STAR_LATEX_DOCUMENTATION@ +dist_starnews_DATA = ast.news +dist_pkgdata_DATA = COPYING COPYING.LESSER COPYING.LIB + +# Make all library code position independent by default. This is handy for +# creating shareable libraries from the static ones (Java JNI libraries). +# Note we do not simply set the AM_CFLAGS variable, as this would also +# apply to programs compiled without using libtool, possibly causing the +# compilation to fail. + +if !NOTHREADS + +if !NOPIC +libast_la_CFLAGS = $(AM_CFLAGS) -prefer-pic -DTHREAD_SAFE +else +libast_la_CFLAGS = $(AM_CFLAGS) -DTHREAD_SAFE +endif + +else +libast_la_CFLAGS = $(AM_CFLAGS) -prefer-pic + +endif + +if !NOPIC +libast_err_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_ems_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_drama_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_grf3d_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_grf_2_0_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_grf_3_2_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_grf_5_6_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_pgplot_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_pgplot3d_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +libast_pal_la_CFLAGS = $(AM_CFLAGS) -prefer-pic +endif + +# The module containing the main AST classes +libast_la_SOURCES = \ + $(GRP_C_ROUTINES) \ + $(F_C_ROUTINES) \ + $(GRP_C_INCLUDE_FILES) \ + $(F_C_INCLUDE_FILES) \ + $(GRP_F_INCLUDE_FILES) \ + $(CMINPACK_FILES) \ + $(WCSLIB_FILES) \ + ast_err.h + +# Ensure libast links against libraries containing functions used within +# libast. If AST is configured --with-external-pal, then the internal +# libast_pal library will be empty, and we link to an external PAL +# library instead. +if EXTERNAL_PAL +libast_la_LIBADD = $(libdir)/libpal.la +else +libast_la_LIBADD = libast_pal.la +endif + +# AST_PAR is really part of GRP_F_INCLUDE_FILES, but it must not be +# distributed, so list it separately. +nodist_libast_la_SOURCES = \ + ast.h \ + AST_PAR + +# The default error reporting module. +libast_err_la_SOURCES = err_null.c + +# The error reporting module that uses EMS to deliver errors. +libast_ems_la_SOURCES = err_ems.c + +# The error reporting module that uses DRAMA Ers to deliver errors. +libast_drama_la_SOURCES = err_drama.c + +# The module containing null implementations of the 3D graphics routines +# required by AST +libast_grf3d_la_SOURCES = grf3d.c + +# The module containing null implementations of the graphics routines +# required by AST V2.0 +libast_grf_2_0_la_SOURCES = grf_2.0.c + +# The module containing null implementations of the graphics routines +# added by AST V3.2 and not present in V2.0 +libast_grf_3_2_la_SOURCES = grf_3.2.c + +# The module containing null implementations of the graphics routines +# added by AST V5.6 and not present in V3.2 +libast_grf_5_6_la_SOURCES = grf_5.6.c + +# The graphics module that uses PGPLOT for 2D graphical output. +libast_pgplot_la_SOURCES = grf_pgplot.c + +# The graphics module that uses PGPLOT for 3D graphical output. +libast_pgplot3d_la_SOURCES = grf3d_pgplot.c + +# Positional astronomy libraries. +libast_pal_la_SOURCES = $(PAL_FILES) + +# The following files are built by the targets in this makefile. +MAINTAINERCLEANFILES = version.h builddocs addversion \ + ast.h $(DOCUMENTATION_PRODUCTS) +CLEANFILES = AST_PAR ast.h + +# Special cases start here + +# The AST_PAR include file is produced by compiling the astbad.c +# program and editing its output into the ast_par.source file (while +# also changing the "E" exponent character to a "D"). The astbad.c +# and ast_par.source must be distributed (the generation of the +# AST__BAD value must be done on the build host) but not installed. +noinst_PROGRAMS = astbad +astbad_SOURCES = astbad.c pointset.h +AST_PAR: ast_par.source astbad + sed -e 's//'`./astbad AST__BAD | tr 'E' 'D'`'/' \ + -e 's//'`./astbad AST__NAN | tr 'E' 'D'`'/' \ + -e 's//'`./astbad AST__NANF | tr 'E' 'D'`'/' \ + ast_par.source >$@ + +# ast_link is generated from ast_link.in; ast_link_adam does not +# need configuration, and so is not mentioned in AC_CONFIG_FILES within +# configure.ac, and so is not distributed automatically. +# +# makeh is required in order to build ast.h after distribution (see below). +EXTRA_DIST = ast_par.source sun210_figures sun211_figures pal erfa cminpack + +# ast.h depends on the error-facility files. ast.h _could_ be made +# before distribution, but since it's generated from other distributed +# files, it's more robust to distribute makeh and make ast.h on the +# build host. +ast.h: $(AST_H_FILES) ast_err.h makeh config.h + @PERL@ ./makeh -s $(srcdir) $(AST_H_FILES) >$@ + +# AST_ERR and ast_err.h are `generated source files', and so must be +# generated before any other compilations are done. Note that these +# files are generated on the distribution host, and so this +# declaration has no effect post-distribution. +# +# AST_PAR is also a generated source file, but it should _not_ be +# included in the list of BUILT_SOURCES, otherwise `make' tries to make +# it before it makes the `astbad' program it depends on. Instead, +# just rely on the dependencies expressed in the main body above to +# have AST_PAR built before it is needed. +# +# version.h is included by object.h, and thus indirectly by most modules. +# It's most straightforward to build it at the beginning. +BUILT_SOURCES = AST_ERR ast_err.h version.h + +# Form a second link to the main object library (static and shared). This +# is used when a second pass through the library is needed during linking +# to resolve references made within other AST libraries (e.g. the grf +# modules, etc), to functions defined within libast (e.g. memory management +# and error reporting functions). Do not forget to change the contents of +# the libast_pass2.la file to refer to libast_pass2.* rather than libast.*. +# Use target install-exec-hook rather than install-exec-local so that the +# soft links and files are created *after* the main library has been +# installed. +install-exec-hook: libast.la + $(mkdir_p) $(DESTDIR)$(libdir) + cd $(DESTDIR)$(libdir); \ + for f in `ls libast.*`; do \ + ff=`echo $$f | sed -e 's/libast/libast_pass2/'`; \ + if test -f "$$ff"; then rm "$$ff"; fi; \ + $(LN_S) $$f $$ff; \ + $(MANIFEST) && echo "MANIFEST:$(DESTDIR)$(libdir)/$$ff" || :; \ + done; \ + if test -f "libast.la"; then \ + if test -f "libast_pass2.la"; then rm "libast_pass2.la"; fi; \ + sed -e 's/libast\./libast_pass2\./g' libast.la > libast_pass2.la; \ + fi + +# Make pre-distribution files. These are files which are required for +# building the distribution, but are not themselves distributed. +# The source files here should be mentioned in STAR_PREDIST_SOURCES in +# configure.ac +@PREDIST@predist_subs = sed \ +@PREDIST@ -e 's,@PACKAGE_VERSION\@,$(PACKAGE_VERSION),' \ +@PREDIST@ -e 's,@PACKAGE_VERSION_MAJOR\@,$(PACKAGE_VERSION_MAJOR),' \ +@PREDIST@ -e 's,@PACKAGE_VERSION_MINOR\@,$(PACKAGE_VERSION_MINOR),' \ +@PREDIST@ -e 's,@PACKAGE_VERSION_RELEASE\@,$(PACKAGE_VERSION_RELEASE),' \ +@PREDIST@ -e 's,@PERL\@,$(PERL),' \ +@PREDIST@ -e 's,@STARLINK\@,$(STARLINK),' + +@PREDIST@version.h: version.h.in configure.ac +@PREDIST@ rm -f $@; $(predist_subs) version.h.in >$@ +@PREDIST@builddocs: builddocs.in configure.ac +@PREDIST@ rm -f $@; $(predist_subs) builddocs.in >$@; chmod +x $@ +@PREDIST@addversion: addversion.in configure.ac +@PREDIST@ rm -f $@; $(predist_subs) addversion.in >$@; chmod +x $@ + +# Documentation +@PREDIST@$(PAPER_DOCUMENTATION): sun211_figures builddocs addversion +@PREDIST@ ./builddocs + +# The contents of the sun211_figures directory is identical to that +# sun210_figures +sun211_figures: sun210_figures + $(LN_S) sun210_figures sun211_figures + +# Installation check + +TESTS = ast_test +check_PROGRAMS = ast_test +ast_test_SOURCES = ast_test.c + +#ast_test_LDADD = `ast_link` +# Expand ast_link to avoid libast_pass2, which causes problems for Solaris +ast_test_LDADD = @LIBPAL@ libast.la libast_pal.la libast_grf_3.2.la libast_grf_5.6.la libast_grf_2.0.la libast_grf3d.la libast_err.la -lm + +# Need to include latex support files in the distribution tar ball so +# that the docs can be built from the tex source files. Requires environment +# variable STARLATEXSUPPORT to be deined. Is there a better way to do this? +dist-hook: + cp -p $(STARLATEXSUPPORT)/starlink.cls $(distdir)/ + cp -p $(STARLATEXSUPPORT)/starabbrev.sty $(distdir)/ + cp -p $(STARLATEXSUPPORT)/starstyle.sty $(distdir)/ + cp -p $(STARLATEXSUPPORT)/sst.sty $(distdir)/ diff --git a/Notes b/Notes new file mode 100644 index 0000000..bb4ac0e --- /dev/null +++ b/Notes @@ -0,0 +1,105 @@ + +Random notes about the autoconfing of AST + +Initial version imported from a tarball supplied by DSB. The tarball +contained David's RCS repository plus the development directory (dev/) +and the `reference' directory (ref/) which contains a checkout of the +RCS repository. The development directory contains working versions +of the files in the reference directory, plus miscellaneous test files +and notes, plus copies of sdt.tar.Z (the RCS wrapper which has long +been used for AST development) and sst_source.tar.Z (the documentation +builder). + +The RCS repository was dropped into the CVS repository on 14 November +2003, and tagged with ast-3-1-0-import. After thinking a bit about +tagging policies (and writing these up on the wiki and posting to the +stardev group), I tagged this same point as +bp-dev-nxg-20031121-autoconfing and created a branch +dev-nxg-20031121-autoconfing (21 November -- busy week since the +14th...). That's the one I'll work on. + +I've created configure.ac, and I've developed the build system using +reasonably current versions of the autotools (namely autoconf-2.57, +automake versions 1.6.3 or 1.7.5, and libtool versions 1.4.2 or 1.5). +There's no point jumping through hoops to use random obsolete versions +(reading old documentation, and trying to keep things forwards +compatible, untestably) simply because they're installed on RH7.3. If +anyone plans to install these newer versions, I recall that autoconf, +libtool, automake was the order that worked (it matters), and you need +to install GNU m4 first on Solaris or Alpha + +Created starlink.m4, which includes macro STAR_FACILITY_CODE which +facilitates declaring the `err' facility code which has been allocated +to this library. This also AC_SUBSTs the variable ERR_FACILITY_CODE, +and I've moved ast_err.msg and error.h to the corresponding .in files, +modified them to have that code inserted, and put them in +AC_CONFIG_FILES. This file should be moved to a central location when +I work out where such a central location should be. At that point, +./bootstrap should be edited to contain that location. + +The two files in grp.make's F_ROUTINES group, object.f and frame.f, +don't appear to be used anywhere. I've omitted them in Makefile.am + + +Building AST +------------ + +To build AST directly from the repository, you need reasonably current +versions of the autotools (as noted above). If anyone plans to +install these newer versions, I recall that autoconf, libtool, +automake was the order that worked (it matters), and you need to +install GNU m4 first on Solaris or Alpha. + +Recall the discussion we had on stardev about autotools and generated +files. I have now checked in the generated files configure, +config.h.in and Makefile.in. Later, I'll either add the fixes I added +to autoastrom to make the `missing' script work, or see if there's a +more supported way to do that. With those fixes and additions, it +_will_not_matter_ which autotools versions you have, as long as you +don't have to alter Makefile.am or configure.ac, and in any case it +the autotools are completely unneeded for building from the +distribution tarball. + +To build from the repository, do + + ./bootstrap + ./configure + make + + +Conventions and facilities so far +--------------------------------- + +The version number is set in the AC_INIT line in configure.ac, and +propagated from there to the locations where it's needed. + +The string @STAR_BOILERPLATE@ must be in Makefile.{am,in}. All this +does at present is include boilerplate for calling messgen _if_ +STAR_FACILITY_CODE is present (which it is, for AST). + +configure.ac should have STAR_DEFAULTS near the beginning (sets the +default installation location to be /star rather than GNU default +/usr/local, ensures that the STARLINK environment variable is set). + +STAR_FACILITY_CODE declares the error-reporting system facility code +which has been allocated to this library. + + +Modifications to AST files +-------------------------- + +ast_err.msg is now created from ast_err.msg.in, and has the facility +code declared in STAR_FACILITY_CODE edited into it at that point. +Same for error.h and error.h.in. + +ast_cpp generated from ast_cpp.in with the CC edited in + +makeh now calls ast_cpp through /bin/sh (avoids having to worry about +execute bits) + +Versioning: The version number (as mentioned above, set in +configure.ac) is now substituted into version.h.in, broken into +major.minor-release, and version.h is included in ast.h. File +object.c now includes version.h to implement the astVersion_ +function. AST_MAJOR_VERS and co removed from makeh (since they're now +included in ast.h inside version.h). diff --git a/STARLINK_BRANCHES b/STARLINK_BRANCHES new file mode 100644 index 0000000..975ab87 --- /dev/null +++ b/STARLINK_BRANCHES @@ -0,0 +1,46 @@ + +AST branches +============ + +dev-dsb-20040406 +---------------- + +Branch created on 6th April 2004, to support classic ast.tar.Z version. + + +DEV-nxg-20040830-ast-timeframe +------------------------------ + +Branch created on 2004 August 30, to support Norman's TimeFrame work. +Should probably have the trunk merged to it at some point, before much +more work is done on TimeFrame. ABANDONED 2005 March 18, in favour of +DEV-nxg-20050314-ast-timeframe. + +DEV-nxg-20050314-ast-timeframe +------------------------------ + +Created 2005 March 14, as a fresh branch from the AST HEAD, on the +grounds that AST has moved forward a significant amount from the +point where branch DEV-nxg-20040830-ast-timeframe was created, and +there's no point in multiplying our merging problems unnecessarily. +The changes done on that previous branch were merged onto this branch, +mostly with a patch file, but by hand where there were already conflicts. + +By 2005-05-04T16:00, the HEAD had again diverged significantly from +the original branch point, so Norman merged the HEAD differences from +the branch point onto the branch: + + cvs update -j bp-DEV-nxg-20050314-ast-timeframe -j HEAD + +DEV-dsb-20041123-ast-stc +------------------------ + +Branch created on "23rd November 2004" to support DSB's STC work. This is +a branch off dev-dsb-20040406. Once I've got something worth using, I'll +create a similar branch of the trunk. Need to think carefully about how +to distribute AST-with-STC since AST is starting to look a bit bloated, +and the majority of AST users won't be interested in STC. + + + + diff --git a/addcopyright b/addcopyright new file mode 100755 index 0000000..4772023 --- /dev/null +++ b/addcopyright @@ -0,0 +1,2 @@ +sed -e 's/* /* Copyright (C) 1997-'"`date +%Y`"' Council for the Central Laboratory of the\n* Research Councils/' \ + -e 's//Copyright (C) 1997-'"`date +%Y`"' Council for the Central Laboratory of the Research Councils/' diff --git a/addlinks b/addlinks new file mode 100755 index 0000000..997dad6 --- /dev/null +++ b/addlinks @@ -0,0 +1,163 @@ +#! /usr/bin/env perl + +# Read a set of labels (one per line) from the "global.labels" file. +# Then read the input lines and create links for the first occurrence +# of each label in each Latex section encountered (the links use \htmlref +# to refer to HTX labels elsewhere in the document). Omit links wherever +# they are not appropriate. + +# Read the list of labels for which links should be generated (these may +# contain Latex escape characters, as they must exactly match the text from +# which the link will be generated). + open( LABELS, 'global.labels' ); + @labels = ; + close( LABELS ); + +# Sort the labels into descending order of length. This is to avoid problems +# with labels which are abbreviations of other labels. + @labels = sort { length( $b ) <=> length( $a ) } @labels; + +# Build a regexp that will match any one of the labels. + $pattern = ""; + for $label ( @labels ) { + +# Remove newlines from each label. + $label =~ s/\n//g; + +# Labels must be whole words unless they start/end without an alphanumeric. + $label_q = quotemeta( $label ); + if ( $label =~ m/^\w/ ) { $label_q = "\\b" . $label_q }; + if ( $label =~ m/\w$/ ) { $label_q = $label_q . "\\b" }; + +# Build the regexp. + $pattern = !$pattern ? $label_q : $pattern . "|" . $label_q; + } + +# Loop through the input data. + $sdiy = 0; + $inr = 0; + $source_line = 10; + $verbatim = 0; + while ( <> ) { + +# Detect the start of each type of document section. + ( $s ) = /^ *\\section\{/; # Latex section + ( $ss ) = /^ *\\subsection\{/; # Latex subsection + ( $sss ) = /^ *\\subsubsection\{/; # Latex subsubsection + ( $sr ) = /^ *\\sstroutine\{/; # SST routine description + +# We only consider sst diytopic sections to be of significance if they +# introduce a listing of attributes or Functions. This is so that these +# sections always contain links. SO if the previous line was a diytopic +# line, see if this line contains the single word "Attributes" or +# "Functions" + if( $sdiy ) { + ( $attrfun ) = /^ *(Attributes|Functions) *$/; + $linkatfun = 0; + $sdiy = 0; + } else { + ( $sdiy ) = /^ *\\sstdiytopic\{/; + } + +# Do not make links in the introductory text in an Attributes or +# Functions DIY section. + if( $attrfun ) { + if( /^ *\\sstitemlist\{/ ) { $linkatfun = 1 }; + } else { + $linkatfun = 0; + } + +# Watch for the end of the list of attributes or functions. + if( $attrfun ) { + if( /^ *\} *$/ ) { + $attrfun = 0; + $linkatfun = 0; + } + } + +# Note if a new section has started. + $sx = ( $s || $ss || $sss || $sr ); + if ( $sx ) { + +# Set a flag to indicate which type of section we are in. + $ins = $inss = $insss = $inr = 0; + if ( $s ) { $ins = 1 }; + if ( $ss ) { $inss = 1 }; + if ( $sss ) { $insss = 1 }; + if ( $sr ) { $inr = 1 }; + +# Clear the count of source lines for this section. + $source_line = 0; + +# Clear the array of flags indicating which labels have been used. + undef %used; + } + +# Note if we are in a Latex "verbatim" or "terminalv" environment. + if ( /^ *\\begin\{(verbatim|terminalv)\} *$/ ) { $verbatim = 1 }; + +# Count the source lines read from each section. + $source_line++; + +# Obtain an array of all the labels matched in the current source line. + @match = /$pattern/og; + +# Consider generating a link for each label matched. + for $label ( @match ) { + $label_q = quotemeta( $label ); + +# In an SST routine description, extract the name of the routine from +# the second source line and mark the label of that name "used". This +# prevents links being made to this section from within itself. + if ( $inr && $source_line == 2 ) { + $used{ $label }++; + $label_x = $_; + $label_x =~ s/\W//g; + #s/$/\\sstlabel{$label_x}/; + } + +# If we are in a Functions or Attributes section, see if the match occurs +# at the start of the line and is followed by a colon. If it is, it is the +# name of the main Function or Attribute being described and so should be +# linkified. + $atfun = ( $linkatfun && ( /^ *$label_q:/ ) ); + +# Is this a caption? Do not replace entries in first part of caption +# that appears in TOC LOF + $iscap = /\\caption/; + +# Check if a link should be made. Omit links in "verbatim" environments, +# in any line which starts a new section (because labels don't work properly +# if they get into the table of contents), in the 4th line of an SST +# routine description (because these also go into the TOC), or if the label +# has already been used in this section. We always linkify any match if it +# is in a list of Function or Attribute descriptions, if it is at the start +# of the line and is followed by a colon. + if ( $atfun || ( !$verbatim && + !$iscap && + !$sx && + !( $inr && ( $source_line == 4 ) ) && + !$used{ $label } ) ) { + +# Increment the number of usages of this label. + $used{ $label }++; + +# Labels must be whole words unless they start/end without an alphanumeric. + if ( $label =~ m/^\w/ ) { $label_q = "\\b" . $label_q }; + if ( $label =~ m/\w$/ ) { $label_q = $label_q . "\\b" }; + +# Form a version of the label with non alphanumerics removed. + $label_x = $label; + #$label_x =~ s/\W//g; + +# Edit each instance of the label in the source line to insert an \htmlref. + s/$label_q/\\htmlref{$label}{$label_x}/; + } + } + +# Detect the end of "verbatim" environments. + if ( /^ *\\end\{(verbatim|terminalv)\} *$/ ) { $verbatim = 0 }; + +# Output the modified line. + print; + } diff --git a/addversion.in b/addversion.in new file mode 100644 index 0000000..955a971 --- /dev/null +++ b/addversion.in @@ -0,0 +1,10 @@ + +vers="@PACKAGE_VERSION@" +majversno="`echo "${vers}" | awk -F . '{print $1;}'`" +minversno="`echo "${vers}" | awk -F . '{print $2;}'`" +relsno="`echo "${vers}" | awk -F . '{print $3;}'`" +versno="${majversno}.${minversno}" +sed -e 's//'"${versno}"'/g' \ + -e 's//'"${majversno}"'/g' \ + -e 's//'"${minversno}"'/g' \ + -e 's//'"${relsno}"'/g' diff --git a/ast-for-wcslib/astTester.c b/ast-for-wcslib/astTester.c new file mode 100644 index 0000000..0cd6a92 --- /dev/null +++ b/ast-for-wcslib/astTester.c @@ -0,0 +1,85 @@ +/* + * Purpose: + * Tests the cut down version of AST used by WCSLIB for handling units + * strings. + */ + +/* System header files. */ +#include + +/* The following line causes the AST header files to make the internal + "protected" interface available. The functions defined in unit.h are + only available with in the protected interface. */ +#define astCLASS wcslib + +/* Include header files for the AST classes which are used below. */ +#include "unit.h" +#include "mapping.h" + +main(){ + int pass; + char *label; + double xin[3], xout[3]; + + pass = 1; + +/* Get the Mapping from a speed value in km/h to a log(speed) value + in "log(m/s)". */ + AstMapping *map = astUnitMapper( "km/h", "log(m/s)", "speed", &label ); + +/* If no Mapping could be found, test has failed. */ + if( !map ) { + pass = 0; + printf("No Mapping returned by astUnitMapper\n"); + +/* If a Mapping was returned by astUnitMapper, it can be used with any of the + methods defined by the Mapping class. See: + + http://www.starlink.ac.uk/~dsb/ast/sun211.htx/node450.html + + Here, we use the Mapping to transform three speed values (first is + negative and so should produce a bad log(speed) value). */ + } else { + xin[0] = -1.0; + xin[1] = 1.0; + xin[2] = 100.0; + astTran1( map, 3, xin, 1, xout ); + +/* Check above transformation was succesful. */ + if( astOK ) { + +/* Check the transformed values and label are correct. */ + if( xout[ 0 ] != AST__BAD ) { + printf( "xout[0] wrong: %.*g should be %.*g\n", + DBL_DIG, xout[0], DBL_DIG, AST__BAD ); + pass = 0; + + } else if( fabs( xout[ 1 ] - (-0.556302500767287) ) > 1.0E-5 ) { + printf( "xout[1] wrong: %.*g should be -0.556302500767287\n", + DBL_DIG, xout[1] ); + pass = 0; + + } else if( fabs( xout[ 2 ] - 1.44369749923271 ) > 1.0E-5 ) { + printf( "xout[2] wrong: %.*g should be 1.44369749923271\n", + DBL_DIG, xout[2] ); + pass = 0; + + } else if( strcmp( label, "log( speed )" ) ) { + printf( "label wrong: \"%s\" should be \"log( speed )\"\n", + label ); + pass = 0; + } + + } else { + printf( "Error on return from astTran1\n" ); + pass = 0; + } + } + +/* Say whether the test has been passed or not */ + if( !pass ) { + printf("\n AST unit test failed\n\n"); + } else { + printf("\n AST unit test passed\n\n"); + } +} diff --git a/ast-for-wcslib/loader.c b/ast-for-wcslib/loader.c new file mode 100644 index 0000000..d71c6fb --- /dev/null +++ b/ast-for-wcslib/loader.c @@ -0,0 +1,35 @@ +/* A dummy version of loader.c for use in the WCSLIB sub-set of AST */ +#define astCLASS +#include "channel.h" +#include "loader.h" +#include "mapping.h" +#include "mathmap.h" +#include "object.h" +#include "pointset.h" +#include "unitmap.h" +#include "zoommap.h" + +#include "error.h" +#include "ast_err.h" +#include +#include + +AstLoaderType *astGetLoader( const char *class ) { + if ( !astOK ) return NULL; + +#define LOAD(name) \ +if ( !strcmp( class, #name ) ) return (AstLoaderType *) astLoad##name##_ + + LOAD(Channel); + LOAD(Mapping); + LOAD(MathMap); + LOAD(Object); + LOAD(PointSet); + LOAD(UnitMap); + LOAD(ZoomMap); + + astError( AST__OCLUK, "astGetLoader: Object of unknown class \"%s\" cannot " + "be loaded.", class ); + return NULL; +#undef LOAD +} diff --git a/ast-for-wcslib/makefile b/ast-for-wcslib/makefile new file mode 100644 index 0000000..02e654c --- /dev/null +++ b/ast-for-wcslib/makefile @@ -0,0 +1,31 @@ + +# Defaults suitable for linux (redhat 9): override these with values +# suitable to the OS being used. +CC = gcc +CFLAGS = -ansi -O -fPIC +AR_IN = ar -r +RANLIB = ranlib +LDFLAGS = -lm + +C_ROUTINES = channel.c error.c mapping.c mathmap.c memory.c object.c \ +pointset.c unit.c unitmap.c zoommap.c err_null.c loader.c + +OBJECT_FILES = $(C_ROUTINES:.c=.o) + +.c.o: + $(CC) $(CFLAGS) -I. -c $< + +build: libast.a + +libast.a: $(OBJECT_FILES) + $(AR_IN) $@ $? + $(RANLIB) $@ + +clean: + @- rm -f $(OBJECT_FILES) >/dev/null 2>/dev/null + +check test: astTester.c libast.a + $(CC) -o asttest astTester.c libast.a $(LDFLAGS); \ + ./asttest; \ + rm -f asttest >/dev/null 2>/dev/null + diff --git a/ast-for-wcslib/matrixmap.h b/ast-for-wcslib/matrixmap.h new file mode 100644 index 0000000..d999180 --- /dev/null +++ b/ast-for-wcslib/matrixmap.h @@ -0,0 +1,7 @@ +/* A dummy version of matrixmap.h for use in the WCSLIB sub-set of AST */ +#define astMatrixMap(nin,nout,mode,mat,opts) \ + (astError(AST__INTER,"Internal AST programming error - " \ + "an attempt has been made to create a MatrixMap" ), \ + astError(AST__INTER,"The WCSLIB version of AST does not include " \ + "the MatrixMap class" ), \ + NULL) diff --git a/ast-for-wcslib/plot.h b/ast-for-wcslib/plot.h new file mode 100644 index 0000000..1678ec9 --- /dev/null +++ b/ast-for-wcslib/plot.h @@ -0,0 +1,2 @@ +/* A dummy version of plot.h for use in the WCSLIB sub-set of AST */ +#define astStripEscapes(result) result diff --git a/ast-for-wcslib/wcslib-instructions b/ast-for-wcslib/wcslib-instructions new file mode 100644 index 0000000..82c4369 --- /dev/null +++ b/ast-for-wcslib/wcslib-instructions @@ -0,0 +1,112 @@ +From dsb@ast.man.ac.uk Thu Jul 15 17:23:14 2004 +Date: Thu, 15 Jul 2004 17:19:38 +0100 (BST) +From: David Berry +To: Mark Calabretta +Cc: Eric Greisen , D.L.Giaretta@rl.ac.uk +Subject: Re: Legacy FITS headers + +Mark, + +> On Tue 2004/07/13 10:38:01 +0100, David Berry wrote +> in a message to: Mark Calabretta +> and copied to: Eric Greisen +> +> >headers. When reading headers, AST parses CUNIT and applies the +> >appropriate conversion factors where necessary. It uses a neat little +> >utility module which, given two units strings (using the paper I syntax) +> >will parse them and return a Mapping between them if possible). +> +> Hi David, +> +> That's something I would like to include directly in WCSLIB if possible +> rather than reinventing the wheel, quite a large one too by the look of +> it! +> +> Looking at unit.c though, it seems to depend on a lot of other AST +> code (dependency list appended), I really need a self-contained +> version, preferably in one .h and one .c file. It looks like the CCLRC +> copyright is reasonably permissive with regard to distributing modified +> forms of the AST code. I'd need to change it only so far as to make it +> self-contained, also probably changing global symbol names to prevent +> potential conflicts with the AST object library. +> +> How does that sound to you? + +All Starlink software is being moved over to GPL licences, so there should +be no legal problem in you using parts of AST. + +Getting everything relevant to unit.c into one .c and one .h file could be +quite tricky. Having said that, you could manage with only a few of the +many files in the AST distribution. I've played about with this a bit +today, and the following seems to produce a minimal AST system which +allows the facilities of unit.c to be used: + +1) Make a new directory and cd into it + +2) Get the latest version of AST (V3.3-4) from: + +ftp://ftp.starlink.ac.uk/pub/users-ftp/dsb/ast/V3.3-4/ast.tar.Z + +(I found and fixed a bug in unit.c today whilst I was looking into this) + +3) Execute the following commands: +% uncompress ast.tar.Z +% tar -xf ast.tar ast_source.tar +% tar -xf ast_source.tar \ + ast_err.h \ + channel.h \ + err.h \ + error.h \ + loader.h \ + mapping.h \ + mathmap.h \ + memory.h \ + object.h \ + pointset.h \ + unitmap.h \ + unit.h \ + zoommap.h \ + channel.c \ + error.c \ + err_null.c \ + mapping.c \ + mathmap.c \ + memory.c \ + object.c \ + pointset.c \ + unitmap.c \ + unit.c \ + zoommap.c + +% rm ast_source.tar ast.tar + +4) Copy the files attached to this e-mail into this directory (astTester.c + loader.c matrixmap.h makefile plot.h). + +5) Do: + +% make +% make test + +(you may need to change some of the macro values in makefile if you are +not running on a system like redhat 9). This will create a libast.a +library and run the astTester.c test program. You could look at +astTester.c to see how to use the astUnitMapper function. + +This is the minimal set of AST files needed to use unit.c - it leaves out +all the graphics and the handling of coordinate systems and FITS headers +(and also most of the Mapping classes). + +In case you need to get back to me about this, I'm away on holiday for the +next two weeks. + +David + + + [ Part 2, "" Application/OCTET-STREAM (Name: "wcslib.tar.gz") ] + [ 2.8KB. ] + [ Unable to print this part. ] + + + + diff --git a/ast.news b/ast.news new file mode 100644 index 0000000..fec5d34 --- /dev/null +++ b/ast.news @@ -0,0 +1,1063 @@ +AST Library +----------- + A new release (V8.2.0) of the Starlink AST (astrometry) library is +now available. + + AST provides a comprehensive range of facilities for attaching +world coordinate systems (such as RA/Dec, frequency, etc) to astronomical +data, for retrieving and interpreting that information and for generating +graphical output based on it. + + The library should be of interest to anyone writing astronomical +software which needs to manipulate coordinate system data, especially +celestial coordinate systems. AST is portable and +environment-independent. + +Main Changes in this Version +---------------------------- + +- The restriction that Mappings are immutable has been extended to all +Mapping classes. This means that attributes representing parameters of +a Mapping's forward or inverse transformation cannot be changed after +the Mapping has been created. In order to minimise the risk to existing +software, this rule does not apply to Mappings that have not yet been +included in other objects such as CmpMaps or FrameSets, or which have not +yet been cloned. In other words, an error is reported if an attempt is +made to change the nature of a Mapping's transformation, but only if the +reference count of the Mapping is greater than one. The Mapping classes +affected include: GrismMap, LutMap, PcdMap, SphMap, WcsMap and ZoomMap. + + +Main Changes in V8.1.0 +---------------------- + +- The configure script has a new option "--without-fortran" that allows +AST to be built in situations where no Fortran compiler is available. The +resulting library has no Fortran interface and so cannot be used within +Fortran applications. Also, the link scripts do not attempt to include the +fortran runtime libraries. + +Main Changes in V8.0.7 +---------------------- + +- A bug in FitsChan has been fixed which could cause a small shift in + spectral axis value when writing out a spectral cube to FITS-WCS headers, + This shift occurred only if the celestial axes in the cube were not FK5 + (RA,Dec). + +- Avoid some more compiler warnings. + +- A "BadKeyValue" warning is now issued by the FitsChan class if an illegal +FITS keyword value is encountered. See attribute "Warnings" and function +"astWarnings". + +Main Changes in V8.0.6 +---------------------- + +- Fix bug in FitsChan that caused SIP headers to be treated as linear +when creating a FrameSet from the headers. + +- Fix bug in LutMap that incorrectly allowed an inverse lutmap to be used +even if the original LutMap was not monotonic. + +- Allow attributes to be set for each plane of a Plot3D. + +- Avoid some compiler warnings. + +Main Changes in V8.0.5 +---------------------- + +- The SkyFrame class has a new attribute called SkyTol, which specifies +the smallest significant distance within the SkyFrame. It is used to +decide if the Mapping between two SkyFrames can be considered a unit +transformation. The default value is 0.001 arc-seconds. + +- A bug has been fixed in the FitsChan class that prevented illegal +characters within FITS keyword names (i.e. characters not allowed by the +FITS standard) being detected. This bug could under some circumstances +cause a subsequent segmentation violation to occur. + +- A "BadKeyName" warning is now issued by the FitsChan class if a FITS +keyword name is encountered that contains any illegal characters. See +attribute "Warnings" and function "astWarnings". + +Main Changes in V8.0.4 +---------------------- + +- The behaviour of the astAddFrame method has been changed slightly. +Previously, astAddFrame modified the FrameSet by storing references to +the supplied Mapping and Frame objects within the FrameSet. This meant +that any subsequent changes to the current Frame of the modified FrameSet +also affected the supplied Frame object. Now, astAddFrame stores deep +copies of the Mapping and Frame objects (rather than references) within +the modified FrameSet. This means that subsequent changes to the modified +FrameSet will now have no effect on the supplied Frame. + +- The choice of default tick-mark for time axes has been improved, to avoid +previous issues which could result in no suitable gap being found, or +inappropriate tick marks when using formatted dates. + +- A new method called astRegionOutline has been added to the Plot class. +It draws the outline of a supplied AST Region. + +- A bug has been fixed that could cause astSimplfy to enter an infinite loop. + +- Some improvements have been made to the Mapping simplification process +that allow more Mappings to be simplified. + +- The Frame class has a new read-only attribute called "InternalUnit", +which gives the units used for the unformatted (i.e. floating-point) axis +values used internally by application code. For most Frames, the +InternalUnit value is just the same as the Unit value (i.e. formatted and +unformatted axis values use the same units). However, the SkyFrame class +always returns "rad" for InternalUnit, regardless of the value of Unit, +indicating that floating-point SkyFrame axis values are always in units +of radians. + +- The LutMap class has a new attribute called LutEpsilon, which specifies +the relative error of the values in the table. It is used to decide if +the LutMap can be simplified to a straight line. + + +Main Changes in V8.0.3 +---------------------- + +- Methods astRebin, astRebinSeq, astResample and astTranGrid now report an +error if an array is specified that has more pixels than can be counted by +a 32 bit integer. + +- The hypertext documentation is now generated using Tex4HT rather +than latex2html. The format of the hypertext docs has changed +significantly. + +- Another bug fix associated with reading CAR projections from FITS-WCS headers. + +- Constructor options strings of the form "..., "%s", text );" can now be +supplied. This avoids a security issue associated with the alternative +form "..., text );". + + +Main Changes in V8.0.2 +---------------------- + +- For security reasons, the change introduced to astAppendString in + V8.0.1 has been moved to a new function called astAppendStringf, and + astAppendString itself has been reverted to its V8.0.0 version. + + +Main Changes in V8.0.1 +---------------------- + +- The macro used to invoke the astAppendString utility function has + changed to allow printf-style converstions to be included in the + supplied text. Any code that uses this macro must be re-compiled. + +- The astRebin and astRebinSeq family of functions now include support + for arrays with char (byte) and unsigned char (unsigned byte) data types. + +- The Base and Current attributes of a FrameSet may now be set using the + Domain name or the index of the required Frame. + +- The FITS XPH projection is now supported. + +- The order of WCS axes within new FITS-WCS headers created by astWrite + can now be controlled using a new attribute called FitsAxisOrder. + +Main Changes in V8.0.0 +---------------------- + +- AST is now distributed under the Lesser GPL licence. + +- Least squares fitting of N-dimensional polynomials is now done using +files copied from the C/C++ Minpack package (see +http://devernay.free.fr/hacks/cminpack/index.html). + +- Use of the IAU SOFA library has been replaced by ERFA library, which is +a re-badged copy of SOFA distributed under a less restrictive license. A +copy of ERFA is included within AST. + +Main Changes in V7.3.4 +---------------------- + +- By default, the simplification of Polygons no longer checks that the +edges are not bent by the simplification. A new attribute, SimpVertices, +can be set to zero in order to re-instate this check. + +- The Polygon class has a new mathod, astConvex, that returns a Polygon +representing the shortest polygon (i.e. convex hull) enclosing a +specified set of pixel values within a supplied array. + +Main Changes in V7.3.3 +---------------------- + +- The FitsChan class has new attributes CardName and CardComm, which hold +the keyword name and comment of the current card. + +- When reading FITS-WCS headers that include polynomial distortion in the +SIP format, any inverse transformation specified in the header is now +ignored and a new inverse is created to replace it based on the supplied +forward transformation. Previously, an inverse was created only if the +header did not include an inverse. The accuracy of the inverse +transformation has also been improved, although it may now be slower to +evaluate in some circumstances. + +- A bug has been fixed that could over-write the FitsChan CarLin attribute +with a non-zero value if the header contains a spectral axis. + +- The default options for each newly created FitsChan can now be +specified via the environment variable FITSCHAN_OPTIONS. + +Main Changes in V7.3.2 +---------------------- + +- Fix support for reading GLS projections from FITS headers. + +- The KeyMap class has new sorting options "KeyAgeUp" and "KeyAgeDown" that +retain the position of an existing entry if its value is changed. See the +SortBy attribute. + +- A bug has been fixed in FitsChan that caused CDELT keywords for sky +axes to be treated as radians rather than degrees when reading a FITS +header, if the corresponding CTYPE values included no projection code. + + +Main Changes in V7.3.1 +---------------------- + +- Fix bug that could cause a segmenatation fault when reading a FITS TNX +header. + +Main Changes in V7.3.0 +---------------------- + +- IMPORTANT! The interface for the astRebinSeq (AST_REBINSEQ) family +of functions has been changed in order to allow a greater number of +pixels to be pasted into the output array. In C, the "nused" parameter +is now a pointer to a "int64_t" variable, instead of a simple "int". In +Fortran, the NUSED argument for AST_REBINSEQ is now an INTEGER*8. + +APPLICATION CODE SHOULD BE CHANGED ACCORDINGLY TO AVOID SEGMENTATION +FAULTS AND OTHER ERRATIC BEHAVIOUR. + +- Added a new facility to the FrameSet class to allow each Frame to be +associated with multiple Mappings, any one of which can be used to +connect the Frame to the other Frames in the FrameSet. The choice of +which Mapping to use is controlled by the new "Variant" attribute of the +FrameSet class. + +- Mappings (but not Frames) that have a value set for their Ident attribute +are now left unchanged by the astSimplify (AST_SIMPLIFY) function. + +Main Changes in V7.2.0 +---------------------- + +- A new method call astMapDefined has been added to the KeyMap class. +It checks if a gtiven key name has a defined value in a given KeyMap. + + +Main Changes in V7.1.1 +---------------------- + +- A bug has been fixed in FitsChan that caused inappropriate CTYPE values +to be generated when writing a FrameSet to FITS-WCS headers if the +current Frame describes generalised spherical coordinates (i.e. a +SkyFrame with System=Unknown). + +- When a FitsChan is used to write an "offset" SkyFrame (see attribute +SkyRefIs) to a FITS-WCS encoded header, two alternate axis descriptions +are now created - one for the offset coordinates and one for the absolute +coordinates. If such a header is subsequently read back into AST, the +original offset SkyFrame is recreated. + + +Main Changes in V7.1.0 +---------------------- + +- IMPORTANT! The default behaviour of astRebinSeq is now NOT to conserve +flux. To conserve flux, the AST__CONSERVEFLUX flag should be supplied +when calling astRebinSeq. Without this flag, each output value is a +weighted mean of the neighbouring input values. + +- A new flag AST__NONORM can be used with astRebinSeq to indicate that +normalisation of the output arrays is not required. In this case no +weights array need be supplied. + +- A bug has been fixed in astAddFrame (AST_ADDFRAME) method that could +result in the incorrect inversion of Mappings within the FrameSet when +the AST__ALLFRAMES flag is supplied for the "iframe" parameter. + +- The astRate method has been re-written to make it faster and more +reliable. + +Main Changes in V7.0.6 +---------------------- + +- A bug has been fixed in astRebinSeq which could result in +incorrect normalisation of the final binned data and variance values. + +- When reading a FrameSet from a FITS-DSS header, the keywords CNPIX1 and +CNPIX2 now default to zero if absent. Previously an error was reported. + +Main Changes in V7.0.5 +---------------------- + +- The FitsChan class can now read FITS headers that use the SAO +convention for representing distorted TAN projections, based on the use +of "COi_j" keywords to hold the coefficients of the distortion polynomial. + + +Main Changes in V7.0.4 +---------------------- + +- The previously private grf3d.h header file is now installed into +prefix/include. + + +Main Changes in V7.0.3 +---------------------- + +- A bug has been fixed which could cause an incorrect axis to be used when +accessing axis attributes within CmpFrames. This could happen if axes +within the CmpFrame have been permuted. + +- A bug has been fixed in the SkyFrame class that could cause the two +values of the SkyRef and/or SkyRefP attributes to be reversed. + +- Bugs have been fixed in the CmpRegion class that should allow the border +around a compound Region to be plotted more quickly, and more accurately. +Previously, component Regions nested deeply inside a CmpRegion may have +been completely or partially ignored. + +- A bug has been fixed in the Plot3D class that caused a segmentation +violation if the MinTick attribute was set to zero. + +- The astResampleX set of methods now includes astResampleK and +astResampleUK that handles 64 bit integer data. + +Main Changes in V7.0.2 +---------------------- + +- The libast_pal library is no longer built if the "--with-external_pal" +option is used when AST is configured. + + +Main Changes in V7.0.1 +---------------------- + +- The levmar and wcslib code distributed within AST is now stored in the +main AST library (libast.so) rather than in separate libraries. + + +Main Changes in V7.0.0 +---------------------- + +- Fundamental positional astronomy calculations are now performed +using the IAU SOFA library where possible, and the Starlink PAL library +otherwise (the PAL library contains a subset of the Fortran Starlink SLALIB +library re-written in C). Copies of these libraries are bundled with AST +and so do not need to be obtained or built separately, although external +copies of SOFA and PAL can be used if necessary by including the +"--with-external_pal" option when configuring AST. + + +Main Changes in V6.0-1 +----------------------- + +- The Spitzer "-SIP" distortion code is now recognised within FITS +headers that describe non-celestial axes, as well as celestial axes. + +- A bug has been fixed that could cause inappropriate equinox values to +be used when aligning SkyFrames if the AlignSystem attribute is set. + +- The format of the version string for AST has changed from +".-" to "..". + +Main Changes in V6.0 +----------------------- + +- This version of AST is the first that can be used with the Python +AST wrapper module, starlink.Ast, available at http://github.com/timj/starlink-pyast. + +- When reading a FITS-WCS header, the FitsChan class now recognises the +non-standard "TPV" projection code within a CTYPE keyword value. This +code is used by SCAMP (see www.astromatic.net/software/scamp) to +represent a distorted TAN projection. + +- The Plot class has been changed to remove visual anomalies (such as +incorrectly rotated numerical axis labels) if the graphics coordinates have +unequal scales on the X and Y axes. + +- The graphics escape sequences used to produce graphical sky axis labels +can now be changed using the new function astTuneC (AST_TUNEC). + +Main Changes in V5.7-2 +----------------------- + +- The PolyMap class can now use an iterative Newton-Raphson method to +evaluate the inverse the inverse transformation if no inverse +transformation is defined when the PolyMap is created. + +- The FitsChan class has a new method astWriteFits (AST_WRITEFITS) +which writes out all cards currently in the FitsChan to the associated +external data sink (specified either by the SinkFile attribute or the +sink function supplied when the FitsChan was created), and then empties +the FitsChan. + +- The FitsChan class has a new method astReadFits (AST_READFITS) +which forces the FitsChan to reads cards from the associated external +source and appends them to the end of the FitsChan. + +- The FitsChan class has a new read-only attribute called "Nkey", which +holds the number of keywords for which values are held in a FitsChan. + +- The FitsChan class has a new read-only attribute called "CardType", which +holds the data type of the keyword value for the current card. + +- The FitsChan astGetFits (AST_GETFITS) methods can now be used to +returned the value of the current card. + +- If the FitsChan astRead method reads a FITS header that uses the +-SIP (Spitzer) distortion code within the CTYPE values, but which does +not provide an inverse polynomial correction, and for which the PolyTran +method of the PolyMap class fails to create an accurate estimate of the +inverse polynomial correction, then an iterative method will be used to +evaluate the inverse correction for each point transformed. + +- The Object class has a new function astToString (C only), which creates +an in-memory textual serialisation of a given AST Object. A corresponding +new function called astFromString re-creates the Object from its +serialisation. + + +Main Changes in V5.7-1 +----------------------- + +- All classes of Channel can now read to and write from specified text +files, without the need to provide source and sink functions when the +Channel is created. The files to use are specified by the new attributes +SourceFile and SinkFile. + +- The FitsChan class now ignores trailing spaces in character-valued WCS +keywords when reading a FrameSet from a FITS header. + +- If the FitsChan astRead method reads a FITS header that uses the -SIP +(Spitzer) distortion code within the CTYPE values, but which does not +provide an inverse polynomial correction, the FitsChan class will now use +the PolyTran method of the PolyMap class to create an estimate of the +inverse polynomial correction. + + + +Main Changes in V5.7-0 +----------------------- + +- The FitsChan class support for the IRAF-specific "TNX" projection has +been extended to include reading TNX headers that use a Chebyshev +representation for the distortion polynomial. + +- The FitsChan class support for the IRAF-specific "ZPX" projection has +been extended to include reading ZPX headers that use simple or Chebyshev +representation for the distortion polynomial. + +- A bug has been fixed in the FitsChan class that caused headers +including the Spitzer "-SIP" distortion code to be read incorrectly if no +inverse polynomial was specified in the header. + +- A new attribute called PolyTan has been added to the FitsChan class. It +can be used to indicate that FITS headers that specify a TAN projection +should be interpreted according to the "distorted TAN" convention +included in an early draft of FITS-WCS paper II. Such headers are created +by (for instance) the SCAMP tool (http://www.astromatic.net/software/scamp). + +- The PolyMap class now provides a method called astPolyTran (AST_POLYTRAN) +that adds an inverse transformation to a PolyMap by sampling the forward +transformation on a regular grid, and then fitting a polynomial function +from the resulting output values to the grid of input values. + + +Main Changes in V5.6-1 +----------------------- + +- Tables can now have any number of parameters describing the global +properties of the Table. + +- Frames now interpret the unit string "A" as meaning "Ampere" rather +than "Angstrom", as specified by FITS-WCS paper I. + +- A bug has been fixed in the astFindFrame (AST_FINDFRAME) method that +allowed a template Frame of a more specialised class to match a target +frame of a less specialised class. For example, this bug would allow a +template SkyFrame to match a target Frame. This no longer happens. + + +Main Changes in V5.6-0 +----------------------- + +- New functions astBBuf (AST_BBUF) and astEBuf (AST_EBUF) have been added +to the Plot class. These control the buffering of graphical output +produced by other Plot methods. + +- New functions astGBBuf and astGEBuf have been added to the interface +defined by file grf.h. The ast_link command has been modified so that the +-grf_v3.2 switch loads dummy versions of the new grf functions. This +means that applications that use the -grf_v3.2 switch should continue to +build without any change. However, the new public functions astBBuf and +astEBuf described in the previous item will report an error unless the +new grf functions are implemented. If you choose to implement them, you +should modify your linking procedure to use the -grf (or -grf_v5.6) +switch in place of the older -grf_v3.2 switch. See the description of the +ast_link command for details of these switches. + +- New method astGetRegionMesh (AST_GETREGIONMESH) returns a set of +positions covering the boundary, or volume, of a supplied Region. + +Main Changes in V5.5-0 +----------------------- + +- The FitsChan "TabOK" attribute is now an integer value rather +than a boolean value. As in previous versions, it is used to indicate +whether the "-TAB" algorithm should be supported by the astRead +(AST_READ) and astWrite (AST_WRITE) methods, but in addition it is now +also used to give the version number to assign to any table gebnerated as +a consequence of calling astWrite (AST_WRITE). A negative or zero value +(the default) indicates that support for the -TAB algorithm is not +available, where as a positive non-zero value indicates that support is +available and also gives the table version number to use when creating +subsequent -TAB headers. + + +Main Changes in V5.4-0 +----------------------- + +- The FitsChan class now has an option to support reading and writing +of FITS-WCS headers that use the -TAB algorithm described in FITS-WCS paper +III. This option is controlled by a new FitsChan attribute called TabOK. +See the documentation for TabOK for more information. + +- A new class called "Table" has been added. A Table is a KeyMap in +which each entry represents a cell in a two-dimensional table. + +- A new class called "FitsTable" has been added. A FitsTable is a +Table that has an associated FitsChan holding headers appropriate to a +FITS binary table. + +- KeyMaps can now hold byte (i.e. "unsigned char" or BYTE) values. + +- A new method called astMapRename (AST_MAPRENAME) has been added to rename +an existing entry in a KeyMap. + +- KeyMaps have a new attribute called KeyCase that can be set to zero to +make the handling of keys case insensitive. + +Main Changes in V5.3-2 +----------------------- + +- A bug has been fixed in the FitsChan class that could cause wavelength +axes to be assigned the units "m/s" when reading WCS information from a +FITS header. + +- The astSet function (AST_SET) now allows literal commas to be included in +string attribute values. String attribute values that include a literal +comma should be enclosed in quotation marks. + +- A bug in FitsChan has been fixed that caused "-SIN" projection codes within +FITS-WCS headers to be mis-interpreted, resulting in no FrameSet being +read by astRead. + +- The KeyMap class has a new attribute called "SortBy". It controls +the order in which keys are returned by the astMapKey (AST_MAPKEY) function. +Keys can be sorted alphabetically or by age, or left unsorted. + +- Access to KeyMaps holding thousands of entries is now significantly +faster. + +- KeyMaps can now hold word (i.e. "short int" or INTEGER*2) values. + +Main Changes in V5.3-1 +----------------------- + +- The KeyMap class has a new method called astMapCopy/AST_MAPCOPY that +copies entries from one KeyMap to another KeyMap. + +- The KeyMap class now supports entries that have undefined values. A +new method called astMapPutU/AST_MAPPUTU will store an entry with undefined +value in a keymap. + +- The KeyMap class has a new boolean attribute called MapLocked. If true +(non-zero), an error is reported if an attempt is made to add any new entries +to a KeyMap (the value associated with any old entry may still be changed # +without error). The default is false (zero). + +- The Object class has a new method called astHasAttribute/AST_HASATTRIBUTE +that returns a boolean value indicating if a specified Object has a named +attribute. + +- The SkyFrame class has two new read-only boolean attributes called +IsLatAxis and IsLonAxis that can be used to determine the nature of a +specified SkyFrame axis. + +- A bug has been fixed in the astRebin(Seq)/AST_REBIN(SEQ) methods +that could cause flux to be lost from the edges of the supplied array. + +- A bug has been fixed in the astRebin(Seq)/AST_REBIN(SEQ) methods +that caused the first user supplied parameter to be interpreted as the +full width of the spreading kernel, rather than the half-width. + +- The StcsChan class now ignores case when reading STC-S phrases (except +that units strings are still case sensitive). + +- The Channel class now has an Indent attribute that controls indentation +in the text created by astWrite/AST_WRITE. The StcsIndent and XmlIndent +attributes have been removed. + +- All classes of Channel now use the string "" to represent the +floating point value AST__BAD, rather than the literal formatted value +(typically "-1.79769313486232e+308" ). + +- The KeyMap class now uses the string "" to represent the +floating point value AST__BAD, rather than the literal formatted value +(typically "-1.79769313486232e+308" ). + +- The KeyMap class has a new method called astMapPutElem/AST_MAPPUTELEM +that allows a value to be put into a single element of a vector entry in +a KeyMap. The vector entry is extended automatically to hold the new +element if required. + +- The DSBSpecFrame class now reports an error if the local oscillator +frequency is less than the absoliute value of the intermediate frequency. + +- A new method astQuadApprox produces a quadratic fit to a 2D Mapping. + +- A new method astSkyOffsetMap produces a Mapping from absolute SkyFrame +coordinates to offset SkyFrame coordinates. + + +Main Changes in Version 5.3 +--------------------------- + +- The details of how a Frame is aligned with another Frame by the +astFindFrame and astConvert (AST_FINDFRAME and AST_CONVERT) functions +have been changed. The changes mean that a Frame can now be aligned with +an instance of a sub-class of Frame, so long as the number of axes and +the Domain values are consistent. For instance, a basic 2-dimensional +Frame with Domain "SKY" will now align succesfully with a SkyFrame, +conversion between the two Frames being achieved using a UnitMap. + +- The arrays that supply input values to astMapPut1 are now declared +"const". + +- Added method astMatchAxes (AST_MATCHAXES) to the Frame class. This +allows corresponding axes in two Frames to be identified. + +- The astAddFrame (AST_ADDFRAME) method can now be used to append one or +more axes to all Frames in a FrameSet. + + +Main Changes in Version 5.1 +--------------------------- + +- A new method called astSetFitsCM (AST_SETFITSCM) has been added to +the FitsChan class. It stores a pure comment card in a FitsChan (that +is, a card with no keyword name or equals sign). + +- A new attribute called ObsAlt has been added to the Frame class. It +records the geodetic altitude of the observer, in metres. It defaults to +zero. It is used when converting times to or from the TDB timescale, or +converting spectral positions to or from the topocentric rest frame, or +converting sky positions to or from horizon coordinates. The FitsChan +class will include its effect when creating a set of values for the +OBSGEO-X/Y/Z keywords, and will also assign a value to it when reading a +set of OBSGEO-X/Y/Z keyword values from a FITS header. + +- The TimeMap conversions "TTTOTDB" and "TDBTOTT", and the SpecMap +conversions "TPF2HL" and "HLF2TP", now have an additional argument - +the observer's geodetic altitude. + +- The Polygon class has been modified to make it consistent with the +IVOA STC definition of a Polygon. Specifically, the inside of a polygon +is now the area to the left of each edge as the vertices are traversed in +an anti-clockwise manner, as seen from the inside of the celestial sphere. +Previously, AST used the anti-clockwise convention, but viewed from the +outside of the celestial sphere instead of the inside. Any Polygon saved +using previous versions of AST will be identified and negated automatically +when read by AST V5.2. + +- A new class of Channel, called StcsChan, has been added that allows +conversion of suitable AST Objects to and from IVOA STC-S format. + +- A new method called astDownsize (AST_DOWNSIZE) has been added to the +Polygon class. It produces a new Polygon that contains a subset of the +vertices in the supplied Polygon. The subset is chosen to retain the main +features of the supplied Polygion, in so far as that is possible, within +specified constraints. + +- A new constructor called astOutline (AST_OUTLINE) has been added to the +Polygon class. Given a 2D data array, it identifies the boundary of a +region within the array that holds pixels with specified values. It then +creates a new Polygon to describe this boundary to a specified accuracy. + +- A new method called astRemoveRegions (AST_REMOVEREGIONS) has been added +to the Mapping class. It removes the masking effects of any Regions found +within a (possibly compound) Mapping or Frame. In effect, it replaces +each Region found within the Mapping or Frame with a UnitMap or +equivalent Frame. + +- A new set of methods, called astMapGetElem (AST_MAPGETELEM) has +been added to the KeyMap class. They allow a single element of a vector +valued entry to be returned. + +- A new attribute called KeyError has been added to the KeyMap Class. It +controls whether the astMapGet... (AST_MAPGET...) family of functions report +an error if an entry with the requested key does not exist in the KeyMap. + +Main Changes in Version 5.1 +--------------------------- + +- The astUnlock function now has an extra parameter that controls whether +or not an error is reported if the Object is currently locked by another +thread. + +- The values of the AST__THREADSAFE macro (defined in ast.h) have +been changed from "yes" and "no" to "1" and "0". + +- The PointList class has a new method, astPoints, that copies the axis +values from the PointList into a supplied array. + +- The PointList class has a new (read-only) attribute, ListSize, that +gives the number of points stored in the PointList. + +- A new method (astIntersect) has been added to the Frame class. It +determines the position at which two geodesic curves intersect. + +- The XmlStrict attribute and astXmlWarnings function have been removed. +The same functionality is now available via the existing Strict attribute, +a new attribute called ReportLevel, and a new function called astWarnings. + +- A bug in the type-checking of Objects passed as arguments to constructor +functions has been fixed. This bug could lead to applications crashing or +showing strange behaviour if an inappropriate class of Object was +supplied as an argument to a constructor. + +- The astPickAxes function will now return a Region, if possible, when +applied to a Region. If this is not possible, a Frame will be returned as +before. + +- The default gap size between the ISO date/time labels used by the Plot +class when displaying an annotated axis described by a TimeFrame has been +changed. The changes are meant to improve the labelling of calendar time +axes that span intervals from a day to a few years. + +Main Changes in Version 5.0 +--------------------------- + +- AST is now thread-safe. Many of the macro definitions in the "ast.h" +header file have changed, and so all source code that include "ast.h" +should be re-compiled. + +- The TimeFrame class now support Local Time as a time scale. The offset +from UTC to Local Time is specified by a new TimeFrame attribute called +LTOffset. + +- Addition of a new class called Plot3D that provides facilities for +producing 3-dimensional annotated coordinate grids. + +- A correction for diurnal aberration is now included when +converting between AZEL and other celestial coordinate systems. The +correction is based on the value of the ObsLat Frame attribute (the +geodetic latitude of the observer). + +- A bug has been fixed which caused the DUT1 attribute to be ignored +by the SkyFrame class when finding conversions between AZEL and other +celestial coordinate systems. + +- The Channel class has a new attribute called Strict which controls +whether or not to report an error if unexpected data items are found +within an AST Object description read from an external data source. Note, +the default behaviour is now not to report such errors. This differs from +previous versions of AST which always reported an error is unexpected +input items were encountered. + + + +Main Changes in Version 4.5 +--------------------------- + +- All FITS-CLASS headers are now created with a frequency axis. If the +FrameSet supplied to astWrite contains a velocity axis (or any other form +of spectral axis) it will be converted to an equivalent frequency axis +before being used to create the FITS-CLASS header. + +- The value stored in the FITS-CLASS keyword "VELO-LSR" has been changed +from the velocity of the source to the velocity of the reference channel. + +- Addition of a new method call astPurgeWCS (AST_PURGEWCS) to the FitsChan +class. This method removes all WCS-related header cards from a FitsChan. + +- The astRebinSeq functions now have an extra parameter that is used to +record the total number of input data val;ues added into the output +array. This is necessary to correct a flaw in the calculation of output +variances based on the spread of input values. NOTE, THIS CHANGE WILL +REQUIRE EXISTING CODE THAT USES ASTREBINSEQ TO BE MODIFIED TO INCLUDE THE +NEW PARAMETER (CALLED "NUSED"). +- The Plot class now honours the value of the LabelUp attribute even if +numerical labels are placed around the edge of the Plot. Previously +LabelUp was only used if the labels were drawn within the interior of +the plot. The LabelUp attribute controls whether numerical labels are +drawn horizontally or parallel to the axis they describe. +- The Plot class has a new attribute called GrfContext that can be used +to comminicate context information between an application and any +graphics functions registered with the Plot class via the astGrfSet +(AST_GRFSET) function. +- Functions registered with the Plot class using astGrfSet (AST_GRFSET) +now take a new additional integer parameter, "grfcon". The Plot class +sets this parameter to value of the Plot's GrfContext attribute before +calling the graphics function. NOTE, THIS CHANGE WILL REQUIRE EXISTING +CODE THAT USES astGrfSet (AST_GRFSET) TO BE MODIFIED TO INCLUDE THE +NEW PARAMETER. +- Support has been added for the FITS-WCS "HPX" projection (HEALPix). +- A new flag "AST__VARWGT" can be supplied to astRebinSeq. This causes +the input data values to be weighted using the reciprocals of the input +variances (if supplied). +- The Frame class has a new read-only attribute called NormUnit that +returns the normalised value of the Unit attribute for an axis. Here, +"normalisation" means cancelling redundant units, etc. So for instance, a +Unit value of "s*(m/s)" would result in a NormUnit value of "m". +- A new method astShowMesh has been added to the Region class. It +displays a mesh of points covering the surface of a Region by writing out +a table of axis values to standard output. +- A bug has been fixed that could segmentation violations when setting +attribute values. + +Main Changes in Version 4.4 +--------------------------- + +- The astFindFrame (AST_FINDFRAME) method can now be used to search a +CmpFrame for an instance of a more specialised class of Frame (SkyFrame, +TimeFrame, SpecFrame, DSBSpecFrame or FluxFrame). That is, if an instance +of one of these classes is used as the "template" when calling +astFindFrame, and the "target" being searched is a CmpFrame (or a +FrameSet in which the current Frame is a CmpFrame), then the component +Frames within the CmpFrame will be searched for an instance of the +supplied template Frame, and, if found, a suitable Mapping (which will +include a PermMap to select the required axes from the CmpFrame) will be +returned by astFindFrame. Note, for this to work, the MaxAxes and MinAxes +attributes of the template Frame must be set so that they cover a range +that includes the number of axes in the target CmpFrame. + +- The DSBSpecFrame class has a new attribute called AlignSB that +specifies whether or not to take account of the SideBand attributes when +aligning two DSBSpecFrames using astConvert (AST_CONVERT). + +- The Frame class has a new attribute called Dut1 that can be used to +store a value for the difference between the UT1 and UTC timescales at +the epoch referred to by the Frame. + +- The number of digits used to format the Frame attributes ObsLat and +ObsLon has been increased. + +- The use of the SkyFrame attribute AlignOffset has been changed. This +attribute is used to control how two SkyFrames are aligned by astConvert. +If the template and target SkyFrames both have a non-zero value for +AlignOffset, then alignment occurs within the offset coordinate systems +(that is, a UnitMap will always be used to align the two SkyFrames). + +- The Plot class has a new attribute called ForceExterior that can be +used to force exterior (rather than interior) tick marks to be produced, +even if this would result in less than 3 tick marks being produced. + +- The TimeFrame class now supports conversion between angle based +timescales such as UT1 and atomic based timescales such as UTC. + + +Main Changes in Version 4.3 +--------------------------- + +- The SpecFrame class has a new attribute called SourceSys that specified +whether the SourceVel attribute (which specifies the rest frame of the +source) should be accessed as an apparent radial velocity or a redshift. +Note, any existing software that assumes that SourceVel always represents +a velocity in km/s should be changed to allow for the possibility of +SourceVel representing a redshift value. + +- The astGetFitsS (AST_GETFITSS) function now strips trailing white space +from the returned string, if the original string contains 8 or fewer +characters. + + +Main Changes in Version 4.2 +--------------------------- + +- The SideBand attribute of the DSBSpecFrame class can now take the +option "LO" in addition to "USB" and "LSB". The new option causes the +DSBSpecFrame to represent the offset from the local oscillator frequency, +rather than either of the two sidebands. + +- The FitsChan class has been changed so that it writes out a VELOSYS +keyword when creating a FITS-WCS encoding (VELOSYS indicates the +topocentric apparent velocity of the standard of rest). FitsChan also +strips out VELOSYS keywords when reading a FrameSet from a FITS-WCS +encoding. + +- The FitsChan class has a new method called astRetainFits (AST_RETAINFITS) +that indicates that the current card in the FitsChan should not be +stripped out of the FitsChan when an AST Object is read from the FitsChan. +Unless this method is used, all cards that were involved in the creation +of the AST Object will be stripped from the FitsChan afte a read operation. + +- The ast_link_adam and ast_link scripts now ignore the -fsla and -csla +options, and always link against the minimal cut-down version of SLALIB +distributed as part of AST. + +- A problem with unaligned memory access that could cause bus errors on +Solaris has been fixed. + +- A new function called astTune (or AST_TUNE) has been added which can be +used to get and set global AST tuning parameters. At the moment there are +only two such parameter, both of which are concerned with memory management +within AST. + +- A new method called astTranGrid (AST_TRANGRID in Fortran) has been +added to the Mapping class. This method creates a regular grid of points +covering a rectangular region within the input space of a Mapping, and +then transforms this set of points into the output space of the Mapping, +using a piecewise-continuous linear approximation to the Mapping if +appropriate in order to achive higher speed. + +- A new subclass of Mapping has been added called SwitchMap. A +SwitchMap represents several alternate Mappings, each of which is used to +transforms input positions within a different region of the input +coordinate space. + +- A new subclass of Mapping has been added called SelectorMap. A +SelectorMap tests each input position to see if it falls within one of +several Regions. If it does, the index of the Region containing the +input position is returned as the Mapping output. + +- The behaviour of the astConvert (AST_CONVERT) method when trying to +align a CmpFrame with another Frame has been modified. If no conversion +between positions in the Frame and CmpFrame can be found, an attempt is +now made to find a conversion between the Frame and one of two component +Frames contained within the CmpFrame. Thus is should now be possible to +align a SkyFrame with a CmpFrame containing a SkyFrame and a SpecFrame +(for instance). The returned Mapping produces bad values for the extra +axes (i.e. for the SpecFrame axis in the above example). + +Main Changes in Version 4.1 +--------------------------- + +- A new control flag has been added to the AST_RESAMPLE/astResample +functions which produces approximate flux conservation. + +- The SkyFrame class now supports a System value of "AZEL" corresponding +to horizon (azimuth/elevation) coordinates. + +- The FitsChan class allows the non-standard strings "AZ--" and "EL--" to +be used as axis types in FITS-WCS CTYPE keyword values. + +- The Frame class now has attributes ObsLon and ObsLat to specify +the geodetic longitude and latitude of the observer. + +- The ClockLon and ClockLat attributes have been removed from the +TimeFrame class. Likewise, the GeoLon and GeoLat attributes have been +removed from the SpecFrame class. Both classes now use the ObsLon and +ObsLat attributes of the parent Frame class instead. However, the old +attribute names can be used as synonyms for ObsLat and ObsLon. Also, +dumps created using the old scheme can be read succesfully by AST V4.1 +and converted to the new form. + +- A new function astMapSplit has been added to the Mapping class. This +splits a Mapping into two component Mappings which, when combined in +parallel, are equivalent to the original Mapping. + +- The default value for the SkyRefIs attribute has been changed from +"Origin" to "Ignored". This means that if you want to use a SkyFrame +to represent offsets from some origin position, you must now set the +SkyRefIs attribute explicitly to either "Pole" or "Origin", in addition +to assigning the required origin position to the SkyRef attribute. + + +Main Changes in Version 4.0 +--------------------------- + +- Experimental support for reading IVOA Space-Time-Coordinates (STC) +descriptions using the XmlChan class has been added. Support is included +for a subset of V1.20 of the draft STC specification. + +- A new set of methods (AST_REBIN/astRebin) has been added to +the Mapping class. These are accurately flux-conserving alternatives to the +existing AST_RESAMPLE/astResample methods. + + +Main Changes in Version 3.7 +--------------------------- + +- Support for time coordinate systems has been introduced throught the +addition of two new classes, TimeFrame and TimeMap. The TimeFrame is a +1-dimensional Frame which can be used to describe moments in time (either +absolute or relative) in various systems (MJD, Julian Epoch, etc.) and +referred to various time scales (TAI, UTC, UT1, GMST, etc). The TimeMap +is a Mapping which can transform time values between these various +systems and time scales. + + +Main Changes in Version 3.6 +--------------------------- + +- If the Format attribute associated with an axis of a SkyFrame starts +with a percent character (%), then axis values are now formatted and +unformatted as a decimal radians value, using the Format syntax of a +simple Frame. + +- The Plot class has a new attribute called Clip which controls the +clipping performed by AST at the plot boundary. + +- The PolyMap class has been modified to allow a PolyMap to be written +succesfully to a FitsChan using Native encoding. + +- A mimimal cut down subset of the C version of SLALIB is now included +with the AST distribution and built as part of building AST. This means +that it is no longer necessary to have SLALIB installed separately at +your site. The SLALIB code included with AST is distrubuted under the +GPL. The default behaviour of the ast_link script is now to link with +this internal slalib subset. However, the ``-csla'' option can still be +used to force linking with an external full C SLALIB library. A new +option ``-fsla'' has been introduced which forces linking with the +external full Fortran SLALIB library. + + +Main Changes in Version 3.5 +--------------------------- + +- AST now provides facilities for representing regions of various +shapes within a coordinate system. The Region class provides general +facilities which are independent of the specific shape of region being +used. Various sub-classes of Region are also now available which provide +means of creating Regions of specific shape. Facilities provided by the +Region class include testing points to see if they are inside the +Region, testing two Regions for overlap, transforming Regions from one +coordinate system to another, etc. + +- A new class of 1-dimensional Frame called FluxFrame has been added which +can be used to describe various systems for describing ovserved value at a +single fixed spectral position. + +- A new class of 2-dimensional Frame called SpecFluxFrame has been added which +can be used to describe a 2-d frame spanned by a spectral position axis +and and an observed value axis. + +- A new class of Mapping called RateMap has been added. A RateMap encapsulates +a previously created Mapping. The inputs of the RateMap correspond to the +inputs of the encapsulated Mapping. All RateMaps have just a single +output which correspond to the rate of change of a specified output of +the encapsulated Mapping with respect to a specified input. + +- The SkyFrame class now supports a value of "J2000" for System. This +system is an equatorial system based on the mean dynamical equator and +equinox at J2000, and differs slightly from an FK5(J2000) system. + +- Methods have been added to the FitsChan class to allow values for named +keywords to be changed or added. + +- The parameter list for the astRate method of the Mapping class has been +modified. It no longer returns a second derivative estimate. Existing +code which uses the astRate (AST_RATE) method will need to be changed. diff --git a/ast_cpp.in b/ast_cpp.in new file mode 100644 index 0000000..c4fb206 --- /dev/null +++ b/ast_cpp.in @@ -0,0 +1,11 @@ + +# Replacement for the C pre-processor command "cpp" which is not +# always available. This uses the compiler command "cc" to do the same +# thing. Also, this reads from standard input (which "cc" won't do). +# +# The name of the CPP processor is substituted in by the ./configure script, +# based on the result of the AC_PROG_CPP test. + +cat >/tmp/ast_cpp_$$.c +@CPP@ /tmp/ast_cpp_$$.c +rm -f /tmp/ast_cpp_$$.c diff --git a/ast_dev b/ast_dev new file mode 100644 index 0000000..a073e9a --- /dev/null +++ b/ast_dev @@ -0,0 +1,86 @@ + +# N.B. the previous line should be blank. +#++ +# Name: +# ast_dev + +# Purpose: +# Create links to AST include files. + +# Type of Module: +# Shell script. + +# Description: +# This command creates (or removes) symbolic links in your current +# directory which refer to the AST include files. It is provided so +# that you may develop software which uses these files without having +# to know where they reside. + +# Invocation: +# ast_dev [option] + +# Arguments: +# option +# If no value is supplied for this argument, symbolic links to AST +# include files (for both Fortran and C) are created in your current +# directory. If the value ``remove'' is given, these links are +# removed. Any other value results in an error. + +# Examples: +# ast_dev +# Creates links to the AST include files in your current directory. +# ast_dev remove +# Removes any links to the AST include files from your current +# directory. + +# Copyright: +# Copyright (C) 1997-2006 Council for the Central Laboratory of the Research Councils + +# Authors: +# RFWS: R.F. Warren-Smith (STARLINK, RAL) +# DSB: David S. Berry (STARLINK) +# {enter_new_authors_here} + +# History: +# 11-NOV-1996 (RFWS): +# Original version. +# 18-NOV-1997 (RFWS): +# Adapted prologue for document extraction. +# 13-JUN-2001 (DSB): +# Added GRF_PAR. +# {enter_changes_here} + +# Bugs: +# {note_any_bugs_here} + +#-- + +# Implementation Notes: +# The pathname of the installation include directory (e.g. /star/include) +# must be edited into this script when it is installed. This is normally +# done by the makefile. + +# Interpret command line. + case "${1}" in + +# No arguments: create appropriate links. + '') + LINK INSTALL_INC/ast.h ast.h + LINK INSTALL_INC/ast_par AST_PAR + LINK INSTALL_INC/ast_err AST_ERR + LINK INSTALL_INC/grf_par GRF_PAR + ;; + +# Argument is "remove": delete links. + remove) + rm -f ast.h AST_PAR AST_ERR GRF_PAR + ;; + +# Any other argument is invalid: report an error. + *) + echo "ast_dev: invalid argument \"${1}\" given" >&2 + exit 1 + ;; + esac + +# End of script. diff --git a/ast_err.msg b/ast_err.msg new file mode 100644 index 0000000..540afe3 --- /dev/null +++ b/ast_err.msg @@ -0,0 +1,211 @@ +.TITLE AST_ERR +.FACILITY AST,1521/PREFIX=AST__ +!author R.F. Warren-Smith & D.S. Berry (STARLINK) + +.SEVERITY ERROR +.BASE 300 + +ATGER +ATSER +ATTIN +AXIIN +BADAT +BADBX +BADIN +BADNI +BADNO +BADPW +BADSM +BADWM +BDBRK +BDFMT +BDFTS +BDOBJ +CLPAX +CORNG +CVBRK +DIMIN +DTERR +ENDIN +EOCHN +EXPIN +FCRPT +FMTER +FRMIN +FRSIN +FTCNV +GRFER +INHAN +INNCO +INTER +INTRD +KYCIR +LDERR +LUTII +LUTIN +MEMIN +MTR23 +MTRAX +MTRML +MTRMT +NAXIN +NCHIN +NCOIN +NCPIN +NELIN +NOCTS +NODEF +NOFTS +NOMEM +NOPTS +NOWRT +NPTIN +OBJIN +OPT +PDSIN +PLFMT +PRMIN +PTRIN +PTRNG +RDERR +REGIN +REMIN +SCSIN +SELIN +SLAIN +TRNND +UNMQT +VSMAL +WCSAX +WCSNC +WCSPA +WCSTY +XSOBJ +ZOOMI + +! New codes introduced for V1.1. +BADCI +ILOST +ITFER +ITFNI +MBBNF +MRITF +OCLUK +UNFER +URITF + +! New codes introduced for V1.2. +GBDIN +NGDIN +PATIN +SISIN +SSPIN + +! New codes introduced for V1.3. +UINER +UK1ER + +! New codes introduced for V1.4. +COMIN +CONIN +DUVAR +INNTF +MIOPA +MIOPR +MISVN +MLPAR +MRPAR +NORHS +UDVOF +VARIN +WRNFA + +! New codes introduced for V2.0. +BADUN +NORSF +NOSOR +SPCIN + +! New codes introduced for V3.1 +XMLNM +XMLCM +XMLPT +XMLIT +XMLWF + +! New codes introduced for V3.2 +ZERAX + +! New codes introduced for V3.3 +BADOC + +! New codes introduced for V3.5-2 +MPGER +MPIND + +! New codes introduced for V3.6 +REGCN +NOVAL +INCTS +TIMIN +STCKEY +STCIND + +! New codes introduced for V4.0 +CNFLX +TUNAM +BDPAR + + +! New codes introduced for V4.6 +3DFSET +PXFRRM +BADSUB +BADFLG + +! New codes introduced for V5.0 +LCKERR +FUNDEF + +! New codes introduced for V5.2 +MPVIN +OPRIN +NONIN +MPKER +MPPER +BADKEY + +! New codes introduced for V5.4 +BADTYP +OLDCOL +BADNULL +BIGKEY +BADCOL +BIGTAB +BADSIZ +BADTAB +NOTAB + +! New codes introduced for V5.7 +LEVMAR +NOFIT +ISNAN +WRERR + +! New codes introduced for V7.3 +BDVNM +MIRRO + +! New codes introduced for V8.0 +MNPCK + +! New codes introduced for V8.0.3 +EXSPIX + +! New codes introduced for V8.0.4 +NOCNV + +! New codes introduced for V8.2.0 +IMMUT + +.END diff --git a/ast_link.in b/ast_link.in new file mode 100644 index 0000000..c5759e2 --- /dev/null +++ b/ast_link.in @@ -0,0 +1,463 @@ + +# N.B. the previous line should be blank. +#++ +# Name: +# ast_link + +# Purpose: +# Link a program with the AST library. + +# Type of Module: +# Shell script. + +# Description: +# This command should be used when building programs which use the AST +# library, in order to generate the correct arguments to allow the compiler +# to link your program. The arguments generated are written to standard +# output but may be substituted into the compiler command line in the +# standard UNIX way using backward quotes (see below). +# +# By default, it is assumed that you are building a stand-alone program +# which does not produce graphical output. However, switches are provided +# for linking other types of program. + +# Invocation: +#c cc program.c -L/star/lib `ast_link [switches]` -o program +#f f77 program.f -L/star/lib `ast_link [switches]` -o program + +# Switches: +# The following switches may optionally be given to this command to +# modify its behaviour: +# +# +# - ``-csla'': Ignored. Provided for backward compatibility only. +# +# - ``-fsla'': Ignored. Provided for backward compatibility only. +# +# - ``-ems'': Requests that the program be linked so that error messages +# produced by the AST library are delivered via the Starlink EMS (Error +# Message Service) library (Starlink System Note SSN/4). By default, +# error messages are simply written to standard error. +# +# - ``-drama'': Requests that the program be linked so that error messages +# produced by the AST library are delivered via the DRAMA Ers (Error +# Reporting Service) library. By default, error messages are simply +# written to standard error. +# +# - ``-grf'': Requests that no arguments be generated to specify which +# 2D graphics system is used to display output from the AST library. You +# should use this option only if you have implemented an interface to a +# new graphics system yourself and wish to provide your own arguments for +# linking with it. This switch differs from the other ``grf'' switches in +# that it assumes that your graphics module implements the complete +# interface required by the current version of AST. If future versions of +# AST introduce new functions to the graphics interface, this switch will +# cause ``unresolved symbol'' errors to occur during linking, warning you +# that you need to implement new functions in your graphics module. To +# avoid such errors, you can use one of the other, version-specific, +# switches in place of the ``-grf'' switch, but these will cause run-time +# errors to be reported if any AST function is invoked which requires +# facilities not in the implemented interface. +# +# - ``-grf_v2.0'': This switch is equivalent to the ``-mygrf'' switch. +# It indicates that you want to link with your own graphics module +# which implements the 2D graphics interface required by V2.0 of AST. +# +# - ``-grf_v3.2'': Indicates that you want to link with your own +# graphics module which implements the 2D graphics interface required by +# V3.2 of AST. +# +# - ``-grf_v5.6'': Indicates that you want to link with your own +# graphics module which implements the 2D graphics interface required by +# V5.6 of AST. +# +# - ``-myerr'': Requests that no arguments be generated to specify how +# error messages produced by the AST library should be delivered. You +# should use this option only if you have implemented an interface to a +# new error delivery system yourself and wish to provide your own +# arguments for linking with it. +# +# - ``-mygrf'': This switch has been superceeded by the ``-grf'' switch, +# but is retained in order to allow applications to be linked with a +# graphics module which implements the 2D interface used by AST V2.0. It +# is equivalent to the ``-grf_v2.0'' switch. +# +# - ``-pgp'': Requests that the program be linked so that 2D +# graphical output from the AST library is displayed via the +# Starlink version of the PGPLOT graphics package (which uses GKS +# for its output). By default, no 2D graphics package is linked and +# this will result in an error at run time if AST routines are +# invoked that attempt to generate graphical output. +# +# - ``-pgplot'': Requests that the program be linked so that 2D +# graphical output from the AST library is displayed via +# the standard (or ``native'') version of the PGPLOT graphics +# package. By default, no 2D graphics package is linked and this will +# result in an error at run time if AST routines are invoked that +# attempt to generate graphical output. +# +# - ``-grf3d'': Requests that no arguments be generated to specify which +# 3D graphics system is used to display output from the AST library. You +# should use this option only if you have implemented an interface to a +# new 3D graphics system yourself and wish to provide your own arguments +# for linking with it. +# +# - ``-pgp3d'': Requests that the program be linked so that 3D +# graphical output from the AST library is displayed via the +# Starlink version of the PGPLOT graphics package (which uses GKS +# for its output). By default, no 3D graphics package is linked and +# this will result in an error at run time if AST routines are +# invoked that attempt to generate graphical output. +# +# - ``-pgplot3d'': Requests that the program be linked so that 3D +# graphical output from the AST library is displayed via +# the standard (or ``native'') version of the PGPLOT graphics +# package. By default, no 3D graphics package is linked and this will +# result in an error at run time if AST routines are invoked that +# attempt to generate graphical output. + +# ERFA & PAL: +# The AST distribution includes bundled copies of the ERFA and PAL +# libraries. These will be used for fundamental positional astronomy +# calculations unless the "--with-external_pal" option was used when +# AST was configured. If "--with-external_pal" is used, this script +# will include "-lpal" in the returned list of linking options, and +# the user should then ensure that external copies of the PAL and +# ERFA libraries are available (ERFA functions are used within PAL). + +# Examples: +#c cc display.c -L/star/lib `ast_link -pgplot` -o display +#c Compiles and links a C program called ``display'' which uses +#c the standard version of PGPLOT for graphical output. +#c cc plotit.c -L. -L/star/lib `ast_link -grf` -lgrf -o plotit +#c Compiles and links a C program ``plotit''. The ``-grf'' +#c switch indicates that graphical output will be delivered through +#c a graphical interface which you have implemented yourself, which +#c corresponds to the interface required by the current version of AST. +#c Here, this interface is supplied by means of the ``-lgrf'' library +#c reference. +#c cc plotit.c -L. -L/star/lib `ast_link -grf_v2.0` -lgrf -o plotit +#c Compiles and links a C program ``plotit''. The ``-grf_v2.0'' +#c switch indicates that graphical output will be delivered through +#c a graphical interface which you have implemented yourself, which +#c corresponds to the interface required by version 2.0 of AST. +#c Here, this interface is supplied by means of the ``-lgrf'' library +#c reference. +#f f77 display.f -L/star/lib `ast_link -pgplot` -o display +#f Compiles and links a Fortran program called ``display'' which uses +#f the standard version of PGPLOT for graphical output. +#f f77 plotit.f -L. -L/star/lib `ast_link -grf` -lgrf -o plotit +#f Compiles and links a Fortran program ``plotit''. The ``-grf'' +#f switch indicates that graphical output will be delivered through +#f a graphical interface which you have implemented yourself, which +#f corresponds to the interface required by the current version of AST. +#f Here, this interface is supplied by means of the ``-lgrf'' library +#f reference. +#f f77 plotit.f -L. -L/star/lib `ast_link -grf_v2.0` -lgrf -o plotit +#f Compiles and links a Fortran program ``plotit''. The ``-grf_v2.0'' +#f switch indicates that graphical output will be delivered through +#f a graphical interface which you have implemented yourself, which +#f corresponds to the interface required by version 2.0 of AST. +#f Here, this interface is supplied by means of the ``-lgrf'' library +#f reference. + +# Copyright: +# Copyright (C) 1997-2006 Council for the Central Laboratory of the Research Councils +# Copyright (C) 2007-2008 Science & Technology Facilities Council. +# All Rights Reserved. + +# Authors: +# RFWS: R.F. Warren-Smith (STARLINK) +# DSB: David S. Berry (STARLINK) +# TIMJ: Tim Jenness (JAC, Hawaii) +# {enter_new_authors_here} + +# History: +# 11-JUN-1996 (RFWS): +# Original version. +# 11-NOV-1996 (RFWS): +# Added switches. +# 18-NOV-1997 (RFWS): +# Adapted prologue for document extraction. +# 28-SEP-1998 (RFWS): +# Distinguish between -pgp and -pgplot options. +# 12-JAN-2001 (DSB): +# Move terminating "}" in function "find" onto a new line to +# avoid error when run under bash 2.04.11(1) (redhat 7). +# 3-MAY-2001 (DSB): +# Added a terminating ";" to the "done" statement at the end of +# the "find" function, so that ast_link can be used on Debian Linux. +# 23-JAN-2004 (DSB): +# Added switches to support older grf implementations. +# 24-AUG-2004 (DSB): +# Removed f77='y' from -ems case. +# 21-APR-2005 (DSB): +# Added "-fsla" option. +# 16-JUN-2006 (DSB): +# Ignore "-fsla" and "-clsa" options, and always use PAL. +# 26-JUN-2007 (DSB): +# Added "-grf3d", "-pgplot3d" and "-pgp3d" flags. +# 13-NOV-2008 (TIMJ): +# Add -drama option for DRAMA Ers support. +# 3-MAR-2011 (DSB): +# Added grf 5.6 options. +# {enter_further_changes_here} + +# Bugs: +# {note_any_bugs_here} + +#-- + +# This line is edited during configuration of this script to define a list +# of the libraries that must be linked in order to resolve Fortran 77 +# references made from within a C main program. Typically, these will arise +# from libraries written in Fortran which the AST library (or the C +# program) calls. The value here is worked out by the autoconf macro +# AC_FC_LIBRARY_LDFLAGS. + flibs='@FCLIBS@' + +# This function searches the directory path specified in PATH, looking for +# an executable file which is not a directory. If found, it echos the full +# file name to standard output. Otherwise, it outputs nothing. + find() { IFS=':'; for d in $PATH; do f="${d:=.}/${1}" + test -x "${f}" -a ! -d "${f}" && echo "${f}" && break + done; + } + +# Initialise linking options. + err='' + grf='' + grf3d='' + sla='' + f77='' + +# Interpret command line switches. +# -------------------------------- + while :; do + case "${1}" in + +# -csla - Previously used to request C version of SLALIB. Now ignored. + -csla) +# sla='c' + shift;; + +# -fsla - Previously used to request Fortran version of SLALIB. Now ignored. + -fsla) +# sla='f' + shift;; + +# -ems - Requests error reporting through EMS. + -ems) + err='ems' + shift;; + +# -drama - Requests error reporting through DRAMA Ers. + -drama) + err='drama' + shift;; + +# -myerr - Requests no error reporting. + -myerr) + err='my' + shift;; + +# -grf - Requests no 2D graphics. + -grf) + grf='current' + shift;; + +# -mygrf - Requests no 2D graphics, except for null implementations of +# functions aded to the grf interface after AST V2.0. + -mygrf) + grf='v2.0' + shift;; + +# -grf_v2.0 - Requests no 2D graphics, except for null implementations of +# functions aded to the grf interface after AST V2.0. + -grf_v2.0) + grf='v2.0' + shift;; + +# -grf_v3.2 - Requests no 2D graphics, except for null implementations of +# functions aded to the grf interface after AST V3.2. + -grf_v3.2) + grf='v3.2' + shift;; + +# -grf_v5.6 - Requests no 2D graphics, except for null implementations of +# functions aded to the grf interface after AST V5.6. + -grf_v5.6) + grf='v5.6' + shift;; + +# -pgp - Requests 2D graphical output through Starlink PGPLOT. + -pgp) + grf='pgp' + shift;; + +# -pgplot - Requests 2D graphical output through native PGPLOT. + -pgplot) + grf='pgplot' + shift;; + +# -grf3d - Requests no 3D graphics. + -grf3d) + grf3d='current' + shift;; + +# -pgp3d - Requests 3D graphical output through Starlink PGPLOT. + -pgp3d) + grf3d='pgp' + shift;; + +# -pgplot3d - Requests 3D graphical output through native PGPLOT. + -pgplot3d) + grf3d='pgplot' + shift;; + +# Once all switches have been read, continue with the rest of the script. + '') break;; + +# Catch unrecognised arguments and report an error. + *) + echo >&2 "ast_link: unknown argument \""${1}"\" given" + exit 1;; + esac + done + +# Link with the main AST library. +# ------------------------------- +# Start forming the list of arguments with the main AST library itself. + args='-last ' + +# Generate arguments for linking PAL. +# ----------------------------------- + + case "@EXTERNAL_PAL@" in + +# If we configured --with-external_pal include a link option to pick up +# an external PAL library. + 1) args="${args} -lpal";; + +# Otherwise, use the internal PAL & ERFA libraries. + *) args="${args} -last_pal";; + + esac + +# Generate arguments for linking the 2D graphics system. +# ------------------------------------------------------ + case "${grf}" in + +# If using Starlink PGPLOT, link with the AST PGPLOT interface and +# the Fortran library via the PGP link script (if found). + pgp) args="${args} -last_pgplot `\`find pgp_link\``" + f77='y';; + +# If using native PGPLOT, link with the AST PGPLOT interface and the +# Fortran library via the PGPLOT link script (if found). + pgplot) args="${args} -last_pgplot `\`find pgplot_link\``" + f77='y';; + +# If using own graphics which conform to the requirements of the current +# version of AST, do not produce any arguments. + current) :;; + +# If using own graphics which conform to the requirements of version 5.6 +# of AST, produce arguments which link in dummy implementations of any +# functions which are required by the current version of AST but which were +# not required by version 5.6. + v5.6) :;; + +# If using own graphics which conform to the requirements of version 3.2 +# of AST, produce arguments which link in dummy implementations of any +# functions which are required by the current version of AST but which were +# not required by version 3.2. + v3.2) args="${args} -last_grf_5.6";; + +# If using own graphics which conform to the requirements of version 2.0 +# of AST, produce arguments which link in dummy implementations of any +# functions which are required by the current version of AST but which were +# not required by version 2.0. + v2.0) args="${args} -last_grf_3.2 -last_grf_5.6";; + +# Default graphics (none) requires linking with all the default (null) AST +# "grf" modules. + *) args="${args} -last_grf_2.0 -last_grf_3.2 -last_grf_5.6";; + esac + + +# Generate arguments for linking the 3D graphics system. +# ------------------------------------------------------ + case "${grf3d}" in + +# If using Starlink PGPLOT, link with the AST 3D PGPLOT interface and +# the Fortran library via the PGP link script (if found). + pgp) args="${args} -last_pgplot3d `\`find pgp_link\``" + f77='y';; + +# If using native PGPLOT, link with the AST 3D PGPLOT interface and the +# Fortran library via the PGPLOT link script (if found). + pgplot) args="${args} -last_pgplot3d `\`find pgplot_link\``" + f77='y';; + +# If using own 3D graphics which conform to the requirements of the current +# version of AST, do not produce any arguments. + current) :;; + +# Default graphics (none) requires linking with all the default (null) AST +# "grf3d" modules. + *) args="${args} -last_grf3d";; + esac + + + +# Make a second pass through the AST library. +# ------------------------------------------- +# This library is a link to the main AST library and results in a second +# pass to resolve any backward references generated by the other modules +# used above. A different library name must be used to avoid the two passes +# being merged into one (either below, or by other link scripts). + args="${args} -last_pass2" + +# Generate arguments for linking the error reporting system. +# ---------------------------------------------------------- + case "${err}" in + +# If using EMS, link with the AST EMS interface and the EMS library via the +# link script (if found). + ems) args="${args} -last_ems `\`find ems_link\``";; + +# If using DRAMA, link with the AST DRAMA interface and the DRAMA Ers library +# via the link script (if found). + drama) args="${args} -last_drama -lers";; + +# If using own error reporting, do not produce any arguments. + my) :;; + +# Default error reporting requires linking with the default AST "err" module. + *) args="${args} -last_err";; + esac + +# Link with the maths library. +# ---------------------------- + args="${args} -lm" + +# Link with the starmem library, if available. +# -------------------------------------------- + args="${args} `\`find starmem_link\``" + +# Resolve Fortran 77 references. +# ------------------------------ +# If libraries written in Fortran are being linked against, then include +# additional libaries needed to resolve the references these will produce +# (in the event that the main program is not Fortran). + if test "${f77}" = 'y'; then args="${args} ${flibs}"; fi + +# Pass the resulting argument list through an awk script which eliminates +# all except the last reference to each library. + echo "${args}" \ + | awk 'BEGIN{RS=" ";FS="\n"} + {if($1)f[i++]=$1} + END{for(;i--;)if(!w[f[i]]++)l=f[i]" "l;print l}' + +# End of script. diff --git a/ast_link_adam.in b/ast_link_adam.in new file mode 100644 index 0000000..df93c6c --- /dev/null +++ b/ast_link_adam.in @@ -0,0 +1,406 @@ + +# N.B. the previous line should be blank. +#++ +# Name: +# ast_link_adam + +# Purpose: +# Link an ADAM program with the AST library. + +# Type of Module: +# Shell script. + +# Description: +# This command should only be used when building Starlink ADAM programs +# which use the AST library, in order to generate the correct arguments +# to allow the ADAM ``alink'' command to link the program. The arguments +# generated are written to standard output but may be substituted into +# the ``alink'' command line in the standard UNIX way using backward +# quotes (see below). +# +# By default, it is assumed that you are building an ADAM program which +# does not produce graphical output. However, switches are provided for +# linking other types of program. This command should not be used when +# building stand-alone (non-ADAM) programs. Use the ``ast_link'' command +# instead. + +# Invocation: +#c alink program.o -L/star/lib `ast_link_adam [switches]` +#f alink program.f -L/star/lib `ast_link_adam [switches]` + +# Switches: +# The following switches may optionally be given to this command to +# modify its behaviour: +# +# - ``-csla'': Ignored. Provided for backward compatibility only. +# +# - ``-fsla'': Ignored. Provided for backward compatibility only. +# +# - ``-grf'': Requests that no arguments be generated to specify which +# 2D graphics system is used to display output from the AST library. You +# should use this option only if you have implemented an interface to a +# new graphics system yourself and wish to provide your own arguments for +# linking with it. This switch differs from the other ``grf'' switches in +# that it assumes that your graphics module implements the complete +# interface required by the current version of AST. If future versions of +# AST introduce new functions to the graphics interface, this switch will +# cause ``unresolved symbol'' errors to occur during linking, warning you +# that you need to implement new functions in your graphics module. To +# avoid such errors, you can use one of the other, version-specific, +# switches in place of the ``-grf'' switch, but these will cause run-time +# errors to be reported if any AST function is invoked which requires +# facilities not in the implemented interface. +# +# - ``-grf_v2.0'': This switch is equivalent to the ``-mygrf'' switch. +# It indicates that you want to link with your own graphics module which +# implements the 2D graphics interface required by V2.0 of AST. +# +# - ``-grf_v3.2'': Indicates that you want to link with your own graphics +# module which implements the 2D graphics interface required by V3.2 of AST. +# +# - ``-grf_v5.6'': Indicates that you want to link with your own graphics +# module which implements the 2D graphics interface required by V5.6 of AST. +# +# - ``-myerr'': Requests that no arguments be generated to specify how +# error messages produced by the AST library should be delivered. You +# should use this option only if you have implemented an interface to a +# new error delivery system yourself and wish to provide your own +# arguments for linking with it. By default, error messages are delivered +# in the standard ADAM way via the EMS Error Message Service (Starlink +# System Note SSN/4). +# +# - ``-mygrf'': This switch has been superceeded by the ``-grf'' switch, +# but is retained in order to allow applications to be linked with a +# graphics module which implements the interface used by AST V2.0. It is +# equivalent to the ``-grf_v2.0'' switch. +# +# - ``-pgp'': Requests that the program be linked so that 2D +# graphical output from the AST library is displayed via the +# Starlink version of the PGPLOT graphics package (which uses GKS +# for its output). By default, no graphics package is linked and +# this will result in an error at run time if AST routines are +# invoked that attempt to generate graphical output. +# +# - ``-pgplot'': Requests that the program be linked so that 2D +# graphical output from the AST library is displayed via the +# standard (or ``native'') version of the PGPLOT graphics +# package. By default, no graphics package is linked and this will +# result in an error at run time if AST routines are invoked that +# attempt to generate graphical output. +# +# - ``-grf3d'': Requests that no arguments be generated to specify which +# 3D graphics system is used to display output from the AST library. You +# should use this option only if you have implemented an interface to a +# new 3D graphics system yourself and wish to provide your own arguments +# for linking with it. +# +# - ``-pgp3d'': Requests that the program be linked so that 3D +# graphical output from the AST library is displayed via the +# Starlink version of the PGPLOT graphics package (which uses GKS +# for its output). By default, no 3D graphics package is linked and +# this will result in an error at run time if AST routines are +# invoked that attempt to generate graphical output. +# +# - ``-pgplot3d'': Requests that the program be linked so that 3D +# graphical output from the AST library is displayed via +# the standard (or ``native'') version of the PGPLOT graphics +# package. By default, no 3D graphics package is linked and this will +# result in an error at run time if AST routines are invoked that +# attempt to generate graphical output. + +# SLALIB: +# The AST distribution includes a cut down subset of the C version of +# the SLALIB library written by Pat Wallace. This subset contains only +# the functions needed by the AST library. It is built as part of the +# process of building AST and is distributed under GPL (and is thus +# compatible with the AST license). Previous version of this script +# allowed AST applications to be linked against external SLALIB +# libraries (either Fortran or C) rather than the internal version. +# The current version of this script does not provide this option, +# and always uses the internal SLALIB library. However, for backward +# compatibility, this script still allows the "-fsla" and "-csla" flags +# (previously used for selecting which version of SLALIB to use) to be +# specified, but they will be ignored. + +# Examples: +#c alink display.o -L/star/lib `ast_link_adam -pgplot` +#c Links an ADAM program ``display'' which uses the standard +#c version of PGPLOT for graphical output. +#c alink plotit.o -L. -L/star/lib `ast_link_adam -grf` -lgrf +#c Links an ADAM program ``plotit'', written in C. The ``-grf'' +#c switch indicates that graphical output will be delivered through +#c a graphical interface which you have implemented yourself, which +#c corresponds to the interface required by the current version of AST. +#c Here, this interface is supplied by means of the ``-lgrf'' library +#c reference. +#c alink plotit.o -L. -L/star/lib `ast_link_adam -grf_v2.0` -lgrf +#c Links an ADAM program ``plotit'', written in C. The ``-grf_v2.0'' +#c switch indicates that graphical output will be delivered through +#c a graphical interface which you have implemented yourself, which +#c corresponds to the interface required by version 2.0 of AST. Here, +#c this interface is supplied by means of the ``-lgrf'' library +#c reference. +#f alink display.f -L/star/lib `ast_link_adam -pgplot` +#f Compiles and links an ADAM Fortran program called ``display'' which +#f uses the standard version of PGPLOT for graphical output. +#f alink plotit.f -L. -L/star/lib `ast_link_adam -grf` -lgrf +#f Compiles and links an ADAM Fortran program ``plotit''. The ``-grf'' +#f switch indicates that graphical output will be delivered through +#f a graphical interface which you have implemented yourself, which +#f corresponds to the interface required by the current version of AST. +#f Here, this interface is supplied by means of the ``-lgrf'' library +#f reference. +#f alink plotit.f -L. -L/star/lib `ast_link_adam -grf_v2.0` -lgrf +#f Compiles and links an ADAM Fortran program ``plotit''. The ``-grf_v2.0'' +#f switch indicates that graphical output will be delivered through +#f a graphical interface which you have implemented yourself, which +#f corresponds to the interface required by version 2.0 of AST. +#f Here, this interface is supplied by means of the ``-lgrf'' library +#f reference. + +# Copyright: +# Copyright (C) 1997-2006 Council for the Central Laboratory of the Research Councils + +# Authors: +# RFWS: R.F. Warren-Smith (STARLINK) +# {enter_new_authors_here} + +# History: +# 11-NOV-1996 (RFWS): +# Original version. +# 18-NOV-1997 (RFWS): +# Adapted prologue for document extraction. +# 28-SEP-1998 (RFWS): +# Distinguish between -pgp and -pgplot options. +# 23-JAN-2004 (DSB): +# Added switches to support older grf implementations. +# 21-APR-2005 (DSB): +# Added "-fsla" option. +# 16-JUN-2006 (DSB): +# Ignore "-fsla" and "-clsa" options, and always use PAL. +# 22-AUG-2007 (DSB): +# Added "-grf3d", "-pgplot3d" and "-pgp3d" flags. +# 4-MAR-2011 (DSB): +# Added v5.6 grf options. +# {enter_changes_here} + +# Bugs: +# {note_any_bugs_here} + +#-- + +# This function searches the directory path specified in PATH, looking for +# an executable file which is not a directory. If found, it echos the full +# file name to standard output. Otherwise, it outputs nothing. + find() { IFS=':'; for d in $PATH; do f="${d:=.}/${1}" + test -x "${f}" -a ! -d "${f}" && echo "${f}" && break + done; + } + +# Initialise linking options. + err='' + grf='' + grf3d='' + sla='' + +# Interpret command line switches. +# -------------------------------- + while :; do + case "${1}" in + +# -csla - Previously used to request C version of SLALIB. Now ignored. + -csla) +# sla='c' + shift;; + +# -fsla - Previously used to request Fortran version of SLALIB. Now ignored. + -fsla) +# sla='f' + shift;; + +# -myerr - Requests no error reporting. + -myerr) + err='my' + shift;; + +# -grf - Requests no 2D graphics. + -grf) + grf='current' + shift;; + +# -mygrf - Requests no 2D graphics, except for null implementations of +# functions aded to the grf interface after AST V2.0. + -mygrf) + grf='v2.0' + shift;; + +# -grf_v2.0 - Requests no 2D graphics, except for null implementations of +# functions aded to the grf interface after AST V2.0. + -grf_v2.0) + grf='v2.0' + shift;; + +# -grf_v3.2 - Requests no 2D graphics, except for null implementations of +# functions aded to the grf interface after AST V3.2. + -grf_v3.2) + grf='v3.2' + shift;; + +# -grf_v5.6 - Requests no 2D graphics, except for null implementations of +# functions added to the grf interface after AST V5.6. + -grf_v5.6) + grf='v5.6' + shift;; + +# -pgp - Requests 2D graphical output through Starlink PGPLOT. + -pgp) + grf='pgp' + shift;; + +# -pgplot - Requests 2D graphical output through native PGPLOT. + -pgplot) + grf='pgplot' + shift;; + +# -grf3d - Requests no 3D graphics. + -grf3d) + grf3d='current' + shift;; + +# -pgp3d - Requests 3D graphical output through Starlink PGPLOT. + -pgp3d) + grf3d='pgp' + shift;; + +# -pgplot3d - Requests 3D graphical output through native PGPLOT. + -pgplot3d) + grf3d='pgplot' + shift;; + +# Once all switches have been read, continue with the rest of the script. + '') break;; + +# Catch unrecognised switches and report an error. + *) + echo >&2 "ast_link_adam: unknown argument \""${1}"\" given" + exit 1;; + esac + done + +# Link with the main AST library. +# ------------------------------- +# Start forming the list of arguments with the main AST library itself. + args='-last' + +# Generate arguments for linking PAL. +# ----------------------------------- + + case "@EXTERNAL_PAL@" in + +# If we configured --with-external_pal include a link option to pick up +# an external PAL library. + 1) args="${args} -lpal";; + +# Otherwise, use the internal PAL & ERFA libraries. + *) args="${args} -last_pal";; + + esac + +# Generate arguments for linking the 2D graphics system. +# ------------------------------------------------------ + case "${grf}" in + +# If using Starlink PGPLOT, link with the AST PGPLOT interface and +# the Fortran library via the PGP link script. + pgp) args="${args} -last_pgplot `pgp_link_adam`";; + +# If using native PGPLOT, link with the AST PGPLOT interface and +# the Fortran library via the PGPLOT link script. + pgplot) args="${args} -last_pgplot `pgplot_link_adam`";; + +# If using own graphics which conform to the requirements of the current +# version of AST, do not produce any arguments. + current) :;; + +# If using own graphics which conform to the requirements of version 5.6 +# of AST, produce arguments which link in dummy implementations of any +# functions which are required by the current version of AST but which were +# not required by version 5.6. + v5.6) :;; + +# If using own graphics which conform to the requirements of version 3.2 +# of AST, produce arguments which link in dummy implementations of any +# functions which are required by the current version of AST but which were +# not required by version 3.2. + v3.2) args="${args} -last_grf_5.6";; + +# If using own graphics which conform to the requirements of version 2.0 +# of AST, produce arguments which link in dummy implementations of any +# functions which are required by the current version of AST but which were +# not required by version 2.0. + v2.0) args="${args} -last_grf_3.2 -last_grf_5.6";; + +# Default graphics (none) requires linking with all the default (null) AST +# "grf" modules. + *) args="${args} -last_grf_2.0 -last_grf_3.2 -last_grf_5.6";; + esac + +# Generate arguments for linking the 3D graphics system. +# ------------------------------------------------------ + case "${grf3d}" in + +# If using Starlink PGPLOT, link with the AST 3D PGPLOT interface and +# the Fortran library via the PGP link script (if found). + pgp) args="${args} -last_pgplot3d `\`find pgp_link\``" + f77='y';; + +# If using native PGPLOT, link with the AST 3D PGPLOT interface and the +# Fortran library via the PGPLOT link script (if found). + pgplot) args="${args} -last_pgplot3d `\`find pgplot_link\``" + f77='y';; + +# If using own 3D graphics which conform to the requirements of the current +# version of AST, do not produce any arguments. + current) :;; + +# Default graphics (none) requires linking with all the default (null) AST +# "grf3d" modules. + *) args="${args} -last_grf3d";; + esac + +# Make a second pass through the AST library. +# ------------------------------------------- +# This library is a link to the main AST library and results in a second +# pass to resolve any backward references generated by the other modules +# used above. A different library name must be used to avoid the two passes +# being merged into one (either below, or by other link scripts). + args="${args} -last_pass2" + +# Generate arguments for linking the error reporting system. +# ---------------------------------------------------------- + case "${err}" in + +# If using own error reporting, do not produce any arguments. + my) :;; + +# Default error reporting requires linking with the AST EMS interface and +# the EMS library via the link script. + *) args="${args} -last_ems `ems_link_adam`";; + esac + +# Link with the maths library. +# ---------------------------- + args="${args} -lm" + +# Link with the starmem library, if available. +# -------------------------------------------- + args="${args} `\`find starmem_link\``" + +# Pass the resulting argument list through an awk script which eliminates +# all except the last reference to each library. + echo "${args}" \ + | awk 'BEGIN{RS=" ";FS="\n"} + {if($1)f[i++]=$1} + END{for(;i--;)if(!w[f[i]]++)l=f[i]" "l;print l}' + +# End of script. diff --git a/ast_par.source b/ast_par.source new file mode 100644 index 0000000..af45921 --- /dev/null +++ b/ast_par.source @@ -0,0 +1,720 @@ +*+ +* Name: +* AST_PAR + +* Purpose: +* Define the Fortran 77 interface to the AST library. + +* Language: +* Fortran 77 + +* Type of Module: +* Include file. + +* Description: +* This file contains definitions which are required by Fortran 77 +* programs which use the AST library. + +* Authors: +* RFWS: R.F. Warren-Smith (STARLINK) +* MBT: Mark Taylor (STARLINK) +* DSB: David S. Berry + +* History: +* 12-NOV-1996 (RFWS): +* Original version. +* 18-MAR-1998 (RFWS): +* Added definitions for the IntraMap class. +* 21-DEC-1998 (RFWS): +* Added resampling definitions for the Mapping class. +* 15-NOV-1999 (RFWS): +* Added definitions for PcdMap. +* 24-NOV-2000 (MBT): +* Added AST__BLOCKAVE interpolation scheme. +* 22-JUN-2001 (DSB): +* Added AST_OFFSET2 and AST_ANGLE to Frame class. +* 6-SEP-2001 (DSB): +* Added AST_AXDISTANCE and AST_AXOFFSET to Frame class. +* 12-SEP-2001 (DSB): +* Added AST_BEAR to Frame class. +* 21-SEP-2001 (DSB): +* Replaced AST_BEAR by AST_AXANGLE. +* 28-JAN-2003 (DSB): +* Added AST_GETACTIVEUNIT. +* 14-FEB-2003 (DSB): +* Added new values for WcsMap projections. +* 30-APR-2003 (DSB): +* Added AST_VERSION. +* 15-JUL-2003 (DSB): +* Added AST_RATE, POLYMAP, SHIFTMAP and GRISMMAP functions. +* 13-NOV-2003 (DSB): +* Added XmlChan class. +* 9-NOV-2004 (DSB): +* Added all initial Region classes. +* 19-NOV-2004 (DSB): +* Added KeyMap. +* 16-JUN-2005 (DSB): +* Added TimeMap and TimeFrame. +* 1-SEP-2005 (DSB): +* Added AST__REBININIT and AST__REBINNORM. +* 17-FEB-2006 (DSB): +* Added AST_ESCAPES. +* 9-FEB-2007 (DSB): +* Use a double precision constant to initialise AST__UNDEFF. +* 4-DEC-2008 (TIMJ): +* Add AST_TESTFITS. Remove AST__UNDEF +* 6-FEB-2009 (DSB): +* Added StcsChan class. +*- + +* Length of character string returned by a character function. + INTEGER AST__SZCHR + PARAMETER ( AST__SZCHR = 200 ) + +* Bad coordinate value. + DOUBLE PRECISION AST__BAD + PARAMETER ( AST__BAD = ) + +* Double precision NaN flag (this value is not actually a NaN itself). + DOUBLE PRECISION AST__NAN + PARAMETER ( AST__NAN = ) + +* Single precision NaN flag (this value is not actually a NaN itself). + REAL AST__NANR + PARAMETER ( AST__NANR = ) + +* Error module. + LOGICAL AST_OK + INTEGER AST_STATUS + +* Object class. + EXTERNAL AST_NULL + INTEGER AST__NULL + PARAMETER ( AST__NULL = 0 ) + + INTEGER AST__TUNULL + PARAMETER ( AST__TUNULL = -99999 ) + + + CHARACTER AST__TUNULLC*11 + PARAMETER ( AST__TUNULLC = '' ) + + CHARACTER * ( AST__SZCHR ) AST_GETC + DOUBLE PRECISION AST_GETD + INTEGER AST_CLONE + INTEGER AST_COPY + LOGICAL AST_EQUAL + INTEGER AST_GETI + INTEGER AST_VERSION + LOGICAL AST_GETL + LOGICAL AST_ISAOBJECT + LOGICAL AST_TEST + LOGICAL AST_HASATTRIBUTE + LOGICAL AST_SAME + INTEGER AST_TUNE + REAL AST_GETR + LOGICAL AST_CHRSUB + +* Channel class. + INTEGER AST_CHANNEL + INTEGER AST_READ + INTEGER AST_WRITE + LOGICAL AST_ISACHANNEL + INTEGER AST_WARNINGS + +* FitsChan class. + INTEGER AST_FITSCHAN + LOGICAL AST_FINDFITS + LOGICAL AST_ISAFITSCHAN + LOGICAL AST_GETFITSCF + LOGICAL AST_GETFITSCI + LOGICAL AST_GETFITSF + LOGICAL AST_GETFITSI + LOGICAL AST_GETFITSL + LOGICAL AST_GETFITSS + LOGICAL AST_GETFITSCN + LOGICAL AST_TESTFITS + INTEGER AST_GETTABLES + + CHARACTER AST__TABEXTNAME*7 + PARAMETER ( AST__TABEXTNAME = 'WCS-TAB' ) + + INTEGER AST__NOTYPE + PARAMETER ( AST__NOTYPE = -1 ) + INTEGER AST__COMMENT + PARAMETER ( AST__COMMENT = 0 ) + INTEGER AST__INT + PARAMETER ( AST__INT = 1 ) + INTEGER AST__FLOAT + PARAMETER ( AST__FLOAT = 2 ) + INTEGER AST__STRING + PARAMETER ( AST__STRING = 3 ) + INTEGER AST__COMPLEXF + PARAMETER ( AST__COMPLEXF = 4 ) + INTEGER AST__COMPLEXI + PARAMETER ( AST__COMPLEXI = 5 ) + INTEGER AST__LOGICAL + PARAMETER ( AST__LOGICAL = 6 ) + INTEGER AST__CONTINUE + PARAMETER ( AST__CONTINUE = 7 ) + INTEGER AST__UNDEF + PARAMETER ( AST__UNDEF = 8 ) + + +* Mapping Class. + INTEGER AST__URESAMP1 + PARAMETER ( AST__URESAMP1 = 1 ) + INTEGER AST__URESAMP2 + PARAMETER ( AST__URESAMP2 = 2 ) + INTEGER AST__URESAMP3 + PARAMETER ( AST__URESAMP3 = 4 ) + INTEGER AST__URESAMP4 + PARAMETER ( AST__URESAMP4 = 8 ) + INTEGER AST__USEVAR + PARAMETER ( AST__USEVAR = 16 ) + INTEGER AST__USEBAD + PARAMETER ( AST__USEBAD = 32 ) + INTEGER AST__CONSERVEFLUX + PARAMETER ( AST__CONSERVEFLUX = 64 ) + INTEGER AST__REBININIT + PARAMETER ( AST__REBININIT = 128 ) + INTEGER AST__REBINEND + PARAMETER ( AST__REBINEND = 256 ) + INTEGER AST__GENVAR + PARAMETER ( AST__GENVAR = 512 ) + INTEGER AST__VARWGT + PARAMETER ( AST__VARWGT = 1024 ) + INTEGER AST__NOBAD + PARAMETER ( AST__NOBAD = 2048 ) + INTEGER AST__DISVAR + PARAMETER ( AST__DISVAR = 4096 ) + INTEGER AST__NONORM + PARAMETER ( AST__NONORM = 8192 ) + + INTEGER AST__UKERN1 + PARAMETER ( AST__UKERN1 = 1 ) +c Not yet implemented +c INTEGER AST__UKERNN +c PARAMETER ( AST__UKERNN = 2 ) + INTEGER AST__UINTERP + PARAMETER ( AST__UINTERP = 3 ) + INTEGER AST__NEAREST + PARAMETER ( AST__NEAREST = 4 ) + INTEGER AST__LINEAR + PARAMETER ( AST__LINEAR = 5 ) + INTEGER AST__SINC + PARAMETER ( AST__SINC = 6 ) + INTEGER AST__SINCSINC + PARAMETER ( AST__SINCSINC = 7 ) + INTEGER AST__SINCCOS + PARAMETER ( AST__SINCCOS = 8 ) + INTEGER AST__SINCGAUSS + PARAMETER ( AST__SINCGAUSS = 9 ) + INTEGER AST__BLOCKAVE + PARAMETER ( AST__BLOCKAVE = 10 ) + INTEGER AST__GAUSS + PARAMETER ( AST__GAUSS = 11 ) + INTEGER AST__SOMB + PARAMETER ( AST__SOMB = 12 ) + INTEGER AST__SOMBCOS + PARAMETER ( AST__SOMBCOS = 13 ) + + INTEGER AST_RESAMPLEB + INTEGER AST_RESAMPLED + INTEGER AST_RESAMPLEI + INTEGER AST_RESAMPLEK + INTEGER AST_RESAMPLER + INTEGER AST_RESAMPLES + INTEGER AST_RESAMPLEUB + INTEGER AST_RESAMPLEUI + INTEGER AST_RESAMPLEUK + INTEGER AST_RESAMPLEUS + INTEGER AST_RESAMPLEUW + INTEGER AST_RESAMPLEW + INTEGER AST_REMOVEREGIONS + INTEGER AST_SIMPLIFY + LOGICAL AST_ISAMAPPING + LOGICAL AST_LINEARAPPROX + LOGICAL AST_QUADAPPROX + DOUBLE PRECISION AST_RATE + +* CmpMap class. + INTEGER AST_CMPMAP + LOGICAL AST_ISACMPMAP + +* Frame class. + CHARACTER * ( AST__SZCHR ) AST_FORMAT + DOUBLE PRECISION AST_DISTANCE + INTEGER AST_CONVERT + INTEGER AST_FINDFRAME + INTEGER AST_FRAME + INTEGER AST_PICKAXES + INTEGER AST_UNFORMAT + LOGICAL AST_ISAFRAME + LOGICAL AST_GETACTIVEUNIT + DOUBLE PRECISION AST_ANGLE + DOUBLE PRECISION AST_OFFSET2 + DOUBLE PRECISION AST_AXDISTANCE + DOUBLE PRECISION AST_AXOFFSET + DOUBLE PRECISION AST_AXANGLE + +* CmpFrame class. + INTEGER AST_CMPFRAME + LOGICAL AST_ISACMPFRAME + +* FrameSet class. + INTEGER AST__BASE + PARAMETER ( AST__BASE = 0 ) + INTEGER AST__CURRENT + PARAMETER ( AST__CURRENT = -1 ) + INTEGER AST__NOFRAME + PARAMETER ( AST__NOFRAME = -99 ) + + INTEGER AST_FRAMESET + INTEGER AST_GETFRAME + INTEGER AST_GETMAPPING + LOGICAL AST_ISAFRAMESET + +* IntraMap class. + INTEGER AST__NOFWD + PARAMETER ( AST__NOFWD = 1 ) + INTEGER AST__NOINV + PARAMETER ( AST__NOINV = 2 ) + INTEGER AST__SIMPFI + PARAMETER ( AST__SIMPFI = 4 ) + INTEGER AST__SIMPIF + PARAMETER ( AST__SIMPIF = 8 ) + INTEGER AST__ANY + PARAMETER ( AST__ANY = -66 ) + + INTEGER AST_INTRAMAP + LOGICAL AST_ISAINTRAMAP + +* LutMap class. + INTEGER AST_LUTMAP + LOGICAL AST_ISALUTMAP + +* PcdMap class. + INTEGER AST_PCDMAP + LOGICAL AST_ISAPCDMAP + +* Plot class. + INTEGER AST_PLOT + LOGICAL AST_BORDER + INTEGER AST_GETGRFCONTEXT + LOGICAL AST_ISAPLOT + INTEGER AST_ESCAPES + CHARACTER * ( AST__SZCHR ) AST_STRIPESCAPES + +* SkyFrame class. + INTEGER AST_SKYFRAME + LOGICAL AST_ISASKYFRAME + INTEGER AST_SKYOFFSETMAP + +* SpecFrame class. + INTEGER AST_SPECFRAME + LOGICAL AST_ISASPECFRAME + +* DSBSpecFrame class. + INTEGER AST_DSBSPECFRAME + LOGICAL AST_ISADSBSPECFRAME + +* MathMap class. + INTEGER AST_MATHMAP + LOGICAL AST_ISAMATHMAP + +* MatrixMap class. + INTEGER AST_MATRIXMAP + LOGICAL AST_ISAMATRIXMAP + +* PermMap class. + INTEGER AST_PERMMAP + LOGICAL AST_ISAPERMMAP + +* PolyMap class. + INTEGER AST_POLYMAP + LOGICAL AST_ISAPOLYMAP + INTEGER AST_POLYTRAN + +* SlaMap class. + INTEGER AST_SLAMAP + LOGICAL AST_ISASLAMAP + +* SpecMap class. + INTEGER AST_SPECMAP + LOGICAL AST_ISASPECMAP + +* SphMap class. + INTEGER AST_SPHMAP + LOGICAL AST_ISASPHMAP + +* UnitMap class. + INTEGER AST_UNITMAP + LOGICAL AST_ISAUNITMAP + +* WcsMap class. + + INTEGER AST__WCSMX + PARAMETER ( AST__WCSMX = 10 ) + + DOUBLE PRECISION AST__DPI + PARAMETER ( AST__DPI = 3.1415926535897932384626433832795028842 ) + + DOUBLE PRECISION AST__DPIBY2 + PARAMETER ( AST__DPIBY2 = 1.570796326794896619231321691639751442 ) + + DOUBLE PRECISION AST__DD2R + PARAMETER ( AST__DD2R = 0.01745329251994329576923690768488612713 ) + + DOUBLE PRECISION AST__DR2D + PARAMETER ( AST__DR2D = 57.2957795130823208767981548141051703324 ) + + INTEGER AST__AIR + PARAMETER ( AST__AIR = 9 ) + INTEGER AST__AIT + PARAMETER ( AST__AIT = 17 ) + INTEGER AST__ARC + PARAMETER ( AST__ARC = 6 ) + INTEGER AST__AZP + PARAMETER ( AST__AZP = 1 ) + INTEGER AST__BON + PARAMETER ( AST__BON = 22 ) + INTEGER AST__CAR + PARAMETER ( AST__CAR = 12 ) + INTEGER AST__CEA + PARAMETER ( AST__CEA = 11 ) + INTEGER AST__COD + PARAMETER ( AST__COD = 20 ) + INTEGER AST__COE + PARAMETER ( AST__COE = 19 ) + INTEGER AST__COO + PARAMETER ( AST__COO = 21 ) + INTEGER AST__COP + PARAMETER ( AST__COP = 18 ) + INTEGER AST__CSC + PARAMETER ( AST__CSC = 25 ) + INTEGER AST__CYP + PARAMETER ( AST__CYP = 10 ) + INTEGER AST__GLS + PARAMETER ( AST__GLS = 28 ) + INTEGER AST__HPX + PARAMETER ( AST__HPX = 30 ) + INTEGER AST__MER + PARAMETER ( AST__MER = 13 ) + INTEGER AST__MOL + PARAMETER ( AST__MOL = 16 ) + INTEGER AST__NCP + PARAMETER ( AST__NCP = 27 ) + INTEGER AST__PAR + PARAMETER ( AST__PAR = 15 ) + INTEGER AST__PCO + PARAMETER ( AST__PCO = 23 ) + INTEGER AST__QSC + PARAMETER ( AST__QSC = 26 ) + INTEGER AST__SFL + PARAMETER ( AST__SFL = 14 ) + INTEGER AST__SIN + PARAMETER ( AST__SIN = 5 ) + INTEGER AST__STG + PARAMETER ( AST__STG = 4 ) + INTEGER AST__SZP + PARAMETER ( AST__SZP = 2 ) + INTEGER AST__TAN + PARAMETER ( AST__TAN = 3 ) + INTEGER AST__TPN + PARAMETER ( AST__TPN = 29 ) + INTEGER AST__TSC + PARAMETER ( AST__TSC = 24 ) + INTEGER AST__XPH + PARAMETER ( AST__XPH = 31 ) + INTEGER AST__ZEA + PARAMETER ( AST__ZEA = 8 ) + INTEGER AST__ZPN + PARAMETER ( AST__ZPN = 7 ) + INTEGER AST__WCSBAD + PARAMETER ( AST__WCSBAD = 32 ) + + INTEGER AST_WCSMAP + LOGICAL AST_ISAWCSMAP + +* ShiftMap class. + INTEGER AST_SHIFTMAP + LOGICAL AST_ISASHIFTMAP + +* WinMap class. + INTEGER AST_WINMAP + LOGICAL AST_ISAWINMAP + +* ZoomMap class. + INTEGER AST_ZOOMMAP + LOGICAL AST_ISAZOOMMAP + +* GrismMap class. + INTEGER AST_GRISMMAP + LOGICAL AST_ISAGRISMMAP + +* XmlChan class. + INTEGER AST_XMLCHAN + LOGICAL AST_ISAXMLCHAN + +* TranMap class. + INTEGER AST_TRANMAP + LOGICAL AST_ISATRANMAP + +* Region class. + INTEGER AST_REGION + INTEGER AST_GETUNC + INTEGER AST_GETREGIONFRAME + LOGICAL AST_ISAREGION + INTEGER AST_MAPREGION + INTEGER AST_OVERLAP + INTEGER AST_MASKB + INTEGER AST_MASKD + INTEGER AST_MASKI + INTEGER AST_MASKR + INTEGER AST_MASKS + INTEGER AST_MASKUB + INTEGER AST_MASKUI + INTEGER AST_MASKUS + INTEGER AST_MASKUW + INTEGER AST_MASKW + +* Box class. + INTEGER AST_BOX + LOGICAL AST_ISABOX + +* PointList class. + INTEGER AST_POINTLIST + LOGICAL AST_ISAPOINTLIST + +* Polygon class. + INTEGER AST_POLYGON + LOGICAL AST_ISAPOLYGON + INTEGER AST_DOWNSIZE + INTEGER AST_OUTLINED + INTEGER AST_OUTLINER + INTEGER AST_OUTLINEI + INTEGER AST_OUTLINEUI + INTEGER AST_OUTLINES + INTEGER AST_OUTLINEUS + INTEGER AST_OUTLINEW + INTEGER AST_OUTLINEUW + INTEGER AST_OUTLINEB + INTEGER AST_OUTLINEUB + + INTEGER AST_CONVEXD + INTEGER AST_CONVEXR + INTEGER AST_CONVEXI + INTEGER AST_CONVEXUI + INTEGER AST_CONVEXS + INTEGER AST_CONVEXUS + INTEGER AST_CONVEXW + INTEGER AST_CONVEXUW + INTEGER AST_CONVEXB + INTEGER AST_CONVEXUB + + INTEGER AST__LE + PARAMETER( AST__LE = 2 ) + + INTEGER AST__EQ + PARAMETER( AST__EQ = 3 ) + + INTEGER AST__GE + PARAMETER( AST__GE = 4 ) + + INTEGER AST__GT + PARAMETER( AST__GT = 5 ) + + INTEGER AST__NE + PARAMETER( AST__NE = 6 ) + +* Circle class. + INTEGER AST_CIRCLE + LOGICAL AST_ISACIRCLE + +* Ellipse class. + INTEGER AST_ELLIPSE + LOGICAL AST_ISAELLIPSE + +* NullRegion class. + INTEGER AST_NULLREGION + LOGICAL AST_ISANULLREGION + +* Interval class. + INTEGER AST_INTERVAL + LOGICAL AST_ISAINTERVAL + +* Prism class. + INTEGER AST_PRISM + LOGICAL AST_ISAPRISM + +* CmpRegion class. + INTEGER AST_CMPREGION + LOGICAL AST_ISACMPREGION + + INTEGER AST__AND + PARAMETER( AST__AND = 1 ) + + INTEGER AST__OR + PARAMETER( AST__OR = 2 ) + + INTEGER AST__XOR + PARAMETER( AST__XOR = 3 ) + +* KeyMap class. + INTEGER AST_KEYMAP + LOGICAL AST_ISAKEYMAP + LOGICAL AST_MAPGET0I + LOGICAL AST_MAPGET0S + LOGICAL AST_MAPGET0B + LOGICAL AST_MAPGET0D + LOGICAL AST_MAPGET0R + LOGICAL AST_MAPGET0C + LOGICAL AST_MAPGET0A + LOGICAL AST_MAPGET1I + LOGICAL AST_MAPGET1B + LOGICAL AST_MAPGET1S + LOGICAL AST_MAPGET1D + LOGICAL AST_MAPGET1R + LOGICAL AST_MAPGET1C + LOGICAL AST_MAPGET1A + LOGICAL AST_MAPGETELEMI + LOGICAL AST_MAPGETELEMS + LOGICAL AST_MAPGETELEMB + LOGICAL AST_MAPGETELEMD + LOGICAL AST_MAPGETELEMR + LOGICAL AST_MAPGETELEMC + LOGICAL AST_MAPGETELEMA + INTEGER AST_MAPSIZE + INTEGER AST_MAPLENGTH + INTEGER AST_MAPLENC + INTEGER AST_MAPTYPE + LOGICAL AST_MAPHASKEY + LOGICAL AST_MAPDEFINED + CHARACTER * ( AST__SZCHR ) AST_MAPKEY + + INTEGER AST__BADTYPE + PARAMETER ( AST__BADTYPE = 0) + + INTEGER AST__INTTYPE + PARAMETER ( AST__INTTYPE = 1) + + INTEGER AST__DOUBLETYPE + PARAMETER ( AST__DOUBLETYPE = 2) + + INTEGER AST__STRINGTYPE + PARAMETER ( AST__STRINGTYPE = 3) + + INTEGER AST__OBJECTTYPE + PARAMETER ( AST__OBJECTTYPE = 4) + + INTEGER AST__FLOATTYPE + PARAMETER ( AST__FLOATTYPE = 5) + + INTEGER AST__SINTTYPE + PARAMETER ( AST__SINTTYPE = 7) + + INTEGER AST__UNDEFTYPE + PARAMETER ( AST__UNDEFTYPE = 8) + + INTEGER AST__BYTETYPE + PARAMETER ( AST__BYTETYPE = 9) + +* FluxFrame class. + INTEGER AST_FLUXFRAME + LOGICAL AST_ISAFLUXFRAME + +* SpecFluxFrame class. + INTEGER AST_SPECFLUXFRAME + LOGICAL AST_ISASPECFLUXFRAME + +* NormMap class. + INTEGER AST_NORMMAP + LOGICAL AST_ISANORMMAP + +* RateMap class. + INTEGER AST_RATEMAP + LOGICAL AST_ISARATEMAP + +* TimeFrame class. + INTEGER AST_TIMEFRAME + LOGICAL AST_ISATIMEFRAME + DOUBLE PRECISION AST_CURRENTTIME + + INTEGER AST__LT + PARAMETER( AST__LT = 11 ) + +* TimeMap class. + INTEGER AST_TIMEMAP + LOGICAL AST_ISATIMEMAP + +* Stc class. + LOGICAL AST_ISASTC + INTEGER AST_GETSTCREGION + INTEGER AST_GETSTCCOORD + INTEGER AST_GETSTCNCOORD + + CHARACTER AST__STCNAME*4 + PARAMETER ( AST__STCNAME = 'Name' ) + + CHARACTER AST__STCVALUE*5 + PARAMETER ( AST__STCVALUE = 'Value' ) + + CHARACTER AST__STCERROR*5 + PARAMETER ( AST__STCERROR = 'Error' ) + + CHARACTER AST__STCRES*10 + PARAMETER ( AST__STCRES = 'Resolution' ) + + CHARACTER AST__STCSIZE*4 + PARAMETER ( AST__STCSIZE = 'Size' ) + + CHARACTER AST__STCPIXSZ*7 + PARAMETER ( AST__STCPIXSZ = 'PixSize' ) + +* StcSearchLocation class. + LOGICAL AST_ISASTCSEARCHLOCATION + INTEGER AST_STCSEARCHLOCATION + +* StcCatalogEntryLocation class. + LOGICAL AST_ISASTCCATALOGENTRYLOCATION + INTEGER AST_STCCATALOGENTRYLOCATION + +* StcResourceProfile class. + LOGICAL AST_ISASTCRESOURCEPROFILE + INTEGER AST_STCRESOURCEPROFILE + +* StcObsDataLocation class. + LOGICAL AST_ISASTCOBSDATALOCATION + INTEGER AST_STCOBSDATALOCATION + +* SwitchMap class. + INTEGER AST_SWITCHMAP + LOGICAL AST_ISASWITCHMAP + +* SelectorMap class. + INTEGER AST_SELECTORMAP + LOGICAL AST_ISASELECTORMAP + +* Plot3D class. + INTEGER AST_PLOT3D + LOGICAL AST_ISAPLOT3D + +* StcsChan class. + INTEGER AST_STCSCHAN + LOGICAL AST_ISASTCSCHAN + +* Table class. + INTEGER AST_TABLE + LOGICAL AST_ISATABLE + LOGICAL AST_HASCOLUMN + CHARACTER * ( AST__SZCHR ) AST_COLUMNNAME + LOGICAL AST_HASPARAMETER + CHARACTER * ( AST__SZCHR ) AST_PARAMETERNAME + +* FitsTable class. + INTEGER AST_FITSTABLE + LOGICAL AST_ISAFITSTABLE + INTEGER AST_COLUMNNULL + INTEGER AST_COLUMNSIZE + INTEGER AST_GETTABLEHEADER diff --git a/ast_test.c b/ast_test.c new file mode 100644 index 0000000..61e948e --- /dev/null +++ b/ast_test.c @@ -0,0 +1,115 @@ +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ +#include "ast.h" /* AST C interface definition */ + +/* C header files. */ +/* --------------- */ +#include + +/* Main function. */ +/* ============== */ +int main( int argc, char *argv[] ) { +/* +*+ +* Name: +* ast_test + +* Purpose: +* Test installation of the AST library. + +* Type: +* C program. + +* Description: +* This program performs a simple test (without using graphics) of +* the AST library, to check that it is correctly installed. It is +* not an exhaustive test of the system. + +* Arguments: +* None. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 19-NOV-1997 (RFWS); +* Original version. +*- +*/ + +/* Local Constants: */ +#define NCOORD 10 /* Number of coordinates to transform */ + +/* Local Variables: */ + AstFrameSet *cvt; /* Pointer to conversion FrameSet */ + AstSkyFrame *sky1; /* Pointer to first SkyFrame */ + AstSkyFrame *sky2; /* Pointer to second SkyFrame */ + double xin[ NCOORD ]; /* Input coordinate array */ + double xout[ NCOORD ]; /* Output coordinate array */ + double yin[ NCOORD ]; /* Input coordinate array */ + double yout[ NCOORD ]; /* Output coordinate array */ + int i; /* Loop counter for coordinates */ + +/* Begin an AST context. */ + astBegin; + +/* Create two SkyFrames. */ + sky1 = astSkyFrame( "system = FK4_NO_E, equinox = B1920, epoch = B1958" ); + sky2 = astSkyFrame( "system = ecliptic, equinox = J2001" ); + +/* Create a FrameSet describing the conversion between them. */ + cvt = astConvert( sky1, sky2, "" ); + +/* If successful, set up some input coordinates. */ + if ( cvt != AST__NULL ) { + for ( i = 0; i < NCOORD; i++ ) { + xin[ i ] = 0.1 * (double) i; + yin[ i ] = 0.2 * (double) i; + } + +/* Display the FrameSet. */ + astShow( cvt ); + printf( "\n"); + +/* Activate reporting of coordinate transformations. */ + astSet( cvt, "Report = 1" ); + +/* Perform the forward transformation. */ + astTran2( cvt, 10, xin, yin, 1, xout, yout ); + printf( "\n"); + +/* Perform the inverse transformation. */ + astTran2( cvt, 10, xout, yout, 0, xin, yin ); + } + +/* End the AST context. */ + astEnd; + +/* Return an error status. */ + return astOK ? 0 : 1; + +/* Undefine local macros. */ +#undef NCOORD +} diff --git a/ast_tester/README b/ast_tester/README new file mode 100644 index 0000000..8c50db6 --- /dev/null +++ b/ast_tester/README @@ -0,0 +1,33 @@ + +This directory contains files used for performing some fairly restricted +testing of the AST library. A Fortran 77 compiler is required. As supplied +it uses the g77 Fortran compiler, but this can be changed by editing the ast_tester +script. + +To perform a test: + +- build AST + +- define the environment variables AST and STARLINK so that $AST/lib contains + the AST libraries to be tested, and $STARLINK/lib contains the the USSC libraries. + +- If required, edit the ast_tester script to modify the fortran compiler and/or options. + +- execute the ast_tester script. + + +The script generates various output files which are compared with existing files +which are presumed to represent correct behaviour of the AST library. Any differences +are reported by the script. + +The script also generates various postscript files representing "critically difficult" plots. +A file "*-new.ps" is created for each file "*.head" in the directory. The directory also +contains files "*.ps" which represent the expected appearance of the plots. By default, the +new plots are displayed using ghostview. This can be disabled by specifying the "-nd" option +on the ast_tester command line. + + + + + + diff --git a/ast_tester/a20070718_00010_02_cube.ast b/ast_tester/a20070718_00010_02_cube.ast new file mode 100644 index 0000000..f005938 --- /dev/null +++ b/ast_tester/a20070718_00010_02_cube.ast @@ -0,0 +1,339 @@ + Begin FrameSet # Set of inter-related coordinate systems +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "SKY-DSBSPECTRUM" # Coordinate system domain +# Epoch = 2007.54208230851 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Radio velocity (USB)" # Label for axis 3 +# System = "Compound" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Uni3 = "km/s" # Units for axis 3 +# Dir1 = 0 # Plot axis 1 in reverse direction + IsA Frame # Coordinate system description + Nframe = 4 # Number of Frames in FrameSet +# Base = 1 # Index of base Frame + Currnt = 4 # Index of current Frame + Nnode = 5 # Number of nodes in FrameSet + Nod1 = 3 # Frame 1 is associated with node 3 + Nod2 = 4 # Frame 2 is associated with node 4 + Nod3 = 5 # Frame 3 is associated with node 5 + Nod4 = 2 # Frame 4 is associated with node 2 + Lnk2 = 1 # Node 2 is derived from node 1 + Lnk3 = 1 # Node 3 is derived from node 1 + Lnk4 = 1 # Node 4 is derived from node 1 + Lnk5 = 1 # Node 5 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Data grid indices; first pixel at (1,1,1)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Data grid index 1" # Label for axis 1 +# Lbl2 = "Data grid index 2" # Label for axis 2 +# Lbl3 = "Data grid index 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Data grid index 1" # Axis Label + Symbol = "g1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Data grid index 2" # Axis Label + Symbol = "g2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Data grid index 3" # Axis Label + Symbol = "g3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm2 = # Frame number 2 + Begin Frame # Coordinate system description + Title = "Pixel coordinates; first pixel at (-8.5,-8.5,-2048.5)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "PIXEL" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Lbl3 = "Pixel coordinate 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 1" # Axis Label + Symbol = "p1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 2" # Axis Label + Symbol = "p2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 3" # Axis Label + Symbol = "p3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm3 = # Frame number 3 + Begin Frame # Coordinate system description + Title = "Axis coordinates; first pixel at (-8.5,-8.5,-2048.5)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "AXIS" # Coordinate system domain +# Lbl1 = "Axis 1" # Label for axis 1 +# Lbl2 = "Axis 2" # Label for axis 2 +# Lbl3 = "Axis 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Axis 1" # Axis Label + Symbol = "a1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Axis 2" # Axis Label + Symbol = "a2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Axis 3" # Axis Label + Symbol = "a3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm4 = # Frame number 4 + Begin CmpFrame # Compound coordinate system description +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "SKY-DSBSPECTRUM" # Coordinate system domain +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Radio velocity (USB)" # Label for axis 3 +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Uni3 = "km/s" # Units for axis 3 +# Dir1 = 0 # Plot axis 1 in reverse direction + IsA Frame # Coordinate system description + FrameA = # First component Frame + Begin SkyFrame # Description of celestial coordinate system + Naxes = 2 # Number of coordinate axes + Epoch = 2007.54208230851 # Julian epoch of observation + System = "FK5" # Coordinate system type + ObsLat = 0.346026050148997 # Observers geodetic latitude (rads) + ObsLon = -2.71363306946838 # Observers geodetic longitude (rads) + Dut1 = -0.162963400559962 # UT1-UTC in seconds + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + SRef1 = 4.90555239050187 # Ref. pos. RA 18:44:16.2 + SRef2 = -0.0698132003068136 # Ref. pos. Dec -4:00:00 + End SkyFrame + FrameB = # Second component Frame + Begin DSBSpecFrame # Dual sideband spectral axis + Naxes = 1 # Number of coordinate axes + Epoch = 2007.54208230851 # Julian epoch of observation + System = "VRAD" # Coordinate system type + ObsLat = 0.346026069000144 # Observers geodetic latitude (rads) + ObsLon = -2.71363307300091 # Observers geodetic longitude (rads) + Dut1 = -0.162963400559962 # UT1-UTC in seconds + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + End Axis + IsA Frame # Coordinate system description + SoR = "LSRK" # Standard of rest + RefRA = 4.90553899950038 # Reference RA (rads, FK5 J2000) + RefDec = -0.0698131700797732 # Reference Dec (rads, FK5 J2000) + RstFrq = 372672509000 # Rest frequency (Hz) + SrcVel = 87012.623732646 # Source velocity (m/s) + SrcVRF = "LSRK" # Source velocity rest frame + UFreq = "GHz" # Preferred units for frequency + IsA SpecFrame # Description of spectral coordinate system + DSBCen = 372576971505.836 # Central frequency (Hz topo) + IF = -5251168918.31241 # Intermediate frequency (Hz) + SideBn = "USB" # Represents upper sideband + End DSBSpecFrame + End CmpFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 10 # Shift for axis 1 + Sft2 = 10.4938775599003 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 6105939.046167 # Shift for axis 1 + Scl1 = -1.63834485003565e-05 # Scale factor for axis 1 + End WinMap + MapB = # Second component Mapping + Begin SpecMap # Conversion between spectral coordinate systems + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nspec = 2 # Number of conversion steps + Spec1 = "FRTOVL" # Convert frequency to rel. velocity + Spec1a = 372672509000 # Rest frequency (Hz) + Spec2 = "VLTOVR" # Convert relativistic to radio velocity + End SpecMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + M0 = -2.90888208665722e-05 # Forward matrix value + M1 = 2.90888208665722e-05 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + M0 = -0.0133908234539359 # Forward matrix value + M1 = 0.981401710741326 # Forward matrix value + M2 = 0.191497697117288 # Forward matrix value + M3 = 0.068459122667767 # Forward matrix value + M4 = 0.191965314976427 # Forward matrix value + M5 = -0.979011065499037 # Forward matrix value + M6 = -0.997564050259824 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = -0.0697564737441253 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 4.90555329534916 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin ZoomMap # Zoom about the origin + Nin = 1 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Zoom = 0.001 # Zoom factor + End ZoomMap + End CmpMap + End CmpMap + Map3 = # Mapping between nodes 1 and 3 + Begin UnitMap # Unit (null) Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + End UnitMap + Map4 = # Mapping between nodes 1 and 4 + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -9.5 # Shift for axis 1 + Sft2 = -9.5 # Shift for axis 2 + Sft3 = -2049.5 # Shift for axis 3 + End WinMap + Map5 = # Mapping between nodes 1 and 5 + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -9.5 # Shift for axis 1 + Sft2 = -9.5 # Shift for axis 2 + Sft3 = -2049.5 # Shift for axis 3 + End WinMap + End FrameSet diff --git a/ast_tester/a20070718_00010_02_cube.fits-wcs b/ast_tester/a20070718_00010_02_cube.fits-wcs new file mode 100644 index 0000000..428efaa --- /dev/null +++ b/ast_tester/a20070718_00010_02_cube.fits-wcs @@ -0,0 +1,61 @@ +WCSAXES = 3 / Number of WCS axes +CRPIX1 = 10.0 / Reference pixel on axis 1 +CRPIX2 = 10.493878 / Reference pixel on axis 2 +CRPIX3 = 1.0 / Reference pixel on axis 3 +CRVAL1 = 281.0675 / Value at ref. pixel on axis 1 +CRVAL2 = -4.0 / Value at ref. pixel on axis 2 +CRVAL3 = -13.610153 / Value at ref. pixel on axis 3 +CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2 +CTYPE3 = 'VRAD ' / Type of co-ordinate on axis 3 +CDELT1 = -0.0016666667 / Pixel size on axis 1 +CDELT2 = 0.0016666667 / Pixel size on axis 2 +CDELT3 = 0.049100739 / Pixel size on axis 3 +CUNIT3 = 'km/s ' / Units for axis 3 +MJD-OBS = 54299.245 / Modified Julian Date of observation +DATE-OBS= '2007-07-18T05:52:31.475' / Date of observation +RADESYS = 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / [yr] Epoch of reference equinox +SPECSYS = 'LSRK ' / Standard of rest for spectral axis +SSYSSRC = 'LSRK ' / Standard of rest for source redshift +ZSOURCE = 0.000290285 / [] Redshift of source +VELOSYS = -10097.217 / [m/s] Topo. apparent velocity of rest frame +RESTFRQ = 3.7267251E+11 / [Hz] Rest frequency +IMAGFREQ= 3.621676E+11 / [Hz] Image frequency +OBSGEO-X= -5461073.2 / [m] Observatory geocentric X +OBSGEO-Y= -2491089.0 / [m] Observatory geocentric Y +OBSGEO-Z= 2149568.8 / [m] Observatory geocentric Z +WCSAXESA= 3 / Number of WCS axes +WCSNAMEA= 'PIXEL ' / Reference name for the coord. frame +CRPIX1A = 1.0 / Reference pixel on axis 1 +CRPIX2A = 1.0 / Reference pixel on axis 2 +CRPIX3A = 1.0 / Reference pixel on axis 3 +CRVAL1A = -8.5 / Value at ref. pixel on axis 1 +CRVAL2A = -8.5 / Value at ref. pixel on axis 2 +CRVAL3A = -2048.5 / Value at ref. pixel on axis 3 +CTYPE1A = 'p1 ' / Pixel coordinate 1 +CTYPE2A = 'p2 ' / Pixel coordinate 2 +CTYPE3A = 'p3 ' / Pixel coordinate 3 +CDELT1A = 1.0 / Pixel size on axis 1 +CDELT2A = 1.0 / Pixel size on axis 2 +CDELT3A = 1.0 / Pixel size on axis 3 +CUNIT1A = 'pixel ' / Units for axis 1 +CUNIT2A = 'pixel ' / Units for axis 2 +CUNIT3A = 'pixel ' / Units for axis 3 +WCSAXESB= 3 / Number of WCS axes +WCSNAMEB= 'AXIS ' / Reference name for the coord. frame +CRPIX1B = 1.0 / Reference pixel on axis 1 +CRPIX2B = 1.0 / Reference pixel on axis 2 +CRPIX3B = 1.0 / Reference pixel on axis 3 +CRVAL1B = -8.5 / Value at ref. pixel on axis 1 +CRVAL2B = -8.5 / Value at ref. pixel on axis 2 +CRVAL3B = -2048.5 / Value at ref. pixel on axis 3 +CTYPE1B = 'a1 ' / Axis 1 +CTYPE2B = 'a2 ' / Axis 2 +CTYPE3B = 'a3 ' / Axis 3 +CDELT1B = 1.0 / Pixel size on axis 1 +CDELT2B = 1.0 / Pixel size on axis 2 +CDELT3B = 1.0 / Pixel size on axis 3 +CUNIT1B = 'pixel ' / Units for axis 1 +CUNIT2B = 'pixel ' / Units for axis 2 +CUNIT3B = 'pixel ' / Units for axis 3 diff --git a/ast_tester/aitoff.attr b/ast_tester/aitoff.attr new file mode 100644 index 0000000..85aa9de --- /dev/null +++ b/ast_tester/aitoff.attr @@ -0,0 +1 @@ +Grid=1,tickall=0,border=1,tol=0.001 diff --git a/ast_tester/aitoff.box b/ast_tester/aitoff.box new file mode 100644 index 0000000..b4f303e --- /dev/null +++ b/ast_tester/aitoff.box @@ -0,0 +1 @@ +10.0 -10.0 300.0 280.0 diff --git a/ast_tester/aitoff.head b/ast_tester/aitoff.head new file mode 100644 index 0000000..5f34de0 --- /dev/null +++ b/ast_tester/aitoff.head @@ -0,0 +1,13 @@ +SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 +BITPIX = -32 / Bits per pixel. +NAXIS = 2 / Number of dimensions +NAXIS1 = 300 / Length of x axis. +NAXIS2 = 300 / Length of y axis. +CTYPE1 = 'GLON-AIT' / X-axis type +CTYPE2 = 'GLAT-AIT' / Y-axis type +CRVAL1 = -149.56866 / Reference pixel value +CRVAL2 = -19.758201 / Reference pixel value +CRPIX1 = 150.500 / Reference pixel +CRPIX2 = 150.500 / Reference pixel +CDELT1 = -1.20000 / Degrees/pixel +CDELT2 = 1.20000 / Degrees/pixel diff --git a/ast_tester/aitoff.ps b/ast_tester/aitoff.ps new file mode 100644 index 0000000..45e2437 --- /dev/null +++ b/ast_tester/aitoff.ps @@ -0,0 +1,1598 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Feb 11 17:19:53 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: aitoff.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 11/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 388.9 389.5 390.1 390.7 391.4 392.0 392.6 393.2 393.8 394.4 395.0 395.5 396.1 396.7 397.2 397.8 398.3 398.9 + 399.4 399.9 400.5 401.0 401.5 402.0 402.5 403.0 403.5 404.0 404.5 405.0 405.4 405.9 406.3 406.8 407.2 407.7 + 408.1 408.5 409.0 409.4 409.8 410.2 410.6 411.0 411.4 411.8 412.1 412.5 412.9 413.2 413.6 413.9 414.3 414.6 + 414.9 415.3 415.6 415.9 416.2 416.5 416.8 417.1 417.4 417.6 417.9 418.2 418.4 418.7 418.9 419.2 419.4 419.6 + 419.8 420.1 420.3 420.5 420.7 420.9 421.1 421.2 421.4 421.6 421.7 421.9 422.0 422.2 422.3 422.4 422.6 422.7 + 422.8 422.9 423.0 423.1 423.2 423.3 423.3 423.4 423.5 423.5 423.6 423.6 423.7 423.7 423.7 423.7 423.7 423.7 + 423.7 423.7 423.7 423.7 423.7 423.6 423.6 423.5 423.5 423.4 423.3 423.3 423.2 423.1 423.0 422.9 422.8 422.7 + 422.5 422.4 422.3 422.1 422.0 421.8 421.7 421.5 421.3 421.1 420.9 420.7 420.5 420.3 420.1 419.9 419.6 419.4 + 419.1 418.9 418.6 418.4 418.1 417.8 417.6 417.3 417.0 416.7 416.4 416.1 415.8 415.5 415.2 414.9 414.6 414.3 + 414.0 413.7 413.4 413.2 412.9 412.7 412.6 412.4 412.4 412.4 412.4 412.6 412.8 413.2 413.7 414.4 415.2 416.2 + 417.4 418.7 420.2 421.8 423.5 425.4 427.3 429.4 431.5 433.7 436.0 438.3 440.6 443.0 445.5 447.9 450.4 ][ 218.3 + 219.1 219.8 220.6 221.3 222.1 222.9 223.7 224.5 225.3 226.1 226.9 227.7 228.6 229.4 230.3 231.1 232.0 232.8 + 233.7 234.6 235.5 236.4 237.2 238.1 239.0 239.9 240.9 241.8 242.7 243.6 244.5 245.5 246.4 247.3 248.3 249.2 + 250.2 251.1 252.1 253.0 254.0 255.0 255.9 256.9 257.9 258.9 259.8 260.8 261.8 262.8 263.8 264.8 265.8 266.8 + 267.8 268.8 269.8 270.8 271.8 272.8 273.8 274.8 275.8 276.8 277.8 278.8 279.9 280.9 281.9 282.9 283.9 285.0 + 286.0 287.0 288.0 289.0 290.1 291.1 292.1 293.1 294.2 295.2 296.2 297.2 298.3 299.3 300.3 301.3 302.4 303.4 + 304.4 305.4 306.4 307.5 308.5 309.5 310.5 311.5 312.5 313.6 314.6 315.6 316.6 317.6 318.6 319.6 320.6 321.6 + 322.6 323.6 324.6 325.6 326.6 327.6 328.6 329.5 330.5 331.5 332.5 333.4 334.4 335.4 336.3 337.3 338.3 339.2 + 340.2 341.1 342.0 343.0 343.9 344.9 345.8 346.7 347.6 348.5 349.4 350.4 351.3 352.2 353.0 353.9 354.8 355.7 + 356.6 357.4 358.3 359.1 360.0 360.8 361.6 362.5 363.3 364.1 364.9 365.7 366.5 367.2 368.0 368.8 369.5 370.2 + 370.9 371.6 372.3 373.0 373.6 374.2 374.8 375.4 375.9 376.4 376.9 377.3 377.7 378.0 378.3 378.6 378.8 379.0 + 379.1 379.3 379.3 379.4 379.4 379.4 379.4 379.3 379.3 379.2 379.0 378.9 378.7 378.5 378.3 378.1 ]plong + s[ 388.9 388.2 387.6 387.0 386.4 385.8 385.2 384.6 384.1 383.5 382.9 382.4 381.8 381.3 380.7 380.2 379.6 379.1 + 378.6 378.1 377.5 377.0 376.5 376.0 375.6 375.1 374.6 374.1 373.7 373.2 372.7 372.3 371.8 371.4 371.0 370.5 + 370.1 369.7 369.3 368.9 368.5 368.1 367.7 367.3 367.0 366.6 366.2 365.9 365.5 365.2 364.8 364.5 364.1 363.8 + 363.5 363.2 362.9 362.6 362.3 362.0 361.7 361.4 361.2 360.9 360.6 360.4 360.1 359.9 359.6 359.4 359.2 358.9 + 358.7 358.5 358.3 358.1 357.9 357.7 357.6 357.4 357.2 357.1 356.9 356.7 356.6 356.5 356.3 356.2 356.1 356.0 + 355.9 355.8 355.7 355.6 355.5 355.4 355.3 355.3 355.2 355.2 355.1 355.1 355.0 355.0 355.0 355.0 355.0 355.0 + 355.0 355.0 355.0 355.0 355.0 355.1 355.1 355.2 355.2 355.3 355.4 355.4 355.5 355.6 355.7 355.8 355.9 356.0 + 356.1 356.3 356.4 356.5 356.7 356.8 357.0 357.2 357.4 357.5 357.7 357.9 358.1 358.3 358.5 358.8 359.0 359.2 + 359.5 359.7 360.0 360.2 360.5 360.7 361.0 361.3 361.6 361.9 362.2 362.5 362.8 363.1 363.4 363.6 363.9 364.2 + 364.5 364.8 365.1 365.3 365.5 365.7 365.9 366.0 366.1 366.1 366.0 365.9 365.6 365.2 364.6 363.9 363.1 362.0 + 360.8 359.5 358.0 356.3 354.5 352.6 350.6 348.6 346.4 344.2 341.9 339.5 337.2 334.7 332.3 329.8 327.3 ][ 218.3 + 219.1 219.8 220.6 221.3 222.1 222.9 223.7 224.5 225.3 226.1 227.0 227.8 228.6 229.5 230.3 231.2 232.0 232.9 + 233.8 234.7 235.6 236.4 237.3 238.2 239.1 240.0 241.0 241.9 242.8 243.7 244.6 245.6 246.5 247.5 248.4 249.3 + 250.3 251.2 252.2 253.2 254.1 255.1 256.1 257.0 258.0 259.0 260.0 260.9 261.9 262.9 263.9 264.9 265.9 266.9 + 267.9 268.9 269.9 270.9 271.9 272.9 273.9 274.9 275.9 277.0 278.0 279.0 280.0 281.0 282.0 283.1 284.1 285.1 + 286.1 287.2 288.2 289.2 290.2 291.2 292.3 293.3 294.3 295.3 296.4 297.4 298.4 299.4 300.5 301.5 302.5 303.5 + 304.6 305.6 306.6 307.6 308.6 309.7 310.7 311.7 312.7 313.7 314.7 315.7 316.8 317.8 318.8 319.8 320.8 321.8 + 322.8 323.8 324.8 325.8 326.7 327.7 328.7 329.7 330.7 331.7 332.6 333.6 334.6 335.5 336.5 337.5 338.4 339.4 + 340.3 341.3 342.2 343.1 344.1 345.0 345.9 346.9 347.8 348.7 349.6 350.5 351.4 352.3 353.2 354.1 355.0 355.8 + 356.7 357.6 358.4 359.3 360.1 361.0 361.8 362.6 363.4 364.3 365.1 365.8 366.6 367.4 368.2 368.9 369.7 370.4 + 371.1 371.8 372.5 373.1 373.8 374.4 375.0 375.5 376.1 376.6 377.0 377.5 377.8 378.2 378.5 378.7 379.0 379.1 + 379.3 379.4 379.5 379.5 379.5 379.5 379.5 379.4 379.3 379.2 379.1 378.9 378.7 378.5 378.3 378.1 ]plong + s[ 388.9 387.6 386.3 385.1 383.8 382.5 381.3 380.0 378.7 377.4 376.1 374.8 373.5 372.2 370.8 369.5 368.2 366.9 + 365.5 364.2 362.9 361.5 360.2 358.8 357.5 356.2 354.9 353.5 352.2 350.9 349.6 348.3 347.0 345.7 344.5 343.2 + 341.9 340.7 339.4 338.2 337.0 335.8 334.6 333.4 332.2 331.1 329.9 328.8 327.7 326.5 325.4 324.4 323.3 322.2 + 321.2 320.2 319.1 318.1 317.2 316.2 315.2 314.3 313.4 312.4 311.5 310.7 309.8 308.9 308.1 307.3 306.5 305.7 + 304.9 304.2 303.4 302.7 302.0 301.3 300.6 300.0 299.3 298.7 298.1 297.5 296.9 296.4 295.9 295.3 294.8 294.3 + 293.9 293.4 293.0 292.6 292.2 291.8 291.4 291.1 290.8 290.4 290.1 289.9 289.6 289.4 289.2 289.0 288.8 288.6 + 288.5 288.3 288.2 288.1 288.1 288.0 288.0 287.9 287.9 288.0 288.0 288.0 288.1 288.2 288.3 288.5 288.6 288.8 + 289.0 289.2 289.4 289.6 289.9 290.2 290.5 290.8 291.1 291.5 291.8 292.2 292.6 293.1 293.5 294.0 294.5 295.0 + 295.5 296.0 296.6 297.1 297.7 298.3 298.9 299.6 300.2 300.9 301.6 302.3 303.0 303.7 304.5 305.2 306.0 306.8 + 307.5 308.3 309.2 310.0 310.8 311.6 312.5 313.3 314.1 315.0 315.8 316.6 317.5 318.3 319.1 319.9 320.7 321.4 + 322.2 322.9 323.6 324.2 324.8 325.4 325.9 326.4 326.8 327.1 327.4 327.6 327.7 327.7 327.7 327.5 327.3 ][ 218.3 + 218.3 218.4 218.5 218.6 218.8 218.9 219.1 219.4 219.7 219.9 220.3 220.6 221.0 221.4 221.8 222.2 222.7 223.2 + 223.7 224.2 224.7 225.2 225.8 226.4 227.0 227.6 228.2 228.9 229.5 230.2 230.8 231.5 232.2 232.9 233.7 234.4 + 235.1 235.9 236.7 237.4 238.2 239.0 239.8 240.6 241.4 242.2 243.1 243.9 244.8 245.6 246.5 247.3 248.2 249.1 + 250.0 250.9 251.8 252.7 253.6 254.5 255.5 256.4 257.3 258.3 259.2 260.2 261.1 262.1 263.0 264.0 265.0 266.0 + 267.0 267.9 268.9 269.9 270.9 271.9 272.9 274.0 275.0 276.0 277.0 278.0 279.0 280.1 281.1 282.1 283.2 284.2 + 285.2 286.3 287.3 288.4 289.4 290.5 291.5 292.6 293.6 294.7 295.7 296.8 297.8 298.9 299.9 301.0 302.0 303.1 + 304.2 305.2 306.3 307.3 308.4 309.4 310.5 311.5 312.6 313.6 314.7 315.7 316.8 317.8 318.9 319.9 321.0 322.0 + 323.0 324.1 325.1 326.1 327.2 328.2 329.2 330.2 331.2 332.2 333.2 334.2 335.2 336.2 337.2 338.2 339.2 340.2 + 341.1 342.1 343.1 344.0 345.0 345.9 346.9 347.8 348.7 349.6 350.5 351.4 352.3 353.2 354.1 355.0 355.9 356.7 + 357.6 358.4 359.2 360.0 360.9 361.7 362.4 363.2 364.0 364.7 365.5 366.2 366.9 367.6 368.3 369.0 369.6 370.3 + 370.9 371.5 372.1 372.6 373.2 373.7 374.2 374.7 375.2 375.7 376.1 376.5 377.0 377.3 377.7 378.1 ]plong + s[ 388.9 388.2 387.6 386.9 386.2 385.6 384.9 384.2 383.5 382.8 382.1 381.3 380.5 379.7 378.9 378.0 377.0 376.0 + 374.9 373.8 372.5 371.1 369.6 368.1 366.4 364.7 362.9 361.0 359.0 357.0 355.0 352.9 350.8 348.7 346.5 344.3 + 342.2 340.0 337.8 335.6 333.4 331.2 329.0 326.8 324.7 322.5 320.3 318.2 316.1 314.0 311.9 309.8 307.7 305.7 + 303.7 301.7 299.7 297.7 295.8 293.9 292.0 290.1 288.2 286.4 284.6 282.8 281.1 279.3 277.6 275.9 274.3 272.7 + 271.1 269.5 268.0 266.4 264.9 263.5 262.1 260.7 259.3 258.0 256.6 255.4 254.1 252.9 251.7 250.5 249.4 248.3 + 247.3 246.2 245.2 244.3 243.3 242.4 241.6 240.7 239.9 239.1 238.4 237.7 237.0 236.4 235.8 235.2 234.7 234.2 + 233.7 233.3 232.9 232.6 232.2 231.9 231.7 231.5 231.3 231.1 231.0 231.0 230.9 230.9 230.9 231.0 231.1 231.2 + 231.4 231.6 231.9 232.1 232.5 232.8 233.2 233.6 234.1 234.6 235.1 235.7 236.3 236.9 237.6 238.3 239.1 239.9 + 240.7 241.5 242.4 243.4 244.3 245.3 246.4 247.4 248.5 249.7 250.9 252.1 253.3 254.6 255.9 257.2 258.6 260.0 + 261.5 262.9 264.5 266.0 267.6 269.2 270.8 272.5 274.2 275.9 277.7 279.5 281.3 283.2 285.0 286.9 288.9 290.8 + 292.8 294.8 296.9 298.9 301.0 303.1 305.2 307.4 309.6 311.7 313.9 316.1 318.4 320.6 322.8 325.1 327.3 ][ 218.3 + 217.6 216.9 216.2 215.5 214.9 214.2 213.6 213.0 212.4 211.8 211.3 210.8 210.4 210.0 209.6 209.3 209.0 208.8 + 208.6 208.5 208.5 208.5 208.5 208.6 208.7 208.9 209.1 209.4 209.6 209.9 210.3 210.6 211.0 211.4 211.8 212.3 + 212.7 213.2 213.7 214.3 214.8 215.4 216.0 216.6 217.2 217.8 218.5 219.1 219.8 220.5 221.2 222.0 222.7 223.5 + 224.3 225.0 225.9 226.7 227.5 228.4 229.2 230.1 231.0 231.9 232.8 233.7 234.7 235.6 236.6 237.6 238.6 239.6 + 240.6 241.6 242.6 243.7 244.7 245.8 246.8 247.9 249.0 250.1 251.2 252.3 253.5 254.6 255.7 256.9 258.0 259.2 + 260.4 261.6 262.7 263.9 265.1 266.3 267.6 268.8 270.0 271.2 272.5 273.7 274.9 276.2 277.4 278.7 280.0 281.2 + 282.5 283.8 285.0 286.3 287.6 288.9 290.2 291.4 292.7 294.0 295.3 296.6 297.9 299.2 300.5 301.7 303.0 304.3 + 305.6 306.9 308.2 309.5 310.7 312.0 313.3 314.6 315.9 317.1 318.4 319.7 320.9 322.2 323.4 324.7 325.9 327.1 + 328.4 329.6 330.8 332.0 333.3 334.5 335.6 336.8 338.0 339.2 340.4 341.5 342.7 343.8 344.9 346.1 347.2 348.3 + 349.4 350.5 351.5 352.6 353.6 354.7 355.7 356.7 357.7 358.7 359.7 360.7 361.6 362.6 363.5 364.4 365.3 366.2 + 367.0 367.9 368.7 369.5 370.3 371.1 371.9 372.7 373.4 374.1 374.8 375.5 376.2 376.8 377.4 378.1 ]plong + s[ 388.9 389.5 390.1 390.8 391.4 392.1 392.7 393.4 394.1 394.8 395.5 396.2 397.0 397.7 398.6 399.4 400.4 401.4 + 402.4 403.6 404.9 406.2 407.7 409.2 410.9 412.6 414.5 416.4 418.3 420.3 422.4 424.5 426.6 428.8 430.9 433.1 + 435.3 437.5 439.8 442.0 444.2 446.4 448.6 450.8 453.0 455.2 457.3 459.5 461.6 463.8 465.9 468.0 470.0 472.1 + 474.1 476.2 478.2 480.1 482.1 484.0 486.0 487.8 489.7 491.6 493.4 495.2 496.9 498.7 500.4 502.1 503.8 505.4 + 507.0 508.6 510.2 511.7 513.2 514.7 516.1 517.5 518.9 520.2 521.6 522.9 524.1 525.4 526.6 527.7 528.9 530.0 + 531.0 532.1 533.1 534.1 535.0 535.9 536.8 537.6 538.4 539.2 540.0 540.7 541.3 542.0 542.6 543.2 543.7 544.2 + 544.7 545.1 545.5 545.9 546.2 546.5 546.7 547.0 547.1 547.3 547.4 547.5 547.5 547.5 547.5 547.4 547.3 547.2 + 547.0 546.8 546.6 546.3 546.0 545.6 545.2 544.8 544.3 543.8 543.3 542.7 542.1 541.4 540.8 540.0 539.3 538.5 + 537.7 536.8 535.9 535.0 534.0 533.0 532.0 530.9 529.8 528.6 527.4 526.2 525.0 523.7 522.4 521.0 519.6 518.2 + 516.7 515.3 513.7 512.2 510.6 509.0 507.3 505.6 503.9 502.2 500.4 498.6 496.8 494.9 493.0 491.1 489.1 487.2 + 485.2 483.1 481.1 479.0 476.9 474.8 472.7 470.5 468.3 466.1 463.9 461.7 459.4 457.2 454.9 452.7 450.4 ][ 218.3 + 217.6 216.9 216.2 215.5 214.8 214.2 213.5 212.9 212.3 211.8 211.2 210.7 210.2 209.8 209.4 209.1 208.8 208.6 + 208.4 208.3 208.2 208.2 208.3 208.4 208.5 208.6 208.9 209.1 209.4 209.7 210.0 210.3 210.7 211.1 211.5 212.0 + 212.4 212.9 213.4 214.0 214.5 215.1 215.6 216.2 216.9 217.5 218.1 218.8 219.5 220.2 220.9 221.6 222.4 223.1 + 223.9 224.7 225.5 226.3 227.2 228.0 228.9 229.8 230.6 231.5 232.5 233.4 234.3 235.3 236.2 237.2 238.2 239.2 + 240.2 241.2 242.3 243.3 244.4 245.4 246.5 247.6 248.7 249.8 250.9 252.0 253.1 254.3 255.4 256.6 257.7 258.9 + 260.1 261.3 262.5 263.7 264.9 266.1 267.3 268.5 269.7 271.0 272.2 273.4 274.7 275.9 277.2 278.5 279.7 281.0 + 282.3 283.5 284.8 286.1 287.4 288.7 289.9 291.2 292.5 293.8 295.1 296.4 297.7 299.0 300.3 301.6 302.9 304.2 + 305.5 306.7 308.0 309.3 310.6 311.9 313.2 314.5 315.7 317.0 318.3 319.6 320.8 322.1 323.3 324.6 325.8 327.1 + 328.3 329.5 330.8 332.0 333.2 334.4 335.6 336.8 338.0 339.1 340.3 341.5 342.6 343.8 344.9 346.0 347.1 348.3 + 349.3 350.4 351.5 352.6 353.6 354.7 355.7 356.7 357.7 358.7 359.7 360.7 361.6 362.6 363.5 364.4 365.3 366.2 + 367.0 367.9 368.7 369.5 370.4 371.1 371.9 372.7 373.4 374.1 374.8 375.5 376.2 376.8 377.4 378.1 ]plong + s[ 388.9 390.1 391.4 392.6 393.9 395.2 396.5 397.7 399.0 400.3 401.6 402.9 404.3 405.6 406.9 408.2 409.6 410.9 + 412.2 413.6 414.9 416.3 417.6 419.0 420.3 421.6 423.0 424.3 425.6 427.0 428.3 429.6 430.9 432.2 433.5 434.7 + 436.0 437.3 438.5 439.8 441.0 442.2 443.4 444.6 445.8 447.0 448.1 449.3 450.4 451.6 452.7 453.8 454.8 455.9 + 457.0 458.0 459.0 460.1 461.1 462.0 463.0 464.0 464.9 465.8 466.7 467.6 468.5 469.4 470.2 471.0 471.8 472.6 + 473.4 474.2 474.9 475.7 476.4 477.1 477.8 478.4 479.1 479.7 480.3 480.9 481.5 482.1 482.6 483.2 483.7 484.2 + 484.6 485.1 485.5 486.0 486.4 486.7 487.1 487.5 487.8 488.1 488.4 488.7 489.0 489.2 489.4 489.6 489.8 490.0 + 490.1 490.3 490.4 490.5 490.6 490.6 490.7 490.7 490.7 490.7 490.6 490.6 490.5 490.4 490.3 490.2 490.0 489.9 + 489.7 489.5 489.2 489.0 488.7 488.5 488.2 487.8 487.5 487.2 486.8 486.4 486.0 485.5 485.1 484.6 484.1 483.6 + 483.1 482.6 482.0 481.5 480.9 480.3 479.6 479.0 478.3 477.7 477.0 476.3 475.5 474.8 474.1 473.3 472.5 471.7 + 470.9 470.1 469.3 468.5 467.7 466.8 466.0 465.1 464.3 463.4 462.6 461.7 460.9 460.0 459.2 458.4 457.6 456.8 + 456.1 455.3 454.6 453.9 453.3 452.7 452.2 451.7 451.3 450.9 450.6 450.4 450.2 450.1 450.1 450.2 450.4 ][ 218.3 + 218.3 218.4 218.4 218.6 218.7 218.9 219.1 219.3 219.5 219.8 220.1 220.5 220.8 221.2 221.6 222.0 222.5 223.0 + 223.5 224.0 224.5 225.0 225.6 226.2 226.7 227.4 228.0 228.6 229.2 229.9 230.6 231.3 232.0 232.7 233.4 234.1 + 234.8 235.6 236.3 237.1 237.9 238.7 239.5 240.3 241.1 241.9 242.7 243.6 244.4 245.3 246.1 247.0 247.9 248.8 + 249.6 250.5 251.4 252.3 253.3 254.2 255.1 256.0 257.0 257.9 258.8 259.8 260.8 261.7 262.7 263.7 264.6 265.6 + 266.6 267.6 268.6 269.6 270.6 271.6 272.6 273.6 274.6 275.6 276.6 277.7 278.7 279.7 280.7 281.8 282.8 283.8 + 284.9 285.9 287.0 288.0 289.1 290.1 291.2 292.2 293.3 294.3 295.4 296.4 297.5 298.5 299.6 300.6 301.7 302.8 + 303.8 304.9 305.9 307.0 308.1 309.1 310.2 311.2 312.3 313.3 314.4 315.4 316.5 317.5 318.6 319.6 320.7 321.7 + 322.7 323.8 324.8 325.8 326.9 327.9 328.9 329.9 330.9 332.0 333.0 334.0 335.0 336.0 337.0 338.0 338.9 339.9 + 340.9 341.9 342.8 343.8 344.7 345.7 346.6 347.6 348.5 349.4 350.3 351.2 352.1 353.0 353.9 354.8 355.7 356.5 + 357.4 358.2 359.1 359.9 360.7 361.5 362.3 363.1 363.8 364.6 365.3 366.1 366.8 367.5 368.2 368.8 369.5 370.1 + 370.8 371.4 372.0 372.6 373.1 373.7 374.2 374.7 375.2 375.6 376.1 376.5 376.9 377.3 377.7 378.1 ]plong + s[ 388.9 389.5 390.1 390.7 391.4 392.0 392.6 393.2 393.8 394.4 395.0 395.5 396.1 396.7 397.2 397.8 398.3 398.9 + 399.4 399.9 400.5 401.0 401.5 402.0 402.5 403.0 403.5 404.0 404.5 405.0 405.4 405.9 406.3 406.8 407.2 407.7 + 408.1 408.5 409.0 409.4 409.8 410.2 410.6 411.0 411.4 411.8 412.1 412.5 412.9 413.2 413.6 413.9 414.3 414.6 + 414.9 415.3 415.6 415.9 416.2 416.5 416.8 417.1 417.4 417.6 417.9 418.2 418.4 418.7 418.9 419.2 419.4 419.6 + 419.8 420.1 420.3 420.5 420.7 420.9 421.1 421.2 421.4 421.6 421.7 421.9 422.0 422.2 422.3 422.4 422.6 422.7 + 422.8 422.9 423.0 423.1 423.2 423.3 423.3 423.4 423.5 423.5 423.6 423.6 423.7 423.7 423.7 423.7 423.7 423.7 + 423.7 423.7 423.7 423.7 423.7 423.6 423.6 423.5 423.5 423.4 423.3 423.3 423.2 423.1 423.0 422.9 422.8 422.7 + 422.5 422.4 422.3 422.1 422.0 421.8 421.7 421.5 421.3 421.1 420.9 420.7 420.5 420.3 420.1 419.9 419.6 419.4 + 419.1 418.9 418.6 418.4 418.1 417.8 417.6 417.3 417.0 416.7 416.4 416.1 415.8 415.5 415.2 414.9 414.6 414.3 + 414.0 413.7 413.4 413.2 412.9 412.7 412.6 412.4 412.4 412.4 412.4 412.6 412.8 413.2 413.7 414.4 415.2 416.2 + 417.4 418.7 420.2 421.8 423.5 425.4 427.3 429.4 431.5 433.7 436.0 438.3 440.6 443.0 445.5 447.9 450.4 ][ 218.3 + 219.1 219.8 220.6 221.3 222.1 222.9 223.7 224.5 225.3 226.1 226.9 227.7 228.6 229.4 230.3 231.1 232.0 232.8 + 233.7 234.6 235.5 236.4 237.2 238.1 239.0 239.9 240.9 241.8 242.7 243.6 244.5 245.5 246.4 247.3 248.3 249.2 + 250.2 251.1 252.1 253.0 254.0 255.0 255.9 256.9 257.9 258.9 259.8 260.8 261.8 262.8 263.8 264.8 265.8 266.8 + 267.8 268.8 269.8 270.8 271.8 272.8 273.8 274.8 275.8 276.8 277.8 278.8 279.9 280.9 281.9 282.9 283.9 285.0 + 286.0 287.0 288.0 289.0 290.1 291.1 292.1 293.1 294.2 295.2 296.2 297.2 298.3 299.3 300.3 301.3 302.4 303.4 + 304.4 305.4 306.4 307.5 308.5 309.5 310.5 311.5 312.5 313.6 314.6 315.6 316.6 317.6 318.6 319.6 320.6 321.6 + 322.6 323.6 324.6 325.6 326.6 327.6 328.6 329.5 330.5 331.5 332.5 333.4 334.4 335.4 336.3 337.3 338.3 339.2 + 340.2 341.1 342.0 343.0 343.9 344.9 345.8 346.7 347.6 348.5 349.4 350.4 351.3 352.2 353.0 353.9 354.8 355.7 + 356.6 357.4 358.3 359.1 360.0 360.8 361.6 362.5 363.3 364.1 364.9 365.7 366.5 367.2 368.0 368.8 369.5 370.2 + 370.9 371.6 372.3 373.0 373.6 374.2 374.8 375.4 375.9 376.4 376.9 377.3 377.7 378.0 378.3 378.6 378.8 379.0 + 379.1 379.3 379.3 379.4 379.4 379.4 379.4 379.3 379.3 379.2 379.0 378.9 378.7 378.5 378.3 378.1 ]plong + s[ 431.8 431.2 430.6 429.9 429.2 428.4 427.6 426.8 425.9 425.0 424.0 423.0 422.0 420.9 419.8 418.7 417.5 416.3 + 415.1 413.9 412.6 411.3 410.0 408.7 407.3 406.0 404.6 403.2 401.8 400.4 398.9 397.5 396.0 394.6 393.1 391.7 + 390.2 388.7 387.3 385.8 384.4 382.9 381.5 380.0 378.6 377.1 375.7 374.3 372.9 371.6 370.2 368.8 367.5 366.2 + 364.9 363.7 362.4 361.2 360.0 358.9 357.8 356.7 355.6 354.6 353.6 352.6 351.7 350.8 350.0 349.2 348.4 347.7 + 347.1 346.4 345.9 345.4 344.9 344.5 344.1 343.9 343.6 343.4 343.3 343.2 343.2 343.3 343.4 343.6 343.8 344.1 + 344.5 344.9 345.4 345.9 346.5 347.2 347.9 348.6 349.4 350.2 351.0 351.8 352.7 353.5 354.2 354.9 355.5 356.0 + 356.3 356.5 356.5 356.5 356.6 356.6 356.6 356.6 356.6 356.6 356.6 356.6 356.6 356.6 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 ][ 231.7 232.5 233.3 234.1 234.8 235.6 236.3 237.0 237.7 238.4 239.0 239.6 240.2 240.8 241.4 242.0 + 242.5 243.0 243.5 244.0 244.4 244.8 245.2 245.6 245.9 246.2 246.5 246.8 247.1 247.3 247.5 247.6 247.8 247.9 + 248.0 248.1 248.1 248.1 248.1 248.0 248.0 247.9 247.8 247.6 247.4 247.2 247.0 246.8 246.5 246.2 245.9 245.5 + 245.2 244.8 244.3 243.9 243.4 242.9 242.4 241.9 241.3 240.8 240.2 239.5 238.9 238.3 237.6 236.9 236.2 235.5 + 234.7 234.0 233.2 232.4 231.6 230.8 230.0 229.1 228.3 227.4 226.6 225.7 224.8 223.9 223.1 222.2 221.3 220.4 + 219.5 218.6 217.7 216.8 215.9 215.1 214.2 213.3 212.5 211.7 210.9 210.1 209.3 208.5 207.8 207.1 206.4 205.8 + 205.2 204.6 204.0 203.5 203.5 203.4 203.4 203.4 203.3 203.3 203.3 203.2 203.2 203.2 203.1 203.1 203.0 203.0 + 203.0 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 ]plong + s[ 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 356.5 + 356.5 356.5 356.5 356.5 ][ 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 ]plong + s[ 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.1 421.1 421.2 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.4 421.8 422.3 422.9 423.6 424.4 425.2 426.0 426.8 427.7 + 428.4 429.2 429.9 430.6 431.3 431.9 432.4 432.9 433.3 433.6 433.9 434.2 434.3 434.4 434.5 434.5 434.4 434.3 + 434.1 433.8 433.5 433.2 432.7 432.3 431.8 431.2 430.6 429.9 429.2 428.4 427.6 426.8 425.9 425.0 424.0 423.0 + 422.0 420.9 419.8 418.7 417.5 416.3 415.1 413.9 412.6 411.3 410.0 408.7 407.3 406.0 404.6 403.2 401.8 400.4 + 398.9 397.5 ][ 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.8 + 202.8 202.8 202.8 202.8 202.8 202.8 202.8 202.9 202.9 202.9 203.0 203.0 203.0 203.1 203.1 203.2 203.2 203.2 + 203.3 203.3 203.3 203.4 203.4 203.4 203.5 203.5 203.6 203.6 204.1 204.7 205.3 205.9 206.5 207.2 207.9 208.6 + 209.4 210.2 211.0 211.8 212.6 213.5 214.3 215.2 216.1 217.0 217.8 218.7 219.6 220.5 221.4 222.3 223.2 224.1 + 225.0 225.8 226.7 227.6 228.4 229.3 230.1 230.9 231.7 232.5 233.3 234.1 234.8 235.6 236.3 237.0 237.7 238.4 + 239.0 239.6 240.2 240.8 241.4 242.0 242.5 243.0 243.5 244.0 244.4 244.8 245.2 245.6 245.9 246.2 246.5 246.8 + 247.1 247.3 247.5 247.6 ]plong + s[ 397.5 396.0 394.6 393.1 391.7 390.2 388.7 387.3 385.8 384.4 382.9 381.5 380.0 378.6 377.1 375.7 374.3 372.9 + 371.6 ][ 247.6 247.8 247.9 248.0 248.1 248.1 248.1 248.1 248.0 248.0 247.9 247.8 247.6 247.4 247.2 247.0 246.8 + 246.5 246.2 ]plong + s[ 467.9 466.3 464.6 462.9 461.1 459.3 457.5 455.6 453.7 451.7 449.7 447.7 445.7 443.6 441.6 439.4 437.3 435.1 + 432.9 430.7 428.5 426.2 424.0 421.7 419.4 417.1 414.8 412.4 410.1 407.7 405.4 403.0 400.6 398.2 395.9 393.5 + 391.1 388.7 386.3 383.9 381.5 379.1 376.7 374.4 372.0 369.6 367.3 364.9 362.6 360.3 358.0 355.7 353.4 351.1 + 348.9 346.7 344.5 342.3 340.1 338.0 335.9 333.8 331.7 329.7 327.7 325.7 323.8 321.9 320.0 318.2 316.4 314.6 + 312.9 311.2 309.6 308.0 306.4 304.9 303.4 302.0 300.6 299.3 298.0 296.7 295.5 294.3 293.2 292.1 291.1 290.1 + 289.1 288.1 287.2 286.3 285.5 284.6 283.8 283.0 282.2 281.4 280.6 279.8 279.0 278.2 277.4 276.5 275.6 274.7 + 273.8 272.8 272.7 272.6 272.5 272.5 272.4 272.3 272.2 272.2 272.1 272.0 271.9 271.9 271.8 271.7 271.6 271.6 + 271.5 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 + 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 271.3 + 271.3 271.3 271.3 271.3 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 + 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 + 271.2 271.2 ][ 259.2 260.2 261.2 262.2 263.2 264.2 265.1 266.0 266.9 267.8 268.7 269.5 270.3 271.1 271.9 272.6 + 273.3 274.0 274.7 275.3 275.9 276.4 277.0 277.5 277.9 278.4 278.8 279.1 279.5 279.8 280.0 280.3 280.5 280.6 + 280.7 280.8 280.9 280.9 280.9 280.8 280.7 280.6 280.4 280.2 280.0 279.7 279.4 279.1 278.7 278.3 277.9 277.4 + 276.9 276.4 275.8 275.2 274.6 273.9 273.2 272.5 271.8 271.0 270.2 269.4 268.6 267.7 266.8 265.9 265.0 264.0 + 263.1 262.1 261.1 260.0 259.0 258.0 256.9 255.8 254.8 253.7 252.6 251.5 250.4 249.2 248.1 247.0 245.9 244.8 + 243.7 242.6 241.5 240.4 239.3 238.2 237.2 236.1 235.1 234.1 233.1 232.1 231.2 230.3 229.4 228.5 227.7 226.8 + 226.0 225.3 224.6 223.8 223.8 223.8 223.7 223.7 223.6 223.6 223.5 223.5 223.4 223.4 223.3 223.3 223.2 223.2 + 223.1 223.1 223.0 223.0 223.0 223.0 223.0 223.0 223.0 223.0 223.0 223.0 223.0 223.0 223.0 223.0 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 ]plong + s[ 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 + 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 + 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 + 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 271.2 + 271.2 271.2 271.2 271.2 ][ 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 ]plong + s[ 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 + 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 + 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 + 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 + 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 + 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.5 506.4 506.4 506.4 506.4 506.4 506.4 506.4 + 506.4 506.4 506.4 506.4 506.4 506.3 506.2 506.2 506.1 506.0 505.9 505.8 505.8 505.7 505.6 505.5 505.5 505.4 + 505.3 505.3 505.2 505.1 505.0 505.0 504.9 504.8 503.8 502.9 501.9 501.1 500.2 499.4 498.6 497.8 497.0 496.2 + 495.4 494.6 493.8 492.9 492.1 491.2 490.3 489.4 488.5 487.5 486.5 485.4 484.3 483.2 482.0 480.8 479.6 478.2 + 476.9 475.5 474.1 472.6 471.1 469.5 467.9 466.3 464.6 462.9 461.1 459.3 457.5 455.6 453.7 451.7 449.7 447.7 + 445.7 443.6 441.6 439.4 437.3 435.1 432.9 430.7 428.5 426.2 424.0 421.7 419.4 417.1 414.8 412.4 410.1 407.7 + 405.4 403.0 ][ 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 222.9 + 222.9 222.9 222.9 222.9 222.9 222.9 222.9 223.0 223.0 223.1 223.1 223.2 223.2 223.3 223.3 223.4 223.4 223.5 + 223.5 223.6 223.6 223.7 223.7 223.8 223.8 223.9 223.9 224.0 224.7 225.4 226.2 227.0 227.8 228.6 229.5 230.4 + 231.3 232.3 233.3 234.3 235.3 236.3 237.3 238.4 239.5 240.5 241.6 242.7 243.8 244.9 246.1 247.2 248.3 249.4 + 250.5 251.6 252.7 253.8 254.9 256.0 257.1 258.1 259.2 260.2 261.2 262.2 263.2 264.2 265.1 266.0 266.9 267.8 + 268.7 269.5 270.3 271.1 271.9 272.6 273.3 274.0 274.7 275.3 275.9 276.4 277.0 277.5 277.9 278.4 278.8 279.1 + 279.5 279.8 280.0 280.3 ]plong + s[ 403.0 400.6 398.2 395.9 393.5 391.1 388.7 386.3 383.9 381.5 379.1 376.7 374.4 372.0 369.6 367.3 364.9 362.6 + 360.3 ][ 280.3 280.5 280.6 280.7 280.8 280.9 280.9 280.9 280.8 280.7 280.6 280.4 280.2 280.0 279.7 279.4 279.1 + 278.7 278.3 ]plong + s[ 487.8 485.4 483.0 480.5 478.1 475.6 473.0 470.5 467.9 465.3 462.7 460.1 457.5 454.8 452.1 449.5 446.8 444.1 + 441.3 438.6 435.9 433.1 430.4 427.6 424.9 422.1 419.3 416.5 413.8 411.0 408.2 405.4 402.6 399.8 397.0 394.2 + 391.4 388.7 385.9 383.1 380.3 377.5 374.7 371.9 369.1 366.3 363.5 360.8 358.0 355.2 352.4 349.7 346.9 344.2 + 341.4 338.7 336.0 333.3 330.5 327.9 325.2 322.5 319.9 317.2 314.6 312.0 309.4 306.9 304.3 301.8 299.3 296.8 + 294.4 292.0 289.6 287.2 284.8 282.5 280.2 278.0 275.7 273.5 271.4 269.2 267.1 265.0 262.9 260.9 258.9 256.9 + 254.9 253.0 251.1 249.2 247.3 245.4 243.6 241.7 239.9 238.1 236.3 234.5 232.7 230.9 229.1 227.3 225.5 223.7 + 221.9 220.1 220.0 219.9 219.7 219.6 219.5 219.3 219.2 219.1 219.0 218.8 218.7 218.6 218.4 218.3 218.2 218.0 + 217.9 217.8 217.8 217.8 217.7 217.7 217.7 217.7 217.7 217.7 217.7 217.7 217.7 217.7 217.7 217.6 217.6 217.6 + 217.6 217.6 217.6 217.6 217.6 217.6 217.6 217.6 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 ][ 292.2 293.2 294.2 295.2 296.2 297.1 298.1 299.0 299.9 300.8 301.7 302.5 303.4 304.2 305.0 305.7 + 306.5 307.2 307.8 308.5 309.1 309.7 310.3 310.8 311.3 311.8 312.2 312.6 312.9 313.3 313.5 313.8 314.0 314.2 + 314.3 314.4 314.5 314.5 314.4 314.4 314.3 314.1 314.0 313.8 313.5 313.2 312.9 312.5 312.1 311.7 311.2 310.7 + 310.2 309.6 309.0 308.4 307.7 307.1 306.3 305.6 304.8 304.0 303.2 302.4 301.5 300.7 299.8 298.9 297.9 297.0 + 296.0 295.0 294.1 293.1 292.1 291.0 290.0 289.0 288.0 286.9 285.9 284.9 283.8 282.8 281.8 280.8 279.8 278.8 + 277.8 276.8 275.8 274.9 273.9 273.0 272.1 271.3 270.4 269.6 268.8 268.0 267.3 266.5 265.9 265.2 264.6 264.0 + 263.5 263.0 262.5 262.1 262.1 262.1 262.0 262.0 262.0 262.0 261.9 261.9 261.9 261.9 261.8 261.8 261.8 261.8 + 261.7 261.7 261.7 261.7 261.7 261.7 261.7 261.7 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 ]plong + s[ 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 ][ 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 ]plong + s[ 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.1 560.1 560.1 560.1 560.1 + 560.1 560.1 560.1 560.1 560.1 559.9 559.8 559.7 559.5 559.4 559.3 559.1 559.0 558.9 558.8 558.6 558.5 558.4 + 558.2 558.1 558.0 557.8 557.7 557.6 557.5 557.3 555.5 553.7 551.9 550.1 548.3 546.5 544.7 543.0 541.2 539.3 + 537.5 535.7 533.9 532.0 530.1 528.3 526.4 524.4 522.5 520.5 518.5 516.5 514.5 512.4 510.3 508.2 506.0 503.8 + 501.6 499.4 497.1 494.8 492.5 490.2 487.8 485.4 483.0 480.5 478.1 475.6 473.0 470.5 467.9 465.3 462.7 460.1 + 457.5 454.8 452.1 449.5 446.8 444.1 441.3 438.6 435.9 433.1 430.4 427.6 424.9 422.1 419.3 416.5 413.8 411.0 + 408.2 405.4 ][ 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.7 261.7 261.7 261.7 261.8 261.8 261.8 261.8 261.9 261.9 261.9 + 261.9 262.0 262.0 262.0 262.0 262.1 262.1 262.1 262.2 262.2 262.6 263.1 263.6 264.1 264.7 265.3 266.0 266.7 + 267.4 268.1 268.9 269.7 270.5 271.4 272.3 273.2 274.1 275.0 276.0 276.9 277.9 278.9 279.9 280.9 281.9 283.0 + 284.0 285.0 286.1 287.1 288.1 289.2 290.2 291.2 292.2 293.2 294.2 295.2 296.2 297.1 298.1 299.0 299.9 300.8 + 301.7 302.5 303.4 304.2 305.0 305.7 306.5 307.2 307.8 308.5 309.1 309.7 310.3 310.8 311.3 311.8 312.2 312.6 + 312.9 313.3 313.5 313.8 ]plong + s[ 405.4 402.6 399.8 397.0 394.2 391.4 388.7 385.9 383.1 380.3 377.5 374.7 371.9 369.1 366.3 363.5 360.8 358.0 + 355.2 ][ 313.8 314.0 314.2 314.3 314.4 314.5 314.5 314.4 314.4 314.3 314.1 314.0 313.8 313.5 313.2 312.9 312.5 + 312.1 311.7 ]plong + s[ 488.6 485.9 483.3 480.6 478.0 475.3 472.6 469.9 467.1 464.4 461.7 458.9 456.2 453.4 450.7 447.9 445.1 442.4 + 439.6 436.9 434.1 431.4 428.7 425.9 423.2 420.5 417.8 415.1 412.5 409.8 407.1 404.5 401.8 399.2 396.5 393.9 + 391.3 388.7 386.0 383.4 380.8 378.1 375.5 372.9 370.2 367.5 364.9 362.2 359.5 356.8 354.1 351.4 348.6 345.9 + 343.2 340.4 337.7 334.9 332.2 329.4 326.6 323.9 321.1 318.4 315.6 312.9 310.2 307.5 304.7 302.0 299.4 296.7 + 294.0 291.4 288.8 286.2 283.6 281.0 278.5 275.9 273.4 271.0 268.5 266.1 263.7 261.3 258.9 256.6 254.3 252.0 + 249.8 247.5 245.3 243.2 241.0 238.9 236.8 234.7 232.7 230.7 228.7 226.7 224.8 222.9 221.0 219.2 217.4 215.6 + 213.8 212.1 212.0 211.9 211.7 211.6 211.5 211.4 211.3 211.1 211.0 210.9 210.8 210.7 210.5 210.4 210.3 210.2 + 210.1 209.9 209.9 209.9 209.9 209.9 209.9 209.9 209.9 209.9 209.9 209.9 209.8 209.8 209.8 209.8 209.8 209.8 + 209.8 209.8 209.8 209.8 209.8 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 + 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 + 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 + 209.7 209.7 ][ 326.5 327.3 328.2 329.0 329.8 330.6 331.4 332.2 333.0 333.7 334.5 335.2 336.0 336.7 337.4 338.1 + 338.8 339.4 340.1 340.7 341.3 341.9 342.4 342.9 343.4 343.8 344.3 344.6 345.0 345.3 345.6 345.9 346.1 346.2 + 346.4 346.5 346.5 346.5 346.5 346.5 346.4 346.2 346.0 345.8 345.6 345.3 344.9 344.6 344.2 343.8 343.3 342.8 + 342.3 341.8 341.2 340.6 340.0 339.4 338.7 338.0 337.3 336.6 335.9 335.1 334.4 333.6 332.8 332.1 331.3 330.5 + 329.7 328.8 328.0 327.2 326.4 325.6 324.8 324.0 323.2 322.4 321.6 320.8 320.0 319.3 318.6 317.8 317.1 316.4 + 315.7 315.1 314.5 313.8 313.3 312.7 312.2 311.7 311.2 310.7 310.3 309.9 309.6 309.3 309.0 308.8 308.6 308.5 + 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.5 308.5 308.5 308.5 308.5 308.5 308.5 308.5 + 308.5 308.5 308.5 308.5 308.5 308.5 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 ]plong + s[ 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 + 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 + 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 + 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 209.7 + 209.7 209.7 209.7 209.7 ][ 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 ]plong + s[ 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 + 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 + 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 + 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 + 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 + 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 568.0 567.9 567.9 567.9 + 567.9 567.9 567.9 567.9 567.9 567.8 567.7 567.5 567.4 567.3 567.2 567.1 566.9 566.8 566.7 566.6 566.5 566.3 + 566.2 566.1 566.0 565.8 565.7 565.6 565.5 565.4 563.6 561.9 560.1 558.3 556.4 554.5 552.6 550.7 548.7 546.7 + 544.7 542.7 540.6 538.5 536.4 534.2 532.0 529.8 527.6 525.4 523.1 520.8 518.4 516.1 513.7 511.3 508.8 506.4 + 503.9 501.4 498.9 496.3 493.8 491.2 488.6 485.9 483.3 480.6 478.0 475.3 472.6 469.9 467.1 464.4 461.7 458.9 + 456.2 453.4 450.7 447.9 445.1 442.4 439.6 436.9 434.1 431.4 428.7 425.9 423.2 420.5 417.8 415.1 412.5 409.8 + 407.1 404.5 ][ 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.5 308.5 308.5 308.5 308.5 308.5 308.5 308.5 308.5 308.5 308.5 + 308.5 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.4 308.5 308.7 308.8 309.1 309.3 + 309.7 310.0 310.4 310.8 311.2 311.7 312.2 312.8 313.3 313.9 314.5 315.2 315.8 316.5 317.2 317.9 318.7 319.4 + 320.2 320.9 321.7 322.5 323.3 324.1 324.9 325.7 326.5 327.3 328.2 329.0 329.8 330.6 331.4 332.2 333.0 333.7 + 334.5 335.2 336.0 336.7 337.4 338.1 338.8 339.4 340.1 340.7 341.3 341.9 342.4 342.9 343.4 343.8 344.3 344.6 + 345.0 345.3 345.6 345.9 ]plong + s[ 404.5 401.8 399.2 396.5 393.9 391.3 388.7 386.0 383.4 380.8 378.1 375.5 372.9 370.2 367.5 364.9 362.2 359.5 + 356.8 ][ 345.9 346.1 346.2 346.4 346.5 346.5 346.5 346.5 346.5 346.4 346.2 346.0 345.8 345.6 345.3 344.9 344.6 + 344.2 343.8 ]plong + s[ 469.9 467.7 465.5 463.2 461.0 458.7 456.5 454.2 451.9 449.6 447.2 444.9 442.6 440.2 437.9 435.5 433.2 430.8 + 428.5 426.2 423.9 421.5 419.3 417.0 414.7 412.5 410.3 408.1 406.0 403.9 401.9 399.9 397.9 396.0 394.2 392.3 + 390.5 388.7 386.9 385.1 383.3 381.4 379.5 377.5 375.5 373.5 371.4 369.3 367.1 364.9 362.7 360.4 358.1 355.8 + 353.5 351.2 348.9 346.5 344.2 341.8 339.5 337.1 334.8 332.5 330.1 327.8 325.5 323.2 320.9 318.6 316.4 314.1 + 311.9 309.7 307.5 305.4 303.2 301.1 299.0 296.9 294.9 292.8 290.8 288.9 286.9 285.0 283.2 281.3 279.5 277.7 + 276.0 274.3 272.6 271.0 269.4 267.9 266.3 264.9 263.4 262.1 260.7 259.4 258.2 257.0 255.9 254.8 253.8 252.8 + 251.9 251.0 251.0 250.9 250.8 250.8 250.7 250.7 250.6 250.5 250.5 250.4 250.4 250.3 250.3 250.2 250.2 250.1 + 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.0 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 ][ 357.7 358.2 358.7 359.2 359.8 360.3 360.9 361.5 362.0 362.6 363.2 363.7 364.3 364.9 365.4 366.0 + 366.6 367.1 367.7 368.3 368.8 369.3 369.9 370.4 370.9 371.4 371.9 372.3 372.8 373.2 373.6 373.9 374.2 374.5 + 374.7 374.8 374.9 374.9 374.9 374.8 374.6 374.4 374.2 373.8 373.5 373.1 372.7 372.3 371.8 371.3 370.8 370.3 + 369.8 369.3 368.7 368.2 367.6 367.1 366.5 365.9 365.4 364.8 364.2 363.6 363.1 362.5 361.9 361.4 360.8 360.3 + 359.7 359.2 358.6 358.1 357.6 357.1 356.6 356.1 355.6 355.1 354.7 354.3 353.8 353.4 353.0 352.7 352.3 352.0 + 351.7 351.4 351.1 350.8 350.6 350.4 350.2 350.1 349.9 349.8 349.7 349.7 349.7 349.7 349.7 349.7 349.8 350.0 + 350.1 350.3 350.5 350.8 350.8 350.8 350.8 350.9 350.9 350.9 350.9 350.9 351.0 351.0 351.0 351.0 351.0 351.1 + 351.1 351.1 351.1 351.1 351.1 351.1 351.1 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 ]plong + s[ 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.9 + 249.9 249.9 249.9 249.9 ][ 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 ]plong + s[ 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 + 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 + 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 + 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 + 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 + 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 527.8 + 527.8 527.8 527.8 527.8 527.8 527.7 527.7 527.6 527.6 527.5 527.4 527.4 527.3 527.3 527.2 527.2 527.1 527.0 + 527.0 526.9 526.9 526.8 526.8 526.7 526.6 526.6 525.7 524.8 523.8 522.8 521.7 520.5 519.3 518.1 516.8 515.4 + 514.1 512.6 511.2 509.6 508.1 506.5 504.8 503.2 501.5 499.7 497.9 496.1 494.3 492.4 490.5 488.5 486.6 484.6 + 482.5 480.5 478.4 476.3 474.2 472.0 469.9 467.7 465.5 463.2 461.0 458.7 456.5 454.2 451.9 449.6 447.2 444.9 + 442.6 440.2 437.9 435.5 433.2 430.8 428.5 426.2 423.9 421.5 419.3 417.0 414.7 412.5 410.3 408.1 406.0 403.9 + 401.9 399.9 ][ 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.2 + 351.2 351.2 351.2 351.2 351.2 351.2 351.2 351.1 351.1 351.1 351.1 351.1 351.0 351.0 351.0 351.0 351.0 350.9 + 350.9 350.9 350.9 350.9 350.8 350.8 350.8 350.8 350.8 350.7 350.5 350.3 350.1 349.9 349.8 349.7 349.7 349.7 + 349.7 349.7 349.7 349.8 349.9 350.1 350.2 350.4 350.6 350.9 351.1 351.4 351.7 352.0 352.4 352.7 353.1 353.5 + 353.9 354.3 354.8 355.2 355.7 356.2 356.7 357.2 357.7 358.2 358.7 359.2 359.8 360.3 360.9 361.5 362.0 362.6 + 363.2 363.7 364.3 364.9 365.4 366.0 366.6 367.1 367.7 368.3 368.8 369.3 369.9 370.4 370.9 371.4 371.9 372.3 + 372.8 373.2 373.6 373.9 ]plong + s[ 399.9 397.9 396.0 394.2 392.3 390.5 388.7 386.9 385.1 383.3 381.4 379.5 377.5 375.5 373.5 371.4 369.3 367.1 + 364.9 ][ 373.9 374.2 374.5 374.7 374.8 374.9 374.9 374.9 374.8 374.6 374.4 374.2 373.8 373.5 373.1 372.7 372.3 + 371.8 371.3 ]plong + 327.3 378.1 327.3 378.1 327.3 378.1 3 pls + 412.2 315.9 412.1 313.1 2 pls + 400.8 316.9 400.7 314.1 2 pls + 389.3 317.2 389.3 314.5 2 pls + 377.9 316.9 377.9 314.2 2 pls + 366.5 316.0 366.6 313.2 2 pls + 343.8 312.3 343.9 309.6 2 pls + 332.6 309.7 332.8 306.9 2 pls + 321.5 306.6 321.8 303.8 2 pls + 310.7 303.1 311.1 300.4 2 pls + 300.2 299.3 300.7 296.6 2 pls + 280.4 291.0 281.2 288.4 2 pls + 271.2 286.8 272.1 284.2 2 pls + 262.4 282.6 263.5 280.0 2 pls + 254.1 278.5 255.3 276.0 2 pls + 246.2 274.6 247.4 272.2 2 pls + 231.2 268.2 232.6 265.8 2 pls + 223.8 265.8 225.2 263.4 2 pls + 216.4 264.0 217.8 261.7 2 pls + 554.5 265.6 553.1 263.2 2 pls + 547.2 267.9 545.8 265.6 2 pls + 532.2 274.3 530.9 271.9 2 pls + 524.3 278.1 523.1 275.6 2 pls + 516.0 282.2 515.0 279.7 2 pls + 507.3 286.4 506.4 283.8 2 pls + 498.1 290.7 497.3 288.0 2 pls + 478.4 298.9 477.8 296.2 2 pls + 467.9 302.8 467.5 300.1 2 pls + 457.1 306.3 456.8 303.6 2 pls + 446.1 309.4 445.9 306.7 2 pls + 434.9 312.1 434.7 309.4 2 pls + 382.7 215.5 385.3 214.6 2 pls + 378.9 212.4 381.4 211.4 2 pls + 374.2 210.4 376.7 209.2 2 pls + 367.5 209.7 370.0 208.5 2 pls + 358.2 210.6 360.6 209.2 2 pls + 336.1 215.6 337.5 213.3 2 pls + 325.0 218.9 325.6 216.3 2 pls + 314.2 222.5 314.0 219.8 2 pls + 303.7 226.4 302.8 223.8 2 pls + 293.6 230.6 292.2 228.3 2 pls + 275.1 240.2 273.0 238.3 2 pls + 266.8 245.5 264.6 243.9 2 pls + 259.4 251.2 257.1 249.7 2 pls + 252.8 257.1 250.4 255.9 2 pls + 247.2 263.4 244.7 262.2 2 pls + 238.7 276.4 236.1 275.5 2 pls + 236.0 283.1 233.4 282.4 2 pls + 234.3 290.0 231.6 289.3 2 pls + 233.6 296.9 230.9 296.3 2 pls + 234.0 303.8 231.3 303.3 2 pls + 237.9 317.6 235.2 317.3 2 pls + 241.5 324.4 238.7 324.1 2 pls + 246.1 331.0 243.4 330.8 2 pls + 251.8 337.5 249.0 337.4 2 pls + 258.5 343.7 255.7 343.7 2 pls + 274.9 355.4 272.1 355.5 2 pls + 284.5 360.8 281.7 360.9 2 pls + 294.9 365.7 292.2 365.9 2 pls + 306.1 370.3 303.4 370.4 2 pls + 317.9 374.3 315.2 374.5 2 pls + s[ 423.5 418.6 413.8 408.9 404.0 399.1 394.2 389.3 384.5 379.6 374.7 369.8 364.9 360.1 355.2 ][ 311.5 312.3 312.9 + 313.5 313.9 314.2 314.4 314.5 314.4 314.3 314.0 313.6 313.1 312.4 311.7 ]plong + s[ 355.2 350.4 345.5 340.7 336.0 331.2 326.5 321.8 317.2 312.6 308.1 303.7 299.3 295.0 290.8 ][ 311.7 310.9 309.9 + 308.9 307.7 306.5 305.2 303.8 302.4 300.9 299.3 297.7 296.0 294.3 292.6 ]plong + s[ 290.8 286.6 282.5 278.5 274.6 270.8 267.1 263.5 259.9 256.4 253.0 249.6 246.4 243.1 239.9 ][ 292.6 290.8 289.0 + 287.2 285.4 283.6 281.8 280.0 278.3 276.5 274.9 273.3 271.7 270.2 268.8 ]plong + s[ 239.9 236.7 233.6 230.5 227.3 224.2 221.0 220.8 220.6 220.3 220.1 219.9 219.7 219.4 219.2 219.0 218.8 218.5 + 218.3 218.1 217.8 217.8 217.8 217.8 217.8 217.8 217.7 217.7 217.7 217.7 217.7 217.7 217.6 217.6 217.6 217.6 + 217.6 217.6 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 + 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 217.5 ][ 268.8 267.4 266.2 265.1 264.0 263.1 262.3 + 262.3 262.2 262.2 262.1 262.1 262.0 262.0 261.9 261.9 261.8 261.8 261.8 261.7 261.7 261.7 261.7 261.7 261.7 + 261.7 261.7 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 ]plong + s[ 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.1 560.1 560.1 560.1 560.1 560.1 560.0 560.0 560.0 560.0 560.0 560.0 559.9 + 559.9 559.9 559.9 559.9 559.9 559.8 559.8 559.6 559.4 559.1 558.9 558.7 558.5 558.2 558.0 557.8 557.6 557.3 + 557.1 556.9 556.6 556.4 556.2 556.0 555.7 555.5 555.3 555.1 554.8 554.6 554.4 554.2 551.0 547.9 544.7 541.6 + 538.4 ][ 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 + 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.7 261.7 + 261.7 261.7 261.7 261.7 261.7 261.7 261.7 261.7 261.7 261.8 261.8 261.9 261.9 261.9 262.0 262.0 262.1 262.1 + 262.2 262.2 262.3 262.3 262.4 262.4 262.5 262.6 262.6 262.7 262.7 262.8 262.8 262.9 262.9 263.8 264.8 266.0 + 267.2 268.5 ]plong + s[ 538.4 535.2 532.0 528.7 525.4 522.0 518.5 515.0 511.4 507.6 503.8 500.0 496.0 491.9 487.8 ][ 268.5 269.9 271.4 + 272.9 274.5 276.2 277.9 279.7 281.4 283.2 285.0 286.8 288.6 290.4 292.2 ]plong + s[ 487.8 483.6 479.3 474.9 470.5 466.0 461.4 456.8 452.1 447.4 442.7 437.9 433.1 428.3 423.5 ][ 292.2 294.0 295.7 + 297.4 299.0 300.6 302.1 303.6 305.0 306.3 307.5 308.7 309.7 310.7 311.5 ]plong + s[ 423.5 418.6 413.8 408.9 404.0 399.1 394.2 389.3 384.5 379.6 374.7 369.8 364.9 360.1 355.2 ][ 311.5 312.3 312.9 + 313.5 313.9 314.2 314.4 314.5 314.4 314.3 314.0 313.6 313.1 312.4 311.7 ]plong + s[ 388.9 387.3 385.8 384.2 382.6 380.8 380.7 380.5 380.4 380.3 380.1 380.0 379.9 379.7 379.6 379.5 379.3 379.2 + 379.0 378.9 378.7 378.6 378.4 378.3 378.1 378.0 377.8 377.7 377.5 377.4 377.2 377.0 376.9 376.7 376.5 376.4 + 376.2 376.0 375.8 375.7 375.5 375.3 375.1 374.9 374.7 374.6 374.4 374.2 374.0 373.8 373.6 373.3 373.1 372.9 + 372.7 372.5 372.3 372.0 371.8 371.6 371.4 371.1 370.9 370.6 370.4 370.1 369.9 369.6 369.4 369.1 368.9 368.6 + 368.4 368.1 367.8 367.5 363.5 359.0 354.3 349.4 ][ 218.3 216.7 215.1 213.6 212.2 211.0 210.9 210.8 210.7 210.7 + 210.6 210.5 210.4 210.4 210.3 210.2 210.2 210.1 210.0 210.0 209.9 209.8 209.8 209.7 209.7 209.6 209.5 209.5 + 209.4 209.4 209.3 209.3 209.2 209.2 209.1 209.1 209.0 209.0 209.0 208.9 208.9 208.9 208.8 208.8 208.8 208.7 + 208.7 208.7 208.6 208.6 208.6 208.6 208.6 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 + 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.5 208.8 209.4 210.0 210.9 ]plong + s[ 349.4 344.3 339.2 334.1 329.0 323.9 318.9 314.0 309.1 304.3 299.7 295.1 290.7 286.4 282.2 ][ 210.9 211.8 212.9 + 214.1 215.4 216.8 218.2 219.8 221.5 223.2 225.0 227.0 228.9 231.0 233.1 ]plong + s[ 282.2 278.2 274.3 270.5 266.9 263.5 260.2 257.1 254.1 251.3 248.7 246.2 243.9 241.8 239.9 ][ 233.1 235.3 237.6 + 239.9 242.3 244.7 247.2 249.7 252.3 255.0 257.7 260.4 263.1 265.9 268.8 ]plong + s[ 239.9 238.2 236.6 235.2 234.0 233.0 232.2 231.6 231.2 231.0 230.9 231.1 231.4 232.0 232.7 ][ 268.8 271.6 274.5 + 277.4 280.4 283.3 286.3 289.3 292.3 295.3 298.3 301.3 304.3 307.3 310.3 ]plong + s[ 232.7 233.6 234.8 236.1 237.6 239.3 241.3 243.4 245.7 248.2 250.9 253.7 256.8 260.0 263.4 ][ 310.3 313.3 316.3 + 319.2 322.2 325.1 328.0 330.8 333.7 336.4 339.2 341.9 344.6 347.2 349.7 ]plong + s[ 263.4 267.1 270.8 274.8 278.9 283.2 287.6 292.2 296.9 301.7 306.7 311.7 316.9 322.1 327.3 ][ 349.7 352.2 354.7 + 357.1 359.4 361.6 363.8 365.9 367.9 369.8 371.7 373.4 375.0 376.6 378.1 ]plong + s[ 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 + 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.4 450.3 450.3 + 450.3 450.3 450.3 450.3 450.3 450.3 450.2 450.2 450.2 450.1 450.1 450.1 450.0 450.0 450.0 450.0 449.9 449.9 + 449.9 449.8 ][ 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 + 378.1 378.1 378.1 378.1 ]plong + s[ 449.8 449.8 449.8 449.7 449.7 449.7 449.7 449.6 449.6 449.6 449.2 448.7 448.3 447.9 447.5 447.1 446.7 446.3 + 445.9 445.5 445.1 444.6 444.2 443.8 443.4 443.0 442.6 442.2 441.8 441.4 441.0 440.6 440.2 439.9 439.5 439.1 + 433.7 428.7 424.1 420.2 419.9 419.7 419.4 419.2 418.9 418.7 418.5 418.3 418.0 417.8 417.6 417.4 417.2 417.0 + 416.8 416.6 416.4 416.2 416.1 415.9 415.7 415.6 415.4 415.2 415.1 414.9 414.8 414.7 414.5 414.4 414.3 414.2 + 414.1 413.9 413.8 413.7 413.6 413.5 413.5 413.4 413.3 413.2 413.1 413.1 413.0 412.9 412.9 412.8 412.8 412.7 + 412.7 412.6 412.6 412.6 412.5 412.5 412.5 412.5 412.4 412.4 412.4 412.4 412.4 412.4 412.4 412.4 412.3 412.3 + 412.3 412.4 412.4 412.4 412.4 412.4 412.4 412.4 412.4 412.4 412.5 412.5 412.5 412.5 412.5 412.6 413.0 413.6 ][ + 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.1 378.2 378.2 378.3 378.3 378.3 378.4 378.4 378.4 + 378.5 378.5 378.5 378.6 378.6 378.6 378.7 378.7 378.7 378.8 378.8 378.8 378.8 378.9 378.9 378.9 379.0 379.0 + 379.3 379.4 379.4 379.3 379.2 379.2 379.2 379.2 379.2 379.1 379.1 379.1 379.1 379.1 379.0 379.0 379.0 378.9 + 378.9 378.9 378.9 378.8 378.8 378.7 378.7 378.7 378.6 378.6 378.6 378.5 378.5 378.4 378.4 378.3 378.3 378.2 + 378.2 378.1 378.1 378.0 378.0 377.9 377.9 377.8 377.7 377.7 377.6 377.6 377.5 377.4 377.4 377.3 377.2 377.2 + 377.1 377.0 376.9 376.9 376.8 376.7 376.6 376.6 376.5 376.4 376.3 376.2 376.2 376.1 376.0 375.9 375.8 375.7 + 375.6 375.5 375.5 375.4 375.3 375.2 375.1 375.0 374.9 374.8 374.7 374.6 374.5 374.4 374.3 374.2 372.7 371.2 ]plong + 358.7 202.5 358.3 202.5 357.9 202.5 357.7 202.7 4 pls + s[ 358.7 359.1 359.5 359.9 360.3 360.7 361.1 361.5 361.9 362.3 ][ 202.5 202.5 202.5 202.5 202.5 202.5 202.5 202.5 + 202.5 202.5 ]plong + s[ 362.3 362.5 362.7 363.1 363.6 364.0 364.4 364.8 365.2 365.6 366.0 ][ 202.5 202.3 202.1 202.1 202.1 202.1 202.1 + 202.1 202.1 202.1 202.1 ]plong + s[ 366.0 366.4 366.8 367.2 367.6 368.0 368.4 368.8 369.0 369.2 369.6 ][ 202.1 202.1 202.1 202.1 202.1 202.1 202.1 + 202.1 201.9 201.7 201.7 ]plong + s[ 369.6 370.0 370.4 370.8 371.2 371.6 372.0 372.4 372.8 373.2 ][ 201.7 201.7 201.7 201.7 201.7 201.7 201.7 201.7 + 201.7 201.7 ]plong + s[ 373.2 373.6 374.0 374.4 374.8 375.2 375.6 376.0 376.4 376.8 ][ 201.7 201.7 201.7 201.7 201.7 201.7 201.7 201.7 + 201.7 201.7 ]plong + s[ 376.8 377.2 377.6 378.0 378.4 378.6 378.8 379.2 379.6 380.0 380.4 ][ 201.7 201.7 201.7 201.7 201.7 201.5 201.3 + 201.3 201.3 201.3 201.3 ]plong + s[ 380.4 380.8 381.2 381.7 382.1 382.5 382.9 383.3 383.7 384.1 ][ 201.3 201.3 201.3 201.3 201.3 201.3 201.3 201.3 + 201.3 201.3 ]plong + s[ 384.1 384.5 384.9 385.3 385.7 386.1 386.5 386.9 387.3 387.7 ][ 201.3 201.3 201.3 201.3 201.3 201.3 201.3 201.3 + 201.3 201.3 ]plong + s[ 387.7 388.1 388.5 388.9 389.3 389.7 390.1 390.5 390.9 391.3 ][ 201.3 201.3 201.3 201.3 201.3 201.3 201.3 201.3 + 201.3 201.3 ]plong + s[ 391.3 391.7 392.1 392.5 392.9 393.3 393.7 394.1 394.5 394.9 ][ 201.3 201.3 201.3 201.3 201.3 201.3 201.3 201.3 + 201.3 201.3 ]plong + s[ 394.9 395.3 395.7 396.1 396.5 396.9 397.3 397.7 398.1 398.5 ][ 201.3 201.3 201.3 201.3 201.3 201.3 201.3 201.3 + 201.3 201.3 ]plong + s[ 398.5 398.9 399.1 399.3 399.7 400.2 400.6 401.0 401.4 401.8 402.2 ][ 201.3 201.3 201.5 201.7 201.7 201.7 201.7 + 201.7 201.7 201.7 201.7 ]plong + s[ 402.2 402.6 403.0 403.4 403.8 404.2 404.6 405.0 405.4 405.8 ][ 201.7 201.7 201.7 201.7 201.7 201.7 201.7 201.7 + 201.7 201.7 ]plong + s[ 405.8 406.2 406.6 407.0 407.4 407.8 408.2 408.6 408.8 409.0 409.4 ][ 201.7 201.7 201.7 201.7 201.7 201.7 201.7 + 201.7 201.9 202.1 202.1 ]plong + s[ 409.4 409.8 410.2 410.6 411.0 411.4 411.8 412.2 412.6 413.0 ][ 202.1 202.1 202.1 202.1 202.1 202.1 202.1 202.1 + 202.1 202.1 ]plong + s[ 413.0 413.4 413.8 414.2 414.6 415.0 415.2 415.4 415.8 416.2 416.6 ][ 202.1 202.1 202.1 202.1 202.1 202.1 202.3 + 202.5 202.5 202.5 202.5 ]plong + s[ 420.1 419.9 419.5 419.1 418.7 418.3 417.8 417.4 417.0 416.6 ][ 202.7 202.5 202.5 202.5 202.5 202.5 202.5 202.5 + 202.5 202.5 ]plong + 329.8 206.1 329.6 206.3 2 pls + s[ 329.8 330.2 330.6 331.0 331.4 331.8 332.0 332.2 332.6 333.0 333.4 ][ 206.1 206.1 206.1 206.1 206.1 206.1 205.9 + 205.7 205.7 205.7 205.7 ]plong + s[ 333.4 333.8 334.2 334.4 334.6 335.0 335.4 335.8 336.2 336.6 337.0 ][ 205.7 205.7 205.7 205.5 205.3 205.3 205.3 + 205.3 205.3 205.3 205.3 ]plong + s[ 337.0 337.2 337.4 337.8 338.2 338.6 339.0 339.4 339.6 339.8 340.2 340.6 ][ 205.3 205.1 204.9 204.9 204.9 204.9 + 204.9 204.9 204.7 204.5 204.5 204.5 ]plong + s[ 340.6 341.0 341.4 341.8 342.2 342.6 342.8 343.0 343.4 343.8 344.2 ][ 204.5 204.5 204.5 204.5 204.5 204.5 204.3 + 204.1 204.1 204.1 204.1 ]plong + s[ 344.2 344.6 345.0 345.5 345.9 346.1 346.3 346.7 347.1 347.5 347.9 ][ 204.1 204.1 204.1 204.1 204.1 203.9 203.7 + 203.7 203.7 203.7 203.7 ]plong + s[ 347.9 348.3 348.7 349.1 349.3 349.5 349.9 350.3 350.7 351.1 351.5 ][ 203.7 203.7 203.7 203.7 203.5 203.3 203.3 + 203.3 203.3 203.3 203.3 ]plong + s[ 351.5 351.9 352.3 352.7 353.1 353.3 353.5 353.9 354.3 354.7 355.1 ][ 203.3 203.3 203.3 203.3 203.3 203.1 202.9 + 202.9 202.9 202.9 202.9 ]plong + s[ 355.1 355.5 355.9 356.3 356.7 357.1 357.5 357.7 ][ 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.7 ]plong + 420.1 202.7 420.3 202.9 2 pls + s[ 420.3 420.7 421.1 421.5 421.9 422.3 422.7 423.1 423.5 423.9 ][ 202.9 202.9 202.9 202.9 202.9 202.9 202.9 202.9 + 202.9 202.9 ]plong + s[ 423.9 424.3 424.5 424.7 425.1 425.5 425.9 426.3 426.7 427.1 427.5 ][ 202.9 202.9 203.1 203.3 203.3 203.3 203.3 + 203.3 203.3 203.3 203.3 ]plong + s[ 427.5 427.9 428.1 428.3 428.7 429.1 429.5 429.9 430.3 430.7 431.1 ][ 203.3 203.3 203.5 203.7 203.7 203.7 203.7 + 203.7 203.7 203.7 203.7 ]plong + s[ 431.1 431.5 431.7 431.9 432.3 432.7 433.1 433.5 433.9 434.3 434.7 ][ 203.7 203.7 203.9 204.1 204.1 204.1 204.1 + 204.1 204.1 204.1 204.1 ]plong + s[ 434.7 434.9 435.1 435.5 435.9 436.3 436.8 437.2 437.6 437.8 438.0 438.4 ][ 204.1 204.3 204.5 204.5 204.5 204.5 + 204.5 204.5 204.5 204.7 204.9 204.9 ]plong + s[ 438.4 438.8 439.2 439.6 440.0 440.4 440.6 440.8 441.2 441.6 442.0 ][ 204.9 204.9 204.9 204.9 204.9 204.9 205.1 + 205.3 205.3 205.3 205.3 ]plong + s[ 442.0 442.4 442.8 443.2 443.4 443.6 444.0 444.4 444.8 445.2 445.6 ][ 205.3 205.3 205.3 205.3 205.5 205.7 205.7 + 205.7 205.7 205.7 205.7 ]plong + s[ 448.2 448.0 447.6 447.2 446.8 446.4 446.0 445.8 445.6 ][ 206.3 206.1 206.1 206.1 206.1 206.1 206.1 205.9 205.7 +]plong + 311.7 209.7 311.5 210.0 2 pls + s[ 311.7 312.1 312.5 312.9 313.3 313.5 313.7 314.1 314.5 314.9 315.1 315.3 ][ 209.7 209.7 209.7 209.7 209.7 209.5 + 209.3 209.3 209.3 209.3 209.1 208.9 ]plong + s[ 315.3 315.7 316.1 316.5 316.9 317.1 317.3 317.7 318.1 318.5 318.9 ][ 208.9 208.9 208.9 208.9 208.9 208.7 208.5 + 208.5 208.5 208.5 208.5 ]plong + s[ 318.9 319.1 319.3 319.7 320.1 320.5 320.7 320.9 321.3 321.7 322.1 322.5 ][ 208.5 208.3 208.1 208.1 208.1 208.1 + 207.9 207.7 207.7 207.7 207.7 207.7 ]plong + s[ 322.5 322.7 322.9 323.3 323.7 324.1 324.5 324.9 325.1 325.3 325.7 326.1 ][ 207.7 207.5 207.3 207.3 207.3 207.3 + 207.3 207.3 207.1 206.9 206.9 206.9 ]plong + s[ 326.1 326.5 327.0 327.2 327.4 327.8 328.2 328.6 329.0 329.4 329.6 ][ 206.9 206.9 206.9 206.7 206.5 206.5 206.5 + 206.5 206.5 206.5 206.3 ]plong + 448.2 206.3 448.4 206.5 448.8 206.5 449.2 206.5 4 pls + s[ 449.2 449.6 450.0 450.4 450.6 450.8 451.2 451.6 452.0 452.4 452.6 452.8 ][ 206.5 206.5 206.5 206.5 206.7 206.9 + 206.9 206.9 206.9 206.9 207.1 207.3 ]plong + s[ 452.8 453.2 453.6 454.0 454.4 454.7 454.9 455.3 455.7 456.1 456.5 ][ 207.3 207.3 207.3 207.3 207.3 207.5 207.7 + 207.7 207.7 207.7 207.7 ]plong + s[ 456.5 456.7 456.9 457.3 457.7 458.1 458.5 458.7 458.9 459.3 459.7 460.1 ][ 207.7 207.9 208.1 208.1 208.1 208.1 + 208.1 208.3 208.5 208.5 208.5 208.5 ]plong + s[ 460.1 460.5 460.7 460.9 461.3 461.7 462.1 462.5 462.7 462.9 463.3 463.7 ][ 208.5 208.5 208.7 208.9 208.9 208.9 + 208.9 208.9 209.1 209.3 209.3 209.3 ]plong + s[ 465.9 465.7 465.3 464.9 464.5 464.3 464.1 463.7 ][ 210.0 209.7 209.7 209.7 209.7 209.5 209.3 209.3 ]plong + s[ 297.8 298.0 298.4 298.8 299.0 299.2 299.6 300.0 300.4 300.6 300.8 ][ 213.6 213.4 213.4 213.4 213.2 213.0 213.0 + 213.0 213.0 212.8 212.6 ]plong + s[ 300.8 301.2 301.6 302.0 302.2 302.4 302.8 303.2 303.4 303.6 304.0 304.4 ][ 212.6 212.6 212.6 212.6 212.4 212.2 + 212.2 212.2 212.0 211.8 211.8 211.8 ]plong + s[ 304.4 304.8 305.0 305.2 305.6 306.0 306.4 306.6 306.8 307.2 307.6 308.0 ][ 211.8 211.8 211.6 211.4 211.4 211.4 + 211.4 211.2 211.0 211.0 211.0 211.0 ]plong + s[ 308.0 308.2 308.4 308.9 309.3 309.7 309.9 310.1 310.5 310.9 311.3 311.5 ][ 211.0 210.8 210.6 210.6 210.6 210.6 + 210.4 210.2 210.2 210.2 210.2 210.0 ]plong + 465.9 210.0 466.1 210.2 466.5 210.2 466.9 210.2 467.3 210.2 5 pls + s[ 467.3 467.7 467.9 468.1 468.5 468.9 469.3 469.5 469.7 470.1 470.5 470.9 ][ 210.2 210.2 210.4 210.6 210.6 210.6 + 210.6 210.8 211.0 211.0 211.0 211.0 ]plong + s[ 470.9 471.1 471.3 471.7 472.1 472.5 472.7 473.0 473.4 473.8 474.0 474.2 474.6 ][ 211.0 211.2 211.4 211.4 211.4 + 211.4 211.6 211.8 211.8 211.8 212.0 212.2 212.2 ]plong + s[ 474.6 475.0 475.4 475.6 475.8 476.2 476.6 477.0 477.2 477.4 477.8 478.2 ][ 212.2 212.2 212.2 212.4 212.6 212.6 + 212.6 212.6 212.8 213.0 213.0 213.0 ]plong + s[ 480.0 479.8 479.4 479.0 478.6 478.4 478.2 ][ 213.6 213.4 213.4 213.4 213.4 213.2 213.0 ]plong + 286.3 217.0 286.1 217.2 2 pls + s[ 286.3 286.7 287.1 287.3 287.5 287.9 288.3 288.5 288.7 289.1 289.5 289.7 289.9 ][ 217.0 217.0 217.0 216.8 216.6 + 216.6 216.6 216.4 216.2 216.2 216.2 216.0 215.8 ]plong + s[ 289.9 290.3 290.8 291.0 291.2 291.6 292.0 292.4 292.6 292.8 293.2 293.6 ][ 215.8 215.8 215.8 215.6 215.4 215.4 + 215.4 215.4 215.2 215.0 215.0 215.0 ]plong + s[ 293.6 293.8 294.0 294.4 294.8 295.0 295.2 295.6 296.0 296.4 296.6 296.8 297.2 ][ 215.0 214.8 214.6 214.6 214.6 + 214.4 214.2 214.2 214.2 214.2 214.0 213.8 213.8 ]plong + 297.8 213.6 297.6 213.8 297.2 213.8 3 pls + s[ 481.8 481.4 481.2 481.0 480.6 480.2 480.0 ][ 214.2 214.2 214.0 213.8 213.8 213.8 213.6 ]plong + s[ 481.8 482.2 482.6 482.8 483.0 483.4 483.8 484.0 484.2 484.6 485.0 485.2 485.4 ][ 214.2 214.2 214.2 214.4 214.6 + 214.6 214.6 214.8 215.0 215.0 215.0 215.2 215.4 ]plong + s[ 485.4 485.8 486.2 486.4 486.6 487.0 487.4 487.8 488.0 488.2 488.6 489.0 ][ 215.4 215.4 215.4 215.6 215.8 215.8 + 215.8 215.8 216.0 216.2 216.2 216.2 ]plong + s[ 491.7 491.5 491.0 490.6 490.4 490.2 489.8 489.4 489.2 489.0 ][ 217.2 217.0 217.0 217.0 216.8 216.6 216.6 216.6 + 216.4 216.2 ]plong + s[ 276.5 276.7 277.1 277.3 277.5 277.9 278.3 278.5 278.7 279.1 ][ 220.8 220.6 220.6 220.4 220.2 220.2 220.2 220.0 + 219.8 219.8 ]plong + s[ 279.1 279.5 279.7 279.9 280.3 280.5 280.7 281.1 281.5 281.7 281.9 282.3 282.7 ][ 219.8 219.8 219.6 219.4 219.4 + 219.2 219.0 219.0 219.0 218.8 218.6 218.6 218.6 ]plong + s[ 282.7 282.9 283.1 283.5 283.9 284.1 284.3 284.7 284.9 285.1 285.5 285.9 286.1 ][ 218.6 218.4 218.2 218.2 218.2 + 218.0 217.8 217.8 217.6 217.4 217.4 217.4 217.2 ]plong + 491.7 217.2 491.9 217.4 492.3 217.4 492.5 217.6 492.7 217.8 5 pls + s[ 492.7 493.1 493.5 493.7 493.9 494.3 494.7 494.9 495.1 495.5 495.9 496.1 496.3 ][ 217.8 217.8 217.8 218.0 218.2 + 218.2 218.2 218.4 218.6 218.6 218.6 218.8 219.0 ]plong + s[ 496.3 496.7 497.1 497.3 497.5 497.9 498.1 498.3 498.7 499.1 499.3 499.5 499.9 ][ 219.0 219.0 219.0 219.2 219.4 + 219.4 219.6 219.8 219.8 219.8 220.0 220.2 220.2 ]plong + s[ 501.3 501.1 500.7 500.5 500.3 499.9 ][ 220.8 220.6 220.6 220.4 220.2 220.2 ]plong + 268.2 224.2 267.8 224.2 267.6 224.4 3 pls + s[ 268.2 268.6 268.8 269.0 269.4 269.6 269.8 270.2 270.4 270.6 271.0 271.2 271.4 271.8 ][ 224.2 224.2 224.0 223.8 + 223.8 223.6 223.4 223.4 223.2 223.0 223.0 222.8 222.6 222.6 ]plong + s[ 271.8 272.3 272.5 272.7 273.1 273.3 273.5 273.9 274.3 274.5 274.7 275.1 275.3 275.5 ][ 222.6 222.6 222.4 222.2 + 222.2 222.0 221.8 221.8 221.8 221.6 221.4 221.4 221.2 221.0 ]plong + 276.5 220.8 276.3 221.0 275.9 221.0 275.5 221.0 4 pls + s[ 503.5 503.3 503.1 502.7 502.5 502.3 501.9 501.5 501.3 ][ 221.8 221.6 221.4 221.4 221.2 221.0 221.0 221.0 220.8 +]plong + s[ 503.5 503.9 504.3 504.5 504.7 505.1 505.3 505.5 505.9 506.1 506.3 506.7 507.1 ][ 221.8 221.8 221.8 222.0 222.2 + 222.2 222.4 222.6 222.6 222.8 223.0 223.0 223.0 ]plong + s[ 510.2 510.0 509.6 509.1 508.9 508.7 508.3 508.1 507.9 507.5 507.3 507.1 ][ 224.4 224.2 224.2 224.2 224.0 223.8 + 223.8 223.6 223.4 223.4 223.2 223.0 ]plong + 261.0 227.4 260.8 227.6 260.6 227.8 260.2 227.8 260.0 228.1 5 pls + s[ 261.0 261.4 261.6 261.8 262.2 262.4 262.6 263.0 263.2 263.4 263.8 264.2 264.4 264.6 ][ 227.4 227.4 227.2 227.0 + 227.0 226.8 226.6 226.6 226.4 226.2 226.2 226.2 226.0 225.8 ]plong + s[ 264.6 265.0 265.2 265.4 265.8 266.0 266.2 266.6 266.8 267.0 267.4 267.6 ][ 225.8 225.8 225.6 225.4 225.4 225.2 + 225.0 225.0 224.8 224.6 224.6 224.4 ]plong + 510.2 224.4 510.4 224.6 510.8 224.6 3 pls + s[ 510.8 511.0 511.2 511.6 511.8 512.0 512.4 512.6 512.8 513.2 513.4 513.6 514.0 514.2 514.4 ][ 224.6 224.8 225.0 + 225.0 225.2 225.4 225.4 225.6 225.8 225.8 226.0 226.2 226.2 226.4 226.6 ]plong + s[ 517.8 517.6 517.2 517.0 516.8 516.4 516.2 516.0 515.6 515.4 515.2 514.8 514.4 ][ 228.1 227.8 227.8 227.6 227.4 + 227.4 227.2 227.0 227.0 226.8 226.6 226.6 226.6 ]plong + 253.7 231.5 253.3 231.5 253.1 231.7 3 pls + s[ 253.7 254.0 254.2 254.6 254.8 255.0 255.4 255.6 255.8 256.2 256.4 256.6 256.8 257.0 257.4 ][ 231.5 231.3 231.1 + 231.1 230.9 230.7 230.7 230.5 230.3 230.3 230.1 229.9 229.7 229.5 229.5 ]plong + s[ 257.4 257.6 257.8 258.2 258.4 258.6 259.0 259.2 259.4 259.8 260.0 ][ 229.5 229.3 229.1 229.1 228.9 228.7 228.7 + 228.5 228.3 228.3 228.1 ]plong + 517.8 228.1 518.0 228.3 2 pls + s[ 518.0 518.4 518.6 518.8 519.2 519.4 519.6 520.0 520.2 520.4 520.6 520.8 521.2 521.4 521.6 ][ 228.3 228.3 228.5 + 228.7 228.7 228.9 229.1 229.1 229.3 229.5 229.7 229.9 229.9 230.1 230.3 ]plong + s[ 524.6 524.4 524.0 523.8 523.6 523.2 523.0 522.8 522.4 522.2 522.0 521.6 ][ 231.7 231.5 231.5 231.3 231.1 231.1 + 230.9 230.7 230.7 230.5 230.3 230.3 ]plong + s[ 247.1 247.3 247.5 247.7 248.1 248.3 248.5 248.9 249.1 249.3 249.5 249.7 250.1 ][ 235.3 235.1 234.9 234.7 234.7 + 234.5 234.3 234.3 234.1 233.9 233.7 233.5 233.5 ]plong + s[ 250.1 250.3 250.5 250.9 251.1 251.3 251.7 251.9 252.1 252.3 252.5 252.9 253.1 ][ 233.5 233.3 233.1 233.1 232.9 + 232.7 232.7 232.5 232.3 232.1 231.9 231.9 231.7 ]plong + 524.6 231.7 524.8 231.9 525.0 232.1 525.2 232.3 4 pls + s[ 525.2 525.6 525.8 526.0 526.4 526.6 526.8 527.2 527.4 527.7 527.9 528.1 528.5 528.7 528.9 ][ 232.3 232.3 232.5 + 232.7 232.7 232.9 233.1 233.1 233.3 233.5 233.7 233.9 233.9 234.1 234.3 ]plong + s[ 530.7 530.5 530.1 529.9 529.7 529.5 529.3 528.9 ][ 235.3 235.1 235.1 234.9 234.7 234.5 234.3 234.3 ]plong + s[ 241.5 241.7 242.1 242.3 242.5 242.7 242.9 ][ 238.9 238.7 238.7 238.5 238.3 238.1 237.9 ]plong + s[ 242.9 243.3 243.5 243.7 243.9 244.1 244.5 244.7 244.9 245.1 245.3 245.7 245.9 246.1 246.3 246.5 ][ 237.9 237.9 + 237.7 237.5 237.3 237.1 237.1 236.9 236.7 236.5 236.3 236.3 236.1 235.9 235.7 235.5 ]plong + 247.1 235.3 246.9 235.5 246.5 235.5 3 pls + s[ 532.5 532.1 531.9 531.7 531.5 531.3 530.9 530.7 ][ 236.3 236.3 236.1 235.9 235.7 235.5 235.5 235.3 ]plong + s[ 532.5 532.7 532.9 533.1 533.3 533.7 533.9 534.1 534.3 534.5 534.9 535.1 535.3 535.5 535.7 536.1 ][ 236.3 236.5 + 236.7 236.9 237.1 237.1 237.3 237.5 237.7 237.9 237.9 238.1 238.3 238.5 238.7 238.7 ]plong + 536.1 238.7 536.3 238.9 2 pls + s[ 236.7 236.9 237.1 237.3 237.5 237.7 238.1 238.3 238.5 238.7 238.9 239.1 239.3 ][ 242.5 242.3 242.1 241.9 241.7 + 241.5 241.5 241.3 241.1 240.9 240.7 240.5 240.3 ]plong + s[ 239.3 239.7 239.9 240.1 240.3 240.5 240.9 241.1 241.3 241.5 ][ 240.3 240.3 240.1 239.9 239.7 239.5 239.5 239.3 + 239.1 238.9 ]plong + s[ 536.3 536.5 536.7 536.9 537.3 537.5 537.7 537.9 538.1 538.3 538.5 538.9 539.1 539.3 539.5 539.7 ][ 238.9 239.1 + 239.3 239.5 239.5 239.7 239.9 240.1 240.3 240.5 240.7 240.7 240.9 241.1 241.3 241.5 ]plong + s[ 541.1 540.9 540.7 540.5 540.1 539.9 539.7 ][ 242.5 242.3 242.1 241.9 241.9 241.7 241.5 ]plong + s[ 232.2 232.4 232.6 232.8 233.0 233.2 233.4 233.6 233.8 234.0 234.4 234.6 234.8 235.0 235.2 235.4 235.6 ][ 246.1 + 245.9 245.7 245.5 245.3 245.1 244.9 244.7 244.5 244.3 244.3 244.1 243.9 243.7 243.5 243.3 243.1 ]plong + 236.7 242.5 236.5 242.7 236.1 242.7 235.9 242.9 235.6 243.1 5 pls + s[ 543.3 543.1 542.9 542.7 542.5 542.3 542.1 541.7 541.5 541.3 541.1 ][ 244.3 244.1 243.9 243.7 243.5 243.3 243.1 + 243.1 242.9 242.7 242.5 ]plong + s[ 545.5 545.3 545.1 544.9 544.7 544.5 544.3 544.1 543.7 543.5 543.3 ][ 246.1 245.9 245.7 245.5 245.3 245.1 244.9 + 244.7 244.7 244.5 244.3 ]plong + 228.4 249.6 228.0 249.6 227.8 249.8 3 pls + s[ 228.4 228.6 228.8 229.0 229.2 229.4 229.6 229.8 230.0 230.2 230.4 230.6 230.8 231.0 231.2 231.4 231.6 232.0 ][ + 249.6 249.4 249.2 249.0 248.8 248.6 248.4 248.2 248.0 247.8 247.6 247.4 247.2 247.0 246.8 246.6 246.4 246.4 ]plong + 232.0 246.4 232.2 246.1 2 pls + s[ 547.0 546.8 546.6 546.2 546.0 545.7 545.5 ][ 247.2 247.0 246.8 246.8 246.6 246.4 246.1 ]plong + s[ 549.6 549.4 549.2 549.0 548.8 548.6 548.4 548.2 548.0 547.8 547.6 547.4 547.2 547.0 ][ 249.8 249.6 249.4 249.2 + 249.0 248.8 248.6 248.4 248.2 248.0 247.8 247.6 247.4 247.2 ]plong + 224.8 252.8 224.6 253.0 224.4 253.2 224.2 253.4 4 pls + s[ 224.8 225.0 225.2 225.4 225.6 225.8 226.0 226.2 226.4 226.6 226.8 227.0 227.2 227.4 227.6 227.8 ][ 252.8 252.6 + 252.4 252.2 252.0 251.8 251.6 251.4 251.2 251.0 250.8 250.6 250.4 250.2 250.0 249.8 ]plong + s[ 550.6 550.4 550.2 550.0 549.8 549.6 ][ 250.8 250.6 250.4 250.2 250.0 249.8 ]plong + s[ 553.2 553.0 552.8 552.8 552.6 552.4 552.2 552.0 551.8 551.6 551.4 551.2 551.0 550.6 ][ 253.4 253.2 253.0 252.6 + 252.4 252.2 252.0 251.8 251.6 251.4 251.2 251.0 250.8 250.8 ]plong + 221.2 256.8 221.0 257.0 2 pls + s[ 221.2 221.4 221.6 221.8 222.0 222.2 222.4 222.6 222.8 223.0 223.2 223.4 223.6 223.8 223.8 224.0 224.2 ][ 256.8 + 256.6 256.4 256.2 256.0 255.8 255.6 255.4 255.2 255.0 254.8 254.6 254.4 254.2 253.8 253.6 253.4 ]plong + s[ 554.2 554.0 553.8 553.6 553.4 553.2 ][ 254.4 254.2 254.0 253.8 253.6 253.4 ]plong + s[ 556.4 556.4 556.2 556.0 555.8 555.6 555.4 555.2 555.0 554.8 554.6 554.4 554.2 ][ 257.0 256.6 256.4 256.2 256.0 + 255.8 255.6 255.4 255.2 255.0 254.8 254.6 254.4 ]plong + s[ 218.2 218.4 218.6 218.8 219.0 219.0 219.2 219.4 219.6 219.8 220.0 220.2 220.4 220.6 220.8 221.0 221.0 ][ 260.6 + 260.4 260.2 260.0 259.8 259.4 259.2 259.0 258.8 258.6 258.4 258.2 258.0 257.8 257.6 257.4 257.0 ]plong + s[ 557.8 557.6 557.4 557.2 557.0 556.8 556.6 556.4 ][ 258.4 258.2 258.0 257.8 257.6 257.4 257.2 257.0 ]plong + s[ 559.6 559.4 559.2 559.0 558.8 558.6 558.4 558.4 558.2 558.0 557.8 ][ 260.6 260.4 260.2 260.0 259.8 259.6 259.4 + 259.0 258.8 258.6 258.4 ]plong + s[ 215.7 215.9 216.1 216.1 216.3 216.5 216.7 216.9 216.9 217.1 217.3 217.6 ][ 264.2 264.0 263.8 263.4 263.2 263.0 + 262.8 262.6 262.2 262.0 261.8 261.6 ]plong + 218.2 260.6 218.0 260.8 217.8 261.0 217.8 261.4 217.6 261.6 5 pls + s[ 561.4 561.2 561.0 560.8 560.8 560.6 560.4 560.2 560.0 559.8 559.6 559.6 ][ 263.2 263.0 262.8 262.6 262.2 262.0 + 261.8 261.6 261.4 261.2 261.0 260.6 ]plong + 561.4 263.2 561.6 263.4 561.6 263.8 561.8 264.0 562.0 264.2 5 pls + 213.9 267.3 213.7 267.5 213.5 267.7 213.3 267.9 4 pls + s[ 213.9 214.1 214.1 214.3 214.5 214.5 214.7 214.9 215.1 215.3 215.3 215.5 215.7 ][ 267.3 267.1 266.7 266.5 266.3 + 265.9 265.7 265.5 265.3 265.1 264.7 264.4 264.2 ]plong + s[ 564.0 564.0 563.8 563.6 563.6 563.4 563.2 563.0 562.8 562.8 562.6 562.4 562.4 562.2 562.0 ][ 267.9 267.5 267.3 + 267.1 266.7 266.5 266.3 266.1 265.9 265.5 265.3 265.1 264.7 264.4 264.2 ]plong + s[ 211.7 211.7 211.9 212.1 212.1 212.3 212.5 212.5 212.7 212.9 212.9 213.1 213.3 213.3 ][ 271.5 271.1 270.9 270.7 + 270.3 270.1 269.9 269.5 269.3 269.1 268.7 268.5 268.3 267.9 ]plong + s[ 565.1 564.9 564.9 564.7 564.5 564.5 564.3 564.0 ][ 269.7 269.5 269.1 268.9 268.7 268.3 268.1 267.9 ]plong + s[ 566.1 565.9 565.7 565.7 565.7 565.5 565.3 565.1 ][ 271.5 271.3 271.1 270.7 270.3 270.1 269.9 269.7 ]plong + 210.3 274.5 210.1 274.7 210.1 275.1 3 pls + s[ 210.3 210.5 210.5 210.7 210.9 210.9 210.9 211.1 211.3 211.3 211.5 211.7 ][ 274.5 274.3 273.9 273.7 273.5 273.1 + 272.7 272.5 272.3 271.9 271.7 271.5 ]plong + s[ 567.7 567.5 567.3 567.3 567.3 567.1 566.9 566.9 566.7 566.5 566.5 566.3 566.1 566.1 ][ 275.1 274.9 274.7 274.3 + 273.9 273.7 273.5 273.1 272.9 272.7 272.3 272.1 271.9 271.5 ]plong + s[ 208.9 208.9 209.1 209.3 209.3 209.3 209.5 209.7 209.7 209.7 209.9 210.1 210.1 ][ 278.7 278.3 278.1 277.9 277.5 + 277.1 276.9 276.7 276.3 275.9 275.7 275.5 275.1 ]plong + s[ 568.7 568.5 568.5 568.5 568.3 568.1 568.1 568.1 567.9 567.7 567.7 ][ 278.1 277.9 277.5 277.1 276.9 276.7 276.3 + 275.9 275.7 275.5 275.1 ]plong + 568.7 278.1 568.9 278.3 568.9 278.7 3 pls + s[ 208.1 208.1 208.1 208.1 208.1 208.3 208.5 208.5 208.5 208.7 208.9 208.9 ][ 282.3 281.9 281.5 281.1 280.7 280.5 + 280.3 279.9 279.5 279.3 279.1 278.7 ]plong + s[ 568.9 568.9 569.1 569.3 569.3 569.3 569.3 569.5 569.7 569.7 569.7 569.7 ][ 278.7 279.1 279.3 279.5 279.9 280.3 + 280.7 280.9 281.1 281.5 281.9 282.3 ]plong + s[ 207.3 207.3 207.3 207.3 207.5 207.7 207.7 207.7 207.7 207.7 207.9 208.1 ][ 286.0 285.6 285.2 284.8 284.6 284.4 + 284.0 283.6 283.2 282.7 282.5 282.3 ]plong + s[ 570.5 570.5 570.5 570.3 570.1 570.1 570.1 570.1 570.1 570.1 569.9 569.7 ][ 286.0 285.6 285.2 285.0 284.8 284.4 + 284.0 283.6 283.2 282.7 282.5 282.3 ]plong + s[ 206.9 206.9 206.9 206.9 206.9 206.9 207.1 207.3 207.3 207.3 207.3 ][ 289.6 289.2 288.8 288.4 288.0 287.6 287.4 + 287.2 286.8 286.4 286.0 ]plong + s[ 570.9 570.9 570.9 570.9 570.9 570.7 570.5 570.5 570.5 570.5 570.5 ][ 289.6 289.2 288.8 288.4 288.0 287.8 287.6 + 287.2 286.8 286.4 286.0 ]plong + s[ 206.9 206.9 206.9 206.9 206.9 206.9 206.9 206.9 206.9 206.9 ][ 289.6 290.0 290.4 290.8 291.2 291.6 292.0 292.4 + 292.8 293.2 ]plong + s[ 570.9 570.9 570.9 570.9 570.9 570.9 570.9 570.9 570.9 570.9 ][ 293.2 292.8 292.4 292.0 291.6 291.2 290.8 290.4 + 290.0 289.6 ]plong + s[ 206.9 206.9 206.9 206.9 206.9 206.9 206.9 206.9 206.9 206.9 ][ 293.2 293.6 294.0 294.4 294.8 295.2 295.6 296.0 + 296.4 296.8 ]plong + s[ 570.9 570.9 570.9 570.9 570.9 570.9 570.9 570.9 570.9 570.9 ][ 296.8 296.4 296.0 295.6 295.2 294.8 294.4 294.0 + 293.6 293.2 ]plong + s[ 206.9 206.9 207.1 207.3 207.3 207.3 207.3 207.3 207.3 207.3 207.5 207.7 ][ 296.8 297.2 297.4 297.6 298.0 298.4 + 298.8 299.2 299.6 300.0 300.2 300.4 ]plong + s[ 570.1 570.1 570.3 570.5 570.5 570.5 570.5 570.5 570.5 570.5 570.7 570.9 ][ 300.4 300.0 299.8 299.6 299.2 298.8 + 298.4 298.0 297.6 297.2 297.0 296.8 ]plong + s[ 208.5 208.3 208.1 208.1 208.1 208.1 207.9 207.7 207.7 207.7 207.7 207.7 ][ 304.1 303.9 303.7 303.3 302.9 302.5 + 302.3 302.1 301.7 301.3 300.8 300.4 ]plong + s[ 569.3 569.5 569.7 569.7 569.7 569.7 569.7 569.9 570.1 570.1 570.1 570.1 ][ 304.1 303.9 303.7 303.3 302.9 302.5 + 302.1 301.9 301.7 301.3 300.8 300.4 ]plong + s[ 209.3 209.3 209.3 209.1 208.9 208.9 208.9 208.7 208.5 208.5 208.5 208.5 ][ 307.7 307.3 306.9 306.7 306.5 306.1 + 305.7 305.5 305.3 304.9 304.5 304.1 ]plong + 568.7 306.7 568.5 306.9 568.5 307.3 568.5 307.7 4 pls + s[ 568.7 568.9 568.9 568.9 568.9 569.1 569.3 569.3 569.3 ][ 306.7 306.5 306.1 305.7 305.3 305.1 304.9 304.5 304.1 +]plong + s[ 210.3 210.1 210.1 210.1 209.9 209.7 209.7 209.7 209.5 209.3 ][ 310.3 310.1 309.7 309.3 309.1 308.9 308.5 308.1 + 307.9 307.7 ]plong + 210.3 310.3 210.5 310.5 210.5 310.9 210.7 311.1 210.9 311.3 5 pls + s[ 566.9 567.1 567.3 567.3 567.3 567.5 567.7 567.7 567.9 568.1 568.1 568.1 568.3 568.5 ][ 311.3 311.1 310.9 310.5 + 310.1 309.9 309.7 309.3 309.1 308.9 308.5 308.1 307.9 307.7 ]plong + s[ 212.5 212.3 212.1 212.1 211.9 211.7 211.7 211.5 211.3 211.3 211.3 211.1 210.9 210.9 ][ 314.9 314.7 314.5 314.1 + 313.9 313.7 313.3 313.1 312.9 312.5 312.1 311.9 311.7 311.3 ]plong + s[ 565.3 565.3 565.5 565.7 565.7 565.9 566.1 566.1 566.3 566.5 566.5 566.7 566.9 566.9 ][ 314.9 314.5 314.3 314.1 + 313.7 313.5 313.3 312.9 312.7 312.5 312.1 311.9 311.7 311.3 ]plong + s[ 213.9 213.7 213.7 213.5 213.3 213.1 212.9 212.9 212.7 212.5 212.5 ][ 317.5 317.3 316.9 316.7 316.5 316.3 316.1 + 315.7 315.5 315.3 314.9 ]plong + 213.9 317.5 214.1 317.7 214.1 318.1 214.3 318.3 214.5 318.5 5 pls + s[ 563.2 563.4 563.6 563.6 563.8 564.0 564.0 564.3 564.5 564.5 564.7 564.9 564.9 565.1 ][ 318.5 318.3 318.1 317.7 + 317.5 317.3 316.9 316.7 316.5 316.1 315.9 315.7 315.3 315.1 ]plong + 565.1 315.1 565.3 314.9 2 pls + s[ 216.9 216.7 216.5 216.3 216.1 216.1 215.9 215.7 215.5 215.3 215.3 215.1 214.9 214.7 214.5 214.5 ][ 322.2 322.0 + 321.8 321.6 321.4 321.0 320.8 320.6 320.4 320.2 319.8 319.6 319.4 319.1 318.9 318.5 ]plong + 561.4 321.6 561.2 321.8 561.0 322.0 560.8 322.2 4 pls + s[ 561.4 561.6 561.6 561.8 562.0 562.0 562.2 562.4 562.6 562.8 562.8 563.0 563.2 ][ 321.6 321.4 321.0 320.8 320.6 + 320.2 320.0 319.8 319.6 319.4 318.9 318.7 318.5 ]plong + 216.9 322.2 216.9 322.6 217.1 322.8 217.3 323.0 217.6 323.2 5 pls + s[ 219.4 219.4 219.2 219.0 218.8 218.6 218.4 218.2 218.2 218.0 217.8 217.6 ][ 325.8 325.4 325.2 325.0 324.8 324.6 + 324.4 324.2 323.8 323.6 323.4 323.2 ]plong + s[ 558.0 558.2 558.4 558.6 558.8 559.0 559.2 559.4 559.6 559.6 559.8 560.0 560.2 560.4 560.4 560.6 560.8 ][ 325.8 + 325.6 325.4 325.2 325.0 324.8 324.6 324.4 324.2 323.8 323.6 323.4 323.2 323.0 322.6 322.4 322.2 ]plong + s[ 221.2 221.0 221.0 220.8 220.6 220.4 220.2 220.0 219.8 219.6 219.4 ][ 328.0 327.8 327.4 327.2 327.0 326.8 326.6 + 326.4 326.2 326.0 325.8 ]plong + s[ 222.6 222.4 222.2 222.0 221.8 221.6 221.4 221.2 ][ 329.4 329.2 329.0 328.8 328.6 328.4 328.2 328.0 ]plong + s[ 555.2 555.4 555.6 555.8 556.0 556.2 556.4 556.4 556.6 556.8 557.0 557.2 557.4 557.6 557.8 ][ 329.4 329.2 329.0 + 328.8 328.6 328.4 328.2 327.8 327.6 327.4 327.2 327.0 326.8 326.6 326.4 ]plong + 557.8 326.4 558.0 326.2 558.0 325.8 3 pls + s[ 224.8 224.6 224.6 224.4 224.2 224.0 223.8 223.6 223.4 223.2 223.0 222.8 222.6 ][ 332.0 331.8 331.4 331.2 331.0 + 330.8 330.6 330.4 330.2 330.0 329.8 329.6 329.4 ]plong + s[ 225.8 225.6 225.4 225.2 225.0 224.8 ][ 333.0 332.8 332.6 332.4 332.2 332.0 ]plong + s[ 551.6 551.8 552.0 552.2 552.4 552.6 552.8 553.0 553.2 553.4 553.6 553.8 554.0 554.2 ][ 333.0 332.8 332.6 332.4 + 332.2 332.0 331.8 331.6 331.4 331.2 331.0 330.8 330.6 330.4 ]plong + s[ 554.2 554.4 554.6 554.8 555.0 555.2 ][ 330.4 330.2 330.0 329.8 329.6 329.4 ]plong + s[ 228.4 228.2 228.0 227.6 227.4 227.2 227.0 226.8 226.6 226.4 226.2 226.0 225.8 ][ 335.2 335.0 334.8 334.8 334.6 + 334.4 334.2 334.0 333.8 333.6 333.4 333.2 333.0 ]plong + s[ 229.8 229.6 229.4 229.2 229.0 228.8 228.6 228.4 ][ 336.6 336.4 336.2 336.0 335.8 335.6 335.4 335.2 ]plong + s[ 548.0 548.2 548.4 548.6 548.8 549.0 549.2 549.4 549.6 549.8 550.0 550.2 550.4 550.6 ][ 336.6 336.4 336.2 336.0 + 335.8 335.6 335.4 335.2 335.0 334.8 334.6 334.4 334.2 334.0 ]plong + s[ 550.6 550.8 551.0 551.2 551.4 551.6 ][ 334.0 333.8 333.6 333.4 333.2 333.0 ]plong + s[ 232.0 231.6 231.4 231.2 231.0 230.8 230.6 230.4 230.2 230.0 229.8 ][ 338.5 338.5 338.3 338.1 337.9 337.7 337.4 + 337.2 337.0 336.8 336.6 ]plong + s[ 233.8 233.6 233.4 233.2 233.0 232.8 232.6 232.4 232.2 232.0 ][ 340.3 340.1 339.9 339.7 339.5 339.3 339.1 338.9 + 338.7 338.5 ]plong + s[ 543.5 543.7 544.1 544.3 544.5 544.7 544.9 545.1 545.3 545.5 545.7 546.0 546.2 546.6 546.8 547.0 ][ 340.3 340.1 + 340.1 339.9 339.7 339.5 339.3 339.1 338.9 338.7 338.5 338.3 338.1 338.1 337.9 337.7 ]plong + s[ 547.0 547.2 547.4 547.6 547.8 548.0 ][ 337.7 337.4 337.2 337.0 336.8 336.6 ]plong + s[ 235.6 235.4 235.2 235.0 234.8 234.6 234.4 234.0 233.8 ][ 341.7 341.5 341.3 341.1 340.9 340.7 340.5 340.5 340.3 +]plong + s[ 238.7 238.5 238.3 238.1 237.7 237.5 237.3 237.1 236.9 236.7 236.5 236.3 236.1 235.6 ][ 343.9 343.7 343.5 343.3 + 343.3 343.1 342.9 342.7 342.5 342.3 342.1 341.9 341.7 341.7 ]plong + 539.7 343.3 539.5 343.5 539.3 343.7 539.1 343.9 4 pls + s[ 539.7 539.9 540.1 540.5 540.7 540.9 541.1 541.3 541.5 541.7 542.1 542.3 542.5 542.7 542.9 543.1 543.3 ][ 343.3 + 343.1 342.9 342.9 342.7 342.5 342.3 342.1 341.9 341.7 341.7 341.5 341.3 341.1 340.9 340.7 340.5 ]plong + 543.3 340.5 543.5 340.3 2 pls + 238.7 343.9 238.9 344.1 239.3 344.1 3 pls + s[ 239.3 239.5 239.7 239.9 240.1 240.3 240.5 240.9 241.1 241.3 241.5 241.7 242.1 242.3 242.5 242.7 242.9 ][ 344.1 + 344.3 344.5 344.7 344.9 345.1 345.3 345.3 345.5 345.7 345.9 346.1 346.1 346.3 346.5 346.7 346.9 ]plong + 242.9 346.9 243.3 346.9 243.5 347.1 243.7 347.3 243.9 347.5 5 pls + s[ 533.9 534.1 534.3 534.5 534.9 535.1 535.3 535.5 535.7 536.1 ][ 347.5 347.3 347.1 346.9 346.9 346.7 346.5 346.3 + 346.1 346.1 ]plong + s[ 536.1 536.3 536.5 536.7 536.9 537.1 537.3 537.7 537.9 538.1 538.3 538.5 538.9 539.1 ][ 346.1 345.9 345.7 345.5 + 345.3 345.1 344.9 344.9 344.7 344.5 344.3 344.1 344.1 343.9 ]plong + s[ 246.5 246.3 246.1 245.9 245.7 245.3 245.1 244.9 244.7 244.5 244.1 243.9 ][ 349.3 349.1 348.9 348.7 348.5 348.5 + 348.3 348.1 347.9 347.7 347.7 347.5 ]plong + s[ 249.9 249.7 249.3 249.1 248.9 248.5 248.3 248.1 247.9 247.7 247.3 247.1 246.9 246.5 ][ 351.1 350.9 350.9 350.7 + 350.5 350.5 350.3 350.1 349.9 349.7 349.7 349.5 349.3 349.3 ]plong + 528.9 350.5 528.7 350.7 528.5 350.9 528.1 350.9 527.9 351.1 5 pls + s[ 528.9 529.3 529.5 529.7 529.9 530.1 530.5 530.7 530.9 531.1 531.3 531.7 531.9 532.1 532.5 ][ 350.5 350.5 350.3 + 350.1 349.9 349.7 349.7 349.5 349.3 349.1 348.9 348.9 348.7 348.5 348.5 ]plong + s[ 532.5 532.7 532.9 533.1 533.3 533.7 533.9 ][ 348.5 348.3 348.1 347.9 347.7 347.7 347.5 ]plong + 249.9 351.1 250.1 351.3 2 pls + s[ 250.1 250.3 250.5 250.9 251.1 251.3 251.7 251.9 252.1 252.5 252.7 252.9 253.1 253.3 253.7 ][ 351.3 351.5 351.7 + 351.7 351.9 352.1 352.1 352.3 352.5 352.5 352.7 352.9 353.1 353.3 353.3 ]plong + s[ 256.4 256.2 255.8 255.6 255.4 255.0 254.8 254.6 254.2 254.0 253.7 ][ 354.7 354.5 354.5 354.3 354.1 354.1 353.9 + 353.7 353.7 353.5 353.3 ]plong + 521.6 354.5 521.4 354.7 2 pls + s[ 521.6 522.0 522.2 522.4 522.8 523.0 523.2 523.6 523.8 524.0 524.2 524.4 524.8 525.0 525.2 ][ 354.5 354.5 354.3 + 354.1 354.1 353.9 353.7 353.7 353.5 353.3 353.1 352.9 352.9 352.7 352.5 ]plong + s[ 525.2 525.6 525.8 526.0 526.4 526.6 526.8 527.0 527.2 527.7 527.9 ][ 352.5 352.5 352.3 352.1 352.1 351.9 351.7 + 351.5 351.3 351.3 351.1 ]plong + 256.4 354.7 256.6 354.9 257.0 354.9 257.2 355.1 257.4 355.3 5 pls + s[ 257.4 257.6 257.8 258.2 258.4 258.6 259.0 259.2 259.4 259.8 260.0 260.2 260.6 260.8 261.0 ][ 355.3 355.5 355.8 + 355.8 356.0 356.2 356.2 356.4 356.6 356.6 356.8 357.0 357.0 357.2 357.4 ]plong + s[ 263.6 263.4 263.0 262.6 262.4 262.2 261.8 261.6 261.4 261.0 ][ 358.4 358.2 358.2 358.2 358.0 357.8 357.8 357.6 + 357.4 357.4 ]plong + 514.4 358.2 514.2 358.4 2 pls + s[ 514.4 514.8 515.0 515.2 515.6 515.8 516.0 516.4 516.6 516.8 517.2 517.4 517.6 518.0 ][ 358.2 358.2 358.0 357.8 + 357.8 357.6 357.4 357.4 357.2 357.0 357.0 356.8 356.6 356.6 ]plong + s[ 518.0 518.2 518.4 518.8 519.0 519.2 519.6 519.8 520.0 520.4 520.6 520.8 521.2 521.4 ][ 356.6 356.4 356.2 356.2 + 356.0 355.8 355.8 355.5 355.3 355.3 355.1 354.9 354.9 354.7 ]plong + 263.6 358.4 263.8 358.6 264.2 358.6 264.4 358.8 264.6 359.0 5 pls + s[ 264.6 265.0 265.2 265.4 265.8 266.0 266.2 266.6 266.8 267.0 267.4 267.6 267.8 268.2 ][ 359.0 359.0 359.2 359.4 + 359.4 359.6 359.8 359.8 360.0 360.2 360.2 360.4 360.6 360.6 ]plong + s[ 271.6 271.4 271.0 270.6 270.4 270.2 269.8 269.6 269.4 269.0 268.8 268.6 268.2 ][ 362.0 361.8 361.8 361.8 361.6 + 361.4 361.4 361.2 361.0 361.0 360.8 360.6 360.6 ]plong + 507.1 361.4 506.9 361.6 506.7 361.8 506.3 361.8 506.1 362.0 5 pls + s[ 507.1 507.5 507.9 508.1 508.3 508.7 508.9 509.1 509.6 509.8 510.0 510.4 510.8 ][ 361.4 361.4 361.4 361.2 361.0 + 361.0 360.8 360.6 360.6 360.4 360.2 360.2 360.2 ]plong + s[ 510.8 511.0 511.2 511.6 511.8 512.0 512.4 512.6 512.8 513.2 513.4 513.6 514.0 514.2 ][ 360.2 360.0 359.8 359.8 + 359.6 359.4 359.4 359.2 359.0 359.0 358.8 358.6 358.6 358.4 ]plong + 271.6 362.0 271.8 362.2 2 pls + s[ 271.8 272.3 272.5 272.7 273.1 273.5 273.7 273.9 274.3 274.5 274.7 275.1 275.3 275.5 ][ 362.2 362.2 362.4 362.6 + 362.6 362.6 362.8 363.0 363.0 363.2 363.4 363.4 363.6 363.8 ]plong + s[ 275.5 275.9 276.3 276.5 276.7 277.1 277.5 277.7 277.9 278.3 278.5 278.7 279.1 ][ 363.8 363.8 363.8 364.0 364.2 + 364.2 364.2 364.4 364.6 364.6 364.8 365.0 365.0 ]plong + s[ 280.5 280.3 279.9 279.7 279.5 279.1 ][ 365.6 365.4 365.4 365.2 365.0 365.0 ]plong + s[ 496.9 497.1 497.5 497.9 498.1 498.3 498.7 499.1 499.3 499.5 499.9 ][ 365.6 365.4 365.4 365.4 365.2 365.0 365.0 + 365.0 364.8 364.6 364.6 ]plong + s[ 499.9 500.1 500.3 500.7 501.1 501.3 501.5 501.9 502.1 502.3 502.7 503.1 503.3 503.5 ][ 364.6 364.4 364.2 364.2 + 364.2 364.0 363.8 363.8 363.6 363.4 363.4 363.4 363.2 363.0 ]plong + s[ 503.5 503.9 504.1 504.3 504.7 505.1 505.3 505.5 505.9 506.1 ][ 363.0 363.0 362.8 362.6 362.6 362.6 362.4 362.2 + 362.2 362.0 ]plong + s[ 282.7 282.3 281.9 281.7 281.5 281.1 280.7 280.5 ][ 366.2 366.2 366.2 366.0 365.8 365.8 365.8 365.6 ]plong + s[ 282.7 282.9 283.1 283.5 283.9 284.1 284.3 284.7 285.1 285.3 285.5 285.9 286.3 ][ 366.2 366.4 366.6 366.6 366.6 + 366.8 367.0 367.0 367.0 367.2 367.4 367.4 367.4 ]plong + s[ 286.3 286.5 286.7 287.1 287.5 287.7 287.9 288.3 288.7 288.9 289.1 289.5 289.9 ][ 367.4 367.6 367.8 367.8 367.8 + 368.0 368.2 368.2 368.2 368.4 368.6 368.6 368.6 ]plong + s[ 291.4 291.2 290.8 290.3 290.1 289.9 ][ 369.2 369.0 369.0 369.0 368.8 368.6 ]plong + s[ 486.4 486.6 487.0 487.4 487.6 487.8 488.2 488.6 488.8 489.0 ][ 369.2 369.0 369.0 369.0 368.8 368.6 368.6 368.6 + 368.4 368.2 ]plong + s[ 489.0 489.4 489.8 490.0 490.2 490.6 491.0 491.3 491.5 491.9 492.3 492.5 492.7 ][ 368.2 368.2 368.2 368.0 367.8 + 367.8 367.8 367.6 367.4 367.4 367.4 367.2 367.0 ]plong + s[ 492.7 493.1 493.5 493.7 493.9 494.3 494.7 494.9 495.1 495.5 495.9 496.1 496.3 ][ 367.0 367.0 367.0 366.8 366.6 + 366.6 366.6 366.4 366.2 366.2 366.2 366.0 365.8 ]plong + 496.9 365.6 496.7 365.8 496.3 365.8 3 pls + s[ 293.6 293.2 292.8 292.6 292.4 292.0 291.6 291.4 ][ 369.8 369.8 369.8 369.6 369.4 369.4 369.4 369.2 ]plong + s[ 293.6 293.8 294.0 294.4 294.8 295.0 295.2 295.6 296.0 296.4 296.6 296.8 297.2 ][ 369.8 370.0 370.2 370.2 370.2 + 370.4 370.6 370.6 370.6 370.6 370.8 371.0 371.0 ]plong + s[ 297.2 297.6 297.8 298.0 298.4 298.8 299.2 299.4 299.6 300.0 300.4 300.6 300.8 ][ 371.0 371.0 371.2 371.4 371.4 + 371.4 371.4 371.6 371.8 371.8 371.8 372.0 372.2 ]plong + s[ 303.8 303.6 303.2 302.8 302.4 302.2 302.0 301.6 301.2 300.8 ][ 372.8 372.6 372.6 372.6 372.6 372.4 372.2 372.2 + 372.2 372.2 ]plong + 474.6 372.6 474.2 372.6 474.0 372.8 3 pls + s[ 474.6 475.0 475.4 475.6 475.8 476.2 476.6 476.8 477.0 477.4 477.8 478.2 ][ 372.6 372.6 372.6 372.4 372.2 372.2 + 372.2 372.0 371.8 371.8 371.8 371.8 ]plong + s[ 478.2 478.4 478.6 479.0 479.4 479.8 480.0 480.2 480.6 481.0 481.2 481.4 481.8 ][ 371.8 371.6 371.4 371.4 371.4 + 371.4 371.2 371.0 371.0 371.0 370.8 370.6 370.6 ]plong + s[ 481.8 482.2 482.4 482.6 483.0 483.4 483.8 484.0 484.2 484.6 485.0 485.2 485.4 ][ 370.6 370.6 370.4 370.2 370.2 + 370.2 370.2 370.0 369.8 369.8 369.8 369.6 369.4 ]plong + 486.4 369.2 486.2 369.4 485.8 369.4 485.4 369.4 4 pls + 303.8 372.8 304.0 373.0 304.4 373.0 3 pls + s[ 304.4 304.8 305.2 305.4 305.6 306.0 306.4 306.6 306.8 307.2 307.6 308.0 ][ 373.0 373.0 373.0 373.2 373.4 373.4 + 373.4 373.6 373.8 373.8 373.8 373.8 ]plong + s[ 308.0 308.2 308.4 308.9 309.3 309.7 310.1 310.3 310.5 310.9 311.3 311.7 ][ 373.8 374.1 374.3 374.3 374.3 374.3 + 374.3 374.5 374.7 374.7 374.7 374.7 ]plong + s[ 311.7 311.9 312.1 312.5 312.9 313.3 313.5 313.7 314.1 314.5 314.9 315.3 ][ 374.7 374.9 375.1 375.1 375.1 375.1 + 375.3 375.5 375.5 375.5 375.5 375.5 ]plong + s[ 315.3 315.5 315.7 316.1 316.5 316.9 317.1 317.3 317.7 318.1 318.5 318.9 ][ 375.5 375.7 375.9 375.9 375.9 375.9 + 376.1 376.3 376.3 376.3 376.3 376.3 ]plong + 318.9 376.3 319.1 376.5 2 pls + 460.1 376.3 459.7 376.3 459.3 376.3 458.9 376.3 458.7 376.5 5 pls + s[ 460.1 460.5 460.7 460.9 461.3 461.7 462.1 462.3 462.5 462.9 463.3 463.7 ][ 376.3 376.3 376.1 375.9 375.9 375.9 + 375.9 375.7 375.5 375.5 375.5 375.5 ]plong + s[ 463.7 464.1 464.3 464.5 464.9 465.3 465.7 465.9 466.1 466.5 466.9 467.3 ][ 375.5 375.5 375.3 375.1 375.1 375.1 + 375.1 374.9 374.7 374.7 374.7 374.7 ]plong + s[ 467.3 467.5 467.7 468.1 468.5 468.9 469.1 469.3 469.7 470.1 470.5 470.7 470.9 ][ 374.7 374.5 374.3 374.3 374.3 + 374.3 374.1 373.8 373.8 373.8 373.8 373.6 373.4 ]plong + s[ 470.9 471.3 471.7 472.1 472.3 472.5 473.0 473.4 473.8 474.0 ][ 373.4 373.4 373.4 373.4 373.2 373.0 373.0 373.0 + 373.0 372.8 ]plong + s[ 319.1 319.3 319.7 320.1 320.5 320.9 321.1 321.3 321.7 322.1 322.5 ][ 376.5 376.7 376.7 376.7 376.7 376.7 376.9 + 377.1 377.1 377.1 377.1 ]plong + s[ 322.5 322.9 323.1 323.3 323.7 324.1 324.5 324.9 325.1 325.3 325.7 326.1 ][ 377.1 377.1 377.3 377.5 377.5 377.5 + 377.5 377.5 377.7 377.9 377.9 377.9 ]plong + s[ 326.1 326.5 327.0 327.4 327.6 327.8 328.2 328.6 329.0 329.4 329.6 329.8 ][ 377.9 377.9 377.9 377.9 378.1 378.3 + 378.3 378.3 378.3 378.3 378.5 378.7 ]plong + s[ 329.8 330.2 330.6 331.0 331.4 331.8 332.0 332.2 332.6 333.0 333.4 ][ 378.7 378.7 378.7 378.7 378.7 378.7 378.9 + 379.1 379.1 379.1 379.1 ]plong + s[ 333.4 333.8 334.2 334.6 334.8 335.0 335.4 335.8 336.2 336.6 337.0 ][ 379.1 379.1 379.1 379.1 379.3 379.5 379.5 + 379.5 379.5 379.5 379.5 ]plong + s[ 340.0 339.8 339.4 339.0 338.6 338.2 337.8 337.4 337.2 337.0 ][ 380.1 379.9 379.9 379.9 379.9 379.9 379.9 379.9 + 379.7 379.5 ]plong + 438.4 379.9 438.0 379.9 437.8 380.1 3 pls + s[ 438.4 438.8 439.2 439.6 440.0 440.4 440.6 440.8 441.2 441.6 442.0 ][ 379.9 379.9 379.9 379.9 379.9 379.9 379.7 + 379.5 379.5 379.5 379.5 ]plong + s[ 442.0 442.4 442.8 443.0 443.2 443.6 444.0 444.4 444.8 445.2 445.4 445.6 ][ 379.5 379.5 379.5 379.3 379.1 379.1 + 379.1 379.1 379.1 379.1 378.9 378.7 ]plong + s[ 445.6 446.0 446.4 446.8 447.2 447.6 447.8 448.0 448.4 448.8 449.2 ][ 378.7 378.7 378.7 378.7 378.7 378.7 378.5 + 378.3 378.3 378.3 378.3 ]plong + s[ 449.2 449.6 450.0 450.2 450.4 450.8 451.2 451.6 452.0 452.4 452.6 452.8 ][ 378.3 378.3 378.3 378.1 377.9 377.9 + 377.9 377.9 377.9 377.9 377.7 377.5 ]plong + s[ 452.8 453.2 453.6 454.0 454.4 454.7 454.9 455.3 455.7 456.1 456.5 ][ 377.5 377.5 377.5 377.5 377.5 377.3 377.1 + 377.1 377.1 377.1 377.1 ]plong + s[ 456.5 456.7 456.9 457.3 457.7 458.1 458.5 458.7 ][ 377.1 376.9 376.7 376.7 376.7 376.7 376.7 376.5 ]plong + 340.0 380.1 340.2 380.3 340.6 380.3 3 pls + s[ 340.6 341.0 341.4 341.8 342.2 342.6 343.0 343.2 343.4 343.8 344.2 ][ 380.3 380.3 380.3 380.3 380.3 380.3 380.3 + 380.5 380.7 380.7 380.7 ]plong + s[ 344.2 344.6 345.0 345.5 345.9 346.3 346.5 346.7 347.1 347.5 347.9 ][ 380.7 380.7 380.7 380.7 380.7 380.7 380.9 + 381.1 381.1 381.1 381.1 ]plong + s[ 347.9 348.3 348.7 349.1 349.5 349.7 349.9 350.3 350.7 351.1 351.5 ][ 381.1 381.1 381.1 381.1 381.1 381.3 381.5 + 381.5 381.5 381.5 381.5 ]plong + s[ 351.5 351.9 352.3 352.7 353.1 353.5 353.7 353.9 354.3 354.7 355.1 ][ 381.5 381.5 381.5 381.5 381.5 381.5 381.7 + 381.9 381.9 381.9 381.9 ]plong + s[ 355.1 355.5 355.9 356.3 356.7 357.1 357.5 357.9 358.1 358.3 358.7 ][ 381.9 381.9 381.9 381.9 381.9 381.9 381.9 + 381.9 382.1 382.3 382.3 ]plong + s[ 358.7 359.1 359.5 359.9 360.3 360.7 361.1 361.5 361.9 362.3 ][ 382.3 382.3 382.3 382.3 382.3 382.3 382.3 382.3 + 382.3 382.3 ]plong + s[ 362.3 362.7 362.9 363.1 363.6 364.0 364.4 364.8 365.2 365.6 366.0 ][ 382.3 382.3 382.5 382.7 382.7 382.7 382.7 + 382.7 382.7 382.7 382.7 ]plong + s[ 366.0 366.4 366.8 367.2 367.6 368.0 368.4 368.8 369.2 369.4 369.6 ][ 382.7 382.7 382.7 382.7 382.7 382.7 382.7 + 382.7 382.7 382.9 383.1 ]plong + s[ 369.6 370.0 370.4 370.8 371.2 371.6 372.0 372.4 372.8 373.2 ][ 383.1 383.1 383.1 383.1 383.1 383.1 383.1 383.1 + 383.1 383.1 ]plong + s[ 373.2 373.6 374.0 374.4 374.8 375.2 375.6 376.0 376.4 376.8 ][ 383.1 383.1 383.1 383.1 383.1 383.1 383.1 383.1 + 383.1 383.1 ]plong + s[ 376.8 377.2 377.6 378.0 378.4 378.8 379.2 379.6 379.8 380.0 380.4 ][ 383.1 383.1 383.1 383.1 383.1 383.1 383.1 + 383.1 383.3 383.5 383.5 ]plong + s[ 380.4 380.8 381.2 381.7 382.1 382.5 382.9 383.3 383.7 384.1 ][ 383.5 383.5 383.5 383.5 383.5 383.5 383.5 383.5 + 383.5 383.5 ]plong + s[ 384.1 384.5 384.9 385.3 385.7 386.1 386.5 386.9 387.3 387.7 ][ 383.5 383.5 383.5 383.5 383.5 383.5 383.5 383.5 + 383.5 383.5 ]plong + s[ 387.7 388.1 388.5 388.9 389.3 389.7 390.1 390.5 390.9 391.3 ][ 383.5 383.5 383.5 383.5 383.5 383.5 383.5 383.5 + 383.5 383.5 ]plong + s[ 391.3 391.7 392.1 392.5 392.9 393.3 393.7 394.1 394.5 394.9 ][ 383.5 383.5 383.5 383.5 383.5 383.5 383.5 383.5 + 383.5 383.5 ]plong + s[ 394.9 395.3 395.7 396.1 396.5 396.9 397.3 397.7 397.9 398.1 398.5 ][ 383.5 383.5 383.5 383.5 383.5 383.5 383.5 + 383.5 383.3 383.1 383.1 ]plong + s[ 398.5 398.9 399.3 399.7 400.2 400.6 401.0 401.4 401.8 402.2 ][ 383.1 383.1 383.1 383.1 383.1 383.1 383.1 383.1 + 383.1 383.1 ]plong + s[ 402.2 402.6 403.0 403.4 403.8 404.2 404.6 405.0 405.4 405.8 ][ 383.1 383.1 383.1 383.1 383.1 383.1 383.1 383.1 + 383.1 383.1 ]plong + s[ 405.8 406.2 406.6 407.0 407.4 407.8 408.0 408.2 408.6 409.0 409.4 ][ 383.1 383.1 383.1 383.1 383.1 383.1 382.9 + 382.7 382.7 382.7 382.7 ]plong + s[ 409.4 409.8 410.2 410.6 411.0 411.4 411.8 412.2 412.6 413.0 ][ 382.7 382.7 382.7 382.7 382.7 382.7 382.7 382.7 + 382.7 382.7 ]plong + s[ 413.0 413.4 413.8 414.2 414.4 414.6 415.0 415.4 415.8 416.2 416.6 ][ 382.7 382.7 382.7 382.7 382.5 382.3 382.3 + 382.3 382.3 382.3 382.3 ]plong + s[ 416.6 417.0 417.4 417.8 418.3 418.7 419.1 419.5 419.7 419.9 420.3 ][ 382.3 382.3 382.3 382.3 382.3 382.3 382.3 + 382.3 382.1 381.9 381.9 ]plong + s[ 420.3 420.7 421.1 421.5 421.9 422.3 422.7 423.1 423.5 423.9 ][ 381.9 381.9 381.9 381.9 381.9 381.9 381.9 381.9 + 381.9 381.9 ]plong + s[ 423.9 424.1 424.3 424.7 425.1 425.5 425.9 426.3 426.7 427.1 427.5 ][ 381.9 381.7 381.5 381.5 381.5 381.5 381.5 + 381.5 381.5 381.5 381.5 ]plong + s[ 427.5 427.9 428.1 428.3 428.7 429.1 429.5 429.9 430.3 430.7 431.1 ][ 381.5 381.5 381.3 381.1 381.1 381.1 381.1 + 381.1 381.1 381.1 381.1 ]plong + s[ 431.1 431.3 431.5 431.9 432.3 432.7 433.1 433.5 433.9 434.3 434.5 434.7 ][ 381.1 380.9 380.7 380.7 380.7 380.7 + 380.7 380.7 380.7 380.7 380.5 380.3 ]plong + s[ 434.7 435.1 435.5 435.9 436.3 436.8 437.2 437.6 437.8 ][ 380.3 380.3 380.3 380.3 380.3 380.3 380.3 380.3 380.1 +]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 408.91 297.70 moveto[ 408.9 410.9 436.0 434.0 ][ 297.7 309.7 305.5 293.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 413.3 298.7 414.7 307.3 413.3 306.3 412.4 306.0 4 pls + s[ 421.7 420.4 419.9 419.7 420.0 420.8 422.4 423.5 424.2 424.5 424.3 423.7 423.3 422.0 420.3 419.1 418.8 418.5 + 418.7 419.3 420.2 421.5 423.3 424.1 424.7 424.8 424.6 423.4 421.7 ][ 306.2 306.0 305.2 304.4 303.5 302.9 302.3 + 301.6 300.7 299.8 298.5 297.8 297.4 297.2 297.5 298.1 298.6 299.5 300.7 301.5 302.2 302.4 302.5 302.8 303.6 + 304.4 305.3 305.9 306.2 ]plong + s[ 430.4 429.1 428.1 427.3 427.1 427.2 427.8 429.0 429.8 431.1 432.1 432.9 433.1 433.0 432.4 431.2 430.4 ][ 304.7 + 304.5 303.4 301.4 300.2 298.1 296.7 296.1 295.9 296.2 297.3 299.2 300.5 302.6 304.0 304.6 304.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 344.62 293.72 moveto[ 344.6 342.7 369.0 371.0 ][ 293.7 305.7 310.0 298.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 345.7 345.7 346.0 346.3 347.1 348.7 349.6 350.1 350.6 350.8 350.5 349.9 346.4 352.2 ][ 302.4 302.8 303.7 304.2 + 304.7 305.0 304.7 304.3 303.6 302.8 301.9 300.5 295.7 296.7 ]plong + 360.4 301.0 354.2 299.9 357.4 306.4 3 pls + 358.8 297.7 357.4 306.4 2 pls + s[ 364.0 362.8 362.2 362.1 362.3 363.0 364.1 365.4 366.2 367.4 368.0 368.1 367.9 367.1 366.1 364.8 364.0 ][ 307.5 + 306.9 305.5 303.4 302.1 300.1 299.0 298.8 299.0 299.6 300.9 303.1 304.3 306.3 307.4 307.6 307.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 284.64 272.61 moveto[ 284.6 279.9 304.6 309.3 ][ 272.6 283.8 294.1 283.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 283.3 287.5 286.5 287.7 288.6 289.1 290.0 290.3 290.4 290.0 289.0 287.8 286.5 286.0 285.3 ][ 283.4 285.2 281.1 + 281.6 281.5 281.3 280.3 279.5 278.2 277.1 276.3 275.8 275.7 275.9 276.5 ]plong + s[ 292.5 291.5 291.3 291.7 292.2 293.4 294.6 295.9 296.7 297.7 298.0 297.6 297.1 295.9 294.6 293.3 292.5 ][ 287.3 + 286.4 284.9 282.8 281.7 279.9 279.1 279.2 279.5 280.4 281.8 283.9 285.1 286.8 287.7 287.6 287.3 ]plong + s[ 300.2 299.2 299.0 299.4 299.9 301.1 302.3 303.6 304.4 305.4 305.7 305.3 304.8 303.6 302.3 301.0 300.2 ][ 290.5 + 289.6 288.1 286.1 284.9 283.1 282.3 282.4 282.7 283.6 285.1 287.2 288.3 290.1 290.9 290.8 290.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 241.66 252.08 moveto[ 241.7 236.9 246.1 250.9 ][ 252.1 263.2 267.2 256.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 241.7 240.8 240.5 240.9 241.4 242.6 243.9 245.2 246.0 247.0 247.2 246.8 246.3 245.1 243.8 242.5 241.7 ][ 263.5 + 262.6 261.1 259.0 257.9 256.1 255.3 255.4 255.8 256.6 258.1 260.2 261.4 263.1 263.9 263.8 263.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 524.08 257.21 moveto[ 524.1 528.8 545.4 540.6 ][ 257.2 268.4 261.3 250.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 534.2 534.2 533.2 532.4 531.1 529.8 528.6 527.8 527.5 528.0 529.0 529.3 530.7 531.8 532.6 532.8 532.9 532.5 + 531.5 531.1 529.8 528.7 527.8 ][ 262.9 263.8 264.7 265.0 265.1 264.3 262.5 260.6 258.9 257.8 256.9 256.8 256.7 + 257.1 258.1 258.5 259.8 260.9 261.8 261.9 262.0 261.6 260.6 ]plong + s[ 539.7 538.4 537.1 535.9 535.4 535.0 535.3 536.3 537.0 538.3 539.6 540.8 541.3 541.7 541.5 540.5 539.7 ][ 261.9 + 262.0 261.2 259.4 258.3 256.2 254.7 253.8 253.5 253.4 254.2 256.0 257.1 259.2 260.7 261.6 261.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 470.40 282.17 moveto[ 470.4 475.1 498.6 493.9 ][ 282.2 293.3 283.4 272.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 474.9 282.1 478.3 290.2 476.7 289.5 475.7 289.4 4 pls + s[ 482.5 482.7 483.4 483.9 484.9 486.4 487.0 487.2 487.3 487.0 486.3 485.0 479.5 484.9 ][ 286.1 286.5 287.1 287.3 + 287.4 286.7 286.0 285.5 284.6 283.8 283.2 282.3 280.1 277.9 ]plong + s[ 492.9 491.6 490.4 489.2 488.7 488.2 488.5 489.5 490.3 491.6 492.9 494.1 494.5 495.0 494.7 493.7 492.9 ][ 284.0 + 284.1 283.2 281.5 280.3 278.2 276.8 275.9 275.6 275.5 276.3 278.1 279.2 281.3 282.8 283.6 284.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 391.48 197.07 moveto[ 391.5 382.4 401.5 410.5 ][ 197.1 205.1 226.7 218.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 393.8 207.9 388.8 202.2 2 pls + s[ 397.7 398.4 398.5 397.9 397.7 396.5 395.3 394.1 393.8 393.2 393.1 393.6 393.9 395.0 396.2 397.7 399.3 400.6 + 401.0 400.4 399.9 398.8 397.9 ][ 215.5 214.4 213.2 212.0 211.7 211.0 210.9 211.4 211.7 212.9 214.0 215.3 215.6 + 216.2 216.3 215.5 214.1 212.4 211.0 209.8 209.1 208.5 208.7 ]plong + s[ 399.1 398.6 399.0 400.3 401.2 403.1 404.6 405.7 406.2 406.8 406.4 405.1 404.1 402.3 400.8 399.7 399.1 ][ 221.5 + 220.3 218.9 217.2 216.3 215.3 215.1 215.7 216.3 217.6 219.0 220.7 221.5 222.6 222.8 222.1 221.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 332.60 197.57 moveto[ 332.6 334.7 363.1 361.0 ][ 197.6 209.5 204.5 192.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 343.0 201.2 335.6 202.6 2 pls + s[ 351.9 351.6 350.5 349.6 348.3 347.3 346.5 346.1 346.3 346.9 348.1 348.5 349.8 350.8 351.4 351.5 351.3 350.6 + 349.5 349.1 347.7 346.8 346.1 ][ 203.5 204.4 205.0 205.1 205.0 203.9 201.9 199.8 198.1 197.1 196.5 196.4 196.6 + 197.3 198.5 198.9 200.2 201.2 201.8 201.9 201.7 201.0 199.8 ]plong + s[ 357.5 356.1 355.1 354.3 354.1 354.2 354.8 355.9 356.7 358.0 359.1 359.9 360.1 360.0 359.4 358.3 357.5 ][ 203.8 + 203.6 202.5 200.5 199.3 197.1 195.8 195.1 195.0 195.2 196.3 198.2 199.5 201.6 203.0 203.6 203.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 262.18 225.58 moveto[ 262.2 267.8 293.3 287.7 ][ 225.6 236.3 222.8 212.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 273.2 225.9 266.6 229.4 2 pls + s[ 278.9 282.9 279.2 280.3 280.8 281.0 280.8 280.4 279.4 278.3 277.0 275.9 275.0 274.8 274.8 ][ 228.6 226.4 224.6 + 224.1 223.3 222.7 221.4 220.7 219.8 219.4 219.6 220.2 221.2 221.7 222.7 ]plong + s[ 287.7 286.4 285.1 283.8 283.2 282.6 282.7 283.6 284.4 285.7 287.0 288.4 288.9 289.5 289.4 288.5 287.7 ][ 223.9 + 224.1 223.4 221.7 220.6 218.6 217.1 216.1 215.7 215.5 216.2 217.9 219.0 221.0 222.5 223.5 223.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 228.10 302.06 moveto[ 228.1 216.4 221.3 233.0 ][ 302.1 305.3 323.0 319.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 218.8 220.0 222.6 222.9 223.5 224.1 225.4 226.2 227.3 227.9 227.9 227.6 226.9 226.3 225.4 ][ 307.6 312.1 308.8 + 310.0 310.7 311.0 311.0 310.8 310.1 309.1 307.7 306.5 305.4 305.1 305.0 ]plong + s[ 221.5 221.5 222.5 224.4 225.6 227.8 229.2 229.9 230.2 230.1 229.1 227.2 226.0 223.9 222.4 221.7 221.5 ][ 317.3 + 316.0 314.9 313.9 313.6 313.4 313.9 315.0 315.8 317.1 318.2 319.2 319.5 319.7 319.2 318.1 317.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 255.03 346.40 moveto[ 255.0 247.9 262.5 269.6 ][ 346.4 356.2 366.7 356.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 255.4 254.6 253.3 252.6 251.9 251.9 252.8 254.0 255.3 256.5 257.8 258.1 258.9 259.1 258.7 258.4 257.4 256.2 + 254.9 254.6 253.8 253.6 254.0 ][ 358.0 358.4 358.0 357.6 356.5 355.0 353.0 351.3 350.2 350.1 350.4 350.7 351.8 + 352.9 354.2 354.5 355.3 355.5 355.1 354.8 353.8 352.6 351.3 ]plong + s[ 259.1 258.3 258.3 259.2 260.0 261.5 262.9 264.2 264.9 265.6 265.6 264.7 264.0 262.4 261.0 259.7 259.1 ][ 362.2 + 361.1 359.6 357.7 356.7 355.2 354.7 355.1 355.6 356.7 358.2 360.1 361.1 362.6 363.1 362.7 362.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 317.86 377.83 moveto[ 317.9 314.7 332.5 335.6 ][ 377.8 389.5 394.3 382.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 323.2 323.1 322.5 321.4 321.0 319.7 318.7 317.9 317.8 317.9 318.5 319.6 320.0 321.3 322.3 323.2 323.7 323.8 + 323.4 322.3 321.5 320.1 319.5 ][ 387.0 385.7 384.7 384.0 383.9 383.9 384.5 385.6 386.0 387.4 388.4 389.1 389.2 + 389.1 388.5 387.0 385.0 382.9 381.5 380.7 380.5 380.6 381.3 ]plong + s[ 327.7 326.6 326.1 326.2 326.5 327.5 328.6 329.9 330.7 331.8 332.3 332.2 331.9 330.9 329.8 328.5 327.7 ][ 391.3 + 390.5 389.1 387.0 385.8 383.9 382.9 382.8 383.0 383.7 385.2 387.3 388.5 390.4 391.4 391.5 391.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 354.85 413.90 moveto[ 354.8 354.8 429.2 429.2 ][ 413.9 426.0 426.0 413.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 356.9 415.6 360.3 424.3 2 pls + 363.6 415.6 360.3 424.3 2 pls + 362.4 418.5 358.2 418.5 2 pls + 372.4 415.6 372.4 424.3 2 pls + 377.8 424.3 372.4 424.3 2 pls + 375.7 420.2 372.4 420.2 2 pls + 379.9 415.6 379.9 424.3 2 pls + 384.9 415.6 384.9 424.3 2 pls + 387.8 424.3 382.0 424.3 2 pls + s[ 395.4 394.5 393.3 391.6 390.4 389.5 389.5 389.9 390.4 391.2 393.7 394.5 394.9 395.4 395.4 394.5 393.3 391.6 + 390.4 389.5 ][ 423.1 423.9 424.3 424.3 423.9 423.1 422.2 421.4 421.0 420.6 419.7 419.3 418.9 418.1 416.8 416.0 + 415.6 415.6 416.0 416.8 ]plong + 407.5 415.6 406.6 415.6 405.8 416.0 405.4 417.2 405.4 424.3 5 pls + 407.1 421.4 404.1 421.4 2 pls + s[ 409.6 414.6 414.6 414.2 413.7 412.9 411.7 410.8 410.0 409.6 409.6 410.0 410.8 411.7 412.9 413.7 414.6 ][ 418.9 + 418.9 419.7 420.6 421.0 421.4 421.4 421.0 420.2 418.9 418.1 416.8 416.0 415.6 415.6 416.0 416.8 ]plong + s[ 421.7 421.3 420.0 418.8 417.5 417.1 417.5 418.3 420.4 421.3 421.7 421.7 421.3 420.0 418.8 417.5 417.1 ][ 420.2 + 421.0 421.4 421.4 421.0 420.2 419.3 418.9 418.5 418.1 417.2 416.8 416.0 415.6 415.6 416.0 416.8 ]plong + 427.1 415.6 426.3 415.6 425.4 416.0 425.0 417.2 425.0 424.3 5 pls + 426.7 421.4 423.8 421.4 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/ast_tester b/ast_tester/ast_tester new file mode 100755 index 0000000..c7c7b13 --- /dev/null +++ b/ast_tester/ast_tester @@ -0,0 +1,230 @@ +#!/bin/tcsh +#+ +# Purpose: +# Does a few tests of the version of AST installed in $INSTALL. + +# Usage: +# ast_tester <-nd> + +# Description: +# Build and run various tests of an AST installation. +# +# Some tests produce graphical output. Postscript plots of the FITS +# headers in the current directory are produced for visual comparison +# with previous versions. For each file matching "*.head" in the current +# directory, a file is created called "*-new.ps". This file should be +# compared visually with the file "*.ps". The new files are displayed +# automatically unless the -nd option is supplied on the command line +# +# This script assumes the gfortran compiler is available. If this is not +# the case, do a global edit of gfortran to whatever fortran 77 compiler +# you have available (you will probably also need to change the compiler +# flags). + +# Options: +# -nd : Suppresses display of test plots. + +# Prior Requirements: +# - Unless the "-nd" option is specified, it requires the gv command (a +# frontend for ghostscript) to be on your PATH. +# - The following environment variables are used: +# STARLINK - should be set to the root of the tree in which the SSC is +# installed. A default of /star is used if not set. +# AST - should be set to the root of the tree in which the version of +# AST to be tested is installed. A default of $STARLINK is used +# if not set. + +# Author: +# DSB: David Berry (JAC, Hawaii) +#- + +if( ! $?STARLINK ) then + setenv STARLINK /star +endif + +if( ! $?AST ) then + setenv AST $STARLINK +endif + +if( ! $?LDFLAGS ) then + setenv LDFLAGS "" +endif + +setenv PATH $AST/bin\:$STARLINK/bin\:$PATH + + +# Build the progs +#gfortran -fno-second-underscore -o regression regression.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK/include -L$AST/lib -L$STARLINK/lib `ast_link -ems` `chr_link` +gfortran -fno-second-underscore -o plotter plotter.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK/include -L$AST/lib -L$STARLINK/lib `ast_link -pgp -ems` `pgplot_link` +gfortran -fno-second-underscore -o wcsconverter wcsconverter.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK/include -L$AST/lib -L$STARLINK/lib `ast_link -ems` `chr_link` `err_link` +gfortran -fno-second-underscore -o simplify simplify.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK/include -L$AST/lib -L$STARLINK/lib `ast_link -ems` `chr_link` `err_link` + +# Run the other test progs +echo "" + + +foreach prog (testskyframe testframeset testchannel testpolymap testcmpmap testlutmap testfitstable testtable teststcschan teststc testspecframe testfitschan testswitchmap testrebin testrebinseq testtrangrid testnormmap testtime testrate testflux testratemap testspecflux testxmlchan testregions testkeymap ) + +gfortran -fno-second-underscore -w -g -o $prog -g $prog.f -fno-range-check $LDFLAGS -I$AST/include \ + -I$STARLINK/include -L$AST/lib -L$STARLINK/lib `ast_link -ems` \ + `psx_link` `prm_link` `chr_link` `err_link` + +$prog +rm $prog + +end + + + +foreach prog (testobject testconvert) + +gcc -o $prog $prog.c -I.. -DHAVE_CONFIG_H $LDFLAGS -L/star/lib `ast_link` + +$prog +rm $prog + +end + + + + +# Make new plots. +echo "" +echo "Generating new plots..." +echo "" +if( $1 == "-nd" ) then +echo " (but not displaying them because the -nd option was supplied)" +echo "" +endif + +rm *-new.ps >& /dev/null + +foreach n (*.head) + set bn = `basename $n .head` + + set atfile = "${bn}.attr" + if( -e $atfile ) then + set attr1 = `cat $atfile` + else + set attr1 = ' ' + endif + + set atfile = "${bn}.fattr" + if( -e $atfile ) then + set attr2 = `cat $atfile` + else + set attr2 = ' ' + endif + + set boxfile = "${bn}.box" + if( -e $boxfile ) then + set box = `cat $boxfile` + else + set box = ' ' + endif + + + set psfile = "${bn}-new.ps" + plotter $n "$attr1" "$attr2" a.ps $box + + if( -e $STARLINK/bin/psmerge ) then + $STARLINK/bin/psmerge -t300x300 -r90 a.ps > $psfile + else + cp a.ps $psfile + endif + + if( $1 != "-nd" ) then + gv $psfile -orientation=landscape + endif + +end + +rm -f a.ps + + +# Make new fits headers +echo "" +echo "Doing WCS conversion tests..." +echo "" + +set ok = 1 +foreach n ( "timj ast fits-wcs cdmatrix=1" \ + "timj ast fits-iraf" \ + "timj ast fits-aips" \ + "timj ast fits-pc" \ + "timj ast native" \ + "timj ast native" \ + "a20070718_00010_02_cube ast fits-wcs" \ + "dss fits-dss ast" \ + "dss ast dss" \ + "dss ast fits-wcs cdmatrix=1" \ + "degen1 ast fits-wcs cdmatrix=1" \ + "longslit fits-pc fits-wcs cdmatrix=1" ) + + set a = `echo $n` + + set base = $a[1] + set in_suffix = $a[2] + set encoding = $a[3] + if( $#a > 3 ) then + set attrs = $a[4] + else + set attrs = "" + endif + + set in_file = "$base.$in_suffix" + set old_file = "$base.$encoding" + set new_file = "$base-new.$encoding" + + + wcsconverter $in_file $encoding $new_file "$attrs,FitsDigits=8" + diff -c $old_file $new_file > $old_file.diff + + if( $status == 0 ) then + rm -f $old_file.diff $new_file + else + echo " $old_file and $new_file differ\!\! (see $old_file.diff)" + echo " Command was:" + echo " wcsconverter $in_file $encoding $new_file $attrs,FitsDigits=8" + set ok = 0 + endif + +end + +if( $ok == 1 ) then + echo " All WCS conversion tests passed" + echo "" +endif + + +echo "" +echo "Doing Simplification tests..." +echo "" + +set ok = 1 +foreach n ( *.map ) + set a = `basename $n .map` + simplify $n $a.out + diff -c $a.simp $a.out > $a.diff + + if( $status == 0 ) then + rm -f $a.out $a.diff + else + echo " $a.simp and $a.out differ\!\! (see $a.diff)" + set ok = 0 + endif + +end + +if( $ok == 1 ) then + echo " All simplification tests passed" + echo "" + echo "" +endif + + + + + + +rm -f plotter wcsconverter simplify testxmlchan diff --git a/ast_tester/brad.map b/ast_tester/brad.map new file mode 100644 index 0000000..d403fce --- /dev/null +++ b/ast_tester/brad.map @@ -0,0 +1,58 @@ +Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates +IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 1 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 0.215698 # Shift for axis 1 + End WinMap + MapB = # Second component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nlut = 11 # Number of lookup table elements + Start = 1 # Input value at first element + Incr = 100 # Input value increment between elements + LutInt = 0 # Interpolation method + L1 = 1.39532005786896 # Lookup table elements... + L2 = 1.50432002544403 + L3 = 1.61332011222839 + L4 = 1.72232007980347 + L5 = 1.83132004737854 + L6 = 1.94032001495361 + L7 = 2.04932022094727 + L8 = 2.15831995010376 + L9 = 2.26732015609741 + L10 = 2.37632012367249 + L11 = 2.48532009124756 + End LutMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin ZoomMap # Zoom about the origin + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Zoom = 5.23598775598299e-06 # Zoom factor + End ZoomMap + MapB = # Second component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + End CmpMap +End CmpMap diff --git a/ast_tester/brad.simp b/ast_tester/brad.simp new file mode 100644 index 0000000..13ef463 --- /dev/null +++ b/ast_tester/brad.simp @@ -0,0 +1,49 @@ + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -0.215698 # Shift for axis 1 + Scl2 = 5.23598775598299e-06 # Scale factor for axis 2 + Scl3 = 5.23598775598299e-06 # Scale factor for axis 3 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nlut = 11 # Number of lookup table elements + Start = 1 # Input value at first element + Incr = 100 # Input value increment between elements + LutInt = 0 # Interpolation method + L1 = 1.39532005786896 # Lookup table elements... + L2 = 1.50432002544403 + L3 = 1.61332011222839 + L4 = 1.72232007980347 + L5 = 1.83132004737854 + L6 = 1.94032001495361 + L7 = 2.04932022094727 + L8 = 2.15831995010376 + L9 = 2.26732015609741 + L10 = 2.37632012367249 + L11 = 2.48532009124756 + End LutMap + MapB = # Second component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + End CmpMap + End CmpMap diff --git a/ast_tester/car1.attr b/ast_tester/car1.attr new file mode 100644 index 0000000..f74084f --- /dev/null +++ b/ast_tester/car1.attr @@ -0,0 +1 @@ +numlabgap=0.05 diff --git a/ast_tester/car1.box b/ast_tester/car1.box new file mode 100644 index 0000000..09670e6 --- /dev/null +++ b/ast_tester/car1.box @@ -0,0 +1 @@ +0 0 2962 562 diff --git a/ast_tester/car1.fattr b/ast_tester/car1.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast_tester/car1.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast_tester/car1.head b/ast_tester/car1.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast_tester/car1.head @@ -0,0 +1,32 @@ +SIMPLE = T / Standard FITS format +BITPIX = 16 / Bits per pixel +NAXIS = 2 / Number of axes +NAXIS1 = 2961 / Number pixels on axis 1 +NAXIS2 = 561 / Number pixels on axis 2 +CTYPE1 = 'GLON-CAR' / axis 1 coord type +CRVAL1 = 1.850000e+02 / coord value at CRPIX1 +CDELT1 = -1.250000e-01 / pixel spacing for axis 1 +CRPIX1 = 1.000000 / ref pixel for axis 1 +CTYPE2 = 'GLAT-CAR' / axis 2 coord type +CRVAL2 = -3.500000e+01 / coord value at CRPIX2 +CDELT2 = 1.250000e-01 / pixel spacing for axis 2 +CRPIX2 = 1.000000 / ref pixel for axis 2 +BSCALE = 2.610167e-02 / real = int*bscale + bzero +BZERO = 8.321995e+02 / +DATAMIN = -6.326761e+00 / minimum real value +DATAMAX = 1.670731e+03 / maximum real value +BLANK = -32768 / missing data flag +COMMENT PARENT DISK FILE: Wco_DHT2001.fits +COMMENT temp =colscales( CHANGE-ME , -185.0000, 185.0000) +COMMENT coords=rowscales( temp, 35.0000, -35.0000) +COMMENT Written by MacFITS +COMMENT Created: Tuesday, 14 August, 2001 01:18:06 PM +COMMENT Whole-Galaxy velocity-integrated CO(1-0) map (Fig. 2) from +COMMENT "The Milky Way in Molecular Clouds: A New Complete CO Survey" +COMMENT T. M. Dame, Dap Hartmann, & P. Thaddeus (2001), ApJ, 547, 792. +COMMENT WARNING: Both the angular resolution and the sensitivity varies +COMMENT from region to region in this map: see Fig. 1 and Table 1 from +COMMENT the paper above. Moment masking and clipping were used as +COMMENT necessary to keep the noise in the map below ~1.5 K km/s. +COMMENT See Section 2.2 for details. +END diff --git a/ast_tester/car1.ps b/ast_tester/car1.ps new file mode 100644 index 0000000..9dd4fba --- /dev/null +++ b/ast_tester/car1.ps @@ -0,0 +1,684 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Feb 11 17:19:53 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: car1.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 11/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 585.0 235.5 585.0 234.4 2 pls + 569.1 234.9 569.1 234.4 2 pls + 537.5 235.5 537.5 234.4 2 pls + 553.3 234.9 553.3 234.4 2 pls + 521.6 234.9 521.6 234.4 2 pls + 490.0 235.5 490.0 234.4 2 pls + 505.8 234.9 505.8 234.4 2 pls + 474.1 234.9 474.1 234.4 2 pls + 442.4 235.5 442.4 234.4 2 pls + 458.3 234.9 458.3 234.4 2 pls + 426.6 234.9 426.6 234.4 2 pls + 394.9 235.5 394.9 234.4 2 pls + 410.8 234.9 410.8 234.4 2 pls + 379.1 234.9 379.1 234.4 2 pls + 347.4 235.5 347.4 234.4 2 pls + 363.2 234.9 363.2 234.4 2 pls + 331.6 234.9 331.6 234.4 2 pls + 299.9 235.5 299.9 234.4 2 pls + 315.7 234.9 315.7 234.4 2 pls + 284.1 234.9 284.1 234.4 2 pls + 252.4 235.5 252.4 234.4 2 pls + 268.2 234.9 268.2 234.4 2 pls + 236.5 234.9 236.5 234.4 2 pls + 204.9 235.5 204.9 234.4 2 pls + 220.7 234.9 220.7 234.4 2 pls + 200.6 239.8 199.5 239.8 2 pls + 200.0 235.6 199.5 235.6 2 pls + 200.0 237.7 199.5 237.7 2 pls + 200.0 241.9 199.5 241.9 2 pls + 200.0 244.0 199.5 244.0 2 pls + 200.6 250.4 199.5 250.4 2 pls + 200.0 246.1 199.5 246.1 2 pls + 200.0 248.3 199.5 248.3 2 pls + 200.0 252.5 199.5 252.5 2 pls + 200.0 254.6 199.5 254.6 2 pls + 200.6 260.9 199.5 260.9 2 pls + 200.0 256.7 199.5 256.7 2 pls + 200.0 258.8 199.5 258.8 2 pls + 200.0 263.0 199.5 263.0 2 pls + 200.0 265.2 199.5 265.2 2 pls + 200.6 271.5 199.5 271.5 2 pls + 200.0 267.3 199.5 267.3 2 pls + 200.0 269.4 199.5 269.4 2 pls + 200.0 273.6 199.5 273.6 2 pls + 200.0 275.7 199.5 275.7 2 pls + 200.6 282.0 199.5 282.0 2 pls + 200.0 277.8 199.5 277.8 2 pls + 200.0 279.9 199.5 279.9 2 pls + 200.0 284.2 199.5 284.2 2 pls + 200.0 286.3 199.5 286.3 2 pls + 200.6 292.6 199.5 292.6 2 pls + 200.0 288.4 199.5 288.4 2 pls + 200.0 290.5 199.5 290.5 2 pls + 200.0 294.7 199.5 294.7 2 pls + 200.0 296.8 199.5 296.8 2 pls + 200.6 303.2 199.5 303.2 2 pls + 200.0 298.9 199.5 298.9 2 pls + 200.0 301.1 199.5 301.1 2 pls + 200.0 305.3 199.5 305.3 2 pls + 200.0 307.4 199.5 307.4 2 pls + 585.0 307.5 585.0 308.6 2 pls + 569.1 308.1 569.1 308.6 2 pls + 537.5 307.5 537.5 308.6 2 pls + 553.3 308.1 553.3 308.6 2 pls + 521.6 308.1 521.6 308.6 2 pls + 490.0 307.5 490.0 308.6 2 pls + 505.8 308.1 505.8 308.6 2 pls + 474.1 308.1 474.1 308.6 2 pls + 442.4 307.5 442.4 308.6 2 pls + 458.3 308.1 458.3 308.6 2 pls + 426.6 308.1 426.6 308.6 2 pls + 394.9 307.5 394.9 308.6 2 pls + 410.8 308.1 410.8 308.6 2 pls + 379.1 308.1 379.1 308.6 2 pls + 347.4 307.5 347.4 308.6 2 pls + 363.2 308.1 363.2 308.6 2 pls + 331.6 308.1 331.6 308.6 2 pls + 299.9 307.5 299.9 308.6 2 pls + 315.7 308.1 315.7 308.6 2 pls + 284.1 308.1 284.1 308.6 2 pls + 252.4 307.5 252.4 308.6 2 pls + 268.2 308.1 268.2 308.6 2 pls + 236.5 308.1 236.5 308.6 2 pls + 204.9 307.5 204.9 308.6 2 pls + 220.7 308.1 220.7 308.6 2 pls + 589.3 239.8 590.4 239.8 2 pls + 589.9 235.6 590.4 235.6 2 pls + 589.9 237.7 590.4 237.7 2 pls + 589.9 241.9 590.4 241.9 2 pls + 589.9 244.0 590.4 244.0 2 pls + 589.3 250.4 590.4 250.4 2 pls + 589.9 246.1 590.4 246.1 2 pls + 589.9 248.3 590.4 248.3 2 pls + 589.9 252.5 590.4 252.5 2 pls + 589.9 254.6 590.4 254.6 2 pls + 589.3 260.9 590.4 260.9 2 pls + 589.9 256.7 590.4 256.7 2 pls + 589.9 258.8 590.4 258.8 2 pls + 589.9 263.0 590.4 263.0 2 pls + 589.9 265.2 590.4 265.2 2 pls + 589.3 271.5 590.4 271.5 2 pls + 589.9 267.3 590.4 267.3 2 pls + 589.9 269.4 590.4 269.4 2 pls + 589.9 273.6 590.4 273.6 2 pls + 589.9 275.7 590.4 275.7 2 pls + 589.3 282.0 590.4 282.0 2 pls + 589.9 277.8 590.4 277.8 2 pls + 589.9 279.9 590.4 279.9 2 pls + 589.9 284.2 590.4 284.2 2 pls + 589.9 286.3 590.4 286.3 2 pls + 589.3 292.6 590.4 292.6 2 pls + 589.9 288.4 590.4 288.4 2 pls + 589.9 290.5 590.4 290.5 2 pls + 589.9 294.7 590.4 294.7 2 pls + 589.9 296.8 590.4 296.8 2 pls + 589.3 303.2 590.4 303.2 2 pls + 589.9 298.9 590.4 298.9 2 pls + 589.9 301.1 590.4 301.1 2 pls + 589.9 305.3 590.4 305.3 2 pls + 589.9 307.4 590.4 307.4 2 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 ][ 234.4 234.4 234.4 + 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 234.4 239.7 245.0 + 250.3 255.6 260.9 266.2 271.5 276.8 282.1 287.4 292.7 298.0 303.3 308.6 ]plong + s[ 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 283.2 255.3 227.4 199.5 ][ 308.6 308.6 308.6 + 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 308.6 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 308.6 303.3 298.0 + 292.7 287.4 282.1 276.8 271.5 266.2 260.9 255.6 250.3 245.0 239.7 234.4 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 192.75 218.58 moveto[ 192.8 192.8 218.2 218.2 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 196.9 220.2 196.9 229.0 195.7 227.8 194.8 227.3 4 pls + s[ 204.0 202.8 202.4 202.4 202.8 203.6 205.3 206.5 207.4 207.8 207.8 207.4 207.0 205.7 204.0 202.8 202.4 201.9 + 201.9 202.4 203.2 204.4 206.1 207.0 207.4 207.4 207.0 205.7 204.0 ][ 229.0 228.6 227.8 226.9 226.1 225.7 225.3 + 224.8 224.0 223.2 221.9 221.1 220.7 220.2 220.2 220.7 221.1 221.9 223.2 224.0 224.8 225.3 225.7 226.1 226.9 + 227.8 228.6 229.0 229.0 ]plong + s[ 212.8 211.5 210.7 210.3 210.3 210.7 211.5 212.8 213.6 214.9 215.7 216.1 216.1 215.7 214.9 213.6 212.8 ][ 229.0 + 228.6 227.3 225.3 224.0 221.9 220.7 220.2 220.2 220.7 221.9 224.0 225.3 227.3 228.6 229.0 229.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 240.27 218.58 moveto[ 240.3 240.3 265.7 265.7 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 244.4 220.2 244.4 229.0 243.2 227.8 242.4 227.3 4 pls + s[ 250.3 254.9 252.4 253.6 254.5 254.9 255.3 255.3 254.9 254.0 252.8 251.5 250.3 249.9 249.5 ][ 229.0 229.0 225.7 + 225.7 225.3 224.8 223.6 222.8 221.5 220.7 220.2 220.2 220.7 221.1 221.9 ]plong + s[ 262.8 258.6 258.2 258.6 259.9 261.1 262.4 263.2 263.7 263.7 263.2 262.4 261.1 259.9 258.6 258.2 257.8 ][ 229.0 + 229.0 225.3 225.7 226.1 226.1 225.7 224.8 223.6 222.8 221.5 220.7 220.2 220.2 220.7 221.1 221.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 290.70 218.58 moveto[ 290.7 290.7 309.1 309.1 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 298.2 297.8 297.0 295.7 295.3 294.0 293.2 292.8 292.8 293.2 294.0 295.3 295.7 297.0 297.8 298.2 298.2 297.8 + 297.0 295.7 294.9 293.6 293.2 ][ 226.1 224.8 224.0 223.6 223.6 224.0 224.8 226.1 226.5 227.8 228.6 229.0 229.0 + 228.6 227.8 226.1 224.0 221.9 220.7 220.2 220.2 220.7 221.5 ]plong + s[ 303.7 302.4 301.6 301.1 301.1 301.6 302.4 303.7 304.5 305.7 306.6 307.0 307.0 306.6 305.7 304.5 303.7 ][ 229.0 + 228.6 227.3 225.3 224.0 221.9 220.7 220.2 220.2 220.7 221.9 224.0 225.3 227.3 228.6 229.0 229.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 338.22 218.58 moveto[ 338.2 338.2 356.6 356.6 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 346.6 223.2 340.3 223.2 344.5 229.0 3 pls + 344.5 220.2 344.5 229.0 2 pls + s[ 353.7 349.5 349.1 349.5 350.7 352.0 353.3 354.1 354.5 354.5 354.1 353.3 352.0 350.7 349.5 349.1 348.7 ][ 229.0 + 229.0 225.3 225.7 226.1 226.1 225.7 224.8 223.6 222.8 221.5 220.7 220.2 220.2 220.7 221.1 221.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 389.91 218.58 moveto[ 389.9 389.9 399.9 399.9 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 394.5 393.3 392.4 392.0 392.0 392.4 393.3 394.5 395.3 396.6 397.4 397.8 397.8 397.4 396.6 395.3 394.5 ][ 229.0 + 228.6 227.3 225.3 224.0 221.9 220.7 220.2 220.2 220.7 221.9 224.0 225.3 227.3 228.6 229.0 229.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 429.07 218.58 moveto[ 429.1 429.1 455.8 455.8 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 432.0 436.6 434.1 435.3 436.2 436.6 437.0 437.0 436.6 435.8 434.5 433.2 432.0 431.6 431.2 ][ 229.0 229.0 225.7 + 225.7 225.3 224.8 223.6 222.8 221.5 220.7 220.2 220.2 220.7 221.1 221.9 ]plong + 442.9 220.2 442.9 229.0 441.6 227.8 440.8 227.3 4 pls + s[ 452.9 448.7 448.3 448.7 450.0 451.2 452.5 453.3 453.7 453.7 453.3 452.5 451.2 450.0 448.7 448.3 447.9 ][ 229.0 + 229.0 225.3 225.7 226.1 226.1 225.7 224.8 223.6 222.8 221.5 220.7 220.2 220.2 220.7 221.1 221.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 476.59 218.58 moveto[ 476.6 476.6 503.3 503.3 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 479.1 479.1 479.5 479.9 480.8 482.4 483.3 483.7 484.1 484.1 483.7 482.9 478.7 484.5 ][ 226.9 227.3 228.2 228.6 + 229.0 229.0 228.6 228.2 227.3 226.5 225.7 224.4 220.2 220.2 ]plong + 488.7 220.2 492.9 229.0 2 pls + 492.9 229.0 487.0 229.0 2 pls + s[ 497.9 496.6 495.8 495.4 495.4 495.8 496.6 497.9 498.7 500.0 500.8 501.2 501.2 500.8 500.0 498.7 497.9 ][ 229.0 + 228.6 227.3 225.3 224.0 221.9 220.7 220.2 220.2 220.7 221.9 224.0 225.3 227.3 228.6 229.0 229.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 524.10 218.58 moveto[ 524.1 524.1 550.8 550.8 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 526.6 526.6 527.0 527.4 528.3 529.9 530.8 531.2 531.6 531.6 531.2 530.4 526.2 532.0 ][ 226.9 227.3 228.2 228.6 + 229.0 229.0 228.6 228.2 227.3 226.5 225.7 224.4 220.2 220.2 ]plong + s[ 535.0 535.0 535.4 535.8 536.6 538.3 539.1 539.6 540.0 540.0 539.6 538.7 534.5 540.4 ][ 226.9 227.3 228.2 228.6 + 229.0 229.0 228.6 228.2 227.3 226.5 225.7 224.4 220.2 220.2 ]plong + s[ 547.9 543.7 543.3 543.7 545.0 546.2 547.5 548.3 548.7 548.7 548.3 547.5 546.2 545.0 543.7 543.3 542.9 ][ 229.0 + 229.0 225.3 225.7 226.1 226.1 225.7 224.8 223.6 222.8 221.5 220.7 220.2 220.2 220.7 221.1 221.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 572.87 218.58 moveto[ 572.9 572.9 598.3 598.3 ][ 218.6 230.7 230.7 218.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 577.0 220.2 577.0 229.0 575.8 227.8 575.0 227.3 4 pls + s[ 584.1 582.9 582.5 582.5 582.9 583.7 585.4 586.7 587.5 587.9 587.9 587.5 587.1 585.8 584.1 582.9 582.5 582.1 + 582.1 582.5 583.3 584.6 586.2 587.1 587.5 587.5 587.1 585.8 584.1 ][ 229.0 228.6 227.8 226.9 226.1 225.7 225.3 + 224.8 224.0 223.2 221.9 221.1 220.7 220.2 220.2 220.7 221.1 221.9 223.2 224.0 224.8 225.3 225.7 226.1 226.9 + 227.8 228.6 229.0 229.0 ]plong + s[ 592.9 591.7 590.8 590.4 590.4 590.8 591.7 592.9 593.8 595.0 595.8 596.3 596.3 595.8 595.0 593.8 592.9 ][ 229.0 + 228.6 227.3 225.3 224.0 221.9 220.7 220.2 220.2 220.7 221.9 224.0 225.3 227.3 228.6 229.0 229.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.76 243.48 moveto[ 167.8 167.8 196.6 196.6 ][ 243.5 255.6 255.6 243.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.4 248.9 169.8 248.9 2 pls + s[ 180.7 180.7 181.1 181.5 182.4 184.0 184.9 185.3 185.7 185.7 185.3 184.5 180.3 186.1 ][ 251.8 252.2 253.1 253.5 + 253.9 253.9 253.5 253.1 252.2 251.4 250.6 249.3 245.1 245.1 ]plong + s[ 191.1 189.9 189.1 188.6 188.6 189.1 189.9 191.1 192.0 193.2 194.1 194.5 194.5 194.1 193.2 192.0 191.1 ][ 253.9 + 253.5 252.2 250.2 248.9 246.8 245.6 245.1 245.1 245.6 246.8 248.9 250.2 252.2 253.5 253.9 253.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.55 264.60 moveto[ 186.6 186.6 196.6 196.6 ][ 264.6 276.7 276.7 264.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 191.1 189.9 189.1 188.6 188.6 189.1 189.9 191.1 192.0 193.2 194.1 194.5 194.5 194.1 193.2 192.0 191.1 ][ 275.0 + 274.6 273.4 271.3 270.0 267.9 266.7 266.3 266.3 266.7 267.9 270.0 271.3 273.4 274.6 275.0 275.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.20 285.71 moveto[ 178.2 178.2 196.6 196.6 ][ 285.7 297.8 297.8 285.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 180.7 180.7 181.1 181.5 182.4 184.0 184.9 185.3 185.7 185.7 185.3 184.5 180.3 186.1 ][ 294.1 294.5 295.3 295.7 + 296.2 296.2 295.7 295.3 294.5 293.7 292.8 291.6 287.4 287.4 ]plong + s[ 191.1 189.9 189.1 188.6 188.6 189.1 189.9 191.1 192.0 193.2 194.1 194.5 194.5 194.1 193.2 192.0 191.1 ][ 296.2 + 295.7 294.5 292.4 291.1 289.1 287.8 287.4 287.4 287.8 289.1 291.1 292.4 294.5 295.7 296.2 296.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 335.61 202.38 moveto[ 335.6 335.6 454.2 454.2 ][ 202.4 217.8 217.8 202.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 344.0 343.5 342.7 341.9 340.2 339.4 338.5 338.1 337.7 337.7 338.1 338.5 339.4 340.2 341.9 342.7 343.5 344.0 + 344.0 ][ 213.7 214.5 215.3 215.7 215.7 215.3 214.5 213.7 212.4 210.3 209.1 208.2 207.4 207.0 207.0 207.4 208.2 + 209.1 210.3 ]plong + 344.0 210.3 341.9 210.3 2 pls + 351.5 207.0 351.5 212.8 2 pls + s[ 351.5 350.6 349.8 348.6 347.7 346.9 346.5 346.5 346.9 347.7 348.6 349.8 350.6 351.5 ][ 211.6 212.4 212.8 212.8 + 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 ]plong + 354.8 207.0 354.8 215.7 2 pls + 362.8 207.0 362.8 212.8 2 pls + s[ 362.8 361.9 361.1 359.8 359.0 358.2 357.7 357.7 358.2 359.0 359.8 361.1 361.9 362.8 ][ 211.6 212.4 212.8 212.8 + 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 ]plong + s[ 370.7 369.9 369.0 367.8 366.9 366.1 365.7 365.7 366.1 366.9 367.8 369.0 369.9 370.7 ][ 211.6 212.4 212.8 212.8 + 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 ]plong + 376.1 207.0 375.3 207.0 374.5 207.4 374.0 208.6 374.0 215.7 5 pls + 375.7 212.8 372.8 212.8 2 pls + 378.2 215.7 378.6 216.2 379.1 215.7 378.6 215.3 378.2 215.7 5 pls + 378.6 207.0 378.6 212.8 2 pls + s[ 386.6 385.7 384.9 383.6 382.8 382.0 381.6 381.6 382.0 382.8 383.6 384.9 385.7 386.6 ][ 211.6 212.4 212.8 212.8 + 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 ]plong + 396.2 207.0 396.2 215.7 2 pls + s[ 401.2 400.4 399.5 399.1 399.1 399.5 400.4 401.2 402.4 403.3 404.1 404.5 404.5 404.1 403.3 402.4 401.2 ][ 212.8 + 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 209.5 210.3 211.6 212.4 212.8 212.8 ]plong + 407.5 207.0 407.5 212.8 2 pls + s[ 407.5 408.7 409.5 410.8 411.6 412.0 412.0 ][ 211.2 212.4 212.8 212.8 212.4 211.2 207.0 ]plong + s[ 420.0 420.0 419.6 419.1 418.3 417.1 416.2 ][ 212.8 206.1 204.9 204.5 204.1 204.1 204.5 ]plong + s[ 420.0 419.1 418.3 417.1 416.2 415.4 415.0 415.0 415.4 416.2 417.1 418.3 419.1 420.0 ][ 211.6 212.4 212.8 212.8 + 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 ]plong + 422.9 215.7 423.3 216.2 423.7 215.7 423.3 215.3 422.9 215.7 5 pls + 423.3 207.0 423.3 212.8 2 pls + 429.2 207.0 428.3 207.0 427.5 207.4 427.1 208.6 427.1 215.7 5 pls + 428.8 212.8 425.8 212.8 2 pls + s[ 431.7 431.7 432.1 432.9 434.2 435.0 436.3 ][ 212.8 208.6 207.4 207.0 207.0 207.4 208.6 ]plong + 436.3 207.0 436.3 212.8 2 pls + 444.2 207.0 444.2 215.7 2 pls + s[ 444.2 443.4 442.5 441.3 440.4 439.6 439.2 439.2 439.6 440.4 441.3 442.5 443.4 444.2 ][ 211.6 212.4 212.8 212.8 + 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 ]plong + s[ 447.1 452.1 452.1 451.7 451.3 450.5 449.2 448.4 447.5 447.1 447.1 447.5 448.4 449.2 450.5 451.3 452.1 ][ 210.3 + 210.3 211.2 212.0 212.4 212.8 212.8 212.4 211.6 210.3 209.5 208.2 207.4 207.0 207.0 207.4 208.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 165.35 217.59 moveto[ 165.3 152.8 152.8 165.3 ][ 217.6 217.6 325.4 325.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 157.0 156.2 155.3 154.9 154.9 155.3 156.2 157.0 158.2 160.3 161.6 162.4 163.3 163.7 163.7 163.3 162.4 161.6 + 160.3 ][ 225.9 225.5 224.7 223.9 222.2 221.4 220.5 220.1 219.7 219.7 220.1 220.5 221.4 222.2 223.9 224.7 225.5 + 225.9 225.9 ]plong + 160.3 225.9 160.3 223.9 2 pls + 163.7 233.5 157.8 233.5 2 pls + s[ 159.1 158.2 157.8 157.8 158.2 159.1 160.3 161.2 162.4 163.3 163.7 163.7 163.3 162.4 ][ 233.5 232.6 231.8 230.5 + 229.7 228.9 228.5 228.5 228.9 229.7 230.5 231.8 232.6 233.5 ]plong + 163.7 236.8 154.9 236.8 2 pls + 163.7 244.7 157.8 244.7 2 pls + s[ 159.1 158.2 157.8 157.8 158.2 159.1 160.3 161.2 162.4 163.3 163.7 163.7 163.3 162.4 ][ 244.7 243.9 243.1 241.8 + 241.0 240.1 239.7 239.7 240.1 241.0 241.8 243.1 243.9 244.7 ]plong + s[ 159.1 158.2 157.8 157.8 158.2 159.1 160.3 161.2 162.4 163.3 163.7 163.7 163.3 162.4 ][ 252.7 251.8 251.0 249.8 + 248.9 248.1 247.7 247.7 248.1 248.9 249.8 251.0 251.8 252.7 ]plong + 163.7 258.1 163.7 257.3 163.3 256.4 162.0 256.0 154.9 256.0 5 pls + 157.8 257.7 157.8 254.8 2 pls + 154.9 260.2 154.5 260.6 154.9 261.0 155.3 260.6 154.9 260.2 5 pls + 163.7 260.6 157.8 260.6 2 pls + s[ 159.1 158.2 157.8 157.8 158.2 159.1 160.3 161.2 162.4 163.3 163.7 163.7 163.3 162.4 ][ 268.5 267.7 266.9 265.6 + 264.8 264.0 263.5 263.5 264.0 264.8 265.6 266.9 267.7 268.5 ]plong + 163.7 278.2 154.9 278.2 2 pls + 163.7 286.1 157.8 286.1 2 pls + s[ 159.1 158.2 157.8 157.8 158.2 159.1 160.3 161.2 162.4 163.3 163.7 163.7 163.3 162.4 ][ 286.1 285.3 284.4 283.2 + 282.3 281.5 281.1 281.1 281.5 282.3 283.2 284.4 285.3 286.1 ]plong + 163.7 291.9 163.7 291.1 163.3 290.3 162.0 289.8 154.9 289.8 5 pls + 157.8 291.5 157.8 288.6 2 pls + 154.9 294.0 154.5 294.4 154.9 294.9 155.3 294.4 154.9 294.0 5 pls + 163.7 294.4 157.8 294.4 2 pls + 163.7 300.3 163.7 299.5 163.3 298.6 162.0 298.2 154.9 298.2 5 pls + 157.8 299.9 157.8 296.9 2 pls + s[ 157.8 162.0 163.3 163.7 163.7 163.3 162.0 ][ 302.8 302.8 303.2 304.1 305.3 306.1 307.4 ]plong + 163.7 307.4 157.8 307.4 2 pls + 163.7 315.3 154.9 315.3 2 pls + s[ 159.1 158.2 157.8 157.8 158.2 159.1 160.3 161.2 162.4 163.3 163.7 163.7 163.3 162.4 ][ 315.3 314.5 313.7 312.4 + 311.6 310.7 310.3 310.3 310.7 311.6 312.4 313.7 314.5 315.3 ]plong + s[ 160.3 160.3 159.5 158.7 158.2 157.8 157.8 158.2 159.1 160.3 161.2 162.4 163.3 163.7 163.7 163.3 162.4 ][ 318.3 + 323.3 323.3 322.8 322.4 321.6 320.3 319.5 318.7 318.3 318.3 318.7 319.5 320.3 321.6 322.4 323.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 312.29 moveto[ 360.9 360.9 435.2 435.2 ][ 312.3 324.4 324.4 312.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 314.0 366.3 322.7 2 pls + 369.7 314.0 366.3 322.7 2 pls + 368.4 316.9 364.2 316.9 2 pls + 378.4 314.0 378.4 322.7 2 pls + 383.9 322.7 378.4 322.7 2 pls + 381.8 318.6 378.4 318.6 2 pls + 385.9 314.0 385.9 322.7 2 pls + 391.0 314.0 391.0 322.7 2 pls + 393.9 322.7 388.0 322.7 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 321.5 322.3 322.7 322.7 322.3 321.5 320.6 319.8 319.4 319.0 318.1 317.7 317.3 316.5 315.2 314.4 + 314.0 314.0 314.4 315.2 ]plong + 413.5 314.0 412.7 314.0 411.8 314.4 411.4 315.6 411.4 322.7 5 pls + 413.1 319.8 410.2 319.8 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 317.3 + 317.3 318.1 319.0 319.4 319.8 319.8 319.4 318.6 317.3 316.5 315.2 314.4 314.0 314.0 314.4 315.2 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 318.6 + 319.4 319.8 319.8 319.4 318.6 317.7 317.3 316.9 316.5 315.6 315.2 314.4 314.0 314.0 314.4 315.2 ]plong + 433.1 314.0 432.3 314.0 431.5 314.4 431.1 315.6 431.1 322.7 5 pls + 432.7 319.8 429.8 319.8 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/car2.attr b/ast_tester/car2.attr new file mode 100644 index 0000000..483f747 --- /dev/null +++ b/ast_tester/car2.attr @@ -0,0 +1 @@ +numlabgap(2)=0.05,labelling=interior diff --git a/ast_tester/car2.box b/ast_tester/car2.box new file mode 100644 index 0000000..09670e6 --- /dev/null +++ b/ast_tester/car2.box @@ -0,0 +1 @@ +0 0 2962 562 diff --git a/ast_tester/car2.fattr b/ast_tester/car2.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast_tester/car2.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast_tester/car2.head b/ast_tester/car2.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast_tester/car2.head @@ -0,0 +1,32 @@ +SIMPLE = T / Standard FITS format +BITPIX = 16 / Bits per pixel +NAXIS = 2 / Number of axes +NAXIS1 = 2961 / Number pixels on axis 1 +NAXIS2 = 561 / Number pixels on axis 2 +CTYPE1 = 'GLON-CAR' / axis 1 coord type +CRVAL1 = 1.850000e+02 / coord value at CRPIX1 +CDELT1 = -1.250000e-01 / pixel spacing for axis 1 +CRPIX1 = 1.000000 / ref pixel for axis 1 +CTYPE2 = 'GLAT-CAR' / axis 2 coord type +CRVAL2 = -3.500000e+01 / coord value at CRPIX2 +CDELT2 = 1.250000e-01 / pixel spacing for axis 2 +CRPIX2 = 1.000000 / ref pixel for axis 2 +BSCALE = 2.610167e-02 / real = int*bscale + bzero +BZERO = 8.321995e+02 / +DATAMIN = -6.326761e+00 / minimum real value +DATAMAX = 1.670731e+03 / maximum real value +BLANK = -32768 / missing data flag +COMMENT PARENT DISK FILE: Wco_DHT2001.fits +COMMENT temp =colscales( CHANGE-ME , -185.0000, 185.0000) +COMMENT coords=rowscales( temp, 35.0000, -35.0000) +COMMENT Written by MacFITS +COMMENT Created: Tuesday, 14 August, 2001 01:18:06 PM +COMMENT Whole-Galaxy velocity-integrated CO(1-0) map (Fig. 2) from +COMMENT "The Milky Way in Molecular Clouds: A New Complete CO Survey" +COMMENT T. M. Dame, Dap Hartmann, & P. Thaddeus (2001), ApJ, 547, 792. +COMMENT WARNING: Both the angular resolution and the sensitivity varies +COMMENT from region to region in this map: see Fig. 1 and Table 1 from +COMMENT the paper above. Moment masking and clipping were used as +COMMENT necessary to keep the noise in the map below ~1.5 K km/s. +COMMENT See Section 2.2 for details. +END diff --git a/ast_tester/car2.ps b/ast_tester/car2.ps new file mode 100644 index 0000000..3192ece --- /dev/null +++ b/ast_tester/car2.ps @@ -0,0 +1,557 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Feb 11 17:19:53 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: car2.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 20/03/2002 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 585.0 272.6 585.0 271.5 2 pls + 569.1 272.0 569.1 271.5 2 pls + 553.3 272.0 553.3 271.5 2 pls + 537.5 272.6 537.5 271.5 2 pls + 521.6 272.0 521.6 271.5 2 pls + 505.8 272.0 505.8 271.5 2 pls + 490.0 272.6 490.0 271.5 2 pls + 474.1 272.0 474.1 271.5 2 pls + 458.3 272.0 458.3 271.5 2 pls + 442.4 272.6 442.4 271.5 2 pls + 426.6 272.0 426.6 271.5 2 pls + 410.8 272.0 410.8 271.5 2 pls + 394.9 272.6 394.9 271.5 2 pls + 379.1 272.0 379.1 271.5 2 pls + 363.2 272.0 363.2 271.5 2 pls + 347.4 272.6 347.4 271.5 2 pls + 331.6 272.0 331.6 271.5 2 pls + 315.7 272.0 315.7 271.5 2 pls + 299.9 272.6 299.9 271.5 2 pls + 284.1 272.0 284.1 271.5 2 pls + 268.2 272.0 268.2 271.5 2 pls + 252.4 272.6 252.4 271.5 2 pls + 236.5 272.0 236.5 271.5 2 pls + 220.7 272.0 220.7 271.5 2 pls + 204.9 272.6 204.9 271.5 2 pls + 395.4 235.6 394.9 235.6 2 pls + 395.4 237.7 394.9 237.7 2 pls + 396.0 239.8 394.9 239.8 2 pls + 395.4 241.9 394.9 241.9 2 pls + 395.4 244.0 394.9 244.0 2 pls + 395.4 246.1 394.9 246.1 2 pls + 395.4 248.3 394.9 248.3 2 pls + 396.0 250.4 394.9 250.4 2 pls + 395.4 252.5 394.9 252.5 2 pls + 395.4 254.6 394.9 254.6 2 pls + 395.4 256.7 394.9 256.7 2 pls + 395.4 258.8 394.9 258.8 2 pls + 396.0 260.9 394.9 260.9 2 pls + 395.4 263.0 394.9 263.0 2 pls + 395.4 265.2 394.9 265.2 2 pls + 395.4 267.3 394.9 267.3 2 pls + 395.4 269.4 394.9 269.4 2 pls + 396.0 271.5 394.9 271.5 2 pls + 395.4 273.6 394.9 273.6 2 pls + 395.4 275.7 394.9 275.7 2 pls + 395.4 277.8 394.9 277.8 2 pls + 395.4 279.9 394.9 279.9 2 pls + 396.0 282.0 394.9 282.0 2 pls + 395.4 284.2 394.9 284.2 2 pls + 395.4 286.3 394.9 286.3 2 pls + 395.4 288.4 394.9 288.4 2 pls + 395.4 290.5 394.9 290.5 2 pls + 396.0 292.6 394.9 292.6 2 pls + 395.4 294.7 394.9 294.7 2 pls + 395.4 296.8 394.9 296.8 2 pls + 395.4 298.9 394.9 298.9 2 pls + 395.4 301.1 394.9 301.1 2 pls + 396.0 303.2 394.9 303.2 2 pls + 395.4 305.3 394.9 305.3 2 pls + 395.4 307.4 394.9 307.4 2 pls + 585.0 271.5 588.4 271.5 590.4 271.5 3 pls + s[ 585.0 581.6 578.2 574.8 571.4 568.0 564.6 561.2 557.8 554.4 551.0 547.6 544.3 540.9 537.5 ][ 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 537.5 534.1 530.7 527.3 523.9 520.5 517.1 513.7 510.3 506.9 503.5 500.1 496.7 493.3 490.0 ][ 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 490.0 486.6 483.2 479.8 476.4 473.0 469.6 466.2 462.8 459.4 456.0 452.6 449.2 445.8 442.4 ][ 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 442.4 439.0 435.7 432.3 428.9 425.5 422.1 418.7 415.3 411.9 408.5 405.1 401.7 398.3 398.1 397.8 397.6 397.3 + 397.1 396.9 396.6 396.4 396.1 395.9 395.7 395.4 395.2 ][ 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 394.7 394.4 394.2 394.0 393.7 393.5 393.2 393.0 392.7 392.5 392.3 392.0 391.8 391.5 388.1 384.7 381.3 378.0 + 374.6 371.2 367.8 364.4 361.0 357.6 354.2 350.8 347.4 ][ 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 347.4 344.0 340.6 337.2 333.8 330.4 327.0 323.7 320.3 316.9 313.5 310.1 306.7 303.3 299.9 ][ 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 299.9 296.5 293.1 289.7 286.3 282.9 279.5 276.1 272.7 269.3 266.0 262.6 259.2 255.8 252.4 ][ 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 252.4 249.0 245.6 242.2 238.8 235.4 232.0 228.6 225.2 221.8 218.4 215.0 211.7 208.3 204.9 ][ 271.5 271.5 271.5 + 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + 199.5 271.5 201.5 271.5 204.9 271.5 3 pls + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 234.4 234.5 235.3 236.0 236.8 237.5 238.3 239.1 239.8 +]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 239.8 240.6 241.3 + 242.1 242.8 243.6 244.3 245.1 245.8 246.6 247.4 248.1 248.9 249.6 250.4 ]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 250.4 251.1 251.9 + 252.6 253.4 254.1 254.9 255.6 256.4 257.2 257.9 258.7 259.4 260.2 260.9 ]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 + 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 260.9 261.7 262.4 263.2 263.9 264.7 265.5 266.2 267.0 + 267.7 268.5 269.2 270.0 270.7 270.8 270.8 270.9 270.9 271.0 271.1 271.1 271.2 271.2 271.3 271.3 271.4 271.4 ]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 + 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 271.5 271.6 271.6 271.7 271.8 271.8 271.9 271.9 272.0 + 272.0 272.1 272.1 272.2 272.2 273.0 273.8 274.5 275.3 276.0 276.8 277.5 278.3 279.0 279.8 280.5 281.3 282.0 ]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 282.0 282.8 283.6 + 284.3 285.1 285.8 286.6 287.3 288.1 288.8 289.6 290.3 291.1 291.9 292.6 ]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 292.6 293.4 294.1 + 294.9 295.6 296.4 297.1 297.9 298.6 299.4 300.1 300.9 301.7 302.4 303.2 ]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 303.2 303.9 304.7 305.4 306.2 306.9 307.7 308.4 308.6 +]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 572.87 258.63 moveto[ 572.9 572.9 598.3 598.3 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 577.0 260.3 577.0 269.1 575.8 267.8 575.0 267.4 4 pls + s[ 584.1 582.9 582.5 582.5 582.9 583.7 585.4 586.7 587.5 587.9 587.9 587.5 587.1 585.8 584.1 582.9 582.5 582.1 + 582.1 582.5 583.3 584.6 586.2 587.1 587.5 587.5 587.1 585.8 584.1 ][ 269.1 268.7 267.8 267.0 266.2 265.7 265.3 + 264.9 264.1 263.2 262.0 261.1 260.7 260.3 260.3 260.7 261.1 262.0 263.2 264.1 264.9 265.3 265.7 266.2 267.0 + 267.8 268.7 269.1 269.1 ]plong + s[ 592.9 591.7 590.8 590.4 590.4 590.8 591.7 592.9 593.8 595.0 595.8 596.3 596.3 595.8 595.0 593.8 592.9 ][ 269.1 + 268.7 267.4 265.3 264.1 262.0 260.7 260.3 260.3 260.7 262.0 264.1 265.3 267.4 268.7 269.1 269.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 524.10 258.63 moveto[ 524.1 524.1 550.8 550.8 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 526.6 526.6 527.0 527.4 528.3 529.9 530.8 531.2 531.6 531.6 531.2 530.4 526.2 532.0 ][ 267.0 267.4 268.2 268.7 + 269.1 269.1 268.7 268.2 267.4 266.6 265.7 264.5 260.3 260.3 ]plong + s[ 535.0 535.0 535.4 535.8 536.6 538.3 539.1 539.6 540.0 540.0 539.6 538.7 534.5 540.4 ][ 267.0 267.4 268.2 268.7 + 269.1 269.1 268.7 268.2 267.4 266.6 265.7 264.5 260.3 260.3 ]plong + s[ 547.9 543.7 543.3 543.7 545.0 546.2 547.5 548.3 548.7 548.7 548.3 547.5 546.2 545.0 543.7 543.3 542.9 ][ 269.1 + 269.1 265.3 265.7 266.2 266.2 265.7 264.9 263.6 262.8 261.6 260.7 260.3 260.3 260.7 261.1 262.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 476.59 258.63 moveto[ 476.6 476.6 503.3 503.3 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 479.1 479.1 479.5 479.9 480.8 482.4 483.3 483.7 484.1 484.1 483.7 482.9 478.7 484.5 ][ 267.0 267.4 268.2 268.7 + 269.1 269.1 268.7 268.2 267.4 266.6 265.7 264.5 260.3 260.3 ]plong + 488.7 260.3 492.9 269.1 2 pls + 492.9 269.1 487.0 269.1 2 pls + s[ 497.9 496.6 495.8 495.4 495.4 495.8 496.6 497.9 498.7 500.0 500.8 501.2 501.2 500.8 500.0 498.7 497.9 ][ 269.1 + 268.7 267.4 265.3 264.1 262.0 260.7 260.3 260.3 260.7 262.0 264.1 265.3 267.4 268.7 269.1 269.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 429.07 258.63 moveto[ 429.1 429.1 455.8 455.8 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 432.0 436.6 434.1 435.3 436.2 436.6 437.0 437.0 436.6 435.8 434.5 433.2 432.0 431.6 431.2 ][ 269.1 269.1 265.7 + 265.7 265.3 264.9 263.6 262.8 261.6 260.7 260.3 260.3 260.7 261.1 262.0 ]plong + 442.9 260.3 442.9 269.1 441.6 267.8 440.8 267.4 4 pls + s[ 452.9 448.7 448.3 448.7 450.0 451.2 452.5 453.3 453.7 453.7 453.3 452.5 451.2 450.0 448.7 448.3 447.9 ][ 269.1 + 269.1 265.3 265.7 266.2 266.2 265.7 264.9 263.6 262.8 261.6 260.7 260.3 260.3 260.7 261.1 262.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 389.91 258.63 moveto[ 389.9 389.9 399.9 399.9 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 394.5 393.3 392.4 392.0 392.0 392.4 393.3 394.5 395.3 396.6 397.4 397.8 397.8 397.4 396.6 395.3 394.5 ][ 269.1 + 268.7 267.4 265.3 264.1 262.0 260.7 260.3 260.3 260.7 262.0 264.1 265.3 267.4 268.7 269.1 269.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 338.22 258.63 moveto[ 338.2 338.2 356.6 356.6 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 346.6 263.2 340.3 263.2 344.5 269.1 3 pls + 344.5 260.3 344.5 269.1 2 pls + s[ 353.7 349.5 349.1 349.5 350.7 352.0 353.3 354.1 354.5 354.5 354.1 353.3 352.0 350.7 349.5 349.1 348.7 ][ 269.1 + 269.1 265.3 265.7 266.2 266.2 265.7 264.9 263.6 262.8 261.6 260.7 260.3 260.3 260.7 261.1 262.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 290.70 258.63 moveto[ 290.7 290.7 309.1 309.1 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 298.2 297.8 297.0 295.7 295.3 294.0 293.2 292.8 292.8 293.2 294.0 295.3 295.7 297.0 297.8 298.2 298.2 297.8 + 297.0 295.7 294.9 293.6 293.2 ][ 266.2 264.9 264.1 263.6 263.6 264.1 264.9 266.2 266.6 267.8 268.7 269.1 269.1 + 268.7 267.8 266.2 264.1 262.0 260.7 260.3 260.3 260.7 261.6 ]plong + s[ 303.7 302.4 301.6 301.1 301.1 301.6 302.4 303.7 304.5 305.7 306.6 307.0 307.0 306.6 305.7 304.5 303.7 ][ 269.1 + 268.7 267.4 265.3 264.1 262.0 260.7 260.3 260.3 260.7 262.0 264.1 265.3 267.4 268.7 269.1 269.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 240.27 258.63 moveto[ 240.3 240.3 265.7 265.7 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 244.4 260.3 244.4 269.1 243.2 267.8 242.4 267.4 4 pls + s[ 250.3 254.9 252.4 253.6 254.5 254.9 255.3 255.3 254.9 254.0 252.8 251.5 250.3 249.9 249.5 ][ 269.1 269.1 265.7 + 265.7 265.3 264.9 263.6 262.8 261.6 260.7 260.3 260.3 260.7 261.1 262.0 ]plong + s[ 262.8 258.6 258.2 258.6 259.9 261.1 262.4 263.2 263.7 263.7 263.2 262.4 261.1 259.9 258.6 258.2 257.8 ][ 269.1 + 269.1 265.3 265.7 266.2 266.2 265.7 264.9 263.6 262.8 261.6 260.7 260.3 260.3 260.7 261.1 262.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 192.75 258.63 moveto[ 192.8 192.8 218.2 218.2 ][ 258.6 270.7 270.7 258.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 196.9 260.3 196.9 269.1 195.7 267.8 194.8 267.4 4 pls + s[ 204.0 202.8 202.4 202.4 202.8 203.6 205.3 206.5 207.4 207.8 207.8 207.4 207.0 205.7 204.0 202.8 202.4 201.9 + 201.9 202.4 203.2 204.4 206.1 207.0 207.4 207.4 207.0 205.7 204.0 ][ 269.1 268.7 267.8 267.0 266.2 265.7 265.3 + 264.9 264.1 263.2 262.0 261.1 260.7 260.3 260.3 260.7 261.1 262.0 263.2 264.1 264.9 265.3 265.7 266.2 267.0 + 267.8 268.7 269.1 269.1 ]plong + s[ 212.8 211.5 210.7 210.3 210.3 210.7 211.5 212.8 213.6 214.9 215.7 216.1 216.1 215.7 214.9 213.6 212.8 ][ 269.1 + 268.7 267.4 265.3 264.1 262.0 260.7 260.3 260.3 260.7 262.0 264.1 265.3 267.4 268.7 269.1 269.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 392.88 225.61 moveto[ 392.9 380.8 380.8 392.9 ][ 225.6 225.6 254.4 254.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 387.5 235.2 387.5 227.7 2 pls + s[ 382.4 382.4 385.8 385.8 386.2 386.6 387.9 388.7 390.0 390.8 391.2 391.2 390.8 390.4 389.5 ][ 239.0 243.6 241.1 + 242.3 243.1 243.6 244.0 244.0 243.6 242.7 241.5 240.2 239.0 238.6 238.1 ]plong + s[ 382.4 382.9 384.1 386.2 387.5 389.5 390.8 391.2 391.2 390.8 389.5 387.5 386.2 384.1 382.9 382.4 382.4 ][ 249.0 + 247.7 246.9 246.5 246.5 246.9 247.7 249.0 249.8 251.1 251.9 252.3 252.3 251.9 251.1 249.8 249.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 392.88 274.11 moveto[ 392.9 380.8 380.8 392.9 ][ 274.1 274.1 291.2 291.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 391.2 278.3 382.4 278.3 383.7 277.0 384.1 276.2 4 pls + s[ 382.4 382.9 384.1 386.2 387.5 389.5 390.8 391.2 391.2 390.8 389.5 387.5 386.2 384.1 382.9 382.4 382.4 ][ 285.8 + 284.6 283.7 283.3 283.3 283.7 284.6 285.8 286.6 287.9 288.7 289.1 289.1 288.7 287.9 286.6 285.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 392.88 293.97 moveto[ 392.9 380.8 380.8 392.9 ][ 294.0 294.0 312.4 312.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 382.4 382.4 385.8 385.8 386.2 386.6 387.9 388.7 390.0 390.8 391.2 391.2 390.8 390.4 389.5 ][ 296.9 301.5 299.0 + 300.2 301.1 301.5 301.9 301.9 301.5 300.7 299.4 298.2 296.9 296.5 296.1 ]plong + s[ 382.4 382.9 384.1 386.2 387.5 389.5 390.8 391.2 391.2 390.8 389.5 387.5 386.2 384.1 382.9 382.4 382.4 ][ 306.9 + 305.7 304.8 304.4 304.4 304.8 305.7 306.9 307.8 309.0 309.8 310.3 310.3 309.8 309.0 307.8 306.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 314.60 moveto[ 360.9 360.9 435.2 435.2 ][ 314.6 326.7 326.7 314.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 316.3 366.3 325.0 2 pls + 369.7 316.3 366.3 325.0 2 pls + 368.4 319.2 364.2 319.2 2 pls + 378.4 316.3 378.4 325.0 2 pls + 383.9 325.0 378.4 325.0 2 pls + 381.8 320.9 378.4 320.9 2 pls + 385.9 316.3 385.9 325.0 2 pls + 391.0 316.3 391.0 325.0 2 pls + 393.9 325.0 388.0 325.0 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 323.8 324.6 325.0 325.0 324.6 323.8 323.0 322.1 321.7 321.3 320.5 320.0 319.6 318.8 317.5 316.7 + 316.3 316.3 316.7 317.5 ]plong + 413.5 316.3 412.7 316.3 411.8 316.7 411.4 317.9 411.4 325.0 5 pls + 413.1 322.1 410.2 322.1 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 319.6 + 319.6 320.5 321.3 321.7 322.1 322.1 321.7 320.9 319.6 318.8 317.5 316.7 316.3 316.3 316.7 317.5 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 320.9 + 321.7 322.1 322.1 321.7 320.9 320.0 319.6 319.2 318.8 317.9 317.5 316.7 316.3 316.3 316.7 317.5 ]plong + 433.1 316.3 432.3 316.3 431.5 316.7 431.1 317.9 431.1 325.0 5 pls + 432.7 322.1 429.8 322.1 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/car3.attr b/ast_tester/car3.attr new file mode 100644 index 0000000..b80a7c0 --- /dev/null +++ b/ast_tester/car3.attr @@ -0,0 +1 @@ +grid=1 diff --git a/ast_tester/car3.box b/ast_tester/car3.box new file mode 100644 index 0000000..54240c7 --- /dev/null +++ b/ast_tester/car3.box @@ -0,0 +1 @@ +0 0 400 400 diff --git a/ast_tester/car3.head b/ast_tester/car3.head new file mode 100644 index 0000000..de6c76e --- /dev/null +++ b/ast_tester/car3.head @@ -0,0 +1,8 @@ +CTYPE1 = 'GLON-CAR' +CRVAL1 = 1.850000e+02 +CDELT1 = -1.250000e-01 +CRPIX1 = 200.000000 +CTYPE2 = 'GLAT-CAR' +CRVAL2 = -3.500000e+01 +CDELT2 = 1.250000e-01 +CRPIX2 = 200.000000 diff --git a/ast_tester/car3.ps b/ast_tester/car3.ps new file mode 100644 index 0000000..1e6d6aa --- /dev/null +++ b/ast_tester/car3.ps @@ -0,0 +1,839 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Thu May 17 12:11:03 2007 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 17/05/1907 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 416.5 418.8 420.9 423.0 425.0 426.9 428.9 430.9 432.9 435.1 437.4 437.4 ][ 76.0 114.5 153.6 192.6 231.6 270.7 + 309.7 348.8 387.8 426.9 465.9 467.0 ]plong + s[ 373.3 371.1 368.9 366.9 364.9 362.9 361.0 359.0 356.9 354.8 352.5 352.4 ][ 76.0 114.5 153.6 192.6 231.6 270.7 + 309.7 348.8 387.8 426.9 465.9 467.0 ]plong + s[ 329.5 323.5 317.1 311.0 305.0 299.2 293.4 287.5 281.5 275.2 268.5 266.7 ][ 76.0 109.8 148.4 187.0 225.7 264.3 + 303.0 341.6 380.3 418.9 457.5 467.0 ]plong + s[ 283.9 276.5 265.8 255.7 245.9 236.4 226.9 217.4 207.6 199.5 ][ 76.0 100.3 138.1 176.0 213.8 251.8 289.7 327.6 + 365.5 396.0 ]plong + 199.5 165.2 201.5 159.7 215.5 123.0 230.3 86.4 234.9 76.0 5 pls + 590.4 165.2 588.4 159.7 574.4 123.0 559.5 86.4 555.0 76.0 5 pls + s[ 506.0 513.3 524.0 534.1 543.9 553.4 562.9 572.5 582.2 590.4 ][ 76.0 100.3 138.1 176.0 213.8 251.8 289.7 327.6 + 365.5 396.0 ]plong + s[ 460.3 466.3 472.7 478.9 484.8 490.6 496.5 502.3 508.4 514.7 521.4 523.2 ][ 76.0 109.8 148.4 187.0 225.7 264.3 + 303.0 341.6 380.3 418.9 457.5 467.0 ]plong + s[ 416.5 418.8 420.9 423.0 425.0 426.9 428.9 430.9 432.9 435.1 437.4 437.4 ][ 76.0 114.5 153.6 192.6 231.6 270.7 + 309.7 348.8 387.8 426.9 465.9 467.0 ]plong + 394.9 76.0 394.9 76.0 394.9 76.0 3 pls + s[ 466.3 439.2 412.0 384.7 357.5 330.3 303.3 276.5 250.0 223.8 203.5 ][ 109.8 113.1 114.8 115.0 113.6 110.7 106.3 + 100.3 92.9 84.0 76.0 ]plong + s[ 586.4 582.2 559.5 536.5 513.3 489.9 466.3 442.6 418.8 394.9 371.1 ][ 76.0 77.8 86.4 93.9 100.3 105.6 109.8 + 112.7 114.5 115.1 114.5 ]plong + s[ 472.7 443.2 413.5 383.8 354.1 324.5 295.0 265.8 236.9 208.4 199.5 ][ 148.4 152.0 153.9 154.1 152.6 149.4 144.6 + 138.1 130.0 120.4 116.9 ]plong + s[ 590.4 574.4 549.3 524.0 498.5 472.7 446.9 420.9 394.9 368.9 ][ 116.9 123.0 131.1 138.1 143.9 148.4 151.6 153.6 + 154.2 153.6 ]plong + s[ 478.9 447.0 415.0 382.9 350.8 318.9 287.2 255.7 224.6 199.5 ][ 187.0 190.9 192.9 193.2 191.6 188.1 182.9 176.0 + 167.3 158.8 ]plong + s[ 590.4 588.4 561.4 534.1 506.6 478.9 451.0 423.0 394.9 366.9 ][ 158.9 159.7 168.4 176.0 182.1 187.0 190.5 192.6 + 193.3 192.6 ]plong + s[ 484.8 450.7 416.4 382.0 347.7 313.5 279.6 245.9 212.7 199.5 ][ 225.7 229.8 232.0 232.3 230.5 226.9 221.3 213.8 + 204.6 200.2 ]plong + s[ 590.4 573.0 543.9 514.5 484.8 455.0 425.0 394.9 364.9 ][ 200.2 205.8 213.8 220.5 225.7 229.4 231.6 232.4 231.6 +]plong + s[ 490.6 454.3 417.8 381.2 344.7 308.3 272.1 236.4 201.1 199.5 ][ 264.3 268.7 271.1 271.3 269.5 265.6 259.7 251.8 + 241.9 241.4 ]plong + s[ 590.4 584.4 553.4 522.2 490.6 458.9 426.9 394.9 362.9 ][ 241.3 243.3 251.8 258.8 264.3 268.3 270.7 271.5 270.7 +]plong + s[ 496.5 457.9 419.2 380.4 341.6 303.0 264.7 226.9 199.5 ][ 303.0 307.7 310.1 310.4 308.5 304.3 298.1 289.7 282.1 +]plong + s[ 590.4 562.9 529.9 496.5 462.8 428.9 394.9 361.0 ][ 282.1 289.7 297.1 303.0 307.2 309.7 310.6 309.7 ]plong + s[ 502.3 461.6 420.6 379.5 338.5 297.7 257.2 217.4 199.5 ][ 341.6 346.6 349.2 349.5 347.5 343.1 336.4 327.6 322.6 +]plong + s[ 590.4 572.5 537.6 502.3 466.7 430.9 394.9 359.0 ][ 322.7 327.6 335.5 341.6 346.1 348.8 349.7 348.8 ]plong + s[ 508.4 465.4 422.1 378.6 335.3 292.2 249.6 207.6 199.5 ][ 380.3 385.5 388.3 388.6 386.4 381.8 374.8 365.5 363.2 +]plong + s[ 590.4 582.2 545.6 508.4 470.8 432.9 394.9 356.9 ][ 363.3 365.5 373.8 380.3 385.0 387.8 388.8 387.8 ]plong + s[ 514.7 469.4 423.6 377.7 331.9 286.4 241.6 199.5 ][ 418.9 424.4 427.4 427.7 425.4 420.5 413.1 403.8 ]plong + s[ 590.4 553.8 514.7 475.1 435.1 394.9 354.8 ][ 403.8 412.0 418.9 423.9 426.9 427.9 426.9 ]plong + s[ 521.4 518.0 514.6 511.2 507.8 504.4 501.0 497.6 494.1 490.7 487.3 483.9 480.4 477.0 473.6 470.1 466.7 463.3 + 459.8 456.4 452.9 449.5 446.0 442.6 439.1 435.6 432.2 428.7 425.3 421.8 418.3 414.9 411.4 407.9 404.5 401.0 + 397.5 394.1 390.6 387.1 383.7 380.2 376.7 373.3 369.8 366.3 362.9 359.4 355.9 352.5 349.0 345.6 342.1 338.7 + 335.2 331.8 328.3 280.3 233.1 199.5 ][ 457.5 458.0 458.5 459.0 459.4 459.9 460.3 460.7 461.1 461.5 461.9 462.3 + 462.6 463.0 463.3 463.6 463.9 464.2 464.5 464.7 465.0 465.2 465.4 465.6 465.8 466.0 466.1 466.3 466.4 466.5 + 466.6 466.7 466.8 466.9 466.9 466.9 467.0 467.0 466.9 466.9 466.9 466.8 466.8 466.7 466.6 466.5 466.4 466.2 + 466.1 465.9 465.7 465.5 465.3 465.1 464.9 464.6 464.3 459.2 451.4 443.9 ]plong + s[ 590.4 562.6 521.4 479.6 437.4 394.9 352.5 ][ 444.0 450.3 457.5 462.7 465.9 467.0 465.9 ]plong + 408.0 78.8 407.9 76.0 2 pls + 399.3 78.8 399.2 76.0 2 pls + 390.6 78.8 390.6 76.0 2 pls + 381.9 78.8 382.0 76.0 2 pls + 364.4 78.7 364.7 76.0 2 pls + 355.7 78.7 355.9 76.0 2 pls + 346.8 78.7 347.2 76.0 2 pls + 338.0 78.7 338.4 76.0 2 pls + 320.0 78.7 320.6 76.0 2 pls + 310.9 78.7 311.5 76.0 2 pls + 301.7 78.7 302.4 76.0 2 pls + 292.4 78.7 293.1 76.0 2 pls + 273.4 78.6 274.3 76.0 2 pls + 263.7 78.6 264.6 76.0 2 pls + 253.9 78.6 254.8 76.0 2 pls + 243.9 78.6 244.9 76.0 2 pls + 223.3 78.5 224.4 76.0 2 pls + 212.7 78.5 213.8 76.0 2 pls + 201.9 78.5 203.1 76.0 2 pls + 588.0 78.5 586.8 76.0 2 pls + 577.2 78.5 576.0 76.0 2 pls + 566.6 78.5 565.5 76.0 2 pls + 546.0 78.6 545.0 76.0 2 pls + 536.0 78.6 535.0 76.0 2 pls + 526.1 78.6 525.2 76.0 2 pls + 516.4 78.6 515.6 76.0 2 pls + 497.4 78.7 496.7 76.0 2 pls + 488.1 78.7 487.5 76.0 2 pls + 478.9 78.7 478.3 76.0 2 pls + 469.8 78.7 469.3 76.0 2 pls + 451.9 78.7 451.5 76.0 2 pls + 443.0 78.7 442.7 76.0 2 pls + 434.2 78.7 433.9 76.0 2 pls + 425.4 78.7 425.2 76.0 2 pls + 202.0 84.1 199.5 83.0 2 pls + 202.0 92.7 199.5 91.6 2 pls + 202.0 101.2 199.5 100.1 2 pls + 202.0 109.6 199.5 108.6 2 pls + 202.0 126.5 199.5 125.5 2 pls + 202.0 134.9 199.5 133.9 2 pls + 202.0 143.2 199.5 142.3 2 pls + 202.0 151.6 199.5 150.6 2 pls + 202.0 168.2 199.5 167.3 2 pls + 202.0 176.5 199.5 175.6 2 pls + 202.0 184.8 199.5 183.9 2 pls + 202.0 193.0 199.5 192.1 2 pls + 202.0 209.5 199.5 208.6 2 pls + 202.0 217.7 199.5 216.8 2 pls + 202.1 225.9 199.5 225.0 2 pls + 202.1 234.1 199.5 233.2 2 pls + 202.1 250.4 199.5 249.6 2 pls + 202.1 258.6 199.5 257.8 2 pls + 202.1 266.8 199.5 266.0 2 pls + 202.1 274.9 199.5 274.1 2 pls + 202.1 291.2 199.5 290.4 2 pls + 202.1 299.3 199.5 298.5 2 pls + 202.1 307.4 199.5 306.7 2 pls + 202.1 315.5 199.5 314.8 2 pls + 202.1 331.8 199.5 331.0 2 pls + 202.1 339.9 199.5 339.1 2 pls + 202.1 348.0 199.5 347.2 2 pls + 202.1 356.0 199.5 355.3 2 pls + 202.1 372.2 199.5 371.5 2 pls + 202.1 380.3 199.5 379.6 2 pls + 202.1 388.4 199.5 387.7 2 pls + 202.1 396.4 199.5 395.8 2 pls + 202.1 412.6 199.5 411.9 2 pls + 202.1 420.7 199.5 420.0 2 pls + 202.1 428.7 199.5 428.1 2 pls + 202.1 436.8 199.5 436.1 2 pls + 202.1 452.9 199.5 452.3 2 pls + 202.1 461.0 199.5 460.3 2 pls + 420.3 464.2 420.4 467.0 2 pls + 403.4 464.2 403.4 467.0 2 pls + 386.5 464.2 386.4 467.0 2 pls + 369.5 464.2 369.4 467.0 2 pls + 335.6 464.2 335.4 467.0 2 pls + 318.6 464.2 318.3 467.0 2 pls + 301.5 464.2 301.2 467.0 2 pls + 284.4 464.3 284.0 467.0 2 pls + 250.0 464.3 249.4 467.0 2 pls + 232.6 464.3 232.0 467.0 2 pls + 215.2 464.3 214.5 467.0 2 pls + 574.7 464.3 575.4 467.0 2 pls + 557.2 464.3 557.8 467.0 2 pls + 539.9 464.3 540.4 467.0 2 pls + 505.4 464.3 505.9 467.0 2 pls + 488.3 464.2 488.7 467.0 2 pls + 471.2 464.2 471.5 467.0 2 pls + 454.2 464.2 454.5 467.0 2 pls + 587.9 84.1 590.4 83.0 2 pls + 587.8 92.6 590.4 91.6 2 pls + 587.8 101.1 590.4 100.1 2 pls + 587.8 109.6 590.4 108.6 2 pls + 587.8 126.4 590.4 125.5 2 pls + 587.8 134.8 590.4 133.9 2 pls + 587.8 143.2 590.4 142.3 2 pls + 587.8 151.5 590.4 150.6 2 pls + 587.8 168.2 590.4 167.3 2 pls + 587.8 176.4 590.4 175.6 2 pls + 587.8 184.7 590.4 183.9 2 pls + 587.8 193.0 590.4 192.1 2 pls + 587.8 209.4 590.4 208.6 2 pls + 587.8 217.6 590.4 216.8 2 pls + 587.8 225.8 590.4 225.0 2 pls + 587.8 234.0 590.4 233.2 2 pls + 587.8 250.4 590.4 249.6 2 pls + 587.8 258.6 590.4 257.8 2 pls + 587.8 266.7 590.4 266.0 2 pls + 587.8 274.9 590.4 274.1 2 pls + 587.8 291.1 590.4 290.4 2 pls + 587.8 299.3 590.4 298.5 2 pls + 587.8 307.4 590.4 306.7 2 pls + 587.8 315.5 590.4 314.8 2 pls + 587.7 331.7 590.4 331.0 2 pls + 587.7 339.8 590.4 339.1 2 pls + 587.7 347.9 590.4 347.2 2 pls + 587.7 356.0 590.4 355.3 2 pls + 587.7 372.2 590.4 371.5 2 pls + 587.7 380.2 590.4 379.6 2 pls + 587.7 388.3 590.4 387.7 2 pls + 587.7 396.4 590.4 395.8 2 pls + 587.7 412.5 590.4 411.9 2 pls + 587.7 420.6 590.4 420.0 2 pls + 587.7 428.7 590.4 428.1 2 pls + 587.7 436.7 590.4 436.1 2 pls + 587.7 452.9 590.4 452.3 2 pls + 587.7 460.9 590.4 460.3 2 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 283.2 255.3 227.4 199.5 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 221.50 59.99 moveto[ 221.5 221.5 248.2 248.2 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 224.0 224.0 224.4 224.8 225.7 227.3 228.2 228.6 229.0 229.0 228.6 227.8 223.6 229.4 ][ 68.3 68.8 69.6 70.0 + 70.4 70.4 70.0 69.6 68.8 67.9 67.1 65.8 61.7 61.7 ]plong + s[ 232.4 232.4 232.8 233.2 234.0 235.7 236.5 237.0 237.4 237.4 237.0 236.1 231.9 237.8 ][ 68.3 68.8 69.6 70.0 + 70.4 70.4 70.0 69.6 68.8 67.9 67.1 65.8 61.7 61.7 ]plong + s[ 242.8 241.5 240.7 240.3 240.3 240.7 241.5 242.8 243.6 244.9 245.7 246.1 246.1 245.7 244.9 243.6 242.8 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.52 59.99 moveto[ 270.5 270.5 297.2 297.2 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 273.0 273.0 273.4 273.9 274.7 276.4 277.2 277.6 278.0 278.0 277.6 276.8 272.6 278.5 ][ 68.3 68.8 69.6 70.0 + 70.4 70.4 70.0 69.6 68.8 67.9 67.1 65.8 61.7 61.7 ]plong + 284.3 61.7 284.3 70.4 283.0 69.2 282.2 68.8 4 pls + s[ 291.8 290.6 289.7 289.3 289.3 289.7 290.6 291.8 292.7 293.9 294.7 295.2 295.2 294.7 293.9 292.7 291.8 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 316.18 59.99 moveto[ 316.2 316.2 342.9 342.9 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 318.7 318.7 319.1 319.5 320.4 322.0 322.9 323.3 323.7 323.7 323.3 322.4 318.3 324.1 ][ 68.3 68.8 69.6 70.0 + 70.4 70.4 70.0 69.6 68.8 67.9 67.1 65.8 61.7 61.7 ]plong + s[ 329.1 327.9 327.0 326.6 326.6 327.0 327.9 329.1 330.0 331.2 332.0 332.5 332.5 332.0 331.2 330.0 329.1 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong + s[ 337.5 336.2 335.4 335.0 335.0 335.4 336.2 337.5 338.3 339.6 340.4 340.8 340.8 340.4 339.6 338.3 337.5 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 361.22 59.99 moveto[ 361.2 361.2 386.7 386.7 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 365.4 61.7 365.4 70.4 364.1 69.2 363.3 68.8 4 pls + s[ 375.8 375.4 374.6 373.3 372.9 371.7 370.8 370.4 370.4 370.8 371.7 372.9 373.3 374.6 375.4 375.8 375.8 375.4 + 374.6 373.3 372.5 371.2 370.8 ][ 67.5 66.3 65.4 65.0 65.0 65.4 66.3 67.5 67.9 69.2 70.0 70.4 70.4 + 70.0 69.2 67.5 65.4 63.3 62.1 61.7 61.7 62.1 62.9 ]plong + s[ 381.3 380.0 379.2 378.8 378.8 379.2 380.0 381.3 382.1 383.4 384.2 384.6 384.6 384.2 383.4 382.1 381.3 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 404.40 59.99 moveto[ 404.4 404.4 429.9 429.9 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 408.6 61.7 408.6 70.4 407.3 69.2 406.5 68.8 4 pls + s[ 415.7 414.4 414.0 414.0 414.4 415.3 416.9 418.2 419.0 419.4 419.4 419.0 418.6 417.4 415.7 414.4 414.0 413.6 + 413.6 414.0 414.8 416.1 417.8 418.6 419.0 419.0 418.6 417.4 415.7 ][ 70.4 70.0 69.2 68.3 67.5 67.1 66.7 + 66.3 65.4 64.6 63.3 62.5 62.1 61.7 61.7 62.1 62.5 63.3 64.6 65.4 66.3 66.7 67.1 67.5 68.3 + 69.2 70.0 70.4 70.4 ]plong + s[ 424.5 423.2 422.4 421.9 421.9 422.4 423.2 424.5 425.3 426.5 427.4 427.8 427.8 427.4 426.5 425.3 424.5 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 448.19 59.99 moveto[ 448.2 448.2 473.7 473.7 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 452.4 61.7 452.4 70.4 451.1 69.2 450.3 68.8 4 pls + 459.1 61.7 463.2 70.4 2 pls + 463.2 70.4 457.4 70.4 2 pls + s[ 468.2 467.0 466.2 465.7 465.7 466.2 467.0 468.2 469.1 470.3 471.2 471.6 471.6 471.2 470.3 469.1 468.2 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 493.85 59.99 moveto[ 493.9 493.9 519.3 519.3 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 498.0 61.7 498.0 70.4 496.8 69.2 495.9 68.8 4 pls + s[ 508.5 508.1 506.8 506.0 504.7 503.9 503.5 503.5 503.9 504.7 506.0 506.4 507.6 508.5 508.9 508.9 508.5 507.6 + 506.4 506.0 504.7 503.9 503.5 ][ 69.2 70.0 70.4 70.4 70.0 68.8 66.7 64.6 62.9 62.1 61.7 61.7 62.1 + 62.9 64.2 64.6 65.8 66.7 67.1 67.1 66.7 65.8 64.6 ]plong + s[ 513.9 512.6 511.8 511.4 511.4 511.8 512.6 513.9 514.7 516.0 516.8 517.2 517.2 516.8 516.0 514.7 513.9 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 542.87 59.99 moveto[ 542.9 542.9 568.3 568.3 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 547.0 61.7 547.0 70.4 545.8 69.2 545.0 68.8 4 pls + s[ 557.1 552.9 552.5 552.9 554.1 555.4 556.7 557.5 557.9 557.9 557.5 556.7 555.4 554.1 552.9 552.5 552.1 ][ 70.4 + 70.4 66.7 67.1 67.5 67.5 67.1 66.3 65.0 64.2 62.9 62.1 61.7 61.7 62.1 62.5 63.3 ]plong + s[ 562.9 561.7 560.8 560.4 560.4 560.8 561.7 562.9 563.8 565.0 565.8 566.3 566.3 565.8 565.0 563.8 562.9 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 110.01 moveto[ 167.6 167.6 196.4 196.4 ][ 110.0 122.1 122.1 110.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 115.4 169.6 115.4 2 pls + s[ 185.1 180.9 180.5 180.9 182.2 183.4 184.7 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 120.5 + 120.5 116.7 117.1 117.5 117.5 117.1 116.3 115.0 114.2 112.9 112.1 111.7 111.7 112.1 112.5 113.4 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 120.5 + 120.0 118.8 116.7 115.4 113.4 112.1 111.7 111.7 112.1 113.4 115.4 116.7 118.8 120.0 120.5 120.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 151.95 moveto[ 167.6 167.6 196.4 196.4 ][ 152.0 164.1 164.1 152.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 157.4 169.6 157.4 2 pls + 186.4 156.5 180.1 156.5 184.3 162.4 3 pls + 184.3 153.6 184.3 162.4 2 pls + s[ 193.5 189.3 188.9 189.3 190.5 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.5 189.3 188.9 188.4 ][ 162.4 + 162.4 158.6 159.1 159.5 159.5 159.1 158.2 157.0 156.1 154.9 154.0 153.6 153.6 154.0 154.5 155.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 193.26 moveto[ 167.6 167.6 196.4 196.4 ][ 193.3 205.4 205.4 193.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 198.7 169.6 198.7 2 pls + 186.4 197.9 180.1 197.9 184.3 203.7 3 pls + 184.3 194.9 184.3 203.7 2 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 203.7 + 203.3 202.0 199.9 198.7 196.6 195.3 194.9 194.9 195.3 196.6 198.7 199.9 202.0 203.3 203.7 203.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 234.50 moveto[ 167.6 167.6 196.4 196.4 ][ 234.5 246.6 246.6 234.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 239.9 169.6 239.9 2 pls + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 244.9 244.9 241.6 + 241.6 241.2 240.8 239.5 238.7 237.4 236.6 236.2 236.2 236.6 237.0 237.8 ]plong + s[ 193.5 189.3 188.9 189.3 190.5 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.5 189.3 188.9 188.4 ][ 244.9 + 244.9 241.2 241.6 242.0 242.0 241.6 240.8 239.5 238.7 237.4 236.6 236.2 236.2 236.6 237.0 237.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 275.16 moveto[ 167.6 167.6 196.4 196.4 ][ 275.2 287.3 287.3 275.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 280.6 169.6 280.6 2 pls + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 285.6 285.6 282.3 + 282.3 281.8 281.4 280.2 279.3 278.1 277.2 276.8 276.8 277.2 277.7 278.5 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 285.6 + 285.2 283.9 281.8 280.6 278.5 277.2 276.8 276.8 277.2 278.5 280.6 281.8 283.9 285.2 285.6 285.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 315.73 moveto[ 167.6 167.6 196.4 196.4 ][ 315.7 327.8 327.8 315.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 321.2 169.6 321.2 2 pls + s[ 180.5 180.5 180.9 181.3 182.2 183.8 184.7 185.1 185.5 185.5 185.1 184.3 180.1 185.9 ][ 324.1 324.5 325.3 325.8 + 326.2 326.2 325.8 325.3 324.5 323.7 322.8 321.6 317.4 317.4 ]plong + s[ 193.5 189.3 188.9 189.3 190.5 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.5 189.3 188.9 188.4 ][ 326.2 + 326.2 322.4 322.8 323.3 323.3 322.8 322.0 320.7 319.9 318.7 317.8 317.4 317.4 317.8 318.2 319.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 356.34 moveto[ 167.6 167.6 196.4 196.4 ][ 356.3 368.4 368.4 356.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 361.8 169.6 361.8 2 pls + s[ 180.5 180.5 180.9 181.3 182.2 183.8 184.7 185.1 185.5 185.5 185.1 184.3 180.1 185.9 ][ 364.7 365.1 365.9 366.4 + 366.8 366.8 366.4 365.9 365.1 364.3 363.4 362.2 358.0 358.0 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 366.8 + 366.4 365.1 363.0 361.8 359.7 358.4 358.0 358.0 358.4 359.7 361.8 363.0 365.1 366.4 366.8 366.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 396.89 moveto[ 167.6 167.6 196.4 196.4 ][ 396.9 409.0 409.0 396.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 402.3 169.6 402.3 2 pls + 183.4 398.6 183.4 407.3 182.2 406.1 181.3 405.7 4 pls + s[ 193.5 189.3 188.9 189.3 190.5 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.5 189.3 188.9 188.4 ][ 407.3 + 407.3 403.6 404.0 404.4 404.4 404.0 403.2 401.9 401.1 399.8 399.0 398.6 398.6 399.0 399.4 400.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.56 437.04 moveto[ 167.6 167.6 196.4 196.4 ][ 437.0 449.2 449.2 437.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 177.2 442.5 169.6 442.5 2 pls + 183.4 438.7 183.4 447.5 182.2 446.2 181.3 445.8 4 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 447.5 + 447.1 445.8 443.7 442.5 440.4 439.1 438.7 438.7 439.1 440.4 442.5 443.7 445.8 447.1 447.5 447.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 335.61 40.63 moveto[ 335.6 335.6 454.2 454.2 ][ 40.6 56.1 56.1 40.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 344.0 343.5 342.7 341.9 340.2 339.4 338.5 338.1 337.7 337.7 338.1 338.5 339.4 340.2 341.9 342.7 343.5 344.0 + 344.0 ][ 51.9 52.7 53.6 54.0 54.0 53.6 52.7 51.9 50.7 48.6 47.3 46.5 45.6 45.2 45.2 45.6 46.5 + 47.3 48.6 ]plong + 344.0 48.6 341.9 48.6 2 pls + 351.5 45.2 351.5 51.1 2 pls + s[ 351.5 350.6 349.8 348.6 347.7 346.9 346.5 346.5 346.9 347.7 348.6 349.8 350.6 351.5 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + 354.8 45.2 354.8 54.0 2 pls + 362.8 45.2 362.8 51.1 2 pls + s[ 362.8 361.9 361.1 359.8 359.0 358.2 357.7 357.7 358.2 359.0 359.8 361.1 361.9 362.8 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + s[ 370.7 369.9 369.0 367.8 366.9 366.1 365.7 365.7 366.1 366.9 367.8 369.0 369.9 370.7 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + 376.1 45.2 375.3 45.2 374.5 45.6 374.0 46.9 374.0 54.0 5 pls + 375.7 51.1 372.8 51.1 2 pls + 378.2 54.0 378.6 54.4 379.1 54.0 378.6 53.6 378.2 54.0 5 pls + 378.6 45.2 378.6 51.1 2 pls + s[ 386.6 385.7 384.9 383.6 382.8 382.0 381.6 381.6 382.0 382.8 383.6 384.9 385.7 386.6 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + 396.2 45.2 396.2 54.0 2 pls + s[ 401.2 400.4 399.5 399.1 399.1 399.5 400.4 401.2 402.4 403.3 404.1 404.5 404.5 404.1 403.3 402.4 401.2 ][ 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 47.7 48.6 49.8 50.7 51.1 51.1 ]plong + 407.5 45.2 407.5 51.1 2 pls + s[ 407.5 408.7 409.5 410.8 411.6 412.0 412.0 ][ 49.4 50.7 51.1 51.1 50.7 49.4 45.2 ]plong + s[ 420.0 420.0 419.6 419.1 418.3 417.1 416.2 ][ 51.1 44.4 43.1 42.7 42.3 42.3 42.7 ]plong + s[ 420.0 419.1 418.3 417.1 416.2 415.4 415.0 415.0 415.4 416.2 417.1 418.3 419.1 420.0 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + 422.9 54.0 423.3 54.4 423.7 54.0 423.3 53.6 422.9 54.0 5 pls + 423.3 45.2 423.3 51.1 2 pls + 429.2 45.2 428.3 45.2 427.5 45.6 427.1 46.9 427.1 54.0 5 pls + 428.8 51.1 425.8 51.1 2 pls + s[ 431.7 431.7 432.1 432.9 434.2 435.0 436.3 ][ 51.1 46.9 45.6 45.2 45.2 45.6 46.9 ]plong + 436.3 45.2 436.3 51.1 2 pls + 444.2 45.2 444.2 54.0 2 pls + s[ 444.2 443.4 442.5 441.3 440.4 439.6 439.2 439.2 439.6 440.4 441.3 442.5 443.4 444.2 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + s[ 447.1 452.1 452.1 451.7 451.3 450.5 449.2 448.4 447.5 447.1 447.1 447.5 448.4 449.2 450.5 451.3 452.1 ][ 48.6 + 48.6 49.4 50.2 50.7 51.1 51.1 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 161.98 217.59 moveto[ 162.0 149.4 149.4 162.0 ][ 217.6 217.6 325.4 325.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 153.6 152.8 152.0 151.5 151.5 152.0 152.8 153.6 154.9 157.0 158.2 159.1 159.9 160.3 160.3 159.9 159.1 158.2 + 157.0 ][ 225.9 225.5 224.7 223.9 222.2 221.4 220.5 220.1 219.7 219.7 220.1 220.5 221.4 222.2 223.9 224.7 225.5 + 225.9 225.9 ]plong + 157.0 225.9 157.0 223.9 2 pls + 160.3 233.5 154.5 233.5 2 pls + s[ 155.7 154.9 154.5 154.5 154.9 155.7 157.0 157.8 159.1 159.9 160.3 160.3 159.9 159.1 ][ 233.5 232.6 231.8 230.5 + 229.7 228.9 228.5 228.5 228.9 229.7 230.5 231.8 232.6 233.5 ]plong + 160.3 236.8 151.5 236.8 2 pls + 160.3 244.7 154.5 244.7 2 pls + s[ 155.7 154.9 154.5 154.5 154.9 155.7 157.0 157.8 159.1 159.9 160.3 160.3 159.9 159.1 ][ 244.7 243.9 243.1 241.8 + 241.0 240.1 239.7 239.7 240.1 241.0 241.8 243.1 243.9 244.7 ]plong + s[ 155.7 154.9 154.5 154.5 154.9 155.7 157.0 157.8 159.1 159.9 160.3 160.3 159.9 159.1 ][ 252.7 251.8 251.0 249.8 + 248.9 248.1 247.7 247.7 248.1 248.9 249.8 251.0 251.8 252.7 ]plong + 160.3 258.1 160.3 257.3 159.9 256.4 158.6 256.0 151.5 256.0 5 pls + 154.5 257.7 154.5 254.8 2 pls + 151.5 260.2 151.1 260.6 151.5 261.0 152.0 260.6 151.5 260.2 5 pls + 160.3 260.6 154.5 260.6 2 pls + s[ 155.7 154.9 154.5 154.5 154.9 155.7 157.0 157.8 159.1 159.9 160.3 160.3 159.9 159.1 ][ 268.5 267.7 266.9 265.6 + 264.8 264.0 263.5 263.5 264.0 264.8 265.6 266.9 267.7 268.5 ]plong + 160.3 278.2 151.5 278.2 2 pls + 160.3 286.1 154.5 286.1 2 pls + s[ 155.7 154.9 154.5 154.5 154.9 155.7 157.0 157.8 159.1 159.9 160.3 160.3 159.9 159.1 ][ 286.1 285.3 284.4 283.2 + 282.3 281.5 281.1 281.1 281.5 282.3 283.2 284.4 285.3 286.1 ]plong + 160.3 291.9 160.3 291.1 159.9 290.3 158.6 289.8 151.5 289.8 5 pls + 154.5 291.5 154.5 288.6 2 pls + 151.5 294.0 151.1 294.4 151.5 294.9 152.0 294.4 151.5 294.0 5 pls + 160.3 294.4 154.5 294.4 2 pls + 160.3 300.3 160.3 299.5 159.9 298.6 158.6 298.2 151.5 298.2 5 pls + 154.5 299.9 154.5 296.9 2 pls + s[ 154.5 158.6 159.9 160.3 160.3 159.9 158.6 ][ 302.8 302.8 303.2 304.1 305.3 306.1 307.4 ]plong + 160.3 307.4 154.5 307.4 2 pls + 160.3 315.3 151.5 315.3 2 pls + s[ 155.7 154.9 154.5 154.5 154.9 155.7 157.0 157.8 159.1 159.9 160.3 160.3 159.9 159.1 ][ 315.3 314.5 313.7 312.4 + 311.6 310.7 310.3 310.3 310.7 311.6 312.4 313.7 314.5 315.3 ]plong + s[ 157.0 157.0 156.1 155.3 154.9 154.5 154.5 154.9 155.7 157.0 157.8 159.1 159.9 160.3 160.3 159.9 159.1 ][ 318.3 + 323.3 323.3 322.8 322.4 321.6 320.3 319.5 318.7 318.3 318.3 318.7 319.5 320.3 321.6 322.4 323.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 486.51 moveto[ 360.9 360.9 435.2 435.2 ][ 486.5 498.6 498.6 486.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 488.2 366.3 496.9 2 pls + 369.7 488.2 366.3 496.9 2 pls + 368.4 491.1 364.2 491.1 2 pls + 378.4 488.2 378.4 496.9 2 pls + 383.9 496.9 378.4 496.9 2 pls + 381.8 492.8 378.4 492.8 2 pls + 385.9 488.2 385.9 496.9 2 pls + 391.0 488.2 391.0 496.9 2 pls + 393.9 496.9 388.0 496.9 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 495.7 496.5 496.9 496.9 496.5 495.7 494.9 494.0 493.6 493.2 492.4 491.9 491.5 490.7 489.4 488.6 + 488.2 488.2 488.6 489.4 ]plong + 413.5 488.2 412.7 488.2 411.8 488.6 411.4 489.8 411.4 496.9 5 pls + 413.1 494.0 410.2 494.0 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 491.5 + 491.5 492.4 493.2 493.6 494.0 494.0 493.6 492.8 491.5 490.7 489.4 488.6 488.2 488.2 488.6 489.4 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 492.8 + 493.6 494.0 494.0 493.6 492.8 491.9 491.5 491.1 490.7 489.8 489.4 488.6 488.2 488.2 488.6 489.4 ]plong + 433.1 488.2 432.3 488.2 431.5 488.6 431.1 489.8 431.1 496.9 5 pls + 432.7 494.0 429.8 494.0 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/car4.attr b/ast_tester/car4.attr new file mode 100644 index 0000000..e69de29 diff --git a/ast_tester/car4.box b/ast_tester/car4.box new file mode 100644 index 0000000..0393b0d --- /dev/null +++ b/ast_tester/car4.box @@ -0,0 +1 @@ +0 0 951 1851 diff --git a/ast_tester/car4.fattr b/ast_tester/car4.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast_tester/car4.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast_tester/car4.head b/ast_tester/car4.head new file mode 100644 index 0000000..95cd97a --- /dev/null +++ b/ast_tester/car4.head @@ -0,0 +1,38 @@ +SIMPLE = T / Written by IDL: Thu Apr 27 08:52:27 2000 +BITPIX = -32 / +NAXIS = 2 / +NAXIS1 = 951 / +NAXIS2 = 1851 / +CRPIX1 = 211076.0 / +CRVAL1 = 0.000000000 / +CTYPE1 = 'GLON-CAR' / +CRPIX2 = 475.39400 / +CRVAL2 = 0.000000000 / +CTYPE2 = 'GLAT-CAR' / +CROTA2 = 0.000000000 / +LONPOLE = 0.00000 / Defined by Greisen and Calabretta +CD1_1 = -0.0016666667 / +CD1_2 = 0.00000 / +CD2_1 = 0.00000 / +CD2_2 = 0.0016666667 / +WAVELENG= 8.28000e-06 / Isophotal wavelength in meters +BUNIT = 'W/m^2-sr' / +SECURITY= 'Unclassified' / +TELESCOP= 'MSX ' / +INSTRUME= 'SPIRITIII' / +ORIGIN = 'AFRL-VSBC' / +MJD-OBS = 50295.5 / Mean modified Julian date of observation +DATE = '16/02/2000' / Date of file generation +HISTORY Convert Version 6.2.X +HISTORY Level-2A Deshadow Version 4.0 +HISTORY Level-2A Saturation Correction Version 1.0 +HISTORY Pointing Convert Version 6.0.1 +HISTORY Makeimage Version 3.2 +HISTORY Destriped +HISTORY Data collected in J2000 FK5 coordinates +HISTORY Data samples transformed to Galactic coordinates +HISTORY and convolved onto image grid using sigma=3.0 arcsec +HISTORY Gaussian kernel +HISTORY Master Plate: GP_351.0_+0.0_A.fits +HISTORY Written by IDL: 28-Jan-2000 17:44:54.00 +END diff --git a/ast_tester/car4.ps b/ast_tester/car4.ps new file mode 100644 index 0000000..f837bd6 --- /dev/null +++ b/ast_tester/car4.ps @@ -0,0 +1,647 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed May 7 20:24:19 2008 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 07/05/1908 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 490.1 77.4 490.1 76.0 2 pls + 483.7 77.4 483.7 76.0 2 pls + 471.1 79.0 471.1 76.0 2 pls + 477.4 77.4 477.4 76.0 2 pls + 464.7 77.4 464.7 76.0 2 pls + 458.4 77.4 458.4 76.0 2 pls + 445.7 79.0 445.7 76.0 2 pls + 452.1 77.4 452.1 76.0 2 pls + 439.4 77.4 439.4 76.0 2 pls + 433.0 77.4 433.0 76.0 2 pls + 420.4 79.0 420.4 76.0 2 pls + 426.7 77.4 426.7 76.0 2 pls + 414.0 77.4 414.0 76.0 2 pls + 407.7 77.4 407.7 76.0 2 pls + 395.0 79.0 395.0 76.0 2 pls + 401.4 77.4 401.4 76.0 2 pls + 388.7 77.4 388.7 76.0 2 pls + 382.4 77.4 382.4 76.0 2 pls + 369.7 79.0 369.7 76.0 2 pls + 376.0 77.4 376.0 76.0 2 pls + 363.3 77.4 363.3 76.0 2 pls + 357.0 77.4 357.0 76.0 2 pls + 344.3 79.0 344.3 76.0 2 pls + 350.7 77.4 350.7 76.0 2 pls + 338.0 77.4 338.0 76.0 2 pls + 331.7 77.4 331.7 76.0 2 pls + 319.0 79.0 319.0 76.0 2 pls + 325.3 77.4 325.3 76.0 2 pls + 312.7 77.4 312.7 76.0 2 pls + 306.3 77.4 306.3 76.0 2 pls + 300.0 77.4 300.0 76.0 2 pls + 297.5 113.1 294.5 113.1 2 pls + 295.9 87.7 294.5 87.7 2 pls + 295.9 100.4 294.5 100.4 2 pls + 295.9 125.7 294.5 125.7 2 pls + 295.9 138.4 294.5 138.4 2 pls + 297.5 176.4 294.5 176.4 2 pls + 295.9 151.1 294.5 151.1 2 pls + 295.9 163.8 294.5 163.8 2 pls + 295.9 189.1 294.5 189.1 2 pls + 295.9 201.8 294.5 201.8 2 pls + 297.5 239.8 294.5 239.8 2 pls + 295.9 214.4 294.5 214.4 2 pls + 295.9 227.1 294.5 227.1 2 pls + 295.9 252.5 294.5 252.5 2 pls + 295.9 265.1 294.5 265.1 2 pls + 297.5 303.1 294.5 303.1 2 pls + 295.9 277.8 294.5 277.8 2 pls + 295.9 290.5 294.5 290.5 2 pls + 295.9 315.8 294.5 315.8 2 pls + 295.9 328.5 294.5 328.5 2 pls + 297.5 366.5 294.5 366.5 2 pls + 295.9 341.2 294.5 341.2 2 pls + 295.9 353.8 294.5 353.8 2 pls + 295.9 379.2 294.5 379.2 2 pls + 295.9 391.9 294.5 391.9 2 pls + 297.5 429.9 294.5 429.9 2 pls + 295.9 404.5 294.5 404.5 2 pls + 295.9 417.2 294.5 417.2 2 pls + 295.9 442.5 294.5 442.5 2 pls + 295.9 455.2 294.5 455.2 2 pls + 490.1 465.6 490.1 467.0 2 pls + 483.7 465.6 483.7 467.0 2 pls + 471.1 463.9 471.1 467.0 2 pls + 477.4 465.6 477.4 467.0 2 pls + 464.7 465.6 464.7 467.0 2 pls + 458.4 465.6 458.4 467.0 2 pls + 445.7 463.9 445.7 467.0 2 pls + 452.1 465.6 452.1 467.0 2 pls + 439.4 465.6 439.4 467.0 2 pls + 433.0 465.6 433.0 467.0 2 pls + 420.4 463.9 420.4 467.0 2 pls + 426.7 465.6 426.7 467.0 2 pls + 414.0 465.6 414.0 467.0 2 pls + 407.7 465.6 407.7 467.0 2 pls + 395.0 463.9 395.0 467.0 2 pls + 401.4 465.6 401.4 467.0 2 pls + 388.7 465.6 388.7 467.0 2 pls + 382.4 465.6 382.4 467.0 2 pls + 369.7 463.9 369.7 467.0 2 pls + 376.0 465.6 376.0 467.0 2 pls + 363.3 465.6 363.3 467.0 2 pls + 357.0 465.6 357.0 467.0 2 pls + 344.3 463.9 344.3 467.0 2 pls + 350.7 465.6 350.7 467.0 2 pls + 338.0 465.6 338.0 467.0 2 pls + 331.7 465.6 331.7 467.0 2 pls + 319.0 463.9 319.0 467.0 2 pls + 325.3 465.6 325.3 467.0 2 pls + 312.7 465.6 312.7 467.0 2 pls + 306.3 465.6 306.3 467.0 2 pls + 300.0 465.6 300.0 467.0 2 pls + 492.3 113.1 495.4 113.1 2 pls + 493.9 87.7 495.4 87.7 2 pls + 493.9 100.4 495.4 100.4 2 pls + 493.9 125.7 495.4 125.7 2 pls + 493.9 138.4 495.4 138.4 2 pls + 492.3 176.4 495.4 176.4 2 pls + 493.9 151.1 495.4 151.1 2 pls + 493.9 163.8 495.4 163.8 2 pls + 493.9 189.1 495.4 189.1 2 pls + 493.9 201.8 495.4 201.8 2 pls + 492.3 239.8 495.4 239.8 2 pls + 493.9 214.4 495.4 214.4 2 pls + 493.9 227.1 495.4 227.1 2 pls + 493.9 252.5 495.4 252.5 2 pls + 493.9 265.1 495.4 265.1 2 pls + 492.3 303.1 495.4 303.1 2 pls + 493.9 277.8 495.4 277.8 2 pls + 493.9 290.5 495.4 290.5 2 pls + 493.9 315.8 495.4 315.8 2 pls + 493.9 328.5 495.4 328.5 2 pls + 492.3 366.5 495.4 366.5 2 pls + 493.9 341.2 495.4 341.2 2 pls + 493.9 353.8 495.4 353.8 2 pls + 493.9 379.2 495.4 379.2 2 pls + 493.9 391.9 495.4 391.9 2 pls + 492.3 429.9 495.4 429.9 2 pls + 493.9 404.5 495.4 404.5 2 pls + 493.9 417.2 495.4 417.2 2 pls + 493.9 442.5 495.4 442.5 2 pls + 493.9 455.2 495.4 455.2 2 pls + s[ 294.5 308.8 323.2 337.5 351.9 366.2 380.6 394.9 409.3 423.6 438.0 452.3 466.7 481.0 495.4 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 495.4 481.0 466.7 452.3 438.0 423.6 409.3 394.9 380.6 366.2 351.9 337.5 323.2 308.8 294.5 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 324.71 61.90 moveto[ 324.7 324.7 364.4 364.4 ][ 61.9 74.0 74.0 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 327.6 332.2 329.7 331.0 331.8 332.2 332.6 332.6 332.2 331.4 330.1 328.9 327.6 327.2 326.8 ][ 72.3 72.3 69.0 + 69.0 68.6 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 340.2 336.0 335.6 336.0 337.2 338.5 339.7 340.6 341.0 341.0 340.6 339.7 338.5 337.2 336.0 335.6 335.2 ][ 72.3 + 72.3 68.6 69.0 69.4 69.4 69.0 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + 346.8 63.6 346.8 72.3 345.6 71.1 344.8 70.7 4 pls + 352.7 64.4 353.1 64.0 352.7 63.6 352.3 64.0 352.7 64.4 5 pls + 362.3 66.5 356.0 66.5 360.2 72.3 3 pls + 360.2 63.6 360.2 72.3 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 375.40 61.90 moveto[ 375.4 375.4 414.7 414.7 ][ 61.9 74.0 74.0 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 378.3 382.9 380.4 381.7 382.5 382.9 383.3 383.3 382.9 382.1 380.8 379.6 378.3 377.9 377.5 ][ 72.3 72.3 69.0 + 69.0 68.6 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 390.9 386.7 386.3 386.7 387.9 389.2 390.4 391.3 391.7 391.7 391.3 390.4 389.2 387.9 386.7 386.3 385.8 ][ 72.3 + 72.3 68.6 69.0 69.4 69.4 69.0 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + 397.5 63.6 397.5 72.3 396.3 71.1 395.4 70.7 4 pls + 403.4 64.4 403.8 64.0 403.4 63.6 403.0 64.0 403.4 64.4 5 pls + s[ 409.2 408.0 407.1 406.7 406.7 407.1 408.0 409.2 410.1 411.3 412.2 412.6 412.6 412.2 411.3 410.1 409.2 ][ 72.3 + 71.9 70.7 68.6 67.3 65.2 64.0 63.6 63.6 64.0 65.2 67.3 68.6 70.7 71.9 72.3 72.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 426.09 61.90 moveto[ 426.1 426.1 465.3 465.3 ][ 61.9 74.0 74.0 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 429.0 433.6 431.1 432.4 433.2 433.6 434.0 434.0 433.6 432.8 431.5 430.3 429.0 428.6 428.2 ][ 72.3 72.3 69.0 + 69.0 68.6 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 441.5 437.4 436.9 437.4 438.6 439.9 441.1 442.0 442.4 442.4 442.0 441.1 439.9 438.6 437.4 436.9 436.5 ][ 72.3 + 72.3 68.6 69.0 69.4 69.4 69.0 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 447.4 446.1 445.3 444.9 444.9 445.3 446.1 447.4 448.2 449.5 450.3 450.7 450.7 450.3 449.5 448.2 447.4 ][ 72.3 + 71.9 70.7 68.6 67.3 65.2 64.0 63.6 63.6 64.0 65.2 67.3 68.6 70.7 71.9 72.3 72.3 ]plong + 454.1 64.4 454.5 64.0 454.1 63.6 453.7 64.0 454.1 64.4 5 pls + s[ 462.8 462.4 461.2 460.3 459.1 458.2 457.8 457.8 458.2 459.1 460.3 460.8 462.0 462.8 463.3 463.3 462.8 462.0 + 460.8 460.3 459.1 458.2 457.8 ][ 71.1 71.9 72.3 72.3 71.9 70.7 68.6 66.5 64.8 64.0 63.6 63.6 64.0 + 64.8 66.1 66.5 67.7 68.6 69.0 69.0 68.6 67.7 66.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 260.32 106.17 moveto[ 260.3 260.3 293.3 293.3 ][ 106.2 118.3 118.3 106.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 269.9 111.6 262.4 111.6 2 pls + s[ 275.4 274.1 273.3 272.9 272.9 273.3 274.1 275.4 276.2 277.4 278.3 278.7 278.7 278.3 277.4 276.2 275.4 ][ 116.6 + 116.2 114.9 112.9 111.6 109.5 108.3 107.8 107.8 108.3 109.5 111.6 112.9 114.9 116.2 116.6 116.6 ]plong + 282.0 108.7 282.5 108.3 282.0 107.8 281.6 108.3 282.0 108.7 5 pls + s[ 290.4 286.2 285.8 286.2 287.5 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.5 286.2 285.8 285.4 ][ 116.6 + 116.6 112.9 113.3 113.7 113.7 113.3 112.4 111.2 110.3 109.1 108.3 107.8 107.8 108.3 108.7 109.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.77 169.53 moveto[ 270.8 270.8 293.3 293.3 ][ 169.5 181.6 181.6 169.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 275.4 274.1 273.3 272.9 272.9 273.3 274.1 275.4 276.2 277.4 278.3 278.7 278.7 278.3 277.4 276.2 275.4 ][ 180.0 + 179.6 178.3 176.2 175.0 172.9 171.6 171.2 171.2 171.6 172.9 175.0 176.2 178.3 179.6 180.0 180.0 ]plong + 282.0 172.0 282.5 171.6 282.0 171.2 281.6 171.6 282.0 172.0 5 pls + s[ 287.9 286.6 285.8 285.4 285.4 285.8 286.6 287.9 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.9 ][ 180.0 + 179.6 178.3 176.2 175.0 172.9 171.6 171.2 171.2 171.6 172.9 175.0 176.2 178.3 179.6 180.0 180.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.77 232.89 moveto[ 270.8 270.8 293.3 293.3 ][ 232.9 245.0 245.0 232.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 275.4 274.1 273.3 272.9 272.9 273.3 274.1 275.4 276.2 277.4 278.3 278.7 278.7 278.3 277.4 276.2 275.4 ][ 243.3 + 242.9 241.7 239.6 238.3 236.2 235.0 234.6 234.6 235.0 236.2 238.3 239.6 241.7 242.9 243.3 243.3 ]plong + 282.0 235.4 282.5 235.0 282.0 234.6 281.6 235.0 282.0 235.4 5 pls + s[ 290.4 286.2 285.8 286.2 287.5 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.5 286.2 285.8 285.4 ][ 243.3 + 243.3 239.6 240.0 240.4 240.4 240.0 239.2 237.9 237.1 235.8 235.0 234.6 234.6 235.0 235.4 236.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 272.02 296.26 moveto[ 272.0 272.0 293.3 293.3 ][ 296.3 308.4 308.4 296.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 276.2 297.9 276.2 306.7 274.9 305.4 274.1 305.0 4 pls + 282.0 298.8 282.5 298.3 282.0 297.9 281.6 298.3 282.0 298.8 5 pls + s[ 287.9 286.6 285.8 285.4 285.4 285.8 286.6 287.9 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.9 ][ 306.7 + 306.3 305.0 302.9 301.7 299.6 298.3 297.9 297.9 298.3 299.6 301.7 302.9 305.0 306.3 306.7 306.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 272.02 359.62 moveto[ 272.0 272.0 293.3 293.3 ][ 359.6 371.7 371.7 359.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 276.2 361.3 276.2 370.1 274.9 368.8 274.1 368.4 4 pls + 282.0 362.1 282.5 361.7 282.0 361.3 281.6 361.7 282.0 362.1 5 pls + s[ 290.4 286.2 285.8 286.2 287.5 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.5 286.2 285.8 285.4 ][ 370.1 + 370.1 366.3 366.7 367.1 367.1 366.7 365.9 364.6 363.8 362.5 361.7 361.3 361.3 361.7 362.1 363.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.77 422.98 moveto[ 270.8 270.8 293.3 293.3 ][ 423.0 435.1 435.1 423.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 273.3 273.3 273.7 274.1 274.9 276.6 277.4 277.9 278.3 278.3 277.9 277.0 272.9 278.7 ][ 431.3 431.8 432.6 433.0 + 433.4 433.4 433.0 432.6 431.8 430.9 430.1 428.8 424.6 424.6 ]plong + 282.0 425.5 282.5 425.1 282.0 424.6 281.6 425.1 282.0 425.5 5 pls + s[ 287.9 286.6 285.8 285.4 285.4 285.8 286.6 287.9 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.9 ][ 433.4 + 433.0 431.8 429.7 428.4 426.3 425.1 424.6 424.6 425.1 426.3 428.4 429.7 431.8 433.0 433.4 433.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 335.61 44.43 moveto[ 335.6 335.6 454.2 454.2 ][ 44.4 59.9 59.9 44.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 344.0 343.5 342.7 341.9 340.2 339.4 338.5 338.1 337.7 337.7 338.1 338.5 339.4 340.2 341.9 342.7 343.5 344.0 + 344.0 ][ 55.7 56.5 57.4 57.8 57.8 57.4 56.5 55.7 54.5 52.4 51.1 50.3 49.4 49.0 49.0 49.4 50.3 + 51.1 52.4 ]plong + 344.0 52.4 341.9 52.4 2 pls + 351.5 49.0 351.5 54.9 2 pls + s[ 351.5 350.6 349.8 348.6 347.7 346.9 346.5 346.5 346.9 347.7 348.6 349.8 350.6 351.5 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 354.8 49.0 354.8 57.8 2 pls + 362.8 49.0 362.8 54.9 2 pls + s[ 362.8 361.9 361.1 359.8 359.0 358.2 357.7 357.7 358.2 359.0 359.8 361.1 361.9 362.8 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + s[ 370.7 369.9 369.0 367.8 366.9 366.1 365.7 365.7 366.1 366.9 367.8 369.0 369.9 370.7 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 376.1 49.0 375.3 49.0 374.5 49.4 374.0 50.7 374.0 57.8 5 pls + 375.7 54.9 372.8 54.9 2 pls + 378.2 57.8 378.6 58.2 379.1 57.8 378.6 57.4 378.2 57.8 5 pls + 378.6 49.0 378.6 54.9 2 pls + s[ 386.6 385.7 384.9 383.6 382.8 382.0 381.6 381.6 382.0 382.8 383.6 384.9 385.7 386.6 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 396.2 49.0 396.2 57.8 2 pls + s[ 401.2 400.4 399.5 399.1 399.1 399.5 400.4 401.2 402.4 403.3 404.1 404.5 404.5 404.1 403.3 402.4 401.2 ][ 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 51.5 52.4 53.6 54.5 54.9 54.9 ]plong + 407.5 49.0 407.5 54.9 2 pls + s[ 407.5 408.7 409.5 410.8 411.6 412.0 412.0 ][ 53.2 54.5 54.9 54.9 54.5 53.2 49.0 ]plong + s[ 420.0 420.0 419.6 419.1 418.3 417.1 416.2 ][ 54.9 48.2 46.9 46.5 46.1 46.1 46.5 ]plong + s[ 420.0 419.1 418.3 417.1 416.2 415.4 415.0 415.0 415.4 416.2 417.1 418.3 419.1 420.0 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 422.9 57.8 423.3 58.2 423.7 57.8 423.3 57.4 422.9 57.8 5 pls + 423.3 49.0 423.3 54.9 2 pls + 429.2 49.0 428.3 49.0 427.5 49.4 427.1 50.7 427.1 57.8 5 pls + 428.8 54.9 425.8 54.9 2 pls + s[ 431.7 431.7 432.1 432.9 434.2 435.0 436.3 ][ 54.9 50.7 49.4 49.0 49.0 49.4 50.7 ]plong + 436.3 49.0 436.3 54.9 2 pls + 444.2 49.0 444.2 57.8 2 pls + s[ 444.2 443.4 442.5 441.3 440.4 439.6 439.2 439.2 439.6 440.4 441.3 442.5 443.4 444.2 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + s[ 447.1 452.1 452.1 451.7 451.3 450.5 449.2 448.4 447.5 447.1 447.1 447.5 448.4 449.2 450.5 451.3 452.1 ][ 52.4 + 52.4 53.2 54.0 54.5 54.9 54.9 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 256.65 217.59 moveto[ 256.6 244.1 244.1 256.6 ][ 217.6 217.6 325.4 325.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 248.3 247.5 246.6 246.2 246.2 246.6 247.5 248.3 249.5 251.6 252.9 253.7 254.6 255.0 255.0 254.6 253.7 252.9 + 251.6 ][ 225.9 225.5 224.7 223.9 222.2 221.4 220.5 220.1 219.7 219.7 220.1 220.5 221.4 222.2 223.9 224.7 225.5 + 225.9 225.9 ]plong + 251.6 225.9 251.6 223.9 2 pls + 255.0 233.5 249.1 233.5 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 233.5 232.6 231.8 230.5 + 229.7 228.9 228.5 228.5 228.9 229.7 230.5 231.8 232.6 233.5 ]plong + 255.0 236.8 246.2 236.8 2 pls + 255.0 244.7 249.1 244.7 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 244.7 243.9 243.1 241.8 + 241.0 240.1 239.7 239.7 240.1 241.0 241.8 243.1 243.9 244.7 ]plong + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 252.7 251.8 251.0 249.8 + 248.9 248.1 247.7 247.7 248.1 248.9 249.8 251.0 251.8 252.7 ]plong + 255.0 258.1 255.0 257.3 254.6 256.4 253.3 256.0 246.2 256.0 5 pls + 249.1 257.7 249.1 254.8 2 pls + 246.2 260.2 245.8 260.6 246.2 261.0 246.6 260.6 246.2 260.2 5 pls + 255.0 260.6 249.1 260.6 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 268.5 267.7 266.9 265.6 + 264.8 264.0 263.5 263.5 264.0 264.8 265.6 266.9 267.7 268.5 ]plong + 255.0 278.2 246.2 278.2 2 pls + 255.0 286.1 249.1 286.1 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 286.1 285.3 284.4 283.2 + 282.3 281.5 281.1 281.1 281.5 282.3 283.2 284.4 285.3 286.1 ]plong + 255.0 291.9 255.0 291.1 254.6 290.3 253.3 289.8 246.2 289.8 5 pls + 249.1 291.5 249.1 288.6 2 pls + 246.2 294.0 245.8 294.4 246.2 294.9 246.6 294.4 246.2 294.0 5 pls + 255.0 294.4 249.1 294.4 2 pls + 255.0 300.3 255.0 299.5 254.6 298.6 253.3 298.2 246.2 298.2 5 pls + 249.1 299.9 249.1 296.9 2 pls + s[ 249.1 253.3 254.6 255.0 255.0 254.6 253.3 ][ 302.8 302.8 303.2 304.1 305.3 306.1 307.4 ]plong + 255.0 307.4 249.1 307.4 2 pls + 255.0 315.3 246.2 315.3 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 315.3 314.5 313.7 312.4 + 311.6 310.7 310.3 310.3 310.7 311.6 312.4 313.7 314.5 315.3 ]plong + s[ 251.6 251.6 250.8 250.0 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 318.3 + 323.3 323.3 322.8 322.4 321.6 320.3 319.5 318.7 318.3 318.3 318.7 319.5 320.3 321.6 322.4 323.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 477.00 moveto[ 360.9 360.9 435.2 435.2 ][ 477.0 489.1 489.1 477.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 478.7 366.3 487.4 2 pls + 369.7 478.7 366.3 487.4 2 pls + 368.4 481.6 364.2 481.6 2 pls + 378.4 478.7 378.4 487.4 2 pls + 383.9 487.4 378.4 487.4 2 pls + 381.8 483.3 378.4 483.3 2 pls + 385.9 478.7 385.9 487.4 2 pls + 391.0 478.7 391.0 487.4 2 pls + 393.9 487.4 388.0 487.4 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 486.2 487.0 487.4 487.4 487.0 486.2 485.4 484.5 484.1 483.7 482.8 482.4 482.0 481.2 479.9 479.1 + 478.7 478.7 479.1 479.9 ]plong + 413.5 478.7 412.7 478.7 411.8 479.1 411.4 480.3 411.4 487.4 5 pls + 413.1 484.5 410.2 484.5 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 482.0 + 482.0 482.8 483.7 484.1 484.5 484.5 484.1 483.3 482.0 481.2 479.9 479.1 478.7 478.7 479.1 479.9 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 483.3 + 484.1 484.5 484.5 484.1 483.3 482.4 482.0 481.6 481.2 480.3 479.9 479.1 478.7 478.7 479.1 479.9 ]plong + 433.1 478.7 432.3 478.7 431.5 479.1 431.1 480.3 431.1 487.4 5 pls + 432.7 484.5 429.8 484.5 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/car5.attr b/ast_tester/car5.attr new file mode 100644 index 0000000..e69de29 diff --git a/ast_tester/car5.box b/ast_tester/car5.box new file mode 100644 index 0000000..0393b0d --- /dev/null +++ b/ast_tester/car5.box @@ -0,0 +1 @@ +0 0 951 1851 diff --git a/ast_tester/car5.head b/ast_tester/car5.head new file mode 100644 index 0000000..2be3a64 --- /dev/null +++ b/ast_tester/car5.head @@ -0,0 +1,38 @@ +SIMPLE = T / Written by IDL: Thu Apr 27 08:52:27 2000 +BITPIX = -32 / +NAXIS = 2 / +NAXIS1 = 951 / +NAXIS2 = 1851 / +CRPIX1 = -4932.0204 / +CRVAL1 = 0.000000000 / +CTYPE1 = 'GLON-CAR' / +CRPIX2 = 475.39400 / +CRVAL2 = 0.000000000 / +CTYPE2 = 'GLAT-CAR' / +CROTA2 = 0.000000000 / +LONPOLE = 0.00000 / Defined by Greisen and Calabretta +CD1_1 = -0.0016666667 / +CD1_2 = 0.00000 / +CD2_1 = 0.00000 / +CD2_2 = 0.0016666667 / +WAVELENG= 8.28000e-06 / Isophotal wavelength in meters +BUNIT = 'W/m^2-sr' / +SECURITY= 'Unclassified' / +TELESCOP= 'MSX ' / +INSTRUME= 'SPIRITIII' / +ORIGIN = 'AFRL-VSBC' / +MJD-OBS = 50295.5 / Mean modified Julian date of observation +DATE = '16/02/2000' / Date of file generation +HISTORY Convert Version 6.2.X +HISTORY Level-2A Deshadow Version 4.0 +HISTORY Level-2A Saturation Correction Version 1.0 +HISTORY Pointing Convert Version 6.0.1 +HISTORY Makeimage Version 3.2 +HISTORY Destriped +HISTORY Data collected in J2000 FK5 coordinates +HISTORY Data samples transformed to Galactic coordinates +HISTORY and convolved onto image grid using sigma=3.0 arcsec +HISTORY Gaussian kernel +HISTORY Master Plate: GP_351.0_+0.0_A.fits +HISTORY Written by IDL: 28-Jan-2000 17:44:54.00 +END diff --git a/ast_tester/car5.ps b/ast_tester/car5.ps new file mode 100644 index 0000000..2a1df69 --- /dev/null +++ b/ast_tester/car5.ps @@ -0,0 +1,670 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed May 7 20:24:55 2008 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 07/05/1908 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 494.7 79.0 494.7 76.0 2 pls + 488.4 77.4 488.4 76.0 2 pls + 482.0 77.4 482.0 76.0 2 pls + 469.4 79.0 469.4 76.0 2 pls + 475.7 77.4 475.7 76.0 2 pls + 463.0 77.4 463.0 76.0 2 pls + 456.7 77.4 456.7 76.0 2 pls + 444.0 79.0 444.0 76.0 2 pls + 450.4 77.4 450.4 76.0 2 pls + 437.7 77.4 437.7 76.0 2 pls + 431.4 77.4 431.4 76.0 2 pls + 418.7 79.0 418.7 76.0 2 pls + 425.0 77.4 425.0 76.0 2 pls + 412.3 77.4 412.3 76.0 2 pls + 406.0 77.4 406.0 76.0 2 pls + 393.3 79.0 393.3 76.0 2 pls + 399.7 77.4 399.7 76.0 2 pls + 387.0 77.4 387.0 76.0 2 pls + 380.7 77.4 380.7 76.0 2 pls + 368.0 79.0 368.0 76.0 2 pls + 374.3 77.4 374.3 76.0 2 pls + 361.7 77.4 361.7 76.0 2 pls + 355.3 77.4 355.3 76.0 2 pls + 342.6 79.0 342.6 76.0 2 pls + 349.0 77.4 349.0 76.0 2 pls + 336.3 77.4 336.3 76.0 2 pls + 330.0 77.4 330.0 76.0 2 pls + 317.3 79.0 317.3 76.0 2 pls + 323.6 77.4 323.6 76.0 2 pls + 311.0 77.4 311.0 76.0 2 pls + 304.6 77.4 304.6 76.0 2 pls + 298.3 77.4 298.3 76.0 2 pls + 297.5 113.1 294.5 113.1 2 pls + 295.9 87.7 294.5 87.7 2 pls + 295.9 100.4 294.5 100.4 2 pls + 295.9 125.7 294.5 125.7 2 pls + 295.9 138.4 294.5 138.4 2 pls + 297.5 176.4 294.5 176.4 2 pls + 295.9 151.1 294.5 151.1 2 pls + 295.9 163.8 294.5 163.8 2 pls + 295.9 189.1 294.5 189.1 2 pls + 295.9 201.8 294.5 201.8 2 pls + 297.5 239.8 294.5 239.8 2 pls + 295.9 214.4 294.5 214.4 2 pls + 295.9 227.1 294.5 227.1 2 pls + 295.9 252.5 294.5 252.5 2 pls + 295.9 265.1 294.5 265.1 2 pls + 297.5 303.1 294.5 303.1 2 pls + 295.9 277.8 294.5 277.8 2 pls + 295.9 290.5 294.5 290.5 2 pls + 295.9 315.8 294.5 315.8 2 pls + 295.9 328.5 294.5 328.5 2 pls + 297.5 366.5 294.5 366.5 2 pls + 295.9 341.2 294.5 341.2 2 pls + 295.9 353.8 294.5 353.8 2 pls + 295.9 379.2 294.5 379.2 2 pls + 295.9 391.9 294.5 391.9 2 pls + 297.5 429.9 294.5 429.9 2 pls + 295.9 404.5 294.5 404.5 2 pls + 295.9 417.2 294.5 417.2 2 pls + 295.9 442.5 294.5 442.5 2 pls + 295.9 455.2 294.5 455.2 2 pls + 494.7 463.9 494.7 467.0 2 pls + 488.4 465.6 488.4 467.0 2 pls + 482.0 465.6 482.0 467.0 2 pls + 469.4 463.9 469.4 467.0 2 pls + 475.7 465.6 475.7 467.0 2 pls + 463.0 465.6 463.0 467.0 2 pls + 456.7 465.6 456.7 467.0 2 pls + 444.0 463.9 444.0 467.0 2 pls + 450.4 465.6 450.4 467.0 2 pls + 437.7 465.6 437.7 467.0 2 pls + 431.4 465.6 431.4 467.0 2 pls + 418.7 463.9 418.7 467.0 2 pls + 425.0 465.6 425.0 467.0 2 pls + 412.3 465.6 412.3 467.0 2 pls + 406.0 465.6 406.0 467.0 2 pls + 393.3 463.9 393.3 467.0 2 pls + 399.7 465.6 399.7 467.0 2 pls + 387.0 465.6 387.0 467.0 2 pls + 380.7 465.6 380.7 467.0 2 pls + 368.0 463.9 368.0 467.0 2 pls + 374.3 465.6 374.3 467.0 2 pls + 361.7 465.6 361.7 467.0 2 pls + 355.3 465.6 355.3 467.0 2 pls + 342.6 463.9 342.6 467.0 2 pls + 349.0 465.6 349.0 467.0 2 pls + 336.3 465.6 336.3 467.0 2 pls + 330.0 465.6 330.0 467.0 2 pls + 317.3 463.9 317.3 467.0 2 pls + 323.6 465.6 323.6 467.0 2 pls + 311.0 465.6 311.0 467.0 2 pls + 304.6 465.6 304.6 467.0 2 pls + 298.3 465.6 298.3 467.0 2 pls + 492.3 113.1 495.4 113.1 2 pls + 493.9 87.7 495.4 87.7 2 pls + 493.9 100.4 495.4 100.4 2 pls + 493.9 125.7 495.4 125.7 2 pls + 493.9 138.4 495.4 138.4 2 pls + 492.3 176.4 495.4 176.4 2 pls + 493.9 151.1 495.4 151.1 2 pls + 493.9 163.8 495.4 163.8 2 pls + 493.9 189.1 495.4 189.1 2 pls + 493.9 201.8 495.4 201.8 2 pls + 492.3 239.8 495.4 239.8 2 pls + 493.9 214.4 495.4 214.4 2 pls + 493.9 227.1 495.4 227.1 2 pls + 493.9 252.5 495.4 252.5 2 pls + 493.9 265.1 495.4 265.1 2 pls + 492.3 303.1 495.4 303.1 2 pls + 493.9 277.8 495.4 277.8 2 pls + 493.9 290.5 495.4 290.5 2 pls + 493.9 315.8 495.4 315.8 2 pls + 493.9 328.5 495.4 328.5 2 pls + 492.3 366.5 495.4 366.5 2 pls + 493.9 341.2 495.4 341.2 2 pls + 493.9 353.8 495.4 353.8 2 pls + 493.9 379.2 495.4 379.2 2 pls + 493.9 391.9 495.4 391.9 2 pls + 492.3 429.9 495.4 429.9 2 pls + 493.9 404.5 495.4 404.5 2 pls + 493.9 417.2 495.4 417.2 2 pls + 493.9 442.5 495.4 442.5 2 pls + 493.9 455.2 495.4 455.2 2 pls + s[ 294.5 308.8 323.2 337.5 351.9 366.2 380.6 394.9 409.3 423.6 438.0 452.3 466.7 481.0 495.4 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 495.4 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 495.4 481.0 466.7 452.3 438.0 423.6 409.3 394.9 380.6 366.2 351.9 337.5 323.2 308.8 294.5 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 294.5 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 323.01 61.90 moveto[ 323.0 323.0 362.7 362.7 ][ 61.9 74.0 74.0 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 325.9 330.5 328.0 329.3 330.1 330.5 331.0 331.0 330.5 329.7 328.4 327.2 325.9 325.5 325.1 ][ 72.3 72.3 69.0 + 69.0 68.6 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 338.5 334.3 333.9 334.3 335.5 336.8 338.1 338.9 339.3 339.3 338.9 338.1 336.8 335.5 334.3 333.9 333.5 ][ 72.3 + 72.3 68.6 69.0 69.4 69.4 69.0 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + 345.2 63.6 345.2 72.3 343.9 71.1 343.1 70.7 4 pls + 351.0 64.4 351.4 64.0 351.0 63.6 350.6 64.0 351.0 64.4 5 pls + 360.6 66.5 354.3 66.5 358.5 72.3 3 pls + 358.5 63.6 358.5 72.3 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 373.70 61.90 moveto[ 373.7 373.7 413.0 413.0 ][ 61.9 74.0 74.0 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 376.6 381.2 378.7 380.0 380.8 381.2 381.6 381.6 381.2 380.4 379.1 377.9 376.6 376.2 375.8 ][ 72.3 72.3 69.0 + 69.0 68.6 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 389.2 385.0 384.6 385.0 386.2 387.5 388.7 389.6 390.0 390.0 389.6 388.7 387.5 386.2 385.0 384.6 384.1 ][ 72.3 + 72.3 68.6 69.0 69.4 69.4 69.0 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + 395.8 63.6 395.8 72.3 394.6 71.1 393.8 70.7 4 pls + 401.7 64.4 402.1 64.0 401.7 63.6 401.3 64.0 401.7 64.4 5 pls + s[ 407.5 406.3 405.4 405.0 405.0 405.4 406.3 407.5 408.4 409.6 410.5 410.9 410.9 410.5 409.6 408.4 407.5 ][ 72.3 + 71.9 70.7 68.6 67.3 65.2 64.0 63.6 63.6 64.0 65.2 67.3 68.6 70.7 71.9 72.3 72.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 424.39 61.90 moveto[ 424.4 424.4 463.7 463.7 ][ 61.9 74.0 74.0 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 427.3 431.9 429.4 430.7 431.5 431.9 432.3 432.3 431.9 431.1 429.8 428.6 427.3 426.9 426.5 ][ 72.3 72.3 69.0 + 69.0 68.6 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 439.8 435.7 435.3 435.7 436.9 438.2 439.4 440.3 440.7 440.7 440.3 439.4 438.2 436.9 435.7 435.3 434.8 ][ 72.3 + 72.3 68.6 69.0 69.4 69.4 69.0 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 445.7 444.4 443.6 443.2 443.2 443.6 444.4 445.7 446.5 447.8 448.6 449.0 449.0 448.6 447.8 446.5 445.7 ][ 72.3 + 71.9 70.7 68.6 67.3 65.2 64.0 63.6 63.6 64.0 65.2 67.3 68.6 70.7 71.9 72.3 72.3 ]plong + 452.4 64.4 452.8 64.0 452.4 63.6 452.0 64.0 452.4 64.4 5 pls + s[ 461.1 460.7 459.5 458.6 457.4 456.6 456.1 456.1 456.6 457.4 458.6 459.1 460.3 461.1 461.6 461.6 461.1 460.3 + 459.1 458.6 457.4 456.6 456.1 ][ 71.1 71.9 72.3 72.3 71.9 70.7 68.6 66.5 64.8 64.0 63.6 63.6 64.0 + 64.8 66.1 66.5 67.7 68.6 69.0 69.0 68.6 67.7 66.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 475.08 61.90 moveto[ 475.1 475.1 514.3 514.3 ][ 61.9 74.0 74.0 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 478.0 482.6 480.1 481.3 482.2 482.6 483.0 483.0 482.6 481.8 480.5 479.3 478.0 477.6 477.2 ][ 72.3 72.3 69.0 + 69.0 68.6 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 490.5 486.4 485.9 486.4 487.6 488.9 490.1 491.0 491.4 491.4 491.0 490.1 488.9 487.6 486.4 485.9 485.5 ][ 72.3 + 72.3 68.6 69.0 69.4 69.4 69.0 68.2 66.9 66.1 64.8 64.0 63.6 63.6 64.0 64.4 65.2 ]plong + s[ 496.4 495.1 494.3 493.9 493.9 494.3 495.1 496.4 497.2 498.5 499.3 499.7 499.7 499.3 498.5 497.2 496.4 ][ 72.3 + 71.9 70.7 68.6 67.3 65.2 64.0 63.6 63.6 64.0 65.2 67.3 68.6 70.7 71.9 72.3 72.3 ]plong + 503.1 64.4 503.5 64.0 503.1 63.6 502.6 64.0 503.1 64.4 5 pls + s[ 506.8 506.8 507.2 507.7 508.5 510.2 511.0 511.4 511.8 511.8 511.4 510.6 506.4 512.3 ][ 70.2 70.7 71.5 71.9 + 72.3 72.3 71.9 71.5 70.7 69.8 69.0 67.7 63.6 63.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 260.32 106.17 moveto[ 260.3 260.3 293.3 293.3 ][ 106.2 118.3 118.3 106.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 269.9 111.6 262.4 111.6 2 pls + s[ 275.4 274.1 273.3 272.9 272.9 273.3 274.1 275.4 276.2 277.4 278.3 278.7 278.7 278.3 277.4 276.2 275.4 ][ 116.6 + 116.2 114.9 112.9 111.6 109.5 108.3 107.8 107.8 108.3 109.5 111.6 112.9 114.9 116.2 116.6 116.6 ]plong + 282.0 108.7 282.5 108.3 282.0 107.8 281.6 108.3 282.0 108.7 5 pls + s[ 290.4 286.2 285.8 286.2 287.5 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.5 286.2 285.8 285.4 ][ 116.6 + 116.6 112.9 113.3 113.7 113.7 113.3 112.4 111.2 110.3 109.1 108.3 107.8 107.8 108.3 108.7 109.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.77 169.53 moveto[ 270.8 270.8 293.3 293.3 ][ 169.5 181.6 181.6 169.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 275.4 274.1 273.3 272.9 272.9 273.3 274.1 275.4 276.2 277.4 278.3 278.7 278.7 278.3 277.4 276.2 275.4 ][ 180.0 + 179.6 178.3 176.2 175.0 172.9 171.6 171.2 171.2 171.6 172.9 175.0 176.2 178.3 179.6 180.0 180.0 ]plong + 282.0 172.0 282.5 171.6 282.0 171.2 281.6 171.6 282.0 172.0 5 pls + s[ 287.9 286.6 285.8 285.4 285.4 285.8 286.6 287.9 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.9 ][ 180.0 + 179.6 178.3 176.2 175.0 172.9 171.6 171.2 171.2 171.6 172.9 175.0 176.2 178.3 179.6 180.0 180.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.77 232.89 moveto[ 270.8 270.8 293.3 293.3 ][ 232.9 245.0 245.0 232.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 275.4 274.1 273.3 272.9 272.9 273.3 274.1 275.4 276.2 277.4 278.3 278.7 278.7 278.3 277.4 276.2 275.4 ][ 243.3 + 242.9 241.7 239.6 238.3 236.2 235.0 234.6 234.6 235.0 236.2 238.3 239.6 241.7 242.9 243.3 243.3 ]plong + 282.0 235.4 282.5 235.0 282.0 234.6 281.6 235.0 282.0 235.4 5 pls + s[ 290.4 286.2 285.8 286.2 287.5 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.5 286.2 285.8 285.4 ][ 243.3 + 243.3 239.6 240.0 240.4 240.4 240.0 239.2 237.9 237.1 235.8 235.0 234.6 234.6 235.0 235.4 236.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 272.02 296.26 moveto[ 272.0 272.0 293.3 293.3 ][ 296.3 308.4 308.4 296.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 276.2 297.9 276.2 306.7 274.9 305.4 274.1 305.0 4 pls + 282.0 298.8 282.5 298.3 282.0 297.9 281.6 298.3 282.0 298.8 5 pls + s[ 287.9 286.6 285.8 285.4 285.4 285.8 286.6 287.9 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.9 ][ 306.7 + 306.3 305.0 302.9 301.7 299.6 298.3 297.9 297.9 298.3 299.6 301.7 302.9 305.0 306.3 306.7 306.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 272.02 359.62 moveto[ 272.0 272.0 293.3 293.3 ][ 359.6 371.7 371.7 359.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 276.2 361.3 276.2 370.1 274.9 368.8 274.1 368.4 4 pls + 282.0 362.1 282.5 361.7 282.0 361.3 281.6 361.7 282.0 362.1 5 pls + s[ 290.4 286.2 285.8 286.2 287.5 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.5 286.2 285.8 285.4 ][ 370.1 + 370.1 366.3 366.7 367.1 367.1 366.7 365.9 364.6 363.8 362.5 361.7 361.3 361.3 361.7 362.1 363.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.77 422.98 moveto[ 270.8 270.8 293.3 293.3 ][ 423.0 435.1 435.1 423.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 273.3 273.3 273.7 274.1 274.9 276.6 277.4 277.9 278.3 278.3 277.9 277.0 272.9 278.7 ][ 431.3 431.8 432.6 433.0 + 433.4 433.4 433.0 432.6 431.8 430.9 430.1 428.8 424.6 424.6 ]plong + 282.0 425.5 282.5 425.1 282.0 424.6 281.6 425.1 282.0 425.5 5 pls + s[ 287.9 286.6 285.8 285.4 285.4 285.8 286.6 287.9 288.7 290.0 290.8 291.2 291.2 290.8 290.0 288.7 287.9 ][ 433.4 + 433.0 431.8 429.7 428.4 426.3 425.1 424.6 424.6 425.1 426.3 428.4 429.7 431.8 433.0 433.4 433.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 335.61 44.43 moveto[ 335.6 335.6 454.2 454.2 ][ 44.4 59.9 59.9 44.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 344.0 343.5 342.7 341.9 340.2 339.4 338.5 338.1 337.7 337.7 338.1 338.5 339.4 340.2 341.9 342.7 343.5 344.0 + 344.0 ][ 55.7 56.5 57.4 57.8 57.8 57.4 56.5 55.7 54.5 52.4 51.1 50.3 49.4 49.0 49.0 49.4 50.3 + 51.1 52.4 ]plong + 344.0 52.4 341.9 52.4 2 pls + 351.5 49.0 351.5 54.9 2 pls + s[ 351.5 350.6 349.8 348.6 347.7 346.9 346.5 346.5 346.9 347.7 348.6 349.8 350.6 351.5 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 354.8 49.0 354.8 57.8 2 pls + 362.8 49.0 362.8 54.9 2 pls + s[ 362.8 361.9 361.1 359.8 359.0 358.2 357.7 357.7 358.2 359.0 359.8 361.1 361.9 362.8 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + s[ 370.7 369.9 369.0 367.8 366.9 366.1 365.7 365.7 366.1 366.9 367.8 369.0 369.9 370.7 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 376.1 49.0 375.3 49.0 374.5 49.4 374.0 50.7 374.0 57.8 5 pls + 375.7 54.9 372.8 54.9 2 pls + 378.2 57.8 378.6 58.2 379.1 57.8 378.6 57.4 378.2 57.8 5 pls + 378.6 49.0 378.6 54.9 2 pls + s[ 386.6 385.7 384.9 383.6 382.8 382.0 381.6 381.6 382.0 382.8 383.6 384.9 385.7 386.6 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 396.2 49.0 396.2 57.8 2 pls + s[ 401.2 400.4 399.5 399.1 399.1 399.5 400.4 401.2 402.4 403.3 404.1 404.5 404.5 404.1 403.3 402.4 401.2 ][ 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 51.5 52.4 53.6 54.5 54.9 54.9 ]plong + 407.5 49.0 407.5 54.9 2 pls + s[ 407.5 408.7 409.5 410.8 411.6 412.0 412.0 ][ 53.2 54.5 54.9 54.9 54.5 53.2 49.0 ]plong + s[ 420.0 420.0 419.6 419.1 418.3 417.1 416.2 ][ 54.9 48.2 46.9 46.5 46.1 46.1 46.5 ]plong + s[ 420.0 419.1 418.3 417.1 416.2 415.4 415.0 415.0 415.4 416.2 417.1 418.3 419.1 420.0 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + 422.9 57.8 423.3 58.2 423.7 57.8 423.3 57.4 422.9 57.8 5 pls + 423.3 49.0 423.3 54.9 2 pls + 429.2 49.0 428.3 49.0 427.5 49.4 427.1 50.7 427.1 57.8 5 pls + 428.8 54.9 425.8 54.9 2 pls + s[ 431.7 431.7 432.1 432.9 434.2 435.0 436.3 ][ 54.9 50.7 49.4 49.0 49.0 49.4 50.7 ]plong + 436.3 49.0 436.3 54.9 2 pls + 444.2 49.0 444.2 57.8 2 pls + s[ 444.2 443.4 442.5 441.3 440.4 439.6 439.2 439.2 439.6 440.4 441.3 442.5 443.4 444.2 ][ 53.6 54.5 54.9 54.9 + 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong + s[ 447.1 452.1 452.1 451.7 451.3 450.5 449.2 448.4 447.5 447.1 447.1 447.5 448.4 449.2 450.5 451.3 452.1 ][ 52.4 + 52.4 53.2 54.0 54.5 54.9 54.9 54.5 53.6 52.4 51.5 50.3 49.4 49.0 49.0 49.4 50.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 256.65 217.59 moveto[ 256.6 244.1 244.1 256.6 ][ 217.6 217.6 325.4 325.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 248.3 247.5 246.6 246.2 246.2 246.6 247.5 248.3 249.5 251.6 252.9 253.7 254.6 255.0 255.0 254.6 253.7 252.9 + 251.6 ][ 225.9 225.5 224.7 223.9 222.2 221.4 220.5 220.1 219.7 219.7 220.1 220.5 221.4 222.2 223.9 224.7 225.5 + 225.9 225.9 ]plong + 251.6 225.9 251.6 223.9 2 pls + 255.0 233.5 249.1 233.5 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 233.5 232.6 231.8 230.5 + 229.7 228.9 228.5 228.5 228.9 229.7 230.5 231.8 232.6 233.5 ]plong + 255.0 236.8 246.2 236.8 2 pls + 255.0 244.7 249.1 244.7 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 244.7 243.9 243.1 241.8 + 241.0 240.1 239.7 239.7 240.1 241.0 241.8 243.1 243.9 244.7 ]plong + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 252.7 251.8 251.0 249.8 + 248.9 248.1 247.7 247.7 248.1 248.9 249.8 251.0 251.8 252.7 ]plong + 255.0 258.1 255.0 257.3 254.6 256.4 253.3 256.0 246.2 256.0 5 pls + 249.1 257.7 249.1 254.8 2 pls + 246.2 260.2 245.8 260.6 246.2 261.0 246.6 260.6 246.2 260.2 5 pls + 255.0 260.6 249.1 260.6 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 268.5 267.7 266.9 265.6 + 264.8 264.0 263.5 263.5 264.0 264.8 265.6 266.9 267.7 268.5 ]plong + 255.0 278.2 246.2 278.2 2 pls + 255.0 286.1 249.1 286.1 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 286.1 285.3 284.4 283.2 + 282.3 281.5 281.1 281.1 281.5 282.3 283.2 284.4 285.3 286.1 ]plong + 255.0 291.9 255.0 291.1 254.6 290.3 253.3 289.8 246.2 289.8 5 pls + 249.1 291.5 249.1 288.6 2 pls + 246.2 294.0 245.8 294.4 246.2 294.9 246.6 294.4 246.2 294.0 5 pls + 255.0 294.4 249.1 294.4 2 pls + 255.0 300.3 255.0 299.5 254.6 298.6 253.3 298.2 246.2 298.2 5 pls + 249.1 299.9 249.1 296.9 2 pls + s[ 249.1 253.3 254.6 255.0 255.0 254.6 253.3 ][ 302.8 302.8 303.2 304.1 305.3 306.1 307.4 ]plong + 255.0 307.4 249.1 307.4 2 pls + 255.0 315.3 246.2 315.3 2 pls + s[ 250.4 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 315.3 314.5 313.7 312.4 + 311.6 310.7 310.3 310.3 310.7 311.6 312.4 313.7 314.5 315.3 ]plong + s[ 251.6 251.6 250.8 250.0 249.5 249.1 249.1 249.5 250.4 251.6 252.5 253.7 254.6 255.0 255.0 254.6 253.7 ][ 318.3 + 323.3 323.3 322.8 322.4 321.6 320.3 319.5 318.7 318.3 318.3 318.7 319.5 320.3 321.6 322.4 323.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 477.00 moveto[ 360.9 360.9 435.2 435.2 ][ 477.0 489.1 489.1 477.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 478.7 366.3 487.4 2 pls + 369.7 478.7 366.3 487.4 2 pls + 368.4 481.6 364.2 481.6 2 pls + 378.4 478.7 378.4 487.4 2 pls + 383.9 487.4 378.4 487.4 2 pls + 381.8 483.3 378.4 483.3 2 pls + 385.9 478.7 385.9 487.4 2 pls + 391.0 478.7 391.0 487.4 2 pls + 393.9 487.4 388.0 487.4 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 486.2 487.0 487.4 487.4 487.0 486.2 485.4 484.5 484.1 483.7 482.8 482.4 482.0 481.2 479.9 479.1 + 478.7 478.7 479.1 479.9 ]plong + 413.5 478.7 412.7 478.7 411.8 479.1 411.4 480.3 411.4 487.4 5 pls + 413.1 484.5 410.2 484.5 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 482.0 + 482.0 482.8 483.7 484.1 484.5 484.5 484.1 483.3 482.0 481.2 479.9 479.1 478.7 478.7 479.1 479.9 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 483.3 + 484.1 484.5 484.5 484.1 483.3 482.4 482.0 481.6 481.2 480.3 479.9 479.1 478.7 478.7 479.1 479.9 ]plong + 433.1 478.7 432.3 478.7 431.5 479.1 431.1 480.3 431.1 487.4 5 pls + 432.7 484.5 429.8 484.5 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/car6.attr b/ast_tester/car6.attr new file mode 100644 index 0000000..e69de29 diff --git a/ast_tester/car6.box b/ast_tester/car6.box new file mode 100644 index 0000000..e5f0493 --- /dev/null +++ b/ast_tester/car6.box @@ -0,0 +1 @@ +0 0 5401 1741 diff --git a/ast_tester/car6.head b/ast_tester/car6.head new file mode 100644 index 0000000..5356fa5 --- /dev/null +++ b/ast_tester/car6.head @@ -0,0 +1,39 @@ +FITS headers in CHIPASS_Equ.fits: +SIMPLE = T / file does conform to FITS standard +BITPIX = -32 / IEEE (big-endian) 32-bit floating point data +NAXIS = 2 / number of data axes +NAXIS1 = 5401 / length of data axis 1 +NAXIS2 = 1741 / length of data axis 2 +EXTEND = T / FITS dataset may contain extensions +COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy +COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H +BUNIT = 'mK ' / Using 438.5 mK/(Jy/beam) plus 3300 mK offset +CTYPE1 = 'RA---CAR' +CRPIX1 = 2701. +CDELT1 = -0.0666666666666667 +CRVAL1 = 180. +CTYPE2 = 'DEC--CAR' +CRPIX2 = 1351. +CDELT2 = 0.0666666666666667 +CRVAL2 = 0. +LONPOLE = 0. / Native longitude of celestial pole +LATPOLE = 90. / Native latitude of celestial pole +RADESYS = 'FK5 ' / Equatorial coordinate system +EQUINOX = 2000.0 / Equinox of equatorial coordinates +BMAJ = 0.24000 / [deg] Beam major axis +BMIN = 0.24000 / [deg] Beam minor axis +BPA = 0.0 / [deg] Beam position angle +FREQENCY= 1.3945E+09 / [Hz] Centre frequency +BANDWID = 6.4E+07 / [Hz] Bandwidth +DATE = '2013-04-20T13:20:39' / file creation date (YYYY-MM-DDThh:mm:ss UT) +COMMENT ------------------------------------------------------------------------ +COMMENT This file contains a 1.4 GHz continuum map of the sky south of dec +26 +COMMENT produced from HIPASS and ZOA data. These surveys were undertaken with +COMMENT the 13-beam multibeam system on the Parkes radio telescope. +COMMENT Details may be found in the following paper: +COMMENT Calabretta, M.R., Staveley-Smith, L., and Barnes, D.G., (2013) +COMMENT PASA (in preparation). +COMMENT ------------------------------------------------------------------------ +COMMENT THIS IS A PRE-PUBLICATION IMAGE, FOR RESTRICTED DISTRIBUTION ONLY +COMMENT ------------------------------------------------------------------------ +END diff --git a/ast_tester/car6.ps b/ast_tester/car6.ps new file mode 100644 index 0000000..2b05323 --- /dev/null +++ b/ast_tester/car6.ps @@ -0,0 +1,1084 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Tue Mar 24 21:29:13 2015 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 24/03/1915 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 525.2 525.2 525.2 525.2 525.2 525.2 525.2 525.2 525.2 525.2 525.2 525.2 525.2 525.2 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 460.1 460.1 460.1 460.1 460.1 460.1 460.1 460.1 460.1 460.1 460.1 460.1 460.1 460.1 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 427.5 427.5 427.5 427.5 427.5 427.5 427.5 427.5 427.5 427.5 427.5 427.5 427.5 427.5 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 395.0 395.0 395.0 395.0 395.0 395.0 395.0 395.0 395.0 395.0 395.0 395.0 395.0 395.0 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 362.4 362.4 362.4 362.4 362.4 362.4 362.4 362.4 362.4 362.4 362.4 362.4 362.4 362.4 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 329.8 329.8 329.8 329.8 329.8 329.8 329.8 329.8 329.8 329.8 329.8 329.8 329.8 329.8 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 264.7 264.7 264.7 264.7 264.7 264.7 264.7 264.7 264.7 264.7 264.7 264.7 264.7 264.7 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 208.6 218.6 228.7 238.8 + 248.9 259.0 269.0 279.1 289.2 299.3 309.4 319.5 329.5 334.5 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 + 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 219.4 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 + 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 + 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 + 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 252.0 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 + 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 262.8 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 + 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 273.7 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 + 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 284.6 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 + 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 295.4 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 + 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 306.3 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 + 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 317.1 ]plong + s[ 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 204.2 204.0 203.8 203.7 203.5 203.3 + 203.2 203.0 202.8 202.7 202.5 202.3 202.2 202.0 201.9 201.7 201.5 201.4 201.2 201.0 200.9 200.7 200.5 200.4 + 200.2 200.0 199.9 ][ 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 ]plong + s[ 590.4 588.1 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 562.5 560.1 557.8 525.2 492.7 460.1 + 427.5 395.0 362.4 329.8 297.2 264.7 232.1 229.8 227.4 225.1 222.8 220.5 218.1 215.8 213.5 211.2 208.8 206.5 + 204.2 201.9 199.5 201.9 204.2 206.5 208.8 211.2 213.5 215.8 218.1 220.5 222.8 225.1 227.4 229.8 232.1 264.7 + 297.2 329.8 362.4 395.0 427.5 460.1 492.7 525.2 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 + 581.1 583.4 585.7 588.1 590.4 ][ 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 ]plong + s[ 590.1 589.9 589.7 589.6 589.4 589.2 589.1 588.9 588.7 588.6 588.4 588.2 588.1 587.9 587.7 587.6 587.4 587.2 + 587.1 586.9 586.7 586.6 586.4 586.2 586.1 585.9 585.7 583.4 581.1 578.8 576.4 574.1 571.8 569.5 567.1 564.8 + 562.5 560.1 557.8 560.1 562.5 564.8 567.1 569.5 571.8 574.1 576.4 578.8 581.1 583.4 585.7 585.9 586.1 586.2 + 586.4 586.6 586.7 586.9 587.1 587.2 587.4 587.6 587.7 587.9 588.1 588.2 588.4 588.6 588.7 588.9 589.1 589.2 + 589.4 589.6 589.7 589.9 590.1 ][ 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 + 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 328.0 ]plong + 582.3 333.6 582.3 334.5 2 pls + 574.1 334.5 574.1 333.6 574.1 334.5 3 pls + 566.0 334.5 566.0 333.6 566.0 334.5 3 pls + 549.7 334.5 549.7 333.6 549.7 334.5 3 pls + 541.5 334.5 541.5 333.6 541.5 334.5 3 pls + 533.4 334.5 533.4 333.6 533.4 334.5 3 pls + 517.1 334.5 517.1 333.6 517.1 334.5 3 pls + 509.0 334.5 509.0 333.6 509.0 334.5 3 pls + 500.8 334.5 500.8 333.6 500.8 334.5 3 pls + 484.5 334.5 484.5 333.6 484.5 334.5 3 pls + 476.4 334.5 476.4 333.6 476.4 334.5 3 pls + 468.2 334.5 468.2 333.6 468.2 334.5 3 pls + 452.0 334.5 452.0 333.6 452.0 334.5 3 pls + 443.8 334.5 443.8 333.6 443.8 334.5 3 pls + 435.7 334.5 435.7 333.6 435.7 334.5 3 pls + 419.4 334.5 419.4 333.6 419.4 334.5 3 pls + 411.2 334.5 411.2 333.6 411.2 334.5 3 pls + 403.1 334.5 403.1 333.6 403.1 334.5 3 pls + 386.8 334.5 386.8 333.6 386.8 334.5 3 pls + 378.7 334.5 378.7 333.6 378.7 334.5 3 pls + 370.5 334.5 370.5 333.6 370.5 334.5 3 pls + 354.2 334.5 354.2 333.6 354.2 334.5 3 pls + 346.1 334.5 346.1 333.6 346.1 334.5 3 pls + 338.0 334.5 338.0 333.6 338.0 334.5 3 pls + 321.7 334.5 321.7 333.6 321.7 334.5 3 pls + 313.5 334.5 313.5 333.6 313.5 334.5 3 pls + 305.4 334.5 305.4 333.6 305.4 334.5 3 pls + 289.1 334.5 289.1 333.6 289.1 334.5 3 pls + 281.0 334.5 281.0 333.6 281.0 334.5 3 pls + 272.8 334.5 272.8 333.6 272.8 334.5 3 pls + 256.5 334.5 256.5 333.6 256.5 334.5 3 pls + 248.4 334.5 248.4 333.6 248.4 334.5 3 pls + 240.2 334.5 240.2 333.6 240.2 334.5 3 pls + 224.0 334.5 224.0 333.6 224.0 334.5 3 pls + 215.8 334.5 215.8 333.6 215.8 334.5 3 pls + 207.7 334.5 207.7 333.6 207.7 334.5 3 pls + s[ 590.3 590.1 590.0 589.8 589.7 589.5 589.4 589.3 589.1 589.0 588.8 588.7 588.5 588.4 586.4 584.4 582.4 580.4 + 578.4 576.4 574.4 572.4 570.4 568.5 566.5 564.5 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 + 283.2 255.3 227.4 225.4 223.4 221.4 219.4 217.4 215.4 213.4 211.4 209.4 207.4 205.4 203.4 201.4 201.3 201.2 + 201.0 200.9 200.7 200.6 200.4 200.3 200.2 200.0 199.9 199.7 199.6 ][ 334.5 334.5 334.5 334.5 334.5 334.5 334.5 + 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 + 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 + 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 334.5 + 334.5 334.5 334.5 334.5 ]plong + s[ 199.9 200.3 201.2 202.0 202.9 203.8 204.6 205.5 206.4 207.2 208.1 209.0 209.8 210.7 211.6 212.4 213.3 214.2 + 215.0 215.9 216.7 217.6 218.5 219.3 220.2 221.1 221.9 222.8 223.7 224.5 225.4 226.3 227.1 228.0 228.9 229.7 + 230.6 231.5 232.3 233.2 234.0 234.9 235.8 236.6 237.5 238.4 239.2 240.1 241.0 241.8 242.7 243.6 244.4 245.3 + 246.2 247.0 247.9 248.8 249.6 250.5 251.3 252.2 253.1 253.9 254.8 255.7 256.5 257.4 258.3 259.1 260.0 260.9 + 261.7 262.6 263.5 264.3 265.2 266.1 266.9 267.8 268.6 269.5 270.4 271.2 272.1 273.0 273.8 274.7 275.6 276.4 + 277.3 278.2 279.0 279.9 280.8 281.6 282.5 283.3 284.2 285.1 285.9 286.8 287.7 288.5 289.4 290.3 291.1 292.0 + 292.9 293.7 294.6 295.5 296.3 297.2 298.1 298.9 299.8 300.6 301.5 302.4 303.2 304.1 305.0 305.8 306.7 307.6 + 308.4 309.3 310.2 311.0 311.9 312.8 313.6 314.5 315.4 316.2 317.1 317.9 318.8 319.7 320.5 321.4 322.3 323.1 + 324.0 324.9 325.7 326.6 327.5 328.3 329.2 330.1 330.9 331.8 332.6 333.5 334.4 335.2 336.1 337.0 337.8 338.7 + 339.6 340.4 341.3 342.2 343.0 343.9 344.8 345.6 346.5 347.4 348.2 349.1 349.9 350.8 351.7 352.5 353.4 354.3 + 355.1 356.0 356.9 357.7 358.6 359.5 360.3 361.2 362.1 362.9 363.8 364.7 365.5 366.4 367.2 368.1 369.0 369.8 + 370.7 371.6 ][ 208.8 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 ]plong + s[ 371.6 372.4 373.3 374.2 375.0 375.9 376.8 377.6 378.5 379.4 380.2 381.1 381.9 382.8 383.7 384.5 385.4 386.3 + 387.1 388.0 388.9 389.7 390.6 391.5 392.3 393.2 394.1 394.9 395.8 396.7 397.5 398.4 399.2 400.1 401.0 401.8 + 402.7 403.6 404.4 405.3 406.2 407.0 407.9 408.8 409.6 410.5 411.4 412.2 413.1 414.0 414.8 415.7 416.5 417.4 + 418.3 419.1 420.0 420.9 421.7 422.6 423.5 424.3 425.2 426.1 426.9 427.8 428.7 429.5 430.4 431.2 432.1 433.0 + 433.8 434.7 435.6 436.4 437.3 438.2 439.0 439.9 440.8 441.6 442.5 443.4 444.2 445.1 446.0 446.8 447.7 448.5 + 449.4 450.3 451.1 452.0 452.9 453.7 454.6 455.5 456.3 457.2 458.1 458.9 459.8 460.7 461.5 462.4 463.3 464.1 + 465.0 465.8 466.7 467.6 468.4 469.3 470.2 471.0 471.9 472.8 473.6 474.5 475.4 476.2 477.1 478.0 478.8 479.7 + 480.5 481.4 482.3 483.1 484.0 484.9 485.7 486.6 487.5 488.3 489.2 490.1 490.9 491.8 492.7 493.5 494.4 495.3 + 496.1 497.0 497.8 498.7 499.6 500.4 501.3 502.2 503.0 503.9 504.8 505.6 506.5 507.4 508.2 509.1 510.0 510.8 + 511.7 512.6 513.4 514.3 515.1 516.0 516.9 517.7 518.6 519.5 520.3 521.2 522.1 522.9 523.8 524.7 525.5 526.4 + 527.3 528.1 529.0 529.9 530.7 531.6 532.4 533.3 534.2 535.0 535.9 536.8 537.6 538.5 539.4 540.2 541.1 542.0 + 542.8 543.7 ][ 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 ]plong + s[ 543.7 544.6 545.4 546.3 547.1 548.0 548.9 549.7 550.6 551.5 552.3 553.2 554.1 554.9 555.8 556.7 557.5 558.4 + 559.3 560.1 561.0 561.9 562.7 563.6 564.4 565.3 566.2 567.0 567.9 568.8 569.6 570.5 571.4 572.2 573.1 574.0 + 574.8 575.7 576.6 577.4 578.3 579.2 580.0 580.9 581.7 582.6 583.5 584.3 585.2 586.1 586.9 587.8 588.7 589.5 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 ][ 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 208.6 + 208.6 208.6 208.8 209.0 209.3 209.6 209.9 210.2 210.4 210.7 211.0 211.3 211.5 211.8 212.1 212.4 212.7 212.9 + 213.2 213.5 213.8 214.1 214.3 214.6 214.9 215.2 215.4 215.7 216.0 216.3 216.6 216.8 217.1 217.4 217.7 218.0 + 218.2 218.5 218.8 219.1 219.4 219.6 219.9 220.2 220.5 220.7 221.0 221.3 221.6 221.9 222.1 222.4 222.7 223.0 + 223.3 223.5 223.8 224.1 224.4 224.6 224.9 225.2 225.5 225.8 226.0 226.3 226.6 226.9 227.2 227.4 227.7 228.0 + 228.3 228.6 228.8 229.1 229.4 229.7 229.9 230.2 230.5 230.8 231.1 231.3 231.6 231.9 232.2 232.5 232.7 233.0 + 233.3 233.6 233.8 234.1 234.4 234.7 235.0 235.2 235.5 235.8 236.1 236.4 236.6 236.9 237.2 237.5 237.8 238.0 + 238.3 238.6 238.9 239.1 239.4 239.7 240.0 240.3 240.5 240.8 241.1 241.4 241.7 241.9 242.2 242.5 242.8 243.1 + 243.3 243.6 243.9 244.2 244.4 244.7 245.0 245.3 245.6 245.8 246.1 246.4 246.7 247.0 247.2 247.5 247.8 248.1 + 248.3 248.6 248.9 249.2 ]plong + s[ 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 ][ 249.2 249.5 249.7 250.0 250.3 250.6 250.9 251.1 251.4 251.7 252.0 252.3 252.5 252.8 253.1 253.4 + 253.6 253.9 254.2 254.5 254.8 255.0 255.3 255.6 255.9 256.2 256.4 256.7 257.0 257.3 257.5 257.8 258.1 258.4 + 258.7 258.9 259.2 259.5 259.8 260.1 260.3 260.6 260.9 261.2 261.5 261.7 262.0 262.3 262.6 262.8 263.1 263.4 + 263.7 264.0 264.2 264.5 264.8 265.1 265.4 265.6 265.9 266.2 266.5 266.7 267.0 267.3 267.6 267.9 268.1 268.4 + 268.7 269.0 269.3 269.5 269.8 270.1 270.4 270.7 270.9 271.2 271.5 271.8 272.0 272.3 272.6 272.9 273.2 273.4 + 273.7 274.0 274.3 274.6 274.8 275.1 275.4 275.7 275.9 276.2 276.5 276.8 277.1 277.3 277.6 277.9 278.2 278.5 + 278.7 279.0 279.3 279.6 279.9 280.1 280.4 280.7 281.0 281.2 281.5 281.8 282.1 282.4 282.6 282.9 283.2 283.5 + 283.8 284.0 284.3 284.6 284.9 285.1 285.4 285.7 286.0 286.3 286.5 286.8 287.1 287.4 287.7 287.9 288.2 288.5 + 288.8 289.1 289.3 289.6 289.9 290.2 290.4 290.7 291.0 291.3 291.6 291.8 292.1 292.4 292.7 293.0 293.2 293.5 + 293.8 294.1 294.4 294.6 294.9 295.2 295.5 295.7 296.0 296.3 296.6 296.9 297.1 297.4 297.7 298.0 298.3 298.5 + 298.8 299.1 299.4 299.6 299.9 300.2 300.5 300.8 301.0 301.3 301.6 301.9 302.2 302.4 302.7 303.0 303.3 303.6 + 303.8 304.1 304.4 304.7 ]plong + s[ 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 + 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 590.0 ][ + 304.7 304.9 305.2 305.5 305.8 306.1 306.3 306.6 306.9 307.2 307.5 307.7 308.0 308.3 308.6 308.8 309.1 309.4 + 309.7 310.0 310.2 310.5 310.8 311.1 311.4 311.6 311.9 312.2 312.5 312.8 313.0 313.3 313.6 313.9 314.1 314.4 + 314.7 315.0 315.3 315.5 315.8 316.1 316.4 316.7 316.9 317.2 317.5 317.8 318.0 318.3 318.6 318.9 319.2 319.4 + 319.7 320.0 320.3 320.6 320.8 321.1 321.4 321.7 322.0 322.2 322.5 322.8 323.1 323.3 323.6 323.9 324.2 324.5 + 324.7 325.0 325.3 325.6 325.9 326.1 326.4 326.7 327.0 327.2 327.5 327.8 328.1 328.4 328.6 328.9 329.2 329.5 + 329.8 330.0 330.3 330.6 330.9 331.2 331.4 331.7 332.0 332.3 332.5 332.8 333.1 333.4 333.7 333.9 334.2 334.5 ]plong + s[ 199.9 200.3 201.2 200.3 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 ][ 208.8 208.6 208.6 208.6 208.8 209.0 209.3 209.6 209.9 210.2 210.4 210.7 211.0 211.3 211.5 211.8 + 212.1 212.4 212.7 212.9 213.2 213.5 213.8 214.1 214.3 214.6 214.9 215.2 215.4 215.7 216.0 216.3 216.6 216.8 + 217.1 217.4 217.7 218.0 218.2 218.5 218.8 219.1 219.4 219.6 219.9 220.2 220.5 220.7 221.0 221.3 221.6 221.9 + 222.1 222.4 222.7 223.0 223.3 223.5 223.8 224.1 224.4 224.6 224.9 225.2 225.5 225.8 226.0 226.3 226.6 226.9 + 227.2 227.4 227.7 228.0 228.3 228.6 228.8 229.1 229.4 229.7 229.9 230.2 230.5 230.8 231.1 231.3 231.6 231.9 + 232.2 232.5 232.7 233.0 233.3 233.6 233.8 234.1 234.4 234.7 235.0 235.2 235.5 235.8 236.1 236.4 236.6 236.9 + 237.2 237.5 237.8 238.0 238.3 238.6 238.9 239.1 239.4 239.7 240.0 240.3 240.5 240.8 241.1 241.4 241.7 241.9 + 242.2 242.5 242.8 243.1 243.3 243.6 243.9 244.2 244.4 244.7 245.0 245.3 245.6 245.8 246.1 246.4 246.7 247.0 + 247.2 247.5 247.8 248.1 248.3 248.6 248.9 249.2 249.5 249.7 250.0 250.3 250.6 250.9 251.1 251.4 251.7 252.0 + 252.3 252.5 252.8 253.1 253.4 253.6 253.9 254.2 254.5 254.8 255.0 255.3 255.6 255.9 256.2 256.4 256.7 257.0 + 257.3 257.5 257.8 258.1 258.4 258.7 258.9 259.2 259.5 259.8 260.1 260.3 260.6 260.9 261.2 261.5 261.7 262.0 + 262.3 262.6 262.8 263.1 ]plong + s[ 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 ][ 263.1 263.4 263.7 264.0 264.2 264.5 264.8 265.1 265.4 265.6 265.9 266.2 266.5 266.7 267.0 267.3 + 267.6 267.9 268.1 268.4 268.7 269.0 269.3 269.5 269.8 270.1 270.4 270.7 270.9 271.2 271.5 271.8 272.0 272.3 + 272.6 272.9 273.2 273.4 273.7 274.0 274.3 274.6 274.8 275.1 275.4 275.7 275.9 276.2 276.5 276.8 277.1 277.3 + 277.6 277.9 278.2 278.5 278.7 279.0 279.3 279.6 279.9 280.1 280.4 280.7 281.0 281.2 281.5 281.8 282.1 282.4 + 282.6 282.9 283.2 283.5 283.8 284.0 284.3 284.6 284.9 285.1 285.4 285.7 286.0 286.3 286.5 286.8 287.1 287.4 + 287.7 287.9 288.2 288.5 288.8 289.1 289.3 289.6 289.9 290.2 290.4 290.7 291.0 291.3 291.6 291.8 292.1 292.4 + 292.7 293.0 293.2 293.5 293.8 294.1 294.4 294.6 294.9 295.2 295.5 295.7 296.0 296.3 296.6 296.9 297.1 297.4 + 297.7 298.0 298.3 298.5 298.8 299.1 299.4 299.6 299.9 300.2 300.5 300.8 301.0 301.3 301.6 301.9 302.2 302.4 + 302.7 303.0 303.3 303.6 303.8 304.1 304.4 304.7 304.9 305.2 305.5 305.8 306.1 306.3 306.6 306.9 307.2 307.5 + 307.7 308.0 308.3 308.6 308.8 309.1 309.4 309.7 310.0 310.2 310.5 310.8 311.1 311.4 311.6 311.9 312.2 312.5 + 312.8 313.0 313.3 313.6 313.9 314.1 314.4 314.7 315.0 315.3 315.5 315.8 316.1 316.4 316.7 316.9 317.2 317.5 + 317.8 318.0 318.3 318.6 ]plong + s[ 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 199.9 + 199.9 199.9 199.9 199.9 ][ 318.6 318.9 319.2 319.4 319.7 320.0 320.3 320.6 320.8 321.1 321.4 321.7 322.0 322.2 + 322.5 322.8 323.1 323.3 323.6 323.9 324.2 324.5 324.7 325.0 325.3 325.6 325.9 326.1 326.4 326.7 327.0 327.2 + 327.5 327.8 328.1 328.4 328.6 328.9 329.2 329.5 329.8 330.0 330.3 330.6 330.9 331.2 331.4 331.7 332.0 332.3 + 332.5 332.8 333.1 333.4 333.7 333.9 334.2 334.5 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 194.51 195.11 moveto[ 194.5 194.5 204.5 204.5 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 199.1 197.9 197.0 196.6 196.6 197.0 197.9 199.1 199.9 201.2 202.0 202.4 202.4 202.0 201.2 199.9 199.1 ][ 205.5 + 205.1 203.9 201.8 200.5 198.4 197.2 196.8 196.8 197.2 198.4 200.5 201.8 203.9 205.1 205.5 205.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 222.91 195.11 moveto[ 222.9 222.9 241.3 241.3 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 225.4 225.4 225.8 226.2 227.1 228.8 229.6 230.0 230.4 230.4 230.0 229.2 225.0 230.8 ][ 203.5 203.9 204.7 205.1 + 205.5 205.5 205.1 204.7 203.9 203.0 202.2 201.0 196.8 196.8 ]plong + s[ 233.8 233.8 234.2 234.6 235.4 237.1 237.9 238.4 238.8 238.8 238.4 237.5 233.3 239.2 ][ 203.5 203.9 204.7 205.1 + 205.5 205.5 205.1 204.7 203.9 203.0 202.2 201.0 196.8 196.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 255.48 195.11 moveto[ 255.5 255.5 273.9 273.9 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 258.0 258.0 258.4 258.8 259.7 261.3 262.2 262.6 263.0 263.0 262.6 261.7 257.6 263.4 ][ 203.5 203.9 204.7 205.1 + 205.5 205.5 205.1 204.7 203.9 203.0 202.2 201.0 196.8 196.8 ]plong + s[ 268.4 267.2 266.3 265.9 265.9 266.3 267.2 268.4 269.3 270.5 271.4 271.8 271.8 271.4 270.5 269.3 268.4 ][ 205.5 + 205.1 203.9 201.8 200.5 198.4 197.2 196.8 196.8 197.2 198.4 200.5 201.8 203.9 205.1 205.5 205.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 289.31 195.11 moveto[ 289.3 289.3 306.4 306.4 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 293.5 196.8 293.5 205.5 292.2 204.3 291.4 203.9 4 pls + s[ 300.6 299.3 298.9 298.9 299.3 300.2 301.8 303.1 303.9 304.3 304.3 303.9 303.5 302.3 300.6 299.3 298.9 298.5 + 298.5 298.9 299.7 301.0 302.7 303.5 303.9 303.9 303.5 302.3 300.6 ][ 205.5 205.1 204.3 203.5 202.6 202.2 201.8 + 201.4 200.5 199.7 198.4 197.6 197.2 196.8 196.8 197.2 197.6 198.4 199.7 200.5 201.4 201.8 202.2 202.6 203.5 + 204.3 205.1 205.5 205.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 321.88 195.11 moveto[ 321.9 321.9 339.0 339.0 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 326.1 196.8 326.1 205.5 324.8 204.3 324.0 203.9 4 pls + s[ 336.5 336.1 334.8 334.0 332.7 331.9 331.5 331.5 331.9 332.7 334.0 334.4 335.7 336.5 336.9 336.9 336.5 335.7 + 334.4 334.0 332.7 331.9 331.5 ][ 204.3 205.1 205.5 205.5 205.1 203.9 201.8 199.7 198.0 197.2 196.8 196.8 197.2 + 198.0 199.3 199.7 201.0 201.8 202.2 202.2 201.8 201.0 199.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 354.45 195.11 moveto[ 354.5 354.5 372.0 372.0 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 358.6 196.8 358.6 205.5 357.4 204.3 356.5 203.9 4 pls + 369.9 199.7 363.6 199.7 367.8 205.5 3 pls + 367.8 196.8 367.8 205.5 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 387.02 195.11 moveto[ 387.0 387.0 404.1 404.1 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 391.2 196.8 391.2 205.5 389.9 204.3 389.1 203.9 4 pls + s[ 396.6 396.6 397.0 397.5 398.3 400.0 400.8 401.2 401.6 401.6 401.2 400.4 396.2 402.1 ][ 203.5 203.9 204.7 205.1 + 205.5 205.5 205.1 204.7 203.9 203.0 202.2 201.0 196.8 196.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 419.60 195.11 moveto[ 419.6 419.6 436.7 436.7 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 423.8 196.8 423.8 205.5 422.5 204.3 421.7 203.9 4 pls + s[ 431.3 430.0 429.2 428.8 428.8 429.2 430.0 431.3 432.1 433.4 434.2 434.6 434.6 434.2 433.4 432.1 431.3 ][ 205.5 + 205.1 203.9 201.8 200.5 198.4 197.2 196.8 196.8 197.2 198.4 200.5 201.8 203.9 205.1 205.5 205.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 455.09 195.11 moveto[ 455.1 455.1 465.1 465.1 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 459.3 458.0 457.6 457.6 458.0 458.9 460.5 461.8 462.6 463.0 463.0 462.6 462.2 460.9 459.3 458.0 457.6 457.2 + 457.2 457.6 458.4 459.7 461.4 462.2 462.6 462.6 462.2 460.9 459.3 ][ 205.5 205.1 204.3 203.5 202.6 202.2 201.8 + 201.4 200.5 199.7 198.4 197.6 197.2 196.8 196.8 197.2 197.6 198.4 199.7 200.5 201.4 201.8 202.2 202.6 203.5 + 204.3 205.1 205.5 205.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 488.08 195.11 moveto[ 488.1 488.1 497.7 497.7 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 495.2 494.8 493.5 492.7 491.4 490.6 490.2 490.2 490.6 491.4 492.7 493.1 494.3 495.2 495.6 495.6 495.2 494.3 + 493.1 492.7 491.4 490.6 490.2 ][ 204.3 205.1 205.5 205.5 205.1 203.9 201.8 199.7 198.0 197.2 196.8 196.8 197.2 + 198.0 199.3 199.7 201.0 201.8 202.2 202.2 201.8 201.0 199.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 520.24 195.11 moveto[ 520.2 520.2 530.7 530.7 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 528.6 199.7 522.3 199.7 526.5 205.5 3 pls + 526.5 196.8 526.5 205.5 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 552.81 195.11 moveto[ 552.8 552.8 562.8 562.8 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 555.3 555.3 555.7 556.2 557.0 558.7 559.5 559.9 560.3 560.3 559.9 559.1 554.9 560.7 ][ 203.5 203.9 204.7 205.1 + 205.5 205.5 205.1 204.7 203.9 203.0 202.2 201.0 196.8 196.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 585.38 195.11 moveto[ 585.4 585.4 595.4 595.4 ][ 195.1 207.2 207.2 195.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 590.0 588.7 587.9 587.5 587.5 587.9 588.7 590.0 590.8 592.1 592.9 593.3 593.3 592.9 592.1 590.8 590.0 ][ 205.5 + 205.1 203.9 201.8 200.5 198.4 197.2 196.8 196.8 197.2 198.4 200.5 201.8 203.9 205.1 205.5 205.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 170.21 234.23 moveto[ 170.2 170.2 199.0 199.0 ][ 234.2 246.3 246.3 234.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 179.8 239.7 172.3 239.7 2 pls + s[ 188.2 187.7 186.5 185.7 184.4 183.6 183.2 183.2 183.6 184.4 185.7 186.1 187.3 188.2 188.6 188.6 188.2 187.3 + 186.1 185.7 184.4 183.6 183.2 ][ 243.4 244.3 244.7 244.7 244.3 243.0 240.9 238.8 237.2 236.3 235.9 235.9 236.3 + 237.2 238.4 238.8 240.1 240.9 241.3 241.3 240.9 240.1 238.8 ]plong + s[ 193.6 192.3 191.5 191.1 191.1 191.5 192.3 193.6 194.4 195.7 196.5 196.9 196.9 196.5 195.7 194.4 193.6 ][ 244.7 + 244.3 243.0 240.9 239.7 237.6 236.3 235.9 235.9 236.3 237.6 239.7 240.9 243.0 244.3 244.7 244.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 170.21 266.80 moveto[ 170.2 170.2 199.0 199.0 ][ 266.8 278.9 278.9 266.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 179.8 272.2 172.3 272.2 2 pls + s[ 183.6 188.2 185.7 186.9 187.7 188.2 188.6 188.6 188.2 187.3 186.1 184.8 183.6 183.2 182.7 ][ 277.2 277.2 273.9 + 273.9 273.5 273.1 271.8 271.0 269.7 268.9 268.5 268.5 268.9 269.3 270.1 ]plong + s[ 193.6 192.3 191.5 191.1 191.1 191.5 192.3 193.6 194.4 195.7 196.5 196.9 196.9 196.5 195.7 194.4 193.6 ][ 277.2 + 276.8 275.6 273.5 272.2 270.1 268.9 268.5 268.5 268.9 270.1 272.2 273.5 275.6 276.8 277.2 277.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 189.00 299.38 moveto[ 189.0 189.0 199.0 199.0 ][ 299.4 311.5 311.5 299.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 193.6 192.3 191.5 191.1 191.1 191.5 192.3 193.6 194.4 195.7 196.5 196.9 196.9 196.5 195.7 194.4 193.6 ][ 309.8 + 309.4 308.1 306.1 304.8 302.7 301.5 301.0 301.0 301.5 302.7 304.8 306.1 308.1 309.4 309.8 309.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 342.50 178.39 moveto[ 342.5 342.5 447.3 447.3 ][ 178.4 193.8 193.8 178.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 344.6 183.0 344.6 191.8 2 pls + s[ 344.6 348.4 349.6 350.0 350.4 350.4 350.0 349.6 348.4 344.6 ][ 191.8 191.8 191.3 190.9 190.1 189.3 188.4 188.0 + 187.6 187.6 ]plong + 350.4 183.0 347.5 187.6 2 pls + 352.9 191.8 353.4 192.2 353.8 191.8 353.4 191.3 352.9 191.8 5 pls + 353.4 183.0 353.4 188.8 2 pls + s[ 361.3 361.3 360.9 360.5 359.6 358.4 357.5 ][ 188.8 182.2 180.9 180.5 180.1 180.1 180.5 ]plong + s[ 361.3 360.5 359.6 358.4 357.5 356.7 356.3 356.3 356.7 357.5 358.4 359.6 360.5 361.3 ][ 187.6 188.4 188.8 188.8 + 188.4 187.6 186.3 185.5 184.2 183.4 183.0 183.0 183.4 184.2 ]plong + 364.6 183.0 364.6 191.8 2 pls + s[ 364.6 365.9 366.7 368.0 368.8 369.2 369.2 ][ 187.2 188.4 188.8 188.8 188.4 187.2 183.0 ]plong + 375.1 183.0 374.2 183.0 373.4 183.4 373.0 184.7 373.0 191.8 5 pls + 374.7 188.8 371.7 188.8 2 pls + 388.9 183.0 388.9 188.8 2 pls + s[ 388.9 388.0 387.2 385.9 385.1 384.3 383.9 383.9 384.3 385.1 385.9 387.2 388.0 388.9 ][ 187.6 188.4 188.8 188.8 + 188.4 187.6 186.3 185.5 184.2 183.4 183.0 183.0 183.4 184.2 ]plong + s[ 396.4 396.0 394.7 393.5 392.2 391.8 392.2 393.0 395.1 396.0 396.4 396.4 396.0 394.7 393.5 392.2 391.8 ][ 187.6 + 188.4 188.8 188.8 188.4 187.6 186.7 186.3 185.9 185.5 184.7 184.2 183.4 183.0 183.0 183.4 184.2 ]plong + s[ 403.9 403.1 402.2 401.0 400.1 399.3 398.9 398.9 399.3 400.1 401.0 402.2 403.1 403.9 ][ 187.6 188.4 188.8 188.8 + 188.4 187.6 186.3 185.5 184.2 183.4 183.0 183.0 183.4 184.2 ]plong + s[ 406.4 411.4 411.4 411.0 410.6 409.8 408.5 407.7 406.8 406.4 406.4 406.8 407.7 408.5 409.8 410.6 411.4 ][ 186.3 + 186.3 187.2 188.0 188.4 188.8 188.8 188.4 187.6 186.3 185.5 184.2 183.4 183.0 183.0 183.4 184.2 ]plong + 414.3 183.0 414.3 188.8 2 pls + s[ 414.3 415.6 416.4 417.7 418.5 418.9 418.9 ][ 187.2 188.4 188.8 188.8 188.4 187.2 183.0 ]plong + s[ 426.5 426.0 424.8 423.5 422.3 421.9 422.3 423.1 425.2 426.0 426.5 426.5 426.0 424.8 423.5 422.3 421.9 ][ 187.6 + 188.4 188.8 188.8 188.4 187.6 186.7 186.3 185.9 185.5 184.7 184.2 183.4 183.0 183.0 183.4 184.2 ]plong + 429.0 191.8 429.4 192.2 429.8 191.8 429.4 191.3 429.0 191.8 5 pls + 429.4 183.0 429.4 188.8 2 pls + s[ 434.4 433.6 432.7 432.3 432.3 432.7 433.6 434.4 435.6 436.5 437.3 437.7 437.7 437.3 436.5 435.6 434.4 ][ 188.8 + 188.4 187.6 186.3 185.5 184.2 183.4 183.0 183.0 183.4 184.2 185.5 186.3 187.6 188.4 188.8 188.8 ]plong + 440.7 183.0 440.7 188.8 2 pls + s[ 440.7 441.9 442.7 444.0 444.8 445.3 445.3 ][ 187.2 188.4 188.8 188.8 188.4 187.2 183.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 167.28 235.77 moveto[ 167.3 154.7 154.7 167.3 ][ 235.8 235.8 307.2 307.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 165.6 237.9 156.8 237.9 2 pls + s[ 156.8 156.8 157.3 158.1 158.9 160.2 162.3 163.5 164.4 165.2 165.6 165.6 ][ 237.9 240.8 242.0 242.9 243.3 243.7 + 243.7 243.3 242.9 242.0 240.8 237.9 ]plong + s[ 162.3 162.3 161.4 160.6 160.2 159.8 159.8 160.2 161.0 162.3 163.1 164.4 165.2 165.6 165.6 165.2 164.4 ][ 246.2 + 251.2 251.2 250.8 250.4 249.5 248.3 247.5 246.6 246.2 246.2 246.6 247.5 248.3 249.5 250.4 251.2 ]plong + s[ 161.0 160.2 159.8 159.8 160.2 161.0 162.3 163.1 164.4 165.2 165.6 165.6 165.2 164.4 ][ 258.7 257.9 257.1 255.8 + 255.0 254.1 253.7 253.7 254.1 255.0 255.8 257.1 257.9 258.7 ]plong + 165.6 261.7 156.8 261.7 2 pls + 156.8 264.6 156.4 265.0 156.8 265.4 157.3 265.0 156.8 264.6 5 pls + 165.6 265.0 159.8 265.0 2 pls + 165.6 268.3 159.8 268.3 2 pls + s[ 161.4 160.2 159.8 159.8 160.2 161.4 165.6 ][ 268.3 269.6 270.4 271.7 272.5 272.9 272.9 ]plong + 165.6 280.9 159.8 280.9 2 pls + s[ 161.0 160.2 159.8 159.8 160.2 161.0 162.3 163.1 164.4 165.2 165.6 165.6 165.2 164.4 ][ 280.9 280.0 279.2 278.0 + 277.1 276.3 275.9 275.9 276.3 277.1 278.0 279.2 280.0 280.9 ]plong + 165.6 286.7 165.6 285.9 165.2 285.1 163.9 284.6 156.8 284.6 5 pls + 159.8 286.3 159.8 283.4 2 pls + 156.8 288.8 156.4 289.2 156.8 289.6 157.3 289.2 156.8 288.8 5 pls + 165.6 289.2 159.8 289.2 2 pls + s[ 159.8 160.2 161.0 162.3 163.1 164.4 165.2 165.6 165.6 165.2 164.4 163.1 162.3 161.0 160.2 159.8 159.8 ][ 294.2 + 293.4 292.6 292.2 292.2 292.6 293.4 294.2 295.5 296.3 297.2 297.6 297.6 297.2 296.3 295.5 294.2 ]plong + 165.6 300.5 159.8 300.5 2 pls + s[ 161.4 160.2 159.8 159.8 160.2 161.4 165.6 ][ 300.5 301.8 302.6 303.8 304.7 305.1 305.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 340.80 moveto[ 360.9 360.9 435.2 435.2 ][ 340.8 352.9 352.9 340.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 342.5 366.3 351.2 2 pls + 369.7 342.5 366.3 351.2 2 pls + 368.4 345.4 364.2 345.4 2 pls + 378.4 342.5 378.4 351.2 2 pls + 383.9 351.2 378.4 351.2 2 pls + 381.8 347.1 378.4 347.1 2 pls + 385.9 342.5 385.9 351.2 2 pls + 391.0 342.5 391.0 351.2 2 pls + 393.9 351.2 388.0 351.2 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 350.0 350.8 351.2 351.2 350.8 350.0 349.2 348.3 347.9 347.5 346.6 346.2 345.8 345.0 343.7 342.9 + 342.5 342.5 342.9 343.7 ]plong + 413.5 342.5 412.7 342.5 411.8 342.9 411.4 344.1 411.4 351.2 5 pls + 413.1 348.3 410.2 348.3 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 345.8 + 345.8 346.6 347.5 347.9 348.3 348.3 347.9 347.1 345.8 345.0 343.7 342.9 342.5 342.5 342.9 343.7 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 347.1 + 347.9 348.3 348.3 347.9 347.1 346.2 345.8 345.4 345.0 344.1 343.7 342.9 342.5 342.5 342.9 343.7 ]plong + 433.1 342.5 432.3 342.5 431.5 342.9 431.1 344.1 431.1 351.2 5 pls + 432.7 348.3 429.8 348.3 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/cobe.attr b/ast_tester/cobe.attr new file mode 100644 index 0000000..c1f61e5 --- /dev/null +++ b/ast_tester/cobe.attr @@ -0,0 +1 @@ +format(1)=gd,format(2)=gd,Grid=1,tickall=0,width(axes)=3 diff --git a/ast_tester/cobe.box b/ast_tester/cobe.box new file mode 100644 index 0000000..b4f303e --- /dev/null +++ b/ast_tester/cobe.box @@ -0,0 +1 @@ +10.0 -10.0 300.0 280.0 diff --git a/ast_tester/cobe.head b/ast_tester/cobe.head new file mode 100644 index 0000000..fb6da34 --- /dev/null +++ b/ast_tester/cobe.head @@ -0,0 +1,61 @@ +SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 +BITPIX = -32 / Bits per pixel. +NAXIS = 2 / Number of dimensions +NAXIS1 = 300 / Length of x axis. +NAXIS2 = 300 / Length of y axis. +CTYPE1 = 'GLON-ZEA' / X-axis type +CTYPE2 = 'GLAT-ZEA' / Y-axis type +CRVAL1 = -149.56866 / Reference pixel value +CRVAL2 = -19.758201 / Reference pixel value +CRPIX1 = 150.500 / Reference pixel +CRPIX2 = 150.500 / Reference pixel +CDELT1 = -1.20000 / Degrees/pixel +CDELT2 = 1.20000 / Degrees/pixel +CROTA1 = 0.00000 / Rotation in degrees. +COMMENT +COMMENT This file was produced by the SkyView survey analysis system from +COMMENT available astronomical surveys. The data are formatted +COMMENT as a simple two-dimensional FITS image with the same units as +COMMENT the orginal survey. A single ASCII table extension may be present +COMMENT which describes catalog objects found within the field of view. +COMMENT Copies of relevant copyright notices are included in this file. +COMMENT +COMMENT Questions should be directed to: +COMMENT +COMMENT scollick@skyview.gsfc.nasa.gov +COMMENT or +COMMENT mcglynn@grossc.gsfc.nasa.gov +COMMENT +COMMENT SkyView +COMMENT Code 668.1 +COMMENT Goddard Space Flight Center, Greenbelt, MD 20771 +COMMENT 301-286-7780 +COMMENT +COMMENT SkyView is supported by NASA ADP grant NAS 5-32068. +COMMENT +SURVEY = 'COBE DIRBE' +BUNITS = 'MJy/sr ' / +ORIGIN = 'CDAC ' / Cosmology Data Analysis Center +TELESCOP= 'COBE ' / COsmic Background Explorer satellite +INSTRUME= 'DIRBE ' / COBE instrument [DIRBE, DMR, FIRAS] +PIXRESOL= 9 / Quad tree pixel resolution [6, 9] +DATE = '27/09/94' / FITS file creation date (dd/mm/yy) +DATE-MAP= '16/09/94' / Date of original file creation (dd/mm/yy) +COMMENT COBE specific keywords +DATE-BEG= '08/12/89' / date of initial data represented (dd/mm/yy) +DATE-END= '25/09/90' / date of final data represented (dd/mm/yy) +COMMENT +COMMENT THE COBE DIRBE map is a combination of the original ten +COMMENT band passes with the following wavelengths: +COMMENT Band 1 - 1.25 microns +COMMENT Band 2 - 2.2 microns +COMMENT Band 3 - 3.5 microns +COMMENT Band 4 - 4.9 microns +COMMENT Band 5 - 12 microns +COMMENT Band 6 - 25 microns +COMMENT Band 7 - 60 microns +COMMENT Band 8 - 100 microns +COMMENT Band 9 - 140 microns +COMMENT Band 10 - 240 microns +COMMENT +END diff --git a/ast_tester/cobe.ps b/ast_tester/cobe.ps new file mode 100644 index 0000000..57ae381 --- /dev/null +++ b/ast_tester/cobe.ps @@ -0,0 +1,934 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Fri Feb 13 14:56:06 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 13/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 388.9 397.2 404.4 410.5 415.4 419.2 421.8 423.1 423.2 421.9 419.1 414.7 408.4 407.9 407.3 406.8 406.2 405.7 + 405.1 404.5 403.9 403.3 402.6 402.0 401.3 400.7 400.0 399.3 398.6 397.8 397.1 396.4 395.6 394.8 394.0 393.2 + 392.4 391.5 390.6 389.8 388.9 ][ 218.3 229.2 241.4 254.5 268.3 282.7 297.4 312.1 326.7 340.9 354.5 367.3 379.0 + 379.8 380.6 381.4 382.1 382.9 383.6 384.4 385.1 385.8 386.6 387.3 388.0 388.6 389.3 390.0 390.6 391.3 391.9 + 392.6 393.2 393.8 394.4 395.0 395.5 396.1 396.6 397.2 397.7 ]plong + s[ 388.9 380.7 373.7 367.8 363.0 359.4 356.9 355.5 355.5 356.8 359.5 363.8 369.8 370.4 370.9 371.4 371.9 372.5 + 373.1 373.6 374.2 374.8 375.5 376.1 376.7 377.4 378.0 378.7 379.4 380.1 380.8 381.6 382.3 383.1 383.8 384.6 + 385.5 386.3 387.1 388.0 388.9 ][ 218.3 229.3 241.5 254.6 268.5 282.9 297.5 312.2 326.8 341.0 354.6 367.3 379.0 + 379.8 380.6 381.3 382.1 382.9 383.6 384.4 385.1 385.8 386.5 387.2 387.9 388.6 389.3 390.0 390.6 391.3 391.9 + 392.5 393.1 393.8 394.4 394.9 395.5 396.1 396.6 397.2 397.7 ]plong + s[ 388.9 387.6 386.3 385.1 383.8 382.6 381.3 380.0 378.8 377.5 376.3 375.0 373.8 372.5 371.3 370.1 368.8 367.6 + 366.4 365.1 363.9 362.7 361.5 360.3 359.1 357.9 356.7 355.5 354.3 353.1 352.0 350.8 349.7 348.5 347.4 346.2 + 345.1 344.0 342.9 341.7 340.6 339.6 338.5 337.4 336.3 335.3 334.2 333.2 332.2 331.1 330.1 329.1 328.2 327.2 + 326.2 325.3 324.3 323.4 322.5 321.6 320.7 319.8 318.9 318.1 317.2 316.4 315.6 314.8 314.0 313.2 312.5 311.7 + 311.0 310.3 309.6 308.9 308.3 307.6 307.0 306.4 305.8 305.2 304.6 304.1 303.5 303.0 302.5 302.1 301.6 301.2 + 300.8 300.4 300.0 299.6 299.3 299.0 298.7 298.4 298.2 297.9 297.7 297.5 297.3 297.2 297.1 297.0 296.9 296.8 + 296.8 296.8 296.8 296.9 296.9 297.0 297.1 297.3 297.4 297.6 297.8 298.0 298.3 298.6 298.9 299.2 299.6 300.0 + 300.4 300.8 301.3 301.8 302.3 302.9 303.5 304.1 304.7 305.3 306.0 306.7 307.5 308.2 309.0 309.8 310.7 311.6 + 312.5 313.4 314.3 315.3 316.3 317.3 318.4 319.5 320.6 321.7 322.9 324.1 325.3 326.5 327.8 329.1 330.4 331.7 + 333.0 334.4 335.8 337.2 338.7 340.1 341.6 343.1 344.6 346.2 347.7 349.3 350.9 352.5 354.1 355.7 357.4 359.1 + 360.7 362.4 364.1 365.9 367.6 369.3 371.1 372.8 374.6 376.4 378.1 379.9 381.7 383.5 385.3 387.1 388.9 ][ 218.3 + 218.3 218.4 218.4 218.5 218.6 218.7 218.8 218.9 219.1 219.2 219.4 219.6 219.8 220.0 220.3 220.5 220.8 221.1 + 221.4 221.8 222.1 222.5 222.8 223.2 223.6 224.1 224.5 225.0 225.4 225.9 226.4 227.0 227.5 228.1 228.6 229.2 + 229.8 230.4 231.1 231.7 232.4 233.0 233.7 234.5 235.2 235.9 236.7 237.4 238.2 239.0 239.8 240.7 241.5 242.4 + 243.2 244.1 245.0 245.9 246.9 247.8 248.8 249.7 250.7 251.7 252.7 253.7 254.8 255.8 256.9 258.0 259.1 260.2 + 261.3 262.4 263.5 264.7 265.8 267.0 268.2 269.4 270.6 271.8 273.0 274.3 275.5 276.8 278.1 279.3 280.6 281.9 + 283.2 284.5 285.9 287.2 288.5 289.9 291.2 292.6 294.0 295.4 296.8 298.1 299.5 300.9 302.4 303.8 305.2 306.6 + 308.1 309.5 310.9 312.4 313.8 315.3 316.7 318.2 319.6 321.1 322.5 324.0 325.4 326.9 328.3 329.8 331.2 332.7 + 334.1 335.6 337.0 338.5 339.9 341.3 342.7 344.2 345.6 347.0 348.4 349.8 351.1 352.5 353.9 355.2 356.5 357.9 + 359.2 360.5 361.8 363.1 364.3 365.6 366.8 368.0 369.2 370.4 371.6 372.7 373.8 375.0 376.0 377.1 378.2 379.2 + 380.2 381.2 382.1 383.1 384.0 384.9 385.7 386.5 387.4 388.1 388.9 389.6 390.3 391.0 391.6 392.2 392.8 393.3 + 393.8 394.3 394.8 395.2 395.6 395.9 396.3 396.6 396.8 397.0 397.2 397.4 397.5 397.6 397.7 397.7 ]plong + s[ 388.9 388.2 387.6 386.9 386.3 385.6 384.9 384.2 383.5 382.9 382.2 381.4 380.7 380.0 379.3 378.6 377.8 377.1 + 376.3 375.5 374.8 374.0 373.2 372.4 371.6 370.8 370.0 369.2 368.4 367.5 366.7 365.8 365.0 364.1 363.2 362.3 + 361.4 360.5 359.6 358.7 357.7 356.8 355.8 354.9 353.9 352.9 351.9 350.9 349.9 348.9 347.8 346.8 345.7 344.7 + 343.6 342.5 341.4 340.3 339.1 338.0 336.8 335.6 334.4 333.2 332.0 330.8 329.5 328.3 327.0 325.7 324.4 323.1 + 321.8 320.4 319.0 317.7 316.3 314.8 313.4 312.0 310.5 309.0 307.5 306.0 304.5 303.0 301.5 299.9 298.3 296.8 + 295.2 293.6 292.0 290.5 288.9 287.3 285.8 284.2 282.7 281.2 279.7 278.2 276.8 275.4 274.1 272.8 271.6 270.4 + 269.3 268.3 267.4 266.6 265.9 265.3 264.8 264.5 264.3 264.2 264.3 264.5 264.8 265.3 266.0 266.8 267.7 268.8 + 270.0 271.4 272.9 274.5 276.2 278.0 279.9 281.8 283.9 286.0 288.2 290.4 292.6 294.9 297.2 299.6 301.9 304.2 + 306.6 308.9 311.2 313.5 315.8 318.0 320.3 322.5 324.6 326.8 328.9 331.0 333.0 335.0 337.0 338.9 340.8 342.7 + 344.5 346.3 348.0 349.8 351.4 353.1 354.7 356.3 357.9 359.4 360.9 362.3 363.8 365.2 366.5 367.9 369.2 370.5 + 371.7 373.0 374.2 375.4 376.6 377.7 378.8 379.9 381.0 382.0 383.1 384.1 385.1 386.0 387.0 387.9 388.9 ][ 218.3 + 217.6 216.9 216.2 215.5 214.8 214.1 213.4 212.8 212.1 211.5 210.8 210.2 209.6 209.0 208.4 207.8 207.2 206.6 + 206.1 205.5 205.0 204.4 203.9 203.4 202.9 202.4 202.0 201.5 201.1 200.6 200.2 199.8 199.4 199.0 198.6 198.3 + 197.9 197.6 197.3 197.0 196.7 196.5 196.2 196.0 195.8 195.6 195.4 195.3 195.1 195.0 194.9 194.8 194.7 194.7 + 194.7 194.7 194.7 194.8 194.9 195.0 195.1 195.3 195.5 195.7 195.9 196.2 196.5 196.8 197.2 197.6 198.1 198.6 + 199.1 199.7 200.3 201.0 201.7 202.4 203.2 204.1 205.0 206.0 207.0 208.1 209.2 210.5 211.7 213.1 214.5 216.1 + 217.6 219.3 221.1 222.9 224.9 226.9 229.0 231.3 233.6 236.1 238.6 241.3 244.0 246.9 249.9 253.0 256.2 259.5 + 262.9 266.4 270.1 273.8 277.6 281.4 285.4 289.4 293.5 297.5 301.7 305.8 310.0 314.1 318.2 322.3 326.3 330.3 + 334.2 338.1 341.8 345.5 349.0 352.4 355.7 358.9 362.0 364.9 367.8 370.4 373.0 375.4 377.7 379.9 382.0 383.9 + 385.7 387.4 389.0 390.5 391.9 393.3 394.5 395.6 396.6 397.6 398.5 399.3 400.0 400.7 401.3 401.9 402.4 402.8 + 403.2 403.6 403.9 404.1 404.3 404.5 404.6 404.7 404.7 404.7 404.7 404.7 404.6 404.5 404.3 404.2 404.0 403.8 + 403.5 403.3 403.0 402.7 402.4 402.0 401.7 401.3 400.9 400.5 400.1 399.6 399.2 398.7 398.2 397.7 ]plong + s[ 388.9 389.5 390.1 390.8 391.4 392.0 392.7 393.4 394.0 394.7 395.4 396.1 396.8 397.5 398.2 398.9 399.6 400.4 + 401.1 401.8 402.6 403.3 404.1 404.9 405.7 406.4 407.2 408.0 408.9 409.7 410.5 411.3 412.2 413.0 413.9 414.8 + 415.6 416.5 417.4 418.3 419.3 420.2 421.1 422.1 423.0 424.0 425.0 425.9 426.9 427.9 429.0 430.0 431.0 432.1 + 433.2 434.2 435.3 436.4 437.6 438.7 439.8 441.0 442.2 443.4 444.6 445.8 447.0 448.3 449.5 450.8 452.1 453.4 + 454.8 456.1 457.5 458.9 460.3 461.7 463.1 464.6 466.0 467.5 469.0 470.5 472.0 473.6 475.1 476.7 478.3 479.9 + 481.5 483.1 484.7 486.3 487.9 489.5 491.1 492.7 494.3 495.9 497.4 498.9 500.4 501.9 503.3 504.6 505.9 507.1 + 508.2 509.3 510.3 511.1 511.9 512.5 513.0 513.4 513.6 513.7 513.7 513.5 513.1 512.6 512.0 511.1 510.2 509.0 + 507.8 506.4 504.8 503.2 501.4 499.6 497.6 495.6 493.4 491.3 489.0 486.8 484.5 482.1 479.8 477.4 475.0 472.6 + 470.3 467.9 465.6 463.3 461.0 458.7 456.5 454.3 452.1 450.0 447.9 445.8 443.8 441.8 439.8 437.9 436.0 434.2 + 432.4 430.6 428.9 427.2 425.5 423.9 422.3 420.7 419.2 417.7 416.2 414.8 413.4 412.0 410.7 409.3 408.1 406.8 + 405.6 404.3 403.2 402.0 400.9 399.7 398.7 397.6 396.5 395.5 394.5 393.5 392.5 391.6 390.7 389.8 388.9 ][ 218.3 + 217.6 216.9 216.2 215.4 214.8 214.1 213.4 212.7 212.0 211.4 210.7 210.1 209.5 208.9 208.2 207.6 207.1 206.5 + 205.9 205.3 204.8 204.3 203.7 203.2 202.7 202.2 201.7 201.3 200.8 200.3 199.9 199.5 199.1 198.7 198.3 197.9 + 197.6 197.2 196.9 196.6 196.3 196.0 195.8 195.5 195.3 195.1 194.9 194.7 194.5 194.4 194.3 194.2 194.1 194.0 + 194.0 194.0 194.0 194.0 194.1 194.2 194.3 194.4 194.6 194.8 195.0 195.2 195.5 195.8 196.2 196.6 197.0 197.5 + 198.0 198.5 199.1 199.7 200.4 201.1 201.9 202.7 203.6 204.6 205.6 206.6 207.7 208.9 210.2 211.5 212.9 214.4 + 216.0 217.7 219.4 221.2 223.2 225.2 227.3 229.6 231.9 234.3 236.9 239.6 242.4 245.3 248.3 251.4 254.7 258.1 + 261.5 265.1 268.8 272.6 276.5 280.5 284.5 288.7 292.8 297.1 301.3 305.6 309.8 314.1 318.3 322.5 326.7 330.7 + 334.8 338.7 342.5 346.2 349.8 353.3 356.7 359.9 363.0 366.0 368.8 371.5 374.1 376.5 378.8 380.9 383.0 384.9 + 386.7 388.4 390.0 391.5 392.8 394.1 395.3 396.4 397.4 398.3 399.2 400.0 400.7 401.3 401.9 402.4 402.9 403.3 + 403.7 404.0 404.3 404.5 404.7 404.8 404.9 405.0 405.0 405.0 404.9 404.9 404.8 404.7 404.5 404.3 404.1 403.9 + 403.7 403.4 403.1 402.8 402.5 402.1 401.7 401.3 400.9 400.5 400.1 399.6 399.2 398.7 398.2 397.7 ]plong + s[ 388.9 390.1 391.4 392.6 393.9 395.2 396.4 397.7 398.9 400.2 401.4 402.7 403.9 405.2 406.4 407.7 408.9 410.1 + 411.4 412.6 413.8 415.1 416.3 417.5 418.7 419.9 421.1 422.3 423.5 424.7 425.8 427.0 428.2 429.3 430.5 431.6 + 432.8 433.9 435.0 436.1 437.2 438.3 439.4 440.5 441.6 442.6 443.7 444.7 445.8 446.8 447.8 448.8 449.8 450.8 + 451.8 452.7 453.7 454.6 455.5 456.5 457.4 458.3 459.1 460.0 460.8 461.7 462.5 463.3 464.1 464.9 465.7 466.4 + 467.1 467.9 468.6 469.3 469.9 470.6 471.2 471.9 472.5 473.1 473.6 474.2 474.7 475.2 475.7 476.2 476.7 477.1 + 477.5 477.9 478.3 478.7 479.0 479.4 479.7 479.9 480.2 480.4 480.7 480.9 481.0 481.2 481.3 481.4 481.5 481.5 + 481.6 481.6 481.6 481.5 481.5 481.4 481.3 481.2 481.0 480.8 480.6 480.4 480.1 479.8 479.5 479.2 478.8 478.4 + 478.0 477.5 477.0 476.5 476.0 475.5 474.9 474.3 473.6 473.0 472.3 471.6 470.8 470.0 469.2 468.4 467.6 466.7 + 465.8 464.8 463.9 462.9 461.9 460.8 459.8 458.7 457.5 456.4 455.2 454.0 452.8 451.6 450.3 449.0 447.7 446.3 + 445.0 443.6 442.2 440.7 439.3 437.8 436.3 434.8 433.3 431.7 430.2 428.6 427.0 425.4 423.7 422.1 420.4 418.7 + 417.0 415.3 413.6 411.9 410.2 408.4 406.7 404.9 403.1 401.4 399.6 397.8 396.0 394.2 392.4 390.6 388.9 ][ 218.3 + 218.3 218.3 218.4 218.4 218.5 218.6 218.7 218.8 218.9 219.1 219.3 219.4 219.6 219.9 220.1 220.3 220.6 220.9 + 221.2 221.5 221.8 222.2 222.6 222.9 223.3 223.8 224.2 224.6 225.1 225.6 226.1 226.6 227.1 227.7 228.2 228.8 + 229.4 230.0 230.6 231.3 231.9 232.6 233.3 234.0 234.7 235.4 236.2 236.9 237.7 238.5 239.3 240.1 241.0 241.8 + 242.7 243.6 244.5 245.4 246.3 247.2 248.2 249.2 250.1 251.1 252.1 253.2 254.2 255.2 256.3 257.4 258.5 259.6 + 260.7 261.8 262.9 264.1 265.3 266.4 267.6 268.8 270.0 271.2 272.5 273.7 275.0 276.2 277.5 278.8 280.1 281.4 + 282.7 284.0 285.4 286.7 288.1 289.4 290.8 292.2 293.5 294.9 296.3 297.7 299.1 300.5 302.0 303.4 304.8 306.3 + 307.7 309.1 310.6 312.0 313.5 315.0 316.4 317.9 319.4 320.8 322.3 323.8 325.2 326.7 328.2 329.6 331.1 332.5 + 334.0 335.5 336.9 338.4 339.8 341.2 342.7 344.1 345.5 346.9 348.3 349.7 351.1 352.5 353.9 355.2 356.6 357.9 + 359.3 360.6 361.9 363.2 364.4 365.7 366.9 368.1 369.3 370.5 371.7 372.9 374.0 375.1 376.2 377.3 378.3 379.4 + 380.4 381.3 382.3 383.2 384.1 385.0 385.9 386.7 387.5 388.3 389.0 389.8 390.5 391.1 391.7 392.3 392.9 393.5 + 394.0 394.4 394.9 395.3 395.7 396.0 396.3 396.6 396.9 397.1 397.3 397.4 397.5 397.6 397.7 397.7 ]plong + s[ 428.9 428.7 428.3 427.9 427.5 427.0 426.5 425.9 425.3 424.7 424.0 423.3 422.5 421.7 420.9 420.1 419.2 418.3 + 417.3 416.3 415.3 414.3 413.3 412.2 411.1 410.0 408.9 407.7 406.5 405.4 404.2 403.0 401.7 400.5 399.3 398.0 + 396.8 395.5 394.2 393.0 391.7 390.4 389.1 387.8 386.6 385.3 384.0 382.7 381.5 380.2 378.9 377.7 376.5 375.2 + 374.0 372.8 371.6 370.5 369.3 368.2 367.1 366.0 364.9 363.8 362.8 361.8 360.8 359.8 358.9 358.0 357.1 356.3 + 355.5 354.7 354.0 353.3 352.6 352.0 351.4 350.9 350.4 350.0 349.5 349.2 348.9 348.6 348.4 348.3 348.2 348.1 + 348.1 348.2 348.3 348.5 348.8 349.1 349.5 349.9 350.4 351.0 351.6 352.3 353.1 353.9 354.8 355.8 356.8 357.9 + 359.1 360.3 361.6 363.0 364.4 365.8 367.4 368.9 370.5 372.2 373.9 375.6 377.4 379.2 381.0 382.9 384.7 386.6 + 388.5 390.4 392.2 394.1 395.9 397.8 399.6 401.4 403.1 404.8 406.5 408.1 409.7 411.3 412.8 414.2 415.6 416.9 + 418.1 419.3 420.4 421.5 422.5 423.4 424.3 425.1 425.8 426.5 427.1 427.6 428.1 428.5 428.8 429.1 429.3 429.5 + 429.6 429.6 429.6 429.5 429.4 429.2 428.9 428.7 428.3 427.9 427.5 427.0 426.5 425.9 425.3 424.7 424.0 423.3 + 422.5 421.7 420.9 420.1 419.2 418.3 417.3 416.3 415.3 414.3 413.3 412.2 411.1 410.0 408.9 407.7 406.5 ][ 226.9 + 227.9 228.8 229.7 230.6 231.5 232.4 233.2 234.0 234.8 235.6 236.3 237.1 237.8 238.5 239.1 239.8 240.4 241.0 + 241.5 242.1 242.6 243.1 243.6 244.1 244.5 244.9 245.3 245.6 246.0 246.3 246.6 246.8 247.1 247.3 247.5 247.6 + 247.8 247.9 248.0 248.0 248.1 248.1 248.1 248.1 248.0 247.9 247.8 247.7 247.5 247.4 247.2 246.9 246.7 246.4 + 246.1 245.8 245.4 245.1 244.7 244.2 243.8 243.3 242.8 242.3 241.8 241.2 240.6 240.0 239.4 238.7 238.0 237.3 + 236.6 235.9 235.1 234.3 233.5 232.7 231.8 231.0 230.1 229.2 228.3 227.3 226.4 225.4 224.4 223.4 222.4 221.4 + 220.4 219.3 218.3 217.2 216.2 215.1 214.0 213.0 211.9 210.9 209.8 208.8 207.8 206.7 205.8 204.8 203.8 202.9 + 202.0 201.2 200.3 199.5 198.8 198.1 197.4 196.8 196.2 195.7 195.2 194.8 194.4 194.1 193.9 193.7 193.6 193.6 + 193.6 193.7 193.8 194.0 194.3 194.6 195.0 195.5 196.0 196.5 197.1 197.8 198.5 199.2 200.0 200.8 201.7 202.5 + 203.5 204.4 205.4 206.3 207.3 208.4 209.4 210.4 211.5 212.5 213.6 214.7 215.7 216.8 217.8 218.9 219.9 221.0 + 222.0 223.0 224.0 225.0 226.0 226.9 227.9 228.8 229.7 230.6 231.5 232.4 233.2 234.0 234.8 235.6 236.3 237.1 + 237.8 238.5 239.1 239.8 240.4 241.0 241.5 242.1 242.6 243.1 243.6 244.1 244.5 244.9 245.3 245.6 ]plong + s[ 462.0 461.0 460.0 458.9 457.7 456.5 455.3 453.9 452.6 451.2 449.7 448.3 446.7 445.2 443.6 441.9 440.2 438.5 + 436.8 435.0 433.2 431.4 429.6 427.7 425.8 423.9 422.0 420.0 418.0 416.1 414.1 412.0 410.0 408.0 405.9 403.9 + 401.8 399.7 397.6 395.6 393.5 391.4 389.3 387.2 385.1 383.0 380.9 378.8 376.8 374.7 372.6 370.6 368.5 366.5 + 364.5 362.5 360.5 358.5 356.5 354.6 352.7 350.8 348.9 347.0 345.2 343.4 341.6 339.9 338.2 336.5 334.8 333.2 + 331.6 330.1 328.6 327.1 325.7 324.3 323.0 321.7 320.5 319.3 318.2 317.1 316.1 315.2 314.3 313.6 312.8 312.2 + 311.6 311.1 310.8 310.5 310.3 310.2 310.2 310.3 310.6 310.9 311.4 312.1 312.9 313.8 314.9 316.2 317.6 319.2 + 321.1 323.1 325.3 327.7 330.4 333.2 336.3 339.6 343.2 346.9 350.8 355.0 359.3 363.8 368.4 373.2 378.0 382.9 + 387.9 392.8 397.7 402.6 407.4 412.1 416.6 421.0 425.2 429.3 433.1 436.7 440.1 443.3 446.2 448.9 451.5 453.8 + 455.9 457.8 459.5 461.0 462.3 463.5 464.5 465.3 466.0 466.6 467.0 467.3 467.5 467.5 467.5 467.3 467.1 466.7 + 466.3 465.8 465.2 464.5 463.7 462.9 462.0 461.0 460.0 458.9 457.7 456.5 455.3 453.9 452.6 451.2 449.7 448.3 + 446.7 445.2 443.6 441.9 440.2 438.5 436.8 435.0 433.2 431.4 429.6 427.7 425.8 423.9 422.0 420.0 418.0 ][ 252.5 + 253.9 255.3 256.6 257.9 259.2 260.4 261.6 262.7 263.8 264.8 265.9 266.8 267.8 268.7 269.6 270.4 271.2 272.0 + 272.7 273.4 274.1 274.7 275.3 275.9 276.4 276.9 277.4 277.9 278.3 278.7 279.0 279.3 279.6 279.9 280.1 280.3 + 280.5 280.6 280.7 280.8 280.9 280.9 280.9 280.8 280.8 280.7 280.6 280.4 280.2 280.0 279.7 279.5 279.2 278.8 + 278.4 278.0 277.6 277.1 276.7 276.1 275.6 275.0 274.4 273.7 273.0 272.3 271.5 270.7 269.9 269.1 268.2 267.2 + 266.3 265.3 264.2 263.1 262.0 260.9 259.7 258.4 257.1 255.8 254.5 253.1 251.6 250.1 248.6 247.0 245.3 243.7 + 241.9 240.2 238.3 236.4 234.5 232.5 230.5 228.5 226.3 224.2 222.0 219.7 217.4 215.1 212.7 210.4 208.0 205.6 + 203.1 200.7 198.4 196.0 193.7 191.4 189.3 187.2 185.2 183.4 181.7 180.2 178.9 177.8 176.9 176.2 175.8 175.6 + 175.7 176.0 176.6 177.4 178.4 179.6 181.1 182.7 184.5 186.4 188.4 190.6 192.8 195.1 197.4 199.8 202.2 204.6 + 207.0 209.4 211.8 214.2 216.5 218.8 221.1 223.3 225.5 227.6 229.7 231.7 233.7 235.7 237.6 239.4 241.2 243.0 + 244.7 246.3 247.9 249.5 251.0 252.5 253.9 255.3 256.6 257.9 259.2 260.4 261.6 262.7 263.8 264.8 265.9 266.8 + 267.8 268.7 269.6 270.4 271.2 272.0 272.7 273.4 274.1 274.7 275.3 275.9 276.4 276.9 277.4 277.9 ]plong + s[ 480.2 478.6 476.9 475.2 473.5 471.7 469.9 468.1 466.2 464.3 462.3 460.4 458.4 456.3 454.3 452.2 450.1 447.9 + 445.8 443.6 441.4 439.2 436.9 434.7 432.4 430.1 427.8 425.5 423.1 420.8 418.4 416.0 413.6 411.2 408.8 406.4 + 404.0 401.5 399.1 396.7 394.2 391.8 389.3 386.9 384.5 382.0 379.6 377.1 374.7 372.3 369.9 367.5 365.1 362.7 + 360.3 357.9 355.5 353.2 350.9 348.5 346.2 344.0 341.7 339.4 337.2 335.0 332.8 330.6 328.5 326.4 324.3 322.2 + 320.2 318.1 316.2 314.2 312.3 310.4 308.5 306.7 304.9 303.2 301.5 299.8 298.2 296.6 295.0 293.5 292.1 290.7 + 289.3 288.0 286.8 285.6 284.5 283.4 282.4 281.5 280.7 279.9 279.3 278.7 278.2 277.9 277.7 277.6 277.6 277.9 + 278.3 279.0 279.9 281.1 282.7 284.6 287.0 290.0 293.6 297.9 303.1 309.3 316.7 325.3 335.2 346.4 358.8 372.1 + 386.0 400.0 413.6 426.4 438.2 448.6 457.7 465.6 472.2 477.8 482.5 486.4 489.6 492.2 494.3 496.0 497.4 498.4 + 499.1 499.7 500.0 500.1 500.1 499.9 499.6 499.2 498.7 498.1 497.4 496.5 495.7 494.7 493.7 492.6 491.4 490.2 + 488.9 487.6 486.2 484.8 483.3 481.8 480.2 478.6 476.9 475.2 473.5 471.7 469.9 468.1 466.2 464.3 462.3 460.4 + 458.4 456.3 454.3 452.2 450.1 447.9 445.8 443.6 441.4 439.2 436.9 434.7 432.4 430.1 427.8 425.5 423.1 ][ 292.2 + 293.3 294.4 295.5 296.5 297.5 298.4 299.3 300.2 301.1 301.9 302.7 303.5 304.2 304.9 305.6 306.3 306.9 307.5 + 308.1 308.6 309.1 309.6 310.1 310.6 311.0 311.4 311.7 312.1 312.4 312.7 313.0 313.2 313.5 313.7 313.9 314.0 + 314.2 314.3 314.4 314.4 314.5 314.5 314.5 314.4 314.4 314.3 314.2 314.1 313.9 313.8 313.6 313.3 313.1 312.8 + 312.5 312.2 311.9 311.5 311.1 310.7 310.3 309.8 309.3 308.8 308.3 307.7 307.1 306.5 305.9 305.2 304.5 303.8 + 303.0 302.2 301.4 300.6 299.7 298.8 297.9 296.9 295.9 294.8 293.7 292.6 291.5 290.2 289.0 287.7 286.4 285.0 + 283.5 282.0 280.5 278.8 277.1 275.4 273.6 271.6 269.6 267.5 265.3 263.0 260.6 258.0 255.3 252.5 249.4 246.2 + 242.8 239.2 235.4 231.3 226.9 222.3 217.3 212.1 206.6 200.9 195.0 189.1 183.3 177.9 173.1 169.3 166.7 165.6 + 166.1 168.1 171.4 175.9 181.1 186.7 192.6 198.5 204.3 209.9 215.3 220.3 225.1 229.6 233.8 237.7 241.4 244.9 + 248.2 251.3 254.2 257.0 259.6 262.1 264.4 266.7 268.8 270.8 272.8 274.7 276.4 278.2 279.8 281.4 282.9 284.4 + 285.8 287.2 288.5 289.7 291.0 292.2 293.3 294.4 295.5 296.5 297.5 298.4 299.3 300.2 301.1 301.9 302.7 303.5 + 304.2 304.9 305.6 306.3 306.9 307.5 308.1 308.6 309.1 309.6 310.1 310.6 311.0 311.4 311.7 312.1 ]plong + s[ 475.7 450.6 421.1 389.3 357.5 327.9 302.7 301.1 299.6 298.1 296.6 295.2 293.8 292.4 291.1 289.8 288.6 287.4 + 286.3 285.2 284.1 283.2 282.2 281.3 280.5 279.8 279.1 278.5 278.0 277.6 277.2 277.0 277.0 277.1 277.3 277.9 + 278.6 279.8 281.3 283.5 286.4 290.3 295.6 302.7 312.3 325.0 326.1 327.1 328.2 329.3 330.5 331.6 332.8 333.9 + 335.1 336.4 337.6 338.9 340.1 341.4 342.8 344.1 345.5 346.8 348.2 349.6 351.1 352.5 354.0 355.4 356.9 358.4 + 360.0 361.5 363.1 364.6 366.2 367.8 369.4 371.0 372.6 374.2 375.9 377.5 379.2 380.8 382.5 384.2 385.8 387.5 + 389.2 390.8 392.5 394.2 395.8 397.5 399.1 400.8 402.4 404.1 405.7 407.3 408.9 410.5 412.1 413.7 415.2 416.8 + 418.3 419.8 421.3 422.8 424.3 425.7 427.2 428.6 430.0 431.4 432.8 434.1 435.4 436.7 438.0 439.3 440.6 441.8 + 443.0 444.2 445.4 446.5 460.7 471.5 479.5 485.5 489.9 493.2 495.6 497.4 498.7 499.6 500.2 500.6 500.7 500.7 + 500.6 500.3 499.9 499.4 498.9 498.2 497.5 496.7 495.8 494.9 494.0 493.0 491.9 490.8 489.6 488.4 487.1 485.8 + 484.5 483.1 481.7 480.2 478.7 477.2 475.7 450.6 421.1 ][ 339.3 342.8 345.5 346.5 345.6 342.9 339.4 339.2 339.0 + 338.8 338.6 338.4 338.2 338.1 337.9 337.8 337.7 337.7 337.6 337.7 337.7 337.8 338.0 338.2 338.5 338.8 339.3 + 339.8 340.5 341.2 342.2 343.2 344.5 346.1 347.8 349.9 352.4 355.3 358.7 362.7 367.4 372.9 379.3 386.7 394.8 + 403.4 404.0 404.6 405.2 405.8 406.4 406.9 407.5 408.1 408.7 409.2 409.8 410.3 410.9 411.4 411.9 412.5 413.0 + 413.4 413.9 414.4 414.8 415.3 415.7 416.1 416.5 416.9 417.3 417.6 418.0 418.3 418.6 418.8 419.1 419.3 419.6 + 419.8 419.9 420.1 420.2 420.3 420.4 420.5 420.6 420.6 420.6 420.6 420.5 420.5 420.4 420.3 420.2 420.0 419.9 + 419.7 419.5 419.3 419.0 418.7 418.5 418.2 417.8 417.5 417.1 416.8 416.4 416.0 415.6 415.1 414.7 414.2 413.8 + 413.3 412.8 412.3 411.8 411.2 410.7 410.1 409.6 409.0 408.5 407.9 407.3 406.7 398.3 389.9 382.2 375.4 369.5 + 364.5 360.2 356.6 353.5 350.9 348.6 346.7 345.1 343.7 342.6 341.6 340.7 340.0 339.5 339.0 338.6 338.3 338.0 + 337.9 337.8 337.7 337.6 337.7 337.7 337.8 337.9 338.0 338.1 338.3 338.5 338.7 338.9 339.1 339.3 342.8 345.5 ]plong + s[ 443.1 442.4 441.7 440.9 440.1 439.2 438.3 437.3 436.3 435.3 434.3 433.2 432.0 430.9 429.7 428.5 427.2 426.0 + 424.7 423.4 422.0 420.7 419.3 417.9 416.5 415.1 413.6 412.2 410.7 389.2 367.6 366.1 364.7 363.2 361.8 360.4 + 359.0 357.6 356.2 354.9 353.5 352.2 351.0 349.7 348.5 347.3 346.1 345.0 343.9 342.8 341.8 340.8 339.8 338.9 + 338.0 337.1 336.3 335.6 334.9 334.2 333.6 333.1 332.6 332.1 331.8 331.5 331.2 331.0 331.0 330.9 331.0 331.2 + 331.4 331.8 332.2 332.7 333.4 334.2 335.0 336.0 337.2 338.4 339.8 341.4 343.1 344.9 346.9 349.0 351.3 353.7 + 356.3 359.0 361.8 364.8 367.9 371.1 374.4 377.8 381.2 384.7 388.2 391.6 395.1 398.6 402.0 405.3 408.5 411.7 + 414.7 417.6 420.4 423.0 425.5 427.8 430.0 432.0 433.9 435.7 437.3 438.7 440.0 441.2 442.3 443.2 444.0 444.7 + 445.3 445.8 446.2 446.5 446.6 446.7 446.8 446.7 446.6 446.4 446.1 445.7 445.3 444.9 444.3 443.7 443.1 442.4 + 441.7 440.9 440.1 439.2 438.3 437.3 436.3 435.3 434.3 433.2 432.0 430.9 429.7 428.5 427.2 426.0 424.7 423.4 + 422.0 420.7 419.3 417.9 416.5 415.1 413.6 412.2 410.7 ][ 380.7 380.3 379.9 379.5 379.2 378.9 378.6 378.3 378.0 + 377.8 377.5 377.3 377.1 376.9 376.8 376.6 376.4 376.3 376.2 376.0 375.9 375.8 375.7 375.6 375.6 375.5 375.4 + 375.3 375.3 374.9 375.3 375.3 375.4 375.4 375.5 375.6 375.7 375.8 375.9 376.0 376.1 376.2 376.4 376.5 376.7 + 376.9 377.1 377.2 377.5 377.7 377.9 378.2 378.5 378.8 379.1 379.4 379.7 380.1 380.5 380.9 381.4 381.9 382.4 + 382.9 383.4 384.0 384.6 385.3 386.0 386.7 387.4 388.2 389.0 389.9 390.7 391.7 392.6 393.6 394.6 395.6 396.7 + 397.8 398.9 400.0 401.1 402.2 403.3 404.4 405.5 406.6 407.6 408.5 409.4 410.2 411.0 411.6 412.2 412.6 413.0 + 413.2 413.3 413.2 413.1 412.8 412.4 411.9 411.3 410.6 409.8 408.9 408.0 407.0 405.9 404.9 403.8 402.7 401.6 + 400.4 399.3 398.2 397.1 396.1 395.0 394.0 393.0 392.0 391.1 390.2 389.3 388.5 387.7 387.0 386.2 385.6 384.9 + 384.3 383.7 383.1 382.6 382.1 381.6 381.1 380.7 380.3 379.9 379.5 379.2 378.9 378.6 378.3 378.0 377.8 377.5 + 377.3 377.1 376.9 376.8 376.6 376.4 376.3 376.2 376.0 375.9 375.8 375.7 375.6 375.6 375.5 375.4 375.3 375.3 ]plong + 472.6 299.9 472.3 297.1 2 pls + 463.9 304.1 463.6 301.4 2 pls + 454.5 307.7 454.3 304.9 2 pls + 444.5 310.6 444.3 307.9 2 pls + 434.0 313.0 433.9 310.3 2 pls + 412.1 316.1 412.0 313.4 2 pls + 400.8 316.9 400.7 314.2 2 pls + 389.3 317.2 389.3 314.5 2 pls + 377.9 317.0 378.0 314.2 2 pls + 366.6 316.2 366.7 313.5 2 pls + 344.6 313.2 344.7 310.4 2 pls + 334.1 310.8 334.3 308.1 2 pls + 324.1 307.9 324.3 305.2 2 pls + 314.6 304.4 314.9 301.7 2 pls + 305.8 300.3 306.1 297.5 2 pls + 290.7 289.5 291.1 286.8 2 pls + 284.7 282.6 285.2 279.9 2 pls + 280.0 274.3 280.7 271.6 2 pls + 277.2 264.0 278.0 261.4 2 pls + 277.0 250.9 278.0 248.4 2 pls + 294.6 210.6 296.4 208.5 2 pls + 326.1 182.8 328.4 181.5 2 pls + 383.9 163.9 386.0 165.6 2 pls + 447.7 180.6 445.3 179.3 2 pls + 481.3 208.3 479.5 206.2 2 pls + 500.5 249.6 499.5 247.1 2 pls + 500.7 263.0 499.9 260.4 2 pls + 498.0 273.5 497.4 270.8 2 pls + 493.5 282.0 493.0 279.3 2 pls + 487.6 289.0 487.1 286.3 2 pls + 472.6 299.9 472.3 297.1 2 pls + 463.9 304.1 463.6 301.4 2 pls + 454.5 307.7 454.3 304.9 2 pls + 444.5 310.6 444.3 307.9 2 pls + 434.0 313.0 433.9 310.3 2 pls + 382.7 215.3 385.3 214.5 2 pls + 378.9 211.8 381.5 210.9 2 pls + 375.0 208.5 377.6 207.6 2 pls + 370.8 205.5 373.4 204.6 2 pls + 366.5 202.9 369.0 201.9 2 pls + 357.0 198.7 359.5 197.6 2 pls + 351.9 197.3 354.3 196.1 2 pls + 346.4 196.3 348.9 195.1 2 pls + 340.7 196.0 343.1 194.7 2 pls + 334.6 196.3 336.9 195.0 2 pls + 321.1 199.5 323.4 198.0 2 pls + 313.8 202.8 315.9 201.1 2 pls + 306.0 207.4 308.0 205.6 2 pls + 297.8 213.8 299.7 211.9 2 pls + 289.4 222.4 291.2 220.3 2 pls + 273.6 247.8 274.8 245.3 2 pls + 267.7 265.2 268.4 262.5 2 pls + 264.7 285.5 264.7 282.7 2 pls + 266.4 307.1 264.7 304.9 2 pls + 268.9 324.5 269.1 327.2 2 pls + 286.8 363.1 288.4 365.3 2 pls + 298.9 377.0 300.9 378.9 2 pls + 311.3 387.4 313.5 389.0 2 pls + 323.3 394.7 325.6 396.1 2 pls + 334.3 399.4 336.8 400.7 2 pls + 353.4 403.7 356.0 404.6 2 pls + 361.4 403.9 364.1 404.6 2 pls + 368.7 403.2 371.3 403.8 2 pls + 375.1 401.8 377.8 402.3 2 pls + 380.9 399.8 383.6 400.2 2 pls + 0.00000 0.00000 0.00000 1 1.08 clnstat + s[ 423.1 427.8 432.4 436.9 441.4 445.8 450.1 454.3 458.4 462.3 466.2 469.9 473.5 476.9 480.2 ][ 312.1 311.4 310.6 + 309.6 308.6 307.5 306.3 304.9 303.5 301.9 300.2 298.4 296.5 294.4 292.2 ]plong + s[ 423.1 418.4 413.6 408.8 404.0 399.1 394.2 389.3 384.5 379.6 374.7 369.9 365.1 360.3 355.5 ][ 312.1 312.7 313.2 + 313.7 314.0 314.3 314.4 314.5 314.4 314.3 314.1 313.8 313.3 312.8 312.2 ]plong + s[ 355.5 350.9 346.2 341.7 337.2 332.8 328.5 324.3 320.2 316.2 312.3 308.5 304.9 301.5 298.2 ][ 312.2 311.5 310.7 + 309.8 308.8 307.7 306.5 305.2 303.8 302.2 300.6 298.8 296.9 294.8 292.6 ]plong + s[ 298.2 295.0 292.1 289.3 286.8 284.5 282.4 280.7 279.3 278.2 277.7 277.6 278.3 279.9 282.7 ][ 292.6 290.2 287.7 + 285.0 282.0 278.8 275.4 271.6 267.5 263.0 258.0 252.5 246.2 239.2 231.3 ]plong + s[ 282.7 287.0 293.6 303.1 304.0 304.8 305.7 306.6 307.5 308.4 309.3 310.3 311.3 312.4 313.4 314.5 315.6 316.7 + 317.8 319.0 320.2 321.4 322.7 324.0 325.3 326.6 328.0 329.4 330.8 332.2 333.7 335.2 336.7 338.3 339.8 341.4 + 343.1 344.7 346.4 348.1 349.8 351.6 353.4 355.2 357.0 358.8 360.7 362.5 364.4 366.3 368.3 370.2 372.1 374.1 + 376.1 378.1 380.0 382.0 384.0 386.0 388.0 390.0 392.0 394.0 396.0 398.0 400.0 402.0 404.0 405.9 407.9 409.8 + 411.7 413.6 415.5 417.4 419.2 421.1 422.9 424.7 426.4 428.2 429.9 431.6 433.3 434.9 436.6 438.2 439.7 441.3 + 442.8 444.3 445.7 447.2 448.6 450.0 451.3 452.7 454.0 455.2 456.5 457.7 458.9 460.1 461.2 462.3 463.4 464.5 + 465.6 466.6 467.6 468.5 469.5 470.4 471.3 472.2 482.5 489.6 494.3 ][ 231.3 222.3 212.1 200.9 200.1 199.2 198.4 + 197.5 196.7 195.9 195.0 194.2 193.3 192.5 191.6 190.8 189.9 189.1 188.3 187.4 186.6 185.8 184.9 184.1 183.3 + 182.5 181.7 180.9 180.2 179.4 178.6 177.9 177.2 176.4 175.7 175.1 174.4 173.7 173.1 172.5 171.9 171.3 170.8 + 170.3 169.8 169.3 168.8 168.4 168.0 167.6 167.3 167.0 166.7 166.4 166.2 166.0 165.9 165.7 165.7 165.6 165.6 + 165.6 165.6 165.7 165.8 165.9 166.1 166.3 166.5 166.7 167.0 167.4 167.7 168.1 168.5 168.9 169.4 169.8 170.4 + 170.9 171.4 172.0 172.6 173.2 173.9 174.5 175.2 175.9 176.6 177.3 178.0 178.8 179.5 180.3 181.1 181.9 182.7 + 183.5 184.3 185.1 185.9 186.7 187.6 188.4 189.2 190.1 190.9 191.8 192.6 193.5 194.3 195.2 196.0 196.9 197.7 + 198.5 209.9 220.3 229.6 ]plong + s[ 494.3 497.4 499.1 500.0 500.1 499.6 498.7 497.4 495.7 493.7 491.4 488.9 486.2 483.3 480.2 ][ 229.6 237.7 244.9 + 251.3 257.0 262.1 266.7 270.8 274.7 278.2 281.4 284.4 287.2 289.7 292.2 ]plong + s[ 480.2 476.9 473.5 469.9 466.2 462.3 458.4 454.3 450.1 445.8 441.4 436.9 432.4 427.8 423.1 ][ 292.2 294.4 296.5 + 298.4 300.2 301.9 303.5 304.9 306.3 307.5 308.6 309.6 310.6 311.4 312.1 ]plong + s[ 480.2 476.9 473.5 469.9 466.2 462.3 458.4 454.3 450.1 445.8 441.4 436.9 432.4 427.8 423.1 ][ 292.2 294.4 296.5 + 298.4 300.2 301.9 303.5 304.9 306.3 307.5 308.6 309.6 310.6 311.4 312.1 ]plong + s[ 388.9 387.3 385.8 384.2 382.6 381.0 379.3 377.6 375.8 374.0 372.2 370.3 368.4 366.4 364.4 ][ 218.3 216.6 215.0 + 213.4 211.9 210.4 209.0 207.6 206.2 205.0 203.7 202.6 201.5 200.5 199.5 ]plong + s[ 364.4 362.3 360.2 358.1 355.8 353.6 351.3 348.9 346.4 343.9 341.4 338.7 336.0 333.2 330.4 ][ 199.5 198.6 197.8 + 197.1 196.5 195.9 195.5 195.1 194.9 194.7 194.7 194.8 195.1 195.5 196.0 ]plong + s[ 330.4 327.4 324.4 321.3 318.1 314.8 311.5 308.0 304.5 300.9 297.3 293.6 289.9 286.3 282.7 ][ 196.0 196.7 197.6 + 198.8 200.1 201.7 203.5 205.6 208.1 210.9 214.1 217.6 221.7 226.2 231.3 ]plong + s[ 282.7 279.2 275.9 272.8 270.0 267.7 265.9 264.7 264.2 264.5 265.5 267.4 270.0 273.4 277.3 ][ 231.3 236.9 243.1 + 249.9 257.3 265.3 273.8 282.7 292.1 301.7 311.3 321.0 330.3 339.3 347.8 ]plong + s[ 277.3 281.8 286.7 291.9 297.2 302.7 308.1 313.5 318.8 323.9 328.9 333.7 338.3 342.7 346.9 ][ 347.8 355.7 363.0 + 369.6 375.4 380.6 385.1 389.0 392.4 395.2 397.6 399.6 401.1 402.4 403.3 ]plong + s[ 346.9 350.9 354.7 358.4 361.8 365.2 368.3 371.3 374.2 376.9 379.5 382.0 384.4 386.7 388.9 ][ 403.3 404.0 404.5 + 404.7 404.7 404.6 404.3 403.8 403.3 402.6 401.8 400.9 399.9 398.8 397.7 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 405.85 297.13 moveto[ 405.8 407.6 432.8 431.1 ][ 297.1 309.1 305.5 293.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 410.2 298.2 411.5 306.9 410.1 305.8 409.2 305.5 4 pls + s[ 418.5 417.2 416.7 416.6 416.8 417.6 419.2 420.4 421.1 421.4 421.2 420.7 420.2 418.9 417.2 416.1 415.7 415.4 + 415.6 416.1 417.1 418.4 420.1 421.0 421.5 421.6 421.3 420.2 418.5 ][ 305.9 305.6 304.8 304.0 303.1 302.6 302.0 + 301.4 300.4 299.5 298.3 297.5 297.2 296.9 297.2 297.8 298.2 299.1 300.4 301.1 301.8 302.1 302.2 302.5 303.3 + 304.1 305.0 305.6 305.9 ]plong + s[ 427.2 425.9 424.9 424.2 424.0 424.1 424.7 425.9 426.8 428.1 429.1 429.8 430.0 429.8 429.2 428.0 427.2 ][ 304.6 + 304.4 303.2 301.2 300.0 297.9 296.5 295.9 295.8 296.0 297.1 299.2 300.4 302.5 303.9 304.5 304.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 432.35 301.24 moveto[ 432.3 433.1 438.8 438.1 ][ 301.2 306.7 305.9 300.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 435.5 434.9 434.4 434.0 433.9 434.1 434.5 435.0 435.7 436.2 436.8 437.2 437.2 437.1 436.7 436.2 435.5 ][ 305.3 + 305.2 304.7 304.0 303.5 302.8 302.2 301.9 301.8 301.9 302.4 303.1 303.6 304.4 304.9 305.2 305.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 341.91 292.87 moveto[ 341.9 340.2 366.7 368.4 ][ 292.9 304.9 308.6 296.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 343.2 343.2 343.5 343.8 344.6 346.2 347.1 347.6 348.1 348.2 348.0 347.3 343.7 349.5 ][ 301.5 301.9 302.8 303.3 + 303.8 304.0 303.7 303.4 302.6 301.8 300.9 299.5 294.8 295.6 ]plong + 357.8 299.7 351.6 298.9 354.9 305.2 3 pls + 356.2 296.5 354.9 305.2 2 pls + s[ 361.6 360.4 359.7 359.6 359.8 360.5 361.5 362.8 363.6 364.8 365.4 365.6 365.4 364.7 363.7 362.4 361.6 ][ 306.2 + 305.6 304.2 302.1 300.9 298.8 297.7 297.5 297.6 298.2 299.5 301.7 302.9 304.9 306.0 306.3 306.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 367.44 304.38 moveto[ 367.4 366.7 372.4 373.2 ][ 304.4 309.8 310.6 305.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 369.3 368.8 368.4 368.3 368.3 368.7 369.3 369.8 370.5 371.0 371.4 371.6 371.5 371.1 370.6 370.0 369.3 ][ 309.2 + 308.9 308.3 307.5 307.0 306.3 305.9 305.7 305.8 306.1 306.7 307.5 308.0 308.7 309.1 309.3 309.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 294.85 269.16 moveto[ 294.8 287.9 309.7 316.7 ][ 269.2 279.1 294.4 284.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 291.2 295.0 294.9 295.9 296.8 297.4 298.5 298.9 299.3 299.1 298.3 297.3 296.0 295.5 294.6 ][ 279.4 282.0 277.8 + 278.6 278.7 278.6 277.8 277.1 275.9 274.7 273.6 272.9 272.5 272.6 273.1 ]plong + s[ 299.4 298.6 298.7 299.5 300.3 301.8 303.2 304.5 305.2 305.9 305.9 305.0 304.3 302.8 301.4 300.1 299.4 ][ 285.1 + 284.1 282.6 280.6 279.6 278.1 277.6 278.0 278.5 279.5 281.0 283.0 284.0 285.5 286.0 285.6 285.1 ]plong + s[ 306.3 305.5 305.5 306.4 307.1 308.6 310.0 311.3 312.0 312.8 312.7 311.9 311.2 309.6 308.2 306.9 306.3 ][ 290.0 + 288.9 287.4 285.4 284.4 282.9 282.4 282.8 283.3 284.3 285.8 287.8 288.8 290.3 290.8 290.4 290.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 312.32 291.06 moveto[ 312.3 309.1 313.9 317.0 ][ 291.1 295.6 298.9 294.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 311.8 311.5 311.4 311.6 311.9 312.5 313.2 313.8 314.4 314.7 314.8 314.6 314.3 313.6 312.9 312.4 311.8 ][ 296.2 + 295.7 295.0 294.2 293.8 293.4 293.2 293.3 293.8 294.2 294.9 295.7 296.1 296.6 296.7 296.6 296.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 281.82 239.39 moveto[ 281.8 293.1 296.8 285.6 ][ 239.4 243.9 234.6 230.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 293.2 292.4 290.9 288.8 287.6 285.9 285.0 285.1 285.4 286.3 287.7 289.8 291.0 292.8 293.6 293.5 293.2 ][ 239.0 + 240.0 240.3 239.9 239.5 238.3 237.1 235.8 235.0 234.0 233.7 234.1 234.5 235.7 236.9 238.2 239.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 292.91 232.87 moveto[ 292.9 298.0 300.2 295.1 ][ 232.9 234.9 229.6 227.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 298.0 297.6 297.0 296.2 295.7 295.1 294.8 294.8 295.1 295.5 296.1 296.9 297.4 298.0 298.3 298.3 298.0 ][ 232.2 + 232.6 232.9 232.8 232.6 232.1 231.5 230.9 230.2 229.8 229.6 229.6 229.8 230.3 231.0 231.5 232.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 487.52 219.88 moveto[ 487.5 476.5 483.8 494.9 ][ 219.9 224.8 241.2 236.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 482.0 481.1 480.2 479.9 479.7 480.5 482.3 484.2 485.9 487.0 487.9 488.0 488.2 487.8 486.8 486.4 485.1 484.0 + 483.1 482.9 482.8 483.2 484.2 ][ 230.1 230.1 229.1 228.3 227.0 225.8 224.5 223.7 223.4 223.8 224.8 225.1 226.4 + 227.6 228.4 228.6 228.7 228.3 227.4 227.0 225.7 224.6 223.7 ]plong + s[ 483.1 483.0 483.8 485.5 486.7 488.7 490.2 491.1 491.5 491.6 490.8 489.1 487.9 485.8 484.4 483.5 483.1 ][ 235.6 + 234.3 233.0 231.8 231.2 230.8 231.0 232.0 232.8 234.1 235.3 236.6 237.1 237.6 237.3 236.3 235.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 487.76 239.63 moveto[ 487.8 482.7 485.1 490.1 ][ 239.6 241.9 247.1 244.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 484.7 484.7 484.9 485.5 486.0 486.8 487.4 487.9 488.2 488.2 487.9 487.3 486.9 486.1 485.4 485.0 484.7 ][ 243.8 + 243.2 242.5 242.0 241.8 241.7 242.0 242.3 243.0 243.6 244.2 244.8 245.0 245.1 244.8 244.4 243.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 457.93 287.06 moveto[ 457.9 465.1 485.6 478.5 ][ 287.1 296.8 281.8 272.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 462.3 285.9 467.5 293.0 465.7 292.7 464.8 292.9 4 pls + s[ 470.6 470.9 471.7 472.3 473.2 474.5 475.0 475.1 474.9 474.4 473.6 472.2 466.3 471.0 ][ 288.1 288.4 288.9 289.0 + 288.8 287.8 287.0 286.4 285.5 284.8 284.4 283.9 283.0 279.5 ]plong + s[ 480.3 479.0 477.6 476.0 475.3 474.4 474.3 475.1 475.8 477.0 478.4 480.0 480.7 481.6 481.7 480.9 480.3 ][ 283.6 + 284.0 283.5 282.1 281.1 279.1 277.6 276.5 276.1 275.6 276.2 277.6 278.6 280.5 282.0 283.1 283.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 483.25 278.23 moveto[ 483.3 486.5 491.2 487.9 ][ 278.2 282.7 279.3 274.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 487.9 487.4 486.7 486.0 485.7 485.5 485.6 485.9 486.5 487.0 487.7 488.4 488.7 488.9 488.8 488.5 487.9 ][ 280.4 + 280.5 280.4 279.9 279.5 278.8 278.1 277.6 277.1 277.0 277.1 277.6 278.0 278.7 279.4 279.9 280.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 390.56 194.12 moveto[ 390.6 381.5 400.5 409.6 ][ 194.1 202.1 223.7 215.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 392.8 204.9 387.9 199.3 2 pls + s[ 396.8 397.5 397.5 397.0 396.7 395.6 394.4 393.2 392.9 392.2 392.2 392.7 392.9 394.1 395.3 396.8 398.4 399.7 + 400.0 399.5 399.0 397.8 396.9 ][ 212.6 211.4 210.2 209.0 208.7 208.0 208.0 208.5 208.8 209.9 211.1 212.3 212.6 + 213.3 213.4 212.6 211.2 209.5 208.0 206.8 206.2 205.5 205.8 ]plong + s[ 398.2 397.7 398.1 399.4 400.3 402.1 403.6 404.8 405.3 405.8 405.5 404.2 403.2 401.4 399.9 398.7 398.2 ][ 218.6 + 217.4 215.9 214.2 213.4 212.3 212.1 212.8 213.4 214.6 216.1 217.8 218.6 219.7 219.9 219.2 218.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 403.82 221.06 moveto[ 403.8 399.7 403.5 407.6 ][ 221.1 224.7 229.0 225.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 402.1 402.0 402.0 402.4 402.8 403.5 404.2 404.7 405.2 405.4 405.3 404.9 404.6 403.8 403.1 402.6 402.1 ][ 225.9 + 225.4 224.7 224.0 223.7 223.3 223.4 223.6 224.2 224.7 225.4 226.1 226.4 226.7 226.7 226.5 225.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 309.99 182.85 moveto[ 310.0 312.6 340.8 338.1 ][ 182.8 194.7 188.4 176.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 320.6 186.1 313.2 187.7 2 pls + s[ 325.3 329.8 326.6 327.8 328.6 328.9 329.0 328.8 328.2 327.2 325.8 324.6 323.5 323.2 322.9 ][ 190.1 189.1 186.4 + 186.1 185.5 185.0 183.7 182.9 181.8 181.2 181.0 181.3 182.0 182.5 183.4 ]plong + s[ 335.1 333.8 332.7 331.8 331.6 331.5 332.1 333.2 334.0 335.3 336.4 337.3 337.5 337.6 337.0 335.9 335.1 ][ 187.9 + 187.8 186.8 184.8 183.6 181.5 180.1 179.4 179.2 179.3 180.4 182.3 183.5 185.7 187.1 187.8 187.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 339.99 184.20 moveto[ 340.0 341.2 346.8 345.6 ][ 184.2 189.6 188.3 182.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 343.4 342.9 342.3 341.9 341.8 341.8 342.2 342.7 343.4 343.9 344.5 344.9 345.0 345.0 344.6 344.1 343.4 ][ 188.1 + 187.9 187.5 186.9 186.4 185.6 185.0 184.6 184.5 184.6 185.0 185.7 186.2 186.9 187.5 187.9 188.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 263.95 228.12 moveto[ 264.0 274.0 279.6 269.6 ][ 228.1 234.9 226.6 219.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 275.2 274.1 272.6 270.7 269.6 268.1 267.6 267.9 268.4 269.4 270.9 272.9 273.9 275.4 276.0 275.6 275.2 ][ 230.1 + 231.0 230.9 230.1 229.4 227.9 226.5 225.2 224.5 223.7 223.8 224.6 225.3 226.8 228.2 229.5 230.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 276.17 224.07 moveto[ 276.2 280.7 284.0 279.4 ][ 224.1 227.2 222.4 219.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 281.3 280.8 280.1 279.4 278.9 278.5 278.3 278.4 278.8 279.3 280.0 280.8 281.2 281.7 281.8 281.7 281.3 ][ 224.5 + 224.8 224.9 224.7 224.4 223.8 223.1 222.6 221.9 221.7 221.5 221.7 222.0 222.6 223.3 223.9 224.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 269.90 338.14 moveto[ 269.9 259.1 267.4 278.2 ][ 338.1 343.6 360.0 354.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 261.9 264.0 265.8 266.4 267.2 267.7 269.0 269.8 270.7 271.1 270.9 270.3 269.4 268.8 267.9 ][ 345.5 349.6 345.8 + 346.9 347.5 347.7 347.5 347.1 346.2 345.0 343.7 342.6 341.7 341.5 341.5 ]plong + s[ 266.5 266.3 267.0 268.7 269.8 271.8 273.3 274.3 274.7 274.9 274.1 272.5 271.3 269.3 267.8 266.8 266.5 ][ 354.4 + 353.1 351.8 350.5 349.9 349.3 349.5 350.4 351.2 352.5 353.8 355.1 355.7 356.3 356.1 355.2 354.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 271.31 358.23 moveto[ 271.3 266.4 269.0 273.9 ][ 358.2 360.7 365.9 363.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 268.4 268.4 268.6 269.2 269.6 270.4 271.1 271.5 271.9 271.9 271.7 271.1 270.7 269.9 269.2 268.8 268.4 ][ 362.5 + 361.9 361.3 360.7 360.5 360.4 360.6 360.9 361.6 362.1 362.8 363.4 363.6 363.7 363.5 363.2 362.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 335.06 403.25 moveto[ 335.1 332.7 350.3 352.7 ][ 403.2 415.1 418.7 406.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 340.2 339.6 338.3 337.5 336.4 335.8 335.8 336.2 336.9 337.9 339.2 339.6 340.8 341.4 341.6 341.5 340.9 339.9 + 338.6 338.2 337.0 336.4 336.2 ][ 413.7 414.4 414.6 414.4 413.7 412.3 410.2 408.2 406.6 406.0 405.8 405.9 406.5 + 407.5 408.8 409.2 410.4 411.0 411.2 411.1 410.5 409.5 408.2 ]plong + s[ 345.3 344.1 343.6 343.6 343.8 344.6 345.7 347.0 347.8 349.0 349.5 349.5 349.3 348.5 347.4 346.1 345.3 ][ 416.0 + 415.3 413.9 411.8 410.5 408.6 407.5 407.4 407.5 408.2 409.6 411.7 412.9 414.9 416.0 416.1 416.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 351.27 414.52 moveto[ 351.3 350.2 355.8 356.9 ][ 414.5 419.9 421.1 415.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 352.8 352.4 352.0 351.9 352.0 352.4 353.0 353.5 354.3 354.7 355.1 355.2 355.1 354.7 354.1 353.6 352.8 ][ 419.4 + 419.1 418.5 417.7 417.2 416.5 416.1 416.0 416.1 416.5 417.1 417.9 418.4 419.0 419.4 419.6 419.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 379.46 405.50 moveto[ 379.5 385.4 401.4 395.5 ][ 405.5 416.1 407.1 396.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 389.7 388.7 387.6 386.3 385.9 385.0 384.7 384.9 385.2 386.1 387.3 388.6 388.9 389.8 390.1 389.7 388.7 387.3 + 385.9 384.6 383.9 383.0 383.1 ][ 408.4 407.5 407.2 407.4 407.6 408.6 409.7 411.0 411.4 412.3 412.6 412.4 412.2 + 411.2 410.1 408.4 406.6 405.0 404.3 404.5 404.9 405.9 406.8 ]plong + s[ 395.9 394.6 393.2 391.8 391.2 390.6 390.7 391.6 392.3 393.6 394.9 396.3 396.9 397.6 397.5 396.6 395.9 ][ 408.3 + 408.5 407.9 406.2 405.1 403.1 401.6 400.6 400.2 400.0 400.7 402.3 403.4 405.4 406.9 407.9 408.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 399.47 403.31 moveto[ 399.5 402.2 407.2 404.5 ][ 403.3 408.1 405.3 400.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 403.9 403.3 402.6 402.0 401.8 401.6 401.8 402.1 402.8 403.4 404.0 404.6 404.9 405.0 404.8 404.5 403.9 ][ 406.0 + 406.1 405.9 405.3 404.9 404.1 403.4 403.0 402.6 402.6 402.8 403.3 403.7 404.5 405.2 405.7 406.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 351.77 440.60 moveto[ 351.8 351.8 426.1 426.1 ][ 440.6 452.7 452.7 440.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 353.9 442.3 357.2 451.0 2 pls + 360.5 442.3 357.2 451.0 2 pls + 359.3 445.2 355.1 445.2 2 pls + 369.3 442.3 369.3 451.0 2 pls + 374.7 451.0 369.3 451.0 2 pls + 372.7 446.9 369.3 446.9 2 pls + 376.8 442.3 376.8 451.0 2 pls + 381.8 442.3 381.8 451.0 2 pls + 384.8 451.0 378.9 451.0 2 pls + s[ 392.3 391.5 390.2 388.5 387.3 386.4 386.4 386.9 387.3 388.1 390.6 391.5 391.9 392.3 392.3 391.5 390.2 388.5 + 387.3 386.4 ][ 449.8 450.6 451.0 451.0 450.6 449.8 449.0 448.1 447.7 447.3 446.4 446.0 445.6 444.8 443.5 442.7 + 442.3 442.3 442.7 443.5 ]plong + 404.4 442.3 403.6 442.3 402.7 442.7 402.3 443.9 402.3 451.0 5 pls + 404.0 448.1 401.1 448.1 2 pls + s[ 406.5 411.5 411.5 411.1 410.7 409.8 408.6 407.7 406.9 406.5 406.5 406.9 407.7 408.6 409.8 410.7 411.5 ][ 445.6 + 445.6 446.4 447.3 447.7 448.1 448.1 447.7 446.9 445.6 444.8 443.5 442.7 442.3 442.3 442.7 443.5 ]plong + s[ 418.6 418.2 416.9 415.7 414.4 414.0 414.4 415.3 417.3 418.2 418.6 418.6 418.2 416.9 415.7 414.4 414.0 ][ 446.9 + 447.7 448.1 448.1 447.7 446.9 446.0 445.6 445.2 444.8 443.9 443.5 442.7 442.3 442.3 442.7 443.5 ]plong + 424.0 442.3 423.2 442.3 422.4 442.7 421.9 443.9 421.9 451.0 5 pls + 423.6 448.1 420.7 448.1 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/degen1.ast b/ast_tester/degen1.ast new file mode 100644 index 0000000..202f149 --- /dev/null +++ b/ast_tester/degen1.ast @@ -0,0 +1,318 @@ +# +# This FrameSet has 2 axes in the Base (GRID) Frame, but 3 in the Current +# Frame. It is used to test the ability of FitsChan to create degenerate +# WCS axes. It represents the first DEC plane from a WAVE/RA/DEC 3D NDF. +# + Begin FrameSet # Set of inter-related coordinate systems +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "CMP" # Coordinate system domain +# Epoch = 2003.0173483725 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Wavelength axis with no distortion" # Label for axis 3 +# System = "Compound" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Uni3 = "um " # Units for axis 3 +# Dir1 = 0 # Plot axis 1 in reverse direction + ActUnt = 0 # Unit strings do not affect alignment + IsA Frame # Coordinate system description + Nframe = 4 # Number of Frames in FrameSet +# Base = 1 # Index of base Frame + Currnt = 4 # Index of current Frame + Nnode = 5 # Number of nodes in FrameSet + Nod1 = 3 # Frame 1 is associated with node 3 + Nod2 = 4 # Frame 2 is associated with node 4 + Nod3 = 5 # Frame 3 is associated with node 5 + Nod4 = 2 # Frame 4 is associated with node 2 + Lnk2 = 1 # Node 2 is derived from node 1 + Lnk3 = 1 # Node 3 is derived from node 1 + Lnk4 = 1 # Node 4 is derived from node 1 + Lnk5 = 1 # Node 5 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Data grid indices; first pixel at (1,1)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Data grid index 1" # Label for axis 1 +# Lbl2 = "Data grid index 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + ActUnt = 0 # Unit strings do not affect alignment + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Data grid index 1" # Axis Label + Symbol = "g1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Data grid index 2" # Axis Label + Symbol = "g2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm2 = # Frame number 2 + Begin Frame # Coordinate system description + Title = "Pixel coordinates; first pixel at (0.5,0.5)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "PIXEL" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + ActUnt = 0 # Unit strings do not affect alignment + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 1" # Axis Label + Symbol = "p1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 2" # Axis Label + Symbol = "p2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm3 = # Frame number 3 + Begin Frame # Coordinate system description + Title = "Axis coordinates; first pixel at (0.5,0.5)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "AXIS" # Coordinate system domain +# Lbl1 = "Axis 1" # Label for axis 1 +# Lbl2 = "Axis 2" # Label for axis 2 + ActUnt = 0 # Unit strings do not affect alignment + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Axis 1" # Axis Label + Symbol = "a1" # Axis symbol + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Axis 2" # Axis Label + Symbol = "a2" # Axis symbol + End Axis + End Frame + Frm4 = # Frame number 4 + Begin CmpFrame # Compound coordinate system description +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "CMP" # Coordinate system domain +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Wavelength axis with no distortion" # Label for axis 3 +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Uni3 = "um " # Units for axis 3 +# Dir1 = 0 # Plot axis 1 in reverse direction + ActUnt = 0 # Unit strings do not affect alignment + IsA Frame # Coordinate system description + Axp1 = 3 # Axis 1 permuted to use internal axis 3 + Axp2 = 1 # Axis 2 permuted to use internal axis 1 + Axp3 = 2 # Axis 3 permuted to use internal axis 2 + FrameA = # First component Frame + Begin SkyFrame # Description of celestial coordinate system + Naxes = 2 # Number of coordinate axes + Epoch = 2003.0173483725 # Julian epoch of observation + System = "FK5" # Coordinate system type + ActUnt = 0 # Unit strings do not affect alignment + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + Proj = "gnomonic" # Description of sky projection + Eqnox = 2000 # Julian epoch of mean equinox + End SkyFrame + FrameB = # Second component Frame + Begin Frame # Coordinate system description + Naxes = 1 # Number of coordinate axes + ActUnt = 0 # Unit strings do not affect alignment + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Wavelength axis with no distortion" # Axis Label + Symbol = "WAVE-WAV" # Axis symbol + Unit = "um " # Axis units + End Axis + End Frame + End CmpFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + Nout = 3 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin PermMap # Coordinate permutation + Nin = 2 # Number of input coordinates + Nout = 3 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Out1 = 1 # Output coordinate 1 = input coordinate 1 + Out2 = 2 # Output coordinate 2 = input coordinate 2 + Out3 = -1 # Output coordinate 3 = constant no. 1 + InCpy = 1 # Input coordinates = output coordinates + Nconst = 1 # Number of constants + Con1 = 1 # Constant number 1 + End PermMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -512.5 # Shift for axis 1 + Sft2 = -480.5 # Shift for axis 2 + Sft3 = -480.5 # Shift for axis 3 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -0.00109 # Forward matrix value + M1 = 5.84161700642502e-07 # Forward matrix value + M2 = -5.84161700642502e-07 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 3 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + WcsAx1 = 2 # Index of celestial longitude axis + WcsAx2 = 3 # Index of celestial latitude axis + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin PermMap # Coordinate permutation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Out1 = 2 # Output coordinate 1 = input coordinate 2 + Out2 = 3 # Output coordinate 2 = input coordinate 3 + Out3 = 1 # Output coordinate 3 = input coordinate 1 + In1 = 3 # Input coordinate 1 = output coordinate 3 + In2 = 1 # Input coordinate 2 = output coordinate 1 + In3 = 2 # Input coordinate 3 = output coordinate 2 + End PermMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + Nout = 3 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + End SphMap + MapB = # Second component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.0122898607703168 # Forward matrix value + M1 = -0.231781944526882 # Forward matrix value + M2 = -0.972690130264301 # Forward matrix value + M3 = -0.00292831230784838 # Forward matrix value + M4 = -0.972767767862061 # Forward matrix value + M5 = 0.231763445771093 # Forward matrix value + M6 = -0.999920188969737 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = -0.0126339103497717 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + End CmpMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + End SphMap + End CmpMap + MapB = # Second component Mapping + Begin WinMap # Map one window on to another + Nin = 1 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Sft1 = 1.9534 # Shift for axis 1 + End WinMap + End CmpMap + MapB = # Second component Mapping + Begin PermMap # Coordinate permutation + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Out1 = 3 # Output coordinate 1 = input coordinate 3 + Out2 = 1 # Output coordinate 2 = input coordinate 1 + Out3 = 2 # Output coordinate 3 = input coordinate 2 + In1 = 2 # Input coordinate 1 = output coordinate 2 + In2 = 3 # Input coordinate 2 = output coordinate 3 + In3 = 1 # Input coordinate 3 = output coordinate 1 + End PermMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + Map3 = # Mapping between nodes 1 and 3 + Begin UnitMap # Unit (null) Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + End UnitMap + Map4 = # Mapping between nodes 1 and 4 + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Sft1 = -0.5 # Shift for axis 1 + Sft2 = -0.5 # Shift for axis 2 + End WinMap + Map5 = # Mapping between nodes 1 and 5 + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Sft1 = -0.5 # Shift for axis 1 + Sft2 = -0.5 # Shift for axis 2 + End WinMap + End FrameSet diff --git a/ast_tester/degen1.fits-wcs b/ast_tester/degen1.fits-wcs new file mode 100644 index 0000000..6e5309d --- /dev/null +++ b/ast_tester/degen1.fits-wcs @@ -0,0 +1,40 @@ +WCSAXES = 3 / Number of WCS axes +CRPIX1 = 1.0 / Reference pixel on axis 1 +CRPIX2 = 480.5 / Reference pixel on axis 2 +CRPIX3 = 480.5 / Reference pixel on axis 3 +CRVAL1 = 2.510935 / Value at ref. pixel on axis 1 +CRVAL2 = 166.59799 / Value at ref. pixel on axis 2 +CRVAL3 = -0.723889 / Value at ref. pixel on axis 3 +CTYPE1 = 'WAVE-WAV' / Wavelength axis with no distortion +CTYPE2 = 'RA---TAN' / Type of co-ordinate on axis 2 +CTYPE3 = 'DEC--TAN' / Type of co-ordinate on axis 3 +CD1_1 = -0.00109 / Transformation matrix element +CD2_2 = 3.347E-5 / Transformation matrix element +CD3_3 = -3.347E-5 / Transformation matrix element +CUNIT1 = 'um ' / Units for axis 1 +MJD-OBS = 52646.586 / Modified Julian Date of observation +DATE-OBS= '2003-01-07T14:03:28.816' / Date of observation +RADESYS = 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / [yr] Epoch of reference equinox +WCSAXESA= 2 / Number of WCS axes +WCSNAMEA= 'PIXEL ' / Reference name for the coord. frame +CRPIX1A = 1.0 / Reference pixel on axis 1 +CRPIX2A = 1.0 / Reference pixel on axis 2 +CRVAL1A = 0.5 / Value at ref. pixel on axis 1 +CRVAL2A = 0.5 / Value at ref. pixel on axis 2 +CTYPE1A = 'p1 ' / Pixel coordinate 1 +CTYPE2A = 'p2 ' / Pixel coordinate 2 +CD1_1A = 1.0 / Transformation matrix element +CD2_2A = 1.0 / Transformation matrix element +CUNIT1A = 'pixel ' / Units for axis 1 +CUNIT2A = 'pixel ' / Units for axis 2 +WCSAXESB= 2 / Number of WCS axes +WCSNAMEB= 'AXIS ' / Reference name for the coord. frame +CRPIX1B = 1.0 / Reference pixel on axis 1 +CRPIX2B = 1.0 / Reference pixel on axis 2 +CRVAL1B = 0.5 / Value at ref. pixel on axis 1 +CRVAL2B = 0.5 / Value at ref. pixel on axis 2 +CTYPE1B = 'a1 ' / Axis 1 +CTYPE2B = 'a2 ' / Axis 2 +CD1_1B = 1.0 / Transformation matrix element +CD2_2B = 1.0 / Transformation matrix element diff --git a/ast_tester/doplot b/ast_tester/doplot new file mode 100755 index 0000000..f7df1ce --- /dev/null +++ b/ast_tester/doplot @@ -0,0 +1,53 @@ +#!/bin/tcsh + + if( ! $?STARLINK ) then + setenv STARLINK /star + endif + + if( ! $?AST ) then + setenv AST $STARLINK + endif + + if( ! $?LDFLAGS ) then + setenv LDFLAGS "" + endif + + setenv PATH $AST/bin\:$STARLINK/bin\:$PATH + + gfortran -fno-second-underscore -o plotter plotter.f -fno-range-check $LDFLAGS -I$AST/include -I$STARLINK/include -L$AST/lib -L$STARLINK/lib `ast_link -pgp -ems` `pgplot_link` + + set bn = $1 + set n = "${bn}.head" + + set atfile = "${bn}.attr" + if( -e $atfile ) then + set attr1 = `cat $atfile` + else + set attr1 = ' ' + endif + + set atfile = "${bn}.fattr" + if( -e $atfile ) then + set attr2 = `cat $atfile` + else + set attr2 = ' ' + endif + + set boxfile = "${bn}.box" + if( -e $boxfile ) then + set box = `cat $boxfile` + else + set box = ' ' + endif + + + plotter $n "$attr1" "$attr2" a.ps $box + + if( -e $STARLINK/bin/psmerge ) then + $STARLINK/bin/psmerge -t300x300 -r90 a.ps > b.ps + else + cp a.ps b.ps + endif + + gv b.ps -orientation=landscape + diff --git a/ast_tester/dss.ast b/ast_tester/dss.ast new file mode 100644 index 0000000..5c1d6ba --- /dev/null +++ b/ast_tester/dss.ast @@ -0,0 +1,178 @@ + Begin FrameSet # Set of inter-related coordinate systems +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic polynomial projection" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Epoch = 2000 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# System = "FK5" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + IsA Frame # Coordinate system description + Nframe = 2 # Number of Frames in FrameSet + Base = 1 # Index of base Frame + Currnt = 2 # Index of current Frame + Lnk2 = 1 # Node 2 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Pixel Coordinates" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Pixel axis 1" # Label for axis 1 +# Lbl2 = "Pixel axis 2" # Label for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel axis 1" # Axis Label + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel axis 2" # Axis Label + End Axis + End Frame + Frm2 = # Frame number 2 + Begin SkyFrame # Description of celestial coordinate system + Ident = " " # Permanent Object identification string + IsA Object # AST Object +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic polynomial projection" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain + Epoch = 2000 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 + System = "FK5" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + Proj = "gnomonic polynomial" # Description of sky projection +# SkyTol = 0.001 # Smallest significant separation [arc-sec] + Eqnox = 2000 # Julian epoch of mean equinox + SRefIs = "Ignored" # Not rotated (ref. pos. is ignored) + SRef1 = 2.82723746556841 # Ref. pos. RA 10:47:57.3 + SRef2 = -1.05181225405027 # Ref. pos. Dec -60:15:52 + End SkyFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 745.064500886117 # Shift for axis 1 + Sft2 = 879.162200955923 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -0.00044129690205588 # Forward matrix value + M1 = 0.00044129690205588 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TPN" # Gnomonic polynomial projection + PV1_0 = 0.000377778137684806 # Projection parameter 0 for axis 1 + PV1_1 = 0.0186753721655106 # Projection parameter 1 for axis 1 + PV1_2 = 1.46591811191706e-05 # Projection parameter 2 for axis 1 + PV1_4 = -5.65418344902417e-09 # Projection parameter 4 for axis 1 + PV1_5 = -1.65986195781758e-10 # Projection parameter 5 for axis 1 + PV1_6 = 3.32464554843278e-09 # Projection parameter 6 for axis 1 + PV1_7 = 6.80293751629639e-10 # Projection parameter 7 for axis 1 + PV1_8 = -1.03153913092106e-11 # Projection parameter 8 for axis 1 + PV1_9 = 6.57701840963167e-10 # Projection parameter 9 for axis 1 + PV1_10 = 4.68437905886917e-11 # Projection parameter 10 for axis 1 + PV1_17 = 0 # Projection parameter 17 for axis 1 + PV1_19 = 0 # Projection parameter 19 for axis 1 + PV1_21 = 0 # Projection parameter 21 for axis 1 + PV2_0 = 0.000207343956905325 # Projection parameter 0 for axis 2 + PV2_1 = 0.0186750898065428 # Projection parameter 1 for axis 2 + PV2_2 = -1.65783917251522e-05 # Projection parameter 2 for axis 2 + PV2_4 = -5.13787679379806e-09 # Projection parameter 4 for axis 2 + PV2_5 = -1.76239327122594e-09 # Projection parameter 5 for axis 2 + PV2_6 = 2.71615473132514e-10 # Projection parameter 6 for axis 2 + PV2_7 = 7.08890740709917e-10 # Projection parameter 7 for axis 2 + PV2_8 = 1.84326185131453e-11 # Projection parameter 8 for axis 2 + PV2_9 = 6.84910619895694e-10 # Projection parameter 9 for axis 2 + PV2_10 = 7.33258596347083e-13 # Projection parameter 10 for axis 2 + PV2_17 = 0 # Projection parameter 17 for axis 2 + PV2_19 = 0 # Projection parameter 19 for axis 2 + PV2_21 = 0 # Projection parameter 21 for axis 2 + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.825772160351044 # Forward matrix value + M1 = -0.309203321967609 # Forward matrix value + M2 = -0.471692320133966 # Forward matrix value + M3 = -0.268488518727377 # Forward matrix value + M4 = -0.950995954609795 # Forward matrix value + M5 = 0.153364303628268 # Forward matrix value + M6 = -0.49599824042102 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = -0.868323525823902 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 2.82723746556841 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End FrameSet diff --git a/ast_tester/dss.dss b/ast_tester/dss.dss new file mode 100644 index 0000000..c57a035 --- /dev/null +++ b/ast_tester/dss.dss @@ -0,0 +1,58 @@ +CNPIX1 = 1.0 / X corner (pixels) +CNPIX2 = 1.0 / Y corner (pixels) +PPO1 = 0.0 / Orientation co-efficients +PPO2 = 0.0 +PPO3 = -18825.904 +PPO4 = 0.0 +PPO5 = 0.0 +PPO6 = -22216.49 +XPIXELSZ= 25.28445 / X pixel size (microns) +YPIXELSZ= 25.28445 / Y pixel size (microns) +PLTRAH = 10.0 / RA at plate centre +PLTRAM = 47.0 +PLTRAS = 57.30587 +PLTDECD = 60.0 / DEC at plate centre +PLTDECM = 15.0 +PLTDECS = 51.85079 +PLTDECSN= '- ' +PLTSCALE= 67.230832 / Plate scale (arcsec/mm) +AMDX1 = 67.23134 / Plate solution x co-efficients +AMDX2 = 0.052773052 +AMDX3 = 1.3600013 +AMDX4 = -2.035506E-5 +AMDX5 = -5.975503E-7 +AMDX6 = 1.1968724E-5 +AMDX7 = 0.0 +AMDX8 = 2.4490575E-6 +AMDX9 = -3.7135409E-8 +AMDX10 = 2.3677266E-6 +AMDX11 = 1.6863765E-7 +AMDX12 = 0.0 +AMDX13 = 0.0 +AMDX14 = 0.0 +AMDX15 = 0.0 +AMDX16 = 0.0 +AMDX17 = 0.0 +AMDX18 = 0.0 +AMDX19 = 0.0 +AMDX20 = 0.0 +AMDY1 = 67.230323 / Plate solution y co-efficients +AMDY2 = -0.05968221 +AMDY3 = 0.74643824 +AMDY4 = -1.8496356E-5 +AMDY5 = -6.3446158E-6 +AMDY6 = 9.778157E-7 +AMDY7 = 0.0 +AMDY8 = 2.5520067E-6 +AMDY9 = 6.6357427E-8 +AMDY10 = 2.4656782E-6 +AMDY11 = 2.6397309E-9 +AMDY12 = 0.0 +AMDY13 = 0.0 +AMDY14 = 0.0 +AMDY15 = 0.0 +AMDY16 = 0.0 +AMDY17 = 0.0 +AMDY18 = 0.0 +AMDY19 = 0.0 +AMDY20 = 0.0 diff --git a/ast_tester/dss.fits-dss b/ast_tester/dss.fits-dss new file mode 100644 index 0000000..c728dc4 --- /dev/null +++ b/ast_tester/dss.fits-dss @@ -0,0 +1,108 @@ +SIMPLE = T /FITS header +BITPIX = 16 /No.Bits per pixel +NAXIS = 2 /No.dimensions +NAXIS1 = 530 /Length X axis +NAXIS2 = 530 /Length Y axis +EXTEND = T / +DATE = '29/08/02 ' /Date of FITS file creation +ORIGIN = 'CASB -- STScI ' /Origin of FITS image +PLTLABEL= 'V 11596 ' /Observatory plate label +PLATEID = '06A6 ' /GSSS Plate ID +REGION = 'XV128 ' /GSSS Region Name +DATE-OBS= '19/01/87 ' /UT date of Observation +UT = '17:35:00.00 ' /UT time of observation +EPOCH = 1.9870512695313E+03 /Epoch of plate +PLTRAH = 10 /Plate center RA +PLTRAM = 47 / +PLTRAS = 5.7305870000000E+01 / +PLTDECSN= '- ' /Plate center Dec +PLTDECD = 60 / +PLTDECM = 15 / +PLTDECS = 5.1850790000000E+01 / +EQUINOX = 2.0000000000000E+03 /Julian Reference frame equinox +EXPOSURE= 4.0000000000000E+00 /Exposure time minutes +BANDPASS= 6 /GSSS Bandpass code +PLTGRADE= 1 /Plate grade +PLTSCALE= 6.7200000000000E+01 /Plate Scale arcsec per mm +SITELAT = '-31:16:24.00 ' /Latitude of Observatory +SITELONG= '+149:03:42.00 ' /Longitude of Observatory +TELESCOP= 'UK Schmidt (new optics)' /Telescope where plate taken +CNPIX1 = 7696 /X corner (pixels) +CNPIX2 = 7926 /Y corner +DATATYPE= 'INTEGER*2 ' /Type of Data +SCANIMG = 'XV128_06A6_00_00.PIM' /Name of original scan +SCANNUM = 0 /Identifies scan of the plate +DCHOPPED= F /Image repaired for chopping effects +DSHEARED= F /Image repaired for shearing effects +DSCNDNUM= 0 /Identifies descendant of plate scan image +XPIXELSZ= 2.5284450000000E+01 /X pixel size microns +YPIXELSZ= 2.5284450000000E+01 /Y pixel size microns +PPO1 = 0.0000000000000E+00 /Orientation Coefficients +PPO2 = 0.0000000000000E+00 / +PPO3 = 1.7573793885557E+05 / +PPO4 = 0.0000000000000E+00 / +PPO5 = 0.0000000000000E+00 / +PPO6 = 1.7816277576304E+05 / +AMDX1 = 6.7231339795838E+01 /Plate solution x coefficients +AMDX2 = 5.2773052029014E-02 / +AMDX3 = 1.3600012956653E+00 / +AMDX4 = -2.0355060416487E-05 / +AMDX5 = -5.9755030481433E-07 / +AMDX6 = 1.1968723974358E-05 / +AMDX7 = 0.0000000000000E+00 / +AMDX8 = 2.4490575058667E-06 / +AMDX9 = -3.7135408713158E-08 / +AMDX10 = 2.3677266274674E-06 / +AMDX11 = 1.6863764611929E-07 / +AMDX12 = 0.0000000000000E+00 / +AMDX13 = 0.0000000000000E+00 / +AMDX14 = 0.0000000000000E+00 / +AMDX15 = 0.0000000000000E+00 / +AMDX16 = 0.0000000000000E+00 / +AMDX17 = 0.0000000000000E+00 / +AMDX18 = 0.0000000000000E+00 / +AMDX19 = 0.0000000000000E+00 / +AMDX20 = 0.0000000000000E+00 / +AMDY1 = 6.7230323303554E+01 /Plate solution y coefficients +AMDY2 = -5.9682210210548E-02 / +AMDY3 = 7.4643824485917E-01 / +AMDY4 = -1.8496356457673E-05 / +AMDY5 = -6.3446157764134E-06 / +AMDY6 = 9.7781570327705E-07 / +AMDY7 = 0.0000000000000E+00 / +AMDY8 = 2.5520066665557E-06 / +AMDY9 = 6.6357426647323E-08 / +AMDY10 = 2.4656782316245E-06 / +AMDY11 = 2.6397309468495E-09 / +AMDY12 = 0.0000000000000E+00 / +AMDY13 = 0.0000000000000E+00 / +AMDY14 = 0.0000000000000E+00 / +AMDY15 = 0.0000000000000E+00 / +AMDY16 = 0.0000000000000E+00 / +AMDY17 = 0.0000000000000E+00 / +AMDY18 = 0.0000000000000E+00 / +AMDY19 = 0.0000000000000E+00 / +AMDY20 = 0.0000000000000E+00 / + Based on photographic data obtained using The UK Schmidt Telescope. + The UK Schmidt Telescope was operated by the Royal Observatory + Edinburgh, with funding from the UK Science and Engineering Research + Council, until 1988 June, and thereafter by the Anglo-Australian + Observatory. Original plate material is copyright (c) the Royal + Observatory Edinburgh and the Anglo-Australian Observatory. The + plates were processed into the present compressed digital form with + their permission. The Digitized Sky Survey was produced at the Space + Telescope Science Institute under US Government grant NAG W-2166. + + Investigators using these scans are requested to include the above + acknowledgements in any publications. + + Copyright (c) 1993, 1994, Association of Universities for Research in + Astronomy, Inc. All rights reserved. +DATAMAX = 23833 /Maximum data value +DATAMIN = 3673 /Minimum data value +OBJECT = 'data ' /Object ID +OBJCTRA = '10 44 10.340 ' /Object Right Ascension (J2000) +OBJCTDEC= '-59 43 11.40 ' /Object Declination (J2000) +OBJCTX = 7961.96 /Object X on plate (pixels) +OBJCTY = 8191.07 /Object Y on plate (pixels) +END diff --git a/ast_tester/dss.fits-wcs b/ast_tester/dss.fits-wcs new file mode 100644 index 0000000..e9896db --- /dev/null +++ b/ast_tester/dss.fits-wcs @@ -0,0 +1,39 @@ +WCSAXES = 2 / Number of WCS axes +CRPIX1 = -745.0645 / Reference pixel on axis 1 +CRPIX2 = -879.1622 / Reference pixel on axis 2 +CRVAL1 = 161.98877 / Value at ref. pixel on axis 1 +CRVAL2 = -60.264403 / Value at ref. pixel on axis 2 +CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2 +CD1_1 = -0.02528445 / Transformation matrix element +CD2_2 = 0.02528445 / Transformation matrix element +MJD-OBS = 51544.499 / Modified Julian Date of observation +DATE-OBS= '2000-01-01T11:58:55.816' / Date of observation +QV1_0 = 0.00037777814 / Projection parameter +QV1_1 = 0.018675372 / Projection parameter +QV1_2 = 1.4659181E-5 / Projection parameter +QV1_4 = -5.6541834E-9 / Projection parameter +QV1_5 = -1.659862E-10 / Projection parameter +QV1_6 = 3.3246455E-9 / Projection parameter +QV1_7 = 6.8029375E-10 / Projection parameter +QV1_8 = -1.0315391E-11 / Projection parameter +QV1_9 = 6.5770184E-10 / Projection parameter +QV1_10 = 4.6843791E-11 / Projection parameter +QV1_17 = 0.0 / Projection parameter +QV1_19 = 0.0 / Projection parameter +QV1_21 = 0.0 / Projection parameter +QV2_0 = 0.00020734396 / Projection parameter +QV2_1 = 0.01867509 / Projection parameter +QV2_2 = -1.6578392E-5 / Projection parameter +QV2_4 = -5.1378768E-9 / Projection parameter +QV2_5 = -1.7623933E-9 / Projection parameter +QV2_6 = 2.7161547E-10 / Projection parameter +QV2_7 = 7.0889074E-10 / Projection parameter +QV2_8 = 1.8432619E-11 / Projection parameter +QV2_9 = 6.8491062E-10 / Projection parameter +QV2_10 = 7.332586E-13 / Projection parameter +QV2_17 = 0.0 / Projection parameter +QV2_19 = 0.0 / Projection parameter +QV2_21 = 0.0 / Projection parameter +RADESYS = 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / [yr] Epoch of reference equinox diff --git a/ast_tester/hpx.attr b/ast_tester/hpx.attr new file mode 100644 index 0000000..496403c --- /dev/null +++ b/ast_tester/hpx.attr @@ -0,0 +1 @@ +border=1 diff --git a/ast_tester/hpx.box b/ast_tester/hpx.box new file mode 100644 index 0000000..8b52762 --- /dev/null +++ b/ast_tester/hpx.box @@ -0,0 +1 @@ +0.5 0.5 300.5 200.5 diff --git a/ast_tester/hpx.head b/ast_tester/hpx.head new file mode 100644 index 0000000..9e82b8e --- /dev/null +++ b/ast_tester/hpx.head @@ -0,0 +1,10 @@ +NAXIS1 = 300 +NAXIS2 = 200 +CTYPE1 = 'GLON-HPX' +CTYPE2 = 'GLAT-HPX' +CRVAL1 = -149.56866 +CRVAL2 = -19.758201 +CRPIX1 = 150.500 +CRPIX2 = 100.500 +CDELT1 = -1.00000 +CDELT2 = 1.00000 diff --git a/ast_tester/hpx.ps b/ast_tester/hpx.ps new file mode 100644 index 0000000..dcc20f0 --- /dev/null +++ b/ast_tester/hpx.ps @@ -0,0 +1,1688 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Thu May 17 09:46:26 2007 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 17/05/1907 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 428.9 428.9 428.9 428.8 428.8 428.8 428.8 428.8 428.8 428.7 428.7 428.7 428.7 428.7 428.7 428.6 428.6 428.6 + 428.6 428.6 428.6 428.5 428.5 428.5 428.5 428.5 428.5 428.2 428.0 427.7 427.5 427.2 426.9 426.7 426.4 426.2 + 425.9 425.6 425.4 425.1 424.9 424.6 424.4 424.1 423.9 423.6 423.4 423.1 422.9 422.7 422.4 422.2 422.0 421.8 + 421.6 421.4 421.2 421.0 421.4 421.7 422.1 422.4 422.7 423.1 423.4 423.7 424.0 427.6 430.7 433.6 436.6 440.0 + 440.3 440.6 440.8 441.1 441.4 441.7 442.0 442.4 442.7 443.0 443.3 443.7 444.1 444.5 445.0 445.5 446.1 446.6 + 447.1 447.6 448.1 448.7 449.2 449.7 450.2 450.7 451.2 451.7 452.2 452.7 453.2 453.7 454.2 454.6 455.1 455.6 + 456.1 456.6 457.1 457.5 458.0 458.5 458.9 459.4 459.9 460.4 460.8 461.3 461.8 462.3 462.8 463.3 463.8 464.3 + 464.8 465.4 466.0 466.7 466.7 466.7 466.8 466.8 466.9 466.9 467.0 467.0 467.1 467.1 467.2 467.2 467.3 467.3 + 467.4 467.5 467.5 467.6 ][ 179.0 179.1 179.1 179.2 179.3 179.3 179.4 179.5 179.6 179.6 179.7 179.8 179.8 179.9 + 180.0 180.0 180.1 180.2 180.3 180.3 180.4 180.5 180.5 180.6 180.7 180.8 180.8 181.8 182.8 183.9 184.9 185.9 + 186.9 188.0 189.0 190.1 191.1 192.1 193.2 194.2 195.3 196.3 197.4 198.4 199.5 200.5 201.6 202.6 203.7 204.7 + 205.7 206.8 207.8 208.8 209.9 210.9 211.9 213.0 214.0 215.0 216.1 217.2 218.3 219.4 220.5 221.6 222.8 239.9 + 258.7 278.1 297.1 314.9 316.1 317.3 318.5 319.6 320.8 321.9 323.0 324.1 325.2 326.3 327.4 328.4 329.5 330.5 + 331.5 332.6 333.6 334.6 335.7 336.7 337.7 338.8 339.8 340.9 341.9 343.0 344.0 345.0 346.1 347.1 348.2 349.2 + 350.3 351.3 352.4 353.4 354.5 355.5 356.5 357.6 358.6 359.6 360.6 361.6 362.6 363.6 364.6 365.6 366.5 367.4 + 368.4 369.2 370.1 371.0 371.8 372.5 373.2 373.9 373.9 374.0 374.0 374.1 374.1 374.2 374.2 374.3 374.3 374.3 + 374.4 374.4 374.5 374.5 374.5 374.6 374.6 374.7 ]plong + s[ 557.0 557.2 557.3 557.4 557.5 557.7 557.8 557.9 558.1 558.2 558.3 558.4 558.6 558.7 558.8 558.9 559.1 559.2 + 559.3 559.4 559.6 559.7 561.4 563.0 564.6 566.1 567.5 569.0 570.4 571.8 573.2 574.7 576.1 577.6 579.1 580.6 + 582.2 583.8 585.4 587.0 588.7 590.4 590.4 ][ 374.8 374.8 374.9 374.9 374.9 375.0 375.0 375.0 375.1 375.1 375.1 + 375.2 375.2 375.2 375.3 375.3 375.3 375.4 375.4 375.4 375.5 375.5 375.9 376.2 376.3 376.4 376.4 376.3 376.0 + 375.7 375.3 374.8 374.2 373.6 372.9 372.2 371.4 370.6 369.7 368.8 367.9 367.0 367.0 ]plong + s[ 360.9 361.6 362.4 363.1 363.8 364.5 365.3 366.0 366.7 367.4 368.1 368.8 369.6 370.3 371.0 371.7 372.4 373.1 + 373.8 374.4 375.1 375.8 376.5 377.2 377.8 378.5 379.2 379.8 380.5 381.2 381.8 382.5 382.6 382.4 382.3 382.1 + 381.9 381.8 381.6 381.5 381.3 381.2 381.0 379.2 377.7 376.3 374.8 373.0 372.9 372.8 372.6 372.5 372.3 372.1 + 372.0 371.8 371.6 371.5 371.2 370.4 369.6 368.8 368.0 367.1 366.3 365.5 364.7 363.9 363.1 362.3 361.5 360.7 + 359.8 359.0 358.2 357.4 356.6 355.8 355.0 354.2 353.3 352.5 351.7 350.9 350.1 349.3 348.5 347.7 346.9 346.1 + 345.3 344.5 343.7 342.9 342.1 341.3 340.5 339.7 338.9 338.2 337.4 336.6 335.9 335.1 334.3 333.5 332.7 331.8 + 331.8 331.7 331.6 331.6 331.5 331.4 331.4 331.3 331.2 331.2 331.1 331.0 331.0 330.9 330.8 330.7 330.7 330.6 + 330.5 330.4 330.4 330.3 330.2 330.1 ][ 178.8 179.9 181.0 182.1 183.2 184.4 185.5 186.6 187.7 188.8 189.8 190.9 + 192.0 193.1 194.2 195.3 196.4 197.5 198.6 199.7 200.7 201.8 202.9 204.0 205.0 206.1 207.2 208.2 209.3 210.3 + 211.4 212.4 213.5 214.5 215.6 216.7 217.8 218.9 220.1 221.2 222.4 223.5 224.7 242.4 261.5 281.1 300.3 318.0 + 319.2 320.3 321.5 322.6 323.8 324.9 326.0 327.1 328.2 329.2 330.3 331.3 332.4 333.4 334.5 335.5 336.6 337.7 + 338.7 339.8 340.9 342.0 343.0 344.1 345.2 346.3 347.4 348.5 349.6 350.7 351.8 352.9 353.9 355.0 356.1 357.2 + 358.3 359.4 360.6 361.7 362.8 363.8 364.9 366.0 367.1 368.2 369.3 370.4 371.5 372.5 373.6 374.6 375.6 376.6 + 377.6 378.6 379.5 380.3 381.0 381.7 381.7 381.8 381.8 381.8 381.9 381.9 382.0 382.0 382.0 382.1 382.1 382.1 + 382.2 382.2 382.2 382.2 382.3 382.3 382.3 382.4 382.4 382.4 382.4 382.4 ]plong + s[ 225.0 224.9 224.8 224.7 224.5 224.4 224.3 224.2 224.1 224.0 223.9 223.8 223.6 223.5 223.4 222.0 220.7 219.4 + 218.2 216.9 215.6 214.3 213.0 211.6 210.2 208.8 207.4 206.0 204.6 203.1 201.7 200.2 199.5 ][ 382.5 382.5 382.6 + 382.6 382.6 382.6 382.6 382.6 382.6 382.6 382.6 382.6 382.7 382.7 382.7 382.6 382.3 381.8 381.2 380.5 379.7 + 378.8 377.9 376.9 375.9 374.9 373.8 372.8 371.7 370.7 369.6 368.5 367.9 ]plong + s[ 360.9 352.9 345.7 337.9 330.9 325.3 320.3 315.3 310.0 303.7 303.2 302.7 302.1 301.6 301.0 300.4 299.8 299.2 + 298.7 298.7 298.7 298.7 298.7 298.7 298.7 298.7 298.8 298.8 298.8 298.8 298.9 298.9 298.9 299.0 299.0 299.1 + 299.1 299.1 299.1 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 299.2 + 299.2 299.2 299.2 299.2 299.2 299.2 ][ 178.8 189.5 202.5 216.2 231.6 249.1 267.8 286.5 304.6 321.0 322.1 323.1 + 324.2 325.2 326.3 327.3 328.3 329.3 330.2 331.2 332.2 333.2 334.1 335.1 336.1 337.0 338.0 339.0 339.9 340.9 + 341.8 342.8 343.8 344.7 345.7 346.6 347.5 348.5 349.4 350.3 350.4 350.4 350.5 350.6 350.6 350.7 350.8 350.8 + 350.9 351.0 351.0 351.1 351.2 351.2 351.3 351.4 351.4 351.5 351.5 351.6 ]plong + s[ 255.8 255.7 255.6 255.5 255.3 255.2 255.1 255.0 254.8 254.7 254.6 254.4 254.3 254.2 254.1 253.9 253.8 253.7 + 253.6 253.4 251.7 249.9 248.2 246.5 244.8 243.1 241.4 239.7 238.1 236.5 234.9 233.3 231.7 230.2 228.7 227.2 + 225.7 224.2 222.7 221.3 219.8 218.4 216.9 215.5 214.0 212.5 211.0 209.5 208.0 206.4 204.8 203.1 201.5 199.7 + 199.5 ][ 351.8 351.9 351.9 352.0 352.1 352.1 352.2 352.3 352.3 352.4 352.4 352.5 352.6 352.6 352.7 352.8 352.8 + 352.9 353.0 353.0 353.9 354.8 355.6 356.5 357.3 358.1 358.9 359.7 360.4 361.2 361.9 362.6 363.2 363.8 364.4 + 364.9 365.4 365.9 366.3 366.6 366.9 367.2 367.4 367.5 367.6 367.6 367.6 367.5 367.3 367.1 366.8 366.5 366.1 + 365.7 365.6 ]plong + s[ 360.9 359.2 357.6 356.0 354.4 352.9 351.4 349.9 348.5 347.1 345.7 344.3 343.0 341.7 340.3 339.0 337.7 336.4 + 335.1 333.7 332.4 331.1 329.7 328.4 327.0 325.6 324.3 322.9 321.5 320.1 318.7 317.2 315.8 314.3 312.9 311.4 + 310.0 308.5 307.0 305.5 304.0 302.5 301.0 299.4 297.9 296.4 294.8 293.3 291.7 290.1 288.6 287.0 285.4 283.8 + 282.2 280.9 280.2 279.5 278.8 278.2 277.6 277.0 276.4 275.8 275.2 274.6 274.1 273.5 273.0 272.5 271.9 265.4 + 259.7 254.2 248.4 248.0 247.5 247.1 246.6 246.1 245.7 245.2 244.7 244.2 243.7 243.2 242.7 242.2 241.7 241.1 + 240.6 240.0 239.4 238.8 238.2 237.6 237.0 236.4 235.7 235.0 234.3 233.5 232.6 231.6 230.7 229.8 228.9 227.9 + 227.0 226.1 225.2 224.3 223.4 222.5 221.6 220.7 219.9 219.0 218.1 217.2 216.4 215.5 214.6 213.8 212.9 212.1 + 211.2 210.4 209.5 208.6 207.8 206.9 206.0 205.2 204.3 203.4 202.5 201.6 200.6 199.6 199.5 ][ 178.8 178.6 178.4 + 178.2 178.1 178.0 178.0 178.1 178.2 178.3 178.5 178.8 179.1 179.4 179.8 180.2 180.7 181.2 181.8 182.3 182.9 + 183.6 184.2 184.9 185.6 186.4 187.1 187.9 188.7 189.5 190.3 191.1 192.0 192.8 193.7 194.6 195.4 196.3 197.2 + 198.1 199.0 200.0 200.9 201.8 202.7 203.7 204.6 205.5 206.5 207.4 208.4 209.3 210.2 211.2 212.1 213.1 214.0 + 215.0 216.0 217.0 218.0 219.0 220.1 221.1 222.2 223.3 224.3 225.5 226.6 227.7 228.8 245.8 264.0 282.6 300.7 + 301.9 303.1 304.4 305.6 306.8 308.0 309.2 310.4 311.6 312.7 313.9 315.0 316.1 317.3 318.4 319.5 320.5 321.6 + 322.7 323.7 324.7 325.8 326.8 327.7 328.7 329.7 330.6 331.6 332.5 333.5 334.4 335.3 336.3 337.2 338.2 339.1 + 340.0 340.9 341.9 342.8 343.7 344.6 345.5 346.4 347.3 348.2 349.1 349.9 350.8 351.6 352.5 353.3 354.1 354.9 + 355.7 356.4 357.2 357.9 358.6 359.2 359.9 360.5 361.1 361.6 362.1 362.2 ]plong + s[ 360.9 359.2 357.5 355.8 354.1 352.5 350.8 349.2 347.6 346.1 344.5 343.0 341.5 340.1 338.6 337.2 335.8 334.4 + 333.0 331.5 330.0 328.4 326.8 325.1 325.0 324.9 324.8 324.6 324.5 324.4 324.3 324.1 324.0 323.9 323.7 323.6 + 323.5 323.4 323.2 323.1 323.0 322.8 322.7 322.6 322.5 ][ 178.8 177.9 176.9 176.0 175.1 174.1 173.3 172.4 171.6 + 170.8 170.1 169.4 168.8 168.2 167.7 167.3 166.9 166.7 166.6 166.6 166.6 166.8 167.1 167.5 167.5 167.5 167.6 + 167.6 167.6 167.7 167.7 167.7 167.8 167.8 167.8 167.9 167.9 167.9 168.0 168.0 168.0 168.1 168.1 168.2 168.2 ]plong + s[ 233.0 232.9 232.9 232.8 232.8 232.7 232.7 232.6 232.6 232.5 232.5 232.4 232.4 232.3 232.3 232.2 232.2 232.1 + 232.1 231.4 230.8 230.3 229.7 229.2 228.7 228.2 227.7 227.2 226.7 226.3 225.8 225.3 224.9 224.4 223.9 223.4 + 223.0 222.5 222.0 221.5 221.0 220.6 220.1 219.6 219.1 218.6 218.1 217.6 217.1 216.6 216.1 215.6 215.1 214.6 + 214.1 213.6 213.1 212.5 212.0 211.5 211.0 210.4 209.9 209.5 209.1 208.8 208.4 208.1 207.8 207.5 207.2 206.9 + 206.6 206.3 206.0 205.7 205.4 202.0 199.5 ][ 168.3 168.3 168.4 168.4 168.5 168.5 168.6 168.6 168.6 168.7 168.7 + 168.8 168.8 168.9 168.9 168.9 169.0 169.0 169.1 169.7 170.5 171.2 172.0 172.9 173.7 174.6 175.5 176.5 177.4 + 178.4 179.4 180.4 181.4 182.4 183.4 184.4 185.4 186.4 187.5 188.5 189.6 190.6 191.6 192.7 193.7 194.8 195.8 + 196.9 197.9 199.0 200.0 201.1 202.1 203.2 204.2 205.2 206.3 207.3 208.4 209.4 210.4 211.4 212.5 213.5 214.5 + 215.6 216.7 217.7 218.8 219.9 221.1 222.2 223.4 224.5 225.7 226.9 228.1 245.8 262.3 ]plong + s[ 429.1 429.2 429.3 429.4 429.6 429.7 429.8 429.9 430.0 430.1 430.2 430.3 430.4 430.5 430.6 430.7 430.8 430.9 + 431.0 431.1 431.2 431.3 431.4 431.5 431.6 431.8 431.9 433.3 434.8 436.2 437.7 439.1 440.6 442.0 443.4 444.8 + 446.2 447.5 448.9 450.2 451.5 452.7 454.0 455.3 456.6 458.0 458.1 458.2 458.3 458.4 458.5 458.6 458.8 458.9 + 459.0 459.1 459.2 459.3 459.4 459.6 ][ 178.7 178.6 178.5 178.5 178.4 178.3 178.2 178.1 178.1 178.0 177.9 177.8 + 177.8 177.7 177.6 177.5 177.4 177.4 177.3 177.2 177.1 177.0 177.0 176.9 176.8 176.7 176.7 175.6 174.5 173.4 + 172.3 171.2 170.2 169.1 168.1 167.1 166.1 165.1 164.2 163.3 162.5 161.8 161.2 160.7 160.4 160.3 160.3 160.3 + 160.3 160.3 160.3 160.4 160.4 160.4 160.4 160.4 160.4 160.4 160.4 160.5 ]plong + s[ 564.7 564.8 564.8 564.9 565.0 565.1 565.2 565.2 565.3 565.4 565.4 565.5 565.6 565.7 565.7 565.8 565.9 565.9 + 566.0 566.1 566.1 566.2 566.3 566.3 566.4 567.3 568.1 568.9 569.6 570.4 571.2 572.0 572.7 573.5 574.3 575.1 + 575.9 576.7 577.4 578.2 579.0 579.8 580.6 581.4 582.2 583.1 583.9 584.7 585.5 586.3 587.1 587.9 588.7 589.5 + 590.3 590.4 ][ 160.5 160.6 160.6 160.6 160.6 160.6 160.7 160.7 160.7 160.8 160.8 160.8 160.9 160.9 160.9 161.0 + 161.0 161.0 161.1 161.1 161.1 161.2 161.2 161.3 161.3 161.9 162.7 163.5 164.4 165.4 166.3 167.3 168.4 169.4 + 170.5 171.5 172.6 173.7 174.8 175.8 176.9 178.0 179.1 180.2 181.3 182.4 183.5 184.6 185.7 186.8 187.9 189.0 + 190.1 191.2 192.3 192.4 ]plong + s[ 429.2 429.3 429.4 429.6 429.7 429.8 430.0 430.1 430.2 430.4 430.5 430.6 430.8 430.9 431.0 431.1 431.3 431.4 + 431.5 431.7 431.8 431.9 432.0 432.2 432.3 432.4 432.6 434.3 436.0 437.7 439.4 441.0 442.5 444.1 445.6 447.1 + 448.6 450.0 451.5 452.9 454.4 455.8 457.3 458.8 460.2 461.7 463.2 464.8 466.3 467.9 469.5 471.1 472.7 474.3 + 476.0 477.6 479.3 481.0 482.8 484.5 486.2 488.0 488.1 488.3 488.4 488.5 488.6 488.8 488.9 489.0 489.1 489.3 + 489.4 489.5 489.7 489.8 489.9 490.0 490.2 490.3 490.4 ][ 178.8 178.7 178.7 178.6 178.6 178.6 178.5 178.5 178.4 + 178.4 178.4 178.3 178.3 178.3 178.2 178.2 178.1 178.1 178.1 178.0 178.0 178.0 177.9 177.9 177.9 177.8 177.8 + 177.3 176.9 176.5 176.2 175.9 175.7 175.5 175.4 175.4 175.4 175.5 175.6 175.8 176.0 176.3 176.7 177.1 177.6 + 178.1 178.6 179.2 179.8 180.4 181.1 181.8 182.5 183.3 184.1 184.9 185.7 186.5 187.3 188.2 189.1 190.0 190.0 + 190.1 190.1 190.2 190.3 190.3 190.4 190.5 190.5 190.6 190.7 190.7 190.8 190.8 190.9 191.0 191.0 191.1 191.2 ]plong + s[ 533.8 533.8 533.8 533.8 533.8 533.8 533.8 533.8 533.8 533.8 533.8 533.8 533.8 533.7 533.7 533.7 533.7 533.7 + 533.7 533.7 533.7 533.7 533.7 533.7 533.6 533.6 533.5 533.5 533.5 533.4 533.4 533.4 533.4 533.3 533.3 533.3 + 533.3 533.3 533.3 533.3 533.3 533.3 533.8 534.4 535.0 535.6 536.1 536.7 537.2 537.8 538.3 544.6 549.9 554.8 + 559.9 565.5 572.5 580.3 580.8 581.4 581.9 582.4 582.9 583.4 583.9 584.4 585.0 585.5 586.0 586.5 587.0 587.5 + 588.0 588.5 589.0 589.6 590.1 590.4 ][ 191.4 191.4 191.5 191.6 191.6 191.7 191.8 191.8 191.9 191.9 192.0 192.1 + 192.1 192.2 192.3 192.3 192.4 192.5 192.5 192.6 192.7 193.6 194.5 195.4 196.4 197.3 198.3 199.2 200.2 201.1 + 202.1 203.1 204.0 205.0 206.0 206.9 207.9 208.9 209.8 210.8 211.8 212.7 213.7 214.7 215.7 216.7 217.7 218.8 + 219.8 220.9 222.0 238.4 256.4 275.2 293.8 311.3 326.8 340.4 341.4 342.4 343.3 344.3 345.2 346.2 347.1 348.0 + 349.0 349.9 350.8 351.7 352.6 353.5 354.4 355.2 356.1 356.9 357.8 358.2 ]plong + s[ 429.1 429.2 429.2 429.3 429.4 429.5 429.6 429.7 429.7 429.8 429.9 430.0 430.1 430.1 430.2 430.3 430.4 430.4 + 430.5 430.6 430.7 430.8 430.8 430.9 431.0 431.1 431.1 432.2 433.2 434.2 435.2 436.1 437.0 438.0 438.9 439.7 + 440.6 441.5 442.4 443.2 444.1 444.9 445.8 446.6 447.5 448.4 449.2 450.1 450.9 451.8 452.7 453.5 454.4 455.3 + 456.2 457.1 458.0 458.9 459.8 460.7 461.6 462.5 463.4 464.3 465.3 466.2 467.1 468.1 468.9 469.6 470.3 470.9 + 471.6 472.2 472.8 473.4 474.0 474.6 475.1 475.7 476.2 476.8 477.3 477.8 478.3 478.8 479.3 479.8 480.2 480.7 + 481.2 481.6 482.1 482.5 483.0 488.8 494.3 500.0 506.5 507.0 507.6 508.1 508.6 509.2 509.8 510.3 510.9 511.5 + 512.1 512.8 513.4 514.1 514.7 515.4 516.8 518.4 520.0 521.6 523.1 524.7 526.3 527.8 529.4 530.9 532.5 534.0 + 535.5 537.0 538.6 540.1 541.6 543.0 544.5 546.0 547.5 548.9 550.4 551.8 553.2 554.6 556.0 557.4 558.8 560.2 + 561.6 562.9 564.3 565.6 567.0 568.3 569.6 570.9 572.3 573.6 574.9 576.2 577.6 578.9 580.3 581.7 583.1 584.5 + 585.9 587.4 589.0 590.4 ][ 178.9 178.9 178.9 179.0 179.0 179.0 179.0 179.0 179.1 179.1 179.1 179.1 179.2 179.2 + 179.2 179.2 179.3 179.3 179.3 179.3 179.4 179.4 179.4 179.4 179.5 179.5 179.5 179.9 180.3 180.8 181.3 181.9 + 182.5 183.1 183.7 184.4 185.1 185.8 186.5 187.3 188.1 188.9 189.7 190.5 191.3 192.2 193.0 193.9 194.8 195.7 + 196.6 197.5 198.4 199.3 200.2 201.1 202.0 203.0 203.9 204.8 205.8 206.7 207.6 208.6 209.5 210.5 211.4 212.4 + 213.3 214.3 215.2 216.2 217.2 218.2 219.3 220.3 221.4 222.4 223.5 224.6 225.7 226.8 228.0 229.1 230.3 231.4 + 232.6 233.8 235.0 236.2 237.4 238.6 239.8 241.1 242.3 260.4 279.0 297.2 314.1 315.3 316.4 317.5 318.6 319.7 + 320.8 321.9 322.9 324.0 325.0 326.0 327.0 328.0 329.0 329.9 330.9 331.8 332.7 333.7 334.6 335.6 336.5 337.4 + 338.4 339.3 340.2 341.2 342.1 343.0 343.9 344.8 345.7 346.6 347.5 348.4 349.3 350.2 351.0 351.8 352.7 353.5 + 354.3 355.1 355.9 356.6 357.3 358.1 358.7 359.4 360.0 360.6 361.2 361.8 362.3 362.7 363.2 363.6 363.9 364.2 + 364.4 364.6 364.8 364.9 364.9 364.9 364.9 364.8 ]plong + s[ 458.6 457.1 455.6 454.1 452.6 451.0 449.4 447.7 446.1 444.4 442.6 440.9 439.1 437.4 435.6 433.7 431.9 430.0 + 428.2 426.3 424.4 422.5 420.6 418.7 417.0 415.2 413.4 411.7 409.9 408.2 406.4 404.6 402.9 401.1 399.3 397.6 + 395.8 394.0 392.3 390.5 388.8 387.0 385.2 383.5 381.7 379.9 378.2 376.4 374.7 372.9 371.1 369.3 367.4 365.5 + 363.6 361.7 359.8 358.0 356.1 354.3 352.5 350.7 349.0 347.2 345.5 343.8 342.1 340.5 338.9 337.3 335.8 334.2 + 332.7 331.3 329.9 328.5 327.1 325.8 324.6 323.3 322.1 321.0 319.9 318.8 317.8 316.8 315.8 314.9 314.1 313.3 + 312.5 311.8 311.1 310.4 309.8 309.3 308.8 308.3 307.9 307.8 307.8 307.8 307.7 307.7 307.7 307.7 307.6 ][ 202.6 + 203.3 203.9 204.5 205.0 205.6 206.2 206.7 207.2 207.7 208.2 208.7 209.1 209.5 210.0 210.4 210.7 211.1 211.5 + 211.8 212.1 212.4 212.7 212.9 213.2 213.4 213.6 213.8 214.0 214.1 214.3 214.4 214.5 214.5 214.6 214.6 214.7 + 214.7 214.6 214.6 214.5 214.5 214.4 214.3 214.1 214.0 213.8 213.6 213.4 213.2 212.9 212.7 212.4 212.1 211.8 + 211.5 211.1 210.7 210.4 210.0 209.5 209.1 208.7 208.2 207.7 207.2 206.7 206.2 205.6 205.1 204.5 203.9 203.3 + 202.7 202.0 201.4 200.7 200.0 199.3 198.6 197.9 197.2 196.5 195.7 195.0 194.2 193.4 192.6 191.8 191.0 190.2 + 189.4 188.6 187.7 186.9 186.0 185.2 184.3 183.5 183.4 183.4 183.3 183.2 183.2 183.1 183.1 183.0 ]plong + s[ 247.5 247.3 247.2 247.0 246.9 246.7 246.6 246.4 246.3 246.1 246.0 245.8 245.7 245.5 245.4 245.2 245.1 245.0 + 242.9 240.9 239.0 237.1 235.2 233.4 231.6 229.9 228.2 226.5 224.9 223.3 221.8 220.3 218.8 217.4 216.0 214.6 + 213.1 211.7 210.3 208.7 208.6 208.5 208.4 208.3 208.2 208.1 208.0 207.9 207.7 207.6 207.5 207.4 207.3 207.2 + 207.1 207.0 206.8 206.7 206.6 206.5 206.4 ][ 182.8 182.7 182.7 182.6 182.6 182.5 182.4 182.4 182.3 182.3 182.2 + 182.1 182.1 182.0 181.9 181.9 181.8 181.8 180.9 180.0 179.2 178.3 177.5 176.7 175.8 175.0 174.2 173.5 172.7 + 172.0 171.3 170.6 169.9 169.4 168.8 168.3 167.9 167.6 167.3 167.1 167.1 167.1 167.1 167.1 167.1 167.1 167.1 + 167.1 167.1 167.1 167.1 167.1 167.0 167.0 167.0 167.0 167.0 167.0 167.0 167.0 167.0 ]plong + s[ 583.5 583.4 583.3 583.2 583.0 582.9 582.8 582.7 582.6 582.5 582.4 582.2 582.1 582.0 581.9 581.8 581.7 581.6 + 581.5 581.3 581.2 581.1 581.0 580.9 580.8 580.7 580.6 580.5 580.4 580.3 580.2 580.0 579.9 578.5 577.0 575.6 + 574.2 572.8 571.3 569.9 568.4 566.9 565.3 563.7 562.1 560.4 558.6 556.9 555.1 553.2 551.3 549.4 547.4 545.4 + 545.2 545.1 544.9 544.8 544.6 544.5 544.3 544.2 544.0 543.9 543.8 543.6 543.5 543.3 543.2 543.0 542.9 542.7 + 542.6 542.4 ][ 167.0 167.0 167.0 167.0 167.0 167.0 167.0 167.0 167.0 167.1 167.1 167.1 167.1 167.1 167.1 167.1 + 167.1 167.1 167.1 167.1 167.1 167.1 167.1 167.2 167.2 167.2 167.2 167.2 167.2 167.2 167.2 167.3 167.3 167.5 + 167.8 168.2 168.7 169.2 169.8 170.4 171.1 171.8 172.5 173.3 174.0 174.8 175.7 176.5 177.3 178.2 179.0 179.9 + 180.7 181.6 181.6 181.7 181.7 181.8 181.9 181.9 182.0 182.1 182.1 182.2 182.2 182.3 182.4 182.4 182.5 182.5 + 182.6 182.7 182.7 182.8 ]plong + s[ 482.2 482.2 482.2 482.1 482.1 482.1 482.1 482.0 482.0 482.0 481.9 481.9 481.9 481.8 481.8 481.8 481.8 481.7 + 481.7 481.7 481.2 480.7 480.2 479.6 478.9 478.3 477.5 476.8 476.0 475.1 474.2 473.3 472.3 471.3 470.2 469.1 + 468.0 466.8 465.6 464.3 463.0 461.7 460.3 458.9 457.4 456.0 454.5 452.9 451.3 449.7 448.1 446.4 444.7 443.0 + 441.3 439.5 437.8 436.0 434.1 432.3 430.5 428.6 426.7 424.8 422.9 421.0 ][ 183.0 183.0 183.1 183.2 183.2 183.3 + 183.3 183.4 183.5 183.5 183.6 183.6 183.7 183.8 183.8 183.9 184.0 184.0 184.1 184.1 185.0 185.8 186.7 187.5 + 188.4 189.2 190.0 190.8 191.6 192.4 193.2 194.0 194.8 195.5 196.3 197.0 197.8 198.5 199.2 199.9 200.6 201.2 + 201.9 202.5 203.1 203.7 204.3 204.9 205.5 206.0 206.6 207.1 207.6 208.1 208.6 209.0 209.4 209.9 210.3 210.7 + 211.0 211.4 211.7 212.0 212.3 212.6 ]plong + s[ 480.9 478.5 476.1 473.8 471.4 469.1 466.7 464.4 462.0 459.7 457.3 455.0 452.7 450.3 448.0 445.6 443.3 440.9 + 438.6 436.3 433.9 431.6 429.2 426.9 424.5 422.1 419.8 417.4 415.1 412.7 410.3 408.0 405.6 403.2 400.9 398.5 + 396.1 393.8 391.4 389.0 386.6 384.3 381.9 379.5 377.2 374.8 372.4 370.1 367.7 365.4 363.0 360.7 358.3 356.0 + 353.6 351.3 348.9 346.6 344.2 341.9 339.6 337.2 334.9 332.5 330.2 327.8 325.5 323.2 320.8 318.5 316.1 313.7 + 311.4 309.0 306.6 304.2 301.8 299.4 297.0 294.6 292.2 289.7 287.3 284.8 282.3 279.8 277.3 274.7 272.2 269.6 + 266.9 264.3 261.6 259.0 256.2 253.5 250.7 247.9 245.1 242.2 239.3 236.2 233.1 230.0 226.9 223.9 220.9 217.9 + 215.0 212.0 209.1 206.2 203.4 200.5 199.5 ][ 236.6 237.5 238.4 239.2 240.1 240.9 241.8 242.6 243.4 244.2 245.0 + 245.7 246.4 247.1 247.8 248.5 249.1 249.7 250.3 250.9 251.4 251.9 252.4 252.9 253.3 253.7 254.0 254.4 254.6 + 254.9 255.1 255.3 255.5 255.6 255.7 255.8 255.8 255.8 255.8 255.7 255.6 255.5 255.3 255.1 254.9 254.6 254.4 + 254.0 253.7 253.3 252.9 252.4 251.9 251.4 250.9 250.3 249.7 249.1 248.5 247.8 247.1 246.4 245.7 245.0 244.2 + 243.4 242.6 241.8 240.9 240.1 239.2 238.4 237.5 236.6 235.7 234.8 233.9 232.9 232.0 231.1 230.2 229.2 228.3 + 227.4 226.5 225.5 224.6 223.7 222.8 221.9 221.1 220.2 219.3 218.5 217.7 216.9 216.1 215.3 214.6 213.8 213.1 + 212.4 211.8 211.1 210.5 209.9 209.3 208.7 208.1 207.6 207.1 206.6 206.2 205.8 205.6 ]plong + s[ 590.4 590.0 587.1 584.3 581.4 578.5 575.6 572.6 569.7 566.7 563.6 560.6 557.5 554.4 551.3 548.3 545.5 542.6 + 539.8 537.0 534.2 531.5 528.8 526.2 523.5 520.9 518.3 515.7 513.2 510.6 508.1 505.6 503.1 500.7 498.2 495.8 + 493.4 491.0 488.6 486.2 483.8 481.4 479.0 476.7 474.3 471.9 469.6 467.2 464.9 462.5 460.2 457.9 455.5 453.2 + 450.8 448.5 446.2 443.8 441.5 439.1 436.8 434.4 432.1 429.7 ][ 205.6 205.7 206.1 206.5 207.0 207.5 208.0 208.6 + 209.1 209.7 210.3 211.0 211.6 212.3 213.0 213.7 214.4 215.1 215.9 216.7 217.5 218.3 219.2 220.0 220.9 221.7 + 222.6 223.5 224.4 225.3 226.2 227.2 228.1 229.0 229.9 230.9 231.8 232.7 233.6 234.6 235.5 236.4 237.3 238.2 + 239.0 239.9 240.8 241.6 242.4 243.2 244.0 244.8 245.5 246.3 247.0 247.7 248.3 249.0 249.6 250.2 250.8 251.3 + 251.8 252.3 ]plong + s[ 494.3 491.7 489.2 486.6 484.0 481.5 478.9 476.3 473.7 471.0 468.4 465.8 463.1 460.5 457.8 455.1 452.4 449.7 + 447.0 444.2 441.5 438.7 436.0 433.2 430.4 427.6 424.8 422.0 419.2 416.3 413.5 410.6 407.8 404.9 402.1 399.2 + 396.4 393.5 390.6 387.8 384.9 382.1 379.2 376.4 373.5 370.7 367.9 365.1 362.3 359.5 356.7 353.9 351.1 348.4 + 345.6 342.9 340.2 337.5 334.8 332.1 329.4 326.8 324.1 321.5 318.8 316.2 313.6 311.0 308.4 305.8 303.3 300.7 + 298.1 295.6 293.0 290.5 287.9 285.4 282.9 280.3 277.8 275.3 272.7 270.2 267.6 265.1 262.6 260.0 257.5 254.9 + 252.3 249.8 247.2 244.6 242.0 239.4 236.8 234.1 231.5 228.8 226.2 223.5 220.8 218.1 215.4 212.7 210.0 207.2 + 204.5 201.7 199.5 ][ 279.0 280.0 281.0 282.0 283.0 284.0 285.0 285.9 286.8 287.8 288.6 289.5 290.3 291.2 292.0 + 292.7 293.5 294.2 294.8 295.5 296.1 296.7 297.2 297.8 298.3 298.7 299.1 299.5 299.8 300.1 300.4 300.6 300.8 + 301.0 301.1 301.2 301.2 301.2 301.2 301.1 301.0 300.8 300.6 300.4 300.1 299.8 299.5 299.1 298.7 298.3 297.8 + 297.3 296.7 296.1 295.5 294.9 294.2 293.5 292.7 292.0 291.2 290.4 289.5 288.7 287.8 286.9 285.9 285.0 284.0 + 283.0 282.0 281.0 280.0 279.0 277.9 276.9 275.8 274.8 273.7 272.6 271.6 270.5 269.4 268.3 267.3 266.2 265.2 + 264.1 263.1 262.1 261.1 260.1 259.1 258.1 257.2 256.2 255.3 254.4 253.6 252.7 251.9 251.1 250.3 249.6 248.9 + 248.2 247.6 246.9 246.3 245.8 245.4 ]plong + s[ 590.4 588.8 586.0 583.3 580.5 577.8 575.1 572.3 569.6 567.0 564.3 561.6 559.0 556.3 553.7 551.1 548.5 545.9 + 543.3 540.7 538.1 535.5 533.0 530.4 527.9 525.3 522.8 520.2 517.7 515.2 512.6 510.1 507.6 505.0 502.5 500.0 + 497.4 494.9 492.3 489.7 487.2 484.6 482.0 479.4 476.8 474.2 471.6 469.0 466.4 463.7 461.0 458.4 455.7 453.0 + 450.3 447.6 444.8 442.1 439.4 436.6 ][ 245.4 245.7 246.2 246.8 247.4 248.1 248.7 249.4 250.2 250.9 251.7 252.5 + 253.4 254.2 255.1 256.0 256.9 257.9 258.9 259.8 260.8 261.8 262.9 263.9 264.9 266.0 267.0 268.1 269.2 270.2 + 271.3 272.4 273.4 274.5 275.6 276.6 277.7 278.7 279.8 280.8 281.8 282.8 283.8 284.8 285.7 286.6 287.6 288.4 + 289.3 290.2 291.0 291.8 292.6 293.3 294.0 294.7 295.4 296.0 296.6 297.1 ]plong + s[ 509.0 506.4 503.8 501.2 498.6 495.9 493.2 490.5 487.8 485.0 482.2 479.3 476.4 473.5 470.4 467.3 464.2 461.1 + 458.1 455.1 452.1 449.2 446.3 443.4 440.5 437.6 434.7 431.9 429.0 426.2 423.3 420.4 417.5 414.6 411.7 408.7 + 408.5 408.3 408.0 407.8 407.6 407.4 407.2 407.0 406.8 406.5 406.3 406.1 405.9 405.7 405.5 405.3 405.0 404.8 + 404.6 404.4 ][ 319.4 320.3 321.1 322.0 322.9 323.7 324.6 325.4 326.2 327.0 327.7 328.5 329.2 329.9 330.6 331.3 + 331.9 332.6 333.2 333.8 334.4 334.9 335.4 335.9 336.4 336.8 337.2 337.6 338.0 338.3 338.5 338.8 339.0 339.1 + 339.2 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.4 339.4 339.4 339.4 + 339.4 339.4 339.4 339.4 ]plong + s[ 385.3 385.1 384.8 384.6 384.4 384.2 384.0 383.8 383.5 383.3 383.1 382.9 382.7 382.5 382.3 382.0 381.8 381.6 + 381.4 381.2 378.2 375.3 372.4 369.5 366.6 363.7 360.8 358.0 355.1 352.3 349.4 346.5 343.6 340.7 337.7 334.8 + 331.8 328.7 325.7 322.6 319.5 316.4 313.5 310.6 307.7 304.9 302.1 299.4 296.6 294.0 291.3 288.6 286.0 283.4 + 280.9 278.3 275.8 273.3 270.8 268.3 265.8 263.4 261.0 258.5 256.1 253.7 251.3 248.9 246.6 244.2 241.8 239.5 + 237.1 234.8 232.4 230.1 227.7 225.4 223.0 220.7 218.4 216.0 213.7 211.3 209.0 206.6 204.3 202.0 199.6 199.5 ][ + 339.4 339.4 339.4 339.4 339.4 339.4 339.4 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 339.3 + 339.3 339.3 339.2 339.1 339.0 338.8 338.5 338.3 338.0 337.6 337.2 336.8 336.4 335.9 335.4 334.9 334.4 333.8 + 333.2 332.6 331.9 331.3 330.6 329.9 329.2 328.5 327.7 327.0 326.2 325.4 324.6 323.7 322.9 322.0 321.1 320.3 + 319.4 318.5 317.6 316.6 315.7 314.8 313.9 312.9 312.0 311.1 310.2 309.2 308.3 307.4 306.5 305.6 304.7 303.8 + 303.0 302.1 301.3 300.5 299.7 298.9 298.1 297.4 296.6 295.9 295.2 294.6 293.9 293.3 292.7 292.2 291.6 291.6 ]plong + s[ 590.4 588.4 586.1 583.7 581.4 579.1 576.7 574.4 572.0 569.7 567.4 565.0 562.7 560.3 558.0 555.6 553.3 550.9 + 548.6 546.2 543.8 541.5 539.1 536.7 534.3 531.9 529.4 527.0 524.6 522.1 519.6 517.1 514.6 512.1 509.6 507.0 + 504.4 501.8 499.2 496.5 493.8 491.1 488.4 485.6 482.8 479.9 477.1 474.2 471.1 468.0 464.9 461.8 458.8 455.8 + 452.8 449.9 446.9 ][ 291.6 292.0 292.6 293.2 293.8 294.4 295.1 295.8 296.5 297.2 297.9 298.7 299.5 300.3 301.1 + 301.9 302.8 303.6 304.5 305.4 306.3 307.2 308.1 309.0 309.9 310.9 311.8 312.7 313.6 314.6 315.5 316.4 317.3 + 318.3 319.2 320.1 320.9 321.8 322.7 323.5 324.4 325.2 326.0 326.8 327.6 328.3 329.1 329.8 330.5 331.1 331.8 + 332.4 333.1 333.7 334.2 334.8 335.3 ]plong + s[ 549.4 548.5 547.7 547.0 546.2 545.6 544.9 544.3 543.8 543.3 542.8 542.8 542.7 542.7 542.7 542.6 542.6 542.6 + 542.6 542.5 542.5 542.5 542.4 542.4 542.4 542.3 542.3 542.3 542.3 542.2 542.2 ][ 350.4 351.2 352.0 352.9 353.7 + 354.5 355.3 356.2 357.0 357.9 358.7 358.8 358.9 358.9 359.0 359.0 359.1 359.2 359.2 359.3 359.3 359.4 359.5 + 359.5 359.6 359.7 359.7 359.8 359.8 359.9 360.0 ]plong + s[ 482.1 482.0 481.8 481.7 481.5 481.4 481.2 481.1 480.9 480.8 480.6 480.5 480.3 480.2 480.0 479.9 479.7 479.6 + 479.4 479.3 479.1 479.0 478.9 478.7 478.6 478.4 478.3 478.1 478.0 477.8 477.7 477.6 477.4 477.3 475.3 473.3 + 471.4 469.6 467.8 466.0 464.3 462.6 460.9 459.3 457.7 456.2 454.7 453.2 451.8 450.4 449.0 447.6 446.1 444.7 + 443.1 443.0 442.9 442.8 442.7 442.6 442.5 442.4 442.3 442.1 442.0 441.9 441.8 441.7 441.6 441.5 441.3 441.2 + 441.1 441.0 440.9 ][ 360.1 360.2 360.3 360.3 360.4 360.4 360.5 360.6 360.6 360.7 360.8 360.8 360.9 360.9 361.0 + 361.1 361.1 361.2 361.2 361.3 361.4 361.4 361.5 361.6 361.6 361.7 361.7 361.8 361.9 361.9 362.0 362.0 362.1 + 362.2 363.0 363.9 364.7 365.6 366.4 367.2 368.0 368.8 369.6 370.4 371.1 371.8 372.5 373.1 373.7 374.2 374.7 + 375.1 375.4 375.7 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 + 375.9 375.9 375.9 375.9 375.9 375.9 ]plong + s[ 348.9 348.8 348.6 348.5 348.4 348.3 348.2 348.1 347.9 347.8 347.7 347.6 347.5 347.4 347.3 347.2 347.0 346.9 + 346.8 346.7 345.2 343.7 342.3 340.9 339.5 338.1 336.6 335.1 333.7 332.1 330.6 329.0 327.3 325.6 323.9 322.1 + 320.3 318.4 316.5 314.6 312.6 310.6 310.4 310.3 310.1 310.0 309.8 309.7 309.5 309.4 309.3 309.1 309.0 308.8 + 308.7 308.5 308.4 308.2 308.1 307.9 307.8 ][ 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 + 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.9 375.7 375.4 375.1 374.7 374.2 373.7 373.1 372.5 371.8 + 371.1 370.4 369.6 368.8 368.0 367.2 366.4 365.6 364.7 363.9 363.0 362.2 361.3 361.3 361.2 361.1 361.1 361.0 + 360.9 360.9 360.8 360.8 360.7 360.6 360.6 360.5 360.5 360.4 360.3 360.3 360.2 360.2 ]plong + s[ 247.6 247.6 247.6 247.6 247.5 247.5 247.5 247.4 247.4 247.4 247.4 247.3 247.3 247.3 247.2 247.2 247.2 247.1 + 247.1 247.1 247.0 246.6 246.1 245.5 244.9 244.3 243.6 242.9 242.1 241.3 240.5 239.6 238.6 237.6 236.6 235.5 + 234.4 233.3 232.1 230.9 229.6 228.3 227.0 225.6 224.2 222.7 221.2 219.7 218.2 216.6 215.0 213.3 211.7 210.0 + 208.3 206.5 204.8 203.0 201.2 199.5 ][ 360.0 359.9 359.8 359.8 359.7 359.7 359.6 359.5 359.5 359.4 359.4 359.3 + 359.2 359.2 359.1 359.0 359.0 358.9 358.9 358.8 358.7 357.9 357.0 356.2 355.4 354.5 353.7 352.9 352.1 351.2 + 350.4 349.7 348.9 348.1 347.3 346.6 345.9 345.1 344.4 343.7 343.0 342.3 341.7 341.0 340.4 339.8 339.2 338.6 + 338.0 337.4 336.9 336.3 335.8 335.3 334.8 334.4 333.9 333.5 333.1 332.7 ]plong + s[ 590.4 589.1 587.3 585.5 583.7 582.0 580.3 578.6 576.9 575.2 573.6 572.0 570.5 569.0 567.5 566.0 564.6 563.2 + 561.9 560.5 559.3 558.0 556.8 555.7 554.6 553.5 552.4 551.4 550.5 549.6 548.7 547.9 547.1 546.4 545.7 545.1 + 544.5 543.9 543.4 542.9 542.9 542.8 542.8 542.8 542.7 542.7 542.7 542.7 542.6 542.6 542.6 542.5 542.5 542.5 + 542.4 542.4 542.4 542.4 542.3 542.3 542.3 542.2 542.2 542.2 ][ 332.7 333.0 333.4 333.8 334.3 334.7 335.2 335.7 + 336.2 336.8 337.3 337.9 338.4 339.0 339.6 340.3 340.9 341.5 342.2 342.9 343.6 344.3 345.0 345.7 346.4 347.2 + 347.9 348.7 349.5 350.3 351.1 351.9 352.7 353.5 354.3 355.2 356.0 356.8 357.7 358.5 358.6 358.7 358.7 358.8 + 358.9 358.9 359.0 359.0 359.1 359.2 359.2 359.3 359.3 359.4 359.5 359.5 359.6 359.6 359.7 359.8 359.8 359.9 + 360.0 360.0 ]plong + s[ 481.8 481.7 481.5 481.4 481.2 481.1 480.9 480.8 480.6 480.5 480.3 480.2 480.0 479.9 479.7 477.7 475.7 473.8 + 471.9 470.0 468.2 466.4 464.6 462.9 ][ 360.3 360.3 360.4 360.4 360.5 360.6 360.6 360.7 360.7 360.8 360.9 360.9 + 361.0 361.1 361.1 362.0 362.8 363.7 364.5 365.4 366.2 367.0 367.9 368.7 ]plong + 476.1 287.9 475.6 286.2 2 pls + 456.8 294.1 456.4 292.4 2 pls + 416.4 302.0 416.2 300.2 2 pls + 395.5 303.0 395.5 301.2 2 pls + 354.1 299.1 354.4 297.4 2 pls + 334.2 294.4 334.6 292.7 2 pls + 296.1 281.1 296.6 279.4 2 pls + 277.6 273.5 278.2 271.7 2 pls + 240.6 258.6 241.0 256.8 2 pls + 221.4 252.4 221.8 250.6 2 pls + 589.3 247.4 589.0 245.6 2 pls + 569.5 252.1 569.1 250.3 2 pls + 531.7 265.3 531.2 263.6 2 pls + 513.3 273.0 512.7 271.3 2 pls + 476.1 287.9 475.6 286.2 2 pls + 456.8 294.1 456.4 292.4 2 pls + 436.2 180.0 434.6 181.1 2 pls + 441.2 183.4 439.6 184.3 2 pls + 445.9 187.5 444.4 188.3 2 pls + 450.7 192.0 449.0 192.8 2 pls + 455.4 196.9 453.7 197.7 2 pls + 465.2 207.1 463.5 207.7 2 pls + 470.3 212.3 468.6 212.9 2 pls + 473.9 217.7 472.2 218.2 2 pls + 477.1 223.4 475.4 224.0 2 pls + 480.0 229.5 478.2 230.1 2 pls + 485.0 242.6 483.3 243.3 2 pls + 487.3 249.5 485.6 250.2 2 pls + 489.6 256.6 487.9 257.3 2 pls + 491.7 263.8 490.0 264.5 2 pls + 493.9 271.1 492.2 271.7 2 pls + 498.2 285.5 496.5 286.2 2 pls + 500.4 292.6 498.7 293.2 2 pls + 502.7 299.5 501.0 300.1 2 pls + 505.2 306.2 503.5 306.8 2 pls + 507.8 312.6 506.1 313.3 2 pls + 513.9 324.5 512.2 325.1 2 pls + 517.9 330.0 516.1 330.4 2 pls + 526.5 335.3 524.7 335.6 2 pls + 534.6 339.6 533.1 340.7 2 pls + 542.8 344.5 541.4 345.6 2 pls + 558.3 353.5 557.1 354.9 2 pls + 565.6 357.4 564.6 358.9 2 pls + 572.7 360.5 571.8 362.1 2 pls + 579.9 362.6 579.1 364.2 2 pls + 587.7 363.3 586.8 364.9 2 pls + s[ 436.6 440.9 445.1 449.4 453.6 457.8 461.9 466.1 470.2 474.2 478.3 482.3 486.3 490.3 494.3 ][ 297.1 296.2 295.3 + 294.2 293.1 292.0 290.7 289.4 288.1 286.6 285.2 283.7 282.1 280.6 279.0 ]plong + s[ 436.6 432.3 427.9 423.6 419.2 414.8 410.3 405.9 401.5 397.0 392.6 388.1 383.7 379.2 374.8 ][ 297.1 297.9 298.7 + 299.3 299.8 300.3 300.7 300.9 301.1 301.2 301.2 301.1 300.9 300.6 300.3 ]plong + s[ 374.8 370.4 366.0 361.6 357.3 353.0 348.7 344.4 340.2 336.0 331.8 327.6 323.5 319.4 315.3 ][ 300.3 299.8 299.2 + 298.6 297.9 297.1 296.2 295.2 294.2 293.1 291.9 290.6 289.3 288.0 286.5 ]plong + s[ 315.3 311.3 307.3 303.3 299.3 295.3 291.3 287.4 283.4 279.5 275.5 271.6 267.6 263.7 259.7 ][ 286.5 285.1 283.6 + 282.0 280.5 278.9 277.2 275.6 273.9 272.3 270.6 268.9 267.3 265.6 264.0 ]plong + s[ 259.7 255.8 251.8 247.7 243.7 239.7 235.6 231.5 227.4 223.2 219.0 214.8 210.6 206.3 202.0 ][ 264.0 262.4 260.8 + 259.3 257.8 256.3 254.9 253.6 252.3 251.0 249.8 248.7 247.7 246.7 245.8 ]plong + 199.5 245.4 202.0 245.8 2 pls + s[ 590.4 587.5 583.3 579.0 574.7 570.5 566.4 562.2 558.1 554.0 549.9 ][ 245.4 245.9 246.8 247.8 248.8 249.9 251.1 + 252.3 253.7 255.0 256.4 ]plong + s[ 549.9 545.9 541.8 537.8 533.8 529.9 525.9 521.9 518.0 514.0 510.1 506.2 502.2 498.3 494.3 ][ 256.4 257.9 259.4 + 260.9 262.5 264.1 265.7 267.4 269.0 270.7 272.4 274.0 275.7 277.3 279.0 ]plong + s[ 494.3 490.3 486.3 482.3 478.3 474.2 470.2 466.1 461.9 457.8 453.6 449.4 445.1 440.9 436.6 ][ 279.0 280.6 282.1 + 283.7 285.2 286.6 288.1 289.4 290.7 292.0 293.1 294.2 295.3 296.2 297.1 ]plong + s[ 494.3 490.3 486.3 482.3 478.3 474.2 470.2 466.1 461.9 457.8 453.6 449.4 445.1 440.9 436.6 ][ 279.0 280.6 282.1 + 283.7 285.2 286.6 288.1 289.4 290.7 292.0 293.1 294.2 295.3 296.2 297.1 ]plong + s[ 429.3 429.5 429.7 429.9 430.1 430.2 430.4 430.6 430.8 431.0 431.1 431.3 431.5 431.7 431.8 432.0 432.2 432.4 + 432.5 432.7 432.9 433.0 433.2 433.4 433.6 433.7 433.9 436.1 438.3 440.3 442.4 444.4 446.4 448.4 450.4 452.4 + 454.4 456.5 458.6 ][ 178.9 179.0 179.1 179.1 179.2 179.2 179.3 179.3 179.4 179.5 179.5 179.6 179.6 179.7 179.8 + 179.8 179.9 180.0 180.1 180.1 180.2 180.3 180.3 180.4 180.5 180.6 180.7 181.9 183.3 184.9 186.5 188.3 190.2 + 192.2 194.2 196.3 198.4 200.5 202.6 ]plong + s[ 458.6 460.7 462.8 465.0 467.1 469.1 470.7 472.2 473.6 474.9 476.2 477.4 478.6 479.8 480.9 ][ 202.6 204.8 207.0 + 209.2 211.4 213.6 215.9 218.2 220.7 223.2 225.7 228.3 231.0 233.8 236.6 ]plong + s[ 480.9 481.9 483.0 484.0 485.0 486.0 486.9 487.9 488.8 489.7 490.6 491.5 492.5 493.4 494.3 ][ 236.6 239.4 242.3 + 245.2 248.2 251.2 254.2 257.3 260.4 263.4 266.5 269.7 272.8 275.9 279.0 ]plong + s[ 494.3 495.2 496.1 497.1 498.0 499.0 500.0 501.0 502.1 503.1 504.2 505.3 506.5 507.7 509.0 ][ 279.0 282.1 285.1 + 288.2 291.2 294.2 297.2 300.1 303.0 305.9 308.7 311.4 314.1 316.8 319.4 ]plong + s[ 509.0 510.3 511.7 513.2 514.7 517.3 521.0 524.7 528.3 532.0 535.5 539.1 542.5 546.0 549.4 ][ 319.4 321.9 324.3 + 326.7 329.0 331.2 333.4 335.6 337.8 339.9 342.1 344.2 346.3 348.4 350.4 ]plong + s[ 549.4 552.7 556.0 559.3 562.5 565.6 568.7 571.8 574.9 578.0 581.2 584.5 587.9 590.4 ][ 350.4 352.4 354.3 356.1 + 357.8 359.4 360.8 362.1 363.2 364.0 364.6 364.9 364.9 364.7 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 + 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 173.8 173.9 174.0 174.1 174.2 174.3 + 174.4 175.7 177.1 178.4 197.0 215.6 234.3 252.9 271.5 290.1 308.7 327.3 346.0 364.6 365.9 367.2 368.6 368.7 + 368.7 368.8 368.9 369.0 369.1 369.2 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 + 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 369.1 369.0 368.9 368.8 368.7 368.7 + 368.6 367.2 365.9 364.6 346.0 327.3 308.7 290.1 271.5 252.9 234.3 215.6 197.0 178.4 177.1 175.7 174.4 174.3 + 174.2 174.1 174.0 173.9 173.8 173.8 ]plong + 217.6 155.5 217.3 155.7 2 pls + s[ 220.6 220.4 220.2 220.0 219.8 219.8 219.6 219.4 219.2 218.8 218.6 218.4 218.2 218.2 218.0 217.8 217.6 ][ 155.7 + 155.5 155.4 155.3 155.1 154.8 154.7 154.6 154.4 154.4 154.6 154.7 154.8 155.1 155.3 155.4 155.5 ]plong + s[ 334.8 335.0 335.2 335.2 335.4 335.6 335.8 336.0 336.0 336.2 336.4 336.6 336.8 336.8 337.0 ][ 155.7 155.5 155.4 + 155.1 155.0 154.8 154.7 154.6 154.3 154.2 154.3 154.4 154.6 154.8 155.0 ]plong + 337.0 155.0 337.2 155.1 337.4 155.3 337.6 155.4 337.6 155.7 5 pls + 452.8 155.0 452.6 155.1 452.4 155.3 452.2 155.4 452.2 155.7 5 pls + s[ 455.1 454.9 454.7 454.7 454.4 454.2 454.0 453.8 453.8 453.6 453.4 453.2 453.0 453.0 452.8 ][ 155.7 155.5 155.4 + 155.1 155.0 154.8 154.7 154.6 154.3 154.2 154.3 154.4 154.6 154.8 155.0 ]plong + s[ 569.3 569.5 569.7 569.9 570.1 570.1 570.3 570.5 570.7 571.1 571.3 571.5 571.7 571.7 571.9 572.1 572.3 ][ 155.7 + 155.5 155.4 155.3 155.1 154.8 154.7 154.6 154.4 154.4 154.6 154.7 154.8 155.1 155.3 155.4 155.5 ]plong + 572.3 155.5 572.5 155.7 2 pls + s[ 215.3 215.3 215.5 215.7 215.7 215.9 216.1 216.3 216.5 216.7 216.9 216.9 217.1 217.3 217.3 ][ 158.1 157.8 157.7 + 157.5 157.3 157.1 157.0 156.9 156.7 156.6 156.5 156.2 156.1 155.9 155.7 ]plong + 220.6 155.7 220.6 155.9 220.8 156.1 221.0 156.2 221.2 156.3 5 pls + s[ 223.0 222.8 222.6 222.4 222.2 222.2 222.0 221.8 221.6 221.4 221.4 221.2 ][ 158.1 157.9 157.8 157.7 157.5 157.3 + 157.1 157.0 156.9 156.7 156.5 156.3 ]plong + s[ 332.4 332.6 332.8 332.8 333.0 333.2 333.4 ][ 158.1 157.9 157.8 157.5 157.4 157.3 157.1 ]plong + s[ 333.4 333.6 333.6 333.8 334.0 334.2 334.4 334.4 334.6 334.8 ][ 157.1 157.0 156.7 156.6 156.5 156.3 156.2 155.9 + 155.8 155.7 ]plong + s[ 340.0 340.0 339.8 339.6 339.4 339.2 339.2 339.0 338.8 338.6 338.4 338.4 338.2 338.0 337.8 337.6 ][ 158.1 157.8 + 157.7 157.5 157.4 157.3 157.0 156.9 156.7 156.6 156.5 156.2 156.1 155.9 155.8 155.7 ]plong + s[ 449.8 449.8 450.0 450.2 450.4 450.6 450.6 450.8 451.0 451.2 451.4 451.4 451.6 451.8 452.0 452.2 ][ 158.1 157.8 + 157.7 157.5 157.4 157.3 157.0 156.9 156.7 156.6 156.5 156.2 156.1 155.9 155.8 155.7 ]plong + s[ 456.5 456.3 456.3 456.1 455.9 455.7 455.5 455.5 455.3 455.1 ][ 157.1 157.0 156.7 156.6 156.5 156.3 156.2 155.9 + 155.8 155.7 ]plong + s[ 457.5 457.3 457.1 457.1 456.9 456.7 456.5 ][ 158.1 157.9 157.8 157.5 157.4 157.3 157.1 ]plong + s[ 566.9 567.1 567.3 567.5 567.7 567.7 567.9 568.1 568.3 568.5 568.5 568.7 ][ 158.1 157.9 157.8 157.7 157.5 157.3 + 157.1 157.0 156.9 156.7 156.5 156.3 ]plong + 569.3 155.7 569.3 155.9 569.1 156.1 568.9 156.2 568.7 156.3 5 pls + s[ 572.5 572.5 572.7 572.9 573.1 573.3 573.3 573.5 573.7 573.9 574.1 574.1 574.3 574.5 574.7 574.9 ][ 155.7 155.9 + 156.1 156.2 156.3 156.5 156.7 156.9 157.0 157.1 157.3 157.5 157.7 157.8 157.9 158.1 ]plong + s[ 212.5 212.7 212.9 213.1 213.3 213.3 213.5 213.7 213.9 ][ 160.5 160.3 160.2 160.1 159.9 159.7 159.5 159.4 159.3 +]plong + s[ 213.9 214.1 214.3 214.5 214.5 214.7 214.9 215.1 215.3 ][ 159.3 159.1 159.0 158.9 158.6 158.5 158.3 158.2 158.1 +]plong + s[ 224.8 224.6 224.6 224.4 224.2 224.0 223.8 223.8 223.6 223.4 223.2 223.0 223.0 ][ 160.1 159.9 159.7 159.5 159.4 + 159.3 159.1 158.9 158.7 158.6 158.5 158.3 158.1 ]plong + 224.8 160.1 225.0 160.2 225.2 160.3 225.4 160.5 4 pls + s[ 330.0 330.2 330.4 330.4 330.6 330.8 331.0 331.2 331.2 331.4 331.6 331.8 332.0 332.0 332.2 332.4 ][ 160.5 160.3 + 160.2 159.9 159.8 159.7 159.5 159.4 159.1 159.0 158.9 158.7 158.6 158.3 158.2 158.1 ]plong + 340.0 158.1 340.2 158.2 340.4 158.3 340.6 158.5 4 pls + s[ 342.4 342.4 342.2 342.0 341.8 341.6 341.6 341.4 341.2 341.0 340.8 340.8 340.6 ][ 160.5 160.2 160.1 159.9 159.8 + 159.7 159.4 159.3 159.1 159.0 158.9 158.6 158.5 ]plong + s[ 447.4 447.4 447.6 447.8 448.0 448.2 448.2 448.4 448.6 448.8 449.0 449.0 449.2 ][ 160.5 160.2 160.1 159.9 159.8 + 159.7 159.4 159.3 159.1 159.0 158.9 158.6 158.5 ]plong + 449.8 158.1 449.6 158.2 449.4 158.3 449.2 158.5 4 pls + s[ 459.9 459.7 459.5 459.5 459.3 459.1 458.9 458.7 458.7 458.5 458.3 458.1 457.9 457.9 457.7 457.5 ][ 160.5 160.3 + 160.2 159.9 159.8 159.7 159.5 159.4 159.1 159.0 158.9 158.7 158.6 158.3 158.2 158.1 ]plong + 565.1 160.1 564.9 160.2 564.7 160.3 564.5 160.5 4 pls + s[ 565.1 565.3 565.3 565.5 565.7 565.9 566.1 566.1 566.3 566.5 566.7 566.9 566.9 ][ 160.1 159.9 159.7 159.5 159.4 + 159.3 159.1 158.9 158.7 158.6 158.5 158.3 158.1 ]plong + s[ 575.9 575.7 575.7 575.5 575.3 575.1 574.9 574.9 ][ 159.3 159.1 158.9 158.7 158.6 158.5 158.3 158.1 ]plong + s[ 577.3 577.1 576.9 576.7 576.5 576.5 576.3 576.1 575.9 ][ 160.5 160.3 160.2 160.1 159.9 159.7 159.5 159.4 159.3 +]plong + s[ 210.5 210.5 210.7 210.9 211.1 211.3 211.3 211.5 211.7 211.9 212.1 212.1 212.3 212.5 212.5 ][ 162.9 162.6 162.5 + 162.4 162.2 162.1 161.8 161.7 161.6 161.4 161.3 161.0 160.9 160.7 160.5 ]plong + s[ 227.8 227.6 227.4 227.2 227.0 227.0 226.8 226.6 226.4 226.2 226.2 226.0 225.8 225.6 225.4 225.4 ][ 162.9 162.8 + 162.6 162.5 162.4 162.1 162.0 161.8 161.7 161.6 161.3 161.2 161.0 160.9 160.7 160.5 ]plong + s[ 327.6 327.8 328.0 328.0 328.2 328.4 328.6 328.8 328.8 329.0 329.2 329.4 329.6 329.6 329.8 ][ 162.9 162.8 162.6 + 162.4 162.2 162.1 162.0 161.8 161.6 161.4 161.3 161.2 161.0 160.7 160.6 ]plong + 329.8 160.6 330.0 160.5 2 pls + s[ 344.2 344.0 344.0 343.8 343.6 343.4 343.2 343.2 343.0 342.8 342.6 342.4 ][ 162.2 162.1 161.8 161.7 161.6 161.4 + 161.3 161.0 160.9 160.7 160.6 160.5 ]plong + 344.2 162.2 344.4 162.4 344.6 162.5 344.8 162.6 344.8 162.9 5 pls + 445.6 162.2 445.4 162.4 445.2 162.5 445.0 162.6 445.0 162.9 5 pls + s[ 445.6 445.8 445.8 446.0 446.2 446.4 446.6 446.6 446.8 447.0 447.2 447.4 ][ 162.2 162.1 161.8 161.7 161.6 161.4 + 161.3 161.0 160.9 160.7 160.6 160.5 ]plong + 459.9 160.5 460.1 160.6 2 pls + s[ 462.3 462.1 461.9 461.9 461.7 461.5 461.3 461.1 461.1 460.9 460.7 460.5 460.3 460.3 460.1 ][ 162.9 162.8 162.6 + 162.4 162.2 162.1 162.0 161.8 161.6 161.4 161.3 161.2 161.0 160.7 160.6 ]plong + s[ 562.0 562.2 562.4 562.6 562.8 562.8 563.0 563.2 563.4 563.6 563.6 563.8 564.1 564.3 564.5 564.5 ][ 162.9 162.8 + 162.6 162.5 162.4 162.1 162.0 161.8 161.7 161.6 161.3 161.2 161.0 160.9 160.7 160.5 ]plong + s[ 579.5 579.3 579.1 578.9 578.9 578.7 578.5 578.3 578.1 578.1 577.9 577.7 577.5 577.3 577.3 ][ 162.8 162.6 162.5 + 162.4 162.1 162.0 161.8 161.7 161.6 161.3 161.2 161.0 160.9 160.7 160.5 ]plong + 579.5 162.8 579.7 162.9 2 pls + s[ 208.1 208.1 208.3 208.5 208.5 208.7 208.9 209.1 209.3 209.3 209.5 209.7 209.9 210.1 210.3 ][ 165.3 165.0 164.9 + 164.8 164.5 164.4 164.2 164.1 164.0 163.7 163.6 163.4 163.3 163.2 163.0 ]plong + 210.3 163.0 210.5 162.9 2 pls + 227.8 162.9 227.8 163.2 228.0 163.3 228.2 163.4 228.4 163.6 5 pls + s[ 230.2 230.0 229.8 229.6 229.4 229.4 229.2 229.0 228.8 228.6 228.6 228.4 ][ 165.3 165.2 165.0 164.9 164.8 164.5 + 164.4 164.2 164.1 164.0 163.7 163.6 ]plong + s[ 325.1 325.3 325.5 325.5 325.7 325.9 326.1 ][ 165.3 165.2 165.0 164.8 164.6 164.5 164.4 ]plong + s[ 326.1 326.3 326.3 326.5 326.7 327.0 327.2 327.2 327.4 327.6 ][ 164.4 164.2 164.0 163.8 163.7 163.6 163.4 163.2 + 163.0 162.9 ]plong + s[ 347.3 347.3 347.1 346.9 346.7 346.5 346.5 346.3 346.1 345.9 345.7 345.7 345.5 345.3 345.0 344.8 ][ 165.3 165.0 + 164.9 164.8 164.6 164.5 164.2 164.1 164.0 163.8 163.7 163.4 163.3 163.2 163.0 162.9 ]plong + s[ 442.6 442.6 442.8 443.0 443.2 443.4 443.4 443.6 443.8 444.0 444.2 444.2 444.4 444.6 444.8 445.0 ][ 165.3 165.0 + 164.9 164.8 164.6 164.5 164.2 164.1 164.0 163.8 163.7 163.4 163.3 163.2 163.0 162.9 ]plong + s[ 463.7 463.5 463.5 463.3 463.1 462.9 462.7 462.7 462.5 462.3 ][ 164.4 164.2 164.0 163.8 163.7 163.6 163.4 163.2 + 163.0 162.9 ]plong + s[ 464.7 464.5 464.3 464.3 464.1 463.9 463.7 ][ 165.3 165.2 165.0 164.8 164.6 164.5 164.4 ]plong + s[ 559.6 559.8 560.0 560.2 560.4 560.4 560.6 560.8 561.0 561.2 561.2 561.4 ][ 165.3 165.2 165.0 164.9 164.8 164.5 + 164.4 164.2 164.1 164.0 163.7 163.6 ]plong + 562.0 162.9 562.0 163.2 561.8 163.3 561.6 163.4 561.4 163.6 5 pls + s[ 579.7 579.7 579.9 580.1 580.3 580.5 580.5 580.7 580.9 581.1 581.3 581.3 581.5 581.7 581.9 582.1 ][ 162.9 163.2 + 163.3 163.4 163.6 163.7 164.0 164.1 164.2 164.4 164.5 164.8 164.9 165.0 165.2 165.3 ]plong + s[ 205.3 205.5 205.7 205.9 206.1 206.1 206.3 206.5 206.7 ][ 167.7 167.6 167.5 167.3 167.2 166.9 166.8 166.6 166.5 +]plong + s[ 206.7 206.9 207.1 207.3 207.3 207.5 207.7 207.9 208.1 ][ 166.5 166.4 166.2 166.1 165.8 165.7 165.6 165.4 165.3 +]plong + s[ 232.0 231.8 231.8 231.6 231.4 231.2 231.0 231.0 230.8 230.6 230.4 230.2 230.2 ][ 167.3 167.2 166.9 166.8 166.6 + 166.5 166.4 166.1 166.0 165.8 165.7 165.6 165.3 ]plong + 232.0 167.3 232.2 167.5 232.4 167.6 232.6 167.7 4 pls + s[ 322.7 322.9 323.1 323.1 323.3 323.5 323.7 323.9 323.9 324.1 324.3 324.5 324.7 324.7 324.9 325.1 ][ 167.7 167.6 + 167.5 167.2 167.0 166.9 166.8 166.6 166.4 166.2 166.1 166.0 165.8 165.6 165.4 165.3 ]plong + 347.3 165.3 347.5 165.4 347.7 165.6 347.9 165.7 4 pls + s[ 349.7 349.7 349.5 349.3 349.1 348.9 348.9 348.7 348.5 348.3 348.1 348.1 347.9 ][ 167.7 167.5 167.3 167.2 167.0 + 166.9 166.6 166.5 166.4 166.2 166.1 165.8 165.7 ]plong + s[ 440.2 440.2 440.4 440.6 440.8 441.0 441.0 441.2 441.4 441.6 441.8 441.8 442.0 ][ 167.7 167.5 167.3 167.2 167.0 + 166.9 166.6 166.5 166.4 166.2 166.1 165.8 165.7 ]plong + 442.6 165.3 442.4 165.4 442.2 165.6 442.0 165.7 4 pls + s[ 467.1 466.9 466.7 466.7 466.5 466.3 466.1 465.9 465.9 465.7 465.5 465.3 465.1 465.1 464.9 464.7 ][ 167.7 167.6 + 167.5 167.2 167.0 166.9 166.8 166.6 166.4 166.2 166.1 166.0 165.8 165.6 165.4 165.3 ]plong + 557.8 167.3 557.6 167.5 557.4 167.6 557.2 167.7 4 pls + s[ 557.8 558.0 558.0 558.2 558.4 558.6 558.8 558.8 559.0 559.2 559.4 559.6 559.6 ][ 167.3 167.2 166.9 166.8 166.6 + 166.5 166.4 166.1 166.0 165.8 165.7 165.6 165.3 ]plong + s[ 583.2 583.0 583.0 582.8 582.6 582.4 582.1 582.1 ][ 166.5 166.4 166.1 166.0 165.8 165.7 165.6 165.3 ]plong + s[ 584.6 584.4 584.2 584.0 583.8 583.8 583.6 583.4 583.2 ][ 167.7 167.6 167.5 167.3 167.2 166.9 166.8 166.6 166.5 +]plong + s[ 203.3 203.3 203.5 203.7 203.9 204.1 204.1 204.3 204.5 204.5 204.7 204.9 205.1 205.3 205.3 ][ 170.1 169.9 169.7 + 169.6 169.5 169.3 169.1 168.9 168.8 168.5 168.4 168.3 168.1 168.0 167.7 ]plong + s[ 235.0 234.8 234.6 234.4 234.2 234.2 234.0 233.8 233.6 233.4 233.4 233.2 233.0 232.8 232.6 232.6 ][ 170.1 170.0 + 169.9 169.7 169.6 169.3 169.2 169.1 168.9 168.8 168.5 168.4 168.3 168.1 168.0 167.7 ]plong + s[ 320.3 320.5 320.7 320.7 320.9 321.1 321.3 321.5 321.5 321.7 321.9 322.1 322.3 322.3 322.5 ][ 170.1 170.0 169.9 + 169.6 169.5 169.3 169.2 169.1 168.8 168.7 168.5 168.4 168.3 168.0 167.9 ]plong + 322.5 167.9 322.7 167.7 2 pls + s[ 351.5 351.3 351.3 351.1 350.9 350.7 350.5 350.5 350.3 350.1 349.9 349.7 ][ 169.5 169.3 169.1 168.9 168.8 168.7 + 168.5 168.3 168.1 168.0 167.9 167.7 ]plong + 351.5 169.5 351.7 169.6 351.9 169.7 352.1 169.9 352.1 170.1 5 pls + 438.4 169.5 438.2 169.6 438.0 169.7 437.8 169.9 437.8 170.1 5 pls + s[ 438.4 438.6 438.6 438.8 439.0 439.2 439.4 439.4 439.6 439.8 440.0 440.2 ][ 169.5 169.3 169.1 168.9 168.8 168.7 + 168.5 168.3 168.1 168.0 167.9 167.7 ]plong + 467.1 167.7 467.3 167.9 2 pls + s[ 469.5 469.3 469.1 469.1 468.9 468.7 468.5 468.3 468.3 468.1 467.9 467.7 467.5 467.5 467.3 ][ 170.1 170.0 169.9 + 169.6 169.5 169.3 169.2 169.1 168.8 168.7 168.5 168.4 168.3 168.0 167.9 ]plong + s[ 554.8 555.0 555.2 555.4 555.6 555.6 555.8 556.0 556.2 556.4 556.4 556.6 556.8 557.0 557.2 557.2 ][ 170.1 170.0 + 169.9 169.7 169.6 169.3 169.2 169.1 168.9 168.8 168.5 168.4 168.3 168.1 168.0 167.7 ]plong + s[ 586.8 586.6 586.4 586.2 586.2 586.0 585.8 585.6 585.4 585.4 585.2 585.0 584.8 584.6 584.6 ][ 170.0 169.9 169.7 + 169.6 169.3 169.2 169.1 168.9 168.8 168.5 168.4 168.3 168.1 168.0 167.7 ]plong + 586.8 170.0 587.0 170.1 2 pls + s[ 200.9 200.9 201.1 201.3 201.5 201.7 201.7 201.9 202.1 202.1 202.3 202.5 202.7 202.9 203.1 ][ 172.5 172.3 172.1 + 172.0 171.9 171.7 171.5 171.3 171.2 170.9 170.8 170.7 170.5 170.4 170.3 ]plong + 203.1 170.3 203.3 170.1 2 pls + 235.0 170.1 235.0 170.4 235.2 170.5 235.4 170.7 235.6 170.8 5 pls + s[ 237.5 237.3 237.1 236.9 236.7 236.7 236.5 236.3 236.1 235.9 235.9 235.6 ][ 172.5 172.4 172.3 172.1 172.0 171.7 + 171.6 171.5 171.3 171.2 170.9 170.8 ]plong + s[ 317.9 318.1 318.3 318.3 318.5 318.7 318.9 ][ 172.5 172.4 172.3 172.0 171.9 171.7 171.6 ]plong + s[ 318.9 319.1 319.1 319.3 319.5 319.7 319.9 319.9 320.1 320.3 ][ 171.6 171.5 171.2 171.1 170.9 170.8 170.7 170.4 + 170.3 170.1 ]plong + s[ 354.5 354.5 354.3 354.1 353.9 353.7 353.7 353.5 353.3 353.1 352.9 352.9 352.7 352.5 352.3 352.1 ][ 172.5 172.3 + 172.1 172.0 171.9 171.7 171.5 171.3 171.2 171.1 170.9 170.7 170.5 170.4 170.3 170.1 ]plong + s[ 435.3 435.3 435.5 435.7 435.9 436.1 436.1 436.4 436.6 436.8 437.0 437.0 437.2 437.4 437.6 437.8 ][ 172.5 172.3 + 172.1 172.0 171.9 171.7 171.5 171.3 171.2 171.1 170.9 170.7 170.5 170.4 170.3 170.1 ]plong + s[ 470.9 470.7 470.7 470.5 470.3 470.1 469.9 469.9 469.7 469.5 ][ 171.6 171.5 171.2 171.1 170.9 170.8 170.7 170.4 + 170.3 170.1 ]plong + s[ 471.9 471.7 471.5 471.5 471.3 471.1 470.9 ][ 172.5 172.4 172.3 172.0 171.9 171.7 171.6 ]plong + s[ 552.4 552.6 552.8 553.0 553.2 553.2 553.4 553.6 553.8 554.0 554.0 554.2 ][ 172.5 172.4 172.3 172.1 172.0 171.7 + 171.6 171.5 171.3 171.2 170.9 170.8 ]plong + 554.8 170.1 554.8 170.4 554.6 170.5 554.4 170.7 554.2 170.8 5 pls + s[ 587.0 587.0 587.2 587.4 587.6 587.8 587.8 588.0 588.2 588.4 588.6 588.6 588.8 589.0 589.2 589.4 ][ 170.1 170.4 + 170.5 170.7 170.8 170.9 171.2 171.3 171.5 171.6 171.7 172.0 172.1 172.3 172.4 172.5 ]plong + s[ 199.5 199.7 199.9 200.1 200.1 200.3 200.5 200.7 200.9 ][ 173.8 173.6 173.5 173.4 173.1 172.9 172.8 172.7 172.5 +]plong + s[ 239.3 239.1 239.1 238.9 238.7 238.5 238.3 238.3 238.1 237.9 237.7 237.5 237.5 ][ 174.6 174.4 174.2 174.0 173.9 + 173.8 173.6 173.4 173.2 173.1 172.9 172.8 172.5 ]plong + 239.3 174.6 239.5 174.7 239.7 174.8 239.9 175.0 4 pls + s[ 315.5 315.7 315.9 315.9 316.1 316.3 316.5 316.7 316.7 316.9 317.1 317.3 317.5 317.5 317.7 317.9 ][ 175.0 174.8 + 174.7 174.4 174.3 174.2 174.0 173.9 173.6 173.5 173.4 173.2 173.1 172.8 172.7 172.5 ]plong + 354.5 172.5 354.7 172.7 354.9 172.8 355.1 172.9 4 pls + s[ 356.9 356.9 356.7 356.5 356.3 356.1 356.1 355.9 355.7 355.5 355.3 355.3 355.1 ][ 175.0 174.7 174.6 174.4 174.3 + 174.2 173.9 173.8 173.6 173.5 173.4 173.1 172.9 ]plong + s[ 432.9 432.9 433.1 433.3 433.5 433.7 433.7 433.9 434.1 434.3 434.5 434.5 434.7 ][ 175.0 174.7 174.6 174.4 174.3 + 174.2 173.9 173.8 173.6 173.5 173.4 173.1 172.9 ]plong + 435.3 172.5 435.1 172.7 434.9 172.8 434.7 172.9 4 pls + s[ 474.4 474.2 474.0 474.0 473.8 473.6 473.4 473.2 473.2 473.0 472.7 472.5 472.3 472.3 472.1 471.9 ][ 175.0 174.8 + 174.7 174.4 174.3 174.2 174.0 173.9 173.6 173.5 173.4 173.2 173.1 172.8 172.7 172.5 ]plong + 550.6 174.6 550.4 174.7 550.2 174.8 550.0 175.0 4 pls + s[ 550.6 550.8 550.8 551.0 551.2 551.4 551.6 551.6 551.8 552.0 552.2 552.4 552.4 ][ 174.6 174.4 174.2 174.0 173.9 + 173.8 173.6 173.4 173.2 173.1 172.9 172.8 172.5 ]plong + s[ 590.4 590.2 590.2 590.0 589.8 589.6 589.4 589.4 ][ 173.8 173.6 173.4 173.2 173.1 172.9 172.8 172.5 ]plong + s[ 242.3 242.1 241.9 241.7 241.5 241.5 241.3 241.1 240.9 240.7 240.7 240.5 240.3 240.1 239.9 239.9 ][ 177.4 177.2 + 177.1 177.0 176.8 176.6 176.4 176.3 176.2 176.0 175.8 175.6 175.5 175.4 175.2 175.0 ]plong + s[ 313.1 313.3 313.5 313.5 313.7 313.9 314.1 314.3 314.3 314.5 314.7 314.9 315.1 315.1 315.3 ][ 177.4 177.2 177.1 + 176.8 176.7 176.6 176.4 176.3 176.0 175.9 175.8 175.6 175.5 175.2 175.1 ]plong + 315.3 175.1 315.5 175.0 2 pls + s[ 358.7 358.5 358.5 358.3 358.1 357.9 357.7 357.7 357.5 357.3 357.1 356.9 ][ 176.7 176.6 176.3 176.2 176.0 175.9 + 175.8 175.5 175.4 175.2 175.1 175.0 ]plong + 358.7 176.7 358.9 176.8 359.1 177.0 359.3 177.1 359.3 177.4 5 pls + 431.1 176.7 430.9 176.8 430.7 177.0 430.5 177.1 430.5 177.4 5 pls + s[ 431.1 431.3 431.3 431.5 431.7 431.9 432.1 432.1 432.3 432.5 432.7 432.9 ][ 176.7 176.6 176.3 176.2 176.0 175.9 + 175.8 175.5 175.4 175.2 175.1 175.0 ]plong + 474.4 175.0 474.6 175.1 2 pls + s[ 476.8 476.6 476.4 476.4 476.2 476.0 475.8 475.6 475.6 475.4 475.2 475.0 474.8 474.8 474.6 ][ 177.4 177.2 177.1 + 176.8 176.7 176.6 176.4 176.3 176.0 175.9 175.8 175.6 175.5 175.2 175.1 ]plong + s[ 547.6 547.8 548.0 548.2 548.4 548.4 548.6 548.8 549.0 549.2 549.2 549.4 549.6 549.8 550.0 550.0 ][ 177.4 177.2 + 177.1 177.0 176.8 176.6 176.4 176.3 176.2 176.0 175.8 175.6 175.5 175.4 175.2 175.0 ]plong + 242.3 177.4 242.3 177.6 242.5 177.8 242.7 177.9 242.9 178.0 5 pls + s[ 244.7 244.5 244.3 244.1 243.9 243.9 243.7 243.5 243.3 243.1 243.1 242.9 ][ 179.8 179.7 179.5 179.4 179.2 179.0 + 178.8 178.7 178.6 178.4 178.2 178.0 ]plong + s[ 310.7 310.9 311.1 311.1 311.3 311.5 311.7 ][ 179.8 179.7 179.5 179.2 179.1 179.0 178.8 ]plong + s[ 311.7 311.9 311.9 312.1 312.3 312.5 312.7 312.7 312.9 313.1 ][ 178.8 178.7 178.4 178.3 178.2 178.0 177.9 177.6 + 177.5 177.4 ]plong + s[ 361.7 361.7 361.5 361.3 361.1 360.9 360.9 360.7 360.5 360.3 360.1 360.1 359.9 359.7 359.5 359.3 ][ 179.8 179.5 + 179.4 179.2 179.1 179.0 178.7 178.6 178.4 178.3 178.2 177.9 177.8 177.6 177.5 177.4 ]plong + s[ 428.1 428.1 428.3 428.5 428.7 428.9 428.9 429.1 429.3 429.5 429.7 429.7 429.9 430.1 430.3 430.5 ][ 179.8 179.5 + 179.4 179.2 179.1 179.0 178.7 178.6 178.4 178.3 178.2 177.9 177.8 177.6 177.5 177.4 ]plong + s[ 478.2 478.0 478.0 477.8 477.6 477.4 477.2 477.2 477.0 476.8 ][ 178.8 178.7 178.4 178.3 178.2 178.0 177.9 177.6 + 177.5 177.4 ]plong + s[ 479.2 479.0 478.8 478.8 478.6 478.4 478.2 ][ 179.8 179.7 179.5 179.2 179.1 179.0 178.8 ]plong + s[ 545.1 545.3 545.5 545.7 546.0 546.0 546.2 546.4 546.6 546.8 546.8 547.0 ][ 179.8 179.7 179.5 179.4 179.2 179.0 + 178.8 178.7 178.6 178.4 178.2 178.0 ]plong + 547.6 177.4 547.6 177.6 547.4 177.8 547.2 177.9 547.0 178.0 5 pls + s[ 246.5 246.3 246.3 246.1 245.9 245.7 245.5 245.5 245.3 245.1 244.9 244.7 244.7 ][ 181.8 181.7 181.4 181.3 181.1 + 181.0 180.9 180.6 180.5 180.3 180.2 180.1 179.8 ]plong + 246.5 181.8 246.7 181.9 246.9 182.1 247.1 182.2 4 pls + s[ 308.2 308.4 308.7 308.7 308.9 309.1 309.3 309.5 309.5 309.7 309.9 310.1 310.3 310.3 310.5 310.7 ][ 182.2 182.1 + 181.9 181.7 181.5 181.4 181.3 181.1 180.9 180.7 180.6 180.5 180.3 180.1 179.9 179.8 ]plong + 361.7 179.8 361.9 179.9 362.1 180.1 362.3 180.2 4 pls + s[ 364.2 364.2 364.0 363.8 363.6 363.3 363.3 363.1 362.9 362.7 362.5 362.5 362.3 ][ 182.2 181.9 181.8 181.7 181.5 + 181.4 181.1 181.0 180.9 180.7 180.6 180.3 180.2 ]plong + s[ 425.7 425.7 425.9 426.1 426.3 426.5 426.5 426.7 426.9 427.1 427.3 427.3 427.5 ][ 182.2 181.9 181.8 181.7 181.5 + 181.4 181.1 181.0 180.9 180.7 180.6 180.3 180.2 ]plong + 428.1 179.8 427.9 179.9 427.7 180.1 427.5 180.2 4 pls + s[ 481.6 481.4 481.2 481.2 481.0 480.8 480.6 480.4 480.4 480.2 480.0 479.8 479.6 479.6 479.4 479.2 ][ 182.2 182.1 + 181.9 181.7 181.5 181.4 181.3 181.1 180.9 180.7 180.6 180.5 180.3 180.1 179.9 179.8 ]plong + 543.3 181.8 543.1 181.9 542.9 182.1 542.7 182.2 4 pls + s[ 543.3 543.5 543.5 543.7 543.9 544.1 544.3 544.3 544.5 544.7 544.9 545.1 545.1 ][ 181.8 181.7 181.4 181.3 181.1 + 181.0 180.9 180.6 180.5 180.3 180.2 180.1 179.8 ]plong + s[ 249.5 249.3 249.1 248.9 248.7 248.7 248.5 248.3 248.1 247.9 247.9 247.7 247.5 247.3 247.1 247.1 ][ 184.6 184.5 + 184.3 184.2 184.1 183.8 183.7 183.5 183.4 183.3 183.0 182.9 182.7 182.6 182.5 182.2 ]plong + s[ 305.8 306.0 306.2 306.2 306.4 306.6 306.8 307.0 307.0 307.2 307.4 307.6 307.8 307.8 308.0 ][ 184.6 184.5 184.3 + 184.1 183.9 183.8 183.7 183.5 183.3 183.1 183.0 182.9 182.7 182.5 182.3 ]plong + 308.0 182.3 308.2 182.2 2 pls + s[ 366.0 365.8 365.8 365.6 365.4 365.2 365.0 365.0 364.8 364.6 364.4 364.2 ][ 183.9 183.8 183.5 183.4 183.3 183.1 + 183.0 182.7 182.6 182.5 182.3 182.2 ]plong + 366.0 183.9 366.2 184.1 366.4 184.2 366.6 184.3 366.6 184.6 5 pls + 423.9 183.9 423.7 184.1 423.5 184.2 423.3 184.3 423.3 184.6 5 pls + s[ 423.9 424.1 424.1 424.3 424.5 424.7 424.9 424.9 425.1 425.3 425.5 425.7 ][ 183.9 183.8 183.5 183.4 183.3 183.1 + 183.0 182.7 182.6 182.5 182.3 182.2 ]plong + 481.6 182.2 481.8 182.3 2 pls + s[ 484.0 483.8 483.6 483.6 483.4 483.2 483.0 482.8 482.8 482.6 482.4 482.2 482.0 482.0 481.8 ][ 184.6 184.5 184.3 + 184.1 183.9 183.8 183.7 183.5 183.3 183.1 183.0 182.9 182.7 182.5 182.3 ]plong + s[ 540.3 540.5 540.7 540.9 541.1 541.1 541.3 541.5 541.7 541.9 541.9 542.1 542.3 542.5 542.7 542.7 ][ 184.6 184.5 + 184.3 184.2 184.1 183.8 183.7 183.5 183.4 183.3 183.0 182.9 182.7 182.6 182.5 182.2 ]plong + 249.5 184.6 249.5 184.9 249.7 185.0 249.9 185.1 250.1 185.3 5 pls + s[ 251.9 251.7 251.5 251.3 251.1 251.1 250.9 250.7 250.5 250.3 250.3 250.1 ][ 187.0 186.9 186.8 186.6 186.5 186.2 + 186.1 186.0 185.8 185.7 185.4 185.3 ]plong + s[ 303.4 303.6 303.8 303.8 304.0 304.2 304.4 ][ 187.0 186.9 186.8 186.5 186.4 186.2 186.1 ]plong + s[ 304.4 304.6 304.6 304.8 305.0 305.2 305.4 305.4 305.6 305.8 ][ 186.1 186.0 185.7 185.6 185.4 185.3 185.1 184.9 + 184.7 184.6 ]plong + s[ 369.0 369.0 368.8 368.6 368.4 368.2 368.2 368.0 367.8 367.6 367.4 367.4 367.2 367.0 366.8 366.6 ][ 187.0 186.8 + 186.6 186.5 186.4 186.2 186.0 185.8 185.7 185.6 185.4 185.1 185.0 184.9 184.7 184.6 ]plong + s[ 420.9 420.9 421.1 421.3 421.5 421.7 421.7 421.9 422.1 422.3 422.5 422.5 422.7 422.9 423.1 423.3 ][ 187.0 186.8 + 186.6 186.5 186.4 186.2 186.0 185.8 185.7 185.6 185.4 185.1 185.0 184.9 184.7 184.6 ]plong + s[ 485.4 485.2 485.2 485.0 484.8 484.6 484.4 484.4 484.2 484.0 ][ 186.1 186.0 185.7 185.6 185.4 185.3 185.1 184.9 + 184.7 184.6 ]plong + s[ 486.4 486.2 486.0 486.0 485.8 485.6 485.4 ][ 187.0 186.9 186.8 186.5 186.4 186.2 186.1 ]plong + s[ 537.9 538.1 538.3 538.5 538.7 538.7 538.9 539.1 539.3 539.5 539.5 539.7 ][ 187.0 186.9 186.8 186.6 186.5 186.2 + 186.1 186.0 185.8 185.7 185.4 185.3 ]plong + 540.3 184.6 540.3 184.9 540.1 185.0 539.9 185.1 539.7 185.3 5 pls + s[ 253.7 253.5 253.5 253.3 253.1 252.9 252.7 252.7 252.5 252.3 252.1 251.9 251.9 ][ 189.0 188.9 188.6 188.5 188.4 + 188.2 188.1 187.8 187.7 187.6 187.4 187.3 187.0 ]plong + 253.7 189.0 254.0 189.2 254.2 189.3 254.4 189.4 4 pls + s[ 301.0 301.2 301.4 301.4 301.6 301.8 302.0 302.2 302.2 302.4 302.6 302.8 303.0 303.0 303.2 303.4 ][ 189.4 189.3 + 189.2 188.9 188.8 188.6 188.5 188.4 188.1 188.0 187.8 187.7 187.6 187.3 187.2 187.0 ]plong + 369.0 187.0 369.2 187.2 369.4 187.3 369.6 187.4 4 pls + s[ 371.4 371.4 371.2 371.0 370.8 370.6 370.6 370.4 370.2 370.0 369.8 369.8 369.6 ][ 189.4 189.2 189.0 188.9 188.8 + 188.6 188.4 188.2 188.1 188.0 187.8 187.6 187.4 ]plong + s[ 418.5 418.5 418.7 418.9 419.1 419.3 419.3 419.5 419.7 419.9 420.1 420.1 420.3 ][ 189.4 189.2 189.0 188.9 188.8 + 188.6 188.4 188.2 188.1 188.0 187.8 187.6 187.4 ]plong + 420.9 187.0 420.7 187.2 420.5 187.3 420.3 187.4 4 pls + s[ 488.8 488.6 488.4 488.4 488.2 488.0 487.8 487.6 487.6 487.4 487.2 487.0 486.8 486.8 486.6 486.4 ][ 189.4 189.3 + 189.2 188.9 188.8 188.6 188.5 188.4 188.1 188.0 187.8 187.7 187.6 187.3 187.2 187.0 ]plong + 536.1 189.0 535.9 189.2 535.7 189.3 535.5 189.4 4 pls + s[ 536.1 536.3 536.3 536.5 536.7 536.9 537.1 537.1 537.3 537.5 537.7 537.9 537.9 ][ 189.0 188.9 188.6 188.5 188.4 + 188.2 188.1 187.8 187.7 187.6 187.4 187.3 187.0 ]plong + s[ 256.8 256.6 256.4 256.2 256.0 256.0 255.8 255.6 255.4 255.2 255.2 255.0 254.8 254.6 254.4 254.4 ][ 191.9 191.7 + 191.6 191.4 191.3 191.0 190.9 190.8 190.6 190.5 190.2 190.1 190.0 189.8 189.7 189.4 ]plong + s[ 298.6 298.8 299.0 299.0 299.2 299.4 299.6 299.8 299.8 300.0 300.2 300.4 300.6 300.6 300.8 ][ 191.9 191.7 191.6 + 191.3 191.2 191.0 190.9 190.8 190.5 190.4 190.2 190.1 190.0 189.7 189.6 ]plong + 300.8 189.6 301.0 189.4 2 pls + s[ 373.2 373.0 373.0 372.8 372.6 372.4 372.2 372.2 372.0 371.8 371.6 371.4 ][ 191.2 191.0 190.8 190.6 190.5 190.4 + 190.2 190.0 189.8 189.7 189.6 189.4 ]plong + 373.2 191.2 373.4 191.3 373.6 191.4 373.8 191.6 373.8 191.9 5 pls + 416.6 191.2 416.4 191.3 416.2 191.4 416.0 191.6 416.0 191.9 5 pls + s[ 416.6 416.8 416.8 417.0 417.2 417.4 417.6 417.6 417.8 418.0 418.3 418.5 ][ 191.2 191.0 190.8 190.6 190.5 190.4 + 190.2 190.0 189.8 189.7 189.6 189.4 ]plong + 488.8 189.4 489.0 189.6 2 pls + s[ 491.3 491.0 490.8 490.8 490.6 490.4 490.2 490.0 490.0 489.8 489.6 489.4 489.2 489.2 489.0 ][ 191.9 191.7 191.6 + 191.3 191.2 191.0 190.9 190.8 190.5 190.4 190.2 190.1 190.0 189.7 189.6 ]plong + s[ 533.1 533.3 533.5 533.7 533.9 533.9 534.1 534.3 534.5 534.7 534.7 534.9 535.1 535.3 535.5 535.5 ][ 191.9 191.7 + 191.6 191.4 191.3 191.0 190.9 190.8 190.6 190.5 190.2 190.1 190.0 189.8 189.7 189.4 ]plong + 256.8 191.9 256.8 192.1 257.0 192.3 257.2 192.4 257.4 192.5 5 pls + s[ 259.2 259.0 258.8 258.6 258.4 258.4 258.2 258.0 257.8 257.6 257.6 257.4 ][ 194.3 194.1 194.0 193.9 193.7 193.5 + 193.3 193.2 193.1 192.9 192.7 192.5 ]plong + s[ 296.2 296.4 296.6 296.6 296.8 297.0 297.2 ][ 194.3 194.1 194.0 193.7 193.6 193.5 193.3 ]plong + s[ 297.2 297.4 297.4 297.6 297.8 298.0 298.2 298.2 298.4 298.6 ][ 193.3 193.2 192.9 192.8 192.7 192.5 192.4 192.1 + 192.0 191.9 ]plong + s[ 376.2 376.2 376.0 375.8 375.6 375.4 375.4 375.2 375.0 374.8 374.6 374.6 374.4 374.2 374.0 373.8 ][ 194.3 194.0 + 193.9 193.7 193.6 193.5 193.2 193.1 192.9 192.8 192.7 192.4 192.3 192.1 192.0 191.9 ]plong + s[ 413.6 413.6 413.8 414.0 414.2 414.4 414.4 414.6 414.8 415.0 415.2 415.2 415.4 415.6 415.8 416.0 ][ 194.3 194.0 + 193.9 193.7 193.6 193.5 193.2 193.1 192.9 192.8 192.7 192.4 192.3 192.1 192.0 191.9 ]plong + s[ 492.7 492.5 492.5 492.3 492.1 491.9 491.7 491.7 491.5 491.3 ][ 193.3 193.2 192.9 192.8 192.7 192.5 192.4 192.1 + 192.0 191.9 ]plong + s[ 493.7 493.5 493.3 493.3 493.1 492.9 492.7 ][ 194.3 194.1 194.0 193.7 193.6 193.5 193.3 ]plong + s[ 530.7 530.9 531.1 531.3 531.5 531.5 531.7 531.9 532.1 532.3 532.3 532.5 ][ 194.3 194.1 194.0 193.9 193.7 193.5 + 193.3 193.2 193.1 192.9 192.7 192.5 ]plong + 533.1 191.9 533.1 192.1 532.9 192.3 532.7 192.4 532.5 192.5 5 pls + s[ 261.0 260.8 260.8 260.6 260.4 260.2 260.0 260.0 259.8 259.6 259.4 259.2 259.2 ][ 196.3 196.1 195.9 195.7 195.6 + 195.5 195.3 195.1 194.9 194.8 194.7 194.5 194.3 ]plong + 261.0 196.3 261.2 196.4 261.4 196.5 261.6 196.7 4 pls + s[ 293.8 294.0 294.2 294.2 294.4 294.6 294.8 295.0 295.0 295.2 295.4 295.6 295.8 295.8 296.0 296.2 ][ 196.7 196.5 + 196.4 196.1 196.0 195.9 195.7 195.6 195.3 195.2 195.1 194.9 194.8 194.5 194.4 194.3 ]plong + 376.2 194.3 376.4 194.4 376.6 194.5 376.8 194.7 4 pls + s[ 378.6 378.6 378.4 378.2 378.0 377.8 377.8 377.6 377.4 377.2 377.0 377.0 376.8 ][ 196.7 196.4 196.3 196.1 196.0 + 195.9 195.6 195.5 195.3 195.2 195.1 194.8 194.7 ]plong + s[ 411.2 411.2 411.4 411.6 411.8 412.0 412.0 412.2 412.4 412.6 412.8 412.8 413.0 ][ 196.7 196.4 196.3 196.1 196.0 + 195.9 195.6 195.5 195.3 195.2 195.1 194.8 194.7 ]plong + 413.6 194.3 413.4 194.4 413.2 194.5 413.0 194.7 4 pls + s[ 496.1 495.9 495.7 495.7 495.5 495.3 495.1 494.9 494.9 494.7 494.5 494.3 494.1 494.1 493.9 493.7 ][ 196.7 196.5 + 196.4 196.1 196.0 195.9 195.7 195.6 195.3 195.2 195.1 194.9 194.8 194.5 194.4 194.3 ]plong + 528.9 196.3 528.7 196.4 528.5 196.5 528.3 196.7 4 pls + s[ 528.9 529.1 529.1 529.3 529.5 529.7 529.9 529.9 530.1 530.3 530.5 530.7 530.7 ][ 196.3 196.1 195.9 195.7 195.6 + 195.5 195.3 195.1 194.9 194.8 194.7 194.5 194.3 ]plong + s[ 264.0 263.8 263.6 263.4 263.2 263.2 263.0 262.8 262.6 262.4 262.4 262.2 262.0 261.8 261.6 261.6 ][ 199.1 199.0 + 198.8 198.7 198.6 198.3 198.2 198.0 197.9 197.8 197.5 197.3 197.2 197.1 196.9 196.7 ]plong + s[ 291.4 291.6 291.8 291.8 292.0 292.2 292.4 292.6 292.6 292.8 293.0 293.2 293.4 293.4 293.6 ][ 199.1 199.0 198.8 + 198.6 198.4 198.3 198.2 198.0 197.8 197.6 197.5 197.3 197.2 196.9 196.8 ]plong + 293.6 196.8 293.8 196.7 2 pls + s[ 380.4 380.2 380.2 380.0 379.8 379.6 379.4 379.4 379.2 379.0 378.8 378.6 ][ 198.4 198.3 198.0 197.9 197.8 197.6 + 197.5 197.2 197.1 196.9 196.8 196.7 ]plong + 380.4 198.4 380.6 198.6 380.8 198.7 381.0 198.8 381.0 199.1 5 pls + 409.4 198.4 409.2 198.6 409.0 198.7 408.8 198.8 408.8 199.1 5 pls + s[ 409.4 409.6 409.6 409.8 410.0 410.2 410.4 410.4 410.6 410.8 411.0 411.2 ][ 198.4 198.3 198.0 197.9 197.8 197.6 + 197.5 197.2 197.1 196.9 196.8 196.7 ]plong + 496.1 196.7 496.3 196.8 2 pls + s[ 498.5 498.3 498.1 498.1 497.9 497.7 497.5 497.3 497.3 497.1 496.9 496.7 496.5 496.5 496.3 ][ 199.1 199.0 198.8 + 198.6 198.4 198.3 198.2 198.0 197.8 197.6 197.5 197.3 197.2 196.9 196.8 ]plong + s[ 525.8 526.0 526.2 526.4 526.6 526.6 526.8 527.0 527.2 527.4 527.4 527.7 527.9 528.1 528.3 528.3 ][ 199.1 199.0 + 198.8 198.7 198.6 198.3 198.2 198.0 197.9 197.8 197.5 197.3 197.2 197.1 196.9 196.7 ]plong + 264.0 199.1 264.0 199.4 264.2 199.5 264.4 199.6 264.6 199.8 5 pls + s[ 266.4 266.2 266.0 265.8 265.6 265.6 265.4 265.2 265.0 264.8 264.8 264.6 ][ 201.5 201.4 201.2 201.1 201.0 200.7 + 200.6 200.4 200.3 200.2 199.9 199.8 ]plong + s[ 288.9 289.1 289.3 289.3 289.5 289.7 289.9 ][ 201.5 201.4 201.2 201.0 200.8 200.7 200.6 ]plong + s[ 289.9 290.1 290.1 290.3 290.6 290.8 291.0 291.0 291.2 291.4 ][ 200.6 200.4 200.2 200.0 199.9 199.8 199.6 199.4 + 199.2 199.1 ]plong + s[ 383.5 383.5 383.3 383.1 382.9 382.7 382.7 382.5 382.3 382.1 381.9 381.9 381.7 381.4 381.2 381.0 ][ 201.5 201.2 + 201.1 201.0 200.8 200.7 200.4 200.3 200.2 200.0 199.9 199.6 199.5 199.4 199.2 199.1 ]plong + s[ 406.4 406.4 406.6 406.8 407.0 407.2 407.2 407.4 407.6 407.8 408.0 408.0 408.2 408.4 408.6 408.8 ][ 201.5 201.2 + 201.1 201.0 200.8 200.7 200.4 200.3 200.2 200.0 199.9 199.6 199.5 199.4 199.2 199.1 ]plong + s[ 499.9 499.7 499.7 499.5 499.3 499.1 498.9 498.9 498.7 498.5 ][ 200.6 200.4 200.2 200.0 199.9 199.8 199.6 199.4 + 199.2 199.1 ]plong + s[ 500.9 500.7 500.5 500.5 500.3 500.1 499.9 ][ 201.5 201.4 201.2 201.0 200.8 200.7 200.6 ]plong + s[ 523.4 523.6 523.8 524.0 524.2 524.2 524.4 524.6 524.8 525.0 525.0 525.2 ][ 201.5 201.4 201.2 201.1 201.0 200.7 + 200.6 200.4 200.3 200.2 199.9 199.8 ]plong + 525.8 199.1 525.8 199.4 525.6 199.5 525.4 199.6 525.2 199.8 5 pls + s[ 268.2 268.0 268.0 267.8 267.6 267.4 267.2 267.2 267.0 266.8 266.6 266.4 266.4 ][ 203.5 203.4 203.1 203.0 202.8 + 202.7 202.6 202.3 202.2 202.0 201.9 201.8 201.5 ]plong + 268.2 203.5 268.4 203.6 268.6 203.8 268.8 203.9 4 pls + s[ 286.5 286.7 286.9 286.9 287.1 287.3 287.5 287.7 287.7 287.9 288.1 288.3 288.5 288.5 288.7 288.9 ][ 203.9 203.8 + 203.6 203.4 203.2 203.1 203.0 202.8 202.6 202.4 202.3 202.2 202.0 201.8 201.6 201.5 ]plong + 383.5 201.5 383.7 201.6 383.9 201.8 384.1 201.9 4 pls + s[ 385.9 385.9 385.7 385.5 385.3 385.1 385.1 384.9 384.7 384.5 384.3 384.3 384.1 ][ 203.9 203.6 203.5 203.4 203.2 + 203.1 202.8 202.7 202.6 202.4 202.3 202.0 201.9 ]plong + s[ 404.0 404.0 404.2 404.4 404.6 404.8 404.8 405.0 405.2 405.4 405.6 405.6 405.8 ][ 203.9 203.6 203.5 203.4 203.2 + 203.1 202.8 202.7 202.6 202.4 202.3 202.0 201.9 ]plong + 406.4 201.5 406.2 201.6 406.0 201.8 405.8 201.9 4 pls + s[ 503.3 503.1 502.9 502.9 502.7 502.5 502.3 502.1 502.1 501.9 501.7 501.5 501.3 501.3 501.1 500.9 ][ 203.9 203.8 + 203.6 203.4 203.2 203.1 203.0 202.8 202.6 202.4 202.3 202.2 202.0 201.8 201.6 201.5 ]plong + 521.6 203.5 521.4 203.6 521.2 203.8 521.0 203.9 4 pls + s[ 521.6 521.8 521.8 522.0 522.2 522.4 522.6 522.6 522.8 523.0 523.2 523.4 523.4 ][ 203.5 203.4 203.1 203.0 202.8 + 202.7 202.6 202.3 202.2 202.0 201.9 201.8 201.5 ]plong + s[ 271.2 271.0 270.8 270.6 270.4 270.4 270.2 270.0 269.8 269.6 269.6 269.4 269.2 269.0 268.8 268.8 ][ 206.3 206.2 + 206.1 205.9 205.8 205.5 205.4 205.3 205.1 205.0 204.7 204.6 204.5 204.3 204.2 203.9 ]plong + s[ 284.1 284.3 284.5 284.5 284.7 284.9 285.1 285.3 285.3 285.5 285.7 285.9 286.1 286.1 286.3 ][ 206.3 206.2 206.1 + 205.8 205.7 205.5 205.4 205.3 205.0 204.9 204.7 204.6 204.5 204.2 204.1 ]plong + 286.3 204.1 286.5 203.9 2 pls + s[ 387.7 387.5 387.5 387.3 387.1 386.9 386.7 386.7 386.5 386.3 386.1 385.9 ][ 205.7 205.5 205.3 205.1 205.0 204.9 + 204.7 204.5 204.3 204.2 204.1 203.9 ]plong + 387.7 205.7 387.9 205.8 388.1 205.9 388.3 206.1 388.3 206.3 5 pls + 402.2 205.7 402.0 205.8 401.8 205.9 401.6 206.1 401.6 206.3 5 pls + s[ 402.2 402.4 402.4 402.6 402.8 403.0 403.2 403.2 403.4 403.6 403.8 404.0 ][ 205.7 205.5 205.3 205.1 205.0 204.9 + 204.7 204.5 204.3 204.2 204.1 203.9 ]plong + 503.3 203.9 503.5 204.1 2 pls + s[ 505.7 505.5 505.3 505.3 505.1 504.9 504.7 504.5 504.5 504.3 504.1 503.9 503.7 503.7 503.5 ][ 206.3 206.2 206.1 + 205.8 205.7 205.5 205.4 205.3 205.0 204.9 204.7 204.6 204.5 204.2 204.1 ]plong + s[ 518.6 518.8 519.0 519.2 519.4 519.4 519.6 519.8 520.0 520.2 520.2 520.4 520.6 520.8 521.0 521.0 ][ 206.3 206.2 + 206.1 205.9 205.8 205.5 205.4 205.3 205.1 205.0 204.7 204.6 204.5 204.3 204.2 203.9 ]plong + 271.2 206.3 271.2 206.6 271.4 206.7 271.6 206.9 271.8 207.0 5 pls + s[ 273.7 273.5 273.3 273.1 272.9 272.9 272.7 272.5 272.3 272.0 272.0 271.8 ][ 208.7 208.6 208.5 208.3 208.2 207.9 + 207.8 207.7 207.5 207.4 207.1 207.0 ]plong + s[ 281.7 281.9 282.1 282.1 282.3 282.5 282.7 ][ 208.7 208.6 208.5 208.2 208.1 207.9 207.8 ]plong + s[ 282.7 282.9 282.9 283.1 283.3 283.5 283.7 283.7 283.9 284.1 ][ 207.8 207.7 207.4 207.3 207.1 207.0 206.9 206.6 + 206.5 206.3 ]plong + s[ 390.7 390.7 390.5 390.3 390.1 389.9 389.9 389.7 389.5 389.3 389.1 389.1 388.9 388.7 388.5 388.3 ][ 208.7 208.5 + 208.3 208.2 208.1 207.9 207.7 207.5 207.4 207.3 207.1 206.9 206.7 206.6 206.5 206.3 ]plong + s[ 399.1 399.1 399.3 399.5 399.7 400.0 400.0 400.2 400.4 400.6 400.8 400.8 401.0 401.2 401.4 401.6 ][ 208.7 208.5 + 208.3 208.2 208.1 207.9 207.7 207.5 207.4 207.3 207.1 206.9 206.7 206.6 206.5 206.3 ]plong + s[ 507.1 506.9 506.9 506.7 506.5 506.3 506.1 506.1 505.9 505.7 ][ 207.8 207.7 207.4 207.3 207.1 207.0 206.9 206.6 + 206.5 206.3 ]plong + s[ 508.1 507.9 507.7 507.7 507.5 507.3 507.1 ][ 208.7 208.6 208.5 208.2 208.1 207.9 207.8 ]plong + s[ 516.2 516.4 516.6 516.8 517.0 517.0 517.2 517.4 517.6 517.8 517.8 518.0 ][ 208.7 208.6 208.5 208.3 208.2 207.9 + 207.8 207.7 207.5 207.4 207.1 207.0 ]plong + 518.6 206.3 518.6 206.6 518.4 206.7 518.2 206.9 518.0 207.0 5 pls + s[ 275.5 275.3 275.3 275.1 274.9 274.7 274.5 274.5 274.3 274.1 273.9 273.7 273.7 ][ 210.8 210.6 210.4 210.2 210.1 + 210.0 209.8 209.5 209.4 209.3 209.1 209.0 208.7 ]plong + 275.5 210.8 275.7 210.9 275.9 211.0 276.1 211.2 4 pls + s[ 279.3 279.5 279.7 279.7 279.9 280.1 280.3 280.5 280.5 280.7 280.9 281.1 281.3 281.3 281.5 281.7 ][ 211.2 211.0 + 210.9 210.6 210.5 210.4 210.2 210.1 209.8 209.7 209.5 209.4 209.3 209.0 208.9 208.7 ]plong + 390.7 208.7 390.9 208.9 391.1 209.0 391.3 209.1 4 pls + s[ 393.1 393.1 392.9 392.7 392.5 392.3 392.3 392.1 391.9 391.7 391.5 391.5 391.3 ][ 211.2 210.9 210.8 210.6 210.5 + 210.4 210.1 210.0 209.8 209.7 209.5 209.3 209.1 ]plong + s[ 396.7 396.7 396.9 397.1 397.3 397.5 397.5 397.7 397.9 398.1 398.3 398.3 398.5 ][ 211.2 210.9 210.8 210.6 210.5 + 210.4 210.1 210.0 209.8 209.7 209.5 209.3 209.1 ]plong + 399.1 208.7 398.9 208.9 398.7 209.0 398.5 209.1 4 pls + s[ 510.6 510.4 510.2 510.2 510.0 509.8 509.6 509.4 509.4 509.1 508.9 508.7 508.5 508.5 508.3 508.1 ][ 211.2 211.0 + 210.9 210.6 210.5 210.4 210.2 210.1 209.8 209.7 209.5 209.4 209.3 209.0 208.9 208.7 ]plong + 514.4 210.8 514.2 210.9 514.0 211.0 513.8 211.2 4 pls + s[ 514.4 514.6 514.6 514.8 515.0 515.2 515.4 515.4 515.6 515.8 516.0 516.2 516.2 ][ 210.8 210.6 210.4 210.2 210.1 + 210.0 209.8 209.5 209.4 209.3 209.1 209.0 208.7 ]plong + s[ 279.1 278.9 278.9 278.7 278.5 278.3 278.1 278.1 277.9 277.5 277.3 277.1 276.9 276.9 276.7 276.5 276.3 276.1 + 276.1 ][ 211.3 211.4 211.7 211.8 212.0 212.1 212.2 212.5 212.6 212.6 212.5 212.4 212.2 212.0 211.8 211.7 211.6 + 211.4 211.2 ]plong + 279.1 211.3 279.3 211.2 2 pls + s[ 394.9 394.7 394.7 394.5 394.3 394.1 393.9 393.9 393.7 393.5 393.3 393.1 ][ 212.9 212.8 212.5 212.4 212.2 212.1 + 212.0 211.7 211.6 211.4 211.3 211.2 ]plong + s[ 394.9 395.1 395.1 395.3 395.5 395.7 395.9 395.9 396.1 396.3 396.5 396.7 ][ 212.9 212.8 212.5 212.4 212.2 212.1 + 212.0 211.7 211.6 211.4 211.3 211.2 ]plong + 510.6 211.2 510.8 211.3 2 pls + s[ 510.8 511.0 511.0 511.2 511.4 511.6 511.8 511.8 512.0 512.4 512.6 512.8 513.0 513.0 513.2 513.4 513.6 513.8 + 513.8 ][ 211.3 211.4 211.7 211.8 212.0 212.1 212.2 212.5 212.6 212.6 212.5 212.4 212.2 212.0 211.8 211.7 211.6 + 211.4 211.2 ]plong + s[ 276.1 276.1 276.3 276.5 276.7 276.9 276.9 277.1 277.3 277.5 277.9 278.1 278.1 278.3 278.5 278.7 278.9 278.9 + 279.1 ][ 331.8 331.6 331.4 331.3 331.1 331.0 330.7 330.6 330.5 330.3 330.3 330.5 330.7 330.9 331.0 331.1 331.3 + 331.6 331.7 ]plong + 279.1 331.7 279.3 331.8 2 pls + s[ 393.1 393.3 393.5 393.7 393.9 393.9 394.1 394.3 394.5 394.7 394.7 394.9 ][ 331.8 331.7 331.6 331.4 331.3 331.0 + 330.9 330.7 330.6 330.5 330.2 330.1 ]plong + s[ 396.7 396.5 396.3 396.1 395.9 395.9 395.7 395.5 395.3 395.1 395.1 394.9 ][ 331.8 331.7 331.6 331.4 331.3 331.0 + 330.9 330.7 330.6 330.5 330.2 330.1 ]plong + 510.8 331.7 510.6 331.8 2 pls + s[ 513.8 513.8 513.6 513.4 513.2 513.0 513.0 512.8 512.6 512.4 512.0 511.8 511.8 511.6 511.4 511.2 511.0 511.0 + 510.8 ][ 331.8 331.6 331.4 331.3 331.1 331.0 330.7 330.6 330.5 330.3 330.3 330.5 330.7 330.9 331.0 331.1 331.3 + 331.6 331.7 ]plong + s[ 273.7 273.7 273.9 274.1 274.3 274.5 274.5 274.7 274.9 275.1 275.3 275.3 275.5 ][ 334.2 334.0 333.8 333.7 333.6 + 333.4 333.2 333.0 332.9 332.8 332.6 332.4 332.2 ]plong + 276.1 331.8 275.9 332.0 275.7 332.1 275.5 332.2 4 pls + s[ 279.3 279.5 279.7 279.7 279.9 280.1 280.3 280.5 280.5 280.7 280.9 281.1 281.3 281.3 281.5 281.7 ][ 331.8 332.0 + 332.1 332.4 332.5 332.6 332.8 332.9 333.2 333.3 333.4 333.6 333.7 334.0 334.1 334.2 ]plong + 391.3 333.8 391.1 334.0 390.9 334.1 390.7 334.2 4 pls + s[ 391.3 391.5 391.5 391.7 391.9 392.1 392.3 392.3 392.5 392.7 392.9 393.1 393.1 ][ 333.8 333.7 333.4 333.3 333.2 + 333.0 332.9 332.6 332.5 332.4 332.2 332.1 331.8 ]plong + s[ 398.5 398.3 398.3 398.1 397.9 397.7 397.5 397.5 397.3 397.1 396.9 396.7 396.7 ][ 333.8 333.7 333.4 333.3 333.2 + 333.0 332.9 332.6 332.5 332.4 332.2 332.1 331.8 ]plong + 398.5 333.8 398.7 334.0 398.9 334.1 399.1 334.2 4 pls + s[ 508.1 508.3 508.5 508.5 508.7 508.9 509.1 509.4 509.4 509.6 509.8 510.0 510.2 510.2 510.4 510.6 ][ 334.2 334.1 + 334.0 333.7 333.6 333.4 333.3 333.2 332.9 332.8 332.6 332.5 332.4 332.1 332.0 331.8 ]plong + 513.8 331.8 514.0 332.0 514.2 332.1 514.4 332.2 4 pls + s[ 516.2 516.2 516.0 515.8 515.6 515.4 515.4 515.2 515.0 514.8 514.6 514.6 514.4 ][ 334.2 334.0 333.8 333.7 333.6 + 333.4 333.2 333.0 332.9 332.8 332.6 332.4 332.2 ]plong + 271.8 336.0 271.6 336.1 271.4 336.2 271.2 336.4 271.2 336.6 5 pls + s[ 271.8 272.0 272.0 272.3 272.5 272.7 272.9 272.9 273.1 273.3 273.5 273.7 ][ 336.0 335.8 335.6 335.4 335.3 335.2 + 335.0 334.8 334.6 334.5 334.4 334.2 ]plong + s[ 282.7 282.5 282.3 282.1 282.1 281.9 281.7 ][ 335.2 335.0 334.9 334.8 334.5 334.4 334.2 ]plong + s[ 284.1 283.9 283.7 283.7 283.5 283.3 283.1 282.9 282.9 282.7 ][ 336.6 336.5 336.4 336.1 336.0 335.8 335.7 335.6 + 335.3 335.2 ]plong + s[ 388.3 388.5 388.7 388.9 389.1 389.1 389.3 389.5 389.7 389.9 389.9 390.1 390.3 390.5 390.7 390.7 ][ 336.6 336.5 + 336.4 336.2 336.1 335.8 335.7 335.6 335.4 335.3 335.0 334.9 334.8 334.6 334.5 334.2 ]plong + s[ 401.6 401.4 401.2 401.0 400.8 400.8 400.6 400.4 400.2 400.0 400.0 399.7 399.5 399.3 399.1 399.1 ][ 336.6 336.5 + 336.4 336.2 336.1 335.8 335.7 335.6 335.4 335.3 335.0 334.9 334.8 334.6 334.5 334.2 ]plong + s[ 505.7 505.9 506.1 506.1 506.3 506.5 506.7 506.9 506.9 507.1 ][ 336.6 336.5 336.4 336.1 336.0 335.8 335.7 335.6 + 335.3 335.2 ]plong + s[ 507.1 507.3 507.5 507.7 507.7 507.9 508.1 ][ 335.2 335.0 334.9 334.8 334.5 334.4 334.2 ]plong + s[ 518.0 517.8 517.8 517.6 517.4 517.2 517.0 517.0 516.8 516.6 516.4 516.2 ][ 336.0 335.8 335.6 335.4 335.3 335.2 + 335.0 334.8 334.6 334.5 334.4 334.2 ]plong + 518.0 336.0 518.2 336.1 518.4 336.2 518.6 336.4 518.6 336.6 5 pls + s[ 268.8 268.8 269.0 269.2 269.4 269.6 269.6 269.8 270.0 270.2 270.4 270.4 270.6 270.8 271.0 271.2 ][ 339.1 338.8 + 338.7 338.5 338.4 338.3 338.0 337.9 337.7 337.6 337.4 337.2 337.0 336.9 336.8 336.6 ]plong + s[ 286.3 286.1 286.1 285.9 285.7 285.5 285.3 285.3 285.1 284.9 284.7 284.5 284.5 284.3 284.1 ][ 338.9 338.8 338.5 + 338.4 338.3 338.1 338.0 337.7 337.6 337.4 337.3 337.2 336.9 336.8 336.6 ]plong + 286.3 338.9 286.5 339.1 2 pls + s[ 385.9 386.1 386.3 386.5 386.7 386.7 386.9 387.1 387.3 387.5 387.5 387.7 ][ 339.1 338.9 338.8 338.7 338.5 338.3 + 338.1 338.0 337.9 337.7 337.4 337.3 ]plong + 388.3 336.6 388.3 336.9 388.1 337.0 387.9 337.2 387.7 337.3 5 pls + 401.6 336.6 401.6 336.9 401.8 337.0 402.0 337.2 402.2 337.3 5 pls + s[ 404.0 403.8 403.6 403.4 403.2 403.2 403.0 402.8 402.6 402.4 402.4 402.2 ][ 339.1 338.9 338.8 338.7 338.5 338.3 + 338.1 338.0 337.9 337.7 337.4 337.3 ]plong + 503.5 338.9 503.3 339.1 2 pls + s[ 503.5 503.7 503.7 503.9 504.1 504.3 504.5 504.5 504.7 504.9 505.1 505.3 505.3 505.5 505.7 ][ 338.9 338.8 338.5 + 338.4 338.3 338.1 338.0 337.7 337.6 337.4 337.3 337.2 336.9 336.8 336.6 ]plong + s[ 521.0 521.0 520.8 520.6 520.4 520.2 520.2 520.0 519.8 519.6 519.4 519.4 519.2 519.0 518.8 518.6 ][ 339.1 338.8 + 338.7 338.5 338.4 338.3 338.0 337.9 337.7 337.6 337.4 337.2 337.0 336.9 336.8 336.6 ]plong + s[ 266.4 266.4 266.6 266.8 267.0 267.2 267.2 267.4 267.6 267.8 268.0 268.0 268.2 ][ 341.5 341.2 341.1 340.9 340.8 + 340.7 340.4 340.3 340.1 340.0 339.9 339.6 339.5 ]plong + 268.8 339.1 268.6 339.2 268.4 339.3 268.2 339.5 4 pls + s[ 286.5 286.7 286.9 286.9 287.1 287.3 287.5 287.7 287.7 287.9 288.1 288.3 288.5 288.5 288.7 288.9 ][ 339.1 339.2 + 339.3 339.6 339.7 339.9 340.0 340.1 340.4 340.5 340.7 340.8 340.9 341.2 341.3 341.5 ]plong + 384.1 341.1 383.9 341.2 383.7 341.3 383.5 341.5 4 pls + s[ 384.1 384.3 384.3 384.5 384.7 384.9 385.1 385.1 385.3 385.5 385.7 385.9 385.9 ][ 341.1 340.9 340.7 340.5 340.4 + 340.3 340.1 339.9 339.7 339.6 339.5 339.3 339.1 ]plong + s[ 405.8 405.6 405.6 405.4 405.2 405.0 404.8 404.8 404.6 404.4 404.2 404.0 404.0 ][ 341.1 340.9 340.7 340.5 340.4 + 340.3 340.1 339.9 339.7 339.6 339.5 339.3 339.1 ]plong + 405.8 341.1 406.0 341.2 406.2 341.3 406.4 341.5 4 pls + s[ 500.9 501.1 501.3 501.3 501.5 501.7 501.9 502.1 502.1 502.3 502.5 502.7 502.9 502.9 503.1 503.3 ][ 341.5 341.3 + 341.2 340.9 340.8 340.7 340.5 340.4 340.1 340.0 339.9 339.7 339.6 339.3 339.2 339.1 ]plong + 521.0 339.1 521.2 339.2 521.4 339.3 521.6 339.5 4 pls + s[ 523.4 523.4 523.2 523.0 522.8 522.6 522.6 522.4 522.2 522.0 521.8 521.8 521.6 ][ 341.5 341.2 341.1 340.9 340.8 + 340.7 340.4 340.3 340.1 340.0 339.9 339.6 339.5 ]plong + 264.6 343.2 264.4 343.3 264.2 343.5 264.0 343.6 264.0 343.9 5 pls + s[ 264.6 264.8 264.8 265.0 265.2 265.4 265.6 265.6 265.8 266.0 266.2 266.4 ][ 343.2 343.1 342.8 342.7 342.5 342.4 + 342.3 342.0 341.9 341.7 341.6 341.5 ]plong + s[ 289.9 289.7 289.5 289.3 289.3 289.1 288.9 ][ 342.4 342.3 342.1 342.0 341.7 341.6 341.5 ]plong + s[ 291.4 291.2 291.0 291.0 290.8 290.6 290.3 290.1 290.1 289.9 ][ 343.9 343.8 343.6 343.3 343.2 343.1 342.9 342.8 + 342.5 342.4 ]plong + s[ 381.0 381.2 381.4 381.7 381.9 381.9 382.1 382.3 382.5 382.7 382.7 382.9 383.1 383.3 383.5 383.5 ][ 343.9 343.8 + 343.6 343.5 343.3 343.1 342.9 342.8 342.7 342.5 342.3 342.1 342.0 341.9 341.7 341.5 ]plong + s[ 408.8 408.6 408.4 408.2 408.0 408.0 407.8 407.6 407.4 407.2 407.2 407.0 406.8 406.6 406.4 406.4 ][ 343.9 343.8 + 343.6 343.5 343.3 343.1 342.9 342.8 342.7 342.5 342.3 342.1 342.0 341.9 341.7 341.5 ]plong + s[ 498.5 498.7 498.9 498.9 499.1 499.3 499.5 499.7 499.7 499.9 ][ 343.9 343.8 343.6 343.3 343.2 343.1 342.9 342.8 + 342.5 342.4 ]plong + s[ 499.9 500.1 500.3 500.5 500.5 500.7 500.9 ][ 342.4 342.3 342.1 342.0 341.7 341.6 341.5 ]plong + s[ 525.2 525.0 525.0 524.8 524.6 524.4 524.2 524.2 524.0 523.8 523.6 523.4 ][ 343.2 343.1 342.8 342.7 342.5 342.4 + 342.3 342.0 341.9 341.7 341.6 341.5 ]plong + 525.2 343.2 525.4 343.3 525.6 343.5 525.8 343.6 525.8 343.9 5 pls + s[ 261.6 261.6 261.8 262.0 262.2 262.4 262.4 262.6 262.8 263.0 263.2 263.2 263.4 263.6 263.8 264.0 ][ 346.3 346.0 + 345.9 345.8 345.6 345.5 345.2 345.1 345.0 344.8 344.7 344.4 344.3 344.2 344.0 343.9 ]plong + s[ 293.6 293.4 293.4 293.2 293.0 292.8 292.6 292.6 292.4 292.2 292.0 291.8 291.8 291.6 291.4 ][ 346.2 346.0 345.8 + 345.6 345.5 345.4 345.2 345.0 344.8 344.7 344.6 344.4 344.2 344.0 343.9 ]plong + 293.6 346.2 293.8 346.3 2 pls + s[ 378.6 378.8 379.0 379.2 379.4 379.4 379.6 379.8 380.0 380.2 380.2 380.4 ][ 346.3 346.2 346.0 345.9 345.8 345.5 + 345.4 345.2 345.1 345.0 344.7 344.6 ]plong + 381.0 343.9 381.0 344.2 380.8 344.3 380.6 344.4 380.4 344.6 5 pls + 408.8 343.9 408.8 344.2 409.0 344.3 409.2 344.4 409.4 344.6 5 pls + s[ 411.2 411.0 410.8 410.6 410.4 410.4 410.2 410.0 409.8 409.6 409.6 409.4 ][ 346.3 346.2 346.0 345.9 345.8 345.5 + 345.4 345.2 345.1 345.0 344.7 344.6 ]plong + 496.3 346.2 496.1 346.3 2 pls + s[ 496.3 496.5 496.5 496.7 496.9 497.1 497.3 497.3 497.5 497.7 497.9 498.1 498.1 498.3 498.5 ][ 346.2 346.0 345.8 + 345.6 345.5 345.4 345.2 345.0 344.8 344.7 344.6 344.4 344.2 344.0 343.9 ]plong + s[ 528.3 528.3 528.1 527.9 527.7 527.4 527.4 527.2 527.0 526.8 526.6 526.6 526.4 526.2 526.0 525.8 ][ 346.3 346.0 + 345.9 345.8 345.6 345.5 345.2 345.1 345.0 344.8 344.7 344.4 344.3 344.2 344.0 343.9 ]plong + s[ 259.2 259.2 259.4 259.6 259.8 260.0 260.0 260.2 260.4 260.6 260.8 260.8 261.0 ][ 348.7 348.4 348.3 348.2 348.0 + 347.9 347.6 347.5 347.4 347.2 347.1 346.8 346.7 ]plong + 261.6 346.3 261.4 346.4 261.2 346.6 261.0 346.7 4 pls + s[ 293.8 294.0 294.2 294.2 294.4 294.6 294.8 295.0 295.0 295.2 295.4 295.6 295.8 295.8 296.0 296.2 ][ 346.3 346.4 + 346.6 346.8 347.0 347.1 347.2 347.4 347.6 347.8 347.9 348.0 348.2 348.4 348.6 348.7 ]plong + 376.8 348.3 376.6 348.4 376.4 348.6 376.2 348.7 4 pls + s[ 376.8 377.0 377.0 377.2 377.4 377.6 377.8 377.8 378.0 378.2 378.4 378.6 378.6 ][ 348.3 348.2 347.9 347.8 347.6 + 347.5 347.4 347.1 347.0 346.8 346.7 346.6 346.3 ]plong + s[ 413.0 412.8 412.8 412.6 412.4 412.2 412.0 412.0 411.8 411.6 411.4 411.2 411.2 ][ 348.3 348.2 347.9 347.8 347.6 + 347.5 347.4 347.1 347.0 346.8 346.7 346.6 346.3 ]plong + 413.0 348.3 413.2 348.4 413.4 348.6 413.6 348.7 4 pls + s[ 493.7 493.9 494.1 494.1 494.3 494.5 494.7 494.9 494.9 495.1 495.3 495.5 495.7 495.7 495.9 496.1 ][ 348.7 348.6 + 348.4 348.2 348.0 347.9 347.8 347.6 347.4 347.2 347.1 347.0 346.8 346.6 346.4 346.3 ]plong + 528.3 346.3 528.5 346.4 528.7 346.6 528.9 346.7 4 pls + s[ 530.7 530.7 530.5 530.3 530.1 529.9 529.9 529.7 529.5 529.3 529.1 529.1 528.9 ][ 348.7 348.4 348.3 348.2 348.0 + 347.9 347.6 347.5 347.4 347.2 347.1 346.8 346.7 ]plong + 257.4 350.5 257.2 350.6 257.0 350.7 256.8 350.9 256.8 351.1 5 pls + s[ 257.4 257.6 257.6 257.8 258.0 258.2 258.4 258.4 258.6 258.8 259.0 259.2 ][ 350.5 350.3 350.1 349.9 349.8 349.7 + 349.5 349.2 349.1 349.0 348.8 348.7 ]plong + s[ 297.2 297.0 296.8 296.6 296.6 296.4 296.2 ][ 349.7 349.5 349.4 349.2 349.0 348.8 348.7 ]plong + s[ 298.6 298.4 298.2 298.2 298.0 297.8 297.6 297.4 297.4 297.2 ][ 351.1 351.0 350.9 350.6 350.5 350.3 350.2 350.1 + 349.8 349.7 ]plong + s[ 373.8 374.0 374.2 374.4 374.6 374.6 374.8 375.0 375.2 375.4 375.4 375.6 375.8 376.0 376.2 376.2 ][ 351.1 351.0 + 350.9 350.7 350.6 350.3 350.2 350.1 349.9 349.8 349.5 349.4 349.2 349.1 349.0 348.7 ]plong + s[ 416.0 415.8 415.6 415.4 415.2 415.2 415.0 414.8 414.6 414.4 414.4 414.2 414.0 413.8 413.6 413.6 ][ 351.1 351.0 + 350.9 350.7 350.6 350.3 350.2 350.1 349.9 349.8 349.5 349.4 349.2 349.1 349.0 348.7 ]plong + s[ 491.3 491.5 491.7 491.7 491.9 492.1 492.3 492.5 492.5 492.7 ][ 351.1 351.0 350.9 350.6 350.5 350.3 350.2 350.1 + 349.8 349.7 ]plong + s[ 492.7 492.9 493.1 493.3 493.3 493.5 493.7 ][ 349.7 349.5 349.4 349.2 349.0 348.8 348.7 ]plong + s[ 532.5 532.3 532.3 532.1 531.9 531.7 531.5 531.5 531.3 531.1 530.9 530.7 ][ 350.5 350.3 350.1 349.9 349.8 349.7 + 349.5 349.2 349.1 349.0 348.8 348.7 ]plong + 532.5 350.5 532.7 350.6 532.9 350.7 533.1 350.9 533.1 351.1 5 pls + s[ 254.4 254.4 254.6 254.8 255.0 255.2 255.2 255.4 255.6 255.8 256.0 256.0 256.2 256.4 256.6 256.8 ][ 353.5 353.3 + 353.1 353.0 352.9 352.7 352.5 352.3 352.2 352.1 351.9 351.7 351.5 351.4 351.3 351.1 ]plong + s[ 300.8 300.6 300.6 300.4 300.2 300.0 299.8 299.8 299.6 299.4 299.2 299.0 299.0 298.8 298.6 ][ 353.4 353.3 353.0 + 352.9 352.7 352.6 352.5 352.2 352.1 351.9 351.8 351.7 351.4 351.3 351.1 ]plong + 300.8 353.4 301.0 353.5 2 pls + s[ 371.4 371.6 371.8 372.0 372.2 372.2 372.4 372.6 372.8 373.0 373.0 373.2 ][ 353.5 353.4 353.3 353.1 353.0 352.7 + 352.6 352.5 352.3 352.2 351.9 351.8 ]plong + 373.8 351.1 373.8 351.4 373.6 351.5 373.4 351.7 373.2 351.8 5 pls + 416.0 351.1 416.0 351.4 416.2 351.5 416.4 351.7 416.6 351.8 5 pls + s[ 418.5 418.3 418.0 417.8 417.6 417.6 417.4 417.2 417.0 416.8 416.8 416.6 ][ 353.5 353.4 353.3 353.1 353.0 352.7 + 352.6 352.5 352.3 352.2 351.9 351.8 ]plong + 489.0 353.4 488.8 353.5 2 pls + s[ 489.0 489.2 489.2 489.4 489.6 489.8 490.0 490.0 490.2 490.4 490.6 490.8 490.8 491.0 491.3 ][ 353.4 353.3 353.0 + 352.9 352.7 352.6 352.5 352.2 352.1 351.9 351.8 351.7 351.4 351.3 351.1 ]plong + s[ 535.5 535.5 535.3 535.1 534.9 534.7 534.7 534.5 534.3 534.1 533.9 533.9 533.7 533.5 533.3 533.1 ][ 353.5 353.3 + 353.1 353.0 352.9 352.7 352.5 352.3 352.2 352.1 351.9 351.7 351.5 351.4 351.3 351.1 ]plong + s[ 251.9 251.9 252.1 252.3 252.5 252.7 252.7 252.9 253.1 253.3 253.5 253.5 253.7 ][ 356.0 355.7 355.5 355.4 355.3 + 355.1 354.9 354.7 354.6 354.5 354.3 354.1 353.9 ]plong + 254.4 353.5 254.2 353.7 254.0 353.8 253.7 353.9 4 pls + s[ 301.0 301.2 301.4 301.4 301.6 301.8 302.0 302.2 302.2 302.4 302.6 302.8 303.0 303.0 303.2 303.4 ][ 353.5 353.7 + 353.8 354.1 354.2 354.3 354.5 354.6 354.9 355.0 355.1 355.3 355.4 355.7 355.8 356.0 ]plong + 369.6 355.5 369.4 355.7 369.2 355.8 369.0 356.0 4 pls + s[ 369.6 369.8 369.8 370.0 370.2 370.4 370.6 370.6 370.8 371.0 371.2 371.4 371.4 ][ 355.5 355.4 355.1 355.0 354.9 + 354.7 354.6 354.3 354.2 354.1 353.9 353.8 353.5 ]plong + s[ 420.3 420.1 420.1 419.9 419.7 419.5 419.3 419.3 419.1 418.9 418.7 418.5 418.5 ][ 355.5 355.4 355.1 355.0 354.9 + 354.7 354.6 354.3 354.2 354.1 353.9 353.8 353.5 ]plong + 420.3 355.5 420.5 355.7 420.7 355.8 420.9 356.0 4 pls + s[ 486.4 486.6 486.8 486.8 487.0 487.2 487.4 487.6 487.6 487.8 488.0 488.2 488.4 488.4 488.6 488.8 ][ 356.0 355.8 + 355.7 355.4 355.3 355.1 355.0 354.9 354.6 354.5 354.3 354.2 354.1 353.8 353.7 353.5 ]plong + 535.5 353.5 535.7 353.7 535.9 353.8 536.1 353.9 4 pls + s[ 537.9 537.9 537.7 537.5 537.3 537.1 537.1 536.9 536.7 536.5 536.3 536.3 536.1 ][ 356.0 355.7 355.5 355.4 355.3 + 355.1 354.9 354.7 354.6 354.5 354.3 354.1 353.9 ]plong + 250.1 357.7 249.9 357.8 249.7 358.0 249.5 358.1 249.5 358.4 5 pls + s[ 250.1 250.3 250.3 250.5 250.7 250.9 251.1 251.1 251.3 251.5 251.7 251.9 ][ 357.7 357.6 357.3 357.2 357.0 356.9 + 356.8 356.5 356.4 356.2 356.1 356.0 ]plong + s[ 304.4 304.2 304.0 303.8 303.8 303.6 303.4 ][ 356.9 356.8 356.6 356.5 356.2 356.1 356.0 ]plong + s[ 305.8 305.6 305.4 305.4 305.2 305.0 304.8 304.6 304.6 304.4 ][ 358.4 358.2 358.1 357.8 357.7 357.6 357.4 357.3 + 357.0 356.9 ]plong + s[ 366.6 366.8 367.0 367.2 367.4 367.4 367.6 367.8 368.0 368.2 368.2 368.4 368.6 368.8 369.0 369.0 ][ 358.4 358.2 + 358.1 358.0 357.8 357.6 357.4 357.3 357.2 357.0 356.8 356.6 356.5 356.4 356.2 356.0 ]plong + s[ 423.3 423.1 422.9 422.7 422.5 422.5 422.3 422.1 421.9 421.7 421.7 421.5 421.3 421.1 420.9 420.9 ][ 358.4 358.2 + 358.1 358.0 357.8 357.6 357.4 357.3 357.2 357.0 356.8 356.6 356.5 356.4 356.2 356.0 ]plong + s[ 484.0 484.2 484.4 484.4 484.6 484.8 485.0 485.2 485.2 485.4 ][ 358.4 358.2 358.1 357.8 357.7 357.6 357.4 357.3 + 357.0 356.9 ]plong + s[ 485.4 485.6 485.8 486.0 486.0 486.2 486.4 ][ 356.9 356.8 356.6 356.5 356.2 356.1 356.0 ]plong + s[ 539.7 539.5 539.5 539.3 539.1 538.9 538.7 538.7 538.5 538.3 538.1 537.9 ][ 357.7 357.6 357.3 357.2 357.0 356.9 + 356.8 356.5 356.4 356.2 356.1 356.0 ]plong + 539.7 357.7 539.9 357.8 540.1 358.0 540.3 358.1 540.3 358.4 5 pls + s[ 247.1 247.1 247.3 247.5 247.7 247.9 247.9 248.1 248.3 248.5 248.7 248.7 248.9 249.1 249.3 249.5 ][ 360.8 360.5 + 360.4 360.2 360.1 360.0 359.7 359.6 359.4 359.3 359.2 358.9 358.8 358.6 358.5 358.4 ]plong + s[ 308.0 307.8 307.8 307.6 307.4 307.2 307.0 307.0 306.8 306.6 306.4 306.2 306.2 306.0 305.8 ][ 360.6 360.5 360.2 + 360.1 360.0 359.8 359.7 359.4 359.3 359.2 359.0 358.9 358.6 358.5 358.4 ]plong + 308.0 360.6 308.2 360.8 2 pls + s[ 364.2 364.4 364.6 364.8 365.0 365.0 365.2 365.4 365.6 365.8 365.8 366.0 ][ 360.8 360.6 360.5 360.4 360.2 360.0 + 359.8 359.7 359.6 359.4 359.2 359.0 ]plong + 366.6 358.4 366.6 358.6 366.4 358.8 366.2 358.9 366.0 359.0 5 pls + 423.3 358.4 423.3 358.6 423.5 358.8 423.7 358.9 423.9 359.0 5 pls + s[ 425.7 425.5 425.3 425.1 424.9 424.9 424.7 424.5 424.3 424.1 424.1 423.9 ][ 360.8 360.6 360.5 360.4 360.2 360.0 + 359.8 359.7 359.6 359.4 359.2 359.0 ]plong + 481.8 360.6 481.6 360.8 2 pls + s[ 481.8 482.0 482.0 482.2 482.4 482.6 482.8 482.8 483.0 483.2 483.4 483.6 483.6 483.8 484.0 ][ 360.6 360.5 360.2 + 360.1 360.0 359.8 359.7 359.4 359.3 359.2 359.0 358.9 358.6 358.5 358.4 ]plong + s[ 542.7 542.7 542.5 542.3 542.1 541.9 541.9 541.7 541.5 541.3 541.1 541.1 540.9 540.7 540.5 540.3 ][ 360.8 360.5 + 360.4 360.2 360.1 360.0 359.7 359.6 359.4 359.3 359.2 358.9 358.8 358.6 358.5 358.4 ]plong + s[ 244.7 244.7 244.9 245.1 245.3 245.5 245.5 245.7 245.9 246.1 246.3 246.3 246.5 ][ 363.2 362.9 362.8 362.7 362.5 + 362.4 362.1 362.0 361.9 361.7 361.6 361.3 361.2 ]plong + 247.1 360.8 246.9 360.9 246.7 361.0 246.5 361.2 4 pls + s[ 308.2 308.4 308.7 308.7 308.9 309.1 309.3 309.5 309.5 309.7 309.9 310.1 310.3 310.3 310.5 310.7 ][ 360.8 360.9 + 361.0 361.3 361.4 361.6 361.7 361.9 362.1 362.3 362.4 362.5 362.7 362.9 363.1 363.2 ]plong + 362.3 362.8 362.1 362.9 361.9 363.1 361.7 363.2 4 pls + s[ 362.3 362.5 362.5 362.7 362.9 363.1 363.3 363.3 363.6 363.8 364.0 364.2 364.2 ][ 362.8 362.7 362.4 362.3 362.1 + 362.0 361.9 361.6 361.4 361.3 361.2 361.0 360.8 ]plong + s[ 427.5 427.3 427.3 427.1 426.9 426.7 426.5 426.5 426.3 426.1 425.9 425.7 425.7 ][ 362.8 362.7 362.4 362.3 362.1 + 362.0 361.9 361.6 361.4 361.3 361.2 361.0 360.8 ]plong + 427.5 362.8 427.7 362.9 427.9 363.1 428.1 363.2 4 pls + s[ 479.2 479.4 479.6 479.6 479.8 480.0 480.2 480.4 480.4 480.6 480.8 481.0 481.2 481.2 481.4 481.6 ][ 363.2 363.1 + 362.9 362.7 362.5 362.4 362.3 362.1 361.9 361.7 361.6 361.4 361.3 361.0 360.9 360.8 ]plong + 542.7 360.8 542.9 360.9 543.1 361.0 543.3 361.2 4 pls + s[ 545.1 545.1 544.9 544.7 544.5 544.3 544.3 544.1 543.9 543.7 543.5 543.5 543.3 ][ 363.2 362.9 362.8 362.7 362.5 + 362.4 362.1 362.0 361.9 361.7 361.6 361.3 361.2 ]plong + 242.9 364.9 242.7 365.1 242.5 365.2 242.3 365.3 242.3 365.6 5 pls + s[ 242.9 243.1 243.1 243.3 243.5 243.7 243.9 243.9 244.1 244.3 244.5 244.7 ][ 364.9 364.8 364.5 364.4 364.3 364.1 + 364.0 363.7 363.6 363.5 363.3 363.2 ]plong + s[ 311.7 311.5 311.3 311.1 311.1 310.9 310.7 ][ 364.1 364.0 363.9 363.7 363.5 363.3 363.2 ]plong + s[ 313.1 312.9 312.7 312.7 312.5 312.3 312.1 311.9 311.9 311.7 ][ 365.6 365.5 365.3 365.1 364.9 364.8 364.7 364.5 + 364.3 364.1 ]plong + s[ 359.3 359.5 359.7 359.9 360.1 360.1 360.3 360.5 360.7 360.9 360.9 361.1 361.3 361.5 361.7 361.7 ][ 365.6 365.5 + 365.3 365.2 365.1 364.8 364.7 364.5 364.4 364.3 364.0 363.9 363.7 363.6 363.5 363.2 ]plong + s[ 430.5 430.3 430.1 429.9 429.7 429.7 429.5 429.3 429.1 428.9 428.9 428.7 428.5 428.3 428.1 428.1 ][ 365.6 365.5 + 365.3 365.2 365.1 364.8 364.7 364.5 364.4 364.3 364.0 363.9 363.7 363.6 363.5 363.2 ]plong + s[ 476.8 477.0 477.2 477.2 477.4 477.6 477.8 478.0 478.0 478.2 ][ 365.6 365.5 365.3 365.1 364.9 364.8 364.7 364.5 + 364.3 364.1 ]plong + s[ 478.2 478.4 478.6 478.8 478.8 479.0 479.2 ][ 364.1 364.0 363.9 363.7 363.5 363.3 363.2 ]plong + s[ 547.0 546.8 546.8 546.6 546.4 546.2 546.0 546.0 545.7 545.5 545.3 545.1 ][ 364.9 364.8 364.5 364.4 364.3 364.1 + 364.0 363.7 363.6 363.5 363.3 363.2 ]plong + 547.0 364.9 547.2 365.1 547.4 365.2 547.6 365.3 547.6 365.6 5 pls + s[ 239.9 239.9 240.1 240.3 240.5 240.7 240.7 240.9 241.1 241.3 241.5 241.5 241.7 241.9 242.1 242.3 ][ 368.0 367.7 + 367.6 367.5 367.3 367.2 366.9 366.8 366.7 366.5 366.4 366.1 366.0 365.9 365.7 365.6 ]plong + s[ 315.3 315.1 315.1 314.9 314.7 314.5 314.3 314.3 314.1 313.9 313.7 313.5 313.5 313.3 313.1 ][ 367.9 367.7 367.5 + 367.3 367.2 367.1 366.9 366.7 366.5 366.4 366.3 366.1 365.9 365.7 365.6 ]plong + 315.3 367.9 315.5 368.0 2 pls + s[ 356.9 357.1 357.3 357.5 357.7 357.7 357.9 358.1 358.3 358.5 358.5 358.7 ][ 368.0 367.9 367.7 367.6 367.5 367.2 + 367.1 366.9 366.8 366.7 366.4 366.3 ]plong + 359.3 365.6 359.3 365.9 359.1 366.0 358.9 366.1 358.7 366.3 5 pls + 430.5 365.6 430.5 365.9 430.7 366.0 430.9 366.1 431.1 366.3 5 pls + s[ 432.9 432.7 432.5 432.3 432.1 432.1 431.9 431.7 431.5 431.3 431.3 431.1 ][ 368.0 367.9 367.7 367.6 367.5 367.2 + 367.1 366.9 366.8 366.7 366.4 366.3 ]plong + 474.6 367.9 474.4 368.0 2 pls + s[ 474.6 474.8 474.8 475.0 475.2 475.4 475.6 475.6 475.8 476.0 476.2 476.4 476.4 476.6 476.8 ][ 367.9 367.7 367.5 + 367.3 367.2 367.1 366.9 366.7 366.5 366.4 366.3 366.1 365.9 365.7 365.6 ]plong + s[ 550.0 550.0 549.8 549.6 549.4 549.2 549.2 549.0 548.8 548.6 548.4 548.4 548.2 548.0 547.8 547.6 ][ 368.0 367.7 + 367.6 367.5 367.3 367.2 366.9 366.8 366.7 366.5 366.4 366.1 366.0 365.9 365.7 365.6 ]plong + s[ 200.5 200.5 200.3 200.1 199.9 199.7 199.7 199.5 ][ 370.4 370.2 370.0 369.9 369.8 369.6 369.4 369.2 ]plong + s[ 237.5 237.5 237.7 237.9 238.1 238.3 238.3 238.5 238.7 238.9 239.1 239.1 239.3 ][ 370.4 370.2 370.0 369.9 369.8 + 369.6 369.4 369.2 369.1 369.0 368.8 368.6 368.4 ]plong + 239.9 368.0 239.7 368.2 239.5 368.3 239.3 368.4 4 pls + s[ 315.5 315.7 315.9 315.9 316.1 316.3 316.5 316.7 316.7 316.9 317.1 317.3 317.5 317.5 317.7 317.9 ][ 368.0 368.2 + 368.3 368.6 368.7 368.8 369.0 369.1 369.4 369.5 369.6 369.8 369.9 370.2 370.3 370.4 ]plong + 355.1 370.0 354.9 370.2 354.7 370.3 354.5 370.4 4 pls + s[ 355.1 355.3 355.3 355.5 355.7 355.9 356.1 356.1 356.3 356.5 356.7 356.9 356.9 ][ 370.0 369.9 369.6 369.5 369.4 + 369.2 369.1 368.8 368.7 368.6 368.4 368.3 368.0 ]plong + s[ 434.7 434.5 434.5 434.3 434.1 433.9 433.7 433.7 433.5 433.3 433.1 432.9 432.9 ][ 370.0 369.9 369.6 369.5 369.4 + 369.2 369.1 368.8 368.7 368.6 368.4 368.3 368.0 ]plong + 434.7 370.0 434.9 370.2 435.1 370.3 435.3 370.4 4 pls + s[ 471.9 472.1 472.3 472.3 472.5 472.7 473.0 473.2 473.2 473.4 473.6 473.8 474.0 474.0 474.2 474.4 ][ 370.4 370.3 + 370.2 369.9 369.8 369.6 369.5 369.4 369.1 369.0 368.8 368.7 368.6 368.3 368.2 368.0 ]plong + 550.0 368.0 550.2 368.2 550.4 368.3 550.6 368.4 4 pls + s[ 552.4 552.4 552.2 552.0 551.8 551.6 551.6 551.4 551.2 551.0 550.8 550.8 550.6 ][ 370.4 370.2 370.0 369.9 369.8 + 369.6 369.4 369.2 369.1 369.0 368.8 368.6 368.4 ]plong + s[ 589.4 589.4 589.6 589.8 590.0 590.2 590.2 590.4 ][ 370.4 370.2 370.0 369.9 369.8 369.6 369.4 369.2 ]plong + s[ 203.1 202.9 202.7 202.5 202.5 202.3 202.1 201.9 201.7 201.7 201.5 201.3 201.1 200.9 200.7 200.5 ][ 372.7 372.6 + 372.4 372.3 372.0 371.9 371.8 371.6 371.5 371.2 371.1 371.0 370.8 370.7 370.6 370.4 ]plong + 203.1 372.7 203.3 372.8 2 pls + 235.6 372.2 235.4 372.3 235.2 372.4 235.0 372.6 235.0 372.8 5 pls + s[ 235.6 235.9 235.9 236.1 236.3 236.5 236.7 236.7 236.9 237.1 237.3 237.5 ][ 372.2 372.0 371.8 371.6 371.5 371.4 + 371.2 371.0 370.8 370.7 370.6 370.4 ]plong + s[ 318.9 318.7 318.5 318.3 318.3 318.1 317.9 ][ 371.4 371.2 371.1 371.0 370.7 370.6 370.4 ]plong + s[ 320.3 320.1 319.9 319.9 319.7 319.5 319.3 319.1 319.1 318.9 ][ 372.8 372.7 372.6 372.3 372.2 372.0 371.9 371.8 + 371.5 371.4 ]plong + s[ 352.1 352.3 352.5 352.7 352.9 352.9 353.1 353.3 353.5 353.7 353.7 353.9 354.1 354.3 354.5 354.5 ][ 372.8 372.7 + 372.6 372.4 372.3 372.0 371.9 371.8 371.6 371.5 371.2 371.1 371.0 370.8 370.7 370.4 ]plong + s[ 437.8 437.6 437.4 437.2 437.0 437.0 436.8 436.6 436.4 436.1 436.1 435.9 435.7 435.5 435.3 435.3 ][ 372.8 372.7 + 372.6 372.4 372.3 372.0 371.9 371.8 371.6 371.5 371.2 371.1 371.0 370.8 370.7 370.4 ]plong + s[ 469.5 469.7 469.9 469.9 470.1 470.3 470.5 470.7 470.7 470.9 ][ 372.8 372.7 372.6 372.3 372.2 372.0 371.9 371.8 + 371.5 371.4 ]plong + s[ 470.9 471.1 471.3 471.5 471.5 471.7 471.9 ][ 371.4 371.2 371.1 371.0 370.7 370.6 370.4 ]plong + s[ 554.2 554.0 554.0 553.8 553.6 553.4 553.2 553.2 553.0 552.8 552.6 552.4 ][ 372.2 372.0 371.8 371.6 371.5 371.4 + 371.2 371.0 370.8 370.7 370.6 370.4 ]plong + 554.2 372.2 554.4 372.3 554.6 372.4 554.8 372.6 554.8 372.8 5 pls + s[ 587.0 587.0 587.2 587.4 587.4 587.6 587.8 588.0 588.2 588.2 588.4 588.6 588.8 589.0 589.2 589.4 ][ 372.8 372.6 + 372.4 372.3 372.0 371.9 371.8 371.6 371.5 371.2 371.1 371.0 370.8 370.7 370.6 370.4 ]plong + s[ 203.3 203.3 203.5 203.7 203.7 203.9 204.1 204.3 204.5 204.5 204.7 204.9 205.1 205.3 205.3 ][ 372.8 373.1 373.2 + 373.4 373.6 373.8 373.9 374.1 374.2 374.5 374.6 374.7 374.9 375.0 375.3 ]plong + s[ 232.6 232.6 232.8 233.0 233.2 233.4 233.4 233.6 233.8 234.0 234.2 234.2 234.4 234.6 234.8 235.0 ][ 375.3 375.0 + 374.9 374.7 374.6 374.5 374.2 374.1 373.9 373.8 373.6 373.4 373.2 373.1 373.0 372.8 ]plong + s[ 322.5 322.3 322.3 322.1 321.9 321.7 321.5 321.5 321.3 321.1 320.9 320.7 320.7 320.5 320.3 ][ 375.1 375.0 374.7 + 374.6 374.5 374.3 374.2 373.9 373.8 373.6 373.5 373.4 373.1 373.0 372.8 ]plong + 322.5 375.1 322.7 375.3 2 pls + s[ 349.7 349.9 350.1 350.3 350.5 350.5 350.7 350.9 351.1 351.3 351.3 351.5 ][ 375.3 375.1 375.0 374.9 374.7 374.5 + 374.3 374.2 374.1 373.9 373.6 373.5 ]plong + 352.1 372.8 352.1 373.1 351.9 373.2 351.7 373.4 351.5 373.5 5 pls + 437.8 372.8 437.8 373.1 438.0 373.2 438.2 373.4 438.4 373.5 5 pls + s[ 440.2 440.0 439.8 439.6 439.4 439.4 439.2 439.0 438.8 438.6 438.6 438.4 ][ 375.3 375.1 375.0 374.9 374.7 374.5 + 374.3 374.2 374.1 373.9 373.6 373.5 ]plong + 467.3 375.1 467.1 375.3 2 pls + s[ 467.3 467.5 467.5 467.7 467.9 468.1 468.3 468.3 468.5 468.7 468.9 469.1 469.1 469.3 469.5 ][ 375.1 375.0 374.7 + 374.6 374.5 374.3 374.2 373.9 373.8 373.6 373.5 373.4 373.1 373.0 372.8 ]plong + s[ 557.2 557.2 557.0 556.8 556.6 556.4 556.4 556.2 556.0 555.8 555.6 555.6 555.4 555.2 555.0 554.8 ][ 375.3 375.0 + 374.9 374.7 374.6 374.5 374.2 374.1 373.9 373.8 373.6 373.4 373.2 373.1 373.0 372.8 ]plong + s[ 584.6 584.6 584.8 585.0 585.2 585.4 585.4 585.6 585.8 586.0 586.2 586.2 586.4 586.6 586.8 ][ 375.3 375.0 374.9 + 374.7 374.6 374.5 374.2 374.1 373.9 373.8 373.6 373.4 373.2 373.1 373.0 ]plong + 586.8 373.0 587.0 372.8 2 pls + s[ 206.7 206.5 206.3 206.1 206.1 205.9 205.7 205.5 205.3 ][ 376.5 376.3 376.2 376.1 375.8 375.7 375.5 375.4 375.3 +]plong + s[ 208.1 207.9 207.7 207.5 207.3 207.1 206.9 206.9 206.7 ][ 377.7 377.5 377.4 377.3 377.1 377.0 376.9 376.6 376.5 +]plong + s[ 230.2 230.2 230.4 230.6 230.8 231.0 231.0 231.2 231.4 231.6 231.8 231.8 232.0 ][ 377.7 377.4 377.3 377.1 377.0 + 376.9 376.6 376.5 376.3 376.2 376.1 375.8 375.7 ]plong + 232.6 375.3 232.4 375.4 232.2 375.5 232.0 375.7 4 pls + s[ 322.7 322.9 323.1 323.1 323.3 323.5 323.7 323.9 323.9 324.1 324.3 324.5 324.7 324.7 324.9 325.1 ][ 375.3 375.4 + 375.5 375.8 375.9 376.1 376.2 376.3 376.6 376.7 376.9 377.0 377.1 377.4 377.5 377.7 ]plong + 347.9 377.3 347.7 377.4 347.5 377.5 347.3 377.7 4 pls + s[ 347.9 348.1 348.1 348.3 348.5 348.7 348.9 348.9 349.1 349.3 349.5 349.7 349.7 ][ 377.3 377.1 376.9 376.7 376.6 + 376.5 376.3 376.1 375.9 375.8 375.7 375.5 375.3 ]plong + s[ 442.0 441.8 441.8 441.6 441.4 441.2 441.0 441.0 440.8 440.6 440.4 440.2 440.2 ][ 377.3 377.1 376.9 376.7 376.6 + 376.5 376.3 376.1 375.9 375.8 375.7 375.5 375.3 ]plong + 442.0 377.3 442.2 377.4 442.4 377.5 442.6 377.7 4 pls + s[ 464.7 464.9 465.1 465.1 465.3 465.5 465.7 465.9 465.9 466.1 466.3 466.5 466.7 466.7 466.9 467.1 ][ 377.7 377.5 + 377.4 377.1 377.0 376.9 376.7 376.6 376.3 376.2 376.1 375.9 375.8 375.5 375.4 375.3 ]plong + 557.2 375.3 557.4 375.4 557.6 375.5 557.8 375.7 4 pls + s[ 559.6 559.6 559.4 559.2 559.0 558.8 558.8 558.6 558.4 558.2 558.0 558.0 557.8 ][ 377.7 377.4 377.3 377.1 377.0 + 376.9 376.6 376.5 376.3 376.2 376.1 375.8 375.7 ]plong + s[ 582.1 582.1 582.4 582.6 582.8 583.0 583.0 583.2 ][ 377.7 377.4 377.3 377.1 377.0 376.9 376.6 376.5 ]plong + s[ 583.2 583.4 583.6 583.8 583.8 584.0 584.2 584.4 584.6 ][ 376.5 376.3 376.2 376.1 375.8 375.7 375.5 375.4 375.3 +]plong + s[ 210.1 210.1 209.9 209.7 209.5 209.3 209.3 209.1 208.9 208.9 208.7 208.5 208.3 208.1 208.1 ][ 380.1 379.8 379.7 + 379.5 379.4 379.3 379.0 378.9 378.7 378.5 378.3 378.2 378.1 377.9 377.7 ]plong + 228.4 379.4 228.2 379.5 228.0 379.7 227.8 379.8 227.8 380.1 5 pls + s[ 228.4 228.6 228.6 228.8 229.0 229.2 229.4 229.4 229.6 229.8 230.0 230.2 ][ 379.4 379.3 379.0 378.9 378.7 378.6 + 378.5 378.2 378.1 377.9 377.8 377.7 ]plong + s[ 326.1 325.9 325.7 325.5 325.5 325.3 325.1 ][ 378.6 378.5 378.3 378.2 377.9 377.8 377.7 ]plong + s[ 327.6 327.4 327.2 327.2 327.0 326.7 326.5 326.3 326.3 326.1 ][ 380.1 379.9 379.8 379.5 379.4 379.3 379.1 379.0 + 378.7 378.6 ]plong + s[ 344.8 345.0 345.3 345.5 345.7 345.7 345.9 346.1 346.3 346.5 346.5 346.7 346.9 347.1 347.3 347.3 ][ 380.1 379.9 + 379.8 379.7 379.5 379.3 379.1 379.0 378.9 378.7 378.5 378.3 378.2 378.1 377.9 377.7 ]plong + s[ 445.0 444.8 444.6 444.4 444.2 444.2 444.0 443.8 443.6 443.4 443.4 443.2 443.0 442.8 442.6 442.6 ][ 380.1 379.9 + 379.8 379.7 379.5 379.3 379.1 379.0 378.9 378.7 378.5 378.3 378.2 378.1 377.9 377.7 ]plong + s[ 462.3 462.5 462.7 462.7 462.9 463.1 463.3 463.5 463.5 463.7 ][ 380.1 379.9 379.8 379.5 379.4 379.3 379.1 379.0 + 378.7 378.6 ]plong + s[ 463.7 463.9 464.1 464.3 464.3 464.5 464.7 ][ 378.6 378.5 378.3 378.2 377.9 377.8 377.7 ]plong + s[ 561.4 561.2 561.2 561.0 560.8 560.6 560.4 560.4 560.2 560.0 559.8 559.6 ][ 379.4 379.3 379.0 378.9 378.7 378.6 + 378.5 378.2 378.1 377.9 377.8 377.7 ]plong + 561.4 379.4 561.6 379.5 561.8 379.7 562.0 379.8 562.0 380.1 5 pls + s[ 579.7 579.7 579.9 580.1 580.3 580.5 580.5 580.7 580.9 581.1 581.3 581.3 581.5 581.7 581.9 582.1 ][ 380.1 379.8 + 379.7 379.5 379.4 379.3 379.0 378.9 378.7 378.6 378.5 378.2 378.1 377.9 377.8 377.7 ]plong + 210.1 380.1 210.3 380.2 2 pls + s[ 212.5 212.5 212.3 212.1 212.1 211.9 211.7 211.5 211.3 211.1 210.9 210.9 210.7 210.5 210.3 ][ 382.5 382.2 382.1 + 382.0 381.7 381.6 381.4 381.3 381.2 381.0 380.9 380.6 380.5 380.4 380.2 ]plong + s[ 225.4 225.4 225.6 225.8 226.0 226.2 226.2 226.4 226.6 226.8 227.0 227.0 227.2 227.4 227.6 227.8 ][ 382.5 382.2 + 382.1 382.0 381.8 381.7 381.4 381.3 381.2 381.0 380.9 380.6 380.5 380.4 380.2 380.1 ]plong + s[ 329.8 329.6 329.6 329.4 329.2 329.0 328.8 328.8 328.6 328.4 328.2 328.0 328.0 327.8 327.6 ][ 382.4 382.2 382.0 + 381.8 381.7 381.6 381.4 381.2 381.0 380.9 380.8 380.6 380.4 380.2 380.1 ]plong + 329.8 382.4 330.0 382.5 2 pls + s[ 342.4 342.6 342.8 343.0 343.2 343.2 343.4 343.6 343.8 344.0 344.0 344.2 ][ 382.5 382.4 382.2 382.1 382.0 381.7 + 381.6 381.4 381.3 381.2 380.9 380.8 ]plong + 344.8 380.1 344.8 380.4 344.6 380.5 344.4 380.6 344.2 380.8 5 pls + 445.0 380.1 445.0 380.4 445.2 380.5 445.4 380.6 445.6 380.8 5 pls + s[ 447.4 447.2 447.0 446.8 446.6 446.6 446.4 446.2 446.0 445.8 445.8 445.6 ][ 382.5 382.4 382.2 382.1 382.0 381.7 + 381.6 381.4 381.3 381.2 380.9 380.8 ]plong + 460.1 382.4 459.9 382.5 2 pls + s[ 460.1 460.3 460.3 460.5 460.7 460.9 461.1 461.1 461.3 461.5 461.7 461.9 461.9 462.1 462.3 ][ 382.4 382.2 382.0 + 381.8 381.7 381.6 381.4 381.2 381.0 380.9 380.8 380.6 380.4 380.2 380.1 ]plong + s[ 564.5 564.5 564.3 564.1 563.8 563.6 563.6 563.4 563.2 563.0 562.8 562.8 562.6 562.4 562.2 562.0 ][ 382.5 382.2 + 382.1 382.0 381.8 381.7 381.4 381.3 381.2 381.0 380.9 380.6 380.5 380.4 380.2 380.1 ]plong + s[ 577.3 577.3 577.5 577.7 577.9 578.1 578.1 578.3 578.5 578.7 578.9 578.9 579.1 579.3 579.5 ][ 382.5 382.2 382.1 + 382.0 381.8 381.7 381.4 381.3 381.2 381.0 380.9 380.6 380.5 380.4 380.2 ]plong + 579.5 380.2 579.7 380.1 2 pls + s[ 213.9 213.7 213.5 213.3 213.3 213.1 212.9 212.7 212.5 ][ 383.7 383.6 383.4 383.3 383.0 382.9 382.8 382.6 382.5 +]plong + s[ 214.9 214.9 214.7 214.5 214.3 214.1 214.1 213.9 ][ 384.9 384.6 384.5 384.4 384.2 384.1 383.8 383.7 ]plong + s[ 223.0 223.0 223.2 223.4 223.6 223.8 223.8 224.0 224.2 224.4 224.6 224.6 224.8 ][ 384.9 384.6 384.5 384.4 384.2 + 384.1 383.8 383.7 383.6 383.4 383.3 383.0 382.9 ]plong + 225.4 382.5 225.2 382.6 225.0 382.8 224.8 382.9 4 pls + s[ 330.0 330.2 330.4 330.4 330.6 330.8 331.0 331.2 331.2 331.4 331.6 331.8 332.0 332.0 332.2 332.4 ][ 382.5 382.6 + 382.8 383.0 383.2 383.3 383.4 383.6 383.8 384.0 384.1 384.2 384.4 384.6 384.8 384.9 ]plong + 340.6 384.5 340.4 384.6 340.2 384.8 340.0 384.9 4 pls + s[ 340.6 340.8 340.8 341.0 341.2 341.4 341.6 341.6 341.8 342.0 342.2 342.4 342.4 ][ 384.5 384.4 384.1 384.0 383.8 + 383.7 383.6 383.3 383.2 383.0 382.9 382.8 382.5 ]plong + s[ 449.2 449.0 449.0 448.8 448.6 448.4 448.2 448.2 448.0 447.8 447.6 447.4 447.4 ][ 384.5 384.4 384.1 384.0 383.8 + 383.7 383.6 383.3 383.2 383.0 382.9 382.8 382.5 ]plong + 449.2 384.5 449.4 384.6 449.6 384.8 449.8 384.9 4 pls + s[ 457.5 457.7 457.9 457.9 458.1 458.3 458.5 458.7 458.7 458.9 459.1 459.3 459.5 459.5 459.7 459.9 ][ 384.9 384.8 + 384.6 384.4 384.2 384.1 384.0 383.8 383.6 383.4 383.3 383.2 383.0 382.8 382.6 382.5 ]plong + 564.5 382.5 564.7 382.6 564.9 382.8 565.1 382.9 4 pls + s[ 566.9 566.9 566.7 566.5 566.3 566.1 566.1 565.9 565.7 565.5 565.3 565.3 565.1 ][ 384.9 384.6 384.5 384.4 384.2 + 384.1 383.8 383.7 383.6 383.4 383.3 383.0 382.9 ]plong + s[ 574.9 574.9 575.1 575.3 575.5 575.7 575.7 575.9 ][ 384.9 384.6 384.5 384.4 384.2 384.1 383.8 383.7 ]plong + s[ 575.9 576.1 576.3 576.5 576.5 576.7 576.9 577.1 577.3 ][ 383.7 383.6 383.4 383.3 383.0 382.9 382.8 382.6 382.5 +]plong + s[ 217.6 217.3 217.1 216.9 216.7 216.5 216.5 216.3 216.1 216.1 215.9 215.7 215.5 215.3 215.1 214.9 ][ 387.2 387.1 + 386.9 386.8 386.7 386.5 386.3 386.1 386.0 385.7 385.6 385.4 385.3 385.2 385.0 384.9 ]plong + 217.6 387.2 217.8 387.3 2 pls + s[ 221.2 221.4 221.4 221.6 221.8 222.0 222.2 222.2 222.4 222.6 222.8 223.0 ][ 386.7 386.5 386.3 386.1 386.0 385.8 + 385.7 385.4 385.3 385.2 385.0 384.9 ]plong + s[ 333.4 333.2 333.0 332.8 332.8 332.6 332.4 ][ 385.8 385.7 385.6 385.4 385.2 385.0 384.9 ]plong + s[ 334.8 334.6 334.4 334.4 334.2 334.0 333.8 333.6 333.6 333.4 ][ 387.3 387.2 387.1 386.8 386.7 386.5 386.4 386.3 + 386.0 385.8 ]plong + s[ 337.6 337.8 338.0 338.2 338.4 338.4 338.6 338.8 339.0 339.2 339.2 339.4 339.6 339.8 340.0 340.0 ][ 387.3 387.2 + 387.1 386.9 386.8 386.5 386.4 386.3 386.1 386.0 385.7 385.6 385.4 385.3 385.2 384.9 ]plong + s[ 452.2 452.0 451.8 451.6 451.4 451.4 451.2 451.0 450.8 450.6 450.6 450.4 450.2 450.0 449.8 449.8 ][ 387.3 387.2 + 387.1 386.9 386.8 386.5 386.4 386.3 386.1 386.0 385.7 385.6 385.4 385.3 385.2 384.9 ]plong + s[ 455.1 455.3 455.5 455.5 455.7 455.9 456.1 456.3 456.3 456.5 ][ 387.3 387.2 387.1 386.8 386.7 386.5 386.4 386.3 + 386.0 385.8 ]plong + s[ 456.5 456.7 456.9 457.1 457.1 457.3 457.5 ][ 385.8 385.7 385.6 385.4 385.2 385.0 384.9 ]plong + s[ 568.7 568.5 568.5 568.3 568.1 567.9 567.7 567.7 567.5 567.3 567.1 566.9 ][ 386.7 386.5 386.3 386.1 386.0 385.8 + 385.7 385.4 385.3 385.2 385.0 384.9 ]plong + 568.7 386.7 568.9 386.8 569.1 386.9 569.3 387.1 569.3 387.3 5 pls + s[ 572.5 572.5 572.7 572.9 573.1 573.3 573.3 573.5 573.7 573.7 573.9 574.1 574.3 574.5 574.7 574.9 ][ 387.3 387.1 + 386.9 386.8 386.7 386.5 386.3 386.1 386.0 385.7 385.6 385.4 385.3 385.2 385.0 384.9 ]plong + s[ 337.0 336.8 336.8 336.6 336.4 336.2 336.0 336.0 335.8 335.6 335.4 335.2 335.2 335.0 334.8 ][ 388.0 388.1 388.4 + 388.5 388.7 388.8 388.7 388.4 388.3 388.1 388.0 387.9 387.6 387.5 387.3 ]plong + 337.6 387.3 337.6 387.6 337.4 387.7 337.2 387.9 337.0 388.0 5 pls + 452.2 387.3 452.2 387.6 452.4 387.7 452.6 387.9 452.8 388.0 5 pls + s[ 452.8 453.0 453.0 453.2 453.4 453.6 453.8 453.8 454.0 454.2 454.4 454.7 454.7 454.9 455.1 ][ 388.0 388.1 388.4 + 388.5 388.7 388.8 388.7 388.4 388.3 388.1 388.0 387.9 387.6 387.5 387.3 ]plong + s[ 572.3 572.1 571.9 571.7 571.7 571.5 571.3 571.1 570.7 570.5 570.3 570.1 570.1 569.9 569.7 569.5 569.3 ][ 387.5 + 387.6 387.7 387.9 388.1 388.3 388.4 388.5 388.5 388.4 388.3 388.1 387.9 387.7 387.6 387.5 387.3 ]plong + 572.3 387.5 572.5 387.3 2 pls + s[ 217.8 217.8 218.0 218.2 218.2 218.4 218.6 218.8 219.2 219.4 219.6 219.8 219.8 220.0 220.2 220.4 220.6 ][ 387.3 + 387.6 387.7 387.9 388.1 388.3 388.4 388.5 388.5 388.4 388.3 388.1 387.9 387.7 387.6 387.5 387.3 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 421.88 285.00 moveto[ 421.9 424.2 449.2 446.9 ][ 285.0 296.9 292.0 280.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 426.3 285.8 428.0 294.4 426.5 293.5 425.6 293.2 4 pls + s[ 434.9 433.6 433.1 432.9 433.2 433.9 435.5 436.6 437.3 437.5 437.3 436.7 436.2 434.9 433.3 432.1 431.8 431.5 + 431.8 432.4 433.3 434.6 436.4 437.3 437.8 438.0 437.7 436.6 434.9 ][ 293.1 292.9 292.2 291.4 290.5 289.9 289.2 + 288.5 287.5 286.6 285.4 284.7 284.3 284.2 284.5 285.1 285.6 286.5 287.7 288.5 289.1 289.3 289.4 289.7 290.4 + 291.2 292.1 292.8 293.1 ]plong + s[ 443.6 442.2 441.2 440.4 440.1 440.2 440.7 441.9 442.7 444.0 445.1 445.9 446.1 446.1 445.5 444.4 443.6 ][ 291.4 + 291.2 290.2 288.2 287.0 284.8 283.5 282.8 282.6 282.8 283.9 285.9 287.1 289.2 290.6 291.3 291.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 362.92 284.33 moveto[ 362.9 361.8 388.4 389.5 ][ 284.3 296.4 299.0 286.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 364.6 364.6 364.9 365.3 366.1 367.7 368.6 369.1 369.6 369.6 369.3 368.6 364.8 370.7 ][ 292.9 293.3 294.2 294.6 + 295.1 295.3 295.0 294.6 293.8 293.0 292.1 290.8 286.2 286.8 ]plong + s[ 372.9 372.9 373.2 373.6 374.4 376.0 376.9 377.4 377.9 378.0 377.6 376.9 373.1 379.0 ][ 293.7 294.1 295.0 295.4 + 295.9 296.1 295.8 295.4 294.6 293.8 292.9 291.6 287.0 287.6 ]plong + s[ 385.6 381.5 381.4 381.8 383.0 384.2 385.5 386.4 387.0 387.0 386.7 386.0 384.8 383.5 382.3 381.8 381.3 ][ 297.0 + 296.6 292.8 293.3 293.8 293.9 293.6 292.9 291.7 290.9 289.6 288.7 288.1 288.0 288.3 288.7 289.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 307.68 268.21 moveto[ 307.7 303.6 328.8 332.9 ][ 268.2 279.6 288.6 277.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 307.2 307.1 307.2 307.5 308.1 309.7 310.6 311.2 311.8 312.1 312.0 311.6 309.1 314.6 ][ 276.9 277.3 278.2 278.8 + 279.4 280.0 279.9 279.6 279.0 278.2 277.3 275.8 270.5 272.4 ]plong + 318.5 273.8 319.5 283.5 2 pls + 319.5 283.5 314.0 281.5 2 pls + s[ 324.3 323.2 322.8 323.2 323.6 324.7 325.9 327.2 328.0 329.0 329.4 329.1 328.7 327.6 326.4 325.0 324.3 ][ 285.2 + 284.4 282.9 280.8 279.6 277.8 276.9 276.9 277.2 278.0 279.5 281.6 282.8 284.6 285.5 285.5 285.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 252.88 245.34 moveto[ 252.9 248.3 273.1 277.6 ][ 245.3 256.6 266.6 255.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 251.7 255.9 254.9 256.0 256.9 257.5 258.3 258.7 258.7 258.3 257.3 256.1 254.8 254.3 253.6 ][ 256.1 257.8 253.8 + 254.3 254.2 254.0 253.0 252.2 250.9 249.8 248.9 248.5 248.4 248.6 249.2 ]plong + 265.0 252.1 261.7 260.2 261.0 258.6 260.4 257.9 4 pls + s[ 271.0 267.1 268.2 268.4 269.4 270.6 271.9 273.0 273.8 274.1 274.2 273.8 272.8 271.6 270.3 269.7 269.0 ][ 264.0 + 262.4 258.8 259.3 260.2 260.6 260.7 260.3 259.3 258.5 257.2 256.1 255.2 254.7 254.7 254.9 255.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 199.96 230.44 moveto[ 200.0 197.6 207.4 209.8 ][ 230.4 242.3 244.3 232.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 202.4 201.3 200.7 200.7 201.0 201.8 202.8 204.1 205.0 206.1 206.7 206.7 206.4 205.6 204.6 203.3 202.4 ][ 241.6 + 240.9 239.5 237.4 236.2 234.2 233.1 233.0 233.1 233.8 235.2 237.3 238.5 240.5 241.6 241.7 241.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 536.35 245.61 moveto[ 536.3 540.4 557.7 553.7 ][ 245.6 257.0 250.9 239.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 545.9 545.1 544.1 542.7 542.3 541.3 540.8 540.8 541.0 541.8 542.8 544.2 544.6 545.6 546.1 545.9 545.2 544.2 + 542.9 541.6 540.8 539.8 539.7 ][ 250.2 249.2 248.6 248.7 248.8 249.6 250.7 252.0 252.4 253.4 253.9 253.9 253.8 + 253.0 251.9 250.2 248.2 246.4 245.5 245.5 245.8 246.6 247.5 ]plong + s[ 552.0 550.7 549.5 548.4 548.0 547.7 548.1 549.1 549.9 551.2 552.4 553.5 553.9 554.2 553.9 552.8 552.0 ][ 251.1 + 251.2 250.3 248.4 247.3 245.1 243.7 242.9 242.6 242.6 243.5 245.3 246.5 248.6 250.1 250.9 251.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 477.53 269.90 moveto[ 477.5 482.1 505.7 501.1 ][ 269.9 281.1 271.5 260.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 482.0 269.9 485.3 278.0 483.7 277.3 482.8 277.2 4 pls + s[ 490.7 495.0 491.4 492.6 493.2 493.4 493.3 493.0 492.2 491.1 489.8 488.6 487.6 487.4 487.3 ][ 275.8 274.1 271.9 + 271.4 270.7 270.2 268.9 268.1 267.1 266.6 266.7 267.2 268.1 268.6 269.5 ]plong + s[ 502.4 498.5 496.7 497.2 498.5 499.7 500.7 501.2 501.1 500.8 499.9 498.8 497.5 496.3 495.3 495.1 495.0 ][ 271.1 + 272.6 269.3 269.5 269.5 269.0 268.1 267.0 265.7 265.0 263.9 263.5 263.6 264.0 264.9 265.5 266.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 346.76 177.51 moveto[ 346.8 344.8 373.3 375.2 ][ 177.5 189.5 194.1 182.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 355.4 184.4 348.0 183.2 2 pls + s[ 363.3 363.1 362.4 361.2 360.8 359.5 358.5 357.9 357.9 358.1 358.8 359.9 360.3 361.7 362.6 363.3 363.6 363.5 + 362.9 361.7 360.9 359.6 359.1 ][ 187.8 186.5 185.5 184.9 184.9 185.1 185.8 186.9 187.3 188.6 189.6 190.2 190.3 + 190.1 189.4 187.8 185.7 183.6 182.2 181.6 181.5 181.7 182.5 ]plong + s[ 368.2 367.0 366.4 366.3 366.5 367.3 368.3 369.6 370.4 371.6 372.2 372.3 372.1 371.3 370.3 369.0 368.2 ][ 191.5 + 190.9 189.6 187.4 186.2 184.2 183.1 182.9 183.0 183.6 185.0 187.1 188.4 190.4 191.5 191.7 191.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 448.03 193.09 moveto[ 448.0 439.3 459.4 468.1 ][ 193.1 201.5 222.2 213.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 450.8 203.8 445.6 198.4 2 pls + s[ 453.9 453.0 451.8 451.3 450.7 451.0 452.2 453.7 455.2 456.4 457.6 457.9 458.4 458.4 457.8 457.5 456.3 455.1 + 454.0 453.7 453.1 453.1 453.7 ][ 212.4 212.7 212.1 211.5 210.3 208.8 207.0 205.6 204.7 204.7 205.4 205.7 206.8 + 208.0 209.2 209.5 210.1 210.0 209.4 209.1 207.9 206.8 205.6 ]plong + s[ 456.8 456.2 456.5 457.7 458.6 460.4 461.9 463.1 463.7 464.2 463.9 462.7 461.8 460.0 458.5 457.4 456.8 ][ 217.2 + 216.0 214.5 212.7 211.9 210.7 210.5 211.1 211.7 212.9 214.3 216.1 216.9 218.1 218.4 217.8 217.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 474.88 223.66 moveto[ 474.9 463.6 474.0 485.3 ][ 223.7 228.0 254.9 250.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 473.3 234.6 470.6 227.6 2 pls + s[ 470.0 471.6 473.8 474.3 475.0 475.5 476.8 477.6 478.6 479.1 479.0 478.6 477.7 477.2 476.3 ][ 239.9 244.2 240.6 + 241.8 242.4 242.7 242.6 242.3 241.5 240.4 239.1 237.9 236.9 236.6 236.6 ]plong + s[ 473.6 473.5 474.4 476.2 477.3 479.4 480.9 481.7 482.0 482.1 481.2 479.4 478.3 476.2 474.7 473.9 473.6 ][ 249.2 + 247.9 246.7 245.5 245.1 244.7 245.1 246.1 246.9 248.2 249.4 250.6 251.0 251.4 251.0 250.0 249.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 503.97 311.61 moveto[ 504.0 493.2 501.6 512.4 ][ 311.6 317.1 333.5 328.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 496.0 498.1 499.9 500.5 501.3 501.8 503.1 503.9 504.8 505.2 505.0 504.4 503.5 502.9 502.0 ][ 319.0 323.1 319.3 + 320.4 321.0 321.2 321.0 320.6 319.6 318.5 317.2 316.1 315.2 315.0 315.0 ]plong + s[ 500.6 500.4 501.1 502.8 503.9 506.0 507.5 508.4 508.8 509.0 508.2 506.6 505.5 503.4 501.9 501.0 500.6 ][ 327.9 + 326.6 325.3 323.9 323.4 322.8 323.0 323.9 324.6 325.9 327.3 328.6 329.2 329.7 329.6 328.6 327.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 541.37 346.78 moveto[ 541.4 535.2 550.7 556.8 ][ 346.8 357.2 366.3 355.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 542.8 542.0 540.7 540.0 539.1 539.1 539.8 540.8 542.0 543.2 544.5 544.8 545.7 546.0 545.7 545.5 544.5 543.4 + 542.1 541.7 540.8 540.6 540.8 ][ 358.3 358.8 358.5 358.1 357.1 355.6 353.6 351.8 350.6 350.3 350.6 350.8 351.8 + 352.9 354.2 354.6 355.4 355.7 355.4 355.2 354.2 353.1 351.8 ]plong + s[ 546.8 546.0 545.9 546.6 547.2 548.7 550.0 551.3 552.0 552.9 553.0 552.3 551.6 550.2 548.9 547.6 546.8 ][ 362.1 + 361.1 359.6 357.6 356.6 355.0 354.3 354.6 355.0 356.0 357.5 359.5 360.6 362.2 362.8 362.6 362.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 401.83 moveto[ 360.9 360.9 435.2 435.2 ][ 401.8 413.9 413.9 401.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 403.5 366.3 412.3 2 pls + 369.7 403.5 366.3 412.3 2 pls + 368.4 406.4 364.2 406.4 2 pls + 378.4 403.5 378.4 412.3 2 pls + 383.9 412.3 378.4 412.3 2 pls + 381.8 408.1 378.4 408.1 2 pls + 385.9 403.5 385.9 412.3 2 pls + 391.0 403.5 391.0 412.3 2 pls + 393.9 412.3 388.0 412.3 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 411.0 411.9 412.3 412.3 411.9 411.0 410.2 409.3 408.9 408.5 407.7 407.3 406.8 406.0 404.8 403.9 + 403.5 403.5 403.9 404.8 ]plong + 413.5 403.5 412.7 403.5 411.8 403.9 411.4 405.2 411.4 412.3 5 pls + 413.1 409.3 410.2 409.3 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 406.8 + 406.8 407.7 408.5 408.9 409.3 409.3 408.9 408.1 406.8 406.0 404.8 403.9 403.5 403.5 403.9 404.8 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 408.1 + 408.9 409.3 409.3 408.9 408.1 407.3 406.8 406.4 406.0 405.2 404.8 403.9 403.5 403.5 403.9 404.8 ]plong + 433.1 403.5 432.3 403.5 431.5 403.9 431.1 405.2 431.1 412.3 5 pls + 432.7 409.3 429.8 409.3 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/joye_car_headers/CAR_model.head b/ast_tester/joye_car_headers/CAR_model.head new file mode 100644 index 0000000..82a7d4e --- /dev/null +++ b/ast_tester/joye_car_headers/CAR_model.head @@ -0,0 +1,47 @@ +FITS headers in CAR_model.fits: +SIMPLE = T / File conforms to NOST standard +BITPIX = -32 / Bits per pixel +NAXIS = 2 / No data is associated with this header +NAXIS1 = 72 / Length of data axis 1 +NAXIS2 = 36 / Length of data axis 2 +EXTEND = T / Extensions may be present +COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy +COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H +CTYPE1 = 'RA---CAR' / RA---%%%, %%% is the projection, e.g., AIT +CRPIX1 = 36.5 / Reference pixel +CRVAL1 = 266.41683 / RA at the reference pixel +CDELT1 = -5. / X-axis incr per pixel at ref pixel (deg) +CUNIT1 = 'deg ' / Physical unit of X-axis +CTYPE2 = 'DEC--CAR' / DEC---%%%, %%% is the projection, e.g., AIT +CRPIX2 = 18.5 / Reference pixel +CRVAL2 = -29.00781 / DEC at the reference pixel +CDELT2 = 5. / Y-axis incr per pixel at ref pixel (deg) +CUNIT2 = 'deg ' / Physical unit of Y-axis +CROTA2 = 0. / Image rotation (deg) +DATE = '2011-03-16T11:50:24' / +TELESCOP= 'GLAST ' / Name of telescope generating data +INSTRUME= 'LAT ' / Name of instrument generating data +EQUINOX = 2000. / Equinox of RA & DEC specifications +CREATOR = 'gtmodel ' / Software creating file +HISTORY $Id: LatCountsMapTemplate,v 1.2 2004/09/24 03:54:20 jc +HISTORY hiang E +CHECKSUM= 'AU2LCU0IAU0IAU0I' / HDU checksum updated 2011-03-16T15:51:49 +DATASUM = '2800807754' / data unit checksum updated 2011-03-16T15:51:49 +DSTYP1 = 'TIME ' +DSUNI1 = 's ' +DSVAL1 = 'TABLE ' +DSREF1 = ':GTI ' +DSTYP2 = 'ENERGY ' +DSUNI2 = 'MeV ' +DSVAL2 = '100:1000' +DSTYP3 = 'EVENT_CLASS' +DSUNI3 = 'dimensionless' +DSVAL3 = '4:4 ' +DSTYP4 = 'ZENITH_ANGLE' +DSUNI4 = 'deg ' +DSVAL4 = '0:105 ' +NDSKEYS = 4 +FILENAME= 'CAR_model.fits' +HISTORY File modified by user 'jsperki1' with fv on 2011-03-16T12:00:29 +HISTORY File modified by user 'jsperki1' with fv on 2011-03-16T12:01:49 +END diff --git a/ast_tester/joye_car_headers/CHIPASS_Equ.head b/ast_tester/joye_car_headers/CHIPASS_Equ.head new file mode 100644 index 0000000..5356fa5 --- /dev/null +++ b/ast_tester/joye_car_headers/CHIPASS_Equ.head @@ -0,0 +1,39 @@ +FITS headers in CHIPASS_Equ.fits: +SIMPLE = T / file does conform to FITS standard +BITPIX = -32 / IEEE (big-endian) 32-bit floating point data +NAXIS = 2 / number of data axes +NAXIS1 = 5401 / length of data axis 1 +NAXIS2 = 1741 / length of data axis 2 +EXTEND = T / FITS dataset may contain extensions +COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy +COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H +BUNIT = 'mK ' / Using 438.5 mK/(Jy/beam) plus 3300 mK offset +CTYPE1 = 'RA---CAR' +CRPIX1 = 2701. +CDELT1 = -0.0666666666666667 +CRVAL1 = 180. +CTYPE2 = 'DEC--CAR' +CRPIX2 = 1351. +CDELT2 = 0.0666666666666667 +CRVAL2 = 0. +LONPOLE = 0. / Native longitude of celestial pole +LATPOLE = 90. / Native latitude of celestial pole +RADESYS = 'FK5 ' / Equatorial coordinate system +EQUINOX = 2000.0 / Equinox of equatorial coordinates +BMAJ = 0.24000 / [deg] Beam major axis +BMIN = 0.24000 / [deg] Beam minor axis +BPA = 0.0 / [deg] Beam position angle +FREQENCY= 1.3945E+09 / [Hz] Centre frequency +BANDWID = 6.4E+07 / [Hz] Bandwidth +DATE = '2013-04-20T13:20:39' / file creation date (YYYY-MM-DDThh:mm:ss UT) +COMMENT ------------------------------------------------------------------------ +COMMENT This file contains a 1.4 GHz continuum map of the sky south of dec +26 +COMMENT produced from HIPASS and ZOA data. These surveys were undertaken with +COMMENT the 13-beam multibeam system on the Parkes radio telescope. +COMMENT Details may be found in the following paper: +COMMENT Calabretta, M.R., Staveley-Smith, L., and Barnes, D.G., (2013) +COMMENT PASA (in preparation). +COMMENT ------------------------------------------------------------------------ +COMMENT THIS IS A PRE-PUBLICATION IMAGE, FOR RESTRICTED DISTRIBUTION ONLY +COMMENT ------------------------------------------------------------------------ +END diff --git a/ast_tester/joye_car_headers/car1.fattr b/ast_tester/joye_car_headers/car1.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast_tester/joye_car_headers/car1.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast_tester/joye_car_headers/car1.head b/ast_tester/joye_car_headers/car1.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast_tester/joye_car_headers/car1.head @@ -0,0 +1,32 @@ +SIMPLE = T / Standard FITS format +BITPIX = 16 / Bits per pixel +NAXIS = 2 / Number of axes +NAXIS1 = 2961 / Number pixels on axis 1 +NAXIS2 = 561 / Number pixels on axis 2 +CTYPE1 = 'GLON-CAR' / axis 1 coord type +CRVAL1 = 1.850000e+02 / coord value at CRPIX1 +CDELT1 = -1.250000e-01 / pixel spacing for axis 1 +CRPIX1 = 1.000000 / ref pixel for axis 1 +CTYPE2 = 'GLAT-CAR' / axis 2 coord type +CRVAL2 = -3.500000e+01 / coord value at CRPIX2 +CDELT2 = 1.250000e-01 / pixel spacing for axis 2 +CRPIX2 = 1.000000 / ref pixel for axis 2 +BSCALE = 2.610167e-02 / real = int*bscale + bzero +BZERO = 8.321995e+02 / +DATAMIN = -6.326761e+00 / minimum real value +DATAMAX = 1.670731e+03 / maximum real value +BLANK = -32768 / missing data flag +COMMENT PARENT DISK FILE: Wco_DHT2001.fits +COMMENT temp =colscales( CHANGE-ME , -185.0000, 185.0000) +COMMENT coords=rowscales( temp, 35.0000, -35.0000) +COMMENT Written by MacFITS +COMMENT Created: Tuesday, 14 August, 2001 01:18:06 PM +COMMENT Whole-Galaxy velocity-integrated CO(1-0) map (Fig. 2) from +COMMENT "The Milky Way in Molecular Clouds: A New Complete CO Survey" +COMMENT T. M. Dame, Dap Hartmann, & P. Thaddeus (2001), ApJ, 547, 792. +COMMENT WARNING: Both the angular resolution and the sensitivity varies +COMMENT from region to region in this map: see Fig. 1 and Table 1 from +COMMENT the paper above. Moment masking and clipping were used as +COMMENT necessary to keep the noise in the map below ~1.5 K km/s. +COMMENT See Section 2.2 for details. +END diff --git a/ast_tester/joye_car_headers/car2.fattr b/ast_tester/joye_car_headers/car2.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast_tester/joye_car_headers/car2.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast_tester/joye_car_headers/car2.head b/ast_tester/joye_car_headers/car2.head new file mode 100644 index 0000000..bec8d59 --- /dev/null +++ b/ast_tester/joye_car_headers/car2.head @@ -0,0 +1,32 @@ +SIMPLE = T / Standard FITS format +BITPIX = 16 / Bits per pixel +NAXIS = 2 / Number of axes +NAXIS1 = 2961 / Number pixels on axis 1 +NAXIS2 = 561 / Number pixels on axis 2 +CTYPE1 = 'GLON-CAR' / axis 1 coord type +CRVAL1 = 1.850000e+02 / coord value at CRPIX1 +CDELT1 = -1.250000e-01 / pixel spacing for axis 1 +CRPIX1 = 1.000000 / ref pixel for axis 1 +CTYPE2 = 'GLAT-CAR' / axis 2 coord type +CRVAL2 = -3.500000e+01 / coord value at CRPIX2 +CDELT2 = 1.250000e-01 / pixel spacing for axis 2 +CRPIX2 = 1.000000 / ref pixel for axis 2 +BSCALE = 2.610167e-02 / real = int*bscale + bzero +BZERO = 8.321995e+02 / +DATAMIN = -6.326761e+00 / minimum real value +DATAMAX = 1.670731e+03 / maximum real value +BLANK = -32768 / missing data flag +COMMENT PARENT DISK FILE: Wco_DHT2001.fits +COMMENT temp =colscales( CHANGE-ME , -185.0000, 185.0000) +COMMENT coords=rowscales( temp, 35.0000, -35.0000) +COMMENT Written by MacFITS +COMMENT Created: Tuesday, 14 August, 2001 01:18:06 PM +COMMENT Whole-Galaxy velocity-integrated CO(1-0) map (Fig. 2) from +COMMENT "The Milky Way in Molecular Clouds: A New Complete CO Survey" +COMMENT T. M. Dame, Dap Hartmann, & P. Thaddeus (2001), ApJ, 547, 792. +COMMENT WARNING: Both the angular resolution and the sensitivity varies +COMMENT from region to region in this map: see Fig. 1 and Table 1 from +COMMENT the paper above. Moment masking and clipping were used as +COMMENT necessary to keep the noise in the map below ~1.5 K km/s. +COMMENT See Section 2.2 for details. +END diff --git a/ast_tester/joye_car_headers/car3.head b/ast_tester/joye_car_headers/car3.head new file mode 100644 index 0000000..de6c76e --- /dev/null +++ b/ast_tester/joye_car_headers/car3.head @@ -0,0 +1,8 @@ +CTYPE1 = 'GLON-CAR' +CRVAL1 = 1.850000e+02 +CDELT1 = -1.250000e-01 +CRPIX1 = 200.000000 +CTYPE2 = 'GLAT-CAR' +CRVAL2 = -3.500000e+01 +CDELT2 = 1.250000e-01 +CRPIX2 = 200.000000 diff --git a/ast_tester/joye_car_headers/car4.fattr b/ast_tester/joye_car_headers/car4.fattr new file mode 100644 index 0000000..5f408d2 --- /dev/null +++ b/ast_tester/joye_car_headers/car4.fattr @@ -0,0 +1 @@ +carlin=1 diff --git a/ast_tester/joye_car_headers/car4.head b/ast_tester/joye_car_headers/car4.head new file mode 100644 index 0000000..95cd97a --- /dev/null +++ b/ast_tester/joye_car_headers/car4.head @@ -0,0 +1,38 @@ +SIMPLE = T / Written by IDL: Thu Apr 27 08:52:27 2000 +BITPIX = -32 / +NAXIS = 2 / +NAXIS1 = 951 / +NAXIS2 = 1851 / +CRPIX1 = 211076.0 / +CRVAL1 = 0.000000000 / +CTYPE1 = 'GLON-CAR' / +CRPIX2 = 475.39400 / +CRVAL2 = 0.000000000 / +CTYPE2 = 'GLAT-CAR' / +CROTA2 = 0.000000000 / +LONPOLE = 0.00000 / Defined by Greisen and Calabretta +CD1_1 = -0.0016666667 / +CD1_2 = 0.00000 / +CD2_1 = 0.00000 / +CD2_2 = 0.0016666667 / +WAVELENG= 8.28000e-06 / Isophotal wavelength in meters +BUNIT = 'W/m^2-sr' / +SECURITY= 'Unclassified' / +TELESCOP= 'MSX ' / +INSTRUME= 'SPIRITIII' / +ORIGIN = 'AFRL-VSBC' / +MJD-OBS = 50295.5 / Mean modified Julian date of observation +DATE = '16/02/2000' / Date of file generation +HISTORY Convert Version 6.2.X +HISTORY Level-2A Deshadow Version 4.0 +HISTORY Level-2A Saturation Correction Version 1.0 +HISTORY Pointing Convert Version 6.0.1 +HISTORY Makeimage Version 3.2 +HISTORY Destriped +HISTORY Data collected in J2000 FK5 coordinates +HISTORY Data samples transformed to Galactic coordinates +HISTORY and convolved onto image grid using sigma=3.0 arcsec +HISTORY Gaussian kernel +HISTORY Master Plate: GP_351.0_+0.0_A.fits +HISTORY Written by IDL: 28-Jan-2000 17:44:54.00 +END diff --git a/ast_tester/joye_car_headers/car5.head b/ast_tester/joye_car_headers/car5.head new file mode 100644 index 0000000..2be3a64 --- /dev/null +++ b/ast_tester/joye_car_headers/car5.head @@ -0,0 +1,38 @@ +SIMPLE = T / Written by IDL: Thu Apr 27 08:52:27 2000 +BITPIX = -32 / +NAXIS = 2 / +NAXIS1 = 951 / +NAXIS2 = 1851 / +CRPIX1 = -4932.0204 / +CRVAL1 = 0.000000000 / +CTYPE1 = 'GLON-CAR' / +CRPIX2 = 475.39400 / +CRVAL2 = 0.000000000 / +CTYPE2 = 'GLAT-CAR' / +CROTA2 = 0.000000000 / +LONPOLE = 0.00000 / Defined by Greisen and Calabretta +CD1_1 = -0.0016666667 / +CD1_2 = 0.00000 / +CD2_1 = 0.00000 / +CD2_2 = 0.0016666667 / +WAVELENG= 8.28000e-06 / Isophotal wavelength in meters +BUNIT = 'W/m^2-sr' / +SECURITY= 'Unclassified' / +TELESCOP= 'MSX ' / +INSTRUME= 'SPIRITIII' / +ORIGIN = 'AFRL-VSBC' / +MJD-OBS = 50295.5 / Mean modified Julian date of observation +DATE = '16/02/2000' / Date of file generation +HISTORY Convert Version 6.2.X +HISTORY Level-2A Deshadow Version 4.0 +HISTORY Level-2A Saturation Correction Version 1.0 +HISTORY Pointing Convert Version 6.0.1 +HISTORY Makeimage Version 3.2 +HISTORY Destriped +HISTORY Data collected in J2000 FK5 coordinates +HISTORY Data samples transformed to Galactic coordinates +HISTORY and convolved onto image grid using sigma=3.0 arcsec +HISTORY Gaussian kernel +HISTORY Master Plate: GP_351.0_+0.0_A.fits +HISTORY Written by IDL: 28-Jan-2000 17:44:54.00 +END diff --git a/ast_tester/joye_car_headers/cmap_3years_GP_D2.head b/ast_tester/joye_car_headers/cmap_3years_GP_D2.head new file mode 100644 index 0000000..d27e368 --- /dev/null +++ b/ast_tester/joye_car_headers/cmap_3years_GP_D2.head @@ -0,0 +1,162 @@ +FITS headers in cmap_3years_GP_D2.fits: +SIMPLE = T / File conforms to NOST standard +BITPIX = 32 / Bits per pixel +NAXIS = 2 / No data is associated with this header +NAXIS1 = 1800 / Length of data axis 1 +NAXIS2 = 500 / Length of data axis 2 +EXTEND = T / Extensions may be present +COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy +COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H +CTYPE1 = 'GLON-CAR' / RA---%%%, %%% represents the projection method +CRPIX1 = 900.5 / Reference pixel +CRVAL1 = 0. / RA at the reference pixel +CDELT1 = -0.2 / X-axis incr per pixel of physical coord at posi +CUNIT1 = 'deg ' / Physical unit of X-axis +CTYPE2 = 'GLAT-CAR' / DEC---%%%, %%% represents the projection method +CRPIX2 = 250.5 / Reference pixel +CRVAL2 = 0. / DEC at the reference pixel +CDELT2 = 0.2 / Y-axis incr per pixel of physical coord at posi +CUNIT2 = 'deg ' / Physical unit of Y-axis +CROTA2 = 0. / Image rotation (deg) +DATE = '2011-11-18T09:38:44' / file creation date (YYYY-MM-DDThh:mm:ss U +FILENAME= 'cmap_3years_GP_D2.fits' / +TELESCOP= 'GLAST ' / name of telescope generating data +INSTRUME= 'LAT ' / name of instrument generating data +DATE-OBS= '2008-08-04T15:43:37.6089' / start date and time of the observation (U +DATE-END= '2011-07-31T23:59:59.0000' / end date and time of the observation (UTC +TIMEUNIT= 's ' / units for the time related keywords +TIMEZERO= 0. / clock correction +TIMESYS = 'TT ' / type of time system that is used +TIMEREF = 'LOCAL ' / reference frame used for times +CLOCKAPP= F / whether a clock drift correction has been appli +GPS_OUT = F / whether GPS time was unavailable at any time du +NDSKEYS = 5 +EQUINOX = 2000. / Equinox of RA & DEC specifications +OBSERVER= 'Peter Michelson' / GLAST/LAT PI +CREATOR = 'gtbin ' / Software and version creating file +HISTORY LatCountMapTemplate,v 1.3 2005/04/05 21:06:39 peachey +HISTORY Exp +CHECKSUM= 'HG8SH95PHE5PH95P' / HDU checksum updated 2011-11-18T08:38:44 +DATASUM = '3748023 ' / data unit checksum updated 2011-11-18T08:38:44 +DSTYP1 = 'TIME ' +DSUNI1 = 's ' +DSVAL1 = 'TABLE ' +DSREF1 = ':GTI ' +DSTYP2 = 'CONVERSION_TYPE' +DSUNI2 = 'dimensionless' +DSVAL2 = '1:1 ' +DSTYP3 = 'ENERGY ' +DSUNI3 = 'MeV ' +DSVAL3 = '2000:10000' +DSTYP4 = 'EVENT_CLASS' +DSUNI4 = 'dimensionless' +DSVAL4 = '4: ' +DSTYP5 = 'ZENITH_ANGLE' +DSUNI5 = 'deg ' +DSVAL5 = '0:100 ' +HISTORY The following history was copied from input files by gtbin +HISTORY ------------------------------------------------------------------------ +HISTORY BEGIN history copied from ft1_D2_Front.fits[EVENTS] +HISTORY ------------------------------------------------------------------------ +HISTORY Input merit file: /scratch/glastmp/P120-FT1/128/r0240311566_v120_merit.r +HISTORY oot +HISTORY Filter string: (FT1EventClass!=0) && (EvtElapsedTime >= 240311568) && ( +HISTORY EvtElapsedTime <= 240314221) +HISTORY CFITSIO used the following filtering expression to create this table: +HISTORY /scratch/glastmp/P120-FT1/128/foo.fit[EVENTS][gtifilter("/scratch/glastm +HISTORY p/P120-FT1/128/gll_xp_p120_r0240311566_v122.fit_tempgti")] +HISTORY CFITSIO used the following filtering expression to create this table: +HISTORY data/august_08-ft1.fits[EVENTS][100 <= ENERGY && ENERGY <= 1000000 && 4 +HISTORY <= EVENT_CLASS && 0 <= TIME && TIME <= 1000000000 && 0 <= ZENITH_ANGLE & +HISTORY & ZENITH_ANGLE <= 100 && gtifilter()] +HISTORY Filter string: 100 <= ENERGY && ENERGY <= 1000000 && 4 <= EVENT_CLASS && +HISTORY 0 <= TIME && TIME <= 1000000000 && 0 <= ZENITH_ANGLE && ZENITH_ANGLE <= +HISTORY 100 && gtifilter() +HISTORY CFITSIO used the following filtering expression to create this table: +HISTORY data/august_08_z100_ft1.fits[EVENTS][gtifilter("data/august_08-ft1.fits_ +HISTORY rocking_tempgti")] +HISTORY Filter string: 0 <= CONVERSION_TYPE && CONVERSION_TYPE <= 0 && 1000 <= E +HISTORY NERGY && ENERGY <= 5000 && 4 <= EVENT_CLASS && 0 <= TIME && TIME <= 1000 +HISTORY 000000 && 0 <= ZENITH_ANGLE && ZENITH_ANGLE <= 100 && gtifilter() +HISTORY ------------------------------------------------------------------------ +HISTORY END copied history +HISTORY ------------------------------------------------------------------------ +HISTORY ------------------------------------------------------------------------ +HISTORY BEGIN history copied from ft1_D2_Back.fits[EVENTS] +HISTORY ------------------------------------------------------------------------ +HISTORY Input merit file: /scratch/glastmp/P120-FT1/128/r0240311566_v120_merit.r +HISTORY oot +HISTORY Filter string: (FT1EventClass!=0) && (EvtElapsedTime >= 240311568) && ( +HISTORY EvtElapsedTime <= 240314221) +HISTORY CFITSIO used the following filtering expression to create this table: +HISTORY /scratch/glastmp/P120-FT1/128/foo.fit[EVENTS][gtifilter("/scratch/glastm +HISTORY p/P120-FT1/128/gll_xp_p120_r0240311566_v122.fit_tempgti")] +HISTORY CFITSIO used the following filtering expression to create this table: +HISTORY data/august_08-ft1.fits[EVENTS][100 <= ENERGY && ENERGY <= 1000000 && 4 +HISTORY <= EVENT_CLASS && 0 <= TIME && TIME <= 1000000000 && 0 <= ZENITH_ANGLE & +HISTORY & ZENITH_ANGLE <= 100 && gtifilter()] +HISTORY Filter string: 100 <= ENERGY && ENERGY <= 1000000 && 4 <= EVENT_CLASS && +HISTORY 0 <= TIME && TIME <= 1000000000 && 0 <= ZENITH_ANGLE && ZENITH_ANGLE <= +HISTORY 100 && gtifilter() +HISTORY CFITSIO used the following filtering expression to create this table: +HISTORY data/august_08_z100_ft1.fits[EVENTS][gtifilter("data/august_08-ft1.fits_ +HISTORY rocking_tempgti")] +HISTORY Filter string: 1 <= CONVERSION_TYPE && CONVERSION_TYPE <= 1 && 2000 <= E +HISTORY NERGY && ENERGY <= 10000 && 4 <= EVENT_CLASS && 0 <= TIME && TIME <= 100 +HISTORY 0000000 && 0 <= ZENITH_ANGLE && ZENITH_ANGLE <= 100 && gtifilter() +HISTORY ------------------------------------------------------------------------ +HISTORY END copied history +HISTORY ------------------------------------------------------------------------ +END +XTENSION= 'BINTABLE' / Binary table extension +BITPIX = 8 / Bits per pixel +NAXIS = 2 / Required value +NAXIS1 = 16 / Number of bytes per row +NAXIS2 = 17159 / Number of rows +PCOUNT = 0 / Normally 0 (no varying arrays) +GCOUNT = 1 / Required value +TFIELDS = 2 / Number of columns in table +TTYPE1 = 'START ' / Start time of an interval +TFORM1 = 'D ' / Data format of this field +TTYPE2 = 'STOP ' / Stop time of an interval +TFORM2 = 'D ' / Data format of this field +EXTNAME = 'GTI ' / Extension name +TELESCOP= 'GLAST ' / name of telescope generating data +INSTRUME= 'LAT ' / name of instrument generating data +MJDREFI = 51910. / Integer part of MJD corresponding to SC clock s +MJDREFF = '7.428703703703703D-4' / Fractional part of MJD corresponding to SC c +TSTART = 239557418.608944 / mission time of the start of the observation +TSTOP = 333849601. / mission time of the end of the observation +EXPOSURE= 69878014.2344566 / Integration time (in seconds) for the PHA data +HDUCLASS= 'OGIP ' / File format is OGIP standard +HDUCLAS1= 'GTI ' / Contains Good Time Intervals +HDUVERS = '1.2.0 ' / Version of file format +DATE-OBS= '2008-08-04T15:43:37.6089' / start date and time of the observation (U +DATE-END= '2011-07-31T23:59:59.0000' / end date and time of the observation (UTC +TIMEUNIT= 's ' / units for the time related keywords +TIMEZERO= 0. / clock correction +TIMESYS = 'TT ' / type of time system that is used +TIMEREF = 'LOCAL ' / reference frame used for times +CLOCKAPP= F / whether a clock drift correction has been appli +GPS_OUT = F / whether GPS time was unavailable at any time du +CREATOR = 'gtbin ' / Software and version creating file +HISTORY LatCountMapTemplate,v 1.3 2005/04/05 21:06:39 peachey +HISTORY Exp + + + + +TUNIT1 = 's ' / Unit of this field + +TUNIT2 = 's ' / Unit of this field +EXTVER = 1 / auto assigned by template parser +CHECKSUM= 'S4FDU3FBS3FBS3FB' / HDU checksum updated 2011-11-18T08:38:44 +DATASUM = '1639695499' / data unit checksum updated 2011-11-18T08:38:44 +HISTORY The following history was copied from input files by gtbin +HISTORY ------------------------------------------------------------------------ +HISTORY No history available in ft1_D2_Front.fits[GTI] +HISTORY ------------------------------------------------------------------------ +HISTORY ------------------------------------------------------------------------ +HISTORY No history available in ft1_D2_Back.fits[GTI] +HISTORY ------------------------------------------------------------------------ +END diff --git a/ast_tester/joye_car_headers/doit b/ast_tester/joye_car_headers/doit new file mode 100755 index 0000000..a78a590 --- /dev/null +++ b/ast_tester/joye_car_headers/doit @@ -0,0 +1,17 @@ +#!/bin/tcsh + +foreach n (*.head) + set bn = `basename $n .head` + echo "Doing $bn" + + set fat = "${bn}.fattr" + if( -e "$fat" ) then + set fattr = `cat $fat` + else + set fattr = "" + endif + + $HOME/work/ast/fplottest $n ps_l "$fattr" n + mv gks74.ps ${bn}.ps +end + diff --git a/ast_tester/joye_car_headers/total_hi.head b/ast_tester/joye_car_headers/total_hi.head new file mode 100644 index 0000000..2ed691c --- /dev/null +++ b/ast_tester/joye_car_headers/total_hi.head @@ -0,0 +1,35 @@ +SIMPLE = T +BITPIX = -32 +NAXIS = 2 +NAXIS1 = 721 +NAXIS2 = 361 +BUNIT = 'K km/s ' +DATAMAX = 11847.260742187500 +DATAMIN = -368.407501220703 +BZERO = 0.000000000000 +BSCALE = 1.000000000000 +BLANK = -32768 +OBJECT = 'Leiden/Dwingeloo HI Survey; (l,b) total HI' +TELESCOP= 'Dwingeloo 25-m' +OBSERVER= 'Dap Hartmann' +DATE-OBS= '02/09/93' +DATE = '20/01/96' +CTYPE1 = 'GLON-CAR' +CRVAL1 = 360.000000000000 +CRPIX1 = 1 +CDELT1 = -0.500000000000 +CTYPE2 = 'GLAT-CAR' +CRVAL2 = -90.000000000000 +CRPIX2 = 1 +CDELT2 = 0.500000000000 +COMMENT = +-------------------------------------------------------------+ +COMMENT = | (l,b) map, integrated between -450 km/s < V_lsr < +400 km/s | +COMMENT = | To Convert to N_HI, multiply by 1.8224e18 K km s^-1 cm^-2 | +COMMENT = +-------------------------------------------------------------+ +COMMENT = +COMMENT = +-------------------------------------------------+ +COMMENT = | The Leiden/Dwingeloo Survey of HI in the Galaxy | +COMMENT = | Dap Hartmann & W.B.Burton | +COMMENT = | Leiden Observatory | +COMMENT = | Cambridge University Press 1997 | +END diff --git a/ast_tester/longslit.fits-pc b/ast_tester/longslit.fits-pc new file mode 100644 index 0000000..2adf05a --- /dev/null +++ b/ast_tester/longslit.fits-pc @@ -0,0 +1,18 @@ +NAXIS = 3 +NAXIS1 = 1024 +NAXIS2 = 2048 +NAXIS3 = 1 +CRPIX1 = 1 +CRPIX2 = 1024.5 +CRPIX3 = 1 +CDELT1 = 100.0 / delta lambda = 100 nm +CDELT2 = -0.0005555555 / sigma = 2 arcsec +CDELT3 = 1 +CTYPE1 = 'WAVE ' +CTYPE2 = 'RA---ARC' +CTYPE3 = 'DEC--ARC' +CUNIT1 = 'nm ' +CRVAL1 = 1000.0 / Lambda-ref = 1000 nm +CRVAL2 = 150.000 / RA-ref = 150 deg (10:00:00) +CRVAL3 = -35.0 / Dec-ref = -35:00:00 +LONPOLE = 120.0 / rho = 30 degs diff --git a/ast_tester/longslit.fits-wcs b/ast_tester/longslit.fits-wcs new file mode 100644 index 0000000..cff2ca0 --- /dev/null +++ b/ast_tester/longslit.fits-wcs @@ -0,0 +1,22 @@ +NAXIS = 3 +NAXIS1 = 1024 +NAXIS2 = 2048 +NAXIS3 = 1 +CRPIX1 = 1.0 / Reference pixel on axis 1 +CRPIX2 = 1024.5 / Reference pixel on axis 2 +CRPIX3 = 1.0 / Reference pixel on axis 3 +CTYPE1 = 'WAVE ' / Type of co-ordinate on axis 1 +CTYPE2 = 'RA---ARC' / Type of co-ordinate on axis 2 +CTYPE3 = 'DEC--ARC' / Type of co-ordinate on axis 3 +CUNIT1 = 'nm ' / Units for axis 1 +CRVAL1 = 1000.0 / Lambda-ref = 1000 nm +CRVAL2 = 150.0 / RA-ref = 150 deg (10:00:00) +CRVAL3 = -35.0 / Dec-ref = -35:00:00 +LONPOLE = 120.0 / rho = 30 degs +CD1_1 = 100.0 / Transformation matrix element +CD2_2 = -5.555555E-4 / Transformation matrix element +CD3_3 = 1.0 / Transformation matrix element +PV2_3 = 120.0 / Projection parameter +RADESYS = 'ICRS ' / Reference frame for RA/DEC values +SPECSYS = 'HELIOCEN' / Standard of rest for spectral axis +VELOSYS = -20038.807 / [m/s] Topo. apparent velocity of rest frame diff --git a/ast_tester/makeplot b/ast_tester/makeplot new file mode 100755 index 0000000..760fac9 --- /dev/null +++ b/ast_tester/makeplot @@ -0,0 +1,47 @@ +#!/bin/tcsh + +if( "$1" == "" ) then + echo "Usage: makeplot " + echo " (e.g. makeplot serpens)" + exit +endif + +gfortran -fno-second-underscore -w -g -fno-range-check -o plotter plotter.f \ + -I$GITSTAR/include -L$GITSTAR/lib `ast_link -pgp -ems` `pgplot_link` + +set bn = $1 + +set atfile = "${bn}.attr" +if( -e $atfile ) then + set attr1 = `cat $atfile` +else + set attr1 = ' ' +endif + +set atfile = "${bn}.fattr" +if( -e $atfile ) then + set attr2 = `cat $atfile` +else + set attr2 = ' ' +endif + +set boxfile = "${bn}.box" +if( -e $boxfile ) then + set box = `cat $boxfile` +else + set box = ' ' +endif + + +set psfile = "${bn}-new.ps" +echo "plotter $bn.head '$attr1' '$attr2' a.ps $box" +plotter $bn.head "$attr1" "$attr2" a.ps $box + +if( -e $GITSTAR/bin/psmerge ) then + $GITSTAR/bin/psmerge -t300x300 -r90 a.ps > $psfile +else + cp a.ps $psfile +endif + +gv $psfile -orientation=landscape + diff --git a/ast_tester/maketest b/ast_tester/maketest new file mode 100755 index 0000000..5891121 --- /dev/null +++ b/ast_tester/maketest @@ -0,0 +1,35 @@ +#!/bin/tcsh + +if( "$1" == "" ) then + echo "Usage: maketest " + echo " (e.g. maketest regions)" + exit +endif + +if( ! $?LDFLAGS ) then + setenv LDFLAGS "" +endif + +set a = "test$1" + +echo "Building $a" +if( "$a" == "testplot3d" ) then + gfortran -fno-second-underscore -w -g -fno-range-check $LDFLAGS -o testplot3d testplot3d.f \ + -L$GITSTAR/lib -I$GITSTAR/include \ + `ast_link -ems -pgplot3d` `sla_link` `chr_link` `err_link` + +else if( -e "$a.f" ) then + gfortran -fno-second-underscore -w -g -fno-range-check $LDFLAGS -o $a $a.f -L$GITSTAR/lib -I$GITSTAR/include \ + `ast_link -ems` `chr_link` `err_link` `prm_link` `psx_link` + +else if( -e "$a.c" ) then + gcc -o $a -g $a.c -I.. -DHAVE_CONFIG_H -L$GITSTAR/lib $LDFLAGS `ast_link` + +else + echo "Cannot find $a.f or $a.c" + +endif + +echo "Running $a" +$a + diff --git a/ast_tester/origin.attr b/ast_tester/origin.attr new file mode 100644 index 0000000..277e359 --- /dev/null +++ b/ast_tester/origin.attr @@ -0,0 +1 @@ +format(1)=ghms,format(2)=gdm,edge(2)=r,tickall=0,grid=1 diff --git a/ast_tester/origin.box b/ast_tester/origin.box new file mode 100644 index 0000000..9b5eb80 --- /dev/null +++ b/ast_tester/origin.box @@ -0,0 +1 @@ +0 0 500 500 diff --git a/ast_tester/origin.head b/ast_tester/origin.head new file mode 100644 index 0000000..88ea783 --- /dev/null +++ b/ast_tester/origin.head @@ -0,0 +1,18 @@ +SIMPLE = T / file does conform to FITS standard +BITPIX = 16 / number of bits per data pixel +NAXIS = 2 / number of data axes +NAXIS1 = 500 / length of data axis 1 +NAXIS2 = 500 / length of data axis 2 +EPOCH = 1.977780E+03 / Epoch of observation +RADECSYS= 'ICRS ' / Reference frame for RA/DEC in original file +CRVAL1 = 0.0 / Axis 1 reference value +CRPIX1 = 250 / Axis 1 pixel value +CTYPE1 = 'RA---ARC' / Quantity represented by axis 1 +CRVAL2 = 0.0 / Axis 2 reference value +CRPIX2 = 250 / Axis 2 pixel value +CTYPE2 = 'DEC--ARC' / Quantity represented by axis 2 +CDELT1 = 0.001 / Co-ordinate transformation matrix +CDELT2 = 0.001 / Co-ordinate transformation matrix +EQUINOX = 2.000000E+03 / Julian reference frame equinox + +END diff --git a/ast_tester/origin.ps b/ast_tester/origin.ps new file mode 100644 index 0000000..65d31e2 --- /dev/null +++ b/ast_tester/origin.ps @@ -0,0 +1,903 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Thu Feb 19 19:50:49 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 19/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 76.0 113.2 155.1 197.0 238.9 280.8 322.7 + 364.6 406.5 448.3 467.0 ]plong + s[ 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 297.2 ][ 76.0 113.2 155.1 197.0 238.9 280.8 322.7 + 364.6 406.5 448.3 467.0 ]plong + s[ 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 394.9 ][ 76.0 113.2 155.1 197.0 238.9 280.8 322.7 + 364.6 406.5 448.3 467.0 ]plong + s[ 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 492.7 ][ 76.0 113.2 155.1 197.0 238.9 280.8 322.7 + 364.6 406.5 448.3 467.0 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 76.0 113.2 155.1 197.0 238.9 280.8 322.7 + 364.6 406.5 448.3 467.0 ]plong + s[ 199.5 220.4 283.2 346.1 408.9 471.7 534.5 590.4 ][ 141.2 141.2 141.2 141.2 141.2 141.2 141.2 141.2 ]plong + s[ 199.5 220.4 283.2 346.1 408.9 471.7 534.5 590.4 ][ 206.3 206.3 206.3 206.3 206.3 206.3 206.3 206.3 ]plong + s[ 199.5 220.4 283.2 346.1 408.9 471.7 534.5 590.4 ][ 271.5 271.5 271.5 271.5 271.5 271.5 271.5 271.5 ]plong + s[ 199.5 220.4 283.2 346.1 408.9 471.7 534.5 590.4 ][ 336.6 336.6 336.6 336.6 336.6 336.6 336.6 336.6 ]plong + s[ 199.5 220.4 283.2 346.1 408.9 471.7 534.5 590.4 ][ 401.8 401.8 401.8 401.8 401.8 401.8 401.8 401.8 ]plong + 215.7 78.8 215.7 76.0 2 pls + 232.0 78.8 232.0 76.0 2 pls + 248.3 78.8 248.3 76.0 2 pls + 264.6 78.8 264.6 76.0 2 pls + 280.9 78.8 280.9 76.0 2 pls + 313.5 78.8 313.5 76.0 2 pls + 329.8 78.8 329.8 76.0 2 pls + 346.1 78.8 346.1 76.0 2 pls + 362.3 78.8 362.3 76.0 2 pls + 378.6 78.8 378.6 76.0 2 pls + 411.2 78.8 411.2 76.0 2 pls + 427.5 78.8 427.5 76.0 2 pls + 443.8 78.8 443.8 76.0 2 pls + 460.1 78.8 460.1 76.0 2 pls + 476.4 78.8 476.4 76.0 2 pls + 508.9 78.8 508.9 76.0 2 pls + 525.2 78.8 525.2 76.0 2 pls + 541.5 78.8 541.5 76.0 2 pls + 557.8 78.8 557.8 76.0 2 pls + 574.1 78.8 574.1 76.0 2 pls + 587.7 89.0 590.4 89.0 2 pls + 587.7 102.1 590.4 102.1 2 pls + 587.7 115.1 590.4 115.1 2 pls + 587.7 128.1 590.4 128.1 2 pls + 587.7 154.2 590.4 154.2 2 pls + 587.7 167.2 590.4 167.2 2 pls + 587.7 180.3 590.4 180.3 2 pls + 587.7 193.3 590.4 193.3 2 pls + 587.7 219.4 590.4 219.4 2 pls + 587.7 232.4 590.4 232.4 2 pls + 587.7 245.4 590.4 245.4 2 pls + 587.7 258.5 590.4 258.5 2 pls + 587.7 284.5 590.4 284.5 2 pls + 587.7 297.6 590.4 297.6 2 pls + 587.7 310.6 590.4 310.6 2 pls + 587.7 323.6 590.4 323.6 2 pls + 587.7 349.7 590.4 349.7 2 pls + 587.7 362.7 590.4 362.7 2 pls + 587.7 375.7 590.4 375.7 2 pls + 587.7 388.8 590.4 388.8 2 pls + 587.7 414.8 590.4 414.8 2 pls + 587.7 427.9 590.4 427.9 2 pls + 587.7 440.9 590.4 440.9 2 pls + 587.7 453.9 590.4 453.9 2 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 283.2 255.3 227.4 199.5 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 160.04 56.34 moveto[ 160.0 160.0 178.4 178.4 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 162.5 162.5 163.0 163.4 164.2 165.9 166.7 167.1 167.6 167.6 167.1 166.3 162.1 168.0 ][ 64.7 65.1 65.9 66.4 + 66.8 66.8 66.4 65.9 65.1 64.3 63.4 62.2 58.0 58.0 ]plong + s[ 171.3 175.9 173.4 174.7 175.5 175.9 176.3 176.3 175.9 175.1 173.8 172.6 171.3 170.9 170.5 ][ 66.8 66.8 63.4 + 63.4 63.0 62.6 61.4 60.5 59.3 58.4 58.0 58.0 58.4 58.8 59.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 179.06 63.63 moveto[ 179.1 179.1 185.2 185.2 ][ 63.6 72.1 72.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 180.5 64.8 180.5 70.9 2 pls + s[ 180.5 181.4 182.0 182.9 183.4 183.7 183.7 ][ 67.7 68.6 68.9 68.9 68.6 67.7 64.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 185.85 56.34 moveto[ 185.8 185.8 203.8 203.8 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 192.9 188.8 188.4 188.8 190.0 191.3 192.5 193.4 193.8 193.8 193.4 192.5 191.3 190.0 188.8 188.4 187.9 ][ 66.8 + 66.8 63.0 63.4 63.9 63.9 63.4 62.6 61.4 60.5 59.3 58.4 58.0 58.0 58.4 58.8 59.7 ]plong + s[ 201.7 201.3 200.5 199.2 198.8 197.5 196.7 196.3 196.3 196.7 197.5 198.8 199.2 200.5 201.3 201.7 201.7 201.3 + 200.5 199.2 198.4 197.1 196.7 ][ 63.9 62.6 61.8 61.4 61.4 61.8 62.6 63.9 64.3 65.5 66.4 66.8 66.8 + 66.4 65.5 63.9 61.8 59.7 58.4 58.0 58.0 58.4 59.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 204.67 63.63 moveto[ 204.7 204.7 214.0 214.0 ][ 63.6 70.1 70.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 206.1 64.8 206.1 68.9 2 pls + s[ 206.1 207.0 207.6 208.5 209.1 209.3 209.3 ][ 67.7 68.6 68.9 68.9 68.6 67.7 64.8 ]plong + s[ 209.3 210.2 210.8 211.7 212.3 212.6 212.6 ][ 67.7 68.6 68.9 68.9 68.6 67.7 64.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 214.47 56.34 moveto[ 214.5 214.5 232.8 232.8 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 219.1 217.8 217.0 216.6 216.6 217.0 217.8 219.1 219.9 221.2 222.0 222.4 222.4 222.0 221.2 219.9 219.1 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong + s[ 227.4 226.2 225.3 224.9 224.9 225.3 226.2 227.4 228.3 229.5 230.3 230.8 230.8 230.3 229.5 228.3 227.4 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 233.00 63.63 moveto[ 233.0 233.0 239.1 239.1 ][ 63.6 70.1 70.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 237.7 237.4 236.5 235.6 234.8 234.5 234.8 235.3 236.8 237.4 237.7 237.7 237.4 236.5 235.6 234.8 234.5 ][ 68.0 + 68.6 68.9 68.9 68.6 68.0 67.4 67.1 66.8 66.6 66.0 65.7 65.1 64.8 64.8 65.1 65.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 284.99 58.39 moveto[ 285.0 285.0 303.4 303.4 ][ 58.4 70.5 70.5 58.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 287.9 292.5 290.0 291.3 292.1 292.5 292.9 292.9 292.5 291.7 290.4 289.2 287.9 287.5 287.1 ][ 68.8 68.8 65.5 + 65.5 65.1 64.7 63.4 62.6 61.3 60.5 60.1 60.1 60.5 60.9 61.7 ]plong + s[ 297.9 296.7 295.8 295.4 295.4 295.8 296.7 297.9 298.8 300.0 300.9 301.3 301.3 300.9 300.0 298.8 297.9 ][ 68.8 + 68.4 67.2 65.1 63.8 61.7 60.5 60.1 60.1 60.5 61.7 63.8 65.1 67.2 68.4 68.8 68.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 303.52 65.68 moveto[ 303.5 303.5 309.7 309.7 ][ 65.7 72.1 72.1 65.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 308.2 307.9 307.0 306.1 305.3 305.0 305.3 305.9 307.3 307.9 308.2 308.2 307.9 307.0 306.1 305.3 305.0 ][ 70.1 + 70.6 70.9 70.9 70.6 70.1 69.5 69.2 68.9 68.6 68.0 67.7 67.1 66.8 66.8 67.1 67.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 359.68 56.34 moveto[ 359.7 359.7 369.7 369.7 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 364.3 363.0 362.2 361.8 361.8 362.2 363.0 364.3 365.1 366.4 367.2 367.6 367.6 367.2 366.4 365.1 364.3 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 370.36 63.63 moveto[ 370.4 370.4 376.5 376.5 ][ 63.6 72.1 72.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 371.8 64.8 371.8 70.9 2 pls + s[ 371.8 372.7 373.3 374.2 374.7 375.0 375.0 ][ 67.7 68.6 68.9 68.9 68.6 67.7 64.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 377.14 56.34 moveto[ 377.1 377.1 395.5 395.5 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 381.7 380.5 379.6 379.2 379.2 379.6 380.5 381.7 382.6 383.8 384.7 385.1 385.1 384.7 383.8 382.6 381.7 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong + s[ 390.1 388.8 388.0 387.6 387.6 388.0 388.8 390.1 390.9 392.2 393.0 393.4 393.4 393.0 392.2 390.9 390.1 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 395.96 63.63 moveto[ 396.0 396.0 405.3 405.3 ][ 63.6 70.1 70.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 397.4 64.8 397.4 68.9 2 pls + s[ 397.4 398.3 398.9 399.8 400.3 400.6 400.6 ][ 67.7 68.6 68.9 68.9 68.6 67.7 64.8 ]plong + s[ 400.6 401.5 402.1 403.0 403.6 403.9 403.9 ][ 67.7 68.6 68.9 68.9 68.6 67.7 64.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 405.76 56.34 moveto[ 405.8 405.8 424.1 424.1 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 410.4 409.1 408.3 407.8 407.8 408.3 409.1 410.4 411.2 412.4 413.3 413.7 413.7 413.3 412.4 411.2 410.4 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong + s[ 418.7 417.5 416.6 416.2 416.2 416.6 417.5 418.7 419.5 420.8 421.6 422.1 422.1 421.6 420.8 419.5 418.7 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 424.29 63.63 moveto[ 424.3 424.3 430.4 430.4 ][ 63.6 70.1 70.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 429.0 428.7 427.8 426.9 426.0 425.8 426.0 426.6 428.1 428.7 429.0 429.0 428.7 427.8 426.9 426.0 425.8 ][ 68.0 + 68.6 68.9 68.9 68.6 68.0 67.4 67.1 66.8 66.6 66.0 65.7 65.1 64.8 64.8 65.1 65.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 480.46 58.39 moveto[ 480.5 480.5 498.8 498.8 ][ 58.4 70.5 70.5 58.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 483.4 488.0 485.5 486.7 487.6 488.0 488.4 488.4 488.0 487.1 485.9 484.6 483.4 483.0 482.5 ][ 68.8 68.8 65.5 + 65.5 65.1 64.7 63.4 62.6 61.3 60.5 60.1 60.1 60.5 60.9 61.7 ]plong + s[ 493.4 492.2 491.3 490.9 490.9 491.3 492.2 493.4 494.2 495.5 496.3 496.7 496.7 496.3 495.5 494.2 493.4 ][ 68.8 + 68.4 67.2 65.1 63.8 61.7 60.5 60.1 60.1 60.5 61.7 63.8 65.1 67.2 68.4 68.8 68.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 498.99 65.68 moveto[ 499.0 499.0 505.1 505.1 ][ 65.7 72.1 72.1 65.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 503.7 503.4 502.5 501.6 500.7 500.4 500.7 501.3 502.8 503.4 503.7 503.7 503.4 502.5 501.6 500.7 500.4 ][ 70.1 + 70.6 70.9 70.9 70.6 70.1 69.5 69.2 68.9 68.6 68.0 67.7 67.1 66.8 66.8 67.1 67.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 563.88 58.39 moveto[ 563.9 563.9 579.8 579.8 ][ 58.4 70.5 70.5 58.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 568.5 567.2 566.4 566.0 566.0 566.4 567.2 568.5 569.3 570.6 571.4 571.8 571.8 571.4 570.6 569.3 568.5 ][ 68.8 + 68.4 67.2 65.1 63.8 61.7 60.5 60.1 60.1 60.5 61.7 63.8 65.1 67.2 68.4 68.8 68.8 ]plong + 577.7 60.1 577.7 68.8 576.4 67.6 575.6 67.2 4 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 582.70 65.68 moveto[ 582.7 582.7 592.1 592.1 ][ 65.7 72.1 72.1 65.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 584.2 66.8 584.2 70.9 2 pls + s[ 584.2 585.0 585.6 586.5 587.1 587.4 587.4 ][ 69.8 70.6 70.9 70.9 70.6 69.8 66.8 ]plong + s[ 587.4 588.3 588.8 589.7 590.3 590.6 590.6 ][ 69.8 70.6 70.9 70.9 70.6 69.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 592.50 58.39 moveto[ 592.5 592.5 610.9 610.9 ][ 58.4 70.5 70.5 58.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 597.1 595.8 595.0 594.6 594.6 595.0 595.8 597.1 597.9 599.2 600.0 600.4 600.4 600.0 599.2 597.9 597.1 ][ 68.8 + 68.4 67.2 65.1 63.8 61.7 60.5 60.1 60.1 60.5 61.7 63.8 65.1 67.2 68.4 68.8 68.8 ]plong + s[ 605.5 604.2 603.4 602.9 602.9 603.4 604.2 605.5 606.3 607.5 608.4 608.8 608.8 608.4 607.5 606.3 605.5 ][ 68.8 + 68.4 67.2 65.1 63.8 61.7 60.5 60.1 60.1 60.5 61.7 63.8 65.1 67.2 68.4 68.8 68.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 611.03 65.68 moveto[ 611.0 611.0 617.2 617.2 ][ 65.7 72.1 72.1 65.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 615.7 615.4 614.5 613.7 612.8 612.5 612.8 613.4 614.8 615.4 615.7 615.7 615.4 614.5 613.7 612.8 612.5 ][ 70.1 + 70.6 70.9 70.9 70.6 70.1 69.5 69.2 68.9 68.6 68.0 67.7 67.1 66.8 66.8 67.1 67.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 593.89 133.65 moveto[ 593.9 593.9 614.4 614.4 ][ 133.7 145.8 145.8 133.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 603.5 139.1 596.0 139.1 2 pls + s[ 608.9 607.7 606.8 606.4 606.4 606.8 607.7 608.9 609.8 611.0 611.8 612.3 612.3 611.8 611.0 609.8 608.9 ][ 144.1 + 143.7 142.4 140.3 139.1 137.0 135.7 135.3 135.3 135.7 137.0 139.1 140.3 142.4 143.7 144.1 144.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 615.10 141.85 moveto[ 615.1 615.1 620.9 620.9 ][ 141.8 147.4 147.4 141.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 617.6 617.1 616.6 616.4 616.4 616.6 617.1 617.6 618.4 618.9 619.4 619.6 619.6 619.4 618.9 618.4 617.6 ][ 146.4 + 146.1 145.6 144.9 144.4 143.6 143.1 142.8 142.8 143.1 143.6 144.4 144.9 145.6 146.1 146.4 146.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 621.69 133.65 moveto[ 621.7 621.7 638.8 638.8 ][ 133.7 145.8 145.8 133.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 625.9 135.3 625.9 144.1 624.6 142.8 623.8 142.4 4 pls + s[ 633.4 632.1 631.3 630.9 630.9 631.3 632.1 633.4 634.2 635.5 636.3 636.7 636.7 636.3 635.5 634.2 633.4 ][ 144.1 + 143.7 142.4 140.3 139.1 137.0 135.7 135.3 135.3 135.7 137.0 139.1 140.3 142.4 143.7 144.1 144.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 639.71 144.20 moveto[ 639.7 639.7 643.3 643.3 ][ 144.2 149.5 149.5 144.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 641.5 145.6 641.5 148.1 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 593.47 198.87 moveto[ 593.5 593.5 611.8 611.8 ][ 198.9 211.0 211.0 198.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 598.1 596.8 596.0 595.6 595.6 596.0 596.8 598.1 598.9 600.2 601.0 601.4 601.4 601.0 600.2 598.9 598.1 ][ 209.3 + 208.9 207.6 205.5 204.3 202.2 201.0 200.5 200.5 201.0 202.2 204.3 205.5 207.6 208.9 209.3 209.3 ]plong + s[ 608.9 604.7 604.3 604.7 606.0 607.3 608.5 609.3 609.8 609.8 609.3 608.5 607.3 606.0 604.7 604.3 603.9 ][ 209.3 + 209.3 205.5 206.0 206.4 206.4 206.0 205.1 203.9 203.0 201.8 201.0 200.5 200.5 201.0 201.4 202.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 612.74 209.41 moveto[ 612.7 612.7 616.3 616.3 ][ 209.4 214.7 214.7 209.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 614.5 210.8 614.5 213.3 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 593.47 263.97 moveto[ 593.5 593.5 603.5 603.5 ][ 264.0 276.1 276.1 264.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 598.1 596.8 596.0 595.6 595.6 596.0 596.8 598.1 598.9 600.2 601.0 601.4 601.4 601.0 600.2 598.9 598.1 ][ 274.4 + 274.0 272.7 270.6 269.4 267.3 266.1 265.6 265.6 266.1 267.3 269.4 270.6 272.7 274.0 274.4 274.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 604.24 272.16 moveto[ 604.2 604.2 610.0 610.0 ][ 272.2 277.7 277.7 272.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 606.8 606.2 605.7 605.5 605.5 605.7 606.2 606.8 607.5 608.0 608.5 608.8 608.8 608.5 608.0 607.5 606.8 ][ 276.7 + 276.4 275.9 275.2 274.7 273.9 273.4 273.2 273.2 273.4 273.9 274.7 275.2 275.9 276.4 276.7 276.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 609.57 263.97 moveto[ 609.6 609.6 628.0 628.0 ][ 264.0 276.1 276.1 264.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 614.2 612.9 612.1 611.7 611.7 612.1 612.9 614.2 615.0 616.3 617.1 617.5 617.5 617.1 616.3 615.0 614.2 ][ 274.4 + 274.0 272.7 270.6 269.4 267.3 266.1 265.6 265.6 266.1 267.3 269.4 270.6 272.7 274.0 274.4 274.4 ]plong + s[ 622.5 621.3 620.4 620.0 620.0 620.4 621.3 622.5 623.4 624.6 625.4 625.9 625.9 625.4 624.6 623.4 622.5 ][ 274.4 + 274.0 272.7 270.6 269.4 267.3 266.1 265.6 265.6 266.1 267.3 269.4 270.6 272.7 274.0 274.4 274.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 628.85 274.51 moveto[ 628.8 628.8 632.4 632.4 ][ 274.5 279.8 279.8 274.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 630.6 275.9 630.6 278.4 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 593.47 329.18 moveto[ 593.5 593.5 611.8 611.8 ][ 329.2 341.3 341.3 329.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 598.1 596.8 596.0 595.6 595.6 596.0 596.8 598.1 598.9 600.2 601.0 601.4 601.4 601.0 600.2 598.9 598.1 ][ 339.6 + 339.2 338.0 335.9 334.6 332.5 331.3 330.9 330.9 331.3 332.5 334.6 335.9 338.0 339.2 339.6 339.6 ]plong + s[ 608.9 604.7 604.3 604.7 606.0 607.3 608.5 609.3 609.8 609.8 609.3 608.5 607.3 606.0 604.7 604.3 603.9 ][ 339.6 + 339.6 335.9 336.3 336.7 336.7 336.3 335.4 334.2 333.4 332.1 331.3 330.9 330.9 331.3 331.7 332.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 612.74 339.73 moveto[ 612.7 612.7 616.3 616.3 ][ 339.7 345.1 345.1 339.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 614.5 341.1 614.5 343.6 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 594.72 394.34 moveto[ 594.7 594.7 611.8 611.8 ][ 394.3 406.5 406.5 394.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 598.9 396.0 598.9 404.8 597.6 403.5 596.8 403.1 4 pls + s[ 606.4 605.2 604.3 603.9 603.9 604.3 605.2 606.4 607.3 608.5 609.3 609.8 609.8 609.3 608.5 607.3 606.4 ][ 404.8 + 404.4 403.1 401.0 399.8 397.7 396.4 396.0 396.0 396.4 397.7 399.8 401.0 403.1 404.4 404.8 404.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 612.74 404.88 moveto[ 612.7 612.7 616.3 616.3 ][ 404.9 410.2 410.2 404.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 614.5 406.3 614.5 408.8 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 342.50 36.98 moveto[ 342.5 342.5 447.3 447.3 ][ 37.0 52.4 52.4 37.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 344.6 41.6 344.6 50.3 2 pls + s[ 344.6 348.4 349.6 350.0 350.4 350.4 350.0 349.6 348.4 344.6 ][ 50.3 50.3 49.9 49.5 48.7 47.8 47.0 46.6 + 46.2 46.2 ]plong + 350.4 41.6 347.5 46.2 2 pls + 352.9 50.3 353.4 50.8 353.8 50.3 353.4 49.9 352.9 50.3 5 pls + 353.4 41.6 353.4 47.4 2 pls + s[ 361.3 361.3 360.9 360.5 359.6 358.4 357.5 ][ 47.4 40.7 39.5 39.1 38.6 38.6 39.1 ]plong + s[ 361.3 360.5 359.6 358.4 357.5 356.7 356.3 356.3 356.7 357.5 358.4 359.6 360.5 361.3 ][ 46.2 47.0 47.4 47.4 + 47.0 46.2 44.9 44.1 42.8 42.0 41.6 41.6 42.0 42.8 ]plong + 364.6 41.6 364.6 50.3 2 pls + s[ 364.6 365.9 366.7 368.0 368.8 369.2 369.2 ][ 45.7 47.0 47.4 47.4 47.0 45.7 41.6 ]plong + 375.1 41.6 374.2 41.6 373.4 42.0 373.0 43.2 373.0 50.3 5 pls + 374.7 47.4 371.7 47.4 2 pls + 388.9 41.6 388.9 47.4 2 pls + s[ 388.9 388.0 387.2 385.9 385.1 384.3 383.9 383.9 384.3 385.1 385.9 387.2 388.0 388.9 ][ 46.2 47.0 47.4 47.4 + 47.0 46.2 44.9 44.1 42.8 42.0 41.6 41.6 42.0 42.8 ]plong + s[ 396.4 396.0 394.7 393.5 392.2 391.8 392.2 393.0 395.1 396.0 396.4 396.4 396.0 394.7 393.5 392.2 391.8 ][ 46.2 + 47.0 47.4 47.4 47.0 46.2 45.3 44.9 44.5 44.1 43.2 42.8 42.0 41.6 41.6 42.0 42.8 ]plong + s[ 403.9 403.1 402.2 401.0 400.1 399.3 398.9 398.9 399.3 400.1 401.0 402.2 403.1 403.9 ][ 46.2 47.0 47.4 47.4 + 47.0 46.2 44.9 44.1 42.8 42.0 41.6 41.6 42.0 42.8 ]plong + s[ 406.4 411.4 411.4 411.0 410.6 409.8 408.5 407.7 406.8 406.4 406.4 406.8 407.7 408.5 409.8 410.6 411.4 ][ 44.9 + 44.9 45.7 46.6 47.0 47.4 47.4 47.0 46.2 44.9 44.1 42.8 42.0 41.6 41.6 42.0 42.8 ]plong + 414.3 41.6 414.3 47.4 2 pls + s[ 414.3 415.6 416.4 417.7 418.5 418.9 418.9 ][ 45.7 47.0 47.4 47.4 47.0 45.7 41.6 ]plong + s[ 426.5 426.0 424.8 423.5 422.3 421.9 422.3 423.1 425.2 426.0 426.5 426.5 426.0 424.8 423.5 422.3 421.9 ][ 46.2 + 47.0 47.4 47.4 47.0 46.2 45.3 44.9 44.5 44.1 43.2 42.8 42.0 41.6 41.6 42.0 42.8 ]plong + 429.0 50.3 429.4 50.8 429.8 50.3 429.4 49.9 429.0 50.3 5 pls + 429.4 41.6 429.4 47.4 2 pls + s[ 434.4 433.6 432.7 432.3 432.3 432.7 433.6 434.4 435.6 436.5 437.3 437.7 437.7 437.3 436.5 435.6 434.4 ][ 47.4 + 47.0 46.2 44.9 44.1 42.8 42.0 41.6 41.6 42.0 42.8 44.1 44.9 46.2 47.0 47.4 47.4 ]plong + 440.7 41.6 440.7 47.4 2 pls + s[ 440.7 441.9 442.7 444.0 444.8 445.3 445.3 ][ 45.7 47.0 47.4 47.4 47.0 45.7 41.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 648.59 307.21 moveto[ 648.6 661.1 661.1 648.6 ][ 307.2 307.2 235.8 235.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 650.3 305.1 659.0 305.1 2 pls + s[ 659.0 659.0 658.6 657.8 656.9 655.7 653.6 652.3 651.5 650.7 650.3 650.3 ][ 305.1 302.2 300.9 300.1 299.7 299.3 + 299.3 299.7 300.1 300.9 302.2 305.1 ]plong + s[ 653.6 653.6 654.4 655.3 655.7 656.1 656.1 655.7 654.9 653.6 652.8 651.5 650.7 650.3 650.3 650.7 651.5 ][ 296.8 + 291.8 291.8 292.2 292.6 293.4 294.7 295.5 296.4 296.8 296.8 296.4 295.5 294.7 293.4 292.6 291.8 ]plong + s[ 654.9 655.7 656.1 656.1 655.7 654.9 653.6 652.8 651.5 650.7 650.3 650.3 650.7 651.5 ][ 284.2 285.1 285.9 287.2 + 288.0 288.8 289.3 289.3 288.8 288.0 287.2 285.9 285.1 284.2 ]plong + 650.3 281.3 659.0 281.3 2 pls + 659.0 278.4 659.4 278.0 659.0 277.6 658.6 278.0 659.0 278.4 5 pls + 650.3 278.0 656.1 278.0 2 pls + 650.3 274.6 656.1 274.6 2 pls + s[ 654.4 655.7 656.1 656.1 655.7 654.4 650.3 ][ 274.6 273.4 272.5 271.3 270.5 270.0 270.0 ]plong + 650.3 262.1 656.1 262.1 2 pls + s[ 654.9 655.7 656.1 656.1 655.7 654.9 653.6 652.8 651.5 650.7 650.3 650.3 650.7 651.5 ][ 262.1 262.9 263.8 265.0 + 265.9 266.7 267.1 267.1 266.7 265.9 265.0 263.8 262.9 262.1 ]plong + 650.3 256.3 650.3 257.1 650.7 257.9 651.9 258.3 659.0 258.3 5 pls + 656.1 256.7 656.1 259.6 2 pls + 659.0 254.2 659.4 253.7 659.0 253.3 658.6 253.7 659.0 254.2 5 pls + 650.3 253.7 656.1 253.7 2 pls + s[ 656.1 655.7 654.9 653.6 652.8 651.5 650.7 650.3 650.3 650.7 651.5 652.8 653.6 654.9 655.7 656.1 656.1 ][ 248.7 + 249.6 250.4 250.8 250.8 250.4 249.6 248.7 247.5 246.6 245.8 245.4 245.4 245.8 246.6 247.5 248.7 ]plong + 650.3 242.5 656.1 242.5 2 pls + s[ 654.4 655.7 656.1 656.1 655.7 654.4 650.3 ][ 242.5 241.2 240.4 239.1 238.3 237.9 237.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 486.51 moveto[ 360.9 360.9 435.2 435.2 ][ 486.5 498.6 498.6 486.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 488.2 366.3 496.9 2 pls + 369.7 488.2 366.3 496.9 2 pls + 368.4 491.1 364.2 491.1 2 pls + 378.4 488.2 378.4 496.9 2 pls + 383.9 496.9 378.4 496.9 2 pls + 381.8 492.8 378.4 492.8 2 pls + 385.9 488.2 385.9 496.9 2 pls + 391.0 488.2 391.0 496.9 2 pls + 393.9 496.9 388.0 496.9 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 495.7 496.5 496.9 496.9 496.5 495.7 494.9 494.0 493.6 493.2 492.4 491.9 491.5 490.7 489.4 488.6 + 488.2 488.2 488.6 489.4 ]plong + 413.5 488.2 412.7 488.2 411.8 488.6 411.4 489.8 411.4 496.9 5 pls + 413.1 494.0 410.2 494.0 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 491.5 + 491.5 492.4 493.2 493.6 494.0 494.0 493.6 492.8 491.5 490.7 489.4 488.6 488.2 488.2 488.6 489.4 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 492.8 + 493.6 494.0 494.0 493.6 492.8 491.9 491.5 491.1 490.7 489.8 489.4 488.6 488.2 488.2 488.6 489.4 ]plong + 433.1 488.2 432.3 488.2 431.5 488.6 431.1 489.8 431.1 496.9 5 pls + 432.7 494.0 429.8 494.0 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/plot3d-test1.ast b/ast_tester/plot3d-test1.ast new file mode 100644 index 0000000..271faf1 --- /dev/null +++ b/ast_tester/plot3d-test1.ast @@ -0,0 +1,340 @@ + Begin FrameSet # Set of inter-related coordinate systems +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "SKY-DSBSPECTRUM" # Coordinate system domain +# Epoch = 2007.3268594222 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Radio velocity (USB)" # Label for axis 3 +# System = "Compound" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Uni3 = "km/s" # Units for axis 3 +# Dir1 = 0 # Plot axis 1 in reverse direction + IsA Frame # Coordinate system description + Nframe = 4 # Number of Frames in FrameSet +# Base = 1 # Index of base Frame + Currnt = 4 # Index of current Frame + Nnode = 5 # Number of nodes in FrameSet + Nod1 = 3 # Frame 1 is associated with node 3 + Nod2 = 4 # Frame 2 is associated with node 4 + Nod3 = 5 # Frame 3 is associated with node 5 + Nod4 = 2 # Frame 4 is associated with node 2 + Lnk2 = 1 # Node 2 is derived from node 1 + Lnk3 = 1 # Node 3 is derived from node 1 + Lnk4 = 1 # Node 4 is derived from node 1 + Lnk5 = 1 # Node 5 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Data grid indices; first pixel at (1,1,1)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Data grid index 1" # Label for axis 1 +# Lbl2 = "Data grid index 2" # Label for axis 2 +# Lbl3 = "Data grid index 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Data grid index 1" # Axis Label + Symbol = "g1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Data grid index 2" # Axis Label + Symbol = "g2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Data grid index 3" # Axis Label + Symbol = "g3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm2 = # Frame number 2 + Begin Frame # Coordinate system description + Title = "Pixel coordinates; first pixel at (-77.5,-52.5,-820.5)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "PIXEL" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Lbl3 = "Pixel coordinate 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 1" # Axis Label + Symbol = "p1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 2" # Axis Label + Symbol = "p2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 3" # Axis Label + Symbol = "p3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm3 = # Frame number 3 + Begin Frame # Coordinate system description + Title = "Axis coordinates; first pixel at (-77.5,-52.5,-820.5)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "AXIS" # Coordinate system domain +# Lbl1 = "Axis 1" # Label for axis 1 +# Lbl2 = "Axis 2" # Label for axis 2 +# Lbl3 = "Axis 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Axis 1" # Axis Label + Symbol = "a1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Axis 2" # Axis Label + Symbol = "a2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Axis 3" # Axis Label + Symbol = "a3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm4 = # Frame number 4 + Begin CmpFrame # Compound coordinate system description +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "SKY-DSBSPECTRUM" # Coordinate system domain +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Radio velocity (USB)" # Label for axis 3 +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Uni3 = "km/s" # Units for axis 3 +# Dir1 = 0 # Plot axis 1 in reverse direction + IsA Frame # Coordinate system description + FrameA = # First component Frame + Begin SkyFrame # Description of celestial coordinate system + Naxes = 2 # Number of coordinate axes + Epoch = 2007.3268594222 # Julian epoch of observation + System = "FK5" # Coordinate system type + ObsLat = 0.346026050148997 # Observers geodetic latitude (rads) + ObsLon = -2.71363306946838 # Observers geodetic longitude (rads) + Dut1 = -0.0989801599329792 # UT1-UTC in seconds + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + SRef1 = 4.8432980149123 # Ref. pos. RA 18:30:00.1 + SRef2 = 0.0213067755653197 # Ref. pos. Dec 1:13:15 + End SkyFrame + FrameB = # Second component Frame + Begin DSBSpecFrame # Dual sideband spectral axis + Naxes = 1 # Number of coordinate axes + Epoch = 2007.3268594222 # Julian epoch of observation + System = "VRAD" # Coordinate system type + ObsLat = 0.346026069000144 # Observers geodetic latitude (rads) + ObsLon = -2.71363307300091 # Observers geodetic longitude (rads) + Dut1 = -0.0989801599329792 # UT1-UTC in seconds + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + End Axis + IsA Frame # Coordinate system description + SoR = "LSRK" # Standard of rest + RefRA = 4.84328867428426 # Reference RA (rads, FK5 J2000) + RefDec = 0.0213075607299356 # Reference Dec (rads, FK5 J2000) + RstFrq = 345795989900 # Rest frequency (Hz) + SrcVel = 7000.08172321467 # Source velocity (m/s) + SrcVRF = "LSRK" # Source velocity rest frame + UFreq = "GHz" # Preferred units for frequency + IsA SpecFrame # Description of spectral coordinate system + DSBCen = 345834569302.861 # Central frequency (Hz topo) + IF = -5000001243.89453 # Intermediate frequency (Hz) + SideBn = "USB" # Represents upper sideband + AlSdBn = 1 # Align sidebands? + End DSBSpecFrame + End CmpFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -78.9102040827274 # Shift for axis 1 + Sft2 = -54.3591836988926 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 346188848728.921 # Shift for axis 1 + Scl1 = -488347.122802699 # Scale factor for axis 1 + End WinMap + MapB = # Second component Mapping + Begin SpecMap # Conversion between spectral coordinate systems + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nspec = 2 # Number of conversion steps + Spec1 = "FRTOVL" # Convert frequency to rel. velocity + Spec1a = 345795989900 # Rest frequency (Hz) + Spec2 = "VLTOVR" # Convert relativistic to radio velocity + End SpecMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + M0 = 1.49080827566563e-05 # Forward matrix value + M1 = -3.19704866431787e-05 # Forward matrix value + M2 = -3.19704866431787e-05 # Forward matrix value + M3 = -1.49080827566563e-05 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.00278113801100606 # Forward matrix value + M1 = 0.991443912134338 # Forward matrix value + M2 = 0.130503771451718 # Forward matrix value + M3 = -0.021123653434259 # Forward matrix value + M4 = 0.130533402207093 # Forward matrix value + M5 = -0.9912188568494 # Forward matrix value + M6 = -0.999773002504545 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 0.0213059490060158 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 4.84329594648948 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin ZoomMap # Zoom about the origin + Nin = 1 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Zoom = 0.001 # Zoom factor + End ZoomMap + End CmpMap + End CmpMap + Map3 = # Mapping between nodes 1 and 3 + Begin UnitMap # Unit (null) Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + End UnitMap + Map4 = # Mapping between nodes 1 and 4 + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -78.5 # Shift for axis 1 + Sft2 = -53.5 # Shift for axis 2 + Sft3 = -821.5 # Shift for axis 3 + End WinMap + Map5 = # Mapping between nodes 1 and 5 + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -78.5 # Shift for axis 1 + Sft2 = -53.5 # Shift for axis 2 + Sft3 = -821.5 # Shift for axis 3 + End WinMap + End FrameSet diff --git a/ast_tester/plotter.f b/ast_tester/plotter.f new file mode 100644 index 0000000..79bb8e1 --- /dev/null +++ b/ast_tester/plotter.f @@ -0,0 +1,230 @@ + PROGRAM PLOTTER + +* Usage: +* PLOTTER [ ] + +* Description: +* Plots a standard grid from the specified fits header file, using +* the specified attributes, and sends postscript output to the +* specified ps file. + +* Parameters: +* file file +* A text file containing fits headers. +* attr1 +* A string containg a comma-separated list of attribute +* settings for the Plot. +* attr2 +* A string containg a comma-separated list of attribute +* settings for the FitsChan. +* ps file +* The output postscript file +* xlo ylo xhi yhi +* The bounds within the GRID Frame of the required plot. +* Taken from the FITS headers if not supplied + + + IMPLICIT NONE + INCLUDE 'AST_PAR' + + INTEGER STATUS, FC, FS, NAXIS1, NAXIS2, PL, PGBEG, IARGC, OC + CHARACTER FILE*80, CARD*80, DEVN*80, PSFILE*80, ATTR*255, TEXT*80 + REAL GBOX(4), RANGE, DELTA, ASP + DOUBLE PRECISION PBOX(4) + + STATUS = 0 +* +* Check command line arguments have been supplied. +* + IF( IARGC() .LT. 3 ) THEN + WRITE(*,*) 'Usage: plotter '// + : ' [ ]' + RETURN + END IF + +* +* Use object caching to minimise allocation of new memory +* + OC = AST_TUNE( 'ObjectCaching', 1, STATUS ) + IF( OC .NE. 0 ) THEN + WRITE(*,'(A,I6)') 'Default ObjectCaching VALUE is ',OC + END IF + + IF( AST_TUNE( 'ObjectCaching', AST__TUNULL, STATUS ) .NE. 1 ) THEN + WRITE(*,'(A,I6)') 'Set ObjectCaching VALUE is ',OC + END IF + +* +* Create a FitsChan to store the FITS headers. +* + CALL GETARG( 3, ATTR ) + FC = AST_FITSCHAN( AST_NULL, AST_NULL, ATTR, STATUS ) + +* +* Open a text file containing a list of FITS headers. +* + CALL GETARG( 1, FILE ) + OPEN( UNIT=10, FILE=FILE, STATUS='OLD' ) + +* +* Read each card out of the text file and store it in the FitsChan. +* + DO WHILE( .TRUE. ) + READ( 10, '(A)', END = 10 ) CARD + CALL AST_PUTFITS( FC, CARD, 0, STATUS ) + END DO + + 10 CLOSE( 10 ) + + +* +* If the base frame box was supplied on the command line, use it. +* + IF( IARGC() .GT. 6 ) THEN + CALL GETARG( 5, TEXT ) + READ( TEXT, * ) PBOX( 1 ) + + CALL GETARG( 6, TEXT ) + READ( TEXT, * ) PBOX( 2 ) + + CALL GETARG( 7, TEXT ) + READ( TEXT, * ) PBOX( 3 ) + + CALL GETARG( 8, TEXT ) + READ( TEXT, * ) PBOX( 4 ) + +* Otherwise use NAXISi keywords in the header. + ELSE + +* +* See if values were supplied for NAXIS1 and NAXIS2. If not assume a value +* of 100 for each. The FitsChan is re-wound before calling AST_FINDFITS so +* that the search starts form the beginning. +* + CALL AST_CLEAR( FC, 'CARD', STATUS ) + IF ( AST_FINDFITS( FC, 'NAXIS1', CARD, .TRUE., STATUS ) ) THEN + READ(CARD(11:),*) NAXIS1 + ELSE + NAXIS1 = 100 + END IF + + CALL AST_CLEAR( FC, 'CARD', STATUS ) + IF ( AST_FINDFITS( FC, 'NAXIS2', CARD, .TRUE., STATUS ) ) THEN + READ(CARD(11:),*) NAXIS2 + ELSE + NAXIS2 = 100 + END IF + + PBOX(1) = 0.5 + PBOX(2) = 0.5 + PBOX(3) = DBLE( NAXIS1 )+0.5 + PBOX(4) = DBLE( NAXIS2 )+0.5 + END IF + +* +* Read an Object from the contents of the FitsChan. This should be a +* FrameSet (this should be tested really, and an error reported if any +* other type of Object is obtained). Re-wind the FitsChan first so that +* its entire contents are read. Note, this is a destructive read, in that +* the cards which are significant to the creation of the FrameSet are +* removed from the FitsChan (this is why NAXIS1 and NAXIS2 are read out +* earlier - just in case they are significant to the FrameSet). Any +* insignificant cards are left as they are. +* + CALL AST_CLEAR( FC, 'CARD', STATUS ) + FS = AST_READ( FC, STATUS ) + + IF( FS .EQ. AST__NULL ) THEN + WRITE(*,*) '!!! No object read from FitsChan!!!' + GO TO 999 + END IF + +* +* If all is OK, start up PGPLOT. +* + IF( STATUS .EQ. 0 ) THEN + CALL GETARG( 4, PSFILE ) + CALL DELETEFILE( PSFILE ) + + DEVN = 'pscol_l;'//PSFILE +c IF( PGBEG( 0, '?', 1, 1 ) .EQ. 1 ) THEN + IF( PGBEG( 0, DEVN, 1, 1 ) .EQ. 1 ) THEN + CALL PGPAGE + CALL PGWNAD( 0.0, 1.0, 0.0, 1.0 ) + +* +* Create the Plot. The pixel coordinates box is +* mapped onto a window which is 10% smaller than the full PGPLOT window. +* This gives some space for things like tick marks with negative length +* (which stick outside the pixel cooridnates box). +* + CALL PGQWIN( GBOX(1), GBOX(3), GBOX(2), GBOX(4) ) + + RANGE = GBOX(3) - GBOX(1) + GBOX(1) = GBOX(1) + 0.05*RANGE + GBOX(3) = GBOX(3) - 0.05*RANGE + + RANGE = GBOX(4) - GBOX(2) + GBOX(2) = GBOX(2) + 0.05*RANGE + GBOX(4) = GBOX(4) - 0.05*RANGE + + ASP = REAL( PBOX(4) - PBOX(2) )/REAL( PBOX(3) - PBOX(1) ) + IF( ASP .LT. 0.05 .OR. ASP .GT. 20 ) ASP = 1.0 + + IF( ASP .GT. 1.0 ) THEN + DELTA = 0.5*( ( GBOX(3) - GBOX(1) ) - + : ( GBOX(4) - GBOX(2) )/ASP ) + GBOX(3) = GBOX(3) - DELTA + GBOX(1) = GBOX(1) + DELTA + ELSE + DELTA = 0.5*( ( GBOX(4) - GBOX(2) ) - + : ASP*( GBOX(3) - GBOX(1) ) ) + GBOX(4) = GBOX(4) - DELTA + GBOX(2) = GBOX(2) + DELTA + END IF + + CALL GETARG( 2, ATTR ) + PL = AST_PLOT( FS, GBOX, PBOX, 'title = A FITS test', + : STATUS ) + CALL AST_SET( PL, ATTR, STATUS ) + +* +* Draw the grid. +* + CALL AST_GRID( PL, STATUS ) + +* +* Annul the Plot, and close PGPLOT. +* + CALL AST_ANNUL( PL, STATUS ) + CALL PGEND + END IF + END IF + +* +* Annul the other objects. +* + CALL AST_ANNUL( FS, STATUS ) + 999 CALL AST_ANNUL( FC, STATUS ) + + END + + +* +* Delete a file if it exists. +* + SUBROUTINE DELETEFILE( FILNAM ) + IMPLICIT NONE + + CHARACTER FILNAM*(*) + LOGICAL EXISTS + + INQUIRE ( FILE = FILNAM, + : EXIST = EXISTS ) + + IF( EXISTS ) THEN + OPEN ( UNIT=10, FILE=FILNAM, STATUS='OLD' ) + CLOSE ( 10, STATUS='DELETE' ) + END IF + + END diff --git a/ast_tester/polco.attr b/ast_tester/polco.attr new file mode 100644 index 0000000..c220287 --- /dev/null +++ b/ast_tester/polco.attr @@ -0,0 +1 @@ +Grid=1,labelling=interior,bottom(1)=0 diff --git a/ast_tester/polco.box b/ast_tester/polco.box new file mode 100644 index 0000000..0661792 --- /dev/null +++ b/ast_tester/polco.box @@ -0,0 +1 @@ +-300 -300 500 500 diff --git a/ast_tester/polco.head b/ast_tester/polco.head new file mode 100644 index 0000000..d953089 --- /dev/null +++ b/ast_tester/polco.head @@ -0,0 +1,86 @@ + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +CURRNT_A= 2 / Index of current Frame +NOD1_A = 2 / Frame 1 is associated with node 2 +NOD2_A = 1 / Frame 2 is associated with node 1 +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Data grid indices; first pixel at (1&'/ Title of coordinate system +CONTINUE '",1) "' +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Data grid index 1' / Axis Label +SYMBOL_A= 'g1 ' / Axis symbol +UNIT_A = 'pixel ' / Axis units +FORMAT_A= '%3.1f ' / Format specifier +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Data grid index 2' / Axis Label +SYMBOL_B= 'g2 ' / Axis symbol +UNIT_B = 'pixel ' / Axis units +FORMAT_B= '%3.1f ' / Format specifier +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'Frame ' / Coordinate system description +TITLE_B = 'Pixel coordinates; first pixel at (-&'/ Title of coordinate system +CONTINUE '100.5,-200.5)' +NAXES_B = 2 / Number of coordinate axes +DOMAIN_B= 'POLAR ' / Coordinate system domain +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'Axis ' / Coordinate axis +LABEL_C = 'Pixel coordinate 1' / Axis Label +SYMBOL_C= 'p1 ' / Axis symbol +UNIT_C = 'pixel ' / Axis units +FORMAT_C= '%3.1f ' / Format specifier +BOTTOM_A= 0.0 / Minimum legal axis value +ENDAST_D= 'Axis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'Axis ' / Coordinate axis +LABEL_D = 'Pixel coordinate 2' / Axis Label +SYMBOL_D= 'p2 ' / Axis symbol +UNIT_D = 'pixel ' / Axis units +FORMAT_D= '%3.1f ' / Format specifier +TOP_A = 3.141592 / Maximum legal axis value +BOTTOM_B= -3.141592 / Minimum legal axis value +ENDAST_E= 'Axis ' / End of object definition +ENDAST_F= 'Frame ' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISA_A = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +INVB_A = 1 / Second Mapping used in inverse direction +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'MathMap ' / Transformation using mathematical functions +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_B = 'Mapping ' / Mapping between coordinate systems +FWD1_A = 'r=sqrt(x*x+y*y)' / Forward function 1 +FWD2_A = 'theta=atan2(y,x)' / Forward function 2 +INV1_A = 'x=r*cos(theta)' / Inverse function 1 +INV2_A = 'y=r*sin(theta)' / Inverse function 2 +SIMPFI_A= 1 / Forward-inverse pairs may simplify +SIMPIF_A= 1 / Inverse-forward pairs may simplify +ENDAST_G= 'MathMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'WinMap ' / Map one window on to another +NIN_C = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_C = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -101.5 / Shift for axis 1 +SFT2_A = -201.5 / Shift for axis 2 +ENDAST_H= 'WinMap ' / End of object definition +ENDAST_I= 'CmpMap ' / End of object definition +ENDAST_J= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST diff --git a/ast_tester/polco.ps b/ast_tester/polco.ps new file mode 100644 index 0000000..b7a40e1 --- /dev/null +++ b/ast_tester/polco.ps @@ -0,0 +1,888 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Sep 29 12:55:33 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 29/09/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 371.2 371.2 371.3 371.3 371.4 371.5 371.7 371.8 372.0 372.2 372.5 372.7 373.0 373.3 373.6 374.0 374.4 374.8 + 375.2 375.6 376.1 376.6 377.1 377.6 378.1 378.7 379.2 379.8 380.4 381.0 381.7 382.3 383.0 383.7 384.4 385.1 + 385.8 386.5 387.2 388.0 388.7 389.5 390.2 391.0 391.8 392.5 393.3 394.1 394.9 395.7 396.4 397.2 398.0 398.8 + 399.6 400.3 401.1 401.9 402.6 403.4 404.1 404.8 405.5 406.3 407.0 407.6 408.3 409.0 409.6 410.3 410.9 411.5 + 412.1 412.7 413.2 413.7 414.3 414.8 415.2 415.7 416.1 416.6 416.9 417.3 417.7 418.0 418.3 418.6 418.8 419.1 + 419.3 419.5 419.6 419.8 419.9 420.0 420.0 420.1 420.1 420.1 420.0 420.0 419.9 419.8 419.6 419.5 419.3 419.1 + 418.8 418.6 418.3 418.0 417.7 417.3 416.9 416.6 416.1 415.7 415.2 414.8 414.3 413.7 413.2 412.7 412.1 411.5 + 410.9 410.3 409.6 409.0 408.3 407.6 407.0 406.3 405.5 404.8 404.1 403.4 402.6 401.9 401.1 400.3 399.6 398.8 + 398.0 397.2 396.4 395.7 394.9 394.1 393.3 392.5 391.8 391.0 390.2 389.5 388.7 388.0 387.2 386.5 385.8 385.1 + 384.4 383.7 383.0 382.3 381.7 381.0 380.4 379.8 379.2 378.7 378.1 377.6 377.1 376.6 376.1 375.6 375.2 374.8 + 374.4 374.0 373.6 373.3 373.0 372.7 372.5 372.2 372.0 371.8 371.7 371.5 371.4 371.3 371.3 371.2 371.2 ][ 321.1 + 320.3 319.5 318.7 318.0 317.2 316.4 315.7 314.9 314.1 313.4 312.7 311.9 311.2 310.5 309.8 309.1 308.4 307.8 + 307.1 306.5 305.9 305.3 304.7 304.1 303.5 303.0 302.5 302.0 301.5 301.0 300.6 300.2 299.8 299.4 299.1 298.7 + 298.4 298.2 297.9 297.7 297.5 297.3 297.1 297.0 296.9 296.8 296.7 296.7 296.7 296.7 296.7 296.8 296.9 297.0 + 297.1 297.3 297.5 297.7 297.9 298.2 298.4 298.7 299.1 299.4 299.8 300.2 300.6 301.0 301.5 302.0 302.5 303.0 + 303.5 304.1 304.7 305.3 305.9 306.5 307.1 307.8 308.4 309.1 309.8 310.5 311.2 311.9 312.7 313.4 314.1 314.9 + 315.7 316.4 317.2 318.0 318.7 319.5 320.3 321.1 321.9 322.7 323.4 324.2 325.0 325.8 326.5 327.3 328.0 328.8 + 329.5 330.3 331.0 331.7 332.4 333.1 333.8 334.4 335.1 335.7 336.3 336.9 337.5 338.1 338.6 339.2 339.7 340.2 + 340.7 341.1 341.6 342.0 342.4 342.8 343.1 343.4 343.7 344.0 344.3 344.5 344.7 344.9 345.1 345.2 345.3 345.4 + 345.5 345.5 345.5 345.5 345.5 345.4 345.3 345.2 345.1 344.9 344.7 344.5 344.3 344.0 343.7 343.4 343.1 342.8 + 342.4 342.0 341.6 341.1 340.7 340.2 339.7 339.2 338.6 338.1 337.5 336.9 336.3 335.7 335.1 334.4 333.8 333.1 + 332.4 331.7 331.0 330.3 329.5 328.8 328.0 327.3 326.5 325.8 325.0 324.2 323.4 322.7 321.9 321.1 ]plong + s[ 346.8 346.8 346.9 347.0 347.2 347.4 347.7 348.0 348.4 348.8 349.3 349.8 350.4 351.0 351.6 352.3 353.1 353.9 + 354.7 355.6 356.5 357.4 358.4 359.5 360.6 361.7 362.8 364.0 365.2 366.4 367.7 369.0 370.3 371.7 373.1 374.5 + 375.9 377.3 378.8 380.3 381.8 383.3 384.8 386.3 387.9 389.4 391.0 392.5 394.1 395.7 397.2 398.8 400.3 401.9 + 403.5 405.0 406.5 408.1 409.6 411.1 412.5 414.0 415.4 416.9 418.3 419.6 421.0 422.3 423.6 424.9 426.1 427.3 + 428.5 429.7 430.8 431.8 432.9 433.9 434.8 435.7 436.6 437.4 438.2 439.0 439.7 440.3 441.0 441.5 442.0 442.5 + 442.9 443.3 443.6 443.9 444.1 444.3 444.4 444.5 444.5 444.5 444.4 444.3 444.1 443.9 443.6 443.3 442.9 442.5 + 442.0 441.5 441.0 440.3 439.7 439.0 438.2 437.4 436.6 435.7 434.8 433.9 432.9 431.8 430.8 429.7 428.5 427.3 + 426.1 424.9 423.6 422.3 421.0 419.6 418.3 416.9 415.4 414.0 412.5 411.1 409.6 408.1 406.5 405.0 403.5 401.9 + 400.3 398.8 397.2 395.7 394.1 392.5 391.0 389.4 387.9 386.3 384.8 383.3 381.8 380.3 378.8 377.3 375.9 374.5 + 373.1 371.7 370.3 369.0 367.7 366.4 365.2 364.0 362.8 361.7 360.6 359.5 358.4 357.4 356.5 355.6 354.7 353.9 + 353.1 352.3 351.6 351.0 350.4 349.8 349.3 348.8 348.4 348.0 347.7 347.4 347.2 347.0 346.9 346.8 346.8 ][ 321.1 + 319.5 318.0 316.4 314.8 313.3 311.7 310.2 308.7 307.2 305.7 304.2 302.8 301.3 299.9 298.5 297.1 295.8 294.4 + 293.1 291.9 290.6 289.4 288.2 287.1 286.0 284.9 283.9 282.9 281.9 281.0 280.1 279.3 278.5 277.8 277.1 276.4 + 275.8 275.2 274.7 274.2 273.8 273.4 273.1 272.8 272.6 272.4 272.3 272.2 272.2 272.2 272.3 272.4 272.6 272.8 + 273.1 273.4 273.8 274.2 274.7 275.2 275.8 276.4 277.1 277.8 278.5 279.3 280.1 281.0 281.9 282.9 283.9 284.9 + 286.0 287.1 288.2 289.4 290.6 291.9 293.1 294.4 295.8 297.1 298.5 299.9 301.3 302.8 304.2 305.7 307.2 308.7 + 310.2 311.7 313.3 314.8 316.4 318.0 319.5 321.1 322.7 324.2 325.8 327.3 328.9 330.4 332.0 333.5 335.0 336.5 + 338.0 339.4 340.9 342.3 343.7 345.1 346.4 347.7 349.0 350.3 351.6 352.8 353.9 355.1 356.2 357.3 358.3 359.3 + 360.3 361.2 362.0 362.9 363.7 364.4 365.1 365.8 366.4 367.0 367.5 367.9 368.4 368.7 369.1 369.3 369.6 369.7 + 369.9 369.9 370.0 369.9 369.9 369.7 369.6 369.3 369.1 368.7 368.4 367.9 367.5 367.0 366.4 365.8 365.1 364.4 + 363.7 362.9 362.0 361.2 360.3 359.3 358.3 357.3 356.2 355.1 353.9 352.8 351.6 350.3 349.0 347.7 346.4 345.1 + 343.7 342.3 340.9 339.4 338.0 336.5 335.0 333.5 332.0 330.4 328.9 327.3 325.8 324.2 322.7 321.1 ]plong + s[ 322.4 322.4 322.5 322.7 323.0 323.3 323.7 324.2 324.8 325.4 326.1 326.9 327.7 328.6 329.6 330.7 331.8 333.0 + 334.2 335.5 336.9 338.3 339.8 341.4 343.0 344.7 346.4 348.1 350.0 351.8 353.7 355.7 357.7 359.7 361.8 363.9 + 366.0 368.1 370.3 372.6 374.8 377.1 379.3 381.6 384.0 386.3 388.6 391.0 393.3 395.7 398.0 400.4 402.7 405.0 + 407.4 409.7 412.0 414.2 416.5 418.8 421.0 423.2 425.3 427.5 429.6 431.6 433.7 435.6 437.6 439.5 441.4 443.2 + 444.9 446.7 448.3 449.9 451.5 453.0 454.4 455.8 457.1 458.3 459.5 460.6 461.7 462.7 463.6 464.4 465.2 465.9 + 466.6 467.1 467.6 468.0 468.4 468.6 468.8 468.9 469.0 468.9 468.8 468.6 468.4 468.0 467.6 467.1 466.6 465.9 + 465.2 464.4 463.6 462.7 461.7 460.6 459.5 458.3 457.1 455.8 454.4 453.0 451.5 449.9 448.3 446.7 444.9 443.2 + 441.4 439.5 437.6 435.6 433.7 431.6 429.6 427.5 425.3 423.2 421.0 418.8 416.5 414.2 412.0 409.7 407.4 405.0 + 402.7 400.4 398.0 395.7 393.3 391.0 388.6 386.3 384.0 381.6 379.3 377.1 374.8 372.6 370.3 368.1 366.0 363.9 + 361.8 359.7 357.7 355.7 353.7 351.8 350.0 348.1 346.4 344.7 343.0 341.4 339.8 338.3 336.9 335.5 334.2 333.0 + 331.8 330.7 329.6 328.6 327.7 326.9 326.1 325.4 324.8 324.2 323.7 323.3 323.0 322.7 322.5 322.4 322.4 ][ 321.1 + 318.7 316.4 314.1 311.7 309.4 307.1 304.8 302.5 300.2 298.0 295.8 293.6 291.4 289.3 287.2 285.1 283.1 281.1 + 279.2 277.2 275.4 273.6 271.8 270.1 268.4 266.8 265.3 263.8 262.3 261.0 259.7 258.4 257.2 256.1 255.0 254.1 + 253.1 252.3 251.5 250.8 250.2 249.6 249.1 248.7 248.4 248.1 247.9 247.8 247.8 247.8 247.9 248.1 248.4 248.7 + 249.1 249.6 250.2 250.8 251.5 252.3 253.1 254.1 255.0 256.1 257.2 258.4 259.7 261.0 262.3 263.8 265.3 266.8 + 268.4 270.1 271.8 273.6 275.4 277.2 279.2 281.1 283.1 285.1 287.2 289.3 291.4 293.6 295.8 298.0 300.2 302.5 + 304.8 307.1 309.4 311.7 314.1 316.4 318.7 321.1 323.4 325.8 328.1 330.5 332.8 335.1 337.4 339.7 341.9 344.2 + 346.4 348.6 350.8 352.9 355.0 357.1 359.1 361.1 363.0 364.9 366.8 368.6 370.4 372.1 373.7 375.4 376.9 378.4 + 379.8 381.2 382.5 383.8 385.0 386.1 387.1 388.1 389.0 389.9 390.7 391.4 392.0 392.6 393.0 393.5 393.8 394.1 + 394.2 394.4 394.4 394.4 394.2 394.1 393.8 393.5 393.0 392.6 392.0 391.4 390.7 389.9 389.0 388.1 387.1 386.1 + 385.0 383.8 382.5 381.2 379.8 378.4 376.9 375.4 373.7 372.1 370.4 368.6 366.8 364.9 363.0 361.1 359.1 357.1 + 355.0 352.9 350.8 348.6 346.4 344.2 341.9 339.7 337.4 335.1 332.8 330.5 328.1 325.8 323.4 321.1 ]plong + s[ 297.9 298.0 298.1 298.4 298.7 299.2 299.7 300.4 301.1 302.0 302.9 303.9 305.1 306.3 307.6 309.0 310.5 312.1 + 313.7 315.5 317.3 319.2 321.2 323.3 325.4 327.7 329.9 332.3 334.7 337.2 339.7 342.3 345.0 347.7 350.4 353.2 + 356.1 359.0 361.9 364.9 367.8 370.9 373.9 377.0 380.1 383.2 386.3 389.4 392.5 395.7 398.8 401.9 405.0 408.2 + 411.3 414.3 417.4 420.4 423.5 426.5 429.4 432.3 435.2 438.1 440.9 443.6 446.3 449.0 451.6 454.1 456.6 459.0 + 461.4 463.6 465.9 468.0 470.1 472.1 474.0 475.8 477.6 479.2 480.8 482.3 483.7 485.0 486.2 487.4 488.4 489.4 + 490.2 490.9 491.6 492.1 492.6 492.9 493.2 493.3 493.4 493.3 493.2 492.9 492.6 492.1 491.6 490.9 490.2 489.4 + 488.4 487.4 486.2 485.0 483.7 482.3 480.8 479.2 477.6 475.8 474.0 472.1 470.1 468.0 465.9 463.6 461.4 459.0 + 456.6 454.1 451.6 449.0 446.3 443.6 440.9 438.1 435.2 432.3 429.4 426.5 423.5 420.4 417.4 414.3 411.3 408.2 + 405.0 401.9 398.8 395.7 392.5 389.4 386.3 383.2 380.1 377.0 373.9 370.9 367.8 364.9 361.9 359.0 356.1 353.2 + 350.4 347.7 345.0 342.3 339.7 337.2 334.7 332.3 329.9 327.7 325.4 323.3 321.2 319.2 317.3 315.5 313.7 312.1 + 310.5 309.0 307.6 306.3 305.1 303.9 302.9 302.0 301.1 300.4 299.7 299.2 298.7 298.4 298.1 298.0 297.9 ][ 321.1 + 318.0 314.8 311.7 308.6 305.5 302.4 299.3 296.3 293.3 290.3 287.3 284.4 281.5 278.7 275.9 273.1 270.4 267.8 + 265.2 262.6 260.2 257.7 255.4 253.1 250.9 248.7 246.7 244.7 242.8 240.9 239.2 237.5 235.9 234.4 233.0 231.7 + 230.5 229.4 228.3 227.4 226.5 225.8 225.2 224.6 224.2 223.8 223.6 223.4 223.4 223.4 223.6 223.8 224.2 224.6 + 225.2 225.8 226.5 227.4 228.3 229.4 230.5 231.7 233.0 234.4 235.9 237.5 239.2 240.9 242.8 244.7 246.7 248.7 + 250.9 253.1 255.4 257.7 260.2 262.6 265.2 267.8 270.4 273.1 275.9 278.7 281.5 284.4 287.3 290.3 293.3 296.3 + 299.3 302.4 305.5 308.6 311.7 314.8 318.0 321.1 324.2 327.4 330.5 333.6 336.7 339.8 342.8 345.9 348.9 351.9 + 354.8 357.8 360.7 363.5 366.3 369.0 371.8 374.4 377.0 379.5 382.0 384.4 386.8 389.1 391.3 393.4 395.5 397.5 + 399.4 401.2 403.0 404.7 406.2 407.7 409.1 410.5 411.7 412.8 413.8 414.8 415.6 416.4 417.0 417.6 418.0 418.4 + 418.6 418.8 418.8 418.8 418.6 418.4 418.0 417.6 417.0 416.4 415.6 414.8 413.8 412.8 411.7 410.5 409.1 407.7 + 406.2 404.7 403.0 401.2 399.4 397.5 395.5 393.4 391.3 389.1 386.8 384.4 382.0 379.5 377.0 374.4 371.8 369.0 + 366.3 363.5 360.7 357.8 354.8 351.9 348.9 345.9 342.8 339.8 336.7 333.6 330.5 327.4 324.2 321.1 ]plong + s[ 273.5 273.5 273.7 274.1 274.5 275.1 275.7 276.5 277.5 278.5 279.7 281.0 282.4 283.9 285.6 287.3 289.2 291.2 + 293.3 295.5 297.7 300.1 302.6 305.2 307.9 310.7 313.5 316.5 319.5 322.6 325.8 329.0 332.3 335.7 339.1 342.6 + 346.2 349.8 353.5 357.2 360.9 364.7 368.5 372.3 376.2 380.0 383.9 387.8 391.7 395.7 399.6 403.5 407.4 411.3 + 415.2 419.0 422.8 426.6 430.4 434.2 437.8 441.5 445.1 448.7 452.2 455.6 459.0 462.3 465.6 468.7 471.8 474.8 + 477.8 480.6 483.4 486.1 488.7 491.2 493.6 495.9 498.0 500.1 502.1 504.0 505.7 507.4 508.9 510.3 511.6 512.8 + 513.8 514.8 515.6 516.3 516.8 517.3 517.6 517.8 517.8 517.8 517.6 517.3 516.8 516.3 515.6 514.8 513.8 512.8 + 511.6 510.3 508.9 507.4 505.7 504.0 502.1 500.1 498.0 495.9 493.6 491.2 488.7 486.1 483.4 480.6 477.8 474.8 + 471.8 468.7 465.6 462.3 459.0 455.6 452.2 448.7 445.1 441.5 437.8 434.2 430.4 426.6 422.8 419.0 415.2 411.3 + 407.4 403.5 399.6 395.7 391.7 387.8 383.9 380.0 376.2 372.3 368.5 364.7 360.9 357.2 353.5 349.8 346.2 342.6 + 339.1 335.7 332.3 329.0 325.8 322.6 319.5 316.5 313.5 310.7 307.9 305.2 302.6 300.1 297.7 295.5 293.3 291.2 + 289.2 287.3 285.6 283.9 282.4 281.0 279.7 278.5 277.5 276.5 275.7 275.1 274.5 274.1 273.7 273.5 273.5 ][ 321.1 + 317.2 313.3 309.4 305.5 301.6 297.7 293.9 290.1 286.3 282.6 278.9 275.2 271.6 268.1 264.6 261.1 257.8 254.4 + 251.2 248.0 244.9 241.9 239.0 236.1 233.3 230.6 228.1 225.6 223.2 220.9 218.7 216.6 214.6 212.8 211.0 209.4 + 207.8 206.4 205.1 204.0 202.9 202.0 201.2 200.5 199.9 199.5 199.2 199.0 198.9 199.0 199.2 199.5 199.9 200.5 + 201.2 202.0 202.9 204.0 205.1 206.4 207.8 209.4 211.0 212.8 214.6 216.6 218.7 220.9 223.2 225.6 228.1 230.6 + 233.3 236.1 239.0 241.9 244.9 248.0 251.2 254.4 257.8 261.1 264.6 268.1 271.6 275.2 278.9 282.6 286.3 290.1 + 293.9 297.7 301.6 305.5 309.4 313.3 317.2 321.1 325.0 328.9 332.8 336.7 340.6 344.4 348.3 352.1 355.8 359.6 + 363.3 366.9 370.5 374.1 377.6 381.0 384.4 387.7 391.0 394.2 397.3 400.3 403.2 406.1 408.8 411.5 414.1 416.6 + 419.0 421.3 423.5 425.6 427.5 429.4 431.2 432.8 434.3 435.7 437.0 438.2 439.3 440.2 441.0 441.7 442.3 442.7 + 443.0 443.2 443.3 443.2 443.0 442.7 442.3 441.7 441.0 440.2 439.3 438.2 437.0 435.7 434.3 432.8 431.2 429.4 + 427.5 425.6 423.5 421.3 419.0 416.6 414.1 411.5 408.8 406.1 403.2 400.3 397.3 394.2 391.0 387.7 384.4 381.0 + 377.6 374.1 370.5 366.9 363.3 359.6 355.8 352.1 348.3 344.4 340.6 336.7 332.8 328.9 325.0 321.1 ]plong + s[ 249.1 249.1 249.4 249.7 250.3 250.9 251.8 252.7 253.8 255.1 256.5 258.1 259.8 261.6 263.6 265.7 267.9 270.3 + 272.8 275.4 278.2 281.0 284.0 287.1 290.3 293.7 297.1 300.6 304.3 308.0 311.8 315.7 319.7 323.7 327.8 332.0 + 336.3 340.6 345.0 349.5 353.9 358.5 363.0 367.6 372.3 376.9 381.6 386.3 391.0 395.7 400.4 405.0 409.7 414.4 + 419.1 423.7 428.3 432.8 437.4 441.9 446.3 450.7 455.0 459.3 463.5 467.6 471.7 475.6 479.5 483.3 487.1 490.7 + 494.2 497.6 501.0 504.2 507.3 510.3 513.1 515.9 518.5 521.0 523.4 525.6 527.7 529.7 531.5 533.2 534.8 536.2 + 537.5 538.6 539.6 540.4 541.1 541.6 542.0 542.2 542.3 542.2 542.0 541.6 541.1 540.4 539.6 538.6 537.5 536.2 + 534.8 533.2 531.5 529.7 527.7 525.6 523.4 521.0 518.5 515.9 513.1 510.3 507.3 504.2 501.0 497.6 494.2 490.7 + 487.1 483.3 479.5 475.6 471.7 467.6 463.5 459.3 455.0 450.7 446.3 441.9 437.4 432.8 428.3 423.7 419.1 414.4 + 410.2 ][ 321.1 316.4 311.7 307.0 302.3 297.7 293.1 288.5 283.9 279.4 274.9 270.5 266.1 261.7 257.5 253.3 249.1 + 245.1 241.1 237.2 233.4 229.7 226.1 222.5 219.1 215.8 212.6 209.5 206.5 203.6 200.8 198.2 195.7 193.4 191.1 + 189.0 187.0 185.2 183.5 182.0 180.5 179.3 178.2 177.2 176.4 175.7 175.2 174.8 174.6 174.5 174.6 174.8 175.2 + 175.7 176.4 177.2 178.2 179.3 180.5 182.0 183.5 185.2 187.0 189.0 191.1 193.4 195.7 198.2 200.8 203.6 206.5 + 209.5 212.6 215.8 219.1 222.5 226.1 229.7 233.4 237.2 241.1 245.1 249.1 253.3 257.5 261.7 266.1 270.5 274.9 + 279.4 283.9 288.5 293.1 297.7 302.3 307.0 311.7 316.4 321.1 325.8 330.5 335.2 339.8 344.5 349.1 353.7 358.3 + 362.8 367.3 371.7 376.1 380.4 384.7 388.9 393.0 397.1 401.1 405.0 408.8 412.5 416.1 419.7 423.1 426.4 429.6 + 432.7 435.7 438.6 441.3 444.0 446.5 448.8 451.1 453.2 455.1 457.0 458.7 460.2 461.6 462.9 464.0 465.0 465.8 + 466.5 467.0 ]plong + s[ 381.1 376.9 372.3 367.6 363.0 358.5 353.9 349.5 345.0 340.6 336.3 332.0 327.8 323.7 319.7 315.7 311.8 308.0 + 304.3 300.6 297.1 293.7 290.3 287.1 284.0 281.0 278.2 275.4 272.8 270.3 267.9 265.7 263.6 261.6 259.8 258.1 + 256.5 255.1 253.8 252.7 251.8 250.9 250.3 249.7 249.4 249.1 249.1 ][ 467.0 466.5 465.8 465.0 464.0 462.9 461.6 + 460.2 458.7 457.0 455.1 453.2 451.1 448.8 446.5 444.0 441.3 438.6 435.7 432.7 429.6 426.4 423.1 419.7 416.1 + 412.5 408.8 405.0 401.1 397.1 393.0 388.9 384.7 380.4 376.1 371.7 367.3 362.8 358.3 353.7 349.1 344.5 339.8 + 335.2 330.5 325.8 321.1 ]plong + s[ 224.6 224.7 225.0 225.4 226.0 226.8 227.8 228.9 230.2 231.7 233.3 235.1 237.1 239.3 241.6 244.0 246.6 249.4 + 252.3 255.4 258.6 261.9 265.4 269.0 272.8 276.7 280.7 284.8 289.0 293.4 297.8 302.3 307.0 311.7 316.5 321.4 + 326.4 331.5 336.6 341.8 347.0 352.3 357.6 363.0 368.4 373.8 379.2 384.7 390.2 395.7 401.1 406.6 412.1 417.5 + 423.0 428.4 433.7 439.0 444.3 449.6 454.7 459.8 464.9 469.9 474.8 479.6 484.3 489.0 493.5 498.0 502.3 506.5 + 510.6 514.6 518.5 522.3 525.9 529.4 532.7 535.9 539.0 541.9 544.7 547.3 549.8 552.1 554.2 556.2 558.0 559.6 + 561.1 562.4 563.5 564.5 565.3 565.9 566.3 566.6 566.7 566.6 566.3 565.9 565.3 564.5 563.5 562.4 561.1 559.6 + 558.0 556.2 554.2 552.1 549.8 547.3 544.7 541.9 539.0 535.9 532.7 529.4 525.9 522.3 518.5 514.6 510.6 506.5 + 502.3 498.0 493.5 489.0 484.9 ][ 321.1 315.6 310.1 304.7 299.2 293.8 288.4 283.0 277.7 272.4 267.2 262.0 256.9 + 251.9 246.9 242.0 237.2 232.4 227.8 223.2 218.8 214.4 210.2 206.1 202.1 198.2 194.5 190.9 187.4 184.0 180.8 + 177.7 174.8 172.1 169.4 167.0 164.7 162.6 160.6 158.8 157.1 155.6 154.3 153.2 152.2 151.5 150.8 150.4 150.1 + 150.1 150.1 150.4 150.8 151.5 152.2 153.2 154.3 155.6 157.1 158.8 160.6 162.6 164.7 167.0 169.4 172.1 174.8 + 177.7 180.8 184.0 187.4 190.9 194.5 198.2 202.1 206.1 210.2 214.4 218.8 223.2 227.8 232.4 237.2 242.0 246.9 + 251.9 256.9 262.0 267.2 272.4 277.7 283.0 288.4 293.8 299.2 304.7 310.1 315.6 321.1 326.6 332.0 337.5 343.0 + 348.4 353.8 359.1 364.5 369.8 375.0 380.2 385.3 390.3 395.3 400.2 405.0 409.8 414.4 418.9 423.4 427.7 432.0 + 436.1 440.1 444.0 447.7 451.3 454.8 458.2 461.4 464.4 467.0 ]plong + s[ 306.4 302.3 297.8 293.4 289.0 284.8 280.7 276.7 272.8 269.0 265.4 261.9 258.6 255.4 252.3 249.4 246.6 244.0 + 241.6 239.3 237.1 235.1 233.3 231.7 230.2 228.9 227.8 226.8 226.0 225.4 225.0 224.7 224.6 ][ 467.0 464.4 461.4 + 458.2 454.8 451.3 447.7 444.0 440.1 436.1 432.0 427.7 423.4 418.9 414.4 409.8 405.0 400.2 395.3 390.3 385.3 + 380.2 375.0 369.8 364.5 359.1 353.8 348.4 343.0 337.5 332.0 326.6 321.1 ]plong + s[ 200.2 200.3 200.6 201.1 201.8 202.7 203.8 205.1 206.6 208.3 210.1 212.2 214.5 216.9 219.5 222.4 225.3 228.5 + 231.8 235.3 239.0 242.8 246.8 251.0 255.2 259.7 264.2 268.9 273.8 278.7 283.8 289.0 294.3 299.7 305.2 310.8 + 316.5 322.3 328.1 334.1 340.0 346.1 352.2 358.3 364.5 370.7 376.9 383.1 389.4 395.7 401.9 408.2 414.4 420.7 + 426.9 433.0 439.2 445.2 451.3 457.3 463.2 469.0 474.8 480.5 486.1 491.6 497.0 502.3 507.5 512.6 517.5 522.4 + 527.1 531.6 536.1 540.4 544.5 548.5 552.3 556.0 559.5 562.8 566.0 569.0 571.8 574.4 576.8 579.1 581.2 583.0 + 584.7 586.2 587.5 588.6 589.5 590.2 590.4 ][ 321.1 314.8 308.6 302.3 296.1 289.9 283.7 277.6 271.5 265.5 259.5 + 253.6 247.7 242.0 236.3 230.7 225.2 219.8 214.5 209.3 204.2 199.2 194.4 189.7 185.1 180.7 176.4 172.2 168.3 + 164.4 160.8 157.3 153.9 150.8 147.8 145.0 142.3 139.9 137.6 135.6 133.7 132.0 130.5 129.2 128.1 127.2 126.5 + 126.0 125.7 125.6 125.7 126.0 126.5 127.2 128.1 129.2 130.5 132.0 133.7 135.6 137.6 139.9 142.3 145.0 147.8 + 150.8 153.9 157.3 160.8 164.4 168.3 172.2 176.4 180.7 185.1 189.7 194.4 199.2 204.2 209.3 214.5 219.8 225.2 + 230.7 236.3 242.0 247.7 253.6 259.5 265.5 271.5 277.6 283.7 289.9 296.1 302.3 304.4 ]plong + s[ 590.4 590.2 589.5 588.6 587.5 586.2 584.7 583.0 581.2 579.1 576.8 574.4 571.8 569.0 566.0 562.8 559.5 556.0 + 552.3 548.5 544.5 540.4 536.1 531.6 527.1 525.7 ][ 337.7 339.9 346.1 352.3 358.5 364.6 370.7 376.7 382.7 388.6 + 394.4 400.2 405.9 411.5 417.0 422.4 427.7 432.9 438.0 443.0 447.8 452.5 457.1 461.5 465.8 467.0 ]plong + s[ 265.6 264.2 259.7 255.2 251.0 246.8 242.8 239.0 235.3 231.8 228.5 225.3 222.4 219.5 216.9 214.5 212.2 210.1 + 208.3 206.6 205.1 203.8 202.7 201.8 201.1 200.6 200.3 200.2 ][ 467.0 465.8 461.5 457.1 452.5 447.8 443.0 438.0 + 432.9 427.7 422.4 417.0 411.5 405.9 400.2 394.4 388.6 382.7 376.7 370.7 364.6 358.5 352.3 346.1 339.9 333.6 + 327.4 321.1 ]plong + s[ 199.5 200.7 204.0 207.6 211.4 215.3 219.4 223.7 228.2 232.9 237.7 242.7 247.8 253.1 258.5 264.1 269.8 275.7 + 281.7 287.7 293.9 300.2 306.6 313.1 319.7 326.4 333.1 339.9 346.7 353.6 360.6 367.5 374.5 381.6 388.6 395.7 + 402.7 409.7 416.8 423.8 430.8 437.7 444.6 451.4 458.2 464.9 471.6 478.2 484.7 491.1 497.4 503.6 509.7 515.6 + 521.5 527.2 532.8 538.2 543.5 548.6 553.6 558.4 563.1 567.6 571.9 576.0 580.0 583.7 587.3 590.4 ][ 221.8 219.4 + 213.2 207.1 201.1 195.3 189.6 184.0 178.5 173.2 168.1 163.1 158.3 153.6 149.2 144.9 140.7 136.8 133.0 129.5 + 126.1 123.0 120.0 117.3 114.7 112.4 110.3 108.4 106.7 105.2 104.0 103.0 102.2 101.6 101.3 101.2 101.3 101.6 + 102.2 103.0 104.0 105.2 106.7 108.4 110.3 112.4 114.7 117.3 120.0 123.0 126.1 129.5 133.0 136.8 140.7 144.9 + 149.2 153.6 158.3 163.1 168.1 173.2 178.5 184.0 189.6 195.3 201.1 207.1 213.2 219.0 ]plong + s[ 590.4 587.3 583.7 580.0 576.0 571.9 567.6 563.1 560.2 ][ 423.2 429.0 435.1 441.1 446.9 452.6 458.2 463.6 467.0 +]plong + s[ 231.1 228.2 223.7 219.4 215.3 211.4 207.6 204.0 200.7 199.5 ][ 467.0 463.6 458.2 452.6 446.9 441.1 435.1 429.0 + 422.8 420.3 ]plong + s[ 199.5 199.8 204.6 209.6 214.8 220.1 225.7 231.4 237.3 243.3 249.5 255.9 262.4 269.0 275.8 282.6 289.6 296.8 + 304.0 311.3 318.7 326.1 333.7 341.3 348.9 356.7 364.4 372.2 380.0 387.8 395.7 403.5 411.3 419.1 426.9 434.7 + 442.4 450.0 457.6 465.2 472.6 480.0 487.3 494.6 501.7 508.7 515.6 522.3 529.0 535.4 541.8 548.0 554.0 559.9 + 565.6 571.2 576.5 581.7 586.7 590.4 ][ 175.5 174.9 168.7 162.7 156.8 151.1 145.6 140.2 135.0 130.1 125.3 120.7 + 116.3 112.1 108.2 104.5 100.9 97.7 94.6 91.8 89.2 86.8 84.7 82.9 81.3 79.9 78.8 77.9 77.3 76.9 + 76.7 76.9 77.3 77.9 78.8 79.9 81.3 82.9 84.7 86.8 89.2 91.8 94.6 97.7 100.9 104.5 108.2 112.1 + 116.3 120.7 125.3 130.1 135.0 140.2 145.6 151.1 156.8 162.7 168.7 173.5 ]plong + 199.5 466.7 199.6 467.0 2 pls + s[ 199.5 202.6 208.7 215.0 221.4 228.1 234.9 241.9 249.0 256.3 263.8 271.3 279.0 285.4 ][ 137.4 134.1 128.0 122.1 + 116.4 111.0 105.7 100.6 95.8 91.2 86.9 82.8 78.9 76.0 ]plong + s[ 506.0 512.3 520.0 527.5 535.0 542.3 549.4 556.4 563.2 569.9 576.4 582.6 588.7 590.4 ][ 76.0 78.9 82.8 86.9 + 91.2 95.8 100.6 105.7 111.0 116.4 122.1 128.0 134.1 135.9 ]plong + s[ 199.5 205.6 212.8 220.3 227.9 234.7 ][ 103.2 97.8 91.9 86.1 80.6 76.0 ]plong + s[ 556.6 563.4 571.0 578.5 585.7 590.4 ][ 76.0 80.6 86.1 91.9 97.8 101.9 ]plong + s[ 395.7 371.5 347.3 323.1 298.9 274.7 250.5 226.3 202.1 199.5 ][ 321.1 317.6 314.2 310.7 307.3 303.8 300.4 297.0 + 293.5 293.1 ]plong + s[ 395.7 376.1 356.5 336.9 317.4 297.8 278.2 258.6 239.1 219.5 199.9 199.5 ][ 321.1 306.5 291.8 277.2 262.6 248.0 + 233.4 218.7 204.1 189.5 174.9 174.5 ]plong + s[ 395.7 385.5 375.3 365.2 355.0 344.8 334.6 324.5 314.3 304.1 294.0 283.8 283.5 ][ 321.1 298.9 276.7 254.4 232.2 + 210.0 187.8 165.6 143.3 121.1 98.9 76.7 76.0 ]plong + s[ 395.7 397.4 399.1 400.8 402.6 404.3 406.0 407.8 409.5 411.2 412.9 413.0 ][ 321.1 296.7 272.3 248.0 223.6 199.2 + 174.9 150.5 126.1 101.7 77.4 76.0 ]plong + s[ 395.7 408.9 422.1 435.3 448.5 461.7 474.9 488.1 501.3 514.5 527.7 540.9 553.0 ][ 321.1 300.5 280.0 259.4 238.8 + 218.3 197.7 177.2 156.6 136.0 115.5 94.9 76.0 ]plong + s[ 395.7 417.1 438.5 460.0 481.4 502.9 524.3 545.8 567.2 588.6 590.4 ][ 321.1 309.4 297.7 285.9 274.2 262.5 250.8 + 239.1 227.4 215.7 214.7 ]plong + s[ 395.7 420.1 444.5 469.0 493.4 517.8 542.3 566.7 590.4 ][ 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 +]plong + s[ 395.7 417.1 438.5 460.0 481.4 502.9 524.3 545.8 567.2 588.6 590.4 ][ 321.1 332.8 344.5 356.2 367.9 379.7 391.4 + 403.1 414.8 426.5 427.5 ]plong + s[ 395.7 408.9 422.1 435.3 448.5 461.7 474.9 488.1 489.3 ][ 321.1 341.6 362.2 382.8 403.3 423.9 444.5 465.0 467.0 +]plong + s[ 395.7 397.4 399.1 400.8 402.6 404.3 406.0 ][ 321.1 345.5 369.8 394.2 418.6 443.0 467.0 ]plong + s[ 395.7 385.5 375.3 365.2 355.0 344.8 334.6 328.9 ][ 321.1 343.3 365.5 387.7 410.0 432.2 454.4 467.0 ]plong + s[ 395.7 376.1 356.5 336.9 317.4 297.8 278.2 258.6 239.1 219.5 200.4 ][ 321.1 335.7 350.3 365.0 379.6 394.2 408.8 + 423.5 438.1 452.7 467.0 ]plong + s[ 395.7 371.5 347.3 323.1 298.9 274.7 250.5 226.3 202.1 199.5 ][ 321.1 324.5 328.0 331.4 334.9 338.3 341.8 345.2 + 348.7 349.1 ]plong + 400.7 318.4 400.5 321.1 2 pls + 405.6 318.4 405.4 321.1 2 pls + 410.5 318.4 410.3 321.1 2 pls + 415.3 318.4 415.2 321.1 2 pls + 425.1 318.4 425.0 321.1 2 pls + 430.0 318.4 429.9 321.1 2 pls + 434.9 318.4 434.8 321.1 2 pls + 439.8 318.4 439.6 321.1 2 pls + 449.5 318.4 449.4 321.1 2 pls + 454.4 318.4 454.3 321.1 2 pls + 459.3 318.4 459.2 321.1 2 pls + 464.2 318.4 464.1 321.1 2 pls + 474.0 318.4 473.8 321.1 2 pls + 478.9 318.4 478.7 321.1 2 pls + 483.8 318.4 483.6 321.1 2 pls + 488.6 318.4 488.5 321.1 2 pls + 498.4 318.4 498.3 321.1 2 pls + 503.3 318.4 503.2 321.1 2 pls + 508.2 318.4 508.1 321.1 2 pls + 513.1 318.4 512.9 321.1 2 pls + 522.8 318.4 522.7 321.1 2 pls + 527.7 318.4 527.6 321.1 2 pls + 532.6 318.4 532.5 321.1 2 pls + 537.5 318.4 537.4 321.1 2 pls + 547.3 318.4 547.1 321.1 2 pls + 552.2 318.4 552.0 321.1 2 pls + 557.1 318.4 556.9 321.1 2 pls + 561.9 318.4 561.8 321.1 2 pls + 571.7 318.4 571.6 321.1 2 pls + 576.6 318.4 576.5 321.1 2 pls + 581.5 318.4 581.4 321.1 2 pls + 586.4 318.4 586.2 321.1 2 pls + 246.4 314.9 249.2 315.0 2 pls + 250.7 285.4 253.3 286.0 2 pls + 254.9 271.1 257.5 272.0 2 pls + 260.6 257.3 263.1 258.4 2 pls + 267.7 244.1 270.0 245.5 2 pls + 285.5 220.2 287.6 222.1 2 pls + 296.2 209.7 298.0 211.8 2 pls + 307.8 200.3 309.4 202.6 2 pls + 320.3 192.2 321.6 194.5 2 pls + 347.4 179.8 348.3 182.4 2 pls + 361.7 175.7 362.3 178.3 2 pls + 376.4 173.0 376.8 175.7 2 pls + 391.3 171.8 391.4 174.5 2 pls + 421.0 173.9 420.6 176.6 2 pls + 435.6 177.2 434.9 179.8 2 pls + 449.8 181.9 448.8 184.4 2 pls + 463.4 188.0 462.2 190.4 2 pls + 488.5 204.1 486.8 206.3 2 pls + 499.7 214.0 497.8 215.9 2 pls + 509.9 224.9 507.8 226.6 2 pls + 518.9 236.8 516.7 238.3 2 pls + 533.2 262.9 530.7 264.0 2 pls + 538.3 277.0 535.7 277.8 2 pls + 542.0 291.4 539.3 292.0 2 pls + 544.3 306.2 541.5 306.5 2 pls + 544.3 336.0 541.5 335.7 2 pls + 542.0 350.8 539.3 350.2 2 pls + 538.3 365.2 535.7 364.4 2 pls + 533.2 379.2 530.7 378.2 2 pls + 518.9 405.4 516.7 403.9 2 pls + 509.9 417.3 507.8 415.5 2 pls + 499.7 428.2 497.8 426.3 2 pls + 488.5 438.1 486.8 435.9 2 pls + 463.4 454.2 462.2 451.7 2 pls + 449.8 460.3 448.8 457.7 2 pls + 435.6 465.0 434.9 462.4 2 pls + 421.0 468.3 420.6 465.6 2 pls + 376.4 469.2 376.8 466.5 2 pls + 361.7 466.5 362.3 463.9 2 pls + 347.4 462.4 348.3 459.8 2 pls + 320.3 450.0 321.6 447.6 2 pls + 307.8 441.8 309.4 439.6 2 pls + 296.2 432.5 298.0 430.4 2 pls + 285.5 422.0 287.6 420.1 2 pls + 267.7 398.1 270.0 396.7 2 pls + 260.6 384.9 263.1 383.7 2 pls + 254.9 371.1 257.5 370.2 2 pls + 250.7 356.8 253.3 356.2 2 pls + 246.4 327.3 249.2 327.2 2 pls + s[ 395.7 397.4 399.1 400.9 402.6 404.4 406.1 407.9 409.6 411.4 413.1 414.9 416.6 418.3 420.1 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 420.1 421.8 423.6 425.3 427.1 428.8 430.6 432.3 434.1 435.8 437.5 439.3 441.0 442.8 444.5 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 444.5 446.3 448.0 449.8 451.5 453.3 455.0 456.7 458.5 460.2 462.0 463.7 465.5 467.2 469.0 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 469.0 470.7 472.4 474.2 475.9 477.7 479.4 481.2 482.9 484.7 486.4 488.2 489.9 491.6 493.4 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 493.4 495.1 496.9 498.6 500.4 502.1 503.9 505.6 507.4 509.1 510.8 512.6 514.3 516.1 517.8 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 517.8 519.6 521.3 523.1 524.8 526.6 528.3 530.0 531.8 533.5 535.3 537.0 538.8 540.5 542.3 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 542.3 544.0 545.8 547.5 549.2 551.0 552.7 554.5 556.2 558.0 559.7 561.5 563.2 564.9 566.7 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 566.7 568.4 570.2 571.9 573.7 575.4 577.2 578.9 580.7 582.4 584.1 585.9 587.6 589.4 590.4 ][ 321.1 321.1 321.1 + 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 321.1 ]plong + s[ 250.5 250.3 250.1 250.0 249.8 249.7 249.5 249.4 249.3 249.2 249.2 249.1 249.1 249.1 249.1 ][ 300.4 301.9 303.3 + 304.8 306.3 307.8 309.2 310.7 312.2 313.7 315.2 316.6 318.1 319.6 321.1 ]plong + s[ 250.5 251.4 252.4 253.6 254.9 256.5 258.2 260.1 262.2 264.5 266.9 269.5 272.2 275.1 278.2 ][ 300.4 295.2 290.1 + 285.0 279.9 274.9 270.0 265.1 260.3 255.6 251.0 246.4 242.0 237.6 233.4 ]plong + s[ 278.2 281.4 284.8 288.3 291.9 295.7 299.5 303.6 307.7 311.9 316.3 320.7 325.3 329.9 334.6 ][ 233.4 229.2 225.2 + 221.3 217.5 213.9 210.4 207.0 203.8 200.7 197.8 195.1 192.5 190.0 187.8 ]plong + s[ 334.6 339.4 344.3 349.3 354.2 359.3 364.4 369.5 374.7 379.9 385.1 390.3 395.6 400.8 406.0 ][ 187.8 185.7 183.8 + 182.0 180.5 179.1 177.9 176.8 176.0 175.3 174.9 174.6 174.5 174.6 174.9 ]plong + s[ 406.0 411.2 416.4 421.6 426.7 431.8 436.9 441.9 446.8 451.7 456.5 461.2 465.9 470.4 474.9 ][ 174.9 175.3 176.0 + 176.8 177.8 179.0 180.4 182.0 183.7 185.6 187.7 190.0 192.4 195.0 197.7 ]plong + s[ 474.9 479.2 483.5 487.6 491.6 495.5 499.3 502.9 506.4 509.8 513.0 516.1 519.0 521.7 524.3 ][ 197.7 200.6 203.7 + 206.9 210.3 213.8 217.4 221.2 225.1 229.1 233.2 237.4 241.8 246.3 250.8 ]plong + s[ 524.3 526.7 529.0 531.1 533.0 534.8 536.3 537.7 538.9 539.9 540.8 541.4 541.9 542.2 542.3 ][ 250.8 255.4 260.2 + 265.0 269.8 274.8 279.8 284.8 289.9 295.0 300.2 305.4 310.6 315.9 321.1 ]plong + s[ 542.3 542.2 541.9 541.4 540.8 539.9 538.9 537.7 536.3 534.8 533.0 531.1 529.0 526.7 524.3 ][ 321.1 326.3 331.6 + 336.8 342.0 347.1 352.3 357.4 362.4 367.4 372.3 377.2 382.0 386.7 391.4 ]plong + s[ 524.3 521.7 519.0 516.1 513.0 509.8 506.4 502.9 499.3 495.5 491.6 487.6 483.5 479.2 474.9 ][ 391.4 395.9 400.4 + 404.7 409.0 413.1 417.1 421.0 424.8 428.4 431.9 435.3 438.5 441.5 444.5 ]plong + s[ 474.9 470.4 465.9 461.2 456.5 451.7 446.8 441.9 436.9 431.8 426.7 421.6 416.4 411.2 410.1 ][ 444.5 447.2 449.8 + 452.2 454.5 456.6 458.5 460.2 461.8 463.2 464.4 465.4 466.2 466.9 467.0 ]plong + s[ 381.2 379.9 374.7 369.5 364.4 359.3 354.2 349.3 344.3 339.4 334.6 ][ 467.0 466.8 466.2 465.3 464.3 463.1 461.7 + 460.2 458.4 456.5 454.4 ]plong + s[ 334.6 329.9 325.3 320.7 316.3 311.9 307.7 303.6 299.5 295.7 291.9 288.3 284.8 281.4 278.2 ][ 454.4 452.1 449.7 + 447.1 444.4 441.4 438.4 435.2 431.8 428.3 424.7 420.9 417.0 413.0 408.8 ]plong + s[ 278.2 275.1 272.2 269.5 266.9 264.5 262.2 260.1 258.2 256.5 254.9 253.6 252.4 251.4 250.5 ][ 408.8 404.6 400.2 + 395.8 391.2 386.6 381.8 377.0 372.2 367.2 362.2 357.2 352.1 346.9 341.8 ]plong + s[ 250.5 250.3 250.1 250.0 249.8 249.7 249.5 249.4 249.3 249.2 249.2 249.1 249.1 249.1 249.1 ][ 341.8 340.3 338.8 + 337.4 335.9 334.4 332.9 331.5 330.0 328.5 327.0 325.5 324.1 322.6 321.1 ]plong + s[ 250.5 250.3 250.1 250.0 249.8 249.7 249.5 249.4 249.3 249.2 249.2 249.1 249.1 249.1 249.1 ][ 341.8 340.3 338.8 + 337.4 335.9 334.4 332.9 331.5 330.0 328.5 327.0 325.5 324.1 322.6 321.1 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 384.38 323.33 moveto[ 384.4 384.4 406.9 406.9 ][ 323.3 335.4 335.4 323.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 389.0 387.7 386.9 386.5 386.5 386.9 387.7 389.0 389.8 391.1 391.9 392.3 392.3 391.9 391.1 389.8 389.0 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong + 395.7 325.8 396.1 325.4 395.7 325.0 395.2 325.4 395.7 325.8 5 pls + s[ 401.5 400.3 399.4 399.0 399.0 399.4 400.3 401.5 402.3 403.6 404.4 404.8 404.8 404.4 403.6 402.3 401.5 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 426.15 323.33 moveto[ 426.1 426.1 464.2 464.2 ][ 323.3 335.4 335.4 323.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 430.3 325.0 430.3 333.8 429.1 332.5 428.2 332.1 4 pls + s[ 437.8 436.6 435.8 435.3 435.3 435.8 436.6 437.8 438.7 439.9 440.8 441.2 441.2 440.8 439.9 438.7 437.8 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong + s[ 446.2 444.9 444.1 443.7 443.7 444.1 444.9 446.2 447.0 448.3 449.1 449.5 449.5 449.1 448.3 447.0 446.2 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong + 452.9 325.8 453.3 325.4 452.9 325.0 452.5 325.4 452.9 325.8 5 pls + s[ 458.7 457.5 456.6 456.2 456.2 456.6 457.5 458.7 459.6 460.8 461.6 462.1 462.1 461.6 460.8 459.6 458.7 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 473.76 323.33 moveto[ 473.8 473.8 513.0 513.0 ][ 323.3 335.4 335.4 323.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 476.3 476.3 476.7 477.1 477.9 479.6 480.4 480.9 481.3 481.3 480.9 480.0 475.8 481.7 ][ 331.7 332.1 332.9 333.4 + 333.8 333.8 333.4 332.9 332.1 331.3 330.4 329.2 325.0 325.0 ]plong + s[ 486.7 485.5 484.6 484.2 484.2 484.6 485.5 486.7 487.5 488.8 489.6 490.1 490.1 489.6 488.8 487.5 486.7 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong + s[ 495.1 493.8 493.0 492.6 492.6 493.0 493.8 495.1 495.9 497.2 498.0 498.4 498.4 498.0 497.2 495.9 495.1 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong + 501.7 325.8 502.2 325.4 501.7 325.0 501.3 325.4 501.7 325.8 5 pls + s[ 507.6 506.3 505.5 505.1 505.1 505.5 506.3 507.6 508.4 509.7 510.5 510.9 510.9 510.5 509.7 508.4 507.6 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 522.63 323.33 moveto[ 522.6 522.6 561.9 561.9 ][ 323.3 335.4 335.4 323.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 525.6 530.1 527.6 528.9 529.7 530.1 530.6 530.6 530.1 529.3 528.1 526.8 525.6 525.1 524.7 ][ 333.8 333.8 330.4 + 330.4 330.0 329.6 328.3 327.5 326.3 325.4 325.0 325.0 325.4 325.8 326.7 ]plong + s[ 535.6 534.3 533.5 533.1 533.1 533.5 534.3 535.6 536.4 537.7 538.5 538.9 538.9 538.5 537.7 536.4 535.6 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong + s[ 543.9 542.7 541.8 541.4 541.4 541.8 542.7 543.9 544.8 546.0 546.9 547.3 547.3 546.9 546.0 544.8 543.9 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong + 550.6 325.8 551.0 325.4 550.6 325.0 550.2 325.4 550.6 325.8 5 pls + s[ 556.5 555.2 554.4 554.0 554.0 554.4 555.2 556.5 557.3 558.5 559.4 559.8 559.8 559.4 558.5 557.3 556.5 ][ 333.8 + 333.4 332.1 330.0 328.8 326.7 325.4 325.0 325.0 325.4 326.7 328.8 330.0 332.1 333.4 333.8 333.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 250.40 316.85 moveto[ 250.4 262.4 267.1 255.1 ][ 316.8 318.6 285.9 284.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 257.1 308.1 256.1 315.6 2 pls + s[ 262.6 263.3 259.6 259.8 259.5 259.2 258.0 257.2 255.9 254.9 254.3 254.1 254.4 254.7 255.5 ][ 305.1 300.6 302.6 + 301.3 300.4 300.0 299.4 299.3 299.5 300.2 301.4 302.6 303.9 304.4 304.9 ]plong + 256.0 295.7 255.6 295.2 255.2 295.6 255.5 296.1 256.0 295.7 5 pls + s[ 264.7 264.1 262.7 260.6 259.4 257.4 256.2 256.0 256.1 256.7 258.1 260.2 261.4 263.5 264.6 264.8 264.7 ][ 291.1 + 292.2 292.9 293.0 292.8 292.1 291.1 289.8 289.0 287.8 287.2 287.0 287.2 287.9 288.9 290.2 291.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 270.21 247.72 moveto[ 270.2 279.9 299.7 290.0 ][ 247.7 255.0 228.6 221.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 280.3 243.3 275.8 249.3 2 pls + s[ 284.7 285.0 285.9 286.5 287.3 288.3 288.5 288.4 288.0 287.3 286.4 284.9 279.1 282.6 ][ 242.4 242.6 242.8 242.7 + 242.3 241.0 240.1 239.5 238.6 238.1 238.0 237.9 238.7 234.0 ]plong + 285.3 231.9 285.2 231.3 284.6 231.4 284.7 231.9 285.3 231.9 5 pls + s[ 296.6 294.1 290.9 291.4 292.5 293.3 293.7 293.5 292.8 292.1 290.9 289.7 288.6 287.9 287.4 287.5 287.9 ][ 229.9 + 233.3 231.4 231.3 230.5 229.5 228.3 227.1 226.0 225.5 225.1 225.3 226.0 227.0 228.3 228.9 229.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 320.75 196.56 moveto[ 320.7 325.8 355.8 350.8 ][ 196.6 207.6 193.9 182.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 331.7 197.5 324.9 200.6 2 pls + s[ 336.0 336.2 336.9 337.4 338.4 339.9 340.5 340.7 340.7 340.4 339.7 338.4 332.8 338.2 ][ 198.8 199.2 199.8 200.0 + 200.0 199.3 198.6 198.0 197.1 196.4 195.8 195.0 192.9 190.5 ]plong + 341.6 189.9 341.8 189.3 341.2 189.1 341.0 189.6 341.6 189.9 5 pls + s[ 350.2 348.8 347.6 346.3 345.8 345.3 345.6 346.5 347.3 348.6 349.9 351.1 351.6 352.1 351.9 350.9 350.2 ][ 194.7 + 194.8 194.0 192.3 191.1 189.1 187.6 186.7 186.3 186.2 187.0 188.7 189.9 191.9 193.4 194.3 194.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 389.62 175.89 moveto[ 389.6 388.7 421.6 422.5 ][ 175.9 188.0 190.4 178.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 398.8 182.0 391.3 181.5 2 pls + 405.3 178.7 404.7 187.5 403.5 186.1 402.7 185.6 4 pls + 411.1 180.0 411.5 179.6 411.2 179.1 410.7 179.5 411.1 180.0 5 pls + s[ 418.8 414.7 414.5 414.9 416.1 417.4 418.7 419.6 420.1 420.1 419.8 419.0 417.8 416.6 415.3 414.8 414.4 ][ 188.5 + 188.2 184.4 184.9 185.4 185.5 185.1 184.4 183.2 182.3 181.0 180.1 179.6 179.5 179.9 180.3 181.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 459.97 190.77 moveto[ 460.0 453.4 481.1 487.7 ][ 190.8 200.9 218.8 208.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 465.1 200.5 458.8 196.5 2 pls + 472.4 200.8 467.6 208.2 467.3 206.4 466.8 205.6 4 pls + 476.9 204.7 477.4 204.5 477.3 204.0 476.7 204.1 476.9 204.7 5 pls + s[ 477.5 476.6 476.6 477.4 478.1 479.6 480.9 482.2 482.9 483.7 483.8 483.0 482.3 480.8 479.4 478.2 477.5 ][ 214.5 + 213.5 212.0 210.0 208.9 207.4 206.8 207.1 207.6 208.6 210.1 212.1 213.1 214.7 215.3 214.9 214.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 514.51 237.60 moveto[ 514.5 503.9 519.8 530.4 ][ 237.6 243.4 272.4 266.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 514.4 248.6 510.8 242.0 2 pls + s[ 512.6 512.4 513.1 514.7 515.8 517.8 519.3 520.3 520.7 520.9 520.2 518.6 517.5 515.5 514.0 513.0 512.6 ][ 255.8 + 254.5 253.2 251.8 251.2 250.6 250.7 251.6 252.3 253.6 255.0 256.3 256.9 257.6 257.4 256.5 255.8 ]plong + 522.8 257.8 523.3 258.0 523.5 257.4 522.9 257.3 522.8 257.8 5 pls + s[ 519.8 517.8 520.9 520.8 521.0 521.6 522.6 523.7 525.0 525.7 526.6 527.0 526.7 526.1 525.1 524.6 523.6 ][ 269.0 + 265.3 263.1 263.7 265.0 266.1 267.0 267.3 267.1 266.7 265.7 264.6 263.3 262.2 261.3 261.1 261.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 504.85 393.64 moveto[ 504.8 515.5 526.3 515.6 ][ 393.6 399.4 379.6 373.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 516.2 515.3 513.8 511.7 510.6 509.0 508.3 508.5 508.9 509.9 511.4 513.4 514.5 516.1 516.8 516.6 516.2 ][ 394.6 + 395.5 395.6 395.0 394.4 393.0 391.7 390.4 389.7 388.8 388.6 389.3 389.9 391.2 392.6 393.9 394.6 ]plong + 512.4 384.9 512.3 384.4 511.7 384.5 511.9 385.1 512.4 384.9 5 pls + s[ 523.4 521.4 517.9 518.5 519.4 520.0 520.3 519.9 519.0 518.3 517.0 515.9 514.9 514.3 514.1 514.2 514.8 ][ 381.4 + 385.0 383.6 383.4 382.5 381.4 380.1 379.0 378.0 377.6 377.4 377.8 378.7 379.8 381.1 381.6 382.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 457.75 436.43 moveto[ 457.8 464.3 482.2 475.6 ][ 436.4 446.6 435.1 424.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 462.2 435.6 466.9 442.9 465.2 442.6 464.3 442.7 4 pls + 467.5 433.1 467.7 432.5 467.1 432.4 467.0 433.0 467.5 433.1 5 pls + s[ 476.8 475.5 474.1 472.6 471.9 471.1 471.2 472.0 472.7 474.0 475.4 476.8 477.5 478.3 478.3 477.5 476.8 ][ 436.6 + 436.9 436.3 434.8 433.7 431.8 430.3 429.2 428.8 428.4 429.0 430.6 431.6 433.6 435.1 436.1 436.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 394.86 452.08 moveto[ 394.9 395.7 417.0 416.1 ][ 452.1 464.2 462.6 450.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 399.1 453.4 399.8 462.2 398.4 461.0 397.6 460.7 4 pls + 405.0 453.8 405.4 453.4 405.0 453.0 404.6 453.5 405.0 453.8 5 pls + s[ 413.9 409.8 409.1 409.5 410.8 412.1 413.3 414.1 414.4 414.3 413.8 412.9 411.6 410.4 409.2 408.8 408.4 ][ 461.1 + 461.5 457.7 458.1 458.4 458.4 457.8 457.0 455.7 454.8 453.6 452.8 452.5 452.6 453.1 453.6 454.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 331.01 435.14 moveto[ 331.0 326.0 346.5 351.5 ][ 435.1 446.2 455.5 444.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 329.8 329.7 329.7 329.9 330.5 332.0 332.9 333.5 334.2 334.6 334.5 334.3 332.2 337.5 ][ 443.8 444.2 445.1 445.7 + 446.4 447.1 447.0 446.8 446.2 445.5 444.5 443.1 437.5 439.9 ]plong + 340.2 442.1 340.8 441.9 340.6 441.3 340.0 441.5 340.2 442.1 5 pls + s[ 342.3 341.3 341.1 341.6 342.1 343.3 344.6 345.9 346.7 347.6 347.9 347.4 346.9 345.6 344.4 343.0 342.3 ][ 451.7 + 450.8 449.4 447.3 446.1 444.4 443.6 443.8 444.1 445.0 446.5 448.6 449.7 451.4 452.2 452.1 451.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 284.24 390.19 moveto[ 284.2 274.6 288.1 297.8 ][ 390.2 397.5 415.5 408.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 279.1 278.7 278.3 278.2 278.4 279.4 280.2 280.8 281.7 282.4 282.8 283.3 284.2 287.7 ][ 397.2 397.5 398.3 398.9 + 399.8 401.1 401.6 401.6 401.5 401.0 400.1 398.7 392.9 397.5 ]plong + 289.0 400.7 289.6 400.8 289.7 400.2 289.1 400.1 289.0 400.7 5 pls + s[ 287.7 285.2 287.9 287.8 288.3 289.0 290.1 291.3 292.5 293.2 293.9 294.1 293.7 292.9 291.9 291.3 290.4 ][ 412.2 + 408.8 406.2 406.8 408.1 409.1 409.8 410.0 409.6 409.1 408.0 406.8 405.6 404.5 403.8 403.7 403.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 264.76 328.31 moveto[ 264.8 252.8 256.0 268.0 ][ 328.3 330.1 352.4 350.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 254.8 255.5 258.4 258.6 259.2 259.6 260.9 261.8 262.9 263.6 263.9 263.7 263.1 262.6 261.7 ][ 332.7 337.3 334.3 + 335.5 336.3 336.7 336.9 336.8 336.2 335.2 333.9 332.7 331.5 331.2 330.9 ]plong + 263.9 339.8 264.4 340.2 264.7 339.7 264.2 339.4 263.9 339.8 5 pls + s[ 256.9 257.1 258.2 260.2 261.5 263.6 265.0 265.6 265.7 265.4 264.3 262.3 261.1 259.0 257.6 257.0 256.9 ][ 346.8 + 345.5 344.5 343.7 343.6 343.7 344.3 345.5 346.3 347.6 348.6 349.3 349.5 349.4 348.8 347.6 346.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 488.73 moveto[ 360.9 360.9 435.2 435.2 ][ 488.7 500.8 500.8 488.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 490.4 366.3 499.2 2 pls + 369.7 490.4 366.3 499.2 2 pls + 368.4 493.3 364.2 493.3 2 pls + 378.4 490.4 378.4 499.2 2 pls + 383.9 499.2 378.4 499.2 2 pls + 381.8 495.0 378.4 495.0 2 pls + 385.9 490.4 385.9 499.2 2 pls + 391.0 490.4 391.0 499.2 2 pls + 393.9 499.2 388.0 499.2 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 497.9 498.8 499.2 499.2 498.8 497.9 497.1 496.2 495.8 495.4 494.6 494.2 493.7 492.9 491.7 490.8 + 490.4 490.4 490.8 491.7 ]plong + 413.5 490.4 412.7 490.4 411.8 490.8 411.4 492.1 411.4 499.2 5 pls + 413.1 496.2 410.2 496.2 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 493.7 + 493.7 494.6 495.4 495.8 496.2 496.2 495.8 495.0 493.7 492.9 491.7 490.8 490.4 490.4 490.8 491.7 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 495.0 + 495.8 496.2 496.2 495.8 495.0 494.2 493.7 493.3 492.9 492.1 491.7 490.8 490.4 490.4 490.8 491.7 ]plong + 433.1 490.4 432.3 490.4 431.5 490.8 431.1 492.1 431.1 499.2 5 pls + 432.7 496.2 429.8 496.2 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/polco2.attr b/ast_tester/polco2.attr new file mode 100644 index 0000000..5019421 --- /dev/null +++ b/ast_tester/polco2.attr @@ -0,0 +1 @@ +Grid=0,labelling=exterior,gap(1)=50 diff --git a/ast_tester/polco2.box b/ast_tester/polco2.box new file mode 100644 index 0000000..0661792 --- /dev/null +++ b/ast_tester/polco2.box @@ -0,0 +1 @@ +-300 -300 500 500 diff --git a/ast_tester/polco2.head b/ast_tester/polco2.head new file mode 100644 index 0000000..d953089 --- /dev/null +++ b/ast_tester/polco2.head @@ -0,0 +1,86 @@ + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +CURRNT_A= 2 / Index of current Frame +NOD1_A = 2 / Frame 1 is associated with node 2 +NOD2_A = 1 / Frame 2 is associated with node 1 +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Data grid indices; first pixel at (1&'/ Title of coordinate system +CONTINUE '",1) "' +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Data grid index 1' / Axis Label +SYMBOL_A= 'g1 ' / Axis symbol +UNIT_A = 'pixel ' / Axis units +FORMAT_A= '%3.1f ' / Format specifier +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Data grid index 2' / Axis Label +SYMBOL_B= 'g2 ' / Axis symbol +UNIT_B = 'pixel ' / Axis units +FORMAT_B= '%3.1f ' / Format specifier +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'Frame ' / Coordinate system description +TITLE_B = 'Pixel coordinates; first pixel at (-&'/ Title of coordinate system +CONTINUE '100.5,-200.5)' +NAXES_B = 2 / Number of coordinate axes +DOMAIN_B= 'POLAR ' / Coordinate system domain +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'Axis ' / Coordinate axis +LABEL_C = 'Pixel coordinate 1' / Axis Label +SYMBOL_C= 'p1 ' / Axis symbol +UNIT_C = 'pixel ' / Axis units +FORMAT_C= '%3.1f ' / Format specifier +BOTTOM_A= 0.0 / Minimum legal axis value +ENDAST_D= 'Axis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'Axis ' / Coordinate axis +LABEL_D = 'Pixel coordinate 2' / Axis Label +SYMBOL_D= 'p2 ' / Axis symbol +UNIT_D = 'pixel ' / Axis units +FORMAT_D= '%3.1f ' / Format specifier +TOP_A = 3.141592 / Maximum legal axis value +BOTTOM_B= -3.141592 / Minimum legal axis value +ENDAST_E= 'Axis ' / End of object definition +ENDAST_F= 'Frame ' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISA_A = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +INVB_A = 1 / Second Mapping used in inverse direction +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'MathMap ' / Transformation using mathematical functions +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_B = 'Mapping ' / Mapping between coordinate systems +FWD1_A = 'r=sqrt(x*x+y*y)' / Forward function 1 +FWD2_A = 'theta=atan2(y,x)' / Forward function 2 +INV1_A = 'x=r*cos(theta)' / Inverse function 1 +INV2_A = 'y=r*sin(theta)' / Inverse function 2 +SIMPFI_A= 1 / Forward-inverse pairs may simplify +SIMPIF_A= 1 / Inverse-forward pairs may simplify +ENDAST_G= 'MathMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'WinMap ' / Map one window on to another +NIN_C = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_C = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -101.5 / Shift for axis 1 +SFT2_A = -201.5 / Shift for axis 2 +ENDAST_H= 'WinMap ' / End of object definition +ENDAST_I= 'CmpMap ' / End of object definition +ENDAST_J= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST diff --git a/ast_tester/polco2.ps b/ast_tester/polco2.ps new file mode 100644 index 0000000..1227d9d --- /dev/null +++ b/ast_tester/polco2.ps @@ -0,0 +1,664 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Feb 11 17:19:54 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: polco2.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 11/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 200.1 277.9 199.5 280.5 2 pls + 199.9 364.3 199.5 361.6 2 pls + 200.4 258.3 199.5 260.9 2 pls + 200.1 384.0 199.5 381.3 2 pls + 202.4 216.7 199.5 221.8 2 pls + 201.8 425.7 199.5 420.4 2 pls + 200.6 243.4 199.5 245.9 2 pls + 200.3 398.9 199.5 396.3 2 pls + 200.7 230.7 199.5 233.1 2 pls + 200.4 411.6 199.5 409.0 2 pls + 200.9 209.1 199.5 211.4 2 pls + 200.7 433.2 199.5 430.8 2 pls + 201.0 199.4 199.5 201.7 2 pls + 200.8 442.9 199.5 440.5 2 pls + 203.2 170.9 199.5 175.5 2 pls + 199.6 467.0 199.5 466.7 2 pls + 201.1 190.3 199.5 192.6 2 pls + 200.8 452.0 199.5 449.6 2 pls + 201.1 181.7 199.5 183.8 2 pls + 200.9 460.7 199.5 458.3 2 pls + 201.2 165.3 199.5 167.4 2 pls + 201.3 157.6 199.5 159.6 2 pls + 203.7 133.3 199.5 137.4 2 pls + 201.3 150.0 199.5 152.0 2 pls + 201.4 142.7 199.5 144.6 2 pls + 201.5 128.5 199.5 130.3 2 pls + 201.5 121.5 199.5 123.4 2 pls + 204.0 99.5 199.5 103.2 2 pls + 201.5 114.8 199.5 116.5 2 pls + 201.5 108.1 199.5 109.8 2 pls + 201.6 95.0 199.5 96.7 2 pls + 201.6 88.6 199.5 90.2 2 pls + 201.6 82.2 199.5 83.8 2 pls + 201.5 76.0 199.5 77.5 2 pls + 285.9 81.3 283.5 76.0 2 pls + 218.9 78.2 217.3 76.0 2 pls + 253.7 78.4 252.3 76.0 2 pls + 312.8 78.6 311.9 76.0 2 pls + 339.1 78.7 338.5 76.0 2 pls + 412.6 81.9 413.0 76.0 2 pls + 364.2 78.7 363.8 76.0 2 pls + 388.6 78.8 388.5 76.0 2 pls + 437.5 78.7 437.9 76.0 2 pls + 463.0 78.7 463.7 76.0 2 pls + 549.9 81.0 553.0 76.0 2 pls + 489.9 78.6 490.9 76.0 2 pls + 519.2 78.5 520.4 76.0 2 pls + 588.4 78.2 590.1 76.0 2 pls + 590.2 298.4 590.4 304.3 2 pls + 589.6 343.7 590.4 337.9 2 pls + 589.9 271.3 590.4 274.0 2 pls + 589.6 370.8 590.4 368.2 2 pls + 589.7 253.7 590.4 256.3 2 pls + 589.4 388.4 590.4 385.9 2 pls + 587.9 213.6 590.4 218.9 2 pls + 587.4 428.3 590.4 423.2 2 pls + 589.5 239.6 590.4 242.1 2 pls + 589.2 402.5 590.4 400.0 2 pls + 589.4 227.4 590.4 229.9 2 pls + 589.1 414.7 590.4 412.2 2 pls + 589.1 206.4 590.4 208.8 2 pls + 588.9 435.7 590.4 433.4 2 pls + 589.1 196.9 590.4 199.3 2 pls + 588.8 445.1 590.4 442.9 2 pls + 587.1 168.7 590.4 173.5 2 pls + 589.0 188.0 590.4 190.3 2 pls + 588.8 454.0 590.4 451.8 2 pls + 588.9 179.5 590.4 181.8 2 pls + 588.7 462.6 590.4 460.4 2 pls + 588.8 163.3 590.4 165.6 2 pls + 588.7 155.7 590.4 157.8 2 pls + 586.6 131.4 590.4 135.8 2 pls + 588.7 148.2 590.4 150.3 2 pls + 588.7 140.9 590.4 143.0 2 pls + 588.6 126.8 590.4 128.8 2 pls + 588.5 119.9 590.4 121.9 2 pls + 586.2 97.8 590.4 101.9 2 pls + 588.5 113.2 590.4 115.1 2 pls + 588.5 106.5 590.4 108.5 2 pls + 588.4 93.5 590.4 95.4 2 pls + 588.4 87.1 590.4 89.0 2 pls + 588.4 80.8 590.4 82.6 2 pls + 590.0 76.0 590.4 76.4 2 pls + 566.8 465.2 568.9 467.0 2 pls + 486.2 462.0 489.3 467.0 2 pls + 535.4 465.0 537.3 467.0 2 pls + 509.7 464.8 511.4 467.0 2 pls + 468.7 464.5 469.9 467.0 2 pls + 451.4 464.4 452.4 467.0 2 pls + 405.6 461.1 406.0 467.0 2 pls + 435.4 464.3 436.2 467.0 2 pls + 420.4 464.3 420.8 467.0 2 pls + 391.5 464.2 391.4 467.0 2 pls + 377.1 464.2 376.7 467.0 2 pls + 331.3 461.6 328.9 467.0 2 pls + 362.2 464.3 361.6 467.0 2 pls + 346.7 464.4 345.8 467.0 2 pls + 311.7 464.6 310.3 467.0 2 pls + 291.1 464.7 289.5 467.0 2 pls + 205.1 463.5 200.4 467.0 2 pls + 267.1 464.9 265.3 467.0 2 pls + 238.4 465.1 236.4 467.0 2 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 283.2 255.3 227.4 199.5 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 157.53 96.37 moveto[ 157.5 157.5 196.4 196.4 ][ 96.4 108.5 108.5 96.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 164.6 164.2 163.0 162.1 160.9 160.0 159.6 159.6 160.0 160.9 162.1 162.5 163.8 164.6 165.1 165.1 164.6 163.8 + 162.5 162.1 160.9 160.0 159.6 ][ 105.6 106.4 106.8 106.8 106.4 105.1 103.1 101.0 99.3 98.5 98.0 98.0 98.5 + 99.3 100.6 101.0 102.2 103.1 103.5 103.5 103.1 102.2 101.0 ]plong + s[ 170.1 168.8 168.0 167.6 167.6 168.0 168.8 170.1 170.9 172.2 173.0 173.4 173.4 173.0 172.2 170.9 170.1 ][ 106.8 + 106.4 105.1 103.1 101.8 99.7 98.5 98.0 98.0 98.5 99.7 101.8 103.1 105.1 106.4 106.8 106.8 ]plong + s[ 178.4 177.2 176.3 175.9 175.9 176.3 177.2 178.4 179.3 180.5 181.3 181.8 181.8 181.3 180.5 179.3 178.4 ][ 106.8 + 106.4 105.1 103.1 101.8 99.7 98.5 98.0 98.0 98.5 99.7 101.8 103.1 105.1 106.4 106.8 106.8 ]plong + 185.1 98.9 185.5 98.5 185.1 98.0 184.7 98.5 185.1 98.9 5 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 106.8 + 106.4 105.1 103.1 101.8 99.7 98.5 98.0 98.0 98.5 99.7 101.8 103.1 105.1 106.4 106.8 106.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 157.12 130.53 moveto[ 157.1 157.1 196.4 196.4 ][ 130.5 142.6 142.6 130.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 164.2 160.0 159.6 160.0 161.3 162.5 163.8 164.6 165.1 165.1 164.6 163.8 162.5 161.3 160.0 159.6 159.2 ][ 141.0 + 141.0 137.2 137.6 138.0 138.0 137.6 136.8 135.5 134.7 133.5 132.6 132.2 132.2 132.6 133.0 133.9 ]plong + s[ 172.6 168.4 168.0 168.4 169.6 170.9 172.2 173.0 173.4 173.4 173.0 172.2 170.9 169.6 168.4 168.0 167.6 ][ 141.0 + 141.0 137.2 137.6 138.0 138.0 137.6 136.8 135.5 134.7 133.5 132.6 132.2 132.2 132.6 133.0 133.9 ]plong + s[ 178.4 177.2 176.3 175.9 175.9 176.3 177.2 178.4 179.3 180.5 181.3 181.8 181.8 181.3 180.5 179.3 178.4 ][ 141.0 + 140.6 139.3 137.2 136.0 133.9 132.6 132.2 132.2 132.6 133.9 136.0 137.2 139.3 140.6 141.0 141.0 ]plong + 185.1 133.0 185.5 132.6 185.1 132.2 184.7 132.6 185.1 133.0 5 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 141.0 + 140.6 139.3 137.2 136.0 133.9 132.6 132.2 132.2 132.6 133.9 136.0 137.2 139.3 140.6 141.0 141.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 157.12 168.60 moveto[ 157.1 157.1 196.4 196.4 ][ 168.6 180.7 180.7 168.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 164.2 160.0 159.6 160.0 161.3 162.5 163.8 164.6 165.1 165.1 164.6 163.8 162.5 161.3 160.0 159.6 159.2 ][ 179.0 + 179.0 175.3 175.7 176.1 176.1 175.7 174.9 173.6 172.8 171.5 170.7 170.3 170.3 170.7 171.1 171.9 ]plong + s[ 170.1 168.8 168.0 167.6 167.6 168.0 168.8 170.1 170.9 172.2 173.0 173.4 173.4 173.0 172.2 170.9 170.1 ][ 179.0 + 178.6 177.4 175.3 174.0 171.9 170.7 170.3 170.3 170.7 171.9 174.0 175.3 177.4 178.6 179.0 179.0 ]plong + s[ 178.4 177.2 176.3 175.9 175.9 176.3 177.2 178.4 179.3 180.5 181.3 181.8 181.8 181.3 180.5 179.3 178.4 ][ 179.0 + 178.6 177.4 175.3 174.0 171.9 170.7 170.3 170.3 170.7 171.9 174.0 175.3 177.4 178.6 179.0 179.0 ]plong + 185.1 171.1 185.5 170.7 185.1 170.3 184.7 170.7 185.1 171.1 5 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 179.0 + 178.6 177.4 175.3 174.0 171.9 170.7 170.3 170.3 170.7 171.9 174.0 175.3 177.4 178.6 179.0 179.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 157.12 214.93 moveto[ 157.1 157.1 196.4 196.4 ][ 214.9 227.0 227.0 214.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 165.5 219.5 159.2 219.5 163.4 225.4 3 pls + 163.4 216.6 163.4 225.4 2 pls + s[ 172.6 168.4 168.0 168.4 169.6 170.9 172.2 173.0 173.4 173.4 173.0 172.2 170.9 169.6 168.4 168.0 167.6 ][ 225.4 + 225.4 221.6 222.0 222.4 222.4 222.0 221.2 219.9 219.1 217.9 217.0 216.6 216.6 217.0 217.4 218.3 ]plong + s[ 178.4 177.2 176.3 175.9 175.9 176.3 177.2 178.4 179.3 180.5 181.3 181.8 181.8 181.3 180.5 179.3 178.4 ][ 225.4 + 225.0 223.7 221.6 220.4 218.3 217.0 216.6 216.6 217.0 218.3 220.4 221.6 223.7 225.0 225.4 225.4 ]plong + 185.1 217.4 185.5 217.0 185.1 216.6 184.7 217.0 185.1 217.4 5 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 225.4 + 225.0 223.7 221.6 220.4 218.3 217.0 216.6 216.6 217.0 218.3 220.4 221.6 223.7 225.0 225.4 225.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 157.12 413.47 moveto[ 157.1 157.1 196.4 196.4 ][ 413.5 425.6 425.6 413.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 165.5 418.1 159.2 418.1 163.4 423.9 3 pls + 163.4 415.1 163.4 423.9 2 pls + s[ 172.6 168.4 168.0 168.4 169.6 170.9 172.2 173.0 173.4 173.4 173.0 172.2 170.9 169.6 168.4 168.0 167.6 ][ 423.9 + 423.9 420.1 420.6 421.0 421.0 420.6 419.7 418.5 417.6 416.4 415.6 415.1 415.1 415.6 416.0 416.8 ]plong + s[ 178.4 177.2 176.3 175.9 175.9 176.3 177.2 178.4 179.3 180.5 181.3 181.8 181.8 181.3 180.5 179.3 178.4 ][ 423.9 + 423.5 422.2 420.1 418.9 416.8 415.6 415.1 415.1 415.6 416.8 418.9 420.1 422.2 423.5 423.9 423.9 ]plong + 185.1 416.0 185.5 415.6 185.1 415.1 184.7 415.6 185.1 416.0 5 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 423.9 + 423.5 422.2 420.1 418.9 416.8 415.6 415.1 415.1 415.6 416.8 418.9 420.1 422.2 423.5 423.9 423.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 157.12 459.80 moveto[ 157.1 157.1 196.4 196.4 ][ 459.8 471.9 471.9 459.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 164.2 160.0 159.6 160.0 161.3 162.5 163.8 164.6 165.1 165.1 164.6 163.8 162.5 161.3 160.0 159.6 159.2 ][ 470.2 + 470.2 466.5 466.9 467.3 467.3 466.9 466.1 464.8 464.0 462.7 461.9 461.5 461.5 461.9 462.3 463.1 ]plong + s[ 170.1 168.8 168.0 167.6 167.6 168.0 168.8 170.1 170.9 172.2 173.0 173.4 173.4 173.0 172.2 170.9 170.1 ][ 470.2 + 469.8 468.6 466.5 465.2 463.1 461.9 461.5 461.5 461.9 463.1 465.2 466.5 468.6 469.8 470.2 470.2 ]plong + s[ 178.4 177.2 176.3 175.9 175.9 176.3 177.2 178.4 179.3 180.5 181.3 181.8 181.8 181.3 180.5 179.3 178.4 ][ 470.2 + 469.8 468.6 466.5 465.2 463.1 461.9 461.5 461.5 461.9 463.1 465.2 466.5 468.6 469.8 470.2 470.2 ]plong + 185.1 462.3 185.5 461.9 185.1 461.5 184.7 461.9 185.1 462.3 5 pls + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 470.2 + 469.8 468.6 466.5 465.2 463.1 461.9 461.5 461.5 461.9 463.1 465.2 466.5 468.6 469.8 470.2 470.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 267.21 59.99 moveto[ 267.2 267.2 300.2 300.2 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 276.8 65.4 269.3 65.4 2 pls + s[ 280.2 280.2 280.6 281.0 281.8 283.5 284.3 284.7 285.2 285.2 284.7 283.9 279.7 285.6 ][ 68.3 68.8 69.6 70.0 + 70.4 70.4 70.0 69.6 68.8 67.9 67.1 65.8 61.7 61.7 ]plong + 288.9 62.5 289.3 62.1 288.9 61.7 288.5 62.1 288.9 62.5 5 pls + s[ 294.8 293.5 292.7 292.3 292.3 292.7 293.5 294.8 295.6 296.9 297.7 298.1 298.1 297.7 296.9 295.6 294.8 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 396.75 59.99 moveto[ 396.7 396.7 429.7 429.7 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 406.4 65.4 398.8 65.4 2 pls + 412.6 61.7 412.6 70.4 411.4 69.2 410.5 68.8 4 pls + 418.5 62.5 418.9 62.1 418.5 61.7 418.0 62.1 418.5 62.5 5 pls + s[ 426.8 422.6 422.2 422.6 423.9 425.1 426.4 427.2 427.7 427.7 427.2 426.4 425.1 423.9 422.6 422.2 421.8 ][ 70.4 + 70.4 66.7 67.1 67.5 67.5 67.1 66.3 65.0 64.2 62.9 62.1 61.7 61.7 62.1 62.5 63.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 536.73 59.99 moveto[ 536.7 536.7 569.7 569.7 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 546.3 65.4 538.8 65.4 2 pls + 552.6 61.7 552.6 70.4 551.3 69.2 550.5 68.8 4 pls + 558.4 62.5 558.9 62.1 558.4 61.7 558.0 62.1 558.4 62.5 5 pls + s[ 564.3 563.0 562.2 561.8 561.8 562.2 563.0 564.3 565.1 566.4 567.2 567.6 567.6 567.2 566.4 565.1 564.3 ][ 70.4 + 70.0 68.8 66.7 65.4 63.3 62.1 61.7 61.7 62.1 63.3 65.4 66.7 68.8 70.0 70.4 70.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 151.95 187.09 moveto[ 152.0 135.2 135.2 152.0 ][ 187.1 187.1 355.8 355.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 147.4 189.2 138.6 189.2 2 pls + s[ 138.6 138.6 139.0 139.4 140.3 141.5 142.3 142.8 143.2 143.2 ][ 189.2 192.9 194.2 194.6 195.0 195.0 194.6 194.2 + 192.9 189.2 ]plong + 138.6 197.5 138.2 198.0 138.6 198.4 139.0 198.0 138.6 197.5 5 pls + 147.4 198.0 141.5 198.0 2 pls + 147.4 205.5 141.5 200.9 2 pls + 147.4 200.9 141.5 205.5 2 pls + s[ 144.0 144.0 143.2 142.3 141.9 141.5 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 ][ 208.0 + 213.0 213.0 212.6 212.2 211.3 210.1 209.2 208.4 208.0 208.0 208.4 209.2 210.1 211.3 212.2 213.0 ]plong + 147.4 215.9 138.6 215.9 2 pls + s[ 142.8 141.9 141.5 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 ][ 230.5 229.7 228.9 227.6 + 226.8 225.9 225.5 225.5 225.9 226.8 227.6 228.9 229.7 230.5 ]plong + s[ 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 144.9 144.0 142.8 141.9 141.5 141.5 ][ 235.1 + 234.3 233.5 233.0 233.0 233.5 234.3 235.1 236.4 237.2 238.0 238.5 238.5 238.0 237.2 236.4 235.1 ]plong + s[ 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 144.9 144.0 142.8 141.9 141.5 141.5 ][ 243.1 + 242.2 241.4 241.0 241.0 241.4 242.2 243.1 244.3 245.1 246.0 246.4 246.4 246.0 245.1 244.3 243.1 ]plong + 147.4 249.3 141.5 249.3 2 pls + 141.5 252.7 141.5 251.4 141.9 250.6 142.8 249.7 144.0 249.3 5 pls + 147.4 259.3 138.6 259.3 2 pls + s[ 142.8 141.9 141.5 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 ][ 259.3 258.5 257.7 256.4 + 255.6 254.8 254.3 254.3 254.8 255.6 256.4 257.7 258.5 259.3 ]plong + 138.6 262.3 138.2 262.7 138.6 263.1 139.0 262.7 138.6 262.3 5 pls + 147.4 262.7 141.5 262.7 2 pls + 147.4 266.0 141.5 266.0 2 pls + s[ 143.2 141.9 141.5 141.5 141.9 143.2 147.4 ][ 266.0 267.3 268.1 269.4 270.2 270.6 270.6 ]plong + 147.4 278.6 141.5 278.6 2 pls + s[ 142.8 141.9 141.5 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 ][ 278.6 277.7 276.9 275.6 + 274.8 274.0 273.6 273.6 274.0 274.8 275.6 276.9 277.7 278.6 ]plong + 147.4 284.4 147.4 283.6 146.9 282.7 145.7 282.3 138.6 282.3 5 pls + 141.5 284.0 141.5 281.1 2 pls + s[ 144.0 144.0 143.2 142.3 141.9 141.5 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 ][ 286.5 + 291.5 291.5 291.1 290.7 289.8 288.6 287.8 286.9 286.5 286.5 286.9 287.8 288.6 289.8 290.7 291.5 ]plong + 147.4 304.0 138.6 304.0 139.8 302.8 140.3 302.0 4 pls + s[ 136.9 137.8 139.0 140.7 142.8 144.4 146.5 148.2 149.4 150.3 ][ 319.1 318.2 317.4 316.6 316.2 316.2 316.6 317.4 + 318.2 319.1 ]plong + 150.3 322.0 141.5 322.0 2 pls + s[ 142.8 141.9 141.5 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 ][ 322.0 322.8 323.7 324.9 + 325.8 326.6 327.0 327.0 326.6 325.8 324.9 323.7 322.8 322.0 ]plong + 138.6 329.5 138.2 329.9 138.6 330.4 139.0 329.9 138.6 329.5 5 pls + 147.4 329.9 141.5 329.9 2 pls + 147.4 337.5 141.5 332.9 2 pls + 147.4 332.9 141.5 337.5 2 pls + s[ 144.0 144.0 143.2 142.3 141.9 141.5 141.5 141.9 142.8 144.0 144.9 146.1 146.9 147.4 147.4 146.9 146.1 ][ 340.0 + 345.0 345.0 344.6 344.1 343.3 342.0 341.2 340.4 340.0 340.0 340.4 341.2 342.0 343.3 344.1 345.0 ]plong + 147.4 347.9 138.6 347.9 2 pls + s[ 136.9 137.8 139.0 140.7 142.8 144.4 146.5 148.2 149.4 150.3 ][ 350.8 351.7 352.5 353.3 353.7 353.7 353.3 352.5 + 351.7 350.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 310.55 39.38 moveto[ 310.6 310.6 479.3 479.3 ][ 39.4 56.1 56.1 39.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 312.6 44.0 312.6 52.7 2 pls + s[ 312.6 316.4 317.7 318.1 318.5 318.5 318.1 317.7 316.4 312.6 ][ 52.7 52.7 52.3 51.9 51.1 49.8 49.0 48.6 + 48.1 48.1 ]plong + 321.0 52.7 321.4 53.2 321.8 52.7 321.4 52.3 321.0 52.7 5 pls + 321.4 44.0 321.4 49.8 2 pls + 328.9 44.0 324.3 49.8 2 pls + 324.3 44.0 328.9 49.8 2 pls + s[ 331.4 336.4 336.4 336.0 335.6 334.8 333.5 332.7 331.9 331.4 331.4 331.9 332.7 333.5 334.8 335.6 336.4 ][ 47.3 + 47.3 48.1 49.0 49.4 49.8 49.8 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 ]plong + 339.4 44.0 339.4 52.7 2 pls + s[ 354.0 353.2 352.3 351.1 350.2 349.4 349.0 349.0 349.4 350.2 351.1 352.3 353.2 354.0 ][ 48.6 49.4 49.8 49.8 + 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 ]plong + s[ 358.6 357.7 356.9 356.5 356.5 356.9 357.7 358.6 359.8 360.7 361.5 361.9 361.9 361.5 360.7 359.8 358.6 ][ 49.8 + 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 46.5 47.3 48.6 49.4 49.8 49.8 ]plong + s[ 366.5 365.7 364.9 364.4 364.4 364.9 365.7 366.5 367.8 368.6 369.4 369.9 369.9 369.4 368.6 367.8 366.5 ][ 49.8 + 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 46.5 47.3 48.6 49.4 49.8 49.8 ]plong + 372.8 44.0 372.8 49.8 2 pls + 376.1 49.8 374.9 49.8 374.0 49.4 373.2 48.6 372.8 47.3 5 pls + 382.8 44.0 382.8 52.7 2 pls + s[ 382.8 382.0 381.1 379.9 379.1 378.2 377.8 377.8 378.2 379.1 379.9 381.1 382.0 382.8 ][ 48.6 49.4 49.8 49.8 + 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 ]plong + 385.7 52.7 386.2 53.2 386.6 52.7 386.2 52.3 385.7 52.7 5 pls + 386.2 44.0 386.2 49.8 2 pls + 389.5 44.0 389.5 49.8 2 pls + s[ 389.5 390.7 391.6 392.8 393.7 394.1 394.1 ][ 48.1 49.4 49.8 49.8 49.4 48.1 44.0 ]plong + 402.0 44.0 402.0 49.8 2 pls + s[ 402.0 401.2 400.4 399.1 398.3 397.4 397.0 397.0 397.4 398.3 399.1 400.4 401.2 402.0 ][ 48.6 49.4 49.8 49.8 + 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 ]plong + 407.9 44.0 407.0 44.0 406.2 44.4 405.8 45.6 405.8 52.7 5 pls + 407.5 49.8 404.5 49.8 2 pls + s[ 410.0 415.0 415.0 414.6 414.1 413.3 412.0 411.2 410.4 410.0 410.0 410.4 411.2 412.0 413.3 414.1 415.0 ][ 47.3 + 47.3 48.1 49.0 49.4 49.8 49.8 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 ]plong + s[ 424.6 424.6 425.0 425.4 426.2 427.9 428.8 429.2 429.6 429.6 429.2 428.3 424.2 430.0 ][ 50.7 51.1 51.9 52.3 + 52.7 52.7 52.3 51.9 51.1 50.2 49.4 48.1 44.0 44.0 ]plong + s[ 442.5 441.7 440.9 440.0 439.6 439.6 440.0 440.9 441.7 442.5 ][ 54.4 53.6 52.3 50.7 48.6 46.9 44.8 43.1 + 41.9 41.0 ]plong + 445.5 41.0 445.5 49.8 2 pls + s[ 445.5 446.3 447.1 448.4 449.2 450.1 450.5 450.5 450.1 449.2 448.4 447.1 446.3 445.5 ][ 48.6 49.4 49.8 49.8 + 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 ]plong + 453.0 52.7 453.4 53.2 453.8 52.7 453.4 52.3 453.0 52.7 5 pls + 453.4 44.0 453.4 49.8 2 pls + 460.9 44.0 456.3 49.8 2 pls + 456.3 44.0 460.9 49.8 2 pls + s[ 463.4 468.4 468.4 468.0 467.6 466.8 465.5 464.7 463.8 463.4 463.4 463.8 464.7 465.5 466.8 467.6 468.4 ][ 47.3 + 47.3 48.1 49.0 49.4 49.8 49.8 49.4 48.6 47.3 46.5 45.2 44.4 44.0 44.0 44.4 45.2 ]plong + 471.4 44.0 471.4 52.7 2 pls + s[ 474.3 475.1 476.0 476.8 477.2 477.2 476.8 476.0 475.1 474.3 ][ 54.4 53.6 52.3 50.7 48.6 46.9 44.8 43.1 + 41.9 41.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 491.46 moveto[ 360.9 360.9 435.2 435.2 ][ 491.5 503.6 503.6 491.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 493.1 366.3 501.9 2 pls + 369.7 493.1 366.3 501.9 2 pls + 368.4 496.1 364.2 496.1 2 pls + 378.4 493.1 378.4 501.9 2 pls + 383.9 501.9 378.4 501.9 2 pls + 381.8 497.7 378.4 497.7 2 pls + 385.9 493.1 385.9 501.9 2 pls + 391.0 493.1 391.0 501.9 2 pls + 393.9 501.9 388.0 501.9 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 500.6 501.5 501.9 501.9 501.5 500.6 499.8 499.0 498.6 498.1 497.3 496.9 496.5 495.6 494.4 493.5 + 493.1 493.1 493.5 494.4 ]plong + 413.5 493.1 412.7 493.1 411.8 493.5 411.4 494.8 411.4 501.9 5 pls + 413.1 499.0 410.2 499.0 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 496.5 + 496.5 497.3 498.1 498.6 499.0 499.0 498.6 497.7 496.5 495.6 494.4 493.5 493.1 493.1 493.5 494.4 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 497.7 + 498.6 499.0 499.0 498.6 497.7 496.9 496.5 496.1 495.6 494.8 494.4 493.5 493.1 493.1 493.5 494.4 ]plong + 433.1 493.1 432.3 493.1 431.5 493.5 431.1 494.8 431.1 501.9 5 pls + 432.7 499.0 429.8 499.0 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/regression.current b/ast_tester/regression.current new file mode 100644 index 0000000..788c2bc --- /dev/null +++ b/ast_tester/regression.current @@ -0,0 +1,7583 @@ + Begin FitsChan # I/O channels to FITS files + Card = 1 # Index of current card +# Encod = "NATIVE" # Encoding system +# FitsDg = 15 # No. of digits for floating point values +# DfB1950 = 1 # Default to FK4 B1950 +# CdMat = 0 # Use PC matrix +# CarLin = 0 # Use full FITS-WCS CAR projections +# Iwc = 0 # Do not include an IWC Frame +# Warn = "Tnx Zpx BadCel BadMat BadCTYPE" # Warnings to be reported + Nm1 = "NAXIS" # FITS keyword name + Ty1 = "integer" # FITS keyword data type + Dt1 = 1 # FITS keyword value + Nm2 = "NAXIS1" # FITS keyword name + Ty2 = "integer" # FITS keyword data type + Dt2 = 100 # FITS keyword value + Nm3 = "CTYPE1" # FITS keyword name + Ty3 = "string" # FITS keyword data type + Dt3 = "fred" # FITS keyword value + Nm4 = "CDELT1" # FITS keyword name + Ty4 = "floating point" # FITS keyword data type + Dt4 = 0 # FITS keyword value + Nm5 = "CRPIX1" # FITS keyword name + Ty5 = "integer" # FITS keyword data type + Dt5 = 50 # FITS keyword value + Nm6 = "CUNIT1" # FITS keyword name + Ty6 = "string" # FITS keyword data type + Dt6 = "GHz" # FITS keyword value + Nm7 = " " # FITS keyword name + Ty7 = "comment" # FITS keyword data type + End FitsChan + Begin FrameSet # Set of inter-related coordinate systems +# Title = "IAU (1958) galactic coordinates; zenithal equal area projection" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Epoch = 1950 # Besselian epoch of observation +# Lbl1 = "Galactic longitude" # Label for axis 1 +# Lbl2 = "Galactic latitude" # Label for axis 2 +# System = "GALACTIC" # Coordinate system type +# Uni1 = "degrees" # Units for axis 1 +# Uni2 = "degrees" # Units for axis 2 +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + IsA Frame # Coordinate system description + Nframe = 2 # Number of Frames in FrameSet + Base = 1 # Index of base Frame + Currnt = 2 # Index of current Frame + Lnk2 = 1 # Node 2 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Pixel Coordinates" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Pixel axis 1" # Label for axis 1 +# Lbl2 = "Pixel axis 2" # Label for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel axis 1" # Axis Label + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel axis 2" # Axis Label + End Axis + End Frame + Frm2 = # Frame number 2 + Begin SkyFrame # Description of celestial coordinate system + Ident = " " # Permanent Object identification string + IsA Object # AST Object +# Title = "IAU (1958) galactic coordinates; zenithal equal area projection" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain + Epoch = 1950 # Besselian epoch of observation +# Lbl1 = "Galactic longitude" # Label for axis 1 +# Lbl2 = "Galactic latitude" # Label for axis 2 + System = "GALACTIC" # Coordinate system type +# Uni1 = "degrees" # Units for axis 1 +# Uni2 = "degrees" # Units for axis 2 +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + Proj = "zenithal equal area" # Description of sky projection + SRefIs = "Ignored" # Not rotated (ref. pos. is ignored) + SRef1 = -2.61046557479594 # Ref. pos. l -149.5687 + SRef2 = -0.344845661720836 # Ref. pos. b -19.7582 + End SkyFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -150.5 # Shift for axis 1 + Sft2 = -150.5 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -0.020943951023932 # Forward matrix value + M1 = 0.020943951023932 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "ZEA" # Zenithal equal area projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.291480364581799 # Forward matrix value + M1 = 0.506505471460186 # AST version 4.2- 1 +PutCards Ncards = 7 +PutCards Card = 1 +PutCards Card = 8 +PutCards Ncards = 7 +PutCards Card = 1 + + + + + FITS test number 1 + ==================== + + + +AST_SHOW: + +REG_SINK: +SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 +BITPIX = -32 / Bits per pixel. +NAXIS = 2 / Number of dimensions +NAXIS1 = 300 / Length of x axis. +NAXIS2 = 300 / Length of y axis. +COMMENT +COMMENT This file was produced by the SkyView survey analysis system from +COMMENT available astronomical surveys. The data are formatted +COMMENT as a simple two-dimensional FITS image with the same units as +COMMENT the orginal survey. A single ASCII table extension may be present +COMMENT which describes catalog objects found within the field of view. +COMMENT Copies of relevant copyright notices are included in this file. +COMMENT +COMMENT Questions should be directed to: +COMMENT +COMMENT scollick@skyview.gsfc.nasa.gov +COMMENT or +COMMENT mcglynn@grossc.gsfc.nasa.gov +COMMENT +COMMENT SkyView +COMMENT Code 668.1 +COMMENT Goddard Space Flight Center, Greenbelt, MD 20771 +COMMENT 301-286-7780 +COMMENT +COMMENT SkyView is supported by NASA ADP grant NAS 5-32068. +COMMENT +SURVEY = 'COBE DIRBE' +BUNITS = 'MJy/sr ' +ORIGIN = 'CDAC ' / Cosmology Data Analysis Center +TELESCOP= 'COBE ' / COsmic Background Explorer satellite +INSTRUME= 'DIRBE ' / COBE instrument [DIRBE, DMR, FIRAS] +PIXRESOL= 9 / Quad tree pixel resolution [6, 9] +DATE = '27/09/94' / FITS file creation date (dd/mm/yy) +DATE-MAP= '16/09/94' / Date of original file creation (dd/mm/yy) +COMMENT COBE specific keywords +DATE-BEG= '08/12/89' / date of initial data represented (dd/mm/yy) +DATE-END= '25/09/90' / date of final data represented (dd/mm/yy) +COMMENT +COMMENT THE COBE DIRBE map is a combination of the original ten +COMMENT band passes with the following wavelengths: +COMMENT Band 1 - 1.25 microns +COMMENT Band 2 - 2.2 microns +COMMENT Band 3 - 3.5 microns +COMMENT Band 4 - 4.9 microns +COMMENT Band 5 - 12 microns +COMMENT Band 6 - 25 microns +COMMENT Band 7 - 60 microns +COMMENT Band 8 - 100 microns +COMMENT Band 9 - 140 microns +COMMENT Band 10 - 240 microns +COMMENT + +Objects written: 1 + +Native Encoding: + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST WCS information in AST format AST +COMMENT AST See http://www.starlink.ac.uk/ast/ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +BASE_A = 1 / Index of base Frame +CURRNT_A= 2 / Index of current Frame +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Pixel Coordinates' / Title of coordinate system +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Pixel axis 1' / Axis Label +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Pixel axis 2' / Axis Label +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'SkyFrame' / Description of celestial coordinate system +IDENT_A = '" " ' / Permanent Object identification string +ISA_A = 'Object ' / AST Object +NAXES_B = 2 / Number of coordinate axes +EPOCH_A = 1950.0 / Besselian epoch of observation +SYSTEM_A= 'GALACTIC' / Coordinate system type +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'SkyAxis ' / Celestial coordinate axis +ENDAST_D= 'SkyAxis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'SkyAxis ' / Celestial coordinate axis +ENDAST_E= 'SkyAxis ' / End of object definition +ISA_B = 'Frame ' / Coordinate system description +PROJ_A = 'zenithal equal area'/ Description of sky projection +SREFIS_A= 'Ignored ' / Not rotated (ref. pos. is ignored) +SREF1_A = -2.61046557479594 / Ref. pos. l -149.5687 +SREF2_A = -0.344845661720836 / Ref. pos. b -19.7582 +ENDAST_F= 'SkyFrame' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISSIMP_A= 1 / Mapping has been simplified +ISA_C = 'Mapping ' / Mapping between coordinate systems +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'WinMap ' / Map one window on to another +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_D = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -150.5 / Shift for axis 1 +SFT2_A = -150.5 / Shift for axis 2 +ENDAST_G= 'WinMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'CmpMap ' / Compound Mapping +NIN_C = 2 / Number of input coordinates +ISA_E = 'Mapping ' / Mapping between coordinate systems +MAPA_B = ' ' / First component Mapping +BEGAST_K= 'MatrixMap' / Matrix transformation +NIN_D = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_F = 'Mapping ' / Mapping between coordinate systems +M0_A = -0.020943951023932 / Forward matrix value +M1_A = 0.020943951023932 / Forward matrix value +FORM_A = 'Diagonal' / Matrix storage form +ENDAST_H= 'MatrixMap' / End of object definition +MAPB_B = ' ' / Second component Mapping +BEGAST_L= 'CmpMap ' / Compound Mapping +NIN_E = 2 / Number of input coordinates +ISA_G = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +MAPA_C = ' ' / First component Mapping +BEGAST_M= 'WcsMap ' / FITS-WCS sky projection +NIN_F = 2 / Number of input coordinates +INVERT_C= 1 / Mapping inverted +ISA_H = 'Mapping ' / Mapping between coordinate systems +TYPE_A = 'ZEA ' / Zenithal equal area projection +ENDAST_I= 'WcsMap ' / End of object definition +MAPB_C = ' ' / Second component Mapping +BEGAST_N= 'CmpMap ' / Compound Mapping +NIN_G = 2 / Number of input coordinates +ISA_I = 'Mapping ' / Mapping between coordinate systems +INVA_B = 1 / First Mapping used in inverse direction +MAPA_D = ' ' / First component Mapping +BEGAST_O= 'SphMap ' / Cartesian to Spherical mapping +NIN_H = 3 / Number of input coordinates +NOUT_A = 2 / Number of output coordinates +INVERT_D= 1 / Mapping inverted +ISA_J = 'Mapping ' / Mapping between coordinate systems +UNTRD_A = 1 / All input vectors have unit length +PLRLG_A = 0.0 / Polar longitude (rad.s) +ENDAST_J= 'SphMap ' / End of object definition +MAPB_D = ' ' / Second component Mapping +BEGAST_P= 'CmpMap ' / Compound Mapping +NIN_I = 3 / Number of input coordinates +NOUT_B = 2 / Number of output coordinates +ISA_K = 'Mapping ' / Mapping between coordinate systems +MAPA_E = ' ' / First component Mapping +BEGAST_Q= 'MatrixMap' / Matrix transformation +NIN_J = 3 / Number of input coordinates +INVERT_E= 0 / Mapping not inverted +ISA_L = 'Mapping ' / Mapping between coordinate systems +M0_B = 0.291480364581799 / Forward matrix value +M1_B = 0.506505471460186 / Forward matrix value +M2_A = -0.811474832908671 / Forward matrix value +M3_A = 0.171224898552328 / Forward matrix value +M4_A = -0.862236746712233 / Forward matrix value +M5_A = -0.476686298035564 / Forward matrix value +M6_A = -0.941127638091139 / Forward matrix value +M7_A = 0.0 / Forward matrix value +M8_A = -0.338051429254475 / Forward matrix value +FORM_B = 'Full ' / Matrix storage form +ENDAST_K= 'MatrixMap' / End of object definition +MAPB_E = ' ' / Second component Mapping +BEGAST_R= 'SphMap ' / Cartesian to Spherical mapping +NIN_K = 3 / Number of input coordinates +NOUT_C = 2 / Number of output coordinates +INVERT_F= 0 / Mapping not inverted +ISA_M = 'Mapping ' / Mapping between coordinate systems +UNTRD_B = 1 / All input vectors have unit length +PLRLG_B = -2.61046557479594 / Polar longitude (rad.s) +ENDAST_L= 'SphMap ' / End of object definition +ENDAST_M= 'CmpMap ' / End of object definition +ENDAST_N= 'CmpMap ' / End of object definition +ENDAST_O= 'CmpMap ' / End of object definition +ENDAST_P= 'CmpMap ' / End of object definition +ENDAST_Q= 'CmpMap ' / End of object definition +ENDAST_R= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST + +ATTRIBUTES: + Colour(axis1) : 1 + Font(Stri) : 1 + Nout : 1 + Class : 4 + Tol : 0.100000E-01 + Gap(1) : 1.04720 + Border : 0 + Invert : 0 + TextLabGap : 0.100000E-01 + Nin : 2 + Current : 3 + Base : 1 + Nobject : 1 + RefCOUNT : 1 + +AST_GRID: +REG_LINE: 15 + 25.4 -29.7 + 20.1 -16.6 + 15.4 -2.03 + 11.5 13.7 + 8.35 30.3 + 5.92 47.5 + 4.25 65.0 + 3.39 82.6 + 3.35 100. + 4.19 117. + 5.99 133. + 8.83 149. + 12.9 163. + 18.3 175. + 25.4 185. +REG_LINE: 119 + 25.4 -29.7 + 24.6 -29.7 + 23.8 -29.7 + 22.9 -29.6 + 22.1 -29.5 + 21.3 -29.5 + 20.4 -29.3 + 19.6 -29.2 + 18.8 -29.0 + 17.9 -28.9 + 17.1 -28.7 + 16.3 -28.5 + 15.5 -28.2 + 14.6 -28.0 + 13.8 -27.7 + 13.0 -27.4 + 12.2 -27.1 + 11.4 -26.7 + 10.6 -26.4 + 9.74 -26.0 + 8.93 -25.6 + 8.12 -25.2 + 7.32 -24.8 + 6.52 -24.3 + 5.73 -23.9 + 4.93 -23.4 + 4.14 -22.9 + 3.35 -22.3 + 2.57 -21.8 + 1.79 -21.2 + 1.02 -20.6 + .247 -20.0 + -.520 -19.4 + -1.28 -18.8 + -2.04 -18.1 + -2.79 -17.4 + -3.54 -16.7 + -4.29 -16.0 + -5.02 -15.3 + -5.76 -14.5 + -6.49 -13.7 + -7.21 -12.9 + -7.92 -12.1 + -17.3 1.12 + -25.1 17.7 + -31.1 37.2 + -34.6 59.1 + -35.4 82.8 + -33.1 107. + -32.8 109. + -32.5 111. + -32.2 112. + -31.9 114. + -31.5 116. + -31.1 117. + -30.7 119. + -30.3 121. + -29.9 123. + -29.4 124. + -28.9 126. + -28.5 128. + -27.9 129. + -27.4 131. + -26.9 132. + -26.3 134. + -25.7 136. + -25.2 137. + -24.5 139. + -23.9 140. + -23.3 142. + -22.6 143. + -21.9 145. + -21.2 146. + -20.5 148. + -19.8 149. + -19.0 151. + -18.2 152. + -17.5 154. + -16.7 155. + -15.8 156. + -15.0 158. + -14.2 159. + -13.3 160. + -12.4 162. + -11.5 163. + -10.6 164. + -9.69 165. + -8.75 166. + -7.80 167. + -6.83 168. + -5.85 170. + -4.86 171. + -3.85 172. + -2.83 172. + -1.80 173. + -.761 174. + .293 175. + 1.36 176. + 2.43 177. + 3.52 178. + 4.61 178. + 5.72 179. + 6.83 180. + 7.95 180. + 9.08 181. + 10.2 181. + 11.4 182. + 12.5 182. + 13.7 183. + 14.8 183. + 16.0 184. + 17.2 184. + 18.3 184. + 19.5 184. + 20.7 185. + 21.9 185. + 23.1 185. + 24.3 185. + 25.4 185. +REG_LINE: 171 + 25.4 -29.7 + 19.1 -40.9 + 11.9 -49.9 + 11.3 -50.4 + 10.8 -50.9 + 10.2 -51.4 + 9.62 -51.9 + 9.04 -52.4 + 8.46 -52.8 + 7.87 -53.3 + 7.28 -53.7 + 6.68 -54.1 + 6.07 -54.5 + 5.46 -54.9 + 4.84 -55.2 + 4.21 -55.6 + 3.58 -55.9 + 2.95 -56.2 + 2.30 -56.5 + 1.65 -56.7 + .990 -56.9 + .325 -57.2 + -.348 -57.3 + -1.03 -57.5 + -1.71 -57.7 + -2.41 -57.8 + -3.11 -57.9 + -3.82 -57.9 + -4.54 -58.0 + -5.27 -58.0 + -6.00 -58.0 + -6.74 -58.0 + -7.50 -57.9 + -8.26 -57.8 + -9.03 -57.7 + -9.80 -57.5 + -10.6 -57.3 + -11.4 -57.1 + -12.2 -56.8 + -13.0 -56.5 + -13.8 -56.2 + -14.7 -55.8 + -15.5 -55.4 + -16.4 -55.0 + -17.2 -54.5 + -18.1 -53.9 + -19.0 -53.4 + -19.9 -52.7 + -20.8 -52.0 + -21.7 -51.3 + -22.6 -50.5 + -23.6 -49.7 + -24.5 -48.8 + -25.5 -47.8 + -26.4 -46.8 + -27.4 -45.7 + -28.4 -44.5 + -29.4 -43.3 + -30.4 -42.0 + -31.4 -40.6 + -32.4 -39.1 + -33.5 -37.6 + -34.5 -36.0 + -35.5 -34.3 + -36.6 -32.5 + -37.6 -30.5 + -38.7 -28.5 + -39.7 -26.4 + -40.8 -24.2 + -41.8 -21.9 + -42.8 -19.5 + -43.9 -16.9 + -44.9 -14.2 + -45.9 -11.4 + -46.9 -8.53 + -47.8 -5.48 + -48.8 -2.30 + -49.7 1.01 + -50.6 4.45 + -51.4 8.02 + -52.2 11.7 + -53.0 15.6 + -53.7 19.5 + -54.4 23.6 + -55.0 27.8 + -55.5 32.2 + -56.0 36.6 + -56.4 41.1 + -56.7 45.8 + -56.9 50.5 + -57.1 55.3 + -57.1 60.1 + -57.1 65.1 + -56.9 70.0 + -56.7 74.9 + -56.4 79.9 + -55.9 84.9 + -55.4 89.8 + -54.8 94.7 + -54.1 99.5 + -53.3 104. + -52.4 109. + -51.4 114. + -50.3 118. + -49.2 122. + -48.0 127. + -46.7 131. + -45.4 135. + -44.1 138. + -42.7 142. + -41.2 146. + -39.8 149. + -38.3 152. + -36.8 155. + -35.2 158. + -33.7 161. + -32.2 164. + -30.6 166. + -29.1 168. + -27.5 171. + -26.0 173. + -24.5 175. + -23.0 176. + -21.5 178. + -20.0 180. + -18.5 181. + -17.1 182. + -15.7 184. + -14.3 185. + -12.9 186. + -11.5 187. + -10.2 188. + -8.92 188. + -7.64 189. + -6.38 190. + -5.15 190. + -3.94 191. + -2.75 191. + -1.59 192. + -.448 192. + .669 193. + 1.76 193. + 2.83 193. + 3.88 193. + 4.91 193. + 5.92 193. + 6.91 193. + 7.88 193. + 8.82 193. + 9.75 193. + 10.7 193. + 11.6 193. + 12.4 193. + 13.3 192. + 14.1 192. + 14.9 192. + 15.7 192. + 16.5 191. + 17.3 191. + 18.1 190. + 18.8 190. + 19.5 190. + 20.2 189. + 20.9 189. + 21.6 188. + 22.3 188. + 22.9 187. + 23.6 187. + 24.2 186. + 24.8 185. + 25.4 185. +REG_LINE: 171 + 25.4 -29.7 + 31.6 -41.1 + 38.7 -50.2 + 39.2 -50.7 + 39.8 -51.2 + 40.3 -51.8 + 40.9 -52.3 + 41.5 -52.8 + 42.0 -53.2 + 42.6 -53.7 + 43.2 -54.1 + 43.8 -54.6 + 44.4 -55.0 + 45.0 -55.4 + 45.6 -55.7 + 46.2 -56.1 + 46.8 -56.4 + 47.4 -56.7 + 48.1 -57.0 + 48.7 -57.3 + 49.4 -57.6 + 50.0 -57.8 + 50.7 -58.0 + 51.3 -58.2 + 52.0 -58.4 + 52.7 -58.5 + 53.4 -58.6 + 54.1 -58.7 + 54.8 -58.8 + 55.5 -58.8 + 56.2 -58.9 + 57.0 -58.8 + 57.7 -58.8 + 58.5 -58.7 + 59.2 -58.6 + 60.0 -58.5 + 60.8 -58.3 + 61.6 -58.2 + 62.4 -57.9 + 63.2 -57.7 + 64.0 -57.4 + 64.8 -57.0 + 65.6 -56.6 + 66.5 -56.2 + 67.3 -55.8 + 68.2 -55.2 + 69.1 -54.7 + 70.0 -54.1 + 70.9 -53.4 + 71.8 -52.7 + 72.7 -52.0 + 73.7 -51.2 + 74.6 -50.3 + 75.6 -49.4 + 76.6 -48.4 + 77.5 -47.3 + 78.5 -46.2 + 79.5 -45.0 + 80.5 -43.7 + 81.6 -42.4 + 82.6 -41.0 + 83.6 -39.5 + 84.7 -37.9 + 85.7 -36.2 + 86.8 -34.4 + 87.8 -32.5 + 88.9 -30.5 + 90.0 -28.5 + 91.1 -26.3 + 92.1 -23.9 + 93.2 -21.5 + 94.2 -19.0 + 95.3 -16.3 + 96.3 -13.5 + 97.3 -10.6 + 98.4 -7.51 + 99.3 -4.31 + 100. -.976 + 101. 2.50 + 102. 6.12 + 103. 9.88 + 104. 13.8 + 105. 17.8 + 105. 22.0 + 106. 26.3 + 106. 30.7 + 107. 35.2 + 107. 39.9 + 108. 44.7 + 108. 49.5 + 108. 54.4 + 108. 59.4 + 108. 64.5 + 108. 69.5 + 108. 74.6 + 107. 79.7 + 107. 84.8 + 106. 89.9 + 106. 94.9 + 105. 99.9 + 104. 105. + 103. 110. + 102. 114. + 101. 119. + 100. 123. + 98.8 128. + 97.5 132. + 96.1 136. + 94.7 140. + 93.3 143. + 91.8 147. + 90.3 150. + 88.8 154. + 87.2 157. + 85.7 159. + 84.1 162. + 82.5 165. + 80.9 167. + 79.4 170. + 77.8 172. + 76.3 174. + 74.7 176. + 73.2 177. + 71.7 179. + 70.2 181. + 68.8 182. + 67.3 183. + 65.9 185. + 64.5 186. + 63.2 187. + 61.8 188. + 60.5 188. + 59.2 189. + 57.9 190. + 56.7 191. + 55.5 191. + 54.3 192. + 53.1 192. + 51.9 192. + 50.8 193. + 49.7 193. + 48.6 193. + 47.6 193. + 46.5 193. + 45.5 194. + 44.5 194. + 43.6 194. + 42.6 194. + 41.7 193. + 40.8 193. + 39.9 193. + 39.0 193. + 38.2 193. + 37.3 193. + 36.5 192. + 35.7 192. + 34.9 192. + 34.2 191. + 33.4 191. + 32.7 191. + 31.9 190. + 31.2 190. + 30.5 189. + 29.8 189. + 29.2 188. + 28.5 188. + 27.9 187. + 27.3 187. + 26.6 186. + 26.0 185. + 25.4 185. +REG_LINE: 119 + 25.4 -29.7 + 26.3 -29.7 + 27.1 -29.7 + 28.0 -29.7 + 28.8 -29.6 + 29.6 -29.5 + 30.5 -29.4 + 31.3 -29.3 + 32.1 -29.2 + 33.0 -29.0 + 33.8 -28.8 + 34.6 -28.6 + 35.4 -28.4 + 36.3 -28.2 + 37.1 -27.9 + 37.9 -27.6 + 38.7 -27.3 + 39.5 -27.0 + 40.4 -26.7 + 41.2 -26.3 + 42.0 -25.9 + 42.8 -25.5 + 43.6 -25.1 + 44.4 -24.7 + 45.2 -24.2 + 46.0 -23.7 + 46.8 -23.2 + 47.6 -22.7 + 48.4 -22.2 + 49.2 -21.6 + 49.9 -21.1 + 50.7 -20.5 + 51.5 -19.8 + 52.2 -19.2 + 53.0 -18.6 + 53.8 -17.9 + 54.5 -17.2 + 55.3 -16.5 + 56.0 -15.8 + 56.8 -15.0 + 57.5 -14.3 + 58.2 -13.5 + 58.9 -12.7 + 68.4 .462 + 76.3 17.0 + 82.3 36.5 + 85.9 58.6 + 86.8 82.4 + 84.5 107. + 84.2 109. + 83.9 110. + 83.5 112. + 83.2 114. + 82.8 116. + 82.4 117. + 82.0 119. + 81.6 121. + 81.2 122. + 80.7 124. + 80.2 126. + 79.7 128. + 79.2 129. + 78.7 131. + 78.1 132. + 77.6 134. + 77.0 136. + 76.4 137. + 75.8 139. + 75.1 140. + 74.5 142. + 73.8 144. + 73.1 145. + 72.4 147. + 71.7 148. + 70.9 150. + 70.2 151. + 69.4 152. + 68.6 154. + 67.8 155. + 67.0 157. + 66.1 158. + 65.3 159. + 64.4 160. + 63.5 162. + 62.6 163. + 61.7 164. + 60.8 165. + 59.8 166. + 58.8 168. + 57.9 169. + 56.9 170. + 55.9 171. + 54.9 172. + 53.8 173. + 52.8 174. + 51.8 175. + 50.7 175. + 49.6 176. + 48.5 177. + 47.4 178. + 46.3 178. + 45.2 179. + 44.1 180. + 43.0 180. + 41.8 181. + 40.7 182. + 39.6 182. + 38.4 182. + 37.2 183. + 36.1 183. + 34.9 184. + 33.7 184. + 32.6 184. + 31.4 184. + 30.2 185. + 29.0 185. + 27.8 185. + 26.6 185. + 25.4 185. +REG_LINE: 15 + 25.4 -29.7 + 31.0 -16.7 + 35.7 -2.18 + 39.8 13.5 + 43.0 30.1 + 45.5 47.3 + 47.2 64.8 + 48.1 82.5 + 48.2 99.9 + 47.3 117. + 45.5 133. + 42.5 149. + 38.4 163. + 32.8 175. + 25.4 185. +REG_LINE: 197 + 37.2 2.94 + 36.4 3.34 + 35.6 3.71 + 34.8 4.05 + 34.0 4.37 + 33.2 4.65 + 32.3 4.90 + 31.5 5.13 + 30.7 5.32 + 29.8 5.49 + 29.0 5.63 + 28.2 5.74 + 27.3 5.82 + 26.5 5.87 + 25.6 5.89 + 24.8 5.88 + 23.9 5.84 + 23.1 5.77 + 22.2 5.68 + 21.4 5.55 + 20.5 5.40 + 19.7 5.21 + 18.9 5.00 + 18.1 4.75 + 17.2 4.48 + 16.4 4.18 + 15.6 3.85 + 14.8 3.49 + 14.0 3.10 + 13.3 2.69 + 12.5 2.24 + 11.8 1.77 + 11.0 1.26 + 10.3 .730 + 9.57 .169 + 8.87 -.420 + 8.18 -1.04 + 7.51 -1.68 + 6.86 -2.36 + 6.23 -3.06 + 5.61 -3.79 + 5.02 -4.55 + 4.44 -5.33 + 3.89 -6.14 + 3.35 -6.98 + 2.84 -7.84 + 2.36 -8.73 + 1.89 -9.65 + 1.46 -10.6 + 1.04 -11.6 + .660 -12.5 + .304 -13.6 + -0.240E-01 -14.6 + -.322 -15.7 + -.589 -16.7 + -.825 -17.8 + -1.03 -19.0 + -1.20 -20.1 + -1.34 -21.3 + -1.44 -22.5 + -1.50 -23.6 + -1.53 -24.9 + -1.52 -26.1 + -1.48 -27.3 + -1.39 -28.6 + -1.26 -29.8 + -1.10 -31.1 + -.889 -32.3 + -.640 -33.6 + -.348 -34.9 + -0.127E-01 -36.1 + .365 -37.4 + .787 -38.7 + 1.25 -39.9 + 1.76 -41.2 + 2.32 -42.4 + 2.92 -43.6 + 3.56 -44.8 + 4.24 -45.9 + 4.97 -47.1 + 5.74 -48.2 + 6.56 -49.2 + 7.41 -50.3 + 8.30 -51.3 + 9.23 -52.2 + 10.2 -53.1 + 11.2 -54.0 + 12.2 -54.8 + 13.3 -55.5 + 14.4 -56.2 + 15.5 -56.8 + 16.7 -57.4 + 17.9 -57.9 + 19.1 -58.3 + 20.3 -58.7 + 21.5 -58.9 + 22.7 -59.1 + 24.0 -59.3 + 25.2 -59.3 + 26.4 -59.3 + 27.7 -59.2 + 28.9 -59.0 + 30.1 -58.8 + 31.4 -58.5 + 32.6 -58.1 + 33.7 -57.6 + 34.9 -57.1 + 36.0 -56.5 + 37.1 -55.8 + 38.2 -55.1 + 39.3 -54.3 + 40.3 -53.5 + 41.3 -52.6 + 42.2 -51.7 + 43.1 -50.7 + 44.0 -49.7 + 44.8 -48.6 + 45.6 -47.5 + 46.4 -46.4 + 47.1 -45.2 + 47.7 -44.1 + 48.3 -42.9 + 48.9 -41.7 + 49.4 -40.4 + 49.9 -39.2 + 50.4 -37.9 + 50.8 -36.7 + 51.1 -35.4 + 51.4 -34.1 + 51.7 -32.8 + 51.9 -31.6 + 52.1 -30.3 + 52.2 -29.1 + 52.3 -27.8 + 52.4 -26.6 + 52.4 -25.3 + 52.4 -24.1 + 52.4 -22.9 + 52.3 -21.7 + 52.2 -20.6 + 52.0 -19.4 + 51.8 -18.3 + 51.6 -17.2 + 51.3 -16.1 + 51.0 -15.0 + 50.7 -14.0 + 50.4 -13.0 + 50.0 -12.0 + 49.6 -11.0 + 49.2 -10.0 + 48.7 -9.10 + 48.2 -8.20 + 47.7 -7.32 + 47.2 -6.48 + 46.7 -5.65 + 46.1 -4.86 + 45.5 -4.09 + 44.9 -3.35 + 44.3 -2.64 + 43.6 -1.95 + 43.0 -1.29 + 42.3 -.665 + 41.6 -0.645E-01 + 40.9 .508 + 40.2 1.05 + 39.4 1.57 + 38.7 2.05 + 37.9 2.51 + 37.2 2.94 + 36.4 3.34 + 35.6 3.71 + 34.8 4.05 + 34.0 4.37 + 33.2 4.65 + 32.3 4.90 + 31.5 5.13 + 30.7 5.32 + 29.8 5.49 + 29.0 5.63 + 28.2 5.74 + 27.3 5.82 + 26.5 5.87 + 25.6 5.89 + 24.8 5.88 + 23.9 5.84 + 23.1 5.77 + 22.2 5.68 + 21.4 5.55 + 20.5 5.40 + 19.7 5.21 + 18.9 5.00 + 18.1 4.75 + 17.2 4.48 + 16.4 4.18 + 15.6 3.85 + 14.8 3.49 + 14.0 3.10 +REG_LINE: 197 + 44.8 41.5 + 43.5 42.0 + 42.1 42.5 + 40.8 42.9 + 39.5 43.3 + 38.1 43.6 + 36.7 43.9 + 35.4 44.2 + 34.0 44.4 + 32.6 44.6 + 31.3 44.8 + 29.9 44.9 + 28.5 45.0 + 27.1 45.1 + 25.7 45.1 + 24.3 45.1 + 23.0 45.1 + 21.6 45.0 + 20.2 44.9 + 18.8 44.7 + 17.4 44.5 + 16.1 44.3 + 14.7 44.0 + 13.3 43.7 + 12.0 43.4 + 10.6 43.0 + 9.29 42.6 + 7.96 42.2 + 6.65 41.7 + 5.34 41.2 + 4.04 40.6 + 2.75 40.0 + 1.48 39.4 + .220 38.8 + -1.02 38.0 + -2.25 37.3 + -3.47 36.5 + -4.66 35.7 + -5.84 34.8 + -7.00 33.9 + -8.14 33.0 + -9.25 32.0 + -10.3 31.0 + -11.4 29.9 + -12.5 28.8 + -13.5 27.6 + -14.5 26.4 + -15.5 25.2 + -16.4 23.9 + -17.3 22.5 + -18.2 21.2 + -19.0 19.7 + -19.8 18.2 + -20.6 16.7 + -21.4 15.1 + -22.1 13.5 + -22.7 11.8 + -23.3 10.1 + -23.9 8.29 + -24.4 6.45 + -24.9 4.55 + -25.3 2.59 + -25.7 .576 + -26.0 -1.49 + -26.3 -3.62 + -26.5 -5.81 + -26.6 -8.05 + -26.7 -10.4 + -26.7 -12.7 + -26.6 -15.1 + -26.4 -17.6 + -26.2 -20.2 + -25.8 -22.7 + -25.4 -25.4 + -24.9 -28.1 + -24.3 -30.8 + -23.5 -33.6 + -22.7 -36.4 + -21.7 -39.3 + -20.7 -42.1 + -19.5 -45.0 + -18.1 -47.9 + -16.6 -50.8 + -15.0 -53.6 + -13.3 -56.4 + -11.4 -59.2 + -9.34 -61.9 + -7.15 -64.5 + -4.82 -67.0 + -2.34 -69.3 + .268 -71.5 + 3.01 -73.5 + 5.87 -75.3 + 8.84 -76.9 + 11.9 -78.3 + 15.1 -79.3 + 18.3 -80.1 + 21.5 -80.6 + 24.8 -80.9 + 28.1 -80.8 + 31.3 -80.4 + 34.6 -79.7 + 37.7 -78.7 + 40.8 -77.5 + 43.8 -76.0 + 46.7 -74.3 + 49.5 -72.4 + 52.2 -70.2 + 54.7 -67.9 + 57.1 -65.5 + 59.4 -63.0 + 61.5 -60.3 + 63.4 -57.6 + 65.2 -54.8 + 66.9 -51.9 + 68.4 -49.1 + 69.8 -46.2 + 71.1 -43.3 + 72.2 -40.4 + 73.2 -37.6 + 74.1 -34.7 + 74.9 -31.9 + 75.5 -29.2 + 76.1 -26.5 + 76.6 -23.8 + 76.9 -21.2 + 77.2 -18.6 + 77.4 -16.1 + 77.5 -13.7 + 77.6 -11.3 + 77.5 -8.97 + 77.4 -6.71 + 77.3 -4.50 + 77.0 -2.34 + 76.7 -.250 + 76.4 1.79 + 76.0 3.76 + 75.5 5.69 + 75.0 7.55 + 74.5 9.37 + 73.9 11.1 + 73.2 12.8 + 72.5 14.5 + 71.8 16.1 + 71.1 17.6 + 70.3 19.1 + 69.4 20.6 + 68.6 22.0 + 67.7 23.3 + 66.7 24.7 + 65.8 25.9 + 64.8 27.1 + 63.8 28.3 + 62.7 29.5 + 61.7 30.5 + 60.6 31.6 + 59.5 32.6 + 58.4 33.5 + 57.2 34.5 + 56.0 35.3 + 54.8 36.2 + 53.6 37.0 + 52.4 37.7 + 51.2 38.5 + 49.9 39.2 + 48.7 39.8 + 47.4 40.4 + 46.1 41.0 + 44.8 41.5 + 43.5 42.0 + 42.1 42.5 + 40.8 42.9 + 39.5 43.3 + 38.1 43.6 + 36.7 43.9 + 35.4 44.2 + 34.0 44.4 + 32.6 44.6 + 31.3 44.8 + 29.9 44.9 + 28.5 45.0 + 27.1 45.1 + 25.7 45.1 + 24.3 45.1 + 23.0 45.1 + 21.6 45.0 + 20.2 44.9 + 18.8 44.7 + 17.4 44.5 + 16.1 44.3 + 14.7 44.0 + 13.3 43.7 + 12.0 43.4 + 10.6 43.0 + 9.29 42.6 + 7.96 42.2 + 6.65 41.7 +REG_LINE: 197 + 48.1 82.5 + 46.6 82.8 + 45.0 83.2 + 43.4 83.5 + 41.8 83.8 + 40.3 84.1 + 38.7 84.4 + 37.1 84.6 + 35.5 84.8 + 33.8 84.9 + 32.2 85.0 + 30.6 85.2 + 29.0 85.2 + 27.4 85.3 + 25.8 85.3 + 24.2 85.3 + 22.5 85.3 + 20.9 85.2 + 19.3 85.1 + 17.7 85.0 + 16.1 84.8 + 14.5 84.6 + 12.9 84.4 + 11.3 84.2 + 9.69 83.9 + 8.10 83.7 + 6.52 83.3 + 4.95 83.0 + 3.39 82.6 + 1.83 82.2 + .284 81.8 + -1.25 81.3 + -2.78 80.8 + -4.29 80.3 + -5.80 79.7 + -7.29 79.2 + -8.77 78.6 + -10.2 77.9 + -11.7 77.2 + -13.1 76.5 + -14.5 75.8 + -15.9 75.0 + -17.3 74.2 + -18.7 73.4 + -20.1 72.5 + -21.4 71.6 + -22.7 70.7 + -24.0 69.7 + -25.3 68.7 + -26.5 67.6 + -27.8 66.5 + -29.0 65.4 + -30.1 64.2 + -31.3 63.0 + -32.4 61.8 + -33.5 60.5 + -34.6 59.1 + -35.7 57.8 + -36.7 56.3 + -37.7 54.8 + -38.7 53.3 + -39.6 51.7 + -40.5 50.0 + -41.3 48.3 + -42.2 46.5 + -42.9 44.6 + -43.7 42.7 + -44.4 40.6 + -45.0 38.5 + -45.7 36.3 + -46.2 34.0 + -46.7 31.6 + -47.1 29.1 + -47.5 26.5 + -47.8 23.7 + -48.1 20.8 + -48.2 17.8 + -48.3 14.5 + -48.2 11.1 + -48.1 7.50 + -47.8 3.67 + -47.3 -.401 + -46.7 -4.73 + -45.9 -9.33 + -44.9 -14.2 + -43.6 -19.5 + -42.0 -25.0 + -40.0 -30.9 + -37.7 -37.2 + -34.8 -43.8 + -31.3 -50.6 + -27.2 -57.6 + -22.3 -64.7 + -16.7 -71.6 + -10.1 -78.1 + -2.67 -83.8 + 5.54 -88.4 + 14.4 -91.5 + 23.6 -92.8 + 32.8 -92.3 + 41.9 -89.9 + 50.3 -85.8 + 58.1 -80.5 + 65.0 -74.3 + 71.1 -67.5 + 76.2 -60.5 + 80.7 -53.4 + 84.4 -46.5 + 87.5 -39.8 + 90.0 -33.4 + 92.1 -27.4 + 93.9 -21.7 + 95.3 -16.3 + 96.4 -11.3 + 97.3 -6.55 + 98.0 -2.11 + 98.5 2.06 + 98.8 5.99 + 99.1 9.69 + 99.1 13.2 + 99.1 16.5 + 99.0 19.6 + 98.8 22.6 + 98.5 25.4 + 98.2 28.1 + 97.8 30.6 + 97.3 33.1 + 96.8 35.4 + 96.2 37.7 + 95.6 39.8 + 94.9 41.9 + 94.1 43.8 + 93.4 45.7 + 92.6 47.6 + 91.7 49.3 + 90.8 51.0 + 89.9 52.6 + 89.0 54.2 + 88.0 55.7 + 87.0 57.2 + 85.9 58.6 + 84.9 60.0 + 83.8 61.3 + 82.7 62.5 + 81.5 63.8 + 80.3 65.0 + 79.1 66.1 + 77.9 67.2 + 76.7 68.3 + 75.4 69.3 + 74.1 70.3 + 72.8 71.2 + 71.5 72.2 + 70.1 73.0 + 68.8 73.9 + 67.4 74.7 + 66.0 75.5 + 64.6 76.2 + 63.2 77.0 + 61.7 77.6 + 60.3 78.3 + 58.8 78.9 + 57.3 79.5 + 55.8 80.1 + 54.3 80.6 + 52.8 81.1 + 51.2 81.6 + 49.7 82.0 + 48.1 82.5 + 46.6 82.8 + 45.0 83.2 + 43.4 83.5 + 41.8 83.8 + 40.3 84.1 + 38.7 84.4 + 37.1 84.6 + 35.5 84.8 + 33.8 84.9 + 32.2 85.0 + 30.6 85.2 + 29.0 85.2 + 27.4 85.3 + 25.8 85.3 + 24.2 85.3 + 22.5 85.3 + 20.9 85.2 + 19.3 85.1 + 17.7 85.0 + 16.1 84.8 + 14.5 84.6 + 12.9 84.4 + 11.3 84.2 + 9.69 83.9 + 8.10 83.7 + 6.52 83.3 + 4.95 83.0 + 3.39 82.6 +REG_LINE: 158 + 46.8 122. + 25.8 124. + 4.68 123. + -14.9 119. + -31.6 115. + -32.7 115. + -33.7 115. + -34.7 114. + -35.7 114. + -36.6 114. + -37.5 114. + -38.4 114. + -39.3 113. + -40.1 113. + -41.0 113. + -41.7 113. + -42.5 113. + -43.2 113. + -43.9 113. + -44.6 113. + -45.2 113. + -45.8 114. + -46.3 114. + -46.8 114. + -47.2 115. + -47.6 116. + -48.0 116. + -48.3 117. + -48.5 118. + -48.6 120. + -48.7 121. + -48.6 123. + -48.4 125. + -48.1 128. + -47.6 131. + -46.8 134. + -45.8 138. + -44.3 143. + -42.4 149. + -39.8 155. + -36.3 163. + -31.6 172. + -25.2 181. + -16.8 192. + -5.96 201. + -5.08 202. + -4.20 203. + -3.30 203. + -2.39 204. + -1.47 204. + -.536 205. + .410 205. + 1.37 206. + 2.33 206. + 3.31 207. + 4.30 207. + 5.30 208. + 6.31 208. + 7.33 209. + 8.36 209. + 9.40 209. + 10.4 210. + 11.5 210. + 12.6 210. + 13.6 211. + 14.7 211. + 15.8 211. + 16.9 211. + 17.9 212. + 19.0 212. + 20.1 212. + 21.2 212. + 22.3 212. + 23.4 212. + 24.5 212. + 25.6 212. + 26.8 212. + 27.9 212. + 29.0 212. + 30.1 212. + 31.2 212. + 32.3 212. + 33.3 212. + 34.4 211. + 35.5 211. + 36.6 211. + 37.7 211. + 38.7 210. + 39.8 210. + 40.8 210. + 41.9 209. + 42.9 209. + 43.9 209. + 44.9 208. + 46.0 208. + 47.0 207. + 47.9 207. + 48.9 206. + 49.9 206. + 50.8 205. + 51.8 205. + 52.7 204. + 53.6 204. + 54.5 203. + 55.4 202. + 56.3 202. + 57.2 201. + 58.0 200. + 58.9 200. + 59.7 199. + 60.5 198. + 61.3 198. + 62.1 197. + 62.9 196. + 63.6 196. + 73.0 186. + 80.2 176. + 85.5 166. + 89.4 158. + 92.4 151. + 94.5 145. + 96.1 140. + 97.3 136. + 98.2 132. + 98.8 129. + 99.2 126. + 99.4 124. + 99.5 122. + 99.5 120. + 99.4 119. + 99.3 118. + 99.0 117. + 98.7 116. + 98.3 115. + 97.9 115. + 97.4 114. + 96.9 114. + 96.3 114. + 95.7 113. + 95.1 113. + 94.4 113. + 93.7 113. + 92.9 113. + 92.2 113. + 91.4 113. + 90.5 113. + 89.7 113. + 88.8 114. + 87.9 114. + 86.9 114. + 86.0 114. + 85.0 115. + 84.0 115. + 82.9 115. + 66.3 119. + 46.8 122. + 25.8 124. + 4.68 123. +REG_LINE: 132 + 39.9 158. + 25.7 158. + 11.4 158. + -1.28 160. + -2.08 160. + -2.85 160. + -3.60 160. + -4.34 161. + -5.05 161. + -5.74 161. + -6.40 162. + -7.04 162. + -7.66 162. + -8.25 163. + -8.81 163. + -9.34 163. + -9.84 164. + -10.3 164. + -10.7 165. + -11.1 165. + -11.5 166. + -11.8 167. + -12.1 167. + -12.4 168. + -12.6 169. + -12.7 169. + -12.8 170. + -12.9 171. + -12.9 172. + -12.9 173. + -12.8 174. + -12.6 174. + -12.4 175. + -12.1 177. + -11.7 178. + -11.3 179. + -10.8 180. + -10.2 181. + -9.53 182. + -8.78 184. + -7.94 185. + -7.02 186. + -6.00 188. + -4.88 189. + -3.67 190. + -2.36 192. + -.951 193. + .557 194. + 2.16 195. + 3.87 197. + 5.66 198. + 7.55 199. + 9.52 200. + 11.6 201. + 13.7 202. + 15.9 202. + 18.1 203. + 20.4 203. + 22.7 203. + 25.0 203. + 27.3 203. + 29.6 203. + 31.9 203. + 34.1 202. + 36.3 202. + 38.5 201. + 40.6 200. + 42.6 199. + 44.5 198. + 46.3 197. + 48.1 196. + 49.7 195. + 51.2 193. + 52.7 192. + 54.0 191. + 55.3 189. + 56.5 188. + 57.5 187. + 58.5 185. + 59.3 184. + 60.1 183. + 60.8 182. + 61.4 180. + 62.0 179. + 62.4 178. + 62.8 177. + 63.2 176. + 63.4 175. + 63.6 174. + 63.7 173. + 63.8 172. + 63.8 171. + 63.8 170. + 63.7 170. + 63.5 169. + 63.3 168. + 63.1 167. + 62.9 167. + 62.5 166. + 62.2 166. + 61.8 165. + 61.4 165. + 60.9 164. + 60.4 164. + 59.9 163. + 59.4 163. + 58.8 162. + 58.2 162. + 57.6 162. + 56.9 161. + 56.2 161. + 55.5 161. + 54.8 161. + 54.0 160. + 53.3 160. + 52.5 160. + 51.7 160. + 50.9 159. + 50.0 159. + 49.2 159. + 48.3 159. + 47.4 159. + 46.5 159. + 45.6 159. + 44.7 158. + 43.8 158. + 42.8 158. + 41.9 158. + 40.9 158. + 39.9 158. + 25.7 158. + 11.4 158. +REG_LINE: 2 + 40.8 84.0 + 40.8 85.8 +REG_LINE: 2 + 33.3 85.0 + 33.3 86.7 +REG_LINE: 2 + 25.8 85.3 + 25.8 87.0 +REG_LINE: 2 + 18.2 85.0 + 18.2 86.8 +REG_LINE: 2 + 10.7 84.1 + 10.7 85.9 +REG_LINE: 2 + -3.79 80.5 + -3.83 82.2 +REG_LINE: 2 + -10.7 77.7 + -10.8 79.4 +REG_LINE: 2 + -17.3 74.2 + -17.4 76.0 +REG_LINE: 2 + -23.6 70.0 + -23.6 71.8 +REG_LINE: 2 + -29.4 65.0 + -29.5 66.8 +REG_LINE: 2 + -39.3 52.2 + -39.4 54.0 +REG_LINE: 2 + -43.2 44.0 + -43.4 45.7 +REG_LINE: 2 + -46.2 34.0 + -46.4 35.8 +REG_LINE: 2 + -48.0 21.8 + -48.3 23.5 +REG_LINE: 2 + -48.0 6.25 + -48.3 7.96 +REG_LINE: 2 + -35.8 -41.5 + -36.5 -39.9 +REG_LINE: 2 + -14.6 -73.9 + -15.8 -72.6 +REG_LINE: 2 + 23.6 -92.8 + 22.6 -94.3 +REG_LINE: 2 + 62.8 -76.5 + 64.1 -75.2 +REG_LINE: 2 + 85.5 -44.2 + 86.2 -42.6 +REG_LINE: 2 + 98.7 4.70 + 99.1 6.41 +REG_LINE: 2 + 99.0 20.6 + 99.3 22.3 +REG_LINE: 2 + 97.3 33.1 + 97.5 34.8 +REG_LINE: 2 + 94.4 43.2 + 94.6 44.9 +REG_LINE: 2 + 90.5 51.6 + 90.7 53.3 +REG_LINE: 2 + 80.7 64.6 + 80.8 66.3 +REG_LINE: 2 + 75.0 69.6 + 75.1 71.4 +REG_LINE: 2 + 68.8 73.9 + 68.8 75.6 +REG_LINE: 2 + 62.2 77.4 + 62.2 79.2 +REG_LINE: 2 + 55.3 80.3 + 55.3 82.0 +REG_LINE: 2 + 40.8 84.0 + 40.8 85.8 +REG_LINE: 2 + 33.3 85.0 + 33.3 86.7 +REG_LINE: 2 + 25.8 85.3 + 25.8 87.0 +REG_LINE: 2 + 18.2 85.0 + 18.2 86.8 +REG_LINE: 2 + 10.7 84.1 + 10.7 85.9 +REG_LINE: 2 + 23.1 -34.3 + 21.6 -33.5 +REG_LINE: 2 + 20.6 -38.6 + 19.1 -37.7 +REG_LINE: 2 + 18.0 -42.6 + 16.5 -41.7 +REG_LINE: 2 + 15.2 -46.2 + 13.8 -45.2 +REG_LINE: 2 + 12.3 -49.4 + 10.9 -48.4 +REG_LINE: 2 + 6.00 -54.5 + 4.63 -53.5 +REG_LINE: 2 + 2.59 -56.3 + 1.25 -55.2 +REG_LINE: 2 + -1.03 -57.5 + -2.33 -56.3 +REG_LINE: 2 + -4.86 -58.0 + -6.12 -56.8 +REG_LINE: 2 + -8.94 -57.7 + -10.1 -56.4 +REG_LINE: 2 + -17.9 -54.1 + -19.0 -52.7 +REG_LINE: 2 + -22.8 -50.3 + -23.9 -48.9 +REG_LINE: 2 + -28.1 -44.9 + -29.0 -43.4 +REG_LINE: 2 + -33.6 -37.4 + -34.4 -35.9 +REG_LINE: 2 + -39.3 -27.4 + -40.0 -25.8 +REG_LINE: 2 + -50.1 2.52 + -50.5 4.22 +REG_LINE: 2 + -54.3 23.2 + -54.6 24.9 +REG_LINE: 2 + -56.8 47.3 + -56.8 49.1 +REG_LINE: 2 + -56.8 73.8 + -56.1 75.5 +REG_LINE: 2 + -53.9 101. + -54.0 98.8 +REG_LINE: 2 + -41.1 146. + -41.7 144. +REG_LINE: 2 + -32.8 162. + -33.7 161. +REG_LINE: 2 + -24.5 175. + -25.5 173. +REG_LINE: 2 + -16.5 183. + -17.6 182. +REG_LINE: 2 + -9.06 188. + -10.4 187. +REG_LINE: 2 + 3.65 193. + 2.17 192. +REG_LINE: 2 + 9.03 193. + 7.49 192. +REG_LINE: 2 + 13.8 192. + 12.2 192. +REG_LINE: 2 + 18.1 190. + 16.5 190. +REG_LINE: 2 + 22.0 188. + 20.3 187. +REG_LINE: 15 + 3.39 82.6 + 6.52 83.3 + 9.69 83.9 + 12.9 84.4 + 16.1 84.8 + 19.3 85.1 + 22.5 85.3 + 25.8 85.3 + 29.0 85.2 + 32.2 85.0 + 35.5 84.8 + 38.7 84.4 + 41.8 83.8 + 45.0 83.2 + 48.1 82.5 +REG_LINE: 15 + 3.39 82.6 + .284 81.8 + -2.78 80.8 + -5.80 79.7 + -8.77 78.6 + -11.7 77.2 + -14.5 75.8 + -17.3 74.2 + -20.1 72.5 + -22.7 70.7 + -25.3 68.7 + -27.8 66.5 + -30.1 64.2 + -32.4 61.8 + -34.6 59.1 +REG_LINE: 15 + -34.6 59.1 + -36.7 56.3 + -38.7 53.3 + -40.5 50.0 + -42.2 46.5 + -43.7 42.7 + -45.0 38.5 + -46.2 34.0 + -47.1 29.1 + -47.8 23.7 + -48.2 17.8 + -48.2 11.1 + -47.8 3.67 + -46.7 -4.73 + -44.9 -14.2 +REG_LINE: 93 + -44.9 -14.2 + -42.0 -25.0 + -37.7 -37.2 + -31.3 -50.6 + -22.3 -64.7 + -21.6 -65.7 + -20.8 -66.7 + -20.0 -67.7 + -19.2 -68.7 + -18.4 -69.7 + -17.5 -70.7 + -16.7 -71.6 + -15.8 -72.6 + -14.9 -73.5 + -14.0 -74.5 + -13.0 -75.4 + -12.1 -76.3 + -11.1 -77.2 + -10.1 -78.1 + -9.08 -79.0 + -8.06 -79.8 + -7.01 -80.7 + -5.95 -81.5 + -4.87 -82.3 + -3.78 -83.1 + -2.67 -83.8 + -1.54 -84.6 + -.395 -85.3 + .764 -86.0 + 1.94 -86.6 + 3.13 -87.2 + 4.33 -87.8 + 5.54 -88.4 + 6.77 -89.0 + 8.01 -89.5 + 9.27 -89.9 + 10.5 -90.4 + 11.8 -90.8 + 13.1 -91.2 + 14.4 -91.5 + 15.7 -91.8 + 17.0 -92.1 + 18.3 -92.3 + 19.6 -92.5 + 20.9 -92.6 + 22.3 -92.8 + 23.6 -92.8 + 24.9 -92.9 + 26.2 -92.9 + 27.6 -92.8 + 28.9 -92.7 + 30.2 -92.6 + 31.5 -92.5 + 32.8 -92.3 + 34.2 -92.0 + 35.5 -91.8 + 36.8 -91.4 + 38.0 -91.1 + 39.3 -90.7 + 40.6 -90.3 + 41.9 -89.9 + 43.1 -89.4 + 44.3 -88.9 + 45.6 -88.3 + 46.8 -87.7 + 48.0 -87.1 + 49.2 -86.5 + 50.3 -85.8 + 51.5 -85.2 + 52.6 -84.4 + 53.8 -83.7 + 54.9 -82.9 + 56.0 -82.2 + 57.0 -81.4 + 58.1 -80.5 + 59.1 -79.7 + 60.2 -78.8 + 61.2 -78.0 + 62.2 -77.1 + 63.1 -76.2 + 64.1 -75.2 + 65.0 -74.3 + 65.9 -73.4 + 66.8 -72.4 + 67.7 -71.4 + 68.6 -70.5 + 69.4 -69.5 + 70.2 -68.5 + 71.1 -67.5 + 80.7 -53.4 + 87.5 -39.8 + 92.1 -27.4 + 95.3 -16.3 +REG_LINE: 15 + 95.3 -16.3 + 97.3 -6.55 + 98.5 2.06 + 99.1 9.69 + 99.1 16.5 + 98.8 22.6 + 98.2 28.1 + 97.3 33.1 + 96.2 37.7 + 94.9 41.9 + 93.4 45.7 + 91.7 49.3 + 89.9 52.6 + 88.0 55.7 + 85.9 58.6 +REG_LINE: 15 + 85.9 58.6 + 83.8 61.3 + 81.5 63.8 + 79.1 66.1 + 76.7 68.3 + 74.1 70.3 + 71.5 72.2 + 68.8 73.9 + 66.0 75.5 + 63.2 77.0 + 60.3 78.3 + 57.3 79.5 + 54.3 80.6 + 51.2 81.6 + 48.1 82.5 +REG_LINE: 15 + 48.1 82.5 + 45.0 83.2 + 41.8 83.8 + 38.7 84.4 + 35.5 84.8 + 32.2 85.0 + 29.0 85.2 + 25.8 85.3 + 22.5 85.3 + 19.3 85.1 + 16.1 84.8 + 12.9 84.4 + 9.69 83.9 + 6.52 83.3 + 3.39 82.6 +REG_LINE: 15 + 48.1 82.5 + 45.0 83.2 + 41.8 83.8 + 38.7 84.4 + 35.5 84.8 + 32.2 85.0 + 29.0 85.2 + 25.8 85.3 + 22.5 85.3 + 19.3 85.1 + 16.1 84.8 + 12.9 84.4 + 9.69 83.9 + 6.52 83.3 + 3.39 82.6 +REG_LINE: 15 + 25.4 -29.7 + 24.4 -31.7 + 23.4 -33.7 + 22.4 -35.6 + 21.3 -37.4 + 20.2 -39.2 + 19.1 -40.9 + 18.0 -42.6 + 16.8 -44.2 + 15.6 -45.7 + 14.4 -47.2 + 13.1 -48.6 + 11.9 -49.9 + 10.6 -51.1 + 9.23 -52.2 +REG_LINE: 15 + 9.23 -52.2 + 7.87 -53.3 + 6.48 -54.2 + 5.05 -55.1 + 3.58 -55.9 + 2.08 -56.5 + .548 -57.1 + -1.03 -57.5 + -2.64 -57.8 + -4.30 -58.0 + -6.00 -58.0 + -7.75 -57.9 + -9.54 -57.6 + -11.4 -57.1 + -13.3 -56.4 +REG_LINE: 15 + -13.3 -56.4 + -15.2 -55.6 + -17.2 -54.5 + -19.3 -53.2 + -21.4 -51.6 + -23.6 -49.7 + -25.8 -47.5 + -28.1 -44.9 + -30.4 -42.0 + -32.8 -38.6 + -35.2 -34.8 + -37.6 -30.5 + -40.1 -25.7 + -42.5 -20.3 + -44.9 -14.2 +REG_LINE: 15 + -44.9 -14.2 + -47.2 -7.52 + -49.4 -.109 + -51.4 8.02 + -53.3 16.9 + -54.8 26.4 + -56.0 36.6 + -56.8 47.3 + -57.1 58.5 + -56.9 70.0 + -56.2 81.6 + -55.0 93.1 + -53.3 104. + -51.0 115. + -48.4 125. +REG_LINE: 15 + -48.4 125. + -45.4 135. + -42.2 143. + -38.8 151. + -35.2 158. + -31.6 164. + -28.0 170. + -24.5 175. + -21.0 179. + -17.6 182. + -14.3 185. + -11.1 187. + -8.06 189. + -5.15 190. + -2.36 192. +REG_LINE: 15 + -2.36 192. + .299 192. + 2.83 193. + 5.25 193. + 7.56 193. + 9.75 193. + 11.8 193. + 13.8 192. + 15.7 192. + 17.6 191. + 19.3 190. + 20.9 189. + 22.5 188. + 24.0 186. + 25.4 185. +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +Forward matrix value + M2 = -0.811474832908671 # Forward matrix value + M3 = 0.171224898552328 # Forward matrix value + M4 = -0.862236746712233 # Forward matrix value + M5 = -0.476686298035564 # Forward matrix value + M6 = -0.941127638091139 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = -0.338051429254475 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = -2.61046557479594 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End FrameSet + Begin FrameSet # Set of inter-related coordinate systems +# Title = "Pixel coordinates; first pixel at (-100.5,-200.5)" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "POLAR" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + IsA Frame # Coordinate system description + Nframe = 2 # Number of Frames in FrameSet +# Base = 1 # Index of base Frame + Currnt = 2 # Index of current Frame + Nod1 = 2 # Frame 1 is associated with node 2 + Nod2 = 1 # Frame 2 is associated with node 1 + Lnk2 = 1 # Node 2 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Data grid indices; first pixel at (1,1) " # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Data grid index 1" # Label for axis 1 +# Lbl2 = "Data grid index 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Data grid index 1" # Axis Label + Symbol = "g1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Data grid index 2" # Axis Label + Symbol = "g2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm2 = # Frame number 2 + Begin Frame # Coordinate system description + Title = "Pixel coordinates; first pixel at (-100.5,-200.5)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "POLAR" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 1" # Axis Label + REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '240' + 4.00 80.2 TC -.245 .969 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '300' + -32.7 57.6 TC -.791 .612 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0' + -42.4 -13.7 BC .974 .227 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '60' + 92.9 -15.7 BC -.973 .231 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '120' + 84.0 57.1 TC .798 .602 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '180' + 47.5 80.0 TC .253 .968 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-90' + 27.7 -30.8 TC -.899 .438 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-60' + 10.8 -54.2 TC -.629 .777 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-30' + -14.2 -58.8 TC .374 .927 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0' + -47.2 -15.1 TC .937 .350 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '30' + -50.8 126. BC -.960 .278 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '60' + -3.21 194. BC -.341 .940 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '90' + 27.2 187. BC .711 .704 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 1 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: ' A FITS test' + 31.5 225. BC .000 1.00 + + + + + FITS test number 2 + ==================== + + + +AST_SHOW: + +REG_SINK: +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +AST +COMMENT AST Beginning of AST data for FrameSet object +AST +COMMENT AST ................................................................ +AST +COMMENT AST ................................................................ +AST +COMMENT AST End of AST data for FrameSet object +AST +COMMENT AST ---------------------------------------------------------------- + +Objects written: 1 + +Native Encoding: + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST WCS information in AST format AST +COMMENT AST See http://www.starlink.ac.uk/ast/ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +CURRNT_A= 2 / Index of current Frame +NOD1_A = 2 / Frame 1 is associated with node 2 +NOD2_A = 1 / Frame 2 is associated with node 1 +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Data grid indices; first pixel at (1&'/ Title of coordinate system +CONTINUE '",1) "' +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Data grid index 1' / Axis Label +SYMBOL_A= 'g1 ' / Axis symbol +UNIT_A = 'pixel ' / Axis units +FORMAT_A= '%3.1f ' / Format specifier +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Data grid index 2' / Axis Label +SYMBOL_B= 'g2 ' / Axis symbol +UNIT_B = 'pixel ' / Axis units +FORMAT_B= '%3.1f ' / Format specifier +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'Frame ' / Coordinate system description +TITLE_B = 'Pixel coordinates; first pixel at (-&'/ Title of coordinate system +CONTINUE '100.5,-200.5)' +NAXES_B = 2 / Number of coordinate axes +DOMAIN_B= 'POLAR ' / Coordinate system domain +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'Axis ' / Coordinate axis +LABEL_C = 'Pixel coordinate 1' / Axis Label +SYMBOL_C= 'p1 ' / Axis symbol +UNIT_C = 'pixel ' / Axis units +FORMAT_C= '%3.1f ' / Format specifier +ENDAST_D= 'Axis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'Axis ' / Coordinate axis +LABEL_D = 'Pixel coordinate 2' / Axis Label +SYMBOL_D= 'p2 ' / Axis symbol +UNIT_D = 'pixel ' / Axis units +FORMAT_D= '%3.1f ' / Format specifier +ENDAST_E= 'Axis ' / End of object definition +ENDAST_F= 'Frame ' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISA_A = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +INVB_A = 1 / Second Mapping used in inverse direction +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'MathMap ' / Transformation using mathematical functions +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_B = 'Mapping ' / Mapping between coordinate systems +FWD1_A = 'r=sqrt(x*x+y*y)' / Forward function 1 +FWD2_A = 'theta=atan2(y,x)' / Forward function 2 +INV1_A = 'x=r*cos(theta)' / Inverse function 1 +INV2_A = 'y=r*sin(theta)' / Inverse function 2 +SIMPFI_A= 1 / Forward-inverse pairs may simplify +SIMPIF_A= 1 / Inverse-forward pairs may simplify +ENDAST_G= 'MathMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'WinMap ' / Map one window on to another +NIN_C = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_C = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -101.5 / Shift for axis 1 +SFT2_A = -201.5 / Shift for axis 2 +ENDAST_H= 'WinMap ' / End of object definition +ENDAST_I= 'CmpMap ' / End of object definition +ENDAST_J= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST + +ATTRIBUTES: + Colour(axis1) : 1 + Font(Stri) : 1 + Nout : 1 + Class : 4 + Tol : 0.100000E-01 + Gap(1) : 50.0000 + Border : 0 + Invert : 0 + TextLabGap : 0.100000E-01 + Nin : 2 + Current : 3 + Base : 1 + Nobject : 1 + RefCOUNT : 1 + +AST_GRID: +REG_LINE: 197 + 10.8 124. + 10.7 123. + 10.5 122. + 10.3 121. + 10.2 120. + 10.1 119. + 10.0 118. + 9.94 117. + 9.89 116. + 9.85 115. + 9.84 113. + 9.85 112. + 9.88 111. + 9.93 110. + 10.0 109. + 10.1 108. + 10.2 107. + 10.3 106. + 10.5 105. + 10.6 104. + 10.8 103. + 11.0 102. + 11.2 100. + 11.5 99.5 + 11.7 98.5 + 12.0 97.5 + 12.3 96.6 + 12.6 95.6 + 13.0 94.7 + 13.3 93.9 + 13.7 93.0 + 14.0 92.2 + 14.4 91.4 + 14.8 90.6 + 15.2 89.8 + 15.7 89.1 + 16.1 88.4 + 16.5 87.8 + 17.0 87.2 + 17.5 86.6 + 18.0 86.0 + 18.5 85.5 + 19.0 85.0 + 19.5 84.6 + 20.0 84.2 + 20.5 83.8 + 21.1 83.5 + 21.6 83.2 + 22.1 82.9 + 22.7 82.7 + 23.2 82.5 + 23.8 82.4 + 24.3 82.3 + 24.9 82.2 + 25.5 82.2 + 26.0 82.2 + 26.6 82.3 + 27.1 82.4 + 27.7 82.5 + 28.2 82.7 + 28.8 82.9 + 29.3 83.2 + 29.9 83.4 + 30.4 83.8 + 30.9 84.2 + 31.4 84.6 + 32.0 85.0 + 32.5 85.5 + 33.0 86.0 + 33.4 86.6 + 33.9 87.1 + 34.4 87.8 + 34.8 88.4 + 35.3 89.1 + 35.7 89.8 + 36.1 90.6 + 36.5 91.3 + 36.9 92.1 + 37.3 93.0 + 37.6 93.8 + 38.0 94.7 + 38.3 95.6 + 38.6 96.5 + 38.9 97.5 + 39.2 98.5 + 39.4 99.4 + 39.7 100. + 39.9 101. + 40.1 103. + 40.3 104. + 40.5 105. + 40.6 106. + 40.7 107. + 40.8 108. + 40.9 109. + 41.0 110. + 41.1 111. + 41.1 112. + 41.1 113. + 41.1 115. + 41.1 116. + 41.0 117. + 40.9 118. + 40.8 119. + 40.7 120. + 40.6 121. + 40.5 122. + 40.3 123. + 40.1 124. + 39.9 125. + 39.7 126. + 39.4 127. + 39.2 128. + 38.9 129. + 38.6 130. + 38.3 131. + 38.0 132. + 37.6 133. + 37.3 134. + 36.9 135. + 36.5 136. + 36.1 136. + 35.7 137. + 35.3 138. + 34.8 138. + 34.4 139. + 33.9 140. + 33.4 140. + 33.0 141. + 32.5 141. + 32.0 142. + 31.4 142. + 30.9 143. + 30.4 143. + 29.9 143. + 29.3 144. + 28.8 144. + 28.2 144. + 27.7 144. + 27.1 145. + 26.6 145. + 26.0 145. + 25.5 145. + 24.9 145. + 24.3 145. + 23.8 145. + 23.2 144. + 22.7 144. + 22.1 144. + 21.6 144. + 21.1 143. + 20.5 143. + 20.0 143. + 19.5 142. + 19.0 142. + 18.5 141. + 18.0 141. + 17.5 140. + 17.0 140. + 16.5 139. + 16.1 138. + 15.7 138. + 15.2 137. + 14.8 136. + 14.4 136. + 14.0 135. + 13.7 134. + 13.3 133. + 13.0 132. + 12.6 131. + 12.3 130. + 12.0 129. + 11.7 128. + 11.5 127. + 11.2 126. + 11.0 125. + 10.8 124. + 10.6 123. + 10.5 122. + 10.3 121. + 10.2 120. + 10.1 119. + 10.0 118. + 9.93 117. + 9.88 116. + 9.85 115. + 9.84 113. + 9.85 112. + 9.89 111. + 9.94 110. + 10.0 109. + 10.1 108. + 10.2 107. + 10.3 106. + 10.5 105. + 10.7 104. + 10.8 102. +REG_LINE: 197 + -3.80 135. + -4.17 133. + -4.50 131. + -4.80 129. + -5.06 127. + -5.28 125. + -5.46 122. + -5.60 120. + -5.70 118. + -5.76 116. + -5.78 114. + -5.76 111. + -5.70 109. + -5.61 107. + -5.47 105. + -5.29 102. + -5.07 100. + -4.82 98.1 + -4.53 95.9 + -4.19 93.8 + -3.82 91.7 + -3.42 89.6 + -2.97 87.5 + -2.49 85.5 + -1.97 83.5 + -1.42 81.6 + -.838 79.7 + -.219 77.8 + .433 76.0 + 1.12 74.3 + 1.83 72.6 + 2.58 70.9 + 3.35 69.3 + 4.15 67.7 + 4.98 66.2 + 5.84 64.8 + 6.72 63.4 + 7.62 62.1 + 8.55 60.9 + 9.50 59.7 + 10.5 58.6 + 11.5 57.6 + 12.5 56.6 + 13.5 55.7 + 14.5 54.9 + 15.6 54.2 + 16.6 53.5 + 17.7 52.9 + 18.8 52.4 + 19.9 51.9 + 21.0 51.6 + 22.1 51.3 + 23.2 51.1 + 24.3 51.0 + 25.4 50.9 + 26.6 51.0 + 27.7 51.1 + 28.8 51.3 + 29.9 51.6 + 31.0 51.9 + 32.1 52.4 + 33.2 52.9 + 34.3 53.5 + 35.3 54.1 + 36.4 54.9 + 37.4 55.7 + 38.4 56.6 + 39.4 57.5 + 40.4 58.6 + 41.4 59.7 + 42.4 60.8 + 43.3 62.1 + 44.2 63.4 + 45.1 64.8 + 45.9 66.2 + 46.8 67.7 + 47.6 69.2 + 48.3 70.8 + 49.1 72.5 + 49.8 74.2 + 50.5 76.0 + 51.1 77.8 + 51.8 79.6 + 52.3 81.5 + 52.9 83.5 + 53.4 85.5 + 53.9 87.5 + 54.3 89.5 + 54.7 91.6 + 55.1 93.7 + 55.5 95.8 + 55.7 98.0 + 56.0 100. + 56.2 102. + 56.4 105. + 56.5 107. + 56.6 109. + 56.7 111. + 56.7 113. + 56.7 116. + 56.6 118. + 56.5 120. + 56.4 122. + 56.2 125. + 56.0 127. + 55.7 129. + 55.5 131. + 55.1 133. + 54.7 135. + 54.3 137. + 53.9 139. + 53.4 141. + 52.9 143. + 52.3 145. + 51.8 147. + 51.1 149. + 50.5 151. + 49.8 153. + 49.1 154. + 48.3 156. + 47.6 158. + 46.8 159. + 45.9 161. + 45.1 162. + 44.2 163. + 43.3 165. + 42.4 166. + 41.4 167. + 40.4 168. + 39.4 169. + 38.4 170. + 37.4 171. + 36.4 172. + 35.3 173. + 34.3 173. + 33.2 174. + 32.1 175. + 31.0 175. + 29.9 175. + 28.8 176. + 27.7 176. + 26.6 176. + 25.4 176. + 24.3 176. + 23.2 176. + 22.1 176. + 21.0 175. + 19.9 175. + 18.8 174. + 17.7 174. + 16.6 173. + 15.6 173. + 14.5 172. + 13.5 171. + 12.5 170. + 11.5 169. + 10.5 168. + 9.50 167. + 8.55 166. + 7.62 165. + 6.72 163. + 5.84 162. + 4.98 161. + 4.15 159. + 3.35 158. + 2.58 156. + 1.83 154. + 1.12 153. + .433 151. + -.219 149. + -.838 147. + -1.42 145. + -1.97 143. + -2.49 141. + -2.97 139. + -3.42 137. + -3.82 135. + -4.19 133. + -4.53 131. + -4.82 129. + -5.07 127. + -5.29 124. + -5.47 122. + -5.61 120. + -5.70 118. + -5.76 116. + -5.78 113. + -5.76 111. + -5.70 109. + -5.60 107. + -5.46 104. + -5.28 102. + -5.06 100. + -4.80 97.9 + -4.50 95.7 + -4.17 93.6 + -3.80 91.5 +REG_LINE: 197 + -18.4 146. + -19.0 143. + -19.5 140. + -19.9 137. + -20.3 133. + -20.7 130. + -20.9 127. + -21.1 124. + -21.3 120. + -21.4 117. + -21.4 114. + -21.4 110. + -21.3 107. + -21.1 104. + -20.9 100. + -20.7 96.9 + -20.3 93.6 + -20.0 90.4 + -19.5 87.1 + -19.0 83.9 + -18.5 80.8 + -17.9 77.7 + -17.2 74.6 + -16.5 71.6 + -15.7 68.6 + -14.9 65.7 + -14.0 62.8 + -13.1 60.0 + -12.1 57.3 + -11.1 54.7 + -9.99 52.1 + -8.87 49.6 + -7.71 47.2 + -6.50 44.9 + -5.26 42.6 + -3.98 40.5 + -2.66 38.4 + -1.30 36.5 + 0.922E-01 34.6 + 1.52 32.9 + 2.97 31.2 + 4.45 29.6 + 5.96 28.2 + 7.50 26.9 + 9.05 25.6 + 10.6 24.5 + 12.2 23.5 + 13.8 22.6 + 15.5 21.8 + 17.1 21.2 + 18.8 20.7 + 20.4 20.2 + 22.1 19.9 + 23.8 19.7 + 25.4 19.7 + 27.1 19.7 + 28.8 19.9 + 30.5 20.2 + 32.1 20.6 + 33.8 21.2 + 35.4 21.8 + 37.0 22.6 + 38.7 23.5 + 40.2 24.5 + 41.8 25.6 + 43.4 26.8 + 44.9 28.1 + 46.4 29.6 + 47.9 31.1 + 49.4 32.8 + 50.8 34.5 + 52.2 36.4 + 53.5 38.4 + 54.9 40.4 + 56.2 42.6 + 57.4 44.8 + 58.6 47.1 + 59.8 49.5 + 60.9 52.0 + 62.0 54.6 + 63.0 57.2 + 64.0 60.0 + 64.9 62.7 + 65.8 65.6 + 66.6 68.5 + 67.4 71.5 + 68.1 74.5 + 68.8 77.5 + 69.4 80.7 + 69.9 83.8 + 70.4 87.0 + 70.9 90.2 + 71.3 93.5 + 71.6 96.8 + 71.9 100. + 72.1 103. + 72.2 107. + 72.3 110. + 72.3 113. + 72.3 117. + 72.2 120. + 72.1 123. + 71.9 127. + 71.6 130. + 71.3 133. + 70.9 137. + 70.4 140. + 69.9 143. + 69.4 146. + 68.8 149. + 68.1 152. + 67.4 155. + 66.6 158. + 65.8 161. + 64.9 164. + 64.0 167. + 63.0 170. + 62.0 172. + 60.9 175. + 59.8 177. + 58.6 180. + 57.4 182. + 56.2 184. + 54.9 186. + 53.5 189. + 52.2 190. + 50.8 192. + 49.4 194. + 47.9 196. + 46.4 197. + 44.9 199. + 43.4 200. + 41.8 201. + 40.2 202. + 38.7 203. + 37.0 204. + 35.4 205. + 33.8 206. + 32.1 206. + 30.5 207. + 28.8 207. + 27.1 207. + 25.4 207. + 23.8 207. + 22.1 207. + 20.4 207. + 18.8 206. + 17.1 206. + 15.5 205. + 13.8 204. + 12.2 203. + 10.6 202. + 9.05 201. + 7.50 200. + 5.96 199. + 4.45 197. + 2.97 196. + 1.52 194. + 0.922E-01 192. + -1.30 190. + -2.66 188. + -3.98 186. + -5.26 184. + -6.50 182. + -7.71 180. + -8.87 177. + -9.99 175. + -11.1 172. + -12.1 170. + -13.1 167. + -14.0 164. + -14.9 161. + -15.7 158. + -16.5 155. + -17.2 152. + -17.9 149. + -18.5 146. + -19.0 143. + -19.5 140. + -20.0 137. + -20.3 133. + -20.7 130. + -20.9 127. + -21.1 123. + -21.3 120. + -21.4 117. + -21.4 113. + -21.4 110. + -21.3 107. + -21.1 103. + -20.9 100. + -20.7 96.7 + -20.3 93.4 + -19.9 90.1 + -19.5 86.9 + -19.0 83.7 + -18.4 80.6 +REG_LINE: 197 + -33.1 157. + -33.8 153. + -34.5 149. + -35.1 145. + -35.6 140. + -36.0 136. + -36.4 131. + -36.7 127. + -36.9 123. + -37.0 118. + -37.0 114. + -37.0 109. + -36.9 105. + -36.7 100. + -36.4 95.8 + -36.1 91.4 + -35.6 87.0 + -35.1 82.7 + -34.5 78.4 + -33.9 74.1 + -33.1 69.9 + -32.3 65.7 + -31.4 61.6 + -30.5 57.6 + -29.4 53.6 + -28.3 49.8 + -27.1 46.0 + -25.9 42.3 + -24.6 38.6 + -23.2 35.1 + -21.8 31.7 + -20.3 28.3 + -18.8 25.1 + -17.2 22.0 + -15.5 19.0 + -13.8 16.2 + -12.0 13.4 + -10.2 10.8 + -8.37 8.34 + -6.47 5.99 + -4.53 3.78 + -2.55 1.71 + -.540 -.225 + 1.51 -2.01 + 3.58 -3.65 + 5.69 -5.14 + 7.82 -6.47 + 9.97 -7.66 + 12.1 -8.69 + 14.3 -9.56 + 16.5 -10.3 + 18.7 -10.8 + 21.0 -11.2 + 23.2 -11.5 + 25.4 -11.6 + 27.7 -11.5 + 29.9 -11.2 + 32.1 -10.9 + 34.3 -10.3 + 36.5 -9.59 + 38.7 -8.72 + 40.9 -7.70 + 43.0 -6.52 + 45.2 -5.19 + 47.3 -3.70 + 49.4 -2.07 + 51.4 -.290 + 53.4 1.63 + 55.4 3.70 + 57.3 5.91 + 59.2 8.25 + 61.1 10.7 + 62.9 13.3 + 64.7 16.1 + 66.4 18.9 + 68.0 21.9 + 69.6 25.0 + 71.2 28.2 + 72.7 31.6 + 74.1 35.0 + 75.5 38.5 + 76.8 42.1 + 78.0 45.8 + 79.2 49.6 + 80.3 53.5 + 81.4 57.5 + 82.3 61.5 + 83.2 65.6 + 84.0 69.7 + 84.8 73.9 + 85.4 78.2 + 86.0 82.5 + 86.5 86.9 + 87.0 91.2 + 87.3 95.6 + 87.6 100. + 87.8 105. + 87.9 109. + 88.0 113. + 87.9 118. + 87.8 122. + 87.6 127. + 87.3 131. + 87.0 136. + 86.5 140. + 86.0 144. + 85.4 149. + 84.8 153. + 84.0 157. + 83.2 161. + 82.3 165. + 81.4 169. + 80.3 173. + 79.2 177. + 78.0 181. + 76.8 185. + 75.5 188. + 74.1 192. + 72.7 195. + 71.2 199. + 69.6 202. + 68.0 205. + 66.4 208. + 64.7 211. + 62.9 214. + 61.1 216. + 59.2 219. + 57.3 221. + 55.4 223. + 53.4 225. + 51.4 227. + 49.4 229. + 47.3 231. + 45.2 232. + 43.0 233. + 40.9 235. + 38.7 236. + 36.5 236. + 34.3 237. + 32.1 238. + 29.9 238. + 27.7 238. + 25.4 238. + 23.2 238. + 21.0 238. + 18.7 238. + 16.5 237. + 14.3 236. + 12.1 236. + 9.97 235. + 7.82 233. + 5.69 232. + 3.58 231. + 1.51 229. + -.540 227. + -2.55 225. + -4.53 223. + -6.47 221. + -8.37 219. + -10.2 216. + -12.0 213. + -13.8 211. + -15.5 208. + -17.2 205. + -18.8 202. + -20.3 199. + -21.8 195. + -23.2 192. + -24.6 188. + -25.9 185. + -27.1 181. + -28.3 177. + -29.4 173. + -30.5 169. + -31.4 165. + -32.3 161. + -33.1 157. + -33.9 153. + -34.5 149. + -35.1 144. + -35.6 140. + -36.1 135. + -36.4 131. + -36.7 127. + -36.9 122. + -37.0 118. + -37.0 113. + -37.0 109. + -36.9 104. + -36.7 99.9 + -36.4 95.5 + -36.0 91.1 + -35.6 86.7 + -35.1 82.4 + -34.5 78.1 + -33.8 73.8 + -33.1 69.6 +REG_LINE: 197 + -47.7 168. + -48.6 163. + -49.5 158. + -50.2 152. + -50.8 147. + -51.4 141. + -51.8 136. + -52.2 130. + -52.4 125. + -52.6 119. + -52.7 114. + -52.6 108. + -52.5 102. + -52.2 96.9 + -51.9 91.4 + -51.4 85.9 + -50.9 80.4 + -50.3 75.0 + -49.5 69.6 + -48.7 64.3 + -47.8 59.0 + -46.7 53.8 + -45.6 48.7 + -44.4 43.6 + -43.1 38.7 + -41.8 33.8 + -40.3 29.1 + -38.8 24.5 + -37.1 19.9 + -35.4 15.5 + -33.6 11.2 + -31.8 7.08 + -29.8 3.06 + -27.8 -.822 + -25.7 -4.55 + -23.6 -8.14 + -21.4 -11.6 + -19.1 -14.8 + -16.8 -17.9 + -14.5 -20.9 + -12.0 -23.6 + -9.56 -26.2 + -7.04 -28.6 + -4.49 -30.9 + -1.89 -32.9 + .740 -34.8 + 3.40 -36.5 + 6.09 -37.9 + 8.81 -39.2 + 11.5 -40.3 + 14.3 -41.2 + 17.1 -41.9 + 19.8 -42.4 + 22.6 -42.7 + 25.4 -42.8 + 28.2 -42.7 + 31.0 -42.4 + 33.8 -41.9 + 36.5 -41.2 + 39.3 -40.3 + 42.0 -39.3 + 44.7 -38.0 + 47.4 -36.5 + 50.1 -34.8 + 52.7 -33.0 + 55.3 -30.9 + 57.9 -28.7 + 60.4 -26.3 + 62.9 -23.7 + 65.3 -21.0 + 67.7 -18.0 + 70.0 -14.9 + 72.3 -11.7 + 74.5 -8.26 + 76.6 -4.68 + 78.7 -.956 + 80.7 2.92 + 82.6 6.93 + 84.5 11.1 + 86.3 15.4 + 88.0 19.8 + 89.6 24.3 + 91.2 28.9 + 92.6 33.7 + 94.0 38.5 + 95.3 43.5 + 96.5 48.5 + 97.6 53.6 + 98.7 58.8 + 99.6 64.1 + 100. 69.4 + 101. 74.8 + 102. 80.2 + 102. 85.7 + 103. 91.2 + 103. 96.7 + 103. 102. + 104. 108. + 104. 113. + 104. 119. + 103. 125. + 103. 130. + 103. 136. + 102. 141. + 102. 147. + 101. 152. + 100. 157. + 99.6 163. + 98.7 168. + 97.6 173. + 96.5 178. + 95.3 183. + 94.0 188. + 92.6 193. + 91.2 198. + 89.6 203. + 88.0 207. + 86.3 212. + 84.5 216. + 82.6 220. + 80.7 224. + 78.7 228. + 76.6 232. + 74.5 235. + 72.3 239. + 70.0 242. + 67.7 245. + 65.3 248. + 62.9 251. + 60.4 253. + 57.9 256. + 55.3 258. + 52.7 260. + 50.1 262. + 47.4 263. + 44.7 265. + 42.0 266. + 39.3 267. + 36.5 268. + 33.8 269. + 31.0 269. + 28.2 270. + 25.4 270. + 22.6 270. + 19.8 269. + 17.1 269. + 14.3 268. + 11.5 267. + 8.81 266. + 6.09 265. + 3.40 263. + .740 262. + -1.89 260. + -4.49 258. + -7.04 256. + -9.56 253. + -12.0 251. + -14.5 248. + -16.8 245. + -19.1 242. + -21.4 238. + -23.6 235. + -25.7 231. + -27.8 228. + -29.8 224. + -31.8 220. + -33.6 216. + -35.4 211. + -37.1 207. + -38.8 202. + -40.3 198. + -41.8 193. + -43.1 188. + -44.4 183. + -45.6 178. + -46.7 173. + -47.8 168. + -48.7 163. + -49.5 157. + -50.3 152. + -50.9 146. + -51.4 141. + -51.9 135. + -52.2 130. + -52.5 124. + -52.6 119. + -52.7 113. + -52.6 108. + -52.4 102. + -52.2 96.5 + -51.8 91.0 + -51.4 85.5 + -50.8 80.0 + -50.2 74.6 + -49.5 69.2 + -48.6 63.9 + -47.7 58.6 +REG_LINE: 141 + -62.3 179. + -63.4 173. + -64.4 167. + -65.3 160. + -66.1 154. + -66.8 147. + -67.3 140. + -67.7 134. + -68.0 127. + -68.2 120. + -68.3 114. + -68.2 107. + -68.1 100. + -67.8 93.6 + -67.3 87.0 + -66.8 80.4 + -66.2 73.8 + -65.4 67.3 + -64.5 60.8 + -63.5 54.4 + -62.4 48.1 + -61.2 41.9 + -59.9 35.7 + -58.4 29.7 + -56.9 23.8 + -55.2 17.9 + -53.5 12.2 + -51.6 6.66 + -49.6 1.22 + -47.6 -4.07 + -45.4 -9.21 + -43.2 -14.2 + -40.9 -19.0 + -38.5 -23.7 + -36.0 -28.2 + -33.4 -32.5 + -30.8 -36.6 + -28.1 -40.5 + -25.3 -44.2 + -22.4 -47.7 + -19.5 -51.1 + -16.6 -54.2 + -13.5 -57.1 + -10.5 -59.7 + -7.36 -62.2 + -4.21 -64.4 + -1.01 -66.4 + 2.22 -68.2 + 5.47 -69.7 + 8.76 -71.1 + 12.1 -72.1 + 15.4 -73.0 + 18.7 -73.6 + 22.1 -73.9 + 25.4 -74.1 + 28.8 -73.9 + 32.1 -73.6 + 35.4 -73.0 + 38.8 -72.2 + 42.1 -71.1 + 45.3 -69.8 + 48.6 -68.3 + 51.8 -66.5 + 55.0 -64.5 + 58.2 -62.3 + 61.3 -59.8 + 64.4 -57.2 + 67.4 -54.3 + 70.4 -51.2 + 73.3 -47.9 + 76.1 -44.3 + 78.9 -40.6 + 81.6 -36.7 + 84.3 -32.6 + 86.8 -28.3 + 89.3 -23.8 + 91.7 -19.2 + 94.1 -14.4 + 96.3 -9.39 + 98.5 -4.25 + 101. 1.03 + 102. 6.46 + 104. 12.0 + 106. 17.7 + 108. 23.5 + 109. 29.5 + 111. 35.5 + 112. 41.7 + 113. 47.9 + 114. 54.2 + 115. 60.6 + 116. 67.0 + 117. 73.6 + 118. 80.1 + 118. 86.7 + 119. 93.4 + 119. 100. + 119. 107. + 119. 113. + 119. 120. + 119. 127. + 119. 133. + 118. 140. + 118. 147. + 117. 153. + 116. 160. + 115. 166. + 114. 173. + 113. 179. + 112. 185. + 111. 191. + 109. 197. + 108. 203. + 106. 209. + 104. 215. + 102. 220. + 101. 226. + 98.5 231. + 96.3 236. + 94.1 241. + 91.7 246. + 89.3 251. + 86.8 255. + 84.3 259. + 81.6 264. + 78.9 267. + 76.1 271. + 73.3 275. + 70.4 278. + 67.4 281. + 64.4 284. + 61.3 287. + 58.2 289. + 55.0 291. + 51.8 293. + 48.6 295. + 45.3 297. + 42.1 298. + 38.8 299. + 35.4 300. + 34.7 300. +REG_LINE: 53 + 16.2 300. + 15.4 300. + 12.1 299. + 8.76 298. + 5.47 297. + 2.22 295. + -1.01 293. + -4.21 291. + -7.36 289. + -10.5 287. + -13.5 284. + -16.6 281. + -19.5 278. + -22.4 275. + -25.3 271. + -28.1 267. + -30.8 263. + -33.4 259. + -36.0 255. + -38.5 251. + -40.9 246. + -43.2 241. + -45.4 236. + -47.6 231. + -49.6 226. + -51.6 220. + -53.5 215. + -55.2 209. + -56.9 203. + -58.4 197. + -59.9 191. + -61.2 185. + -62.4 179. + -63.5 172. + -64.5 166. + -65.4 160. + -66.2 153. + -66.8 147. + -67.3 140. + -67.8 133. + -68.1 127. + -68.2 120. + -68.3 113. + -68.2 107. + -68.0 99.8 + -67.7 93.2 + -67.3 86.5 + -66.8 79.9 + -66.1 73.3 + -65.3 66.8 + -64.4 60.4 + -63.4 54.0 + -62.3 47.7 +REG_LINE: 128 + -77.0 190. + -78.3 183. + -79.4 175. + -80.5 168. + -81.4 160. + -82.1 153. + -82.8 145. + -83.3 137. + -83.6 129. + -83.8 122. + -83.9 114. + -83.8 106. + -83.6 98.1 + -83.3 90.3 + -82.8 82.6 + -82.2 74.9 + -81.4 67.2 + -80.5 59.6 + -79.5 52.0 + -78.3 44.6 + -77.1 37.2 + -75.6 29.9 + -74.1 22.8 + -72.4 15.7 + -70.6 8.81 + -68.7 2.01 + -66.6 -4.64 + -64.4 -11.1 + -62.2 -17.5 + -59.8 -23.7 + -57.3 -29.7 + -54.7 -35.5 + -51.9 -41.1 + -49.1 -46.5 + -46.2 -51.8 + -43.2 -56.8 + -40.2 -61.6 + -37.0 -66.1 + -33.7 -70.5 + -30.4 -74.6 + -27.0 -78.5 + -23.6 -82.1 + -20.0 -85.5 + -16.5 -88.6 + -12.8 -91.5 + -9.15 -94.1 + -5.42 -96.4 + -1.66 -98.5 + 2.14 -100. + 5.97 -102. + 9.83 -103. + 13.7 -104. + 17.6 -105. + 21.5 -105. + 25.4 -105. + 29.3 -105. + 33.2 -105. + 37.1 -104. + 41.0 -103. + 44.8 -102. + 48.7 -100. + 52.5 -98.5 + 56.2 -96.5 + 60.0 -94.2 + 63.6 -91.6 + 67.3 -88.7 + 70.9 -85.6 + 74.4 -82.2 + 77.8 -78.6 + 81.2 -74.7 + 84.6 -70.6 + 87.8 -66.3 + 91.0 -61.7 + 94.1 -56.9 + 97.1 -51.9 + 100. -46.7 + 103. -41.3 + 105. -35.7 + 108. -29.9 + 111. -23.9 + 113. -17.7 + 115. -11.4 + 117. -4.87 + 120. 1.78 + 121. 8.56 + 123. 15.5 + 125. 22.5 + 127. 29.7 + 128. 37.0 + 129. 44.3 + 130. 51.8 + 131. 59.3 + 132. 66.9 + 133. 74.6 + 134. 82.3 + 134. 90.0 + 135. 97.8 + 135. 106. + 135. 113. + 135. 121. + 135. 129. + 134. 137. + 134. 145. + 133. 152. + 132. 160. + 131. 168. + 130. 175. + 129. 183. + 128. 190. + 127. 197. + 125. 204. + 123. 211. + 121. 218. + 120. 225. + 117. 232. + 115. 238. + 113. 245. + 111. 251. + 108. 257. + 105. 263. + 103. 268. + 100. 274. + 97.1 279. + 94.1 284. + 91.0 289. + 87.8 293. + 84.6 298. + 82.5 300. +REG_LINE: 40 + -31.6 300. + -33.7 297. + -37.0 293. + -40.2 288. + -43.2 284. + -46.2 279. + -49.1 273. + -51.9 268. + -54.7 262. + -57.3 257. + -59.8 251. + -62.2 244. + -64.4 238. + -66.6 232. + -68.7 225. + -70.6 218. + -72.4 211. + -74.1 204. + -75.6 197. + -77.1 190. + -78.3 182. + -79.5 175. + -80.5 167. + -81.4 160. + -82.2 152. + -82.8 144. + -83.3 137. + -83.6 129. + -83.8 121. + -83.9 113. + -83.8 105. + -83.6 97.5 + -83.3 89.8 + -82.8 82.0 + -82.1 74.3 + -81.4 66.7 + -80.5 59.0 + -79.4 51.5 + -78.3 44.1 + -77.0 36.7 +REG_LINE: 97 + -91.6 201. + -93.1 193. + -94.4 184. + -95.6 176. + -96.6 167. + -97.5 158. + -98.2 149. + -98.8 140. + -99.2 132. + -99.4 123. + -99.5 114. + -99.5 105. + -99.2 95.9 + -98.8 87.0 + -98.3 78.2 + -97.6 69.3 + -96.7 60.6 + -95.7 51.9 + -94.5 43.3 + -93.2 34.8 + -91.7 26.3 + -90.1 18.0 + -88.3 9.83 + -86.4 1.77 + -84.3 -6.14 + -82.1 -13.9 + -79.8 -21.5 + -77.3 -28.9 + -74.7 -36.2 + -71.9 -43.2 + -69.1 -50.1 + -66.1 -56.7 + -63.0 -63.2 + -59.8 -69.4 + -56.5 -75.3 + -53.1 -81.1 + -49.5 -86.6 + -45.9 -91.8 + -42.2 -96.8 + -38.4 -101. + -34.5 -106. + -30.6 -110. + -26.5 -114. + -22.5 -117. + -18.3 -121. + -14.1 -124. + -9.84 -126. + -5.53 -129. + -1.19 -131. + 3.19 -133. + 7.59 -134. + 12.0 -135. + 16.5 -136. + 20.9 -136. + 25.4 -137. + 29.9 -136. + 34.3 -136. + 38.8 -135. + 43.2 -134. + 47.6 -133. + 52.0 -131. + 56.3 -129. + 60.6 -126. + 64.9 -124. + 69.1 -121. + 73.2 -118. + 77.3 -114. + 81.4 -110. + 85.3 -106. + 89.2 -102. + 93.0 -96.9 + 96.7 -92.0 + 100. -86.8 + 104. -81.3 + 107. -75.6 + 111. -69.6 + 114. -63.4 + 117. -57.0 + 120. -50.3 + 123. -43.5 + 126. -36.4 + 128. -29.2 + 131. -21.8 + 133. -14.2 + 135. -6.42 + 137. 1.49 + 139. 9.54 + 141. 17.7 + 143. 26.0 + 144. 34.5 + 145. 43.0 + 147. 51.6 + 148. 60.3 + 148. 69.0 + 149. 77.8 + 150. 86.7 + 150. 92.2 +REG_LINE: 23 + 150. 135. + 150. 140. + 149. 149. + 148. 158. + 148. 167. + 147. 175. + 145. 184. + 144. 192. + 143. 201. + 141. 209. + 139. 217. + 137. 225. + 135. 233. + 133. 241. + 131. 249. + 128. 256. + 126. 263. + 123. 270. + 120. 277. + 117. 284. + 114. 290. + 111. 296. + 109. 300. +REG_LINE: 35 + -57.7 300. + -59.8 296. + -63.0 290. + -66.1 284. + -69.1 277. + -71.9 270. + -74.7 263. + -77.3 256. + -79.8 248. + -82.1 241. + -84.3 233. + -86.4 225. + -88.3 217. + -90.1 209. + -91.7 201. + -93.2 192. + -94.5 184. + -95.7 175. + -96.7 166. + -97.6 158. + -98.3 149. + -98.8 140. + -99.2 131. + -99.5 122. + -99.5 113. + -99.4 104. + -99.2 95.3 + -98.8 86.4 + -98.2 77.5 + -97.5 68.7 + -96.6 60.0 + -95.6 51.3 + -94.4 42.7 + -93.1 34.2 + -91.6 25.7 +REG_LINE: 63 + -100. -13.5 + -98.0 -21.1 + -95.5 -29.8 + -92.9 -38.4 + -90.1 -46.7 + -87.2 -54.9 + -84.1 -62.8 + -80.9 -70.5 + -77.5 -78.0 + -74.1 -85.2 + -70.5 -92.2 + -66.7 -98.9 + -62.9 -105. + -58.9 -112. + -54.8 -117. + -50.7 -123. + -46.4 -128. + -42.0 -133. + -37.6 -138. + -33.1 -142. + -28.4 -146. + -23.8 -150. + -19.0 -153. + -14.3 -156. + -9.41 -159. + -4.52 -161. + .403 -163. + 5.36 -165. + 10.3 -166. + 15.3 -167. + 20.4 -168. + 25.4 -168. + 30.4 -168. + 35.4 -167. + 40.4 -166. + 45.4 -165. + 50.4 -163. + 55.3 -161. + 60.2 -159. + 65.0 -156. + 69.8 -153. + 74.5 -150. + 79.2 -146. + 83.8 -142. + 88.4 -138. + 92.8 -133. + 97.2 -129. + 101. -123. + 106. -118. + 110. -112. + 114. -106. + 118. -99.2 + 121. -92.5 + 125. -85.5 + 128. -78.3 + 132. -70.8 + 135. -63.1 + 138. -55.2 + 141. -47.0 + 144. -38.7 + 146. -30.1 + 149. -21.4 + 150. -17.1 +REG_LINE: 9 + 150. 244. + 149. 248. + 146. 257. + 144. 266. + 141. 274. + 138. 282. + 135. 290. + 132. 298. + 131. 300. +REG_LINE: 9 + -79.7 300. + -80.9 297. + -84.1 290. + -87.2 282. + -90.1 274. + -92.9 265. + -95.5 257. + -98.0 248. + -100. 240. +REG_LINE: 54 + -100. -72.8 + -99.7 -73.6 + -96.3 -82.4 + -92.7 -91.0 + -89.0 -99.3 + -85.1 -107. + -81.1 -115. + -77.0 -123. + -72.7 -130. + -68.3 -137. + -63.8 -143. + -59.1 -149. + -54.4 -155. + -49.5 -161. + -44.6 -166. + -39.6 -171. + -34.4 -175. + -29.2 -179. + -24.0 -183. + -18.7 -186. + -13.3 -189. + -7.85 -192. + -2.38 -194. + 3.13 -196. + 8.66 -197. + 14.2 -198. + 19.8 -199. + 25.4 -199. + 30.9 -199. + 36.5 -198. + 42.1 -197. + 47.6 -196. + 53.1 -194. + 58.6 -192. + 64.0 -189. + 69.4 -186. + 74.7 -183. + 80.0 -179. + 85.2 -175. + 90.3 -171. + 95.3 -166. + 100. -161. + 105. -155. + 110. -150. + 115. -143. + 119. -137. + 123. -130. + 128. -123. + 132. -115. + 136. -108. + 140. -99.6 + 144. -91.3 + 147. -82.7 + 150. -75.3 +REG_LINE: 2 + -99.9 300. + -100. 300. +REG_LINE: 13 + -100. -121. + -96.2 -129. + -91.8 -138. + -87.2 -146. + -82.5 -154. + -77.7 -162. + -72.7 -169. + -67.6 -176. + -62.4 -182. + -57.0 -188. + -51.6 -194. + -46.1 -199. + -45.1 -200. +REG_LINE: 13 + 96.0 -200. + 96.8 -199. + 102. -194. + 108. -188. + 113. -182. + 118. -176. + 123. -169. + 128. -162. + 133. -154. + 138. -146. + 143. -138. + 147. -130. + 150. -123. +REG_LINE: 6 + -100. -165. + -97.4 -170. + -92.3 -178. + -87.0 -187. + -81.6 -194. + -77.4 -200. +REG_LINE: 6 + 128. -200. + 132. -195. + 138. -187. + 143. -179. + 148. -170. + 150. -167. +REG_LINE: 10 + 40.9 118. + 24.4 113. + 7.79 108. + -8.78 104. + -25.4 98.9 + -41.9 94.2 + -58.5 89.5 + -75.1 84.8 + -91.7 80.0 + -100. 77.7 +REG_LINE: 12 + 38.0 132. + 24.6 112. + 11.2 92.1 + -2.25 72.0 + -15.7 52.0 + -29.1 31.9 + -42.5 11.9 + -55.9 -8.13 + -69.3 -28.2 + -82.7 -48.2 + -96.1 -68.2 + -100. -74.0 +REG_LINE: 13 + 32.0 142. + 25.0 111. + 18.0 81.0 + 11.1 50.5 + 4.10 20.1 + -2.86 -10.4 + -9.83 -40.8 + -16.8 -71.3 + -23.8 -102. + -30.7 -132. + -37.7 -163. + -44.7 -193. + -46.3 -200. +REG_LINE: 12 + 24.4 145. + 25.5 111. + 26.7 77.8 + 27.9 44.4 + 29.1 11.0 + 30.3 -22.4 + 31.5 -55.8 + 32.7 -89.2 + 33.8 -123. + 35.0 -156. + 36.2 -189. + 36.6 -200. +REG_LINE: 14 + 17.0 140. + 26.1 112. + 35.1 83.4 + 44.2 55.2 + 53.2 27.0 + 62.3 -1.14 + 71.3 -29.3 + 80.3 -57.5 + 89.4 -85.7 + 98.4 -114. + 107. -142. + 117. -170. + 126. -198. + 126. -200. +REG_LINE: 11 + 11.8 128. + 26.4 112. + 41.1 96.3 + 55.8 80.3 + 70.5 64.2 + 85.2 48.2 + 99.9 32.1 + 115. 16.1 + 129. 0.199E-02 + 144. -16.1 + 150. -22.6 +REG_LINE: 10 + 9.84 113. + 26.6 113. + 43.3 113. + 60.1 113. + 76.8 113. + 93.5 113. + 110. 113. + 127. 113. + 144. 113. + 150. 113. +REG_LINE: 11 + 11.8 98.5 + 26.4 115. + 41.1 131. + 55.8 147. + 70.5 163. + 85.2 179. + 99.9 195. + 115. 211. + 129. 227. + 144. 243. + 150. 250. +REG_LINE: 9 + 17.0 87.1 + 26.1 115. + 35.1 143. + 44.2 172. + 53.2 200. + 62.3 228. + 71.3 256. + 80.3 284. + 85.4 300. +REG_LINE: 8 + 24.4 82.3 + 25.5 116. + 26.7 149. + 27.9 182. + 29.1 216. + 30.3 249. + 31.5 283. + 32.1 300. +REG_LINE: 9 + 32.0 85.0 + 25.0 115. + 18.0 146. + 11.1 176. + 4.10 207. + -2.86 237. + -9.83 268. + -16.8 298. + -17.2 300. +REG_LINE: 12 + 38.0 94.7 + 24.6 115. + 11.2 135. + -2.25 155. + -15.7 175. + -29.1 195. + -42.5 215. + -55.9 235. + -69.3 255. + -82.7 275. + -96.1 295. + -99.4 300. +REG_LINE: 10 + 40.9 109. + 24.4 114. + 7.79 118. + -8.78 123. + -25.4 128. + -41.9 133. + -58.5 137. + -75.1 142. + -91.7 147. + -100. 149. +REG_LINE: 2 + 18.7 134. + 17.7 133. +REG_LINE: 2 + 20.4 129. + 19.4 128. +REG_LINE: 2 + 22.1 124. + 21.1 123. +REG_LINE: 2 + 23.8 119. + 22.8 117. +REG_LINE: 2 + 27.2 108. + 28.2 110. +REG_LINE: 2 + 28.8 103. + 29.8 104. +REG_LINE: 2 + 30.5 97.7 + 31.5 99.1 +REG_LINE: 2 + 32.2 92.4 + 33.2 93.8 +REG_LINE: 2 + 35.6 81.9 + 36.6 83.3 +REG_LINE: 2 + 37.3 76.6 + 38.3 78.1 +REG_LINE: 2 + 39.0 71.4 + 40.0 72.8 +REG_LINE: 2 + 40.7 66.1 + 41.7 67.5 +REG_LINE: 2 + 44.0 55.6 + 45.0 57.0 +REG_LINE: 2 + 45.7 50.3 + 46.7 51.8 +REG_LINE: 2 + 47.4 45.1 + 48.4 46.5 +REG_LINE: 2 + 49.1 39.8 + 50.1 41.2 +REG_LINE: 2 + 52.5 29.3 + 53.5 30.7 +REG_LINE: 2 + 54.2 24.0 + 55.2 25.5 +REG_LINE: 2 + 55.9 18.8 + 56.9 20.2 +REG_LINE: 2 + 57.5 13.5 + 58.6 14.9 +REG_LINE: 2 + 60.9 2.99 + 61.9 4.43 +REG_LINE: 2 + 62.6 -2.26 + 63.6 -.831 +REG_LINE: 2 + 64.3 -7.52 + 65.3 -6.09 +REG_LINE: 2 + 66.0 -12.8 + 67.0 -11.3 +REG_LINE: 2 + 69.4 -23.3 + 70.4 -21.9 +REG_LINE: 2 + 71.1 -28.6 + 72.1 -27.1 +REG_LINE: 2 + 72.7 -33.8 + 73.7 -32.4 +REG_LINE: 2 + 74.4 -39.1 + 75.4 -37.6 +REG_LINE: 2 + 77.8 -49.6 + 78.8 -48.2 +REG_LINE: 2 + 79.5 -54.9 + 80.5 -53.4 +REG_LINE: 2 + 81.2 -60.1 + 82.2 -58.7 +REG_LINE: 2 + 82.9 -65.4 + 83.9 -63.9 +REG_LINE: 2 + 86.3 -75.9 + 87.3 -74.5 +REG_LINE: 2 + 87.9 -81.2 + 88.9 -79.7 +REG_LINE: 2 + 89.6 -86.4 + 90.6 -85.0 +REG_LINE: 2 + 91.3 -91.7 + 92.3 -90.2 +REG_LINE: 2 + 94.7 -102. + 95.7 -101. +REG_LINE: 2 + 96.4 -107. + 97.4 -106. +REG_LINE: 2 + 98.1 -113. + 99.1 -111. +REG_LINE: 2 + 99.8 -118. + 101. -117. +REG_LINE: 2 + 103. -128. + 104. -127. +REG_LINE: 2 + 105. -134. + 106. -132. +REG_LINE: 2 + 107. -139. + 108. -138. +REG_LINE: 2 + 108. -144. + 109. -143. +REG_LINE: 2 + 112. -155. + 113. -153. +REG_LINE: 2 + 113. -160. + 114. -159. +REG_LINE: 2 + 115. -165. + 116. -164. +REG_LINE: 2 + 117. -171. + 118. -169. +REG_LINE: 2 + 120. -181. + 121. -180. +REG_LINE: 2 + 122. -186. + 123. -185. +REG_LINE: 2 + 123. -192. + 124. -190. +REG_LINE: 2 + 125. -197. + 126. -195. +REG_LINE: 2 + -80.3 169. + -81.8 170. +REG_LINE: 2 + -82.5 148. + -84.2 148. +REG_LINE: 2 + -83.7 126. + -85.5 126. +REG_LINE: 2 + -83.8 104. + -85.6 104. +REG_LINE: 2 + -80.7 61.1 + -82.3 60.3 +REG_LINE: 2 + -77.6 40.2 + -79.0 39.1 +REG_LINE: 2 + -73.4 19.9 + -74.7 18.7 +REG_LINE: 2 + -68.3 .672 + -69.4 -.674 +REG_LINE: 2 + -55.2 -34.3 + -56.0 -35.9 +REG_LINE: 2 + -47.4 -49.7 + -48.1 -51.3 +REG_LINE: 2 + -38.9 -63.4 + -39.5 -65.1 +REG_LINE: 2 + -29.7 -75.4 + -30.2 -77.1 +REG_LINE: 2 + -9.89 -93.6 + -10.2 -95.3 +REG_LINE: 2 + .619 -99.6 + .416 -101. +REG_LINE: 2 + 11.4 -103. + 11.3 -105. +REG_LINE: 2 + 22.3 -105. + 22.2 -107. +REG_LINE: 2 + 44.1 -102. + 44.2 -104. +REG_LINE: 2 + 54.7 -97.3 + 55.0 -99.1 +REG_LINE: 2 + 65.1 -90.4 + 65.4 -92.2 +REG_LINE: 2 + 75.1 -81.5 + 75.5 -83.2 +REG_LINE: 2 + 93.5 -57.9 + 94.1 -59.5 +REG_LINE: 2 + 102. -43.5 + 102. -45.1 +REG_LINE: 2 + 109. -27.5 + 110. -29.0 +REG_LINE: 2 + 116. -10.1 + 117. -11.5 +REG_LINE: 2 + 126. 28.3 + 128. 27.1 +REG_LINE: 2 + 130. 48.8 + 131. 47.9 +REG_LINE: 2 + 133. 70.0 + 134. 69.3 +REG_LINE: 2 + 134. 91.6 + 136. 91.3 +REG_LINE: 2 + 134. 135. + 136. 136. +REG_LINE: 2 + 133. 157. + 134. 158. +REG_LINE: 2 + 130. 178. + 131. 179. +REG_LINE: 2 + 126. 199. + 128. 200. +REG_LINE: 2 + 116. 237. + 117. 238. +REG_LINE: 2 + 109. 254. + 110. 256. +REG_LINE: 2 + 102. 270. + 102. 272. +REG_LINE: 2 + 93.5 285. + 94.1 286. +REG_LINE: 2 + -38.9 290. + -39.5 292. +REG_LINE: 2 + -47.4 277. + -48.1 278. +REG_LINE: 2 + -55.2 261. + -56.0 263. +REG_LINE: 2 + -68.3 226. + -69.4 228. +REG_LINE: 2 + -73.4 207. + -74.7 208. +REG_LINE: 2 + -77.6 187. + -79.0 188. +REG_LINE: 2 + -80.7 166. + -82.3 167. +REG_LINE: 2 + -83.8 123. + -85.6 123. +REG_LINE: 2 + -83.7 101. + -85.5 100. +REG_LINE: 2 + -82.5 78.9 + -84.2 78.4 +REG_LINE: 2 + -80.3 57.5 + -81.8 56.7 +REG_LINE: 15 + 25.5 113. + 24.9 115. + 24.3 117. + 23.7 119. + 23.1 121. + 22.5 123. + 21.9 125. + 21.2 127. + 20.6 128. + 20.0 130. + 19.4 132. + 18.8 134. + 18.2 136. + 17.6 138. + 17.0 140. +REG_LINE: 15 + 25.5 113. + 26.1 112. + 26.7 110. + 27.3 108. + 27.9 106. + 28.5 104. + 29.1 102. + 29.7 100. + 30.3 98.4 + 30.9 96.5 + 31.5 94.7 + 32.1 92.8 + 32.7 90.9 + 33.3 89.0 + 33.9 87.1 +REG_LINE: 15 + 33.9 87.1 + 34.5 85.3 + 35.1 83.4 + 35.7 81.5 + 36.3 79.6 + 36.9 77.8 + 37.5 75.9 + 38.1 74.0 + 38.7 72.1 + 39.3 70.2 + 39.9 68.4 + 40.5 66.5 + 41.1 64.6 + 41.8 62.7 + 42.4 60.8 +REG_LINE: 15 + 42.4 60.8 + 43.0 59.0 + 43.6 57.1 + 44.2 55.2 + 44.8 53.3 + 45.4 51.5 + 46.0 49.6 + 46.6 47.7 + 47.2 45.8 + 47.8 43.9 + 48.4 42.1 + 49.0 40.2 + 49.6 38.3 + 50.2 36.4 + 50.8 34.5 +REG_LINE: 15 + 50.8 34.5 + 51.4 32.7 + 52.0 30.8 + 52.6 28.9 + 53.2 27.0 + 53.8 25.2 + 54.4 23.3 + 55.0 21.4 + 55.6 19.5 + 56.2 17.6 + 56.8 15.8 + 57.4 13.9 + 58.0 12.0 + 58.6 10.1 + 59.2 8.25 +REG_LINE: 15 + 59.2 8.25 + 59.8 6.38 + 60.4 4.50 + 61.0 2.62 + 61.6 .740 + 62.3 -1.14 + 62.9 -3.02 + 63.5 -4.89 + 64.1 -6.77 + 64.7 -8.65 + 65.3 -10.5 + 65.9 -12.4 + 66.5 -14.3 + 67.1 -16.2 + 67.7 -18.0 +REG_LINE: 15 + 67.7 -18.0 + 68.3 -19.9 + 68.9 -21.8 + 69.5 -23.7 + 70.1 -25.6 + 70.7 -27.4 + 71.3 -29.3 + 71.9 -31.2 + 72.5 -33.1 + 73.1 -34.9 + 73.7 -36.8 + 74.3 -38.7 + 74.9 -40.6 + 75.5 -42.5 + 76.1 -44.3 +REG_LINE: 15 + 76.1 -44.3 + 76.7 -46.2 + 77.3 -48.1 + 77.9 -50.0 + 78.5 -51.9 + 79.1 -53.7 + 79.7 -55.6 + 80.3 -57.5 + 80.9 -59.4 + 81.5 -61.2 + 82.2 -63.1 + 82.8 -65.0 + 83.4 -66.9 + 84.0 -68.8 + 84.6 -70.6 +REG_LINE: 15 + 84.6 -70.6 + 85.2 -72.5 + 85.8 -74.4 + 86.4 -76.3 + 87.0 -78.1 + 87.6 -80.0 + 88.2 -81.9 + 88.8 -83.8 + 89.4 -85.7 + 90.0 -87.5 + 90.6 -89.4 + 91.2 -91.3 + 91.8 -93.2 + 92.4 -95.1 + 93.0 -96.9 +REG_LINE: 15 + 93.0 -96.9 + 93.6 -98.8 + 94.2 -101. + 94.8 -103. + 95.4 -104. + 96.0 -106. + 96.6 -108. + 97.2 -110. + 97.8 -112. + 98.4 -114. + 99.0 -116. + 99.6 -118. + 100. -119. + 101. -121. + 101. -123. +REG_LINE: 15 + 101. -123. + 102. -125. + 103. -127. + 103. -129. + 104. -131. + 104. -133. + 105. -134. + 106. -136. + 106. -138. + 107. -140. + 107. -142. + 108. -144. + 109. -146. + 109. -148. + 110. -150. +REG_LINE: 15 + 110. -150. + 110. -151. + 111. -153. + 112. -155. + 112. -157. + 113. -159. + 114. -161. + 114. -163. + 115. -165. + 115. -166. + 116. -168. + 117. -170. + 117. -172. + 118. -174. + 118. -176. +REG_LINE: 14 + 118. -176. + 119. -178. + 120. -180. + 120. -181. + 121. -183. + 121. -185. + 122. -187. + 123. -189. + 123. -191. + 124. -193. + 124. -195. + 125. -196. + 126. -198. + 126. -200. +REG_LINE: 15 + -82.8 82.6 + -83.3 90.3 + -83.6 98.1 + -83.8 106. + -83.9 114. + -83.8 122. + -83.6 129. + -83.3 137. + -82.8 145. + -82.1 153. + -81.4 160. + -80.5 168. + -79.4 175. + -78.3 183. + -77.0 190. +REG_LINE: 15 + -82.8 82.6 + -82.2 74.9 + -81.4 67.2 + -80.5 59.6 + -79.5 52.0 + -78.3 44.6 + -77.1 37.2 + -75.6 29.9 + -74.1 22.8 + -72.4 15.7 + -70.6 8.81 + -68.7 2.01 + -66.6 -4.64 + -64.4 -11.1 + -62.2 -17.5 +REG_LINE: 15 + -62.2 -17.5 + -59.8 -23.7 + -57.3 -29.7 + -54.7 -35.5 + -51.9 -41.1 + -49.1 -46.5 + -46.2 -51.8 + -43.2 -56.8 + -40.2 -61.6 + -37.0 -66.1 + -33.7 -70.5 + -30.4 -74.6 + -27.0 -78.5 + -23.6 -82.1 + -20.0 -85.5 +REG_LINE: 15 + -20.0 -85.5 + -16.5 -88.6 + -12.8 -91.5 + -9.15 -94.1 + -5.42 -96.4 + -1.66 -98.5 + 2.14 -100. + 5.97 -102. + 9.83 -103. + 13.7 -104. + 17.6 -105. + 21.5 -105. + 25.4 -105. + 29.3 -105. + 33.2 -105. +REG_LINE: 15 + 33.2 -105. + 37.1 -104. + 41.0 -103. + 44.8 -102. + 48.7 -100. + 52.5 -98.5 + 56.2 -96.5 + 60.0 -94.2 + 63.6 -91.6 + 67.3 -88.7 + 70.9 -85.6 + 74.4 -82.2 + 77.8 -78.6 + 81.2 -74.7 + 84.6 -70.6 +REG_LINE: 15 + 84.6 -70.6 + 87.8 -66.3 + 91.0 -61.7 + 94.1 -56.9 + 97.1 -51.9 + 100. -46.7 + 103. -41.3 + 105. -35.7 + 108. -29.9 + 111. -23.9 + 113. -17.7 + 115. -11.4 + 117. -4.87 + 120. 1.78 + 121. 8.56 +REG_LINE: 15 + 121. 8.56 + 123. 15.5 + 125. 22.5 + 127. 29.7 + 128. 37.0 + 129. 44.3 + 130. 51.8 + 131. 59.3 + 132. 66.9 + 133. 74.6 + 134. 82.3 + 134. 90.0 + 135. 97.8 + 135. 106. + 135. 113. +REG_LINE: 15 + 135. 113. + 135. 121. + 135. 129. + 134. 137. + 134. 145. + 133. 152. + 132. 160. + 131. 168. + 130. 175. + 129. 183. + 128. 190. + 127. 197. + 125. 204. + 123. 211. + 121. 218. +REG_LINE: 15 + 121. 218. + 120. 225. + 117. 232. + 115. 238. + 113. 245. + 111. 251. + 108. 257. + 105. 263. + 103. 268. + 100. 274. + 97.1 279. + 94.1 284. + 91.0 289. + 87.8 293. + 84.6 298. +REG_LINE: 2 + 84.6 298. + 82.5 300. +REG_LINE: 12 + -31.6 300. + -33.7 297. + -37.0 293. + -40.2 288. + -43.2 284. + -46.2 279. + -49.1 273. + -51.9 268. + -54.7 262. + -57.3 257. + -59.8 251. + -62.2 244. +REG_LINE: 15 + -62.2 244. + -64.4 238. + -66.6 232. + -68.7 225. + -70.6 218. + -72.4 211. + -74.1 204. + -75.6 197. + -77.1 190. + -78.3 182. + -79.5 175. + -80.5 167. + -81.4 160. + -82.2 152. + -82.8 144. +REG_LINE: 15 + -82.8 144. + -83.3 137. + -83.6 129. + -83.8 121. + -83.9 113. + -83.8 105. + -83.6 97.5 + -83.3 89.8 + -82.8 82.0 + -82.1 74.3 + -81.4 66.7 + -80.5 59.0 + -79.4 51.5 + -78.3 44.1 + -77.0 36.7 +REG_LINE: 15 + -82.8 144. + -83.3 137. + -83.6 129. + -83.8 121. + -83.9 113. + -83.8 105. + -83.6 97.5 + -83.3 89.8 + -82.8 82.0 + -82.1 74.3 + -81.4 66.7 + -80.5 59.0 + -79.4 51.5 + -78.3 44.1 + -77.0 36.7 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0.0' + 27.8 114. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '50.0' + 36.3 87.9 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '100.0' + 44.7 61.6 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '150.0' + 53.2 35.3 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '200.0' + 61.6 9.02 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '250.0' + 70.1 -17.3 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '300.0' + 78.5 -43.6 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '350.0' + 86.9 -69.9 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '400.0' + 95.4 -96.2 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '450.0' + 104. -122. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '500.0' + 112. -149. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '550.0' + 121. -175. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-3.0' + -80.3 82.7 BC .997 0.724E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-2.5' + -59.8 -16.6 BC .936 .352 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-2.0' + -18.4 -83.6 BC .673 .740 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-1.5' +Symbol = "p1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 2" # Axis Label + Symbol = "p2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + InvB = 1 # Second Mapping used in inverse direction + MapA = # First component Mapping + Begin MathMap # Transformation using mathematical functions + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Fwd1 = "r=sqrt(x*x+y*y)" # Forward function 1 + Fwd2 = "theta=atan2(y,x)" # Forward function 2 + Inv1 = "x=r*cos(theta)" # Inverse function 1 + Inv2 = "y=r*sin(theta)" # Inverse function 2 + SimpFI = 1 # Forward-inverse pairs may simplify + SimpIF = 1 # Inverse-forward pairs may simplify + End MathMap + MapB = # Second component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -101.5 # Shift for axis 1 + Sft2 = -201.5 # Shift for axis 2 + End WinMap + End CmpMap + End FrameSet + Begin FrameSet # Set of inter-related coordinate systems +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic projection" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Epoch = 1977.77512999212 # Besselian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# System = "FK5" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + IsA Frame # Coordinate system description + Nframe = 2 # Number of Frames in FrameSet + Base = 1 # Index of base Frame + Currnt = 2 # Index of current Frame + Lnk2 = 1 # Node 2 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Pixel Coordinates" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Pixel axis 1" # Label for axis 1 +# Lbl2 = "Pixel axis 2" # Label for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel axis 1" # Axis Label + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel axis 2" # Axis Label + End Axis + End Frame + Frm2 = # Frame number 2 + Begin SkyFrame # Description of celestial coordinate system + Ident = " " # Permanent Object identification string + IsA Object # AST Object +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic projection" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain + Epoch = 1977.77512999212 # Besselian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 + System = "FK5" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + Proj = "gnomonic" # Description of sky projection + Eqnox = 2000 # Julian epoch of mean equinox + SRefIs = "Ignored" # Not rotated (ref. pos. is ignored) + SRef1 = 0 # Ref. pos. RA 0:00:00.0 + SRef2 = -1.57079633000002 # Ref. pos. Dec -90:00:00 + End SkyFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -893.6318379289 # Shift for axis 1 + Sft2 = -223.8380193875 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -3.25441534352674e-06 # Forward matrix value + M1 = -1.60367292352974e-08 # Forward matrix value + M2 = -1.812057487023e-08 # Forward matrix value + M3 = 3.25725533992408e-06 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longit 32.8 -102. BC -.145 .989 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-0.5' + 119. 9.22 BC -.965 .262 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0.0' + 132. 113. BC -1.00 0.125E-02 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0.5' + 119. 218. TC .964 .265 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '1.0' + 82.6 296. TC .787 .617 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '2.5' + -59.8 243. TC -.936 .352 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '3.0' + -80.3 144. TC -.997 0.724E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 1 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: ' A FITS test' + 31.0 313. BC .000 1.00 + + + + + FITS test number 3 + ==================== + + + +AST_SHOW: + +REG_SINK: +SIMPLE = T / file does conform to FITS standard +BITPIX = 16 / number of bits per data pixel +NAXIS = 2 / number of data axes +NAXIS1 = 1787 / length of data axis 1 +NAXIS2 = 447 / length of data axis 2 +EXTEND = T / FITS dataset may contain extensions +COMMENT FITS (Flexible Image Transport System) format defined in Astronomy and +COMMENT Astrophysics Supplement Series v44/p363, v44/p371, v73/p359, v73/p365. +COMMENT Contact the NASA Science Office of Standards and Technology for the +COMMENT FITS Definition document #100 and other FITS information. +PLATENUM= '3665 ' / Plate number +EMULSION= 'IIIaJ ' / Kodak emulsion type +FILTER = 'GG395 ' / Schott glass filter type +PLTSCALE= '67.14 ' / [arcsec/mm] plate scale +FIELDNUM= '1 ' / Sky survey field number +TELESCOP= 'UKST ' / Telescope on which the plate was taken +TELETYPE= 'SCHM ' / Type of telescope +SITELAT = -0.5458410576565 / [radians] latitude of telescope +SITELONG= 2.601766194458 / [radians] longitude of telescope +LST = '00:20 ' / [hh:mm] local sidereal time at start of obs +INSTRUME= 'SuperCOSMOS I' / Measuring machine +DATE-MES= '2000-11-04' / [yyyy-mm-dd] Date of this plate measurement +NHKLINES= 146 / Number of lines from house-keeping file +HKLIN001= 'JOB.JOBNO UKJ001' +HKLIN002= 'JOB.DATE-MES 2000:11:04' +HKLIN003= 'JOB.TIME 12:51:09' +HKLIN004= 'JOB.INSTRUME SuperCOSMOS I' +HKLIN005= 'JOB.ORIGIN Royal Observatory Edinburgh' +HKLIN006= 'JOB.SOFTWARE /home/scosdev/v033' +HKLIN007= 'JOB.OPERATOR ebt' +HKLIN008= 'JOB.USER htm' +HKLIN009= 'JOB.USERREF NONE' +HKLIN010= 'JOB.UORIGIN ROE' +HKLIN011= 'JOB.UCOUNTRY uk' +HKLIN012= 'JOB.COMMENT Digital catalogue of the Sky' +HKLIN013= 'JOB.IAM_FILE iam.srt'/ / ' / +HKLIN014= 'PLATE.TELESCOP UKST' +HKLIN015= 'PLATE.TELTYPE SCHM' +HKLIN016= 'PLATE.PLATE 3665' +HKLIN017= 'PLATE.MATERIAL 3mm glass' +HKLIN018= 'PLATE.EMULSION IIIaJ' +HKLIN019= 'PLATE.FILTER GG395' +HKLIN020= 'PLATE.PSCALE 67.14' +HKLIN021= 'PLATE.FIELD 1' +HKLIN022= 'PLATE.RA_PNT 0' +HKLIN023= 'PLATE.DEC_PNT -90' +HKLIN024= 'PLATE.RADECSYS FK4' +HKLIN025= 'PLATE.EQUINOX 1950' +HKLIN026= 'PLATE.TIMESYS BESSELIAN' +HKLIN027= 'PLATE.EPOCH 1977.78'/ / ' / +HKLIN028= 'PLATE.EXPOSURE 75' +HKLIN029= 'PLATE.UTDATE 771011' +HKLIN030= 'PLATE.LST 0020' +HKLIN031= 'PLATE.MJD 43426.573008796' +HKLIN032= 'PLATE.TELLAT -0.54584105765654' +HKLIN033= 'PLATE.TELLONG 2.6017661944583' +HKLIN034= 'PLATE.TELHT 1145' +HKLIN035= 'PLATE.TEMP 273.155'/ / ' / +HKLIN036= 'PLATE.ATMOSP 1013.25'/ / ' / +HKLIN037= 'PLATE.HUMID 0.5' +HKLIN038= 'PLATE.WAVE 4500' +HKLIN039= 'PLATE.TROPL 0.0065' +HKLIN040= 'CALIBRATION.CALTYPE SPLINE' +HKLIN041= 'CALIBRATION.STEPWEDG KPNO' +HKLIN042= 'CALIBRATION.NSTEPS 8' +HKLIN043= 'MEASUREMENT.ORIENTAT news' +HKLIN044= 'MEASUREMENT.EMULPOS UP' +HKLIN045= 'MEASUREMENT.SCANFILT 14' +HKLIN046= 'MEASUREMENT.SOSP 552' +HKLIN047= 'MEASUREMENT.STEPSIZE 10' +HKLIN048= 'MEASUREMENT.SCANLEN 1152' +HKLIN049= 'MEASUREMENT.A-XMIN 1622000'/ / ' / +HKLIN050= 'MEASUREMENT.A-YMIN 1622000'/ / ' / +HKLIN051= 'MEASUREMENT.A-XMAX 33878000' +HKLIN052= 'MEASUREMENT.A-YMAX 33878000' +HKLIN053= 'MEASUREMENT.X_PNT 17500000' +HKLIN054= 'MEASUREMENT.Y_PNT 18000000' +HKLIN055= 'ANALYSIS.NPARAMS 32' +HKLIN056= 'ANALYSIS.AREACUT 8' +HKLIN057= 'ANALYSIS.AP-PARAM 1.07' +HKLIN058= 'DEBLEND.DB-PARAM 1.05' +HKLIN059= 'DEBLEND.DB-AMIN 16' +HKLIN060= 'DEBLEND.DB-AMAX 100000' +HKLIN061= 'DEBLEND.DB-ACUT 8' +HKLIN062= 'DEBLEND.DB-LEVEL 16' +HKLIN063= 'DEBLEND.SELECT PARENT+CHILD' +HKLIN064= 'SKY.SKYSQUAR 64' +HKLIN065= 'SKY.SKYDEFN MEDIAN' +HKLIN066= 'SKY.SKYFILTR bdkjunk'/ / ' / +HKLIN067= 'SKY.F-THRESH 8' +HKLIN068= 'SKY.F-SCLEN 4' +HKLIN069= 'THRESHOLDING.PCUT 10' +HKLIN070= 'IAMQC.AREAMIN 8' +HKLIN071= 'IAMQC.AREAMAX 77346' +HKLIN072= 'IAMQC.MINMAG -30515' +HKLIN073= 'IAMQC.MAXMAG -17954' +HKLIN074= 'IAMQC.MINELL 0.0004156232' +HKLIN075= 'IAMQC.MAXELL 1' +HKLIN076= 'IAMQC.MODELL 0.14' +HKLIN077= 'IAMQC.MODOR 91' +HKLIN078= 'IAMQC.MIDELL 0.21' +HKLIN079= 'IAMQC.MIDOR 93' +HKLIN080= 'IAMQC.MEANELL 0.2467037' +HKLIN081= 'IAMQC.MEANOR 91.63474' +HKLIN082= 'IAMQC.NUMOBJ 556985' +HKLIN083= 'IAMQC.PARENTS 486656' +HKLIN084= 'IAMQC.RANGING TRUE' +HKLIN085= 'IAMQC.LANE_1 15571' +HKLIN086= 'IAMQC.LANE_2 33207' +HKLIN087= 'IAMQC.LANE_3 51478' +HKLIN088= 'IAMQC.LANE_4 69944' +HKLIN089= 'IAMQC.LANE_5 89236' +HKLIN090= 'IAMQC.LANE_6 108416' +HKLIN091= 'IAMQC.LANE_7 127481' +HKLIN092= 'IAMQC.LANE_8 146699' +HKLIN093= 'IAMQC.LANE_9 166380' +HKLIN094= 'IAMQC.LANE_10 186126' +HKLIN095= 'IAMQC.LANE_11 205946' +HKLIN096= 'IAMQC.LANE_12 225915' +HKLIN097= 'IAMQC.LANE_13 245926' +HKLIN098= 'IAMQC.LANE_14 266574' +HKLIN099= 'IAMQC.LANE_15 287150' +HKLIN100= 'IAMQC.LANE_16 308087' +HKLIN101= 'IAMQC.LANE_17 328830' +HKLIN102= 'IAMQC.LANE_18 350253' +HKLIN103= 'IAMQC.LANE_19 370738' +HKLIN104= 'IAMQC.LANE_20 391722' +HKLIN105= 'IAMQC.LANE_21 412801' +HKLIN106= 'IAMQC.LANE_22 433795' +HKLIN107= 'IAMQC.LANE_23 454383' +HKLIN108= 'IAMQC.LANE_24 474711' +HKLIN109= 'IAMQC.LANE_25 495108' +HKLIN110= 'IAMQC.LANE_26 515755' +HKLIN111= 'IAMQC.LANE_27 536499' +HKLIN112= 'IAMQC.LANE_28 556985' +HKLIN113= 'XYTORADEC.STARCAT /sdata/scos/refcats/tycho2.FIT' +HKLIN114= 'XYTORADEC.BRIGHTLIM 9' +HKLIN115= 'XYTORADEC.C-EQUIN 2000' +HKLIN116= 'XYTORADEC.C-EQTSYS JULIAN' +HKLIN117= 'XYTORADEC.C-EPOCH 2000' +HKLIN118= 'XYTORADEC.C-EPTSYS JULIAN' +HKLIN119= 'XYTORADEC.R-EQUIN 2000' +HKLIN120= 'XYTORADEC.R-TSYS JULIAN' +HKLIN121= 'XYTORADEC.MAXITER 5000' +HKLIN122= 'XYTORADEC.RCRITINI 500000' +HKLIN123= 'XYTORADEC.RCRITABS 50000' +HKLIN124= 'XYTORADEC.RCRITREL 1' +HKLIN125= 'XYTORADEC.RCRITFIN 3' +HKLIN126= 'XYTORADEC.HARDCOPY /scos1/scos/UKJ001/UKJ001.ps' +HKLIN127= 'XYTORADEC.REFSMULT 5' +HKLIN128= 'XYTORADEC.RESDMULT 1000' +HKLIN129= 'XYTORADEC.RACOL RA' +HKLIN130= 'XYTORADEC.DECOL DEC' +HKLIN131= 'XYTORADEC.RAPMCOL PMRA' +HKLIN132= 'XYTORADEC.DECPMCOL PMDE' +HKLIN133= 'XYTORADEC.PLXCOL NONE' +HKLIN134= 'XYTORADEC.RVCOL NONE' +HKLIN135= 'XYTORADEC.MAGCOL VT' +HKLIN136= 'XYTORADEC.STARSC 2374' +HKLIN137= 'XYTORADEC.STARSU 1727' +HKLIN138= 'XYTORADEC.COEFFS_1 17.640343856524' +HKLIN139= 'XYTORADEC.COEFFS_2 -260.44151995641' +HKLIN140= 'XYTORADEC.COEFFS_3 -163.09155572601' +HKLIN141= 'XYTORADEC.COEFFS_4 17.504230442205' +HKLIN142= 'XYTORADEC.COEFFS_5 -163.08676953832' +HKLIN143= 'XYTORADEC.COEFFS_6 260.48817907668' +HKLIN144= 'XYTORADEC.DISTR -0.33333333333333' +HKLIN145= 'XYTORADEC.RA_PNT 0.54924996662137' +HKLIN146= 'XYTORADEC.DEC_PNT -1.5684931501781' +HISTORY = 'SuperCOSMOS image analysis and mapping mode (IAM and MM)' / +HISTORY = 'data written by xydcomp_ss.' / +HISTORY = 'Any questions/comments/suggestions/bug reports should be sent' / +HISTORY = 'to N.Hambly@roe.ac.uk' / +ASTSIGX = 0.37 / [arcsec] std. dev. of astrometric fit in X +ASTSIGY = 0.38 / [arcsec] std. dev. of astrometric fit in Y +PC001001= 1.0 / DEPRECATED - Axis rotation matrix +PC001002= 0.004927623810613 / DEPRECATED - Axis rotation matrix +PC002001= -0.005563056187788 / DEPRECATED - Axis rotation matrix +PC002002= 1.0 / DEPRECATED - Axis rotation matrix +CROTA2 = 0.3005532298491 / DEPRECATED - rotation of axis 2 +DATATYPE= 'INTEGER*2' / Type of data +DATUNITS= 'DENSITY ' / Units: transmission, density or intensity +XPIXELSZ= 9.997114974 / [microns] X pixel size +YPIXELSZ= 10.0 / [microns] Y pixel size +OBJCTRA = ' 0 0 0.000' / Centre Right Ascension (J2000) +OBJCTDEC= '-90 0 0.00' / Centre Declination (J2000) +OBJCTX = 16368.63183793 / [pixels] Centre X on plate +OBJCTY = 14740.83801939 / [pixels] Centre Y on plate + +Objects written: 1 + +Native Encoding: + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST WCS information in AST format AST +COMMENT AST See http://www.starlink.ac.uk/ast/ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +BASE_A = 1 / Index of base Frame +CURRNT_A= 2 / Index of current Frame +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Pixel Coordinates' / Title of coordinate system +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Pixel axis 1' / Axis Label +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Pixel axis 2' / Axis Label +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'SkyFrame' / Description of celestial coordinate system +IDENT_A = '" " ' / Permanent Object identification string +ISA_A = 'Object ' / AST Object +NAXES_B = 2 / Number of coordinate axes +EPOCH_A = 1977.77512999212 / Besselian epoch of observation +SYSTEM_A= 'FK5 ' / Coordinate system type +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'SkyAxis ' / Celestial coordinate axis +ENDAST_D= 'SkyAxis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'SkyAxis ' / Celestial coordinate axis +ENDAST_E= 'SkyAxis ' / End of object definition +ISA_B = 'Frame ' / Coordinate system description +PROJ_A = 'gnomonic' / Description of sky projection +EQNOX_A = 2000.0 / Julian epoch of mean equinox +SREFIS_A= 'Ignored ' / Not rotated (ref. pos. is ignored) +SREF1_A = 0.0 / Ref. pos. RA 0:00:00.0 +SREF2_A = -1.57079633 / Ref. pos. Dec -90:00:00 +ENDAST_F= 'SkyFrame' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISSIMP_A= 1 / Mapping has been simplified +ISA_C = 'Mapping ' / Mapping between coordinate systems +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'WinMap ' / Map one window on to another +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_D = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -893.6318379289 / Shift for axis 1 +SFT2_A = -223.8380193875 / Shift for axis 2 +ENDAST_G= 'WinMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'CmpMap ' / Compound Mapping +NIN_C = 2 / Number of input coordinates +ISA_E = 'Mapping ' / Mapping between coordinate systems +MAPA_B = ' ' / First component Mapping +BEGAST_K= 'MatrixMap' / Matrix transformation +NIN_D = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_F = 'Mapping ' / Mapping between coordinate systems +M0_A = -3.25441534352674E-6/ Forward matrix value +M1_A = -1.60367292352974E-8/ Forward matrix value +M2_A = -1.812057487023E-8 / Forward matrix value +M3_A = 3.25725533992408E-6 / Forward matrix value +FORM_A = 'Full ' / Matrix storage form +ENDAST_H= 'MatrixMap' / End of object definition +MAPB_B = ' ' / Second component Mapping +BEGAST_L= 'CmpMap ' / Compound Mapping +NIN_E = 2 / Number of input coordinates +ISA_G = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +MAPA_C = ' ' / First component Mapping +BEGAST_M= 'WcsMap ' / FITS-WCS sky projection +NIN_F = 2 / Number of input coordinates +INVERT_C= 1 / Mapping inverted +ISA_H = 'Mapping ' / Mapping between coordinate systems +TYPE_A = 'TAN ' / Gnomonic projection +ENDAST_I= 'WcsMap ' / End of object definition +MAPB_C = ' ' / Second component Mapping +BEGAST_N= 'CmpMap ' / Compound Mapping +NIN_G = 2 / Number of input coordinates +ISA_I = 'Mapping ' / Mapping between coordinate systems +INVA_B = 1 / First Mapping used in inverse direction +MAPA_D = ' ' / First component Mapping +BEGAST_O= 'SphMap ' / Cartesian to Spherical mapping +NIN_H = 3 / Number of input coordinates +NOUT_A = 2 / Number of output coordinates +INVERT_D= 1 / Mapping inverted +ISA_J = 'Mapping ' / Mapping between coordinate systems +UNTRD_A = 1 / All input vectors have unit length +PLRLG_A = 0.0 / Polar longitude (rad.s) +ENDAST_J= 'SphMap ' / End of object definition +MAPB_D = ' ' / Second component Mapping +BEGAST_P= 'CmpMap ' / Compound Mapping +NIN_I = 3 / Number of input coordinates +NOUT_B = 2 / Number of output coordinates +ISA_K = 'Mapping ' / Mapping between coordinate systems +MAPA_E = ' ' / First component Mapping +BEGAST_Q= 'MatrixMap' / Matrix transformation +NIN_J = 3 / Number of input coordinates +INVERT_E= 0 / Mapping not inverted +ISA_L = 'Mapping ' / Mapping between coordinate systems +M0_B = -1.0 / Forward matrix value +M1_B = 1.0 / Forward matrix value +M2_B = -1.0 / Forward matrix value +FORM_B = 'Diagonal' / Matrix storage form +ENDAST_K= 'MatrixMap' / End of object definition +MAPB_E = ' ' / Second component Mapping +BEGAST_R= 'SphMap ' / Cartesian to Spherical mapping +NIN_K = 3 / Number of input coordinates +NOUT_C = 2 / Number of output coordinates +INVERT_F= 0 / Mapping not inverted +ISA_M = 'Mapping ' / Mapping between coordinate systems +UNTRD_B = 1 / All input vectors have unit length +PLRLG_B = 0.0 / Polar longitude (rad.s) +ENDAST_L= 'SphMap ' / End of object definition +ENDAST_M= 'CmpMap ' / End of object definition +ENDAST_N= 'CmpMap ' / End of object definition +ENDAST_O= 'CmpMap ' / End of object definition +ENDAST_P= 'CmpMap ' / End of object definition +ENDAST_Q= 'CmpMap ' / End of object definition +ENDAST_R= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST + +ATTRIBUTES: + Colour(axis1) : 1 + Font(Stri) : 1 + Nout : 1 + Class : 4 + Tol : 0.100000E-01 + Gap(1) : .523599 + Border : 0 + Invert : 0 + TextLabGap : 0.100000E-01 + Nin : 2 + Current : 3 + Base : 1 + Nobject : 1 + RefCOUNT : 1 + +AST_GRID: +REG_LINE: 2 + 15.5 -136. + 15.4 -137. +REG_LINE: 2 + 18.6 -144. + 18.5 -146. +REG_LINE: 2 + 21.8 -149. + 21.8 -151. +REG_LINE: 2 + 25.1 -150. + 25.1 -154. +REG_LINE: 2 + 28.3 -149. + 28.4 -150. +REG_LINE: 2 + 31.5 -143. + 31.6 -145. +REG_LINE: 2 + 34.6 -135. + 34.7 -136. +REG_LINE: 2 + 37.6 -123. + 37.8 -127. +REG_LINE: 2 + 40.3 -108. + 40.4 -110. +REG_LINE: 2 + 42.7 -91.0 + 42.9 -92.7 +REG_LINE: 2 + 44.9 -71.2 + 45.2 -72.9 +REG_LINE: 2 + 46.7 -49.3 + 47.5 -53.0 +REG_LINE: 2 + 48.1 -25.8 + 48.6 -27.4 +REG_LINE: 2 + 49.2 -.928 + 49.9 -2.51 +REG_LINE: 2 + 49.8 24.8 + 51.0 23.5 +REG_LINE: 2 + 50.0 50.9 + 53.7 51.1 +REG_LINE: 2 + 49.7 77.1 + 50.9 78.4 +REG_LINE: 2 + 49.1 103. + 49.8 104. +REG_LINE: 2 + 48.0 127. + 48.5 129. +REG_LINE: 2 + 46.6 151. + 47.3 155. +REG_LINE: 2 + 44.7 173. + 45.0 174. +REG_LINE: 2 + 42.6 192. + 42.8 194. +REG_LINE: 2 + 40.1 209. + 40.2 211. +REG_LINE: 2 + 37.4 224. + 37.6 228. +REG_LINE: 2 + 34.4 235. + 34.5 237. +REG_LINE: 2 + 31.3 244. + 31.4 245. +REG_LINE: 2 + 28.1 248. + 28.1 250. +REG_LINE: 2 + 24.8 250. + 24.8 254. +REG_LINE: 2 + 21.6 248. + 21.5 250. +REG_LINE: 2 + 18.4 243. + 18.3 245. +REG_LINE: 2 + 15.3 234. + 15.2 236. +REG_LINE: 2 + 12.3 223. + 12.1 226. +REG_LINE: 2 + 9.62 208. + 9.45 210. +REG_LINE: 2 + 7.17 191. + 6.95 192. +REG_LINE: 2 + 5.02 171. + 4.74 173. +REG_LINE: 2 + 3.22 149. + 2.41 153. +REG_LINE: 2 + 1.78 125. + 1.27 127. +REG_LINE: 2 + .747 101. + -0.657E-02 102. +REG_LINE: 2 + .124 74.8 + -1.11 76.1 +REG_LINE: 2 + -0.740E-01 48.7 + -3.82 48.5 +REG_LINE: 2 + .156 22.6 + -1.02 21.3 +REG_LINE: 2 + .811 -3.08 + 0.841E-01 -4.67 +REG_LINE: 2 + 1.88 -27.8 + 1.38 -29.5 +REG_LINE: 2 + 3.34 -51.3 + 2.56 -54.9 +REG_LINE: 2 + 5.17 -73.0 + 4.89 -74.7 +REG_LINE: 2 + 7.34 -92.6 + 7.13 -94.3 +REG_LINE: 2 + 9.81 -110. + 9.65 -111. +REG_LINE: 2 + 12.5 -124. + 12.3 -128. +REG_LINE: 2 + 15.5 -136. + 15.4 -137. +REG_LINE: 2 + 18.6 -144. + 18.5 -146. +REG_LINE: 2 + 21.8 -149. + 21.8 -151. +REG_LINE: 2 + 25.1 -150. + 25.1 -154. +REG_LINE: 2 + 28.3 -149. + 28.4 -150. +REG_LINE: 2 + 31.5 -143. + 31.6 -145. +REG_LINE: 2 + 34.6 -135. + 34.7 -136. +REG_LINE: 2 + 24.9 74.8 + 26.5 75.6 +REG_LINE: 2 + 24.9 99.9 + 26.5 101. +REG_LINE: 2 + 24.9 125. + 26.5 126. +REG_LINE: 2 + 24.9 150. + 28.3 152. +REG_LINE: 2 + 24.9 175. + 26.5 176. +REG_LINE: 2 + 24.9 200. + 26.4 201. +REG_LINE: 2 + 24.8 225. + 26.4 226. +REG_LINE: 2 + 24.8 250. + 28.2 252. +REG_LINE: 2 + 24.8 275. + 26.4 276. +REG_LINE: 15 + 25.1 -150. + 24.1 -150. + 23.2 -150. + 22.3 -149. + 21.3 -148. + 20.4 -147. + 19.5 -146. + 18.6 -144. + 17.7 -142. + 16.8 -140. + 15.9 -137. + 15.1 -134. + 14.2 -131. + 13.4 -128. + 12.5 -124. +REG_LINE: 15 + 25.1 -150. + 26.0 -150. + 26.9 -150. + 27.9 -149. + 28.8 -148. + 29.7 -147. + 30.6 -145. + 31.5 -143. + 32.4 -141. + 33.3 -139. + 34.2 -136. + 35.1 -133. + 35.9 -130. + 36.7 -127. + 37.6 -123. +REG_LINE: 15 + 37.6 -123. + 38.4 -119. + 39.1 -115. + 39.9 -111. + 40.6 -106. + 41.4 -101. + 42.1 -96.2 + 42.7 -91.0 + 43.4 -85.6 + 44.0 -79.9 + 44.6 -74.2 + 45.2 -68.2 + 45.7 -62.1 + 46.2 -55.8 + 46.7 -49.3 +REG_LINE: 15 + 46.7 -49.3 + 47.1 -42.8 + 47.5 -36.1 + 47.9 -29.2 + 48.3 -22.3 + 48.6 -15.3 + 48.9 -8.13 + 49.2 -.928 + 49.4 6.35 + 49.6 13.7 + 49.7 21.1 + 49.8 28.5 + 49.9 36.0 + 50.0 43.4 + 50.0 50.9 +REG_LINE: 15 + 50.0 50.9 + 49.9 58.4 + 49.9 65.9 + 49.8 73.3 + 49.7 80.8 + 49.5 88.1 + 49.3 95.5 + 49.1 103. + 48.8 110. + 48.5 117. + 48.2 124. + 47.8 131. + 47.4 138. + 47.0 144. + 46.6 151. +REG_LINE: 15 + 46.6 151. + 46.1 157. + 45.6 164. + 45.0 170. + 44.4 176. + 43.8 181. + 43.2 187. + 42.6 192. + 41.9 197. + 41.2 202. + 40.5 207. + 39.7 212. + 38.9 216. + 38.2 220. + 37.4 224. +REG_LINE: 15 + 37.4 224. + 36.5 227. + 35.7 231. + 34.8 234. + 34.0 237. + 33.1 239. + 32.2 241. + 31.3 244. + 30.4 245. + 29.5 247. + 28.6 248. + 27.6 249. + 26.7 250. + 25.8 250. + 24.8 250. +REG_LINE: 15 + 24.8 250. + 23.9 250. + 23.0 249. + 22.0 249. + 21.1 248. + 20.2 246. + 19.3 245. + 18.4 243. + 17.5 241. + 16.6 238. + 15.7 236. + 14.8 233. + 14.0 230. + 13.1 226. + 12.3 223. +REG_LINE: 15 + 12.3 223. + 11.5 219. + 10.8 215. + 9.99 210. + 9.25 206. + 8.53 201. + 7.84 196. + 7.17 191. + 6.52 185. + 5.90 180. + 5.31 174. + 4.74 168. + 4.20 162. + 3.70 155. + 3.22 149. +REG_LINE: 15 + 3.22 149. + 2.77 142. + 2.35 136. + 1.96 129. + 1.61 122. + 1.29 115. + 1.00 108. + .747 101. + .526 93.3 + .339 85.9 + .187 78.6 + 0.696E-01 71.1 + -0.132E-01 63.7 + -0.611E-01 56.2 + -0.740E-01 48.7 +REG_LINE: 15 + -0.740E-01 48.7 + -0.519E-01 41.2 + 0.519E-02 33.7 + 0.971E-01 26.3 + .224 18.9 + .385 11.5 + .581 4.18 + .811 -3.08 + 1.07 -10.3 + 1.37 -17.4 + 1.70 -24.4 + 2.06 -31.3 + 2.46 -38.1 + 2.88 -44.7 + 3.34 -51.3 +REG_LINE: 15 + 3.34 -51.3 + 3.83 -57.7 + 4.34 -63.9 + 4.89 -70.0 + 5.46 -75.9 + 6.06 -81.6 + 6.69 -87.2 + 7.34 -92.6 + 8.02 -97.7 + 8.72 -103. + 9.44 -107. + 10.2 -112. + 11.0 -116. + 11.7 -120. + 12.5 -124. +REG_LINE: 15 + 12.5 -124. + 13.4 -128. + 14.2 -131. + 15.1 -134. + 15.9 -137. + 16.8 -140. + 17.7 -142. + 18.6 -144. + 19.5 -146. + 20.4 -147. + 21.3 -148. + 22.3 -149. + 23.2 -150. + 24.1 -150. + 25.1 -150. +REG_LINE: 15 + 25.1 -150. + 26.0 -150. + 26.9 -150. + 27.9 -149. + 28.8 -148. + 29.7 -147. + 30.6 -145. + 31.5 -143. + 32.4 -141. + 33.3 -139. + 34.2 -136. + 35.1 -133. + 35.9 -130. + 36.7 -127. + 37.6 -123. +REG_LINE: 15 + 25.1 -150. + 26.0 -150. + 26.9 -150. + 27.9 -149. + 28.8 -148. + 29.7 -147. + 30.6 -145. + 31.5 -143. + 32.4 -141. + 33.3 -139. + 34.2 -136. + 35.1 -133. + 35.9 -130. + 36.7 -127. + 37.6 -123. +REG_LINE: 15 + 24.9 49.8 + 24.9 57.0 + 24.9 64.1 + 24.9 71.3 + 24.9 78.4 + 24.9 85.6 + 24.9 92.7 + 24.9 99.9 + 24.9 107. + 24.9 114. + 24.9 121. + 24.9 128. + 24.9 136. + 24.9 143. + 24.9 150. +REG_LINE: 15 + 24.9 150. + 24.9 157. + 24.9 164. + 24.9 171. + 24.9 179. + 24.9 186. + 24.9 193. + 24.9 200. + 24.9 207. + 24.8 214. + 24.8 221. + 24.8 229. + 24.8 236. + 24.8 243. + 24.8 250. +REG_LINE: 8 + 24.8 250. + 24.8 257. + 24.8 264. + 24.8 272. + 24.8 279. + 24.8 286. + 24.8 293. + 24.8 300. +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '12' + 24.9 -148. BC -0.654E-01 .998 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '14' + 35.1 -123. BC -.978 .210 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '16' + 44.2 -49.2 BC -.997 0.716E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '18' + 47.5 50.9 BC -1.00 -0.942E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '20' + 44.1 151. TC .997 0.732E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '22' + 34.9 223. TC .977 .213 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0' + 24.9 248. TC -0.236E-01 1.00 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '2' + 14.8 222. TC -.978 .210 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '4' + 5.71 149. TC -.997 0.716E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '6' + 2.43 48.7 BC 1.00 0.288E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '8' + 5.83 -51.1 BC .997 0.732E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '10' + 15.0 -124. BC .977 .215 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-90:00' + 22.4 49.8 BC -1.00 -0.615E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '59' + 22.4 150. BC -1.00 -0.615E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-89:58' + 22.3 250. BC -1.00 -0.615E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 1 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: ' A FITS test' + 30.9 313. BC .000 1.00 +ude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -1 # Forward matrix value + M1 = 1 # Forward matrix value + M2 = -1 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End FrameSet diff --git a/ast_tester/regression.f b/ast_tester/regression.f new file mode 100644 index 0000000..e9ca32e --- /dev/null +++ b/ast_tester/regression.f @@ -0,0 +1,1515 @@ + PROGRAM REGRESSION +*+ +* Name: +* REGRESSION + +* Purpose: +* Tests many aspects of the AST library (Fortran interface). + +* Language: +* Starlink Fortran 77 + +* Type of Module: +* Fortran program + +* Invocation: +* regression + +* Description: +* This application utilizes many aspects of the AST library, producing +* textual output on standard output. The output should be redirected +* to a text file and compared to the output from previous runs to +* detect any changes in functionality. + +* Authors: +* DSB: David Berry (STARLINK) +* {enter_new_authors_here} + +* History: +* 29-JAN-2002 (DSB): +* Original version. +* {enter_further_changes_here} + +*- + +* Type Definitions: + IMPLICIT NONE ! No implicit typing + +* Global Variables: + INTEGER CMN_FTEST ! Which FITS test are we doing? + INTEGER CMN_LINE ! The index of the next header to read + COMMON /REG/ CMN_FTEST, CMN_LINE + +* Global Constants: + INCLUDE 'SAE_PAR' ! Standard SAE constants + INCLUDE 'AST_PAR' ! AST constants and declarations + +* Status: + INTEGER STATUS ! Global status + +* External References: + INTEGER CHR_LEN + + EXTERNAL REG_SOURCE + EXTERNAL REG_SINK + + EXTERNAL REG_ATTR + EXTERNAL REG_FLUSH + EXTERNAL REG_LINE + EXTERNAL REG_MARK + EXTERNAL REG_TEXT + EXTERNAL REG_TXEXT + EXTERNAL REG_CAP + EXTERNAL REG_QCH + EXTERNAL REG_SCALES + +* Local Constants: + INTEGER NFITS_TESTS ! How many FITS tests? + PARAMETER ( NFITS_TESTS = 3 ) + + INTEGER NCAT, NRAT, NLAT, NDAT, NIAT ! Numbers of Attributes of each type + PARAMETER ( NCAT = 4, + : NRAT = 2, + : NLAT = 2, + : NDAT = 1, + : NIAT = 5 ) + +* Local Variables: + CHARACTER ATTRS( NFITS_TESTS )*255 ! Plot attributes for each FITS test + CHARACTER CARDS*(7*80) ! Used fot testing ast_putcards + INTEGER FC, FS, PLOT, I, J, OC + + REAL GBOX( 4 ) ! Area of graphics coords to use + DOUBLE PRECISION BBOX( 4, NFITS_TESTS ) ! Base Frame area to be + ! mapped onto GBOX for each FITS test + + CHARACTER*20 CAT(NCAT), RAT(NRAT), LAT(NLAT), DAT(NDAT), IAT(NIAT) + CHARACTER CV*50 + REAL RV + LOGICAL LV + DOUBLE PRECISION DV + INTEGER IV, VERS, MAJ, MIN, REV + +* Data initialization: + DATA CAT / 'Colour(axis1)', 'Font(Stri)', 'Nout', 'Class' / + DATA RAT / 'Tol', 'Gap(1)' / + DATA LAT / 'Border', 'Invert' / + DATA DAT / 'TextLabGap' / + DATA IAT / 'Nin', 'Current', 'Base', 'Nobject', 'RefCOUNT' / + DATA GBOX /-100.0, -200.0, 150.0, 300.0/ + + + + DATA BBOX / 10.0, -10.0, 290.0, 300.0, + : -300.0, -300.0, 500.0, 500.0, + : 1.0, 1.0, 1787.0, 447.0 / + + + + DATA ATTRS/ 'Grid=1,tickall=0', + : 'Grid=1,labelling=interior', + : 'Grid=0' / + + +*. + +* Initialize inherited global status. + STATUS = SAI__OK + +* Use object caching to minimise allocation of new memory + OC = AST_TUNE( 'ObjectCaching', 1, STATUS ) + IF( OC .NE. 0 ) THEN + WRITE(*,'(A,I2)') 'Default ObjectCaching VALUE is ',OC + END IF + + IF( AST_TUNE( 'ObjectCaching', AST__TUNULL, STATUS ) .NE. 1 ) THEN + WRITE(*,'(A,I2)') 'Set ObjectCaching VALUE is ',OC + END IF + +* Display the AST version number. + VERS = AST_VERSION() + MAJ = VERS/1000000 + VERS = VERS - 1000000*MAJ + MIN = VERS/1000 + REV = VERS - 1000*MIN + WRITE(*,'(A,I2,A,I1,A,I2)') 'AST version ',MAJ,'.',MIN,'-',REV + +* First do a test of the AST_PUTCARDS routine. + FC = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) + + CARDS = 'NAXIS = 1' + CARDS( 81: ) = 'NAXIS1 = 100' + CARDS( 2*80 + 1: ) = 'CTYPE1 = ''fred''' + CARDS( 3*80 + 1: ) = 'CDELT1 = 0.0' + CARDS( 4*80 + 1: ) = 'CRPIX1 = 50' + CARDS( 5*80 + 1: ) = 'CUNIT1 = ''GHz''' + + CALL AST_PUTCARDS( FC, CARDS, STATUS ) + WRITE(*,'(A,I2)') 'PutCards Ncards = ',AST_GETI( FC, 'NCARD', + : STATUS ) + WRITE(*,'(A,I2)') 'PutCards Card = ',AST_GETI( FC, 'CARD', + : STATUS ) + + CALL AST_SETI( FC, 'CARD', 10, STATUS ) + WRITE(*,'(A,I2)') 'PutCards Card = ',AST_GETI( FC, 'CARD', + : STATUS ) + + CALL AST_PUTCARDS( FC, CARDS, STATUS ) + WRITE(*,'(A,I2)') 'PutCards Ncards = ',AST_GETI( FC, 'NCARD', + : STATUS ) + WRITE(*,'(A,I2)') 'PutCards Card = ',AST_GETI( FC, 'CARD', + : STATUS ) + CALL AST_SHOW( FC, STATUS ) + +* We loop round testing several sorts of FITS Headers. + DO I = 1, NFITS_TESTS + IF ( STATUS .NE. SAI__OK ) GO TO 999 + +* Tell the REG_SOURCE function which FITS header to load. + CMN_FTEST = I + CMN_LINE = 1 + + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') ' ' + WRITE(*,'(A,I2)') ' FITS test number ',I + WRITE(*,'(A)') ' ====================' + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') ' ' + +* Create a FitsChan, read an Object from it, and dump the Object +* to standard output. The Object should be a FrameSet if all is OK. + FC = AST_FITSCHAN( REG_SOURCE, REG_SINK, ' ', STATUS ) + FS = AST_READ( FC, STATUS ) + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') 'AST_SHOW:' + CALL AST_SHOW( FS, STATUS ) + +* Annul the FitsChan. This will cause the unused contents (if any) to +* be written out using REG_SINK. + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') 'REG_SINK:' + CALL AST_ANNUL( FC, STATUS ) + +* Create another FrameSet with Native encoding. Write the FrameSet to +* it, and then annul the FitsChan (this will cause the FITS cards to be +* written to stdout). + FC = AST_FITSCHAN( AST_NULL, REG_SINK, 'Encoding=native', + : STATUS ) + WRITE(*,'(A)') ' ' + WRITE(*,'(A,I2)') 'Objects written: ', AST_WRITE( FC, FS, + : STATUS ) + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') 'Native Encoding:' + CALL AST_ANNUL( FC, STATUS ) + +* Create a Plot which maps the area specified by BBOX the Base Frame +* of the FrameSet onto the GBOX area in graphics coords. + PLOT = AST_PLOT( FS, GBOX, BBOX( 1, I), ' grf = 1 , '// + : 'title = A FITS test', STATUS ) + +* Annul the FrameSet. + CALL AST_ANNUL( FS, STATUS ) + +* Tell the Plot to use the REG_... routines included in this file to +* do the drawing. + CALL AST_GRFSET( PLOT, 'Attr', REG_ATTR, STATUS ) + CALL AST_GRFSET( PLOT, 'Flush', REG_FLUSH, STATUS ) + CALL AST_GRFSET( PLOT, 'Line', REG_LINE, STATUS ) + CALL AST_GRFSET( PLOT, 'Mark', REG_MARK, STATUS ) + CALL AST_GRFSET( PLOT, 'Text', REG_TEXT, STATUS ) + CALL AST_GRFSET( PLOT, 'TxExt', REG_TXEXT, STATUS ) + CALL AST_GRFSET( PLOT, 'Scales', REG_SCALES, STATUS ) + CALL AST_GRFSET( PLOT, 'Cap', REG_CAP, STATUS ) + CALL AST_GRFSET( PLOT, 'Qch', REG_QCH, STATUS ) + +* Set some attributes. + CALL AST_SET( PLOT, ATTRS( I ), STATUS ) + +* Get some attributes (separate the AST_GET calls and the WRITEs in order +* to avoid recursive I/O due to the REG_xxx routines trying to write to +* standard output). + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') 'ATTRIBUTES:' + + DO J = 1, NCAT + CV = AST_GETC( PLOT, CAT(J), STATUS ) + WRITE(*,'(A,I10)') ' '//CAT(J)//': ',CHR_LEN(CV) + END DO + + DO J = 1, NRAT + RV = AST_GETR( PLOT, RAT(J), STATUS ) + WRITE(*,'(A,G13.6)') ' '//RAT(J)//': ',RV + END DO + + DO J = 1, NLAT + LV = AST_GETL( PLOT, LAT(J), STATUS ) + IF( LV ) THEN + IV = 1 + ELSE + IV = 0 + END IF + WRITE(*,'(A,I1)') ' '//LAT(J)//': ',IV + END DO + + DO J = 1, NDAT + DV = AST_GETD( PLOT, DAT(J), STATUS ) + WRITE(*,'(A,G13.6)') ' '//DAT(J)//': ',DV + END DO + + DO J = 1, NIAT + IV = AST_GETI( PLOT, IAT(J), STATUS ) + WRITE(*,'(A,I4)') ' '//IAT(J)//': ',IV + END DO + + +* Draw a grid. + WRITE(*,'(A)') ' ' + WRITE(*,'(A)') 'AST_GRID:' + CALL AST_GRID( PLOT, STATUS ) + +* Annul the Plot. + CALL AST_ANNUL( PLOT, STATUS ) + + END DO + + 999 CONTINUE + + END + + + + + +* Grf plotting routines for the Plot tests. These are used in preference +* to the grf routines specified at link time. +* ====================================================================== + +* Flush graphics. +* --------------- + INTEGER FUNCTION REG_FLUSH() + WRITE(*,'(A)') 'REG_FLUSH:' + REG_FLUSH = 1 + END + +* Set or get a Plot graphics attribute. +* ------------------------------------- + INTEGER FUNCTION REG_ATTR( ATT, VAL, OLDVAL, PRIM ) + IMPLICIT NONE + +* Includes: + INCLUDE 'AST_PAR' + INCLUDE 'GRF_PAR' + +* Arguments: + INTEGER ATT + DOUBLE PRECISION VAL + INTEGER PRIM + DOUBLE PRECISION OLDVAL + +* Local Variables: + INTEGER I, J + DOUBLE PRECISION ATTRS( 5, 3 ) + +* Initialization: + DATA ATTRS /15*0.0D0/ + +* Log this call. + WRITE(*,'(I4,1X,G10.3,1X,I4)') 'REG_GATTR: ', ATT, VAL, PRIM + +* Identify the required element. + IF( ATT .EQ. GRF__STYLE ) THEN + I = 1 + ELSE IF( ATT .EQ. GRF__WIDTH ) THEN + I = 2 + ELSE IF( ATT .EQ. GRF__SIZE ) THEN + I = 3 + ELSE IF( ATT .EQ. GRF__FONT ) THEN + I = 4 + ELSE IF( ATT .EQ. GRF__COLOUR ) THEN + I = 5 + ELSE + WRITE(*,'(A,I2)') 'Bad ATT value: ', ATT + END IF + + IF( PRIM .EQ. GRF__LINE ) THEN + J = 1 + ELSE IF( PRIM .EQ. GRF__MARK ) THEN + J = 2 + ELSE IF( PRIM .EQ. GRF__TEXT ) THEN + J = 3 + ELSE + WRITE(*,'(A,I2)') 'Bad PRIM value: ', PRIM + END IF + +* Return the old value. + OLDVAL = ATTRS( I, J ) + +* Store the new value if not bad. + IF( VAL .NE. AST__BAD ) ATTRS( I, J ) = VAL + +* Initialize the returned value to indicate success. + REG_ATTR = 1 + + END + + +* Draw a polyline. +* ---------------- + INTEGER FUNCTION REG_LINE( N, X, Y ) + IMPLICIT NONE + + INTEGER N + REAL X( N ) + REAL Y( N ) + INTEGER I + + WRITE(*,'(A,I4)') 'REG_LINE: ',N + DO I = 1, N + WRITE(*,'(3X,G10.3,1X,G10.3)') X(I),Y(I) + END DO + + REG_LINE = 1 + END + +* Draw a set of markers. +* ---------------------- + INTEGER FUNCTION REG_MARK( N, X, Y, TYPE ) + IMPLICIT NONE + + INTEGER N, TYPE + REAL X( N ) + REAL Y( N ) + INTEGER I + + WRITE(*,'(A,I4,I2)') 'REG_MARK: ', N, TYPE + DO I = 1, N + WRITE(*,'(3X,G10.3,1X,G10.3)') X(I),Y(I) + END DO + + REG_MARK = 1 + END + +* Draw a text string. +* ------------------- + INTEGER FUNCTION REG_TEXT( TEXT, X, Y, JUST, UPX, UPY ) + IMPLICIT NONE + + CHARACTER TEXT*(*), JUST*(*) + REAL X, Y, UPX, UPY + + WRITE(*,'(A,A,A)') 'REG_TEXT: ''', TEXT,'''' + WRITE(*,'(3X,G10.3,1X,G10.3,1X,A,1X,G10.3,1X,G10.3)') + : X, Y, JUST, UPX, UPY + + REG_TEXT = 1 + END + +* Return the extent of a text string. +* +* For some reason, the arguments to this function seem particularly +* prone to random rounding errors, resulting in the regression test +* always failing when run twice in succession, even if not changes +* have been made to the code in plot.c. For this reason this function +* does not write out its argument to standard output. +* -------------------------------------------------------------------- + INTEGER FUNCTION REG_TXEXT( TEXT, X, Y, JUST, UPX, UPY, XB, YB ) + IMPLICIT NONE + + CHARACTER TEXT*(*), JUST*(*) + REAL X, Y, UPX, UPY, XB(4), YB(4) + +c WRITE(*,*) 'REG_TXEXT: ''', TEXT,'''' +c WRITE(*,*) ' ', X, Y, ' ''', JUST,''' ', UPX, UPY + + XB( 1 ) = X - LEN( TEXT )*0.5 + XB( 2 ) = X + LEN( TEXT )*0.5 + XB( 3 ) = XB( 2 ) + XB( 4 ) = XB( 1 ) + + YB( 1 ) = Y - 0.5 + YB( 2 ) = YB( 1 ) + YB( 3 ) = Y + 0.5 + YB( 4 ) = YB( 3 ) + + REG_TXEXT = 1 + END + +* Inquire a capability +* --------------------- + INTEGER FUNCTION REG_CAP( CAP, VALUE ) + IMPLICIT NONE + + INCLUDE 'GRF_PAR' + + INTEGER CAP, VALUE + + WRITE(*,'(A,I2)') 'REG_CAP: ', CAP + + REG_CAP = 0 + IF( CAP .EQ. GRF__SCALES ) REG_CAP = 1 + + END + +* Inquire axis scales +* --------------------- + INTEGER FUNCTION REG_SCALES( ALPHA, BETA ) + IMPLICIT NONE + REAL ALPHA, BETA + + WRITE(*,'(A)') 'REG_SCALES: ' + + ALPHA = 1.0 + BETA = 1.0 + + REG_SCALES = 1 + + END + +* Inquire character size +* ---------------------- + INTEGER FUNCTION REG_QCH( CHV, CHH ) + IMPLICIT NONE + REAL CHV, CHH + + WRITE(*,'(A)') 'REG_QCH: ' + + CHV = 0.01 + CHH = 0.01 + + REG_QCH = 1 + + END + + + +* A Sink funtion for use with the FitsChan class. It writes the FitsChan +* contents to standard output. +* ====================================================================== + SUBROUTINE REG_SINK( CARD, STATUS ) + IMPLICIT NONE + CHARACTER CARD*80 + INTEGER STATUS + WRITE(*,'(A)') CARD + END + + + +* A Source funtion for use with the FitsChan class. It returns a different +* header for each value of REG_FTEST. +* ====================================================================== + INTEGER FUNCTION REG_SOURCE( CARD, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + + INTEGER CMN_FTEST ! Which FITS test are we doing? + INTEGER CMN_LINE ! The index of the next header to read + COMMON /REG/ CMN_FTEST, CMN_LINE + + CHARACTER CARD*80 + INTEGER STATUS + +* Check the inherited status + REG_SOURCE = 0 + IF( STATUS .NE. SAI__OK ) RETURN + +* Assume more cards will be returned. + REG_SOURCE = 1 + +* The following code defines the FITS headers and is generated automatically +* from FITS header files using script make_regtest (in the AST development +* system).... + + +* FITS headers from cobe.head (Tue Jan 29 13:37:07 2002) + IF( CMN_FTEST .EQ. 1 ) THEN + IF( CMN_LINE .EQ. 1 ) THEN + CARD = 'SIMPLE = T / Written by I'// + : 'DL: 30-Jul-1997 05:35:42.00' + ELSE IF( CMN_LINE .EQ. 2 ) THEN + CARD = 'BITPIX = -32 / Bits per pix'// + : 'el.' + ELSE IF( CMN_LINE .EQ. 3 ) THEN + CARD = 'NAXIS = 2 / Number of di'// + : 'mensions' + ELSE IF( CMN_LINE .EQ. 4 ) THEN + CARD = 'NAXIS1 = 300 / Length of x '// + : 'axis.' + ELSE IF( CMN_LINE .EQ. 5 ) THEN + CARD = 'NAXIS2 = 300 / Length of y '// + : 'axis.' + ELSE IF( CMN_LINE .EQ. 6 ) THEN + CARD = 'CTYPE1 = ''GLON-ZEA'' / X-axis typ'// + : 'e' + ELSE IF( CMN_LINE .EQ. 7 ) THEN + CARD = 'CTYPE2 = ''GLAT-ZEA'' / Y-axis typ'// + : 'e' + ELSE IF( CMN_LINE .EQ. 8 ) THEN + CARD = 'CRVAL1 = -149.56866 / Reference pi'// + : 'xel value' + ELSE IF( CMN_LINE .EQ. 9 ) THEN + CARD = 'CRVAL2 = -19.758201 / Reference pi'// + : 'xel value' + ELSE IF( CMN_LINE .EQ. 10 ) THEN + CARD = 'CRPIX1 = 150.500 / Reference pi'// + : 'xel' + ELSE IF( CMN_LINE .EQ. 11 ) THEN + CARD = 'CRPIX2 = 150.500 / Reference pi'// + : 'xel' + ELSE IF( CMN_LINE .EQ. 12 ) THEN + CARD = 'CDELT1 = -1.20000 / Degrees/pixe'// + : 'l' + ELSE IF( CMN_LINE .EQ. 13 ) THEN + CARD = 'CDELT2 = 1.20000 / Degrees/pixe'// + : 'l' + ELSE IF( CMN_LINE .EQ. 14 ) THEN + CARD = 'CROTA1 = 0.00000 / Rotation in '// + : 'degrees.' + ELSE IF( CMN_LINE .EQ. 15 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 16 ) THEN + CARD = 'COMMENT This file was produced by the SkyView'// + : ' survey analysis system from' + ELSE IF( CMN_LINE .EQ. 17 ) THEN + CARD = 'COMMENT available astronomical surveys. The '// + : 'data are formatted' + ELSE IF( CMN_LINE .EQ. 18 ) THEN + CARD = 'COMMENT as a simple two-dimensional FITS imag'// + : 'e with the same units as' + ELSE IF( CMN_LINE .EQ. 19 ) THEN + CARD = 'COMMENT the orginal survey. A single ASCII t'// + : 'able extension may be present' + ELSE IF( CMN_LINE .EQ. 20 ) THEN + CARD = 'COMMENT which describes catalog objects found'// + : ' within the field of view.' + ELSE IF( CMN_LINE .EQ. 21 ) THEN + CARD = 'COMMENT Copies of relevant copyright notices '// + : 'are included in this file.' + ELSE IF( CMN_LINE .EQ. 22 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 23 ) THEN + CARD = 'COMMENT Questions should be directed to:' + ELSE IF( CMN_LINE .EQ. 24 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 25 ) THEN + CARD = 'COMMENT scollick@skyview.gsfc.nasa.gov' + ELSE IF( CMN_LINE .EQ. 26 ) THEN + CARD = 'COMMENT or' + ELSE IF( CMN_LINE .EQ. 27 ) THEN + CARD = 'COMMENT mcglynn@grossc.gsfc.nasa.gov' + ELSE IF( CMN_LINE .EQ. 28 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 29 ) THEN + CARD = 'COMMENT SkyView' + ELSE IF( CMN_LINE .EQ. 30 ) THEN + CARD = 'COMMENT Code 668.1' + ELSE IF( CMN_LINE .EQ. 31 ) THEN + CARD = 'COMMENT Goddard Space Flight Center, Gree'// + : 'nbelt, MD 20771' + ELSE IF( CMN_LINE .EQ. 32 ) THEN + CARD = 'COMMENT 301-286-7780' + ELSE IF( CMN_LINE .EQ. 33 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 34 ) THEN + CARD = 'COMMENT SkyView is supported by NASA ADP gran'// + : 't NAS 5-32068.' + ELSE IF( CMN_LINE .EQ. 35 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 36 ) THEN + CARD = 'SURVEY = ''COBE DIRBE''' + ELSE IF( CMN_LINE .EQ. 37 ) THEN + CARD = 'BUNITS = ''MJy/sr '' /' + ELSE IF( CMN_LINE .EQ. 38 ) THEN + CARD = 'ORIGIN = ''CDAC '' / Cosmology '// + : 'Data Analysis Center' + ELSE IF( CMN_LINE .EQ. 39 ) THEN + CARD = 'TELESCOP= ''COBE '' / COsmic Bac'// + : 'kground Explorer satellite' + ELSE IF( CMN_LINE .EQ. 40 ) THEN + CARD = 'INSTRUME= ''DIRBE '' / COBE instr'// + : 'ument [DIRBE, DMR, FIRAS]' + ELSE IF( CMN_LINE .EQ. 41 ) THEN + CARD = 'PIXRESOL= 9 / Quad tree pi'// + : 'xel resolution [6, 9]' + ELSE IF( CMN_LINE .EQ. 42 ) THEN + CARD = 'DATE = ''27/09/94'' / FITS file '// + : 'creation date (dd/mm/yy)' + ELSE IF( CMN_LINE .EQ. 43 ) THEN + CARD = 'DATE-MAP= ''16/09/94'' / Date of or'// + : 'iginal file creation (dd/mm/yy)' + ELSE IF( CMN_LINE .EQ. 44 ) THEN + CARD = 'COMMENT COBE specific keywords' + ELSE IF( CMN_LINE .EQ. 45 ) THEN + CARD = 'DATE-BEG= ''08/12/89'' / date of in'// + : 'itial data represented (dd/mm/yy)' + ELSE IF( CMN_LINE .EQ. 46 ) THEN + CARD = 'DATE-END= ''25/09/90'' / date of fi'// + : 'nal data represented (dd/mm/yy)' + ELSE IF( CMN_LINE .EQ. 47 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 48 ) THEN + CARD = 'COMMENT THE COBE DIRBE map is a combination o'// + : 'f the original ten' + ELSE IF( CMN_LINE .EQ. 49 ) THEN + CARD = 'COMMENT band passes with the following wavele'// + : 'ngths:' + ELSE IF( CMN_LINE .EQ. 50 ) THEN + CARD = 'COMMENT Band 1 - 1.25 microns' + ELSE IF( CMN_LINE .EQ. 51 ) THEN + CARD = 'COMMENT Band 2 - 2.2 microns' + ELSE IF( CMN_LINE .EQ. 52 ) THEN + CARD = 'COMMENT Band 3 - 3.5 microns' + ELSE IF( CMN_LINE .EQ. 53 ) THEN + CARD = 'COMMENT Band 4 - 4.9 microns' + ELSE IF( CMN_LINE .EQ. 54 ) THEN + CARD = 'COMMENT Band 5 - 12 microns' + ELSE IF( CMN_LINE .EQ. 55 ) THEN + CARD = 'COMMENT Band 6 - 25 microns' + ELSE IF( CMN_LINE .EQ. 56 ) THEN + CARD = 'COMMENT Band 7 - 60 microns' + ELSE IF( CMN_LINE .EQ. 57 ) THEN + CARD = 'COMMENT Band 8 - 100 microns' + ELSE IF( CMN_LINE .EQ. 58 ) THEN + CARD = 'COMMENT Band 9 - 140 microns' + ELSE IF( CMN_LINE .EQ. 59 ) THEN + CARD = 'COMMENT Band 10 - 240 microns' + ELSE IF( CMN_LINE .EQ. 60 ) THEN + CARD = 'COMMENT' + ELSE IF( CMN_LINE .EQ. 61 ) THEN + CARD = 'END' + REG_SOURCE = 0 + ELSE + REG_SOURCE = 0 + END IF + +* FITS headers from polco.head (Tue Jan 29 15:06:35 2002) + ELSE IF( CMN_FTEST .EQ. 2 ) THEN + IF( CMN_LINE .EQ. 1 ) THEN + CARD = 'COMMENT AST +++++++++++++++++++++++++++++++++'// + : '+++++++++++++++++++++++++++++++' + ELSE IF( CMN_LINE .EQ. 2 ) THEN + CARD = 'AST' + ELSE IF( CMN_LINE .EQ. 3 ) THEN + CARD = 'COMMENT AST Beginning of AST data '// + : 'for FrameSet object' + ELSE IF( CMN_LINE .EQ. 4 ) THEN + CARD = 'AST' + ELSE IF( CMN_LINE .EQ. 5 ) THEN + CARD = 'COMMENT AST .................................'// + : '...............................' + ELSE IF( CMN_LINE .EQ. 6 ) THEN + CARD = 'AST' + ELSE IF( CMN_LINE .EQ. 7 ) THEN + CARD = 'BEGAST_A= ''FrameSet'' / Set of int'// + : 'er-related coordinate systems' + ELSE IF( CMN_LINE .EQ. 8 ) THEN + CARD = 'NFRAME_A= 2 / Number of Fr'// + : 'ames in FrameSet' + ELSE IF( CMN_LINE .EQ. 9 ) THEN + CARD = 'CURRNT_A= 2 / Index of cur'// + : 'rent Frame' + ELSE IF( CMN_LINE .EQ. 10 ) THEN + CARD = 'NOD1_A = 2 / Frame 1 is a'// + : 'ssociated with node 2' + ELSE IF( CMN_LINE .EQ. 11 ) THEN + CARD = 'NOD2_A = 1 / Frame 2 is a'// + : 'ssociated with node 1' + ELSE IF( CMN_LINE .EQ. 12 ) THEN + CARD = 'LNK2_A = 1 / Node 2 is de'// + : 'rived from node 1' + ELSE IF( CMN_LINE .EQ. 13 ) THEN + CARD = 'FRM1_A = '' '' / Frame numb'// + : 'er 1' + ELSE IF( CMN_LINE .EQ. 14 ) THEN + CARD = 'BEGAST_B= ''Frame '' / Coordinate'// + : ' system description' + ELSE IF( CMN_LINE .EQ. 15 ) THEN + CARD = 'TITLE_A = ''Data grid indices; first pixel at'// + : ' (1&''/ Title of coordinate system' + ELSE IF( CMN_LINE .EQ. 16 ) THEN + CARD = 'CONTINUE '',1) ''' + ELSE IF( CMN_LINE .EQ. 17 ) THEN + CARD = 'NAXES_A = 2 / Number of co'// + : 'ordinate axes' + ELSE IF( CMN_LINE .EQ. 18 ) THEN + CARD = 'DOMAIN_A= ''GRID '' / Coordinate'// + : ' system domain' + ELSE IF( CMN_LINE .EQ. 19 ) THEN + CARD = 'AX1_A = '' '' / Axis numbe'// + : 'r 1' + ELSE IF( CMN_LINE .EQ. 20 ) THEN + CARD = 'BEGAST_C= ''Axis '' / Coordinate'// + : ' axis' + ELSE IF( CMN_LINE .EQ. 21 ) THEN + CARD = 'LABEL_A = ''Data grid index 1'' / Axis Label' + ELSE IF( CMN_LINE .EQ. 22 ) THEN + CARD = 'SYMBOL_A= ''g1 '' / Axis symbo'// + : 'l' + ELSE IF( CMN_LINE .EQ. 23 ) THEN + CARD = 'UNIT_A = ''pixel '' / Axis units' + ELSE IF( CMN_LINE .EQ. 24 ) THEN + CARD = 'FORMAT_A= ''%3.1f '' / Format spe'// + : 'cifier' + ELSE IF( CMN_LINE .EQ. 25 ) THEN + CARD = 'ENDAST_A= ''Axis '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 26 ) THEN + CARD = 'AX2_A = '' '' / Axis numbe'// + : 'r 2' + ELSE IF( CMN_LINE .EQ. 27 ) THEN + CARD = 'BEGAST_D= ''Axis '' / Coordinate'// + : ' axis' + ELSE IF( CMN_LINE .EQ. 28 ) THEN + CARD = 'LABEL_B = ''Data grid index 2'' / Axis Label' + ELSE IF( CMN_LINE .EQ. 29 ) THEN + CARD = 'SYMBOL_B= ''g2 '' / Axis symbo'// + : 'l' + ELSE IF( CMN_LINE .EQ. 30 ) THEN + CARD = 'UNIT_B = ''pixel '' / Axis units' + ELSE IF( CMN_LINE .EQ. 31 ) THEN + CARD = 'FORMAT_B= ''%3.1f '' / Format spe'// + : 'cifier' + ELSE IF( CMN_LINE .EQ. 32 ) THEN + CARD = 'ENDAST_B= ''Axis '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 33 ) THEN + CARD = 'ENDAST_C= ''Frame '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 34 ) THEN + CARD = 'FRM2_A = '' '' / Frame numb'// + : 'er 2' + ELSE IF( CMN_LINE .EQ. 35 ) THEN + CARD = 'BEGAST_E= ''Frame '' / Coordinate'// + : ' system description' + ELSE IF( CMN_LINE .EQ. 36 ) THEN + CARD = 'TITLE_B = ''Pixel coordinates; first pixel at'// + : ' (-&''/ Title of coordinate system' + ELSE IF( CMN_LINE .EQ. 37 ) THEN + CARD = 'CONTINUE ''100.5,-200.5)''' + ELSE IF( CMN_LINE .EQ. 38 ) THEN + CARD = 'NAXES_B = 2 / Number of co'// + : 'ordinate axes' + ELSE IF( CMN_LINE .EQ. 39 ) THEN + CARD = 'DOMAIN_B= ''POLAR '' / Coordinate'// + : ' system domain' + ELSE IF( CMN_LINE .EQ. 40 ) THEN + CARD = 'AX1_B = '' '' / Axis numbe'// + : 'r 1' + ELSE IF( CMN_LINE .EQ. 41 ) THEN + CARD = 'BEGAST_F= ''Axis '' / Coordinate'// + : ' axis' + ELSE IF( CMN_LINE .EQ. 42 ) THEN + CARD = 'LABEL_C = ''Pixel coordinate 1'' / Axis Label' + ELSE IF( CMN_LINE .EQ. 43 ) THEN + CARD = 'SYMBOL_C= ''p1 '' / Axis symbo'// + : 'l' + ELSE IF( CMN_LINE .EQ. 44 ) THEN + CARD = 'UNIT_C = ''pixel '' / Axis units' + ELSE IF( CMN_LINE .EQ. 45 ) THEN + CARD = 'FORMAT_C= ''%3.1f '' / Format spe'// + : 'cifier' + ELSE IF( CMN_LINE .EQ. 46 ) THEN + CARD = 'ENDAST_D= ''Axis '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 47 ) THEN + CARD = 'AX2_B = '' '' / Axis numbe'// + : 'r 2' + ELSE IF( CMN_LINE .EQ. 48 ) THEN + CARD = 'BEGAST_G= ''Axis '' / Coordinate'// + : ' axis' + ELSE IF( CMN_LINE .EQ. 49 ) THEN + CARD = 'LABEL_D = ''Pixel coordinate 2'' / Axis Label' + ELSE IF( CMN_LINE .EQ. 50 ) THEN + CARD = 'SYMBOL_D= ''p2 '' / Axis symbo'// + : 'l' + ELSE IF( CMN_LINE .EQ. 51 ) THEN + CARD = 'UNIT_D = ''pixel '' / Axis units' + ELSE IF( CMN_LINE .EQ. 52 ) THEN + CARD = 'FORMAT_D= ''%3.1f '' / Format spe'// + : 'cifier' + ELSE IF( CMN_LINE .EQ. 53 ) THEN + CARD = 'ENDAST_E= ''Axis '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 54 ) THEN + CARD = 'ENDAST_F= ''Frame '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 55 ) THEN + CARD = 'MAP2_A = '' '' / Mapping be'// + : 'tween nodes 1 and 2' + ELSE IF( CMN_LINE .EQ. 56 ) THEN + CARD = 'BEGAST_H= ''CmpMap '' / Compound M'// + : 'apping' + ELSE IF( CMN_LINE .EQ. 57 ) THEN + CARD = 'NIN_A = 2 / Number of in'// + : 'put coordinates' + ELSE IF( CMN_LINE .EQ. 58 ) THEN + CARD = 'ISA_A = ''Mapping '' / Mapping be'// + : 'tween coordinate systems' + ELSE IF( CMN_LINE .EQ. 59 ) THEN + CARD = 'INVA_A = 1 / First Mappin'// + : 'g used in inverse direction' + ELSE IF( CMN_LINE .EQ. 60 ) THEN + CARD = 'INVB_A = 1 / Second Mappi'// + : 'ng used in inverse direction' + ELSE IF( CMN_LINE .EQ. 61 ) THEN + CARD = 'MAPA_A = '' '' / First comp'// + : 'onent Mapping' + ELSE IF( CMN_LINE .EQ. 62 ) THEN + CARD = 'BEGAST_I= ''MathMap '' / Transforma'// + : 'tion using mathematical functions' + ELSE IF( CMN_LINE .EQ. 63 ) THEN + CARD = 'NIN_B = 2 / Number of in'// + : 'put coordinates' + ELSE IF( CMN_LINE .EQ. 64 ) THEN + CARD = 'INVERT_A= 0 / Mapping not '// + : 'inverted' + ELSE IF( CMN_LINE .EQ. 65 ) THEN + CARD = 'ISA_B = ''Mapping '' / Mapping be'// + : 'tween coordinate systems' + ELSE IF( CMN_LINE .EQ. 66 ) THEN + CARD = 'FWD1_A = ''r=sqrt(x*x+y*y)'' / Forward fu'// + : 'nction 1' + ELSE IF( CMN_LINE .EQ. 67 ) THEN + CARD = 'FWD2_A = ''theta=atan2(y,x)'' / Forward fu'// + : 'nction 2' + ELSE IF( CMN_LINE .EQ. 68 ) THEN + CARD = 'INV1_A = ''x=r*cos(theta)'' / Inverse fu'// + : 'nction 1' + ELSE IF( CMN_LINE .EQ. 69 ) THEN + CARD = 'INV2_A = ''y=r*sin(theta)'' / Inverse fu'// + : 'nction 2' + ELSE IF( CMN_LINE .EQ. 70 ) THEN + CARD = 'SIMPFI_A= 1 / Forward-inve'// + : 'rse pairs may simplify' + ELSE IF( CMN_LINE .EQ. 71 ) THEN + CARD = 'SIMPIF_A= 1 / Inverse-forw'// + : 'ard pairs may simplify' + ELSE IF( CMN_LINE .EQ. 72 ) THEN + CARD = 'ENDAST_G= ''MathMap '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 73 ) THEN + CARD = 'MAPB_A = '' '' / Second com'// + : 'ponent Mapping' + ELSE IF( CMN_LINE .EQ. 74 ) THEN + CARD = 'BEGAST_J= ''WinMap '' / Map one wi'// + : 'ndow on to another' + ELSE IF( CMN_LINE .EQ. 75 ) THEN + CARD = 'NIN_C = 2 / Number of in'// + : 'put coordinates' + ELSE IF( CMN_LINE .EQ. 76 ) THEN + CARD = 'INVERT_B= 0 / Mapping not '// + : 'inverted' + ELSE IF( CMN_LINE .EQ. 77 ) THEN + CARD = 'ISA_C = ''Mapping '' / Mapping be'// + : 'tween coordinate systems' + ELSE IF( CMN_LINE .EQ. 78 ) THEN + CARD = 'SFT1_A = -101.5 / Shift for ax'// + : 'is 1' + ELSE IF( CMN_LINE .EQ. 79 ) THEN + CARD = 'SFT2_A = -201.5 / Shift for ax'// + : 'is 2' + ELSE IF( CMN_LINE .EQ. 80 ) THEN + CARD = 'ENDAST_H= ''WinMap '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 81 ) THEN + CARD = 'ENDAST_I= ''CmpMap '' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 82 ) THEN + CARD = 'ENDAST_J= ''FrameSet'' / End of obj'// + : 'ect definition' + ELSE IF( CMN_LINE .EQ. 83 ) THEN + CARD = 'COMMENT AST .................................'// + : '...............................' + ELSE IF( CMN_LINE .EQ. 84 ) THEN + CARD = 'AST' + ELSE IF( CMN_LINE .EQ. 85 ) THEN + CARD = 'COMMENT AST End of AST data for'// + : ' FrameSet object' + ELSE IF( CMN_LINE .EQ. 86 ) THEN + CARD = 'AST' + ELSE IF( CMN_LINE .EQ. 87 ) THEN + CARD = 'COMMENT AST ---------------------------------'// + : '-------------------------------' + ELSE IF( CMN_LINE .EQ. 88 ) THEN + CARD = 'AST' + REG_SOURCE = 0 + ELSE + REG_SOURCE = 0 + END IF + + +* FITS headers from scp.head (Tue Jan 29 15:17:50 2002) + ELSE IF( CMN_FTEST .EQ. 3 ) THEN + IF( CMN_LINE .EQ. 1 ) THEN + CARD = 'SIMPLE = T / file does co'// + : 'nform to FITS standard' + ELSE IF( CMN_LINE .EQ. 2 ) THEN + CARD = 'BITPIX = 16 / number of bi'// + : 'ts per data pixel' + ELSE IF( CMN_LINE .EQ. 3 ) THEN + CARD = 'NAXIS = 2 / number of da'// + : 'ta axes' + ELSE IF( CMN_LINE .EQ. 4 ) THEN + CARD = 'NAXIS1 = 1787 / length of da'// + : 'ta axis 1' + ELSE IF( CMN_LINE .EQ. 5 ) THEN + CARD = 'NAXIS2 = 447 / length of da'// + : 'ta axis 2' + ELSE IF( CMN_LINE .EQ. 6 ) THEN + CARD = 'EXTEND = T / FITS dataset'// + : ' may contain extensions' + ELSE IF( CMN_LINE .EQ. 7 ) THEN + CARD = 'COMMENT FITS (Flexible Image Transport Syst'// + : 'em) format defined in Astronomy and' + ELSE IF( CMN_LINE .EQ. 8 ) THEN + CARD = 'COMMENT Astrophysics Supplement Series v44/'// + : 'p363, v44/p371, v73/p359, v73/p365.' + ELSE IF( CMN_LINE .EQ. 9 ) THEN + CARD = 'COMMENT Contact the NASA Science Office of '// + : 'Standards and Technology for the' + ELSE IF( CMN_LINE .EQ. 10 ) THEN + CARD = 'COMMENT FITS Definition document #100 and o'// + : 'ther FITS information.' + ELSE IF( CMN_LINE .EQ. 11 ) THEN + CARD = 'PLATENUM= ''3665 '' / Plate numb'// + : 'er' + ELSE IF( CMN_LINE .EQ. 12 ) THEN + CARD = 'EMULSION= ''IIIaJ '' / Kodak emul'// + : 'sion type' + ELSE IF( CMN_LINE .EQ. 13 ) THEN + CARD = 'FILTER = ''GG395 '' / Schott gla'// + : 'ss filter type' + ELSE IF( CMN_LINE .EQ. 14 ) THEN + CARD = 'PLTSCALE= ''67.14 '' / [arcsec/mm'// + : '] plate scale' + ELSE IF( CMN_LINE .EQ. 15 ) THEN + CARD = 'FIELDNUM= ''1 '' / Sky survey'// + : ' field number' + ELSE IF( CMN_LINE .EQ. 16 ) THEN + CARD = 'EPOCH = 1.977780E+03 / Epoch of obs'// + : 'ervation' + ELSE IF( CMN_LINE .EQ. 17 ) THEN + CARD = 'DATE-OBS= ''1977-10-11'' / [yyyy-mm-d'// + : 'd] UT date of observation' + ELSE IF( CMN_LINE .EQ. 18 ) THEN + CARD = 'TELESCOP= ''UKST '' / Telescope '// + : 'on which the plate was taken' + ELSE IF( CMN_LINE .EQ. 19 ) THEN + CARD = 'TELETYPE= ''SCHM '' / Type of te'// + : 'lescope' + ELSE IF( CMN_LINE .EQ. 20 ) THEN + CARD = 'SITELAT = -5.458410576565E-01 / [radians] la'// + : 'titude of telescope' + ELSE IF( CMN_LINE .EQ. 21 ) THEN + CARD = 'SITELONG= 2.601766194458E+00 / [radians] lo'// + : 'ngitude of telescope' + ELSE IF( CMN_LINE .EQ. 22 ) THEN + CARD = 'LST = ''00:20 '' / [hh:mm] lo'// + : 'cal sidereal time at start of obs' + ELSE IF( CMN_LINE .EQ. 23 ) THEN + CARD = 'MJD-OBS = 4.342657300880E+04 / Modified Jul'// + : 'ian Date of observation' + ELSE IF( CMN_LINE .EQ. 24 ) THEN + CARD = 'INSTRUME= ''SuperCOSMOS I'' / Measuring '// + : 'machine' + ELSE IF( CMN_LINE .EQ. 25 ) THEN + CARD = 'DATE-MES= ''2000-11-04'' / [yyyy-mm-d'// + : 'd] Date of this plate measurement' + ELSE IF( CMN_LINE .EQ. 26 ) THEN + CARD = 'RADECSYS= ''FK5 '' / Reference '// + : 'frame for RA/DEC in original file' + ELSE IF( CMN_LINE .EQ. 27 ) THEN + CARD = 'NHKLINES= 146 / Number of li'// + : 'nes from house-keeping file' + ELSE IF( CMN_LINE .EQ. 28 ) THEN + CARD = 'HKLIN001= ''JOB.JOBNO UKJ001'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 29 ) THEN + CARD = 'HKLIN002= ''JOB.DATE-MES 2000:11:'// + : '04'' /' + ELSE IF( CMN_LINE .EQ. 30 ) THEN + CARD = 'HKLIN003= ''JOB.TIME 12:51:09'// + : ''' /' + ELSE IF( CMN_LINE .EQ. 31 ) THEN + CARD = 'HKLIN004= ''JOB.INSTRUME SuperCOS'// + : 'MOS I'' /' + ELSE IF( CMN_LINE .EQ. 32 ) THEN + CARD = 'HKLIN005= ''JOB.ORIGIN Royal Ob'// + : 'servatory Edinburgh'' /' + ELSE IF( CMN_LINE .EQ. 33 ) THEN + CARD = 'HKLIN006= ''JOB.SOFTWARE /home/sc'// + : 'osdev/v033'' /' + ELSE IF( CMN_LINE .EQ. 34 ) THEN + CARD = 'HKLIN007= ''JOB.OPERATOR ebt'' /' + ELSE IF( CMN_LINE .EQ. 35 ) THEN + CARD = 'HKLIN008= ''JOB.USER htm'' /' + ELSE IF( CMN_LINE .EQ. 36 ) THEN + CARD = 'HKLIN009= ''JOB.USERREF NONE'' /' + ELSE IF( CMN_LINE .EQ. 37 ) THEN + CARD = 'HKLIN010= ''JOB.UORIGIN ROE'' /' + ELSE IF( CMN_LINE .EQ. 38 ) THEN + CARD = 'HKLIN011= ''JOB.UCOUNTRY uk'' /' + ELSE IF( CMN_LINE .EQ. 39 ) THEN + CARD = 'HKLIN012= ''JOB.COMMENT Digital '// + : 'catalogue of the Sky'' /' + ELSE IF( CMN_LINE .EQ. 40 ) THEN + CARD = 'HKLIN013= ''JOB.IAM_FILE iam.srt''// + : '' /' + ELSE IF( CMN_LINE .EQ. 41 ) THEN + CARD = 'HKLIN014= ''PLATE.TELESCOP UKST'' /' + ELSE IF( CMN_LINE .EQ. 42 ) THEN + CARD = 'HKLIN015= ''PLATE.TELTYPE SCHM'' /' + ELSE IF( CMN_LINE .EQ. 43 ) THEN + CARD = 'HKLIN016= ''PLATE.PLATE 3665'' /' + ELSE IF( CMN_LINE .EQ. 44 ) THEN + CARD = 'HKLIN017= ''PLATE.MATERIAL 3mm glas'// + : 's'' /' + ELSE IF( CMN_LINE .EQ. 45 ) THEN + CARD = 'HKLIN018= ''PLATE.EMULSION IIIaJ'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 46 ) THEN + CARD = 'HKLIN019= ''PLATE.FILTER GG395'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 47 ) THEN + CARD = 'HKLIN020= ''PLATE.PSCALE 67.14'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 48 ) THEN + CARD = 'HKLIN021= ''PLATE.FIELD 1'' /' + ELSE IF( CMN_LINE .EQ. 49 ) THEN + CARD = 'HKLIN022= ''PLATE.RA_PNT 0'' /' + ELSE IF( CMN_LINE .EQ. 50 ) THEN + CARD = 'HKLIN023= ''PLATE.DEC_PNT -90'' /' + ELSE IF( CMN_LINE .EQ. 51 ) THEN + CARD = 'HKLIN024= ''PLATE.RADECSYS FK4'' /' + ELSE IF( CMN_LINE .EQ. 52 ) THEN + CARD = 'HKLIN025= ''PLATE.EQUINOX 1950'' /' + ELSE IF( CMN_LINE .EQ. 53 ) THEN + CARD = 'HKLIN026= ''PLATE.TIMESYS BESSELIA'// + : 'N'' /' + ELSE IF( CMN_LINE .EQ. 54 ) THEN + CARD = 'HKLIN027= ''PLATE.EPOCH 1977.78''// + : '' /' + ELSE IF( CMN_LINE .EQ. 55 ) THEN + CARD = 'HKLIN028= ''PLATE.EXPOSURE 75'' /' + ELSE IF( CMN_LINE .EQ. 56 ) THEN + CARD = 'HKLIN029= ''PLATE.UTDATE 771011'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 57 ) THEN + CARD = 'HKLIN030= ''PLATE.LST 0020'' /' + ELSE IF( CMN_LINE .EQ. 58 ) THEN + CARD = 'HKLIN031= ''PLATE.MJD 43426.57'// + : '3008796'' /' + ELSE IF( CMN_LINE .EQ. 59 ) THEN + CARD = 'HKLIN032= ''PLATE.TELLAT -0.54584'// + : '105765654'' /' + ELSE IF( CMN_LINE .EQ. 60 ) THEN + CARD = 'HKLIN033= ''PLATE.TELLONG 2.601766'// + : '1944583'' /' + ELSE IF( CMN_LINE .EQ. 61 ) THEN + CARD = 'HKLIN034= ''PLATE.TELHT 1145'' /' + ELSE IF( CMN_LINE .EQ. 62 ) THEN + CARD = 'HKLIN035= ''PLATE.TEMP 273.155''// + : '' /' + ELSE IF( CMN_LINE .EQ. 63 ) THEN + CARD = 'HKLIN036= ''PLATE.ATMOSP 1013.25''// + : '' /' + ELSE IF( CMN_LINE .EQ. 64 ) THEN + CARD = 'HKLIN037= ''PLATE.HUMID 0.5'' /' + ELSE IF( CMN_LINE .EQ. 65 ) THEN + CARD = 'HKLIN038= ''PLATE.WAVE 4500'' /' + ELSE IF( CMN_LINE .EQ. 66 ) THEN + CARD = 'HKLIN039= ''PLATE.TROPL 0.0065'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 67 ) THEN + CARD = 'HKLIN040= ''CALIBRATION.CALTYPE SPLINE'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 68 ) THEN + CARD = 'HKLIN041= ''CALIBRATION.STEPWEDG KPNO'' /' + ELSE IF( CMN_LINE .EQ. 69 ) THEN + CARD = 'HKLIN042= ''CALIBRATION.NSTEPS 8'' /' + ELSE IF( CMN_LINE .EQ. 70 ) THEN + CARD = 'HKLIN043= ''MEASUREMENT.ORIENTAT news'' /' + ELSE IF( CMN_LINE .EQ. 71 ) THEN + CARD = 'HKLIN044= ''MEASUREMENT.EMULPOS UP'' /' + ELSE IF( CMN_LINE .EQ. 72 ) THEN + CARD = 'HKLIN045= ''MEASUREMENT.SCANFILT 14'' /' + ELSE IF( CMN_LINE .EQ. 73 ) THEN + CARD = 'HKLIN046= ''MEASUREMENT.SOSP 552'' /' + ELSE IF( CMN_LINE .EQ. 74 ) THEN + CARD = 'HKLIN047= ''MEASUREMENT.STEPSIZE 10'' /' + ELSE IF( CMN_LINE .EQ. 75 ) THEN + CARD = 'HKLIN048= ''MEASUREMENT.SCANLEN 1152'' /' + ELSE IF( CMN_LINE .EQ. 76 ) THEN + CARD = 'HKLIN049= ''MEASUREMENT.A-XMIN 1622000''// + : '' /' + ELSE IF( CMN_LINE .EQ. 77 ) THEN + CARD = 'HKLIN050= ''MEASUREMENT.A-YMIN 1622000''// + : '' /' + ELSE IF( CMN_LINE .EQ. 78 ) THEN + CARD = 'HKLIN051= ''MEASUREMENT.A-XMAX 33878000'// + : ''' /' + ELSE IF( CMN_LINE .EQ. 79 ) THEN + CARD = 'HKLIN052= ''MEASUREMENT.A-YMAX 33878000'// + : ''' /' + ELSE IF( CMN_LINE .EQ. 80 ) THEN + CARD = 'HKLIN053= ''MEASUREMENT.X_PNT 17500000'// + : ''' /' + ELSE IF( CMN_LINE .EQ. 81 ) THEN + CARD = 'HKLIN054= ''MEASUREMENT.Y_PNT 18000000'// + : ''' /' + ELSE IF( CMN_LINE .EQ. 82 ) THEN + CARD = 'HKLIN055= ''ANALYSIS.NPARAMS 32'' /' + ELSE IF( CMN_LINE .EQ. 83 ) THEN + CARD = 'HKLIN056= ''ANALYSIS.AREACUT 8'' /' + ELSE IF( CMN_LINE .EQ. 84 ) THEN + CARD = 'HKLIN057= ''ANALYSIS.AP-PARAM 1.07'' /' + ELSE IF( CMN_LINE .EQ. 85 ) THEN + CARD = 'HKLIN058= ''DEBLEND.DB-PARAM 1.05'' /' + ELSE IF( CMN_LINE .EQ. 86 ) THEN + CARD = 'HKLIN059= ''DEBLEND.DB-AMIN 16'' /' + ELSE IF( CMN_LINE .EQ. 87 ) THEN + CARD = 'HKLIN060= ''DEBLEND.DB-AMAX 100000'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 88 ) THEN + CARD = 'HKLIN061= ''DEBLEND.DB-ACUT 8'' /' + ELSE IF( CMN_LINE .EQ. 89 ) THEN + CARD = 'HKLIN062= ''DEBLEND.DB-LEVEL 16'' /' + ELSE IF( CMN_LINE .EQ. 90 ) THEN + CARD = 'HKLIN063= ''DEBLEND.SELECT PARENT+C'// + : 'HILD'' /' + ELSE IF( CMN_LINE .EQ. 91 ) THEN + CARD = 'HKLIN064= ''SKY.SKYSQUAR 64'' /' + ELSE IF( CMN_LINE .EQ. 92 ) THEN + CARD = 'HKLIN065= ''SKY.SKYDEFN MEDIAN'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 93 ) THEN + CARD = 'HKLIN066= ''SKY.SKYFILTR bdkjunk''// + : '' /' + ELSE IF( CMN_LINE .EQ. 94 ) THEN + CARD = 'HKLIN067= ''SKY.F-THRESH 8'' /' + ELSE IF( CMN_LINE .EQ. 95 ) THEN + CARD = 'HKLIN068= ''SKY.F-SCLEN 4'' /' + ELSE IF( CMN_LINE .EQ. 96 ) THEN + CARD = 'HKLIN069= ''THRESHOLDING.PCUT 10'' /' + ELSE IF( CMN_LINE .EQ. 97 ) THEN + CARD = 'HKLIN070= ''IAMQC.AREAMIN 8'' /' + ELSE IF( CMN_LINE .EQ. 98 ) THEN + CARD = 'HKLIN071= ''IAMQC.AREAMAX 77346'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 99 ) THEN + CARD = 'HKLIN072= ''IAMQC.MINMAG -30515'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 100 ) THEN + CARD = 'HKLIN073= ''IAMQC.MAXMAG -17954'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 101 ) THEN + CARD = 'HKLIN074= ''IAMQC.MINELL 0.000415'// + : '6232'' /' + ELSE IF( CMN_LINE .EQ. 102 ) THEN + CARD = 'HKLIN075= ''IAMQC.MAXELL 1'' /' + ELSE IF( CMN_LINE .EQ. 103 ) THEN + CARD = 'HKLIN076= ''IAMQC.MODELL 0.14'' /' + ELSE IF( CMN_LINE .EQ. 104 ) THEN + CARD = 'HKLIN077= ''IAMQC.MODOR 91'' /' + ELSE IF( CMN_LINE .EQ. 105 ) THEN + CARD = 'HKLIN078= ''IAMQC.MIDELL 0.21'' /' + ELSE IF( CMN_LINE .EQ. 106 ) THEN + CARD = 'HKLIN079= ''IAMQC.MIDOR 93'' /' + ELSE IF( CMN_LINE .EQ. 107 ) THEN + CARD = 'HKLIN080= ''IAMQC.MEANELL 0.246703'// + : '7'' /' + ELSE IF( CMN_LINE .EQ. 108 ) THEN + CARD = 'HKLIN081= ''IAMQC.MEANOR 91.63474'// + : ''' /' + ELSE IF( CMN_LINE .EQ. 109 ) THEN + CARD = 'HKLIN082= ''IAMQC.NUMOBJ 556985'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 110 ) THEN + CARD = 'HKLIN083= ''IAMQC.PARENTS 486656'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 111 ) THEN + CARD = 'HKLIN084= ''IAMQC.RANGING TRUE'' /' + ELSE IF( CMN_LINE .EQ. 112 ) THEN + CARD = 'HKLIN085= ''IAMQC.LANE_1 15571'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 113 ) THEN + CARD = 'HKLIN086= ''IAMQC.LANE_2 33207'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 114 ) THEN + CARD = 'HKLIN087= ''IAMQC.LANE_3 51478'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 115 ) THEN + CARD = 'HKLIN088= ''IAMQC.LANE_4 69944'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 116 ) THEN + CARD = 'HKLIN089= ''IAMQC.LANE_5 89236'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 117 ) THEN + CARD = 'HKLIN090= ''IAMQC.LANE_6 108416'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 118 ) THEN + CARD = 'HKLIN091= ''IAMQC.LANE_7 127481'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 119 ) THEN + CARD = 'HKLIN092= ''IAMQC.LANE_8 146699'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 120 ) THEN + CARD = 'HKLIN093= ''IAMQC.LANE_9 166380'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 121 ) THEN + CARD = 'HKLIN094= ''IAMQC.LANE_10 186126'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 122 ) THEN + CARD = 'HKLIN095= ''IAMQC.LANE_11 205946'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 123 ) THEN + CARD = 'HKLIN096= ''IAMQC.LANE_12 225915'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 124 ) THEN + CARD = 'HKLIN097= ''IAMQC.LANE_13 245926'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 125 ) THEN + CARD = 'HKLIN098= ''IAMQC.LANE_14 266574'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 126 ) THEN + CARD = 'HKLIN099= ''IAMQC.LANE_15 287150'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 127 ) THEN + CARD = 'HKLIN100= ''IAMQC.LANE_16 308087'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 128 ) THEN + CARD = 'HKLIN101= ''IAMQC.LANE_17 328830'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 129 ) THEN + CARD = 'HKLIN102= ''IAMQC.LANE_18 350253'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 130 ) THEN + CARD = 'HKLIN103= ''IAMQC.LANE_19 370738'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 131 ) THEN + CARD = 'HKLIN104= ''IAMQC.LANE_20 391722'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 132 ) THEN + CARD = 'HKLIN105= ''IAMQC.LANE_21 412801'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 133 ) THEN + CARD = 'HKLIN106= ''IAMQC.LANE_22 433795'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 134 ) THEN + CARD = 'HKLIN107= ''IAMQC.LANE_23 454383'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 135 ) THEN + CARD = 'HKLIN108= ''IAMQC.LANE_24 474711'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 136 ) THEN + CARD = 'HKLIN109= ''IAMQC.LANE_25 495108'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 137 ) THEN + CARD = 'HKLIN110= ''IAMQC.LANE_26 515755'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 138 ) THEN + CARD = 'HKLIN111= ''IAMQC.LANE_27 536499'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 139 ) THEN + CARD = 'HKLIN112= ''IAMQC.LANE_28 556985'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 140 ) THEN + CARD = 'HKLIN113= ''XYTORADEC.STARCAT /sdata/s'// + : 'cos/refcats/tycho2.FIT'' /' + ELSE IF( CMN_LINE .EQ. 141 ) THEN + CARD = 'HKLIN114= ''XYTORADEC.BRIGHTLIM 9'' /' + ELSE IF( CMN_LINE .EQ. 142 ) THEN + CARD = 'HKLIN115= ''XYTORADEC.C-EQUIN 2000'' /' + ELSE IF( CMN_LINE .EQ. 143 ) THEN + CARD = 'HKLIN116= ''XYTORADEC.C-EQTSYS JULIAN'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 144 ) THEN + CARD = 'HKLIN117= ''XYTORADEC.C-EPOCH 2000'' /' + ELSE IF( CMN_LINE .EQ. 145 ) THEN + CARD = 'HKLIN118= ''XYTORADEC.C-EPTSYS JULIAN'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 146 ) THEN + CARD = 'HKLIN119= ''XYTORADEC.R-EQUIN 2000'' /' + ELSE IF( CMN_LINE .EQ. 147 ) THEN + CARD = 'HKLIN120= ''XYTORADEC.R-TSYS JULIAN'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 148 ) THEN + CARD = 'HKLIN121= ''XYTORADEC.MAXITER 5000'' /' + ELSE IF( CMN_LINE .EQ. 149 ) THEN + CARD = 'HKLIN122= ''XYTORADEC.RCRITINI 500000'''// + : ' /' + ELSE IF( CMN_LINE .EQ. 150 ) THEN + CARD = 'HKLIN123= ''XYTORADEC.RCRITABS 50000'' '// + : '/' + ELSE IF( CMN_LINE .EQ. 151 ) THEN + CARD = 'HKLIN124= ''XYTORADEC.RCRITREL 1'' /' + ELSE IF( CMN_LINE .EQ. 152 ) THEN + CARD = 'HKLIN125= ''XYTORADEC.RCRITFIN 3'' /' + ELSE IF( CMN_LINE .EQ. 153 ) THEN + CARD = 'HKLIN126= ''XYTORADEC.HARDCOPY /scos1/s'// + : 'cos/UKJ001/UKJ001.ps'' /' + ELSE IF( CMN_LINE .EQ. 154 ) THEN + CARD = 'HKLIN127= ''XYTORADEC.REFSMULT 5'' /' + ELSE IF( CMN_LINE .EQ. 155 ) THEN + CARD = 'HKLIN128= ''XYTORADEC.RESDMULT 1000'' /' + ELSE IF( CMN_LINE .EQ. 156 ) THEN + CARD = 'HKLIN129= ''XYTORADEC.RACOL RA'' /' + ELSE IF( CMN_LINE .EQ. 157 ) THEN + CARD = 'HKLIN130= ''XYTORADEC.DECOL DEC'' /' + ELSE IF( CMN_LINE .EQ. 158 ) THEN + CARD = 'HKLIN131= ''XYTORADEC.RAPMCOL PMRA'' /' + ELSE IF( CMN_LINE .EQ. 159 ) THEN + CARD = 'HKLIN132= ''XYTORADEC.DECPMCOL PMDE'' /' + ELSE IF( CMN_LINE .EQ. 160 ) THEN + CARD = 'HKLIN133= ''XYTORADEC.PLXCOL NONE'' /' + ELSE IF( CMN_LINE .EQ. 161 ) THEN + CARD = 'HKLIN134= ''XYTORADEC.RVCOL NONE'' /' + ELSE IF( CMN_LINE .EQ. 162 ) THEN + CARD = 'HKLIN135= ''XYTORADEC.MAGCOL VT'' /' + ELSE IF( CMN_LINE .EQ. 163 ) THEN + CARD = 'HKLIN136= ''XYTORADEC.STARSC 2374'' /' + ELSE IF( CMN_LINE .EQ. 164 ) THEN + CARD = 'HKLIN137= ''XYTORADEC.STARSU 1727'' /' + ELSE IF( CMN_LINE .EQ. 165 ) THEN + CARD = 'HKLIN138= ''XYTORADEC.COEFFS_1 17.64034'// + : '3856524'' /' + ELSE IF( CMN_LINE .EQ. 166 ) THEN + CARD = 'HKLIN139= ''XYTORADEC.COEFFS_2 -260.441'// + : '51995641'' /' + ELSE IF( CMN_LINE .EQ. 167 ) THEN + CARD = 'HKLIN140= ''XYTORADEC.COEFFS_3 -163.091'// + : '55572601'' /' + ELSE IF( CMN_LINE .EQ. 168 ) THEN + CARD = 'HKLIN141= ''XYTORADEC.COEFFS_4 17.50423'// + : '0442205'' /' + ELSE IF( CMN_LINE .EQ. 169 ) THEN + CARD = 'HKLIN142= ''XYTORADEC.COEFFS_5 -163.086'// + : '76953832'' /' + ELSE IF( CMN_LINE .EQ. 170 ) THEN + CARD = 'HKLIN143= ''XYTORADEC.COEFFS_6 260.4881'// + : '7907668'' /' + ELSE IF( CMN_LINE .EQ. 171 ) THEN + CARD = 'HKLIN144= ''XYTORADEC.DISTR -0.33333'// + : '333333333'' /' + ELSE IF( CMN_LINE .EQ. 172 ) THEN + CARD = 'HKLIN145= ''XYTORADEC.RA_PNT 0.549249'// + : '96662137'' /' + ELSE IF( CMN_LINE .EQ. 173 ) THEN + CARD = 'HKLIN146= ''XYTORADEC.DEC_PNT -1.56849'// + : '31501781'' /' + ELSE IF( CMN_LINE .EQ. 174 ) THEN + CARD = 'HISTORY = ''SuperCOSMOS image analysis and ma'// + : 'pping mode (IAM and MM)'' /' + ELSE IF( CMN_LINE .EQ. 175 ) THEN + CARD = 'HISTORY = ''data written by xydcomp_ss.'' /' + ELSE IF( CMN_LINE .EQ. 176 ) THEN + CARD = 'HISTORY = ''Any questions/comments/suggestion'// + : 's/bug reports should be sent'' /' + ELSE IF( CMN_LINE .EQ. 177 ) THEN + CARD = 'HISTORY = ''to N.Hambly@roe.ac.uk'' /' + ELSE IF( CMN_LINE .EQ. 178 ) THEN + CARD = 'ASTSIGX = 3.700000E-01 / [arcsec] std'// + : '. dev. of astrometric fit in X' + ELSE IF( CMN_LINE .EQ. 179 ) THEN + CARD = 'ASTSIGY = 3.800000E-01 / [arcsec] std'// + : '. dev. of astrometric fit in Y' + ELSE IF( CMN_LINE .EQ. 180 ) THEN + CARD = 'CRVAL1 = 0.000000000000E+00 / Axis 1 refer'// + : 'ence value' + ELSE IF( CMN_LINE .EQ. 181 ) THEN + CARD = 'CRPIX1 = 8.936318379289E+02 / Axis 1 pixel'// + : ' value' + ELSE IF( CMN_LINE .EQ. 182 ) THEN + CARD = 'CTYPE1 = ''RA---TAN'' / Quantity r'// + : 'epresented by axis 1' + ELSE IF( CMN_LINE .EQ. 183 ) THEN + CARD = 'CRVAL2 = -9.000000018364E+01 / Axis 2 refer'// + : 'ence value' + ELSE IF( CMN_LINE .EQ. 184 ) THEN + CARD = 'CRPIX2 = 2.238380193875E+02 / Axis 2 pixel'// + : ' value' + ELSE IF( CMN_LINE .EQ. 185 ) THEN + CARD = 'CTYPE2 = ''DEC--TAN'' / Quantity r'// + : 'epresented by axis 2' + ELSE IF( CMN_LINE .EQ. 186 ) THEN + CARD = 'CD1_1 = -1.864642639667E-04 / Co-ordinate '// + : 'transformation matrix' + ELSE IF( CMN_LINE .EQ. 187 ) THEN + CARD = 'CD1_2 = -9.188369023766E-07 / Co-ordinate '// + : 'transformation matrix' + ELSE IF( CMN_LINE .EQ. 188 ) THEN + CARD = 'CD2_1 = -1.038232462415E-06 / Co-ordinate '// + : 'transformation matrix' + ELSE IF( CMN_LINE .EQ. 189 ) THEN + CARD = 'CD2_2 = 1.866269837741E-04 / Co-ordinate '// + : 'transformation matrix' + ELSE IF( CMN_LINE .EQ. 190 ) THEN + CARD = 'CDELT1 = -1.864665278217E-04 / DEPRECATED -'// + : ' Increment per pixel on axis 1' + ELSE IF( CMN_LINE .EQ. 191 ) THEN + CARD = 'CDELT2 = 1.866298716692E-04 / DEPRECATED -'// + : ' Increment per pixel on axis 2' + ELSE IF( CMN_LINE .EQ. 192 ) THEN + CARD = 'PC001001= 9.999878591881E-01 / DEPRECATED -'// + : ' Axis rotation matrix' + ELSE IF( CMN_LINE .EQ. 193 ) THEN + CARD = 'PC001002= 4.927623810613E-03 / DEPRECATED -'// + : ' Axis rotation matrix' + ELSE IF( CMN_LINE .EQ. 194 ) THEN + CARD = 'PC002001= -5.563056187788E-03 / DEPRECATED -'// + : ' Axis rotation matrix' + ELSE IF( CMN_LINE .EQ. 195 ) THEN + CARD = 'PC002002= 9.999845260832E-01 / DEPRECATED -'// + : ' Axis rotation matrix' + ELSE IF( CMN_LINE .EQ. 196 ) THEN + CARD = 'CROTA2 = 3.005532298491E-01 / DEPRECATED -'// + : ' rotation of axis 2' + ELSE IF( CMN_LINE .EQ. 197 ) THEN + CARD = 'EQUINOX = 2.000000E+03 / Julian refer'// + : 'ence frame equinox' + ELSE IF( CMN_LINE .EQ. 198 ) THEN + CARD = 'DATATYPE= ''INTEGER*2'' / Type of da'// + : 'ta' + ELSE IF( CMN_LINE .EQ. 199 ) THEN + CARD = 'DATUNITS= ''DENSITY '' / Units: tra'// + : 'nsmission, density or intensity' + ELSE IF( CMN_LINE .EQ. 200 ) THEN + CARD = 'XPIXELSZ= 9.997114974000E+00 / [microns] X '// + : 'pixel size' + ELSE IF( CMN_LINE .EQ. 201 ) THEN + CARD = 'YPIXELSZ= 1.000000000000E+01 / [microns] Y '// + : 'pixel size' + ELSE IF( CMN_LINE .EQ. 202 ) THEN + CARD = 'OBJCTRA = '' 0 0 0.000'' / Centre Rig'// + : 'ht Ascension (J2000)' + ELSE IF( CMN_LINE .EQ. 203 ) THEN + CARD = 'OBJCTDEC= ''-90 0 0.00'' / Centre Dec'// + : 'lination (J2000)' + ELSE IF( CMN_LINE .EQ. 204 ) THEN + CARD = 'OBJCTX = 1.636863183793E+04 / [pixels] Cen'// + : 'tre X on plate' + ELSE IF( CMN_LINE .EQ. 205 ) THEN + CARD = 'OBJCTY = 1.474083801939E+04 / [pixels] Cen'// + : 'tre Y on plate' + ELSE IF( CMN_LINE .EQ. 206 ) THEN + CARD = 'END' + REG_SOURCE = 0 + ELSE + REG_SOURCE = 0 + END IF + + +* Insert new header code here.... (create new header code using script +* "make_regtest" in the AST development archive). + ELSE + REG_SOURCE = 0 + STATUS = SAI__ERROR + WRITE(*,'(A,I2)') 'REG_SOURCE: No such test: ',CMN_FTEST + END IF + + CMN_LINE = CMN_LINE + 1 + + END + + diff --git a/ast_tester/regression.out b/ast_tester/regression.out new file mode 100644 index 0000000..7fcaa9c --- /dev/null +++ b/ast_tester/regression.out @@ -0,0 +1,7630 @@ + Begin FitsChan # I/O channels to FITS files + Card = 1 # Index of current card +# Encod = "NATIVE" # Encoding system +# FitsDg = 15 # No. of digits for floating point values +# DfB1950 = 1 # Default to FK4 B1950 +# CdMat = 0 # Use PC matrix +# CarLin = 0 # Use full FITS-WCS CAR projections +# Iwc = 0 # Do not include an IWC Frame +# Warn = "Tnx Zpx BadCel BadMat BadCTYPE" # Warnings to be reported + Nm1 = "NAXIS" # FITS keyword name + Ty1 = "integer" # FITS keyword data type + Dt1 = 1 # FITS keyword value + Nm2 = "NAXIS1" # FITS keyword name + Ty2 = "integer" # FITS keyword data type + Dt2 = 100 # FITS keyword value + Nm3 = "CTYPE1" # FITS keyword name + Ty3 = "string" # FITS keyword data type + Dt3 = "fred" # FITS keyword value + Nm4 = "CDELT1" # FITS keyword name + Ty4 = "floating point" # FITS keyword data type + Dt4 = 0 # FITS keyword value + Nm5 = "CRPIX1" # FITS keyword name + Ty5 = "integer" # FITS keyword data type + Dt5 = 50 # FITS keyword value + Nm6 = "CUNIT1" # FITS keyword name + Ty6 = "string" # FITS keyword data type + Dt6 = "GHz" # FITS keyword value + Nm7 = " " # FITS keyword name + Ty7 = "comment" # FITS keyword data type + End FitsChan + Begin FrameSet # Set of inter-related coordinate systems +# Title = "IAU (1958) galactic coordinates; zenithal equal area projection" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Epoch = 1950 # Besselian epoch of observation +# Lbl1 = "Galactic longitude" # Label for axis 1 +# Lbl2 = "Galactic latitude" # Label for axis 2 +# System = "GALACTIC" # Coordinate system type +# Uni1 = "degrees" # Units for axis 1 +# Uni2 = "degrees" # Units for axis 2 +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + IsA Frame # Coordinate system description + Nframe = 2 # Number of Frames in FrameSet + Base = 1 # Index of base Frame + Currnt = 2 # Index of current Frame + Lnk2 = 1 # Node 2 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Pixel Coordinates" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Pixel axis 1" # Label for axis 1 +# Lbl2 = "Pixel axis 2" # Label for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel axis 1" # Axis Label + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel axis 2" # Axis Label + End Axis + End Frame + Frm2 = # Frame number 2 + Begin SkyFrame # Description of celestial coordinate system + Ident = " " # Permanent Object identification string + IsA Object # AST Object +# Title = "IAU (1958) galactic coordinates; zenithal equal area projection" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain + Epoch = 1950 # Besselian epoch of observation +# Lbl1 = "Galactic longitude" # Label for axis 1 +# Lbl2 = "Galactic latitude" # Label for axis 2 + System = "GALACTIC" # Coordinate system type +# Uni1 = "degrees" # Units for axis 1 +# Uni2 = "degrees" # Units for axis 2 +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + Proj = "zenithal equal area" # Description of sky projection + SRefIs = "Ignored" # Not rotated (ref. pos. is ignored) + SRef1 = -2.61046557479594 # Ref. pos. l -149.5687 + SRef2 = -0.344845661720836 # Ref. pos. b -19.7582 + End SkyFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -150.5 # Shift for axis 1 + Sft2 = -150.5 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -0.020943951023932 # Forward matrix value + M1 = 0.020943951023932 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "ZEA" # Zenithal equal area projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.291480364581799 # Forward matrix value + M1 = 0.506505471460186 # AST version 4.3- 0 +PutCards Ncards = 7 +PutCards Card = 1 +PutCards Card = 8 +PutCards Ncards = 7 +PutCards Card = 1 + + + + + FITS test number 1 + ==================== + + + +AST_SHOW: + +REG_SINK: +SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 +BITPIX = -32 / Bits per pixel. +NAXIS = 2 / Number of dimensions +NAXIS1 = 300 / Length of x axis. +NAXIS2 = 300 / Length of y axis. +COMMENT +COMMENT This file was produced by the SkyView survey analysis system from +COMMENT available astronomical surveys. The data are formatted +COMMENT as a simple two-dimensional FITS image with the same units as +COMMENT the orginal survey. A single ASCII table extension may be present +COMMENT which describes catalog objects found within the field of view. +COMMENT Copies of relevant copyright notices are included in this file. +COMMENT +COMMENT Questions should be directed to: +COMMENT +COMMENT scollick@skyview.gsfc.nasa.gov +COMMENT or +COMMENT mcglynn@grossc.gsfc.nasa.gov +COMMENT +COMMENT SkyView +COMMENT Code 668.1 +COMMENT Goddard Space Flight Center, Greenbelt, MD 20771 +COMMENT 301-286-7780 +COMMENT +COMMENT SkyView is supported by NASA ADP grant NAS 5-32068. +COMMENT +SURVEY = 'COBE DIRBE' +BUNITS = 'MJy/sr ' +ORIGIN = 'CDAC ' / Cosmology Data Analysis Center +TELESCOP= 'COBE ' / COsmic Background Explorer satellite +INSTRUME= 'DIRBE ' / COBE instrument [DIRBE, DMR, FIRAS] +PIXRESOL= 9 / Quad tree pixel resolution [6, 9] +DATE = '27/09/94' / FITS file creation date (dd/mm/yy) +DATE-MAP= '16/09/94' / Date of original file creation (dd/mm/yy) +COMMENT COBE specific keywords +DATE-BEG= '08/12/89' / date of initial data represented (dd/mm/yy) +DATE-END= '25/09/90' / date of final data represented (dd/mm/yy) +COMMENT +COMMENT THE COBE DIRBE map is a combination of the original ten +COMMENT band passes with the following wavelengths: +COMMENT Band 1 - 1.25 microns +COMMENT Band 2 - 2.2 microns +COMMENT Band 3 - 3.5 microns +COMMENT Band 4 - 4.9 microns +COMMENT Band 5 - 12 microns +COMMENT Band 6 - 25 microns +COMMENT Band 7 - 60 microns +COMMENT Band 8 - 100 microns +COMMENT Band 9 - 140 microns +COMMENT Band 10 - 240 microns +COMMENT + +Objects written: 1 + +Native Encoding: + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST WCS information in AST format AST +COMMENT AST See http://www.starlink.ac.uk/ast/ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +BASE_A = 1 / Index of base Frame +CURRNT_A= 2 / Index of current Frame +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Pixel Coordinates' / Title of coordinate system +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Pixel axis 1' / Axis Label +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Pixel axis 2' / Axis Label +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'SkyFrame' / Description of celestial coordinate system +IDENT_A = '" " ' / Permanent Object identification string +ISA_A = 'Object ' / AST Object +NAXES_B = 2 / Number of coordinate axes +EPOCH_A = 1950.0 / Besselian epoch of observation +SYSTEM_A= 'GALACTIC' / Coordinate system type +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'SkyAxis ' / Celestial coordinate axis +ENDAST_D= 'SkyAxis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'SkyAxis ' / Celestial coordinate axis +ENDAST_E= 'SkyAxis ' / End of object definition +ISA_B = 'Frame ' / Coordinate system description +PROJ_A = 'zenithal equal area'/ Description of sky projection +SREFIS_A= 'Ignored ' / Not rotated (ref. pos. is ignored) +SREF1_A = -2.61046557479594 / Ref. pos. l -149.5687 +SREF2_A = -0.344845661720836 / Ref. pos. b -19.7582 +ENDAST_F= 'SkyFrame' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISSIMP_A= 1 / Mapping has been simplified +ISA_C = 'Mapping ' / Mapping between coordinate systems +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'WinMap ' / Map one window on to another +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_D = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -150.5 / Shift for axis 1 +SFT2_A = -150.5 / Shift for axis 2 +ENDAST_G= 'WinMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'CmpMap ' / Compound Mapping +NIN_C = 2 / Number of input coordinates +ISA_E = 'Mapping ' / Mapping between coordinate systems +MAPA_B = ' ' / First component Mapping +BEGAST_K= 'MatrixMap' / Matrix transformation +NIN_D = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_F = 'Mapping ' / Mapping between coordinate systems +M0_A = -0.020943951023932 / Forward matrix value +M1_A = 0.020943951023932 / Forward matrix value +FORM_A = 'Diagonal' / Matrix storage form +ENDAST_H= 'MatrixMap' / End of object definition +MAPB_B = ' ' / Second component Mapping +BEGAST_L= 'CmpMap ' / Compound Mapping +NIN_E = 2 / Number of input coordinates +ISA_G = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +MAPA_C = ' ' / First component Mapping +BEGAST_M= 'WcsMap ' / FITS-WCS sky projection +NIN_F = 2 / Number of input coordinates +INVERT_C= 1 / Mapping inverted +ISA_H = 'Mapping ' / Mapping between coordinate systems +TYPE_A = 'ZEA ' / Zenithal equal area projection +ENDAST_I= 'WcsMap ' / End of object definition +MAPB_C = ' ' / Second component Mapping +BEGAST_N= 'CmpMap ' / Compound Mapping +NIN_G = 2 / Number of input coordinates +ISA_I = 'Mapping ' / Mapping between coordinate systems +INVA_B = 1 / First Mapping used in inverse direction +MAPA_D = ' ' / First component Mapping +BEGAST_O= 'SphMap ' / Cartesian to Spherical mapping +NIN_H = 3 / Number of input coordinates +NOUT_A = 2 / Number of output coordinates +INVERT_D= 1 / Mapping inverted +ISA_J = 'Mapping ' / Mapping between coordinate systems +UNTRD_A = 1 / All input vectors have unit length +PLRLG_A = 0.0 / Polar longitude (rad.s) +ENDAST_J= 'SphMap ' / End of object definition +MAPB_D = ' ' / Second component Mapping +BEGAST_P= 'CmpMap ' / Compound Mapping +NIN_I = 3 / Number of input coordinates +NOUT_B = 2 / Number of output coordinates +ISA_K = 'Mapping ' / Mapping between coordinate systems +MAPA_E = ' ' / First component Mapping +BEGAST_Q= 'MatrixMap' / Matrix transformation +NIN_J = 3 / Number of input coordinates +INVERT_E= 0 / Mapping not inverted +ISA_L = 'Mapping ' / Mapping between coordinate systems +M0_B = 0.291480364581799 / Forward matrix value +M1_B = 0.506505471460186 / Forward matrix value +M2_A = -0.811474832908671 / Forward matrix value +M3_A = 0.171224898552328 / Forward matrix value +M4_A = -0.862236746712233 / Forward matrix value +M5_A = -0.476686298035564 / Forward matrix value +M6_A = -0.941127638091139 / Forward matrix value +M7_A = 0.0 / Forward matrix value +M8_A = -0.338051429254475 / Forward matrix value +FORM_B = 'Full ' / Matrix storage form +ENDAST_K= 'MatrixMap' / End of object definition +MAPB_E = ' ' / Second component Mapping +BEGAST_R= 'SphMap ' / Cartesian to Spherical mapping +NIN_K = 3 / Number of input coordinates +NOUT_C = 2 / Number of output coordinates +INVERT_F= 0 / Mapping not inverted +ISA_M = 'Mapping ' / Mapping between coordinate systems +UNTRD_B = 1 / All input vectors have unit length +PLRLG_B = -2.61046557479594 / Polar longitude (rad.s) +ENDAST_L= 'SphMap ' / End of object definition +ENDAST_M= 'CmpMap ' / End of object definition +ENDAST_N= 'CmpMap ' / End of object definition +ENDAST_O= 'CmpMap ' / End of object definition +ENDAST_P= 'CmpMap ' / End of object definition +ENDAST_Q= 'CmpMap ' / End of object definition +ENDAST_R= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST + +ATTRIBUTES: + Colour(axis1) : 1 + Font(Stri) : 1 + Nout : 1 + Class : 4 + Tol : 0.100000E-01 + Gap(1) : 1.04720 + Border : 0 + Invert : 0 + TextLabGap : 0.100000E-01 + Nin : 2 + Current : 3 + Base : 1 + Nobject : 1 + RefCOUNT : 1 + +AST_GRID: +REG_LINE: 15 + 25.4 -29.7 + 31.0 -16.7 + 35.7 -2.18 + 39.8 13.5 + 43.0 30.1 + 45.5 47.3 + 47.2 64.8 + 48.1 82.5 + 48.2 99.9 + 47.3 117. + 45.5 133. + 42.5 149. + 38.4 163. + 32.8 175. + 25.4 185. +REG_LINE: 15 + 25.4 -29.7 + 20.1 -16.6 + 15.4 -2.03 + 11.5 13.7 + 8.35 30.3 + 5.92 47.5 + 4.25 65.0 + 3.39 82.6 + 3.35 100. + 4.19 117. + 5.99 133. + 8.83 149. + 12.9 163. + 18.3 175. + 25.4 185. +REG_LINE: 119 + 25.4 -29.7 + 24.6 -29.7 + 23.8 -29.7 + 22.9 -29.6 + 22.1 -29.5 + 21.3 -29.5 + 20.4 -29.3 + 19.6 -29.2 + 18.8 -29.0 + 17.9 -28.9 + 17.1 -28.7 + 16.3 -28.5 + 15.5 -28.2 + 14.6 -28.0 + 13.8 -27.7 + 13.0 -27.4 + 12.2 -27.1 + 11.4 -26.7 + 10.6 -26.4 + 9.74 -26.0 + 8.93 -25.6 + 8.12 -25.2 + 7.32 -24.8 + 6.52 -24.3 + 5.73 -23.9 + 4.93 -23.4 + 4.14 -22.9 + 3.35 -22.3 + 2.57 -21.8 + 1.79 -21.2 + 1.02 -20.6 + .247 -20.0 + -.520 -19.4 + -1.28 -18.8 + -2.04 -18.1 + -2.79 -17.4 + -3.54 -16.7 + -4.29 -16.0 + -5.02 -15.3 + -5.76 -14.5 + -6.49 -13.7 + -7.21 -12.9 + -7.92 -12.1 + -17.3 1.12 + -25.1 17.7 + -31.1 37.2 + -34.6 59.1 + -35.4 82.8 + -33.1 107. + -32.8 109. + -32.5 111. + -32.2 112. + -31.9 114. + -31.5 116. + -31.1 117. + -30.7 119. + -30.3 121. + -29.9 123. + -29.4 124. + -28.9 126. + -28.5 128. + -27.9 129. + -27.4 131. + -26.9 132. + -26.3 134. + -25.7 136. + -25.2 137. + -24.5 139. + -23.9 140. + -23.3 142. + -22.6 143. + -21.9 145. + -21.2 146. + -20.5 148. + -19.8 149. + -19.0 151. + -18.2 152. + -17.5 154. + -16.7 155. + -15.8 156. + -15.0 158. + -14.2 159. + -13.3 160. + -12.4 162. + -11.5 163. + -10.6 164. + -9.69 165. + -8.75 166. + -7.80 167. + -6.83 168. + -5.85 170. + -4.86 171. + -3.85 172. + -2.83 172. + -1.80 173. + -.761 174. + .293 175. + 1.36 176. + 2.43 177. + 3.52 178. + 4.61 178. + 5.72 179. + 6.83 180. + 7.95 180. + 9.08 181. + 10.2 181. + 11.4 182. + 12.5 182. + 13.7 183. + 14.8 183. + 16.0 184. + 17.2 184. + 18.3 184. + 19.5 184. + 20.7 185. + 21.9 185. + 23.1 185. + 24.3 185. + 25.4 185. +REG_LINE: 171 + 25.4 -29.7 + 19.1 -40.9 + 11.9 -49.9 + 11.3 -50.4 + 10.8 -50.9 + 10.2 -51.4 + 9.62 -51.9 + 9.04 -52.4 + 8.46 -52.8 + 7.87 -53.3 + 7.28 -53.7 + 6.68 -54.1 + 6.07 -54.5 + 5.46 -54.9 + 4.84 -55.2 + 4.21 -55.6 + 3.58 -55.9 + 2.95 -56.2 + 2.30 -56.5 + 1.65 -56.7 + .990 -56.9 + .325 -57.2 + -.348 -57.3 + -1.03 -57.5 + -1.71 -57.7 + -2.41 -57.8 + -3.11 -57.9 + -3.82 -57.9 + -4.54 -58.0 + -5.27 -58.0 + -6.00 -58.0 + -6.74 -58.0 + -7.50 -57.9 + -8.26 -57.8 + -9.03 -57.7 + -9.80 -57.5 + -10.6 -57.3 + -11.4 -57.1 + -12.2 -56.8 + -13.0 -56.5 + -13.8 -56.2 + -14.7 -55.8 + -15.5 -55.4 + -16.4 -55.0 + -17.2 -54.5 + -18.1 -53.9 + -19.0 -53.4 + -19.9 -52.7 + -20.8 -52.0 + -21.7 -51.3 + -22.6 -50.5 + -23.6 -49.7 + -24.5 -48.8 + -25.5 -47.8 + -26.4 -46.8 + -27.4 -45.7 + -28.4 -44.5 + -29.4 -43.3 + -30.4 -42.0 + -31.4 -40.6 + -32.4 -39.1 + -33.5 -37.6 + -34.5 -36.0 + -35.5 -34.3 + -36.6 -32.5 + -37.6 -30.5 + -38.7 -28.5 + -39.7 -26.4 + -40.8 -24.2 + -41.8 -21.9 + -42.8 -19.5 + -43.9 -16.9 + -44.9 -14.2 + -45.9 -11.4 + -46.9 -8.53 + -47.8 -5.48 + -48.8 -2.30 + -49.7 1.01 + -50.6 4.45 + -51.4 8.02 + -52.2 11.7 + -53.0 15.6 + -53.7 19.5 + -54.4 23.6 + -55.0 27.8 + -55.5 32.2 + -56.0 36.6 + -56.4 41.1 + -56.7 45.8 + -56.9 50.5 + -57.1 55.3 + -57.1 60.1 + -57.1 65.1 + -56.9 70.0 + -56.7 74.9 + -56.4 79.9 + -55.9 84.9 + -55.4 89.8 + -54.8 94.7 + -54.1 99.5 + -53.3 104. + -52.4 109. + -51.4 114. + -50.3 118. + -49.2 122. + -48.0 127. + -46.7 131. + -45.4 135. + -44.1 138. + -42.7 142. + -41.2 146. + -39.8 149. + -38.3 152. + -36.8 155. + -35.2 158. + -33.7 161. + -32.2 164. + -30.6 166. + -29.1 168. + -27.5 171. + -26.0 173. + -24.5 175. + -23.0 176. + -21.5 178. + -20.0 180. + -18.5 181. + -17.1 182. + -15.7 184. + -14.3 185. + -12.9 186. + -11.5 187. + -10.2 188. + -8.92 188. + -7.64 189. + -6.38 190. + -5.15 190. + -3.94 191. + -2.75 191. + -1.59 192. + -.448 192. + .669 193. + 1.76 193. + 2.83 193. + 3.88 193. + 4.91 193. + 5.92 193. + 6.91 193. + 7.88 193. + 8.82 193. + 9.75 193. + 10.7 193. + 11.6 193. + 12.4 193. + 13.3 192. + 14.1 192. + 14.9 192. + 15.7 192. + 16.5 191. + 17.3 191. + 18.1 190. + 18.8 190. + 19.5 190. + 20.2 189. + 20.9 189. + 21.6 188. + 22.3 188. + 22.9 187. + 23.6 187. + 24.2 186. + 24.8 185. + 25.4 185. +REG_LINE: 171 + 25.4 -29.7 + 31.6 -41.1 + 38.7 -50.2 + 39.2 -50.7 + 39.8 -51.2 + 40.3 -51.8 + 40.9 -52.3 + 41.5 -52.8 + 42.0 -53.2 + 42.6 -53.7 + 43.2 -54.1 + 43.8 -54.6 + 44.4 -55.0 + 45.0 -55.4 + 45.6 -55.7 + 46.2 -56.1 + 46.8 -56.4 + 47.4 -56.7 + 48.1 -57.0 + 48.7 -57.3 + 49.4 -57.6 + 50.0 -57.8 + 50.7 -58.0 + 51.3 -58.2 + 52.0 -58.4 + 52.7 -58.5 + 53.4 -58.6 + 54.1 -58.7 + 54.8 -58.8 + 55.5 -58.8 + 56.2 -58.9 + 57.0 -58.8 + 57.7 -58.8 + 58.5 -58.7 + 59.2 -58.6 + 60.0 -58.5 + 60.8 -58.3 + 61.6 -58.2 + 62.4 -57.9 + 63.2 -57.7 + 64.0 -57.4 + 64.8 -57.0 + 65.6 -56.6 + 66.5 -56.2 + 67.3 -55.8 + 68.2 -55.2 + 69.1 -54.7 + 70.0 -54.1 + 70.9 -53.4 + 71.8 -52.7 + 72.7 -52.0 + 73.7 -51.2 + 74.6 -50.3 + 75.6 -49.4 + 76.6 -48.4 + 77.5 -47.3 + 78.5 -46.2 + 79.5 -45.0 + 80.5 -43.7 + 81.6 -42.4 + 82.6 -41.0 + 83.6 -39.5 + 84.7 -37.9 + 85.7 -36.2 + 86.8 -34.4 + 87.8 -32.5 + 88.9 -30.5 + 90.0 -28.5 + 91.1 -26.3 + 92.1 -23.9 + 93.2 -21.5 + 94.2 -19.0 + 95.3 -16.3 + 96.3 -13.5 + 97.3 -10.6 + 98.4 -7.51 + 99.3 -4.31 + 100. -.976 + 101. 2.50 + 102. 6.12 + 103. 9.88 + 104. 13.8 + 105. 17.8 + 105. 22.0 + 106. 26.3 + 106. 30.7 + 107. 35.2 + 107. 39.9 + 108. 44.7 + 108. 49.5 + 108. 54.4 + 108. 59.4 + 108. 64.5 + 108. 69.5 + 108. 74.6 + 107. 79.7 + 107. 84.8 + 106. 89.9 + 106. 94.9 + 105. 99.9 + 104. 105. + 103. 110. + 102. 114. + 101. 119. + 100. 123. + 98.8 128. + 97.5 132. + 96.1 136. + 94.7 140. + 93.3 143. + 91.8 147. + 90.3 150. + 88.8 154. + 87.2 157. + 85.7 159. + 84.1 162. + 82.5 165. + 80.9 167. + 79.4 170. + 77.8 172. + 76.3 174. + 74.7 176. + 73.2 177. + 71.7 179. + 70.2 181. + 68.8 182. + 67.3 183. + 65.9 185. + 64.5 186. + 63.2 187. + 61.8 188. + 60.5 188. + 59.2 189. + 57.9 190. + 56.7 191. + 55.5 191. + 54.3 192. + 53.1 192. + 51.9 192. + 50.8 193. + 49.7 193. + 48.6 193. + 47.6 193. + 46.5 193. + 45.5 194. + 44.5 194. + 43.6 194. + 42.6 194. + 41.7 193. + 40.8 193. + 39.9 193. + 39.0 193. + 38.2 193. + 37.3 193. + 36.5 192. + 35.7 192. + 34.9 192. + 34.2 191. + 33.4 191. + 32.7 191. + 31.9 190. + 31.2 190. + 30.5 189. + 29.8 189. + 29.2 188. + 28.5 188. + 27.9 187. + 27.3 187. + 26.6 186. + 26.0 185. + 25.4 185. +REG_LINE: 119 + 25.4 -29.7 + 26.3 -29.7 + 27.1 -29.7 + 28.0 -29.7 + 28.8 -29.6 + 29.6 -29.5 + 30.5 -29.4 + 31.3 -29.3 + 32.1 -29.2 + 33.0 -29.0 + 33.8 -28.8 + 34.6 -28.6 + 35.4 -28.4 + 36.3 -28.2 + 37.1 -27.9 + 37.9 -27.6 + 38.7 -27.3 + 39.5 -27.0 + 40.4 -26.7 + 41.2 -26.3 + 42.0 -25.9 + 42.8 -25.5 + 43.6 -25.1 + 44.4 -24.7 + 45.2 -24.2 + 46.0 -23.7 + 46.8 -23.2 + 47.6 -22.7 + 48.4 -22.2 + 49.2 -21.6 + 49.9 -21.1 + 50.7 -20.5 + 51.5 -19.8 + 52.2 -19.2 + 53.0 -18.6 + 53.8 -17.9 + 54.5 -17.2 + 55.3 -16.5 + 56.0 -15.8 + 56.8 -15.0 + 57.5 -14.3 + 58.2 -13.5 + 58.9 -12.7 + 68.4 .462 + 76.3 17.0 + 82.3 36.5 + 85.9 58.6 + 86.8 82.4 + 84.5 107. + 84.2 109. + 83.9 110. + 83.5 112. + 83.2 114. + 82.8 116. + 82.4 117. + 82.0 119. + 81.6 121. + 81.2 122. + 80.7 124. + 80.2 126. + 79.7 128. + 79.2 129. + 78.7 131. + 78.1 132. + 77.6 134. + 77.0 136. + 76.4 137. + 75.8 139. + 75.1 140. + 74.5 142. + 73.8 144. + 73.1 145. + 72.4 147. + 71.7 148. + 70.9 150. + 70.2 151. + 69.4 152. + 68.6 154. + 67.8 155. + 67.0 157. + 66.1 158. + 65.3 159. + 64.4 160. + 63.5 162. + 62.6 163. + 61.7 164. + 60.8 165. + 59.8 166. + 58.8 168. + 57.9 169. + 56.9 170. + 55.9 171. + 54.9 172. + 53.8 173. + 52.8 174. + 51.8 175. + 50.7 175. + 49.6 176. + 48.5 177. + 47.4 178. + 46.3 178. + 45.2 179. + 44.1 180. + 43.0 180. + 41.8 181. + 40.7 182. + 39.6 182. + 38.4 182. + 37.2 183. + 36.1 183. + 34.9 184. + 33.7 184. + 32.6 184. + 31.4 184. + 30.2 185. + 29.0 185. + 27.8 185. + 26.6 185. + 25.4 185. +REG_LINE: 15 + 25.4 -29.7 + 31.0 -16.7 + 35.7 -2.18 + 39.8 13.5 + 43.0 30.1 + 45.5 47.3 + 47.2 64.8 + 48.1 82.5 + 48.2 99.9 + 47.3 117. + 45.5 133. + 42.5 149. + 38.4 163. + 32.8 175. + 25.4 185. +REG_LINE: 197 + 52.0 -19.4 + 51.8 -18.1 + 51.5 -16.9 + 51.2 -15.6 + 50.9 -14.4 + 50.5 -13.2 + 50.1 -12.1 + 49.6 -11.0 + 49.1 -9.89 + 48.6 -8.84 + 48.0 -7.82 + 47.5 -6.84 + 46.8 -5.89 + 46.2 -4.97 + 45.5 -4.09 + 44.8 -3.25 + 44.1 -2.44 + 43.4 -1.67 + 42.6 -.931 + 41.8 -.233 + 41.0 .428 + 40.2 1.05 + 39.3 1.64 + 38.5 2.19 + 37.6 2.70 + 36.7 3.17 + 35.8 3.61 + 34.9 4.01 + 34.0 4.37 + 33.0 4.69 + 32.1 4.97 + 31.2 5.22 + 30.2 5.42 + 29.2 5.59 + 28.3 5.72 + 27.3 5.82 + 26.3 5.87 + 25.4 5.89 + 24.4 5.87 + 23.4 5.81 + 22.5 5.71 + 21.5 5.57 + 20.5 5.40 + 19.6 5.18 + 18.6 4.93 + 17.7 4.64 + 16.8 4.31 + 15.9 3.95 + 14.9 3.55 + 14.0 3.10 + 13.2 2.63 + 12.3 2.11 + 11.4 1.55 + 10.6 .962 + 9.77 .333 + 8.97 -.334 + 8.18 -1.04 + 7.42 -1.78 + 6.68 -2.56 + 5.96 -3.37 + 5.27 -4.22 + 4.60 -5.10 + 3.96 -6.02 + 3.35 -6.98 + 2.77 -7.97 + 2.22 -8.99 + 1.70 -10.1 + 1.22 -11.1 + .767 -12.3 + .353 -13.4 + -0.240E-01 -14.6 + -.362 -15.8 + -.660 -17.1 + -.917 -18.3 + -1.13 -19.6 + -1.30 -20.9 + -1.42 -22.3 + -1.50 -23.6 + -1.53 -25.0 + -1.51 -26.4 + -1.44 -27.8 + -1.32 -29.3 + -1.15 -30.7 + -.921 -32.2 + -.640 -33.6 + -.302 -35.1 + 0.909E-01 -36.5 + .541 -37.9 + 1.05 -39.4 + 1.61 -40.8 + 2.24 -42.2 + 2.92 -43.6 + 3.65 -44.9 + 4.45 -46.3 + 5.30 -47.5 + 6.20 -48.8 + 7.16 -50.0 + 8.17 -51.1 + 9.23 -52.2 + 10.3 -53.3 + 11.5 -54.2 + 12.7 -55.1 + 13.9 -55.9 + 15.2 -56.7 + 16.5 -57.3 + 17.9 -57.9 + 19.2 -58.4 + 20.6 -58.7 + 22.0 -59.0 + 23.4 -59.2 + 24.8 -59.3 + 26.3 -59.3 + 27.7 -59.2 + 29.1 -59.0 + 30.5 -58.7 + 31.9 -58.3 + 33.2 -57.8 + 34.6 -57.2 + 35.9 -56.6 + 37.1 -55.8 + 38.4 -55.0 + 39.6 -54.1 + 40.7 -53.1 + 41.8 -52.1 + 42.9 -51.0 + 43.9 -49.8 + 44.8 -48.6 + 45.7 -47.4 + 46.6 -46.1 + 47.4 -44.7 + 48.1 -43.4 + 48.8 -42.0 + 49.4 -40.6 + 49.9 -39.2 + 50.4 -37.7 + 50.9 -36.3 + 51.2 -34.8 + 51.6 -33.4 + 51.9 -31.9 + 52.1 -30.5 + 52.2 -29.1 + 52.3 -27.6 + 52.4 -26.2 + 52.4 -24.8 + 52.4 -23.4 + 52.3 -22.1 + 52.2 -20.7 + 52.0 -19.4 + 51.8 -18.1 + 51.5 -16.9 + 51.2 -15.6 + 50.9 -14.4 + 50.5 -13.2 + 50.1 -12.1 + 49.6 -11.0 + 49.1 -9.89 + 48.6 -8.84 + 48.0 -7.82 + 47.5 -6.84 + 46.8 -5.89 + 46.2 -4.97 + 45.5 -4.09 + 44.8 -3.25 + 44.1 -2.44 + 43.4 -1.67 + 42.6 -.931 + 41.8 -.233 + 41.0 .428 + 40.2 1.05 + 39.3 1.64 + 38.5 2.19 + 37.6 2.70 + 36.7 3.17 + 35.8 3.61 + 34.9 4.01 + 34.0 4.37 + 33.0 4.69 + 32.1 4.97 + 31.2 5.22 + 30.2 5.42 + 29.2 5.59 + 28.3 5.72 + 27.3 5.82 + 26.3 5.87 + 25.4 5.89 + 24.4 5.87 + 23.4 5.81 + 22.5 5.71 + 21.5 5.57 + 20.5 5.40 + 19.6 5.18 + 18.6 4.93 + 17.7 4.64 + 16.8 4.31 + 15.9 3.95 + 14.9 3.55 + 14.0 3.10 +REG_LINE: 197 + 73.9 11.1 + 73.1 13.1 + 72.3 14.9 + 71.5 16.8 + 70.6 18.5 + 69.7 20.2 + 68.7 21.8 + 67.7 23.3 + 66.6 24.8 + 65.5 26.3 + 64.4 27.7 + 63.2 29.0 + 62.0 30.2 + 60.7 31.4 + 59.5 32.6 + 58.2 33.7 + 56.9 34.7 + 55.5 35.7 + 54.2 36.7 + 52.8 37.5 + 51.4 38.4 + 49.9 39.2 + 48.5 39.9 + 47.0 40.6 + 45.5 41.2 + 44.0 41.8 + 42.5 42.3 + 41.0 42.8 + 39.5 43.3 + 37.9 43.7 + 36.4 44.0 + 34.8 44.3 + 33.2 44.6 + 31.7 44.8 + 30.1 44.9 + 28.5 45.0 + 26.9 45.1 + 25.3 45.1 + 23.7 45.1 + 22.2 45.0 + 20.6 44.9 + 19.0 44.7 + 17.4 44.5 + 15.9 44.3 + 14.3 44.0 + 12.8 43.6 + 11.2 43.2 + 9.68 42.8 + 8.15 42.3 + 6.65 41.7 + 5.15 41.1 + 3.67 40.5 + 2.20 39.8 + .758 39.0 + -.671 38.2 + -2.08 37.4 + -3.47 36.5 + -4.83 35.6 + -6.17 34.6 + -7.49 33.5 + -8.78 32.4 + -10.0 31.3 + -11.3 30.0 + -12.5 28.8 + -13.6 27.5 + -14.8 26.1 + -15.9 24.6 + -16.9 23.1 + -17.9 21.6 + -18.9 19.9 + -19.8 18.2 + -20.7 16.5 + -21.6 14.7 + -22.3 12.8 + -23.1 10.8 + -23.7 8.81 + -24.4 6.71 + -24.9 4.55 + -25.4 2.30 + -25.8 -0.953E-02 + -26.1 -2.40 + -26.4 -4.86 + -26.6 -7.41 + -26.7 -10.0 + -26.7 -12.7 + -26.6 -15.5 + -26.4 -18.3 + -26.0 -21.3 + -25.6 -24.2 + -25.0 -27.3 + -24.4 -30.4 + -23.5 -33.6 + -22.6 -36.8 + -21.4 -40.1 + -20.2 -43.4 + -18.7 -46.7 + -17.1 -49.9 + -15.3 -53.2 + -13.3 -56.4 + -11.1 -59.6 + -8.73 -62.7 + -6.17 -65.6 + -3.42 -68.3 + -.492 -70.9 + 2.61 -73.3 + 5.87 -75.3 + 9.27 -77.1 + 12.8 -78.6 + 16.4 -79.7 + 20.1 -80.5 + 23.8 -80.8 + 27.6 -80.8 + 31.3 -80.4 + 35.0 -79.6 + 38.6 -78.4 + 42.1 -76.9 + 45.5 -75.1 + 48.8 -72.9 + 51.8 -70.6 + 54.7 -67.9 + 57.5 -65.2 + 60.0 -62.2 + 62.3 -59.1 + 64.5 -56.0 + 66.4 -52.7 + 68.2 -49.5 + 69.8 -46.2 + 71.2 -42.9 + 72.5 -39.6 + 73.6 -36.3 + 74.6 -33.1 + 75.4 -30.0 + 76.0 -26.8 + 76.6 -23.8 + 77.0 -20.8 + 77.3 -17.9 + 77.5 -15.1 + 77.6 -12.3 + 77.5 -9.63 + 77.4 -7.03 + 77.3 -4.50 + 77.0 -2.04 + 76.6 .337 + 76.2 2.64 + 75.7 4.87 + 75.2 7.03 + 74.5 9.11 + 73.9 11.1 + 73.1 13.1 + 72.3 14.9 + 71.5 16.8 + 70.6 18.5 + 69.7 20.2 + 68.7 21.8 + 67.7 23.3 + 66.6 24.8 + 65.5 26.3 + 64.4 27.7 + 63.2 29.0 + 62.0 30.2 + 60.7 31.4 + 59.5 32.6 + 58.2 33.7 + 56.9 34.7 + 55.5 35.7 + 54.2 36.7 + 52.8 37.5 + 51.4 38.4 + 49.9 39.2 + 48.5 39.9 + 47.0 40.6 + 45.5 41.2 + 44.0 41.8 + 42.5 42.3 + 41.0 42.8 + 39.5 43.3 + 37.9 43.7 + 36.4 44.0 + 34.8 44.3 + 33.2 44.6 + 31.7 44.8 + 30.1 44.9 + 28.5 45.0 + 26.9 45.1 + 25.3 45.1 + 23.7 45.1 + 22.2 45.0 + 20.6 44.9 + 19.0 44.7 + 17.4 44.5 + 15.9 44.3 + 14.3 44.0 + 12.8 43.6 + 11.2 43.2 + 9.68 42.8 + 8.15 42.3 + 6.65 41.7 +REG_LINE: 197 + 85.9 58.6 + 84.7 60.1 + 83.5 61.6 + 82.2 63.1 + 80.8 64.4 + 79.5 65.8 + 78.1 67.0 + 76.7 68.3 + 75.2 69.4 + 73.7 70.6 + 72.2 71.6 + 70.7 72.7 + 69.2 73.6 + 67.6 74.6 + 66.0 75.5 + 64.4 76.3 + 62.7 77.2 + 61.1 77.9 + 59.4 78.7 + 57.7 79.3 + 56.0 80.0 + 54.3 80.6 + 52.5 81.2 + 50.8 81.7 + 49.0 82.2 + 47.2 82.7 + 45.5 83.1 + 43.7 83.5 + 41.8 83.8 + 40.0 84.1 + 38.2 84.4 + 36.4 84.7 + 34.5 84.9 + 32.7 85.0 + 30.9 85.1 + 29.0 85.2 + 27.2 85.3 + 25.3 85.3 + 23.5 85.3 + 21.6 85.2 + 19.8 85.1 + 17.9 85.0 + 16.1 84.8 + 14.2 84.6 + 12.4 84.4 + 10.6 84.1 + 8.78 83.8 + 6.97 83.4 + 5.17 83.0 + 3.39 82.6 + 1.61 82.2 + -.156 81.6 + -1.91 81.1 + -3.65 80.5 + -5.37 79.9 + -7.08 79.2 + -8.77 78.6 + -10.4 77.8 + -12.1 77.0 + -13.7 76.2 + -15.3 75.4 + -16.9 74.4 + -18.5 73.5 + -20.1 72.5 + -21.6 71.5 + -23.1 70.4 + -24.5 69.3 + -26.0 68.1 + -27.4 66.9 + -28.8 65.6 + -30.1 64.2 + -31.5 62.9 + -32.8 61.4 + -34.0 59.9 + -35.2 58.4 + -36.4 56.7 + -37.6 55.0 + -38.7 53.3 + -39.7 51.4 + -40.7 49.5 + -41.7 47.5 + -42.6 45.4 + -43.5 43.2 + -44.3 40.9 + -45.0 38.5 + -45.7 36.0 + -46.4 33.4 + -46.9 30.6 + -47.4 27.6 + -47.7 24.5 + -48.0 21.3 + -48.2 17.8 + -48.3 14.1 + -48.2 10.1 + -47.9 5.89 + -47.5 1.37 + -46.9 -3.46 + -46.0 -8.66 + -44.9 -14.2 + -43.4 -20.2 + -41.5 -26.7 + -39.1 -33.6 + -36.1 -40.9 + -32.4 -48.6 + -27.8 -56.6 + -22.3 -64.7 + -15.8 -72.6 + -8.06 -79.8 + .764 -86.0 + 10.5 -90.4 + 20.9 -92.6 + 31.5 -92.5 + 41.9 -89.9 + 51.5 -85.2 + 60.2 -78.8 + 67.7 -71.4 + 74.1 -63.5 + 79.5 -55.4 + 83.9 -47.5 + 87.5 -39.8 + 90.4 -32.5 + 92.7 -25.7 + 94.5 -19.3 + 96.0 -13.4 + 97.1 -7.87 + 97.9 -2.73 + 98.5 2.06 + 98.9 6.53 + 99.1 10.7 + 99.2 14.6 + 99.1 18.3 + 98.9 21.7 + 98.6 25.0 + 98.2 28.1 + 97.7 31.0 + 97.2 33.8 + 96.5 36.4 + 95.8 38.9 + 95.1 41.3 + 94.3 43.6 + 93.4 45.7 + 92.5 47.8 + 91.5 49.8 + 90.5 51.7 + 89.4 53.5 + 88.3 55.3 + 87.1 57.0 + 85.9 58.6 + 84.7 60.1 + 83.5 61.6 + 82.2 63.1 + 80.8 64.4 + 79.5 65.8 + 78.1 67.0 + 76.7 68.3 + 75.2 69.4 + 73.7 70.6 + 72.2 71.6 + 70.7 72.7 + 69.2 73.6 + 67.6 74.6 + 66.0 75.5 + 64.4 76.3 + 62.7 77.2 + 61.1 77.9 + 59.4 78.7 + 57.7 79.3 + 56.0 80.0 + 54.3 80.6 + 52.5 81.2 + 50.8 81.7 + 49.0 82.2 + 47.2 82.7 + 45.5 83.1 + 43.7 83.5 + 41.8 83.8 + 40.0 84.1 + 38.2 84.4 + 36.4 84.7 + 34.5 84.9 + 32.7 85.0 + 30.9 85.1 + 29.0 85.2 + 27.2 85.3 + 25.3 85.3 + 23.5 85.3 + 21.6 85.2 + 19.8 85.1 + 17.9 85.0 + 16.1 84.8 + 14.2 84.6 + 12.4 84.4 + 10.6 84.1 + 8.78 83.8 + 6.97 83.4 + 5.17 83.0 + 3.39 82.6 +REG_LINE: 145 + 82.9 115. + 63.7 120. + 40.9 123. + 16.6 123. + -6.79 121. + -27.2 116. + -28.5 116. + -29.8 116. + -31.0 115. + -32.2 115. + -33.4 115. + -34.5 114. + -35.7 114. + -36.7 114. + -37.8 114. + -38.8 113. + -39.8 113. + -40.7 113. + -41.6 113. + -42.5 113. + -43.3 113. + -44.1 113. + -44.8 113. + -45.5 114. + -46.1 114. + -46.7 114. + -47.2 115. + -47.7 116. + -48.1 117. + -48.4 118. + -48.6 119. + -48.7 121. + -48.6 123. + -48.4 125. + -48.0 128. + -47.4 132. + -46.4 136. + -45.0 141. + -43.0 147. + -40.2 154. + -36.3 163. + -30.8 173. + -23.1 184. + -12.5 196. + -11.6 197. + -10.7 198. + -9.78 198. + -8.85 199. + -7.90 200. + -6.94 201. + -5.96 201. + -4.96 202. + -3.94 203. + -2.91 203. + -1.87 204. + -.804 205. + .274 205. + 1.37 206. + 2.47 207. + 3.60 207. + 4.73 208. + 5.88 208. + 7.04 209. + 8.21 209. + 9.40 209. + 10.6 210. + 11.8 210. + 13.0 211. + 14.2 211. + 15.5 211. + 16.7 211. + 17.9 212. + 19.2 212. + 20.4 212. + 21.7 212. + 23.0 212. + 24.2 212. + 25.5 212. + 26.8 212. + 28.0 212. + 29.3 212. + 30.5 212. + 31.8 212. + 33.0 212. + 34.3 211. + 35.5 211. + 36.7 211. + 38.0 211. + 39.2 210. + 40.4 210. + 41.6 209. + 42.8 209. + 43.9 209. + 45.1 208. + 46.2 208. + 47.4 207. + 48.5 206. + 49.6 206. + 50.7 205. + 51.8 205. + 65.1 194. + 75.3 183. + 82.6 171. + 87.9 162. + 91.6 153. + 94.3 146. + 96.1 140. + 97.5 135. + 98.4 131. + 99.0 128. + 99.3 125. + 99.5 122. + 99.5 121. + 99.4 119. + 99.2 118. + 98.9 116. + 98.5 116. + 98.1 115. + 97.5 114. + 97.0 114. + 96.3 114. + 95.6 113. + 94.9 113. + 94.1 113. + 93.3 113. + 92.4 113. + 91.5 113. + 90.5 113. + 89.5 113. + 88.5 114. + 87.5 114. + 86.4 114. + 85.3 114. + 84.1 115. + 82.9 115. + 81.7 115. + 80.5 116. + 79.2 116. + 77.9 116. + 76.6 117. + 75.3 117. + 73.9 117. + 52.6 122. + 28.8 124. + 4.68 123. +REG_LINE: 145 + 61.4 165. + 60.9 164. + 60.3 163. + 59.7 163. + 59.0 163. + 58.4 162. + 57.6 162. + 56.9 161. + 56.1 161. + 55.3 161. + 54.5 160. + 53.6 160. + 52.7 160. + 51.8 160. + 50.9 159. + 49.9 159. + 48.9 159. + 47.9 159. + 46.9 159. + 45.9 159. + 44.8 158. + 43.8 158. + 42.7 158. + 41.6 158. + 40.5 158. + 39.4 158. + 38.2 158. + 37.1 158. + 35.9 158. + 19.4 158. + 3.82 159. + 2.81 159. + 1.81 159. + .836 159. + -.119 159. + -1.05 160. + -1.96 160. + -2.85 160. + -3.71 160. + -4.54 161. + -5.35 161. + -6.12 161. + -6.86 162. + -7.57 162. + -8.25 163. + -8.88 163. + -9.48 164. + -10.0 164. + -10.6 165. + -11.0 165. + -11.5 166. + -11.8 167. + -12.2 167. + -12.4 168. + -12.6 169. + -12.8 170. + -12.9 171. + -12.9 172. + -12.9 173. + -12.7 174. + -12.5 175. + -12.3 176. + -11.9 177. + -11.4 178. + -10.9 180. + -10.2 181. + -9.43 183. + -8.55 184. + -7.56 186. + -6.44 187. + -5.21 189. + -3.85 190. + -2.36 192. + -.742 193. + 1.01 195. + 2.88 196. + 4.88 197. + 7.00 199. + 9.23 200. + 11.6 201. + 14.0 202. + 16.5 202. + 19.1 203. + 21.7 203. + 24.3 203. + 27.0 203. + 29.6 203. + 32.2 203. + 34.8 202. + 37.3 202. + 39.7 201. + 42.0 200. + 44.2 198. + 46.3 197. + 48.3 196. + 50.2 194. + 51.9 193. + 53.5 191. + 55.0 190. + 56.3 188. + 57.5 187. + 58.6 185. + 59.6 184. + 60.4 182. + 61.2 181. + 61.8 180. + 62.4 178. + 62.8 177. + 63.2 176. + 63.5 175. + 63.7 173. + 63.8 172. + 63.8 171. + 63.8 170. + 63.7 170. + 63.5 169. + 63.3 168. + 63.0 167. + 62.7 166. + 62.3 166. + 61.9 165. + 61.4 165. + 60.9 164. + 60.3 163. + 59.7 163. + 59.0 163. + 58.4 162. + 57.6 162. + 56.9 161. + 56.1 161. + 55.3 161. + 54.5 160. + 53.6 160. + 52.7 160. + 51.8 160. + 50.9 159. + 49.9 159. + 48.9 159. + 47.9 159. + 46.9 159. + 45.9 159. + 44.8 158. + 43.8 158. + 27.7 158. + 11.4 158. +REG_LINE: 2 + 80.7 64.6 + 80.8 66.3 +REG_LINE: 2 + 75.0 69.6 + 75.1 71.4 +REG_LINE: 2 + 68.8 73.9 + 68.8 75.6 +REG_LINE: 2 + 62.2 77.4 + 62.2 79.2 +REG_LINE: 2 + 55.3 80.3 + 55.3 82.0 +REG_LINE: 2 + 40.8 84.0 + 40.8 85.8 +REG_LINE: 2 + 33.3 85.0 + 33.3 86.7 +REG_LINE: 2 + 25.8 85.3 + 25.8 87.0 +REG_LINE: 2 + 18.2 85.0 + 18.2 86.8 +REG_LINE: 2 + 10.7 84.1 + 10.7 85.9 +REG_LINE: 2 + -3.79 80.5 + -3.83 82.2 +REG_LINE: 2 + -10.7 77.7 + -10.8 79.4 +REG_LINE: 2 + -17.3 74.2 + -17.4 76.0 +REG_LINE: 2 + -23.6 70.0 + -23.6 71.8 +REG_LINE: 2 + -29.4 65.0 + -29.5 66.8 +REG_LINE: 2 + -39.3 52.2 + -39.4 54.0 +REG_LINE: 2 + -43.2 44.0 + -43.4 45.7 +REG_LINE: 2 + -46.2 34.0 + -46.4 35.8 +REG_LINE: 2 + -48.0 21.8 + -48.3 23.5 +REG_LINE: 2 + -48.0 6.25 + -48.3 7.96 +REG_LINE: 2 + -35.8 -41.5 + -36.5 -39.9 +REG_LINE: 2 + -14.6 -73.9 + -15.8 -72.6 +REG_LINE: 2 + 23.6 -92.8 + 22.6 -94.3 +REG_LINE: 2 + 62.8 -76.5 + 64.1 -75.2 +REG_LINE: 2 + 85.5 -44.2 + 86.2 -42.6 +REG_LINE: 2 + 98.7 4.70 + 99.1 6.41 +REG_LINE: 2 + 99.0 20.6 + 99.3 22.3 +REG_LINE: 2 + 97.3 33.1 + 97.5 34.8 +REG_LINE: 2 + 94.4 43.2 + 94.6 44.9 +REG_LINE: 2 + 90.5 51.6 + 90.7 53.3 +REG_LINE: 2 + 80.7 64.6 + 80.8 66.3 +REG_LINE: 2 + 75.0 69.6 + 75.1 71.4 +REG_LINE: 2 + 68.8 73.9 + 68.8 75.6 +REG_LINE: 2 + 62.2 77.4 + 62.2 79.2 +REG_LINE: 2 + 55.3 80.3 + 55.3 82.0 +REG_LINE: 2 + 40.8 84.0 + 40.8 85.8 +REG_LINE: 2 + 33.3 85.0 + 33.3 86.7 +REG_LINE: 2 + 25.8 85.3 + 25.8 87.0 +REG_LINE: 2 + 18.2 85.0 + 18.2 86.8 +REG_LINE: 2 + 10.7 84.1 + 10.7 85.9 +REG_LINE: 2 + 23.1 -34.3 + 21.6 -33.5 +REG_LINE: 2 + 20.6 -38.6 + 19.1 -37.7 +REG_LINE: 2 + 18.0 -42.6 + 16.5 -41.7 +REG_LINE: 2 + 15.2 -46.2 + 13.8 -45.2 +REG_LINE: 2 + 12.3 -49.4 + 10.9 -48.4 +REG_LINE: 2 + 6.00 -54.5 + 4.63 -53.5 +REG_LINE: 2 + 2.59 -56.3 + 1.25 -55.2 +REG_LINE: 2 + -1.03 -57.5 + -2.33 -56.3 +REG_LINE: 2 + -4.86 -58.0 + -6.12 -56.8 +REG_LINE: 2 + -8.94 -57.7 + -10.1 -56.4 +REG_LINE: 2 + -17.9 -54.1 + -19.0 -52.7 +REG_LINE: 2 + -22.8 -50.3 + -23.9 -48.9 +REG_LINE: 2 + -28.1 -44.9 + -29.0 -43.4 +REG_LINE: 2 + -33.6 -37.4 + -34.4 -35.9 +REG_LINE: 2 + -39.3 -27.4 + -40.0 -25.8 +REG_LINE: 2 + -50.1 2.52 + -50.5 4.22 +REG_LINE: 2 + -54.3 23.2 + -54.6 24.9 +REG_LINE: 2 + -56.8 47.3 + -56.8 49.1 +REG_LINE: 2 + -56.8 73.8 + -56.1 75.5 +REG_LINE: 2 + -53.9 101. + -54.0 98.8 +REG_LINE: 2 + -41.1 146. + -41.7 144. +REG_LINE: 2 + -32.8 162. + -33.7 161. +REG_LINE: 2 + -24.5 175. + -25.5 173. +REG_LINE: 2 + -16.5 183. + -17.6 182. +REG_LINE: 2 + -9.06 188. + -10.4 187. +REG_LINE: 2 + 3.65 193. + 2.17 192. +REG_LINE: 2 + 9.03 193. + 7.49 192. +REG_LINE: 2 + 13.8 192. + 12.2 192. +REG_LINE: 2 + 18.1 190. + 16.5 190. +REG_LINE: 2 + 22.0 188. + 20.3 187. +REG_LINE: 15 + 48.1 82.5 + 51.2 81.6 + 54.3 80.6 + 57.3 79.5 + 60.3 78.3 + 63.2 77.0 + 66.0 75.5 + 68.8 73.9 + 71.5 72.2 + 74.1 70.3 + 76.7 68.3 + 79.1 66.1 + 81.5 63.8 + 83.8 61.3 + 85.9 58.6 +REG_LINE: 15 + 48.1 82.5 + 45.0 83.2 + 41.8 83.8 + 38.7 84.4 + 35.5 84.8 + 32.2 85.0 + 29.0 85.2 + 25.8 85.3 + 22.5 85.3 + 19.3 85.1 + 16.1 84.8 + 12.9 84.4 + 9.69 83.9 + 6.52 83.3 + 3.39 82.6 +REG_LINE: 15 + 3.39 82.6 + .284 81.8 + -2.78 80.8 + -5.80 79.7 + -8.77 78.6 + -11.7 77.2 + -14.5 75.8 + -17.3 74.2 + -20.1 72.5 + -22.7 70.7 + -25.3 68.7 + -27.8 66.5 + -30.1 64.2 + -32.4 61.8 + -34.6 59.1 +REG_LINE: 15 + -34.6 59.1 + -36.7 56.3 + -38.7 53.3 + -40.5 50.0 + -42.2 46.5 + -43.7 42.7 + -45.0 38.5 + -46.2 34.0 + -47.1 29.1 + -47.8 23.7 + -48.2 17.8 + -48.2 11.1 + -47.8 3.67 + -46.7 -4.73 + -44.9 -14.2 +REG_LINE: 93 + -44.9 -14.2 + -42.0 -25.0 + -37.7 -37.2 + -31.3 -50.6 + -22.3 -64.7 + -21.6 -65.7 + -20.8 -66.7 + -20.0 -67.7 + -19.2 -68.7 + -18.4 -69.7 + -17.5 -70.7 + -16.7 -71.6 + -15.8 -72.6 + -14.9 -73.5 + -14.0 -74.5 + -13.0 -75.4 + -12.1 -76.3 + -11.1 -77.2 + -10.1 -78.1 + -9.08 -79.0 + -8.06 -79.8 + -7.01 -80.7 + -5.95 -81.5 + -4.87 -82.3 + -3.78 -83.1 + -2.67 -83.8 + -1.54 -84.6 + -.395 -85.3 + .764 -86.0 + 1.94 -86.6 + 3.13 -87.2 + 4.33 -87.8 + 5.54 -88.4 + 6.77 -89.0 + 8.01 -89.5 + 9.27 -89.9 + 10.5 -90.4 + 11.8 -90.8 + 13.1 -91.2 + 14.4 -91.5 + 15.7 -91.8 + 17.0 -92.1 + 18.3 -92.3 + 19.6 -92.5 + 20.9 -92.6 + 22.3 -92.8 + 23.6 -92.8 + 24.9 -92.9 + 26.2 -92.9 + 27.6 -92.8 + 28.9 -92.7 + 30.2 -92.6 + 31.5 -92.5 + 32.8 -92.3 + 34.2 -92.0 + 35.5 -91.8 + 36.8 -91.4 + 38.0 -91.1 + 39.3 -90.7 + 40.6 -90.3 + 41.9 -89.9 + 43.1 -89.4 + 44.3 -88.9 + 45.6 -88.3 + 46.8 -87.7 + 48.0 -87.1 + 49.2 -86.5 + 50.3 -85.8 + 51.5 -85.2 + 52.6 -84.4 + 53.8 -83.7 + 54.9 -82.9 + 56.0 -82.2 + 57.0 -81.4 + 58.1 -80.5 + 59.1 -79.7 + 60.2 -78.8 + 61.2 -78.0 + 62.2 -77.1 + 63.1 -76.2 + 64.1 -75.2 + 65.0 -74.3 + 65.9 -73.4 + 66.8 -72.4 + 67.7 -71.4 + 68.6 -70.5 + 69.4 -69.5 + 70.2 -68.5 + 71.1 -67.5 + 80.7 -53.4 + 87.5 -39.8 + 92.1 -27.4 + 95.3 -16.3 +REG_LINE: 15 + 95.3 -16.3 + 97.3 -6.55 + 98.5 2.06 + 99.1 9.69 + 99.1 16.5 + 98.8 22.6 + 98.2 28.1 + 97.3 33.1 + 96.2 37.7 + 94.9 41.9 + 93.4 45.7 + 91.7 49.3 + 89.9 52.6 + 88.0 55.7 + 85.9 58.6 +REG_LINE: 15 + 85.9 58.6 + 83.8 61.3 + 81.5 63.8 + 79.1 66.1 + 76.7 68.3 + 74.1 70.3 + 71.5 72.2 + 68.8 73.9 + 66.0 75.5 + 63.2 77.0 + 60.3 78.3 + 57.3 79.5 + 54.3 80.6 + 51.2 81.6 + 48.1 82.5 +REG_LINE: 15 + 48.1 82.5 + 45.0 83.2 + 41.8 83.8 + 38.7 84.4 + 35.5 84.8 + 32.2 85.0 + 29.0 85.2 + 25.8 85.3 + 22.5 85.3 + 19.3 85.1 + 16.1 84.8 + 12.9 84.4 + 9.69 83.9 + 6.52 83.3 + 3.39 82.6 +REG_LINE: 15 + 48.1 82.5 + 45.0 83.2 + 41.8 83.8 + 38.7 84.4 + 35.5 84.8 + 32.2 85.0 + 29.0 85.2 + 25.8 85.3 + 22.5 85.3 + 19.3 85.1 + 16.1 84.8 + 12.9 84.4 + 9.69 83.9 + 6.52 83.3 + 3.39 82.6 +REG_LINE: 15 + 25.4 -29.7 + 24.4 -31.7 + 23.4 -33.7 + 22.4 -35.6 + 21.3 -37.4 + 20.2 -39.2 + 19.1 -40.9 + 18.0 -42.6 + 16.8 -44.2 + 15.6 -45.7 + 14.4 -47.2 + 13.1 -48.6 + 11.9 -49.9 + 10.6 -51.1 + 9.23 -52.2 +REG_LINE: 15 + 9.23 -52.2 + 7.87 -53.3 + 6.48 -54.2 + 5.05 -55.1 + 3.58 -55.9 + 2.08 -56.5 + .548 -57.1 + -1.03 -57.5 + -2.64 -57.8 + -4.30 -58.0 + -6.00 -58.0 + -7.75 -57.9 + -9.54 -57.6 + -11.4 -57.1 + -13.3 -56.4 +REG_LINE: 15 + -13.3 -56.4 + -15.2 -55.6 + -17.2 -54.5 + -19.3 -53.2 + -21.4 -51.6 + -23.6 -49.7 + -25.8 -47.5 + -28.1 -44.9 + -30.4 -42.0 + -32.8 -38.6 + -35.2 -34.8 + -37.6 -30.5 + -40.1 -25.7 + -42.5 -20.3 + -44.9 -14.2 +REG_LINE: 15 + -44.9 -14.2 + -47.2 -7.52 + -49.4 -.109 + -51.4 8.02 + -53.3 16.9 + -54.8 26.4 + -56.0 36.6 + -56.8 47.3 + -57.1 58.5 + -56.9 70.0 + -56.2 81.6 + -55.0 93.1 + -53.3 104. + -51.0 115. + -48.4 125. +REG_LINE: 15 + -48.4 125. + -45.4 135. + -42.2 143. + -38.8 151. + -35.2 158. + -31.6 164. + -28.0 170. + -24.5 175. + -21.0 179. + -17.6 182. + -14.3 185. + -11.1 187. +Forward matrix value + M2 = -0.811474832908671 # Forward matrix value + M3 = 0.171224898552328 # Forward matrix value + M4 = -0.862236746712233 # Forward matrix value + M5 = -0.476686298035564 # Forward matrix value + M6 = -0.941127638091139 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = -0.338051429254475 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = -2.61046557479594 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End FrameSet + Begin FrameSet # Set of inter-related coordinate systems +# Title = "Pixel coordinates; first pixel at (-100.5,-200.5)" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "POLAR" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + IsA Frame # Coordinate system description + Nframe = 2 # Number of Frames in FrameSet +# Base = 1 # Index of base Frame + Currnt = 2 # Index of current Frame + Nod1 = 2 # Frame 1 is associated with node 2 + Nod2 = 1 # Frame 2 is associated with node 1 + Lnk2 = 1 # Node 2 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Data grid indices; first pixel at (1,1) " # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Data grid index 1" # Label for axis 1 +# Lbl2 = "Data grid index 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Data grid index 1" # Axis Label + Symbol = "g1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Data grid index 2" # Axis Label + Symbol = "g2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm2 = # Frame number 2 + Begin Frame # Coordinate system description + Title = "Pixel coordinates; first pixel at (-100.5,-200.5)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "POLAR" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 1" # Axis Label + -8.06 189. + -5.15 190. + -2.36 192. +REG_LINE: 15 + -2.36 192. + .299 192. + 2.83 193. + 5.25 193. + 7.56 193. + 9.75 193. + 11.8 193. + 13.8 192. + 15.7 192. + 17.6 191. + 19.3 190. + 20.9 189. + 22.5 188. + 24.0 186. + 25.4 185. +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '180' + 47.5 80.0 TC .248 .969 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '240' + 4.00 80.2 TC -.245 .969 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '300' + -32.7 57.6 TC -.786 .618 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0' + -42.4 -13.7 BC .976 .217 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '60' + 92.9 -15.7 BC -.971 .241 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '120' + 84.0 57.1 TC .798 .602 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-90' + 27.7 -30.8 TC -.899 .438 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-60' + 10.8 -54.2 TC -.629 .777 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-30' + -14.2 -58.8 TC .374 .927 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0' + -47.2 -15.1 TC .937 .350 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '30' + -50.8 126. BC -.960 .278 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '60' + -3.21 194. BC -.341 .940 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '90' + 27.2 187. BC .711 .704 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 1 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: ' A FITS test' + 31.5 225. BC .000 1.00 + + + + + FITS test number 2 + ==================== + + + +AST_SHOW: + +REG_SINK: +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +AST +COMMENT AST Beginning of AST data for FrameSet object +AST +COMMENT AST ................................................................ +AST +COMMENT AST ................................................................ +AST +COMMENT AST End of AST data for FrameSet object +AST +COMMENT AST ---------------------------------------------------------------- + +Objects written: 1 + +Native Encoding: + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST WCS information in AST format AST +COMMENT AST See http://www.starlink.ac.uk/ast/ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +CURRNT_A= 2 / Index of current Frame +NOD1_A = 2 / Frame 1 is associated with node 2 +NOD2_A = 1 / Frame 2 is associated with node 1 +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Data grid indices; first pixel at (1&'/ Title of coordinate system +CONTINUE '",1) "' +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Data grid index 1' / Axis Label +SYMBOL_A= 'g1 ' / Axis symbol +UNIT_A = 'pixel ' / Axis units +FORMAT_A= '%3.1f ' / Format specifier +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Data grid index 2' / Axis Label +SYMBOL_B= 'g2 ' / Axis symbol +UNIT_B = 'pixel ' / Axis units +FORMAT_B= '%3.1f ' / Format specifier +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'Frame ' / Coordinate system description +TITLE_B = 'Pixel coordinates; first pixel at (-&'/ Title of coordinate system +CONTINUE '100.5,-200.5)' +NAXES_B = 2 / Number of coordinate axes +DOMAIN_B= 'POLAR ' / Coordinate system domain +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'Axis ' / Coordinate axis +LABEL_C = 'Pixel coordinate 1' / Axis Label +SYMBOL_C= 'p1 ' / Axis symbol +UNIT_C = 'pixel ' / Axis units +FORMAT_C= '%3.1f ' / Format specifier +ENDAST_D= 'Axis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'Axis ' / Coordinate axis +LABEL_D = 'Pixel coordinate 2' / Axis Label +SYMBOL_D= 'p2 ' / Axis symbol +UNIT_D = 'pixel ' / Axis units +FORMAT_D= '%3.1f ' / Format specifier +ENDAST_E= 'Axis ' / End of object definition +ENDAST_F= 'Frame ' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISA_A = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +INVB_A = 1 / Second Mapping used in inverse direction +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'MathMap ' / Transformation using mathematical functions +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_B = 'Mapping ' / Mapping between coordinate systems +FWD1_A = 'r=sqrt(x*x+y*y)' / Forward function 1 +FWD2_A = 'theta=atan2(y,x)' / Forward function 2 +INV1_A = 'x=r*cos(theta)' / Inverse function 1 +INV2_A = 'y=r*sin(theta)' / Inverse function 2 +SIMPFI_A= 1 / Forward-inverse pairs may simplify +SIMPIF_A= 1 / Inverse-forward pairs may simplify +ENDAST_G= 'MathMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'WinMap ' / Map one window on to another +NIN_C = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_C = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -101.5 / Shift for axis 1 +SFT2_A = -201.5 / Shift for axis 2 +ENDAST_H= 'WinMap ' / End of object definition +ENDAST_I= 'CmpMap ' / End of object definition +ENDAST_J= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST + +ATTRIBUTES: + Colour(axis1) : 1 + Font(Stri) : 1 + Nout : 1 + Class : 4 + Tol : 0.100000E-01 + Gap(1) : 50.0000 + Border : 0 + Invert : 0 + TextLabGap : 0.100000E-01 + Nin : 2 + Current : 3 + Base : 1 + Nobject : 1 + RefCOUNT : 1 + +AST_GRID: +REG_LINE: 197 + 10.8 124. + 10.7 123. + 10.5 122. + 10.3 121. + 10.2 120. + 10.1 119. + 10.0 118. + 9.94 117. + 9.89 116. + 9.85 115. + 9.84 113. + 9.85 112. + 9.88 111. + 9.93 110. + 10.0 109. + 10.1 108. + 10.2 107. + 10.3 106. + 10.5 105. + 10.6 104. + 10.8 103. + 11.0 102. + 11.2 100. + 11.5 99.5 + 11.7 98.5 + 12.0 97.5 + 12.3 96.6 + 12.6 95.6 + 13.0 94.7 + 13.3 93.9 + 13.7 93.0 + 14.0 92.2 + 14.4 91.4 + 14.8 90.6 + 15.2 89.8 + 15.7 89.1 + 16.1 88.4 + 16.5 87.8 + 17.0 87.2 + 17.5 86.6 + 18.0 86.0 + 18.5 85.5 + 19.0 85.0 + 19.5 84.6 + 20.0 84.2 + 20.5 83.8 + 21.1 83.5 + 21.6 83.2 + 22.1 82.9 + 22.7 82.7 + 23.2 82.5 + 23.8 82.4 + 24.3 82.3 + 24.9 82.2 + 25.5 82.2 + 26.0 82.2 + 26.6 82.3 + 27.1 82.4 + 27.7 82.5 + 28.2 82.7 + 28.8 82.9 + 29.3 83.2 + 29.9 83.4 + 30.4 83.8 + 30.9 84.2 + 31.4 84.6 + 32.0 85.0 + 32.5 85.5 + 33.0 86.0 + 33.4 86.6 + 33.9 87.1 + 34.4 87.8 + 34.8 88.4 + 35.3 89.1 + 35.7 89.8 + 36.1 90.6 + 36.5 91.3 + 36.9 92.1 + 37.3 93.0 + 37.6 93.8 + 38.0 94.7 + 38.3 95.6 + 38.6 96.5 + 38.9 97.5 + 39.2 98.5 + 39.4 99.4 + 39.7 100. + 39.9 101. + 40.1 103. + 40.3 104. + 40.5 105. + 40.6 106. + 40.7 107. + 40.8 108. + 40.9 109. + 41.0 110. + 41.1 111. + 41.1 112. + 41.1 113. + 41.1 115. + 41.1 116. + 41.0 117. + 40.9 118. + 40.8 119. + 40.7 120. + 40.6 121. + 40.5 122. + 40.3 123. + 40.1 124. + 39.9 125. + 39.7 126. + 39.4 127. + 39.2 128. + 38.9 129. + 38.6 130. + 38.3 131. + 38.0 132. + 37.6 133. + 37.3 134. + 36.9 135. + 36.5 136. + 36.1 136. + 35.7 137. + 35.3 138. + 34.8 138. + 34.4 139. + 33.9 140. + 33.4 140. + 33.0 141. + 32.5 141. + 32.0 142. + 31.4 142. + 30.9 143. + 30.4 143. + 29.9 143. + 29.3 144. + 28.8 144. + 28.2 144. + 27.7 144. + 27.1 145. + 26.6 145. + 26.0 145. + 25.5 145. + 24.9 145. + 24.3 145. + 23.8 145. + 23.2 144. + 22.7 144. + 22.1 144. + 21.6 144. + 21.1 143. + 20.5 143. + 20.0 143. + 19.5 142. + 19.0 142. + 18.5 141. + 18.0 141. + 17.5 140. + 17.0 140. + 16.5 139. + 16.1 138. + 15.7 138. + 15.2 137. + 14.8 136. + 14.4 136. + 14.0 135. + 13.7 134. + 13.3 133. + 13.0 132. + 12.6 131. + 12.3 130. + 12.0 129. + 11.7 128. + 11.5 127. + 11.2 126. + 11.0 125. + 10.8 124. + 10.6 123. + 10.5 122. + 10.3 121. + 10.2 120. + 10.1 119. + 10.0 118. + 9.93 117. + 9.88 116. + 9.85 115. + 9.84 113. + 9.85 112. + 9.89 111. + 9.94 110. + 10.0 109. + 10.1 108. + 10.2 107. + 10.3 106. + 10.5 105. + 10.7 104. + 10.8 102. +REG_LINE: 197 + -3.80 135. + -4.17 133. + -4.50 131. + -4.80 129. + -5.06 127. + -5.28 125. + -5.46 122. + -5.60 120. + -5.70 118. + -5.76 116. + -5.78 114. + -5.76 111. + -5.70 109. + -5.61 107. + -5.47 105. + -5.29 102. + -5.07 100. + -4.82 98.1 + -4.53 95.9 + -4.19 93.8 + -3.82 91.7 + -3.42 89.6 + -2.97 87.5 + -2.49 85.5 + -1.97 83.5 + -1.42 81.6 + -.838 79.7 + -.219 77.8 + .433 76.0 + 1.12 74.3 + 1.83 72.6 + 2.58 70.9 + 3.35 69.3 + 4.15 67.7 + 4.98 66.2 + 5.84 64.8 + 6.72 63.4 + 7.62 62.1 + 8.55 60.9 + 9.50 59.7 + 10.5 58.6 + 11.5 57.6 + 12.5 56.6 + 13.5 55.7 + 14.5 54.9 + 15.6 54.2 + 16.6 53.5 + 17.7 52.9 + 18.8 52.4 + 19.9 51.9 + 21.0 51.6 + 22.1 51.3 + 23.2 51.1 + 24.3 51.0 + 25.4 50.9 + 26.6 51.0 + 27.7 51.1 + 28.8 51.3 + 29.9 51.6 + 31.0 51.9 + 32.1 52.4 + 33.2 52.9 + 34.3 53.5 + 35.3 54.1 + 36.4 54.9 + 37.4 55.7 + 38.4 56.6 + 39.4 57.5 + 40.4 58.6 + 41.4 59.7 + 42.4 60.8 + 43.3 62.1 + 44.2 63.4 + 45.1 64.8 + 45.9 66.2 + 46.8 67.7 + 47.6 69.2 + 48.3 70.8 + 49.1 72.5 + 49.8 74.2 + 50.5 76.0 + 51.1 77.8 + 51.8 79.6 + 52.3 81.5 + 52.9 83.5 + 53.4 85.5 + 53.9 87.5 + 54.3 89.5 + 54.7 91.6 + 55.1 93.7 + 55.5 95.8 + 55.7 98.0 + 56.0 100. + 56.2 102. + 56.4 105. + 56.5 107. + 56.6 109. + 56.7 111. + 56.7 113. + 56.7 116. + 56.6 118. + 56.5 120. + 56.4 122. + 56.2 125. + 56.0 127. + 55.7 129. + 55.5 131. + 55.1 133. + 54.7 135. + 54.3 137. + 53.9 139. + 53.4 141. + 52.9 143. + 52.3 145. + 51.8 147. + 51.1 149. + 50.5 151. + 49.8 153. + 49.1 154. + 48.3 156. + 47.6 158. + 46.8 159. + 45.9 161. + 45.1 162. + 44.2 163. + 43.3 165. + 42.4 166. + 41.4 167. + 40.4 168. + 39.4 169. + 38.4 170. + 37.4 171. + 36.4 172. + 35.3 173. + 34.3 173. + 33.2 174. + 32.1 175. + 31.0 175. + 29.9 175. + 28.8 176. + 27.7 176. + 26.6 176. + 25.4 176. + 24.3 176. + 23.2 176. + 22.1 176. + 21.0 175. + 19.9 175. + 18.8 174. + 17.7 174. + 16.6 173. + 15.6 173. + 14.5 172. + 13.5 171. + 12.5 170. + 11.5 169. + 10.5 168. + 9.50 167. + 8.55 166. + 7.62 165. + 6.72 163. + 5.84 162. + 4.98 161. + 4.15 159. + 3.35 158. + 2.58 156. + 1.83 154. + 1.12 153. + .433 151. + -.219 149. + -.838 147. + -1.42 145. + -1.97 143. + -2.49 141. + -2.97 139. + -3.42 137. + -3.82 135. + -4.19 133. + -4.53 131. + -4.82 129. + -5.07 127. + -5.29 124. + -5.47 122. + -5.61 120. + -5.70 118. + -5.76 116. + -5.78 113. + -5.76 111. + -5.70 109. + -5.60 107. + -5.46 104. + -5.28 102. + -5.06 100. + -4.80 97.9 + -4.50 95.7 + -4.17 93.6 + -3.80 91.5 +REG_LINE: 197 + -18.4 146. + -19.0 143. + -19.5 140. + -19.9 137. + -20.3 133. + -20.7 130. + -20.9 127. + -21.1 124. + -21.3 120. + -21.4 117. + -21.4 114. + -21.4 110. + -21.3 107. + -21.1 104. + -20.9 100. + -20.7 96.9 + -20.3 93.6 + -20.0 90.4 + -19.5 87.1 + -19.0 83.9 + -18.5 80.8 + -17.9 77.7 + -17.2 74.6 + -16.5 71.6 + -15.7 68.6 + -14.9 65.7 + -14.0 62.8 + -13.1 60.0 + -12.1 57.3 + -11.1 54.7 + -9.99 52.1 + -8.87 49.6 + -7.71 47.2 + -6.50 44.9 + -5.26 42.6 + -3.98 40.5 + -2.66 38.4 + -1.30 36.5 + 0.922E-01 34.6 + 1.52 32.9 + 2.97 31.2 + 4.45 29.6 + 5.96 28.2 + 7.50 26.9 + 9.05 25.6 + 10.6 24.5 + 12.2 23.5 + 13.8 22.6 + 15.5 21.8 + 17.1 21.2 + 18.8 20.7 + 20.4 20.2 + 22.1 19.9 + 23.8 19.7 + 25.4 19.7 + 27.1 19.7 + 28.8 19.9 + 30.5 20.2 + 32.1 20.6 + 33.8 21.2 + 35.4 21.8 + 37.0 22.6 + 38.7 23.5 + 40.2 24.5 + 41.8 25.6 + 43.4 26.8 + 44.9 28.1 + 46.4 29.6 + 47.9 31.1 + 49.4 32.8 + 50.8 34.5 + 52.2 36.4 + 53.5 38.4 + 54.9 40.4 + 56.2 42.6 + 57.4 44.8 + 58.6 47.1 + 59.8 49.5 + 60.9 52.0 + 62.0 54.6 + 63.0 57.2 + 64.0 60.0 + 64.9 62.7 + 65.8 65.6 + 66.6 68.5 + 67.4 71.5 + 68.1 74.5 + 68.8 77.5 + 69.4 80.7 + 69.9 83.8 + 70.4 87.0 + 70.9 90.2 + 71.3 93.5 + 71.6 96.8 + 71.9 100. + 72.1 103. + 72.2 107. + 72.3 110. + 72.3 113. + 72.3 117. + 72.2 120. + 72.1 123. + 71.9 127. + 71.6 130. + 71.3 133. + 70.9 137. + 70.4 140. + 69.9 143. + 69.4 146. + 68.8 149. + 68.1 152. + 67.4 155. + 66.6 158. + 65.8 161. + 64.9 164. + 64.0 167. + 63.0 170. + 62.0 172. + 60.9 175. + 59.8 177. + 58.6 180. + 57.4 182. + 56.2 184. + 54.9 186. + 53.5 189. + 52.2 190. + 50.8 192. + 49.4 194. + 47.9 196. + 46.4 197. + 44.9 199. + 43.4 200. + 41.8 201. + 40.2 202. + 38.7 203. + 37.0 204. + 35.4 205. + 33.8 206. + 32.1 206. + 30.5 207. + 28.8 207. + 27.1 207. + 25.4 207. + 23.8 207. + 22.1 207. + 20.4 207. + 18.8 206. + 17.1 206. + 15.5 205. + 13.8 204. + 12.2 203. + 10.6 202. + 9.05 201. + 7.50 200. + 5.96 199. + 4.45 197. + 2.97 196. + 1.52 194. + 0.922E-01 192. + -1.30 190. + -2.66 188. + -3.98 186. + -5.26 184. + -6.50 182. + -7.71 180. + -8.87 177. + -9.99 175. + -11.1 172. + -12.1 170. + -13.1 167. + -14.0 164. + -14.9 161. + -15.7 158. + -16.5 155. + -17.2 152. + -17.9 149. + -18.5 146. + -19.0 143. + -19.5 140. + -20.0 137. + -20.3 133. + -20.7 130. + -20.9 127. + -21.1 123. + -21.3 120. + -21.4 117. + -21.4 113. + -21.4 110. + -21.3 107. + -21.1 103. + -20.9 100. + -20.7 96.7 + -20.3 93.4 + -19.9 90.1 + -19.5 86.9 + -19.0 83.7 + -18.4 80.6 +REG_LINE: 197 + -33.1 157. + -33.8 153. + -34.5 149. + -35.1 145. + -35.6 140. + -36.0 136. + -36.4 131. + -36.7 127. + -36.9 123. + -37.0 118. + -37.0 114. + -37.0 109. + -36.9 105. + -36.7 100. + -36.4 95.8 + -36.1 91.4 + -35.6 87.0 + -35.1 82.7 + -34.5 78.4 + -33.9 74.1 + -33.1 69.9 + -32.3 65.7 + -31.4 61.6 + -30.5 57.6 + -29.4 53.6 + -28.3 49.8 + -27.1 46.0 + -25.9 42.3 + -24.6 38.6 + -23.2 35.1 + -21.8 31.7 + -20.3 28.3 + -18.8 25.1 + -17.2 22.0 + -15.5 19.0 + -13.8 16.2 + -12.0 13.4 + -10.2 10.8 + -8.37 8.34 + -6.47 5.99 + -4.53 3.78 + -2.55 1.71 + -.540 -.225 + 1.51 -2.01 + 3.58 -3.65 + 5.69 -5.14 + 7.82 -6.47 + 9.97 -7.66 + 12.1 -8.69 + 14.3 -9.56 + 16.5 -10.3 + 18.7 -10.8 + 21.0 -11.2 + 23.2 -11.5 + 25.4 -11.6 + 27.7 -11.5 + 29.9 -11.2 + 32.1 -10.9 + 34.3 -10.3 + 36.5 -9.59 + 38.7 -8.72 + 40.9 -7.70 + 43.0 -6.52 + 45.2 -5.19 + 47.3 -3.70 + 49.4 -2.07 + 51.4 -.290 + 53.4 1.63 + 55.4 3.70 + 57.3 5.91 + 59.2 8.25 + 61.1 10.7 + 62.9 13.3 + 64.7 16.1 + 66.4 18.9 + 68.0 21.9 + 69.6 25.0 + 71.2 28.2 + 72.7 31.6 + 74.1 35.0 + 75.5 38.5 + 76.8 42.1 + 78.0 45.8 + 79.2 49.6 + 80.3 53.5 + 81.4 57.5 + 82.3 61.5 + 83.2 65.6 + 84.0 69.7 + 84.8 73.9 + 85.4 78.2 + 86.0 82.5 + 86.5 86.9 + 87.0 91.2 + 87.3 95.6 + 87.6 100. + 87.8 105. + 87.9 109. + 88.0 113. + 87.9 118. + 87.8 122. + 87.6 127. + 87.3 131. + 87.0 136. + 86.5 140. + 86.0 144. + 85.4 149. + 84.8 153. + 84.0 157. + 83.2 161. + 82.3 165. + 81.4 169. + 80.3 173. + 79.2 177. + 78.0 181. + 76.8 185. + 75.5 188. + 74.1 192. + 72.7 195. + 71.2 199. + 69.6 202. + 68.0 205. + 66.4 208. + 64.7 211. + 62.9 214. + 61.1 216. + 59.2 219. + 57.3 221. + 55.4 223. + 53.4 225. + 51.4 227. + 49.4 229. + 47.3 231. + 45.2 232. + 43.0 233. + 40.9 235. + 38.7 236. + 36.5 236. + 34.3 237. + 32.1 238. + 29.9 238. + 27.7 238. + 25.4 238. + 23.2 238. + 21.0 238. + 18.7 238. + 16.5 237. + 14.3 236. + 12.1 236. + 9.97 235. + 7.82 233. + 5.69 232. + 3.58 231. + 1.51 229. + -.540 227. + -2.55 225. + -4.53 223. + -6.47 221. + -8.37 219. + -10.2 216. + -12.0 213. + -13.8 211. + -15.5 208. + -17.2 205. + -18.8 202. + -20.3 199. + -21.8 195. + -23.2 192. + -24.6 188. + -25.9 185. + -27.1 181. + -28.3 177. + -29.4 173. + -30.5 169. + -31.4 165. + -32.3 161. + -33.1 157. + -33.9 153. + -34.5 149. + -35.1 144. + -35.6 140. + -36.1 135. + -36.4 131. + -36.7 127. + -36.9 122. + -37.0 118. + -37.0 113. + -37.0 109. + -36.9 104. + -36.7 99.9 + -36.4 95.5 + -36.0 91.1 + -35.6 86.7 + -35.1 82.4 + -34.5 78.1 + -33.8 73.8 + -33.1 69.6 +REG_LINE: 197 + -47.7 168. + -48.6 163. + -49.5 158. + -50.2 152. + -50.8 147. + -51.4 141. + -51.8 136. + -52.2 130. + -52.4 125. + -52.6 119. + -52.7 114. + -52.6 108. + -52.5 102. + -52.2 96.9 + -51.9 91.4 + -51.4 85.9 + -50.9 80.4 + -50.3 75.0 + -49.5 69.6 + -48.7 64.3 + -47.8 59.0 + -46.7 53.8 + -45.6 48.7 + -44.4 43.6 + -43.1 38.7 + -41.8 33.8 + -40.3 29.1 + -38.8 24.5 + -37.1 19.9 + -35.4 15.5 + -33.6 11.2 + -31.8 7.08 + -29.8 3.06 + -27.8 -.822 + -25.7 -4.55 + -23.6 -8.14 + -21.4 -11.6 + -19.1 -14.8 + -16.8 -17.9 + -14.5 -20.9 + -12.0 -23.6 + -9.56 -26.2 + -7.04 -28.6 + -4.49 -30.9 + -1.89 -32.9 + .740 -34.8 + 3.40 -36.5 + 6.09 -37.9 + 8.81 -39.2 + 11.5 -40.3 + 14.3 -41.2 + 17.1 -41.9 + 19.8 -42.4 + 22.6 -42.7 + 25.4 -42.8 + 28.2 -42.7 + 31.0 -42.4 + 33.8 -41.9 + 36.5 -41.2 + 39.3 -40.3 + 42.0 -39.3 + 44.7 -38.0 + 47.4 -36.5 + 50.1 -34.8 + 52.7 -33.0 + 55.3 -30.9 + 57.9 -28.7 + 60.4 -26.3 + 62.9 -23.7 + 65.3 -21.0 + 67.7 -18.0 + 70.0 -14.9 + 72.3 -11.7 + 74.5 -8.26 + 76.6 -4.68 + 78.7 -.956 + 80.7 2.92 + 82.6 6.93 + 84.5 11.1 + 86.3 15.4 + 88.0 19.8 + 89.6 24.3 + 91.2 28.9 + 92.6 33.7 + 94.0 38.5 + 95.3 43.5 + 96.5 48.5 + 97.6 53.6 + 98.7 58.8 + 99.6 64.1 + 100. 69.4 + 101. 74.8 + 102. 80.2 + 102. 85.7 + 103. 91.2 + 103. 96.7 + 103. 102. + 104. 108. + 104. 113. + 104. 119. + 103. 125. + 103. 130. + 103. 136. + 102. 141. + 102. 147. + 101. 152. + 100. 157. + 99.6 163. + 98.7 168. + 97.6 173. + 96.5 178. + 95.3 183. + 94.0 188. + 92.6 193. + 91.2 198. + 89.6 203. + 88.0 207. + 86.3 212. + 84.5 216. + 82.6 220. + 80.7 224. + 78.7 228. + 76.6 232. + 74.5 235. + 72.3 239. + 70.0 242. + 67.7 245. + 65.3 248. + 62.9 251. + 60.4 253. + 57.9 256. + 55.3 258. + 52.7 260. + 50.1 262. + 47.4 263. + 44.7 265. + 42.0 266. + 39.3 267. + 36.5 268. + 33.8 269. + 31.0 269. + 28.2 270. + 25.4 270. + 22.6 270. + 19.8 269. + 17.1 269. + 14.3 268. + 11.5 267. + 8.81 266. + 6.09 265. + 3.40 263. + .740 262. + -1.89 260. + -4.49 258. + -7.04 256. + -9.56 253. + -12.0 251. + -14.5 248. + -16.8 245. + -19.1 242. + -21.4 238. + -23.6 235. + -25.7 231. + -27.8 228. + -29.8 224. + -31.8 220. + -33.6 216. + -35.4 211. + -37.1 207. + -38.8 202. + -40.3 198. + -41.8 193. + -43.1 188. + -44.4 183. + -45.6 178. + -46.7 173. + -47.8 168. + -48.7 163. + -49.5 157. + -50.3 152. + -50.9 146. + -51.4 141. + -51.9 135. + -52.2 130. + -52.5 124. + -52.6 119. + -52.7 113. + -52.6 108. + -52.4 102. + -52.2 96.5 + -51.8 91.0 + -51.4 85.5 + -50.8 80.0 + -50.2 74.6 + -49.5 69.2 + -48.6 63.9 + -47.7 58.6 +REG_LINE: 141 + -62.3 179. + -63.4 173. + -64.4 167. + -65.3 160. + -66.1 154. + -66.8 147. + -67.3 140. + -67.7 134. + -68.0 127. + -68.2 120. + -68.3 114. + -68.2 107. + -68.1 100. + -67.8 93.6 + -67.3 87.0 + -66.8 80.4 + -66.2 73.8 + -65.4 67.3 + -64.5 60.8 + -63.5 54.4 + -62.4 48.1 + -61.2 41.9 + -59.9 35.7 + -58.4 29.7 + -56.9 23.8 + -55.2 17.9 + -53.5 12.2 + -51.6 6.66 + -49.6 1.22 + -47.6 -4.07 + -45.4 -9.21 + -43.2 -14.2 + -40.9 -19.0 + -38.5 -23.7 + -36.0 -28.2 + -33.4 -32.5 + -30.8 -36.6 + -28.1 -40.5 + -25.3 -44.2 + -22.4 -47.7 + -19.5 -51.1 + -16.6 -54.2 + -13.5 -57.1 + -10.5 -59.7 + -7.36 -62.2 + -4.21 -64.4 + -1.01 -66.4 + 2.22 -68.2 + 5.47 -69.7 + 8.76 -71.1 + 12.1 -72.1 + 15.4 -73.0 + 18.7 -73.6 + 22.1 -73.9 + 25.4 -74.1 + 28.8 -73.9 + 32.1 -73.6 + 35.4 -73.0 + 38.8 -72.2 + 42.1 -71.1 + 45.3 -69.8 + 48.6 -68.3 + 51.8 -66.5 + 55.0 -64.5 + 58.2 -62.3 + 61.3 -59.8 + 64.4 -57.2 + 67.4 -54.3 + 70.4 -51.2 + 73.3 -47.9 + 76.1 -44.3 + 78.9 -40.6 + 81.6 -36.7 + 84.3 -32.6 + 86.8 -28.3 + 89.3 -23.8 + 91.7 -19.2 + 94.1 -14.4 + 96.3 -9.39 + 98.5 -4.25 + 101. 1.03 + 102. 6.46 + 104. 12.0 + 106. 17.7 + 108. 23.5 + 109. 29.5 + 111. 35.5 + 112. 41.7 + 113. 47.9 + 114. 54.2 + 115. 60.6 + 116. 67.0 + 117. 73.6 + 118. 80.1 + 118. 86.7 + 119. 93.4 + 119. 100. + 119. 107. + 119. 113. + 119. 120. + 119. 127. + 119. 133. + 118. 140. + 118. 147. + 117. 153. + 116. 160. + 115. 166. + 114. 173. + 113. 179. + 112. 185. + 111. 191. + 109. 197. + 108. 203. + 106. 209. + 104. 215. + 102. 220. + 101. 226. + 98.5 231. + 96.3 236. + 94.1 241. + 91.7 246. + 89.3 251. + 86.8 255. + 84.3 259. + 81.6 264. + 78.9 267. + 76.1 271. + 73.3 275. + 70.4 278. + 67.4 281. + 64.4 284. + 61.3 287. + 58.2 289. + 55.0 291. + 51.8 293. + 48.6 295. + 45.3 297. + 42.1 298. + 38.8 299. + 35.4 300. + 34.7 300. +REG_LINE: 53 + 16.2 300. + 15.4 300. + 12.1 299. + 8.76 298. + 5.47 297. + 2.22 295. + -1.01 293. + -4.21 291. + -7.36 289. + -10.5 287. + -13.5 284. + -16.6 281. + -19.5 278. + -22.4 275. + -25.3 271. + -28.1 267. + -30.8 263. + -33.4 259. + -36.0 255. + -38.5 251. + -40.9 246. + -43.2 241. + -45.4 236. + -47.6 231. + -49.6 226. + -51.6 220. + -53.5 215. + -55.2 209. + -56.9 203. + -58.4 197. + -59.9 191. + -61.2 185. + -62.4 179. + -63.5 172. + -64.5 166. + -65.4 160. + -66.2 153. + -66.8 147. + -67.3 140. + -67.8 133. + -68.1 127. + -68.2 120. + -68.3 113. + -68.2 107. + -68.0 99.8 + -67.7 93.2 + -67.3 86.5 + -66.8 79.9 + -66.1 73.3 + -65.3 66.8 + -64.4 60.4 + -63.4 54.0 + -62.3 47.7 +REG_LINE: 128 + -77.0 190. + -78.3 183. + -79.4 175. + -80.5 168. + -81.4 160. + -82.1 153. + -82.8 145. + -83.3 137. + -83.6 129. + -83.8 122. + -83.9 114. + -83.8 106. + -83.6 98.1 + -83.3 90.3 + -82.8 82.6 + -82.2 74.9 + -81.4 67.2 + -80.5 59.6 + -79.5 52.0 + -78.3 44.6 + -77.1 37.2 + -75.6 29.9 + -74.1 22.8 + -72.4 15.7 + -70.6 8.81 + -68.7 2.01 + -66.6 -4.64 + -64.4 -11.1 + -62.2 -17.5 + -59.8 -23.7 + -57.3 -29.7 + -54.7 -35.5 + -51.9 -41.1 + -49.1 -46.5 + -46.2 -51.8 + -43.2 -56.8 + -40.2 -61.6 + -37.0 -66.1 + -33.7 -70.5 + -30.4 -74.6 + -27.0 -78.5 + -23.6 -82.1 + -20.0 -85.5 + -16.5 -88.6 + -12.8 -91.5 + -9.15 -94.1 + -5.42 -96.4 + -1.66 -98.5 + 2.14 -100. + 5.97 -102. + 9.83 -103. + 13.7 -104. + 17.6 -105. + 21.5 -105. + 25.4 -105. + 29.3 -105. + 33.2 -105. + 37.1 -104. + 41.0 -103. + 44.8 -102. + 48.7 -100. + 52.5 -98.5 + 56.2 -96.5 + 60.0 -94.2 + 63.6 -91.6 + 67.3 -88.7 + 70.9 -85.6 + 74.4 -82.2 + 77.8 -78.6 + 81.2 -74.7 + 84.6 -70.6 + 87.8 -66.3 + 91.0 -61.7 + 94.1 -56.9 + 97.1 -51.9 + 100. -46.7 + 103. -41.3 + 105. -35.7 + 108. -29.9 + 111. -23.9 + 113. -17.7 + 115. -11.4 + 117. -4.87 + 120. 1.78 + 121. 8.56 + 123. 15.5 + 125. 22.5 + 127. 29.7 + 128. 37.0 + 129. 44.3 + 130. 51.8 + 131. 59.3 + 132. 66.9 + 133. 74.6 + 134. 82.3 + 134. 90.0 + 135. 97.8 + 135. 106. + 135. 113. + 135. 121. + 135. 129. + 134. 137. + 134. 145. + 133. 152. + 132. 160. + 131. 168. + 130. 175. + 129. 183. + 128. 190. + 127. 197. + 125. 204. + 123. 211. + 121. 218. + 120. 225. + 117. 232. + 115. 238. + 113. 245. + 111. 251. + 108. 257. + 105. 263. + 103. 268. + 100. 274. + 97.1 279. + 94.1 284. + 91.0 289. + 87.8 293. + 84.6 298. + 82.5 300. +REG_LINE: 40 + -31.6 300. + -33.7 297. + -37.0 293. + -40.2 288. + -43.2 284. + -46.2 279. + -49.1 273. + -51.9 268. + -54.7 262. + -57.3 257. + -59.8 251. + -62.2 244. + -64.4 238. + -66.6 232. + -68.7 225. + -70.6 218. + -72.4 211. + -74.1 204. + -75.6 197. + -77.1 190. + -78.3 182. + -79.5 175. + -80.5 167. + -81.4 160. + -82.2 152. + -82.8 144. + -83.3 137. + -83.6 129. + -83.8 121. + -83.9 113. + -83.8 105. + -83.6 97.5 + -83.3 89.8 + -82.8 82.0 + -82.1 74.3 + -81.4 66.7 + -80.5 59.0 + -79.4 51.5 + -78.3 44.1 + -77.0 36.7 +REG_LINE: 97 + -91.6 201. + -93.1 193. + -94.4 184. + -95.6 176. + -96.6 167. + -97.5 158. + -98.2 149. + -98.8 140. + -99.2 132. + -99.4 123. + -99.5 114. + -99.5 105. + -99.2 95.9 + -98.8 87.0 + -98.3 78.2 + -97.6 69.3 + -96.7 60.6 + -95.7 51.9 + -94.5 43.3 + -93.2 34.8 + -91.7 26.3 + -90.1 18.0 + -88.3 9.83 + -86.4 1.77 + -84.3 -6.14 + -82.1 -13.9 + -79.8 -21.5 + -77.3 -28.9 + -74.7 -36.2 + -71.9 -43.2 + -69.1 -50.1 + -66.1 -56.7 + -63.0 -63.2 + -59.8 -69.4 + -56.5 -75.3 + -53.1 -81.1 + -49.5 -86.6 + -45.9 -91.8 + -42.2 -96.8 + -38.4 -101. + -34.5 -106. + -30.6 -110. + -26.5 -114. + -22.5 -117. + -18.3 -121. + -14.1 -124. + -9.84 -126. + -5.53 -129. + -1.19 -131. + 3.19 -133. + 7.59 -134. + 12.0 -135. + 16.5 -136. + 20.9 -136. + 25.4 -137. + 29.9 -136. + 34.3 -136. + 38.8 -135. + 43.2 -134. + 47.6 -133. + 52.0 -131. + 56.3 -129. + 60.6 -126. + 64.9 -124. + 69.1 -121. + 73.2 -118. + 77.3 -114. + 81.4 -110. + 85.3 -106. + 89.2 -102. + 93.0 -96.9 + 96.7 -92.0 + 100. -86.8 + 104. -81.3 + 107. -75.6 + 111. -69.6 + 114. -63.4 + 117. -57.0 + 120. -50.3 + 123. -43.5 + 126. -36.4 + 128. -29.2 + 131. -21.8 + 133. -14.2 + 135. -6.42 + 137. 1.49 + 139. 9.54 + 141. 17.7 + 143. 26.0 + 144. 34.5 + 145. 43.0 + 147. 51.6 + 148. 60.3 + 148. 69.0 + 149. 77.8 + 150. 86.7 + 150. 92.2 +REG_LINE: 23 + 150. 135. + 150. 140. + 149. 149. + 148. 158. + 148. 167. + 147. 175. + 145. 184. + 144. 192. + 143. 201. + 141. 209. + 139. 217. + 137. 225. + 135. 233. + 133. 241. + 131. 249. + 128. 256. + 126. 263. + 123. 270. + 120. 277. + 117. 284. + 114. 290. + 111. 296. + 109. 300. +REG_LINE: 35 + -57.7 300. + -59.8 296. + -63.0 290. + -66.1 284. + -69.1 277. + -71.9 270. + -74.7 263. + -77.3 256. + -79.8 248. + -82.1 241. + -84.3 233. + -86.4 225. + -88.3 217. + -90.1 209. + -91.7 201. + -93.2 192. + -94.5 184. + -95.7 175. + -96.7 166. + -97.6 158. + -98.3 149. + -98.8 140. + -99.2 131. + -99.5 122. + -99.5 113. + -99.4 104. + -99.2 95.3 + -98.8 86.4 + -98.2 77.5 + -97.5 68.7 + -96.6 60.0 + -95.6 51.3 + -94.4 42.7 + -93.1 34.2 + -91.6 25.7 +REG_LINE: 63 + -100. -13.5 + -98.0 -21.1 + -95.5 -29.8 + -92.9 -38.4 + -90.1 -46.7 + -87.2 -54.9 + -84.1 -62.8 + -80.9 -70.5 + -77.5 -78.0 + -74.1 -85.2 + -70.5 -92.2 + -66.7 -98.9 + -62.9 -105. + -58.9 -112. + -54.8 -117. + -50.7 -123. + -46.4 -128. + -42.0 -133. + -37.6 -138. + -33.1 -142. + -28.4 -146. + -23.8 -150. + -19.0 -153. + -14.3 -156. + -9.41 -159. + -4.52 -161. + .403 -163. + 5.36 -165. + 10.3 -166. + 15.3 -167. + 20.4 -168. + 25.4 -168. + 30.4 -168. + 35.4 -167. + 40.4 -166. + 45.4 -165. + 50.4 -163. + 55.3 -161. + 60.2 -159. + 65.0 -156. + 69.8 -153. + 74.5 -150. + 79.2 -146. + 83.8 -142. + 88.4 -138. + 92.8 -133. + 97.2 -129. + 101. -123. + 106. -118. + 110. -112. + 114. -106. + 118. -99.2 + 121. -92.5 + 125. -85.5 + 128. -78.3 + 132. -70.8 + 135. -63.1 + 138. -55.2 + 141. -47.0 + 144. -38.7 + 146. -30.1 + 149. -21.4 + 150. -17.1 +REG_LINE: 9 + 150. 244. + 149. 248. + 146. 257. + 144. 266. + 141. 274. + 138. 282. + 135. 290. + 132. 298. + 131. 300. +REG_LINE: 9 + -79.7 300. + -80.9 297. + -84.1 290. + -87.2 282. + -90.1 274. + -92.9 265. + -95.5 257. + -98.0 248. + -100. 240. +REG_LINE: 54 + -100. -72.8 + -99.7 -73.6 + -96.3 -82.4 + -92.7 -91.0 + -89.0 -99.3 + -85.1 -107. + -81.1 -115. + -77.0 -123. + -72.7 -130. + -68.3 -137. + -63.8 -143. + -59.1 -149. + -54.4 -155. + -49.5 -161. + -44.6 -166. + -39.6 -171. + -34.4 -175. + -29.2 -179. + -24.0 -183. + -18.7 -186. + -13.3 -189. + -7.85 -192. + -2.38 -194. + 3.13 -196. + 8.66 -197. + 14.2 -198. + 19.8 -199. + 25.4 -199. + 30.9 -199. + 36.5 -198. + 42.1 -197. + 47.6 -196. + 53.1 -194. + 58.6 -192. + 64.0 -189. + 69.4 -186. + 74.7 -183. + 80.0 -179. + 85.2 -175. + 90.3 -171. + 95.3 -166. + 100. -161. + 105. -155. + 110. -150. + 115. -143. + 119. -137. + 123. -130. + 128. -123. + 132. -115. + 136. -108. + 140. -99.6 + 144. -91.3 + 147. -82.7 + 150. -75.3 +REG_LINE: 2 + -99.9 300. + -100. 300. +REG_LINE: 13 + -100. -121. + -96.2 -129. + -91.8 -138. + -87.2 -146. + -82.5 -154. + -77.7 -162. + -72.7 -169. + -67.6 -176. + -62.4 -182. + -57.0 -188. + -51.6 -194. + -46.1 -199. + -45.1 -200. +REG_LINE: 13 + 96.0 -200. + 96.8 -199. + 102. -194. + 108. -188. + 113. -182. + 118. -176. + 123. -169. + 128. -162. + 133. -154. + 138. -146. + 143. -138. + 147. -130. + 150. -123. +REG_LINE: 6 + -100. -165. + -97.4 -170. + -92.3 -178. + -87.0 -187. + -81.6 -194. + -77.4 -200. +REG_LINE: 6 + 128. -200. + 132. -195. + 138. -187. + 143. -179. + 148. -170. + 150. -167. +REG_LINE: 10 + 40.9 118. + 24.4 113. + 7.79 108. + -8.78 104. + -25.4 98.9 + -41.9 94.2 + -58.5 89.5 + -75.1 84.8 + -91.7 80.0 + -100. 77.7 +REG_LINE: 12 + 38.0 132. + 24.6 112. + 11.2 92.1 + -2.25 72.0 + -15.7 52.0 + -29.1 31.9 + -42.5 11.9 + -55.9 -8.13 + -69.3 -28.2 + -82.7 -48.2 + -96.1 -68.2 + -100. -74.0 +REG_LINE: 13 + 32.0 142. + 25.0 111. + 18.0 81.0 + 11.1 50.5 + 4.10 20.1 + -2.86 -10.4 + -9.83 -40.8 + -16.8 -71.3 + -23.8 -102. + -30.7 -132. + -37.7 -163. + -44.7 -193. + -46.3 -200. +REG_LINE: 12 + 24.4 145. + 25.5 111. + 26.7 77.8 + 27.9 44.4 + 29.1 11.0 + 30.3 -22.4 + 31.5 -55.8 + 32.7 -89.2 + 33.8 -123. + 35.0 -156. + 36.2 -189. + 36.6 -200. +REG_LINE: 14 + 17.0 140. + 26.1 112. + 35.1 83.4 + 44.2 55.2 + 53.2 27.0 + 62.3 -1.14 + 71.3 -29.3 + 80.3 -57.5 + 89.4 -85.7 + 98.4 -114. + 107. -142. + 117. -170. + 126. -198. + 126. -200. +REG_LINE: 11 + 11.8 128. + 26.4 112. + 41.1 96.3 + 55.8 80.3 + 70.5 64.2 + 85.2 48.2 + 99.9 32.1 + 115. 16.1 + 129. 0.199E-02 + 144. -16.1 + 150. -22.6 +REG_LINE: 10 + 9.84 113. + 26.6 113. + 43.3 113. + 60.1 113. + 76.8 113. + 93.5 113. + 110. 113. + 127. 113. + 144. 113. + 150. 113. +REG_LINE: 11 + 11.8 98.5 + 26.4 115. + 41.1 131. + 55.8 147. + 70.5 163. + 85.2 179. + 99.9 195. + 115. 211. + 129. 227. + 144. 243. + 150. 250. +REG_LINE: 9 + 17.0 87.1 + 26.1 115. + 35.1 143. + 44.2 172. + 53.2 200. + 62.3 228. + 71.3 256. + 80.3 284. + 85.4 300. +REG_LINE: 8 + 24.4 82.3 + 25.5 116. + 26.7 149. + 27.9 182. + 29.1 216. + 30.3 249. + 31.5 283. + 32.1 300. +REG_LINE: 9 + 32.0 85.0 + 25.0 115. + 18.0 146. + 11.1 176. + 4.10 207. + -2.86 237. + -9.83 268. + -16.8 298. + -17.2 300. +REG_LINE: 12 + 38.0 94.7 + 24.6 115. + 11.2 135. + -2.25 155. + -15.7 175. + -29.1 195. + -42.5 215. + -55.9 235. + -69.3 255. + -82.7 275. + -96.1 295. + -99.4 300. +REG_LINE: 10 + 40.9 109. + 24.4 114. + 7.79 118. + -8.78 123. + -25.4 128. + -41.9 133. + -58.5 137. + -75.1 142. + -91.7 147. + -100. 149. +REG_LINE: 2 + 18.7 134. + 17.7 133. +REG_LINE: 2 + 20.4 129. + 19.4 128. +REG_LINE: 2 + 22.1 124. + 21.1 123. +REG_LINE: 2 + 23.8 119. + 22.8 117. +REG_LINE: 2 + 27.2 108. + 28.2 110. +REG_LINE: 2 + 28.8 103. + 29.8 104. +REG_LINE: 2 + 30.5 97.7 + 31.5 99.1 +REG_LINE: 2 + 32.2 92.4 + 33.2 93.8 +REG_LINE: 2 + 35.6 81.9 + 36.6 83.3 +REG_LINE: 2 + 37.3 76.6 + 38.3 78.1 +REG_LINE: 2 + 39.0 71.4 + 40.0 72.8 +REG_LINE: 2 + 40.7 66.1 + 41.7 67.5 +REG_LINE: 2 + 44.0 55.6 + 45.0 57.0 +REG_LINE: 2 + 45.7 50.3 + 46.7 51.8 +REG_LINE: 2 + 47.4 45.1 + 48.4 46.5 +REG_LINE: 2 + 49.1 39.8 + 50.1 41.2 +REG_LINE: 2 + 52.5 29.3 + 53.5 30.7 +REG_LINE: 2 + 54.2 24.0 + 55.2 25.5 +REG_LINE: 2 + 55.9 18.8 + 56.9 20.2 +REG_LINE: 2 + 57.5 13.5 + 58.6 14.9 +REG_LINE: 2 + 60.9 2.99 + 61.9 4.43 +REG_LINE: 2 + 62.6 -2.26 + 63.6 -.831 +REG_LINE: 2 + 64.3 -7.52 + 65.3 -6.09 +REG_LINE: 2 + 66.0 -12.8 + 67.0 -11.3 +REG_LINE: 2 + 69.4 -23.3 + 70.4 -21.9 +REG_LINE: 2 + 71.1 -28.6 + 72.1 -27.1 +REG_LINE: 2 + 72.7 -33.8 + 73.7 -32.4 +REG_LINE: 2 + 74.4 -39.1 + 75.4 -37.6 +REG_LINE: 2 + 77.8 -49.6 + 78.8 -48.2 +REG_LINE: 2 + 79.5 -54.9 + 80.5 -53.4 +REG_LINE: 2 + 81.2 -60.1 + 82.2 -58.7 +REG_LINE: 2 + 82.9 -65.4 + 83.9 -63.9 +REG_LINE: 2 + 86.3 -75.9 + 87.3 -74.5 +REG_LINE: 2 + 87.9 -81.2 + 88.9 -79.7 +REG_LINE: 2 + 89.6 -86.4 + 90.6 -85.0 +REG_LINE: 2 + 91.3 -91.7 + 92.3 -90.2 +REG_LINE: 2 + 94.7 -102. + 95.7 -101. +REG_LINE: 2 + 96.4 -107. + 97.4 -106. +REG_LINE: 2 + 98.1 -113. + 99.1 -111. +REG_LINE: 2 + 99.8 -118. + 101. -117. +REG_LINE: 2 + 103. -128. + 104. -127. +REG_LINE: 2 + 105. -134. + 106. -132. +REG_LINE: 2 + 107. -139. + 108. -138. +REG_LINE: 2 + 108. -144. + 109. -143. +REG_LINE: 2 + 112. -155. + 113. -153. +REG_LINE: 2 + 113. -160. + 114. -159. +REG_LINE: 2 + 115. -165. + 116. -164. +REG_LINE: 2 + 117. -171. + 118. -169. +REG_LINE: 2 + 120. -181. + 121. -180. +REG_LINE: 2 + 122. -186. + 123. -185. +REG_LINE: 2 + 123. -192. + 124. -190. +REG_LINE: 2 + 125. -197. + 126. -195. +REG_LINE: 2 + -80.3 169. + -81.8 170. +REG_LINE: 2 + -82.5 148. + -84.2 148. +REG_LINE: 2 + -83.7 126. + -85.5 126. +REG_LINE: 2 + -83.8 104. + -85.6 104. +REG_LINE: 2 + -80.7 61.1 + -82.3 60.3 +REG_LINE: 2 + -77.6 40.2 + -79.0 39.1 +REG_LINE: 2 + -73.4 19.9 + -74.7 18.7 +REG_LINE: 2 + -68.3 .672 + -69.4 -.674 +REG_LINE: 2 + -55.2 -34.3 + -56.0 -35.9 +REG_LINE: 2 + -47.4 -49.7 + -48.1 -51.3 +REG_LINE: 2 + -38.9 -63.4 + -39.5 -65.1 +REG_LINE: 2 + -29.7 -75.4 + -30.2 -77.1 +REG_LINE: 2 + -9.89 -93.6 + -10.2 -95.3 +REG_LINE: 2 + .619 -99.6 + .416 -101. +REG_LINE: 2 + 11.4 -103. + 11.3 -105. +REG_LINE: 2 + 22.3 -105. + 22.2 -107. +REG_LINE: 2 + 44.1 -102. + 44.2 -104. +REG_LINE: 2 + 54.7 -97.3 + 55.0 -99.1 +REG_LINE: 2 + 65.1 -90.4 + 65.4 -92.2 +REG_LINE: 2 + 75.1 -81.5 + 75.5 -83.2 +REG_LINE: 2 + 93.5 -57.9 + 94.1 -59.5 +REG_LINE: 2 + 102. -43.5 + 102. -45.1 +REG_LINE: 2 + 109. -27.5 + 110. -29.0 +REG_LINE: 2 + 116. -10.1 + 117. -11.5 +REG_LINE: 2 + 126. 28.3 + 128. 27.1 +REG_LINE: 2 + 130. 48.8 + 131. 47.9 +REG_LINE: 2 + 133. 70.0 + 134. 69.3 +REG_LINE: 2 + 134. 91.6 + 136. 91.3 +REG_LINE: 2 + 134. 135. + 136. 136. +REG_LINE: 2 + 133. 157. + 134. 158. +REG_LINE: 2 + 130. 178. + 131. 179. +REG_LINE: 2 + 126. 199. + 128. 200. +REG_LINE: 2 + 116. 237. + 117. 238. +REG_LINE: 2 + 109. 254. + 110. 256. +REG_LINE: 2 + 102. 270. + 102. 272. +REG_LINE: 2 + 93.5 285. + 94.1 286. +REG_LINE: 2 + -38.9 290. + -39.5 292. +REG_LINE: 2 + -47.4 277. + -48.1 278. +REG_LINE: 2 + -55.2 261. + -56.0 263. +REG_LINE: 2 + -68.3 226. + -69.4 228. +REG_LINE: 2 + -73.4 207. + -74.7 208. +REG_LINE: 2 + -77.6 187. + -79.0 188. +REG_LINE: 2 + -80.7 166. + -82.3 167. +REG_LINE: 2 + -83.8 123. + -85.6 123. +REG_LINE: 2 + -83.7 101. + -85.5 100. +REG_LINE: 2 + -82.5 78.9 + -84.2 78.4 +REG_LINE: 2 + -80.3 57.5 + -81.8 56.7 +REG_LINE: 15 + 25.5 113. + 24.9 115. + 24.3 117. + 23.7 119. + 23.1 121. + 22.5 123. + 21.9 125. + 21.2 127. + 20.6 128. + 20.0 130. + 19.4 132. + 18.8 134. + 18.2 136. + 17.6 138. + 17.0 140. +REG_LINE: 15 + 25.5 113. + 26.1 112. + 26.7 110. + 27.3 108. + 27.9 106. + 28.5 104. + 29.1 102. + 29.7 100. + 30.3 98.4 + 30.9 96.5 + 31.5 94.7 + 32.1 92.8 + 32.7 90.9 + 33.3 89.0 + 33.9 87.1 +REG_LINE: 15 + 33.9 87.1 + 34.5 85.3 + 35.1 83.4 + 35.7 81.5 + 36.3 79.6 + 36.9 77.8 + 37.5 75.9 + 38.1 74.0 + 38.7 72.1 + 39.3 70.2 + 39.9 68.4 + 40.5 66.5 + 41.1 64.6 + 41.8 62.7 + 42.4 60.8 +REG_LINE: 15 + 42.4 60.8 + 43.0 59.0 + 43.6 57.1 + 44.2 55.2 + 44.8 53.3 + 45.4 51.5 + 46.0 49.6 + 46.6 47.7 + 47.2 45.8 + 47.8 43.9 + 48.4 42.1 + 49.0 40.2 + 49.6 38.3 + 50.2 36.4 + 50.8 34.5 +REG_LINE: 15 + 50.8 34.5 + 51.4 32.7 + 52.0 30.8 + 52.6 28.9 + 53.2 27.0 + 53.8 25.2 + 54.4 23.3 + 55.0 21.4 + 55.6 19.5 + 56.2 17.6 + 56.8 15.8 + 57.4 13.9 + 58.0 12.0 + 58.6 10.1 + 59.2 8.25 +REG_LINE: 15 + 59.2 8.25 + 59.8 6.38 + 60.4 4.50 + 61.0 2.62 + 61.6 .740 + 62.3 -1.14 + 62.9 -3.02 + 63.5 -4.89 + 64.1 -6.77 + 64.7 -8.65 + 65.3 -10.5 + 65.9 -12.4 + 66.5 -14.3 + 67.1 -16.2 + 67.7 -18.0 +REG_LINE: 15 + 67.7 -18.0 + 68.3 -19.9 + 68.9 -21.8 + 69.5 -23.7 + 70.1 -25.6 + 70.7 -27.4 + 71.3 -29.3 + 71.9 -31.2 + 72.5 -33.1 + 73.1 -34.9 + 73.7 -36.8 + 74.3 -38.7 + 74.9 -40.6 + 75.5 -42.5 + 76.1 -44.3 +REG_LINE: 15 + 76.1 -44.3 + 76.7 -46.2 + 77.3 -48.1 + 77.9 -50.0 + 78.5 -51.9 + 79.1 -53.7 + 79.7 -55.6 + 80.3 -57.5 + 80.9 -59.4 + 81.5 -61.2 + 82.2 -63.1 + 82.8 -65.0 + 83.4 -66.9 + 84.0 -68.8 + 84.6 -70.6 +REG_LINE: 15 + 84.6 -70.6 + 85.2 -72.5 + 85.8 -74.4 + 86.4 -76.3 + 87.0 -78.1 + 87.6 -80.0 + 88.2 -81.9 + 88.8 -83.8 + 89.4 -85.7 + 90.0 -87.5 + 90.6 -89.4 + 91.2 -91.3 + 91.8 -93.2 + 92.4 -95.1 + 93.0 -96.9 +REG_LINE: 15 + 93.0 -96.9 + 93.6 -98.8 + 94.2 -101. + 94.8 -103. + 95.4 -104. + 96.0 -106. + 96.6 -108. + 97.2 -110. + 97.8 -112. + 98.4 -114. + 99.0 -116. + 99.6 -118. + 100. -119. + 101. -121. + 101. -123. +REG_LINE: 15 + 101. -123. + 102. -125. + 103. -127. + 103. -129. + 104. -131. + 104. -133. + 105. -134. + 106. -136. + 106. -138. + 107. -140. + 107. -142. + 108. -144. + 109. -146. + 109. -148. + 110. -150. +REG_LINE: 15 + 110. -150. + 110. -151. + 111. -153. + 112. -155. + 112. -157. + 113. -159. + 114. -161. + 114. -163. + 115. -165. + 115. -166. + 116. -168. + 117. -170. + 117. -172. + 118. -174. + 118. -176. +REG_LINE: 14 + 118. -176. + 119. -178. + 120. -180. + 120. -181. + 121. -183. + 121. -185. + 122. -187. + 123. -189. + 123. -191. + 124. -193. + 124. -195. + 125. -196. + 126. -198. + 126. -200. +REG_LINE: 15 + -82.8 82.6 + -83.3 90.3 + -83.6 98.1 + -83.8 106. + -83.9 114. + -83.8 122. + -83.6 129. + -83.3 137. + -82.8 145. + -82.1 153. + -81.4 160. + -80.5 168. + -79.4 175. + -78.3 183. + -77.0 190. +REG_LINE: 15 + -82.8 82.6 + -82.2 74.9 + -81.4 67.2 + -80.5 59.6 + -79.5 52.0 + -78.3 44.6 + -77.1 37.2 + -75.6 29.9 + -74.1 22.8 + -72.4 15.7 + -70.6 8.81 + -68.7 2.01 + -66.6 -4.64 + -64.4 -11.1 + -62.2 -17.5 +REG_LINE: 15 + -62.2 -17.5 + -59.8 -23.7 + -57.3 -29.7 + -54.7 -35.5 + -51.9 -41.1 + -49.1 -46.5 + -46.2 -51.8 + -43.2 -56.8 + -40.2 -61.6 + -37.0 -66.1 + -33.7 -70.5 + -30.4 -74.6 + -27.0 -78.5 + -23.6 -82.1 + -20.0 -85.5 +REG_LINE: 15 + -20.0 -85.5 + -16.5 -88.6 + -12.8 -91.5 + -9.15 -94.1 + -5.42 -96.4 + -1.66 -98.5 + 2.14 -100. + 5.97 -102. + 9.83 -103. + 13.7 -104. + 17.6 -105. + 21.5 -105. + 25.4 -105. + 29.3 -105. + 33.2 -105. +REG_LINE: 15 + 33.2 -105. + 37.1 -104. + 41.0 -103. + 44.8 -102. + 48.7 -100. + 52.5 -98.5 + 56.2 -96.5 + 60.0 -94.2 + 63.6 -91.6 + 67.3 -88.7 + 70.9 -85.6 + 74.4 -82.2 + 77.8 -78.6 + 81.2 -74.7 + 84.6 -70.6 +REG_LINE: 15 + 84.6 -70.6 + 87.8 -66.3 + 91.0 -61.7 + 94.1 -56.9 + 97.1 -51.9 + 100. -46.7 + 103. -41.3 + 105. -35.7 + 108. -29.9 + 111. -23.9 + 113. -17.7 + 115. -11.4 + 117. -4.87 + 120. 1.78 + 121. 8.56 +REG_LINE: 15 + 121. 8.56 + 123. 15.5 + 125. 22.5 + 127. 29.7 + 128. 37.0 + 129. 44.3 + 130. 51.8 + 131. 59.3 + 132. 66.9 + 133. 74.6 + 134. 82.3 + 134. 90.0 + 135. 97.8 + 135. 106. + 135. 113. +REG_LINE: 15 + 135. 113. + 135. 121. + 135. 129. + 134. 137. + 134. 145. + 133. 152. + 132. 160. + 131. 168. + 130. 175. + 129. 183. + 128. 190. + 127. 197. + 125. 204. + 123. 211. + 121. 218. +REG_LINE: 15 + 121. 218. + 120. 225. + 117. 232. + 115. 238. + 113. 245. + 111. 251. + 108. 257. + 105. 263. + 103. 268. + 100. 274. + 97.1 279. + 94.1 284. + 91.0 289. + 87.8 293. + 84.6 298. +REG_LINE: 2 + 84.6 298. + 82.5 300. +REG_LINE: 12 + -31.6 300. + -33.7 297. + -37.0 293. + -40.2 288. + -43.2 284. + -46.2 279. + -49.1 273. + -51.9 268. + -54.7 262. + -57.3 257. + -59.8 251. + -62.2 244. +REG_LINE: 15 + -62.2 244. + -64.4 238. + -66.6 232. + -68.7 225. + -70.6 218. + -72.4 211. + -74.1 204. + -75.6 197. + -77.1 190. + -78.3 182. + -79.5 175. + -80.5 167. + -81.4 160. + -82.2 152. + -82.8 144. +REG_LINE: 15 + -82.8 144. + -83.3 137. + -83.6 129. + -83.8 121. + -83.9 113. + -83.8 105. + -83.6 97.5 + -83.3 89.8 + -82.8 82.0 + -82.1 74.3 + -81.4 66.7 + -80.5 59.0 + -79.4 51.5 + -78.3 44.1 + -77.0 36.7 +REG_LINE: 15 + -82.8 144. + -83.3 137. + -83.6 129. + -83.8 121. + -83.9 113. + -83.8 105. + -83.6 97.5 + -83.3 89.8 + -82.8 82.0 + -82.1 74.3 + -81.4 66.7 + -80.5 59.0 + -79.4 51.5 + -78.3 44.1 + -77.0 36.7 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0.0' + 27.8 114. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '50.0' + 36.3 87.9 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '100.0' + 44.7 61.6 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '150.0' + 53.2 35.3 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '200.0' + 61.6 9.02 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '250.0' + 70.1 -17.3 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '300.0' + 78.5 -43.6 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '350.0' + 86.9 -69.9 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '400.0' + 95.4 -96.2 BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '450.0' + 104. -122. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '500.0' + 112. -149. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '550.0' + 121. -175. BC .952 .306 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +Symbol = "p1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 2" # Axis Label + Symbol = "p2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + InvB = 1 # Second Mapping used in inverse direction + MapA = # First component Mapping + Begin MathMap # Transformation using mathematical functions + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Fwd1 = "r=sqrt(x*x+y*y)" # Forward function 1 + Fwd2 = "theta=atan2(y,x)" # Forward function 2 + Inv1 = "x=r*cos(theta)" # Inverse function 1 + Inv2 = "y=r*sin(theta)" # Inverse function 2 + SimpFI = 1 # Forward-inverse pairs may simplify + SimpIF = 1 # Inverse-forward pairs may simplify + End MathMap + MapB = # Second component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -101.5 # Shift for axis 1 + Sft2 = -201.5 # Shift for axis 2 + End WinMap + End CmpMap + End FrameSet + Begin FrameSet # Set of inter-related coordinate systems +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic projection" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Epoch = 1977.77512999212 # Besselian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# System = "FK5" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + IsA Frame # Coordinate system description + Nframe = 2 # Number of Frames in FrameSet + Base = 1 # Index of base Frame + Currnt = 2 # Index of current Frame + Lnk2 = 1 # Node 2 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Pixel Coordinates" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Pixel axis 1" # Label for axis 1 +# Lbl2 = "Pixel axis 2" # Label for axis 2 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel axis 1" # Axis Label + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel axis 2" # Axis Label + End Axis + End Frame + Frm2 = # Frame number 2 + Begin SkyFrame # Description of celestial coordinate system + Ident = " " # Permanent Object identification string + IsA Object # AST Object +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic projection" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain + Epoch = 1977.77512999212 # Besselian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 + System = "FK5" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + Proj = "gnomonic" # Description of sky projection + Eqnox = 2000 # Julian epoch of mean equinox + SRefIs = "Ignored" # Not rotated (ref. pos. is ignored) + SRef1 = 0 # Ref. pos. RA 0:00:00.0 + SRef2 = -1.57079633000002 # Ref. pos. Dec -90:00:00 + End SkyFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -893.6318379289 # Shift for axis 1 + Sft2 = -223.8380193875 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -3.25441534352674e-06 # Forward matrix value + M1 = -1.60367292352974e-08 # Forward matrix value + M2 = -1.812057487023e-08 # Forward matrix value + M3 = 3.25725533992408e-06 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitREG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-3.0' + -80.3 82.7 BC .997 0.724E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-2.5' + -59.8 -16.6 BC .936 .352 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-2.0' + -18.4 -83.6 BC .673 .740 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-1.5' + 32.8 -102. BC -.145 .989 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-0.5' + 119. 9.22 BC -.965 .262 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0.0' + 132. 113. BC -1.00 0.125E-02 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0.5' + 119. 218. TC .964 .265 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '1.0' + 82.6 296. TC .787 .617 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '2.5' + -59.8 243. TC -.936 .352 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '3.0' + -80.3 144. TC -.997 0.724E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 1 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: ' A FITS test' + 31.0 313. BC .000 1.00 + + + + + FITS test number 3 + ==================== + + + +AST_SHOW: + +REG_SINK: +SIMPLE = T / file does conform to FITS standard +BITPIX = 16 / number of bits per data pixel +NAXIS = 2 / number of data axes +NAXIS1 = 1787 / length of data axis 1 +NAXIS2 = 447 / length of data axis 2 +EXTEND = T / FITS dataset may contain extensions +COMMENT FITS (Flexible Image Transport System) format defined in Astronomy and +COMMENT Astrophysics Supplement Series v44/p363, v44/p371, v73/p359, v73/p365. +COMMENT Contact the NASA Science Office of Standards and Technology for the +COMMENT FITS Definition document #100 and other FITS information. +PLATENUM= '3665 ' / Plate number +EMULSION= 'IIIaJ ' / Kodak emulsion type +FILTER = 'GG395 ' / Schott glass filter type +PLTSCALE= '67.14 ' / [arcsec/mm] plate scale +FIELDNUM= '1 ' / Sky survey field number +TELESCOP= 'UKST ' / Telescope on which the plate was taken +TELETYPE= 'SCHM ' / Type of telescope +SITELAT = -0.5458410576565 / [radians] latitude of telescope +SITELONG= 2.601766194458 / [radians] longitude of telescope +LST = '00:20 ' / [hh:mm] local sidereal time at start of obs +INSTRUME= 'SuperCOSMOS I' / Measuring machine +DATE-MES= '2000-11-04' / [yyyy-mm-dd] Date of this plate measurement +NHKLINES= 146 / Number of lines from house-keeping file +HKLIN001= 'JOB.JOBNO UKJ001' +HKLIN002= 'JOB.DATE-MES 2000:11:04' +HKLIN003= 'JOB.TIME 12:51:09' +HKLIN004= 'JOB.INSTRUME SuperCOSMOS I' +HKLIN005= 'JOB.ORIGIN Royal Observatory Edinburgh' +HKLIN006= 'JOB.SOFTWARE /home/scosdev/v033' +HKLIN007= 'JOB.OPERATOR ebt' +HKLIN008= 'JOB.USER htm' +HKLIN009= 'JOB.USERREF NONE' +HKLIN010= 'JOB.UORIGIN ROE' +HKLIN011= 'JOB.UCOUNTRY uk' +HKLIN012= 'JOB.COMMENT Digital catalogue of the Sky' +HKLIN013= 'JOB.IAM_FILE iam.srt'/ / ' / +HKLIN014= 'PLATE.TELESCOP UKST' +HKLIN015= 'PLATE.TELTYPE SCHM' +HKLIN016= 'PLATE.PLATE 3665' +HKLIN017= 'PLATE.MATERIAL 3mm glass' +HKLIN018= 'PLATE.EMULSION IIIaJ' +HKLIN019= 'PLATE.FILTER GG395' +HKLIN020= 'PLATE.PSCALE 67.14' +HKLIN021= 'PLATE.FIELD 1' +HKLIN022= 'PLATE.RA_PNT 0' +HKLIN023= 'PLATE.DEC_PNT -90' +HKLIN024= 'PLATE.RADECSYS FK4' +HKLIN025= 'PLATE.EQUINOX 1950' +HKLIN026= 'PLATE.TIMESYS BESSELIAN' +HKLIN027= 'PLATE.EPOCH 1977.78'/ / ' / +HKLIN028= 'PLATE.EXPOSURE 75' +HKLIN029= 'PLATE.UTDATE 771011' +HKLIN030= 'PLATE.LST 0020' +HKLIN031= 'PLATE.MJD 43426.573008796' +HKLIN032= 'PLATE.TELLAT -0.54584105765654' +HKLIN033= 'PLATE.TELLONG 2.6017661944583' +HKLIN034= 'PLATE.TELHT 1145' +HKLIN035= 'PLATE.TEMP 273.155'/ / ' / +HKLIN036= 'PLATE.ATMOSP 1013.25'/ / ' / +HKLIN037= 'PLATE.HUMID 0.5' +HKLIN038= 'PLATE.WAVE 4500' +HKLIN039= 'PLATE.TROPL 0.0065' +HKLIN040= 'CALIBRATION.CALTYPE SPLINE' +HKLIN041= 'CALIBRATION.STEPWEDG KPNO' +HKLIN042= 'CALIBRATION.NSTEPS 8' +HKLIN043= 'MEASUREMENT.ORIENTAT news' +HKLIN044= 'MEASUREMENT.EMULPOS UP' +HKLIN045= 'MEASUREMENT.SCANFILT 14' +HKLIN046= 'MEASUREMENT.SOSP 552' +HKLIN047= 'MEASUREMENT.STEPSIZE 10' +HKLIN048= 'MEASUREMENT.SCANLEN 1152' +HKLIN049= 'MEASUREMENT.A-XMIN 1622000'/ / ' / +HKLIN050= 'MEASUREMENT.A-YMIN 1622000'/ / ' / +HKLIN051= 'MEASUREMENT.A-XMAX 33878000' +HKLIN052= 'MEASUREMENT.A-YMAX 33878000' +HKLIN053= 'MEASUREMENT.X_PNT 17500000' +HKLIN054= 'MEASUREMENT.Y_PNT 18000000' +HKLIN055= 'ANALYSIS.NPARAMS 32' +HKLIN056= 'ANALYSIS.AREACUT 8' +HKLIN057= 'ANALYSIS.AP-PARAM 1.07' +HKLIN058= 'DEBLEND.DB-PARAM 1.05' +HKLIN059= 'DEBLEND.DB-AMIN 16' +HKLIN060= 'DEBLEND.DB-AMAX 100000' +HKLIN061= 'DEBLEND.DB-ACUT 8' +HKLIN062= 'DEBLEND.DB-LEVEL 16' +HKLIN063= 'DEBLEND.SELECT PARENT+CHILD' +HKLIN064= 'SKY.SKYSQUAR 64' +HKLIN065= 'SKY.SKYDEFN MEDIAN' +HKLIN066= 'SKY.SKYFILTR bdkjunk'/ / ' / +HKLIN067= 'SKY.F-THRESH 8' +HKLIN068= 'SKY.F-SCLEN 4' +HKLIN069= 'THRESHOLDING.PCUT 10' +HKLIN070= 'IAMQC.AREAMIN 8' +HKLIN071= 'IAMQC.AREAMAX 77346' +HKLIN072= 'IAMQC.MINMAG -30515' +HKLIN073= 'IAMQC.MAXMAG -17954' +HKLIN074= 'IAMQC.MINELL 0.0004156232' +HKLIN075= 'IAMQC.MAXELL 1' +HKLIN076= 'IAMQC.MODELL 0.14' +HKLIN077= 'IAMQC.MODOR 91' +HKLIN078= 'IAMQC.MIDELL 0.21' +HKLIN079= 'IAMQC.MIDOR 93' +HKLIN080= 'IAMQC.MEANELL 0.2467037' +HKLIN081= 'IAMQC.MEANOR 91.63474' +HKLIN082= 'IAMQC.NUMOBJ 556985' +HKLIN083= 'IAMQC.PARENTS 486656' +HKLIN084= 'IAMQC.RANGING TRUE' +HKLIN085= 'IAMQC.LANE_1 15571' +HKLIN086= 'IAMQC.LANE_2 33207' +HKLIN087= 'IAMQC.LANE_3 51478' +HKLIN088= 'IAMQC.LANE_4 69944' +HKLIN089= 'IAMQC.LANE_5 89236' +HKLIN090= 'IAMQC.LANE_6 108416' +HKLIN091= 'IAMQC.LANE_7 127481' +HKLIN092= 'IAMQC.LANE_8 146699' +HKLIN093= 'IAMQC.LANE_9 166380' +HKLIN094= 'IAMQC.LANE_10 186126' +HKLIN095= 'IAMQC.LANE_11 205946' +HKLIN096= 'IAMQC.LANE_12 225915' +HKLIN097= 'IAMQC.LANE_13 245926' +HKLIN098= 'IAMQC.LANE_14 266574' +HKLIN099= 'IAMQC.LANE_15 287150' +HKLIN100= 'IAMQC.LANE_16 308087' +HKLIN101= 'IAMQC.LANE_17 328830' +HKLIN102= 'IAMQC.LANE_18 350253' +HKLIN103= 'IAMQC.LANE_19 370738' +HKLIN104= 'IAMQC.LANE_20 391722' +HKLIN105= 'IAMQC.LANE_21 412801' +HKLIN106= 'IAMQC.LANE_22 433795' +HKLIN107= 'IAMQC.LANE_23 454383' +HKLIN108= 'IAMQC.LANE_24 474711' +HKLIN109= 'IAMQC.LANE_25 495108' +HKLIN110= 'IAMQC.LANE_26 515755' +HKLIN111= 'IAMQC.LANE_27 536499' +HKLIN112= 'IAMQC.LANE_28 556985' +HKLIN113= 'XYTORADEC.STARCAT /sdata/scos/refcats/tycho2.FIT' +HKLIN114= 'XYTORADEC.BRIGHTLIM 9' +HKLIN115= 'XYTORADEC.C-EQUIN 2000' +HKLIN116= 'XYTORADEC.C-EQTSYS JULIAN' +HKLIN117= 'XYTORADEC.C-EPOCH 2000' +HKLIN118= 'XYTORADEC.C-EPTSYS JULIAN' +HKLIN119= 'XYTORADEC.R-EQUIN 2000' +HKLIN120= 'XYTORADEC.R-TSYS JULIAN' +HKLIN121= 'XYTORADEC.MAXITER 5000' +HKLIN122= 'XYTORADEC.RCRITINI 500000' +HKLIN123= 'XYTORADEC.RCRITABS 50000' +HKLIN124= 'XYTORADEC.RCRITREL 1' +HKLIN125= 'XYTORADEC.RCRITFIN 3' +HKLIN126= 'XYTORADEC.HARDCOPY /scos1/scos/UKJ001/UKJ001.ps' +HKLIN127= 'XYTORADEC.REFSMULT 5' +HKLIN128= 'XYTORADEC.RESDMULT 1000' +HKLIN129= 'XYTORADEC.RACOL RA' +HKLIN130= 'XYTORADEC.DECOL DEC' +HKLIN131= 'XYTORADEC.RAPMCOL PMRA' +HKLIN132= 'XYTORADEC.DECPMCOL PMDE' +HKLIN133= 'XYTORADEC.PLXCOL NONE' +HKLIN134= 'XYTORADEC.RVCOL NONE' +HKLIN135= 'XYTORADEC.MAGCOL VT' +HKLIN136= 'XYTORADEC.STARSC 2374' +HKLIN137= 'XYTORADEC.STARSU 1727' +HKLIN138= 'XYTORADEC.COEFFS_1 17.640343856524' +HKLIN139= 'XYTORADEC.COEFFS_2 -260.44151995641' +HKLIN140= 'XYTORADEC.COEFFS_3 -163.09155572601' +HKLIN141= 'XYTORADEC.COEFFS_4 17.504230442205' +HKLIN142= 'XYTORADEC.COEFFS_5 -163.08676953832' +HKLIN143= 'XYTORADEC.COEFFS_6 260.48817907668' +HKLIN144= 'XYTORADEC.DISTR -0.33333333333333' +HKLIN145= 'XYTORADEC.RA_PNT 0.54924996662137' +HKLIN146= 'XYTORADEC.DEC_PNT -1.5684931501781' +HISTORY = 'SuperCOSMOS image analysis and mapping mode (IAM and MM)' / +HISTORY = 'data written by xydcomp_ss.' / +HISTORY = 'Any questions/comments/suggestions/bug reports should be sent' / +HISTORY = 'to N.Hambly@roe.ac.uk' / +ASTSIGX = 0.37 / [arcsec] std. dev. of astrometric fit in X +ASTSIGY = 0.38 / [arcsec] std. dev. of astrometric fit in Y +PC001001= 1.0 / DEPRECATED - Axis rotation matrix +PC001002= 0.004927623810613 / DEPRECATED - Axis rotation matrix +PC002001= -0.005563056187788 / DEPRECATED - Axis rotation matrix +PC002002= 1.0 / DEPRECATED - Axis rotation matrix +CROTA2 = 0.3005532298491 / DEPRECATED - rotation of axis 2 +DATATYPE= 'INTEGER*2' / Type of data +DATUNITS= 'DENSITY ' / Units: transmission, density or intensity +XPIXELSZ= 9.997114974 / [microns] X pixel size +YPIXELSZ= 10.0 / [microns] Y pixel size +OBJCTRA = ' 0 0 0.000' / Centre Right Ascension (J2000) +OBJCTDEC= '-90 0 0.00' / Centre Declination (J2000) +OBJCTX = 16368.63183793 / [pixels] Centre X on plate +OBJCTY = 14740.83801939 / [pixels] Centre Y on plate + +Objects written: 1 + +Native Encoding: + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST WCS information in AST format AST +COMMENT AST See http://www.starlink.ac.uk/ast/ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 2 / Number of Frames in FrameSet +BASE_A = 1 / Index of base Frame +CURRNT_A= 2 / Index of current Frame +LNK2_A = 1 / Node 2 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Pixel Coordinates' / Title of coordinate system +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Pixel axis 1' / Axis Label +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Pixel axis 2' / Axis Label +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'SkyFrame' / Description of celestial coordinate system +IDENT_A = '" " ' / Permanent Object identification string +ISA_A = 'Object ' / AST Object +NAXES_B = 2 / Number of coordinate axes +EPOCH_A = 1977.77512999212 / Besselian epoch of observation +SYSTEM_A= 'FK5 ' / Coordinate system type +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'SkyAxis ' / Celestial coordinate axis +ENDAST_D= 'SkyAxis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'SkyAxis ' / Celestial coordinate axis +ENDAST_E= 'SkyAxis ' / End of object definition +ISA_B = 'Frame ' / Coordinate system description +PROJ_A = 'gnomonic' / Description of sky projection +EQNOX_A = 2000.0 / Julian epoch of mean equinox +SREFIS_A= 'Ignored ' / Not rotated (ref. pos. is ignored) +SREF1_A = 0.0 / Ref. pos. RA 0:00:00.0 +SREF2_A = -1.57079633 / Ref. pos. Dec -90:00:00 +ENDAST_F= 'SkyFrame' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_H= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISSIMP_A= 1 / Mapping has been simplified +ISA_C = 'Mapping ' / Mapping between coordinate systems +MAPA_A = ' ' / First component Mapping +BEGAST_I= 'WinMap ' / Map one window on to another +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_D = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -893.6318379289 / Shift for axis 1 +SFT2_A = -223.8380193875 / Shift for axis 2 +ENDAST_G= 'WinMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_J= 'CmpMap ' / Compound Mapping +NIN_C = 2 / Number of input coordinates +ISA_E = 'Mapping ' / Mapping between coordinate systems +MAPA_B = ' ' / First component Mapping +BEGAST_K= 'MatrixMap' / Matrix transformation +NIN_D = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_F = 'Mapping ' / Mapping between coordinate systems +M0_A = -3.25441534352674E-6/ Forward matrix value +M1_A = -1.60367292352974E-8/ Forward matrix value +M2_A = -1.812057487023E-8 / Forward matrix value +M3_A = 3.25725533992408E-6 / Forward matrix value +FORM_A = 'Full ' / Matrix storage form +ENDAST_H= 'MatrixMap' / End of object definition +MAPB_B = ' ' / Second component Mapping +BEGAST_L= 'CmpMap ' / Compound Mapping +NIN_E = 2 / Number of input coordinates +ISA_G = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +MAPA_C = ' ' / First component Mapping +BEGAST_M= 'WcsMap ' / FITS-WCS sky projection +NIN_F = 2 / Number of input coordinates +INVERT_C= 1 / Mapping inverted +ISA_H = 'Mapping ' / Mapping between coordinate systems +TYPE_A = 'TAN ' / Gnomonic projection +ENDAST_I= 'WcsMap ' / End of object definition +MAPB_C = ' ' / Second component Mapping +BEGAST_N= 'CmpMap ' / Compound Mapping +NIN_G = 2 / Number of input coordinates +ISA_I = 'Mapping ' / Mapping between coordinate systems +INVA_B = 1 / First Mapping used in inverse direction +MAPA_D = ' ' / First component Mapping +BEGAST_O= 'SphMap ' / Cartesian to Spherical mapping +NIN_H = 3 / Number of input coordinates +NOUT_A = 2 / Number of output coordinates +INVERT_D= 1 / Mapping inverted +ISA_J = 'Mapping ' / Mapping between coordinate systems +UNTRD_A = 1 / All input vectors have unit length +PLRLG_A = 0.0 / Polar longitude (rad.s) +ENDAST_J= 'SphMap ' / End of object definition +MAPB_D = ' ' / Second component Mapping +BEGAST_P= 'CmpMap ' / Compound Mapping +NIN_I = 3 / Number of input coordinates +NOUT_B = 2 / Number of output coordinates +ISA_K = 'Mapping ' / Mapping between coordinate systems +MAPA_E = ' ' / First component Mapping +BEGAST_Q= 'MatrixMap' / Matrix transformation +NIN_J = 3 / Number of input coordinates +INVERT_E= 0 / Mapping not inverted +ISA_L = 'Mapping ' / Mapping between coordinate systems +M0_B = -1.0 / Forward matrix value +M1_B = 1.0 / Forward matrix value +M2_B = -1.0 / Forward matrix value +FORM_B = 'Diagonal' / Matrix storage form +ENDAST_K= 'MatrixMap' / End of object definition +MAPB_E = ' ' / Second component Mapping +BEGAST_R= 'SphMap ' / Cartesian to Spherical mapping +NIN_K = 3 / Number of input coordinates +NOUT_C = 2 / Number of output coordinates +INVERT_F= 0 / Mapping not inverted +ISA_M = 'Mapping ' / Mapping between coordinate systems +UNTRD_B = 1 / All input vectors have unit length +PLRLG_B = 0.0 / Polar longitude (rad.s) +ENDAST_L= 'SphMap ' / End of object definition +ENDAST_M= 'CmpMap ' / End of object definition +ENDAST_N= 'CmpMap ' / End of object definition +ENDAST_O= 'CmpMap ' / End of object definition +ENDAST_P= 'CmpMap ' / End of object definition +ENDAST_Q= 'CmpMap ' / End of object definition +ENDAST_R= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST + +ATTRIBUTES: + Colour(axis1) : 1 + Font(Stri) : 1 + Nout : 1 + Class : 4 + Tol : 0.100000E-01 + Gap(1) : .523599 + Border : 0 + Invert : 0 + TextLabGap : 0.100000E-01 + Nin : 2 + Current : 3 + Base : 1 + Nobject : 1 + RefCOUNT : 1 + +AST_GRID: +REG_LINE: 2 + 15.5 -136. + 15.4 -137. +REG_LINE: 2 + 18.6 -144. + 18.5 -146. +REG_LINE: 2 + 21.8 -149. + 21.8 -151. +REG_LINE: 2 + 25.1 -150. + 25.1 -154. +REG_LINE: 2 + 28.3 -149. + 28.4 -150. +REG_LINE: 2 + 31.5 -143. + 31.6 -145. +REG_LINE: 2 + 34.6 -135. + 34.7 -136. +REG_LINE: 2 + 37.6 -123. + 37.8 -127. +REG_LINE: 2 + 40.3 -108. + 40.4 -110. +REG_LINE: 2 + 42.7 -91.0 + 42.9 -92.7 +REG_LINE: 2 + 44.9 -71.2 + 45.2 -72.9 +REG_LINE: 2 + 46.7 -49.3 + 47.5 -53.0 +REG_LINE: 2 + 48.1 -25.8 + 48.6 -27.4 +REG_LINE: 2 + 49.2 -.928 + 49.9 -2.51 +REG_LINE: 2 + 49.8 24.8 + 51.0 23.5 +REG_LINE: 2 + 50.0 50.9 + 53.7 51.1 +REG_LINE: 2 + 49.7 77.1 + 50.9 78.4 +REG_LINE: 2 + 49.1 103. + 49.8 104. +REG_LINE: 2 + 48.0 127. + 48.5 129. +REG_LINE: 2 + 46.6 151. + 47.3 155. +REG_LINE: 2 + 44.7 173. + 45.0 174. +REG_LINE: 2 + 42.6 192. + 42.8 194. +REG_LINE: 2 + 40.1 209. + 40.2 211. +REG_LINE: 2 + 37.4 224. + 37.6 228. +REG_LINE: 2 + 34.4 235. + 34.5 237. +REG_LINE: 2 + 31.3 244. + 31.4 245. +REG_LINE: 2 + 28.1 248. + 28.1 250. +REG_LINE: 2 + 24.8 250. + 24.8 254. +REG_LINE: 2 + 21.6 248. + 21.5 250. +REG_LINE: 2 + 18.4 243. + 18.3 245. +REG_LINE: 2 + 15.3 234. + 15.2 236. +REG_LINE: 2 + 12.3 223. + 12.1 226. +REG_LINE: 2 + 9.62 208. + 9.45 210. +REG_LINE: 2 + 7.17 191. + 6.95 192. +REG_LINE: 2 + 5.02 171. + 4.74 173. +REG_LINE: 2 + 3.22 149. + 2.41 153. +REG_LINE: 2 + 1.78 125. + 1.27 127. +REG_LINE: 2 + .747 101. + -0.657E-02 102. +REG_LINE: 2 + .124 74.8 + -1.11 76.1 +REG_LINE: 2 + -0.740E-01 48.7 + -3.82 48.5 +REG_LINE: 2 + .156 22.6 + -1.02 21.3 +REG_LINE: 2 + .811 -3.08 + 0.841E-01 -4.67 +REG_LINE: 2 + 1.88 -27.8 + 1.38 -29.5 +REG_LINE: 2 + 3.34 -51.3 + 2.56 -54.9 +REG_LINE: 2 + 5.17 -73.0 + 4.89 -74.7 +REG_LINE: 2 + 7.34 -92.6 + 7.13 -94.3 +REG_LINE: 2 + 9.81 -110. + 9.65 -111. +REG_LINE: 2 + 12.5 -124. + 12.3 -128. +REG_LINE: 2 + 15.5 -136. + 15.4 -137. +REG_LINE: 2 + 18.6 -144. + 18.5 -146. +REG_LINE: 2 + 21.8 -149. + 21.8 -151. +REG_LINE: 2 + 25.1 -150. + 25.1 -154. +REG_LINE: 2 + 28.3 -149. + 28.4 -150. +REG_LINE: 2 + 31.5 -143. + 31.6 -145. +REG_LINE: 2 + 34.6 -135. + 34.7 -136. +REG_LINE: 2 + 24.9 74.8 + 26.5 75.6 +REG_LINE: 2 + 24.9 99.9 + 26.5 101. +REG_LINE: 2 + 24.9 125. + 26.5 126. +REG_LINE: 2 + 24.9 150. + 28.3 152. +REG_LINE: 2 + 24.9 175. + 26.5 176. +REG_LINE: 2 + 24.9 200. + 26.4 201. +REG_LINE: 2 + 24.8 225. + 26.4 226. +REG_LINE: 2 + 24.8 250. + 28.2 252. +REG_LINE: 2 + 24.8 275. + 26.4 276. +REG_LINE: 15 + 25.1 -150. + 24.1 -150. + 23.2 -150. + 22.3 -149. + 21.3 -148. + 20.4 -147. + 19.5 -146. + 18.6 -144. + 17.7 -142. + 16.8 -140. + 15.9 -137. + 15.1 -134. + 14.2 -131. + 13.4 -128. + 12.5 -124. +REG_LINE: 15 + 25.1 -150. + 26.0 -150. + 26.9 -150. + 27.9 -149. + 28.8 -148. + 29.7 -147. + 30.6 -145. + 31.5 -143. + 32.4 -141. + 33.3 -139. + 34.2 -136. + 35.1 -133. + 35.9 -130. + 36.7 -127. + 37.6 -123. +REG_LINE: 15 + 37.6 -123. + 38.4 -119. + 39.1 -115. + 39.9 -111. + 40.6 -106. + 41.4 -101. + 42.1 -96.2 + 42.7 -91.0 + 43.4 -85.6 + 44.0 -79.9 + 44.6 -74.2 + 45.2 -68.2 + 45.7 -62.1 + 46.2 -55.8 + 46.7 -49.3 +REG_LINE: 15 + 46.7 -49.3 + 47.1 -42.8 + 47.5 -36.1 + 47.9 -29.2 + 48.3 -22.3 + 48.6 -15.3 + 48.9 -8.13 + 49.2 -.928 + 49.4 6.35 + 49.6 13.7 + 49.7 21.1 + 49.8 28.5 + 49.9 36.0 + 50.0 43.4 + 50.0 50.9 +REG_LINE: 15 + 50.0 50.9 + 49.9 58.4 + 49.9 65.9 + 49.8 73.3 + 49.7 80.8 + 49.5 88.1 + 49.3 95.5 + 49.1 103. + 48.8 110. + 48.5 117. + 48.2 124. + 47.8 131. + 47.4 138. + 47.0 144. + 46.6 151. +REG_LINE: 15 + 46.6 151. + 46.1 157. + 45.6 164. + 45.0 170. + 44.4 176. + 43.8 181. + 43.2 187. + 42.6 192. + 41.9 197. + 41.2 202. + 40.5 207. + 39.7 212. + 38.9 216. + 38.2 220. + 37.4 224. +REG_LINE: 15 + 37.4 224. + 36.5 227. + 35.7 231. + 34.8 234. + 34.0 237. + 33.1 239. + 32.2 241. + 31.3 244. + 30.4 245. + 29.5 247. + 28.6 248. + 27.6 249. + 26.7 250. + 25.8 250. + 24.8 250. +REG_LINE: 15 + 24.8 250. + 23.9 250. + 23.0 249. + 22.0 249. + 21.1 248. + 20.2 246. + 19.3 245. + 18.4 243. + 17.5 241. + 16.6 238. + 15.7 236. + 14.8 233. + 14.0 230. + 13.1 226. + 12.3 223. +REG_LINE: 15 + 12.3 223. + 11.5 219. + 10.8 215. + 9.99 210. + 9.25 206. + 8.53 201. + 7.84 196. + 7.17 191. + 6.52 185. + 5.90 180. + 5.31 174. + 4.74 168. + 4.20 162. + 3.70 155. + 3.22 149. +REG_LINE: 15 + 3.22 149. + 2.77 142. + 2.35 136. + 1.96 129. + 1.61 122. + 1.29 115. + 1.00 108. + .747 101. + .526 93.3 + .339 85.9 + .187 78.6 + 0.696E-01 71.1 + -0.132E-01 63.7 + -0.611E-01 56.2 + -0.740E-01 48.7 +REG_LINE: 15 + -0.740E-01 48.7 + -0.519E-01 41.2 + 0.519E-02 33.7 + 0.971E-01 26.3 + .224 18.9 + .385 11.5 + .581 4.18 + .811 -3.08 + 1.07 -10.3 + 1.37 -17.4 + 1.70 -24.4 + 2.06 -31.3 + 2.46 -38.1 + 2.88 -44.7 + 3.34 -51.3 +REG_LINE: 15 + 3.34 -51.3 + 3.83 -57.7 + 4.34 -63.9 + 4.89 -70.0 + 5.46 -75.9 + 6.06 -81.6 + 6.69 -87.2 + 7.34 -92.6 + 8.02 -97.7 + 8.72 -103. + 9.44 -107. + 10.2 -112. + 11.0 -116. + 11.7 -120. + 12.5 -124. +REG_LINE: 15 + 12.5 -124. + 13.4 -128. + 14.2 -131. + 15.1 -134. + 15.9 -137. + 16.8 -140. + 17.7 -142. + 18.6 -144. + 19.5 -146. + 20.4 -147. + 21.3 -148. + 22.3 -149. + 23.2 -150. + 24.1 -150. + 25.1 -150. +REG_LINE: 15 + 25.1 -150. + 26.0 -150. + 26.9 -150. + 27.9 -149. + 28.8 -148. + 29.7 -147. + 30.6 -145. + 31.5 -143. + 32.4 -141. + 33.3 -139. + 34.2 -136. + 35.1 -133. + 35.9 -130. + 36.7 -127. + 37.6 -123. +REG_LINE: 15 + 25.1 -150. + 26.0 -150. + 26.9 -150. + 27.9 -149. + 28.8 -148. + 29.7 -147. + 30.6 -145. + 31.5 -143. + 32.4 -141. + 33.3 -139. + 34.2 -136. + 35.1 -133. + 35.9 -130. + 36.7 -127. + 37.6 -123. +REG_LINE: 15 + 24.9 49.8 + 24.9 57.0 + 24.9 64.1 + 24.9 71.3 + 24.9 78.4 + 24.9 85.6 + 24.9 92.7 + 24.9 99.9 + 24.9 107. + 24.9 114. + 24.9 121. + 24.9 128. + 24.9 136. + 24.9 143. + 24.9 150. +REG_LINE: 15 + 24.9 150. + 24.9 157. + 24.9 164. + 24.9 171. + 24.9 179. + 24.9 186. + 24.9 193. + 24.9 200. + 24.9 207. + 24.8 214. + 24.8 221. + 24.8 229. + 24.8 236. + 24.8 243. + 24.8 250. +REG_LINE: 8 + 24.8 250. + 24.8 257. + 24.8 264. + 24.8 272. + 24.8 279. + 24.8 286. + 24.8 293. + 24.8 300. +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '12' + 24.9 -148. BC -0.654E-01 .998 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '14' + 35.1 -123. BC -.978 .210 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '16' + 44.2 -49.2 BC -.997 0.716E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '18' + 47.5 50.9 BC -1.00 -0.942E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '20' + 44.1 151. TC .997 0.732E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '22' + 34.9 223. TC .977 .213 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '0' + 24.9 248. TC -0.236E-01 1.00 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '2' + 14.8 222. TC -.978 .210 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '4' + 5.71 149. TC -.997 0.716E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '6' + 2.43 48.7 BC 1.00 0.288E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '8' + 5.83 -51.1 BC .997 0.732E-01 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '10' + 15.0 -124. BC .977 .215 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-90:00' + 22.4 49.8 BC -1.00 -0.615E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '59' + 22.4 150. BC -1.00 -0.615E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: '-89:58' + 22.3 250. BC -1.00 -0.615E-03 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 1 +REG_CAP: 2 +REG_SCALES: +REG_QCH: +REG_CAP: 0 +REG_CAP: 0 +REG_TEXT: ' A FITS test' + 30.9 313. BC .000 1.00 +ude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -1 # Forward matrix value + M1 = 1 # Forward matrix value + M2 = -1 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End FrameSet diff --git a/ast_tester/rigby.map b/ast_tester/rigby.map new file mode 100644 index 0000000..6482149 --- /dev/null +++ b/ast_tester/rigby.map @@ -0,0 +1,240 @@ + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.1 # Forward matrix value + M1 = 1 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -0.0500000000000114 # Shift for axis 1 + End WinMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Sft1 = -3129 # Shift for axis 1 + Sft2 = -8 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin PermMap # Coordinate permutation + Nin = 2 # Number of input coordinates + Nout = 3 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Out1 = 1 # Output coordinate 1 = input coordinate 1 + Out2 = -1 # Output coordinate 2 = constant no. 1 + Out3 = 2 # Output coordinate 3 = input coordinate 2 + In1 = 1 # Input coordinate 1 = output coordinate 1 + In2 = 3 # Input coordinate 2 = output coordinate 3 + Nconst = 1 # Number of constants + Con1 = 232.84 # Constant number 1 + End PermMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.812968257918776 # Forward matrix value + M1 = 0.582308004080753 # Forward matrix value + M2 = 0 # Forward matrix value + M3 = -0.582308004080753 # Forward matrix value + M4 = 0.812968257918776 # Forward matrix value + M5 = 0 # Forward matrix value + M6 = 0 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 1 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -1236.82078999603 # Shift for axis 1 + Scl1 = 0.999999999977888 # Scale factor for axis 1 + Sft2 = 586.515648677204 # Shift for axis 2 + Scl2 = 0.999999999985306 # Scale factor for axis 2 + Sft3 = 330420868230.796 # Shift for axis 3 + Scl3 = 551361.368168752 # Scale factor for axis 3 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -3.28230370472069e-05 # Forward matrix value + M1 = -1.68687502681513e-05 # Forward matrix value + M2 = -1.68687502681513e-05 # Forward matrix value + M3 = 3.28230370472069e-05 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.0160398354926887 # Forward matrix value + M1 = 0.967192049208341 # Forward matrix value + M2 = 0.253539471533602 # Forward matrix value + M3 = -0.0610660311421554 # Forward matrix value + M4 = 0.254046334254542 # Forward matrix value + M5 = -0.96526234770262 # Forward matrix value + M6 = -0.998004841430097 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 0.0631374412063651 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 4.96925054084174 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin SlaMap # Conversion between sky coordinate systems + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nsla = 1 # Number of conversion steps + Sla1 = "EQGAL" # J2000.0 equatorial (FK5) to galactic (IAU 1958) + End SlaMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin SpecMap # Conversion between spectral coordinate systems + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nspec = 2 # Number of conversion steps + Spec1 = "FRTOVL" # Convert frequency to rel. velocity + Spec1a = 330587960100 # Rest frequency (Hz) + Spec2 = "VLTOVR" # Convert relativistic to radio velocity + End SpecMap + MapB = # Second component Mapping + Begin ZoomMap # Zoom about the origin + Nin = 1 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Zoom = 0.001 # Zoom factor + End ZoomMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin PermMap # Coordinate permutation + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + Out1 = 1 # Output coordinate 1 = input coordinate 1 + Out2 = 3 # Output coordinate 2 = input coordinate 3 + In1 = 1 # Input coordinate 1 = output coordinate 1 + In2 = -1 # Input coordinate 2 = constant no. 1 + In3 = 2 # Input coordinate 3 = output coordinate 2 + Nconst = 1 # Number of constants + Con1 = -0.000391559614368643 # Constant number 1 + End PermMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap diff --git a/ast_tester/rigby.simp b/ast_tester/rigby.simp new file mode 100644 index 0000000..82b6b94 --- /dev/null +++ b/ast_tester/rigby.simp @@ -0,0 +1,201 @@ + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin PermMap # Coordinate permutation + Nin = 2 # Number of input coordinates + Nout = 3 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Out1 = 1 # Output coordinate 1 = input coordinate 1 + Out2 = -1 # Output coordinate 2 = constant no. 1 + Out3 = 2 # Output coordinate 3 = input coordinate 2 + In1 = 1 # Input coordinate 1 = output coordinate 1 + In2 = 3 # Input coordinate 2 = output coordinate 3 + Nconst = 1 # Number of constants + Con1 = 232.84 # Constant number 1 + End PermMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 8.12968257918776 # Forward matrix value + M1 = 0.582308004080753 # Forward matrix value + M2 = 0 # Forward matrix value + M3 = -5.82308004080753 # Forward matrix value + M4 = 0.812968257918776 # Forward matrix value + M5 = 0 # Forward matrix value + M6 = 0 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 1 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -3780.19198483868 # Shift for axis 1 + Scl1 = 0.999999999977888 # Scale factor for axis 1 + Sft2 = 2408.26623941707 # Shift for axis 2 + Scl2 = 0.999999999985306 # Scale factor for axis 2 + Sft3 = 330416457339.851 # Shift for axis 3 + Scl3 = 551361.368168752 # Scale factor for axis 3 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -3.28230370472069e-05 # Forward matrix value + M1 = -1.68687502681513e-05 # Forward matrix value + M2 = -1.68687502681513e-05 # Forward matrix value + M3 = 3.28230370472069e-05 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap # FITS-WCS sky projection + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 0 # Polar longitude (rad.s) + End SphMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.0160398354926887 # Forward matrix value + M1 = 0.967192049208341 # Forward matrix value + M2 = 0.253539471533602 # Forward matrix value + M3 = -0.0610660311421554 # Forward matrix value + M4 = 0.254046334254542 # Forward matrix value + M5 = -0.96526234770262 # Forward matrix value + M6 = -0.998004841430097 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 0.0631374412063651 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin SphMap # Cartesian to Spherical mapping + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + PlrLg = 4.96925054084174 # Polar longitude (rad.s) + End SphMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin SlaMap # Conversion between sky coordinate systems + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nsla = 1 # Number of conversion steps + Sla1 = "EQGAL" # J2000.0 equatorial (FK5) to galactic (IAU 1958) + End SlaMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin SpecMap # Conversion between spectral coordinate systems + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nspec = 2 # Number of conversion steps + Spec1 = "FRTOVL" # Convert frequency to rel. velocity + Spec1a = 330587960100 # Rest frequency (Hz) + Spec2 = "VLTOVR" # Convert relativistic to radio velocity + End SpecMap + MapB = # Second component Mapping + Begin ZoomMap # Zoom about the origin + Nin = 1 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Zoom = 0.001 # Zoom factor + End ZoomMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin PermMap # Coordinate permutation + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + Out1 = 1 # Output coordinate 1 = input coordinate 1 + Out2 = 3 # Output coordinate 2 = input coordinate 3 + In1 = 1 # Input coordinate 1 = output coordinate 1 + In2 = -1 # Input coordinate 2 = constant no. 1 + In3 = 2 # Input coordinate 3 = output coordinate 2 + Nconst = 1 # Number of constants + Con1 = -0.000391559614368643 # Constant number 1 + End PermMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap diff --git a/ast_tester/scp.attr b/ast_tester/scp.attr new file mode 100644 index 0000000..54434e8 --- /dev/null +++ b/ast_tester/scp.attr @@ -0,0 +1 @@ +Grid=1,labelling=int diff --git a/ast_tester/scp.box b/ast_tester/scp.box new file mode 100644 index 0000000..76f5ba8 --- /dev/null +++ b/ast_tester/scp.box @@ -0,0 +1 @@ +1.0 1.0 1500.0 1300.0 diff --git a/ast_tester/scp.head b/ast_tester/scp.head new file mode 100644 index 0000000..f35c6f4 --- /dev/null +++ b/ast_tester/scp.head @@ -0,0 +1,204 @@ +SIMPLE = T / file does conform to FITS standard +BITPIX = 16 / number of bits per data pixel +NAXIS = 2 / number of data axes +NAXIS1 = 1787 / length of data axis 1 +NAXIS2 = 447 / length of data axis 2 +EXTEND = T / FITS dataset may contain extensions +COMMENT FITS (Flexible Image Transport System) format defined in Astronomy and +COMMENT Astrophysics Supplement Series v44/p363, v44/p371, v73/p359, v73/p365. +COMMENT Contact the NASA Science Office of Standards and Technology for the +COMMENT FITS Definition document #100 and other FITS information. +PLATENUM= '3665 ' / Plate number +EMULSION= 'IIIaJ ' / Kodak emulsion type +FILTER = 'GG395 ' / Schott glass filter type +PLTSCALE= '67.14 ' / [arcsec/mm] plate scale +FIELDNUM= '1 ' / Sky survey field number +EPOCH = 1.977780E+03 / Epoch of observation +DATE-OBS= '1977-10-11' / [yyyy-mm-dd] UT date of observation +TELESCOP= 'UKST ' / Telescope on which the plate was taken +TELETYPE= 'SCHM ' / Type of telescope +SITELAT = -5.458410576565E-01 / [radians] latitude of telescope +SITELONG= 2.601766194458E+00 / [radians] longitude of telescope +LST = '00:20 ' / [hh:mm] local sidereal time at start of obs +MJD-OBS = 4.342657300880E+04 / Modified Julian Date of observation +INSTRUME= 'SuperCOSMOS I' / Measuring machine +DATE-MES= '2000-11-04' / [yyyy-mm-dd] Date of this plate measurement +RADECSYS= 'FK5 ' / Reference frame for RA/DEC in original file +NHKLINES= 146 / Number of lines from house-keeping file +HKLIN001= 'JOB.JOBNO UKJ001' / +HKLIN002= 'JOB.DATE-MES 2000:11:04' / +HKLIN003= 'JOB.TIME 12:51:09' / +HKLIN004= 'JOB.INSTRUME SuperCOSMOS I' / +HKLIN005= 'JOB.ORIGIN Royal Observatory Edinburgh' / +HKLIN006= 'JOB.SOFTWARE /home/scosdev/v033' / +HKLIN007= 'JOB.OPERATOR ebt' / +HKLIN008= 'JOB.USER htm' / +HKLIN009= 'JOB.USERREF NONE' / +HKLIN010= 'JOB.UORIGIN ROE' / +HKLIN011= 'JOB.UCOUNTRY uk' / +HKLIN012= 'JOB.COMMENT Digital catalogue of the Sky' / +HKLIN013= 'JOB.IAM_FILE iam.srt' / +HKLIN014= 'PLATE.TELESCOP UKST' / +HKLIN015= 'PLATE.TELTYPE SCHM' / +HKLIN016= 'PLATE.PLATE 3665' / +HKLIN017= 'PLATE.MATERIAL 3mm glass' / +HKLIN018= 'PLATE.EMULSION IIIaJ' / +HKLIN019= 'PLATE.FILTER GG395' / +HKLIN020= 'PLATE.PSCALE 67.14' / +HKLIN021= 'PLATE.FIELD 1' / +HKLIN022= 'PLATE.RA_PNT 0' / +HKLIN023= 'PLATE.DEC_PNT -90' / +HKLIN024= 'PLATE.RADECSYS FK4' / +HKLIN025= 'PLATE.EQUINOX 1950' / +HKLIN026= 'PLATE.TIMESYS BESSELIAN' / +HKLIN027= 'PLATE.EPOCH 1977.78' / +HKLIN028= 'PLATE.EXPOSURE 75' / +HKLIN029= 'PLATE.UTDATE 771011' / +HKLIN030= 'PLATE.LST 0020' / +HKLIN031= 'PLATE.MJD 43426.573008796' / +HKLIN032= 'PLATE.TELLAT -0.54584105765654' / +HKLIN033= 'PLATE.TELLONG 2.6017661944583' / +HKLIN034= 'PLATE.TELHT 1145' / +HKLIN035= 'PLATE.TEMP 273.155' / +HKLIN036= 'PLATE.ATMOSP 1013.25' / +HKLIN037= 'PLATE.HUMID 0.5' / +HKLIN038= 'PLATE.WAVE 4500' / +HKLIN039= 'PLATE.TROPL 0.0065' / +HKLIN040= 'CALIBRATION.CALTYPE SPLINE' / +HKLIN041= 'CALIBRATION.STEPWEDG KPNO' / +HKLIN042= 'CALIBRATION.NSTEPS 8' / +HKLIN043= 'MEASUREMENT.ORIENTAT news' / +HKLIN044= 'MEASUREMENT.EMULPOS UP' / +HKLIN045= 'MEASUREMENT.SCANFILT 14' / +HKLIN046= 'MEASUREMENT.SOSP 552' / +HKLIN047= 'MEASUREMENT.STEPSIZE 10' / +HKLIN048= 'MEASUREMENT.SCANLEN 1152' / +HKLIN049= 'MEASUREMENT.A-XMIN 1622000' / +HKLIN050= 'MEASUREMENT.A-YMIN 1622000' / +HKLIN051= 'MEASUREMENT.A-XMAX 33878000' / +HKLIN052= 'MEASUREMENT.A-YMAX 33878000' / +HKLIN053= 'MEASUREMENT.X_PNT 17500000' / +HKLIN054= 'MEASUREMENT.Y_PNT 18000000' / +HKLIN055= 'ANALYSIS.NPARAMS 32' / +HKLIN056= 'ANALYSIS.AREACUT 8' / +HKLIN057= 'ANALYSIS.AP-PARAM 1.07' / +HKLIN058= 'DEBLEND.DB-PARAM 1.05' / +HKLIN059= 'DEBLEND.DB-AMIN 16' / +HKLIN060= 'DEBLEND.DB-AMAX 100000' / +HKLIN061= 'DEBLEND.DB-ACUT 8' / +HKLIN062= 'DEBLEND.DB-LEVEL 16' / +HKLIN063= 'DEBLEND.SELECT PARENT+CHILD' / +HKLIN064= 'SKY.SKYSQUAR 64' / +HKLIN065= 'SKY.SKYDEFN MEDIAN' / +HKLIN066= 'SKY.SKYFILTR bdkjunk' / +HKLIN067= 'SKY.F-THRESH 8' / +HKLIN068= 'SKY.F-SCLEN 4' / +HKLIN069= 'THRESHOLDING.PCUT 10' / +HKLIN070= 'IAMQC.AREAMIN 8' / +HKLIN071= 'IAMQC.AREAMAX 77346' / +HKLIN072= 'IAMQC.MINMAG -30515' / +HKLIN073= 'IAMQC.MAXMAG -17954' / +HKLIN074= 'IAMQC.MINELL 0.0004156232' / +HKLIN075= 'IAMQC.MAXELL 1' / +HKLIN076= 'IAMQC.MODELL 0.14' / +HKLIN077= 'IAMQC.MODOR 91' / +HKLIN078= 'IAMQC.MIDELL 0.21' / +HKLIN079= 'IAMQC.MIDOR 93' / +HKLIN080= 'IAMQC.MEANELL 0.2467037' / +HKLIN081= 'IAMQC.MEANOR 91.63474' / +HKLIN082= 'IAMQC.NUMOBJ 556985' / +HKLIN083= 'IAMQC.PARENTS 486656' / +HKLIN084= 'IAMQC.RANGING TRUE' / +HKLIN085= 'IAMQC.LANE_1 15571' / +HKLIN086= 'IAMQC.LANE_2 33207' / +HKLIN087= 'IAMQC.LANE_3 51478' / +HKLIN088= 'IAMQC.LANE_4 69944' / +HKLIN089= 'IAMQC.LANE_5 89236' / +HKLIN090= 'IAMQC.LANE_6 108416' / +HKLIN091= 'IAMQC.LANE_7 127481' / +HKLIN092= 'IAMQC.LANE_8 146699' / +HKLIN093= 'IAMQC.LANE_9 166380' / +HKLIN094= 'IAMQC.LANE_10 186126' / +HKLIN095= 'IAMQC.LANE_11 205946' / +HKLIN096= 'IAMQC.LANE_12 225915' / +HKLIN097= 'IAMQC.LANE_13 245926' / +HKLIN098= 'IAMQC.LANE_14 266574' / +HKLIN099= 'IAMQC.LANE_15 287150' / +HKLIN100= 'IAMQC.LANE_16 308087' / +HKLIN101= 'IAMQC.LANE_17 328830' / +HKLIN102= 'IAMQC.LANE_18 350253' / +HKLIN103= 'IAMQC.LANE_19 370738' / +HKLIN104= 'IAMQC.LANE_20 391722' / +HKLIN105= 'IAMQC.LANE_21 412801' / +HKLIN106= 'IAMQC.LANE_22 433795' / +HKLIN107= 'IAMQC.LANE_23 454383' / +HKLIN108= 'IAMQC.LANE_24 474711' / +HKLIN109= 'IAMQC.LANE_25 495108' / +HKLIN110= 'IAMQC.LANE_26 515755' / +HKLIN111= 'IAMQC.LANE_27 536499' / +HKLIN112= 'IAMQC.LANE_28 556985' / +HKLIN113= 'XYTORADEC.STARCAT /sdata/scos/refcats/tycho2.FIT' / +HKLIN114= 'XYTORADEC.BRIGHTLIM 9' / +HKLIN115= 'XYTORADEC.C-EQUIN 2000' / +HKLIN116= 'XYTORADEC.C-EQTSYS JULIAN' / +HKLIN117= 'XYTORADEC.C-EPOCH 2000' / +HKLIN118= 'XYTORADEC.C-EPTSYS JULIAN' / +HKLIN119= 'XYTORADEC.R-EQUIN 2000' / +HKLIN120= 'XYTORADEC.R-TSYS JULIAN' / +HKLIN121= 'XYTORADEC.MAXITER 5000' / +HKLIN122= 'XYTORADEC.RCRITINI 500000' / +HKLIN123= 'XYTORADEC.RCRITABS 50000' / +HKLIN124= 'XYTORADEC.RCRITREL 1' / +HKLIN125= 'XYTORADEC.RCRITFIN 3' / +HKLIN126= 'XYTORADEC.HARDCOPY /scos1/scos/UKJ001/UKJ001.ps' / +HKLIN127= 'XYTORADEC.REFSMULT 5' / +HKLIN128= 'XYTORADEC.RESDMULT 1000' / +HKLIN129= 'XYTORADEC.RACOL RA' / +HKLIN130= 'XYTORADEC.DECOL DEC' / +HKLIN131= 'XYTORADEC.RAPMCOL PMRA' / +HKLIN132= 'XYTORADEC.DECPMCOL PMDE' / +HKLIN133= 'XYTORADEC.PLXCOL NONE' / +HKLIN134= 'XYTORADEC.RVCOL NONE' / +HKLIN135= 'XYTORADEC.MAGCOL VT' / +HKLIN136= 'XYTORADEC.STARSC 2374' / +HKLIN137= 'XYTORADEC.STARSU 1727' / +HKLIN138= 'XYTORADEC.COEFFS_1 17.640343856524' / +HKLIN139= 'XYTORADEC.COEFFS_2 -260.44151995641' / +HKLIN140= 'XYTORADEC.COEFFS_3 -163.09155572601' / +HKLIN141= 'XYTORADEC.COEFFS_4 17.504230442205' / +HKLIN142= 'XYTORADEC.COEFFS_5 -163.08676953832' / +HKLIN143= 'XYTORADEC.COEFFS_6 260.48817907668' / +HKLIN144= 'XYTORADEC.DISTR -0.33333333333333' / +HKLIN145= 'XYTORADEC.RA_PNT 0.54924996662137' / +HKLIN146= 'XYTORADEC.DEC_PNT -1.5684931501781' / +HISTORY = 'SuperCOSMOS image analysis and mapping mode (IAM and MM)' / +HISTORY = 'data written by xydcomp_ss.' / +HISTORY = 'Any questions/comments/suggestions/bug reports should be sent' / +HISTORY = 'to N.Hambly@roe.ac.uk' / +ASTSIGX = 3.700000E-01 / [arcsec] std. dev. of astrometric fit in X +ASTSIGY = 3.800000E-01 / [arcsec] std. dev. of astrometric fit in Y +CRVAL1 = 0.000000000000E+00 / Axis 1 reference value +CRPIX1 = 8.936318379289E+02 / Axis 1 pixel value +CTYPE1 = 'RA---TAN' / Quantity represented by axis 1 +CRVAL2 = -9.000000018364E+01 / Axis 2 reference value +CRPIX2 = 2.238380193875E+02 / Axis 2 pixel value +CTYPE2 = 'DEC--TAN' / Quantity represented by axis 2 +CD1_1 = -1.864642639667E-04 / Co-ordinate transformation matrix +CD1_2 = -9.188369023766E-07 / Co-ordinate transformation matrix +CD2_1 = -1.038232462415E-06 / Co-ordinate transformation matrix +CD2_2 = 1.866269837741E-04 / Co-ordinate transformation matrix +PC001001= 9.999878591881E-01 / DEPRECATED - Axis rotation matrix +PC001002= 4.927623810613E-03 / DEPRECATED - Axis rotation matrix +PC002001= -5.563056187788E-03 / DEPRECATED - Axis rotation matrix +PC002002= 9.999845260832E-01 / DEPRECATED - Axis rotation matrix +CROTA2 = 3.005532298491E-01 / DEPRECATED - rotation of axis 2 +EQUINOX = 2.000000E+03 / Julian reference frame equinox +DATATYPE= 'INTEGER*2' / Type of data +DATUNITS= 'DENSITY ' / Units: transmission, density or intensity +XPIXELSZ= 9.997114974000E+00 / [microns] X pixel size +YPIXELSZ= 1.000000000000E+01 / [microns] Y pixel size +OBJCTRA = ' 0 0 0.000' / Centre Right Ascension (J2000) +OBJCTDEC= '-90 0 0.00' / Centre Declination (J2000) +OBJCTX = 1.636863183793E+04 / [pixels] Centre X on plate +OBJCTY = 1.474083801939E+04 / [pixels] Centre Y on plate +END diff --git a/ast_tester/scp.ps b/ast_tester/scp.ps new file mode 100644 index 0000000..0037ff7 --- /dev/null +++ b/ast_tester/scp.ps @@ -0,0 +1,641 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Feb 11 17:19:55 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: scp.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 23/10/2002 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 432.5 102.1 432.4 130.3 432.3 160.2 3 pls + 466.2 102.1 462.5 108.5 447.4 134.4 432.3 160.2 4 pls + 534.3 102.1 510.3 115.7 484.3 130.6 458.3 145.4 432.3 160.2 5 pls + s[ 432.3 462.2 492.2 522.2 552.1 582.1 590.4 ][ 160.2 160.4 160.5 160.7 160.9 161.0 161.1 ]plong + s[ 432.3 458.1 484.0 509.9 535.8 561.7 587.5 590.4 ][ 160.2 175.3 190.4 205.6 220.7 235.8 250.9 252.6 ]plong + s[ 432.3 447.1 462.0 476.8 491.7 506.5 521.4 536.3 551.1 566.0 580.8 590.4 ][ 160.2 186.2 212.2 238.3 264.3 290.3 + 316.3 342.3 368.3 394.4 420.4 437.1 ]plong + s[ 432.3 432.1 432.0 431.8 431.7 431.5 431.4 431.2 431.1 430.9 430.9 ][ 160.2 190.2 220.1 250.0 280.0 309.9 339.9 + 369.8 399.8 429.7 440.9 ]plong + s[ 432.3 417.1 402.0 386.9 371.8 356.7 341.6 326.5 311.3 296.2 281.1 268.2 ][ 160.2 186.1 211.9 237.8 263.6 289.5 + 315.3 341.2 367.0 392.9 418.7 440.9 ]plong + s[ 432.3 406.2 380.2 354.2 328.1 302.1 276.1 250.0 224.0 199.5 ][ 160.2 175.0 189.9 204.7 219.5 234.4 249.2 264.0 + 278.8 292.8 ]plong + s[ 432.3 402.3 372.3 342.3 312.4 282.4 252.4 222.5 199.5 ][ 160.2 160.0 159.9 159.7 159.5 159.4 159.2 159.0 158.9 +]plong + 332.7 102.1 354.6 114.9 380.5 130.0 406.4 145.1 432.3 160.2 5 pls + 399.1 102.1 402.5 108.2 417.4 134.2 432.3 160.2 4 pls + 432.5 102.1 432.4 130.3 432.3 160.2 3 pls + s[ 409.1 410.7 412.2 413.8 415.4 417.1 418.7 420.4 422.1 423.8 425.5 427.3 429.0 430.7 432.5 434.2 436.0 437.7 + 439.4 441.1 442.8 444.5 446.2 447.9 449.5 451.1 452.7 454.2 455.8 457.3 458.7 460.1 461.5 462.8 464.1 465.4 + 466.6 467.7 468.8 469.9 470.9 471.8 472.7 473.6 474.4 475.1 475.7 476.3 476.9 477.3 477.8 478.1 478.4 478.6 + 478.8 478.8 478.9 478.8 478.7 478.6 478.3 478.0 477.7 477.2 476.7 476.2 475.6 474.9 474.2 473.4 472.5 471.6 + 470.6 469.6 468.6 467.4 466.3 465.1 463.8 462.5 461.1 459.8 458.3 456.9 455.4 453.8 452.3 450.7 449.1 447.4 + 445.8 444.1 442.4 440.7 439.0 437.2 435.5 433.8 432.0 430.3 428.5 426.8 425.1 423.4 421.7 420.0 418.3 416.6 + 415.0 413.4 411.8 410.3 408.7 407.3 405.8 404.4 403.0 401.7 400.4 399.1 397.9 396.8 395.7 394.6 393.6 392.7 + 391.8 390.9 390.1 389.4 388.8 388.2 387.6 387.2 386.7 386.4 386.1 385.9 385.7 385.7 385.6 385.7 385.8 385.9 + 386.2 386.5 386.9 387.3 387.8 388.3 388.9 389.6 390.3 391.1 392.0 392.9 393.9 394.9 395.9 397.1 398.2 399.4 + 400.7 402.0 403.4 404.8 406.2 407.6 409.1 410.7 412.2 413.8 415.4 417.1 418.7 420.4 422.1 423.8 425.5 427.3 + 429.0 430.7 432.5 434.2 436.0 437.7 439.4 441.1 442.8 444.5 446.2 447.9 449.5 451.1 452.7 454.2 455.8 ][ 119.7 + 118.9 118.1 117.4 116.8 116.2 115.6 115.2 114.7 114.4 114.1 113.9 113.7 113.7 113.6 113.7 113.8 114.0 114.2 + 114.5 114.9 115.3 115.8 116.3 116.9 117.6 118.4 119.2 120.0 120.9 121.9 122.9 124.0 125.1 126.2 127.5 128.7 + 130.0 131.4 132.8 134.2 135.7 137.1 138.7 140.2 141.8 143.4 145.1 146.7 148.4 150.1 151.8 153.5 155.3 157.0 + 158.7 160.5 162.2 164.0 165.7 167.4 169.1 170.8 172.5 174.2 175.8 177.5 179.1 180.7 182.2 183.7 185.2 186.7 + 188.1 189.5 190.8 192.1 193.3 194.5 195.7 196.8 197.8 198.8 199.8 200.7 201.5 202.3 203.0 203.7 204.3 204.8 + 205.3 205.7 206.0 206.3 206.5 206.7 206.8 206.8 206.8 206.6 206.5 206.2 205.9 205.6 205.1 204.6 204.1 203.5 + 202.8 202.1 201.3 200.4 199.5 198.6 197.5 196.5 195.4 194.2 193.0 191.7 190.4 189.1 187.7 186.2 184.8 183.3 + 181.8 180.2 178.6 177.0 175.4 173.7 172.0 170.3 168.6 166.9 165.2 163.4 161.7 160.0 158.2 156.5 154.7 153.0 + 151.3 149.6 147.9 146.2 144.6 143.0 141.3 139.8 138.2 136.7 135.2 133.8 132.3 131.0 129.6 128.3 127.1 125.9 + 124.7 123.6 122.6 121.6 120.6 119.7 118.9 118.1 117.4 116.8 116.2 115.6 115.2 114.7 114.4 114.1 113.9 113.7 + 113.7 113.6 113.7 113.8 114.0 114.2 114.5 114.9 115.3 115.8 116.3 116.9 117.6 118.4 119.2 120.0 ]plong + s[ 505.1 505.4 507.5 509.6 511.4 513.2 514.9 516.5 517.9 519.2 520.4 521.5 522.4 523.3 524.0 524.5 525.0 525.3 + 525.4 525.5 525.4 525.2 524.9 524.4 523.8 523.1 522.2 521.2 520.1 518.9 517.5 516.1 514.5 512.8 511.0 509.0 + 507.0 504.9 502.6 500.3 497.9 495.3 492.7 490.0 487.3 484.4 481.5 478.5 475.4 472.3 469.1 465.9 462.6 459.3 + 455.9 452.6 449.1 445.7 442.2 438.8 435.3 431.8 428.3 424.8 421.4 417.9 414.5 411.1 407.7 404.3 401.0 397.8 + 394.5 391.4 388.3 385.2 382.3 379.3 376.5 373.8 371.1 368.5 366.0 363.6 361.3 359.1 357.0 355.0 353.1 351.3 + 349.6 348.0 346.6 345.3 344.1 343.0 342.1 341.2 340.5 340.0 339.5 339.2 339.1 339.0 339.1 339.3 339.6 340.1 + 340.7 341.4 342.3 343.3 344.4 345.6 347.0 348.4 350.0 351.7 353.5 355.5 357.5 359.3 ][ 102.1 102.5 105.3 108.2 + 111.1 114.1 117.1 120.3 123.4 126.7 129.9 133.2 136.6 140.0 143.4 146.8 150.3 153.8 157.2 160.7 164.2 167.7 + 171.2 174.6 178.0 181.4 184.8 188.2 191.5 194.7 197.9 201.1 204.2 207.2 210.2 213.1 216.0 218.7 221.4 224.0 + 226.5 228.9 231.2 233.4 235.5 237.5 239.4 241.2 242.8 244.4 245.8 247.1 248.3 249.4 250.3 251.2 251.8 252.4 + 252.8 253.2 253.3 253.4 253.3 253.1 252.7 252.3 251.7 250.9 250.1 249.1 248.0 246.7 245.4 243.9 242.3 240.6 + 238.8 236.9 234.9 232.7 230.5 228.2 225.7 223.2 220.6 217.9 215.1 212.3 209.3 206.3 203.3 200.2 197.0 193.8 + 190.5 187.2 183.8 180.4 177.0 173.6 170.1 166.7 163.2 159.7 156.2 152.7 149.3 145.8 142.4 139.0 135.6 132.3 + 129.0 125.7 122.5 119.3 116.2 113.2 110.2 107.3 104.5 102.1 ]plong + s[ 559.4 560.7 562.7 564.5 566.1 567.5 568.8 569.8 570.7 571.3 571.8 572.0 572.1 572.0 571.7 571.2 570.5 569.5 + 568.5 567.2 565.7 564.0 562.2 560.2 558.0 555.6 553.0 550.3 547.4 544.4 541.2 537.8 534.3 530.7 526.9 523.0 + 518.9 514.8 510.5 506.1 501.6 497.0 492.3 487.6 482.7 477.8 472.8 467.8 462.7 457.6 452.4 447.2 442.0 436.8 + 431.6 426.3 421.1 415.9 410.7 405.6 400.5 395.4 390.4 385.4 380.5 375.7 370.9 366.3 361.7 357.3 352.9 348.6 + 344.5 340.5 336.6 332.9 329.3 325.8 322.5 319.3 316.3 313.5 310.8 308.3 305.9 303.8 301.8 300.0 298.4 297.0 + 295.7 294.7 293.8 293.2 292.7 292.5 292.4 292.5 292.8 293.3 294.1 295.0 296.0 297.3 298.8 300.5 302.3 304.3 + 305.0 ][ 102.1 105.0 109.9 114.8 119.8 124.8 129.9 135.0 140.2 145.3 150.5 155.8 161.0 166.2 171.4 176.6 181.8 + 187.0 192.1 197.1 202.1 207.1 212.0 216.8 221.5 226.2 230.8 235.2 239.6 243.8 247.9 252.0 255.8 259.6 263.2 + 266.6 270.0 273.1 276.1 279.0 281.6 284.1 286.5 288.6 290.6 292.4 294.0 295.4 296.6 297.7 298.5 299.2 299.6 + 299.9 300.0 299.8 299.5 299.0 298.3 297.4 296.3 295.0 293.5 291.9 290.0 288.0 285.8 283.4 280.8 278.1 275.2 + 272.2 269.0 265.6 262.1 258.5 254.7 250.8 246.7 242.6 238.3 233.9 229.4 224.8 220.1 215.4 210.5 205.6 200.7 + 195.6 190.6 185.4 180.3 175.1 169.9 164.7 159.4 154.2 149.0 143.8 138.6 133.5 128.4 123.3 118.3 113.3 108.4 + 103.6 102.1 ]plong + s[ 590.4 589.7 585.8 581.8 577.5 573.0 568.3 563.5 558.4 553.2 547.8 542.3 536.5 530.7 524.7 518.6 512.3 506.0 + 499.5 493.0 486.3 479.6 472.9 466.0 459.1 452.2 445.3 438.3 431.3 424.4 417.4 410.5 403.5 396.7 389.9 383.1 + 376.4 369.8 363.3 356.8 350.5 344.3 338.2 332.3 326.4 320.8 315.3 309.9 304.7 299.7 294.9 290.3 285.9 281.7 + 277.7 273.9 270.3 266.9 263.8 261.0 258.3 255.9 253.8 251.9 250.2 248.8 247.7 246.8 246.2 245.9 245.8 245.9 + 246.4 247.0 248.0 249.2 250.6 252.4 254.3 255.1 ][ 259.0 260.2 266.0 271.7 277.2 282.5 287.7 292.7 297.5 302.1 + 306.5 310.7 314.7 318.5 322.1 325.4 328.5 331.4 334.0 336.4 338.6 340.5 342.1 343.5 344.6 345.5 346.1 346.4 + 346.5 346.4 345.9 345.2 344.3 343.1 341.6 339.9 338.0 335.7 333.3 330.6 327.6 324.5 321.1 317.4 313.6 309.5 + 305.2 300.8 296.1 291.2 286.2 281.0 275.6 270.0 264.3 258.5 252.5 246.4 240.1 233.8 227.3 220.8 214.1 207.4 + 200.7 193.8 187.0 180.0 173.1 166.1 159.2 152.2 145.3 138.3 131.4 124.6 117.7 111.0 104.3 102.1 ]plong + s[ 590.4 590.0 583.4 576.7 569.8 562.6 555.3 547.8 540.2 532.4 524.4 516.3 508.2 499.9 491.5 483.0 474.5 465.9 + 457.2 448.5 439.8 431.1 422.4 413.7 405.0 396.4 387.8 379.3 370.8 362.4 354.2 346.0 338.0 330.1 322.3 314.7 + 307.3 300.0 292.9 286.0 279.3 272.9 266.6 260.6 254.8 249.3 244.0 239.0 234.3 229.8 225.6 221.7 218.1 214.8 + 211.8 209.2 206.8 204.7 203.0 201.6 200.5 199.7 199.5 ][ 331.4 331.8 337.6 343.1 348.4 353.4 358.1 362.6 366.7 + 370.6 374.2 377.5 380.5 383.2 385.5 387.6 389.3 390.7 391.8 392.6 393.0 393.1 392.9 392.4 391.5 390.3 388.8 + 387.0 384.8 382.4 379.6 376.5 373.2 369.5 365.5 361.3 356.7 351.9 346.8 341.5 335.9 330.1 324.0 317.7 311.2 + 304.4 297.5 290.3 283.0 275.5 267.9 260.1 252.2 244.1 235.9 227.6 219.2 210.8 202.2 193.6 185.0 176.3 171.2 ]plong + s[ 199.5 199.9 200.7 201.9 203.4 205.2 206.5 ][ 148.6 141.5 132.8 124.2 115.6 107.1 102.1 ]plong + s[ 590.4 588.7 579.9 570.9 561.7 552.4 542.9 533.2 523.3 513.4 503.3 493.2 482.9 472.6 462.2 451.8 441.3 430.9 + 420.4 410.0 399.6 389.2 378.9 368.7 358.5 348.5 338.6 328.8 319.1 309.6 300.3 291.2 282.3 273.5 265.0 256.8 + 248.7 241.0 233.5 226.3 219.3 212.7 206.4 200.4 199.5 ][ 390.8 392.0 397.7 403.0 408.0 412.7 417.0 420.9 424.5 + 427.7 430.6 433.0 435.1 436.8 438.1 439.0 439.6 439.7 439.4 438.8 437.8 436.3 434.5 432.3 429.8 426.8 423.5 + 419.8 415.8 411.3 406.6 401.5 396.0 390.3 384.2 377.8 371.0 364.0 356.7 349.2 341.3 333.3 324.9 316.4 315.0 ]plong + 563.9 106.6 561.7 107.5 2 pls + 569.8 124.2 567.5 124.8 2 pls + 573.4 142.4 571.0 142.7 2 pls + 573.2 179.5 570.8 179.2 2 pls + 569.5 197.8 567.2 197.1 2 pls + 563.4 215.3 561.2 214.4 2 pls + 544.7 247.4 542.8 245.9 2 pls + 532.3 261.3 530.7 259.6 2 pls + 518.3 273.4 516.8 271.6 2 pls + 486.0 291.8 485.1 289.6 2 pls + 468.4 297.7 467.8 295.4 2 pls + 450.1 301.2 449.8 298.9 2 pls + 413.0 301.0 413.3 298.7 2 pls + 394.8 297.3 395.4 295.0 2 pls + 377.2 291.2 378.1 289.0 2 pls + 345.1 272.5 346.6 270.6 2 pls + 331.2 260.1 332.9 258.5 2 pls + 319.0 246.1 320.9 244.7 2 pls + 300.6 213.9 302.8 213.0 2 pls + 294.7 196.2 297.0 195.6 2 pls + 291.1 178.0 293.5 177.7 2 pls + 291.3 140.9 293.7 141.2 2 pls + 295.0 122.7 297.3 123.3 2 pls + 301.1 105.1 303.3 106.0 2 pls + 434.6 172.0 432.2 171.9 2 pls + 434.5 183.6 432.1 183.5 2 pls + 434.4 195.3 432.1 195.1 2 pls + 434.3 218.6 432.0 218.4 2 pls + 434.3 230.2 431.9 230.1 2 pls + 434.2 241.9 431.9 241.7 2 pls + 434.1 265.2 431.7 265.0 2 pls + 434.0 276.8 431.7 276.7 2 pls + 434.0 288.4 431.6 288.3 2 pls + 433.9 311.7 431.5 311.6 2 pls + 433.8 323.4 431.4 323.2 2 pls + 433.8 335.0 431.4 334.9 2 pls + 433.6 358.3 431.3 358.2 2 pls + 433.6 370.0 431.2 369.8 2 pls + 433.5 381.6 431.2 381.5 2 pls + 433.4 404.9 431.0 404.8 2 pls + 433.4 416.5 431.0 416.4 2 pls + 433.3 428.2 430.9 428.1 2 pls + s[ 559.4 560.7 562.7 564.5 566.1 567.5 568.8 569.8 570.7 571.3 571.8 572.0 572.1 ][ 102.1 105.0 109.9 114.8 119.8 + 124.8 129.9 135.0 140.2 145.3 150.5 155.8 161.0 ]plong + s[ 572.1 572.0 571.7 571.2 570.5 569.5 568.5 567.2 565.7 564.0 562.2 560.2 558.0 555.6 553.0 ][ 161.0 166.2 171.4 + 176.6 181.8 187.0 192.1 197.1 202.1 207.1 212.0 216.8 221.5 226.2 230.8 ]plong + s[ 553.0 550.3 547.4 544.4 541.2 537.8 534.3 530.7 526.9 523.0 518.9 514.8 510.5 506.1 501.6 ][ 230.8 235.2 239.6 + 243.8 247.9 252.0 255.8 259.6 263.2 266.6 270.0 273.1 276.1 279.0 281.6 ]plong + s[ 501.6 497.0 492.3 487.6 482.7 477.8 472.8 467.8 462.7 457.6 452.4 447.2 442.0 436.8 431.6 ][ 281.6 284.1 286.5 + 288.6 290.6 292.4 294.0 295.4 296.6 297.7 298.5 299.2 299.6 299.9 300.0 ]plong + s[ 431.6 426.3 421.1 415.9 410.7 405.6 400.5 395.4 390.4 385.4 380.5 375.7 370.9 366.3 361.7 ][ 300.0 299.8 299.5 + 299.0 298.3 297.4 296.3 295.0 293.5 291.9 290.0 288.0 285.8 283.4 280.8 ]plong + s[ 361.7 357.3 352.9 348.6 344.5 340.5 336.6 332.9 329.3 325.8 322.5 319.3 316.3 313.5 310.8 ][ 280.8 278.1 275.2 + 272.2 269.0 265.6 262.1 258.5 254.7 250.8 246.7 242.6 238.3 233.9 229.4 ]plong + s[ 310.8 308.3 305.9 303.8 301.8 300.0 298.4 297.0 295.7 294.7 293.8 293.2 292.7 292.5 292.4 ][ 229.4 224.8 220.1 + 215.4 210.5 205.6 200.7 195.6 190.6 185.4 180.3 175.1 169.9 164.7 159.4 ]plong + s[ 292.4 292.5 292.8 293.3 294.1 295.0 296.0 297.3 298.8 300.5 302.3 304.3 305.0 ][ 159.4 154.2 149.0 143.8 138.6 + 133.5 128.4 123.3 118.3 113.3 108.4 103.6 102.1 ]plong + s[ 432.3 432.2 432.2 432.2 432.2 432.2 432.2 432.1 432.1 432.1 432.1 432.1 432.1 432.0 432.0 ][ 160.2 163.5 166.9 + 170.2 173.5 176.8 180.2 183.5 186.8 190.2 193.5 196.8 200.1 203.5 206.8 ]plong + s[ 432.0 432.0 432.0 432.0 432.0 431.9 431.9 431.9 431.9 431.9 431.9 431.8 431.8 431.8 431.8 ][ 206.8 210.1 213.4 + 216.8 220.1 223.4 226.8 230.1 233.4 236.7 240.1 243.4 246.7 250.0 253.4 ]plong + s[ 431.8 431.8 431.8 431.7 431.7 431.7 431.7 431.7 431.7 431.6 431.6 431.6 431.6 431.6 431.6 ][ 253.4 256.7 260.0 + 263.4 266.7 270.0 273.3 276.7 280.0 283.3 286.6 290.0 293.3 296.6 300.0 ]plong + s[ 431.6 431.5 431.5 431.5 431.5 431.5 431.5 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.3 ][ 300.0 303.3 306.6 + 309.9 313.3 316.6 319.9 323.2 326.6 329.9 333.2 336.6 339.9 343.2 346.5 ]plong + s[ 431.3 431.3 431.3 431.3 431.3 431.3 431.2 431.2 431.2 431.2 431.2 431.2 431.1 431.1 431.1 ][ 346.5 349.9 353.2 + 356.5 359.8 363.2 366.5 369.8 373.2 376.5 379.8 383.1 386.5 389.8 393.1 ]plong + s[ 431.1 431.1 431.1 431.1 431.0 431.0 431.0 431.0 431.0 431.0 430.9 430.9 430.9 430.9 430.9 ][ 393.1 396.4 399.8 + 403.1 406.4 409.8 413.1 416.4 419.7 423.1 426.4 429.7 433.0 436.4 439.7 ]plong + 430.9 440.9 430.9 439.7 2 pls + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 426.60 284.44 moveto[ 426.6 426.6 436.6 436.6 ][ 284.4 296.6 296.6 284.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 431.2 429.9 429.1 428.7 428.7 429.1 429.9 431.2 432.0 433.3 434.1 434.5 434.5 434.1 433.3 432.0 431.2 ][ 294.9 + 294.5 293.2 291.1 289.9 287.8 286.5 286.1 286.1 286.5 287.8 289.9 291.1 293.2 294.5 294.9 294.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 535.01 230.82 moveto[ 535.0 545.5 554.8 544.3 ][ 230.8 237.0 221.1 215.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 543.5 543.8 544.8 545.3 546.1 547.0 547.0 546.9 546.4 545.7 544.7 543.2 537.5 540.5 ][ 232.9 233.1 233.2 233.0 + 232.5 231.1 230.1 229.6 228.8 228.4 228.3 228.4 229.9 224.8 ]plong + s[ 550.6 549.6 548.1 546.1 545.0 543.4 542.7 543.0 543.4 544.4 545.9 548.0 549.0 550.6 551.3 551.0 550.6 ][ 225.0 + 225.8 225.9 225.2 224.6 223.2 221.8 220.5 219.8 218.9 218.8 219.5 220.2 221.6 222.9 224.2 225.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 570.46 153.04 moveto[ 570.5 558.3 558.2 570.3 ][ 153.0 152.9 170.1 170.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 568.8 157.2 560.0 157.1 561.2 155.9 561.7 155.1 4 pls + s[ 559.9 560.4 561.2 562.0 562.9 563.3 563.7 564.1 564.9 565.8 567.0 567.8 568.3 568.7 568.7 568.3 567.9 567.0 + 565.8 565.0 564.1 563.7 563.3 562.8 562.0 561.2 560.3 559.9 559.9 ][ 164.2 163.0 162.6 162.6 163.0 163.8 165.5 + 166.8 167.6 168.0 168.1 167.6 167.2 166.0 164.3 163.0 162.6 162.2 162.2 162.6 163.4 164.7 166.4 167.2 167.6 + 167.6 167.2 165.9 164.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 485.91 272.74 moveto[ 485.9 491.9 507.9 501.9 ][ 272.7 283.3 274.1 263.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 492.2 492.4 493.2 493.8 494.7 496.2 496.7 496.8 496.8 496.4 495.6 494.3 488.5 493.6 ][ 278.7 279.1 279.6 279.8 + 279.7 278.9 278.1 277.5 276.6 275.9 275.4 274.7 273.2 270.2 ]plong + s[ 499.5 499.7 500.5 501.0 502.0 503.4 503.9 504.1 504.0 503.6 502.8 501.5 495.8 500.9 ][ 274.6 275.0 275.5 275.6 + 275.6 274.7 274.0 273.4 272.5 271.7 271.2 270.5 269.0 266.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 365.17 264.92 moveto[ 365.2 359.1 367.8 373.8 ][ 264.9 275.4 280.4 270.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 363.1 362.9 362.9 363.0 363.5 365.0 365.9 366.5 367.3 367.7 367.7 367.7 366.1 371.2 ][ 273.4 273.8 274.7 275.3 + 276.1 276.9 276.9 276.8 276.3 275.6 274.6 273.1 267.4 270.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 321.72 217.34 moveto[ 321.7 311.2 316.4 326.9 ][ 217.3 223.4 232.4 226.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 321.9 226.9 318.8 221.4 315.8 228.0 3 pls + 323.4 223.6 315.8 228.0 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 294.09 164.04 moveto[ 294.1 306.2 306.2 294.1 ][ 164.0 164.1 154.5 154.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 303.3 304.1 304.5 304.5 304.1 302.9 300.8 298.7 297.0 296.2 295.8 295.8 296.2 297.0 298.3 298.7 300.0 300.8 + 301.2 301.2 300.8 299.9 298.7 ][ 157.0 157.4 158.6 159.5 160.7 161.5 162.0 162.0 161.5 160.7 159.4 159.0 157.8 + 156.9 156.5 156.5 156.9 157.8 159.0 159.5 160.7 161.5 162.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 430.66 135.56 moveto[ 430.7 418.5 418.3 430.4 ][ 135.6 135.5 185.2 185.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 425.2 145.1 425.2 137.6 2 pls + s[ 423.0 424.3 425.1 425.6 425.6 425.2 424.3 423.1 422.7 421.4 420.6 420.1 420.1 420.5 421.4 423.0 425.1 427.2 + 428.5 428.9 428.9 428.5 427.7 ][ 153.5 153.1 152.2 151.0 150.6 149.3 148.5 148.0 148.0 148.5 149.3 150.5 151.0 + 152.2 153.1 153.5 153.5 153.1 152.3 151.0 150.2 148.9 148.5 ]plong + s[ 420.1 420.5 421.8 423.9 425.1 427.2 428.5 428.9 428.9 428.4 427.2 425.1 423.8 421.8 420.5 420.1 420.1 ][ 158.9 + 157.6 156.8 156.4 156.4 156.8 157.7 158.9 159.8 161.0 161.9 162.3 162.3 161.8 161.0 159.7 158.9 ]plong + 423.0 165.6 423.4 166.0 423.8 165.6 423.4 165.2 423.0 165.6 5 pls + 428.0 165.6 428.4 166.0 428.8 165.6 428.4 165.2 428.0 165.6 5 pls + s[ 420.0 420.5 421.7 423.8 425.1 427.1 428.4 428.8 428.8 428.4 427.1 425.0 423.8 421.7 420.4 420.0 420.0 ][ 171.4 + 170.2 169.3 168.9 168.9 169.4 170.2 171.5 172.3 173.6 174.4 174.8 174.8 174.4 173.5 172.3 171.4 ]plong + s[ 420.0 420.4 421.7 423.8 425.0 427.1 428.4 428.8 428.8 428.3 427.1 425.0 423.7 421.7 420.4 420.0 420.0 ][ 179.8 + 178.5 177.7 177.3 177.3 177.7 178.6 179.8 180.7 181.9 182.7 183.1 183.1 182.7 181.9 180.6 179.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 429.51 368.46 moveto[ 429.5 417.4 417.2 429.3 ][ 368.5 368.4 418.1 418.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 424.0 378.0 424.1 370.5 2 pls + s[ 419.0 419.4 420.3 421.1 421.9 422.3 422.7 423.2 424.0 424.8 426.1 426.9 427.3 427.8 427.8 427.4 426.9 426.1 + 424.9 424.0 423.2 422.8 422.3 421.9 421.1 420.2 419.4 419.0 419.0 ][ 383.0 381.8 381.4 381.4 381.8 382.6 384.3 + 385.6 386.4 386.8 386.8 386.4 386.0 384.7 383.1 381.8 381.4 381.0 381.0 381.4 382.2 383.5 385.1 386.0 386.4 + 386.4 386.0 384.7 383.0 ]plong + s[ 421.9 423.1 424.0 424.4 424.4 424.0 423.1 421.9 421.5 420.2 419.4 419.0 418.9 419.4 420.2 421.9 423.9 426.0 + 427.3 427.7 427.7 427.3 426.5 ][ 394.7 394.3 393.5 392.2 391.8 390.6 389.7 389.3 389.3 389.7 390.5 391.8 392.2 + 393.5 394.3 394.7 394.7 394.3 393.5 392.3 391.4 390.2 389.7 ]plong + 421.8 398.5 422.3 398.9 422.7 398.5 422.3 398.1 421.8 398.5 5 pls + 426.9 398.5 427.3 398.9 427.7 398.5 427.3 398.1 426.9 398.5 5 pls + s[ 418.9 418.9 422.7 422.2 421.8 421.8 422.2 423.1 424.3 425.1 426.4 427.2 427.7 427.7 427.3 426.8 426.0 ][ 406.8 + 402.7 402.3 402.7 403.9 405.2 406.4 407.3 407.7 407.7 407.3 406.5 405.2 404.0 402.7 402.3 401.9 ]plong + s[ 418.8 419.3 420.5 422.6 423.9 426.0 427.2 427.6 427.6 427.2 425.9 423.8 422.6 420.5 419.3 418.8 418.8 ][ 412.7 + 411.4 410.6 410.2 410.2 410.6 411.5 412.7 413.6 414.8 415.6 416.0 416.0 415.6 414.8 413.5 412.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 430.35 197.59 moveto[ 430.4 418.2 418.1 430.3 ][ 197.6 197.5 215.9 216.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 419.9 419.9 423.7 423.2 422.8 422.8 423.2 424.0 425.3 426.1 427.4 428.2 428.7 428.7 428.2 427.8 427.0 ][ 204.6 + 200.5 200.1 200.5 201.7 203.0 204.2 205.1 205.5 205.5 205.1 204.3 203.0 201.8 200.5 200.1 199.7 ]plong + s[ 419.8 420.3 421.1 421.9 422.8 423.2 423.6 424.0 424.8 425.7 426.9 427.8 428.2 428.6 428.6 428.2 427.8 427.0 + 425.7 424.9 424.0 423.6 423.2 422.8 421.9 421.1 420.3 419.8 419.8 ][ 210.1 208.8 208.4 208.4 208.8 209.7 211.3 + 212.6 213.4 213.9 213.9 213.5 213.0 211.8 210.1 208.9 208.4 208.0 208.0 208.4 209.3 210.5 212.2 213.0 213.4 + 213.4 213.0 211.7 210.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 430.12 244.17 moveto[ 430.1 418.0 417.9 430.0 ][ 244.2 244.1 262.5 262.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 419.6 419.7 423.4 423.0 422.6 422.6 423.0 423.8 425.1 425.9 427.2 428.0 428.4 428.4 428.0 427.6 426.8 ][ 251.2 + 247.0 246.6 247.1 248.3 249.6 250.8 251.7 252.1 252.1 251.7 250.8 249.6 248.3 247.1 246.7 246.2 ]plong + s[ 420.9 420.0 419.6 419.6 420.0 421.3 423.4 425.5 427.1 428.0 428.4 428.4 428.0 427.1 425.9 425.4 424.2 423.4 + 423.0 423.0 423.4 424.2 425.5 ][ 260.0 259.6 258.3 257.5 256.2 255.4 255.0 255.0 255.4 256.3 257.5 257.9 259.2 + 260.0 260.4 260.4 260.0 259.2 257.9 257.5 256.3 255.4 255.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 429.66 337.34 moveto[ 429.7 417.5 417.5 429.6 ][ 337.3 337.3 355.7 355.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 419.2 419.2 423.0 422.5 422.1 422.1 422.5 423.4 424.6 425.4 426.7 427.5 428.0 428.0 427.6 427.1 426.3 ][ 344.4 + 340.2 339.8 340.2 341.5 342.7 344.0 344.8 345.2 345.3 344.8 344.0 342.8 341.5 340.2 339.8 339.4 ]plong + s[ 421.3 420.8 420.0 419.6 419.2 419.2 419.6 420.0 420.8 421.6 422.5 423.7 427.9 427.9 ][ 348.2 348.2 348.6 349.0 + 349.8 351.5 352.3 352.7 353.2 353.2 352.8 351.9 347.8 353.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 429.20 430.50 moveto[ 429.2 417.1 417.0 429.1 ][ 430.5 430.4 448.8 448.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 424.6 438.8 424.6 432.6 418.7 436.7 3 pls + 427.5 436.8 418.7 436.7 2 pls + s[ 418.7 419.1 420.0 420.8 421.6 422.0 422.5 422.9 423.7 424.5 425.8 426.6 427.0 427.5 427.5 427.1 426.6 425.8 + 424.6 423.7 422.9 422.5 422.0 421.6 420.8 419.9 419.1 418.7 418.7 ][ 443.0 441.7 441.3 441.3 441.7 442.6 444.2 + 445.5 446.3 446.8 446.8 446.4 445.9 444.7 443.0 441.8 441.3 440.9 440.9 441.3 442.2 443.4 445.1 445.9 446.3 + 446.3 445.9 444.6 443.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 464.36 moveto[ 360.9 360.9 435.2 435.2 ][ 464.4 476.5 476.5 464.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 466.0 366.3 474.8 2 pls + 369.7 466.0 366.3 474.8 2 pls + 368.4 469.0 364.2 469.0 2 pls + 378.4 466.0 378.4 474.8 2 pls + 383.9 474.8 378.4 474.8 2 pls + 381.8 470.6 378.4 470.6 2 pls + 385.9 466.0 385.9 474.8 2 pls + 391.0 466.0 391.0 474.8 2 pls + 393.9 474.8 388.0 474.8 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 473.5 474.4 474.8 474.8 474.4 473.5 472.7 471.9 471.5 471.0 470.2 469.8 469.4 468.5 467.3 466.4 + 466.0 466.0 466.4 467.3 ]plong + 413.5 466.0 412.7 466.0 411.8 466.4 411.4 467.7 411.4 474.8 5 pls + 413.1 471.9 410.2 471.9 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 469.4 + 469.4 470.2 471.0 471.5 471.9 471.9 471.5 470.6 469.4 468.5 467.3 466.4 466.0 466.0 466.4 467.3 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 470.6 + 471.5 471.9 471.9 471.5 470.6 469.8 469.4 469.0 468.5 467.7 467.3 466.4 466.0 466.0 466.4 467.3 ]plong + 433.1 466.0 432.3 466.0 431.5 466.4 431.1 467.7 431.1 474.8 5 pls + 432.7 471.9 429.8 471.9 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/serpens.attr b/ast_tester/serpens.attr new file mode 100644 index 0000000..496403c --- /dev/null +++ b/ast_tester/serpens.attr @@ -0,0 +1 @@ +border=1 diff --git a/ast_tester/serpens.box b/ast_tester/serpens.box new file mode 100644 index 0000000..f9576e8 --- /dev/null +++ b/ast_tester/serpens.box @@ -0,0 +1 @@ +0.5 0.5 150.5 120.5 diff --git a/ast_tester/serpens.head b/ast_tester/serpens.head new file mode 100644 index 0000000..0775604 --- /dev/null +++ b/ast_tester/serpens.head @@ -0,0 +1,160 @@ +NAXIS1 = 150 / length of data axis 1 +NAXIS2 = 120 / length of data axis 2 +DATE-OBS= '2007-04-30T15:13:53.717'/ Date of observation +CRPIX1 = 75.910204 / Reference pixel on axis 1 +CRPIX2 = 60.359184 / Reference pixel on axis 2 +CRVAL1 = 1.220833333 / Value at ref. pixel on axis 1 +CRVAL2 = 277.5004167 / Value at ref. pixel on axis 2 +CTYPE1 = 'DEC--TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'RA---TAN' / Type of co-ordinate on axis 2 +CD1_1 = -0.001831773954 / Transformation matrix element +CD1_2 = -8.541702226E-4 / Transformation matrix element +CD2_1 = 0.0008541702226 / Transformation matrix element +CD2_2 = -0.001831773954 / Transformation matrix element +RADECSYS= 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / Epoch of reference equinox +DUT1 = -1.145603703E-6 / [d] UT1-UTC correction + + + + + + + + + + + + + + + + + + + + + + + + + + +EXP_TIME= 41.97277069 / [s] Median MAKECUBE exposure time +EFF_TIME= 11.13561535 / [s] Median MAKECUBE effective integration time +PROVCNT = 1 / Number of unique OBSIDs +OBS00001= 'acsis_98_20070430T151247'/ OBSID from component observation + + +HISTORY History structure created 2007 Apr 30 05:52:50.656 +HISTORY Update mode: NORMAL Current record: 8 + +HISTORY 1: 2007 Apr 30 05:52:53.959 - MAKECUBE (SMURF V0.2.2) +HISTORY User: operator Host: kolea Width: 72 +HISTORY Dataset: /jcmtdata/reduced/acsis/20070430/a20070430_00098_01_cube +HISTORY Parameters: AUTOGRID=TRUE CROTA=115 DETECTORS=! +HISTORY FBL=[4.8435931408225,0.024206080047514] +HISTORY FBR=[4.8456657339925,0.019762137343435] +HISTORY FLBND=[4.8407875332009,0.018441755319268,-426.75185625342] +HISTORY FUBND=[4.8456891772589,0.024229519070352,440.3284772626] +HISTORY FTL=[4.8408109789176,0.022909013956528] +HISTORY FTR=[4.8428837776034,0.018465194476559] GENVAR='tsys' +HISTORY IN=@^/jcmtdata/reduced/acsis/20070430/oractemppV8I2A.lis +HISTORY INWEIGHT=TRUE LBOUND=[-70,-41,-1024] OUT=@a20070430_00098_01_cube +HISTORY OUTCAT=! PIXSIZE=7.2761 MSG_FILTER='NORM' REFLAT='1:13:15' +HISTORY REFLON='18:30:00.1' SPARSE=FALSE SPECBOUNDS='-426.5402 440.1168' +HISTORY TRIM=TRUE WEIGHTS=FALSE SPREAD='nearest' SYSTEM='TRACKING' +HISTORY UBOUND=[69,46,1023] USEDETPOS=TRUE +HISTORY Software: /star/bin/smurf/smurf_mon + +HISTORY 2: 2007 Apr 30 05:52:59.952 - NDFCOPY (KAPPA 1.7-2) +HISTORY User: operator Host: kolea Width: 72 +HISTORY Dataset: /jcmtdata/reduced/acsis/20070430/a20070430_00098_01_em +HISTORY Parameters: IN=@a20070430_00098_01_cube(,,-820:819) LIKE=! +HISTORY OUT=@a20070430_00098_01_em TITLE=! TRIM=FALSE +HISTORY Software: /star/bin/kappa/ndfpack_mon + +HISTORY 3: 2007 Apr 30 05:53:04.009 - MFITTREND (KAPPA 1.7-2) +HISTORY User: operator Host: kolea Width: 72 +HISTORY Dataset: /jcmtdata/reduced/acsis/20070430/a20070430_00098_01_bl +HISTORY Parameters: ARANGES=[-820,-56,47,819] AUTO=TRUE AXIS=@3 CLIP=[2,2,2.5,3] +HISTORY IN=@a20070430_00098_01_em MODIFYIN=FALSE ORDER=0 +HISTORY OUT=@a20070430_00098_01_bl RMSCLIP=! SECTION='-70:69,-41:46,' +HISTORY SUBTRACT=TRUE TITLE=! VARIANCE=TRUE +HISTORY Software: /star/bin/kappa/kappa_mon + +HISTORY 4: 2007 Apr 30 05:53:17.048 - NDFCOPY (KAPPA 1.7-2) +HISTORY User: operator Host: kolea Width: 72 +HISTORY Dataset: /jcmtdata/reduced/acsis/20070430/ga20070430_98 +HISTORY Parameters: IN=@a20070430_00098_01_bl LIKE=! OUT=@ga20070430_98 +HISTORY TITLE='ga20070430_98' TRIM=FALSE +HISTORY Software: /star/bin/kappa/ndfpack_mon + +HISTORY 5: 2007 Apr 30 06:40:02.994 - WCSMOSAIC (KAPPA 1.7-2) +HISTORY User: operator Host: kolea Width: 72 +HISTORY Dataset: /jcmtdata/reduced/acsis/20070430/oractemppLv7wv +HISTORY Parameters: ACC=0.05 GENVAR=FALSE ILEVEL=2 +HISTORY IN=@^/jcmtdata/reduced/acsis/20070430/oractempyopUhZ.lis LBND=! +HISTORY LBOUND=[-70,-43,-820] MAXPIX=1000 METHOD='nearest' +HISTORY OUT=@/jcmtdata/reduced/acsis/20070430/oractemppLv7wv REF=! UBND=! +HISTORY UBOUND=[69,46,819] VARIANCE=TRUE WLIM=1E-10 +HISTORY Software: /star/bin/kappa/kappa_mon + +HISTORY 6: 2007 Apr 30 06:40:05.801 - NDFCOPY (KAPPA 1.7-2) +HISTORY User: operator Host: kolea Width: 72 +HISTORY Dataset: /jcmtdata/reduced/acsis/20070430/ga20070430_98 +HISTORY Parameters: IN=@/jcmtdata/reduced/acsis/20070430/oractemppLv7wv LIKE=! +HISTORY OUT=@ga20070430_98 TITLE=! TRIM=FALSE +HISTORY Software: /star/bin/kappa/ndfpack_mon + +HISTORY 7: 2007 May 10 10:42:40.627 - WCSMOSAIC (KAPPA 1.7-3) +HISTORY User: jbuckle Host: scubadev Width: 72 +HISTORY Dataset: /home/jbuckle/data/serpens_cube +HISTORY Parameters: ACC=0.05 GENVAR=FALSE ILEVEL=2 +HISTORY IN=@ga20070430_98,ga20070502_63_1 LBND=! LBOUND=[-77,-52,-820] +HISTORY MAXPIX=1000 METHOD='nearest' OUT=@serpens_cube REF=! +HISTORY UBOUND=[77,54,819] VARIANCE=TRUE WLIM=1E-10 +HISTORY Software: /star/bin/kappa/wcsmosaic + +HISTORY 8: 2007 May 17 12:04:41.724 - NDFCOPY (KAPPA 1.7-2) +HISTORY User: dsb Host: localhost.localdomain Width: 72 +HISTORY Dataset: /stardev/jaccvs/jcmt/scuba2/soft/smurf/makecube/jane +HISTORY Parameters: IN=@serpens_cube(~150,~120,0) LIKE=! OUT=@jane TITLE=! +HISTORY TRIM=TRUE TRIMWCS=TRUE USEAXIS=! +HISTORY Software: /stardev/cvs/star/bin/kappa/ndfcopy +END +XTENSION= 'IMAGE ' / IMAGE extension +BITPIX = -32 / number of bits per data pixel +NAXIS = 2 / number of data axes +NAXIS1 = 150 / length of data axis 1 +NAXIS2 = 120 / length of data axis 2 +PCOUNT = 0 / required keyword; must = 0 +GCOUNT = 1 / required keyword; must = 1 +LBOUND1 = -74 / Pixel origin along axis 1 +LBOUND2 = -58 / Pixel origin along axis 2 +OBJECT = 'ga20070430_98' / Title of the dataset +LABEL = 'T%s60+%v30+A%^50+%<20+*%+ corrected antenna ...'/ Label of the pri +BUNIT = 'K ' / Units of the primary array +DATE = '2007-05-17T11:05:09'/ file creation date (YYYY-MM-DDThh:mm:ss UT) +ORIGIN = 'Starlink Project, U.K.'/ Origin of this FITS file +BSCALE = 1.0 / True_value = BSCALE * FITS_value + BZERO +BZERO = 0.0 / True_value = BSCALE * FITS_value + BZERO +HDUCLAS1= 'NDF ' / Starlink NDF (hierarchical n-dim format) +HDUCLAS2= 'VARIANCE' / Array component subclass +EXTNAME = 'VARIANCE' / Array component +HDSTYPE = 'NDF ' / HDS data type of the component + +CRPIX1 = 75.910204 / Reference pixel on axis 1 +CRPIX2 = 60.359184 / Reference pixel on axis 2 +CRVAL1 = 1.220833333 / Value at ref. pixel on axis 1 +CRVAL2 = 277.5004167 / Value at ref. pixel on axis 2 +CTYPE1 = 'DEC--TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'RA---TAN' / Type of co-ordinate on axis 2 +CD1_1 = -0.001831773954 / Transformation matrix element +CD1_2 = -8.541702226E-4 / Transformation matrix element +CD2_1 = 0.0008541702226 / Transformation matrix element +CD2_2 = -0.001831773954 / Transformation matrix element +RADECSYS= 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / Epoch of reference equinox +DATE-OBS= '2007-04-30T15:13:53.717'/ Date of observation +END diff --git a/ast_tester/serpens.ps b/ast_tester/serpens.ps new file mode 100644 index 0000000..550e264 --- /dev/null +++ b/ast_tester/serpens.ps @@ -0,0 +1,705 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Thu May 17 12:12:01 2007 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 17/05/1907 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 580.4 117.1 581.4 115.1 2 pls + 568.6 117.1 569.5 115.1 2 pls + 543.8 119.4 545.8 115.1 2 pls + 556.7 117.1 557.7 115.1 2 pls + 533.0 117.1 533.9 115.1 2 pls + 521.2 117.1 522.1 115.1 2 pls + 496.4 119.4 498.4 115.1 2 pls + 509.3 117.1 510.2 115.1 2 pls + 485.6 117.1 486.5 115.1 2 pls + 473.7 117.1 474.7 115.1 2 pls + 449.0 119.4 451.0 115.1 2 pls + 461.9 117.1 462.8 115.1 2 pls + 438.2 117.1 439.1 115.1 2 pls + 426.3 117.1 427.2 115.1 2 pls + 401.5 119.4 403.5 115.1 2 pls + 414.5 117.1 415.4 115.1 2 pls + 390.7 117.1 391.7 115.1 2 pls + 378.9 117.1 379.8 115.1 2 pls + 354.1 119.4 356.1 115.1 2 pls + 367.0 117.1 368.0 115.1 2 pls + 343.3 117.1 344.2 115.1 2 pls + 331.5 117.1 332.4 115.1 2 pls + 306.7 119.4 308.7 115.1 2 pls + 319.6 117.1 320.5 115.1 2 pls + 295.9 117.1 296.8 115.1 2 pls + 284.0 117.1 285.0 115.1 2 pls + 259.3 119.4 261.2 115.1 2 pls + 272.2 117.1 273.1 115.1 2 pls + 248.5 117.1 249.4 115.1 2 pls + 236.6 117.1 237.5 115.1 2 pls + 211.8 119.4 213.8 115.1 2 pls + 224.7 117.1 225.7 115.1 2 pls + 201.0 117.1 202.0 115.1 2 pls + 203.7 419.1 199.5 417.1 2 pls + 201.4 406.2 199.5 405.3 2 pls + 201.4 394.4 199.5 393.4 2 pls + 203.7 359.9 199.5 357.9 2 pls + 201.4 382.5 199.5 381.6 2 pls + 201.4 370.7 199.5 369.7 2 pls + 201.4 346.9 199.5 346.0 2 pls + 201.4 335.1 199.5 334.2 2 pls + 203.7 300.6 199.5 298.6 2 pls + 201.4 323.2 199.5 322.3 2 pls + 201.4 311.4 199.5 310.5 2 pls + 201.4 287.7 199.5 286.7 2 pls + 201.4 275.8 199.5 274.9 2 pls + 203.7 241.3 199.5 239.3 2 pls + 201.4 264.0 199.5 263.0 2 pls + 201.4 252.1 199.5 251.2 2 pls + 201.4 228.4 199.5 227.5 2 pls + 201.4 216.6 199.5 215.6 2 pls + 203.7 182.0 199.5 180.1 2 pls + 201.4 204.7 199.5 203.8 2 pls + 201.4 192.8 199.5 191.9 2 pls + 201.4 169.1 199.5 168.2 2 pls + 201.4 157.3 199.5 156.4 2 pls + 203.7 122.8 199.5 120.8 2 pls + 201.4 145.4 199.5 144.5 2 pls + 201.4 133.6 199.5 132.7 2 pls + 590.4 426.3 589.7 427.9 2 pls + 578.7 425.9 577.8 427.9 2 pls + 566.9 425.9 566.0 427.9 2 pls + 544.2 423.6 542.3 427.9 2 pls + 555.0 425.9 554.1 427.9 2 pls + 531.3 425.9 530.4 427.9 2 pls + 519.5 425.9 518.5 427.9 2 pls + 496.8 423.6 494.8 427.9 2 pls + 507.6 425.9 506.7 427.9 2 pls + 483.9 425.9 483.0 427.9 2 pls + 472.0 425.9 471.1 427.9 2 pls + 449.4 423.6 447.4 427.9 2 pls + 460.2 425.9 459.3 427.9 2 pls + 436.5 425.9 435.5 427.9 2 pls + 424.6 425.9 423.7 427.9 2 pls + 402.0 423.6 400.0 427.9 2 pls + 412.7 425.9 411.8 427.9 2 pls + 389.0 425.9 388.1 427.9 2 pls + 377.2 425.9 376.3 427.9 2 pls + 354.5 423.6 352.5 427.9 2 pls + 365.3 425.9 364.4 427.9 2 pls + 341.6 425.9 340.7 427.9 2 pls + 329.8 425.9 328.8 427.9 2 pls + 307.1 423.6 305.1 427.9 2 pls + 317.9 425.9 317.0 427.9 2 pls + 294.2 425.9 293.3 427.9 2 pls + 282.3 425.9 281.4 427.9 2 pls + 259.7 423.6 257.7 427.9 2 pls + 270.5 425.9 269.5 427.9 2 pls + 246.7 425.9 245.8 427.9 2 pls + 234.9 425.9 234.0 427.9 2 pls + 212.2 423.6 210.3 427.9 2 pls + 223.0 425.9 222.1 427.9 2 pls + 586.1 419.7 590.4 421.6 2 pls + 588.4 408.9 590.4 409.8 2 pls + 588.4 397.0 590.4 397.9 2 pls + 586.1 360.4 590.4 362.4 2 pls + 588.4 385.1 590.4 386.1 2 pls + 588.4 373.3 590.4 374.2 2 pls + 588.4 349.6 590.4 350.5 2 pls + 588.4 337.7 590.4 338.7 2 pls + 586.1 301.1 590.4 303.1 2 pls + 588.4 325.9 590.4 326.8 2 pls + 588.4 314.0 590.4 314.9 2 pls + 588.4 290.3 590.4 291.2 2 pls + 588.4 278.5 590.4 279.4 2 pls + 586.1 241.8 590.4 243.8 2 pls + 588.4 266.6 590.4 267.5 2 pls + 588.4 254.7 590.4 255.7 2 pls + 588.4 231.0 590.4 232.0 2 pls + 588.4 219.2 590.4 220.1 2 pls + 586.1 182.6 590.4 184.5 2 pls + 588.4 207.3 590.4 208.3 2 pls + 588.4 195.5 590.4 196.4 2 pls + 588.4 171.8 590.4 172.7 2 pls + 588.4 159.9 590.4 160.8 2 pls + 586.1 123.3 590.4 125.3 2 pls + 588.4 148.1 590.4 149.0 2 pls + 588.4 136.2 590.4 137.1 2 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 ][ 115.1 115.1 115.1 + 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.1 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 115.1 137.5 159.8 + 182.1 204.5 226.8 249.1 271.5 293.8 316.2 338.5 360.8 383.2 405.5 427.9 ]plong + s[ 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 283.2 255.3 227.4 199.5 ][ 427.9 427.9 427.9 + 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 427.9 405.5 383.2 + 360.8 338.5 316.2 293.8 271.5 249.1 226.8 204.5 182.1 159.8 137.5 115.1 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 204.63 99.87 moveto[ 204.6 204.6 223.4 223.4 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 207.1 207.1 207.6 208.0 208.8 210.5 211.3 211.7 212.1 212.1 211.7 210.9 206.7 212.6 ][ 108.2 108.6 109.5 109.9 + 110.3 110.3 109.9 109.5 108.6 107.8 107.0 105.7 101.5 101.5 ]plong + 221.3 104.5 215.1 104.5 219.2 110.3 3 pls + 219.2 101.5 219.2 110.3 2 pls +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 252.06 99.87 moveto[ 252.1 252.1 270.4 270.4 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 254.6 254.6 255.0 255.4 256.2 257.9 258.7 259.2 259.6 259.6 259.2 258.3 254.1 260.0 ][ 108.2 108.6 109.5 109.9 + 110.3 110.3 109.9 109.5 108.6 107.8 107.0 105.7 101.5 101.5 ]plong + s[ 262.9 262.9 263.3 263.8 264.6 266.3 267.1 267.5 267.9 267.9 267.5 266.7 262.5 268.3 ][ 108.2 108.6 109.5 109.9 + 110.3 110.3 109.9 109.5 108.6 107.8 107.0 105.7 101.5 101.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 294.47 99.87 moveto[ 294.5 294.5 324.1 324.1 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 298.6 101.5 298.6 110.3 297.4 109.1 296.6 108.6 4 pls + 304.5 107.4 304.9 107.0 304.5 106.6 304.1 107.0 304.5 107.4 5 pls + 304.5 102.4 304.9 102.0 304.5 101.5 304.1 102.0 304.5 102.4 5 pls + s[ 308.3 308.3 308.7 309.1 309.9 311.6 312.4 312.8 313.3 313.3 312.8 312.0 307.8 313.7 ][ 108.2 108.6 109.5 109.9 + 110.3 110.3 109.9 109.5 108.6 107.8 107.0 105.7 101.5 101.5 ]plong + s[ 318.7 317.4 316.6 316.2 316.2 316.6 317.4 318.7 319.5 320.8 321.6 322.0 322.0 321.6 320.8 319.5 318.7 ][ 110.3 + 109.9 108.6 106.6 105.3 103.2 102.0 101.5 101.5 102.0 103.2 105.3 106.6 108.6 109.9 110.3 110.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 348.16 99.87 moveto[ 348.2 348.2 365.3 365.3 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 352.3 101.5 352.3 110.3 351.1 109.1 350.3 108.6 4 pls + s[ 359.4 358.2 357.8 357.8 358.2 359.0 360.7 361.9 362.8 363.2 363.2 362.8 362.4 361.1 359.4 358.2 357.8 357.4 + 357.4 357.8 358.6 359.9 361.5 362.4 362.8 362.8 362.4 361.1 359.4 ][ 110.3 109.9 109.1 108.2 107.4 107.0 106.6 + 106.1 105.3 104.5 103.2 102.4 102.0 101.5 101.5 102.0 102.4 103.2 104.5 105.3 106.1 106.6 107.0 107.4 108.2 + 109.1 109.9 110.3 110.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 395.59 99.87 moveto[ 395.6 395.6 412.7 412.7 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 399.8 101.5 399.8 110.3 398.5 109.1 397.7 108.6 4 pls + s[ 410.2 409.8 408.5 407.7 406.4 405.6 405.2 405.2 405.6 406.4 407.7 408.1 409.4 410.2 410.6 410.6 410.2 409.4 + 408.1 407.7 406.4 405.6 405.2 ][ 109.1 109.9 110.3 110.3 109.9 108.6 106.6 104.5 102.8 102.0 101.5 101.5 102.0 + 102.8 104.0 104.5 105.7 106.6 107.0 107.0 106.6 105.7 104.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 443.02 99.87 moveto[ 443.0 443.0 460.6 460.6 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 447.2 101.5 447.2 110.3 445.9 109.1 445.1 108.6 4 pls + 458.5 104.5 452.2 104.5 456.4 110.3 3 pls + 456.4 101.5 456.4 110.3 2 pls +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 490.44 99.87 moveto[ 490.4 490.4 507.6 507.6 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 494.6 101.5 494.6 110.3 493.4 109.1 492.5 108.6 4 pls + s[ 500.0 500.0 500.5 500.9 501.7 503.4 504.2 504.6 505.1 505.1 504.6 503.8 499.6 505.5 ][ 108.2 108.6 109.5 109.9 + 110.3 110.3 109.9 109.5 108.6 107.8 107.0 105.7 101.5 101.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 537.87 99.87 moveto[ 537.9 537.9 555.0 555.0 ][ 99.9 112.0 112.0 99.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 542.0 101.5 542.0 110.3 540.8 109.1 540.0 108.6 4 pls + s[ 549.6 548.3 547.5 547.1 547.1 547.5 548.3 549.6 550.4 551.7 552.5 552.9 552.9 552.5 551.7 550.4 549.6 ][ 110.3 + 109.9 108.6 106.6 105.3 103.2 102.0 101.5 101.5 102.0 103.2 105.3 106.6 108.6 109.9 110.3 110.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 180.03 113.91 moveto[ 180.0 180.0 197.2 197.2 ][ 113.9 126.0 126.0 113.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 184.2 115.6 184.2 124.3 183.0 123.1 182.1 122.7 4 pls + s[ 191.7 190.5 189.6 189.2 189.2 189.6 190.5 191.7 192.6 193.8 194.7 195.1 195.1 194.7 193.8 192.6 191.7 ][ 124.3 + 123.9 122.7 120.6 119.3 117.2 116.0 115.6 115.6 116.0 117.2 119.3 120.6 122.7 123.9 124.3 124.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 138.27 173.17 moveto[ 138.3 138.3 197.2 197.2 ][ 173.2 185.3 185.3 173.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 142.4 174.8 142.4 183.6 141.2 182.4 140.4 181.9 4 pls + s[ 149.5 148.3 147.9 147.9 148.3 149.1 150.8 152.1 152.9 153.3 153.3 152.9 152.5 151.2 149.5 148.3 147.9 147.5 + 147.5 147.9 148.7 150.0 151.6 152.5 152.9 152.9 152.5 151.2 149.5 ][ 183.6 183.2 182.4 181.5 180.7 180.3 179.9 + 179.4 178.6 177.8 176.5 175.7 175.3 174.8 174.8 175.3 175.7 176.5 177.8 178.6 179.4 179.9 180.3 180.7 181.5 + 182.4 183.2 183.6 183.6 ]plong + 156.6 180.7 157.1 180.3 156.6 179.9 156.2 180.3 156.6 180.7 5 pls + 156.6 175.7 157.1 175.3 156.6 174.8 156.2 175.3 156.6 175.7 5 pls + s[ 160.8 165.4 162.9 164.2 165.0 165.4 165.8 165.8 165.4 164.6 163.3 162.1 160.8 160.4 160.0 ][ 183.6 183.6 180.3 + 180.3 179.9 179.4 178.2 177.4 176.1 175.3 174.8 174.8 175.3 175.7 176.5 ]plong + s[ 170.8 169.6 168.8 168.3 168.3 168.8 169.6 170.8 171.7 172.9 173.8 174.2 174.2 173.8 172.9 171.7 170.8 ][ 183.6 + 183.2 181.9 179.9 178.6 176.5 175.3 174.8 174.8 175.3 176.5 178.6 179.9 181.9 183.2 183.6 183.6 ]plong + 177.5 180.7 177.9 180.3 177.5 179.9 177.1 180.3 177.5 180.7 5 pls + 177.5 175.7 177.9 175.3 177.5 174.8 177.1 175.3 177.5 175.7 5 pls + s[ 183.4 182.1 181.3 180.9 180.9 181.3 182.1 183.4 184.2 185.5 186.3 186.7 186.7 186.3 185.5 184.2 183.4 ][ 183.6 + 183.2 181.9 179.9 178.6 176.5 175.3 174.8 174.8 175.3 176.5 178.6 179.9 181.9 183.2 183.6 183.6 ]plong + s[ 191.7 190.5 189.6 189.2 189.2 189.6 190.5 191.7 192.6 193.8 194.7 195.1 195.1 194.7 193.8 192.6 191.7 ][ 183.6 + 183.2 181.9 179.9 178.6 176.5 175.3 174.8 174.8 175.3 176.5 178.6 179.9 181.9 183.2 183.6 183.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.78 232.44 moveto[ 178.8 178.8 197.2 197.2 ][ 232.4 244.6 244.6 232.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 185.9 181.7 181.3 181.7 183.0 184.2 185.5 186.3 186.7 186.7 186.3 185.5 184.2 183.0 181.7 181.3 180.9 ][ 242.9 + 242.9 239.1 239.5 240.0 240.0 239.5 238.7 237.5 236.6 235.4 234.5 234.1 234.1 234.5 234.9 235.8 ]plong + s[ 191.7 190.5 189.6 189.2 189.2 189.6 190.5 191.7 192.6 193.8 194.7 195.1 195.1 194.7 193.8 192.6 191.7 ][ 242.9 + 242.5 241.2 239.1 237.9 235.8 234.5 234.1 234.1 234.5 235.8 237.9 239.1 241.2 242.5 242.9 242.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.78 291.71 moveto[ 178.8 178.8 197.2 197.2 ][ 291.7 303.8 303.8 291.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 187.1 296.3 180.9 296.3 185.0 302.2 3 pls + 185.0 293.4 185.0 302.2 2 pls + s[ 191.7 190.5 189.6 189.2 189.2 189.6 190.5 191.7 192.6 193.8 194.7 195.1 195.1 194.7 193.8 192.6 191.7 ][ 302.2 + 301.7 300.5 298.4 297.1 295.1 293.8 293.4 293.4 293.8 295.1 297.1 298.4 300.5 301.7 302.2 302.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.78 350.98 moveto[ 178.8 178.8 197.2 197.2 ][ 351.0 363.1 363.1 351.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 181.7 186.3 183.8 185.0 185.9 186.3 186.7 186.7 186.3 185.5 184.2 183.0 181.7 181.3 180.9 ][ 361.4 361.4 358.1 + 358.1 357.7 357.2 356.0 355.2 353.9 353.1 352.6 352.6 353.1 353.5 354.3 ]plong + s[ 191.7 190.5 189.6 189.2 189.2 189.6 190.5 191.7 192.6 193.8 194.7 195.1 195.1 194.7 193.8 192.6 191.7 ][ 361.4 + 361.0 359.8 357.7 356.4 354.3 353.1 352.6 352.6 353.1 354.3 356.4 357.7 359.8 361.0 361.4 361.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 157.90 410.25 moveto[ 157.9 157.9 197.2 197.2 ][ 410.2 422.4 422.4 410.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 160.4 160.4 160.8 161.2 162.1 163.7 164.6 165.0 165.4 165.4 165.0 164.2 160.0 165.8 ][ 418.6 419.0 419.9 420.3 + 420.7 420.7 420.3 419.9 419.0 418.2 417.4 416.1 411.9 411.9 ]plong + s[ 173.8 173.4 172.5 171.3 170.8 169.6 168.8 168.3 168.3 168.8 169.6 170.8 171.3 172.5 173.4 173.8 173.8 173.4 + 172.5 171.3 170.4 169.2 168.8 ][ 417.8 416.5 415.7 415.3 415.3 415.7 416.5 417.8 418.2 419.4 420.3 420.7 420.7 + 420.3 419.4 417.8 415.7 413.6 412.3 411.9 411.9 412.3 413.2 ]plong + 177.5 417.8 177.9 417.4 177.5 416.9 177.1 417.4 177.5 417.8 5 pls + 177.5 412.8 177.9 412.3 177.5 411.9 177.1 412.3 177.5 412.8 5 pls + s[ 181.3 181.3 181.7 182.1 183.0 184.6 185.5 185.9 186.3 186.3 185.9 185.0 180.9 186.7 ][ 418.6 419.0 419.9 420.3 + 420.7 420.7 420.3 419.9 419.0 418.2 417.4 416.1 411.9 411.9 ]plong + s[ 191.7 190.5 189.6 189.2 189.2 189.6 190.5 191.7 192.6 193.8 194.7 195.1 195.1 194.7 193.8 192.6 191.7 ][ 420.7 + 420.3 419.0 416.9 415.7 413.6 412.3 411.9 411.9 412.3 413.6 415.7 416.9 419.0 420.3 420.7 420.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 359.21 84.21 moveto[ 359.2 359.2 430.6 430.6 ][ 84.2 96.7 96.7 84.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 361.3 85.9 361.3 94.7 2 pls + s[ 361.3 364.2 365.5 366.3 366.7 367.1 367.1 366.7 366.3 365.5 364.2 361.3 ][ 94.7 94.7 94.2 93.4 92.6 91.3 + 89.2 88.0 87.1 86.3 85.9 85.9 ]plong + s[ 369.7 374.7 374.7 374.2 373.8 373.0 371.7 370.9 370.1 369.7 369.7 370.1 370.9 371.7 373.0 373.8 374.7 ][ 89.2 + 89.2 90.1 90.9 91.3 91.7 91.7 91.3 90.5 89.2 88.4 87.1 86.3 85.9 85.9 86.3 87.1 ]plong + s[ 382.2 381.3 380.5 379.3 378.4 377.6 377.2 377.2 377.6 378.4 379.3 380.5 381.3 382.2 ][ 90.5 91.3 91.7 91.7 + 91.3 90.5 89.2 88.4 87.1 86.3 85.9 85.9 86.3 87.1 ]plong + 385.1 85.9 385.1 94.7 2 pls + 388.0 94.7 388.4 95.1 388.9 94.7 388.4 94.2 388.0 94.7 5 pls + 388.4 85.9 388.4 91.7 2 pls + 391.8 85.9 391.8 91.7 2 pls + s[ 391.8 393.0 393.9 395.1 396.0 396.4 396.4 ][ 90.1 91.3 91.7 91.7 91.3 90.1 85.9 ]plong + 404.3 85.9 404.3 91.7 2 pls + s[ 404.3 403.5 402.6 401.4 400.6 399.7 399.3 399.3 399.7 400.6 401.4 402.6 403.5 404.3 ][ 90.5 91.3 91.7 91.7 + 91.3 90.5 89.2 88.4 87.1 86.3 85.9 85.9 86.3 87.1 ]plong + 410.2 85.9 409.3 85.9 408.5 86.3 408.1 87.6 408.1 94.7 5 pls + 409.8 91.7 406.8 91.7 2 pls + 412.3 94.7 412.7 95.1 413.1 94.7 412.7 94.2 412.3 94.7 5 pls + 412.7 85.9 412.7 91.7 2 pls + s[ 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 421.0 421.0 420.6 419.8 418.9 417.7 ][ 91.7 + 91.3 90.5 89.2 88.4 87.1 86.3 85.9 85.9 86.3 87.1 88.4 89.2 90.5 91.3 91.7 91.7 ]plong + 424.0 85.9 424.0 91.7 2 pls + s[ 424.0 425.2 426.0 427.3 428.1 428.5 428.5 ][ 90.1 91.3 91.7 91.7 91.3 90.1 85.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 132.63 219.05 moveto[ 132.6 117.2 117.2 132.6 ][ 219.1 219.1 323.9 323.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 128.0 221.1 119.3 221.1 2 pls + s[ 119.3 119.3 119.7 120.1 120.9 121.8 122.6 123.0 123.4 123.4 ][ 221.1 224.9 226.2 226.6 227.0 227.0 226.6 226.2 + 224.9 221.1 ]plong + 128.0 227.0 123.4 224.1 2 pls + 119.3 229.5 118.9 229.9 119.3 230.3 119.7 229.9 119.3 229.5 5 pls + 128.0 229.9 122.2 229.9 2 pls + s[ 122.2 128.9 130.1 130.5 131.0 131.0 130.5 ][ 237.8 237.8 237.4 237.0 236.2 234.9 234.1 ]plong + s[ 123.4 122.6 122.2 122.2 122.6 123.4 124.7 125.5 126.8 127.6 128.0 128.0 127.6 126.8 ][ 237.8 237.0 236.2 234.9 + 234.1 233.3 232.8 232.8 233.3 234.1 234.9 236.2 237.0 237.8 ]plong + 128.0 241.2 119.3 241.2 2 pls + s[ 123.9 122.6 122.2 122.2 122.6 123.9 128.0 ][ 241.2 242.4 243.3 244.5 245.4 245.8 245.8 ]plong + 128.0 251.6 128.0 250.8 127.6 250.0 126.4 249.5 119.3 249.5 5 pls + 122.2 251.2 122.2 248.3 2 pls + 128.0 265.4 122.2 265.4 2 pls + s[ 123.4 122.6 122.2 122.2 122.6 123.4 124.7 125.5 126.8 127.6 128.0 128.0 127.6 126.8 ][ 265.4 264.6 263.7 262.5 + 261.7 260.8 260.4 260.4 260.8 261.7 262.5 263.7 264.6 265.4 ]plong + s[ 123.4 122.6 122.2 122.2 122.6 123.4 124.3 124.7 125.1 125.5 126.4 126.8 127.6 128.0 128.0 127.6 126.8 ][ 272.9 + 272.5 271.3 270.0 268.8 268.3 268.8 269.6 271.7 272.5 272.9 272.9 272.5 271.3 270.0 268.8 268.3 ]plong + s[ 123.4 122.6 122.2 122.2 122.6 123.4 124.7 125.5 126.8 127.6 128.0 128.0 127.6 126.8 ][ 280.5 279.6 278.8 277.5 + 276.7 275.9 275.4 275.4 275.9 276.7 277.5 278.8 279.6 280.5 ]plong + s[ 124.7 124.7 123.9 123.0 122.6 122.2 122.2 122.6 123.4 124.7 125.5 126.8 127.6 128.0 128.0 127.6 126.8 ][ 283.0 + 288.0 288.0 287.6 287.1 286.3 285.0 284.2 283.4 283.0 283.0 283.4 284.2 285.0 286.3 287.1 288.0 ]plong + 128.0 290.9 122.2 290.9 2 pls + s[ 123.9 122.6 122.2 122.2 122.6 123.9 128.0 ][ 290.9 292.1 293.0 294.2 295.1 295.5 295.5 ]plong + s[ 123.4 122.6 122.2 122.2 122.6 123.4 124.3 124.7 125.1 125.5 126.4 126.8 127.6 128.0 128.0 127.6 126.8 ][ 303.0 + 302.6 301.3 300.1 298.8 298.4 298.8 299.7 301.8 302.6 303.0 303.0 302.6 301.3 300.1 298.8 298.4 ]plong + 119.3 305.5 118.9 305.9 119.3 306.3 119.7 305.9 119.3 305.5 5 pls + 128.0 305.9 122.2 305.9 2 pls + s[ 122.2 122.6 123.4 124.7 125.5 126.8 127.6 128.0 128.0 127.6 126.8 125.5 124.7 123.4 122.6 122.2 122.2 ][ 310.9 + 310.1 309.3 308.9 308.9 309.3 310.1 310.9 312.2 313.0 313.9 314.3 314.3 313.9 313.0 312.2 310.9 ]plong + 128.0 317.2 122.2 317.2 2 pls + s[ 123.9 122.6 122.2 122.2 122.6 123.9 128.0 ][ 317.2 318.5 319.3 320.5 321.4 321.8 321.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 443.50 moveto[ 360.9 360.9 435.2 435.2 ][ 443.5 455.6 455.6 443.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 445.2 366.3 453.9 2 pls + 369.7 445.2 366.3 453.9 2 pls + 368.4 448.1 364.2 448.1 2 pls + 378.4 445.2 378.4 453.9 2 pls + 383.9 453.9 378.4 453.9 2 pls + 381.8 449.8 378.4 449.8 2 pls + 385.9 445.2 385.9 453.9 2 pls + 391.0 445.2 391.0 453.9 2 pls + 393.9 453.9 388.0 453.9 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 452.7 453.5 453.9 453.9 453.5 452.7 451.9 451.0 450.6 450.2 449.4 448.9 448.5 447.7 446.4 445.6 + 445.2 445.2 445.6 446.4 ]plong + 413.5 445.2 412.7 445.2 411.8 445.6 411.4 446.8 411.4 453.9 5 pls + 413.1 451.0 410.2 451.0 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 448.5 + 448.5 449.4 450.2 450.6 451.0 451.0 450.6 449.8 448.5 447.7 446.4 445.6 445.2 445.2 445.6 446.4 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 449.8 + 450.6 451.0 451.0 450.6 449.8 448.9 448.5 448.1 447.7 446.8 446.4 445.6 445.2 445.2 445.6 446.4 ]plong + 433.1 445.2 432.3 445.2 431.5 445.6 431.1 446.8 431.1 453.9 5 pls + 432.7 451.0 429.8 451.0 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/simplify.f b/ast_tester/simplify.f new file mode 100644 index 0000000..063ebd1 --- /dev/null +++ b/ast_tester/simplify.f @@ -0,0 +1,123 @@ + PROGRAM SIMPLIFY + +* Usage: +* simplify + +* Description: +* Reads a Mapping from "in file" (as an AST dump), and writes out the +* simplified Mapping to "out file". + +* Parameters: +* in file +* A text file containing an AST dump of a Mapping. +* out file +* The output file. Contains an AST dump of the simplified Mapping +* on exit. + + + IMPLICIT NONE + INCLUDE 'AST_PAR' + EXTERNAL SOURCE, SINK + + INTEGER STATUS, OBJECT, IARGC, CHAN, CHR_LEN, OC, SMAP + CHARACTER FILE*80, OFILE*80, LINE*255 + + STATUS = 0 +* +* Check command line arguments have been supplied. +* + IF( IARGC() .LT. 2 ) THEN + WRITE(*,*) 'Usage: simplify ' + RETURN + END IF + +* +* Open the name of the input text file. +* + CALL GETARG( 1, FILE ) + +* Attempt to read an object from the text file as an AST dump. + OPEN( UNIT=10, FILE=FILE, STATUS='OLD' ) + CHAN = AST_CHANNEL( SOURCE, AST_NULL, ' ', STATUS ) + OBJECT = AST_READ( CHAN, STATUS ) + CALL AST_ANNUL( CHAN, STATUS ) + CLOSE( 10 ) + +* +* Abort if no object was read. +* + IF( OBJECT .EQ. AST__NULL ) THEN + WRITE(*,*) 'simplify: no Mapping could be read from ', + : file( : chr_len( file ) ) + RETURN + +* +* Otherwise write out the simplified Mapping +* + ELSE + CALL GETARG( 2, OFILE ) + CALL DELETEFILE( OFILE ) + + SMAP = AST_SIMPLIFY( OBJECT, STATUS ) + + OPEN( UNIT=10, FILE=OFILE, STATUS='NEW' ) + CHAN = AST_CHANNEL( AST_NULL, SINK, ' ', STATUS ) + IF( AST_WRITE( CHAN, SMAP, STATUS ) .NE. 1 ) THEN + WRITE(*,*) 'simplify: Simplified Mapping read from ', + : file( : chr_len( file ) ),' could not be '// + : 'written out.' + END IF + CALL AST_ANNUL( CHAN, STATUS ) + CALL AST_ANNUL( SMAP, STATUS ) + CLOSE( 10 ) + END IF + + + END + + +* +* Delete a file if it exists. +* + SUBROUTINE DELETEFILE( FILNAM ) + IMPLICIT NONE + + CHARACTER FILNAM*(*) + LOGICAL EXISTS + + INQUIRE ( FILE = FILNAM, + : EXIST = EXISTS ) + + IF( EXISTS ) THEN + OPEN ( UNIT=10, FILE=FILNAM, STATUS='OLD' ) + CLOSE ( 10, STATUS='DELETE' ) + END IF + + END + + +* +* SOURCE FUNCTION FOR AST_CHANNEL. +* + SUBROUTINE SOURCE( STATUS ) + IMPLICIT NONE + INTEGER STATUS + CHARACTER BUFFER*200 + READ( 10, '(A)', END=99 ) BUFFER + CALL AST_PUTLINE( BUFFER, LEN( BUFFER ), STATUS ) + RETURN + 99 CALL AST_PUTLINE( BUFFER, -1, STATUS ) + END + +* +* SINK FUNCTION FOR AST_CHANNEL. +* + SUBROUTINE SINK( STATUS ) + IMPLICIT NONE + INTEGER STATUS, L + CHARACTER BUFFER*200 + + CALL AST_GETLINE( BUFFER, L, STATUS ) + IF( L .GT. 0 ) WRITE( 10, '(A)' ) BUFFER( : L ) + + END diff --git a/ast_tester/sip.head b/ast_tester/sip.head new file mode 100644 index 0000000..0e9e838 --- /dev/null +++ b/ast_tester/sip.head @@ -0,0 +1,288 @@ +SIMPLE = T / Fits standard +BITPIX = -32 / FOUR-BYTE SINGLE PRECISION FLOATING POINT +NAXIS = 2 / STANDARD FITS FORMAT +NAXIS1 = 256 / STANDARD FITS FORMAT +NAXIS2 = 256 / STANDARD FITS FORMAT +ORIGIN = 'Spitzer Science Center' / Organization generating this FITS file +CREATOR = 'S16.1.0 ' / SW version used to create this FITS file +TELESCOP= 'Spitzer ' / SPITZER Space Telescope +INSTRUME= 'IRAC ' / SPITZER Space Telescope instrument ID +CHNLNUM = 1 / 1 digit instrument channel number +EXPTYPE = 'sci ' / Exposure Type +REQTYPE = 'AOR ' / Request type (AOR, IER, or SER) +AOT_TYPE= 'IracMap ' / Observation template type +AORLABEL= '05cs-IRAC-2' / AOR Label +FOVID = 67 / Field of View ID +FOVNAME = 'IRAC_Center_of_3.6&5.8umArray' / Field of View Name + + / PROPOSAL INFORMATION + +OBSRVR = 'Ben Sugerman' / Observer Name (Last, First) +OBSRVRID= 14621 / Observer ID of Principal Investigator +PROCYCL = 6 / Proposal Cycle +PROGID = 30494 / Program ID +PROTITLE= 'Supernovae and the Origin of Dust in Galaxies: Follow-Up Observation' +PROGCAT = 30 / Program Category + + / TIME AND EXPOSURE INFORMATION + +DATE_OBS= '2007-06-29T03:15:33.555' / Date & time at DCE start +MJD_OBS = 54280.135805 / [days] MJD at DCE start (,JD-2400000.5) +UTCS_OBS= 236358933.555 / [sec] J2000 ephem. time at DCE start +SCLK_OBS= 867554299.94 / [sec] SCLK time (since 1/1/1980) at DCE start +SAMPTIME= 0.2 / [sec] Sample integration time +FRAMTIME= 12. / [sec] Time spent integrating (whole array) +COMMENT Photons in Well = Flux[photons/sec/pixel] * FRAMTIME +EXPTIME = 10.4 / [sec] Effective integration time per pixel +COMMENT DN per pixel = Flux[photons/sec/pixel] / GAIN * EXPTIME +INTRFDLY= 8. / [sec] Inter Frame Delay Time +AINTBEG = 107646.2 / [Secs since IRAC turn-on] Time of integ. start +ATIMEEND= 107658.16 / [Secs since IRAC turn-on] Time of integ. end +AFOWLNUM= 8 / Fowler number +AWAITPER= 44 / [0.2 sec] Wait period +ANUMREPS= 1 / Number of repeat integrations +AREADMOD= 0 / Full (0) or subarray (1) +HDRMODE = F / DCE taken in High Dynamic Range mode +ABARREL = 3 / Barrel shift +APEDSIG = 0 / 0=Normal, 1=Pedestal, 2=Signal + + / TARGET AND POINTING INFORMATION + +OBJECT = 'SN 2005cs' / Target Name +OBJTYPE = 'TargetFixedSingle' / Object Type +CRVAL1 = 202.482322805429 / [deg] RA at CRPIX1,CRPIX2 (using Pointing Recon +CRVAL2 = 47.1751189300101 / [deg] DEC at CRPIX1,CRPIX2 (using Pointing Reco +RA_HMS = '13h29m55.8s' / [hh:mm:ss.s] CRVAL1 as sexagesimal +DEC_DMS = '+47d10m30s' / [dd:mm:ss] CRVAL2 as sexagesimal +RADESYS = 'ICRS ' / International Celestial Reference System +EQUINOX = 2000. / Equinox for ICRS celestial coord. system +CD1_1 = 0.000249756880272355 / Corrected CD matrix element with Pointing Recon +CD1_2 = 0.000230177809743655 / Corrected CD matrix element with Pointing Recon +CD2_1 = 0.000230428519265417 / Corrected CD matrix element with Pointing Recon +CD2_2 = -0.000249965770576587 / Corrected CD matrix element with Pointing Reco +CTYPE1 = 'RA---TAN-SIP' / RA---TAN with distortion in pixel space +CTYPE2 = 'DEC--TAN-SIP' / DEC--TAN with distortion in pixel space +CRPIX1 = 128. / Reference pixel along axis 1 +CRPIX2 = 128. / Reference pixel along axis 2 +PXSCAL1 = -1.22334117768332 / [arcsec/pix] Scale for axis 1 at CRPIX1,CRPIX2 +PXSCAL2 = 1.22328355209902 / [arcsec/pix] Scale for axis 2 at CRPIX1,CRPIX2 +CRDER1 = 4.02509762361481E-05 / [deg] Uncertainty in CRVAL1 +CRDER2 = 3.4274613195288E-05 / [deg] Uncertainty in CRVAL2 +UNCRTPA = 0.000378787843382542 / [deg] Uncertainty in position angle +CSDRADEC= 4.75971783897902E-07 / [deg] Costandard deviation in RA and Dec +SIGRA = 0.0339487135007807 / [arcsec] RMS dispersion of RA over DCE +SIGDEC = 0.0637682657426773 / [arcsec] RMS dispersion of DEC over DCE +SIGPA = 1.36464273573987 / [arcsec] RMS dispersion of PA over DCE +PA = 137.359976084299 / [deg] Position angle of axis 2 (E of N) (was OR +RA_RQST = 202.482116615975 / [deg] Requested RA at CRPIX1, CRPIX2 +DEC_RQST= 47.1750785413522 / [deg] Requested Dec at CRPIX1, CRPIX2 +PM_RA = 0. / [arcsec/yr] Proper Motion in RA (J2000) +PM_DEC = 0. / [arcsec/yr] Proper Motion in Dec (J200) +RMS_JIT = 0.00768303401855732 / [arcsec] RMS jitter during DCE +RMS_JITY= 0.00515070337437348 / [arcsec] RMS jitter during DCE along Y +RMS_JITZ= 0.00570081279113133 / [arcsec] RMS jitter during DCE along Z +SIG_JTYZ= 0.00237806410221437 / [arcsec] Costadard deviation of jitter in YZ +PTGDIFF = 0.525106524659797 / [arcsec] Offset btwn actual and rqsted pntng +PTGDIFFX= 0.46966798486043 / [pixels] rqsted - actual pntng along axis 1 +PTGDIFFY= -0.235285197422366 / [pixels] rqsted - actual pntng along axis 2 +RA_REF = 202.472375 / [deg] Commanded RA (J2000) of ref. position +DEC_REF = 47.1745 / [deg] Commanded Dec (J2000) of ref. position +USEDBPHF= T / T if Boresight Pointing History File was used +BPHFNAME= 'SBPHF.0867542400.031.pntg' / Boresight Pointing History Filename +FOVVERSN= 'BodyFrames_FTU_14a.xls' / FOV/BodyFrames file version used +RECONFOV= 'IRAC_Center_of_3.6umArray' / Reconstructed Field of View +RARFND = 202.482315063477 / [deg] Refined RA +DECRFND = 47.175121307373 / [deg] Refined DEC +CT2RFND = 137.359054565430 / [deg] Refined CROT2 +ERARFND = 0.000002332304 / [deg] Error in RARFND value +EDECRFND= 0.000002332411 / [deg] Error in DECRFND value +ECT2RFND= 0.000540699053 / [deg] Error in CT2RFND value +NASTROM = 12 / Astrometric sources for absolute refinement +ORIG_RA = 202.482055664062 / [deg] Original RA from raw BPHF (without pointi +ORIG_DEC= 47.1750717163086 / [deg] Original Dec from raw BPHF (without point +ORIGCD11= 0.0002497563255 / [deg/pix] Original CD1_1 element (without point +ORIGCD12= 0.0002301784261 / [deg/pix] Original CD1_2 element (without point +ORIGCD21= 0.0002304291411 / [deg/pix] Original CD2_1 element (without point +ORIGCD22= -0.0002499652037 / [deg/pix] Original CD2_2 element (without point + + + + / DISTORTION KEYWORDS + +A_ORDER = 3 / polynomial order, axis 1, detector to sky +A_0_2 = 2.9656E-06 / distortion coefficient +A_0_3 = 3.7746E-09 / distortion coefficient +A_1_1 = 2.1886E-05 / distortion coefficient +A_1_2 = -1.6847E-07 / distortion coefficient +A_2_0 = -2.3863E-05 / distortion coefficient +A_2_1 = -8.561E-09 / distortion coefficient +A_3_0 = -1.4172E-07 / distortion coefficient +A_DMAX = 1.394 / [pixel] maximum correction +B_ORDER = 3 / polynomial order, axis 2, detector to sky +B_0_2 = 2.31E-05 / distortion coefficient +B_0_3 = -1.6168E-07 / distortion coefficient +B_1_1 = -2.4386E-05 / distortion coefficient +B_1_2 = -5.7813E-09 / distortion coefficient +B_2_0 = 2.1197E-06 / distortion coefficient +B_2_1 = -1.6583E-07 / distortion coefficient +B_3_0 = -2.0249E-08 / distortion coefficient +B_DMAX = 1.501 / [pixel] maximum correction +AP_ORDER= 3 / polynomial order, axis 1, sky to detector +AP_0_1 = -6.4275E-07 / distortion coefficient +AP_0_2 = -2.9425E-06 / distortion coefficient +AP_0_3 = -3.582E-09 / distortion coefficient +AP_1_0 = -1.4897E-05 / distortion coefficient +AP_1_1 = -2.225E-05 / distortion coefficient +AP_1_2 = 1.7195E-07 / distortion coefficient +AP_2_0 = 2.4146E-05 / distortion coefficient +AP_2_1 = 6.709E-09 / distortion coefficient +AP_3_0 = 1.4492E-07 / distortion coefficient +BP_ORDER= 3 / polynomial order, axis 2, sky to detector +BP_0_1 = -1.6588E-05 / distortion coefficient +BP_0_2 = -2.3424E-05 / distortion coefficient +BP_0_3 = 1.651E-07 / distortion coefficient +BP_1_0 = -2.6783E-06 / distortion coefficient +BP_1_1 = 2.4753E-05 / distortion coefficient +BP_1_2 = 3.8917E-09 / distortion coefficient +BP_2_0 = -2.151E-06 / distortion coefficient +BP_2_1 = 1.7E-07 / distortion coefficient +BP_3_0 = 2.0482E-08 / distortion coefficient + + / PHOTOMETRY + +BUNIT = 'MJy/sr ' / Units of image data +FLUXCONV= 0.1088 / Flux Conv. factor (MJy/sr per DN/sec) +GAIN = 3.3 / e/DN conversion +RONOISE = 9.4 / [Electrons] Readout Noise from Array +ZODY_EST= 0.05291139 / [MJy/sr] Zodiacal Background Estimate +ISM_EST = 0.003677277 / [MJy/sr] Interstellar Medium Estimate +CIB_EST = 0. / [MJy/sr] Cosmic Infrared Background Estimate +SKYDRKZB= 0.044533 / [MJy/sr] Zodiacal Background Est of subracted s +SKYDKMED= 0.039081 / [MJy/sr] Median of Subtracted Skydark +SKDKFDLY= 11.554 / [sec] Average Frame Delay Time of Skydark +SKDKIDLY= 11.554 / [sec] Average Immediate Delay Time of Skydark + + / GENERAL MAPPING KEYWORDS + +DITHPOS = 2 / Current dither position + + / IRAC MAPPING KEYWORDS + +READMODE= 'FULL ' / Readout mode +DITHSCAL= 'medium ' / Dither scale (small, medium, large) + + / INSTRUMENT TELEMETRY DATA + +ASHTCON = 2 / Shutter condition (1:closed, 2: open) +AWEASIDE= 0 / WEA side in use (0:B, 1:A) +ACTXSTAT= 0 / Cmded transcal status +ATXSTAT = 0 / transcal status +ACFLSTAT= 0 / Cmded floodcal status +AFLSTAT = 0 / floodcal status +AVRSTUCC= -3.5 / [Volts] Cmded VRSTUC Bias +AVRSTBEG= -3.5114367 / [Volts] VRSTUC Bias at start integration +AVDETC = -2.75 / [Volts] Cmded VDET Bias +AVDETBEG= -2.7585216 / [Volts] VDET Bias at start of integration +AVGG1C = -3.65 / [Volts] Cmded VGG1 Bias +AVGG1BEG= -3.2078801 / [Volts] VGG1 Bias at start of integration +AVDDUCC = -3 / [Volts] Cmded VDDUC Bias +AVDDUBEG= -3 / [Volts] VDDUC Bias at start integration +AVGGCLC = 1 / [Volts] Cmnded VGGCL clock rail voltage +AVGGCBEG= 1 / [Volts] VGGCL clock rail voltage +AHTRIBEG= 204.49722 / [uAmps] Heater current at start of integ +AHTRVBEG= 2.3936886 / [Volts] Heater Voltage at start integ. +AFPAT2B = 15.022535 / [Deg_K] FPA Temp sensor #2 at start integ. +AFPAT2BT= 107644.61 / [Sec] FPA Temp sensor #2 time tag +AFPAT2E = 15.022535 / [Deg_K] FPA temp sensor #2, end integ. +AFPAT2ET= 107644.61 / [Sec] FPA temp sensor #2 time tag +ACTENDT = 20.376773 / [Deg_C] C&T board thermistor +AFPECTE = 18.3055 / [Deg_C] FPE control board thermistor +AFPEATE = 21.814696 / [Deg_C] FPE analog board thermistor +ASHTEMPE= 21.504564 / [Deg_C] Shutter board thermistor +ATCTEMPE= 22.662835 / [Deg_C] Temp. controller board thermistor +ACETEMPE= 20.285331 / [Deg_C] Calib. electronics board thermistor +APDTEMPE= 20.590139 / [Deg_C] PDU board thermistor +ACATMP1E= 1.3181374 / [Deg_K] CA Temp, end integration for temp1 +ACATMP2E= 1.3011689 / [Deg_K] CA Temp, end integration for temp2 +ACATMP3E= 1.3331941 / [Deg_K] CA Temp, end integration for temp3 +ACATMP4E= 1.3299368 / [Deg_K] CA Temp, end integration for temp4 +ACATMP5E= 1.3280274 / [Deg_K] CA Temp, end integration for temp5 +ACATMP6E= 1.32662 / [Deg_K] CA Temp, end integration for temp6 +ACATMP7E= 1.3253879 / [Deg_K] CA Temp, end integration for temp7 +ACATMP8E= 1.3185339 / [Deg_K] CA Temp, end integration for temp8 + + / DATA FLOW KEYWORDS + +ORIGIN0 = 'JPL_FOS ' / Site where RAW FITS file was written +CREATOR0= 'J5.3 ' / SW system that created RAW FITS +DATE = '2007-07-10T04:40:23' / [YYYY-MM-DDThh:mm:ss UTC] file creation date +AORKEY = 18279424 / AOR or EIR key. Astrnmy Obs Req/Instr Eng Req +DS_IDENT= 'ads/sa.spitzer#0018279424' / Data Set Identification for ADS/journals +EXPID = 14 / Exposure ID (0-9999) +DCENUM = 0 / DCE number (0-9999) +TLMGRPS = 1 / expected number of groups +FILE_VER= 1 / Version of the raw file made by SIS +RAWFILE = 'IRAC.1.0018279424.0014.0000.01.mipl.fits' / Raw data file name +CPT_VER = '3.1.11 ' / Channel Param Table FOS versioN +CTD_VER = '3.0.94S ' / Cmded telemetry data version +EXPDFLAG= F / (T/F) expedited DCE +MISS_LCT= 0 / Total Missed Line Cnt in this FITS +MANCPKT = F / T if this FITS is Missing Ancillary Data +MISSDATA= F / T if this FITS is Missing Image Data +CHECKSUM= 0 / MIPL computed checksum +PAONUM = 2463 / PAO Number +CAMPAIGN= 'IRAC009800' / Campaign +DCEID = 80565322 / Data-Collection-Event ID +DCEINSID= 16981930 / DCE Instance ID +DPID = 178643698 / Data Product Instance ID +PIPENUM = 107 / Pipeline Script Number +SOS_VER = 2 / Data-Product Version +PLVID = 4 / Pipeline Version ID +CALID = 6 / CalTrans Version ID + +SDRKEPID= 3230182 / Sky Dark ensemble product ID + +PMSKFBID= 995 / Pixel mask ID +LDRKFBID= 836 / Fall-back lab dark ID +LINCFBID= 357 / Fall-back Linearity correction ID +FLATFBID= 1011 / Fall-back flat ID +FLXCFBID= 1025 / Flux conversion ID +MBLTFBID= 696 / Muxbleed Lookup Table ID +MBCFFBID= 704 / Muxbleed Coefficients ID +LBDRKFLE= 'FUL_12s_12sf8d1r1_ch1_v1.2.0_dark.txt' / Labdark File Used +DDCORR1 = 1.488018 / Darkdrift Correction for Readout Channel 1 +DDCORR2 = -5.919937 / Darkdrift Correction for Readout Channel 2 +DDCORR3 = 2.243147 / Darkdrift Correction for Readout Channel 3 +DDCORR4 = 2.188772 / Darkdrift Correction for Readout Channel 4 +DDBKGND = 68.5909 / arkdrift 'Background' Term + + + + / PROCESSING HISTORY + +HISTORY job.c ver: 1.50 +HISTORY TRANHEAD v. 12.8, ran Mon Jul 9 21:40:04 2007 +HISTORY CALTRANS v. 4.0, ran Mon Jul 9 21:40:08 2007 +HISTORY INSBPOSDOM v. 1.1, ran Mon Jul 9 21:40:08 2007 +HISTORY cvti2r4 v. 1.31 A61025, generated 7/09/07 at 21:40:09 +HISTORY hdrupd8 v. 1.5 A50126, updated 7/09/07 at 21:40:11 +HISTORY FFC v. 1.0, ran Mon Jul 9 21:40:12 2007 +HISTORY MUXBLEEDCORR v. 1.600, ran Mon Jul 9 21:40:13 2007 +HISTORY FOWLINEARIZE v. 4.900000, ran Mon Jul 9 21:40:13 2007 +HISTORY hdrupd8 v. 1.5 A50126, updated 7/09/07 at 21:40:14 +HISTORY BGMODEL v. 1.0, ran Mon Jul 9 21:40:14 2007 +HISTORY SLREMOVE v. 1.0, ran Mon Jul 9 21:40:14 2007 +HISTORY hdrupd8 v. 1.5 A50126, updated 7/09/07 at 21:40:15 +HISTORY hdrupd8 v. 1.5 A50126, updated 7/09/07 at 21:40:16 +HISTORY hdrupd8 v. 1.5 A50126, updated 7/09/07 at 21:40:16 +HISTORY hdrupd8 v. 1.5 A50126, updated 7/09/07 at 21:40:17 +HISTORY hdrupd8 v. 1.5 A50126, updated 7/09/07 at 21:40:18 +HISTORY DARKSUBNG v. 1.000, ran Mon Jul 9 21:40:18 2007 +HISTORY DARKDRIFT v. 4.0, ran Mon Jul 9 21:40:19 2007 +HISTORY FLATAP v. 1.500 Mon Jul 9 21:40:20 2007 +HISTORY DNTOFLUX v. 4.1, ran Mon Jul 9 21:40:23 2007 +HISTORY CALTRANS v. 4.0, ran Mon Jul 9 21:47:55 2007 +HISTORY PTNTRAN v. 1.4, ran Mon Jul 9 21:47:56 2007 +HISTORY FPGen v. 1.25, ran Mon Jul 9 21:47:58 2007 +HISTORY PTGADJUST v. 1.0, ran Mon Jul 9 21:51:34 2007 +END diff --git a/ast_tester/sparse.ast b/ast_tester/sparse.ast new file mode 100644 index 0000000..c28cb4d --- /dev/null +++ b/ast_tester/sparse.ast @@ -0,0 +1,392 @@ + Begin FrameSet # Set of inter-related coordinate systems +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "SKY-DSBSPECTRUM" # Coordinate system domain +# Epoch = 2007.10664386351 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Radio velocity (USB)" # Label for axis 3 +# System = "Compound" # Coordinate system type +# Uni1 = "hh:mm:ss.sss" # Units for axis 1 +# Uni2 = "ddd:mm:ss.ss" # Units for axis 2 +# Uni3 = "km/s" # Units for axis 3 +# Dig1 = 9 # Individual precision for axis 1 +# Dig2 = 9 # Individual precision for axis 2 +# Digits = 7 # Default formatting precision +# Fmt1 = "hms.3" # Format specifier for axis 1 +# Fmt2 = "dms.2" # Format specifier for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction + IsA Frame # Coordinate system description + Nframe = 5 # Number of Frames in FrameSet +# Base = 1 # Index of base Frame + Currnt = 5 # Index of current Frame + Nnode = 6 # Number of nodes in FrameSet + Nod1 = 3 # Frame 1 is associated with node 3 + Nod2 = 4 # Frame 2 is associated with node 4 + Nod3 = 5 # Frame 3 is associated with node 5 + Nod4 = 6 # Frame 4 is associated with node 6 + Nod5 = 2 # Frame 5 is associated with node 2 + Lnk2 = 1 # Node 2 is derived from node 1 + Lnk3 = 1 # Node 3 is derived from node 1 + Lnk4 = 1 # Node 4 is derived from node 1 + Lnk5 = 1 # Node 5 is derived from node 1 + Lnk6 = 1 # Node 6 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame # Coordinate system description + Title = "Data grid indices; first pixel at (1,1,1)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Data grid index 1" # Label for axis 1 +# Lbl2 = "Data grid index 2" # Label for axis 2 +# Lbl3 = "Data grid index 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Data grid index 1" # Axis Label + Symbol = "g1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Data grid index 2" # Axis Label + Symbol = "g2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Data grid index 3" # Axis Label + Symbol = "g3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm2 = # Frame number 2 + Begin Frame # Coordinate system description + Title = "Pixel coordinates; first pixel at (0.5,0.5,-1024.5)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "PIXEL" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Lbl3 = "Pixel coordinate 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 1" # Axis Label + Symbol = "p1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 2" # Axis Label + Symbol = "p2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Pixel coordinate 3" # Axis Label + Symbol = "p3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm3 = # Frame number 3 + Begin Frame # Coordinate system description + Title = "Axis coordinates; first pixel at (0.5,0.5,-1024.5)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "AXIS" # Coordinate system domain +# Lbl1 = "Axis 1" # Label for axis 1 +# Lbl2 = "Axis 2" # Label for axis 2 +# Lbl3 = "Axis 3" # Label for axis 3 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 +# Uni3 = "pixel" # Units for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Axis 1" # Axis Label + Symbol = "a1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Axis 2" # Axis Label + Symbol = "a2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Axis 3" # Axis Label + Symbol = "a3" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm4 = # Frame number 4 + Begin Frame # Coordinate system description + Title = "Normalised pixel coordinates; first pixel at (0.03846154,0.5,0.0002441406)" # Title of coordinate system + Naxes = 3 # Number of coordinate axes + Domain = "FRACTION" # Coordinate system domain +# Lbl1 = "Normalised pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Normalised pixel coordinate 2" # Label for axis 2 +# Lbl3 = "Normalised pixel coordinate 3" # Label for axis 3 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Label = "Normalised pixel coordinate 1" # Axis Label + Symbol = "f1" # Axis symbol + Unit = "" # Axis units + Format = "%5.4f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis # Coordinate axis + Label = "Normalised pixel coordinate 2" # Axis Label + Symbol = "f2" # Axis symbol + Unit = "" # Axis units + Format = "%5.4f" # Format specifier + End Axis + Ax3 = # Axis number 3 + Begin Axis # Coordinate axis + Label = "Normalised pixel coordinate 3" # Axis Label + Symbol = "f3" # Axis symbol + Unit = "" # Axis units + Format = "%5.4f" # Format specifier + End Axis + End Frame + Frm5 = # Frame number 5 + Begin CmpFrame # Compound coordinate system description +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "SKY-DSBSPECTRUM" # Coordinate system domain +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Lbl3 = "Radio velocity (USB)" # Label for axis 3 +# Uni1 = "hh:mm:ss.sss" # Units for axis 1 +# Uni2 = "ddd:mm:ss.ss" # Units for axis 2 +# Uni3 = "km/s" # Units for axis 3 +# Dig1 = 9 # Individual precision for axis 1 +# Dig2 = 9 # Individual precision for axis 2 +# Digits = 7 # Default formatting precision +# Fmt1 = "hms.3" # Format specifier for axis 1 +# Fmt2 = "dms.2" # Format specifier for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction + IsA Frame # Coordinate system description + FrameA = # First component Frame + Begin SkyFrame # Description of celestial coordinate system + Naxes = 2 # Number of coordinate axes + Epoch = 2007.10664386351 # Julian epoch of observation + System = "FK5" # Coordinate system type + ObsLat = 0.346026069000145 # Observers geodetic latitude (rads) + ObsLon = -2.71363307300091 # Observers geodetic longitude (rads) + Dut1 = -0.00321845632046122 # UT1-UTC in seconds + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + Digits = 9 # Default formatting precision + IsA Axis # Coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + Digits = 9 # Default formatting precision + IsA Axis # Coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + SRef1 = 1.2343627630284 # Ref. pos. RA 4:42:53.706 + SRef2 = 0.63032350835257 # Ref. pos. Dec 36:06:53.56 + End SkyFrame + FrameB = # Second component Frame + Begin DSBSpecFrame # Dual sideband spectral axis + Naxes = 1 # Number of coordinate axes + Epoch = 2007.10664386353 # Julian epoch of observation + System = "VRAD" # Coordinate system type + ObsLat = 0.346026069000144 # Observers geodetic latitude (rads) + ObsLon = -2.71363307300091 # Observers geodetic longitude (rads) + Dut1 = -0.00321845632046122 # UT1-UTC in seconds + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + End Axis + IsA Frame # Coordinate system description + SoR = "LSRK" # Standard of rest + RefRA = 1.23436224892048 # Reference RA (rads, FK5 J2000) + RefDec = 0.630323957597852 # Reference Dec (rads, FK5 J2000) + RstFrq = 345795989900 # Rest frequency (Hz) + SrcVel = -21699.2154029027 # Source velocity (m/s) + SrcVRF = "LSRK" # Source velocity rest frame + UFreq = "GHz" # Preferred units for frequency + IsA SpecFrame # Description of spectral coordinate system + DSBCen = 345782054267.698 # Central frequency (Hz topo) + IF = -4999999979.60052 # Intermediate frequency (Hz) + SideBn = "USB" # Represents upper sideband + AlSdBn = 1 # Align sidebands? + End DSBSpecFrame + End CmpFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin PermMap # Coordinate permutation + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Out1 = 1 # Output coordinate 1 = input coordinate 1 + Out2 = 1 # Output coordinate 2 = input coordinate 1 + In1 = 1 # Input coordinate 1 = output coordinate 1 + In2 = 0 # Input coordinate 2 is "bad" + End PermMap + MapB = # Second component Mapping + Begin WinMap # Map one window on to another + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 2.86538852378726e-05 # Shift for axis 1 + End WinMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 3 # Number of input coordinates + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + Nlut = 13 # Number of lookup table elements + Start = 1 # Input value at first element +# Incr = 1 # Input value increment between elements + LutInt = 1 # Interpolation method + L1 = 1.23418282732218 # Lookup table elements... + L2 = 1.2343639301616 + L3 = 1.23436339112322 + L4 = 1.2343628519694 + L5 = 1.23436231270009 + L6 = 1.23454235656739 + L7 = 1.23454287673211 + L8 = 1.2345433967854 + L9 = 1.23454391672729 + L10 = 1.23472390339668 + L11 = 1.23472340255133 + L12 = 1.23472290159864 + L13 = 1.23472240053857 + End LutMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + Nlut = 13 # Number of lookup table elements + Start = 1 # Input value at first element +# Incr = 1 # Input value increment between elements + LutInt = 1 # Interpolation method + L1 = 0.630323242601191 # Lookup table elements... + L2 = 0.630032824768848 + L3 = 0.630178255337191 + L4 = 0.630323685912113 + L5 = 0.630469116487462 + L6 = 0.630469544411238 + L7 = 0.630324113791166 + L8 = 0.630178683171532 + L9 = 0.630033252558487 + L10 = 0.630033664925644 + L11 = 0.630179095578678 + L12 = 0.630324526238311 + L13 = 0.63046995689839 + End LutMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 346321451689.665 # Shift for axis 1 + Scl1 = -488226.2403564 # Scale factor for axis 1 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin SpecMap # Conversion between spectral coordinate systems + Nin = 1 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Nspec = 2 # Number of conversion steps + Spec1 = "FRTOVL" # Convert frequency to rel. velocity + Spec1a = 345795989900 # Rest frequency (Hz) + Spec2 = "VLTOVR" # Convert relativistic to radio velocity + End SpecMap + MapB = # Second component Mapping + Begin ZoomMap # Zoom about the origin + Nin = 1 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Zoom = 0.001 # Zoom factor + End ZoomMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + Map3 = # Mapping between nodes 1 and 3 + Begin UnitMap # Unit (null) Mapping + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + End UnitMap + Map4 = # Mapping between nodes 1 and 4 + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -0.5 # Shift for axis 1 + Sft2 = -0.5 # Shift for axis 2 + Sft3 = -1025.5 # Shift for axis 3 + End WinMap + Map5 = # Mapping between nodes 1 and 5 + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -0.5 # Shift for axis 1 + Sft2 = -0.5 # Shift for axis 2 + Sft3 = -1025.5 # Shift for axis 3 + End WinMap + Map6 = # Mapping between nodes 1 and 6 + Begin WinMap # Map one window on to another + Nin = 3 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + IsA Mapping # Mapping between coordinate systems + Sft1 = -0.0384615384615385 # Shift for axis 1 + Scl1 = 0.0769230769230769 # Scale factor for axis 1 + Sft2 = -0.5 # Shift for axis 2 + Sft3 = -0.000244140625 # Shift for axis 3 + Scl3 = 0.00048828125 # Scale factor for axis 3 + End WinMap + End FrameSet diff --git a/ast_tester/specflux.ast b/ast_tester/specflux.ast new file mode 100644 index 0000000..0da5fea --- /dev/null +++ b/ast_tester/specflux.ast @@ -0,0 +1,29 @@ + Begin CmpFrame # Compound coordinate system description +# Title = "2-d compound coordinate system" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SPECTRUM-" # Coordinate system domain +# Lbl1 = "Wavelength" # Label for axis 1 +# Lbl2 = "Axis 2" # Label for axis 2 +# Uni1 = "um" # Units for axis 1 +# Uni2 = "10%^50+%s50+-26%+W/m%^50+%s50+2%+/Angstrom" # Units for axis 2 + IsA Frame # Coordinate system description + FrameA = # First component Frame + Begin SpecFrame # Description of spectral coordinate system + Naxes = 1 # Number of coordinate axes + System = "WAVE" # Coordinate system type + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Unit = "um" # Axis units + End Axis + IsA Frame # Coordinate system description + UWave = "um" # Preferred units for wavelength + End SpecFrame + FrameB = # Second component Frame + Begin Frame # Coordinate system description + Naxes = 1 # Number of coordinate axes + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Unit = "10%^50+%s50+-26%+W/m%^50+%s50+2%+/Angstrom" # Axis units + End Axis + End Frame + End CmpFrame diff --git a/ast_tester/specflux.attr b/ast_tester/specflux.attr new file mode 100644 index 0000000..e00d209 --- /dev/null +++ b/ast_tester/specflux.attr @@ -0,0 +1 @@ +system=freq,logplot=1,loggap(1)=100 diff --git a/ast_tester/specflux.box b/ast_tester/specflux.box new file mode 100644 index 0000000..7c6f180 --- /dev/null +++ b/ast_tester/specflux.box @@ -0,0 +1,2 @@ +1 3 1.0E-8 9 + diff --git a/ast_tester/specflux.head b/ast_tester/specflux.head new file mode 100644 index 0000000..ac4a035 --- /dev/null +++ b/ast_tester/specflux.head @@ -0,0 +1,28 @@ + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST Beginning of AST data for CmpFrame object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'CmpFrame' / Compound coordinate system description +FRAMEA_A= ' ' / First component Frame +BEGAST_B= 'SpecFrame' / Description of spectral coordinate system +NAXES_A = 1 / Number of coordinate axes +SYSTEM_A= 'WAVE ' / Coordinate system type +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +UNIT_A = 'um ' / Axis units +ENDAST_A= 'Axis ' / End of object definition +ISA_A = 'Frame ' / Coordinate system description +UWAVE_A = 'um ' / Preferred units for wavelength +ENDAST_B= 'SpecFrame' / End of object definition +FRAMEB_A= ' ' / Second component Frame +BEGAST_D= 'Frame ' / Coordinate system description +NAXES_B = 1 / Number of coordinate axes +AX1_B = ' ' / Axis number 1 +BEGAST_E= 'Axis ' / Coordinate axis +UNIT_B = '10%^50+%s50+-26%+W/m%^50+%s50+2%+/Angstrom'/ Axis units +ENDAST_C= 'Axis ' / End of object definition +ENDAST_D= 'Frame ' / End of object definition +ENDAST_E= 'CmpFrame' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for CmpFrame object AST +COMMENT AST ---------------------------------------------------------------- AST diff --git a/ast_tester/specflux.ps b/ast_tester/specflux.ps new file mode 100644 index 0000000..e7b7fff --- /dev/null +++ b/ast_tester/specflux.ps @@ -0,0 +1,769 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Thu Feb 19 19:50:49 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 19/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 202.0 78.8 202.0 76.0 2 pls + 208.0 78.8 208.0 76.0 2 pls + 224.9 81.9 224.9 76.0 2 pls + 212.6 78.8 212.6 76.0 2 pls + 216.4 78.8 216.4 76.0 2 pls + 219.7 78.8 219.7 76.0 2 pls + 222.5 78.8 222.5 76.0 2 pls + 277.7 78.8 277.7 76.0 2 pls + 291.5 78.8 291.5 76.0 2 pls + 299.8 78.8 299.8 76.0 2 pls + 305.8 78.8 305.8 76.0 2 pls + 322.7 81.9 322.7 76.0 2 pls + 310.4 78.8 310.4 76.0 2 pls + 314.2 78.8 314.2 76.0 2 pls + 317.4 78.8 317.4 76.0 2 pls + 320.2 78.8 320.2 76.0 2 pls + 375.4 78.8 375.4 76.0 2 pls + 389.3 78.8 389.3 76.0 2 pls + 397.5 78.8 397.5 76.0 2 pls + 403.5 78.8 403.5 76.0 2 pls + 420.5 81.9 420.5 76.0 2 pls + 408.1 78.8 408.1 76.0 2 pls + 411.9 78.8 411.9 76.0 2 pls + 415.1 78.8 415.1 76.0 2 pls + 417.9 78.8 417.9 76.0 2 pls + 473.1 78.8 473.1 76.0 2 pls + 487.0 78.8 487.0 76.0 2 pls + 495.3 78.8 495.3 76.0 2 pls + 501.2 78.8 501.2 76.0 2 pls + 518.2 81.9 518.2 76.0 2 pls + 505.8 78.8 505.8 76.0 2 pls + 509.6 78.8 509.6 76.0 2 pls + 512.9 78.8 512.9 76.0 2 pls + 515.7 78.8 515.7 76.0 2 pls + 571.0 78.8 571.0 76.0 2 pls + 571.6 78.8 571.6 76.0 2 pls + 585.1 78.8 585.1 76.0 2 pls + 585.0 78.8 585.0 76.0 2 pls + 202.2 99.0 199.5 99.0 2 pls + 202.2 120.6 199.5 120.6 2 pls + 205.3 178.4 199.5 178.4 2 pls + 202.2 140.9 199.5 140.9 2 pls + 202.2 160.1 199.5 160.1 2 pls + 202.2 195.7 199.5 195.7 2 pls + 202.2 212.3 199.5 212.3 2 pls + 205.3 257.8 199.5 257.8 2 pls + 202.2 228.1 199.5 228.1 2 pls + 202.2 243.3 199.5 243.3 2 pls + 202.2 271.7 199.5 271.7 2 pls + 202.2 285.2 199.5 285.2 2 pls + 205.3 322.7 199.5 322.7 2 pls + 202.2 298.1 199.5 298.1 2 pls + 202.2 310.6 199.5 310.6 2 pls + 202.2 334.3 199.5 334.3 2 pls + 202.2 345.6 199.5 345.6 2 pls + 205.3 377.5 199.5 377.5 2 pls + 202.2 356.6 199.5 356.6 2 pls + 202.2 367.2 199.5 367.2 2 pls + 202.2 387.6 199.5 387.6 2 pls + 202.2 397.3 199.5 397.3 2 pls + 205.3 425.0 199.5 425.0 2 pls + 202.2 406.8 199.5 406.8 2 pls + 202.2 416.0 199.5 416.0 2 pls + 202.2 433.8 199.5 433.8 2 pls + 202.2 442.4 199.5 442.4 2 pls + 205.3 467.0 199.5 467.0 2 pls + 202.2 450.8 199.5 450.8 2 pls + 202.2 459.0 199.5 459.0 2 pls + 202.0 464.2 202.0 467.0 2 pls + 208.0 464.2 208.0 467.0 2 pls + 224.9 461.1 224.9 467.0 2 pls + 212.6 464.2 212.6 467.0 2 pls + 216.4 464.2 216.4 467.0 2 pls + 219.7 464.2 219.7 467.0 2 pls + 222.5 464.2 222.5 467.0 2 pls + 277.7 464.2 277.7 467.0 2 pls + 291.5 464.2 291.5 467.0 2 pls + 299.8 464.2 299.8 467.0 2 pls + 305.8 464.2 305.8 467.0 2 pls + 322.7 461.1 322.7 467.0 2 pls + 310.4 464.2 310.4 467.0 2 pls + 314.2 464.2 314.2 467.0 2 pls + 317.4 464.2 317.4 467.0 2 pls + 320.2 464.2 320.2 467.0 2 pls + 375.4 464.2 375.4 467.0 2 pls + 389.3 464.2 389.3 467.0 2 pls + 397.5 464.2 397.5 467.0 2 pls + 403.5 464.2 403.5 467.0 2 pls + 420.5 461.1 420.5 467.0 2 pls + 408.1 464.2 408.1 467.0 2 pls + 411.9 464.2 411.9 467.0 2 pls + 415.1 464.2 415.1 467.0 2 pls + 417.9 464.2 417.9 467.0 2 pls + 473.1 464.2 473.1 467.0 2 pls + 487.0 464.2 487.0 467.0 2 pls + 495.3 464.2 495.3 467.0 2 pls + 501.2 464.2 501.2 467.0 2 pls + 518.2 461.1 518.2 467.0 2 pls + 505.8 464.2 505.8 467.0 2 pls + 509.6 464.2 509.6 467.0 2 pls + 512.9 464.2 512.9 467.0 2 pls + 515.7 464.2 515.7 467.0 2 pls + 571.0 464.2 571.0 467.0 2 pls + 571.6 464.2 571.6 467.0 2 pls + 585.1 464.2 585.1 467.0 2 pls + 585.0 464.2 585.0 467.0 2 pls + 587.7 99.0 590.4 99.0 2 pls + 587.7 120.6 590.4 120.6 2 pls + 584.5 178.4 590.4 178.4 2 pls + 587.7 140.9 590.4 140.9 2 pls + 587.7 160.1 590.4 160.1 2 pls + 587.7 195.7 590.4 195.7 2 pls + 587.7 212.3 590.4 212.3 2 pls + 584.5 257.8 590.4 257.8 2 pls + 587.7 228.1 590.4 228.1 2 pls + 587.7 243.3 590.4 243.3 2 pls + 587.7 271.7 590.4 271.7 2 pls + 587.7 285.2 590.4 285.2 2 pls + 584.5 322.7 590.4 322.7 2 pls + 587.7 298.1 590.4 298.1 2 pls + 587.7 310.6 590.4 310.6 2 pls + 587.7 334.3 590.4 334.3 2 pls + 587.7 345.6 590.4 345.6 2 pls + 584.5 377.5 590.4 377.5 2 pls + 587.7 356.6 590.4 356.6 2 pls + 587.7 367.2 590.4 367.2 2 pls + 587.7 387.6 590.4 387.6 2 pls + 587.7 397.3 590.4 397.3 2 pls + 584.5 425.0 590.4 425.0 2 pls + 587.7 406.8 590.4 406.8 2 pls + 587.7 416.0 590.4 416.0 2 pls + 587.7 433.8 590.4 433.8 2 pls + 587.7 442.4 590.4 442.4 2 pls + 584.5 467.0 590.4 467.0 2 pls + 587.7 450.8 590.4 450.8 2 pls + 587.7 459.0 590.4 459.0 2 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 283.2 255.3 227.4 199.5 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 213.43 56.34 moveto[ 213.4 213.4 230.6 230.6 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 217.6 58.0 217.6 66.8 216.4 65.5 215.5 65.1 4 pls + s[ 225.1 223.9 223.0 222.6 222.6 223.0 223.9 225.1 226.0 227.2 228.0 228.5 228.5 228.0 227.2 226.0 225.1 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 231.20 63.63 moveto[ 231.2 231.2 237.9 237.9 ][ 63.6 72.1 72.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 236.2 235.9 235.0 234.4 233.5 233.0 232.7 232.7 233.0 233.5 234.4 234.7 235.6 236.2 236.5 236.5 236.2 235.6 + 234.7 234.4 233.5 233.0 232.7 ][ 70.1 70.6 70.9 70.9 70.6 69.8 68.3 66.8 65.7 65.1 64.8 64.8 65.1 + 65.7 66.6 66.8 67.7 68.3 68.6 68.6 68.3 67.7 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 311.16 56.34 moveto[ 311.2 311.2 328.3 328.3 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 315.3 58.0 315.3 66.8 314.1 65.5 313.3 65.1 4 pls + s[ 322.9 321.6 320.8 320.4 320.4 320.8 321.6 322.9 323.7 324.9 325.8 326.2 326.2 325.8 324.9 323.7 322.9 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 328.64 63.63 moveto[ 328.6 328.6 335.7 335.7 ][ 63.6 72.1 72.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 331.6 330.7 330.4 330.4 330.7 331.3 332.4 333.3 333.9 334.2 334.2 333.9 333.6 332.7 331.6 330.7 330.4 330.1 + 330.1 330.4 331.0 331.9 333.0 333.6 333.9 333.9 333.6 332.7 331.6 ][ 70.9 70.6 70.1 69.5 68.9 68.6 68.3 + 68.0 67.4 66.8 66.0 65.4 65.1 64.8 64.8 65.1 65.4 66.0 66.8 67.4 68.0 68.3 68.6 68.9 69.5 + 70.1 70.6 70.9 70.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 405.98 56.34 moveto[ 406.0 406.0 423.1 423.1 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 410.2 58.0 410.2 66.8 408.9 65.5 408.1 65.1 4 pls + s[ 417.7 416.4 415.6 415.2 415.2 415.6 416.4 417.7 418.5 419.8 420.6 421.0 421.0 420.6 419.8 418.5 417.7 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 424.33 63.63 moveto[ 424.3 424.3 436.3 436.3 ][ 63.6 72.1 72.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 427.3 64.8 427.3 70.9 426.4 70.1 425.8 69.8 4 pls + s[ 432.5 431.6 431.1 430.8 430.8 431.1 431.6 432.5 433.1 434.0 434.6 434.9 434.9 434.6 434.0 433.1 432.5 ][ 70.9 + 70.6 69.8 68.3 67.4 66.0 65.1 64.8 64.8 65.1 66.0 67.4 68.3 69.8 70.6 70.9 70.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 503.71 56.34 moveto[ 503.7 503.7 520.8 520.8 ][ 56.3 68.5 68.5 56.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 507.9 58.0 507.9 66.8 506.6 65.5 505.8 65.1 4 pls + s[ 515.4 514.2 513.3 512.9 512.9 513.3 514.2 515.4 516.2 517.5 518.3 518.7 518.7 518.3 517.5 516.2 515.4 ][ 66.8 + 66.4 65.1 63.0 61.8 59.7 58.4 58.0 58.0 58.4 59.7 61.8 63.0 65.1 66.4 66.8 66.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 522.07 63.63 moveto[ 522.1 522.1 534.1 534.1 ][ 63.6 72.1 72.1 63.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 525.0 64.8 525.0 70.9 524.1 70.1 523.5 69.8 4 pls + s[ 528.8 528.8 529.1 529.4 530.0 531.1 531.7 532.0 532.3 532.3 532.0 531.4 528.5 532.6 ][ 69.5 69.8 70.4 70.6 + 70.9 70.9 70.6 70.4 69.8 69.2 68.6 67.7 64.8 64.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.35 69.13 moveto[ 186.4 186.4 196.4 196.4 ][ 69.1 81.2 81.2 69.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 189.3 193.9 191.4 192.6 193.5 193.9 194.3 194.3 193.9 193.0 191.8 190.5 189.3 188.9 188.4 ][ 79.6 79.6 76.2 + 76.2 75.8 75.4 74.1 73.3 72.0 71.2 70.8 70.8 71.2 71.6 72.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.35 171.50 moveto[ 186.4 186.4 196.8 196.8 ][ 171.5 183.6 183.6 171.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 194.7 176.1 188.4 176.1 192.6 181.9 3 pls + 192.6 173.2 192.6 181.9 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.35 250.90 moveto[ 186.4 186.4 196.4 196.4 ][ 250.9 263.0 263.0 250.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 193.5 189.3 188.9 189.3 190.5 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.5 189.3 188.9 188.4 ][ 261.3 + 261.3 257.6 258.0 258.4 258.4 258.0 257.2 255.9 255.1 253.8 253.0 252.6 252.6 253.0 253.4 254.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.77 315.78 moveto[ 186.8 186.8 196.4 196.4 ][ 315.8 327.9 327.9 315.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 193.9 193.5 192.2 191.4 190.1 189.3 188.9 188.9 189.3 190.1 191.4 191.8 193.0 193.9 194.3 194.3 193.9 193.0 + 191.8 191.4 190.1 189.3 188.9 ][ 325.0 325.8 326.2 326.2 325.8 324.6 322.5 320.4 318.7 317.9 317.5 317.5 317.9 + 318.7 320.0 320.4 321.6 322.5 322.9 322.9 322.5 321.6 320.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.35 370.64 moveto[ 186.4 186.4 196.4 196.4 ][ 370.6 382.8 382.8 370.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 190.1 372.3 194.3 381.1 2 pls + 194.3 381.1 188.4 381.1 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.35 418.15 moveto[ 186.4 186.4 196.4 196.4 ][ 418.2 430.3 430.3 418.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 190.5 189.3 188.9 188.9 189.3 190.1 191.8 193.0 193.9 194.3 194.3 193.9 193.5 192.2 190.5 189.3 188.9 188.4 + 188.4 188.9 189.7 190.9 192.6 193.5 193.9 193.9 193.5 192.2 190.5 ][ 428.6 428.2 427.3 426.5 425.7 425.3 424.8 + 424.4 423.6 422.7 421.5 420.7 420.2 419.8 419.8 420.2 420.7 421.5 422.7 423.6 424.4 424.8 425.3 425.7 426.5 + 427.3 428.2 428.6 428.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 186.35 460.07 moveto[ 186.4 186.4 196.0 196.0 ][ 460.1 472.2 472.2 460.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 193.9 193.5 192.6 191.4 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.4 192.6 193.5 193.9 193.9 193.5 + 192.6 191.4 190.5 189.3 188.9 ][ 467.6 466.3 465.5 465.1 465.1 465.5 466.3 467.6 468.0 469.3 470.1 470.5 470.5 + 470.1 469.3 467.6 465.5 463.4 462.2 461.7 461.7 462.2 463.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 361.51 37.40 moveto[ 361.5 361.5 429.2 429.2 ][ 37.4 52.4 52.4 37.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.6 42.0 363.6 50.8 2 pls + 369.0 50.8 363.6 50.8 2 pls + 366.9 46.6 363.6 46.6 2 pls + 371.1 42.0 371.1 47.8 2 pls + 374.5 47.8 373.2 47.8 372.4 47.4 371.5 46.6 371.1 45.3 5 pls + s[ 376.1 381.1 381.1 380.7 380.3 379.5 378.2 377.4 376.5 376.1 376.1 376.5 377.4 378.2 379.5 380.3 381.1 ][ 45.3 + 45.3 46.2 47.0 47.4 47.8 47.8 47.4 46.6 45.3 44.5 43.2 42.4 42.0 42.0 42.4 43.2 ]plong + 388.7 39.1 388.7 47.8 2 pls + s[ 388.7 387.8 387.0 385.7 384.9 384.1 383.6 383.6 384.1 384.9 385.7 387.0 387.8 388.7 ][ 46.6 47.4 47.8 47.8 + 47.4 46.6 45.3 44.5 43.2 42.4 42.0 42.0 42.4 43.2 ]plong + s[ 392.0 392.0 392.4 393.3 394.5 395.3 396.6 ][ 47.8 43.7 42.4 42.0 42.0 42.4 43.7 ]plong + 396.6 42.0 396.6 47.8 2 pls + s[ 399.5 404.5 404.5 404.1 403.7 402.9 401.6 400.8 399.9 399.5 399.5 399.9 400.8 401.6 402.9 403.7 404.5 ][ 45.3 + 45.3 46.2 47.0 47.4 47.8 47.8 47.4 46.6 45.3 44.5 43.2 42.4 42.0 42.0 42.4 43.2 ]plong + 407.5 42.0 407.5 47.8 2 pls + s[ 407.5 408.7 409.5 410.8 411.6 412.0 412.0 ][ 46.2 47.4 47.8 47.8 47.4 46.2 42.0 ]plong + s[ 420.0 419.1 418.3 417.1 416.2 415.4 415.0 415.0 415.4 416.2 417.1 418.3 419.1 420.0 ][ 46.6 47.4 47.8 47.8 + 47.4 46.6 45.3 44.5 43.2 42.4 42.0 42.0 42.4 43.2 ]plong + 424.6 42.0 422.1 47.8 2 pls + s[ 427.1 424.6 423.7 422.9 422.1 421.7 ][ 47.8 42.0 40.3 39.5 39.1 39.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 181.19 168.58 moveto[ 181.2 164.5 164.5 181.2 ][ 168.6 168.6 240.4 240.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 176.6 170.7 167.8 174.0 2 pls + 176.6 177.4 167.8 174.0 2 pls + 173.7 176.1 173.7 171.9 2 pls + 176.6 183.6 170.7 179.0 2 pls + 176.6 179.0 170.7 183.6 2 pls + 167.8 186.1 167.4 186.5 167.8 187.0 168.2 186.5 167.8 186.1 5 pls + 176.6 186.5 170.7 186.5 2 pls + s[ 172.0 171.2 170.7 170.7 171.2 172.0 172.8 173.3 173.7 174.1 174.9 175.3 176.2 176.6 176.6 176.2 175.3 ][ 194.1 + 193.6 192.4 191.1 189.9 189.5 189.9 190.7 192.8 193.6 194.1 194.1 193.6 192.4 191.1 189.9 189.5 ]plong + s[ 169.9 169.5 168.7 168.2 167.8 167.8 168.2 168.7 169.5 170.3 171.2 172.4 176.6 176.6 ][ 203.7 203.7 204.1 204.5 + 205.3 207.0 207.8 208.3 208.7 208.7 208.3 207.4 203.3 209.1 ]plong + s[ 166.2 167.0 168.2 169.9 172.0 173.7 175.8 177.4 178.7 179.5 ][ 221.6 220.8 220.0 219.1 218.7 218.7 219.1 220.0 + 220.8 221.6 ]plong + 176.6 227.5 167.8 227.5 169.1 226.2 169.5 225.4 4 pls + s[ 167.8 168.2 169.5 171.6 172.8 174.9 176.2 176.6 176.6 176.2 174.9 172.8 171.6 169.5 168.2 167.8 167.8 ][ 235.0 + 233.7 232.9 232.5 232.5 232.9 233.7 235.0 235.8 237.1 237.9 238.3 238.3 237.9 237.1 235.8 235.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 170.65 241.14 moveto[ 170.6 164.6 164.6 170.6 ][ 241.1 241.1 255.5 255.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 167.9 245.9 167.9 242.2 2 pls + s[ 166.5 166.3 165.8 165.6 165.4 165.4 165.6 165.8 166.3 166.7 167.1 167.7 169.8 169.8 ][ 247.6 247.6 247.8 248.0 + 248.4 249.3 249.7 249.9 250.1 250.1 249.9 249.5 247.4 250.3 ]plong + s[ 166.1 165.6 165.4 165.4 165.6 166.3 167.3 168.3 169.2 169.6 169.8 169.8 169.6 169.2 168.6 168.3 167.7 167.3 + 167.1 167.1 167.3 167.7 168.3 ][ 254.3 254.1 253.5 253.0 252.4 252.0 251.8 251.8 252.0 252.4 253.0 253.2 253.9 + 254.3 254.5 254.5 254.3 253.9 253.2 253.0 252.4 252.0 251.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 181.19 255.61 moveto[ 181.2 164.5 164.5 181.2 ][ 255.6 255.6 289.0 289.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 176.6 259.8 167.8 257.7 2 pls + 176.6 259.8 167.8 261.9 2 pls + 176.6 264.0 167.8 261.9 2 pls + 176.6 264.0 167.8 266.1 2 pls + 179.5 267.7 166.2 275.2 2 pls + 176.6 277.7 170.7 277.7 2 pls + s[ 172.4 171.2 170.7 170.7 171.2 172.4 176.6 ][ 277.7 279.0 279.8 281.1 281.9 282.3 282.3 ]plong + s[ 172.4 171.2 170.7 170.7 171.2 172.4 176.6 ][ 282.3 283.6 284.4 285.7 286.5 286.9 286.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 170.65 289.93 moveto[ 170.6 164.6 164.6 170.6 ][ 289.9 289.9 294.9 294.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 166.5 166.3 165.8 165.6 165.4 165.4 165.6 165.8 166.3 166.7 167.1 167.7 169.8 169.8 ][ 291.2 291.2 291.4 291.6 + 292.0 292.9 293.3 293.5 293.7 293.7 293.5 293.1 291.0 293.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 181.19 295.01 moveto[ 181.2 164.5 164.5 181.2 ][ 295.0 295.0 373.1 373.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 179.5 297.1 166.2 304.6 2 pls + 176.6 305.9 167.8 309.2 2 pls + 176.6 312.6 167.8 309.2 2 pls + 173.7 311.3 173.7 307.1 2 pls + 176.6 314.6 170.7 314.6 2 pls + s[ 172.4 171.2 170.7 170.7 171.2 172.4 176.6 ][ 314.6 315.9 316.7 318.0 318.8 319.2 319.2 ]plong + s[ 170.7 177.4 178.7 179.1 179.5 179.5 179.1 ][ 327.2 327.2 326.8 326.3 325.5 324.2 323.4 ]plong + s[ 172.0 171.2 170.7 170.7 171.2 172.0 173.3 174.1 175.3 176.2 176.6 176.6 176.2 175.3 ][ 327.2 326.3 325.5 324.2 + 323.4 322.6 322.2 322.2 322.6 323.4 324.2 325.5 326.3 327.2 ]plong + s[ 172.0 171.2 170.7 170.7 171.2 172.0 172.8 173.3 173.7 174.1 174.9 175.3 176.2 176.6 176.6 176.2 175.3 ][ 334.7 + 334.3 333.0 331.8 330.5 330.1 330.5 331.3 333.4 334.3 334.7 334.7 334.3 333.0 331.8 330.5 330.1 ]plong + 176.6 340.1 176.6 339.3 176.2 338.4 174.9 338.0 167.8 338.0 5 pls + 170.7 339.7 170.7 336.8 2 pls + 176.6 342.6 170.7 342.6 2 pls + 170.7 346.0 170.7 344.7 171.2 343.9 172.0 343.0 173.3 342.6 5 pls + s[ 170.7 171.2 172.0 173.3 174.1 175.3 176.2 176.6 176.6 176.2 175.3 174.1 173.3 172.0 171.2 170.7 170.7 ][ 349.7 + 348.9 348.1 347.6 347.6 348.1 348.9 349.7 351.0 351.8 352.6 353.1 353.1 352.6 351.8 351.0 349.7 ]plong + 176.6 356.0 170.7 356.0 2 pls + s[ 172.4 171.2 170.7 170.7 171.2 172.4 176.6 ][ 356.0 357.2 358.1 359.3 360.2 360.6 360.6 ]plong + s[ 172.4 171.2 170.7 170.7 171.2 172.4 176.6 ][ 360.6 361.8 362.7 363.9 364.8 365.2 365.2 ]plong + s[ 166.2 167.0 168.2 169.9 172.0 173.7 175.8 177.4 178.7 179.5 ][ 368.1 368.9 369.8 370.6 371.0 371.0 370.6 369.8 + 368.9 368.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 491.73 moveto[ 360.9 360.9 435.2 435.2 ][ 491.7 503.8 503.8 491.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 493.4 366.3 502.2 2 pls + 369.7 493.4 366.3 502.2 2 pls + 368.4 496.3 364.2 496.3 2 pls + 378.4 493.4 378.4 502.2 2 pls + 383.9 502.2 378.4 502.2 2 pls + 381.8 498.0 378.4 498.0 2 pls + 385.9 493.4 385.9 502.2 2 pls + 391.0 493.4 391.0 502.2 2 pls + 393.9 502.2 388.0 502.2 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 500.9 501.8 502.2 502.2 501.8 500.9 500.1 499.2 498.8 498.4 497.6 497.2 496.7 495.9 494.7 493.8 + 493.4 493.4 493.8 494.7 ]plong + 413.5 493.4 412.7 493.4 411.8 493.8 411.4 495.1 411.4 502.2 5 pls + 413.1 499.2 410.2 499.2 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 496.7 + 496.7 497.6 498.4 498.8 499.2 499.2 498.8 498.0 496.7 495.9 494.7 493.8 493.4 493.4 493.8 494.7 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 498.0 + 498.8 499.2 499.2 498.8 498.0 497.2 496.7 496.3 495.9 495.1 494.7 493.8 493.4 493.4 493.8 494.7 ]plong + 433.1 493.4 432.3 493.4 431.5 493.8 431.1 495.1 431.1 502.2 5 pls + 432.7 499.2 429.8 499.2 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/splittest1.ast b/ast_tester/splittest1.ast new file mode 100644 index 0000000..9fb1e2f --- /dev/null +++ b/ast_tester/splittest1.ast @@ -0,0 +1,185 @@ + Begin CmpMap # Compound Mapping + Nin = 4 # Number of input coordinates + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin UnitMap # Unit (null) Mapping + Nin = 4 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + End UnitMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 4 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 4 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -129 # Shift for axis 1 + Sft2 = -65 # Shift for axis 2 + Sft3 = -0.5 # Shift for axis 3 + Sft4 = -1 # Shift for axis 4 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 4 # Number of input coordinates + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap # Matrix transformation + Nin = 4 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.996194698 # Forward matrix value + M1 = 0.087155743 # Forward matrix value + M2 = 0 # Forward matrix value + M3 = 0 # Forward matrix value + M4 = -0.087155743 # Forward matrix value + M5 = 0.996194698 # Forward matrix value + M6 = 0 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 0 # Forward matrix value + M9 = 0 # Forward matrix value + M10 = 1 # Forward matrix value + M11 = 0 # Forward matrix value + M12 = 0 # Forward matrix value + M13 = 0 # Forward matrix value + M14 = 0 # Forward matrix value + M15 = 1 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 4 # Number of input coordinates + Invert = 0 # Mapping not inverted + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 136 # Shift for axis 1 + Sft2 = 76 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 2 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + Series = 0 # Component Mappings applied in parallel + MapA = # First component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap # Map one window on to another + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = 0.5 # Shift for axis 1 + End WinMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Nlut = 8 # Number of lookup table elements + Start = 1 # Input value at first element +# Incr = 1 # Input value increment between elements + LutInt = 0 # Interpolation method + L1 = 0.5 # Lookup table elements... + L2 = 1.5 + L3 = 1.5 + L4 = 2.5 + L5 = 2.5 + L6 = 3.5 + L7 = 3.5 + L8 = 4.5 + End LutMap + MapB = # Second component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Nlut = 8 # Number of lookup table elements + Start = 1 # Input value at first element +# Incr = 1 # Input value increment between elements + LutInt = 0 # Interpolation method + L1 = 0.21106114 # Lookup table elements... + L2 = 0.21076437 + L3 = 2e-06 + L4 = 2.2e-06 + L5 = 5e-07 + L6 = 6.5e-07 + L7 = 1.24e-09 + L8 = 2.48e-09 + End LutMap + End CmpMap + End CmpMap + MapB = # Second component Mapping + Begin CmpMap # Compound Mapping + Nin = 1 # Number of input coordinates + IsSimp = 1 # Mapping has been simplified + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Nlut = 8 # Number of lookup table elements + Start = 1 # Input value at first element +# Incr = 1 # Input value increment between elements + LutInt = 0 # Interpolation method + L1 = 0 # Lookup table elements... + L2 = 1 + L3 = 1 + L4 = 2 + L5 = 2 + L6 = 3 + L7 = 3 + L8 = 4 + End LutMap + MapB = # Second component Mapping + Begin LutMap # Map 1-d coordinates using a lookup table + Nin = 1 # Number of input coordinates + Invert = 0 # Mapping not inverted + Inv = 0 # Inverse transformation not defined + IsA Mapping # Mapping between coordinate systems + Nlut = 8 # Number of lookup table elements + Start = 1 # Input value at first element +# Incr = 1 # Input value increment between elements + LutInt = 0 # Interpolation method + L1 = 1997.84512 # Lookup table elements... + L2 = 1997.84631 + L3 = 1993.28451 + L4 = 1993.28456 + L5 = 2001.59234 + L6 = 2001.59239 + L7 = 2002.18265 + L8 = 2002.18301 + End LutMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap diff --git a/ast_tester/stcschan-test1-doc3-props.ast b/ast_tester/stcschan-test1-doc3-props.ast new file mode 100644 index 0000000..4ab41aa --- /dev/null +++ b/ast_tester/stcschan-test1-doc3-props.ast @@ -0,0 +1,126 @@ +Begin KeyMap # Map of key/value pairs + MapSz = 16 # Size of hash table + Key1 = "REDSHIFT_PROPS" # Item name + Typ1 = 4 # Item data type (AST Object) + Val1 = # Item value + Begin KeyMap # Map of key/value pairs + MapSz = 16 # Size of hash table + Key1 = "PIXSIZE" # Item name + Typ1 = 3 # Item data type (string) + Val1 = "0.3" # Item value + Key2 = "ID" # Item name + Typ2 = 3 # Item data type (string) + Val2 = "RedshiftInterval" # Item value + Key3 = "REFPOS" # Item name + Typ3 = 3 # Item data type (string) + Val3 = "BARYCENTER" # Item value + Key4 = "HILIMIT" # Item name + Typ4 = 3 # Item data type (string) + Val4 = "2300.0" # Item value + Key5 = "TYPE" # Item name + Typ5 = 3 # Item data type (string) + Val5 = "VELOCITY" # Item value + Key6 = "RESOLUTION" # Item name + Typ6 = 3 # Item data type (string) + Val6 = "0.7" # Item value + Key7 = "DOPPLERDEF" # Item name + Typ7 = 3 # Item data type (string) + Val7 = "OPTICAL" # Item value + Key8 = "REDSHIFT" # Item name + Typ8 = 3 # Item data type (string) + Val8 = "300.0" # Item value + Key9 = "LOLIMIT" # Item name + Typ9 = 3 # Item data type (string) + Val9 = "200.0" # Item value + End KeyMap + Key2 = "SPACE_PROPS" # Item name + Typ2 = 4 # Item data type (AST Object) + Val2 = # Item value + Begin KeyMap # Map of key/value pairs + MapSz = 16 # Size of hash table + Key1 = "SIZE" # Item name + Typ1 = 3 # Item data type (string) + Val1 = "0.000333 0.000278" # Item value + Key2 = "FRAME" # Item name + Typ2 = 3 # Item data type (string) + Val2 = "ICRS" # Item value + Key3 = "PIXSIZE" # Item name + Typ3 = 3 # Item data type (string) + Val3 = "0.000083 0.000083" # Item value + Key4 = "ID" # Item name + Typ4 = 3 # Item data type (string) + Val4 = "Circle" # Item value + Key5 = "REFPOS" # Item name + Typ5 = 3 # Item data type (string) + Val5 = "GEOCENTER" # Item value + Key6 = "CENTRE" # Item name + Typ6 = 3 # Item data type (string) + Val6 = "179.0 -11.5" # Item value + Key7 = "RESOLUTION" # Item name + Typ7 = 3 # Item data type (string) + Val7 = "0.001778" # Item value + Key8 = "POSITION" # Item name + Typ8 = 3 # Item data type (string) + Val8 = "179.0 -11.5" # Item value + Key9 = "RADIUS" # Item name + Typ9 = 3 # Item data type (string) + Val9 = "0.5" # Item value + Key10 = "ERROR" # Item name + Typ10 = 3 # Item data type (string) + Val10 = "0.000889" # Item value + End KeyMap + Key3 = "SPECTRAL_PROPS" # Item name + Typ3 = 4 # Item data type (AST Object) + Val3 = # Item value + Begin KeyMap # Map of key/value pairs + MapSz = 16 # Size of hash table + Key1 = "ID" # Item name + Typ1 = 3 # Item data type (string) + Val1 = "Spectral" # Item value + Key2 = "SPECTRAL" # Item name + Typ2 = 3 # Item data type (string) + Val2 = "1420.4" # Item value + Key3 = "REFPOS" # Item name + Typ3 = 3 # Item data type (string) + Val3 = "BARYCENTER" # Item value + Key4 = "UNIT" # Item name + Typ4 = 3 # Item data type (string) + Val4 = "MHz" # Item value + Key5 = "RESOLUTION" # Item name + Typ5 = 3 # Item data type (string) + Val5 = "10.0" # Item value + End KeyMap + Key4 = "TIME_PROPS" # Item name + Typ4 = 4 # Item data type (AST Object) + Val4 = # Item value + Begin KeyMap # Map of key/value pairs + MapSz = 16 # Size of hash table + Key1 = "PIXSIZE" # Item name + Typ1 = 3 # Item data type (string) + Val1 = "1024.0" # Item value + Key2 = "ID" # Item name + Typ2 = 3 # Item data type (string) + Val2 = "TimeInterval" # Item value + Key3 = "START" # Item name + Typ3 = 3 # Item data type (string) + Val3 = "1996-01-01T00:00:00" # Item value + Key4 = "TIME" # Item name + Typ4 = 3 # Item data type (string) + Val4 = "MJD 50814.0" # Item value + Key5 = "REFPOS" # Item name + Typ5 = 3 # Item data type (string) + Val5 = "GEOCENTER" # Item value + Key6 = "RESOLUTION" # Item name + Typ6 = 3 # Item data type (string) + Val6 = "0.8" # Item value + Key7 = "STOP" # Item name + Typ7 = 3 # Item data type (string) + Val7 = "1996-01-01T00:30:00" # Item value + Key8 = "TIMESCALE" # Item name + Typ8 = 3 # Item data type (string) + Val8 = "TT" # Item value + Key9 = "ERROR" # Item name + Typ9 = 3 # Item data type (string) + Val9 = "1.2" # Item value + End KeyMap +End KeyMap diff --git a/ast_tester/stcschan-test1-doc3.ast b/ast_tester/stcschan-test1-doc3.ast new file mode 100644 index 0000000..0758ca7 --- /dev/null +++ b/ast_tester/stcschan-test1-doc3.ast @@ -0,0 +1,272 @@ + Begin Prism # Region extrusion into higher dimensions +# Title = "5-d compound coordinate system" # Title of coordinate system +# Naxes = 5 # Number of coordinate axes +# Domain = "TIME-SKY-SPECTRUM-REDSHIFT" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Modified Julian Date offset from 1996-01-01 " # Label for axis 1 +# Lbl2 = "Right ascension" # Label for axis 2 +# Lbl3 = "Declination" # Label for axis 3 +# Lbl4 = "Frequency" # Label for axis 4 +# Lbl5 = "Optical velocity" # Label for axis 5 +# System = "Compound" # Coordinate system type +# Uni1 = "d" # Units for axis 1 (day) +# Uni2 = "hh:mm:ss.s" # Units for axis 2 +# Uni3 = "ddd:mm:ss" # Units for axis 3 +# Uni4 = "MHz" # Units for axis 4 +# Uni5 = "km/s" # Units for axis 5 +# Dir2 = 0 # Plot axis 2 in reverse direction + IsA Frame # Coordinate system description + Adapt = 1 # Region adapts to coord sys changes + Frm = # Coordinate system + Begin CmpFrame # Compound coordinate system description +# Title = "5-d compound coordinate system" # Title of coordinate system +# Naxes = 5 # Number of coordinate axes +# Domain = "TIME-SKY-SPECTRUM-REDSHIFT" # Coordinate system domain +# Lbl1 = "Modified Julian Date offset from 1996-01-01 " # Label for axis 1 +# Lbl2 = "Right ascension" # Label for axis 2 +# Lbl3 = "Declination" # Label for axis 3 +# Lbl4 = "Frequency" # Label for axis 4 +# Lbl5 = "Optical velocity" # Label for axis 5 +# Uni1 = "d" # Units for axis 1 (day) +# Uni2 = "hh:mm:ss.s" # Units for axis 2 +# Uni3 = "ddd:mm:ss" # Units for axis 3 +# Uni4 = "MHz" # Units for axis 4 +# Uni5 = "km/s" # Units for axis 5 +# Dir2 = 0 # Plot axis 2 in reverse direction + IsA Frame # Coordinate system description + FrameA = # First component Frame + Begin CmpFrame # Compound coordinate system description + FrameA = # First component Frame + Begin CmpFrame # Compound coordinate system description + FrameA = # First component Frame + Begin TimeFrame # Description of time coordinate system + Naxes = 1 # Number of coordinate axes + Epoch = 1995.9986310746 # Julian epoch of observation + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + End Axis + IsA Frame # Coordinate system description + TmScl = "TT" # Time scale + TmOrg = 50083 # Time offset + End TimeFrame + FrameB = # Second component Frame + Begin SkyFrame # Description of celestial coordinate system + Naxes = 2 # Number of coordinate axes + Epoch = 1995.9986310746 # Julian epoch of observation + System = "ICRS" # Coordinate system type + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + End SkyFrame + End CmpFrame + FrameB = # Second component Frame + Begin SpecFrame # Description of spectral coordinate system + Naxes = 1 # Number of coordinate axes + Epoch = 1995.9986310746 # Julian epoch of observation + System = "FREQ" # Coordinate system type + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Unit = "MHz" # Axis units + End Axis + IsA Frame # Coordinate system description + SoR = "Barycentric" # Standard of rest + UFreq = "MHz" # Preferred units for frequency + End SpecFrame + End CmpFrame + FrameB = # Second component Frame + Begin SpecFrame # Description of spectral coordinate system + Naxes = 1 # Number of coordinate axes + Domain = "REDSHIFT" # Coordinate system domain + Epoch = 1995.9986310746 # Julian epoch of observation + System = "VOPT" # Coordinate system type + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Unit = "km/s" # Axis units + End Axis + IsA Frame # Coordinate system description + SoR = "Barycentric" # Standard of rest + UVopt = "km/s" # Preferred units for optical velocity + End SpecFrame + End CmpFrame + RegAxes = 5 # Number of axes spanned by the Region + IsA Region # An area within a coordinate system + RegionA = # First component Region + Begin Prism # Region extrusion into higher dimensions +# Title = "4-d compound coordinate system" # Title of coordinate system +# Naxes = 4 # Number of coordinate axes +# Domain = "TIME-SKY-SPECTRUM" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Modified Julian Date offset from 1996-01-01 " # Label for axis 1 +# Lbl2 = "Right ascension" # Label for axis 2 +# Lbl3 = "Declination" # Label for axis 3 +# Lbl4 = "Frequency" # Label for axis 4 +# System = "Compound" # Coordinate system type +# Uni1 = "d" # Units for axis 1 (day) +# Uni2 = "hh:mm:ss.s" # Units for axis 2 +# Uni3 = "ddd:mm:ss" # Units for axis 3 +# Uni4 = "MHz" # Units for axis 4 +# Dir2 = 0 # Plot axis 2 in reverse direction + IsA Frame # Coordinate system description + Adapt = 1 # Region adapts to coord sys changes + RegAxes = 4 # Number of axes spanned by the Region + IsA Region # An area within a coordinate system + RegionA = # First component Region + Begin Prism # Region extrusion into higher dimensions +# Title = "3-d compound coordinate system" # Title of coordinate system +# Naxes = 3 # Number of coordinate axes +# Domain = "TIME-SKY" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Modified Julian Date offset from 1996-01-01 " # Label for axis 1 +# Lbl2 = "Right ascension" # Label for axis 2 +# Lbl3 = "Declination" # Label for axis 3 +# System = "Compound" # Coordinate system type +# Uni1 = "d" # Units for axis 1 (day) +# Uni2 = "hh:mm:ss.s" # Units for axis 2 +# Uni3 = "ddd:mm:ss" # Units for axis 3 +# Dir2 = 0 # Plot axis 2 in reverse direction + IsA Frame # Coordinate system description + Adapt = 1 # Region adapts to coord sys changes + RegAxes = 3 # Number of axes spanned by the Region + IsA Region # An area within a coordinate system + RegionA = # First component Region + Begin Box # Axis intervals +# Title = "Modified Julian Date [TT] offset from 1996-01-01 00:00:00" # Title of coordinate system +# Naxes = 1 # Number of coordinate axes +# Domain = "TIME" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Modified Julian Date offset from 1996-01-01 " # Label for axis 1 +# System = "MJD" # Coordinate system type +# Uni1 = "d" # Units for axis 1 (day) + IsA Frame # Coordinate system description + Closed = 1 # Boundary is inside + Adapt = 1 # Region adapts to coord sys changes + Points = # Points defining the shape + Begin PointSet # Container for a set of points + Npoint = 2 # Number of points + Ncoord = 1 # Number of coordinates per point + X1 = 0.0104166666678793 # Coordinate values... + X2 = 0.0208333333357587 + End PointSet + Unc = # Region defining positional uncertainties. + Begin Box # Axis intervals +# Title = "Modified Julian Date [TT] offset from 1996-01-01 00:00:00" # Title of coordinate system +# Naxes = 1 # Number of coordinate axes +# Domain = "TIME" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Modified Julian Date offset from 1996-01-01 " # Label for axis 1 +# System = "MJD" # Coordinate system type +# Uni1 = "d" # Units for axis 1 (day) + IsA Frame # Coordinate system description + Adapt = 1 # Region adapts to coord sys changes + Points = # Points defining the shape + Begin PointSet # Container for a set of points + Npoint = 2 # Number of points + Ncoord = 1 # Number of coordinates per point + X1 = 0.0104166666678793 # Coordinate values... + X2 = 0.0104305555567682 + End PointSet + IsA Region # An area within a coordinate system + End Box + IsA Region # An area within a coordinate system + End Box + RegionB = # Second component Region + Begin Circle # Circular or spherical region +# Title = "ICRS coordinates" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# System = "ICRS" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + IsA Frame # Coordinate system description + Adapt = 1 # Region adapts to coord sys changes + Points = # Points defining the shape + Begin PointSet # Container for a set of points + Npoint = 2 # Number of points + Ncoord = 2 # Number of coordinates per point + X1 = 3.12413936106985 # Coordinate values... + X2 = -0.200712863979348 + X3 = 3.13304478740064 + X4 = -0.200712708456374 + End PointSet + Unc = # Region defining positional uncertainties. + Begin Box # Axis intervals +# Title = "ICRS coordinates" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# System = "ICRS" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + IsA Frame # Coordinate system description + Adapt = 1 # Region adapts to coord sys changes + Points = # Points defining the shape + Begin PointSet # Container for a set of points + Npoint = 2 # Number of points + Ncoord = 2 # Number of coordinates per point + X1 = 3.12413936106985 # Coordinate values... + X2 = -0.200712863979348 + X3 = 3.12415519494 + X4 = -0.200697348002298 + End PointSet + IsA Region # An area within a coordinate system + End Box + IsA Region # An area within a coordinate system + End Circle + End Prism + RegionB = # Second component Region + Begin PointList # Collection of points +# Title = "Frequency (Barycentric)" # Title of coordinate system +# Naxes = 1 # Number of coordinate axes +# Domain = "SPECTRUM" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Frequency" # Label for axis 1 +# System = "FREQ" # Coordinate system type +# Uni1 = "MHz" # Units for axis 1 + IsA Frame # Coordinate system description + Adapt = 1 # Region adapts to coord sys changes + Points = # Points defining the shape + Begin PointSet # Container for a set of points + Npoint = 1 # Number of points + Ncoord = 1 # Number of coordinates per point + X1 = 1420.4 # Coordinate values... + End PointSet + IsA Region # An area within a coordinate system + End PointList + End Prism + RegionB = # Second component Region + Begin Box # Axis intervals +# Title = "Optical velocity (Barycentric), rest frequency = 100000 GHz" # Title of coordinate system +# Naxes = 1 # Number of coordinate axes +# Domain = "REDSHIFT" # Coordinate system domain +# Epoch = 1995.9986310746 # Julian epoch of observation +# Lbl1 = "Optical velocity" # Label for axis 1 +# System = "VOPT" # Coordinate system type +# Uni1 = "km/s" # Units for axis 1 + IsA Frame # Coordinate system description + Closed = 1 # Boundary is inside + Adapt = 1 # Region adapts to coord sys changes + Points = # Points defining the shape + Begin PointSet # Container for a set of points + Npoint = 2 # Number of points + Ncoord = 1 # Number of coordinates per point + X1 = 1250 # Coordinate values... + X2 = 2300 + End PointSet + IsA Region # An area within a coordinate system + End Box + End Prism diff --git a/ast_tester/testchannel.f b/ast_tester/testchannel.f new file mode 100644 index 0000000..ac69b2c --- /dev/null +++ b/ast_tester/testchannel.f @@ -0,0 +1,88 @@ + program testrate + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + integer status, ch, sf, sf2 + character buff*50 + status = sai__ok + + call err_begin( status ) + call ast_begin( status ) + + + call ast_tunec( "hrdel", AST__TUNULLC, buff, status ) + if( buff .ne. '%-%^50+%s70+h%+' .and. status .eq. sai__ok ) then + call stopit( 0, status ) + endif + call ast_tunec( "hrdel", "junk", buff, status ) + call ast_tunec( "hrdel", AST__TUNULLC, buff, status ) + if( buff .ne. 'junk' .and. status .eq. sai__ok ) then + call stopit( -1, status ) + endif + + sf = ast_skyframe( ' ', status ) + ch = ast_channel( AST_NULL, AST_NULL, 'SinkFile=./fred.txt', + : status ) + if( ast_write( ch, sf, status ) .ne. 1 ) then + call stopit( 1, status ) + end if + + call ast_set( ch, 'SourceFile=./fred.txt', status ) + if( status .eq. SAI__OK ) then + sf2 = ast_read( ch, status ) + if( status .eq. AST__RDERR ) then + call err_annul( status ) + else + call stopit( 7, status ) + end if + end if + + call ast_clear( ch, 'SinkFile', status ) + + call ast_set( ch, 'SourceFile=./fred.txt', status ) + sf2 = ast_read( ch, status ) + if( sf2 .eq. AST__NULL ) call stopit( 2, status ) + if( .not. ast_equal( sf, sf2, status ) ) then + call stopit( 3, status ) + end if + + + call ast_set( ch, 'SinkFile=./fred2.txt', status ) + if( ast_write( ch, sf, status ) .ne. 1 ) then + call stopit( 4, status ) + end if + call ast_clear( ch, 'SinkFile', status ) + + call ast_set( ch, 'SourceFile=./fred2.txt', status ) + sf2 = ast_read( ch, status ) + if( sf2 .eq. AST__NULL ) call stopit( 5, status ) + if( .not. ast_equal( sf, sf2, status ) ) then + call stopit( 6, status ) + end if + + + call ast_end( status ) + call err_end( status ) + + + + if( status .eq. sai__ok ) then + write(*,*) 'All Channel tests passed' + else + write(*,*) 'Channel tests failed' + end if + + end + + + subroutine stopit( i, status ) + implicit none + include 'SAE_PAR' + integer i, status + if( status .eq. sai__ok ) then + write( *,* ) 'Error ',i + status = sai__error + end if + end diff --git a/ast_tester/testcmpmap.f b/ast_tester/testcmpmap.f new file mode 100644 index 0000000..bd3cad1 --- /dev/null +++ b/ast_tester/testcmpmap.f @@ -0,0 +1,149 @@ + program testcmpmap + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + integer m1, m2, m3, m4, m5, status, i, in(7), out(7) + double precision x( 7 ), y(7), y2(7), matrix( 3 ) + + data matrix /-1.0D0, 1.0D0, 2.0D0 / + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + + + m1 = ast_UnitMap( 1, ' ', status ) + m2 = ast_ZoomMap( 2, 2.0D0, ' ', status ) + m3 = ast_MatrixMap( 3, 3, 1, matrix, ' ', status ) + m4 = ast_CmpMap( ast_CmpMap( m1, m2, .false., ' ', status ), m3, + : .false., ' ', status ) + + + in( 1 ) = 3 + in( 2 ) = 6 + in( 3 ) = 4 + call ast_mapsplit( m4, 3, in, out, m5, status ) + if( m5 .eq. AST__NULL ) then + call stopit( status, 'Error 1' ) + else if( ast_geti( m5, 'Nin', status ) .ne. 3 ) then + call stopit( status, 'Error 2' ) + else if( ast_geti( m5, 'Nout', status ) .ne. 3 ) then + call stopit( status, 'Error 3' ) + end if + + if( out( 1 ) .ne. 3 ) call stopit( status, 'Error 4' ) + if( out( 2 ) .ne. 4 ) call stopit( status, 'Error 5' ) + if( out( 3 ) .ne. 6 ) call stopit( status, 'Error 6' ) + + + call readobj( 'splittest1.ast', m1, status ) + in(1)= 1 + call ast_mapsplit( m1, 1, in, out, m2, status ) + if( m2 .ne. AST__NULL ) call stopit( status, 'Error 7' ) + + in(2)= 4 + in(3)= 2 + call ast_mapsplit( m1, 3, in, out, m2, status ) + if( m2 .eq. AST__NULL ) then + call stopit( status, 'Error 8' ) + else if( ast_geti( m2, 'Nin', status ) .ne. 3 ) then + call stopit( status, 'Error 9' ) + else if( ast_geti( m2, 'Nout', status ) .ne. 3 ) then + call stopit( status, 'Error 10' ) + end if + + + + x(1) = 1.0D0 + x(2) = 2.0D0 + x(3) = 4.0D0 + x(4) = 8.0D0 + call ast_trann( m1, 1,4, 1, x, .true., 4, 1, y, status ) + + x(1) = 1.0D0 + x(2) = 8.0D0 + x(3) = 2.0D0 + call ast_trann( m2, 1, 3, 1, x, .true., 3, 1, y2, status ) + + if( y2( 1 ) .ne. y( 1 ) ) then + call stopit( status, 'Error 11' ) + else if( y2( 2 ) .ne. y( 2 ) ) then + call stopit( status, 'Error 12' ) + else if( y2( 3 ) .ne. y( 4 ) ) then + call stopit( status, 'Error 13' ) + end if + + + + + + + + call ast_end( status ) + call err_rlse( status ) + +c call ast_activememory( 'testcmpmap' ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All cmpmap tests passed' + else + write(*,*) 'cmpmap tests failed' + end if + + end + + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + end + + + + subroutine readobj( file, iobj, status ) + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + external chsource + integer iobj, status, ch + character file*(*) + + open( 10, status='old', file=file ) + + ch = ast_channel( chsource, AST_NULL, ' ', status ) + iobj = ast_read( ch, status ) + call ast_annul( ch, status ) + + close( 10 ) + + end + + subroutine chsource( status ) + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + integer status + character line*200 + + read( 10, '(A)', end=99 ) line + + call ast_putline( line, len( line ), status ) + return + + 99 call ast_putline( line, -1, status ) + + end + diff --git a/ast_tester/testconvert.c b/ast_tester/testconvert.c new file mode 100644 index 0000000..5d86b33 --- /dev/null +++ b/ast_tester/testconvert.c @@ -0,0 +1,232 @@ +#define astCLASS testconvert + +#include "ast_err.h" +#include "error.h" +#include "object.h" +#include "skyframe.h" +#include "specframe.h" +#include "cmpframe.h" +#include "frame.h" +#include "unitmap.h" +#include "permmap.h" + +main(){ + int status_value = 0; + int *status = &status_value; + + AstFrameSet *fs; + + AstSkyFrame *sf = astSkyFrame( " ", status ); + AstSpecFrame *df = astSpecFrame( " ", status ); + AstCmpFrame *cf = astCmpFrame( df, sf, " ", status ); + AstFrame *bf = astFrame( 2, "Domain=SKY", status ); + AstFrame *target, *template; + + + fs = astConvert( bf, sf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), bf ) && astOK ) { + astError( AST__INTER, "Error 1\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), sf ) && astOK ) { + astError( AST__INTER, "Error 2\n", status ); + } else if( !astIsAUnitMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 3\n", status ); + } + } else { + astError( AST__INTER, "Error 4\n", status ); + } + + fs = astConvert( sf, bf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), sf ) && astOK ) { + astError( AST__INTER, "Error 5\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), bf ) && astOK ) { + astError( AST__INTER, "Error 6\n", status ); + } else if( !astIsAUnitMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 7\n", status ); + } + } else { + astError( AST__INTER, "Error 8\n", status ); + } + + + astSetDomain( bf, "NOTSKY" ); + fs = astConvert( bf, sf, " " ); + if( fs ) { + astShow( fs ); + astError( AST__INTER, "Error 9\n", status ); + } + + fs = astConvert( sf, bf, " " ); + if( fs ) { + astShow( fs ); + astError( AST__INTER, "Error 10\n", status ); + } + + astClearDomain( bf ); + + fs = astConvert( bf, sf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), bf ) && astOK ) { + astError( AST__INTER, "Error 11\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), sf ) && astOK ) { + astError( AST__INTER, "Error 12\n", status ); + } else if( !astIsAUnitMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 13\n", status ); + } + } else { + astError( AST__INTER, "Error 14\n", status ); + } + + fs = astConvert( sf, bf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), sf ) && astOK ) { + astError( AST__INTER, "Error 15\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), bf ) && astOK ) { + astError( AST__INTER, "Error 16\n", status ); + } else if( !astIsAUnitMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 17\n", status ); + } + } else { + astError( AST__INTER, "Error 18\n", status ); + } + + + fs = astConvert( bf, cf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), bf ) && astOK ) { + astError( AST__INTER, "Error 19\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), cf ) && astOK ) { + astError( AST__INTER, "Error 20\n", status ); + } else if( !astIsAPermMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 21\n", status ); + } + } else { + astError( AST__INTER, "Error 22\n", status ); + } + + fs = astConvert( cf, bf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), cf ) && astOK ) { + astError( AST__INTER, "Error 23\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), bf ) && astOK ) { + astError( AST__INTER, "Error 24\n", status ); + } else if( !astIsAPermMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 25\n", status ); + } + } else { + astError( AST__INTER, "Error 26\n", status ); + } + + + astSetDomain( bf, "NOTSKY" ); + fs = astConvert( bf, cf, " " ); + if( fs ) { + astShow( fs ); + astError( AST__INTER, "Error 27\n", status ); + } + + fs = astConvert( cf, bf, " " ); + if( fs ) { + astShow( fs ); + astError( AST__INTER, "Error 28\n", status ); + } + + + astSetDomain( bf, "SKY" ); + fs = astConvert( bf, cf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), bf ) && astOK ) { + astError( AST__INTER, "Error 29\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), cf ) && astOK ) { + astError( AST__INTER, "Error 30\n", status ); + } else if( !astIsAPermMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 31\n", status ); + } + } else { + astError( AST__INTER, "Error 32\n", status ); + } + + fs = astConvert( cf, bf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), cf ) && astOK ) { + astError( AST__INTER, "Error 33\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), bf ) && astOK ) { + astError( AST__INTER, "Error 34\n", status ); + } else if( !astIsAPermMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 35\n", status ); + } + } else { + astError( AST__INTER, "Error 36\n", status ); + } + + + fs = astConvert( sf, cf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), sf ) && astOK ) { + astError( AST__INTER, "Error 37\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), cf ) && astOK ) { + astError( AST__INTER, "Error 38\n", status ); + } else if( !astIsAPermMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 39\n", status ); + } + } else { + astError( AST__INTER, "Error 40\n", status ); + } + + fs = astConvert( cf, sf, " " ); + if( fs ) { + if( !astEqual( astGetFrame( fs, AST__BASE ), cf ) && astOK ) { + astError( AST__INTER, "Error 41\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), sf ) && astOK ) { + astError( AST__INTER, "Error 42\n", status ); + } else if( !astIsAPermMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 43\n", status ); + } + } else { + astError( AST__INTER, "Error 44\n", status ); + } + + + fs = astFindFrame( sf, cf, " " ); + if( !fs && astOK ) { + astError( AST__INTER, "Error 45\n", status ); + } + + fs = astFindFrame( cf, sf, " " ); + if( fs && astOK ) { + astError( AST__INTER, "Error 46\n", status ); + } + + astSetMaxAxes( sf, 3 ); + astSetMinAxes( sf, 1 ); + + fs = astFindFrame( cf, sf, " " ); + if( !fs && astOK ) { + astError( AST__INTER, "Error 47\n", status ); + } else { + if( !astEqual( astGetFrame( fs, AST__BASE ), cf ) && astOK ) { + astError( AST__INTER, "Error 48\n", status ); + } else if( !astEqual( astGetFrame( fs, AST__CURRENT ), sf ) && astOK ) { + astError( AST__INTER, "Error 49\n", status ); + } else if( !astIsAPermMap( astGetMapping( fs, AST__BASE, AST__CURRENT ) ) ) { + astError( AST__INTER, "Error 50\n", status ); + } + } + + target = astFrame( 2, "Domain=ARDAPP", status ); + template = (AstFrame *) astSkyFrame( "System=GAPPT", status ); + fs = astFindFrame( target, template, " " ); + if( fs && astOK ) { + astError( AST__INTER, "Error 51\n", status ); + } + + + + + if( astOK ) { + printf(" All astConvert tests passed\n"); + } else { + printf("astConvert tests failed\n"); + } +} diff --git a/ast_tester/testfitschan.f b/ast_tester/testfitschan.f new file mode 100644 index 0000000..9b21e2a --- /dev/null +++ b/ast_tester/testfitschan.f @@ -0,0 +1,918 @@ + program testfitschan + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, fs, fc, i, val + character cards(10)*80, card*80 + + status = sai__ok + + +c call ast_watchmemory( 225192 ) + call ast_begin( status ) + +* Create a FitsChan that will write its contents out to text file +* fred.txt when it is deleted. + fc = ast_fitschan( AST_NULL, AST_NULL, 'SinkFile=./fred.txt', + : status ) + +* Put a FITS-WCS header into it. + cards(1) = 'CRPIX1 = 45' + cards(2) = 'CRPIX2 = 45' + cards(3) = 'CRVAL1 = 45' + cards(4) = 'CRVAL2 = 89.9' + cards(5) = 'CDELT1 = -0.01' + cards(6) = 'CDELT2 = 0.01' + cards(7) = 'CTYPE1 = ''RA---TAN''' + cards(8) = 'CTYPE2 = ''DEC--TAN''' + do i = 1, 8 + call ast_putfits( fc, cards(i), .false., status ) + end do + + call ast_seti( fc, 'Card', 2, status ) + if( .not. ast_getfitsi( fc, '.', val, status ) ) then + call stopit( 777, ' ', status ) + else if( val .ne. 45 ) then + call stopit( 778, ' ', status ) + endif + +* Annul the fitschan. Only 1 ref so this should delete it. + call ast_annul( fc, status ) + +* Create another FitsChan and tell it to read headers from fred.txt. + fc = ast_fitschan( AST_NULL, AST_NULL, 'SourceFile=./fred.txt', + : status ) + +* Check it looks like the original header. + if( ast_geti( fc, 'NCard', status ) .ne. 8 ) then + write(*,*) ast_geti( fc, 'NCard', status ) + call stopit( 1000, ' ', status ) + endif + + if( ast_geti( fc, 'Nkey', status ) .ne. 8 ) then + write(*,*) ast_geti( fc, 'Nkey', status ) + call stopit( 999, ' ', status ) + endif + + call ast_clear( fc, 'Card', status ) + i = 0 + do while( ast_findfits( fc, '%f', card, .true., status ) ) + i = i + 1 + if( card .ne. cards( i ) ) then + call stopit( 1001, ' ', status ) + endif + end do + +* Annul the fitschan. + call ast_annul( fc, status ) + +* Put a simple FITS-WCS header into a FitsChan. + cards(1) = 'CRPIX1 = 45' + cards(2) = 'CRPIX2 = 45' + cards(3) = 'CRVAL1 = 45' + cards(4) = 'CRVAL2 = 89.9' + cards(5) = 'CDELT1 = -0.01' + cards(6) = 'CDELT2 = 0.01' + cards(7) = 'CTYPE1 = ''RA---TAN''' + cards(8) = 'CTYPE2 = ''DEC--TAN''' + + fc = ast_fitschan( AST_NULL, AST_NULL, ' ', status ) + do i = 1, 8 + call ast_putfits( fc, cards(i), .false., status ) + end do + + + call ast_clear( fc, 'Card', status ) + if( ast_geti( fc, 'CardType', status ) .NE. AST__INT ) then + write(*,*) ast_geti( fc, 'CardType', status ),' should be ', + : AST__STRING + call stopit( 993, ' ', status ) + endif + + +* Indicate that the CTYPE1 card should be retained by ast_read. + if( ast_findfits( fc, 'CTYPE1', card, .FALSE., status ) ) then + call ast_retainfits( fc, status ) + endif + +* Read a FrameSet from the FitsChan. + call ast_clear( fc, 'Card', status ) + fs = ast_read( fc, status ) + if( fs .eq. AST__NULL ) then + call stopit( 1, 'No FrameSet read from FitsChan', status ) + end if + +* Check the CTYPE1 card is still present in the FitsChan. + call ast_clear( fc, 'Card', status ) + if( .not. ast_findfits( fc, 'CTYPE1', card, .FALSE., + : status ) ) then + call stopit( 2, 'CTYPE1 has not been retained', status ) + end if + +* Check the CTYPE2 card is not present in the FitsChan. + call ast_clear( fc, 'Card', status ) + if( ast_findfits( fc, 'CTYPE2', card, .FALSE., status ) ) then + call stopit( 3, 'CTYPE2 has been retained', status ) + end if + + + + + +* Do it again, this time with an illegal value for CRPIX2. This will +* cause ast_read to report an error. + cards(1) = 'CRPIX1 = 45' + cards(2) = 'CRPIX2 = ''fred''' + cards(3) = 'CRVAL1 = 45' + cards(4) = 'CRVAL2 = 89.9' + cards(5) = 'CDELT1 = -0.01' + cards(6) = 'CDELT2 = 0.01' + cards(7) = 'CTYPE1 = ''RA---TAN''' + cards(8) = 'CTYPE2 = ''DEC--TAN''' + + fc = ast_fitschan( AST_NULL, AST_NULL, ' ', status ) + do i = 1, 8 + call ast_putfits( fc, cards(i), .false., status ) + end do + +* Set the Clean attribute to true so that used cards are removed from the +* FitsChan even if an error occurrs in astRead. +c call ast_setl( fc, 'Clean', .true., status ) + +* Indicate that the CTYPE1 card should be retained by ast_read. + call ast_clear( fc, 'Card', status ) + if( ast_findfits( fc, 'CTYPE1', card, .FALSE., status ) ) then + call ast_retainfits( fc, status ) + endif + +* Abort if an error has occurred. + if( status .ne. sai__ok ) go to 999 + +* Read a FrameSet from the FitsChan, deferring error reporting. Check an +* error is reported by ast_read. + call ast_clear( fc, 'Card', status ) + + call err_begin( status ) + fs = ast_read( fc, status ) + + if( fs .ne. AST__NULL ) then + call stopit( 4, 'A FrameSet has been read from the FitsChan', + : status ) + + else if( status .eq. sai__ok ) then + call stopit( 5, 'No error has been reported by ast_read', + : status ) + + else + call err_annul( status ) + end if + + call err_end( status ) + +* Check the CTYPE1 card is still present in the FitsChan. + call ast_clear( fc, 'Card', status ) + if( .not. ast_findfits( fc, 'CTYPE1', card, .FALSE., + : status ) ) then + call stopit( 6, 'CTYPE1 has not been retained', status ) + end if + +* Check the CTYPE2 card is also still present in the FitsChan (because +* cards are not removed if an error is reported in ast_read unless the +* Clean attribute is set true). + call ast_clear( fc, 'Card', status ) + if( .not. ast_findfits( fc, 'CTYPE2', card, .FALSE., + : status ) ) then + call stopit( 7, 'CTYPE2 has not been retained', status ) + end if + + + + + +* Do it again, again with an illegal value for CRPIX2, but this time +* setting the Clean attribute true. + cards(1) = 'CRPIX1 = 45' + cards(2) = 'CRPIX2 = ''fred''' + cards(3) = 'CRVAL1 = 45' + cards(4) = 'CRVAL2 = 89.9' + cards(5) = 'CDELT1 = -0.01' + cards(6) = 'CDELT2 = 0.01' + cards(7) = 'CTYPE1 = ''RA---TAN''' + cards(8) = 'CTYPE2 = ''DEC--TAN''' + + fc = ast_fitschan( AST_NULL, AST_NULL, 'Clean=1', status ) + do i = 1, 8 + call ast_putfits( fc, cards(i), .false., status ) + end do + +* Indicate that the CTYPE1 card should be retained by ast_read. + call ast_clear( fc, 'Card', status ) + if( ast_findfits( fc, 'CTYPE1', card, .FALSE., status ) ) then + call ast_retainfits( fc, status ) + endif + +* Abort if an error has occurred. + if( status .ne. sai__ok ) go to 999 + +* Read a FrameSet from the FitsChan, deferring error reporting. Check an +* error is reported by ast_read. + call ast_clear( fc, 'Card', status ) + + call err_begin( status ) + fs = ast_read( fc, status ) + + if( fs .ne. AST__NULL ) then + call stopit( 8, 'A FrameSet has been read from the FitsChan', + : status ) + + else if( status .eq. sai__ok ) then + call stopit( 9, 'No error has been reported by ast_read', + : status ) + + else + call err_annul( status ) + end if + + call err_end( status ) + +* Check the CTYPE1 card is still present in the FitsChan (because of the +* call to ast_retainfits). + call ast_clear( fc, 'Card', status ) + if( .not. ast_findfits( fc, 'CTYPE1', card, .FALSE., + : status ) ) then + call stopit( 10, 'CTYPE1 has not been retained', status ) + end if + +* Check the CTYPE2 card is no longer present in the FitsChan (because +* the Clean attribute is set true). + call ast_clear( fc, 'Card', status ) + if( ast_findfits( fc, 'CTYPE2', card, .FALSE., status ) ) then + call stopit( 11, 'CTYPE2 has been retained', status ) + end if + +* Test -TAB + call checktab( status ) + call checktab2( status ) + +* Read a SIP header and then attempt to write it out. It should fail +* because the SIP header is non-linear. + call ast_emptyfits( fc, status ) + call ast_set( fc, 'SourceFile=sip.head', status ) + call ast_clear( fc, 'Card', status ) + fs = ast_read( fc, status ) + call ast_set( fc, 'Encoding=FITS-WCS', status ) + if( fs .eq. AST__NULL ) then + call stopit( 12, 'Failed to read SIP header', status ) + else if( ast_write( fc, fs, status ) .gt. 0 ) then + call stopit( 13, 'Test on SIP header non-linearity failed', + : status ) + end if + + + + + + + + + 999 continue + + call ast_end( status ) + call ast_activememory( 'testfitschan' ) + call ast_flushmemory( 1 ); + + + if( status .eq. sai__ok ) then + write(*,*) 'All FitsChan tests passed' + else + write(*,*) 'FitsChan tests failed' + end if + + end + + + subroutine stopit( errnum, text, status ) + implicit none + include 'SAE_PAR' + character text*(*) + integer errnum, status + + if( status .eq. sai__ok ) then + status = sai__error + call msg_seti( 'N', errnum ) + call msg_setc( 'T', text ) + call err_rep( ' ', 'Error ^N: ^T', status ) + end if + + end + + + subroutine checktab( status ) + implicit none + + external tabsource + + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + include 'PRM_PAR' + include 'CNF_PAR' + integer status, sf, mm, fc, fs,gf, tables, table, size, pntr1, + : nelem, pntr2, i, lm, sm, fs2, map, fc2, ncard, fs3 + character key*20,card*80 + double precision lut( 100 ), shift, x(3), y(3), y2(3) + + common /tabsrc/ tables + + if( status .ne. sai__ok ) return + + call err_begin( status ) + call ast_begin( status ) + + sf = ast_specframe( 'system=freq,unit=MHz', status ) + gf = ast_frame( 1, 'domain=GRID', status ) + mm = ast_mathmap( 1, 1, 1, 'y=1/(x*x)', 1, 'x=1/sqrt(y)', ' ', + : status ) + fs = ast_frameset( gf, ' ', status ) + call ast_addframe( fs, AST__BASE, mm, sf, status ) + + fc = ast_fitschan( ast_null, ast_null, 'Encoding=FITS-WCS', + : status ) + call ast_putfits( fc, 'NAXIS = 1', .false., status ) + call ast_putfits( fc, 'NAXIS1 = 100', .false., status ) + + + if( ast_write( fc, fs, status ) .ne. 0 ) then + call stopit( 1000, ' ', status ) + else if( ast_gettables( fc, status ) .ne. AST__NULL ) then + call stopit( 1001, ' ', status ) + endif + + call ast_setl( fc, 'TabOK', .true., status ) + + if( ast_write( fc, fs, status ) .ne. 1 ) + : call stopit( 1002, ' ', status ) + + tables = ast_gettables( fc, status ) + if( tables .eq. AST__NULL ) then + call stopit( 1003, ' ', status ) + else if( .not. ast_isakeymap( tables, status ) ) then + call stopit( 1004, ' ', status ) + else if( ast_mapsize( tables, status ) .ne. 1 ) then + call stopit( 1005, ' ', status ) + endif + + key = ast_mapkey( tables, 1, status ) + if( key .ne. 'WCS-TAB' ) then + call stopit( 1006, ' ', status ) + endif + + if( .not. ast_mapget0a( tables, 'WCS-TAB', table, + : status ) ) then + call stopit( 1007, ' ', status ) + else if( .not. ast_isafitstable( table, status ) ) then + call stopit( 1004, ' ', status ) + endif + + if( ast_geti( table, 'NColumn', status ) .ne. 2 ) then + call stopit( 1005, ' ', status ) + else if( ast_geti( table, 'NRow', status ) .ne. 1 ) then + call stopit( 1006, ' ', status ) + else if( ast_geti( table, 'ColumnLength(coords1)', status ) .ne. + : 197 ) then + call stopit( 1007, ' ', status ) + else if( ast_geti( table, 'ColumnLength(index1)', status ) .ne. + : 197 ) then + call stopit( 1008, ' ', status ) + end if + + size = ast_columnsize( table, 'COORDS1', status ) + if( size .ne. VAL__NBD*197 ) call stopit( 1009, ' ', status ) + call psx_malloc( size, pntr1, status ) + call ast_getcolumndata( table, 'Coords1', 0.0, AST__BAD, size, + : %val( cnf_pval( pntr1 ) ), nelem, + : status ) + if( nelem .ne. 197 ) call stopit( 1010, ' ', status ) + + size = ast_columnsize( table, 'INDEX1', status ) + if( size .ne. VAL__NBD*197 ) call stopit( 1011, ' ', status ) + call psx_malloc( size, pntr2, status ) + call ast_getcolumndata( table, 'inDex1', 0.0, AST__BAD, size, + : %val( cnf_pval( pntr2 ) ), nelem, + : status ) + if( nelem .ne. 197 ) call stopit( 1012, ' ', status ) + + call checkft( 197, %val( cnf_pval( pntr1 ) ), + : %val( cnf_pval( pntr2 ) ), status ) + + call psx_free( pntr1, status ) + call psx_free( pntr2, status ) + +c -------------------------------------------------------------------- + do i = 1, 100 + lut( i ) = 1.0D0/dble(i*i) + end do + + lm = ast_lutmap( 100, lut, -49.0D0, 1.0D0, ' ', status ) + + call ast_set( sf, 'System=Wave,Unit=m', status ) + call ast_removeframe( fs, AST__CURRENT, status ) + call ast_addframe( fs, AST__BASE, lm, sf, status ) + call ast_set( fs, 'System=freq', status ) + + shift = 50.0D0 + sm = ast_shiftmap( 1, shift, ' ', status ) + call ast_remapframe( fs, AST__BASE, sm, status ) + + call ast_removetables( fc, 'WCS-TAB', status ) + call ast_purgewcs( fc, status ) + + if( ast_write( fc, fs, status ) .ne. 1 ) + : call stopit( 1013, ' ', status ) + tables = ast_gettables( fc, status ) + + if( tables .eq. AST__NULL ) then + call stopit( 1014, ' ', status ) + else if( .not. ast_isakeymap( tables, status ) ) then + call stopit( 1015, ' ', status ) + else if( ast_mapsize( tables, status ) .ne. 1 ) then + call stopit( 1016, ' ', status ) + endif + + key = ast_mapkey( tables, 1, status ) + if( key .ne. 'WCS-TAB' ) then + call stopit( 1017, ' ', status ) + endif + + if( .not. ast_mapget0a( tables, 'WCS-TAB', table, + : status ) ) then + call stopit( 1018, ' ', status ) + else if( .not. ast_isafitstable( table, status ) ) then + call stopit( 1019, ' ', status ) + endif + + if( ast_geti( table, 'NColumn', status ) .ne. 1 ) then + call stopit( 1020, ' ', status ) + else if( ast_geti( table, 'NRow', status ) .ne. 1 ) then + call stopit( 1021, ' ', status ) + else if( ast_geti( table, 'ColumnLength(coords1)', status ) .ne. + : 100 ) then + call stopit( 1022, ' ', status ) + end if + + size = ast_columnsize( table, 'COORDS1', status ) + if( size .ne. VAL__NBD*100 ) call stopit( 1024, ' ', status ) + call psx_malloc( size, pntr1, status ) + call ast_getcolumndata( table, 'Coords1', 0.0, AST__BAD, size, + : %val( cnf_pval( pntr1 ) ), nelem, + : status ) + if( nelem .ne. 100 ) call stopit( 1025, ' ', status ) + + call checkft2( 100, %val( cnf_pval( pntr1 ) ), status ) + + call psx_free( pntr1, status ) + +c -------------------------------------------------------------------- + + call ast_removetables( fc, ' ', status ) + fc2 = ast_copy( fc, status ) + call ast_puttables( fc, tables, status ) + call ast_clear( fc, 'Card', status ) + + fs2 = ast_read( fc, status ) + if( fs2 .eq. ast__null ) call stopit( 1028, ' ', status ) + + if( .not. ast_equal( ast_getframe( fs, ast__current, status ), + : ast_getframe( fs2, ast__current, status ), + : status ) ) then + call stopit( 1029, ' ', status ) + endif + + map = ast_cmpmap( ast_getmapping( fs, ast__base, ast__current, + : status ), + : ast_getmapping( fs2, ast__current, ast__base, + : status ), .TRUE., ' ', status ) + + x(1) = 1.0D0; + x(2) = 50.0D0; + x(3) = 100.0D0; + call ast_tran1( map, 3, x, .true., y, status ) + + if( abs( y(1) - x(1) ) .gt. 1.0D-4 .OR. + : abs( y(2) - x(2) ) .gt. 1.0D-4 .OR. + : abs( y(3) - x(3) ) .gt. 1.0D-4 ) then + call stopit( 1030, ' ', status ) + end if + + +c -------------------------------------------------------------------- + if( .not. ast_getl( fc2, 'TabOK', status ) ) then + call stopit( 1031, ' ', status ) + endif + ncard = ast_geti( fc2, 'Ncard', status ) + + call ast_clear( fc2, 'Card', status ) + fs2 = ast_read( fc2, status ) + if( status .ne. AST__NOTAB ) then + if( status .ne. SAI__OK ) call err_flush( status ) + call stopit( 1032, ' ', status ) + else + call err_annul( status ) + end if + + call ast_setl( fc2, 'TabOK', .false., status ) + call ast_clear( fc2, 'Card', status ) + fs2 = ast_read( fc2, status ) + if( status .ne. AST__BDFTS ) then + if( status .ne. SAI__OK ) call err_flush( status ) + call stopit( 1032, ' ', status ) + else + call err_annul( status ) + end if + call ast_setl( fc2, 'TabOK', .true., status ) + + if( ncard .ne. ast_geti( fc2, 'Ncard', status ) ) then + call stopit( 1034, ' ', status ) + endif + + call ast_tablesource( fc2, tabsource, status ) + call ast_clear( fc2, 'Card', status ) + fs2 = ast_read( fc2, status ) + if( fs2 .eq. ast__null ) call stopit( 1035, ' ', status ) + + if( .not. ast_equal( ast_getframe( fs, ast__current, status ), + : ast_getframe( fs2, ast__current, status ), + : status ) ) then + call stopit( 1036, ' ', status ) + endif + + map = ast_cmpmap( ast_getmapping( fs, ast__base, ast__current, + : status ), + : ast_getmapping( fs2, ast__current, ast__base, + : status ), .TRUE., ' ', status ) + + x(1) = 1.0D0; + x(2) = 50.0D0; + x(3) = 100.0D0; + call ast_tran1( map, 3, x, .true., y, status ) + + if( abs( y(1) - x(1) ) .gt. 1.0D-4 .OR. + : abs( y(2) - x(2) ) .gt. 1.0D-4 .OR. + : abs( y(3) - x(3) ) .gt. 1.0D-4 ) then + call stopit( 1037, ' ', status ) + end if + +c -------------------------------------------------------------------- + call readobj( 'sparse.ast', fs, status ) + + fc = ast_fitschan( ast_null, ast_null, + : 'Encoding=FITS-WCS,TabOK=1', status ) + + call ast_putfits( fc, 'NAXIS = 2', .false., status ) + call ast_putfits( fc, 'NAXIS1 = 2000', .false., status ) + call ast_putfits( fc, 'NAXIS2 = 1', .false., status ) + + if( ast_write( fc, fs, status ) .ne. 1 ) then + call stopit( 1038, ' ', status ) + end if + + call ast_clear( fc, 'Card', status ) + fs2 = ast_read( fc, status ) + + call ast_invert( fs, status ) + call ast_invert( fs2, status ) + fs3 = ast_convert( fs, fs2, 'SKY-DSBSPECTRUM', status ) + if( fs3 .eq. AST__NULL ) then + call stopit( 1039, ' ', status ) + endif + + if( ast_getc( ast_getframe( fs, AST__BASE, status ), 'Domain', + : status ) .ne. 'SKY-DSBSPECTRUM' ) then + call stopit( 1040, ' ', status ) + endif + + if( ast_getc( ast_getframe( fs2, AST__BASE, status ), 'Domain', + : status ) .ne. 'SKY-DSBSPECTRUM' ) then + call stopit( 1041, ' ', status ) + endif + + call ast_invert( fs, status ) + call ast_invert( fs2, status ) + + x( 1 ) = 1.0 + x( 2 ) = 1.0 + x( 3 ) = 1.0 + call ast_trann( fs, 1, 3, 1, x, .true., 3, 1, y, status ) + call ast_trann( fs2, 1, 3, 1, x, .true., 3, 1, y2, status ) + + do i = 1, 3 + if( abs( y(i) - y2(i) ) .gt. 1.0E-8 ) then + call stopit( 1042, ' ', status ) + endif + enddo + + x( 1 ) = 10.0 + x( 2 ) = 1.0 + x( 3 ) = 1000.0 + call ast_trann( fs, 1, 3, 1, x, .true., 3, 1, y, status ) + call ast_trann( fs2, 1, 3, 1, x, .true., 3, 1, y2, status ) + + do i = 1, 3 + if( abs( y(i) - y2(i) ) .gt. 1.0E-8 ) then + call stopit( 1042, ' ', status ) + endif + enddo + + +c -------------------------------------------------------------------- + + sf = ast_frame( 1, 'domain=voltage,unit=V', status ) + gf = ast_frame( 1, 'domain=GRID', status ) + mm = ast_mathmap( 1, 1, 1, 'y=1/(x*x)', 1, 'x=1/sqrt(y)', ' ', + : status ) + fs = ast_frameset( gf, ' ', status ) + call ast_addframe( fs, AST__BASE, mm, sf, status ) + + call ast_emptyfits( fc, status ) + call ast_putfits( fc, 'NAXIS = 1', .false., status ) + call ast_putfits( fc, 'NAXIS1 = 100', .false., status ) + + if( ast_write( fc, fs, status ) .ne. 1 ) + : call stopit( 1043, ' ', status ) + + if( ast_getfitss( fc, 'CTYPE1', card, status ) ) then + if( card .ne. 'VOLT-TAB' ) call stopit( 1059, ' ', status ) + else + call stopit( 1060, ' ', status ) + endif + + tables = ast_gettables( fc, status ) + if( tables .eq. AST__NULL ) then + call stopit( 1044, ' ', status ) + else if( .not. ast_isakeymap( tables, status ) ) then + call stopit( 1045, ' ', status ) + else if( ast_mapsize( tables, status ) .ne. 1 ) then + call stopit( 1046, ' ', status ) + endif + + key = ast_mapkey( tables, 1, status ) + if( key .ne. 'WCS-TAB' ) then + call stopit( 1047, ' ', status ) + endif + + if( .not. ast_mapget0a( tables, 'WCS-TAB', table, + : status ) ) then + call stopit( 1048, ' ', status ) + else if( .not. ast_isafitstable( table, status ) ) then + call stopit( 1049, ' ', status ) + endif + + if( ast_geti( table, 'NColumn', status ) .ne. 2 ) then + call stopit( 1050, ' ', status ) + else if( ast_geti( table, 'NRow', status ) .ne. 1 ) then + call stopit( 1051, ' ', status ) + else if( ast_geti( table, 'ColumnLength(coords1)', status ) .ne. + : 197 ) then + call stopit( 1052, ' ', status ) + else if( ast_geti( table, 'ColumnLength(index1)', status ) .ne. + : 197 ) then + call stopit( 1053, ' ', status ) + end if + + size = ast_columnsize( table, 'COORDS1', status ) + + if( size .ne. VAL__NBD*197 ) call stopit( 1054, ' ', status ) + call psx_malloc( size, pntr1, status ) + call ast_getcolumndata( table, 'Coords1', 0.0, AST__BAD, size, + : %val( cnf_pval( pntr1 ) ), nelem, + : status ) + if( nelem .ne. 197 ) call stopit( 1055, ' ', status ) + + size = ast_columnsize( table, 'INDEX1', status ) + if( size .ne. VAL__NBD*197 ) call stopit( 1056, ' ', status ) + call psx_malloc( size, pntr2, status ) + call ast_getcolumndata( table, 'inDex1', 0.0, AST__BAD, size, + : %val( cnf_pval( pntr2 ) ), nelem, + : status ) + if( nelem .ne. 197 ) call stopit( 1057, ' ', status ) + + call checkft( 197, %val( cnf_pval( pntr1 ) ), + : %val( cnf_pval( pntr2 ) ), status ) + + call psx_free( pntr1, status ) + call psx_free( pntr2, status ) + + + call ast_clear( fc, 'Card', status ) + fs2 = ast_read( fc, status ) + if( fs2 .eq. AST__NULL ) + : call stopit( 1058, ' ', status ) + + call ast_invert( fs, status ) + call ast_invert( fs2, status ) + fs3 = ast_convert( fs2, fs, ' ', status ) + if( fs3 .eq. AST__NULL ) then + call stopit( 1061, ' ', status ) + endif + + if( ast_getc( ast_getframe( fs, AST__BASE, status ), 'Domain', + : status ) .ne. 'VOLTAGE' ) then + call stopit( 1062, ' ', status ) + endif + + if( ast_getc( ast_getframe( fs2, AST__BASE, status ), 'Domain', + : status ) .ne. 'VOLTAGE' ) then + call stopit( 1063, ' ', status ) + endif + + x(1) = 1.0 + x(2) = 10.0 + x(3) = 100.0 + call ast_tran1( fs3, 3, x, .true., y, status ) + if( abs( y(1) - x(1) ) .gt. 1.0D-2 .OR. + : abs( y(2) - x(2) ) .gt. 1.0D-2 .OR. + : abs( y(3) - x(3) ) .gt. 1.0D-2 ) then + call stopit( 1064, ' ', status ) + end if + + + + call ast_end( status ) + call err_end( status ) + + end + + + subroutine checkft( nelem, coords, indx, status ) + implicit none + include 'SAE_PAR' + integer nelem, status + double precision coords( nelem ), indx( nelem ) + + if( status .ne. sai__ok ) return + + if( indx( 1 ) .ne. 1.0D0 ) then + call stopit( 2001, ' ', status ) + + else if( coords( 1 ) .ne. 1.0D0 ) then + call stopit( 2002, ' ', status ) + + else if( indx( nelem ) .ne. 1.0D2 ) then + call stopit( 2003, ' ', status ) + + else if( coords( nelem ) .ne. 1.0D-4 ) then + call stopit( 2004, ' ', status ) + + else if( coords( nelem/2 ) .ne. + : indx( nelem/2 )**(-2) ) then + call stopit( 2005, ' ', status ) + end if + + end + + subroutine checkft2( nelem, coords, status ) + implicit none + include 'SAE_PAR' + integer nelem, status + double precision coords( nelem ) + + if( status .ne. sai__ok ) return + + if( abs( coords( 1 ) - 299.792458 ) .gt. 1.0D-5 ) then + call stopit( 3002, ' ', status ) + + else if( abs( coords( nelem ) - 2997924.58 ) .gt. 1.0D-1 ) then + call stopit( 3004, ' ', status ) + + end if + + end + + subroutine tabsource( fc, extnam, extver, extlevel, status ) + implicit none + include 'AST_PAR' + + integer fc, status, tables, table, extver, extlevel + character extnam*(*) + + common /tabsrc/ tables + + if( extnam .ne. 'WCS-TAB' ) then + call stopit( 1035, ' ', status ) + + else if( .not. ast_mapget0a( tables, extnam, table, + : status ) ) then + call stopit( 1036, ' ', status ) + + else if( .not. ast_isafitstable( table, status ) ) then + call stopit( 1037, ' ', status ) + + else if( extver .ne. 1 ) then + write(*,*) 'EXTVER=',extver + call stopit( 1065, ' ', status ) + + else if( extlevel .ne. 1 ) then + call stopit( 1066, ' ', status ) + + else + call ast_puttables( fc, tables, status ) + + endif + + call ast_annul( table, status ) + + end + + + + subroutine readobj( file, iobj, status ) + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + external chsource + integer iobj, status, ch + character file*(*) + + open( 10, status='old', file=file ) + + ch = ast_channel( chsource, AST_NULL, ' ', status ) + iobj = ast_read( ch, status ) + call ast_annul( ch, status ) + + close( 10 ) + + end + + subroutine chsource( status ) + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + integer status + character line*200 + + read( 10, '(A)', end=99 ) line + + call ast_putline( line, len( line ), status ) + return + + 99 call ast_putline( line, -1, status ) + + end + + + + + subroutine checktab2( status ) + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + integer status, sf, mmm, mm, fc, fs, fs2, gf + + if( status .ne. sai__ok ) return + + call err_begin( status ) + call ast_begin( status ) + + sf = ast_skyframe( ' ', status ) + call ast_setd( sf, 'SkyRef(1)', 0.0001D0, status ) + call ast_setd( sf, 'SkyRef(2)', 0.0001D0, status ) + + gf = ast_frame( 2, 'domain=GRID', status ) + + mm = ast_mathmap( 1, 1, 1, 'y=(x+50)**(-2)', 1, 'x=-50+1/sqrt(y)', + : ' ', status ) + mmm = ast_cmpmap( mm, mm, .false., ' ', status ) + + fs = ast_frameset( gf, ' ', status ) + call ast_addframe( fs, AST__BASE, mmm, sf, status ) + + fc = ast_fitschan( ast_null, ast_null, 'Encoding=FITS-WCS,'// + : 'TabOK=1', status ) + call ast_putfits( fc, 'NAXIS = 2', .false., status ) + call ast_putfits( fc, 'NAXIS1 = 100', .false., status ) + call ast_putfits( fc, 'NAXIS2 = 100', .false., status ) + + if( ast_write( fc, fs, status ) .ne. 1 ) then + call stopit( 2000, ' ', status ) + endif + + call ast_clear( fc, 'Card', status ) + fs2 = ast_read( fc, status ) + if( fs2 .eq. AST__NULL ) + : call stopit( 2001, ' ', status ) + + if( abs( ast_getd( fs2, 'SkyRef(1)', status ) - 0.0001 ) .gt. + : 1E-7 ) call stopit( 2001, ' ', status ) + if( abs( ast_getd( fs2, 'SkyRef(2)', status ) - 0.0001 ) .gt. + : 1E-7 ) call stopit( 2001, ' ', status ) + + call ast_end( status ) + call err_end( status ) + + end diff --git a/ast_tester/testfitstable.f b/ast_tester/testfitstable.f new file mode 100644 index 0000000..e85f065 --- /dev/null +++ b/ast_tester/testfitstable.f @@ -0,0 +1,590 @@ + program testfitstable + implicit none + + include 'AST_PAR' + include 'AST_ERR' + include 'SAE_PAR' + include 'CNF_PAR' + + integer status, table, table2, dims( 7 ), header, ival, l, nval, + : icard, colsize, pntr, head, clen, oldnull, null + byte bytes(2,3),bval + logical wasset, hasnull + real rval + character cval*30, text(3)*10, card*70 + character header1(18)*30 + character header2(20)*30 + + data header1 / 'XTENSION= ''BINTABLE''', + : 'BITPIX = 8', + : 'NAXIS = 2', + : 'NAXIS1 = 10', + : 'NAXIS2 = 0', + : 'PCOUNT = 0', + : 'GCOUNT = 1', + : 'TFIELDS = 3', + : 'TFORM1 = ''6B ''', + : 'TTYPE1 = ''BYTECOL ''', + : 'TUNIT1 = ''ADU ''', + : 'TDIM1 = ''(2,3) ''', + : 'TFORM2 = ''1J ''', + : 'TTYPE2 = ''INTCOL ''', + : 'TUNIT2 = ''m ''', + : 'TFORM3 = ''0A ''', + : 'TTYPE3 = ''STRINGCOL''', + : 'TDIM3 = ''(0,3) ''' / + + + data header2 / 'XTENSION= ''BINTABLE''', + : 'BITPIX = 8', + : 'NAXIS = 2', + : 'NAXIS1 = 40', + : 'NAXIS2 = 3', + : 'PCOUNT = 0', + : 'GCOUNT = 1', + : 'TFIELDS = 3', + : 'TFORM1 = ''6B ''', + : 'TTYPE1 = ''BYTECOL ''', + : 'TUNIT1 = ''ADU ''', + : 'TNULL1 = 254', + : 'TDIM1 = ''(2,3) ''', + : 'TFORM2 = ''1J ''', + : 'TTYPE2 = ''INTCOL ''', + : 'TUNIT2 = ''m ''', + : 'TNULL2 = 2147483647', + : 'TFORM3 = ''30A ''', + : 'TTYPE3 = ''STRINGCOL''', + : 'TDIM3 = ''(10,3) ''' / + +c call ast_watchmemory(483) + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + + table = ast_fitstable( AST__NULL, ' ', status ) + + header = ast_gettableheader( table, status ) + if( ast_geti( header, 'NCard', status ) .ne. 8 ) then + call stopit( status, 'FitsTable error 1' ) + else if( .not. ast_getfitsi( header, 'NAXIS', ival, status )) then + call stopit( status, 'FitsTable error 2' ) + else if( ival .ne. 2 ) then + call stopit( status, 'FitsTable error 3' ) + else if( .not. ast_getfitsi( header, 'NAXIS1', ival, status)) then + call stopit( status, 'FitsTable error 4' ) + else if( ival .ne. 0 ) then + call stopit( status, 'FitsTable error 5' ) + else if( .not. ast_getfitsi( header, 'NAXIS2', ival, status)) then + call stopit( status, 'FitsTable error 6' ) + else if( ival .ne. 0 ) then + call stopit( status, 'FitsTable error 7' ) + endif + call ast_annul( header, status ) + + + call ast_addcolumn( table, 'JUNK', AST__OBJECTTYPE, 0, 0, 'm', + : status ) + if( status .eq. AST__NAXIN ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, 'FitsTable error 8' ) + endif + + dims( 1 ) = 2 + dims( 2 ) = 3 + call ast_addcolumn( table, 'BYTECOL', AST__BYTETYPE, 2, dims, + : 'ADU', status ) + + call ast_addcolumn( table, 'INTCOL', AST__INTTYPE, 0, 0, 'm', + : status ) + + dims( 1 ) = 3 + call ast_addcolumn( table, 'STRINGCOL', AST__STRINGTYPE, 1, dims, + : ' ', status ) + + + header = ast_gettableheader( table, status ) + icard = 0 + do while( ast_findfits( header, '%f', card, .true., status ) ) + icard = icard + 1 + if( icard .gt. 18 ) then + call stopit( status, 'FitsTable error 9' ) + else if( card .ne. header1( icard ) ) then + call stopit( status, 'FitsTable error 10' ) + end if + end do + if( icard .ne. 18 ) call stopit( status, 'FitsTable error 11' ) + + + table2 = ast_fitstable( header, ' ', status ) + call ast_annul( header, status ) + + if( ast_geti( table2, 'Ncolumn', status ) .ne. 3 ) then + call stopit( status, 'FitsTable error 11a' ) + end if + + + + if( ast_geti( table2, 'ColumnLength(bytecol)', status ) + : .ne. 6 ) then + call stopit( status, 'FitsTable error 11b' ) + endif + + if( ast_geti( table2, 'ColumnNdim(bytecol)', status ) + : .ne. 2 ) then + call stopit( status, 'FitsTable error 11c' ) + end if + + if( ast_geti( table2, 'ColumnType(bytecol)', status ) + : .ne. AST__BYTETYPE ) then + call stopit( status, 'FitsTable error 11d' ) + end if + + if( ast_getc( table2, 'ColumnUnit(bytecol)', status ) + : .ne. 'ADU' ) then + call stopit( status, 'FitsTable error 11e' ) + end if + + + if( ast_geti( table2, 'ColumnLength(intcol)', status ) + : .ne. 1 ) then + call stopit( status, 'FitsTable error 11f' ) + endif + + if( ast_geti( table2, 'ColumnNdim(intcol)', status ) + : .ne. 0 ) then + call stopit( status, 'FitsTable error 11g' ) + end if + + if( ast_geti( table2, 'ColumnType(intcol)', status ) + : .ne. AST__INTTYPE ) then + call stopit( status, 'FitsTable error 11h' ) + end if + + if( ast_getc( table2, 'ColumnUnit(intcol)', status ) + : .ne. 'm' ) then + call stopit( status, 'FitsTable error 11i' ) + end if + + + if( ast_geti( table2, 'ColumnLength(StringCol)', status ) + : .ne. 3 ) then + call stopit( status, 'FitsTable error 11j' ) + endif + + if( ast_geti( table2, 'ColumnNdim(StringCol)', status ) + : .ne. 1 ) then + call stopit( status, 'FitsTable error 11k' ) + end if + + if( ast_geti( table2, 'ColumnType(StringCol)', status ) + : .ne. AST__STRINGTYPE ) then + call stopit( status, 'FitsTable error 11l' ) + end if + + if( ast_getc( table2, 'ColumnUnit(StringCol)', status ) + : .ne. ' ' ) then + call stopit( status, 'FitsTable error 11m' ) + end if + + + + bytes(1,1) = 0 + bytes(1,2) = 128 + bytes(1,3) = -127 + bytes(2,1) = 1 + bytes(2,2) = 127 + bytes(2,3) = -1 + call ast_mapput1b( table, 'BYTECOL(1)', 6, bytes, ' ', status ) + + bytes(1,1) = 0 + bytes(1,2) = 0 + bytes(1,3) = 0 + bytes(2,1) = 1 + bytes(2,2) = 1 + bytes(2,3) = 1 + call ast_mapput1b( table, 'BYTECOL(2)', 6, bytes, ' ', status ) + + call ast_mapput0i( table, 'INTCOL(2)', 10, ' ', status ) + + call ast_mapput0i( table, 'INTCOL(3)', -10, ' ', status ) + + text( 1 ) = 'hello' + text( 2 ) = ' ' + text( 3 ) = 'goodbye' + call ast_mapput1c( table, 'STRINGCOL(1)', 3, text, ' ', status ) + + text( 1 ) = ' ' + text( 2 ) = ' ' + text( 3 ) = ' ' + call ast_mapput1c( table, 'STRINGCOL(3)', 3, text, ' ', status ) + + if( ast_geti( table, 'Nrow', status ) .ne. 3 ) then + call stopit( status, 'FitsTable error 12' ) + endif + + if( ast_geti( table, 'Ncolumn', status ) .ne. 3 ) then + call stopit( status, 'FitsTable error 13' ) + endif + + head = ast_gettableheader( table, status ) + table2 = ast_fitstable( head, ' ', status ) + call ast_annul( head, status ) + + colsize = ast_columnsize( table, 'stringcol', status ) + if( colsize .ne. 90 ) then + call stopit( status, 'FitsTable error 13a' ) + else + call psx_malloc( colsize, pntr, status ) + call ast_getcolumndata( table, 'StringCol', 0.0, 0.0D0, + : colsize, %val( cnf_pval(pntr)), + : colsize, status ) + if( colsize .ne. 9 ) call stopit( status, + : 'FitsTable error 13b' ) + call checkstrings( table, %val( CNF_PVAL( pntr ) ), status ) + + clen = ast_geti( table, 'ColumnLenC(StringCol)', status ) + if( clen .ne. 10 ) call stopit( status, + : 'FitsTable error 13c' ) + + colsize = 90 + call ast_putcolumndata( table2, 'StringCol', 10, colsize, + : %val( CNF_PVAL( pntr ) ), status ) + call ast_getcolumndata( table2, 'StringCol', 0.0, 0.0D0, + : colsize, %val( cnf_pval(pntr)), + : colsize, status ) + + if( colsize .ne. 9 ) call stopit( status, + : 'FitsTable error 13d' ) + call checkstrings( table2, %val( CNF_PVAL( pntr ) ), status ) + + call psx_free( pntr, status ) + end if + + colsize = ast_columnsize( table, 'bytecol', status ) + if( colsize .ne. 18 ) then + call stopit( status, 'FitsTable error 13e' ) + else + call psx_malloc( colsize, pntr, status ) + call ast_getcolumndata( table, 'BYTECOL', 0.0, 0.0D0, colsize, + : %val( cnf_pval( pntr ) ), colsize, + : status ) + if( colsize .ne. 18 ) call stopit( status, + : 'FitsTable error 13f' ) + + null = ast_columnnull( table, 'BYTECOL', .FALSE., 0, + : wasset, hasnull, status ) + call checkbytes( table, %val( CNF_PVAL( pntr ) ), null, + : status ) + + colsize = 18 + call ast_putcolumndata( table2, 'byteCol', 0, colsize, + : %val( CNF_PVAL( pntr ) ), status ) + oldnull = ast_columnnull( table2, 'BYTECOL', .TRUE., null, + : wasset, hasnull, status ) + call ast_getcolumndata( table2, 'BYTECOL', 0.0, 0.0D0, colsize, + : %val( cnf_pval( pntr ) ), colsize, + : status ) + if( colsize .ne. 18 ) call stopit( status, + : 'FitsTable error 13g' ) + call checkbytes( table2, %val( CNF_PVAL( pntr ) ), null, + : status ) + + call psx_free( pntr, status ) + end if + + colsize = ast_columnsize( table, 'intcol', status ) + if( colsize .ne. 12 ) then + call stopit( status, 'FitsTable error 13h' ) + else + call psx_malloc( colsize, pntr, status ) + call ast_getcolumndata( table, 'INTCOL', 0.0, 0.0D0, colsize, + : %val( cnf_pval( pntr ) ), colsize, + : status ) + if( colsize .ne. 3 ) call stopit( status, + : 'FitsTable error 13i' ) + call checkints( table, %val( CNF_PVAL( pntr ) ), + : ast_columnnull( table, 'INTCOL', .FALSE., 0, + : wasset, hasnull, status ), + : status ) + + colsize = 12 + call ast_putcolumndata( table2, 'INTCol', 0, colsize, + : %val( CNF_PVAL( pntr ) ), status ) + + call ast_getcolumndata( table2, 'INTCOL', 0.0, 0.0D0, colsize, + : %val( cnf_pval( pntr ) ), colsize, + : status ) + if( colsize .ne. 3 ) call stopit( status, + : 'FitsTable error 13j' ) + call checkints( table2, %val( CNF_PVAL( pntr ) ), + : ast_columnnull( table2, 'INTCOL', .FALSE., 0, + : wasset, hasnull, status ), + : status ) + + call psx_free( pntr, status ) + end if + + + call ast_addcolumn( table, 'REALCOL', AST__FLOATTYPE, 0, 0, ' ', + : status ) + call ast_addcolumn( table2, 'REALCOL', AST__FLOATTYPE, 0, 0, ' ', + : status ) + call ast_mapput0r( table, 'REALCOL(1)', -10.0, ' ', status ) + call ast_mapput0r( table, 'REALCOL(3)', 10.0, ' ', status ) + + colsize = ast_columnsize( table, 'realcol', status ) + if( colsize .ne. 12 ) then + call stopit( status, 'FitsTable error 13k' ) + else + call psx_malloc( colsize, pntr, status ) + call ast_getcolumndata( table, 'REALCOL', -1.0, 0.0D0, colsize, + : %val( cnf_pval( pntr ) ), colsize, + : status ) + if( colsize .ne. 3 ) call stopit( status, + : 'FitsTable error 13l' ) + call checkreals( table, %val( CNF_PVAL( pntr ) ), -1.0, + : status ) + + colsize = 12 + call ast_putcolumndata( table2, 'realCol', 0, colsize, + : %val( CNF_PVAL( pntr ) ), status ) + + + call ast_mapremove( table2, 'REALCOL(2)', status ) + call ast_getcolumndata( table2, 'REALCOL', AST__NANR, 0.0D0, + : colsize, + : %val( cnf_pval( pntr ) ), colsize, + : status ) + if( colsize .ne. 3 ) call stopit( status, + : 'FitsTable error 13m' ) + call checkreals( table2, %val( CNF_PVAL( pntr ) ), AST__NANR, + : status ) + + call psx_free( pntr, status ) + end if + + call ast_removecolumn( table, 'REALCOL', status ) + + call ast_mapremove( table, 'BYTECOL(3)', status ) + call ast_mapremove( table, 'INTCOL(3)', status ) + call ast_mapremove( table, 'STRINGCOL(3)', status ) + + if( ast_geti( table, 'Nrow', status ) .ne. 3 ) then + call stopit( status, 'FitsTable error 14' ) + endif + + if( ast_geti( table, 'Ncolumn', status ) .ne. 3 ) then + call stopit( status, 'FitsTable error 15' ) + endif + + + + + header = ast_gettableheader( table, status ) + icard = 0 + do while( ast_findfits( header, '%f', card, .true., status ) ) + icard = icard + 1 + if( icard .gt. 20 ) then + call stopit( status, 'FitsTable error 16' ) + else if( card .ne. header2( icard ) ) then + call stopit( status, 'FitsTable error 17' ) + end if + end do + call ast_annul( header, status ) + if( icard .ne. 20 ) call stopit( status, 'FitsTable error 18' ) + + + + if( ast_columnnull( table, 'BYTECOL', .FALSE., 0, wasset, + : hasnull, status ) .ne. 254 ) then + call stopit( status, 'FitsTable error 19' ) + else if( wasset ) then + call stopit( status, 'FitsTable error 20' ) + else if( .not. hasnull ) then + call stopit( status, 'FitsTable error 21' ) + end if + + + + call ast_purgerows( table, status ) + if( ast_geti( table, 'Nrow', status ) .ne. 2 ) then + call stopit( status, 'FitsTable error 22' ) + endif + + if( ast_geti( table, 'Ncolumn', status ) .ne. 3 ) then + call stopit( status, 'FitsTable error 23' ) + endif + + header = ast_gettableheader( table, status ) + if( ast_getfitsi( header, 'TNULL1', ival, status ) ) then + call stopit( status, 'FitsTable error 24' ) + endif + call ast_annul( header, status ) + + if( ast_columnnull( table, 'BYTECOL', .TRUE., 11, wasset, + : hasnull, status ) .ne. 11 ) then + call stopit( status, 'FitsTable error 25' ) + else if( wasset ) then + call stopit( status, 'FitsTable error 26' ) + else if( hasnull ) then + call stopit( status, 'FitsTable error 27' ) + end if + + if( ast_columnnull( table, 'BYTECOL', .FALSE., 0, wasset, + : hasnull, status ) .ne. 11 ) then + call stopit( status, 'FitsTable error 28' ) + else if( .not. wasset ) then + call stopit( status, 'FitsTable error 29' ) + else if( hasnull ) then + call stopit( status, 'FitsTable error 30' ) + end if + + + table2 = ast_copy( table, status ) + + call ast_end( status ) + call err_rlse( status ) + +c call ast_activememory( 'testfitstable' ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All FitsTable tests passed' + else + write(*,*) 'FitsTable tests failed' + end if + + end + + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + end + + + + subroutine checkbytes( table, vals, null, status ) + implicit none + include 'SAE_PAR' + integer status, table, null, i + byte vals( * ), ans( 12 ), bnull + + data ans / 0, 1, 128, 127, -127, -1, 0, 1, 0, 1, 0, 1 / + + if( status .ne. sai__ok ) return + + do i = 1, 12 + if( vals( i ) .ne. ans( i ) ) then + write(*,*) 'i,vals,ans: ',i,' ',vals(i),' ',ans(i) + call stopit( status, 'FitsTable error checkbytes 1' ) + end if + end do + + bnull = null + do i = 13, 18 + if( vals( i ) .ne. bnull ) then + call stopit( status, 'FitsTable error checkbytes 2' ) + end if + end do + + end + + subroutine checkints( table, vals, null, status ) + implicit none + include 'SAE_PAR' + integer status, table, null + integer vals( * ) + + if( status .ne. sai__ok ) return + + if( vals( 1 ) .ne. null ) then + call stopit( status, 'FitsTable error checkints 1' ) + end if + + if( vals( 2 ) .ne. 10 ) then + call stopit( status, 'FitsTable error checkints 2' ) + end if + + if( vals( 3 ) .ne. -10 ) then + call stopit( status, 'FitsTable error checkints 3' ) + end if + + end + + subroutine checkreals( table, vals, null, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, table + real vals( * ), null + + if( status .ne. sai__ok ) return + + if( vals( 1 ) .ne. -10.0 ) then + call stopit( status, 'FitsTable error checkreals 1' ) + end if + + if( null .ne. AST__NANR ) then + if( vals( 2 ) .ne. null ) then + call stopit( status, 'FitsTable error checkreals 2a' ) + end if + else + if( .not. isnan( vals( 2 ) ) ) then + call stopit( status, 'FitsTable error checkreals 2b' ) + end if + end if + + if( vals( 3 ) .ne. 10.0 ) then + call stopit( status, 'FitsTable error checkreals 3' ) + end if + + end + + subroutine checkstrings( table, vals, status ) + implicit none + include 'SAE_PAR' + integer status, table, i, start, end, j + character ans( 9 )*10 + character vals*( * ) + + data ans / 'hello', ' ', 'goodbye', '', '', '', ' ', ' ', ' ' / + + if( status .ne. sai__ok ) return + + start = 1 + end = 10 + + do i = 1, 9 + + do j = 1, 11 + if( vals( start + j - 1 : start + j - 1 ) .lt. ' ' ) then + vals( start + j - 1 : start + j - 1 ) = ' ' + endif + end do + + if( vals( start : end ) .ne. ans( i ) ) then + write(*,*) 'start,end,i : ',start,' ',end,' ',i + write(*,*) 'vals: ',vals( start : end ) + write(*,*) 'ans: ',ans( i ) + call stopit( status, 'FitsTable error checkstrings 1' ) + end if + + start = start + 10 + end = end + 10 + + end do + + end + + + + diff --git a/ast_tester/testflux.f b/ast_tester/testflux.f new file mode 100644 index 0000000..3ac240f --- /dev/null +++ b/ast_tester/testflux.f @@ -0,0 +1,353 @@ + program testflux + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + double precision xin, xout + integer status, sf, ff, ff2, mp, fs, sf2 + status = sai__ok + + sf = ast_specframe( 'system=freq,unit=GHz', status ) + ff = ast_Fluxframe( 123.0D0, sf, ' ', status ) + + if( ast_GetD( ff, 'specval', status ) .ne. 123.0D0 ) then + call stopit( status, 'Error 1' ) + end if + + if( ast_Test( ff, 'specval', status ) ) then + call stopit( status, 'Error 2' ) + end if + + call ast_setd( ff, 'specval', 333.3D0, status ) + if( ast_GetD( ff, 'specval', status ) .ne. 333.3D0 ) then + call stopit( status, 'Error 3' ) + end if + + if( .not. ast_Test( ff, 'specval', status ) ) then + call stopit( status, 'Error 4' ) + end if + + call ast_clear( ff, 'specval', status ) + + if( ast_GetD( ff, 'specval', status ) .ne. 123.0D0 ) then + call stopit( status, 'Error 5' ) + end if + + if( ast_Test( ff, 'specval', status ) ) then + call stopit( status, 'Error 6' ) + end if + + + call checkDump( ff, 'CheckDump 1', status ) + + + ff2 = ast_Fluxframe( 123.1D0, sf, ' ', status ) + fs = ast_convert( ff, ff2, ' ', status ) + if( fs .eq. ast__null ) then + call stopit( status, 'error 8' ) + else + mp = ast_getmapping( fs, AST__BASE, AST__CURRENT, status ) + if( .not. ast_isaunitmap( mp, status ) ) then + call stopit( status, 'error 9' ) + end if + end if + + + + + + ff = ast_Fluxframe( 123.0D0, sf, 'unit=W/m^2/Hz', status ) + if( ast_GetC( ff, 'System', status ) .ne. 'FLXDN' ) then + write(*,*) ast_GetC( ff, 'System', status ) + call stopit( status, 'error 10' ) + endif + + ff2 = ast_Fluxframe( 123.0D0, sf, 'unit=W/m^2/GHz', status ) + if( ast_GetC( ff2, 'System', status ) .ne. 'FLXDN' ) then + write(*,*) ast_GetC( ff2, 'System', status ) + call stopit( status, 'error 11' ) + endif + + fs = ast_convert( ff2, ff, ' ', status ) + if( fs .eq. ast__null ) then + call stopit( status, 'error 12' ) + else + mp = ast_getmapping( fs, AST__BASE, AST__CURRENT, status ) + if( .not. ast_isazoommap( mp, status ) ) then + call stopit( status, 'error 13' ) + else if( ast_getd( mp, 'Zoom', status ) .ne. 1.0D-9 ) then + write(*,*) ast_getd( mp, 'Zoom', status ) + call stopit( status, 'error 14' ) + end if + end if + + + ff = ast_Fluxframe( 123.0D0, sf, 'unit=W/m^2/m', status ) + if( ast_GetC( ff, 'System', status ) .ne. 'FLXDNW' ) then + write(*,*) ast_GetC( ff, 'System', status ) + call stopit( status, 'error 15' ) + endif + + sf2 = ast_specframe( 'system=freq,unit=Hz', status ) + ff2 = ast_Fluxframe( 123.0D9, sf2, 'unit=W/m^2/Angstrom', status ) + if( ast_GetC( ff2, 'System', status ) .ne. 'FLXDNW' ) then + write(*,*) ast_GetC( ff2, 'System', status ) + call stopit( status, 'error 16' ) + endif + + fs = ast_convert( ff2, ff, ' ', status ) + if( fs .eq. ast__null ) then + call stopit( status, 'error 17' ) + else + mp = ast_getmapping( fs, AST__BASE, AST__CURRENT, status ) + if( .not. ast_isazoommap( mp, status ) ) then + call stopit( status, 'error 18' ) + else if( ast_getd( mp, 'Zoom', status ) .ne. 1.0D10 ) then + write(*,*) ast_getd( mp, 'Zoom', status ) + call stopit( status, 'error 19' ) + end if + end if + + + + + ff = ast_Fluxframe( 123.0D0, sf, 'unit=W/m^2/m', status ) + if( ast_GetC( ff, 'System', status ) .ne. 'FLXDNW' ) then + write(*,*) ast_GetC( ff, 'System', status ) + call stopit( status, 'error 20' ) + endif + + sf2 = ast_specframe( 'system=wave,unit=nm', status ) + ff2 = ast_Fluxframe( 2437337.06D0, sf2, 'unit=W/m^2/Angstrom', + : status ) + if( ast_GetC( ff2, 'System', status ) .ne. 'FLXDNW' ) then + write(*,*) ast_GetC( ff2, 'System', status ) + call stopit( status, 'error 21' ) + endif + + fs = ast_convert( ff, ff2, ' ', status ) + if( fs .eq. ast__null ) then + call stopit( status, 'error 22' ) + else + mp = ast_getmapping( fs, AST__BASE, AST__CURRENT, status ) + if( .not. ast_isazoommap( mp, status ) ) then + call stopit( status, 'error 23' ) + else if( ast_getd( mp, 'Zoom', status ) .ne. 1.0D-10 ) then + write(*,*) ast_getd( mp, 'Zoom', status ) + call stopit( status, 'error 24' ) + end if + end if + + + sf = ast_specframe( 'system=freq,unit=GHz', status ) + ff = ast_Fluxframe( 123.0D0, sf, 'unit=W/m^2/Hz', status ) + sf2 = ast_specframe( 'system=wave,unit=nm', status ) + ff2 = ast_Fluxframe( 2437337.06D0, sf2, 'unit=W/m^2/m', + : status ) + fs = ast_convert( ff, ff2, ' ', status ) + if( fs .eq. ast__null ) then + call stopit( status, 'error 25' ) + else + xin = 1.0D-13 + call ast_tran1( fs, 1,xin, 1,xout, status ) + if( abs( xout - 5.04649119D0 ) .gt. 1.0D-6 ) then + call stopit( status, 'error 26' ) + end if + end if + + + sf = ast_specframe( 'system=freq,unit=GHz', status ) + ff = ast_Fluxframe( 123.0D0, sf, 'unit=W/m^2/Hz/arcsec**2', + : status ) + if( ast_getc( ff, 'System', status ) .ne. 'SFCBR' ) + : call stopit( status, 'error 27a' ) + + sf2 = ast_specframe( 'system=wave,unit=nm', status ) + ff2 = ast_Fluxframe( 2437337.06D0, sf2, 'unit=W/m^2/m/deg**2', + : status ) + if( ast_getc( ff2, 'System', status ) .ne. 'SFCBRW' ) + : call stopit( status, 'error 27b' ) + + fs = ast_convert( ff, ff2, ' ', status ) + if( fs .eq. ast__null ) then + call stopit( status, 'error 27' ) + else + xin = 1.0D-13 + call ast_tran1( fs, 1,xin, 1,xout, status ) + if( abs( xout - 65402525.8D0 ) .gt. 1.0 ) then + write(*,*) xout - 65402525.8D0 + call stopit( status, 'error 28' ) + end if + end if + + + ff = ast_Fluxframe( 123.0D0, sf, 'unit=W/m^2/Hz/arcsec**2', + : status ) + ff2 = ast_Fluxframe( 2437337.06D0, sf2, 'unit=W/m^2/m', + : status ) + + fs = ast_convert( ff, ff2, ' ', status ) + if( fs .ne. ast__null ) call stopit( status, 'error 29' ) + + + + + + + + + + + + + + + + if( status .eq. sai__ok ) then + write(*,*) 'All FluxFrame tests passed' + else + write(*,*) 'FluxFrame tests failed' + end if + + end + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + + subroutine checkdump( obj, text, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap + external mysource, mysink + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + ch = ast_channel( mysource, mysink, ' ', status ) + + + ll = 110 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + + + + if( ast_getd( obj, 'specval', status ) .ne. + : ast_getd( result, 'specval', status ) ) then + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + + end + + subroutine sink1( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + logical fsfound, done + common /sink1com/ fsfound, done + + integer status, l + character line*200 + + if( status .ne. sai__ok ) return + call ast_getline( line, l, status ) + + if( index( line( : l ),'Unc =' ) .GT. 0 ) then + done = .true. + + else if( .not. done .and. + : index( line( : l ),'FrameSet' ) .GT. 0 ) then + fsfound= .true. + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf*25000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 25000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + endif + + next = next + ll + + end + + diff --git a/ast_tester/testframeset.f b/ast_tester/testframeset.f new file mode 100644 index 0000000..0c9c52e --- /dev/null +++ b/ast_tester/testframeset.f @@ -0,0 +1,391 @@ + program testframeset + implicit none + + include 'AST_PAR' + include 'AST_ERR' + include 'SAE_PAR' + + integer status, pfrm, ffrm, p2fmap, fs, p2fmap2, result, orig + double precision ina(2), inb(2), outa(2), outb(2), xout, yout + character text*100 + +c call ast_watchmemory(100) + + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + + + pfrm = ast_frame( 2, "Domain=PIXEL", status ) + ffrm = ast_frame( 2, "Domain=FPLANE", status ) + + ina( 1 ) = 1.0 + ina( 2 ) = 1.0 + inb( 1 ) = 100.0 + inb( 2 ) = 200.0 + + outa( 1 ) = -2.5 + outa( 2 ) = -1.0 + outb( 1 ) = 2.5 + outb( 2 ) = 1.0 + p2fmap = ast_winmap( 2, ina, inb, outa, outb, ' ', status ) + + fs = ast_frameset( pfrm, ' ', status ) + call ast_addframe( fs, AST__CURRENT, p2fmap, ffrm, status ) + + call ast_setc( fs, 'Base', 'Fplane', status ) + if( ast_geti( fs, 'Base', status ) .ne. 2 ) + : call stopit( status, 'Error -3' ) + + call ast_setc( fs, 'Base', 'pixel', status ) + if( ast_geti( fs, 'Base', status ) .ne. 1 ) + : call stopit( status, 'Error -2' ) + + call ast_setc( fs, 'Current', 'PIXEL', status ) + if( ast_geti( fs, 'Current', status ) .ne. 1 ) + : call stopit( status, 'Error -1' ) + + call ast_setc( fs, 'Current', 'fplane', status ) + if( ast_geti( fs, 'Current', status ) .ne. 2 ) + : call stopit( status, 'Error 0' ) + + text = ast_getc( fs, 'AllVariants', status ) + if( text .ne. 'FPLANE' ) call stopit( status, 'Error 1' ) + + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'FPLANE' ) call stopit( status, 'Error 2' ) + + if( ast_test( fs, 'Variant', status ) ) call stopit( status, + : 'Error 3' ) + + + call ast_addvariant( FS, ast__null, 'FP1', status ) + + text = ast_getc( fs, 'AllVariants', status ) + if( text .ne. 'FP1' ) call stopit( status, 'Error 4' ) + + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'FP1' ) call stopit( status, 'Error 5' ) + + if( .not. ast_test( fs, 'Variant', status ) ) call stopit( status, + : 'Error 6' ) + + call ast_clear( fs, 'Variant', status ) + + text = ast_getc( fs, 'AllVariants', status ) + if( text .ne. 'FPLANE' ) call stopit( status, 'Error 7' ) + + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'FPLANE' ) call stopit( status, 'Error 8' ) + + if( ast_test( fs, 'Variant', status ) ) call stopit( status, + : 'Error 9' ) + + call ast_addvariant( FS, ast__null, 'FP1', status ) + + outa( 1 ) = 100.0 + outa( 2 ) = 100.0 + outb( 1 ) = 200.0 + outb( 2 ) = 200.0 + p2fmap2 = ast_winmap( 2, ina, inb, outa, outb, ' ', status ) + + call ast_invert( p2fmap, status ) + call ast_addvariant( fs, ast_simplify( + : ast_cmpmap( p2fmap, p2fmap2, 1, ' ', + : status ), status ), + : 'FP2', status ) + + text = ast_getc( fs, 'AllVariants', status ) + if( text .ne. 'FP1 FP2' ) call stopit( status, 'Error 10' ) + + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'FP2' ) call stopit( status, 'Error 11' ) + + if( .not. ast_test( fs, 'Variant', status ) ) call stopit( status, + : 'Error 12' ) + call ast_tran2( fs, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 150.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 150.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 13' ) + + call ast_setc( fs, 'Variant', 'FP1', status ) + call ast_tran2( fs, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 0.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 0.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 14' ) + + outa( 1 ) = -100.0 + outa( 2 ) = -100.0 + outb( 1 ) = -200.0 + outb( 2 ) = -200.0 + p2fmap2 = ast_winmap( 2, ina, inb, outa, outb, ' ', status ) + + p2fmap = ast_getmapping( fs, AST__CURRENT, AST__BASE, status ) + call ast_addvariant( fs, ast_simplify( + : ast_cmpmap( p2fmap, p2fmap2, 1, ' ', + : status ), status ), + : 'FP3', status ) + + text = ast_getc( fs, 'AllVariants', status ) + if( text .ne. 'FP1 FP2 FP3' ) call stopit( status, 'Error 15' ) + + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'FP3' ) call stopit( status, 'Error 16' ) + + if( .not. ast_test( fs, 'Variant', status ) ) call stopit( status, + : 'Error 17' ) + call ast_tran2( fs, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout + 150.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout + 150.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 18' ) + + call ast_setc( fs, 'Variant', 'FP2', status ) + call ast_tran2( fs, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 150.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 150.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 19' ) + + call checkdump( fs, result, status ) + + text = ast_getc( result, 'AllVariants', status ) + if( text .ne. 'FP1 FP2 FP3' ) call stopit( status, 'Error 20' ) + + text = ast_getc( result, 'Variant', status ) + if( text .ne. 'FP2' ) call stopit( status, 'Error 21' ) + + call ast_tran2( result, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 150.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 150.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 22' ) + + + + orig = ast_geti( fs, 'Current', status ) + call ast_addframe( fs, AST__CURRENT, AST_UNITMAP( 2, '', status ), + : AST_FRAME( 2, "Domain=DSB", status ), status ) + call ast_tran2( fs, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 150.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 150.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 23' ) + + if( status .eq. sai__ok ) then + call ast_setc( fs, 'Variant', 'FP1', status ) + if( status .eq. ast__attin ) then + call err_annul( status ) + else + call err_flush( status ) + call stopit( status, 'Error 24' ) + end if + end if + + text = ast_getc( fs, 'AllVariants', status ) + if( text .ne. 'DSB' ) call stopit( status, 'Error 25' ) + + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'DSB' ) call stopit( status, 'Error 26' ) + + if( ast_test( fs, 'Variant', status ) ) call stopit( status, + : 'Error 27' ) + + call ast_mirrorvariants( fs, orig, status ) + + text = ast_getc( fs, 'AllVariants', status ) + if( text .ne. 'FP1 FP2 FP3' ) call stopit( status, 'Error 28' ) + + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'FP2' ) call stopit( status, 'Error 29' ) + + if( .not. ast_test( fs, 'Variant', status ) ) call stopit( status, + : 'Error 30' ) + + call ast_tran2( fs, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 150.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 150.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 31' ) + + call ast_set( fs, 'Variant=FP1', status ) + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'FP1' ) call stopit( status, 'Error 32' ) + + call ast_tran2( fs, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 0.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 0.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 33' ) + + call checkdump( fs, result, status ) + + text = ast_getc( result, 'AllVariants', status ) + if( text .ne. 'FP1 FP2 FP3' ) call stopit( status, 'Error 34' ) + + text = ast_getc( result, 'Variant', status ) + if( text .ne. 'FP1' ) call stopit( status, 'Error 35' ) + + call ast_tran2( result, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 0.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 0.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 36' ) + + result = ast_copy( fs, status ) + + text = ast_getc( result, 'AllVariants', status ) + if( text .ne. 'FP1 FP2 FP3' ) call stopit( status, 'Error 37' ) + + text = ast_getc( result, 'Variant', status ) + if( text .ne. 'FP1' ) call stopit( status, 'Error 38' ) + + call ast_tran2( result, 1, 50.5D0, 100.5D0, .TRUE., xout, yout, + : status ) + if( abs( xout - 0.0D0 ) .gt. 1.0E-6 .OR. + : abs( yout - 0.0D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Error 39' ) + + call ast_clear( fs, 'Variant', status ) + text = ast_getc( fs, 'Variant', status ) + if( text .ne. 'DSB' ) call stopit( status, 'Error 40' ) + + + + + + + call ast_end( status ) + call err_rlse( status ) + + call ast_activememory( 'testframeset' ) + call ast_flushmemory( 1 ); + + if( status .eq. sai__ok ) then + write(*,*) 'All FrameSet tests passed' + else + write(*,*) 'FrameSet tests failed' + end if + + end + + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + subroutine checkdump( obj, result, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character key*30,txt1*50,txt2*50 + integer obj, status, next, end, ch, result, ll, overlap, size, + : i, type,obj1,obj2,l1,l2,nl,nrow,nrowold + external mysource, mysink + character buf*400000 + + common /ss1/ buf + common /ss2/ next, end, ll, nl + + if( status .ne. sai__ok ) return + + ch = ast_channel( mysource, mysink, ' ', status ) + + nl = 0 + ll = 110 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + next = 1 + nl = 0 + result = ast_read( ch, status ) + + if( result .eq. ast__null ) then + call stopit( status, 'Cannot read object from channel' ) + end if + + if( .not. ast_isaframeset( result, status ) ) then + call stopit( status, 'Object read from channel is not a '// + : 'FrameSet') + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll, nl + character buf*400000 + + common /ss1/ buf + common /ss2/ next, end, ll,nl + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + +c write(*,*) buf( next : next + ll - 1 ) + call ast_putline( buf( next : ), ll, status ) + nl = nl + 1 + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll, nl + character buf*400000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll, nl + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 400000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + nl = nl + 1 + endif + + next = next + ll + + end + + diff --git a/ast_tester/testkeymap.f b/ast_tester/testkeymap.f new file mode 100644 index 0000000..4ce64c0 --- /dev/null +++ b/ast_tester/testkeymap.f @@ -0,0 +1,1350 @@ + program testkeymap + implicit none + include 'AST_PAR' + include 'AST_ERR' + include 'SAE_PAR' + integer status,map,map2,ival,aval,l,ivec(2),avec(4),nval,i,iat, + : map1, map3, km2 + character cval*20,cvec(3)*10,key*20,cval0*40 + double precision dval, dvec(2) + logical gota, gotc, gotd, goti, gotr, gotw, lval + real rval + integer*2 sval,svec(2) + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + +c call ast_watchmemory( 29286 ) + + call testcasesens( status ) + call testsorting( status ) + + map = ast_keymap( ' ', status ) + + call ast_MapPut0s( map, 'Freds', 1999, 'com 1', status ) + call ast_MapPut0i( map, 'Fredi', 1999, 'com 1', status ) + call ast_MapPut0d( map, 'Fredd', 1999.9D0, 'com2 ', status ) + call ast_MapPut0r( map, 'Fredr', 1999.9, 'com2 ', status ) + call ast_MapPut0c( map, 'Fredc', 'Hello', ' ', status ) + call ast_MapPut0A( map, 'Freda', ast_skyframe( ' ', status ), + : ' ', status ) + + if( .not. ast_mapdefined( map, 'Freda', status ) ) then + call stopit( status, 'Error -12' ) + end if + + if( ast_maplenc( map, 'Fredi', status ) .ne. 4 ) then + write(*,*) ast_maplenc( map, 'Fredi', status ) + call stopit( status, 'Error -11' ) + end if + + if( ast_maplenc( map, 'Freda', status ) .ne. 0 ) then + write(*,*) ast_maplenc( map, 'Freda', status ) + call stopit( status, 'Error -10' ) + end if + + if( ast_maplenc( map, 'Fredc', status ) .ne. 5 ) then + write(*,*) ast_maplenc( map, 'Fredc', status ) + call stopit( status, 'Error -9' ) + end if + + if( ast_maptype( map, 'freda', status ) .ne. AST__BADTYPE) then + call stopit( status, 'Error -8' ) + end if + + if( ast_maptype( map, 'Freda', status ) .ne. AST__OBJECTTYPE) then + call stopit( status, 'Error -7' ) + end if + + if( ast_maptype( map, 'Fredc', status ) .ne. AST__STRINGTYPE) then + call stopit( status, 'Error -6' ) + end if + + if( ast_maptype( map, 'Fredd', status ) .ne. AST__DOUBLETYPE) then + call stopit( status, 'Error -5' ) + end if + + if( ast_maptype( map, 'Fredr', status ) .ne. AST__FLOATTYPE) then + call stopit( status, 'Error -5b' ) + end if + + if( ast_maptype( map, 'Fredi', status ) .ne. AST__INTTYPE ) then + call stopit( status, 'Error -4' ) + end if + + if( ast_maphaskey( map, 'fredi', status ) ) then + call stopit( status, 'Error -3' ) + end if + + if( .not. ast_maphaskey( map, 'Fredi', status ) ) then + call stopit( status, 'Error -2' ) + end if + + map2 = ast_copy( map, status ) + + + if( ast_mapsize( map2, status ) .ne. 6 ) then + write(*,*) ast_mapsize( map2, status ) + call stopit( status, 'Error 0' ) + end if + + goti = .false. + gotd = .false. + gotr = .false. + gotc = .false. + gota = .false. + gotw = .false. + + do i = 1, ast_mapsize( map2, status ) + key = ast_mapkey( map2, i, status ) + if( .not. goti .and. key .eq. 'Fredi' ) then + goti = .true. + else if( .not. gotd .and. key .eq. 'Fredd' ) then + gotd = .true. + else if( .not. gotw .and. key .eq. 'Freds' ) then + gotw = .true. + else if( .not. gotr .and. key .eq. 'Fredr' ) then + gotr = .true. + else if( .not. gotc .and. key .eq. 'Fredc' ) then + gotc = .true. + else if( .not. gota .and. key .eq. 'Freda' ) then + gota = .true. + else + call stopit( status, 'Error badkey' ) + endif + end do + + if( .not. ( goti .AND. gotd .AND. gotc + : .AND. gota .and. gotr .and. gotw) ) then + call stopit( status, 'Error nokey' ) + endif + + if( ast_maplength( map2, 'Fredi', status ) .ne. 1 ) then + write(*,*) ast_maplength( map2, 'Fredi', status ) + call stopit( status, 'Error -1' ) + end if + + if( .not. ast_mapget0i( map2, 'Fredi', ival, status ) ) then + call stopit( status, 'Error 1' ) + else if( ival .ne. 1999 ) then + write(*,*) ival + call stopit( status, 'Error 2' ) + end if + + if( .not. ast_mapget0s( map2, 'Freds', sval, status ) ) then + call stopit( status, 'Error 1' ) + else if( sval .ne. 1999 ) then + write(*,*) ival + call stopit( status, 'Error 2B' ) + end if + + if( .not. ast_mapget0d( map2, 'Fredd', dval, status ) ) then + call stopit( status, 'Error 3' ) + else if( dval .ne. 1999.9D0 ) then + write(*,*) dval - 1999.9D0 + call stopit( status, 'Error 4' ) + end if + + if( .not. ast_mapget0r( map2, 'Fredr', rval, status ) ) then + call stopit( status, 'Error 3b' ) + else if( rval .ne. 1999.9 ) then + write(*,*) rval - 1999.9 + call stopit( status, 'Error 4b' ) + end if + + if( .not. ast_mapget0c( map2, 'Fredc', cval, l, status ) ) then + call stopit( status, 'Error 5' ) + else if( l .ne. 5 ) then + write(*,*) l + call stopit( status, 'Error 6' ) + else if( cval( :l ) .ne. 'Hello' ) then + write(*,*) cval( :l ) + call stopit( status, 'Error 7' ) + end if + + if( .not. ast_mapget0a( map2, 'Freda', aval, status ) ) then + call stopit( status, 'Error 8' ) + else if( .not. ast_IsASkyFrame( aval, STATUS ) ) then + call stopit( status, 'Error 9' ) + end if + + if( .not. ast_mapget0d( map2, 'Fredi', dval, status ) ) then + call stopit( status, 'Error 10' ) + else if( dval .ne. 1999 ) then + write(*,*) dval + call stopit( status, 'Error 11' ) + end if + + if( .not. ast_mapget0r( map2, 'Fredi', rval, status ) ) then + call stopit( status, 'Error 10b' ) + else if( rval .ne. 1999 ) then + call stopit( status, 'Error 11b' ) + end if + + if( .not. ast_mapget0c( map2, 'Fredi', cval, l, status ) ) then + call stopit( status, 'Error 12' ) + else if( l .ne. 4 ) then + write(*,*) l + call stopit( status, 'Error 13a' ) + else if( cval( :l ) .ne. '1999' ) then + write(*,*) cval + call stopit( status, 'Error 13' ) + end if + + if( .not. ast_mapget0i( map2, 'Fredd', ival, status ) ) then + call stopit( status, 'Error 14' ) + else if( ival .ne. 2000.0 ) then + write(*,*) ival + call stopit( status, 'Error 15' ) + end if + + if( .not. ast_mapget0s( map2, 'Fredd', sval, status ) ) then + call stopit( status, 'Error 14b' ) + else if( sval .ne. 2000.0 ) then + write(*,*) sval + call stopit( status, 'Error 15b' ) + end if + + if( .not. ast_mapget0c( map2, 'Fredd', cval, l, status ) ) then + call stopit( status, 'Error 16' ) + else if( l .ne. 6 ) then + write(*,*) l + call stopit( status, 'Error 17a' ) + else if( cval( :l ) .ne. '1999.9' ) then + write(*,*) cval + call stopit( status, 'Error 17' ) + end if + + + ivec(1) = -10 + ivec(2) = -10 + if( .not. ast_mapget1i( map2, 'Fredi', 2, nval, ivec, + : status ) ) then + call stopit( status, 'Error 18' ) + else if( nval .ne. 1 ) then + write(*,*) nval + call stopit( status, 'Error 19' ) + else if( ivec( 1 ) .ne. 1999 ) then + write(*,*) ivec( 1 ) + call stopit( status, 'Error 20' ) + else if( ivec( 2 ) .ne. -10 ) then + write(*,*) ivec( 2 ) + call stopit( status, 'Error 21' ) + end if + + + dvec(1) = -10.0D0 + dvec(2) = -10.0D0 + if( .not. ast_mapget1d( map2, 'Fredd', 2, nval, dvec, + : status ) ) then + call stopit( status, 'Error 22' ) + else if( nval .ne. 1 ) then + write(*,*) nval + call stopit( status, 'Error 23' ) + else if( dvec( 1 ) .ne. 1999.9D0 ) then + write(*,*) dvec( 1 ) + call stopit( status, 'Error 24' ) + else if( dvec( 2 ) .ne. -10.0D0 ) then + write(*,*) dvec( 2 ) + call stopit( status, 'Error 25' ) + end if + + avec(1) = AST__NULL + avec(2) = AST__NULL + if( .not. ast_mapget1a( map2, 'Freda', 2, nval, avec, + : status ) ) then + call stopit( status, 'Error 26' ) + else if( nval .ne. 1 ) then + write(*,*) nval + call stopit( status, 'Error 27' ) + else if( .not. ast_IsASkyFrame( avec( 1 ), STATUS ) ) then + write(*,*) ast_getc( avec( 1 ), 'class', status ) + call stopit( status, 'Error 28' ) + else if( avec( 2 ) .ne. AST__NULL ) then + write(*,*) ast_getc( avec( 2 ), 'class', status ) + call stopit( status, 'Error 29' ) + end if + + + ivec(1)=1999 + ivec(2)=0 + call ast_mapput1i( map, 'Fredi', 2, ivec, 'com 1', STATUS ) + + if( ast_maplength( map, 'Fredi', status ) .ne. 2 ) then + write(*,*) ast_maplength( map, 'Fredi', status ) + call stopit( status, 'Error 29b' ) + + end if + + svec(1)=1999 + svec(2)=0 + call ast_mapput1s( map, 'Freds', 2, svec, 'com 1', STATUS ) + + if( ast_maplength( map, 'Freds', status ) .ne. 2 ) then + write(*,*) ast_maplength( map, 'Freds', status ) + call stopit( status, 'Error 29c' ) + + end if + + dvec(1)=1999.9D0 + dvec(2)=-0.01D0 + call ast_mapput1d( map, 'Fredd', 2, dvec, 'com2', STATUS ) + + cvec(1)='Hello' + cvec(2)=' ' + cvec(3)=' Hello' + call ast_mapput1c( map, 'Fredc', 3, cvec, ' ', STATUS ) + + if( ast_maplenc( map, 'Fredc', status ) .ne. len(cvec(3)) ) then + write(*,*) ast_maplenc( map, 'Fredc', status ) + call stopit( status, 'Error 29c' ) + end if + + avec(1) = ast_skyframe( ' ', status ) + avec(2) = AST__NULL + avec(3) = ast_specframe( ' ', status ) + avec(4) = AST__NULL + call ast_mapput1a( map, 'Freda', 4, avec, ' ', STATUS ) + + map2 = ast_copy( map, status ) + + if( .not. ast_mapget0i( map2, 'Fredi', ival, status ) ) then + call stopit( status, 'Error A1' ) + else if( ival .ne. 1999 ) then + write(*,*) ival + call stopit( status, 'Error A2' ) + end if + + if( .not. ast_mapget0d( map2, 'Fredd', dval, status ) ) then + call stopit( status, 'Error A3' ) + else if( dval .ne. 1999.9D0 ) then + write(*,*) dval - 1999.9D0 + call stopit( status, 'Error A4' ) + end if + + if( .not. ast_mapget0c( map2, 'Fredc', cval, l, status ) ) then + call stopit( status, 'Error A5' ) + else if( l .ne. 10 ) then + write(*,*) l + call stopit( status, 'Error A6' ) + else if( cval( :l ) .ne. 'Hello ' ) then + write(*,*) cval( :l ) + call stopit( status, 'Error A7' ) + end if + + if( .not. ast_mapget0a( map2, 'Freda', aval, status ) ) then + call stopit( status, 'Error A8' ) + else if( .not. ast_IsASkyFrame( aval, STATUS ) ) then + call stopit( status, 'Error A9' ) + end if + + if( .not. ast_mapget0d( map2, 'Fredi', dval, status ) ) then + call stopit( status, 'Error A10' ) + else if( dval .ne. 1999 ) then + write(*,*) dval + call stopit( status, 'Error A11' ) + end if + + if( .not. ast_mapget0c( map2, 'Fredi', cval, l, status ) ) then + call stopit( status, 'Error A12' ) + else if( l .ne. 4 ) then + write(*,*) l + call stopit( status, 'Error A13a' ) + else if( cval( :l ) .ne. '1999' ) then + write(*,*) cval + call stopit( status, 'Error A13' ) + end if + + if( .not. ast_mapget0i( map2, 'Fredd', ival, status ) ) then + call stopit( status, 'Error A14' ) + else if( ival .ne. 2000.0 ) then + write(*,*) ival + call stopit( status, 'Error A15' ) + end if + + if( .not. ast_mapget0c( map2, 'Fredd', cval, l, status ) ) then + call stopit( status, 'Error A16' ) + else if( l .ne. 6 ) then + write(*,*) l + call stopit( status, 'Error A17a' ) + else if( cval( :l ) .ne. '1999.9' ) then + write(*,*) cval + call stopit( status, 'Error A17' ) + end if + + +c Read vector entries as vectors. + if( .not. ast_mapget1i( map2, 'Fredi', 2, nval, ivec, + : status ) ) then + call stopit( status, 'Error B1' ) + else if( nval .ne. 2 ) then + write(*,*) nval + call stopit( status, 'Error B2a' ) + else if( ivec( 1 ) .ne. 1999 ) then + write(*,*) ivec( 1 ) + call stopit( status, 'Error B2b' ) + else if( ivec( 2 ) .ne. 0 ) then + write(*,*) ivec( 2 ) + call stopit( status, 'Error B2c' ) + end if + + if( .not. ast_mapget1d( map2, 'Fredd', 2, nval, dvec, + : status ) ) then + call stopit( status, 'Error B3' ) + else if( nval .ne. 2 ) then + write(*,*) nval + call stopit( status, 'Error B4a' ) + else if( dvec( 1 ) .ne. 1999.9D0 ) then + write(*,*) dvec( 1 ) + call stopit( status, 'Error B4b' ) + else if( dvec( 2 ) .ne. -0.01D0 ) then + write(*,*) dvec( 2 ) + call stopit( status, 'Error B4c' ) + end if + + if( .not. ast_mapget1a( map2, 'Freda', 4, nval, avec, + : status ) ) then + call stopit( status, 'Error B5' ) + else if( nval .ne. 4 ) then + write(*,*) nval + call stopit( status, 'Error B6a' ) + else if( .not. ast_isaskyframe( avec( 1 ), status ) ) then + write(*,*) ast_getc( avec( 1 ), 'class', status ) + call stopit( status, 'Error B6b' ) + else if( avec( 2 ) .NE. AST__NULL ) then + write(*,*) ast_getc( avec( 2 ), 'class', status ) + call stopit( status, 'Error B6c' ) + else if( .not. ast_isaspecframe( avec( 3 ), status ) ) then + write(*,*) ast_getc( avec( 3 ), 'class', status ) + call stopit( status, 'Error B6d' ) + else if( avec( 4 ) .ne. AST__NULL ) then + write(*,*) ast_getc( avec( 4 ), 'class', status ) + call stopit( status, 'Error B6e' ) + end if + + + if( .not. ast_mapget1c( map2, 'Fredc', 3, nval, cvec, + : status ) ) then + call stopit( status, 'Error B7' ) + else if( nval .ne. 3 ) then + write(*,*) nval + call stopit( status, 'Error B8a' ) + else if( cvec( 1 ) .ne. 'Hello ' ) then + write(*,*) cvec( 1 ) + call stopit( status, 'Error B8b' ) + else if( cvec( 2 ) .ne. ' ' ) then + write(*,*) cvec( 2 ) + call stopit( status, 'Error B8c' ) + else if( cvec( 3 ) .ne. ' Hello ' ) then + write(*,*) cvec( 2 ) + call stopit( status, 'Error B8d' ) + end if + +c Read single elements of vector entries as scalars. + if( .not. ast_mapgetelemi( map2, 'Fredi', 1, ivec, + : status ) ) then + call stopit( status, 'Error B1z' ) + else if( ivec( 1 ) .ne. 1999 ) then + write(*,*) ivec( 1 ) + call stopit( status, 'Error B2bz' ) + end if + + if( .not. ast_mapgetelemd( map2, 'Fredd', 2, dvec, + : status ) ) then + call stopit( status, 'Error B3z' ) + else if( dvec( 1 ) .ne. -0.01D0 ) then + write(*,*) dvec( 1 ) + call stopit( status, 'Error B4cz' ) + end if + + if( .not. ast_mapgetelema( map2, 'Freda', 3, avec, + : status ) ) then + call stopit( status, 'Error B5z' ) + else if( .not. ast_isaspecframe( avec( 1 ), status ) ) then + write(*,*) ast_getc( avec( 1 ), 'class', status ) + call stopit( status, 'Error B6dz' ) + end if + + + if( .not. ast_mapgetelemc( map2, 'Fredc', 3, cval0, + : status ) ) then + call stopit( status, 'Error B7z' ) + else if( cval0 .ne. ' Hello ' ) then + write(*,*) cval0 + call stopit( status, 'Error B8dz' ) + end if + + + call ast_mapremove( map2, 'Bert', status ) + call ast_mapremove( map2, 'Fredc', status ) + if( ast_mapget1c( map2, 'Fredc', 3, nval, cvec, status ) ) then + call stopit( status, 'Error C1' ) + endif + + + call checkDump( map2, 'checkDump 1 ', status ) + + call ast_Annul( map, status ) + call ast_Annul( map2, status ) + + + map = ast_keymap( ' ', status ) + + do i = 1, 500 + key = 'Fred' + iat = 4 + call chr_puti( i, key, iat ) + call ast_MapPut0i( map, key, i, ' ', status ) + end do + + if( ast_mapsize( map, status ) .ne. 500 ) then + call stopit( status, 'Error d1 ' ) + end if + + if( ast_maptype( map, 'Fred123', status ) .ne. AST__INTTYPE ) then + call stopit( status, 'Error d2 ' ) + end if + + if( .not. ast_mapget0c( map, 'Fred489', cval, l, status ) ) then + call stopit( status, 'Error d2 ' ) + else if( cval( : l ) .ne. '489' ) then + call stopit( status, 'Error d3 ' ) + end if + + call checkDump( map, 'checkDump 2 ', status ) + + + +c Test putting single elements into vector entries. + map = ast_keymap( ' ', status ) + + ivec(1) = 1 + ivec(2) = 2 + call ast_mapput1i( map, 'Fredi', 2, ivec, 'com 1', STATUS ) + + call ast_mapputelemi( map, 'Fredi', 1, -1, STATUS ) + if( .not. ast_mapgetelemi( map, 'Fredi', 1, ival, + : status ) ) then + call stopit( status, 'Error GETELEM_1' ) + else if( ival .ne. -1 ) then + write(*,*) ival + call stopit( status, 'Error GETELEM_2' ) + end if + + call ast_mapputelemi( map, 'Fredi', 10, -2, STATUS ) + if( .not. ast_mapgetelemi( map, 'Fredi', 3, ival, + : status ) ) then + call stopit( status, 'Error GETELEM_3' ) + else if( ival .ne. -2 ) then + write(*,*) ival + call stopit( status, 'Error GETELEM_4' ) + end if + + call ast_mapputelemi( map, 'Fredi', 0, -3, STATUS ) + if( .not. ast_mapgetelemi( map, 'Fredi', 4, ival, + : status ) ) then + call stopit( status, 'Error GETELEM_5' ) + else if( ival .ne. -3 ) then + write(*,*) ival + call stopit( status, 'Error GETELEM_6' ) + end if + + if( ast_maplength( map, 'Fredi', status ) .ne. 4 ) then + write(*,*) ast_maplength( map, 'Fredi', status ) + call stopit( status, 'Error GETELEM_7' ) + end if + + map2 = ast_keymap( ' ', status ) + call ast_mapputelema( map2, 'A A', 1, map, STATUS ) + if( ast_maplength( map2, 'A A', status ) .ne. 1 ) then + write(*,*) ast_maplength( map, 'Fredi', status ) + call stopit( status, 'Error GETELEM_8' ) + end if + + if( .not. ast_mapgetelema( map2, 'A A', 1, map3, + : status ) ) then + call stopit( status, 'Error GETELEM_9' ) + else if( .not. ast_mapgetelemi( map3, 'Fredi', 4, ival, + : status ) ) then + call stopit( status, 'Error GETELEM_10' ) + else if( ival .ne. -3 ) then + write(*,*) ival + call stopit( status, 'Error GETELEM_11' ) + end if + + if( status .eq. sai__ok ) then + call ast_mapputelema( map2, 'A A', 1, map2, STATUS ) + if( status .eq. ast__kycir ) then + call err_annul( status ) + else + call stopit( status, 'Error GETELEM_12' ) + end if + end if + + call ast_mapput0c( map, ' B', 'Hello', ' ', status ) + + + call ast_setl( map, 'MapLocked', .TRUE., status ) + if( status .eq. sai__ok ) then + call ast_mapput0c( map, ' BZZ', 'Bye Bye', ' ', STATUS ) + if( status .eq. AST__BADKEY ) then + call err_annul( status ) + call ast_clear( map, 'maplocked', status ) + else + call stopit( status, 'Error GETELEM_12B' ) + end if + end if + + call ast_mapput0c( map, ' BZZ', 'Bye Bye', ' ', STATUS ) + km2 = ast_keymap( ' ', status ) + call ast_mapput0a( map, ' BZY', km2, ' ', STATUS ) + call ast_mapput0c( km2, ' BZZ', 'Bye Bye', ' ', STATUS ) + + call ast_setl( map, 'MapLocked', .TRUE., status ) + call ast_mapput0c( map, ' BZZ', 'You Bye', ' ', STATUS ) + call ast_mapput0c( km2, ' BZZ', 'You Bye', ' ', STATUS ) + if( status .eq. sai__ok ) then + call ast_mapput0c( km2, ' BZA', 'No Bye', ' ', STATUS ) + if( status .eq. AST__BADKEY ) then + call err_annul( status ) + call ast_clear( map, 'maplocked', status ) + call ast_mapput0c( km2, ' BZA', 'No Bye', ' ', STATUS ) + else + call stopit( status, 'Error GETELEM_12C' ) + end if + end if + + if( ast_getl( km2, 'KeyError', status ) ) then + call stopit( status, 'Error GETELEM_12D' ) + end if + + call ast_setl( map, 'KeyError', .TRUE., status ) + + if( .not. ast_getl( km2, 'KeyError', status ) ) then + call stopit( status, 'Error GETELEM_12E' ) + end if + + if( status .eq. sai__ok ) then + lval = ast_mapget0c( km2, 'FRED', cval, l, status ) + if( status .eq. AST__MPKER ) then + call err_annul( status ) + call ast_clear( map, 'keyerror', status ) + lval = ast_mapget0c( km2, 'FRED', cval, l, status ) + else + call stopit( status, 'Error GETELEM_12F' ) + end if + endif + + + + + + + + + call ast_mapputelemc( map, ' B ', 3, 'YES YES', STATUS ) + + if( ast_maplength( map, ' B', status ) .ne. 2 ) then + write(*,*) ast_maplength( map, ' B', status ) + call stopit( status, 'Error GETELEM_13' ) + + else if( .not. ast_mapgetelemc( map, ' B ', 2, cval0, + : status ) ) then + call stopit( status, 'Error GETELEM_14' ) + + else if( cval0 .ne. 'YES YES' ) then + write(*,*) cval0 + call stopit( status, 'Error GETELEM_15' ) + end if + + call ast_annul( map, status ) + + +C Test ast_mapcopy + map = ast_keymap( ' ', status ) + map1 = ast_keymap( ' ', status ) + map2 = ast_keymap( ' ', status ) + map3 = ast_keymap( ' ', status ) + + call ast_mapput0i( map1, 'a1', 1, ' ', status ) + call ast_mapput0i( map1, 'a2', 2, ' ', status ) + call ast_mapput0i( map1, 'a3', 3, ' ', status ) + + call ast_mapput0c( map, 'aa1', 'Yes', ' ', status ) + call ast_mapput0i( map, 'aa2', 2, ' ', status ) + call ast_mapput0a( map, 'aa3', map1, ' ', status ) + + call ast_mapput0i( map2, 'b1', 10, ' ', status ) + call ast_mapput0i( map2, 'b2', 20, ' ', status ) + call ast_mapput0i( map2, 'b3', 30, ' ', status ) + + call ast_mapput0c( map3, 'bb1', 'No', ' ', status ) + call ast_mapput0i( map3, 'aa2', 20, ' ', status ) + call ast_mapput0a( map3, 'bb3', map2, ' ', status ) + + call ast_mapcopy( map, map3, status ) + + if( ast_mapsize( map, status ) .ne. 5 ) then + write(*,*) ast_mapsize( map, status ) + call stopit( status, 'Error MAPCOPY_0' ) + end if + + if( .not. ast_mapget0c( map, 'aa1', cval, l, status ) ) then + call stopit( status, 'Error MAPCOPY_1' ) + else if( cval .ne. 'Yes' ) then + write(*,*) cval + call stopit( status, 'Error MAPCOPY_2' ) + end if + + if( .not. ast_mapget0i( map, 'aa2', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_3' ) + else if( ival .ne. 20 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_4' ) + end if + + if( .not. ast_mapget0a( map, 'aa3', aval, status ) ) then + call stopit( status, 'Error MAPCOPY_5' ) + else if( .not. ast_isakeymap( aval, status ) ) then + write(*,*) ast_getc( aval, 'Class' ) + call stopit( status, 'Error MAPCOPY_6' ) + + if( .not. ast_mapget0i( aval, 'a1', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_7' ) + else if( ival .ne. 1 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_8' ) + end if + + if( .not. ast_mapget0i( aval, 'a2', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_9' ) + else if( ival .ne. 20 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_10' ) + end if + + if( .not. ast_mapget0i( aval, 'a3', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_11' ) + else if( ival .ne. 3 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_12' ) + end if + end if + + if( .not. ast_mapget0c( map, 'bb1', cval, l, status ) ) then + call stopit( status, 'Error MAPCOPY_13' ) + else if( cval .ne. 'No' ) then + write(*,*) cval + call stopit( status, 'Error MAPCOPY_14' ) + end if + + if( .not. ast_mapget0a( map, 'bb3', aval, status ) ) then + call stopit( status, 'Error MAPCOPY_15' ) + else if( .not. ast_isakeymap( aval, status ) ) then + write(*,*) ast_getc( aval, 'Class' ) + call stopit( status, 'Error MAPCOPY_16' ) + + if( .not. ast_mapget0i( aval, 'b1', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_17' ) + else if( ival .ne. 10 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_18' ) + end if + + if( .not. ast_mapget0i( aval, 'b2', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_19' ) + else if( ival .ne. 20 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_20' ) + end if + + if( .not. ast_mapget0i( aval, 'b3', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_21' ) + else if( ival .ne. 30 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_22' ) + end if + end if + + + map = ast_keymap( ' ', status ) + map1 = ast_keymap( ' ', status ) + map2 = ast_keymap( ' ', status ) + map3 = ast_keymap( ' ', status ) + + call ast_mapput0i( map1, 'a1', 1, ' ', status ) + call ast_mapput0i( map1, 'a2', 2, ' ', status ) + call ast_mapput0i( map1, 'a3', 3, ' ', status ) + + call ast_mapput0c( map, 'aa1', 'Yes', ' ', status ) + call ast_mapput0i( map, 'aa2', 2, ' ', status ) + call ast_mapput0a( map, 'aa3', map1, ' ', status ) + + call ast_mapput0i( map2, 'b1', 10, ' ', status ) + call ast_mapput0i( map2, 'b2', 20, ' ', status ) + call ast_mapput0i( map2, 'b3', 30, ' ', status ) + + call ast_mapput0i( map3, 'aa1', 0, ' ', status ) + call ast_mapput0i( map3, 'aa2', 20, ' ', status ) + call ast_mapput0a( map3, 'aa3', map2, ' ', status ) + + call ast_mapcopy( map, map3, status ) + + if( ast_mapsize( map, status ) .ne. 3 ) then + write(*,*) ast_mapsize( map, status ) + call stopit( status, 'Error MAPCOPY_23' ) + end if + + if( .not. ast_mapget0i( map, 'aa1', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_24' ) + else if( ival .ne. 0 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_25' ) + end if + + if( .not. ast_mapget0i( map, 'aa2', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_26' ) + else if( ival .ne. 20 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_27' ) + end if + + if( .not. ast_mapget0a( map, 'aa3', aval, status ) ) then + call stopit( status, 'Error MAPCOPY_28' ) + else if( .not. ast_isakeymap( aval, status ) ) then + write(*,*) ast_getc( aval, 'Class' ) + call stopit( status, 'Error MAPCOPY_29' ) + + if( .not. ast_mapget0i( aval, 'a1', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_30' ) + else if( ival .ne. 1 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_31' ) + end if + + if( .not. ast_mapget0i( aval, 'a2', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_32' ) + else if( ival .ne. 20 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_33' ) + end if + + if( .not. ast_mapget0i( aval, 'a3', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_34' ) + else if( ival .ne. 3 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_35' ) + end if + + if( .not. ast_mapget0i( aval, 'b1', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_36' ) + else if( ival .ne. 10 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_37' ) + end if + + if( .not. ast_mapget0i( aval, 'b2', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_38' ) + else if( ival .ne. 20 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_39' ) + end if + + if( .not. ast_mapget0i( aval, 'b3', ival, status ) ) then + call stopit( status, 'Error MAPCOPY_40' ) + else if( ival .ne. 30 ) then + write(*,*) ival + call stopit( status, 'Error MAPCOPY_41' ) + end if + + end if + + + map = ast_keymap( ' ', status ) + map1 = ast_keymap( ' ', status ) + map2 = ast_keymap( ' ', status ) + map3 = ast_keymap( ' ', status ) + + call ast_mapput0i( map1, 'a1', 1, ' ', status ) + call ast_mapput0i( map1, 'a2', 2, ' ', status ) + call ast_mapput0i( map1, 'a3', 3, ' ', status ) + + call ast_mapput0c( map, 'aa1', 'Yes', ' ', status ) + call ast_mapput0i( map, 'aa2', 2, ' ', status ) + call ast_mapput0a( map, 'aa3', map1, ' ', status ) + + call ast_mapput0i( map2, 'b1', 10, ' ', status ) + call ast_mapput0i( map2, 'b2', 20, ' ', status ) + call ast_mapput0i( map2, 'b3', 30, ' ', status ) + + call ast_mapput0i( map3, 'aa1', 0, ' ', status ) + call ast_mapput0i( map3, 'aa2', 20, ' ', status ) + call ast_mapput0a( map3, 'aa3', map2, ' ', status ) + + call ast_setl( map, 'MapLocked', .TRUE., status ) + if( status .eq. SAI__OK ) then + call ast_mapcopy( map, map3, status ) + if( status .eq. AST__BADKEY ) then + call err_annul( status ) + else + call stopit( status, 'Error MAPCOPY_42' ) + end if + end if + + +C Test AST_MAPPUTU and undefined values + map = ast_keymap( ' ', status ) + call ast_mapputu( map, 'GG', 'A comment', status ) + if( ast_mapdefined( map, 'GG', status ) ) then + call stopit( status, 'Error UNDEF_0' ) + else if( ast_mapget0i( map, 'GG', ival, status ) ) then + call stopit( status, 'Error UNDEF_1' ) + else if( ast_mapget0s( map, 'GG', sval, status ) ) then + call stopit( status, 'Error UNDEF_1B' ) + else if( ast_mapget0c( map, 'GG', cval, l, status ) ) then + call stopit( status, 'Error UNDEF_2' ) + else if( ast_mapget0a( map, 'GG', aval, status ) ) then + call stopit( status, 'Error UNDEF_3' ) + else if( ast_mapget1i( map, 'GG', 2, nval, ivec, + : status ) ) then + call stopit( status, 'Error UNDEF_4' ) + else if( ast_mapgetelemc( map, 'gg', 1, cval, status ) ) then + call stopit( status, 'Error UNDEF_5' ) + else if( .not. ast_maphaskey( map, 'GG', status ) ) then + call stopit( status, 'Error UNDEF_6' ) + end if + + if( ast_maptype( map, 'GG', status ) .ne. AST__UNDEFTYPE ) then + write(*,*) ast_maptype( map, 'GG', status ) + call stopit( status, 'Error UNDEF_7' ) + else if( ast_mapsize( map, status ) .ne. 1 ) then + call stopit( status, 'Error UNDEF_8' ) + end if + + call ast_mapput0i( map, 'GG', 0, ' ', status ) + if( .not. ast_mapget0i( map, 'GG', ival, status ) ) then + call stopit( status, 'Error UNDEF_9' ) + else if( ival .ne. 0 ) then + call stopit( status, 'Error UNDEF_10' ) + endif + + call ast_maprename( map, 'GG', 'GGNEW', status ) + if( ast_maphaskey( map, 'GG', status ) ) then + call stopit( status, 'Error RENAME_1' ) + else if( .not. ast_mapget0i( map, 'GGNEW', ival, status ) ) then + call stopit( status, 'Error RENAME_2' ) + else if( ival .ne. 0 ) then + call stopit( status, 'Error RENAME_3' ) + endif + + call ast_maprename( map, 'GGNEW', 'GG', status ) + if( ast_maphaskey( map, 'GGNEW', status ) ) then + call stopit( status, 'Error RENAME_4' ) + else if( .not. ast_mapget0i( map, 'GG', ival, status ) ) then + call stopit( status, 'Error RENAME_5' ) + else if( ival .ne. 0 ) then + call stopit( status, 'Error RENAME_6' ) + endif + + + + + + + call ast_end( status ) + + call ast_activememory( ' ' ) + call ast_flushmemory( 1 ); + + call err_rlse( status ) + + if( status .eq. sai__ok ) then + write(*,*) 'All KeyMap tests passed' + else + write(*,*) 'KeyMap tests failed' + end if + + end + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + subroutine checkdump( obj, text, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*),key*30,txt1*50,txt2*50 + integer obj, status, next, end, ch, result, ll, overlap, size, + : i, type,obj1,obj2,l1,l2,nl + external mysource, mysink + character buf*400000 + + common /ss1/ buf + common /ss2/ next, end, ll, nl + + if( status .ne. sai__ok ) return + + ch = ast_channel( mysource, mysink, ' ', status ) + + + nl = 0 + ll = 110 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + next = 1 + nl = 0 + result = ast_read( ch, status ) + + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + + size = ast_mapsize( result, status ) + if( ast_mapsize( obj, status ) .ne. size ) then + write(*,*) size, ast_mapsize( obj, status ) + call stopit( status, 'checkDump 1' ) + else + do i = 1, size + key = ast_mapkey( result, i, status ) + type = ast_maptype( result, key, status ) + if( ast_maptype( obj, key, status ) .ne. type ) then + write(*,*) type, ast_maptype( obj, key, status ) + call stopit( status, 'checkDump 4' ) + else + + if( type .eq. AST__OBJECTTYPE ) then + + if( .not. ast_mapGet0A( result, key, obj1, + : status ) ) call stopit( status, 'checkDump 5' ) + if( .not. ast_mapGet0A( obj, key, obj2, + : status ) ) call stopit( status, 'checkDump 6' ) + if( ast_GetC( obj1, 'class', status ) .ne. + : ast_GetC( obj2, 'class', status ) ) then + call stopit( status, 'checkDump 7' ) + end if + + else + + if( .not. ast_mapGet0C( result, key, txt1, l1, + : status ) ) call stopit( status, 'checkDump 8' ) + if( .not. ast_mapGet0C( obj, key, txt2, l2, + : status ) ) call stopit( status, 'checkDump 9' ) + if( txt1( : l1 ) .ne. txt2( : l2 ) .or. + : l1 .ne. l2 ) then + call stopit( status, 'checkDump 10' ) + end if + + end if + end if + end do + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll, nl + character buf*400000 + + common /ss1/ buf + common /ss2/ next, end, ll,nl + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + nl = nl + 1 + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll, nl + character buf*400000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll, nl + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 400000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + nl = nl + 1 + endif + + next = next + ll + + end + + + + subroutine testsorting( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, km, i + character keys(5)*15 + character skeys(5)*15 + character key*( AST__SZCHR ) + + + data keys / 'ABC', 'zzzzzzzzzzz', 'this_is_a_key', 'HE-HE', 'A' / + data skeys / 'A', 'ABC', 'HE-HE', 'this_is_a_key', 'zzzzzzzzzzz' / + + + if( status .ne. sai__ok ) return + +C Value Age sorting... + +C First test adding entries into an already sorted KeyMap + + km = ast_keymap( 'Sortby=AgeDown', status ) + + do i = 1, 5 + call ast_mapput0i( km, keys(i), i, ' ', status ) + end do + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( key .ne. keys(i) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i,' is ',key,' (should be ',keys(i),')' + call stopit( status, 'Error Sort 1' ) + return + end if + end do + +C Now test sorting existing entries in a KeyMap. + call ast_set( km, 'Sortby=AgeUp', status ) + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( key .ne. keys(6-i) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i,' is ',key,' (should be ',keys(6-i),')' + call stopit( status, 'Error Sort 2' ) + return + end if + end do + + +C Changing the value of an existing entry should change its position in +C the list. + call ast_mapput0i( km, keys(1), 10, ' ', status ) + call ast_set( km, 'Sortby=AgeDown', status ) + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( i .eq. 5 ) then + if( key .ne. keys(1) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',1,' is ',key,' (should be ',keys(1),')' + call stopit( status, 'Error Sort 2b' ) + return + end if + else if( key .ne. keys(i+1) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i+1,' is ',key,' (should be ',keys(i+1), + : ')' + call stopit( status, 'Error Sort 2c' ) + return + end if + end do + + call ast_annul( km, status ) + + +C Key Age sorting... + +C First test adding entries into an already sorted KeyMap + + km = ast_keymap( 'Sortby=KeyAgeDown', status ) + + do i = 1, 5 + call ast_mapput0i( km, keys(i), i, ' ', status ) + end do + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( key .ne. keys(i) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i,' is ',key,' (should be ',keys(i),')' + call stopit( status, 'Error Sort 0' ) + return + end if + end do + +C Now test sorting existing entries in a KeyMap. + call ast_set( km, 'Sortby=KeyAgeUp', status ) + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( key .ne. keys(6-i) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i,' is ',key,' (should be ',keys(6-i),')' + call stopit( status, 'Error Sort -1' ) + return + end if + end do + + +C Changing the value of an existing entry should not change its position +C in the list. + call ast_mapput0i( km, keys(1), 10, ' ', status ) + call ast_set( km, 'Sortby=KeyAgeDown', status ) + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( key .ne. keys(i) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i,' is ',key,' (should be ',keys(i),')' + call stopit( status, 'Error Sort -2' ) + return + end if + end do + + call ast_annul( km, status ) + + + +C Key sorting... + +C First test adding entries into an already sorted KeyMap + + km = ast_keymap( 'Sortby=KeyUp', status ) + + do i = 1, 5 + call ast_mapput0i( km, keys(i), i, ' ', status ) + end do + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( key .ne. skeys(i) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i,' is ',key,' (should be ',skeys(i),')' + call stopit( status, 'Error Sort 3' ) + return + end if + end do + +C Now test sorting existing entries in a KeyMap. + call ast_set( km, 'Sortby=KeyDown', status ) + + do i = 1, 5 + key = ast_mapkey( km, i, status ) + if( key .ne. skeys(6-i) .and. status .eq. SAI__OK ) then + write(*,*) 'Key ',i,' is ',key,' (should be ',skeys(6-i),')' + call stopit( status, 'Error Sort 4' ) + return + end if + end do + + call ast_annul( km, status ) + + + + + end + + + + + subroutine testcasesens( status ) + implicit none + include 'AST_PAR' + include 'AST_ERR' + include 'SAE_PAR' + + integer status, map, l + character sval*( AST__SZCHR ) + + if( status .ne. sai__ok ) return + + map = ast_keymap( 'KeyCase=0', status ) + call ast_mapput0i( map, 'Freds', 1999, 'com 1', status ) + + if( .not. ast_maphaskey( map, 'fReDs', status ) ) then + call stopit( status, 'Error case 1' ) + endif + + if( ast_mapkey( map, 1, status ) .ne. 'FREDS' ) then + call stopit( status, 'Error case 2' ) + endif + + if( .not. ast_mapget0c( map, 'freds', sval, l, status ) ) then + call stopit( status, 'Error case 3' ) + else if( sval .ne. '1999' ) then + call stopit( status, 'Error case 4' ) + else if( l .ne. 4 ) then + call stopit( status, 'Error case 4b' ) + end if + + call ast_setl( map, 'KeyCase', 0, status ); + + if( status .eq. sai__ok ) then + call ast_clear( map, 'KeyCase', status ) + if( status .eq. AST__NOWRT ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, 'Error case 5' ) + end if + end if + + if( ast_mapsize( map, status ) .ne. 1 ) then + call stopit( status, 'Error case 6' ) + end if + + call ast_mapremove( map, 'freDs', status ) + + if( ast_mapsize( map, status ) .ne. 0 ) then + call stopit( status, 'Error case 7' ) + end if + + call ast_clear( map, 'KeyCase', status ) + call ast_mapput0i( map, 'Freds', 1999, 'com 1', status ) + if( ast_maphaskey( map, ' fReDs', status ) ) then + call stopit( status, 'Error case 8' ) + endif + + + call ast_annul( map, status ) + + end + + + + diff --git a/ast_tester/testlutmap.f b/ast_tester/testlutmap.f new file mode 100644 index 0000000..e183375 --- /dev/null +++ b/ast_tester/testlutmap.f @@ -0,0 +1,177 @@ + program testlutmap + implicit none + + include 'AST_PAR' + include 'SAE_PAR' + + integer lm, status, i + double precision lut1( 10 ), x( 7 ), y(7) + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + + + data lut1/ -1D0, 0D0, 1D0, 2D0, 3D0, 4D0, 5D0, 6D0, 7D0, 8D0 / + + + + + lm = ast_lutmap( 10, lut1, -1.0D0, 1.0D0, ' ', status ) + x( 1 ) = -2.0D0 + x( 2 ) = -1.0D0 + x( 3 ) = -0.5D0 + x( 4 ) = 3.0D0 + x( 5 ) = 7.5D0 + x( 6 ) = 8.0D0 + x( 7 ) = 8.5D0 + + call ast_tran1( lm, 7, x, .TRUE., y, status ) + + do i = 1, 7 + if( x( i ) .ne. y( i ) ) then + call stopit( status, "Error 1" ); + end if + end do + + call ast_tran1( lm, 7, y, .FALSE., x, status ) + + do i = 1, 7 + if( x( i ) .ne. y( i ) ) then + call stopit( status, "Error 2" ); + end if + end do + + + + + lut1( 1 ) = lut1( 2 ) + lm = ast_lutmap( 10, lut1, -1.0D0, 1.0D0, ' ', status ) + x( 1 ) = -2.0D0 + x( 2 ) = -1.0D0 + x( 3 ) = -0.5D0 + x( 4 ) = 0.5D0 + x( 5 ) = 3.0D0 + x( 6 ) = 8.0D0 + x( 7 ) = 8.5D0 + + call ast_tran1( lm, 7, x, .TRUE., y, status ) + + do i = 1, 3 + if( y( i ) .ne. 0.0 ) then + call stopit( status, "Error 3" ); + end if + end do + + do i = 4, 7 + if( x( i ) .ne. y( i ) ) then + call stopit( status, "Error 4" ); + end if + end do + + call ast_tran1( lm, 7, y, .FALSE., x, status ) + + do i = 1, 3 + if( x( i ) .ne. AST__BAD ) then + call stopit( status, "Error 5" ); + end if + end do + + do i = 4, 7 + if( x( i ) .ne. y( i ) ) then + call stopit( status, "Error 6" ); + end if + end do + + + lut1( 5 ) = AST__BAD + lm = ast_lutmap( 10, lut1, -1.0D0, 1.0D0, ' ', status ) + x( 1 ) = -2.0D0 + x( 2 ) = -1.0D0 + x( 3 ) = -0.5D0 + x( 4 ) = 0.5D0 + x( 5 ) = 3.0D0 + x( 6 ) = 8.0D0 + x( 7 ) = 8.5D0 + + call ast_tran1( lm, 7, x, .TRUE., y, status ) + + do i = 1, 3 + if( y( i ) .ne. 0.0 ) then + call stopit( status, "Error 7" ); + end if + end do + + if( y( 5 ) .ne. AST__BAD ) then + call stopit( status, "Error 8" ); + end if + y(5) = x( 5 ) + + do i = 4, 7 + if( x( i ) .ne. y( i ) ) then + call stopit( status, "Error 9" ); + end if + end do + + call ast_tran1( lm, 7, y, .FALSE., x, status ) + + do i = 1, 3 + if( x( i ) .ne. AST__BAD ) then + call stopit( status, "Error 10" ); + end if + end do + + if( x( 5 ) .ne. AST__BAD ) then + call stopit( status, "Error 11" ); + end if + x(5) = y( 5 ) + + do i = 4, 7 + if( x( i ) .ne. y( i ) ) then + call stopit( status, "Error 12" ); + end if + end do + + + + + + + + + + + + + + + + + call ast_end( status ) + call err_rlse( status ) + +c call ast_activememory( 'testlutmap' ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All LutMap tests passed' + else + write(*,*) 'LutMap tests failed' + end if + + end + + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + end + + + diff --git a/ast_tester/testnormmap.f b/ast_tester/testnormmap.f new file mode 100644 index 0000000..418ba7b --- /dev/null +++ b/ast_tester/testnormmap.f @@ -0,0 +1,76 @@ + program testnormmap + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, m, m2, m3, f, perm(3) + double precision at(3), bt(3) + + call ast_begin( status ) + + + status = sai__ok + + f = ast_cmpframe( ast_specframe( ' ', status ), + : ast_skyframe( ' ', status ), ' ', status ) + + perm( 1 ) = 3 + perm( 2 ) = 1 + perm( 3 ) = 2 + call ast_permaxes( f, perm, status ) + m = ast_normmap( f, ' ', status ) + + if( ast_geti( m, 'nin', status ) .ne. 3 ) call stopit( 1, status ) + if( ast_geti( m, 'nout', status ) .ne. 3 ) call stopit( 2, status) + + if( .not. ast_getl( m, 'TranForward', status ) ) call stopit( 3, + : status ) + if( .not. ast_getl( m, 'TranInverse', status ) ) call stopit( 4, + : status ) + + + m2 = ast_copy( m, status ) + call ast_invert( m2, status ) + m3 = ast_simplify( ast_cmpmap( m, m2, .true., ' ', status ), + : status ) + if( .not. ast_isaunitmap( m3, status ) ) call stopit( 5, status ) + + + + at( 1 ) = 2.0D0 + at( 2 ) = 3.0D4 + at( 3 ) = 1.0D0 + + call ast_trann( m, 1, 3, 1, at, 1, 3, 1, bt, status ) + + if( abs( bt(1)-1.14159265D0) .gt. 1.0D-6 ) then + write(*,*) bt(1)-1.14159265D0 + call stopit(6,status) + end if + if( bt(2) .ne. 3.0D4 ) call stopit(7,status) + if( abs( bt(3)-4.14159265D0) .gt. 1.0D-6 ) call stopit(8,status) + + + call ast_end( status ) + + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All NormMap tests passed' + else + write(*,*) 'NormMap tests failed' + end if + + end + + + subroutine stopit( i, status ) + implicit none + include 'SAE_PAR' + integer i, status + if( status .eq. sai__ok ) then + write( *,* ) 'Error ',i + status = sai__error + end if + end diff --git a/ast_tester/testobject.c b/ast_tester/testobject.c new file mode 100644 index 0000000..fc5b24b --- /dev/null +++ b/ast_tester/testobject.c @@ -0,0 +1,40 @@ +#include "ast.h" +#include + +main(){ + char *pickle1; + char *pickle2; + AstSkyFrame *sf = astSkyFrame( " " ); + AstFrame *bf = astFrame( 2, "Domain=SKY" ); + AstFrameSet *fs = astConvert( bf, sf, " " ); + + if( fs ) { + pickle1 = astToString( fs ); + AstFrameSet *fs2 = astFromString( pickle1 ); + pickle2 = astToString( fs2 ); + if( pickle1 && pickle2 ) { + if( strcmp( pickle1, pickle2 ) && astOK ) { + astError( AST__INTER, "Error 1\n" ); + } + } else if( astOK ) { + astError( AST__INTER, "Error 2\n" ); + } + + + pickle1 = astFree( pickle1 ); + pickle2 = astFree( pickle2 ); + + if( fs2 && !astEqual( fs, fs2 ) && astOK ) { + astError( AST__INTER, "Error 3\n" ); + } + + } else if( astOK ){ + astError( AST__INTER, "Error 4\n" ); + } + + if( astOK ) { + printf(" All Object tests passed\n"); + } else { + printf("Object tests failed\n"); + } +} diff --git a/ast_tester/testplot3d.f b/ast_tester/testplot3d.f new file mode 100644 index 0000000..66a6858 --- /dev/null +++ b/ast_tester/testplot3d.f @@ -0,0 +1,1357 @@ + program testplot3d + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer fset, plot3d, status, pgbeg + real gbox(6), lbnd(3), ubnd(3) + double precision bbox(6) + integer readtest + logical ok, pg3d_autocamera + character device*30 + + status = sai__ok + +c call ast_watchmemory( 130836 ) + call ast_begin( status ) + + lbnd( 1 ) = -1.0 + lbnd( 2 ) = -1.0 + lbnd( 3 ) = -1.0 + ubnd( 1 ) = 1.0 + ubnd( 2 ) = 1.0 + ubnd( 3 ) = 1.0 + + if( iargc() .gt. 0 ) then + call getarg( 1, device ) + else + device = '/XSERVE' + end if + ok = ( pgbeg( 0, device, 1, 1 ) .eq. 1 ) + + if( .not. ok ) write(*,*) 'PGPLOT OPEN FIALED' + + call pgask( .false. ) + + call pgpage + call pgwnad( 0.0, 1.0, 0.0, 1.0 ) + + ok = pg3d_autocamera( lbnd, ubnd ) +c + gbox(1) = lbnd(1) + gbox(2) = lbnd(2) + gbox(3) = lbnd(3) + gbox(4) = ubnd(1) + gbox(5) = ubnd(2) + gbox(6) = ubnd(3) + + bbox(1) = -1.0 + bbox(2) = -1.0 + bbox(3) = -1.0 + bbox(4) = 1.0 + bbox(5) = 1.0 + bbox(6) = 1.0 + + plot3d = ast_plot3d( AST__NULL, gbox, bbox, 'minticklen=0', + : status ) + call checkdump( plot3d, 'CheckDump test 1', status ) + call ast_annul( plot3d, status ) + bbox(1) = 0.5 + bbox(2) = 0.5 + bbox(3) = 0.5 + bbox(4) = 155.5 + bbox(5) = 107.5 + bbox(6) = 1640.5 + plot3d = ast_plot3d( readtest( "plot3d-test1.ast", status ), gbox, + : bbox, ' ' , status ) +c call checkdump( plot3d, 'CheckDump test 2', status ) +c call ast_set( plot3d, "System(1)=galactic,system(3)=freq", +c : status ) +c call ast_grid( plot3d, status ) + call explore( plot3d, status ) + call pgend + call ast_end( status ) + call ast_activememory( 'testplot3d' ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All Plot3D tests passed' + else + write(*,*) 'Plot3D tests failed' + end if + + end + + subroutine checkdump( obj, text, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap + external mysource, mysink, mysink2 + character buf*400000,buf2*400000 + common /ss1/ buf + common /ss2/ next, end, ll + common /ss3/ buf2 + if( status .ne. sai__ok ) return + call ast_begin( status ) + ch = ast_channel( mysource, mysink, ' ', status ) + ll = 200 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + + ll = 200 + next = 1 + ch = ast_channel( AST_NULL, mysink2, ' ', status ) + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write copied object to '// + : 'channel' ) + end if + if( buf .ne. buf2 ) then + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + call ast_end( status ) + end + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll + character buf*400000 + common /ss1/ buf + common /ss2/ next, end, ll + if( status .ne. sai__ok ) return + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + endif + next = next + ll + end + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf*400000 + character line*1000 + common /ss1/ buf + common /ss2/ next, end, ll + if( status .ne. sai__ok ) return + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + if( next + ll - 1 .ge. 400000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) line( f : l ) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + endif + next = next + ll + end + subroutine mysink2( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf2*400000 + character line*1000 + common /ss3/ buf2 + common /ss2/ next, end, ll + if( status .ne. sai__ok ) return + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf2( next : ) = line( f : l ) + l = l - f + 1 + if( next + ll - 1 .ge. 400000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink2!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf2( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink2!!' ) + else + end = next + l + buf2( end : next + ll - 1 ) = ' ' + endif + next = next + ll + end + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + end + + integer function readtest( name, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character name*(*) + integer status, ch + external mysource3 + readtest = AST__NULL + if( status .ne. sai__ok ) return + open( unit=1, file=name, status='old' ) + ch = ast_channel( mysource3, ast_null, ' ', status ) + readtest = ast_read( ch, status ) + call ast_annul( ch, status ) + close(1) + end + + subroutine mysource3( status ) + integer status + character buffer*200 + read( 1, '(A)', end = 99 ) buffer + call ast_putline( buffer, len( buffer ), status ) + return + 99 call ast_putline( buffer, -1, status ) + + end + + + subroutine explore( plot3d, status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + + real common_par + integer common_map + logical use_common + double precision common_x, common_y + common /ss4/ common_par,common_x,common_y,common_map,use_common + + real pi + parameter( pi = 3.1415927 ) + integer diag, plot3d, status, iopt, i, iclose + logical more, draw, ok, pg3d_findnearest, lval, pg3d_seteye, + : pg3d_setup + character settings*40, attr*20, corn(8)*3, text*40,just*4 + real xc(8), yc(8), zc(8) + double precision pos(3),x,y + real up(3), eye(3) + integer type, map + integer readtest + data xc /-1, 1, -1, 1, -1, 1, -1, 1 / + data yc /-1, -1, 1, 1, -1, -1, 1, 1 / + data zc /-1, -1, -1, -1, 1, 1, 1, 1 / + data corn/ 'lll', 'ull', 'lul', 'uul', + : 'llu', 'ulu', 'luu', 'uuu' / + if( status .ne. sai__ok ) return + eye( 1 ) = -3 + eye( 2 ) = 4 + eye( 3 ) = 3 + ok = pg3d_seteye( eye ) + up( 1 ) = 0.0 + up( 2 ) = -0.03 + up( 3 ) = 1.0 + ok = pg3d_setup( up ) + diag = 1 + common_x = AST__BAD + common_y = AST__BAD + common_map = AST__NULL + use_common = .false. + + call drawit( diag, plot3d, status ) + more = .true. + do while( more ) + + write(*,*) ' 0 - exit' + write(*,*) ' 1 - move forward' + write(*,*) ' 2 - move backwards' + write(*,*) ' 3 - rotate right' + write(*,*) ' 4 - rotate left' + write(*,*) ' 5 - rotate up' + write(*,*) ' 6 - rotate down' + write(*,*) ' 7 - rotate right continuous' + write(*,*) ' 8 - set attribute values' + write(*,*) ' 9 - get an attribute value' + write(*,*) ' 10 - clear an attribute value' + write(*,*) ' 11 - test an attribute value' + write(*,*) ' 12 - show the plot3d (big!)' + write(*,*) ' 13 - Draw a border' + write(*,*) ' 14 - Draw a marker' + write(*,*) ' 15 - Draw a text string' + write(*,*) ' 16 - Draw az-el diagram' + write(*,*) ' 17 - Draw grid-box diagram' + write(*,*) ' 18 - Vary parameter continuously' + write(*,*) ' 19 - Next figure' + write(*,*) ' 20 - Set Mapping' + write(*,*) ' 21 - Mark mapped position' + write(*,'(A,$)' ) 'Option: ' + read(*,*) iopt + + draw = .true. + if( iopt .eq. 0 ) then + draw = .false. + more = .false. + else if( iopt .eq. 1 ) then + call pg3d_forward( 0.1 ) + else if( iopt .eq. 2 ) then + call pg3d_forward( -0.1 ) + else if( iopt .eq. 3 ) then + call pg3d_rotateeye( 4, 7.0 ) + else if( iopt .eq. 4 ) then + call pg3d_rotateeye( 3, 7.0 ) + else if( iopt .eq. 5 ) then + call pg3d_rotateeye( 1, 7.0 ) + else if( iopt .eq. 6 ) then + call pg3d_rotateeye( 2, 7.0 ) + else if( iopt .eq. 7 ) then + do i = 0, 360, 7 + call pg3d_rotateeye( 4, 7.0 ) + call pgpage + call drawit( diag, plot3d, status ) + call pause + end do + draw = .false. + else if( iopt .eq. 8 ) then + write( *, '(A,$)' ) 'Attribute settings: ' + read( *, '(A)' ) settings + call ast_set( plot3d, settings, status ) + else if( iopt .eq. 9 ) then + write( *, '(A,$)' ) 'Attribute name to get: ' + read( *, '(A)' ) attr + write(*,*) ast_getc( plot3d, attr, status ) + draw = .false. + else if( iopt .eq. 10 ) then + write( *, '(A,$)' ) 'Attribute name to clear: ' + read( *, '(A)' ) attr + call ast_clear( plot3d, attr, status ) + else if( iopt .eq. 11 ) then + write( *, '(A,$)' ) 'Attribute name to test: ' + read( *, '(A)' ) attr + write(*,*) ast_test( plot3d, attr, status ) + draw = .false. + else if( iopt .eq. 12 ) then + call ast_show( plot3d, status ) + draw = .false. + else if( iopt .eq. 13 ) then + lval = ast_border( plot3d, status ) + draw = .false. + else if( iopt .eq. 14 ) then + write(*,'(A,$)') 'Marker X coord: ' + read(*,*) pos(1) + write(*,'(A,$)') 'Marker Y coord: ' + read(*,*) pos(2) + write(*,'(A,$)') 'Marker Z coord: ' + read(*,*) pos(3) + write(*,'(A,$)') 'Marker type: ' + read(*,*) type + call ast_mark( plot3d, 1, 3, 1, pos, type, status ) + draw = .false. + else if( iopt .eq. 15 ) then + write(*,'(A,$)') 'Text X coord: ' + read(*,*) pos(1) + write(*,'(A,$)') 'Text Y coord: ' + read(*,*) pos(2) + write(*,'(A,$)') 'Text Z coord: ' + read(*,*) pos(3) + write(*,'(A,$)') 'Text: ' + read(*,*) text + write(*,'(A,$)') 'Justification: ' + read(*,*) just + write(*,'(A,$)') 'Up X: ' + read(*,*) up(1) + write(*,'(A,$)') 'Up Y: ' + read(*,*) up(2) + write(*,'(A,$)') 'Up Z: ' + read(*,*) up(3) + call ast_text( plot3d, text, pos, up, just, status ) + draw = .false. + else if( iopt .eq. 16 ) then + diag = 2 + else if( iopt .eq. 17 ) then + diag = 1 + else if( iopt .eq. 18 ) then + use_common = .true. + do i = 0, 90, 1 + common_par = pi*i/180.0; + call pgpage + call drawit( diag, plot3d, status ) + call pause + end do + draw = .false. + use_common = .false. + else if( iopt .eq. 19 ) then + diag = diag + 1 + write(*,*) 'Drawing diag ',diag + + else if( iopt .eq. 20 ) then + write(*,'(A,$)') 'Supply a 2-in, 2-out Mapping (outputs '// + : 'are (long,lat)):' + read(*,*) text + common_map = readtest( text, status ) + draw = .false. + + else if( iopt .eq. 21 ) then + write(*,'(A,$)') 'Value for input 1: ' + read(*,*) common_x + write(*,'(A,$)') 'Value for input 2: ' + read(*,*) common_y + + else + draw = .false. + write(*,*) 'Bad option ',iopt + end if + + if( draw ) then + call pgpage +c ok = pg3d_findnearest( 8, xc, yc, zc, iclose ) +c call ast_setc( plot3d, 'RootCorner', corn( iclose + 1 ), +c : status ) + call drawit( diag, plot3d, status ) + end if + + if( status .ne. sai__ok ) call err_flush( status ) + end do + + end + + subroutine pause + implicit none + integer i + double precision a + a = 1.0D0 + do i = 1, 2000000 + a = tan( a ) + end do + end + + subroutine drawit( diag, plot3d, status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + integer NP + parameter ( NP = 50 ) + integer w1, w2 + real rd, pi + parameter( w1 = 6, w2 = 3, rd = 1.5, pi = 3.1415927 ) + + real common_par + integer common_map + logical use_common + double precision common_x, common_y + common /ss4/ common_par,common_x,common_y,common_map,use_common + + double precision dlat, dlon, up, north + integer diag, plot3d, status, i, j, azelmap + real x(NP), y(NP), z(NP), axlen, a1, a2, r(3), u(3), n(3), + : ang, k, cen(3), norm(3), start(3), lon, lat, origin(3), + : theta,phi + if( status .ne. sai__ok ) return + + if( diag .eq. 1 ) then + call ast_grid( plot3d, status ) + + else + axlen = 1.8 + a1 = 0.1 + a2 = a1*0.6 + lon = pi*210.0/180.0 + if( use_common ) then + lat = common_par + else + lat = pi*25.0/180.0 + endif + + do while( lat .gt. pi ) + lat = lat - 2*pi + end do + + do while( lat .lt. -pi ) + lat = lat + 2*pi + end do + + if( lat .gt. pi/2 ) then + lat = pi - lat + lon = lon + pi + + else if( lat .lt. -pi/2 ) then + lat = -pi - lat + lon = lon + pi + end if + + do while( lon .gt. 2*pi ) + lon = lon - 2*pi + end do + + do while( lon .lt. 0 ) + lon = lon + 2*pi + end do + + +c+ Fig1 (part a) + if( diag .eq. 2 ) then + call pgslw( w2 ) + cen(1) = 0.0 + cen(2) = 0.0 + cen(3) = 0.0 + call s2c( lon - pi/2, 0.0, 1.0, norm ) + call s2c( lon, 0.0, rd*0.6, start ) + call pgsci( 10 ) + call pgscr( 10, 0.0, 0.0, 1.0 ) + call arc( cen, norm, start, -lat, -rd*0.1 ) + + u(1)=0 + u(2)=0 + u(3)=1 + call s2c( lon, lat/2.2, rd*0.7, r ) + call g3dtext( 'el', r, 'CL', u, norm ) + + norm(1)=0 + norm(2)=0 + norm(3)=-1 + start(1)=rd*0.6 + start(2)=0 + start(3)=0 + call arc( cen, norm, start, lon, rd*0.1 ) + + call s2c( 110.0/180.0*PI, 0.0, rd*0.7, r ) + call g3dtext( 'az', r, 'TC', r, norm ) + + call pgsci( 1 ) + endif + + origin(1) = 0.0 + origin(2) = 0.0 + origin(3) = 0.0 + call axes( origin, axlen, '(north) u', '(east) v', + : '(zenith) w', w1, w2, 'X', 0.0, 0.0, 0.0 ) + +* Quadrant from (0,0,1) to (-1,0,0) + cen(1) = 0.0 + cen(2) = 0.0 + cen(3) = 0.0 + call s2c( (int(2*lon/pi)-1)*pi/2, 0.0, 1.0, norm ) + start(1) = 0.0 + start(2) = 0.0 + start(3) = rd + call pgslw( w2 ) + call arc( cen, norm, start, pi/2, 0.0 ) + +* Quadrant from (0,0,1) to (0,-1,0) + call s2c( int(2*lon/pi)*pi/2, 0.0, 1.0, norm ) + call arc( cen, norm, start, pi/2, 0.0 ) + +* Quadrant from (-1,0,0) to (0,-1,0) + norm(1) = 0.0 + norm(2) = 0.0 + norm(3) = 1.0 + call s2c( (int(2*lon/pi)+1)*pi/2, 0.0, rd, start ) + call arc( cen, norm, start, pi/2, 0.0 ) + +* Dashed red quadrant at az = lon degs + call s2c( lon-pi/2, 0.0, 1.0, norm ) + start(1) = 0.0 + start(2) = 0.0 + start(3) = rd + call pgsci( 10 ) + call pgscr( 10, 1.0, 0.0, 0.0 ) + call pgsls( 2 ) + call pgslw( 2 ) + call arc( cen, norm, start, pi/2, 0.0 ) + +* Dashed red line of latitude at el = lat degs + cen(1) = 0.0 + cen(2) = 0.0 + cen(3) = sin(lat)*rd + norm(1) = 0.0 + norm(2) = 0.0 + norm(3) = 1.0 + call s2c( (int(2*lon/pi)+1)*pi/2, lat, rd, start ) + call arc( cen, norm, start, pi/2, 0.0 ) + +* Dashed red line from centre to az=lon el=0 + x( 1 ) = 0.0 + y( 1 ) = 0.0 + z( 1 ) = 0.0 + x( 2 ) = cos(lon)*rd + y( 2 ) = sin(lon)*rd + z( 2 ) = 0.0 + call g3dline( 2, x, y, z ) + +* Solid red line from centre to az=210 el=45 + x( 1 ) = 0.0 + y( 1 ) = 0.0 + z( 1 ) = 0.0 + call s2c( lon, lat, rd, origin ) + x( 2 ) = origin(1) + y( 2 ) = origin(2) + z( 2 ) = origin(3) + call pgsls( 1 ) + call g3dline( 2, x, y, z ) + +c+ Fig1 - (part b) + if( diag .eq. 2 ) then + call pgscr( 10, 0.0, 0.6, 0.0 ) + call s2c( lon, lat, rd, origin ) + call axes( origin, 0.2*axlen, 'daz', 'del', ' ', w1, w2, + : 'ZYZ', lon, (pi/2 - lat), pi/2 ) + endif + + +c+ Fig2 + if( diag .eq. 3 ) then + call pgscr( 10, 0.6, 0.6, 0.0 ) + call s2c( lon, lat, rd, origin ) + call axes( origin, 0.4*axlen, 'UP', 'N', ' ', w1, w2, + : 'ZYZ', lon, (pi/2 - lat), lat ) + + + call pgscr( 10, 0.0, 0.0, 1.0 ) + call s2c( lon+0.22, lat, rd, start ) + call s2c( lon, lat, rd, cen ) + call s2c( lon, lat, rd, norm ) + call arc( cen, norm, start, -(lat-0.05), -rd*0.02 ) + + call s2c( lon+0.25, lat*1.1, rd, r ) + norm(1) = 0 + norm(2) = 0 + norm(3) = 0 + u(1) = 0 + u(2) = 0 + u(3) = 1.0 + call g3dtext( 'el', r, 'CR', u, norm ) + + endif + + +c+ Fig3 + if( diag .eq. 4 ) then + call pgscr( 10, 0.0, 0.0, 1.0 ) + call s2c( lon, lat, rd, origin ) + do i = 1, 4 + theta = i*5.0*pi/180.0 + cen(1) = cos(theta)*origin(1) + cen(2) = cos(theta)*origin(2) + cen(3) = cos(theta)*origin(3) + call s2c( lon + theta, lat, rd, start ) + call arc( cen, cen, start, 2*pi, 0.05 ) + end do + + + cen(1)=0 + cen(2)=0 + cen(3)=0 + + call s2c( pi, pi/2-pi/2, rd, start ) + call s2c( pi+pi/2, 0.0, 1.0, norm ) + + call rotvec( 'ZYZ', lon, pi/2-lat, lat, norm ) + call rotvec( 'ZYZ', lon, pi/2-lat, lat, start ) + call arc( cen, norm, start, pi/2, 0.0 ) + + + call pgscr( 10, 0.0, 1.0, 1.0 ) + call s2c( lon, lat, rd, origin ) + call axes( origin, 0.2*axlen, 'y', 'x', ' ', w1, w2, + : 'ZYZ', lon, (pi/2 - lat), lat ) + endif + + +c+ Fig4 + if( diag .eq. 5 ) then + call pgscr( 10, 0.0, 0.0, 1.0 ) + call s2c( lon, lat, rd, origin ) + do i = 1, 4 + theta = i*5.0*pi/180.0 + cen(1) = cos(theta)*origin(1) + cen(2) = cos(theta)*origin(2) + cen(3) = cos(theta)*origin(3) + call s2c( lon + theta, lat, rd, start ) + call arc( cen, cen, start, 2*pi, 0.05 ) + end do + + + cen(1)=0 + cen(2)=0 + cen(3)=0 + + call s2c( pi, pi/2-pi/2, rd, start ) + call s2c( pi+pi/2, 0.0, 1.0, norm ) + + call rotvec( 'ZYZ', lon, pi/2-lat, lat, norm ) + call rotvec( 'ZYZ', lon, pi/2-lat, lat, start ) + call arc( cen, norm, start, pi/2, 0.0 ) + + + call pgscr( 10, 0.0, 1.0, 1.0 ) + call s2c( lon, lat, 0.0, origin ) + call axes( origin, rd, 'b', 'a', 'c', w1, w2, + : 'ZYZ', lon, (pi/2 - lat), pi/2 + lat ) + endif + +c+ Fig5 + if( diag .eq. 6 ) then + call pgscr( 10, 0.0, 1.0, 1.0 ) + call s2c( lon, lat, 0.0, origin ) + call axes( origin, rd, 'b', 'a', 'c', w1, w2, + : 'ZYZ', lon, (pi/2 - lat), pi/2 + lat ) + + + call pgscr( 10, 0.0, 0.0, 1.0 ) + call axes( origin, rd, 'b''', 'a''', 'c''', w1, w2, + : 'ZYZ', lon, (pi/2 - lat), pi/2 ) + + call pgsci( 1 ) + call s2c( lon, lat, 1.0, norm ) + start(1) = 0.0 + start(2) = 0.0 + start(3) = rd*0.5 + call s2c( lon, lat, 0.0, cen ) + call arc( cen, norm, start, -lat, 0.0 ) + + call pgsci( 1 ) + call s2c( 0.5*( lon - pi/2), lat/2+0.2, rd*0.55, r ) + norm(1) = 0 + norm(2) = 0 + norm(3) = 0 + u(1) = 0 + u(2) = 0 + u(3) = 1.0 + call g3dtext( 'el', r, 'CR', u, norm ) + endif + +c+ Fig6 + if( diag .eq. 7 ) then + call s2c( lon, lat, 0.0, origin ) + + call pgscr( 10, 0.0, 0.0, 1.0 ) + call axes( origin, rd, 'b''', 'a''', 'c''', w1, w2, + : 'ZYZ', lon, (pi/2 - lat), pi ) + + call pgscr( 10, 0.0, 1.0, 0.0 ) + call axes( origin, rd, 'b''''', 'a''''', 'c''''', w1, w2, + : 'ZYZ', lon, 0.0, pi ) + + endif + + + + + + if( diag .eq. 8 ) then + call pgscr( 10, 0.0, 1.0, 1.0 ) + call s2c( lon, lat, 0.0, origin ) + call axes( origin, rd, 'a', 'b', 'c', w1, w2, + : 'ZYZ', 0.0, 0.0, 0.0 ) + end if + + if( diag .eq. 9 ) then + call pgscr( 10, 0.0, 1.0, 1.0 ) + call s2c( lon, lat, 0.0, origin ) + call axes( origin, rd, 'a', 'b', 'c', w1, w2, + : 'ZYZ', lon, 0.0, 0.0 ) + end if + + if( diag .eq. 10 ) then + call pgscr( 10, 0.0, 1.0, 1.0 ) + call s2c( lon, lat, 0.0, origin ) + call axes( origin, rd, 'a', 'b', 'c', w1, w2, + : 'ZYZ', lon, pi/2-lat, 0.0 ) + end if + + if( diag .eq. 11 ) then + call pgscr( 10, 0.0, 1.0, 1.0 ) + call s2c( lon, lat, 0.0, origin ) + call axes( origin, rd, 'a', 'b', 'c', w1, w2, + : 'ZYZ', lon, pi/2-lat, lat ) + end if + + + + + + + + + + + +c if( common_map .ne. AST__NULL .and. +c ; common_x .ne. AST__BAD .and. +c : common_y .ne. AST__BAD ) then +c call ast_tran2( common_map, 1, common_x, common_y, .true., +c : dlon, dlat, status ) +c call s2c( real(dlon), real(dlat), rd, cen ) +c call pgsci( 10 ) +c call pgscr( 10, 1.0, 0.0, 1.0 ) +c call g3dmark( cen, 2 ) +c end if + + call pgsci( 1 ) + call pgslw( 4 ) + call pgsls( 1 ) + + end if + + end + + subroutine arc( cen, normal, start, ang, arrow ) + implicit none + real cen(3), normal(3), start(3), ang, arrow, ut(3), ur(3) + integer np + parameter( NP = 50 ) + integer i + real v1(3), v2( 3 ), v1l, v2l, x(NP), y(NP), z(NP), a, ca, sa, + : t1(3), t2(3), n(3) + call copy( normal, n ) + call norm( n ) + call sub( start, cen, v1 ) + call cross( n, v1, t1 ) + call cross( t1, n, v1 ) + call mod( v1, v1l ) + call cross( v1, n, v2 ) + call mod( v2, v2l ) + do i = 1, 3 + v1( i ) = v1(i)/v1l + v2( i ) = v2(i)/v2l + end do + + do i = 1, NP + a = ang*( i - 1 )/( NP - 1 ) + ca = cos(a) + sa = sin(a) + x(i) = v1l*( v1(1)*ca + v2(1)*sa ) + cen(1) + y(i) = v1l*( v1(2)*ca + v2(2)*sa ) + cen(2) + z(i) = v1l*( v1(3)*ca + v2(3)*sa ) + cen(3) + end do + call g3dline( NP, x, y, z ) + if( arrow .ne. 0.0 ) then + ca = cos(ang) + sa = sin(ang) + do i = 1, 3 + ut(i) = sa*v1(i)-ca*v2(i) + ur(i) = ca*v1(i)+sa*v2(i) + end do + x(1) = x(NP) + arrow*( ut(1) - 0.5*ur(1) ) + y(1) = y(NP) + arrow*( ut(2) - 0.5*ur(2) ) + z(1) = z(NP) + arrow*( ut(3) - 0.5*ur(3) ) + x(2) = x(NP) + y(2) = y(NP) + z(2) = z(NP) + x(3) = x(NP) + arrow*( ut(1) + 0.5*ur(1) ) + y(3) = y(NP) + arrow*( ut(2) + 0.5*ur(2) ) + z(3) = z(NP) + arrow*( ut(3) + 0.5*ur(3) ) + call g3dline( 3, x, y, z ) + end if + end + subroutine s2c( lon, lat, r, p ) + implicit none + real lon, lat, r, p(3), k + p( 3 ) = r*sin(lat) + k = r*cos(lat) + p( 1 ) = k*cos(lon) + p( 2 ) = k*sin(lon) + end + + subroutine axes( origin, axlen, tx, ty, tz, w1, w2, order, phi, + : theta, psi ) + implicit none + character order*(*),tx*(*), ty*(*), tz*(*) + real phi, theta, psi + double precision rmat(3,3),va,vb + real axlen, a1, a2, mat(3,3),x(10),y(10),z(10),r(3),u(3),n(3), + : origin(3), sn + integer w1, w2 + a1 = 0.05 + a2 = a1*0.6 + sn = 0.0 + call deuler( order, dble(phi), dble(theta), dble(psi), rmat ) + mat(1,1) = rmat(1,1) + mat(1,2) = rmat(1,2) + mat(1,3) = rmat(1,3) + mat(2,1) = rmat(2,1) + mat(2,2) = rmat(2,2) + mat(2,3) = rmat(2,3) + mat(3,1) = rmat(3,1) + mat(3,2) = rmat(3,2) + mat(3,3) = rmat(3,3) +cc* X axis with arrow and label + if( tx .ne. ' ' ) then + x(1) = axlen + y(1) = 0.0 + z(1) = 0.0 + x(2) = -axlen + y(2) = 0.0 + z(2) = 0.0 + call mxv( mat, 2, x, y, z, origin ) + call pgslw( w1 ) + call g3dline( 2, x, y, z ) + x(1) = axlen - a1 + y(1) = -a2 + z(1) = 0.0 + x(2) = axlen + y(2) = 0.0 + z(2) = 0.0 + x(3) = axlen - a1 + y(3) = a2 + z(3) = 0.0 + call mxv( mat, 3, x, y, z, origin ) + call g3dline( 3, x, y, z ) + r(1) = axlen + a1 + r(2) = 0.0 + r(3) = a2 + u(1) = 0.0 + u(2) = 0.0 + u(3) = 1.0 + n(1) = 0.0 + n(2) = sn + n(3) = 0.0 + + call pgslw( w2 ) + call mxv2( mat, r, .true., origin ) + call g3dtext( tx, r, 'BR', u, n ) + call pgslw( w1 ) + end if +c* Y axis with arrow and label + if( ty .ne. ' ' ) then + x(1) = 0.0 + y(1) = -axlen + z(1) = 0.0 + x(2) = 0.0 + y(2) = axlen + z(2) = 0.0 + call mxv( mat, 2, x, y, z, origin ) + call g3dline( 2, x, y, z ) + x(1) = -a2 + y(1) = axlen - a1 + z(1) = 0.0 + x(2) = 0.0 + y(2) = axlen + z(2) = 0.0 + x(3) = a2 + y(3) = axlen - a1 + z(3) = 0.0 + call mxv( mat, 3, x, y, z, origin ) + call g3dline( 3, x, y, z ) + r(1) = 0.0 + r(2) = axlen + a1 + r(3) = a2 + u(1) = 0.0 + u(2) = 0.0 + u(3) = 1.0 + n(1) = sn + n(2) = 0.0 + n(3) = 0.0 + + call pgslw( w2 ) + call mxv2( mat, r, .true., origin ) + call g3dtext( ty, r, 'BR', u, n ) + call pgslw( w1 ) + end if +c* Z axis with arrow and label + if( tz .ne. ' ' ) then + x(1) = 0.0 + y(1) = 0.0 + z(1) = axlen + x(2) = 0.0 + y(2) = 0.0 + z(2) = 0.0 + call mxv( mat, 2, x, y, z, origin ) + call g3dline( 2, x, y, z ) + x(1) = -a2 + y(1) = 0.0 + z(1) = axlen - a1 + x(2) = 0.0 + y(2) = 0.0 + z(2) = axlen + x(3) = a2 + y(3) = 0.0 + z(3) = axlen - a1 + call mxv( mat, 3, x, y, z, origin ) + call g3dline( 3, x, y, z ) + r(1) = 0.0 + r(2) = 0.0 + r(3) = axlen + a1 + u(1) = 0.0 + u(2) = 0.0 + u(3) = 1.0 + n(1) = 0.0 + n(2) = 0.0 + n(3) = 0.0 + + call pgslw( w2 ) + call mxv2( mat, r, .true., origin ) + call g3dtext( tz, r, 'CR', u, n ) + call pgslw( w1 ) + end if + end + + subroutine mxv( mat, n, x, y, z, origin ) + implicit none + integer n, i + real x(n), y(n), z(n), mat(3,3), va(3),vb(3),origin(3) + do i = 1, n + va(1)= x(i) + va(2)= y(i) + va(3)= z(i) + call sla_mxv( mat, va, vb ) + x(i) = vb(1) + origin(1) + y(i) = vb(2) + origin(2) + z(i) = vb(3) + origin(3) + end do + end + + subroutine mxv2( mat, r, move, origin ) + implicit none + real r(3), mat(3,3), vb(3), origin(3) + logical move + call sla_mxv( mat, r, vb ) + if( move ) then + r(1) = vb(1) + origin(1) + r(2) = vb(2) + origin(2) + r(3) = vb(3) + origin(3) + else + r(1) = vb(1) + r(2) = vb(2) + r(3) = vb(3) + end if + end + + subroutine deuler( order, phi, theta, psi, rmat ) + implicit none + character order*(*) + double precision phi, theta, psi, rmat(3,3), t(3), smat(3,3) + double precision ang, v(3), ux(3), uy(3), uz(3), tmat(3,3), + : axvec(3) + integer n, i, j + n = len( order ) + do i = 1, 3 + ux(i) = 0.0D0 + uy(i) = 0.0D0 + uz(i) = 0.0D0 + do j = 1, 3 + rmat(i,j) = 0.0 + end do + rmat(i,i) = 1.0 + end do + ux(1) = 1.0D0 + uy(2) = 1.0D0 + uz(3) = 1.0D0 + do i = 1, 3 + if( i .le. n ) then + if( i .eq. 1 ) then + ang = phi + else if( i .eq. 2 ) then + ang = theta + else + ang = psi + end if + if( order( i : i ) .eq. 'X' ) then + do j = 1, 3 + axvec(j) = -ux(j)*ang + end do + else if( order( i : i ) .eq. 'Y' ) then + do j = 1, 3 + axvec(j) = -uy(j)*ang + end do + else if( order( i : i ) .eq. 'Z' ) then + do j = 1, 3 + axvec(j) = -uz(j)*ang + end do + else + write(*,*) 'Bad axis label (',order(i:i),') in deuler!!!' + end if + call sla_dav2m( axvec, tmat ) + do j = 1, 3 + smat(1,j) = rmat(1,j) + smat(2,j) = rmat(2,j) + smat(3,j) = rmat(3,j) + end do + call sla_dmxm( tmat, smat, rmat ) + call sla_dmxv( tmat, ux, v ) + ux(1) = v(1) + ux(2) = v(2) + ux(3) = v(3) + call sla_dmxv( tmat, uy, v ) + uy(1) = v(1) + uy(2) = v(2) + uy(3) = v(3) + call sla_dmxv( tmat, uz, v ) + uz(1) = v(1) + uz(2) = v(2) + uz(3) = v(3) + end if + end do + end + + subroutine rotvec( order, phi, theta, psi, v ) + implicit none + character order*(*) + real phi, theta, psi,v(3),vt(3) + double precision rmat(3,3) + real mat(3,3) + call deuler( order, dble(phi), dble(theta), dble(psi), rmat ) + mat(1,1) = rmat(1,1) + mat(1,2) = rmat(1,2) + mat(1,3) = rmat(1,3) + mat(2,1) = rmat(2,1) + mat(2,2) = rmat(2,2) + mat(2,3) = rmat(2,3) + mat(3,1) = rmat(3,1) + mat(3,2) = rmat(3,2) + mat(3,3) = rmat(3,3) + call sla_mxv( mat, v, vt ) + v(1) = vt(1) + v(2) = vt(2) + v(3) = vt(3) + end + + subroutine cross( a, b, c ) + implicit none + real a(3), b(3), c(3) + c(1) = a(2)*b(3) - a(3)*b(2) + c(2) = - a(1)*b(3) + a(3)*b(1) + c(3) = a(1)*b(2) - a(2)*b(1) + end + + subroutine sub( a, b, c ) + implicit none + real a(3), b(3), c(3) + c(1) = a(1) - b(1) + c(2) = a(2) - b(2) + c(3) = a(3) - b(3) + end + + subroutine add( a, b, c ) + implicit none + real a(3), b(3), c(3) + c(1) = a(1) + b(1) + c(2) = a(2) + b(2) + c(3) = a(3) + b(3) + end + + subroutine dot( a, b, c ) + implicit none + real a(3), b(3), c + c = a(1)*b(1) + a(2)*b(2) + a(3)*b(3) + end + + subroutine mod( a, c ) + implicit none + real a(3), c + c = sqrt( a(1)*a(1) + a(2)*a(2) + a(3)*a(3) ) + end + + subroutine copy( a, b ) + implicit none + real a(3), b(3) + b(1) = a(1) + b(2) = a(2) + b(3) = a(3) + end + + subroutine norm( a ) + implicit none + real a(3), b + call mod( a, b ) + if( b .ne. 0.0 ) then + a(1) = a(1)/b + a(2) = a(2)/b + a(3) = a(3)/b + end if + end + + subroutine g3dtext( t, r, j, u, n ) + implicit none + character*(*) t, j + real r(3), u(3), n(3), r2(3),u2(3),n2(3) + integer junk, ast_g3dtext + + call copy( r, r2 ) + call copy( u, u2 ) + call copy( n, n2 ) + r2(2) = -r2(2) + u2(2) = -u2(2) + n2(2) = -n2(2) + + junk = ast_g3dtext( t, r2, j, u2, n2 ) + end + + subroutine g3dline( n, x, y, z ) + implicit none + integer i, n + logical junk, ast_g3dline + real x(n), y(n), z(n) + + do i = 1, n + y(i)=-y(i) + end do + + junk = ast_g3dline( n, x, y, z ) + + do i = 1, n + y(i)=-y(i) + end do + + end + + subroutine g3dmark( pos, type ) + implicit none + real pos(3) + integer type + logical junk, ast_g3dmark + + pos(2) = -pos(2) + junk = ast_g3dmark( 1, pos(1), pos(2), pos(3), type, pos ) + pos(2) = -pos(2) + + end + + + + + + subroutine maketanmap( new, lon, lat, rot, azelmap, status ) + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + + logical new + double precision lon, lat, rot + integer azelmap, status + + double precision mmt( 2 ), mat(9), mat1(9), mat2(3) + integer tanmap, tmap, sphmap, wcsmap, m1, m2, m3, c0, c1, matmap1, + : matmap2 + double precision pi, piby2 + + if( status .ne. sai__ok ) return + + pi = 3.1415927D0 + piby2 = pi/2.0 + + c0 = ast_SphMap( 'UnitRadius=1', status ) + wcsmap = ast_WcsMap( 2, AST__TAN, 1, 2, ' ', status ) + call ast_Invert( wcsmap, status ) + call ast_Invert( c0, status ) + c1 = ast_CmpMap( wcsmap, c0, .true., ' ', status ) + call ast_Invert( c0, status ) + + if( new ) then + mmt( 1 ) = -1.0 + mmt( 2 ) = -1.0 + tmap = ast_MatrixMap( 2, 2, 1, mmt, ' ', status ) + +* Note, sla_deuler groups columns in the returned matrix, but slaDeuler +* groups rows. AST always expects rows to be grouped, so transpose the +* matrices returned by sla_deuler. + call sla_Deuler( 'Z', -rot, 0.0D0, 0.0D0, mat1 ) + call trans( mat1 ) + m1 = ast_MatrixMap( 3, 3, 0, mat1, ' ', status ) + + call sla_Deuler( 'Y', (PIBY2-lat), 0.0D0, 0.0D0, mat1 ) + call trans( mat1 ) + m2 = ast_MatrixMap( 3, 3, 0, mat1, ' ', status ) + + call sla_Deuler( 'Z', (lon-PI), 0.0D0, 0.0D0, mat1 ) + call trans( mat1 ) + m3 = ast_MatrixMap( 3, 3, 0, mat1, ' ', status ) + + matmap1 = ast_CmpMap( m1, ast_CmpMap( m2, m3, .true., ' ', + : status ), + : .TRUE., ' ', status ) + + mat2( 1 ) = 1.0 + mat2( 2 ) = -1.0 + mat2( 3 ) = 1.0 + matmap2 = ast_MatrixMap( 3, 3, 1, mat2, ' ', status ) + + m1 = ast_CmpMap( c1, matmap1, .true., ' ', status ) + m2 = ast_CmpMap( m1, matmap2, .true., ' ', status ) + tanmap = ast_CmpMap( m2, c0, .true., ' ', status ) + azelmap = ast_CmpMap( tmap, tanmap, .true., ' ', status ) + + else + +* Note, sla_deuler groups columns in the returned matrix, but slaDeuler +* groups rows. AST always expects rows to be grouped, so transpose the +* matrices returned by sla_deuler. + call sla_Deuler( 'Z', rot, 0.0D0, 0.0D0, mat1 ) + call trans( mat1 ) + m1 = ast_MatrixMap( 3, 3, 0, mat1, ' ', status ) + + call sla_Deuler( 'Y', -(PIBY2-lat), 0.0D0, 0.0D0, mat1 ) + call trans( mat1 ) + m2 = ast_MatrixMap( 3, 3, 0, mat1, ' ', status ) + + call sla_Deuler( 'Z', -lon, 0.0D0, 0.0D0, mat1 ) + call trans( mat1 ) + m3 = ast_MatrixMap( 3, 3, 0, mat1, ' ', status ) + + + matmap1 = ast_CmpMap( m1, ast_CmpMap( m2, m3, .true., ' ', + : status ), + : .TRUE., ' ', status ) + + m1 = ast_CmpMap( c1, matmap1, .true., ' ', status ) + + + + + + + mmt( 1 ) = -1.0 + mmt( 2 ) = 1.0 + tmap = ast_MatrixMap( 2, 2, 1, mmt, ' ', status ) + m2 = ast_CmpMap( tmap, m1, .true., ' ', status ) + + + + + + + azelmap = ast_CmpMap( m2, c0, .true., ' ', status ) + + end if + + end + + subroutine trans( mat ) + implicit none + double precision t, mat(9) + + t = mat(8) + mat(8) = mat(6) + mat(6) = t + + t = mat(3) + mat(3) = mat(7) + mat(7) = t + + t = mat(2) + mat(2) = mat(4) + mat(4) = t + + end diff --git a/ast_tester/testpolymap.f b/ast_tester/testpolymap.f new file mode 100644 index 0000000..90fa790 --- /dev/null +++ b/ast_tester/testpolymap.f @@ -0,0 +1,246 @@ + program testpolymap + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, pm, pm2, i, maxord + double precision coeff( 16 ), lbnd( 2 ), ubnd( 2 ), + : xin(3), yin(3), xout(3), yout(3), errlim, + : xin2(3), yin2(3), coeff_1d(6), acc, + : coeff2( 24 ), coeff3( 6*4 ), err, maxacc + + data coeff / 1.0, 1.0, 0.0, 0.0, + : 2.0, 1.0, 1.0, 0.0, + : 1.0, 2.0, 0.0, 0.0, + : 3.0, 2.0, 0.0, 1.0 / + + data coeff2 / 1.0, 1.0, 0.0, 0.0, + : 2.0, 1.0, 1.0, 0.0, + : 1.0, 1.0, 0.0, 1.0, + : 1.0, 2.0, 0.0, 0.0, + : 1.0, 2.0, 1.0, 0.0, + : 2.0, 2.0, 0.0, 1.0 / + +c data coeff3 / -0.1, 1.0, 0.0, 0.0, +c : 0.99, 1.0, 1.0, 0.0, +c : 1.0E-4, 1.0, 1.0, 1.0, +c : -1.0E-9, 1.0, 2.0, 1.0, +c : -0.1, 2.0, 0.0, 0.0, +c : 0.99, 2.0, 0.0, 1.0, +c : 1.0E-4, 2.0, 1.0, 1.0, +c : -1.0E-9, 2.0, 1.0, 2.0 / + + data coeff3 / -0.1, 1.0, 0.0, 0.0, + : 0.99, 1.0, 1.0, 0.0, + : 1.0E-4, 1.0, 1.0, 1.0, + : -0.1, 2.0, 0.0, 0.0, + : 0.99, 2.0, 0.0, 1.0, + : 1.0E-4, 2.0, 1.0, 1.0 / + + + data coeff_1d / 1.0, 1.0, 0.0, + : 2.0, 1.0, 1.0 / + + data lbnd / -10.0D2, -10.0D2 / + data ubnd / 10.0D2, 10.0D2 / + + + + status = sai__ok + call ast_begin( status ) + +c call ast_watchmemory( 131 ) + + acc = 1.0D-7 + errlim = 1000*acc + maxacc = 1.0D-3 + maxord = 10 + + pm = ast_polymap( 2, 2, 4, coeff, 0, coeff, ' ', status ) + pm2 = ast_polytran( pm, .FALSE., acc, maxacc, maxord, lbnd, + : ubnd, status ) + + xin( 1 ) = 1.0d0 + xin( 2 ) = 100.0d0 + xin( 3 ) = -50.0d0 + yin( 1 ) = 1.0d0 + yin( 2 ) = 100.0d0 + yin( 3 ) = -50.0d0 + + call ast_tran2( pm2, 3, xin, yin, .true., xout, yout, + : status ) + call ast_tran2( pm2, 3, xout, yout, .false., xin2, yin2, + : status ) + + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + call stopit( 1, status ) + endif + if( abs( yin( i ) - yin2( i ) ) .gt. errlim ) then + call stopit( 2, status ) + endif + end do + + + + call ast_setl( pm2, 'IterInverse', .TRUE., status ) + call ast_tran2( pm2, 3, xout, yout, .false., xin2, yin2, + : status ) + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + call stopit( 1001, status ) + endif + if( abs( yin( i ) - yin2( i ) ) .gt. errlim ) then + call stopit( 1002, status ) + endif + end do + + + + + + pm = ast_polymap( 1, 1, 2, coeff_1d, 0, coeff_1d, ' ', status ) + pm2 = ast_polytran( pm, .FALSE., acc, maxacc, maxord, lbnd, + : ubnd, status ) + + xin( 1 ) = 1.0d0 + xin( 2 ) = 100.0d0 + xin( 3 ) = -50.0d0 + + call ast_tran1( pm2, 3, xin, .true., xout, status ) + call ast_tran1( pm2, 3, xout, .false., xin2, status ) + + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + call stopit( 3, status ) + endif + end do + + call ast_setl( pm2, 'IterInverse', .TRUE., status ) + call ast_tran1( pm2, 3, xout, .false., xin2, status ) + + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + call stopit( 3001, status ) + endif + end do + + + + + pm = ast_polymap( 2, 2, 6, coeff2, 0, coeff2, ' ', status ) + pm2 = ast_polytran( pm, .FALSE., acc, maxacc, maxord, lbnd, + : ubnd, status ) + + xin( 1 ) = 1.0d0 + xin( 2 ) = 100.0d0 + xin( 3 ) = -50.0d0 + yin( 1 ) = 1.0d0 + yin( 2 ) = 100.0d0 + yin( 3 ) = -50.0d0 + + call ast_tran2( pm2, 3, xin, yin, .true., xout, yout, + : status ) + call ast_tran2( pm2, 3, xout, yout, .false., xin2, yin2, + : status ) + + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + call stopit( 4, status ) + endif + if( abs( yin( i ) - yin2( i ) ) .gt. errlim ) then + call stopit( 5, status ) + endif + end do + + call ast_setl( pm2, 'IterInverse', .TRUE., status ) + call ast_tran2( pm2, 3, xout, yout, .false., xin2, yin2, + : status ) + + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + call stopit( 4001, status ) + endif + if( abs( yin( i ) - yin2( i ) ) .gt. errlim ) then + call stopit( 5001, status ) + endif + end do + + + + + + + + pm = ast_polymap( 2, 2, 6, coeff3, 0, coeff3, ' ', status ) + pm2 = ast_polytran( pm, .FALSE., acc, maxacc, maxord, lbnd, + : ubnd, status ) + + xin( 1 ) = 1.0d0 + xin( 2 ) = 100.0d0 + xin( 3 ) = -50.0d0 + yin( 1 ) = 1.0d0 + yin( 2 ) = 100.0d0 + yin( 3 ) = -50.0d0 + + call ast_tran2( pm2, 3, xin, yin, .true., xout, yout, + : status ) + call ast_tran2( pm2, 3, xout, yout, .false., xin2, yin2, + : status ) + + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + write(*,*) i, xin( i ), xin2( i ), errlim + call stopit( 6, status ) + endif + if( abs( yin( i ) - yin2( i ) ) .gt. errlim ) then + call stopit( 7, status ) + endif + end do + + + call ast_setl( pm2, 'IterInverse', .TRUE., status ) + call ast_tran2( pm2, 3, xout, yout, .false., xin2, yin2, + : status ) + + do i = 1, 3 + if( abs( xin( i ) - xin2( i ) ) .gt. errlim ) then + write(*,*) i, xin( i ), xin2( i ), errlim + call stopit( 6001, status ) + endif + if( abs( yin( i ) - yin2( i ) ) .gt. errlim ) then + call stopit( 7001, status ) + endif + end do + + + + + + + + + + call ast_end( status ) + call ast_activememory( 'testpolymap' ); + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All PolyMap tests passed' + else + write(*,*) 'PolyMap tests failed' + end if + + end + + + subroutine stopit( i, status ) + implicit none + include 'SAE_PAR' + integer i, status + if( status .eq. sai__ok ) then + write( *,* ) 'Error ',i + status = sai__error + end if + end diff --git a/ast_tester/testrate.f b/ast_tester/testrate.f new file mode 100644 index 0000000..4803294 --- /dev/null +++ b/ast_tester/testrate.f @@ -0,0 +1,344 @@ + program testrate + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, m, outp(4), inp(4), c1, c2, c3, c4 + double precision at(4), r, mat(4), b1(2), b2(2), a1(2), + : a2(4) + + status = sai__ok + + at(1) = 10.0D0 + at(2) = 1.2D6 + +* UnitMap + m = ast_unitmap( 2, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 1, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 2, r, status ) + + call ast_invert( m, status ) + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 202, r, status ) + +* ZoomMap + m = ast_zoommap( 2, 2.0D0, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 2.0D0 ) call stopit( 3, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 4, r, status ) + + call ast_invert( m, status ) + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 0.5D0 ) call stopit( 402, r, status ) + +* MatrixMap + m = ast_matrixmap( 2, 2, 2, mat, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 5, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 6, r, status ) + + call ast_invert( m, status ) + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 602, r, status ) + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 603, r, status ) + + mat(1)= -2.0D0 + mat(2)= 1.5D0 + m = ast_matrixmap( 2, 2, 1, mat, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. -2.0D0 ) call stopit( 7, r, status ) + r = ast_rate( m, at, 2, 2, status ) + if( r .ne. 1.5D0 ) call stopit( 8, r, status ) + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 9, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 10, r, status ) + + call ast_invert( m, status ) + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. -0.5D0 ) call stopit( 1002, r, status ) + + mat(1)= 1.2D0 + mat(2)= 1.6D0 + mat(3)= -1.6D0 + mat(4)= 2.2D0 + m = ast_matrixmap( 2, 2, 0, mat, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.2D0 ) call stopit( 11, r, status ) + r = ast_rate( m, at, 2, 2, status ) + if( r .ne. 2.2D0 ) call stopit( 12, r, status ) + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 1.6D0 ) call stopit( 13, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. -1.6D0 ) call stopit( 14, r, status ) + + call ast_invert( m, status ) + + r = ast_rate( m, at, 1, 1, status ) + if( abs( r - 0.423076923 ) .gt. 1.0E-6 ) call stopit( 15, r, + : status ) + + r = ast_rate( m, at, 2, 2, status ) + if( abs( r - 0.230769231 ) .gt. 1.0E-6 ) call stopit( 16, r, + : status ) + + r = ast_rate( m, at, 1, 2, status ) + if( abs( r + 0.307692308 ) .gt. 1.0E-6 ) call stopit( 17, r, + : status ) + + r = ast_rate( m, at, 2, 1, status ) + if( abs( r - 0.307692308 ) .gt. 1.0E-6 ) call stopit( 18, r, + : status ) + + +* ShiftMap + mat(1) = -1.2D0 + mat(2) = 1.2D0 + m = ast_shiftmap( 2, mat, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 20, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 21, r, status ) + + call ast_invert( m, status ) + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 23, r, status ) + + +* WinMap + a1( 1 ) = 0.0D0 + a1( 2 ) = 0.0D0 + a2( 1 ) = 1.0D0 + a2( 2 ) = 1.0D0 + b1( 1 ) = 0.5D0 + b1( 2 ) = 0.5D0 + b2( 1 ) = 2.5D0 + b2( 2 ) = 2.5D0 + m = ast_winmap( 2, a1, a2, b1, b2, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 2.0D0 ) call stopit( 24, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 25, r, status ) + + r = ast_rate( m, at, 2, 2, status ) + if( r .ne. 2.0D0 ) call stopit( 26, r, status ) + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 27, r, status ) + + + call ast_invert( m, status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 0.5D0 ) call stopit( 29, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 30, r, status ) + + r = ast_rate( m, at, 2, 2, status ) + if( r .ne. 0.5D0 ) call stopit( 31, r, status ) + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 32, r, status ) + + + +* PermMap + outp(1)=2 + outp(2)=1 + inp(1)=1 + inp(2)=2 + m = ast_permmap( 2, inp, 2, outp, 0.0D0, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 34, r, status ) + + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 35, r, status ) + + r = ast_rate( m, at, 2, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 37, r, status ) + + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 1.0D0 ) call stopit( 38, r, status ) + + call ast_invert( m, status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 1.0D0 ) call stopit( 40, r, status ) + + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 41, r, status ) + + r = ast_rate( m, at, 2, 2, status ) + if( r .ne. 1.0D0 ) call stopit( 43, r, status ) + + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 44, r, status ) + +* TranMap + a1( 1 ) = 0.0D0 + a1( 2 ) = 0.0D0 + a2( 1 ) = 1.0D0 + a2( 2 ) = 1.0D0 + b1( 1 ) = 0.5D0 + b1( 2 ) = 0.5D0 + b2( 1 ) = 2.5D0 + b2( 2 ) = 2.5D0 + c1 = ast_winmap( 2, a1, a2, b1, b2, ' ', status ) + + mat(1)= 1.2D0 + mat(2)= 1.6D0 + mat(3)= -1.6D0 + mat(4)= 2.2D0 + c2 = ast_matrixmap( 2, 2, 0, mat, ' ', status ) + + m = ast_tranmap( c1, c2, ' ', status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 2.0D0 ) call stopit( 46, r, status ) + r = ast_rate( m, at, 2, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 47, r, status ) + + r = ast_rate( m, at, 2, 2, status ) + if( r .ne. 2.0D0 ) call stopit( 48, r, status ) + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 49, r, status ) + + call ast_invert( m, status ) + + r = ast_rate( m, at, 1, 1, status ) + if( abs( r - 0.423076923 ) .gt. 1.0E-6 ) call stopit( 51, r, + : status ) + r = ast_rate( m, at, 2, 2, status ) + if( abs( r - 0.230769231 ) .gt. 1.0E-6 ) call stopit( 52, r, + : status ) + r = ast_rate( m, at, 1, 2, status ) + if( abs( r + 0.307692308 ) .gt. 1.0E-6 ) call stopit( 53, r, + : status ) + r = ast_rate( m, at, 2, 1, status ) + if( abs( r - 0.307692308 ) .gt. 1.0E-6 ) call stopit( 54, r, + : status ) + + +* CmpMap + mat(1) = -1.0D0 + mat(2) = 1.0D0 + c1 = ast_shiftmap( 2, mat, ' ', status ) + mat(1)= 1.0D0 + mat(2)= 2.0D0 + mat(3)= -2.0D0 + mat(4)= 3.0D0 + c2 = ast_matrixmap( 2, 2, 0, mat, ' ', status ) + c3 = ast_cmpmap( c1, c2, 0, ' ', status ) + + outp(1) = 3 + outp(2) = 4 + outp(3) = 1 + outp(4) = 2 + inp(1) = 3 + inp(2) = 4 + inp(3) = 1 + inp(4) = 2 + c1 = ast_permmap( 4, inp, 4, outp, 0.0D0, ' ', status ) + c2 = ast_ZoomMap( 4, 0.25D0, ' ', status ) + call ast_invert( c2, status ) + + c4 = ast_cmpmap( c1, c2, 1, ' ', status ) + call ast_invert( c4, status ) + + m = ast_cmpmap( c3, c4, 1, ' ', status ) + + call ast_invert( c2, status ) + call ast_invert( c3, status ) + call ast_invert( c4, status ) + + at(1) = 1.0D0 + at(2) = 2.0D0 + at(3) = 3.0D0 + at(4) = 4.0D0 + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 55, r, status ) + + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 56, r, status ) + + r = ast_rate( m, at, 1, 3, status ) + if( abs( r - 0.25D0 ) .gt. 1.0D-6 ) call stopit( 57, r, status ) + + r = ast_rate( m, at, 1, 4, status ) + if( r .ne. 0.5D0 ) call stopit( 58, r, status ) + + r = ast_rate( m, at, 3, 1, status ) + if( r .ne. 0.25D0 ) call stopit( 59, r, status ) + + r = ast_rate( m, at, 3, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 60, r, status ) + + r = ast_rate( m, at, 3, 3, status ) + if( r .ne. 0.0D0 ) call stopit( 61, r, status ) + + r = ast_rate( m, at, 3, 4, status ) + if( r .ne. 0.0D0 ) call stopit( 62, r, status ) + + + call ast_invert( m, status ) + + r = ast_rate( m, at, 1, 1, status ) + if( r .ne. 0.0D0 ) call stopit( 63, r, status ) + + r = ast_rate( m, at, 1, 2, status ) + if( r .ne. 0.0D0 ) call stopit( 64, r, status ) + + r = ast_rate( m, at, 1, 3, status ) + if( r .ne. 4.0D0 ) call stopit( 65, r, status ) + + r = ast_rate( m, at, 1, 4, status ) + if( r .ne. 0.0D0 ) call stopit( 66, r, status ) + + r = ast_rate( m, at, 3, 1, status ) + if( abs( r - 12.0D0/7.0D0 ) .gt. 1.0D-6 ) + : call stopit( 67, r, status ) + + r = ast_rate( m, at, 3, 2, status ) + if( abs( r - (-8.0D0/7.0D0) ) .gt. 1.0D-6 ) + : call stopit( 68, r, status ) + + r = ast_rate( m, at, 3, 3, status ) + if( r .ne. 0.0D0 ) call stopit( 69, r, status ) + + r = ast_rate( m, at, 3, 4, status ) + if( r .ne. 0.0D0 ) call stopit( 70, r, status ) + + + + + if( status .eq. sai__ok ) then + write(*,*) 'All AST_RATE tests passed' + else + write(*,*) 'AST_RATE tests failed' + end if + + end + + + subroutine stopit( i, r, status ) + implicit none + include 'SAE_PAR' + integer i, status + double precision r + if( status .eq. sai__ok ) then + write( *,* ) 'Error ',i,': ',r + status = sai__error + end if + end diff --git a/ast_tester/testratemap.f b/ast_tester/testratemap.f new file mode 100644 index 0000000..720a4b9 --- /dev/null +++ b/ast_tester/testratemap.f @@ -0,0 +1,148 @@ + program testratemap + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, m, outp(4), inp(4), c1, c2, c3, c4, rm + double precision at(4), r, mat(4), b1(2), b2(2), a1(2), + : a2(4) + + status = sai__ok + + at(1) = 10.0D0 + at(2) = 1.2D6 + + mat(1) = -1.0D0 + mat(2) = 1.0D0 + c1 = ast_shiftmap( 2, mat, ' ', status ) + mat(1)= 1.0D0 + mat(2)= 2.0D0 + mat(3)= -2.0D0 + mat(4)= 3.0D0 + c2 = ast_matrixmap( 2, 2, 0, mat, ' ', status ) + c3 = ast_cmpmap( c1, c2, 0, ' ', status ) + + outp(1) = 3 + outp(2) = 4 + outp(3) = 1 + outp(4) = 2 + inp(1) = 3 + inp(2) = 4 + inp(3) = 1 + inp(4) = 2 + c1 = ast_permmap( 4, inp, 4, outp, 0.0D0, ' ', status ) + c2 = ast_ZoomMap( 4, 0.25D0, ' ', status ) + call ast_invert( c2, status ) + + c4 = ast_cmpmap( c1, c2, 1, ' ', status ) + call ast_invert( c4, status ) + + m = ast_cmpmap( c3, c4, 1, ' ', status ) + + call ast_invert( c2, status ) + call ast_invert( c3, status ) + call ast_invert( c4, status ) + + rm = ast_ratemap( m, 1, 1, ' ', status ) + at(1) = 1.0D0 + at(2) = 2.0D0 + at(3) = 3.0D0 + at(4) = 4.0D0 + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 1, r, status ) + + if( .not. ast_getl( rm, 'TranForward', status ) ) call stopit( 2, + : r, status ) + + if( ast_getl( rm, 'TranInverse', status ) ) call stopit( 3, r, + : status ) + + rm = ast_ratemap( m, 1, 2, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 4, r, status ) + + rm = ast_ratemap( m, 1, 3, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( abs( r - 0.25D0 ) .gt. 1.0D-6 ) call stopit( 5, r, status ) + + rm = ast_ratemap( m, 1, 4, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.5D0 ) call stopit( 6, r, status ) + + rm = ast_ratemap( m, 3, 1, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.25D0 ) call stopit( 7, r, status ) + + rm = ast_ratemap( m, 3, 2, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 8, r, status ) + + rm = ast_ratemap( m, 3, 3, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 9, r, status ) + + rm = ast_ratemap( m, 3, 4, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 10, r, status ) + + call ast_invert( m, status ) + + rm = ast_ratemap( m, 1, 1, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 11, r, status ) + + rm = ast_ratemap( m, 1, 2, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 12, r, status ) + + rm = ast_ratemap( m, 1, 3, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 4.0D0 ) call stopit( 13, r, status ) + + rm = ast_ratemap( m, 1, 4, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 14, r, status ) + + rm = ast_ratemap( m, 3, 1, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( abs( r - 12.0D0/7.0D0 ) .gt. 1.0E-6 ) + : call stopit( 15, r, status ) + + rm = ast_ratemap( m, 3, 2, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( abs( r - (-8.0D0/7.0D0) ) .gt. 1.0E-6 ) + : call stopit( 16, r, status ) + + rm = ast_ratemap( m, 3, 3, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 17, r, status ) + + rm = ast_ratemap( m, 3, 4, ' ', status ) + call ast_trann( rm, 1, 4, 1, at, 1, 1, 1, r, status ) + if( r .ne. 0.0D0 ) call stopit( 18, r, status ) + + + + + + + if( status .eq. sai__ok ) then + write(*,*) 'All RateMap tests passed' + else + write(*,*) 'RateMap tests failed' + end if + + end + + + subroutine stopit( i, r, status ) + implicit none + include 'SAE_PAR' + integer i, status + double precision r + if( status .eq. sai__ok ) then + write( *,* ) 'Error ',i,': ',r + status = sai__error + end if + end diff --git a/ast_tester/testrebin.f b/ast_tester/testrebin.f new file mode 100644 index 0000000..5ec976f --- /dev/null +++ b/ast_tester/testrebin.f @@ -0,0 +1,4176 @@ + program testrebin + implicit none + include 'SAE_PAR' + external test1, test2, test3, test4, test5, test6, test7, test8, + : test9 + integer status + status = sai__ok + + call ast_begin( status ) + + call tester( test7, status ) + call tester( test8, status ) + call tester( test9, status ) + call tester( test1, status ) + call tester( test2, status ) + call tester( test3, status ) + call tester( test4, status ) + call tester( test5, status ) + call tester( test6, status ) + + call ast_end( status ) + call ast_flushmemory( 1 ) + + + if( status .eq. sai__ok ) then + write(*,*) 'All AST_REBIN tests passed' + else + write(*,*) 'AST_REBIN tests failed' + end if + + end + + + + +* +* Do a given test with a all data types and spread functions. +* + subroutine tester( testfun, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + include 'CNF_PAR' + + integer m, lbnd_in(10), ubnd_in(10), ipin, ipin_var, + : lbnd_out(10), ubnd_out(10), lbnd(10), ubnd(10), ipout, + : ipout_var, status, nin, nout, i, nel_in, nel_out, + : spreads(6), j + character types(3)*15, name*20 + double precision tol, params(20) + external testfun + + data types/ '_DOUBLE', '_REAL', '_INTEGER' / + + data spreads/ AST__SINC, AST__NEAREST, AST__LINEAR, + : AST__SINCSINC, AST__SINCCOS, AST__SINCGAUSS / + + + if( status .ne. sai__ok ) return + +* Get the scalar properties of the test. + call testfun( 0, name, types(1), + : lbnd_in, ubnd_in, ipin, ipin_var, + : lbnd_out, ubnd_out, ipout, ipout_var, + : lbnd, ubnd, m, params, tol, j, status ) + +* Get the number of input and output axes. + nin = ast_geti( m, 'Nin', status ) + nout = ast_geti( m, 'Nout', status ) + +* Get no. of pixels in entire input array. + nel_in = 1 + do i = 1, nin + nel_in = nel_in*( ubnd_in( i ) - lbnd_in( i ) + 1 ) + end do + +* Get no. of pixels in entire output array. + nel_out = 1 + do i = 1, nout + nel_out = nel_out*( ubnd_out( i ) - lbnd_out( i ) + 1 ) + end do + +* Loop round all data types. + do i = 1, 3 + +* Allocate memory for input and output data and variance arrays + call psx_calloc( nel_in, types(i), ipin, status ) + call psx_calloc( nel_in, types(i), ipin_var, status ) + + call psx_calloc( nel_out, types(i), ipout, status ) + call psx_calloc( nel_out, types(i), ipout_var, status ) + +* Loop round all spread functions + do j = 1, 6 + +* Get the scalar properties of the test. This may change the Mapping. + call testfun( 0, name, types(i), + : lbnd_in, ubnd_in, ipin, ipin_var, + : lbnd_out, ubnd_out, ipout, ipout_var, + : lbnd, ubnd, m, params, tol, spreads(j), status ) + +* Fill the input data and variance arrays using the supplied function. + call testfun( 1, name, types(i), + : lbnd_in, ubnd_in, ipin, ipin_var, + : lbnd_out, ubnd_out, ipout, ipout_var, + : lbnd, ubnd, m, params, tol, spreads(j), + : status ) + +* Rebin the input data using the AST function appropriate to the +* supplied data type. + if( types(i) .eq. '_REAL' ) then + call ast_rebinr( m, 0.0D0, nin, lbnd_in, ubnd_in, + : %val( cnf_pval( ipin )), %val( cnf_pval(ipin_var )), + : spreads(j), params, + : AST__USEBAD+AST__USEVAR, tol, 100, VAL__BADR, + : nout, lbnd_out, ubnd_out, + : lbnd, ubnd, %val( cnf_pval( ipout )), + : %val( cnf_pval( ipout_var )), status ) + + else if( types(i) .eq. '_DOUBLE' ) then + call ast_rebind( m, 0.0D0, nin, lbnd_in, ubnd_in, + : %val( cnf_pval( ipin )), %val( cnf_pval(ipin_var )), + : spreads(j), params, + : AST__USEBAD+AST__USEVAR, tol, 100, VAL__BADD, + : nout, lbnd_out, ubnd_out, + : lbnd, ubnd, %val( cnf_pval( ipout ) ), + : %val( cnf_pval( ipout_var )), status ) + + else if( types(i) .eq. '_INTEGER' ) then + call ast_rebini( m, 0.0D0, nin, lbnd_in, ubnd_in, + : %val( cnf_pval( ipin )), %val( cnf_pval(ipin_var )), + : spreads(j), params, + : AST__USEBAD+AST__USEVAR, tol, 100, VAL__BADI, + : nout, lbnd_out, ubnd_out, + : lbnd, ubnd, %val( cnf_pval( ipout )), + : %val( cnf_pval( ipout_var )), status ) + + else if( status .eq. sai__ok ) then + status = SAI__ERROR + call msg_setc( 'T', types(i) ) + call err_rep( ' ', 'Bad data type (^T) supplied to '// + : 'rebin', status ) + end if + +* Call the supplied function to test the results. + call testfun( 2, name, types(i), + : lbnd_in, ubnd_in, ipin, ipin_var, + : lbnd_out, ubnd_out, ipout, ipout_var, + : lbnd, ubnd, m, params, tol, + : spreads(j), status ) + +* Report the data type and spread function if an error occurred, and +* abort. + if( status .ne. sai__ok ) then + call msg_seti( 'sf', j ) + call msg_setc( 'dt', types( i ) ) + call msg_setc( 't', name ) + call err_rep( ' ', '^t failed: Spread function ^sf '// + : 'data type ^dt', status ) + go to 999 + end if + + end do + +* Free resources. + call psx_free( ipout, status ) + call psx_free( ipout_var, status ) + call psx_free( ipin, status ) + call psx_free( ipin_var, status ) + end do + + 999 continue + + end + + LOGICAL FUNCTION EQUALB( A, B ) + IMPLICIT NONE + BYTE A, B + EQUALB = ( A .EQ. B ) + END + + LOGICAL FUNCTION EQUALD( A, B ) + IMPLICIT NONE + INCLUDE 'PRM_PAR' + DOUBLE PRECISION A, B + IF( A .NE. 0.0D0 .AND. B .NE. 0.0D0 ) THEN + EQUALD = ( ABS( A - B ) .LE. 1.0E9*ABS( A + B )*VAL__EPSD ) + ELSE + EQUALD = ( ABS( A + B ) .LE. 1.0D-11 ) + END IF + + END + + LOGICAL FUNCTION MYEQUALD( A, B ) + IMPLICIT NONE + DOUBLE PRECISION A, B + LOGICAL EQUALD + MYEQUALD = EQUALD( A, B ) + END + + LOGICAL FUNCTION EQUALI( A, B ) + IMPLICIT NONE + INTEGER A, B + EQUALI = ( A .EQ. B ) + + END + + LOGICAL FUNCTION EQUALR( A, B ) + IMPLICIT NONE + INCLUDE 'PRM_PAR' + REAL A, B + + IF( A .NE. 0.0 .AND. B .NE. 0.0 ) THEN + EQUALR = ( ABS( A - B ) .LE. 50.0*ABS( A + B )*VAL__EPSR ) + ELSE + EQUALR = ( ABS( A + B ) .LE. 1.0E-11 ) + END IF + + END + + LOGICAL FUNCTION EQUALW( A, B ) + IMPLICIT NONE + INTEGER*2 A, B + EQUALW = ( A .EQ. B ) + END + + + + +* ----------------------------------------------- +* Test 7 +* + + SUBROUTINE TEST7( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST7' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST7R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST7D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST7I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST7', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST7D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, NZ + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), SUM, KT + DOUBLE PRECISION TOL, PARAMS(*), K + LOGICAL EQUALD, MYEQUALD, GOOD + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_SHIFTMAP( 1, 1.5D0, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = 0 + IN_VAR( I - LBND_IN(1) + 1 ) = K + END DO + IN( 14 - LBND_IN(1) + 1 ) = K + +* Otherwise check output data and variance arrays look right. + ELSE + + SUM = 0 + DO I = LBND_OUT(1), UBND_OUT(1) + IF( OUT( I - LBND_OUT(1) + 1) .NE. VAL__BADD ) THEN + SUM = SUM + OUT( I - LBND_OUT(1) + 1) + END IF + END DO + + KT = K + + + IF( 'D' .EQ. 'R' .OR. 'D' .EQ. 'D' ) THEN + GOOD = EQUALD( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 3 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST7D Data sum is ^S should be ^K', + : STATUS ) + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + NZ = 0 + DO I = LBND_OUT(1), UBND_OUT(1) + IF( OUT( I - LBND_OUT(1) + 1) .NE. 0 .AND. + : OUT( I - LBND_OUT(1) + 1) .NE. VAL__BADD ) THEN + IF( NZ .EQ. 0 ) THEN + NZ = NZ + 1 + IF( OUT( I - LBND_OUT(1) + 1) .NE. KT ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL MSG_SETD( 'K', dble( KT ) ) + CALL ERR_REP( ' ', 'TEST7D ^I: ^D1 ^K', + : STATUS ) + END IF + ELSE + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST7D ^I: ^D1', + : STATUS ) + END IF + END IF + END DO + + ELSE + DO I = 0, 3 + IF( .NOT. EQUALD( OUT( 15 - I - LBND_OUT(1) + 1 ), + : OUT( 16 + I - LBND_OUT(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I1', 15 - I ) + CALL MSG_SETI( 'I2', 16 + I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( 15 - I - LBND_OUT(1) + 1))) + CALL MSG_SETD( 'D2', + : DBLE( OUT( 16 + I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST7D ^I1 (^D1) != '// + : '^I2 (^D2)', STATUS ) + END IF + END DO + END IF + + END IF + + END + + + SUBROUTINE TEST7I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, NZ + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), SUM, KT + DOUBLE PRECISION TOL, PARAMS(*), K + LOGICAL EQUALI, MYEQUALD, GOOD + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_SHIFTMAP( 1, 1.5D0, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = 0 + IN_VAR( I - LBND_IN(1) + 1 ) = K + END DO + IN( 14 - LBND_IN(1) + 1 ) = K + +* Otherwise check output data and variance arrays look right. + ELSE + + SUM = 0 + DO I = LBND_OUT(1), UBND_OUT(1) + IF( OUT( I - LBND_OUT(1) + 1) .NE. VAL__BADI ) THEN + SUM = SUM + OUT( I - LBND_OUT(1) + 1) + END IF + END DO + + KT = K + + + IF( 'I' .EQ. 'R' .OR. 'I' .EQ. 'D' ) THEN + GOOD = EQUALI( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 3 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST7I Data sum is ^S should be ^K', + : STATUS ) + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + NZ = 0 + DO I = LBND_OUT(1), UBND_OUT(1) + IF( OUT( I - LBND_OUT(1) + 1) .NE. 0 .AND. + : OUT( I - LBND_OUT(1) + 1) .NE. VAL__BADI ) THEN + IF( NZ .EQ. 0 ) THEN + NZ = NZ + 1 + IF( OUT( I - LBND_OUT(1) + 1) .NE. KT ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL MSG_SETD( 'K', dble( KT ) ) + CALL ERR_REP( ' ', 'TEST7I ^I: ^D1 ^K', + : STATUS ) + END IF + ELSE + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST7I ^I: ^D1', + : STATUS ) + END IF + END IF + END DO + + ELSE + DO I = 0, 3 + IF( .NOT. EQUALI( OUT( 15 - I - LBND_OUT(1) + 1 ), + : OUT( 16 + I - LBND_OUT(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I1', 15 - I ) + CALL MSG_SETI( 'I2', 16 + I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( 15 - I - LBND_OUT(1) + 1))) + CALL MSG_SETD( 'D2', + : DBLE( OUT( 16 + I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST7I ^I1 (^D1) != '// + : '^I2 (^D2)', STATUS ) + END IF + END DO + END IF + + END IF + + END + + + SUBROUTINE TEST7R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, NZ + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), SUM, KT + DOUBLE PRECISION TOL, PARAMS(*), K + LOGICAL EQUALR, MYEQUALD, GOOD + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_SHIFTMAP( 1, 1.5D0, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = 0 + IN_VAR( I - LBND_IN(1) + 1 ) = K + END DO + IN( 14 - LBND_IN(1) + 1 ) = K + +* Otherwise check output data and variance arrays look right. + ELSE + + SUM = 0 + DO I = LBND_OUT(1), UBND_OUT(1) + IF( OUT( I - LBND_OUT(1) + 1) .NE. VAL__BADR ) THEN + SUM = SUM + OUT( I - LBND_OUT(1) + 1) + END IF + END DO + + KT = K + + + IF( 'R' .EQ. 'R' .OR. 'R' .EQ. 'D' ) THEN + GOOD = EQUALR( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 3 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST7R Data sum is ^S should be ^K', + : STATUS ) + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + NZ = 0 + DO I = LBND_OUT(1), UBND_OUT(1) + IF( OUT( I - LBND_OUT(1) + 1) .NE. 0 .AND. + : OUT( I - LBND_OUT(1) + 1) .NE. VAL__BADR ) THEN + IF( NZ .EQ. 0 ) THEN + NZ = NZ + 1 + IF( OUT( I - LBND_OUT(1) + 1) .NE. KT ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL MSG_SETD( 'K', dble( KT ) ) + CALL ERR_REP( ' ', 'TEST7R ^I: ^D1 ^K', + : STATUS ) + END IF + ELSE + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST7R ^I: ^D1', + : STATUS ) + END IF + END IF + END DO + + ELSE + DO I = 0, 3 + IF( .NOT. EQUALR( OUT( 15 - I - LBND_OUT(1) + 1 ), + : OUT( 16 + I - LBND_OUT(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I1', 15 - I ) + CALL MSG_SETI( 'I2', 16 + I ) + CALL MSG_SETD( 'D1', + : DBLE( OUT( 15 - I - LBND_OUT(1) + 1))) + CALL MSG_SETD( 'D2', + : DBLE( OUT( 16 + I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST7R ^I1 (^D1) != '// + : '^I2 (^D2)', STATUS ) + END IF + END DO + END IF + + END IF + + END + + + + +* ----------------------------------------------- +* Test 8 +* + + SUBROUTINE TEST8( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST8' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST8R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST8D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST8I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST8', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST8D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, NZ, + : II, JJ, KK + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), SUM, KT + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(2) + LOGICAL EQUALD, GOOD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 10000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -2 + UBND_IN( 1 ) = 3 + LBND_OUT( 1 ) = -2 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -2 + UBND( 1 ) = 3 + LBND_IN( 2 ) = 0 + UBND_IN( 2 ) = 5 + LBND_OUT( 2 ) = 0 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 0 + UBND( 2 ) = 5 + SHIFTS(1) = 0.5D0 + SHIFTS(2) = -0.5D0 + M = AST_SHIFTMAP( 2, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = 0 + IN_VAR( K ) = KFAC + K = K + 1 + END DO + END DO + IN( 21 ) = KFAC + +* Otherwise check output data and variance arrays look right. + ELSE + K = 0 + SUM = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. VAL__BADD ) THEN + SUM = SUM + OUT( K ) + END IF + END DO + END DO + + KT = KFAC + + IF( 'D' .EQ. 'R' .OR. 'D' .EQ. 'D' ) THEN + GOOD = EQUALD( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 5 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST8D Data sum is ^S should be ^K', + : STATUS ) + GO TO 999 + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + NZ = 0 + K = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. 0 .AND. + : OUT( K ) .NE. VAL__BADD ) THEN + IF( NZ .EQ. 0 ) THEN + NZ = NZ + 1 + IF( OUT( K ) .NE. KT ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT( K ))) + CALL MSG_SETD( 'KT', DBLE( KT ) ) + CALL ERR_REP( ' ', 'TEST8D ^K: ^D1 ^KT', + : STATUS ) + GO TO 999 + END IF + ELSE + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT( K ))) + CALL ERR_REP( ' ', 'TEST8D ^K: ^D1', + : STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + ELSE + K = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + II = 1 - I + JJ = 5 - J + IF( II .GE. LBND_OUT(1) .AND. + : II .LE. UBND_OUT(1) .AND. + : JJ .GE. LBND_OUT(2) .AND. + : JJ .LE. UBND_OUT(2) ) THEN + KK = 6*JJ + ( II + 3 ) + + IF( .NOT. EQUALD( OUT( KK ), OUT( K ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'KK', KK ) + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT(KK) ) ) + CALL MSG_SETD( 'D2', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST8D ^KK (^D1) != '// + : '^K (^D2)', STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + END IF + + END IF + + 999 CONTINUE + + END + + + + SUBROUTINE TEST8I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, NZ, + : II, JJ, KK + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), SUM, KT + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(2) + LOGICAL EQUALI, GOOD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 10000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -2 + UBND_IN( 1 ) = 3 + LBND_OUT( 1 ) = -2 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -2 + UBND( 1 ) = 3 + LBND_IN( 2 ) = 0 + UBND_IN( 2 ) = 5 + LBND_OUT( 2 ) = 0 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 0 + UBND( 2 ) = 5 + SHIFTS(1) = 0.5D0 + SHIFTS(2) = -0.5D0 + M = AST_SHIFTMAP( 2, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = 0 + IN_VAR( K ) = KFAC + K = K + 1 + END DO + END DO + IN( 21 ) = KFAC + +* Otherwise check output data and variance arrays look right. + ELSE + K = 0 + SUM = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. VAL__BADI ) THEN + SUM = SUM + OUT( K ) + END IF + END DO + END DO + + KT = KFAC + + IF( 'I' .EQ. 'R' .OR. 'I' .EQ. 'D' ) THEN + GOOD = EQUALI( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 5 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST8I Data sum is ^S should be ^K', + : STATUS ) + GO TO 999 + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + NZ = 0 + K = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. 0 .AND. + : OUT( K ) .NE. VAL__BADI ) THEN + IF( NZ .EQ. 0 ) THEN + NZ = NZ + 1 + IF( OUT( K ) .NE. KT ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT( K ))) + CALL MSG_SETD( 'KT', DBLE( KT ) ) + CALL ERR_REP( ' ', 'TEST8I ^K: ^D1 ^KT', + : STATUS ) + GO TO 999 + END IF + ELSE + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT( K ))) + CALL ERR_REP( ' ', 'TEST8I ^K: ^D1', + : STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + ELSE + K = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + II = 1 - I + JJ = 5 - J + IF( II .GE. LBND_OUT(1) .AND. + : II .LE. UBND_OUT(1) .AND. + : JJ .GE. LBND_OUT(2) .AND. + : JJ .LE. UBND_OUT(2) ) THEN + KK = 6*JJ + ( II + 3 ) + + IF( .NOT. EQUALI( OUT( KK ), OUT( K ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'KK', KK ) + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT(KK) ) ) + CALL MSG_SETD( 'D2', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST8I ^KK (^D1) != '// + : '^K (^D2)', STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + END IF + + END IF + + 999 CONTINUE + + END + + + + SUBROUTINE TEST8R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, NZ, + : II, JJ, KK + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), SUM, KT + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(2) + LOGICAL EQUALR, GOOD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 10000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -2 + UBND_IN( 1 ) = 3 + LBND_OUT( 1 ) = -2 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -2 + UBND( 1 ) = 3 + LBND_IN( 2 ) = 0 + UBND_IN( 2 ) = 5 + LBND_OUT( 2 ) = 0 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 0 + UBND( 2 ) = 5 + SHIFTS(1) = 0.5D0 + SHIFTS(2) = -0.5D0 + M = AST_SHIFTMAP( 2, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = 0 + IN_VAR( K ) = KFAC + K = K + 1 + END DO + END DO + IN( 21 ) = KFAC + +* Otherwise check output data and variance arrays look right. + ELSE + K = 0 + SUM = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. VAL__BADR ) THEN + SUM = SUM + OUT( K ) + END IF + END DO + END DO + + KT = KFAC + + IF( 'R' .EQ. 'R' .OR. 'R' .EQ. 'D' ) THEN + GOOD = EQUALR( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 5 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST8R Data sum is ^S should be ^K', + : STATUS ) + GO TO 999 + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + NZ = 0 + K = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. 0 .AND. + : OUT( K ) .NE. VAL__BADR ) THEN + IF( NZ .EQ. 0 ) THEN + NZ = NZ + 1 + IF( OUT( K ) .NE. KT ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT( K ))) + CALL MSG_SETD( 'KT', DBLE( KT ) ) + CALL ERR_REP( ' ', 'TEST8R ^K: ^D1 ^KT', + : STATUS ) + GO TO 999 + END IF + ELSE + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT( K ))) + CALL ERR_REP( ' ', 'TEST8R ^K: ^D1', + : STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + ELSE + K = 0 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + II = 1 - I + JJ = 5 - J + IF( II .GE. LBND_OUT(1) .AND. + : II .LE. UBND_OUT(1) .AND. + : JJ .GE. LBND_OUT(2) .AND. + : JJ .LE. UBND_OUT(2) ) THEN + KK = 6*JJ + ( II + 3 ) + + IF( .NOT. EQUALR( OUT( KK ), OUT( K ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'KK', KK ) + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'D1', DBLE( OUT(KK) ) ) + CALL MSG_SETD( 'D2', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST8R ^KK (^D1) != '// + : '^K (^D2)', STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + END IF + + END IF + + 999 CONTINUE + + END + + + + + +* ----------------------------------------------- +* Test 9 +* + + SUBROUTINE TEST9( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST9' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST9R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST9D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST9I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST9', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST9D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), KT, SUM + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(3), G(3), W + LOGICAL EQUALD, GOOD, MYEQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 10000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 0 + UBND_IN( 1 ) = 6 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 6 + LBND( 1 ) = 0 + UBND( 1 ) = 6 + LBND_IN( 2 ) = 0 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 0 + UBND_OUT( 2 ) = 6 + LBND( 2 ) = 0 + UBND( 2 ) = 6 + LBND_IN( 3 ) = 0 + UBND_IN( 3 ) = 6 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 6 + LBND( 3 ) = 0 + UBND( 3 ) = 6 + + IF( SPREAD .EQ. AST__NEAREST ) THEN + SHIFTS(1) = 1.7D0 + SHIFTS(2) = 2.1D0 + SHIFTS(3) = -1.2D0 + ELSE + SHIFTS(1) = 0.5D0 + SHIFTS(2) = 0.0D0 + SHIFTS(3) = -0.5D0 + END IF + + M = AST_SHIFTMAP( 3, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = 0 + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + IN( 172 ) = KFAC + +* Otherwise check output data and variance arrays look right. + ELSE + K = 0 + SUM = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. VAL__BADD ) THEN + SUM = SUM + OUT( K ) + END IF + END DO + END DO + END DO + + KT = KFAC + + IF( 'D' .EQ. 'R' .OR. 'D' .EQ. 'D' ) THEN + GOOD = EQUALD( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 5 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST9D Data sum is ^S should be ^K', + : STATUS ) + GO TO 999 + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + K = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( K .EQ. 139 ) THEN + IF( .NOT. EQUALD( OUT(K), KT ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', DBLE( KT ) ) + CALL MSG_SETD( 'O', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST9D El. 139 is '// + : '^O should be ^K', STATUS ) + GO TO 999 + END IF + ELSE + IF( .NOT. EQUALD( OUT(K), 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'O', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST9D El. ^K is '// + : '^O should be zero', STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + END DO + ELSE + + G(1) = 0.0 + G(2) = 0.0 + G(3) = 0.0 + W = 0.0 + K = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + G(1) = G(1) + DBLE( I*OUT( k ) ) + G(2) = G(2) + DBLE( J*OUT( K ) ) + G(3) = G(3) + DBLE( L*OUT( K ) ) + W = W + DBLE( OUT( K ) ) + END DO + END DO + END DO + + IF( .NOT. MYEQUALD( G(1)/W, 3.5D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(1)/W ) + CALL ERR_REP( ' ', 'TEST9D Mean X is ^A '// + : ' should be 3.5', STATUS ) + GO TO 999 + ELSE IF( .NOT. MYEQUALD( G(2)/W, 3.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(2)/W ) + CALL ERR_REP( ' ', 'TEST9D Mean Y is ^A '// + : ' should be 3.0', STATUS ) + GO TO 999 + ELSE IF( .NOT. MYEQUALD( G(3)/W, 2.5D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(3)/W ) + CALL ERR_REP( ' ', 'TEST9D Mean Z is ^A '// + : ' should be 2.5', STATUS ) + GO TO 999 + END IF + + END IF + END IF + + 999 CONTINUE + + END + + + + SUBROUTINE TEST9I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), KT, SUM + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(3), G(3), W + LOGICAL EQUALI, GOOD, MYEQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 10000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 0 + UBND_IN( 1 ) = 6 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 6 + LBND( 1 ) = 0 + UBND( 1 ) = 6 + LBND_IN( 2 ) = 0 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 0 + UBND_OUT( 2 ) = 6 + LBND( 2 ) = 0 + UBND( 2 ) = 6 + LBND_IN( 3 ) = 0 + UBND_IN( 3 ) = 6 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 6 + LBND( 3 ) = 0 + UBND( 3 ) = 6 + + IF( SPREAD .EQ. AST__NEAREST ) THEN + SHIFTS(1) = 1.7D0 + SHIFTS(2) = 2.1D0 + SHIFTS(3) = -1.2D0 + ELSE + SHIFTS(1) = 0.5D0 + SHIFTS(2) = 0.0D0 + SHIFTS(3) = -0.5D0 + END IF + + M = AST_SHIFTMAP( 3, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = 0 + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + IN( 172 ) = KFAC + +* Otherwise check output data and variance arrays look right. + ELSE + K = 0 + SUM = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. VAL__BADI ) THEN + SUM = SUM + OUT( K ) + END IF + END DO + END DO + END DO + + KT = KFAC + + IF( 'I' .EQ. 'R' .OR. 'I' .EQ. 'D' ) THEN + GOOD = EQUALI( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 5 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST9I Data sum is ^S should be ^K', + : STATUS ) + GO TO 999 + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + K = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( K .EQ. 139 ) THEN + IF( .NOT. EQUALI( OUT(K), KT ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', DBLE( KT ) ) + CALL MSG_SETD( 'O', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST9I El. 139 is '// + : '^O should be ^K', STATUS ) + GO TO 999 + END IF + ELSE + IF( .NOT. EQUALI( OUT(K), 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'O', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST9I El. ^K is '// + : '^O should be zero', STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + END DO + ELSE + + G(1) = 0.0 + G(2) = 0.0 + G(3) = 0.0 + W = 0.0 + K = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + G(1) = G(1) + DBLE( I*OUT( k ) ) + G(2) = G(2) + DBLE( J*OUT( K ) ) + G(3) = G(3) + DBLE( L*OUT( K ) ) + W = W + DBLE( OUT( K ) ) + END DO + END DO + END DO + + IF( .NOT. MYEQUALD( G(1)/W, 3.5D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(1)/W ) + CALL ERR_REP( ' ', 'TEST9I Mean X is ^A '// + : ' should be 3.5', STATUS ) + GO TO 999 + ELSE IF( .NOT. MYEQUALD( G(2)/W, 3.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(2)/W ) + CALL ERR_REP( ' ', 'TEST9I Mean Y is ^A '// + : ' should be 3.0', STATUS ) + GO TO 999 + ELSE IF( .NOT. MYEQUALD( G(3)/W, 2.5D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(3)/W ) + CALL ERR_REP( ' ', 'TEST9I Mean Z is ^A '// + : ' should be 2.5', STATUS ) + GO TO 999 + END IF + + END IF + END IF + + 999 CONTINUE + + END + + + + SUBROUTINE TEST9R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*), KT, SUM + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(3), G(3), W + LOGICAL EQUALR, GOOD, MYEQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 10000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 0 + UBND_IN( 1 ) = 6 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 6 + LBND( 1 ) = 0 + UBND( 1 ) = 6 + LBND_IN( 2 ) = 0 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 0 + UBND_OUT( 2 ) = 6 + LBND( 2 ) = 0 + UBND( 2 ) = 6 + LBND_IN( 3 ) = 0 + UBND_IN( 3 ) = 6 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 6 + LBND( 3 ) = 0 + UBND( 3 ) = 6 + + IF( SPREAD .EQ. AST__NEAREST ) THEN + SHIFTS(1) = 1.7D0 + SHIFTS(2) = 2.1D0 + SHIFTS(3) = -1.2D0 + ELSE + SHIFTS(1) = 0.5D0 + SHIFTS(2) = 0.0D0 + SHIFTS(3) = -0.5D0 + END IF + + M = AST_SHIFTMAP( 3, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = 0 + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + IN( 172 ) = KFAC + +* Otherwise check output data and variance arrays look right. + ELSE + K = 0 + SUM = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( OUT( K ) .NE. VAL__BADR ) THEN + SUM = SUM + OUT( K ) + END IF + END DO + END DO + END DO + + KT = KFAC + + IF( 'R' .EQ. 'R' .OR. 'R' .EQ. 'D' ) THEN + GOOD = EQUALR( SUM, KT ) + ELSE + GOOD = ( ABS( SUM - KT ) .LT. 5 ) + END IF + + IF( .NOT. GOOD ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', dble( KT ) ) + CALL MSG_SETD( 'S', dble( SUM ) ) + CALL ERR_REP( ' ', 'TEST9R Data sum is ^S should be ^K', + : STATUS ) + GO TO 999 + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + K = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + IF( K .EQ. 139 ) THEN + IF( .NOT. EQUALR( OUT(K), KT ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'K', DBLE( KT ) ) + CALL MSG_SETD( 'O', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST9R El. 139 is '// + : '^O should be ^K', STATUS ) + GO TO 999 + END IF + ELSE + IF( .NOT. EQUALR( OUT(K), 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'K', K ) + CALL MSG_SETD( 'O', DBLE( OUT(K) ) ) + CALL ERR_REP( ' ', 'TEST9R El. ^K is '// + : '^O should be zero', STATUS ) + GO TO 999 + END IF + END IF + END DO + END DO + END DO + ELSE + + G(1) = 0.0 + G(2) = 0.0 + G(3) = 0.0 + W = 0.0 + K = 0 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + K = K + 1 + G(1) = G(1) + DBLE( I*OUT( k ) ) + G(2) = G(2) + DBLE( J*OUT( K ) ) + G(3) = G(3) + DBLE( L*OUT( K ) ) + W = W + DBLE( OUT( K ) ) + END DO + END DO + END DO + + IF( .NOT. MYEQUALD( G(1)/W, 3.5D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(1)/W ) + CALL ERR_REP( ' ', 'TEST9R Mean X is ^A '// + : ' should be 3.5', STATUS ) + GO TO 999 + ELSE IF( .NOT. MYEQUALD( G(2)/W, 3.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(2)/W ) + CALL ERR_REP( ' ', 'TEST9R Mean Y is ^A '// + : ' should be 3.0', STATUS ) + GO TO 999 + ELSE IF( .NOT. MYEQUALD( G(3)/W, 2.5D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'A', G(3)/W ) + CALL ERR_REP( ' ', 'TEST9R Mean Z is ^A '// + : ' should be 2.5', STATUS ) + GO TO 999 + END IF + + END IF + END IF + + 999 CONTINUE + + END + + + + + +* ----------------------------------------------- +* Test 1 +* + + SUBROUTINE TEST1( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST1' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST1R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST1D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST1I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST1', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST1D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), STATUS, M, I, SPREAD + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + LOGICAL EQUALD, IGNORE + DOUBLE PRECISION TOL, PARAMS(*), K + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_UNITMAP( 1, ' ', STATUS ) + IF( SPREAD .EQ. AST__GAUSS ) THEN + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + ELSE + PARAMS(1) = 2.0 + PARAMS(2) = 0.5 + END IF + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = I*K + IN_VAR( I - LBND_IN(1) + 1 ) = I + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + DO I = LBND_OUT(1), UBND(1) + IGNORE = ( SPREAD .EQ. AST__GAUSS .AND. + : ( I .LE. LBND_OUT(1) + 1 .OR. + : I .GE. UBND(1) - 1 ) ) + IF( IGNORE ) THEN + + ELSE IF( .NOT. EQUALD( OUT( I - LBND_OUT(1) + 1 ), + : IN( I - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1 ) ) ) + CALL MSG_SETD( 'B', DBLE( IN( I - LBND_IN(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST1D ^I: data ^V != ^B', STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( I - LBND_OUT(1) + 1 ), + : IN_VAR( I - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1) ) ) + CALL MSG_SETD( 'B', DBLE( IN_VAR(I-LBND_IN(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST1D ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END DO + DO I = UBND(1) + 1, UBND_OUT(1) + IF( .NOT. EQUALD( OUT( I - LBND_OUT(1) + 1 ), + : 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST1D ^I: ^V != 0.0', STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( I - LBND_OUT(1) + 1 ), + : 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1))) + CALL ERR_REP( ' ', 'TEST1D ^I: variance ^V != 0.0', + : STATUS ) + RETURN + END IF + END DO + + END IF + + END + + + + SUBROUTINE TEST1I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), STATUS, M, I, SPREAD + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + LOGICAL EQUALI, IGNORE + DOUBLE PRECISION TOL, PARAMS(*), K + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_UNITMAP( 1, ' ', STATUS ) + IF( SPREAD .EQ. AST__GAUSS ) THEN + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + ELSE + PARAMS(1) = 2.0 + PARAMS(2) = 0.5 + END IF + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = I*K + IN_VAR( I - LBND_IN(1) + 1 ) = I + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + DO I = LBND_OUT(1), UBND(1) + IGNORE = ( SPREAD .EQ. AST__GAUSS .AND. + : ( I .LE. LBND_OUT(1) + 1 .OR. + : I .GE. UBND(1) - 1 ) ) + IF( IGNORE ) THEN + + ELSE IF( .NOT. EQUALI( OUT( I - LBND_OUT(1) + 1 ), + : IN( I - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1 ) ) ) + CALL MSG_SETD( 'B', DBLE( IN( I - LBND_IN(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST1I ^I: data ^V != ^B', STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( I - LBND_OUT(1) + 1 ), + : IN_VAR( I - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1) ) ) + CALL MSG_SETD( 'B', DBLE( IN_VAR(I-LBND_IN(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST1I ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END DO + DO I = UBND(1) + 1, UBND_OUT(1) + IF( .NOT. EQUALI( OUT( I - LBND_OUT(1) + 1 ), + : 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST1I ^I: ^V != 0.0', STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( I - LBND_OUT(1) + 1 ), + : 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1))) + CALL ERR_REP( ' ', 'TEST1I ^I: variance ^V != 0.0', + : STATUS ) + RETURN + END IF + END DO + + END IF + + END + + + + SUBROUTINE TEST1R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), STATUS, M, I, SPREAD + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + LOGICAL EQUALR, IGNORE + DOUBLE PRECISION TOL, PARAMS(*), K + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_UNITMAP( 1, ' ', STATUS ) + IF( SPREAD .EQ. AST__GAUSS ) THEN + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + ELSE + PARAMS(1) = 2.0 + PARAMS(2) = 0.5 + END IF + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = I*K + IN_VAR( I - LBND_IN(1) + 1 ) = I + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + DO I = LBND_OUT(1), UBND(1) + IGNORE = ( SPREAD .EQ. AST__GAUSS .AND. + : ( I .LE. LBND_OUT(1) + 1 .OR. + : I .GE. UBND(1) - 1 ) ) + IF( IGNORE ) THEN + + ELSE IF( .NOT. EQUALR( OUT( I - LBND_OUT(1) + 1 ), + : IN( I - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1 ) ) ) + CALL MSG_SETD( 'B', DBLE( IN( I - LBND_IN(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST1R ^I: data ^V != ^B', STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( I - LBND_OUT(1) + 1 ), + : IN_VAR( I - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1) ) ) + CALL MSG_SETD( 'B', DBLE( IN_VAR(I-LBND_IN(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST1R ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END DO + DO I = UBND(1) + 1, UBND_OUT(1) + IF( .NOT. EQUALR( OUT( I - LBND_OUT(1) + 1 ), + : 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1))) + CALL ERR_REP( ' ', 'TEST1R ^I: ^V != 0.0', STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( I - LBND_OUT(1) + 1 ), + : 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1))) + CALL ERR_REP( ' ', 'TEST1R ^I: variance ^V != 0.0', + : STATUS ) + RETURN + END IF + END DO + + END IF + + END + + + + + +* ----------------------------------------------- +* Test 2 +* + + SUBROUTINE TEST2( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST2' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST2R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST2D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST2I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST2', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST2D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), STATUS, M, I, J, K, SPREAD + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC + LOGICAL EQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + M = AST_UNITMAP( 2, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + IF( K .LE. 4 .OR. MOD( K, 4 ) .EQ. 0 .OR. + : MOD( K, 4 ) .EQ. 3 ) THEN + IF( .NOT. EQUALD( OUT( K ), 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + CALL ERR_REP( ' ', 'TEST2D ^I: ^V != 0', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( K ), 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + CALL ERR_REP( ' ', 'TEST2D ^I: variance ^V '// + : '!= 0', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALD( OUT( K ), IN( K - 3 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K - 3 ) ) ) + CALL ERR_REP( ' ', 'TEST2D ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT( K ), IN( K-3 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K - 3 ) ) ) + CALL ERR_REP( ' ', + : 'TEST2D ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + + END IF + + END + + + + SUBROUTINE TEST2I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), STATUS, M, I, J, K, SPREAD + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC + LOGICAL EQUALI + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + M = AST_UNITMAP( 2, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + IF( K .LE. 4 .OR. MOD( K, 4 ) .EQ. 0 .OR. + : MOD( K, 4 ) .EQ. 3 ) THEN + IF( .NOT. EQUALI( OUT( K ), 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + CALL ERR_REP( ' ', 'TEST2I ^I: ^V != 0', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( K ), 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + CALL ERR_REP( ' ', 'TEST2I ^I: variance ^V '// + : '!= 0', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALI( OUT( K ), IN( K - 3 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K - 3 ) ) ) + CALL ERR_REP( ' ', 'TEST2I ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT( K ), IN( K-3 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K - 3 ) ) ) + CALL ERR_REP( ' ', + : 'TEST2I ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + + END IF + + END + + + + SUBROUTINE TEST2R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), STATUS, M, I, J, K, SPREAD + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC + LOGICAL EQUALR + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + M = AST_UNITMAP( 2, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + IF( K .LE. 4 .OR. MOD( K, 4 ) .EQ. 0 .OR. + : MOD( K, 4 ) .EQ. 3 ) THEN + IF( .NOT. EQUALR( OUT( K ), 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + CALL ERR_REP( ' ', 'TEST2R ^I: ^V != 0', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( K ), 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + CALL ERR_REP( ' ', 'TEST2R ^I: variance ^V '// + : '!= 0', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALR( OUT( K ), IN( K - 3 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K - 3 ) ) ) + CALL ERR_REP( ' ', 'TEST2R ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT( K ), IN( K-3 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K - 3 ) ) ) + CALL ERR_REP( ' ', + : 'TEST2R ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + + END IF + + END + + + + + +* ----------------------------------------------- +* Test 3 +* + + SUBROUTINE TEST3( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST3' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST3R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST3D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST3I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST3', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST3D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC + LOGICAL EQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + LBND_IN( 3 ) = -1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 2 + LBND( 3 ) = -1 + UBND( 3 ) = 1 + M = AST_UNITMAP( 3, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + + K2 = MOD( K - 1, 16 ) + 1 + IF( K2 .LE. 4 .OR. MOD( K2, 4 ) .EQ. 0 .OR. + : MOD( K2, 4 ) .EQ. 3 .OR. + : L .EQ. 2 ) THEN + IF( .NOT. EQUALD( OUT( K ), 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST3D ^I: ^V != 0', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( K ), + ; 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST3D ^I: variance ^V '// + : '!= 0', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALD( OUT( K ), IN( K + 13 ))) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K+13 ) ) ) + CALL ERR_REP( ' ', 'TEST3D ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT( K ), IN(K+13) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K+13 ) ) ) + CALL ERR_REP( ' ', + : 'TEST3D ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + END DO + END IF + + END + + + + SUBROUTINE TEST3I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC + LOGICAL EQUALI + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + LBND_IN( 3 ) = -1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 2 + LBND( 3 ) = -1 + UBND( 3 ) = 1 + M = AST_UNITMAP( 3, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + + K2 = MOD( K - 1, 16 ) + 1 + IF( K2 .LE. 4 .OR. MOD( K2, 4 ) .EQ. 0 .OR. + : MOD( K2, 4 ) .EQ. 3 .OR. + : L .EQ. 2 ) THEN + IF( .NOT. EQUALI( OUT( K ), 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST3I ^I: ^V != 0', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( K ), + ; 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST3I ^I: variance ^V '// + : '!= 0', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALI( OUT( K ), IN( K + 13 ))) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K+13 ) ) ) + CALL ERR_REP( ' ', 'TEST3I ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT( K ), IN(K+13) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K+13 ) ) ) + CALL ERR_REP( ' ', + : 'TEST3I ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + END DO + END IF + + END + + + + SUBROUTINE TEST3R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC + LOGICAL EQUALR + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + LBND_IN( 3 ) = -1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 2 + LBND( 3 ) = -1 + UBND( 3 ) = 1 + M = AST_UNITMAP( 3, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + + K2 = MOD( K - 1, 16 ) + 1 + IF( K2 .LE. 4 .OR. MOD( K2, 4 ) .EQ. 0 .OR. + : MOD( K2, 4 ) .EQ. 3 .OR. + : L .EQ. 2 ) THEN + IF( .NOT. EQUALR( OUT( K ), 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST3R ^I: ^V != 0', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( K ), + ; 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST3R ^I: variance ^V '// + : '!= 0', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALR( OUT( K ), IN( K + 13 ))) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K+13 ) ) ) + CALL ERR_REP( ' ', 'TEST3R ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT( K ), IN(K+13) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K+13 ) ) ) + CALL ERR_REP( ' ', + : 'TEST3R ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + END DO + END IF + + END + + + + + +* ----------------------------------------------- +* Test 4 +* + + SUBROUTINE TEST4( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST4' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST4R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST4D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST4I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST4', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST4D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), K + LOGICAL EQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_SHIFTMAP( 1, 3.0D0, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = I*K + IN_VAR( I - LBND_IN(1) + 1 ) = I + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + + DO I = LBND_OUT(1), LBND(1) + 2 + IF( .NOT. EQUALD( OUT( I - LBND_OUT(1) + 1 ), + : 0.0D0) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST4D ^I: ^V != BAD', STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( I - LBND_OUT(1) + 1 ), + : 0.0D0) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST4D ^I: variance ^V != BAD', + : STATUS ) + RETURN + END IF + END DO + + DO I = LBND(1) + 3, UBND_OUT(1) + IF( .NOT. EQUALD( OUT( I - LBND_OUT(1) + 1 ), + : IN( I - 3 - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + IF( OUT( I - LBND_OUT(1) + 1 ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( I -3 - LBND_IN(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST4D ^I: data ^V != ^B', STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( I - LBND_OUT(1) + 1 ), + : IN_VAR( I - 3 - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + IF( OUT_VAR(I-LBND_OUT(1)+1) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR(I-3-LBND_IN(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST4D ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END DO + + END IF + + END + + SUBROUTINE TEST4I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), K + LOGICAL EQUALI + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_SHIFTMAP( 1, 3.0D0, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = I*K + IN_VAR( I - LBND_IN(1) + 1 ) = I + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + + DO I = LBND_OUT(1), LBND(1) + 2 + IF( .NOT. EQUALI( OUT( I - LBND_OUT(1) + 1 ), + : 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST4I ^I: ^V != BAD', STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( I - LBND_OUT(1) + 1 ), + : 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST4I ^I: variance ^V != BAD', + : STATUS ) + RETURN + END IF + END DO + + DO I = LBND(1) + 3, UBND_OUT(1) + IF( .NOT. EQUALI( OUT( I - LBND_OUT(1) + 1 ), + : IN( I - 3 - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + IF( OUT( I - LBND_OUT(1) + 1 ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( I -3 - LBND_IN(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST4I ^I: data ^V != ^B', STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( I - LBND_OUT(1) + 1 ), + : IN_VAR( I - 3 - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + IF( OUT_VAR(I-LBND_OUT(1)+1) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR(I-3-LBND_IN(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST4I ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END DO + + END IF + + END + + SUBROUTINE TEST4R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), K + LOGICAL EQUALR + + IF( STATUS .NE. SAI__OK ) RETURN + + K = MIN( 1000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = 10 + UBND_IN( 1 ) = 19 + LBND_OUT( 1 ) = 12 + UBND_OUT( 1 ) = 20 + LBND( 1 ) = 11 + UBND( 1 ) = 17 + M = AST_SHIFTMAP( 1, 3.0D0, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.1 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + DO I = LBND_IN(1), UBND_IN(1) + IN( I - LBND_IN(1) + 1 ) = I*K + IN_VAR( I - LBND_IN(1) + 1 ) = I + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + + DO I = LBND_OUT(1), LBND(1) + 2 + IF( .NOT. EQUALR( OUT( I - LBND_OUT(1) + 1 ), + : 0.0E0) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST4R ^I: ^V != BAD', STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( I - LBND_OUT(1) + 1 ), + : 0.0E0) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST4R ^I: variance ^V != BAD', + : STATUS ) + RETURN + END IF + END DO + + DO I = LBND(1) + 3, UBND_OUT(1) + IF( .NOT. EQUALR( OUT( I - LBND_OUT(1) + 1 ), + : IN( I - 3 - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + IF( OUT( I - LBND_OUT(1) + 1 ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( I - LBND_OUT(1) + 1))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( I -3 - LBND_IN(1) + 1 ) ) ) + CALL ERR_REP( ' ', 'TEST4R ^I: data ^V != ^B', STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( I - LBND_OUT(1) + 1 ), + : IN_VAR( I - 3 - LBND_IN(1) + 1 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + IF( OUT_VAR(I-LBND_OUT(1)+1) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR(I-LBND_OUT(1)+1))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR(I-3-LBND_IN(1)+1) ) ) + CALL ERR_REP( ' ', 'TEST4R ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END DO + + END IF + + END + + + +* ----------------------------------------------- +* Test 5 +* + + SUBROUTINE TEST5( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST5' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST5R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST5D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST5I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST5', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST5D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(2) + LOGICAL EQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + SHIFTS(1) = 3.0D0 + SHIFTS(2) = -1.0D0 + M = AST_SHIFTMAP( 2, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + IF( MOD( K - 1, 4 ) .LT. 2 ) THEN + IF( .NOT. EQUALD( OUT( K ), 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST5D ^I: ^V != BAD', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( K ), + : 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST5D ^I: variance ^V '// + : '!= BAD', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALD( OUT( K ), IN( K - 2 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K - 2 ) ) ) + CALL ERR_REP( ' ', 'TEST5D ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT( K ), IN( K-2 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K - 2 ) ) ) + CALL ERR_REP( ' ', + : 'TEST5D ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + + END IF + + END + + + + SUBROUTINE TEST5I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(2) + LOGICAL EQUALI + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + SHIFTS(1) = 3.0D0 + SHIFTS(2) = -1.0D0 + M = AST_SHIFTMAP( 2, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + IF( MOD( K - 1, 4 ) .LT. 2 ) THEN + IF( .NOT. EQUALI( OUT( K ), 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST5I ^I: ^V != BAD', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( K ), + : 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST5I ^I: variance ^V '// + : '!= BAD', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALI( OUT( K ), IN( K - 2 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K - 2 ) ) ) + CALL ERR_REP( ' ', 'TEST5I ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT( K ), IN( K-2 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K - 2 ) ) ) + CALL ERR_REP( ' ', + : 'TEST5I ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + + END IF + + END + + + + SUBROUTINE TEST5R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(2) + LOGICAL EQUALR + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + SHIFTS(1) = 3.0D0 + SHIFTS(2) = -1.0D0 + M = AST_SHIFTMAP( 2, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + IF( MOD( K - 1, 4 ) .LT. 2 ) THEN + IF( .NOT. EQUALR( OUT( K ), 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST5R ^I: ^V != BAD', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( K ), + : 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST5R ^I: variance ^V '// + : '!= BAD', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALR( OUT( K ), IN( K - 2 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K - 2 ) ) ) + CALL ERR_REP( ' ', 'TEST5R ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT( K ), IN( K-2 ) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K - 2 ) ) ) + CALL ERR_REP( ' ', + : 'TEST5R ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + + END IF + + END + + + + + +* ----------------------------------------------- +* Test 6 +* + + SUBROUTINE TEST6( DO, NAME, TYPE, + : LBND_IN, UBND_IN, IPIN, IPIN_VAR, + : LBND_OUT, UBND_OUT, IPOUT, IPOUT_VAR, + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + + INTEGER M, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : LBND(*), UBND(*), IPIN, IPIN_VAR, IPOUT, IPOUT_VAR, + : STATUS, DO, J + DOUBLE PRECISION TOL, PARAMS(*) + CHARACTER TYPE*(*), NAME*(*) + + IF( STATUS .NE. SAI__OK ) RETURN + + NAME = 'TEST6' + +* Fill the input data and variance arrays if required. + IF( TYPE .EQ. '_REAL' ) THEN + CALL TEST6R( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_DOUBLE' ) THEN + CALL TEST6D( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( TYPE .EQ. '_INTEGER' ) THEN + CALL TEST6I( DO, LBND_IN, UBND_IN, %VAL(CNF_PVAL(IPIN)), + : %VAL(CNF_PVAL(IPIN_VAR)), LBND_OUT, UBND_OUT, + : %VAL(CNF_PVAL(IPOUT)),%VAL(CNF_PVAL(IPOUT_VAR)), + : LBND, UBND, M, PARAMS, TOL, J, STATUS ) + + ELSE IF( STATUS .EQ. SAI__OK ) then + STATUS = SAI__ERROR + CALL MSG_SETC( 'T', TYPE ) + CALL ERR_REP( ' ', 'Bad data type (^T) supplied to TEST6', + : STATUS ) + END IF + + END + + + + + SUBROUTINE TEST6D( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + DOUBLE PRECISION IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(3) + LOGICAL EQUALD + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_DTOD( VAL__MAXD )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + LBND_IN( 3 ) = -1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 2 + LBND( 3 ) = -1 + UBND( 3 ) = 1 + SHIFTS(1) = 3.0D0 + SHIFTS(2) = -1.0D0 + SHIFTS(3) = 1.0D0 + M = AST_SHIFTMAP( 3, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + + K2 = MOD( K - 1, 16 ) + 1 + IF( MOD( K2 - 1, 4 ) .LT. 2 ) THEN + IF( .NOT. EQUALD( OUT( K ), 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST6D ^I: ^V != BAD', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT_VAR( K ), + : 0.0D0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST6D ^I: variance ^V '// + : '!= BAD', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALD( OUT( K ), IN( K - 2 ))) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K-2 ) ) ) + CALL ERR_REP( ' ', 'TEST6D ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALD( OUT( K ), IN(K-2) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADD ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K-2 ) ) ) + CALL ERR_REP( ' ', + : 'TEST6D ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + END DO + END IF + + END + + + + SUBROUTINE TEST6I( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + INTEGER IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(3) + LOGICAL EQUALI + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_ITOD( VAL__MAXI )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + LBND_IN( 3 ) = -1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 2 + LBND( 3 ) = -1 + UBND( 3 ) = 1 + SHIFTS(1) = 3.0D0 + SHIFTS(2) = -1.0D0 + SHIFTS(3) = 1.0D0 + M = AST_SHIFTMAP( 3, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + + K2 = MOD( K - 1, 16 ) + 1 + IF( MOD( K2 - 1, 4 ) .LT. 2 ) THEN + IF( .NOT. EQUALI( OUT( K ), 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST6I ^I: ^V != BAD', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT_VAR( K ), + : 0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST6I ^I: variance ^V '// + : '!= BAD', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALI( OUT( K ), IN( K - 2 ))) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K-2 ) ) ) + CALL ERR_REP( ' ', 'TEST6I ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALI( OUT( K ), IN(K-2) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADI ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K-2 ) ) ) + CALL ERR_REP( ' ', + : 'TEST6I ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + END DO + END IF + + END + + + + SUBROUTINE TEST6R( DO, LBND_IN, UBND_IN, IN, IN_VAR, LBND_OUT, + : UBND_OUT, OUT, OUT_VAR, LBND, UBND, M, + : PARAMS, TOL, SPREAD, STATUS ) + + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'PRM_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'CNF_PAR' + INCLUDE 'NUM_DEC' + INCLUDE 'NUM_DEF' + + INTEGER DO, LBND_IN(*), UBND_IN(*), LBND_OUT(*), UBND_OUT(*), + : SPREAD, LBND(*), UBND(*), STATUS, M, I, J, K, L, K2 + REAL IN(*), IN_VAR(*), OUT(*), OUT_VAR(*) + DOUBLE PRECISION TOL, PARAMS(*), KFAC, SHIFTS(3) + LOGICAL EQUALR + + IF( STATUS .NE. SAI__OK ) RETURN + + KFAC = MIN( 1000.0D0, NUM_RTOD( VAL__MAXR )/20.0 ) + +* Return the scalar parameters of the test if required. + IF( DO .EQ. 0 ) THEN + LBND_IN( 1 ) = -1 + UBND_IN( 1 ) = 2 + LBND_OUT( 1 ) = 0 + UBND_OUT( 1 ) = 3 + LBND( 1 ) = -1 + UBND( 1 ) = 1 + LBND_IN( 2 ) = 3 + UBND_IN( 2 ) = 6 + LBND_OUT( 2 ) = 2 + UBND_OUT( 2 ) = 5 + LBND( 2 ) = 3 + UBND( 2 ) = 6 + LBND_IN( 3 ) = -1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 0 + UBND_OUT( 3 ) = 2 + LBND( 3 ) = -1 + UBND( 3 ) = 1 + SHIFTS(1) = 3.0D0 + SHIFTS(2) = -1.0D0 + SHIFTS(3) = 1.0D0 + M = AST_SHIFTMAP( 3, SHIFTS, ' ', STATUS ) + PARAMS(1) = 2.0 + PARAMS(2) = 2.0 + TOL = 0.0 + +* Fill the input data and variance arrays if required. + ELSE IF( DO .EQ. 1 ) THEN + K = 1 + DO L = LBND_IN(3), UBND_IN(3) + DO J = LBND_IN(2), UBND_IN(2) + DO I = LBND_IN(1), UBND_IN(1) + IN( K ) = K*KFAC + IN_VAR( K ) = K + K = K + 1 + END DO + END DO + END DO + +* Otherwise check output data and variance arrays look right. + ELSE + K = 1 + DO L = LBND_OUT(3), UBND_OUT(3) + DO J = LBND_OUT(2), UBND_OUT(2) + DO I = LBND_OUT(1), UBND_OUT(1) + + K2 = MOD( K - 1, 16 ) + 1 + IF( MOD( K2 - 1, 4 ) .LT. 2 ) THEN + IF( .NOT. EQUALR( OUT( K ), 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST6R ^I: ^V != BAD', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT_VAR( K ), + : 0.0E0 ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT_VAR( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ))) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL ERR_REP( ' ', 'TEST6R ^I: variance ^V '// + : '!= BAD', STATUS ) + RETURN + END IF + ELSE + IF( .NOT. EQUALR( OUT( K ), IN( K - 2 ))) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN( K-2 ) ) ) + CALL ERR_REP( ' ', 'TEST6R ^I: data ^V != ^B', + : STATUS ) + RETURN + ELSE IF( .NOT. EQUALR( OUT( K ), IN(K-2) ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', K ) + IF( OUT( K ) .NE. VAL__BADR ) THEN + CALL MSG_SETD( 'V', DBLE( OUT_VAR( K ) ) ) + ELSE + CALL MSG_SETC( 'V', 'BAD' ) + END IF + CALL MSG_SETD( 'B', DBLE( IN_VAR( K-2 ) ) ) + CALL ERR_REP( ' ', + : 'TEST6R ^I: variance ^V != ^B', + : STATUS ) + RETURN + END IF + END IF + K = K + 1 + END DO + END DO + END DO + END IF + + END + + + diff --git a/ast_tester/testrebinseq.f b/ast_tester/testrebinseq.f new file mode 100644 index 0000000..3ab4e43 --- /dev/null +++ b/ast_tester/testrebinseq.f @@ -0,0 +1,1580 @@ + program testrebin + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status + double precision params(2) + character ch + status = sai__ok + + call ast_begin( status ) + + params(1) = 1.5 + params(2) = 1 + + call tests( 1, AST__GAUSS, params, status ) + call tests( 2, AST__GAUSS, params, status ) + call tests( 3, AST__GAUSS, params, status ) + call tests( 1, AST__NEAREST, params, status ) + call tests( 2, AST__NEAREST, params, status ) + call tests( 3, AST__NEAREST, params, status ) + call tests( 1, AST__LINEAR, params, status ) + call tests( 2, AST__LINEAR, params, status ) + call tests( 3, AST__LINEAR, params, status ) + + call ast_end( status ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All AST_REBINSEQ tests passed' + else + write(*,*) 'AST_REBINSEQ tests failed' + end if + + end + + subroutine tests( NDIM, SPREAD, params, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer spread, status, ndim + double precision params(2) + + if( status .ne. sai__ok ) return + + call test1( NDIM, SPREAD, params, status ) + call test2( NDIM, SPREAD, params, status ) + call test3( NDIM, SPREAD, params, status ) + call test4( NDIM, SPREAD, params, status ) + call test5( NDIM, SPREAD, params, status ) + call test6( NDIM, SPREAD, params, status ) + call test7( NDIM, SPREAD, params, status ) + call test8( NDIM, SPREAD, params, status ) + call test9( NDIM, SPREAD, params, status ) + call test10( NDIM, SPREAD, params, status ) + call test11( NDIM, SPREAD, params, status ) + call test12( NDIM, SPREAD, params, status ) + + if( status .ne. SAI__OK ) then + call msg_seti( 'N', ndim ) + + if( spread .eq. AST__GAUSS ) then + call msg_setc( 'S', 'AST__GAUSS' ) + else if( spread .eq. AST__NEAREST ) then + call msg_setc( 'S', 'AST__NEAREST' ) + else if( spread .eq. AST__LINEAR ) then + call msg_setc( 'S', 'AST__LINEAR' ) + endif + call err_rep( ' ', 'Spread=^S (^N-dimensional)', status ) + endif + + end + + + + + SUBROUTINE ADDNOISE( N, ARRAY, SIGMA, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER BUFSIZE + PARAMETER ( BUFSIZE = 100 ) + + INTEGER N, STATUS, MM, IAT, NUSED, I + DOUBLE PRECISION ARRAY( N ) + DOUBLE PRECISION SIGMA + DOUBLE PRECISION NOISE( BUFSIZE ) + DOUBLE PRECISION JUNK( BUFSIZE ) + + CHARACTER FWD(1)*80 + CHARACTER INV(1)*80 + + DATA JUNK/ BUFSIZE*0.0D0 / + + IF( STATUS .NE. SAI__OK ) RETURN + + FWD(1) = 'Y=Gauss(0.0,' + IAT = 12 + CALL CHR_PUTD( SIGMA, FWD(1), IAT ) + CALL CHR_APPND( ')', FWD(1), IAT ) + INV(1) = 'X' + + MM = AST_MATHMAP( 1, 1, 1, FWD, 1, INV, ' ', STATUS ) + + NUSED = BUFSIZE + DO I = 1, N + IF( NUSED .EQ. BUFSIZE ) THEN + CALL AST_TRAN1( MM, BUFSIZE, JUNK, .TRUE., NOISE, STATUS ) + NUSED = 0 + END IF + NUSED = NUSED + 1 + ARRAY( I ) = ARRAY( I ) + NOISE( NUSED ) + END DO + + CALL AST_ANNUL( MM, STATUS ) + + END + + + + + + + SUBROUTINE TEST1( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NX, NY, JHI + PARAMETER( NX = 100 ) + PARAMETER( NY = 200 ) + + + REAL IN( NX, NY ) + REAL OUT( NX, NY ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX, NY ) + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, NX + DO J = 1, NY + IN( I, J ) = 1.0 + END DO + END DO + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + LBND_IN( 3 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + UBND_IN( 3 ) = 1 + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + + MAP = AST_UNITMAP( NDIM, ' ', STATUS ) + CALL AST_REBINSEQR( MAP, 0.0D0, NDIM, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADR, + : NDIM, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : out, OUT, WEIGHTS, NUSED, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny + ENDIF + + DO I = 1, NX + DO J = 1, JHI + IF( ABS( OUT( I, J ) - 1.0 ) .GT. 1.0E-6 .AND. + : STATUS .EQ. SAI__OK ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETI( 'J', J ) + CALL MSG_SETR( 'V', OUT(I,J) ) + CALL ERR_REP( ' ', 'Output pixel (^I,^J) should be '// + : '1.0 but is ^V', status ) + END IF + END DO + END DO + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test1 failed', STATUS ) + END IF + + END + + + + SUBROUTINE TEST2( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NX, NY, JHI + PARAMETER( NX = 100 ) + PARAMETER( NY = 200 ) + + + DOUBLE PRECISION IN( NX, NY ) + DOUBLE PRECISION OUT( NX, NY ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX, NY ) + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, NX + DO J = 1, NY + IN( I, J ) = 1.0D0 + END DO + END DO + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + + MAP = AST_UNITMAP( NDIM, ' ', STATUS ) + + DO I = 1, 3 + IF( I .EQ. 3 ) FLAGS = AST__REBINEND + CALL AST_REBINSEQD( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADD, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, OUT, WEIGHTS, NUSED, STATUS ) + END DO + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny + ENDIF + + DO I = 1, NX + DO J = 1, JHI + IF( ABS( OUT( I, J ) - 1.0D0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETI( 'J', J ) + CALL MSG_SETD( 'V', OUT(I,J) ) + CALL ERR_REP( ' ', 'Output pixel (^I,^J) should be '// + : '1.0 but is ^V', status ) + END IF + END DO + END DO + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test2 failed', STATUS ) + END IF + + END + + + + + + SUBROUTINE TEST3( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NX, NY + PARAMETER( NX = 100 ) + PARAMETER( NY = 200 ) + + INTEGER IN( NX, NY ) + INTEGER OUT( NX, NY ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX, NY ) + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J, JHI + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, NX + DO J = 1, NY + IN( I, J ) = 1.0D0 + END DO + END DO + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__NONORM + + MAP = AST_UNITMAP( NDIM, ' ', STATUS ) + + DO I = 1, 3 + CALL AST_REBINSEQI( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADI, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, OUT, WEIGHTS, NUSED, STATUS ) + FLAGS = AST__NONORM + END DO + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny + ENDIF + + DO I = 1, NX + DO J = 1, JHI + IF( OUT( I, J ) .NE. 3 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETI( 'J', J ) + CALL MSG_SETI( 'V', OUT(I,J) ) + CALL ERR_REP( ' ', 'Output pixel (^I,^J) should be '// + : '3 but is ^V', status ) + END IF + END DO + END DO + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test3 failed', STATUS ) + END IF + + END + + + + + + + SUBROUTINE TEST4( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NX, NY + PARAMETER( NX = 100 ) + PARAMETER( NY = 200 ) + + REAL IN( NX, NY ) + REAL OUT( NX, NY ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX, NY ), INA(3), + : INB(3), OUTA(3), OUTB(3), SUM, ANSWER + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J, LBOXG(3), UBOXG(3), JHI + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, NX + DO J = 1, NY + IN( I, J ) = 1.0 + END DO + END DO + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + AST__CONSERVEFLUX + + INA( 1 ) = LBND_IN( 1 ) + INA( 2 ) = LBND_IN( 2 ) + INA( 3 ) = LBND_IN( 3 ) + INB( 1 ) = UBND_IN( 1 ) + INB( 2 ) = UBND_IN( 2 ) + INB( 3 ) = UBND_IN( 3 ) + 1.0D0 + + OUTA( 1 ) = 0.75*LBND_OUT( 1 ) + 0.25*UBND_OUT( 1 ) + OUTA( 2 ) = 0.75*LBND_OUT( 2 ) + 0.25*UBND_OUT( 2 ) + OUTA( 3 ) = INA( 3 ) + OUTB( 1 ) = 0.25*LBND_OUT( 1 ) + 0.75*UBND_OUT( 1 ) + OUTB( 2 ) = 0.25*LBND_OUT( 2 ) + 0.75*UBND_OUT( 2 ) + OUTB( 3 ) = INB( 3 ) + + MAP = AST_WINMAP( NDIM, INA, INB, OUTA, OUTB, ' ', STATUS ) + CALL AST_REBINSEQR( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 1000, + : VAL__BADR, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, OUT, WEIGHTS, NUSED, STATUS ) + + LBOXG( 1 ) = VAL__MAXI + LBOXG( 2 ) = VAL__MAXI + UBOXG( 1 ) = VAL__MINI + UBOXG( 2 ) = VAL__MINI + + SUM = 0.0D0 + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny + ENDIF + + DO I = 1, NX + DO J = 1, JHI + + IF( OUT(I,J) .NE. VAL__BADR ) THEN + SUM = SUM + OUT(I,J) + IF( I .LT. LBOXG(1) ) THEN + LBOXG(1) = I + ELSE IF( I .GT. UBOXG(1) ) THEN + UBOXG(1) = I + ENDIF + IF( J .LT. LBOXG(2) ) THEN + LBOXG(2) = J + ELSE IF( J .GT. UBOXG(2) ) THEN + UBOXG(2) = J + ENDIF + ENDIF + END DO + END DO + + IF( NDIM .EQ. 1 ) THEN + + IF( ( ( SPREAD .EQ. AST__GAUSS ) .AND. ( + : LBOXG( 1 ) .NE. 24 .OR. + : UBOXG( 1 ) .NE. 77 )) .OR. ( + : ( SPREAD .EQ. AST__NEAREST ) .AND. ( + : LBOXG( 1 ) .NE. 26 .OR. + : UBOXG( 1 ) .NE. 75 )) .OR. ( + : ( SPREAD .EQ. AST__LINEAR ) .AND. ( + : LBOXG( 1 ) .NE. 25 .OR. + : UBOXG( 1 ) .NE. 76 ) ) ) THEN + STATUS = SAI__ERROR + CALL ERR_REP( ' ', 'Good pixel bounding box is wrong', + : STATUS ) + write(*,*) LBOXG, UBOXG + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + ANSWER = 100.0 + ELSE IF( SPREAD .EQ. AST__GAUSS ) THEN + ANSWER = 108.0 + ELSE IF( SPREAD .EQ. AST__LINEAR ) THEN + ANSWER = 104.0 + ELSE + ANSWER = -1 + END IF + + else + + IF( ( ( SPREAD .EQ. AST__GAUSS ) .AND. ( + : LBOXG( 1 ) .NE. 24 .OR. + : LBOXG( 2 ) .NE. 49 .OR. + : UBOXG( 1 ) .NE. 77 .OR. + : UBOXG( 2 ) .NE. 152 ) ) .OR. ( + : ( SPREAD .EQ. AST__NEAREST ) .AND. ( + : LBOXG( 1 ) .NE. 26 .OR. + : LBOXG( 2 ) .NE. 51 .OR. + : UBOXG( 1 ) .NE. 75 .OR. + : UBOXG( 2 ) .NE. 150 ) ) .OR. ( + : ( SPREAD .EQ. AST__LINEAR ) .AND. ( + : LBOXG( 1 ) .NE. 25 .OR. + : LBOXG( 2 ) .NE. 50 .OR. + : UBOXG( 1 ) .NE. 76 .OR. + : UBOXG( 2 ) .NE. 151 ) ) ) THEN + STATUS = SAI__ERROR + CALL ERR_REP( ' ', 'Good pixel bounding box is wrong', + : STATUS ) + write(*,*) LBOXG, UBOXG + END IF + + IF( SPREAD .EQ. AST__NEAREST ) THEN + ANSWER = 20000.0 + ELSE IF( SPREAD .EQ. AST__GAUSS ) THEN + ANSWER = 22464.0 + ELSE IF( SPREAD .EQ. AST__LINEAR ) THEN + ANSWER = 21216.0 + ELSE + ANSWER = -1 + END IF + endif + + IF( ABS( SUM - ANSWER ) .GT. 0.01 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', SUM ) + CALL MSG_SETD( 'W', ANSWER ) + CALL ERR_REP( ' ', 'Total output data sum is ^V (should '// + : 'be ^W).', STATUS ) + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test4 failed', STATUS ) + END IF + + END + + + + SUBROUTINE TEST5( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NX_IN, NY_IN + PARAMETER( NX_IN = 100 ) + PARAMETER( NY_IN = 200 ) + + INTEGER BORDER_OUT + PARAMETER( BORDER_OUT = 10 ) + + INTEGER NX_OUT, NY_OUT + PARAMETER( NX_OUT = NX_IN + 2*BORDER_OUT ) + PARAMETER( NY_OUT = NY_IN + 2*BORDER_OUT ) + + DOUBLE PRECISION IN( NX_IN, NY_IN ) + DOUBLE PRECISION OUT( NX_OUT, NY_OUT ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX_OUT, NY_OUT ), INA(3), + : INB(3), OUTA(3), OUTB(3), SUM, VA, VB + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : K, FLAGS, I, J, LBOXG(3), UBOXG(3), JHI + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX_IN - 1 + UBND_IN( 2 ) = NY_IN + + LBND_OUT( 1 ) = LBND_IN( 1 ) - BORDER_OUT + LBND_OUT( 2 ) = LBND_IN( 2 ) - BORDER_OUT + UBND_OUT( 1 ) = UBND_IN( 1 ) + BORDER_OUT + UBND_OUT( 2 ) = UBND_IN( 2 ) + BORDER_OUT + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__NONORM + + INA( 1 ) = LBND_IN( 1 ) + INA( 2 ) = LBND_IN( 2 ) + INA( 3 ) = LBND_IN( 3 ) + INB( 1 ) = UBND_IN( 1 ) + INB( 2 ) = UBND_IN( 2 ) + INB( 3 ) = UBND_IN( 3 ) + 1.0D0 + + DO K = 1, 3 + + DO I = 1, NX_IN + DO J = 1, NY_IN + IN( I, J ) = K + END DO + END DO + + VA = (k-1)*0.25 + VB = VA + 0.5 + + OUTA( 1 ) = VB*LBND_IN( 1 ) + VA*UBND_IN( 1 ) + OUTA( 2 ) = VB*LBND_IN( 2 ) + VA*UBND_IN( 2 ) + OUTB( 1 ) = VA*LBND_IN( 1 ) + VB*UBND_IN( 1 ) + OUTB( 2 ) = VA*LBND_IN( 2 ) + VB*UBND_IN( 2 ) + OUTA( 3 ) = INA( 3 ) + OUTB( 3 ) = INB( 3 ) + + MAP = AST_WINMAP( NDIM, INA, INB, OUTA, OUTB, ' ', STATUS ) + CALL AST_REBINSEQD( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADD, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, OUT, WEIGHTS, NUSED, STATUS ) + + FLAGS = AST__NONORM + END DO + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny_out + ENDIF + + SUM = 0.0D0 + + DO I = 1, NX_OUT + DO J = 1, JHI + IF( OUT(I,J) .NE. VAL__BADR ) THEN + SUM = SUM + OUT(I,J) + ENDIF + END DO + END DO + + IF( NDIM .EQ. 1 ) THEN + IF( ABS( SUM - 600 ) .GT. 1.0E-3 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', SUM ) + CALL ERR_REP( ' ', 'Total output data sum is ^V (should '// + : 'be 600).', STATUS ) + + END IF + + IF( ABS( OUT(20,1) - 2.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(20,1) ) + CALL ERR_REP( ' ', 'Output pixel (20) should be 2, '// + : 'is ^V', STATUS ) + + ELSE IF( ABS( OUT(50,1) - 6.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(50,1) ) + CALL ERR_REP( ' ', 'Output pixel (50) should be 6, '// + : 'is ^V', STATUS ) + + ELSE IF( ABS( OUT(70,1) - 10.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(70,1) ) + CALL ERR_REP( ' ', 'Output pixel (70) should be 10, '// + : 'is ^V', STATUS ) + + ELSE IF( ABS( OUT(100,1) - 6.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(100,1) ) + CALL ERR_REP( ' ', 'Output pixel (100) should be 6, '// + : 'is ^V', STATUS ) + END IF + + ELSE IF( NDIM .EQ. 2 ) THEN + IF( ABS( SUM - 120000 ) .GT. 1.0E-3 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', SUM ) + CALL ERR_REP( ' ', 'Total output data sum is ^V (should '// + : 'be 120000).', STATUS ) + + END IF + + IF( ABS( OUT(40,40) - 4.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(40,40) ) + CALL ERR_REP( ' ', 'Output pixel (40,40) should be 4, '// + : 'is ^V', STATUS ) + + ELSE IF( ABS( OUT(50,90) - 12.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(50,90) ) + CALL ERR_REP( ' ', 'Output pixel (50,90) should be 12, '// + : 'is ^V', STATUS ) + + ELSE IF( ABS( OUT(70,80) - 8.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(70,80) ) + CALL ERR_REP( ' ', 'Output pixel (70,80) should be 8, '// + : 'is ^V', STATUS ) + + ELSE IF( ABS( OUT(70,130) - 20.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(70,130) ) + CALL ERR_REP( ' ', 'Output pixel (70,130) should be 20, '// + : 'is ^V', STATUS ) + + ELSE IF( ABS( OUT(20,130) - 0.0 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', OUT(20,130) ) + CALL ERR_REP( ' ', 'Output pixel (20,130) should be 0, '// + : 'is ^V', STATUS ) + + END IF + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test5 failed', STATUS ) + END IF + + END + + + + + SUBROUTINE TEST6( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NK + PARAMETER( NK = 3 ) + + INTEGER NX_IN, NY_IN + PARAMETER( NX_IN = 100 ) + PARAMETER( NY_IN = 200 ) + + INTEGER BORDER_OUT + PARAMETER( BORDER_OUT = 10 ) + + INTEGER NX_OUT, NY_OUT + PARAMETER( NX_OUT = NX_IN + 2*BORDER_OUT ) + PARAMETER( NY_OUT = NY_IN + 2*BORDER_OUT ) + + DOUBLE PRECISION IN( NX_IN, NY_IN ), ANSWER + DOUBLE PRECISION OUT( NX_OUT, NY_OUT ), MNVAL, MXVAL + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX_OUT, NY_OUT ), INA(3), + : INB(3), OUTA(3), OUTB(3), SUM, VA, VB + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : K, FLAGS, I, J, LBOXG(3), UBOXG(3), JHI + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX_IN - 1 + UBND_IN( 2 ) = NY_IN + + LBND_OUT( 1 ) = LBND_IN( 1 ) - BORDER_OUT + LBND_OUT( 2 ) = LBND_IN( 2 ) - BORDER_OUT + UBND_OUT( 1 ) = UBND_IN( 1 ) + BORDER_OUT + UBND_OUT( 2 ) = UBND_IN( 2 ) + BORDER_OUT + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + INA( 1 ) = LBND_IN( 1 ) + INA( 2 ) = LBND_IN( 2 ) + INA( 3 ) = LBND_IN( 3 ) + INB( 1 ) = UBND_IN( 1 ) + INB( 2 ) = UBND_IN( 2 ) + INB( 3 ) = UBND_IN( 3 ) + 1.0D0 + + DO K = 1, NK + + FLAGS = AST__CONSERVEFLUX + IF( K .EQ. 1 ) FLAGS = FLAGS + AST__REBININIT + IF( K .EQ. NK ) FLAGS = FLAGS + AST__REBINEND + + DO I = 1, NX_IN + DO J = 1, NY_IN + IN( I, J ) = K + END DO + END DO + + VA = (k-1)*0.25 + VB = VA + 0.5 + + OUTA( 1 ) = VB*LBND_IN( 1 ) + VA*UBND_IN( 1 ) + OUTA( 2 ) = VB*LBND_IN( 2 ) + VA*UBND_IN( 2 ) + OUTB( 1 ) = VA*LBND_IN( 1 ) + VB*UBND_IN( 1 ) + OUTB( 2 ) = VA*LBND_IN( 2 ) + VB*UBND_IN( 2 ) + OUTA( 3 ) = INA( 3 ) + OUTB( 3 ) = INB( 3 ) + + MAP = AST_WINMAP( NDIM, INA, INB, OUTA, OUTB, ' ', STATUS ) + CALL AST_REBINSEQD( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADD, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, OUT, WEIGHTS, NUSED, STATUS ) + + END DO + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny_out + ENDIF + + SUM = 0.0D0 + MXVAL = VAL__MIND + MNVAL = VAL__MAXD + DO I = 1, NX_OUT + DO J = 1, JHI + IF( OUT(I,J) .NE. VAL__BADD ) THEN + SUM = SUM + OUT(I,J) + IF( OUT(I,J) .GT. MXVAL ) MXVAL = OUT(I,J) + IF( OUT(I,J) .lT. MnVAL ) MNVAL = OUT(I,J) + ENDIF + END DO + END DO + + IF( NDIM .eq. 1 ) THEN + IF( SPREAD .EQ. AST__GAUSS ) THEN + ANSWER = 414.0D0 + ELSE IF( SPREAD .EQ. AST__NEAREST ) THEN + ANSWER = 399.4D0 + ELSE IF( SPREAD .EQ. AST__LINEAR ) THEN + ANSWER = 400.0D0 + ELSE + ANSWER = -1.0 + END IF + + IF( ABS( SUM - ANSWER ) .GT. 1.0D-3 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', SUM ) + CALL MSG_SETD( 'W', ANSWER ) + CALL ERR_REP( ' ', 'Total output data sum is ^V (should '// + : 'be ^W).', STATUS ) + + ELSE IF( ABS( MXVAL - 6 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', MXVAL ) + CALL ERR_REP( ' ', 'Max value is ^V (should be 6).', + : STATUS ) + + ELSE IF( ABS( MNVAL - 2 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', MNVAL ) + CALL ERR_REP( ' ', 'Min value is ^V (should be 2).', + : STATUS ) + + END IF + + ELSE IF( NDIM .eq. 2 ) THEN + IF( SPREAD .EQ. AST__GAUSS ) THEN + ANSWER = 109011.729592723D0 + ELSE IF( SPREAD .EQ. AST__NEAREST ) THEN + ANSWER = 100716.666666667D0 + ELSE IF( SPREAD .EQ. AST__LINEAR ) THEN + ANSWER = 102816.0D0 + ELSE + ANSWER = -1.0 + END IF + + IF( ABS( SUM - ANSWER ) .GT. 1.0D-3 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', SUM ) + CALL MSG_SETD( 'W', ANSWER ) + CALL ERR_REP( ' ', 'Total output data sum is ^V (should '// + : 'be ^W).', STATUS ) + + ELSE IF( ABS( MXVAL - 12 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', MXVAL ) + CALL ERR_REP( ' ', 'Max value is ^V (should be 12).', + : STATUS ) + + ELSE IF( ABS( MNVAL - 4 ) .GT. 1.0E-6 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', MNVAL ) + CALL ERR_REP( ' ', 'Min value is ^V (should be 4).', + : STATUS ) + + END IF + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test6 failed', STATUS ) + END IF + + END + + + SUBROUTINE TEST7( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NX, NY + PARAMETER( NX = 100 ) + PARAMETER( NY = 200 ) + + + REAL IN( NX, NY ) + REAL OUT( NX, NY ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX, NY ) + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : JHI, FLAGS, I, J + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, NX + DO J = 1, NY + IN( I, J ) = 1.0 + END DO + END DO + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + + MAP = AST_ZOOMMAP( NDIM, 0.5D0, ' ', STATUS ) + CALL AST_REBINSEQR( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADR, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, OUT, WEIGHTS, NUSED, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny + ENDIF + + DO I = 1, NX + DO J = 1, JHI + IF( ABS( OUT( I, J ) - 1.0 ) .GT. 1.0E-6 .AND. + : OUT( I, J ) .NE. VAL__BADR ) THEN + STATUS = SAI__ERROR + CALL MSG_SETI( 'I', I ) + CALL MSG_SETI( 'J', J ) + CALL MSG_SETR( 'V', OUT(I,J) ) + CALL ERR_REP( ' ', 'Output pixel (^I,^J) should be '// + : '1.0 but is ^V', status ) + END IF + END DO + END DO + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test7 failed', STATUS ) + END IF + + END + + + SUBROUTINE TEST8( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER NX, NY + PARAMETER( NX = 100 ) + PARAMETER( NY = 200 ) + + + REAL IN( NX, NY ) + REAL OUT( NX, NY ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( NX, NY ), SHIFTS(3), SUM + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J, JHI + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, NX + DO J = 1, NY + IN( I, J ) = 1.0 + END DO + END DO + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + AST__NONORM + + SHIFTS(1) = 5.0D0 + SHIFTS(2) = 5.0D0 + + if( ndim .lt. 3 ) then + MAP = AST_CMPMAP( AST_ZOOMMAP( NDIM, 0.5D0, ' ', STATUS ), + : AST_SHIFTMAP( NDIM, SHIFTS, ' ', STATUS ), + : .TRUE., ' ', STATUS ) + else + MAP = AST_CMPMAP( AST_CMPMAP( AST_ZOOMMAP( 2, 0.5D0, ' ', + : STATUS ), + : AST_SHIFTMAP( 2, SHIFTS, ' ', + : STATUS ), + : .TRUE., ' ', STATUS ), + : AST_UNITMAP( 1, ' ', STATUS ), .FALSE., + : ' ', STATUS ) + endif + + CALL AST_REBINSEQR( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, IN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADR, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, OUT, WEIGHTS, NUSED, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + JHI = 1 + ELSE + JHI = ny + ENDIF + + SUM = 0.0D0 + DO I = 1, NX + DO J = 1, JHI + if( out(i,j) .ne. VAL__BADR ) then + SUM = SUM + DBLE(OUT(I,J)) + end if + END DO + END DO + + IF( SUM .NE. SUM ) THEN + STATUS = SAI__ERROR + CALL ERR_REP( ' ', 'Total output data sum is NaN', STATUS ) + + ELSE IF( ABS( SUM - NX*JHI ) .GT. SUM*1.0D-7 ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', SUM ) + CALL MSG_SETD( 'W', DBLE( NX*JHI) ) + CALL ERR_REP( ' ', 'Total output data sum is ^V (should '// + : 'be ^W).', STATUS ) + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test8 failed', STATUS ) + END IF + + END + + + SUBROUTINE TEST9( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER SIZE + PARAMETER( SIZE = 20000 ) + + INTEGER NX1, NY1 + PARAMETER( NX1 = SIZE ) + PARAMETER( NY1 = 1 ) + + INTEGER NX2, NY2 + PARAMETER( NX2 = SIZE/200 ) + PARAMETER( NY2 = 200 ) + + DOUBLE PRECISION SIGMA + PARAMETER ( SIGMA = 0.1 ) + + REAL*8 IN( size ), VIN( size ) + REAL*8 OUT( size ), VOUT( size ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( size ) + DOUBLE PRECISION REALVAR,MEANVAR,SUM,SUM2,SUM3 + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J, NVAL, JHI, JLO, NX, NY, ILO, IHI, k + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, size + IN( I ) = 1.0D0 + VIN( I ) = SIGMA**2 + END DO + + CALL ADDNOISE( size, IN, SIGMA, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + NX = NX1 + Ny = NY1 + ELSE + NX = NX2 + Ny = NY2 + END IF + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + AST__NONORM + AST__USEVAR + + MAP = AST_ZOOMMAP( NDIM, 0.5D0, ' ', STATUS ) + CALL AST_REBINSEQD( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, VIN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADD, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, VOUT, WEIGHTS, NUSED, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + ILO = 6 + IHI = NINT(0.45*NX1) + JLO = 1 + JHI = 1 + ELSE + ILO = 6 + IHI = 41 + JLO = 8 + JHI = 91 + ENDIF + + SUM = 0.0D0 + SUM2 = 0.0D0 + SUM3 = 0.0D0 + NVAL = 0 + + DO I = ILO, IHI + DO J = JLO, JHI + K = ( J - 1 )*NX + I + IF( OUT(K) .NE. VAL__BADD ) THEN + SUM = SUM + OUT(K) + SUM2 = SUM2 + OUT(K)**2 + SUM3 = SUM3 + VOUT(K) + NVAL = NVAL + 1 + END IF + END DO + END DO + + SUM = SUM/NVAL + REALVAR = SUM2/NVAL - SUM*SUM + MEANVAR = SUM3/NVAL + IF( ABS( REALVAR - MEANVAR ) .GT. + : 0.05*( REALVAR + MEANVAR ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', REALVAR ) + CALL MSG_SETD( 'W', MEANVAR ) + CALL ERR_REP( ' ', 'Real variance is ^V - estimate is ^W.', + : STATUS ) + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test9 failed', STATUS ) + END IF + + END + + + SUBROUTINE TEST10( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER SIZE + PARAMETER( SIZE = 20000 ) + + INTEGER NX1, NY1 + PARAMETER( NX1 = SIZE ) + PARAMETER( NY1 = 1 ) + + INTEGER NX2, NY2 + PARAMETER( NX2 = SIZE/200 ) + PARAMETER( NY2 = 200 ) + + DOUBLE PRECISION SIGMA + PARAMETER ( SIGMA = 0.1 ) + + REAL*8 IN( size ), VIN( size ) + REAL*8 OUT( size ), VOUT( size ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( size ) + DOUBLE PRECISION REALVAR,MEANVAR,SUM,SUM2,SUM3 + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J, NVAL, JHI, JLO, ILO, IHI, NX, NY, K + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, SIZE + IN( I ) = 1.0D0 + VIN( I ) = SIGMA**2 + END DO + + CALL ADDNOISE( SIZE, IN, SIGMA, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + NX = NX1 + Ny = NY1 + ELSE + NX = NX2 + Ny = NY2 + END IF + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + AST__CONSERVEFLUX + : + AST__USEVAR + + MAP = AST_ZOOMMAP( NDIM, 0.5D0, ' ', STATUS ) + CALL AST_REBINSEQD( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, VIN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADD, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, VOUT, WEIGHTS, NUSED, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + ILO = 6 + IHI = NINT(0.45*NX1) + JLO = 1 + JHI = 1 + ELSE + ILO = 6 + IHI = 41 + JLO = 8 + JHI = 91 + ENDIF + + SUM = 0.0D0 + SUM2 = 0.0D0 + SUM3 = 0.0D0 + NVAL = 0 + + DO I = ILO,IHI + DO J = JLO, JHI + K = ( J - 1 )*NX + I + IF( OUT(K) .NE. VAL__BADD ) THEN + SUM = SUM + OUT(K) + SUM2 = SUM2 + OUT(K)**2 + SUM3 = SUM3 + VOUT(K) + NVAL = NVAL + 1 + END IF + END DO + END DO + + SUM = SUM/NVAL + REALVAR = SUM2/NVAL - SUM*SUM + MEANVAR = SUM3/NVAL + + IF( ABS( REALVAR - MEANVAR ) .GT. + : 0.05*( REALVAR + MEANVAR ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', REALVAR ) + CALL MSG_SETD( 'W', MEANVAR ) + CALL ERR_REP( ' ', 'Real variance is ^V - estimate is ^W.', + : STATUS ) + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test10 failed', STATUS ) + END IF + + END + + SUBROUTINE TEST11( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER SIZE + PARAMETER( SIZE = 20000 ) + + INTEGER NX1, NY1 + PARAMETER( NX1 = SIZE ) + PARAMETER( NY1 = 1 ) + + INTEGER NX2, NY2 + PARAMETER( NX2 = SIZE/200 ) + PARAMETER( NY2 = 200 ) + + DOUBLE PRECISION SIGMA + PARAMETER ( SIGMA = 0.1 ) + + REAL*8 IN( SIZE ), VIN( SIZE ) + REAL*8 OUT( SIZE ), VOUT( SIZE ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( SIZE, 2 ) + DOUBLE PRECISION REALVAR,MEANVAR,SUM,SUM2,SUM3 + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J, NVAL, JLO, JHI, NX, NY, ILO, IHI, K + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, SIZE + IN( I ) = 1.0D0 + VIN( I ) = SIGMA**2 + END DO + + CALL ADDNOISE( SIZE, IN, SIGMA, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + NX = NX1 + Ny = NY1 + ELSE + NX = NX2 + Ny = NY2 + END IF + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + AST__GENVAR + + MAP = AST_ZOOMMAP( NDIM, 0.5D0, ' ', STATUS ) + CALL AST_REBINSEQD( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, VIN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADD, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, VOUT, WEIGHTS, NUSED, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + ILO = 6 + IHI = NINT(0.45*NX1) + JLO = 1 + JHI = 1 + ELSE + ILO = 6 + IHI = 41 + JLO = 8 + JHI = 91 + ENDIF + + SUM = 0.0D0 + SUM2 = 0.0D0 + SUM3 = 0.0D0 + NVAL = 0 + + DO I = ILO,IHI + DO J = JLO, JHI + K = ( J - 1 )*NX + I + IF( OUT(K) .NE. VAL__BADD ) THEN + SUM = SUM + OUT(K) + SUM2 = SUM2 + OUT(K)**2 + SUM3 = SUM3 + VOUT(K) + NVAL = NVAL + 1 + END IF + END DO + END DO + + SUM = SUM/NVAL + REALVAR = SUM2/NVAL - SUM*SUM + MEANVAR = SUM3/NVAL + IF( ABS( REALVAR - MEANVAR ) .GT. + : 0.05*( REALVAR + MEANVAR ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', REALVAR ) + CALL MSG_SETD( 'W', MEANVAR ) + CALL ERR_REP( ' ', 'Real variance is ^V - estimate is ^W.', + : STATUS ) + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test11 failed', STATUS ) + END IF + + END + + + SUBROUTINE TEST12( NDIM, SPREAD, PARAMS, STATUS ) + IMPLICIT NONE + INCLUDE 'SAE_PAR' + INCLUDE 'AST_PAR' + INCLUDE 'PRM_PAR' + + INTEGER SPREAD, STATUS, NDIM + + INTEGER SIZE + PARAMETER( SIZE = 20000 ) + + INTEGER NX1, NY1 + PARAMETER( NX1 = SIZE ) + PARAMETER( NY1 = 1 ) + + INTEGER NX2, NY2 + PARAMETER( NX2 = SIZE/200 ) + PARAMETER( NY2 = 200 ) + + DOUBLE PRECISION SIGMA + PARAMETER ( SIGMA = 0.1 ) + + REAL*8 IN( SIZE ), VIN( SIZE ) + REAL*8 OUT( SIZE ), VOUT( SIZE ) + DOUBLE PRECISION PARAMS(2), WEIGHTS( SIZE, 2 ) + DOUBLE PRECISION REALVAR,MEANVAR,SUM,SUM2,SUM3 + INTEGER MAP, LBND_IN(3), UBND_IN(3), LBND_OUT(3), UBND_OUT(3), + : FLAGS, I, J, NVAL,jlo, jhi,NX, NY, ILO, IHI, K + INTEGER*8 NUSED + + IF( STATUS .NE. SAI__OK ) RETURN + CALL AST_BEGIN( STATUS ) + + DO I = 1, SIZE + IN( I ) = 1.0D0 + VIN( I ) = SIGMA**2 + END DO + + CALL ADDNOISE( SIZE, IN, SIGMA, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + NX = NX1 + Ny = NY1 + ELSE + NX = NX2 + Ny = NY2 + END IF + + LBND_IN( 1 ) = 0 + LBND_IN( 2 ) = 1 + UBND_IN( 1 ) = NX - 1 + UBND_IN( 2 ) = NY + + LBND_OUT( 1 ) = 0 + LBND_OUT( 2 ) = 1 + UBND_OUT( 1 ) = NX - 1 + UBND_OUT( 2 ) = NY + + LBND_IN( 3 ) = 1 + UBND_IN( 3 ) = 1 + LBND_OUT( 3 ) = 1 + UBND_OUT( 3 ) = 1 + + FLAGS = AST__REBININIT + AST__REBINEND + AST__GENVAR + + : AST__CONSERVEFLUX + AST__VARWGT + + MAP = AST_ZOOMMAP( NDIM, 0.5D0, ' ', STATUS ) + CALL AST_REBINSEQD( MAP, 0.0D0, ndim, LBND_IN, UBND_IN, IN, VIN, + : spread, PARAMS, FLAGS, 0.01D0, 50, VAL__BADD, + : ndim, LBND_OUT, UBND_OUT, LBND_IN, UBND_IN, + : OUT, VOUT, WEIGHTS, NUSED, STATUS ) + + IF( NDIM .EQ. 1 ) THEN + ILO = 6 + IHI = NINT(0.45*NX1) + JLO = 1 + JHI = 1 + ELSE + ILO = 6 + IHI = 41 + JLO = 8 + JHI = 91 + ENDIF + + SUM = 0.0D0 + SUM2 = 0.0D0 + SUM3 = 0.0D0 + NVAL = 0 + + DO I = ILO,IHI + DO J = JLO, JHI + K = ( J - 1 )*NX + I + IF( OUT(K) .NE. VAL__BADD ) THEN + SUM = SUM + OUT(K) + SUM2 = SUM2 + OUT(K)**2 + SUM3 = SUM3 + VOUT(K) + NVAL = NVAL + 1 + END IF + END DO + END DO + + SUM = SUM/NVAL + REALVAR = SUM2/NVAL - SUM*SUM + MEANVAR = SUM3/NVAL + IF( ABS( REALVAR - MEANVAR ) .GT. + : 0.05*( REALVAR + MEANVAR ) ) THEN + STATUS = SAI__ERROR + CALL MSG_SETD( 'V', REALVAR ) + CALL MSG_SETD( 'W', MEANVAR ) + CALL ERR_REP( ' ', 'Real variance is ^V - estimate is ^W.', + : STATUS ) + END IF + + CALL AST_END( STATUS ) + IF( STATUS .NE. SAI__OK ) THEN + CALL ERR_REP( ' ', 'test12 failed', STATUS ) + END IF + + END + + diff --git a/ast_tester/testregions.f b/ast_tester/testregions.f new file mode 100644 index 0000000..201ab17 --- /dev/null +++ b/ast_tester/testregions.f @@ -0,0 +1,4032 @@ + program testregions + implicit none + include 'SAE_PAR' + integer status + + status = sai__ok + +c call ast_watchmemory( 282905 ) + + call ast_begin( status ) + call checkConvex( status ) + call checkRemoveRegions( status ) + call checkInterval( status ) + call checkEllipse( status ) + call checkPrism( status ) + call checkPolygon( status ) + call checkCircle( status ) + call checkBox( status ) + call checkNullRegion( status ) + call generalChecks( status ) + call checkCmpRegion( status ) + call checkPointList( status ) + + call ast_end( status ) + +c call ast_activememory( 'testregions' ) + + if( status .eq. sai__ok ) then + write(*,*) 'All Region tests passed' + else + write(*,*) 'Region tests failed' + end if + + end + + + subroutine generalChecks( status ) + implicit none + include 'AST_PAR' + include 'PRM_PAR' + include 'SAE_PAR' + + integer status, frm1, frm2, frm3, reg1, reg2, reg3, reg4, reg5 + double precision lbnd(3), ubnd(3), p1(2), p2(2) + + if( status .ne.sai__ok ) return + + call ast_begin( status ) + + + + lbnd(1) = 0.0D0 + lbnd(2) = AST__BAD + ubnd(1) = AST__BAD + ubnd(2) = 0.0D0 + frm1 = ast_frame( 2, ' ', status ) + reg1 = ast_interval( frm1, lbnd, ubnd, AST__NULL, ' ', status ) + + call ast_getregionbounds( reg1, lbnd, ubnd, status ) + if( lbnd(1) .ne. 0.0D0 ) call stopit( status, 'General 1' ) + if( lbnd(2) .gt. 0.99*val__mind ) call stopit( status, + : 'General 2' ) + if( ubnd(1) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 3' ) + if( ubnd(2) .ne. 0.0D0 ) call stopit( status, 'General 4' ) + + + + p1(1) = 0.0D0 + p1(2) = 0.0D0 + p2(1) = 1.0D0 + reg2 = ast_circle( frm1, 1, p1, p2, AST__NULL, ' ', status ) + + call ast_getregionbounds( reg2, lbnd, ubnd, status ) + if( lbnd(1) .ne. -1.0D0 ) call stopit( status, 'General 5' ) + if( lbnd(2) .ne. -1.0D0 ) call stopit( status, 'General 6' ) + if( ubnd(1) .ne. 1.0D0 ) call stopit( status, 'General 7' ) + if( ubnd(2) .ne. 1.0D0 ) call stopit( status, 'General 8' ) + + + + reg3 = ast_cmpregion( reg1, reg2, AST__OR, ' ', status ) + + call ast_getregionbounds( reg3, lbnd, ubnd, status ) + if( lbnd(1) .ne. -1.0D0 ) call stopit( status, 'General 9' ) + if( lbnd(2) .gt. 0.99*val__mind ) call stopit( status, + : 'General 10' ) + if( ubnd(1) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 11' ) + if( ubnd(2) .ne. 1.0D0 ) call stopit( status, 'General 12' ) + + + + lbnd(1) = -1.0D0 + ubnd(1) = 1.0D0 + frm2 = ast_frame( 1, ' ', status ) + reg4 = ast_interval( frm2, lbnd, ubnd, AST__NULL, ' ', status ) + + call ast_getregionbounds( reg4, lbnd, ubnd, status ) + if( lbnd(1) .ne. -1.0D0 ) call stopit( status, 'General 13' ) + if( ubnd(1) .ne. 1.0D0 ) call stopit( status, 'General 14' ) + + + + reg5 = ast_prism( reg3, reg4, ' ', status ) + + call ast_getregionbounds( reg5, lbnd, ubnd, status ) + if( lbnd(1) .ne. -1.0D0 ) call stopit( status, 'General 15' ) + if( lbnd(2) .gt. 0.99*val__mind ) call stopit( status, + : 'General 16' ) + if( ubnd(1) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 17' ) + if( ubnd(2) .ne. 1.0D0 ) call stopit( status, 'General 18' ) + if( lbnd(3) .ne. -1.0D0 ) call stopit( status, 'General 19' ) + if( ubnd(3) .ne. 1.0D0 ) call stopit( status, 'General 20' ) + + + + call ast_negate( reg2, status ) + reg3 = ast_cmpregion( reg1, reg2, AST__OR, ' ', status ) + + call ast_getregionbounds( reg3, lbnd, ubnd, status ) + if( lbnd(1) .gt. 0.99*val__mind ) call stopit( status, + : 'General 21' ) + if( lbnd(2) .gt. 0.99*val__mind ) call stopit( status, + : 'General 22' ) + if( ubnd(1) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 23' ) + if( ubnd(2) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 24' ) + + + reg5 = ast_prism( reg3, reg4, ' ', status ) + + call ast_getregionbounds( reg5, lbnd, ubnd, status ) + if( lbnd(1) .gt. 0.99*val__mind ) call stopit( status, + : 'General 25' ) + if( lbnd(2) .gt. 0.99*val__mind ) call stopit( status, + : 'General 26' ) + if( ubnd(1) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 27' ) + if( ubnd(2) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 28' ) + if( lbnd(3) .ne. -1.0D0 ) call stopit( status, 'General 29' ) + if( ubnd(3) .ne. 1.0D0 ) call stopit( status, 'General 30' ) + + + reg3 = ast_cmpregion( reg1, reg2, AST__AND, ' ', status ) + + call ast_getregionbounds( reg3, lbnd, ubnd, status ) + if( lbnd(1) .ne. 0.0D0 ) call stopit( status, 'General 31' ) + if( lbnd(2) .gt. 0.99*val__mind ) call stopit( status, + : 'General 32' ) + if( ubnd(1) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 33' ) + if( ubnd(2) .ne. 0.0D0 ) call stopit( status, 'General 34' ) + + + + reg5 = ast_prism( reg3, reg4, ' ', status ) + + call ast_getregionbounds( reg5, lbnd, ubnd, status ) + if( lbnd(1) .ne. 0.0D0 ) call stopit( status, 'General 35' ) + if( lbnd(2) .gt. 0.99*val__mind ) call stopit( status, + : 'General 36' ) + if( ubnd(1) .lt. 0.99*val__maxd ) call stopit( status, + : 'General 37' ) + if( ubnd(2) .ne. 0.0D0 ) call stopit( status, 'General 38' ) + if( lbnd(3) .ne. -1.0D0 ) call stopit( status, 'General 39' ) + if( ubnd(3) .ne. 1.0D0 ) call stopit( status, 'General 40' ) + + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'General tests failed' + + end + + + + + subroutine checkInterval( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + + integer status, frm1, frm2, frm3, unc, int1, int2, int3, int4, + : int5, frm4, map, outperm(6), inperm(6), pm, reg + double precision lbnd(3), ubnd(3), p(5,3), q(5,3),in(4,3),out(4,3) + double precision xin(9), yin(9), xout(9), yout(9) + + logical hasframeset + + if( status .ne.sai__ok ) return + + call ast_begin( status ) + + frm1 = ast_skyframe( ' ', status ) + frm2 = ast_specframe( 'Unit=Angstrom', status ) + frm3 = ast_cmpframe( frm1, frm2, ' ', status ) + + if( ast_getc( frm1, 'InternalUnit(1)', status ) .ne. 'rad' ) + : call stopit( status, 'InternalUnit 1' ) + if( ast_getc( frm1, 'InternalUnit(2)', status ) .ne. 'rad' ) + : call stopit( status, 'InternalUnit 2' ) + if( ast_getc( frm2, 'InternalUnit(1)', status ) .ne. 'Angstrom' ) + : call stopit( status, 'InternalUnit 3' ) + if( ast_getc( frm3, 'InternalUnit(1)', status ) .ne. 'rad' ) + : call stopit( status, 'InternalUnit 4' ) + if( ast_getc( frm3, 'InternalUnit(2)', status ) .ne. 'rad' ) + : call stopit( status, 'InternalUnit 5' ) + if( ast_getc( frm3, 'InternalUnit(3)', status ) .ne. 'Angstrom' ) + : call stopit( status, 'InternalUnit 6' ) + + lbnd( 1 ) = AST__BAD + lbnd( 2 ) = AST__BAD + lbnd( 3 ) = 5000.0 + ubnd( 1 ) = AST__BAD + ubnd( 2 ) = AST__BAD + ubnd( 3 ) = 6000.0 + + int1 = ast_interval( frm3, lbnd, ubnd, AST__NULL, ' ', status ) + call checkdump( int1, 'checkdump int1', status ) + + p(1,1) = 0.0 ! On boundary + p(1,2) = 0.0 + p(1,3) = 5000.0 + p(2,1) = 2.0 ! On boundary + p(2,2) = -1.0 + p(2,3) = 6000.0 + p(3,1) = -2.0 ! Inside + p(3,2) = 1.0 + p(3,3) = 5999.0 + p(4,1) = 2.0 ! Outside + p(4,2) = -2.0 + p(4,3) = 6010.0 + p(5,1) = 1.0 ! Outside + p(5,2) = -1.0 + p(5,3) = 4910.0 + + call ast_trann( int1, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. p(1,1)) call stopit( status, 'Interval 1' ) + if( q(1,2) .ne. p(1,2)) call stopit( status, 'Interval 1b' ) + if( q(1,3) .ne. p(1,3)) call stopit( status, 'Interval 1c' ) + if( q(2,1) .ne. p(2,1)) call stopit( status, 'Interval 2' ) + if( q(2,2) .ne. p(2,2)) call stopit( status, 'Interval 2b' ) + if( q(2,3) .ne. p(2,3)) call stopit( status, 'Interval 2c' ) + if( q(3,1) .ne. p(3,1)) call stopit( status, 'Interval 3' ) + if( q(3,2) .ne. p(3,2)) call stopit( status, 'Interval 3b' ) + if( q(3,3) .ne. p(3,3)) call stopit( status, 'Interval 3c' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Interval 4' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Interval 4b' ) + if( q(4,3) .ne. AST__BAD ) call stopit( status, 'Interval 4c' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Interval 5' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Interval 5b' ) + if( q(5,3) .ne. AST__BAD ) call stopit( status, 'Interval 5c' ) + + call ast_negate( int1, status ) + call ast_trann( int1, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. p(1,1)) call stopit( status, 'Interval 6' ) + if( q(1,2) .ne. p(1,2)) call stopit( status, 'Interval 6b' ) + if( q(1,3) .ne. p(1,3)) call stopit( status, 'Interval 6c' ) + if( q(2,1) .ne. p(2,1)) call stopit( status, 'Interval 7' ) + if( q(2,2) .ne. p(2,2)) call stopit( status, 'Interval 7b' ) + if( q(2,3) .ne. p(2,3)) call stopit( status, 'Interval 7c' ) + if( q(3,1) .ne. AST__BAD) call stopit( status, 'Interval 8' ) + if( q(3,2) .ne. AST__BAD) call stopit( status, 'Interval 8b' ) + if( q(3,3) .ne. AST__BAD) call stopit( status, 'Interval 8c' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Interval 9' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Interval 9b' ) + if( q(4,3) .ne. p(4,3) ) call stopit( status, 'Interval 9c' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Interval 10' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Interval 10b' ) + if( q(5,3) .ne. p(5,3) ) call stopit( status, 'Interval 10c' ) + + call ast_set( int1, 'closed=0,negated=0', status ) + call ast_trann( int1, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. AST__BAD ) call stopit( status, 'Interval 11' ) + if( q(1,2) .ne. AST__BAD ) call stopit( status, 'Interval 11b' ) + if( q(1,3) .ne. AST__BAD ) call stopit( status, 'Interval 11c' ) + if( q(2,1) .ne. AST__BAD ) call stopit( status, 'Interval 12' ) + if( q(2,2) .ne. AST__BAD ) call stopit( status, 'Interval 12b' ) + if( q(2,3) .ne. AST__BAD ) call stopit( status, 'Interval 12c' ) + if( q(3,1) .ne. p(3,1)) call stopit( status, 'Interval 13' ) + if( q(3,2) .ne. p(3,2)) call stopit( status, 'Interval 13b' ) + if( q(3,3) .ne. p(3,3)) call stopit( status, 'Interval 13c' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Interval 14' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Interval 14b' ) + if( q(4,3) .ne. AST__BAD ) call stopit( status, 'Interval 14c' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Interval 15' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Interval 15b' ) + if( q(5,3) .ne. AST__BAD ) call stopit( status, 'Interval 15c' ) + + call ast_negate( int1, status ) + call ast_trann( int1, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. AST__BAD) call stopit( status, 'Interval 16' ) + if( q(1,2) .ne. AST__BAD) call stopit( status, 'Interval 16b' ) + if( q(1,3) .ne. AST__BAD) call stopit( status, 'Interval 16c' ) + if( q(2,1) .ne. AST__BAD) call stopit( status, 'Interval 17' ) + if( q(2,2) .ne. AST__BAD) call stopit( status, 'Interval 17b' ) + if( q(2,3) .ne. AST__BAD) call stopit( status, 'Interval 17c' ) + if( q(3,1) .ne. AST__BAD) call stopit( status, 'Interval 18' ) + if( q(3,2) .ne. AST__BAD) call stopit( status, 'Interval 18b' ) + if( q(3,3) .ne. AST__BAD) call stopit( status, 'Interval 18c' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Interval 19' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Interval 19b' ) + if( q(4,3) .ne. p(4,3) ) call stopit( status, 'Interval 19c' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Interval 11' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Interval 11b' ) + if( q(5,3) .ne. p(5,3) ) call stopit( status, 'Interval 11c' ) + + + lbnd( 1 ) = AST__BAD + lbnd( 2 ) = AST__BAD + lbnd( 3 ) = 6000.0 + ubnd( 1 ) = AST__BAD + ubnd( 2 ) = AST__BAD + ubnd( 3 ) = 5000.0 + + int2 = ast_interval( frm3, lbnd, ubnd, AST__NULL, ' ', status ) + call checkdump( int2, 'checkdump int2', status ) + + p(1,1) = 0.0 ! On boundary + p(1,2) = 0.0 + p(1,3) = 5000.0 + p(2,1) = 2.0 ! On boundary + p(2,2) = -1.0 + p(2,3) = 6000.0 + p(3,1) = -2.0 ! Outside + p(3,2) = 1.0 + p(3,3) = 5999.0 + p(4,1) = 2.0 ! Inside + p(4,2) = -2.0 + p(4,3) = 6010.0 + p(5,1) = 1.0 ! Inside + p(5,2) = -1.0 + p(5,3) = 4910.0 + + call ast_negate( int2, status ) + call ast_trann( int2, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. p(1,1)) call stopit( status, 'Interval B 1' ) + if( q(1,2) .ne. p(1,2)) call stopit( status, 'Interval B 1b' ) + if( q(1,3) .ne. p(1,3)) call stopit( status, 'Interval B 1c' ) + if( q(2,1) .ne. p(2,1)) call stopit( status, 'Interval B 2' ) + if( q(2,2) .ne. p(2,2)) call stopit( status, 'Interval B 2b' ) + if( q(2,3) .ne. p(2,3)) call stopit( status, 'Interval B 2c' ) + if( q(3,1) .ne. p(3,1)) call stopit( status, 'Interval B 3' ) + if( q(3,2) .ne. p(3,2)) call stopit( status, 'Interval B 3b' ) + if( q(3,3) .ne. p(3,3)) call stopit( status, 'Interval B 3c' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Interval B 4' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Interval B 4b' ) + if( q(4,3) .ne. AST__BAD ) call stopit( status, 'Interval B 4c' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Interval B 5' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Interval B 5b' ) + if( q(5,3) .ne. AST__BAD ) call stopit( status, 'Interval B 5c' ) + + call ast_negate( int2, status ) + call ast_trann( int2, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. p(1,1)) call stopit( status, 'Interval B 6' ) + if( q(1,2) .ne. p(1,2)) call stopit( status, 'Interval B 6b' ) + if( q(1,3) .ne. p(1,3)) call stopit( status, 'Interval B 6c' ) + if( q(2,1) .ne. p(2,1)) call stopit( status, 'Interval B 7' ) + if( q(2,2) .ne. p(2,2)) call stopit( status, 'Interval B 7b' ) + if( q(2,3) .ne. p(2,3)) call stopit( status, 'Interval B 7c' ) + if( q(3,1) .ne. AST__BAD) call stopit( status, 'Interval B 8' ) + if( q(3,2) .ne. AST__BAD) call stopit( status, 'Interval B 8b' ) + if( q(3,3) .ne. AST__BAD) call stopit( status, 'Interval B 8c' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Interval B 9' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Interval B 9b' ) + if( q(4,3) .ne. p(4,3) ) call stopit( status, 'Interval B 9c' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Interval B 10' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Interval B 10b' ) + if( q(5,3) .ne. p(5,3) ) call stopit( status, 'Interval B 10c' ) + + call ast_set( int2, 'closed=0,negated=1', status ) + call ast_trann( int2, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. AST__BAD ) call stopit( status, 'Interval B 11' ) + if( q(1,2) .ne. AST__BAD ) call stopit( status, 'Interval B 11b' ) + if( q(1,3) .ne. AST__BAD ) call stopit( status, 'Interval B 11c' ) + if( q(2,1) .ne. AST__BAD ) call stopit( status, 'Interval B 12' ) + if( q(2,2) .ne. AST__BAD ) call stopit( status, 'Interval B 12b' ) + if( q(2,3) .ne. AST__BAD ) call stopit( status, 'Interval B 12c' ) + if( q(3,1) .ne. p(3,1)) call stopit( status, 'Interval B 13' ) + if( q(3,2) .ne. p(3,2)) call stopit( status, 'Interval B 13b' ) + if( q(3,3) .ne. p(3,3)) call stopit( status, 'Interval B 13c' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Interval B 14' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Interval B 14b' ) + if( q(4,3) .ne. AST__BAD ) call stopit( status, 'Interval B 14c' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Interval B 15' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Interval B 15b' ) + if( q(5,3) .ne. AST__BAD ) call stopit( status, 'Interval B 15c' ) + + call ast_negate( int2, status ) + call ast_trann( int2, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. AST__BAD) call stopit( status, 'Interval B 16' ) + if( q(1,2) .ne. AST__BAD) call stopit( status, 'Interval B 16b' ) + if( q(1,3) .ne. AST__BAD) call stopit( status, 'Interval B 16c' ) + if( q(2,1) .ne. AST__BAD) call stopit( status, 'Interval B 17' ) + if( q(2,2) .ne. AST__BAD) call stopit( status, 'Interval B 17b' ) + if( q(2,3) .ne. AST__BAD) call stopit( status, 'Interval B 17c' ) + if( q(3,1) .ne. AST__BAD) call stopit( status, 'Interval B 18' ) + if( q(3,2) .ne. AST__BAD) call stopit( status, 'Interval B 18b' ) + if( q(3,3) .ne. AST__BAD) call stopit( status, 'Interval B 18c' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Interval B 19' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Interval B 19b' ) + if( q(4,3) .ne. p(4,3) ) call stopit( status, 'Interval B 19c' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Interval B 11' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Interval B 11b' ) + if( q(5,3) .ne. p(5,3) ) call stopit( status, 'Interval B 11c' ) + + + + + lbnd( 1 ) = AST__BAD + lbnd( 2 ) = AST__BAD + lbnd( 3 ) = 5000.0 + ubnd( 1 ) = 0.5 + ubnd( 2 ) = AST__BAD + ubnd( 3 ) = AST__BAD + + int3 = ast_interval( frm3, lbnd, ubnd, AST__NULL, ' ', status ) + + call checkdump( int3, 'checkdump int3', status ) + + p(1,1) = 0.0 ! On boundary + p(1,2) = 0.0 + p(1,3) = 5000.0 + p(2,1) = 0.5 ! On boundary + p(2,2) = -1.0 + p(2,3) = 6000.0 + p(3,1) = -2.0 ! Inside + p(3,2) = 0.4 + p(3,3) = 5999.0 + p(4,1) = 2.0 ! Outside + p(4,2) = -2.0 + p(4,3) = 6010.0 + p(5,1) = 0.0 ! Outside + p(5,2) = -3.0 + p(5,3) = 4910.0 + + call ast_trann( int3, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. p(1,1)) call stopit( status, 'Interval C 1' ) + if( q(1,2) .ne. p(1,2)) call stopit( status, 'Interval C 1b' ) + if( q(1,3) .ne. p(1,3)) call stopit( status, 'Interval C 1c' ) + if( q(2,1) .ne. p(2,1)) call stopit( status, 'Interval C 2' ) + if( q(2,2) .ne. p(2,2)) call stopit( status, 'Interval C 2b' ) + if( q(2,3) .ne. p(2,3)) call stopit( status, 'Interval C 2c' ) + if( q(3,1) .ne. p(3,1)) call stopit( status, 'Interval C 3' ) + if( q(3,2) .ne. p(3,2)) call stopit( status, 'Interval C 3b' ) + if( q(3,3) .ne. p(3,3)) call stopit( status, 'Interval C 3c' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Interval C 4' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Interval C 4b' ) + if( q(4,3) .ne. AST__BAD ) call stopit( status, 'Interval C 4c' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Interval C 5' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Interval C 5b' ) + if( q(5,3) .ne. AST__BAD ) call stopit( status, 'Interval C 5c' ) + + call ast_negate( int3, status ) + call ast_trann( int3, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. p(1,1)) call stopit( status, 'Interval C 6' ) + if( q(1,2) .ne. p(1,2)) call stopit( status, 'Interval C 6b' ) + if( q(1,3) .ne. p(1,3)) call stopit( status, 'Interval C 6c' ) + if( q(2,1) .ne. p(2,1)) call stopit( status, 'Interval C 7' ) + if( q(2,2) .ne. p(2,2)) call stopit( status, 'Interval C 7b' ) + if( q(2,3) .ne. p(2,3)) call stopit( status, 'Interval C 7c' ) + if( q(3,1) .ne. AST__BAD) call stopit( status, 'Interval C 8' ) + if( q(3,2) .ne. AST__BAD) call stopit( status, 'Interval C 8b' ) + if( q(3,3) .ne. AST__BAD) call stopit( status, 'Interval C 8c' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Interval C 9' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Interval C 9b' ) + if( q(4,3) .ne. p(4,3) ) call stopit( status, 'Interval C 9c' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Interval C 10' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Interval C 10b' ) + if( q(5,3) .ne. p(5,3) ) call stopit( status, 'Interval C 10c' ) + + call ast_set( int3, 'closed=0,negated=0', status ) + call ast_trann( int3, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. AST__BAD ) call stopit( status, 'Interval C 11' ) + if( q(1,2) .ne. AST__BAD ) call stopit( status, 'Interval C 11b' ) + if( q(1,3) .ne. AST__BAD ) call stopit( status, 'Interval C 11c' ) + if( q(2,1) .ne. AST__BAD ) call stopit( status, 'Interval C 12' ) + if( q(2,2) .ne. AST__BAD ) call stopit( status, 'Interval C 12b' ) + if( q(2,3) .ne. AST__BAD ) call stopit( status, 'Interval C 12c' ) + if( q(3,1) .ne. p(3,1)) call stopit( status, 'Interval C 13' ) + if( q(3,2) .ne. p(3,2)) call stopit( status, 'Interval C 13b' ) + if( q(3,3) .ne. p(3,3)) call stopit( status, 'Interval C 13c' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Interval C 14' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Interval C 14b' ) + if( q(4,3) .ne. AST__BAD ) call stopit( status, 'Interval C 14c' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Interval C 15' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Interval C 15b' ) + if( q(5,3) .ne. AST__BAD ) call stopit( status, 'Interval C 15c' ) + + call ast_negate( int3, status ) + call ast_trann( int3, 5, 3, 5, p, .true., 3, 5, q, status ) + if( q(1,1) .ne. AST__BAD) call stopit( status, 'Interval C 16' ) + if( q(1,2) .ne. AST__BAD) call stopit( status, 'Interval C 16b' ) + if( q(1,3) .ne. AST__BAD) call stopit( status, 'Interval C 16c' ) + if( q(2,1) .ne. AST__BAD) call stopit( status, 'Interval C 17' ) + if( q(2,2) .ne. AST__BAD) call stopit( status, 'Interval C 17b' ) + if( q(2,3) .ne. AST__BAD) call stopit( status, 'Interval C 17c' ) + if( q(3,1) .ne. AST__BAD) call stopit( status, 'Interval C 18' ) + if( q(3,2) .ne. AST__BAD) call stopit( status, 'Interval C 18b' ) + if( q(3,3) .ne. AST__BAD) call stopit( status, 'Interval C 18c' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Interval C 19' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Interval C 19b' ) + if( q(4,3) .ne. p(4,3) ) call stopit( status, 'Interval C 19c' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Interval C 11' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Interval C 11b' ) + if( q(5,3) .ne. p(5,3) ) call stopit( status, 'Interval C 11c' ) + + + + lbnd( 1 ) = AST__BAD + lbnd( 2 ) = 0.0 + lbnd( 3 ) = AST__BAD + ubnd( 1 ) = AST__BAD + ubnd( 2 ) = -1.0 + ubnd( 3 ) = 6000.0 + + call ast_setl( int3, 'Negated', .false., status ) + int4 = ast_interval( frm3, lbnd, ubnd, AST__NULL, ' ', status ) + if( ast_overlap( int3, int4, status ) .ne. 4 ) + : call stopit( status, 'Interval overlap 1' ) + + call ast_negate( int3, status ) + if( ast_overlap( int3, int4, status ) .ne. 4 ) + : call stopit( status, 'Interval overlap 2' ) + + call ast_negate( int4, status ) + if( ast_overlap( int3, int4, status ) .ne. 4 ) + : call stopit( status, 'Interval overlap 3' ) + + call ast_negate( int3, status ) + if( ast_overlap( int3, int4, status ) .ne. 4 ) + : call stopit( status, 'Interval overlap 4' ) + + + lbnd( 1 ) = 0.6 + lbnd( 2 ) = 0.0 + lbnd( 3 ) = AST__BAD + ubnd( 1 ) = AST__BAD + ubnd( 2 ) = -1.0 + ubnd( 3 ) = 6000.0 + + int4 = ast_interval( frm3, lbnd, ubnd, AST__NULL, ' ', status ) + if( ast_overlap( int3, int4, status ) .ne. 1 ) + : call stopit( status, 'Interval overlap 5' ) + + call ast_negate( int3, status ) + if( ast_overlap( int3, int4, status ) .ne. 3 ) + : call stopit( status, 'Interval overlap 6' ) + + call ast_negate( int4, status ) + if( ast_overlap( int3, int4, status ) .ne. 4 ) + : call stopit( status, 'Interval overlap 7' ) + + call ast_negate( int3, status ) + if( ast_overlap( int3, int4, status ) .ne. 2 ) + : call stopit( status, 'Interval overlap 8' ) + + + int4 = ast_copy( int3, status ) + if( ast_overlap( int3, int4, status ) .ne. 5 ) + : call stopit( status, 'Interval overlap 9' ) + + call ast_negate( int4, status ) + if( ast_overlap( int3, int4, status ) .ne. 6 ) + : call stopit( status, 'Interval overlap 10' ) + + + +* Changing the number of axes in the Interval. + + frm1 = ast_frame( 2, 'Domain=A', status ) + + lbnd(1) = 0.0 + lbnd(2) = 0.0 + ubnd(1) = 0.01 + ubnd(2) = 0.01 + unc = ast_box( frm1, 0, lbnd, ubnd, AST__NULL, ' ', status ) + + lbnd(1) = -2.0 + lbnd(2) = 0.5 + ubnd(1) = 0.0 + ubnd(2) = AST__BAD + int1 = ast_interval( frm1, lbnd, ubnd, unc, ' ', status ) + + outperm(1) = 2 + outperm(2) = -1 + outperm(3) = 1 + + inperm(1) = 3 + inperm(2) = 1 + + pm = ast_permmap( 2, inperm, 3, outperm, 0.0D0, ' ', status ) + + frm2 = ast_frame( 3, 'Domain=B', status ) + reg = ast_mapregion( int1, pm, frm2, status ) + + if( .not. ast_isainterval( reg, status ) ) call stopit( status, + : 'Int: perm check 1' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Int: perm check 2' ) + if( ast_geti( reg, 'naxes', status ) .ne. 3 ) call stopit( status, + : 'Int: perm check 3' ) + + in( 1, 1 ) = 0.0 ! Outside + in( 1, 2 ) = 0.0 + in( 1, 3 ) = -0.5 + in( 2, 1 ) = 20.0 ! Inside + in( 2, 2 ) = 0.0 + in( 2, 3 ) = -0.5 + in( 3, 1 ) = 20.0 ! Outside + in( 3, 2 ) = -10.0 + in( 3, 3 ) = 0.5 + in( 4, 1 ) = 20.0 ! Boundary + in( 4, 2 ) = 0.0 + in( 4, 3 ) = -2.0 + + call ast_trann( reg, 4, 3, 4, in, .true., 3, 4, out, status ) + + if( out( 1, 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 1' ) + if( out( 1, 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 2' ) + if( out( 1, 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 3' ) + + if( out( 2, 1 ) .ne. in( 2,1 )) call stopit( status, 'Int: pc 4' ) + if( out( 2, 2 ) .ne. in( 2,2 )) call stopit( status, 'Int: pc 5' ) + if( out( 2, 3 ) .ne. in( 2,3 )) call stopit( status, 'Int: pc 6' ) + + if( out( 3, 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 7' ) + if( out( 3, 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 8' ) + if( out( 3, 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 9' ) + + if( out( 4, 1 ) .ne. in( 4,1 )) call stopit( status, 'Int: pc 10') + if( out( 4, 2 ) .ne. in( 4,2 )) call stopit( status, 'Int: pc 11') + if( out( 4, 3 ) .ne. in( 4,3 )) call stopit( status, 'Int: pc 12') + + + + outperm(1) = 2 + outperm(2) = -1 + outperm(3) = 1 + + inperm(1) = 3 + inperm(2) = 1 + + pm = ast_permmap( 2, inperm, 3, outperm, 1.5D0, ' ', status ) + + frm2 = ast_frame( 3, 'Domain=B', status ) + reg = ast_mapregion( int1, pm, frm2, status ) + + if( .not. ast_isainterval( reg, status ) ) call stopit( status, + : 'Int: perm check 4' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Int: perm check 5' ) + if( ast_geti( reg, 'naxes', status ) .ne. 3 ) call stopit( status, + : 'Int: perm check 6' ) + + in( 1, 1 ) = 20.0 ! Outside + in( 1, 2 ) = 0.0 + in( 1, 3 ) = -0.5 + in( 2, 1 ) = 20.0 ! Inside + in( 2, 2 ) = 1.5 + in( 2, 3 ) = -0.5 + in( 3, 1 ) = 20.0 ! Outside + in( 3, 2 ) = 1.6 + in( 3, 3 ) = -0.5 + in( 4, 1 ) = 0.5 ! Boundary + in( 4, 2 ) = 1.5 + in( 4, 3 ) = 0.0 + + call ast_trann( reg, 4, 3, 4, in, .true., 3, 4, out, status ) + + if( out( 1, 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 13') + if( out( 1, 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 14') + if( out( 1, 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 15') + + if( out( 2, 1 ) .ne. in( 2,1 )) call stopit( status, 'Int: pc 16') + if( out( 2, 2 ) .ne. in( 2,2 )) call stopit( status, 'Int: pc 17') + if( out( 2, 3 ) .ne. in( 2,3 )) call stopit( status, 'Int: pc 18') + + if( out( 3, 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 19') + if( out( 3, 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 20') + if( out( 3, 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 21') + + if( out( 4, 1 ) .ne. in( 4,1 )) call stopit( status, 'Int: pc 22') + if( out( 4, 2 ) .ne. in( 4,2 )) call stopit( status, 'Int: pc 23') + if( out( 4, 3 ) .ne. in( 4,3 )) call stopit( status, 'Int: pc 24') + + + + call ast_negate( int1, status ) + call ast_set( int1, 'closed=0', status ) + reg = ast_mapregion( int1, pm, frm2, status ) + call ast_negate( int1, status ) + call ast_set( int1, 'closed=1', status ) + + if( .not. ast_isainterval( reg, status ) ) call stopit( status, + : 'Int: perm check 7' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Int: perm check 8' ) + if( ast_geti( reg, 'naxes', status ) .ne. 3 ) call stopit( status, + : 'Int: perm check 9' ) + + in( 1, 1 ) = 20.0 ! Inside + in( 1, 2 ) = 0.0 + in( 1, 3 ) = -0.5 + in( 2, 1 ) = 20.0 ! Outside + in( 2, 2 ) = 1.5 + in( 2, 3 ) = -0.5 + in( 3, 1 ) = 20.0 ! Inside + in( 3, 2 ) = 1.6 + in( 3, 3 ) = -0.5 + in( 4, 1 ) = 0.5 ! Outside + in( 4, 2 ) = 1.5 + in( 4, 3 ) = 0.0 + + call ast_trann( reg, 4, 3, 4, in, .true., 3, 4, out, status ) + + if( out( 1, 1 ) .ne. in( 1,1 )) call stopit( status, 'Int: pc 25') + if( out( 1, 2 ) .ne. in( 1,2 )) call stopit( status, 'Int: pc 26') + if( out( 1, 3 ) .ne. in( 1,3 )) call stopit( status, 'Int: pc 27') + + if( out( 2, 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 28') + if( out( 2, 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 29') + if( out( 2, 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 30') + + if( out( 3, 1 ) .ne. in( 3,1 )) call stopit( status, 'Int: pc 31') + if( out( 3, 2 ) .ne. in( 3,2 )) call stopit( status, 'Int: pc 32') + if( out( 3, 3 ) .ne. in( 3,3 )) call stopit( status, 'Int: pc 33') + + if( out( 4, 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 34') + if( out( 4, 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 35') + if( out( 4, 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 36') + + + + + frm1 = ast_frame( 3, 'Domain=A', status ) + + + lbnd(1) = 0.0 + lbnd(2) = 0.0 + lbnd(3) = 0.0 + ubnd(1) = 0.01 + ubnd(2) = 0.01 + ubnd(3) = 0.01 + unc = ast_box( frm1, 0, lbnd, ubnd, AST__NULL, ' ', status ) + + lbnd(1) = 0.5 + lbnd(2) = -1.0 + lbnd(3) = -2.0 + ubnd(1) = AST__BAD + ubnd(2) = AST__BAD + ubnd(3) = 0.0 + + int1 = ast_interval( frm1, lbnd, ubnd, unc, ' ', status ) + + outperm(1) = 1 + outperm(2) = 3 + + inperm(1) = 1 + inperm(2) = -1 + inperm(3) = 2 + + pm = ast_permmap( 3, inperm, 2, outperm, 1.0D0, ' ', status ) + + frm2 = ast_frame( 2, 'Domain=B', status ) + reg = ast_mapregion( int1, pm, frm2, status ) + + if( .not. ast_isainterval( reg, status ) ) call stopit( status, + : 'Int: perm check 10' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Int: perm check 11' ) + if( ast_geti( reg, 'naxes', status ) .ne. 2 ) call stopit( status, + : 'Int: perm check 12' ) + + xin( 1 ) = 0.4 ! Out + yin( 1 ) = -1.0 + xin( 2 ) = 1.0 ! Out + yin( 2 ) = 0.1 + xin( 3 ) = 1.0 ! Out + yin( 3 ) = -2.1 + xin( 4 ) = 0.5 ! Boundary + yin( 4 ) = -1.0 + xin( 5 ) = 10.0 ! In + yin( 5 ) = -0.1 + xin( 6 ) = 0.55 ! Boundary + yin( 6 ) = -2.0 + + + call ast_tran2( reg, 6, xin, yin, .true., xout, yout, status ) + + if( xout( 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 37') + if( yout( 1 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 38') + if( xout( 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 39') + if( yout( 2 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 40') + if( xout( 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 41') + if( yout( 3 ) .ne. AST__BAD ) call stopit( status, 'Int: pc 42') + if( xout( 4 ) .ne. xin( 4 ) ) call stopit( status, 'Int: pc 43') + if( yout( 4 ) .ne. yin( 4 ) ) call stopit( status, 'Int: pc 44') + if( xout( 5 ) .ne. xin( 5 ) ) call stopit( status, 'Int: pc 45') + if( yout( 5 ) .ne. yin( 5 ) ) call stopit( status, 'Int: pc 46') + if( xout( 6 ) .ne. xin( 6 ) ) call stopit( status, 'Int: pc 47') + if( yout( 6 ) .ne. yin( 6 ) ) call stopit( status, 'Int: pc 48') + + + pm = ast_permmap( 3, inperm, 2, outperm, -2.0D0, ' ', status ) + reg = ast_mapregion( int1, pm, frm2, status ) + + if( .not. ast_isanullregion( reg, status ) ) call stopit( status, + : 'Int: perm check 13' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Int: perm check 14' ) + if( ast_geti( reg, 'naxes', status ) .ne. 2 ) call stopit( status, + : 'Int: perm check 15' ) + if( ast_getl( reg, 'negated', status ) ) call stopit( status, + : 'Int: perm check 16' ) + + + call ast_negate( int1, status ) + reg = ast_mapregion( int1, pm, frm2, status ) + + if( .not. ast_isanullregion( reg, status ) ) call stopit( status, + : 'Int: perm check 17' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Int: perm check 18' ) + if( ast_geti( reg, 'naxes', status ) .ne. 2 ) call stopit( status, + : 'Int: perm check 19' ) + if( .NOT.ast_getl( reg, 'negated', status ) ) call stopit( status, + : 'Int: perm check 20' ) + + + + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'Interval tests failed' + + end + + + subroutine checkPolygon( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + + integer status, frm, unc, pol1, pol2, f2, r2, r3, r4 + double precision pi, p(5,2), q(5,2), p1(2), p2(2) + double precision xin(2), yin(2), xout(2), yout(2), lbnd(5), + : ubnd(5) + logical hasframeset + + + if( status .ne.sai__ok ) return + + call ast_begin( status ) + + pi = acos( -1.0d0 ) + + frm = ast_SkyFrame( ' ', status ) + + p1(1) = 0.0 + p1(2) = 0.5*pi + p2(1) = 0.01 + unc = ast_circle( frm, 1, p1, p2, AST__NULL, ' ', status ) + + p(1,1) = 0.0 + p(1,2) = 0.0 + p(2,1) = 1.0 + p(2,2) = 0.5*pi + p(3,1) = 0.5*pi + p(3,2) = 0.25*pi + p(4,1) = 0.25*pi + p(4,2) = 0.0 + p(5,1) = 0.25*pi + p(5,2) = 0.25*pi + + pol1 = ast_polygon( frm, 5, 5, p, unc, 'closed=0', status ) + + call checkdump( pol1, 'checkdump pol1', status ) + + + p(1,1) = 0.0 ! On boundary + p(1,2) = 0.0 + p(2,1) = 1.0 ! Outside + p(2,2) = 0.5*pi + 0.1 + p(3,1) = 0.5*pi - 0.1 ! Inside + p(3,2) = 0.25*pi + p(4,1) = 0.0 ! On boundary + p(4,2) = 0.1 + p(5,1) = 0.25*pi ! Inside + p(5,2) = 0.25*pi + 0.1 + + call ast_trann( pol1, 5, 2, 5, p, .true., 2, 5, q, status ) + + if( q(1,1) .ne. AST__BAD ) call stopit( status, 'Poly 1' ) + if( q(1,2) .ne. AST__BAD ) call stopit( status, 'Poly 1b' ) + if( q(2,1) .ne. AST__BAD ) call stopit( status, 'Poly 2' ) + if( q(2,2) .ne. AST__BAD ) call stopit( status, 'Poly 2b' ) + if( q(3,1) .ne. p(3,1) ) call stopit( status, 'Poly 3' ) + if( q(3,2) .ne. p(3,2) ) call stopit( status, 'Poly 3b' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Poly 4' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Poly 4b' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Poly 5' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Poly 5b' ) + + + call ast_setl( pol1, 'closed', .true., status ) + call ast_trann( pol1, 5, 2, 5, p, .true., 2, 5, q, status ) + if( q(1,1) .ne. p(1,1) ) call stopit( status, 'Poly 6' ) + if( q(1,2) .ne. p(1,2) ) call stopit( status, 'Poly 6b' ) + if( q(2,1) .ne. AST__BAD ) call stopit( status, 'Poly 7' ) + if( q(2,2) .ne. AST__BAD ) call stopit( status, 'Poly 7b' ) + if( q(3,1) .ne. p(3,1) ) call stopit( status, 'Poly 8' ) + if( q(3,2) .ne. p(3,2) ) call stopit( status, 'Poly 8b' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Poly 9' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Poly 9b' ) + if( q(5,1) .ne. p(5,1) ) call stopit( status, 'Poly 10' ) + if( q(5,2) .ne. p(5,2) ) call stopit( status, 'Poly 10b' ) + + call ast_setl( pol1, 'negated', .true., status ) + call ast_trann( pol1, 5, 2, 5, p, .true., 2, 5, q, status ) + if( q(1,1) .ne. p(1,1) ) call stopit( status, 'Poly 11' ) + if( q(1,2) .ne. p(1,2) ) call stopit( status, 'Poly 11b' ) + if( q(2,1) .ne. p(2,1) ) call stopit( status, 'Poly 12' ) + if( q(2,2) .ne. p(2,2) ) call stopit( status, 'Poly 12b' ) + if( q(3,1) .ne. AST__BAD ) call stopit( status, 'Poly 13' ) + if( q(3,2) .ne. AST__BAD ) call stopit( status, 'Poly 13b' ) + if( q(4,1) .ne. p(4,1) ) call stopit( status, 'Poly 14' ) + if( q(4,2) .ne. p(4,2) ) call stopit( status, 'Poly 14b' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Poly 15' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Poly 15b' ) + + call ast_setl( pol1, 'closed', .false., status ) + call ast_trann( pol1, 5, 2, 5, p, .true., 2, 5, q, status ) + if( q(1,1) .ne. AST__BAD ) call stopit( status, 'Poly 16' ) + if( q(1,2) .ne. AST__BAD ) call stopit( status, 'Poly 16b' ) + if( q(2,1) .ne. p(2,1) ) call stopit( status, 'Poly 17' ) + if( q(2,2) .ne. p(2,2) ) call stopit( status, 'Poly 17b' ) + if( q(3,1) .ne. AST__BAD ) call stopit( status, 'Poly 18' ) + if( q(3,2) .ne. AST__BAD ) call stopit( status, 'Poly 18b' ) + if( q(4,1) .ne. AST__BAD ) call stopit( status, 'Poly 19' ) + if( q(4,2) .ne. AST__BAD ) call stopit( status, 'Poly 19b' ) + if( q(5,1) .ne. AST__BAD ) call stopit( status, 'Poly 20' ) + if( q(5,2) .ne. AST__BAD ) call stopit( status, 'Poly 20b' ) + + + if( hasframeset( pol1, status ) ) then + call stopit( status, 'pol1 has FrameSet' ) + end if + + call ast_setc( pol1, 'system', 'fk5', status ) + call checkdump( pol1, 'checkdump pol2', status ) + + if( .not. hasframeset( pol1, status ) ) then + call stopit( status, 'pol1 does not have FrameSet' ) + end if + + call ast_seti( pol1, 'meshsize', 30, status ) + + pol2 = ast_simplify( pol1, status ) + + if( hasframeset( pol2, status ) ) then + call stopit( status, 'pol2 has FrameSet' ) + end if + + + + frm = ast_SkyFrame( ' ', status ) + + p1(1) = 0.0 + p1(2) = 0.5*pi + p2(1) = 0.01 + unc = ast_circle( frm, 1, p1, p2, AST__NULL, ' ', status ) + + p(1,1) = 1.5*pi + p(1,2) = 0.4*pi + p(2,1) = pi + p(2,2) = 0.4*pi + p(3,1) = 0.5*pi + p(3,2) = 0.4*pi + p(4,1) = 0.0 + p(4,2) = 0.4*pi + + pol1 = ast_polygon( frm, 4, 5, p, unc, ' ', status ) + + xin(1) = 0.0 + yin(1) = 0.5*pi + call ast_tran2( pol1, 1, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. xin(1) ) call stopit( status, 'Poly 21' ) + if( yout(1) .ne. yin(1) ) call stopit( status, 'Poly 22' ) + + call ast_getregionbounds( pol1, lbnd, ubnd, status ) + if( abs( lbnd(1) ) .gt. 1.0E-10 ) call stopit( status, 'Poly 23' ) + if( abs( lbnd(2) - 1.25663708 ) .gt. 1.0E-6 ) + : call stopit( status, 'Poly 24' ) + if( abs( ubnd(1) - 6.28318531 ) .gt. 1.0E-6 ) + : call stopit( status, 'Poly 25' ) + if( abs( ubnd(2) - 1.57079633 ) .gt. 1.0E-6 ) + : call stopit( status, 'Poly 26' ) + + + f2 = ast_specframe( 'Unit=Angstrom', status ) + lbnd( 1 ) = 5000.0 + ubnd( 1 ) = 6000.0 + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r3 = ast_prism( pol1, r2, ' ', status ) + r4 = ast_Simplify( r3, status ) + + call ast_getregionbounds( r4, lbnd, ubnd, status ) + if( abs( lbnd(1) ) .gt. 1.0E-10 ) call stopit( status, 'Poly 27' ) + if( abs( lbnd(2) - 1.25663708 ) .gt. 1.0E-6 ) + : call stopit( status, 'Poly 28' ) + if( abs( ubnd(1) - 6.28318531 ) .gt. 1.0E-6 ) + : call stopit( status, 'Poly 29' ) + if( abs( ubnd(2) - 1.57079633 ) .gt. 1.0E-6 ) + : call stopit( status, 'Poly 30' ) + if( abs( lbnd(3) - 5000.0 ) .gt. 1.0E-10 ) + : call stopit( status, 'Poly 31' ) + if( abs( ubnd(3) - 6000.0 ) .gt. 1.0E-6 ) + : call stopit( status, 'Poly 32' ) + + + + + + + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'Polygon tests failed' + + end + + + + + + subroutine checkBox( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, box1, frm1, i, fc, fs, map1, perm(3), frm2, box3, + : box2, frm3, map2, res, j, bfrm, cfrm, reg1, map, + : npoint + double precision p1(3), p2(3), v2, xin(9), yin(9), xout(9), + : yout(9),in(4,3),out(4,3),matrix(9),grid(250,2) + character*(AST__SZCHR) t1, t2, cards(9)*80 + logical hasframeset + + integer lbnd_in(2), ubnd_in(2) + real rin(5,5),image(50,50) + integer outperm(3), inperm(3),pm, reg, unc + + + data cards /'CTYPE1 = ''RA---TAN''', + : 'CTYPE2 = ''DEC--TAN''', + : 'CRPIX1 = 100', + : 'CRPIX2 = 100', + : 'CRVAL1 = 71.619724', + : 'CRVAL2 = 42.971835', + : ' ', + : 'CDELT1 = 0.6', + : 'CDELT2 = 0.6' / + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + + fc = ast_fitschan( ast_null, ast_null, ' ', status ) + do i = 1, 9 + call ast_putfits( fc, cards(i), .false., status ) + end do + call ast_clear( fc, 'card', status ) + fs = ast_read( fc, status ) + + frm1 = ast_getframe( fs, ast__current, status ) + call ast_seti( frm1, 'digits(1)', 12, status ) + + p1( 1 ) = 1.25 + p1( 2 ) = 0.75 + p2( 1 ) = 1.5 + p2( 2 ) = 0.5 + + box1 = ast_box( frm1, 0, p1, p2, AST__NULL, ' ', status ) + call checkdump( box1, 'checkdump box1', status ) + + if( ast_getc( box1, 'system', status ) .ne. 'ICRS' ) + : call stopit( status, 'box1 system is not ICRS' ) + + call ast_setc( box1, 'system', 'galactic', status ) + + perm(1)=2 + perm(2)=1 + call ast_permaxes( box1, perm, status ) + + box3 = ast_copy( box1, status ) + + yin(1) = 2.82175432250852 + xin(1) = -0.0269096590283195 + yin(2) = 2.70798275154741 + xin(2) = 0.2467384819891 + + call ast_tran2( box1, 2, xin, yin, .true., xout, yout, status ) + + if( abs( yout(1)-2.82175422 ) .gt. 1.0E-6 ) + : call stopit( status, 'error 1' ) + if( abs( xout(1)+0.0269096587 ) .gt. 1.0E-7 ) + : call stopit( status, 'error 2' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, 'error 3' ) + if( xout(2) .ne. AST__BAD ) call stopit( status, 'error 4' ) + + + if( .not. ast_getl( box3, 'Adaptive', status ) ) + : call stopit( status, 'error 4a' ) + + call ast_setl( box3, 'Adaptive', .false., status ) + call ast_setc( box3, 'system', 'icrs', status ) + + yin(1) = 2.82175432250852 + xin(1) = -0.0269096590283195 + yin(2) = 2.70798275154741 + xin(2) = 0.2467384819891 + + call ast_tran2( box3, 2, xin, yin, .true., xout, yout, status ) + + if( abs( yout(1)-2.82175422 ) .gt. 1.0E-8 ) + : call stopit( status, 'error 1' ) + if( abs( xout(1)+0.0269096587 ) .gt. 1.0E-8 ) + : call stopit( status, 'error 2' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, 'error 4b' ) + if( xout(2) .ne. AST__BAD ) call stopit( status, 'error 4c' ) + + call ast_clear( box3, 'system', status ) + + yin(1) = 2.82175432250852 + xin(1) = -0.0269096590283195 + yin(2) = 2.70798275154741 + xin(2) = 0.2467384819891 + + call ast_tran2( box3, 2, xin, yin, .true., xout, yout, status ) + + if( abs( yout(1)-2.82175422 ) .gt. 1.0E-8 ) + : call stopit( status, 'error 1' ) + if( abs( xout(1)+0.0269096587 ) .gt. 1.0E-8 ) + : call stopit( status, 'error 2' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, 'error 4d' ) + if( xout(2) .ne. AST__BAD ) call stopit( status, 'error 4e' ) + + box2 = ast_simplify( box1, status ) + + call ast_setc( box1, 'system', 'icrs', status ) + call ast_permaxes( box1, perm, status ) + + t1 = ast_format( frm1, 1, 0.25D0, status ) + call ast_annul( frm1, status ) + t2 = ast_format( box1, 1, 0.25D0, status ) + if( t1 .ne. t2 ) call stopit( status, + : 'ast_format is different for frm1 and box1' ) + + i = ast_unformat( box1, 1, t2, v2, status ) + if( abs( v2 - 0.25 ) .GT. 1.0E-10 ) then + call stopit( status, 'ast_unformat failed for box1' ) + end if + + if( ast_getc( box1, 'System', status ) .ne. 'ICRS' ) then + call stopit( status, 'Box1(b) system is not ICRS' ) + end if + + if( ast_getc( box1, 'Equinox', status ) .ne. '2000.0' ) then + call stopit( status, 'Box1 equinox is not 2000.0' ) + end if + + if( .not. ast_getl( box1, 'Closed', status ) ) then + call stopit( status, 'Box1 closed is not .true.' ) + end if + + xin( 1 ) = 1.25 + yin( 1 ) = 0.75 + xin( 2 ) = 1.0 + yin( 2 ) = 1.0 + xin( 3 ) = 1.0 + yin( 3 ) = 0.5 + xin( 4 ) = 1.5 + yin( 4 ) = 0.5 + xin( 5 ) = 1.5 + yin( 5 ) = 1.0 + xin( 6 ) = 1.0 + yin( 6 ) = 1.2 + xin( 7 ) = 0.8 + yin( 7 ) = 0.5 + xin( 8 ) = 1.5 + yin( 8 ) = 0.45 + xin( 9 ) = 1.501 + yin( 9 ) = 1.0 + + call ast_tran2( BOX1, 9, xin, yin, .true., xout, yout, status ) + + if( xout( 1 ) .ne. 1.25 ) call stopit( status, 'error A1' ) + if( yout( 1 ) .ne. 0.75 ) call stopit( status, 'error A2' ) + if( xout( 2 ) .ne. 1.0 ) call stopit( status, 'error A3' ) + if( yout( 2 ) .ne. 1.0 ) call stopit( status, 'error A4' ) + if( xout( 3 ) .ne. 1.0 ) call stopit( status, 'error A5' ) + if( yout( 3 ) .ne. 0.5 ) call stopit( status, 'error A6' ) + if( xout( 4 ) .ne. 1.5 ) call stopit( status, 'error A7' ) + if( yout( 4 ) .ne. 0.5 ) call stopit( status, 'error A8' ) + if( xout( 5 ) .ne. 1.5 ) call stopit( status, 'error A9' ) + if( yout( 5 ) .ne. 1.0 ) call stopit( status, 'error A10' ) + if( xout( 6 ) .ne. AST__BAD ) call stopit( status, 'error A11' ) + if( yout( 6 ) .ne. AST__BAD ) call stopit( status, 'error A12' ) + if( xout( 7 ) .ne. AST__BAD ) call stopit( status, 'error A13' ) + if( yout( 7 ) .ne. AST__BAD ) call stopit( status, 'error A14' ) + if( xout( 8 ) .ne. AST__BAD ) call stopit( status, 'error A15' ) + if( yout( 8 ) .ne. AST__BAD ) call stopit( status, 'error A16' ) + if( xout( 9 ) .ne. AST__BAD ) call stopit( status, 'error A17' ) + if( yout( 9 ) .ne. AST__BAD ) call stopit( status, 'error A18' ) + + call ast_tran2( box1, 9, xin, yin, .false., xout, yout, status ) + + if( xout( 1 ) .ne. 1.25 ) call stopit( status, 'error B1' ) + if( yout( 1 ) .ne. 0.75 ) call stopit( status, 'error B2' ) + if( xout( 2 ) .ne. 1.0 ) call stopit( status, 'error B3' ) + if( yout( 2 ) .ne. 1.0 ) call stopit( status, 'error B4' ) + if( xout( 3 ) .ne. 1.0 ) call stopit( status, 'error B5' ) + if( yout( 3 ) .ne. 0.5 ) call stopit( status, 'error B6' ) + if( xout( 4 ) .ne. 1.5 ) call stopit( status, 'error B7' ) + if( yout( 4 ) .ne. 0.5 ) call stopit( status, 'error B8' ) + if( xout( 5 ) .ne. 1.5 ) call stopit( status, 'error B9' ) + if( yout( 5 ) .ne. 1.0 ) call stopit( status, 'error B10' ) + if( xout( 6 ) .ne. AST__BAD ) call stopit( status, 'error B11' ) + if( yout( 6 ) .ne. AST__BAD ) call stopit( status, 'error B12' ) + if( xout( 7 ) .ne. AST__BAD ) call stopit( status, 'error B13' ) + if( yout( 7 ) .ne. AST__BAD ) call stopit( status, 'error B14' ) + if( xout( 8 ) .ne. AST__BAD ) call stopit( status, 'error B15' ) + if( yout( 8 ) .ne. AST__BAD ) call stopit( status, 'error B16' ) + if( xout( 9 ) .ne. AST__BAD ) call stopit( status, 'error B17' ) + if( yout( 9 ) .ne. AST__BAD ) call stopit( status, 'error B18' ) + + call ast_negate( box1, status ) + call ast_tran2( box1, 9, xin, yin, .true., xout, yout, status ) + + if( xout( 1 ) .ne. AST__BAD ) call stopit( status, 'error C1' ) + if( yout( 1 ) .ne. AST__BAD ) call stopit( status, 'error C2' ) + if( xout( 2 ) .ne. 1.0 ) call stopit( status, 'error C3' ) + if( yout( 2 ) .ne. 1.0 ) call stopit( status, 'error C4' ) + if( xout( 3 ) .ne. 1.0 ) call stopit( status, 'error C5' ) + if( yout( 3 ) .ne. 0.5 ) call stopit( status, 'error C6' ) + if( xout( 4 ) .ne. 1.5 ) call stopit( status, 'error C7' ) + if( yout( 4 ) .ne. 0.5 ) call stopit( status, 'error C8' ) + if( xout( 5 ) .ne. 1.5 ) call stopit( status, 'error C9' ) + if( yout( 5 ) .ne. 1.0 ) call stopit( status, 'error C10' ) + if( xout( 6 ) .ne. 1.0 ) call stopit( status, 'error C11' ) + if( yout( 6 ) .ne. 1.2 ) call stopit( status, 'error C12' ) + if( xout( 7 ) .ne. 0.8 ) call stopit( status, 'error C13' ) + if( yout( 7 ) .ne. 0.5 ) call stopit( status, 'error C14' ) + if( xout( 8 ) .ne. 1.5 ) call stopit( status, 'error C15' ) + if( yout( 8 ) .ne. 0.45 ) call stopit( status, 'error C16' ) + if( xout( 9 ) .ne. 1.501 ) call stopit( status, 'error C17' ) + if( yout( 9 ) .ne. 1.0 ) call stopit( status, 'error C18' ) + + call ast_setl( box1, 'closed', .false., status ) + call ast_negate( box1, status ) + call ast_tran2( box1, 9, xin, yin, .true., xout, yout, status ) + + if( xout( 1 ) .ne. 1.25 ) call stopit( status, 'error D1' ) + if( yout( 1 ) .ne. 0.75 ) call stopit( status, 'error D2' ) + if( xout( 2 ) .ne. AST__BAD ) call stopit( status, 'error D3' ) + if( yout( 2 ) .ne. AST__BAD ) call stopit( status, 'error D4' ) + if( xout( 3 ) .ne. AST__BAD ) call stopit( status, 'error D5' ) + if( yout( 3 ) .ne. AST__BAD ) call stopit( status, 'error D6' ) + if( xout( 4 ) .ne. AST__BAD ) call stopit( status, 'error D7' ) + if( yout( 4 ) .ne. AST__BAD ) call stopit( status, 'error D8' ) + if( xout( 5 ) .ne. AST__BAD ) call stopit( status, 'error D9' ) + if( yout( 5 ) .ne. AST__BAD ) call stopit( status, 'error D10' ) + if( xout( 6 ) .ne. AST__BAD ) call stopit( status, 'error D11' ) + if( yout( 6 ) .ne. AST__BAD ) call stopit( status, 'error D12' ) + if( xout( 7 ) .ne. AST__BAD ) call stopit( status, 'error D13' ) + if( yout( 7 ) .ne. AST__BAD ) call stopit( status, 'error D14' ) + if( xout( 8 ) .ne. AST__BAD ) call stopit( status, 'error D15' ) + if( yout( 8 ) .ne. AST__BAD ) call stopit( status, 'error D16' ) + if( xout( 9 ) .ne. AST__BAD ) call stopit( status, 'error D17' ) + if( yout( 9 ) .ne. AST__BAD ) call stopit( status, 'error D18' ) + + call ast_setl( box1, 'Negated', .true., status ) + call ast_tran2( box1, 9, xin, yin, .true., xout, yout, status ) + + if( xout( 1 ) .ne. AST__BAD ) call stopit( status, 'error E1' ) + if( yout( 1 ) .ne. AST__BAD ) call stopit( status, 'error E2' ) + if( xout( 2 ) .ne. AST__BAD ) call stopit( status, 'error E3' ) + if( yout( 2 ) .ne. AST__BAD ) call stopit( status, 'error E4' ) + if( xout( 3 ) .ne. AST__BAD ) call stopit( status, 'error E5' ) + if( yout( 3 ) .ne. AST__BAD ) call stopit( status, 'error E6' ) + if( xout( 4 ) .ne. AST__BAD ) call stopit( status, 'error E7' ) + if( yout( 4 ) .ne. AST__BAD ) call stopit( status, 'error E8' ) + if( xout( 5 ) .ne. AST__BAD ) call stopit( status, 'error E9' ) + if( yout( 5 ) .ne. AST__BAD ) call stopit( status, 'error E10' ) + if( xout( 6 ) .ne. 1.0 ) call stopit( status, 'error E11' ) + if( yout( 6 ) .ne. 1.2 ) call stopit( status, 'error E12' ) + if( xout( 7 ) .ne. 0.8 ) call stopit( status, 'error E13' ) + if( yout( 7 ) .ne. 0.5 ) call stopit( status, 'error E14' ) + if( xout( 8 ) .ne. 1.5 ) call stopit( status, 'error E15' ) + if( yout( 8 ) .ne. 0.45 ) call stopit( status, 'error E16' ) + if( xout( 9 ) .ne. 1.501 ) call stopit( status, 'error E17' ) + if( yout( 9 ) .ne. 1.0 ) call stopit( status, 'error E18' ) + + call ast_clear( box1, 'Negated', status ) + call ast_clear( box1, 'Closed', status ) + + call ast_addframe( fs, ast__current, ast_unitmap(2,' ',status), + : box1, status ) + + + + map1 = ast_getmapping( fs, ast__current, ast__current, status ) + + if( .not.ast_isaregion( map1, status ) ) + : call stopit( status, 'map1 is not a Region' ) + + call ast_setl( fs, 'Negated', .true., status ) + if( ast_getl( box1, 'Negated', status ) ) + : call stopit( status, + : 'FrameSet Negated attribute reflected in box1' ) + call ast_clear( fs, 'Negated', status ) + + map1 = ast_getmapping( fs, ast__base, ast__current, status ) + + call ast_tran2( map1, 9, xin, yin, .false., xout, yout, status ) + + if( xout( 1 ) .eq. AST__BAD ) call stopit( status, 'error F1' ) + if( yout( 1 ) .eq. AST__BAD ) call stopit( status, 'error F2' ) + if( xout( 2 ) .eq. AST__BAD ) call stopit( status, 'error F3' ) + if( yout( 2 ) .eq. AST__BAD ) call stopit( status, 'error F4' ) + if( xout( 3 ) .eq. AST__BAD ) call stopit( status, 'error F5' ) + if( yout( 3 ) .eq. AST__BAD ) call stopit( status, 'error F6' ) + if( xout( 4 ) .eq. AST__BAD ) call stopit( status, 'error F7' ) + if( yout( 4 ) .eq. AST__BAD ) call stopit( status, 'error F8' ) + if( xout( 5 ) .eq. AST__BAD ) call stopit( status, 'error F9' ) + if( yout( 5 ) .eq. AST__BAD ) call stopit( status, 'error F10' ) + if( xout( 6 ) .ne. AST__BAD ) call stopit( status, 'error F11' ) + if( yout( 6 ) .ne. AST__BAD ) call stopit( status, 'error F12' ) + if( xout( 7 ) .ne. AST__BAD ) call stopit( status, 'error F13' ) + if( yout( 7 ) .ne. AST__BAD ) call stopit( status, 'error F14' ) + if( xout( 8 ) .ne. AST__BAD ) call stopit( status, 'error F15' ) + if( yout( 8 ) .ne. AST__BAD ) call stopit( status, 'error F16' ) + if( xout( 9 ) .ne. AST__BAD ) call stopit( status, 'error F17' ) + if( yout( 9 ) .ne. AST__BAD ) call stopit( status, 'error F18' ) + + call ast_tran2( map1, 9, xout, yout, .true., xout, yout, status ) + + if( abs( xout( 1 ) - 1.25 ) .gt. 1.0D-7 ) call stopit( status, + : 'error G1' ) + if( abs( yout( 1 ) - 0.75 ) .gt. 1.0D-7 ) call stopit( status, + : 'error G2' ) + if( xout( 6 ) .ne. AST__BAD ) call stopit( status, 'error G11' ) + if( yout( 6 ) .ne. AST__BAD ) call stopit( status, 'error G12' ) + if( xout( 7 ) .ne. AST__BAD ) call stopit( status, 'error G13' ) + if( yout( 7 ) .ne. AST__BAD ) call stopit( status, 'error G14' ) + if( xout( 8 ) .ne. AST__BAD ) call stopit( status, 'error G15' ) + if( yout( 8 ) .ne. AST__BAD ) call stopit( status, 'error G16' ) + if( xout( 9 ) .ne. AST__BAD ) call stopit( status, 'error G17' ) + if( yout( 9 ) .ne. AST__BAD ) call stopit( status, 'error G18' ) + + + + + frm2 = ast_specframe( 'Unit=Angstrom', status ) + p1( 1 ) = 1000.0 + p2( 1 ) = 1100.0 + box2 = ast_box( frm2, 0, p1, p2, AST__NULL, ' ', status ) + frm3 = ast_cmpframe( box1, box2, ' ', status ) + + perm(1)=2 + perm(2)=3 + perm(3)=1 + call ast_permaxes( frm3, perm, status ) + call ast_setc( frm3, 'system(1)', 'galactic', status ) + call ast_setc( frm3, 'system(2)', 'Freq', status ) + + in( 1, 1 ) = -0.0269096590283195 ! In both boxes + in( 1, 2 ) = 2997924.58 + in( 1, 3 ) = 2.82175432250852 + in( 2, 1 ) = 0.2467384819891 ! In spec box, out sky box + in( 2, 2 ) = 2997924.58 + in( 2, 3 ) = 2.70798275154741 + in( 3, 1 ) = -0.0269096590283195 ! Out spec box in sky box + in( 3, 2 ) = 4000000.0 + in( 3, 3 ) = 2.82175432250852 + in( 4, 1 ) = 0.2467384819891 ! Out spec box, out sky box + in( 4, 2 ) = 4000000.0 + in( 4, 3 ) = 2.70798275154741 + call ast_trann( frm3, 4, 3, 4, in, .true., 3, 4, out, status ) + + if( abs( out(1,1)+0.0269096587 ) .gt. 1.0E-8 ) + : call stopit( status, 'error H1' ) + if( abs( out(1,2)-2997924.5 ) .gt. 1.0E-1 ) + : call stopit( status, 'error H2' ) + if( abs( out(1,3)-2.82175422 ) .gt. 1.0E-6 ) + : call stopit( status, 'error H3' ) + + if( out(2,1) .ne. ast__bad ) call stopit( status, 'error H4' ) + if( abs( out(2,2)-2997924.5 ) .gt. 1.0E-1 ) + : call stopit( status, 'error H5' ) + if( out(2,3) .ne. ast__bad ) call stopit( status, 'error H6' ) + + if( abs( out(3,1)+0.0269096587 ) .gt. 1.0E-8 ) + : call stopit( status, 'error H7' ) + if( out(3,2) .ne. ast__bad ) call stopit( status, 'error H8' ) + if( abs( out(3,3)-2.82175422 ) .gt. 1.0E-6 ) + : call stopit( status, 'error H9' ) + + if( out(4,1) .ne. ast__bad ) call stopit( status, 'error H10' ) + if( out(4,2) .ne. ast__bad ) call stopit( status, 'error H11' ) + if( out(4,3) .ne. ast__bad ) call stopit( status, 'error H12' ) + + if( .not. ast_getl( frm3, 'closed(1)', status ) ) + : call stopit( status, 'compound frame region is not closed' ) + + + + +C +C Testing astMapRegion +C + + frm1 = ast_frame( 3, 'Domain=A', status ) + p1(1) = 100 + p1(2) = 200 + p1(3) = 300 + p2(1) = 0 + p2(2) = 400 + p2(3) = 250 + box1 = ast_box( frm1, 0, p1, p2, AST__NULL, ' ', status ) + + frm2 = ast_frame( 3, 'Domain=B', status ) + + matrix(1) = 2.0 + matrix(2) = 0.0 + matrix(3) = 0.0 + matrix(4) = 0.0 + matrix(5) = 4.0 + matrix(6) = 0.0 + matrix(7) = 0.0 + matrix(8) = 0.0 + matrix(9) = 6.0 + + map2 = ast_matrixmap( 3, 3, 0, matrix, ' ', status ) + box2 = ast_mapregion( box1, map2, frm2, status ) + + if( ast_getc( box2, 'Domain', status ) .ne. 'B' ) then + call stopit( status, 'ast_mapregion1: Box2 domain is not B' ) + end if + + if( hasframeset( box2, status ) ) then + call stopit( status, 'ast_mapregion2: Box2 has FrameSet' ) + end if + + matrix(1) = 2.0 + matrix(2) = .1 + matrix(3) = 0.0 + matrix(4) = 0.0 + matrix(5) = 4.0 + matrix(6) = 0.0 + matrix(7) = 0.0 + matrix(8) = 0.0 + matrix(9) = 6.0 + + map2 = ast_matrixmap( 3, 3, 0, matrix, ' ', status ) + + box2 = ast_mapregion( box1, map2, frm2, status ) + + if( ast_getc( box2, 'Domain', status ) .ne. 'B' ) then + call stopit( status, 'ast_mapregion3: Box2 domain is not B' ) + end if + + if( hasframeset( box2, status ) ) then + call stopit( status, 'ast_mapregion4: Box2 has FrameSet' ) + end if + + call checkdump( box2, 'checkdump box2', status ) + + frm1 = ast_frame( 1, 'Domain=A', status ) + p1(1) = 100 + p2(1) = 0 + box1 = ast_box( frm1, 0, p1, p2, AST__NULL, ' ', status ) + + frm2 = ast_frame( 1, 'Domain=B', status ) + + map2 = ast_zoommap( 1, 2.0D0, ' ', status ) + box2 = ast_mapregion( box1, map2, frm2, status ) + + if( ast_getc( box2, 'Domain', status ) .ne. 'B' ) then + call stopit( status, 'ast_mapregion5: Box2 domain is not B' ) + end if + + if( hasframeset( box2, status ) ) then + call stopit( status, 'ast_mapregion6: Box2 has FrameSet (B)' ) + end if + + frm1 = ast_skyframe( ' ', status ) + p1(1) = 0 + p1(2) = 0 + p2(1) = 0.001 + p2(2) = 0.001 + box1 = ast_box( frm1, 0, p1, p2, AST__NULL, ' ', status ) + + frm2 = ast_copy( frm1, status ) + call ast_setd( frm2, 'skyref(1)', 0.0005D0, status ) + call ast_setc( frm2, 'skyrefis', 'origin', status ) + + fs = ast_convert( frm1, frm2, ' ', status ) + + box2 = ast_mapregion( box1, fs, frm2, status ) + + if( hasframeset( box2, status ) ) then + call stopit( status, 'ast_mapregion7: Box2 has FrameSet (C)' ) + end if + + xin( 1 ) = 0.00049 + yin( 1 ) = 0.0009 + xin( 2 ) = 0.00051 + yin( 2 ) = 0.0009 + xin( 3 ) = -0.0016 + yin( 3 ) = 0.0 + xin( 4 ) = -0.0014 + yin( 4 ) = 0.0 + xin( 5 ) = 6.2815853 + yin( 5 ) = 0.0 + xin( 6 ) = 6.2817853 + yin( 6 ) = 0.0 + + call ast_tran2( box2, 6, xin, yin, .true., xout, yout, status ) + + if( abs( xout( 1 ) - xin( 1 ) ) .gt. 1D-10 ) call stopit( status, + : 'error I1' ) + if( abs( yout( 1 ) - yin( 1 ) ) .gt. 1D-10 ) call stopit( status, + : 'error I2' ) + + if( xout(2) .ne. AST__BAD ) call stopit( status, 'error I3' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, 'error I4' ) + if( xout(5) .ne. AST__BAD ) call stopit( status, 'error I5' ) + if( yout(5) .ne. AST__BAD ) call stopit( status, 'error I6' ) + if( xout(3) .ne. AST__BAD ) call stopit( status, 'error I7' ) + if( yout(3) .ne. AST__BAD ) call stopit( status, 'error I8' ) + + if( abs( xout( 4 ) - xin( 4 ) ) .gt. 1D-10 ) call stopit( status, + : 'error I9' ) + if( abs( yout( 4 ) - yin( 4 ) ) .gt. 1D-10 ) call stopit( status, + : 'error I10' ) + if( abs( xout( 6 ) - xin( 6 ) ) .gt. 1D-10 ) call stopit( status, + : 'error I11' ) + if( abs( yout( 6 ) - yin( 6 ) ) .gt. 1D-10 ) call stopit( status, + : 'error I12' ) + + + call ast_setc( box2, 'skyrefis', 'pole', status ) + box2 = ast_Simplify( box2, status ) + + if( hasframeset( box2, status ) ) then + call stopit( status, 'ast_mapregion8: Box2 has '// + : 'FrameSet (B)' ) + end if + +C +C Testing astOverlap +C + + frm1 = ast_frame( 3, 'Domain=A', status ) + p1(1) = 100 + p1(2) = 200 + p1(3) = 300 + p2(1) = 0 + p2(2) = 400 + p2(3) = 250 + box1 = ast_box( frm1, 0, p1, p2, AST__NULL, 'closed=1', status ) + + frm2 = ast_frame( 3, 'Domain=B', status ) + box2 = ast_box( frm2, 0, p1, p2, AST__NULL, 'closed=0', status ) + + if( ast_overlap( box1, box2, status ) .ne. 0 ) then + call stopit( status, 'ast_overlap A: result should be zero' ) + end if + + if( ast_overlap( box1, box1, status ) .ne. 5 ) then + call stopit( status, 'ast_overlap B: result should be 5' ) + end if + + if( ast_overlap( box2, box2, status ) .ne. 5 ) then + call stopit( status, 'ast_overlap C: result should be 5' ) + end if + + call ast_setc( frm2, 'Domain', 'A', status ) + p1(1) = 100 + p1(2) = 200 + p1(3) = 300 + p2(1) = -100 + p2(2) = 600 + p2(3) = 400 + box2 = ast_box( frm2, 0, p1, p2, AST__NULL, ' ', status ) + + if( ast_overlap( box1, box2, status ) .ne. 2 ) then + write(*,*) 'Result is ',ast_overlap( box1, box2, status ) + call stopit( status, 'ast_overlap D: result should be 2' ) + end if + + if( ast_overlap( box2, box1, status ) .ne. 3 ) then + write(*,*) 'Result is ',ast_overlap( box2, box1, status ) + call stopit( status, 'ast_overlap E: result should be 3' ) + end if + + p1(1) = 300 + p1(2) = 200 + p1(3) = 300 + p2(1) = 201 + p2(2) = 400 + p2(3) = 250 + box2 = ast_box( frm2, 0, p1, p2, AST__NULL, ' ', status ) + + if( ast_overlap( box1, box2, status ) .ne. 1 ) then + call stopit( status, 'ast_overlap F: result should be 1' ) + end if + + if( ast_overlap( box2, box1, status ) .ne. 1 ) then + call stopit( status, 'ast_overlap G: result should be 1' ) + end if + + p1(1) = 150 + p1(2) = 200 + p1(3) = 300 + p2(1) = 50 + p2(2) = 400 + p2(3) = 250 + box2 = ast_box( frm2, 0, p1, p2, AST__NULL, ' ', status ) + + if( ast_overlap( box1, box2, status ) .ne. 4 ) then + call stopit( status, 'ast_overlap H: result should be 4' ) + end if + + if( ast_overlap( box2, box1, status ) .ne. 4 ) then + call stopit( status, 'ast_overlap I: result should be 4' ) + end if + + +* Pixel masks + frm1 = ast_frame( 2, 'Domain=A', status ) + p1(1) = 1.0 + p1(2) = 1.0 + p2(1) = 3.1 + p2(2) = 4.1 + box1 = ast_box( frm1, 0, p1, p2, AST__NULL, ' ', status ) + + lbnd_in(1) = 1 + lbnd_in(2) = 1 + ubnd_in(1) = 5 + ubnd_in(2) = 5 + + do i =1, 5 + do j = 1, 5 + rin( j,i)=1.0 + end do + end do + + res = ast_maskr( box1, AST__NULL, .false., 2, lbnd_in, ubnd_in, + : rin, VAL__BADR, status ) + + if( res .ne. 13 ) then + write(*,*) 'Res is ',res + call stopit( status, 'res should be 13' ) + end if + + do i =1, 5 + do j = 1, 5 + if( j .le. 3 .and. i .le. 4 ) then + if( rin(j,i) .NE. 1.0 ) then + write(*,*) 'rin(',j,',',i,') = ',rin(j,i) + call stopit( status, 'Above value should be 1.0' ) + end if + else + if( rin(j,i) .NE. VAL__BADR ) then + write(*,*) 'rin(',j,',',i,') = ',rin(j,i) + call stopit( status, 'Above value should be '// + : 'VAL__BADR' ) + end if + endif + end do + end do + + cards(3) = 'CRPIX1 = 20' + cards(4) = 'CRPIX2 = 20' + cards(5) = 'CRVAL1 = 0.0' + cards(6) = 'CRVAL2 = 0.0' + cards(7) = ' ' + cards(8) = 'CDELT1 = 1.6' + cards(9) = 'CDELT2 = 1.6' + + fc = ast_fitschan( ast_null, ast_null, ' ', status ) + do i = 1, 9 + call ast_putfits( fc, cards(i), .false., status ) + end do + call ast_clear( fc, 'card', status ) + fs = ast_read( fc, status ) + + p1( 1 ) = 0.13089969 ! RA at centre = 0h30m + p1( 2 ) = 0.17453293 ! Dec at centre = 10d + p2( 1 ) = -0.13089971 ! RA at corner = 23h30m + p2( 2 ) = -0.17453293 ! Dec at corner = -10d + + box1 = ast_box( fs, 0, p1, p2, AST__NULL, ' ', status ) + + do i =1, 50 + do j = 1, 50 + image( j,i)=1.0 + end do + end do + + lbnd_in(1) = 1 + lbnd_in(2) = 1 + ubnd_in(1) = 50 + ubnd_in(2) = 50 + + call ast_negate( box1, status ) + call ast_invert( fs, status ) + res = ast_maskr( box1, fs, .false., 2, lbnd_in, ubnd_in, + : image, VAL__BADR, status ) + + if( res .ne. 522 ) then + write(*,*) 'Res is ',res + call stopit( status, 'res should be 522' ) + end if + + if( image(34,42) .ne. VAL__BADR ) then + write(*,*) 'image(34,42) = ',image(34,42) + call stopit( status, 'Above value should be VAL__BADR' ) + end if + + if( image(33,42) .ne. 1.0 ) then + write(*,*) 'image(33,42) = ',image(33,42) + call stopit( status, 'Above value should be 1.0' ) + end if + + if( image(16,14) .ne. VAL__BADR ) then + write(*,*) 'image(16,14) = ',image(16,14) + call stopit( status, 'Above value should be VAL__BADR' ) + end if + + if( image(15,13) .ne. 1.0 ) then + write(*,*) 'image(15,13) = ',image(15,13) + call stopit( status, 'Above value should be 1.0' ) + end if + + +* Changing the number of axes in the Region + + frm1 = ast_frame( 2, 'Domain=A', status ) + + p1(1) = 0.0 + p1(2) = 0.0 + p2(1) = 0.01 + unc = ast_circle( frm1, 1, p1, p2, AST__NULL, ' ', status ) + + p1(1) = -1.0 + p1(2) = 1.0 + p2(1) = -2.0 + p2(2) = 1.5 + box1 = ast_box( frm1, 0, p1, p2, unc, ' ', status ) + + + outperm(1) = 2 + outperm(2) = -1 + outperm(3) = 1 + + inperm(1) = 3 + inperm(2) = 1 + + pm = ast_permmap( 2, inperm, 3, outperm, 0.0D0, ' ', status ) + + frm2 = ast_frame( 3, 'Domain=B', status ) + reg = ast_mapregion( box1, pm, frm2, status ) + + if( .not. ast_isabox( reg, status ) ) call stopit( status, + : 'Box: perm check 1' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Box: perm check 2' ) + if( ast_geti( reg, 'naxes', status ) .ne. 3 ) call stopit( status, + : 'Box: perm check 3' ) + + in( 1, 1 ) = 0.0 ! Outside + in( 1, 2 ) = 0.0 + in( 1, 3 ) = 0.0 + in( 2, 1 ) = 0.7 ! Inside + in( 2, 2 ) = 0.0 + in( 2, 3 ) = -0.5 + in( 3, 1 ) = 2.0 ! Outside + in( 3, 2 ) = 0.0 + in( 3, 3 ) = -1.0 + in( 4, 1 ) = 1.5 ! Boundary + in( 4, 2 ) = 0.0 + in( 4, 3 ) = 0.0 + + call ast_trann( reg, 4, 3, 4, in, .true., 3, 4, out, status ) + + if( out( 1, 1 ) .ne. AST__BAD ) call stopit( status, 'box: pc 1' ) + if( out( 1, 2 ) .ne. AST__BAD ) call stopit( status, 'box: pc 2' ) + if( out( 1, 3 ) .ne. AST__BAD ) call stopit( status, 'box: pc 3' ) + + if( out( 2, 1 ) .ne. in( 2,1 )) call stopit( status, 'box: pc 4' ) + if( out( 2, 2 ) .ne. in( 2,2 )) call stopit( status, 'box: pc 5' ) + if( out( 2, 3 ) .ne. in( 2,3 )) call stopit( status, 'box: pc 6' ) + + if( out( 3, 1 ) .ne. AST__BAD ) call stopit( status, 'box: pc 7' ) + if( out( 3, 2 ) .ne. AST__BAD ) call stopit( status, 'box: pc 8' ) + if( out( 3, 3 ) .ne. AST__BAD ) call stopit( status, 'box: pc 9' ) + + if( out( 4, 1 ) .ne. in( 4,1 )) call stopit( status, 'box: pc 10') + if( out( 4, 2 ) .ne. in( 4,2 )) call stopit( status, 'box: pc 11') + if( out( 4, 3 ) .ne. in( 4,3 )) call stopit( status, 'box: pc 12') + + + outperm(1) = 2 + outperm(2) = -1 + outperm(3) = 1 + + inperm(1) = 3 + inperm(2) = 1 + + pm = ast_permmap( 2, inperm, 3, outperm, 1.0D0, ' ', status ) + + frm2 = ast_frame( 3, 'Domain=B', status ) + reg = ast_mapregion( box1, pm, frm2, status ) + + if( .not. ast_isabox( reg, status ) ) call stopit( status, + : 'Box: perm check 4' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Box: perm check 5' ) + if( ast_geti( reg, 'naxes', status ) .ne. 3 ) call stopit( status, + : 'Box: perm check 6' ) + + in( 1, 1 ) = 0.0 ! Outside + in( 1, 2 ) = 0.0 + in( 1, 3 ) = 0.0 + in( 2, 1 ) = 0.7 ! boundary + in( 2, 2 ) = 1.0 + in( 2, 3 ) = -0.5 + in( 3, 1 ) = 0.7 ! outside + in( 3, 2 ) = 1.1 + in( 3, 3 ) = -0.5 + in( 4, 1 ) = 0.7 ! outside + in( 4, 2 ) = 0.9 + in( 4, 3 ) = -0.5 + + call ast_trann( reg, 4, 3, 4, in, .true., 3, 4, out, status ) + + if( out( 1, 1 ) .ne. AST__BAD ) call stopit( status, 'box: pc 11') + if( out( 1, 2 ) .ne. AST__BAD ) call stopit( status, 'box: pc 12') + if( out( 1, 3 ) .ne. AST__BAD ) call stopit( status, 'box: pc 13') + + if( out( 2, 1 ) .ne. in( 2,1 )) call stopit( status, 'box: pc 14') + if( out( 2, 2 ) .ne. in( 2,2 )) call stopit( status, 'box: pc 15') + if( out( 2, 3 ) .ne. in( 2,3 )) call stopit( status, 'box: pc 16') + + if( out( 3, 1 ) .ne. AST__BAD ) call stopit( status, 'box: pc 17') + if( out( 3, 2 ) .ne. AST__BAD ) call stopit( status, 'box: pc 18') + if( out( 3, 3 ) .ne. AST__BAD ) call stopit( status, 'box: pc 19') + + if( out( 4, 1 ) .ne. AST__BAD ) call stopit( status, 'box: pc 17') + if( out( 4, 2 ) .ne. AST__BAD ) call stopit( status, 'box: pc 18') + if( out( 4, 3 ) .ne. AST__BAD ) call stopit( status, 'box: pc 19') + + + + outperm(1) = 1 + + inperm(1) = 1 + inperm(2) = -1 + + pm = ast_permmap( 2, inperm, 1, outperm, 1.4D0, ' ', status ) + + frm2 = ast_frame( 1, 'Domain=B', status ) + reg = ast_mapregion( box1, pm, frm2, status ) + + if( .not. ast_isabox( reg, status ) ) call stopit( status, + : 'Box: perm check 7' ) + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Box: perm check 8' ) + if( ast_geti( reg, 'naxes', status ) .ne. 1 ) call stopit( status, + : 'Box: perm check 9' ) + + xin( 1 ) = -2.5 ! Outside + xin( 2 ) = -1.9 ! Inside + xin( 3 ) = 0.0 ! boundary + xin( 4 ) = 0.5 ! outside + + call ast_tran1( reg, 4, xin, .true., xout, status ) + + if( xout( 1 ) .ne. AST__BAD ) call stopit( status, 'box: pc 20') + if( xout( 2 ) .ne. xin(2) ) call stopit( status, 'box: pc 21') + if( xout( 3 ) .ne. xin(3) ) call stopit( status, 'box: pc 22') + if( xout( 4 ) .ne. AST__BAD ) call stopit( status, 'box: pc 23') + + + + outperm(1) = 1 + + inperm(1) = 1 + inperm(2) = -1 + + pm = ast_permmap( 2, inperm, 1, outperm, 1.6D0, ' ', status ) + frm2 = ast_frame( 1, 'Domain=B', status ) + reg = ast_mapregion( box1, pm, frm2, status ) + if( .not. ast_isanullregion( reg, status ) ) call stopit( status, + : 'Box: perm check 10' ) + + + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Box: perm check 11' ) + if( ast_geti( reg, 'naxes', status ) .ne. 1 ) call stopit( status, + : 'Box: perm check 12' ) + + xin( 1 ) = -2.5 + xin( 2 ) = -1.9 + xin( 3 ) = 0.0 + xin( 4 ) = 0.5 + + call ast_tran1( reg, 4, xin, .true., xout, status ) + + if( xout( 1 ) .ne. AST__BAD ) call stopit( status, 'box: pc 24') + if( xout( 2 ) .ne. AST__BAD ) call stopit( status, 'box: pc 25') + if( xout( 3 ) .ne. AST__BAD ) call stopit( status, 'box: pc 26') + if( xout( 4 ) .ne. AST__BAD ) call stopit( status, 'box: pc 27') + + + frm1 = ast_frame( 3, 'Domain=A', status ) + + p1(1) = 0.5 + p1(2) = -1.0 + p1(3) = -2.0 + p2(1) = 30.0 + p2(2) = 5.0 + p2(3) = 0.0 + + box1 = ast_box( frm1, 1, p1, p2, AST__NULL, ' ', status ) + + outperm(1) = 1 + outperm(2) = 3 + + inperm(1) = 1 + inperm(2) = -1 + inperm(3) = 2 + + pm = ast_permmap( 3, inperm, 2, outperm, 1.0D0, ' ', status ) + + frm2 = ast_frame( 2, 'Domain=B', status ) + reg = ast_mapregion( box1, pm, frm2, status ) + + if( .not. ast_isabox( reg, status ) ) call stopit( status, + : 'Box: perm check 13' ) + + if( hasFrameSet( reg, status ) ) call stopit( status, + : 'Box: perm check 14' ) + if( ast_geti( reg, 'naxes', status ) .ne. 2 ) call stopit( status, + : 'Box: perm check 15' ) + + xin( 1 ) = 0.4 ! Out + yin( 1 ) = -1.0 + xin( 2 ) = 1.0 ! Out + yin( 2 ) = 0.1 + xin( 3 ) = 1.0 ! Out + yin( 3 ) = -2.1 + xin( 4 ) = 0.5 ! Boundary + yin( 4 ) = -1.0 + xin( 5 ) = 10.0 ! In + yin( 5 ) = -0.1 + xin( 6 ) = 0.55 ! Boundary + yin( 6 ) = -2.0 + + + call ast_tran2( reg, 6, xin, yin, .true., xout, yout, status ) + + if( xout( 1 ) .ne. AST__BAD ) call stopit( status, 'Box: pc 37') + if( yout( 1 ) .ne. AST__BAD ) call stopit( status, 'Box: pc 38') + if( xout( 2 ) .ne. AST__BAD ) call stopit( status, 'Box: pc 39') + if( yout( 2 ) .ne. AST__BAD ) call stopit( status, 'Box: pc 40') + if( xout( 3 ) .ne. AST__BAD ) call stopit( status, 'Box: pc 41') + if( yout( 3 ) .ne. AST__BAD ) call stopit( status, 'Box: pc 42') + if( xout( 4 ) .ne. xin( 4 ) ) call stopit( status, 'Box: pc 43') + if( yout( 4 ) .ne. yin( 4 ) ) call stopit( status, 'Box: pc 44') + if( xout( 5 ) .ne. xin( 5 ) ) call stopit( status, 'Box: pc 45') + if( yout( 5 ) .ne. yin( 5 ) ) call stopit( status, 'Box: pc 46') + if( xout( 6 ) .ne. xin( 6 ) ) call stopit( status, 'Box: pc 47') + if( yout( 6 ) .ne. yin( 6 ) ) call stopit( status, 'Box: pc 48') + + cards(1) = 'CTYPE1 = ''RA---TAN''' + cards(2) = 'CTYPE2 = ''DEC--TAN''' + cards(3) = 'CRPIX1 = 20' + cards(4) = 'CRPIX2 = 20' + cards(5) = 'CRVAL1 = 0.0' + cards(6) = 'CRVAL2 = 0.0' + cards(7) = 'CROTA1 = 30.0' + cards(8) = 'CDELT1 = -0.00001' + cards(9) = 'CDELT2 = 0.00001' + + fc = ast_fitschan( ast_null, ast_null, ' ', status ) + do i = 1, 9 + call ast_putfits( fc, cards(i), .false., status ) + end do + call ast_clear( fc, 'card', status ) + fs = ast_read( fc, status ) + + bfrm = ast_getFrame( fs, AST__BASE, status ) + + p1(1) = 0.0 + p1(2) = 0.0 + p2(1) = 0.1 + unc = ast_circle( bfrm, 1, p1, p2, AST__NULL, ' ', status ) + + p1( 1 ) = 100.0 ! Pix_X at centre + p1( 2 ) = 150.0 ! Pix_Y at centre + p2( 1 ) = 150.0 ! Pix_X at corner + p2( 2 ) = 170.0 ! Pix_Y at corner + + box1 = ast_box( bfrm, 0, p1, p2, AST__NULL, ' ', status ) + + + call ast_getregionmesh( box1, .false., 250, 2, npoint, grid, + : status ) + + if( npoint .ne. 176 ) then + write(*,*) npoint + call stopit( status, 'Box: Error mesh 3' ) + endif + + if( status .ne. SAI__OK ) go to 991 + + do i = 1, npoint + if( abs( grid(i,1) - 100 ) .gt. 50.0D0 ) then + call stopit( status, 'Box: Error mesh 1' ) + else if( abs( grid(i,2) - 150 ) .gt. 20.0D0 ) then + call stopit( status, 'Box: Error mesh 2' ) + endif + enddo + + call ast_getregionmesh( box1, .true., 250, 2, npoint, grid, + : status ) + if( npoint .ne. 198 ) + : call stopit( status, 'Box: Error mesh 4' ) + + if( status .ne. SAI__OK ) go to 991 + + do i = 1, npoint + if( grid(i,1) .ne. 50.0D0 .and. grid(i,1) .ne. 150.0D0 .and. + : grid(i,2) .ne. 130.0D0 .and. grid(i,2) .ne. 170.0D0 ) then + call stopit( status, 'Box: Error mesh 5' ) + endif + enddo + + cfrm = ast_getFrame( fs, AST__CURRENT, status ) + map = ast_getmapping( fs, AST__BASE, AST__CURRENT, status ) + reg1 = ast_mapregion( box1, map, cfrm, status ) + + if( hasFrameSet( reg1, status ) ) call stopit( status, + : 'Box: poly simp 1' ) + if( .not. ast_isapolygon( reg1, status) ) call stopit( status, + : 'Box: poly simp 2' ) + + 991 continue + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'Box tests failed' + + end + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + + + logical function hasframeset( reg, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer reg, status, ch,nw + + logical fsfound, done + common /sink1com/ fsfound, done + external sink1 + + hasframeset = .false. + if( status .ne. sai__ok ) return + + + fsfound = .false. + done = .false. + ch = ast_channel( AST_NULL, sink1, ' ', STATUS ) + nw = ast_write( ch, reg, status ) + call ast_annul( ch, status ) + + hasframeset = fsfound + + end + + subroutine sink1( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + logical fsfound, done + common /sink1com/ fsfound, done + + integer status, l + character line*200 + + if( status .ne. sai__ok ) return + call ast_getline( line, l, status ) + + if( index( line( : l ),'Unc =' ) .GT. 0 ) then + done = .true. + + else if( .not. done .and. + : index( line( : l ),'FrameSet' ) .GT. 0 ) then + fsfound= .true. + end if + + end + + + + subroutine checkPointList( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + + character fwd(1)*30,inv(1)*30 + integer status, frm, reg, reg2, reg3, reg4, mm, map + integer mdata(-1:15),lbnd,ubnd,nbad,unc + double precision pnts( 3 ), xin(3),xout(3), acc, ina, inb, outa, + : outb + data mdata /17*0/ + + if( status .ne.sai__ok ) return + + call ast_begin( status ) + frm = ast_specframe( ' ', status ) + + pnts(1)=0.0 + pnts(2)=1.0E-5 + unc = ast_box( frm, 0, pnts(1), pnts(2), AST__NULL, ' ', status ) + + pnts(1)=1.0 + pnts(2)=1.1 + reg = ast_pointlist( frm, 2, 1, 3, pnts, unc, ' ', status ) + call checkdump( reg, 'checkdump reg', status ) + + if( ast_overlap( reg, reg, status ) .ne. 5 ) then + call stopit( status, + : 'PointList: self is not identical with self' ) + end if + + reg2 = ast_copy( reg, status ) + call ast_negate( reg2, status ) + call checkdump( reg2, 'checkdump reg2', status ) + + if( ast_overlap( reg, reg2, status ) .ne. 6 ) then + call stopit( status, + : 'PointList: overlap with self-exclusion' ) + end if + + + xin( 1 ) = 1.0 + xin( 2 ) = 1.05 + xin( 3 ) = 1.1 + call ast_tran1( reg, 3, xin, .true., xout, status ) + + if( xout( 1 ) .ne. 1.0 ) then + call stopit( status, 'PointList: Error 1' ) + else if( xout( 2 ) .ne. AST__BAD ) then + call stopit( status, 'PointList: Error 2' ) + else if( xout( 3 ) .ne. 1.1 ) then + call stopit( status, 'PointList: Error 3' ) + end if + + + call ast_tran1( reg2, 3, xin, .true., xout, status ) + if( xout( 1 ) .ne. AST__BAD ) then + call stopit( status, 'PointList: Error 4' ) + else if( xout( 2 ) .ne. 1.05 ) then + call stopit( status, 'PointList: Error 5' ) + else if( xout( 3 ) .ne. AST__BAD ) then + call stopit( status, 'PointList: Error 6' ) + end if + + fwd(1) = 'y=x**2' + inv(1) = 'x=y**0.5' + mm = ast_mathmap( 1, 1, 1, fwd, 1, inv, ' ', status ) + reg3 = ast_mapregion( reg, mm, ast_frame(1,' ', status ), status ) + reg4 = ast_simplify( reg3, status ) + call checkdump( reg4, 'checkdump reg4', status ) + + xin( 1 ) = 1.21 + xin( 2 ) = 1.5 + call ast_tran1( reg4, 2, xin, .true., xout, status ) + if( xout( 1 ) .ne. 1.21 ) then + write(*,*) xout(1), ' (should be 1.21)' + call stopit( status, 'PointList: Error 7' ) + else if( xout( 2 ) .ne. AST__BAD ) then + write(*,*) xout(2), ' (should be bad)' + call stopit( status, 'PointList: Error 8' ) + end if + + + lbnd = -1 + ubnd = 15 + + ina = 1.01 + inb = 1.11 + outa = 2.0 + outb = 7.0 + map = ast_winmap( 1, ina, inb, outa, outb, ' ', status ) + + nbad = ast_maski( reg, map, .true., 1, lbnd, ubnd, mdata, 2, + : status ) + + if( nbad .ne. 2 ) then + write(*,*) 'nbad = ',nbad + call stopit( status, 'Above value should be 2' ) + end if + + if( mdata(1) .ne. 0 ) then + write(*,*) 'mdata(1) = ',mdata(1) + call stopit( status, 'Above value should be 0' ) + end if + + if( mdata(2) .ne. 2 ) then + write(*,*) 'mdata(2) = ',mdata(2) + call stopit( status, 'Above value should be 2' ) + end if + + if( mdata(3) .ne. 0 ) then + write(*,*) 'mdata(3) = ',mdata(3) + call stopit( status, 'Above value should be 0' ) + end if + + if( mdata(6) .ne. 0 ) then + write(*,*) 'mdata(6) = ',mdata(6) + call stopit( status, 'Above value should be 0' ) + end if + + if( mdata(7) .ne. 2 ) then + write(*,*) 'mdata(7) = ',mdata(7) + call stopit( status, 'Above value should be 2' ) + end if + + if( mdata(8) .ne. 0 ) then + write(*,*) 'mdata(8) = ',mdata(8) + call stopit( status, 'Above value should be 0' ) + end if + + + + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'PointList tests failed' + + end + + + + + + + + + subroutine checkCircle( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + + integer status, cir1, cir2, fc, i, fs, frm1,unc,f1,f2,f3, + : npoint, j + double precision p1(4),p2(4),xin(2),yin(2),xout(2),yout(2), + : p3(3),rad,zin(2),zout(2),pp1(3),pp2(3), + : lbnd(2),ubnd(2), mesh(250,3) + character cards(8)*80, sys*40 + logical hasframeset + + double precision in( 2, 3 ), out( 2, 3 ) + + + data cards /'CTYPE1 = ''RA---TAN''', + : 'CTYPE2 = ''DEC--TAN''', + : 'CRPIX1 = 100', + : 'CRPIX2 = 100', + : 'CRVAL1 = 70.0', + : 'CRVAL2 = 80.0', + : 'CDELT1 = 0.6', + : 'CDELT2 = 0.6' / + + + if( status .ne.sai__ok ) return + call ast_begin( status ) + +* Test 2D circles. + + fc = ast_fitschan( ast_null, ast_null, ' ', status ) + do i = 1, 8 + call ast_putfits( fc, cards(i), .false., status ) + end do + call ast_clear( fc, 'card', status ) + fs = ast_read( fc, status ) + + frm1 = ast_getframe( fs, ast__current, status ) + + p1( 1 ) = 0.0 + p1( 2 ) = 1.0 + p2( 1 ) = 0.01 + + cir1 = ast_circle( frm1, 1, p1, p2, AST__NULL, ' ', status ) + call ast_getregionbounds( cir1, lbnd, ubnd, status ) + + if( abs(lbnd(1)-(-0.01850666061475259)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA1' ) + if( abs(lbnd(2)-(0.9900000002235173)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA2' ) + if( abs(ubnd(1)-(0.01850666061475276)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA3' ) + if( abs(ubnd(2)-(1.009994987166073)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA4' ) + + p1( 1 ) = 0.0 + p1( 2 ) = 1.57 + p2( 1 ) = 0.01 + + cir1 = ast_circle( frm1, 1, p1, p2, AST__NULL, ' ', status ) + call ast_getregionbounds( cir1, lbnd, ubnd, status ) + + if( abs(lbnd(1)-(0.0)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA5' ) + if( abs(lbnd(2)-(1.560000052675599)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA6' ) + if( abs(ubnd(1)-(6.283185307179586)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA7' ) + if( abs(ubnd(2)-(1.5707963267948966)) .gt. 1.0E-6 ) + : call stopit( status, 'Circle: Error AA8' ) + + call ast_getregionmesh( cir1, .true., 0, 0, npoint, 0, status ) + if( npoint .ne. 200 ) + : call stopit( status, 'Circle: Error mesh 1' ) + + call ast_getregionmesh( cir1, .true., 250, 3, npoint, mesh, + : status ) + + do i = 1, npoint + p2(1) = mesh(i,1) + p2(2) = mesh(i,2) + if( abs( ast_distance( frm1, p1, p2, status ) - 0.01 ) .gt. + : 1.0E-6 ) call stopit( status, 'Circle: Error mesh 2' ) + enddo + + call ast_getregionmesh( cir1, .false., 250, 3, npoint, mesh, + : status ) + + if( npoint .ne. 201 ) then + write(*,*) npoint + call stopit( status, 'Circle: Error mesh 3' ) + endif + + do i = 1, npoint + p2(1) = mesh(i,1) + p2(2) = mesh(i,2) + if( ast_distance( frm1, p1, p2, status ) .gt. 0.01 ) + : call stopit( status, 'Circle: Error mesh 4' ) + enddo + + p1( 1 ) = 1.2217305 + p1( 2 ) = 1.3962634 + p2( 1 ) = 0.8 + p2( 2 ) = 0.8 + + cir1 = ast_circle( frm1, 0, p1, p2, AST__NULL, ' ', status ) + call checkdump( cir1, 'checkdump cir1', status ) + + rad = ast_distance( cir1, p1, p2, status ) + + call ast_offset( frm1, p1, p2, rad*0.999, p3, status ) + xin(1) = p3(1) + yin(1) = p3(2) + call ast_offset( frm1, p1, p2, rad*1.001, p3, status ) + xin(2) = p3(1) + yin(2) = p3(2) + + call ast_tran2( cir1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, 'Circle: Error 1' ) + if( yout(1) .ne. yin(1) ) call stopit( status, 'Circle: Error 2' ) + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 3' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 4' ) + + + xin(1) = 0.0 + yin(1) = 1.5707963 + call ast_tran2( cir1, 1, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, 'Circle: Error 1b') + if( yout(1) .ne. yin(1) ) call stopit( status, 'Circle: Error 2b') + + p2(1)=0.0 + p2(2)=0.0 + call ast_offset( frm1, p1, p2, rad*0.999, p3, status ) + xin(1) = p3(1) + yin(1) = p3(2) + call ast_offset( frm1, p1, p2, rad*1.001, p3, status ) + xin(2) = p3(1) + yin(2) = p3(2) + + call ast_tran2( cir1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, 'Circle: Error 5' ) + if( yout(1) .ne. yin(1) ) call stopit( status, 'Circle: Error 6' ) + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 7' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 8' ) + + + call ast_setc( cir1, 'system', 'galactic', status ) + cir1 = ast_simplify( cir1, status ) + if( .not. hasframeset( cir1,status ) ) call stopit( status, + : 'Circle: error 9' ) + + + pp1( 1 ) = 0.0 + pp1( 2 ) = 0.0 + pp2( 1 ) = 1.0D-6 + pp2( 2 ) = 1.0D-6 + unc = ast_box( frm1, 0, pp1, pp2, AST__NULL, ' ', status ) + + p1( 1 ) = 1.2217305 + p1( 2 ) = 1.3962634 + p2( 1 ) = 1.2218 + p2( 2 ) = 1.3963 + cir1 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + + rad = ast_distance( cir1, p1, p2, status ) + + call ast_offset( frm1, p1, p2, rad*0.999, p3, status ) + xin(1) = p3(1) + yin(1) = p3(2) + call ast_offset( frm1, p1, p2, rad*1.001, p3, status ) + xin(2) = p3(1) + yin(2) = p3(2) + + call ast_tran2( cir1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, 'Circle: Error 1b') + if( yout(1) .ne. yin(1) ) call stopit( status, 'Circle: Error 2b') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 3b' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 4b' ) + + p2(1)=0.0 + p2(2)=0.0 + call ast_offset( frm1, p1, p2, rad*0.999, p3, status ) + xin(1) = p3(1) + yin(1) = p3(2) + call ast_offset( frm1, p1, p2, rad*1.001, p3, status ) + xin(2) = p3(1) + yin(2) = p3(2) + + call ast_tran2( cir1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, 'Circle: Error 5b') + if( yout(1) .ne. yin(1) ) call stopit( status, 'Circle: Error 6b') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 7b' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Circle: Error 8b' ) + + cir2 = ast_copy( cir1, status ) + call ast_setc( cir2, 'system', 'galactic', status ) + call checkdump( cir2, 'checkdump cir2', status ) + + cir2 = ast_simplify( cir2, status ) + + if( hasframeset( cir2,status ) ) call stopit( status, + : 'Circle: error 9b' ) + + if( ast_overlap( cir1, cir2, status ) .ne. 5 ) call stopit(status, + : 'Circle: Error 10' ) + if( ast_overlap( cir2, cir1, status ) .ne. 5 ) call stopit(status, + : 'Circle: Error 11' ) + + p1( 1 ) = 1.2217305 + p1( 2 ) = 1.3964 + p2( 1 ) = 1.2218 + p2( 2 ) = 1.3963 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + if( ast_overlap( cir1, cir2, status ) .ne. 4 ) call stopit(status, + : 'Circle: Error 12' ) + if( ast_overlap( cir2, cir1, status ) .ne. 4 ) call stopit(status, + : 'Circle: Error 13' ) + + p1( 1 ) = 1.2217305 + p1( 2 ) = 1.3962634 + p2( 1 ) = 1.221731 + p2( 2 ) = 1.396268 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + if( ast_overlap( cir1, cir2, status ) .ne. 3 ) call stopit(status, + : 'Circle: Error 14' ) + if( ast_overlap( cir2, cir1, status ) .ne. 2 ) call stopit(status, + : 'Circle: Error 15' ) + + p1( 1 ) = 0.8 + p1( 2 ) = 1.0 + p2( 1 ) = 0.88 + p2( 2 ) = 1.05 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + if( ast_overlap( cir1, cir2, status ) .ne. 1 ) call stopit(status, + : 'Circle: Error 16' ) + + + p1( 1 ) = 0.8 + p1( 2 ) = 1.5707963 + p2( 1 ) = 0.1 + cir2 = ast_circle( frm1, 1, p1, p2, unc, ' ', status ) + call ast_getregionbounds( cir2, lbnd, ubnd, status ) + if( lbnd(1) .ne. 0.0D0 ) call stopit( status, + : 'Circle: Error 16a' ) + if( abs( lbnd(2) - 1.47079625 ) .gt. 1.0E-6 ) call stopit( status, + : 'Circle: Error 16b' ) + if( abs( ubnd(1) - 6.28318531 ) .gt. 1.0E-6 ) call stopit( status, + : 'Circle: Error 16c' ) + if( abs( ubnd(2) - 1.57079633 ) .gt. 1.0E-6 ) call stopit( status, + : 'Circle: Error 16d' ) + + + frm1 = ast_frame(2,"domain=aa",status) + + pp1( 1 ) = 0.0 + pp1( 2 ) = 0.0 + pp2( 1 ) = 1.0D-6 + pp2( 2 ) = 1.0D-6 + unc = ast_box( frm1, 0, pp1, pp2, AST__NULL, ' ', status ) + + p1( 1 ) = 1.2217305 + p1( 2 ) = 1.3962634 + p2( 1 ) = 1.2218 + p2( 2 ) = 1.3963 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + + if( ast_overlap( cir1, cir2, status ) .ne. 0 ) call stopit(status, + : 'Circle: Error 17' ) + if( ast_overlap( cir2, cir1, status ) .ne. 0 ) call stopit(status, + : 'Circle: Error 18' ) + + + f1 = ast_skyframe( ' ', status ) + f2 = ast_frame( 2, ' ', status ) + f3 = ast_cmpframe( f1, f2, ' ', status ) + + p1( 1 ) = 1.0 + p1( 2 ) = 1.0 + p1( 3 ) = 3.0 + p1( 4 ) = 3.0 + p2( 1 ) = 1.01 + p2( 2 ) = 1.02 + p2( 3 ) = 3.01 + p2( 4 ) = 3.01 + cir2 = ast_circle( f3, 0, p1, p2, AST__NULL, ' ', status ) + if( ast_overlap( cir2, cir2, status ) .ne. 5 ) call stopit(status, + : 'Circle: Error 18b' ) + +* Test 3D spheres + + frm1 = ast_frame( 3, ' ', status ) + + pp1( 1 ) = 0.0 + pp1( 2 ) = 0.0 + pp1( 3 ) = 0.0 + pp2( 1 ) = 1.0E-6 + pp2( 2 ) = 2.0E-6 + pp2( 3 ) = 2.0E-6 + unc = ast_box( frm1, 0, pp1, pp2, AST__NULL, ' ', status ) + + p1( 1 ) = 1.0 + p1( 2 ) = 2.0 + p1( 3 ) = 3.0 + p2( 1 ) = 0.0 + p2( 2 ) = -1.0 + p2( 3 ) = -2.0 + cir1 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + call checkdump( cir1, 'checkdump sph1', status ) + + rad = ast_distance( cir1, p1, p2, status ) + + call ast_offset( frm1, p1, p2, rad*0.999, p3, status ) + in(1,1) = p3(1) + in(1,2) = p3(2) + in(1,3) = p3(3) + call ast_offset( frm1, p1, p2, rad*1.001, p3, status ) + in(2,1) = p3(1) + in(2,2) = p3(2) + in(2,3) = p3(3) + + + call ast_trann( cir1, 2, 3, 2, in, .true., 3, 2, out, status ) + + if( out(1,1) .ne. in(1,1) ) call stopit( status, + : 'Sphere: Error 1' ) + if( out(1,2) .ne. in(1,2) ) call stopit( status, + : 'Sphere: Error 2' ) + if( out(1,3) .ne. in(1,3) ) call stopit( status, + : 'Sphere: Error 2z') + if( out(2,1) .ne. AST__BAD ) call stopit( status, + : 'Sphere: Error 3' ) + if( out(2,2) .ne. AST__BAD ) call stopit( status, + : 'Sphere: Error 4' ) + if( out(2,3) .ne. AST__BAD ) call stopit( status, + : 'Sphere: Error 4z' ) + + p2(1)=0.0 + p2(2)=0.0 + p2(3)=0.0 + call ast_offset( frm1, p1, p2, rad*0.999, p3, status ) + in(1,1) = p3(1) + in(1,2) = p3(2) + in(1,3) = p3(3) + call ast_offset( frm1, p1, p2, rad*1.001, p3, status ) + in(2,1) = p3(1) + in(2,2) = p3(2) + in(2,3) = p3(3) + + call ast_trann( cir1, 2, 3, 2, in, .true., 3, 2, out, status ) + if( out(1,1) .ne. in(1,1) ) call stopit( status, + : 'Sphere: Error 5' ) + if( out(1,2) .ne. in(1,2) ) call stopit( status, + : 'Sphere: Error 6' ) + if( out(1,3) .ne. in(1,3) ) call stopit( status, + : 'Sphere: Error 6z') + if( out(2,1) .ne. AST__BAD ) call stopit( status, + : 'Sphere: Error 7' ) + if( out(2,2) .ne. AST__BAD ) call stopit( status, + : 'Sphere: Error 8' ) + if( out(2,3) .ne. AST__BAD ) call stopit( status, + : 'Sphere: Error 8z' ) + + + if( ast_overlap( cir1, cir1, status ) .ne. 5 ) call stopit(status, + : 'Sphere: Error 10' ) + + + + + p1( 1 ) = 1.0 + p1( 2 ) = 2.0 + p1( 3 ) = 3.0 + p2( 1 ) = 0.5 + p2( 2 ) = 0.0 + p2( 3 ) = -1.0 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + call checkdump( cir2, 'checkdump sph2', status ) + + if( ast_overlap( cir2, cir1, status ) .ne. 2 ) call stopit(status, + : 'Sphere: Error 11' ) + + if( ast_overlap( cir1, cir2, status ) .ne. 3 ) call stopit(status, + : 'Sphere: Error 12' ) + + + + p1( 1 ) = 1.0 + p1( 2 ) = 0.0 + p1( 3 ) = 3.0 + p2( 1 ) = 0.0 + p2( 2 ) = -1.0 + p2( 3 ) = -2.0 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + if( ast_overlap( cir1, cir2, status ) .ne. 4 ) call stopit(status, + : 'Sphere: Error 13' ) + + p1( 1 ) = 1.0 + p1( 2 ) = 102.0 + p1( 3 ) = 3.0 + p2( 1 ) = 0.0 + p2( 2 ) = 99.0 + p2( 3 ) = -2.0 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + if( ast_overlap( cir1, cir2, status ) .ne. 1 ) call stopit(status, + : 'Sphere: Error 14' ) + + + p1( 1 ) = 0.0 + p1( 2 ) = 0.0 + p1( 3 ) = 0.0 + p2( 1 ) = 0.0 + p2( 2 ) = 0.0 + p2( 3 ) = 1.0 + cir1 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + + p1( 1 ) = 2.0000001 + p1( 2 ) = 0.0 + p1( 3 ) = 0.0 + p2( 1 ) = 2.000001 + p2( 2 ) = 1.0 + p2( 3 ) = 0.0 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + + if( ast_overlap( cir1, cir2, status ) .ne. 4 ) then + write(*,*) ast_overlap( cir1, cir2, status ),' should be 4 ' + call stopit(status, 'Sphere: Error 15' ) + end if + + p1( 1 ) = 2.000001 + p1( 2 ) = 0.0 + p1( 3 ) = 0.0 + p2( 1 ) = 2.000001 + p2( 2 ) = 1.0 + p2( 3 ) = 0.0 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + + if( ast_overlap( cir1, cir2, status ) .ne. 4 ) call stopit(status, + : 'Sphere: Error 16' ) + + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + + call ast_setl( cir1, 'Closed', .false., status ) + call ast_setl( cir2, 'Closed', .false., status ) + if( ast_overlap( cir1, cir2, status ) .ne. 1 ) call stopit(status, + : 'Sphere: Error 17' ) + call ast_clear( cir1, 'Closed', status ) + call ast_clear( cir2, 'Closed', status ) + + p1( 1 ) = 2.000004 + p1( 2 ) = 0.0 + p1( 3 ) = 0.0 + p2( 1 ) = 2.000004 + p2( 2 ) = 1.0 + p2( 3 ) = 0.0 + cir2 = ast_circle( frm1, 0, p1, p2, unc, ' ', status ) + + if( ast_overlap( cir1, cir2, status ) .ne. 1 ) call stopit(status, + : 'Sphere: Error 18' ) + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'Circle tests failed' + + end + + + + + + + + subroutine checkEllipse( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + + integer status, ell1, ell2, fc, i, fs, frm1, fs2,mm,ell3,ell4, + : reg, unc, f1, f2, f3, f4, f5, map, perm(2) + double precision p1(2),p2(2),p3(2),p4(2),pp1(2),pp2(2) + double precision q1(2),q2(2),q3(2),q4(2),lbnd(2),ubnd(2) + double precision q1b(2),q2b(2),q3b(2),q4b(2) + double precision p1b(2),p2b(2),p3b(2),p4b(2),matrix(4) + character cards(10)*80 + double precision xin(4),yin(4),xout(4),yout(4),rad + logical hasframeset + + data cards /'NAXIS1 = 300', + : 'NAXIS2 = 300', + : 'CTYPE1 = ''RA---TAN''', + : 'CTYPE2 = ''DEC--TAN''', + : 'CRPIX1 = 100', + : 'CRPIX2 = 100', + : 'CRVAL1 = 0.0', + : 'CRVAL2 = 90.0', + : 'CDELT1 = 0.6', + : 'CDELT2 = 0.6' / + + + if( status .ne.sai__ok ) return + call ast_begin( status ) + + f1 = ast_SkyFrame( 'system=fk4', status ) + f3 = ast_cmpframe( ast_pickaxes( f1, 1, 1, map, status ), + : ast_specframe( 'system=wave,unit=um', status ), + : ' ', status ) + perm(1)=2 + perm(2)=1 + call ast_permaxes( f3, perm, status ) + p1(1) = 0.0 + p1(2) = 0.0 + p2(1) = 0.001 + p2(2) = 0.001 + p3(1) = -0.001 + p3(2) = 0.001 + ell1 = ast_ellipse( f3, 0, p1, p2, p3, AST__NULL, ' ', status ) + + xin(1) = 0.0 + yin(1) = 0.00141421 + xin(2) = 0.0 + yin(2) = 0.00141422 + xin(3) = -0.000999 + yin(3) = 0.0009999 + xin(4) = -0.001001 + yin(4) = 0.001001 + call ast_tran2( ell1, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. xin(1) ) call stopit( status, 'Ellipse: Cmp 1') + if( yout(1) .ne. yin(1) ) call stopit( status, 'Ellipse: Cmp 2') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Cmp 3' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Cmp 4' ) + if( xout(3) .ne. xin(3) ) call stopit( status, 'Ellipse: Cmp 5') + if( yout(3) .ne. yin(3) ) call stopit( status, 'Ellipse: Cmp 6') + if( xout(4) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Cmp 7' ) + if( yout(4) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Cmp 8' ) + + call checkdump( ell1, 'checkdump ell1 cmp', status ) + ell2 = ast_simplify( ell1, status ) + call checkdump( ell2, 'checkdump ell2 cmp', status ) + if( ast_overlap( ell1, ell2, status ) .ne. 5 ) call stopit(status, + : 'ellipse: Error 5 cmp' ) + + fc = ast_fitschan( ast_null, ast_null, ' ', status ) + do i = 1, 10 + call ast_putfits( fc, cards(i), .false., status ) + end do + call ast_clear( fc, 'card', status ) + fs = ast_read( fc, status ) + + frm1 = ast_getframe( fs, ast__current, status ) + + p1( 1 ) = 1.2217305 + p1( 2 ) = 1.570796 + p2( 1 ) = 0.9 + p2( 2 ) = 1.470796 + p3( 1 ) = 2.9217305 + p3( 2 ) = 1.370796 + + ell1 = ast_ellipse( frm1, 0, p1, p2, p3, AST__NULL, ' ', status ) + call checkdump( ell1, 'checkdump ell1', status ) + + + call ast_getregionbounds( ell1, lbnd, ubnd, status ) + if( abs( lbnd(1) ) .gt. 1.0E-10 ) call stopit( status, + : 'Error b1' ) + if( abs( lbnd(2) - 1.19059777 ) .gt. 1.0E-6 ) + : call stopit( status, 'Error b2' ) + if( abs( ubnd(1) - 6.28318531 ) .gt. 1.0E-6 ) + : call stopit( status, 'Error b3' ) + if( abs( ubnd(2) - 1.57079633 ) .gt. 1.0E-6 ) + : call stopit( status, 'Error b4' ) + + rad = ast_distance( ell1, p1, p2, status ) + + call ast_offset( frm1, p1, p2, rad*0.999, p4, status ) + xin(1) = p4(1) + yin(1) = p4(2) + call ast_offset( frm1, p1, p2, rad*1.001, p4, status ) + xin(2) = p4(1) + yin(2) = p4(2) + + call ast_tran2( ell1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, 'Ellipse: Error 1') + if( yout(1) .ne. yin(1) ) call stopit( status, 'Ellipse: Error 2') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 3' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 4' ) + + + call ast_offset( frm1, p1, p2, -rad*0.999, p4, status ) + xin(1) = p4(1) + yin(1) = p4(2) + call ast_offset( frm1, p1, p2, -rad*1.001, p4, status ) + xin(2) = p4(1) + yin(2) = p4(2) + + call ast_tran2( ell1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, + : 'Ellipse: Error 1b') + if( yout(1) .ne. yin(1) ) call stopit( status, + : 'Ellipse: Error 2b') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 3b' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 4b' ) + + + rad = ast_distance( ell1, p1, p3, status ) + + call ast_offset( frm1, p1, p3, rad*0.999, p4, status ) + xin(1) = p4(1) + yin(1) = p4(2) + call ast_offset( frm1, p1, p3, rad*1.001, p4, status ) + xin(2) = p4(1) + yin(2) = p4(2) + + call ast_tran2( ell1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, + : 'Ellipse: Error 1c') + if( yout(1) .ne. yin(1) ) call stopit( status, + : 'Ellipse: Error 2c') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 3c' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 4c' ) + + call ast_offset( frm1, p1, p3, -rad*0.999, p4, status ) + xin(1) = p4(1) + yin(1) = p4(2) + call ast_offset( frm1, p1, p3, -rad*1.001, p4, status ) + xin(2) = p4(1) + yin(2) = p4(2) + + call ast_tran2( ell1, 2, xin, yin, .true., xout, yout, status ) + if( xout(1) .ne. xin(1) ) call stopit( status, + : 'Ellipse: Error 1d') + if( yout(1) .ne. yin(1) ) call stopit( status, + : 'Ellipse: Error 2d') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 3d' ) + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'Ellipse: Error 4d' ) + + + ell2 = ast_copy( ell1, status ) + + if( ast_overlap( ell1, ell2, status ) .ne. 5 ) call stopit(status, + : 'ellipse: Error 5' ) + if( ast_overlap( ell2, ell1, status ) .ne. 5 ) call stopit(status, + : 'ellipse: Error 6' ) + + call ast_set( ell2, 'system=galactic', status ) + if( ast_overlap( ell1, ell2, status ) .ne. 5 ) call stopit(status, + : 'ellipse: Error 7' ) + if( ast_overlap( ell2, ell1, status ) .ne. 5 ) call stopit(status, + : 'ellipse: Error 8' ) + + + + + xin(1) = p1( 1 ) + yin(1) = p1( 2 ) + xin(2) = p2( 1 ) + yin(2) = p2( 2 ) + xin(3) = p3( 1 ) + yin(3) = p3( 2 ) + call ast_tran2( fs, 3, xin, yin, .false., xout, yout, status ) + q1(1) = xout(1) + q1(2) = yout(1) + q2(1) = xout(2) + q2(2) = yout(2) + q3(1) = xout(3) + q3(2) = yout(3) + + frm1 = ast_GetFrame( fs, AST__BASE, status ) + + rad = ast_distance( frm1, q1, q2, status ) + + call ast_offset( frm1, q1, q2, rad*1.95, q1b, status ) + + q2b( 1 ) = q2( 1 ) + ( q1b( 1 ) - q1( 1 ) ) + q2b( 2 ) = q2( 2 ) + ( q1b( 2 ) - q1( 2 ) ) + + q3b( 1 ) = q3( 1 ) + ( q1b( 1 ) - q1( 1 ) ) + q3b( 2 ) = q3( 2 ) + ( q1b( 2 ) - q1( 2 ) ) + + xout(1) = q1b(1) + yout(1) = q1b(2) + xout(2) = q2b(1) + yout(2) = q2b(2) + xout(3) = q3b(1) + yout(3) = q3b(2) + call ast_tran2( fs, 3, xout, yout, .true., xin, yin, status ) + p1b( 1 ) = xin(1) + p1b( 2 ) = yin(1) + p2b( 1 ) = xin(2) + p2b( 2 ) = yin(2) + p3b( 1 ) = xin(3) + p3b( 2 ) = yin(3) + + frm1 = ast_GetFrame( fs, AST__CURRENT, status ) + + pp1( 1 ) = 0.0 + pp1( 2 ) = 0.0 + pp2( 1 ) = 1.0D-7 + pp2( 2 ) = 1.0D-7 + unc = ast_box( frm1, 0, pp1, pp2, AST__NULL, ' ', status ) + ell2 = ast_ellipse( frm1, 0, p1b, p2b, p3b, unc, ' ', status ) + if( ast_overlap( ell2, ell1, status ) .ne. 4 ) call stopit(status, + : 'ellipse: Error 9' ) + if( ast_overlap( ell1, ell2, status ) .ne. 4 ) call stopit(status, + : 'ellipse: Error 10' ) + + + call ast_offset( frm1, q1, q2, rad*2.05, q1b, status ) + + q2b( 1 ) = q2( 1 ) + ( q1b( 1 ) - q1( 1 ) ) + q2b( 2 ) = q2( 2 ) + ( q1b( 2 ) - q1( 2 ) ) + + q3b( 1 ) = q3( 1 ) + ( q1b( 1 ) - q1( 1 ) ) + q3b( 2 ) = q3( 2 ) + ( q1b( 2 ) - q1( 2 ) ) + + xout(1) = q1b(1) + yout(1) = q1b(2) + xout(2) = q2b(1) + yout(2) = q2b(2) + xout(3) = q3b(1) + yout(3) = q3b(2) + call ast_tran2( fs, 3, xout, yout, .true., xin, yin, status ) + p1b( 1 ) = xin(1) + p1b( 2 ) = yin(1) + p2b( 1 ) = xin(2) + p2b( 2 ) = yin(2) + p3b( 1 ) = xin(3) + p3b( 2 ) = yin(3) + + frm1 = ast_GetFrame( fs, AST__CURRENT, status ) + pp1( 1 ) = 0.0 + pp1( 2 ) = 0.0 + pp2( 1 ) = 1.0D-7 + pp2( 2 ) = 1.0D-7 + unc = ast_box( frm1, 0, pp1, pp2, AST__NULL, ' ', status ) + ell2 = ast_ellipse( frm1, 0, p1b, p2b, p3b, unc, ' ', status ) + if( ast_overlap( ell2, ell1, status ) .ne. 1 ) call stopit(status, + : 'ellipse: Error 11' ) + if( ast_overlap( ell1, ell2, status ) .ne. 1 ) call stopit(status, + : 'ellipse: Error 12' ) + + p1b( 1 ) = p1( 1 ) + p1b( 2 ) = p1( 2 ) + p2b( 1 ) = p2( 1 ) + p2b( 2 ) = 0.9*p2( 2 ) + 0.1*p1( 2 ) + p3b( 1 ) = p3( 1 ) + p3b( 2 ) = 0.9*p3( 2 ) + 0.1*p1( 2 ) + + ell2 = ast_ellipse( frm1, 0, p1b, p2b, p3b, unc, ' ', status ) + if( ast_overlap( ell2, ell1, status ) .ne. 2 ) call stopit(status, + : 'ellipse: Error 13' ) + if( ast_overlap( ell1, ell2, status ) .ne. 3 ) call stopit(status, + : 'ellipse: Error 14' ) + + + + + + + frm1 = ast_frame( 2, ' ', status ) + + pp1( 1 ) = 0.0 + pp1( 2 ) = 0.0 + pp2( 1 ) = 1.0D-7 + pp2( 2 ) = 1.0D-7 + unc = ast_box( frm1, 0, pp1, pp2, AST__NULL, ' ', status ) + + p1(1)=0.0 + p1(2)=0.0 + p2(1)=1.0 + p2(2)=0.0 + p3(1)=0.0 + p3(2)=0.5 + ell1 = ast_ellipse( frm1, 0, p1, p2, p3, unc, ' ', status ) + + + matrix(1) = 1.73 + matrix(2) = 0.5003 + matrix(3) = -1.0006 + matrix(4) = 0.866 + mm = ast_matrixmap( 2, 2, 0, matrix, ' ', status ) + + ell2 = ast_mapregion( ell1, mm, frm1, status ) + call checkdump( ell2, 'checkdump ell2', status ) + if( hasframeset( ell2, status ) ) call stopit( status, + : 'Ellipse: error 15' ) + call ast_invert( mm, status ) + ell3 = ast_mapregion( ell2, mm, frm1, status ) + if( hasframeset( ell3,status ) ) call stopit( status, + : 'Ellipse: error 16' ) + if( ast_overlap( ell1, ell3, status ) .ne. 5 ) call stopit(status, + : 'ellipse: Error 17' ) + + + frm1 = ast_frame( 2, ' ', status ) + pp1( 1 ) = 0.0 + pp1( 2 ) = 0.0 + pp2( 1 ) = 1.0D-7 + pp2( 2 ) = 1.0D-7 + unc = ast_box( frm1, 0, pp1, pp2, AST__NULL, ' ', status ) + + p1(1)=0.0 + p1(2)=0.0 + p2(1)=1.0 + p2(2)=0.0 + p3(1)=0.0 + p3(2)=1.0 + ell1 = ast_ellipse( frm1, 0, p1, p2, p3, unc, ' ', status ) + reg = ast_simplify( ell1, status ) + if( .not. ast_IsACircle( reg, status ) ) call stopit(status, + : 'ellipse: Error 18' ) + + ell1 = ast_circle( frm1, 0, p1, p2, AST__NULL, ' ', status ) + if( ast_overlap( reg, ell1, status ) .ne. 5 ) call stopit(status, + : 'Ellipse: Error 19' ) + + + + frm1 = ast_skyframe( ' ', status ) + p1(1)=0.0D0 + p1(2)=0.0D0 + p2(1)=0.01D0 + p2(2)=0.01D0 + p3(1)=0.0D0 + p3(2)=0.0D0 + ell1 = ast_ellipse( frm1, 1, p1, p2, p3, AST__NULL, ' ', status ) + + p1(1)=-0.015D0 + p1(2)=0.0D0 + p2(1)=0.01D0 + p2(2)=0.01D0 + p3(1)=0.0D0 + p3(2)=0.0D0 + ell2 = ast_ellipse( frm1, 1, p1, p2, p3, AST__NULL, ' ', status ) + + if( ast_overlap( ell1, ell2, status ) .ne. 4 ) call stopit(status, + : 'Ellipse: Error 20' ) + + p1(1)=6.2681853D0 + p1(2)=0.0D0 + p2(1)=0.01D0 + p2(2)=0.01D0 + p3(1)=0.0D0 + p3(2)=0.0D0 + ell2 = ast_ellipse( frm1, 1, p1, p2, p3, AST__NULL, ' ', status ) + + if( ast_overlap( ell1, ell2, status ) .ne. 4 ) call stopit(status, + : 'Ellipse: Error 21' ) + + p1(1)=-0.015D0 + p1(2)=0.0D0 + p2(1)=0.01D0 + p2(2)=0.01D0 + p3(1)=0.0D0 + p3(2)=0.0D0 + ell1 = ast_ellipse( frm1, 1, p1, p2, p3, AST__NULL, ' ', status ) + + if( ast_overlap( ell1, ell2, status ) .ne. 5 ) call stopit(status, + : 'Ellipse: Error 22' ) + + + + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'Ellipse tests failed' + + end + + + subroutine checkNullRegion( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + include 'PRM_PAR' + + integer status, f1, f2, f3, nr, cir, i, j, lbnd_in(2), ubnd_in(2), + : nr2, res + double precision p1(4),p2(4),rin(5,5) + logical hasframeset + + if( status .ne.sai__ok ) return + + call ast_begin( status ) + + f1 = ast_skyframe( ' ', status ) + f2 = ast_frame( 2, ' ', status ) + f3 = ast_cmpframe( f1, f2, ' ', status ) + nr = ast_NullRegion( f3, AST__NULL, ' ', status ) + + call checkdump( nr, 'checkdump NullRegion:nr', status ) + + p1( 1 ) = 1.0 + p1( 2 ) = 1.0 + p1( 3 ) = 3.0 + p1( 4 ) = 3.0 + p2( 1 ) = 1.01 + p2( 2 ) = 1.02 + p2( 3 ) = 3.01 + p2( 4 ) = 3.01 + cir = ast_circle( nr, 0, p1, p2, AST__NULL, ' ', status ) + call checkdump( cir, 'checkdump NullRegion:cir', status ) + + if( ast_overlap( cir, nr, status ) .ne. 1 ) call stopit(status, + : 'NullRegion: Error 1' ) + + if( ast_overlap( nr, cir, status ) .ne. 1 ) call stopit(status, + : 'NullRegion: Error 2' ) + + if( ast_overlap( nr, nr, status ) .ne. 5 ) call stopit(status, + : 'NullRegion: Error 3' ) + + call ast_negate( nr, status ) + + if( ast_overlap( cir, nr, status ) .ne. 2 ) call stopit(status, + : 'NullRegion: Error 4' ) + + if( ast_overlap( nr, cir, status ) .ne. 3 ) call stopit(status, + : 'NullRegion: Error 5' ) + + if( ast_overlap( nr, nr, status ) .ne. 5 ) call stopit(status, + : 'NullRegion: Error 6' ) + + call ast_set( nr, 'system(1)=FK4', status ) + nr2 = ast_simplify( nr, status ) + call ast_set( nr2, 'system(1)=ICRS', status ) + nr = ast_simplify( nr2, status ) + if( hasframeset( nr, status ) ) call stopit( status, + : 'NullRegion: error 7' ) + + lbnd_in(1) = 1 + lbnd_in(2) = 1 + ubnd_in(1) = 5 + ubnd_in(2) = 5 + + do i =1, 5 + do j = 1, 5 + rin( j,i)=1.0 + end do + end do + + nr = ast_NullRegion( f2, AST__NULL, 'negated=1', status ) + res = ast_maskd( nr, AST__NULL, .false., 2, lbnd_in, ubnd_in, + : rin, VAL__BADD, status ) + + if( res .ne. 0 ) then + write(*,*) 'NullRegion:Res is ',res + call stopit( status, 'res should be 0' ) + end if + + do i =1, 5 + do j = 1, 5 + if( rin(j,i) .NE. 1.0 ) then + write(*,*) 'rin(',j,',',i,') = ',rin(j,i) + call stopit( status, 'Above value should be 1.0' ) + end if + end do + end do + + call ast_negate( nr, status ) + res = ast_maskd( nr, AST__NULL, .false., 2, lbnd_in, ubnd_in, + : rin, VAL__BADD, status ) + + if( res .ne. 25 ) then + write(*,*) 'NullRegion:Res is ',res + call stopit( status, 'res should be 25' ) + end if + + do i =1, 5 + do j = 1, 5 + if( rin(j,i) .NE. VAL__BADD ) then + write(*,*) 'rin(',j,',',i,') = ',rin(j,i) + call stopit( status, 'Above value should be BAD' ) + end if + end do + end do + + + + + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'NullRegion tests failed' + + end + + + + + subroutine checkCmpRegion( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + include 'PRM_PAR' + + integer status, r1, r2, r3, cr, f1, f2, cr2, cr3, frm, map, fs + double precision p1(2),p2(2),xout(4),yout(4),xin(4),yin(4) + logical hasframeset + + if( status .ne.sai__ok ) return + + call ast_begin( status ) + + + f1 = ast_skyframe( 'system=fk5', status ) + p1(1) = 0.0 + p1(2) = 0.0 + p2(1) = 1.0E-4 + p2(2) = 1.0E-4 + r1 = ast_box( f1, 0, p1, p2, AST__NULL, ' ', status ) + + f2 = ast_skyframe( 'system=galactic', status ) + + p1(1) = 1.68166715892457 + p1(2) = -1.050436507472 + p2(1) = 1.68140254777194 + p2(2) = -1.05048840003467 + r2 = ast_circle( f2, 0, p1, p2, AST__NULL, ' ', status ) + + if( ast_overlap( r1, r2, status ) .ne. 4 ) call stopit(status, + : 'CmpRegion: Error 0' ) + + cr = ast_cmpregion( r1, r2, AST__AND, ' ', status ) + cr = ast_Copy( cr, status ) + if( ast_overlap( cr, cr, status ) .ne. 5 ) call stopit(status, + : 'CmpRegion: Error 1' ) + + xin( 1 ) = 0.5E-4! In both r1 and r2 + xin( 2 ) = 1.5E-4! In r2 but not r1 + xin( 3 ) = -0.5E-4! In r1 but not r2 + xin( 4 ) = 1.1E-4! In neither + + yin( 1 ) = 0.5E-4 + yin( 2 ) = 1.5E-4 + yin( 3 ) = -0.5E-4 + yin( 4 ) = -1.1E-4 + + call ast_tran2( cr, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. xin(1) ) call stopit( status, + : 'CmpRegion: AND Error 1x') + if( yout(1) .ne. yin(1) ) call stopit( status, + : 'CmpRegion: AND Error 1y') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: AND Error 2x') + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: AND Error 2y') + if( xout(3) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: AND Error 3x') + if( yout(3) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: AND Error 3y') + if( xout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: AND Error 4x') + if( yout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: AND Error 4y') + + cr = ast_cmpregion( r1, r2, AST__OR, ' ', status ) + call ast_tran2( cr, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. xin(1) ) call stopit( status, + : 'CmpRegion: OR Error 1x') + if( yout(1) .ne. yin(1) ) call stopit( status, + : 'CmpRegion: OR Error 1y') + if( xout(2) .ne. xin(2) ) call stopit( status, + : 'CmpRegion: OR Error 2x') + if( yout(2) .ne. yin(2) ) call stopit( status, + : 'CmpRegion: OR Error 2y') + if( xout(3) .ne. xin(3) ) call stopit( status, + : 'CmpRegion: OR Error 3x') + if( yout(3) .ne. yin(3) ) call stopit( status, + : 'CmpRegion: OR Error 3y') + if( xout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: OR Error 4x') + if( yout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: OR Error 4y') + + + call ast_negate( r2, status ) + cr = ast_cmpregion( r1, r2, AST__AND, ' ', status ) + call ast_tran2( cr, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDb Error 1x') + if( yout(1) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDb Error 1y') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDb Error 2x') + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDb Error 2y') + if( xout(3) .ne. xin(3) ) call stopit( status, + : 'CmpRegion: ANDb Error 3x') + if( yout(3) .ne. yin(3) ) call stopit( status, + : 'CmpRegion: ANDb Error 3y') + if( xout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDb Error 4x') + if( yout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDb Error 4y') + + call ast_negate( r1, status ) + cr = ast_cmpregion( r1, r2, AST__AND, ' ', status ) + call ast_tran2( cr, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDc Error 1x') + if( yout(1) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDc Error 1y') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDc Error 2x') + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDc Error 2y') + if( xout(3) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDc Error 3x') + if( yout(3) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDc Error 3y') + if( xout(4) .ne. xin(4) ) call stopit( status, + : 'CmpRegion: ANDc Error 4x') + if( yout(4) .ne. yin(4) ) call stopit( status, + : 'CmpRegion: ANDc Error 4y') + + + cr = ast_cmpregion( r1, r2, AST__AND, ' ', status ) + call ast_negate( cr, status ) + call ast_tran2( cr, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. xin(1) ) call stopit( status, + : 'CmpRegion: ANDd Error 1x') + if( yout(1) .ne. yin(1) ) call stopit( status, + : 'CmpRegion: ANDd Error 1y') + if( xout(2) .ne. xin(2) ) call stopit( status, + : 'CmpRegion: ANDd Error 2x') + if( yout(2) .ne. yin(2) ) call stopit( status, + : 'CmpRegion: ANDd Error 2y') + if( xout(3) .ne. xin(3) ) call stopit( status, + : 'CmpRegion: ANDd Error 3x') + if( yout(3) .ne. yin(3) ) call stopit( status, + : 'CmpRegion: ANDd Error 3y') + if( xout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDd Error 4x') + if( yout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion: ANDd Error 4y') + + + cr2 = ast_cmpregion( r2, r1, AST__AND, ' ', status ) + + fs = ast_convert( cr, cr2, ' ', status ) + if( fs .eq. AST__NULL ) call stopit( status, + : 'CmpRegion: Error 5') + map = ast_getmapping( fs, AST__BASE, AST__CURRENT, status ) + frm = ast_getframe( fs, AST__CURRENT, status ) + cr3 = ast_mapRegion( cr, map, frm, status ) + if( ast_overlap( cr3, cr2, status ) .ne. 6 ) call stopit(status, + : 'CmpRegion: Error 6' ) + + cr = ast_Copy( cr, status ) + call checkdump( cr, 'checkdump CmpRegion: cr', status ) + + + cr2 = ast_Copy( cr, status ) + call ast_negate( cr2, status ) + + cr3 = ast_cmpregion( cr2, cr, AST__OR, ' ', status ) + cr3 = ast_Simplify( cr3, status ) + if( .not. ast_isanullregion( cr3, status ) ) then + call stopit(status, 'CmpRegion: Error 7' ) + else if( .not. ast_getl( cr3, 'negated', status ) ) then + call stopit(status, 'CmpRegion: Error 8' ) + end if + + cr3 = ast_cmpregion( cr2, cr, AST__AND, ' ', status ) + cr3 = ast_Simplify( cr3, status ) + if( .not. ast_isanullregion( cr3, status ) ) then + call stopit(status, 'CmpRegion: Error 9' ) + else if( ast_getl( cr3, 'negated', status ) ) then + call stopit(status, 'CmpRegion: Error 10' ) + end if + + + + f1 = ast_frame( 2, ' ', status ) + p1(1) = 0.0 + p1(2) = 0.0 + p2(1) = 1.0 + p2(2) = 1.0 + r1 = ast_box( f1, 0, p1, p2, AST__NULL, ' ', status ) + + p1(1) = -1.0 + p1(2) = 0.0 + p2(1) = 0.0 + p2(2) = 0.0 + r2 = ast_circle( f1, 0, p1, p2, AST__NULL, ' ', status ) + + p1(1) = 1.0 + p1(2) = 0.0 + p2(1) = 0.0 + p2(2) = 0.0 + r3 = ast_circle( f1, 0, p1, p2, AST__NULL, ' ', status ) + + cr = ast_cmpregion( r2, r3, AST__OR, ' ', status ) + call checkdump( cr, 'checkdump CmpRegion: cr', status ) + + call ast_negate( cr, status ) + cr2 = ast_cmpregion( cr, r1, AST__AND, ' ', status ) + call checkdump( cr2, 'checkdump CmpRegion: cr2', status ) + + cr2 = ast_simplify( cr2, status ) + + xin( 1 ) = 0.0 + xin( 2 ) = 0.2 + xin( 3 ) = 0.5 + xin( 4 ) = -0.5 + + yin( 1 ) = 0.5 + yin( 2 ) = 1.5 + yin( 3 ) = 0.5 + yin( 4 ) = 0.5 + + call ast_tran2( cr2, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. xin(1) ) call stopit( status, + : 'CmpRegion:Error 11') + if( yout(1) .ne. yin(1) ) call stopit( status, + : 'CmpRegion:Error 12') + if( xout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 13') + if( yout(2) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 14') + if( xout(3) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 15') + if( yout(3) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 16') + if( xout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 17') + if( yout(4) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 18') + + + + call ast_negate( cr2, status ) + cr2 = ast_simplify( cr2, status ) + call ast_tran2( cr2, 4, xin, yin, .true., xout, yout, status ) + + if( xout(1) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 19') + if( yout(1) .ne. AST__BAD ) call stopit( status, + : 'CmpRegion:Error 20') + if( xout(2) .ne. xin(2) ) call stopit( status, + : 'CmpRegion:Error 21') + if( yout(2) .ne. yin(2) ) call stopit( status, + : 'CmpRegion:Error 22') + if( xout(3) .ne. xin(3) ) call stopit( status, + : 'CmpRegion:Error 23') + if( yout(3) .ne. yin(3) ) call stopit( status, + : 'CmpRegion:Error 24') + if( xout(4) .ne. xin(4) ) call stopit( status, + : 'CmpRegion:Error 25') + if( yout(4) .ne. yin(4) ) call stopit( status, + : 'CmpRegion:Error 26') + + + + + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'CmpRegion tests failed' + + end + + + + + + + +* +* Tests the dump function, the loader, and the astOverlap method. +* + subroutine checkdump( obj, text, status ) + + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap + external mysource, mysink + character buf*45000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + +* Create a Channel which reads and writes to an internal string buffer. + ch = ast_channel( mysource, mysink, ' ', status ) + +* Write the supplied Region out to this Channel. + ll = 160 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + +* Read an Object back from this Channel. + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + +* Check that it is a Region and its boundary is identical to the supplied +* Region. + overlap = ast_overlap( obj, result, status ) + if( overlap .ne. 5 ) then + write(*,*) 'obj result Overlap: ', overlap + write(*,*) 'obj self-Overlap: ', ast_overlap( obj, obj, + : status ) + write(*,*) 'result self-Overlap: ', ast_overlap( result, + : result, status ) + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll + character buf*45000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf*45000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 45000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l,' greater than ',ll + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + endif + + next = next + ll + + end + + + + + subroutine checkPrism( status ) + implicit none + include 'AST_PAR' + include 'SAE_PAR' + + integer f1, f2, r1, r2, r3, r4, status + double precision lbnd(5),ubnd(5),p1(5),p2(5) + logical hasframeset + + if( status .ne.sai__ok ) return + + call ast_begin( status ) + + f1 = ast_skyframe( 'system=fk5', status ) + p1(1) = 0.0 + p1(2) = 0.0 + p2(1) = 1.0E-4 + p2(2) = 1.0E-4 + r1 = ast_box( f1, 0, p1, p2, AST__NULL, ' ', status ) + + f2 = ast_specframe( 'Unit=Angstrom', status ) + lbnd( 1 ) = 5000.0 + ubnd( 1 ) = 6000.0 + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r3 = ast_prism( r1, r2, ' ', status ) + + call checkdump( r3, 'checkdump Prism 1', status ) + + if( ast_overlap( r3, r3, status ) .ne. 5 ) call stopit( status, + : 'Prism 1' ) + + r4 = ast_Simplify( r3, status ) + if( .not. ast_isabox( r4, status ) ) call stopit( status, + : 'Prism 1b' ) + if( hasframeset( r4, status ) ) call stopit( status, 'Prism 1c' ) + if( ast_overlap( r3, r4, status ) .ne. 5 ) call stopit( status, + : 'Prism 1d' ) + + + lbnd( 1 ) = 5500.0 + ubnd( 1 ) = 5800.0 + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + + if( ast_overlap( r3, r4, status ) .ne. 3 ) call stopit( status, + : 'Prism 2' ) + if( ast_overlap( r4, r3, status ) .ne. 2 ) then + write(*,*) ast_overlap( r4, r3, status ),' should be 2' + call stopit( status, 'Prism 3' ) + end if + + lbnd( 1 ) = 5500.0 + ubnd( 1 ) = 6500.0 + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + if( ast_overlap( r3, r4, status ) .ne. 4 ) call stopit( status, + : 'Prism 4' ) + if( ast_overlap( r4, r3, status ) .ne. 4 ) call stopit( status, + : 'Prism 5' ) + + lbnd( 1 ) = 6500.0 + ubnd( 1 ) = 7500.0 + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + if( ast_overlap( r3, r4, status ) .ne. 1 ) call stopit( status, + : 'Prism 6' ) + if( ast_overlap( r4, r3, status ) .ne. 1 ) call stopit( status, + : 'Prism 7' ) + + r4 = ast_copy( r3, status ) + call ast_Negate( r4, status ) + if( ast_overlap( r4, r3, status ) .ne. 6 ) call stopit( status, + : 'Prism 8' ) + + + p1(1) = 2.0E-4 + p1(2) = 2.0E-4 + p2(1) = 1.1E-4 + p2(2) = 1.0E-4 + r1 = ast_box( f1, 0, p1, p2, AST__NULL, ' ', status ) + lbnd( 1 ) = 5000.0 + ubnd( 1 ) = 6000.0 + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + if( ast_overlap( r3, r4, status ) .ne. 1 ) call stopit( status, + : 'Prism 9' ) + + p1(1) = 2.0E-4 + p1(2) = 2.0E-4 + p2(1) = 1.0E-4 + p2(2) = 1.0E-4 + r1 = ast_box( f1, 0, p1, p2, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + if( ast_overlap( r3, r4, status ) .ne. 4 ) call stopit( status, + : 'Prism 10' ) + + call ast_setl( r3, 'Closed', .false., status ) + call ast_setl( r4, 'Closed', .false., status ) + if( ast_overlap( r3, r4, status ) .ne. 1 ) call stopit( status, + : 'Prism 11' ) + + + + f1 = ast_skyframe( 'system=fk5', status ) + p1(1) = 0.0 + p1(2) = 0.0 + p2(1) = 1.0E-4 + p2(2) = 1.0E-4 + r1 = ast_box( f1, 0, p1, p2, AST__NULL, ' ', status ) + + f2 = ast_specframe( 'System=Wavelen,Unit=Angstrom', status ) + lbnd( 1 ) = 5000.0 + ubnd( 1 ) = AST__BAD + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r3 = ast_prism( r1, r2, ' ', status ) + + lbnd( 1 ) = 6000.0 + ubnd( 1 ) = AST__BAD + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + + call ast_setc( r3, 'system(1)', 'galactic', status ) + + if( ast_overlap( r3, r4, status ) .ne. 3 ) call stopit( status, + : 'Prism 12' ) + + ubnd( 1 ) = 6000.0 + lbnd( 1 ) = AST__BAD + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + + if( ast_overlap( r3, r4, status ) .ne. 4 ) call stopit( status, + : 'Prism 13' ) + + ubnd( 1 ) = 5000.0 + lbnd( 1 ) = AST__BAD + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r4 = ast_prism( r1, r2, ' ', status ) + call ast_setc( r4, 'system(3)', 'freq', status ) + if( ast_overlap( r3, r4, status ) .ne. 4 ) call stopit( status, + : 'Prism 14' ) + + call ast_setl( r4, 'closed', .false., status ) + if( ast_overlap( r3, r4, status ) .ne. 1 ) call stopit( status, + : 'Prism 15' ) + + + f1 = ast_skyframe( 'system=fk5', status ) + p1(1) = 0.0 + p1(2) = -1.57 + p2(1) = 0.8 + p2(2) = -1.5 + r1 = ast_box( f1, 0, p1, p2, AST__NULL, ' ', status ) + + f2 = ast_specframe( 'Unit=Angstrom', status ) + lbnd( 1 ) = 5000.0 + ubnd( 1 ) = 6000.0 + r2 = ast_interval( f2, lbnd, ubnd, AST__NULL, ' ', status ) + r3 = ast_prism( r1, r2, ' ', status ) + r4 = ast_Simplify( r3, status ) + + call ast_getregionbounds( r4, lbnd, ubnd, status ) + if( abs( lbnd(1) + 0.8D0 ) .gt. 1.0E-6 ) call stopit( status, + : 'Prism 16' ) + if( abs( lbnd(2) + 1.64D0 ) .gt. 1.0E-6 ) + : call stopit( status, 'Prism 17' ) + if( abs( ubnd(1) - 0.8D0 ) .gt. 1.0E-6 ) + : call stopit( status, 'Prism 18' ) + if( abs( ubnd(2) + 1.5 ) .gt. 1.0E-6 ) + : call stopit( status, 'Prism 19' ) + if( abs( lbnd(3) - 5000.0 ) .gt. 1.0E-10 ) + : call stopit( status, 'Prism 20' ) + if( abs( ubnd(3) - 6000.0 ) .gt. 1.0E-6 ) + : call stopit( status, 'Prism 21' ) + + call ast_end( status ) + if( status .ne. sai__ok ) write(*,*) 'Prism tests failed' + + end + + + + subroutine checkRemoveRegions( status ) + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + + integer status, sf1, sf2, reg, fs, map, fs2 + double precision cen(2), ixin(2), iyin(2), gxin(2), gyin(2), + : xout(2), yout(2) + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + + sf1 = ast_skyframe( 'System=ICRS', status ) + cen(1) = 0.0 + cen(2) = 0.0 + reg = ast_circle( sf1, 1, cen, 0.001D0, AST__NULL, ' ', status ) + + ixin(1) = 0.0 + iyin(1) = 0.0 + ixin(2) = 0.01 + iyin(2) = 0.01 + + call ast_tran2( reg, 2, ixin, iyin, .true., xout, yout, status ) + + if( xout(1) .eq. AST__BAD .or. yout(1) .eq. AST__BAD ) then + call stopit( status, 'RemoveRegions test 1 failed' ) + + else if( abs( xout(1) - ixin(1) ) .gt. 1.0E-10 .or. + : abs( yout(1) - iyin(1) ) .gt. 1.0E-10 ) then + call stopit( status, 'RemoveRegions test 2 failed' ) + + else if( xout(2) .ne. AST__BAD .or. yout(2) .ne. AST__BAD ) then + write(*,*) xout(2), ixin(2) + write(*,*) yout(2), iyin(2) + call stopit( status, 'RemoveRegions test 3 failed' ) + end if + + + + + sf2 = ast_skyframe( 'System=Galactic', status ) + fs = ast_convert( sf1, sf2, ' ', status ) + call ast_tran2( fs, 2, ixin, iyin, .true., gxin, gyin, status ) + + fs2 = ast_frameset( sf2, ' ', status ) + call ast_addframe( fs2, AST__BASE, ast_unitmap( 2, ' ', status ), + : sf2, status ) + + + fs = ast_convert( fs2, reg, ' ', status ) + + + map = ast_getmapping( fs, AST__BASE, AST__CURRENT, status ) + call ast_tran2( map, 2, gxin, gyin, .true., xout, yout, status ) + + if( xout(1) .eq. AST__BAD .or. yout(1) .eq. AST__BAD ) then + call stopit( status, 'RemoveRegions test 4 failed' ) + + else if( abs( xout(1) - ixin(1) ) .gt. 1.0E-10 .or. + : abs( yout(1) - iyin(1) ) .gt. 1.0E-10 ) then + call stopit( status, 'RemoveRegions test 5 failed' ) + + else if( xout(2) .ne. AST__BAD .or. yout(2) .ne. AST__BAD ) then + write(*,*) xout(2), ixin(2) + write(*,*) yout(2), iyin(2) + call stopit( status, 'RemoveRegions test 6 failed' ) + end if + + + + fs2 = ast_removeregions( fs, status ) + + map = ast_getmapping( fs2, AST__BASE, AST__CURRENT, status ) + call ast_tran2( map, 2, gxin, gyin, .true., xout, yout, status ) + + if( xout(1) .eq. AST__BAD .or. yout(1) .eq. AST__BAD ) then + call stopit( status, 'RemoveRegions test 7 failed' ) + + else if( abs( xout(1) - ixin(1) ) .gt. 1.0E-10 .or. + : abs( yout(1) - iyin(1) ) .gt. 1.0E-10 ) then + call stopit( status, 'RemoveRegions test 8 failed' ) + + else if( abs( xout(2) - ixin(2) ) .gt. 1.0E-10 .or. + : abs( yout(2) - iyin(2) ) .gt. 1.0E-10 ) then + call stopit( status, 'RemoveRegions test 9 failed' ) + end if + + + + call ast_end( status ) + + end + + + + + + subroutine checkConvex( status ) + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + + integer nx, ny, nel + parameter( nx = 8 ) + parameter( ny = 7 ) + parameter( nel = nx*ny ) + + integer status, poly, lbnd(2), ubnd(2), npoint + real array( nx, ny ) + double precision points( 10, 2 ) + + data array / nel*0.0 / + data lbnd / -10, 3 / + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + + ubnd( 1 ) = lbnd( 1 ) + nx- 1 + ubnd( 2 ) = lbnd( 2 ) + ny- 1 + + array( 6, 1 ) = 1.0 + array( 7, 1 ) = 1.0 + array( 8, 1 ) = 1.0 + array( 7, 2 ) = 1.0 + array( 8, 2 ) = 1.0 + array( 2, 3 ) = 1.0 + array( 8, 3 ) = 1.0 + array( 1, 4 ) = 1.0 + array( 1, 6 ) = 1.0 + array( 2, 6 ) = 1.0 + array( 6, 6 ) = 1.0 + + poly = ast_convexr( 1.0, AST__EQ, array, lbnd, ubnd, .FALSE., + : status ) + + call ast_getregionpoints( poly, 10, 2, npoint, points, status ) + + if( npoint .ne. 7 ) call stopit( status, 'Convex 1' ) + if( points( 1, 1 ) .ne. -3) call stopit( status, 'Convex 2' ) + if( points( 1, 2 ) .ne. 3) call stopit( status, 'Convex 3' ) + if( points( 2, 1 ) .ne. -3) call stopit( status, 'Convex 4' ) + if( points( 2, 2 ) .ne. 5) call stopit( status, 'Convex 5' ) + if( points( 3, 1 ) .ne. -5) call stopit( status, 'Convex 6' ) + if( points( 3, 2 ) .ne. 8) call stopit( status, 'Convex 7' ) + if( points( 4, 1 ) .ne. -10) call stopit( status, 'Convex 8' ) + if( points( 4, 2 ) .ne. 8) call stopit( status, 'Convex 9' ) + if( points( 5, 1 ) .ne. -10) call stopit( status, 'Convex 10' ) + if( points( 5, 2 ) .ne. 6) call stopit( status, 'Convex 11' ) + if( points( 6, 1 ) .ne. -9) call stopit( status, 'Convex 12' ) + if( points( 6, 2 ) .ne. 5) call stopit( status, 'Convex 13' ) + if( points( 7, 1 ) .ne. -5) call stopit( status, 'Convex 14' ) + if( points( 7, 2 ) .ne. 3) call stopit( status, 'Convex 15' ) + + call ast_end( status ) + + end + diff --git a/ast_tester/testskyframe.f b/ast_tester/testskyframe.f new file mode 100644 index 0000000..f1c47cf --- /dev/null +++ b/ast_tester/testskyframe.f @@ -0,0 +1,70 @@ + program testskyframe + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, sf1, sf2, fs + status = sai__ok + + sf1 = ast_skyframe( 'system=fk5,epoch=2015.0', status ) + sf2 = ast_skyframe( 'system=fk5,epoch=2015.1', status ) + fs = ast_convert( sf1, sf2, 'SKY', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'Error 0' ) + end if + + if( .not. ast_isaunitmap( ast_getmapping( fs, ast__base, + : ast__current, status ), + : status ) ) then + call stopit( status, 'Error 1' ) + end if + + if( ast_getd( sf1, 'SkyTol', status ) .ne. 0.001D0 ) then + call stopit( status, 'Error 2' ) + end if + + call ast_setd( sf2, 'SkyTol', 1.0D-6, status ) + fs = ast_convert( sf1, sf2, 'SKY', status ) + + if( ast_isaunitmap( ast_getmapping( fs, ast__base, + : ast__current, status ), + : status ) ) then + call stopit( status, 'Error 3' ) + end if + + sf2 = ast_skyframe( 'system=fk5,epoch=2016.6', status ) + fs = ast_convert( sf1, sf2, 'SKY', status ) + if( ast_isaunitmap( ast_getmapping( fs, ast__base, + : ast__current, status ), + : status ) ) then + call stopit( status, 'Error 4' ) + end if + + + + + + + + + if( status .eq. sai__ok ) then + write(*,*) 'All SkyFrame tests passed' + else + write(*,*) 'SkyFrame tests failed' + end if + + end + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + diff --git a/ast_tester/testspecflux.f b/ast_tester/testspecflux.f new file mode 100644 index 0000000..ba19984 --- /dev/null +++ b/ast_tester/testspecflux.f @@ -0,0 +1,331 @@ + program testspecflux + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + double precision xin, xout,yin, yout + integer status, sff, sff2, sf, ff, ff2, mp, fs, sf2,perm(2),csff + status = sai__ok + + sf = ast_specframe( 'system=freq,unit=GHz', status ) + ff = ast_Fluxframe( 123.0D0, sf, 'Unit=Jy', status ) + sff = ast_specfluxframe( sf, ff, ' ', status ) + + + if( ast_GetC( sff, 'class', status ) .ne. 'SpecFluxFrame' ) then + call stopit( status, 'Error 0' ) + end if + + if( ast_GetD( sff, 'specval', status ) .ne. 123.0D0 ) then + call stopit( status, 'Error 1' ) + end if + + if( ast_Test( sff, 'specval', status ) ) then + call stopit( status, 'Error 2' ) + end if + + call ast_setd( sff, 'specval', 333.3D0, status ) + if( ast_GetD( sff, 'specval', status ) .ne. 333.3D0 ) then + call stopit( status, 'Error 3' ) + end if + + if( .not. ast_Test( sff, 'specval', status ) ) then + call stopit( status, 'Error 4' ) + end if + + call ast_clear( sff, 'specval', status ) + + if( ast_GetD( sff, 'specval', status ) .ne. 123.0D0 ) then + call stopit( status, 'Error 5' ) + end if + + if( ast_Test( sff, 'specval', status ) ) then + call stopit( status, 'Error 6' ) + end if + + + call checkDump( sff, 'CheckDump 1', status ) + + + ff2 = ast_Fluxframe( 123.1D0, sf, 'System=flxdnw', status ) + + if( ast_getc( ff2, 'unit', status ) .ne. 'W/m^2/Angstrom' ) + : call stopit( status, 'Error 6B' ) + if( ast_getc( ff2, 'system', status ) .ne. 'FLXDNW' ) + : call stopit( status, 'error 6C' ) + + sff2 = ast_specfluxframe( sf, ff2, ' ', status ) + if( ast_GetC( sff2, 'class', status ) .ne. 'SpecFluxFrame' ) then + call stopit( status, 'Error 7' ) + end if + + csff = ast_copy( sff,status ) + fs = ast_convert( sff, sff2, ' ', status ) + if( fs .eq. ast__null ) call stopit( status, 'error 8' ) + + yin = 1.0D0 + xin = 2.0D0 + call ast_tran2( fs, 1, xin, yin, .true., xout, yout, status ) + + if( abs(yout - 1.33425638D-26) .gt. 1.0D-32 ) + : call stopit( status, 'error 9' ) + + if( xout .ne. 2.0D0 ) call stopit( status, 'error 10' ) + + perm(1)=2 + perm(2)=1 + call ast_PermAxes( sff2, perm, status ) + + fs = ast_convert( sff, sff2, ' ', status ) + if( fs .eq. ast__null ) call stopit( status, 'error 11' ) + call ast_tran2( fs, 1, xin, yin, .true., xout, yout, status ) + + if( abs(xout - 1.33425638D-26) .gt. 1.0D-32 ) + : call stopit( status, 'error 12' ) + + if( yout .ne. 2.0D0 ) call stopit( status, 'error 13' ) + + perm(1)=2 + perm(2)=1 + call ast_PermAxes( sff, perm, status ) + + fs = ast_convert( sff, sff2, ' ', status ) + if( fs .eq. ast__null ) call stopit( status, 'error 14' ) + + yin = 2.0D0 + xin = 1.0D0 + call ast_tran2( fs, 1, xin, yin, .true., xout, yout, status ) + + if( abs(xout - 1.33425638D-26) .gt. 1.0D-32 ) + : call stopit( status, 'error 15' ) + + if( yout .ne. 2.0D0 ) call stopit( status, 'error 16' ) + + + + ff2 = ast_Fluxframe( AST__BAD, AST__NULL, 'Unit=log(W/m2/nm)', + : status ) + if( ast_getc( ff2, 'system', status ) .ne. 'FLXDNW' ) + : call stopit( status, 'error 17' ) + sff2 = ast_specfluxframe( sf, ff2, ' ', status ) + + fs = ast_convert( csff, sff2, ' ', status ) + if( fs .eq. ast__null ) call stopit( status, 'error 18' ) + + yin = 1.0D0 + xin = 2.0D0 + call ast_tran2( fs, 1, xin, yin, .true., xout, yout, status ) + + if( abs(yout + 24.8747607 ) .gt. 0.000001 ) then + write(*,*) yout + 24.8747607 + call stopit( status, 'error 19' ) + endif + + if( xout .ne. 2.0D0 ) call stopit( status, 'error 20' ) + + + call ast_tran2( fs, 1, xout, yout, .false., xin, yin, status ) + + if( abs( xin - 2.0D0 ) .gt. 1.0D-9 ) call stopit( status, + : 'error 21' ) + if( abs( yin - 1.0D0 ) .gt. 1.0D-9 ) call stopit( status, + : 'error 22' ) + + + + + + ff2 = ast_Fluxframe( AST__BAD, AST__NULL, 'Unit=log(W/m2/nm/sr)', + : status ) + if( ast_getc( ff2, 'system', status ) .ne. 'SFCBRW' ) + : call stopit( status, 'error 23' ) + sff2 = ast_specfluxframe( sf, ff2, ' ', status ) + + sf = ast_specframe( 'system=freq,unit=GHz', status ) + ff = ast_Fluxframe( 123.0D0, sf, 'Unit=Jy/deg**2', status ) + if( ast_getc( ff, 'system', status ) .ne. 'SFCBR' ) + : call stopit( status, 'error 24' ) + sff = ast_specfluxframe( sf, ff, ' ', status ) + + fs = ast_convert( sff, sff2, ' ', status ) + if( fs .eq. ast__null ) call stopit( status, 'error 25' ) + + yin = 1.0D0 + xin = 2.0D0 + call ast_tran2( fs, 1, xin, yin, .true., xout, yout, status ) + + if( abs(yout + 21.3585154D0 ) .gt. 0.000001 ) + : call stopit( status, 'error 26' ) + + if( xout .ne. 2.0D0 ) call stopit( status, 'error 27' ) + + call ast_tran2( fs, 1, xout, yout, .false., xin, yin, status ) + if( abs( xin - 2.0D0 ) .gt. 1.0D-9 ) call stopit( status, + : 'error 28' ) + if( abs( yin - 1.0D0 ) .gt. 1.0D-9 ) call stopit( status, + : 'error 29' ) + + + + + + + + + + + + + + + + + + if( status .eq. sai__ok ) then + write(*,*) 'All SpecFluxFrame tests passed' + else + write(*,*) 'SpecFluxFrame tests failed' + end if + + end + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + + subroutine checkdump( obj, text, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap + external mysource, mysink + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + ch = ast_channel( mysource, mysink, ' ', status ) + + + ll = 110 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + + + + if( ast_getd( obj, 'specval', status ) .ne. + : ast_getd( result, 'specval', status ) ) then + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + + end + + subroutine sink1( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + logical fsfound, done + common /sink1com/ fsfound, done + + integer status, l + character line*200 + + if( status .ne. sai__ok ) return + call ast_getline( line, l, status ) + + if( index( line( : l ),'Unc =' ) .GT. 0 ) then + done = .true. + + else if( .not. done .and. + : index( line( : l ),'FrameSet' ) .GT. 0 ) then + fsfound= .true. + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf*25000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 25000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + endif + + next = next + ll + + end + + diff --git a/ast_tester/testspecframe.f b/ast_tester/testspecframe.f new file mode 100644 index 0000000..490139a --- /dev/null +++ b/ast_tester/testspecframe.f @@ -0,0 +1,249 @@ + program testspecframe + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + double precision rf, x, y + integer status, sf, sf1, sf2, fs + status = sai__ok + + sf = ast_specframe( 'system=freq,unit=Hz', status ) + if( ast_GetD( sf, 'SpecOrigin', status ) .ne. 0.0 ) then + call stopit( status, 'Error 0' ) + end if + + rf = ast_GetD( sf, 'RestFreq', status ) + call ast_SetD( sf, 'SpecOrigin', rf*1.0D9, status ) + if( ast_GetD( sf, 'SpecOrigin', status ) .ne. rf*1.0D9 ) then + call stopit( status, 'Error 1' ) + end if + + call ast_setc( sf, 'Unit(1)', 'GHz', status ) + if( ast_GetD( sf, 'SpecOrigin', status ) .ne. rf ) then + call stopit( status, 'Error 2' ) + end if + + call checkdump( sf, 'Error 3', status ) + + call ast_setc( sf, 'System', 'vrad', status ) + if( abs( ast_GetD( sf, 'SpecOrigin', status ) ) .gt. 1.0D-8 ) then + write(*,*) ast_GetD( sf, 'SpecOrigin', status ) + call stopit( status, 'Error 4' ) + end if + + call ast_setc( sf, 'System', 'freq', status ) + call ast_setc( sf, 'Unit(1)', 'Hz', status ) + if( abs( ast_GetD( sf, 'SpecOrigin', status ) ) .ne. + : rf*1.0D9 ) then + write(*,*) ast_GetD( sf, 'SpecOrigin', status ) + call stopit( status, 'Error 5' ) + end if + + call ast_setc( sf, 'StdOfRest', 'LSRD', status ) + if( abs( ast_GetD( sf, 'SpecOrigin', status ) - + : rf*1.00000212890848D9 ) .gt. 10.0 ) then + write(*,*) ast_GetD( sf, 'SpecOrigin', status ) + write(*,*) 'Should be ',rf*1.00000212890848D9 + call stopit( status, 'Error 6' ) + end if + + + sf1 = ast_specframe( 'system=freq,unit=Hz', status ) + call ast_setd( sf1, 'SpecOrigin', 1.0D20, status ) + sf2 = ast_specframe( 'system=freq,unit=Hz', status ) + call ast_setd( sf2, 'SpecOrigin', 1.01D20, status ) + fs = ast_convert( sf1, sf2, "", status ); + + x = 0.03D20 + call ast_tran1( fs, 1, x, .true., y, status ) + if( abs( y - 0.02D20 ) .gt. 0.0 ) then + write(*,*) y, y - 0.02D20 + call stopit( status, 'Error 7' ) + end if + + if( ast_getl( sf1, 'AlignSpecOffset', status ) ) then + call stopit( status, 'Error 8' ) + end if + call ast_setl( sf1, 'AlignSpecOffset', .true., status ) + call ast_setl( sf2, 'AlignSpecOffset', .true., status ) + + fs = ast_convert( sf1, sf2, "", status ); + + x = 0.03D20 + call ast_tran1( fs, 1, x, .true., y, status ) + if( abs( y - x ) .gt. 0.0 ) then + write(*,*) y, y - x + call stopit( status, 'Error 9' ) + end if + + sf = ast_specframe( 'system=freq,unit=Hz', status ) + call ast_setc( sf, 'SourceVRF', 'LSRK', status ) + call ast_setd( sf, 'SourceVel', 1000.0D0, status ) + + call ast_setc( sf, 'SourceVRF', 'BARY', status ) + call ast_setc( sf, 'SourceSys', 'ZOPT', status ) + + if( abs( ast_getd( sf, 'SourceVel', status ) - + : 0.00334028336870307D0 ) .gt. 1.0D-10 ) then + write(*,*) ast_getd( sf, 'SourceVel', status ) + call stopit( status, 'Error 11' ) + end if + + call checkdump( sf, 'Error 10', status ) + call ast_setc( sf, 'SourceVRF', 'LSRK', status ) + call ast_setc( sf, 'SourceSys', 'VREL', status ) + + if( abs( ast_getd( sf, 'SourceVel', status ) - + : 1000.0D0 ) .gt. 1.0D-6 ) then + write(*,*) ast_getd( sf, 'SourceVel', status ) + call stopit( status, 'Error 12' ) + end if + + if( status .eq. sai__ok ) then + write(*,*) 'All SpecFrame tests passed' + else + write(*,*) 'SpecFrame tests failed' + end if + + end + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + + subroutine checkdump( obj, text, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap + external mysource, mysink + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + ch = ast_channel( mysource, mysink, ' ', status ) + + + ll = 110 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + + + + if( ast_getd( obj, 'specorigin', status ) .ne. + : ast_getd( result, 'specorigin', status ) ) then + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + + end + + subroutine sink1( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + logical fsfound, done + common /sink1com/ fsfound, done + + integer status, l + character line*200 + + if( status .ne. sai__ok ) return + call ast_getline( line, l, status ) + + if( index( line( : l ),'Unc =' ) .GT. 0 ) then + done = .true. + + else if( .not. done .and. + : index( line( : l ),'FrameSet' ) .GT. 0 ) then + fsfound= .true. + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf*25000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 25000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + endif + + next = next + ll + + end + + diff --git a/ast_tester/teststc.f b/ast_tester/teststc.f new file mode 100644 index 0000000..8e0fa4e --- /dev/null +++ b/ast_tester/teststc.f @@ -0,0 +1,1857 @@ + program teststc + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + + + integer status + + status = sai__ok + + +c call ast_SetWatchId( 565300 ) + + call ast_begin( status ) + call Example5( status ) + call Example1( status ) + call Example1b( status ) + call Example4( status ) + call misc( status ) + call Example3( status ) + call Example2( status ) + call ast_end( status ) + +c call ast_listissued( 'teststc' ) + + + if( status .eq. sai__ok ) then + write(*,*) 'All Stc tests passed' + else + write(*,*) 'Stc tests failed' + end if + + end + + + subroutine misc( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + + integer status, obj1, obj2, overlap + double precision x, y, xo, yo + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + + call puteg( 'teststc_eg6', 1, status ) + call xmlread( 1, obj1, ' ', status ) + obj1 = ast_simplify( obj1, status ) + call checkdump( obj1, 'checkdump 1', status ) + + call puteg( 'teststc_eg7', 1, status ) + call xmlread( 1, obj2, ' ', status ) + obj2 = ast_simplify( obj2, status ) + call checkdump( obj2, 'checkdump 2', status ) + + overlap = ast_overlap( obj1, obj2, status ) + if( overlap .ne. 4 .and.status .eq. sai__ok ) then + write(*,*) 'Overlap is ',overlap,' (should be 4)' + call stopit( status, 'Error 1' ) + endif + + call puteg( 'teststc_eg8', 1, status ) + call xmlread( 1, obj2, ' ', status ) + obj2 = ast_simplify( obj2, status ) + call checkdump( obj2, 'checkdump 3', status ) + + overlap = ast_overlap( obj1, obj2, status ) + if( overlap .ne. 3 .and.status .eq. sai__ok ) then + write(*,*) 'Overlap is ',overlap,' (should be 3)' + call stopit( status, 'Error 2' ) + endif + + overlap = ast_overlap( obj2, obj1, status ) + if( overlap .ne. 2 .and.status .eq. sai__ok ) then + write(*,*) 'Overlap is ',overlap,' (should be 2)' + call stopit( status, 'Error 3' ) + endif + + call puteg( 'teststc_eg9', 1, status ) + call xmlread( 1, obj2, ' ', status ) + + overlap = ast_overlap( obj1, obj2, status ) + if( overlap .ne. 1 .and.status .eq. sai__ok ) then + write(*,*) 'Overlap is ',overlap,' (should be 1)' + call stopit( status, 'Error 4' ) + endif + + + call puteg( 'teststc_eg10', 1, status ) + call xmlread( 1, obj2, ' ', status ) + + x = 2.4958208 + y = 0.73303829 + call ast_tran2( obj2, 1, x, y, .true., xo, yo, status ) + if( xo .ne. 2.4958208 .or. yo .ne. 0.73303829 ) then + call stopit( status, 'Error 5' ) + end if + + x = 2.4958208 + y = -0.73303829 + call ast_tran2( obj2, 1, x, y, .true., xo, yo, status ) + if( xo .ne. AST__BAD .or. yo .ne. AST__BAD ) then + call stopit( status, 'Error 6' ) + end if + + + + + call ast_end( status ) + + if( status .ne. sai__ok ) write(*,*) 'teststc: miscellaneous '// + : 'tests failed' + + end + + subroutine Example1( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + integer status, obj2, obj, i, j, unc, km, nval, fs, m, r, f, + : axes(2), map + double precision in(8,4), out(8,4), lbnd(5), ubnd(5) + character cvals(10)*30 + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + +* Put an example of an STCResourceProfile into file 1. + call puteg( 'teststc_eg1', 1, status ) + +* Use a new XmlChan to read an object from file 1,and simplify it. + call xmlread( 1, obj, ' ', status ) + obj = ast_simplify( obj, status ) + +* Write out the object through a Channel and read it back. + call checkdump( obj, 'checkdump 1', status ) + +* Test simplify by negating and simplifying twice. + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + +* Check it is a STCResourceProfile + if( .not. ast_isastcresourceprofile( obj, status ) ) + : call stopit( status, 'Error 1' ) + +* Check it contains an Interval. + if( .not. ast_isainterval( ast_getstcregion( obj, status ), + : status ) ) + : call stopit( status, 'Error 1a' ) + +* Timescale should be tt. Try changing it to TAI. + if( ast_getc( obj, 'timescale', status ) .ne. 'TT' ) + : call stopit( status, 'Error 0a' ) + + if( abs( ast_getd(obj,'TimeOrigin',status)-51382.6666666D0 ) + : .gt. 1.0D-7 ) call stopit( status, 'Error 0b' ) + + call ast_getregionbounds( obj, lbnd, ubnd, status ) + if( lbnd(3) .ne. 0.0 ) call stopit( status, 'Error 0c' ) + + call ast_set( obj, 'timescale=tai', status ) + if( ast_getc( obj, 'timescale', status ) .ne. 'TAI' ) + : call stopit( status, 'Error 0d' ) + + if( abs( ast_getd(obj,'TimeOrigin',status)-51382.6662941667D0 ) + : .gt. 1.0D-7 ) call stopit( status, 'Error 0e' ) + + call ast_getregionbounds( obj, lbnd, ubnd, status ) + if( abs( lbnd(3) ) .gt. 1.0D-6 ) THEN + write(*,*) lbnd(3) + call stopit( status, 'Error 0f' ) + END IF + + + call ast_set( obj, 'timescale=tt', status ) + obj = ast_Simplify( obj, status ) + if( ast_getc( obj, 'timescale', status ) .ne. 'TT' ) + : call stopit( status, 'Error 0g' ) + + if( abs( ast_getd(obj,'TimeOrigin',status)-51382.6666666D0 ) + : .gt. 1.0D-7 ) call stopit( status, 'Error 0h' ) + + call ast_getregionbounds( obj, lbnd, ubnd, status ) + if( abs( lbnd(3) ) .gt. 1.0D-6 ) + : call stopit( status, 'Error 0i' ) + + + +* Other tests + if( ast_getd( obj, 'fillfactor', status ) .ne. 0.02D0 ) + : call stopit( status, 'Error 1b' ) + + if( ast_getc( obj, 'ident', status ) .ne. 'AllSky-CXO' ) + : call stopit( status, 'Error 1c' ) + + if( ast_getc( obj, 'domain(3)', status ) .ne. 'TIME' ) + : call stopit( status, 'Error 2' ) + + if( ast_getc( obj, 'title(3)', status ) .ne. 'Time' ) + : call stopit( status, 'Error 2a' ) + + if( ast_getc( obj, 'label(3)', status ) .ne. + : 'Modified Julian Date offset from 1999-07-23 16:00:00' ) THEN + call stopit( status, 'Error 2b' ) + end if + + if( ast_getc( obj, 'domain(1)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 3' ) + + if( ast_getc( obj, 'system(1)', status ) .ne. 'ICRS' ) + : call stopit( status, 'Error 3a' ) + + if( ast_getc( obj, 'label(1)', status ) .ne. 'Right ascension' ) + : call stopit( status, 'Error 3b' ) + + if( ast_getc( obj, 'label(2)', status ) .ne. 'Declination' ) + : call stopit( status, 'Error 3c' ) + + if( ast_getc( obj, 'title(2)', status ) .ne. 'Space' ) + : call stopit( status, 'Error 3d' ) + + if( ast_getc( obj, 'domain(2)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 4' ) + + if( ast_getc( obj, 'domain(4)', status ) .ne. 'SPECTRUM' ) + : call stopit( status, 'Error 5' ) + + if( ast_getc( obj, 'system(4)', status ) .ne. 'ENER' ) + : call stopit( status, 'Error 5a' ) + + if( ast_getc( obj, 'stdofrest', status ) .ne. 'Topocentric' ) + : call stopit( status, 'Error 5b' ) + + if( ast_getc( obj, 'title(4)', status ) .ne. + : 'Energy (Topocentric)' ) call stopit( status, 'Error 5c' ) + + if( ast_getc( obj, 'unit(4)', status ) .ne. 'keV' ) + : call stopit( status, 'Error 5d' ) + + if( ast_geti( obj, 'naxes', status ) .ne. 4 ) + : call stopit( status, 'Error 6' ) + + in(1,1) = 10.0 + in(1,2) = 10.0 + in(1,3) = -0.1 + in(1,4) = 0.11 + + in(2,1) = -10.0 + in(2,2) = 10.0 + in(2,3) = 0.1 + in(2,4) = 0.11 + + in(3,1) = 0.0 ! inside + in(3,2) = 0.0 + in(3,3) = 100.0 + in(3,4) = 0.13 + + in(4,1) = -1.0 + in(4,2) = 1.0 + in(4,3) = -100.0 + in(4,4) = 0.13 + + in(5,1) = 10.0 + in(5,2) = 10.0 + in(5,3) = -1000.0 + in(5,4) = 9.9 + + in(6,1) = -10.0 ! inside + in(6,2) = 10.0 + in(6,3) = 1000.0 + in(6,4) = 9.9 + + in(7,1) = 0.0 + in(7,2) = 0.0 + in(7,3) = 10.0 + in(7,4) = 10.1 + + in(8,1) = -1.0 + in(8,2) = 1.0 + in(8,3) = -10.0 + in(8,4) = 10.1 + + call ast_trann( obj, 8, 4, 8, in, .true., 4, 8, out, status ) + + do i = 1, 8 + if( i .eq. 3 .or. i .eq. 6 ) then + do j = 1, 4 + if( out(i,j) .ne. in(i,j) ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 7' ) + end if + end if + end do + else + do j = 1, 4 + if( out(i,j) .ne. AST__BAD ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 8' ) + end if + end if + end do + end if + end do + +* AstroCoords + if( ast_getstcncoord( obj, status ) .ne. 1 ) then + call stopit( status, 'Error 25' ) + end if + km = ast_getstccoord( obj, 1, status ) + + if( ast_mapsize( km, status ) .ne. 4 ) then + call stopit( status, 'Error 25b' ) + endif + + if( .not. ast_mapget0A( km, AST__STCERROR, r, status ) ) then + call stopit( status, 'Error 26' ) + else if( .not.ast_isabox( r, status ) ) then + call stopit( status, 'Error 27' ) + else if( ast_geti( r, 'naxes', status ) .ne. 4 ) then + call stopit( status, 'Error 28' ) + else + fs = ast_convert( obj, r, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'Error 29' ) + else + m = ast_getMapping( fs, AST__BASE, AST__CURRENT, status ) + m = ast_simplify( m, status ) + if( .not. ast_isaunitmap( m, status ) ) then + call stopit( status, 'Error 30' ) + endif + end if + + + call ast_getregionbounds( r, lbnd, ubnd, status ) + + if( abs( lbnd(1)+2.42406841E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 31a' ) + if( abs( ubnd(1)-2.42406841E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 31b' ) + if( abs( lbnd(2)+2.42406841E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 31c' ) + if( abs( ubnd(2)-2.42406841E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 31d' ) + if( abs( 0.5*(ubnd(3)+lbnd(3)) ) .gt. 1.0E-10) + : call stopit( status, 'Error 31e' ) + if( abs( 0.5*(ubnd(3)-lbnd(3))-0.578703703718D-09 ) .gt. + : 1.0E-15 ) call stopit( status, 'Error 31e2' ) + if( abs( lbnd(4)-5.01 ) .gt. 0.00001 ) + : call stopit( status, 'Error 31g' ) + if( abs( ubnd(4)-5.11 ) .gt. 0.00001 ) + : call stopit( status, 'Error 31h' ) + + end if + + + if( .not. ast_mapget1C( km, AST__STCNAME, 6, nval, cvals, + : status ) ) then + call stopit( status, 'Error 32' ) + + else if( nval .ne. 4 ) then + call stopit( status, 'Error 33' ) + else + if( cvals(1) .ne. 'Position' ) + : call stopit( status, 'Error 34a' ) + if( cvals(2) .ne. 'Position' ) + : call stopit( status, 'Error 34b' ) + if( cvals(3) .ne. 'Time' ) + : call stopit( status, 'Error 34c' ) + if( cvals(4) .ne. 'Energy' ) + : call stopit( status, 'Error 34d' ) + end if + + if( .not. ast_mapget0A( km, AST__STCRES, r, status ) ) then + call stopit( status, 'Error 35' ) + else if( .not.ast_isabox( r, status ) ) then + call stopit( status, 'Error 36' ) + else if( ast_geti( r, 'naxes', status ) .ne. 4 ) then + call stopit( status, 'Error 37' ) + else + fs = ast_convert( obj, r, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'Error 38' ) + else + m = ast_getMapping( fs, AST__BASE, AST__CURRENT, status ) + m = ast_simplify( m, status ) + if( .not. ast_isaunitmap( m, status ) ) then + call stopit( status, 'Error 39' ) + endif + end if + + call ast_getregionbounds( r, lbnd, ubnd, status ) + + if( abs( lbnd(1)+1.2120342E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 40a' ) + if( abs( ubnd(1)-1.2120342E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 40b' ) + if( abs( lbnd(2)+1.2120342E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 40c' ) + if( abs( ubnd(2)-1.2120342E-06 ) .gt. 0.0001E-6 ) + : call stopit( status, 'Error 40d' ) + + if( abs( 86400.0D0*(ubnd(3)-lbnd(3))-1.6D-5 ) .gt. 1.0E-10 ) + : call stopit( status, 'Error 40e' ) + if( abs( 0.5*(ubnd(3)+lbnd(3)) ) .gt. 1.0E-10 ) + : call stopit( status, 'Error 40f' ) + if( abs( lbnd(4)-5.05 ) .gt. 0.00001 ) + : call stopit( status, 'Error 40g' ) + if( abs( ubnd(4)-5.07 ) .gt. 0.00001 ) + : call stopit( status, 'Error 40h' ) + + end if + + if( .not. ast_mapget0A( km, AST__STCSIZE, r, status ) ) then + call stopit( status, 'Error 41' ) + else if( .not.ast_isabox( r, status ) ) then + call stopit( status, 'Error 42' ) + else if( ast_geti( r, 'naxes', status ) .ne. 4 ) then + call stopit( status, 'Error 43' ) + else + fs = ast_convert( obj, r, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'Error 44' ) + else + m = ast_getMapping( fs, AST__BASE, AST__CURRENT, status ) + m = ast_simplify( m, status ) + if( .not. ast_isaunitmap( m, status ) ) then + call stopit( status, 'Error 45' ) + endif + end if + + call ast_getregionbounds( r, lbnd, ubnd, status ) + + if( abs( lbnd(1)+0.00242406841 ) .gt. 0.01E-6 ) + : call stopit( status, 'Error 46a' ) + if( abs( ubnd(1)-0.00242406841 ) .gt. 0.01E-6 ) + : call stopit( status, 'Error 46b' ) + if( abs( lbnd(2)+0.00242406841 ) .gt. 0.01E-6 ) + : call stopit( status, 'Error 46c' ) + if( abs( ubnd(2)-0.00242406841 ) .gt. 0.01E-6 ) + : call stopit( status, 'Error 46d' ) + if( abs( 86400.0D0*(ubnd(3)-lbnd(3))- 1000.0 ) .gt. 1.0E-10 ) + : call stopit( status, 'Error 46e' ) + if( abs( 0.5*(ubnd(3)+lbnd(3)) ) .gt. 1.0E-10 ) + : call stopit( status, 'Error 46f' ) + if( abs( lbnd(4)-4.06 ) .gt. 0.001 ) + : call stopit( status, 'Error 46g' ) + if( abs( ubnd(4)-6.06 ) .gt. 0.001 ) + : call stopit( status, 'Error 46h' ) + + end if + + + + + + + obj2 = ast_Copy( obj, status ) + + call ast_setl( obj2, 'Adaptive', .false., status ) + call ast_setc( obj2, 'epoch', '2005', status ) + call ast_clear( obj2, 'Adaptive', status ) + + call ast_setc( obj2, 'system(1)', 'galactic', status ) + + if( ast_getstcncoord( obj2, status ) .ne. 1 ) then + call stopit( status, 'Error 25b' ) + end if + km = ast_getstccoord( obj2, 1, status ) + + if( ast_mapsize( km, status ) .ne. 3 ) then + call stopit( status, 'Error 25bb' ) + end if + + if( .not. ast_mapget0A( km, AST__STCERROR, r, status ) ) then + call stopit( status, 'Error 26b' ) + else if( .not.ast_isaprism( r, status ) ) then + call stopit( status, 'Error 27b' ) + else if( ast_geti( r, 'naxes', status ) .ne. 4 ) then + call stopit( status, 'Error 28b' ) + else + fs = ast_convert( obj2, r, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'Error 29b' ) + else + m = ast_getMapping( fs, AST__BASE, AST__CURRENT, status ) + m = ast_simplify( m, status ) + if( .not. ast_isaunitmap( m, status ) ) then + call stopit( status, 'Error 30b' ) + endif + end if + + call ast_getregionbounds( r, lbnd, ubnd, status ) + + if( abs( lbnd(1)-1.68139639 ) .gt. 1.0E-7 ) + : call stopit( status, 'Error 31ab' ) + if( abs( ubnd(1)-1.68140922 ) .gt. 1.0E-7 ) + : call stopit( status, 'Error 31bb' ) + if( abs( lbnd(2)+1.05049161 ) .gt. 1.0E-7 ) + : call stopit( status, 'Error 31cb' ) + if( abs( ubnd(2)+1.05048523 ) .gt. 1.0E-7 ) + : call stopit( status, 'Error 31db' ) + if( abs( 0.5*86400.0D0*(ubnd(3)-lbnd(3))- 5.0D-5) .gt. 1.0E-10) + : call stopit( status, 'Error 31eb' ) + if( abs( 0.5*86400.0D0*(ubnd(3)+lbnd(3))) .gt. 1.0E-10 ) + : call stopit( status, 'Error 31fb' ) + if( abs( lbnd(4)-5.01 ) .gt. 0.000001 ) + : call stopit( status, 'Error 31gb' ) + if( abs( ubnd(4)-5.11 ) .gt. 0.000001 ) + : call stopit( status, 'Error 31hb' ) + + end if + + + if( ast_mapget1C( km, AST__STCNAME, 6, nval, cvals, status )) + : call stopit( status, 'Error 32b' ) + + +* Uncertainty tests + + unc = ast_getunc( obj, .true., status ) + + if( unc .eq. AST__NULL ) call stopit( status, 'Error 9' ) + if( ast_getunc( unc, .false., status ) .ne. AST__NULL ) + : call stopit( status, 'Error 9a' ) + + call ast_getregionbounds( unc, lbnd, ubnd, status ) + + + if( abs( lbnd(1) + 2.42406841E-06 ) .gt. 0.0000001E-06 ) + : call stopit( status, 'Error 10' ) + + if( abs( lbnd(2) + 2.42406841E-06 ) .gt. 0.0000001E-06 ) + : call stopit( status, 'Error 11' ) + if( abs( 86400.0D0*lbnd(3) + 5.0D-5 ) .gt. 0.1E-10 ) + : call stopit( status, 'Error 12' ) + if( abs( lbnd(4) - 0.07 ) .gt. 0.0001 ) + : call stopit( status, 'Error 13' ) + if( abs( ubnd(1) - 2.42406841E-06 ) .gt. 0.0000001E-06 ) + : call stopit( status, 'Error 14' ) + if( abs( ubnd(2) - 2.42406841E-06 ) .gt. 0.0000001E-06 ) + : call stopit( status, 'Error 15' ) + if( abs( 86400.0D0*ubnd(3) - 5.0D-5 ) .gt. 0.1E-10 ) + : call stopit( status, 'Error 16' ) + if( abs( ubnd(4) - 0.17 ) .gt. 0.0001 ) + : call stopit( status, 'Error 17' ) + + +* UseDefs tests. +c if( status .eq. SAI__OK ) then +c obj2 = ast_copy( obj, status ) +c call err_begin( status ) +c call ast_set( obj2, 'System=FK4', status ) +c if( status .ne. AST__NOVAL ) then +c write(*,*) 'status is ',status,': should be ',AST__NOVAL +c if( status .ne. sai__ok ) call err_annul( status ) +c call stopit( status, 'Error 18' ) +c else +c call err_annul( status ) +c end if +c call err_end( status ) +c call ast_annul( obj2, status ) +c end if +c +c if( status .eq. SAI__OK ) then +c obj2 = ast_copy( obj, status ) +c call err_begin( status ) +c call ast_set( obj2, 'System=velo', status ) +c if( status .ne. AST__NOVAL ) then +c write(*,*) 'status is ',status,': should be ',AST__NOVAL +c if( status .ne. sai__ok ) call err_annul( status ) +c call stopit( status, 'Error 19' ) +c else +c call err_annul( status ) +c end if +c call err_end( status ) +c call ast_annul( obj2, status ) +c end if + + call ast_set( obj, 'Unit(4)=J', status ) + + if( status .ne. SAI__OK ) call stopit( status, 'Error 20' ) + +* Tests on reference values + if( ast_test( obj, 'RefRA(4)', status ) ) then + call stopit( status, 'Error 21' ) + end if + + if( ast_test( obj, 'RefDec(4)', status ) ) then + call stopit( status, 'Error 22' ) + end if + +c if( ast_test( obj, 'Epoch(4)', status ) ) then +c call stopit( status, 'Error 22' ) +c end if + +c if( ast_test( obj, 'Epoch(1)', status ) ) then +c call stopit( status, 'Error 23' ) +c end if + +c if( ast_test( obj, 'Epoch(2)', status ) ) then +c call stopit( status, 'Error 24' ) +c end if + + + + + call ast_end( status ) + + if( status .ne. sai__ok ) write(*,*) 'teststc: example 1 '// + : 'tests failed' + + end + + + subroutine Example1b( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + include 'teststc_com' + + integer status, obj, i, l + character value*200 + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + +* Test the Strict attribute. + call puteg( 'teststc_eg1', 1, status ) + + call err_mark + call xmlread( 1, obj, 'Strict=1', status ) + + if( status .ne. ast__badin ) then + if( status .ne. sai__ok ) call err_flush( status ) + call stopit( status, 'Error 1' ) + else + call err_annul( status ) + end if + call err_rlse + +* Test the ast_warnings function. + call xmlread( 1, obj, 'Strict=0', status ) + if( warns .EQ. AST__NULL ) then + call stopit( status, 'Error 2' ) + + else if( ast_mapsize( warns, status ) .ne. 5 ) then + call stopit( status, 'Error 3' ) + + else if( .not. ast_mapget0c( warns, 'Warning_1', value, l, + : status ) ) then + call stopit( status, 'Error 4' ) + + else if( value(:l) .ne. 'astRead(XmlChan): Warning whilst '// + : 'reading a Position2D element: contains more than '// + : 'one element. AST can only use the first' ) then + call stopit( status, 'Error 5' ) + end if + + + call ast_end( status ) + + if( status .ne. sai__ok ) write(*,*) 'teststc: example 1b '// + : 'tests failed' + + end + + + subroutine Example2( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, obj, i, j + double precision in(12,5), out(12,5) + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + +* Put an example of a CatalogEntryLocation into file 1. + call puteg( 'teststc_eg2', 1, status ) + +* Use a new XmlChan to read an object from file 1,and simplify it. + call xmlread( 1, obj, ' ', status ) + obj = ast_simplify( obj, status ) + +* Test simplify by negating and simplifying twice. + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call checkdump( obj, 'checkdump 1', status ) + +* Check it is a StcCatalogEntryLocation + if( .not. ast_isastcCatalogEntryLocation( obj, status ) ) + : call stopit( status, 'Error 1' ) + +* Check it is an Interval. + if( .not. ast_isainterval( ast_getstcregion( obj, status ), + : status ) ) + : call stopit( status, 'Error 1a' ) + +* Check it has no uncertainty + if( ast_getunc( obj, .false., status ) .NE. AST__NULL ) + : call stopit( status, 'Error 1b' ) + +* Other tests + if( ast_geti( obj, 'naxes', status ) .ne. 5 ) + : call stopit( status, 'Error 1ab' ) + + if( ast_getd( obj, 'fillfactor', status ) .ne. 1.0D0 ) + : call stopit( status, 'Error 1b' ) + + if( ast_getc( obj, 'ident', status ) .ne. 'RA6-18hDec20-70deg' ) + : call stopit( status, 'Error 1c' ) + + if( ast_getc( obj, 'domain(3)', status ) .ne. 'TIME' ) + : call stopit( status, 'Error 2' ) + + if( ast_getc( obj, 'title(3)', status ) .ne. + : 'Julian Date [TT] offset from 1968-05-23 12:00:00' ) + : call stopit( status, 'Error 2a' ) + + if( ast_getc( obj, 'label(3)', status ) .ne. + : 'Julian Date offset from 1968-05-23 12:00:00' ) THEN + call stopit( status, 'Error 2b' ) + endif + + if( ast_getc( obj, 'domain(1)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 3' ) + + if( ast_getc( obj, 'system(1)', status ) .ne. 'FK4' ) + : call stopit( status, 'Error 3a' ) + + if( ast_getc( obj, 'label(1)', status ) .ne. 'Right ascension' ) + : call stopit( status, 'Error 3b' ) + + if( ast_getc( obj, 'label(2)', status ) .ne. 'Declination' ) + : call stopit( status, 'Error 3c' ) + + if( ast_getc( obj, 'title(2)', status ) .ne. 'PosEq' ) + : call stopit( status, 'Error 3d' ) + + if( ast_getd( obj, 'Equinox', status ) .ne. 1950D0 ) + : call stopit( status, 'Error 3d' ) + + if( ast_getc( obj, 'domain(2)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 4' ) + + if( ast_getc( obj, 'domain(4)', status ) .ne. 'SPECTRUM' ) + : call stopit( status, 'Error 5' ) + + if( ast_getc( obj, 'system(4)', status ) .ne. 'WAVE' ) + : call stopit( status, 'Error 5a' ) + + if( ast_getc( obj, 'stdofrest', status ) .ne. 'Topocentric' ) + : call stopit( status, 'Error 5b' ) + + if( ast_test( obj, 'title(4)', status ) ) + : call stopit( status, 'Error 5c' ) + + if( ast_geti( obj, 'naxes', status ) .ne. 5 ) + : call stopit( status, 'Error 6' ) + + if( ast_getc( obj, 'domain(5)', status ) .ne. 'REDSHIFT' ) + : call stopit( status, 'Error 6a' ) + + if( ast_getc( obj, 'system(5)', status ) .ne. 'VOPT' ) + : call stopit( status, 'Error 6b' ) + + if( ast_getc( obj, 'label(5)', status ) .ne. 'Optical velocity' ) + : call stopit( status, 'Error 6c' ) + + if( ast_getc( obj, 'unit(5)', status ) .ne. 'km/s' ) + : call stopit( status, 'Error 6d' ) + + if( ast_getc( obj, 'unit(4)', status ) .ne. 'Angstrom' ) + : call stopit( status, 'Error 6e' ) + + + in(1,1) = 4.71238 ! inside + in(1,2) = 1.2216 + in(1,3) = 1 + in(1,4) = 6499.9 + in(1,5) = 9999.9 + + in(2,1) = 4.71240 ! outside + in(2,2) = 1.2216 + in(2,3) = 1 + in(2,4) = 6499.9 + in(2,5) = 9999.9 + + in(3,1) = 4.71238 ! outside + in(3,2) = 1.2218 + in(3,3) = 1 + in(3,4) = 6499.9 + in(3,5) = 9999.9 + + in(4,1) = 4.71238 ! outside + in(4,2) = 1.2216 + in(4,3) = -0.6 + in(4,4) = 6499.9 + in(4,5) = 9999.9 + + in(5,1) = 4.71238 ! outside + in(5,2) = 1.2216 + in(5,3) = 1 + in(5,4) = 6500.1 + in(5,5) = 9999.9 + + in(6,1) = 4.71238 ! outside + in(6,2) = 1.2216 + in(6,3) = 1 + in(6,4) = 6499.9 + in(6,5) = 10000.1 + + in(7,1) = 1.5709 ! inside + in(7,2) = 0.3492 + in(7,3) = 999.6 + in(7,4) = 5000.1 + in(7,5) = 5000 + + in(8,1) = 1.5707 ! outside + in(8,2) = 0.3492 + in(8,3) = 999.6 + in(8,4) = 5000.1 + in(8,5) = 5000 + + in(9,1) = 1.5709 ! outside + in(9,2) = 0.3490 + in(9,3) = 999.6 + in(9,4) = 5000.1 + in(9,5) = 5000 + + in(10,1) = 1.5709 ! outside + in(10,2) = 0.3492 + in(10,3) = 1000.4 + in(10,4) = 5000.1 + in(10,5) = 5000 + + in(11,1) = 1.5709 ! outside + in(11,2) = 0.3492 + in(11,3) = 999.6 + in(11,4) = 4999.9 + in(11,5) = 5000 + + in(12,1) = 1.5709 ! inside + in(12,2) = 0.3492 + in(12,3) = 999.6 + in(12,4) = 5000.1 + in(12,5) = 1000 + + call ast_trann( obj, 12, 5, 12, in, .true., 5, 12, out, status ) + + do i = 1, 12 + if( i .eq. 1 .or. i .eq. 7 .or. i .eq. 12 ) then ! inside points + do j = 1, 5 + if( out(i,j) .ne. in(i,j) ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 7' ) + end if + end if + end do + else ! outside points + do j = 1, 5 + if( out(i,j) .ne. AST__BAD ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 8' ) + end if + end if + end do + end if + end do + +* Tests on reference values + if( ast_test( obj, 'RefRA(4)', status ) ) then + call stopit( status, 'Error 9' ) + end if + + if( ast_test( obj, 'RefDec(4)', status ) ) then + call stopit( status, 'Error 10' ) + end if + +c if( ast_test( obj, 'Epoch(4)', status ) ) then +c call stopit( status, 'Error 11' ) +c end if + +c if( ast_test( obj, 'Epoch(1)', status ) ) then +c call stopit( status, 'Error 12' ) +c end if + +c if( ast_test( obj, 'Epoch(2)', status ) ) then +c call stopit( status, 'Error 13' ) +c end if + + if( ast_test( obj, 'RestFreq(5)', status ) ) then + call stopit( status, 'Error 14' ) + end if + + + + + + + call ast_end( status ) + + if( status .ne. sai__ok ) write(*,*) 'teststc: example 2 '// + : 'tests failed' + + end + + + + + + + + subroutine Example3( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, obj, i, j + double precision in(12,5), out(12,5) + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + +* Put an example of a CatalogEntryLocation into file 1. + call puteg( 'teststc_eg3', 1, status ) + +* Use a new XmlChan to read an object from file 1,and simplify it. + call xmlread( 1, obj, ' ', status ) + obj = ast_simplify( obj, status ) + +* Test simplify by negating and simplifying twice. + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call checkdump( obj, 'checkdump 1', status ) + +* Check it is a StcCatalogEntryLocation + if( .not. ast_isastcCatalogEntryLocation( obj, status ) ) + : call stopit( status, 'Error 1' ) + +* Check it is an Interval. + if( .not. ast_isainterval( ast_getstcregion( obj, status ), + : status ) ) then + write(*,*) ast_GetC( ast_getstcregion( obj, status ), 'Class', + : status ) + call stopit( status, 'Error 1a' ) + end if + +* Check it has no uncertainty + if( ast_getunc( obj, .false., status ) .NE. AST__NULL ) + : call stopit( status, 'Error 1b' ) + +* Check it has 5 axes. + if( ast_geti( obj, 'naxes', status ) .ne. 5 ) + : call stopit( status, 'Error 1ab' ) + +* Check the rest frequency for axis 5 (redshift) is 5000 Angstrom + if( abs( ast_getd( obj, 'restfreq(5)', status ) - 599584.916D0 ) + : .gt. 0.001D0 ) call stopit( status, 'Error A1' ) + +* Check the epoch for allaxes is JD 2440000 + if( abs( ast_getd( obj, 'epoch(1)', status ) - 1968.39212D0 ) .gt. + : 0.00001D0 ) call stopit( status, 'Error B1' ) + if( abs( ast_getd( obj, 'epoch(2)', status ) - 1968.39212D0 ) .gt. + : 0.00001D0 ) call stopit( status, 'Error B2' ) + if( abs( ast_getd( obj, 'epoch(3)', status ) - 1968.39212D0 ) .gt. + : 0.00001D0 ) call stopit( status, 'Error B3' ) + if( abs( ast_getd( obj, 'epoch(4)', status ) - 1968.39212D0 ) .gt. + : 0.00001D0 ) call stopit( status, 'Error B4' ) + if( abs( ast_getd( obj, 'epoch(5)', status ) - 1968.39212D0 ) .gt. + : 0.00001D0 ) call stopit( status, 'Error B5' ) + +* Other tests + if( ast_getd( obj, 'fillfactor', status ) .ne. 1.0D0 ) + : call stopit( status, 'Error 1b' ) + + if( ast_getc( obj, 'ident', status ) .ne. 'RA6-18hDec20-70deg' ) + : call stopit( status, 'Error 1c' ) + + if( ast_getc( obj, 'domain(3)', status ) .ne. 'TIME' ) + : call stopit( status, 'Error 2' ) + + if( ast_getc( obj, 'label(3)', status ) .ne. + : 'Julian Date offset from 1968-05-23 12:00:00' ) THEN + call stopit( status, 'Error 2b' ) + end if + + if( ast_getc( obj, 'domain(1)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 3' ) + + if( ast_getc( obj, 'system(1)', status ) .ne. 'FK4' ) + : call stopit( status, 'Error 3a' ) + + if( ast_getc( obj, 'label(1)', status ) .ne. 'Right ascension' ) + : call stopit( status, 'Error 3b' ) + + if( ast_getc( obj, 'label(2)', status ) .ne. 'Declination' ) + : call stopit( status, 'Error 3c' ) + + if( ast_getc( obj, 'title(2)', status ) .ne. 'PosEq' ) + : call stopit( status, 'Error 3d' ) + + if( ast_getd( obj, 'Equinox', status ) .ne. 1950D0 ) + : call stopit( status, 'Error 3d' ) + + if( ast_getc( obj, 'domain(2)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 4' ) + + if( ast_getc( obj, 'domain(4)', status ) .ne. 'SPECTRUM' ) + : call stopit( status, 'Error 5' ) + + if( ast_getc( obj, 'system(4)', status ) .ne. 'WAVE' ) + : call stopit( status, 'Error 5a' ) + + if( ast_getc( obj, 'stdofrest', status ) .ne. 'Topocentric' ) + : call stopit( status, 'Error 5b' ) + + if( ast_test( obj, 'title(4)', status ) ) + : call stopit( status, 'Error 5c' ) + + if( ast_geti( obj, 'naxes', status ) .ne. 5 ) + : call stopit( status, 'Error 6' ) + + if( ast_getc( obj, 'domain(5)', status ) .ne. 'REDSHIFT' ) + : call stopit( status, 'Error 6a' ) + + if( ast_getc( obj, 'system(5)', status ) .ne. 'VOPT' ) + : call stopit( status, 'Error 6b' ) + + if( ast_getc( obj, 'label(5)', status ) .ne. 'Optical velocity' ) + : call stopit( status, 'Error 6c' ) + + if( ast_getc( obj, 'unit(5)', status ) .ne. 'km/s' ) + : call stopit( status, 'Error 6d' ) + + if( ast_getc( obj, 'unit(4)', status ) .ne. 'Angstrom' ) + : call stopit( status, 'Error 6e' ) + + + in(1,1) = 4.71238 ! inside + in(1,2) = 1.2216 + in(1,3) = 0.0D0 + in(1,4) = 5000 + in(1,5) = 9999.9 + + in(2,1) = 4.71240 ! outside + in(2,2) = 1.2216 + in(2,3) = 0.0D0 + in(2,4) = 5000 + in(2,5) = 9999.9 + + in(3,1) = 4.71238 ! outside + in(3,2) = 1.2218 + in(3,3) = 0.0D0 + in(3,4) = 5000 + in(3,5) = 9999.9 + + in(4,1) = 4.71238 ! outside + in(4,2) = 1.2216 + in(4,3) = 0.5D0 + in(4,4) = 5000 + in(4,5) = 9999.9 + + in(5,1) = 4.71238 ! outside + in(5,2) = 1.2216 + in(5,3) = 0.0D0 + in(5,4) = 6500.1 + in(5,5) = 9999.9 + + in(6,1) = 4.71238 ! outside + in(6,2) = 1.2216 + in(6,3) = 0.0D0 + in(6,4) = 5000 + in(6,5) = 10000.1 + + in(7,1) = 1.5709 ! inside + in(7,2) = 0.3492 + in(7,3) = 0.0D0 + in(7,4) = 5000 + in(7,5) = 5000 + + in(8,1) = 1.5707 ! outside + in(8,2) = 0.3492 + in(8,3) = 0.0D0 + in(8,4) = 5000 + in(8,5) = 5000 + + in(9,1) = 1.5709 ! outside + in(9,2) = 0.3490 + in(9,3) = 0.0D0 + in(9,4) = 5000 + in(9,5) = 5000 + + in(10,1) = 1.5709 ! outside + in(10,2) = 0.3492 + in(10,3) = 39999.4D0 + in(10,4) = 5000 + in(10,5) = 5000 + + in(11,1) = 1.5709 ! outside + in(11,2) = 0.3492 + in(11,3) = 0.0D0 + in(11,4) = 4999.9 + in(11,5) = 5000 + + in(12,1) = 1.5709 ! inside + in(12,2) = 0.3492 + in(12,3) = 0.0D0 + in(12,4) = 5000 + in(12,5) = 1000 + + call ast_trann( obj, 12, 5, 12, in, .true., 5, 12, out, status ) + + do i = 1, 12 + if( i .eq. 1 .or. i .eq. 7 .or. i .eq. 12 ) then ! inside points + do j = 1, 5 + if( out(i,j) .ne. in(i,j) ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 7' ) + end if + end if + end do + else ! outside points + do j = 1, 5 + if( out(i,j) .ne. AST__BAD ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 8' ) + end if + end if + end do + end if + end do + +* Tests on reference values + if( ast_test( obj, 'RefRA(4)', status ) ) then + call stopit( status, 'Error 9' ) + end if + + if( ast_test( obj, 'RefDec(4)', status ) ) then + call stopit( status, 'Error 10' ) + end if + + if( .not. ast_test( obj, 'Epoch(4)', status ) ) then + call stopit( status, 'Error 11' ) + end if + + if( .not. ast_test( obj, 'Epoch(1)', status ) ) then + call stopit( status, 'Error 12' ) + end if + + if( .not. ast_test( obj, 'Epoch(2)', status ) ) then + call stopit( status, 'Error 13' ) + end if + + if( .not. ast_test( obj, 'RestFreq(5)', status ) ) then + call stopit( status, 'Error 14' ) + end if + + + + + + + call ast_end( status ) + + if( status .ne. sai__ok ) write(*,*) 'teststc: example 3 '// + : 'tests failed' + + end + + + + + + + subroutine Example4( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + integer status, obj2, obj, i, j, unc, frm + double precision in(12,4), out(12,4), lbnd(4), ubnd(4) + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + +* Put an example of an STCSearchLocation into file 1. + call puteg( 'teststc_eg4', 1, status ) + +* Use a new XmlChan to read an object from file 1,and simplify it. + call xmlread( 1, obj, ' ', status ) + obj = ast_simplify( obj, status ) + +* Test simplify by negating and simplifying twice. + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call checkdump( obj, 'checkdump 1', status ) + + +* Check it is a STCSearchLocation + if( .not. ast_isastcsearchlocation( obj, status ) ) + : call stopit( status, 'Error 1' ) + +* Check it is a Prism. + if( .not. ast_isaprism( ast_getstcregion( obj, status ), + : status ) ) + : call stopit( status, 'Error 1a' ) + +* Check it has no uncertainty + if( ast_getunc( obj, .false., status ) .NE. AST__NULL ) + : call stopit( status, 'Error 1b' ) + +* Other tests + if( ast_geti( obj, 'naxes', status ) .ne. 4 ) + : call stopit( status, 'Error 1ab' ) + + if( ast_getd( obj, 'fillfactor', status ) .ne. 1.0D0 ) + : call stopit( status, 'Error 1b' ) + + if( ast_getc( obj, 'ident', status ) .ne. 'M81' ) + : call stopit( status, 'Error 1c' ) + + if( ast_getc( obj, 'domain(3)', status ) .ne. 'TIME' ) + : call stopit( status, 'Error 2' ) + + if( ast_getc( obj, 'label(3)', status ) .ne. + : 'Modified Julian Date offset from 1900-01-01' ) THEN + call stopit( status, 'Error 2b' ) + end if + + if( ast_getc( obj, 'domain(1)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 3' ) + + if( ast_getc( obj, 'system(1)', status ) .ne. 'ICRS' ) + : call stopit( status, 'Error 3a' ) + + if( ast_getc( obj, 'label(1)', status ) .ne. 'Right ascension' ) + : call stopit( status, 'Error 3b' ) + + if( ast_getc( obj, 'label(2)', status ) .ne. 'Declination' ) + : call stopit( status, 'Error 3c' ) + + if( ast_getc( obj, 'title(2)', status ) .ne. 'Equatorial' ) + : call stopit( status, 'Error 3d' ) + + if( ast_test( obj, 'Equinox', status ) ) + : call stopit( status, 'Error 3d2' ) + + if( ast_getc( obj, 'domain(2)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 4' ) + + if( ast_getc( obj, 'domain(4)', status ) .ne. 'SPECTRUM' ) + : call stopit( status, 'Error 5' ) + + if( ast_getc( obj, 'system(4)', status ) .ne. 'WAVE' ) + : call stopit( status, 'Error 5a' ) + + if( ast_getc( obj, 'stdofrest', status ) .ne. 'Barycentric' ) + : call stopit( status, 'Error 5b' ) + + if( ast_getc( obj, 'title(4)', status ) .ne. 'Wavelength' ) + : call stopit( status, 'Error 5c' ) + + if( ast_geti( obj, 'naxes', status ) .ne. 4 ) + : call stopit( status, 'Error 6' ) + + if( ast_getc( obj, 'unit(4)', status ) .ne. 'Angstrom' ) + : call stopit( status, 'Error 6e' ) + + frm = ast_getregionframe( obj, status ) + if( ast_getc( frm, 'Ident', status ) .ne. 'ICRS-TT-BARY' ) + : call stopit( status, 'Error 7' ) + + + +* Tests on reference values + if( ast_test( obj, 'RefRA(4)', status ) ) then + call stopit( status, 'Error 9' ) + end if + + if( ast_test( obj, 'RefDec(4)', status ) ) then + call stopit( status, 'Error 10' ) + end if + +c if( ast_test( obj, 'Epoch(4)', status ) ) then +c call stopit( status, 'Error 11' ) +c end if + +c if( ast_test( obj, 'Epoch(1)', status ) ) then +c call stopit( status, 'Error 12' ) +c end if + +c if( ast_test( obj, 'Epoch(2)', status ) ) then +c call stopit( status, 'Error 13' ) +c end if + + if( ast_test( obj, 'RestFreq(4)', status ) ) then + call stopit( status, 'Error 14' ) + end if + + if( abs( ast_getd( obj, 'Epoch(3)', status ) - 1900.00051056532 ) + : .gt. 0.0001 ) then + call stopit( status, 'Error 12b' ) + end if + + if( abs( ast_getd( obj, 'TimeOrigin', status ) - 15020.0D0 ) + : .gt. 0.0001 ) then + call stopit( status, 'Error 12c' ) + end if + + + + + in(1,1) = 2.51126532207628 ! inside + in(1,2) = 1.22218015796595 + in(1,3) = 0.01 + in(1,4) = 4001 + + in(2,1) = 2.5094191311777 ! outside + in(2,2) = 1.22248014367694 + in(2,3) = 0.01 + in(2,4) = 4001 + + in(3,1) = 2.51126532207628 ! outside + in(3,2) = 1.22218015796595 + in(3,3) = 0.01 + in(3,4) = 3999 + + in(4,1) = 2.51126532207628 ! outside + in(4,2) = 1.22218015796595 + in(4,3) = -0.2 + in(4,4) = 4001 + + in(5,1) = 2.5094191311777 ! outside + in(5,2) = 1.22248014367694 + in(5,3) = -0.2 + in(5,4) = 4001 + + in(6,1) = 2.51126532207628 ! outside + in(6,2) = 1.22218015796595 + in(6,3) = -0.2 + in(6,4) = 3999 + + in(7,1) = 2.51682141503858 ! inside + in(7,2) = 1.18868060989363 + in(7,3) = 0.01 + in(7,4) = 6999 + + in(8,1) = 2.51524001365674 ! outside + in(8,2) = 1.18830732379242 + in(8,3) = 0.01 + in(8,4) = 6999 + + in(9,1) = 2.51682141503858 ! outside + in(9,2) = 1.18868060989363 + in(9,3) = 0.01 + in(9,4) = 7001 + + in(10,1) = 2.51682141503858 ! outside + in(10,2) = 1.18868060989363 + in(10,3) = -0.2 + in(10,4) = 6999 + + in(11,1) = 2.51524001365674 ! outside + in(11,2) = 1.18830732379242 + in(11,3) = -0.2 + in(11,4) = 6999 + + in(12,1) = 2.51682141503858 ! outside + in(12,2) = 1.18868060989363 + in(12,3) = -0.2 + in(12,4) = 7001 + + call ast_trann( obj, 12, 4, 12, in, .true., 4, 12, out, status ) + + do i = 1, 12 + if( i .eq. 1 .or. i .eq. 7 ) then ! inside points + do j = 1, 4 + if( out(i,j) .ne. in(i,j) ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 13c' ) + end if + end if + end do + else ! outside points + do j = 1, 4 + if( out(i,j) .ne. AST__BAD ) then + if( status .eq. sai__ok ) then + write(*,*) i,j,out(i,j),in(i,j) + call stopit( status, 'Error 14c' ) + end if + end if + end do + end if + end do + + + call ast_end( status ) + + if( status .ne. sai__ok ) write(*,*) 'teststc: example 4 '// + : 'tests failed' + + end + + + + + subroutine Example5( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + integer status, obj2, obj, i, j, unc, frm + double precision in(12,4), out(12,4), lbnd(4), ubnd(4) + + if( status .ne. sai__ok ) return + + call ast_begin( status ) + +* Put an example of an STCSearchLocation into file 1. + call puteg( 'teststc_eg5', 1, status ) + +* Use a new XmlChan to read an object from file 1,and simplify it. + call xmlread( 1, obj, ' ', status ) + call checkdump( obj, 'checkdump 2', status ) + obj = ast_simplify( obj, status ) + +* Test simplify by negating and simplifying twice. + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call ast_negate( obj, status ) + obj = ast_simplify( obj, status ) + call checkdump( obj, 'checkdump 1', status ) + +* Check it is a STCObsDataLocation + if( .not. ast_isastcobsdatalocation( obj, status ) ) + : call stopit( status, 'Error 1' ) + +* Check it contains a Prism. + if( .not. ast_isaprism( ast_getstcregion( obj, status ), + : status ) ) + : call stopit( status, 'Error 1a' ) + +* Other tests + if( ast_getd( obj, 'fillfactor', status ) .ne. 1.0D0 ) + : call stopit( status, 'Error 1b' ) + + if( ast_getc( obj, 'ident', status ) .ne. 'M81' ) + : call stopit( status, 'Error 1c' ) + + if( ast_getc( obj, 'domain(3)', status ) .ne. 'TIME' ) + : call stopit( status, 'Error 2' ) + + if( ast_getc( obj, 'label(3)', status ) .ne. + : 'Modified Julian Date offset from 2004-07-15 08:23:56' ) then + call stopit( status, 'Error 2b' ) + end if + + if( ast_getc( obj, 'domain(1)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 3' ) + + if( ast_getc( obj, 'system(1)', status ) .ne. 'ICRS' ) + : call stopit( status, 'Error 3a' ) + + if( ast_getc( obj, 'label(1)', status ) .ne. 'Right ascension' ) + : call stopit( status, 'Error 3b' ) + + if( ast_getc( obj, 'label(2)', status ) .ne. 'Declination' ) + : call stopit( status, 'Error 3c' ) + + if( ast_getc( obj, 'title(2)', status ) .ne. 'Equatorial' ) + : call stopit( status, 'Error 3d' ) + + if( ast_getc( obj, 'domain(2)', status ) .ne. 'SKY' ) + : call stopit( status, 'Error 4' ) + + if( ast_getc( obj, 'domain(4)', status ) .ne. 'SPECTRUM' ) + : call stopit( status, 'Error 5' ) + + if( ast_getc( obj, 'system(4)', status ) .ne. 'WAVE' ) + : call stopit( status, 'Error 5a' ) + + if( ast_getc( obj, 'stdofrest', status ) .ne. 'Topocentric' ) + : call stopit( status, 'Error 5b' ) + + if( ast_getc( obj, 'title(4)', status ) .ne. + : 'Wavelength' ) call stopit( status, 'Error 5c' ) + + if( ast_getc( obj, 'unit(4)', status ) .ne. 'Angstrom' ) + : call stopit( status, 'Error 5d' ) + + if( ast_geti( obj, 'naxes', status ) .ne. 4 ) + : call stopit( status, 'Error 6' ) + + call ast_getregionbounds( obj, lbnd, ubnd, status ) + + + + lbnd(1) = 0.5*(lbnd(1) + ubnd(1)) + lbnd(2) = 0.5*(lbnd(2) + ubnd(2)) + lbnd(3) = 0.5*(lbnd(3) + ubnd(3)) + lbnd(4) = 0.5*(lbnd(4) + ubnd(4)) + + if( abs( lbnd(1) - 2.59858948190075 ) .gt. 1E-06 ) + : call stopit( status, 'Error 10' ) + if( abs( lbnd(2) - 1.20541670934471 ) .gt. 1E-06 ) + : call stopit( status, 'Error 11' ) + if( abs( lbnd(3) ) .gt. 1E-5 ) + : call stopit( status, 'Error 12' ) + if( abs( lbnd(4) - 4600 ) .gt. 0.0001 ) + : call stopit( status, 'Error 13' ) + if( abs( ubnd(1) - 2.61080678666471 ) .gt. 1E-06 ) + : call stopit( status, 'Error 14' ) + if( abs( ubnd(2) - 1.2097800324747 ) .gt. 1E-06 ) + : call stopit( status, 'Error 15' ) + if( abs( ubnd(3) - 380.0D0 ) .gt. 1E-5 ) + : call stopit( status, 'Error 16' ) + if( abs( ubnd(4) - 4800 ) .gt. 0.0001 ) + : call stopit( status, 'Error 17' ) + if( ast_getc( obj, 'ObsLon', status ) .ne. 'W111:35:39.84' ) + : call stopit( status, 'Error 18' ) + if( ast_getc( obj, 'ObsLat', status ) .ne. 'N31:57:30.96' ) + : call stopit( status, 'Error 19' ) + + unc = ast_getunc( obj, .true., status ) + if( unc .eq. AST__NULL ) call stopit( status, 'Error 20' ) + + call ast_getregionbounds( unc, lbnd, ubnd, status ) + + lbnd(1) = 0.5*(lbnd(1) + ubnd(1)) + lbnd(2) = 0.5*(lbnd(2) + ubnd(2)) + lbnd(3) = 0.5*(lbnd(3) + ubnd(3)) + lbnd(4) = 0.5*(lbnd(4) + ubnd(4)) + + if( abs( lbnd(1) - 2.59858948190075D0) .gt. 1E-05 ) + : call stopit( status, 'Error 21' ) + if( abs( lbnd(2) - 1.20541670934471D0) .gt. 1E-05 ) + : call stopit( status, 'Error 22' ) + if( abs( lbnd(3) ) .gt. 1.0D-05 ) + : call stopit( status, 'Error 23' ) + if( abs( lbnd(4) - 4600.0D0) .gt. 0.0001 ) + : call stopit( status, 'Error 24' ) + if( abs( ubnd(1) - 2.59859209989462D0) .gt. 1E-05 ) + : call stopit( status, 'Error 25' ) + + if( abs( ubnd(2) - 1.20541932733859D0) .gt. 1E-05 ) + : call stopit( status, 'Error 26' ) + if( abs( ubnd(3) - 0.3803143212621760D-03 ) .gt. 1E-05 ) + : call stopit( status, 'Error 27' ) + if( abs( ubnd(4) - 4600.0002D0) .gt. 0.000001 ) + : call stopit( status, 'Error 28' ) + + if( status .ne. sai__ok ) write(*,*) 'teststc: example 5 '// + : 'tests failed' + + end + + + + + + + + + + + + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + + + + +* +* Read an object out of the specified internal file using an XmlChan. +* + subroutine xmlread( ifil, obj, opts, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + include 'teststc_com' + + external xmlSource + integer obj, ifil, status, ch + character opts*(*) + + if( status .ne. sai__ok ) return + + ifile = ifil + iline = 1 + + ch = ast_xmlchan( xmlSource, ast_null, opts, status ) + obj = ast_read( ch, status ) + if( obj .eq. ast__null ) then + call stopit( status, 'checkXmlChan: Failed to read STC '// + : 'object from XmlChan.' ) + end if + + warns = ast_warnings( ch, status ) + call ast_annul( ch, status ) + + end + + + + +* +* Reads line "iline" from internal file "ifile" and returns it to AST using +* the AST_PUTLINE routine. Then increments "iline" ready for next time. +* + subroutine xmlSource( status ) + implicit none + + include 'teststc_com' + + integer status, l, chr_len + + if( iline .le. filelen( ifile ) ) then + l = chr_len( files(ifile,iline) ) + call ast_putline( files(ifile,iline), l, status ) + iline = iline + 1 + else + call ast_putline( ' ', -1, status ) + end if + + end + +* +* Append a line obtained using ast_getline function to the end of the +* internal file indicated by "ifile", and increment the file length. +* + subroutine xmlSink( status ) + implicit none + + include 'teststc_com' + + integer status, l + character line*(linelen) + + call ast_getline( line, l, status ) + if( l .gt. 0 ) then + + if( filelen( ifile ) .ge. mxline ) then + call stopit( status, 'checkXmlChan: Too many lines sent '// + : 'to sink function' ) + + else if( l .gt. linelen ) then + call stopit( status, 'checkXmlChan: Text truncated in '// + : 'sink function' ) + + else + filelen( ifile ) = filelen( ifile ) + 1 + files( ifile, filelen( ifile ) ) = line(:l) + end if + + end if + + end + + + subroutine puteg( flnam, ifl, status ) + implicit none + + include 'SAE_PAR' + include 'AST_PAR' + include 'teststc_com' + + integer status, ifl + character flnam*(*) + + if( status .ne. sai__ok ) return + + open( file=flnam, status='old', unit=10 ) + + iline = 1 + 10 continue + if( iline .gt. mxline ) call stopit( status, + : 'mxline exceeded in puteg' ) + read( 10, '(A)', end=20 ) files( ifl, iline ) + + if( files( ifl, iline )( linelen : linelen ) .ne. ' ' ) then + call stopit( status, 'linelen exceeded in puteg' ) + end if + + iline = iline + 1 + go to 10 + + 20 continue + close( 10 ) + filelen( ifl ) = iline - 1 + + end + + +* +* Tests the dump function, the loader, and the astOverlap method. +* + subroutine checkdump( obj, text, status ) + + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap + external mysource, mysink + character buf*190000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + +* Create a Channel which reads and writes to an internal string buffer. + ch = ast_channel( mysource, mysink, ' ', status ) + +* Write the supplied Region out to this Channel. + ll = 160 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + +* Read an Object back from this Channel. + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + +* Check that it is a Region and its boundary is identical to the supplied +* Region. + overlap = ast_overlap( obj, result, status ) + if( overlap .ne. 5 ) then + write(*,*) 'obj result Overlap: ', overlap + write(*,*) 'obj self-Overlap: ', ast_overlap( obj, obj, + : status ) + write(*,*) 'result self-Overlap: ', ast_overlap( result, + : result, status ) + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + +* Return the new Region pointer in place of the old. + obj = result + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll + character buf*190000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf*190000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 190000 ) then + write(*,*) buf + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l,' greater than ',ll + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + endif + + next = next + ll + + end + + diff --git a/ast_tester/teststc_com b/ast_tester/teststc_com new file mode 100644 index 0000000..f9a7fb1 --- /dev/null +++ b/ast_tester/teststc_com @@ -0,0 +1,13 @@ +* +* Common block declaration used by teststc.f for storing internal +* files and associated info. +* + integer mxline + parameter ( mxline = 500 ) + + integer linelen + parameter ( linelen = 3000 ) + + character files( 3, mxline )*(linelen) + integer ifile,iline,filelen( 3 ),warns + common /files/ files, ifile, iline, filelen, warns diff --git a/ast_tester/teststc_eg1 b/ast_tester/teststc_eg1 new file mode 100644 index 0000000..b1044c6 --- /dev/null +++ b/ast_tester/teststc_eg1 @@ -0,0 +1,71 @@ + + + + + + + + Time + TT + + + + Space + + + + + + Energy + + + + + + Time + 0.0001 + 0.000016 + 3.0 + 1000 + 170000 + + + Position + + 1.0 1.0 + + + 0.5 0.5 + + + 1000 1000 + + + 4000 4000 + + + + Energy + 0.1 + 0.02 + 2.0 + 2 + 10 + + + + + + TT + 1999-07-23T16:00:00 + + + + + + + 0.12 + 10.0 + + + diff --git a/ast_tester/teststc_eg10 b/ast_tester/teststc_eg10 new file mode 100644 index 0000000..977fa57 --- /dev/null +++ b/ast_tester/teststc_eg10 @@ -0,0 +1,18 @@ + + + + Space + + + + + + + + + 143.0 42.0 + 1.0 1.0 + + + + diff --git a/ast_tester/teststc_eg2 b/ast_tester/teststc_eg2 new file mode 100644 index 0000000..6665676 --- /dev/null +++ b/ast_tester/teststc_eg2 @@ -0,0 +1,114 @@ + + + + + + + + ET + + + + PosEq + + B1950.0 + + + + + + + + + DopplerVelocity + OPTICAL + + + + + + + Time + ET + + + + SGC + + + + + + Optical + + + + DopplerVelocity + OPTICAL + + + + + + RA,Dec + Column3 + Column4 + Column5 + + + Vrad(barycenter) + Column6 + Column7 + + + + + SGLong,SGLat + Column8 + Column9 + Column10 + + + Vrad(Galcenter) + Column11 + Column12 + + + + + + ET + 2440000 + + + ET + 2441000 + + + + + + 270 20 + + + 90 20 + + + + 90 70 + + + 270 70 + + + + + + 5000 + 6500 + + + 10000 + + + diff --git a/ast_tester/teststc_eg3 b/ast_tester/teststc_eg3 new file mode 100644 index 0000000..028d484 --- /dev/null +++ b/ast_tester/teststc_eg3 @@ -0,0 +1,113 @@ + + + + + + + + ET + + + + PosEq + + B1950.0 + + + + + + + + + DopplerVelocity + OPTICAL + + + + + + Time + ET + + + + SGC + + + + + + Optical + + + + DopplerVelocity + OPTICAL + + + + + + RA,Dec + Column3 + Column4 + Column5 + + + Vrad(barycenter) + Column6 + Column7 + + + + + SGLong,SGLat + Column8 + Column9 + Column10 + + + Vrad(Galcenter) + Column11 + Column12 + + + + + + ET + 2440000 + + + ET + 2440000 + + + + + + 270 20 + + + 90 20 + + + + 90 70 + + + 270 70 + + + + + + 5000 + 5000 + + + 10000 + + + diff --git a/ast_tester/teststc_eg4 b/ast_tester/teststc_eg4 new file mode 100644 index 0000000..5b9d8ab --- /dev/null +++ b/ast_tester/teststc_eg4 @@ -0,0 +1,55 @@ + + + + + Time + TT + + + + Equatorial + + + + + + Wavelength + + + + + + RA,Dec + 0.0001 0.0001 + 0.0003 0.0003 + 0.5 0.5 + 0.67 0.67 + 0.00005 0.00005 + 0.00015 0.00015 + + + Lambda + 300 + 600 + + + + + + TT + 1900-01-01T00:00:00 + + + + + 148.9 69.1 + 2 + + + + 4000 + 7000 + + + + diff --git a/ast_tester/teststc_eg5 b/ast_tester/teststc_eg5 new file mode 100644 index 0000000..97aad97 --- /dev/null +++ b/ast_tester/teststc_eg5 @@ -0,0 +1,109 @@ + + + + + + Time + TT + + + + GeoLongLatElev + + + + + + + + LongLatElev + 248.4056 31.9586 2158 + + + + + + + Time + TT + + + + Equatorial + + + + + + Wavelength + + + + + + Time + + TT + 2004-07-15T08:23:56 + + 1000 + + + RA,Dec + 148.88821 69.06529 + 0.0003 0.0003 + 0.00025 0.00025 + 0.0001 0.0001 + + + Lambda + 4600 + 400 + 400 + + + + + + TT + 2004-07-15T08:17:36 + + + TT + 2004-07-15T08:30:16 + + + + + 148.18821 68.81529 + 149.58821 69.31529 + + + + 4400 + 4800 + + + + + + + X + + + Y + + + + + 1 + 1024 + + + 1 + 1024 + + + + + diff --git a/ast_tester/teststc_eg6 b/ast_tester/teststc_eg6 new file mode 100644 index 0000000..7a291f6 --- /dev/null +++ b/ast_tester/teststc_eg6 @@ -0,0 +1,49 @@ + + + + Time + TAI + + + + Space + + + + + + Energy + + + + + + + Position + + 1.0 1.0 + + + + Energy + 0.1 + + + + + + 1999-07-23T16:00:00 + + + + + + + 0.12 + 10.0 + + + diff --git a/ast_tester/teststc_eg7 b/ast_tester/teststc_eg7 new file mode 100644 index 0000000..0106c85 --- /dev/null +++ b/ast_tester/teststc_eg7 @@ -0,0 +1,52 @@ + + + + Time + + TAI + + + Space + + + + + + Energy + + + + + + + Position + + 1.0 1.0 + + + + Energy + 0.1 + + + + + + 1998-07-23T16:00:00 + + + + +
0.0 90.0
+ 2.0 +
+
+ + 8.0 + 11.0 + +
+
diff --git a/ast_tester/teststc_eg8 b/ast_tester/teststc_eg8 new file mode 100644 index 0000000..17c6574 --- /dev/null +++ b/ast_tester/teststc_eg8 @@ -0,0 +1,52 @@ + + + + Time + UTC + + + + Space + + + + + + Energy + + + + + + + Position + + 1.0 1.0 + + + + Energy + 0.1 + + + + + + 1999-09-23T16:00:00 + + + + +
0.0 90.0
+ 2.0 +
+
+ + 8.0 + 9.0 + +
+
diff --git a/ast_tester/teststc_eg9 b/ast_tester/teststc_eg9 new file mode 100644 index 0000000..195adb7 --- /dev/null +++ b/ast_tester/teststc_eg9 @@ -0,0 +1,49 @@ + + + + Time + TCB + + + + Space + + + + + + Energy + + + + + + + Position + + 1.0 1.0 + + + + Energy + 0.1 + + + + + + 1999-06-23T16:00:00 + + + + + + + 0.12 + 10.0 + + + diff --git a/ast_tester/teststcschan.f b/ast_tester/teststcschan.f new file mode 100755 index 0000000..928b301 --- /dev/null +++ b/ast_tester/teststcschan.f @@ -0,0 +1,654 @@ + program teststcschan + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status + + status = sai__ok + + call ast_begin( status ) + +c call ast_watchmemory( 209814 ); + + call test2( status ) + call test1( status ) + + + + + call ast_end( status ) +c call ast_activememory( ' ' ) +c call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + call msg_out( ' ', ' All StcsChan tests passed', status ) + else + call err_rep( ' ', 'StcsChan tests failed', status ) + end if + + end + + + + + + subroutine test1( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + include 'PRM_PAR' + + integer iwrite + character buff(30)*300 + common /bbb/ iwrite, buff + + integer iread, idoc + common /aaa/ iread, idoc + + integer status, ch, obj, km, i, sb, iobj, nobj + external source, sink + + double precision lbnd(4), ubnd(4) + if( status .ne. sai__ok ) return + + call ast_begin( status ) + + ch = ast_stcschan( source, sink, 'ReportLevel=3', status ) + + + idoc = 4 + iread = 0 + obj = ast_read( ch, status ) + + idoc = 1 + iread = 0 + + call err_mark + obj = ast_read( ch, status ) + if( status .eq. AST__BADIN ) then + call err_annul( status ) + call err_rlse + else + call err_rlse + call error( 'Failed to report error about "fred"', status ) + end if + + + idoc = 2 + iread = 0 + obj = ast_read( ch, status ) + + km = ast_warnings( ch, status ) + if( km .eq. AST__NULL ) call error( 'No Warnings keymap', status ) + call asserti( 'Warnings mapsize', ast_mapsize( km, status ), 4, + : status ) + + call asserta( obj, 'Class', 'Prism', status ) + call asserta( obj, 'Naxes', '4', status ) + call asserta( obj, 'Label(1)', 'Modified Julian Date offset '// + : 'from 1900-01-01', status ) + call asserta( obj, 'Label(2)', 'Right ascension', status ) + call asserta( obj, 'Label(3)', 'Declination', status ) + call asserta( obj, 'Label(4)', 'Wavelength', status ) + call asserta( obj, 'Unit(1)', 'd', status ) + call asserta( obj, 'Unit(4)', 'Angstrom', status ) + + call ast_GetRegionBounds( obj, lbnd, ubnd, status ) + call ast_setc( obj, 'Format(1)', 'iso.2', status ) + call assertd( 'Time upper bounds', ubnd(1), VAL__MAXD, status ) + call assertc( 'Time lower bound', + : ast_format( obj, 1, lbnd(1), status ), + : '1900-01-01 00:00:00.00', status ) + call assertd( 'RA lower bound', lbnd(2), 2.50080939227851D0, + : status ) + call assertd( 'RA upper bound', ubnd(2), 2.6967811201606D0, + : status ) + call assertd( 'Dec lower bound', lbnd(3), 1.171115928088195D0, + : status ) + call assertd( 'Dec upper bound', ubnd(3), 1.24091013301998D0, + : status ) + call assertd( 'Wavelength lower bound', lbnd(4), 4000.0D0, + : status ) + call assertd( 'Wavelength upper bound', ubnd(4), 7000.0D0, + : status ) + + + + idoc = 3 + iread = 0 + obj = ast_read( ch, status ) + + call readast( 'stcschan-test1-doc3.ast', sb, status ) + if( .not. ast_equal( obj, sb, status ) ) then + call error( 'Object read from doc3 is not equal to the '// + : 'object read from file stcschan-test1-doc3.ast.', + : status ) + end if + + + call ast_setl( ch, 'StcsCoords', .true., status ) + call ast_setl( ch, 'StcsProps', .true., status ) + + idoc = 3 + iread = 0 + obj = ast_read( ch, status ) + + call asserta( obj, 'Class', 'KeyMap', status ) + call assert( 'Has PROPS entry', AST_MAPHASKEY( obj, 'PROPS', + : status ), status ) + call assert( 'Has COORDS entry', AST_MAPHASKEY( obj, 'COORDS', + : status ), status ) + + if( ast_mapget0a( obj, 'AREA', iobj, status ) ) then + call readast( 'stcschan-test1-doc3.ast', sb, status ) + if( .not. ast_equal( iobj, sb, status ) ) then + call error( 'AREA read from doc3 is not equal to the '// + : 'object read from file stcschan-test1-doc3.ast', + : status ) + end if + else + call error( 'No AREA entry found', status ) + end if + + if( ast_mapget0a( obj, 'PROPS', iobj, status ) ) then + call readast( 'stcschan-test1-doc3-props.ast', sb, status ) + if( .not. ast_equal( iobj, sb, status ) ) then + call error( 'PROPS read from doc3 is not equal to the '// + : 'object read from file stcschan-test1-doc3-props.ast', + : status ) + end if + else + call error( 'No PROPS entry found', status ) + end if + + + + idoc = 5 + iread = 0 + + call ast_setl( ch, 'Indent', .true., status ) + + obj = ast_read( ch, status ) + + iwrite = 0 + nobj = ast_write( ch, obj, status ) + call asserti( 'N obj', nobj, 1, status ) + + call assertc( 'line 1 3', buff(1), + : 'TimeInterval TT geocenter 1996-01-01T00:00:00 '// + : '1996-01-01T00:30:00', status ) + call assertc( 'line 2 3', buff(2), + : ' Time MJD 50814.0 Error 1.2 Resolution 0.8 '// + : 'PixSize 1024.0', status ) + call assertc( 'line 3 3', buff(3), + : 'Union ICRS GEOCENTER ( ', status ) + call assertc( 'line 4 3', buff(4), + : ' Circle 180 10 20 ', status ) + call assertc( 'line 5 3', buff(5), + : ' Circle 190 10 20 ', status ) + call assertc( 'line 6 3', buff(6), + : ' Intersection ( ', status ) + call assertc( 'line 7 3', buff(7), + : ' Circle 120 -10 20 ', status ) + call assertc( 'line 8 3', buff(8), + : ' Difference ( ', status ) + call assertc( 'line 9 3', buff(9), + : ' Circle 130 -10 20 ', status ) + call assertc( 'line 10 3', buff(10), + : ' Circle 115 -10 10 ', status ) + call assertc( 'line 11 3', buff(11), + : ' ) ', status ) + call assertc( 'line 12 3', buff(12), + : ' ) ', status ) + call assertc( 'line 13 3', buff(13), + : ' ) ', status ) + call assertc( 'line 14 3', buff(14), + : ' Position 179.0 -11.5 Error 0.000889 0.000889 '// + : 'Resolution 0.001778', status ) + call assertc( 'line 15 3', buff(15), + : ' Size 0.000333 0.000278 PixSize 0.000083 '// + : '0.000083', status ) + call assertc( 'line 16 3', buff(16), + : 'Spectral BARYCENTER 1420.4 unit MHz Resolution '// + : '10.0 ', status ) + call assertc( 'line 17 3', buff(17), + : 'RedshiftInterval BARYCENTER VELOCITY OPTICAL '// + : '200 2300 Redshift 300', status ) + + call ast_end( status ) + + if( status .ne. sai__ok ) call err_rep( ' ', 'test1 failed.', + : status ) + + end + + + subroutine test2( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + include 'PRM_PAR' + + integer iwrite + character buff(30)*300 + common /bbb/ iwrite, buff + + integer iread, idoc + common /aaa/ iread, idoc + + + + external source, sink + + integer status, ch, sf, unc, reg, nobj, obj, chr_len + double precision p1(2), p2(2), p3(3), lbnd(2), ubnd(2) + + if( status .ne. sai__ok ) return + + + call ast_begin( status ) + ch = ast_stcschan( source, sink, 'ReportLevel=3',status ) + call ast_setl( ch, 'Indent', .true., status ) + call ast_seti( ch, 'StcsLength', 60, status ) + + sf = ast_skyframe( ' ', status ); + p1( 1 ) = 0.0 + p1( 2 ) = 1.3 + p2( 1 ) = 0.01 + + unc = ast_circle( sf, 1, p1, p2, AST__NULL, ' ', status ) + + p1( 1 ) = 1.3 + p1( 2 ) = 0.5 + p2( 1 ) = 0.3 + p2( 2 ) = 0.1 + p3( 1 ) = 1.0 + reg = ast_ellipse( sf, 1, p1, p2, p3, unc, ' ', status ) + + iwrite = 0 + nobj = ast_write( ch, reg, status ) + call asserti( 'N obj', nobj, 1, status ) + call asserti( 'iwrite', iwrite, 2, status ) + call assertc( 'line 1', buff(1), 'Ellipse ICRS TOPOCENTER '// + : '74.48451 28.64789 17.18873 5.729578', status ) + call assertc( 'line 2', buff(2), ' 57.29578 Error 0.5729514 '// + : '0.5726735', status ) + + call ast_set( ch, 'StcsCoords=1,StcsProps=1', status ) + + idoc = 3 + iread = 0 + obj = ast_read( ch, status ) + + if( obj .ne. AST__NULL ) then + iwrite = 0 + nobj = ast_write( ch, obj, status ) + call asserti( 'N obj 2', nobj, 1, status ) + + call assertc( 'line 1 2', buff(1), 'TimeInterval TT '// + : 'GEoCENTER 1996-01-01T00:00:00', status ) + + call assertc( 'line 2 2', buff(2), ' 1996-01-01T00:30:00 '// + : 'Time MJD 50814.0 Error 1.2', status ); + + call assertc( 'line 3 2', buff(3), ' Resolution 0.8 '// + : 'PixSize 1024.0', status ) + + call assertc( 'line 4 2', buff(4), 'Circle ICRS GEOCENTER '// + : '179.0 -11.5 0.5 Position 179.0 -11.5', status ) + + call assertc( 'line 5 2', buff(5), ' Error 0.000889 '// + : '0.000889 Resolution 0.001778 Size 0.000333', + : status ) + + call assertc( 'line 6 2', buff(6), ' 0.000278 PixSize '// + : '0.000083 0.000083', status ) + + call assertc( 'line 7 2', buff(7), 'Spectral BARYCENTER '// + : '1420.4 unit MHz Resolution 10.0', status ) + + call assertc( 'line 8 2', buff(8), 'RedshiftInterval '// + : 'BARYCENTER VELOCITY OPTICAL 200 2300 ', + : status ) + + call assertc( 'line 9 2', buff(9), ' Redshift 300 '// + : 'Resolution 0.7 PixSize 0.3', status ) + + else + write(*,*) 'No object read from doc 3' + end if + + + call ast_end( status ) + + if( status .ne. sai__ok ) call err_rep( ' ', 'test2 failed.', + : status ) + + end + + + + + + + + + + + + + + + subroutine source( status ) + implicit none + + integer iread, idoc + common /aaa/ iread, idoc + + logical done + integer status, l, chr_len + character c*80 + + c = ' ' + done = .false. + + if( idoc .eq. 1 ) then + if( iread .eq. 0 ) then + c = 'StartTime 1900-01-01 Circle ICRS 148.9 69.1 2.0 fred' + else if( iread .eq. 1 ) then + c = 'SpectralInterval 4000 7000 unit Angstrom' + else + done = .true. + end if + + else if( idoc .eq. 2 ) then + if( iread .eq. 0 ) then + c = 'StartTime 1900-01-01 Circle ICRS 148.9 69.1 2.0 ' + else if( iread .eq. 1 ) then + c = 'SpeCtralInterval 4000 7000 unit Angstrom' + else + done = .true. + end if + + else if( idoc .eq. 3 ) then + if( iread .eq. 0 ) then + c = 'TimeInterVal TT GEoCENTER' + else if( iread .eq. 1 ) then + c = '1996-01-01T00:00:00 1996-01-01T00:30:00' + else if( iread .eq. 2 ) then + c = 'Time MJD 50814.0 Error 1.2' + else if( iread .eq. 3 ) then + c = 'Resolution 0.8 PixSize 1024.0' + else if( iread .eq. 4 ) then + c = 'Circle ICRS GEOCENTER 179.0 -11.5 0.5' + else if( iread .eq. 5 ) then + c = 'Position 179.0 -11.5 Error 0.000889' + else if( iread .eq. 6 ) then + c = 'Resolution 0.001778 Size 0.000333 0.000278' + else if( iread .eq. 7 ) then + c = 'PixSIZE 0.000083 0.000083' + else if( iread .eq. 8 ) then + c = 'Spectral BARYCENTER 1420.4 unit MHz' + else if( iread .eq. 9 ) then + c = 'Resolution 10.0' + else if( iread .eq. 10 ) then + c = 'RedshiftInterval BARYCENTER VELOCITY OPTICAL' + else if( iread .eq. 11 ) then + c = '200.0 2300.0 Redshift 300.0' + else if( iread .eq. 12 ) then + c = 'Resolution 0.7 PixSize 0.3' + else + done = .true. + end if + + else if( idoc .eq. 4 ) then + if( iread .eq. 0 ) then + c = 'TimeInterval TT GEOCENTER' + else if( iread .eq. 1 ) then + c = '1996-01-01T00:00:00 1996-01-01T00:30:00' + else if( iread .eq. 2 ) then + c = 'Time mjd 50814.0 ERROR 1.2' + else if( iread .eq. 3 ) then + c = 'Resolution 0.8 PixSize 1024.0' + else if( iread .eq. 4 ) then + c = 'Spectral barycenter 1420.4 UNIT MHz' + else if( iread .eq. 5 ) then + c = 'Resolution 10.0' + else + done = .true. + end if + +* Like doc 3 but with a compound spatial region + else if( idoc .eq. 5 ) then + if( iread .eq. 0 ) then + c = 'tIMEiNTERVAL tt geocenter' + else if( iread .eq. 1 ) then + c = '1996-01-01T00:00:00 1996-01-01T00:30:00' + else if( iread .eq. 2 ) then + c = 'Time MJD 50814.0 Error 1.2' + else if( iread .eq. 3 ) then + c = 'Resolution 0.8 PixSize 1024.0' + else if( iread .eq. 4 ) then + c = ' ' + else if( iread .eq. 5 ) then + c = 'Union ICRS GEOCENTER' + else if( iread .eq. 6 ) then + c = ' (Circle 180 10 20' + else if( iread .eq. 7 ) then + c = ' Circle 190 10 20' + else if( iread .eq. 8 ) then + c = ' Intersection (' + else if( iread .eq. 9 ) then + c = ' cIRCLE 120 -10 20 dIFFERENCE ' + else if( iread .eq. 10 ) then + c = ' ( Circle 130 -10 20 ' + else if( iread .eq. 11 ) then + c = ' Circle 115 -10 10 ' + else if( iread .eq. 12 ) then + c = ' )' + else if( iread .eq. 13 ) then + c = ' Not (Circle 118 -8 3)' + else if( iread .eq. 14 ) then + c = ' )' + else if( iread .eq. 15 ) then + c = ' )' + else if( iread .eq. 16 ) then + c = 'Position 179.0 -11.5 Error 0.000889' + else if( iread .eq. 17 ) then + c = 'Resolution 0.001778 Size 0.000333 0.000278' + else if( iread .eq. 18 ) then + c = 'PixSize 0.000083 0.000083' + else if( iread .eq. 19 ) then + c = 'Spectral BARYCENTER 1420.4 unit MHz' + else if( iread .eq. 20 ) then + c = 'rESOLUTION 10.0' + else if( iread .eq. 21 ) then + c = 'rEDSHIFTiNTERVAL barycenter velocity optical' + else if( iread .eq. 22 ) then + c = '200.0 2300.0 rEDSHIFT 300.0' + else if( iread .eq. 23 ) then + c = 'Resolution 0.7 PixSize 0.3' + else + done = .true. + end if + + end if + + l = max( chr_len( c ), 1 ) + if( .not. done ) then + call ast_putline( c, l, status ) + iread = iread + 1 + else + call ast_putline( ' ', -1, status ) + end if + + end + + + + + + + + subroutine sink( status ) + implicit none + + integer iwrite + character buff(30)*300 + common /bbb/ iwrite, buff + + integer status, l + character line*300 + + call ast_getline( line, l, status ) + if( l .gt. 0 ) then + if( iwrite .lt. 0 ) then + write(*,*) line( : l ) + else + iwrite = iwrite + 1 + buff( iwrite ) = ' ' + buff( iwrite ) = line( : l ) + end if + end if + + end + + + subroutine asserta( obj, anam, asb, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer obj, status + character anam*(*), asb*(*), aval*80 + + aval = ast_GetC( obj, anam, status ) + + if( aval .ne. asb ) then + call msg_setc( 'A', anam ) + call msg_setc( 'B', aval ) + call msg_setc( 'C', asb ) + call error( '^A (^B) should be "^C".', status ) + end if + + end + + subroutine assertc( name, val, sb, status ) + implicit none + include 'SAE_PAR' + integer status, i + character name*(*), val*(*), sb*(*) + character blank*500 + + if( val .ne. sb .and. status .eq. sai__ok ) then + call msg_setc( 'A', name ) + call msg_setc( 'B', val ) + call error( '^A (^B) should be:', status ) + + i = 1 + blank = ' ' + do while( val( i : i ) .eq. sb( i : i ) ) + i = i + 1 + end do + blank( i : i ) = '^' + + write(*,*) sb + write(*,*) blank( : i + 2 ) + + end if + + end + + subroutine asserti( name, val, sb, status ) + implicit none + include 'SAE_PAR' + integer status + character name*(*) + integer val, sb + + if( val .ne. sb ) then + call msg_setc( 'A', name ) + call msg_seti( 'B', val ) + call msg_seti( 'C', sb ) + call error( '^A (^B) should be ^C.', status ) + end if + + end + + subroutine assert( name, val, status ) + implicit none + include 'SAE_PAR' + integer status + character name*(*) + logical val + + if( .not. val ) then + call msg_setc( 'A', name ) + call error( '^A is not true.', status ) + end if + + end + + subroutine assertd( name, val, sb, status ) + implicit none + include 'SAE_PAR' + integer status + character name*(*) + double precision val, sb + + if( abs( val - sb ) .gt. 0.5E-8*( val + sb ) ) then + call msg_setc( 'A', name ) + call msg_setd( 'B', val ) + call msg_setd( 'C', sb ) + call error( '^A (^B) should be ^C.', status ) + end if + + end + + subroutine error( text, status ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .eq. sai__ok ) then + status = sai__error + call err_rep( ' ', text, status ) + end if + + end + + + subroutine readast( file, obj, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer obj, status, channel + character file*(*) + external rsource + + if( status .ne. sai__ok ) return + + open( 10, file=file, status='old' ) + channel = ast_channel( rsource, ast_null, ' ', status ) + obj = ast_read( channel, status ) + call ast_annul( channel, status ) + close( 10 ) + + end + + + subroutine rsource( status ) + integer status + character buffer*200 + + read( 10, '(a)', end = 99 ) buffer + call ast_putline( buffer, len( buffer ), status ) + return + + 99 call ast_putline( buffer, -1, status ) + end + + diff --git a/ast_tester/testswitchmap.f b/ast_tester/testswitchmap.f new file mode 100644 index 0000000..6e63868 --- /dev/null +++ b/ast_tester/testswitchmap.f @@ -0,0 +1,792 @@ + program testswitchmap + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, outperm(2), inperm(2), wm1, wm2, pm1, pm2, rm(2), + : fs, is, swm, i, swm2, cm, cm2, sm, oc, mc, fc, + : box(2), gridframe + double precision x1, x2, l1, l2, in(4,2), out(4,2), at(2), r, + : p1(2),p2(2), rmout(4,2), r1, r2 + character text*10, fwd(1)*40, inv(2)*40, card(10)*80 + + status = sai__ok + + call ast_begin( status ) + +c call ast_watchmemory(22617) + + oc = ast_tune( 'ObjectCaching', 1, status ) + mc = ast_tune( 'MemoryCaching', 1, status ) + + + +* A 2D input grid has 2 rows and 101 columns. Each row contains a spectrum. +* The two spectra cover overlaping regions of wavelength. The spectrum in +* row 1 has "wavelength = (gridx - 1)*10+1000", whilst the spectrum in +* row 2 has "wavelength = (gridx - 1)*11+1600". We use a (Nin=2,Nout=1) +* SwitchMap to describe the Mapping from grid (x,y) to wavelength. This +* SwitchMap contains 2 route Mappings, one for each row of the grid. +* -------------------------------------------------------------------- + +* Produce a 1D Mapping from gridx to wavelength for row 1. + x1 = 1.0 + x2 = 101.0 + l1 = 1000.0 + l2 = 2000.0 + wm1 = ast_winmap( 1, x1, x2, l1, l2, ' ', status ) + +* Since the SwicthMap has 2 inputs, each of the route Mappings must also +* have 2 inputs. Produce a PermMap which passes on its 1st input to its +* (one and only) output. The inverse transformation supplied a value of +* 1.0 for the missing 2nd input (1.0 is the grid Y value for the first +* row). + outperm( 1 ) = 1 + inperm( 1 ) = 1 + inperm( 2 ) = -1 + pm1 = ast_permmap( 2, inperm, 1, outperm, 1.0D0, ' ', status ) + +* Combine the PermMap and WinMap in series to get the total route +* Mapping for the first row. + rm(1) = ast_cmpmap( pm1, wm1, .true., ' ', status ) + +* Likewise, produce the route Mapping for the second row. The grid y +* value for the second row is 2.0, so use this as the constant in the +* PermMap (i.e. the value which the inverse transformation supplies for +* the missing second input). + l1 = 1600.0 + l2 = 2700.0 + wm2 = ast_winmap( 1, x1, x2, l1, l2, ' ', status ) + pm2 = ast_permmap( 2, inperm, 1, outperm, 2.0D0, ' ', status ) + rm(2) = ast_cmpmap( pm2, wm2, .true., ' ', status ) + +* The forward selector Mapping just uses the second input (the grid Y +* value) as the selector value (i.e. gridy=1 selects the first route +* Mapping and gridy=2 selects the second route Mapping). The inverse +* transformation of this Mapping is never used and so does not matter. + outperm( 1 ) = 2 + inperm( 2 ) = 1 + inperm( 1 ) = 0 + fs = ast_permmap( 2, inperm, 1, outperm, 0.0D0, ' ', status ) + +* The inverse selector function needs to decide which route Mapping to +* use for any supplied ("output") wavelength value. We arbitrarily +* decide to to use the first row for wavelengths less than or equal to +* 1800, and the second row for wavelengths larger than 1800 (1800 is the +* mid-point of the overlap between the two spectra). We use a MathMap to +* implement this transformation, which must be the *inverse* +* transformation of the MathMap. The forward transformation of the +* inverse slector Mapping is never used and so is left unspecified in +* the MathMap constructor. + is = ast_mathmap( 1, 1, 1, 'y', 1, 'x=qif(y>1800,2,1)', ' ', + : status ) + +* Now create the SwitchMap. + swm = ast_switchmap( fs, is, 2, rm, ' ', status ) + +* Test the forward transformation of the SwitchMap. To add complication, +* we first invert the SwitchMap and then use ast_trann in the inverse +* direction (the two inversions cancel resulting in the forward +* transformation being used). + call ast_invert( swm, status ) + + in(1,1) = 1.0 + in(1,2) = 1.0 + in(2,1) = 101.0 + in(2,2) = 2.0 + in(3,1) = 1.0 + in(3,2) = 2.0 + in(4,1) = 101.0 + in(4,2) = 1.0 + call ast_trann( swm, 4, 2, 4, in, .false., 1, 4, out, status ) + + do i = 1, 4 + if( out(i,1) .eq. ast__bad ) then + call stopit( i, out(i,1), status ) + end if + end do + + if( abs( out(1,1) - 1000.0 ) .gt. 1.0E-5 ) then + call stopit( 5, out(1,1), status ) + else if( abs( out(2,1) - 2700.0 ) .gt. 1.0E-5 ) then + call stopit( 6, out(2,1), status ) + else if( abs( out(3,1) - 1600.0 ) .gt. 1.0E-5 ) then + call stopit( 7, out(3,1), status ) + else if( abs( out(4,1) - 2000.0 ) .gt. 1.0E-5 ) then + call stopit( 8, out(4,1), status ) + end if + +* Test the inverse transformation of the SwitchMap. + call ast_trann( swm, 4, 1, 4, out, .true., 2, 4, in, status ) + + do i = 1, 4 + if( in(i,1) .eq. ast__bad ) then + call stopit( 7 + 2*i, in(i,1), status ) + else if( in(i,2) .eq. ast__bad ) then + call stopit( 8 + 2*i, in(i,2), status ) + end if + end do + + if( abs( in(1,1) - 1.0 ) .gt. 1.0E-5 ) then + call stopit( 17, in(1,1), status ) + else if( abs( in(1,2) - 1.0 ) .gt. 1.0E-5 ) then + call stopit( 18, in(1,2), status ) + else if( abs( in(2,1) - 101.0 ) .gt. 1.0E-5 ) then + call stopit( 19, in(2,1), status ) + else if( abs( in(2,2) - 2.0 ) .gt. 1.0E-5 ) then + call stopit( 20, in(2,2), status ) + else if( abs( in(3,1) - 61.0 ) .gt. 1.0E-5 ) then + call stopit( 21, in(3,1), status ) + else if( abs( in(3,2) - 1.0 ) .gt. 1.0E-5 ) then + call stopit( 22, in(3,2), status ) + else if( abs( in(4,1) - 37.3636364 ) .gt. 1.0E-5 ) then + call stopit( 23, in(4,1), status ) + else if( abs( in(4,2) - 2.0 ) .gt. 1.0E-5 ) then + call stopit( 24, in(4,2), status ) + end if + +* Check no simplification is done on a single non-inverted SwicthMap. + call ast_setl( swm, 'Invert', .false., status ) + swm2 = ast_simplify( swm, status ) + call compare( swm, swm2, 'ast_equal 1', status ) + +* Check an inverted SwitchMap simplies to an non-inverted switchmap. + call ast_setl( swm, 'Invert', .true., status ) + swm2 = ast_simplify( swm, status ) + if( ast_getl( swm2, 'Invert', status ) ) then + call stopit( 25, 1.0D0, status ) + end if + +* Check two adjacent opposite SwitchMaps cancel. + swm2 = ast_copy( swm, status ) + call ast_invert( swm2, status ) + cm = ast_cmpmap( swm, swm2, .true., ' ', status ) + cm2 = ast_simplify( cm, status ) + if( .not. ast_isaunitmap( cm2, status ) ) then + call stopit( 26, 1.0D0, status ) + end if + + cm = ast_cmpmap( swm2, swm, .true., ' ', status ) + cm2 = ast_simplify( cm, status ) + if( .not. ast_isaunitmap( cm2, status ) ) then + call stopit( 27, 1.0D0, status ) + end if + +* Check that the SwitchMap can be written out to a AstChannel and read +* back again succesfully. + call checkdump( swm, 'Channel test 1', status ) + +* Check the ast_rate function works OK. + call ast_setl( swm, 'Invert', .false., status ) + + at(1) = 20.0 + at(2) = 1.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 10.0 ) .gt. 1.0E-6 ) call stopit( 28, r, status ) + + at(1) = 20.0 + at(2) = 2.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 11.0 ) .gt. 1.0E-6 ) call stopit( 29, r, status ) + + call ast_setl( swm, 'Invert', .true., status ) + + at(1) = 1700.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 1.0/10.0 ) .gt. 1.0E-6 ) call stopit( 30, r, status ) + + at(1) = 1900.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 1.0/11.0 ) .gt. 1.0E-6 ) call stopit( 31, r, status ) + + call ast_setl( swm, 'Invert', .false., status ) + + +* A 1D input grid has 1 rows and 202 columns. Each half of the row +* (1:101 and 102:202) contains a spectrum. The two spectra cover +* overlaping regions of wavelength. The spectrum in thw lower half has +* "wavelength = (gridx - 1)*10+1000", whilst the spectrum in the upper +* half has "wavelength = (gridx - 1)*11+1600". We use a (Nin=1,Nout=1) +* SwitchMap to describe the Mapping from grid (x) to wavelength. This +* SwitchMap contains 2 route Mappings, one for each half of the row. +* -------------------------------------------------------------------- + +* Produce a 1D Mapping from gridx to wavelength for the lower half. + x1 = 1.0 + x2 = 101.0 + l1 = 1000.0 + l2 = 2000.0 + rm(1) = ast_winmap( 1, x1, x2, l1, l2, ' ', status ) + +* Likewise, produce a 1D Mapping from gridx to wavelength for the upper half. + x1 = 102.0 + x2 = 202.0 + l1 = 1600.0 + l2 = 2700.0 + rm(2) = ast_winmap( 1, x1, x2, l1, l2, ' ', status ) + +* We can use a single MathMap for both selector Mappings - the forward +* transformation (used as the forward selector) gives 1 for all gridx less +* than 101.5 and 2 for all gridx greater than 101.5. The inverse +* transformation (used as the inverse selector) gives 1 for all +* wavelengths les than 1800 and 2 for all wavelength greater than 1800 +* (1800 is the mid point of the overlap region). + sm = ast_mathmap( 1, 1, 1, 'y=qif(x>101.5,2,1)', + : 1, 'x=qif(y>1800,2,1)', + : ' ', status ) + +* Now create the SwitchMap. + swm = ast_switchmap( sm, sm, 2, rm, ' ', status ) + +* Test the forward transformation of the SwitchMap. To add complication, +* we first invert the SwitchMap and then use ast_trann in the inverse +* direction (the two inversions cancel resulting in the forward +* transformation being used). We alo invert the selector mapping (this +* should have no effect on the SwitchMap). + call ast_invert( swm, status ) + call ast_invert( sm, status ) + + in(1,1) = 1.0 + in(2,1) = 202.0 + in(3,1) = 102.0 + in(4,1) = 101.0 + call ast_trann( swm, 4, 1, 4, in, .false., 1, 4, out, status ) + + do i = 1, 4 + if( out(i,1) .eq. ast__bad ) then + call stopit( 100+i, out(i,1), status ) + end if + end do + + if( abs( out(1,1) - 1000.0 ) .gt. 1.0E-5 ) then + call stopit( 105, out(1,1), status ) + else if( abs( out(2,1) - 2700.0 ) .gt. 1.0E-5 ) then + call stopit( 106, out(2,1), status ) + else if( abs( out(3,1) - 1600.0 ) .gt. 1.0E-5 ) then + call stopit( 107, out(3,1), status ) + else if( abs( out(4,1) - 2000.0 ) .gt. 1.0E-5 ) then + call stopit( 108, out(4,1), status ) + end if + +* Test the inverse transformation of the SwitchMap. + call ast_trann( swm, 4, 1, 4, out, .true., 1, 4, in, status ) + + do i = 1, 4 + if( in(i,1) .eq. ast__bad ) then + call stopit( 107 + 2*i, in(i,1), status ) + end if + end do + + if( abs( in(1,1) - 1.0 ) .gt. 1.0E-5 ) then + call stopit( 117, in(1,1), status ) + else if( abs( in(2,1) - 202.0 ) .gt. 1.0E-5 ) then + call stopit( 119, in(2,1), status ) + else if( abs( in(3,1) - 61.0 ) .gt. 1.0E-5 ) then + call stopit( 121, in(3,1), status ) + else if( abs( in(4,1) - 138.3636364 ) .gt. 1.0E-5 ) then + call stopit( 123, in(4,1), status ) + end if + +* Check no simplification is done on a single non-inverted SwicthMap. + call ast_setl( swm, 'Invert', .false., status ) + swm2 = ast_simplify( swm, status ) + call compare( swm, swm2, 'ast_equal 2', status ) + +* Check an inverted SwitchMap simplies to an non-inverted switchmap. + call ast_setl( swm, 'Invert', .true., status ) + swm2 = ast_simplify( swm, status ) + if( ast_getl( swm2, 'Invert', status ) ) then + call stopit( 125, 1.0D0, status ) + end if + +* Check two adjacent opposite SwitchMaps cancel. + swm2 = ast_copy( swm, status ) + call ast_invert( swm2, status ) + cm = ast_cmpmap( swm, swm2, .true., ' ', status ) + cm2 = ast_simplify( cm, status ) + if( .not. ast_isaunitmap( cm2, status ) ) then + call stopit( 126, 1.0D0, status ) + end if + + cm = ast_cmpmap( swm2, swm, .true., ' ', status ) + cm2 = ast_simplify( cm, status ) + if( .not. ast_isaunitmap( cm2, status ) ) then + call stopit( 127, 1.0D0, status ) + end if + +* Check that the SwitchMap can be written out to a AstChannel and read +* back again succesfully. + call checkdump( swm, 'Channel test 2', status ) + +* Check the ast_rate function works OK. + call ast_setl( swm, 'Invert', .false., status ) + + at(1) = 20.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 10.0 ) .gt. 1.0E-6 ) call stopit( 128, r, status ) + + at(1) = 120.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 11.0 ) .gt. 1.0E-6 ) call stopit( 129, r, status ) + + call ast_setl( swm, 'Invert', .true., status ) + + at(1) = 1700.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 1.0/10.0 ) .gt. 1.0E-6 ) call stopit( 130, r, + : status ) + + at(1) = 1900.0 + r = ast_rate( swm, at, 1, 1, status ) + if( abs( r - 1.0/11.0 ) .gt. 1.0E-6 ) call stopit( 131, r, + : status ) + + call ast_setl( swm, 'Invert', .false., status ) + + +* A 2D input grid has bounds (1:180,1:100). The area (10:80,10:80) +* and the area (100:170,10:80) both contain images of a fixed part of +* the sky (e.g. in different polarisations). The WCS for each sub-region +* is derived from a single FITS-WCS header,with suitably shifted origin. +* -------------------------------------------------------------------- + card(1) = 'CRPIX1 = 45' + card(2) = 'CRPIX2 = 45' + card(3) = 'CRVAL1 = 45' + card(4) = 'CRVAL2 = 89.9' + card(5) = 'CDELT1 = -0.01' + card(6) = 'CDELT2 = 0.01' + card(7) = 'CTYPE1 = ''RA---TAN''' + card(8) = 'CTYPE2 = ''DEC--TAN''' + + fc = ast_fitschan( AST_NULL, AST_NULL, ' ', status ) + do i = 1, 8 + call ast_putfits( fc, card(i), .false., status ) + end do + call ast_clear( fc, 'Card', status ) + fs = ast_read( fc, status ) + rm(1) = ast_getMapping( fs, ast__base, ast__current, status ) + + card(1) = 'CRPIX1 = 135' + call ast_clear( fc, 'Card', status ) + do i = 1, 8 + call ast_putfits( fc, card(i), .true., status ) + end do + call ast_clear( fc, 'Card', status ) + fs = ast_read( fc, status ) + rm(2) = ast_getMapping( fs, ast__base, ast__current, status ) + +* Forward Selector Mapping: A SelectorMap which encapsulates the two Box +* Regions in GRID coords. + gridframe = ast_getframe( fs, AST__BASE, status ) + + p1(1) = 10 + p1(2) = 10 + p2(1) = 80 + p2(2) = 80 + box(1) = ast_box( gridframe, 1, p1, p2, AST__NULL, ' ', status ) + + p1(1) = 100 + p1(2) = 10 + p2(1) = 170 + p2(2) = 80 + box(2) = ast_box( gridframe, 1, p1, p2, AST__NULL, ' ', status ) + + fs = ast_selectormap( 2, box, AST__BAD, ' ', status ) + +* Inverse Selector Mapping: A PermMap which has an inverse transformation +* which gives an input value of 1 for all output values. This means that the +* inverse transformation of the SwitchMap always returns a GRID position in +* the lower (left-hand) of the two images. + inperm(1) = -1 + outperm(1) = 0 + outperm(2) = 0 + is = ast_permmap( 1, inperm, 2, outperm, 1.0D0, ' ', status ) + +* Now create the SwitchMap. + swm = ast_switchmap( fs, is, 2, rm, ' ', status ) + +* Test the forward transformation of the SwitchMap. To add complication, +* we first invert the SwitchMap and then use ast_trann in the inverse +* direction (the two inversions cancel resulting in the forward +* transformation being used). We alo invert the selector mapping (this +* should have no effect on the SwitchMap). + call ast_invert( swm, status ) + call ast_invert( fs, status ) + + in(1,1) = 5.0 + in(1,2) = 5.0 + + in(2,1) = 50.0 + in(2,2) = 50.0 + + in(3,1) = 90.0 + in(3,2) = 50.0 + + in(4,1) = 140.0 + in(4,2) = 50.0 + + call ast_trann( swm, 4, 2, 4, in, .false., 2, 4, out, status ) + +* Transform these same positions using the Mapping for the left hand +* image obtained from the FITS header + call ast_trann( rm(1), 4, 2, 4, in, .true., 2, 4, rmout, status ) + +* Check the SwitchMap results. Points 1 and 3 should be bad because they +* fall outside either image. points 2 and 4 should both be equal to the +* result of transforming point 2 using the FITS-WCS mapping. + + if( out(1,1) .ne. AST__BAD ) then + call stopit( 132, out(1,1), status ) + + else if( out(1,2) .ne. AST__BAD ) then + call stopit( 133, out(1,1), status ) + + else if( out(2,1) .ne. rmout(2,1) ) then + call stopit( 134, out(2,1), status ) + + else if( out(2,2) .ne. rmout(2,2) ) then + call stopit( 135, out(2,2), status ) + + else if( out(3,1) .ne. AST__BAD ) then + call stopit( 136, out(1,1), status ) + + else if( out(3,2) .ne. AST__BAD ) then + call stopit( 137, out(1,1), status ) + + else if( out(4,1) .ne. rmout(2,1) ) then + call stopit( 138, out(2,1), status ) + + else if( out(4,2) .ne. rmout(2,2) ) then + call stopit( 139, out(2,2), status ) + + end if + + +* Test the inverse transformation of the SwitchMap. + call ast_trann( swm, 4, 2, 4, out, .true., 2, 4, in, status ) + + if( in(1,1) .ne. AST__BAD ) then + call stopit( 140, in(1,1), status ) + + else if( in(1,2) .ne. AST__BAD ) then + call stopit( 141, in(1,1), status ) + + else if( abs( in(2,1) - 50.0 ) .gt. 1.0E-6 ) then + call stopit( 142, in(2,1), status ) + + else if( abs( in(2,2) - 50.0 ) .gt. 1.0E-6 ) then + call stopit( 143, in(2,2), status ) + + else if( in(3,1) .ne. AST__BAD ) then + call stopit( 144, in(1,1), status ) + + else if( in(3,2) .ne. AST__BAD ) then + call stopit( 145, in(1,1), status ) + + else if( abs( in(4,1) - 50.0 ) .gt. 1.0E-6 ) then + call stopit( 146, in(2,1), status ) + + else if( abs( in(4,2) - 50.0 ) .gt. 1.0E-6 ) then + call stopit( 147, in(2,2), status ) + + end if + +* Check no simplification is done on a single non-inverted SwicthMap. + call ast_setl( swm, 'Invert', .false., status ) + swm2 = ast_simplify( swm, status ) + call compare( swm, swm2, 'ast_equal 3', status ) + +* Check an inverted SwitchMap simplies to an non-inverted switchmap. + call ast_setl( swm, 'Invert', .true., status ) + swm2 = ast_simplify( swm, status ) + if( ast_getl( swm2, 'Invert', status ) ) then + call stopit( 148, 1.0D0, status ) + end if + +* Check two adjacent opposite SwitchMaps cancel. + swm2 = ast_copy( swm, status ) + call ast_invert( swm2, status ) + cm = ast_cmpmap( swm, swm2, .true., ' ', status ) + cm2 = ast_simplify( cm, status ) + if( .not. ast_isaunitmap( cm2, status ) ) then + call stopit( 149, 1.0D0, status ) + end if + + cm = ast_cmpmap( swm2, swm, .true., ' ', status ) + cm2 = ast_simplify( cm, status ) + if( .not. ast_isaunitmap( cm2, status ) ) then + call stopit( 150, 1.0D0, status ) + end if + +* Check that the SwitchMap can be written out to a AstChannel and read +* back again succesfully. + call checkdump( swm, 'Channel test 3', status ) + +* Check the ast_rate function works OK. + call ast_setl( swm, 'Invert', .false., status ) + + at(1) = 140.0 + at(2) = 50.0 + r1 = ast_rate( swm, at, 1, 1, status ) + + at(1) = 50.0 + r2 = ast_rate( rm(1), at, 1, 1, status ) + + if( abs( r1 - r2 ) .gt. abs( 1.0E-6*r2 ) ) call stopit( 151, r1, + : status ) + + at(1) = 140.0 + r1 = ast_rate( swm, at, 2, 2, status ) + + at(1) = 50.0 + r2 = ast_rate( rm(1), at, 2, 2, status ) + + if( abs( r1 - r2 ) .gt. abs( 1.0E-6*r2 ) ) call stopit( 152, r1, + : status ) + + at(1) = 140.0 + r1 = ast_rate( swm, at, 1, 2, status ) + + at(1) = 50.0 + r2 = ast_rate( rm(1), at, 1, 2, status ) + + if( abs( r1 - r2 ) .gt. abs( 1.0E-6*r2 ) ) call stopit( 153, r1, + : status ) + + at(1) = 140.0 + r1 = ast_rate( swm, at, 2, 1, status ) + + at(1) = 50.0 + r2 = ast_rate( rm(1), at, 2, 1, status ) + + if( abs( r1 - r2 ) .gt. abs( 1.0E-6*r2 ) ) call stopit( 154, r1, + : status ) + + + + + mc = ast_tune( 'MemoryCaching', mc, status ) + oc = ast_tune( 'ObjectCaching', oc, status ) + call ast_end( status ) + + call ast_activememory( 'testswitchmap' ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All SwitchMap tests passed' + else + write(*,*) 'SwitchMap tests failed' + end if + + end + + + subroutine stopit( i, r, status ) + implicit none + include 'SAE_PAR' + integer i, status + double precision r + if( status .eq. sai__ok ) then + write( *,* ) 'Error ',i,': ',r + status = sai__error + end if + end + + + + subroutine checkdump( obj, text, status ) + + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap, ibuf, + : len1, len2 + external mysource, mysink + character buf1*45000 + character buf2*45000 + + common /ss1/ buf1 + common /ss3/ buf2 + common /ss2/ next, end, ll, ibuf + + if( status .ne. sai__ok ) return + +* Create a Channel which reads and writes to an internal string buffer. + ch = ast_channel( mysource, mysink, ' ', status ) + +* Write the supplied Object out to this Channel. + ll = 160 + next = 1 + ibuf = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( 1000, 1.0D0, status ) + end if + len1 = next + +* Read an Object back from this Channel. + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( 1001, 1.0D0, status ) + end if + +* Check that it is of the same class as the supplied object. + if( ast_getc( result, 'Class', status ) .ne. + : ast_getc( obj, 'Class', status ) ) then + write(*,*) text + call stopit( 1002, 1.0D0, status ) + end if + +* Compare it to the suuplied object. + call compare( obj, result, text, status ) + + end + + + + + subroutine compare( obj1, obj2, text, status ) + + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj1, status, next, end, ch, result, ll, overlap, ibuf, + : len1, len2, obj2 + external mysource, mysink + character buf1*45000 + character buf2*45000 + + common /ss1/ buf1 + common /ss3/ buf2 + common /ss2/ next, end, ll, ibuf + + if( status .ne. sai__ok ) return + +* Create a Channel which reads and writes to an internal string buffer. + ch = ast_channel( mysource, mysink, ' ', status ) + +* Write the first supplied Object out to this Channel, using buf1 + ll = 160 + next = 1 + ibuf = 1 + if( ast_write( ch, obj1, status ) .ne.1 ) then + write(*,*) text + call stopit( 2000, 1.0D0, status ) + end if + len1 = next + +* Write the second object out to the second buffer. + ll = 160 + next = 1 + ibuf = 2 + if( ast_write( ch, obj2, status ) .ne.1 ) then + write(*,*) text + call stopit( 2001, 1.0D0, status ) + end if + len2 = next + +* Compare the contents of the two buffers. + if( buf1( : len1 ) .ne. buf2( : len2 ) ) then + write(*,*) text + write(*,*) len1, len2 + call ast_show( obj1, status ) + call ast_show( obj2, status ) + call stopit( 2002, 1.0D0, status ) + + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll, ibuf + character buf1*45000 + character buf2*45000 + + common /ss1/ buf1 + common /ss3/ buf2 + common /ss2/ next, end, ll, ibuf + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + if( ibuf .eq. 1 ) then + call ast_putline( buf1, -1, status ) + else + call ast_putline( buf2, -1, status ) + endif + else + if( ibuf .eq. 1 ) then + call ast_putline( buf1( next : ), ll, status ) + else + call ast_putline( buf2( next : ), ll, status ) + endif + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll, ibuf + character line*1000 + character buf1*45000 + character buf2*45000 + + common /ss1/ buf1 + common /ss3/ buf2 + common /ss2/ next, end, ll, ibuf + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + + if( ibuf .eq. 1 ) then + buf1( next : ) = line( f : l ) + else + buf2( next : ) = line( f : l ) + end if + + l = l - f + 1 + + if( next + ll - 1 .ge. 45000 ) then + write(*,*) 'Buffer overflow in mysink!!' + status = SAI__ERROR + + else if( l .gt. ll ) then + write(*,*) + if( ibuf .eq. 1 ) then + write(*,*) buf1( next : next + l) + else + write(*,*) buf2( next : next + l) + end if + write(*,*) 'Line length ',l,' greater than ',ll + write(*,*) 'Line overflow in mysink!!' + status = SAI__ERROR + else + end = next + l + if( ibuf .eq. 1 ) then + buf1( end : next + ll - 1 ) = ' ' + else + buf2( end : next + ll - 1 ) = ' ' + end if + endif + + next = next + ll + + end + + + + diff --git a/ast_tester/testtable.f b/ast_tester/testtable.f new file mode 100644 index 0000000..907dd66 --- /dev/null +++ b/ast_tester/testtable.f @@ -0,0 +1,537 @@ + program testtable + implicit none + + include 'AST_PAR' + include 'AST_ERR' + include 'SAE_PAR' + + integer status, table, table2, dims( 7 ), ival, l, nval + byte bytes(1,2),bval + real rval + character cval*30, text(2,2)*10 + + +c call ast_watchmemory(483) + + + status = sai__ok + call err_mark( status ) + call ast_begin( status ) + + table = ast_table( ' ', status ) + + call ast_mapput0i( table, 'Fred', 123, 'com 1', status ) + if( status .eq. AST__BADKEY ) then + call err_annul( status ) + else + if( status .ne. sai__ok ) call err_annul( status ) + call stopit( status, 'Table error 1' ) + endif + + call ast_mapput0i( table, 'Fred(2)', 123, 'com 1', status ) + if( status .eq. AST__BADKEY ) then + call err_annul( status ) + else + if( status .ne. sai__ok ) call err_annul( status ) + call stopit( status, 'Table error 2' ) + endif + + dims( 1 ) = 5 + dims( 2 ) = 2 + call ast_addcolumn( table, 'Fred', AST__FLOATTYPE, 2, dims, + : ' ', status ) + + if( ast_geti( table, 'NColumn', status ) .ne. 1 ) then + call stopit( status, 'Table error 2b' ) + endif + + if( ast_columnname( table, 1, status ) .ne. 'FRED' ) then + call stopit( status, 'Table error 2c' ) + endif + + if( ast_geti( table, 'ColumnType(Fred)', status ) .ne. + : AST__FLOATTYPE ) then + call stopit( status, 'Table error 2d' ) + endif + + call ast_columnshape( table, 'Fred', 2, nval, dims, status ) + if( nval .ne. 2 ) then + call stopit( status, 'Table error 2e' ) + else if( dims( 1 ) .ne. 5 ) then + call stopit( status, 'Table error 2f' ) + else if( dims( 2 ) .ne. 2 ) then + call stopit( status, 'Table error 2g' ) + endif + + if( ast_getc( table, 'ColumnUnit(Fred)', status ) .ne. ' ' ) then + call stopit( status, 'Table error 2h' ) + endif + + call ast_mapput0i( table, 'Fred(2)', 123, 'com 1', status ) + if( status .eq. AST__BADTYP ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, 'Table error 3' ) + endif + + call ast_mapput0r( table, 'Fred(2)', 123, 'com 1', status ) + if( status .eq. AST__BADTYP ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, 'Table error 4' ) + endif + + call ast_addcolumn( table, 'Fred', AST__FLOATTYPE, 0, 0, + : 'pW', status ) + if( status .eq. AST__OLDCOL ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, 'Table error 5' ) + endif + + call ast_removecolumn( table, 'Fred', status ) + + if( ast_geti( table, 'NColumn', status ) .ne. 0 ) then + call stopit( status, 'Table error 5b' ) + endif + + call ast_addcolumn( table, 'Fred', AST__FLOATTYPE, 0, 0, + : 'pW', status ) + + call ast_mapput0r( table, 'Fred(1)', -123.0, 'com 1', status ) + call ast_mapput0r( table, 'Fred(2)', 123.0, 'com 2', status ) + + if( ast_mapget0r( table, 'Fred(2)', rval, status ) ) then + if( rval .ne. 123.0 ) call stopit( status, 'Table error 6' ) + else + call stopit( status, 'Table error 7' ) + endif + + if( ast_getc( table, 'ColumnUnit(Fred)', status ) .ne. 'pW' ) then + call stopit( status, 'Table error 7b' ) + endif + + call ast_addcolumn( table, 'Dick', AST__OBJECTTYPE, 0, 0, + : 'W/m**2', status ) + call ast_mapput0a( table, 'Dick(1)', table, 'com 1', status ) + if( status .eq. AST__KYCIR ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, 'Table error 8' ) + endif + + if( ast_geti( table, 'NColumn', status ) .ne. 2 ) then + call stopit( status, 'Table error 8b' ) + endif + + if( ast_columnname( table, 1, status ) .ne. 'FRED' ) then + call stopit( status, 'Table error 8c' ) + endif + + if( ast_columnname( table, 2, status ) .ne. 'DICK' ) then + call stopit( status, 'Table error 8d' ) + endif + + call ast_removecolumn( table, 'Dick', status ) + + if( ast_geti( table, 'NRow', status ) .ne. 2 ) then + call stopit( status, 'Table error 9' ) + endif + + if( ast_mapget0r( table, 'Fred(3)', rval, status ) ) then + call stopit( status, 'Table error 10' ) + endif + + if( ast_mapget0i( table, 'Fred(2)', ival, status ) ) then + if( ival .ne. 123 ) call stopit( status, 'Table error 11' ) + else + call stopit( status, 'Table error 12' ) + endif + + if( ast_mapget0c( table, 'Fred(2)', cval, l, status ) ) then + if( cval .ne. '123' ) call stopit( status, 'Table error 13' ) + if( l .ne. 3 ) call stopit( status, 'Table error 14' ) + else + call stopit( status, 'Table error 15' ) + endif + + call ast_removerow( table, 3, status ) + if( ast_geti( table, 'NRow', status ) .ne. 2 ) then + call stopit( status, 'Table error 16' ) + endif + + call ast_removerow( table, 2, status ) + if( ast_geti( table, 'NRow', status ) .ne. 1 ) then + call stopit( status, 'Table error 17' ) + endif + + if( ast_mapget0r( table, 'Fred(2)', rval, status ) ) then + call stopit( status, 'Table error 18' ) + endif + + call ast_addparameter( table, 'COLOUR', status ) + if( .not. ast_hasparameter( table, 'COLOUR', status ) ) then + call stopit( status, 'Table error 18_1' ) + endif + + call ast_mapput0C( table, 'COLOUR', 'Red', ' ', status ) + if( ast_mapget0c( table, 'COLOUR', cval, l, status ) ) then + if( cval .ne. 'Red' ) call stopit( status, 'Table error 18_3' ) + if( l .ne. 3 ) call stopit( status, 'Table error 18_4' ) + else + call stopit( status, 'Table error 18_5' ) + endif + + call ast_removeparameter( table, 'COLOUR', status ) + if( ast_hasparameter( table, 'COLOUR', status ) ) then + call stopit( status, 'Table error 18_2' ) + endif + + call checkDump( table, 'checkDump 1 ', status ) + + table2 = ast_copy( table, status ) + + if( ast_mapget0c( table2, 'Fred(1)', cval, l, status ) ) then + if( cval .ne. '-123' ) call stopit( status, 'Table error 19' ) + if( l .ne. 4 ) call stopit( status, 'Table error 20' ) + else + call stopit( status, 'Table error 21' ) + endif + + dims( 1 ) = 2 + dims( 2 ) = 2 + call ast_addcolumn( table, 'Dick', AST__STRINGTYPE, 2, dims, + : ' ', status ) + + text(1,1) = 'One' + text(2,1) = 'two' + text(1,2) = 'three' + text(2,2) = 'FouR' + + call ast_mapput1c( table, 'Dick(4)', 4, text, 'jjjj', status ) + if( ast_mapget1c( table, 'Dick(4)', 4, nval, text, status ) ) then + + if( text(1,1) .ne. 'One' ) + : call stopit( status, 'Table error 22' ) + if( text(2,1) .ne. 'two' ) + : call stopit( status, 'Table error 23' ) + if( text(1,2) .ne. 'three' ) + : call stopit( status, 'Table error 24' ) + if( text(2,2) .ne. 'FouR' ) + : call stopit( status, 'Table error 25' ) + + if( nval .ne. 4 ) call stopit( status, 'Table error 26' ) + else + call stopit( status, 'Table error 27' ) + endif + + call ast_mapputelemc( table, 'Dick(4)', 3, 'OHOHOHOH', status ) + if( ast_mapgetelemc( table, 'Dick(4)', 3, cval, status ) ) then + if( cval .ne. 'OHOHOHOH' ) + : call stopit( status, 'Table error 28' ) + else + call stopit( status, 'Table error 29' ) + endif + + if( ast_geti( table, 'columnlenc(Dick)', status ) .ne. 10 ) then + call stopit( status, 'Table error 29b' ) + endif + + dims( 1 ) = 1 + dims( 2 ) = 2 + call ast_addcolumn( table, 'HeHe', AST__BYTETYPE, 2, dims, + : ' ', status ) + bytes(1,1) = 127 + bytes(1,2) = 255 + call ast_mapput1b( table, 'HeHe(2)', 2, bytes, 'jjjj', status ) + if( ast_mapget1b( table, 'HeHe(2)', 2, nval, bytes, + : status ) ) then + if( nval .ne. 2 ) call stopit( status, 'Table error 30' ) + if( bytes(1,1) .ne. 127 ) call stopit( status, + : 'Table error 31' ) + if( bytes(1,2) .ne. -1 ) call stopit( status, + : 'Table error 32' ) + else + call stopit( status, 'Table error 33' ) + endif + + call ast_addcolumn( table, 'GoGo', AST__BYTETYPE, 0, 0, + : ' ', status ) + call ast_mapput0b( table, 'GoGo(2)', -10, ' ', status ) + if( ast_mapget0b( table, 'GoGo(2)', bval, status ) ) then + if( bval .ne. -10 ) call stopit( status, 'Table error 33' ) + else + call stopit( status, 'Table error 34' ) + endif + + call checkpurge( status ) + + if( .not. ast_hascolumn( table, 'GoGo', status ) ) then + call stopit( status, 'Table error 35' ) + else if( ast_hascolumn( table, 'dodo', status ) ) then + call stopit( status, 'Table error 36' ) + endif + + call ast_removecolumn( table, 'GoGo', status ) + if( ast_hascolumn( table, 'GoGo', status ) ) then + call stopit( status, 'Table error 37' ) + endif + + + + call ast_end( status ) + call err_rlse( status ) + +c call ast_activememory( 'testtable' ) + call ast_flushmemory( 1 ); + + if( status .eq. sai__ok ) then + write(*,*) 'All Table tests passed' + else + write(*,*) 'Table tests failed' + end if + + end + + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + subroutine checkdump( obj, text, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*),key*30,txt1*50,txt2*50 + integer obj, status, next, end, ch, result, ll, overlap, size, + : i, type,obj1,obj2,l1,l2,nl,nrow,nrowold + external mysource, mysink + character buf*400000 + + common /ss1/ buf + common /ss2/ next, end, ll, nl + + if( status .ne. sai__ok ) return + + ch = ast_channel( mysource, mysink, ' ', status ) + + + + nl = 0 + ll = 110 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + next = 1 + nl = 0 + result = ast_read( ch, status ) + + + + + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + + if( .not. ast_isatable( result, status ) ) then + call stopit( status, 'Object read from channel is not a table') + end if + + nrowold = ast_geti( obj, 'NRow', status ) + nrow = ast_geti( result, 'NRow', status ) + if( nrow .ne. nrowold ) then + write(*,*) nrow, nrowold + call stopit( status, 'checkDump 0' ) + endif + + size = ast_mapsize( result, status ) + if( ast_mapsize( obj, status ) .ne. size ) then + write(*,*) size, ast_mapsize( obj, status ) + call stopit( status, 'checkDump 1' ) + else + do i = 1, size + key = ast_mapkey( result, i, status ) + type = ast_maptype( result, key, status ) + if( ast_maptype( obj, key, status ) .ne. type ) then + write(*,*) type, ast_maptype( obj, key, status ) + call stopit( status, 'checkDump 4' ) + else + + if( type .eq. AST__OBJECTTYPE ) then + + if( .not. ast_mapGet0A( result, key, obj1, + : status ) ) call stopit( status, 'checkDump 5' ) + if( .not. ast_mapGet0A( obj, key, obj2, + : status ) ) call stopit( status, 'checkDump 6' ) + if( ast_GetC( obj1, 'class', status ) .ne. + : ast_GetC( obj2, 'class', status ) ) then + call stopit( status, 'checkDump 7' ) + end if + + else + + if( .not. ast_mapGet0C( result, key, txt1, l1, + : status ) ) call stopit( status, 'checkDump 8' ) + if( .not. ast_mapGet0C( obj, key, txt2, l2, + : status ) ) call stopit( status, 'checkDump 9' ) + if( txt1( : l1 ) .ne. txt2( : l2 ) .or. + : l1 .ne. l2 ) then + call stopit( status, 'checkDump 10' ) + end if + + end if + end if + end do + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll, nl + character buf*400000 + + common /ss1/ buf + common /ss2/ next, end, ll,nl + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + +c write(*,*) buf( next : next + ll - 1 ) + call ast_putline( buf( next : ), ll, status ) + nl = nl + 1 + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll, nl + character buf*400000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll, nl + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 400000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + nl = nl + 1 + endif + + next = next + ll + + end + + + subroutine checkpurge( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, table, ival + + if( status .ne. sai__ok ) return + + table = ast_table( ' ', status ) + + call ast_addcolumn( table, 'Fred', AST__INTTYPE, 0, 0, ' ', + : status ) + call ast_addcolumn( table, 'Tom', AST__INTTYPE, 0, 0, ' ', + : status ) + + call ast_mapput0i( table, 'Fred(2)', 123, ' ', status ) + call ast_mapput0i( table, 'Fred(4)', 456, ' ', status ) + call ast_mapput0i( table, 'Tom(1)', -123, ' ', status ) + call ast_mapput0i( table, 'Tom(2)', -456, ' ', status ) + call ast_mapput0i( table, 'Tom(6)', 0, ' ', status ) + + if( ast_geti( table, 'NRow', status ) .ne. 6 ) then + call stopit( status, 'Table error purge-1' ) + endif + + call ast_mapremove( table, 'Tom(6)', status ) + if( ast_geti( table, 'NRow', status ) .ne. 6 ) then + call stopit( status, 'Table error purge-2' ) + endif + + call ast_purgerows( table, status ) + if( ast_geti( table, 'NRow', status ) .ne. 3 ) then + write(*,*) 'ZZ: ',ast_geti( table, 'NRow', status ) + call stopit( status, 'Table error purge-3' ) + endif + + if( ast_mapget0i( table, 'Tom(1)', ival, status ) ) then + if( ival .ne. -123 ) call stopit( status, + : 'Table error purge-4' ) + else + call stopit( status, 'Table error purge-5' ) + endif + + if( ast_mapget0i( table, 'Tom(2)', ival, status ) ) then + if( ival .ne. -456 ) call stopit( status, + : 'Table error purge-6' ) + else + call stopit( status, 'Table error purge-7' ) + endif + + if( ast_mapget0i( table, 'Fred(1)', ival, status ) ) then + call stopit( status, 'Table error purge-8' ) + endif + + if( ast_mapget0i( table, 'Fred(2)', ival, status ) ) then + if( ival .ne. 123 ) call stopit( status, + : 'Table error purge-9' ) + else + call stopit( status, 'Table error purge-10' ) + endif + + if( ast_mapget0i( table, 'Fred(3)', ival, status ) ) then + if( ival .ne. 456 ) call stopit( status, + : 'Table error purge-11' ) + else + call stopit( status, 'Table error purge-12' ) + endif + + call ast_annul( table, status ) + + end + diff --git a/ast_tester/testtime.f b/ast_tester/testtime.f new file mode 100644 index 0000000..deb49f6 --- /dev/null +++ b/ast_tester/testtime.f @@ -0,0 +1,888 @@ + program testtime + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'AST_ERR' + + character txt*40 + double precision xin, xout, ct, ctl, origin + integer status, tf, tf1, tf2, fs, n, chr_len, nc + status = sai__ok + + call ast_begin( status ) + +c call ast_SetWatchId( 740050 ) + +c +c Test default attribute values +c + tf = ast_timeframe( ' ', status ) + + if( ast_getc( tf, 'System', status ) .ne. 'MJD' ) then + write(*,*) ast_getc( tf, 'System', status ) + call stopit( status, 'error 1' ) + endif + + if( ast_getd( tf, 'TimeOrigin', status ) .ne. 0.0 ) then + write(*,*) ast_getd( tf, 'TimeOrigin', status ) + call stopit( status, 'error 2' ) + endif + + if( ast_getc( tf, 'ObsLon', status ) .ne. 'E0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLon', status ) + call stopit( status, 'error 3' ) + endif + + if( ast_getc( tf, 'ObsLat', status ) .ne. 'N0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLat', status ) + call stopit( status, 'error 4' ) + endif + + if( ast_getc( tf, 'TimeScale', status ) .ne. 'TAI' ) then + write(*,*) ast_getc( tf, 'TimeScale', status ) + call stopit( status, 'error 5' ) + endif + + if( ast_getc( tf, 'AlignTimeScale', status ) .ne. 'TAI' ) then + write(*,*) ast_getc( tf, 'AlignTimeScale', status ) + call stopit( status, 'error 6' ) + endif + + if( ast_geti( tf, 'naxes', status ) .ne. 1 ) then + write(*,*) ast_getc( tf, 'Naxes', status ) + call stopit( status, 'error 7' ) + endif + + if( ast_getd( tf, 'Epoch', status ) .ne. 2000.0 ) then + write(*,*) ast_getd( tf, 'Epoch', status ) + call stopit( status, 'error 8' ) + endif + + if( ast_getc( tf, 'Label', status ) .ne. + : 'Modified Julian Date' ) then + write(*,*) ast_getc( tf, 'Label', status ) + call stopit( status, 'error 9' ) + endif + + if( ast_getc( tf, 'Symbol', status ) .ne. 'MJD' ) then + write(*,*) ast_getc( tf, 'Symbol', status ) + call stopit( status, 'error 10' ) + endif + + if( ast_getc( tf, 'Title', status ) .ne. + : 'Modified Julian Date' ) then + write(*,*) ast_getc( tf, 'Title', status ) + call stopit( status, 'error 11' ) + endif + + if( ast_getc( tf, 'unit', status ) .ne. 'd' ) then + write(*,*) ast_getc( tf, 'unit', status ) + call stopit( status, 'error 12' ) + endif + + if( ast_getc( tf, ' domain ', status ) .ne. 'TIME' ) then + write(*,*) ast_getc( tf, ' domain ', status ) + call stopit( status, 'error 13' ) + endif + + if( ast_getc( tf, 'alignSystem', status ) .ne. 'MJD' ) then + write(*,*) ast_getc( tf, 'alignSystem', status ) + call stopit( status, 'error 14' ) + endif + +c +c Test dependency of default attribute values on System +c + call ast_setc( tf, 'system', 'jd', status ) + + if( ast_getc( tf, 'System', status ) .ne. 'JD' ) then + write(*,*) ast_getc( tf, 'System', status ) + call stopit( status, 'error 1b' ) + endif + + if( ast_getd( tf, 'TimeOrigin', status ) .ne. 0.0 ) then + write(*,*) ast_getd( tf, 'TimeOrigin', status ) + call stopit( status, 'error 2b' ) + endif + + if( ast_getc( tf, 'ObsLon', status ) .ne. 'E0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLon', status ) + call stopit( status, 'error 3b' ) + endif + + if( ast_getc( tf, 'ObsLat', status ) .ne. 'N0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLat', status ) + call stopit( status, 'error 4b' ) + endif + + if( ast_getc( tf, 'TimeScale', status ) .ne. 'TAI' ) then + write(*,*) ast_getc( tf, 'TimeScale', status ) + call stopit( status, 'error 5b' ) + endif + + if( ast_getc( tf, 'AlignTimeScale', status ) .ne. 'TAI' ) then + write(*,*) ast_getc( tf, 'AlignTimeScale', status ) + call stopit( status, 'error 6b' ) + endif + + if( ast_geti( tf, 'naxes', status ) .ne. 1 ) then + write(*,*) ast_getc( tf, 'Naxes', status ) + call stopit( status, 'error 7b' ) + endif + + if( ast_getd( tf, 'Epoch', status ) .ne. 2000.0 ) then + write(*,*) ast_getd( tf, 'Epoch', status ) + call stopit( status, 'error 8b' ) + endif + + if( ast_getc( tf, 'Label', status ) .ne. + : 'Julian Date' ) then + write(*,*) ast_getc( tf, 'Label', status ) + call stopit( status, 'error 9b' ) + endif + + if( ast_getc( tf, 'Symbol', status ) .ne. 'JD' ) then + write(*,*) ast_getc( tf, 'Symbol', status ) + call stopit( status, 'error 10b' ) + endif + + if( ast_getc( tf, 'Title', status ) .ne. + : 'Julian Date' ) then + write(*,*) ast_getc( tf, 'Title', status ) + call stopit( status, 'error 11b' ) + endif + + if( ast_getc( tf, 'unit', status ) .ne. 'd' ) then + write(*,*) ast_getc( tf, 'unit', status ) + call stopit( status, 'error 12b' ) + endif + + if( ast_getc( tf, ' domain ', status ) .ne. 'TIME' ) then + write(*,*) ast_getc( tf, ' domain ', status ) + call stopit( status, 'error 13b' ) + endif + + if( ast_getc( tf, 'alignSystem', status ) .ne. 'MJD' ) then + write(*,*) ast_getc( tf, 'alignSystem', status ) + call stopit( status, 'error 14b' ) + endif + + + + call ast_setc( tf, 'system', 'jepoch', status ) + + if( ast_getc( tf, 'System', status ) .ne. 'JEPOCH' ) then + write(*,*) ast_getc( tf, 'System', status ) + call stopit( status, 'error 1c' ) + endif + + if( ast_getd( tf, 'TimeOrigin', status ) .ne. 0.0 ) then + write(*,*) ast_getd( tf, 'TimeOrigin', status ) + call stopit( status, 'error 2c' ) + endif + + if( ast_getc( tf, 'ObsLon', status ) .ne. 'E0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLon', status ) + call stopit( status, 'error 3c' ) + endif + + if( ast_getc( tf, 'ObsLat', status ) .ne. 'N0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLat', status ) + call stopit( status, 'error 4c' ) + endif + + if( ast_getc( tf, 'TimeScale', status ) .ne. 'TAI' ) then + write(*,*) ast_getc( tf, 'TimeScale', status ) + call stopit( status, 'error 5c' ) + endif + + if( ast_getc( tf, 'AlignTimeScale', status ) .ne. 'TAI' ) then + write(*,*) ast_getc( tf, 'AlignTimeScale', status ) + call stopit( status, 'error 6c' ) + endif + + if( ast_geti( tf, 'naxes', status ) .ne. 1 ) then + write(*,*) ast_getc( tf, 'Naxes', status ) + call stopit( status, 'error 7c' ) + endif + + if( ast_getd( tf, 'Epoch', status ) .ne. 2000.0 ) then + write(*,*) ast_getd( tf, 'Epoch', status ) + call stopit( status, 'error 8c' ) + endif + + if( ast_getc( tf, 'Label', status ) .ne. + : 'Julian Epoch' ) then + write(*,*) ast_getc( tf, 'Label', status ) + call stopit( status, 'error 9c' ) + endif + + if( ast_getc( tf, 'Symbol', status ) .ne. 'JEP' ) then + write(*,*) ast_getc( tf, 'Symbol', status ) + call stopit( status, 'error 10c' ) + endif + + if( ast_getc( tf, 'Title', status ) .ne. + : 'Julian Epoch' ) then + write(*,*) ast_getc( tf, 'Title', status ) + call stopit( status, 'error 11c' ) + endif + + if( ast_getc( tf, 'unit', status ) .ne. 'yr' ) then + write(*,*) ast_getc( tf, 'unit', status ) + call stopit( status, 'error 12c' ) + endif + + if( ast_getc( tf, ' domain ', status ) .ne. 'TIME' ) then + write(*,*) ast_getc( tf, ' domain ', status ) + call stopit( status, 'error 13c' ) + endif + + if( ast_getc( tf, 'alignSystem', status ) .ne. 'MJD' ) then + write(*,*) ast_getc( tf, 'alignSystem', status ) + call stopit( status, 'error 14c' ) + endif + + + call ast_setc( tf, 'system', 'bepoch', status ) + + if( ast_getc( tf, 'System', status ) .ne. 'BEPOCH' ) then + write(*,*) ast_getc( tf, 'System', status ) + call stopit( status, 'error 1d' ) + endif + + if( ast_getd( tf, 'TimeOrigin', status ) .ne. 0.0 ) then + write(*,*) ast_getd( tf, 'TimeOrigin', status ) + call stopit( status, 'error 2d' ) + endif + + if( ast_getc( tf, 'ObsLon', status ) .ne. 'E0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLon', status ) + call stopit( status, 'error 3d' ) + endif + + if( ast_getc( tf, 'ObsLat', status ) .ne. 'N0:00:00.00' ) then + write(*,*) ast_getc( tf, 'ObsLat', status ) + call stopit( status, 'error 4d' ) + endif + + if( ast_getc( tf, 'TimeScale', status ) .ne. 'TT' ) then + write(*,*) ast_getc( tf, 'TimeScale', status ) + call stopit( status, 'error 5d' ) + endif + + if( ast_getc( tf, 'AlignTimeScale', status ) .ne. 'TAI' ) then + write(*,*) ast_getc( tf, 'AlignTimeScale', status ) + call stopit( status, 'error 6d' ) + endif + + if( ast_geti( tf, 'naxes', status ) .ne. 1 ) then + write(*,*) ast_getc( tf, 'Naxes', status ) + call stopit( status, 'error 7d' ) + endif + + if( ast_getd( tf, 'Epoch', status ) .ne. 2000.0 ) then + write(*,*) ast_getd( tf, 'Epoch', status ) + call stopit( status, 'error 8d' ) + endif + + if( ast_getc( tf, 'Label', status ) .ne. + : 'Besselian Epoch' ) then + write(*,*) ast_getc( tf, 'Label', status ) + call stopit( status, 'error 9d' ) + endif + + if( ast_getc( tf, 'Symbol', status ) .ne. 'BEP' ) then + write(*,*) ast_getc( tf, 'Symbol', status ) + call stopit( status, 'error 10d' ) + endif + + if( ast_getc( tf, 'Title', status ) .ne. + : 'Besselian Epoch' ) then + write(*,*) ast_getc( tf, 'Title', status ) + call stopit( status, 'error 11d' ) + endif + + if( ast_getc( tf, 'unit', status ) .ne. 'yr' ) then + write(*,*) ast_getc( tf, 'unit', status ) + call stopit( status, 'error 12d' ) + endif + + if( ast_getc( tf, ' domain ', status ) .ne. 'TIME' ) then + write(*,*) ast_getc( tf, ' domain ', status ) + call stopit( status, 'error 13d' ) + endif + + if( ast_getc( tf, 'alignSystem', status ) .ne. 'MJD' ) then + write(*,*) ast_getc( tf, 'alignSystem', status ) + call stopit( status, 'error 14d' ) + endif + +c +c Test dump and load +c + call checkDump( tf, 'CheckDump 1', status ) + +c +c Test CurrentTime method. +c + call ast_set( tf, 'system=jepoch,unit=yr,timescale=utc,'// + : 'timeorigin=0', status ) + n = 0 + + write(*,*) ' Testing astCurrentTime: approx 1 second pause '// + : 'following...' + ctl = ast_currenttime( tf, status ) + 1.0D0/(86400.0D0*365.25D0) + do while( ast_currenttime( tf, status ) .lt. ctl ) + n = n + 1 + if( n .gt. 2000000 ) then + call stopit( status, 'error 15' ) + return + end if + end do + write(*,*) ' 1 second pause finished.' + + +c +c Test behaviour of TimeOrigin attribute +c + tf = ast_timeframe( 'timescale=utc', status ) + origin = ast_currenttime( tf, status ) + call ast_setd( tf, 'TimeOrigin', origin, status ) + write(*,*) ' Testing TimeOrigin: approx 1 second pause '// + : 'following...' + n = 0 + do while( ast_currenttime( tf, status ) .lt. + : 1.0D0/(86400.0D0*364.25D0) ) + n = n + 1 + if( n .gt. 2000000 ) then + call stopit( status, 'error 16' ) + return + end if + end do + write(*,*) ' 1 second pause finished.' + + call ast_set( tf, 'unit=s', status ) + if( abs( ast_getd( tf, 'TimeOrigin', status ) - + : origin*86400.0D0 ) .gt. 0.01 ) then + write(*,*) abs( ast_getd( tf, 'TimeOrigin', status ) - + : origin*86400.0D0 ) + call stopit( status, 'error 17' ) + end if + + +c +c Test conversions between basic systems with arbitrary offsets +c + tf1 = ast_timeframe( 'system=mjd,timeorigin=53000', status ) + tf2 = ast_timeframe( 'system=jd,timeorigin=2453000.5', status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 18' ) + else if( .not. ast_isaunitmap( ast_getMapping( fs, AST__BASE, + : AST__CURRENT, + : status ), status ) ) then + call stopit( status, 'error 19' ) + end if + + + + tf1 = ast_timeframe( 'system=mjd,timescale=UTC,timeorigin=53000', + : status ) + tf2 = ast_timeframe( 'system=bepoch,timeorigin=2004', status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 20' ) + else + xin = 100.0D0 + call ast_tran1( fs, 1, xin, .true., xout, status ) + if( abs( xout - 0.2600974092354136D0 ) .gt. 1.0D-10 ) then + call stopit( status, 'error 21' ) + end if + call ast_tran1( fs, 1, xout, .false., xin, status ) + if( abs( xin - 100.0D0 ) .gt. 1.0D-6 ) then + call stopit( status, 'error 21b' ) + end if + end if + + + tf1 = ast_timeframe( 'system=bepoch,timeorigin=0', status ) + if( status .eq.sai__OK ) then + call err_mark + call ast_set( tf1, 'TimeScale=TAI', status ) + if( status .eq. AST__ATTIN ) then + call err_annul( status ) + else + call stopit( status, 'error 21b' ); + endif + + call ast_set( tf1, 'Unit=s', status ) + if( status .eq. AST__ATTIN ) then + call err_annul( status ) + else + call stopit( status, 'error 21c' ); + endif + call err_rlse + endif + + tf2 = ast_timeframe( 'system=jepoch,timescale=tai,'// + : 'timeorigin=100.0', status ) + call ast_set( tf2, 'unit=d', status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 22' ) + else + xin = 100.0D0 + call ast_tran1( fs, 1, xin, .true., xout, status ) + if( abs( xout - 14.35534169996282 ) .gt. 1.0D-6 ) then + call stopit( status, 'error 23' ) + end if + call ast_tran1( fs, 1, xout, .false., xin, status ) + if( abs( xin - 100.0D0 ) .gt. 1.0D-6 ) then + call stopit( status, 'error 23b' ) + end if + end if + +c Besselian epoch offset from B2000 [TT, yr] + call ast_set( tf1, 'timeorigin=2000', status ) + +c Julian date offset from 2450000.5 days [TDB, h] + call ast_set( tf2, 'system=JD,timescale=TDB,unit=h,'// + : 'timeorigin=2450000.5 d', status ) + + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 24' ) + else + xin = 0.1 + call ast_tran1( fs, 1, xin, .true., xout, status ) + if( abs( xout - 37933.38284478387D0) .gt. 1.0D-5 ) then + call stopit( status, 'error 25' ) + end if + call ast_tran1( fs, 1, xout, .false., xin, status ) + if( abs( xin - 0.1 ) .gt. 1.0D-10 ) then + call stopit( status, 'error 25b' ) + end if + end if + + +c +c Test Formatting and unformatting +c + tf1 = ast_timeframe( 'system=jepoch,timeorigin=2005.0', status ) + + txt = ast_format( tf1, 1, 100.0D0, status ) + if( txt .ne. '100' ) then + write(*,*) ast_format( tf1, 1, 100.0D0, status ) + call stopit( status, 'error 26' ) + end if + nc = ast_unformat( tf1, 1, txt, xout, status ) + if( nc .ne. len( txt ) .or. xout .ne. 100.0D0 ) then + write(*,*) nc, xout + call stopit( status, 'error 26b' ) + end if + + + + call ast_set( tf1, 'format=iso', status ) + txt = ast_format( tf1, 1, 1.0D0, status ) + if( txt .ne. '2006-01-01' ) then + write(*,*) ast_format( tf1, 1, 1.0D0, status ) + call stopit( status, 'error 27' ) + end if + + nc = ast_unformat( tf1, 1, txt, xout, status ) + if( nc .ne. len( txt ) .or. xout .ne. 1.0D0 ) then + write(*,*) nc, xout + call stopit( status, 'error 27b' ) + end if + + + + call ast_set( tf1, 'format=iso.0', status ) + txt = ast_format( tf1, 1, 1.0D0, status ) + if( txt .ne. '2006-01-01 00:00:00' ) then + write(*,*) ast_format( tf1, 1, 1.0D0, status ) + call stopit( status, 'error 28' ) + end if + + nc = ast_unformat( tf1, 1, txt, xout, status ) + if( nc .ne. len( txt ) .or. xout .ne. 1.0D0 ) then + write(*,*) nc, xout + call stopit( status, 'error 28b' ) + end if + + + + call ast_set( tf1, 'unit=s,format=iso.2', status ) + txt = ast_format( tf1, 1, 10.0D0, status ) + if( txt .ne. '2004-12-31 18:00:10.00' ) then + write(*,*) ast_format( tf1, 1, 10.0D0, status ) + call stopit( status, 'error 29' ) + end if + + nc = ast_unformat( tf1, 1, txt, xout, status ) + if( nc .ne. len( txt ) .or. + : abs( xout - 10.0D0 ) .gt. 1.0E-3 ) then + write(*,*) nc, xout + call stopit( status, 'error 29b' ) + end if + + + + txt = ast_format( tf1, 1, 10.12D0, status ) + if( txt .ne. '2004-12-31 18:00:10.12' ) then + write(*,*) ast_format( tf1, 1, 10.12D0, status ) + call stopit( status, 'error 30' ) + end if + + nc = ast_unformat( tf1, 1, txt, xout, status ) + if( nc .ne. len( txt ) .or. + : abs( xout - 10.12D0 ) .gt. 1.0E-3 ) then + write(*,*) nc, xout + call stopit( status, 'error 30b' ) + end if + + + call ast_set( tf1, 'timescale=utc', status ) + xin = ast_currenttime( tf1, status ) + txt = ast_format( tf1, 1, xin, status ) + write(*,*) ' Current system time (UTC): ', + : txt( : chr_len( txt ) ) + nc = ast_unformat( tf1, 1, txt(:20), xout, status ) + if( nc .ne. 20 .or. abs( xout - xin ) .gt. 1.0E-3 ) then + write(*,*) nc, xout + call stopit( status, 'error 30c' ) + end if + + tf1 = ast_timeframe( 'system=jepoch,timeorigin=2005.0', status ) + nc = ast_unformat( tf1, 1, 'J2005.0', xout, status ) + if( nc .ne. 7 .or. xout .ne. 0.0D0 ) then + write(*,*) nc, xout + call stopit( status, 'error 31' ) + end if + + nc = ast_unformat( tf1, 1, 'J2010.0', xout, status ) + if( nc .ne. 7 .or. xout .ne. 5.0D0 ) then + write(*,*) nc, xout + call stopit( status, 'error 32' ) + end if + + nc = ast_unformat( tf1, 1, '2005-jun-1 12:30 lunch time', xout, + : status ) + if( nc .ne. 17 .or. abs( xout - 0.415525896D0 ) .gt. 1.0E-7 ) then + write(*,*) nc, xout + call stopit( status, 'error 33' ) + end if + + call ast_set( tf1, 'timescale=utc', status ) + nc = ast_unformat( tf1, 1, 'B2001.5 lunch time', xout, + : status ) + if( nc .ne. 8 .or. + : abs( xout + 3.50131054408916D0 ) .gt. 1.0E-10 ) then + write(*,*) nc, xout, abs( xout + 3.50131054408916D0 ) + call stopit( status, 'error 34' ) + end if + + + + + + + tf1 = ast_timeframe( 'system=mjd,timescale=tai', status ) + nc = ast_unformat( tf1, 1, "1977-01-01 00:00:00", xin, status ) + + + tf2 = ast_timeframe( 'system=mjd,timescale=tai,format=iso.6', + : status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 35' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status ) + txt = ast_format( tf2, 1, xout, status ) + if( txt .ne. '1977-01-01 00:00:00.000000' ) then + write(*,*) txt( :chr_len(txt) ) + call stopit( status, 'error 36' ) + end if + end if + + tf2 = ast_timeframe( 'system=mjd,timescale=utc,format=iso.6', + : status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 37' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status ) + txt = ast_format( tf2, 1, xout, status ) + if( txt .ne. '1976-12-31 23:59:45.000000' ) then + write(*,*) txt( :chr_len(txt) ) + call stopit( status, 'error 38' ) + end if + end if + + tf2 = ast_timeframe( 'system=mjd,timescale=tt,format=iso.6', + : status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 39' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status ) + txt = ast_format( tf2, 1, xout, status ) + if( txt .ne. '1977-01-01 00:00:32.184000' ) then + write(*,*) txt( :chr_len(txt) ) + call stopit( status, 'error 40' ) + end if + end if + + tf2 = ast_timeframe( 'system=mjd,timescale=tdb,format=iso.6', + : status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 41' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status ) + txt = ast_format( tf2, 1, xout, status ) + if( txt .ne. '1977-01-01 00:00:32.183935' ) then + write(*,*) txt( :chr_len(txt) ) + call stopit( status, 'error 42' ) + end if + end if + + tf2 = ast_timeframe( 'system=mjd,timescale=tcb,format=iso.6', + : status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 43' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status ) + txt = ast_format( tf2, 1, xout, status ) + if( txt .ne. '1977-01-01 00:00:32.184000' ) then + write(*,*) txt( :chr_len(txt) ) + call stopit( status, 'error 44' ) + end if + end if + + tf2 = ast_timeframe( 'system=mjd,timescale=tcg,format=iso.6', + : status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 45' ) + else + call ast_tran1( fs, 1, xin, .true., xout, status ) + txt = ast_format( tf2, 1, xout, status ) + if( txt .ne. '1977-01-01 00:00:32.184000' ) then + write(*,*) txt( :chr_len(txt) ) + call stopit( status, 'error 46' ) + end if + end if + + + + + tf1 = ast_timeframe( 'system=mjd,timescale=gmst,ObsLon=90,'// + : 'ObsLat=0,timeorigin=53000.0', status ) + tf2 = ast_timeframe( 'system=mjd,timescale=lmst,ObsLon=90,'// + : 'ObsLat=0,timeorigin=53000.0', status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 47' ) + else + xin = 1.0D0 + call ast_tran1( fs, 1, xin, .true., xout, status ) + if( xout .ne. 1.25D0 ) then + write(*,*) xout + call stopit( status, 'error 48' ) + end if + call ast_tran1( fs, 1, xout, .false., xin, status ) + if( xin .ne. 1.0D0 ) then + write(*,*) xin + call stopit( status, 'error 48b' ) + end if + end if + + +* Test use of DUT1 + tf1 = ast_timeframe( 'system=mjd,timescale=tdb,dut1=0.1', status ) + tf2 = ast_timeframe( 'system=mjd,timescale=last,dut1=0.1', + : status ) + fs = ast_convert( tf1, tf2, ' ', status ) + if( fs .eq. AST__NULL ) then + call stopit( status, 'error 49' ) + else + xin = 53991.675D0 + call ast_tran1( fs, 1, xin, .true., xout, status ) + if( abs(xout - 53998.65344633732D0) .gt. 1.0D-8 ) then + write(*,*) xout + call stopit( status, 'error 50' ) + end if + call ast_tran1( fs, 1, xout, .false., xin, status ) + if( abs( xin - 53991.675D0 ) .gt. 1.0D-8 ) then + write(*,*) xin + call stopit( status, 'error 51' ) + end if + end if + + + + + + call ast_end( status ) +c call ast_listissued( 'testtime' ) + + + + if( status .eq. sai__ok ) then + write(*,*) 'All timeFrame tests passed' + else + write(*,*) 'timeFrame tests failed' + end if + + end + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + + end + + + subroutine checkdump( obj, text, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + character text*(*) + integer obj, status, next, end, ch, result, ll, overlap + external mysource, mysink + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + ch = ast_channel( mysource, mysink, ' ', status ) + + + ll = 110 + next = 1 + if( ast_write( ch, obj, status ) .ne.1 ) then + write(*,*) text + call stopit( status, 'Cannot write supplied object to '// + : 'channel' ) + end if + + next = 1 + result = ast_read( ch, status ) + if( result .eq. ast__null ) then + write(*,*) text + call stopit( status, 'Cannot read object from channel' ) + end if + + + + if( ast_getd( obj, 'timeorigin', status ) .ne. + : ast_getd( result, 'timeorigin', status ) .or. + : ast_getc( obj, 'timescale', status ) .ne. + : ast_getc( result, 'timescale', status ) .or. + : ast_getc( obj, 'ObsLon', status ) .ne. + : ast_getc( result, 'ObsLon', status ) .or. + : ast_getc( obj, 'ObsLat', status ) .ne. + : ast_getc( result, 'ObsLat', status ) ) then + call ast_Show( obj, status ) + call ast_Show( result, status ) + write(*,*) text + call stopit( status, 'Object has changed' ) + end if + + end + + subroutine sink1( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + logical fsfound, done + common /sink1com/ fsfound, done + + integer status, l + character line*200 + + if( status .ne. sai__ok ) return + call ast_getline( line, l, status ) + + if( index( line( : l ),'Unc =' ) .GT. 0 ) then + done = .true. + + else if( .not. done .and. + : index( line( : l ),'FrameSet' ) .GT. 0 ) then + fsfound= .true. + end if + + end + + subroutine mysource( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, ll + character buf*25000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + if( next .ge. end ) then + call ast_putline( buf, -1, status ) + else + call ast_putline( buf( next : ), ll, status ) + endif + + next = next + ll + + end + + subroutine mysink( status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer status, next, end, f, l, ll + character buf*25000 + character line*1000 + + common /ss1/ buf + common /ss2/ next, end, ll + + if( status .ne. sai__ok ) return + + line = ' ' + call ast_getline( line, l, status ) + call chr_fandl( line( : l ), f, l ) + buf( next : ) = line( f : l ) + l = l - f + 1 + + if( next + ll - 1 .ge. 25000 ) then + write(*,*) + call stopit( status, 'Buffer overflow in mysink!!' ) + else if( l .gt. ll ) then + write(*,*) + write(*,*) buf( next : next + l) + write(*,*) 'Line length ',l + call stopit( status, 'Line overflow in mysink!!' ) + else + end = next + l + buf( end : next + ll - 1 ) = ' ' + endif + + next = next + ll + + end + + diff --git a/ast_tester/testtrangrid.f b/ast_tester/testtrangrid.f new file mode 100644 index 0000000..b867291 --- /dev/null +++ b/ast_tester/testtrangrid.f @@ -0,0 +1,276 @@ + program testtrangrid + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'PRM_PAR' + + integer status, i, m, outp(4), inp(4), c1, c2, c3, c4, fc, fs + double precision at(4), r, mat(4), b1(2), b2(2), a1(2), + : a2(4) + character cards(9)*80 + + status = sai__ok + + at(1) = 10.0D0 + at(2) = 1.2D6 + +* UnitMap + m = ast_unitmap( 2, ' ', status ) + call testmap( m, 1, 0.5D0, status ) + +* ZoomMap + m = ast_zoommap( 2, 2.0D0, ' ', status ) + call testmap( m, 2, 0.5D0, status ) + +* MatrixMap + m = ast_matrixmap( 2, 2, 2, mat, ' ', status ) + call testmap( m, 3, 0.5D0, status ) + +* PermMap + outp(1)=2 + outp(2)=1 + inp(1)=2 + inp(2)=1 + m = ast_permmap( 2, inp, 2, outp, 0.0D0, ' ', status ) + call testmap( m, 4, 0.5D0, status ) + +* TranMap + a1( 1 ) = 0.0D0 + a1( 2 ) = 0.0D0 + a2( 1 ) = 1.0D0 + a2( 2 ) = 1.0D0 + b1( 1 ) = 0.5D0 + b1( 2 ) = 0.5D0 + b2( 1 ) = 2.5D0 + b2( 2 ) = 2.5D0 + c1 = ast_winmap( 2, a1, a2, b1, b2, ' ', status ) + c2 = ast_copy( c1, status ) + + m = ast_tranmap( c1, c2, ' ', status ) + call testmap( m, 5, 0.5D0, status ) + +* 3D CmpMap + mat(1) = -1.0D0 + c1 = ast_shiftmap( 1, mat, ' ', status ) + mat(1)= 1.0D0 + mat(2)= 2.0D0 + mat(3)= -2.0D0 + mat(4)= 3.0D0 + c2 = ast_matrixmap( 2, 2, 0, mat, ' ', status ) + c3 = ast_cmpmap( c1, c2, 0, ' ', status ) + + outp(1) = 3 + outp(2) = 2 + outp(3) = 1 + inp(1) = 3 + inp(2) = 2 + inp(3) = 1 + c1 = ast_permmap( 3, inp, 3, outp, 0.0D0, ' ', status ) + c2 = ast_ZoomMap( 3, 0.25D0, ' ', status ) + call ast_invert( c2, status ) + + c4 = ast_cmpmap( c1, c2, 1, ' ', status ) + call ast_invert( c4, status ) + + m = ast_cmpmap( c3, c4, 1, ' ', status ) + call testmap( m, 6, 0.5D0, status ) + + + +* 1D non-linear Mapping + m = ast_mathmap( 1, 1, 1, 'y=x**3', 1, + : 'x=sign((abs(y)**(1/3)),y)', ' ', status ) + call testmap( m, 7, 0.0001D0, status ) + + + +* A FITS-WCS pixel->sky mapping + cards(1) = 'CTYPE1 = ''RA---TAN''' + cards(2) = 'CTYPE2 = ''DEC--TAN''' + cards(3) = 'CRPIX1 = 20' + cards(4) = 'CRPIX2 = 20' + cards(5) = 'CRVAL1 = 0.0' + cards(6) = 'CRVAL2 = 0.0' + cards(7) = 'CROTA1 = 30.0' + cards(8) = 'CDELT1 = -0.001' + cards(9) = 'CDELT2 = 0.001' + + fc = ast_fitschan( ast_null, ast_null, ' ', status ) + do i = 1, 9 + call ast_putfits( fc, cards(i), .false., status ) + end do + call ast_clear( fc, 'card', status ) + fs = ast_read( fc, status ) + + call testmap( fs, 8, 0.0001D0, status ) + + + + + + + + + + if( status .eq. sai__ok ) then + write(*,*) 'All AST_TRANGRID tests passed' + else + write(*,*) 'AST_TRANGRID tests failed' + end if + + end + + + + + + subroutine testmap( map, itest, tol, status ) + implicit none + include 'SAE_PAR' + + integer status, maxpix, itest, map + double precision tol + + if( status .ne. sai__ok ) return + + maxpix = 100 + call testgridpair( map, tol, maxpix, itest*10, status ) + call testgridpair( map, 0.0D0, maxpix, itest*10+2, status ) + + maxpix = 4 + call testgridpair( map, tol, maxpix, itest*10+4, status ) + call testgridpair( map, 0.0D0, maxpix, itest*10+6, status ) + + end + + + + + subroutine testgridpair( map, tol, maxpix, itest, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + integer status, lbnd( 3 ), ubnd( 3 ), nin, nout, i, outdim, + : maxpix, itest, size, pos(3), j, map, lbndi(3), ubndi(3) + logical fwd + double precision tol, xl(3), xu(3), dlbndi(3), dubndi(3), + : dlbnd(3), dubnd(3) + + data lbnd / -6, 0, 10 / + data ubnd / 15, 30, 40/ + + if( status .ne. sai__ok ) return + + + nin = ast_geti( map, 'nin', status ) + nout = ast_geti( map, 'nout', status ) + + outdim = 1 + do i = 1, nin + outdim = outdim*( ubnd( i ) - lbnd( i ) + 1 ) + dlbnd( i ) = lbnd( i ) + dubnd( i ) = ubnd( i ) + end do + + call testgrid( map, nin, lbnd, ubnd, tol, maxpix, .true., nout, + : outdim, itest, status ) + + outdim = 1 + do i = 1, nout + call ast_mapbox( map, dlbnd, dubnd, .true., i, dlbndi(i), + : dubndi(i), xl, xu, status ) + lbndi( i ) = dlbndi( i ) + ubndi( i ) = dubndi( i ) + outdim = outdim*( ubndi( i ) - lbndi( i ) + 1 ) + end do + + call testgrid( map, nout, lbndi, ubndi, tol, maxpix, .false., nin, + : outdim, itest + 1, status ) + + + end + + + + + + subroutine testgrid( map, nin, lbnd, ubnd, tol, maxpix, fwd, nout, + : outdim, itest, status ) + implicit none + include 'SAE_PAR' + + integer MAXPNT + parameter( MAXPNT = 50000 ) + + integer status, lbnd( 3 ), ubnd( 3 ), nin, nout, i, outdim, + : maxpix, itest, pos(3), j, map + logical fwd + double precision tol, out( MAXPNT, 3 ), in( MAXPNT, 3 ) + + if( status .ne. sai__ok ) return + +* Check arrays are not over full + if( outdim .gt. MAXPNT ) then + status = sai__error + write(*,*) 'Array length exceeded in TESTTRANGRID:TESTGRID' + return + end if + +* Create a regular grid of positions within the input space of the +* Mapping, and transform it to the output space of the Mapping. + call ast_trangrid( map, nin, lbnd, ubnd, tol, maxpix, fwd, + : nout, MAXPNT, out, status ) + +* Convert the transformed output positions back into the input space. + call ast_trann( map, outdim, nout, MAXPNT, out, .not. fwd, nin, + : MAXPNT, in, status ) + +* Check the input space positions are close to a regular grid. + if( status .eq. sai__ok ) then + + do i = 1, nin + pos( i ) = lbnd( i ) + end do + + do j = 1, outdim + + do i = 1, nin + + if( pos( i ) .ne. 0 ) then + if( abs( in( j, i ) - pos( i ) ) .gt. + : 1.0E-6*abs( 0.5*( in( j, i ) + pos( i ) ) ) ) then + status = sai__error + write(*,*) 'Test ',itest,' failed at point ',j, + : ' axis ',i,': ',in( j, i ), + : ' should be ',pos(1) + return + end if + else + if( abs( in( j, i ) ) .gt. 1.0E-5 ) then + status = sai__error + write(*,*) 'Test ',itest,' failed at point ',j, + : ' axis ',i,': ',in( j, i ), + : ' should be ', pos(i) + return + end if + end if + end do + + pos( 1 ) = pos( 1 ) + 1 + if( pos( 1 ) .gt. ubnd( 1 ) ) then + pos( 1 ) = lbnd( 1 ) + if( nin .gt. 1 ) then + pos( 2 ) = pos( 2 ) + 1 + if( pos( 2 ) .gt. ubnd( 2 ) ) then + pos( 2 ) = lbnd( 2 ) + if( nin .gt. 2 ) then + pos( 3 ) = pos( 3 ) + 1 + end if + end if + end if + end if + end do + end if + + end diff --git a/ast_tester/testxmlchan.f b/ast_tester/testxmlchan.f new file mode 100644 index 0000000..51e58ec --- /dev/null +++ b/ast_tester/testxmlchan.f @@ -0,0 +1,246 @@ + program testxmlchan + implicit none + include 'SAE_PAR' + include 'AST_PAR' + include 'testxmlchan_com' + + integer status, obj, i, ifmt, nfmt + character fmt(2)*30 + logical ok + + data nfmt /2/, + : fmt / 'native', 'quoted' / + + status = sai__ok + + call ast_begin( status ) + +* +* Create an AST object. +* + call makeobject( obj, status ) + +* +* Create a normal Channel and write the object out to file1 +* + call chanwrite( obj, 1, status ) + +* +* Test each XML format in turn. + do ifmt = 1, nfmt + +* +* Create an XmlChan and write the object out to file2 using the current +* format. +* + call xmlwrite( obj, 2, fmt( ifmt ), status ) + +* +* Use a new XmlChan to read an object from file2 +* + call xmlread( 2, obj, status ) + +* +* Write this object out to file 3 using a simple Channel. +* + call chanwrite( obj, 3, status ) + +* +* Report an error if the contents of files 1 and 3 differ. +* + ok = .true. + if( filelen( 1 ) .ne. filelen( 3 ) ) then + write(*,*) 'TestXmlChan: files 1 and 3 have different '// + : 'lengths (',filelen( 1 ),',',filelen( 3 ),').' + ok =.false. + else + do i = 1, filelen( 1 ) + if( files( 1, i ) .ne. files( 3, i ) ) then + write(*,*) 'TestXmlChan: Line ',i,' differs in '// + : 'files 1 and 3:' + write(*,*) files( 1, i ) + write(*,*) files( 3, i ) + ok = .false. + go to 10 + end if + end do + end if + + 10 continue + + if( .not. ok ) then + write(*,*) 'TestXmlChan: Test failed on XmlFormat ''', + : fmt(ifmt),'''.' + go to 20 + end if + + end do + + 20 continue + + call ast_end( status ) + + if( ok ) then + write(*,*) 'All XmlChan tests passed' + else + write(*,*) 'XmlChan tests failed' + end if + + end + +* +* Reads line "iline" from internal file "ifile" and returns it to AST using +* the AST_PULINE routine. Then increments "iline" ready for next time. +* + subroutine source( status ) + implicit none + + include 'testxmlchan_com' + + integer status, l, chr_len + + if( iline .le. filelen( ifile ) ) then + l = chr_len( files(ifile,iline) ) + call ast_putline( files(ifile,iline), l, status ) + iline = iline + 1 + else + call ast_putline( ' ', -1, status ) + end if + + end + +* +* Append a line obtained using ast_getline function to the end of the +* internal file indicated by "ifile", and increment the file length. +* + subroutine sink( status ) + implicit none + + include 'testxmlchan_com' + + integer status, l + character line*(linelen) + + call ast_getline( line, l, status ) + if( l .gt. 0 ) then + + if( filelen( ifile ) .ge. mxline ) then + stop 'TestXmlChan: Too many lines sent to sink function' + + else if( l .gt. linelen ) then + stop 'TestXmlChan: Text truncated in sink function' + + else + filelen( ifile ) = filelen( ifile ) + 1 + files( ifile, filelen( ifile ) ) = line(:l) + end if + + end if + + end + +* +* Create an AST object to be used as the test object. +* + subroutine makeobject( obj, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + integer obj, sf, f, m, status + + obj = ast__null + if( status .ne. sai__ok ) return + + sf = Ast_SkyFrame( ' ', status ) + f = Ast_Frame( 2, ' ', status ) + call ast_setc( f, 'title', 'A new title', status ) + m = ast_UnitMap( 2, ' ', status ) + obj = ast_FrameSet( f, ' ', status ) + call ast_addFrame( obj, 1, m, sf, status ) + + end + +* +* Write the supplied object out to the specified internal file using a +* basic Channel. +* + subroutine chanwrite( obj, ifil, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + include 'testxmlchan_com' + + external sink + integer obj, ifil, status, ch + + if( status .ne. sai__ok ) return + + ifile = ifil + filelen( ifil ) = 0 + + ch = ast_channel( ast_null, sink, ' ', status ) + if( ast_write( ch, obj, status ) .ne. 1 ) then + stop 'TestXmlChan: Failed to write object to Channel.' + end if + call ast_annul( ch, status ) + + end + +* +* Write the supplied object out to the specified internal file using an +* XmlChan. +* + subroutine xmlwrite( obj, ifil, fmt, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + include 'testxmlchan_com' + + external sink + integer obj, ifil, status, ch + character fmt*(*) + + if( status .ne. sai__ok ) return + + ifile = ifil + filelen( ifil ) = 0 + + ch = ast_xmlchan( ast_null, sink, 'indent=1,comment=1', + : status ) + call ast_seti( ch, 'xmllength', linelen, status ) + call ast_setc( ch, 'xmlformat', fmt, status ) + if( ast_write( ch, obj, status ) .ne. 1 ) then + stop 'TestXmlChan: Failed to write object to XmlChan.' + end if + call ast_annul( ch, status ) + + end + +* +* Read an object out of the specified internal file using an XmlChan. +* + subroutine xmlread( ifil, obj, status ) + implicit none + include 'SAE_PAR' + include 'AST_PAR' + + include 'testxmlchan_com' + + external source + integer obj, ifil, status, ch + + if( status .ne. sai__ok ) return + + ifile = ifil + iline = 1 + + ch = ast_xmlchan( source, ast_null, ' ', status ) + obj = ast_read( ch, status ) + if( obj .eq. ast__null ) then + stop 'TestXmlChan: Failed to read object from XmlChan.' + end if + call ast_annul( ch, status ) + + end diff --git a/ast_tester/testxmlchan_com b/ast_tester/testxmlchan_com new file mode 100644 index 0000000..3b7500e --- /dev/null +++ b/ast_tester/testxmlchan_com @@ -0,0 +1,13 @@ +* +* Common block declaration used by testxmlchan.f for storing internal +* files and associated info. +* + integer mxline + parameter ( mxline = 500 ) + + integer linelen + parameter ( linelen = 100 ) + + character files( 3, mxline )*(linelen) + integer ifile,iline,filelen( 3 ) + common /files/ files, ifile, iline, filelen diff --git a/ast_tester/testzoommap.f b/ast_tester/testzoommap.f new file mode 100644 index 0000000..0c2afd7 --- /dev/null +++ b/ast_tester/testzoommap.f @@ -0,0 +1,91 @@ + program testzoommap + implicit none + + include 'AST_PAR' + include 'AST_ERR' + include 'SAE_PAR' + + integer zm, cm, status + + status = sai__ok + call ast_begin( status ) + + zm = ast_zoommap( 1, -1.0D0, ' ', status ) + if( .not. ast_test( zm, 'Zoom', status ) ) then + call stopit( status, "Error 1" ); + else if( ast_getd( zm, 'Zoom', status ) .ne. -1.0D0 ) then + call stopit( status, "Error 2" ); + end if + + call ast_clear( zm, 'Zoom', status ) + if( ast_test( zm, 'Zoom', status ) ) then + call stopit( status, "Error 3" ); + else if( ast_getd( zm, 'Zoom', status ) .ne. 1.0D0 ) then + call stopit( status, "Error 4" ); + end if + + call ast_setd( zm, 'Zoom', 2.5D0, status ) + if( .not. ast_test( zm, 'Zoom', status ) ) then + call stopit( status, "Error 5" ); + else if( ast_getd( zm, 'Zoom', status ) .ne. 2.5D0 ) then + call stopit( status, "Error 6" ); + end if + + cm = ast_cmpmap( zm, ast_unitmap( 1, ' ', status ), .TRUE., + : ' ', status ) + + if( .not. ast_test( zm, 'Zoom', status ) ) then + call stopit( status, "Error 7" ); + else if( ast_getd( zm, 'Zoom', status ) .ne. 2.5D0 ) then + call stopit( status, "Error 8" ); + end if + + call err_mark + + call ast_setd( zm, 'Zoom', 1.5D0, status ) + if( status .eq. ast__immut ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, "Error 9" ); + else + call stopit( status, "Error 10" ); + end if + + call ast_clear( zm, 'Zoom', status ) + if( status .eq. ast__immut ) then + call err_annul( status ) + else if( status .eq. sai__ok ) then + call stopit( status, "Error 11" ); + else + call stopit( status, "Error 12" ); + end if + + call err_rlse + + call ast_end( status ) + +c call ast_activememory( 'testzoommap' ) + call ast_flushmemory( 1 ) + + if( status .eq. sai__ok ) then + write(*,*) 'All ZoomMap tests passed' + else + write(*,*) 'ZoomMap tests failed' + end if + + end + + + + subroutine stopit( status, text ) + implicit none + include 'SAE_PAR' + integer status + character text*(*) + if( status .ne. sai__ok ) return + status = sai__error + write(*,*) text + end + + + diff --git a/ast_tester/timeplot.attr b/ast_tester/timeplot.attr new file mode 100644 index 0000000..5b43238 --- /dev/null +++ b/ast_tester/timeplot.attr @@ -0,0 +1 @@ +minticklen=0.02,majticklen=0.05,gap(2)=0.1,NumLabGap=0.05,textlabgap=0.1,size=0.9 diff --git a/ast_tester/timeplot.box b/ast_tester/timeplot.box new file mode 100644 index 0000000..c2351b4 --- /dev/null +++ b/ast_tester/timeplot.box @@ -0,0 +1 @@ +0.046 3.0 1.008 3.3 diff --git a/ast_tester/timeplot.head b/ast_tester/timeplot.head new file mode 100644 index 0000000..95ea8d5 --- /dev/null +++ b/ast_tester/timeplot.head @@ -0,0 +1,34 @@ + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST Beginning of AST data for CmpFrame object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'CmpFrame' / Compound coordinate system description +DOMAIN_A= 'DATAPLOT' / Coordinate system domain +ACTUNT_A= 1 / Unit strings affects alignment +ISA_A = 'Frame ' / Coordinate system description +FRAMEA_A= ' ' / First component Frame +BEGAST_B= 'TimeFrame' / Description of time coordinate system +TITLE_A = 'KAPPA - Trandat' / Title of coordinate system +NAXES_A = 1 / Number of coordinate axes +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +FORMAT_A= 'iso.0 ' / Format specifier +ENDAST_A= 'Axis ' / End of object definition +ISA_B = 'Frame ' / Coordinate system description +TMORG_A = 57128.585856 / Time offset +ENDAST_B= 'TimeFrame' / End of object definition +FRAMEB_A= ' ' / Second component Frame +BEGAST_D= 'Frame ' / Coordinate system description +NAXES_B = 1 / Number of coordinate axes +ACTUNT_B= 1 / Unit strings affects alignment +AX1_B = ' ' / Axis number 1 +BEGAST_E= 'Axis ' / Coordinate axis +LABEL_A = 'Data up-loaded' / Axis Label +SYMBOL_A= 'Data ' / Axis symbol +UNIT_A = 'GB ' / Axis units +ENDAST_C= 'Axis ' / End of object definition +ENDAST_D= 'Frame ' / End of object definition +ENDAST_E= 'CmpFrame' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for CmpFrame object AST +COMMENT AST ---------------------------------------------------------------- AST diff --git a/ast_tester/timeplot.ps b/ast_tester/timeplot.ps new file mode 100644 index 0000000..485177c --- /dev/null +++ b/ast_tester/timeplot.ps @@ -0,0 +1,687 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Fri Apr 17 15:42:39 2015 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 17/04/1915 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 247.5 216.6 247.5 210.5 2 pls + 213.6 210.5 213.6 213.0 213.6 210.5 3 pls + 281.3 210.5 281.3 213.0 281.3 210.5 3 pls + 349.1 210.5 349.1 216.6 349.1 210.5 3 pls + 315.2 210.5 315.2 213.0 315.2 210.5 3 pls + 382.9 210.5 382.9 213.0 382.9 210.5 3 pls + 450.7 210.5 450.7 216.6 450.7 210.5 3 pls + 416.8 210.5 416.8 213.0 416.8 210.5 3 pls + 484.5 210.5 484.5 213.0 484.5 210.5 3 pls + 552.3 210.5 552.3 216.6 552.3 210.5 3 pls + 518.4 210.5 518.4 213.0 518.4 210.5 3 pls + 586.1 210.5 586.1 213.0 586.1 210.5 3 pls + 201.9 218.7 199.5 218.7 2 pls + 199.5 226.8 201.9 226.8 199.5 226.8 3 pls + 199.5 251.2 205.5 251.2 199.5 251.2 3 pls + 199.5 234.9 201.9 234.9 199.5 234.9 3 pls + 199.5 243.0 201.9 243.0 199.5 243.0 3 pls + 199.5 259.3 201.9 259.3 199.5 259.3 3 pls + 199.5 267.4 201.9 267.4 199.5 267.4 3 pls + 199.5 291.8 205.5 291.8 199.5 291.8 3 pls + 199.5 275.6 201.9 275.6 199.5 275.6 3 pls + 199.5 283.7 201.9 283.7 199.5 283.7 3 pls + 199.5 299.9 201.9 299.9 199.5 299.9 3 pls + 199.5 308.1 201.9 308.1 199.5 308.1 3 pls + 199.5 316.2 201.9 316.2 199.5 316.2 3 pls + 199.5 324.3 201.9 324.3 199.5 324.3 3 pls + 247.5 326.4 247.5 332.4 2 pls + 213.6 332.4 213.6 330.0 213.6 332.4 3 pls + 281.3 332.4 281.3 330.0 281.3 332.4 3 pls + 349.1 332.4 349.1 326.4 349.1 332.4 3 pls + 315.2 332.4 315.2 330.0 315.2 332.4 3 pls + 382.9 332.4 382.9 330.0 382.9 332.4 3 pls + 450.7 332.4 450.7 326.4 450.7 332.4 3 pls + 416.8 332.4 416.8 330.0 416.8 332.4 3 pls + 484.5 332.4 484.5 330.0 484.5 332.4 3 pls + 552.3 332.4 552.3 326.4 552.3 332.4 3 pls + 518.4 332.4 518.4 330.0 518.4 332.4 3 pls + 586.1 332.4 586.1 330.0 586.1 332.4 3 pls + 588.0 218.7 590.4 218.7 2 pls + 590.4 226.8 588.0 226.8 590.4 226.8 3 pls + 590.4 251.2 584.3 251.2 590.4 251.2 3 pls + 590.4 234.9 588.0 234.9 590.4 234.9 3 pls + 590.4 243.0 588.0 243.0 590.4 243.0 3 pls + 590.4 259.3 588.0 259.3 590.4 259.3 3 pls + 590.4 267.4 588.0 267.4 590.4 267.4 3 pls + 590.4 291.8 584.3 291.8 590.4 291.8 3 pls + 590.4 275.6 588.0 275.6 590.4 275.6 3 pls + 590.4 283.7 588.0 283.7 590.4 283.7 3 pls + 590.4 299.9 588.0 299.9 590.4 299.9 3 pls + 590.4 308.1 588.0 308.1 590.4 308.1 3 pls + 590.4 316.2 588.0 316.2 590.4 316.2 3 pls + 590.4 324.3 588.0 324.3 590.4 324.3 3 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 590.4 590.4 590.4 + 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 + 367.0 339.1 311.1 283.2 255.3 227.4 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 + 199.5 199.5 199.5 ][ 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 210.5 + 219.2 227.9 236.7 245.4 254.1 262.8 271.5 280.2 288.9 297.6 306.3 315.0 323.7 332.4 332.4 332.4 332.4 332.4 + 332.4 332.4 332.4 332.4 332.4 332.4 332.4 332.4 332.4 332.4 323.7 315.0 306.3 297.6 288.9 280.2 271.5 262.8 + 254.1 245.4 236.7 227.9 219.2 210.5 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 206.87 193.53 moveto[ 206.9 206.9 288.1 288.1 ][ 193.5 204.4 204.4 193.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 209.1 209.1 209.5 209.9 210.6 212.1 212.9 213.3 213.6 213.6 213.3 212.5 208.7 214.0 ][ 201.1 201.4 202.2 202.6 + 202.9 202.9 202.6 202.2 201.4 200.7 199.9 198.8 195.0 195.0 ]plong + s[ 218.5 217.4 216.6 216.3 216.3 216.6 217.4 218.5 219.3 220.4 221.1 221.5 221.5 221.1 220.4 219.3 218.5 ][ 202.9 + 202.6 201.4 199.5 198.4 196.5 195.4 195.0 195.0 195.4 196.5 198.4 199.5 201.4 202.6 202.9 202.9 ]plong + 226.8 195.0 226.8 202.9 225.7 201.8 224.9 201.4 4 pls + s[ 235.8 232.1 231.7 232.1 233.2 234.3 235.4 236.2 236.6 236.6 236.2 235.4 234.3 233.2 232.1 231.7 231.3 ][ 202.9 + 202.9 199.5 199.9 200.3 200.3 199.9 199.2 198.0 197.3 196.2 195.4 195.0 195.0 195.4 195.8 196.5 ]plong + 246.0 198.4 239.2 198.4 2 pls + s[ 250.8 249.7 249.0 248.6 248.6 249.0 249.7 250.8 251.6 252.7 253.5 253.9 253.9 253.5 252.7 251.6 250.8 ][ 202.9 + 202.6 201.4 199.5 198.4 196.5 195.4 195.0 195.0 195.4 196.5 198.4 199.5 201.4 202.6 202.9 202.9 ]plong + 261.7 197.7 256.1 197.7 259.9 202.9 3 pls + 259.9 195.0 259.9 202.9 2 pls + 270.8 198.4 264.0 198.4 2 pls + 276.4 195.0 276.4 202.9 275.3 201.8 274.5 201.4 4 pls + s[ 285.8 285.4 284.3 283.6 282.4 281.7 281.3 281.3 281.7 282.4 283.6 283.9 285.1 285.8 286.2 286.2 285.8 285.1 + 283.9 283.6 282.4 281.7 281.3 ][ 201.8 202.6 202.9 202.9 202.6 201.4 199.5 197.7 196.2 195.4 195.0 195.0 195.4 + 196.2 197.3 197.7 198.8 199.5 199.9 199.9 199.5 198.8 197.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 227.28 181.32 moveto[ 227.3 227.3 280.3 280.3 ][ 181.3 192.2 192.2 181.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 231.0 182.8 231.0 190.7 229.9 189.6 229.2 189.2 4 pls + s[ 237.4 236.3 235.9 235.9 236.3 237.1 238.6 239.7 240.4 240.8 240.8 240.4 240.1 238.9 237.4 236.3 235.9 235.5 + 235.5 235.9 236.7 237.8 239.3 240.1 240.4 240.4 240.1 238.9 237.4 ][ 190.7 190.3 189.6 188.8 188.1 187.7 187.3 + 187.0 186.2 185.5 184.3 183.6 183.2 182.8 182.8 183.2 183.6 184.3 185.5 186.2 187.0 187.3 187.7 188.1 188.8 + 189.6 190.3 190.7 190.7 ]plong + 243.8 188.1 244.2 187.7 243.8 187.3 243.4 187.7 243.8 188.1 5 pls + 243.8 183.6 244.2 183.2 243.8 182.8 243.4 183.2 243.8 183.6 5 pls + s[ 249.1 248.0 247.2 246.8 246.8 247.2 248.0 249.1 249.8 251.0 251.7 252.1 252.1 251.7 251.0 249.8 249.1 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 256.6 255.5 254.7 254.3 254.3 254.7 255.5 256.6 257.4 258.5 259.2 259.6 259.6 259.2 258.5 257.4 256.6 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + 262.6 188.1 263.0 187.7 262.6 187.3 262.2 187.7 262.6 188.1 5 pls + 262.6 183.6 263.0 183.2 262.6 182.8 262.2 183.2 262.6 183.6 5 pls + s[ 267.9 266.7 266.0 265.6 265.6 266.0 266.7 267.9 268.6 269.8 270.5 270.9 270.9 270.5 269.8 268.6 267.9 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 275.4 274.3 273.5 273.1 273.1 273.5 274.3 275.4 276.1 277.3 278.0 278.4 278.4 278.0 277.3 276.1 275.4 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 308.46 193.53 moveto[ 308.5 308.5 389.7 389.7 ][ 193.5 204.4 204.4 193.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 310.7 310.7 311.1 311.5 312.2 313.7 314.5 314.9 315.2 315.2 314.9 314.1 310.3 315.6 ][ 201.1 201.4 202.2 202.6 + 202.9 202.9 202.6 202.2 201.4 200.7 199.9 198.8 195.0 195.0 ]plong + s[ 320.1 319.0 318.2 317.9 317.9 318.2 319.0 320.1 320.9 322.0 322.7 323.1 323.1 322.7 322.0 320.9 320.1 ][ 202.9 + 202.6 201.4 199.5 198.4 196.5 195.4 195.0 195.0 195.4 196.5 198.4 199.5 201.4 202.6 202.9 202.9 ]plong + 328.4 195.0 328.4 202.9 327.3 201.8 326.5 201.4 4 pls + s[ 337.4 333.6 333.3 333.6 334.8 335.9 337.0 337.8 338.2 338.2 337.8 337.0 335.9 334.8 333.6 333.3 332.9 ][ 202.9 + 202.9 199.5 199.9 200.3 200.3 199.9 199.2 198.0 197.3 196.2 195.4 195.0 195.0 195.4 195.8 196.5 ]plong + 347.6 198.4 340.8 198.4 2 pls + s[ 352.4 351.3 350.6 350.2 350.2 350.6 351.3 352.4 353.2 354.3 355.1 355.5 355.5 355.1 354.3 353.2 352.4 ][ 202.9 + 202.6 201.4 199.5 198.4 196.5 195.4 195.0 195.0 195.4 196.5 198.4 199.5 201.4 202.6 202.9 202.9 ]plong + 363.3 197.7 357.7 197.7 361.5 202.9 3 pls + 361.5 195.0 361.5 202.9 2 pls + 372.4 198.4 365.6 198.4 2 pls + 378.0 195.0 378.0 202.9 376.9 201.8 376.1 201.4 4 pls + 384.0 195.0 387.8 202.9 2 pls + 387.8 202.9 382.5 202.9 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 327.75 181.32 moveto[ 327.7 327.7 381.9 381.9 ][ 181.3 192.2 192.2 181.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 331.9 330.8 330.0 329.6 329.6 330.0 330.8 331.9 332.6 333.8 334.5 334.9 334.9 334.5 333.8 332.6 331.9 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 339.4 338.3 337.5 337.1 337.1 337.5 338.3 339.4 340.2 341.3 342.0 342.4 342.4 342.0 341.3 340.2 339.4 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + 345.4 188.1 345.8 187.7 345.4 187.3 345.0 187.7 345.4 188.1 5 pls + 345.4 183.6 345.8 183.2 345.4 182.8 345.0 183.2 345.4 183.6 5 pls + s[ 350.7 349.5 348.8 348.4 348.4 348.8 349.5 350.7 351.4 352.6 353.3 353.7 353.7 353.3 352.6 351.4 350.7 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 358.2 357.1 356.3 355.9 355.9 356.3 357.1 358.2 358.9 360.1 360.8 361.2 361.2 360.8 360.1 358.9 358.2 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + 364.2 188.1 364.6 187.7 364.2 187.3 363.8 187.7 364.2 188.1 5 pls + 364.2 183.6 364.6 183.2 364.2 182.8 363.8 183.2 364.2 183.6 5 pls + s[ 369.5 368.3 367.6 367.2 367.2 367.6 368.3 369.5 370.2 371.4 372.1 372.5 372.5 372.1 371.4 370.2 369.5 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 377.0 375.9 375.1 374.7 374.7 375.1 375.9 377.0 377.7 378.9 379.6 380.0 380.0 379.6 378.9 377.7 377.0 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 410.06 193.53 moveto[ 410.1 410.1 491.3 491.3 ][ 193.5 204.4 204.4 193.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 412.3 412.3 412.7 413.1 413.8 415.3 416.1 416.4 416.8 416.8 416.4 415.7 411.9 417.2 ][ 201.1 201.4 202.2 202.6 + 202.9 202.9 202.6 202.2 201.4 200.7 199.9 198.8 195.0 195.0 ]plong + s[ 421.7 420.6 419.8 419.5 419.5 419.8 420.6 421.7 422.5 423.6 424.3 424.7 424.7 424.3 423.6 422.5 421.7 ][ 202.9 + 202.6 201.4 199.5 198.4 196.5 195.4 195.0 195.0 195.4 196.5 198.4 199.5 201.4 202.6 202.9 202.9 ]plong + 430.0 195.0 430.0 202.9 428.9 201.8 428.1 201.4 4 pls + s[ 439.0 435.2 434.9 435.2 436.4 437.5 438.6 439.4 439.8 439.8 439.4 438.6 437.5 436.4 435.2 434.9 434.5 ][ 202.9 + 202.9 199.5 199.9 200.3 200.3 199.9 199.2 198.0 197.3 196.2 195.4 195.0 195.0 195.4 195.8 196.5 ]plong + 449.2 198.4 442.4 198.4 2 pls + s[ 454.0 452.9 452.2 451.8 451.8 452.2 452.9 454.0 454.8 455.9 456.7 457.0 457.0 456.7 455.9 454.8 454.0 ][ 202.9 + 202.6 201.4 199.5 198.4 196.5 195.4 195.0 195.0 195.4 196.5 198.4 199.5 201.4 202.6 202.9 202.9 ]plong + 464.9 197.7 459.3 197.7 463.1 202.9 3 pls + 463.1 195.0 463.1 202.9 2 pls + 474.0 198.4 467.2 198.4 2 pls + 479.6 195.0 479.6 202.9 478.5 201.8 477.7 201.4 4 pls + 485.6 195.0 489.4 202.9 2 pls + 489.4 202.9 484.1 202.9 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 429.34 181.32 moveto[ 429.3 429.3 483.5 483.5 ][ 181.3 192.2 192.2 181.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 433.5 432.3 431.6 431.2 431.2 431.6 432.3 433.5 434.2 435.4 436.1 436.5 436.5 436.1 435.4 434.2 433.5 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 443.6 443.3 442.1 441.4 440.2 439.5 439.1 439.1 439.5 440.2 441.4 441.7 442.9 443.6 444.0 444.0 443.6 442.9 + 441.7 441.4 440.2 439.5 439.1 ][ 189.6 190.3 190.7 190.7 190.3 189.2 187.3 185.5 184.0 183.2 182.8 182.8 183.2 + 184.0 185.1 185.5 186.6 187.3 187.7 187.7 187.3 186.6 185.5 ]plong + 447.0 188.1 447.4 187.7 447.0 187.3 446.6 187.7 447.0 188.1 5 pls + 447.0 183.6 447.4 183.2 447.0 182.8 446.6 183.2 447.0 183.6 5 pls + s[ 452.3 451.1 450.4 450.0 450.0 450.4 451.1 452.3 453.0 454.2 454.9 455.3 455.3 454.9 454.2 453.0 452.3 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 459.8 458.7 457.9 457.5 457.5 457.9 458.7 459.8 460.5 461.7 462.4 462.8 462.8 462.4 461.7 460.5 459.8 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + 465.8 188.1 466.2 187.7 465.8 187.3 465.4 187.7 465.8 188.1 5 pls + 465.8 183.6 466.2 183.2 465.8 182.8 465.4 183.2 465.8 183.6 5 pls + s[ 471.1 469.9 469.2 468.8 468.8 469.2 469.9 471.1 471.8 472.9 473.7 474.1 474.1 473.7 472.9 471.8 471.1 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 478.6 477.5 476.7 476.3 476.3 476.7 477.5 478.6 479.3 480.5 481.2 481.6 481.6 481.2 480.5 479.3 478.6 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 543.37 193.53 moveto[ 543.4 543.4 558.8 558.8 ][ 193.5 204.4 204.4 193.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 547.1 195.0 547.1 202.9 546.0 201.8 545.2 201.4 4 pls + 553.1 195.0 556.9 202.9 2 pls + 556.9 202.9 551.6 202.9 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 527.31 181.32 moveto[ 527.3 527.3 580.3 580.3 ][ 181.3 192.2 192.2 181.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 531.1 182.8 531.1 190.7 529.9 189.6 529.2 189.2 4 pls + s[ 536.0 536.0 536.3 536.7 537.5 539.0 539.7 540.1 540.5 540.5 540.1 539.3 535.6 540.8 ][ 188.8 189.2 190.0 190.3 + 190.7 190.7 190.3 190.0 189.2 188.5 187.7 186.6 182.8 182.8 ]plong + 543.8 188.1 544.2 187.7 543.8 187.3 543.5 187.7 543.8 188.1 5 pls + 543.8 183.6 544.2 183.2 543.8 182.8 543.5 183.2 543.8 183.6 5 pls + s[ 549.1 548.0 547.2 546.9 546.9 547.2 548.0 549.1 549.9 551.0 551.7 552.1 552.1 551.7 551.0 549.9 549.1 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 556.6 555.5 554.7 554.4 554.4 554.7 555.5 556.6 557.4 558.5 559.3 559.6 559.6 559.3 558.5 557.4 556.6 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + 562.6 188.1 563.0 187.7 562.6 187.3 562.3 187.7 562.6 188.1 5 pls + 562.6 183.6 563.0 183.2 562.6 182.8 562.3 183.2 562.6 183.6 5 pls + s[ 567.9 566.8 566.0 565.6 565.6 566.0 566.8 567.9 568.7 569.8 570.5 570.9 570.9 570.5 569.8 568.7 567.9 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong + s[ 575.4 574.3 573.5 573.2 573.2 573.5 574.3 575.4 576.2 577.3 578.1 578.4 578.4 578.1 577.3 576.2 575.4 ][ 190.7 + 190.3 189.2 187.3 186.2 184.3 183.2 182.8 182.8 183.2 184.3 186.2 187.3 189.2 190.3 190.7 190.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 185.09 204.33 moveto[ 185.1 185.1 194.1 194.1 ][ 204.3 215.2 215.2 204.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 187.7 191.9 189.6 190.7 191.5 191.9 192.2 192.2 191.9 191.1 190.0 188.8 187.7 187.3 187.0 ][ 213.7 213.7 210.7 + 210.7 210.3 210.0 208.8 208.1 207.0 206.2 205.8 205.8 206.2 206.6 207.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 173.81 244.97 moveto[ 173.8 173.8 191.9 191.9 ][ 245.0 255.9 255.9 245.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 176.4 180.6 178.3 179.4 180.2 180.6 181.0 181.0 180.6 179.8 178.7 177.6 176.4 176.1 175.7 ][ 254.4 254.4 251.4 + 251.4 251.0 250.6 249.5 248.7 247.6 246.8 246.5 246.5 246.8 247.2 248.0 ]plong + 184.0 247.2 184.3 246.8 184.0 246.5 183.6 246.8 184.0 247.2 5 pls + 190.0 246.5 190.0 254.4 188.8 253.2 188.1 252.9 4 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 173.81 285.61 moveto[ 173.8 173.8 194.1 194.1 ][ 285.6 296.5 296.5 285.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 176.4 180.6 178.3 179.4 180.2 180.6 181.0 181.0 180.6 179.8 178.7 177.6 176.4 176.1 175.7 ][ 295.0 295.0 292.0 + 292.0 291.6 291.2 290.1 289.4 288.2 287.5 287.1 287.1 287.5 287.9 288.6 ]plong + 184.0 287.9 184.3 287.5 184.0 287.1 183.6 287.5 184.0 287.9 5 pls + s[ 187.3 187.3 187.7 188.1 188.8 190.3 191.1 191.5 191.9 191.9 191.5 190.7 187.0 192.2 ][ 293.1 293.5 294.3 294.6 + 295.0 295.0 294.6 294.3 293.5 292.7 292.0 290.9 287.1 287.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 173.81 326.24 moveto[ 173.8 173.8 194.1 194.1 ][ 326.2 337.1 337.1 326.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 176.4 180.6 178.3 179.4 180.2 180.6 181.0 181.0 180.6 179.8 178.7 177.6 176.4 176.1 175.7 ][ 335.6 335.6 332.6 + 332.6 332.3 331.9 330.8 330.0 328.9 328.1 327.7 327.7 328.1 328.5 329.3 ]plong + 184.0 328.5 184.3 328.1 184.0 327.7 183.6 328.1 184.0 328.5 5 pls + s[ 187.7 191.9 189.6 190.7 191.5 191.9 192.2 192.2 191.9 191.1 190.0 188.8 187.7 187.3 187.0 ][ 335.6 335.6 332.6 + 332.6 332.3 331.9 330.8 330.0 328.9 328.1 327.7 327.7 328.1 328.5 329.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 351.88 154.09 moveto[ 351.9 351.9 438.0 438.0 ][ 154.1 169.1 169.1 154.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 353.8 158.2 353.8 166.1 2 pls + s[ 353.8 356.4 357.5 358.3 358.6 359.0 359.0 358.6 358.3 357.5 356.4 353.8 ][ 166.1 166.1 165.7 165.0 164.2 163.1 + 161.2 160.1 159.4 158.6 158.2 158.2 ]plong + 365.8 158.2 365.8 163.5 2 pls + s[ 365.8 365.0 364.3 363.2 362.4 361.7 361.3 361.3 361.7 362.4 363.2 364.3 365.0 365.8 ][ 162.4 163.1 163.5 163.5 + 163.1 162.4 161.2 160.5 159.4 158.6 158.2 158.2 158.6 159.4 ]plong + 371.1 158.2 370.3 158.2 369.5 158.6 369.2 159.7 369.2 166.1 5 pls + 370.7 163.5 368.0 163.5 2 pls + s[ 372.9 377.4 377.4 377.1 376.7 375.9 374.8 374.1 373.3 372.9 372.9 373.3 374.1 374.8 375.9 376.7 377.4 ][ 161.2 + 161.2 162.0 162.7 163.1 163.5 163.5 163.1 162.4 161.2 160.5 159.4 158.6 158.2 158.2 158.6 159.4 ]plong + 379.3 155.6 386.1 167.6 2 pls + 389.8 158.2 389.8 166.1 2 pls + 392.5 166.1 387.2 166.1 2 pls + 394.0 166.1 394.4 166.5 394.7 166.1 394.4 165.7 394.0 166.1 5 pls + 394.4 158.2 394.4 163.5 2 pls + 397.4 158.2 397.4 163.5 2 pls + s[ 397.4 398.5 399.2 400.4 401.1 401.5 401.5 ][ 162.0 163.1 163.5 163.5 163.1 162.0 158.2 ]plong + s[ 401.5 402.6 403.4 404.5 405.3 405.6 405.6 ][ 162.0 163.1 163.5 163.5 163.1 162.0 158.2 ]plong + s[ 408.3 412.8 412.8 412.4 412.0 411.3 410.1 409.4 408.6 408.3 408.3 408.6 409.4 410.1 411.3 412.0 412.8 ][ 161.2 + 161.2 162.0 162.7 163.1 163.5 163.5 163.1 162.4 161.2 160.5 159.4 158.6 158.2 158.2 158.6 159.4 ]plong + s[ 424.1 423.3 422.6 421.8 421.4 421.4 421.8 422.6 423.3 424.1 ][ 167.6 166.9 165.7 164.2 162.4 160.9 159.0 157.5 + 156.3 155.6 ]plong + 430.8 158.2 430.8 166.1 2 pls + s[ 430.8 430.1 429.3 428.2 427.4 426.7 426.3 426.3 426.7 427.4 428.2 429.3 430.1 430.8 ][ 162.4 163.1 163.5 163.5 + 163.1 162.4 161.2 160.5 159.4 158.6 158.2 158.2 158.6 159.4 ]plong + s[ 433.5 434.2 435.0 435.7 436.1 436.1 435.7 435.0 434.2 433.5 ][ 167.6 166.9 165.7 164.2 162.4 160.9 159.0 157.5 + 156.3 155.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 160.49 207.38 moveto[ 160.5 145.5 145.5 160.5 ][ 207.4 207.4 335.6 335.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.4 209.3 148.5 209.3 2 pls + s[ 148.5 148.5 148.8 149.6 150.3 151.5 153.3 154.5 155.2 156.0 156.4 156.4 ][ 209.3 211.9 213.0 213.8 214.1 214.5 + 214.5 214.1 213.8 213.0 211.9 209.3 ]plong + 156.4 221.3 151.1 221.3 2 pls + s[ 152.2 151.5 151.1 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 ][ 221.3 220.5 219.8 218.7 + 217.9 217.1 216.8 216.8 217.1 217.9 218.7 219.8 220.5 221.3 ]plong + 156.4 226.5 156.4 225.8 156.0 225.0 154.9 224.7 148.5 224.7 5 pls + 151.1 226.2 151.1 223.5 2 pls + 156.4 232.9 151.1 232.9 2 pls + s[ 152.2 151.5 151.1 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 ][ 232.9 232.2 231.4 230.3 + 229.6 228.8 228.4 228.4 228.8 229.6 230.3 231.4 232.2 232.9 ]plong + s[ 151.1 154.9 156.0 156.4 156.4 156.0 154.9 ][ 242.0 242.0 242.3 243.1 244.2 245.0 246.1 ]plong + 156.4 246.1 151.1 246.1 2 pls + 159.0 249.1 151.1 249.1 2 pls + s[ 152.2 151.5 151.1 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 ][ 249.1 249.9 250.6 251.7 + 252.5 253.2 253.6 253.6 253.2 252.5 251.7 250.6 249.9 249.1 ]plong + 153.0 263.0 153.0 256.2 2 pls + 156.4 266.0 148.5 266.0 2 pls + s[ 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 154.1 153.3 152.2 151.5 151.1 151.1 ][ 270.5 + 269.8 269.0 268.6 268.6 269.0 269.8 270.5 271.7 272.4 273.2 273.5 273.5 273.2 272.4 271.7 270.5 ]plong + 156.4 280.3 151.1 280.3 2 pls + s[ 152.2 151.5 151.1 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 ][ 280.3 279.6 278.8 277.7 + 276.9 276.2 275.8 275.8 276.2 276.9 277.7 278.8 279.6 280.3 ]plong + 156.4 287.4 148.5 287.4 2 pls + s[ 152.2 151.5 151.1 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 ][ 287.4 286.7 285.9 284.8 + 284.1 283.3 282.9 282.9 283.3 284.1 284.8 285.9 286.7 287.4 ]plong + s[ 153.3 153.3 152.6 151.8 151.5 151.1 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 ][ 290.1 + 294.6 294.6 294.2 293.8 293.1 292.0 291.2 290.5 290.1 290.1 290.5 291.2 292.0 293.1 293.8 294.6 ]plong + 156.4 301.4 148.5 301.4 2 pls + s[ 152.2 151.5 151.1 151.1 151.5 152.2 153.3 154.1 155.2 156.0 156.4 156.4 156.0 155.2 ][ 301.4 300.6 299.8 298.7 + 298.0 297.2 296.8 296.8 297.2 298.0 298.7 299.8 300.6 301.4 ]plong + s[ 147.0 147.7 148.8 150.3 152.2 153.7 155.6 157.1 158.2 159.0 ][ 313.0 312.3 311.5 310.8 310.4 310.4 310.8 311.5 + 312.3 313.0 ]plong + s[ 150.3 149.6 148.8 148.5 148.5 148.8 149.6 150.3 151.5 153.3 154.5 155.2 156.0 156.4 156.4 156.0 155.2 154.5 + 153.3 ][ 320.9 320.5 319.8 319.0 317.5 316.8 316.0 315.6 315.3 315.3 315.6 316.0 316.8 317.5 319.0 319.8 320.5 + 320.9 320.9 ]plong + 153.3 320.9 153.3 319.0 2 pls + 156.4 323.5 148.5 323.5 2 pls + s[ 148.5 148.5 148.8 149.2 150.0 150.7 151.5 151.8 152.2 ][ 323.5 326.9 328.0 328.4 328.8 328.8 328.4 328.0 326.9 +]plong + s[ 152.2 152.2 152.6 153.0 153.7 154.9 155.6 156.0 156.4 156.4 ][ 323.5 326.9 328.0 328.4 328.8 328.8 328.4 328.0 + 326.9 323.5 ]plong + s[ 147.0 147.7 148.8 150.3 152.2 153.7 155.6 157.1 158.2 159.0 ][ 331.0 331.8 332.6 333.3 333.7 333.7 333.3 332.6 + 331.8 331.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 364.29 343.24 moveto[ 364.3 364.3 431.2 431.2 ][ 343.2 354.1 354.1 343.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 366.2 344.7 369.2 352.6 2 pls + 372.2 344.7 369.2 352.6 2 pls + 371.1 347.4 367.3 347.4 2 pls + 380.1 344.7 380.1 352.6 2 pls + 385.0 352.6 380.1 352.6 2 pls + 383.1 348.9 380.1 348.9 2 pls + 386.8 344.7 386.8 352.6 2 pls + 391.4 344.7 391.4 352.6 2 pls + 394.0 352.6 388.7 352.6 2 pls + s[ 400.7 400.0 398.9 397.4 396.2 395.5 395.5 395.9 396.2 397.0 399.2 400.0 400.4 400.7 400.7 400.0 398.9 397.4 + 396.2 395.5 ][ 351.5 352.3 352.6 352.6 352.3 351.5 350.8 350.0 349.6 349.3 348.5 348.1 347.8 347.0 345.9 345.1 + 344.7 344.7 345.1 345.9 ]plong + 411.7 344.7 410.9 344.7 410.1 345.1 409.8 346.2 409.8 352.6 5 pls + 411.3 350.0 408.6 350.0 2 pls + s[ 413.5 418.0 418.0 417.7 417.3 416.5 415.4 414.7 413.9 413.5 413.5 413.9 414.7 415.4 416.5 417.3 418.0 ][ 347.8 + 347.8 348.5 349.3 349.6 350.0 350.0 349.6 348.9 347.8 347.0 345.9 345.1 344.7 344.7 345.1 345.9 ]plong + s[ 424.4 424.1 422.9 421.8 420.7 420.3 420.7 421.4 423.3 424.1 424.4 424.4 424.1 422.9 421.8 420.7 420.3 ][ 348.9 + 349.6 350.0 350.0 349.6 348.9 348.1 347.8 347.4 347.0 346.2 345.9 345.1 344.7 344.7 345.1 345.9 ]plong + 429.3 344.7 428.6 344.7 427.8 345.1 427.4 346.2 427.4 352.6 5 pls + 428.9 350.0 426.3 350.0 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/timj.ast b/ast_tester/timj.ast new file mode 100644 index 0000000..ed011dd --- /dev/null +++ b/ast_tester/timj.ast @@ -0,0 +1,234 @@ + Begin FrameSet +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic projection" # Title of coordinate system +# Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction + IsA Frame # Coordinate system description + Nframe = 4 # Number of Frames in FrameSet +# Base = 1 # Index of base Frame + Currnt = 4 # Index of current Frame + Nnode = 5 # Number of nodes in FrameSet + Nod1 = 3 # Frame 1 is associated with node 3 + Nod2 = 4 # Frame 2 is associated with node 4 + Nod3 = 5 # Frame 3 is associated with node 5 + Nod4 = 2 # Frame 4 is associated with node 2 + Lnk2 = 1 # Node 2 is derived from node 1 + Lnk3 = 1 # Node 3 is derived from node 1 + Lnk4 = 1 # Node 4 is derived from node 1 + Lnk5 = 1 # Node 5 is derived from node 1 + Frm1 = # Frame number 1 + Begin Frame + Title = "Data grid indices; first pixel at (1,1)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "GRID" # Coordinate system domain +# Lbl1 = "Data grid index 1" # Label for axis 1 +# Lbl2 = "Data grid index 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + Ax1 = # Axis number 1 + Begin Axis + Label = "Data grid index 1" # Axis Label + Symbol = "g1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis + Label = "Data grid index 2" # Axis Label + Symbol = "g2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm2 = # Frame number 2 + Begin Frame + Title = "Pixel coordinates; first pixel at (-128.5,-127.5)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "PIXEL" # Coordinate system domain +# Lbl1 = "Pixel coordinate 1" # Label for axis 1 +# Lbl2 = "Pixel coordinate 2" # Label for axis 2 +# Uni1 = "pixel" # Units for axis 1 +# Uni2 = "pixel" # Units for axis 2 + Ax1 = # Axis number 1 + Begin Axis + Label = "Pixel coordinate 1" # Axis Label + Symbol = "p1" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + Ax2 = # Axis number 2 + Begin Axis + Label = "Pixel coordinate 2" # Axis Label + Symbol = "p2" # Axis symbol + Unit = "pixel" # Axis units + Format = "%3.1f" # Format specifier + End Axis + End Frame + Frm3 = # Frame number 3 + Begin Frame + Title = "Axis coordinates; first pixel at (384,-381)" # Title of coordinate system + Naxes = 2 # Number of coordinate axes + Domain = "AXIS" # Coordinate system domain +# Lbl1 = "R.A. offset" # Label for axis 1 +# Lbl2 = "Declination offset" # Label for axis 2 +# Uni1 = "arcsec" # Units for axis 1 +# Uni2 = "arcsec" # Units for axis 2 + Ax1 = # Axis number 1 + Begin Axis + Label = "R.A. offset" # Axis Label + Symbol = "a1" # Axis symbol + Unit = "arcsec" # Axis units + End Axis + Ax2 = # Axis number 2 + Begin Axis + Label = "Declination offset" # Axis Label + Symbol = "a2" # Axis symbol + Unit = "arcsec" # Axis units + End Axis + End Frame + Frm4 = # Frame number 4 + Begin SkyFrame +# Title = "FK5 equatorial coordinates; mean equinox J2000.0; gnomonic projection" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction + Ax1 = # Axis number 1 + Begin SkyAxis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis + End SkyAxis + IsA Frame # Coordinate system description + System = "FK5" # Celestial coordinate system type + Proj = "gnomonic" # Description of sky projection + Epoch = 2002.3821786502 # Julian epoch of observation + Eqnox = 2000 # Julian epoch of mean equinox + End SkyFrame + Map2 = # Mapping between nodes 1 and 2 + Begin CmpMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin WinMap + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + Sft1 = -129 # Shift for axis 1 + Sft2 = -128 # Shift for axis 2 + End WinMap + MapB = # Second component Mapping + Begin CmpMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap + Nin = 2 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = -1.45444097695548e-05 # Forward matrix value + M1 = 1.45444097695548e-05 # Forward matrix value + Form = "Diagonal" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin WcsMap + Nin = 2 # Number of input coordinates + Invert = 1 # Mapping inverted + IsA Mapping # Mapping between coordinate systems + Type = "TAN" # Gnomonic projection + End WcsMap + MapB = # Second component Mapping + Begin CmpMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + InvA = 1 # First Mapping used in inverse direction + MapA = # First component Mapping + Begin SphMap + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + End SphMap + MapB = # Second component Mapping + Begin CmpMap + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin MatrixMap + Nin = 3 # Number of input coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + M0 = 0.425569982779421 # Forward matrix value + M1 = 0.773738866158297 # Forward matrix value + M2 = 0.469274287334386 # Forward matrix value + M3 = -0.519775230563316 # Forward matrix value + M4 = 0.633504670066506 # Forward matrix value + M5 = -0.573154030516038 # Forward matrix value + M6 = -0.740759002274002 # Forward matrix value + M7 = 0 # Forward matrix value + M8 = 0.671770869084113 # Forward matrix value + Form = "Full" # Matrix storage form + End MatrixMap + MapB = # Second component Mapping + Begin CmpMap + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + IsA Mapping # Mapping between coordinate systems + MapA = # First component Mapping + Begin SphMap + Nin = 3 # Number of input coordinates + Nout = 2 # Number of output coordinates + Invert = 0 # Mapping not inverted + IsA Mapping # Mapping between coordinate systems + UntRd = 1 # All input vectors have unit length + End SphMap + MapB = # Second component Mapping + Begin SlaMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Nsla = 1 # Number of conversion steps + Sla1 = "FK45Z" # FK4 to FK5 J2000.0 (no PM or parallax) + Sla1a = 2002.38350704493 # Besselian epoch of FK4 coordinates + End SlaMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + End CmpMap + Map3 = # Mapping between nodes 1 and 3 + Begin UnitMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + End UnitMap + Map4 = # Mapping between nodes 1 and 4 + Begin WinMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Sft1 = -129.5 # Shift for axis 1 + Sft2 = -128.5 # Shift for axis 2 + End WinMap + Map5 = # Mapping between nodes 1 and 5 + Begin WinMap + Nin = 2 # Number of input coordinates + IsA Mapping # Mapping between coordinate systems + Sft1 = 387 # Shift for axis 1 + Scl1 = -3 # Scale factor for axis 1 + Sft2 = -384 # Shift for axis 2 + Scl2 = 3 # Scale factor for axis 2 + End WinMap + End FrameSet diff --git a/ast_tester/timj.fits-aips b/ast_tester/timj.fits-aips new file mode 100644 index 0000000..4fdd94b --- /dev/null +++ b/ast_tester/timj.fits-aips @@ -0,0 +1,11 @@ +CRPIX1 = 129.0 / Reference pixel on axis 1 +CRPIX2 = 128.0 / Reference pixel on axis 2 +CRVAL1 = 309.75469 / Value at ref. pixel on axis 1 +CRVAL2 = 42.381032 / Value at ref. pixel on axis 2 +CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2 +CDELT1 = -8.3333273E-4 / Pixel size +CDELT2 = 0.00083333273 / Pixel size +CROTA2 = 0.29013902 / Axis rotation +EPOCH = 2000.0 / Epoch of reference equinox +DATE-OBS= '2002-05-20T14:09:36.786' / Date of observation diff --git a/ast_tester/timj.fits-iraf b/ast_tester/timj.fits-iraf new file mode 100644 index 0000000..88c3d94 --- /dev/null +++ b/ast_tester/timj.fits-iraf @@ -0,0 +1,13 @@ +CRPIX1 = 129.0 / Reference pixel on axis 1 +CRPIX2 = 128.0 / Reference pixel on axis 2 +CRVAL1 = 309.75469 / Value at ref. pixel on axis 1 +CRVAL2 = 42.381032 / Value at ref. pixel on axis 2 +CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2 +CD1_1 = -8.3332205E-4 / Transformation matrix element +CD1_2 = -4.2198799E-6 / Transformation matrix element +CD2_1 = -4.2198799E-6 / Transformation matrix element +CD2_2 = 0.00083332205 / Transformation matrix element +RADECSYS= 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / Epoch of reference equinox +DATE-OBS= '2002-05-20T14:09:36.786' / Date of observation diff --git a/ast_tester/timj.fits-pc b/ast_tester/timj.fits-pc new file mode 100644 index 0000000..f3bfc6f --- /dev/null +++ b/ast_tester/timj.fits-pc @@ -0,0 +1,16 @@ +PC001001= 1.0 +PC001002= 0.0050638595 +PC002001= -0.0050638595 +PC002002= 1.0 +CDELT1 = -8.3333273E-4 / Pixel scale on axis 1 +CDELT2 = 0.00083333273 / Pixel scale on axis 2 +CRPIX1 = 129.0 / Reference pixel on axis 1 +CRPIX2 = 128.0 / Reference pixel on axis 2 +CRVAL1 = 309.75469 / Value at ref. pixel on axis 1 +CRVAL2 = 42.381032 / Value at ref. pixel on axis 2 +CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2 +RADECSYS= 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / Epoch of reference equinox +MJD-OBS = 52414.59 / Modified Julian Date of observation +DATE-OBS= '2002-05-20T14:09:36.786' / Date of observation diff --git a/ast_tester/timj.fits-wcs b/ast_tester/timj.fits-wcs new file mode 100644 index 0000000..f8b3e8a --- /dev/null +++ b/ast_tester/timj.fits-wcs @@ -0,0 +1,39 @@ +WCSAXES = 2 / Number of WCS axes +CRPIX1 = 129.0 / Reference pixel on axis 1 +CRPIX2 = 128.0 / Reference pixel on axis 2 +CRVAL1 = 309.75469 / Value at ref. pixel on axis 1 +CRVAL2 = 42.381032 / Value at ref. pixel on axis 2 +CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1 +CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2 +CD1_1 = -8.3332205E-4 / Transformation matrix element +CD1_2 = -4.2198799E-6 / Transformation matrix element +CD2_1 = -4.2198799E-6 / Transformation matrix element +CD2_2 = 0.00083332205 / Transformation matrix element +MJD-OBS = 52414.59 / Modified Julian Date of observation +DATE-OBS= '2002-05-20T14:09:36.786' / Date of observation +RADESYS = 'FK5 ' / Reference frame for RA/DEC values +EQUINOX = 2000.0 / [yr] Epoch of reference equinox +WCSAXESA= 2 / Number of WCS axes +WCSNAMEA= 'PIXEL ' / Reference name for the coord. frame +CRPIX1A = 1.0 / Reference pixel on axis 1 +CRPIX2A = 1.0 / Reference pixel on axis 2 +CRVAL1A = -128.5 / Value at ref. pixel on axis 1 +CRVAL2A = -127.5 / Value at ref. pixel on axis 2 +CTYPE1A = 'p1 ' / Pixel coordinate 1 +CTYPE2A = 'p2 ' / Pixel coordinate 2 +CD1_1A = 1.0 / Transformation matrix element +CD2_2A = 1.0 / Transformation matrix element +CUNIT1A = 'pixel ' / Units for axis 1 +CUNIT2A = 'pixel ' / Units for axis 2 +WCSAXESB= 2 / Number of WCS axes +WCSNAMEB= 'AXIS ' / Reference name for the coord. frame +CRPIX1B = 1.0 / Reference pixel on axis 1 +CRPIX2B = 1.0 / Reference pixel on axis 2 +CRVAL1B = 384.0 / Value at ref. pixel on axis 1 +CRVAL2B = -381.0 / Value at ref. pixel on axis 2 +CTYPE1B = 'a1 ' / R.A. offset +CTYPE2B = 'a2 ' / Declination offset +CD1_1B = -3.0 / Transformation matrix element +CD2_2B = 3.0 / Transformation matrix element +CUNIT1B = 'arcsec ' / Units for axis 1 +CUNIT2B = 'arcsec ' / Units for axis 2 diff --git a/ast_tester/timj.native b/ast_tester/timj.native new file mode 100644 index 0000000..fb54315 --- /dev/null +++ b/ast_tester/timj.native @@ -0,0 +1,217 @@ + +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST WCS information in AST format AST +COMMENT AST See http://www.starlink.ac.uk/ast/ AST +COMMENT AST Beginning of AST data for FrameSet object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'FrameSet' / Set of inter-related coordinate systems +NFRAME_A= 4 / Number of Frames in FrameSet +CURRNT_A= 4 / Index of current Frame +NNODE_A = 5 / Number of nodes in FrameSet +NOD1_A = 3 / Frame 1 is associated with node 3 +NOD2_A = 4 / Frame 2 is associated with node 4 +NOD3_A = 5 / Frame 3 is associated with node 5 +NOD4_A = 2 / Frame 4 is associated with node 2 +LNK2_A = 1 / Node 2 is derived from node 1 +LNK3_A = 1 / Node 3 is derived from node 1 +LNK4_A = 1 / Node 4 is derived from node 1 +LNK5_A = 1 / Node 5 is derived from node 1 +FRM1_A = ' ' / Frame number 1 +BEGAST_B= 'Frame ' / Coordinate system description +TITLE_A = 'Data grid indices; first pixel at (1&' / Title of coordinate system +CONTINUE ',1) ' +NAXES_A = 2 / Number of coordinate axes +DOMAIN_A= 'GRID ' / Coordinate system domain +AX1_A = ' ' / Axis number 1 +BEGAST_C= 'Axis ' / Coordinate axis +LABEL_A = 'Data grid index 1' / Axis Label +SYMBOL_A= 'g1 ' / Axis symbol +UNIT_A = 'pixel ' / Axis units +FORMAT_A= '%3.1f ' / Format specifier +ENDAST_A= 'Axis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_D= 'Axis ' / Coordinate axis +LABEL_B = 'Data grid index 2' / Axis Label +SYMBOL_B= 'g2 ' / Axis symbol +UNIT_B = 'pixel ' / Axis units +FORMAT_B= '%3.1f ' / Format specifier +ENDAST_B= 'Axis ' / End of object definition +ENDAST_C= 'Frame ' / End of object definition +FRM2_A = ' ' / Frame number 2 +BEGAST_E= 'Frame ' / Coordinate system description +TITLE_B = 'Pixel coordinates; first pixel at (-&' / Title of coordinate system +CONTINUE '128.5,-127.5)' +NAXES_B = 2 / Number of coordinate axes +DOMAIN_B= 'PIXEL ' / Coordinate system domain +AX1_B = ' ' / Axis number 1 +BEGAST_F= 'Axis ' / Coordinate axis +LABEL_C = 'Pixel coordinate 1' / Axis Label +SYMBOL_C= 'p1 ' / Axis symbol +UNIT_C = 'pixel ' / Axis units +FORMAT_C= '%3.1f ' / Format specifier +ENDAST_D= 'Axis ' / End of object definition +AX2_B = ' ' / Axis number 2 +BEGAST_G= 'Axis ' / Coordinate axis +LABEL_D = 'Pixel coordinate 2' / Axis Label +SYMBOL_D= 'p2 ' / Axis symbol +UNIT_D = 'pixel ' / Axis units +FORMAT_D= '%3.1f ' / Format specifier +ENDAST_E= 'Axis ' / End of object definition +ENDAST_F= 'Frame ' / End of object definition +FRM3_A = ' ' / Frame number 3 +BEGAST_H= 'Frame ' / Coordinate system description +TITLE_C = 'Axis coordinates; first pixel at (38&' / Title of coordinate system +CONTINUE '4,-381) ' +NAXES_C = 2 / Number of coordinate axes +DOMAIN_C= 'AXIS ' / Coordinate system domain +AX1_C = ' ' / Axis number 1 +BEGAST_I= 'Axis ' / Coordinate axis +LABEL_E = 'R.A. offset' / Axis Label +SYMBOL_E= 'a1 ' / Axis symbol +UNIT_E = 'arcsec ' / Axis units (arc-second) +ENDAST_G= 'Axis ' / End of object definition +AX2_C = ' ' / Axis number 2 +BEGAST_J= 'Axis ' / Coordinate axis +LABEL_F = 'Declination offset' / Axis Label +SYMBOL_F= 'a2 ' / Axis symbol +UNIT_F = 'arcsec ' / Axis units (arc-second) +ENDAST_H= 'Axis ' / End of object definition +ENDAST_I= 'Frame ' / End of object definition +FRM4_A = ' ' / Frame number 4 +BEGAST_K= 'SkyFrame' / Description of celestial coordinate system +NAXES_D = 2 / Number of coordinate axes +EPOCH_A = 2002.3822 / Julian epoch of observation +SYSTEM_A= 'FK5 ' / Coordinate system type +AX1_D = ' ' / Axis number 1 +BEGAST_L= 'SkyAxis ' / Celestial coordinate axis +ENDAST_J= 'SkyAxis ' / End of object definition +AX2_D = ' ' / Axis number 2 +BEGAST_M= 'SkyAxis ' / Celestial coordinate axis +ENDAST_K= 'SkyAxis ' / End of object definition +ISA_A = 'Frame ' / Coordinate system description +PROJ_A = 'gnomonic' / Description of sky projection +EQNOX_A = 2000.0 / Julian epoch of mean equinox +ENDAST_L= 'SkyFrame' / End of object definition +MAP2_A = ' ' / Mapping between nodes 1 and 2 +BEGAST_N= 'CmpMap ' / Compound Mapping +NIN_A = 2 / Number of input coordinates +ISA_B = 'Mapping ' / Mapping between coordinate systems +MAPA_A = ' ' / First component Mapping +BEGAST_O= 'WinMap ' / Map one window on to another +NIN_B = 2 / Number of input coordinates +INVERT_A= 0 / Mapping not inverted +ISA_C = 'Mapping ' / Mapping between coordinate systems +SFT1_A = -129.0 / Shift for axis 1 +SFT2_A = -128.0 / Shift for axis 2 +ENDAST_M= 'WinMap ' / End of object definition +MAPB_A = ' ' / Second component Mapping +BEGAST_P= 'CmpMap ' / Compound Mapping +NIN_C = 2 / Number of input coordinates +ISA_D = 'Mapping ' / Mapping between coordinate systems +MAPA_B = ' ' / First component Mapping +BEGAST_Q= 'MatrixMap' / Matrix transformation +NIN_D = 2 / Number of input coordinates +INVERT_B= 0 / Mapping not inverted +ISA_E = 'Mapping ' / Mapping between coordinate systems +M0_A = -1.454441E-5 / Forward matrix value +M1_A = 1.454441E-5 / Forward matrix value +FORM_A = 'Diagonal' / Matrix storage form +ENDAST_N= 'MatrixMap' / End of object definition +MAPB_B = ' ' / Second component Mapping +BEGAST_R= 'CmpMap ' / Compound Mapping +NIN_E = 2 / Number of input coordinates +ISA_F = 'Mapping ' / Mapping between coordinate systems +INVA_A = 1 / First Mapping used in inverse direction +MAPA_C = ' ' / First component Mapping +BEGAST_S= 'WcsMap ' / FITS-WCS sky projection +NIN_F = 2 / Number of input coordinates +INVERT_C= 1 / Mapping inverted +ISA_G = 'Mapping ' / Mapping between coordinate systems +TYPE_A = 'TAN ' / Gnomonic projection +ENDAST_O= 'WcsMap ' / End of object definition +MAPB_C = ' ' / Second component Mapping +BEGAST_T= 'CmpMap ' / Compound Mapping +NIN_G = 2 / Number of input coordinates +ISA_H = 'Mapping ' / Mapping between coordinate systems +INVA_B = 1 / First Mapping used in inverse direction +MAPA_D = ' ' / First component Mapping +BEGAST_U= 'SphMap ' / Cartesian to Spherical mapping +NIN_H = 3 / Number of input coordinates +NOUT_A = 2 / Number of output coordinates +INVERT_D= 0 / Mapping not inverted +ISA_I = 'Mapping ' / Mapping between coordinate systems +UNTRD_A = 1 / All input vectors have unit length +ENDAST_P= 'SphMap ' / End of object definition +MAPB_D = ' ' / Second component Mapping +BEGAST_V= 'CmpMap ' / Compound Mapping +NIN_I = 3 / Number of input coordinates +NOUT_B = 2 / Number of output coordinates +ISA_J = 'Mapping ' / Mapping between coordinate systems +MAPA_E = ' ' / First component Mapping +BEGAST_W= 'MatrixMap' / Matrix transformation +NIN_J = 3 / Number of input coordinates +INVERT_E= 0 / Mapping not inverted +ISA_K = 'Mapping ' / Mapping between coordinate systems +M0_B = 0.42556998 / Forward matrix value +M1_B = 0.77373887 / Forward matrix value +M2_A = 0.46927429 / Forward matrix value +M3_A = -0.51977523 / Forward matrix value +M4_A = 0.63350467 / Forward matrix value +M5_A = -0.57315403 / Forward matrix value +M6_A = -0.740759 / Forward matrix value +M7_A = 0.0 / Forward matrix value +M8_A = 0.67177087 / Forward matrix value +FORM_B = 'Full ' / Matrix storage form +ENDAST_Q= 'MatrixMap' / End of object definition +MAPB_E = ' ' / Second component Mapping +BEGAST_X= 'CmpMap ' / Compound Mapping +NIN_K = 3 / Number of input coordinates +NOUT_C = 2 / Number of output coordinates +ISA_L = 'Mapping ' / Mapping between coordinate systems +MAPA_F = ' ' / First component Mapping +BEGAST_Y= 'SphMap ' / Cartesian to Spherical mapping +NIN_L = 3 / Number of input coordinates +NOUT_D = 2 / Number of output coordinates +INVERT_F= 0 / Mapping not inverted +ISA_M = 'Mapping ' / Mapping between coordinate systems +UNTRD_B = 1 / All input vectors have unit length +ENDAST_R= 'SphMap ' / End of object definition +MAPB_F = ' ' / Second component Mapping +BEGAST_Z= 'SlaMap ' / Conversion between sky coordinate systems +NIN_M = 2 / Number of input coordinates +ISA_N = 'Mapping ' / Mapping between coordinate systems +NSLA_A = 1 / Number of conversion steps +SLA1_A = 'FK45Z ' / FK4 to FK5 J2000.0 (no PM or parallax) +SLA1A_A = 2002.3835 / Besselian epoch of FK4 coordinates +ENDAST_S= 'SlaMap ' / End of object definition +ENDAST_T= 'CmpMap ' / End of object definition +ENDAST_U= 'CmpMap ' / End of object definition +ENDAST_V= 'CmpMap ' / End of object definition +ENDAST_W= 'CmpMap ' / End of object definition +ENDAST_X= 'CmpMap ' / End of object definition +ENDAST_Y= 'CmpMap ' / End of object definition +MAP3_A = ' ' / Mapping between nodes 1 and 3 +BEGASTA_= 'UnitMap ' / Unit (null) Mapping +NIN_N = 2 / Number of input coordinates +ISA_O = 'Mapping ' / Mapping between coordinate systems +ENDAST_Z= 'UnitMap ' / End of object definition +MAP4_A = ' ' / Mapping between nodes 1 and 4 +BEGASTAA= 'WinMap ' / Map one window on to another +NIN_O = 2 / Number of input coordinates +ISA_P = 'Mapping ' / Mapping between coordinate systems +SFT1_B = -129.5 / Shift for axis 1 +SFT2_B = -128.5 / Shift for axis 2 +ENDASTA_= 'WinMap ' / End of object definition +MAP5_A = ' ' / Mapping between nodes 1 and 5 +BEGASTAB= 'WinMap ' / Map one window on to another +NIN_P = 2 / Number of input coordinates +ISA_Q = 'Mapping ' / Mapping between coordinate systems +SFT1_C = 387.0 / Shift for axis 1 +SCL1_A = -3.0 / Scale factor for axis 1 +SFT2_C = -384.0 / Shift for axis 2 +SCL2_A = 3.0 / Scale factor for axis 2 +ENDASTAA= 'WinMap ' / End of object definition +ENDASTAB= 'FrameSet' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for FrameSet object AST +COMMENT AST ---------------------------------------------------------------- AST diff --git a/ast_tester/tnx.attr b/ast_tester/tnx.attr new file mode 100644 index 0000000..e5498b5 --- /dev/null +++ b/ast_tester/tnx.attr @@ -0,0 +1 @@ +Grid=1,textlabgap=0.1,border=0 diff --git a/ast_tester/tnx.box b/ast_tester/tnx.box new file mode 100644 index 0000000..f69daff --- /dev/null +++ b/ast_tester/tnx.box @@ -0,0 +1 @@ +0.0 0.0 2048.0 4096.0 diff --git a/ast_tester/tnx.head b/ast_tester/tnx.head new file mode 100644 index 0000000..58c6aa0 --- /dev/null +++ b/ast_tester/tnx.head @@ -0,0 +1,180 @@ +SIMPLE = T / Fits standard +BITPIX = 16 / Bits per pixel +NAXIS = 2 / Number of axes +NAXIS1 = 2048 / Axis length +NAXIS2 = 4096 / Axis length +EXTEND = F / File may contain extensions +ORIGIN = 'NOAO-IRAF FITS Image Kernel July 1999' / FITS file originator +DATE = '2001-11-20T16:52:11' / Date FITS file was generated +IRAF-TLM= '16:52:09 (20/11/2001)' / Time of last modification +OBJECT = 'A576 R ' / Name of the object observed +NEXTEND = 8 / Number of extensions +FILENAME= 'obj214r ' / Original host filename +OBSTYPE = 'object ' / Observation type +EXPTIME = 350.000 / Exposure time (sec) +DARKTIME= 351.888 / Dark time (sec) +PREFLASH= 0.000000 / Preflash time (sec) +RADECSYS= 'FK5 ' / Default coordinate system +RA = '07:21:44.00 ' / RA of observation (hr) +DEC = '55:53:16.99 ' / DEC of observation (deg) +EQUINOX = 1.9998000490000E+03 / Equinox of coordinate system + +TIMESYS = 'UTC approximate' / Time system +DATE-OBS= '1999-10-09' / Date of observation start (UTC approximate) +TIME-OBS= '12:08:33.0' / Time of observation start +MJD-OBS = 51460.50593750 / MJD of observation start +MJDHDR = 51460.50591435 / MJD of header creation +LSTHDR = '05:52:50.00 ' / LST of header creation + +OBSERVAT= 'KPNO ' / Observatory +TELESCOP= 'KPNO 0.9 meter telescope' / Telescope +ZD = 2.8469999000000E+01 / Zenith distance +AIRMASS = 1.1400000000000E+00 / Airmass +TELFOCUS= 4414 / Telescope focus +CORRCTOR= 'KPNO 0.9m Corrector' / Corrector Identification +ADC = 'none ' / ADC Identification + +DETECTOR= 'CCDMosaThin1' / Detector +DETSIZE = '[1:8176,1:8192]' / Detector size for DETSEC +NCCDS = 8 / Number of CCDs +NAMPS = 8 / Number of Amplifiers +PIXSIZE1= 15. / Pixel size for axis 1 (microns) +PIXSIZE2= 15. / Pixel size for axis 2 (microns) +PIXSCAL1= 0.423 / Pixel scale for axis 1 (arcsec/pixel) +PIXSCAL2= 0.423 / Pixel scale for axis 2 (arcsec/pixel) +RAPANGL = 0. / Position angle of RA axis (deg) +DECPANGL= 270. / Position angle of DEC axis (deg) +FILPOS = 4 / Filter position +FILTER = 'R ' / Filter name(s) +SHUTSTAT= 'guide ' / Shutter status +TV1FOC = -5.9600000000000E-01 / North TV Camera focus position +TV2FOC = -1.6230000000000E+00 / South Camera focus position +ENVTEM = 1.7299999000000E+01 / Ambient temperature (C) +DEWAR = 'CCDMosaThin1 Dewar' / Dewar identification +DEWTEM1 = -1.7060000600000E+02 / Dewar temperature (C) +DEWTEM2 = -4.7000000000000E+00 / Fill Neck temperature (C) +CCDTEM = -9.5000000000000E+01 / CCD temperature (C) + +CONTROLR= 'Mosaic Arcon' / Controller identification +CONSWV = '2.000 13Feb96 (add mode and group to hdrs)' / Controller softwar +AMPINTEG= 3000 / (ns) Double Correlated Sample time +READTIME= 13200 / (ns) unbinned pixel read time +ARCONWD = 'Obs Thu Aug 19 10:45:52 1999' / Date waveforms last compiled +ARCONWM = 'OverlapXmit EarlyReset SplitShift ' / Waveform mode switches on +ARCONGI = 1 / Gain selection (index into Gain Table) + +OBSERVER= 'G.Wegner,M.Hudson' / Observer(s) +PROPOSER= '' / Proposer(s) +PROPOSAL= '' / Proposal title +PROPID = '99B-0020' / Proposal identification +OBSID = 'kp09m.19991009T120833' / Observation ID + +IMAGESWV= 'mosdca (Aug99), CCDMosaThin1V8.tcl (Aug99)' / Image creation software +KWDICT = 'MosaicV1.dic (Sep97)' / Keyword dictionary + +CHECKSUM= '' / Header checksum +DATASUM = '' / Data checksum +CHECKVER= '' / Checksum version +RECNO = 0 / NOAO archive sequence number +IMAGEID = 5 / Image identification + +TELRA = '07:21:44.00 ' / RA of observation (hr) +TELDEC = '55:53:16.99 ' / DEC of observation (deg) +TELEQUIN= 1.9998000490000E+03 / Equinox of coordinate system +TELRADEC= 'FK5 ' / Default coordinate system + +CCDNAME = 'SITe #7061FBR03-02 (NOAO 02)' / CCD name +AMPNAME = 'SITe #7061FBR03-02 (NOAO 02), upper left (Amp321)' / Amplifier name +GAIN = 2.3 / gain for amp 321 (e-/ADU) +RDNOISE = 9.5 / read noise for amp 321 (e-) +SATURATE= 22000 / Maximum good data value (ADU) +CONHWV = 'ACEB003_AMP21' / Controller hardware version +ARCONG = 2.3 / gain expected for amp 321 (e-/ADU) +ARCONRN = 5.1 / read noise expected for amp 321 (e-) +BPM = 'mscdb$noao/CCDMosaThin1/bpm_im5' / Bad pixel mask +CCDSIZE = '[1:2048,1:4096]' / CCD size +CCDSUM = '1 1 ' / CCD pixel summing +CCDSEC = '[1:2048,1:4096]' / CCD section +AMPSEC = '[1:2048,4096:1]' / Amplifier section +DETSEC = '[1:2048,4097:8192]' / Detector section + +ATM1_1 = 1. / CCD to amplifier transformation +ATM2_2 = -1. / CCD to amplifier transformation +ATV1 = 0. / CCD to amplifier transformation +ATV2 = 4097. / CCD to amplifier transformation +LTM1_1 = 1.0 / CCD to image transformation +LTM2_2 = 1.0 / CCD to image transformation +DTM1_1 = 1. / CCD to detector transformation +DTM2_2 = 1. / CCD to detector transformation +DTV1 = 0. / CCD to detector transformation +DTV2 = 4096. / CCD to detector transformation + +WCSASTRM= 'kp09m.19980909T031418 (Tr 37 V Mosaic) by L. Davis 1998-09-09' / WCS +WCSDIM = 2 / WCS dimensionality +CTYPE1 = 'RA---TNX' / Coordinate type +CTYPE2 = 'DEC--TNX' / Coordinate type +CRVAL1 = 110.52095558563 / Coordinate reference value +CRVAL2 = 55.842849490386 / Coordinate reference value +CRPIX1 = 4189.77390083542 / Coordinate reference pixel +CRPIX2 = -30.0894072403876 / Coordinate reference pixel +CD1_1 = -7.5733524638274E-7 / Coordinate matrix +CD2_1 = -1.1748235187739E-4 / Coordinate matrix +CD1_2 = 1.17677003379415E-4 / Coordinate matrix +CD2_2 = -1.2623022560872E-6 / Coordinate matrix +WAT0_001= 'system=image' / Coordinate system +WAT1_001= 'wtype=tnx axtype=ra lngcor = "3. 4. 4. 2. 0.005280549145776419 0.488' +WAT1_002= '7181432338267 0.2464122630921897 0.4920677651238937 -0.23529018838' +WAT1_003= '0298 0.004720134828570101 -0.001121960789613695 -0.01414958280730338' +WAT1_004= ' 0.01246754057675834 1.048124909467771E-4 0.007168575568475281 -0.02' +WAT1_005= '778217836490202 -0.01675668830707639 0.02500648596542957 "' +WAT2_001= 'wtype=tnx axtype=dec latcor = "3. 4. 4. 2. 0.005280549145776419 0.48' +WAT2_002= '87181432338267 0.2464122630921897 0.4920677651238937 0.1531926820' +WAT2_003= '595278 -0.002539369349529043 6.454692689700324E-4 4.863689582871599E-' +WAT2_004= '4 0.003549715563706861 -0.002061136945766966 -0.01644646641299351 0.' +WAT2_005= '009061645978984397 0.005570842598401512 -0.02240395721775152 "' +XTALKCOR= '03-Oct-2000 21:33:16 No crosstalk correction' +TRIM = 'Oct 3 21:34 Trim is [1:2048,1:4096]' +FIXPIX = 'Oct 3 21:35 Pixel mask is mscdb$noao/CCDMosaThin1/bpm_im5' +OVERSCAN= 'Oct 3 21:34 Overscan is [2063:2112,1:4096], mean 993.5216' +ZEROCOR = 'Oct 3 21:35 Zero is Zeromed[im5]' +FLATCOR = 'Oct 3 21:35 Flat is FlatR.fits[im5], scale 5081.475' +CCDMEAN = 255.7904 +CCDMEANT= 655076137 +CCDPROC = 'Oct 3 21:35 CCD processing done' +CHIPNUM = 5 +FZEROPT = 22.040344 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +END diff --git a/ast_tester/tnx.ps b/ast_tester/tnx.ps new file mode 100644 index 0000000..58e455b --- /dev/null +++ b/ast_tester/tnx.ps @@ -0,0 +1,688 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Feb 11 17:19:55 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: tnx.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 11/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 492.7 473.7 452.5 431.2 410.0 388.7 367.5 346.2 324.9 303.5 297.2 ][ 82.8 82.7 82.6 82.5 82.5 82.4 82.4 + 82.3 82.2 82.2 82.1 ]plong + s[ 492.7 473.2 452.0 430.7 409.5 388.2 367.0 345.7 324.4 303.0 297.2 ][ 138.9 138.8 138.7 138.6 138.5 138.4 138.4 + 138.3 138.2 138.1 138.1 ]plong + s[ 492.7 472.5 451.3 430.1 408.9 387.6 366.3 345.0 323.7 302.4 297.2 ][ 195.0 194.9 194.8 194.7 194.6 194.5 194.4 + 194.3 194.2 194.0 194.0 ]plong + s[ 492.7 471.8 450.5 429.3 408.1 386.8 365.5 344.2 322.9 301.6 297.2 ][ 251.2 251.1 250.9 250.8 250.6 250.5 250.4 + 250.3 250.1 250.0 250.0 ]plong + s[ 492.7 492.1 470.9 449.6 428.4 407.1 385.9 364.6 343.3 322.0 300.6 297.2 ][ 307.5 307.5 307.3 307.1 306.9 306.8 + 306.6 306.5 306.3 306.2 306.0 306.0 ]plong + s[ 492.7 491.1 469.8 448.6 427.3 406.1 384.8 363.5 342.2 320.8 299.5 297.2 ][ 363.8 363.8 363.6 363.4 363.2 363.0 + 362.8 362.7 362.5 362.3 362.1 362.1 ]plong + s[ 492.7 489.9 468.7 447.4 426.2 404.9 383.6 362.3 340.9 319.6 298.2 297.2 ][ 420.3 420.3 420.0 419.8 419.6 419.3 + 419.1 418.9 418.7 418.5 418.3 418.3 ]plong + s[ 477.6 477.5 477.1 476.6 476.0 475.4 474.7 473.9 473.0 472.0 471.5 ][ 76.0 90.7 134.8 178.9 223.0 267.2 311.3 + 355.6 399.9 444.3 467.0 ]plong + s[ 450.6 450.5 450.1 449.6 449.0 448.4 447.6 446.8 445.9 445.0 444.4 ][ 76.0 90.6 134.7 178.7 222.8 266.9 311.1 + 355.3 399.6 444.0 467.0 ]plong + s[ 423.6 423.5 423.1 422.6 422.0 421.3 420.6 419.8 418.9 417.9 417.3 ][ 76.0 90.5 134.6 178.6 222.7 266.8 310.9 + 355.1 399.3 443.7 467.0 ]plong + s[ 396.5 396.4 396.0 395.5 395.0 394.3 393.5 392.7 391.8 390.8 390.2 ][ 76.0 90.5 134.5 178.5 222.5 266.6 310.7 + 354.9 399.1 443.4 467.0 ]plong + s[ 369.4 369.3 368.9 368.5 367.9 367.2 366.5 365.6 364.7 363.6 363.0 ][ 76.0 90.4 134.4 178.4 222.4 266.4 310.5 + 354.6 398.8 443.1 467.0 ]plong + s[ 342.3 342.2 341.9 341.4 340.8 340.1 339.3 338.5 337.5 336.5 335.8 ][ 76.0 90.3 134.3 178.2 222.2 266.3 310.3 + 354.4 398.6 442.8 467.0 ]plong + s[ 315.2 315.1 314.7 314.2 313.6 313.0 312.2 311.3 310.3 309.3 308.6 ][ 76.0 90.2 134.2 178.1 222.1 266.1 310.1 + 354.2 398.4 442.6 467.0 ]plong + 298.6 91.5 297.2 91.5 2 pls + 298.6 100.8 297.2 100.8 2 pls + 298.6 110.1 297.2 110.1 2 pls + 298.6 119.4 297.2 119.4 2 pls + 298.6 128.8 297.2 128.8 2 pls + 298.6 147.4 297.2 147.4 2 pls + 298.6 156.7 297.2 156.7 2 pls + 298.6 166.1 297.2 166.0 2 pls + 298.6 175.4 297.2 175.4 2 pls + 298.6 184.7 297.2 184.7 2 pls + 298.6 203.3 297.2 203.3 2 pls + 298.6 212.7 297.2 212.7 2 pls + 298.6 222.0 297.2 222.0 2 pls + 298.6 231.3 297.2 231.3 2 pls + 298.6 240.7 297.2 240.7 2 pls + 298.6 259.3 297.2 259.3 2 pls + 298.6 268.7 297.2 268.7 2 pls + 298.6 278.0 297.2 278.0 2 pls + 298.6 287.3 297.2 287.3 2 pls + 298.6 296.7 297.2 296.7 2 pls + 298.6 315.4 297.2 315.4 2 pls + 298.6 324.7 297.2 324.7 2 pls + 298.6 334.1 297.2 334.1 2 pls + 298.6 343.4 297.2 343.4 2 pls + 298.6 352.8 297.2 352.8 2 pls + 298.6 371.5 297.2 371.5 2 pls + 298.6 380.9 297.2 380.9 2 pls + 298.6 390.2 297.2 390.2 2 pls + 298.6 399.6 297.2 399.6 2 pls + 298.6 409.0 297.2 409.0 2 pls + 298.6 427.7 297.2 427.7 2 pls + 298.6 437.1 297.2 437.1 2 pls + 298.6 446.5 297.2 446.5 2 pls + 298.6 455.9 297.2 455.9 2 pls + 298.6 465.3 297.2 465.3 2 pls + 491.1 77.4 491.1 76.0 2 pls + 484.3 77.4 484.3 76.0 2 pls + 470.8 77.4 470.8 76.0 2 pls + 464.1 77.4 464.1 76.0 2 pls + 457.3 77.4 457.3 76.0 2 pls + 443.8 77.4 443.8 76.0 2 pls + 437.1 77.4 437.1 76.0 2 pls + 430.3 77.4 430.3 76.0 2 pls + 416.8 77.4 416.8 76.0 2 pls + 410.0 77.4 410.0 76.0 2 pls + 403.3 77.4 403.3 76.0 2 pls + 389.7 77.4 389.8 76.0 2 pls + 383.0 77.4 383.0 76.0 2 pls + 376.2 77.4 376.2 76.0 2 pls + 362.7 77.4 362.7 76.0 2 pls + 355.9 77.4 355.9 76.0 2 pls + 349.1 77.4 349.1 76.0 2 pls + 335.6 77.4 335.6 76.0 2 pls + 328.8 77.4 328.8 76.0 2 pls + 322.0 77.4 322.0 76.0 2 pls + 308.4 77.4 308.4 76.0 2 pls + 301.6 77.4 301.6 76.0 2 pls + 491.3 92.1 492.7 92.1 2 pls + 491.3 101.5 492.7 101.5 2 pls + 491.3 110.8 492.7 110.8 2 pls + 491.3 120.2 492.7 120.2 2 pls + 491.3 129.5 492.7 129.5 2 pls + 491.3 148.2 492.7 148.2 2 pls + 491.3 157.6 492.7 157.6 2 pls + 491.3 166.9 492.7 166.9 2 pls + 491.3 176.3 492.7 176.3 2 pls + 491.3 185.7 492.7 185.7 2 pls + 491.3 204.4 492.7 204.4 2 pls + 491.3 213.7 492.7 213.8 2 pls + 491.3 223.1 492.7 223.1 2 pls + 491.3 232.5 492.7 232.5 2 pls + 491.3 241.8 492.7 241.9 2 pls + 491.3 260.6 492.7 260.6 2 pls + 491.3 270.0 492.7 270.0 2 pls + 491.3 279.3 492.7 279.3 2 pls + 491.3 288.7 492.7 288.7 2 pls + 491.3 298.1 492.7 298.1 2 pls + 491.3 316.9 492.7 316.9 2 pls + 491.3 326.2 492.7 326.3 2 pls + 491.3 335.6 492.7 335.7 2 pls + 491.3 345.0 492.7 345.0 2 pls + 491.3 354.4 492.7 354.4 2 pls + 491.3 373.2 492.7 373.3 2 pls + 491.3 382.6 492.7 382.7 2 pls + 491.3 392.1 492.7 392.1 2 pls + 491.3 401.5 492.7 401.5 2 pls + 491.3 410.9 492.7 410.9 2 pls + 491.3 429.7 492.7 429.8 2 pls + 491.3 439.2 492.7 439.2 2 pls + 491.3 448.6 492.7 448.6 2 pls + 491.3 458.0 492.7 458.1 2 pls + 491.8 465.6 491.8 467.0 2 pls + 485.1 465.6 485.0 467.0 2 pls + 478.3 465.6 478.3 467.0 2 pls + 464.8 465.6 464.7 467.0 2 pls + 458.0 465.6 458.0 467.0 2 pls + 451.2 465.6 451.2 467.0 2 pls + 437.7 465.6 437.6 467.0 2 pls + 430.9 465.6 430.9 467.0 2 pls + 424.1 465.6 424.1 467.0 2 pls + 410.6 465.6 410.5 467.0 2 pls + 403.8 465.6 403.7 467.0 2 pls + 397.0 465.6 397.0 467.0 2 pls + 383.4 465.6 383.4 467.0 2 pls + 376.6 465.6 376.6 467.0 2 pls + 369.9 465.6 369.8 467.0 2 pls + 356.3 465.6 356.2 467.0 2 pls + 349.5 465.6 349.4 467.0 2 pls + 342.7 465.6 342.6 467.0 2 pls + 329.1 465.6 329.0 467.0 2 pls + 322.3 465.6 322.2 467.0 2 pls + 315.5 465.6 315.4 467.0 2 pls + 301.9 465.6 301.8 467.0 2 pls + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 256.81 75.25 moveto[ 256.8 256.8 296.1 296.1 ][ 75.3 87.4 87.4 75.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 259.3 259.3 259.7 260.1 261.0 262.7 263.5 263.9 264.3 264.3 263.9 263.1 258.9 264.7 ][ 83.6 84.0 84.9 85.3 + 85.7 85.7 85.3 84.9 84.0 83.2 82.4 81.1 76.9 76.9 ]plong + s[ 269.8 268.5 267.7 267.2 267.2 267.7 268.5 269.8 270.6 271.8 272.7 273.1 273.1 272.7 271.8 270.6 269.8 ][ 85.7 + 85.3 84.0 81.9 80.7 78.6 77.3 76.9 76.9 77.3 78.6 80.7 81.9 84.0 85.3 85.7 85.7 ]plong + 276.4 82.8 276.9 82.4 276.4 81.9 276.0 82.4 276.4 82.8 5 pls + 276.4 77.8 276.9 77.3 276.4 76.9 276.0 77.3 276.4 77.8 5 pls + s[ 280.6 285.2 282.7 284.0 284.8 285.2 285.6 285.6 285.2 284.4 283.1 281.9 280.6 280.2 279.8 ][ 85.7 85.7 82.4 + 82.4 81.9 81.5 80.3 79.4 78.2 77.3 76.9 76.9 77.3 77.8 78.6 ]plong + s[ 290.6 289.4 288.5 288.1 288.1 288.5 289.4 290.6 291.5 292.7 293.6 294.0 294.0 293.6 292.7 291.5 290.6 ][ 85.7 + 85.3 84.0 81.9 80.7 78.6 77.3 76.9 76.9 77.3 78.6 80.7 81.9 84.0 85.3 85.7 85.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 256.81 131.18 moveto[ 256.8 256.8 296.1 296.1 ][ 131.2 143.3 143.3 131.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 259.3 259.3 259.7 260.1 261.0 262.7 263.5 263.9 264.3 264.3 263.9 263.1 258.9 264.7 ][ 139.5 140.0 140.8 141.2 + 141.6 141.6 141.2 140.8 140.0 139.1 138.3 137.0 132.9 132.9 ]plong + 270.6 132.9 270.6 141.6 269.3 140.4 268.5 140.0 4 pls + 276.4 138.7 276.9 138.3 276.4 137.9 276.0 138.3 276.4 138.7 5 pls + 276.4 133.7 276.9 133.3 276.4 132.9 276.0 133.3 276.4 133.7 5 pls + s[ 282.3 281.0 280.2 279.8 279.8 280.2 281.0 282.3 283.1 284.4 285.2 285.6 285.6 285.2 284.4 283.1 282.3 ][ 141.6 + 141.2 140.0 137.9 136.6 134.5 133.3 132.9 132.9 133.3 134.5 136.6 137.9 140.0 141.2 141.6 141.6 ]plong + s[ 290.6 289.4 288.5 288.1 288.1 288.5 289.4 290.6 291.5 292.7 293.6 294.0 294.0 293.6 292.7 291.5 290.6 ][ 141.6 + 141.2 140.0 137.9 136.6 134.5 133.3 132.9 132.9 133.3 134.5 136.6 137.9 140.0 141.2 141.6 141.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 277.69 187.12 moveto[ 277.7 277.7 296.1 296.1 ][ 187.1 199.2 199.2 187.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 280.6 285.2 282.7 284.0 284.8 285.2 285.6 285.6 285.2 284.4 283.1 281.9 280.6 280.2 279.8 ][ 197.6 197.6 194.2 + 194.2 193.8 193.4 192.1 191.3 190.0 189.2 188.8 188.8 189.2 189.6 190.5 ]plong + s[ 290.6 289.4 288.5 288.1 288.1 288.5 289.4 290.6 291.5 292.7 293.6 294.0 294.0 293.6 292.7 291.5 290.6 ][ 197.6 + 197.1 195.9 193.8 192.6 190.5 189.2 188.8 188.8 189.2 190.5 192.6 193.8 195.9 197.1 197.6 197.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 244.28 243.10 moveto[ 244.3 244.3 296.1 296.1 ][ 243.1 255.2 255.2 243.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 248.0 244.8 252.2 253.5 2 pls + 252.2 253.5 246.4 253.5 2 pls + 255.6 250.6 256.0 250.2 255.6 249.8 255.1 250.2 255.6 250.6 5 pls + 255.6 245.6 256.0 245.2 255.6 244.8 255.1 245.2 255.6 245.6 5 pls + s[ 259.3 259.3 259.7 260.1 261.0 262.7 263.5 263.9 264.3 264.3 263.9 263.1 258.9 264.7 ][ 251.5 251.9 252.7 253.1 + 253.5 253.5 253.1 252.7 251.9 251.0 250.2 248.9 244.8 244.8 ]plong + s[ 267.7 267.7 268.1 268.5 269.3 271.0 271.8 272.3 272.7 272.7 272.3 271.4 267.2 273.1 ][ 251.5 251.9 252.7 253.1 + 253.5 253.5 253.1 252.7 251.9 251.0 250.2 248.9 244.8 244.8 ]plong + 276.4 250.6 276.9 250.2 276.4 249.8 276.0 250.2 276.4 250.6 5 pls + 276.4 245.6 276.9 245.2 276.4 244.8 276.0 245.2 276.4 245.6 5 pls + s[ 282.3 281.0 280.2 279.8 279.8 280.2 281.0 282.3 283.1 284.4 285.2 285.6 285.6 285.2 284.4 283.1 282.3 ][ 253.5 + 253.1 251.9 249.8 248.5 246.4 245.2 244.8 244.8 245.2 246.4 248.5 249.8 251.9 253.1 253.5 253.5 ]plong + s[ 290.6 289.4 288.5 288.1 288.1 288.5 289.4 290.6 291.5 292.7 293.6 294.0 294.0 293.6 292.7 291.5 290.6 ][ 253.5 + 253.1 251.9 249.8 248.5 246.4 245.2 244.8 244.8 245.2 246.4 248.5 249.8 251.9 253.1 253.5 253.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 277.69 299.13 moveto[ 277.7 277.7 296.1 296.1 ][ 299.1 311.2 311.2 299.1 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 280.6 285.2 282.7 284.0 284.8 285.2 285.6 285.6 285.2 284.4 283.1 281.9 280.6 280.2 279.8 ][ 309.6 309.6 306.2 + 306.2 305.8 305.4 304.1 303.3 302.0 301.2 300.8 300.8 301.2 301.6 302.5 ]plong + s[ 290.6 289.4 288.5 288.1 288.1 288.5 289.4 290.6 291.5 292.7 293.6 294.0 294.0 293.6 292.7 291.5 290.6 ][ 309.6 + 309.1 307.9 305.8 304.6 302.5 301.2 300.8 300.8 301.2 302.5 304.6 305.8 307.9 309.1 309.6 309.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 256.81 355.23 moveto[ 256.8 256.8 296.1 296.1 ][ 355.2 367.3 367.3 355.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 259.3 259.3 259.7 260.1 261.0 262.7 263.5 263.9 264.3 264.3 263.9 263.1 258.9 264.7 ][ 363.6 364.0 364.8 365.3 + 365.7 365.7 365.3 364.8 364.0 363.2 362.3 361.1 356.9 356.9 ]plong + s[ 268.1 272.7 270.2 271.4 272.3 272.7 273.1 273.1 272.7 271.8 270.6 269.3 268.1 267.7 267.2 ][ 365.7 365.7 362.3 + 362.3 361.9 361.5 360.2 359.4 358.2 357.3 356.9 356.9 357.3 357.7 358.6 ]plong + 276.4 362.8 276.9 362.3 276.4 361.9 276.0 362.3 276.4 362.8 5 pls + 276.4 357.7 276.9 357.3 276.4 356.9 276.0 357.3 276.4 357.7 5 pls + s[ 282.3 281.0 280.2 279.8 279.8 280.2 281.0 282.3 283.1 284.4 285.2 285.6 285.6 285.2 284.4 283.1 282.3 ][ 365.7 + 365.3 364.0 361.9 360.7 358.6 357.3 356.9 356.9 357.3 358.6 360.7 361.9 364.0 365.3 365.7 365.7 ]plong + s[ 290.6 289.4 288.5 288.1 288.1 288.5 289.4 290.6 291.5 292.7 293.6 294.0 294.0 293.6 292.7 291.5 290.6 ][ 365.7 + 365.3 364.0 361.9 360.7 358.6 357.3 356.9 356.9 357.3 358.6 360.7 361.9 364.0 365.3 365.7 365.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 277.69 411.45 moveto[ 277.7 277.7 296.1 296.1 ][ 411.4 423.6 423.6 411.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 280.6 285.2 282.7 284.0 284.8 285.2 285.6 285.6 285.2 284.4 283.1 281.9 280.6 280.2 279.8 ][ 421.9 421.9 418.5 + 418.5 418.1 417.7 416.5 415.6 414.4 413.5 413.1 413.1 413.5 414.0 414.8 ]plong + s[ 290.6 289.4 288.5 288.1 288.1 288.5 289.4 290.6 291.5 292.7 293.6 294.0 294.0 293.6 292.7 291.5 290.6 ][ 421.9 + 421.5 420.2 418.1 416.9 414.8 413.5 413.1 413.1 413.5 414.8 416.9 418.1 420.2 421.5 421.9 421.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 306.02 61.95 moveto[ 306.0 306.0 324.4 324.4 ][ 61.9 74.1 74.1 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 308.5 308.5 308.9 309.4 310.2 311.9 312.7 313.1 313.5 313.5 313.1 312.3 308.1 314.0 ][ 70.3 70.7 71.6 72.0 + 72.4 72.4 72.0 71.6 70.7 69.9 69.0 67.8 63.6 63.6 ]plong + s[ 318.5 317.3 316.9 316.9 317.3 318.1 319.8 321.1 321.9 322.3 322.3 321.9 321.5 320.2 318.5 317.3 316.9 316.5 + 316.5 316.9 317.7 319.0 320.6 321.5 321.9 321.9 321.5 320.2 318.5 ][ 72.4 72.0 71.1 70.3 69.5 69.0 68.6 + 68.2 67.4 66.5 65.3 64.5 64.0 63.6 63.6 64.0 64.5 65.3 66.5 67.4 68.2 68.6 69.0 69.5 70.3 + 71.1 72.0 72.4 72.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.26 61.95 moveto[ 360.3 360.3 379.1 379.1 ][ 61.9 74.1 74.1 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 362.8 362.8 363.2 363.6 364.4 366.1 366.9 367.4 367.8 367.8 367.4 366.5 362.3 368.2 ][ 70.3 70.7 71.6 72.0 + 72.4 72.4 72.0 71.6 70.7 69.9 69.0 67.8 63.6 63.6 ]plong + 377.0 66.5 370.7 66.5 374.9 72.4 3 pls + 374.9 63.6 374.9 72.4 2 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 403.92 61.95 moveto[ 403.9 403.9 443.2 443.2 ][ 61.9 74.1 74.1 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 411.0 406.8 406.4 406.8 408.1 409.4 410.6 411.4 411.9 411.9 411.4 410.6 409.4 408.1 406.8 406.4 406.0 ][ 72.4 + 72.4 68.6 69.0 69.5 69.5 69.0 68.2 67.0 66.1 64.9 64.0 63.6 63.6 64.0 64.5 65.3 ]plong + s[ 419.8 419.4 418.1 417.3 416.0 415.2 414.8 414.8 415.2 416.0 417.3 417.7 419.0 419.8 420.2 420.2 419.8 419.0 + 417.7 417.3 416.0 415.2 414.8 ][ 71.1 72.0 72.4 72.4 72.0 70.7 68.6 66.5 64.9 64.0 63.6 63.6 64.0 + 64.9 66.1 66.5 67.8 68.6 69.0 69.0 68.6 67.8 66.5 ]plong + 423.6 69.5 424.0 69.0 423.6 68.6 423.1 69.0 423.6 69.5 5 pls + 423.6 64.5 424.0 64.0 423.6 63.6 423.1 64.0 423.6 64.5 5 pls + s[ 427.3 427.3 427.7 428.1 429.0 430.7 431.5 431.9 432.3 432.3 431.9 431.1 426.9 432.7 ][ 70.3 70.7 71.6 72.0 + 72.4 72.4 72.0 71.6 70.7 69.9 69.0 67.8 63.6 63.6 ]plong + s[ 437.8 436.5 435.7 435.3 435.3 435.7 436.5 437.8 438.6 439.8 440.7 441.1 441.1 440.7 439.8 438.6 437.8 ][ 72.4 + 72.0 70.7 68.6 67.4 65.3 64.0 63.6 63.6 64.0 65.3 67.4 68.6 70.7 72.0 72.4 72.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 469.63 61.95 moveto[ 469.6 469.6 486.8 486.8 ][ 61.9 74.1 74.1 61.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 473.8 63.6 473.8 72.4 472.6 71.1 471.7 70.7 4 pls + s[ 484.2 483.8 482.6 481.7 480.5 479.7 479.2 479.2 479.7 480.5 481.7 482.2 483.4 484.2 484.7 484.7 484.2 483.4 + 482.2 481.7 480.5 479.7 479.2 ][ 71.1 72.0 72.4 72.4 72.0 70.7 68.6 66.5 64.9 64.0 63.6 63.6 64.0 + 64.9 66.1 66.5 67.8 68.6 69.0 69.0 68.6 67.8 66.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 223.48 219.05 moveto[ 223.5 208.0 208.0 223.5 ][ 219.1 219.1 323.9 323.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 218.9 221.1 210.1 221.1 2 pls + s[ 210.1 210.1 210.5 210.9 211.8 212.6 213.5 213.9 214.3 214.3 ][ 221.1 224.9 226.2 226.6 227.0 227.0 226.6 226.2 + 224.9 221.1 ]plong + 218.9 227.0 214.3 224.1 2 pls + 210.1 229.5 209.7 229.9 210.1 230.3 210.5 229.9 210.1 229.5 5 pls + 218.9 229.9 213.0 229.9 2 pls + s[ 213.0 219.7 221.0 221.4 221.8 221.8 221.4 ][ 237.8 237.8 237.4 237.0 236.2 234.9 234.1 ]plong + s[ 214.3 213.5 213.0 213.0 213.5 214.3 215.5 216.4 217.6 218.5 218.9 218.9 218.5 217.6 ][ 237.8 237.0 236.2 234.9 + 234.1 233.3 232.8 232.8 233.3 234.1 234.9 236.2 237.0 237.8 ]plong + 218.9 241.2 210.1 241.2 2 pls + s[ 214.7 213.5 213.0 213.0 213.5 214.7 218.9 ][ 241.2 242.4 243.3 244.5 245.4 245.8 245.8 ]plong + 218.9 251.6 218.9 250.8 218.5 250.0 217.2 249.5 210.1 249.5 5 pls + 213.0 251.2 213.0 248.3 2 pls + 218.9 265.4 213.0 265.4 2 pls + s[ 214.3 213.5 213.0 213.0 213.5 214.3 215.5 216.4 217.6 218.5 218.9 218.9 218.5 217.6 ][ 265.4 264.6 263.7 262.5 + 261.7 260.8 260.4 260.4 260.8 261.7 262.5 263.7 264.6 265.4 ]plong + s[ 214.3 213.5 213.0 213.0 213.5 214.3 215.1 215.5 216.0 216.4 217.2 217.6 218.5 218.9 218.9 218.5 217.6 ][ 272.9 + 272.5 271.3 270.0 268.8 268.3 268.8 269.6 271.7 272.5 272.9 272.9 272.5 271.3 270.0 268.8 268.3 ]plong + s[ 214.3 213.5 213.0 213.0 213.5 214.3 215.5 216.4 217.6 218.5 218.9 218.9 218.5 217.6 ][ 280.5 279.6 278.8 277.5 + 276.7 275.9 275.4 275.4 275.9 276.7 277.5 278.8 279.6 280.5 ]plong + s[ 215.5 215.5 214.7 213.9 213.5 213.0 213.0 213.5 214.3 215.5 216.4 217.6 218.5 218.9 218.9 218.5 217.6 ][ 283.0 + 288.0 288.0 287.6 287.1 286.3 285.0 284.2 283.4 283.0 283.0 283.4 284.2 285.0 286.3 287.1 288.0 ]plong + 218.9 290.9 213.0 290.9 2 pls + s[ 214.7 213.5 213.0 213.0 213.5 214.7 218.9 ][ 290.9 292.1 293.0 294.2 295.1 295.5 295.5 ]plong + s[ 214.3 213.5 213.0 213.0 213.5 214.3 215.1 215.5 216.0 216.4 217.2 217.6 218.5 218.9 218.9 218.5 217.6 ][ 303.0 + 302.6 301.3 300.1 298.8 298.4 298.8 299.7 301.8 302.6 303.0 303.0 302.6 301.3 300.1 298.8 298.4 ]plong + 210.1 305.5 209.7 305.9 210.1 306.3 210.5 305.9 210.1 305.5 5 pls + 218.9 305.9 213.0 305.9 2 pls + s[ 213.0 213.5 214.3 215.5 216.4 217.6 218.5 218.9 218.9 218.5 217.6 216.4 215.5 214.3 213.5 213.0 213.0 ][ 310.9 + 310.1 309.3 308.9 308.9 309.3 310.1 310.9 312.2 313.0 313.9 314.3 314.3 313.9 313.0 312.2 310.9 ]plong + 218.9 317.2 213.0 317.2 2 pls + s[ 214.7 213.5 213.0 213.0 213.5 214.7 218.9 ][ 317.2 318.5 319.3 320.5 321.4 321.8 321.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 359.21 29.87 moveto[ 359.2 359.2 430.6 430.6 ][ 29.9 42.4 42.4 29.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 361.3 31.5 361.3 40.3 2 pls + s[ 361.3 364.2 365.5 366.3 366.7 367.1 367.1 366.7 366.3 365.5 364.2 361.3 ][ 40.3 40.3 39.9 39.1 38.2 37.0 + 34.9 33.6 32.8 32.0 31.5 31.5 ]plong + s[ 369.7 374.7 374.7 374.2 373.8 373.0 371.7 370.9 370.1 369.7 369.7 370.1 370.9 371.7 373.0 373.8 374.7 ][ 34.9 + 34.9 35.7 36.6 37.0 37.4 37.4 37.0 36.1 34.9 34.0 32.8 32.0 31.5 31.5 32.0 32.8 ]plong + s[ 382.2 381.3 380.5 379.3 378.4 377.6 377.2 377.2 377.6 378.4 379.3 380.5 381.3 382.2 ][ 36.1 37.0 37.4 37.4 + 37.0 36.1 34.9 34.0 32.8 32.0 31.5 31.5 32.0 32.8 ]plong + 385.1 31.5 385.1 40.3 2 pls + 388.0 40.3 388.4 40.7 388.9 40.3 388.4 39.9 388.0 40.3 5 pls + 388.4 31.5 388.4 37.4 2 pls + 391.8 31.5 391.8 37.4 2 pls + s[ 391.8 393.0 393.9 395.1 396.0 396.4 396.4 ][ 35.7 37.0 37.4 37.4 37.0 35.7 31.5 ]plong + 404.3 31.5 404.3 37.4 2 pls + s[ 404.3 403.5 402.6 401.4 400.6 399.7 399.3 399.3 399.7 400.6 401.4 402.6 403.5 404.3 ][ 36.1 37.0 37.4 37.4 + 37.0 36.1 34.9 34.0 32.8 32.0 31.5 31.5 32.0 32.8 ]plong + 410.2 31.5 409.3 31.5 408.5 32.0 408.1 33.2 408.1 40.3 5 pls + 409.8 37.4 406.8 37.4 2 pls + 412.3 40.3 412.7 40.7 413.1 40.3 412.7 39.9 412.3 40.3 5 pls + 412.7 31.5 412.7 37.4 2 pls + s[ 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 421.0 421.0 420.6 419.8 418.9 417.7 ][ 37.4 + 37.0 36.1 34.9 34.0 32.8 32.0 31.5 31.5 32.0 32.8 34.0 34.9 36.1 37.0 37.4 37.4 ]plong + 424.0 31.5 424.0 37.4 2 pls + s[ 424.0 425.2 426.0 427.3 428.1 428.5 428.5 ][ 35.7 37.0 37.4 37.4 37.0 35.7 31.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 476.73 moveto[ 360.9 360.9 435.2 435.2 ][ 476.7 488.8 488.8 476.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 478.4 366.3 487.2 2 pls + 369.7 478.4 366.3 487.2 2 pls + 368.4 481.3 364.2 481.3 2 pls + 378.4 478.4 378.4 487.2 2 pls + 383.9 487.2 378.4 487.2 2 pls + 381.8 483.0 378.4 483.0 2 pls + 385.9 478.4 385.9 487.2 2 pls + 391.0 478.4 391.0 487.2 2 pls + 393.9 487.2 388.0 487.2 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 485.9 486.8 487.2 487.2 486.8 485.9 485.1 484.3 483.8 483.4 482.6 482.2 481.7 480.9 479.7 478.8 + 478.4 478.4 478.8 479.7 ]plong + 413.5 478.4 412.7 478.4 411.8 478.8 411.4 480.1 411.4 487.2 5 pls + 413.1 484.3 410.2 484.3 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 481.7 + 481.7 482.6 483.4 483.8 484.3 484.3 483.8 483.0 481.7 480.9 479.7 478.8 478.4 478.4 478.8 479.7 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 483.0 + 483.8 484.3 484.3 483.8 483.0 482.2 481.7 481.3 480.9 480.1 479.7 478.8 478.4 478.4 478.8 479.7 ]plong + 433.1 478.4 432.3 478.4 431.5 478.8 431.1 480.1 431.1 487.2 5 pls + 432.7 484.3 429.8 484.3 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/tsc.attr b/ast_tester/tsc.attr new file mode 100644 index 0000000..496403c --- /dev/null +++ b/ast_tester/tsc.attr @@ -0,0 +1 @@ +border=1 diff --git a/ast_tester/tsc.box b/ast_tester/tsc.box new file mode 100644 index 0000000..45cc65e --- /dev/null +++ b/ast_tester/tsc.box @@ -0,0 +1 @@ +1 1 1600 1600 diff --git a/ast_tester/tsc.head b/ast_tester/tsc.head new file mode 100644 index 0000000..e41aefb --- /dev/null +++ b/ast_tester/tsc.head @@ -0,0 +1,116 @@ +SIMPLE = T +BITPIX = -32 / IEEE (big-endian) 32-bit floating point data +NAXIS = 2 +NAXIS1 = 1600 +NAXIS2 = 1600 +BUNIT = 'JY/BEAM ' +CTYPE1 = 'RA---TSC' +CRPIX1 = 1000 +CDELT1 = -3.666666666667E-01 +CRVAL1 = 0.000000000000E+00 +CTYPE2 = 'DEC--TSC' +CRPIX2 = 800 +CDELT2 = 3.666666666667E-01 +CRVAL2 = -9.000000000000E+01 +LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole +LATPOLE = 0.000000000000E+00 / Native latitude of celestial pole +EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates +BMAJ = 2.399999936422E-01 / Beam major axis in degrees +BMIN = 2.399999936422E-01 / Beam minor axis in degrees +BPA = 0.000000000000E+00 / Beam position angle in degrees +RESTFREQ= 1.420405750000E+09 / Line rest frequency, Hz +HISTORY Parkes Multibeam continuum map +HISTORY Formed on Mon 2004/02/09 02:23:02 GMT by "pksgridzilla" which was +HISTORY compiled on Feb 9 2004 12:08:02 (local time) within +HISTORY AIPS++ version 19.405.00 dated . +HISTORY Polarization mode: A and B aggregated +HISTORY Gridding parameters: +HISTORY Method: WGTMED +HISTORY Clip fraction: 0.000 +HISTORY Tsys weighting: applied +HISTORY Beam weight order: 1 +HISTORY Beam FWHM: 14.4 arcmin +HISTORY Beam normalization: applied +HISTORY Smoothing kernel type: TOP-HAT +HISTORY Kernel FWHM: 12.0 arcmin +HISTORY Cutoff radius: 6.0 arcmin +HISTORY Beam RSS cutoff: 0.0 +HISTORY Input data sets: +HISTORY 97-10-09_0356_193558-66_206a.sdfits +HISTORY 97-10-12_0142_182123-66_193a.sdfits +HISTORY 97-10-12_0151_182707-66_194a.sdfits +HISTORY 97-10-12_0200_183252-66_195a.sdfits +HISTORY 97-11-07_0510_183836-66_196a.sdfits +HISTORY 97-11-07_0519_184420-66_197a.sdfits +HISTORY 97-11-07_0528_185004-66_198a.sdfits +HISTORY 97-11-07_0537_185548-66_199a.sdfits +HISTORY 97-11-07_0546_190132-66_200a.sdfits +HISTORY 97-11-07_0556_190717-66_201a.sdfits +HISTORY 97-11-07_0645_191301-66_202a.sdfits +HISTORY 97-11-07_0654_191845-66_203a.sdfits +HISTORY 97-11-07_0703_192429-66_204a.sdfits +HISTORY 97-11-07_0712_193013-66_205a.sdfits +HISTORY 97-11-07_0724_194142-66_207a.sdfits +HISTORY 97-11-18_0256_193815-66_206c.sdfits +HISTORY 97-11-18_0306_194359-66_207c.sdfits +HISTORY 97-11-19_0447_182341-66_193c.sdfits +HISTORY 97-11-19_0456_182925-66_194c.sdfits +HISTORY 97-11-19_0507_190350-66_200c.sdfits +HISTORY 97-11-19_0516_190934-66_201c.sdfits +HISTORY 97-11-19_0525_191519-66_202c.sdfits +HISTORY 97-11-19_0534_192103-66_203c.sdfits +HISTORY 97-11-19_0544_192647-66_204c.sdfits +HISTORY 97-11-19_0553_193231-66_205c.sdfits +HISTORY 97-11-19_0602_183509-66_195c.sdfits +HISTORY 97-11-19_0612_184053-66_196c.sdfits +HISTORY 97-11-19_0622_184638-66_197c.sdfits +HISTORY 97-11-19_0631_185222-66_198c.sdfits +HISTORY 97-11-19_0640_185806-66_199c.sdfits +HISTORY 98-03-24_2107_193706-66_206b.sdfits +HISTORY 98-03-24_2116_194251-66_207b.sdfits +HISTORY 98-03-25_2020_190826-66_201b.sdfits +HISTORY 98-03-25_2029_191410-66_202b.sdfits +HISTORY 98-03-25_2038_191954-66_203b.sdfits +HISTORY 98-03-25_2047_192538-66_204b.sdfits +HISTORY 98-03-25_2056_193122-66_205b.sdfits +HISTORY 98-03-26_2048_190459-66_200d.sdfits +HISTORY 98-03-27_2034_191627-66_202d.sdfits +HISTORY 98-03-27_2043_192212-66_203d.sdfits +HISTORY 98-03-27_2052_192756-66_204d.sdfits +HISTORY 98-03-27_2102_193340-66_205d.sdfits +HISTORY 98-03-27_2111_193924-66_206d.sdfits +HISTORY 98-03-27_2120_194508-66_207d.sdfits +HISTORY 98-03-27_2130_191043-66_201d.sdfits +HISTORY 98-05-10_2123_182232-66_193b.sdfits +HISTORY 98-05-10_2133_182816-66_194b.sdfits +HISTORY 98-05-10_2142_183400-66_195b.sdfits +HISTORY 98-05-10_2151_183945-66_196b.sdfits +HISTORY 98-05-10_2200_184529-66_197b.sdfits +HISTORY 98-05-10_2209_185113-66_198b.sdfits +HISTORY 98-05-10_2219_185657-66_199b.sdfits +HISTORY 98-05-10_2228_190241-66_200b.sdfits +HISTORY 98-05-13_2132_182450-66_193d.sdfits +HISTORY 98-05-13_2151_183034-66_194d.sdfits +HISTORY 98-05-13_2200_183618-66_195d.sdfits +HISTORY 98-05-13_2210_184202-66_196d.sdfits +HISTORY 98-05-13_2219_184746-66_197d.sdfits +HISTORY 98-05-13_2228_185331-66_198d.sdfits +HISTORY 98-05-13_2237_185915-66_199d.sdfits +HISTORY 98-05-25_1711_182559-66_193e.sdfits +HISTORY 98-05-25_1720_183143-66_194e.sdfits +HISTORY 98-05-25_1729_183727-66_195e.sdfits +HISTORY 98-05-25_1738_184311-66_196e.sdfits +HISTORY 98-05-25_1747_184855-66_197e.sdfits +HISTORY 98-05-25_1756_185439-66_198e.sdfits +HISTORY 98-05-25_1806_190024-66_199e.sdfits +HISTORY 98-05-25_1815_190608-66_200e.sdfits +HISTORY 98-05-25_1824_191152-66_201e.sdfits +HISTORY 98-05-25_1833_191736-66_202e.sdfits +HISTORY 98-05-25_1842_192320-66_203e.sdfits +HISTORY 98-05-25_1851_192905-66_204e.sdfits +HISTORY 98-05-25_1901_193449-66_205e.sdfits +HISTORY 98-05-25_1910_194033-66_206e.sdfits +HISTORY 98-05-25_1919_194617-66_207e.sdfits +HISTORY Original FITS filename "1904-66_TSC.continuum.fits". +HISTORY Noise level of continuum map: 61 mJy (RMS) +END diff --git a/ast_tester/tsc.ps b/ast_tester/tsc.ps new file mode 100644 index 0000000..9a3befa --- /dev/null +++ b/ast_tester/tsc.ps @@ -0,0 +1,835 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Thu Feb 19 19:50:49 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 19/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 443.7 448.5 453.9 460.6 461.2 461.8 462.4 463.0 463.6 464.2 464.9 465.6 466.3 467.1 467.8 468.6 469.4 470.3 + 471.2 472.1 ][ 271.4 266.5 261.1 254.4 253.9 253.3 252.7 252.1 251.5 250.8 250.1 249.5 248.7 248.0 247.2 246.4 + 245.6 244.8 243.9 242.9 ]plong + s[ 235.1 236.0 237.0 237.9 238.9 239.8 240.7 241.5 242.4 243.2 244.1 244.9 245.7 246.5 247.3 248.0 248.8 249.6 + 250.3 251.1 251.8 252.5 253.3 254.0 263.7 273.3 274.1 274.8 275.5 276.3 277.0 280.8 281.6 282.4 ][ 241.4 241.4 + 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 + 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 ]plong + s[ 292.3 293.3 294.3 295.2 296.2 297.1 297.9 298.8 299.5 300.3 301.1 301.8 302.5 303.1 303.8 304.4 305.0 305.6 + 306.2 306.8 313.5 318.8 323.7 ][ 241.4 241.4 242.0 242.9 243.9 244.8 245.6 246.4 247.2 248.0 248.7 249.5 250.1 + 250.8 251.5 252.1 252.7 253.3 253.9 254.4 261.1 266.5 271.4 ]plong + s[ 443.7 450.5 458.1 458.7 459.4 460.0 460.6 461.2 461.9 462.6 463.2 463.9 464.6 465.4 466.1 466.9 467.6 468.4 + 469.2 470.1 470.9 471.8 ][ 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 + 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 ]plong + s[ 234.6 235.5 236.4 237.3 238.1 238.9 239.7 240.5 241.3 242.0 242.7 243.4 244.1 244.8 245.5 246.1 246.8 247.4 + 248.0 248.6 249.2 256.8 263.7 270.5 278.1 287.6 299.7 309.2 316.8 323.7 ][ 271.4 271.4 271.4 271.4 271.4 271.4 + 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 + 271.4 271.4 271.4 271.4 271.4 271.4 ]plong + s[ 443.7 448.5 453.9 460.6 461.2 461.8 462.4 463.0 463.6 464.2 464.9 465.6 466.3 467.1 467.8 468.6 469.4 470.3 + 471.2 472.1 ][ 271.4 276.2 281.6 288.3 288.8 289.4 290.0 290.6 291.3 291.9 292.6 293.3 294.0 294.7 295.5 296.3 + 297.1 298.0 298.9 299.8 ]plong + 473.7 310.9 473.7 310.1 473.7 305.7 473.7 304.7 473.7 303.8 5 pls + 251.1 301.4 250.3 301.4 249.6 301.4 3 pls + 473.7 328.0 473.7 327.3 473.7 326.6 473.7 325.9 4 pls + 473.7 346.2 473.7 345.5 473.7 344.7 3 pls + s[ 295.2 296.2 297.1 297.9 298.8 299.5 300.3 301.1 301.8 302.5 303.1 303.8 304.4 305.0 305.6 306.2 306.8 313.5 + 318.8 323.7 ][ 299.8 298.9 298.0 297.1 296.3 295.5 294.7 294.0 293.3 292.6 291.9 291.3 290.6 290.0 289.4 288.8 + 288.3 281.6 276.2 271.4 ]plong + s[ 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 + 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 ][ 271.4 278.2 285.8 295.3 307.4 316.9 + 324.5 331.4 338.2 345.8 346.4 347.0 347.6 348.3 348.9 349.6 350.2 350.9 351.6 352.3 353.0 353.8 354.5 355.3 + 356.1 356.9 357.8 358.6 359.5 360.4 ]plong + s[ 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 + 323.7 323.7 323.7 323.7 ][ 299.5 298.6 297.8 296.9 296.1 295.3 294.5 293.8 293.0 292.3 291.6 290.9 290.2 289.6 + 288.9 288.3 287.6 287.0 286.4 285.8 278.2 271.4 ]plong + s[ 443.7 438.9 433.5 426.8 426.2 425.6 425.0 424.4 423.8 423.2 422.5 421.8 421.1 420.3 419.6 418.8 418.0 417.1 + 416.2 415.3 414.3 414.2 414.2 414.1 414.0 413.9 413.9 413.8 413.7 413.7 412.4 412.3 413.7 413.7 413.7 ][ 271.4 + 276.2 281.6 288.3 288.8 289.4 290.0 290.6 291.3 291.9 292.6 293.3 294.0 294.7 295.5 296.3 297.1 298.0 298.9 + 299.8 300.8 300.8 300.9 301.0 301.1 301.1 301.2 301.3 301.3 301.4 301.4 301.4 303.8 304.7 305.7 ]plong + 409.1 301.4 409.1 301.4 409.2 301.4 3 pls + 413.7 307.4 413.7 307.3 413.7 307.2 3 pls + 406.3 301.4 406.4 301.4 406.4 301.4 3 pls + 413.7 310.9 413.7 310.1 413.7 309.2 3 pls + 396.3 301.4 397.0 301.4 397.8 301.4 3 pls + 413.7 328.0 413.7 327.3 413.7 326.6 413.7 325.9 4 pls + 373.9 301.4 374.0 301.4 374.0 301.4 3 pls + s[ 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 + 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 413.7 + 413.7 413.7 413.7 413.7 413.7 ][ 341.3 341.4 341.4 341.5 341.5 341.6 341.8 341.9 342.1 342.1 342.2 342.3 342.4 + 342.4 342.4 342.4 342.4 342.5 342.5 342.5 342.5 342.6 342.6 342.7 342.7 343.4 343.4 343.5 344.0 344.1 345.5 + 346.2 348.0 348.1 349.1 349.2 350.2 350.3 350.3 351.2 351.2 ]plong + s[ 352.1 351.2 350.3 349.4 348.6 347.8 347.0 346.3 345.6 344.9 344.2 343.6 342.9 342.3 341.7 341.2 340.6 333.9 + 328.5 323.7 ][ 299.8 298.9 298.0 297.1 296.3 295.5 294.7 294.0 293.3 292.6 291.9 291.3 290.6 290.0 289.4 288.8 + 288.3 281.6 276.2 271.4 ]plong + s[ 443.7 436.9 429.2 419.8 407.6 398.1 390.5 383.7 376.8 369.2 359.8 347.6 338.1 330.5 323.7 ][ 271.4 271.4 271.4 + 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 271.4 ]plong + s[ 443.7 438.9 433.5 426.8 426.2 425.6 425.0 424.4 423.8 423.2 422.5 421.8 421.1 420.3 419.6 418.8 418.0 417.1 + 416.2 415.3 414.3 413.3 412.3 411.3 410.4 409.4 408.5 407.6 406.7 405.8 405.0 404.1 393.4 383.7 374.0 373.3 + 372.6 371.8 371.1 370.3 366.5 365.7 364.9 ][ 271.4 266.5 261.1 254.4 253.9 253.3 252.7 252.1 251.5 250.8 250.1 + 249.5 248.7 248.0 247.2 246.4 245.6 244.8 243.9 242.9 242.0 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 + 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 ]plong + s[ 355.1 354.1 353.1 352.1 351.2 350.3 349.4 348.6 347.8 347.0 346.3 345.6 344.9 344.2 343.6 342.9 342.3 341.7 + 341.2 340.6 333.9 328.5 323.7 ][ 241.4 241.4 242.0 242.9 243.9 244.8 245.6 246.4 247.2 248.0 248.7 249.5 250.1 + 250.8 251.5 252.1 252.7 253.3 253.9 254.4 261.1 266.5 271.4 ]plong + s[ 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 + 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 ][ 271.4 264.5 256.9 247.4 235.3 225.8 + 218.2 211.4 204.5 196.9 196.3 195.7 195.1 194.5 193.8 193.2 192.5 191.8 191.1 190.4 189.7 189.0 188.2 187.4 + 186.6 185.8 185.0 184.1 183.2 182.3 ]plong + s[ 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 + 323.7 323.7 323.7 323.7 ][ 243.2 244.1 245.0 245.8 246.6 247.4 248.2 249.0 249.7 250.4 251.1 251.8 252.5 253.2 + 253.8 254.5 255.1 255.7 256.3 256.9 264.5 271.4 ]plong + s[ 443.7 444.3 444.9 445.6 446.2 446.8 447.4 448.0 448.6 449.2 449.8 450.4 451.0 451.5 452.1 452.6 453.1 453.7 + 454.2 454.7 455.1 455.6 456.0 456.5 456.9 457.3 457.7 458.0 458.4 458.7 459.0 459.3 459.5 459.8 460.0 460.2 + 460.4 460.5 460.7 460.8 460.9 460.9 461.0 461.0 461.0 461.0 461.0 460.9 460.8 460.7 460.6 460.4 460.2 460.0 + 459.8 459.6 459.3 459.0 458.7 458.4 458.1 457.7 457.3 456.9 456.5 456.1 455.7 455.2 454.7 454.2 453.7 453.2 + 452.7 452.1 451.6 451.0 450.5 449.9 449.3 448.7 448.1 447.5 446.9 446.3 445.6 445.0 444.4 443.8 443.1 442.5 + 441.9 441.3 440.7 440.0 439.4 438.8 438.2 437.7 437.1 436.5 435.9 435.4 434.8 434.3 433.8 433.3 432.8 432.3 + 431.8 431.4 431.0 430.6 430.2 429.8 429.4 429.1 428.7 428.4 428.2 427.9 427.6 427.4 427.2 427.0 426.9 426.7 + 426.6 426.5 426.5 426.4 426.4 426.4 426.4 426.4 426.5 426.6 426.7 426.8 427.0 427.2 427.3 427.6 427.8 428.1 + 428.3 428.6 429.0 429.3 429.7 430.0 430.4 430.8 431.3 431.7 432.2 432.6 433.1 433.6 434.1 434.7 435.2 435.7 + 436.3 436.9 437.5 438.0 438.6 439.2 439.8 440.5 441.1 441.7 442.3 442.9 443.6 444.2 444.8 445.4 446.1 446.7 + 447.3 447.9 448.5 449.1 449.7 450.3 450.8 451.4 452.0 452.5 453.0 453.6 454.1 454.6 455.0 455.5 455.9 ][ 254.0 + 254.1 254.1 254.1 254.2 254.3 254.4 254.6 254.8 254.9 255.2 255.4 255.6 255.9 256.2 256.5 256.8 257.2 257.6 + 258.0 258.4 258.8 259.2 259.7 260.1 260.6 261.1 261.6 262.1 262.7 263.2 263.8 264.4 264.9 265.5 266.1 266.7 + 267.3 267.9 268.5 269.2 269.8 270.4 271.0 271.6 272.3 272.9 273.5 274.1 274.7 275.4 276.0 276.6 277.2 277.7 + 278.3 278.9 279.4 280.0 280.5 281.0 281.6 282.1 282.5 283.0 283.5 283.9 284.3 284.7 285.1 285.5 285.8 286.2 + 286.5 286.8 287.1 287.3 287.6 287.8 288.0 288.1 288.3 288.4 288.5 288.6 288.6 288.7 288.7 288.7 288.6 288.6 + 288.5 288.4 288.3 288.2 288.0 287.8 287.6 287.4 287.1 286.9 286.6 286.3 285.9 285.6 285.2 284.8 284.4 284.0 + 283.6 283.1 282.6 282.2 281.7 281.2 280.6 280.1 279.6 279.0 278.4 277.9 277.3 276.7 276.1 275.5 274.9 274.3 + 273.7 273.0 272.4 271.8 271.2 270.5 269.9 269.3 268.7 268.1 267.4 266.8 266.2 265.6 265.1 264.5 263.9 263.4 + 262.8 262.3 261.7 261.2 260.7 260.2 259.8 259.3 258.9 258.4 258.0 257.7 257.3 256.9 256.6 256.3 256.0 255.7 + 255.4 255.2 255.0 254.8 254.6 254.5 254.3 254.2 254.2 254.1 254.1 254.0 254.0 254.1 254.1 254.2 254.3 254.4 + 254.6 254.7 254.9 255.1 255.3 255.6 255.8 256.1 256.4 256.8 257.1 257.5 257.9 258.3 258.7 259.1 ]plong + s[ 443.7 444.8 445.9 447.0 448.1 449.2 450.3 451.4 452.6 453.8 455.0 456.3 457.6 458.9 460.3 461.7 463.2 464.8 + 466.5 468.2 470.1 ][ 228.7 228.7 228.7 228.8 228.9 229.0 229.1 229.2 229.4 229.6 229.9 230.1 230.4 230.8 231.1 + 231.6 232.0 232.5 233.1 233.7 234.4 ]plong + s[ 240.2 240.9 241.6 242.2 242.7 243.2 243.6 244.0 244.4 244.7 245.0 245.3 245.5 245.7 245.8 246.0 246.1 246.2 + 246.3 246.3 246.3 246.3 246.3 246.3 246.2 246.1 246.0 245.9 245.7 245.5 245.3 245.0 244.8 244.4 244.1 243.7 + 243.3 242.8 242.2 241.6 241.0 240.3 ][ 243.9 245.8 247.6 249.3 251.0 252.5 254.0 255.4 256.8 258.1 259.4 260.6 + 261.8 263.0 264.1 265.3 266.4 267.5 268.6 269.7 270.8 271.8 272.9 274.0 275.1 276.2 277.3 278.5 279.6 280.8 + 282.0 283.2 284.5 285.8 287.2 288.6 290.1 291.6 293.2 294.9 296.7 298.6 ]plong + s[ 470.3 468.4 466.7 465.0 463.4 461.9 460.4 459.0 457.7 456.4 455.2 453.9 452.7 451.6 450.4 449.3 448.2 447.1 + 446.0 444.9 443.8 442.7 441.7 440.6 439.5 438.4 437.2 436.1 434.9 433.7 432.5 431.3 430.0 428.7 427.3 425.8 + 424.3 422.8 421.1 419.4 417.5 417.4 417.3 417.1 417.0 416.8 416.7 416.6 416.4 416.3 416.1 416.0 415.8 415.7 + 415.6 415.4 415.3 415.1 415.0 414.8 414.7 414.5 414.4 414.2 414.1 413.9 ][ 308.2 308.9 309.6 310.1 310.7 311.1 + 311.5 311.9 312.3 312.6 312.8 313.1 313.3 313.5 313.6 313.8 313.9 313.9 314.0 314.0 314.1 314.0 314.0 314.0 + 313.9 313.8 313.7 313.5 313.3 313.1 312.9 312.6 312.3 312.0 311.6 311.2 310.8 310.3 309.7 309.1 308.4 308.3 + 308.3 308.2 308.2 308.1 308.1 308.0 308.0 307.9 307.9 307.8 307.7 307.7 307.6 307.6 307.5 307.5 307.4 307.3 + 307.3 307.2 307.2 307.1 307.0 307.0 ]plong + s[ 408.1 407.3 406.5 405.8 405.2 404.7 404.2 403.8 403.4 403.0 402.7 402.4 402.1 401.9 401.7 401.5 401.4 401.3 + 401.2 401.1 401.0 401.0 401.0 401.0 401.1 401.1 401.2 401.3 401.5 401.6 401.8 402.0 402.3 402.6 402.9 403.2 + 403.6 404.0 404.5 405.1 405.6 406.3 407.0 407.1 407.1 407.2 407.2 407.3 407.3 407.4 407.5 407.5 407.6 407.6 + 407.7 407.7 407.8 407.9 407.9 408.0 408.0 408.1 ][ 301.1 299.1 297.1 295.3 293.6 291.9 290.4 288.9 287.5 286.1 + 284.8 283.5 282.3 281.1 279.9 278.7 277.6 276.5 275.4 274.3 273.2 272.1 271.0 269.9 268.8 267.7 266.6 265.5 + 264.4 263.2 262.1 260.9 259.6 258.4 257.1 255.7 254.3 252.8 251.3 249.7 248.0 246.2 244.3 244.2 244.1 243.9 + 243.8 243.6 243.5 243.3 243.2 243.0 242.9 242.8 242.6 242.5 242.3 242.2 242.0 241.9 241.7 241.5 ]plong + s[ 414.0 414.1 414.3 414.4 414.6 414.7 414.9 415.0 415.2 415.3 415.5 415.6 415.8 415.9 416.0 416.2 416.3 416.5 + 416.6 416.7 416.9 418.8 420.5 422.2 423.8 425.3 426.8 428.2 429.5 430.8 432.1 433.3 434.5 435.7 436.9 438.0 + 439.1 440.2 441.3 442.4 443.5 444.5 445.6 446.7 447.8 448.9 450.0 451.2 452.3 453.5 454.7 456.0 457.3 458.6 + 460.0 461.4 462.9 464.5 466.1 467.8 469.7 471.6 473.7 ][ 235.7 235.7 235.6 235.6 235.5 235.4 235.4 235.3 235.3 + 235.2 235.1 235.1 235.0 235.0 234.9 234.9 234.8 234.8 234.7 234.6 234.6 233.9 233.2 232.7 232.1 231.7 231.2 + 230.9 230.5 230.2 229.9 229.7 229.5 229.3 229.1 229.0 228.9 228.8 228.7 228.7 228.7 228.7 228.7 228.8 228.8 + 228.9 229.1 229.2 229.4 229.6 229.8 230.1 230.4 230.7 231.1 231.5 231.9 232.4 233.0 233.6 234.3 235.0 235.9 ]plong + s[ 443.7 444.8 445.9 447.0 448.1 449.2 450.3 451.4 452.6 453.8 455.0 456.3 457.6 458.9 460.3 461.7 463.2 464.8 + 466.5 468.2 470.1 ][ 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 ]plong + s[ 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 + 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 + 263.7 263.7 263.7 263.7 263.7 263.7 ][ 243.9 245.8 247.6 249.3 251.0 252.5 254.0 255.4 256.8 258.1 259.4 260.6 + 261.8 263.0 264.1 265.3 266.4 267.5 268.6 269.7 270.8 271.8 272.9 274.0 275.1 276.2 277.3 278.5 279.6 280.8 + 282.0 283.2 284.5 285.8 287.2 288.6 290.1 291.6 293.2 294.9 296.7 298.6 ]plong + s[ 470.3 468.4 466.7 465.0 463.4 461.9 460.4 459.0 457.7 456.4 455.2 453.9 452.7 451.6 450.4 449.3 448.2 447.1 + 446.0 444.9 443.8 442.7 441.7 440.6 439.5 438.4 437.2 436.1 434.9 433.7 432.5 431.3 430.0 428.7 427.3 425.8 + 424.3 422.8 421.1 419.4 417.5 417.4 417.3 417.1 417.0 416.8 416.7 416.6 416.4 416.3 416.1 416.0 415.8 415.7 + 415.6 415.4 415.3 415.1 415.0 414.8 414.7 414.5 414.4 414.2 414.1 413.9 ][ 331.4 331.4 331.4 331.4 331.4 331.4 + 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 + 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 + 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 + 331.4 331.4 331.4 331.4 331.4 331.4 ]plong + s[ 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 + 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 + 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 + 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 + 383.7 383.7 383.7 ][ 301.1 301.0 300.8 300.7 300.5 300.4 300.2 300.1 299.9 299.8 299.6 299.5 299.3 299.2 299.1 + 297.1 295.3 293.6 291.9 290.4 288.9 287.5 286.1 284.8 283.5 282.3 281.1 279.9 278.7 277.6 276.5 275.4 274.3 + 273.2 272.1 271.0 269.9 268.8 267.7 266.6 265.5 264.4 263.2 262.1 260.9 259.6 258.4 257.1 255.7 254.3 252.8 + 251.3 249.7 248.0 246.2 244.3 244.2 244.1 243.9 243.8 243.6 243.5 243.3 243.2 243.0 242.9 242.8 242.6 242.5 + 242.3 242.2 242.0 241.9 241.7 241.5 ]plong + s[ 414.0 414.1 414.3 414.4 414.6 414.7 414.9 415.0 415.2 415.3 415.5 415.6 415.8 415.9 416.0 416.2 416.3 416.5 + 416.6 416.7 416.9 418.8 420.5 422.2 423.8 425.3 426.8 428.2 429.5 430.8 432.1 433.3 434.5 435.7 436.9 452.3 + 473.7 ][ 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 + 211.4 211.4 ]plong + s[ 443.7 444.8 445.9 447.0 448.1 449.2 450.3 451.4 452.6 453.8 455.0 456.3 457.6 458.9 460.3 461.7 463.2 464.8 + 466.5 468.2 470.1 ][ 194.0 194.0 194.0 193.9 193.8 193.7 193.6 193.5 193.3 193.1 192.8 192.6 192.3 191.9 191.6 + 191.1 190.7 190.2 189.6 189.0 188.3 ]plong + s[ 287.2 286.4 285.8 285.2 284.6 284.1 283.7 283.3 282.9 282.6 282.3 282.1 281.8 281.7 281.5 281.3 281.2 281.1 + 281.1 281.0 281.0 281.0 281.0 281.1 281.1 281.2 281.3 281.5 281.6 281.8 282.0 282.3 282.6 282.9 283.2 283.6 + 284.1 284.6 285.1 285.7 286.3 287.1 ][ 243.9 245.8 247.6 249.3 251.0 252.5 254.0 255.4 256.8 258.1 259.4 260.6 + 261.8 263.0 264.1 265.3 266.4 267.5 268.6 269.7 270.8 271.8 272.9 274.0 275.1 276.2 277.3 278.5 279.6 280.8 + 282.0 283.2 284.5 285.8 287.2 288.6 290.1 291.6 293.2 294.9 296.7 298.6 ]plong + s[ 470.3 468.4 466.7 465.0 463.4 461.9 460.4 459.0 457.7 456.4 455.2 453.9 452.7 451.6 450.4 449.3 448.2 447.1 + 446.0 444.9 443.8 442.7 441.7 440.6 439.5 438.4 437.2 436.1 434.9 433.7 432.5 431.3 430.0 428.7 427.3 425.8 + 424.3 422.8 421.1 419.4 417.5 417.4 417.3 417.1 417.0 416.8 416.7 416.6 416.4 416.3 416.1 416.0 415.8 415.7 + 415.6 415.4 415.3 415.1 415.0 414.8 414.7 414.5 414.4 414.2 414.1 413.9 ][ 354.5 353.8 353.2 352.6 352.1 351.6 + 351.2 350.8 350.5 350.2 349.9 349.7 349.5 349.3 349.1 349.0 348.9 348.8 348.8 348.7 348.7 348.7 348.7 348.8 + 348.9 349.0 349.1 349.3 349.4 349.6 349.9 350.1 350.4 350.8 351.1 351.5 352.0 352.5 353.1 353.7 354.4 354.4 + 354.5 354.5 354.6 354.6 354.7 354.7 354.8 354.8 354.9 355.0 355.0 355.1 355.1 355.2 355.2 355.3 355.4 355.4 + 355.5 355.5 355.6 355.7 355.7 355.8 ]plong + s[ 359.3 359.3 359.4 359.5 359.5 359.6 359.7 359.7 359.8 359.8 359.9 359.9 360.0 360.1 360.1 360.9 361.5 362.1 + 362.7 363.2 363.6 364.0 364.4 364.7 365.0 365.3 365.5 365.7 365.9 366.0 366.1 366.2 366.3 366.3 366.4 366.4 + 366.3 366.3 366.2 366.1 366.0 365.9 365.7 365.6 365.3 365.1 364.8 364.5 364.1 363.8 363.3 362.9 362.3 361.7 + 361.1 360.4 360.3 360.3 360.2 360.2 360.1 360.0 360.0 359.9 359.9 359.8 359.7 359.7 359.6 359.6 359.5 359.4 + 359.4 359.3 359.3 ][ 301.1 301.0 300.8 300.7 300.5 300.4 300.2 300.1 299.9 299.8 299.6 299.5 299.3 299.2 299.1 + 297.1 295.3 293.6 291.9 290.4 288.9 287.5 286.1 284.8 283.5 282.3 281.1 279.9 278.7 277.6 276.5 275.4 274.3 + 273.2 272.1 271.0 269.9 268.8 267.7 266.6 265.5 264.4 263.2 262.1 260.9 259.6 258.4 257.1 255.7 254.3 252.8 + 251.3 249.7 248.0 246.2 244.3 244.2 244.1 243.9 243.8 243.6 243.5 243.3 243.2 243.0 242.9 242.8 242.6 242.5 + 242.3 242.2 242.0 241.9 241.7 241.5 ]plong + s[ 414.0 414.1 414.3 414.4 414.6 414.7 414.9 415.0 415.2 415.3 415.5 415.6 415.8 415.9 416.0 416.2 416.3 416.5 + 416.6 416.7 416.9 418.8 420.5 422.2 423.8 425.3 426.8 428.2 429.5 430.8 432.1 433.3 434.5 435.7 436.9 438.0 + 439.1 440.2 441.3 442.4 443.5 444.5 445.6 446.7 447.8 448.9 450.0 451.2 452.3 453.5 454.7 456.0 457.3 458.6 + 460.0 461.4 462.9 464.5 466.1 467.8 469.7 471.6 473.7 ][ 187.0 187.0 187.1 187.2 187.2 187.3 187.3 187.4 187.4 + 187.5 187.6 187.6 187.7 187.7 187.8 187.8 187.9 188.0 188.0 188.1 188.1 188.8 189.5 190.1 190.6 191.0 191.5 + 191.9 192.2 192.5 192.8 193.0 193.2 193.4 193.6 193.7 193.8 193.9 194.0 194.0 194.0 194.0 194.0 193.9 193.9 + 193.8 193.6 193.5 193.3 193.1 192.9 192.6 192.3 192.0 191.6 191.2 190.8 190.3 189.7 189.1 188.4 187.7 186.9 ]plong + s[ 323.7 323.1 322.4 321.8 321.2 320.6 320.0 319.3 318.7 318.2 317.6 317.0 316.4 315.8 315.3 314.8 314.2 313.7 + 313.2 312.7 312.2 311.8 311.3 310.9 310.5 310.1 309.7 309.3 309.0 308.7 308.4 308.1 307.8 307.6 307.4 307.2 + 307.0 306.8 306.7 306.6 306.5 306.4 306.4 306.4 306.4 306.4 306.4 306.5 306.6 306.7 306.8 307.0 307.1 307.3 + 307.6 307.8 308.1 308.3 308.6 309.0 309.3 309.7 310.0 310.4 310.9 311.3 311.7 312.2 312.7 313.1 313.7 314.2 + 314.7 315.2 315.8 316.3 316.9 317.5 318.1 318.7 319.3 319.9 320.5 321.1 321.7 322.4 323.0 323.6 324.2 324.9 + 325.5 326.1 326.7 327.3 327.9 328.5 329.1 329.7 330.3 330.9 331.4 332.0 332.5 333.1 333.6 334.1 334.6 335.1 + 335.5 336.0 336.4 336.8 337.2 337.6 338.0 338.3 338.6 338.9 339.2 339.5 339.7 340.0 340.2 340.3 340.5 340.6 + 340.8 340.8 340.9 341.0 341.0 341.0 341.0 340.9 340.9 340.8 340.7 340.5 340.4 340.2 340.0 339.8 339.6 339.3 + 339.0 338.7 338.4 338.1 337.7 337.3 337.0 336.5 336.1 335.7 335.2 334.7 334.3 333.8 333.2 332.7 332.2 331.6 + 331.1 330.5 329.9 329.3 328.7 328.1 327.5 326.9 326.3 325.7 325.1 324.4 323.8 323.2 322.6 321.9 321.3 320.7 + 320.1 319.5 318.9 318.3 317.7 317.1 316.5 316.0 315.4 314.9 314.3 313.8 313.3 312.8 312.3 311.9 311.4 ][ 254.0 + 254.1 254.1 254.1 254.2 254.3 254.4 254.6 254.8 254.9 255.2 255.4 255.6 255.9 256.2 256.5 256.8 257.2 257.6 + 258.0 258.4 258.8 259.2 259.7 260.1 260.6 261.1 261.6 262.1 262.7 263.2 263.8 264.4 264.9 265.5 266.1 266.7 + 267.3 267.9 268.5 269.2 269.8 270.4 271.0 271.6 272.3 272.9 273.5 274.1 274.7 275.4 276.0 276.6 277.2 277.7 + 278.3 278.9 279.4 280.0 280.5 281.0 281.6 282.1 282.5 283.0 283.5 283.9 284.3 284.7 285.1 285.5 285.8 286.2 + 286.5 286.8 287.1 287.3 287.6 287.8 288.0 288.1 288.3 288.4 288.5 288.6 288.6 288.7 288.7 288.7 288.6 288.6 + 288.5 288.4 288.3 288.2 288.0 287.8 287.6 287.4 287.1 286.9 286.6 286.3 285.9 285.6 285.2 284.8 284.4 284.0 + 283.6 283.1 282.6 282.2 281.7 281.2 280.6 280.1 279.6 279.0 278.4 277.9 277.3 276.7 276.1 275.5 274.9 274.3 + 273.7 273.0 272.4 271.8 271.2 270.5 269.9 269.3 268.7 268.1 267.4 266.8 266.2 265.6 265.1 264.5 263.9 263.4 + 262.8 262.3 261.7 261.2 260.7 260.2 259.8 259.3 258.9 258.4 258.0 257.7 257.3 256.9 256.6 256.3 256.0 255.7 + 255.4 255.2 255.0 254.8 254.6 254.5 254.3 254.2 254.2 254.1 254.1 254.0 254.0 254.1 254.1 254.2 254.3 254.4 + 254.6 254.7 254.9 255.1 255.3 255.6 255.8 256.1 256.4 256.8 257.1 257.5 257.9 258.3 258.7 259.1 ]plong + 447.6 208.6 447.6 211.4 2 pls + 451.7 208.6 451.7 211.4 2 pls + 456.1 208.6 456.1 211.4 2 pls + 461.0 208.6 461.0 211.4 2 pls + 466.7 208.6 466.7 211.4 2 pls + 266.4 248.3 263.7 248.3 2 pls + 266.4 254.0 263.7 254.0 2 pls + 266.4 258.9 263.7 258.9 2 pls + 266.4 263.3 263.7 263.3 2 pls + 266.4 267.4 263.7 267.4 2 pls + 266.4 275.3 263.7 275.3 2 pls + 266.4 279.4 263.7 279.4 2 pls + 266.4 283.8 263.7 283.8 2 pls + 266.4 288.7 263.7 288.7 2 pls + 266.4 294.4 263.7 294.4 2 pls + 466.7 334.1 466.7 331.4 2 pls + 461.0 334.1 461.0 331.4 2 pls + 456.1 334.1 456.1 331.4 2 pls + 451.7 334.1 451.7 331.4 2 pls + 447.6 334.1 447.6 331.4 2 pls + 439.7 334.1 439.7 331.4 2 pls + 435.7 334.1 435.7 331.4 2 pls + 431.3 334.1 431.3 331.4 2 pls + 426.4 334.1 426.4 331.4 2 pls + 420.7 334.1 420.7 331.4 2 pls + 381.0 294.4 383.7 294.4 2 pls + 381.0 288.7 383.7 288.7 2 pls + 381.0 283.8 383.7 283.8 2 pls + 381.0 279.4 383.7 279.4 2 pls + 381.0 275.3 383.7 275.3 2 pls + 381.0 267.4 383.7 267.4 2 pls + 381.0 263.3 383.7 263.3 2 pls + 381.0 258.9 383.7 258.9 2 pls + 381.0 254.0 383.7 254.0 2 pls + 381.0 248.3 383.7 248.3 2 pls + 420.7 208.6 420.7 211.4 2 pls + 426.4 208.6 426.4 211.4 2 pls + 431.3 208.6 431.3 211.4 2 pls + 435.7 208.6 435.7 211.4 2 pls + 439.7 208.6 439.7 211.4 2 pls + 447.6 208.6 447.6 211.4 2 pls + 451.7 208.6 451.7 211.4 2 pls + 456.1 208.6 456.1 211.4 2 pls + 461.0 208.6 461.0 211.4 2 pls + 466.7 208.6 466.7 211.4 2 pls + 446.4 274.2 443.7 274.0 2 pls + 446.4 276.9 443.7 276.7 2 pls + 446.4 279.6 443.7 279.4 2 pls + 446.4 282.5 443.7 282.3 2 pls + 446.4 285.6 443.7 285.4 2 pls + 446.4 292.6 443.7 292.4 2 pls + 446.4 296.8 443.7 296.5 2 pls + 446.4 301.6 443.7 301.4 2 pls + 446.4 306.4 443.7 306.2 2 pls + 446.4 310.5 443.7 310.4 2 pls + 446.4 317.5 443.7 317.4 2 pls + 446.4 320.5 443.7 320.5 2 pls + 446.4 323.4 443.7 323.3 2 pls + 446.4 326.1 443.7 326.1 2 pls + 446.4 328.8 443.7 328.8 2 pls + 446.4 334.0 443.7 334.0 2 pls + 446.4 336.6 443.7 336.7 2 pls + 446.4 339.4 443.7 339.4 2 pls + 446.4 342.2 443.7 342.3 2 pls + 446.4 345.3 443.7 345.4 2 pls + 446.4 352.2 443.7 352.4 2 pls + 446.4 356.4 443.7 356.6 2 pls + 446.1 362.7 443.7 361.4 2 pls + 320.9 296.8 323.7 296.5 2 pls + 320.9 292.6 323.7 292.4 2 pls + 320.9 285.6 323.7 285.4 2 pls + 320.9 282.5 323.7 282.3 2 pls + 320.9 279.6 323.7 279.4 2 pls + 320.9 276.9 323.7 276.7 2 pls + 320.9 274.2 323.7 274.0 2 pls + s[ 473.2 473.0 472.8 472.5 472.3 472.1 471.8 471.6 471.4 471.2 470.9 470.7 470.5 470.3 470.1 469.9 469.7 469.5 + 469.2 469.0 468.8 468.6 468.4 468.2 468.0 467.8 467.6 465.0 462.6 460.3 458.1 456.1 454.2 452.3 450.5 448.8 + 447.1 445.4 443.7 ][ 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 ]plong + s[ 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 ][ 241.4 244.6 247.4 + 250.1 252.5 254.8 256.9 258.9 260.9 262.7 264.5 266.3 268.0 269.7 271.4 ]plong + s[ 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 + 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 263.7 + 263.7 263.7 263.7 263.7 263.7 ][ 271.4 273.1 274.7 276.5 278.2 280.0 281.9 283.8 285.8 287.9 290.2 292.7 295.3 + 295.5 295.7 295.9 296.1 296.3 296.5 296.7 296.9 297.1 297.3 297.5 297.8 298.0 298.2 298.4 298.6 298.8 299.1 + 299.3 299.5 299.7 300.0 300.2 300.4 300.7 300.9 301.1 301.4 ]plong + s[ 473.7 470.5 467.6 465.0 462.6 460.3 458.1 456.1 454.2 452.3 450.5 448.8 447.1 445.4 443.7 ][ 331.4 331.4 331.4 + 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 ]plong + s[ 443.7 442.0 440.3 438.6 436.9 435.1 433.2 431.3 429.2 427.1 424.8 422.4 419.8 416.9 413.7 ][ 331.4 331.4 331.4 + 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 331.4 ]plong + s[ 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 + 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 + 383.7 383.7 383.7 ][ 300.9 300.7 300.4 300.2 300.0 299.7 299.5 299.3 299.1 298.8 298.6 298.4 298.2 298.0 297.8 + 297.5 297.3 297.1 296.9 296.7 296.5 296.3 296.1 295.9 295.7 295.5 295.3 292.7 290.2 287.9 285.8 283.8 281.9 + 280.0 278.2 276.5 274.7 273.1 271.4 ]plong + s[ 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 383.7 ][ 271.4 269.7 268.0 + 266.3 264.5 262.7 260.9 258.9 256.9 254.8 252.5 250.1 247.4 244.6 241.4 ]plong + s[ 414.2 414.4 414.6 414.9 415.1 415.3 415.6 415.8 416.0 416.2 416.4 416.7 416.9 417.1 417.3 417.5 417.7 417.9 + 418.2 418.4 418.6 418.8 419.0 419.2 419.4 419.6 419.8 422.4 424.8 427.1 429.2 431.3 433.2 435.1 436.9 438.6 + 440.3 442.0 443.7 ][ 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 ]plong + s[ 443.7 445.4 447.1 448.8 450.5 452.3 454.2 456.1 458.1 460.3 462.6 465.0 467.6 470.5 473.7 ][ 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 ]plong + s[ 443.7 445.4 447.1 448.8 450.5 452.3 454.2 456.1 458.1 460.3 462.6 465.0 467.6 470.5 473.7 ][ 211.4 211.4 211.4 + 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 211.4 ]plong + s[ 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 ][ 271.4 272.5 273.6 + 274.7 275.9 277.0 278.2 279.4 280.6 281.9 283.1 284.5 285.8 287.2 288.7 ]plong + s[ 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 ][ 288.7 290.2 291.8 + 293.5 295.3 297.2 299.2 301.4 303.5 305.6 307.4 309.2 310.9 312.5 314.1 ]plong + s[ 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 ][ 314.1 315.5 316.9 + 318.3 319.6 320.9 322.1 323.3 324.5 325.7 326.9 328.0 329.1 330.3 331.4 ]plong + s[ 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 ][ 331.4 332.5 333.6 + 334.8 335.9 337.1 338.2 339.4 340.6 341.9 343.2 344.5 345.8 347.2 348.7 ]plong + s[ 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 443.7 + 443.7 443.7 443.7 ][ 348.7 350.2 351.8 353.5 355.3 357.2 359.2 359.4 359.5 359.7 359.8 360.0 360.1 360.3 360.4 + 360.6 360.7 360.9 361.1 361.2 361.4 ]plong + s[ 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 + 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 ][ 301.1 300.9 300.7 300.6 + 300.4 300.3 300.1 300.0 299.8 299.7 299.5 299.4 299.2 299.1 298.9 298.8 298.6 298.5 298.3 298.2 298.0 297.9 + 297.8 297.6 297.5 297.3 297.2 295.3 293.5 291.8 290.2 288.7 ]plong + s[ 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 323.7 ][ 288.7 287.2 285.8 + 284.5 283.1 281.9 280.6 279.4 278.2 277.0 275.9 274.7 273.6 272.5 271.4 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 241.6 + 241.7 241.9 242.0 242.1 242.3 242.4 242.6 242.7 242.9 243.0 243.1 243.3 243.4 243.6 271.5 299.4 ]plong + 421.0 182.4 416.6 182.4 414.5 184.6 414.5 189.0 414.5 193.3 5 pls + 434.0 182.4 429.7 182.4 425.3 182.4 421.0 182.4 4 pls + 447.0 182.4 442.7 182.4 438.4 182.4 434.0 182.4 4 pls + 460.1 182.4 455.7 182.4 451.4 182.4 447.0 182.4 4 pls + 473.1 182.4 468.8 182.4 464.4 182.4 460.1 182.4 4 pls + 473.1 182.4 475.3 184.6 475.3 189.0 475.3 193.3 4 pls + 414.5 193.3 414.5 197.6 414.5 202.0 414.5 206.3 4 pls + 475.3 206.3 475.3 202.0 475.3 197.6 475.3 193.3 4 pls + 414.5 206.3 414.5 210.7 414.5 215.0 414.5 219.4 4 pls + 475.3 219.4 475.3 215.0 475.3 210.7 475.3 206.3 4 pls + 414.5 219.4 414.5 223.7 414.5 228.1 414.5 232.4 4 pls + 475.3 232.4 475.3 228.1 475.3 223.7 475.3 219.4 4 pls + 238.5 243.3 234.2 243.3 232.0 245.4 3 pls + 251.6 243.3 247.2 243.3 242.9 243.3 238.5 243.3 4 pls + 264.6 243.3 260.3 243.3 255.9 243.3 251.6 243.3 4 pls + 277.6 243.3 273.3 243.3 269.0 243.3 264.6 243.3 4 pls + 290.7 243.3 286.3 243.3 282.0 243.3 277.6 243.3 4 pls + 303.7 243.3 299.4 243.3 295.0 243.3 290.7 243.3 4 pls + 316.7 243.3 312.4 243.3 308.0 243.3 303.7 243.3 4 pls + 329.8 243.3 325.4 243.3 321.1 243.3 316.7 243.3 4 pls + 342.8 243.3 338.5 243.3 334.1 243.3 329.8 243.3 4 pls + 355.8 243.3 351.5 243.3 347.1 243.3 342.8 243.3 4 pls + 368.9 243.3 364.5 243.3 360.2 243.3 355.8 243.3 4 pls + 381.9 243.3 377.5 243.3 373.2 243.3 368.9 243.3 4 pls + 394.9 243.3 390.6 243.3 386.2 243.3 381.9 243.3 4 pls + 408.0 243.3 403.6 243.3 399.3 243.3 394.9 243.3 4 pls + 414.5 232.4 414.5 236.7 414.5 241.1 412.3 243.3 408.0 243.3 5 pls + s[ 475.3 475.3 475.3 477.5 481.8 486.1 ][ 232.4 236.7 241.1 243.3 243.3 243.3 ]plong + 499.2 243.3 494.8 243.3 490.5 243.3 486.1 243.3 4 pls + 512.2 243.3 507.9 243.3 503.5 243.3 499.2 243.3 4 pls + 525.2 243.3 520.9 243.3 516.5 243.3 512.2 243.3 4 pls + 538.3 243.3 533.9 243.3 529.6 243.3 525.2 243.3 4 pls + 551.3 243.3 547.0 243.3 542.6 243.3 538.3 243.3 4 pls + 564.3 243.3 560.0 243.3 555.6 243.3 551.3 243.3 4 pls + 577.4 243.3 573.0 243.3 568.7 243.3 564.3 243.3 4 pls + 590.4 243.3 586.1 243.3 581.7 243.3 577.4 243.3 4 pls + 232.0 245.4 232.0 249.8 232.0 254.1 232.0 258.5 4 pls + 232.0 258.5 232.0 262.8 232.0 267.1 232.0 271.5 4 pls + 232.0 271.5 232.0 275.8 232.0 280.2 232.0 284.5 4 pls + 232.0 284.5 232.0 288.9 232.0 293.2 232.0 297.6 4 pls + 232.0 297.6 234.2 299.7 238.5 299.7 3 pls + 251.6 299.7 247.2 299.7 242.9 299.7 238.5 299.7 4 pls + 264.6 299.7 260.3 299.7 255.9 299.7 251.6 299.7 4 pls + 277.6 299.7 273.3 299.7 269.0 299.7 264.6 299.7 4 pls + 290.7 299.7 286.3 299.7 282.0 299.7 277.6 299.7 4 pls + 303.7 299.7 299.4 299.7 295.0 299.7 290.7 299.7 4 pls + 316.7 299.7 312.4 299.7 308.0 299.7 303.7 299.7 4 pls + 329.8 299.7 325.4 299.7 321.1 299.7 316.7 299.7 4 pls + 342.8 299.7 338.5 299.7 334.1 299.7 329.8 299.7 4 pls + 355.8 299.7 351.5 299.7 347.1 299.7 342.8 299.7 4 pls + 368.9 299.7 364.5 299.7 360.2 299.7 355.8 299.7 4 pls + 381.9 299.7 377.5 299.7 373.2 299.7 368.9 299.7 4 pls + 394.9 299.7 390.6 299.7 386.2 299.7 381.9 299.7 4 pls + 408.0 299.7 403.6 299.7 399.3 299.7 394.9 299.7 4 pls + 408.0 299.7 412.3 299.7 414.5 301.9 414.5 306.2 414.5 310.6 5 pls + s[ 475.3 475.3 475.3 477.5 481.8 486.1 ][ 310.6 306.2 301.9 299.7 299.7 299.7 ]plong + 499.2 299.7 494.8 299.7 490.5 299.7 486.1 299.7 4 pls + 512.2 299.7 507.9 299.7 503.5 299.7 499.2 299.7 4 pls + 525.2 299.7 520.9 299.7 516.5 299.7 512.2 299.7 4 pls + 538.3 299.7 533.9 299.7 529.6 299.7 525.2 299.7 4 pls + 551.3 299.7 547.0 299.7 542.6 299.7 538.3 299.7 4 pls + 564.3 299.7 560.0 299.7 555.6 299.7 551.3 299.7 4 pls + 577.4 299.7 573.0 299.7 568.7 299.7 564.3 299.7 4 pls + 590.4 299.7 586.1 299.7 581.7 299.7 577.4 299.7 4 pls + 414.5 310.6 414.5 314.9 414.5 319.3 414.5 323.6 4 pls + 475.3 323.6 475.3 319.3 475.3 314.9 475.3 310.6 4 pls + 414.5 323.6 414.5 328.0 414.5 332.3 414.5 336.6 4 pls + 475.3 336.6 475.3 332.3 475.3 328.0 475.3 323.6 4 pls + 414.5 336.6 414.5 341.0 414.5 345.3 414.5 349.7 4 pls + 475.3 349.7 475.3 345.3 475.3 341.0 475.3 336.6 4 pls + 414.5 349.7 414.5 354.0 414.5 358.4 416.6 360.5 421.0 360.5 5 pls + 434.0 360.5 429.7 360.5 425.3 360.5 421.0 360.5 4 pls + 447.0 360.5 442.7 360.5 438.4 360.5 434.0 360.5 4 pls + 460.1 360.5 455.7 360.5 451.4 360.5 447.0 360.5 4 pls + 473.1 360.5 468.8 360.5 464.4 360.5 460.1 360.5 4 pls + 473.1 360.5 475.3 358.4 475.3 354.0 475.3 349.7 4 pls + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 261.43 233.42 moveto[ 261.4 249.3 249.3 261.4 ][ 233.4 233.4 250.5 250.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 259.8 237.6 251.0 237.6 252.2 236.3 252.7 235.5 4 pls + s[ 251.0 251.0 254.7 254.3 253.9 253.9 254.3 255.2 256.4 257.2 258.5 259.3 259.8 259.8 259.3 258.9 258.1 ][ 247.6 + 243.4 243.0 243.4 244.7 246.0 247.2 248.0 248.5 248.5 248.0 247.2 246.0 244.7 243.4 243.0 242.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 261.43 263.43 moveto[ 261.4 249.3 249.3 261.4 ][ 263.4 263.4 280.6 280.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 259.8 267.6 251.0 267.6 252.2 266.4 252.7 265.5 4 pls + s[ 251.0 251.4 252.2 253.1 253.9 254.3 254.7 255.2 256.0 256.8 258.1 258.9 259.3 259.8 259.8 259.3 258.9 258.1 + 256.8 256.0 255.2 254.7 254.3 253.9 253.1 252.2 251.4 251.0 251.0 ][ 274.7 273.5 273.0 273.0 273.5 274.3 276.0 + 277.2 278.0 278.5 278.5 278.0 277.6 276.4 274.7 273.5 273.0 272.6 272.6 273.0 273.9 275.1 276.8 277.6 278.0 + 278.0 277.6 276.4 274.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 464.52 315.36 moveto[ 464.5 464.5 480.4 480.4 ][ 315.4 327.5 327.5 315.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 467.0 467.0 467.4 467.9 468.7 470.4 471.2 471.6 472.0 472.0 471.6 470.8 466.6 472.5 ][ 323.7 324.1 325.0 325.4 + 325.8 325.8 325.4 325.0 324.1 323.3 322.5 321.2 317.0 317.0 ]plong + 478.3 317.0 478.3 325.8 477.0 324.5 476.2 324.1 4 pls +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 438.69 315.36 moveto[ 438.7 438.7 448.7 448.7 ][ 315.4 327.5 327.5 315.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 443.3 442.0 441.2 440.8 440.8 441.2 442.0 443.3 444.1 445.4 446.2 446.6 446.6 446.2 445.4 444.1 443.3 ][ 325.8 + 325.4 324.1 322.0 320.8 318.7 317.4 317.0 317.0 317.4 318.7 320.8 322.0 324.1 325.4 325.8 325.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 408.68 315.36 moveto[ 408.7 408.7 418.7 418.7 ][ 315.4 327.5 327.5 315.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 411.6 416.2 413.7 414.9 415.8 416.2 416.6 416.6 416.2 415.4 414.1 412.9 411.6 411.2 410.8 ][ 325.8 325.8 322.5 + 322.5 322.0 321.6 320.4 319.5 318.3 317.4 317.0 317.0 317.4 317.9 318.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 385.93 275.96 moveto[ 385.9 398.0 398.0 385.9 ][ 276.0 276.0 266.4 266.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 395.1 396.0 396.4 396.4 396.0 394.7 392.6 390.5 388.8 388.0 387.6 387.6 388.0 388.8 390.1 390.5 391.8 392.6 + 393.0 393.0 392.6 391.8 390.5 ][ 268.9 269.3 270.5 271.4 272.6 273.5 273.9 273.9 273.5 272.6 271.4 270.9 269.7 + 268.9 268.4 268.4 268.9 269.7 270.9 271.4 272.6 273.5 273.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 385.93 246.37 moveto[ 385.9 398.0 398.0 385.9 ][ 246.4 246.4 236.8 236.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 393.4 392.2 391.4 390.9 390.9 391.4 392.2 393.4 393.9 395.1 396.0 396.4 396.4 396.0 395.1 393.4 391.4 389.3 + 388.0 387.6 387.6 388.0 388.8 ][ 238.9 239.3 240.1 241.4 241.8 243.0 243.9 244.3 244.3 243.9 243.0 241.8 241.4 + 240.1 239.3 238.9 238.9 239.3 240.1 241.4 242.2 243.4 243.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 435.76 213.59 moveto[ 435.8 435.8 452.9 452.9 ][ 213.6 225.7 225.7 213.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 439.9 215.3 439.9 224.0 438.7 222.8 437.9 222.4 4 pls + s[ 445.4 445.4 445.8 446.2 447.0 448.7 449.5 450.0 450.4 450.4 450.0 449.1 445.0 450.8 ][ 221.9 222.4 223.2 223.6 + 224.0 224.0 223.6 223.2 222.4 221.5 220.7 219.4 215.3 215.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 441.46 257.16 moveto[ 441.5 429.3 429.3 441.5 ][ 257.2 257.2 286.0 286.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 436.0 266.8 436.0 259.2 2 pls + s[ 433.9 435.2 436.0 436.4 436.4 436.0 435.2 433.9 433.5 432.3 431.4 431.0 431.0 431.4 432.3 433.9 436.0 438.1 + 439.4 439.8 439.8 439.4 438.5 ][ 275.1 274.7 273.9 272.6 272.2 270.9 270.1 269.7 269.7 270.1 270.9 272.2 272.6 + 273.9 274.7 275.1 275.1 274.7 273.9 272.6 271.8 270.5 270.1 ]plong + s[ 431.0 431.4 432.7 434.8 436.0 438.1 439.4 439.8 439.8 439.4 438.1 436.0 434.8 432.7 431.4 431.0 431.0 ][ 280.6 + 279.3 278.5 278.0 278.0 278.5 279.3 280.6 281.4 282.6 283.5 283.9 283.9 283.5 282.6 281.4 280.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 441.46 339.51 moveto[ 441.5 429.3 429.3 441.5 ][ 339.5 339.5 357.9 357.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 431.0 431.0 434.4 434.4 434.8 435.2 436.4 437.3 438.5 439.4 439.8 439.8 439.4 439.0 438.1 ][ 342.4 347.0 344.5 + 345.8 346.6 347.0 347.4 347.4 347.0 346.2 344.9 343.7 342.4 342.0 341.6 ]plong + s[ 431.0 431.4 432.7 434.8 436.0 438.1 439.4 439.8 439.8 439.4 438.1 436.0 434.8 432.7 431.4 431.0 431.0 ][ 352.5 + 351.2 350.4 350.0 350.0 350.4 351.2 352.5 353.3 354.5 355.4 355.8 355.8 355.4 354.5 353.3 352.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 325.91 280.56 moveto[ 325.9 338.0 338.0 325.9 ][ 280.6 280.6 262.2 262.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 333.4 332.2 331.3 330.9 330.9 331.3 332.2 333.4 333.9 335.1 335.9 336.4 336.4 335.9 335.1 333.4 331.3 329.3 + 328.0 327.6 327.6 328.0 328.8 ][ 273.0 273.5 274.3 275.5 276.0 277.2 278.1 278.5 278.5 278.1 277.2 276.0 275.5 + 274.3 273.5 273.0 273.0 273.5 274.3 275.5 276.4 277.6 278.1 ]plong + s[ 336.4 335.9 334.7 332.6 331.3 329.3 328.0 327.6 327.6 328.0 329.3 331.3 332.6 334.7 335.9 336.4 336.4 ][ 267.6 + 268.9 269.7 270.1 270.1 269.7 268.9 267.6 266.8 265.5 264.7 264.3 264.3 264.7 265.5 266.8 267.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 377.17 382.20 moveto[ 377.2 377.2 451.5 451.5 ][ 382.2 394.3 394.3 382.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 379.3 383.9 382.6 392.6 2 pls + 385.9 383.9 382.6 392.6 2 pls + 384.7 386.8 380.5 386.8 2 pls + 394.7 383.9 394.7 392.6 2 pls + 400.1 392.6 394.7 392.6 2 pls + 398.1 388.5 394.7 388.5 2 pls + 402.2 383.9 402.2 392.6 2 pls + 407.2 383.9 407.2 392.6 2 pls + 410.2 392.6 404.3 392.6 2 pls + s[ 417.7 416.9 415.6 413.9 412.7 411.8 411.8 412.3 412.7 413.5 416.0 416.9 417.3 417.7 417.7 416.9 415.6 413.9 + 412.7 411.8 ][ 391.4 392.2 392.6 392.6 392.2 391.4 390.6 389.7 389.3 388.9 388.0 387.6 387.2 386.4 385.1 384.3 + 383.9 383.9 384.3 385.1 ]plong + 429.8 383.9 429.0 383.9 428.1 384.3 427.7 385.5 427.7 392.6 5 pls + 429.4 389.7 426.5 389.7 2 pls + s[ 431.9 436.9 436.9 436.5 436.1 435.2 434.0 433.1 432.3 431.9 431.9 432.3 433.1 434.0 435.2 436.1 436.9 ][ 387.2 + 387.2 388.0 388.9 389.3 389.7 389.7 389.3 388.5 387.2 386.4 385.1 384.3 383.9 383.9 384.3 385.1 ]plong + s[ 444.0 443.6 442.3 441.1 439.8 439.4 439.8 440.7 442.7 443.6 444.0 444.0 443.6 442.3 441.1 439.8 439.4 ][ 388.5 + 389.3 389.7 389.7 389.3 388.5 387.6 387.2 386.8 386.4 385.5 385.1 384.3 383.9 383.9 384.3 385.1 ]plong + 449.4 383.9 448.6 383.9 447.8 384.3 447.3 385.5 447.3 392.6 5 pls + 449.0 389.7 446.1 389.7 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/wcsconverter.f b/ast_tester/wcsconverter.f new file mode 100644 index 0000000..9af2e30 --- /dev/null +++ b/ast_tester/wcsconverter.f @@ -0,0 +1,222 @@ + PROGRAM WCSCONVERTER + +* Usage: +* WCSCONVERTER + +* Description: +* Reads a FrameSet from "in file" (as a FITS header if possible, +* otherwise as an AST dump of a FrameSet), and writes out the +* FrameSet to "out file" using the specified encoding. + +* Parameters: +* in file +* A text file containing fits headers or an AST dump of a FrameSet. +* encoding +* The name of a FITS encoding (e.g. "FITS-WCS", "NATIVE", etc), or +* "AST". +* out file +* The output file. Contains an AST dump of the FrameSet if +* "encoding" is "AST", or a set of FITS header cards otherwise. +* attrs +* A list of attribute settings to apply to the FitsChan before +* reading the input headers. + + + IMPLICIT NONE + INCLUDE 'AST_PAR' + EXTERNAL SOURCE, SINK + + INTEGER STATUS, FC, OBJECT, IARGC, CHAN, CHR_LEN, OC + CHARACTER FILE*80, OFILE*80, LINE*255, ENCODING*50 + CHARACTER ATTRS*200 + LOGICAL CDM + + STATUS = 0 +* +* Check command line arguments have been supplied. +* + IF( IARGC() .LT. 3 ) THEN + WRITE(*,*) 'Usage: wcsconverter '// + : ' ' + RETURN + END IF + +* +* Use object caching to minimise allocation of new memory +* + OC = AST_TUNE( 'ObjectCaching', 1, STATUS ) + IF( OC .NE. 0 ) THEN + WRITE(*,'(A,I6)') 'Default ObjectCaching VALUE is ',OC + END IF + + IF( AST_TUNE( 'ObjectCaching', AST__TUNULL, STATUS ) .NE. 1 ) THEN + WRITE(*,'(A,I6)') 'Set ObjectCaching VALUE is ',OC + END IF + +* +* Create a FitsChan to store the FITS headers. +* + FC = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) + +* +* Apply any attribute settings to the FitsChan. +* + IF( IARGC() .EQ. 4 ) THEN + CALL GETARG( 4, ATTRS ) + CALL AST_SET( FC, ATTRS, STATUS ) + ELSE + ATTRS = ' ' + END IF + +* +* Open the input text file. +* + CALL GETARG( 1, FILE ) + OPEN( UNIT=10, FILE=FILE, STATUS='OLD' ) + +* +* Read each line out of the text file and store it in the FitsChan. +* + CALL ERR_MARK + DO WHILE( .TRUE. ) + READ( 10, '(A)', END = 10 ) LINE + CALL AST_PUTFITS( FC, LINE, 0, STATUS ) + END DO + + 10 CLOSE( 10 ) + +* +* Set the value of CDMatrix, unless it has already been set. +* + IF( .NOT. AST_TEST( FC, 'CDMATRIX', STATUS ) ) THEN + CDM = AST_GETL( FC, 'CDMATRIX', STATUS ) + CALL AST_SETL( FC, 'CDMATRIX', CDM, status ) + END IF + +* +* Attempt to read an Object form the FitsChan. +* + CALL AST_CLEAR( FC, 'CARD', STATUS ) + OBJECT = AST_READ( FC, STATUS ) + + IF( STATUS .NE. 0 ) CALL ERR_ANNUL( STATUS ) + CALL ERR_RLSE + +* If no object was read, attempt to read an object from the text file as +* an AST dump. + IF( OBJECT .EQ. AST__NULL ) THEN + CALL AST_ANNUL( FC, STATUS ) + OPEN( UNIT=10, FILE=FILE, STATUS='OLD' ) + CHAN = AST_CHANNEL( SOURCE, AST_NULL, ' ', STATUS ) + OBJECT = AST_READ( CHAN, STATUS ) + CALL AST_ANNUL( CHAN, STATUS ) + CLOSE( 10 ) + END IF + +* +* Abort if no object was read. +* + IF( OBJECT .EQ. AST__NULL ) THEN + WRITE(*,*) 'wcsconverter: no WCS could be read from ', + : file( : chr_len( file ) ) + RETURN + + +* +* Otherwise write out the object using the specified encoding. +* + ELSE + CALL GETARG( 3, OFILE ) + CALL DELETEFILE( OFILE ) + + CALL GETARG( 2, ENCODING ) + IF( ENCODING .EQ. 'AST' .OR. ENCODING .EQ. 'ast' ) THEN + OPEN( UNIT=10, FILE=OFILE, STATUS='NEW' ) + CHAN = AST_CHANNEL( AST_NULL, SINK, ' ', STATUS ) + IF( AST_WRITE( CHAN, OBJECT, STATUS ) .NE. 1 ) THEN + WRITE(*,*) 'wcsconverter: WCS read from ', + : file( : chr_len( file ) ),' could not be '// + : 'converted to ', + : encoding( : chr_len(encoding ) ),' format.' + END IF + CALL AST_ANNUL( CHAN, STATUS ) + CLOSE( 10 ) + + ELSE + OPEN( UNIT=10, FILE=OFILE, STATUS='NEW' ) + IF( FC .EQ. AST__NULL ) THEN + FC = AST_FITSCHAN( AST_NULL, AST_NULL, ATTRS, STATUS ) + END IF + CALL AST_SETC( FC, 'ENCODING', ENCODING, STATUS ) + CALL AST_CLEAR( FC, 'CARD', STATUS ) + + IF( AST_WRITE( FC, OBJECT, STATUS ) .NE. 1 ) THEN + WRITE(*,*) 'wcsconverter: WCS read from ', + : file( : chr_len( file ) ),' could not be '// + : 'converted to ', + : encoding( : chr_len(encoding ) ),' format.' + ELSE + CALL AST_CLEAR( FC, 'CARD', STATUS ) + DO WHILE( AST_FINDFITS( FC, '%f', LINE, .TRUE., + : STATUS ) ) + WRITE(10,'(A)') LINE( : 80 ) + END DO + END IF + CLOSE( 10 ) + + CALL AST_ANNUL( FC, STATUS ) + + + END IF + CALL AST_ANNUL( OBJECT, STATUS ) + END IF + + + END + + +* +* Delete a file if it exists. +* + SUBROUTINE DELETEFILE( FILNAM ) + IMPLICIT NONE + + CHARACTER FILNAM*(*) + LOGICAL EXISTS + + INQUIRE ( FILE = FILNAM, + : EXIST = EXISTS ) + + IF( EXISTS ) THEN + OPEN ( UNIT=10, FILE=FILNAM, STATUS='OLD' ) + CLOSE ( 10, STATUS='DELETE' ) + END IF + + END + + +* +* SOURCE FUNCTION FOR AST_CHANNEL. +* + SUBROUTINE SOURCE( STATUS ) + IMPLICIT NONE + INTEGER STATUS + CHARACTER BUFFER*200 + READ( 10, '(A)', END=99 ) BUFFER + CALL AST_PUTLINE( BUFFER, LEN( BUFFER ), STATUS ) + RETURN + 99 CALL AST_PUTLINE( BUFFER, -1, STATUS ) + END + +* +* SINK FUNCTION FOR AST_CHANNEL. +* + SUBROUTINE SINK( STATUS ) + IMPLICIT NONE + INTEGER STATUS, L + CHARACTER BUFFER*200 + + CALL AST_GETLINE( BUFFER, L, STATUS ) + IF( L .GT. 0 ) WRITE( 10, '(A)' ) BUFFER( : L ) + + END diff --git a/ast_tester/zpn.attr b/ast_tester/zpn.attr new file mode 100644 index 0000000..d74badf --- /dev/null +++ b/ast_tester/zpn.attr @@ -0,0 +1 @@ +tol=0.0001 diff --git a/ast_tester/zpn.box b/ast_tester/zpn.box new file mode 100644 index 0000000..ee14a3a --- /dev/null +++ b/ast_tester/zpn.box @@ -0,0 +1 @@ +1 1 200.0 200.0 diff --git a/ast_tester/zpn.head b/ast_tester/zpn.head new file mode 100644 index 0000000..a5e90af --- /dev/null +++ b/ast_tester/zpn.head @@ -0,0 +1,136 @@ +SIMPLE = T +BITPIX = -32 / IEEE (big-endian) 32-bit floating point data +NAXIS = 2 +NAXIS1 = 200 +NAXIS2 = 200 +BUNIT = 'JY/BEAM ' +CTYPE1 = 'RA---ZPN' +CRPIX1 = 100 +CDELT1 = -6.666666666667E-02 +CRVAL1 = 0.000000000000E+00 +CTYPE2 = 'DEC--ZPN' +CRPIX2 = 100 +CDELT2 = 6.666666666667E-02 +CRVAL2 = -9.000000000000E+01 +LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole +LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole +PV2_0 = 10.000000000000E-02 / Projection parameter 0 +PV2_1 = 9.750000000000E-01 / Projection parameter 1 +COMMENT PV2_2 = -8.070000000000E-01 / Projection parameter 2 +COMMENT PV2_3 = 3.370000000000E-01 / Projection parameter 3 +COMMENT PV2_4 = -6.500000000000E-02 / Projection parameter 4 +COMMENT PV2_5 = 1.000000000000E-02 / Projection parameter 5 +COMMENT PV2_6 = 3.000000000000E-03 / Projection parameter 6 +COMMENT PV2_7 = -1.000000000000E-03 / Projection parameter 7 +COMMENT PV2_8 = 0.000000000000E+00 / Projection parameter 8 +COMMENT PV2_9 = 0.000000000000E+00 / Projection parameter 9 +COMMENT PV2_10 = 0.000000000000E+00 / Projection parameter 10 +COMMENT PV2_11 = 0.000000000000E+00 / Projection parameter 11 +COMMENT PV2_12 = 0.000000000000E+00 / Projection parameter 12 +COMMENT PV2_13 = 0.000000000000E+00 / Projection parameter 13 +COMMENT PV2_14 = 0.000000000000E+00 / Projection parameter 14 +COMMENT PV2_15 = 0.000000000000E+00 / Projection parameter 15 +COMMENT PV2_16 = 0.000000000000E+00 / Projection parameter 16 +COMMENT PV2_17 = 0.000000000000E+00 / Projection parameter 17 +COMMENT PV2_18 = 0.000000000000E+00 / Projection parameter 18 +COMMENT PV2_19 = 0.000000000000E+00 / Projection parameter 19 +EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates +BMAJ = 2.399999936422E-01 / Beam major axis in degrees +BMIN = 2.399999936422E-01 / Beam minor axis in degrees +BPA = 0.000000000000E+00 / Beam position angle in degrees +RESTFREQ= 1.420405750000E+09 / Line rest frequency, Hz +HISTORY Parkes Multibeam continuum map +HISTORY Formed on Mon 2004/02/09 01:38:20 GMT by "pksgridzilla" which was +HISTORY compiled on Feb 9 2004 12:08:02 (local time) within +HISTORY AIPS++ version 19.405.00 dated . +HISTORY Polarization mode: A and B aggregated +HISTORY Gridding parameters: +HISTORY Method: WGTMED +HISTORY Clip fraction: 0.000 +HISTORY Tsys weighting: applied +HISTORY Beam weight order: 1 +HISTORY Beam FWHM: 14.4 arcmin +HISTORY Beam normalization: applied +HISTORY Smoothing kernel type: TOP-HAT +HISTORY Kernel FWHM: 12.0 arcmin +HISTORY Cutoff radius: 6.0 arcmin +HISTORY Beam RSS cutoff: 0.0 +HISTORY Input data sets: +HISTORY 97-10-09_0356_193558-66_206a.sdfits +HISTORY 97-10-12_0142_182123-66_193a.sdfits +HISTORY 97-10-12_0151_182707-66_194a.sdfits +HISTORY 97-10-12_0200_183252-66_195a.sdfits +HISTORY 97-11-07_0510_183836-66_196a.sdfits +HISTORY 97-11-07_0519_184420-66_197a.sdfits +HISTORY 97-11-07_0528_185004-66_198a.sdfits +HISTORY 97-11-07_0537_185548-66_199a.sdfits +HISTORY 97-11-07_0546_190132-66_200a.sdfits +HISTORY 97-11-07_0556_190717-66_201a.sdfits +HISTORY 97-11-07_0645_191301-66_202a.sdfits +HISTORY 97-11-07_0654_191845-66_203a.sdfits +HISTORY 97-11-07_0703_192429-66_204a.sdfits +HISTORY 97-11-07_0712_193013-66_205a.sdfits +HISTORY 97-11-07_0724_194142-66_207a.sdfits +HISTORY 97-11-18_0256_193815-66_206c.sdfits +HISTORY 97-11-18_0306_194359-66_207c.sdfits +HISTORY 97-11-19_0447_182341-66_193c.sdfits +HISTORY 97-11-19_0456_182925-66_194c.sdfits +HISTORY 97-11-19_0507_190350-66_200c.sdfits +HISTORY 97-11-19_0516_190934-66_201c.sdfits +HISTORY 97-11-19_0525_191519-66_202c.sdfits +HISTORY 97-11-19_0534_192103-66_203c.sdfits +HISTORY 97-11-19_0544_192647-66_204c.sdfits +HISTORY 97-11-19_0553_193231-66_205c.sdfits +HISTORY 97-11-19_0602_183509-66_195c.sdfits +HISTORY 97-11-19_0612_184053-66_196c.sdfits +HISTORY 97-11-19_0622_184638-66_197c.sdfits +HISTORY 97-11-19_0631_185222-66_198c.sdfits +HISTORY 97-11-19_0640_185806-66_199c.sdfits +HISTORY 98-03-24_2107_193706-66_206b.sdfits +HISTORY 98-03-24_2116_194251-66_207b.sdfits +HISTORY 98-03-25_2020_190826-66_201b.sdfits +HISTORY 98-03-25_2029_191410-66_202b.sdfits +HISTORY 98-03-25_2038_191954-66_203b.sdfits +HISTORY 98-03-25_2047_192538-66_204b.sdfits +HISTORY 98-03-25_2056_193122-66_205b.sdfits +HISTORY 98-03-26_2048_190459-66_200d.sdfits +HISTORY 98-03-27_2034_191627-66_202d.sdfits +HISTORY 98-03-27_2043_192212-66_203d.sdfits +HISTORY 98-03-27_2052_192756-66_204d.sdfits +HISTORY 98-03-27_2102_193340-66_205d.sdfits +HISTORY 98-03-27_2111_193924-66_206d.sdfits +HISTORY 98-03-27_2120_194508-66_207d.sdfits +HISTORY 98-03-27_2130_191043-66_201d.sdfits +HISTORY 98-05-10_2123_182232-66_193b.sdfits +HISTORY 98-05-10_2133_182816-66_194b.sdfits +HISTORY 98-05-10_2142_183400-66_195b.sdfits +HISTORY 98-05-10_2151_183945-66_196b.sdfits +HISTORY 98-05-10_2200_184529-66_197b.sdfits +HISTORY 98-05-10_2209_185113-66_198b.sdfits +HISTORY 98-05-10_2219_185657-66_199b.sdfits +HISTORY 98-05-10_2228_190241-66_200b.sdfits +HISTORY 98-05-13_2132_182450-66_193d.sdfits +HISTORY 98-05-13_2151_183034-66_194d.sdfits +HISTORY 98-05-13_2200_183618-66_195d.sdfits +HISTORY 98-05-13_2210_184202-66_196d.sdfits +HISTORY 98-05-13_2219_184746-66_197d.sdfits +HISTORY 98-05-13_2228_185331-66_198d.sdfits +HISTORY 98-05-13_2237_185915-66_199d.sdfits +HISTORY 98-05-25_1711_182559-66_193e.sdfits +HISTORY 98-05-25_1720_183143-66_194e.sdfits +HISTORY 98-05-25_1729_183727-66_195e.sdfits +HISTORY 98-05-25_1738_184311-66_196e.sdfits +HISTORY 98-05-25_1747_184855-66_197e.sdfits +HISTORY 98-05-25_1756_185439-66_198e.sdfits +HISTORY 98-05-25_1806_190024-66_199e.sdfits +HISTORY 98-05-25_1815_190608-66_200e.sdfits +HISTORY 98-05-25_1824_191152-66_201e.sdfits +HISTORY 98-05-25_1833_191736-66_202e.sdfits +HISTORY 98-05-25_1842_192320-66_203e.sdfits +HISTORY 98-05-25_1851_192905-66_204e.sdfits +HISTORY 98-05-25_1901_193449-66_205e.sdfits +HISTORY 98-05-25_1910_194033-66_206e.sdfits +HISTORY 98-05-25_1919_194617-66_207e.sdfits +HISTORY Original FITS filename "1904-66_ZPN.continuum.fits". +HISTORY Noise level of continuum map: 61 mJy (RMS) +END diff --git a/ast_tester/zpn.ps b/ast_tester/zpn.ps new file mode 100644 index 0000000..a0b1cd8 --- /dev/null +++ b/ast_tester/zpn.ps @@ -0,0 +1,6407 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Thu May 17 12:12:37 2007 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: a.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37-1 x86_64-unknown-linux-gnu - Workstation 2721 +%%Creation date: 17/05/1907 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 393.9 76.0 393.9 77.0 393.9 85.2 393.9 93.5 393.9 101.7 5 pls + s[ 513.3 513.3 513.3 513.3 513.3 513.3 513.3 513.3 513.3 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 + 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.5 513.5 513.5 513.6 513.6 513.6 513.7 + 513.7 513.7 513.7 513.8 513.8 513.8 513.9 513.9 513.9 514.0 514.0 514.0 514.0 514.1 514.1 514.1 514.2 514.6 + 515.0 515.4 515.8 516.2 516.6 517.1 517.5 517.9 518.3 518.7 519.1 519.5 520.0 520.4 520.8 521.2 521.6 522.0 + 522.4 522.9 523.3 523.7 524.1 524.5 524.9 530.7 536.5 542.4 548.2 554.0 559.8 565.6 571.4 577.2 583.0 588.4 ][ + 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 + 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.0 151.0 151.0 150.9 150.9 150.9 150.9 150.8 150.8 + 150.8 150.7 150.7 150.7 150.6 150.6 150.6 150.6 150.5 150.5 150.5 150.4 150.4 150.4 150.3 150.3 150.3 149.9 + 149.5 149.0 148.6 148.2 147.8 147.4 147.0 146.6 146.1 145.7 145.3 144.9 144.5 144.1 143.7 143.2 142.8 142.4 + 142.0 141.6 141.2 140.8 140.3 139.9 139.5 133.7 127.9 122.1 116.3 110.5 104.7 98.9 93.1 87.3 81.5 76.0 ]plong + s[ 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 + 562.8 562.8 562.8 562.8 562.9 562.9 562.9 562.9 562.9 562.9 562.9 563.0 563.0 563.1 563.1 563.2 563.2 563.2 + 563.3 563.3 563.4 563.4 563.5 563.5 563.5 563.6 563.6 563.7 563.7 563.7 563.8 563.8 563.9 563.9 564.0 564.5 + 565.1 565.7 566.3 566.9 567.5 568.1 568.6 569.2 569.8 570.4 571.0 571.6 572.2 572.7 573.3 573.9 574.5 575.1 + 575.7 576.3 576.9 577.4 578.0 578.6 579.2 587.4 590.4 ][ 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 ]plong + s[ 513.3 513.3 513.3 513.3 513.3 513.3 513.3 513.3 513.3 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 + 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.4 513.5 513.5 513.5 513.6 513.6 513.6 513.7 + 513.7 513.7 513.7 513.8 513.8 513.8 513.9 513.9 513.9 514.0 514.0 514.0 514.0 514.1 514.1 514.1 514.2 514.6 + 515.0 515.4 515.8 516.2 516.6 517.1 517.5 517.9 518.3 518.7 519.1 519.5 520.0 520.4 520.8 521.2 521.6 522.0 + 522.4 522.9 523.3 523.7 524.1 524.5 524.9 530.7 536.5 542.4 548.2 554.0 559.8 565.6 571.4 577.2 583.0 588.8 + 590.4 ][ 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 + 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 390.0 390.0 390.0 390.0 390.0 390.1 390.1 390.1 390.2 390.2 + 390.2 390.2 390.3 390.3 390.3 390.4 390.4 390.4 390.5 390.5 390.5 390.5 390.6 390.6 390.6 390.7 390.7 390.7 + 391.1 391.6 392.0 392.4 392.8 393.2 393.6 394.0 394.5 394.9 395.3 395.7 396.1 396.5 396.9 397.4 397.8 398.2 + 398.6 399.0 399.4 399.8 400.3 400.7 401.1 401.5 407.3 413.1 418.9 424.7 430.5 436.3 442.1 447.9 453.7 459.5 + 465.4 467.0 ]plong + s[ 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 + 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 + 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 + 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 + 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 393.9 ][ 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 + 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 439.4 + 439.5 439.5 439.6 439.6 439.6 439.7 439.7 439.8 439.8 439.8 439.9 439.9 440.0 440.0 440.1 440.1 440.1 440.2 + 440.2 440.3 440.3 440.4 440.4 440.4 440.5 440.5 441.1 441.7 442.3 442.9 443.5 444.0 444.6 445.2 445.8 446.4 + 447.0 447.6 448.1 448.7 449.3 449.9 450.5 451.1 451.7 452.2 452.8 453.4 454.0 454.6 455.2 455.8 464.0 467.0 ]plong + s[ 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 + 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.4 274.4 274.4 274.3 274.3 274.3 274.3 274.2 + 274.2 274.2 274.1 274.1 274.1 274.0 274.0 274.0 274.0 273.9 273.9 273.9 273.8 273.8 273.8 273.8 273.7 273.3 + 272.9 272.5 272.1 271.7 271.2 270.8 270.4 270.0 269.6 269.2 268.7 268.3 267.9 267.5 267.1 266.7 266.3 265.8 + 265.4 265.0 264.6 264.2 263.8 263.4 262.9 257.1 251.3 245.5 239.7 233.9 228.1 222.3 216.5 210.7 204.9 199.5 ][ + 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 389.9 + 389.9 389.9 389.9 389.9 389.9 389.9 389.9 390.0 390.0 390.0 390.0 390.0 390.1 390.1 390.1 390.2 390.2 390.2 + 390.2 390.3 390.3 390.3 390.4 390.4 390.4 390.5 390.5 390.5 390.5 390.6 390.6 390.6 390.7 390.7 390.7 391.1 + 391.6 392.0 392.4 392.8 393.2 393.6 394.0 394.5 394.9 395.3 395.7 396.1 396.5 396.9 397.4 397.8 398.2 398.6 + 399.0 399.4 399.8 400.3 400.7 401.1 401.5 407.3 413.1 418.9 424.7 430.5 436.3 442.1 447.9 453.7 459.5 465.0 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.0 225.0 + 225.0 225.0 225.0 225.0 225.0 225.0 225.0 225.0 225.0 225.0 224.9 224.9 224.8 224.8 224.8 224.7 224.7 224.6 + 224.6 224.6 224.5 224.5 224.4 224.4 224.3 224.3 224.3 224.2 224.2 224.1 224.1 224.1 224.0 224.0 223.9 223.3 + 222.8 222.2 221.6 221.0 220.4 219.8 219.2 218.7 218.1 217.5 216.9 216.3 215.7 215.1 214.5 214.0 213.4 212.8 + 212.2 211.6 211.0 210.4 209.9 209.3 208.7 200.5 199.5 ][ 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 + 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 270.5 ]plong + s[ 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 + 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.5 274.4 274.4 274.4 274.3 274.3 274.3 274.3 274.2 + 274.2 274.2 274.1 274.1 274.1 274.0 274.0 274.0 274.0 273.9 273.9 273.9 273.8 273.8 273.8 273.8 273.7 273.3 + 272.9 272.5 272.1 271.7 271.2 270.8 270.4 270.0 269.6 269.2 268.7 268.3 267.9 267.5 267.1 266.7 266.3 265.8 + 265.4 265.0 264.6 264.2 263.8 263.4 262.9 257.1 251.3 245.5 239.7 233.9 228.1 222.3 216.5 210.7 204.9 199.5 ][ + 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 + 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.1 151.0 151.0 151.0 150.9 150.9 150.9 150.9 150.8 150.8 + 150.8 150.7 150.7 150.7 150.6 150.6 150.6 150.6 150.5 150.5 150.5 150.4 150.4 150.4 150.3 150.3 150.3 149.9 + 149.5 149.0 148.6 148.2 147.8 147.4 147.0 146.6 146.1 145.7 145.3 144.9 144.5 144.1 143.7 143.2 142.8 142.4 + 142.0 141.6 141.2 140.8 140.3 139.9 139.5 133.7 127.9 122.1 116.3 110.5 104.7 98.9 93.1 87.3 81.5 76.0 ]plong + 393.9 76.0 393.9 77.0 393.9 85.2 393.9 93.5 393.9 101.7 5 pls + s[ 264.4 264.8 265.1 265.5 265.9 266.3 266.6 267.0 267.4 267.8 268.2 268.5 268.9 269.3 269.7 270.1 270.5 270.8 + 271.2 271.6 272.0 272.4 272.8 273.2 273.6 274.0 274.4 274.8 275.2 275.6 276.0 276.4 276.8 277.2 277.6 278.0 + 278.4 278.8 279.2 279.6 280.0 280.5 280.9 281.3 281.7 282.1 282.5 282.9 283.4 283.8 284.2 284.6 285.0 285.5 + 285.9 286.3 286.7 287.2 287.6 288.0 288.4 288.9 289.3 289.7 290.2 290.6 291.0 291.5 291.9 292.3 292.8 293.2 + 293.6 294.1 294.5 295.0 295.4 295.8 296.3 296.7 297.2 297.6 298.1 298.5 299.0 299.4 299.9 300.3 300.8 301.2 + 301.7 302.1 302.6 303.0 303.5 303.9 304.4 304.9 305.3 305.8 306.2 306.7 307.2 307.6 308.1 308.6 309.0 309.5 + 309.9 310.4 310.9 311.3 311.8 312.3 312.8 313.2 313.7 314.2 314.6 315.1 315.6 316.1 316.5 317.0 317.5 318.0 + 318.4 318.9 319.4 319.9 320.4 320.8 321.3 321.8 322.3 322.8 323.3 323.7 324.2 324.7 325.2 325.7 326.2 326.7 + 327.1 327.6 328.1 328.6 329.1 329.6 330.1 330.6 331.1 331.6 332.0 332.5 333.0 333.5 334.0 334.5 335.0 335.5 + 336.0 336.5 337.0 337.5 338.0 338.5 339.0 339.5 340.0 340.5 341.0 341.5 342.0 342.5 343.0 343.5 344.0 344.5 + 345.0 345.5 346.1 346.6 347.1 347.6 348.1 348.6 349.1 349.6 350.1 350.6 351.1 351.6 352.2 352.7 353.2 353.7 + 354.2 354.7 ][ 141.0 140.6 140.2 139.9 139.5 139.1 138.8 138.4 138.0 137.7 137.3 136.9 136.6 136.2 135.9 135.5 + 135.2 134.8 134.5 134.1 133.8 133.4 133.1 132.7 132.4 132.0 131.7 131.3 131.0 130.7 130.3 130.0 129.7 129.3 + 129.0 128.7 128.3 128.0 127.7 127.3 127.0 126.7 126.4 126.0 125.7 125.4 125.1 124.8 124.4 124.1 123.8 123.5 + 123.2 122.9 122.6 122.3 121.9 121.6 121.3 121.0 120.7 120.4 120.1 119.8 119.5 119.2 118.9 118.6 118.4 118.1 + 117.8 117.5 117.2 116.9 116.6 116.3 116.1 115.8 115.5 115.2 114.9 114.7 114.4 114.1 113.8 113.6 113.3 113.0 + 112.8 112.5 112.2 112.0 111.7 111.4 111.2 110.9 110.7 110.4 110.2 109.9 109.7 109.4 109.2 108.9 108.7 108.4 + 108.2 107.9 107.7 107.5 107.2 107.0 106.7 106.5 106.3 106.0 105.8 105.6 105.4 105.1 104.9 104.7 104.5 104.2 + 104.0 103.8 103.6 103.4 103.2 102.9 102.7 102.5 102.3 102.1 101.9 101.7 101.5 101.3 101.1 100.9 100.7 100.5 + 100.3 100.1 99.9 99.7 99.5 99.3 99.2 99.0 98.8 98.6 98.4 98.2 98.1 97.9 97.7 97.5 97.4 97.2 + 97.0 96.9 96.7 96.5 96.4 96.2 96.0 95.9 95.7 95.6 95.4 95.3 95.1 95.0 94.8 94.7 94.5 94.4 + 94.2 94.1 93.9 93.8 93.7 93.5 93.4 93.3 93.1 93.0 92.9 92.7 92.6 92.5 92.4 92.3 92.1 92.0 + 91.9 91.8 91.7 91.6 ]plong + s[ 354.7 355.2 355.7 356.2 356.8 357.3 357.8 358.3 358.8 359.3 359.8 360.4 360.9 361.4 361.9 362.4 362.9 363.5 + 364.0 364.5 365.0 365.5 366.0 366.6 367.1 367.6 368.1 368.6 369.2 369.7 370.2 370.7 371.2 371.8 372.3 372.8 + 373.3 373.8 374.4 374.9 375.4 375.9 376.5 377.0 377.5 378.0 378.5 379.1 379.6 380.1 380.6 381.2 381.7 382.2 + 382.7 383.2 383.8 384.3 384.8 385.3 385.9 386.4 386.9 387.4 388.0 388.5 389.0 389.5 390.1 390.6 391.1 391.6 + 392.2 392.7 393.2 393.7 394.3 394.8 395.3 395.8 396.4 396.9 397.4 397.9 398.4 399.0 399.5 400.0 400.5 401.1 + 401.6 402.1 402.6 403.2 403.7 404.2 404.7 405.3 405.8 406.3 406.8 407.4 407.9 408.4 408.9 409.4 410.0 410.5 + 411.0 411.5 412.1 412.6 413.1 413.6 414.1 414.7 415.2 415.7 416.2 416.7 417.3 417.8 418.3 418.8 419.3 419.9 + 420.4 420.9 421.4 421.9 422.5 423.0 423.5 424.0 424.5 425.0 425.6 426.1 426.6 427.1 427.6 428.1 428.7 429.2 + 429.7 430.2 430.7 431.2 431.7 432.3 432.8 433.3 433.8 434.3 434.8 435.3 435.8 436.3 436.9 437.4 437.9 438.4 + 438.9 439.4 439.9 440.4 440.9 441.4 441.9 442.4 442.9 443.4 444.0 444.5 445.0 445.5 446.0 446.5 447.0 447.5 + 448.0 448.5 449.0 449.5 450.0 450.5 451.0 451.5 452.0 452.5 453.0 453.5 454.0 454.4 454.9 455.4 455.9 456.4 + 456.9 457.4 ][ 91.6 91.4 91.3 91.2 91.1 91.0 90.9 90.8 90.7 90.6 90.5 90.4 90.3 90.2 90.1 90.0 + 89.9 89.9 89.8 89.7 89.6 89.5 89.4 89.4 89.3 89.2 89.1 89.1 89.0 88.9 88.8 88.8 88.7 88.6 + 88.6 88.5 88.5 88.4 88.3 88.3 88.2 88.2 88.1 88.1 88.0 88.0 87.9 87.9 87.9 87.8 87.8 87.7 + 87.7 87.7 87.6 87.6 87.6 87.6 87.5 87.5 87.5 87.5 87.4 87.4 87.4 87.4 87.4 87.4 87.3 87.3 + 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.4 87.4 87.4 87.4 + 87.4 87.4 87.5 87.5 87.5 87.5 87.6 87.6 87.6 87.7 87.7 87.7 87.8 87.8 87.8 87.9 87.9 88.0 + 88.0 88.0 88.1 88.1 88.2 88.3 88.3 88.4 88.4 88.5 88.5 88.6 88.7 88.7 88.8 88.9 88.9 89.0 + 89.1 89.1 89.2 89.3 89.4 89.5 89.5 89.6 89.7 89.8 89.9 90.0 90.0 90.1 90.2 90.3 90.4 90.5 + 90.6 90.7 90.8 90.9 91.0 91.1 91.2 91.4 91.5 91.6 91.7 91.8 91.9 92.0 92.2 92.3 92.4 92.5 + 92.6 92.8 92.9 93.0 93.2 93.3 93.4 93.6 93.7 93.8 94.0 94.1 94.3 94.4 94.5 94.7 94.8 95.0 + 95.1 95.3 95.5 95.6 95.8 95.9 96.1 96.2 96.4 96.6 96.7 96.9 97.1 97.2 97.4 97.6 97.8 97.9 + 98.1 98.3 98.5 98.6 ]plong + s[ 457.4 457.9 458.4 458.9 459.4 459.9 460.4 460.8 461.3 461.8 462.3 462.8 463.3 463.8 464.2 464.7 465.2 465.7 + 466.2 466.7 467.1 467.6 468.1 468.6 469.1 469.5 470.0 470.5 471.0 471.4 471.9 472.4 472.9 473.3 473.8 474.3 + 474.8 475.2 475.7 476.2 476.6 477.1 477.6 478.0 478.5 479.0 479.4 479.9 480.3 480.8 481.3 481.7 482.2 482.7 + 483.1 483.6 484.0 484.5 484.9 485.4 485.8 486.3 486.8 487.2 487.7 488.1 488.6 489.0 489.5 489.9 490.3 490.8 + 491.2 491.7 492.1 492.6 493.0 493.4 493.9 494.3 494.8 495.2 495.6 496.1 496.5 496.9 497.4 497.8 498.2 498.7 + 499.1 499.5 500.0 500.4 500.8 501.2 501.7 502.1 502.5 502.9 503.3 503.8 504.2 504.6 505.0 505.4 505.9 506.3 + 506.7 507.1 507.5 507.9 508.3 508.7 509.1 509.6 510.0 510.4 510.8 511.2 511.6 512.0 512.4 512.8 513.2 513.6 + 514.0 514.4 514.8 515.2 515.5 515.9 516.3 516.7 517.1 517.5 517.9 518.3 518.7 519.0 519.4 519.8 520.2 520.6 + 520.9 521.3 521.7 522.1 522.4 522.8 523.2 523.6 523.9 524.3 524.7 525.0 525.4 525.8 526.1 526.5 526.9 527.2 + 527.6 527.9 528.3 528.6 529.0 529.4 529.7 530.1 530.4 530.8 531.1 531.5 531.8 532.1 532.5 532.8 533.2 533.5 + 533.8 534.2 534.5 534.9 535.2 535.5 535.9 536.2 536.5 536.8 537.2 537.5 537.8 538.2 538.5 538.8 539.1 539.4 + 539.8 540.1 ][ 98.6 98.8 99.0 99.2 99.4 99.6 99.8 100.0 100.1 100.3 100.5 100.7 100.9 101.1 101.3 101.5 + 101.7 101.9 102.1 102.4 102.6 102.8 103.0 103.2 103.4 103.6 103.8 104.1 104.3 104.5 104.7 104.9 105.2 105.4 + 105.6 105.9 106.1 106.3 106.6 106.8 107.0 107.3 107.5 107.7 108.0 108.2 108.5 108.7 109.0 109.2 109.5 109.7 + 110.0 110.2 110.5 110.7 111.0 111.2 111.5 111.8 112.0 112.3 112.5 112.8 113.1 113.4 113.6 113.9 114.2 114.4 + 114.7 115.0 115.3 115.5 115.8 116.1 116.4 116.7 117.0 117.3 117.5 117.8 118.1 118.4 118.7 119.0 119.3 119.6 + 119.9 120.2 120.5 120.8 121.1 121.4 121.7 122.0 122.3 122.6 122.9 123.2 123.6 123.9 124.2 124.5 124.8 125.1 + 125.5 125.8 126.1 126.4 126.7 127.1 127.4 127.7 128.1 128.4 128.7 129.1 129.4 129.7 130.1 130.4 130.7 131.1 + 131.4 131.8 132.1 132.4 132.8 133.1 133.5 133.8 134.2 134.5 134.9 135.2 135.6 135.9 136.3 136.7 137.0 137.4 + 137.7 138.1 138.5 138.8 139.2 139.6 139.9 140.3 140.7 141.0 141.4 141.8 142.2 142.5 142.9 143.3 143.7 144.0 + 144.4 144.8 145.2 145.6 145.9 146.3 146.7 147.1 147.5 147.9 148.3 148.7 149.1 149.4 149.8 150.2 150.6 151.0 + 151.4 151.8 152.2 152.6 153.0 153.4 153.8 154.2 154.6 155.1 155.5 155.9 156.3 156.7 157.1 157.5 157.9 158.3 + 158.8 159.2 159.6 160.0 ]plong + s[ 540.1 540.4 540.7 541.0 541.3 541.6 541.9 542.3 542.6 542.9 543.2 543.5 543.8 544.1 544.4 544.7 545.0 545.3 + 545.6 545.9 546.2 546.4 546.7 547.0 547.3 547.6 547.9 548.2 548.4 548.7 549.0 549.3 549.6 549.8 550.1 550.4 + 550.7 550.9 551.2 551.5 551.7 552.0 552.3 552.5 552.8 553.1 553.3 553.6 553.8 554.1 554.3 554.6 554.8 555.1 + 555.3 555.6 555.8 556.1 556.3 556.6 556.8 557.0 557.3 557.5 557.8 558.0 558.2 558.5 558.7 558.9 559.1 559.4 + 559.6 559.8 560.0 560.3 560.5 560.7 560.9 561.1 561.3 561.6 561.8 562.0 562.2 562.4 562.6 562.8 563.0 563.2 + 563.4 563.6 563.8 564.0 564.2 564.4 564.6 564.8 565.0 565.1 565.3 565.5 565.7 565.9 566.1 566.2 566.4 566.6 + 566.8 566.9 567.1 567.3 567.4 567.6 567.8 567.9 568.1 568.3 568.4 568.6 568.7 568.9 569.1 569.2 569.4 569.5 + 569.7 569.8 570.0 570.1 570.2 570.4 570.5 570.7 570.8 570.9 571.1 571.2 571.3 571.5 571.6 571.7 571.9 572.0 + 572.1 572.2 572.3 572.5 572.6 572.7 572.8 572.9 573.0 573.1 573.2 573.4 573.5 573.6 573.7 573.8 573.9 574.0 + 574.1 574.2 574.3 574.3 574.4 574.5 574.6 574.7 574.8 574.9 574.9 575.0 575.1 575.2 575.3 575.3 575.4 575.5 + 575.5 575.6 575.7 575.7 575.8 575.9 575.9 576.0 576.1 576.1 576.2 576.2 576.3 576.3 576.4 576.4 576.5 576.5 + 576.5 576.6 ][ 160.0 160.4 160.8 161.3 161.7 162.1 162.5 163.0 163.4 163.8 164.2 164.7 165.1 165.5 165.9 166.4 + 166.8 167.2 167.7 168.1 168.5 169.0 169.4 169.9 170.3 170.7 171.2 171.6 172.1 172.5 172.9 173.4 173.8 174.3 + 174.7 175.2 175.6 176.1 176.5 177.0 177.4 177.9 178.3 178.8 179.2 179.7 180.1 180.6 181.1 181.5 182.0 182.4 + 182.9 183.4 183.8 184.3 184.7 185.2 185.7 186.1 186.6 187.1 187.5 188.0 188.5 188.9 189.4 189.9 190.4 190.8 + 191.3 191.8 192.2 192.7 193.2 193.7 194.1 194.6 195.1 195.6 196.1 196.5 197.0 197.5 198.0 198.5 198.9 199.4 + 199.9 200.4 200.9 201.4 201.9 202.3 202.8 203.3 203.8 204.3 204.8 205.3 205.8 206.3 206.7 207.2 207.7 208.2 + 208.7 209.2 209.7 210.2 210.7 211.2 211.7 212.2 212.7 213.2 213.7 214.2 214.7 215.2 215.7 216.2 216.7 217.2 + 217.7 218.2 218.7 219.2 219.7 220.2 220.7 221.2 221.7 222.2 222.7 223.2 223.7 224.2 224.7 225.3 225.8 226.3 + 226.8 227.3 227.8 228.3 228.8 229.3 229.8 230.4 230.9 231.4 231.9 232.4 232.9 233.4 233.9 234.5 235.0 235.5 + 236.0 236.5 237.0 237.5 238.1 238.6 239.1 239.6 240.1 240.6 241.2 241.7 242.2 242.7 243.2 243.8 244.3 244.8 + 245.3 245.8 246.4 246.9 247.4 247.9 248.4 249.0 249.5 250.0 250.5 251.0 251.6 252.1 252.6 253.1 253.6 254.2 + 254.7 255.2 255.7 256.3 ]plong + s[ 576.6 576.6 576.7 576.7 576.7 576.8 576.8 576.8 576.9 576.9 576.9 576.9 577.0 577.0 577.0 577.0 577.1 577.1 + 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 577.1 + 577.1 577.1 577.1 577.0 577.0 577.0 577.0 577.0 576.9 576.9 576.9 576.9 576.8 576.8 576.8 576.7 576.7 576.6 + 576.6 576.6 576.5 576.5 576.4 576.4 576.3 576.3 576.2 576.2 576.1 576.1 576.0 576.0 575.9 575.8 575.8 575.7 + 575.6 575.6 575.5 575.4 575.4 575.3 575.2 575.1 575.1 575.0 574.9 574.8 574.7 574.6 574.6 574.5 574.4 574.3 + 574.2 574.1 574.0 573.9 573.8 573.7 573.6 573.5 573.4 573.3 573.2 573.1 573.0 572.9 572.7 572.6 572.5 572.4 + 572.3 572.1 572.0 571.9 571.8 571.6 571.5 571.4 571.3 571.1 571.0 570.9 570.7 570.6 570.4 570.3 570.2 570.0 + 569.9 569.7 569.6 569.4 569.3 569.1 569.0 568.8 568.7 568.5 568.3 568.2 568.0 567.8 567.7 567.5 567.3 567.2 + 567.0 566.8 566.7 566.5 566.3 566.1 565.9 565.8 565.6 565.4 565.2 565.0 564.8 564.6 564.5 564.3 564.1 563.9 + 563.7 563.5 563.3 563.1 562.9 562.7 562.5 562.3 562.1 561.8 561.6 561.4 561.2 561.0 560.8 560.6 560.3 560.1 + 559.9 559.7 559.5 559.2 559.0 558.8 558.5 558.3 558.1 557.8 557.6 557.4 557.1 556.9 556.7 556.4 556.2 555.9 + 555.7 555.4 ][ 256.3 256.8 257.3 257.8 258.3 258.9 259.4 259.9 260.4 261.0 261.5 262.0 262.5 263.1 263.6 264.1 + 264.6 265.2 265.7 266.2 266.7 267.3 267.8 268.3 268.8 269.4 269.9 270.4 270.9 271.4 272.0 272.5 273.0 273.5 + 274.1 274.6 275.1 275.6 276.2 276.7 277.2 277.7 278.3 278.8 279.3 279.8 280.4 280.9 281.4 281.9 282.5 283.0 + 283.5 284.0 284.5 285.1 285.6 286.1 286.6 287.2 287.7 288.2 288.7 289.2 289.8 290.3 290.8 291.3 291.9 292.4 + 292.9 293.4 293.9 294.5 295.0 295.5 296.0 296.5 297.1 297.6 298.1 298.6 299.1 299.6 300.2 300.7 301.2 301.7 + 302.2 302.7 303.3 303.8 304.3 304.8 305.3 305.8 306.4 306.9 307.4 307.9 308.4 308.9 309.4 309.9 310.5 311.0 + 311.5 312.0 312.5 313.0 313.5 314.0 314.5 315.0 315.6 316.1 316.6 317.1 317.6 318.1 318.6 319.1 319.6 320.1 + 320.6 321.1 321.6 322.1 322.6 323.1 323.6 324.1 324.6 325.1 325.6 326.1 326.6 327.1 327.6 328.1 328.6 329.1 + 329.6 330.1 330.6 331.1 331.6 332.1 332.6 333.1 333.6 334.1 334.6 335.1 335.5 336.0 336.5 337.0 337.5 338.0 + 338.5 339.0 339.5 339.9 340.4 340.9 341.4 341.9 342.4 342.8 343.3 343.8 344.3 344.8 345.2 345.7 346.2 346.7 + 347.1 347.6 348.1 348.6 349.0 349.5 350.0 350.5 350.9 351.4 351.9 352.3 352.8 353.3 353.8 354.2 354.7 355.2 + 355.6 356.1 356.5 357.0 ]plong + 554.4 358.9 554.7 358.4 554.9 357.9 555.2 357.5 555.4 357.0 5 pls + s[ 554.4 554.2 553.9 553.7 553.4 553.2 552.9 552.6 552.4 552.1 551.8 551.6 551.3 551.0 550.8 550.5 550.2 550.0 + 549.7 549.4 549.1 548.8 548.6 548.3 548.0 547.7 547.4 547.1 546.9 546.6 546.3 546.0 545.7 545.4 545.1 544.8 + 544.5 544.2 543.9 543.6 543.3 543.0 542.7 542.4 542.1 541.8 541.5 541.1 540.8 540.5 540.2 539.9 539.6 539.2 + 538.9 538.6 538.3 538.0 537.6 537.3 537.0 536.7 536.3 536.0 535.7 535.3 535.0 534.7 534.3 534.0 533.6 533.3 + 533.0 532.6 532.3 531.9 531.6 531.2 530.9 530.5 530.2 529.8 529.5 529.1 528.8 528.4 528.1 527.7 527.4 527.0 + 526.6 526.3 525.9 525.5 525.2 524.8 524.4 524.1 523.7 523.3 523.0 522.6 522.2 521.8 521.5 521.1 520.7 520.3 + 520.0 519.6 519.2 518.8 518.4 518.0 517.7 517.3 516.9 516.5 516.1 515.7 515.3 514.9 514.5 514.1 513.7 513.3 + 512.9 512.5 512.1 511.7 511.3 510.9 510.5 510.1 509.7 509.3 508.9 508.5 508.1 507.7 507.3 506.9 506.4 506.0 + 505.6 505.2 504.8 504.4 503.9 503.5 503.1 502.7 502.3 501.8 501.4 501.0 500.6 500.1 499.7 499.3 498.8 498.4 + 498.0 497.5 497.1 496.7 496.2 495.8 495.4 494.9 494.5 494.1 493.6 493.2 492.7 492.3 491.9 491.4 491.0 490.5 + 490.1 489.6 489.2 488.7 488.3 487.8 487.4 486.9 486.5 486.0 485.6 485.1 484.7 484.2 483.8 483.3 482.8 482.4 + 481.9 481.5 ][ 358.9 359.3 359.8 360.2 360.7 361.1 361.6 362.0 362.5 363.0 363.4 363.9 364.3 364.8 365.2 365.7 + 366.1 366.6 367.0 367.4 367.9 368.3 368.8 369.2 369.7 370.1 370.5 371.0 371.4 371.9 372.3 372.7 373.2 373.6 + 374.0 374.5 374.9 375.3 375.8 376.2 376.6 377.0 377.5 377.9 378.3 378.7 379.2 379.6 380.0 380.4 380.8 381.3 + 381.7 382.1 382.5 382.9 383.3 383.7 384.2 384.6 385.0 385.4 385.8 386.2 386.6 387.0 387.4 387.8 388.2 388.6 + 389.0 389.4 389.8 390.2 390.6 391.0 391.4 391.8 392.2 392.6 393.0 393.4 393.8 394.1 394.5 394.9 395.3 395.7 + 396.1 396.4 396.8 397.2 397.6 398.0 398.3 398.7 399.1 399.5 399.8 400.2 400.6 400.9 401.3 401.7 402.0 402.4 + 402.8 403.1 403.5 403.8 404.2 404.6 404.9 405.3 405.6 406.0 406.3 406.7 407.0 407.4 407.7 408.1 408.4 408.8 + 409.1 409.5 409.8 410.1 410.5 410.8 411.2 411.5 411.8 412.2 412.5 412.8 413.2 413.5 413.8 414.1 414.5 414.8 + 415.1 415.4 415.7 416.1 416.4 416.7 417.0 417.3 417.6 418.0 418.3 418.6 418.9 419.2 419.5 419.8 420.1 420.4 + 420.7 421.0 421.3 421.6 421.9 422.2 422.5 422.8 423.1 423.4 423.6 423.9 424.2 424.5 424.8 425.1 425.4 425.6 + 425.9 426.2 426.5 426.7 427.0 427.3 427.6 427.8 428.1 428.4 428.6 428.9 429.1 429.4 429.7 429.9 430.2 430.4 + 430.7 431.0 431.2 431.5 ]plong + s[ 481.5 481.0 480.5 480.1 479.6 479.1 478.7 478.2 477.7 477.3 476.8 476.3 475.9 475.4 474.9 474.5 474.0 473.5 + 473.1 472.6 472.1 471.6 471.2 470.7 470.2 469.7 469.2 468.8 468.3 467.8 467.3 466.9 466.4 465.9 465.4 464.9 + 464.4 464.0 463.5 463.0 462.5 462.0 461.5 461.0 460.5 460.1 459.6 459.1 458.6 458.1 457.6 457.1 456.6 456.1 + 455.6 455.1 454.6 454.2 453.7 453.2 452.7 452.2 451.7 451.2 450.7 450.2 449.7 449.2 448.7 448.2 447.7 447.2 + 446.7 446.2 445.7 445.2 444.7 444.2 443.6 443.1 442.6 442.1 441.6 441.1 440.6 440.1 439.6 439.1 438.6 438.1 + 437.6 437.1 436.5 436.0 435.5 435.0 434.5 434.0 433.5 433.0 432.5 431.9 431.4 430.9 430.4 429.9 429.4 428.9 + 428.3 427.8 427.3 426.8 426.3 425.8 425.2 424.7 424.2 423.7 423.2 422.7 422.1 421.6 421.1 420.6 420.1 419.6 + 419.0 418.5 418.0 417.5 417.0 416.4 415.9 415.4 414.9 414.3 413.8 413.3 412.8 412.3 411.7 411.2 410.7 410.2 + 409.7 409.1 408.6 408.1 407.6 407.0 406.5 406.0 405.5 404.9 404.4 403.9 403.4 402.9 402.3 401.8 401.3 400.8 + 400.2 399.7 399.2 398.7 398.1 397.6 397.1 396.6 396.0 395.5 395.0 394.5 393.9 393.4 392.9 392.4 391.8 391.3 + 390.8 390.3 389.7 389.2 388.7 388.2 387.6 387.1 386.6 386.1 385.6 385.0 384.5 384.0 383.5 382.9 382.4 381.9 + 381.4 380.8 ][ 431.5 431.7 432.0 432.2 432.4 432.7 432.9 433.2 433.4 433.7 433.9 434.1 434.4 434.6 434.8 435.1 + 435.3 435.5 435.7 436.0 436.2 436.4 436.6 436.9 437.1 437.3 437.5 437.7 437.9 438.2 438.4 438.6 438.8 439.0 + 439.2 439.4 439.6 439.8 440.0 440.2 440.4 440.6 440.8 441.0 441.2 441.4 441.6 441.7 441.9 442.1 442.3 442.5 + 442.7 442.8 443.0 443.2 443.4 443.5 443.7 443.9 444.0 444.2 444.4 444.5 444.7 444.9 445.0 445.2 445.3 445.5 + 445.7 445.8 446.0 446.1 446.3 446.4 446.6 446.7 446.8 447.0 447.1 447.3 447.4 447.5 447.7 447.8 447.9 448.1 + 448.2 448.3 448.4 448.6 448.7 448.8 448.9 449.0 449.2 449.3 449.4 449.5 449.6 449.7 449.8 449.9 450.0 450.1 + 450.3 450.4 450.5 450.5 450.6 450.7 450.8 450.9 451.0 451.1 451.2 451.3 451.4 451.4 451.5 451.6 451.7 451.8 + 451.8 451.9 452.0 452.1 452.1 452.2 452.3 452.3 452.4 452.5 452.5 452.6 452.6 452.7 452.7 452.8 452.8 452.9 + 452.9 453.0 453.0 453.1 453.1 453.2 453.2 453.2 453.3 453.3 453.3 453.4 453.4 453.4 453.5 453.5 453.5 453.5 + 453.6 453.6 453.6 453.6 453.6 453.7 453.7 453.7 453.7 453.7 453.7 453.7 453.7 453.7 453.7 453.7 453.7 453.7 + 453.7 453.7 453.7 453.7 453.7 453.7 453.6 453.6 453.6 453.6 453.6 453.5 453.5 453.5 453.5 453.4 453.4 453.4 + 453.3 453.3 453.3 453.2 ]plong + s[ 380.8 380.3 379.8 379.3 378.8 378.2 377.7 377.2 376.7 376.1 375.6 375.1 374.6 374.1 373.5 373.0 372.5 372.0 + 371.4 370.9 370.4 369.9 369.4 368.8 368.3 367.8 367.3 366.8 366.3 365.7 365.2 364.7 364.2 363.7 363.1 362.6 + 362.1 361.6 361.1 360.6 360.1 359.5 359.0 358.5 358.0 357.5 357.0 356.5 355.9 355.4 354.9 354.4 353.9 353.4 + 352.9 352.4 351.8 351.3 350.8 350.3 349.8 349.3 348.8 348.3 347.8 347.3 346.8 346.3 345.7 345.2 344.7 344.2 + 343.7 343.2 342.7 342.2 341.7 341.2 340.7 340.2 339.7 339.2 338.7 338.2 337.7 337.2 336.7 336.2 335.7 335.2 + 334.7 334.2 333.7 333.2 332.7 332.2 331.8 331.3 330.8 330.3 329.8 329.3 328.8 328.3 327.8 327.3 326.8 326.4 + 325.9 325.4 324.9 324.4 323.9 323.4 323.0 322.5 322.0 321.5 321.0 320.6 320.1 319.6 319.1 318.6 318.2 317.7 + 317.2 316.7 316.3 315.8 315.3 314.8 314.4 313.9 313.4 312.9 312.5 312.0 311.5 311.1 310.6 310.1 309.7 309.2 + 308.7 308.3 307.8 307.3 306.9 306.4 306.0 305.5 305.0 304.6 304.1 303.7 303.2 302.8 302.3 301.9 301.4 300.9 + 300.5 300.0 299.6 299.1 298.7 298.3 297.8 297.4 296.9 296.5 296.0 295.6 295.1 294.7 294.3 293.8 293.4 292.9 + 292.5 292.1 291.6 291.2 290.8 290.3 289.9 289.5 289.0 288.6 288.2 287.8 287.3 286.9 286.5 286.1 285.6 285.2 + 284.8 284.4 ][ 453.2 453.2 453.2 453.1 453.1 453.0 453.0 452.9 452.9 452.8 452.8 452.7 452.7 452.6 452.6 452.5 + 452.5 452.4 452.3 452.3 452.2 452.1 452.1 452.0 451.9 451.8 451.8 451.7 451.6 451.5 451.4 451.4 451.3 451.2 + 451.1 451.0 450.9 450.8 450.7 450.6 450.5 450.5 450.4 450.3 450.1 450.0 449.9 449.8 449.7 449.6 449.5 449.4 + 449.3 449.2 449.0 448.9 448.8 448.7 448.6 448.4 448.3 448.2 448.1 447.9 447.8 447.7 447.5 447.4 447.3 447.1 + 447.0 446.8 446.7 446.6 446.4 446.3 446.1 446.0 445.8 445.7 445.5 445.3 445.2 445.0 444.9 444.7 444.5 444.4 + 444.2 444.0 443.9 443.7 443.5 443.4 443.2 443.0 442.8 442.7 442.5 442.3 442.1 441.9 441.7 441.6 441.4 441.2 + 441.0 440.8 440.6 440.4 440.2 440.0 439.8 439.6 439.4 439.2 439.0 438.8 438.6 438.4 438.2 437.9 437.7 437.5 + 437.3 437.1 436.9 436.6 436.4 436.2 436.0 435.7 435.5 435.3 435.1 434.8 434.6 434.4 434.1 433.9 433.7 433.4 + 433.2 432.9 432.7 432.4 432.2 432.0 431.7 431.5 431.2 431.0 430.7 430.4 430.2 429.9 429.7 429.4 429.1 428.9 + 428.6 428.4 428.1 427.8 427.6 427.3 427.0 426.7 426.5 426.2 425.9 425.6 425.4 425.1 424.8 424.5 424.2 423.9 + 423.6 423.4 423.1 422.8 422.5 422.2 421.9 421.6 421.3 421.0 420.7 420.4 420.1 419.8 419.5 419.2 418.9 418.6 + 418.3 418.0 417.6 417.3 ]plong + s[ 284.4 283.9 283.5 283.1 282.7 282.3 281.9 281.4 281.0 280.6 280.2 279.8 279.4 279.0 278.6 278.2 277.8 277.4 + 276.9 276.5 276.1 275.7 275.3 274.9 274.5 274.1 273.8 273.4 273.0 272.6 272.2 271.8 271.4 271.0 270.6 270.2 + 269.8 269.5 269.1 268.7 268.3 267.9 267.5 267.2 266.8 266.4 266.0 265.7 265.3 264.9 264.5 264.2 263.8 263.4 + 263.1 262.7 262.3 262.0 261.6 261.2 260.9 260.5 260.2 259.8 259.5 259.1 258.7 258.4 258.0 257.7 257.3 257.0 + 256.6 256.3 255.9 255.6 255.3 254.9 254.6 254.2 253.9 253.6 253.2 252.9 252.6 252.2 251.9 251.6 251.2 250.9 + 250.6 250.2 249.9 249.6 249.3 249.0 248.6 248.3 248.0 247.7 247.4 247.1 246.7 246.4 246.1 245.8 245.5 245.2 + 244.9 244.6 244.3 244.0 243.7 243.4 243.1 242.8 242.5 242.2 241.9 241.6 241.3 241.0 240.7 240.5 240.2 239.9 + 239.6 239.3 239.0 238.8 238.5 238.2 237.9 237.7 237.4 237.1 236.8 236.6 236.3 236.0 235.8 235.5 235.2 235.0 + 234.7 234.5 234.2 234.0 233.7 233.4 233.2 232.9 232.7 232.4 232.2 231.9 231.7 231.5 231.2 231.0 230.7 230.5 + 230.3 230.0 229.8 229.6 229.3 229.1 228.9 228.7 228.4 228.2 228.0 227.8 227.5 227.3 227.1 226.9 226.7 226.5 + 226.2 226.0 225.8 225.6 225.4 225.2 225.0 224.8 224.6 224.4 224.2 224.0 223.8 223.6 223.4 223.2 223.0 222.9 + 222.7 222.5 ][ 417.3 417.0 416.7 416.4 416.1 415.7 415.4 415.1 414.8 414.5 414.1 413.8 413.5 413.2 412.8 412.5 + 412.2 411.8 411.5 411.2 410.8 410.5 410.1 409.8 409.5 409.1 408.8 408.4 408.1 407.7 407.4 407.0 406.7 406.3 + 406.0 405.6 405.3 404.9 404.6 404.2 403.8 403.5 403.1 402.8 402.4 402.0 401.7 401.3 400.9 400.6 400.2 399.8 + 399.5 399.1 398.7 398.3 398.0 397.6 397.2 396.8 396.4 396.1 395.7 395.3 394.9 394.5 394.1 393.8 393.4 393.0 + 392.6 392.2 391.8 391.4 391.0 390.6 390.2 389.8 389.4 389.0 388.6 388.2 387.8 387.4 387.0 386.6 386.2 385.8 + 385.4 385.0 384.6 384.2 383.7 383.3 382.9 382.5 382.1 381.7 381.3 380.8 380.4 380.0 379.6 379.2 378.7 378.3 + 377.9 377.5 377.0 376.6 376.2 375.8 375.3 374.9 374.5 374.0 373.6 373.2 372.7 372.3 371.9 371.4 371.0 370.5 + 370.1 369.7 369.2 368.8 368.3 367.9 367.4 367.0 366.6 366.1 365.7 365.2 364.8 364.3 363.9 363.4 363.0 362.5 + 362.0 361.6 361.1 360.7 360.2 359.8 359.3 358.9 358.4 357.9 357.5 357.0 356.5 356.1 355.6 355.2 354.7 354.2 + 353.8 353.3 352.8 352.3 351.9 351.4 350.9 350.5 350.0 349.5 349.0 348.6 348.1 347.6 347.1 346.7 346.2 345.7 + 345.2 344.8 344.3 343.8 343.3 342.8 342.4 341.9 341.4 340.9 340.4 339.9 339.5 339.0 338.5 338.0 337.5 337.0 + 336.5 336.0 335.5 335.1 ]plong + s[ 222.5 222.3 222.1 221.9 221.8 221.6 221.4 221.2 221.1 220.9 220.7 220.5 220.4 220.2 220.0 219.9 219.7 219.5 + 219.4 219.2 219.1 218.9 218.8 218.6 218.5 218.3 218.2 218.0 217.9 217.7 217.6 217.4 217.3 217.2 217.0 216.9 + 216.8 216.6 216.5 216.4 216.2 216.1 216.0 215.9 215.7 215.6 215.5 215.4 215.3 215.1 215.0 214.9 214.8 214.7 + 214.6 214.5 214.4 214.3 214.2 214.1 214.0 213.9 213.8 213.7 213.6 213.5 213.4 213.3 213.2 213.2 213.1 213.0 + 212.9 212.8 212.7 212.7 212.6 212.5 212.4 212.4 212.3 212.2 212.2 212.1 212.0 212.0 211.9 211.9 211.8 211.8 + 211.7 211.6 211.6 211.5 211.5 211.4 211.4 211.4 211.3 211.3 211.2 211.2 211.2 211.1 211.1 211.1 211.0 211.0 + 211.0 210.9 210.9 210.9 210.9 210.9 210.8 210.8 210.8 210.8 210.8 210.8 210.8 210.8 210.7 210.7 210.7 210.7 + 210.7 210.7 210.7 210.7 210.7 210.8 210.8 210.8 210.8 210.8 210.8 210.8 210.8 210.9 210.9 210.9 210.9 211.0 + 211.0 211.0 211.0 211.1 211.1 211.1 211.2 211.2 211.3 211.3 211.3 211.4 211.4 211.5 211.5 211.6 211.6 211.7 + 211.7 211.8 211.8 211.9 211.9 212.0 212.1 212.1 212.2 212.3 212.3 212.4 212.5 212.5 212.6 212.7 212.8 212.9 + 212.9 213.0 213.1 213.2 213.3 213.4 213.4 213.5 213.6 213.7 213.8 213.9 214.0 214.1 214.2 214.3 214.4 214.5 + 214.6 214.7 ][ 335.1 334.6 334.1 333.6 333.1 332.6 332.1 331.6 331.1 330.6 330.1 329.6 329.1 328.6 328.1 327.6 + 327.1 326.6 326.1 325.6 325.1 324.6 324.1 323.6 323.1 322.6 322.1 321.6 321.1 320.6 320.1 319.6 319.1 318.6 + 318.1 317.6 317.1 316.6 316.1 315.6 315.0 314.5 314.0 313.5 313.0 312.5 312.0 311.5 311.0 310.5 309.9 309.4 + 308.9 308.4 307.9 307.4 306.9 306.4 305.8 305.3 304.8 304.3 303.8 303.3 302.7 302.2 301.7 301.2 300.7 300.2 + 299.6 299.1 298.6 298.1 297.6 297.1 296.5 296.0 295.5 295.0 294.5 293.9 293.4 292.9 292.4 291.9 291.3 290.8 + 290.3 289.8 289.2 288.7 288.2 287.7 287.2 286.6 286.1 285.6 285.1 284.5 284.0 283.5 283.0 282.5 281.9 281.4 + 280.9 280.4 279.8 279.3 278.8 278.3 277.7 277.2 276.7 276.2 275.6 275.1 274.6 274.1 273.5 273.0 272.5 272.0 + 271.4 270.9 270.4 269.9 269.4 268.8 268.3 267.8 267.3 266.7 266.2 265.7 265.2 264.6 264.1 263.6 263.1 262.5 + 262.0 261.5 261.0 260.4 259.9 259.4 258.9 258.3 257.8 257.3 256.8 256.3 255.7 255.2 254.7 254.2 253.6 253.1 + 252.6 252.1 251.6 251.0 250.5 250.0 249.5 249.0 248.4 247.9 247.4 246.9 246.4 245.8 245.3 244.8 244.3 243.8 + 243.2 242.7 242.2 241.7 241.2 240.6 240.1 239.6 239.1 238.6 238.1 237.5 237.0 236.5 236.0 235.5 235.0 234.5 + 233.9 233.4 232.9 232.4 ]plong + 215.2 230.4 215.1 230.9 215.0 231.4 214.9 231.9 214.7 232.4 5 pls + s[ 215.2 215.3 215.4 215.5 215.7 215.8 215.9 216.0 216.2 216.3 216.4 216.5 216.7 216.8 216.9 217.1 217.2 217.4 + 217.5 217.6 217.8 217.9 218.1 218.2 218.4 218.5 218.7 218.8 219.0 219.1 219.3 219.5 219.6 219.8 219.9 220.1 + 220.3 220.4 220.6 220.8 220.9 221.1 221.3 221.5 221.6 221.8 222.0 222.2 222.4 222.6 222.7 222.9 223.1 223.3 + 223.5 223.7 223.9 224.1 224.3 224.5 224.7 224.9 225.1 225.3 225.5 225.7 225.9 226.1 226.3 226.5 226.8 227.0 + 227.2 227.4 227.6 227.8 228.1 228.3 228.5 228.7 229.0 229.2 229.4 229.7 229.9 230.1 230.4 230.6 230.8 231.1 + 231.3 231.6 231.8 232.0 232.3 232.5 232.8 233.0 233.3 233.5 233.8 234.1 234.3 234.6 234.8 235.1 235.4 235.6 + 235.9 236.1 236.4 236.7 236.9 237.2 237.5 237.8 238.0 238.3 238.6 238.9 239.2 239.4 239.7 240.0 240.3 240.6 + 240.9 241.1 241.4 241.7 242.0 242.3 242.6 242.9 243.2 243.5 243.8 244.1 244.4 244.7 245.0 245.3 245.6 245.9 + 246.2 246.6 246.9 247.2 247.5 247.8 248.1 248.4 248.8 249.1 249.4 249.7 250.1 250.4 250.7 251.0 251.4 251.7 + 252.0 252.4 252.7 253.0 253.4 253.7 254.0 254.4 254.7 255.1 255.4 255.7 256.1 256.4 256.8 257.1 257.5 257.8 + 258.2 258.5 258.9 259.2 259.6 260.0 260.3 260.7 261.0 261.4 261.8 262.1 262.5 262.8 263.2 263.6 264.0 264.3 + 264.7 265.1 ][ 230.4 229.8 229.3 228.8 228.3 227.8 227.3 226.8 226.3 225.8 225.3 224.7 224.2 223.7 223.2 222.7 + 222.2 221.7 221.2 220.7 220.2 219.7 219.2 218.7 218.2 217.7 217.2 216.7 216.2 215.7 215.2 214.7 214.2 213.7 + 213.2 212.7 212.2 211.7 211.2 210.7 210.2 209.7 209.2 208.7 208.2 207.7 207.2 206.7 206.3 205.8 205.3 204.8 + 204.3 203.8 203.3 202.8 202.3 201.9 201.4 200.9 200.4 199.9 199.4 198.9 198.5 198.0 197.5 197.0 196.5 196.1 + 195.6 195.1 194.6 194.1 193.7 193.2 192.7 192.2 191.8 191.3 190.8 190.4 189.9 189.4 188.9 188.5 188.0 187.5 + 187.1 186.6 186.1 185.7 185.2 184.7 184.3 183.8 183.4 182.9 182.4 182.0 181.5 181.1 180.6 180.1 179.7 179.2 + 178.8 178.3 177.9 177.4 177.0 176.5 176.1 175.6 175.2 174.7 174.3 173.8 173.4 172.9 172.5 172.1 171.6 171.2 + 170.7 170.3 169.9 169.4 169.0 168.5 168.1 167.7 167.2 166.8 166.4 165.9 165.5 165.1 164.7 164.2 163.8 163.4 + 163.0 162.5 162.1 161.7 161.3 160.8 160.4 160.0 159.6 159.2 158.8 158.3 157.9 157.5 157.1 156.7 156.3 155.9 + 155.5 155.1 154.6 154.2 153.8 153.4 153.0 152.6 152.2 151.8 151.4 151.0 150.6 150.2 149.8 149.4 149.1 148.7 + 148.3 147.9 147.5 147.1 146.7 146.3 145.9 145.6 145.2 144.8 144.4 144.0 143.7 143.3 142.9 142.5 142.2 141.8 + 141.4 141.0 140.7 140.3 ]plong + s[ 265.1 265.4 265.8 266.2 266.6 266.9 267.3 267.7 268.1 268.5 268.8 269.2 269.6 270.0 270.4 270.8 271.2 271.6 + 271.9 272.3 272.7 273.1 273.5 273.9 274.3 274.7 275.1 275.5 275.9 276.3 276.7 277.1 277.5 277.9 278.3 278.7 + 279.1 279.6 280.0 280.4 280.8 281.2 281.6 282.0 282.4 282.9 283.3 283.7 284.1 284.5 285.0 285.4 285.8 286.2 + 286.6 287.1 287.5 287.9 288.4 288.8 289.2 289.6 290.1 290.5 290.9 291.4 291.8 292.2 292.7 293.1 293.6 294.0 + 294.4 294.9 295.3 295.8 296.2 296.6 297.1 297.5 298.0 298.4 298.9 299.3 299.8 300.2 300.7 301.1 301.6 302.0 + 302.5 302.9 303.4 303.9 304.3 304.8 305.2 305.7 306.1 306.6 307.1 307.5 308.0 308.5 308.9 309.4 309.9 310.3 + 310.8 311.3 311.7 312.2 312.7 313.1 313.6 314.1 314.5 315.0 315.5 316.0 316.4 316.9 317.4 317.9 318.3 318.8 + 319.3 319.8 320.3 320.7 321.2 321.7 322.2 322.7 323.2 323.6 324.1 324.6 325.1 325.6 326.1 326.6 327.0 327.5 + 328.0 328.5 329.0 329.5 330.0 330.5 331.0 331.5 331.9 332.4 332.9 333.4 333.9 334.4 334.9 335.4 335.9 336.4 + 336.9 337.4 337.9 338.4 338.9 339.4 339.9 340.4 340.9 341.4 341.9 342.4 342.9 343.4 343.9 344.4 344.9 345.4 + 345.9 346.5 347.0 347.5 348.0 348.5 349.0 349.5 350.0 350.5 351.0 351.5 352.0 352.6 353.1 353.6 354.1 354.6 + 355.1 355.6 ][ 140.3 139.9 139.6 139.2 138.8 138.5 138.1 137.7 137.4 137.0 136.7 136.3 135.9 135.6 135.2 134.9 + 134.5 134.2 133.8 133.5 133.1 132.8 132.4 132.1 131.8 131.4 131.1 130.7 130.4 130.1 129.7 129.4 129.1 128.7 + 128.4 128.1 127.7 127.4 127.1 126.7 126.4 126.1 125.8 125.5 125.1 124.8 124.5 124.2 123.9 123.6 123.2 122.9 + 122.6 122.3 122.0 121.7 121.4 121.1 120.8 120.5 120.2 119.9 119.6 119.3 119.0 118.7 118.4 118.1 117.8 117.5 + 117.3 117.0 116.7 116.4 116.1 115.8 115.5 115.3 115.0 114.7 114.4 114.2 113.9 113.6 113.4 113.1 112.8 112.5 + 112.3 112.0 111.8 111.5 111.2 111.0 110.7 110.5 110.2 110.0 109.7 109.5 109.2 109.0 108.7 108.5 108.2 108.0 + 107.7 107.5 107.3 107.0 106.8 106.6 106.3 106.1 105.9 105.6 105.4 105.2 104.9 104.7 104.5 104.3 104.1 103.8 + 103.6 103.4 103.2 103.0 102.8 102.6 102.4 102.1 101.9 101.7 101.5 101.3 101.1 100.9 100.7 100.5 100.3 100.1 + 100.0 99.8 99.6 99.4 99.2 99.0 98.8 98.6 98.5 98.3 98.1 97.9 97.8 97.6 97.4 97.2 97.1 96.9 + 96.7 96.6 96.4 96.2 96.1 95.9 95.8 95.6 95.5 95.3 95.1 95.0 94.8 94.7 94.5 94.4 94.3 94.1 + 94.0 93.8 93.7 93.6 93.4 93.3 93.2 93.0 92.9 92.8 92.6 92.5 92.4 92.3 92.2 92.0 91.9 91.8 + 91.7 91.6 91.5 91.4 ]plong + s[ 355.6 356.1 356.7 357.2 357.7 358.2 358.7 359.2 359.7 360.3 360.8 361.3 361.8 362.3 362.8 363.4 363.9 364.4 + 364.9 365.4 365.9 366.5 367.0 367.5 368.0 368.5 369.1 369.6 370.1 370.6 371.1 371.7 372.2 372.7 373.2 373.7 + 374.3 374.8 375.3 375.8 376.3 376.9 377.4 377.9 378.4 379.0 379.5 380.0 380.5 381.1 381.6 382.1 382.6 383.1 + 383.7 384.2 384.7 385.2 385.8 386.3 386.8 387.3 387.9 388.4 388.9 389.4 390.0 390.5 391.0 391.5 392.1 392.6 + 393.1 393.6 394.2 394.7 395.2 395.7 396.2 396.8 397.3 397.8 398.3 398.9 399.4 399.9 400.4 401.0 401.5 402.0 + 402.5 403.1 403.6 404.1 404.6 405.2 405.7 406.2 406.7 407.2 407.8 408.3 408.8 409.3 409.9 410.4 410.9 411.4 + 412.0 412.5 413.0 413.5 414.0 414.6 415.1 415.6 416.1 416.6 417.2 417.7 418.2 418.7 419.2 419.8 420.3 420.8 + 421.3 421.8 422.4 422.9 423.4 423.9 424.4 424.9 425.5 426.0 426.5 427.0 427.5 428.0 428.6 429.1 429.6 430.1 + 430.6 431.1 431.6 432.1 432.7 433.2 433.7 434.2 434.7 435.2 435.7 436.2 436.7 437.3 437.8 438.3 438.8 439.3 + 439.8 440.3 440.8 441.3 441.8 442.3 442.8 443.3 443.9 444.4 444.9 445.4 445.9 446.4 446.9 447.4 447.9 448.4 + 448.9 449.4 449.9 450.4 450.9 451.4 451.9 452.4 452.9 453.4 453.9 454.4 454.8 455.3 455.8 456.3 456.8 457.3 + 457.8 458.3 ][ 91.4 91.2 91.1 91.0 90.9 90.8 90.7 90.6 90.5 90.4 90.3 90.2 90.1 90.0 90.0 89.9 + 89.8 89.7 89.6 89.5 89.5 89.4 89.3 89.2 89.1 89.1 89.0 88.9 88.9 88.8 88.7 88.7 88.6 88.5 + 88.5 88.4 88.4 88.3 88.3 88.2 88.1 88.1 88.0 88.0 88.0 87.9 87.9 87.8 87.8 87.8 87.7 87.7 + 87.7 87.6 87.6 87.6 87.5 87.5 87.5 87.5 87.4 87.4 87.4 87.4 87.4 87.4 87.3 87.3 87.3 87.3 + 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.3 87.4 87.4 87.4 87.4 87.4 87.4 + 87.5 87.5 87.5 87.5 87.6 87.6 87.6 87.6 87.7 87.7 87.7 87.8 87.8 87.9 87.9 87.9 88.0 88.0 + 88.1 88.1 88.2 88.2 88.3 88.3 88.4 88.5 88.5 88.6 88.6 88.7 88.8 88.8 88.9 89.0 89.1 89.1 + 89.2 89.3 89.4 89.4 89.5 89.6 89.7 89.8 89.9 89.9 90.0 90.1 90.2 90.3 90.4 90.5 90.6 90.7 + 90.8 90.9 91.0 91.1 91.2 91.3 91.4 91.6 91.7 91.8 91.9 92.0 92.1 92.3 92.4 92.5 92.6 92.7 + 92.9 93.0 93.1 93.3 93.4 93.5 93.7 93.8 93.9 94.1 94.2 94.4 94.5 94.7 94.8 95.0 95.1 95.3 + 95.4 95.6 95.7 95.9 96.0 96.2 96.4 96.5 96.7 96.9 97.0 97.2 97.4 97.5 97.7 97.9 98.1 98.2 + 98.4 98.6 98.8 99.0 ]plong + s[ 458.3 458.8 459.3 459.8 460.3 460.7 461.2 461.7 462.2 462.7 463.2 463.7 464.1 464.6 465.1 465.6 466.1 466.6 + 467.0 467.5 468.0 468.5 469.0 469.4 469.9 470.4 470.9 471.3 471.8 472.3 472.8 473.2 473.7 474.2 474.7 475.1 + 475.6 476.1 476.5 477.0 477.5 477.9 478.4 478.9 479.3 479.8 480.3 480.7 481.2 481.6 482.1 482.6 483.0 483.5 + 483.9 484.4 484.8 485.3 485.8 486.2 486.7 487.1 487.6 488.0 488.5 488.9 489.4 489.8 490.3 490.7 491.1 491.6 + 492.0 492.5 492.9 493.4 493.8 494.2 494.7 495.1 495.5 496.0 496.4 496.9 497.3 497.7 498.2 498.6 499.0 499.4 + 499.9 500.3 500.7 501.2 501.6 502.0 502.4 502.8 503.3 503.7 504.1 504.5 504.9 505.4 505.8 506.2 506.6 507.0 + 507.4 507.8 508.2 508.7 509.1 509.5 509.9 510.3 510.7 511.1 511.5 511.9 512.3 512.7 513.1 513.5 513.9 514.3 + 514.7 515.1 515.5 515.9 516.3 516.6 517.0 517.4 517.8 518.2 518.6 519.0 519.3 519.7 520.1 520.5 520.9 521.2 + 521.6 522.0 522.4 522.7 523.1 523.5 ][ 99.0 99.2 99.3 99.5 99.7 99.9 100.1 100.3 100.5 100.7 100.9 101.1 + 101.3 101.5 101.7 101.9 102.1 102.3 102.5 102.7 102.9 103.2 103.4 103.6 103.8 104.0 104.2 104.5 104.7 104.9 + 105.1 105.4 105.6 105.8 106.0 106.3 106.5 106.7 107.0 107.2 107.5 107.7 107.9 108.2 108.4 108.7 108.9 109.2 + 109.4 109.7 109.9 110.2 110.4 110.7 110.9 111.2 111.4 111.7 112.0 112.2 112.5 112.8 113.0 113.3 113.6 113.8 + 114.1 114.4 114.7 114.9 115.2 115.5 115.8 116.1 116.3 116.6 116.9 117.2 117.5 117.8 118.1 118.4 118.6 118.9 + 119.2 119.5 119.8 120.1 120.4 120.7 121.0 121.3 121.6 121.9 122.3 122.6 122.9 123.2 123.5 123.8 124.1 124.4 + 124.8 125.1 125.4 125.7 126.0 126.4 126.7 127.0 127.3 127.7 128.0 128.3 128.7 129.0 129.3 129.7 130.0 130.3 + 130.7 131.0 131.3 131.7 132.0 132.4 132.7 133.1 133.4 133.8 134.1 134.5 134.8 135.2 135.5 135.9 136.2 136.6 + 136.9 137.3 137.7 138.0 138.4 138.8 139.1 139.5 139.9 140.2 140.6 141.0 ]plong + s[ 254.2 254.6 255.0 255.4 255.8 256.3 256.7 257.1 257.5 257.9 258.3 258.7 259.1 259.5 259.9 260.4 260.8 261.2 + 261.6 262.0 262.5 262.9 263.3 263.7 264.2 264.6 265.0 265.4 265.9 266.3 266.7 267.2 267.6 268.0 268.5 268.9 + 269.3 269.8 270.2 270.7 271.1 271.6 272.0 272.4 272.9 273.3 273.8 274.2 274.7 275.1 275.6 276.0 276.5 277.0 + 277.4 277.9 278.3 278.8 279.2 279.7 280.2 280.6 281.1 281.6 282.0 282.5 283.0 283.4 283.9 284.4 284.8 285.3 + 285.8 286.3 286.7 287.2 287.7 288.2 288.6 289.1 289.6 290.1 290.6 291.0 291.5 292.0 292.5 293.0 293.5 293.9 + 294.4 294.9 295.4 295.9 296.4 296.9 297.4 297.9 298.4 298.9 299.4 299.9 300.4 300.9 301.4 301.9 302.4 302.9 + 303.4 303.9 304.4 304.9 305.4 305.9 306.4 306.9 307.4 307.9 308.4 308.9 309.4 310.0 310.5 311.0 311.5 312.0 + 312.5 313.0 313.6 314.1 314.6 315.1 315.6 316.1 316.7 317.2 317.7 318.2 318.8 319.3 319.8 320.3 320.8 321.4 + 321.9 322.4 323.0 323.5 324.0 324.5 325.1 325.6 326.1 326.7 327.2 327.7 328.3 328.8 329.3 329.9 330.4 330.9 + 331.5 332.0 332.5 333.1 333.6 334.2 334.7 335.2 335.8 336.3 336.9 337.4 337.9 338.5 339.0 339.6 340.1 340.7 + 341.2 341.8 342.3 342.8 343.4 343.9 344.5 345.0 345.6 346.1 346.7 347.2 347.8 348.3 348.9 349.4 350.0 350.5 + 351.1 351.6 ][ 130.8 130.4 130.0 129.6 129.2 128.8 128.4 128.0 127.6 127.3 126.9 126.5 126.1 125.7 125.3 124.9 + 124.6 124.2 123.8 123.4 123.0 122.7 122.3 121.9 121.5 121.2 120.8 120.4 120.1 119.7 119.3 119.0 118.6 118.2 + 117.9 117.5 117.2 116.8 116.5 116.1 115.8 115.4 115.1 114.7 114.4 114.0 113.7 113.3 113.0 112.6 112.3 112.0 + 111.6 111.3 111.0 110.6 110.3 110.0 109.6 109.3 109.0 108.7 108.3 108.0 107.7 107.4 107.1 106.7 106.4 106.1 + 105.8 105.5 105.2 104.9 104.6 104.2 103.9 103.6 103.3 103.0 102.7 102.4 102.1 101.8 101.6 101.3 101.0 100.7 + 100.4 100.1 99.8 99.5 99.3 99.0 98.7 98.4 98.1 97.9 97.6 97.3 97.0 96.8 96.5 96.2 96.0 95.7 + 95.4 95.2 94.9 94.7 94.4 94.2 93.9 93.6 93.4 93.1 92.9 92.6 92.4 92.2 91.9 91.7 91.4 91.2 + 91.0 90.7 90.5 90.3 90.0 89.8 89.6 89.3 89.1 88.9 88.7 88.5 88.2 88.0 87.8 87.6 87.4 87.2 + 87.0 86.7 86.5 86.3 86.1 85.9 85.7 85.5 85.3 85.1 84.9 84.7 84.6 84.4 84.2 84.0 83.8 83.6 + 83.4 83.3 83.1 82.9 82.7 82.5 82.4 82.2 82.0 81.9 81.7 81.5 81.4 81.2 81.0 80.9 80.7 80.6 + 80.4 80.3 80.1 80.0 79.8 79.7 79.5 79.4 79.2 79.1 78.9 78.8 78.7 78.5 78.4 78.3 78.1 78.0 + 77.9 77.8 77.6 77.5 ]plong + s[ 351.6 352.2 352.7 353.3 353.8 354.4 355.0 355.5 356.1 356.6 357.2 357.7 358.3 358.8 359.2 ][ 77.5 77.4 77.3 + 77.2 77.0 76.9 76.8 76.7 76.6 76.5 76.4 76.3 76.2 76.1 76.0 ]plong + s[ 428.7 429.2 429.7 430.3 430.8 431.4 431.9 432.5 433.0 433.6 434.1 434.7 435.3 435.8 436.4 436.9 437.5 438.0 + 438.6 439.1 439.7 440.2 440.8 441.3 441.9 442.4 443.0 443.5 444.1 444.6 445.1 445.7 446.2 446.8 447.3 447.9 + 448.4 449.0 449.5 450.0 450.6 451.1 451.7 452.2 452.8 453.3 453.8 454.4 454.9 455.4 456.0 456.5 457.1 457.6 + 458.1 458.7 459.2 459.7 460.3 460.8 461.3 461.9 462.4 462.9 463.4 464.0 464.5 465.0 465.6 466.1 466.6 467.1 + 467.7 468.2 468.7 469.2 469.8 470.3 470.8 471.3 471.8 472.4 472.9 473.4 473.9 474.4 474.9 475.5 476.0 476.5 + 477.0 477.5 478.0 478.5 479.1 479.6 480.1 480.6 481.1 481.6 482.1 482.6 483.1 483.6 484.1 484.6 485.1 485.6 + 486.1 486.6 487.1 487.6 488.1 488.6 489.1 489.6 490.1 490.6 491.1 491.6 492.1 492.6 493.1 493.5 494.0 494.5 + 495.0 495.5 496.0 496.5 496.9 497.4 497.9 498.4 498.9 499.3 499.8 500.3 500.8 501.2 501.7 502.2 502.7 503.1 + 503.6 504.1 504.6 505.0 505.5 506.0 506.4 506.9 507.3 507.8 508.3 508.7 509.2 509.6 510.1 510.6 511.0 511.5 + 511.9 512.4 512.8 513.3 513.7 514.2 514.6 515.1 515.5 516.0 516.4 516.9 517.3 517.7 518.2 518.6 519.1 519.5 + 519.9 520.4 520.8 521.2 521.7 522.1 522.5 523.0 523.4 523.8 524.2 524.7 525.1 525.5 525.9 526.3 526.8 527.2 + 527.6 528.0 ][ 76.0 76.1 76.2 76.3 76.4 76.5 76.6 76.7 76.8 77.0 77.1 77.2 77.3 77.4 77.5 77.7 + 77.8 77.9 78.0 78.2 78.3 78.4 78.6 78.7 78.8 79.0 79.1 79.3 79.4 79.5 79.7 79.8 80.0 80.1 + 80.3 80.4 80.6 80.8 80.9 81.1 81.2 81.4 81.6 81.7 81.9 82.1 82.2 82.4 82.6 82.8 82.9 83.1 + 83.3 83.5 83.7 83.8 84.0 84.2 84.4 84.6 84.8 85.0 85.2 85.4 85.6 85.8 86.0 86.2 86.4 86.6 + 86.8 87.0 87.2 87.4 87.6 87.8 88.1 88.3 88.5 88.7 88.9 89.2 89.4 89.6 89.8 90.1 90.3 90.5 + 90.8 91.0 91.2 91.5 91.7 92.0 92.2 92.5 92.7 92.9 93.2 93.4 93.7 93.9 94.2 94.5 94.7 95.0 + 95.2 95.5 95.8 96.0 96.3 96.6 96.8 97.1 97.4 97.6 97.9 98.2 98.5 98.7 99.0 99.3 99.6 99.9 + 100.2 100.5 100.7 101.0 101.3 101.6 101.9 102.2 102.5 102.8 103.1 103.4 103.7 104.0 104.3 104.6 104.9 105.2 + 105.5 105.9 106.2 106.5 106.8 107.1 107.4 107.8 108.1 108.4 108.7 109.0 109.4 109.7 110.0 110.4 110.7 111.0 + 111.4 111.7 112.0 112.4 112.7 113.1 113.4 113.7 114.1 114.4 114.8 115.1 115.5 115.8 116.2 116.5 116.9 117.2 + 117.6 118.0 118.3 118.7 119.0 119.4 119.8 120.1 120.5 120.9 121.2 121.6 122.0 122.4 122.7 123.1 123.5 123.9 + 124.2 124.6 125.0 125.4 ]plong + s[ 528.0 528.4 528.8 529.3 529.7 530.1 530.5 530.9 531.3 531.7 532.1 532.5 532.9 533.3 533.7 534.1 534.5 534.9 + 535.3 535.7 536.1 536.5 536.9 537.3 537.7 538.1 538.4 538.8 539.2 539.6 540.0 540.4 540.7 541.1 541.5 541.9 + 542.2 542.6 543.0 543.3 543.7 544.1 544.5 544.8 545.2 545.5 545.9 546.3 546.6 547.0 547.3 547.7 548.1 548.4 + 548.8 549.1 549.5 549.8 550.2 550.5 550.8 551.2 551.5 551.9 552.2 552.5 552.9 553.2 553.6 553.9 554.2 554.5 + 554.9 555.2 555.5 555.9 556.2 556.5 556.8 557.1 557.5 557.8 558.1 558.4 558.7 559.0 559.3 559.6 560.0 560.3 + 560.6 560.9 561.2 561.5 561.8 562.1 562.4 562.7 563.0 563.2 563.5 563.8 564.1 564.4 564.7 565.0 565.2 565.5 + 565.8 566.1 566.4 566.6 566.9 567.2 567.5 567.7 568.0 568.3 568.5 568.8 569.1 569.3 569.6 569.8 570.1 570.3 + 570.6 570.9 571.1 571.4 571.6 571.8 572.1 572.3 572.6 572.8 573.1 573.3 573.5 573.8 574.0 574.2 574.5 574.7 + 574.9 575.1 575.4 575.6 575.8 576.0 576.3 576.5 576.7 576.9 577.1 577.3 577.5 577.7 578.0 578.2 578.4 578.6 + 578.8 579.0 579.2 579.4 579.5 579.7 579.9 580.1 580.3 580.5 580.7 580.9 581.1 581.2 581.4 581.6 581.8 581.9 + 582.1 582.3 582.5 582.6 582.8 583.0 583.1 583.3 583.4 583.6 583.8 583.9 584.1 584.2 584.4 584.5 584.7 584.8 + 585.0 585.1 ][ 125.4 125.8 126.2 126.6 126.9 127.3 127.7 128.1 128.5 128.9 129.3 129.7 130.1 130.5 130.9 131.3 + 131.7 132.1 132.5 132.9 133.3 133.7 134.1 134.5 134.9 135.4 135.8 136.2 136.6 137.0 137.4 137.8 138.3 138.7 + 139.1 139.5 140.0 140.4 140.8 141.2 141.7 142.1 142.5 143.0 143.4 143.8 144.3 144.7 145.1 145.6 146.0 146.4 + 146.9 147.3 147.8 148.2 148.7 149.1 149.5 150.0 150.4 150.9 151.3 151.8 152.2 152.7 153.2 153.6 154.1 154.5 + 155.0 155.4 155.9 156.4 156.8 157.3 157.8 158.2 158.7 159.1 159.6 160.1 160.6 161.0 161.5 162.0 162.4 162.9 + 163.4 163.9 164.3 164.8 165.3 165.8 166.3 166.7 167.2 167.7 168.2 168.7 169.2 169.6 170.1 170.6 171.1 171.6 + 172.1 172.6 173.1 173.6 174.0 174.5 175.0 175.5 176.0 176.5 177.0 177.5 178.0 178.5 179.0 179.5 180.0 180.5 + 181.0 181.5 182.0 182.5 183.1 183.6 184.1 184.6 185.1 185.6 186.1 186.6 187.1 187.6 188.2 188.7 189.2 189.7 + 190.2 190.7 191.3 191.8 192.3 192.8 193.3 193.9 194.4 194.9 195.4 195.9 196.5 197.0 197.5 198.0 198.6 199.1 + 199.6 200.2 200.7 201.2 201.7 202.3 202.8 203.3 203.9 204.4 204.9 205.5 206.0 206.5 207.1 207.6 208.1 208.7 + 209.2 209.8 210.3 210.8 211.4 211.9 212.5 213.0 213.5 214.1 214.6 215.2 215.7 216.2 216.8 217.3 217.9 218.4 + 219.0 219.5 220.1 220.6 ]plong + s[ 585.1 585.2 585.4 585.5 585.7 585.8 585.9 586.1 586.2 586.3 586.5 586.6 586.7 586.8 587.0 587.1 587.2 587.3 + 587.4 587.5 587.6 587.8 587.9 588.0 588.1 588.2 588.3 588.4 588.5 588.6 588.7 588.8 588.9 589.0 589.1 589.1 + 589.2 589.3 589.4 589.5 589.6 589.6 589.7 589.8 589.9 589.9 590.0 590.1 590.1 590.2 590.3 590.3 590.4 590.4 ][ + 220.6 221.2 221.7 222.3 222.8 223.4 223.9 224.4 225.0 225.6 226.1 226.7 227.2 227.8 228.3 228.9 229.4 230.0 + 230.5 231.1 231.6 232.2 232.7 233.3 233.8 234.4 235.0 235.5 236.1 236.6 237.2 237.7 238.3 238.9 239.4 240.0 + 240.5 241.1 241.7 242.2 242.8 243.3 243.9 244.5 245.0 245.6 246.1 246.7 247.3 247.8 248.4 248.9 249.5 249.5 ]plong + s[ 590.4 590.4 590.3 590.2 590.2 590.1 590.0 590.0 589.9 589.8 589.7 589.7 589.6 589.5 589.4 589.3 589.3 589.2 + 589.1 589.0 588.9 588.8 588.7 588.6 588.5 588.4 588.3 588.2 588.1 588.0 587.9 587.8 587.7 587.6 587.5 587.4 + 587.2 587.1 587.0 586.9 586.8 586.6 586.5 586.4 586.3 586.1 586.0 585.9 585.7 585.6 585.4 585.3 585.2 585.0 + 584.9 584.7 584.6 584.4 584.3 584.1 584.0 583.8 583.7 583.5 583.3 583.2 583.0 582.9 582.7 582.5 582.4 582.2 + 582.0 581.8 581.7 581.5 581.3 581.1 580.9 580.8 580.6 580.4 580.2 580.0 579.8 579.6 579.4 579.2 579.0 578.8 + 578.6 578.4 578.2 578.0 577.8 577.6 577.4 577.2 577.0 576.8 576.6 576.3 576.1 575.9 575.7 575.5 575.2 575.0 + 574.8 574.6 574.3 574.1 573.9 573.6 573.4 573.2 572.9 572.7 572.4 572.2 571.9 571.7 571.5 571.2 571.0 570.7 + 570.4 570.2 569.9 569.7 569.4 569.2 568.9 568.6 568.4 568.1 567.8 567.6 567.3 567.0 566.7 566.5 566.2 565.9 + 565.6 565.4 565.1 564.8 564.5 564.2 563.9 563.6 563.4 563.1 562.8 562.5 562.2 561.9 561.6 561.3 561.0 560.7 + 560.4 560.1 559.8 559.5 559.2 558.8 558.5 558.2 557.9 557.6 557.3 556.9 556.6 556.3 556.0 555.7 555.3 555.0 + 554.7 554.3 554.0 553.7 553.4 553.0 552.7 552.3 552.0 551.7 551.3 551.0 550.6 550.3 549.9 549.6 549.3 548.9 + 548.5 548.2 ][ 291.5 291.8 292.4 293.0 293.5 294.1 294.6 295.2 295.8 296.3 296.9 297.5 298.0 298.6 299.1 299.7 + 300.3 300.8 301.4 301.9 302.5 303.0 303.6 304.2 304.7 305.3 305.8 306.4 306.9 307.5 308.1 308.6 309.2 309.7 + 310.3 310.8 311.4 311.9 312.5 313.0 313.6 314.1 314.7 315.2 315.8 316.3 316.9 317.4 318.0 318.5 319.1 319.6 + 320.2 320.7 321.3 321.8 322.4 322.9 323.5 324.0 324.5 325.1 325.6 326.2 326.7 327.3 327.8 328.3 328.9 329.4 + 330.0 330.5 331.0 331.6 332.1 332.7 333.2 333.7 334.3 334.8 335.3 335.9 336.4 336.9 337.5 338.0 338.5 339.1 + 339.6 340.1 340.6 341.2 341.7 342.2 342.8 343.3 343.8 344.3 344.9 345.4 345.9 346.4 346.9 347.5 348.0 348.5 + 349.0 349.5 350.1 350.6 351.1 351.6 352.1 352.6 353.2 353.7 354.2 354.7 355.2 355.7 356.2 356.7 357.2 357.8 + 358.3 358.8 359.3 359.8 360.3 360.8 361.3 361.8 362.3 362.8 363.3 363.8 364.3 364.8 365.3 365.8 366.3 366.8 + 367.3 367.8 368.2 368.7 369.2 369.7 370.2 370.7 371.2 371.7 372.2 372.6 373.1 373.6 374.1 374.6 375.0 375.5 + 376.0 376.5 377.0 377.4 377.9 378.4 378.9 379.3 379.8 380.3 380.7 381.2 381.7 382.1 382.6 383.1 383.5 384.0 + 384.5 384.9 385.4 385.8 386.3 386.8 387.2 387.7 388.1 388.6 389.0 389.5 389.9 390.4 390.8 391.3 391.7 392.2 + 392.6 393.1 393.5 394.0 ]plong + s[ 548.2 547.8 547.5 547.1 546.8 546.4 546.1 545.7 545.3 545.0 544.6 544.2 543.9 543.5 543.1 542.8 542.4 542.0 + 541.6 541.3 540.9 540.5 540.1 539.7 539.4 539.0 538.6 538.2 537.8 537.4 537.0 536.7 536.3 535.9 535.5 535.1 + 534.7 534.3 533.9 533.5 533.1 532.7 532.3 531.9 531.5 531.1 530.7 530.2 529.8 529.4 529.0 528.6 528.2 527.8 + 527.4 526.9 526.5 526.1 525.7 525.3 524.8 524.4 524.0 523.6 523.1 522.7 522.3 521.8 521.4 521.0 520.5 520.1 + 519.7 519.2 518.8 518.4 517.9 517.5 517.0 516.6 516.1 515.7 515.3 514.8 514.4 513.9 513.5 513.0 512.6 512.1 + 511.7 511.2 510.7 510.3 509.8 509.4 508.9 508.5 508.0 507.5 507.1 506.6 506.1 505.7 505.2 504.7 504.3 503.8 + 503.3 502.9 502.4 501.9 501.4 501.0 500.5 500.0 499.5 499.1 498.6 498.1 497.6 497.1 496.7 496.2 495.7 495.2 + 494.7 494.2 493.7 493.2 492.8 492.3 491.8 491.3 490.8 490.3 489.8 489.3 488.8 488.3 487.8 487.3 486.8 486.3 + 485.8 485.3 484.8 484.3 483.8 483.3 482.8 482.3 481.8 481.3 480.8 480.3 479.8 479.3 478.7 478.2 477.7 477.2 + 476.7 476.2 475.7 475.2 474.6 474.1 473.6 473.1 472.6 472.0 471.5 471.0 470.5 470.0 469.4 468.9 468.4 467.9 + 467.3 466.8 466.3 465.8 465.2 464.7 464.2 463.7 463.1 462.6 462.1 461.5 461.0 460.5 459.9 459.4 458.9 458.3 + 457.8 457.3 ][ 394.0 394.4 394.8 395.3 395.7 396.1 396.6 397.0 397.5 397.9 398.3 398.7 399.2 399.6 400.0 400.5 + 400.9 401.3 401.7 402.2 402.6 403.0 403.4 403.8 404.3 404.7 405.1 405.5 405.9 406.3 406.7 407.1 407.5 408.0 + 408.4 408.8 409.2 409.6 410.0 410.4 410.8 411.2 411.6 412.0 412.4 412.7 413.1 413.5 413.9 414.3 414.7 415.1 + 415.5 415.8 416.2 416.6 417.0 417.4 417.7 418.1 418.5 418.9 419.2 419.6 420.0 420.4 420.7 421.1 421.5 421.8 + 422.2 422.5 422.9 423.3 423.6 424.0 424.3 424.7 425.0 425.4 425.7 426.1 426.4 426.8 427.1 427.5 427.8 428.2 + 428.5 428.8 429.2 429.5 429.9 430.2 430.5 430.8 431.2 431.5 431.8 432.2 432.5 432.8 433.1 433.4 433.8 434.1 + 434.4 434.7 435.0 435.3 435.7 436.0 436.3 436.6 436.9 437.2 437.5 437.8 438.1 438.4 438.7 439.0 439.3 439.6 + 439.9 440.2 440.4 440.7 441.0 441.3 441.6 441.9 442.2 442.4 442.7 443.0 443.3 443.5 443.8 444.1 444.3 444.6 + 444.9 445.1 445.4 445.7 445.9 446.2 446.5 446.7 447.0 447.2 447.5 447.7 448.0 448.2 448.5 448.7 449.0 449.2 + 449.4 449.7 449.9 450.1 450.4 450.6 450.8 451.1 451.3 451.5 451.8 452.0 452.2 452.4 452.6 452.9 453.1 453.3 + 453.5 453.7 453.9 454.1 454.4 454.6 454.8 455.0 455.2 455.4 455.6 455.8 456.0 456.2 456.3 456.5 456.7 456.9 + 457.1 457.3 457.5 457.7 ]plong + s[ 457.3 456.7 456.2 455.7 455.1 454.6 454.0 453.5 453.0 452.4 451.9 451.3 450.8 450.3 449.7 449.2 448.6 448.1 + 447.5 447.0 446.5 445.9 445.4 444.8 444.3 443.7 443.2 442.6 442.1 441.5 441.0 440.4 439.9 439.3 438.8 438.2 + 437.7 437.1 436.6 436.0 435.5 434.9 434.4 433.8 433.3 432.7 432.2 431.6 431.0 430.5 429.9 429.4 428.8 428.3 + 427.7 427.1 426.6 426.0 425.5 424.9 424.4 423.8 423.2 422.7 422.1 421.6 421.0 420.4 419.9 419.3 418.8 418.2 + 417.6 417.1 416.5 415.9 415.4 414.9 ][ 457.7 457.8 458.0 458.2 458.4 458.5 458.7 458.9 459.1 459.2 459.4 459.6 + 459.7 459.9 460.0 460.2 460.4 460.5 460.7 460.8 461.0 461.1 461.3 461.4 461.6 461.7 461.8 462.0 462.1 462.3 + 462.4 462.5 462.7 462.8 462.9 463.0 463.2 463.3 463.4 463.5 463.7 463.8 463.9 464.0 464.1 464.2 464.3 464.5 + 464.6 464.7 464.8 464.9 465.0 465.1 465.2 465.3 465.4 465.5 465.5 465.6 465.7 465.8 465.9 466.0 466.1 466.1 + 466.2 466.3 466.4 466.4 466.5 466.6 466.7 466.7 466.8 466.8 466.9 467.0 ]plong + s[ 373.0 372.5 371.9 371.4 370.8 370.2 369.7 369.1 368.6 368.0 367.4 366.9 366.3 365.8 365.2 364.6 364.1 363.5 + 363.0 362.4 361.9 361.3 360.7 360.2 359.6 359.1 358.5 358.0 357.4 356.8 356.3 355.7 355.2 354.6 354.1 353.5 + 353.0 352.4 351.9 351.3 350.7 350.2 349.6 349.1 348.5 348.0 347.4 346.9 346.3 345.8 345.2 344.7 344.2 343.6 + 343.1 342.5 342.0 341.4 340.9 340.3 339.8 339.2 338.7 338.2 337.6 337.1 336.5 336.0 335.5 334.9 334.4 333.8 + 333.3 332.8 332.2 331.7 331.1 330.6 330.1 329.5 329.0 328.5 327.9 327.4 326.9 326.3 325.8 325.3 324.8 324.2 + 323.7 323.2 322.6 322.1 321.6 321.1 320.5 320.0 319.5 319.0 318.4 317.9 317.4 316.9 316.4 315.8 315.3 314.8 + 314.3 313.8 313.2 312.7 312.2 311.7 311.2 310.7 310.2 309.6 309.1 308.6 308.1 307.6 307.1 306.6 306.1 305.6 + 305.1 304.6 304.1 303.6 303.1 302.6 302.1 301.6 301.1 300.6 300.1 299.6 299.1 298.6 298.1 297.6 297.1 296.6 + 296.1 295.6 295.1 294.6 294.1 293.7 293.2 292.7 292.2 291.7 291.2 290.7 290.3 289.8 289.3 288.8 288.3 287.9 + 287.4 286.9 286.4 286.0 285.5 285.0 284.6 284.1 283.6 283.1 282.7 282.2 281.7 281.3 280.8 280.4 279.9 279.4 + 279.0 278.5 278.0 277.6 277.1 276.7 276.2 275.8 275.3 274.9 274.4 274.0 273.5 273.1 272.6 272.2 271.7 271.3 + 270.8 270.4 ][ 467.0 466.9 466.8 466.8 466.7 466.7 466.6 466.5 466.4 466.4 466.3 466.2 466.1 466.1 466.0 465.9 + 465.8 465.7 465.6 465.5 465.5 465.4 465.3 465.2 465.1 465.0 464.9 464.8 464.7 464.6 464.5 464.3 464.2 464.1 + 464.0 463.9 463.8 463.7 463.5 463.4 463.3 463.2 463.0 462.9 462.8 462.7 462.5 462.4 462.3 462.1 462.0 461.8 + 461.7 461.6 461.4 461.3 461.1 461.0 460.8 460.7 460.5 460.4 460.2 460.0 459.9 459.7 459.6 459.4 459.2 459.1 + 458.9 458.7 458.5 458.4 458.2 458.0 457.8 457.7 457.5 457.3 457.1 456.9 456.7 456.5 456.3 456.2 456.0 455.8 + 455.6 455.4 455.2 455.0 454.8 454.6 454.4 454.1 453.9 453.7 453.5 453.3 453.1 452.9 452.6 452.4 452.2 452.0 + 451.8 451.5 451.3 451.1 450.8 450.6 450.4 450.1 449.9 449.7 449.4 449.2 449.0 448.7 448.5 448.2 448.0 447.7 + 447.5 447.2 447.0 446.7 446.5 446.2 445.9 445.7 445.4 445.1 444.9 444.6 444.3 444.1 443.8 443.5 443.3 443.0 + 442.7 442.4 442.2 441.9 441.6 441.3 441.0 440.7 440.4 440.2 439.9 439.6 439.3 439.0 438.7 438.4 438.1 437.8 + 437.5 437.2 436.9 436.6 436.3 436.0 435.7 435.3 435.0 434.7 434.4 434.1 433.8 433.4 433.1 432.8 432.5 432.2 + 431.8 431.5 431.2 430.8 430.5 430.2 429.9 429.5 429.2 428.8 428.5 428.2 427.8 427.5 427.1 426.8 426.4 426.1 + 425.7 425.4 425.0 424.7 ]plong + s[ 270.4 270.0 269.5 269.1 268.6 268.2 267.8 267.3 266.9 266.5 266.0 265.6 265.2 264.8 264.3 263.9 263.5 263.1 + 262.6 262.2 261.8 261.4 260.9 260.5 260.1 259.7 259.3 258.9 258.5 258.0 257.6 257.2 256.8 256.4 256.0 255.6 + 255.2 254.8 254.4 254.0 253.6 253.2 252.8 252.4 252.0 251.6 251.2 250.8 250.5 250.1 249.7 249.3 248.9 248.5 + 248.1 247.8 247.4 247.0 246.6 246.2 245.9 245.5 245.1 244.8 244.4 244.0 243.6 243.3 242.9 242.6 242.2 241.8 + 241.5 241.1 240.8 240.4 240.0 239.7 239.3 239.0 238.6 238.3 237.9 237.6 237.2 236.9 236.6 236.2 235.9 235.5 + 235.2 234.9 234.5 234.2 233.9 233.5 233.2 232.9 232.5 232.2 231.9 231.6 231.3 230.9 230.6 230.3 230.0 229.7 + 229.4 229.0 228.7 228.4 228.1 227.8 227.5 227.2 226.9 226.6 226.3 226.0 225.7 225.4 225.1 224.8 224.5 224.2 + 223.9 223.7 223.4 223.1 222.8 222.5 222.2 222.0 221.7 221.4 221.1 220.9 220.6 220.3 220.0 219.8 219.5 219.2 + 219.0 218.7 218.5 218.2 217.9 217.7 217.4 217.2 216.9 216.7 216.4 216.2 215.9 215.7 215.4 215.2 215.0 214.7 + 214.5 214.3 214.0 213.8 213.6 213.3 213.1 212.9 212.6 212.4 212.2 212.0 211.8 211.5 211.3 211.1 210.9 210.7 + 210.5 210.3 210.1 209.8 209.6 209.4 209.2 209.0 208.8 208.6 208.4 208.3 208.1 207.9 207.7 207.5 207.3 207.1 + 206.9 206.8 ][ 424.7 424.3 424.0 423.6 423.3 422.9 422.5 422.2 421.8 421.5 421.1 420.7 420.4 420.0 419.6 419.2 + 418.9 418.5 418.1 417.7 417.4 417.0 416.6 416.2 415.8 415.5 415.1 414.7 414.3 413.9 413.5 413.1 412.7 412.4 + 412.0 411.6 411.2 410.8 410.4 410.0 409.6 409.2 408.8 408.4 408.0 407.5 407.1 406.7 406.3 405.9 405.5 405.1 + 404.7 404.3 403.8 403.4 403.0 402.6 402.2 401.7 401.3 400.9 400.5 400.0 399.6 399.2 398.7 398.3 397.9 397.5 + 397.0 396.6 396.1 395.7 395.3 394.8 394.4 394.0 393.5 393.1 392.6 392.2 391.7 391.3 390.8 390.4 389.9 389.5 + 389.0 388.6 388.1 387.7 387.2 386.8 386.3 385.8 385.4 384.9 384.5 384.0 383.5 383.1 382.6 382.1 381.7 381.2 + 380.7 380.3 379.8 379.3 378.9 378.4 377.9 377.4 377.0 376.5 376.0 375.5 375.0 374.6 374.1 373.6 373.1 372.6 + 372.2 371.7 371.2 370.7 370.2 369.7 369.2 368.7 368.2 367.8 367.3 366.8 366.3 365.8 365.3 364.8 364.3 363.8 + 363.3 362.8 362.3 361.8 361.3 360.8 360.3 359.8 359.3 358.8 358.3 357.8 357.2 356.7 356.2 355.7 355.2 354.7 + 354.2 353.7 353.2 352.6 352.1 351.6 351.1 350.6 350.1 349.5 349.0 348.5 348.0 347.5 346.9 346.4 345.9 345.4 + 344.9 344.3 343.8 343.3 342.8 342.2 341.7 341.2 340.6 340.1 339.6 339.1 338.5 338.0 337.5 336.9 336.4 335.9 + 335.3 334.8 334.3 333.7 ]plong + s[ 206.8 206.6 206.4 206.2 206.0 205.9 205.7 205.5 205.4 205.2 205.0 204.9 204.7 204.5 204.4 204.2 204.1 203.9 + 203.8 203.6 203.4 203.3 203.2 203.0 202.9 202.7 202.6 202.4 202.3 202.2 202.0 201.9 201.8 201.6 201.5 201.4 + 201.2 201.1 201.0 200.9 200.8 200.6 200.5 200.4 200.3 200.2 200.1 200.0 199.9 199.8 199.7 199.6 199.5 199.5 ][ + 333.7 333.2 332.7 332.1 331.6 331.0 330.5 330.0 329.4 328.9 328.3 327.8 327.3 326.7 326.2 325.6 325.1 324.5 + 324.0 323.5 322.9 322.4 321.8 321.3 320.7 320.2 319.6 319.1 318.5 318.0 317.4 316.9 316.3 315.8 315.2 314.7 + 314.1 313.6 313.0 312.5 311.9 311.4 310.8 310.3 309.7 309.2 308.6 308.1 307.5 306.9 306.4 305.8 305.3 305.3 ]plong + s[ 199.5 199.5 199.6 199.7 199.8 199.9 200.0 200.1 200.2 200.3 200.5 200.6 200.7 200.8 200.9 201.1 201.2 201.3 + 201.4 201.6 201.7 201.8 201.9 202.1 202.2 202.4 202.5 202.6 202.8 202.9 203.1 203.2 203.4 203.5 203.7 203.8 + 204.0 204.1 204.3 204.4 204.6 204.8 204.9 205.1 205.3 205.4 205.6 205.8 205.9 206.1 206.3 206.5 206.6 206.8 + 207.0 207.2 207.4 207.6 207.8 207.9 208.1 208.3 208.5 208.7 208.9 209.1 209.3 209.5 209.7 209.9 210.1 210.3 + 210.6 210.8 211.0 211.2 211.4 211.6 211.8 212.1 212.3 212.5 212.7 213.0 213.2 213.4 213.6 213.9 214.1 214.3 + 214.6 214.8 215.1 215.3 215.5 215.8 216.0 216.3 216.5 216.8 217.0 217.3 217.5 217.8 218.0 218.3 218.6 218.8 + 219.1 219.4 219.6 219.9 220.2 220.4 220.7 221.0 221.2 221.5 221.8 222.1 222.4 222.6 222.9 223.2 223.5 223.8 + 224.1 224.3 224.6 224.9 225.2 225.5 225.8 226.1 226.4 226.7 227.0 227.3 227.6 227.9 228.2 228.5 228.9 229.2 + 229.5 229.8 230.1 230.4 230.7 231.1 231.4 231.7 232.0 232.4 232.7 233.0 233.3 233.7 234.0 234.3 234.7 235.0 + 235.3 235.7 236.0 236.4 236.7 237.0 237.4 237.7 238.1 238.4 238.8 239.1 239.5 239.8 240.2 240.5 240.9 241.3 + 241.6 242.0 242.3 242.7 243.1 243.4 243.8 244.2 244.5 244.9 245.3 245.6 246.0 246.4 246.8 247.2 247.5 247.9 + 248.3 248.7 ][ 235.7 235.5 235.0 234.4 233.8 233.3 232.7 232.2 231.6 231.1 230.5 230.0 229.4 228.9 228.3 227.8 + 227.2 226.7 226.1 225.6 225.0 224.4 223.9 223.4 222.8 222.3 221.7 221.2 220.6 220.1 219.5 219.0 218.4 217.9 + 217.3 216.8 216.2 215.7 215.2 214.6 214.1 213.5 213.0 212.5 211.9 211.4 210.8 210.3 209.8 209.2 208.7 208.1 + 207.6 207.1 206.5 206.0 205.5 204.9 204.4 203.9 203.3 202.8 202.3 201.7 201.2 200.7 200.2 199.6 199.1 198.6 + 198.0 197.5 197.0 196.5 195.9 195.4 194.9 194.4 193.9 193.3 192.8 192.3 191.8 191.3 190.7 190.2 189.7 189.2 + 188.7 188.2 187.6 187.1 186.6 186.1 185.6 185.1 184.6 184.1 183.6 183.1 182.5 182.0 181.5 181.0 180.5 180.0 + 179.5 179.0 178.5 178.0 177.5 177.0 176.5 176.0 175.5 175.0 174.5 174.0 173.6 173.1 172.6 172.1 171.6 171.1 + 170.6 170.1 169.6 169.2 168.7 168.2 167.7 167.2 166.7 166.3 165.8 165.3 164.8 164.3 163.9 163.4 162.9 162.4 + 162.0 161.5 161.0 160.6 160.1 159.6 159.1 158.7 158.2 157.8 157.3 156.8 156.4 155.9 155.4 155.0 154.5 154.1 + 153.6 153.2 152.7 152.2 151.8 151.3 150.9 150.4 150.0 149.5 149.1 148.7 148.2 147.8 147.3 146.9 146.4 146.0 + 145.6 145.1 144.7 144.3 143.8 143.4 143.0 142.5 142.1 141.7 141.2 140.8 140.4 140.0 139.5 139.1 138.7 138.3 + 137.8 137.4 137.0 136.6 ]plong + s[ 248.7 249.1 249.4 249.8 250.2 250.6 251.0 251.4 251.8 252.2 252.6 253.0 253.4 253.8 254.2 254.6 255.0 255.4 + 255.8 256.2 256.6 257.0 257.4 257.8 258.2 258.6 259.0 259.4 259.9 260.3 260.7 261.1 261.5 262.0 262.4 262.8 + 263.2 263.6 264.1 264.5 264.9 265.4 265.8 266.2 266.6 267.1 267.5 267.9 268.4 268.8 269.3 269.7 270.1 270.6 + 271.0 271.5 271.9 272.4 272.8 273.2 273.7 274.1 274.6 275.0 275.5 276.0 276.4 276.9 277.3 277.8 278.2 278.7 + 279.2 279.6 280.1 280.5 281.0 281.5 281.9 282.4 282.9 283.3 283.8 284.3 284.7 285.2 285.7 286.2 286.6 287.1 + 287.6 288.1 288.5 289.0 289.5 290.0 290.5 290.9 291.4 291.9 292.4 292.9 293.4 293.9 294.3 294.8 295.3 295.8 + 296.3 296.8 297.3 297.8 298.3 298.8 299.3 299.8 300.3 300.8 301.3 301.8 302.3 302.8 303.3 303.8 304.3 304.8 + 305.3 305.8 306.3 306.8 307.3 307.8 308.3 308.8 309.3 309.9 310.4 310.9 311.4 311.9 312.4 312.9 313.5 314.0 + 314.5 315.0 315.5 316.0 316.6 317.1 317.6 318.1 318.6 319.2 319.7 320.2 320.7 321.3 321.8 322.3 322.8 323.4 + 323.9 324.4 325.0 325.5 326.0 326.6 327.1 327.6 328.2 328.7 329.2 329.8 330.3 330.8 331.4 331.9 332.4 333.0 + 333.5 334.1 334.6 335.1 335.7 336.2 336.8 337.3 337.8 338.4 338.9 339.5 340.0 340.6 341.1 341.6 342.2 342.7 + 343.3 343.8 ][ 136.6 136.2 135.8 135.4 134.9 134.5 134.1 133.7 133.3 132.9 132.5 132.1 131.7 131.3 130.9 130.5 + 130.1 129.7 129.3 128.9 128.5 128.1 127.7 127.3 126.9 126.6 126.2 125.8 125.4 125.0 124.6 124.2 123.9 123.5 + 123.1 122.7 122.4 122.0 121.6 121.2 120.9 120.5 120.1 119.8 119.4 119.0 118.7 118.3 118.0 117.6 117.2 116.9 + 116.5 116.2 115.8 115.5 115.1 114.8 114.4 114.1 113.7 113.4 113.1 112.7 112.4 112.0 111.7 111.4 111.0 110.7 + 110.4 110.0 109.7 109.4 109.0 108.7 108.4 108.1 107.8 107.4 107.1 106.8 106.5 106.2 105.9 105.5 105.2 104.9 + 104.6 104.3 104.0 103.7 103.4 103.1 102.8 102.5 102.2 101.9 101.6 101.3 101.0 100.7 100.5 100.2 99.9 99.6 + 99.3 99.0 98.7 98.5 98.2 97.9 97.6 97.4 97.1 96.8 96.6 96.3 96.0 95.8 95.5 95.2 95.0 94.7 + 94.5 94.2 93.9 93.7 93.4 93.2 92.9 92.7 92.5 92.2 92.0 91.7 91.5 91.2 91.0 90.8 90.5 90.3 + 90.1 89.8 89.6 89.4 89.2 88.9 88.7 88.5 88.3 88.1 87.8 87.6 87.4 87.2 87.0 86.8 86.6 86.4 + 86.2 86.0 85.8 85.6 85.4 85.2 85.0 84.8 84.6 84.4 84.2 84.0 83.8 83.7 83.5 83.3 83.1 82.9 + 82.8 82.6 82.4 82.2 82.1 81.9 81.7 81.6 81.4 81.2 81.1 80.9 80.8 80.6 80.4 80.3 80.1 80.0 + 79.8 79.7 79.5 79.4 ]plong + s[ 343.8 344.4 344.9 345.5 346.0 346.6 347.1 347.7 348.2 348.8 349.3 349.9 350.4 351.0 351.5 352.1 352.6 353.2 + 353.7 354.3 354.8 355.4 356.0 356.5 357.1 357.6 358.2 358.7 359.2 ][ 79.4 79.3 79.1 79.0 78.8 78.7 78.6 + 78.4 78.3 78.2 78.0 77.9 77.8 77.7 77.5 77.4 77.3 77.2 77.1 77.0 76.8 76.7 76.6 76.5 76.4 + 76.3 76.2 76.1 76.0 ]plong + s[ 428.7 429.0 429.6 430.2 430.7 431.3 431.8 432.4 432.9 433.5 434.0 434.6 435.1 435.7 436.2 436.8 437.4 437.9 + 438.5 439.0 439.6 440.1 440.7 441.2 441.8 442.3 442.9 443.4 443.9 444.5 445.0 445.6 446.1 446.7 447.2 447.8 + 448.3 448.9 449.4 449.9 450.5 451.0 451.6 452.1 452.6 453.2 453.7 454.3 454.8 455.3 455.9 456.4 456.9 457.5 + 458.0 458.6 459.1 459.6 460.2 460.7 461.2 461.8 462.3 462.8 463.3 463.9 464.4 464.9 465.5 466.0 466.5 467.0 + 467.6 468.1 468.6 469.1 469.7 470.2 470.7 471.2 471.7 472.3 472.8 473.3 473.8 474.3 474.8 475.4 475.9 476.4 + 476.9 477.4 477.9 478.4 478.9 479.5 480.0 480.5 481.0 481.5 482.0 482.5 483.0 483.5 484.0 484.5 485.0 485.5 + 486.0 486.5 487.0 487.5 488.0 488.5 489.0 489.5 490.0 490.5 491.0 491.5 492.0 492.5 493.0 493.4 493.9 494.4 + 494.9 495.4 495.9 496.4 496.8 497.3 497.8 498.3 498.8 499.2 499.7 500.2 500.7 501.2 501.6 502.1 502.6 503.0 + 503.5 504.0 504.5 504.9 505.4 505.9 506.3 506.8 507.3 507.7 508.2 508.6 509.1 509.6 510.0 510.5 510.9 511.4 + 511.8 512.3 512.7 513.2 513.6 514.1 514.5 515.0 515.4 515.9 516.3 516.8 517.2 517.7 518.1 518.5 519.0 519.4 + 519.8 520.3 520.7 521.1 521.6 522.0 522.4 522.9 523.3 523.7 524.2 524.6 525.0 525.4 525.8 526.3 526.7 527.1 + 527.5 527.9 ][ 76.0 76.1 76.2 76.3 76.4 76.5 76.6 76.7 76.8 76.9 77.0 77.2 77.3 77.4 77.5 77.6 + 77.8 77.9 78.0 78.1 78.3 78.4 78.5 78.7 78.8 78.9 79.1 79.2 79.4 79.5 79.7 79.8 80.0 80.1 + 80.3 80.4 80.6 80.7 80.9 81.0 81.2 81.4 81.5 81.7 81.9 82.0 82.2 82.4 82.5 82.7 82.9 83.1 + 83.3 83.4 83.6 83.8 84.0 84.2 84.4 84.6 84.7 84.9 85.1 85.3 85.5 85.7 85.9 86.1 86.3 86.5 + 86.7 87.0 87.2 87.4 87.6 87.8 88.0 88.2 88.5 88.7 88.9 89.1 89.3 89.6 89.8 90.0 90.3 90.5 + 90.7 91.0 91.2 91.4 91.7 91.9 92.2 92.4 92.6 92.9 93.1 93.4 93.6 93.9 94.2 94.4 94.7 94.9 + 95.2 95.4 95.7 96.0 96.2 96.5 96.8 97.0 97.3 97.6 97.9 98.1 98.4 98.7 99.0 99.3 99.5 99.8 + 100.1 100.4 100.7 101.0 101.3 101.6 101.8 102.1 102.4 102.7 103.0 103.3 103.6 103.9 104.2 104.6 104.9 105.2 + 105.5 105.8 106.1 106.4 106.7 107.1 107.4 107.7 108.0 108.3 108.7 109.0 109.3 109.6 110.0 110.3 110.6 111.0 + 111.3 111.6 112.0 112.3 112.6 113.0 113.3 113.7 114.0 114.4 114.7 115.1 115.4 115.8 116.1 116.5 116.8 117.2 + 117.5 117.9 118.2 118.6 119.0 119.3 119.7 120.1 120.4 120.8 121.2 121.5 121.9 122.3 122.7 123.0 123.4 123.8 + 124.2 124.6 124.9 125.3 ]plong + s[ 527.9 528.4 528.8 529.2 529.6 530.0 530.4 530.8 531.2 531.6 532.0 532.4 532.8 533.2 533.6 ][ 125.3 125.7 126.1 + 126.5 126.9 127.3 127.6 128.0 128.4 128.8 129.2 129.6 130.0 130.4 130.8 ]plong + s[ 244.1 244.5 244.9 245.4 245.8 246.2 246.7 247.1 247.5 248.0 248.4 248.9 249.3 249.8 250.2 250.6 251.1 251.5 + 252.0 252.4 252.9 253.4 253.8 254.3 254.7 255.2 255.6 256.1 256.6 257.0 257.5 257.9 258.4 258.9 259.3 259.8 + 260.3 260.8 261.2 261.7 262.2 262.7 263.1 263.6 264.1 264.6 265.1 265.5 266.0 266.5 267.0 267.5 268.0 268.4 + 268.9 269.4 269.9 270.4 270.9 271.4 271.9 272.4 272.9 273.4 273.9 274.4 274.9 275.4 275.9 276.4 276.9 277.4 + 277.9 278.4 278.9 279.4 280.0 280.5 281.0 281.5 282.0 282.5 283.0 283.6 284.1 284.6 285.1 285.6 286.2 286.7 + 287.2 287.7 288.3 288.8 289.3 289.8 290.4 290.9 291.4 292.0 292.5 293.0 293.6 294.1 294.6 295.2 295.7 296.2 + 296.8 297.3 297.9 298.4 298.9 299.5 300.0 300.6 301.1 301.7 302.2 302.8 303.3 303.8 304.4 304.9 305.5 306.0 + 306.6 307.2 307.7 308.3 308.8 309.4 309.7 ][ 120.6 120.2 119.8 119.4 118.9 118.5 118.1 117.7 117.3 116.8 116.4 + 116.0 115.6 115.2 114.8 114.4 113.9 113.5 113.1 112.7 112.3 111.9 111.5 111.1 110.7 110.3 109.9 109.5 109.1 + 108.7 108.3 108.0 107.6 107.2 106.8 106.4 106.0 105.6 105.3 104.9 104.5 104.1 103.8 103.4 103.0 102.6 102.3 + 101.9 101.5 101.2 100.8 100.4 100.1 99.7 99.4 99.0 98.6 98.3 97.9 97.6 97.2 96.9 96.5 96.2 95.9 + 95.5 95.2 94.8 94.5 94.2 93.8 93.5 93.1 92.8 92.5 92.2 91.8 91.5 91.2 90.9 90.5 90.2 89.9 + 89.6 89.3 89.0 88.6 88.3 88.0 87.7 87.4 87.1 86.8 86.5 86.2 85.9 85.6 85.3 85.0 84.7 84.4 + 84.1 83.9 83.6 83.3 83.0 82.7 82.4 82.2 81.9 81.6 81.3 81.1 80.8 80.5 80.2 80.0 79.7 79.5 + 79.2 78.9 78.7 78.4 78.2 77.9 77.7 77.4 77.2 76.9 76.7 76.4 76.2 76.0 ]plong + s[ 478.1 478.6 479.2 479.7 480.3 480.8 481.4 481.9 482.5 483.0 483.6 484.1 484.7 485.2 485.8 486.3 486.9 487.4 + 488.0 488.5 489.1 489.6 490.1 490.7 491.2 491.8 492.3 492.8 493.4 493.9 494.4 495.0 495.5 496.0 496.6 497.1 + 497.6 498.2 498.7 499.2 499.7 500.3 500.8 501.3 501.8 502.4 502.9 503.4 503.9 504.4 504.9 505.5 506.0 506.5 + 507.0 507.5 508.0 508.5 509.1 509.6 510.1 510.6 511.1 511.6 512.1 512.6 513.1 513.6 514.1 514.6 515.1 515.6 + 516.1 516.6 517.1 517.6 518.1 518.6 519.0 519.5 520.0 520.5 521.0 521.5 522.0 522.4 522.9 523.4 523.9 524.4 + 524.8 525.3 525.8 526.3 526.7 527.2 527.7 528.2 528.6 529.1 529.6 530.0 530.5 531.0 531.4 531.9 532.3 532.8 + 533.3 533.7 534.2 534.6 535.1 535.5 536.0 536.4 536.9 537.3 537.8 538.2 538.7 539.1 539.5 540.0 540.4 540.9 + 541.3 541.7 542.2 542.6 543.0 543.5 543.9 544.3 544.7 545.2 545.6 546.0 546.4 546.9 547.3 547.7 548.1 548.5 + 548.9 549.4 549.8 550.2 550.6 551.0 551.4 551.8 552.2 552.6 553.0 553.4 553.8 554.2 554.6 555.0 555.4 555.8 + 556.2 556.6 557.0 557.3 557.7 558.1 558.5 558.9 559.3 559.6 560.0 560.4 560.8 561.1 561.5 561.9 562.3 562.6 + 563.0 563.4 563.7 564.1 564.4 564.8 565.2 565.5 565.9 566.2 566.6 566.9 567.3 567.6 568.0 568.3 568.7 569.0 + 569.3 569.7 ][ 76.0 76.2 76.5 76.7 77.0 77.2 77.5 77.7 78.0 78.2 78.5 78.7 79.0 79.2 79.5 79.8 + 80.0 80.3 80.6 80.8 81.1 81.4 81.7 81.9 82.2 82.5 82.8 83.1 83.3 83.6 83.9 84.2 84.5 84.8 + 85.1 85.4 85.7 86.0 86.3 86.6 86.9 87.2 87.5 87.8 88.1 88.4 88.7 89.0 89.3 89.6 90.0 90.3 + 90.6 90.9 91.2 91.6 91.9 92.2 92.6 92.9 93.2 93.5 93.9 94.2 94.6 94.9 95.2 95.6 95.9 96.3 + 96.6 97.0 97.3 97.7 98.0 98.4 98.7 99.1 99.4 99.8 100.2 100.5 100.9 101.2 101.6 102.0 102.3 102.7 + 103.1 103.5 103.8 104.2 104.6 105.0 105.3 105.7 106.1 106.5 106.9 107.3 107.6 108.0 108.4 108.8 109.2 109.6 + 110.0 110.4 110.8 111.2 111.6 112.0 112.4 112.8 113.2 113.6 114.0 114.4 114.8 115.3 115.7 116.1 116.5 116.9 + 117.3 117.8 118.2 118.6 119.0 119.4 119.9 120.3 120.7 121.2 121.6 122.0 122.5 122.9 123.3 123.8 124.2 124.6 + 125.1 125.5 126.0 126.4 126.9 127.3 127.8 128.2 128.7 129.1 129.6 130.0 130.5 130.9 131.4 131.8 132.3 132.8 + 133.2 133.7 134.1 134.6 135.1 135.5 136.0 136.5 136.9 137.4 137.9 138.4 138.8 139.3 139.8 140.3 140.8 141.2 + 141.7 142.2 142.7 143.2 143.6 144.1 144.6 145.1 145.6 146.1 146.6 147.1 147.6 148.1 148.6 149.1 149.6 150.1 + 150.6 151.1 151.6 152.1 ]plong + s[ 569.7 570.0 570.4 570.7 571.0 571.4 571.7 572.0 572.4 572.7 573.0 573.3 573.7 574.0 574.3 574.6 574.9 575.2 + 575.6 575.9 576.2 576.5 576.8 577.1 577.4 577.7 578.0 578.3 578.6 578.9 579.2 579.5 579.8 580.1 580.4 580.7 + 580.9 581.2 581.5 581.8 582.1 582.3 582.6 582.9 583.2 583.4 583.7 584.0 584.3 584.5 584.8 585.0 585.3 585.6 + 585.8 586.1 586.3 586.6 586.8 587.1 587.3 587.6 587.8 588.1 588.3 588.6 588.8 589.0 589.3 589.5 589.7 590.0 + 590.2 590.4 ][ 152.1 152.6 153.1 153.6 154.1 154.6 155.1 155.6 156.1 156.6 157.1 157.6 158.2 158.7 159.2 159.7 + 160.2 160.7 161.3 161.8 162.3 162.8 163.3 163.9 164.4 164.9 165.4 166.0 166.5 167.0 167.6 168.1 168.6 169.2 + 169.7 170.2 170.8 171.3 171.8 172.4 172.9 173.4 174.0 174.5 175.1 175.6 176.2 176.7 177.2 177.8 178.3 178.9 + 179.4 180.0 180.5 181.1 181.6 182.2 182.7 183.3 183.8 184.4 184.9 185.5 186.1 186.6 187.2 187.7 188.3 188.8 + 189.4 190.0 190.5 191.0 ]plong + s[ 590.4 590.3 590.1 589.8 589.6 589.4 589.1 588.9 588.7 588.4 588.2 587.9 587.7 587.4 587.2 586.9 586.7 586.4 + 586.2 585.9 585.7 585.4 585.2 584.9 584.6 584.4 584.1 583.8 583.6 583.3 583.0 582.7 582.5 582.2 581.9 581.6 + 581.3 581.1 580.8 580.5 580.2 579.9 579.6 579.3 579.0 578.7 578.4 578.1 577.8 577.5 577.2 576.9 576.6 576.3 + 576.0 575.7 575.4 575.1 574.7 574.4 574.1 573.8 573.5 573.1 572.8 572.5 572.2 571.8 571.5 571.2 570.8 570.5 + 570.2 569.8 569.5 569.1 568.8 568.5 568.1 567.8 567.4 567.1 566.7 566.4 566.0 565.7 565.3 564.9 564.6 564.2 + 563.9 563.5 563.1 562.8 562.4 562.0 561.7 561.3 560.9 560.5 560.2 559.8 559.4 559.0 558.7 558.3 557.9 557.5 + 557.1 556.7 556.3 555.9 555.6 555.2 554.8 554.4 554.0 553.6 553.2 552.8 552.4 552.0 551.6 551.2 550.8 550.3 + 549.9 549.5 549.1 548.7 548.3 547.9 547.4 547.0 546.6 546.2 545.8 545.3 544.9 544.5 544.1 543.6 543.2 542.8 + 542.3 541.9 541.5 541.0 540.6 540.2 539.7 539.3 538.8 538.4 537.9 537.5 537.1 536.6 536.2 535.7 535.3 534.8 + 534.3 533.9 533.4 533.0 532.5 532.1 531.6 531.1 530.7 530.2 529.7 529.3 528.8 528.3 527.9 527.4 526.9 526.5 + 526.0 525.5 525.0 524.6 524.1 523.6 523.1 522.6 522.2 521.7 521.2 520.7 520.2 519.7 519.2 518.7 518.3 517.8 + 517.3 516.8 ][ 350.0 350.3 350.8 351.4 351.9 352.5 353.1 353.6 354.2 354.7 355.3 355.9 356.4 357.0 357.5 358.1 + 358.6 359.2 359.7 360.3 360.8 361.4 361.9 362.5 363.0 363.6 364.1 364.6 365.2 365.7 366.3 366.8 367.3 367.9 + 368.4 369.0 369.5 370.0 370.6 371.1 371.6 372.2 372.7 373.2 373.8 374.3 374.8 375.4 375.9 376.4 376.9 377.5 + 378.0 378.5 379.0 379.5 380.1 380.6 381.1 381.6 382.1 382.6 383.2 383.7 384.2 384.7 385.2 385.7 386.2 386.7 + 387.2 387.7 388.3 388.8 389.3 389.8 390.3 390.8 391.3 391.8 392.3 392.8 393.2 393.7 394.2 394.7 395.2 395.7 + 396.2 396.7 397.2 397.7 398.1 398.6 399.1 399.6 400.1 400.5 401.0 401.5 402.0 402.5 402.9 403.4 403.9 404.3 + 404.8 405.3 405.8 406.2 406.7 407.1 407.6 408.1 408.5 409.0 409.5 409.9 410.4 410.8 411.3 411.7 412.2 412.6 + 413.1 413.5 414.0 414.4 414.9 415.3 415.8 416.2 416.6 417.1 417.5 417.9 418.4 418.8 419.2 419.7 420.1 420.5 + 421.0 421.4 421.8 422.2 422.7 423.1 423.5 423.9 424.3 424.8 425.2 425.6 426.0 426.4 426.8 427.2 427.6 428.0 + 428.5 428.9 429.3 429.7 430.1 430.5 430.9 431.3 431.6 432.0 432.4 432.8 433.2 433.6 434.0 434.4 434.8 435.1 + 435.5 435.9 436.3 436.7 437.0 437.4 437.8 438.2 438.5 438.9 439.3 439.6 440.0 440.4 440.7 441.1 441.4 441.8 + 442.2 442.5 442.9 443.2 ]plong + s[ 516.8 516.3 515.8 515.3 514.8 514.3 513.8 513.3 512.8 512.3 511.8 511.3 510.8 510.3 509.8 509.3 508.7 508.2 + 507.7 507.2 506.7 506.2 505.7 505.2 504.6 504.1 503.6 503.1 502.6 502.0 501.5 501.0 500.5 499.9 499.4 498.9 + 498.4 497.8 497.3 496.8 496.2 495.7 495.2 494.6 494.1 493.6 493.0 492.5 492.0 491.4 490.9 490.4 489.8 489.3 + 488.7 488.2 487.6 487.1 486.6 486.0 485.5 484.9 484.4 483.8 483.3 482.7 482.2 481.6 481.1 480.5 480.0 479.4 + 478.8 478.3 477.7 477.2 476.6 476.1 475.5 474.9 474.4 473.8 473.5 ][ 443.2 443.6 443.9 444.3 444.6 445.0 445.3 + 445.6 446.0 446.3 446.7 447.0 447.3 447.7 448.0 448.3 448.7 449.0 449.3 449.6 450.0 450.3 450.6 450.9 451.2 + 451.6 451.9 452.2 452.5 452.8 453.1 453.4 453.7 454.0 454.3 454.6 454.9 455.2 455.5 455.8 456.1 456.4 456.7 + 457.0 457.3 457.6 457.8 458.1 458.4 458.7 459.0 459.2 459.5 459.8 460.1 460.3 460.6 460.9 461.1 461.4 461.7 + 461.9 462.2 462.4 462.7 463.0 463.2 463.5 463.7 464.0 464.2 464.5 464.7 464.9 465.2 465.4 465.7 465.9 466.1 + 466.4 466.6 466.8 467.0 ]plong + s[ 314.4 314.1 313.5 312.9 312.4 311.8 311.3 310.7 310.2 309.6 309.0 308.5 307.9 307.4 306.8 306.3 305.7 305.2 + 304.6 304.1 303.5 303.0 302.4 301.9 301.3 300.8 300.2 299.7 299.2 298.6 298.1 297.5 297.0 296.5 295.9 295.4 + 294.8 294.3 293.8 293.2 292.7 292.2 291.6 291.1 290.6 290.0 289.5 289.0 288.5 287.9 287.4 286.9 286.4 285.8 + 285.3 284.8 284.3 283.8 283.2 282.7 282.2 281.7 281.2 280.7 280.2 279.6 279.1 278.6 278.1 277.6 277.1 276.6 + 276.1 275.6 275.1 274.6 274.1 273.6 273.1 272.6 272.1 271.6 271.1 270.6 270.1 269.6 269.1 268.6 268.2 267.7 + 267.2 266.7 266.2 265.7 265.2 264.8 264.3 263.8 263.3 262.8 262.4 261.9 261.4 260.9 260.5 260.0 259.5 259.1 + 258.6 258.1 257.7 257.2 256.7 256.3 255.8 255.4 254.9 254.4 254.0 253.5 253.1 252.6 252.2 251.7 251.3 250.8 + 250.4 249.9 249.5 249.0 248.6 248.2 247.7 247.3 246.8 246.4 246.0 245.5 245.1 244.7 244.3 243.8 243.4 243.0 + 242.5 242.1 241.7 241.3 240.9 240.4 240.0 239.6 239.2 238.8 238.4 237.9 237.5 237.1 236.7 236.3 235.9 235.5 + 235.1 234.7 234.3 233.9 233.5 233.1 232.7 232.3 231.9 231.5 231.2 230.8 230.4 230.0 229.6 229.2 228.8 228.5 + 228.1 227.7 227.3 227.0 226.6 226.2 225.8 225.5 225.1 224.7 224.4 224.0 223.7 223.3 222.9 222.6 222.2 221.9 + 221.5 221.2 ][ 467.0 466.8 466.6 466.4 466.1 465.9 465.7 465.4 465.2 464.9 464.7 464.5 464.2 464.0 463.7 463.5 + 463.2 463.0 462.7 462.4 462.2 461.9 461.7 461.4 461.1 460.9 460.6 460.3 460.1 459.8 459.5 459.2 459.0 458.7 + 458.4 458.1 457.8 457.6 457.3 457.0 456.7 456.4 456.1 455.8 455.5 455.2 454.9 454.6 454.3 454.0 453.7 453.4 + 453.1 452.8 452.5 452.2 451.9 451.6 451.2 450.9 450.6 450.3 450.0 449.6 449.3 449.0 448.7 448.3 448.0 447.7 + 447.3 447.0 446.7 446.3 446.0 445.6 445.3 445.0 444.6 444.3 443.9 443.6 443.2 442.9 442.5 442.2 441.8 441.4 + 441.1 440.7 440.4 440.0 439.6 439.3 438.9 438.5 438.2 437.8 437.4 437.0 436.7 436.3 435.9 435.5 435.1 434.8 + 434.4 434.0 433.6 433.2 432.8 432.4 432.0 431.6 431.3 430.9 430.5 430.1 429.7 429.3 428.9 428.5 428.0 427.6 + 427.2 426.8 426.4 426.0 425.6 425.2 424.8 424.3 423.9 423.5 423.1 422.7 422.2 421.8 421.4 421.0 420.5 420.1 + 419.7 419.2 418.8 418.4 417.9 417.5 417.1 416.6 416.2 415.8 415.3 414.9 414.4 414.0 413.5 413.1 412.6 412.2 + 411.7 411.3 410.8 410.4 409.9 409.5 409.0 408.5 408.1 407.6 407.1 406.7 406.2 405.8 405.3 404.8 404.3 403.9 + 403.4 402.9 402.5 402.0 401.5 401.0 400.5 400.1 399.6 399.1 398.6 398.1 397.7 397.2 396.7 396.2 395.7 395.2 + 394.7 394.2 393.7 393.2 ]plong + s[ 221.2 220.8 220.5 220.1 219.8 219.4 219.1 218.7 218.4 218.1 217.7 217.4 217.1 216.7 216.4 216.1 215.7 215.4 + 215.1 214.7 214.4 214.1 213.8 213.5 213.1 212.8 212.5 212.2 211.9 211.6 211.3 211.0 210.7 210.4 210.1 209.8 + 209.5 209.2 208.9 208.6 208.3 208.0 207.7 207.4 207.1 206.8 206.5 206.3 206.0 205.7 205.4 205.1 204.9 204.6 + 204.3 204.1 203.8 203.5 203.3 203.0 202.7 202.5 202.2 202.0 201.7 201.4 201.2 200.9 200.7 200.4 200.2 199.9 + 199.7 199.5 199.5 ][ 393.2 392.8 392.3 391.8 391.3 390.8 390.3 389.8 389.3 388.8 388.3 387.7 387.2 386.7 386.2 + 385.7 385.2 384.7 384.2 383.7 383.2 382.6 382.1 381.6 381.1 380.6 380.1 379.5 379.0 378.5 378.0 377.5 376.9 + 376.4 375.9 375.4 374.8 374.3 373.8 373.2 372.7 372.2 371.6 371.1 370.6 370.0 369.5 369.0 368.4 367.9 367.3 + 366.8 366.3 365.7 365.2 364.6 364.1 363.6 363.0 362.5 361.9 361.4 360.8 360.3 359.7 359.2 358.6 358.1 357.5 + 357.0 356.4 355.9 355.3 354.7 354.7 ]plong + s[ 199.5 199.6 199.8 200.0 200.3 200.5 200.8 201.0 201.3 201.5 201.8 202.1 202.3 202.6 202.8 203.1 203.4 203.6 + 203.9 204.2 204.4 204.7 205.0 205.3 205.5 205.8 206.1 206.4 206.7 206.9 207.2 207.5 207.8 208.1 208.4 208.7 + 209.0 209.3 209.6 209.9 210.2 210.5 210.8 211.1 211.4 211.7 212.0 212.3 212.6 213.0 213.3 213.6 213.9 214.2 + 214.6 214.9 215.2 215.5 215.9 216.2 216.5 216.9 217.2 217.5 217.9 218.2 218.5 218.9 219.2 219.6 219.9 220.3 + 220.6 221.0 221.3 221.7 222.0 222.4 222.7 223.1 223.4 223.8 224.2 224.5 224.9 225.3 225.6 226.0 226.4 226.7 + 227.1 227.5 227.9 228.2 228.6 229.0 229.4 229.8 230.1 230.5 230.9 231.3 231.7 232.1 232.5 232.9 233.3 233.7 + 234.1 234.5 234.9 235.3 235.7 236.1 236.5 236.9 237.3 237.7 238.1 238.5 238.9 239.4 239.8 240.2 240.6 241.0 + 241.4 241.9 242.3 242.7 243.1 243.6 244.0 244.4 244.9 245.3 245.7 246.2 246.6 247.0 247.5 247.9 248.3 248.8 + 249.2 249.7 250.1 250.6 251.0 251.5 251.9 252.4 252.8 253.3 253.7 254.2 254.6 255.1 255.5 256.0 256.5 256.9 + 257.4 257.9 258.3 258.8 259.3 259.7 260.2 260.7 261.1 261.6 262.1 262.6 263.0 263.5 264.0 264.5 265.0 265.4 + 265.9 266.4 266.9 267.4 267.9 268.3 268.8 269.3 269.8 270.3 270.8 271.3 271.8 272.3 272.8 273.3 273.8 274.3 + 274.8 275.3 ][ 186.3 186.1 185.5 184.9 184.4 183.8 183.3 182.7 182.2 181.6 181.1 180.5 180.0 179.4 178.9 178.3 + 177.8 177.2 176.7 176.2 175.6 175.1 174.5 174.0 173.4 172.9 172.4 171.8 171.3 170.8 170.2 169.7 169.2 168.6 + 168.1 167.6 167.0 166.5 166.0 165.4 164.9 164.4 163.9 163.3 162.8 162.3 161.8 161.3 160.7 160.2 159.7 159.2 + 158.7 158.2 157.6 157.1 156.6 156.1 155.6 155.1 154.6 154.1 153.6 153.1 152.6 152.1 151.6 151.1 150.6 150.1 + 149.6 149.1 148.6 148.1 147.6 147.1 146.6 146.1 145.6 145.1 144.6 144.1 143.6 143.2 142.7 142.2 141.7 141.2 + 140.8 140.3 139.8 139.3 138.8 138.4 137.9 137.4 136.9 136.5 136.0 135.5 135.1 134.6 134.1 133.7 133.2 132.8 + 132.3 131.8 131.4 130.9 130.5 130.0 129.6 129.1 128.7 128.2 127.8 127.3 126.9 126.4 126.0 125.5 125.1 124.6 + 124.2 123.8 123.3 122.9 122.5 122.0 121.6 121.2 120.7 120.3 119.9 119.4 119.0 118.6 118.2 117.8 117.3 116.9 + 116.5 116.1 115.7 115.3 114.8 114.4 114.0 113.6 113.2 112.8 112.4 112.0 111.6 111.2 110.8 110.4 110.0 109.6 + 109.2 108.8 108.4 108.0 107.6 107.3 106.9 106.5 106.1 105.7 105.3 105.0 104.6 104.2 103.8 103.5 103.1 102.7 + 102.3 102.0 101.6 101.2 100.9 100.5 100.2 99.8 99.4 99.1 98.7 98.4 98.0 97.7 97.3 97.0 96.6 96.3 + 95.9 95.6 95.2 94.9 ]plong + s[ 275.3 275.8 276.3 276.8 277.3 277.8 278.3 278.8 279.3 279.9 280.4 280.9 281.4 281.9 282.4 282.9 283.5 284.0 + 284.5 285.0 285.5 286.1 286.6 287.1 287.6 288.1 288.7 289.2 289.7 290.3 290.8 291.3 291.8 292.4 292.9 293.4 + 294.0 294.5 295.1 295.6 296.1 296.7 297.2 297.7 298.3 298.8 299.4 299.9 300.5 301.0 301.5 302.1 302.6 303.2 + 303.7 304.3 304.8 305.4 305.9 306.5 307.0 307.6 308.2 308.7 309.3 309.7 ][ 94.9 94.6 94.2 93.9 93.5 93.2 + 92.9 92.6 92.2 91.9 91.6 91.2 90.9 90.6 90.3 90.0 89.6 89.3 89.0 88.7 88.4 88.1 87.8 87.5 + 87.2 86.9 86.6 86.3 86.0 85.7 85.4 85.1 84.8 84.5 84.2 83.9 83.6 83.3 83.1 82.8 82.5 82.2 + 81.9 81.7 81.4 81.1 80.8 80.6 80.3 80.0 79.8 79.5 79.2 79.0 78.7 78.5 78.2 78.0 77.7 77.5 + 77.2 77.0 76.7 76.5 76.2 76.0 ]plong + s[ 478.1 478.5 479.1 479.6 480.2 480.7 481.3 481.8 482.4 482.9 483.5 484.0 484.6 485.1 485.7 486.2 486.8 487.3 + 487.9 488.4 488.9 489.5 490.0 490.6 491.1 491.6 492.2 492.7 493.3 493.8 494.3 494.9 495.4 495.9 496.5 497.0 + 497.5 498.0 498.6 499.1 499.6 500.2 500.7 501.2 501.7 502.2 502.8 503.3 503.8 504.3 504.8 505.4 505.9 506.4 + 506.9 507.4 507.9 508.4 508.9 509.5 510.0 510.5 511.0 511.5 512.0 512.5 513.0 513.5 514.0 514.5 515.0 515.5 + 516.0 516.5 517.0 517.5 518.0 518.5 518.9 519.4 519.9 520.4 520.9 521.4 521.9 522.3 522.8 523.3 523.8 524.3 + 524.7 525.2 525.7 526.2 526.6 527.1 527.6 528.1 528.5 529.0 529.5 529.9 530.4 530.9 531.3 531.8 532.2 532.7 + 533.2 533.6 534.1 534.5 535.0 535.4 535.9 536.3 536.8 537.2 537.7 538.1 538.6 539.0 539.5 539.9 540.3 540.8 + 541.2 541.6 542.1 542.5 542.9 543.4 543.8 ][ 76.0 76.2 76.4 76.7 76.9 77.2 77.4 77.7 77.9 78.2 78.4 + 78.7 78.9 79.2 79.5 79.7 80.0 80.2 80.5 80.8 81.1 81.3 81.6 81.9 82.2 82.4 82.7 83.0 83.3 + 83.6 83.9 84.1 84.4 84.7 85.0 85.3 85.6 85.9 86.2 86.5 86.8 87.1 87.4 87.7 88.0 88.3 88.6 + 89.0 89.3 89.6 89.9 90.2 90.5 90.9 91.2 91.5 91.8 92.2 92.5 92.8 93.1 93.5 93.8 94.2 94.5 + 94.8 95.2 95.5 95.9 96.2 96.5 96.9 97.2 97.6 97.9 98.3 98.6 99.0 99.4 99.7 100.1 100.4 100.8 + 101.2 101.5 101.9 102.3 102.6 103.0 103.4 103.8 104.1 104.5 104.9 105.3 105.6 106.0 106.4 106.8 107.2 107.6 + 108.0 108.3 108.7 109.1 109.5 109.9 110.3 110.7 111.1 111.5 111.9 112.3 112.7 113.1 113.5 113.9 114.4 114.8 + 115.2 115.6 116.0 116.4 116.8 117.3 117.7 118.1 118.5 118.9 119.4 119.8 120.2 120.6 ]plong + s[ 233.9 234.4 234.8 235.3 235.8 236.2 236.7 237.2 237.6 238.1 238.6 239.0 239.5 240.0 240.5 240.9 241.4 241.9 + 242.4 242.9 243.3 243.8 244.3 244.8 245.3 245.8 246.3 246.8 247.2 247.7 248.2 248.7 249.2 249.7 250.2 250.7 + 251.2 251.7 252.2 252.7 253.2 253.8 254.3 254.8 255.3 255.8 256.3 256.8 257.3 257.9 258.4 258.9 259.4 259.9 + 260.5 261.0 261.5 262.0 262.6 263.1 263.6 264.2 264.7 265.2 265.7 266.3 266.8 267.4 267.9 268.4 269.0 269.5 + 270.1 270.6 271.1 271.7 272.2 272.8 273.3 273.9 274.4 275.0 275.5 276.1 276.6 277.2 277.7 278.2 ][ 110.5 110.0 + 109.6 109.1 108.7 108.2 107.8 107.3 106.9 106.4 106.0 105.5 105.1 104.6 104.2 103.8 103.3 102.9 102.5 102.0 + 101.6 101.2 100.7 100.3 99.9 99.5 99.0 98.6 98.2 97.8 97.4 96.9 96.5 96.1 95.7 95.3 94.9 94.5 + 94.1 93.7 93.3 92.9 92.5 92.1 91.7 91.3 90.9 90.5 90.1 89.7 89.3 88.9 88.5 88.1 87.8 87.4 + 87.0 86.6 86.2 85.9 85.5 85.1 84.8 84.4 84.0 83.6 83.3 82.9 82.6 82.2 81.8 81.5 81.1 80.8 + 80.4 80.1 79.7 79.4 79.0 78.7 78.3 78.0 77.7 77.3 77.0 76.7 76.3 76.0 ]plong + s[ 509.6 509.7 510.3 510.8 511.4 511.9 512.5 513.0 513.6 514.1 514.7 515.2 515.8 516.3 516.9 517.4 517.9 518.5 + 519.0 519.6 520.1 520.6 521.2 521.7 522.2 522.8 523.3 523.8 524.4 524.9 525.4 525.9 526.5 527.0 527.5 528.0 + 528.6 529.1 529.6 530.1 530.6 531.2 531.7 532.2 532.7 533.2 533.7 534.2 534.7 535.2 535.7 536.2 536.8 537.3 + 537.8 538.3 538.8 539.3 539.7 540.2 540.7 541.2 541.7 542.2 542.7 543.2 543.7 544.2 544.6 545.1 545.6 546.1 + 546.6 547.0 547.5 548.0 548.5 548.9 549.4 549.9 550.3 550.8 551.3 551.7 552.2 552.7 553.1 553.6 554.1 554.5 + 555.0 555.4 555.9 556.3 556.8 557.2 557.7 558.1 558.6 559.0 559.4 559.9 560.3 560.8 561.2 561.6 562.1 562.5 + 562.9 563.4 563.8 564.2 564.7 565.1 565.5 565.9 566.3 566.8 567.2 567.6 568.0 568.4 568.8 569.2 569.7 570.1 + 570.5 570.9 571.3 571.7 572.1 572.5 572.9 573.3 573.7 574.1 574.4 574.8 575.2 575.6 576.0 576.4 576.8 577.1 + 577.5 577.9 578.3 578.7 579.0 579.4 579.8 580.1 580.5 580.9 581.2 581.6 582.0 582.3 582.7 583.0 583.4 583.7 + 584.1 584.4 584.8 585.1 585.5 585.8 586.2 586.5 586.9 587.2 587.5 587.9 588.2 588.5 588.9 589.2 589.5 589.8 + 590.2 590.4 ][ 76.0 76.1 76.4 76.7 77.1 77.4 77.7 78.1 78.4 78.8 79.1 79.4 79.8 80.1 80.5 80.8 + 81.2 81.6 81.9 82.3 82.6 83.0 83.4 83.7 84.1 84.5 84.8 85.2 85.6 85.9 86.3 86.7 87.1 87.5 + 87.8 88.2 88.6 89.0 89.4 89.8 90.2 90.5 90.9 91.3 91.7 92.1 92.5 92.9 93.3 93.7 94.1 94.5 + 95.0 95.4 95.8 96.2 96.6 97.0 97.4 97.9 98.3 98.7 99.1 99.5 100.0 100.4 100.8 101.2 101.7 102.1 + 102.5 103.0 103.4 103.9 104.3 104.7 105.2 105.6 106.1 106.5 107.0 107.4 107.9 108.3 108.8 109.2 109.7 110.1 + 110.6 111.0 111.5 112.0 112.4 112.9 113.4 113.8 114.3 114.8 115.2 115.7 116.2 116.6 117.1 117.6 118.1 118.6 + 119.0 119.5 120.0 120.5 121.0 121.5 121.9 122.4 122.9 123.4 123.9 124.4 124.9 125.4 125.9 126.4 126.9 127.4 + 127.9 128.4 128.9 129.4 129.9 130.4 130.9 131.4 132.0 132.5 133.0 133.5 134.0 134.5 135.0 135.6 136.1 136.6 + 137.1 137.7 138.2 138.7 139.2 139.8 140.3 140.8 141.4 141.9 142.4 143.0 143.5 144.0 144.6 145.1 145.6 146.2 + 146.7 147.3 147.8 148.4 148.9 149.4 150.0 150.5 151.1 151.6 152.2 152.7 153.3 153.9 154.4 155.0 155.5 156.1 + 156.6 157.2 157.8 158.2 ]plong + s[ 590.4 590.3 590.0 589.6 589.3 589.0 588.7 588.3 588.0 587.7 587.3 587.0 586.7 586.3 586.0 585.6 585.3 584.9 + 584.6 584.2 583.9 583.5 583.2 582.8 582.5 582.1 581.7 581.4 581.0 580.7 580.3 579.9 579.5 579.2 578.8 578.4 + 578.1 577.7 577.3 576.9 576.5 576.2 575.8 575.4 575.0 574.6 574.2 573.8 573.4 573.0 572.6 572.2 571.8 571.4 + 571.0 570.6 570.2 569.8 569.4 569.0 568.6 568.2 567.8 567.3 566.9 566.5 566.1 565.7 565.2 564.8 564.4 564.0 + 563.5 563.1 562.7 562.2 561.8 561.4 560.9 560.5 560.1 559.6 559.2 558.7 558.3 557.9 557.4 557.0 556.5 556.1 + 555.6 555.1 554.7 554.2 553.8 553.3 552.9 552.4 551.9 551.5 551.0 550.5 550.1 549.6 549.1 548.7 548.2 547.7 + 547.2 546.8 546.3 545.8 545.3 544.8 544.3 543.9 543.4 542.9 542.4 541.9 541.4 540.9 540.4 539.9 539.4 539.0 + 538.5 538.0 537.5 537.0 536.5 535.9 535.4 534.9 534.4 533.9 533.4 532.9 532.4 531.9 531.4 530.8 530.3 529.8 + 529.3 528.8 528.3 527.7 527.2 526.7 526.2 525.6 525.1 524.6 524.0 523.5 523.0 522.5 521.9 521.4 520.8 520.3 + 519.8 519.2 518.7 518.2 517.6 517.1 516.5 516.0 515.4 514.9 514.3 513.8 513.2 512.7 512.1 511.6 511.0 510.5 + 509.9 509.4 508.8 508.2 507.7 507.1 506.6 506.3 ][ 382.8 383.0 383.6 384.1 384.7 385.3 385.8 386.4 386.9 387.5 + 388.0 388.6 389.1 389.7 390.2 390.8 391.3 391.9 392.4 393.0 393.5 394.1 394.6 395.2 395.7 396.2 396.8 397.3 + 397.8 398.4 398.9 399.4 400.0 400.5 401.0 401.6 402.1 402.6 403.1 403.7 404.2 404.7 405.2 405.8 406.3 406.8 + 407.3 407.8 408.3 408.9 409.4 409.9 410.4 410.9 411.4 411.9 412.4 412.9 413.4 413.9 414.4 414.9 415.4 415.9 + 416.4 416.9 417.4 417.9 418.4 418.9 419.4 419.8 420.3 420.8 421.3 421.8 422.3 422.7 423.2 423.7 424.2 424.7 + 425.1 425.6 426.1 426.5 427.0 427.5 427.9 428.4 428.9 429.3 429.8 430.3 430.7 431.2 431.6 432.1 432.5 433.0 + 433.4 433.9 434.3 434.8 435.2 435.7 436.1 436.5 437.0 437.4 437.9 438.3 438.7 439.2 439.6 440.0 440.4 440.9 + 441.3 441.7 442.1 442.6 443.0 443.4 443.8 444.2 444.7 445.1 445.5 445.9 446.3 446.7 447.1 447.5 447.9 448.3 + 448.7 449.1 449.5 449.9 450.3 450.7 451.1 451.5 451.9 452.3 452.6 453.0 453.4 453.8 454.2 454.5 454.9 455.3 + 455.7 456.0 456.4 456.8 457.1 457.5 457.9 458.2 458.6 459.0 459.3 459.7 460.0 460.4 460.7 461.1 461.4 461.8 + 462.1 462.5 462.8 463.1 463.5 463.8 464.2 464.5 464.8 465.2 465.5 465.8 466.1 466.5 466.8 467.0 ]plong + s[ 281.6 281.3 280.8 280.2 279.6 279.1 278.5 278.0 277.4 276.8 276.3 275.7 275.2 274.6 274.1 273.5 273.0 272.4 + 271.9 271.4 270.8 270.3 269.7 269.2 268.6 268.1 267.6 267.0 266.5 266.0 265.4 264.9 264.4 263.8 263.3 262.8 + 262.2 261.7 261.2 260.7 260.1 259.6 259.1 258.6 258.1 257.6 257.0 256.5 256.0 255.5 255.0 254.5 254.0 253.5 + 252.9 252.4 251.9 251.4 250.9 250.4 249.9 249.4 248.9 248.4 247.9 247.4 246.9 246.5 246.0 245.5 245.0 244.5 + 244.0 243.5 243.0 242.6 242.1 241.6 241.1 240.7 240.2 239.7 239.2 238.8 238.3 237.8 237.3 236.9 236.4 235.9 + 235.5 235.0 234.6 234.1 233.6 233.2 232.7 232.3 231.8 231.4 230.9 230.5 230.0 229.6 229.1 228.7 228.3 227.8 + 227.4 226.9 226.5 226.1 225.6 225.2 224.8 224.3 223.9 223.5 223.1 222.6 222.2 221.8 221.4 221.0 220.5 220.1 + 219.7 219.3 218.9 218.5 218.1 217.7 217.3 216.8 216.4 216.0 215.6 215.2 214.9 214.5 214.1 213.7 213.3 212.9 + 212.5 212.1 211.7 211.3 211.0 210.6 210.2 209.8 209.5 209.1 208.7 208.3 208.0 207.6 207.2 206.9 206.5 206.1 + 205.8 205.4 205.1 204.7 204.4 204.0 203.6 203.3 202.9 202.6 202.3 201.9 201.6 201.2 200.9 200.6 200.2 199.9 + 199.6 199.5 ][ 467.0 466.8 466.5 466.1 465.8 465.5 465.2 464.8 464.5 464.2 463.8 463.5 463.1 462.8 462.5 462.1 + 461.8 461.4 461.1 460.7 460.4 460.0 459.7 459.3 459.0 458.6 458.2 457.9 457.5 457.1 456.8 456.4 456.0 455.7 + 455.3 454.9 454.5 454.2 453.8 453.4 453.0 452.6 452.3 451.9 451.5 451.1 450.7 450.3 449.9 449.5 449.1 448.7 + 448.3 447.9 447.5 447.1 446.7 446.3 445.9 445.5 445.1 444.7 444.2 443.8 443.4 443.0 442.6 442.1 441.7 441.3 + 440.9 440.4 440.0 439.6 439.2 438.7 438.3 437.9 437.4 437.0 436.5 436.1 435.7 435.2 434.8 434.3 433.9 433.4 + 433.0 432.5 432.1 431.6 431.2 430.7 430.3 429.8 429.3 428.9 428.4 427.9 427.5 427.0 426.5 426.1 425.6 425.1 + 424.7 424.2 423.7 423.2 422.7 422.3 421.8 421.3 420.8 420.3 419.8 419.4 418.9 418.4 417.9 417.4 416.9 416.4 + 415.9 415.4 414.9 414.4 413.9 413.4 412.9 412.4 411.9 411.4 410.9 410.4 409.9 409.4 408.9 408.3 407.8 407.3 + 406.8 406.3 405.8 405.2 404.7 404.2 403.7 403.1 402.6 402.1 401.6 401.0 400.5 400.0 399.4 398.9 398.4 397.8 + 397.3 396.8 396.2 395.7 395.2 394.6 394.1 393.5 393.0 392.4 391.9 391.3 390.8 390.2 389.7 389.1 388.6 388.0 + 387.5 386.9 386.4 386.2 ]plong + s[ 199.5 199.7 200.0 200.4 200.7 201.0 201.4 201.7 202.0 202.4 202.7 203.1 203.4 203.8 204.1 204.5 204.8 205.2 + 205.6 205.9 206.3 206.6 207.0 207.4 207.7 208.1 208.5 208.9 209.2 209.6 210.0 210.4 210.7 211.1 211.5 211.9 + 212.3 212.7 213.0 213.4 213.8 214.2 214.6 215.0 215.4 215.8 216.2 216.6 217.0 217.4 217.8 218.2 218.6 219.0 + 219.5 219.9 220.3 220.7 221.1 221.5 222.0 222.4 222.8 223.2 223.7 224.1 224.5 224.9 225.4 225.8 226.2 226.7 + 227.1 227.6 228.0 228.4 228.9 229.3 229.8 230.2 230.7 231.1 231.6 232.0 232.5 232.9 233.4 233.8 234.3 234.7 + 235.2 235.7 236.1 236.6 237.1 237.5 238.0 238.5 238.9 239.4 239.9 240.4 240.8 241.3 241.8 242.3 242.8 243.2 + 243.7 244.2 244.7 245.2 245.7 246.2 246.7 247.1 247.6 248.1 248.6 249.1 249.6 250.1 250.6 251.1 251.6 252.1 + 252.6 253.1 253.7 254.2 254.7 255.2 255.7 256.2 256.7 257.2 257.8 258.3 258.8 259.3 259.8 260.4 260.9 261.4 + 261.9 262.5 263.0 263.5 264.0 264.6 265.1 265.6 266.2 266.7 267.2 267.8 268.3 268.9 269.4 269.9 270.5 271.0 + 271.6 272.1 272.7 273.2 273.8 274.3 274.9 275.4 276.0 276.5 277.1 277.6 278.2 278.2 ][ 154.8 154.4 153.9 153.3 + 152.7 152.2 151.6 151.1 150.5 150.0 149.4 148.9 148.4 147.8 147.3 146.7 146.2 145.6 145.1 144.6 144.0 143.5 + 143.0 142.4 141.9 141.4 140.8 140.3 139.8 139.2 138.7 138.2 137.7 137.1 136.6 136.1 135.6 135.0 134.5 134.0 + 133.5 133.0 132.5 132.0 131.4 130.9 130.4 129.9 129.4 128.9 128.4 127.9 127.4 126.9 126.4 125.9 125.4 124.9 + 124.4 123.9 123.4 122.9 122.4 121.9 121.5 121.0 120.5 120.0 119.5 119.0 118.6 118.1 117.6 117.1 116.6 116.2 + 115.7 115.2 114.8 114.3 113.8 113.4 112.9 112.4 112.0 111.5 111.0 110.6 110.1 109.7 109.2 108.8 108.3 107.9 + 107.4 107.0 106.5 106.1 105.6 105.2 104.7 104.3 103.9 103.4 103.0 102.5 102.1 101.7 101.2 100.8 100.4 100.0 + 99.5 99.1 98.7 98.3 97.9 97.4 97.0 96.6 96.2 95.8 95.4 95.0 94.5 94.1 93.7 93.3 92.9 92.5 + 92.1 91.7 91.3 90.9 90.5 90.2 89.8 89.4 89.0 88.6 88.2 87.8 87.5 87.1 86.7 86.3 85.9 85.6 + 85.2 84.8 84.5 84.1 83.7 83.4 83.0 82.6 82.3 81.9 81.6 81.2 80.8 80.5 80.1 79.8 79.4 79.1 + 78.8 78.4 78.1 77.7 77.4 77.1 76.7 76.4 76.1 76.0 ]plong + s[ 509.6 510.1 510.7 511.3 511.8 512.4 512.9 513.5 514.0 514.6 515.1 515.7 516.2 516.7 517.3 517.8 518.4 518.9 + 519.5 520.0 520.5 521.1 521.6 522.1 522.7 523.2 523.7 524.3 524.8 525.3 525.8 526.4 526.9 527.4 527.9 528.5 + 529.0 529.5 530.0 530.5 531.1 531.6 532.1 532.6 533.1 533.6 534.1 534.6 535.1 535.6 536.1 536.7 537.2 537.7 + 538.2 538.7 539.2 539.6 540.1 540.6 541.1 541.6 542.1 542.6 543.1 543.6 544.1 544.5 545.0 545.5 546.0 546.5 + 546.9 547.4 547.9 548.4 548.8 549.3 549.8 550.3 550.7 551.2 551.7 552.1 552.6 553.0 553.5 554.0 ][ 76.0 76.3 + 76.7 77.0 77.3 77.7 78.0 78.3 78.7 79.0 79.4 79.7 80.1 80.4 80.8 81.1 81.5 81.8 82.2 82.6 + 82.9 83.3 83.6 84.0 84.4 84.8 85.1 85.5 85.9 86.2 86.6 87.0 87.4 87.8 88.1 88.5 88.9 89.3 + 89.7 90.1 90.5 90.9 91.3 91.7 92.1 92.5 92.9 93.3 93.7 94.1 94.5 94.9 95.3 95.7 96.1 96.5 + 96.9 97.4 97.8 98.2 98.6 99.0 99.5 99.9 100.3 100.7 101.2 101.6 102.0 102.5 102.9 103.3 103.8 104.2 + 104.6 105.1 105.5 106.0 106.4 106.9 107.3 107.8 108.2 108.7 109.1 109.6 110.0 110.5 ]plong + s[ 223.8 224.3 224.7 225.2 225.7 226.2 226.7 227.2 227.7 228.2 228.7 229.2 229.7 230.2 230.7 231.2 231.7 232.2 + 232.8 233.3 233.8 234.3 234.8 235.3 235.8 236.4 236.9 237.4 237.9 238.5 239.0 239.5 240.0 240.6 241.1 241.6 + 242.2 242.7 243.2 243.8 244.3 244.9 245.4 245.9 246.5 247.0 247.6 248.1 248.7 249.2 249.8 250.3 250.9 251.4 + 252.0 252.2 ][ 100.3 99.8 99.4 98.9 98.4 97.9 97.4 97.0 96.5 96.0 95.5 95.1 94.6 94.1 93.6 93.2 + 92.7 92.3 91.8 91.3 90.9 90.4 90.0 89.5 89.0 88.6 88.1 87.7 87.3 86.8 86.4 85.9 85.5 85.0 + 84.6 84.2 83.7 83.3 82.9 82.4 82.0 81.6 81.1 80.7 80.3 79.9 79.5 79.0 78.6 78.2 77.8 77.4 + 77.0 76.6 76.2 76.0 ]plong + s[ 535.7 536.0 536.6 537.1 537.7 538.2 538.8 539.3 539.9 540.4 541.0 541.5 542.0 542.6 543.1 543.7 544.2 544.7 + 545.3 545.8 546.4 546.9 547.4 547.9 548.5 549.0 549.5 550.1 550.6 551.1 551.6 552.1 552.7 553.2 553.7 554.2 + 554.7 555.2 555.7 556.3 556.8 557.3 557.8 558.3 558.8 559.3 559.8 560.3 560.8 561.3 561.8 562.3 562.7 563.2 + 563.7 564.2 564.7 565.2 565.7 566.2 566.6 567.1 567.6 568.1 568.5 569.0 569.5 570.0 570.4 570.9 571.4 571.8 + 572.3 572.7 573.2 573.7 574.1 574.6 575.0 575.5 575.9 576.4 576.8 577.3 577.7 578.2 578.6 579.1 579.5 579.9 + 580.4 580.8 581.2 581.7 582.1 582.5 583.0 583.4 583.8 584.2 584.7 585.1 585.5 585.9 586.3 586.7 587.1 587.6 + 588.0 588.4 588.8 589.2 589.6 590.0 590.4 590.4 ][ 76.0 76.2 76.6 77.1 77.5 77.9 78.3 78.7 79.1 79.5 + 80.0 80.4 80.8 81.2 81.7 82.1 82.5 82.9 83.4 83.8 84.2 84.7 85.1 85.6 86.0 86.4 86.9 87.3 + 87.8 88.2 88.7 89.1 89.6 90.0 90.5 91.0 91.4 91.9 92.3 92.8 93.3 93.7 94.2 94.7 95.1 95.6 + 96.1 96.6 97.0 97.5 98.0 98.5 99.0 99.5 99.9 100.4 100.9 101.4 101.9 102.4 102.9 103.4 103.9 104.4 + 104.9 105.4 105.9 106.4 106.9 107.4 107.9 108.4 108.9 109.4 109.9 110.4 111.0 111.5 112.0 112.5 113.0 113.6 + 114.1 114.6 115.1 115.7 116.2 116.7 117.2 117.8 118.3 118.8 119.4 119.9 120.5 121.0 121.5 122.1 122.6 123.2 + 123.7 124.3 124.8 125.3 125.9 126.4 127.0 127.6 128.1 128.7 129.2 129.8 130.3 130.9 131.5 131.5 ]plong + s[ 590.4 590.1 589.7 589.3 588.9 588.5 588.1 587.7 587.3 586.9 586.5 586.1 585.7 585.2 584.8 584.4 584.0 583.6 + 583.1 582.7 582.3 581.8 581.4 581.0 580.5 580.1 579.7 579.2 578.8 578.4 577.9 577.5 577.0 576.6 576.1 575.7 + 575.2 574.8 574.3 573.9 573.4 572.9 572.5 572.0 571.5 571.1 570.6 570.1 569.7 569.2 568.7 568.3 567.8 567.3 + 566.8 566.3 565.9 565.4 564.9 564.4 563.9 563.4 562.9 562.5 562.0 561.5 561.0 560.5 560.0 559.5 559.0 558.5 + 558.0 557.5 557.0 556.5 555.9 555.4 554.9 554.4 553.9 553.4 552.9 552.3 551.8 551.3 550.8 550.3 549.7 549.2 + 548.7 548.2 547.6 547.1 546.6 546.0 545.5 545.0 544.4 543.9 543.3 542.8 542.3 541.7 541.2 540.6 540.1 539.5 + 539.0 538.4 537.9 537.3 536.8 536.2 535.7 535.1 534.6 534.0 533.4 533.0 ][ 409.5 409.9 410.4 411.0 411.6 412.1 + 412.7 413.2 413.8 414.3 414.9 415.4 416.0 416.5 417.1 417.6 418.2 418.7 419.3 419.8 420.3 420.9 421.4 422.0 + 422.5 423.0 423.6 424.1 424.6 425.1 425.7 426.2 426.7 427.2 427.8 428.3 428.8 429.3 429.8 430.4 430.9 431.4 + 431.9 432.4 432.9 433.4 433.9 434.4 434.9 435.4 435.9 436.4 436.9 437.4 437.9 438.4 438.9 439.4 439.9 440.4 + 440.9 441.4 441.8 442.3 442.8 443.3 443.8 444.2 444.7 445.2 445.7 446.1 446.6 447.1 447.6 448.0 448.5 448.9 + 449.4 449.9 450.3 450.8 451.2 451.7 452.1 452.6 453.0 453.5 453.9 454.4 454.8 455.3 455.7 456.2 456.6 457.0 + 457.5 457.9 458.3 458.8 459.2 459.6 460.0 460.5 460.9 461.3 461.7 462.1 462.6 463.0 463.4 463.8 464.2 464.6 + 465.0 465.4 465.8 466.2 466.6 467.0 ]plong + s[ 254.9 254.4 253.9 253.3 252.8 252.2 251.7 251.1 250.5 250.0 249.4 248.9 248.3 247.8 247.2 246.7 246.2 245.6 + 245.1 244.5 244.0 243.5 242.9 242.4 241.8 241.3 240.8 240.3 239.7 239.2 238.7 238.1 237.6 237.1 236.6 236.1 + 235.5 235.0 234.5 234.0 233.5 233.0 232.4 231.9 231.4 230.9 230.4 229.9 229.4 228.9 228.4 227.9 227.4 226.9 + 226.4 225.9 225.4 224.9 224.4 224.0 223.5 223.0 222.5 222.0 221.5 221.1 220.6 220.1 219.6 219.2 218.7 218.2 + 217.7 217.3 216.8 216.3 215.9 215.4 214.9 214.5 214.0 213.6 213.1 212.7 212.2 211.8 211.3 210.9 210.4 210.0 + 209.5 209.1 208.6 208.2 207.8 207.3 206.9 206.5 206.0 205.6 205.2 204.8 204.3 203.9 203.5 203.1 202.6 202.2 + 201.8 201.4 201.0 200.6 200.2 199.8 199.5 ][ 467.0 466.6 466.2 465.8 465.4 465.0 464.6 464.2 463.8 463.4 463.0 + 462.6 462.1 461.7 461.3 460.9 460.5 460.0 459.6 459.2 458.8 458.3 457.9 457.5 457.0 456.6 456.2 455.7 455.3 + 454.8 454.4 453.9 453.5 453.0 452.6 452.1 451.7 451.2 450.8 450.3 449.9 449.4 448.9 448.5 448.0 447.6 447.1 + 446.6 446.1 445.7 445.2 444.7 444.2 443.8 443.3 442.8 442.3 441.8 441.4 440.9 440.4 439.9 439.4 438.9 438.4 + 437.9 437.4 436.9 436.4 435.9 435.4 434.9 434.4 433.9 433.4 432.9 432.4 431.9 431.4 430.9 430.4 429.8 429.3 + 428.8 428.3 427.8 427.2 426.7 426.2 425.7 425.1 424.6 424.1 423.6 423.0 422.5 422.0 421.4 420.9 420.3 419.8 + 419.3 418.7 418.2 417.6 417.1 416.5 416.0 415.4 414.9 414.3 413.8 413.2 412.7 412.3 ]plong + s[ 199.5 199.5 199.9 200.3 200.7 201.1 201.6 202.0 202.4 202.8 203.2 203.7 204.1 204.5 204.9 205.4 205.8 206.2 + 206.6 207.1 207.5 207.9 208.4 208.8 209.3 209.7 210.2 210.6 211.0 211.5 211.9 212.4 212.8 213.3 213.8 214.2 + 214.7 215.1 215.6 216.1 216.5 217.0 217.5 217.9 218.4 218.9 219.3 219.8 220.3 220.8 221.2 221.7 222.2 222.7 + 223.2 223.7 224.2 224.6 225.1 225.6 226.1 226.6 227.1 227.6 228.1 228.6 229.1 229.6 230.1 230.6 231.1 231.6 + 232.1 232.6 233.2 233.7 234.2 234.7 235.2 235.7 236.3 236.8 237.3 237.8 238.4 238.9 239.4 239.9 240.5 241.0 + 241.5 242.1 242.6 243.1 243.7 244.2 244.7 245.3 245.8 246.4 246.9 247.5 248.0 248.6 249.1 249.7 250.2 250.8 + 251.3 251.9 252.2 ][ 128.7 128.7 128.1 127.6 127.0 126.4 125.9 125.3 124.8 124.3 123.7 123.2 122.6 122.1 121.5 + 121.0 120.5 119.9 119.4 118.8 118.3 117.8 117.2 116.7 116.2 115.7 115.1 114.6 114.1 113.6 113.0 112.5 112.0 + 111.5 111.0 110.4 109.9 109.4 108.9 108.4 107.9 107.4 106.9 106.4 105.9 105.4 104.9 104.4 103.9 103.4 102.9 + 102.4 101.9 101.4 100.9 100.4 99.9 99.5 99.0 98.5 98.0 97.5 97.0 96.6 96.1 95.6 95.1 94.7 94.2 + 93.7 93.3 92.8 92.3 91.9 91.4 91.0 90.5 90.0 89.6 89.1 88.7 88.2 87.8 87.3 86.9 86.4 86.0 + 85.6 85.1 84.7 84.2 83.8 83.4 82.9 82.5 82.1 81.7 81.2 80.8 80.4 80.0 79.5 79.1 78.7 78.3 + 77.9 77.5 77.1 76.6 76.2 76.0 ]plong + s[ 535.7 535.9 536.4 537.0 537.6 538.1 538.7 539.2 539.8 540.3 540.9 541.4 541.9 542.5 543.0 543.6 544.1 544.6 + 545.2 545.7 546.2 546.8 547.3 547.8 548.4 548.9 549.4 549.9 550.5 551.0 551.5 552.0 552.6 553.1 553.6 554.1 + 554.6 555.1 555.6 556.1 556.7 557.2 557.7 558.2 558.7 559.2 559.7 560.2 560.7 561.2 561.7 562.2 562.7 563.1 + 563.6 564.1 ][ 76.0 76.2 76.6 77.0 77.4 77.8 78.2 78.6 79.0 79.5 79.9 80.3 80.7 81.1 81.6 82.0 + 82.4 82.9 83.3 83.7 84.2 84.6 85.0 85.5 85.9 86.4 86.8 87.3 87.7 88.1 88.6 89.0 89.5 90.0 + 90.4 90.9 91.3 91.8 92.3 92.7 93.2 93.6 94.1 94.6 95.1 95.5 96.0 96.5 97.0 97.4 97.9 98.4 + 98.9 99.4 99.8 100.3 ]plong + s[ 213.6 214.1 214.6 215.2 215.7 216.2 216.7 217.3 217.8 218.3 218.8 219.4 219.9 220.4 221.0 221.5 222.0 222.6 + 223.1 223.7 224.2 224.8 225.3 225.9 226.4 227.0 227.5 228.1 228.6 229.0 ][ 90.2 89.7 89.1 88.6 88.1 87.6 + 87.1 86.6 86.1 85.6 85.1 84.6 84.1 83.6 83.1 82.6 82.1 81.6 81.1 80.6 80.1 79.7 79.2 78.7 + 78.2 77.7 77.3 76.8 76.3 76.0 ]plong + s[ 558.9 559.4 559.9 560.5 561.0 561.6 562.1 562.7 563.2 563.8 564.3 564.9 565.4 565.9 566.5 567.0 567.6 568.1 + 568.6 569.1 569.7 570.2 570.7 571.3 571.8 572.3 572.8 573.3 573.9 574.4 574.9 575.4 575.9 576.4 576.9 577.4 + 578.0 578.5 579.0 579.5 580.0 580.5 581.0 581.5 581.9 582.4 582.9 583.4 583.9 584.4 584.9 585.4 585.8 586.3 + 586.8 587.3 587.8 588.2 588.7 589.2 589.6 590.1 590.4 ][ 76.0 76.4 76.9 77.4 77.8 78.3 78.8 79.3 79.8 + 80.2 80.7 81.2 81.7 82.2 82.7 83.2 83.7 84.2 84.7 85.2 85.7 86.2 86.7 87.2 87.7 88.2 88.7 + 89.2 89.8 90.3 90.8 91.3 91.8 92.4 92.9 93.4 93.9 94.5 95.0 95.5 96.0 96.6 97.1 97.6 98.2 + 98.7 99.3 99.8 100.3 100.9 101.4 102.0 102.5 103.1 103.6 104.2 104.7 105.3 105.9 106.4 107.0 107.5 107.9 ]plong + s[ 590.4 590.3 589.8 589.4 588.9 588.4 587.9 587.5 587.0 586.5 586.0 585.6 585.1 584.6 584.1 583.6 583.1 582.6 + 582.1 581.7 581.2 580.7 580.2 579.7 579.2 578.7 578.2 577.7 577.1 576.6 576.1 575.6 575.1 574.6 574.1 573.6 + 573.0 572.5 572.0 571.5 570.9 570.4 569.9 569.4 568.8 568.3 567.8 567.2 566.7 566.2 565.6 565.1 564.5 564.0 + 563.4 562.9 562.4 561.8 561.3 560.7 560.1 559.6 559.0 558.5 557.9 557.4 556.8 556.6 ][ 433.1 433.3 433.8 434.4 + 434.9 435.5 436.0 436.6 437.2 437.7 438.3 438.8 439.4 439.9 440.4 441.0 441.5 442.1 442.6 443.2 443.7 444.2 + 444.8 445.3 445.8 446.3 446.9 447.4 447.9 448.5 449.0 449.5 450.0 450.5 451.0 451.6 452.1 452.6 453.1 453.6 + 454.1 454.6 455.1 455.6 456.1 456.6 457.1 457.6 458.1 458.6 459.1 459.6 460.1 460.6 461.1 461.5 462.0 462.5 + 463.0 463.5 463.9 464.4 464.9 465.4 465.8 466.3 466.8 467.0 ]plong + s[ 231.3 231.1 230.5 230.0 229.4 228.8 228.3 227.7 227.2 226.6 226.1 225.5 225.0 224.4 223.9 223.3 222.8 222.3 + 221.7 221.2 220.7 220.1 219.6 219.1 218.5 218.0 217.5 216.9 216.4 215.9 215.4 214.8 214.3 213.8 213.3 212.8 + 212.3 211.8 211.2 210.7 210.2 209.7 209.2 208.7 208.2 207.7 207.2 206.7 206.2 205.7 205.2 204.8 204.3 203.8 + 203.3 202.8 202.3 201.8 201.4 200.9 200.4 199.9 199.5 199.5 ][ 467.0 466.8 466.3 465.8 465.4 464.9 464.4 463.9 + 463.5 463.0 462.5 462.0 461.5 461.1 460.6 460.1 459.6 459.1 458.6 458.1 457.6 457.1 456.6 456.1 455.6 455.1 + 454.6 454.1 453.6 453.1 452.6 452.1 451.6 451.0 450.5 450.0 449.5 449.0 448.5 447.9 447.4 446.9 446.3 445.8 + 445.3 444.8 444.2 443.7 443.2 442.6 442.1 441.5 441.0 440.4 439.9 439.4 438.8 438.3 437.7 437.2 436.6 436.0 + 435.5 435.5 ]plong + s[ 199.5 199.7 200.1 200.6 201.1 201.6 202.0 202.5 203.0 203.5 204.0 204.5 204.9 205.4 205.9 206.4 206.9 207.4 + 207.9 208.4 208.9 209.4 209.9 210.4 210.9 211.4 212.0 212.5 213.0 213.5 214.0 214.5 215.1 215.6 216.1 216.6 + 217.1 217.7 218.2 218.7 219.3 219.8 220.3 220.9 221.4 221.9 222.5 223.0 223.6 224.1 224.7 225.2 225.7 226.3 + 226.8 227.4 228.0 228.5 229.0 ][ 105.5 105.3 104.7 104.2 103.6 103.1 102.5 102.0 101.4 100.9 100.3 99.8 99.3 + 98.7 98.2 97.6 97.1 96.6 96.0 95.5 95.0 94.5 93.9 93.4 92.9 92.4 91.8 91.3 90.8 90.3 89.8 + 89.2 88.7 88.2 87.7 87.2 86.7 86.2 85.7 85.2 84.7 84.2 83.7 83.2 82.7 82.2 81.7 81.2 80.7 + 80.2 79.8 79.3 78.8 78.3 77.8 77.4 76.9 76.4 76.0 ]plong + s[ 558.9 559.3 559.8 560.4 560.9 561.5 562.0 562.6 563.1 563.7 564.2 564.8 565.3 565.8 566.4 566.9 567.4 568.0 + 568.5 569.0 569.6 570.1 570.6 571.2 571.7 572.2 572.7 573.2 573.8 574.3 ][ 76.0 76.3 76.8 77.3 77.7 78.2 + 78.7 79.2 79.7 80.1 80.6 81.1 81.6 82.1 82.6 83.1 83.6 84.1 84.6 85.1 85.6 86.1 86.6 87.1 + 87.6 88.1 88.6 89.1 89.7 90.2 ]plong + s[ 203.4 204.0 204.5 205.1 205.6 206.2 206.7 207.3 207.5 ][ 80.0 79.5 78.9 78.4 77.8 77.3 76.8 76.2 76.0 +]plong + s[ 580.4 580.7 581.2 581.8 582.4 582.9 583.5 584.0 584.5 585.1 585.6 586.2 586.7 587.2 587.8 588.3 588.9 589.4 + 589.9 590.4 ][ 76.0 76.3 76.9 77.4 78.0 78.5 79.0 79.6 80.1 80.7 81.2 81.8 82.3 82.9 83.4 84.0 + 84.5 85.1 85.7 86.2 ]plong + s[ 590.4 590.1 589.6 589.1 588.5 588.0 587.5 586.9 586.4 585.8 585.3 584.8 584.2 583.7 583.1 582.6 582.0 581.5 + 580.9 580.4 579.8 579.2 578.7 578.3 ][ 454.8 455.1 455.7 456.3 456.8 457.4 457.9 458.5 459.0 459.6 460.1 460.7 + 461.2 461.8 462.3 462.8 463.4 463.9 464.5 465.0 465.5 466.1 466.6 467.0 ]plong + s[ 209.6 209.2 208.6 208.1 207.5 207.0 206.4 205.9 205.3 204.8 204.2 203.7 203.1 202.6 202.0 201.5 201.0 200.4 + 199.9 199.5 ][ 467.0 466.6 466.1 465.5 465.0 464.5 463.9 463.4 462.8 462.3 461.8 461.2 460.7 460.1 459.6 459.0 + 458.5 457.9 457.4 456.9 ]plong + s[ 199.5 199.6 200.1 200.6 201.2 201.7 202.3 202.8 203.3 203.9 204.4 205.0 205.5 206.1 206.6 207.2 207.5 ][ 84.1 + 84.0 83.4 82.9 82.3 81.8 81.2 80.7 80.1 79.6 79.0 78.5 78.0 77.4 76.9 76.3 76.0 ]plong + s[ 580.4 580.6 581.1 581.7 582.2 582.8 583.3 583.9 584.4 ][ 76.0 76.2 76.8 77.3 77.8 78.4 78.9 79.5 80.0 +]plong + 419.2 78.7 419.5 76.0 2 pls + 445.4 78.7 446.1 76.0 2 pls + 473.5 78.5 474.5 76.0 2 pls + 504.9 78.4 506.2 76.0 2 pls + 541.5 78.2 543.2 76.0 2 pls + 246.4 78.2 244.7 76.0 2 pls + 283.0 78.4 281.6 76.0 2 pls + 314.4 78.5 313.4 76.0 2 pls + 342.5 78.7 341.8 76.0 2 pls + 368.7 78.7 368.3 76.0 2 pls + 199.9 251.5 199.5 254.2 2 pls + 199.5 289.5 199.5 286.8 2 pls + 200.3 221.5 199.5 224.1 2 pls + 199.9 319.7 199.5 317.0 2 pls + 200.4 212.1 199.5 214.6 2 pls + 200.0 329.0 199.5 326.4 2 pls + 200.5 204.0 199.5 206.5 2 pls + 200.1 337.2 199.5 334.5 2 pls + 200.6 196.7 199.5 199.2 2 pls + 200.2 344.4 199.5 341.8 2 pls + 200.7 190.1 199.5 192.5 2 pls + 200.3 351.1 199.5 348.5 2 pls + 200.8 178.1 199.5 180.5 2 pls + 200.4 363.1 199.5 360.6 2 pls + 200.8 172.5 199.5 174.9 2 pls + 200.5 368.7 199.5 366.1 2 pls + 200.9 167.3 199.5 169.6 2 pls + 200.5 373.9 199.5 371.4 2 pls + 200.9 162.2 199.5 164.5 2 pls + 200.6 379.0 199.5 376.5 2 pls + 201.0 157.3 199.5 159.6 2 pls + 200.6 383.9 199.5 381.4 2 pls + 201.1 148.0 199.5 150.2 2 pls + 200.7 393.2 199.5 390.8 2 pls + 201.1 143.5 199.5 145.7 2 pls + 200.7 397.7 199.5 395.3 2 pls + 201.1 139.2 199.5 141.3 2 pls + 200.8 402.1 199.5 399.7 2 pls + 201.2 134.9 199.5 137.1 2 pls + 200.8 406.3 199.5 404.0 2 pls + 201.2 130.8 199.5 132.9 2 pls + 200.9 410.5 199.5 408.2 2 pls + 201.3 122.7 199.5 124.7 2 pls + 200.9 418.6 199.5 416.3 2 pls + 201.3 118.7 199.5 120.8 2 pls + 200.9 422.5 199.5 420.2 2 pls + 201.3 114.9 199.5 116.9 2 pls + 201.0 426.4 199.5 424.1 2 pls + 201.3 111.1 199.5 113.0 2 pls + 201.0 430.2 199.5 428.0 2 pls + 201.4 107.3 199.5 109.3 2 pls + 201.0 434.0 199.5 431.8 2 pls + 201.4 99.9 199.5 101.9 2 pls + 201.1 441.4 199.5 439.2 2 pls + 201.4 96.3 199.5 98.2 2 pls + 201.1 445.0 199.5 442.8 2 pls + 201.4 92.8 199.5 94.6 2 pls + 201.1 448.5 199.5 446.4 2 pls + 201.5 89.2 199.5 91.1 2 pls + 201.1 452.1 199.5 449.9 2 pls + 201.5 85.7 199.5 87.6 2 pls + 201.2 455.6 199.5 453.4 2 pls + 201.5 78.8 199.5 80.7 2 pls + 201.2 462.5 199.5 460.4 2 pls + 200.9 76.0 199.5 77.2 2 pls + 201.2 465.9 199.5 463.8 2 pls + 543.0 464.8 544.7 467.0 2 pls + 506.0 464.6 507.4 467.0 2 pls + 474.3 464.4 475.3 467.0 2 pls + 445.9 464.3 446.6 467.0 2 pls + 419.4 464.2 419.8 467.0 2 pls + 368.4 464.2 368.1 467.0 2 pls + 342.0 464.3 341.3 467.0 2 pls + 313.6 464.4 312.6 467.0 2 pls + 281.9 464.6 280.5 467.0 2 pls + 244.9 464.8 243.2 467.0 2 pls + 590.1 230.5 590.4 233.2 2 pls + 589.7 310.4 590.4 307.8 2 pls + 589.9 219.3 590.4 222.0 2 pls + 589.5 321.6 590.4 319.0 2 pls + 589.8 210.1 590.4 212.8 2 pls + 589.4 330.8 590.4 328.2 2 pls + 589.7 202.2 590.4 204.8 2 pls + 589.3 338.7 590.4 336.2 2 pls + 589.6 195.0 590.4 197.6 2 pls + 589.2 345.9 590.4 343.4 2 pls + 589.5 182.2 590.4 184.8 2 pls + 589.1 358.6 590.4 356.2 2 pls + 589.4 176.4 590.4 179.0 2 pls + 589.0 364.4 590.4 362.0 2 pls + 589.4 170.9 590.4 173.5 2 pls + 589.0 369.9 590.4 367.5 2 pls + 589.3 165.7 590.4 168.2 2 pls + 588.9 375.2 590.4 372.8 2 pls + 589.3 160.6 590.4 163.1 2 pls + 588.9 380.2 590.4 377.9 2 pls + 589.2 151.0 590.4 153.4 2 pls + 588.8 389.8 590.4 387.6 2 pls + 589.1 146.4 590.4 148.8 2 pls + 588.8 394.4 590.4 392.2 2 pls + 589.1 141.9 590.4 144.3 2 pls + 588.7 398.9 590.4 396.7 2 pls + 589.1 137.6 590.4 140.0 2 pls + 588.7 403.2 590.4 401.1 2 pls + 589.0 133.3 590.4 135.7 2 pls + 588.7 407.5 590.4 405.3 2 pls + 589.0 125.0 590.4 127.4 2 pls + 588.6 415.7 590.4 413.6 2 pls + 588.9 121.0 590.4 123.4 2 pls + 588.6 419.7 590.4 417.7 2 pls + 588.9 117.1 590.4 119.4 2 pls + 588.6 423.6 590.4 421.6 2 pls + 588.9 113.2 590.4 115.5 2 pls + 588.5 427.5 590.4 425.5 2 pls + 588.8 109.4 590.4 111.7 2 pls + 588.5 431.3 590.4 429.3 2 pls + 588.8 101.9 590.4 104.2 2 pls + 588.5 438.8 590.4 436.9 2 pls + 588.8 98.3 590.4 100.5 2 pls + 588.4 442.5 590.4 440.5 2 pls + 588.7 94.6 590.4 96.8 2 pls + 588.4 446.1 590.4 444.2 2 pls + 588.7 91.1 590.4 93.2 2 pls + 588.4 449.7 590.4 447.8 2 pls + 588.7 87.5 590.4 89.7 2 pls + 588.4 453.2 590.4 451.3 2 pls + 588.7 80.6 590.4 82.7 2 pls + 588.4 460.1 590.4 458.3 2 pls + 588.6 77.1 590.4 79.2 2 pls + 588.3 463.6 590.4 461.8 2 pls + 588.3 467.0 590.4 465.2 2 pls + s[ 199.5 227.4 255.3 283.2 311.1 339.1 367.0 394.9 422.8 450.8 478.7 506.6 534.5 562.5 590.4 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 590.4 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 590.4 562.5 534.5 506.6 478.7 450.8 422.8 394.9 367.0 339.1 311.1 283.2 255.3 227.4 199.5 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 199.5 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + s[ 377.5 377.5 377.5 377.6 377.6 377.7 377.7 377.7 377.8 ][ 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 +]plong + s[ 377.8 377.8 377.9 377.9 377.9 377.9 378.0 378.0 378.1 378.1 378.1 378.2 378.2 378.2 378.3 378.3 378.3 378.4 + 378.4 378.4 378.5 378.5 378.6 378.6 378.6 378.7 378.7 378.7 378.8 ][ 102.5 102.5 102.5 102.4 102.4 102.4 102.4 + 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 + 102.4 102.4 102.4 102.3 ]plong + s[ 378.8 378.8 378.8 378.9 378.9 379.0 379.0 379.0 379.1 379.1 379.1 379.2 379.2 379.2 379.3 379.3 379.3 379.4 + 379.4 379.5 379.5 379.5 379.6 379.6 379.6 379.7 379.7 379.7 ][ 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 + 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 + 102.3 102.3 ]plong + s[ 379.7 379.8 379.8 379.9 379.9 379.9 380.0 380.0 380.0 380.1 380.1 380.1 380.2 380.2 380.2 380.3 380.3 380.4 + 380.4 380.4 380.5 380.5 380.6 380.6 380.6 380.6 380.7 380.7 ][ 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 + 102.3 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 + 102.2 102.2 ]plong + s[ 380.7 380.8 380.8 380.8 380.9 380.9 381.0 381.0 381.0 381.0 381.1 381.1 381.1 381.2 381.2 381.3 381.3 381.3 + 381.4 381.4 381.5 381.5 381.5 381.6 381.6 381.6 381.7 381.7 ][ 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 + 102.2 102.2 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 + 102.1 102.1 ]plong + s[ 381.7 381.7 381.8 381.8 381.9 381.9 381.9 382.0 382.0 382.0 382.1 382.1 382.1 382.2 382.2 382.2 382.3 382.3 + 382.4 382.4 382.4 382.5 382.5 382.6 382.6 382.6 382.7 ][ 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 + 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 ]plong + s[ 382.7 382.7 382.7 382.8 382.8 382.8 382.9 382.9 382.9 383.0 383.0 383.1 383.1 383.1 383.2 383.2 383.3 383.3 + 383.3 383.3 383.4 383.4 383.5 383.5 383.5 383.6 383.6 383.7 ][ 102.1 102.0 102.0 102.0 102.0 102.0 102.0 102.0 + 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 + 102.0 102.0 ]plong + s[ 383.7 383.7 383.7 383.8 383.8 383.9 383.9 383.9 383.9 384.0 384.0 384.0 384.1 384.1 384.2 384.2 384.2 384.3 + 384.3 384.4 384.4 384.4 384.5 384.5 384.6 384.6 384.6 ][ 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 + 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 ]plong + s[ 384.6 384.7 384.7 384.7 384.8 384.8 384.8 384.9 384.9 384.9 385.0 385.0 385.1 385.1 385.1 385.2 385.2 385.3 + 385.3 385.3 385.4 385.4 385.4 385.5 385.5 385.5 385.6 385.6 ][ 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 + 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 + 101.9 101.9 ]plong + s[ 385.6 385.7 385.7 385.7 385.8 385.8 385.8 385.9 385.9 386.0 386.0 386.0 386.1 386.1 386.2 386.2 386.2 386.2 + 386.3 386.3 386.4 386.4 386.4 386.5 386.5 386.6 386.6 ][ 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 + 101.9 101.9 101.9 101.9 101.9 101.9 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 ]plong + s[ 386.6 386.6 386.7 386.7 386.8 386.8 386.8 386.9 386.9 386.9 387.0 387.0 387.1 387.1 387.1 387.1 387.2 387.2 + 387.3 387.3 387.3 387.4 387.4 387.5 387.5 387.5 387.6 ][ 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 + 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 ]plong + s[ 387.6 387.6 387.7 387.7 387.7 387.8 387.8 387.8 387.9 387.9 388.0 388.0 388.0 388.1 388.1 388.1 388.2 388.2 + 388.2 388.3 388.3 388.4 388.4 388.4 388.5 388.5 388.6 ][ 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 + 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 388.6 388.6 388.6 388.7 388.7 388.8 388.8 388.8 388.9 388.9 388.9 389.0 389.0 389.1 389.1 389.1 389.2 389.2 + 389.3 389.3 389.3 389.4 389.4 389.4 389.5 389.5 389.5 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 389.5 389.6 389.6 389.7 389.7 389.7 389.8 389.8 389.8 389.9 389.9 390.0 390.0 390.0 390.1 390.1 390.2 390.2 + 390.2 390.3 390.3 390.4 390.4 390.4 390.5 390.5 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 390.5 390.6 390.6 390.6 390.7 390.7 390.7 390.8 390.8 390.9 390.9 390.9 391.0 391.0 391.1 391.1 391.1 391.2 + 391.2 391.3 391.3 391.3 391.3 391.4 391.4 391.5 391.5 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 391.5 391.5 391.6 391.6 391.7 391.7 391.7 391.8 391.8 391.8 391.9 391.9 392.0 392.0 392.0 392.1 392.1 392.2 + 392.2 392.2 392.3 392.3 392.4 392.4 392.4 392.5 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 392.5 392.5 392.6 392.6 392.6 392.7 392.7 392.7 392.8 392.8 392.9 392.9 392.9 393.0 393.0 393.1 393.1 393.1 + 393.2 393.2 393.3 393.3 393.3 393.4 393.4 393.5 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 393.5 393.5 393.5 393.6 393.6 393.6 393.7 393.7 393.8 393.8 393.8 393.9 393.9 394.0 394.0 394.0 394.1 394.1 + 394.2 394.2 394.2 394.3 394.3 394.4 394.4 394.4 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 394.4 394.5 394.5 394.6 394.6 394.6 394.7 394.7 394.7 394.8 394.8 394.9 394.9 394.9 395.0 395.0 395.1 395.1 + 395.1 395.2 395.2 395.3 395.3 395.3 395.4 395.4 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 395.4 395.5 395.5 395.5 395.6 395.6 395.6 395.7 395.7 395.8 395.8 395.8 395.9 395.9 396.0 396.0 396.0 396.1 + 396.1 396.2 396.2 396.2 396.3 396.3 396.4 396.4 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 396.4 396.4 396.5 396.5 396.5 396.6 396.6 396.6 396.7 396.7 396.7 396.8 396.8 396.9 396.9 396.9 397.0 397.0 + 397.1 397.1 397.1 397.2 397.2 397.3 397.3 397.3 397.4 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 397.4 397.4 397.5 397.5 397.5 397.6 397.6 397.6 397.7 397.7 397.8 397.8 397.8 397.9 397.9 398.0 398.0 398.0 + 398.1 398.1 398.2 398.2 398.2 398.3 398.3 398.4 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 398.4 398.4 398.4 398.5 398.5 398.5 398.5 398.6 398.6 398.7 398.7 398.7 398.8 398.8 398.9 398.9 398.9 399.0 + 399.0 399.1 399.1 399.1 399.2 399.2 399.3 399.3 399.3 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 ]plong + s[ 399.3 399.4 399.4 399.4 399.5 399.5 399.6 399.6 399.6 399.7 399.7 399.7 399.8 399.8 399.8 399.9 399.9 400.0 + 400.0 400.0 400.1 400.1 400.2 400.2 400.2 400.3 400.3 ][ 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 101.7 + 101.7 101.7 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 ]plong + s[ 400.3 400.4 400.4 400.4 400.5 400.5 400.5 400.6 400.6 400.7 400.7 400.7 400.8 400.8 400.8 400.9 400.9 400.9 + 401.0 401.0 401.1 401.1 401.1 401.2 401.2 401.3 401.3 ][ 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 + 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 ]plong + s[ 401.3 401.3 401.4 401.4 401.4 401.5 401.5 401.6 401.6 401.6 401.7 401.7 401.7 401.8 401.8 401.8 401.9 401.9 + 402.0 402.0 402.0 402.1 402.1 402.2 402.2 402.2 402.3 ][ 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 101.8 + 101.8 101.8 101.8 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 ]plong + s[ 402.3 402.3 402.3 402.4 402.4 402.5 402.5 402.5 402.5 402.6 402.6 402.7 402.7 402.7 402.8 402.8 402.9 402.9 + 402.9 403.0 403.0 403.1 403.1 403.1 403.2 403.2 403.2 403.3 ][ 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 + 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 + 101.9 101.9 ]plong + s[ 403.3 403.3 403.3 403.4 403.4 403.4 403.5 403.5 403.6 403.6 403.6 403.7 403.7 403.8 403.8 403.8 403.9 403.9 + 403.9 404.0 404.0 404.0 404.1 404.1 404.2 404.2 404.2 ][ 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 + 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 101.9 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 ]plong + s[ 404.2 404.3 404.3 404.3 404.4 404.4 404.5 404.5 404.5 404.6 404.6 404.6 404.7 404.7 404.7 404.8 404.8 404.9 + 404.9 404.9 405.0 405.0 405.1 405.1 405.1 405.2 405.2 405.2 ][ 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 + 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 102.0 + 102.0 102.1 ]plong + s[ 405.2 405.3 405.3 405.3 405.4 405.4 405.4 405.5 405.5 405.6 405.6 405.6 405.7 405.7 405.7 405.8 405.8 405.8 + 405.9 405.9 406.0 406.0 406.0 406.1 406.1 406.2 406.2 ][ 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 + 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 ]plong + s[ 406.2 406.2 406.3 406.3 406.3 406.3 406.4 406.4 406.5 406.5 406.5 406.6 406.6 406.7 406.7 406.7 406.8 406.8 + 406.8 406.9 406.9 406.9 407.0 407.0 407.1 407.1 407.1 407.2 ][ 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 + 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.1 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 + 102.2 102.2 ]plong + s[ 407.2 407.2 407.2 407.3 407.3 407.3 407.4 407.4 407.4 407.5 407.5 407.6 407.6 407.6 407.7 407.7 407.8 407.8 + 407.8 407.8 407.9 407.9 408.0 408.0 408.0 408.1 408.1 408.2 ][ 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 + 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.2 102.3 102.3 102.3 102.3 102.3 102.3 102.3 + 102.3 102.3 ]plong + s[ 408.2 408.2 408.2 408.3 408.3 408.3 408.3 408.4 408.4 408.5 408.5 408.5 408.6 408.6 408.7 408.7 408.7 408.7 + 408.8 408.8 408.9 408.9 408.9 409.0 409.0 409.1 409.1 409.1 ][ 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 + 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 102.3 + 102.3 102.3 ]plong + s[ 409.1 409.1 409.2 409.2 409.2 409.3 409.3 409.4 409.4 409.4 409.5 409.5 409.6 409.6 409.6 409.6 409.7 409.7 + 409.8 409.8 409.8 409.9 409.9 410.0 410.0 410.0 410.0 410.1 410.1 ][ 102.3 102.4 102.4 102.4 102.4 102.4 102.4 + 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 102.4 + 102.4 102.5 102.5 102.5 ]plong + s[ 410.4 410.4 410.3 410.3 410.3 410.2 410.2 410.1 410.1 ][ 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 +]plong + s[ 369.4 369.5 369.5 369.5 369.6 369.6 369.7 369.7 369.7 369.7 369.8 369.8 369.9 369.9 369.9 ][ 103.5 103.4 103.4 + 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 ]plong + s[ 369.9 370.0 370.0 370.0 370.1 370.1 370.1 370.2 370.2 370.3 370.3 370.3 370.3 370.4 370.4 370.4 370.5 370.5 + 370.5 370.6 370.6 370.6 370.7 370.7 370.8 370.8 370.8 370.8 370.9 370.9 ][ 103.4 103.4 103.4 103.4 103.4 103.4 + 103.4 103.4 103.4 103.4 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 + 103.3 103.3 103.3 103.2 103.2 103.2 ]plong + s[ 370.9 371.0 371.0 371.0 371.1 371.1 371.1 371.2 371.2 371.2 371.3 371.3 371.3 371.4 371.4 371.4 371.5 371.5 + 371.5 371.6 371.6 371.7 371.7 371.7 371.7 371.8 371.8 371.9 371.9 ][ 103.2 103.2 103.2 103.2 103.2 103.2 103.2 + 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.1 103.1 103.1 + 103.1 103.1 103.1 103.1 ]plong + s[ 371.9 371.9 372.0 372.0 372.0 372.1 372.1 372.1 372.2 372.2 372.3 372.3 372.3 372.3 372.4 372.4 372.4 372.5 + 372.5 372.6 372.6 372.6 372.6 372.7 372.7 372.8 372.8 372.8 372.9 ][ 103.1 103.1 103.1 103.1 103.1 103.1 103.1 + 103.1 103.1 103.1 103.1 103.1 103.1 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.0 + 103.0 103.0 103.0 103.0 ]plong + s[ 372.9 372.9 372.9 373.0 373.0 373.0 373.1 373.1 373.2 373.2 373.2 373.2 373.3 373.3 373.3 373.4 373.4 373.5 + 373.5 373.5 373.6 373.6 373.6 373.7 373.7 373.7 373.8 373.8 373.9 ][ 103.0 103.0 103.0 103.0 103.0 103.0 103.0 + 103.0 103.0 103.0 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 + 102.9 102.9 102.9 102.9 ]plong + s[ 373.9 373.9 373.9 373.9 374.0 374.0 374.1 374.1 374.1 374.2 374.2 374.2 374.2 374.3 374.3 374.4 374.4 374.4 + 374.5 374.5 374.5 374.6 374.6 374.6 374.7 374.7 374.8 374.8 374.8 ][ 102.9 102.9 102.8 102.8 102.8 102.8 102.8 + 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 + 102.8 102.8 102.8 102.8 ]plong + s[ 374.8 374.9 374.9 374.9 375.0 375.0 375.0 375.1 375.1 375.2 375.2 375.2 375.2 375.3 375.3 375.3 375.4 375.4 + 375.5 375.5 375.5 375.6 375.6 375.6 375.7 375.7 375.7 375.8 375.8 ][ 102.8 102.8 102.7 102.7 102.7 102.7 102.7 + 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.6 102.6 + 102.6 102.6 102.6 102.6 ]plong + s[ 375.8 375.9 375.9 375.9 376.0 376.0 376.0 376.1 376.1 376.1 376.2 376.2 376.2 376.3 376.3 376.3 376.4 376.4 + 376.4 376.5 376.5 376.6 376.6 376.6 376.7 376.7 376.7 376.8 376.8 ][ 102.6 102.6 102.6 102.6 102.6 102.6 102.6 + 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 + 102.5 102.5 102.5 102.5 ]plong + s[ 376.8 376.8 376.9 376.9 377.0 377.0 377.0 377.1 377.1 377.1 377.1 377.2 377.2 377.3 377.3 377.3 377.4 377.4 + 377.5 377.5 ][ 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 + 102.5 102.5 102.5 102.5 ]plong + s[ 411.1 411.1 411.0 411.0 410.9 410.9 410.9 410.8 410.8 410.8 410.7 410.7 410.7 410.6 410.6 410.5 410.5 410.5 + 410.4 410.4 ][ 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 102.5 + 102.5 102.5 102.5 102.5 ]plong + s[ 411.1 411.1 411.2 411.2 411.2 411.2 411.3 411.3 411.4 411.4 411.4 411.5 411.5 411.6 411.6 411.6 411.6 411.7 + 411.7 411.8 411.8 411.8 411.9 411.9 411.9 412.0 412.0 412.0 412.1 ][ 102.5 102.5 102.5 102.5 102.6 102.6 102.6 + 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 102.6 + 102.6 102.6 102.6 102.6 ]plong + s[ 412.1 412.1 412.1 412.2 412.2 412.3 412.3 412.3 412.3 412.4 412.4 412.5 412.5 412.5 412.6 412.6 412.6 412.7 + 412.7 412.7 412.8 412.8 412.9 412.9 412.9 413.0 413.0 413.0 413.0 ][ 102.6 102.6 102.6 102.6 102.6 102.6 102.7 + 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 102.7 + 102.7 102.7 102.8 102.8 ]plong + s[ 413.0 413.1 413.1 413.2 413.2 413.2 413.3 413.3 413.3 413.4 413.4 413.4 413.5 413.5 413.6 413.6 413.6 413.7 + 413.7 413.7 413.8 413.8 413.8 413.9 413.9 414.0 414.0 414.0 414.0 ][ 102.8 102.8 102.8 102.8 102.8 102.8 102.8 + 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 102.8 + 102.8 102.8 102.9 102.9 ]plong + s[ 414.0 414.1 414.1 414.1 414.2 414.2 414.3 414.3 414.3 414.3 414.4 414.4 414.5 414.5 414.5 414.6 414.6 414.6 + 414.7 414.7 414.7 414.8 414.8 414.9 414.9 414.9 415.0 415.0 415.0 ][ 102.9 102.9 102.9 102.9 102.9 102.9 102.9 + 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 102.9 103.0 103.0 103.0 103.0 103.0 103.0 103.0 + 103.0 103.0 103.0 103.0 ]plong + s[ 415.0 415.0 415.1 415.1 415.2 415.2 415.2 415.3 415.3 415.3 415.4 415.4 415.4 415.5 415.5 415.6 415.6 415.6 + 415.6 415.7 415.7 415.8 415.8 415.8 415.9 415.9 415.9 415.9 416.0 ][ 103.0 103.0 103.0 103.0 103.0 103.0 103.0 + 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.0 103.1 103.1 103.1 103.1 103.1 103.1 103.1 103.1 103.1 + 103.1 103.1 103.1 103.1 ]plong + s[ 416.0 416.0 416.1 416.1 416.1 416.2 416.2 416.2 416.3 416.3 416.3 416.4 416.4 416.5 416.5 416.5 416.5 416.6 + 416.6 416.7 416.7 416.7 416.8 416.8 416.8 416.9 416.9 416.9 417.0 ][ 103.1 103.1 103.1 103.1 103.1 103.1 103.2 + 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 103.2 + 103.2 103.2 103.2 103.2 ]plong + s[ 417.0 417.0 417.0 417.1 417.1 417.1 417.2 417.2 417.2 417.3 417.3 417.3 417.4 417.4 417.4 417.5 417.5 417.6 + 417.6 417.6 417.6 417.7 417.7 417.8 417.8 417.8 417.9 417.9 417.9 417.9 ][ 103.2 103.2 103.2 103.3 103.3 103.3 + 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.3 103.4 103.4 103.4 103.4 + 103.4 103.4 103.4 103.4 103.4 103.4 ]plong + s[ 418.4 418.4 418.4 418.3 418.3 418.3 418.2 418.2 418.2 418.1 418.1 418.1 418.0 418.0 417.9 ][ 103.5 103.4 103.4 + 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 103.4 ]plong + s[ 363.5 363.5 363.6 363.6 363.7 363.7 363.7 363.7 363.8 363.8 363.9 363.9 363.9 364.0 364.0 364.0 364.1 ][ 104.4 + 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.3 104.3 104.3 ]plong + s[ 364.1 364.1 364.1 364.2 364.2 364.2 364.3 364.3 364.3 364.4 364.4 364.4 364.5 364.5 364.5 364.6 364.6 364.6 + 364.6 364.7 364.7 364.8 364.8 364.8 364.8 364.9 364.9 365.0 365.0 365.0 ][ 104.3 104.3 104.3 104.3 104.3 104.3 + 104.3 104.3 104.3 104.3 104.3 104.3 104.3 104.3 104.3 104.3 104.2 104.2 104.2 104.2 104.2 104.2 104.2 104.2 + 104.2 104.2 104.2 104.2 104.2 104.2 ]plong + s[ 365.0 365.1 365.1 365.1 365.2 365.2 365.2 365.3 365.3 365.3 365.4 365.4 365.4 365.5 365.5 365.5 365.5 365.6 + 365.6 365.7 365.7 365.7 365.7 365.8 365.8 365.9 365.9 365.9 366.0 366.0 366.0 ][ 104.2 104.2 104.1 104.1 104.1 + 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.0 104.0 + 104.0 104.0 104.0 104.0 104.0 104.0 104.0 104.0 ]plong + s[ 366.0 366.1 366.1 366.1 366.2 366.2 366.2 366.3 366.3 366.3 366.4 366.4 366.4 366.4 366.5 366.5 366.6 366.6 + 366.6 366.7 366.7 366.7 366.8 366.8 366.8 366.9 366.9 366.9 367.0 367.0 ][ 104.0 104.0 104.0 104.0 104.0 104.0 + 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 + 103.9 103.9 103.8 103.8 103.8 103.8 ]plong + s[ 367.0 367.0 367.1 367.1 367.2 367.2 367.2 367.2 367.3 367.3 367.4 367.4 367.4 367.4 367.5 367.5 367.5 367.6 + 367.6 367.6 367.7 367.7 367.7 367.8 367.8 367.9 367.9 367.9 367.9 368.0 ][ 103.8 103.8 103.8 103.8 103.8 103.8 + 103.8 103.8 103.8 103.8 103.8 103.8 103.8 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 + 103.7 103.7 103.7 103.7 103.7 103.7 ]plong + s[ 368.0 368.0 368.1 368.1 368.1 368.2 368.2 368.2 368.3 368.3 368.3 368.4 368.4 368.4 368.4 368.5 368.5 368.6 + 368.6 368.6 368.7 368.7 368.7 368.8 368.8 368.8 368.9 368.9 368.9 369.0 ][ 103.7 103.7 103.7 103.7 103.7 103.6 + 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.5 103.5 103.5 + 103.5 103.5 103.5 103.5 103.5 103.5 ]plong + s[ 369.0 369.0 369.0 369.1 369.1 369.2 369.2 369.2 369.2 369.3 369.3 369.3 369.4 369.4 369.4 ][ 103.5 103.5 103.5 + 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 ]plong + s[ 418.9 418.9 418.8 418.8 418.8 418.7 418.7 418.7 418.7 418.6 418.6 418.5 418.5 418.5 418.4 ][ 103.5 103.5 103.5 + 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 103.5 ]plong + s[ 418.9 418.9 419.0 419.0 419.0 419.1 419.1 419.2 419.2 419.2 419.2 419.3 419.3 419.4 419.4 419.4 419.5 419.5 + 419.5 419.6 419.6 419.6 419.7 419.7 419.7 419.8 419.8 419.8 419.9 419.9 ][ 103.5 103.5 103.5 103.5 103.5 103.5 + 103.5 103.5 103.5 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 103.6 + 103.6 103.7 103.7 103.7 103.7 103.7 ]plong + s[ 419.9 419.9 420.0 420.0 420.0 420.1 420.1 420.1 420.2 420.2 420.2 420.3 420.3 420.3 420.4 420.4 420.5 420.5 + 420.5 420.5 420.6 420.6 420.7 420.7 420.7 420.7 420.8 420.8 420.8 420.9 ][ 103.7 103.7 103.7 103.7 103.7 103.7 + 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.7 103.8 103.8 103.8 103.8 103.8 103.8 103.8 + 103.8 103.8 103.8 103.8 103.8 103.8 ]plong + s[ 420.9 420.9 420.9 421.0 421.0 421.0 421.1 421.1 421.2 421.2 421.2 421.2 421.3 421.3 421.4 421.4 421.4 421.5 + 421.5 421.5 421.6 421.6 421.6 421.7 421.7 421.7 421.8 421.8 421.8 421.9 ][ 103.8 103.8 103.8 103.9 103.9 103.9 + 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 103.9 + 104.0 104.0 104.0 104.0 104.0 104.0 ]plong + s[ 421.9 421.9 421.9 421.9 422.0 422.0 422.1 422.1 422.1 422.2 422.2 422.2 422.3 422.3 422.3 422.4 422.4 422.4 + 422.5 422.5 422.5 422.6 422.6 422.6 422.7 422.7 422.7 422.8 422.8 422.8 422.8 ][ 104.0 104.0 104.0 104.0 104.0 + 104.0 104.0 104.0 104.0 104.0 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 104.1 + 104.1 104.1 104.1 104.1 104.1 104.1 104.2 104.2 ]plong + s[ 422.8 422.9 422.9 423.0 423.0 423.0 423.1 423.1 423.1 423.2 423.2 423.2 423.3 423.3 423.3 423.4 423.4 423.4 + 423.5 423.5 423.5 423.6 423.6 423.6 423.7 423.7 423.7 423.7 423.8 423.8 ][ 104.2 104.2 104.2 104.2 104.2 104.2 + 104.2 104.2 104.2 104.2 104.2 104.2 104.2 104.2 104.3 104.3 104.3 104.3 104.3 104.3 104.3 104.3 104.3 104.3 + 104.3 104.3 104.3 104.3 104.3 104.3 ]plong + s[ 424.4 424.3 424.3 424.3 424.2 424.2 424.2 424.1 424.1 424.1 424.0 424.0 423.9 423.9 423.9 423.9 423.8 ][ 104.4 + 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.4 104.3 104.3 104.3 ]plong + s[ 358.6 358.6 358.7 358.7 358.7 358.7 358.8 358.8 358.8 358.9 358.9 358.9 359.0 359.0 359.0 359.1 359.1 359.1 + 359.2 ][ 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.3 105.3 105.3 105.3 105.3 105.3 + 105.3 105.3 ]plong + s[ 359.2 359.2 359.2 359.3 359.3 359.3 359.4 359.4 359.4 359.5 359.5 359.5 359.6 359.6 359.6 359.7 359.7 359.7 + 359.7 359.8 359.8 359.9 359.9 359.9 359.9 360.0 360.0 360.1 360.1 360.1 360.1 ][ 105.3 105.3 105.3 105.3 105.3 + 105.3 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.1 + 105.1 105.1 105.1 105.1 105.1 105.1 105.1 105.1 ]plong + s[ 360.1 360.2 360.2 360.3 360.3 360.3 360.3 360.4 360.4 360.5 360.5 360.5 360.5 360.6 360.6 360.6 360.7 360.7 + 360.7 360.8 360.8 360.8 360.9 360.9 360.9 361.0 361.0 361.0 361.1 361.1 361.1 ][ 105.1 105.1 105.1 105.1 105.1 + 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 104.9 + 104.9 104.9 104.9 104.9 104.9 104.9 104.9 104.9 ]plong + s[ 361.1 361.2 361.2 361.2 361.3 361.3 361.3 361.4 361.4 361.4 361.5 361.5 361.5 361.6 361.6 361.6 361.6 361.7 + 361.7 361.7 361.8 361.8 361.8 361.9 361.9 361.9 362.0 362.0 362.0 362.1 362.1 ][ 104.9 104.9 104.9 104.9 104.9 + 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.7 + 104.7 104.7 104.7 104.7 104.7 104.7 104.7 104.7 ]plong + s[ 362.1 362.1 362.2 362.2 362.2 362.3 362.3 362.3 362.4 362.4 362.5 362.5 362.5 362.5 362.6 362.6 362.6 362.7 + 362.7 362.7 362.8 362.8 362.8 362.9 362.9 362.9 363.0 363.0 363.0 363.1 ][ 104.7 104.7 104.7 104.7 104.7 104.6 + 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.5 + 104.5 104.5 104.5 104.5 104.5 104.5 ]plong + s[ 363.1 363.1 363.1 363.2 363.2 363.2 363.3 363.3 363.3 363.4 363.4 363.4 363.5 363.5 363.5 ][ 104.5 104.5 104.5 + 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.4 ]plong + s[ 424.8 424.8 424.8 424.7 424.7 424.7 424.6 424.6 424.6 424.5 424.5 424.5 424.4 424.4 424.4 ][ 104.5 104.5 104.5 + 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.5 104.4 ]plong + s[ 424.8 424.8 424.9 424.9 425.0 425.0 425.0 425.0 425.1 425.1 425.2 425.2 425.2 425.2 425.3 425.3 425.4 425.4 + 425.4 425.4 425.5 425.5 425.6 425.6 425.6 425.6 425.7 425.7 425.7 425.8 ][ 104.5 104.5 104.5 104.5 104.5 104.5 + 104.5 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 104.6 + 104.7 104.7 104.7 104.7 104.7 104.7 ]plong + s[ 425.8 425.8 425.8 425.9 425.9 425.9 426.0 426.0 426.0 426.1 426.1 426.1 426.2 426.2 426.2 426.3 426.3 426.3 + 426.4 426.4 426.4 426.5 426.5 426.5 426.6 426.6 426.6 426.6 426.7 426.7 426.8 ][ 104.7 104.7 104.7 104.7 104.7 + 104.7 104.7 104.7 104.7 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 104.8 + 104.8 104.8 104.8 104.9 104.9 104.9 104.9 104.9 ]plong + s[ 426.8 426.8 426.8 426.8 426.9 426.9 427.0 427.0 427.0 427.0 427.1 427.1 427.2 427.2 427.2 427.2 427.3 427.3 + 427.4 427.4 427.4 427.4 427.5 427.5 427.6 427.6 427.6 427.6 427.7 427.7 427.7 ][ 104.9 104.9 104.9 104.9 104.9 + 104.9 104.9 104.9 104.9 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 105.0 + 105.0 105.0 105.0 105.1 105.1 105.1 105.1 105.1 ]plong + s[ 427.7 427.8 427.8 427.8 427.9 427.9 427.9 428.0 428.0 428.0 428.1 428.1 428.1 428.2 428.2 428.2 428.3 428.3 + 428.3 428.4 428.4 428.4 428.5 428.5 428.5 428.5 428.6 428.6 428.6 428.7 428.7 ][ 105.1 105.1 105.1 105.1 105.1 + 105.1 105.1 105.1 105.1 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 105.2 + 105.2 105.2 105.3 105.3 105.3 105.3 105.3 105.3 ]plong + s[ 429.3 429.3 429.2 429.2 429.2 429.1 429.1 429.1 429.0 429.0 429.0 428.9 428.9 428.9 428.8 428.8 428.8 428.7 + 428.7 ][ 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.4 105.3 105.3 105.3 105.3 105.3 105.3 + 105.3 105.3 ]plong + s[ 354.3 354.3 354.3 354.4 354.4 354.4 354.5 354.5 354.5 354.6 354.6 354.6 354.7 354.7 354.7 354.8 354.8 354.8 + 354.8 354.9 354.9 354.9 355.0 355.0 355.0 355.1 355.1 355.1 355.2 355.2 355.2 ][ 106.4 106.4 106.4 106.4 106.4 + 106.4 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.2 106.2 + 106.2 106.2 106.2 106.2 106.2 106.2 106.2 106.2 ]plong + s[ 355.2 355.3 355.3 355.3 355.4 355.4 355.4 355.4 355.5 355.5 355.6 355.6 355.6 355.6 355.7 355.7 355.8 355.8 + 355.8 355.8 355.9 355.9 355.9 355.9 356.0 356.0 356.1 356.1 356.1 356.1 356.2 356.2 ][ 106.2 106.2 106.1 106.1 + 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.0 106.0 106.0 106.0 106.0 + 106.0 106.0 106.0 106.0 106.0 106.0 106.0 105.9 105.9 105.9 ]plong + s[ 356.2 356.3 356.3 356.3 356.3 356.4 356.4 356.4 356.5 356.5 356.5 356.6 356.6 356.6 356.7 356.7 356.7 356.8 + 356.8 356.8 356.8 356.9 356.9 357.0 357.0 357.0 357.0 357.1 357.1 357.1 357.2 357.2 ][ 105.9 105.9 105.9 105.9 + 105.9 105.9 105.9 105.9 105.9 105.9 105.9 105.9 105.9 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.8 + 105.8 105.8 105.8 105.7 105.7 105.7 105.7 105.7 105.7 105.7 ]plong + s[ 357.2 357.2 357.3 357.3 357.3 357.4 357.4 357.4 357.5 357.5 357.5 357.6 357.6 357.6 357.7 357.7 357.7 357.7 + 357.8 357.8 357.8 357.9 357.9 357.9 358.0 358.0 358.0 358.1 358.1 358.1 358.2 ][ 105.7 105.7 105.7 105.7 105.7 + 105.7 105.7 105.7 105.7 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.5 105.5 + 105.5 105.5 105.5 105.5 105.5 105.5 105.5 105.5 ]plong + s[ 358.2 358.2 358.2 358.3 358.3 358.3 358.4 358.4 358.4 358.5 358.5 358.5 358.6 358.6 ][ 105.5 105.5 105.5 105.5 + 105.5 105.5 105.5 105.5 105.4 105.4 105.4 105.4 105.4 105.4 ]plong + s[ 429.7 429.7 429.6 429.6 429.6 429.5 429.5 429.5 429.5 429.4 429.4 429.4 429.3 429.3 ][ 105.5 105.5 105.5 105.5 + 105.5 105.5 105.5 105.5 105.4 105.4 105.4 105.4 105.4 105.4 ]plong + s[ 429.7 429.7 429.8 429.8 429.8 429.9 429.9 429.9 430.0 430.0 430.0 430.1 430.1 430.1 430.2 430.2 430.2 430.3 + 430.3 430.3 430.4 430.4 430.4 430.5 430.5 430.5 430.5 430.6 430.6 430.6 430.7 ][ 105.5 105.5 105.5 105.5 105.5 + 105.5 105.5 105.5 105.5 105.5 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.6 105.7 + 105.7 105.7 105.7 105.7 105.7 105.7 105.7 105.7 ]plong + s[ 430.7 430.7 430.7 430.8 430.8 430.8 430.9 430.9 430.9 431.0 431.0 431.0 431.1 431.1 431.1 431.2 431.2 431.2 + 431.3 431.3 431.3 431.4 431.4 431.4 431.5 431.5 431.5 431.5 431.6 431.6 431.6 431.7 ][ 105.7 105.7 105.7 105.7 + 105.7 105.7 105.7 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.8 105.9 105.9 105.9 + 105.9 105.9 105.9 105.9 105.9 105.9 105.9 105.9 105.9 105.9 ]plong + s[ 431.7 431.7 431.7 431.8 431.8 431.8 431.9 431.9 431.9 432.0 432.0 432.0 432.1 432.1 432.1 432.1 432.2 432.2 + 432.3 432.3 432.3 432.3 432.4 432.4 432.4 432.4 432.5 432.5 432.6 432.6 432.6 432.6 ][ 105.9 105.9 105.9 106.0 + 106.0 106.0 106.0 106.0 106.0 106.0 106.0 106.0 106.0 106.0 106.0 106.1 106.1 106.1 106.1 106.1 106.1 106.1 + 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.1 106.2 106.2 ]plong + s[ 433.6 433.6 433.5 433.5 433.5 433.4 433.4 433.4 433.4 433.3 433.3 433.3 433.2 433.2 433.2 433.1 433.1 433.1 + 433.0 433.0 433.0 432.9 432.9 432.9 432.8 432.8 432.8 432.8 432.7 432.7 432.6 ][ 106.4 106.4 106.4 106.4 106.4 + 106.4 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.3 106.2 106.2 + 106.2 106.2 106.2 106.2 106.2 106.2 106.2 106.2 ]plong + s[ 350.4 350.5 350.5 350.5 350.6 350.6 350.6 350.7 350.7 350.7 350.7 350.8 350.8 350.9 350.9 350.9 350.9 351.0 + 351.0 351.0 351.0 351.1 351.1 351.2 351.2 351.2 351.2 351.3 351.3 351.3 ][ 107.4 107.4 107.4 107.4 107.3 107.3 + 107.3 107.3 107.3 107.3 107.3 107.3 107.3 107.3 107.3 107.2 107.2 107.2 107.2 107.2 107.2 107.2 107.2 107.2 + 107.2 107.2 107.2 107.2 107.1 107.1 ]plong + s[ 351.3 351.4 351.4 351.4 351.5 351.5 351.5 351.6 351.6 351.6 351.6 351.7 351.7 351.8 351.8 351.8 351.8 351.9 + 351.9 351.9 351.9 352.0 352.0 352.1 352.1 352.1 352.1 352.2 352.2 352.2 352.3 352.3 ][ 107.1 107.1 107.1 107.1 + 107.1 107.1 107.1 107.1 107.1 107.1 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 + 107.0 107.0 106.9 106.9 106.9 106.9 106.9 106.9 106.9 106.9 ]plong + s[ 352.3 352.3 352.4 352.4 352.4 352.5 352.5 352.5 352.5 352.6 352.6 352.7 352.7 352.7 352.7 352.8 352.8 352.8 + 352.9 352.9 352.9 353.0 353.0 353.0 353.0 353.1 353.1 353.1 353.2 353.2 353.2 353.3 ][ 106.9 106.9 106.9 106.9 + 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.7 106.7 106.7 106.7 106.7 + 106.7 106.7 106.7 106.7 106.7 106.7 106.6 106.6 106.6 106.6 ]plong + s[ 353.3 353.3 353.3 353.4 353.4 353.4 353.5 353.5 353.5 353.6 353.6 353.6 353.7 353.7 353.7 353.8 353.8 353.8 + 353.8 353.9 353.9 353.9 354.0 354.0 354.0 354.1 354.1 354.1 354.1 354.2 354.2 354.3 ][ 106.6 106.6 106.6 106.6 + 106.6 106.6 106.6 106.6 106.6 106.6 106.6 106.6 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.5 + 106.5 106.4 106.4 106.4 106.4 106.4 106.4 106.4 106.4 106.4 ]plong + 354.3 106.4 354.3 106.4 2 pls + 433.6 106.4 433.6 106.4 2 pls + s[ 433.6 433.7 433.7 433.7 433.8 433.8 433.8 433.9 433.9 433.9 433.9 434.0 434.0 434.1 434.1 434.1 434.1 434.2 + 434.2 434.2 434.3 434.3 434.3 434.4 434.4 434.4 434.4 434.5 434.5 434.5 434.6 434.6 ][ 106.4 106.4 106.4 106.4 + 106.4 106.4 106.4 106.4 106.4 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.5 106.6 106.6 + 106.6 106.6 106.6 106.6 106.6 106.6 106.6 106.6 106.6 106.6 ]plong + s[ 434.6 434.6 434.7 434.7 434.7 434.8 434.8 434.8 434.9 434.9 434.9 435.0 435.0 435.0 435.0 435.1 435.1 435.2 + 435.2 435.2 435.2 435.3 435.3 435.3 435.3 435.4 435.4 435.5 435.5 435.5 435.5 435.6 ][ 106.6 106.6 106.6 106.7 + 106.7 106.7 106.7 106.7 106.7 106.7 106.7 106.7 106.7 106.7 106.8 106.8 106.8 106.8 106.8 106.8 106.8 106.8 + 106.8 106.8 106.8 106.8 106.8 106.8 106.9 106.9 106.9 106.9 ]plong + s[ 435.6 435.6 435.6 435.7 435.7 435.7 435.8 435.8 435.8 435.9 435.9 435.9 436.0 436.0 436.0 436.1 436.1 436.1 + 436.1 436.2 436.2 436.3 436.3 436.3 436.3 436.4 436.4 436.4 436.4 436.5 436.5 436.6 ][ 106.9 106.9 106.9 106.9 + 106.9 106.9 106.9 106.9 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 107.0 + 107.1 107.1 107.1 107.1 107.1 107.1 107.1 107.1 107.1 107.1 ]plong + s[ 437.4 437.4 437.4 437.3 437.3 437.3 437.3 437.2 437.2 437.2 437.2 437.1 437.1 437.0 437.0 437.0 437.0 436.9 + 436.9 436.9 436.8 436.8 436.8 436.7 436.7 436.7 436.6 436.6 436.6 436.6 ][ 107.4 107.4 107.4 107.4 107.3 107.3 + 107.3 107.3 107.3 107.3 107.3 107.3 107.3 107.3 107.3 107.2 107.2 107.2 107.2 107.2 107.2 107.2 107.2 107.2 + 107.2 107.2 107.2 107.2 107.1 107.1 ]plong + s[ 346.9 346.9 347.0 347.0 347.0 347.0 347.1 347.1 347.2 347.2 347.2 347.2 347.3 347.3 347.3 347.4 347.4 ][ 108.4 + 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.2 108.2 108.2 108.2 ]plong + s[ 347.4 347.4 347.5 347.5 347.5 347.6 347.6 347.6 347.6 347.7 347.7 347.7 347.8 347.8 347.8 347.9 347.9 347.9 + 348.0 348.0 348.0 348.0 348.1 348.1 348.1 348.1 348.2 348.2 348.3 348.3 348.3 348.3 348.4 ][ 108.2 108.2 108.2 + 108.2 108.2 108.2 108.2 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.0 + 108.0 108.0 108.0 108.0 108.0 108.0 108.0 108.0 108.0 107.9 107.9 107.9 ]plong + s[ 348.4 348.4 348.4 348.5 348.5 348.5 348.6 348.6 348.6 348.7 348.7 348.7 348.7 348.8 348.8 348.8 348.9 348.9 + 348.9 349.0 349.0 349.0 349.0 349.1 349.1 349.1 349.2 349.2 349.2 349.3 349.3 349.3 349.4 ][ 107.9 107.9 107.9 + 107.9 107.9 107.9 107.9 107.9 107.9 107.9 107.9 107.8 107.8 107.8 107.8 107.8 107.8 107.8 107.8 107.8 107.8 + 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.7 ]plong + s[ 349.4 349.4 349.4 349.4 349.5 349.5 349.5 349.6 349.6 349.6 349.7 349.7 349.7 349.8 349.8 349.8 349.9 349.9 + 349.9 349.9 350.0 350.0 350.0 350.1 350.1 350.1 350.1 350.2 350.2 350.3 350.3 350.3 350.3 ][ 107.7 107.7 107.6 + 107.6 107.6 107.6 107.6 107.6 107.6 107.6 107.6 107.6 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 + 107.5 107.5 107.5 107.5 107.4 107.4 107.4 107.4 107.4 107.4 107.4 107.4 ]plong + 350.4 107.4 350.4 107.4 350.4 107.4 350.3 107.4 4 pls + 437.4 107.4 437.5 107.4 437.5 107.4 437.5 107.4 4 pls + s[ 437.5 437.6 437.6 437.6 437.7 437.7 437.7 437.8 437.8 437.8 437.9 437.9 437.9 437.9 438.0 438.0 438.0 438.1 + 438.1 438.1 438.2 438.2 438.2 438.3 438.3 438.3 438.3 438.4 438.4 438.4 438.5 438.5 438.5 ][ 107.4 107.4 107.4 + 107.4 107.4 107.4 107.4 107.4 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 107.5 + 107.6 107.6 107.6 107.6 107.6 107.6 107.6 107.6 107.6 107.6 107.7 107.7 ]plong + s[ 438.5 438.6 438.6 438.6 438.6 438.7 438.7 438.7 438.8 438.8 438.8 438.9 438.9 438.9 439.0 439.0 439.0 439.1 + 439.1 439.1 439.2 439.2 439.2 439.2 439.3 439.3 439.3 439.3 439.4 439.4 439.4 439.5 439.5 ][ 107.7 107.7 107.7 + 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.7 107.8 107.8 107.8 107.8 107.8 107.8 107.8 107.8 107.8 + 107.8 107.9 107.9 107.9 107.9 107.9 107.9 107.9 107.9 107.9 107.9 107.9 ]plong + s[ 439.5 439.5 439.6 439.6 439.6 439.7 439.7 439.7 439.8 439.8 439.8 439.9 439.9 439.9 439.9 440.0 440.0 440.0 + 440.1 440.1 440.1 440.2 440.2 440.2 440.2 440.3 440.3 440.3 440.4 440.4 440.4 440.4 440.5 ][ 107.9 107.9 107.9 + 108.0 108.0 108.0 108.0 108.0 108.0 108.0 108.0 108.0 108.0 108.1 108.1 108.1 108.1 108.1 108.1 108.1 108.1 + 108.1 108.1 108.1 108.1 108.1 108.2 108.2 108.2 108.2 108.2 108.2 108.2 ]plong + s[ 441.0 441.0 440.9 440.9 440.9 440.8 440.8 440.8 440.7 440.7 440.7 440.6 440.6 440.6 440.6 440.5 440.5 ][ 108.4 + 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.3 108.2 108.2 108.2 108.2 ]plong + s[ 343.7 343.7 343.7 343.8 343.8 343.8 343.8 343.9 343.9 343.9 344.0 344.0 344.0 344.0 344.1 344.1 344.1 344.2 + 344.2 344.2 344.3 344.3 344.3 344.3 344.4 344.4 344.4 344.5 ][ 109.3 109.3 109.3 109.3 109.3 109.3 109.3 109.3 + 109.3 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.1 109.1 109.1 109.1 109.1 + 109.1 109.1 ]plong + s[ 344.5 344.5 344.5 344.5 344.6 344.6 344.7 344.7 344.7 344.7 344.8 344.8 344.8 344.9 344.9 344.9 344.9 345.0 + 345.0 345.1 345.1 345.1 345.1 345.2 345.2 345.2 345.2 345.3 345.3 345.3 345.4 345.4 345.4 ][ 109.1 109.1 109.1 + 109.0 109.0 109.0 109.0 109.0 109.0 109.0 109.0 109.0 109.0 109.0 109.0 108.9 108.9 108.9 108.9 108.9 108.9 + 108.9 108.9 108.9 108.9 108.8 108.8 108.8 108.8 108.8 108.8 108.8 108.8 ]plong + s[ 345.4 345.5 345.5 345.5 345.6 345.6 345.6 345.6 345.7 345.7 345.7 345.8 345.8 345.8 345.9 345.9 345.9 346.0 + 346.0 346.0 346.0 346.1 346.1 346.1 346.1 346.2 346.2 346.2 346.3 346.3 346.3 346.4 346.4 346.4 ][ 108.8 108.8 + 108.8 108.8 108.8 108.7 108.7 108.7 108.7 108.7 108.7 108.7 108.7 108.7 108.7 108.6 108.6 108.6 108.6 108.6 + 108.6 108.6 108.6 108.6 108.6 108.6 108.6 108.5 108.5 108.5 108.5 108.5 108.5 108.5 ]plong + s[ 346.4 346.5 346.5 346.5 346.5 346.6 346.6 346.6 346.7 346.7 346.7 346.8 346.8 346.8 346.9 346.9 346.9 ][ 108.5 + 108.5 108.5 108.5 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 ]plong + s[ 441.5 441.4 441.4 441.4 441.3 441.3 441.3 441.2 441.2 441.2 441.2 441.1 441.1 441.1 441.0 441.0 441.0 ][ 108.5 + 108.5 108.5 108.5 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 108.4 ]plong + s[ 441.5 441.5 441.5 441.5 441.6 441.6 441.6 441.7 441.7 441.7 441.8 441.8 441.8 441.9 441.9 441.9 441.9 442.0 + 442.0 442.0 442.1 442.1 442.1 442.2 442.2 442.2 442.2 442.3 442.3 442.3 442.4 442.4 442.4 442.4 ][ 108.5 108.5 + 108.5 108.5 108.5 108.5 108.5 108.6 108.6 108.6 108.6 108.6 108.6 108.6 108.6 108.6 108.6 108.6 108.6 108.7 + 108.7 108.7 108.7 108.7 108.7 108.7 108.7 108.7 108.7 108.8 108.8 108.8 108.8 108.8 ]plong + s[ 442.4 442.5 442.5 442.6 442.6 442.6 442.6 442.7 442.7 442.7 442.8 442.8 442.8 442.8 442.9 442.9 443.0 443.0 + 443.0 443.0 443.1 443.1 443.1 443.1 443.2 443.2 443.2 443.3 443.3 443.3 443.3 443.4 443.4 ][ 108.8 108.8 108.8 + 108.8 108.8 108.8 108.8 108.8 108.9 108.9 108.9 108.9 108.9 108.9 108.9 108.9 108.9 108.9 109.0 109.0 109.0 + 109.0 109.0 109.0 109.0 109.0 109.0 109.0 109.0 109.1 109.1 109.1 109.1 ]plong + s[ 444.2 444.2 444.2 444.1 444.1 444.1 444.1 444.0 444.0 444.0 443.9 443.9 443.9 443.8 443.8 443.8 443.7 443.7 + 443.7 443.7 443.6 443.6 443.6 443.5 443.5 443.5 443.5 443.4 ][ 109.3 109.3 109.3 109.3 109.3 109.3 109.3 109.3 + 109.3 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.2 109.1 109.1 109.1 109.1 109.1 + 109.1 109.1 ]plong + s[ 340.6 340.6 340.7 340.7 340.7 340.7 340.8 340.8 340.8 340.9 340.9 340.9 341.0 341.0 341.0 341.1 341.1 341.1 + 341.1 341.2 341.2 341.2 341.2 341.3 341.3 341.3 341.4 341.4 341.4 341.4 341.5 341.5 ][ 110.3 110.3 110.3 110.3 + 110.3 110.3 110.3 110.3 110.2 110.2 110.2 110.2 110.2 110.2 110.2 110.2 110.2 110.1 110.1 110.1 110.1 110.1 + 110.1 110.1 110.1 110.1 110.1 110.1 110.0 110.0 110.0 110.0 ]plong + s[ 341.5 341.5 341.6 341.6 341.6 341.7 341.7 341.7 341.8 341.8 341.8 341.8 341.9 341.9 341.9 342.0 342.0 342.0 + 342.1 342.1 342.1 342.2 342.2 342.2 342.2 342.3 342.3 342.3 342.3 342.4 342.4 342.4 342.5 342.5 ][ 110.0 110.0 + 110.0 110.0 110.0 110.0 110.0 109.9 109.9 109.9 109.9 109.9 109.9 109.9 109.9 109.9 109.9 109.9 109.8 109.8 + 109.8 109.8 109.8 109.8 109.8 109.8 109.8 109.7 109.7 109.7 109.7 109.7 109.7 109.7 ]plong + s[ 342.5 342.5 342.5 342.6 342.6 342.6 342.7 342.7 342.7 342.8 342.8 342.8 342.9 342.9 342.9 342.9 343.0 343.0 + 343.0 343.1 343.1 343.1 343.1 343.2 343.2 343.2 343.3 343.3 343.3 343.4 343.4 343.4 343.4 343.5 ][ 109.7 109.7 + 109.7 109.7 109.7 109.6 109.6 109.6 109.6 109.6 109.6 109.6 109.6 109.6 109.6 109.5 109.5 109.5 109.5 109.5 + 109.5 109.5 109.5 109.5 109.5 109.5 109.4 109.4 109.4 109.4 109.4 109.4 109.4 109.4 ]plong + s[ 343.5 343.5 343.5 343.6 343.6 343.6 343.7 ][ 109.4 109.4 109.4 109.3 109.3 109.3 109.3 ]plong + s[ 444.4 444.4 444.3 444.3 444.3 444.2 444.2 ][ 109.4 109.4 109.4 109.3 109.3 109.3 109.3 ]plong + s[ 444.4 444.4 444.5 444.5 444.5 444.6 444.6 444.6 444.6 444.7 444.7 444.7 444.8 444.8 444.8 444.9 444.9 444.9 + 445.0 445.0 445.0 445.0 445.1 445.1 445.1 445.1 445.2 445.2 445.2 445.3 445.3 445.3 445.4 445.4 ][ 109.4 109.4 + 109.4 109.4 109.4 109.4 109.4 109.4 109.5 109.5 109.5 109.5 109.5 109.5 109.5 109.5 109.5 109.5 109.5 109.6 + 109.6 109.6 109.6 109.6 109.6 109.6 109.6 109.6 109.7 109.7 109.7 109.7 109.7 109.7 ]plong + s[ 445.4 445.4 445.5 445.5 445.5 445.5 445.6 445.6 445.6 445.7 445.7 445.7 445.7 445.8 445.8 445.8 445.9 445.9 + 445.9 446.0 446.0 446.0 446.0 446.1 446.1 446.1 446.2 446.2 446.2 446.2 446.3 446.3 446.3 446.4 ][ 109.7 109.7 + 109.7 109.7 109.7 109.7 109.7 109.8 109.8 109.8 109.8 109.8 109.8 109.8 109.8 109.8 109.9 109.9 109.9 109.9 + 109.9 109.9 109.9 109.9 109.9 109.9 109.9 110.0 110.0 110.0 110.0 110.0 110.0 110.0 ]plong + s[ 447.3 447.3 447.2 447.2 447.2 447.1 447.1 447.1 447.0 447.0 447.0 447.0 446.9 446.9 446.9 446.8 446.8 446.8 + 446.8 446.7 446.7 446.7 446.6 446.6 446.6 446.5 446.5 446.5 446.4 446.4 446.4 446.4 ][ 110.3 110.3 110.3 110.3 + 110.3 110.3 110.3 110.3 110.2 110.2 110.2 110.2 110.2 110.2 110.2 110.2 110.2 110.1 110.1 110.1 110.1 110.1 + 110.1 110.1 110.1 110.1 110.1 110.1 110.1 110.0 110.0 110.0 ]plong + s[ 337.7 337.8 337.8 337.8 337.9 337.9 337.9 338.0 338.0 338.0 338.0 338.1 338.1 338.1 338.2 338.2 338.2 338.2 + 338.3 338.3 338.3 338.3 338.4 338.4 338.4 338.5 338.5 338.5 338.5 338.6 ][ 111.3 111.3 111.3 111.3 111.3 111.2 + 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.1 111.1 111.1 111.1 111.1 111.1 111.1 111.1 + 111.0 111.0 111.0 111.0 111.0 111.0 ]plong + s[ 338.6 338.6 338.6 338.7 338.7 338.7 338.8 338.8 338.8 338.9 338.9 338.9 338.9 339.0 339.0 339.0 339.1 339.1 + 339.1 339.1 339.2 339.2 339.2 339.3 339.3 339.3 339.3 339.4 339.4 339.4 339.5 339.5 339.5 339.5 339.6 ][ 111.0 + 111.0 111.0 111.0 111.0 111.0 110.9 110.9 110.9 110.9 110.9 110.9 110.9 110.9 110.9 110.8 110.8 110.8 110.8 + 110.8 110.8 110.8 110.8 110.8 110.8 110.8 110.7 110.7 110.7 110.7 110.7 110.7 110.7 110.7 110.6 ]plong + s[ 339.6 339.6 339.6 339.7 339.7 339.7 339.8 339.8 339.8 339.8 339.9 339.9 339.9 340.0 340.0 340.0 340.0 340.1 + 340.1 340.1 340.2 340.2 340.2 340.2 340.3 340.3 340.3 340.4 340.4 340.4 340.5 340.5 340.5 340.5 ][ 110.6 110.6 + 110.6 110.6 110.6 110.6 110.6 110.6 110.6 110.6 110.6 110.5 110.5 110.5 110.5 110.5 110.5 110.5 110.5 110.5 + 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.3 110.3 110.3 ]plong + 340.6 110.3 340.6 110.3 340.5 110.3 3 pls + 447.3 110.3 447.3 110.3 447.3 110.3 3 pls + s[ 447.3 447.4 447.4 447.4 447.5 447.5 447.5 447.5 447.6 447.6 447.6 447.7 447.7 447.7 447.8 447.8 447.8 447.9 + 447.9 447.9 447.9 448.0 448.0 448.0 448.0 448.1 448.1 448.1 448.2 448.2 448.2 448.2 448.3 448.3 448.3 ][ 110.3 + 110.3 110.3 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.4 110.5 110.5 110.5 110.5 110.5 + 110.5 110.5 110.5 110.5 110.6 110.6 110.6 110.6 110.6 110.6 110.6 110.6 110.6 110.6 110.7 110.7 ]plong + s[ 448.3 448.4 448.4 448.4 448.4 448.5 448.5 448.5 448.6 448.6 448.6 448.7 448.7 448.7 448.8 448.8 448.8 448.8 + 448.9 448.9 448.9 448.9 449.0 449.0 449.0 449.1 449.1 449.1 449.1 449.2 449.2 449.2 449.3 449.3 ][ 110.7 110.7 + 110.7 110.7 110.7 110.7 110.7 110.7 110.8 110.8 110.8 110.8 110.8 110.8 110.8 110.8 110.8 110.8 110.8 110.9 + 110.9 110.9 110.9 110.9 110.9 110.9 110.9 110.9 111.0 111.0 111.0 111.0 111.0 111.0 ]plong + s[ 450.1 450.1 450.1 450.0 450.0 450.0 450.0 449.9 449.9 449.9 449.9 449.8 449.8 449.8 449.7 449.7 449.7 449.7 + 449.6 449.6 449.6 449.5 449.5 449.5 449.4 449.4 449.4 449.4 449.3 449.3 ][ 111.3 111.3 111.3 111.3 111.3 111.2 + 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.2 111.1 111.1 111.1 111.1 111.1 111.1 111.1 111.1 + 111.1 111.0 111.0 111.0 111.0 111.0 ]plong + s[ 335.0 335.1 335.1 335.1 335.2 335.2 335.2 335.3 335.3 335.3 335.3 335.4 335.4 335.4 335.4 335.5 335.5 335.5 + 335.6 335.6 335.6 335.6 ][ 112.3 112.3 112.3 112.3 112.2 112.2 112.2 112.2 112.2 112.2 112.2 112.2 112.1 112.1 + 112.1 112.1 112.1 112.1 112.1 112.1 112.1 112.1 ]plong + s[ 335.6 335.7 335.7 335.7 335.8 335.8 335.8 335.8 335.9 335.9 335.9 336.0 336.0 336.0 336.0 336.1 336.1 336.1 + 336.2 336.2 336.2 336.2 336.3 336.3 336.3 336.4 336.4 336.4 336.4 336.5 336.5 336.5 336.6 336.6 336.6 ][ 112.1 + 112.1 112.0 112.0 112.0 112.0 112.0 112.0 112.0 112.0 111.9 111.9 111.9 111.9 111.9 111.9 111.9 111.9 111.9 + 111.9 111.8 111.8 111.8 111.8 111.8 111.8 111.8 111.8 111.8 111.7 111.7 111.7 111.7 111.7 111.7 ]plong + s[ 336.6 336.6 336.7 336.7 336.7 336.8 336.8 336.8 336.9 336.9 336.9 336.9 337.0 337.0 337.0 337.1 337.1 337.1 + 337.1 337.2 337.2 337.2 337.3 337.3 337.3 337.3 337.4 337.4 337.4 337.4 337.5 337.5 337.5 337.6 337.6 ][ 111.7 + 111.7 111.7 111.7 111.7 111.6 111.6 111.6 111.6 111.6 111.6 111.6 111.6 111.6 111.5 111.5 111.5 111.5 111.5 + 111.5 111.5 111.5 111.5 111.5 111.4 111.4 111.4 111.4 111.4 111.4 111.4 111.4 111.4 111.3 111.3 ]plong + s[ 337.6 337.6 337.6 337.7 337.7 337.7 ][ 111.3 111.3 111.3 111.3 111.3 111.3 ]plong + s[ 450.3 450.2 450.2 450.2 450.2 450.1 ][ 111.3 111.3 111.3 111.3 111.3 111.3 ]plong + s[ 450.3 450.3 450.3 450.4 450.4 450.4 450.5 450.5 450.5 450.6 450.6 450.6 450.6 450.7 450.7 450.7 450.8 450.8 + 450.8 450.8 450.9 450.9 450.9 450.9 451.0 451.0 451.0 451.1 451.1 451.1 451.1 451.2 451.2 451.2 451.3 ][ 111.3 + 111.3 111.4 111.4 111.4 111.4 111.4 111.4 111.4 111.4 111.4 111.5 111.5 111.5 111.5 111.5 111.5 111.5 111.5 + 111.5 111.5 111.6 111.6 111.6 111.6 111.6 111.6 111.6 111.6 111.6 111.7 111.7 111.7 111.7 111.7 ]plong + s[ 451.3 451.3 451.3 451.3 451.4 451.4 451.4 451.5 451.5 451.5 451.6 451.6 451.6 451.7 451.7 451.7 451.7 451.8 + 451.8 451.8 451.8 451.9 451.9 451.9 452.0 452.0 452.0 452.0 452.1 452.1 452.1 452.2 452.2 452.2 452.2 ][ 111.7 + 111.7 111.7 111.7 111.7 111.7 111.8 111.8 111.8 111.8 111.8 111.8 111.8 111.8 111.8 111.9 111.9 111.9 111.9 + 111.9 111.9 111.9 111.9 111.9 111.9 112.0 112.0 112.0 112.0 112.0 112.0 112.0 112.0 112.1 112.1 ]plong + s[ 452.8 452.8 452.8 452.8 452.7 452.7 452.7 452.6 452.6 452.6 452.6 452.5 452.5 452.5 452.4 452.4 452.4 452.4 + 452.3 452.3 452.3 452.2 ][ 112.3 112.3 112.3 112.3 112.2 112.2 112.2 112.2 112.2 112.2 112.2 112.2 112.2 112.1 + 112.1 112.1 112.1 112.1 112.1 112.1 112.1 112.1 ]plong + s[ 332.5 332.5 332.5 332.6 332.6 332.6 332.7 332.7 332.7 ][ 113.2 113.2 113.2 113.2 113.2 113.2 113.2 113.2 113.2 +]plong + s[ 332.7 332.7 332.8 332.8 332.8 332.9 332.9 332.9 332.9 333.0 333.0 333.0 333.1 333.1 333.1 333.1 333.2 333.2 + 333.2 333.3 333.3 333.3 333.3 333.4 333.4 333.4 333.4 333.5 333.5 333.5 333.6 333.6 333.6 333.6 333.7 ][ 113.2 + 113.2 113.2 113.1 113.1 113.1 113.1 113.1 113.1 113.1 113.1 113.0 113.0 113.0 113.0 113.0 113.0 113.0 113.0 + 113.0 112.9 112.9 112.9 112.9 112.9 112.9 112.9 112.9 112.9 112.8 112.8 112.8 112.8 112.8 112.8 ]plong + s[ 333.7 333.7 333.7 333.8 333.8 333.8 333.8 333.9 333.9 333.9 334.0 334.0 334.0 334.0 334.1 334.1 334.1 334.2 + 334.2 334.2 334.2 334.3 334.3 334.3 334.4 334.4 334.4 334.4 334.5 334.5 334.5 334.5 334.6 334.6 334.6 334.7 ][ + 112.8 112.8 112.8 112.8 112.7 112.7 112.7 112.7 112.7 112.7 112.7 112.7 112.7 112.6 112.6 112.6 112.6 112.6 + 112.6 112.6 112.6 112.6 112.6 112.5 112.5 112.5 112.5 112.5 112.5 112.5 112.5 112.4 112.4 112.4 112.4 112.4 ]plong + s[ 334.7 334.7 334.7 334.7 334.8 334.8 334.8 334.9 334.9 334.9 335.0 335.0 335.0 335.0 ][ 112.4 112.4 112.4 112.4 + 112.4 112.4 112.3 112.3 112.3 112.3 112.3 112.3 112.3 112.3 ]plong + s[ 453.2 453.2 453.2 453.1 453.1 453.1 453.0 453.0 453.0 452.9 452.9 452.9 452.9 452.8 ][ 112.4 112.4 112.4 112.4 + 112.4 112.4 112.3 112.3 112.3 112.3 112.3 112.3 112.3 112.3 ]plong + s[ 453.2 453.2 453.3 453.3 453.3 453.4 453.4 453.4 453.5 453.5 453.5 453.5 453.6 453.6 453.6 453.7 453.7 453.7 + 453.7 453.8 453.8 453.8 453.8 453.9 453.9 453.9 454.0 454.0 454.0 454.0 454.1 454.1 454.1 454.2 454.2 454.2 ][ + 112.4 112.4 112.4 112.4 112.4 112.5 112.5 112.5 112.5 112.5 112.5 112.5 112.5 112.6 112.6 112.6 112.6 112.6 + 112.6 112.6 112.6 112.6 112.6 112.7 112.7 112.7 112.7 112.7 112.7 112.7 112.7 112.8 112.8 112.8 112.8 112.8 ]plong + s[ 454.2 454.2 454.3 454.3 454.3 454.4 454.4 454.4 454.4 454.5 454.5 454.5 454.6 454.6 454.6 454.6 454.7 454.7 + 454.7 454.7 454.8 454.8 454.8 454.9 454.9 454.9 454.9 455.0 455.0 455.0 455.1 455.1 455.1 455.1 455.2 ][ 112.8 + 112.8 112.8 112.8 112.8 112.8 112.9 112.9 112.9 112.9 112.9 112.9 112.9 112.9 113.0 113.0 113.0 113.0 113.0 + 113.0 113.0 113.0 113.0 113.0 113.1 113.1 113.1 113.1 113.1 113.1 113.1 113.1 113.2 113.2 113.2 ]plong + s[ 455.4 455.4 455.3 455.3 455.3 455.3 455.2 455.2 455.2 ][ 113.2 113.2 113.2 113.2 113.2 113.2 113.2 113.2 113.2 +]plong + s[ 330.0 330.0 330.1 330.1 330.1 330.2 330.2 330.2 330.2 330.3 330.3 330.3 330.4 330.4 330.4 330.4 330.5 330.5 + 330.5 330.5 330.6 330.6 330.6 330.7 330.7 330.7 330.7 ][ 114.2 114.2 114.2 114.2 114.2 114.2 114.2 114.2 114.1 + 114.1 114.1 114.1 114.1 114.1 114.1 114.1 114.1 114.1 114.0 114.0 114.0 114.0 114.0 114.0 114.0 114.0 113.9 ]plong + s[ 330.7 330.8 330.8 330.8 330.9 330.9 330.9 330.9 331.0 331.0 331.0 331.1 331.1 331.1 331.1 331.2 331.2 331.2 + 331.3 331.3 331.3 331.3 331.4 331.4 331.4 331.5 331.5 331.5 331.5 331.6 331.6 331.6 331.6 331.7 331.7 331.7 ][ + 113.9 113.9 113.9 113.9 113.9 113.9 113.9 113.9 113.9 113.8 113.8 113.8 113.8 113.8 113.8 113.8 113.8 113.7 + 113.7 113.7 113.7 113.7 113.7 113.7 113.7 113.7 113.6 113.6 113.6 113.6 113.6 113.6 113.6 113.6 113.5 113.5 ]plong + s[ 331.7 331.8 331.8 331.8 331.8 331.9 331.9 331.9 332.0 332.0 332.0 332.0 332.1 332.1 332.1 332.2 332.2 332.2 + 332.2 332.3 332.3 332.3 332.4 332.4 332.4 332.4 332.5 332.5 ][ 113.5 113.5 113.5 113.5 113.5 113.5 113.5 113.5 + 113.5 113.4 113.4 113.4 113.4 113.4 113.4 113.4 113.4 113.3 113.3 113.3 113.3 113.3 113.3 113.3 113.3 113.3 + 113.3 113.2 ]plong + s[ 456.2 456.1 456.1 456.1 456.0 456.0 456.0 456.0 455.9 455.9 455.9 455.8 455.8 455.8 455.8 455.7 455.7 455.7 + 455.7 455.6 455.6 455.6 455.5 455.5 455.5 455.5 455.4 455.4 ][ 113.5 113.5 113.5 113.5 113.5 113.5 113.5 113.5 + 113.5 113.4 113.4 113.4 113.4 113.4 113.4 113.4 113.4 113.3 113.3 113.3 113.3 113.3 113.3 113.3 113.3 113.3 + 113.3 113.2 ]plong + s[ 456.2 456.2 456.2 456.2 456.3 456.3 456.3 456.4 456.4 456.4 456.4 456.5 456.5 456.5 456.6 456.6 456.6 456.6 + 456.7 456.7 456.7 456.7 456.8 456.8 456.8 456.9 456.9 456.9 456.9 457.0 457.0 457.0 457.1 457.1 457.1 457.1 ][ + 113.5 113.6 113.6 113.6 113.6 113.6 113.6 113.6 113.6 113.7 113.7 113.7 113.7 113.7 113.7 113.7 113.7 113.7 + 113.7 113.8 113.8 113.8 113.8 113.8 113.8 113.8 113.8 113.9 113.9 113.9 113.9 113.9 113.9 113.9 113.9 113.9 ]plong + s[ 457.9 457.8 457.8 457.8 457.7 457.7 457.7 457.7 457.7 457.6 457.6 457.6 457.5 457.5 457.5 457.5 457.4 457.4 + 457.4 457.3 457.3 457.3 457.3 457.2 457.2 457.2 457.1 ][ 114.2 114.2 114.2 114.2 114.2 114.2 114.2 114.2 114.1 + 114.1 114.1 114.1 114.1 114.1 114.1 114.1 114.1 114.1 114.0 114.0 114.0 114.0 114.0 114.0 114.0 114.0 113.9 ]plong + s[ 327.7 327.7 327.7 327.8 327.8 327.8 ][ 115.2 115.2 115.2 115.2 115.2 115.2 ]plong + s[ 327.8 327.8 327.9 327.9 327.9 327.9 328.0 328.0 328.0 328.1 328.1 328.1 328.1 328.2 328.2 328.2 328.3 328.3 + 328.3 328.3 328.4 328.4 328.4 328.4 328.5 328.5 328.5 328.6 328.6 328.6 328.6 328.7 328.7 328.7 328.7 328.8 ][ + 115.2 115.2 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.0 115.0 115.0 115.0 115.0 115.0 115.0 115.0 + 115.0 114.9 114.9 114.9 114.9 114.9 114.9 114.9 114.9 114.8 114.8 114.8 114.8 114.8 114.8 114.8 114.8 114.8 ]plong + s[ 328.8 328.8 328.8 328.9 328.9 328.9 328.9 329.0 329.0 329.0 329.1 329.1 329.1 329.1 329.2 329.2 329.2 329.3 + 329.3 329.3 329.3 329.4 329.4 329.4 329.5 329.5 329.5 329.5 329.6 329.6 329.6 329.6 329.7 329.7 329.7 329.7 + 329.8 ][ 114.8 114.7 114.7 114.7 114.7 114.7 114.7 114.7 114.7 114.6 114.6 114.6 114.6 114.6 114.6 114.6 114.6 + 114.6 114.5 114.5 114.5 114.5 114.5 114.5 114.5 114.5 114.4 114.4 114.4 114.4 114.4 114.4 114.4 114.4 114.4 + 114.3 114.3 ]plong + s[ 329.8 329.8 329.8 329.8 329.9 329.9 329.9 330.0 330.0 330.0 ][ 114.3 114.3 114.3 114.3 114.3 114.3 114.3 114.2 + 114.2 114.2 ]plong + s[ 458.1 458.1 458.0 458.0 458.0 458.0 457.9 457.9 457.9 457.9 ][ 114.3 114.3 114.3 114.3 114.3 114.3 114.3 114.2 + 114.2 114.2 ]plong + s[ 458.1 458.1 458.2 458.2 458.2 458.2 458.3 458.3 458.3 458.4 458.4 458.4 458.4 458.5 458.5 458.5 458.6 458.6 + 458.6 458.6 458.7 458.7 458.7 458.7 458.8 458.8 458.8 458.9 458.9 458.9 458.9 459.0 459.0 459.0 459.1 459.1 + 459.1 ][ 114.3 114.3 114.4 114.4 114.4 114.4 114.4 114.4 114.4 114.4 114.4 114.5 114.5 114.5 114.5 114.5 114.5 + 114.5 114.5 114.6 114.6 114.6 114.6 114.6 114.6 114.6 114.6 114.6 114.7 114.7 114.7 114.7 114.7 114.7 114.7 + 114.7 114.8 ]plong + s[ 459.1 459.1 459.2 459.2 459.2 459.3 459.3 459.3 459.3 459.4 459.4 459.4 459.5 459.5 459.5 459.5 459.6 459.6 + 459.6 459.6 459.6 459.7 459.7 459.7 459.8 459.8 459.8 459.8 459.9 459.9 459.9 460.0 460.0 460.0 460.0 460.1 ][ + 114.8 114.8 114.8 114.8 114.8 114.8 114.8 114.8 114.8 114.9 114.9 114.9 114.9 114.9 114.9 114.9 114.9 115.0 + 115.0 115.0 115.0 115.0 115.0 115.0 115.0 115.0 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.1 115.2 115.2 ]plong + 460.1 115.2 460.1 115.2 460.1 115.2 460.2 115.2 460.2 115.2 5 pls + s[ 325.4 325.5 325.5 325.5 325.5 325.6 325.6 325.6 325.7 325.7 325.7 325.7 325.8 325.8 325.8 325.8 ][ 116.2 116.2 + 116.2 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.0 116.0 116.0 ]plong + s[ 325.8 325.9 325.9 325.9 326.0 326.0 326.0 326.0 326.1 326.1 326.1 326.1 326.2 326.2 326.2 326.2 326.3 326.3 + 326.3 326.4 326.4 326.4 326.4 326.5 326.5 326.5 326.6 326.6 326.6 326.6 326.7 326.7 326.7 326.7 326.8 326.8 + 326.8 ][ 116.0 116.0 116.0 116.0 116.0 116.0 115.9 115.9 115.9 115.9 115.9 115.9 115.9 115.9 115.8 115.8 115.8 + 115.8 115.8 115.8 115.8 115.8 115.7 115.7 115.7 115.7 115.7 115.7 115.7 115.7 115.7 115.6 115.6 115.6 115.6 + 115.6 115.6 ]plong + s[ 326.8 326.8 326.9 326.9 326.9 327.0 327.0 327.0 327.0 327.1 327.1 327.1 327.1 327.2 327.2 327.2 327.3 327.3 + 327.3 327.3 327.4 327.4 327.4 327.5 327.5 327.5 327.5 327.6 327.6 327.6 327.6 327.7 ][ 115.6 115.6 115.5 115.5 + 115.5 115.5 115.5 115.5 115.5 115.5 115.5 115.4 115.4 115.4 115.4 115.4 115.4 115.4 115.4 115.3 115.3 115.3 + 115.3 115.3 115.3 115.3 115.3 115.3 115.2 115.2 115.2 115.2 ]plong + s[ 461.1 461.0 461.0 461.0 460.9 460.9 460.9 460.9 460.8 460.8 460.8 460.7 460.7 460.7 460.7 460.6 460.6 460.6 + 460.6 460.6 460.5 460.5 460.5 460.4 460.4 460.4 460.4 460.3 460.3 460.3 460.2 460.2 460.2 ][ 115.6 115.6 115.6 + 115.5 115.5 115.5 115.5 115.5 115.5 115.5 115.5 115.5 115.4 115.4 115.4 115.4 115.4 115.4 115.4 115.3 115.3 + 115.3 115.3 115.3 115.3 115.3 115.3 115.3 115.2 115.2 115.2 115.2 115.2 ]plong + s[ 461.1 461.1 461.1 461.1 461.2 461.2 461.2 461.3 461.3 461.3 461.3 461.4 461.4 461.4 461.5 461.5 461.5 461.5 + 461.5 461.6 461.6 461.6 461.6 461.7 461.7 461.7 461.8 461.8 461.8 461.8 461.9 461.9 461.9 462.0 462.0 462.0 + 462.0 ][ 115.6 115.6 115.6 115.6 115.6 115.6 115.7 115.7 115.7 115.7 115.7 115.7 115.7 115.7 115.7 115.8 115.8 + 115.8 115.8 115.8 115.8 115.8 115.9 115.9 115.9 115.9 115.9 115.9 115.9 115.9 115.9 116.0 116.0 116.0 116.0 + 116.0 116.0 ]plong + s[ 462.5 462.4 462.4 462.4 462.4 462.3 462.3 462.3 462.2 462.2 462.2 462.2 462.1 462.1 462.1 462.0 ][ 116.2 116.2 + 116.2 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.1 116.0 116.0 116.0 ]plong + s[ 323.3 323.3 323.3 323.4 323.4 323.4 323.4 323.5 323.5 323.5 323.5 323.6 323.6 323.6 323.7 323.7 323.7 323.7 + 323.8 323.8 323.8 323.8 323.9 323.9 ][ 117.2 117.1 117.1 117.1 117.1 117.1 117.1 117.1 117.1 117.1 117.0 117.0 + 117.0 117.0 117.0 117.0 117.0 117.0 117.0 116.9 116.9 116.9 116.9 116.9 ]plong + s[ 323.9 323.9 323.9 324.0 324.0 324.0 324.0 324.1 324.1 324.1 324.2 324.2 324.2 324.2 324.3 324.3 324.3 324.4 + 324.4 324.4 324.4 324.5 324.5 324.5 324.6 324.6 324.6 324.6 324.7 324.7 324.7 324.7 324.7 324.8 324.8 324.8 + 324.9 ][ 116.9 116.9 116.9 116.8 116.8 116.8 116.8 116.8 116.8 116.8 116.8 116.8 116.7 116.7 116.7 116.7 116.7 + 116.7 116.7 116.6 116.6 116.6 116.6 116.6 116.6 116.6 116.6 116.6 116.5 116.5 116.5 116.5 116.5 116.5 116.5 + 116.4 116.4 ]plong + s[ 324.9 324.9 324.9 324.9 325.0 325.0 325.0 325.1 325.1 325.1 325.1 325.2 325.2 325.2 325.3 325.3 325.3 325.3 + 325.4 325.4 325.4 325.4 ][ 116.4 116.4 116.4 116.4 116.4 116.4 116.4 116.4 116.3 116.3 116.3 116.3 116.3 116.3 + 116.3 116.3 116.2 116.2 116.2 116.2 116.2 116.2 ]plong + s[ 463.0 463.0 463.0 462.9 462.9 462.9 462.9 462.8 462.8 462.8 462.7 462.7 462.7 462.7 462.6 462.6 462.6 462.5 + 462.5 462.5 462.5 462.5 ][ 116.4 116.4 116.4 116.4 116.4 116.4 116.4 116.4 116.3 116.3 116.3 116.3 116.3 116.3 + 116.3 116.3 116.2 116.2 116.2 116.2 116.2 116.2 ]plong + s[ 463.0 463.1 463.1 463.1 463.1 463.2 463.2 463.2 463.2 463.3 463.3 463.3 463.3 463.4 463.4 463.4 463.5 463.5 + 463.5 463.5 463.6 463.6 463.6 463.6 463.7 463.7 463.7 463.7 463.8 463.8 463.8 463.8 463.9 463.9 463.9 464.0 + 464.0 ][ 116.4 116.4 116.5 116.5 116.5 116.5 116.5 116.5 116.5 116.6 116.6 116.6 116.6 116.6 116.6 116.6 116.6 + 116.6 116.7 116.7 116.7 116.7 116.7 116.7 116.7 116.8 116.8 116.8 116.8 116.8 116.8 116.8 116.8 116.8 116.9 + 116.9 116.9 ]plong + s[ 464.6 464.6 464.5 464.5 464.5 464.5 464.5 464.4 464.4 464.4 464.4 464.3 464.3 464.3 464.2 464.2 464.2 464.2 + 464.1 464.1 464.1 464.0 464.0 464.0 ][ 117.2 117.1 117.1 117.1 117.1 117.1 117.1 117.1 117.1 117.1 117.0 117.0 + 117.0 117.0 117.0 117.0 117.0 117.0 117.0 116.9 116.9 116.9 116.9 116.9 ]plong + s[ 321.2 321.2 321.2 321.3 321.3 321.3 321.3 321.4 321.4 321.4 321.4 321.5 321.5 321.5 321.5 321.6 321.6 321.6 + 321.7 321.7 321.7 321.7 321.8 321.8 321.8 321.8 321.8 321.9 321.9 321.9 ][ 118.1 118.1 118.1 118.1 118.1 118.1 + 118.1 118.1 118.1 118.1 118.0 118.0 118.0 118.0 118.0 118.0 118.0 117.9 117.9 117.9 117.9 117.9 117.9 117.9 + 117.9 117.8 117.8 117.8 117.8 117.8 ]plong + s[ 321.9 322.0 322.0 322.0 322.0 322.1 322.1 322.1 322.2 322.2 322.2 322.2 322.3 322.3 322.3 322.3 322.4 322.4 + 322.4 322.4 322.5 322.5 322.5 322.6 322.6 322.6 322.6 322.7 322.7 322.7 322.8 322.8 322.8 322.8 322.8 322.9 + 322.9 ][ 117.8 117.8 117.8 117.8 117.7 117.7 117.7 117.7 117.7 117.7 117.7 117.7 117.6 117.6 117.6 117.6 117.6 + 117.6 117.6 117.5 117.5 117.5 117.5 117.5 117.5 117.5 117.5 117.5 117.4 117.4 117.4 117.4 117.4 117.4 117.4 + 117.3 117.3 ]plong + s[ 322.9 322.9 322.9 323.0 323.0 323.0 323.1 323.1 323.1 323.1 323.2 323.2 323.2 323.3 323.3 ][ 117.3 117.3 117.3 + 117.3 117.3 117.3 117.3 117.2 117.2 117.2 117.2 117.2 117.2 117.2 117.2 ]plong + s[ 465.0 465.0 464.9 464.9 464.9 464.9 464.8 464.8 464.8 464.7 464.7 464.7 464.7 464.6 464.6 ][ 117.3 117.3 117.3 + 117.3 117.3 117.3 117.3 117.3 117.2 117.2 117.2 117.2 117.2 117.2 117.2 ]plong + s[ 465.0 465.0 465.0 465.1 465.1 465.1 465.1 465.2 465.2 465.2 465.3 465.3 465.3 465.3 465.4 465.4 465.4 465.4 + 465.5 465.5 465.5 465.5 465.6 465.6 465.6 465.6 465.7 465.7 465.7 465.8 465.8 465.8 465.8 465.9 465.9 465.9 + 465.9 466.0 ][ 117.3 117.3 117.4 117.4 117.4 117.4 117.4 117.4 117.4 117.5 117.5 117.5 117.5 117.5 117.5 117.5 + 117.5 117.5 117.6 117.6 117.6 117.6 117.6 117.6 117.6 117.7 117.7 117.7 117.7 117.7 117.7 117.7 117.7 117.8 + 117.8 117.8 117.8 117.8 ]plong + s[ 466.7 466.7 466.6 466.6 466.6 466.5 466.5 466.5 466.5 466.4 466.4 466.4 466.4 466.4 466.3 466.3 466.3 466.2 + 466.2 466.2 466.2 466.1 466.1 466.1 466.1 466.0 466.0 466.0 ][ 118.1 118.1 118.1 118.1 118.1 118.1 118.1 118.1 + 118.1 118.0 118.0 118.0 118.0 118.0 118.0 118.0 117.9 117.9 117.9 117.9 117.9 117.9 117.9 117.9 117.8 117.8 + 117.8 117.8 ]plong + s[ 319.2 319.2 319.2 319.2 319.3 319.3 319.3 319.3 319.4 319.4 319.4 319.5 319.5 319.5 319.5 319.6 319.6 319.6 + 319.6 319.7 319.7 319.7 319.7 319.8 319.8 319.8 319.9 319.9 319.9 319.9 319.9 320.0 ][ 119.1 119.1 119.1 119.1 + 119.1 119.1 119.0 119.0 119.0 119.0 119.0 119.0 119.0 119.0 119.0 118.9 118.9 118.9 118.9 118.9 118.9 118.9 + 118.8 118.8 118.8 118.8 118.8 118.8 118.8 118.8 118.7 118.7 ]plong + s[ 320.0 320.0 320.0 320.0 320.1 320.1 320.1 320.2 320.2 320.2 320.2 320.3 320.3 320.3 320.4 320.4 320.4 320.4 + 320.5 320.5 320.5 320.5 320.6 320.6 320.6 320.6 320.7 320.7 320.7 320.8 320.8 320.8 320.8 320.8 320.9 320.9 + 320.9 320.9 ][ 118.7 118.7 118.7 118.7 118.7 118.7 118.6 118.6 118.6 118.6 118.6 118.6 118.6 118.6 118.6 118.5 + 118.5 118.5 118.5 118.5 118.5 118.5 118.4 118.4 118.4 118.4 118.4 118.4 118.4 118.4 118.3 118.3 118.3 118.3 + 118.3 118.3 118.3 118.2 ]plong + s[ 320.9 321.0 321.0 321.0 321.1 321.1 321.1 321.1 321.2 ][ 118.2 118.2 118.2 118.2 118.2 118.2 118.2 118.2 118.1 +]plong + s[ 466.9 466.9 466.9 466.9 466.8 466.8 466.8 466.7 466.7 466.7 ][ 118.2 118.2 118.2 118.2 118.2 118.2 118.2 118.2 + 118.2 118.1 ]plong + s[ 466.9 467.0 467.0 467.0 467.0 467.1 467.1 467.1 467.1 467.2 467.2 467.2 467.3 467.3 467.3 467.3 467.4 467.4 + 467.4 467.4 467.4 467.5 467.5 467.5 467.6 467.6 467.6 467.6 467.7 467.7 467.7 467.7 467.8 467.8 467.8 467.8 + 467.9 467.9 ][ 118.2 118.3 118.3 118.3 118.3 118.3 118.3 118.3 118.4 118.4 118.4 118.4 118.4 118.4 118.4 118.4 + 118.5 118.5 118.5 118.5 118.5 118.5 118.5 118.6 118.6 118.6 118.6 118.6 118.6 118.6 118.6 118.7 118.7 118.7 + 118.7 118.7 118.7 118.7 ]plong + s[ 468.7 468.7 468.7 468.6 468.6 468.6 468.6 468.5 468.5 468.5 468.5 468.4 468.4 468.4 468.3 468.3 468.3 468.3 + 468.3 468.2 468.2 468.2 468.2 468.1 468.1 468.1 468.0 468.0 468.0 468.0 467.9 467.9 ][ 119.1 119.1 119.1 119.1 + 119.1 119.1 119.0 119.0 119.0 119.0 119.0 119.0 119.0 119.0 119.0 118.9 118.9 118.9 118.9 118.9 118.9 118.9 + 118.8 118.8 118.8 118.8 118.8 118.8 118.8 118.8 118.7 118.7 ]plong + s[ 317.2 317.2 317.3 317.3 317.3 317.3 317.4 317.4 317.4 317.4 317.5 317.5 317.5 317.5 317.6 317.6 317.6 317.7 + 317.7 317.7 317.7 317.8 317.8 317.8 317.8 317.9 317.9 317.9 317.9 318.0 318.0 318.0 ][ 120.1 120.1 120.1 120.1 + 120.0 120.0 120.0 120.0 120.0 120.0 120.0 120.0 120.0 119.9 119.9 119.9 119.9 119.9 119.9 119.9 119.9 119.8 + 119.8 119.8 119.8 119.8 119.8 119.8 119.7 119.7 119.7 119.7 ]plong + s[ 318.0 318.0 318.1 318.1 318.1 318.1 318.2 318.2 318.2 318.2 318.3 318.3 318.3 318.4 318.4 318.4 318.4 318.5 + 318.5 318.5 318.5 318.6 318.6 318.6 318.6 318.7 318.7 318.7 318.8 318.8 318.8 318.8 318.9 318.9 318.9 318.9 + 318.9 319.0 ][ 119.7 119.7 119.7 119.7 119.7 119.6 119.6 119.6 119.6 119.6 119.6 119.6 119.5 119.5 119.5 119.5 + 119.5 119.5 119.5 119.5 119.4 119.4 119.4 119.4 119.4 119.4 119.4 119.3 119.3 119.3 119.3 119.3 119.3 119.3 + 119.3 119.2 119.2 119.2 ]plong + s[ 319.0 319.0 319.0 319.1 319.1 319.1 319.1 319.2 ][ 119.2 119.2 119.2 119.2 119.2 119.1 119.1 119.1 ]plong + s[ 468.9 468.9 468.9 468.8 468.8 468.8 468.7 468.7 ][ 119.2 119.2 119.2 119.2 119.2 119.1 119.1 119.1 ]plong + s[ 468.9 468.9 469.0 469.0 469.0 469.0 469.1 469.1 469.1 469.1 469.2 469.2 469.2 469.3 469.3 469.3 469.3 469.3 + 469.4 469.4 469.4 469.4 469.5 469.5 469.5 469.6 469.6 469.6 469.6 469.7 469.7 469.7 469.7 469.8 469.8 469.8 + 469.8 469.9 ][ 119.2 119.2 119.2 119.3 119.3 119.3 119.3 119.3 119.3 119.3 119.3 119.4 119.4 119.4 119.4 119.4 + 119.4 119.4 119.5 119.5 119.5 119.5 119.5 119.5 119.5 119.5 119.6 119.6 119.6 119.6 119.6 119.6 119.6 119.7 + 119.7 119.7 119.7 119.7 ]plong + s[ 470.7 470.7 470.6 470.6 470.6 470.5 470.5 470.5 470.5 470.4 470.4 470.4 470.4 470.3 470.3 470.3 470.3 470.2 + 470.2 470.2 470.2 470.1 470.1 470.1 470.1 470.0 470.0 470.0 470.0 469.9 469.9 469.9 ][ 120.1 120.1 120.1 120.1 + 120.0 120.0 120.0 120.0 120.0 120.0 120.0 120.0 120.0 119.9 119.9 119.9 119.9 119.9 119.9 119.9 119.9 119.8 + 119.8 119.8 119.8 119.8 119.8 119.8 119.7 119.7 119.7 119.7 ]plong + s[ 315.3 315.3 315.4 315.4 315.4 315.5 315.5 315.5 315.5 315.6 315.6 315.6 315.6 315.7 315.7 315.7 315.7 315.8 + 315.8 315.8 315.8 315.9 315.9 315.9 315.9 316.0 316.0 316.0 316.0 ][ 121.1 121.1 121.1 121.0 121.0 121.0 121.0 + 121.0 121.0 121.0 121.0 121.0 120.9 120.9 120.9 120.9 120.9 120.9 120.8 120.8 120.8 120.8 120.8 120.8 120.8 + 120.8 120.7 120.7 120.7 ]plong + s[ 316.0 316.1 316.1 316.1 316.1 316.2 316.2 316.2 316.2 316.3 316.3 316.3 316.4 316.4 316.4 316.4 316.5 316.5 + 316.5 316.5 316.6 316.6 316.6 316.6 316.7 316.7 316.7 316.7 316.8 316.8 316.8 316.8 316.9 316.9 316.9 316.9 + 317.0 317.0 317.0 ][ 120.7 120.7 120.7 120.7 120.7 120.6 120.6 120.6 120.6 120.6 120.6 120.6 120.6 120.5 120.5 + 120.5 120.5 120.5 120.5 120.5 120.4 120.4 120.4 120.4 120.4 120.4 120.4 120.3 120.3 120.3 120.3 120.3 120.3 + 120.3 120.2 120.2 120.2 120.2 120.2 ]plong + s[ 317.0 317.0 317.1 317.1 317.1 317.1 317.2 317.2 ][ 120.2 120.2 120.2 120.2 120.1 120.1 120.1 120.1 ]plong + s[ 470.9 470.8 470.8 470.8 470.8 470.7 470.7 470.7 ][ 120.2 120.2 120.2 120.2 120.1 120.1 120.1 120.1 ]plong + s[ 470.9 470.9 470.9 470.9 471.0 471.0 471.0 471.0 471.1 471.1 471.1 471.1 471.2 471.2 471.2 471.2 471.3 471.3 + 471.3 471.3 471.4 471.4 471.4 471.4 471.5 471.5 471.5 471.6 471.6 471.6 471.6 471.7 471.7 471.7 471.7 471.8 + 471.8 471.8 471.8 ][ 120.2 120.2 120.2 120.2 120.2 120.3 120.3 120.3 120.3 120.3 120.3 120.4 120.4 120.4 120.4 + 120.4 120.4 120.4 120.4 120.5 120.5 120.5 120.5 120.5 120.5 120.5 120.6 120.6 120.6 120.6 120.6 120.6 120.6 + 120.6 120.7 120.7 120.7 120.7 120.7 ]plong + s[ 472.6 472.5 472.5 472.5 472.5 472.4 472.4 472.4 472.3 472.3 472.3 472.3 472.2 472.2 472.2 472.2 472.2 472.1 + 472.1 472.1 472.1 472.0 472.0 472.0 472.0 471.9 471.9 471.9 471.8 ][ 121.1 121.1 121.1 121.0 121.0 121.0 121.0 + 121.0 121.0 121.0 121.0 121.0 120.9 120.9 120.9 120.9 120.9 120.9 120.9 120.8 120.8 120.8 120.8 120.8 120.8 + 120.8 120.7 120.7 120.7 ]plong + s[ 313.5 313.5 313.5 313.6 313.6 313.6 313.6 313.7 313.7 313.7 313.7 313.8 313.8 313.8 313.8 313.9 313.9 313.9 + 313.9 314.0 314.0 314.0 314.0 314.1 314.1 ][ 122.1 122.0 122.0 122.0 122.0 122.0 122.0 122.0 122.0 122.0 121.9 + 121.9 121.9 121.9 121.9 121.9 121.9 121.8 121.8 121.8 121.8 121.8 121.8 121.8 121.7 ]plong + s[ 314.1 314.1 314.1 314.2 314.2 314.2 314.2 314.3 314.3 314.3 314.3 314.4 314.4 314.4 314.4 314.5 314.5 314.5 + 314.6 314.6 314.6 314.6 314.7 314.7 314.7 314.7 314.8 314.8 314.8 314.8 314.9 314.9 314.9 314.9 315.0 315.0 + 315.0 315.0 315.1 ][ 121.7 121.7 121.7 121.7 121.7 121.7 121.7 121.7 121.6 121.6 121.6 121.6 121.6 121.6 121.5 + 121.5 121.5 121.5 121.5 121.5 121.5 121.5 121.4 121.4 121.4 121.4 121.4 121.4 121.4 121.3 121.3 121.3 121.3 + 121.3 121.3 121.3 121.2 121.2 121.2 ]plong + s[ 315.1 315.1 315.1 315.1 315.2 315.2 315.2 315.2 315.3 315.3 315.3 ][ 121.2 121.2 121.2 121.2 121.2 121.1 121.1 + 121.1 121.1 121.1 121.1 ]plong + s[ 472.8 472.8 472.8 472.7 472.7 472.7 472.7 472.6 472.6 472.6 472.6 ][ 121.2 121.2 121.2 121.2 121.2 121.1 121.1 + 121.1 121.1 121.1 121.1 ]plong + s[ 472.8 472.9 472.9 472.9 472.9 472.9 473.0 473.0 473.0 473.1 473.1 473.1 473.1 473.2 473.2 473.2 473.2 473.2 + 473.3 473.3 473.3 473.4 473.4 473.4 473.4 473.5 473.5 473.5 473.5 473.6 473.6 473.6 473.6 473.7 473.7 473.7 + 473.7 473.8 473.8 ][ 121.2 121.2 121.2 121.3 121.3 121.3 121.3 121.3 121.3 121.3 121.4 121.4 121.4 121.4 121.4 + 121.4 121.4 121.5 121.5 121.5 121.5 121.5 121.5 121.5 121.5 121.6 121.6 121.6 121.6 121.6 121.6 121.7 121.7 + 121.7 121.7 121.7 121.7 121.7 121.7 ]plong + s[ 474.4 474.4 474.3 474.3 474.3 474.3 474.2 474.2 474.2 474.2 474.2 474.1 474.1 474.1 474.0 474.0 474.0 474.0 + 474.0 473.9 473.9 473.9 473.8 473.8 473.8 ][ 122.1 122.0 122.0 122.0 122.0 122.0 122.0 122.0 122.0 122.0 121.9 + 121.9 121.9 121.9 121.9 121.9 121.9 121.8 121.8 121.8 121.8 121.8 121.8 121.8 121.7 ]plong + s[ 311.7 311.7 311.8 311.8 311.8 311.8 311.9 311.9 311.9 311.9 312.0 312.0 312.0 312.1 312.1 312.1 312.1 312.1 ][ + 123.0 123.0 123.0 123.0 123.0 123.0 122.9 122.9 122.9 122.9 122.9 122.9 122.9 122.9 122.9 122.8 122.8 122.8 ]plong + s[ 312.1 312.2 312.2 312.2 312.2 312.3 312.3 312.3 312.3 312.4 312.4 312.4 312.4 312.5 312.5 312.5 312.5 312.6 + 312.6 312.6 312.6 312.7 312.7 312.7 312.8 312.8 312.8 312.8 312.9 312.9 312.9 312.9 313.0 313.0 313.0 313.0 + 313.1 313.1 313.1 ][ 122.8 122.8 122.8 122.8 122.8 122.7 122.7 122.7 122.7 122.7 122.7 122.7 122.6 122.6 122.6 + 122.6 122.6 122.6 122.6 122.5 122.5 122.5 122.5 122.5 122.5 122.5 122.4 122.4 122.4 122.4 122.4 122.4 122.4 + 122.3 122.3 122.3 122.3 122.3 122.3 ]plong + s[ 313.1 313.1 313.1 313.2 313.2 313.2 313.3 313.3 313.3 313.3 313.3 313.4 313.4 313.4 313.5 313.5 ][ 122.3 122.3 + 122.2 122.2 122.2 122.2 122.2 122.2 122.2 122.1 122.1 122.1 122.1 122.1 122.1 122.1 ]plong + s[ 474.8 474.8 474.7 474.7 474.7 474.7 474.6 474.6 474.6 474.5 474.5 474.5 474.5 474.5 474.4 474.4 ][ 122.3 122.3 + 122.2 122.2 122.2 122.2 122.2 122.2 122.2 122.2 122.1 122.1 122.1 122.1 122.1 122.1 ]plong + s[ 474.8 474.8 474.8 474.9 474.9 474.9 474.9 475.0 475.0 475.0 475.0 475.1 475.1 475.1 475.1 475.2 475.2 475.2 + 475.2 475.3 475.3 475.3 475.3 475.4 475.4 475.4 475.4 475.5 475.5 475.5 475.5 475.6 475.6 475.6 475.6 475.7 + 475.7 475.7 475.7 475.8 ][ 122.3 122.3 122.3 122.3 122.3 122.3 122.4 122.4 122.4 122.4 122.4 122.4 122.4 122.5 + 122.5 122.5 122.5 122.5 122.5 122.5 122.6 122.6 122.6 122.6 122.6 122.6 122.6 122.7 122.7 122.7 122.7 122.7 + 122.7 122.7 122.8 122.8 122.8 122.8 122.8 122.8 ]plong + s[ 476.2 476.1 476.1 476.1 476.1 476.1 476.0 476.0 476.0 476.0 475.9 475.9 475.9 475.8 475.8 475.8 475.8 ][ 123.0 + 123.0 123.0 123.0 123.0 123.0 122.9 122.9 122.9 122.9 122.9 122.9 122.9 122.9 122.9 122.8 122.8 ]plong + s[ 310.0 310.0 310.0 310.0 310.1 310.1 310.1 310.1 310.2 ][ 124.0 124.0 124.0 124.0 124.0 124.0 123.9 123.9 123.9 +]plong + s[ 310.2 310.2 310.2 310.2 310.2 310.3 310.3 310.3 310.4 310.4 310.4 310.4 310.5 310.5 310.5 310.5 310.6 310.6 + 310.6 310.6 310.7 310.7 310.7 310.7 310.8 310.8 310.8 310.8 310.9 310.9 310.9 310.9 311.0 311.0 311.0 311.0 + 311.1 311.1 311.1 311.1 311.1 ][ 123.9 123.9 123.9 123.9 123.9 123.9 123.8 123.8 123.8 123.8 123.8 123.8 123.8 + 123.7 123.7 123.7 123.7 123.7 123.7 123.7 123.6 123.6 123.6 123.6 123.6 123.6 123.6 123.5 123.5 123.5 123.5 + 123.5 123.5 123.5 123.4 123.4 123.4 123.4 123.4 123.4 123.3 ]plong + s[ 311.1 311.2 311.2 311.2 311.3 311.3 311.3 311.3 311.4 311.4 311.4 311.4 311.5 311.5 311.5 311.5 311.6 311.6 + 311.6 311.6 311.7 311.7 311.7 ][ 123.3 123.3 123.3 123.3 123.3 123.3 123.3 123.3 123.2 123.2 123.2 123.2 123.2 + 123.2 123.1 123.1 123.1 123.1 123.1 123.1 123.1 123.1 123.0 ]plong + s[ 476.7 476.7 476.7 476.7 476.7 476.6 476.6 476.6 476.5 476.5 476.5 476.5 476.4 476.4 476.4 476.4 476.3 476.3 + 476.3 476.3 476.2 476.2 476.2 476.2 ][ 123.4 123.4 123.3 123.3 123.3 123.3 123.3 123.3 123.3 123.2 123.2 123.2 + 123.2 123.2 123.2 123.1 123.1 123.1 123.1 123.1 123.1 123.1 123.1 123.0 ]plong + s[ 476.7 476.8 476.8 476.8 476.9 476.9 476.9 476.9 477.0 477.0 477.0 477.0 477.1 477.1 477.1 477.1 477.1 477.2 + 477.2 477.2 477.2 477.3 477.3 477.3 477.3 477.4 477.4 477.4 477.4 477.5 477.5 477.5 477.6 477.6 477.6 477.6 + 477.6 477.7 477.7 477.7 ][ 123.4 123.4 123.4 123.4 123.4 123.4 123.5 123.5 123.5 123.5 123.5 123.5 123.5 123.6 + 123.6 123.6 123.6 123.6 123.6 123.6 123.7 123.7 123.7 123.7 123.7 123.7 123.7 123.8 123.8 123.8 123.8 123.8 + 123.8 123.8 123.9 123.9 123.9 123.9 123.9 123.9 ]plong + s[ 477.9 477.9 477.9 477.8 477.8 477.8 477.8 477.7 ][ 124.0 124.0 124.0 124.0 124.0 123.9 123.9 123.9 ]plong + s[ 308.3 308.3 308.3 308.3 308.4 308.4 308.4 308.4 308.5 308.5 308.5 308.5 308.6 308.6 308.6 308.6 308.7 308.7 + 308.7 308.7 308.8 308.8 308.8 308.8 308.9 308.9 308.9 308.9 309.0 309.0 309.0 309.0 309.1 309.1 309.1 309.2 + 309.2 309.2 ][ 125.0 125.0 125.0 125.0 124.9 124.9 124.9 124.9 124.9 124.9 124.9 124.9 124.8 124.8 124.8 124.8 + 124.8 124.8 124.8 124.7 124.7 124.7 124.7 124.7 124.7 124.7 124.6 124.6 124.6 124.6 124.6 124.6 124.6 124.5 + 124.5 124.5 124.5 124.5 ]plong + s[ 309.2 309.2 309.2 309.3 309.3 309.3 309.3 309.4 309.4 309.4 309.4 309.5 309.5 309.5 309.5 309.6 309.6 309.6 + 309.6 309.7 309.7 309.7 309.7 309.8 309.8 309.8 309.8 309.9 309.9 309.9 309.9 310.0 ][ 124.5 124.5 124.4 124.4 + 124.4 124.4 124.4 124.4 124.4 124.4 124.3 124.3 124.3 124.3 124.3 124.3 124.2 124.2 124.2 124.2 124.2 124.2 + 124.2 124.1 124.1 124.1 124.1 124.1 124.1 124.1 124.0 124.0 ]plong + s[ 478.7 478.7 478.7 478.6 478.6 478.6 478.5 478.5 478.5 478.5 478.4 478.4 478.4 478.4 478.3 478.3 478.3 478.3 + 478.2 478.2 478.2 478.2 478.1 478.1 478.1 478.1 478.0 478.0 478.0 478.0 478.0 477.9 477.9 ][ 124.5 124.5 124.4 + 124.4 124.4 124.4 124.4 124.4 124.4 124.4 124.3 124.3 124.3 124.3 124.3 124.3 124.2 124.2 124.2 124.2 124.2 + 124.2 124.2 124.1 124.1 124.1 124.1 124.1 124.1 124.1 124.0 124.0 124.0 ]plong + s[ 479.6 479.6 479.5 479.5 479.5 479.5 479.4 479.4 479.4 479.4 479.3 479.3 479.3 479.3 479.2 479.2 479.2 479.2 + 479.1 479.1 479.1 479.1 479.0 479.0 479.0 479.0 478.9 478.9 478.9 478.9 478.9 478.8 478.8 478.8 478.7 478.7 + 478.7 ][ 125.0 125.0 125.0 124.9 124.9 124.9 124.9 124.9 124.9 124.9 124.9 124.8 124.8 124.8 124.8 124.8 124.8 + 124.8 124.7 124.7 124.7 124.7 124.7 124.7 124.7 124.6 124.6 124.6 124.6 124.6 124.6 124.6 124.5 124.5 124.5 + 124.5 124.5 ]plong + s[ 306.6 306.6 306.7 306.7 306.7 306.8 306.8 306.8 306.8 306.8 306.9 306.9 306.9 307.0 307.0 307.0 307.0 307.0 + 307.1 307.1 307.1 307.2 307.2 307.2 307.2 307.2 ][ 126.0 126.0 126.0 125.9 125.9 125.9 125.9 125.9 125.9 125.8 + 125.8 125.8 125.8 125.8 125.8 125.8 125.8 125.7 125.7 125.7 125.7 125.7 125.7 125.7 125.6 125.6 ]plong + s[ 307.2 307.3 307.3 307.3 307.3 307.4 307.4 307.4 307.4 307.5 307.5 307.5 307.5 307.6 307.6 307.6 307.6 307.7 + 307.7 307.7 307.7 307.8 307.8 307.8 307.8 307.9 307.9 307.9 307.9 308.0 308.0 308.0 308.0 308.1 308.1 308.1 + 308.1 308.2 308.2 308.2 ][ 125.6 125.6 125.6 125.6 125.6 125.6 125.5 125.5 125.5 125.5 125.5 125.5 125.5 125.4 + 125.4 125.4 125.4 125.4 125.4 125.3 125.3 125.3 125.3 125.3 125.3 125.3 125.2 125.2 125.2 125.2 125.2 125.2 + 125.2 125.1 125.1 125.1 125.1 125.1 125.1 125.1 ]plong + 308.3 125.0 308.2 125.0 308.2 125.0 308.2 125.1 4 pls + 479.6 125.0 479.6 125.0 479.6 125.0 479.7 125.0 479.7 125.1 5 pls + s[ 479.7 479.7 479.7 479.8 479.8 479.8 479.8 479.9 479.9 479.9 479.9 480.0 480.0 480.0 480.0 480.0 480.1 480.1 + 480.1 480.1 480.2 480.2 480.2 480.2 480.3 480.3 480.3 480.3 480.4 480.4 480.4 480.4 480.5 480.5 480.5 480.5 + 480.6 480.6 480.6 480.6 480.7 ][ 125.1 125.1 125.1 125.1 125.1 125.1 125.1 125.2 125.2 125.2 125.2 125.2 125.2 + 125.2 125.3 125.3 125.3 125.3 125.3 125.3 125.3 125.4 125.4 125.4 125.4 125.4 125.4 125.5 125.5 125.5 125.5 + 125.5 125.5 125.5 125.6 125.6 125.6 125.6 125.6 125.6 125.7 ]plong + s[ 481.2 481.2 481.2 481.2 481.1 481.1 481.1 481.0 481.0 481.0 481.0 481.0 480.9 480.9 480.9 480.9 480.8 480.8 + 480.8 480.8 480.7 480.7 480.7 480.7 ][ 126.0 126.0 125.9 125.9 125.9 125.9 125.9 125.9 125.9 125.8 125.8 125.8 + 125.8 125.8 125.8 125.8 125.8 125.7 125.7 125.7 125.7 125.7 125.7 125.7 ]plong + s[ 305.1 305.1 305.1 305.1 305.2 305.2 305.2 305.2 305.2 305.3 ][ 127.0 126.9 126.9 126.9 126.9 126.9 126.9 126.8 + 126.8 126.8 ]plong + s[ 305.3 305.3 305.3 305.3 305.4 305.4 305.4 305.4 305.5 305.5 305.5 305.5 305.6 305.6 305.6 305.6 305.7 305.7 + 305.7 305.7 305.7 305.8 305.8 305.8 305.9 305.9 305.9 305.9 305.9 306.0 306.0 306.0 306.1 306.1 306.1 306.1 + 306.1 306.2 306.2 306.2 306.3 ][ 126.8 126.8 126.8 126.8 126.8 126.8 126.7 126.7 126.7 126.7 126.7 126.7 126.7 + 126.6 126.6 126.6 126.6 126.6 126.6 126.5 126.5 126.5 126.5 126.5 126.5 126.5 126.4 126.4 126.4 126.4 126.4 + 126.4 126.4 126.3 126.3 126.3 126.3 126.3 126.3 126.2 126.2 ]plong + s[ 306.3 306.3 306.3 306.3 306.3 306.4 306.4 306.4 306.4 306.5 306.5 306.5 306.5 306.6 306.6 306.6 306.6 ][ 126.2 + 126.2 126.2 126.2 126.2 126.2 126.1 126.1 126.1 126.1 126.1 126.1 126.0 126.0 126.0 126.0 126.0 ]plong + s[ 481.6 481.6 481.6 481.6 481.6 481.5 481.5 481.5 481.4 481.4 481.4 481.4 481.4 481.3 481.3 481.3 481.2 481.2 ][ + 126.2 126.2 126.2 126.2 126.2 126.2 126.1 126.1 126.1 126.1 126.1 126.1 126.0 126.0 126.0 126.0 126.0 126.0 ]plong + s[ 481.6 481.7 481.7 481.7 481.8 481.8 481.8 481.8 481.8 481.9 481.9 481.9 481.9 481.9 482.0 482.0 482.0 482.1 + 482.1 482.1 482.1 482.1 482.2 482.2 482.2 482.3 482.3 482.3 482.3 482.3 482.4 482.4 482.4 482.5 482.5 482.5 + 482.5 482.5 482.6 482.6 482.6 ][ 126.2 126.2 126.3 126.3 126.3 126.3 126.3 126.3 126.4 126.4 126.4 126.4 126.4 + 126.4 126.4 126.5 126.5 126.5 126.5 126.5 126.5 126.6 126.6 126.6 126.6 126.6 126.6 126.6 126.7 126.7 126.7 + 126.7 126.7 126.7 126.7 126.8 126.8 126.8 126.8 126.8 126.8 ]plong + s[ 482.8 482.8 482.8 482.8 482.7 482.7 482.7 482.7 482.6 482.6 ][ 127.0 126.9 126.9 126.9 126.9 126.9 126.9 126.9 + 126.8 126.8 ]plong + s[ 303.5 303.5 303.5 303.5 303.6 303.6 303.6 303.6 303.7 303.7 303.7 303.7 303.8 303.8 303.8 303.8 303.9 303.9 + 303.9 303.9 304.0 304.0 304.0 304.0 304.1 304.1 304.1 304.1 304.2 304.2 304.2 304.2 304.3 304.3 304.3 ][ 127.9 + 127.9 127.9 127.9 127.9 127.9 127.8 127.8 127.8 127.8 127.8 127.8 127.8 127.8 127.7 127.7 127.7 127.7 127.7 + 127.7 127.6 127.6 127.6 127.6 127.6 127.6 127.6 127.5 127.5 127.5 127.5 127.5 127.5 127.4 127.4 ]plong + s[ 304.3 304.3 304.3 304.4 304.4 304.4 304.4 304.5 304.5 304.5 304.5 304.6 304.6 304.6 304.6 304.7 304.7 304.7 + 304.7 304.8 304.8 304.8 304.8 304.9 304.9 304.9 304.9 305.0 305.0 305.0 305.0 305.1 ][ 127.4 127.4 127.4 127.4 + 127.4 127.4 127.3 127.3 127.3 127.3 127.3 127.3 127.2 127.2 127.2 127.2 127.2 127.2 127.2 127.1 127.1 127.1 + 127.1 127.1 127.1 127.0 127.0 127.0 127.0 127.0 127.0 127.0 ]plong + s[ 483.6 483.6 483.5 483.5 483.5 483.5 483.4 483.4 483.4 483.4 483.3 483.3 483.3 483.3 483.2 483.2 483.2 483.2 + 483.1 483.1 483.1 483.1 483.0 483.0 483.0 483.0 482.9 482.9 482.9 482.9 482.9 482.8 ][ 127.4 127.4 127.4 127.4 + 127.4 127.4 127.3 127.3 127.3 127.3 127.3 127.3 127.2 127.2 127.2 127.2 127.2 127.2 127.2 127.1 127.1 127.1 + 127.1 127.1 127.1 127.1 127.0 127.0 127.0 127.0 127.0 127.0 ]plong + s[ 484.4 484.4 484.4 484.3 484.3 484.3 484.3 484.2 484.2 484.2 484.2 484.1 484.1 484.1 484.1 484.0 484.0 484.0 + 484.0 483.9 483.9 483.9 483.9 483.9 483.8 483.8 483.8 483.8 483.7 483.7 483.7 483.7 483.6 483.6 483.6 ][ 127.9 + 127.9 127.9 127.9 127.9 127.9 127.8 127.8 127.8 127.8 127.8 127.8 127.8 127.8 127.7 127.7 127.7 127.7 127.7 + 127.7 127.7 127.6 127.6 127.6 127.6 127.6 127.6 127.5 127.5 127.5 127.5 127.5 127.5 127.4 127.4 ]plong + s[ 302.0 302.0 302.0 302.0 302.1 302.1 302.1 302.1 302.1 302.2 302.2 302.2 302.3 302.3 302.3 302.3 302.3 ][ 128.9 + 128.9 128.9 128.9 128.9 128.8 128.8 128.8 128.8 128.8 128.8 128.7 128.7 128.7 128.7 128.7 128.7 ]plong + s[ 302.3 302.4 302.4 302.4 302.4 302.4 302.5 302.5 302.5 302.5 302.6 302.6 302.6 302.6 302.7 302.7 302.7 302.7 + 302.8 302.8 302.8 302.8 302.9 302.9 302.9 302.9 303.0 303.0 303.0 303.0 303.1 303.1 303.1 303.1 303.2 303.2 + 303.2 303.2 303.2 303.3 303.3 303.3 ][ 128.7 128.7 128.7 128.6 128.6 128.6 128.6 128.6 128.6 128.5 128.5 128.5 + 128.5 128.5 128.5 128.4 128.4 128.4 128.4 128.4 128.4 128.4 128.3 128.3 128.3 128.3 128.3 128.3 128.2 128.2 + 128.2 128.2 128.2 128.2 128.2 128.1 128.1 128.1 128.1 128.1 128.1 128.0 ]plong + s[ 303.3 303.4 303.4 303.4 303.4 303.4 303.5 303.5 ][ 128.0 128.0 128.0 128.0 128.0 128.0 128.0 127.9 ]plong + s[ 484.6 484.5 484.5 484.5 484.5 484.5 484.4 484.4 ][ 128.0 128.0 128.0 128.0 128.0 128.0 128.0 127.9 ]plong + s[ 484.6 484.6 484.6 484.6 484.7 484.7 484.7 484.7 484.8 484.8 484.8 484.8 484.8 484.9 484.9 484.9 484.9 485.0 + 485.0 485.0 485.0 485.1 485.1 485.1 485.1 485.2 485.2 485.2 485.2 485.3 485.3 485.3 485.3 485.4 485.4 485.4 + 485.4 485.5 485.5 485.5 485.5 485.6 ][ 128.0 128.1 128.1 128.1 128.1 128.1 128.1 128.2 128.2 128.2 128.2 128.2 + 128.2 128.2 128.3 128.3 128.3 128.3 128.3 128.3 128.4 128.4 128.4 128.4 128.4 128.4 128.5 128.5 128.5 128.5 + 128.5 128.5 128.5 128.6 128.6 128.6 128.6 128.6 128.6 128.7 128.7 128.7 ]plong + s[ 485.9 485.9 485.9 485.9 485.8 485.8 485.8 485.8 485.7 485.7 485.7 485.7 485.6 485.6 485.6 485.6 485.6 ][ 128.9 + 128.9 128.9 128.9 128.9 128.8 128.8 128.8 128.8 128.8 128.8 128.7 128.7 128.7 128.7 128.7 128.7 ]plong + s[ 300.5 300.5 300.5 300.5 300.6 300.6 300.6 300.6 300.6 300.7 300.7 300.7 300.7 300.8 300.8 300.8 300.8 300.9 + 300.9 300.9 300.9 301.0 301.0 301.0 301.0 301.1 301.1 301.1 301.1 301.2 301.2 301.2 301.2 301.2 301.3 301.3 + 301.3 301.3 301.4 ][ 129.9 129.9 129.9 129.8 129.8 129.8 129.8 129.8 129.8 129.8 129.7 129.7 129.7 129.7 129.7 + 129.7 129.7 129.6 129.6 129.6 129.6 129.6 129.6 129.5 129.5 129.5 129.5 129.5 129.5 129.5 129.4 129.4 129.4 + 129.4 129.4 129.4 129.3 129.3 129.3 ]plong + s[ 301.4 301.4 301.4 301.4 301.5 301.5 301.5 301.5 301.5 301.6 301.6 301.6 301.6 301.7 301.7 301.7 301.7 301.8 + 301.8 301.8 301.8 301.9 301.9 301.9 301.9 302.0 ][ 129.3 129.3 129.3 129.3 129.3 129.2 129.2 129.2 129.2 129.2 + 129.2 129.1 129.1 129.1 129.1 129.1 129.1 129.0 129.0 129.0 129.0 129.0 129.0 128.9 128.9 128.9 ]plong + s[ 486.5 486.5 486.5 486.5 486.5 486.4 486.4 486.4 486.4 486.3 486.3 486.3 486.3 486.2 486.2 486.2 486.2 486.1 + 486.1 486.1 486.1 486.0 486.0 486.0 486.0 485.9 485.9 ][ 129.3 129.3 129.3 129.3 129.3 129.3 129.2 129.2 129.2 + 129.2 129.2 129.2 129.1 129.1 129.1 129.1 129.1 129.1 129.0 129.0 129.0 129.0 129.0 129.0 128.9 128.9 128.9 ]plong + s[ 487.4 487.4 487.4 487.4 487.3 487.3 487.3 487.3 487.2 487.2 487.2 487.2 487.1 487.1 487.1 487.1 487.0 487.0 + 487.0 487.0 486.9 486.9 486.9 486.9 486.8 486.8 486.8 486.8 486.8 486.7 486.7 486.7 486.7 486.6 486.6 486.6 + 486.6 486.5 ][ 129.9 129.9 129.9 129.8 129.8 129.8 129.8 129.8 129.8 129.8 129.7 129.7 129.7 129.7 129.7 129.7 + 129.7 129.6 129.6 129.6 129.6 129.6 129.6 129.5 129.5 129.5 129.5 129.5 129.5 129.5 129.4 129.4 129.4 129.4 + 129.4 129.4 129.3 129.3 ]plong + s[ 299.0 299.0 299.1 299.1 299.1 299.1 299.2 299.2 299.2 299.2 299.2 299.3 299.3 299.3 299.3 299.4 299.4 ][ 130.9 + 130.9 130.8 130.8 130.8 130.8 130.8 130.8 130.7 130.7 130.7 130.7 130.7 130.7 130.7 130.6 130.6 ]plong + s[ 299.4 299.4 299.4 299.5 299.5 299.5 299.5 299.5 299.6 299.6 299.6 299.6 299.7 299.7 299.7 299.7 299.8 299.8 + 299.8 299.8 299.9 299.9 299.9 299.9 299.9 300.0 300.0 300.0 300.0 300.1 300.1 300.1 300.1 300.2 300.2 300.2 + 300.2 300.3 300.3 300.3 300.3 300.4 300.4 ][ 130.6 130.6 130.6 130.6 130.6 130.6 130.5 130.5 130.5 130.5 130.5 + 130.5 130.4 130.4 130.4 130.4 130.4 130.4 130.3 130.3 130.3 130.3 130.3 130.3 130.2 130.2 130.2 130.2 130.2 + 130.2 130.2 130.1 130.1 130.1 130.1 130.1 130.1 130.0 130.0 130.0 130.0 130.0 130.0 ]plong + 300.5 129.9 300.4 129.9 300.4 129.9 300.4 129.9 300.4 130.0 5 pls + 487.4 129.9 487.4 129.9 487.5 129.9 487.5 129.9 487.5 130.0 5 pls + s[ 487.5 487.5 487.6 487.6 487.6 487.6 487.7 487.7 487.7 487.7 487.7 487.8 487.8 487.8 487.8 487.9 487.9 487.9 + 487.9 488.0 488.0 488.0 488.0 488.1 488.1 488.1 488.1 488.1 488.2 488.2 488.2 488.2 488.3 488.3 488.3 488.3 + 488.4 488.4 488.4 488.4 488.5 488.5 488.5 ][ 130.0 130.0 130.0 130.0 130.0 130.0 130.1 130.1 130.1 130.1 130.1 + 130.1 130.2 130.2 130.2 130.2 130.2 130.2 130.2 130.3 130.3 130.3 130.3 130.3 130.3 130.4 130.4 130.4 130.4 + 130.4 130.4 130.5 130.5 130.5 130.5 130.5 130.5 130.6 130.6 130.6 130.6 130.6 130.6 ]plong + s[ 488.9 488.8 488.8 488.8 488.8 488.7 488.7 488.7 488.7 488.7 488.6 488.6 488.6 488.6 488.5 488.5 488.5 ][ 130.9 + 130.9 130.8 130.8 130.8 130.8 130.8 130.8 130.7 130.7 130.7 130.7 130.7 130.7 130.7 130.7 130.6 ]plong + s[ 297.6 297.6 297.6 297.6 297.7 297.7 297.7 297.7 297.8 297.8 297.8 297.8 297.8 297.9 297.9 297.9 297.9 298.0 + 298.0 298.0 298.0 298.1 298.1 298.1 298.1 298.2 298.2 298.2 298.2 298.3 298.3 298.3 298.3 298.3 298.4 298.4 + 298.4 ][ 131.9 131.8 131.8 131.8 131.8 131.8 131.8 131.8 131.7 131.7 131.7 131.7 131.7 131.7 131.7 131.6 131.6 + 131.6 131.6 131.6 131.6 131.5 131.5 131.5 131.5 131.5 131.5 131.4 131.4 131.4 131.4 131.4 131.4 131.3 131.3 + 131.3 131.3 ]plong + s[ 298.4 298.4 298.5 298.5 298.5 298.5 298.5 298.6 298.6 298.6 298.6 298.7 298.7 298.7 298.7 298.8 298.8 298.8 + 298.8 298.8 298.9 298.9 298.9 298.9 299.0 299.0 299.0 ][ 131.3 131.3 131.3 131.2 131.2 131.2 131.2 131.2 131.2 + 131.1 131.1 131.1 131.1 131.1 131.1 131.1 131.0 131.0 131.0 131.0 131.0 131.0 130.9 130.9 130.9 130.9 130.9 ]plong + s[ 489.5 489.5 489.4 489.4 489.4 489.4 489.3 489.3 489.3 489.3 489.2 489.2 489.2 489.2 489.2 489.1 489.1 489.1 + 489.1 489.0 489.0 489.0 489.0 488.9 488.9 488.9 488.9 ][ 131.3 131.3 131.3 131.3 131.2 131.2 131.2 131.2 131.2 + 131.1 131.1 131.1 131.1 131.1 131.1 131.1 131.0 131.0 131.0 131.0 131.0 131.0 130.9 130.9 130.9 130.9 130.9 ]plong + s[ 490.3 490.3 490.3 490.2 490.2 490.2 490.2 490.1 490.1 490.1 490.1 490.1 490.0 490.0 490.0 490.0 489.9 489.9 + 489.9 489.9 489.8 489.8 489.8 489.8 489.7 489.7 489.7 489.7 489.7 489.6 489.6 489.6 489.6 489.6 489.5 489.5 + 489.5 ][ 131.9 131.8 131.8 131.8 131.8 131.8 131.8 131.8 131.7 131.7 131.7 131.7 131.7 131.7 131.7 131.6 131.6 + 131.6 131.6 131.6 131.6 131.5 131.5 131.5 131.5 131.5 131.5 131.4 131.4 131.4 131.4 131.4 131.4 131.3 131.3 + 131.3 131.3 ]plong + s[ 296.2 296.2 296.2 296.3 296.3 296.3 296.3 296.4 296.4 296.4 296.4 296.5 ][ 132.8 132.8 132.8 132.8 132.8 132.7 + 132.7 132.7 132.7 132.7 132.7 132.7 ]plong + s[ 296.5 296.5 296.5 296.5 296.5 296.6 296.6 296.6 296.6 296.6 296.7 296.7 296.7 296.7 296.8 296.8 296.8 296.8 + 296.9 296.9 296.9 296.9 296.9 297.0 297.0 297.0 297.0 297.1 297.1 297.1 297.1 297.2 297.2 297.2 297.2 297.3 + 297.3 297.3 297.3 297.3 297.4 297.4 297.4 297.4 ][ 132.7 132.6 132.6 132.6 132.6 132.6 132.6 132.6 132.5 132.5 + 132.5 132.5 132.5 132.5 132.4 132.4 132.4 132.4 132.4 132.4 132.3 132.3 132.3 132.3 132.3 132.3 132.2 132.2 + 132.2 132.2 132.2 132.2 132.1 132.1 132.1 132.1 132.1 132.1 132.0 132.0 132.0 132.0 132.0 132.0 ]plong + s[ 297.4 297.5 297.5 297.5 297.5 297.5 297.6 ][ 132.0 131.9 131.9 131.9 131.9 131.9 131.9 ]plong + s[ 490.5 490.4 490.4 490.4 490.4 490.3 490.3 ][ 132.0 131.9 131.9 131.9 131.9 131.9 131.9 ]plong + s[ 490.5 490.5 490.5 490.5 490.5 490.6 490.6 490.6 490.6 490.7 490.7 490.7 490.7 490.7 490.8 490.8 490.8 490.8 + 490.9 490.9 490.9 490.9 491.0 491.0 491.0 491.0 491.0 491.1 491.1 491.1 491.1 491.2 491.2 491.2 491.2 491.3 + 491.3 491.3 491.3 491.4 491.4 491.4 491.4 491.4 ][ 132.0 132.0 132.0 132.0 132.0 132.0 132.1 132.1 132.1 132.1 + 132.1 132.1 132.2 132.2 132.2 132.2 132.2 132.2 132.3 132.3 132.3 132.3 132.3 132.3 132.4 132.4 132.4 132.4 + 132.4 132.4 132.5 132.5 132.5 132.5 132.5 132.5 132.6 132.6 132.6 132.6 132.6 132.6 132.6 132.7 ]plong + s[ 491.7 491.7 491.6 491.6 491.6 491.6 491.6 491.5 491.5 491.5 491.5 491.4 ][ 132.8 132.8 132.8 132.8 132.8 132.8 + 132.7 132.7 132.7 132.7 132.7 132.7 ]plong + s[ 294.8 294.8 294.9 294.9 294.9 294.9 295.0 295.0 295.0 295.0 295.0 295.1 295.1 295.1 295.1 295.2 295.2 295.2 + 295.2 295.3 295.3 295.3 295.3 295.4 295.4 295.4 295.4 295.4 295.5 295.5 ][ 133.8 133.8 133.8 133.8 133.8 133.7 + 133.7 133.7 133.7 133.7 133.6 133.6 133.6 133.6 133.6 133.6 133.6 133.5 133.5 133.5 133.5 133.5 133.5 133.5 + 133.4 133.4 133.4 133.4 133.4 133.3 ]plong + s[ 295.5 295.5 295.5 295.6 295.6 295.6 295.6 295.6 295.7 295.7 295.7 295.7 295.7 295.8 295.8 295.8 295.8 295.9 + 295.9 295.9 295.9 296.0 296.0 296.0 296.0 296.1 296.1 296.1 296.1 296.1 296.2 296.2 ][ 133.3 133.3 133.3 133.3 + 133.3 133.3 133.3 133.2 133.2 133.2 133.2 133.2 133.1 133.1 133.1 133.1 133.1 133.1 133.1 133.0 133.0 133.0 + 133.0 133.0 132.9 132.9 132.9 132.9 132.9 132.9 132.9 132.8 ]plong + s[ 492.4 492.4 492.4 492.4 492.3 492.3 492.3 492.3 492.2 492.2 492.2 492.2 492.1 492.1 492.1 492.1 492.1 492.0 + 492.0 492.0 492.0 491.9 491.9 491.9 491.9 491.8 491.8 491.8 491.8 491.8 491.7 491.7 491.7 ][ 133.4 133.4 133.3 + 133.3 133.3 133.3 133.3 133.3 133.2 133.2 133.2 133.2 133.2 133.2 133.1 133.1 133.1 133.1 133.1 133.1 133.0 + 133.0 133.0 133.0 133.0 133.0 132.9 132.9 132.9 132.9 132.9 132.9 132.8 ]plong + s[ 493.1 493.0 493.0 493.0 493.0 492.9 492.9 492.9 492.9 492.9 492.8 492.8 492.8 492.8 492.7 492.7 492.7 492.7 + 492.6 492.6 492.6 492.6 492.6 492.5 492.5 492.5 492.5 492.5 492.4 ][ 133.8 133.8 133.8 133.8 133.8 133.7 133.7 + 133.7 133.7 133.7 133.6 133.6 133.6 133.6 133.6 133.6 133.6 133.6 133.5 133.5 133.5 133.5 133.5 133.5 133.4 + 133.4 133.4 133.4 133.4 ]plong + 293.5 134.8 293.5 134.8 2 pls + s[ 293.5 293.5 293.6 293.6 293.6 293.6 293.7 293.7 293.7 293.7 293.7 293.7 293.8 293.8 293.8 293.8 293.9 293.9 + 293.9 293.9 294.0 294.0 294.0 294.0 294.1 294.1 294.1 294.1 294.1 294.2 294.2 294.2 294.2 294.3 294.3 294.3 + 294.3 294.3 294.4 294.4 294.4 294.4 294.5 294.5 ][ 134.8 134.8 134.7 134.7 134.7 134.7 134.7 134.7 134.6 134.6 + 134.6 134.6 134.6 134.6 134.6 134.5 134.5 134.5 134.5 134.5 134.5 134.4 134.4 134.4 134.4 134.4 134.4 134.3 + 134.3 134.3 134.3 134.3 134.3 134.2 134.2 134.2 134.2 134.2 134.2 134.1 134.1 134.1 134.1 134.1 ]plong + s[ 294.5 294.5 294.5 294.6 294.6 294.6 294.6 294.6 294.7 294.7 294.7 294.7 294.7 294.8 294.8 294.8 ][ 134.1 134.1 + 134.0 134.0 134.0 134.0 134.0 134.0 133.9 133.9 133.9 133.9 133.9 133.8 133.8 133.8 ]plong + s[ 493.4 493.4 493.4 493.3 493.3 493.3 493.3 493.2 493.2 493.2 493.2 493.2 493.1 493.1 493.1 493.1 ][ 134.1 134.1 + 134.0 134.0 134.0 134.0 134.0 134.0 133.9 133.9 133.9 133.9 133.9 133.9 133.8 133.8 ]plong + s[ 493.4 493.4 493.5 493.5 493.5 493.5 493.5 493.6 493.6 493.6 493.6 493.6 493.7 493.7 493.7 493.7 493.8 493.8 + 493.8 493.8 493.9 493.9 493.9 493.9 493.9 494.0 494.0 494.0 494.0 494.1 494.1 494.1 494.1 494.1 494.2 494.2 + 494.2 494.2 494.3 494.3 494.3 494.3 494.4 494.4 ][ 134.1 134.1 134.1 134.1 134.1 134.2 134.2 134.2 134.2 134.2 + 134.2 134.3 134.3 134.3 134.3 134.3 134.3 134.4 134.4 134.4 134.4 134.4 134.4 134.5 134.5 134.5 134.5 134.5 + 134.5 134.6 134.6 134.6 134.6 134.6 134.6 134.6 134.7 134.7 134.7 134.7 134.7 134.7 134.8 134.8 ]plong + 494.4 134.8 494.4 134.8 2 pls + s[ 292.2 292.2 292.2 292.2 292.3 292.3 292.3 292.3 292.3 292.4 292.4 292.4 292.4 292.5 292.5 292.5 292.5 ][ 135.8 + 135.8 135.8 135.7 135.7 135.7 135.7 135.7 135.6 135.6 135.6 135.6 135.6 135.6 135.5 135.5 135.5 ]plong + s[ 292.5 292.6 292.6 292.6 292.6 292.6 292.7 292.7 292.7 292.7 292.7 292.8 292.8 292.8 292.8 292.9 292.9 292.9 + 292.9 292.9 293.0 293.0 293.0 293.0 293.1 293.1 293.1 293.1 293.2 293.2 293.2 293.2 293.2 293.3 293.3 293.3 + 293.3 293.4 293.4 293.4 293.4 293.4 293.5 293.5 ][ 135.5 135.5 135.5 135.5 135.5 135.4 135.4 135.4 135.4 135.4 + 135.4 135.3 135.3 135.3 135.3 135.3 135.3 135.2 135.2 135.2 135.2 135.2 135.2 135.1 135.1 135.1 135.1 135.1 + 135.1 135.0 135.0 135.0 135.0 135.0 134.9 134.9 134.9 134.9 134.9 134.9 134.8 134.8 134.8 134.8 ]plong + s[ 494.4 494.4 494.5 494.5 494.5 494.5 494.5 494.5 494.6 494.6 494.6 494.6 494.7 494.7 494.7 494.7 494.8 494.8 + 494.8 494.8 494.8 494.9 494.9 494.9 494.9 495.0 495.0 495.0 495.0 495.0 495.1 495.1 495.1 495.1 495.2 495.2 + 495.2 495.2 495.3 495.3 495.3 495.3 495.3 495.4 ][ 134.8 134.8 134.8 134.8 134.9 134.9 134.9 134.9 134.9 134.9 + 135.0 135.0 135.0 135.0 135.0 135.1 135.1 135.1 135.1 135.1 135.1 135.2 135.2 135.2 135.2 135.2 135.2 135.3 + 135.3 135.3 135.3 135.3 135.3 135.4 135.4 135.4 135.4 135.4 135.4 135.5 135.5 135.5 135.5 135.5 ]plong + s[ 495.7 495.7 495.7 495.6 495.6 495.6 495.6 495.6 495.5 495.5 495.5 495.5 495.5 495.4 495.4 495.4 495.4 ][ 135.8 + 135.8 135.8 135.7 135.7 135.7 135.7 135.7 135.6 135.6 135.6 135.6 135.6 135.6 135.5 135.5 135.5 ]plong + s[ 290.9 290.9 290.9 290.9 291.0 291.0 291.0 291.0 291.0 291.1 291.1 291.1 291.1 291.2 291.2 291.2 291.2 291.3 + 291.3 291.3 291.3 291.3 291.4 291.4 291.4 291.4 291.5 291.5 291.5 291.5 291.5 291.6 ][ 136.8 136.7 136.7 136.7 + 136.7 136.7 136.7 136.6 136.6 136.6 136.6 136.6 136.6 136.5 136.5 136.5 136.5 136.5 136.5 136.5 136.4 136.4 + 136.4 136.4 136.4 136.4 136.3 136.3 136.3 136.3 136.3 136.2 ]plong + s[ 291.6 291.6 291.6 291.6 291.7 291.7 291.7 291.7 291.7 291.8 291.8 291.8 291.8 291.8 291.9 291.9 291.9 291.9 + 292.0 292.0 292.0 292.0 292.1 292.1 292.1 292.1 292.1 292.2 ][ 136.2 136.2 136.2 136.2 136.2 136.2 136.1 136.1 + 136.1 136.1 136.1 136.1 136.0 136.0 136.0 136.0 136.0 136.0 135.9 135.9 135.9 135.9 135.9 135.9 135.8 135.8 + 135.8 135.8 ]plong + s[ 496.3 496.3 496.3 496.3 496.3 496.2 496.2 496.2 496.2 496.1 496.1 496.1 496.1 496.1 496.0 496.0 496.0 496.0 + 495.9 495.9 495.9 495.9 495.9 495.8 495.8 495.8 495.8 495.7 495.7 ][ 136.3 136.3 136.2 136.2 136.2 136.2 136.2 + 136.2 136.1 136.1 136.1 136.1 136.1 136.0 136.0 136.0 136.0 136.0 136.0 135.9 135.9 135.9 135.9 135.9 135.9 + 135.8 135.8 135.8 135.8 ]plong + s[ 497.0 497.0 496.9 496.9 496.9 496.9 496.8 496.8 496.8 496.8 496.8 496.7 496.7 496.7 496.7 496.6 496.6 496.6 + 496.6 496.6 496.5 496.5 496.5 496.5 496.5 496.4 496.4 496.4 496.4 496.3 ][ 136.8 136.7 136.7 136.7 136.7 136.7 + 136.7 136.6 136.6 136.6 136.6 136.6 136.5 136.5 136.5 136.5 136.5 136.5 136.5 136.4 136.4 136.4 136.4 136.4 + 136.4 136.3 136.3 136.3 136.3 136.3 ]plong + s[ 289.6 289.6 289.7 289.7 289.7 289.7 289.8 289.8 289.8 289.8 289.8 289.9 289.9 289.9 289.9 289.9 290.0 290.0 + 290.0 290.0 290.1 290.1 290.1 290.1 290.1 290.2 290.2 290.2 290.2 290.3 290.3 290.3 290.3 290.3 290.4 290.4 + 290.4 290.4 290.5 290.5 290.5 290.5 290.5 290.6 ][ 137.7 137.7 137.7 137.7 137.7 137.7 137.6 137.6 137.6 137.6 + 137.6 137.6 137.5 137.5 137.5 137.5 137.5 137.5 137.5 137.4 137.4 137.4 137.4 137.4 137.3 137.3 137.3 137.3 + 137.3 137.3 137.2 137.2 137.2 137.2 137.2 137.2 137.1 137.1 137.1 137.1 137.1 137.0 137.0 137.0 ]plong + s[ 290.6 290.6 290.6 290.6 290.7 290.7 290.7 290.7 290.7 290.8 290.8 290.8 290.8 290.8 290.9 ][ 137.0 137.0 137.0 + 137.0 136.9 136.9 136.9 136.9 136.9 136.9 136.8 136.8 136.8 136.8 136.8 ]plong + s[ 497.3 497.3 497.3 497.3 497.2 497.2 497.2 497.2 497.1 497.1 497.1 497.1 497.1 497.0 497.0 497.0 ][ 137.0 137.0 + 137.0 137.0 136.9 136.9 136.9 136.9 136.9 136.9 136.8 136.8 136.8 136.8 136.8 136.8 ]plong + s[ 498.3 498.3 498.2 498.2 498.2 498.2 498.1 498.1 498.1 498.1 498.0 498.0 498.0 498.0 498.0 497.9 497.9 497.9 + 497.9 497.8 497.8 497.8 497.8 497.8 497.7 497.7 497.7 497.7 497.6 497.6 497.6 497.6 497.6 497.5 497.5 497.5 + 497.5 497.4 497.4 497.4 497.4 497.4 497.3 497.3 ][ 137.7 137.7 137.7 137.7 137.7 137.7 137.6 137.6 137.6 137.6 + 137.6 137.6 137.5 137.5 137.5 137.5 137.5 137.5 137.5 137.4 137.4 137.4 137.4 137.4 137.3 137.3 137.3 137.3 + 137.3 137.3 137.2 137.2 137.2 137.2 137.2 137.2 137.1 137.1 137.1 137.1 137.1 137.1 137.0 137.0 ]plong + s[ 288.4 288.4 288.4 288.5 288.5 288.5 288.5 288.6 288.6 288.6 288.6 ][ 138.7 138.7 138.7 138.7 138.6 138.6 138.6 + 138.6 138.6 138.6 138.5 ]plong + s[ 288.6 288.6 288.7 288.7 288.7 288.7 288.8 288.8 288.8 288.8 288.8 288.8 288.9 288.9 288.9 288.9 289.0 289.0 + 289.0 289.0 289.0 289.1 289.1 289.1 289.1 289.2 289.2 289.2 289.2 289.2 289.3 289.3 289.3 289.3 289.4 289.4 + 289.4 289.4 289.4 289.5 289.5 289.5 289.5 289.6 289.6 289.6 ][ 138.5 138.5 138.5 138.5 138.5 138.5 138.5 138.4 + 138.4 138.4 138.4 138.4 138.3 138.3 138.3 138.3 138.3 138.3 138.2 138.2 138.2 138.2 138.2 138.2 138.1 138.1 + 138.1 138.1 138.1 138.0 138.0 138.0 138.0 138.0 138.0 137.9 137.9 137.9 137.9 137.9 137.9 137.8 137.8 137.8 + 137.8 137.8 ]plong + 289.6 137.8 289.6 137.7 2 pls + 498.3 137.7 498.3 137.8 2 pls + s[ 498.3 498.3 498.3 498.4 498.4 498.4 498.4 498.4 498.5 498.5 498.5 498.5 498.5 498.6 498.6 498.6 498.6 498.7 + 498.7 498.7 498.7 498.7 498.8 498.8 498.8 498.8 498.9 498.9 498.9 498.9 498.9 499.0 499.0 499.0 499.0 499.1 + 499.1 499.1 499.1 499.1 499.2 499.2 499.2 499.2 499.3 499.3 ][ 137.8 137.8 137.8 137.8 137.8 137.9 137.9 137.9 + 137.9 137.9 137.9 138.0 138.0 138.0 138.0 138.0 138.0 138.1 138.1 138.1 138.1 138.1 138.2 138.2 138.2 138.2 + 138.2 138.2 138.3 138.3 138.3 138.3 138.3 138.4 138.4 138.4 138.4 138.4 138.4 138.5 138.5 138.5 138.5 138.5 + 138.5 138.5 ]plong + s[ 499.5 499.5 499.4 499.4 499.4 499.4 499.4 499.3 499.3 499.3 499.3 ][ 138.7 138.7 138.7 138.7 138.6 138.6 138.6 + 138.6 138.6 138.6 138.5 ]plong + s[ 287.2 287.2 287.2 287.2 287.3 287.3 287.3 287.3 287.4 287.4 287.4 287.4 287.4 287.5 287.5 287.5 287.5 287.6 + 287.6 287.6 287.6 287.6 ][ 139.7 139.7 139.7 139.6 139.6 139.6 139.6 139.6 139.6 139.5 139.5 139.5 139.5 139.5 + 139.4 139.4 139.4 139.4 139.4 139.4 139.4 139.3 ]plong + s[ 287.6 287.7 287.7 287.7 287.7 287.8 287.8 287.8 287.8 287.8 287.8 287.9 287.9 287.9 287.9 288.0 288.0 288.0 + 288.0 288.0 288.1 288.1 288.1 288.1 288.2 288.2 288.2 288.2 288.2 288.3 288.3 288.3 288.3 288.4 288.4 288.4 ][ + 139.3 139.3 139.3 139.3 139.3 139.3 139.2 139.2 139.2 139.2 139.2 139.1 139.1 139.1 139.1 139.1 139.1 139.0 + 139.0 139.0 139.0 139.0 138.9 138.9 138.9 138.9 138.9 138.9 138.8 138.8 138.8 138.8 138.8 138.8 138.7 138.7 ]plong + s[ 500.3 500.2 500.2 500.2 500.2 500.1 500.1 500.1 500.1 500.1 500.0 500.0 500.0 500.0 499.9 499.9 499.9 499.9 + 499.9 499.8 499.8 499.8 499.8 499.7 499.7 499.7 499.7 499.7 499.6 499.6 499.6 499.6 499.5 499.5 499.5 499.5 ][ + 139.3 139.3 139.3 139.3 139.3 139.3 139.2 139.2 139.2 139.2 139.2 139.1 139.1 139.1 139.1 139.1 139.1 139.0 + 139.0 139.0 139.0 139.0 139.0 138.9 138.9 138.9 138.9 138.9 138.8 138.8 138.8 138.8 138.8 138.8 138.7 138.7 ]plong + s[ 500.7 500.7 500.7 500.6 500.6 500.6 500.6 500.5 500.5 500.5 500.5 500.5 500.4 500.4 500.4 500.4 500.4 500.3 + 500.3 500.3 500.3 500.3 ][ 139.7 139.7 139.7 139.6 139.6 139.6 139.6 139.6 139.6 139.5 139.5 139.5 139.5 139.5 + 139.4 139.4 139.4 139.4 139.4 139.4 139.4 139.3 ]plong + s[ 286.0 286.0 286.0 286.1 286.1 286.1 286.1 286.1 286.2 286.2 286.2 286.2 286.3 286.3 286.3 286.3 286.3 286.4 + 286.4 286.4 286.4 286.5 286.5 286.5 286.5 286.5 286.6 286.6 286.6 286.6 286.7 ][ 140.7 140.7 140.6 140.6 140.6 + 140.6 140.6 140.5 140.5 140.5 140.5 140.5 140.5 140.4 140.4 140.4 140.4 140.4 140.4 140.3 140.3 140.3 140.3 + 140.3 140.3 140.2 140.2 140.2 140.2 140.2 140.2 ]plong + s[ 286.7 286.7 286.7 286.7 286.7 286.8 286.8 286.8 286.8 286.8 286.9 286.9 286.9 286.9 286.9 287.0 287.0 287.0 + 287.0 287.0 287.1 287.1 287.1 287.1 287.2 287.2 ][ 140.2 140.1 140.1 140.1 140.1 140.1 140.0 140.0 140.0 140.0 + 140.0 140.0 139.9 139.9 139.9 139.9 139.9 139.8 139.8 139.8 139.8 139.8 139.8 139.7 139.7 139.7 ]plong + s[ 501.2 501.2 501.2 501.2 501.2 501.1 501.1 501.1 501.1 501.0 501.0 501.0 501.0 501.0 500.9 500.9 500.9 500.9 + 500.9 500.8 500.8 500.8 500.8 500.7 500.7 500.7 ][ 140.2 140.1 140.1 140.1 140.1 140.1 140.0 140.0 140.0 140.0 + 140.0 140.0 139.9 139.9 139.9 139.9 139.9 139.8 139.8 139.8 139.8 139.8 139.8 139.7 139.7 139.7 ]plong + s[ 501.9 501.9 501.8 501.8 501.8 501.8 501.8 501.7 501.7 501.7 501.7 501.6 501.6 501.6 501.6 501.6 501.5 501.5 + 501.5 501.5 501.4 501.4 501.4 501.4 501.4 501.3 501.3 501.3 501.3 501.3 501.2 ][ 140.7 140.7 140.6 140.6 140.6 + 140.6 140.6 140.5 140.5 140.5 140.5 140.5 140.5 140.4 140.4 140.4 140.4 140.4 140.4 140.4 140.3 140.3 140.3 + 140.3 140.3 140.2 140.2 140.2 140.2 140.2 140.2 ]plong + s[ 284.8 284.9 284.9 284.9 284.9 284.9 284.9 285.0 285.0 285.0 285.0 285.1 285.1 285.1 285.1 285.1 285.2 285.2 + 285.2 285.2 285.2 285.3 285.3 285.3 285.3 285.4 285.4 285.4 285.4 285.4 285.5 285.5 285.5 285.5 285.6 285.6 + 285.6 285.6 285.6 285.7 285.7 ][ 141.7 141.6 141.6 141.6 141.6 141.6 141.5 141.5 141.5 141.5 141.5 141.5 141.4 + 141.4 141.4 141.4 141.4 141.4 141.3 141.3 141.3 141.3 141.3 141.3 141.2 141.2 141.2 141.2 141.2 141.1 141.1 + 141.1 141.1 141.1 141.1 141.0 141.0 141.0 141.0 141.0 140.9 ]plong + s[ 285.7 285.7 285.7 285.8 285.8 285.8 285.8 285.8 285.9 285.9 285.9 285.9 285.9 285.9 286.0 286.0 ][ 140.9 140.9 + 140.9 140.9 140.9 140.9 140.8 140.8 140.8 140.8 140.8 140.7 140.7 140.7 140.7 140.7 ]plong + s[ 502.2 502.2 502.2 502.1 502.1 502.1 502.1 502.1 502.0 502.0 502.0 502.0 501.9 501.9 501.9 501.9 ][ 140.9 140.9 + 140.9 140.9 140.9 140.9 140.8 140.8 140.8 140.8 140.8 140.7 140.7 140.7 140.7 140.7 ]plong + s[ 503.1 503.0 503.0 503.0 503.0 503.0 502.9 502.9 502.9 502.9 502.8 502.8 502.8 502.8 502.8 502.7 502.7 502.7 + 502.7 502.6 502.6 502.6 502.6 502.6 502.5 502.5 502.5 502.5 502.5 502.4 502.4 502.4 502.4 502.3 502.3 502.3 + 502.3 502.3 502.3 502.2 502.2 ][ 141.7 141.6 141.6 141.6 141.6 141.6 141.5 141.5 141.5 141.5 141.5 141.5 141.4 + 141.4 141.4 141.4 141.4 141.4 141.3 141.3 141.3 141.3 141.3 141.3 141.2 141.2 141.2 141.2 141.2 141.1 141.1 + 141.1 141.1 141.1 141.1 141.0 141.0 141.0 141.0 141.0 140.9 ]plong + 283.7 142.6 283.7 142.6 2 pls + s[ 283.7 283.7 283.8 283.8 283.8 283.8 283.8 283.9 283.9 283.9 283.9 284.0 284.0 284.0 284.0 284.0 284.0 284.1 + 284.1 284.1 284.1 284.1 284.2 284.2 284.2 284.2 284.3 284.3 284.3 284.3 284.3 284.4 284.4 284.4 284.4 284.4 + 284.5 284.5 284.5 284.5 284.6 284.6 284.6 284.6 284.6 284.7 284.7 284.7 ][ 142.6 142.6 142.6 142.6 142.5 142.5 + 142.5 142.5 142.5 142.4 142.4 142.4 142.4 142.4 142.4 142.3 142.3 142.3 142.3 142.3 142.3 142.2 142.2 142.2 + 142.2 142.2 142.2 142.1 142.1 142.1 142.1 142.1 142.0 142.0 142.0 142.0 142.0 142.0 141.9 141.9 141.9 141.9 + 141.9 141.8 141.8 141.8 141.8 141.8 ]plong + s[ 284.7 284.7 284.7 284.8 284.8 284.8 284.8 ][ 141.8 141.7 141.7 141.7 141.7 141.7 141.7 ]plong + s[ 503.2 503.2 503.1 503.1 503.1 503.1 503.1 ][ 141.8 141.8 141.7 141.7 141.7 141.7 141.7 ]plong + s[ 503.2 503.2 503.2 503.3 503.3 503.3 503.3 503.3 503.3 503.4 503.4 503.4 503.4 503.5 503.5 503.5 503.5 503.5 + 503.6 503.6 503.6 503.6 503.6 503.7 503.7 503.7 503.7 503.8 503.8 503.8 503.8 503.8 503.9 503.9 503.9 503.9 + 503.9 504.0 504.0 504.0 504.0 504.1 504.1 504.1 504.1 504.1 504.2 504.2 ][ 141.8 141.8 141.8 141.8 141.8 141.9 + 141.9 141.9 141.9 141.9 142.0 142.0 142.0 142.0 142.0 142.0 142.1 142.1 142.1 142.1 142.1 142.2 142.2 142.2 + 142.2 142.2 142.2 142.3 142.3 142.3 142.3 142.3 142.3 142.4 142.4 142.4 142.4 142.4 142.4 142.5 142.5 142.5 + 142.5 142.5 142.6 142.6 142.6 142.6 ]plong + 504.2 142.6 504.2 142.6 2 pls + s[ 282.6 282.6 282.6 282.6 282.6 282.7 282.7 282.7 282.7 282.7 ][ 143.6 143.6 143.6 143.6 143.5 143.5 143.5 143.5 + 143.5 143.4 ]plong + s[ 282.7 282.8 282.8 282.8 282.8 282.9 282.9 282.9 282.9 282.9 283.0 283.0 283.0 283.0 283.0 283.0 283.1 283.1 + 283.1 283.1 283.2 283.2 283.2 283.2 283.2 283.3 283.3 283.3 283.3 283.3 283.4 283.4 283.4 283.4 283.5 283.5 + 283.5 283.5 283.5 283.6 283.6 283.6 283.6 283.6 283.7 283.7 ][ 143.4 143.4 143.4 143.4 143.4 143.4 143.3 143.3 + 143.3 143.3 143.3 143.3 143.2 143.2 143.2 143.2 143.2 143.2 143.1 143.1 143.1 143.1 143.1 143.0 143.0 143.0 + 143.0 143.0 142.9 142.9 142.9 142.9 142.9 142.9 142.8 142.8 142.8 142.8 142.8 142.7 142.7 142.7 142.7 142.7 + 142.7 142.6 ]plong + s[ 504.2 504.2 504.2 504.2 504.3 504.3 504.3 504.3 504.4 504.4 504.4 504.4 504.4 504.5 504.5 504.5 504.5 504.6 + 504.6 504.6 504.6 504.6 504.7 504.7 504.7 504.7 504.7 504.8 504.8 504.8 504.8 504.8 504.9 504.9 504.9 504.9 + 505.0 505.0 505.0 505.0 505.0 505.1 505.1 505.1 505.1 505.2 ][ 142.6 142.7 142.7 142.7 142.7 142.7 142.7 142.8 + 142.8 142.8 142.8 142.8 142.9 142.9 142.9 142.9 142.9 142.9 143.0 143.0 143.0 143.0 143.0 143.1 143.1 143.1 + 143.1 143.1 143.2 143.2 143.2 143.2 143.2 143.2 143.3 143.3 143.3 143.3 143.3 143.3 143.4 143.4 143.4 143.4 + 143.4 143.4 ]plong + s[ 505.3 505.3 505.3 505.3 505.2 505.2 505.2 505.2 505.2 505.2 ][ 143.6 143.6 143.6 143.6 143.5 143.5 143.5 143.5 + 143.5 143.4 ]plong + s[ 281.5 281.5 281.5 281.5 281.5 281.6 281.6 281.6 281.6 281.6 281.7 281.7 281.7 281.7 281.7 281.8 ][ 144.6 144.6 + 144.6 144.5 144.5 144.5 144.5 144.5 144.4 144.4 144.4 144.4 144.4 144.3 144.3 144.3 ]plong + s[ 281.8 281.8 281.8 281.8 281.9 281.9 281.9 281.9 281.9 282.0 282.0 282.0 282.0 282.0 282.0 282.1 282.1 282.1 + 282.1 282.2 282.2 282.2 282.2 282.2 282.3 282.3 282.3 282.3 282.3 282.4 282.4 282.4 282.4 282.5 282.5 282.5 + 282.5 282.5 282.6 ][ 144.3 144.3 144.3 144.3 144.3 144.2 144.2 144.2 144.2 144.2 144.1 144.1 144.1 144.1 144.1 + 144.0 144.0 144.0 144.0 144.0 144.0 143.9 143.9 143.9 143.9 143.9 143.8 143.8 143.8 143.8 143.8 143.7 143.7 + 143.7 143.7 143.7 143.7 143.6 143.6 ]plong + s[ 506.1 506.1 506.1 506.1 506.1 506.0 506.0 506.0 506.0 506.0 505.9 505.9 505.9 505.9 505.9 505.8 505.8 505.8 + 505.8 505.7 505.7 505.7 505.7 505.7 505.6 505.6 505.6 505.6 505.6 505.5 505.5 505.5 505.5 505.4 505.4 505.4 + 505.4 505.4 505.3 505.3 ][ 144.3 144.3 144.3 144.3 144.3 144.3 144.2 144.2 144.2 144.2 144.2 144.1 144.1 144.1 + 144.1 144.1 144.0 144.0 144.0 144.0 144.0 144.0 143.9 143.9 143.9 143.9 143.9 143.8 143.8 143.8 143.8 143.8 + 143.8 143.7 143.7 143.7 143.7 143.7 143.6 143.6 ]plong + s[ 506.4 506.4 506.4 506.4 506.3 506.3 506.3 506.3 506.3 506.2 506.2 506.2 506.2 506.2 506.1 ][ 144.6 144.6 144.6 + 144.5 144.5 144.5 144.5 144.5 144.4 144.4 144.4 144.4 144.4 144.3 144.3 ]plong + s[ 280.4 280.4 280.4 280.4 280.4 280.5 280.5 280.5 280.5 280.5 280.6 280.6 280.6 280.6 280.7 280.7 280.7 280.7 + 280.7 280.8 280.8 ][ 145.6 145.6 145.5 145.5 145.5 145.5 145.5 145.4 145.4 145.4 145.4 145.4 145.3 145.3 145.3 + 145.3 145.3 145.3 145.2 145.2 145.2 ]plong + s[ 280.8 280.8 280.8 280.8 280.9 280.9 280.9 280.9 280.9 281.0 281.0 281.0 281.0 281.0 281.1 281.1 281.1 281.1 + 281.1 281.2 281.2 281.2 281.2 281.2 281.3 281.3 281.3 281.3 281.3 281.4 281.4 281.4 281.4 281.5 ][ 145.2 145.2 + 145.2 145.2 145.1 145.1 145.1 145.1 145.1 145.0 145.0 145.0 145.0 145.0 145.0 144.9 144.9 144.9 144.9 144.9 + 144.8 144.8 144.8 144.8 144.8 144.7 144.7 144.7 144.7 144.7 144.6 144.6 144.6 144.6 ]plong + s[ 507.1 507.1 507.1 507.1 507.0 507.0 507.0 507.0 507.0 506.9 506.9 506.9 506.9 506.8 506.8 506.8 506.8 506.8 + 506.7 506.7 506.7 506.7 506.7 506.6 506.6 506.6 506.6 506.6 506.5 506.5 506.5 506.5 506.4 506.4 ][ 145.2 145.2 + 145.2 145.2 145.1 145.1 145.1 145.1 145.1 145.0 145.0 145.0 145.0 145.0 145.0 144.9 144.9 144.9 144.9 144.9 + 144.8 144.8 144.8 144.8 144.8 144.7 144.7 144.7 144.7 144.7 144.7 144.6 144.6 144.6 ]plong + s[ 507.5 507.5 507.5 507.5 507.4 507.4 507.4 507.4 507.4 507.3 507.3 507.3 507.3 507.2 507.2 507.2 507.2 507.2 + 507.2 507.1 507.1 ][ 145.6 145.6 145.5 145.5 145.5 145.5 145.5 145.4 145.4 145.4 145.4 145.4 145.4 145.3 145.3 + 145.3 145.3 145.3 145.2 145.2 145.2 ]plong + s[ 279.3 279.3 279.3 279.4 279.4 279.4 279.4 279.4 279.5 279.5 279.5 279.5 279.5 279.6 279.6 279.6 279.6 279.6 + 279.7 279.7 279.7 279.7 279.8 279.8 279.8 ][ 146.6 146.5 146.5 146.5 146.5 146.5 146.4 146.4 146.4 146.4 146.4 + 146.3 146.3 146.3 146.3 146.3 146.2 146.2 146.2 146.2 146.2 146.2 146.2 146.1 146.1 ]plong + s[ 279.8 279.8 279.8 279.9 279.9 279.9 279.9 279.9 280.0 280.0 280.0 280.0 280.0 280.1 280.1 280.1 280.1 280.1 + 280.1 280.2 280.2 280.2 280.2 280.3 280.3 280.3 280.3 280.3 280.4 ][ 146.1 146.1 146.1 146.1 146.0 146.0 146.0 + 146.0 146.0 145.9 145.9 145.9 145.9 145.9 145.8 145.8 145.8 145.8 145.8 145.7 145.7 145.7 145.7 145.7 145.7 + 145.6 145.6 145.6 145.6 ]plong + s[ 508.1 508.1 508.1 508.0 508.0 508.0 508.0 508.0 507.9 507.9 507.9 507.9 507.9 507.8 507.8 507.8 507.8 507.8 + 507.7 507.7 507.7 507.7 507.6 507.6 507.6 507.6 507.6 507.5 507.5 ][ 146.1 146.1 146.1 146.1 146.0 146.0 146.0 + 146.0 146.0 145.9 145.9 145.9 145.9 145.9 145.8 145.8 145.8 145.8 145.8 145.8 145.7 145.7 145.7 145.7 145.7 + 145.6 145.6 145.6 145.6 ]plong + s[ 508.6 508.6 508.5 508.5 508.5 508.5 508.5 508.4 508.4 508.4 508.4 508.4 508.3 508.3 508.3 508.3 508.3 508.2 + 508.2 508.2 508.2 508.1 508.1 508.1 508.1 ][ 146.6 146.5 146.5 146.5 146.5 146.5 146.4 146.4 146.4 146.4 146.4 + 146.3 146.3 146.3 146.3 146.3 146.2 146.2 146.2 146.2 146.2 146.2 146.2 146.1 146.1 ]plong + s[ 278.2 278.3 278.3 278.3 278.3 278.3 278.4 278.4 278.4 278.4 278.4 278.5 278.5 278.5 278.5 278.5 278.6 278.6 + 278.6 278.6 278.6 278.7 278.7 278.7 278.7 278.8 278.8 278.8 278.8 ][ 147.5 147.5 147.5 147.5 147.5 147.4 147.4 + 147.4 147.4 147.4 147.3 147.3 147.3 147.3 147.3 147.2 147.2 147.2 147.2 147.2 147.2 147.1 147.1 147.1 147.1 + 147.1 147.1 147.0 147.0 ]plong + s[ 278.8 278.8 278.9 278.9 278.9 278.9 278.9 279.0 279.0 279.0 279.0 279.0 279.1 279.1 279.1 279.1 279.1 279.1 + 279.2 279.2 279.2 279.2 279.3 279.3 279.3 ][ 147.0 147.0 147.0 147.0 146.9 146.9 146.9 146.9 146.9 146.8 146.8 + 146.8 146.8 146.8 146.7 146.7 146.7 146.7 146.7 146.6 146.6 146.6 146.6 146.6 146.6 ]plong + s[ 509.1 509.1 509.0 509.0 509.0 509.0 509.0 508.9 508.9 508.9 508.9 508.9 508.8 508.8 508.8 508.8 508.8 508.7 + 508.7 508.7 508.7 508.6 508.6 508.6 508.6 ][ 147.0 147.0 147.0 147.0 146.9 146.9 146.9 146.9 146.9 146.8 146.8 + 146.8 146.8 146.8 146.7 146.7 146.7 146.7 146.7 146.7 146.6 146.6 146.6 146.6 146.6 ]plong + s[ 509.6 509.6 509.6 509.6 509.6 509.5 509.5 509.5 509.5 509.5 509.4 509.4 509.4 509.4 509.4 509.3 509.3 509.3 + 509.3 509.2 509.2 509.2 509.2 509.2 509.1 509.1 509.1 509.1 509.1 ][ 147.5 147.5 147.5 147.5 147.5 147.4 147.4 + 147.4 147.4 147.4 147.3 147.3 147.3 147.3 147.3 147.2 147.2 147.2 147.2 147.2 147.2 147.2 147.1 147.1 147.1 + 147.1 147.1 147.0 147.0 ]plong + s[ 277.2 277.2 277.2 277.2 277.3 277.3 277.3 277.3 277.4 277.4 277.4 277.4 277.4 277.5 277.5 277.5 277.5 277.5 + 277.6 277.6 277.6 277.6 277.6 277.7 277.7 277.7 277.7 277.7 277.8 277.8 277.8 277.8 277.8 ][ 148.5 148.5 148.5 + 148.5 148.5 148.4 148.4 148.4 148.4 148.4 148.3 148.3 148.3 148.3 148.3 148.2 148.2 148.2 148.2 148.2 148.2 + 148.1 148.1 148.1 148.1 148.1 148.0 148.0 148.0 148.0 148.0 147.9 147.9 ]plong + s[ 277.8 277.9 277.9 277.9 277.9 277.9 278.0 278.0 278.0 278.0 278.0 278.1 278.1 278.1 278.1 278.1 278.2 278.2 + 278.2 278.2 278.2 ][ 147.9 147.9 147.9 147.9 147.8 147.8 147.8 147.8 147.8 147.7 147.7 147.7 147.7 147.7 147.7 + 147.6 147.6 147.6 147.6 147.6 147.5 ]plong + s[ 510.1 510.0 510.0 510.0 510.0 510.0 509.9 509.9 509.9 509.9 509.8 509.8 509.8 509.8 509.8 509.7 509.7 509.7 + 509.7 509.7 509.6 ][ 147.9 147.9 147.9 147.9 147.8 147.8 147.8 147.8 147.8 147.8 147.7 147.7 147.7 147.7 147.7 + 147.6 147.6 147.6 147.6 147.6 147.5 ]plong + s[ 510.7 510.6 510.6 510.6 510.6 510.6 510.5 510.5 510.5 510.5 510.5 510.4 510.4 510.4 510.4 510.4 510.3 510.3 + 510.3 510.3 510.3 510.2 510.2 510.2 510.2 510.2 510.1 510.1 510.1 510.1 510.1 510.1 ][ 148.5 148.5 148.5 148.5 + 148.4 148.4 148.4 148.4 148.4 148.3 148.3 148.3 148.3 148.3 148.2 148.2 148.2 148.2 148.2 148.2 148.1 148.1 + 148.1 148.1 148.1 148.0 148.0 148.0 148.0 148.0 147.9 147.9 ]plong + s[ 276.2 276.2 276.2 276.3 276.3 276.3 276.3 276.3 276.4 276.4 276.4 276.4 276.4 276.5 276.5 276.5 276.5 276.5 + 276.6 276.6 276.6 276.6 276.6 276.7 276.7 276.7 276.7 276.7 276.8 276.8 276.8 276.8 276.8 276.9 ][ 149.5 149.5 + 149.5 149.4 149.4 149.4 149.4 149.4 149.3 149.3 149.3 149.3 149.3 149.2 149.2 149.2 149.2 149.2 149.1 149.1 + 149.1 149.1 149.1 149.1 149.0 149.0 149.0 149.0 149.0 148.9 148.9 148.9 148.9 148.9 ]plong + s[ 276.9 276.9 276.9 276.9 276.9 277.0 277.0 277.0 277.0 277.0 277.1 277.1 277.1 277.1 277.1 277.2 277.2 277.2 ][ + 148.9 148.8 148.8 148.8 148.8 148.8 148.7 148.7 148.7 148.7 148.7 148.6 148.6 148.6 148.6 148.6 148.5 148.5 ]plong + s[ 511.0 511.0 511.0 511.0 511.0 510.9 510.9 510.9 510.9 510.9 510.8 510.8 510.8 510.8 510.8 510.7 510.7 510.7 + 510.7 ][ 148.9 148.8 148.8 148.8 148.8 148.8 148.7 148.7 148.7 148.7 148.7 148.6 148.6 148.6 148.6 148.6 148.6 + 148.5 148.5 ]plong + s[ 511.7 511.7 511.6 511.6 511.6 511.6 511.6 511.5 511.5 511.5 511.5 511.5 511.4 511.4 511.4 511.4 511.4 511.3 + 511.3 511.3 511.3 511.3 511.2 511.2 511.2 511.2 511.2 511.1 511.1 511.1 511.1 511.1 511.0 511.0 ][ 149.5 149.5 + 149.5 149.4 149.4 149.4 149.4 149.4 149.3 149.3 149.3 149.3 149.3 149.2 149.2 149.2 149.2 149.2 149.1 149.1 + 149.1 149.1 149.1 149.1 149.0 149.0 149.0 149.0 149.0 148.9 148.9 148.9 148.9 148.9 ]plong + s[ 275.2 275.2 275.2 275.2 275.3 275.3 275.3 275.3 275.3 275.4 275.4 275.4 275.4 275.4 275.5 275.5 275.5 275.5 + 275.5 275.6 275.6 275.6 275.6 275.6 275.7 275.7 275.7 275.7 275.7 275.8 275.8 275.8 275.8 275.8 275.9 275.9 ][ + 150.5 150.5 150.4 150.4 150.4 150.4 150.4 150.3 150.3 150.3 150.3 150.3 150.2 150.2 150.2 150.2 150.2 150.1 + 150.1 150.1 150.1 150.1 150.1 150.0 150.0 150.0 150.0 150.0 149.9 149.9 149.9 149.9 149.9 149.8 149.8 149.8 ]plong + s[ 275.9 275.9 275.9 275.9 276.0 276.0 276.0 276.0 276.0 276.1 276.1 276.1 276.1 276.2 276.2 276.2 276.2 ][ 149.8 + 149.8 149.8 149.7 149.7 149.7 149.7 149.7 149.6 149.6 149.6 149.6 149.6 149.6 149.5 149.5 149.5 ]plong + s[ 512.0 512.0 512.0 511.9 511.9 511.9 511.9 511.9 511.8 511.8 511.8 511.8 511.8 511.7 511.7 511.7 511.7 ][ 149.8 + 149.8 149.8 149.8 149.7 149.7 149.7 149.7 149.7 149.6 149.6 149.6 149.6 149.6 149.5 149.5 149.5 ]plong + s[ 512.7 512.7 512.7 512.6 512.6 512.6 512.6 512.6 512.5 512.5 512.5 512.5 512.5 512.4 512.4 512.4 512.4 512.4 + 512.3 512.3 512.3 512.3 512.3 512.2 512.2 512.2 512.2 512.2 512.1 512.1 512.1 512.1 512.1 512.0 512.0 512.0 ][ + 150.5 150.5 150.4 150.4 150.4 150.4 150.4 150.3 150.3 150.3 150.3 150.3 150.2 150.2 150.2 150.2 150.2 150.1 + 150.1 150.1 150.1 150.1 150.1 150.0 150.0 150.0 150.0 150.0 149.9 149.9 149.9 149.9 149.9 149.8 149.8 149.8 ]plong + s[ 274.2 274.2 274.2 274.3 274.3 274.3 274.3 274.3 274.4 274.4 274.4 274.4 274.4 274.5 274.5 274.5 274.5 274.5 + 274.6 274.6 274.6 274.6 274.6 274.7 274.7 274.7 274.7 274.7 274.8 274.8 274.8 274.8 274.8 274.9 274.9 274.9 ][ + 151.5 151.4 151.4 151.4 151.4 151.4 151.3 151.3 151.3 151.3 151.3 151.2 151.2 151.2 151.2 151.2 151.1 151.1 + 151.1 151.1 151.1 151.1 151.0 151.0 151.0 151.0 151.0 150.9 150.9 150.9 150.9 150.9 150.8 150.8 150.8 150.8 ]plong + s[ 274.9 274.9 274.9 275.0 275.0 275.0 275.0 275.0 275.1 275.1 275.1 275.1 275.1 275.2 275.2 275.2 ][ 150.8 150.8 + 150.7 150.7 150.7 150.7 150.7 150.6 150.6 150.6 150.6 150.6 150.5 150.5 150.5 150.5 ]plong + s[ 513.0 513.0 513.0 512.9 512.9 512.9 512.9 512.9 512.8 512.8 512.8 512.8 512.8 512.7 512.7 512.7 ][ 150.8 150.8 + 150.7 150.7 150.7 150.7 150.7 150.6 150.6 150.6 150.6 150.6 150.5 150.5 150.5 150.5 ]plong + s[ 513.7 513.7 513.6 513.6 513.6 513.6 513.6 513.5 513.5 513.5 513.5 513.5 513.4 513.4 513.4 513.4 513.4 513.3 + 513.3 513.3 513.3 513.3 513.2 513.2 513.2 513.2 513.2 513.1 513.1 513.1 513.1 513.1 513.0 513.0 513.0 513.0 ][ + 151.5 151.4 151.4 151.4 151.4 151.4 151.3 151.3 151.3 151.3 151.3 151.2 151.2 151.2 151.2 151.2 151.1 151.1 + 151.1 151.1 151.1 151.1 151.0 151.0 151.0 151.0 151.0 150.9 150.9 150.9 150.9 150.9 150.8 150.8 150.8 150.8 ]plong + s[ 273.2 273.3 273.3 273.3 273.3 273.3 273.3 273.4 273.4 273.4 273.4 273.4 273.5 273.5 273.5 273.5 273.5 273.6 + 273.6 273.6 273.6 273.6 273.7 273.7 273.7 273.7 273.7 273.8 273.8 273.8 273.8 273.8 273.9 273.9 273.9 273.9 ][ + 152.4 152.4 152.4 152.4 152.4 152.3 152.3 152.3 152.3 152.3 152.2 152.2 152.2 152.2 152.2 152.1 152.1 152.1 + 152.1 152.1 152.0 152.0 152.0 152.0 152.0 152.0 151.9 151.9 151.9 151.9 151.9 151.8 151.8 151.8 151.8 151.8 ]plong + s[ 273.9 273.9 274.0 274.0 274.0 274.0 274.0 274.1 274.1 274.1 274.1 274.1 274.2 274.2 274.2 274.2 ][ 151.8 151.7 + 151.7 151.7 151.7 151.7 151.6 151.6 151.6 151.6 151.6 151.5 151.5 151.5 151.5 151.5 ]plong + s[ 514.0 514.0 513.9 513.9 513.9 513.9 513.9 513.8 513.8 513.8 513.8 513.8 513.7 513.7 513.7 513.7 ][ 151.8 151.7 + 151.7 151.7 151.7 151.7 151.6 151.6 151.6 151.6 151.6 151.5 151.5 151.5 151.5 151.5 ]plong + s[ 514.7 514.6 514.6 514.6 514.6 514.6 514.5 514.5 514.5 514.5 514.5 514.4 514.4 514.4 514.4 514.4 514.3 514.3 + 514.3 514.3 514.3 514.2 514.2 514.2 514.2 514.2 514.1 514.1 514.1 514.1 514.1 514.0 514.0 514.0 514.0 514.0 ][ + 152.4 152.4 152.4 152.4 152.4 152.3 152.3 152.3 152.3 152.3 152.2 152.2 152.2 152.2 152.2 152.1 152.1 152.1 + 152.1 152.1 152.0 152.0 152.0 152.0 152.0 152.0 151.9 151.9 151.9 151.9 151.9 151.8 151.8 151.8 151.8 151.8 ]plong + s[ 272.3 272.3 272.3 272.3 272.4 272.4 272.4 272.4 272.4 272.5 272.5 272.5 272.5 272.5 272.6 272.6 272.6 272.6 + 272.6 272.7 272.7 272.7 272.7 272.7 272.8 272.8 272.8 272.8 272.8 272.9 272.9 272.9 272.9 272.9 ][ 153.4 153.4 + 153.4 153.4 153.3 153.3 153.3 153.3 153.3 153.2 153.2 153.2 153.2 153.2 153.1 153.1 153.1 153.1 153.1 153.0 + 153.0 153.0 153.0 153.0 153.0 152.9 152.9 152.9 152.9 152.9 152.8 152.8 152.8 152.8 ]plong + s[ 272.9 273.0 273.0 273.0 273.0 273.0 273.0 273.1 273.1 273.1 273.1 273.1 273.2 273.2 273.2 273.2 273.2 ][ 152.8 + 152.8 152.7 152.7 152.7 152.7 152.6 152.6 152.6 152.6 152.6 152.5 152.5 152.5 152.5 152.5 152.4 ]plong + s[ 514.9 514.9 514.9 514.9 514.9 514.9 514.8 514.8 514.8 514.8 514.8 514.7 514.7 514.7 514.7 514.7 514.7 ][ 152.8 + 152.8 152.7 152.7 152.7 152.7 152.7 152.6 152.6 152.6 152.6 152.6 152.5 152.5 152.5 152.5 152.4 ]plong + s[ 515.6 515.6 515.6 515.5 515.5 515.5 515.5 515.5 515.4 515.4 515.4 515.4 515.4 515.3 515.3 515.3 515.3 515.3 + 515.2 515.2 515.2 515.2 515.2 515.1 515.1 515.1 515.1 515.1 515.0 515.0 515.0 515.0 515.0 514.9 ][ 153.4 153.4 + 153.4 153.4 153.3 153.3 153.3 153.3 153.3 153.2 153.2 153.2 153.2 153.2 153.1 153.1 153.1 153.1 153.1 153.0 + 153.0 153.0 153.0 153.0 153.0 152.9 152.9 152.9 152.9 152.9 152.8 152.8 152.8 152.8 ]plong + s[ 271.3 271.4 271.4 271.4 271.4 271.4 271.5 271.5 271.5 271.5 271.5 271.6 271.6 271.6 271.6 271.6 271.7 271.7 + 271.7 271.7 271.7 271.8 271.8 271.8 271.8 271.8 271.9 271.9 271.9 271.9 271.9 271.9 272.0 ][ 154.4 154.4 154.4 + 154.3 154.3 154.3 154.3 154.3 154.2 154.2 154.2 154.2 154.2 154.1 154.1 154.1 154.1 154.1 154.0 154.0 154.0 + 154.0 154.0 154.0 153.9 153.9 153.9 153.9 153.9 153.8 153.8 153.8 153.8 ]plong + s[ 272.0 272.0 272.0 272.0 272.0 272.1 272.1 272.1 272.1 272.1 272.2 272.2 272.2 272.2 272.2 272.3 272.3 272.3 ][ + 153.8 153.7 153.7 153.7 153.7 153.7 153.6 153.6 153.6 153.6 153.6 153.5 153.5 153.5 153.5 153.5 153.4 153.4 ]plong + s[ 515.9 515.9 515.9 515.9 515.9 515.9 515.8 515.8 515.8 515.8 515.8 515.7 515.7 515.7 515.7 515.7 515.6 515.6 + 515.6 ][ 153.8 153.8 153.8 153.7 153.7 153.7 153.7 153.6 153.6 153.6 153.6 153.6 153.5 153.5 153.5 153.5 153.5 + 153.4 153.4 ]plong + s[ 516.5 516.5 516.5 516.5 516.5 516.4 516.4 516.4 516.4 516.4 516.3 516.3 516.3 516.3 516.3 516.2 516.2 516.2 + 516.2 516.2 516.1 516.1 516.1 516.1 516.1 516.0 516.0 516.0 516.0 516.0 515.9 515.9 ][ 154.4 154.4 154.4 154.3 + 154.3 154.3 154.3 154.3 154.2 154.2 154.2 154.2 154.2 154.1 154.1 154.1 154.1 154.1 154.0 154.0 154.0 154.0 + 154.0 154.0 153.9 153.9 153.9 153.9 153.9 153.8 153.8 153.8 ]plong + s[ 270.4 270.5 270.5 270.5 270.5 270.5 270.6 270.6 270.6 270.6 270.6 270.6 270.7 270.7 270.7 270.7 270.7 270.8 + 270.8 270.8 270.8 270.8 270.9 270.9 270.9 270.9 270.9 271.0 271.0 ][ 155.4 155.4 155.3 155.3 155.3 155.3 155.3 + 155.2 155.2 155.2 155.2 155.1 155.1 155.1 155.1 155.1 155.0 155.0 155.0 155.0 155.0 155.0 154.9 154.9 154.9 + 154.9 154.9 154.8 154.8 ]plong + s[ 271.0 271.0 271.0 271.0 271.1 271.1 271.1 271.1 271.1 271.2 271.2 271.2 271.2 271.2 271.2 271.3 271.3 271.3 + 271.3 271.3 271.3 ][ 154.8 154.8 154.8 154.8 154.7 154.7 154.7 154.7 154.7 154.6 154.6 154.6 154.6 154.5 154.5 + 154.5 154.5 154.5 154.4 154.4 154.4 ]plong + s[ 516.9 516.9 516.9 516.9 516.8 516.8 516.8 516.8 516.8 516.7 516.7 516.7 516.7 516.7 516.7 516.6 516.6 516.6 + 516.6 516.6 516.5 ][ 154.8 154.8 154.8 154.8 154.7 154.7 154.7 154.7 154.7 154.6 154.6 154.6 154.6 154.6 154.5 + 154.5 154.5 154.5 154.4 154.4 154.4 ]plong + s[ 517.4 517.4 517.4 517.4 517.4 517.3 517.3 517.3 517.3 517.3 517.2 517.2 517.2 517.2 517.2 517.2 517.1 517.1 + 517.1 517.1 517.1 517.0 517.0 517.0 517.0 517.0 516.9 516.9 516.9 ][ 155.4 155.4 155.3 155.3 155.3 155.3 155.3 + 155.2 155.2 155.2 155.2 155.1 155.1 155.1 155.1 155.1 155.0 155.0 155.0 155.0 155.0 155.0 154.9 154.9 154.9 + 154.9 154.9 154.8 154.8 ]plong + s[ 269.5 269.6 269.6 269.6 269.6 269.6 269.6 269.7 269.7 269.7 269.7 269.7 269.8 269.8 269.8 269.8 269.8 269.9 + 269.9 269.9 269.9 269.9 270.0 270.0 270.0 ][ 156.4 156.3 156.3 156.3 156.3 156.2 156.2 156.2 156.2 156.2 156.1 + 156.1 156.1 156.1 156.1 156.0 156.0 156.0 156.0 156.0 155.9 155.9 155.9 155.9 155.9 ]plong + s[ 270.0 270.0 270.0 270.1 270.1 270.1 270.1 270.1 270.1 270.2 270.2 270.2 270.2 270.2 270.3 270.3 270.3 270.3 + 270.3 270.4 270.4 270.4 270.4 270.4 270.4 ][ 155.9 155.9 155.8 155.8 155.8 155.8 155.7 155.7 155.7 155.7 155.7 + 155.6 155.6 155.6 155.6 155.6 155.5 155.5 155.5 155.5 155.5 155.4 155.4 155.4 155.4 ]plong + s[ 517.9 517.9 517.8 517.8 517.8 517.8 517.8 517.8 517.8 517.7 517.7 517.7 517.7 517.7 517.6 517.6 517.6 517.6 + 517.6 517.5 517.5 517.5 517.5 517.5 517.4 ][ 155.9 155.9 155.8 155.8 155.8 155.8 155.7 155.7 155.7 155.7 155.7 + 155.6 155.6 155.6 155.6 155.6 155.5 155.5 155.5 155.5 155.5 155.4 155.4 155.4 155.4 ]plong + s[ 518.3 518.3 518.3 518.3 518.3 518.3 518.2 518.2 518.2 518.2 518.2 518.1 518.1 518.1 518.1 518.1 518.0 518.0 + 518.0 518.0 518.0 517.9 517.9 517.9 517.9 ][ 156.4 156.3 156.3 156.3 156.3 156.2 156.2 156.2 156.2 156.2 156.1 + 156.1 156.1 156.1 156.1 156.0 156.0 156.0 156.0 156.0 155.9 155.9 155.9 155.9 155.9 ]plong + s[ 268.6 268.7 268.7 268.7 268.7 268.7 268.8 268.8 268.8 268.8 268.8 268.8 268.9 268.9 268.9 268.9 268.9 269.0 + 269.0 269.0 269.0 ][ 157.3 157.3 157.3 157.3 157.3 157.2 157.2 157.2 157.2 157.1 157.1 157.1 157.1 157.1 157.0 + 157.0 157.0 157.0 157.0 156.9 156.9 ]plong + s[ 269.0 269.0 269.1 269.1 269.1 269.1 269.1 269.2 269.2 269.2 269.2 269.2 269.2 269.3 269.3 269.3 269.3 269.3 + 269.4 269.4 269.4 269.4 269.4 269.4 269.5 269.5 269.5 269.5 269.5 ][ 156.9 156.9 156.9 156.9 156.9 156.8 156.8 + 156.8 156.8 156.7 156.7 156.7 156.7 156.7 156.6 156.6 156.6 156.6 156.6 156.5 156.5 156.5 156.5 156.5 156.4 + 156.4 156.4 156.4 156.4 ]plong + s[ 518.9 518.8 518.8 518.8 518.8 518.8 518.8 518.7 518.7 518.7 518.7 518.7 518.7 518.6 518.6 518.6 518.6 518.6 + 518.5 518.5 518.5 518.5 518.5 518.4 518.4 518.4 518.4 518.4 518.3 ][ 156.9 156.9 156.9 156.9 156.9 156.8 156.8 + 156.8 156.8 156.8 156.7 156.7 156.7 156.7 156.6 156.6 156.6 156.6 156.6 156.5 156.5 156.5 156.5 156.5 156.4 + 156.4 156.4 156.4 156.4 ]plong + s[ 519.2 519.2 519.2 519.2 519.2 519.1 519.1 519.1 519.1 519.1 519.1 519.0 519.0 519.0 519.0 519.0 518.9 518.9 + 518.9 518.9 518.9 ][ 157.3 157.3 157.3 157.3 157.3 157.2 157.2 157.2 157.2 157.1 157.1 157.1 157.1 157.1 157.0 + 157.0 157.0 157.0 157.0 156.9 156.9 ]plong + s[ 267.7 267.8 267.8 267.8 267.8 267.8 267.9 267.9 267.9 267.9 267.9 268.0 268.0 268.0 268.0 268.0 ][ 158.3 158.3 + 158.3 158.3 158.2 158.2 158.2 158.2 158.2 158.1 158.1 158.1 158.1 158.1 158.0 158.0 ]plong + s[ 268.0 268.1 268.1 268.1 268.1 268.1 268.1 268.2 268.2 268.2 268.2 268.2 268.3 268.3 268.3 268.3 268.3 268.4 + 268.4 268.4 268.4 268.4 268.4 268.4 268.5 268.5 268.5 268.5 268.5 268.6 268.6 268.6 268.6 268.6 ][ 158.0 158.0 + 158.0 157.9 157.9 157.9 157.9 157.9 157.9 157.8 157.8 157.8 157.8 157.8 157.7 157.7 157.7 157.7 157.7 157.6 + 157.6 157.6 157.6 157.5 157.5 157.5 157.5 157.5 157.4 157.4 157.4 157.4 157.4 157.3 ]plong + s[ 519.8 519.8 519.8 519.8 519.8 519.8 519.8 519.7 519.7 519.7 519.7 519.7 519.6 519.6 519.6 519.6 519.6 519.5 + 519.5 519.5 519.5 519.5 519.4 519.4 519.4 519.4 519.4 519.4 519.3 519.3 519.3 519.3 519.3 519.2 ][ 158.0 158.0 + 158.0 158.0 157.9 157.9 157.9 157.9 157.9 157.8 157.8 157.8 157.8 157.8 157.7 157.7 157.7 157.7 157.7 157.6 + 157.6 157.6 157.6 157.5 157.5 157.5 157.5 157.5 157.4 157.4 157.4 157.4 157.4 157.3 ]plong + s[ 520.1 520.1 520.1 520.1 520.0 520.0 520.0 520.0 520.0 519.9 519.9 519.9 519.9 519.9 519.8 ][ 158.3 158.3 158.3 + 158.2 158.2 158.2 158.2 158.2 158.1 158.1 158.1 158.1 158.1 158.0 158.0 ]plong + s[ 266.9 266.9 266.9 266.9 267.0 267.0 267.0 267.0 267.0 267.1 ][ 159.3 159.3 159.3 159.2 159.2 159.2 159.2 159.2 + 159.1 159.1 ]plong + s[ 267.1 267.1 267.1 267.1 267.1 267.2 267.2 267.2 267.2 267.2 267.2 267.3 267.3 267.3 267.3 267.3 267.4 267.4 + 267.4 267.4 267.4 267.4 267.5 267.5 267.5 267.5 267.5 267.5 267.6 267.6 267.6 267.6 267.6 267.7 267.7 267.7 + 267.7 267.7 267.7 ][ 159.1 159.1 159.1 159.1 159.0 159.0 159.0 159.0 158.9 158.9 158.9 158.9 158.9 158.8 158.8 + 158.8 158.8 158.8 158.8 158.7 158.7 158.7 158.7 158.6 158.6 158.6 158.6 158.6 158.5 158.5 158.5 158.5 158.5 + 158.4 158.4 158.4 158.4 158.4 158.3 ]plong + s[ 520.8 520.8 520.8 520.8 520.7 520.7 520.7 520.7 520.7 520.7 520.7 520.6 520.6 520.6 520.6 520.6 520.5 520.5 + 520.5 520.5 520.5 520.4 520.4 520.4 520.4 520.4 520.4 520.3 520.3 520.3 520.3 520.3 520.2 520.2 520.2 520.2 + 520.2 520.1 520.1 520.1 ][ 159.1 159.1 159.1 159.1 159.0 159.0 159.0 159.0 158.9 158.9 158.9 158.9 158.9 158.8 + 158.8 158.8 158.8 158.8 158.8 158.7 158.7 158.7 158.7 158.6 158.6 158.6 158.6 158.6 158.5 158.5 158.5 158.5 + 158.5 158.4 158.4 158.4 158.4 158.4 158.3 158.3 ]plong + s[ 521.0 521.0 520.9 520.9 520.9 520.9 520.9 520.8 520.8 ][ 159.3 159.3 159.2 159.2 159.2 159.2 159.2 159.1 159.1 +]plong + 266.1 160.3 266.1 160.3 2 pls + s[ 266.1 266.1 266.1 266.1 266.1 266.2 266.2 266.2 266.2 266.2 266.3 266.3 266.3 266.3 266.3 266.3 266.4 266.4 + 266.4 266.4 266.4 266.5 266.5 266.5 266.5 266.5 266.5 266.6 266.6 266.6 266.6 266.6 266.6 266.7 266.7 266.7 + 266.7 266.7 266.8 266.8 266.8 266.8 266.8 266.9 266.9 266.9 ][ 160.3 160.2 160.2 160.2 160.2 160.1 160.1 160.1 + 160.1 160.1 160.0 160.0 160.0 160.0 160.0 159.9 159.9 159.9 159.9 159.8 159.8 159.8 159.8 159.8 159.8 159.7 + 159.7 159.7 159.7 159.7 159.6 159.6 159.6 159.6 159.5 159.5 159.5 159.5 159.5 159.4 159.4 159.4 159.4 159.4 + 159.3 159.3 ]plong + s[ 521.8 521.8 521.8 521.7 521.7 521.7 521.7 521.7 521.7 521.7 521.6 521.6 521.6 521.6 521.6 521.5 521.5 521.5 + 521.5 521.5 521.5 521.4 521.4 521.4 521.4 521.4 521.3 521.3 521.3 521.3 521.3 521.2 521.2 521.2 521.2 521.2 + 521.2 521.1 521.1 521.1 521.1 521.1 521.0 521.0 521.0 521.0 521.0 ][ 160.3 160.2 160.2 160.2 160.2 160.1 160.1 + 160.1 160.1 160.1 160.0 160.0 160.0 160.0 160.0 159.9 159.9 159.9 159.9 159.8 159.8 159.8 159.8 159.8 159.8 + 159.7 159.7 159.7 159.7 159.7 159.6 159.6 159.6 159.6 159.5 159.5 159.5 159.5 159.5 159.4 159.4 159.4 159.4 + 159.4 159.3 159.3 159.3 ]plong + 521.8 160.3 521.8 160.3 2 pls + s[ 265.2 265.2 265.2 265.3 265.3 265.3 265.3 265.3 265.4 265.4 265.4 265.4 265.4 265.4 265.5 265.5 265.5 265.5 + 265.5 265.5 265.6 265.6 265.6 265.6 265.6 265.6 265.7 265.7 265.7 265.7 265.7 265.8 265.8 265.8 265.8 265.8 + 265.9 265.9 265.9 265.9 265.9 265.9 266.0 266.0 266.0 266.0 266.0 266.1 ][ 161.3 161.2 161.2 161.2 161.2 161.2 + 161.1 161.1 161.1 161.1 161.1 161.0 161.0 161.0 161.0 160.9 160.9 160.9 160.9 160.9 160.8 160.8 160.8 160.8 + 160.8 160.7 160.7 160.7 160.7 160.7 160.6 160.6 160.6 160.6 160.6 160.5 160.5 160.5 160.5 160.4 160.4 160.4 + 160.4 160.4 160.3 160.3 160.3 160.3 ]plong + s[ 521.8 521.8 521.9 521.9 521.9 521.9 521.9 522.0 522.0 522.0 522.0 522.0 522.0 522.1 522.1 522.1 522.1 522.1 + 522.2 522.2 522.2 522.2 522.2 522.3 522.3 522.3 522.3 522.3 522.3 522.4 522.4 522.4 522.4 522.4 522.5 522.5 + 522.5 522.5 522.5 522.5 522.6 522.6 522.6 522.6 522.6 522.7 522.7 522.7 ][ 160.3 160.3 160.3 160.3 160.4 160.4 + 160.4 160.4 160.4 160.5 160.5 160.5 160.5 160.6 160.6 160.6 160.6 160.6 160.7 160.7 160.7 160.7 160.7 160.8 + 160.8 160.8 160.8 160.8 160.9 160.9 160.9 160.9 160.9 161.0 161.0 161.0 161.0 161.1 161.1 161.1 161.1 161.1 + 161.2 161.2 161.2 161.2 161.2 161.3 ]plong + s[ 264.4 264.4 264.4 264.4 264.5 264.5 264.5 264.5 264.5 264.5 264.5 264.6 264.6 264.6 264.6 264.6 264.7 264.7 + 264.7 264.7 264.7 264.7 264.8 264.8 264.8 264.8 264.8 264.9 264.9 264.9 264.9 264.9 265.0 265.0 265.0 265.0 + 265.0 265.0 265.1 265.1 265.1 ][ 162.2 162.2 162.2 162.2 162.2 162.1 162.1 162.1 162.1 162.0 162.0 162.0 162.0 + 162.0 161.9 161.9 161.9 161.9 161.9 161.8 161.8 161.8 161.8 161.7 161.7 161.7 161.7 161.7 161.7 161.6 161.6 + 161.6 161.6 161.5 161.5 161.5 161.5 161.5 161.4 161.4 161.4 ]plong + s[ 265.1 265.1 265.1 265.2 265.2 265.2 265.2 ][ 161.4 161.4 161.4 161.3 161.3 161.3 161.3 ]plong + s[ 522.8 522.8 522.7 522.7 522.7 522.7 522.7 ][ 161.4 161.4 161.4 161.3 161.3 161.3 161.3 ]plong + s[ 523.5 523.5 523.5 523.5 523.4 523.4 523.4 523.4 523.4 523.3 523.3 523.3 523.3 523.3 523.3 523.2 523.2 523.2 + 523.2 523.2 523.1 523.1 523.1 523.1 523.1 523.1 523.0 523.0 523.0 523.0 523.0 522.9 522.9 522.9 522.9 522.9 + 522.9 522.8 522.8 522.8 522.8 ][ 162.2 162.2 162.2 162.2 162.2 162.1 162.1 162.1 162.1 162.1 162.0 162.0 162.0 + 162.0 161.9 161.9 161.9 161.9 161.9 161.8 161.8 161.8 161.8 161.7 161.7 161.7 161.7 161.7 161.7 161.6 161.6 + 161.6 161.6 161.5 161.5 161.5 161.5 161.5 161.4 161.4 161.4 ]plong + s[ 263.6 263.6 263.6 263.6 263.6 263.7 263.7 263.7 263.7 263.7 263.7 263.8 263.8 263.8 263.8 263.8 263.9 263.9 + 263.9 263.9 263.9 263.9 264.0 264.0 264.0 264.0 264.0 264.1 264.1 264.1 264.1 ][ 163.2 163.2 163.2 163.1 163.1 + 163.1 163.1 163.1 163.0 163.0 163.0 163.0 162.9 162.9 162.9 162.9 162.9 162.8 162.8 162.8 162.8 162.7 162.7 + 162.7 162.7 162.7 162.7 162.6 162.6 162.6 162.6 ]plong + s[ 264.1 264.1 264.1 264.2 264.2 264.2 264.2 264.2 264.3 264.3 264.3 264.3 264.3 264.3 264.4 264.4 ][ 162.6 162.6 + 162.5 162.5 162.5 162.5 162.4 162.4 162.4 162.4 162.4 162.3 162.3 162.3 162.3 162.2 ]plong + s[ 523.8 523.7 523.7 523.7 523.7 523.7 523.7 523.7 523.6 523.6 523.6 523.6 523.6 523.5 523.5 523.5 ][ 162.6 162.6 + 162.5 162.5 162.5 162.5 162.4 162.4 162.4 162.4 162.4 162.3 162.3 162.3 162.3 162.2 ]plong + s[ 524.3 524.3 524.3 524.3 524.2 524.2 524.2 524.2 524.2 524.1 524.1 524.1 524.1 524.1 524.1 524.0 524.0 524.0 + 524.0 524.0 523.9 523.9 523.9 523.9 523.9 523.9 523.8 523.8 523.8 523.8 523.8 ][ 163.2 163.2 163.2 163.1 163.1 + 163.1 163.1 163.1 163.0 163.0 163.0 163.0 162.9 162.9 162.9 162.9 162.9 162.8 162.8 162.8 162.8 162.8 162.7 + 162.7 162.7 162.7 162.7 162.6 162.6 162.6 162.6 ]plong + s[ 262.8 262.8 262.8 262.8 262.8 262.9 262.9 262.9 262.9 262.9 262.9 263.0 263.0 263.0 263.0 263.0 263.0 263.1 + 263.1 263.1 263.1 263.1 ][ 164.2 164.2 164.2 164.1 164.1 164.1 164.1 164.0 164.0 164.0 164.0 164.0 163.9 163.9 + 163.9 163.9 163.8 163.8 163.8 163.8 163.8 163.7 ]plong + s[ 263.1 263.2 263.2 263.2 263.2 263.2 263.2 263.3 263.3 263.3 263.3 263.3 263.4 263.4 263.4 263.4 263.4 263.4 + 263.5 263.5 263.5 263.5 263.5 263.6 263.6 263.6 ][ 163.7 163.7 163.7 163.7 163.7 163.6 163.6 163.6 163.6 163.6 + 163.5 163.5 163.5 163.5 163.5 163.4 163.4 163.4 163.4 163.3 163.3 163.3 163.3 163.3 163.2 163.2 ]plong + s[ 524.7 524.7 524.7 524.7 524.7 524.6 524.6 524.6 524.6 524.6 524.6 524.6 524.5 524.5 524.5 524.5 524.5 524.5 + 524.4 524.4 524.4 524.4 524.4 524.3 524.3 524.3 ][ 163.7 163.7 163.7 163.7 163.7 163.7 163.6 163.6 163.6 163.6 + 163.5 163.5 163.5 163.5 163.5 163.4 163.4 163.4 163.4 163.3 163.3 163.3 163.3 163.3 163.2 163.2 ]plong + s[ 525.1 525.1 525.1 525.1 525.0 525.0 525.0 525.0 525.0 525.0 524.9 524.9 524.9 524.9 524.9 524.8 524.8 524.8 + 524.8 524.8 524.8 524.7 ][ 164.2 164.2 164.2 164.1 164.1 164.1 164.1 164.0 164.0 164.0 164.0 164.0 163.9 163.9 + 163.9 163.9 163.9 163.8 163.8 163.8 163.8 163.7 ]plong + s[ 262.0 262.0 262.0 262.0 262.1 262.1 262.1 262.1 262.1 262.1 262.2 ][ 165.2 165.2 165.1 165.1 165.1 165.1 165.0 + 165.0 165.0 165.0 165.0 ]plong + s[ 262.2 262.2 262.2 262.2 262.2 262.2 262.3 262.3 262.3 262.3 262.3 262.4 262.4 262.4 262.4 262.4 262.4 262.5 + 262.5 262.5 262.5 262.5 262.5 262.6 262.6 262.6 262.6 262.6 262.6 262.7 262.7 262.7 262.7 262.7 262.7 262.8 ][ + 165.0 164.9 164.9 164.9 164.9 164.8 164.8 164.8 164.8 164.8 164.7 164.7 164.7 164.7 164.7 164.6 164.6 164.6 + 164.6 164.6 164.5 164.5 164.5 164.5 164.4 164.4 164.4 164.4 164.4 164.3 164.3 164.3 164.3 164.2 164.2 164.2 ]plong + s[ 525.7 525.7 525.7 525.7 525.6 525.6 525.6 525.6 525.6 525.6 525.6 525.5 525.5 525.5 525.5 525.5 525.5 525.4 + 525.4 525.4 525.4 525.4 525.3 525.3 525.3 525.3 525.3 525.3 525.2 525.2 525.2 525.2 525.2 525.2 525.1 525.1 ][ + 165.0 164.9 164.9 164.9 164.9 164.9 164.8 164.8 164.8 164.8 164.7 164.7 164.7 164.7 164.7 164.6 164.6 164.6 + 164.6 164.6 164.5 164.5 164.5 164.5 164.4 164.4 164.4 164.4 164.4 164.3 164.3 164.3 164.3 164.2 164.2 164.2 ]plong + s[ 525.9 525.9 525.9 525.8 525.8 525.8 525.8 525.8 525.8 525.7 525.7 ][ 165.2 165.2 165.1 165.1 165.1 165.1 165.1 + 165.0 165.0 165.0 165.0 ]plong + s[ 261.2 261.2 261.2 261.3 261.3 261.3 261.3 261.3 261.3 261.4 261.4 261.4 261.4 261.4 261.4 261.5 261.5 261.5 + 261.5 261.5 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.7 261.7 261.7 261.7 261.7 261.7 261.8 261.8 261.8 + 261.8 261.8 261.9 261.9 261.9 261.9 261.9 261.9 262.0 262.0 ][ 166.2 166.1 166.1 166.1 166.1 166.1 166.0 166.0 + 166.0 166.0 165.9 165.9 165.9 165.9 165.8 165.8 165.8 165.8 165.8 165.7 165.7 165.7 165.7 165.6 165.6 165.6 + 165.6 165.6 165.6 165.5 165.5 165.5 165.5 165.4 165.4 165.4 165.4 165.4 165.3 165.3 165.3 165.3 165.2 165.2 + 165.2 165.2 ]plong + s[ 526.7 526.7 526.6 526.6 526.6 526.6 526.6 526.6 526.6 526.5 526.5 526.5 526.5 526.5 526.4 526.4 526.4 526.4 + 526.4 526.4 526.3 526.3 526.3 526.3 526.3 526.3 526.2 526.2 526.2 526.2 526.2 526.1 526.1 526.1 526.1 526.1 + 526.1 526.0 526.0 526.0 526.0 526.0 525.9 525.9 525.9 525.9 ][ 166.2 166.1 166.1 166.1 166.1 166.1 166.0 166.0 + 166.0 166.0 165.9 165.9 165.9 165.9 165.9 165.8 165.8 165.8 165.8 165.7 165.7 165.7 165.7 165.7 165.6 165.6 + 165.6 165.6 165.6 165.5 165.5 165.5 165.5 165.5 165.4 165.4 165.4 165.4 165.3 165.3 165.3 165.3 165.3 165.2 + 165.2 165.2 ]plong + s[ 260.5 260.5 260.5 260.5 260.5 260.5 260.6 260.6 260.6 260.6 260.6 260.6 260.7 260.7 260.7 260.7 260.7 260.7 + 260.7 260.8 260.8 260.8 260.8 260.8 260.9 260.9 260.9 260.9 260.9 260.9 261.0 261.0 261.0 261.0 261.0 261.0 + 261.1 261.1 261.1 261.1 261.1 261.2 261.2 261.2 ][ 167.1 167.1 167.1 167.1 167.0 167.0 167.0 167.0 167.0 166.9 + 166.9 166.9 166.9 166.8 166.8 166.8 166.8 166.7 166.7 166.7 166.7 166.7 166.6 166.6 166.6 166.6 166.6 166.5 + 166.5 166.5 166.5 166.5 166.4 166.4 166.4 166.4 166.3 166.3 166.3 166.3 166.3 166.2 166.2 166.2 ]plong + 261.2 166.2 261.2 166.2 2 pls + 526.7 166.2 526.7 166.2 526.7 166.2 3 pls + s[ 527.4 527.4 527.4 527.4 527.4 527.4 527.3 527.3 527.3 527.3 527.3 527.3 527.2 527.2 527.2 527.2 527.2 527.1 + 527.1 527.1 527.1 527.1 527.1 527.0 527.0 527.0 527.0 527.0 527.0 526.9 526.9 526.9 526.9 526.9 526.8 526.8 + 526.8 526.8 526.8 526.8 526.7 526.7 526.7 ][ 167.1 167.1 167.1 167.1 167.0 167.0 167.0 167.0 167.0 166.9 166.9 + 166.9 166.9 166.8 166.8 166.8 166.8 166.8 166.7 166.7 166.7 166.7 166.6 166.6 166.6 166.6 166.6 166.5 166.5 + 166.5 166.5 166.5 166.4 166.4 166.4 166.4 166.3 166.3 166.3 166.3 166.3 166.2 166.2 ]plong + s[ 259.7 259.7 259.7 259.7 259.7 259.8 259.8 259.8 259.8 259.8 259.8 259.9 259.9 259.9 259.9 259.9 259.9 260.0 + 260.0 260.0 260.0 260.0 260.1 260.1 260.1 260.1 260.1 260.1 260.2 260.2 260.2 260.2 ][ 168.1 168.1 168.1 168.1 + 168.0 168.0 168.0 168.0 168.0 167.9 167.9 167.9 167.9 167.8 167.8 167.8 167.8 167.7 167.7 167.7 167.7 167.7 + 167.6 167.6 167.6 167.6 167.6 167.5 167.5 167.5 167.5 167.4 ]plong + s[ 260.2 260.2 260.2 260.3 260.3 260.3 260.3 260.3 260.3 260.4 260.4 260.4 260.4 260.4 260.5 ][ 167.4 167.4 167.4 + 167.4 167.4 167.3 167.3 167.3 167.3 167.2 167.2 167.2 167.2 167.2 167.1 ]plong + s[ 527.7 527.7 527.7 527.6 527.6 527.6 527.6 527.6 527.5 527.5 527.5 527.5 527.5 527.5 527.5 527.4 ][ 167.5 167.5 + 167.4 167.4 167.4 167.4 167.3 167.3 167.3 167.3 167.2 167.2 167.2 167.2 167.2 167.1 ]plong + s[ 528.2 528.2 528.2 528.1 528.1 528.1 528.1 528.1 528.1 528.0 528.0 528.0 528.0 528.0 527.9 527.9 527.9 527.9 + 527.9 527.9 527.8 527.8 527.8 527.8 527.8 527.8 527.7 527.7 527.7 527.7 ][ 168.1 168.1 168.1 168.0 168.0 168.0 + 168.0 168.0 167.9 167.9 167.9 167.9 167.8 167.8 167.8 167.8 167.7 167.7 167.7 167.7 167.7 167.6 167.6 167.6 + 167.6 167.6 167.5 167.5 167.5 167.5 ]plong + s[ 259.0 259.0 259.0 259.0 259.0 259.0 259.1 259.1 259.1 259.1 259.1 259.1 259.2 259.2 259.2 259.2 259.2 ][ 169.1 + 169.1 169.0 169.0 169.0 169.0 169.0 168.9 168.9 168.9 168.9 168.8 168.8 168.8 168.8 168.7 168.7 ]plong + s[ 259.2 259.2 259.3 259.3 259.3 259.3 259.3 259.3 259.4 259.4 259.4 259.4 259.4 259.4 259.5 259.5 259.5 259.5 + 259.5 259.6 259.6 259.6 259.6 259.6 259.6 259.7 259.7 259.7 ][ 168.7 168.7 168.7 168.7 168.6 168.6 168.6 168.6 + 168.5 168.5 168.5 168.5 168.5 168.4 168.4 168.4 168.4 168.4 168.3 168.3 168.3 168.3 168.2 168.2 168.2 168.2 + 168.2 168.1 ]plong + s[ 528.7 528.6 528.6 528.6 528.6 528.6 528.6 528.5 528.5 528.5 528.5 528.5 528.5 528.4 528.4 528.4 528.4 528.4 + 528.4 528.3 528.3 528.3 528.3 528.3 528.3 528.2 528.2 528.2 528.2 ][ 168.7 168.7 168.7 168.7 168.6 168.6 168.6 + 168.6 168.5 168.5 168.5 168.5 168.5 168.5 168.4 168.4 168.4 168.4 168.3 168.3 168.3 168.3 168.2 168.2 168.2 + 168.2 168.2 168.1 168.1 ]plong + s[ 528.9 528.9 528.9 528.9 528.9 528.8 528.8 528.8 528.8 528.8 528.8 528.7 528.7 528.7 528.7 528.7 528.7 ][ 169.1 + 169.1 169.0 169.0 169.0 169.0 169.0 168.9 168.9 168.9 168.9 168.8 168.8 168.8 168.8 168.7 168.7 ]plong + 258.2 170.1 258.2 170.1 2 pls + s[ 258.2 258.3 258.3 258.3 258.3 258.3 258.3 258.4 258.4 258.4 258.4 258.4 258.4 258.5 258.5 258.5 258.5 258.5 + 258.5 258.6 258.6 258.6 258.6 258.6 258.6 258.7 258.7 258.7 258.7 258.7 258.7 258.7 258.8 258.8 258.8 258.8 + 258.8 258.8 258.9 258.9 258.9 258.9 258.9 259.0 ][ 170.1 170.0 170.0 170.0 170.0 169.9 169.9 169.9 169.9 169.8 + 169.8 169.8 169.8 169.8 169.7 169.7 169.7 169.7 169.6 169.6 169.6 169.6 169.6 169.5 169.5 169.5 169.5 169.5 + 169.4 169.4 169.4 169.4 169.3 169.3 169.3 169.3 169.3 169.2 169.2 169.2 169.2 169.1 169.1 169.1 ]plong + s[ 529.6 529.6 529.6 529.6 529.6 529.6 529.5 529.5 529.5 529.5 529.5 529.5 529.5 529.4 529.4 529.4 529.4 529.4 + 529.4 529.3 529.3 529.3 529.3 529.3 529.3 529.2 529.2 529.2 529.2 529.2 529.2 529.1 529.1 529.1 529.1 529.1 + 529.0 529.0 529.0 529.0 529.0 529.0 528.9 528.9 ][ 170.1 170.0 170.0 170.0 170.0 169.9 169.9 169.9 169.9 169.8 + 169.8 169.8 169.8 169.8 169.7 169.7 169.7 169.7 169.6 169.6 169.6 169.6 169.6 169.5 169.5 169.5 169.5 169.5 + 169.4 169.4 169.4 169.4 169.3 169.3 169.3 169.3 169.3 169.2 169.2 169.2 169.2 169.1 169.1 169.1 ]plong + 529.6 170.1 529.7 170.1 2 pls + s[ 257.5 257.5 257.5 257.6 257.6 257.6 257.6 257.6 257.6 257.7 257.7 257.7 257.7 257.7 257.7 257.7 257.8 257.8 + 257.8 257.8 257.8 257.8 257.9 257.9 257.9 257.9 257.9 257.9 258.0 258.0 258.0 258.0 258.0 258.0 258.1 258.1 + 258.1 258.1 258.1 258.1 258.2 258.2 258.2 258.2 ][ 171.1 171.0 171.0 171.0 171.0 170.9 170.9 170.9 170.9 170.8 + 170.8 170.8 170.8 170.7 170.7 170.7 170.7 170.7 170.6 170.6 170.6 170.6 170.5 170.5 170.5 170.5 170.5 170.4 + 170.4 170.4 170.4 170.4 170.3 170.3 170.3 170.3 170.2 170.2 170.2 170.2 170.1 170.1 170.1 170.1 ]plong + s[ 529.7 529.7 529.7 529.7 529.7 529.7 529.8 529.8 529.8 529.8 529.8 529.8 529.9 529.9 529.9 529.9 529.9 529.9 + 530.0 530.0 530.0 530.0 530.0 530.1 530.1 530.1 530.1 530.1 530.1 530.1 530.2 530.2 530.2 530.2 530.2 530.3 + 530.3 530.3 530.3 530.3 530.3 530.4 530.4 530.4 ][ 170.1 170.1 170.1 170.2 170.2 170.2 170.2 170.2 170.3 170.3 + 170.3 170.3 170.4 170.4 170.4 170.4 170.4 170.5 170.5 170.5 170.5 170.5 170.6 170.6 170.6 170.6 170.7 170.7 + 170.7 170.7 170.8 170.8 170.8 170.8 170.8 170.9 170.9 170.9 170.9 171.0 171.0 171.0 171.0 171.1 ]plong + s[ 256.8 256.8 256.8 256.8 256.8 256.9 256.9 256.9 256.9 256.9 256.9 257.0 257.0 257.0 257.0 257.0 257.0 257.1 + 257.1 257.1 257.1 257.1 257.1 257.2 257.2 257.2 257.2 257.2 257.2 257.3 ][ 172.0 172.0 172.0 172.0 172.0 171.9 + 171.9 171.9 171.9 171.8 171.8 171.8 171.8 171.7 171.7 171.7 171.7 171.7 171.6 171.6 171.6 171.6 171.5 171.5 + 171.5 171.5 171.4 171.4 171.4 171.4 ]plong + s[ 257.3 257.3 257.3 257.3 257.3 257.3 257.4 257.4 257.4 257.4 257.4 257.4 257.5 257.5 257.5 257.5 ][ 171.4 171.4 + 171.3 171.3 171.3 171.3 171.3 171.2 171.2 171.2 171.2 171.1 171.1 171.1 171.1 171.1 ]plong + s[ 530.6 530.6 530.6 530.6 530.6 530.5 530.5 530.5 530.5 530.5 530.5 530.4 530.4 530.4 530.4 530.4 ][ 171.4 171.4 + 171.4 171.3 171.3 171.3 171.3 171.2 171.2 171.2 171.2 171.1 171.1 171.1 171.1 171.1 ]plong + s[ 531.1 531.1 531.1 531.0 531.0 531.0 531.0 531.0 531.0 530.9 530.9 530.9 530.9 530.9 530.9 530.8 530.8 530.8 + 530.8 530.8 530.8 530.7 530.7 530.7 530.7 530.7 530.7 530.6 530.6 ][ 172.0 172.0 172.0 172.0 171.9 171.9 171.9 + 171.9 171.8 171.8 171.8 171.8 171.8 171.7 171.7 171.7 171.7 171.6 171.6 171.6 171.6 171.5 171.5 171.5 171.5 + 171.4 171.4 171.4 171.4 ]plong + s[ 256.1 256.1 256.1 256.1 256.2 256.2 256.2 256.2 256.2 256.2 256.3 256.3 ][ 173.0 173.0 173.0 172.9 172.9 172.9 + 172.9 172.8 172.8 172.8 172.8 172.8 ]plong + s[ 256.3 256.3 256.3 256.3 256.3 256.4 256.4 256.4 256.4 256.4 256.4 256.5 256.5 256.5 256.5 256.5 256.5 256.6 + 256.6 256.6 256.6 256.6 256.6 256.7 256.7 256.7 256.7 256.7 256.7 256.8 256.8 256.8 ][ 172.8 172.7 172.7 172.7 + 172.7 172.6 172.6 172.6 172.6 172.5 172.5 172.5 172.5 172.5 172.4 172.4 172.4 172.4 172.4 172.3 172.3 172.3 + 172.3 172.2 172.2 172.2 172.2 172.1 172.1 172.1 172.1 172.0 ]plong + s[ 531.6 531.6 531.6 531.6 531.5 531.5 531.5 531.5 531.5 531.5 531.4 531.4 531.4 531.4 531.4 531.4 531.4 531.3 + 531.3 531.3 531.3 531.3 531.3 531.2 531.2 531.2 531.2 531.2 531.2 531.1 531.1 531.1 531.1 ][ 172.8 172.7 172.7 + 172.7 172.7 172.6 172.6 172.6 172.6 172.5 172.5 172.5 172.5 172.5 172.4 172.4 172.4 172.4 172.4 172.3 172.3 + 172.3 172.3 172.2 172.2 172.2 172.2 172.1 172.1 172.1 172.1 172.1 172.0 ]plong + s[ 531.8 531.8 531.8 531.7 531.7 531.7 531.7 531.7 531.7 531.6 531.6 531.6 ][ 173.0 173.0 173.0 172.9 172.9 172.9 + 172.9 172.9 172.8 172.8 172.8 172.8 ]plong + s[ 255.4 255.4 255.4 255.4 255.5 255.5 255.5 255.5 255.5 255.6 255.6 255.6 255.6 255.6 255.6 255.6 255.7 255.7 + 255.7 255.7 255.7 255.7 255.8 255.8 255.8 255.8 255.8 255.8 255.8 255.9 255.9 255.9 255.9 255.9 255.9 256.0 + 256.0 256.0 256.0 256.0 256.0 256.1 256.1 256.1 ][ 174.0 174.0 174.0 173.9 173.9 173.9 173.9 173.8 173.8 173.8 + 173.8 173.7 173.7 173.7 173.7 173.6 173.6 173.6 173.6 173.6 173.5 173.5 173.5 173.5 173.4 173.4 173.4 173.4 + 173.4 173.3 173.3 173.3 173.3 173.2 173.2 173.2 173.2 173.2 173.1 173.1 173.1 173.1 173.0 173.0 ]plong + s[ 532.5 532.5 532.4 532.4 532.4 532.4 532.4 532.4 532.4 532.3 532.3 532.3 532.3 532.3 532.3 532.2 532.2 532.2 + 532.2 532.2 532.2 532.1 532.1 532.1 532.1 532.1 532.1 532.1 532.0 532.0 532.0 532.0 532.0 531.9 531.9 531.9 + 531.9 531.9 531.9 531.9 531.8 531.8 531.8 531.8 ][ 174.0 174.0 174.0 173.9 173.9 173.9 173.9 173.8 173.8 173.8 + 173.8 173.7 173.7 173.7 173.7 173.7 173.6 173.6 173.6 173.6 173.5 173.5 173.5 173.5 173.4 173.4 173.4 173.4 + 173.4 173.3 173.3 173.3 173.3 173.3 173.2 173.2 173.2 173.2 173.1 173.1 173.1 173.1 173.0 173.0 ]plong + s[ 254.7 254.7 254.8 254.8 254.8 254.8 254.8 254.8 254.8 254.9 254.9 254.9 254.9 254.9 254.9 255.0 255.0 255.0 + 255.0 255.0 255.0 255.1 255.1 255.1 255.1 255.1 255.1 255.2 255.2 255.2 255.2 255.2 255.2 255.3 255.3 255.3 + 255.3 ][ 175.0 174.9 174.9 174.9 174.9 174.9 174.8 174.8 174.8 174.8 174.7 174.7 174.7 174.7 174.6 174.6 174.6 + 174.6 174.5 174.5 174.5 174.5 174.5 174.4 174.4 174.4 174.4 174.4 174.3 174.3 174.3 174.3 174.2 174.2 174.2 + 174.2 174.1 ]plong + s[ 255.3 255.3 255.3 255.4 255.4 255.4 255.4 ][ 174.1 174.1 174.1 174.1 174.0 174.0 174.0 ]plong + s[ 532.6 532.6 532.5 532.5 532.5 532.5 532.5 ][ 174.1 174.1 174.1 174.1 174.1 174.0 174.0 ]plong + s[ 533.2 533.1 533.1 533.1 533.1 533.1 533.1 533.0 533.0 533.0 533.0 533.0 533.0 533.0 532.9 532.9 532.9 532.9 + 532.9 532.8 532.8 532.8 532.8 532.8 532.8 532.8 532.7 532.7 532.7 532.7 532.7 532.7 532.6 532.6 532.6 532.6 + 532.6 ][ 175.0 175.0 174.9 174.9 174.9 174.9 174.8 174.8 174.8 174.8 174.7 174.7 174.7 174.7 174.6 174.6 174.6 + 174.6 174.6 174.5 174.5 174.5 174.5 174.4 174.4 174.4 174.4 174.4 174.3 174.3 174.3 174.3 174.2 174.2 174.2 + 174.2 174.1 ]plong + s[ 254.1 254.1 254.1 254.1 254.1 254.1 254.1 254.2 254.2 254.2 254.2 254.2 254.3 254.3 254.3 254.3 254.3 ][ 176.0 + 175.9 175.9 175.9 175.9 175.8 175.8 175.8 175.8 175.7 175.7 175.7 175.7 175.6 175.6 175.6 175.6 ]plong + s[ 254.3 254.3 254.3 254.4 254.4 254.4 254.4 254.4 254.4 254.5 254.5 254.5 254.5 254.5 254.5 254.6 254.6 254.6 + 254.6 254.6 254.6 254.7 254.7 254.7 254.7 254.7 254.7 ][ 175.6 175.6 175.5 175.5 175.5 175.5 175.4 175.4 175.4 + 175.4 175.3 175.3 175.3 175.3 175.3 175.2 175.2 175.2 175.2 175.1 175.1 175.1 175.1 175.0 175.0 175.0 175.0 ]plong + s[ 533.6 533.5 533.5 533.5 533.5 533.5 533.5 533.5 533.4 533.4 533.4 533.4 533.4 533.4 533.4 533.3 533.3 533.3 + 533.3 533.3 533.3 533.2 533.2 533.2 533.2 533.2 533.2 ][ 175.6 175.6 175.5 175.5 175.5 175.5 175.4 175.4 175.4 + 175.4 175.3 175.3 175.3 175.3 175.3 175.2 175.2 175.2 175.2 175.1 175.1 175.1 175.1 175.1 175.0 175.0 175.0 ]plong + s[ 533.8 533.8 533.8 533.8 533.8 533.7 533.7 533.7 533.7 533.7 533.7 533.6 533.6 533.6 533.6 533.6 533.6 ][ 176.0 + 175.9 175.9 175.9 175.9 175.8 175.8 175.8 175.8 175.7 175.7 175.7 175.7 175.6 175.6 175.6 175.6 ]plong + s[ 253.4 253.4 253.4 253.4 253.5 253.5 253.5 253.5 253.5 253.5 253.6 253.6 253.6 253.6 253.6 253.6 253.7 253.7 + 253.7 253.7 253.7 253.7 253.8 253.8 253.8 253.8 253.8 253.8 253.8 253.9 253.9 253.9 253.9 253.9 253.9 253.9 + 254.0 254.0 254.0 254.0 254.0 254.0 254.1 ][ 176.9 176.9 176.9 176.9 176.8 176.8 176.8 176.8 176.7 176.7 176.7 + 176.7 176.7 176.6 176.6 176.6 176.6 176.5 176.5 176.5 176.5 176.4 176.4 176.4 176.4 176.3 176.3 176.3 176.3 + 176.3 176.2 176.2 176.2 176.2 176.1 176.1 176.1 176.1 176.0 176.0 176.0 176.0 176.0 ]plong + s[ 534.5 534.5 534.4 534.4 534.4 534.4 534.4 534.4 534.4 534.3 534.3 534.3 534.3 534.3 534.3 534.3 534.2 534.2 + 534.2 534.2 534.2 534.2 534.1 534.1 534.1 534.1 534.1 534.1 534.0 534.0 534.0 534.0 534.0 534.0 533.9 533.9 + 533.9 533.9 533.9 533.9 533.9 533.8 533.8 ][ 176.9 176.9 176.9 176.9 176.8 176.8 176.8 176.8 176.7 176.7 176.7 + 176.7 176.7 176.6 176.6 176.6 176.6 176.5 176.5 176.5 176.5 176.4 176.4 176.4 176.4 176.3 176.3 176.3 176.3 + 176.3 176.2 176.2 176.2 176.2 176.2 176.1 176.1 176.1 176.1 176.0 176.0 176.0 176.0 ]plong + s[ 252.7 252.8 252.8 252.8 252.8 252.8 252.8 252.9 252.9 252.9 252.9 252.9 252.9 252.9 253.0 253.0 253.0 253.0 + 253.0 253.0 253.0 253.1 253.1 253.1 253.1 253.1 253.1 253.2 253.2 253.2 253.2 253.2 253.2 253.3 253.3 253.3 + 253.3 253.3 253.3 ][ 177.9 177.9 177.9 177.9 177.8 177.8 177.8 177.8 177.7 177.7 177.7 177.7 177.6 177.6 177.6 + 177.6 177.5 177.5 177.5 177.5 177.4 177.4 177.4 177.4 177.4 177.3 177.3 177.3 177.3 177.3 177.2 177.2 177.2 + 177.2 177.1 177.1 177.1 177.1 177.0 ]plong + 253.4 176.9 253.4 177.0 253.4 177.0 253.4 177.0 253.3 177.0 5 pls + 534.5 176.9 534.5 177.0 534.5 177.0 534.5 177.0 534.5 177.0 5 pls + s[ 535.1 535.1 535.1 535.1 535.1 535.0 535.0 535.0 535.0 535.0 535.0 535.0 534.9 534.9 534.9 534.9 534.9 534.9 + 534.8 534.8 534.8 534.8 534.8 534.8 534.7 534.7 534.7 534.7 534.7 534.7 534.7 534.6 534.6 534.6 534.6 534.6 + 534.6 534.5 ][ 177.9 177.9 177.9 177.8 177.8 177.8 177.8 177.7 177.7 177.7 177.7 177.6 177.6 177.6 177.6 177.6 + 177.5 177.5 177.5 177.5 177.4 177.4 177.4 177.4 177.3 177.3 177.3 177.3 177.3 177.2 177.2 177.2 177.2 177.1 + 177.1 177.1 177.1 177.0 ]plong + s[ 252.1 252.1 252.1 252.2 252.2 252.2 252.2 252.2 252.2 252.2 252.3 252.3 252.3 252.3 252.3 252.3 252.4 ][ 178.9 + 178.9 178.9 178.8 178.8 178.8 178.8 178.7 178.7 178.7 178.7 178.6 178.6 178.6 178.6 178.5 178.5 ]plong + s[ 252.4 252.4 252.4 252.4 252.4 252.4 252.5 252.5 252.5 252.5 252.5 252.5 252.5 252.6 252.6 252.6 252.6 252.6 + 252.6 252.7 252.7 252.7 252.7 252.7 252.7 252.7 ][ 178.5 178.5 178.5 178.4 178.4 178.4 178.4 178.3 178.3 178.3 + 178.3 178.3 178.2 178.2 178.2 178.2 178.2 178.1 178.1 178.1 178.1 178.0 178.0 178.0 178.0 177.9 ]plong + s[ 535.5 535.5 535.5 535.5 535.5 535.4 535.4 535.4 535.4 535.4 535.4 535.3 535.3 535.3 535.3 535.3 535.3 535.3 + 535.3 535.2 535.2 535.2 535.2 535.2 535.2 535.1 535.1 ][ 178.5 178.5 178.5 178.4 178.4 178.4 178.4 178.3 178.3 + 178.3 178.3 178.3 178.2 178.2 178.2 178.2 178.2 178.1 178.1 178.1 178.1 178.0 178.0 178.0 178.0 177.9 177.9 ]plong + s[ 535.8 535.8 535.7 535.7 535.7 535.7 535.7 535.7 535.6 535.6 535.6 535.6 535.6 535.6 535.5 535.5 535.5 ][ 178.9 + 178.9 178.9 178.8 178.8 178.8 178.8 178.7 178.7 178.7 178.7 178.6 178.6 178.6 178.6 178.5 178.5 ]plong + s[ 251.5 251.5 251.5 251.5 251.5 251.6 251.6 251.6 251.6 251.6 251.6 251.7 251.7 251.7 251.7 251.7 251.7 251.8 + 251.8 251.8 251.8 251.8 251.8 251.8 251.9 251.9 251.9 251.9 251.9 251.9 251.9 251.9 252.0 252.0 252.0 252.0 + 252.0 252.0 252.1 252.1 252.1 252.1 ][ 179.9 179.9 179.8 179.8 179.8 179.8 179.7 179.7 179.7 179.7 179.6 179.6 + 179.6 179.6 179.5 179.5 179.5 179.5 179.4 179.4 179.4 179.4 179.4 179.3 179.3 179.3 179.2 179.2 179.2 179.2 + 179.2 179.1 179.1 179.1 179.1 179.1 179.0 179.0 179.0 179.0 178.9 178.9 ]plong + s[ 536.4 536.4 536.4 536.3 536.3 536.3 536.3 536.3 536.3 536.3 536.3 536.2 536.2 536.2 536.2 536.2 536.2 536.1 + 536.1 536.1 536.1 536.1 536.1 536.1 536.0 536.0 536.0 536.0 536.0 536.0 535.9 535.9 535.9 535.9 535.9 535.9 + 535.9 535.8 535.8 535.8 535.8 535.8 ][ 179.9 179.9 179.8 179.8 179.8 179.8 179.7 179.7 179.7 179.7 179.6 179.6 + 179.6 179.6 179.5 179.5 179.5 179.5 179.4 179.4 179.4 179.4 179.4 179.3 179.3 179.3 179.3 179.2 179.2 179.2 + 179.2 179.1 179.1 179.1 179.1 179.1 179.0 179.0 179.0 179.0 178.9 178.9 ]plong + s[ 250.9 250.9 250.9 250.9 250.9 250.9 251.0 251.0 251.0 251.0 251.0 251.0 251.0 251.0 251.1 251.1 251.1 251.1 + 251.1 251.1 251.2 251.2 251.2 251.2 251.2 251.2 251.2 251.3 251.3 251.3 251.3 251.3 251.3 251.4 251.4 ][ 180.9 + 180.8 180.8 180.8 180.8 180.7 180.7 180.7 180.7 180.7 180.6 180.6 180.6 180.5 180.5 180.5 180.5 180.5 180.4 + 180.4 180.4 180.4 180.3 180.3 180.3 180.3 180.2 180.2 180.2 180.2 180.2 180.1 180.1 180.1 180.1 ]plong + s[ 251.4 251.4 251.4 251.4 251.4 251.5 251.5 251.5 ][ 180.1 180.0 180.0 180.0 180.0 179.9 179.9 179.9 ]plong + s[ 536.5 536.5 536.5 536.5 536.4 536.4 536.4 536.4 ][ 180.1 180.0 180.0 180.0 180.0 179.9 179.9 179.9 ]plong + s[ 537.0 537.0 537.0 537.0 537.0 536.9 536.9 536.9 536.9 536.9 536.9 536.8 536.8 536.8 536.8 536.8 536.8 536.8 + 536.7 536.7 536.7 536.7 536.7 536.7 536.6 536.6 536.6 536.6 536.6 536.6 536.5 536.5 536.5 536.5 ][ 180.9 180.8 + 180.8 180.8 180.7 180.7 180.7 180.7 180.7 180.6 180.6 180.6 180.6 180.5 180.5 180.5 180.5 180.4 180.4 180.4 + 180.4 180.3 180.3 180.3 180.3 180.2 180.2 180.2 180.2 180.2 180.1 180.1 180.1 180.1 ]plong + s[ 250.3 250.3 250.3 250.3 250.3 250.3 250.3 250.4 250.4 250.4 ][ 181.8 181.8 181.8 181.8 181.7 181.7 181.7 181.7 + 181.6 181.6 ]plong + s[ 250.4 250.4 250.4 250.4 250.5 250.5 250.5 250.5 250.5 250.5 250.5 250.6 250.6 250.6 250.6 250.6 250.6 250.7 + 250.7 250.7 250.7 250.7 250.7 250.7 250.8 250.8 250.8 250.8 250.8 250.8 250.9 250.9 ][ 181.6 181.6 181.6 181.5 + 181.5 181.5 181.5 181.4 181.4 181.4 181.4 181.3 181.3 181.3 181.3 181.2 181.2 181.2 181.2 181.2 181.1 181.1 + 181.1 181.1 181.0 181.0 181.0 181.0 180.9 180.9 180.9 180.9 ]plong + s[ 537.5 537.5 537.5 537.4 537.4 537.4 537.4 537.4 537.4 537.3 537.3 537.3 537.3 537.3 537.3 537.3 537.2 537.2 + 537.2 537.2 537.2 537.2 537.2 537.2 537.1 537.1 537.1 537.1 537.1 537.1 537.0 537.0 537.0 ][ 181.6 181.6 181.6 + 181.5 181.5 181.5 181.5 181.4 181.4 181.4 181.4 181.3 181.3 181.3 181.3 181.2 181.2 181.2 181.2 181.2 181.1 + 181.1 181.1 181.1 181.0 181.0 181.0 181.0 180.9 180.9 180.9 180.9 180.9 ]plong + s[ 537.6 537.6 537.6 537.6 537.6 537.5 537.5 537.5 537.5 537.5 ][ 181.8 181.8 181.8 181.8 181.7 181.7 181.7 181.7 + 181.6 181.6 ]plong + s[ 249.7 249.7 249.7 249.7 249.7 249.7 249.8 249.8 249.8 249.8 249.8 249.8 249.8 249.9 249.9 249.9 249.9 249.9 + 249.9 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.0 250.1 250.1 250.1 250.1 250.1 250.1 250.1 250.2 250.2 + 250.2 250.2 250.2 250.2 250.3 ][ 182.8 182.8 182.8 182.7 182.7 182.7 182.7 182.6 182.6 182.6 182.6 182.5 182.5 + 182.5 182.5 182.4 182.4 182.4 182.4 182.3 182.3 182.3 182.3 182.2 182.2 182.2 182.2 182.1 182.1 182.1 182.1 + 182.1 182.0 182.0 182.0 182.0 181.9 181.9 181.9 181.9 181.8 ]plong + s[ 538.2 538.2 538.2 538.2 538.2 538.1 538.1 538.1 538.1 538.1 538.1 538.1 538.0 538.0 538.0 538.0 538.0 538.0 + 538.0 537.9 537.9 537.9 537.9 537.9 537.9 537.9 537.8 537.8 537.8 537.8 537.8 537.8 537.7 537.7 537.7 537.7 + 537.7 537.7 537.7 537.6 537.6 ][ 182.8 182.8 182.8 182.7 182.7 182.7 182.7 182.6 182.6 182.6 182.6 182.5 182.5 + 182.5 182.5 182.4 182.4 182.4 182.4 182.3 182.3 182.3 182.3 182.2 182.2 182.2 182.2 182.1 182.1 182.1 182.1 + 182.1 182.0 182.0 182.0 182.0 181.9 181.9 181.9 181.9 181.8 ]plong + s[ 249.0 249.1 249.1 249.1 249.1 249.1 249.1 249.2 249.2 249.2 249.2 249.2 249.2 249.2 249.3 249.3 249.3 249.3 + 249.3 249.3 249.4 249.4 249.4 249.4 249.4 249.4 ][ 183.8 183.8 183.8 183.7 183.7 183.7 183.7 183.6 183.6 183.6 + 183.6 183.5 183.5 183.5 183.5 183.4 183.4 183.4 183.4 183.3 183.3 183.3 183.3 183.2 183.2 183.2 ]plong + s[ 249.4 249.4 249.5 249.5 249.5 249.5 249.5 249.5 249.6 249.6 249.6 249.6 249.6 249.6 249.6 249.7 249.7 ][ 183.2 + 183.2 183.1 183.1 183.1 183.1 183.1 183.0 183.0 183.0 183.0 182.9 182.9 182.9 182.9 182.8 182.8 ]plong + s[ 538.5 538.4 538.4 538.4 538.4 538.4 538.4 538.4 538.3 538.3 538.3 538.3 538.3 538.3 538.3 538.2 538.2 538.2 ][ + 183.2 183.2 183.2 183.1 183.1 183.1 183.1 183.1 183.0 183.0 183.0 183.0 182.9 182.9 182.9 182.9 182.8 182.8 ]plong + s[ 538.8 538.8 538.8 538.8 538.8 538.7 538.7 538.7 538.7 538.7 538.7 538.6 538.6 538.6 538.6 538.6 538.6 538.6 + 538.5 538.5 538.5 538.5 538.5 538.5 ][ 183.8 183.8 183.7 183.7 183.7 183.7 183.6 183.6 183.6 183.6 183.5 183.5 + 183.5 183.5 183.4 183.4 183.4 183.4 183.3 183.3 183.3 183.3 183.2 183.2 ]plong + s[ 248.5 248.5 248.5 248.5 248.5 248.6 248.6 248.6 248.6 248.6 248.6 248.7 248.7 248.7 248.7 248.7 248.7 248.7 + 248.8 248.8 248.8 248.8 248.8 248.8 248.9 248.9 248.9 248.9 248.9 248.9 248.9 248.9 249.0 249.0 249.0 249.0 + 249.0 249.0 249.0 249.0 ][ 184.8 184.7 184.7 184.7 184.7 184.6 184.6 184.6 184.6 184.5 184.5 184.5 184.5 184.4 + 184.4 184.4 184.4 184.3 184.3 184.3 184.3 184.2 184.2 184.2 184.2 184.1 184.1 184.1 184.1 184.1 184.0 184.0 + 184.0 184.0 183.9 183.9 183.9 183.9 183.8 183.8 ]plong + s[ 539.4 539.4 539.4 539.3 539.3 539.3 539.3 539.3 539.3 539.3 539.2 539.2 539.2 539.2 539.2 539.2 539.2 539.2 + 539.1 539.1 539.1 539.1 539.1 539.1 539.0 539.0 539.0 539.0 539.0 539.0 539.0 538.9 538.9 538.9 538.9 538.9 + 538.9 538.9 538.8 538.8 538.8 ][ 184.8 184.7 184.7 184.7 184.7 184.7 184.6 184.6 184.6 184.5 184.5 184.5 184.5 + 184.4 184.4 184.4 184.4 184.3 184.3 184.3 184.3 184.2 184.2 184.2 184.2 184.1 184.1 184.1 184.1 184.1 184.0 + 184.0 184.0 184.0 183.9 183.9 183.9 183.9 183.8 183.8 183.8 ]plong + s[ 247.9 247.9 248.0 248.0 248.0 248.0 248.0 248.0 248.0 248.0 248.0 248.1 248.1 248.1 248.1 248.1 248.1 248.1 + 248.2 248.2 248.2 248.2 248.2 248.2 248.3 248.3 248.3 248.3 248.3 248.3 248.3 248.4 248.4 248.4 248.4 248.4 + 248.4 248.4 ][ 185.8 185.7 185.7 185.7 185.7 185.6 185.6 185.6 185.6 185.5 185.5 185.5 185.5 185.4 185.4 185.4 + 185.4 185.3 185.3 185.3 185.3 185.2 185.2 185.2 185.2 185.1 185.1 185.1 185.1 185.0 185.0 185.0 185.0 184.9 + 184.9 184.9 184.9 184.8 ]plong + 248.5 184.8 248.5 184.8 248.5 184.8 248.4 184.8 4 pls + 539.4 184.8 539.4 184.8 539.4 184.8 539.4 184.9 539.4 184.9 5 pls + s[ 540.0 540.0 539.9 539.9 539.9 539.9 539.9 539.9 539.9 539.8 539.8 539.8 539.8 539.8 539.8 539.8 539.7 539.7 + 539.7 539.7 539.7 539.7 539.7 539.6 539.6 539.6 539.6 539.6 539.6 539.5 539.5 539.5 539.5 539.5 539.5 539.5 + 539.4 ][ 185.8 185.7 185.7 185.7 185.7 185.6 185.6 185.6 185.6 185.5 185.5 185.5 185.5 185.4 185.4 185.4 185.4 + 185.3 185.3 185.3 185.3 185.2 185.2 185.2 185.2 185.1 185.1 185.1 185.1 185.0 185.0 185.0 185.0 184.9 184.9 + 184.9 184.9 ]plong + s[ 247.4 247.4 247.4 247.4 247.4 247.4 247.4 247.4 247.5 ][ 186.7 186.7 186.7 186.7 186.6 186.6 186.6 186.5 186.5 +]plong + s[ 247.5 247.5 247.5 247.5 247.5 247.5 247.6 247.6 247.6 247.6 247.6 247.6 247.6 247.7 247.7 247.7 247.7 247.7 + 247.7 247.7 247.8 247.8 247.8 247.8 247.8 247.8 247.8 247.9 247.9 247.9 247.9 247.9 ][ 186.5 186.5 186.5 186.5 + 186.4 186.4 186.4 186.3 186.3 186.3 186.3 186.2 186.2 186.2 186.2 186.1 186.1 186.1 186.1 186.0 186.0 186.0 + 186.0 186.0 185.9 185.9 185.9 185.9 185.8 185.8 185.8 185.8 ]plong + s[ 540.4 540.4 540.4 540.4 540.4 540.4 540.3 540.3 540.3 540.3 540.3 540.3 540.2 540.2 540.2 540.2 540.2 540.2 + 540.2 540.2 540.1 540.1 540.1 540.1 540.1 540.1 540.1 540.1 540.0 540.0 540.0 540.0 540.0 ][ 186.6 186.5 186.5 + 186.5 186.5 186.4 186.4 186.4 186.3 186.3 186.3 186.3 186.3 186.2 186.2 186.2 186.1 186.1 186.1 186.1 186.1 + 186.0 186.0 186.0 186.0 185.9 185.9 185.9 185.9 185.8 185.8 185.8 185.8 ]plong + s[ 540.5 540.5 540.5 540.5 540.5 540.5 540.4 540.4 ][ 186.7 186.7 186.7 186.7 186.6 186.6 186.6 186.6 ]plong + s[ 246.8 246.8 246.8 246.8 246.9 246.9 246.9 246.9 246.9 246.9 246.9 246.9 247.0 247.0 247.0 247.0 247.0 247.0 + 247.1 247.1 247.1 247.1 247.1 247.1 247.1 247.1 247.1 247.2 247.2 247.2 247.2 247.2 247.2 247.2 247.3 247.3 + 247.3 247.3 247.3 247.3 247.4 ][ 187.7 187.7 187.7 187.7 187.6 187.6 187.6 187.6 187.5 187.5 187.5 187.4 187.4 + 187.4 187.4 187.4 187.3 187.3 187.3 187.2 187.2 187.2 187.2 187.1 187.1 187.1 187.1 187.0 187.0 187.0 187.0 + 187.0 186.9 186.9 186.9 186.9 186.8 186.8 186.8 186.8 186.7 ]plong + s[ 541.1 541.1 541.1 541.0 541.0 541.0 541.0 541.0 541.0 541.0 540.9 540.9 540.9 540.9 540.9 540.9 540.9 540.8 + 540.8 540.8 540.8 540.8 540.8 540.8 540.7 540.7 540.7 540.7 540.7 540.7 540.7 540.6 540.6 540.6 540.6 540.6 + 540.6 540.6 540.5 540.5 ][ 187.7 187.7 187.7 187.6 187.6 187.6 187.6 187.5 187.5 187.5 187.5 187.4 187.4 187.4 + 187.4 187.3 187.3 187.3 187.2 187.2 187.2 187.2 187.1 187.1 187.1 187.1 187.0 187.0 187.0 187.0 187.0 186.9 + 186.9 186.9 186.9 186.8 186.8 186.8 186.8 186.7 ]plong + s[ 246.2 246.2 246.3 246.3 246.3 246.3 246.3 246.3 246.3 246.4 246.4 246.4 246.4 246.4 246.4 246.4 246.5 246.5 ][ + 188.7 188.7 188.7 188.6 188.6 188.6 188.6 188.5 188.5 188.5 188.5 188.4 188.4 188.4 188.3 188.3 188.3 188.3 ]plong + s[ 246.5 246.5 246.5 246.5 246.5 246.5 246.6 246.6 246.6 246.6 246.6 246.6 246.7 246.7 246.7 246.7 246.7 246.7 + 246.7 246.7 246.8 246.8 246.8 ][ 188.3 188.3 188.2 188.2 188.2 188.1 188.1 188.1 188.1 188.0 188.0 188.0 188.0 + 187.9 187.9 187.9 187.9 187.9 187.8 187.8 187.8 187.8 187.7 ]plong + s[ 541.4 541.4 541.4 541.4 541.3 541.3 541.3 541.3 541.3 541.3 541.3 541.2 541.2 541.2 541.2 541.2 541.2 541.2 + 541.1 541.1 541.1 541.1 541.1 541.1 ][ 188.3 188.3 188.2 188.2 188.2 188.1 188.1 188.1 188.1 188.1 188.0 188.0 + 188.0 187.9 187.9 187.9 187.9 187.9 187.8 187.8 187.8 187.8 187.7 187.7 ]plong + s[ 541.6 541.6 541.6 541.6 541.6 541.6 541.5 541.5 541.5 541.5 541.5 541.5 541.5 541.4 541.4 541.4 541.4 ][ 188.7 + 188.7 188.6 188.6 188.6 188.6 188.5 188.5 188.5 188.5 188.4 188.4 188.4 188.4 188.3 188.3 188.3 ]plong + s[ 245.7 245.7 245.7 245.8 245.8 245.8 245.8 245.8 245.8 245.8 245.9 245.9 245.9 245.9 245.9 245.9 245.9 246.0 + 246.0 246.0 246.0 246.0 246.0 246.0 246.1 246.1 246.1 246.1 246.1 246.1 246.1 246.1 246.1 246.2 246.2 246.2 + 246.2 246.2 246.2 ][ 189.7 189.6 189.6 189.6 189.6 189.5 189.5 189.5 189.5 189.4 189.4 189.4 189.4 189.3 189.3 + 189.3 189.3 189.2 189.2 189.2 189.2 189.1 189.1 189.1 189.1 189.0 189.0 189.0 189.0 188.9 188.9 188.9 188.9 + 188.8 188.8 188.8 188.8 188.7 188.7 ]plong + s[ 542.2 542.2 542.1 542.1 542.1 542.1 542.1 542.1 542.1 542.1 542.0 542.0 542.0 542.0 542.0 542.0 542.0 541.9 + 541.9 541.9 541.9 541.9 541.9 541.9 541.8 541.8 541.8 541.8 541.8 541.8 541.8 541.7 541.7 541.7 541.7 541.7 + 541.7 541.7 541.6 541.6 ][ 189.7 189.6 189.6 189.6 189.6 189.5 189.5 189.5 189.5 189.4 189.4 189.4 189.4 189.3 + 189.3 189.3 189.3 189.2 189.2 189.2 189.2 189.1 189.1 189.1 189.1 189.0 189.0 189.0 189.0 188.9 188.9 188.9 + 188.9 188.8 188.8 188.8 188.8 188.7 188.7 188.7 ]plong + s[ 245.2 245.2 245.2 245.2 245.2 245.2 245.2 245.3 245.3 245.3 245.3 245.3 245.3 245.4 245.4 245.4 245.4 245.4 + 245.4 245.4 245.4 245.5 245.5 245.5 245.5 ][ 190.7 190.6 190.6 190.6 190.6 190.5 190.5 190.5 190.5 190.4 190.4 + 190.4 190.4 190.3 190.3 190.3 190.3 190.2 190.2 190.2 190.1 190.1 190.1 190.1 190.0 ]plong + s[ 245.5 245.5 245.5 245.5 245.6 245.6 245.6 245.6 245.6 245.6 245.6 245.7 245.7 245.7 245.7 245.7 ][ 190.0 190.0 + 190.0 190.0 189.9 189.9 189.9 189.9 189.9 189.8 189.8 189.8 189.8 189.7 189.7 189.7 ]plong + s[ 542.4 542.4 542.3 542.3 542.3 542.3 542.3 542.3 542.3 542.2 542.2 542.2 542.2 542.2 542.2 542.2 ][ 190.0 190.0 + 190.0 190.0 189.9 189.9 189.9 189.9 189.9 189.8 189.8 189.8 189.8 189.7 189.7 189.7 ]plong + s[ 542.7 542.7 542.7 542.7 542.7 542.6 542.6 542.6 542.6 542.6 542.6 542.6 542.5 542.5 542.5 542.5 542.5 542.5 + 542.5 542.4 542.4 542.4 542.4 542.4 542.4 ][ 190.7 190.6 190.6 190.6 190.6 190.5 190.5 190.5 190.5 190.4 190.4 + 190.4 190.4 190.3 190.3 190.3 190.3 190.2 190.2 190.2 190.1 190.1 190.1 190.1 190.0 ]plong + s[ 244.7 244.7 244.7 244.7 244.7 244.7 244.7 244.8 244.8 244.8 244.8 244.8 244.8 244.8 244.9 244.9 244.9 244.9 + 244.9 244.9 244.9 244.9 245.0 245.0 245.0 245.0 245.0 245.0 245.0 245.1 245.1 245.1 245.1 245.1 245.1 245.1 + 245.1 245.2 245.2 ][ 191.6 191.6 191.6 191.6 191.5 191.5 191.5 191.5 191.4 191.4 191.4 191.4 191.3 191.3 191.3 + 191.2 191.2 191.2 191.2 191.1 191.1 191.1 191.1 191.0 191.0 191.0 191.0 190.9 190.9 190.9 190.9 190.9 190.8 + 190.8 190.8 190.7 190.7 190.7 190.7 ]plong + s[ 543.2 543.2 543.2 543.2 543.2 543.1 543.1 543.1 543.1 543.1 543.1 543.1 543.1 543.1 543.0 543.0 543.0 543.0 + 543.0 543.0 543.0 543.0 542.9 542.9 542.9 542.9 542.9 542.9 542.8 542.8 542.8 542.8 542.8 542.8 542.8 542.8 + 542.7 542.7 542.7 ][ 191.6 191.6 191.6 191.6 191.5 191.5 191.5 191.5 191.4 191.4 191.4 191.4 191.3 191.3 191.3 + 191.2 191.2 191.2 191.2 191.1 191.1 191.1 191.1 191.0 191.0 191.0 191.0 190.9 190.9 190.9 190.9 190.9 190.8 + 190.8 190.8 190.7 190.7 190.7 190.7 ]plong + s[ 244.2 244.2 244.2 244.2 244.2 244.2 244.2 244.2 244.2 244.3 244.3 244.3 244.3 244.3 244.3 244.3 244.4 244.4 + 244.4 244.4 244.4 244.4 244.4 244.5 244.5 244.5 244.5 244.5 244.5 ][ 192.6 192.6 192.6 192.5 192.5 192.5 192.5 + 192.4 192.4 192.4 192.3 192.3 192.3 192.3 192.2 192.2 192.2 192.2 192.1 192.1 192.1 192.1 192.0 192.0 192.0 + 192.0 191.9 191.9 191.9 ]plong + s[ 244.5 244.5 244.5 244.6 244.6 244.6 244.6 244.6 244.6 244.6 244.7 ][ 191.9 191.9 191.8 191.8 191.8 191.8 191.7 + 191.7 191.7 191.7 191.6 ]plong + s[ 543.4 543.3 543.3 543.3 543.3 543.3 543.3 543.3 543.3 543.2 543.2 ][ 191.9 191.9 191.8 191.8 191.8 191.8 191.7 + 191.7 191.7 191.7 191.6 ]plong + s[ 543.7 543.7 543.7 543.7 543.7 543.7 543.7 543.6 543.6 543.6 543.6 543.6 543.6 543.6 543.5 543.5 543.5 543.5 + 543.5 543.5 543.5 543.5 543.4 543.4 543.4 543.4 543.4 543.4 543.4 ][ 192.6 192.6 192.6 192.5 192.5 192.5 192.5 + 192.4 192.4 192.4 192.4 192.3 192.3 192.3 192.3 192.2 192.2 192.2 192.1 192.1 192.1 192.1 192.0 192.0 192.0 + 192.0 191.9 191.9 191.9 ]plong + s[ 243.6 243.6 243.7 243.7 243.7 243.7 243.7 243.7 243.7 243.8 243.8 243.8 243.8 243.8 243.8 243.8 243.9 243.9 + 243.9 243.9 243.9 243.9 243.9 244.0 244.0 244.0 244.0 244.0 244.0 244.0 244.0 244.1 244.1 244.1 244.1 244.1 + 244.1 244.1 244.2 ][ 193.6 193.6 193.5 193.5 193.5 193.5 193.4 193.4 193.4 193.4 193.3 193.3 193.3 193.3 193.2 + 193.2 193.2 193.2 193.1 193.1 193.1 193.0 193.0 193.0 193.0 192.9 192.9 192.9 192.9 192.8 192.8 192.8 192.8 + 192.7 192.7 192.7 192.7 192.6 192.6 ]plong + s[ 544.2 544.2 544.2 544.2 544.2 544.2 544.2 544.1 544.1 544.1 544.1 544.1 544.1 544.1 544.0 544.0 544.0 544.0 + 544.0 544.0 544.0 544.0 544.0 543.9 543.9 543.9 543.9 543.9 543.9 543.9 543.9 543.8 543.8 543.8 543.8 543.8 + 543.8 543.8 543.7 ][ 193.6 193.6 193.6 193.5 193.5 193.5 193.4 193.4 193.4 193.4 193.3 193.3 193.3 193.3 193.2 + 193.2 193.2 193.2 193.1 193.1 193.1 193.0 193.0 193.0 193.0 192.9 192.9 192.9 192.9 192.8 192.8 192.8 192.8 + 192.7 192.7 192.7 192.7 192.6 192.6 ]plong + s[ 243.1 243.2 243.2 243.2 243.2 243.2 243.2 243.2 243.2 243.2 243.3 243.3 243.3 243.3 243.3 243.3 243.3 243.4 + 243.4 243.4 243.4 243.4 243.4 243.4 243.4 243.5 243.5 243.5 243.5 243.5 243.5 243.5 ][ 194.6 194.6 194.5 194.5 + 194.5 194.5 194.4 194.4 194.4 194.3 194.3 194.3 194.3 194.2 194.2 194.2 194.2 194.1 194.1 194.1 194.1 194.0 + 194.0 194.0 193.9 193.9 193.9 193.9 193.8 193.8 193.8 193.8 ]plong + s[ 243.5 243.6 243.6 243.6 243.6 243.6 243.6 243.6 ][ 193.8 193.8 193.7 193.7 193.7 193.6 193.6 193.6 ]plong + s[ 544.3 544.3 544.3 544.3 544.3 544.3 544.2 544.2 ][ 193.8 193.8 193.7 193.7 193.7 193.7 193.6 193.6 ]plong + s[ 544.8 544.7 544.7 544.7 544.7 544.7 544.7 544.7 544.6 544.6 544.6 544.6 544.6 544.6 544.6 544.6 544.5 544.5 + 544.5 544.5 544.5 544.5 544.5 544.4 544.4 544.4 544.4 544.4 544.4 544.4 544.4 544.3 ][ 194.6 194.6 194.5 194.5 + 194.5 194.5 194.4 194.4 194.4 194.3 194.3 194.3 194.3 194.2 194.2 194.2 194.2 194.1 194.1 194.1 194.1 194.0 + 194.0 194.0 193.9 193.9 193.9 193.9 193.8 193.8 193.8 193.8 ]plong + s[ 242.7 242.7 242.7 242.7 242.7 242.7 242.7 242.7 242.8 242.8 242.8 242.8 242.8 242.8 242.8 242.9 242.9 242.9 + 242.9 242.9 242.9 242.9 242.9 243.0 243.0 243.0 243.0 243.0 243.0 243.0 243.1 243.1 243.1 243.1 243.1 243.1 + 243.1 243.1 ][ 195.6 195.5 195.5 195.5 195.4 195.4 195.4 195.4 195.3 195.3 195.3 195.3 195.2 195.2 195.2 195.2 + 195.1 195.1 195.1 195.0 195.0 195.0 195.0 194.9 194.9 194.9 194.9 194.8 194.8 194.8 194.8 194.7 194.7 194.7 + 194.7 194.6 194.6 194.6 ]plong + s[ 545.2 545.2 545.2 545.2 545.2 545.2 545.1 545.1 545.1 545.1 545.1 545.1 545.1 545.1 545.0 545.0 545.0 545.0 + 545.0 545.0 545.0 545.0 545.0 544.9 544.9 544.9 544.9 544.9 544.9 544.9 544.8 544.8 544.8 544.8 544.8 544.8 + 544.8 544.8 ][ 195.6 195.5 195.5 195.5 195.4 195.4 195.4 195.4 195.3 195.3 195.3 195.3 195.2 195.2 195.2 195.2 + 195.1 195.1 195.1 195.0 195.0 195.0 195.0 194.9 194.9 194.9 194.9 194.8 194.8 194.8 194.8 194.7 194.7 194.7 + 194.7 194.6 194.6 194.6 ]plong + s[ 242.2 242.2 242.2 242.2 242.2 242.2 242.2 242.2 242.3 242.3 242.3 242.3 242.3 242.3 242.3 242.3 242.4 242.4 + 242.4 242.4 242.4 242.4 242.4 242.5 242.5 242.5 242.5 242.5 242.5 242.5 242.5 242.6 ][ 196.5 196.5 196.5 196.5 + 196.4 196.4 196.4 196.4 196.3 196.3 196.3 196.3 196.2 196.2 196.2 196.1 196.1 196.1 196.1 196.0 196.0 196.0 + 196.0 195.9 195.9 195.9 195.9 195.8 195.8 195.8 195.7 195.7 ]plong + s[ 242.6 242.6 242.6 242.6 242.6 242.6 242.6 242.7 ][ 195.7 195.7 195.7 195.7 195.6 195.6 195.6 195.6 ]plong + s[ 545.3 545.3 545.3 545.3 545.3 545.3 545.2 545.2 ][ 195.7 195.7 195.7 195.7 195.6 195.6 195.6 195.6 ]plong + s[ 545.7 545.7 545.7 545.7 545.7 545.6 545.6 545.6 545.6 545.6 545.6 545.6 545.6 545.5 545.5 545.5 545.5 545.5 + 545.5 545.5 545.5 545.4 545.4 545.4 545.4 545.4 545.4 545.4 545.3 545.3 545.3 ][ 196.5 196.5 196.5 196.5 196.4 + 196.4 196.4 196.3 196.3 196.3 196.3 196.2 196.2 196.2 196.1 196.1 196.1 196.1 196.0 196.0 196.0 196.0 195.9 + 195.9 195.9 195.9 195.8 195.8 195.8 195.7 195.7 ]plong + s[ 241.7 241.7 241.7 241.7 241.7 241.8 241.8 241.8 241.8 241.8 241.8 241.8 241.8 241.9 241.9 241.9 241.9 241.9 + 241.9 241.9 242.0 242.0 242.0 242.0 242.0 242.0 242.0 242.0 242.1 242.1 242.1 242.1 242.1 242.1 242.1 242.1 + 242.2 242.2 ][ 197.5 197.5 197.5 197.4 197.4 197.4 197.4 197.3 197.3 197.3 197.3 197.2 197.2 197.2 197.2 197.1 + 197.1 197.1 197.0 197.0 197.0 197.0 196.9 196.9 196.9 196.9 196.8 196.8 196.8 196.8 196.7 196.7 196.7 196.7 + 196.6 196.6 196.6 196.5 ]plong + s[ 546.2 546.2 546.2 546.2 546.1 546.1 546.1 546.1 546.1 546.1 546.1 546.0 546.0 546.0 546.0 546.0 546.0 546.0 + 546.0 546.0 545.9 545.9 545.9 545.9 545.9 545.9 545.9 545.9 545.8 545.8 545.8 545.8 545.8 545.8 545.8 545.7 + 545.7 545.7 545.7 ][ 197.5 197.5 197.5 197.4 197.4 197.4 197.4 197.3 197.3 197.3 197.3 197.2 197.2 197.2 197.2 + 197.1 197.1 197.1 197.0 197.0 197.0 197.0 196.9 196.9 196.9 196.9 196.8 196.8 196.8 196.8 196.7 196.7 196.7 + 196.7 196.6 196.6 196.6 196.6 196.5 ]plong + s[ 241.2 241.2 241.3 241.3 241.3 241.3 241.3 241.3 241.3 241.3 241.3 241.4 241.4 241.4 241.4 241.4 241.4 241.4 + 241.4 241.5 241.5 241.5 241.5 241.5 241.5 241.5 241.6 241.6 241.6 241.6 ][ 198.5 198.5 198.5 198.4 198.4 198.4 + 198.3 198.3 198.3 198.3 198.2 198.2 198.2 198.2 198.1 198.1 198.1 198.1 198.0 198.0 198.0 197.9 197.9 197.9 + 197.9 197.8 197.8 197.8 197.7 197.7 ]plong + s[ 241.6 241.6 241.6 241.6 241.6 241.6 241.7 241.7 241.7 ][ 197.7 197.7 197.7 197.6 197.6 197.6 197.6 197.6 197.5 +]plong + s[ 546.3 546.3 546.3 546.3 546.2 546.2 546.2 546.2 546.2 546.2 ][ 197.8 197.7 197.7 197.7 197.7 197.6 197.6 197.6 + 197.6 197.5 ]plong + s[ 546.6 546.6 546.6 546.6 546.6 546.6 546.6 546.6 546.6 546.5 546.5 546.5 546.5 546.5 546.5 546.5 546.4 546.4 + 546.4 546.4 546.4 546.4 546.4 546.4 546.3 546.3 546.3 546.3 ][ 198.5 198.5 198.4 198.4 198.4 198.3 198.3 198.3 + 198.3 198.2 198.2 198.2 198.2 198.1 198.1 198.1 198.1 198.0 198.0 198.0 197.9 197.9 197.9 197.9 197.8 197.8 + 197.8 197.8 ]plong + s[ 240.8 240.8 240.8 240.8 240.8 240.8 240.9 240.9 240.9 240.9 240.9 240.9 240.9 240.9 240.9 241.0 241.0 241.0 + 241.0 241.0 241.0 241.0 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.2 241.2 241.2 241.2 241.2 241.2 + 241.2 ][ 199.5 199.4 199.4 199.4 199.4 199.3 199.3 199.3 199.3 199.2 199.2 199.2 199.1 199.1 199.1 199.1 199.0 + 199.0 199.0 199.0 198.9 198.9 198.9 198.8 198.8 198.8 198.8 198.7 198.7 198.7 198.7 198.6 198.6 198.6 198.6 + 198.5 198.5 ]plong + s[ 547.1 547.1 547.1 547.1 547.1 547.0 547.0 547.0 547.0 547.0 547.0 547.0 546.9 546.9 546.9 546.9 546.9 546.9 + 546.9 546.9 546.9 546.9 546.8 546.8 546.8 546.8 546.8 546.8 546.8 546.8 546.7 546.7 546.7 546.7 546.7 546.7 + 546.7 546.6 ][ 199.5 199.4 199.4 199.4 199.4 199.3 199.3 199.3 199.3 199.2 199.2 199.2 199.2 199.1 199.1 199.1 + 199.0 199.0 199.0 199.0 198.9 198.9 198.9 198.8 198.8 198.8 198.8 198.7 198.7 198.7 198.7 198.6 198.6 198.6 + 198.6 198.5 198.5 198.5 ]plong + s[ 240.3 240.3 240.3 240.3 240.4 240.4 240.4 240.4 240.4 240.4 240.4 240.4 240.5 240.5 240.5 240.5 240.5 240.5 + 240.5 240.5 240.6 240.6 240.6 240.6 ][ 200.5 200.4 200.4 200.4 200.4 200.3 200.3 200.3 200.3 200.2 200.2 200.2 + 200.1 200.1 200.1 200.1 200.0 200.0 200.0 199.9 199.9 199.9 199.9 199.8 ]plong + s[ 240.6 240.6 240.6 240.6 240.7 240.7 240.7 240.7 240.7 240.7 240.7 240.7 240.7 240.8 240.8 ][ 199.8 199.8 199.8 + 199.8 199.7 199.7 199.7 199.6 199.6 199.6 199.6 199.6 199.5 199.5 199.5 ]plong + s[ 547.3 547.3 547.3 547.2 547.2 547.2 547.2 547.2 547.2 547.2 547.1 547.1 547.1 547.1 547.1 ][ 199.8 199.8 199.8 + 199.8 199.7 199.7 199.7 199.7 199.6 199.6 199.6 199.6 199.5 199.5 199.5 ]plong + s[ 547.6 547.6 547.5 547.5 547.5 547.5 547.5 547.5 547.5 547.5 547.4 547.4 547.4 547.4 547.4 547.4 547.4 547.4 + 547.3 547.3 547.3 547.3 547.3 547.3 ][ 200.5 200.4 200.4 200.4 200.4 200.3 200.3 200.3 200.3 200.2 200.2 200.2 + 200.1 200.1 200.1 200.1 200.0 200.0 200.0 199.9 199.9 199.9 199.9 199.8 ]plong + s[ 239.9 239.9 239.9 239.9 239.9 239.9 240.0 240.0 240.0 240.0 240.0 240.0 240.0 240.0 240.0 240.1 240.1 240.1 + 240.1 240.1 240.1 240.1 240.2 240.2 240.2 240.2 240.2 240.2 240.2 240.2 240.2 240.3 240.3 240.3 240.3 240.3 + 240.3 ][ 201.4 201.4 201.4 201.4 201.3 201.3 201.3 201.2 201.2 201.2 201.2 201.1 201.1 201.1 201.0 201.0 201.0 + 201.0 200.9 200.9 200.9 200.9 200.8 200.8 200.8 200.8 200.7 200.7 200.7 200.6 200.6 200.6 200.6 200.5 200.5 + 200.5 200.5 ]plong + s[ 548.0 548.0 548.0 548.0 548.0 547.9 547.9 547.9 547.9 547.9 547.9 547.9 547.9 547.9 547.8 547.8 547.8 547.8 + 547.8 547.8 547.8 547.8 547.7 547.7 547.7 547.7 547.7 547.7 547.7 547.7 547.6 547.6 547.6 547.6 547.6 547.6 + 547.6 ][ 201.4 201.4 201.4 201.4 201.3 201.3 201.3 201.2 201.2 201.2 201.2 201.1 201.1 201.1 201.1 201.0 201.0 + 201.0 200.9 200.9 200.9 200.9 200.8 200.8 200.8 200.8 200.7 200.7 200.7 200.6 200.6 200.6 200.6 200.5 200.5 + 200.5 200.5 ]plong + s[ 239.4 239.4 239.5 239.5 239.5 239.5 239.5 239.5 239.5 239.6 239.6 239.6 239.6 239.6 239.6 239.6 ][ 202.4 202.4 + 202.4 202.3 202.3 202.3 202.3 202.2 202.2 202.2 202.1 202.1 202.1 202.1 202.0 202.0 ]plong + s[ 239.6 239.6 239.6 239.7 239.7 239.7 239.7 239.7 239.7 239.7 239.7 239.8 239.8 239.8 239.8 239.8 239.8 239.8 + 239.8 239.9 239.9 239.9 ][ 202.0 202.0 201.9 201.9 201.9 201.9 201.8 201.8 201.8 201.7 201.7 201.7 201.7 201.6 + 201.6 201.6 201.6 201.5 201.5 201.5 201.5 201.4 ]plong + s[ 548.3 548.2 548.2 548.2 548.2 548.2 548.2 548.2 548.2 548.1 548.1 548.1 548.1 548.1 548.1 548.1 548.1 548.0 + 548.0 548.0 548.0 548.0 ][ 202.0 202.0 201.9 201.9 201.9 201.9 201.8 201.8 201.8 201.8 201.7 201.7 201.7 201.6 + 201.6 201.6 201.6 201.5 201.5 201.5 201.5 201.4 ]plong + s[ 548.4 548.4 548.4 548.4 548.4 548.4 548.4 548.4 548.3 548.3 548.3 548.3 548.3 548.3 548.3 548.3 ][ 202.4 202.4 + 202.4 202.3 202.3 202.3 202.3 202.2 202.2 202.2 202.1 202.1 202.1 202.1 202.0 202.0 ]plong + s[ 239.0 239.0 239.0 239.1 239.1 239.1 239.1 239.1 239.1 239.1 239.1 239.1 239.2 239.2 239.2 239.2 239.2 239.2 + 239.2 239.3 239.3 239.3 239.3 239.3 239.3 239.3 239.3 239.3 239.3 239.4 239.4 239.4 239.4 239.4 239.4 239.4 + 239.4 ][ 203.4 203.4 203.3 203.3 203.3 203.3 203.2 203.2 203.2 203.2 203.1 203.1 203.1 203.0 203.0 203.0 203.0 + 202.9 202.9 202.9 202.8 202.8 202.8 202.8 202.7 202.7 202.7 202.6 202.6 202.6 202.6 202.5 202.5 202.5 202.5 + 202.4 202.4 ]plong + s[ 548.9 548.9 548.9 548.8 548.8 548.8 548.8 548.8 548.8 548.8 548.8 548.7 548.7 548.7 548.7 548.7 548.7 548.7 + 548.7 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.5 548.5 548.5 548.5 548.5 548.5 548.5 548.4 + 548.4 ][ 203.4 203.4 203.3 203.3 203.3 203.3 203.2 203.2 203.2 203.2 203.1 203.1 203.1 203.0 203.0 203.0 203.0 + 202.9 202.9 202.9 202.8 202.8 202.8 202.8 202.7 202.7 202.7 202.6 202.6 202.6 202.6 202.5 202.5 202.5 202.5 + 202.5 202.4 ]plong + s[ 238.6 238.6 238.6 238.6 238.6 238.6 ][ 204.4 204.4 204.3 204.3 204.3 204.2 ]plong + s[ 238.6 238.7 238.7 238.7 238.7 238.7 238.7 238.7 238.7 238.7 238.8 238.8 238.8 238.8 238.8 238.8 238.8 238.9 + 238.9 238.9 238.9 238.9 238.9 238.9 238.9 238.9 239.0 239.0 239.0 239.0 239.0 239.0 ][ 204.2 204.2 204.2 204.2 + 204.1 204.1 204.1 204.1 204.0 204.0 204.0 203.9 203.9 203.9 203.9 203.8 203.8 203.8 203.7 203.7 203.7 203.7 + 203.6 203.6 203.6 203.5 203.5 203.5 203.5 203.4 203.4 203.4 ]plong + s[ 549.2 549.2 549.2 549.2 549.2 549.2 549.2 549.2 549.1 549.1 549.1 549.1 549.1 549.1 549.1 549.1 549.1 549.0 + 549.0 549.0 549.0 549.0 549.0 549.0 549.0 548.9 548.9 548.9 548.9 548.9 548.9 548.9 548.9 ][ 204.3 204.3 204.2 + 204.2 204.2 204.1 204.1 204.1 204.1 204.0 204.0 204.0 203.9 203.9 203.9 203.9 203.8 203.8 203.8 203.7 203.7 + 203.7 203.7 203.6 203.6 203.6 203.5 203.5 203.5 203.5 203.5 203.4 203.4 ]plong + 549.2 204.3 549.3 204.3 549.3 204.3 549.3 204.4 549.3 204.4 5 pls + s[ 238.2 238.2 238.2 238.2 238.2 238.2 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.4 238.4 + 238.4 238.4 238.4 238.4 238.4 238.4 238.5 238.5 238.5 238.5 238.5 238.5 238.5 238.5 238.5 238.6 238.6 238.6 ][ + 205.4 205.3 205.3 205.3 205.2 205.2 205.2 205.2 205.1 205.1 205.1 205.0 205.0 205.0 205.0 204.9 204.9 204.9 + 204.8 204.8 204.8 204.8 204.7 204.7 204.7 204.6 204.6 204.6 204.6 204.5 204.5 204.5 204.4 204.4 204.4 204.4 ]plong + s[ 549.7 549.7 549.7 549.7 549.7 549.7 549.6 549.6 549.6 549.6 549.6 549.6 549.6 549.5 549.5 549.5 549.5 549.5 + 549.5 549.5 549.5 549.5 549.4 549.4 549.4 549.4 549.4 549.4 549.4 549.4 549.3 549.3 549.3 549.3 549.3 549.3 ][ + 205.4 205.3 205.3 205.3 205.2 205.2 205.2 205.2 205.1 205.1 205.1 205.0 205.0 205.0 205.0 204.9 204.9 204.9 + 204.8 204.8 204.8 204.8 204.7 204.7 204.7 204.6 204.6 204.6 204.6 204.5 204.5 204.5 204.4 204.4 204.4 204.4 ]plong + s[ 237.8 237.8 237.8 237.8 237.8 237.8 237.8 237.8 237.9 237.9 237.9 237.9 237.9 237.9 237.9 237.9 238.0 238.0 + 238.0 238.0 238.0 238.0 238.0 238.0 238.0 238.1 238.1 238.1 238.1 238.1 238.1 238.1 238.1 238.2 238.2 238.2 + 238.2 ][ 206.3 206.3 206.3 206.3 206.2 206.2 206.2 206.1 206.1 206.1 206.1 206.0 206.0 206.0 205.9 205.9 205.9 + 205.9 205.8 205.8 205.8 205.7 205.7 205.7 205.7 205.6 205.6 205.6 205.6 205.5 205.5 205.5 205.4 205.4 205.4 + 205.4 205.4 ]plong + s[ 550.1 550.1 550.1 550.1 550.1 550.0 550.0 550.0 550.0 550.0 550.0 550.0 550.0 550.0 549.9 549.9 549.9 549.9 + 549.9 549.9 549.9 549.9 549.9 549.9 549.9 549.8 549.8 549.8 549.8 549.8 549.8 549.8 549.7 549.7 549.7 549.7 + 549.7 ][ 206.3 206.3 206.3 206.3 206.2 206.2 206.2 206.1 206.1 206.1 206.1 206.0 206.0 206.0 206.0 205.9 205.9 + 205.9 205.8 205.8 205.8 205.8 205.7 205.7 205.7 205.6 205.6 205.6 205.6 205.5 205.5 205.5 205.5 205.4 205.4 + 205.4 205.4 ]plong + s[ 237.4 237.4 237.4 237.4 237.4 237.4 237.4 237.4 237.5 237.5 237.5 237.5 237.5 237.5 237.5 237.5 237.6 237.6 + 237.6 237.6 237.6 237.6 237.6 237.6 237.6 237.6 237.7 ][ 207.3 207.3 207.3 207.2 207.2 207.2 207.2 207.1 207.1 + 207.1 207.0 207.0 207.0 207.0 206.9 206.9 206.9 206.8 206.8 206.8 206.8 206.7 206.7 206.7 206.6 206.6 206.6 ]plong + s[ 237.7 237.7 237.7 237.7 237.7 237.7 237.7 237.7 237.8 237.8 ][ 206.6 206.6 206.5 206.5 206.5 206.4 206.4 206.4 + 206.4 206.3 ]plong + s[ 550.2 550.2 550.2 550.2 550.2 550.2 550.2 550.1 550.1 550.1 550.1 ][ 206.6 206.6 206.6 206.5 206.5 206.5 206.4 + 206.4 206.4 206.4 206.3 ]plong + s[ 550.5 550.5 550.5 550.5 550.5 550.5 550.4 550.4 550.4 550.4 550.4 550.4 550.4 550.4 550.4 550.3 550.3 550.3 + 550.3 550.3 550.3 550.3 550.3 550.2 550.2 550.2 ][ 207.3 207.3 207.3 207.2 207.2 207.2 207.2 207.1 207.1 207.1 + 207.0 207.0 207.0 207.0 206.9 206.9 206.9 206.8 206.8 206.8 206.8 206.7 206.7 206.7 206.6 206.6 ]plong + s[ 237.0 237.0 237.0 237.0 237.0 237.0 237.1 237.1 237.1 237.1 237.1 237.1 237.1 237.1 237.1 237.1 237.2 237.2 + 237.2 237.2 237.2 237.2 237.2 237.2 237.3 237.3 237.3 237.3 237.3 237.3 237.3 237.3 237.3 237.4 237.4 237.4 ][ + 208.3 208.3 208.2 208.2 208.2 208.2 208.1 208.1 208.1 208.0 208.0 208.0 208.0 207.9 207.9 207.9 207.8 207.8 + 207.8 207.8 207.7 207.7 207.7 207.6 207.6 207.6 207.6 207.5 207.5 207.5 207.4 207.4 207.4 207.4 207.4 207.3 ]plong + s[ 550.9 550.9 550.9 550.9 550.9 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.7 550.7 550.7 + 550.7 550.7 550.7 550.7 550.7 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.5 550.5 550.5 ][ + 208.3 208.3 208.3 208.2 208.2 208.2 208.1 208.1 208.1 208.1 208.0 208.0 208.0 207.9 207.9 207.9 207.9 207.8 + 207.8 207.8 207.7 207.7 207.7 207.7 207.6 207.6 207.6 207.5 207.5 207.5 207.5 207.4 207.4 207.4 207.4 207.3 ]plong + s[ 236.6 236.6 236.6 236.6 236.6 236.6 236.7 236.7 236.7 ][ 209.3 209.3 209.2 209.2 209.2 209.1 209.1 209.1 209.1 +]plong + s[ 236.7 236.7 236.7 236.7 236.7 236.7 236.7 236.8 236.8 236.8 236.8 236.8 236.8 236.8 236.8 236.9 236.9 236.9 + 236.9 236.9 236.9 236.9 236.9 236.9 236.9 237.0 237.0 237.0 ][ 209.1 209.0 209.0 209.0 208.9 208.9 208.9 208.9 + 208.8 208.8 208.8 208.7 208.7 208.7 208.7 208.6 208.6 208.6 208.5 208.5 208.5 208.5 208.4 208.4 208.4 208.3 + 208.3 208.3 ]plong + s[ 551.2 551.2 551.2 551.2 551.1 551.1 551.1 551.1 551.1 551.1 551.1 551.1 551.1 551.1 551.0 551.0 551.0 551.0 + 551.0 551.0 551.0 551.0 550.9 550.9 550.9 550.9 550.9 550.9 ][ 209.1 209.0 209.0 209.0 209.0 208.9 208.9 208.9 + 208.8 208.8 208.8 208.7 208.7 208.7 208.7 208.6 208.6 208.6 208.5 208.5 208.5 208.5 208.4 208.4 208.4 208.3 + 208.3 208.3 ]plong + s[ 551.3 551.3 551.3 551.3 551.2 551.2 551.2 551.2 551.2 ][ 209.3 209.3 209.2 209.2 209.2 209.2 209.1 209.1 209.1 +]plong + s[ 236.2 236.2 236.2 236.3 236.3 236.3 236.3 236.3 236.3 236.3 236.3 236.4 236.4 236.4 236.4 236.4 236.4 236.4 + 236.4 236.4 236.4 236.4 236.5 236.5 236.5 236.5 236.5 236.5 236.5 236.5 236.5 236.6 236.6 236.6 236.6 ][ 210.3 + 210.2 210.2 210.2 210.1 210.1 210.1 210.1 210.0 210.0 210.0 209.9 209.9 209.9 209.8 209.8 209.8 209.8 209.7 + 209.7 209.7 209.6 209.6 209.6 209.6 209.5 209.5 209.5 209.4 209.4 209.4 209.4 209.3 209.3 209.3 ]plong + s[ 551.7 551.7 551.6 551.6 551.6 551.6 551.6 551.6 551.6 551.6 551.6 551.5 551.5 551.5 551.5 551.5 551.5 551.5 + 551.5 551.5 551.4 551.4 551.4 551.4 551.4 551.4 551.4 551.4 551.4 551.3 551.3 551.3 551.3 551.3 551.3 ][ 210.3 + 210.2 210.2 210.2 210.1 210.1 210.1 210.1 210.0 210.0 210.0 209.9 209.9 209.9 209.9 209.8 209.8 209.8 209.7 + 209.7 209.7 209.7 209.6 209.6 209.6 209.5 209.5 209.5 209.4 209.4 209.4 209.4 209.3 209.3 209.3 ]plong + s[ 235.8 235.9 235.9 235.9 235.9 235.9 235.9 235.9 235.9 235.9 236.0 236.0 236.0 236.0 236.0 236.0 236.0 236.0 + 236.0 236.0 236.1 236.1 236.1 236.1 236.1 236.1 236.1 236.1 236.2 236.2 236.2 236.2 236.2 236.2 236.2 236.2 ][ + 211.2 211.2 211.2 211.2 211.1 211.1 211.1 211.0 211.0 211.0 211.0 210.9 210.9 210.9 210.8 210.8 210.8 210.8 + 210.7 210.7 210.7 210.6 210.6 210.6 210.6 210.5 210.5 210.5 210.4 210.4 210.4 210.3 210.3 210.3 210.3 210.3 ]plong + s[ 552.0 552.0 552.0 552.0 552.0 552.0 552.0 552.0 551.9 551.9 551.9 551.9 551.9 551.9 551.9 551.9 551.9 551.8 + 551.8 551.8 551.8 551.8 551.8 551.8 551.8 551.8 551.8 551.8 551.7 551.7 551.7 551.7 551.7 551.7 551.7 551.7 ][ + 211.2 211.2 211.2 211.2 211.1 211.1 211.1 211.0 211.0 211.0 211.0 210.9 210.9 210.9 210.8 210.8 210.8 210.8 + 210.7 210.7 210.7 210.6 210.6 210.6 210.6 210.5 210.5 210.5 210.4 210.4 210.4 210.4 210.3 210.3 210.3 210.3 ]plong + s[ 235.5 235.5 235.5 235.5 235.5 235.5 235.5 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.7 + 235.7 235.7 235.7 235.7 ][ 212.2 212.2 212.2 212.1 212.1 212.1 212.0 212.0 212.0 211.9 211.9 211.9 211.9 211.8 + 211.8 211.8 211.7 211.7 211.7 211.7 211.6 211.6 ]plong + s[ 235.7 235.7 235.7 235.7 235.8 235.8 235.8 235.8 235.8 235.8 235.8 235.8 235.8 235.8 ][ 211.6 211.6 211.5 211.5 + 211.5 211.5 211.4 211.4 211.4 211.3 211.3 211.3 211.3 211.2 ]plong + s[ 552.2 552.2 552.2 552.2 552.1 552.1 552.1 552.1 552.1 552.1 552.1 552.1 552.0 552.0 552.0 ][ 211.6 211.6 211.6 + 211.5 211.5 211.5 211.5 211.4 211.4 211.4 211.3 211.3 211.3 211.3 211.2 ]plong + s[ 552.4 552.4 552.4 552.4 552.4 552.4 552.3 552.3 552.3 552.3 552.3 552.3 552.3 552.3 552.2 552.2 552.2 552.2 + 552.2 552.2 552.2 ][ 212.2 212.2 212.2 212.1 212.1 212.1 212.0 212.0 212.0 212.0 211.9 211.9 211.9 211.8 211.8 + 211.8 211.7 211.7 211.7 211.7 211.6 ]plong + s[ 235.1 235.1 235.2 235.2 235.2 235.2 235.2 235.2 235.2 235.2 235.2 235.3 235.3 235.3 235.3 235.3 235.3 235.3 + 235.3 235.3 235.3 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.5 235.5 235.5 ][ 213.2 + 213.2 213.1 213.1 213.1 213.0 213.0 213.0 213.0 212.9 212.9 212.9 212.8 212.8 212.8 212.8 212.7 212.7 212.7 + 212.6 212.6 212.6 212.6 212.5 212.5 212.5 212.4 212.4 212.4 212.3 212.3 212.3 212.3 212.2 212.2 ]plong + s[ 552.8 552.8 552.7 552.7 552.7 552.7 552.7 552.7 552.7 552.7 552.7 552.6 552.6 552.6 552.6 552.6 552.6 552.6 + 552.6 552.6 552.6 552.5 552.5 552.5 552.5 552.5 552.5 552.5 552.5 552.4 552.4 552.4 552.4 552.4 552.4 ][ 213.2 + 213.2 213.1 213.1 213.1 213.0 213.0 213.0 213.0 212.9 212.9 212.9 212.8 212.8 212.8 212.8 212.7 212.7 212.7 + 212.6 212.6 212.6 212.6 212.5 212.5 212.5 212.4 212.4 212.4 212.4 212.3 212.3 212.3 212.2 212.2 ]plong + s[ 234.8 234.8 234.8 234.8 234.8 234.8 234.8 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.9 + 235.0 235.0 235.0 235.0 235.0 235.0 235.0 235.0 235.1 235.1 235.1 235.1 235.1 235.1 235.1 235.1 235.1 ][ 214.2 + 214.1 214.1 214.1 214.1 214.0 214.0 214.0 213.9 213.9 213.9 213.9 213.8 213.8 213.8 213.7 213.7 213.7 213.6 + 213.6 213.6 213.6 213.5 213.5 213.5 213.4 213.4 213.4 213.3 213.3 213.3 213.3 213.2 213.2 213.2 ]plong + s[ 553.1 553.1 553.1 553.1 553.1 553.1 553.0 553.0 553.0 553.0 553.0 553.0 553.0 553.0 552.9 552.9 552.9 552.9 + 552.9 552.9 552.9 552.9 552.9 552.9 552.9 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 ][ 214.2 + 214.1 214.1 214.1 214.1 214.0 214.0 214.0 213.9 213.9 213.9 213.9 213.8 213.8 213.8 213.7 213.7 213.7 213.7 + 213.6 213.6 213.6 213.5 213.5 213.5 213.4 213.4 213.4 213.3 213.3 213.3 213.3 213.2 213.2 213.2 ]plong + s[ 234.4 234.4 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.6 234.6 234.6 234.6 234.6 + 234.6 234.6 234.6 234.6 234.7 234.7 234.7 234.7 234.7 234.7 234.7 234.7 ][ 215.1 215.1 215.1 215.1 215.0 215.0 + 215.0 215.0 214.9 214.9 214.9 214.8 214.8 214.8 214.8 214.7 214.7 214.7 214.6 214.6 214.6 214.5 214.5 214.5 + 214.4 214.4 214.4 214.4 214.3 214.3 ]plong + s[ 234.7 234.7 234.7 234.7 234.8 234.8 ][ 214.3 214.3 214.2 214.2 214.2 214.2 ]plong + s[ 553.2 553.1 553.1 553.1 553.1 553.1 553.1 ][ 214.3 214.3 214.3 214.2 214.2 214.2 214.2 ]plong + s[ 553.5 553.4 553.4 553.4 553.4 553.4 553.4 553.4 553.4 553.4 553.3 553.3 553.3 553.3 553.3 553.3 553.3 553.3 + 553.3 553.3 553.3 553.2 553.2 553.2 553.2 553.2 553.2 553.2 553.2 ][ 215.1 215.1 215.1 215.1 215.0 215.0 215.0 + 215.0 214.9 214.9 214.9 214.8 214.8 214.8 214.8 214.7 214.7 214.7 214.6 214.6 214.6 214.5 214.5 214.5 214.4 + 214.4 214.4 214.4 214.3 ]plong + s[ 234.1 234.1 234.1 234.1 234.1 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.3 234.3 + 234.3 234.3 234.3 234.3 234.3 234.3 234.3 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 ][ 216.1 + 216.1 216.1 216.1 216.0 216.0 216.0 215.9 215.9 215.9 215.9 215.8 215.8 215.8 215.7 215.7 215.7 215.7 215.6 + 215.6 215.6 215.5 215.5 215.5 215.4 215.4 215.4 215.3 215.3 215.3 215.3 215.2 215.2 215.2 215.1 ]plong + s[ 553.8 553.8 553.8 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.6 553.6 553.6 + 553.6 553.6 553.6 553.6 553.6 553.6 553.5 553.5 553.5 553.5 553.5 553.5 553.5 553.5 553.5 553.5 ][ 216.1 216.1 + 216.1 216.0 216.0 216.0 215.9 215.9 215.9 215.9 215.8 215.8 215.8 215.7 215.7 215.7 215.7 215.6 215.6 215.6 + 215.5 215.5 215.5 215.4 215.4 215.4 215.3 215.3 215.3 215.3 215.2 215.2 215.2 215.1 ]plong + s[ 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.9 233.9 233.9 233.9 233.9 233.9 233.9 233.9 + 233.9 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.1 234.1 234.1 234.1 ][ 217.1 217.1 + 217.1 217.0 217.0 217.0 216.9 216.9 216.9 216.8 216.8 216.8 216.8 216.7 216.7 216.7 216.6 216.6 216.6 216.6 + 216.5 216.5 216.5 216.4 216.4 216.4 216.3 216.3 216.3 216.2 216.2 216.2 216.2 216.1 ]plong + s[ 554.1 554.1 554.1 554.1 554.1 554.1 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 553.9 + 553.9 553.9 553.9 553.9 553.9 553.9 553.9 553.9 553.8 553.8 553.8 553.8 553.8 553.8 553.8 553.8 553.8 ][ 217.1 + 217.1 217.1 217.0 217.0 217.0 217.0 216.9 216.9 216.9 216.8 216.8 216.8 216.7 216.7 216.7 216.6 216.6 216.6 + 216.6 216.5 216.5 216.5 216.4 216.4 216.4 216.4 216.3 216.3 216.3 216.2 216.2 216.2 216.1 216.1 ]plong + s[ 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.6 233.6 233.6 233.6 233.6 233.6 + 233.6 233.6 233.6 233.6 233.6 233.7 233.7 233.7 233.7 233.7 233.7 233.7 233.7 233.7 ][ 218.1 218.0 218.0 218.0 + 218.0 217.9 217.9 217.9 217.9 217.8 217.8 217.8 217.7 217.7 217.7 217.6 217.6 217.6 217.5 217.5 217.5 217.5 + 217.4 217.4 217.4 217.3 217.3 217.3 217.3 217.2 217.2 217.2 ]plong + 233.7 217.2 233.8 217.1 233.8 217.1 3 pls + 554.1 217.1 554.1 217.1 554.1 217.2 554.1 217.2 4 pls + s[ 554.4 554.4 554.4 554.4 554.4 554.4 554.4 554.4 554.4 554.4 554.3 554.3 554.3 554.3 554.3 554.3 554.3 554.3 + 554.3 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.1 ][ 218.1 218.0 218.0 218.0 218.0 + 217.9 217.9 217.9 217.9 217.8 217.8 217.8 217.7 217.7 217.7 217.7 217.6 217.6 217.6 217.5 217.5 217.5 217.4 + 217.4 217.4 217.3 217.3 217.3 217.3 217.2 217.2 ]plong + s[ 233.1 233.1 233.1 233.2 233.2 233.2 233.2 233.2 233.2 233.2 233.2 233.2 233.3 233.3 233.3 233.3 233.3 233.3 + 233.3 233.3 233.3 233.3 233.3 233.4 233.4 233.4 233.4 233.4 233.4 233.4 233.4 233.4 233.4 233.5 ][ 219.1 219.0 + 219.0 219.0 219.0 218.9 218.9 218.9 218.8 218.8 218.8 218.7 218.7 218.7 218.6 218.6 218.6 218.6 218.5 218.5 + 218.5 218.4 218.4 218.4 218.4 218.3 218.3 218.3 218.2 218.2 218.2 218.1 218.1 218.1 ]plong + s[ 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.6 554.6 554.6 554.6 554.6 554.6 + 554.6 554.6 554.6 554.6 554.6 554.5 554.5 554.5 554.5 554.5 554.5 554.5 554.5 554.5 554.4 554.4 ][ 219.1 219.0 + 219.0 219.0 219.0 218.9 218.9 218.9 218.8 218.8 218.8 218.7 218.7 218.7 218.6 218.6 218.6 218.6 218.5 218.5 + 218.5 218.4 218.4 218.4 218.4 218.3 218.3 218.3 218.2 218.2 218.2 218.1 218.1 218.1 ]plong + s[ 232.8 232.8 232.8 232.8 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 233.0 233.0 233.0 + 233.0 233.0 233.0 233.0 233.0 233.0 233.1 233.1 233.1 233.1 233.1 233.1 233.1 233.1 233.1 233.1 ][ 220.0 220.0 + 220.0 220.0 219.9 219.9 219.9 219.8 219.8 219.8 219.7 219.7 219.7 219.7 219.6 219.6 219.6 219.5 219.5 219.5 + 219.5 219.4 219.4 219.4 219.3 219.3 219.3 219.2 219.2 219.2 219.1 219.1 219.1 219.1 ]plong + s[ 555.1 555.1 555.0 555.0 555.0 555.0 555.0 555.0 555.0 555.0 555.0 555.0 554.9 554.9 554.9 554.9 554.9 554.9 + 554.9 554.9 554.9 554.9 554.9 554.8 554.8 554.8 554.8 554.8 554.8 554.8 554.8 554.8 554.7 554.7 ][ 220.0 220.0 + 220.0 220.0 219.9 219.9 219.9 219.9 219.8 219.8 219.7 219.7 219.7 219.7 219.6 219.6 219.6 219.5 219.5 219.5 + 219.5 219.4 219.4 219.4 219.3 219.3 219.3 219.2 219.2 219.2 219.1 219.1 219.1 219.1 ]plong + s[ 232.5 232.5 232.5 232.5 232.5 232.5 232.6 232.6 232.6 232.6 232.6 232.6 232.6 232.6 232.6 232.6 232.7 232.7 + 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.8 ][ 221.0 221.0 221.0 220.9 220.9 220.9 220.9 220.8 + 220.8 220.8 220.7 220.7 220.7 220.6 220.6 220.6 220.6 220.5 220.5 220.5 220.4 220.4 220.4 220.3 220.3 220.3 + 220.2 220.2 ]plong + s[ 232.8 232.8 232.8 232.8 232.8 232.8 232.8 ][ 220.2 220.2 220.2 220.1 220.1 220.1 220.0 ]plong + s[ 555.1 555.1 555.1 555.1 555.1 555.1 555.1 ][ 220.2 220.2 220.2 220.1 220.1 220.1 220.0 ]plong + s[ 555.4 555.4 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.2 555.2 555.2 555.2 + 555.2 555.2 555.2 555.2 555.2 555.2 555.1 555.1 555.1 555.1 ][ 221.0 221.0 221.0 220.9 220.9 220.9 220.9 220.8 + 220.8 220.8 220.7 220.7 220.7 220.6 220.6 220.6 220.6 220.5 220.5 220.5 220.4 220.4 220.4 220.3 220.3 220.3 + 220.2 220.2 ]plong + s[ 232.2 232.2 232.2 232.2 232.3 232.3 232.3 232.3 232.3 232.3 232.3 232.3 232.3 232.4 232.4 232.4 232.4 232.4 + 232.4 232.4 232.4 232.4 232.4 232.4 232.4 232.5 232.5 232.5 232.5 232.5 232.5 232.5 232.5 ][ 222.0 222.0 221.9 + 221.9 221.9 221.9 221.8 221.8 221.8 221.7 221.7 221.7 221.6 221.6 221.6 221.5 221.5 221.5 221.5 221.4 221.4 + 221.4 221.3 221.3 221.3 221.2 221.2 221.2 221.1 221.1 221.1 221.1 221.0 ]plong + s[ 555.7 555.7 555.7 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.5 555.5 555.5 555.5 555.5 + 555.5 555.5 555.5 555.5 555.5 555.5 555.5 555.4 555.4 555.4 555.4 555.4 555.4 555.4 555.4 ][ 222.0 222.0 221.9 + 221.9 221.9 221.9 221.8 221.8 221.8 221.7 221.7 221.7 221.6 221.6 221.6 221.5 221.5 221.5 221.5 221.4 221.4 + 221.4 221.3 221.3 221.3 221.2 221.2 221.2 221.1 221.1 221.1 221.1 221.0 ]plong + s[ 231.9 231.9 231.9 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.1 232.1 232.1 + 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.2 232.2 232.2 232.2 232.2 232.2 232.2 232.2 232.2 ][ 223.0 222.9 + 222.9 222.9 222.9 222.8 222.8 222.8 222.8 222.7 222.7 222.7 222.6 222.6 222.6 222.5 222.5 222.5 222.4 222.4 + 222.4 222.4 222.3 222.3 222.3 222.2 222.2 222.2 222.1 222.1 222.1 222.0 222.0 222.0 ]plong + s[ 556.0 555.9 555.9 555.9 555.9 555.9 555.9 555.9 555.9 555.9 555.9 555.8 555.8 555.8 555.8 555.8 555.8 555.8 + 555.8 555.8 555.8 555.8 555.8 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 ][ 223.0 223.0 + 222.9 222.9 222.9 222.8 222.8 222.8 222.8 222.7 222.7 222.7 222.6 222.6 222.6 222.5 222.5 222.5 222.4 222.4 + 222.4 222.4 222.3 222.3 222.3 222.2 222.2 222.2 222.1 222.1 222.1 222.0 222.0 222.0 ]plong + s[ 231.6 231.6 231.6 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.8 231.8 231.8 231.8 ][ 224.0 + 223.9 223.9 223.9 223.9 223.8 223.8 223.8 223.7 223.7 223.7 223.6 223.6 223.6 223.5 223.5 223.5 ]plong + s[ 231.8 231.8 231.8 231.8 231.8 231.8 231.8 231.8 231.8 231.9 231.9 231.9 231.9 231.9 231.9 231.9 231.9 ][ 223.5 + 223.5 223.4 223.4 223.3 223.3 223.3 223.3 223.2 223.2 223.2 223.1 223.1 223.1 223.1 223.0 223.0 ]plong + s[ 556.1 556.1 556.1 556.1 556.1 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 ][ 223.5 + 223.5 223.4 223.4 223.4 223.3 223.3 223.3 223.2 223.2 223.2 223.1 223.1 223.1 223.1 223.0 223.0 ]plong + s[ 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.1 556.1 556.1 556.1 556.1 556.1 ][ 224.0 + 223.9 223.9 223.9 223.9 223.8 223.8 223.8 223.7 223.7 223.7 223.6 223.6 223.6 223.5 223.5 223.5 ]plong + s[ 231.4 231.4 231.4 231.4 231.4 231.4 231.4 231.4 231.4 231.5 231.5 231.5 231.5 231.5 231.5 231.5 231.5 231.5 + 231.5 231.5 231.5 231.5 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 ][ 224.9 224.9 224.9 + 224.8 224.8 224.8 224.8 224.7 224.7 224.7 224.6 224.6 224.6 224.6 224.5 224.5 224.4 224.4 224.4 224.4 224.3 + 224.3 224.3 224.2 224.2 224.2 224.1 224.1 224.1 224.0 224.0 224.0 224.0 ]plong + s[ 556.5 556.5 556.5 556.5 556.5 556.5 556.5 556.5 556.4 556.4 556.4 556.4 556.4 556.4 556.4 556.4 556.4 556.4 + 556.4 556.4 556.4 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.2 556.2 ][ 224.9 224.9 224.9 + 224.8 224.8 224.8 224.8 224.7 224.7 224.7 224.6 224.6 224.6 224.6 224.5 224.5 224.5 224.4 224.4 224.4 224.3 + 224.3 224.3 224.2 224.2 224.2 224.2 224.1 224.1 224.0 224.0 224.0 224.0 ]plong + s[ 231.1 231.1 231.1 231.1 231.1 231.1 231.1 231.1 231.2 231.2 231.2 231.2 231.2 231.2 231.2 231.2 231.2 231.2 + 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.4 231.4 ][ 225.9 225.9 225.8 + 225.8 225.8 225.8 225.7 225.7 225.7 225.7 225.6 225.6 225.6 225.5 225.5 225.5 225.4 225.4 225.4 225.3 225.3 + 225.3 225.3 225.2 225.2 225.1 225.1 225.1 225.1 225.0 225.0 225.0 224.9 ]plong + s[ 556.8 556.8 556.8 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.6 556.6 + 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.5 556.5 556.5 ][ 225.9 225.9 225.9 + 225.8 225.8 225.8 225.7 225.7 225.7 225.7 225.6 225.6 225.6 225.5 225.5 225.5 225.4 225.4 225.4 225.3 225.3 + 225.3 225.3 225.2 225.2 225.1 225.1 225.1 225.1 225.0 225.0 225.0 224.9 ]plong + s[ 230.8 230.8 230.8 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 231.0 231.0 + 231.0 231.0 231.0 231.0 231.0 231.0 231.0 231.0 231.0 231.1 231.1 231.1 231.1 231.1 231.1 ][ 226.9 226.9 226.8 + 226.8 226.8 226.8 226.7 226.7 226.7 226.6 226.6 226.6 226.5 226.5 226.5 226.4 226.4 226.4 226.4 226.3 226.3 + 226.3 226.2 226.2 226.2 226.1 226.1 226.1 226.0 226.0 226.0 226.0 225.9 ]plong + s[ 557.1 557.1 557.0 557.0 557.0 557.0 557.0 557.0 557.0 557.0 557.0 557.0 556.9 556.9 556.9 556.9 556.9 556.9 + 556.9 556.9 556.9 556.9 556.9 556.9 556.9 556.8 556.8 556.8 556.8 556.8 556.8 556.8 556.8 ][ 226.9 226.9 226.8 + 226.8 226.8 226.8 226.7 226.7 226.7 226.6 226.6 226.6 226.6 226.5 226.5 226.4 226.4 226.4 226.4 226.3 226.3 + 226.3 226.2 226.2 226.2 226.1 226.1 226.1 226.0 226.0 226.0 226.0 225.9 ]plong + s[ 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.7 230.7 230.7 230.7 230.7 230.7 230.7 + 230.7 230.7 230.7 230.7 230.7 230.7 230.7 230.8 230.8 230.8 230.8 230.8 ][ 227.9 227.9 227.8 227.8 227.8 227.7 + 227.7 227.7 227.7 227.6 227.6 227.6 227.5 227.5 227.5 227.4 227.4 227.4 227.3 227.3 227.3 227.3 227.2 227.2 + 227.1 227.1 227.1 227.1 227.0 227.0 ]plong + 230.8 227.0 230.8 227.0 230.8 226.9 230.8 226.9 4 pls + 557.1 226.9 557.1 226.9 557.1 227.0 557.1 227.0 4 pls + s[ 557.3 557.3 557.3 557.3 557.3 557.3 557.3 557.3 557.3 557.2 557.2 557.2 557.2 557.2 557.2 557.2 557.2 557.2 + 557.2 557.1 557.1 557.1 557.1 557.1 557.1 557.1 557.1 557.1 557.1 ][ 227.9 227.8 227.8 227.8 227.7 227.7 227.7 + 227.7 227.6 227.6 227.6 227.5 227.5 227.5 227.4 227.4 227.4 227.3 227.3 227.3 227.3 227.2 227.2 227.2 227.1 + 227.1 227.1 227.0 227.0 ]plong + s[ 230.3 230.3 230.3 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.5 + 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.6 230.6 230.6 230.6 ][ 228.9 228.8 228.8 228.8 + 228.7 228.7 228.7 228.7 228.6 228.6 228.6 228.5 228.5 228.5 228.4 228.4 228.4 228.3 228.3 228.3 228.2 228.2 + 228.2 228.2 228.1 228.1 228.0 228.0 228.0 228.0 227.9 227.9 ]plong + s[ 557.6 557.6 557.6 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.4 + 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.3 557.3 557.3 557.3 557.3 557.3 ][ 228.9 228.8 228.8 + 228.8 228.7 228.7 228.7 228.7 228.6 228.6 228.6 228.5 228.5 228.5 228.4 228.4 228.4 228.3 228.3 228.3 228.2 + 228.2 228.2 228.2 228.1 228.1 228.1 228.0 228.0 228.0 227.9 227.9 227.9 ]plong + s[ 230.1 230.1 230.1 230.1 230.1 230.1 230.1 230.1 230.1 230.1 230.2 230.2 230.2 230.2 230.2 230.2 230.2 230.2 + 230.2 230.2 230.2 230.2 230.2 230.2 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 ][ 229.8 229.8 229.8 229.7 + 229.7 229.7 229.7 229.6 229.6 229.6 229.5 229.5 229.5 229.4 229.4 229.4 229.3 229.3 229.3 229.2 229.2 229.2 + 229.1 229.1 229.1 229.1 229.0 229.0 229.0 228.9 228.9 228.9 ]plong + s[ 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.7 557.7 557.7 557.7 557.7 557.7 557.7 557.7 557.7 557.7 + 557.7 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 ][ 229.8 229.8 229.8 229.7 + 229.7 229.7 229.7 229.6 229.6 229.6 229.5 229.5 229.5 229.4 229.4 229.4 229.3 229.3 229.3 229.3 229.2 229.2 + 229.1 229.1 229.1 229.1 229.0 229.0 229.0 228.9 228.9 228.9 ]plong + s[ 229.8 229.8 229.8 229.8 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 230.0 230.0 230.0 + 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.1 230.1 ][ 230.8 230.8 230.7 230.7 + 230.7 230.7 230.6 230.6 230.6 230.5 230.5 230.5 230.4 230.4 230.4 230.4 230.3 230.3 230.2 230.2 230.2 230.2 + 230.1 230.1 230.1 230.0 230.0 230.0 229.9 229.9 229.9 229.8 ]plong + s[ 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 557.9 557.9 557.9 557.9 + 557.9 557.9 557.9 557.9 557.9 557.9 557.9 557.8 557.8 557.8 557.8 557.8 557.8 557.8 ][ 230.8 230.8 230.7 230.7 + 230.7 230.7 230.6 230.6 230.6 230.5 230.5 230.5 230.4 230.4 230.4 230.4 230.3 230.3 230.2 230.2 230.2 230.2 + 230.1 230.1 230.1 230.0 230.0 230.0 229.9 229.9 229.9 229.8 ]plong + s[ 229.6 229.6 229.6 229.6 229.6 229.6 229.6 229.6 229.6 229.7 229.7 229.7 229.7 229.7 229.7 229.7 229.7 229.7 + 229.7 229.7 229.7 229.8 229.8 229.8 229.8 229.8 229.8 229.8 229.8 229.8 229.8 ][ 231.8 231.8 231.7 231.7 231.7 + 231.6 231.6 231.6 231.5 231.5 231.5 231.5 231.4 231.4 231.3 231.3 231.3 231.3 231.2 231.2 231.2 231.1 231.1 + 231.1 231.0 231.0 231.0 230.9 230.9 230.9 230.8 ]plong + 229.8 230.8 229.8 230.8 2 pls + 558.0 230.8 558.1 230.8 2 pls + s[ 558.3 558.3 558.3 558.3 558.3 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.2 + 558.2 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 ][ 231.8 231.8 231.7 231.7 231.7 + 231.6 231.6 231.6 231.5 231.5 231.5 231.5 231.4 231.4 231.4 231.3 231.3 231.3 231.2 231.2 231.2 231.1 231.1 + 231.1 231.0 231.0 231.0 230.9 230.9 230.9 230.8 ]plong + s[ 229.4 229.4 229.4 229.4 229.4 229.4 229.4 229.4 229.4 229.4 229.5 229.5 229.5 229.5 229.5 229.5 229.5 229.5 + 229.5 229.5 229.5 229.5 229.5 229.5 229.5 229.6 229.6 229.6 229.6 229.6 229.6 229.6 ][ 232.8 232.7 232.7 232.7 + 232.7 232.6 232.6 232.6 232.5 232.5 232.5 232.4 232.4 232.4 232.3 232.3 232.3 232.2 232.2 232.2 232.2 232.1 + 232.1 232.0 232.0 232.0 232.0 231.9 231.9 231.8 231.8 231.8 ]plong + s[ 558.5 558.5 558.5 558.5 558.5 558.5 558.5 558.5 558.5 558.4 558.4 558.4 558.4 558.4 558.4 558.4 558.4 558.4 + 558.4 558.4 558.4 558.4 558.4 558.4 558.3 558.3 558.3 558.3 558.3 558.3 558.3 558.3 ][ 232.8 232.7 232.7 232.7 + 232.7 232.6 232.6 232.6 232.5 232.5 232.5 232.4 232.4 232.4 232.4 232.3 232.3 232.2 232.2 232.2 232.2 232.1 + 232.1 232.0 232.0 232.0 232.0 231.9 231.9 231.9 231.8 231.8 ]plong + s[ 229.1 229.1 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.3 229.3 229.3 229.3 + 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.4 229.4 229.4 ][ 233.8 233.7 233.7 233.7 + 233.6 233.6 233.6 233.6 233.5 233.5 233.5 233.4 233.4 233.4 233.3 233.3 233.3 233.2 233.2 233.2 233.1 233.1 + 233.1 233.0 233.0 233.0 232.9 232.9 232.9 232.8 232.8 232.8 ]plong + s[ 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.6 558.6 558.6 558.6 558.6 558.6 + 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.5 558.5 558.5 558.5 ][ 233.8 233.7 233.7 233.7 + 233.6 233.6 233.6 233.6 233.5 233.5 233.5 233.4 233.4 233.4 233.3 233.3 233.3 233.2 233.2 233.2 233.1 233.1 + 233.1 233.0 233.0 233.0 232.9 232.9 232.9 232.9 232.8 232.8 ]plong + s[ 228.9 228.9 228.9 228.9 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.1 229.1 + 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 ][ 234.7 234.7 234.7 234.6 234.6 + 234.6 234.5 234.5 234.5 234.4 234.4 234.4 234.4 234.3 234.3 234.3 234.2 234.2 234.2 234.1 234.1 234.1 234.0 + 234.0 234.0 233.9 233.9 233.9 233.8 233.8 233.8 ]plong + s[ 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.8 558.8 558.8 + 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.7 558.7 558.7 558.7 ][ 234.7 234.7 234.7 234.6 234.6 + 234.6 234.5 234.5 234.5 234.5 234.4 234.4 234.4 234.3 234.3 234.3 234.2 234.2 234.2 234.1 234.1 234.1 234.0 + 234.0 234.0 233.9 233.9 233.9 233.8 233.8 233.8 ]plong + s[ 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 + 228.8 ][ 235.7 235.7 235.7 235.6 235.6 235.6 235.5 235.5 235.5 235.5 235.4 235.4 235.3 235.3 235.3 235.3 235.2 + 235.2 235.2 ]plong + s[ 228.8 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 ][ 235.2 235.1 235.1 235.1 + 235.0 235.0 235.0 234.9 234.9 234.9 234.8 234.8 234.8 234.7 ]plong + s[ 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 558.9 558.9 ][ 235.2 235.1 235.1 235.1 + 235.0 235.0 235.0 234.9 234.9 234.9 234.8 234.8 234.8 234.7 ]plong + s[ 559.2 559.2 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 + 559.0 ][ 235.7 235.7 235.7 235.6 235.6 235.6 235.5 235.5 235.5 235.5 235.4 235.4 235.4 235.3 235.3 235.3 235.2 + 235.2 235.2 ]plong + s[ 228.5 228.5 228.5 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 + 228.6 228.6 228.6 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.7 ][ 236.7 236.7 236.6 236.6 236.6 + 236.5 236.5 236.5 236.5 236.4 236.4 236.4 236.3 236.3 236.3 236.2 236.2 236.2 236.1 236.1 236.1 236.0 236.0 + 236.0 235.9 235.9 235.9 235.8 235.8 235.8 235.7 ]plong + s[ 559.4 559.4 559.4 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 + 559.3 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 ][ 236.7 236.7 236.6 236.6 236.6 + 236.5 236.5 236.5 236.5 236.4 236.4 236.4 236.3 236.3 236.3 236.2 236.2 236.2 236.1 236.1 236.1 236.0 236.0 + 236.0 235.9 235.9 235.9 235.8 235.8 235.8 235.7 ]plong + s[ 228.3 228.3 228.3 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 + 228.4 228.4 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 ][ 237.7 237.6 237.6 237.6 237.6 + 237.5 237.5 237.4 237.4 237.4 237.4 237.3 237.3 237.3 237.2 237.2 237.2 237.1 237.1 237.1 237.0 237.0 237.0 + 236.9 236.9 236.9 236.8 236.8 236.8 236.7 236.7 ]plong + s[ 559.6 559.6 559.6 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 + 559.5 559.5 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 ][ 237.7 237.6 237.6 237.6 237.6 + 237.5 237.5 237.4 237.4 237.4 237.4 237.3 237.3 237.3 237.2 237.2 237.2 237.1 237.1 237.1 237.0 237.0 237.0 + 236.9 236.9 236.9 236.8 236.8 236.8 236.7 236.7 ]plong + s[ 228.1 228.1 228.1 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 + 228.2 228.2 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 ][ 238.7 238.6 238.6 238.6 238.5 + 238.5 238.5 238.4 238.4 238.4 238.4 238.3 238.3 238.2 238.2 238.2 238.2 238.1 238.1 238.0 238.0 238.0 238.0 + 237.9 237.9 237.8 237.8 237.8 237.8 237.7 237.7 ]plong + s[ 559.8 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.6 559.6 559.6 559.6 + 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 ][ 238.7 238.6 238.6 238.6 238.5 + 238.5 238.5 238.4 238.4 238.4 238.4 238.3 238.3 238.3 238.2 238.2 238.2 238.1 238.1 238.0 238.0 238.0 238.0 + 237.9 237.9 237.8 237.8 237.8 237.8 237.7 237.7 ]plong + s[ 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 + 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 ][ 239.6 239.6 239.6 239.5 239.5 239.4 + 239.4 239.4 239.4 239.3 239.3 239.3 239.2 239.2 239.2 239.1 239.1 239.1 239.0 239.0 239.0 238.9 238.9 238.9 + 238.8 238.8 238.8 238.7 238.7 238.7 ]plong + s[ 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.8 559.8 559.8 559.8 559.8 559.8 + 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 ][ 239.6 239.6 239.6 239.5 239.5 239.5 + 239.4 239.4 239.4 239.3 239.3 239.3 239.3 239.2 239.2 239.1 239.1 239.1 239.0 239.0 239.0 238.9 238.9 238.9 + 238.8 238.8 238.8 238.7 238.7 238.7 ]plong + s[ 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.9 ][ 240.6 + 240.6 240.5 240.5 240.5 240.5 240.4 240.4 240.3 240.3 240.3 240.3 240.2 240.2 240.2 240.1 240.1 ]plong + s[ 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 228.0 ][ 240.1 240.1 240.0 + 240.0 240.0 239.9 239.9 239.9 239.8 239.8 239.8 239.7 239.7 239.7 239.6 ]plong + s[ 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 559.9 559.9 ][ 240.1 240.1 240.0 + 240.0 240.0 239.9 239.9 239.9 239.8 239.8 239.8 239.7 239.7 239.7 239.6 ]plong + s[ 560.1 560.1 560.1 560.1 560.1 560.1 560.1 560.1 560.1 560.1 560.0 560.0 560.0 560.0 560.0 560.0 560.0 ][ 240.6 + 240.6 240.5 240.5 240.5 240.5 240.4 240.4 240.3 240.3 240.3 240.3 240.2 240.2 240.2 240.1 240.1 ]plong + s[ 227.6 227.6 227.6 227.6 227.6 227.6 227.6 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 + 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.8 227.8 227.8 ][ 241.6 241.6 241.5 241.5 241.4 241.4 + 241.4 241.4 241.3 241.3 241.3 241.2 241.2 241.2 241.1 241.1 241.1 241.0 241.0 241.0 240.9 240.9 240.9 240.8 + 240.8 240.7 240.7 240.7 240.7 240.6 ]plong + s[ 560.3 560.3 560.3 560.3 560.3 560.3 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.1 560.1 560.1 560.1 560.1 ][ 241.6 241.6 241.5 241.5 241.4 241.4 + 241.4 241.4 241.3 241.3 241.3 241.2 241.2 241.2 241.1 241.1 241.1 241.0 241.0 241.0 240.9 240.9 240.9 240.8 + 240.8 240.8 240.7 240.7 240.7 240.6 ]plong + s[ 227.4 227.4 227.4 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 + 227.5 227.5 227.5 227.5 227.6 227.6 227.6 227.6 227.6 227.6 227.6 227.6 227.6 ][ 242.6 242.5 242.5 242.5 242.5 + 242.4 242.4 242.3 242.3 242.3 242.3 242.2 242.2 242.2 242.1 242.1 242.1 242.0 242.0 242.0 241.9 241.9 241.9 + 241.8 241.8 241.8 241.7 241.7 241.6 241.6 241.6 ]plong + s[ 560.5 560.5 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 + 560.4 560.4 560.4 560.4 560.3 560.3 560.3 560.3 560.3 560.3 560.3 560.3 560.3 ][ 242.6 242.6 242.5 242.5 242.5 + 242.4 242.4 242.3 242.3 242.3 242.3 242.2 242.2 242.2 242.1 242.1 242.1 242.0 242.0 242.0 241.9 241.9 241.9 + 241.8 241.8 241.8 241.7 241.7 241.6 241.6 241.6 ]plong + s[ 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.4 + 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 ][ 243.6 243.5 243.5 243.5 243.4 243.4 + 243.4 243.3 243.3 243.3 243.2 243.2 243.2 243.1 243.1 243.1 243.0 243.0 243.0 242.9 242.9 242.9 242.8 242.8 + 242.8 242.7 242.7 242.7 242.6 242.6 ]plong + s[ 560.6 560.6 560.6 560.6 560.6 560.6 560.6 560.6 560.6 560.6 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 + 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 ][ 243.6 243.5 243.5 243.5 243.4 243.4 + 243.4 243.3 243.3 243.3 243.3 243.2 243.2 243.1 243.1 243.1 243.0 243.0 243.0 242.9 242.9 242.9 242.8 242.8 + 242.8 242.7 242.7 242.7 242.6 242.6 ]plong + s[ 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.2 + 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.3 227.3 227.3 227.3 227.3 ][ 244.5 244.5 244.5 244.5 244.4 244.4 + 244.3 244.3 244.3 244.2 244.2 244.2 244.2 244.1 244.1 244.0 244.0 244.0 244.0 243.9 243.9 243.8 243.8 243.8 + 243.7 243.7 243.7 243.6 243.6 243.6 ]plong + s[ 560.8 560.8 560.8 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 + 560.7 560.7 560.7 560.7 560.7 560.7 560.6 560.6 560.6 560.6 560.6 560.6 ][ 244.5 244.5 244.5 244.5 244.4 244.4 + 244.3 244.3 244.3 244.2 244.2 244.2 244.2 244.1 244.1 244.0 244.0 244.0 244.0 243.9 243.9 243.8 243.8 243.8 + 243.7 243.7 243.7 243.6 243.6 243.6 ]plong + s[ 226.9 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 + 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 ][ 245.5 245.5 245.5 245.4 245.4 245.4 + 245.3 245.3 245.2 245.2 245.2 245.2 245.1 245.1 245.1 245.0 245.0 245.0 244.9 244.9 244.9 244.8 244.8 244.7 + 244.7 244.7 244.7 244.6 244.6 244.5 ]plong + s[ 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.8 560.8 560.8 + 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 ][ 245.5 245.5 245.4 245.4 245.4 245.3 245.3 + 245.3 245.2 245.2 245.2 245.1 245.1 245.1 245.0 245.0 245.0 244.9 244.9 244.9 244.8 244.8 244.8 244.7 244.7 + 244.7 244.6 244.6 244.5 ]plong + s[ 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 ][ 246.5 246.5 246.4 + 246.4 246.3 246.3 246.3 246.3 246.2 246.2 246.2 246.1 246.1 246.0 246.0 ]plong + s[ 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 ][ 246.0 246.0 246.0 + 245.9 245.9 245.8 245.8 245.8 245.7 245.7 245.7 245.6 245.6 245.6 245.5 ]plong + s[ 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 ][ 246.1 + 246.0 246.0 246.0 245.9 245.9 245.8 245.8 245.8 245.8 245.7 245.7 245.6 245.6 245.6 245.5 245.5 ]plong + s[ 561.1 561.1 561.1 561.1 561.1 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 ][ 246.5 246.5 246.4 246.4 + 246.3 246.3 246.3 246.3 246.2 246.2 246.2 246.1 246.1 246.1 ]plong + s[ 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 + 226.7 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 ][ 247.5 247.4 247.4 247.4 247.4 247.3 + 247.3 247.2 247.2 247.2 247.1 247.1 247.1 247.1 247.0 247.0 246.9 246.9 246.9 246.8 246.8 246.8 246.7 246.7 + 246.7 246.6 246.6 246.6 246.5 246.5 ]plong + s[ 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.1 561.1 561.1 561.1 561.1 561.1 561.1 + 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 ][ 247.5 247.4 247.4 247.4 247.4 247.3 + 247.3 247.2 247.2 247.2 247.1 247.1 247.1 247.1 247.0 247.0 246.9 246.9 246.9 246.9 246.8 246.8 246.7 246.7 + 246.7 246.6 246.6 246.6 246.5 246.5 ]plong + s[ 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 + 226.6 226.6 226.6 226.6 226.7 226.7 226.7 226.7 226.7 226.7 226.7 ][ 248.5 248.4 248.4 248.3 248.3 248.3 248.2 + 248.2 248.2 248.1 248.1 248.1 248.0 248.0 248.0 248.0 247.9 247.9 247.8 247.8 247.8 247.7 247.7 247.7 247.6 + 247.6 247.6 247.5 247.5 ]plong + s[ 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 + 561.3 561.3 561.3 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 ][ 248.5 248.4 248.4 248.3 248.3 248.3 248.3 + 248.2 248.2 248.1 248.1 248.1 248.0 248.0 248.0 248.0 247.9 247.9 247.8 247.8 247.8 247.7 247.7 247.7 247.6 + 247.6 247.6 247.5 247.5 ]plong + s[ 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 + 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.6 226.6 226.6 ][ 249.4 249.4 249.4 249.3 249.3 249.2 249.2 + 249.2 249.1 249.1 249.1 249.1 249.0 249.0 248.9 248.9 248.9 248.9 248.8 248.8 248.7 248.7 248.7 248.6 248.6 + 248.6 248.5 248.5 248.5 ]plong + s[ 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 + 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.3 561.3 561.3 561.3 ][ 249.4 249.4 249.4 249.3 249.3 249.2 249.2 + 249.2 249.2 249.1 249.1 249.1 249.0 249.0 248.9 248.9 248.9 248.9 248.8 248.8 248.7 248.7 248.7 248.6 248.6 + 248.6 248.5 248.5 248.5 ]plong + s[ 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 + 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 ][ 250.4 250.4 250.3 250.3 250.3 250.2 250.2 + 250.1 250.1 250.1 250.1 250.0 250.0 250.0 249.9 249.9 249.8 249.8 249.8 249.8 249.7 249.7 249.6 249.6 249.6 + 249.5 249.5 249.5 249.4 ]plong + s[ 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 + 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 ][ 250.4 250.4 250.3 250.3 250.3 250.2 250.2 + 250.2 250.1 250.1 250.1 250.0 250.0 250.0 249.9 249.9 249.8 249.8 249.8 249.8 249.7 249.7 249.6 249.6 249.6 + 249.5 249.5 249.5 249.4 ]plong + s[ 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 + 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 ][ 251.4 251.4 251.3 251.3 251.2 251.2 251.2 + 251.1 251.1 251.1 251.0 251.0 251.0 250.9 250.9 250.9 250.8 250.8 250.8 250.7 250.7 250.7 250.6 250.6 250.5 + 250.5 250.5 250.4 250.4 ]plong + s[ 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 + 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 ][ 251.4 251.4 251.3 251.3 251.2 251.2 251.2 + 251.1 251.1 251.1 251.0 251.0 251.0 250.9 250.9 250.9 250.8 250.8 250.8 250.7 250.7 250.7 250.6 250.6 250.5 + 250.5 250.5 250.5 250.4 ]plong + s[ 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 + 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 ][ 252.4 252.3 252.3 252.3 252.2 252.2 252.2 + 252.1 252.1 252.1 252.0 252.0 252.0 251.9 251.9 251.8 251.8 251.8 251.8 251.7 251.7 251.6 251.6 251.6 251.5 + 251.5 251.5 251.4 251.4 ]plong + s[ 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.7 561.7 561.7 + 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 ][ 252.4 252.3 252.3 252.3 252.2 252.2 252.2 + 252.1 252.1 252.1 252.0 252.0 252.0 251.9 251.9 251.9 251.8 251.8 251.8 251.7 251.7 251.6 251.6 251.6 251.5 + 251.5 251.5 251.4 251.4 ]plong + s[ 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 + 226.0 226.0 226.0 226.0 226.0 226.0 226.1 226.1 226.1 226.1 226.1 ][ 253.4 253.3 253.3 253.3 253.2 253.2 253.2 + 253.1 253.1 253.0 253.0 253.0 252.9 252.9 252.9 252.9 252.8 252.8 252.7 252.7 252.7 252.6 252.6 252.5 252.5 + 252.5 252.5 252.4 252.4 ]plong + s[ 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.8 561.8 561.8 561.8 + 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 ][ 253.4 253.3 253.3 253.3 253.2 253.2 253.2 + 253.1 253.1 253.0 253.0 253.0 252.9 252.9 252.9 252.9 252.8 252.8 252.7 252.7 252.7 252.6 252.6 252.6 252.5 + 252.5 252.5 252.4 252.4 ]plong + s[ 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 ][ 254.3 254.3 254.3 254.2 254.2 254.1 254.1 254.1 254.0 +]plong + s[ 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 226.0 226.0 226.0 226.0 226.0 226.0 + 226.0 226.0 ][ 254.0 254.0 254.0 253.9 253.9 253.9 253.8 253.8 253.8 253.7 253.7 253.7 253.6 253.6 253.6 253.5 + 253.5 253.4 253.4 253.4 ]plong + s[ 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 561.9 561.9 561.9 561.9 561.9 561.9 561.9 + 561.9 561.9 561.9 ][ 254.1 254.1 254.0 254.0 253.9 253.9 253.9 253.8 253.8 253.8 253.7 253.7 253.7 253.6 253.6 + 253.6 253.5 253.5 253.4 253.4 253.4 ]plong + s[ 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 ][ 254.3 254.3 254.3 254.2 254.2 254.1 254.1 254.1 ]plong + s[ 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 + 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.9 225.9 225.9 225.9 ][ 255.3 255.3 255.3 255.2 255.2 255.2 255.1 + 255.1 255.0 255.0 255.0 254.9 254.9 254.9 254.9 254.8 254.8 254.7 254.7 254.7 254.6 254.6 254.5 254.5 254.5 + 254.4 254.4 254.4 254.3 ]plong + s[ 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.1 562.1 + 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 ][ 254.3 254.4 254.4 254.5 254.5 254.5 254.5 254.6 + 254.6 254.7 254.7 254.7 254.8 254.8 254.9 254.9 254.9 254.9 255.0 255.0 255.0 255.1 255.1 255.2 255.2 255.2 + 255.3 255.3 ]plong + s[ 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.8 + 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 ][ 256.3 256.3 256.2 256.2 256.2 256.1 256.1 256.1 + 256.0 256.0 255.9 255.9 255.9 255.8 255.8 255.8 255.7 255.7 255.7 255.6 255.6 255.6 255.5 255.5 255.4 255.4 + 255.4 255.3 ]plong + s[ 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.1 562.1 562.1 + 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 ][ 256.3 256.3 256.2 256.2 256.2 256.1 256.1 + 256.1 256.0 256.0 255.9 255.9 255.9 255.8 255.8 255.8 255.8 255.7 255.7 255.6 255.6 255.6 255.5 255.5 255.4 + 255.4 255.4 255.3 255.3 ]plong + s[ 225.6 225.6 225.6 225.6 225.6 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 + 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 ][ 257.3 257.2 257.2 257.2 257.1 257.1 257.1 257.0 + 257.0 257.0 256.9 256.9 256.8 256.8 256.8 256.7 256.7 256.7 256.6 256.6 256.6 256.5 256.5 256.5 256.4 256.4 + 256.3 256.3 ]plong + s[ 562.3 562.3 562.3 562.3 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 + 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 ][ 257.3 257.2 257.2 257.2 257.2 257.1 257.1 257.0 + 257.0 257.0 256.9 256.9 256.8 256.8 256.8 256.7 256.7 256.7 256.7 256.6 256.6 256.5 256.5 256.5 256.4 256.4 + 256.3 256.3 ]plong + s[ 225.5 225.5 225.5 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 + 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 ][ 258.3 258.2 258.2 258.2 258.1 258.1 258.1 258.0 + 258.0 257.9 257.9 257.9 257.8 257.8 257.8 257.7 257.7 257.6 257.6 257.6 257.6 257.5 257.5 257.4 257.4 257.4 + 257.3 257.3 ]plong + s[ 562.4 562.4 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 + 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 ][ 258.3 258.2 258.2 258.2 258.1 258.1 258.1 258.0 + 258.0 257.9 257.9 257.9 257.8 257.8 257.8 257.7 257.7 257.7 257.6 257.6 257.6 257.5 257.5 257.4 257.4 257.4 + 257.3 257.3 ]plong + s[ 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 + 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 ][ 259.2 259.2 259.2 259.1 259.1 259.0 259.0 259.0 258.9 + 258.9 258.8 258.8 258.8 258.7 258.7 258.7 258.7 258.6 258.6 258.5 258.5 258.5 258.4 258.4 258.3 258.3 258.3 ]plong + s[ 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 + 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 ][ 259.2 259.2 259.2 259.1 259.1 259.0 259.0 259.0 258.9 + 258.9 258.8 258.8 258.8 258.8 258.7 258.7 258.7 258.6 258.6 258.5 258.5 258.5 258.4 258.4 258.3 258.3 258.3 ]plong + s[ 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 + 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 ][ 260.2 260.2 260.1 260.1 260.1 260.0 260.0 259.9 + 259.9 259.9 259.9 259.8 259.8 259.8 259.7 259.7 259.6 259.6 259.6 259.5 259.5 259.4 259.4 259.4 259.3 259.3 + 259.3 259.2 ]plong + s[ 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 + 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 ][ 260.2 260.2 260.1 260.1 260.1 260.0 260.0 260.0 + 259.9 259.9 259.9 259.8 259.8 259.8 259.7 259.7 259.6 259.6 259.6 259.5 259.5 259.4 259.4 259.4 259.3 259.3 + 259.3 259.2 ]plong + s[ 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 + 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 ][ 261.2 261.2 261.1 261.1 261.0 261.0 261.0 260.9 260.9 + 260.8 260.8 260.8 260.7 260.7 260.7 260.6 260.6 260.5 260.5 260.5 260.5 260.4 260.4 260.3 260.3 260.3 260.2 ]plong + s[ 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 + 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 ][ 261.2 261.2 261.1 261.1 261.0 261.0 261.0 260.9 260.9 + 260.8 260.8 260.8 260.7 260.7 260.7 260.6 260.6 260.6 260.5 260.5 260.5 260.4 260.4 260.3 260.3 260.3 260.2 ]plong + s[ 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 + 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.4 225.4 ][ 262.2 262.1 262.1 262.1 262.0 262.0 261.9 261.9 + 261.9 261.9 261.8 261.8 261.7 261.7 261.7 261.6 261.6 261.6 261.5 261.5 261.4 261.4 261.4 261.3 261.3 261.2 + 261.2 261.2 ]plong + s[ 562.6 562.6 562.6 562.6 562.6 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 + 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 ][ 262.2 262.1 262.1 262.1 262.0 262.0 262.0 261.9 + 261.9 261.9 261.8 261.8 261.7 261.7 261.7 261.6 261.6 261.6 261.5 261.5 261.4 261.4 261.4 261.3 261.3 261.3 + 261.2 261.2 ]plong + s[ 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 + 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 ][ 263.2 263.1 263.1 263.0 263.0 263.0 262.9 262.9 262.8 + 262.8 262.8 262.7 262.7 262.7 262.7 262.6 262.6 262.5 262.5 262.5 262.4 262.4 262.3 262.3 262.3 262.2 262.2 ]plong + s[ 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 + 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 ][ 263.2 263.1 263.1 263.0 263.0 263.0 262.9 262.9 262.9 + 262.8 262.8 262.8 262.7 262.7 262.7 262.6 262.6 262.5 262.5 262.5 262.4 262.4 262.3 262.3 262.3 262.2 262.2 ]plong + s[ 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.3 225.3 225.3 225.3 + 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 ][ 264.1 264.1 264.1 264.0 264.0 263.9 263.9 263.9 263.8 + 263.8 263.7 263.7 263.7 263.6 263.6 263.6 263.6 263.5 263.5 263.4 263.4 263.4 263.3 263.3 263.2 263.2 263.2 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 + 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 ][ 264.1 264.1 264.1 264.0 264.0 263.9 263.9 263.9 263.8 + 263.8 263.8 263.7 263.7 263.7 263.6 263.6 263.6 263.5 263.5 263.4 263.4 263.4 263.3 263.3 263.2 263.2 263.2 ]plong + s[ 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 + 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 ][ 265.1 265.1 265.0 265.0 265.0 264.9 264.9 264.8 264.8 + 264.8 264.7 264.7 264.7 264.6 264.6 264.6 264.5 264.5 264.5 264.4 264.4 264.3 264.3 264.3 264.2 264.2 264.1 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 265.1 265.1 265.0 265.0 265.0 264.9 264.9 264.9 264.8 + 264.8 264.8 264.7 264.7 264.6 264.6 264.6 264.5 264.5 264.5 264.4 264.4 264.3 264.3 264.3 264.2 264.2 264.1 ]plong + s[ 225.1 225.1 225.1 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 + 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 ][ 266.1 266.1 266.0 266.0 266.0 265.9 265.9 265.9 265.8 + 265.8 265.7 265.7 265.7 265.6 265.6 265.6 265.5 265.5 265.4 265.4 265.4 265.3 265.3 265.2 265.2 265.2 265.1 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 266.1 266.1 266.0 266.0 265.9 265.9 265.9 265.8 265.8 265.7 + 265.7 265.7 265.6 265.6 265.6 265.5 265.5 265.4 265.4 265.4 265.3 265.3 265.2 265.2 265.2 265.1 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 267.1 267.0 267.0 267.0 266.9 266.9 266.8 266.8 266.8 266.7 + 266.7 266.6 266.6 266.6 266.5 266.5 266.5 266.4 266.4 266.3 266.3 266.3 266.2 266.2 266.1 266.1 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 267.1 267.0 267.0 267.0 266.9 266.9 266.8 266.8 266.8 + 266.7 266.7 266.6 266.6 266.6 266.5 266.5 266.5 266.4 266.4 266.3 266.3 266.3 266.2 266.2 266.2 266.1 266.1 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 268.1 268.0 268.0 267.9 267.9 267.9 267.8 267.8 267.8 + 267.7 267.7 267.7 267.6 267.6 267.5 267.5 267.5 267.4 267.4 267.4 267.3 267.3 267.2 267.2 267.2 267.1 267.1 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 268.1 268.0 268.0 267.9 267.9 267.9 267.8 267.8 267.7 267.7 + 267.7 267.6 267.6 267.5 267.5 267.5 267.4 267.4 267.4 267.3 267.3 267.2 267.2 267.2 267.1 267.1 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 269.0 269.0 269.0 268.9 268.9 268.8 268.8 268.8 268.7 268.7 + 268.6 268.6 268.6 268.5 268.5 268.5 268.4 268.4 268.3 268.3 268.3 268.2 268.2 268.1 268.1 268.1 ]plong + s[ 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 + 562.8 562.8 562.8 562.8 562.8 562.8 562.7 562.7 562.7 ][ 269.0 269.0 269.0 268.9 268.9 268.8 268.8 268.8 268.7 + 268.7 268.6 268.6 268.6 268.5 268.5 268.5 268.4 268.4 268.3 268.3 268.3 268.2 268.2 268.2 268.1 268.1 268.1 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 270.0 270.0 269.9 269.9 269.9 269.8 269.8 269.7 269.7 269.7 + 269.6 269.6 269.5 269.5 269.5 269.4 269.4 269.4 269.3 269.3 269.2 269.2 269.2 269.1 269.1 269.0 ]plong + s[ 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 + 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 ][ 270.0 270.0 269.9 269.9 269.9 269.8 269.8 269.7 269.7 269.7 + 269.6 269.6 269.5 269.5 269.5 269.4 269.4 269.4 269.3 269.3 269.2 269.2 269.2 269.1 269.1 269.0 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 271.0 271.0 270.9 270.9 270.8 270.8 270.8 270.7 270.7 270.6 + 270.6 270.6 270.5 270.5 270.4 270.4 270.4 270.3 270.3 270.3 270.2 270.2 270.1 270.1 270.1 270.0 ]plong + s[ 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 + 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 ][ 271.0 271.0 270.9 270.9 270.8 270.8 270.8 270.7 270.7 270.6 + 270.6 270.6 270.5 270.5 270.4 270.4 270.4 270.3 270.3 270.3 270.2 270.2 270.1 270.1 270.1 270.0 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 272.0 271.9 271.9 271.9 271.8 271.8 271.7 271.7 271.7 271.6 + 271.6 271.5 271.5 271.5 271.4 271.4 271.4 271.3 271.3 271.2 271.2 271.2 271.1 271.1 271.0 271.0 ]plong + s[ 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 + 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 ][ 272.0 271.9 271.9 271.9 271.8 271.8 271.7 271.7 271.7 271.6 + 271.6 271.5 271.5 271.5 271.4 271.4 271.4 271.3 271.3 271.2 271.2 271.2 271.1 271.1 271.0 271.0 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 273.0 272.9 272.9 272.8 272.8 272.8 272.7 272.7 272.6 272.6 + 272.6 272.5 272.5 272.4 272.4 272.4 272.3 272.3 272.3 272.2 272.2 272.1 272.1 272.1 272.0 272.0 ]plong + s[ 562.7 562.7 562.7 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 + 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 562.8 ][ 273.0 272.9 272.9 272.9 272.8 272.8 272.8 272.7 272.7 + 272.6 272.6 272.6 272.5 272.5 272.4 272.4 272.4 272.3 272.3 272.3 272.2 272.2 272.1 272.1 272.1 272.0 272.0 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 273.9 273.9 273.9 273.8 273.8 273.7 273.7 273.7 273.6 + 273.6 273.5 273.5 273.5 273.4 273.4 273.3 273.3 273.3 273.2 273.2 273.2 273.2 273.1 273.1 273.0 273.0 273.0 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 273.9 273.9 273.9 273.8 273.8 273.7 273.7 273.7 273.6 273.6 + 273.5 273.5 273.5 273.4 273.4 273.3 273.3 273.3 273.2 273.2 273.2 273.1 273.1 273.0 273.0 273.0 ]plong + s[ 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 + 225.1 225.1 225.1 225.1 225.1 225.1 225.1 225.1 ][ 274.9 274.9 274.8 274.8 274.8 274.7 274.7 274.6 274.6 274.6 + 274.5 274.5 274.4 274.4 274.4 274.3 274.3 274.3 274.2 274.2 274.1 274.1 274.1 274.0 274.0 273.9 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 274.9 274.9 274.8 274.8 274.8 274.8 274.7 274.7 274.6 + 274.6 274.6 274.5 274.5 274.4 274.4 274.4 274.3 274.3 274.3 274.2 274.2 274.1 274.1 274.1 274.0 274.0 273.9 ]plong + s[ 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 + 225.2 225.2 225.2 225.2 225.2 225.2 225.1 225.1 225.1 ][ 275.9 275.9 275.8 275.8 275.7 275.7 275.7 275.6 275.6 + 275.5 275.5 275.5 275.4 275.4 275.3 275.3 275.3 275.2 275.2 275.2 275.1 275.1 275.0 275.0 275.0 275.0 274.9 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 275.9 275.9 275.8 275.8 275.7 275.7 275.7 275.6 275.6 275.5 + 275.5 275.5 275.4 275.4 275.3 275.3 275.3 275.2 275.2 275.2 275.1 275.1 275.0 275.0 275.0 274.9 ]plong + s[ 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 + 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 ][ 276.9 276.8 276.8 276.8 276.7 276.7 276.6 276.6 276.6 + 276.5 276.5 276.4 276.4 276.4 276.3 276.3 276.3 276.3 276.2 276.2 276.1 276.1 276.1 276.0 276.0 275.9 275.9 ]plong + s[ 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 276.9 276.8 276.8 276.8 276.7 276.7 276.6 276.6 276.6 + 276.5 276.5 276.4 276.4 276.4 276.3 276.3 276.3 276.2 276.2 276.2 276.1 276.1 276.1 276.0 276.0 275.9 275.9 ]plong + s[ 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.2 225.2 225.2 225.2 225.2 + 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 225.2 ][ 277.9 277.8 277.8 277.7 277.7 277.7 277.6 277.6 277.5 + 277.5 277.5 277.4 277.4 277.4 277.3 277.3 277.3 277.2 277.2 277.2 277.1 277.1 277.0 277.0 277.0 276.9 276.9 ]plong + s[ 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.7 + 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 562.7 ][ 277.9 277.8 277.8 277.7 277.7 277.7 277.6 277.6 277.5 + 277.5 277.5 277.4 277.4 277.3 277.3 277.3 277.2 277.2 277.2 277.2 277.1 277.1 277.0 277.0 277.0 276.9 276.9 ]plong + s[ 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 + 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 ][ 278.8 278.8 278.8 278.7 278.7 278.6 278.6 278.6 278.5 + 278.5 278.4 278.4 278.4 278.3 278.3 278.3 278.2 278.2 278.2 278.1 278.1 278.1 278.0 278.0 277.9 277.9 277.9 ]plong + s[ 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 + 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 562.6 ][ 278.8 278.8 278.8 278.7 278.7 278.6 278.6 278.6 278.5 + 278.5 278.4 278.4 278.4 278.3 278.3 278.2 278.2 278.2 278.2 278.1 278.1 278.1 278.0 278.0 277.9 277.9 277.9 ]plong + s[ 225.4 225.4 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 + 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 225.3 ][ 279.8 279.8 279.8 279.7 279.7 279.7 279.6 279.6 + 279.5 279.5 279.5 279.4 279.4 279.3 279.3 279.3 279.2 279.2 279.2 279.1 279.1 279.1 279.0 279.0 279.0 278.9 + 278.9 278.8 ]plong + s[ 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 + 562.5 562.5 562.5 562.5 562.5 562.6 562.6 562.6 562.6 562.6 ][ 279.8 279.8 279.7 279.7 279.7 279.7 279.6 279.6 + 279.5 279.5 279.5 279.4 279.4 279.3 279.3 279.3 279.2 279.2 279.2 279.1 279.1 279.0 279.0 279.0 279.0 278.9 + 278.9 278.8 ]plong + s[ 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 + 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 ][ 280.8 280.8 280.7 280.7 280.6 280.6 280.6 280.5 280.5 + 280.5 280.4 280.4 280.4 280.3 280.3 280.2 280.2 280.2 280.1 280.1 280.1 280.0 280.0 279.9 279.9 279.9 279.8 ]plong + s[ 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 + 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 ][ 280.8 280.8 280.7 280.7 280.6 280.6 280.6 280.5 280.5 + 280.4 280.4 280.4 280.4 280.3 280.3 280.2 280.2 280.2 280.1 280.1 280.1 280.0 280.0 279.9 279.9 279.9 279.8 ]plong + s[ 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 + 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 225.4 ][ 281.8 281.8 281.7 281.7 281.7 281.6 281.6 281.5 + 281.5 281.5 281.4 281.4 281.3 281.3 281.3 281.2 281.2 281.1 281.1 281.1 281.1 281.0 281.0 281.0 280.9 280.9 + 280.8 280.8 ]plong + s[ 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 + 562.4 562.4 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 ][ 281.8 281.7 281.7 281.7 281.7 281.6 281.6 281.5 + 281.5 281.5 281.4 281.4 281.3 281.3 281.3 281.2 281.2 281.1 281.1 281.1 281.1 281.0 281.0 281.0 280.9 280.9 + 280.8 280.8 ]plong + s[ 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 + 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 225.5 ][ 282.8 282.7 282.7 282.6 282.6 282.6 282.5 282.5 282.4 + 282.4 282.4 282.3 282.3 282.3 282.2 282.2 282.2 282.1 282.1 282.1 282.0 282.0 281.9 281.9 281.9 281.8 281.8 ]plong + s[ 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 + 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 562.4 ][ 282.8 282.7 282.7 282.6 282.6 282.6 282.5 282.5 282.4 + 282.4 282.4 282.3 282.3 282.2 282.2 282.2 282.2 282.1 282.1 282.1 282.0 282.0 281.9 281.9 281.9 281.8 281.8 ]plong + s[ 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.6 + 225.6 225.6 225.6 225.6 225.6 225.6 225.6 225.5 225.5 225.5 ][ 283.7 283.7 283.7 283.6 283.6 283.5 283.5 283.5 + 283.4 283.4 283.4 283.3 283.3 283.3 283.2 283.2 283.1 283.1 283.1 283.0 283.0 283.0 282.9 282.9 282.9 282.8 + 282.8 282.8 ]plong + s[ 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 + 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.3 562.4 ][ 283.7 283.7 283.7 283.6 283.6 283.5 283.5 283.5 + 283.4 283.4 283.3 283.3 283.3 283.3 283.2 283.2 283.1 283.1 283.1 283.0 283.0 283.0 282.9 282.9 282.8 282.8 + 282.8 282.8 ]plong + s[ 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 + 225.7 225.7 225.7 225.7 225.7 225.6 225.6 225.6 225.6 225.6 ][ 284.7 284.7 284.6 284.6 284.6 284.5 284.5 284.4 + 284.4 284.4 284.4 284.3 284.3 284.2 284.2 284.2 284.1 284.1 284.0 284.0 284.0 283.9 283.9 283.9 283.9 283.8 + 283.8 283.7 ]plong + s[ 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 + 562.2 562.2 562.2 562.2 562.2 562.2 562.3 562.3 562.3 562.3 ][ 284.7 284.7 284.6 284.6 284.6 284.5 284.5 284.4 + 284.4 284.4 284.3 284.3 284.3 284.2 284.2 284.2 284.1 284.1 284.0 284.0 284.0 283.9 283.9 283.9 283.8 283.8 + 283.8 283.7 ]plong + s[ 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.7 225.7 225.7 225.7 225.7 225.7 225.7 + 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 225.7 ][ 285.7 285.7 285.6 285.6 285.5 285.5 285.5 285.4 + 285.4 285.3 285.3 285.3 285.3 285.2 285.2 285.1 285.1 285.1 285.0 285.0 285.0 284.9 284.9 284.9 284.8 284.8 + 284.8 284.7 ]plong + s[ 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.2 562.2 562.2 562.2 + 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 562.2 ][ 285.7 285.7 285.7 285.6 285.6 285.5 285.5 + 285.5 285.4 285.4 285.3 285.3 285.3 285.2 285.2 285.2 285.1 285.1 285.1 285.0 285.0 285.0 284.9 284.9 284.8 + 284.8 284.8 284.8 284.7 ]plong + s[ 225.9 225.9 225.9 225.9 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 + 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 225.8 ][ 286.7 286.6 286.6 286.6 286.6 286.5 286.5 + 286.4 286.4 286.4 286.3 286.3 286.2 286.2 286.2 286.1 286.1 286.1 286.0 286.0 286.0 285.9 285.9 285.9 285.8 + 285.8 285.7 285.7 285.7 ]plong + s[ 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.1 562.1 + 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 562.1 ][ 286.7 286.6 286.6 286.6 286.5 286.5 286.5 286.4 + 286.4 286.4 286.3 286.3 286.2 286.2 286.2 286.1 286.1 286.1 286.0 286.0 286.0 285.9 285.9 285.9 285.8 285.8 + 285.7 285.7 ]plong + s[ 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 ][ 287.0 286.9 286.9 286.9 286.8 286.8 286.8 286.7 286.7 +]plong + s[ 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 225.9 + 225.9 225.9 ][ 287.7 287.6 287.6 287.5 287.5 287.5 287.4 287.4 287.4 287.3 287.3 287.3 287.2 287.2 287.1 287.1 + 287.1 287.0 287.0 287.0 ]plong + s[ 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 + 562.0 562.0 562.0 ][ 287.7 287.6 287.6 287.5 287.5 287.5 287.4 287.4 287.3 287.3 287.3 287.3 287.2 287.2 287.1 + 287.1 287.1 287.0 287.0 286.9 286.9 ]plong + s[ 562.0 562.0 562.0 562.0 562.0 562.0 562.0 562.0 ][ 286.7 286.7 286.8 286.8 286.8 286.9 286.9 286.9 ]plong + s[ 226.1 226.1 226.1 226.1 226.1 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 + 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 226.0 ][ 288.6 288.6 288.6 288.5 288.5 288.5 288.4 + 288.4 288.4 288.3 288.3 288.2 288.2 288.2 288.1 288.1 288.1 288.0 288.0 288.0 287.9 287.9 287.9 287.8 287.8 + 287.8 287.7 287.7 287.7 ]plong + s[ 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.9 561.9 561.9 + 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 561.9 ][ 288.6 288.6 288.6 288.5 288.5 288.5 288.4 + 288.4 288.4 288.3 288.3 288.2 288.2 288.2 288.1 288.1 288.1 288.0 288.0 288.0 287.9 287.9 287.9 287.8 287.8 + 287.7 287.7 287.7 287.7 ]plong + s[ 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.1 226.1 226.1 226.1 226.1 226.1 + 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 226.1 ][ 289.6 289.6 289.6 289.5 289.5 289.5 289.4 + 289.4 289.3 289.3 289.3 289.2 289.2 289.2 289.1 289.1 289.1 289.0 289.0 288.9 288.9 288.9 288.9 288.8 288.8 + 288.8 288.7 288.7 288.6 ]plong + s[ 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.8 561.8 561.8 561.8 + 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 561.8 ][ 289.6 289.6 289.5 289.5 289.5 289.5 289.4 + 289.4 289.3 289.3 289.3 289.2 289.2 289.2 289.1 289.1 289.1 289.0 289.0 288.9 288.9 288.9 288.8 288.8 288.8 + 288.8 288.7 288.7 288.6 ]plong + s[ 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.2 226.2 226.2 226.2 226.2 226.2 226.2 + 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 226.2 ][ 290.6 290.6 290.6 290.5 290.5 290.4 290.4 + 290.4 290.3 290.3 290.3 290.2 290.2 290.2 290.1 290.1 290.0 290.0 290.0 289.9 289.9 289.9 289.8 289.8 289.8 + 289.7 289.7 289.7 289.6 ]plong + s[ 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 561.6 + 561.6 561.6 561.6 561.7 561.7 561.7 561.7 561.7 561.7 561.7 561.7 ][ 290.6 290.6 290.5 290.5 290.5 290.4 290.4 + 290.4 290.3 290.3 290.2 290.2 290.2 290.2 290.1 290.1 290.0 290.0 290.0 289.9 289.9 289.9 289.8 289.8 289.8 + 289.7 289.7 289.7 289.6 ]plong + s[ 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 226.4 + 226.4 226.4 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 226.3 ][ 291.6 291.5 291.5 291.5 291.5 291.4 291.4 + 291.3 291.3 291.3 291.2 291.2 291.2 291.1 291.1 291.1 291.0 291.0 290.9 290.9 290.9 290.9 290.8 290.8 290.8 + 290.7 290.7 290.6 290.6 ]plong + s[ 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 561.5 + 561.5 561.5 561.5 561.5 561.6 561.6 561.6 561.6 561.6 561.6 561.6 ][ 291.6 291.5 291.5 291.5 291.5 291.4 291.4 + 291.3 291.3 291.3 291.2 291.2 291.2 291.1 291.1 291.1 291.0 291.0 290.9 290.9 290.9 290.8 290.8 290.8 290.8 + 290.7 290.7 290.6 290.6 ]plong + s[ 226.6 226.6 226.6 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 226.5 + 226.5 226.5 226.5 226.5 226.4 226.4 226.4 226.4 226.4 226.4 226.4 ][ 292.6 292.5 292.5 292.5 292.4 292.4 292.4 + 292.3 292.3 292.2 292.2 292.2 292.1 292.1 292.1 292.0 292.0 292.0 291.9 291.9 291.8 291.8 291.8 291.8 291.7 + 291.7 291.7 291.6 291.6 ]plong + s[ 561.3 561.3 561.3 561.3 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 561.4 + 561.4 561.4 561.4 561.4 561.4 561.5 561.5 561.5 561.5 561.5 561.5 ][ 292.6 292.5 292.5 292.4 292.4 292.4 292.4 + 292.3 292.3 292.2 292.2 292.2 292.1 292.1 292.1 292.0 292.0 292.0 291.9 291.9 291.8 291.8 291.8 291.8 291.7 + 291.7 291.7 291.6 291.6 ]plong + s[ 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 + 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 226.6 ][ 293.5 293.5 293.5 293.4 293.4 293.3 293.3 + 293.3 293.3 293.2 293.2 293.1 293.1 293.1 293.0 293.0 293.0 292.9 292.9 292.9 292.8 292.8 292.8 292.7 292.7 + 292.7 292.6 292.6 292.6 ]plong + s[ 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 + 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 561.3 ][ 293.5 293.5 293.5 293.4 293.4 293.3 293.3 + 293.3 293.3 293.2 293.2 293.1 293.1 293.1 293.0 293.0 293.0 292.9 292.9 292.9 292.8 292.8 292.8 292.7 292.7 + 292.7 292.6 292.6 292.6 ]plong + s[ 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.8 226.7 226.7 226.7 226.7 226.7 226.7 226.7 + 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 226.7 ][ 294.5 294.5 294.5 294.4 294.4 294.4 + 294.3 294.3 294.2 294.2 294.2 294.2 294.1 294.1 294.0 294.0 294.0 293.9 293.9 293.9 293.8 293.8 293.8 293.7 + 293.7 293.7 293.6 293.6 293.6 293.5 ]plong + s[ 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 561.1 + 561.1 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 561.2 ][ 294.5 294.5 294.4 294.4 294.4 294.4 + 294.3 294.3 294.2 294.2 294.2 294.1 294.1 294.1 294.0 294.0 294.0 293.9 293.9 293.9 293.8 293.8 293.8 293.7 + 293.7 293.7 293.6 293.6 293.6 293.5 ]plong + s[ 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.8 226.8 226.8 226.8 226.8 226.8 226.8 ][ 295.0 295.0 294.9 + 294.9 294.9 294.8 294.8 294.7 294.7 294.7 294.7 294.6 294.6 294.6 294.5 ]plong + s[ 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 226.9 ][ 295.5 295.5 295.4 + 295.4 295.3 295.3 295.3 295.2 295.2 295.2 295.1 295.1 295.1 295.0 295.0 ]plong + s[ 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 ][ 295.5 + 295.5 295.5 295.4 295.4 295.3 295.3 295.3 295.2 295.2 295.2 295.1 295.1 295.1 295.0 295.0 295.0 ]plong + s[ 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.0 561.1 561.1 561.1 561.1 561.1 ][ 295.0 294.9 294.9 294.9 + 294.8 294.8 294.7 294.7 294.7 294.7 294.6 294.6 294.6 294.5 ]plong + s[ 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.0 227.0 227.0 227.0 227.0 + 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 227.0 226.9 ][ 296.5 296.4 296.4 296.4 296.3 296.3 + 296.3 296.2 296.2 296.2 296.1 296.1 296.0 296.0 296.0 296.0 295.9 295.9 295.8 295.8 295.8 295.8 295.7 295.7 + 295.7 295.6 295.6 295.5 295.5 295.5 ]plong + s[ 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.8 560.9 560.9 560.9 560.9 + 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 560.9 ][ 296.5 296.4 296.4 296.4 296.3 296.3 296.3 + 296.2 296.2 296.2 296.1 296.1 296.0 296.0 296.0 296.0 295.9 295.9 295.8 295.8 295.8 295.7 295.7 295.7 295.7 + 295.6 295.6 295.5 295.5 ]plong + s[ 227.3 227.3 227.3 227.3 227.3 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.2 227.2 + 227.2 227.2 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 227.1 ][ 297.5 297.4 297.4 297.3 297.3 297.3 + 297.3 297.2 297.2 297.1 297.1 297.1 297.0 297.0 297.0 296.9 296.9 296.9 296.8 296.8 296.8 296.7 296.7 296.7 + 296.6 296.6 296.6 296.5 296.5 296.5 ]plong + s[ 560.6 560.6 560.6 560.6 560.6 560.6 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 + 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.7 560.8 560.8 560.8 ][ 297.5 297.4 297.4 297.3 297.3 297.3 + 297.3 297.2 297.2 297.1 297.1 297.1 297.0 297.0 297.0 296.9 296.9 296.9 296.8 296.8 296.8 296.7 296.7 296.7 + 296.6 296.6 296.6 296.5 296.5 296.5 ]plong + s[ 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.4 227.3 227.3 227.3 227.3 227.3 + 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 227.3 ][ 298.4 298.4 298.4 298.3 298.3 298.3 + 298.2 298.2 298.2 298.1 298.1 298.0 298.0 298.0 298.0 297.9 297.9 297.8 297.8 297.8 297.8 297.7 297.7 297.6 + 297.6 297.6 297.5 297.5 297.5 297.5 ]plong + s[ 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 560.5 + 560.5 560.5 560.6 560.6 560.6 560.6 560.6 560.6 560.6 560.6 560.6 560.6 ][ 298.4 298.4 298.4 298.3 298.3 298.2 + 298.2 298.2 298.2 298.1 298.1 298.0 298.0 298.0 298.0 297.9 297.9 297.8 297.8 297.8 297.7 297.7 297.7 297.6 + 297.6 297.6 297.5 297.5 297.5 297.5 ]plong + s[ 227.6 227.6 227.6 227.6 227.6 227.6 227.6 227.6 227.6 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 + 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.5 227.4 227.4 227.4 ][ 299.4 299.4 299.4 299.3 299.3 + 299.3 299.2 299.2 299.2 299.1 299.1 299.1 299.0 299.0 299.0 298.9 298.9 298.9 298.8 298.8 298.7 298.7 298.7 + 298.7 298.6 298.6 298.6 298.5 298.5 298.5 298.4 ]plong + s[ 560.3 560.3 560.3 560.3 560.3 560.3 560.3 560.3 560.3 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 + 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.4 560.5 560.5 ][ 299.4 299.4 299.4 299.3 299.3 + 299.3 299.2 299.2 299.2 299.1 299.1 299.1 299.0 299.0 298.9 298.9 298.9 298.9 298.8 298.8 298.7 298.7 298.7 + 298.7 298.6 298.6 298.6 298.5 298.5 298.5 298.4 ]plong + s[ 227.8 227.8 227.8 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 227.7 + 227.7 227.7 227.7 227.7 227.7 227.6 227.6 227.6 227.6 227.6 227.6 227.6 ][ 300.4 300.4 300.3 300.3 300.3 300.2 + 300.2 300.2 300.1 300.1 300.1 300.0 300.0 300.0 299.9 299.9 299.9 299.8 299.8 299.8 299.7 299.7 299.6 299.6 + 299.6 299.6 299.5 299.5 299.5 299.4 ]plong + s[ 560.1 560.1 560.1 560.1 560.1 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 560.2 + 560.2 560.2 560.2 560.2 560.2 560.2 560.3 560.3 560.3 560.3 560.3 560.3 ][ 300.4 300.4 300.3 300.3 300.3 300.2 + 300.2 300.2 300.1 300.1 300.0 300.0 300.0 300.0 299.9 299.9 299.8 299.8 299.8 299.8 299.7 299.7 299.6 299.6 + 299.6 299.6 299.5 299.5 299.5 299.4 ]plong + s[ 227.9 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 227.8 ][ 300.9 + 300.9 300.9 300.8 300.8 300.7 300.7 300.7 300.7 300.6 300.6 300.5 300.5 300.5 300.5 300.4 300.4 ]plong + s[ 228.0 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 227.9 ][ 301.4 301.4 301.3 + 301.3 301.3 301.2 301.2 301.2 301.1 301.1 301.1 301.0 301.0 300.9 300.9 ]plong + s[ 559.9 559.9 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.0 ][ 301.4 301.4 301.3 + 301.3 301.3 301.2 301.2 301.1 301.1 301.1 301.1 301.0 301.0 300.9 300.9 ]plong + s[ 560.0 560.0 560.0 560.0 560.0 560.0 560.0 560.1 560.1 560.1 560.1 560.1 560.1 560.1 560.1 560.1 560.1 ][ 300.9 + 300.9 300.9 300.8 300.8 300.7 300.7 300.7 300.7 300.6 300.6 300.5 300.5 300.5 300.5 300.4 300.4 ]plong + s[ 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.1 228.0 228.0 228.0 228.0 228.0 228.0 + 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 228.0 ][ 302.4 302.3 302.3 302.2 302.2 302.2 + 302.2 302.1 302.1 302.0 302.0 302.0 302.0 301.9 301.9 301.8 301.8 301.8 301.8 301.7 301.7 301.6 301.6 301.6 + 301.6 301.5 301.5 301.5 301.4 301.4 ]plong + s[ 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 559.8 + 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 559.9 ][ 302.4 302.3 302.3 302.2 302.2 302.2 + 302.2 302.1 302.1 302.0 302.0 302.0 302.0 301.9 301.9 301.8 301.8 301.8 301.7 301.7 301.7 301.6 301.6 301.6 + 301.5 301.5 301.5 301.5 301.4 301.4 ]plong + s[ 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.3 228.2 228.2 228.2 228.2 228.2 228.2 228.2 + 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.2 228.1 228.1 228.1 ][ 303.3 303.3 303.3 303.2 303.2 + 303.2 303.1 303.1 303.1 303.0 303.0 303.0 302.9 302.9 302.9 302.8 302.8 302.8 302.7 302.7 302.7 302.6 302.6 + 302.6 302.5 302.5 302.5 302.4 302.4 302.4 302.4 ]plong + s[ 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.6 559.7 + 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.7 559.8 ][ 303.3 303.3 303.3 303.2 303.2 + 303.2 303.1 303.1 303.1 303.0 303.0 303.0 302.9 302.9 302.9 302.8 302.8 302.8 302.7 302.7 302.7 302.6 302.6 + 302.6 302.5 302.5 302.5 302.4 302.4 302.4 302.4 ]plong + s[ 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.5 228.4 228.4 228.4 228.4 228.4 228.4 228.4 + 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.4 228.3 228.3 228.3 ][ 304.3 304.3 304.2 304.2 304.2 + 304.2 304.1 304.1 304.0 304.0 304.0 304.0 303.9 303.9 303.8 303.8 303.8 303.8 303.7 303.7 303.6 303.6 303.6 + 303.6 303.5 303.5 303.4 303.4 303.4 303.4 303.3 ]plong + s[ 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.4 559.5 559.5 559.5 559.5 559.5 559.5 559.5 + 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.5 559.6 559.6 559.6 ][ 304.3 304.3 304.2 304.2 304.2 + 304.1 304.1 304.1 304.0 304.0 304.0 304.0 303.9 303.9 303.8 303.8 303.8 303.8 303.7 303.7 303.6 303.6 303.6 + 303.6 303.5 303.5 303.4 303.4 303.4 303.4 303.3 ]plong + s[ 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.7 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 + 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.6 228.5 228.5 228.5 ][ 305.3 305.3 305.2 305.2 305.1 + 305.1 305.1 305.1 305.0 305.0 305.0 304.9 304.9 304.9 304.8 304.8 304.8 304.7 304.7 304.7 304.6 304.6 304.6 + 304.5 304.5 304.5 304.4 304.4 304.4 304.4 304.3 ]plong + s[ 559.1 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.2 559.3 559.3 559.3 559.3 559.3 + 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.3 559.4 559.4 ][ 305.3 305.3 305.3 305.2 + 305.2 305.1 305.1 305.1 305.1 305.0 305.0 304.9 304.9 304.9 304.9 304.8 304.8 304.7 304.7 304.7 304.7 304.6 + 304.6 304.5 304.5 304.5 304.5 304.4 304.4 304.4 304.3 304.3 ]plong + s[ 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.8 228.7 228.7 228.7 228.7 228.7 228.7 + 228.7 ][ 305.9 305.8 305.8 305.8 305.7 305.7 305.7 305.6 305.6 305.6 305.5 305.5 305.5 305.4 305.4 305.4 305.3 + 305.3 305.3 ]plong + s[ 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.9 228.8 ][ 306.3 306.2 306.2 306.2 + 306.2 306.1 306.1 306.1 306.0 306.0 306.0 305.9 305.9 305.9 ]plong + s[ 558.9 558.9 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 559.0 ][ 306.3 306.2 306.2 306.2 + 306.2 306.1 306.1 306.0 306.0 306.0 306.0 305.9 305.9 305.9 ]plong + s[ 559.0 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 559.1 ][ + 305.9 305.8 305.8 305.8 305.7 305.7 305.7 305.6 305.6 305.6 305.5 305.5 305.5 305.4 305.4 305.4 305.3 305.3 ]plong + s[ 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.1 229.0 229.0 229.0 + 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.0 229.0 228.9 228.9 228.9 228.9 ][ 307.3 307.2 307.2 307.1 307.1 + 307.1 307.1 307.0 307.0 307.0 306.9 306.9 306.9 306.8 306.8 306.8 306.7 306.7 306.7 306.6 306.6 306.6 306.5 + 306.5 306.5 306.4 306.4 306.4 306.3 306.3 306.3 ]plong + s[ 558.7 558.7 558.7 558.7 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.8 558.9 558.9 + 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 558.9 ][ 307.3 307.2 307.2 307.1 307.1 + 307.1 307.1 307.0 307.0 306.9 306.9 306.9 306.9 306.8 306.8 306.8 306.7 306.7 306.7 306.6 306.6 306.6 306.5 + 306.5 306.5 306.4 306.4 306.4 306.3 306.3 306.3 ]plong + s[ 229.4 229.4 229.4 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 229.3 + 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.2 229.1 229.1 ][ 308.2 308.2 308.2 308.2 + 308.1 308.1 308.0 308.0 308.0 308.0 307.9 307.9 307.8 307.8 307.8 307.8 307.7 307.7 307.7 307.6 307.6 307.6 + 307.5 307.5 307.5 307.4 307.4 307.4 307.3 307.3 307.3 307.3 ]plong + s[ 558.5 558.5 558.5 558.5 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 558.6 + 558.6 558.6 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 558.7 ][ 308.2 308.2 308.2 308.1 + 308.1 308.1 308.0 308.0 308.0 308.0 307.9 307.9 307.8 307.8 307.8 307.8 307.7 307.7 307.6 307.6 307.6 307.6 + 307.5 307.5 307.5 307.4 307.4 307.4 307.3 307.3 307.3 307.3 ]plong + s[ 229.6 229.6 229.6 229.6 229.6 229.6 229.6 229.6 229.5 229.5 229.5 229.5 229.5 229.5 229.5 229.5 229.5 229.5 + 229.5 229.5 229.5 229.5 229.4 229.4 229.4 229.4 229.4 229.4 229.4 229.4 229.4 229.4 ][ 309.2 309.2 309.2 309.1 + 309.1 309.1 309.0 309.0 309.0 308.9 308.9 308.9 308.8 308.8 308.8 308.7 308.7 308.7 308.7 308.6 308.6 308.5 + 308.5 308.5 308.5 308.4 308.4 308.3 308.3 308.3 308.3 308.2 ]plong + s[ 558.3 558.3 558.3 558.3 558.3 558.3 558.3 558.3 558.4 558.4 558.4 558.4 558.4 558.4 558.4 558.4 558.4 558.4 + 558.4 558.4 558.4 558.4 558.4 558.5 558.5 558.5 558.5 558.5 558.5 558.5 558.5 558.5 ][ 309.2 309.2 309.2 309.1 + 309.1 309.1 309.0 309.0 309.0 308.9 308.9 308.9 308.8 308.8 308.8 308.7 308.7 308.7 308.6 308.6 308.6 308.5 + 308.5 308.5 308.5 308.4 308.4 308.3 308.3 308.3 308.3 308.2 ]plong + s[ 229.8 229.8 229.8 229.8 229.8 229.8 229.8 229.8 229.8 229.8 229.7 229.7 229.7 229.7 229.7 229.7 229.7 229.7 + 229.7 229.7 229.7 229.7 229.6 229.6 229.6 229.6 229.6 229.6 229.6 229.6 229.6 ][ 310.2 310.2 310.1 310.1 310.0 + 310.0 310.0 310.0 309.9 309.9 309.9 309.8 309.8 309.8 309.7 309.7 309.7 309.6 309.6 309.6 309.5 309.5 309.5 + 309.4 309.4 309.4 309.3 309.3 309.3 309.2 309.2 ]plong + 229.8 310.2 229.8 310.2 2 pls + 558.1 310.1 558.0 310.2 558.0 310.2 3 pls + s[ 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.1 558.2 558.2 558.2 558.2 558.2 558.2 558.2 + 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.2 558.3 558.3 558.3 558.3 ][ 310.1 310.1 310.1 310.0 310.0 310.0 + 310.0 309.9 309.9 309.8 309.8 309.8 309.8 309.7 309.7 309.7 309.6 309.6 309.6 309.5 309.5 309.5 309.4 309.4 + 309.4 309.3 309.3 309.3 309.2 309.2 ]plong + s[ 229.8 229.8 229.8 229.8 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 229.9 230.0 230.0 230.0 + 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.0 230.1 230.1 230.1 ][ 310.2 310.2 310.3 310.3 + 310.3 310.3 310.4 310.4 310.5 310.5 310.5 310.5 310.6 310.6 310.6 310.7 310.7 310.7 310.8 310.8 310.8 310.9 + 310.9 310.9 311.0 311.0 311.0 311.1 311.1 311.1 311.1 311.2 ]plong + s[ 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.9 557.9 557.9 557.9 557.9 557.9 557.9 557.9 557.9 557.9 557.9 + 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 558.0 ][ 311.2 311.1 311.1 311.1 + 311.1 311.0 311.0 311.0 310.9 310.9 310.9 310.8 310.8 310.8 310.7 310.7 310.7 310.6 310.6 310.6 310.5 310.5 + 310.5 310.5 310.4 310.4 310.3 310.3 310.3 310.3 310.2 310.2 ]plong + s[ 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.3 230.2 230.2 230.2 230.2 230.2 230.2 230.2 230.2 230.2 + 230.2 230.2 230.2 230.2 230.2 230.1 230.1 230.1 230.1 230.1 230.1 230.1 230.1 230.1 ][ 312.2 312.1 312.1 312.1 + 312.0 312.0 312.0 311.9 311.9 311.9 311.8 311.8 311.8 311.7 311.7 311.7 311.6 311.6 311.6 311.6 311.5 311.5 + 311.4 311.4 311.4 311.4 311.3 311.3 311.3 311.2 311.2 311.2 ]plong + s[ 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.6 557.7 557.7 557.7 557.7 557.7 + 557.7 557.7 557.7 557.7 557.7 557.7 557.8 557.8 557.8 557.8 557.8 557.8 557.8 557.8 ][ 312.2 312.1 312.1 312.1 + 312.0 312.0 312.0 311.9 311.9 311.9 311.8 311.8 311.8 311.7 311.7 311.7 311.6 311.6 311.6 311.6 311.5 311.5 + 311.4 311.4 311.4 311.4 311.3 311.3 311.3 311.2 311.2 311.2 ]plong + s[ 230.6 230.6 230.6 230.6 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.5 230.4 230.4 230.4 + 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.4 230.3 230.3 230.3 ][ 313.1 313.1 313.1 313.0 + 313.0 313.0 312.9 312.9 312.9 312.8 312.8 312.8 312.7 312.7 312.7 312.7 312.6 312.6 312.5 312.5 312.5 312.5 + 312.4 312.4 312.4 312.3 312.3 312.3 312.2 312.2 312.2 312.2 ]plong + s[ 557.3 557.3 557.3 557.3 557.3 557.3 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.4 557.5 + 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.5 557.6 557.6 557.6 ][ 313.1 313.1 313.1 + 313.1 313.0 313.0 313.0 312.9 312.9 312.9 312.8 312.8 312.8 312.7 312.7 312.7 312.6 312.6 312.6 312.5 312.5 + 312.5 312.5 312.4 312.4 312.4 312.3 312.3 312.3 312.2 312.2 312.2 312.2 ]plong + s[ 230.8 230.8 230.8 230.8 230.8 230.7 230.7 230.7 230.7 230.7 230.7 230.7 230.7 230.7 230.7 230.7 230.7 230.7 + 230.7 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 230.6 ][ 314.0 314.0 314.0 313.9 313.9 313.9 + 313.8 313.8 313.8 313.7 313.7 313.7 313.6 313.6 313.6 313.6 313.5 313.5 313.4 313.4 313.4 313.4 313.3 313.3 + 313.3 313.2 313.2 313.2 313.1 313.1 ]plong + 230.8 314.0 230.8 314.0 230.8 314.1 230.8 314.1 4 pls + 557.1 314.0 557.1 314.0 557.1 314.1 557.1 314.1 4 pls + s[ 557.1 557.1 557.1 557.1 557.1 557.1 557.1 557.1 557.1 557.1 557.2 557.2 557.2 557.2 557.2 557.2 557.2 557.2 + 557.2 557.2 557.3 557.3 557.3 557.3 557.3 557.3 557.3 557.3 557.3 ][ 314.0 314.0 314.0 313.9 313.9 313.9 313.8 + 313.8 313.8 313.7 313.7 313.7 313.6 313.6 313.6 313.5 313.5 313.5 313.4 313.4 313.4 313.4 313.3 313.3 313.3 + 313.2 313.2 313.2 313.1 ]plong + s[ 230.8 230.8 230.8 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 230.9 231.0 231.0 + 231.0 231.0 231.0 231.0 231.0 231.0 231.0 231.0 231.0 231.1 231.1 231.1 231.1 231.1 231.1 ][ 314.1 314.1 314.2 + 314.2 314.2 314.3 314.3 314.3 314.3 314.4 314.4 314.4 314.5 314.5 314.5 314.6 314.6 314.6 314.7 314.7 314.7 + 314.8 314.8 314.8 314.9 314.9 314.9 314.9 315.0 315.0 315.0 315.1 315.1 ]plong + s[ 556.8 556.8 556.8 556.8 556.8 556.8 556.8 556.8 556.9 556.9 556.9 556.9 556.9 556.9 556.9 556.9 556.9 556.9 + 556.9 556.9 556.9 557.0 557.0 557.0 557.0 557.0 557.0 557.0 557.0 557.0 557.0 557.1 557.1 ][ 315.1 315.1 315.0 + 315.0 315.0 314.9 314.9 314.9 314.9 314.8 314.8 314.7 314.7 314.7 314.7 314.6 314.6 314.6 314.5 314.5 314.5 + 314.4 314.4 314.4 314.3 314.3 314.3 314.3 314.2 314.2 314.2 314.1 314.1 ]plong + s[ 231.4 231.4 231.4 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.3 231.2 231.2 231.2 + 231.2 231.2 231.2 231.2 231.2 231.2 231.2 231.2 231.1 231.1 231.1 231.1 231.1 231.1 231.1 ][ 316.1 316.0 316.0 + 316.0 316.0 315.9 315.9 315.9 315.8 315.8 315.8 315.7 315.7 315.7 315.6 315.6 315.6 315.6 315.5 315.5 315.5 + 315.4 315.4 315.4 315.3 315.3 315.3 315.2 315.2 315.2 315.2 315.1 315.1 ]plong + s[ 556.5 556.5 556.5 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.6 556.7 + 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.7 556.8 556.8 556.8 ][ 316.1 316.0 316.0 + 316.0 316.0 315.9 315.9 315.9 315.8 315.8 315.8 315.7 315.7 315.7 315.6 315.6 315.6 315.6 315.5 315.5 315.5 + 315.4 315.4 315.4 315.3 315.3 315.3 315.2 315.2 315.2 315.1 315.1 315.1 ]plong + s[ 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.6 231.5 231.5 231.5 231.5 231.5 231.5 231.5 + 231.5 231.5 231.5 231.5 231.5 231.5 231.4 231.4 231.4 231.4 231.4 231.4 231.4 231.4 231.4 ][ 317.0 317.0 317.0 + 317.0 316.9 316.9 316.9 316.9 316.8 316.8 316.7 316.7 316.7 316.7 316.6 316.6 316.6 316.5 316.5 316.5 316.4 + 316.4 316.4 316.3 316.3 316.3 316.3 316.2 316.2 316.2 316.1 316.1 316.1 ]plong + s[ 556.2 556.2 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.3 556.4 556.4 556.4 556.4 556.4 556.4 + 556.4 556.4 556.4 556.4 556.4 556.4 556.4 556.5 556.5 556.5 556.5 556.5 556.5 556.5 556.5 ][ 317.0 317.0 317.0 + 317.0 316.9 316.9 316.9 316.8 316.8 316.8 316.7 316.7 316.7 316.7 316.6 316.6 316.6 316.5 316.5 316.5 316.4 + 316.4 316.4 316.3 316.3 316.3 316.3 316.2 316.2 316.2 316.1 316.1 316.1 ]plong + s[ 231.8 231.8 231.8 231.8 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.7 231.6 231.6 231.6 ][ 317.5 + 317.5 317.5 317.4 317.4 317.4 317.4 317.3 317.3 317.3 317.2 317.2 317.2 317.1 317.1 317.1 317.0 ]plong + s[ 231.9 231.9 231.9 231.9 231.9 231.9 231.9 231.9 231.8 231.8 231.8 231.8 231.8 231.8 231.8 231.8 231.8 ][ 318.0 + 318.0 318.0 317.9 317.9 317.9 317.8 317.8 317.8 317.8 317.7 317.7 317.7 317.6 317.6 317.6 317.5 ]plong + s[ 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.0 556.1 556.1 556.1 556.1 556.1 ][ 318.0 + 318.0 318.0 317.9 317.9 317.9 317.8 317.8 317.8 317.8 317.7 317.7 317.7 317.6 317.6 317.6 317.5 ]plong + s[ 556.1 556.1 556.1 556.1 556.1 556.1 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 556.2 ][ 317.5 + 317.5 317.5 317.4 317.4 317.4 317.4 317.3 317.3 317.3 317.2 317.2 317.2 317.1 317.1 317.1 317.0 ]plong + s[ 232.2 232.2 232.2 232.2 232.2 232.2 232.2 232.2 232.2 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 232.1 + 232.1 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 232.0 231.9 231.9 231.9 ][ 319.0 319.0 + 319.0 318.9 318.9 318.9 318.9 318.8 318.8 318.8 318.7 318.7 318.7 318.6 318.6 318.6 318.5 318.5 318.5 318.5 + 318.4 318.4 318.4 318.3 318.3 318.3 318.2 318.2 318.2 318.1 318.1 318.1 318.1 318.0 ]plong + s[ 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.7 555.8 555.8 555.8 555.8 555.8 555.8 555.8 + 555.8 555.8 555.8 555.8 555.8 555.9 555.9 555.9 555.9 555.9 555.9 555.9 555.9 555.9 555.9 556.0 ][ 319.0 319.0 + 319.0 318.9 318.9 318.9 318.9 318.8 318.8 318.7 318.7 318.7 318.7 318.6 318.6 318.6 318.5 318.5 318.5 318.5 + 318.4 318.4 318.3 318.3 318.3 318.3 318.2 318.2 318.2 318.1 318.1 318.1 318.0 318.0 ]plong + s[ 232.5 232.5 232.5 232.5 232.5 232.5 232.5 232.5 232.4 232.4 232.4 232.4 232.4 232.4 232.4 232.4 232.4 232.4 + 232.4 232.4 232.3 232.3 232.3 232.3 232.3 232.3 232.3 232.3 232.3 232.2 232.2 232.2 232.2 ][ 320.0 319.9 319.9 + 319.9 319.9 319.8 319.8 319.8 319.8 319.7 319.7 319.7 319.6 319.6 319.6 319.5 319.5 319.5 319.4 319.4 319.4 + 319.4 319.3 319.3 319.3 319.2 319.2 319.2 319.1 319.1 319.1 319.0 319.0 ]plong + s[ 555.4 555.4 555.4 555.4 555.4 555.4 555.4 555.4 555.5 555.5 555.5 555.5 555.5 555.5 555.5 555.5 555.5 555.5 + 555.5 555.5 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.6 555.7 555.7 555.7 ][ 320.0 319.9 319.9 + 319.9 319.9 319.8 319.8 319.8 319.8 319.7 319.7 319.6 319.6 319.6 319.6 319.5 319.5 319.5 319.4 319.4 319.4 + 319.4 319.3 319.3 319.3 319.2 319.2 319.2 319.1 319.1 319.1 319.0 319.0 ]plong + s[ 232.8 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.7 232.6 232.6 232.6 232.6 232.6 + 232.6 232.6 232.6 232.6 232.5 232.5 232.5 232.5 232.5 232.5 ][ 320.8 320.8 320.7 320.7 320.7 320.7 320.6 320.6 + 320.6 320.5 320.5 320.5 320.4 320.4 320.4 320.3 320.3 320.3 320.3 320.2 320.2 320.2 320.1 320.1 320.1 320.0 + 320.0 320.0 ]plong + s[ 232.8 232.8 232.8 232.8 232.8 232.8 232.8 ][ 321.0 320.9 320.9 320.9 320.9 320.8 320.8 ]plong + s[ 555.1 555.1 555.1 555.1 555.1 555.1 555.1 ][ 321.0 320.9 320.9 320.9 320.9 320.8 320.8 ]plong + s[ 555.1 555.1 555.1 555.1 555.2 555.2 555.2 555.2 555.2 555.2 555.2 555.2 555.2 555.2 555.3 555.3 555.3 555.3 + 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.3 555.4 555.4 ][ 320.8 320.8 320.7 320.7 320.7 320.7 320.6 320.6 + 320.5 320.5 320.5 320.5 320.4 320.4 320.4 320.3 320.3 320.3 320.3 320.2 320.2 320.2 320.1 320.1 320.1 320.0 + 320.0 320.0 ]plong + s[ 233.1 233.1 233.1 233.1 233.1 233.1 233.1 233.1 233.1 233.1 233.0 233.0 233.0 233.0 233.0 233.0 233.0 233.0 + 233.0 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.9 232.8 232.8 232.8 232.8 ][ 321.9 321.9 + 321.9 321.9 321.8 321.8 321.8 321.8 321.7 321.7 321.7 321.6 321.6 321.6 321.5 321.5 321.5 321.4 321.4 321.4 + 321.4 321.3 321.3 321.3 321.2 321.2 321.2 321.2 321.1 321.1 321.1 321.0 321.0 321.0 ]plong + s[ 554.7 554.7 554.8 554.8 554.8 554.8 554.8 554.8 554.8 554.8 554.8 554.9 554.9 554.9 554.9 554.9 554.9 554.9 + 554.9 554.9 554.9 554.9 554.9 555.0 555.0 555.0 555.0 555.0 555.0 555.0 555.0 555.0 555.1 555.1 ][ 321.9 321.9 + 321.9 321.9 321.8 321.8 321.8 321.8 321.7 321.7 321.7 321.6 321.6 321.6 321.5 321.5 321.5 321.4 321.4 321.4 + 321.4 321.3 321.3 321.3 321.2 321.2 321.2 321.1 321.1 321.1 321.0 321.0 321.0 321.0 ]plong + s[ 233.5 233.5 233.4 233.4 233.4 233.4 233.4 233.4 233.4 233.4 233.4 233.3 233.3 233.3 233.3 233.3 233.3 233.3 + 233.3 233.3 233.3 233.3 233.2 233.2 233.2 233.2 233.2 233.2 233.2 233.2 233.2 233.1 233.1 233.1 ][ 322.9 322.9 + 322.9 322.8 322.8 322.8 322.8 322.7 322.7 322.7 322.6 322.6 322.6 322.5 322.5 322.5 322.5 322.4 322.4 322.4 + 322.3 322.3 322.3 322.3 322.2 322.2 322.2 322.1 322.1 322.1 322.0 322.0 322.0 321.9 ]plong + s[ 554.4 554.4 554.5 554.5 554.5 554.5 554.5 554.5 554.5 554.5 554.5 554.6 554.6 554.6 554.6 554.6 554.6 554.6 + 554.6 554.6 554.6 554.6 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 554.7 ][ 322.9 322.9 + 322.9 322.8 322.8 322.8 322.8 322.7 322.7 322.7 322.6 322.6 322.6 322.5 322.5 322.5 322.5 322.4 322.4 322.4 + 322.3 322.3 322.3 322.3 322.2 322.2 322.1 322.1 322.1 322.1 322.0 322.0 322.0 321.9 ]plong + s[ 233.7 233.7 233.7 233.7 233.7 233.7 233.7 233.7 233.7 233.6 233.6 233.6 233.6 233.6 233.6 233.6 233.6 233.6 + 233.6 233.6 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 233.5 ][ 323.8 323.8 323.8 323.8 + 323.7 323.7 323.7 323.6 323.6 323.6 323.6 323.5 323.5 323.5 323.4 323.4 323.4 323.4 323.3 323.3 323.3 323.2 + 323.2 323.2 323.1 323.1 323.1 323.0 323.0 323.0 323.0 322.9 ]plong + 233.7 323.8 233.8 323.9 233.8 323.9 3 pls + 554.1 323.8 554.1 323.8 554.1 323.9 554.1 323.9 4 pls + s[ 554.1 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.2 554.3 554.3 554.3 554.3 554.3 554.3 + 554.3 554.3 554.3 554.4 554.4 554.4 554.4 554.4 554.4 554.4 554.4 554.4 554.4 ][ 323.8 323.8 323.8 323.7 323.7 + 323.7 323.6 323.6 323.6 323.6 323.5 323.5 323.5 323.4 323.4 323.4 323.3 323.3 323.3 323.2 323.2 323.2 323.2 + 323.1 323.1 323.1 323.0 323.0 323.0 323.0 322.9 ]plong + s[ 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.8 233.9 233.9 233.9 233.9 233.9 233.9 233.9 233.9 + 233.9 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.0 234.1 234.1 234.1 234.1 234.1 234.1 ][ 323.9 + 323.9 324.0 324.0 324.0 324.1 324.1 324.1 324.1 324.2 324.2 324.2 324.3 324.3 324.3 324.3 324.4 324.4 324.4 + 324.5 324.5 324.5 324.6 324.6 324.6 324.7 324.7 324.7 324.7 324.8 324.8 324.8 324.8 324.9 324.9 ]plong + s[ 553.8 553.8 553.8 553.8 553.8 553.8 553.8 553.8 553.8 553.9 553.9 553.9 553.9 553.9 553.9 553.9 553.9 553.9 + 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.0 554.1 554.1 554.1 554.1 554.1 554.1 ][ 324.9 + 324.9 324.8 324.8 324.8 324.8 324.7 324.7 324.7 324.6 324.6 324.6 324.5 324.5 324.5 324.5 324.4 324.4 324.4 + 324.3 324.3 324.3 324.3 324.2 324.2 324.2 324.1 324.1 324.1 324.0 324.0 324.0 323.9 323.9 323.9 ]plong + s[ 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.4 234.3 234.3 234.3 234.3 234.3 234.3 234.3 234.3 + 234.3 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.2 234.1 234.1 234.1 234.1 ][ 325.9 325.8 + 325.8 325.8 325.7 325.7 325.7 325.7 325.6 325.6 325.6 325.6 325.5 325.5 325.5 325.4 325.4 325.4 325.3 325.3 + 325.3 325.2 325.2 325.2 325.2 325.1 325.1 325.1 325.0 325.0 325.0 325.0 324.9 324.9 ]plong + s[ 553.5 553.5 553.5 553.5 553.5 553.5 553.5 553.5 553.5 553.5 553.6 553.6 553.6 553.6 553.6 553.6 553.6 553.6 + 553.6 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.7 553.8 553.8 553.8 ][ 325.9 325.8 + 325.8 325.8 325.7 325.7 325.7 325.7 325.6 325.6 325.6 325.6 325.5 325.5 325.5 325.4 325.4 325.4 325.3 325.3 + 325.3 325.2 325.2 325.2 325.2 325.1 325.1 325.1 325.0 325.0 325.0 325.0 324.9 324.9 ]plong + s[ 234.7 234.7 234.7 234.7 234.7 234.7 234.7 234.7 234.6 234.6 234.6 234.6 234.6 234.6 234.6 234.6 234.6 234.5 + 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.5 234.4 234.4 ][ 326.7 326.7 326.7 326.6 326.6 326.6 + 326.5 326.5 326.5 326.5 326.4 326.4 326.4 326.3 326.3 326.3 326.2 326.2 326.2 326.1 326.1 326.1 326.1 326.0 + 326.0 326.0 325.9 325.9 325.9 325.9 ]plong + s[ 234.8 234.8 234.8 234.7 234.7 234.7 ][ 326.8 326.8 326.8 326.8 326.7 326.7 ]plong + s[ 553.1 553.1 553.1 553.1 553.1 553.1 553.2 ][ 326.8 326.8 326.8 326.8 326.7 326.7 326.7 ]plong + s[ 553.2 553.2 553.2 553.2 553.2 553.2 553.2 553.2 553.3 553.3 553.3 553.3 553.3 553.3 553.3 553.3 553.3 553.3 + 553.3 553.4 553.4 553.4 553.4 553.4 553.4 553.4 553.4 553.4 553.5 ][ 326.7 326.7 326.6 326.6 326.6 326.5 326.5 + 326.5 326.5 326.4 326.4 326.4 326.3 326.3 326.3 326.2 326.2 326.2 326.1 326.1 326.1 326.1 326.0 326.0 326.0 + 325.9 325.9 325.9 325.9 ]plong + s[ 235.1 235.1 235.1 235.1 235.1 235.1 235.1 235.1 235.1 235.0 235.0 235.0 235.0 235.0 235.0 235.0 235.0 235.0 + 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.9 234.8 234.8 234.8 234.8 234.8 234.8 234.8 ][ 327.8 + 327.8 327.8 327.7 327.7 327.7 327.7 327.6 327.6 327.6 327.6 327.5 327.5 327.5 327.4 327.4 327.4 327.3 327.3 + 327.3 327.3 327.2 327.2 327.2 327.1 327.1 327.1 327.0 327.0 327.0 327.0 326.9 326.9 326.9 326.8 ]plong + s[ 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.9 552.9 552.9 552.9 552.9 552.9 552.9 552.9 + 552.9 552.9 552.9 553.0 553.0 553.0 553.0 553.0 553.0 553.0 553.0 553.1 553.1 553.1 553.1 553.1 553.1 ][ 327.8 + 327.8 327.8 327.7 327.7 327.7 327.7 327.6 327.6 327.6 327.6 327.5 327.5 327.5 327.4 327.4 327.4 327.3 327.3 + 327.3 327.2 327.2 327.2 327.2 327.1 327.1 327.1 327.0 327.0 327.0 327.0 326.9 326.9 326.9 326.8 ]plong + s[ 235.5 235.5 235.5 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.4 235.3 235.3 235.3 235.3 + 235.3 235.3 235.3 235.3 235.3 235.3 235.2 235.2 235.2 235.2 235.2 235.2 235.2 235.2 235.2 235.1 235.1 ][ 328.8 + 328.8 328.7 328.7 328.7 328.7 328.7 328.6 328.6 328.6 328.5 328.5 328.5 328.4 328.4 328.4 328.3 328.3 328.3 + 328.3 328.2 328.2 328.2 328.1 328.1 328.1 328.1 328.0 328.0 328.0 327.9 327.9 327.9 327.9 327.8 ]plong + s[ 552.4 552.4 552.4 552.4 552.4 552.4 552.5 552.5 552.5 552.5 552.5 552.5 552.5 552.5 552.6 552.6 552.6 552.6 + 552.6 552.6 552.6 552.6 552.6 552.6 552.7 552.7 552.7 552.7 552.7 552.7 552.7 552.7 552.7 552.8 552.8 ][ 328.8 + 328.8 328.7 328.7 328.7 328.7 328.6 328.6 328.6 328.6 328.5 328.5 328.5 328.4 328.4 328.4 328.3 328.3 328.3 + 328.3 328.2 328.2 328.2 328.1 328.1 328.1 328.1 328.0 328.0 328.0 327.9 327.9 327.9 327.9 327.8 ]plong + s[ 235.7 235.7 235.7 235.7 235.7 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.6 235.5 235.5 235.5 + 235.5 235.5 235.5 235.5 ][ 329.4 329.4 329.4 329.3 329.3 329.3 329.2 329.2 329.2 329.2 329.1 329.1 329.1 329.0 + 329.0 329.0 329.0 328.9 328.9 328.9 328.8 328.8 ]plong + s[ 235.8 235.8 235.8 235.8 235.8 235.8 235.8 235.8 235.8 235.8 235.7 235.7 235.7 235.7 ][ 329.8 329.7 329.7 329.7 + 329.7 329.6 329.6 329.6 329.6 329.5 329.5 329.5 329.4 329.4 ]plong + s[ 552.0 552.0 552.0 552.1 552.1 552.1 552.1 552.1 552.1 552.1 552.1 552.2 552.2 552.2 552.2 ][ 329.8 329.7 329.7 + 329.7 329.7 329.6 329.6 329.6 329.6 329.5 329.5 329.5 329.4 329.4 329.4 ]plong + s[ 552.2 552.2 552.2 552.2 552.2 552.2 552.2 552.3 552.3 552.3 552.3 552.3 552.3 552.3 552.3 552.3 552.4 552.4 + 552.4 552.4 552.4 ][ 329.4 329.4 329.3 329.3 329.3 329.2 329.2 329.2 329.2 329.1 329.1 329.1 329.0 329.0 329.0 + 328.9 328.9 328.9 328.9 328.8 328.8 ]plong + s[ 236.2 236.2 236.2 236.2 236.2 236.2 236.2 236.2 236.1 236.1 236.1 236.1 236.1 236.1 236.1 236.1 236.1 236.0 + 236.0 236.0 236.0 236.0 236.0 236.0 236.0 236.0 235.9 235.9 235.9 235.9 235.9 235.9 235.9 235.9 235.9 235.8 ][ + 330.8 330.7 330.7 330.7 330.6 330.6 330.6 330.6 330.6 330.5 330.5 330.5 330.4 330.4 330.4 330.3 330.3 330.3 + 330.3 330.2 330.2 330.2 330.1 330.1 330.1 330.1 330.0 330.0 330.0 329.9 329.9 329.9 329.9 329.8 329.8 329.8 ]plong + s[ 551.7 551.7 551.7 551.7 551.7 551.7 551.7 551.7 551.7 551.8 551.8 551.8 551.8 551.8 551.8 551.8 551.8 551.8 + 551.8 551.9 551.9 551.9 551.9 551.9 551.9 551.9 551.9 551.9 552.0 552.0 552.0 552.0 552.0 552.0 552.0 552.0 ][ + 330.8 330.7 330.7 330.7 330.6 330.6 330.6 330.6 330.5 330.5 330.5 330.5 330.4 330.4 330.4 330.3 330.3 330.3 + 330.3 330.2 330.2 330.2 330.1 330.1 330.1 330.1 330.0 330.0 330.0 329.9 329.9 329.9 329.9 329.8 329.8 329.8 ]plong + s[ 236.6 236.6 236.6 236.6 236.5 236.5 236.5 236.5 236.5 236.5 236.5 236.5 236.5 236.4 236.4 236.4 236.4 236.4 + 236.4 236.4 236.4 236.4 236.4 236.4 236.3 236.3 236.3 236.3 236.3 236.3 236.3 236.3 236.2 236.2 236.2 ][ 331.7 + 331.7 331.7 331.6 331.6 331.6 331.6 331.6 331.5 331.5 331.5 331.4 331.4 331.4 331.4 331.3 331.3 331.3 331.2 + 331.2 331.2 331.1 331.1 331.1 331.1 331.0 331.0 331.0 330.9 330.9 330.9 330.9 330.8 330.8 330.8 ]plong + s[ 551.3 551.3 551.3 551.3 551.3 551.3 551.3 551.4 551.4 551.4 551.4 551.4 551.4 551.4 551.4 551.5 551.5 551.5 + 551.5 551.5 551.5 551.5 551.5 551.5 551.6 551.6 551.6 551.6 551.6 551.6 551.6 551.6 551.6 551.7 551.7 ][ 331.7 + 331.7 331.7 331.6 331.6 331.6 331.6 331.5 331.5 331.5 331.5 331.4 331.4 331.4 331.3 331.3 331.3 331.3 331.2 + 331.2 331.2 331.1 331.1 331.1 331.1 331.0 331.0 331.0 330.9 330.9 330.9 330.8 330.8 330.8 330.8 ]plong + s[ 236.7 236.7 236.7 236.6 236.6 236.6 236.6 236.6 236.6 ][ 332.0 331.9 331.9 331.9 331.9 331.8 331.8 331.8 331.7 +]plong + s[ 237.0 237.0 237.0 236.9 236.9 236.9 236.9 236.9 236.9 236.9 236.9 236.9 236.9 236.8 236.8 236.8 236.8 236.8 + 236.8 236.8 236.8 236.7 236.7 236.7 236.7 236.7 236.7 236.7 ][ 332.7 332.7 332.7 332.6 332.6 332.6 332.5 332.5 + 332.5 332.5 332.5 332.4 332.4 332.4 332.3 332.3 332.3 332.3 332.2 332.2 332.2 332.1 332.1 332.1 332.1 332.0 + 332.0 332.0 ]plong + s[ 550.9 550.9 550.9 550.9 550.9 550.9 551.0 551.0 551.0 551.0 551.0 551.0 551.0 551.0 551.1 551.1 551.1 551.1 + 551.1 551.1 551.1 551.1 551.1 551.1 551.2 551.2 551.2 551.2 ][ 332.7 332.7 332.7 332.6 332.6 332.6 332.5 332.5 + 332.5 332.5 332.4 332.4 332.4 332.4 332.3 332.3 332.3 332.2 332.2 332.2 332.2 332.1 332.1 332.1 332.0 332.0 + 332.0 332.0 ]plong + s[ 551.2 551.2 551.2 551.2 551.2 551.3 551.3 551.3 551.3 ][ 332.0 331.9 331.9 331.9 331.8 331.8 331.8 331.8 331.7 +]plong + s[ 237.4 237.4 237.4 237.3 237.3 237.3 237.3 237.3 237.3 237.3 237.3 237.3 237.3 237.2 237.2 237.2 237.2 237.2 + 237.2 237.2 237.2 237.1 237.1 237.1 237.1 237.1 237.1 237.1 237.1 237.1 237.0 237.0 237.0 237.0 237.0 237.0 ][ + 333.7 333.7 333.6 333.6 333.6 333.5 333.5 333.5 333.5 333.5 333.4 333.4 333.4 333.3 333.3 333.3 333.3 333.2 + 333.2 333.2 333.1 333.1 333.1 333.1 333.0 333.0 333.0 332.9 332.9 332.9 332.9 332.8 332.8 332.8 332.7 332.7 ]plong + s[ 550.5 550.5 550.5 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.6 550.7 550.7 550.7 550.7 550.7 + 550.7 550.7 550.7 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.8 550.9 550.9 550.9 550.9 550.9 ][ + 333.7 333.7 333.6 333.6 333.6 333.5 333.5 333.5 333.5 333.5 333.4 333.4 333.4 333.3 333.3 333.3 333.3 333.2 + 333.2 333.2 333.1 333.1 333.1 333.1 333.0 333.0 333.0 332.9 332.9 332.9 332.9 332.8 332.8 332.8 332.7 332.7 ]plong + s[ 237.7 237.6 237.6 237.6 237.6 237.6 237.6 237.6 237.6 237.6 237.6 237.5 237.5 237.5 237.5 237.5 237.5 237.5 + 237.5 237.4 237.4 237.4 237.4 237.4 237.4 237.4 237.4 ][ 334.4 334.4 334.4 334.3 334.3 334.3 334.3 334.2 334.2 + 334.2 334.1 334.1 334.1 334.1 334.0 334.0 334.0 333.9 333.9 333.9 333.9 333.8 333.8 333.8 333.7 333.7 333.7 ]plong + s[ 237.8 237.8 237.8 237.7 237.7 237.7 237.7 237.7 237.7 237.7 ][ 334.7 334.6 334.6 334.6 334.6 334.5 334.5 334.5 + 334.5 334.4 ]plong + s[ 550.1 550.1 550.1 550.1 550.2 550.2 550.2 550.2 550.2 550.2 550.2 ][ 334.7 334.6 334.6 334.6 334.6 334.5 334.5 + 334.5 334.5 334.4 334.4 ]plong + s[ 550.2 550.2 550.2 550.3 550.3 550.3 550.3 550.3 550.3 550.3 550.3 550.4 550.4 550.4 550.4 550.4 550.4 550.4 + 550.4 550.4 550.5 550.5 550.5 550.5 550.5 550.5 ][ 334.4 334.4 334.3 334.3 334.3 334.3 334.2 334.2 334.2 334.1 + 334.1 334.1 334.1 334.0 334.0 334.0 333.9 333.9 333.9 333.9 333.8 333.8 333.8 333.7 333.7 333.7 ]plong + s[ 238.2 238.2 238.2 238.2 238.1 238.1 238.1 238.1 238.1 238.1 238.1 238.1 238.0 238.0 238.0 238.0 238.0 238.0 + 238.0 238.0 238.0 237.9 237.9 237.9 237.9 237.9 237.9 237.9 237.9 237.8 237.8 237.8 237.8 237.8 237.8 237.8 + 237.8 ][ 335.7 335.6 335.6 335.6 335.6 335.5 335.5 335.5 335.5 335.4 335.4 335.4 335.4 335.3 335.3 335.3 335.2 + 335.2 335.2 335.2 335.1 335.1 335.1 335.0 335.0 335.0 335.0 334.9 334.9 334.9 334.8 334.8 334.8 334.8 334.7 + 334.7 334.7 ]plong + s[ 549.7 549.7 549.7 549.7 549.7 549.8 549.8 549.8 549.8 549.8 549.8 549.8 549.9 549.9 549.9 549.9 549.9 549.9 + 549.9 549.9 549.9 549.9 549.9 550.0 550.0 550.0 550.0 550.0 550.0 550.0 550.0 550.0 550.1 550.1 550.1 550.1 + 550.1 ][ 335.7 335.6 335.6 335.6 335.5 335.5 335.5 335.5 335.5 335.4 335.4 335.4 335.4 335.3 335.3 335.3 335.2 + 335.2 335.2 335.2 335.1 335.1 335.1 335.0 335.0 335.0 335.0 334.9 334.9 334.9 334.8 334.8 334.8 334.8 334.7 + 334.7 334.7 ]plong + s[ 238.6 238.6 238.6 238.5 238.5 238.5 238.5 238.5 238.5 238.5 238.5 238.5 238.4 238.4 238.4 238.4 238.4 238.4 + 238.4 238.4 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.3 238.2 238.2 238.2 238.2 238.2 238.2 ][ + 336.6 336.6 336.6 336.6 336.5 336.5 336.5 336.4 336.4 336.4 336.4 336.3 336.3 336.3 336.3 336.2 336.2 336.2 + 336.1 336.1 336.1 336.1 336.0 336.0 336.0 335.9 335.9 335.9 335.9 335.8 335.8 335.8 335.7 335.7 335.7 335.7 ]plong + s[ 549.3 549.3 549.3 549.3 549.3 549.3 549.4 549.4 549.4 549.4 549.4 549.4 549.4 549.4 549.5 549.5 549.5 549.5 + 549.5 549.5 549.5 549.5 549.5 549.6 549.6 549.6 549.6 549.6 549.6 549.6 549.7 549.7 549.7 549.7 549.7 549.7 ][ + 336.6 336.6 336.6 336.5 336.5 336.5 336.5 336.4 336.4 336.4 336.4 336.3 336.3 336.3 336.3 336.2 336.2 336.2 + 336.1 336.1 336.1 336.1 336.0 336.0 336.0 335.9 335.9 335.9 335.9 335.8 335.8 335.8 335.7 335.7 335.7 335.7 ]plong + 238.6 336.6 238.6 336.7 238.6 336.7 238.6 336.7 238.6 336.7 5 pls + s[ 239.0 239.0 239.0 239.0 239.0 239.0 238.9 238.9 238.9 238.9 238.9 238.9 238.9 238.9 238.9 238.8 238.8 238.8 + 238.8 238.8 238.8 238.8 238.8 238.7 238.7 238.7 238.7 238.7 238.7 238.7 238.7 238.7 238.6 ][ 337.6 337.6 337.6 + 337.5 337.5 337.5 337.5 337.4 337.4 337.4 337.4 337.3 337.3 337.3 337.2 337.2 337.2 337.2 337.1 337.1 337.1 + 337.0 337.0 337.0 337.0 336.9 336.9 336.9 336.9 336.8 336.8 336.8 336.7 ]plong + s[ 548.9 548.9 548.9 548.9 548.9 548.9 548.9 548.9 548.9 549.0 549.0 549.0 549.0 549.0 549.0 549.0 549.1 549.1 + 549.1 549.1 549.1 549.1 549.1 549.1 549.1 549.2 549.2 549.2 549.2 549.2 549.2 549.2 549.2 ][ 337.6 337.6 337.6 + 337.5 337.5 337.5 337.5 337.4 337.4 337.4 337.4 337.3 337.3 337.3 337.2 337.2 337.2 337.2 337.1 337.1 337.1 + 337.0 337.0 337.0 337.0 336.9 336.9 336.9 336.8 336.8 336.8 336.8 336.7 ]plong + 549.3 336.6 549.3 336.7 549.3 336.7 549.3 336.7 549.2 336.7 5 pls + s[ 239.4 239.4 239.4 239.4 239.4 239.4 239.4 239.4 239.3 239.3 239.3 239.3 239.3 239.3 239.3 239.3 239.3 239.3 + 239.2 239.2 239.2 239.2 239.2 239.2 239.2 239.1 239.1 239.1 239.1 239.1 239.1 239.1 239.1 239.1 239.0 239.0 + 239.0 ][ 338.6 338.6 338.6 338.5 338.5 338.5 338.4 338.4 338.4 338.4 338.3 338.3 338.3 338.3 338.2 338.2 338.2 + 338.1 338.1 338.1 338.1 338.0 338.0 338.0 338.0 337.9 337.9 337.9 337.8 337.8 337.8 337.8 337.7 337.7 337.7 + 337.7 337.6 ]plong + s[ 548.4 548.4 548.5 548.5 548.5 548.5 548.5 548.5 548.5 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 548.6 + 548.7 548.7 548.7 548.7 548.7 548.7 548.7 548.7 548.8 548.8 548.8 548.8 548.8 548.8 548.8 548.8 548.8 548.9 + 548.9 ][ 338.6 338.6 338.5 338.5 338.5 338.5 338.4 338.4 338.4 338.4 338.3 338.3 338.3 338.3 338.2 338.2 338.2 + 338.1 338.1 338.1 338.1 338.0 338.0 338.0 337.9 337.9 337.9 337.9 337.8 337.8 337.8 337.8 337.7 337.7 337.7 + 337.6 337.6 ]plong + s[ 239.6 239.6 239.6 239.6 239.6 239.6 239.6 239.5 239.5 239.5 239.5 239.5 239.5 239.5 239.4 239.4 ][ 339.0 339.0 + 339.0 338.9 338.9 338.9 338.8 338.8 338.8 338.8 338.7 338.7 338.7 338.7 338.6 338.6 ]plong + s[ 239.9 239.9 239.9 239.8 239.8 239.8 239.8 239.8 239.8 239.8 239.8 239.7 239.7 239.7 239.7 239.7 239.7 239.7 + 239.7 239.6 239.6 239.6 ][ 339.6 339.5 339.5 339.5 339.5 339.4 339.4 339.4 339.4 339.3 339.3 339.3 339.3 339.2 + 339.2 339.2 339.2 339.1 339.1 339.1 339.0 339.0 ]plong + s[ 548.0 548.0 548.0 548.0 548.0 548.1 548.1 548.1 548.1 548.1 548.1 548.1 548.1 548.2 548.2 548.2 548.2 548.2 + 548.2 548.2 548.2 548.3 ][ 339.6 339.5 339.5 339.5 339.5 339.4 339.4 339.4 339.4 339.3 339.3 339.3 339.3 339.2 + 339.2 339.2 339.2 339.1 339.1 339.1 339.0 339.0 ]plong + s[ 548.3 548.3 548.3 548.3 548.3 548.3 548.3 548.3 548.4 548.4 548.4 548.4 548.4 548.4 548.4 548.4 ][ 339.0 339.0 + 339.0 338.9 338.9 338.9 338.8 338.8 338.8 338.8 338.7 338.7 338.7 338.7 338.6 338.6 ]plong + s[ 240.3 240.3 240.3 240.3 240.3 240.3 240.3 240.2 240.2 240.2 240.2 240.2 240.2 240.2 240.2 240.1 240.1 240.1 + 240.1 240.1 240.1 240.1 240.1 240.0 240.0 240.0 240.0 240.0 240.0 240.0 240.0 239.9 239.9 239.9 239.9 239.9 + 239.9 ][ 340.6 340.5 340.5 340.5 340.4 340.4 340.4 340.4 340.3 340.3 340.3 340.3 340.2 340.2 340.2 340.2 340.1 + 340.1 340.1 340.1 340.0 340.0 340.0 339.9 339.9 339.9 339.9 339.8 339.8 339.8 339.7 339.7 339.7 339.7 339.6 + 339.6 339.6 ]plong + s[ 547.6 547.6 547.6 547.6 547.6 547.6 547.6 547.7 547.7 547.7 547.7 547.7 547.7 547.7 547.7 547.8 547.8 547.8 + 547.8 547.8 547.8 547.8 547.8 547.9 547.9 547.9 547.9 547.9 547.9 547.9 547.9 547.9 548.0 548.0 548.0 548.0 + 548.0 ][ 340.6 340.5 340.5 340.5 340.4 340.4 340.4 340.4 340.3 340.3 340.3 340.3 340.2 340.2 340.2 340.2 340.1 + 340.1 340.1 340.0 340.0 340.0 340.0 339.9 339.9 339.9 339.9 339.8 339.8 339.8 339.7 339.7 339.7 339.7 339.6 + 339.6 339.6 ]plong + s[ 240.6 240.6 240.6 240.6 240.5 240.5 240.5 240.5 240.5 240.5 240.5 240.5 240.5 240.4 240.4 240.4 240.4 240.4 + 240.4 240.4 240.3 240.3 240.3 240.3 ][ 341.2 341.2 341.1 341.1 341.1 341.0 341.0 341.0 341.0 340.9 340.9 340.9 + 340.8 340.8 340.8 340.8 340.7 340.7 340.7 340.7 340.6 340.6 340.6 340.6 ]plong + s[ 240.8 240.8 240.7 240.7 240.7 240.7 240.7 240.7 240.7 240.7 240.7 240.6 240.6 240.6 240.6 ][ 341.5 341.5 341.5 + 341.5 341.4 341.4 341.4 341.3 341.3 341.3 341.3 341.3 341.2 341.2 341.2 ]plong + s[ 547.1 547.1 547.1 547.1 547.1 547.2 547.2 547.2 547.2 547.2 547.2 547.2 547.3 547.3 547.3 ][ 341.5 341.5 341.5 + 341.5 341.4 341.4 341.4 341.3 341.3 341.3 341.3 341.3 341.2 341.2 341.2 ]plong + s[ 547.3 547.3 547.3 547.3 547.3 547.3 547.4 547.4 547.4 547.4 547.4 547.4 547.4 547.4 547.5 547.5 547.5 547.5 + 547.5 547.5 547.5 547.5 547.6 547.6 ][ 341.2 341.2 341.1 341.1 341.1 341.0 341.0 341.0 341.0 340.9 340.9 340.9 + 340.8 340.8 340.8 340.8 340.7 340.7 340.7 340.7 340.6 340.6 340.6 340.6 ]plong + s[ 241.3 241.2 241.2 241.2 241.2 241.2 241.2 241.2 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.1 241.0 241.0 + 241.0 241.0 241.0 241.0 241.0 240.9 240.9 240.9 240.9 240.9 240.9 240.9 240.9 240.9 240.8 240.8 240.8 240.8 + 240.8 240.8 ][ 342.5 342.5 342.5 342.4 342.4 342.4 342.4 342.3 342.3 342.3 342.3 342.2 342.2 342.2 342.2 342.1 + 342.1 342.1 342.1 342.0 342.0 342.0 342.0 341.9 341.9 341.9 341.8 341.8 341.8 341.8 341.7 341.7 341.7 341.7 + 341.6 341.6 341.6 341.5 ]plong + s[ 546.6 546.7 546.7 546.7 546.7 546.7 546.7 546.7 546.8 546.8 546.8 546.8 546.8 546.8 546.8 546.8 546.9 546.9 + 546.9 546.9 546.9 546.9 546.9 546.9 546.9 546.9 547.0 547.0 547.0 547.0 547.0 547.0 547.0 547.1 547.1 547.1 + 547.1 547.1 ][ 342.5 342.5 342.5 342.4 342.4 342.4 342.4 342.3 342.3 342.3 342.3 342.2 342.2 342.2 342.2 342.1 + 342.1 342.1 342.1 342.0 342.0 342.0 341.9 341.9 341.9 341.9 341.8 341.8 341.8 341.8 341.7 341.7 341.7 341.7 + 341.6 341.6 341.6 341.5 ]plong + s[ 241.6 241.6 241.6 241.5 241.5 241.5 241.5 241.5 241.5 241.5 241.4 241.4 241.4 241.4 241.4 241.4 241.4 241.4 + 241.3 241.3 241.3 241.3 241.3 241.3 241.3 241.3 241.3 241.3 ][ 343.2 343.2 343.2 343.2 343.2 343.1 343.1 343.1 + 343.0 343.0 343.0 343.0 342.9 342.9 342.9 342.9 342.8 342.8 342.8 342.8 342.7 342.7 342.7 342.6 342.6 342.6 + 342.6 342.5 ]plong + s[ 241.7 241.7 241.7 241.6 241.6 241.6 241.6 241.6 241.6 241.6 ][ 343.5 343.5 343.4 343.4 343.4 343.4 343.3 343.3 + 343.3 343.2 ]plong + s[ 546.2 546.2 546.2 546.2 546.2 546.2 546.2 546.3 546.3 546.3 546.3 ][ 343.5 343.5 343.5 343.4 343.4 343.4 343.3 + 343.3 343.3 343.3 343.2 ]plong + s[ 546.3 546.3 546.3 546.3 546.4 546.4 546.4 546.4 546.4 546.4 546.4 546.4 546.5 546.5 546.5 546.5 546.5 546.5 + 546.5 546.6 546.6 546.6 546.6 546.6 546.6 546.6 546.6 546.6 ][ 343.2 343.2 343.2 343.2 343.2 343.1 343.1 343.1 + 343.0 343.0 343.0 343.0 342.9 342.9 342.9 342.9 342.8 342.8 342.8 342.8 342.7 342.7 342.7 342.6 342.6 342.6 + 342.6 342.5 ]plong + s[ 242.2 242.2 242.2 242.1 242.1 242.1 242.1 242.1 242.1 242.1 242.0 242.0 242.0 242.0 242.0 242.0 242.0 242.0 + 241.9 241.9 241.9 241.9 241.9 241.9 241.9 241.8 241.8 241.8 241.8 241.8 241.8 241.8 241.8 241.7 241.7 241.7 + 241.7 241.7 ][ 344.5 344.4 344.4 344.4 344.4 344.3 344.3 344.3 344.2 344.2 344.2 344.2 344.2 344.1 344.1 344.1 + 344.1 344.0 344.0 344.0 343.9 343.9 343.9 343.9 343.8 343.8 343.8 343.8 343.7 343.7 343.7 343.7 343.6 343.6 + 343.6 343.5 343.5 343.5 ]plong + s[ 545.7 545.7 545.7 545.7 545.8 545.8 545.8 545.8 545.8 545.8 545.8 545.9 545.9 545.9 545.9 545.9 545.9 545.9 + 545.9 546.0 546.0 546.0 546.0 546.0 546.0 546.0 546.0 546.0 546.1 546.1 546.1 546.1 546.1 546.1 546.1 546.2 + 546.2 546.2 ][ 344.5 344.5 344.4 344.4 344.4 344.4 344.3 344.3 344.3 344.2 344.2 344.2 344.2 344.2 344.1 344.1 + 344.1 344.1 344.0 344.0 344.0 343.9 343.9 343.9 343.9 343.8 343.8 343.8 343.8 343.7 343.7 343.7 343.7 343.6 + 343.6 343.6 343.5 343.5 ]plong + s[ 242.6 242.5 242.5 242.5 242.5 242.5 242.5 242.5 242.5 242.4 242.4 242.4 242.4 242.4 242.4 242.4 242.3 242.3 + 242.3 242.3 242.3 242.3 242.3 242.3 242.2 242.2 242.2 242.2 242.2 242.2 242.2 242.2 ][ 345.3 345.3 345.2 345.2 + 345.2 345.2 345.1 345.1 345.1 345.1 345.0 345.0 345.0 345.0 344.9 344.9 344.9 344.8 344.8 344.8 344.8 344.7 + 344.7 344.7 344.7 344.6 344.6 344.6 344.6 344.5 344.5 344.5 ]plong + s[ 242.7 242.6 242.6 242.6 242.6 242.6 242.6 242.6 ][ 345.5 345.4 345.4 345.4 345.4 345.3 345.3 345.3 ]plong + s[ 545.2 545.2 545.3 545.3 545.3 545.3 545.3 545.3 ][ 345.5 345.4 345.4 345.4 345.4 345.3 345.3 345.3 ]plong + s[ 545.3 545.3 545.3 545.4 545.4 545.4 545.4 545.4 545.4 545.4 545.5 545.5 545.5 545.5 545.5 545.5 545.5 545.5 + 545.5 545.6 545.6 545.6 545.6 545.6 545.6 545.6 545.7 545.7 545.7 545.7 545.7 ][ 345.3 345.3 345.2 345.2 345.2 + 345.2 345.1 345.1 345.1 345.1 345.0 345.0 345.0 345.0 344.9 344.9 344.9 344.8 344.8 344.8 344.8 344.7 344.7 + 344.7 344.6 344.6 344.6 344.6 344.5 344.5 344.5 ]plong + s[ 243.1 243.1 243.1 243.1 243.1 243.1 243.1 243.1 243.0 243.0 243.0 243.0 243.0 243.0 243.0 242.9 242.9 242.9 + 242.9 242.9 242.9 242.9 242.9 242.8 242.8 242.8 242.8 242.8 242.8 242.8 242.7 242.7 242.7 242.7 242.7 242.7 + 242.7 242.7 ][ 346.4 346.4 346.4 346.4 346.3 346.3 346.3 346.2 346.2 346.2 346.2 346.1 346.1 346.1 346.1 346.1 + 346.0 346.0 346.0 345.9 345.9 345.9 345.9 345.8 345.8 345.8 345.8 345.7 345.7 345.7 345.7 345.6 345.6 345.6 + 345.5 345.5 345.5 345.5 ]plong + s[ 544.8 544.8 544.8 544.8 544.8 544.8 544.8 544.8 544.9 544.9 544.9 544.9 544.9 544.9 544.9 545.0 545.0 545.0 + 545.0 545.0 545.0 545.0 545.0 545.0 545.1 545.1 545.1 545.1 545.1 545.1 545.1 545.1 545.2 545.2 545.2 545.2 + 545.2 545.2 ][ 346.4 346.4 346.4 346.4 346.3 346.3 346.3 346.2 346.2 346.2 346.2 346.1 346.1 346.1 346.1 346.1 + 346.0 346.0 346.0 345.9 345.9 345.9 345.9 345.8 345.8 345.8 345.8 345.7 345.7 345.7 345.7 345.6 345.6 345.6 + 345.5 345.5 345.5 345.5 ]plong + s[ 243.5 243.5 243.5 243.5 243.5 243.5 243.5 243.4 243.4 243.4 243.4 243.4 243.4 243.4 243.4 243.3 243.3 243.3 + 243.3 243.3 243.3 243.3 243.2 243.2 243.2 243.2 243.2 243.2 243.2 243.2 243.2 243.1 ][ 347.2 347.2 347.2 347.2 + 347.1 347.1 347.1 347.1 347.0 347.0 347.0 347.0 346.9 346.9 346.9 346.9 346.8 346.8 346.8 346.8 346.7 346.7 + 346.7 346.6 346.6 346.6 346.6 346.5 346.5 346.5 346.5 346.4 ]plong + s[ 243.6 243.6 243.6 243.6 243.6 243.6 243.6 243.5 ][ 347.4 347.4 347.4 347.3 347.3 347.3 347.3 347.2 ]plong + s[ 544.2 544.2 544.3 544.3 544.3 544.3 544.3 544.3 544.3 ][ 347.4 347.4 347.4 347.3 347.3 347.3 347.3 347.2 347.2 +]plong + s[ 544.3 544.4 544.4 544.4 544.4 544.4 544.4 544.4 544.5 544.5 544.5 544.5 544.5 544.5 544.5 544.6 544.6 544.6 + 544.6 544.6 544.6 544.6 544.6 544.7 544.7 544.7 544.7 544.7 544.7 544.7 544.8 ][ 347.2 347.2 347.1 347.1 347.1 + 347.1 347.1 347.0 347.0 347.0 347.0 346.9 346.9 346.9 346.9 346.8 346.8 346.8 346.8 346.7 346.7 346.7 346.6 + 346.6 346.6 346.6 346.5 346.5 346.5 346.5 346.4 ]plong + s[ 244.2 244.1 244.1 244.1 244.1 244.1 244.1 244.1 244.0 244.0 244.0 244.0 244.0 244.0 244.0 244.0 243.9 243.9 + 243.9 243.9 243.9 243.9 243.9 243.8 243.8 243.8 243.8 243.8 243.8 243.8 243.8 243.7 243.7 243.7 243.7 243.7 + 243.7 243.6 243.6 ][ 348.4 348.4 348.4 348.3 348.3 348.3 348.2 348.2 348.2 348.2 348.1 348.1 348.1 348.1 348.1 + 348.0 348.0 348.0 347.9 347.9 347.9 347.9 347.8 347.8 347.8 347.8 347.7 347.7 347.7 347.7 347.6 347.6 347.6 + 347.6 347.5 347.5 347.5 347.5 347.4 ]plong + s[ 543.7 543.8 543.8 543.8 543.8 543.8 543.8 543.8 543.9 543.9 543.9 543.9 543.9 543.9 543.9 543.9 544.0 544.0 + 544.0 544.0 544.0 544.0 544.0 544.0 544.0 544.1 544.1 544.1 544.1 544.1 544.1 544.1 544.2 544.2 544.2 544.2 + 544.2 544.2 544.2 ][ 348.4 348.4 348.4 348.3 348.3 348.3 348.2 348.2 348.2 348.2 348.1 348.1 348.1 348.1 348.1 + 348.0 348.0 348.0 347.9 347.9 347.9 347.9 347.8 347.8 347.8 347.8 347.7 347.7 347.7 347.7 347.6 347.6 347.6 + 347.5 347.5 347.5 347.5 347.4 347.4 ]plong + s[ 244.5 244.5 244.5 244.5 244.5 244.5 244.4 244.4 244.4 244.4 244.4 244.4 244.4 244.3 244.3 244.3 244.3 244.3 + 244.3 244.3 244.2 244.2 244.2 244.2 244.2 244.2 244.2 244.2 244.2 ][ 349.1 349.1 349.1 349.0 349.0 349.0 349.0 + 349.0 348.9 348.9 348.9 348.8 348.8 348.8 348.8 348.7 348.7 348.7 348.7 348.6 348.6 348.6 348.6 348.5 348.5 + 348.5 348.5 348.4 348.4 ]plong + s[ 244.7 244.6 244.6 244.6 244.6 244.6 244.6 244.6 244.5 244.5 244.5 ][ 349.4 349.4 349.3 349.3 349.3 349.3 349.2 + 349.2 349.2 349.1 349.1 ]plong + s[ 543.2 543.2 543.2 543.3 543.3 543.3 543.3 543.3 543.3 543.3 543.4 ][ 349.4 349.3 349.3 349.3 349.3 349.2 349.2 + 349.2 349.2 349.1 349.1 ]plong + s[ 543.4 543.4 543.4 543.4 543.4 543.4 543.4 543.5 543.5 543.5 543.5 543.5 543.5 543.5 543.5 543.6 543.6 543.6 + 543.6 543.6 543.6 543.6 543.7 543.7 543.7 543.7 543.7 543.7 543.7 ][ 349.1 349.1 349.1 349.0 349.0 349.0 349.0 + 349.0 348.9 348.9 348.9 348.8 348.8 348.8 348.8 348.7 348.7 348.7 348.7 348.6 348.6 348.6 348.6 348.5 348.5 + 348.5 348.4 348.4 348.4 ]plong + s[ 245.2 245.2 245.1 245.1 245.1 245.1 245.1 245.1 245.1 245.1 245.1 245.0 245.0 245.0 245.0 245.0 245.0 244.9 + 244.9 244.9 244.9 244.9 244.9 244.9 244.9 244.8 244.8 244.8 244.8 244.8 244.8 244.8 244.7 244.7 244.7 244.7 + 244.7 244.7 244.7 ][ 350.4 350.3 350.3 350.3 350.2 350.2 350.2 350.2 350.1 350.1 350.1 350.1 350.0 350.0 350.0 + 350.0 350.0 349.9 349.9 349.9 349.9 349.8 349.8 349.8 349.7 349.7 349.7 349.7 349.6 349.6 349.6 349.6 349.5 + 349.5 349.5 349.5 349.4 349.4 349.4 ]plong + s[ 542.7 542.7 542.7 542.8 542.8 542.8 542.8 542.8 542.8 542.8 542.8 542.9 542.9 542.9 542.9 542.9 542.9 542.9 + 543.0 543.0 543.0 543.0 543.0 543.0 543.0 543.1 543.1 543.1 543.1 543.1 543.1 543.1 543.1 543.1 543.2 543.2 + 543.2 543.2 543.2 ][ 350.4 350.3 350.3 350.3 350.2 350.2 350.2 350.2 350.1 350.1 350.1 350.1 350.0 350.0 350.0 + 350.0 349.9 349.9 349.9 349.9 349.9 349.8 349.8 349.8 349.7 349.7 349.7 349.7 349.6 349.6 349.6 349.6 349.5 + 349.5 349.5 349.5 349.4 349.4 349.4 ]plong + s[ 245.5 245.5 245.5 245.5 245.4 245.4 245.4 245.4 245.4 245.4 245.4 245.4 245.3 245.3 245.3 245.3 245.3 245.3 + 245.2 245.2 245.2 245.2 245.2 245.2 245.2 ][ 351.0 351.0 350.9 350.9 350.9 350.8 350.8 350.8 350.8 350.7 350.7 + 350.7 350.7 350.6 350.6 350.6 350.6 350.5 350.5 350.5 350.5 350.4 350.4 350.4 350.4 ]plong + s[ 245.7 245.7 245.7 245.7 245.7 245.6 245.6 245.6 245.6 245.6 245.6 245.6 245.5 245.5 245.5 245.5 ][ 351.3 351.3 + 351.3 351.3 351.2 351.2 351.2 351.2 351.1 351.1 351.1 351.1 351.0 351.0 351.0 351.0 ]plong + s[ 542.2 542.2 542.2 542.2 542.2 542.2 542.2 542.3 542.3 542.3 542.3 542.3 542.3 542.3 542.4 542.4 ][ 351.3 351.3 + 351.3 351.3 351.2 351.2 351.2 351.2 351.1 351.1 351.1 351.1 351.0 351.0 351.0 351.0 ]plong + s[ 542.4 542.4 542.4 542.4 542.4 542.4 542.5 542.5 542.5 542.5 542.5 542.5 542.5 542.6 542.6 542.6 542.6 542.6 + 542.6 542.6 542.7 542.7 542.7 542.7 542.7 ][ 351.0 351.0 350.9 350.9 350.9 350.8 350.8 350.8 350.8 350.7 350.7 + 350.7 350.7 350.6 350.6 350.6 350.6 350.5 350.5 350.5 350.5 350.4 350.4 350.4 350.4 ]plong + s[ 246.3 246.2 246.2 246.2 246.2 246.2 246.2 246.1 246.1 246.1 246.1 246.1 246.1 246.1 246.1 246.1 246.0 246.0 + 246.0 246.0 246.0 246.0 246.0 245.9 245.9 245.9 245.9 245.9 245.9 245.9 245.8 245.8 245.8 245.8 245.8 245.8 + 245.8 245.7 245.7 245.7 ][ 352.3 352.3 352.3 352.3 352.2 352.2 352.2 352.2 352.1 352.1 352.1 352.0 352.0 352.0 + 352.0 351.9 351.9 351.9 351.9 351.9 351.8 351.8 351.8 351.8 351.7 351.7 351.7 351.7 351.6 351.6 351.6 351.5 + 351.5 351.5 351.5 351.5 351.4 351.4 351.4 351.3 ]plong + s[ 541.6 541.6 541.7 541.7 541.7 541.7 541.7 541.7 541.7 541.8 541.8 541.8 541.8 541.8 541.8 541.8 541.9 541.9 + 541.9 541.9 541.9 541.9 541.9 542.0 542.0 542.0 542.0 542.0 542.0 542.0 542.1 542.1 542.1 542.1 542.1 542.1 + 542.1 542.1 542.2 542.2 ][ 352.3 352.3 352.3 352.2 352.2 352.2 352.2 352.2 352.1 352.1 352.1 352.0 352.0 352.0 + 352.0 351.9 351.9 351.9 351.9 351.9 351.8 351.8 351.8 351.8 351.7 351.7 351.7 351.7 351.6 351.6 351.6 351.5 + 351.5 351.5 351.5 351.4 351.4 351.4 351.4 351.3 ]plong + s[ 246.5 246.5 246.5 246.4 246.4 246.4 246.4 246.4 246.4 246.3 246.3 246.3 246.3 246.3 246.3 246.3 246.3 ][ 352.7 + 352.7 352.7 352.7 352.6 352.6 352.6 352.6 352.5 352.5 352.5 352.5 352.4 352.4 352.4 352.4 352.3 ]plong + s[ 246.8 246.8 246.8 246.8 246.7 246.7 246.7 246.7 246.7 246.7 246.7 246.7 246.6 246.6 246.6 246.6 246.6 246.6 + 246.5 246.5 246.5 246.5 246.5 246.5 ][ 353.3 353.3 353.3 353.2 353.2 353.2 353.2 353.1 353.1 353.1 353.1 353.0 + 353.0 353.0 353.0 352.9 352.9 352.9 352.9 352.8 352.8 352.8 352.8 352.7 ]plong + s[ 541.1 541.1 541.1 541.1 541.1 541.1 541.2 541.2 541.2 541.2 541.2 541.2 541.2 541.3 541.3 541.3 541.3 541.3 + 541.3 541.3 541.4 541.4 541.4 541.4 ][ 353.3 353.3 353.3 353.2 353.2 353.2 353.2 353.1 353.1 353.1 353.1 353.0 + 353.0 353.0 352.9 352.9 352.9 352.9 352.9 352.8 352.8 352.8 352.8 352.7 ]plong + s[ 541.4 541.4 541.4 541.4 541.5 541.5 541.5 541.5 541.5 541.5 541.5 541.6 541.6 541.6 541.6 541.6 541.6 ][ 352.7 + 352.7 352.7 352.7 352.6 352.6 352.6 352.6 352.5 352.5 352.5 352.5 352.4 352.4 352.4 352.4 352.3 ]plong + s[ 247.4 247.3 247.3 247.3 247.3 247.3 247.3 247.2 247.2 247.2 247.2 247.2 247.2 247.2 247.2 247.1 247.1 247.1 + 247.1 247.1 247.1 247.1 247.1 247.0 247.0 247.0 247.0 247.0 247.0 246.9 246.9 246.9 246.9 246.9 246.9 246.9 + 246.9 246.8 246.8 246.8 ][ 354.3 354.3 354.2 354.2 354.2 354.2 354.1 354.1 354.1 354.1 354.0 354.0 354.0 354.0 + 353.9 353.9 353.9 353.9 353.9 353.8 353.8 353.8 353.7 353.7 353.7 353.7 353.6 353.6 353.6 353.6 353.5 353.5 + 353.5 353.5 353.4 353.4 353.4 353.4 353.3 353.3 ]plong + s[ 540.5 540.5 540.6 540.6 540.6 540.6 540.6 540.6 540.6 540.7 540.7 540.7 540.7 540.7 540.7 540.7 540.8 540.8 + 540.8 540.8 540.8 540.8 540.8 540.9 540.9 540.9 540.9 540.9 540.9 540.9 541.0 541.0 541.0 541.0 541.0 541.0 + 541.0 541.1 541.1 541.1 ][ 354.3 354.3 354.2 354.2 354.2 354.2 354.1 354.1 354.1 354.1 354.0 354.0 354.0 354.0 + 353.9 353.9 353.9 353.9 353.8 353.8 353.8 353.8 353.7 353.7 353.7 353.7 353.6 353.6 353.6 353.6 353.5 353.5 + 353.5 353.5 353.4 353.4 353.4 353.4 353.3 353.3 ]plong + s[ 247.5 247.4 247.4 247.4 247.4 247.4 247.4 247.4 ][ 354.5 354.4 354.4 354.4 354.4 354.3 354.3 354.3 ]plong + s[ 247.9 247.9 247.9 247.9 247.9 247.8 247.8 247.8 247.8 247.8 247.8 247.8 247.7 247.7 247.7 247.7 247.7 247.7 + 247.7 247.6 247.6 247.6 247.6 247.6 247.6 247.6 247.5 247.5 247.5 247.5 247.5 247.5 247.5 ][ 355.3 355.2 355.2 + 355.2 355.2 355.1 355.1 355.1 355.1 355.0 355.0 355.0 355.0 354.9 354.9 354.9 354.9 354.8 354.8 354.8 354.8 + 354.7 354.7 354.7 354.7 354.6 354.6 354.6 354.6 354.5 354.5 354.5 354.5 ]plong + s[ 540.0 540.0 540.0 540.0 540.0 540.1 540.1 540.1 540.1 540.1 540.1 540.1 540.1 540.2 540.2 540.2 540.2 540.2 + 540.2 540.2 540.2 540.3 540.3 540.3 540.3 540.3 540.3 540.4 540.4 540.4 540.4 540.4 540.4 ][ 355.3 355.2 355.2 + 355.2 355.2 355.1 355.1 355.1 355.1 355.0 355.0 355.0 354.9 354.9 354.9 354.9 354.9 354.8 354.8 354.8 354.8 + 354.7 354.7 354.7 354.7 354.6 354.6 354.6 354.6 354.5 354.5 354.5 354.5 ]plong + s[ 540.4 540.4 540.4 540.5 540.5 540.5 540.5 540.5 ][ 354.5 354.4 354.4 354.4 354.4 354.3 354.3 354.3 ]plong + s[ 248.4 248.4 248.4 248.4 248.4 248.4 248.3 248.3 248.3 248.3 248.3 248.3 248.3 248.2 248.2 248.2 248.2 248.2 + 248.2 248.1 248.1 248.1 248.1 248.1 248.1 248.1 248.1 248.0 248.0 248.0 248.0 248.0 248.0 248.0 248.0 247.9 + 247.9 ][ 356.1 356.1 356.1 356.1 356.0 356.0 356.0 356.0 355.9 355.9 355.9 355.9 355.8 355.8 355.8 355.8 355.8 + 355.7 355.7 355.7 355.7 355.6 355.6 355.6 355.6 355.5 355.5 355.5 355.5 355.4 355.4 355.4 355.4 355.3 355.3 + 355.3 355.3 ]plong + 248.4 356.1 248.5 356.2 248.5 356.2 248.5 356.2 248.5 356.2 5 pls + 539.4 356.1 539.4 356.2 539.4 356.2 539.4 356.2 539.4 356.2 5 pls + s[ 539.4 539.5 539.5 539.5 539.5 539.5 539.5 539.5 539.6 539.6 539.6 539.6 539.6 539.6 539.7 539.7 539.7 539.7 + 539.7 539.7 539.7 539.8 539.8 539.8 539.8 539.8 539.8 539.8 539.9 539.9 539.9 539.9 539.9 539.9 539.9 540.0 + 540.0 ][ 356.1 356.1 356.1 356.1 356.0 356.0 356.0 356.0 355.9 355.9 355.9 355.9 355.8 355.8 355.8 355.8 355.8 + 355.7 355.7 355.7 355.7 355.6 355.6 355.6 355.6 355.5 355.5 355.5 355.5 355.4 355.4 355.4 355.4 355.3 355.3 + 355.3 355.3 ]plong + s[ 249.1 249.1 249.0 249.0 249.0 249.0 249.0 249.0 249.0 249.0 248.9 248.9 248.9 248.9 248.9 248.9 248.9 248.8 + 248.8 248.8 248.8 248.8 248.8 248.7 248.7 248.7 248.7 248.7 248.7 248.7 248.6 248.6 248.6 248.6 248.6 248.6 + 248.5 248.5 248.5 248.5 248.5 ][ 357.2 357.2 357.2 357.2 357.1 357.1 357.1 357.1 357.0 357.0 357.0 357.0 356.9 + 356.9 356.9 356.9 356.8 356.8 356.8 356.8 356.8 356.7 356.7 356.7 356.6 356.6 356.6 356.6 356.6 356.5 356.5 + 356.5 356.4 356.4 356.4 356.4 356.4 356.3 356.3 356.3 356.2 ]plong + s[ 538.8 538.8 538.8 538.9 538.9 538.9 538.9 538.9 538.9 538.9 539.0 539.0 539.0 539.0 539.0 539.0 539.0 539.1 + 539.1 539.1 539.1 539.1 539.1 539.2 539.2 539.2 539.2 539.2 539.2 539.2 539.2 539.3 539.3 539.3 539.3 539.3 + 539.3 539.3 539.4 539.4 539.4 ][ 357.2 357.2 357.2 357.2 357.1 357.1 357.1 357.1 357.0 357.0 357.0 357.0 356.9 + 356.9 356.9 356.9 356.8 356.8 356.8 356.8 356.7 356.7 356.7 356.7 356.6 356.6 356.6 356.6 356.5 356.5 356.5 + 356.5 356.4 356.4 356.4 356.4 356.3 356.3 356.3 356.3 356.2 ]plong + s[ 249.4 249.4 249.4 249.4 249.4 249.3 249.3 249.3 249.3 249.3 249.3 249.2 249.2 249.2 249.2 249.2 249.2 249.2 + 249.1 249.1 249.1 249.1 249.1 249.1 ][ 357.8 357.8 357.8 357.7 357.7 357.7 357.7 357.6 357.6 357.6 357.6 357.5 + 357.5 357.5 357.5 357.4 357.4 357.4 357.4 357.3 357.3 357.3 357.3 357.2 ]plong + s[ 249.7 249.7 249.6 249.6 249.6 249.6 249.6 249.6 249.6 249.5 249.5 249.5 249.5 249.5 249.5 249.4 249.4 249.4 ][ + 358.2 358.2 358.2 358.1 358.1 358.1 358.1 358.0 358.0 358.0 358.0 357.9 357.9 357.9 357.9 357.8 357.8 357.8 ]plong + s[ 538.2 538.2 538.2 538.2 538.3 538.3 538.3 538.3 538.3 538.3 538.4 538.4 538.4 538.4 538.4 538.4 538.4 538.5 ][ + 358.2 358.2 358.2 358.1 358.1 358.1 358.1 358.0 358.0 358.0 358.0 357.9 357.9 357.9 357.9 357.8 357.8 357.8 ]plong + s[ 538.5 538.5 538.5 538.5 538.5 538.5 538.6 538.6 538.6 538.6 538.6 538.6 538.6 538.7 538.7 538.7 538.7 538.7 + 538.7 538.8 538.8 538.8 538.8 538.8 ][ 357.8 357.8 357.8 357.7 357.7 357.7 357.7 357.6 357.6 357.6 357.6 357.5 + 357.5 357.5 357.5 357.4 357.4 357.4 357.4 357.3 357.3 357.3 357.3 357.2 ]plong + s[ 250.3 250.2 250.2 250.2 250.2 250.2 250.2 250.1 250.1 250.1 250.1 250.1 250.1 250.1 250.0 250.0 250.0 250.0 + 250.0 250.0 250.0 250.0 249.9 249.9 249.9 249.9 249.9 249.9 249.9 249.8 249.8 249.8 249.8 249.8 249.8 249.7 + 249.7 249.7 249.7 249.7 249.7 ][ 359.2 359.2 359.1 359.1 359.1 359.1 359.0 359.0 359.0 359.0 358.9 358.9 358.9 + 358.9 358.8 358.8 358.8 358.8 358.7 358.7 358.7 358.7 358.7 358.6 358.6 358.6 358.6 358.5 358.5 358.5 358.5 + 358.4 358.4 358.4 358.4 358.3 358.3 358.3 358.3 358.2 358.2 ]plong + s[ 537.6 537.6 537.7 537.7 537.7 537.7 537.7 537.7 537.7 537.8 537.8 537.8 537.8 537.8 537.8 537.8 537.9 537.9 + 537.9 537.9 537.9 537.9 538.0 538.0 538.0 538.0 538.0 538.0 538.0 538.1 538.1 538.1 538.1 538.1 538.1 538.1 + 538.2 538.2 538.2 538.2 538.2 ][ 359.2 359.2 359.1 359.1 359.1 359.1 359.0 359.0 359.0 359.0 358.9 358.9 358.9 + 358.9 358.8 358.8 358.8 358.8 358.7 358.7 358.7 358.7 358.7 358.6 358.6 358.6 358.6 358.5 358.5 358.5 358.5 + 358.4 358.4 358.4 358.4 358.3 358.3 358.3 358.3 358.2 358.2 ]plong + s[ 250.4 250.4 250.4 250.3 250.3 250.3 250.3 250.3 250.3 250.3 ][ 359.4 359.4 359.4 359.3 359.3 359.3 359.3 359.2 + 359.2 359.2 ]plong + s[ 250.9 250.9 250.8 250.8 250.8 250.8 250.8 250.8 250.7 250.7 250.7 250.7 250.7 250.7 250.7 250.6 250.6 250.6 + 250.6 250.6 250.6 250.5 250.5 250.5 250.5 250.5 250.5 250.5 250.4 250.4 250.4 250.4 ][ 360.2 360.1 360.1 360.1 + 360.0 360.0 360.0 360.0 360.0 359.9 359.9 359.9 359.9 359.8 359.8 359.8 359.8 359.7 359.7 359.7 359.7 359.7 + 359.6 359.6 359.6 359.5 359.5 359.5 359.5 359.5 359.4 359.4 ]plong + s[ 537.0 537.0 537.0 537.1 537.1 537.1 537.1 537.1 537.1 537.2 537.2 537.2 537.2 537.2 537.2 537.2 537.2 537.3 + 537.3 537.3 537.3 537.3 537.3 537.3 537.4 537.4 537.4 537.4 537.4 537.4 537.5 537.5 537.5 ][ 360.2 360.1 360.1 + 360.1 360.1 360.0 360.0 360.0 360.0 360.0 359.9 359.9 359.9 359.8 359.8 359.8 359.8 359.8 359.7 359.7 359.7 + 359.7 359.6 359.6 359.6 359.6 359.5 359.5 359.5 359.5 359.5 359.4 359.4 ]plong + s[ 537.5 537.5 537.5 537.5 537.5 537.6 537.6 537.6 537.6 537.6 ][ 359.4 359.4 359.3 359.3 359.3 359.3 359.3 359.2 + 359.2 359.2 ]plong + s[ 251.4 251.4 251.3 251.3 251.3 251.3 251.3 251.3 251.3 251.2 251.2 251.2 251.2 251.2 251.2 251.1 251.1 251.1 + 251.1 251.1 251.1 251.1 251.0 251.0 251.0 251.0 251.0 251.0 251.0 250.9 250.9 250.9 250.9 250.9 250.9 ][ 361.0 + 360.9 360.9 360.9 360.9 360.8 360.8 360.8 360.8 360.7 360.7 360.7 360.7 360.7 360.6 360.6 360.6 360.6 360.5 + 360.5 360.5 360.5 360.4 360.4 360.4 360.4 360.3 360.3 360.3 360.3 360.2 360.2 360.2 360.2 360.2 ]plong + s[ 251.5 251.5 251.5 251.4 251.4 251.4 251.4 251.4 ][ 361.1 361.1 361.1 361.1 361.0 361.0 361.0 361.0 ]plong + s[ 536.4 536.4 536.4 536.4 536.5 536.5 536.5 536.5 ][ 361.1 361.1 361.1 361.1 361.0 361.0 361.0 361.0 ]plong + s[ 536.5 536.5 536.5 536.5 536.6 536.6 536.6 536.6 536.6 536.6 536.7 536.7 536.7 536.7 536.7 536.7 536.7 536.8 + 536.8 536.8 536.8 536.8 536.8 536.9 536.9 536.9 536.9 536.9 536.9 537.0 537.0 537.0 537.0 537.0 ][ 361.0 360.9 + 360.9 360.9 360.9 360.8 360.8 360.8 360.8 360.7 360.7 360.7 360.7 360.7 360.6 360.6 360.6 360.6 360.5 360.5 + 360.5 360.5 360.4 360.4 360.4 360.4 360.3 360.3 360.3 360.3 360.2 360.2 360.2 360.2 ]plong + s[ 252.1 252.1 252.1 252.1 252.0 252.0 252.0 252.0 252.0 252.0 251.9 251.9 251.9 251.9 251.9 251.9 251.9 251.9 + 251.8 251.8 251.8 251.8 251.8 251.8 251.8 251.7 251.7 251.7 251.7 251.7 251.7 251.6 251.6 251.6 251.6 251.6 + 251.6 251.6 251.5 251.5 251.5 251.5 ][ 362.1 362.1 362.1 362.0 362.0 362.0 362.0 361.9 361.9 361.9 361.9 361.8 + 361.8 361.8 361.8 361.7 361.7 361.7 361.7 361.7 361.6 361.6 361.6 361.6 361.5 361.5 361.5 361.5 361.5 361.4 + 361.4 361.4 361.4 361.3 361.3 361.3 361.3 361.2 361.2 361.2 361.2 361.1 ]plong + s[ 535.8 535.8 535.8 535.8 535.8 535.9 535.9 535.9 535.9 535.9 535.9 535.9 536.0 536.0 536.0 536.0 536.0 536.0 + 536.1 536.1 536.1 536.1 536.1 536.1 536.1 536.2 536.2 536.2 536.2 536.2 536.2 536.3 536.3 536.3 536.3 536.3 + 536.3 536.3 536.3 536.4 536.4 536.4 ][ 362.1 362.1 362.1 362.0 362.0 362.0 362.0 361.9 361.9 361.9 361.9 361.8 + 361.8 361.8 361.8 361.7 361.7 361.7 361.7 361.7 361.6 361.6 361.6 361.6 361.5 361.5 361.5 361.5 361.4 361.4 + 361.4 361.4 361.3 361.3 361.3 361.3 361.3 361.2 361.2 361.2 361.2 361.1 ]plong + s[ 252.4 252.3 252.3 252.3 252.3 252.3 252.3 252.3 252.2 252.2 252.2 252.2 252.2 252.2 252.1 252.1 252.1 ][ 362.5 + 362.5 362.5 362.4 362.4 362.4 362.4 362.3 362.3 362.3 362.3 362.2 362.2 362.2 362.2 362.1 362.1 ]plong + s[ 252.8 252.8 252.7 252.7 252.7 252.7 252.7 252.7 252.6 252.6 252.6 252.6 252.6 252.6 252.5 252.5 252.5 252.5 + 252.5 252.5 252.5 252.4 252.4 252.4 252.4 252.4 252.4 ][ 363.1 363.1 363.1 363.0 363.0 363.0 363.0 362.9 362.9 + 362.9 362.9 362.8 362.8 362.8 362.8 362.7 362.7 362.7 362.7 362.7 362.6 362.6 362.6 362.6 362.6 362.5 362.5 ]plong + s[ 535.1 535.1 535.2 535.2 535.2 535.2 535.2 535.2 535.3 535.3 535.3 535.3 535.3 535.3 535.3 535.3 535.4 535.4 + 535.4 535.4 535.4 535.4 535.5 535.5 535.5 535.5 535.5 ][ 363.1 363.1 363.1 363.0 363.0 363.0 363.0 362.9 362.9 + 362.9 362.9 362.8 362.8 362.8 362.8 362.7 362.7 362.7 362.7 362.6 362.6 362.6 362.6 362.6 362.5 362.5 362.5 ]plong + s[ 535.5 535.5 535.5 535.6 535.6 535.6 535.6 535.6 535.6 535.7 535.7 535.7 535.7 535.7 535.7 535.8 535.8 ][ 362.5 + 362.5 362.4 362.4 362.4 362.4 362.4 362.3 362.3 362.3 362.3 362.2 362.2 362.2 362.2 362.1 362.1 ]plong + s[ 253.3 253.3 253.3 253.3 253.3 253.3 253.2 253.2 253.2 253.2 253.2 253.2 253.1 253.1 253.1 253.1 253.1 253.1 + 253.0 253.0 253.0 253.0 253.0 253.0 253.0 252.9 252.9 252.9 252.9 252.9 252.9 252.9 252.9 252.8 252.8 252.8 + 252.8 252.8 ][ 364.0 364.0 363.9 363.9 363.9 363.9 363.8 363.8 363.8 363.8 363.7 363.7 363.7 363.7 363.6 363.6 + 363.6 363.6 363.6 363.5 363.5 363.5 363.5 363.5 363.4 363.4 363.4 363.3 363.3 363.3 363.3 363.3 363.2 363.2 + 363.2 363.2 363.1 363.1 ]plong + 253.3 364.0 253.4 364.0 253.4 364.0 253.4 364.1 253.4 364.1 5 pls + 534.5 364.0 534.5 364.0 534.5 364.0 534.5 364.0 534.5 364.1 5 pls + s[ 534.5 534.6 534.6 534.6 534.6 534.6 534.6 534.6 534.7 534.7 534.7 534.7 534.7 534.7 534.8 534.8 534.8 534.8 + 534.8 534.8 534.9 534.9 534.9 534.9 534.9 534.9 535.0 535.0 535.0 535.0 535.0 535.0 535.0 535.1 535.1 535.1 + 535.1 535.1 ][ 364.0 364.0 363.9 363.9 363.9 363.9 363.8 363.8 363.8 363.8 363.7 363.7 363.7 363.7 363.6 363.6 + 363.6 363.6 363.6 363.5 363.5 363.5 363.5 363.4 363.4 363.4 363.4 363.3 363.3 363.3 363.3 363.3 363.2 363.2 + 363.2 363.2 363.1 363.1 ]plong + s[ 254.1 254.0 254.0 254.0 254.0 254.0 254.0 253.9 253.9 253.9 253.9 253.9 253.9 253.9 253.8 253.8 253.8 253.8 + 253.8 253.8 253.8 253.7 253.7 253.7 253.7 253.7 253.7 253.6 253.6 253.6 253.6 253.6 253.6 253.5 253.5 253.5 + 253.5 253.5 253.5 253.4 253.4 253.4 253.4 ][ 365.1 365.0 365.0 365.0 365.0 364.9 364.9 364.9 364.9 364.9 364.8 + 364.8 364.8 364.8 364.7 364.7 364.7 364.7 364.6 364.6 364.6 364.6 364.6 364.5 364.5 364.5 364.5 364.4 364.4 + 364.4 364.4 364.3 364.3 364.3 364.3 364.2 364.2 364.2 364.2 364.2 364.1 364.1 364.1 ]plong + s[ 533.8 533.8 533.9 533.9 533.9 533.9 533.9 533.9 533.9 534.0 534.0 534.0 534.0 534.0 534.0 534.1 534.1 534.1 + 534.1 534.1 534.1 534.2 534.2 534.2 534.2 534.2 534.2 534.3 534.3 534.3 534.3 534.3 534.3 534.3 534.3 534.4 + 534.4 534.4 534.4 534.4 534.4 534.5 534.5 ][ 365.1 365.0 365.0 365.0 365.0 364.9 364.9 364.9 364.9 364.8 364.8 + 364.8 364.8 364.7 364.7 364.7 364.7 364.7 364.6 364.6 364.6 364.6 364.6 364.5 364.5 364.5 364.5 364.4 364.4 + 364.4 364.4 364.3 364.3 364.3 364.3 364.2 364.2 364.2 364.2 364.1 364.1 364.1 364.1 ]plong + s[ 254.3 254.3 254.3 254.3 254.3 254.2 254.2 254.2 254.2 254.2 254.2 254.1 254.1 254.1 254.1 254.1 254.1 ][ 365.4 + 365.4 365.4 365.4 365.3 365.3 365.3 365.3 365.3 365.2 365.2 365.2 365.2 365.1 365.1 365.1 365.1 ]plong + s[ 254.7 254.7 254.7 254.7 254.7 254.7 254.6 254.6 254.6 254.6 254.6 254.6 254.5 254.5 254.5 254.5 254.5 254.5 + 254.4 254.4 254.4 254.4 254.4 254.4 254.3 254.3 254.3 ][ 366.0 366.0 366.0 366.0 365.9 365.9 365.9 365.9 365.9 + 365.8 365.8 365.8 365.8 365.7 365.7 365.7 365.7 365.6 365.6 365.6 365.6 365.5 365.5 365.5 365.5 365.5 365.4 ]plong + s[ 533.2 533.2 533.2 533.2 533.2 533.2 533.3 533.3 533.3 533.3 533.3 533.3 533.4 533.4 533.4 533.4 533.4 533.4 + 533.4 533.5 533.5 533.5 533.5 533.5 533.5 533.5 533.6 ][ 366.0 366.0 366.0 366.0 365.9 365.9 365.9 365.9 365.8 + 365.8 365.8 365.8 365.8 365.7 365.7 365.7 365.7 365.6 365.6 365.6 365.6 365.5 365.5 365.5 365.5 365.5 365.4 ]plong + s[ 533.6 533.6 533.6 533.6 533.6 533.6 533.7 533.7 533.7 533.7 533.7 533.7 533.8 533.8 533.8 533.8 533.8 ][ 365.4 + 365.4 365.4 365.4 365.3 365.3 365.3 365.3 365.3 365.2 365.2 365.2 365.2 365.1 365.1 365.1 365.1 ]plong + s[ 255.3 255.3 255.3 255.3 255.2 255.2 255.2 255.2 255.2 255.2 255.1 255.1 255.1 255.1 255.1 255.1 255.0 255.0 + 255.0 255.0 255.0 255.0 254.9 254.9 254.9 254.9 254.9 254.9 254.8 254.8 254.8 254.8 254.8 254.8 254.8 254.8 + 254.7 ][ 366.9 366.9 366.8 366.8 366.8 366.8 366.7 366.7 366.7 366.7 366.6 366.6 366.6 366.6 366.5 366.5 366.5 + 366.5 366.5 366.4 366.4 366.4 366.4 366.4 366.3 366.3 366.3 366.3 366.2 366.2 366.2 366.2 366.1 366.1 366.1 + 366.1 366.0 ]plong + s[ 255.4 255.4 255.4 255.4 255.3 255.3 255.3 ][ 367.0 367.0 367.0 366.9 366.9 366.9 366.9 ]plong + s[ 532.5 532.5 532.5 532.5 532.5 532.6 532.6 532.6 ][ 367.0 367.0 367.0 366.9 366.9 366.9 366.9 366.8 ]plong + s[ 532.6 532.6 532.6 532.6 532.7 532.7 532.7 532.7 532.7 532.7 532.8 532.8 532.8 532.8 532.8 532.8 532.8 532.9 + 532.9 532.9 532.9 532.9 532.9 533.0 533.0 533.0 533.0 533.0 533.0 533.1 533.1 533.1 533.1 533.1 533.1 533.2 ][ + 366.8 366.8 366.8 366.8 366.8 366.7 366.7 366.7 366.7 366.6 366.6 366.6 366.6 366.5 366.5 366.5 366.5 366.5 + 366.4 366.4 366.4 366.4 366.3 366.3 366.3 366.3 366.3 366.2 366.2 366.2 366.2 366.1 366.1 366.1 366.1 366.0 ]plong + s[ 256.1 256.1 256.1 256.0 256.0 256.0 256.0 256.0 256.0 255.9 255.9 255.9 255.9 255.9 255.9 255.8 255.8 255.8 + 255.8 255.8 255.8 255.8 255.8 255.7 255.7 255.7 255.7 255.7 255.6 255.6 255.6 255.6 255.6 255.6 255.6 255.5 + 255.5 255.5 255.5 255.5 255.5 255.4 255.4 255.4 ][ 368.0 368.0 368.0 367.9 367.9 367.9 367.9 367.8 367.8 367.8 + 367.8 367.8 367.7 367.7 367.7 367.7 367.6 367.6 367.6 367.6 367.5 367.5 367.5 367.5 367.5 367.4 367.4 367.4 + 367.4 367.3 367.3 367.3 367.3 367.3 367.2 367.2 367.2 367.2 367.1 367.1 367.1 367.1 367.0 367.0 ]plong + s[ 531.8 531.8 531.8 531.8 531.9 531.9 531.9 531.9 531.9 531.9 531.9 532.0 532.0 532.0 532.0 532.0 532.0 532.1 + 532.1 532.1 532.1 532.1 532.1 532.2 532.2 532.2 532.2 532.2 532.2 532.3 532.3 532.3 532.3 532.3 532.3 532.4 + 532.4 532.4 532.4 532.4 532.4 532.4 532.5 532.5 ][ 368.0 368.0 368.0 367.9 367.9 367.9 367.9 367.8 367.8 367.8 + 367.8 367.7 367.7 367.7 367.7 367.6 367.6 367.6 367.6 367.6 367.5 367.5 367.5 367.5 367.5 367.4 367.4 367.4 + 367.4 367.3 367.3 367.3 367.3 367.3 367.2 367.2 367.2 367.2 367.1 367.1 367.1 367.1 367.0 367.0 ]plong + s[ 256.3 256.3 256.2 256.2 256.2 256.2 256.2 256.2 256.1 256.1 256.1 256.1 ][ 368.3 368.2 368.2 368.2 368.2 368.1 + 368.1 368.1 368.1 368.1 368.0 368.0 ]plong + s[ 256.8 256.8 256.8 256.8 256.7 256.7 256.7 256.7 256.7 256.7 256.6 256.6 256.6 256.6 256.6 256.6 256.5 256.5 + 256.5 256.5 256.5 256.5 256.4 256.4 256.4 256.4 256.4 256.4 256.3 256.3 256.3 256.3 256.3 ][ 369.0 369.0 368.9 + 368.9 368.9 368.9 368.9 368.8 368.8 368.8 368.8 368.7 368.7 368.7 368.7 368.6 368.6 368.6 368.6 368.5 368.5 + 368.5 368.5 368.5 368.4 368.4 368.4 368.4 368.4 368.3 368.3 368.3 368.3 ]plong + s[ 531.1 531.1 531.1 531.1 531.2 531.2 531.2 531.2 531.2 531.2 531.3 531.3 531.3 531.3 531.3 531.3 531.4 531.4 + 531.4 531.4 531.4 531.4 531.4 531.5 531.5 531.5 531.5 531.5 531.5 531.5 531.6 531.6 531.6 ][ 369.0 369.0 368.9 + 368.9 368.9 368.9 368.8 368.8 368.8 368.8 368.8 368.7 368.7 368.7 368.7 368.6 368.6 368.6 368.6 368.5 368.5 + 368.5 368.5 368.5 368.4 368.4 368.4 368.4 368.4 368.3 368.3 368.3 368.3 ]plong + s[ 531.6 531.6 531.6 531.7 531.7 531.7 531.7 531.7 531.7 531.7 531.8 531.8 ][ 368.3 368.2 368.2 368.2 368.2 368.1 + 368.1 368.1 368.1 368.0 368.0 368.0 ]plong + s[ 257.3 257.2 257.2 257.2 257.2 257.2 257.2 257.1 257.1 257.1 257.1 257.1 257.1 257.0 257.0 257.0 257.0 257.0 + 257.0 256.9 256.9 256.9 256.9 256.9 256.9 256.8 256.8 256.8 256.8 ][ 369.6 369.6 369.6 369.6 369.5 369.5 369.5 + 369.5 369.4 369.4 369.4 369.4 369.4 369.3 369.3 369.3 369.3 369.2 369.2 369.2 369.2 369.2 369.1 369.1 369.1 + 369.1 369.0 369.0 369.0 ]plong + s[ 257.5 257.5 257.5 257.5 257.4 257.4 257.4 257.4 257.4 257.4 257.3 257.3 257.3 257.3 257.3 257.3 ][ 370.0 369.9 + 369.9 369.9 369.9 369.9 369.8 369.8 369.8 369.8 369.7 369.7 369.7 369.7 369.6 369.6 ]plong + s[ 530.4 530.4 530.4 530.4 530.4 530.4 530.5 530.5 530.5 530.5 530.5 530.6 530.6 530.6 530.6 530.6 ][ 370.0 369.9 + 369.9 369.9 369.9 369.8 369.8 369.8 369.8 369.8 369.7 369.7 369.7 369.7 369.6 369.6 ]plong + s[ 530.6 530.6 530.7 530.7 530.7 530.7 530.7 530.7 530.8 530.8 530.8 530.8 530.8 530.8 530.9 530.9 530.9 530.9 + 530.9 530.9 531.0 531.0 531.0 531.0 531.0 531.0 531.1 531.1 531.1 ][ 369.6 369.6 369.6 369.6 369.5 369.5 369.5 + 369.5 369.4 369.4 369.4 369.4 369.4 369.3 369.3 369.3 369.3 369.2 369.2 369.2 369.2 369.1 369.1 369.1 369.1 + 369.1 369.0 369.0 369.0 ]plong + s[ 258.2 258.2 258.2 258.2 258.2 258.1 258.1 258.1 258.1 258.1 258.1 258.0 258.0 258.0 258.0 258.0 257.9 257.9 + 257.9 257.9 257.9 257.9 257.8 257.8 257.8 257.8 257.8 257.8 257.7 257.7 257.7 257.7 257.7 257.7 257.7 257.6 + 257.6 257.6 257.6 257.6 257.6 257.5 257.5 257.5 ][ 370.9 370.9 370.9 370.9 370.8 370.8 370.8 370.8 370.8 370.7 + 370.7 370.7 370.7 370.6 370.6 370.6 370.6 370.5 370.5 370.5 370.5 370.5 370.4 370.4 370.4 370.4 370.4 370.3 + 370.3 370.3 370.3 370.2 370.2 370.2 370.2 370.2 370.1 370.1 370.1 370.1 370.0 370.0 370.0 370.0 ]plong + s[ 529.7 529.7 529.7 529.7 529.7 529.7 529.8 529.8 529.8 529.8 529.8 529.8 529.9 529.9 529.9 529.9 529.9 529.9 + 530.0 530.0 530.0 530.0 530.0 530.0 530.1 530.1 530.1 530.1 530.1 530.1 530.2 530.2 530.2 530.2 530.2 530.3 + 530.3 530.3 530.3 530.3 530.3 530.4 530.4 530.4 ][ 370.9 370.9 370.9 370.9 370.8 370.8 370.8 370.8 370.8 370.7 + 370.7 370.7 370.7 370.6 370.6 370.6 370.6 370.5 370.5 370.5 370.5 370.5 370.4 370.4 370.4 370.4 370.4 370.3 + 370.3 370.3 370.3 370.2 370.2 370.2 370.2 370.2 370.1 370.1 370.1 370.1 370.0 370.0 370.0 370.0 ]plong + 258.2 370.9 258.2 371.0 2 pls + s[ 259.0 258.9 258.9 258.9 258.9 258.9 258.8 258.8 258.8 258.8 258.8 258.8 258.7 258.7 258.7 258.7 258.7 258.7 + 258.7 258.6 258.6 258.6 258.6 258.6 258.6 258.5 258.5 258.5 258.5 258.5 258.5 258.4 258.4 258.4 258.4 258.4 + 258.4 258.3 258.3 258.3 258.3 258.3 258.3 258.2 ][ 371.9 371.9 371.9 371.9 371.8 371.8 371.8 371.8 371.7 371.7 + 371.7 371.7 371.7 371.6 371.6 371.6 371.6 371.5 371.5 371.5 371.5 371.4 371.4 371.4 371.4 371.4 371.4 371.3 + 371.3 371.3 371.3 371.2 371.2 371.2 371.2 371.1 371.1 371.1 371.1 371.1 371.0 371.0 371.0 371.0 ]plong + s[ 528.9 528.9 529.0 529.0 529.0 529.0 529.0 529.0 529.1 529.1 529.1 529.1 529.1 529.1 529.2 529.2 529.2 529.2 + 529.2 529.2 529.3 529.3 529.3 529.3 529.3 529.4 529.4 529.4 529.4 529.4 529.4 529.5 529.5 529.5 529.5 529.5 + 529.5 529.5 529.6 529.6 529.6 529.6 529.6 529.6 ][ 371.9 371.9 371.9 371.9 371.8 371.8 371.8 371.8 371.7 371.7 + 371.7 371.7 371.6 371.6 371.6 371.6 371.6 371.5 371.5 371.5 371.5 371.4 371.4 371.4 371.4 371.4 371.3 371.3 + 371.3 371.3 371.3 371.2 371.2 371.2 371.2 371.1 371.1 371.1 371.1 371.1 371.0 371.0 371.0 371.0 ]plong + 529.6 371.0 529.7 370.9 2 pls + s[ 259.2 259.2 259.2 259.2 259.2 259.1 259.1 259.1 259.1 259.1 259.1 259.0 259.0 259.0 259.0 259.0 259.0 ][ 372.3 + 372.3 372.2 372.2 372.2 372.2 372.2 372.1 372.1 372.1 372.1 372.0 372.0 372.0 372.0 372.0 371.9 ]plong + s[ 259.7 259.7 259.7 259.7 259.6 259.6 259.6 259.6 259.6 259.6 259.5 259.5 259.5 259.5 259.5 259.4 259.4 259.4 + 259.4 259.4 259.4 259.3 259.3 259.3 259.3 259.3 259.3 259.2 259.2 ][ 372.9 372.9 372.9 372.8 372.8 372.8 372.8 + 372.7 372.7 372.7 372.7 372.7 372.6 372.6 372.6 372.6 372.5 372.5 372.5 372.5 372.5 372.4 372.4 372.4 372.4 + 372.3 372.3 372.3 372.3 ]plong + s[ 528.2 528.2 528.2 528.2 528.3 528.3 528.3 528.3 528.3 528.3 528.4 528.4 528.4 528.4 528.4 528.4 528.5 528.5 + 528.5 528.5 528.5 528.5 528.5 528.6 528.6 528.6 528.6 528.6 528.6 528.7 ][ 372.9 372.9 372.9 372.8 372.8 372.8 + 372.8 372.7 372.7 372.7 372.7 372.7 372.6 372.6 372.6 372.6 372.5 372.5 372.5 372.5 372.5 372.4 372.4 372.4 + 372.4 372.3 372.3 372.3 372.3 372.3 ]plong + s[ 528.7 528.7 528.7 528.7 528.7 528.8 528.8 528.8 528.8 528.8 528.8 528.9 528.9 528.9 528.9 528.9 ][ 372.3 372.2 + 372.2 372.2 372.2 372.2 372.1 372.1 372.1 372.1 372.0 372.0 372.0 372.0 371.9 371.9 ]plong + s[ 260.2 260.2 260.2 260.1 260.1 260.1 260.1 260.1 260.1 260.0 260.0 260.0 260.0 260.0 259.9 259.9 259.9 259.9 + 259.9 259.9 259.8 259.8 259.8 259.8 259.8 259.8 259.7 259.7 259.7 259.7 ][ 373.5 373.5 373.5 373.5 373.5 373.4 + 373.4 373.4 373.4 373.3 373.3 373.3 373.3 373.3 373.3 373.2 373.2 373.2 373.2 373.1 373.1 373.1 373.1 373.0 + 373.0 373.0 373.0 373.0 372.9 372.9 ]plong + s[ 260.5 260.4 260.4 260.4 260.4 260.4 260.3 260.3 260.3 260.3 260.3 260.3 260.2 260.2 260.2 260.2 ][ 373.9 373.9 + 373.8 373.8 373.8 373.8 373.8 373.7 373.7 373.7 373.7 373.6 373.6 373.6 373.6 373.5 ]plong + s[ 527.4 527.5 527.5 527.5 527.5 527.5 527.5 527.5 527.6 527.6 527.6 527.6 527.6 527.7 527.7 527.7 ][ 373.9 373.9 + 373.8 373.8 373.8 373.8 373.7 373.7 373.7 373.7 373.7 373.6 373.6 373.6 373.6 373.5 ]plong + s[ 527.7 527.7 527.7 527.7 527.8 527.8 527.8 527.8 527.8 527.8 527.9 527.9 527.9 527.9 527.9 527.9 528.0 528.0 + 528.0 528.0 528.0 528.0 528.1 528.1 528.1 528.1 528.1 528.2 528.2 528.2 ][ 373.5 373.5 373.5 373.5 373.5 373.4 + 373.4 373.4 373.4 373.3 373.3 373.3 373.3 373.3 373.2 373.2 373.2 373.2 373.2 373.1 373.1 373.1 373.1 373.0 + 373.0 373.0 373.0 373.0 372.9 372.9 ]plong + s[ 261.2 261.2 261.2 261.1 261.1 261.1 261.1 261.1 261.0 261.0 261.0 261.0 261.0 261.0 260.9 260.9 260.9 260.9 + 260.9 260.9 260.8 260.8 260.8 260.8 260.8 260.8 260.7 260.7 260.7 260.7 260.7 260.7 260.6 260.6 260.6 260.6 + 260.6 260.6 260.5 260.5 260.5 260.5 260.5 260.5 ][ 374.8 374.8 374.8 374.8 374.7 374.7 374.7 374.7 374.7 374.6 + 374.6 374.6 374.6 374.5 374.5 374.5 374.5 374.5 374.4 374.4 374.4 374.4 374.3 374.3 374.3 374.3 374.3 374.2 + 374.2 374.2 374.2 374.2 374.1 374.1 374.1 374.1 374.0 374.0 374.0 374.0 374.0 373.9 373.9 373.9 ]plong + 261.2 374.8 261.2 374.9 2 pls + 526.7 374.8 526.7 374.8 526.7 374.9 3 pls + s[ 526.7 526.7 526.7 526.8 526.8 526.8 526.8 526.8 526.8 526.9 526.9 526.9 526.9 526.9 527.0 527.0 527.0 527.0 + 527.0 527.0 527.1 527.1 527.1 527.1 527.1 527.1 527.2 527.2 527.2 527.2 527.2 527.2 527.3 527.3 527.3 527.3 + 527.3 527.4 527.4 527.4 527.4 527.4 527.4 ][ 374.8 374.8 374.8 374.7 374.7 374.7 374.7 374.7 374.6 374.6 374.6 + 374.6 374.5 374.5 374.5 374.5 374.4 374.4 374.4 374.4 374.4 374.3 374.3 374.3 374.3 374.3 374.2 374.2 374.2 + 374.2 374.2 374.1 374.1 374.1 374.1 374.0 374.0 374.0 374.0 374.0 373.9 373.9 373.9 ]plong + s[ 261.2 261.2 261.2 261.3 261.3 261.3 261.3 261.3 261.3 261.4 261.4 261.4 261.4 261.4 261.5 261.5 261.5 261.5 + 261.5 261.5 261.6 261.6 261.6 261.6 261.6 261.6 261.6 261.7 261.7 261.7 261.7 261.7 261.8 261.8 261.8 261.8 + 261.8 261.8 261.9 261.9 261.9 261.9 261.9 261.9 262.0 262.0 ][ 374.9 374.9 374.9 374.9 374.9 375.0 375.0 375.0 + 375.0 375.1 375.1 375.1 375.1 375.1 375.2 375.2 375.2 375.2 375.2 375.3 375.3 375.3 375.3 375.3 375.4 375.4 + 375.4 375.4 375.5 375.5 375.5 375.5 375.5 375.6 375.6 375.6 375.6 375.7 375.7 375.7 375.7 375.8 375.8 375.8 + 375.8 375.8 ]plong + s[ 525.9 525.9 525.9 525.9 526.0 526.0 526.0 526.0 526.0 526.1 526.1 526.1 526.1 526.1 526.1 526.2 526.2 526.2 + 526.2 526.2 526.3 526.3 526.3 526.3 526.3 526.3 526.4 526.4 526.4 526.4 526.4 526.4 526.5 526.5 526.5 526.5 + 526.5 526.5 526.6 526.6 526.6 526.6 526.6 526.6 526.7 526.7 ][ 375.8 375.8 375.8 375.8 375.7 375.7 375.7 375.7 + 375.7 375.6 375.6 375.6 375.6 375.5 375.5 375.5 375.5 375.5 375.4 375.4 375.4 375.4 375.3 375.3 375.3 375.3 + 375.3 375.2 375.2 375.2 375.2 375.2 375.1 375.1 375.1 375.1 375.1 375.0 375.0 375.0 375.0 374.9 374.9 374.9 + 374.9 374.9 ]plong + s[ 262.2 262.1 262.1 262.1 262.1 262.1 262.1 262.0 262.0 262.0 262.0 ][ 376.1 376.0 376.0 376.0 376.0 376.0 375.9 + 375.9 375.9 375.9 375.8 ]plong + s[ 262.8 262.7 262.7 262.7 262.7 262.7 262.7 262.6 262.6 262.6 262.6 262.6 262.6 262.6 262.5 262.5 262.5 262.5 + 262.5 262.4 262.4 262.4 262.4 262.4 262.4 262.3 262.3 262.3 262.3 262.3 262.2 262.2 262.2 262.2 262.2 262.2 ][ + 376.8 376.8 376.8 376.7 376.7 376.7 376.7 376.7 376.6 376.6 376.6 376.6 376.5 376.5 376.5 376.5 376.5 376.4 + 376.4 376.4 376.4 376.3 376.3 376.3 376.3 376.3 376.2 376.2 376.2 376.2 376.2 376.2 376.1 376.1 376.1 376.1 ]plong + s[ 525.1 525.1 525.1 525.2 525.2 525.2 525.2 525.2 525.3 525.3 525.3 525.3 525.3 525.3 525.4 525.4 525.4 525.4 + 525.4 525.5 525.5 525.5 525.5 525.5 525.5 525.6 525.6 525.6 525.6 525.6 525.6 525.6 525.7 525.7 525.7 525.7 ][ + 376.8 376.8 376.8 376.7 376.7 376.7 376.7 376.7 376.6 376.6 376.6 376.6 376.5 376.5 376.5 376.5 376.5 376.4 + 376.4 376.4 376.4 376.3 376.3 376.3 376.3 376.3 376.2 376.2 376.2 376.2 376.2 376.1 376.1 376.1 376.1 376.1 ]plong + s[ 525.7 525.7 525.8 525.8 525.8 525.8 525.8 525.8 525.9 525.9 525.9 ][ 376.1 376.0 376.0 376.0 376.0 375.9 375.9 + 375.9 375.9 375.9 375.8 ]plong + s[ 263.1 263.1 263.1 263.1 263.1 263.1 263.0 263.0 263.0 263.0 263.0 262.9 262.9 262.9 262.9 262.9 262.9 262.8 + 262.8 262.8 262.8 262.8 ][ 377.3 377.2 377.2 377.2 377.2 377.2 377.1 377.1 377.1 377.1 377.1 377.0 377.0 377.0 + 377.0 376.9 376.9 376.9 376.9 376.9 376.8 376.8 ]plong + s[ 263.6 263.6 263.6 263.5 263.5 263.5 263.5 263.5 263.4 263.4 263.4 263.4 263.4 263.4 263.3 263.3 263.3 263.3 + 263.3 263.2 263.2 263.2 263.2 263.2 263.2 263.1 ][ 377.8 377.8 377.8 377.7 377.7 377.7 377.7 377.7 377.6 377.6 + 377.6 377.6 377.5 377.5 377.5 377.5 377.5 377.4 377.4 377.4 377.4 377.3 377.3 377.3 377.3 377.3 ]plong + s[ 524.3 524.3 524.3 524.4 524.4 524.4 524.4 524.4 524.5 524.5 524.5 524.5 524.5 524.5 524.6 524.6 524.6 524.6 + 524.6 524.6 524.6 524.7 524.7 524.7 524.7 524.7 ][ 377.8 377.8 377.8 377.7 377.7 377.7 377.7 377.7 377.6 377.6 + 377.6 377.6 377.5 377.5 377.5 377.5 377.5 377.4 377.4 377.4 377.4 377.3 377.3 377.3 377.3 377.3 ]plong + s[ 524.7 524.8 524.8 524.8 524.8 524.8 524.8 524.9 524.9 524.9 524.9 524.9 525.0 525.0 525.0 525.0 525.0 525.0 + 525.1 525.1 525.1 525.1 ][ 377.3 377.2 377.2 377.2 377.2 377.2 377.1 377.1 377.1 377.1 377.1 377.0 377.0 377.0 + 377.0 376.9 376.9 376.9 376.9 376.9 376.8 376.8 ]plong + s[ 264.1 264.1 264.1 264.1 264.0 264.0 264.0 264.0 264.0 264.0 263.9 263.9 263.9 263.9 263.9 263.8 263.8 263.8 + 263.8 263.8 263.8 263.7 263.7 263.7 263.7 263.7 263.6 263.6 263.6 263.6 263.6 ][ 378.4 378.4 378.4 378.4 378.4 + 378.3 378.3 378.3 378.3 378.2 378.2 378.2 378.2 378.2 378.1 378.1 378.1 378.1 378.1 378.0 378.0 378.0 378.0 + 378.0 377.9 377.9 377.9 377.9 377.9 377.8 377.8 ]plong + s[ 264.4 264.4 264.4 264.3 264.3 264.3 264.3 264.3 264.2 264.2 264.2 264.2 264.2 264.2 264.1 264.1 ][ 378.8 378.7 + 378.7 378.7 378.7 378.7 378.6 378.6 378.6 378.6 378.6 378.5 378.5 378.5 378.5 378.4 ]plong + s[ 523.5 523.5 523.5 523.6 523.6 523.6 523.6 523.6 523.7 523.7 523.7 523.7 523.7 523.7 523.7 523.8 ][ 378.8 378.7 + 378.7 378.7 378.7 378.7 378.6 378.6 378.6 378.6 378.6 378.5 378.5 378.5 378.5 378.4 ]plong + s[ 523.8 523.8 523.8 523.8 523.8 523.9 523.9 523.9 523.9 523.9 523.9 524.0 524.0 524.0 524.0 524.0 524.1 524.1 + 524.1 524.1 524.1 524.1 524.2 524.2 524.2 524.2 524.2 524.3 524.3 524.3 524.3 ][ 378.4 378.4 378.4 378.4 378.4 + 378.3 378.3 378.3 378.3 378.2 378.2 378.2 378.2 378.2 378.1 378.1 378.1 378.1 378.1 378.0 378.0 378.0 378.0 + 378.0 377.9 377.9 377.9 377.9 377.8 377.8 377.8 ]plong + s[ 265.1 265.1 265.1 265.0 265.0 265.0 265.0 265.0 265.0 264.9 264.9 264.9 264.9 264.9 264.8 264.8 264.8 264.8 + 264.8 264.8 264.7 264.7 264.7 264.7 264.7 264.6 264.6 264.6 264.6 264.6 264.6 264.5 264.5 264.5 264.5 264.5 + 264.5 264.4 264.4 264.4 264.4 ][ 379.6 379.6 379.6 379.6 379.5 379.5 379.5 379.5 379.4 379.4 379.4 379.4 379.4 + 379.3 379.3 379.3 379.3 379.3 379.2 379.2 379.2 379.2 379.1 379.1 379.1 379.1 379.1 379.1 379.0 379.0 379.0 + 379.0 378.9 378.9 378.9 378.9 378.9 378.8 378.8 378.8 378.8 ]plong + s[ 265.2 265.2 265.2 265.2 265.1 265.1 265.1 ][ 379.8 379.7 379.7 379.7 379.7 379.6 379.6 ]plong + s[ 522.7 522.7 522.7 522.7 522.7 522.7 522.8 522.8 ][ 379.8 379.7 379.7 379.7 379.7 379.7 379.6 379.6 ]plong + s[ 522.8 522.8 522.8 522.8 522.9 522.9 522.9 522.9 522.9 522.9 523.0 523.0 523.0 523.0 523.0 523.1 523.1 523.1 + 523.1 523.1 523.1 523.2 523.2 523.2 523.2 523.2 523.3 523.3 523.3 523.3 523.3 523.3 523.4 523.4 523.4 523.4 + 523.4 523.5 523.5 523.5 523.5 ][ 379.6 379.6 379.6 379.6 379.5 379.5 379.5 379.5 379.4 379.4 379.4 379.4 379.4 + 379.3 379.3 379.3 379.3 379.2 379.2 379.2 379.2 379.2 379.1 379.1 379.1 379.1 379.1 379.1 379.0 379.0 379.0 + 379.0 378.9 378.9 378.9 378.9 378.9 378.8 378.8 378.8 378.8 ]plong + s[ 266.1 266.0 266.0 266.0 266.0 266.0 265.9 265.9 265.9 265.9 265.9 265.9 265.8 265.8 265.8 265.8 265.8 265.7 + 265.7 265.7 265.7 265.7 265.6 265.6 265.6 265.6 265.6 265.6 265.5 265.5 265.5 265.5 265.5 265.5 265.4 265.4 + 265.4 265.4 265.4 265.4 265.3 265.3 265.3 265.3 265.3 265.2 265.2 265.2 ][ 380.7 380.7 380.7 380.7 380.7 380.6 + 380.6 380.6 380.6 380.6 380.5 380.5 380.5 380.5 380.4 380.4 380.4 380.4 380.4 380.3 380.3 380.3 380.3 380.3 + 380.2 380.2 380.2 380.2 380.1 380.1 380.1 380.1 380.1 380.1 380.0 380.0 380.0 380.0 379.9 379.9 379.9 379.9 + 379.9 379.8 379.8 379.8 379.8 379.8 ]plong + s[ 521.8 521.8 521.9 521.9 521.9 521.9 521.9 522.0 522.0 522.0 522.0 522.0 522.0 522.1 522.1 522.1 522.1 522.1 + 522.2 522.2 522.2 522.2 522.2 522.2 522.3 522.3 522.3 522.3 522.3 522.4 522.4 522.4 522.4 522.4 522.5 522.5 + 522.5 522.5 522.5 522.5 522.6 522.6 522.6 522.6 522.6 522.7 522.7 ][ 380.7 380.7 380.7 380.7 380.7 380.6 380.6 + 380.6 380.6 380.5 380.5 380.5 380.5 380.5 380.4 380.4 380.4 380.4 380.4 380.3 380.3 380.3 380.3 380.2 380.2 + 380.2 380.2 380.2 380.1 380.1 380.1 380.1 380.1 380.1 380.0 380.0 380.0 380.0 379.9 379.9 379.9 379.9 379.9 + 379.8 379.8 379.8 379.8 ]plong + 266.1 380.7 266.1 380.8 2 pls + s[ 266.9 266.9 266.9 266.8 266.8 266.8 266.8 266.8 266.7 266.7 266.7 266.7 266.7 266.6 266.6 266.6 266.6 266.6 + 266.6 266.5 266.5 266.5 266.5 266.5 266.5 266.4 266.4 266.4 266.4 266.4 266.4 266.3 266.3 266.3 266.3 266.3 + 266.2 266.2 266.2 266.2 266.2 266.1 266.1 266.1 266.1 266.1 ][ 381.7 381.7 381.7 381.6 381.6 381.6 381.6 381.6 + 381.5 381.5 381.5 381.5 381.5 381.4 381.4 381.4 381.4 381.3 381.3 381.3 381.3 381.3 381.2 381.2 381.2 381.2 + 381.2 381.1 381.1 381.1 381.1 381.1 381.0 381.0 381.0 381.0 381.0 380.9 380.9 380.9 380.9 380.9 380.8 380.8 + 380.8 380.8 ]plong + s[ 521.0 521.0 521.0 521.0 521.0 521.1 521.1 521.1 521.1 521.1 521.2 521.2 521.2 521.2 521.2 521.2 521.3 521.3 + 521.3 521.3 521.3 521.4 521.4 521.4 521.4 521.4 521.5 521.5 521.5 521.5 521.5 521.5 521.6 521.6 521.6 521.6 + 521.6 521.7 521.7 521.7 521.7 521.7 521.7 521.7 521.8 521.8 521.8 ][ 381.7 381.7 381.7 381.7 381.6 381.6 381.6 + 381.6 381.6 381.5 381.5 381.5 381.5 381.4 381.4 381.4 381.4 381.4 381.3 381.3 381.3 381.3 381.3 381.2 381.2 + 381.2 381.2 381.2 381.1 381.1 381.1 381.1 381.1 381.0 381.0 381.0 381.0 381.0 380.9 380.9 380.9 380.9 380.9 + 380.8 380.8 380.8 380.8 ]plong + 521.8 380.8 521.8 380.7 2 pls + s[ 267.1 267.0 267.0 267.0 267.0 267.0 266.9 266.9 266.9 266.9 ][ 381.9 381.9 381.9 381.8 381.8 381.8 381.8 381.8 + 381.7 381.7 ]plong + s[ 267.8 267.8 267.7 267.7 267.7 267.7 267.7 267.6 267.6 267.6 267.6 267.6 267.5 267.5 267.5 267.5 267.5 267.5 + 267.5 267.4 267.4 267.4 267.4 267.4 267.3 267.3 267.3 267.3 267.3 267.2 267.2 267.2 267.2 267.2 267.2 267.1 + 267.1 267.1 267.1 267.1 ][ 382.7 382.7 382.7 382.6 382.6 382.6 382.6 382.6 382.5 382.5 382.5 382.5 382.5 382.4 + 382.4 382.4 382.4 382.3 382.3 382.3 382.3 382.3 382.2 382.2 382.2 382.2 382.2 382.1 382.1 382.1 382.1 382.1 + 382.0 382.0 382.0 382.0 382.0 381.9 381.9 381.9 ]plong + s[ 520.1 520.1 520.1 520.2 520.2 520.2 520.2 520.2 520.3 520.3 520.3 520.3 520.3 520.4 520.4 520.4 520.4 520.4 + 520.4 520.5 520.5 520.5 520.5 520.5 520.6 520.6 520.6 520.6 520.6 520.7 520.7 520.7 520.7 520.7 520.7 520.7 + 520.8 520.8 520.8 520.8 ][ 382.7 382.7 382.7 382.6 382.6 382.6 382.6 382.6 382.5 382.5 382.5 382.5 382.5 382.4 + 382.4 382.4 382.4 382.3 382.3 382.3 382.3 382.3 382.2 382.2 382.2 382.2 382.2 382.1 382.1 382.1 382.1 382.0 + 382.0 382.0 382.0 382.0 382.0 381.9 381.9 381.9 ]plong + s[ 520.8 520.8 520.9 520.9 520.9 520.9 520.9 521.0 521.0 ][ 381.9 381.9 381.9 381.8 381.8 381.8 381.8 381.8 381.7 +]plong + s[ 268.0 268.0 268.0 268.0 268.0 267.9 267.9 267.9 267.9 267.9 267.8 267.8 267.8 267.8 267.8 ][ 383.0 383.0 383.0 + 382.9 382.9 382.9 382.9 382.9 382.8 382.8 382.8 382.8 382.8 382.7 382.7 ]plong + s[ 268.6 268.6 268.6 268.6 268.6 268.5 268.5 268.5 268.5 268.5 268.4 268.4 268.4 268.4 268.4 268.4 268.4 268.3 + 268.3 268.3 268.3 268.3 268.2 268.2 268.2 268.2 268.2 268.1 268.1 268.1 268.1 268.1 268.1 268.0 ][ 383.7 383.7 + 383.6 383.6 383.6 383.6 383.6 383.5 383.5 383.5 383.5 383.4 383.4 383.4 383.4 383.4 383.3 383.3 383.3 383.3 + 383.3 383.2 383.2 383.2 383.2 383.2 383.1 383.1 383.1 383.1 383.0 383.0 383.0 383.0 ]plong + s[ 519.2 519.3 519.3 519.3 519.3 519.3 519.4 519.4 519.4 519.4 519.4 519.4 519.5 519.5 519.5 519.5 519.5 519.6 + 519.6 519.6 519.6 519.6 519.7 519.7 519.7 519.7 519.7 519.8 519.8 519.8 519.8 519.8 519.8 519.8 ][ 383.7 383.7 + 383.6 383.6 383.6 383.6 383.6 383.5 383.5 383.5 383.5 383.4 383.4 383.4 383.4 383.4 383.3 383.3 383.3 383.3 + 383.3 383.2 383.2 383.2 383.2 383.2 383.1 383.1 383.1 383.1 383.0 383.0 383.0 383.0 ]plong + s[ 519.8 519.9 519.9 519.9 519.9 519.9 520.0 520.0 520.0 520.0 520.0 520.1 520.1 520.1 520.1 ][ 383.0 383.0 383.0 + 382.9 382.9 382.9 382.9 382.9 382.8 382.8 382.8 382.8 382.7 382.7 382.7 ]plong + s[ 269.0 269.0 269.0 269.0 268.9 268.9 268.9 268.9 268.9 268.8 268.8 268.8 268.8 268.8 268.8 268.7 268.7 268.7 + 268.7 268.7 268.6 ][ 384.1 384.1 384.0 384.0 384.0 384.0 384.0 384.0 383.9 383.9 383.9 383.9 383.8 383.8 383.8 + 383.8 383.8 383.7 383.7 383.7 383.7 ]plong + s[ 269.5 269.5 269.5 269.5 269.5 269.4 269.4 269.4 269.4 269.4 269.4 269.3 269.3 269.3 269.3 269.3 269.2 269.2 + 269.2 269.2 269.2 269.2 269.1 269.1 269.1 269.1 269.1 269.0 269.0 ][ 384.7 384.6 384.6 384.6 384.6 384.6 384.5 + 384.5 384.5 384.5 384.5 384.4 384.4 384.4 384.4 384.4 384.3 384.3 384.3 384.3 384.2 384.2 384.2 384.2 384.2 + 384.1 384.1 384.1 384.1 ]plong + s[ 518.3 518.4 518.4 518.4 518.4 518.4 518.5 518.5 518.5 518.5 518.5 518.6 518.6 518.6 518.6 518.6 518.6 518.7 + 518.7 518.7 518.7 518.7 518.8 518.8 518.8 518.8 518.8 518.8 518.9 ][ 384.7 384.6 384.6 384.6 384.6 384.6 384.5 + 384.5 384.5 384.5 384.5 384.4 384.4 384.4 384.4 384.3 384.3 384.3 384.3 384.3 384.2 384.2 384.2 384.2 384.2 + 384.1 384.1 384.1 384.1 ]plong + s[ 518.9 518.9 518.9 518.9 518.9 519.0 519.0 519.0 519.0 519.0 519.0 519.1 519.1 519.1 519.1 519.1 519.2 519.2 + 519.2 519.2 519.2 ][ 384.1 384.1 384.0 384.0 384.0 384.0 384.0 384.0 383.9 383.9 383.9 383.9 383.8 383.8 383.8 + 383.8 383.8 383.7 383.7 383.7 383.7 ]plong + s[ 270.0 270.0 270.0 269.9 269.9 269.9 269.9 269.9 269.8 269.8 269.8 269.8 269.8 269.7 269.7 269.7 269.7 269.7 + 269.6 269.6 269.6 269.6 269.6 269.6 269.5 ][ 385.1 385.1 385.1 385.1 385.1 385.0 385.0 385.0 385.0 385.0 384.9 + 384.9 384.9 384.9 384.9 384.9 384.8 384.8 384.8 384.8 384.7 384.7 384.7 384.7 384.7 ]plong + s[ 270.4 270.4 270.4 270.4 270.4 270.4 270.3 270.3 270.3 270.3 270.3 270.2 270.2 270.2 270.2 270.2 270.1 270.1 + 270.1 270.1 270.1 270.1 270.0 270.0 270.0 ][ 385.6 385.6 385.6 385.6 385.6 385.5 385.5 385.5 385.5 385.5 385.4 + 385.4 385.4 385.4 385.4 385.3 385.3 385.3 385.3 385.2 385.2 385.2 385.2 385.2 385.1 ]plong + s[ 517.4 517.5 517.5 517.5 517.5 517.5 517.6 517.6 517.6 517.6 517.6 517.7 517.7 517.7 517.7 517.7 517.7 517.8 + 517.8 517.8 517.8 517.8 517.8 517.9 517.9 ][ 385.6 385.6 385.6 385.6 385.6 385.5 385.5 385.5 385.5 385.5 385.4 + 385.4 385.4 385.4 385.4 385.3 385.3 385.3 385.3 385.2 385.2 385.2 385.2 385.2 385.1 ]plong + s[ 517.9 517.9 517.9 517.9 518.0 518.0 518.0 518.0 518.0 518.1 518.1 518.1 518.1 518.1 518.2 518.2 518.2 518.2 + 518.2 518.2 518.3 518.3 518.3 518.3 518.3 ][ 385.1 385.1 385.1 385.1 385.1 385.0 385.0 385.0 385.0 385.0 384.9 + 384.9 384.9 384.9 384.9 384.9 384.8 384.8 384.8 384.8 384.7 384.7 384.7 384.7 384.7 ]plong + s[ 271.0 271.0 270.9 270.9 270.9 270.9 270.9 270.8 270.8 270.8 270.8 270.8 270.7 270.7 270.7 270.7 270.7 270.6 + 270.6 270.6 270.6 270.6 270.6 270.5 270.5 270.5 270.5 270.5 270.4 ][ 386.2 386.2 386.2 386.1 386.1 386.1 386.1 + 386.1 386.0 386.0 386.0 386.0 386.0 385.9 385.9 385.9 385.9 385.9 385.8 385.8 385.8 385.8 385.8 385.7 385.7 + 385.7 385.7 385.7 385.6 ]plong + s[ 271.3 271.3 271.3 271.3 271.3 271.3 271.2 271.2 271.2 271.2 271.2 271.2 271.1 271.1 271.1 271.1 271.1 271.0 + 271.0 271.0 271.0 ][ 386.6 386.6 386.6 386.6 386.5 386.5 386.5 386.5 386.4 386.4 386.4 386.4 386.4 386.3 386.3 + 386.3 386.3 386.3 386.2 386.2 386.2 ]plong + s[ 516.5 516.5 516.6 516.6 516.6 516.6 516.6 516.7 516.7 516.7 516.7 516.7 516.8 516.8 516.8 516.8 516.8 516.9 + 516.9 516.9 516.9 ][ 386.6 386.6 386.6 386.5 386.5 386.5 386.5 386.5 386.4 386.4 386.4 386.4 386.4 386.3 386.3 + 386.3 386.3 386.3 386.2 386.2 386.2 ]plong + s[ 516.9 516.9 516.9 517.0 517.0 517.0 517.0 517.0 517.1 517.1 517.1 517.1 517.1 517.2 517.2 517.2 517.2 517.2 + 517.2 517.3 517.3 517.3 517.3 517.3 517.4 517.4 517.4 517.4 517.4 ][ 386.2 386.2 386.2 386.1 386.1 386.1 386.1 + 386.1 386.0 386.0 386.0 386.0 386.0 385.9 385.9 385.9 385.9 385.9 385.8 385.8 385.8 385.8 385.8 385.7 385.7 + 385.7 385.7 385.7 385.6 ]plong + s[ 272.0 271.9 271.9 271.9 271.9 271.9 271.8 271.8 271.8 271.8 271.8 271.7 271.7 271.7 271.7 271.7 271.6 271.6 + 271.6 271.6 271.6 271.5 271.5 271.5 271.5 271.5 271.4 271.4 271.4 271.4 271.4 271.3 ][ 387.2 387.2 387.2 387.2 + 387.1 387.1 387.1 387.1 387.1 387.0 387.0 387.0 387.0 387.0 386.9 386.9 386.9 386.9 386.9 386.9 386.8 386.8 + 386.8 386.8 386.8 386.7 386.7 386.7 386.7 386.7 386.6 386.6 ]plong + s[ 272.3 272.3 272.3 272.2 272.2 272.2 272.2 272.2 272.1 272.1 272.1 272.1 272.1 272.0 272.0 272.0 272.0 272.0 + 272.0 ][ 387.6 387.6 387.6 387.5 387.5 387.5 387.5 387.5 387.4 387.4 387.4 387.4 387.4 387.3 387.3 387.3 387.3 + 387.2 387.2 ]plong + s[ 515.6 515.6 515.6 515.7 515.7 515.7 515.7 515.7 515.8 515.8 515.8 515.8 515.8 515.8 515.9 515.9 515.9 515.9 + 515.9 ][ 387.6 387.6 387.6 387.5 387.5 387.5 387.5 387.5 387.4 387.4 387.4 387.4 387.3 387.3 387.3 387.3 387.3 + 387.2 387.2 ]plong + s[ 515.9 515.9 516.0 516.0 516.0 516.0 516.0 516.1 516.1 516.1 516.1 516.1 516.2 516.2 516.2 516.2 516.2 516.3 + 516.3 516.3 516.3 516.3 516.4 516.4 516.4 516.4 516.4 516.5 516.5 516.5 516.5 516.5 ][ 387.2 387.2 387.2 387.2 + 387.1 387.1 387.1 387.1 387.1 387.0 387.0 387.0 387.0 387.0 386.9 386.9 386.9 386.9 386.9 386.9 386.8 386.8 + 386.8 386.8 386.8 386.7 386.7 386.7 386.7 386.7 386.6 386.6 ]plong + s[ 272.9 272.9 272.9 272.9 272.9 272.8 272.8 272.8 272.8 272.8 272.7 272.7 272.7 272.7 272.7 272.6 272.6 272.6 + 272.6 272.6 272.5 272.5 272.5 272.5 272.5 272.4 272.4 272.4 272.4 272.4 272.3 272.3 272.3 272.3 ][ 388.2 388.2 + 388.2 388.2 388.2 388.1 388.1 388.1 388.1 388.1 388.0 388.0 388.0 388.0 388.0 387.9 387.9 387.9 387.9 387.9 + 387.9 387.8 387.8 387.8 387.8 387.8 387.7 387.7 387.7 387.7 387.7 387.6 387.6 387.6 ]plong + s[ 273.2 273.2 273.2 273.2 273.2 273.2 273.1 273.1 273.1 273.1 273.1 273.0 273.0 273.0 273.0 273.0 272.9 ][ 388.6 + 388.6 388.5 388.5 388.5 388.5 388.4 388.4 388.4 388.4 388.4 388.3 388.3 388.3 388.3 388.3 388.2 ]plong + s[ 514.6 514.6 514.7 514.7 514.7 514.7 514.7 514.8 514.8 514.8 514.8 514.8 514.9 514.9 514.9 514.9 514.9 514.9 ][ + 388.6 388.6 388.5 388.5 388.5 388.5 388.5 388.4 388.4 388.4 388.4 388.4 388.3 388.3 388.3 388.3 388.3 388.2 ]plong + s[ 514.9 515.0 515.0 515.0 515.0 515.0 515.1 515.1 515.1 515.1 515.1 515.2 515.2 515.2 515.2 515.2 515.3 515.3 + 515.3 515.3 515.3 515.4 515.4 515.4 515.4 515.4 515.5 515.5 515.5 515.5 515.5 515.6 515.6 515.6 ][ 388.2 388.2 + 388.2 388.2 388.2 388.1 388.1 388.1 388.1 388.1 388.0 388.0 388.0 388.0 388.0 387.9 387.9 387.9 387.9 387.9 + 387.9 387.8 387.8 387.8 387.8 387.8 387.7 387.7 387.7 387.7 387.7 387.6 387.6 387.6 ]plong + s[ 273.9 273.9 273.9 273.9 273.8 273.8 273.8 273.8 273.8 273.7 273.7 273.7 273.7 273.7 273.6 273.6 273.6 273.6 + 273.6 273.5 273.5 273.5 273.5 273.5 273.4 273.4 273.4 273.4 273.4 273.3 273.3 273.3 273.3 273.3 273.3 273.2 ][ + 389.3 389.2 389.2 389.2 389.2 389.2 389.1 389.1 389.1 389.1 389.1 389.0 389.0 389.0 389.0 389.0 388.9 388.9 + 388.9 388.9 388.9 388.8 388.8 388.8 388.8 388.8 388.8 388.7 388.7 388.7 388.7 388.7 388.6 388.6 388.6 388.6 ]plong + s[ 274.2 274.2 274.2 274.2 274.1 274.1 274.1 274.1 274.1 274.0 274.0 274.0 274.0 274.0 273.9 273.9 ][ 389.6 389.5 + 389.5 389.5 389.5 389.5 389.4 389.4 389.4 389.4 389.4 389.3 389.3 389.3 389.3 389.3 ]plong + s[ 513.7 513.7 513.7 513.7 513.8 513.8 513.8 513.8 513.8 513.9 513.9 513.9 513.9 513.9 514.0 514.0 ][ 389.6 389.5 + 389.5 389.5 389.5 389.5 389.4 389.4 389.4 389.4 389.4 389.3 389.3 389.3 389.3 389.3 ]plong + s[ 514.0 514.0 514.0 514.0 514.0 514.1 514.1 514.1 514.1 514.1 514.2 514.2 514.2 514.2 514.2 514.3 514.3 514.3 + 514.3 514.3 514.4 514.4 514.4 514.4 514.4 514.5 514.5 514.5 514.5 514.5 514.5 514.6 514.6 514.6 514.6 ][ 389.3 + 389.2 389.2 389.2 389.2 389.2 389.1 389.1 389.1 389.1 389.1 389.0 389.0 389.0 389.0 389.0 388.9 388.9 388.9 + 388.9 388.9 388.8 388.8 388.8 388.8 388.8 388.7 388.7 388.7 388.7 388.7 388.6 388.6 388.6 388.6 ]plong + s[ 274.9 274.9 274.9 274.8 274.8 274.8 274.8 274.8 274.7 274.7 274.7 274.7 274.7 274.6 274.6 274.6 274.6 274.6 + 274.5 274.5 274.5 274.5 274.5 274.4 274.4 274.4 274.4 274.4 274.3 274.3 274.3 274.3 274.3 274.2 274.2 274.2 ][ + 390.2 390.2 390.2 390.2 390.2 390.1 390.1 390.1 390.1 390.1 390.0 390.0 390.0 390.0 390.0 389.9 389.9 389.9 + 389.9 389.9 389.8 389.8 389.8 389.8 389.8 389.8 389.7 389.7 389.7 389.7 389.7 389.6 389.6 389.6 389.6 389.6 ]plong + s[ 275.2 275.2 275.2 275.1 275.1 275.1 275.1 275.1 275.0 275.0 275.0 275.0 275.0 274.9 274.9 274.9 ][ 390.5 390.5 + 390.5 390.5 390.5 390.4 390.4 390.4 390.4 390.4 390.3 390.3 390.3 390.3 390.3 390.2 ]plong + s[ 512.7 512.7 512.7 512.8 512.8 512.8 512.8 512.8 512.9 512.9 512.9 512.9 512.9 513.0 513.0 513.0 ][ 390.5 390.5 + 390.5 390.5 390.5 390.4 390.4 390.4 390.4 390.4 390.3 390.3 390.3 390.3 390.3 390.2 ]plong + s[ 513.0 513.0 513.0 513.0 513.1 513.1 513.1 513.1 513.1 513.2 513.2 513.2 513.2 513.2 513.3 513.3 513.3 513.3 + 513.3 513.4 513.4 513.4 513.4 513.4 513.5 513.5 513.5 513.5 513.5 513.6 513.6 513.6 513.6 513.6 513.7 513.7 ][ + 390.2 390.2 390.2 390.2 390.2 390.1 390.1 390.1 390.1 390.1 390.0 390.0 390.0 390.0 390.0 389.9 389.9 389.9 + 389.9 389.9 389.8 389.8 389.8 389.8 389.8 389.8 389.7 389.7 389.7 389.7 389.7 389.6 389.6 389.6 389.6 389.6 ]plong + s[ 275.9 275.9 275.8 275.8 275.8 275.8 275.8 275.7 275.7 275.7 275.7 275.7 275.6 275.6 275.6 275.6 275.6 275.5 + 275.5 275.5 275.5 275.5 275.4 275.4 275.4 275.4 275.4 275.3 275.3 275.3 275.3 275.3 275.2 275.2 275.2 275.2 ][ + 391.2 391.2 391.2 391.2 391.1 391.1 391.1 391.1 391.1 391.0 391.0 391.0 391.0 391.0 390.9 390.9 390.9 390.9 + 390.9 390.8 390.8 390.8 390.8 390.8 390.8 390.7 390.7 390.7 390.7 390.7 390.6 390.6 390.6 390.6 390.6 390.5 ]plong + s[ 276.2 276.2 276.2 276.2 276.1 276.1 276.1 276.1 276.1 276.0 276.0 276.0 276.0 276.0 275.9 275.9 275.9 ][ 391.5 + 391.5 391.5 391.5 391.4 391.4 391.4 391.4 391.4 391.3 391.3 391.3 391.3 391.3 391.3 391.2 391.2 ]plong + s[ 511.7 511.7 511.7 511.7 511.8 511.8 511.8 511.8 511.8 511.9 511.9 511.9 511.9 511.9 512.0 512.0 512.0 512.0 ][ + 391.5 391.5 391.5 391.5 391.4 391.4 391.4 391.4 391.4 391.3 391.3 391.3 391.3 391.3 391.2 391.2 391.2 391.2 ]plong + s[ 512.0 512.0 512.0 512.1 512.1 512.1 512.1 512.1 512.2 512.2 512.2 512.2 512.3 512.3 512.3 512.3 512.3 512.4 + 512.4 512.4 512.4 512.4 512.5 512.5 512.5 512.5 512.5 512.6 512.6 512.6 512.6 512.6 512.7 512.7 512.7 ][ 391.2 + 391.2 391.1 391.1 391.1 391.1 391.1 391.0 391.0 391.0 391.0 391.0 391.0 390.9 390.9 390.9 390.9 390.9 390.8 + 390.8 390.8 390.8 390.8 390.8 390.7 390.7 390.7 390.7 390.7 390.6 390.6 390.6 390.6 390.6 390.5 ]plong + s[ 276.9 276.8 276.8 276.8 276.8 276.8 276.7 276.7 276.7 276.7 276.7 276.6 276.6 276.6 276.6 276.6 276.5 276.5 + 276.5 276.5 276.5 276.4 276.4 276.4 276.4 276.4 276.3 276.3 276.3 276.3 276.3 276.2 276.2 276.2 ][ 392.2 392.1 + 392.1 392.1 392.1 392.1 392.0 392.0 392.0 392.0 392.0 391.9 391.9 391.9 391.9 391.9 391.8 391.8 391.8 391.8 + 391.8 391.7 391.7 391.7 391.7 391.7 391.7 391.6 391.6 391.6 391.6 391.6 391.5 391.5 ]plong + s[ 277.2 277.2 277.2 277.2 277.1 277.1 277.1 277.1 277.1 277.0 277.0 277.0 277.0 277.0 276.9 276.9 276.9 276.9 + 276.9 ][ 392.5 392.5 392.5 392.4 392.4 392.4 392.4 392.4 392.4 392.3 392.3 392.3 392.3 392.3 392.2 392.2 392.2 + 392.2 392.2 ]plong + s[ 510.7 510.7 510.7 510.7 510.7 510.8 510.8 510.8 510.8 510.8 510.9 510.9 510.9 510.9 511.0 511.0 511.0 511.0 + 511.0 ][ 392.5 392.5 392.5 392.4 392.4 392.4 392.4 392.4 392.3 392.3 392.3 392.3 392.3 392.3 392.2 392.2 392.2 + 392.2 392.2 ]plong + s[ 511.0 511.0 511.1 511.1 511.1 511.1 511.1 511.2 511.2 511.2 511.2 511.2 511.3 511.3 511.3 511.3 511.3 511.4 + 511.4 511.4 511.4 511.4 511.5 511.5 511.5 511.5 511.5 511.6 511.6 511.6 511.6 511.6 511.7 511.7 ][ 392.2 392.1 + 392.1 392.1 392.1 392.1 392.0 392.0 392.0 392.0 392.0 391.9 391.9 391.9 391.9 391.9 391.8 391.8 391.8 391.8 + 391.8 391.7 391.7 391.7 391.7 391.7 391.7 391.6 391.6 391.6 391.6 391.6 391.5 391.5 ]plong + s[ 277.8 277.8 277.8 277.8 277.8 277.7 277.7 277.7 277.7 277.7 277.6 277.6 277.6 277.6 277.6 277.5 277.5 277.5 + 277.5 277.5 277.4 277.4 277.4 277.4 277.4 277.3 277.3 277.3 277.3 277.3 277.2 277.2 ][ 393.1 393.1 393.1 393.0 + 393.0 393.0 393.0 393.0 392.9 392.9 392.9 392.9 392.9 392.8 392.8 392.8 392.8 392.8 392.7 392.7 392.7 392.7 + 392.7 392.7 392.6 392.6 392.6 392.6 392.6 392.5 392.5 392.5 ]plong + s[ 278.2 278.2 278.2 278.2 278.2 278.1 278.1 278.1 278.1 278.1 278.1 278.0 278.0 278.0 278.0 277.9 277.9 277.9 + 277.9 277.9 277.8 ][ 393.5 393.5 393.4 393.4 393.4 393.4 393.4 393.3 393.3 393.3 393.3 393.3 393.2 393.2 393.2 + 393.2 393.2 393.2 393.1 393.1 393.1 ]plong + s[ 509.6 509.7 509.7 509.7 509.7 509.7 509.8 509.8 509.8 509.8 509.8 509.9 509.9 509.9 509.9 509.9 510.0 510.0 + 510.0 510.0 510.1 ][ 393.5 393.5 393.4 393.4 393.4 393.4 393.4 393.3 393.3 393.3 393.3 393.3 393.2 393.2 393.2 + 393.2 393.2 393.1 393.1 393.1 393.1 ]plong + s[ 510.1 510.1 510.1 510.1 510.1 510.1 510.2 510.2 510.2 510.2 510.2 510.3 510.3 510.3 510.3 510.3 510.4 510.4 + 510.4 510.4 510.4 510.5 510.5 510.5 510.5 510.5 510.6 510.6 510.6 510.6 510.6 510.7 ][ 393.1 393.1 393.1 393.0 + 393.0 393.0 393.0 393.0 392.9 392.9 392.9 392.9 392.9 392.8 392.8 392.8 392.8 392.8 392.7 392.7 392.7 392.7 + 392.7 392.7 392.6 392.6 392.6 392.6 392.6 392.5 392.5 392.5 ]plong + s[ 278.8 278.8 278.8 278.8 278.7 278.7 278.7 278.7 278.7 278.6 278.6 278.6 278.6 278.5 278.5 278.5 278.5 278.5 + 278.4 278.4 278.4 278.4 278.4 278.3 278.3 278.3 278.3 278.3 278.2 ][ 394.0 394.0 394.0 393.9 393.9 393.9 393.9 + 393.9 393.8 393.8 393.8 393.8 393.8 393.8 393.7 393.7 393.7 393.7 393.7 393.7 393.6 393.6 393.6 393.6 393.6 + 393.5 393.5 393.5 393.5 ]plong + s[ 279.3 279.3 279.3 279.2 279.2 279.2 279.2 279.1 279.1 279.1 279.1 279.1 279.1 279.0 279.0 279.0 279.0 279.0 + 278.9 278.9 278.9 278.9 278.9 278.8 278.8 ][ 394.5 394.4 394.4 394.4 394.4 394.4 394.4 394.3 394.3 394.3 394.3 + 394.3 394.2 394.2 394.2 394.2 394.2 394.1 394.1 394.1 394.1 394.1 394.0 394.0 394.0 ]plong + s[ 508.6 508.6 508.6 508.6 508.7 508.7 508.7 508.7 508.7 508.8 508.8 508.8 508.8 508.9 508.9 508.9 508.9 508.9 + 509.0 509.0 509.0 509.0 509.0 509.1 509.1 ][ 394.5 394.4 394.4 394.4 394.4 394.4 394.3 394.3 394.3 394.3 394.3 + 394.3 394.2 394.2 394.2 394.2 394.2 394.1 394.1 394.1 394.1 394.1 394.0 394.0 394.0 ]plong + s[ 509.1 509.1 509.1 509.1 509.1 509.2 509.2 509.2 509.2 509.2 509.3 509.3 509.3 509.3 509.3 509.4 509.4 509.4 + 509.4 509.5 509.5 509.5 509.5 509.5 509.6 509.6 509.6 509.6 509.6 ][ 394.0 394.0 394.0 393.9 393.9 393.9 393.9 + 393.9 393.8 393.8 393.8 393.8 393.8 393.7 393.7 393.7 393.7 393.7 393.7 393.7 393.6 393.6 393.6 393.6 393.6 + 393.5 393.5 393.5 393.5 ]plong + s[ 279.8 279.8 279.8 279.7 279.7 279.7 279.7 279.6 279.6 279.6 279.6 279.6 279.5 279.5 279.5 279.5 279.5 279.4 + 279.4 279.4 279.4 279.4 279.3 279.3 279.3 ][ 394.9 394.9 394.9 394.8 394.8 394.8 394.8 394.8 394.8 394.7 394.7 + 394.7 394.7 394.7 394.6 394.6 394.6 394.6 394.6 394.6 394.5 394.5 394.5 394.5 394.5 ]plong + s[ 280.4 280.3 280.3 280.3 280.3 280.3 280.2 280.2 280.2 280.2 280.1 280.1 280.1 280.1 280.1 280.1 280.0 280.0 + 280.0 280.0 280.0 279.9 279.9 279.9 279.9 279.9 279.8 279.8 279.8 ][ 395.4 395.4 395.4 395.4 395.4 395.3 395.3 + 395.3 395.3 395.3 395.3 395.2 395.2 395.2 395.2 395.2 395.1 395.1 395.1 395.1 395.1 395.0 395.0 395.0 395.0 + 395.0 394.9 394.9 394.9 ]plong + s[ 507.5 507.5 507.6 507.6 507.6 507.6 507.6 507.7 507.7 507.7 507.7 507.7 507.8 507.8 507.8 507.8 507.9 507.9 + 507.9 507.9 507.9 508.0 508.0 508.0 508.0 508.0 508.1 508.1 508.1 ][ 395.4 395.4 395.4 395.4 395.4 395.3 395.3 + 395.3 395.3 395.3 395.2 395.2 395.2 395.2 395.2 395.2 395.1 395.1 395.1 395.1 395.1 395.0 395.0 395.0 395.0 + 395.0 394.9 394.9 394.9 ]plong + s[ 508.1 508.1 508.1 508.1 508.2 508.2 508.2 508.2 508.2 508.3 508.3 508.3 508.3 508.4 508.4 508.4 508.4 508.4 + 508.5 508.5 508.5 508.5 508.5 508.6 508.6 ][ 394.9 394.9 394.9 394.8 394.8 394.8 394.8 394.8 394.7 394.7 394.7 + 394.7 394.7 394.7 394.6 394.6 394.6 394.6 394.6 394.6 394.5 394.5 394.5 394.5 394.5 ]plong + s[ 280.8 280.8 280.7 280.7 280.7 280.7 280.7 280.6 280.6 280.6 280.6 280.5 280.5 280.5 280.5 280.5 280.4 280.4 + 280.4 280.4 280.4 ][ 395.8 395.8 395.8 395.7 395.7 395.7 395.7 395.7 395.6 395.6 395.6 395.6 395.6 395.6 395.6 + 395.5 395.5 395.5 395.5 395.5 395.4 ]plong + s[ 281.5 281.4 281.4 281.4 281.4 281.3 281.3 281.3 281.3 281.3 281.2 281.2 281.2 281.2 281.2 281.1 281.1 281.1 + 281.1 281.1 281.0 281.0 281.0 281.0 281.0 280.9 280.9 280.9 280.9 280.9 280.8 280.8 280.8 280.8 ][ 396.4 396.4 + 396.4 396.4 396.4 396.3 396.3 396.3 396.3 396.3 396.2 396.2 396.2 396.2 396.2 396.1 396.1 396.1 396.1 396.1 + 396.0 396.0 396.0 396.0 396.0 396.0 395.9 395.9 395.9 395.9 395.9 395.8 395.8 395.8 ]plong + s[ 506.4 506.4 506.5 506.5 506.5 506.5 506.6 506.6 506.6 506.6 506.6 506.7 506.7 506.7 506.7 506.7 506.8 506.8 + 506.8 506.8 506.8 506.9 506.9 506.9 506.9 507.0 507.0 507.0 507.0 507.0 507.1 507.1 507.1 507.1 ][ 396.4 396.4 + 396.4 396.4 396.3 396.3 396.3 396.3 396.3 396.3 396.2 396.2 396.2 396.2 396.2 396.1 396.1 396.1 396.1 396.1 + 396.0 396.0 396.0 396.0 396.0 396.0 395.9 395.9 395.9 395.9 395.9 395.8 395.8 395.8 ]plong + s[ 507.1 507.1 507.2 507.2 507.2 507.2 507.2 507.2 507.3 507.3 507.3 507.3 507.3 507.4 507.4 507.4 507.4 507.5 + 507.5 507.5 507.5 ][ 395.8 395.8 395.8 395.7 395.7 395.7 395.7 395.7 395.6 395.6 395.6 395.6 395.6 395.6 395.6 + 395.5 395.5 395.5 395.5 395.5 395.4 ]plong + s[ 281.8 281.7 281.7 281.7 281.7 281.6 281.6 281.6 281.6 281.6 281.5 281.5 281.5 281.5 281.5 ][ 396.7 396.7 396.6 + 396.6 396.6 396.6 396.6 396.6 396.5 396.5 396.5 396.5 396.5 396.4 396.4 ]plong + s[ 282.6 282.5 282.5 282.5 282.5 282.5 282.4 282.4 282.4 282.4 282.3 282.3 282.3 282.3 282.3 282.2 282.2 282.2 + 282.2 282.2 282.1 282.1 282.1 282.1 282.0 282.0 282.0 282.0 282.0 282.0 281.9 281.9 281.9 281.9 281.9 281.8 + 281.8 281.8 281.8 281.8 ][ 397.4 397.4 397.4 397.3 397.3 397.3 397.3 397.3 397.3 397.2 397.2 397.2 397.2 397.2 + 397.1 397.1 397.1 397.1 397.1 397.0 397.0 397.0 397.0 397.0 397.0 396.9 396.9 396.9 396.9 396.9 396.8 396.8 + 396.8 396.8 396.8 396.7 396.7 396.7 396.7 396.7 ]plong + s[ 505.3 505.3 505.4 505.4 505.4 505.4 505.4 505.5 505.5 505.5 505.5 505.6 505.6 505.6 505.6 505.6 505.7 505.7 + 505.7 505.7 505.7 505.8 505.8 505.8 505.8 505.9 505.9 505.9 505.9 505.9 506.0 506.0 506.0 506.0 506.0 506.1 + 506.1 506.1 506.1 506.1 ][ 397.4 397.4 397.4 397.3 397.3 397.3 397.3 397.3 397.2 397.2 397.2 397.2 397.2 397.2 + 397.1 397.1 397.1 397.1 397.1 397.0 397.0 397.0 397.0 397.0 396.9 396.9 396.9 396.9 396.9 396.9 396.8 396.8 + 396.8 396.8 396.8 396.7 396.7 396.7 396.7 396.7 ]plong + s[ 506.1 506.2 506.2 506.2 506.2 506.2 506.3 506.3 506.3 506.3 506.3 506.4 506.4 506.4 506.4 ][ 396.7 396.6 396.6 + 396.6 396.6 396.6 396.6 396.6 396.5 396.5 396.5 396.5 396.5 396.4 396.4 ]plong + s[ 282.7 282.7 282.7 282.7 282.6 282.6 282.6 282.6 282.6 ][ 397.5 397.5 397.5 397.5 397.5 397.5 397.4 397.4 397.4 +]plong + s[ 283.7 283.7 283.7 283.6 283.6 283.6 283.6 283.5 283.5 283.5 283.5 283.5 283.4 283.4 283.4 283.4 283.4 283.3 + 283.3 283.3 283.3 283.2 283.2 283.2 283.2 283.2 283.1 283.1 283.1 283.1 283.1 283.0 283.0 283.0 283.0 283.0 + 282.9 282.9 282.9 282.9 282.9 282.8 282.8 282.8 282.8 282.8 282.7 ][ 398.4 398.4 398.3 398.3 398.3 398.3 398.3 + 398.3 398.2 398.2 398.2 398.2 398.2 398.1 398.1 398.1 398.1 398.1 398.1 398.0 398.0 398.0 398.0 398.0 397.9 + 397.9 397.9 397.9 397.9 397.8 397.8 397.8 397.8 397.8 397.8 397.7 397.7 397.7 397.7 397.7 397.6 397.6 397.6 + 397.6 397.6 397.6 397.5 ]plong + s[ 504.2 504.2 504.2 504.2 504.3 504.3 504.3 504.3 504.4 504.4 504.4 504.4 504.4 504.5 504.5 504.5 504.5 504.5 + 504.6 504.6 504.6 504.6 504.7 504.7 504.7 504.7 504.7 504.8 504.8 504.8 504.8 504.8 504.9 504.9 504.9 504.9 + 505.0 505.0 505.0 505.0 505.0 505.1 505.1 505.1 505.1 505.1 505.2 ][ 398.4 398.4 398.3 398.3 398.3 398.3 398.3 + 398.3 398.2 398.2 398.2 398.2 398.2 398.1 398.1 398.1 398.1 398.1 398.0 398.0 398.0 398.0 398.0 398.0 397.9 + 397.9 397.9 397.9 397.9 397.8 397.8 397.8 397.8 397.8 397.8 397.7 397.7 397.7 397.7 397.7 397.6 397.6 397.6 + 397.6 397.6 397.6 397.5 ]plong + s[ 505.2 505.2 505.2 505.2 505.2 505.3 505.3 505.3 505.3 ][ 397.5 397.5 397.5 397.5 397.5 397.5 397.4 397.4 397.4 +]plong + 283.7 398.4 283.7 398.4 2 pls + s[ 283.7 283.7 283.8 283.8 283.8 283.8 283.8 283.9 283.9 283.9 283.9 284.0 284.0 284.0 284.0 284.0 284.0 284.1 + 284.1 284.1 284.1 284.1 284.2 284.2 284.2 284.2 284.3 284.3 284.3 284.3 284.3 284.4 284.4 284.4 284.4 284.5 + 284.5 284.5 284.5 284.5 284.6 284.6 284.6 284.6 284.6 284.7 284.7 284.7 ][ 398.4 398.4 398.4 398.5 398.5 398.5 + 398.5 398.5 398.5 398.6 398.6 398.6 398.6 398.6 398.6 398.7 398.7 398.7 398.7 398.7 398.8 398.8 398.8 398.8 + 398.8 398.8 398.9 398.9 398.9 398.9 398.9 399.0 399.0 399.0 399.0 399.0 399.0 399.1 399.1 399.1 399.1 399.1 + 399.2 399.2 399.2 399.2 399.2 399.3 ]plong + s[ 284.8 284.8 284.8 284.8 284.8 284.7 284.7 ][ 399.4 399.3 399.3 399.3 399.3 399.3 399.3 ]plong + s[ 503.1 503.1 503.1 503.1 503.1 503.2 503.2 503.2 ][ 399.4 399.3 399.3 399.3 399.3 399.3 399.2 399.2 ]plong + s[ 503.2 503.2 503.3 503.3 503.3 503.3 503.3 503.3 503.4 503.4 503.4 503.4 503.5 503.5 503.5 503.5 503.5 503.6 + 503.6 503.6 503.6 503.6 503.7 503.7 503.7 503.7 503.8 503.8 503.8 503.8 503.8 503.9 503.9 503.9 503.9 503.9 + 504.0 504.0 504.0 504.0 504.1 504.1 504.1 504.1 504.1 504.2 504.2 ][ 399.2 399.2 399.2 399.2 399.2 399.1 399.1 + 399.1 399.1 399.1 399.0 399.0 399.0 399.0 399.0 399.0 398.9 398.9 398.9 398.9 398.9 398.8 398.8 398.8 398.8 + 398.8 398.8 398.7 398.7 398.7 398.7 398.7 398.6 398.6 398.6 398.6 398.6 398.5 398.5 398.5 398.5 398.5 398.5 + 398.5 398.4 398.4 398.4 ]plong + 504.2 398.4 504.2 398.4 2 pls + s[ 285.7 285.7 285.6 285.6 285.6 285.6 285.6 285.5 285.5 285.5 285.5 285.4 285.4 285.4 285.4 285.4 285.3 285.3 + 285.3 285.3 285.3 285.2 285.2 285.2 285.2 285.1 285.1 285.1 285.1 285.1 285.0 285.0 285.0 285.0 284.9 284.9 + 284.9 284.9 284.9 284.9 284.8 ][ 400.1 400.1 400.0 400.0 400.0 400.0 400.0 399.9 399.9 399.9 399.9 399.9 399.9 + 399.8 399.8 399.8 399.8 399.8 399.7 399.7 399.7 399.7 399.7 399.7 399.6 399.6 399.6 399.6 399.6 399.5 399.5 + 399.5 399.5 399.5 399.5 399.5 399.4 399.4 399.4 399.4 399.4 ]plong + s[ 286.0 286.0 286.0 285.9 285.9 285.9 285.9 285.9 285.8 285.8 285.8 285.8 285.8 285.7 285.7 285.7 ][ 400.3 400.3 + 400.3 400.3 400.3 400.3 400.2 400.2 400.2 400.2 400.2 400.1 400.1 400.1 400.1 400.1 ]plong + s[ 501.9 501.9 501.9 501.9 502.0 502.0 502.0 502.0 502.1 502.1 502.1 502.1 502.1 502.2 502.2 502.2 ][ 400.3 400.3 + 400.3 400.3 400.3 400.3 400.2 400.2 400.2 400.2 400.2 400.1 400.1 400.1 400.1 400.1 ]plong + s[ 502.2 502.2 502.3 502.3 502.3 502.3 502.3 502.3 502.4 502.4 502.4 502.4 502.5 502.5 502.5 502.5 502.5 502.6 + 502.6 502.6 502.6 502.6 502.7 502.7 502.7 502.7 502.8 502.8 502.8 502.8 502.8 502.9 502.9 502.9 502.9 503.0 + 503.0 503.0 503.0 503.0 503.1 ][ 400.1 400.1 400.0 400.0 400.0 400.0 400.0 399.9 399.9 399.9 399.9 399.9 399.9 + 399.8 399.8 399.8 399.8 399.8 399.7 399.7 399.7 399.7 399.7 399.6 399.6 399.6 399.6 399.6 399.6 399.5 399.5 + 399.5 399.5 399.5 399.5 399.5 399.4 399.4 399.4 399.4 399.4 ]plong + s[ 286.7 286.6 286.6 286.6 286.6 286.5 286.5 286.5 286.5 286.5 286.4 286.4 286.4 286.4 286.3 286.3 286.3 286.3 + 286.3 286.2 286.2 286.2 286.2 286.1 286.1 286.1 286.1 286.1 286.0 286.0 286.0 ][ 400.9 400.9 400.8 400.8 400.8 + 400.8 400.8 400.7 400.7 400.7 400.7 400.7 400.7 400.6 400.6 400.6 400.6 400.6 400.5 400.5 400.5 400.5 400.5 + 400.5 400.5 400.4 400.4 400.4 400.4 400.4 400.3 ]plong + s[ 287.2 287.2 287.1 287.1 287.1 287.1 287.0 287.0 287.0 287.0 287.0 286.9 286.9 286.9 286.9 286.9 286.9 286.8 + 286.8 286.8 286.8 286.7 286.7 286.7 286.7 286.7 ][ 401.3 401.3 401.3 401.3 401.2 401.2 401.2 401.2 401.2 401.2 + 401.1 401.1 401.1 401.1 401.1 401.0 401.0 401.0 401.0 401.0 401.0 400.9 400.9 400.9 400.9 400.9 ]plong + s[ 500.7 500.7 500.7 500.8 500.8 500.8 500.8 500.8 500.9 500.9 500.9 500.9 501.0 501.0 501.0 501.0 501.0 501.1 + 501.1 501.1 501.1 501.2 501.2 501.2 501.2 501.2 ][ 401.3 401.3 401.3 401.3 401.2 401.2 401.2 401.2 401.2 401.2 + 401.1 401.1 401.1 401.1 401.1 401.0 401.0 401.0 401.0 401.0 401.0 400.9 400.9 400.9 400.9 400.9 ]plong + s[ 501.2 501.3 501.3 501.3 501.3 501.3 501.4 501.4 501.4 501.4 501.4 501.5 501.5 501.5 501.5 501.6 501.6 501.6 + 501.6 501.6 501.7 501.7 501.7 501.7 501.8 501.8 501.8 501.8 501.8 501.9 501.9 ][ 400.9 400.8 400.8 400.8 400.8 + 400.8 400.8 400.7 400.7 400.7 400.7 400.7 400.6 400.6 400.6 400.6 400.6 400.6 400.5 400.5 400.5 400.5 400.5 + 400.5 400.5 400.4 400.4 400.4 400.4 400.4 400.3 ]plong + s[ 287.6 287.6 287.6 287.6 287.6 287.5 287.5 287.5 287.5 287.4 287.4 287.4 287.4 287.4 287.3 287.3 287.3 287.3 + 287.2 287.2 287.2 287.2 ][ 401.7 401.7 401.6 401.6 401.6 401.6 401.6 401.6 401.5 401.5 401.5 401.5 401.5 401.4 + 401.4 401.4 401.4 401.4 401.4 401.4 401.3 401.3 ]plong + s[ 288.4 288.4 288.4 288.3 288.3 288.3 288.3 288.2 288.2 288.2 288.2 288.2 288.1 288.1 288.1 288.1 288.0 288.0 + 288.0 288.0 288.0 287.9 287.9 287.9 287.9 287.8 287.8 287.8 287.8 287.8 287.8 287.7 287.7 287.7 287.7 287.6 ][ + 402.3 402.3 402.3 402.2 402.2 402.2 402.2 402.2 402.2 402.1 402.1 402.1 402.1 402.1 402.0 402.0 402.0 402.0 + 402.0 402.0 401.9 401.9 401.9 401.9 401.9 401.9 401.8 401.8 401.8 401.8 401.8 401.7 401.7 401.7 401.7 401.7 ]plong + s[ 499.5 499.5 499.5 499.5 499.6 499.6 499.6 499.6 499.7 499.7 499.7 499.7 499.7 499.8 499.8 499.8 499.8 499.9 + 499.9 499.9 499.9 499.9 500.0 500.0 500.0 500.0 500.1 500.1 500.1 500.1 500.1 500.2 500.2 500.2 500.2 500.3 ][ + 402.3 402.3 402.3 402.2 402.2 402.2 402.2 402.2 402.2 402.1 402.1 402.1 402.1 402.1 402.0 402.0 402.0 402.0 + 402.0 402.0 401.9 401.9 401.9 401.9 401.9 401.9 401.8 401.8 401.8 401.8 401.8 401.7 401.7 401.7 401.7 401.7 ]plong + s[ 500.3 500.3 500.3 500.3 500.3 500.4 500.4 500.4 500.4 500.4 500.5 500.5 500.5 500.5 500.5 500.6 500.6 500.6 + 500.6 500.7 500.7 500.7 ][ 401.7 401.7 401.6 401.6 401.6 401.6 401.6 401.5 401.5 401.5 401.5 401.5 401.5 401.4 + 401.4 401.4 401.4 401.4 401.4 401.4 401.3 401.3 ]plong + s[ 288.6 288.6 288.6 288.6 288.5 288.5 288.5 288.5 288.4 288.4 288.4 ][ 402.5 402.4 402.4 402.4 402.4 402.4 402.4 + 402.4 402.3 402.3 402.3 ]plong + s[ 288.6 288.6 288.7 288.7 288.7 288.7 288.8 288.8 288.8 288.8 288.8 288.8 288.9 288.9 288.9 288.9 289.0 289.0 + 289.0 289.0 289.0 289.1 289.1 289.1 289.1 289.2 289.2 289.2 289.2 289.2 289.3 289.3 289.3 289.3 289.4 289.4 + 289.4 289.4 289.4 289.5 289.5 289.5 289.5 289.6 289.6 289.6 ][ 402.5 402.5 402.5 402.5 402.5 402.5 402.6 402.6 + 402.6 402.6 402.6 402.7 402.7 402.7 402.7 402.7 402.7 402.8 402.8 402.8 402.8 402.8 402.8 402.9 402.9 402.9 + 402.9 402.9 403.0 403.0 403.0 403.0 403.0 403.0 403.1 403.1 403.1 403.1 403.1 403.1 403.2 403.2 403.2 403.2 + 403.2 403.3 ]plong + 289.6 403.3 289.6 403.3 289.7 403.3 3 pls + 498.3 403.3 498.3 403.3 498.2 403.3 3 pls + s[ 498.3 498.3 498.3 498.4 498.4 498.4 498.4 498.4 498.4 498.5 498.5 498.5 498.5 498.6 498.6 498.6 498.6 498.7 + 498.7 498.7 498.7 498.7 498.8 498.8 498.8 498.8 498.9 498.9 498.9 498.9 498.9 499.0 499.0 499.0 499.0 499.1 + 499.1 499.1 499.1 499.1 499.2 499.2 499.2 499.2 499.3 499.3 ][ 403.3 403.2 403.2 403.2 403.2 403.2 403.1 403.1 + 403.1 403.1 403.1 403.1 403.0 403.0 403.0 403.0 403.0 403.0 402.9 402.9 402.9 402.9 402.9 402.8 402.8 402.8 + 402.8 402.8 402.8 402.7 402.7 402.7 402.7 402.7 402.7 402.6 402.6 402.6 402.6 402.6 402.5 402.5 402.5 402.5 + 402.5 402.5 ]plong + s[ 499.3 499.3 499.3 499.3 499.4 499.4 499.4 499.4 499.4 499.5 499.5 ][ 402.5 402.4 402.4 402.4 402.4 402.4 402.4 + 402.4 402.3 402.3 402.3 ]plong + s[ 290.6 290.6 290.5 290.5 290.5 290.5 290.4 290.4 290.4 290.4 290.3 290.3 290.3 290.3 290.3 290.2 290.2 290.2 + 290.2 290.1 290.1 290.1 290.1 290.1 290.0 290.0 290.0 290.0 289.9 289.9 289.9 289.9 289.9 289.8 289.8 289.8 + 289.8 289.8 289.7 289.7 289.7 289.7 289.7 ][ 404.0 404.0 404.0 404.0 403.9 403.9 403.9 403.9 403.9 403.8 403.8 + 403.8 403.8 403.8 403.8 403.7 403.7 403.7 403.7 403.7 403.7 403.6 403.6 403.6 403.6 403.6 403.6 403.5 403.5 + 403.5 403.5 403.5 403.4 403.4 403.4 403.4 403.4 403.4 403.4 403.3 403.3 403.3 403.3 ]plong + s[ 290.9 290.9 290.8 290.8 290.8 290.8 290.8 290.7 290.7 290.7 290.7 290.7 290.6 290.6 290.6 290.6 ][ 404.3 404.2 + 404.2 404.2 404.2 404.2 404.2 404.1 404.1 404.1 404.1 404.1 404.1 404.0 404.0 404.0 ]plong + s[ 497.0 497.0 497.0 497.1 497.1 497.1 497.1 497.1 497.2 497.2 497.2 497.2 497.3 497.3 497.3 497.3 ][ 404.3 404.2 + 404.2 404.2 404.2 404.2 404.2 404.1 404.1 404.1 404.1 404.1 404.1 404.0 404.0 404.0 ]plong + s[ 497.3 497.3 497.4 497.4 497.4 497.4 497.4 497.5 497.5 497.5 497.5 497.5 497.6 497.6 497.6 497.6 497.7 497.7 + 497.7 497.7 497.8 497.8 497.8 497.8 497.8 497.9 497.9 497.9 497.9 498.0 498.0 498.0 498.0 498.0 498.1 498.1 + 498.1 498.1 498.2 498.2 498.2 498.2 498.2 ][ 404.0 404.0 404.0 404.0 403.9 403.9 403.9 403.9 403.9 403.8 403.8 + 403.8 403.8 403.8 403.8 403.7 403.7 403.7 403.7 403.7 403.7 403.6 403.6 403.6 403.6 403.6 403.5 403.5 403.5 + 403.5 403.5 403.5 403.4 403.4 403.4 403.4 403.4 403.4 403.4 403.3 403.3 403.3 403.3 ]plong + s[ 291.6 291.5 291.5 291.5 291.5 291.4 291.4 291.4 291.4 291.3 291.3 291.3 291.3 291.3 291.2 291.2 291.2 291.2 + 291.1 291.1 291.1 291.1 291.1 291.0 291.0 291.0 291.0 290.9 290.9 290.9 ][ 404.7 404.7 404.7 404.7 404.7 404.7 + 404.6 404.6 404.6 404.6 404.6 404.6 404.5 404.5 404.5 404.5 404.5 404.5 404.4 404.4 404.4 404.4 404.4 404.4 + 404.4 404.3 404.3 404.3 404.3 404.3 ]plong + s[ 292.2 292.1 292.1 292.1 292.1 292.1 292.0 292.0 292.0 292.0 291.9 291.9 291.9 291.9 291.9 291.8 291.8 291.8 + 291.8 291.7 291.7 291.7 291.7 291.7 291.6 291.6 291.6 291.6 291.6 ][ 405.2 405.2 405.2 405.2 405.2 405.1 405.1 + 405.1 405.1 405.1 405.1 405.0 405.0 405.0 405.0 405.0 405.0 404.9 404.9 404.9 404.9 404.9 404.9 404.8 404.8 + 404.8 404.8 404.8 404.7 ]plong + s[ 495.7 495.7 495.7 495.8 495.8 495.8 495.8 495.8 495.9 495.9 495.9 495.9 496.0 496.0 496.0 496.0 496.1 496.1 + 496.1 496.1 496.1 496.2 496.2 496.2 496.2 496.3 496.3 496.3 496.3 496.3 ][ 405.2 405.2 405.2 405.2 405.2 405.2 + 405.1 405.1 405.1 405.1 405.1 405.1 405.0 405.0 405.0 405.0 405.0 405.0 404.9 404.9 404.9 404.9 404.9 404.9 + 404.8 404.8 404.8 404.8 404.8 404.7 ]plong + s[ 496.3 496.4 496.4 496.4 496.4 496.5 496.5 496.5 496.5 496.5 496.6 496.6 496.6 496.6 496.6 496.7 496.7 496.7 + 496.7 496.8 496.8 496.8 496.8 496.8 496.9 496.9 496.9 496.9 497.0 497.0 ][ 404.7 404.7 404.7 404.7 404.7 404.7 + 404.6 404.6 404.6 404.6 404.6 404.6 404.5 404.5 404.5 404.5 404.5 404.5 404.4 404.4 404.4 404.4 404.4 404.4 + 404.4 404.3 404.3 404.3 404.3 404.3 ]plong + s[ 292.5 292.5 292.5 292.5 292.4 292.4 292.4 292.4 292.3 292.3 292.3 292.3 292.3 292.2 292.2 292.2 292.2 ][ 405.5 + 405.5 405.5 405.4 405.4 405.4 405.4 405.4 405.4 405.3 405.3 405.3 405.3 405.3 405.3 405.3 405.2 ]plong + s[ 293.5 293.5 293.5 293.4 293.4 293.4 293.4 293.3 293.3 293.3 293.3 293.2 293.2 293.2 293.2 293.2 293.1 293.1 + 293.1 293.1 293.0 293.0 293.0 293.0 292.9 292.9 292.9 292.9 292.9 292.8 292.8 292.8 292.8 292.7 292.7 292.7 + 292.7 292.7 292.6 292.6 292.6 292.6 292.6 292.5 ][ 406.2 406.2 406.2 406.2 406.2 406.1 406.1 406.1 406.1 406.1 + 406.1 406.0 406.0 406.0 406.0 406.0 406.0 405.9 405.9 405.9 405.9 405.9 405.8 405.8 405.8 405.8 405.8 405.8 + 405.7 405.7 405.7 405.7 405.7 405.7 405.6 405.6 405.6 405.6 405.6 405.6 405.5 405.5 405.5 405.5 ]plong + s[ 494.4 494.4 494.4 494.5 494.5 494.5 494.5 494.5 494.6 494.6 494.6 494.6 494.7 494.7 494.7 494.7 494.7 494.8 + 494.8 494.8 494.8 494.9 494.9 494.9 494.9 495.0 495.0 495.0 495.0 495.0 495.1 495.1 495.1 495.1 495.2 495.2 + 495.2 495.2 495.2 495.3 495.3 495.3 495.3 495.4 ][ 406.2 406.2 406.2 406.2 406.2 406.1 406.1 406.1 406.1 406.1 + 406.0 406.0 406.0 406.0 406.0 406.0 405.9 405.9 405.9 405.9 405.9 405.9 405.8 405.8 405.8 405.8 405.8 405.8 + 405.7 405.7 405.7 405.7 405.7 405.7 405.6 405.6 405.6 405.6 405.6 405.6 405.5 405.5 405.5 405.5 ]plong + s[ 495.4 495.4 495.4 495.4 495.5 495.5 495.5 495.5 495.5 495.5 495.6 495.6 495.6 495.6 495.7 495.7 ][ 405.5 405.5 + 405.4 405.4 405.4 405.4 405.4 405.4 405.3 405.3 405.3 405.3 405.3 405.3 405.3 405.2 ]plong + 293.5 406.2 293.5 406.2 2 pls + s[ 293.5 293.5 293.6 293.6 293.6 293.6 293.7 293.7 293.7 293.7 293.7 293.8 293.8 293.8 293.8 293.8 293.9 293.9 + 293.9 293.9 294.0 294.0 294.0 294.0 294.1 294.1 294.1 294.1 294.1 294.2 294.2 294.2 294.2 294.3 294.3 294.3 + 294.3 294.4 294.4 294.4 294.4 294.4 294.5 294.5 ][ 406.2 406.3 406.3 406.3 406.3 406.3 406.3 406.3 406.4 406.4 + 406.4 406.4 406.4 406.4 406.5 406.5 406.5 406.5 406.5 406.5 406.6 406.6 406.6 406.6 406.6 406.6 406.7 406.7 + 406.7 406.7 406.7 406.7 406.8 406.8 406.8 406.8 406.8 406.8 406.9 406.9 406.9 406.9 406.9 406.9 ]plong + s[ 294.8 294.8 294.8 294.8 294.7 294.7 294.7 294.7 294.6 294.6 294.6 294.6 294.6 294.5 294.5 294.5 ][ 407.2 407.2 + 407.2 407.1 407.1 407.1 407.1 407.1 407.1 407.1 407.0 407.0 407.0 407.0 407.0 406.9 ]plong + s[ 493.1 493.1 493.1 493.1 493.2 493.2 493.2 493.2 493.2 493.3 493.3 493.3 493.3 493.4 493.4 493.4 ][ 407.2 407.2 + 407.2 407.1 407.1 407.1 407.1 407.1 407.1 407.0 407.0 407.0 407.0 407.0 407.0 406.9 ]plong + s[ 493.4 493.4 493.5 493.5 493.5 493.5 493.5 493.6 493.6 493.6 493.6 493.6 493.7 493.7 493.7 493.7 493.8 493.8 + 493.8 493.8 493.8 493.9 493.9 493.9 493.9 494.0 494.0 494.0 494.0 494.1 494.1 494.1 494.1 494.1 494.2 494.2 + 494.2 494.2 494.3 494.3 494.3 494.3 494.4 494.4 ][ 406.9 406.9 406.9 406.9 406.9 406.9 406.8 406.8 406.8 406.8 + 406.8 406.8 406.7 406.7 406.7 406.7 406.7 406.7 406.6 406.6 406.6 406.6 406.6 406.6 406.5 406.5 406.5 406.5 + 406.5 406.5 406.4 406.4 406.4 406.4 406.4 406.4 406.3 406.3 406.3 406.3 406.3 406.3 406.3 406.2 ]plong + 494.4 406.2 494.4 406.2 2 pls + s[ 295.5 295.4 295.4 295.4 295.4 295.4 295.3 295.3 295.3 295.3 295.2 295.2 295.2 295.2 295.1 295.1 295.1 295.1 + 295.1 295.0 295.0 295.0 295.0 294.9 294.9 294.9 294.9 294.8 294.8 ][ 407.6 407.6 407.6 407.6 407.6 407.6 407.6 + 407.5 407.5 407.5 407.5 407.5 407.4 407.4 407.4 407.4 407.4 407.4 407.3 407.3 407.3 407.3 407.3 407.3 407.3 + 407.3 407.2 407.2 407.2 ]plong + s[ 296.2 296.2 296.1 296.1 296.1 296.1 296.1 296.0 296.0 296.0 296.0 295.9 295.9 295.9 295.9 295.8 295.8 295.8 + 295.8 295.8 295.7 295.7 295.7 295.7 295.6 295.6 295.6 295.6 295.6 295.5 295.5 295.5 295.5 ][ 408.2 408.2 408.2 + 408.1 408.1 408.1 408.1 408.1 408.0 408.0 408.0 408.0 408.0 408.0 408.0 407.9 407.9 407.9 407.9 407.9 407.8 + 407.8 407.8 407.8 407.8 407.8 407.8 407.7 407.7 407.7 407.7 407.7 407.6 ]plong + s[ 491.7 491.7 491.7 491.7 491.8 491.8 491.8 491.8 491.9 491.9 491.9 491.9 492.0 492.0 492.0 492.0 492.1 492.1 + 492.1 492.1 492.1 492.2 492.2 492.2 492.2 492.3 492.3 492.3 492.3 492.4 492.4 492.4 492.4 ][ 408.2 408.2 408.1 + 408.1 408.1 408.1 408.1 408.1 408.0 408.0 408.0 408.0 408.0 408.0 407.9 407.9 407.9 407.9 407.9 407.9 407.8 + 407.8 407.8 407.8 407.8 407.8 407.7 407.7 407.7 407.7 407.7 407.7 407.6 ]plong + s[ 492.4 492.5 492.5 492.5 492.5 492.5 492.6 492.6 492.6 492.6 492.6 492.7 492.7 492.7 492.7 492.8 492.8 492.8 + 492.8 492.8 492.9 492.9 492.9 492.9 493.0 493.0 493.0 493.0 493.1 ][ 407.6 407.6 407.6 407.6 407.6 407.6 407.5 + 407.5 407.5 407.5 407.5 407.5 407.4 407.4 407.4 407.4 407.4 407.4 407.3 407.3 407.3 407.3 407.3 407.3 407.3 + 407.3 407.2 407.2 407.2 ]plong + s[ 296.5 296.4 296.4 296.4 296.4 296.3 296.3 296.3 296.3 296.2 296.2 296.2 ][ 408.3 408.3 408.3 408.3 408.3 408.3 + 408.2 408.2 408.2 408.2 408.2 408.2 ]plong + s[ 296.5 296.5 296.5 296.5 296.5 296.6 296.6 296.6 296.6 296.6 296.7 296.7 296.7 296.7 296.8 296.8 296.8 296.8 + 296.9 296.9 296.9 296.9 297.0 297.0 297.0 297.0 297.0 297.1 297.1 297.1 297.1 297.2 297.2 297.2 297.2 297.3 + 297.3 297.3 297.3 297.4 297.4 297.4 297.4 297.4 ][ 408.3 408.4 408.4 408.4 408.4 408.4 408.4 408.5 408.5 408.5 + 408.5 408.5 408.5 408.6 408.6 408.6 408.6 408.6 408.6 408.7 408.7 408.7 408.7 408.7 408.7 408.8 408.8 408.8 + 408.8 408.8 408.8 408.9 408.9 408.9 408.9 408.9 408.9 409.0 409.0 409.0 409.0 409.0 409.0 409.1 ]plong + s[ 297.6 297.5 297.5 297.5 297.5 297.5 297.4 ][ 409.2 409.1 409.1 409.1 409.1 409.1 409.1 ]plong + s[ 490.3 490.3 490.3 490.4 490.4 490.4 490.4 490.5 ][ 409.2 409.1 409.1 409.1 409.1 409.1 409.1 409.1 ]plong + s[ 490.5 490.5 490.5 490.5 490.5 490.6 490.6 490.6 490.6 490.7 490.7 490.7 490.7 490.7 490.8 490.8 490.8 490.8 + 490.9 490.9 490.9 490.9 491.0 491.0 491.0 491.0 491.0 491.1 491.1 491.1 491.1 491.2 491.2 491.2 491.2 491.3 + 491.3 491.3 491.3 491.4 491.4 491.4 491.4 491.4 ][ 409.1 409.0 409.0 409.0 409.0 409.0 409.0 408.9 408.9 408.9 + 408.9 408.9 408.9 408.8 408.8 408.8 408.8 408.8 408.8 408.7 408.7 408.7 408.7 408.7 408.7 408.6 408.6 408.6 + 408.6 408.6 408.6 408.5 408.5 408.5 408.5 408.5 408.5 408.4 408.4 408.4 408.4 408.4 408.4 408.3 ]plong + s[ 491.4 491.5 491.5 491.5 491.5 491.6 491.6 491.6 491.6 491.6 491.7 491.7 ][ 408.3 408.3 408.3 408.3 408.3 408.3 + 408.2 408.2 408.2 408.2 408.2 408.2 ]plong + s[ 298.4 298.4 298.4 298.3 298.3 298.3 298.3 298.3 298.2 298.2 298.2 298.2 298.1 298.1 298.1 298.1 298.0 298.0 + 298.0 298.0 297.9 297.9 297.9 297.9 297.9 297.8 297.8 297.8 297.8 297.7 297.7 297.7 297.7 297.6 297.6 297.6 + 297.6 ][ 409.7 409.7 409.7 409.7 409.7 409.6 409.6 409.6 409.6 409.6 409.6 409.5 409.5 409.5 409.5 409.5 409.5 + 409.4 409.4 409.4 409.4 409.4 409.4 409.3 409.3 409.3 409.3 409.3 409.3 409.2 409.2 409.2 409.2 409.2 409.2 + 409.2 409.2 ]plong + s[ 299.0 299.0 299.0 298.9 298.9 298.9 298.9 298.8 298.8 298.8 298.8 298.8 298.7 298.7 298.7 298.7 298.6 298.6 + 298.6 298.6 298.5 298.5 298.5 298.5 298.5 298.4 298.4 ][ 410.1 410.1 410.1 410.1 410.1 410.1 410.0 410.0 410.0 + 410.0 410.0 410.0 410.0 409.9 409.9 409.9 409.9 409.9 409.8 409.8 409.8 409.8 409.8 409.8 409.8 409.7 409.7 ]plong + s[ 488.9 488.9 488.9 488.9 489.0 489.0 489.0 489.0 489.1 489.1 489.1 489.1 489.1 489.2 489.2 489.2 489.2 489.3 + 489.3 489.3 489.3 489.4 489.4 489.4 489.4 489.5 489.5 ][ 410.1 410.1 410.1 410.1 410.1 410.1 410.0 410.0 410.0 + 410.0 410.0 410.0 409.9 409.9 409.9 409.9 409.9 409.9 409.8 409.8 409.8 409.8 409.8 409.8 409.8 409.7 409.7 ]plong + s[ 489.5 489.5 489.5 489.6 489.6 489.6 489.6 489.6 489.7 489.7 489.7 489.7 489.7 489.8 489.8 489.8 489.8 489.9 + 489.9 489.9 489.9 490.0 490.0 490.0 490.0 490.1 490.1 490.1 490.1 490.1 490.2 490.2 490.2 490.2 490.3 490.3 ][ + 409.7 409.7 409.7 409.7 409.7 409.6 409.6 409.6 409.6 409.6 409.6 409.5 409.5 409.5 409.5 409.5 409.5 409.4 + 409.4 409.4 409.4 409.4 409.4 409.3 409.3 409.3 409.3 409.3 409.3 409.2 409.2 409.2 409.2 409.2 409.2 409.2 ]plong + s[ 299.4 299.4 299.4 299.3 299.3 299.3 299.3 299.2 299.2 299.2 299.2 299.1 299.1 299.1 299.1 299.0 299.0 ][ 410.4 + 410.4 410.3 410.3 410.3 410.3 410.3 410.3 410.3 410.2 410.2 410.2 410.2 410.2 410.2 410.2 410.1 ]plong + s[ 299.4 299.4 299.4 299.5 299.5 299.5 299.5 299.5 299.6 299.6 299.6 299.6 299.7 299.7 299.7 299.7 299.8 299.8 + 299.8 299.8 299.9 299.9 299.9 299.9 299.9 300.0 300.0 300.0 300.0 300.1 300.1 300.1 300.1 300.2 300.2 300.2 + 300.2 300.3 300.3 300.3 300.3 300.4 300.4 ][ 410.4 410.4 410.4 410.4 410.4 410.5 410.5 410.5 410.5 410.5 410.5 + 410.6 410.6 410.6 410.6 410.6 410.6 410.7 410.7 410.7 410.7 410.7 410.7 410.8 410.8 410.8 410.8 410.8 410.8 + 410.9 410.9 410.9 410.9 410.9 410.9 410.9 411.0 411.0 411.0 411.0 411.0 411.0 411.1 ]plong + 300.4 411.1 300.4 411.1 300.4 411.1 300.4 411.1 300.5 411.1 5 pls + 487.5 411.1 487.5 411.1 487.5 411.1 487.4 411.1 487.4 411.1 5 pls + s[ 487.5 487.5 487.6 487.6 487.6 487.6 487.7 487.7 487.7 487.7 487.7 487.8 487.8 487.8 487.8 487.9 487.9 487.9 + 487.9 487.9 488.0 488.0 488.0 488.0 488.1 488.1 488.1 488.1 488.2 488.2 488.2 488.2 488.3 488.3 488.3 488.3 + 488.4 488.4 488.4 488.4 488.5 488.5 488.5 ][ 411.1 411.0 411.0 411.0 411.0 411.0 411.0 410.9 410.9 410.9 410.9 + 410.9 410.9 410.8 410.8 410.8 410.8 410.8 410.8 410.7 410.7 410.7 410.7 410.7 410.7 410.7 410.6 410.6 410.6 + 410.6 410.6 410.6 410.5 410.5 410.5 410.5 410.5 410.5 410.4 410.4 410.4 410.4 410.4 ]plong + s[ 488.5 488.5 488.5 488.6 488.6 488.6 488.6 488.7 488.7 488.7 488.7 488.7 488.8 488.8 488.8 488.8 488.9 ][ 410.4 + 410.4 410.3 410.3 410.3 410.3 410.3 410.3 410.3 410.2 410.2 410.2 410.2 410.2 410.2 410.2 410.1 ]plong + s[ 301.4 301.3 301.3 301.3 301.3 301.2 301.2 301.2 301.2 301.1 301.1 301.1 301.1 301.0 301.0 301.0 301.0 300.9 + 300.9 300.9 300.9 300.8 300.8 300.8 300.8 300.8 300.7 300.7 300.7 300.6 300.6 300.6 300.6 300.6 300.5 300.5 + 300.5 300.5 ][ 411.7 411.7 411.7 411.6 411.6 411.6 411.6 411.6 411.6 411.5 411.5 411.5 411.5 411.5 411.5 411.4 + 411.4 411.4 411.4 411.4 411.4 411.4 411.3 411.3 411.3 411.3 411.3 411.3 411.2 411.2 411.2 411.2 411.2 411.2 + 411.2 411.1 411.1 411.1 ]plong + s[ 302.0 301.9 301.9 301.9 301.9 301.8 301.8 301.8 301.8 301.7 301.7 301.7 301.7 301.6 301.6 301.6 301.6 301.5 + 301.5 301.5 301.5 301.5 301.4 301.4 301.4 301.4 301.4 ][ 412.1 412.1 412.1 412.1 412.0 412.0 412.0 412.0 412.0 + 412.0 411.9 411.9 411.9 411.9 411.9 411.9 411.8 411.8 411.8 411.8 411.8 411.8 411.8 411.7 411.7 411.7 411.7 ]plong + s[ 485.9 485.9 486.0 486.0 486.0 486.0 486.1 486.1 486.1 486.1 486.2 486.2 486.2 486.2 486.3 486.3 486.3 486.3 + 486.4 486.4 486.4 486.4 486.5 486.5 486.5 486.5 486.5 ][ 412.1 412.1 412.1 412.0 412.0 412.0 412.0 412.0 412.0 + 412.0 411.9 411.9 411.9 411.9 411.9 411.9 411.8 411.8 411.8 411.8 411.8 411.8 411.8 411.7 411.7 411.7 411.7 ]plong + s[ 486.5 486.6 486.6 486.6 486.6 486.7 486.7 486.7 486.7 486.8 486.8 486.8 486.8 486.8 486.9 486.9 486.9 486.9 + 487.0 487.0 487.0 487.0 487.1 487.1 487.1 487.1 487.2 487.2 487.2 487.2 487.2 487.3 487.3 487.3 487.3 487.4 + 487.4 487.4 ][ 411.7 411.7 411.7 411.6 411.6 411.6 411.6 411.6 411.6 411.5 411.5 411.5 411.5 411.5 411.5 411.4 + 411.4 411.4 411.4 411.4 411.4 411.4 411.3 411.3 411.3 411.3 411.3 411.3 411.2 411.2 411.2 411.2 411.2 411.2 + 411.1 411.1 411.1 411.1 ]plong + s[ 302.3 302.3 302.3 302.3 302.3 302.2 302.2 302.2 302.2 302.1 302.1 302.1 302.1 302.0 302.0 302.0 302.0 ][ 412.3 + 412.3 412.3 412.3 412.3 412.3 412.2 412.2 412.2 412.2 412.2 412.2 412.1 412.1 412.1 412.1 412.1 ]plong + s[ 302.3 302.4 302.4 302.4 302.4 302.4 302.5 302.5 302.5 302.5 302.6 302.6 302.6 302.6 302.7 302.7 302.7 302.7 + 302.8 302.8 302.8 302.8 302.9 302.9 302.9 302.9 303.0 303.0 303.0 303.0 303.1 303.1 303.1 303.1 303.2 303.2 + 303.2 303.2 303.3 303.3 303.3 303.3 ][ 412.3 412.3 412.4 412.4 412.4 412.4 412.4 412.4 412.5 412.5 412.5 412.5 + 412.5 412.5 412.5 412.6 412.6 412.6 412.6 412.6 412.6 412.7 412.7 412.7 412.7 412.7 412.7 412.8 412.8 412.8 + 412.8 412.8 412.8 412.8 412.9 412.9 412.9 412.9 412.9 412.9 413.0 413.0 ]plong + s[ 303.5 303.5 303.4 303.4 303.4 303.4 303.4 303.3 ][ 413.1 413.1 413.1 413.0 413.0 413.0 413.0 413.0 ]plong + s[ 484.4 484.4 484.5 484.5 484.5 484.5 484.5 484.6 ][ 413.1 413.1 413.1 413.0 413.0 413.0 413.0 413.0 ]plong + s[ 484.6 484.6 484.6 484.6 484.7 484.7 484.7 484.7 484.8 484.8 484.8 484.8 484.8 484.9 484.9 484.9 484.9 485.0 + 485.0 485.0 485.0 485.1 485.1 485.1 485.1 485.2 485.2 485.2 485.2 485.3 485.3 485.3 485.3 485.4 485.4 485.4 + 485.4 485.5 485.5 485.5 485.5 485.6 ][ 413.0 413.0 412.9 412.9 412.9 412.9 412.9 412.9 412.8 412.8 412.8 412.8 + 412.8 412.8 412.7 412.7 412.7 412.7 412.7 412.7 412.7 412.6 412.6 412.6 412.6 412.6 412.6 412.5 412.5 412.5 + 412.5 412.5 412.5 412.5 412.4 412.4 412.4 412.4 412.4 412.4 412.3 412.3 ]plong + s[ 485.6 485.6 485.6 485.6 485.6 485.7 485.7 485.7 485.7 485.8 485.8 485.8 485.8 485.8 485.9 485.9 485.9 ][ 412.3 + 412.3 412.3 412.3 412.3 412.3 412.2 412.2 412.2 412.2 412.2 412.2 412.1 412.1 412.1 412.1 412.1 ]plong + s[ 304.3 304.3 304.2 304.2 304.2 304.2 304.1 304.1 304.1 304.1 304.0 304.0 304.0 304.0 303.9 303.9 303.9 303.9 + 303.8 303.8 303.8 303.8 303.7 303.7 303.7 303.7 303.7 303.6 303.6 303.6 303.6 303.5 303.5 303.5 ][ 413.6 413.6 + 413.5 413.5 413.5 413.5 413.5 413.5 413.4 413.4 413.4 413.4 413.4 413.4 413.4 413.3 413.3 413.3 413.3 413.3 + 413.3 413.2 413.2 413.2 413.2 413.2 413.2 413.2 413.1 413.1 413.1 413.1 413.1 413.1 ]plong + s[ 305.1 305.0 305.0 305.0 305.0 304.9 304.9 304.9 304.9 304.8 304.8 304.8 304.8 304.7 304.7 304.7 304.7 304.6 + 304.6 304.6 304.6 304.5 304.5 304.5 304.5 304.4 304.4 304.4 304.4 304.3 304.3 304.3 304.3 ][ 414.1 414.0 414.0 + 414.0 414.0 414.0 414.0 414.0 413.9 413.9 413.9 413.9 413.9 413.9 413.8 413.8 413.8 413.8 413.8 413.8 413.8 + 413.7 413.7 413.7 413.7 413.7 413.7 413.6 413.6 413.6 413.6 413.6 413.6 ]plong + s[ 482.8 482.9 482.9 482.9 482.9 482.9 483.0 483.0 483.0 483.0 483.1 483.1 483.1 483.1 483.2 483.2 483.2 483.2 + 483.3 483.3 483.3 483.3 483.4 483.4 483.4 483.4 483.5 483.5 483.5 483.5 483.6 483.6 483.6 ][ 414.1 414.0 414.0 + 414.0 414.0 414.0 414.0 414.0 413.9 413.9 413.9 413.9 413.9 413.9 413.8 413.8 413.8 413.8 413.8 413.8 413.8 + 413.7 413.7 413.7 413.7 413.7 413.7 413.6 413.6 413.6 413.6 413.6 413.6 ]plong + s[ 483.6 483.6 483.7 483.7 483.7 483.7 483.8 483.8 483.8 483.8 483.9 483.9 483.9 483.9 483.9 484.0 484.0 484.0 + 484.0 484.1 484.1 484.1 484.1 484.2 484.2 484.2 484.2 484.3 484.3 484.3 484.3 484.3 484.4 484.4 ][ 413.6 413.6 + 413.5 413.5 413.5 413.5 413.5 413.5 413.4 413.4 413.4 413.4 413.4 413.4 413.4 413.3 413.3 413.3 413.3 413.3 + 413.3 413.2 413.2 413.2 413.2 413.2 413.2 413.2 413.1 413.1 413.1 413.1 413.1 413.1 ]plong + s[ 305.3 305.3 305.2 305.2 305.2 305.2 305.1 305.1 305.1 305.1 ][ 414.2 414.2 414.1 414.1 414.1 414.1 414.1 414.1 + 414.1 414.1 ]plong + s[ 305.3 305.3 305.3 305.3 305.4 305.4 305.4 305.4 305.5 305.5 305.5 305.5 305.6 305.6 305.6 305.6 305.7 305.7 + 305.7 305.7 305.8 305.8 305.8 305.8 305.9 305.9 305.9 305.9 306.0 306.0 306.0 306.0 306.1 306.1 306.1 306.1 + 306.2 306.2 306.2 306.2 306.3 ][ 414.2 414.2 414.2 414.2 414.2 414.3 414.3 414.3 414.3 414.3 414.3 414.3 414.4 + 414.4 414.4 414.4 414.4 414.4 414.5 414.5 414.5 414.5 414.5 414.5 414.5 414.6 414.6 414.6 414.6 414.6 414.6 + 414.7 414.7 414.7 414.7 414.7 414.7 414.7 414.8 414.8 414.8 ]plong + s[ 306.7 306.6 306.6 306.6 306.6 306.5 306.5 306.5 306.5 306.4 306.4 306.4 306.4 306.3 306.3 306.3 306.3 306.3 ][ + 415.0 415.0 415.0 415.0 415.0 415.0 415.0 414.9 414.9 414.9 414.9 414.9 414.9 414.9 414.8 414.8 414.8 414.8 ]plong + s[ 481.2 481.2 481.3 481.3 481.3 481.3 481.4 481.4 481.4 481.4 481.5 481.5 481.5 481.5 481.6 481.6 481.6 481.6 ][ + 415.0 415.0 415.0 415.0 415.0 415.0 414.9 414.9 414.9 414.9 414.9 414.9 414.9 414.8 414.8 414.8 414.8 414.8 ]plong + s[ 481.6 481.7 481.7 481.7 481.7 481.8 481.8 481.8 481.8 481.9 481.9 481.9 481.9 481.9 482.0 482.0 482.0 482.1 + 482.1 482.1 482.1 482.1 482.2 482.2 482.2 482.2 482.3 482.3 482.3 482.3 482.4 482.4 482.4 482.4 482.5 482.5 + 482.5 482.5 482.6 482.6 482.6 ][ 414.8 414.8 414.8 414.7 414.7 414.7 414.7 414.7 414.7 414.7 414.6 414.6 414.6 + 414.6 414.6 414.6 414.5 414.5 414.5 414.5 414.5 414.5 414.5 414.4 414.4 414.4 414.4 414.4 414.4 414.3 414.3 + 414.3 414.3 414.3 414.3 414.3 414.2 414.2 414.2 414.2 414.2 ]plong + s[ 482.6 482.6 482.7 482.7 482.7 482.7 482.8 482.8 482.8 482.8 ][ 414.2 414.2 414.1 414.1 414.1 414.1 414.1 414.1 + 414.1 414.1 ]plong + s[ 307.2 307.2 307.2 307.2 307.1 307.1 307.1 307.1 307.0 307.0 307.0 307.0 306.9 306.9 306.9 306.9 306.8 306.8 + 306.8 306.8 306.7 306.7 306.7 306.7 ][ 415.4 415.4 415.3 415.3 415.3 415.3 415.3 415.3 415.2 415.2 415.2 415.2 + 415.2 415.2 415.2 415.1 415.1 415.1 415.1 415.1 415.1 415.1 415.0 415.0 ]plong + s[ 307.2 307.2 307.3 307.3 307.3 307.3 307.4 307.4 307.4 307.4 307.5 307.5 307.5 307.5 307.6 307.6 307.6 307.6 + 307.7 307.7 307.7 307.7 307.8 307.8 307.8 307.8 307.9 307.9 307.9 307.9 308.0 308.0 308.0 308.0 308.1 308.1 + 308.1 308.1 308.2 308.2 308.2 ][ 415.4 415.4 415.4 415.4 415.4 415.4 415.5 415.5 415.5 415.5 415.5 415.5 415.6 + 415.6 415.6 415.6 415.6 415.6 415.6 415.7 415.7 415.7 415.7 415.7 415.7 415.7 415.8 415.8 415.8 415.8 415.8 + 415.8 415.8 415.9 415.9 415.9 415.9 415.9 415.9 416.0 416.0 ]plong + 308.2 416.0 308.2 416.0 308.2 416.0 308.3 416.0 308.3 416.0 5 pls + 479.7 416.0 479.7 416.0 479.6 416.0 479.6 416.0 479.6 416.0 5 pls + s[ 479.7 479.7 479.7 479.8 479.8 479.8 479.8 479.9 479.9 479.9 479.9 480.0 480.0 480.0 480.0 480.0 480.1 480.1 + 480.1 480.1 480.2 480.2 480.2 480.2 480.3 480.3 480.3 480.3 480.4 480.4 480.4 480.4 480.5 480.5 480.5 480.5 + 480.6 480.6 480.6 480.6 480.7 ][ 416.0 416.0 415.9 415.9 415.9 415.9 415.9 415.9 415.8 415.8 415.8 415.8 415.8 + 415.8 415.8 415.7 415.7 415.7 415.7 415.7 415.7 415.6 415.6 415.6 415.6 415.6 415.6 415.6 415.6 415.5 415.5 + 415.5 415.5 415.5 415.5 415.4 415.4 415.4 415.4 415.4 415.4 ]plong + s[ 480.7 480.7 480.7 480.7 480.8 480.8 480.8 480.8 480.9 480.9 480.9 480.9 481.0 481.0 481.0 481.0 481.0 481.1 + 481.1 481.1 481.2 481.2 481.2 481.2 ][ 415.4 415.4 415.3 415.3 415.3 415.3 415.3 415.3 415.2 415.2 415.2 415.2 + 415.2 415.2 415.2 415.1 415.1 415.1 415.1 415.1 415.1 415.1 415.0 415.0 ]plong + s[ 309.2 309.2 309.2 309.1 309.1 309.1 309.0 309.0 309.0 309.0 309.0 308.9 308.9 308.9 308.8 308.8 308.8 308.8 + 308.8 308.7 308.7 308.7 308.6 308.6 308.6 308.6 308.5 308.5 308.5 308.5 308.4 308.4 308.4 308.4 308.3 308.3 + 308.3 ][ 416.5 416.5 416.5 416.5 416.5 416.5 416.5 416.4 416.4 416.4 416.4 416.4 416.4 416.3 416.3 416.3 416.3 + 416.3 416.3 416.3 416.2 416.2 416.2 416.2 416.2 416.2 416.2 416.1 416.1 416.1 416.1 416.1 416.1 416.1 416.0 + 416.0 416.0 ]plong + s[ 310.0 310.0 309.9 309.9 309.9 309.9 309.8 309.8 309.8 309.8 309.7 309.7 309.7 309.7 309.6 309.6 309.6 309.6 + 309.5 309.5 309.5 309.5 309.4 309.4 309.4 309.4 309.3 309.3 309.3 309.3 309.2 309.2 309.2 ][ 417.0 417.0 417.0 + 417.0 416.9 416.9 416.9 416.9 416.9 416.9 416.9 416.8 416.8 416.8 416.8 416.8 416.8 416.8 416.7 416.7 416.7 + 416.7 416.7 416.7 416.7 416.6 416.6 416.6 416.6 416.6 416.6 416.5 416.5 ]plong + s[ 477.9 477.9 478.0 478.0 478.0 478.0 478.0 478.1 478.1 478.1 478.1 478.2 478.2 478.2 478.2 478.3 478.3 478.3 + 478.3 478.4 478.4 478.4 478.4 478.5 478.5 478.5 478.5 478.6 478.6 478.6 478.6 478.7 478.7 ][ 417.0 417.0 417.0 + 417.0 416.9 416.9 416.9 416.9 416.9 416.9 416.9 416.8 416.8 416.8 416.8 416.8 416.8 416.8 416.7 416.7 416.7 + 416.7 416.7 416.7 416.7 416.6 416.6 416.6 416.6 416.6 416.6 416.5 416.5 ]plong + s[ 478.7 478.7 478.7 478.8 478.8 478.8 478.8 478.9 478.9 478.9 478.9 479.0 479.0 479.0 479.0 479.1 479.1 479.1 + 479.1 479.2 479.2 479.2 479.2 479.3 479.3 479.3 479.3 479.4 479.4 479.4 479.4 479.5 479.5 479.5 479.5 479.6 + 479.6 ][ 416.5 416.5 416.5 416.5 416.5 416.5 416.4 416.4 416.4 416.4 416.4 416.4 416.4 416.3 416.3 416.3 416.3 + 416.3 416.3 416.3 416.2 416.2 416.2 416.2 416.2 416.2 416.1 416.1 416.1 416.1 416.1 416.1 416.1 416.1 416.0 + 416.0 416.0 ]plong + s[ 310.2 310.1 310.1 310.1 310.1 310.0 310.0 310.0 ][ 417.1 417.1 417.1 417.0 417.0 417.0 417.0 417.0 ]plong + s[ 310.2 310.2 310.2 310.2 310.3 310.3 310.3 310.3 310.4 310.4 310.4 310.4 310.5 310.5 310.5 310.5 310.6 310.6 + 310.6 310.6 310.7 310.7 310.7 310.7 310.8 310.8 310.8 310.8 310.9 310.9 310.9 310.9 311.0 311.0 311.0 311.0 + 311.1 311.1 311.1 311.1 ][ 417.1 417.1 417.1 417.1 417.1 417.2 417.2 417.2 417.2 417.2 417.2 417.2 417.3 417.3 + 417.3 417.3 417.3 417.3 417.4 417.4 417.4 417.4 417.4 417.4 417.4 417.4 417.5 417.5 417.5 417.5 417.5 417.5 + 417.6 417.6 417.6 417.6 417.6 417.6 417.6 417.6 ]plong + s[ 311.7 311.7 311.7 311.6 311.6 311.6 311.6 311.5 311.5 311.5 311.5 311.4 311.4 311.4 311.4 311.3 311.3 311.3 + 311.3 311.2 311.2 311.2 311.2 311.1 ][ 418.0 417.9 417.9 417.9 417.9 417.9 417.9 417.9 417.9 417.8 417.8 417.8 + 417.8 417.8 417.8 417.8 417.8 417.7 417.7 417.7 417.7 417.7 417.7 417.6 ]plong + s[ 476.2 476.2 476.2 476.2 476.3 476.3 476.3 476.3 476.4 476.4 476.4 476.4 476.5 476.5 476.5 476.5 476.6 476.6 + 476.6 476.6 476.7 476.7 476.7 476.7 ][ 418.0 417.9 417.9 417.9 417.9 417.9 417.9 417.9 417.9 417.8 417.8 417.8 + 417.8 417.8 417.8 417.8 417.8 417.7 417.7 417.7 417.7 417.7 417.7 417.6 ]plong + s[ 476.7 476.8 476.8 476.8 476.9 476.9 476.9 476.9 477.0 477.0 477.0 477.0 477.1 477.1 477.1 477.1 477.1 477.2 + 477.2 477.2 477.2 477.3 477.3 477.3 477.3 477.4 477.4 477.4 477.4 477.5 477.5 477.5 477.5 477.6 477.6 477.6 + 477.6 477.7 477.7 477.7 ][ 417.6 417.6 417.6 417.6 417.6 417.6 417.6 417.6 417.5 417.5 417.5 417.5 417.5 417.5 + 417.4 417.4 417.4 417.4 417.4 417.4 417.4 417.3 417.3 417.3 417.3 417.3 417.3 417.3 417.2 417.2 417.2 417.2 + 417.2 417.2 417.2 417.1 417.1 417.1 417.1 417.1 ]plong + s[ 477.7 477.8 477.8 477.8 477.8 477.8 477.9 477.9 ][ 417.1 417.1 417.1 417.0 417.0 417.0 417.0 417.0 ]plong + s[ 312.1 312.1 312.1 312.1 312.0 312.0 312.0 311.9 311.9 311.9 311.9 311.8 311.8 311.8 311.8 311.7 311.7 ][ 418.2 + 418.2 418.2 418.1 418.1 418.1 418.1 418.1 418.1 418.1 418.1 418.0 418.0 418.0 418.0 418.0 418.0 ]plong + s[ 312.1 312.1 312.2 312.2 312.2 312.2 312.3 312.3 312.3 312.3 312.4 312.4 312.4 312.4 312.5 312.5 312.5 312.6 + 312.6 312.6 312.6 312.6 312.7 312.7 312.7 312.8 312.8 312.8 312.8 312.9 312.9 312.9 312.9 313.0 313.0 313.0 + 313.0 313.1 313.1 313.1 ][ 418.2 418.2 418.2 418.2 418.3 418.3 418.3 418.3 418.3 418.3 418.3 418.3 418.4 418.4 + 418.4 418.4 418.4 418.4 418.4 418.5 418.5 418.5 418.5 418.5 418.5 418.5 418.6 418.6 418.6 418.6 418.6 418.6 + 418.6 418.7 418.7 418.7 418.7 418.7 418.7 418.7 ]plong + s[ 313.5 313.5 313.4 313.4 313.4 313.4 313.3 313.3 313.3 313.3 313.2 313.2 313.2 313.1 313.1 313.1 ][ 418.9 418.9 + 418.9 418.9 418.9 418.9 418.9 418.9 418.8 418.8 418.8 418.8 418.8 418.8 418.8 418.7 ]plong + s[ 474.4 474.4 474.4 474.5 474.5 474.5 474.5 474.6 474.6 474.6 474.7 474.7 474.7 474.7 474.8 474.8 ][ 418.9 418.9 + 418.9 418.9 418.9 418.9 418.9 418.9 418.8 418.8 418.8 418.8 418.8 418.8 418.8 418.7 ]plong + s[ 474.8 474.8 474.8 474.9 474.9 474.9 474.9 475.0 475.0 475.0 475.0 475.1 475.1 475.1 475.1 475.2 475.2 475.2 + 475.2 475.3 475.3 475.3 475.3 475.4 475.4 475.4 475.4 475.5 475.5 475.5 475.5 475.6 475.6 475.6 475.6 475.7 + 475.7 475.7 475.7 475.8 ][ 418.7 418.7 418.7 418.7 418.7 418.7 418.7 418.6 418.6 418.6 418.6 418.6 418.6 418.6 + 418.5 418.5 418.5 418.5 418.5 418.5 418.5 418.4 418.4 418.4 418.4 418.4 418.4 418.4 418.3 418.3 418.3 418.3 + 418.3 418.3 418.3 418.2 418.2 418.2 418.2 418.2 ]plong + s[ 475.8 475.8 475.8 475.8 475.9 475.9 475.9 476.0 476.0 476.0 476.0 476.0 476.1 476.1 476.1 476.1 476.2 ][ 418.2 + 418.2 418.2 418.1 418.1 418.1 418.1 418.1 418.1 418.1 418.0 418.0 418.0 418.0 418.0 418.0 418.0 ]plong + s[ 314.1 314.1 314.0 314.0 314.0 314.0 313.9 313.9 313.9 313.9 313.9 313.8 313.8 313.8 313.7 313.7 313.7 313.7 + 313.6 313.6 313.6 313.6 313.5 313.5 313.5 ][ 419.3 419.3 419.2 419.2 419.2 419.2 419.2 419.2 419.2 419.1 419.1 + 419.1 419.1 419.1 419.1 419.1 419.0 419.0 419.0 419.0 419.0 419.0 419.0 419.0 418.9 ]plong + s[ 314.1 314.1 314.1 314.2 314.2 314.2 314.2 314.3 314.3 314.3 314.4 314.4 314.4 314.4 314.5 314.5 314.5 314.5 + 314.6 314.6 314.6 314.6 314.7 314.7 314.7 314.7 314.8 314.8 314.8 314.8 314.9 314.9 314.9 315.0 315.0 315.0 + 315.0 315.0 315.1 ][ 419.3 419.3 419.3 419.3 419.3 419.3 419.4 419.4 419.4 419.4 419.4 419.4 419.4 419.4 419.5 + 419.5 419.5 419.5 419.5 419.5 419.6 419.6 419.6 419.6 419.6 419.6 419.6 419.6 419.7 419.7 419.7 419.7 419.7 + 419.7 419.7 419.8 419.8 419.8 419.8 ]plong + s[ 315.3 315.3 315.3 315.2 315.2 315.2 315.2 315.1 315.1 315.1 315.1 ][ 419.9 419.9 419.9 419.9 419.9 419.9 419.9 + 419.8 419.8 419.8 419.8 ]plong + s[ 472.6 472.6 472.6 472.6 472.7 472.7 472.7 472.7 472.8 472.8 472.8 ][ 419.9 419.9 419.9 419.9 419.9 419.9 419.8 + 419.8 419.8 419.8 419.8 ]plong + s[ 472.8 472.8 472.9 472.9 472.9 472.9 473.0 473.0 473.0 473.1 473.1 473.1 473.1 473.2 473.2 473.2 473.2 473.2 + 473.3 473.3 473.3 473.3 473.4 473.4 473.4 473.4 473.5 473.5 473.5 473.6 473.6 473.6 473.6 473.7 473.7 473.7 + 473.7 473.8 473.8 ][ 419.8 419.8 419.8 419.8 419.7 419.7 419.7 419.7 419.7 419.7 419.7 419.6 419.6 419.6 419.6 + 419.6 419.6 419.6 419.6 419.5 419.5 419.5 419.5 419.5 419.5 419.4 419.4 419.4 419.4 419.4 419.4 419.4 419.4 + 419.3 419.3 419.3 419.3 419.3 419.3 ]plong + s[ 473.8 473.8 473.8 473.9 473.9 473.9 473.9 474.0 474.0 474.0 474.0 474.1 474.1 474.1 474.2 474.2 474.2 474.2 + 474.2 474.3 474.3 474.3 474.3 474.4 474.4 ][ 419.3 419.3 419.2 419.2 419.2 419.2 419.2 419.2 419.2 419.1 419.1 + 419.1 419.1 419.1 419.1 419.1 419.0 419.0 419.0 419.0 419.0 419.0 419.0 419.0 418.9 ]plong + s[ 316.0 316.0 316.0 316.0 315.9 315.9 315.9 315.9 315.9 315.8 315.8 315.8 315.7 315.7 315.7 315.7 315.6 315.6 + 315.6 315.6 315.5 315.5 315.5 315.5 315.4 315.4 315.4 315.3 315.3 ][ 420.3 420.3 420.3 420.3 420.3 420.2 420.2 + 420.2 420.2 420.2 420.2 420.1 420.1 420.1 420.1 420.1 420.1 420.1 420.1 420.0 420.0 420.0 420.0 420.0 420.0 + 420.0 419.9 419.9 419.9 ]plong + s[ 316.0 316.1 316.1 316.1 316.1 316.2 316.2 316.2 316.2 316.3 316.3 316.3 316.4 316.4 316.4 316.4 316.5 316.5 + 316.5 316.5 316.6 316.6 316.6 316.6 316.7 316.7 316.7 316.8 316.8 316.8 316.8 316.9 316.9 316.9 316.9 316.9 + 317.0 317.0 317.0 ][ 420.3 420.3 420.3 420.3 420.4 420.4 420.4 420.4 420.4 420.4 420.4 420.5 420.5 420.5 420.5 + 420.5 420.5 420.5 420.5 420.6 420.6 420.6 420.6 420.6 420.6 420.6 420.7 420.7 420.7 420.7 420.7 420.7 420.7 + 420.7 420.8 420.8 420.8 420.8 420.8 ]plong + s[ 317.2 317.2 317.1 317.1 317.1 317.1 317.1 317.0 ][ 420.9 420.9 420.9 420.9 420.8 420.8 420.8 420.8 ]plong + s[ 470.6 470.7 470.7 470.7 470.7 470.8 470.8 470.8 470.9 ][ 420.9 420.9 420.9 420.9 420.8 420.8 420.8 420.8 420.8 +]plong + s[ 470.9 470.9 470.9 470.9 471.0 471.0 471.0 471.0 471.1 471.1 471.1 471.1 471.2 471.2 471.2 471.2 471.3 471.3 + 471.3 471.3 471.4 471.4 471.4 471.4 471.5 471.5 471.5 471.6 471.6 471.6 471.6 471.7 471.7 471.7 471.7 471.8 + 471.8 471.8 471.8 ][ 420.8 420.8 420.8 420.8 420.8 420.7 420.7 420.7 420.7 420.7 420.7 420.7 420.7 420.6 420.6 + 420.6 420.6 420.6 420.6 420.6 420.5 420.5 420.5 420.5 420.5 420.5 420.5 420.5 420.4 420.4 420.4 420.4 420.4 + 420.4 420.4 420.3 420.3 420.3 420.3 ]plong + s[ 471.8 471.9 471.9 471.9 471.9 472.0 472.0 472.0 472.0 472.1 472.1 472.1 472.2 472.2 472.2 472.2 472.2 472.3 + 472.3 472.3 472.3 472.4 472.4 472.4 472.5 472.5 472.5 472.5 472.6 ][ 420.3 420.3 420.3 420.3 420.2 420.2 420.2 + 420.2 420.2 420.2 420.2 420.1 420.1 420.1 420.1 420.1 420.1 420.1 420.1 420.0 420.0 420.0 420.0 420.0 420.0 + 420.0 419.9 419.9 419.9 ]plong + s[ 318.0 318.0 318.0 317.9 317.9 317.9 317.9 317.8 317.8 317.8 317.8 317.7 317.7 317.7 317.7 317.6 317.6 317.6 + 317.5 317.5 317.5 317.5 317.4 317.4 317.4 317.4 317.3 317.3 317.3 317.3 317.2 317.2 ][ 421.3 421.3 421.3 421.3 + 421.3 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.1 421.1 421.1 421.1 421.1 421.1 421.1 421.0 421.0 + 421.0 421.0 421.0 421.0 421.0 421.0 420.9 420.9 420.9 420.9 ]plong + s[ 318.0 318.0 318.1 318.1 318.1 318.1 318.2 318.2 318.2 318.2 318.3 318.3 318.3 318.4 318.4 318.4 318.4 318.5 + 318.5 318.5 318.5 318.6 318.6 318.6 318.6 318.7 318.7 318.7 318.8 318.8 318.8 318.8 318.9 318.9 318.9 318.9 + 318.9 319.0 ][ 421.3 421.3 421.3 421.4 421.4 421.4 421.4 421.4 421.4 421.4 421.4 421.5 421.5 421.5 421.5 421.5 + 421.5 421.5 421.6 421.6 421.6 421.6 421.6 421.6 421.6 421.6 421.7 421.7 421.7 421.7 421.7 421.7 421.7 421.8 + 421.8 421.8 421.8 421.8 ]plong + s[ 319.2 319.1 319.1 319.1 319.1 319.0 319.0 319.0 ][ 421.9 421.9 421.9 421.8 421.8 421.8 421.8 421.8 ]plong + s[ 468.7 468.7 468.8 468.8 468.8 468.9 468.9 468.9 ][ 421.9 421.9 421.9 421.8 421.8 421.8 421.8 421.8 ]plong + s[ 468.9 468.9 469.0 469.0 469.0 469.0 469.1 469.1 469.1 469.1 469.2 469.2 469.2 469.3 469.3 469.3 469.3 469.3 + 469.4 469.4 469.4 469.4 469.5 469.5 469.5 469.6 469.6 469.6 469.6 469.7 469.7 469.7 469.7 469.8 469.8 469.8 + 469.8 469.9 ][ 421.8 421.8 421.8 421.8 421.8 421.7 421.7 421.7 421.7 421.7 421.7 421.7 421.6 421.6 421.6 421.6 + 421.6 421.6 421.6 421.6 421.5 421.5 421.5 421.5 421.5 421.5 421.5 421.4 421.4 421.4 421.4 421.4 421.4 421.4 + 421.4 421.3 421.3 421.3 ]plong + s[ 469.9 469.9 469.9 470.0 470.0 470.0 470.0 470.1 470.1 470.1 470.1 470.2 470.2 470.2 470.2 470.3 470.3 470.3 + 470.3 470.4 470.4 470.4 470.4 470.5 470.5 470.5 470.5 470.6 470.6 470.6 470.6 ][ 421.3 421.3 421.3 421.3 421.3 + 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.2 421.1 421.1 421.1 421.1 421.1 421.1 421.1 421.0 421.0 421.0 + 421.0 421.0 421.0 421.0 420.9 420.9 420.9 420.9 ]plong + s[ 320.0 319.9 319.9 319.9 319.9 319.8 319.8 319.8 319.7 319.7 319.7 319.7 319.6 319.6 319.6 319.6 319.5 319.5 + 319.5 319.5 319.4 319.4 319.4 319.3 319.3 319.3 319.3 319.2 319.2 319.2 319.2 ][ 422.3 422.3 422.2 422.2 422.2 + 422.2 422.2 422.2 422.2 422.2 422.1 422.1 422.1 422.1 422.1 422.1 422.1 422.1 422.0 422.0 422.0 422.0 422.0 + 422.0 422.0 421.9 421.9 421.9 421.9 421.9 421.9 ]plong + s[ 320.0 320.0 320.0 320.0 320.1 320.1 320.1 320.1 320.2 320.2 320.2 320.2 320.3 320.3 320.3 320.4 320.4 320.4 + 320.4 320.5 320.5 320.5 320.5 320.6 320.6 320.6 320.6 320.7 320.7 320.7 320.8 320.8 320.8 320.8 320.8 320.9 + 320.9 320.9 320.9 ][ 422.3 422.3 422.3 422.3 422.3 422.3 422.3 422.4 422.4 422.4 422.4 422.4 422.4 422.4 422.5 + 422.5 422.5 422.5 422.5 422.5 422.5 422.5 422.6 422.6 422.6 422.6 422.6 422.6 422.6 422.7 422.7 422.7 422.7 + 422.7 422.7 422.7 422.7 422.8 422.8 ]plong + s[ 321.2 321.2 321.1 321.1 321.1 321.1 321.0 321.0 321.0 320.9 ][ 422.9 422.8 422.8 422.8 422.8 422.8 422.8 422.8 + 422.8 422.8 ]plong + s[ 466.7 466.7 466.7 466.8 466.8 466.8 466.8 466.9 466.9 466.9 466.9 ][ 422.9 422.8 422.8 422.8 422.8 422.8 422.8 + 422.8 422.8 422.8 422.7 ]plong + s[ 466.9 467.0 467.0 467.0 467.1 467.1 467.1 467.1 467.2 467.2 467.2 467.3 467.3 467.3 467.3 467.4 467.4 467.4 + 467.4 467.4 467.5 467.5 467.5 467.6 467.6 467.6 467.6 467.7 467.7 467.7 467.7 467.8 467.8 467.8 467.8 467.9 + 467.9 467.9 ][ 422.7 422.7 422.7 422.7 422.7 422.7 422.7 422.7 422.7 422.6 422.6 422.6 422.6 422.6 422.6 422.6 + 422.5 422.5 422.5 422.5 422.5 422.5 422.5 422.5 422.4 422.4 422.4 422.4 422.4 422.4 422.4 422.3 422.3 422.3 + 422.3 422.3 422.3 422.3 ]plong + s[ 467.9 468.0 468.0 468.0 468.0 468.1 468.1 468.1 468.1 468.2 468.2 468.2 468.2 468.3 468.3 468.3 468.3 468.4 + 468.4 468.4 468.5 468.5 468.5 468.5 468.6 468.6 468.6 468.6 468.7 468.7 468.7 ][ 422.3 422.3 422.2 422.2 422.2 + 422.2 422.2 422.2 422.2 422.1 422.1 422.1 422.1 422.1 422.1 422.1 422.1 422.1 422.0 422.0 422.0 422.0 422.0 + 422.0 422.0 421.9 421.9 421.9 421.9 421.9 421.9 ]plong + s[ 321.9 321.9 321.9 321.8 321.8 321.8 321.8 321.7 321.7 321.7 321.7 321.6 321.6 321.6 321.5 321.5 321.5 321.5 + 321.4 321.4 321.4 321.4 321.3 321.3 321.3 321.3 321.2 321.2 ][ 423.2 423.2 423.2 423.2 423.2 423.2 423.1 423.1 + 423.1 423.1 423.1 423.1 423.1 423.0 423.0 423.0 423.0 423.0 423.0 423.0 423.0 422.9 422.9 422.9 422.9 422.9 + 422.9 422.9 ]plong + s[ 321.9 321.9 322.0 322.0 322.0 322.0 322.1 322.1 322.1 322.2 322.2 322.2 322.2 322.3 322.3 322.3 322.3 322.4 + 322.4 322.4 322.4 322.5 322.5 322.5 322.6 322.6 322.6 322.6 322.7 322.7 322.7 322.8 322.8 322.8 322.8 322.8 + 322.9 322.9 ][ 423.2 423.2 423.2 423.2 423.3 423.3 423.3 423.3 423.3 423.3 423.3 423.4 423.4 423.4 423.4 423.4 + 423.4 423.4 423.4 423.5 423.5 423.5 423.5 423.5 423.5 423.5 423.5 423.6 423.6 423.6 423.6 423.6 423.6 423.6 + 423.6 423.7 423.7 423.7 ]plong + s[ 323.3 323.3 323.2 323.2 323.2 323.1 323.1 323.1 323.1 323.0 323.0 323.0 322.9 322.9 322.9 ][ 423.8 423.8 423.8 + 423.8 423.8 423.8 423.8 423.8 423.8 423.7 423.7 423.7 423.7 423.7 423.7 ]plong + s[ 464.6 464.6 464.7 464.7 464.7 464.7 464.8 464.8 464.8 464.8 464.9 464.9 464.9 465.0 465.0 ][ 423.8 423.8 423.8 + 423.8 423.8 423.8 423.8 423.8 423.8 423.7 423.7 423.7 423.7 423.7 423.7 ]plong + s[ 465.0 465.0 465.0 465.1 465.1 465.1 465.1 465.2 465.2 465.2 465.3 465.3 465.3 465.3 465.4 465.4 465.4 465.4 + 465.4 465.5 465.5 465.5 465.6 465.6 465.6 465.6 465.7 465.7 465.7 465.8 465.8 465.8 465.8 465.9 465.9 465.9 + 465.9 466.0 ][ 423.7 423.7 423.7 423.6 423.6 423.6 423.6 423.6 423.6 423.6 423.6 423.5 423.5 423.5 423.5 423.5 + 423.5 423.5 423.4 423.4 423.4 423.4 423.4 423.4 423.4 423.4 423.4 423.3 423.3 423.3 423.3 423.3 423.3 423.3 + 423.2 423.2 423.2 423.2 ]plong + s[ 466.0 466.0 466.0 466.0 466.1 466.1 466.1 466.2 466.2 466.2 466.2 466.3 466.3 466.3 466.4 466.4 466.4 466.4 + 466.4 466.5 466.5 466.5 466.5 466.6 466.6 466.6 466.7 466.7 ][ 423.2 423.2 423.2 423.2 423.2 423.1 423.1 423.1 + 423.1 423.1 423.1 423.1 423.1 423.0 423.0 423.0 423.0 423.0 423.0 423.0 423.0 422.9 422.9 422.9 422.9 422.9 + 422.9 422.9 ]plong + s[ 323.9 323.9 323.8 323.8 323.8 323.8 323.7 323.7 323.7 323.7 323.6 323.6 323.6 323.6 323.5 323.5 323.5 323.4 + 323.4 323.4 323.4 323.3 323.3 323.3 ][ 424.1 424.1 424.1 424.1 424.1 424.1 424.1 424.0 424.0 424.0 424.0 424.0 + 424.0 424.0 423.9 423.9 423.9 423.9 423.9 423.9 423.9 423.9 423.9 423.8 ]plong + s[ 323.9 323.9 323.9 324.0 324.0 324.0 324.1 324.1 324.1 324.1 324.2 324.2 324.2 324.2 324.3 324.3 324.3 324.4 + 324.4 324.4 324.4 324.5 324.5 324.5 324.6 324.6 324.6 324.6 324.7 324.7 324.7 324.7 324.7 324.8 324.8 324.8 + 324.9 ][ 424.1 424.1 424.2 424.2 424.2 424.2 424.2 424.2 424.2 424.2 424.3 424.3 424.3 424.3 424.3 424.3 424.3 + 424.3 424.4 424.4 424.4 424.4 424.4 424.4 424.4 424.4 424.5 424.5 424.5 424.5 424.5 424.5 424.5 424.5 424.5 + 424.6 424.6 ]plong + s[ 325.4 325.4 325.4 325.4 325.3 325.3 325.3 325.3 325.2 325.2 325.2 325.1 325.1 325.1 325.1 325.0 325.0 325.0 + 324.9 324.9 324.9 324.9 ][ 424.8 424.8 424.8 424.8 424.8 424.8 424.7 424.7 424.7 424.7 424.7 424.7 424.7 424.7 + 424.7 424.7 424.6 424.6 424.6 424.6 424.6 424.6 ]plong + s[ 462.5 462.5 462.5 462.5 462.5 462.6 462.6 462.6 462.7 462.7 462.7 462.7 462.8 462.8 462.8 462.9 462.9 462.9 + 462.9 463.0 463.0 463.0 ][ 424.8 424.8 424.8 424.8 424.8 424.8 424.7 424.7 424.7 424.7 424.7 424.7 424.7 424.7 + 424.7 424.7 424.6 424.6 424.6 424.6 424.6 424.6 ]plong + s[ 463.0 463.0 463.1 463.1 463.1 463.2 463.2 463.2 463.2 463.3 463.3 463.3 463.3 463.4 463.4 463.4 463.5 463.5 + 463.5 463.5 463.6 463.6 463.6 463.6 463.7 463.7 463.7 463.7 463.8 463.8 463.8 463.8 463.9 463.9 463.9 464.0 + 464.0 ][ 424.6 424.6 424.5 424.5 424.5 424.5 424.5 424.5 424.5 424.5 424.5 424.4 424.4 424.4 424.4 424.4 424.4 + 424.4 424.3 424.3 424.3 424.3 424.3 424.3 424.3 424.3 424.3 424.2 424.2 424.2 424.2 424.2 424.2 424.2 424.1 + 424.1 424.1 ]plong + s[ 464.0 464.0 464.0 464.1 464.1 464.1 464.2 464.2 464.2 464.2 464.3 464.3 464.3 464.3 464.4 464.4 464.4 464.5 + 464.5 464.5 464.5 464.5 464.6 464.6 ][ 424.1 424.1 424.1 424.1 424.1 424.1 424.1 424.0 424.0 424.0 424.0 424.0 + 424.0 424.0 423.9 423.9 423.9 423.9 423.9 423.9 423.9 423.9 423.9 423.8 ]plong + s[ 325.8 325.8 325.8 325.8 325.7 325.7 325.7 325.7 325.6 325.6 325.6 325.6 325.5 325.5 325.5 325.4 ][ 425.0 425.0 + 425.0 425.0 425.0 424.9 424.9 424.9 424.9 424.9 424.9 424.9 424.8 424.8 424.8 424.8 ]plong + s[ 325.8 325.9 325.9 325.9 326.0 326.0 326.0 326.0 326.1 326.1 326.1 326.1 326.2 326.2 326.2 326.3 326.3 326.3 + 326.3 326.4 326.4 326.4 326.4 326.5 326.5 326.5 326.6 326.6 326.6 326.6 326.7 326.7 326.7 326.7 326.8 326.8 + 326.8 ][ 425.0 425.0 425.0 425.0 425.0 425.1 425.1 425.1 425.1 425.1 425.1 425.1 425.2 425.2 425.2 425.2 425.2 + 425.2 425.2 425.2 425.2 425.3 425.3 425.3 425.3 425.3 425.3 425.3 425.3 425.4 425.4 425.4 425.4 425.4 425.4 + 425.4 425.4 ]plong + s[ 327.7 327.7 327.6 327.6 327.6 327.6 327.5 327.5 327.5 327.5 327.4 327.4 327.4 327.3 327.3 327.3 327.3 327.2 + 327.2 327.2 327.2 327.1 327.1 327.1 327.0 327.0 327.0 327.0 326.9 326.9 326.9 326.9 326.8 ][ 425.8 425.8 425.8 + 425.8 425.8 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.6 425.6 425.6 425.6 425.6 425.6 + 425.6 425.6 425.6 425.5 425.5 425.5 425.5 425.5 425.5 425.5 425.4 425.4 ]plong + s[ 460.2 460.2 460.2 460.3 460.3 460.3 460.4 460.4 460.4 460.4 460.5 460.5 460.5 460.5 460.6 460.6 460.6 460.6 + 460.7 460.7 460.7 460.7 460.8 460.8 460.8 460.9 460.9 460.9 460.9 461.0 461.0 461.0 461.1 ][ 425.8 425.8 425.8 + 425.8 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.7 425.6 425.6 425.6 425.6 425.6 425.6 425.6 + 425.6 425.6 425.5 425.5 425.5 425.5 425.5 425.5 425.5 425.5 425.4 425.4 ]plong + s[ 461.1 461.1 461.1 461.1 461.2 461.2 461.2 461.3 461.3 461.3 461.3 461.4 461.4 461.4 461.5 461.5 461.5 461.5 + 461.5 461.6 461.6 461.6 461.6 461.7 461.7 461.7 461.8 461.8 461.8 461.8 461.9 461.9 461.9 462.0 462.0 462.0 + 462.0 ][ 425.4 425.4 425.4 425.4 425.4 425.4 425.4 425.4 425.3 425.3 425.3 425.3 425.3 425.3 425.3 425.3 425.2 + 425.2 425.2 425.2 425.2 425.2 425.2 425.2 425.1 425.1 425.1 425.1 425.1 425.1 425.1 425.1 425.0 425.0 425.0 + 425.0 425.0 ]plong + s[ 462.0 462.1 462.1 462.1 462.2 462.2 462.2 462.2 462.3 462.3 462.3 462.3 462.4 462.4 462.4 462.5 ][ 425.0 425.0 + 425.0 425.0 425.0 424.9 424.9 424.9 424.9 424.9 424.9 424.9 424.8 424.8 424.8 424.8 ]plong + 327.7 425.8 327.7 425.8 327.8 425.8 327.8 425.8 327.8 425.9 5 pls + s[ 327.8 327.8 327.9 327.9 327.9 328.0 328.0 328.0 328.0 328.1 328.1 328.1 328.2 328.2 328.2 328.2 328.3 328.3 + 328.3 328.3 328.4 328.4 328.4 328.5 328.5 328.5 328.5 328.6 328.6 328.6 328.6 328.7 328.7 328.7 328.7 328.8 ][ + 425.9 425.9 425.9 425.9 425.9 425.9 425.9 425.9 425.9 426.0 426.0 426.0 426.0 426.0 426.0 426.0 426.0 426.1 + 426.1 426.1 426.1 426.1 426.1 426.1 426.1 426.1 426.2 426.2 426.2 426.2 426.2 426.2 426.2 426.2 426.3 426.3 ]plong + s[ 328.8 328.8 328.8 328.9 328.9 328.9 328.9 329.0 329.0 329.0 329.1 329.1 329.1 329.1 329.2 329.2 329.2 329.3 + 329.3 329.3 329.3 329.4 329.4 329.4 329.5 329.5 329.5 329.5 329.6 329.6 329.6 329.6 329.7 329.7 329.7 329.7 + 329.8 ][ 426.3 426.3 426.3 426.3 426.3 426.3 426.3 426.3 426.4 426.4 426.4 426.4 426.4 426.4 426.4 426.4 426.5 + 426.5 426.5 426.5 426.5 426.5 426.5 426.5 426.5 426.6 426.6 426.6 426.6 426.6 426.6 426.6 426.6 426.7 426.7 + 426.7 426.7 ]plong + s[ 330.1 330.0 330.0 330.0 329.9 329.9 329.9 329.9 329.8 329.8 329.8 ][ 426.8 426.8 426.8 426.8 426.7 426.7 426.7 + 426.7 426.7 426.7 426.7 ]plong + s[ 457.8 457.8 457.9 457.9 457.9 458.0 458.0 458.0 458.0 458.1 458.1 ][ 426.8 426.8 426.8 426.8 426.7 426.7 426.7 + 426.7 426.7 426.7 426.7 ]plong + s[ 458.1 458.1 458.2 458.2 458.2 458.2 458.3 458.3 458.3 458.4 458.4 458.4 458.4 458.5 458.5 458.5 458.6 458.6 + 458.6 458.6 458.7 458.7 458.7 458.7 458.8 458.8 458.8 458.9 458.9 458.9 458.9 459.0 459.0 459.0 459.1 459.1 + 459.1 ][ 426.7 426.7 426.7 426.7 426.6 426.6 426.6 426.6 426.6 426.6 426.6 426.6 426.5 426.5 426.5 426.5 426.5 + 426.5 426.5 426.5 426.5 426.4 426.4 426.4 426.4 426.4 426.4 426.4 426.4 426.3 426.3 426.3 426.3 426.3 426.3 + 426.3 426.3 ]plong + s[ 459.1 459.1 459.2 459.2 459.2 459.3 459.3 459.3 459.3 459.4 459.4 459.4 459.4 459.5 459.5 459.5 459.6 459.6 + 459.6 459.6 459.6 459.7 459.7 459.7 459.8 459.8 459.8 459.8 459.9 459.9 459.9 460.0 460.0 460.0 460.0 460.1 ][ + 426.3 426.3 426.2 426.2 426.2 426.2 426.2 426.2 426.2 426.2 426.1 426.1 426.1 426.1 426.1 426.1 426.1 426.1 + 426.1 426.0 426.0 426.0 426.0 426.0 426.0 426.0 426.0 425.9 425.9 425.9 425.9 425.9 425.9 425.9 425.9 425.9 ]plong + 460.2 425.8 460.2 425.8 460.1 425.8 460.1 425.8 460.1 425.9 5 pls + s[ 330.7 330.7 330.7 330.7 330.6 330.6 330.6 330.5 330.5 330.5 330.5 330.4 330.4 330.4 330.4 330.3 330.3 330.3 + 330.2 330.2 330.2 330.2 330.1 330.1 330.1 330.1 ][ 427.1 427.1 427.0 427.0 427.0 427.0 427.0 427.0 427.0 427.0 + 427.0 427.0 426.9 426.9 426.9 426.9 426.9 426.9 426.9 426.9 426.8 426.8 426.8 426.8 426.8 426.8 ]plong + s[ 330.7 330.8 330.8 330.8 330.9 330.9 330.9 330.9 331.0 331.0 331.0 331.1 331.1 331.1 331.1 331.2 331.2 331.2 + 331.3 331.3 331.3 331.3 331.4 331.4 331.4 331.5 331.5 331.5 331.5 331.6 331.6 331.6 331.6 331.7 331.7 331.7 ][ + 427.1 427.1 427.1 427.1 427.1 427.1 427.1 427.2 427.2 427.2 427.2 427.2 427.2 427.2 427.2 427.2 427.3 427.3 + 427.3 427.3 427.3 427.3 427.3 427.3 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.5 427.5 ]plong + s[ 332.5 332.5 332.4 332.4 332.4 332.4 332.3 332.3 332.3 332.2 332.2 332.2 332.2 332.1 332.1 332.1 332.0 332.0 + 332.0 332.0 331.9 331.9 331.9 331.8 331.8 331.8 331.8 331.7 ][ 427.8 427.7 427.7 427.7 427.7 427.7 427.7 427.7 + 427.7 427.7 427.7 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.5 427.5 427.5 427.5 427.5 + 427.5 427.5 ]plong + s[ 455.4 455.4 455.5 455.5 455.5 455.5 455.6 455.6 455.6 455.7 455.7 455.7 455.7 455.8 455.8 455.8 455.8 455.9 + 455.9 455.9 456.0 456.0 456.0 456.0 456.1 456.1 456.1 456.2 ][ 427.8 427.7 427.7 427.7 427.7 427.7 427.7 427.7 + 427.7 427.7 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.6 427.5 427.5 427.5 427.5 427.5 + 427.5 427.5 ]plong + s[ 456.2 456.2 456.2 456.2 456.3 456.3 456.3 456.4 456.4 456.4 456.4 456.5 456.5 456.5 456.6 456.6 456.6 456.6 + 456.7 456.7 456.7 456.7 456.8 456.8 456.8 456.9 456.9 456.9 456.9 457.0 457.0 457.0 457.1 457.1 457.1 457.1 ][ + 427.5 427.5 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.4 427.3 427.3 427.3 427.3 427.3 427.3 + 427.3 427.3 427.2 427.2 427.2 427.2 427.2 427.2 427.2 427.2 427.2 427.1 427.1 427.1 427.1 427.1 427.1 427.1 ]plong + s[ 457.1 457.2 457.2 457.2 457.3 457.3 457.3 457.3 457.4 457.4 457.4 457.4 457.5 457.5 457.5 457.6 457.6 457.6 + 457.6 457.7 457.7 457.7 457.7 457.8 457.8 457.8 ][ 427.1 427.1 427.0 427.0 427.0 427.0 427.0 427.0 427.0 427.0 + 427.0 426.9 426.9 426.9 426.9 426.9 426.9 426.9 426.9 426.8 426.8 426.8 426.8 426.8 426.8 426.8 ]plong + s[ 332.7 332.7 332.7 332.6 332.6 332.6 332.5 332.5 332.5 ][ 427.9 427.8 427.8 427.8 427.8 427.8 427.8 427.8 427.8 +]plong + s[ 332.7 332.7 332.8 332.8 332.8 332.9 332.9 332.9 332.9 333.0 333.0 333.0 333.1 333.1 333.1 333.1 333.2 333.2 + 333.2 333.3 333.3 333.3 333.3 333.4 333.4 333.4 333.4 333.5 333.5 333.5 333.6 333.6 333.6 333.6 333.7 ][ 427.9 + 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 428.0 428.0 428.0 428.0 428.0 428.0 428.0 428.0 428.1 + 428.1 428.1 428.1 428.1 428.1 428.1 428.1 428.1 428.1 428.2 428.2 428.2 428.2 428.2 428.2 428.2 ]plong + s[ 333.7 333.7 333.7 333.8 333.8 333.8 333.8 333.9 333.9 333.9 334.0 334.0 334.0 334.0 334.1 334.1 334.1 334.2 + 334.2 334.2 334.2 334.3 334.3 334.3 334.4 334.4 334.4 334.4 334.5 334.5 334.5 334.5 334.6 334.6 334.6 334.7 ][ + 428.2 428.2 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.4 428.4 428.4 428.4 428.4 428.4 + 428.4 428.4 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.6 428.6 428.6 428.6 428.6 428.6 ]plong + s[ 335.1 335.1 335.0 335.0 335.0 334.9 334.9 334.9 334.8 334.8 334.8 334.8 334.7 334.7 334.7 ][ 428.7 428.7 428.7 + 428.7 428.7 428.7 428.7 428.7 428.7 428.6 428.6 428.6 428.6 428.6 428.6 ]plong + s[ 452.8 452.8 452.9 452.9 452.9 452.9 453.0 453.0 453.0 453.1 453.1 453.1 453.1 453.2 453.2 ][ 428.7 428.7 428.7 + 428.7 428.7 428.7 428.7 428.7 428.7 428.6 428.6 428.6 428.6 428.6 428.6 ]plong + s[ 453.2 453.2 453.3 453.3 453.3 453.4 453.4 453.4 453.4 453.5 453.5 453.5 453.6 453.6 453.6 453.7 453.7 453.7 + 453.7 453.8 453.8 453.8 453.8 453.9 453.9 453.9 453.9 454.0 454.0 454.0 454.1 454.1 454.1 454.2 454.2 454.2 ][ + 428.6 428.6 428.6 428.6 428.6 428.6 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.5 428.4 428.4 + 428.4 428.4 428.4 428.4 428.4 428.4 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.3 428.2 428.2 ]plong + s[ 454.2 454.2 454.3 454.3 454.3 454.4 454.4 454.4 454.4 454.5 454.5 454.5 454.6 454.6 454.6 454.6 454.7 454.7 + 454.7 454.7 454.8 454.8 454.8 454.9 454.9 454.9 454.9 455.0 455.0 455.0 455.1 455.1 455.1 455.1 455.2 ][ 428.2 + 428.2 428.2 428.2 428.2 428.2 428.2 428.1 428.1 428.1 428.1 428.1 428.1 428.1 428.1 428.1 428.1 428.0 428.0 + 428.0 428.0 428.0 428.0 428.0 428.0 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 427.9 ]plong + s[ 455.2 455.2 455.2 455.3 455.3 455.3 455.3 455.4 455.4 ][ 427.9 427.8 427.8 427.8 427.8 427.8 427.8 427.8 427.8 +]plong + s[ 335.6 335.6 335.6 335.6 335.5 335.5 335.5 335.4 335.4 335.4 335.4 335.3 335.3 335.3 335.3 335.2 335.2 335.2 + 335.1 335.1 335.1 ][ 429.0 429.0 428.9 428.9 428.9 428.9 428.9 428.9 428.9 428.9 428.8 428.8 428.8 428.8 428.8 + 428.8 428.8 428.8 428.8 428.8 428.7 ]plong + s[ 335.6 335.7 335.7 335.7 335.8 335.8 335.8 335.8 335.9 335.9 335.9 336.0 336.0 336.0 336.0 336.1 336.1 336.1 + 336.2 336.2 336.2 336.3 336.3 336.3 336.3 336.4 336.4 336.4 336.4 336.5 336.5 336.5 336.6 336.6 336.6 ][ 429.0 + 429.0 429.0 429.0 429.0 429.0 429.0 429.0 429.0 429.1 429.1 429.1 429.1 429.1 429.1 429.1 429.1 429.1 429.2 + 429.2 429.2 429.2 429.2 429.2 429.2 429.2 429.2 429.2 429.3 429.3 429.3 429.3 429.3 429.3 429.3 ]plong + s[ 336.6 336.7 336.7 336.7 336.7 336.8 336.8 336.8 336.9 336.9 336.9 336.9 337.0 337.0 337.0 337.1 337.1 337.1 + 337.1 337.2 337.2 337.2 337.3 337.3 337.3 337.3 337.4 337.4 337.4 337.4 337.5 337.5 337.5 337.6 337.6 ][ 429.3 + 429.3 429.3 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.5 429.5 429.5 429.5 429.5 429.5 + 429.5 429.5 429.5 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.7 429.7 ]plong + s[ 337.8 337.8 337.7 337.7 337.7 337.6 337.6 ][ 429.7 429.7 429.7 429.7 429.7 429.7 429.7 ]plong + s[ 450.1 450.1 450.2 450.2 450.2 450.2 450.3 ][ 429.7 429.7 429.7 429.7 429.7 429.7 429.7 ]plong + s[ 450.3 450.3 450.3 450.4 450.4 450.4 450.5 450.5 450.5 450.6 450.6 450.6 450.6 450.7 450.7 450.7 450.8 450.8 + 450.8 450.8 450.9 450.9 450.9 450.9 451.0 451.0 451.0 451.1 451.1 451.1 451.1 451.2 451.2 451.2 451.3 ][ 429.7 + 429.7 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.6 429.5 429.5 429.5 429.5 429.5 429.5 + 429.5 429.5 429.5 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.4 429.3 429.3 429.3 ]plong + s[ 451.3 451.3 451.3 451.3 451.4 451.4 451.4 451.5 451.5 451.5 451.6 451.6 451.6 451.6 451.7 451.7 451.7 451.8 + 451.8 451.8 451.8 451.9 451.9 451.9 452.0 452.0 452.0 452.0 452.1 452.1 452.1 452.2 452.2 452.2 452.2 ][ 429.3 + 429.3 429.3 429.3 429.3 429.3 429.3 429.2 429.2 429.2 429.2 429.2 429.2 429.2 429.2 429.2 429.2 429.1 429.1 + 429.1 429.1 429.1 429.1 429.1 429.1 429.1 429.0 429.0 429.0 429.0 429.0 429.0 429.0 429.0 429.0 ]plong + s[ 452.2 452.3 452.3 452.3 452.4 452.4 452.4 452.4 452.5 452.5 452.5 452.6 452.6 452.6 452.6 452.7 452.7 452.7 + 452.8 452.8 452.8 ][ 429.0 429.0 428.9 428.9 428.9 428.9 428.9 428.9 428.9 428.9 428.8 428.8 428.8 428.8 428.8 + 428.8 428.8 428.8 428.8 428.8 428.7 ]plong + s[ 338.6 338.6 338.5 338.5 338.5 338.4 338.4 338.4 338.3 338.3 338.3 338.3 338.2 338.2 338.2 338.2 338.1 338.1 + 338.1 338.0 338.0 338.0 338.0 337.9 337.9 337.9 337.8 337.8 337.8 ][ 430.0 430.0 430.0 430.0 430.0 430.0 429.9 + 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.8 429.8 429.8 429.8 429.8 429.8 429.8 429.8 + 429.8 429.7 429.7 429.7 ]plong + s[ 338.6 338.6 338.7 338.7 338.7 338.7 338.8 338.8 338.8 338.9 338.9 338.9 338.9 339.0 339.0 339.0 339.1 339.1 + 339.1 339.1 339.2 339.2 339.2 339.3 339.3 339.3 339.3 339.4 339.4 339.4 339.5 339.5 339.5 339.6 ][ 430.0 430.0 + 430.0 430.0 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.2 430.2 430.2 430.2 430.2 430.2 + 430.2 430.2 430.2 430.3 430.3 430.3 430.3 430.3 430.3 430.3 430.3 430.3 430.3 430.3 ]plong + s[ 339.6 339.6 339.6 339.6 339.7 339.7 339.7 339.8 339.8 339.8 339.8 339.9 339.9 339.9 340.0 340.0 340.0 340.1 + 340.1 340.1 340.2 340.2 340.2 340.2 340.3 340.3 340.3 340.3 340.4 340.4 340.4 340.5 340.5 340.5 340.5 ][ 430.3 + 430.4 430.4 430.4 430.4 430.4 430.4 430.4 430.4 430.4 430.5 430.5 430.5 430.5 430.5 430.5 430.5 430.5 430.5 + 430.5 430.5 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.7 430.7 ]plong + 340.5 430.7 340.6 430.7 340.6 430.7 340.6 430.7 4 pls + 447.3 430.7 447.3 430.7 447.3 430.7 447.2 430.7 4 pls + s[ 447.3 447.4 447.4 447.4 447.5 447.5 447.5 447.5 447.6 447.6 447.6 447.7 447.7 447.7 447.7 447.8 447.8 447.8 + 447.9 447.9 447.9 448.0 448.0 448.0 448.0 448.1 448.1 448.1 448.2 448.2 448.2 448.2 448.3 448.3 448.3 ][ 430.7 + 430.7 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.6 430.5 430.5 430.5 430.5 430.5 + 430.5 430.5 430.5 430.5 430.5 430.5 430.4 430.4 430.4 430.4 430.4 430.4 430.4 430.4 430.4 430.3 ]plong + s[ 448.3 448.4 448.4 448.4 448.4 448.5 448.5 448.5 448.6 448.6 448.6 448.7 448.7 448.7 448.8 448.8 448.8 448.8 + 448.9 448.9 448.9 448.9 449.0 449.0 449.0 449.1 449.1 449.1 449.1 449.2 449.2 449.2 449.3 449.3 ][ 430.3 430.3 + 430.3 430.3 430.3 430.3 430.3 430.3 430.3 430.3 430.3 430.2 430.2 430.2 430.2 430.2 430.2 430.2 430.2 430.2 + 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.1 430.0 430.0 430.0 430.0 ]plong + s[ 449.3 449.3 449.3 449.4 449.4 449.4 449.5 449.5 449.5 449.6 449.6 449.6 449.7 449.7 449.7 449.7 449.8 449.8 + 449.8 449.9 449.9 449.9 449.9 450.0 450.0 450.0 450.0 450.1 450.1 ][ 430.0 430.0 430.0 430.0 430.0 430.0 429.9 + 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.9 429.8 429.8 429.8 429.8 429.8 429.8 429.8 429.8 + 429.7 429.7 429.7 429.7 ]plong + s[ 341.5 341.5 341.5 341.4 341.4 341.4 341.3 341.3 341.3 341.2 341.2 341.2 341.2 341.1 341.1 341.1 341.1 341.0 + 341.0 341.0 340.9 340.9 340.9 340.9 340.8 340.8 340.8 340.7 340.7 340.7 340.6 ][ 431.0 431.0 431.0 431.0 431.0 + 431.0 430.9 430.9 430.9 430.9 430.9 430.9 430.9 430.9 430.9 430.8 430.8 430.8 430.8 430.8 430.8 430.8 430.8 + 430.8 430.8 430.8 430.7 430.7 430.7 430.7 430.7 ]plong + s[ 341.5 341.6 341.6 341.6 341.6 341.7 341.7 341.7 341.8 341.8 341.8 341.8 341.9 341.9 341.9 342.0 342.0 342.0 + 342.1 342.1 342.1 342.2 342.2 342.2 342.2 342.3 342.3 342.3 342.3 342.4 342.4 342.4 342.5 342.5 ][ 431.0 431.0 + 431.0 431.0 431.0 431.0 431.1 431.1 431.1 431.1 431.1 431.1 431.1 431.1 431.1 431.2 431.2 431.2 431.2 431.2 + 431.2 431.2 431.2 431.2 431.2 431.2 431.3 431.3 431.3 431.3 431.3 431.3 431.3 431.3 ]plong + s[ 342.5 342.5 342.6 342.6 342.6 342.7 342.7 342.7 342.7 342.8 342.8 342.8 342.9 342.9 342.9 342.9 343.0 343.0 + 343.0 343.1 343.1 343.1 343.1 343.2 343.2 343.2 343.3 343.3 343.3 343.4 343.4 343.4 343.4 343.5 ][ 431.3 431.3 + 431.3 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.5 431.5 431.5 431.5 431.5 431.5 + 431.5 431.5 431.5 431.5 431.5 431.5 431.5 431.6 431.6 431.6 431.6 431.6 431.6 431.6 ]plong + s[ 343.7 343.6 343.6 343.6 343.5 343.5 343.5 ][ 431.7 431.7 431.7 431.7 431.6 431.6 431.6 ]plong + s[ 444.2 444.2 444.3 444.3 444.3 444.4 444.4 ][ 431.7 431.7 431.7 431.7 431.6 431.6 431.6 ]plong + s[ 444.4 444.4 444.5 444.5 444.5 444.6 444.6 444.6 444.6 444.7 444.7 444.7 444.8 444.8 444.8 444.9 444.9 444.9 + 445.0 445.0 445.0 445.0 445.1 445.1 445.1 445.1 445.2 445.2 445.2 445.3 445.3 445.3 445.3 445.4 ][ 431.6 431.6 + 431.6 431.6 431.6 431.6 431.6 431.5 431.5 431.5 431.5 431.5 431.5 431.5 431.5 431.5 431.5 431.5 431.5 431.5 + 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.4 431.3 431.3 431.3 ]plong + s[ 445.4 445.4 445.4 445.5 445.5 445.5 445.6 445.6 445.6 445.7 445.7 445.7 445.7 445.8 445.8 445.8 445.9 445.9 + 445.9 446.0 446.0 446.0 446.0 446.1 446.1 446.1 446.2 446.2 446.2 446.2 446.3 446.3 446.3 446.4 ][ 431.3 431.3 + 431.3 431.3 431.3 431.3 431.3 431.2 431.2 431.2 431.2 431.2 431.2 431.2 431.2 431.2 431.2 431.2 431.2 431.1 + 431.1 431.1 431.1 431.1 431.1 431.1 431.1 431.1 431.0 431.0 431.0 431.0 431.0 431.0 ]plong + s[ 446.4 446.4 446.4 446.4 446.5 446.5 446.5 446.6 446.6 446.6 446.7 446.7 446.7 446.8 446.8 446.8 446.8 446.9 + 446.9 446.9 447.0 447.0 447.0 447.0 447.1 447.1 447.1 447.1 447.2 447.2 447.2 ][ 431.0 431.0 431.0 431.0 431.0 + 431.0 430.9 430.9 430.9 430.9 430.9 430.9 430.9 430.9 430.9 430.8 430.8 430.8 430.8 430.8 430.8 430.8 430.8 + 430.8 430.8 430.8 430.7 430.7 430.7 430.7 430.7 ]plong + s[ 344.5 344.4 344.4 344.4 344.3 344.3 344.3 344.3 344.2 344.2 344.2 344.1 344.1 344.1 344.1 344.0 344.0 344.0 + 343.9 343.9 343.9 343.8 343.8 343.8 343.8 343.7 343.7 343.7 ][ 431.9 431.9 431.9 431.9 431.9 431.9 431.9 431.9 + 431.9 431.9 431.8 431.8 431.8 431.8 431.8 431.8 431.8 431.8 431.8 431.7 431.7 431.7 431.7 431.7 431.7 431.7 + 431.7 431.7 ]plong + s[ 344.5 344.5 344.5 344.6 344.6 344.6 344.7 344.7 344.7 344.7 344.8 344.8 344.8 344.9 344.9 344.9 345.0 345.0 + 345.0 345.1 345.1 345.1 345.1 345.2 345.2 345.2 345.2 345.3 345.3 345.3 345.4 345.4 345.4 ][ 431.9 431.9 431.9 + 432.0 432.0 432.0 432.0 432.0 432.0 432.0 432.0 432.0 432.1 432.1 432.1 432.1 432.1 432.1 432.1 432.1 432.1 + 432.1 432.1 432.1 432.2 432.2 432.2 432.2 432.2 432.2 432.2 432.2 432.2 ]plong + s[ 345.4 345.5 345.5 345.5 345.6 345.6 345.6 345.6 345.7 345.7 345.7 345.8 345.8 345.8 345.9 345.9 345.9 346.0 + 346.0 346.0 346.0 346.1 346.1 346.1 346.1 346.2 346.2 346.2 346.3 346.3 346.3 346.4 346.4 346.4 ][ 432.2 432.2 + 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.4 432.4 432.4 432.4 432.4 432.4 + 432.4 432.4 432.4 432.4 432.5 432.5 432.5 432.5 432.5 432.5 432.5 432.5 432.5 432.5 ]plong + s[ 346.9 346.9 346.9 346.8 346.8 346.8 346.7 346.7 346.7 346.6 346.6 346.6 346.5 346.5 346.5 346.5 346.4 ][ 432.7 + 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.5 432.5 432.5 432.5 ]plong + s[ 441.0 441.0 441.0 441.1 441.1 441.1 441.2 441.2 441.2 441.2 441.3 441.3 441.3 441.4 441.4 441.4 441.5 ][ 432.7 + 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.6 432.5 432.5 432.5 432.5 ]plong + s[ 441.5 441.5 441.5 441.5 441.6 441.6 441.6 441.7 441.7 441.7 441.8 441.8 441.8 441.9 441.9 441.9 441.9 442.0 + 442.0 442.0 442.1 442.1 442.1 442.1 442.2 442.2 442.2 442.3 442.3 442.3 442.4 442.4 442.4 442.4 ][ 432.5 432.5 + 432.5 432.5 432.5 432.5 432.5 432.5 432.5 432.5 432.4 432.4 432.4 432.4 432.4 432.4 432.4 432.4 432.4 432.4 + 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.3 432.2 432.2 ]plong + s[ 442.4 442.5 442.5 442.5 442.6 442.6 442.6 442.7 442.7 442.7 442.8 442.8 442.8 442.8 442.9 442.9 442.9 443.0 + 443.0 443.0 443.1 443.1 443.1 443.1 443.2 443.2 443.2 443.3 443.3 443.3 443.3 443.4 443.4 ][ 432.2 432.2 432.2 + 432.2 432.2 432.2 432.2 432.2 432.2 432.1 432.1 432.1 432.1 432.1 432.1 432.1 432.1 432.1 432.1 432.1 432.0 + 432.0 432.0 432.0 432.0 432.0 432.0 432.0 432.0 432.0 431.9 431.9 431.9 ]plong + s[ 443.4 443.4 443.5 443.5 443.5 443.6 443.6 443.6 443.7 443.7 443.7 443.7 443.8 443.8 443.8 443.9 443.9 443.9 + 444.0 444.0 444.0 444.1 444.1 444.1 444.1 444.2 444.2 444.2 ][ 431.9 431.9 431.9 431.9 431.9 431.9 431.9 431.9 + 431.9 431.9 431.8 431.8 431.8 431.8 431.8 431.8 431.8 431.8 431.8 431.7 431.7 431.7 431.7 431.7 431.7 431.7 + 431.7 431.7 ]plong + s[ 347.4 347.4 347.3 347.3 347.3 347.2 347.2 347.2 347.2 347.1 347.1 347.1 347.0 347.0 347.0 346.9 346.9 ][ 432.8 + 432.8 432.8 432.8 432.8 432.8 432.8 432.7 432.7 432.7 432.7 432.7 432.7 432.7 432.7 432.7 432.7 ]plong + s[ 347.4 347.4 347.5 347.5 347.5 347.6 347.6 347.6 347.6 347.7 347.7 347.7 347.8 347.8 347.8 347.9 347.9 347.9 + 348.0 348.0 348.0 348.0 348.1 348.1 348.1 348.2 348.2 348.2 348.3 348.3 348.3 348.3 348.4 ][ 432.8 432.8 432.8 + 432.8 432.8 432.8 432.8 432.9 432.9 432.9 432.9 432.9 432.9 432.9 432.9 432.9 432.9 433.0 433.0 433.0 433.0 + 433.0 433.0 433.0 433.0 433.0 433.0 433.0 433.0 433.1 433.1 433.1 433.1 ]plong + s[ 348.4 348.4 348.4 348.5 348.5 348.5 348.6 348.6 348.6 348.7 348.7 348.7 348.7 348.8 348.8 348.9 348.9 348.9 + 348.9 349.0 349.0 349.0 349.0 349.1 349.1 349.1 349.2 349.2 349.2 349.3 349.3 349.3 349.4 ][ 433.1 433.1 433.1 + 433.1 433.1 433.1 433.1 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.3 + 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.4 433.4 ]plong + s[ 349.4 349.4 349.4 349.4 349.5 349.5 349.6 349.6 349.6 349.6 349.7 349.7 349.7 349.8 349.8 349.8 349.9 349.9 + 349.9 349.9 350.0 350.0 350.0 350.1 350.1 350.1 350.2 350.2 350.2 350.3 350.3 350.3 350.3 ][ 433.4 433.4 433.4 + 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 + 433.5 433.5 433.5 433.5 433.5 433.6 433.6 433.6 433.6 433.6 433.6 433.6 ]plong + 350.3 433.6 350.4 433.6 350.4 433.6 350.4 433.6 4 pls + 437.5 433.6 437.5 433.6 437.5 433.6 437.4 433.6 4 pls + s[ 437.5 437.6 437.6 437.6 437.7 437.7 437.7 437.7 437.8 437.8 437.9 437.9 437.9 437.9 438.0 438.0 438.0 438.1 + 438.1 438.1 438.2 438.2 438.2 438.3 438.3 438.3 438.3 438.4 438.4 438.4 438.5 438.5 438.5 ][ 433.6 433.6 433.6 + 433.6 433.6 433.6 433.6 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 433.5 + 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.4 433.4 ]plong + s[ 438.5 438.6 438.6 438.6 438.6 438.7 438.7 438.7 438.8 438.8 438.8 438.9 438.9 438.9 439.0 439.0 439.0 439.0 + 439.1 439.1 439.2 439.2 439.2 439.2 439.3 439.3 439.3 439.3 439.4 439.4 439.4 439.5 439.5 ][ 433.4 433.4 433.3 + 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.3 433.2 433.2 433.2 433.2 433.2 433.2 433.2 433.2 + 433.2 433.2 433.2 433.2 433.1 433.1 433.1 433.1 433.1 433.1 433.1 433.1 ]plong + s[ 439.5 439.5 439.6 439.6 439.6 439.7 439.7 439.7 439.7 439.8 439.8 439.9 439.9 439.9 439.9 440.0 440.0 440.0 + 440.1 440.1 440.1 440.2 440.2 440.2 440.2 440.3 440.3 440.3 440.4 440.4 440.4 440.4 440.5 ][ 433.1 433.1 433.1 + 433.1 433.0 433.0 433.0 433.0 433.0 433.0 433.0 433.0 433.0 433.0 433.0 433.0 432.9 432.9 432.9 432.9 432.9 + 432.9 432.9 432.9 432.9 432.9 432.8 432.8 432.8 432.8 432.8 432.8 432.8 ]plong + s[ 440.5 440.5 440.5 440.6 440.6 440.6 440.7 440.7 440.7 440.8 440.8 440.8 440.8 440.9 440.9 441.0 441.0 ][ 432.8 + 432.8 432.8 432.8 432.8 432.8 432.8 432.7 432.7 432.7 432.7 432.7 432.7 432.7 432.7 432.7 432.7 ]plong + s[ 351.3 351.3 351.2 351.2 351.2 351.2 351.1 351.1 351.0 351.0 351.0 351.0 350.9 350.9 350.9 350.9 350.8 350.8 + 350.8 350.7 350.7 350.7 350.6 350.6 350.6 350.5 350.5 350.5 350.4 ][ 433.9 433.9 433.9 433.9 433.8 433.8 433.8 + 433.8 433.8 433.8 433.8 433.8 433.8 433.8 433.8 433.7 433.7 433.7 433.7 433.7 433.7 433.7 433.7 433.7 433.7 + 433.7 433.7 433.7 433.6 ]plong + s[ 351.3 351.3 351.4 351.4 351.4 351.5 351.5 351.5 351.6 351.6 351.6 351.7 351.7 351.7 351.8 351.8 351.8 351.8 + 351.9 351.9 351.9 351.9 352.0 352.0 352.1 352.1 352.1 352.1 352.2 352.2 352.2 352.3 352.3 ][ 433.9 433.9 433.9 + 433.9 433.9 433.9 433.9 433.9 433.9 433.9 433.9 434.0 434.0 434.0 434.0 434.0 434.0 434.0 434.0 434.0 434.0 + 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.1 ]plong + s[ 352.3 352.3 352.4 352.4 352.4 352.5 352.5 352.5 352.6 352.6 352.6 352.7 352.7 352.7 352.7 352.8 352.8 352.9 + 352.9 352.9 352.9 353.0 353.0 353.0 353.0 353.1 353.1 353.2 353.2 353.2 353.2 353.3 ][ 434.1 434.1 434.1 434.2 + 434.2 434.2 434.2 434.2 434.2 434.2 434.2 434.2 434.2 434.2 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 + 434.3 434.3 434.3 434.3 434.3 434.3 434.4 434.4 434.4 434.4 ]plong + s[ 353.3 353.3 353.3 353.4 353.4 353.4 353.5 353.5 353.5 353.6 353.6 353.6 353.7 353.7 353.7 353.8 353.8 353.8 + 353.8 353.9 353.9 353.9 354.0 354.0 354.0 354.1 354.1 354.1 354.1 354.2 354.2 354.3 ][ 434.4 434.4 434.4 434.4 + 434.4 434.4 434.4 434.4 434.4 434.4 434.4 434.4 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.5 + 434.5 434.6 434.6 434.6 434.6 434.6 434.6 434.6 434.6 434.6 ]plong + 354.3 434.6 354.3 434.6 2 pls + 433.6 434.6 433.6 434.6 433.6 434.6 3 pls + s[ 433.6 433.7 433.7 433.7 433.8 433.8 433.8 433.9 433.9 433.9 433.9 434.0 434.0 434.1 434.1 434.1 434.1 434.2 + 434.2 434.2 434.3 434.3 434.3 434.4 434.4 434.4 434.4 434.5 434.5 434.5 434.6 434.6 ][ 434.6 434.6 434.6 434.6 + 434.6 434.6 434.6 434.6 434.6 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.5 434.4 434.4 + 434.4 434.4 434.4 434.4 434.4 434.4 434.4 434.4 434.4 434.4 ]plong + s[ 434.6 434.6 434.7 434.7 434.7 434.8 434.8 434.8 434.9 434.9 434.9 435.0 435.0 435.0 435.0 435.1 435.1 435.2 + 435.2 435.2 435.2 435.3 435.3 435.3 435.3 435.4 435.4 435.5 435.5 435.5 435.5 435.6 ][ 434.4 434.4 434.4 434.4 + 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.3 434.2 434.2 434.2 434.2 + 434.2 434.2 434.2 434.2 434.2 434.2 434.2 434.1 434.1 434.1 ]plong + s[ 435.6 435.6 435.6 435.7 435.7 435.7 435.8 435.8 435.8 435.9 435.9 435.9 436.0 436.0 436.0 436.1 436.1 436.1 + 436.1 436.2 436.2 436.2 436.3 436.3 436.3 436.4 436.4 436.4 436.4 436.5 436.5 436.5 436.6 ][ 434.1 434.1 434.1 + 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.1 434.0 434.0 434.0 434.0 434.0 434.0 434.0 434.0 434.0 + 434.0 433.9 433.9 433.9 433.9 433.9 433.9 433.9 433.9 433.9 433.9 433.9 ]plong + s[ 436.6 436.6 436.6 436.7 436.7 436.7 436.8 436.8 436.8 436.9 436.9 436.9 437.0 437.0 437.0 437.0 437.1 437.1 + 437.1 437.2 437.2 437.2 437.3 437.3 437.3 437.3 437.4 437.4 437.4 ][ 433.9 433.9 433.9 433.9 433.8 433.8 433.8 + 433.8 433.8 433.8 433.8 433.8 433.8 433.8 433.7 433.7 433.7 433.7 433.7 433.7 433.7 433.7 433.7 433.7 433.7 + 433.7 433.7 433.7 433.6 ]plong + s[ 354.3 354.3 354.3 354.4 354.4 354.5 354.5 354.5 354.5 354.6 354.6 354.6 354.7 354.7 354.7 354.8 354.8 354.8 + 354.8 354.9 354.9 354.9 355.0 355.0 355.0 355.1 355.1 355.1 355.2 355.2 355.2 ][ 434.6 434.6 434.6 434.6 434.6 + 434.6 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.8 434.8 434.8 434.8 434.8 434.8 + 434.8 434.8 434.8 434.8 434.8 434.8 434.8 434.8 ]plong + s[ 355.2 355.3 355.3 355.3 355.4 355.4 355.4 355.5 355.5 355.5 355.6 355.6 355.6 355.6 355.7 355.7 355.8 355.8 + 355.8 355.8 355.9 355.9 355.9 356.0 356.0 356.0 356.1 356.1 356.1 356.1 356.2 356.2 ][ 434.8 434.8 434.9 434.9 + 434.9 434.9 434.9 434.9 434.9 434.9 434.9 434.9 434.9 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 + 435.0 435.0 435.0 435.0 435.0 435.0 435.1 435.1 435.1 435.1 ]plong + s[ 356.2 356.3 356.3 356.3 356.3 356.4 356.4 356.5 356.5 356.5 356.5 356.6 356.6 356.6 356.7 356.7 356.7 356.8 + 356.8 356.8 356.8 356.9 356.9 357.0 357.0 357.0 357.0 357.1 357.1 357.2 357.2 357.2 ][ 435.1 435.1 435.1 435.1 + 435.1 435.1 435.1 435.1 435.1 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 + 435.2 435.2 435.3 435.3 435.3 435.3 435.3 435.3 435.3 435.3 ]plong + s[ 357.2 357.2 357.3 357.3 357.3 357.4 357.4 357.4 357.5 357.5 357.5 357.6 357.6 357.6 357.7 357.7 357.7 357.7 + 357.8 357.8 357.9 357.9 357.9 357.9 358.0 358.0 358.0 358.1 358.1 358.1 358.2 ][ 435.3 435.3 435.3 435.3 435.3 + 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.5 + 435.5 435.5 435.5 435.5 435.5 435.5 435.5 435.5 ]plong + s[ 358.6 358.6 358.5 358.5 358.5 358.4 358.4 358.4 358.3 358.3 358.3 358.2 358.2 358.2 ][ 435.6 435.6 435.6 435.6 + 435.6 435.6 435.5 435.5 435.5 435.5 435.5 435.5 435.5 435.5 ]plong + s[ 429.3 429.3 429.4 429.4 429.4 429.5 429.5 429.5 429.5 429.6 429.6 429.6 429.7 429.7 ][ 435.6 435.6 435.6 435.6 + 435.6 435.6 435.5 435.5 435.5 435.5 435.5 435.5 435.5 435.5 ]plong + s[ 429.7 429.7 429.8 429.8 429.8 429.9 429.9 429.9 430.0 430.0 430.0 430.1 430.1 430.1 430.2 430.2 430.2 430.3 + 430.3 430.3 430.4 430.4 430.4 430.5 430.5 430.5 430.5 430.6 430.6 430.6 430.7 ][ 435.5 435.5 435.5 435.5 435.5 + 435.5 435.5 435.5 435.5 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 435.4 + 435.4 435.4 435.4 435.3 435.3 435.3 435.3 435.3 ]plong + s[ 430.7 430.7 430.7 430.8 430.8 430.8 430.9 430.9 430.9 431.0 431.0 431.0 431.1 431.1 431.1 431.2 431.2 431.2 + 431.3 431.3 431.3 431.4 431.4 431.4 431.4 431.5 431.5 431.5 431.6 431.6 431.6 431.7 ][ 435.3 435.3 435.3 435.3 + 435.3 435.3 435.3 435.3 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 435.2 + 435.2 435.1 435.1 435.1 435.1 435.1 435.1 435.1 435.1 435.1 ]plong + s[ 431.7 431.7 431.7 431.8 431.8 431.8 431.9 431.9 431.9 431.9 432.0 432.0 432.1 432.1 432.1 432.1 432.2 432.2 + 432.3 432.3 432.3 432.3 432.4 432.4 432.4 432.4 432.5 432.5 432.6 432.6 432.6 432.6 ][ 435.1 435.1 435.1 435.1 + 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 435.0 434.9 434.9 434.9 + 434.9 434.9 434.9 434.9 434.9 434.9 434.9 434.9 434.8 434.8 ]plong + s[ 432.6 432.7 432.7 432.7 432.8 432.8 432.8 432.9 432.9 432.9 433.0 433.0 433.0 433.1 433.1 433.1 433.2 433.2 + 433.2 433.3 433.3 433.3 433.4 433.4 433.4 433.4 433.5 433.5 433.5 433.6 ][ 434.8 434.8 434.8 434.8 434.8 434.8 + 434.8 434.8 434.8 434.8 434.8 434.8 434.8 434.8 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.7 434.7 + 434.7 434.6 434.6 434.6 434.6 434.6 ]plong + s[ 359.2 359.1 359.1 359.1 359.0 359.0 359.0 359.0 358.9 358.9 358.8 358.8 358.8 358.8 358.7 358.7 358.7 358.6 + 358.6 ][ 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.6 435.6 435.6 435.6 435.6 + 435.6 435.6 ]plong + s[ 359.2 359.2 359.2 359.3 359.3 359.3 359.4 359.4 359.4 359.5 359.5 359.5 359.6 359.6 359.6 359.7 359.7 359.7 + 359.7 359.8 359.8 359.9 359.9 359.9 359.9 360.0 360.0 360.1 360.1 360.1 360.1 ][ 435.7 435.7 435.7 435.7 435.7 + 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.9 435.9 435.9 435.9 435.9 + 435.9 435.9 435.9 435.9 435.9 435.9 435.9 435.9 ]plong + s[ 360.1 360.2 360.2 360.3 360.3 360.3 360.3 360.4 360.4 360.5 360.5 360.5 360.5 360.6 360.6 360.6 360.7 360.7 + 360.7 360.8 360.8 360.8 360.9 360.9 360.9 361.0 361.0 361.0 361.1 361.1 361.1 ][ 435.9 435.9 435.9 435.9 436.0 + 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.1 436.1 436.1 436.1 436.1 436.1 + 436.1 436.1 436.1 436.1 436.1 436.1 436.1 436.1 ]plong + s[ 361.1 361.2 361.2 361.2 361.3 361.3 361.3 361.4 361.4 361.4 361.5 361.5 361.5 361.6 361.6 361.6 361.7 361.7 + 361.7 361.7 361.8 361.8 361.9 361.9 361.9 361.9 362.0 362.0 362.1 362.1 362.1 ][ 436.1 436.1 436.1 436.1 436.2 + 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.3 436.3 436.3 436.3 436.3 + 436.3 436.3 436.3 436.3 436.3 436.3 436.3 436.3 ]plong + s[ 362.1 362.1 362.2 362.2 362.3 362.3 362.3 362.3 362.4 362.4 362.5 362.5 362.5 362.5 362.6 362.6 362.6 362.7 + 362.7 362.7 362.8 362.8 362.8 362.9 362.9 362.9 363.0 363.0 363.0 363.1 ][ 436.3 436.3 436.3 436.3 436.3 436.4 + 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 + 436.5 436.5 436.5 436.5 436.5 436.5 ]plong + s[ 363.5 363.5 363.5 363.4 363.4 363.4 363.3 363.3 363.3 363.2 363.2 363.2 363.1 363.1 363.1 ][ 436.6 436.6 436.6 + 436.6 436.6 436.6 436.5 436.5 436.5 436.5 436.5 436.5 436.5 436.5 436.5 ]plong + s[ 424.4 424.4 424.4 424.5 424.5 424.5 424.6 424.6 424.6 424.7 424.7 424.7 424.8 424.8 424.8 ][ 436.6 436.6 436.6 + 436.6 436.6 436.6 436.5 436.5 436.5 436.5 436.5 436.5 436.5 436.5 436.5 ]plong + s[ 424.8 424.8 424.9 424.9 425.0 425.0 425.0 425.0 425.1 425.1 425.2 425.2 425.2 425.2 425.3 425.3 425.4 425.4 + 425.4 425.4 425.5 425.5 425.6 425.6 425.6 425.6 425.7 425.7 425.7 425.8 ][ 436.5 436.5 436.5 436.5 436.5 436.5 + 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 436.4 + 436.4 436.3 436.3 436.3 436.3 436.3 ]plong + s[ 425.8 425.8 425.8 425.9 425.9 425.9 426.0 426.0 426.0 426.1 426.1 426.1 426.2 426.2 426.2 426.3 426.3 426.3 + 426.4 426.4 426.4 426.5 426.5 426.5 426.6 426.6 426.6 426.6 426.7 426.7 426.8 ][ 436.3 436.3 436.3 436.3 436.3 + 436.3 436.3 436.3 436.3 436.3 436.3 436.3 436.3 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 436.2 + 436.2 436.2 436.2 436.2 436.1 436.1 436.1 436.1 ]plong + s[ 426.8 426.8 426.8 426.8 426.9 426.9 427.0 427.0 427.0 427.0 427.1 427.1 427.2 427.2 427.2 427.2 427.3 427.3 + 427.4 427.4 427.4 427.4 427.5 427.5 427.6 427.6 427.6 427.6 427.7 427.7 427.7 ][ 436.1 436.1 436.1 436.1 436.1 + 436.1 436.1 436.1 436.1 436.1 436.1 436.1 436.1 436.1 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.0 436.0 + 436.0 436.0 436.0 435.9 435.9 435.9 435.9 435.9 ]plong + s[ 427.7 427.8 427.8 427.8 427.9 427.9 427.9 428.0 428.0 428.0 428.1 428.1 428.1 428.2 428.2 428.2 428.3 428.3 + 428.3 428.4 428.4 428.4 428.5 428.5 428.5 428.5 428.6 428.6 428.6 428.7 428.7 428.7 ][ 435.9 435.9 435.9 435.9 + 435.9 435.9 435.9 435.9 435.9 435.9 435.9 435.9 435.9 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 435.8 + 435.8 435.8 435.8 435.8 435.7 435.7 435.7 435.7 435.7 435.7 ]plong + s[ 428.7 428.8 428.8 428.8 428.9 428.9 428.9 429.0 429.0 429.0 429.1 429.1 429.1 429.2 429.2 429.2 429.3 429.3 ][ + 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.7 435.6 435.6 435.6 435.6 435.6 435.6 435.6 ]plong + s[ 364.1 364.0 364.0 364.0 363.9 363.9 363.9 363.8 363.8 363.8 363.7 363.7 363.7 363.6 363.6 363.5 363.5 ][ 436.7 + 436.7 436.7 436.7 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 ]plong + s[ 364.1 364.1 364.1 364.2 364.2 364.2 364.3 364.3 364.3 364.4 364.4 364.4 364.5 364.5 364.5 364.6 364.6 364.6 + 364.6 364.7 364.7 364.8 364.8 364.8 364.9 364.9 364.9 365.0 365.0 365.0 ][ 436.7 436.7 436.7 436.7 436.7 436.7 + 436.7 436.7 436.7 436.7 436.7 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 + 436.8 436.8 436.8 436.8 436.8 436.8 ]plong + s[ 365.0 365.1 365.1 365.1 365.2 365.2 365.2 365.3 365.3 365.3 365.4 365.4 365.4 365.5 365.5 365.5 365.5 365.6 + 365.6 365.7 365.7 365.7 365.8 365.8 365.8 365.9 365.9 365.9 366.0 366.0 366.0 ][ 436.8 436.9 436.9 436.9 436.9 + 436.9 436.9 436.9 436.9 436.9 436.9 436.9 436.9 436.9 436.9 436.9 437.0 437.0 437.0 437.0 437.0 437.0 437.0 + 437.0 437.0 437.0 437.0 437.0 437.0 437.0 437.0 ]plong + s[ 366.0 366.1 366.1 366.1 366.2 366.2 366.2 366.3 366.3 366.3 366.4 366.4 366.4 366.5 366.5 366.5 366.6 366.6 + 366.6 366.7 366.7 366.7 366.8 366.8 366.8 366.9 366.9 366.9 367.0 367.0 ][ 437.0 437.0 437.0 437.0 437.0 437.0 + 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.2 437.2 437.2 + 437.2 437.2 437.2 437.2 437.2 437.2 ]plong + s[ 367.0 367.0 367.1 367.1 367.2 367.2 367.2 367.2 367.3 367.3 367.4 367.4 367.4 367.4 367.5 367.5 367.5 367.6 + 367.6 367.7 367.7 367.7 367.7 367.8 367.8 367.9 367.9 367.9 367.9 368.0 ][ 437.2 437.2 437.2 437.2 437.2 437.2 + 437.2 437.2 437.2 437.2 437.2 437.2 437.2 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 + 437.3 437.3 437.3 437.3 437.3 437.3 ]plong + s[ 368.0 368.0 368.1 368.1 368.1 368.2 368.2 368.2 368.3 368.3 368.3 368.4 368.4 368.4 368.4 368.5 368.5 368.6 + 368.6 368.6 368.7 368.7 368.7 368.8 368.8 368.8 368.9 368.9 369.0 ][ 437.3 437.3 437.3 437.3 437.3 437.3 437.4 + 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.5 437.5 437.5 + 437.5 437.5 437.5 437.5 ]plong + s[ 369.5 369.5 369.4 369.4 369.3 369.3 369.3 369.2 369.2 369.2 369.2 369.1 369.1 369.0 369.0 369.0 369.0 ][ 437.6 + 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 ]plong + s[ 418.4 418.4 418.5 418.5 418.5 418.6 418.6 418.7 418.7 418.7 418.7 418.8 418.8 418.8 418.9 418.9 418.9 ][ 437.6 + 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 437.5 ]plong + s[ 418.9 419.0 419.0 419.0 419.1 419.1 419.2 419.2 419.2 419.2 419.3 419.3 419.4 419.4 419.4 419.5 419.5 419.5 + 419.6 419.6 419.6 419.7 419.7 419.7 419.8 419.8 419.8 419.9 419.9 ][ 437.5 437.5 437.5 437.5 437.5 437.5 437.5 + 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.4 437.3 437.3 + 437.3 437.3 437.3 437.3 ]plong + s[ 419.9 419.9 420.0 420.0 420.0 420.1 420.1 420.1 420.2 420.2 420.2 420.3 420.3 420.3 420.4 420.4 420.4 420.5 + 420.5 420.5 420.6 420.6 420.7 420.7 420.7 420.7 420.8 420.8 420.8 420.9 ][ 437.3 437.3 437.3 437.3 437.3 437.3 + 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.3 437.2 437.2 437.2 437.2 437.2 437.2 437.2 + 437.2 437.2 437.2 437.2 437.2 437.2 ]plong + s[ 420.9 420.9 420.9 421.0 421.0 421.0 421.1 421.1 421.2 421.2 421.2 421.2 421.3 421.3 421.4 421.4 421.4 421.4 + 421.5 421.5 421.6 421.6 421.6 421.7 421.7 421.7 421.8 421.8 421.8 421.9 ][ 437.2 437.2 437.2 437.2 437.2 437.2 + 437.2 437.2 437.2 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 437.1 + 437.0 437.0 437.0 437.0 437.0 437.0 ]plong + s[ 421.9 421.9 421.9 421.9 422.0 422.0 422.1 422.1 422.1 422.1 422.2 422.2 422.3 422.3 422.3 422.4 422.4 422.4 + 422.5 422.5 422.5 422.6 422.6 422.6 422.7 422.7 422.7 422.8 422.8 422.8 422.8 ][ 437.0 437.0 437.0 437.0 437.0 + 437.0 437.0 437.0 437.0 437.0 437.0 437.0 437.0 437.0 437.0 436.9 436.9 436.9 436.9 436.9 436.9 436.9 436.9 + 436.9 436.9 436.9 436.9 436.9 436.9 436.8 436.8 ]plong + s[ 422.8 422.9 422.9 423.0 423.0 423.0 423.0 423.1 423.1 423.2 423.2 423.2 423.3 423.3 423.3 423.4 423.4 423.4 + 423.5 423.5 423.5 423.6 423.6 423.6 423.7 423.7 423.7 423.7 423.8 423.8 ][ 436.8 436.8 436.8 436.8 436.8 436.8 + 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.8 436.7 436.7 436.7 436.7 436.7 436.7 + 436.7 436.7 436.7 436.7 436.7 436.7 ]plong + s[ 423.8 423.9 423.9 423.9 423.9 424.0 424.0 424.1 424.1 424.1 424.1 424.2 424.2 424.3 424.3 424.3 424.4 ][ 436.7 + 436.7 436.7 436.7 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 436.6 ]plong + s[ 369.9 369.9 369.9 369.8 369.8 369.8 369.7 369.7 369.7 369.6 369.6 369.5 369.5 369.5 ][ 437.6 437.6 437.6 437.6 + 437.6 437.6 437.6 437.6 437.6 437.6 437.6 437.6 437.6 437.6 ]plong + s[ 369.9 370.0 370.0 370.0 370.1 370.1 370.1 370.2 370.2 370.3 370.3 370.3 370.3 370.4 370.4 370.4 370.5 370.5 + 370.6 370.6 370.6 370.6 370.7 370.7 370.8 370.8 370.8 370.9 370.9 370.9 ][ 437.6 437.6 437.6 437.6 437.7 437.7 + 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 + 437.7 437.7 437.7 437.8 437.8 437.8 ]plong + s[ 370.9 371.0 371.0 371.0 371.1 371.1 371.2 371.2 371.2 371.2 371.3 371.3 371.3 371.4 371.4 371.4 371.5 371.5 + 371.5 371.6 371.6 371.7 371.7 371.7 371.7 371.8 371.8 371.9 371.9 ][ 437.8 437.8 437.8 437.8 437.8 437.8 437.8 + 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.9 437.9 437.9 437.9 437.9 437.9 437.9 437.9 437.9 + 437.9 437.9 437.9 437.9 ]plong + s[ 371.9 371.9 372.0 372.0 372.0 372.1 372.1 372.1 372.2 372.2 372.3 372.3 372.3 372.3 372.4 372.4 372.4 372.5 + 372.5 372.6 372.6 372.6 372.6 372.7 372.7 372.8 372.8 372.8 372.9 ][ 437.9 437.9 437.9 437.9 437.9 437.9 437.9 + 437.9 437.9 437.9 437.9 437.9 437.9 438.0 438.0 438.0 438.0 438.0 438.0 438.0 438.0 438.0 438.0 438.0 438.0 + 438.0 438.0 438.0 438.0 ]plong + s[ 372.9 372.9 372.9 373.0 373.0 373.0 373.1 373.1 373.2 373.2 373.2 373.2 373.3 373.3 373.3 373.4 373.4 373.5 + 373.5 373.5 373.6 373.6 373.6 373.7 373.7 373.7 373.8 373.8 373.9 ][ 438.0 438.0 438.0 438.1 438.1 438.1 438.1 + 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 + 438.1 438.1 438.1 438.1 ]plong + s[ 373.9 373.9 373.9 373.9 374.0 374.0 374.1 374.1 374.1 374.2 374.2 374.2 374.2 374.3 374.3 374.4 374.4 374.4 + 374.5 374.5 374.6 374.6 374.6 374.6 374.7 374.7 374.8 374.8 374.8 ][ 438.1 438.1 438.2 438.2 438.2 438.2 438.2 + 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.3 438.3 438.3 + 438.3 438.3 438.3 438.3 ]plong + s[ 374.8 374.9 374.9 374.9 375.0 375.0 375.0 375.1 375.1 375.2 375.2 375.2 375.3 375.3 375.3 375.3 375.4 375.4 + 375.5 375.5 375.5 375.6 375.6 375.6 375.7 375.7 375.7 375.8 375.8 ][ 438.3 438.3 438.3 438.3 438.3 438.3 438.3 + 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.4 + 438.4 438.4 438.4 438.4 ]plong + s[ 375.8 375.9 375.9 375.9 376.0 376.0 376.0 376.1 376.1 376.1 376.2 376.2 376.2 376.3 376.3 376.3 376.4 376.4 + 376.4 376.5 376.5 376.6 376.6 376.6 376.7 376.7 376.7 376.8 376.8 ][ 438.4 438.4 438.4 438.4 438.4 438.4 438.4 + 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 + 438.4 438.5 438.5 438.5 ]plong + s[ 377.5 377.5 377.5 377.4 377.4 377.3 377.3 377.3 377.2 377.2 377.1 377.1 377.1 377.1 377.0 377.0 377.0 376.9 + 376.9 376.8 376.8 ][ 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 + 438.5 438.5 438.5 438.5 438.5 438.5 ]plong + s[ 410.4 410.4 410.4 410.5 410.5 410.5 410.6 410.6 410.7 410.7 410.7 410.8 410.8 410.8 410.9 410.9 410.9 411.0 + 411.0 411.1 411.1 ][ 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 438.5 + 438.5 438.5 438.5 438.5 438.5 438.5 ]plong + s[ 411.1 411.1 411.1 411.2 411.2 411.2 411.3 411.3 411.4 411.4 411.4 411.5 411.5 411.5 411.6 411.6 411.6 411.7 + 411.7 411.8 411.8 411.8 411.9 411.9 411.9 412.0 412.0 412.0 412.1 ][ 438.5 438.5 438.5 438.4 438.4 438.4 438.4 + 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 438.4 + 438.4 438.4 438.4 438.4 ]plong + s[ 412.1 412.1 412.1 412.2 412.2 412.2 412.3 412.3 412.3 412.4 412.4 412.5 412.5 412.5 412.6 412.6 412.6 412.7 + 412.7 412.7 412.8 412.8 412.9 412.9 412.9 413.0 413.0 413.0 413.0 ][ 438.4 438.4 438.4 438.4 438.4 438.3 438.3 + 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 438.3 + 438.3 438.3 438.3 438.3 ]plong + s[ 413.0 413.1 413.1 413.2 413.2 413.2 413.3 413.3 413.3 413.4 413.4 413.4 413.5 413.5 413.6 413.6 413.6 413.7 + 413.7 413.7 413.8 413.8 413.8 413.9 413.9 414.0 414.0 414.0 414.0 ][ 438.3 438.3 438.3 438.3 438.3 438.3 438.3 + 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 438.2 + 438.2 438.2 438.1 438.1 ]plong + s[ 414.0 414.1 414.1 414.1 414.2 414.2 414.3 414.3 414.3 414.3 414.4 414.4 414.5 414.5 414.5 414.6 414.6 414.6 + 414.7 414.7 414.7 414.8 414.8 414.9 414.9 414.9 414.9 415.0 415.0 ][ 438.1 438.1 438.1 438.1 438.1 438.1 438.1 + 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 438.1 + 438.0 438.0 438.0 438.0 ]plong + s[ 415.0 415.0 415.1 415.1 415.2 415.2 415.2 415.2 415.3 415.3 415.4 415.4 415.4 415.5 415.5 415.5 415.6 415.6 + 415.6 415.7 415.7 415.8 415.8 415.8 415.9 415.9 415.9 415.9 416.0 ][ 438.0 438.0 438.0 438.0 438.0 438.0 438.0 + 438.0 438.0 438.0 438.0 438.0 438.0 438.0 438.0 438.0 437.9 437.9 437.9 437.9 437.9 437.9 437.9 437.9 437.9 + 437.9 437.9 437.9 437.9 ]plong + s[ 416.0 416.0 416.1 416.1 416.1 416.1 416.2 416.2 416.3 416.3 416.3 416.4 416.4 416.4 416.5 416.5 416.5 416.6 + 416.6 416.7 416.7 416.7 416.7 416.8 416.8 416.9 416.9 416.9 417.0 ][ 437.9 437.9 437.9 437.9 437.9 437.9 437.9 + 437.9 437.9 437.9 437.9 437.9 437.9 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.8 437.8 + 437.8 437.8 437.8 437.8 ]plong + s[ 417.0 417.0 417.0 417.0 417.1 417.1 417.2 417.2 417.2 417.3 417.3 417.3 417.4 417.4 417.4 417.5 417.5 417.6 + 417.6 417.6 417.6 417.7 417.7 417.8 417.8 417.8 417.9 417.9 417.9 417.9 ][ 437.8 437.8 437.8 437.7 437.7 437.7 + 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 437.7 + 437.7 437.7 437.6 437.6 437.6 437.6 ]plong + s[ 417.9 418.0 418.0 418.1 418.1 418.1 418.1 418.2 418.2 418.3 418.3 418.3 418.4 418.4 ][ 437.6 437.6 437.6 437.6 + 437.6 437.6 437.6 437.6 437.6 437.6 437.6 437.6 437.6 437.6 ]plong + s[ 377.8 377.7 377.7 377.7 377.6 377.6 377.5 377.5 ][ 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.5 ]plong + s[ 377.8 377.8 377.9 377.9 377.9 377.9 378.0 378.0 378.1 378.1 378.1 378.2 378.2 378.2 378.3 378.3 378.3 378.4 + 378.4 378.4 378.5 378.5 378.6 378.6 378.6 378.7 378.7 378.8 ][ 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 + 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 + 438.6 438.6 ]plong + s[ 378.8 378.8 378.8 378.8 378.9 378.9 379.0 379.0 379.0 379.1 379.1 379.1 379.2 379.2 379.2 379.3 379.3 379.3 + 379.4 379.4 379.5 379.5 379.5 379.6 379.6 379.7 379.7 379.7 379.7 ][ 438.6 438.7 438.7 438.7 438.7 438.7 438.7 + 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 + 438.7 438.7 438.8 438.8 ]plong + s[ 379.7 379.8 379.8 379.9 379.9 379.9 380.0 380.0 380.0 380.1 380.1 380.1 380.2 380.2 380.2 380.3 380.3 380.4 + 380.4 380.4 380.5 380.5 380.6 380.6 380.6 380.6 380.7 380.7 ][ 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 + 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 + 438.8 438.8 ]plong + s[ 380.7 380.8 380.8 380.8 380.9 380.9 381.0 381.0 381.0 381.1 381.1 381.1 381.1 381.2 381.2 381.3 381.3 381.3 + 381.4 381.4 381.5 381.5 381.5 381.6 381.6 381.6 381.7 381.7 ][ 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 + 438.8 438.8 438.8 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 + 438.9 438.9 ]plong + s[ 381.7 381.7 381.8 381.8 381.9 381.9 381.9 382.0 382.0 382.0 382.1 382.1 382.2 382.2 382.2 382.2 382.3 382.3 + 382.4 382.4 382.4 382.5 382.5 382.6 382.6 382.6 382.7 ][ 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 + 438.9 438.9 438.9 438.9 438.9 438.9 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 ]plong + s[ 382.7 382.7 382.8 382.8 382.8 382.8 382.9 382.9 382.9 383.0 383.0 383.1 383.1 383.1 383.2 383.2 383.3 383.3 + 383.3 383.4 383.4 383.4 383.5 383.5 383.5 383.6 383.6 383.7 ][ 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 + 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 + 439.0 439.0 ]plong + s[ 383.7 383.7 383.7 383.8 383.8 383.9 383.9 383.9 384.0 384.0 384.0 384.0 384.1 384.1 384.2 384.2 384.2 384.3 + 384.3 384.4 384.4 384.4 384.5 384.5 384.6 384.6 384.6 ][ 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 + 439.0 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 ]plong + s[ 384.6 384.7 384.7 384.7 384.8 384.8 384.8 384.9 384.9 384.9 385.0 385.0 385.1 385.1 385.1 385.2 385.2 385.3 + 385.3 385.3 385.4 385.4 385.5 385.5 385.5 385.5 385.6 385.6 ][ 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 + 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.2 439.2 + 439.2 439.2 ]plong + s[ 385.6 385.7 385.7 385.7 385.8 385.8 385.8 385.9 385.9 386.0 386.0 386.0 386.1 386.1 386.2 386.2 386.2 386.3 + 386.3 386.3 386.4 386.4 386.4 386.5 386.5 386.6 386.6 ][ 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 + 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 ]plong + s[ 386.6 386.6 386.7 386.7 386.8 386.8 386.8 386.9 386.9 386.9 387.0 387.0 387.1 387.1 387.1 387.2 387.2 387.2 + 387.3 387.3 387.3 387.4 387.4 387.5 387.5 387.5 387.6 ][ 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 + 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 ]plong + s[ 387.6 387.6 387.7 387.7 387.7 387.8 387.8 387.8 387.9 387.9 388.0 388.0 388.0 388.1 388.1 388.2 388.2 388.2 + 388.3 388.3 388.3 388.4 388.4 388.4 388.5 388.5 388.6 ][ 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 + 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 388.6 388.6 388.6 388.7 388.7 388.8 388.8 388.8 388.9 388.9 388.9 389.0 389.0 389.1 389.1 389.1 389.2 389.2 + 389.3 389.3 389.3 389.4 389.4 389.5 389.5 389.5 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 389.5 389.6 389.6 389.6 389.7 389.7 389.7 389.8 389.8 389.8 389.9 389.9 390.0 390.0 390.0 390.1 390.1 390.2 + 390.2 390.2 390.3 390.3 390.4 390.4 390.4 390.5 390.5 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 390.5 390.6 390.6 390.6 390.7 390.7 390.7 390.8 390.8 390.9 390.9 390.9 391.0 391.0 391.1 391.1 391.1 391.2 + 391.2 391.3 391.3 391.3 391.4 391.4 391.5 391.5 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 391.5 391.5 391.6 391.6 391.6 391.7 391.7 391.7 391.8 391.8 391.8 391.9 391.9 392.0 392.0 392.0 392.1 392.1 + 392.2 392.2 392.2 392.3 392.3 392.4 392.4 392.4 392.5 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 392.5 392.5 392.6 392.6 392.6 392.7 392.7 392.7 392.8 392.8 392.9 392.9 392.9 393.0 393.0 393.1 393.1 393.1 + 393.2 393.2 393.3 393.3 393.3 393.4 393.4 393.5 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 393.5 393.5 393.5 393.6 393.6 393.6 393.7 393.7 393.8 393.8 393.8 393.9 393.9 394.0 394.0 394.0 394.1 394.1 + 394.2 394.2 394.2 394.3 394.3 394.4 394.4 394.4 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 394.4 394.5 394.5 394.6 394.6 394.6 394.7 394.7 394.7 394.8 394.8 394.9 394.9 394.9 395.0 395.0 395.1 395.1 + 395.1 395.2 395.2 395.3 395.3 395.3 395.4 395.4 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 395.4 395.5 395.5 395.5 395.6 395.6 395.6 395.7 395.7 395.8 395.8 395.8 395.9 395.9 396.0 396.0 396.0 396.1 + 396.1 396.2 396.2 396.2 396.3 396.3 396.3 396.4 396.4 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 396.4 396.4 396.5 396.5 396.5 396.6 396.6 396.7 396.7 396.7 396.8 396.8 396.9 396.9 396.9 397.0 397.0 397.1 + 397.1 397.1 397.2 397.2 397.3 397.3 397.3 397.4 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 397.4 397.4 397.5 397.5 397.5 397.6 397.6 397.6 397.7 397.7 397.8 397.8 397.8 397.9 397.9 398.0 398.0 398.0 + 398.1 398.1 398.2 398.2 398.2 398.3 398.3 398.3 398.4 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 398.4 398.4 398.4 398.5 398.5 398.5 398.6 398.6 398.7 398.7 398.7 398.8 398.8 398.9 398.9 398.9 399.0 399.0 + 399.1 399.1 399.1 399.2 399.2 399.3 399.3 399.3 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 + 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 ]plong + s[ 399.3 399.4 399.4 399.4 399.5 399.5 399.6 399.6 399.6 399.6 399.7 399.7 399.8 399.8 399.8 399.9 399.9 400.0 + 400.0 400.0 400.1 400.1 400.2 400.2 400.2 400.3 400.3 ][ 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.3 439.2 + 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 ]plong + s[ 400.3 400.4 400.4 400.4 400.5 400.5 400.5 400.6 400.6 400.7 400.7 400.7 400.7 400.8 400.8 400.9 400.9 400.9 + 401.0 401.0 401.1 401.1 401.1 401.2 401.2 401.3 401.3 ][ 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 + 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 ]plong + s[ 401.3 401.3 401.4 401.4 401.4 401.5 401.5 401.6 401.6 401.6 401.6 401.7 401.7 401.8 401.8 401.8 401.9 401.9 + 402.0 402.0 402.0 402.1 402.1 402.2 402.2 402.2 402.3 ][ 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 + 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 439.2 ]plong + s[ 402.3 402.3 402.3 402.4 402.4 402.4 402.5 402.5 402.5 402.6 402.6 402.7 402.7 402.7 402.8 402.8 402.9 402.9 + 402.9 403.0 403.0 403.1 403.1 403.1 403.2 403.2 403.2 403.3 ][ 439.2 439.2 439.2 439.2 439.1 439.1 439.1 439.1 + 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 + 439.1 439.1 ]plong + s[ 403.3 403.3 403.3 403.4 403.4 403.4 403.5 403.5 403.6 403.6 403.6 403.7 403.7 403.8 403.8 403.8 403.9 403.9 + 403.9 404.0 404.0 404.0 404.1 404.1 404.2 404.2 404.2 ][ 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 + 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.1 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 ]plong + s[ 404.2 404.3 404.3 404.3 404.4 404.4 404.5 404.5 404.5 404.5 404.6 404.6 404.7 404.7 404.7 404.8 404.8 404.9 + 404.9 404.9 405.0 405.0 405.1 405.1 405.1 405.1 405.2 405.2 ][ 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 + 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 + 439.0 439.0 ]plong + s[ 405.2 405.3 405.3 405.3 405.4 405.4 405.4 405.5 405.5 405.6 405.6 405.6 405.7 405.7 405.7 405.8 405.8 405.8 + 405.9 405.9 406.0 406.0 406.0 406.1 406.1 406.2 406.2 ][ 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 439.0 + 439.0 439.0 439.0 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 ]plong + s[ 406.2 406.2 406.2 406.3 406.3 406.3 406.4 406.4 406.5 406.5 406.5 406.6 406.6 406.7 406.7 406.7 406.8 406.8 + 406.8 406.9 406.9 406.9 407.0 407.0 407.1 407.1 407.1 407.2 ][ 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 + 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.9 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 + 438.8 438.8 ]plong + s[ 407.2 407.2 407.2 407.3 407.3 407.3 407.4 407.4 407.4 407.5 407.5 407.6 407.6 407.6 407.7 407.7 407.7 407.8 + 407.8 407.8 407.9 407.9 408.0 408.0 408.0 408.1 408.1 408.2 ][ 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 + 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 438.8 + 438.8 438.8 ]plong + s[ 408.2 408.2 408.2 408.2 408.3 408.3 408.3 408.4 408.4 408.5 408.5 408.5 408.6 408.6 408.7 408.7 408.7 408.7 + 408.8 408.8 408.9 408.9 408.9 409.0 409.0 409.1 409.1 409.1 409.1 ][ 438.8 438.8 438.7 438.7 438.7 438.7 438.7 + 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 438.7 + 438.7 438.7 438.7 438.6 ]plong + s[ 409.1 409.2 409.2 409.2 409.3 409.3 409.4 409.4 409.4 409.5 409.5 409.5 409.6 409.6 409.6 409.7 409.7 409.8 + 409.8 409.8 409.9 409.9 410.0 410.0 410.0 410.0 410.1 410.1 ][ 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 + 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.6 + 438.6 438.6 ]plong + s[ 410.1 410.1 410.2 410.2 410.3 410.3 410.3 410.4 ][ 438.6 438.6 438.6 438.6 438.6 438.6 438.6 438.5 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 194.44 59.99 moveto[ 194.4 194.4 204.0 204.0 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 202.0 201.5 200.7 199.5 199.0 197.8 196.9 196.5 196.5 196.9 197.8 199.0 199.5 200.7 201.5 202.0 202.0 201.5 + 200.7 199.5 198.6 197.4 196.9 ][ 67.5 66.3 65.4 65.0 65.0 65.4 66.3 67.5 67.9 69.2 70.0 70.4 70.4 + 70.0 69.2 67.5 65.4 63.3 62.1 61.7 61.7 62.1 62.9 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 386.00 59.99 moveto[ 386.0 386.0 403.1 403.1 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 390.2 61.7 390.2 70.4 388.9 69.2 388.1 68.8 4 pls + s[ 395.6 395.6 396.0 396.4 397.3 399.0 399.8 400.2 400.6 400.6 400.2 399.4 395.2 401.0 ][ 68.3 68.8 69.6 70.0 + 70.4 70.4 70.0 69.6 68.8 67.9 67.1 65.8 61.7 61.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 580.49 59.99 moveto[ 580.5 580.5 597.6 597.6 ][ 60.0 72.1 72.1 60.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 584.7 61.7 584.7 70.4 583.4 69.2 582.6 68.8 4 pls + s[ 594.7 590.5 590.1 590.5 591.8 593.0 594.3 595.1 595.5 595.5 595.1 594.3 593.0 591.8 590.5 590.1 589.7 ][ 70.4 + 70.4 66.7 67.1 67.5 67.5 67.1 66.3 65.0 64.2 62.9 62.1 61.7 61.7 62.1 62.5 63.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 146.67 77.20 moveto[ 146.7 146.7 196.4 196.4 ][ 77.2 89.3 89.3 77.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.3 82.6 148.8 82.6 2 pls + s[ 161.3 160.0 159.6 159.6 160.0 160.9 162.5 163.8 164.6 165.1 165.1 164.6 164.2 163.0 161.3 160.0 159.6 159.2 + 159.2 159.6 160.5 161.7 163.4 164.2 164.6 164.6 164.2 163.0 161.3 ][ 87.6 87.2 86.4 85.6 84.7 84.3 83.9 + 83.5 82.6 81.8 80.5 79.7 79.3 78.9 78.9 79.3 79.7 80.5 81.8 82.6 83.5 83.9 84.3 84.7 85.6 + 86.4 87.2 87.6 87.6 ]plong + s[ 173.0 172.6 171.3 170.5 169.2 168.4 168.0 168.0 168.4 169.2 170.5 170.9 172.2 173.0 173.4 173.4 173.0 172.2 + 170.9 170.5 169.2 168.4 168.0 ][ 86.4 87.2 87.6 87.6 87.2 86.0 83.9 81.8 80.1 79.3 78.9 78.9 79.3 + 80.1 81.4 81.8 83.0 83.9 84.3 84.3 83.9 83.0 81.8 ]plong + 176.7 84.7 177.2 84.3 176.7 83.9 176.3 84.3 176.7 84.7 5 pls + 176.7 79.7 177.2 79.3 176.7 78.9 176.3 79.3 176.7 79.7 5 pls + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 87.6 87.6 84.3 + 84.3 83.9 83.5 82.2 81.4 80.1 79.3 78.9 78.9 79.3 79.7 80.5 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 87.6 + 87.2 86.0 83.9 82.6 80.5 79.3 78.9 78.9 79.3 80.5 82.6 83.9 86.0 87.2 87.6 87.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 146.67 98.64 moveto[ 146.7 146.7 196.4 196.4 ][ 98.6 110.8 110.8 98.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.3 104.1 148.8 104.1 2 pls + s[ 161.3 160.0 159.6 159.6 160.0 160.9 162.5 163.8 164.6 165.1 165.1 164.6 164.2 163.0 161.3 160.0 159.6 159.2 + 159.2 159.6 160.5 161.7 163.4 164.2 164.6 164.6 164.2 163.0 161.3 ][ 109.1 108.7 107.8 107.0 106.2 105.7 105.3 + 104.9 104.1 103.2 102.0 101.1 100.7 100.3 100.3 100.7 101.1 102.0 103.2 104.1 104.9 105.3 105.7 106.2 107.0 + 107.8 108.7 109.1 109.1 ]plong + 169.2 100.3 173.4 109.1 2 pls + 173.4 109.1 167.6 109.1 2 pls + 176.7 106.2 177.2 105.7 176.7 105.3 176.3 105.7 176.7 106.2 5 pls + 176.7 101.1 177.2 100.7 176.7 100.3 176.3 100.7 176.7 101.1 5 pls + s[ 182.6 181.3 180.5 180.1 180.1 180.5 181.3 182.6 183.4 184.7 185.5 185.9 185.9 185.5 184.7 183.4 182.6 ][ 109.1 + 108.7 107.4 105.3 104.1 102.0 100.7 100.3 100.3 100.7 102.0 104.1 105.3 107.4 108.7 109.1 109.1 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 109.1 + 108.7 107.4 105.3 104.1 102.0 100.7 100.3 100.3 100.7 102.0 104.1 105.3 107.4 108.7 109.1 109.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.00 121.86 moveto[ 178.0 178.0 196.4 196.4 ][ 121.9 134.0 134.0 121.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 132.3 132.3 129.0 + 129.0 128.5 128.1 126.9 126.0 124.8 123.9 123.5 123.5 123.9 124.4 125.2 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 132.3 + 131.9 130.6 128.5 127.3 125.2 123.9 123.5 123.5 123.9 125.2 127.3 128.5 130.6 131.9 132.3 132.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 146.67 147.91 moveto[ 146.7 146.7 196.4 196.4 ][ 147.9 160.0 160.0 147.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.3 153.3 148.8 153.3 2 pls + s[ 161.3 160.0 159.6 159.6 160.0 160.9 162.5 163.8 164.6 165.1 165.1 164.6 164.2 163.0 161.3 160.0 159.6 159.2 + 159.2 159.6 160.5 161.7 163.4 164.2 164.6 164.6 164.2 163.0 161.3 ][ 158.4 157.9 157.1 156.3 155.4 155.0 154.6 + 154.2 153.3 152.5 151.3 150.4 150.0 149.6 149.6 150.0 150.4 151.3 152.5 153.3 154.2 154.6 155.0 155.4 156.3 + 157.1 157.9 158.4 158.4 ]plong + s[ 169.6 168.4 168.0 168.0 168.4 169.2 170.9 172.2 173.0 173.4 173.4 173.0 172.6 171.3 169.6 168.4 168.0 167.6 + 167.6 168.0 168.8 170.1 171.7 172.6 173.0 173.0 172.6 171.3 169.6 ][ 158.4 157.9 157.1 156.3 155.4 155.0 154.6 + 154.2 153.3 152.5 151.3 150.4 150.0 149.6 149.6 150.0 150.4 151.3 152.5 153.3 154.2 154.6 155.0 155.4 156.3 + 157.1 157.9 158.4 158.4 ]plong + 176.7 155.4 177.2 155.0 176.7 154.6 176.3 155.0 176.7 155.4 5 pls + 176.7 150.4 177.2 150.0 176.7 149.6 176.3 150.0 176.7 150.4 5 pls + s[ 182.6 181.3 180.5 180.1 180.1 180.5 181.3 182.6 183.4 184.7 185.5 185.9 185.9 185.5 184.7 183.4 182.6 ][ 158.4 + 157.9 156.7 154.6 153.3 151.3 150.0 149.6 149.6 150.0 151.3 153.3 154.6 156.7 157.9 158.4 158.4 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 158.4 + 157.9 156.7 154.6 153.3 151.3 150.0 149.6 149.6 150.0 151.3 153.3 154.6 156.7 157.9 158.4 158.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.00 179.41 moveto[ 178.0 178.0 196.4 196.4 ][ 179.4 191.5 191.5 179.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 189.8 189.8 186.5 + 186.5 186.1 185.7 184.4 183.6 182.3 181.5 181.1 181.1 181.5 181.9 182.7 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 189.8 + 189.4 188.2 186.1 184.8 182.7 181.5 181.1 181.1 181.5 182.7 184.8 186.1 188.2 189.4 189.8 189.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.00 228.86 moveto[ 178.0 178.0 196.4 196.4 ][ 228.9 241.0 241.0 228.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 182.6 181.3 180.5 180.1 180.1 180.5 181.3 182.6 183.4 184.7 185.5 185.9 185.9 185.5 184.7 183.4 182.6 ][ 239.3 + 238.9 237.6 235.5 234.3 232.2 230.9 230.5 230.5 230.9 232.2 234.3 235.5 237.6 238.9 239.3 239.3 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 239.3 + 238.9 237.6 235.5 234.3 232.2 230.9 230.5 230.5 230.9 232.2 234.3 235.5 237.6 238.9 239.3 239.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 146.67 298.37 moveto[ 146.7 146.7 196.4 196.4 ][ 298.4 310.5 310.5 298.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.3 303.8 148.8 303.8 2 pls + s[ 161.3 160.0 159.6 159.6 160.0 160.9 162.5 163.8 164.6 165.1 165.1 164.6 164.2 163.0 161.3 160.0 159.6 159.2 + 159.2 159.6 160.5 161.7 163.4 164.2 164.6 164.6 164.2 163.0 161.3 ][ 308.8 308.4 307.6 306.7 305.9 305.5 305.1 + 304.6 303.8 303.0 301.7 300.9 300.5 300.0 300.0 300.5 300.9 301.7 303.0 303.8 304.6 305.1 305.5 305.9 306.7 + 307.6 308.4 308.8 308.8 ]plong + s[ 173.0 172.6 171.7 170.5 170.1 168.8 168.0 167.6 167.6 168.0 168.8 170.1 170.5 171.7 172.6 173.0 173.0 172.6 + 171.7 170.5 169.6 168.4 168.0 ][ 305.9 304.6 303.8 303.4 303.4 303.8 304.6 305.9 306.3 307.6 308.4 308.8 308.8 + 308.4 307.6 305.9 303.8 301.7 300.5 300.0 300.0 300.5 301.3 ]plong + 176.7 305.9 177.2 305.5 176.7 305.1 176.3 305.5 176.7 305.9 5 pls + 176.7 300.9 177.2 300.5 176.7 300.0 176.3 300.5 176.7 300.9 5 pls + s[ 182.6 181.3 180.5 180.1 180.1 180.5 181.3 182.6 183.4 184.7 185.5 185.9 185.9 185.5 184.7 183.4 182.6 ][ 308.8 + 308.4 307.1 305.1 303.8 301.7 300.5 300.0 300.0 300.5 301.7 303.8 305.1 307.1 308.4 308.8 308.8 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 308.8 + 308.4 307.1 305.1 303.8 301.7 300.5 300.0 300.0 300.5 301.7 303.8 305.1 307.1 308.4 308.8 308.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.00 347.82 moveto[ 178.0 178.0 196.4 196.4 ][ 347.8 359.9 359.9 347.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 358.3 358.3 354.9 + 354.9 354.5 354.1 352.8 352.0 350.7 349.9 349.5 349.5 349.9 350.3 351.2 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 358.3 + 357.8 356.6 354.5 353.3 351.2 349.9 349.5 349.5 349.9 351.2 353.3 354.5 356.6 357.8 358.3 358.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 146.67 379.31 moveto[ 146.7 146.7 196.4 196.4 ][ 379.3 391.4 391.4 379.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.3 384.7 148.8 384.7 2 pls + s[ 161.3 160.0 159.6 159.6 160.0 160.9 162.5 163.8 164.6 165.1 165.1 164.6 164.2 163.0 161.3 160.0 159.6 159.2 + 159.2 159.6 160.5 161.7 163.4 164.2 164.6 164.6 164.2 163.0 161.3 ][ 389.8 389.3 388.5 387.7 386.8 386.4 386.0 + 385.6 384.7 383.9 382.7 381.8 381.4 381.0 381.0 381.4 381.8 382.7 383.9 384.7 385.6 386.0 386.4 386.8 387.7 + 388.5 389.3 389.8 389.8 ]plong + s[ 169.6 168.4 168.0 168.0 168.4 169.2 170.9 172.2 173.0 173.4 173.4 173.0 172.6 171.3 169.6 168.4 168.0 167.6 + 167.6 168.0 168.8 170.1 171.7 172.6 173.0 173.0 172.6 171.3 169.6 ][ 389.8 389.3 388.5 387.7 386.8 386.4 386.0 + 385.6 384.7 383.9 382.7 381.8 381.4 381.0 381.0 381.4 381.8 382.7 383.9 384.7 385.6 386.0 386.4 386.8 387.7 + 388.5 389.3 389.8 389.8 ]plong + 176.7 386.8 177.2 386.4 176.7 386.0 176.3 386.4 176.7 386.8 5 pls + 176.7 381.8 177.2 381.4 176.7 381.0 176.3 381.4 176.7 381.8 5 pls + s[ 182.6 181.3 180.5 180.1 180.1 180.5 181.3 182.6 183.4 184.7 185.5 185.9 185.9 185.5 184.7 183.4 182.6 ][ 389.8 + 389.3 388.1 386.0 384.7 382.7 381.4 381.0 381.0 381.4 382.7 384.7 386.0 388.1 389.3 389.8 389.8 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 389.8 + 389.3 388.1 386.0 384.7 382.7 381.4 381.0 381.0 381.4 382.7 384.7 386.0 388.1 389.3 389.8 389.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 178.00 405.37 moveto[ 178.0 178.0 196.4 196.4 ][ 405.4 417.5 417.5 405.4 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 415.8 415.8 412.5 + 412.5 412.1 411.6 410.4 409.5 408.3 407.5 407.0 407.0 407.5 407.9 408.7 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 415.8 + 415.4 414.1 412.1 410.8 408.7 407.5 407.0 407.0 407.5 408.7 410.8 412.1 414.1 415.4 415.8 415.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 146.67 428.59 moveto[ 146.7 146.7 196.4 196.4 ][ 428.6 440.7 440.7 428.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.3 434.0 148.8 434.0 2 pls + s[ 161.3 160.0 159.6 159.6 160.0 160.9 162.5 163.8 164.6 165.1 165.1 164.6 164.2 163.0 161.3 160.0 159.6 159.2 + 159.2 159.6 160.5 161.7 163.4 164.2 164.6 164.6 164.2 163.0 161.3 ][ 439.0 438.6 437.8 436.9 436.1 435.7 435.3 + 434.9 434.0 433.2 431.9 431.1 430.7 430.3 430.3 430.7 431.1 431.9 433.2 434.0 434.9 435.3 435.7 436.1 436.9 + 437.8 438.6 439.0 439.0 ]plong + 169.2 430.3 173.4 439.0 2 pls + 173.4 439.0 167.6 439.0 2 pls + 176.7 436.1 177.2 435.7 176.7 435.3 176.3 435.7 176.7 436.1 5 pls + 176.7 431.1 177.2 430.7 176.7 430.3 176.3 430.7 176.7 431.1 5 pls + s[ 182.6 181.3 180.5 180.1 180.1 180.5 181.3 182.6 183.4 184.7 185.5 185.9 185.9 185.5 184.7 183.4 182.6 ][ 439.0 + 438.6 437.4 435.3 434.0 431.9 430.7 430.3 430.3 430.7 431.9 434.0 435.3 437.4 438.6 439.0 439.0 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 439.0 + 438.6 437.4 435.3 434.0 431.9 430.7 430.3 430.3 430.7 431.9 434.0 435.3 437.4 438.6 439.0 439.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 146.67 450.03 moveto[ 146.7 146.7 196.4 196.4 ][ 450.0 462.1 462.1 450.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 156.3 455.5 148.8 455.5 2 pls + s[ 161.3 160.0 159.6 159.6 160.0 160.9 162.5 163.8 164.6 165.1 165.1 164.6 164.2 163.0 161.3 160.0 159.6 159.2 + 159.2 159.6 160.5 161.7 163.4 164.2 164.6 164.6 164.2 163.0 161.3 ][ 460.5 460.1 459.2 458.4 457.5 457.1 456.7 + 456.3 455.5 454.6 453.4 452.5 452.1 451.7 451.7 452.1 452.5 453.4 454.6 455.5 456.3 456.7 457.1 457.5 458.4 + 459.2 460.1 460.5 460.5 ]plong + s[ 173.0 172.6 171.3 170.5 169.2 168.4 168.0 168.0 168.4 169.2 170.5 170.9 172.2 173.0 173.4 173.4 173.0 172.2 + 170.9 170.5 169.2 168.4 168.0 ][ 459.2 460.1 460.5 460.5 460.1 458.8 456.7 454.6 453.0 452.1 451.7 451.7 452.1 + 453.0 454.2 454.6 455.9 456.7 457.1 457.1 456.7 455.9 454.6 ]plong + 176.7 457.5 177.2 457.1 176.7 456.7 176.3 457.1 176.7 457.5 5 pls + 176.7 452.5 177.2 452.1 176.7 451.7 176.3 452.1 176.7 452.5 5 pls + s[ 180.9 185.5 183.0 184.3 185.1 185.5 185.9 185.9 185.5 184.7 183.4 182.2 180.9 180.5 180.1 ][ 460.5 460.5 457.1 + 457.1 456.7 456.3 455.0 454.2 453.0 452.1 451.7 451.7 452.1 452.5 453.4 ]plong + s[ 190.9 189.7 188.9 188.4 188.4 188.9 189.7 190.9 191.8 193.0 193.9 194.3 194.3 193.9 193.0 191.8 190.9 ][ 460.5 + 460.1 458.8 456.7 455.5 453.4 452.1 451.7 451.7 452.1 453.4 455.5 456.7 458.8 460.1 460.5 460.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 342.50 40.63 moveto[ 342.5 342.5 447.3 447.3 ][ 40.6 56.1 56.1 40.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 344.6 45.2 344.6 54.0 2 pls + s[ 344.6 348.4 349.6 350.0 350.4 350.4 350.0 349.6 348.4 344.6 ][ 54.0 54.0 53.6 53.2 52.3 51.5 50.7 50.2 + 49.8 49.8 ]plong + 350.4 45.2 347.5 49.8 2 pls + 352.9 54.0 353.4 54.4 353.8 54.0 353.4 53.6 352.9 54.0 5 pls + 353.4 45.2 353.4 51.1 2 pls + s[ 361.3 361.3 360.9 360.5 359.6 358.4 357.5 ][ 51.1 44.4 43.1 42.7 42.3 42.3 42.7 ]plong + s[ 361.3 360.5 359.6 358.4 357.5 356.7 356.3 356.3 356.7 357.5 358.4 359.6 360.5 361.3 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + 364.6 45.2 364.6 54.0 2 pls + s[ 364.6 365.9 366.7 368.0 368.8 369.2 369.2 ][ 49.4 50.7 51.1 51.1 50.7 49.4 45.2 ]plong + 375.1 45.2 374.2 45.2 373.4 45.6 373.0 46.9 373.0 54.0 5 pls + 374.7 51.1 371.7 51.1 2 pls + 388.9 45.2 388.9 51.1 2 pls + s[ 388.9 388.0 387.2 385.9 385.1 384.3 383.9 383.9 384.3 385.1 385.9 387.2 388.0 388.9 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + s[ 396.4 396.0 394.7 393.5 392.2 391.8 392.2 393.0 395.1 396.0 396.4 396.4 396.0 394.7 393.5 392.2 391.8 ][ 49.8 + 50.7 51.1 51.1 50.7 49.8 49.0 48.6 48.1 47.7 46.9 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + s[ 403.9 403.1 402.2 401.0 400.1 399.3 398.9 398.9 399.3 400.1 401.0 402.2 403.1 403.9 ][ 49.8 50.7 51.1 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + s[ 406.4 411.4 411.4 411.0 410.6 409.8 408.5 407.7 406.8 406.4 406.4 406.8 407.7 408.5 409.8 410.6 411.4 ][ 48.6 + 48.6 49.4 50.2 50.7 51.1 51.1 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + 414.3 45.2 414.3 51.1 2 pls + s[ 414.3 415.6 416.4 417.7 418.5 418.9 418.9 ][ 49.4 50.7 51.1 51.1 50.7 49.4 45.2 ]plong + s[ 426.5 426.0 424.8 423.5 422.3 421.9 422.3 423.1 425.2 426.0 426.5 426.5 426.0 424.8 423.5 422.3 421.9 ][ 49.8 + 50.7 51.1 51.1 50.7 49.8 49.0 48.6 48.1 47.7 46.9 46.5 45.6 45.2 45.2 45.6 46.5 ]plong + 429.0 54.0 429.4 54.4 429.8 54.0 429.4 53.6 429.0 54.0 5 pls + 429.4 45.2 429.4 51.1 2 pls + s[ 434.4 433.6 432.7 432.3 432.3 432.7 433.6 434.4 435.6 436.5 437.3 437.7 437.7 437.3 436.5 435.6 434.4 ][ 51.1 + 50.7 49.8 48.6 47.7 46.5 45.6 45.2 45.2 45.6 46.5 47.7 48.6 49.8 50.7 51.1 51.1 ]plong + 440.7 45.2 440.7 51.1 2 pls + s[ 440.7 441.9 442.7 444.0 444.8 445.3 445.3 ][ 49.4 50.7 51.1 51.1 50.7 49.4 45.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 141.09 235.77 moveto[ 141.1 128.6 128.6 141.1 ][ 235.8 235.8 307.2 307.2 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 139.4 237.9 130.7 237.9 2 pls + s[ 130.7 130.7 131.1 131.9 132.7 134.0 136.1 137.3 138.2 139.0 139.4 139.4 ][ 237.9 240.8 242.0 242.9 243.3 243.7 + 243.7 243.3 242.9 242.0 240.8 237.9 ]plong + s[ 136.1 136.1 135.2 134.4 134.0 133.6 133.6 134.0 134.8 136.1 136.9 138.2 139.0 139.4 139.4 139.0 138.2 ][ 246.2 + 251.2 251.2 250.8 250.4 249.5 248.3 247.5 246.6 246.2 246.2 246.6 247.5 248.3 249.5 250.4 251.2 ]plong + s[ 134.8 134.0 133.6 133.6 134.0 134.8 136.1 136.9 138.2 139.0 139.4 139.4 139.0 138.2 ][ 258.7 257.9 257.1 255.8 + 255.0 254.1 253.7 253.7 254.1 255.0 255.8 257.1 257.9 258.7 ]plong + 139.4 261.7 130.7 261.7 2 pls + 130.7 264.6 130.2 265.0 130.7 265.4 131.1 265.0 130.7 264.6 5 pls + 139.4 265.0 133.6 265.0 2 pls + 139.4 268.3 133.6 268.3 2 pls + s[ 135.2 134.0 133.6 133.6 134.0 135.2 139.4 ][ 268.3 269.6 270.4 271.7 272.5 272.9 272.9 ]plong + 139.4 280.9 133.6 280.9 2 pls + s[ 134.8 134.0 133.6 133.6 134.0 134.8 136.1 136.9 138.2 139.0 139.4 139.4 139.0 138.2 ][ 280.9 280.0 279.2 278.0 + 277.1 276.3 275.9 275.9 276.3 277.1 278.0 279.2 280.0 280.9 ]plong + 139.4 286.7 139.4 285.9 139.0 285.1 137.8 284.6 130.7 284.6 5 pls + 133.6 286.3 133.6 283.4 2 pls + 130.7 288.8 130.2 289.2 130.7 289.6 131.1 289.2 130.7 288.8 5 pls + 139.4 289.2 133.6 289.2 2 pls + s[ 133.6 134.0 134.8 136.1 136.9 138.2 139.0 139.4 139.4 139.0 138.2 136.9 136.1 134.8 134.0 133.6 133.6 ][ 294.2 + 293.4 292.6 292.2 292.2 292.6 293.4 294.2 295.5 296.3 297.2 297.6 297.6 297.2 296.3 295.5 294.2 ]plong + 139.4 300.5 133.6 300.5 2 pls + s[ 135.2 134.0 133.6 133.6 134.0 135.2 139.4 ][ 300.5 301.8 302.6 303.8 304.7 305.1 305.1 ]plong +restore save + 177.732 54.298 177.732 488.679 612.114 488.679 612.114 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 486.51 moveto[ 360.9 360.9 435.2 435.2 ][ 486.5 498.6 498.6 486.5 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 488.2 366.3 496.9 2 pls + 369.7 488.2 366.3 496.9 2 pls + 368.4 491.1 364.2 491.1 2 pls + 378.4 488.2 378.4 496.9 2 pls + 383.9 496.9 378.4 496.9 2 pls + 381.8 492.8 378.4 492.8 2 pls + 385.9 488.2 385.9 496.9 2 pls + 391.0 488.2 391.0 496.9 2 pls + 393.9 496.9 388.0 496.9 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 495.7 496.5 496.9 496.9 496.5 495.7 494.9 494.0 493.6 493.2 492.4 491.9 491.5 490.7 489.4 488.6 + 488.2 488.2 488.6 489.4 ]plong + 413.5 488.2 412.7 488.2 411.8 488.6 411.4 489.8 411.4 496.9 5 pls + 413.1 494.0 410.2 494.0 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 491.5 + 491.5 492.4 493.2 493.6 494.0 494.0 493.6 492.8 491.5 490.7 489.4 488.6 488.2 488.2 488.6 489.4 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 492.8 + 493.6 494.0 494.0 493.6 492.8 491.9 491.5 491.1 490.7 489.8 489.4 488.6 488.2 488.2 488.6 489.4 ]plong + 433.1 488.2 432.3 488.2 431.5 488.6 431.1 489.8 431.1 496.9 5 pls + 432.7 494.0 429.8 494.0 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/ast_tester/zpx.attr b/ast_tester/zpx.attr new file mode 100644 index 0000000..f073d5c --- /dev/null +++ b/ast_tester/zpx.attr @@ -0,0 +1 @@ +Grid=1,style(grid1)=2,size(textlab2)=2 diff --git a/ast_tester/zpx.head b/ast_tester/zpx.head new file mode 100644 index 0000000..a69353a --- /dev/null +++ b/ast_tester/zpx.head @@ -0,0 +1,153 @@ +SIMPLE = T / Fits standard +BITPIX = 16 / Bits per pixel +NAXIS = 2 / Number of axes +NAXIS1 = 2048 / Axis length +NAXIS2 = 4100 / Axis length +BSCALE = 1.145669E0 / REAL = TAPE*BSCALE + BZERO +BZERO = 3.793600E4 / +EXTEND = F / File may contain extensions +IRAF-TLM= '14:31:18 (18/05/1999)' / Time of last modification +OBJECT = 'VIRGO311' / +ORIGIN = 'KPNO-IRAF' / +DATE = '19/05/99' / +IRAFNAME= 'iraf3432b' / NAME OF IRAF IMAGE FILE +IRAF-MAX= 7.546810E4 / DATA MAX +IRAF-MIN= 4.038996E2 / DATA MIN +IRAF-BPX= 64 / DATA BITS/PIXEL +IRAFTYPE= 'DOUBLE ' / PIXEL TYPE +ORIGIN = 'NOAO-IRAF FITS Image Kernel Aug 1 1997' / FITS file originator +DATE = '18/05/99' / Date FITS file was generated +RUN = 158500 / Run number +SYSVER = 's7-1 ' / Version of observing system +OBSERVAT= 'LAPALMA ' / Name of observatory (IRAF style) +OBSTYPE = 'TARGET ' / Type of observation, eg. ARC +IMAGETYP= 'TARGET ' / Type of observation, eg. ARC +CTYPE1 = 'RA---ZPX' / Type of coordinate on axis 1 +CTYPE2 = 'DEC--ZPX' / Type of coordinate on axis 2 +CRPIX1 = -319.077517318151 / Reference pixel on axis 1 +CRPIX2 = 3035.64982635572 / Reference pixel on axis 2 +CRVAL1 = 187.8708 / Value at ref. pixel on axis 1 +CRVAL2 = 12.40001 / Value at ref. pixel on axis 2 +CD1_1 = -1.5845078730445E-6 / Transformation matrix +CD1_2 = -9.2521190078722E-5 / Transformation matrix +CD2_1 = -9.2538856101295E-5 / Transformation matrix +CD2_2 = 1.63254208024377E-6 / Transformation matrix +LATITUDE= 28.762000 / Telescope latitude (degrees), +28:45:43.2 +LONGITUD= 17.877639 / Telescope longitude (degrees), +17:52:39.5 +HEIGHT = 2348 / [m] Height above sea level. +SLATEL = 'LPO2.5 ' / Telescope name known to SLALIB +TELESCOP= 'INT ' / 2.5m Isaac Newton Telescope +TELSTAT = 'GUIDING ' / Telescope status: TRACKING or GUIDING normally. +RA = ' 12:31:28.998' / RA (187.8708261822271200 degrees) +DEC = '+12:24:00.04' / DEC ( 12.4000109941873690 degrees) +EQUINOX = '2000. ' / Equinox of coordinates +RADECSYS= 'FK5 ' / mean place new (after the 1976 IAU) system +XAPNOM = 0.0000000000 / nominal aperture in x (0.00 arcsec) +YAPNOM = 0.0000000000 / nominal aperture in y (0.00 arcsec) +XAPOFF = 0.0000000000 / total aperture offset in x (0.00 arcsec) +YAPOFF = 0.0000000000 / total aperture offset in y (0.00 arcsec) +MJD-OBS = 51277.9941545 / Modified Julian Date of midtime of observation +JD = 2451278.4941545 / Julian Date of midtime of observation +STSTART = ' 11:44:54.8' / Local sidereal time at start of observation +ST = ' 11:44:54.8' / Local sidereal time at start of observation +AZIMUTH = 148.155505 / Mean azimuth of observation (degrees) +ZD = 18.907242 / Mean zenith-distance of observation (degrees) +FSTATION= 'PRIME ' / Focal station of observation +PLATESCA= 6.856013 / [d/m] Platescale ( 24.68arcsec/mm) +TELFOCUS= 0.045998 / Telescope focus (metres) +ROTTRACK= T / Rotator always tracks sky on equatorial mount +ROTSKYPA= 179.899970 / Turntable position angle (degrees) +PARANGLE= 331.834373 / Parallactic angle at observation midpoint +VIGNETTE= F / Can we see out? +DOMEAZ = 147.089879 / Mean dome azimuth during observation +AIRMASS = 1.056698 / Effective mean airmass +TEMPTUBE= 9.005279 / Truss Temperature (degrees Celsius) +CAT-NAME= 'VIRGO311' / Target input-catalogue name +CAT-RA = ' 12:31:29.000' / Target Right Ascension +CAT-DEC = '+12:24:00.00' / Target Declination +CAT-EQUI= 'J2000.00' / Equinox of target coordinates +CAT-EPOC= 2000.00 / Target epoch of proper motions +PM-RA = 0.000000 / Target proper-motion RA (sec time/year) +PM-DEC = 0.000000 / Target proper-motion (sec arc/year) +PARALLAX= 0.000000 / Target Parallax (arcsec) +RADVEL = 0.000000 / Target radial velocity (km/s) +RATRACK = 0.000000 / Differential-tracking rate RA (arcsec/sec) +DECTRACK= 0.000000 / Differential-tracking rate Dec (arcsec/sec) +INSTRUME= 'WFC ' / INT wide-field camera is in use. +WFFPOS = 1 / Position-number of deployed filter +WFFBAND = 'B ' / Waveband of filter +WFFPSYS = 'Kitt Peak ' / Photometric system of filter +WFFID = '210 ' / Unique identifier of filter +SECPPIX = 0.333 / Arcseconds per pixel +UTSTART = '23:45:20.1 ' / UTC of start of observation +ELAPSED = 749.340 / Length of observation including pauses +DARKTIME= 749.340 / Length of observation including pauses +EXPOSED = 749.340 / Length of observation excluding pauses +EXPTIME = 749.340 / Length of observation excluding pauses +DATE-OBS= '1999-04-09 ' / UTC date start of observation +DETECTOR= 'MOS1 ' / Name of the detector +PREFLASH= 0.000 / Length of Preflash in seconds +BUNIT = 'ADU ' / Unit of the array of image data +GAIN = 2.900 / Electrons per ADU +READNOIS= 12.000 / Readout noise in electrons per pix +CCDSPEED= 'TURBO ' / Readout speed of the CCD +CCDNCHIP= 1 / Number of CCDs in this head +CCDCHIP = 1 / Head number of this CCD +CCDTYPE = 'EEV42 ' / Type of CCD used in this detector +CCDXSIZE= 2148 / X Size in pixels of digitised frame +CCDYSIZE= 4128 / Y Size in pixels of digitised frame +CCDXIMSI= 2048 / X Size of useful imaging area +CCDYIMSI= 4100 / Y Size of useful imaging area +CCDXIMST= 50 / X Start pixel of useful imaging area +CCDYIMST= 0 / Y Start pixel of useful imaging area +CCDWMODE= F / True is windows are enabled +CCDWXO1 = 0 / Offset of window 1 in X +CCDWYO1 = 0 / Offset of window 1 in Y +CCDWXS1 = 0 / Size of window 1 in X +CCDWYS1 = 0 / Size of window 1 in Y +CCDWXO2 = 0 / Offset of window 2 in X +CCDWYO2 = 0 / Offset of window 2 in Y +CCDWXS2 = 0 / Size of window 2 in X +CCDWYS2 = 0 / Size of window 2 in Y +CCDWXO3 = 0 / Offset of window 3 in X +CCDWYO3 = 0 / Offset of window 3 in Y +CCDWXS3 = 0 / Size of window 3 in X +CCDWYS3 = 0 / Size of window 3 in Y +CCDWXO4 = 0 / Offset of window 4 in X +CCDWYO4 = 0 / Offset of window 4 in Y +CCDWXS4 = 0 / Size of window 4 in X +CCDWYS4 = 0 / Size of window 4 in Y +CCDXPIXE= 1.350000e-05 / Size in meters of the pixels, in X +CCDYPIXE= 1.350000e-05 / Size in meters of the pixels, in Y +CCDXBIN = 1 / Binning factor, in X +CCDYBIN = 1 / Binning factor, in Y +CCDSTEMP= 153 / Required temperature of CCD, in Kevlin +CCDATEMP= 154 / Actual temperature of CCD, in Kevlin +CHIPNAME= 'A5506-4 ' / Name of CCD chip +DASCHAN = 1 / DAS channel +WINSEC1 = '[0:0,0:0] ' / Readout window 1 +WINSEC2 = '[0:0,0:0] ' / Readout window 2 +WINSEC3 = '[0:0,0:0] ' / Readout window 3 +WINSEC4 = '[0:0,0:0] ' / Readout window 4 +HISTORY This is the end of the header written by the ING observing-system. +WAT0_001= 'system=image' +WAT1_001= 'wtype=zpx axtype=ra projp1=6.0 projp2=100.0 projp3=2059.8' +WAT2_001= 'wtype=zpx axtype=dec projp1=1.0 projp3=-1000.8' +WFCWCS = 'Done ' +IMDTOI = 'Done ' +WCSDIM = 2 +LTM1_1 = 1. +LTM2_2 = 1. +TRIM = 'May 18 13:12 Trim data section is [51:2098,1:4100]' +BP-FLAG = 'May 18 13:12 Bad pixel file is /home/jrl/wfcred/stds/A5506-4.bad' +BT-FLAG = 'May 18 13:12 Overscan section is [1:50,1:4128] with mean=1501.636' +BI-FLAG = 'May 18 13:12 Zero level correction image is /data/cass03a/was/mframe' +FF-FLAG = 'May 18 13:12 Flat field image is /data/cass03c/was/mframes/B_9269342' +ILLUMCOR= 'May 18 13:12 Illumination image is tmpill.pl with scale=0.9314932' +CCDSEC = '[51:2098,1:4100]' +LTV1 = -50. +CCDPROC = 'May 18 13:12 CCD processing done' +XIRMS = 0.3418021 +ETARMS = 0.2019652 +WFCREDVR= 'v0.1 ' +END diff --git a/ast_tester/zpx.ps b/ast_tester/zpx.ps new file mode 100644 index 0000000..cfaecdc --- /dev/null +++ b/ast_tester/zpx.ps @@ -0,0 +1,949 @@ +%!PS-Adobe-1.0 +%%Creator: psmerge V1.0 +%%Creation date: Wed Feb 11 17:19:56 2004 +%%Pages: 1 +%%DocumentFonts: (atend) +%%EndComments +%%Page: ? 1 +clippath pathbbox pop pop translate/BEGINEPSFILE{ + /EPSFsave save def + 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [] 0 setdash + newpath + /showpage {} def + /defaultmatrix {currentmatrix} def +} bind def +/ENDEPSFILE{ + EPSFsave restore +} bind def +BEGINEPSFILE +[ 0.000001 1.000000 -1.000000 0.000001 -299.999602 300.000398 ] concat +%%BeginFile: zpx.ps +%!PS-Adobe-2.1 +%%Creator: RAL GKS 1.37 Linux - Workstation 2721 +%%Creation date: 11/02/2004 +%%EndComments +200 dict begin +/align{2 copy exch 5 -1 roll sub exch div ceiling mul sub}def +/ca{currentfile line readhexstring pop}def +/clnstat{ + setlinewidth plsty exch get + [ + exch + { + currentlinewidth 1 1 itransform pop 0 0 itransform pop sub + 10 mul div 1 add mul + } + forall + ] + 0 setdash + setrgbcolor + }def +/fagen{ + dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/fapdo{ + closepath pathbbox + /ury exch def/urx exch def + /lly exch refy psizey align def + /llx exch refx psizex align def + /nxpix urx llx sub psizex div nx mul round cvi 1 add def + /nypix ury lly sub psizey div ny mul round cvi 1 add def + /nxbyte nxpix 3 mul def + /scan + ny + {nxbyte string} + repeat + ny array astore + def + 0 1 ny 1 sub + { + dup patdef exch get/ww nxbyte nxb add 2 mul string def/indn nxb def + ww 0 3 -1 roll putinterval ww + { + 0 indn getinterval ww exch indn dup nxbyte nxb add + lt + {exch putinterval /indn indn dup add def ww} + { + pop 0 nxbyte nxb add getinterval exch pop 0 nxbyte + getinterval exit + } + ifelse + } + loop + scan 3 1 roll put + } + for + gsave + eoclip + newpath + llx lly translate + nxpix psizex mul nx div nypix psizey mul ny div scale + faput + grestore + }def +/fapi{ + /psizey exch def /psizex exch def + /refy exch def/refx exch def/ny exch def/nx exch def + /nxb nx 3 mul def + /line nxb string def /patdef ny array def + 0 1 ny 1 sub + {patdef exch ca nxb string copy put} + for + }def +/faput{ + /temp ny 1 sub nypix ny mod sub def + nxpix nypix 8 [nxpix 0 0 nypix neg 0 nypix] + { + /temp + temp 1 add dup ny 1 sub le + {} + {pop 0} + ifelse + def + scan temp get + } + im + }def +/fasoldo{eoclip eofill}def +/fastat{/fsi exch def}def +/hitdot{transform round exch round exch itransform}def +/im{false 3 colorimage}def +/init{ + /eflag 1 def/epsf exch def/margin exch def + 1 sub/delta exch def/pflag 0 def/orient exch def + /port [1 0 0 1] def/land [0 1 -1 0] def + epsf eflag eq + {/llx 0 def/lly 0 def} + {initmatrix + clippath pathbbox + pop pop/lly exch def/llx exch def} + ifelse + [ + orient pflag eq + {/off 0 def port} + {/off delta def land} + ifelse + aload pop off llx margin add add lly + ] + concat + 1 setlinejoin 1 setlinecap + newpath + }def +/pln{ + 2 copy 0 get exch 0 get exch moveto dup length 1 sub + 1 1 3 -1 roll + {plseg} + for + pop pop + }def +/plong{pln stroke restore}def +/pls{1 sub 3 1 roll moveto {lineto} repeat stroke}def +/plseg{3 copy get 3 1 roll exch get exch lineto}def +/plsty[ [] [] [12 5.5] [0.5 2] [10 2 0.5 2] [ 8 2 0.5 2 0.5 2] ]def +/pm{dup length 1 sub 0 1 3 -1 roll {pmdomk} for pop pop}def +/pmdomk{ + 3 copy get 3 1 roll exch get exch + gsave + translate [] 0 setdash + /tempwd currentlinewidth msz div def + msz dup scale + tempwd setlinewidth + newpath + pmx mt get exec stroke + grestore + }def +/pmstat{/msz exch def/mt exch def}def +/pmx[{} + {0 0 moveto 0 0 lineto} + {-.5 0 moveto .5 0 lineto 0 -.5 moveto 0 .5 lineto} + {pmx 2 get exec pmx 5 get exec} + {.5 0 moveto 0 0 .5 0 360 arc} + {-.45 -.45 moveto .45 .45 lineto -.45 .45 moveto .45 -.45 lineto} + ]def +/s{save}def +/setclipbox{newpath moveto lineto lineto lineto closepath clip newpath}def +/txchar{ + dup length 1 sub + 0 1 3 -1 roll + {txdoch} + for + pop pop pop + }def +/txdoch{ + dup 4 1 roll 3 copy get 3 1 roll exch get exch + gsave + translate + tng rotate + texp 1 scale + 3 -1 roll 3 index exch 1 getinterval 0 0 moveto + show + grestore + }def +/txstat{ + /texp exch def /tng exch def + gsave + /ft 3 -1 roll findfont def ft 1 scalefont setfont + 0 0 moveto ( ) stringwidth pop + grestore + div ft exch scalefont setfont + /ft 0 def + }def +/txstr{ + gsave + translate + tng rotate + texp 1 scale + 0 0 moveto + show + grestore + }def +/tz{ + 2 copy exch + 8 -1 roll exch moveto + 6 -1 roll exch lineto + 3 -1 roll exch lineto lineto + }def +1 543 8.000 0 init +%%EndProlog +%%PageBoundingBox: (atend) +save +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 490.2 465.8 492.6 465.9 2 pls + 485.4 465.5 487.8 465.6 2 pls + 480.6 465.2 483.0 465.4 2 pls + 475.8 465.0 478.2 465.1 2 pls + 471.0 464.7 473.4 464.8 2 pls + 466.2 464.4 468.6 464.5 2 pls + 461.4 464.1 463.8 464.3 2 pls + 456.6 463.8 459.0 464.0 2 pls + 451.8 463.6 454.2 463.7 2 pls + 447.0 463.3 449.4 463.4 2 pls + 442.2 463.0 444.6 463.1 2 pls + 437.4 462.7 439.8 462.9 2 pls + 432.6 462.4 435.0 462.6 2 pls + 427.9 462.1 430.2 462.3 2 pls + 423.1 461.9 425.5 462.0 2 pls + 418.3 461.6 420.7 461.7 2 pls + 413.5 461.3 415.9 461.4 2 pls + 408.7 461.0 411.1 461.1 2 pls + 403.9 460.7 405.4 460.8 406.3 460.9 3 pls + 399.1 460.4 401.5 460.6 2 pls + 394.3 460.2 396.7 460.3 2 pls + 389.5 459.9 391.9 460.0 2 pls + 384.7 459.6 387.1 459.7 2 pls + 379.9 459.3 382.3 459.5 2 pls + 375.1 459.1 377.5 459.2 2 pls + 370.3 458.8 372.7 458.9 2 pls + 365.5 458.5 367.9 458.6 2 pls + 360.7 458.2 363.1 458.4 363.1 458.4 3 pls + 356.0 458.0 358.3 458.1 2 pls + 351.2 457.7 353.6 457.9 2 pls + 346.4 457.5 348.8 457.6 2 pls + 341.6 457.3 343.1 457.3 344.0 457.4 3 pls + 336.8 457.0 339.2 457.1 2 pls + 332.0 456.8 334.4 456.9 2 pls + 327.2 456.6 329.6 456.7 2 pls + 322.4 456.4 323.7 456.5 324.8 456.5 3 pls + 317.6 456.3 320.0 456.3 2 pls + 312.8 456.1 315.2 456.2 2 pls + 308.0 455.9 310.4 456.0 2 pls + 303.2 455.8 304.9 455.8 305.6 455.9 3 pls + 298.4 455.7 300.8 455.8 2 pls + 488.9 410.3 491.3 410.3 2 pls + 484.1 410.2 486.5 410.2 2 pls + 479.3 410.1 481.7 410.1 2 pls + 474.5 410.0 476.9 410.0 2 pls + 469.7 409.9 470.1 409.9 472.1 409.9 3 pls + 464.9 409.7 467.3 409.8 2 pls + 460.1 409.6 462.5 409.7 2 pls + 455.3 409.5 457.7 409.6 2 pls + 450.5 409.4 452.9 409.5 2 pls + 445.7 409.3 446.3 409.3 448.1 409.4 3 pls + 440.9 409.2 443.3 409.3 2 pls + 436.1 409.1 438.5 409.1 2 pls + 431.3 409.0 433.7 409.0 2 pls + 426.5 408.9 428.9 408.9 2 pls + 421.7 408.7 423.3 408.8 424.1 408.8 3 pls + 416.9 408.6 419.3 408.7 2 pls + 412.1 408.5 414.5 408.6 2 pls + 407.3 408.4 409.7 408.4 2 pls + 402.5 408.3 404.9 408.3 2 pls + 397.7 408.1 400.1 408.2 2 pls + 392.9 408.0 395.3 408.1 2 pls + 388.1 407.9 390.5 408.0 2 pls + 383.3 407.8 385.7 407.8 2 pls + 378.5 407.6 379.6 407.7 380.9 407.7 3 pls + 373.7 407.5 376.1 407.6 2 pls + 368.9 407.4 371.3 407.5 2 pls + 364.1 407.3 366.5 407.3 2 pls + 359.3 407.1 361.7 407.2 2 pls + 354.5 407.0 356.9 407.1 2 pls + 349.7 406.9 352.1 406.9 2 pls + 344.9 406.7 347.3 406.8 2 pls + 340.1 406.6 342.5 406.7 2 pls + 335.3 406.5 337.7 406.5 2 pls + 330.5 406.3 332.9 406.4 2 pls + 325.7 406.2 328.1 406.3 2 pls + 320.9 406.1 323.3 406.2 2 pls + 316.1 406.0 318.5 406.0 2 pls + 311.3 405.9 313.7 405.9 2 pls + 306.5 405.8 308.9 405.8 2 pls + 301.7 405.7 302.1 405.7 304.1 405.7 3 pls + 297.3 405.6 299.3 405.6 2 pls + 492.1 356.1 492.6 356.1 2 pls + 487.3 356.2 489.7 356.2 2 pls + 482.5 356.3 484.9 356.3 2 pls + 477.7 356.4 480.1 356.4 2 pls + 472.9 356.5 475.3 356.5 2 pls + 468.1 356.6 470.5 356.6 2 pls + 463.3 356.7 465.7 356.7 2 pls + 458.5 356.8 460.9 356.8 2 pls + 453.7 356.9 456.1 356.9 2 pls + 448.9 357.1 451.3 357.0 2 pls + 444.1 357.2 444.3 357.2 446.5 357.1 3 pls + 439.3 357.3 441.7 357.2 2 pls + 434.5 357.4 436.9 357.3 2 pls + 429.7 357.5 432.1 357.4 2 pls + 424.9 357.6 427.3 357.5 2 pls + 420.1 357.7 421.2 357.7 422.5 357.6 3 pls + 415.3 357.8 417.7 357.7 2 pls + 410.5 357.9 412.9 357.8 2 pls + 405.7 358.0 408.1 357.9 2 pls + 400.9 358.1 403.3 358.1 2 pls + 396.1 358.2 398.5 358.2 2 pls + 391.3 358.3 393.7 358.3 2 pls + 386.5 358.4 388.9 358.4 2 pls + 381.7 358.5 384.1 358.5 2 pls + 376.9 358.6 377.7 358.6 379.3 358.6 3 pls + 372.1 358.7 374.5 358.7 2 pls + 367.3 358.9 369.7 358.8 2 pls + 362.5 359.0 364.9 358.9 2 pls + 357.7 359.1 360.1 359.0 2 pls + 352.9 359.2 355.3 359.1 2 pls + 348.1 359.3 350.5 359.2 2 pls + 343.3 359.4 345.7 359.3 2 pls + 338.5 359.5 340.9 359.5 2 pls + 333.7 359.6 336.1 359.6 2 pls + 328.9 359.7 331.3 359.7 2 pls + 324.1 359.8 326.5 359.8 2 pls + 319.3 359.9 321.7 359.9 2 pls + 314.5 360.1 316.9 360.0 2 pls + 309.7 360.2 312.1 360.1 2 pls + 304.9 360.3 307.3 360.2 2 pls + 300.1 360.4 300.4 360.4 302.5 360.3 3 pls + 297.3 360.5 297.7 360.5 2 pls + 490.6 301.8 492.6 301.6 2 pls + 485.8 302.1 488.2 301.9 2 pls + 481.0 302.4 483.4 302.2 2 pls + 476.2 302.7 478.6 302.5 2 pls + 471.4 303.0 473.8 302.8 2 pls + 466.7 303.3 468.8 303.1 469.0 303.1 3 pls + 461.9 303.6 464.3 303.4 2 pls + 457.1 303.9 459.5 303.7 2 pls + 452.3 304.2 454.7 304.1 2 pls + 447.5 304.5 449.9 304.4 2 pls + 442.7 304.8 445.0 304.7 445.1 304.7 3 pls + 437.9 305.2 440.3 305.0 2 pls + 433.1 305.5 435.5 305.3 2 pls + 428.3 305.8 430.7 305.6 2 pls + 423.5 306.1 425.9 305.9 2 pls + 418.7 306.4 421.1 306.3 2 pls + 413.9 306.7 416.3 306.6 2 pls + 409.2 307.1 411.6 306.9 2 pls + 404.4 307.4 406.8 307.2 2 pls + 399.6 307.7 399.8 307.7 402.0 307.6 3 pls + 394.8 308.0 397.2 307.9 2 pls + 390.0 308.4 392.4 308.2 2 pls + 385.2 308.7 387.6 308.5 2 pls + 380.4 309.0 382.8 308.9 2 pls + 375.6 309.4 378.0 309.2 2 pls + 370.8 309.7 373.2 309.5 2 pls + 366.0 310.0 368.4 309.9 2 pls + 361.3 310.4 363.7 310.2 2 pls + 356.5 310.7 357.8 310.6 358.9 310.5 3 pls + 351.7 311.0 354.1 310.8 2 pls + 346.9 311.3 349.3 311.2 2 pls + 342.1 311.7 344.5 311.5 2 pls + 337.3 312.0 338.1 311.9 339.7 311.8 3 pls + 332.5 312.3 334.9 312.2 2 pls + 327.7 312.6 330.1 312.5 2 pls + 322.9 313.0 325.3 312.8 2 pls + 318.1 313.3 319.1 313.2 320.5 313.1 3 pls + 313.3 313.6 315.7 313.4 2 pls + 308.6 313.9 311.0 313.7 2 pls + 303.8 314.1 306.2 314.0 2 pls + 299.0 314.4 300.8 314.3 301.4 314.3 3 pls + 489.5 245.7 491.9 245.4 2 pls + 484.7 246.1 487.1 245.9 2 pls + 479.9 246.6 482.3 246.4 2 pls + 475.1 247.1 477.5 246.8 2 pls + 470.3 247.5 471.9 247.4 472.7 247.3 3 pls + 465.6 248.0 468.0 247.8 2 pls + 460.8 248.5 463.2 248.2 2 pls + 456.0 249.0 458.4 248.7 2 pls + 451.2 249.4 453.6 249.2 2 pls + 446.5 249.9 448.1 249.7 448.8 249.7 3 pls + 441.7 250.4 444.1 250.1 2 pls + 436.9 250.8 439.3 250.6 2 pls + 432.1 251.3 434.5 251.1 2 pls + 427.3 251.8 429.7 251.6 2 pls + 422.6 252.3 425.0 252.0 2 pls + 417.8 252.7 420.2 252.5 2 pls + 413.0 253.2 415.4 253.0 2 pls + 408.2 253.7 410.6 253.4 2 pls + 403.5 254.2 405.8 253.9 2 pls + 398.7 254.6 401.1 254.4 2 pls + 393.9 255.1 396.3 254.9 2 pls + 389.1 255.6 391.5 255.3 2 pls + 384.3 256.0 386.7 255.8 2 pls + 379.6 256.5 381.2 256.3 382.0 256.3 3 pls + 374.8 256.9 377.2 256.7 2 pls + 370.0 257.4 372.4 257.2 2 pls + 365.2 257.8 367.6 257.6 2 pls + 360.4 258.3 362.8 258.1 2 pls + 355.7 258.7 358.0 258.5 2 pls + 350.9 259.2 353.3 258.9 2 pls + 346.1 259.6 348.5 259.4 2 pls + 341.3 260.0 343.7 259.8 2 pls + 336.5 260.4 338.9 260.2 2 pls + 331.7 260.8 334.1 260.6 2 pls + 327.0 261.2 329.3 261.0 2 pls + 322.2 261.6 324.6 261.4 2 pls + 317.4 261.9 319.8 261.7 2 pls + 312.6 262.2 315.0 262.1 2 pls + 307.8 262.5 310.2 262.4 2 pls + 303.0 262.9 305.4 262.7 2 pls + 298.2 263.1 300.6 263.0 2 pls + 488.7 186.5 491.1 186.2 2 pls + 484.0 187.1 486.3 186.8 2 pls + 479.2 187.6 481.6 187.3 2 pls + 474.4 188.2 476.8 187.9 476.8 187.9 3 pls + 469.7 188.8 472.0 188.5 2 pls + 464.9 189.4 467.3 189.1 2 pls + 460.1 189.9 462.5 189.7 2 pls + 455.4 190.5 457.7 190.2 2 pls + 450.6 191.1 452.8 190.8 453.0 190.8 3 pls + 445.8 191.7 448.2 191.4 2 pls + 441.1 192.2 443.4 192.0 2 pls + 436.3 192.8 438.7 192.5 2 pls + 431.5 193.4 433.9 193.1 2 pls + 426.7 193.9 429.1 193.7 2 pls + 422.0 194.5 424.4 194.2 2 pls + 417.2 195.0 419.6 194.8 2 pls + 412.4 195.6 414.8 195.3 2 pls + 407.7 196.2 410.1 195.9 2 pls + 402.9 196.7 405.3 196.4 2 pls + 398.1 197.2 400.5 197.0 2 pls + 393.4 197.8 395.7 197.5 2 pls + 388.6 198.3 391.0 198.0 2 pls + 383.8 198.8 385.0 198.7 386.2 198.6 3 pls + 379.0 199.3 381.4 199.1 2 pls + 374.3 199.8 376.6 199.6 2 pls + 369.5 200.3 371.9 200.1 2 pls + 364.7 200.8 367.1 200.6 2 pls + 359.9 201.3 362.3 201.1 2 pls + 355.2 201.8 357.5 201.5 2 pls + 350.4 202.2 352.8 202.0 2 pls + 345.6 202.7 348.0 202.4 2 pls + 340.8 203.1 343.0 202.9 343.2 202.9 3 pls + 336.0 203.5 338.4 203.3 2 pls + 331.2 203.9 333.6 203.7 2 pls + 326.5 204.3 328.8 204.1 2 pls + 321.7 204.7 322.8 204.6 324.1 204.5 3 pls + 316.9 205.0 319.3 204.8 2 pls + 312.1 205.3 314.5 205.1 2 pls + 307.3 205.6 309.7 205.5 2 pls + 302.5 205.9 302.9 205.9 304.9 205.8 3 pls + 297.7 206.2 300.1 206.1 2 pls + 488.2 123.2 490.6 122.9 2 pls + 483.5 123.9 485.9 123.6 2 pls + 478.7 124.5 481.1 124.2 2 pls + 474.0 125.2 476.3 124.9 2 pls + 469.2 125.8 471.6 125.5 2 pls + 464.4 126.4 466.8 126.1 2 pls + 459.7 127.1 462.1 126.8 2 pls + 454.9 127.7 457.3 127.4 2 pls + 450.2 128.3 452.5 128.0 2 pls + 445.4 128.9 447.8 128.6 2 pls + 440.6 129.6 443.0 129.3 2 pls + 435.9 130.2 438.3 129.9 2 pls + 431.1 130.8 433.5 130.5 2 pls + 426.3 131.4 428.7 131.1 2 pls + 421.6 132.0 424.0 131.7 2 pls + 416.8 132.6 419.2 132.3 2 pls + 412.0 133.2 414.4 132.9 2 pls + 407.3 133.7 409.7 133.4 2 pls + 402.5 134.3 404.9 134.0 2 pls + 397.7 134.8 400.1 134.6 2 pls + 393.0 135.4 395.4 135.1 2 pls + 388.2 135.9 389.2 135.8 390.6 135.7 3 pls + 383.4 136.5 385.8 136.2 2 pls + 378.7 137.0 381.0 136.7 2 pls + 373.9 137.5 376.3 137.2 2 pls + 369.1 138.0 371.5 137.7 2 pls + 364.3 138.5 366.7 138.3 2 pls + 359.6 138.9 361.9 138.7 2 pls + 354.8 139.4 357.2 139.2 2 pls + 350.0 139.9 352.4 139.6 2 pls + 345.2 140.3 345.8 140.3 347.6 140.1 3 pls + 340.4 140.7 342.8 140.5 2 pls + 335.6 141.1 338.0 140.9 2 pls + 330.9 141.5 333.2 141.3 2 pls + 326.1 141.9 328.5 141.7 2 pls + 321.3 142.2 323.7 142.0 2 pls + 316.5 142.5 318.9 142.4 2 pls + 311.7 142.8 314.1 142.7 2 pls + 306.9 143.1 309.3 143.0 2 pls + 302.1 143.4 303.9 143.3 304.5 143.3 3 pls + 297.3 143.6 299.7 143.5 2 pls + s[ 492.6 489.2 487.2 486.9 488.3 491.0 492.6 ][ 454.8 414.2 371.6 329.2 286.1 241.4 222.9 ]plong + s[ 467.6 466.5 462.9 461.0 460.7 462.0 464.8 468.5 473.0 478.0 479.9 ][ 467.0 456.5 413.5 371.8 330.4 288.1 244.0 + 197.6 148.4 96.2 76.0 ]plong + s[ 442.4 441.1 437.6 435.6 435.3 436.7 439.4 443.0 447.2 451.8 453.9 ][ 467.0 455.1 412.9 372.1 331.5 290.0 246.6 + 200.6 151.7 99.7 76.0 ]plong + s[ 418.0 416.6 413.1 411.2 410.9 412.3 414.9 418.3 422.2 426.3 428.5 ][ 467.0 453.7 412.2 372.3 332.6 291.9 249.1 + 203.5 154.8 103.0 76.0 ]plong + s[ 394.5 393.0 389.6 387.7 387.5 388.8 391.3 394.4 397.9 401.5 403.6 ][ 467.0 452.4 411.5 372.4 333.7 293.7 251.4 + 206.2 157.7 106.1 76.0 ]plong + s[ 371.8 370.3 367.1 365.2 365.0 366.3 368.5 371.3 374.3 377.3 379.2 ][ 467.0 451.2 410.9 372.6 334.8 295.5 253.6 + 208.6 160.4 108.8 76.0 ]plong + s[ 350.0 348.5 345.5 343.6 343.4 344.6 346.6 348.9 351.2 353.6 355.2 ][ 467.0 450.1 410.2 372.8 335.8 297.2 255.7 + 210.8 162.7 111.1 76.0 ]plong + s[ 328.8 327.5 324.8 323.0 322.8 323.8 325.4 327.1 328.8 330.4 331.5 ][ 467.0 449.1 409.6 372.9 336.8 298.7 257.4 + 212.7 164.6 113.1 76.0 ]plong + s[ 308.4 307.3 305.0 303.3 303.1 303.9 304.8 305.8 306.7 307.5 308.1 ][ 467.0 448.4 409.1 373.0 337.8 300.1 258.9 + 214.2 166.1 114.7 76.0 ]plong + 298.6 466.4 297.3 466.4 2 pls + 298.6 445.2 297.3 445.2 2 pls + 298.6 435.0 297.3 435.0 2 pls + 298.6 425.0 297.3 424.9 2 pls + 298.6 415.2 297.3 415.1 2 pls + 298.6 396.2 297.3 396.2 2 pls + 298.6 387.1 297.3 387.1 2 pls + 298.6 378.1 297.3 378.1 2 pls + 298.6 369.3 297.3 369.3 2 pls + 298.6 351.6 297.3 351.6 2 pls + 298.6 342.6 297.3 342.6 2 pls + 298.6 333.4 297.3 333.5 2 pls + 298.6 324.0 297.3 324.1 2 pls + 298.6 304.6 297.3 304.7 2 pls + 298.6 294.6 297.3 294.7 2 pls + 298.6 284.3 297.3 284.4 2 pls + 298.6 273.8 297.3 273.9 2 pls + 298.6 252.2 297.3 252.3 2 pls + 298.6 241.0 297.3 241.1 2 pls + 298.6 229.6 297.3 229.7 2 pls + 298.6 218.0 297.3 218.1 2 pls + 298.6 194.1 297.3 194.2 2 pls + 298.6 181.8 297.3 181.9 2 pls + 298.6 169.3 297.3 169.4 2 pls + 298.6 156.6 297.3 156.6 2 pls + 298.6 130.5 297.3 130.5 2 pls + 298.6 117.1 297.3 117.1 2 pls + 298.6 103.5 297.3 103.5 2 pls + 298.6 89.7 297.3 89.7 2 pls + 486.4 77.4 486.5 76.0 2 pls + 473.2 77.4 473.4 76.0 2 pls + 466.7 77.4 466.8 76.0 2 pls + 460.2 77.4 460.4 76.0 2 pls + 447.4 77.4 447.5 76.0 2 pls + 441.0 77.4 441.1 76.0 2 pls + 434.7 77.4 434.8 76.0 2 pls + 422.1 77.4 422.2 76.0 2 pls + 415.9 77.4 416.0 76.0 2 pls + 409.7 77.4 409.8 76.0 2 pls + 397.4 77.4 397.5 76.0 2 pls + 391.3 77.4 391.3 76.0 2 pls + 385.2 77.4 385.2 76.0 2 pls + 373.1 77.4 373.1 76.0 2 pls + 367.1 77.4 367.1 76.0 2 pls + 361.1 77.4 361.1 76.0 2 pls + 349.2 77.4 349.2 76.0 2 pls + 343.3 77.4 343.3 76.0 2 pls + 337.3 77.4 337.4 76.0 2 pls + 325.6 77.4 325.6 76.0 2 pls + 319.7 77.4 319.8 76.0 2 pls + 313.9 77.4 313.9 76.0 2 pls + 302.2 77.4 302.2 76.0 2 pls + 491.2 454.6 492.6 454.6 2 pls + 491.2 443.4 492.6 443.4 2 pls + 491.2 432.3 492.6 432.3 2 pls + 491.2 421.3 492.6 421.3 2 pls + 491.2 399.4 492.6 399.4 2 pls + 491.2 388.6 492.6 388.6 2 pls + 491.2 377.7 492.6 377.7 2 pls + 491.2 366.9 492.6 366.9 2 pls + 491.2 345.3 492.6 345.3 2 pls + 491.2 334.5 492.6 334.4 2 pls + 491.2 323.6 492.6 323.6 2 pls + 491.2 312.7 492.6 312.6 2 pls + 491.2 290.7 492.6 290.6 2 pls + 491.2 279.5 492.6 279.4 2 pls + 491.2 268.3 492.6 268.2 2 pls + 491.2 257.0 492.6 256.8 2 pls + 491.2 233.9 492.6 233.8 2 pls + 491.2 222.2 492.6 222.1 2 pls + 491.2 210.3 492.6 210.2 2 pls + 491.2 198.3 492.6 198.2 2 pls + 491.2 173.8 492.6 173.7 2 pls + 491.2 161.4 492.6 161.2 2 pls + 491.2 148.7 492.6 148.5 2 pls + 491.2 135.9 492.6 135.7 2 pls + 491.2 109.6 492.6 109.4 2 pls + 491.2 96.2 492.6 96.1 2 pls + 491.2 82.7 492.6 82.5 2 pls + 487.0 465.6 487.1 467.0 2 pls + 480.4 465.6 480.5 467.0 2 pls + 473.9 465.6 474.0 467.0 2 pls + 461.0 465.6 461.2 467.0 2 pls + 454.7 465.6 454.8 467.0 2 pls + 448.4 465.6 448.5 467.0 2 pls + 436.0 465.6 436.1 467.0 2 pls + 429.9 465.6 430.0 467.0 2 pls + 423.8 465.6 423.9 467.0 2 pls + 411.8 465.6 412.0 467.0 2 pls + 405.9 465.6 406.1 467.0 2 pls + 400.1 465.6 400.2 467.0 2 pls + 388.5 465.6 388.7 467.0 2 pls + 382.9 465.6 383.0 467.0 2 pls + 377.2 465.6 377.4 467.0 2 pls + 366.1 465.6 366.2 467.0 2 pls + 360.6 465.6 360.8 467.0 2 pls + 355.2 465.6 355.3 467.0 2 pls + 344.5 465.6 344.6 467.0 2 pls + 339.2 465.6 339.3 467.0 2 pls + 333.9 465.6 334.1 467.0 2 pls + 323.6 465.6 323.7 467.0 2 pls + 318.4 465.6 318.5 467.0 2 pls + 313.3 465.6 313.4 467.0 2 pls + 303.3 465.6 303.3 467.0 2 pls + 298.2 465.6 298.3 467.0 2 pls + s[ 297.3 311.2 325.2 339.1 353.1 367.0 381.0 394.9 408.9 422.8 436.8 450.7 464.7 478.6 492.6 ][ 76.0 76.0 76.0 + 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 76.0 ]plong + s[ 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 492.6 ][ 76.0 103.9 131.9 + 159.8 187.7 215.6 243.6 271.5 299.4 327.3 355.3 383.2 411.1 439.0 467.0 ]plong + s[ 492.6 478.6 464.7 450.7 436.8 422.8 408.9 394.9 381.0 367.0 353.1 339.1 325.2 311.2 297.3 ][ 467.0 467.0 467.0 + 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 467.0 ]plong + s[ 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 297.3 ][ 467.0 439.0 411.1 + 383.2 355.3 327.3 299.4 271.5 243.6 215.6 187.7 159.8 131.9 103.9 76.0 ]plong + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 279.04 136.76 moveto[ 279.0 279.0 296.2 296.2 ][ 136.8 148.9 148.9 136.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 283.2 138.4 283.2 147.2 282.0 145.9 281.1 145.5 4 pls + s[ 290.7 289.5 288.6 288.2 288.2 288.6 289.5 290.7 291.6 292.8 293.7 294.1 294.1 293.7 292.8 291.6 290.7 ][ 147.2 + 146.8 145.5 143.4 142.2 140.1 138.8 138.4 138.4 138.8 140.1 142.2 143.4 145.5 146.8 147.2 147.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 237.27 199.30 moveto[ 237.3 237.3 296.2 296.2 ][ 199.3 211.4 211.4 199.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 241.4 201.0 241.4 209.7 240.2 208.5 239.4 208.1 4 pls + s[ 246.9 246.9 247.3 247.7 248.6 250.2 251.1 251.5 251.9 251.9 251.5 250.6 246.5 252.3 ][ 207.7 208.1 208.9 209.3 + 209.7 209.7 209.3 208.9 208.1 207.2 206.4 205.1 201.0 201.0 ]plong + 255.7 206.8 256.1 206.4 255.7 206.0 255.2 206.4 255.7 206.8 5 pls + 255.7 201.8 256.1 201.4 255.7 201.0 255.2 201.4 255.7 201.8 5 pls + s[ 259.8 264.4 261.9 263.2 264.0 264.4 264.8 264.8 264.4 263.6 262.3 261.1 259.8 259.4 259.0 ][ 209.7 209.7 206.4 + 206.4 206.0 205.6 204.3 203.5 202.2 201.4 201.0 201.0 201.4 201.8 202.6 ]plong + s[ 267.8 267.8 268.2 268.6 269.4 271.1 271.9 272.4 272.8 272.8 272.4 271.5 267.3 273.2 ][ 207.7 208.1 208.9 209.3 + 209.7 209.7 209.3 208.9 208.1 207.2 206.4 205.1 201.0 201.0 ]plong + 276.5 206.8 277.0 206.4 276.5 206.0 276.1 206.4 276.5 206.8 5 pls + 276.5 201.8 277.0 201.4 276.5 201.0 276.1 201.4 276.5 201.8 5 pls + s[ 282.4 281.1 280.3 279.9 279.9 280.3 281.1 282.4 283.2 284.5 285.3 285.7 285.7 285.3 284.5 283.2 282.4 ][ 209.7 + 209.3 208.1 206.0 204.7 202.6 201.4 201.0 201.0 201.4 202.6 204.7 206.0 208.1 209.3 209.7 209.7 ]plong + s[ 290.7 289.5 288.6 288.2 288.2 288.6 289.5 290.7 291.6 292.8 293.7 294.1 294.1 293.7 292.8 291.6 290.7 ][ 209.7 + 209.3 208.1 206.0 204.7 202.6 201.4 201.0 201.0 201.4 202.6 204.7 206.0 208.1 209.3 209.7 209.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 277.79 256.28 moveto[ 277.8 277.8 296.2 296.2 ][ 256.3 268.4 268.4 256.3 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 284.9 280.7 280.3 280.7 282.0 283.2 284.5 285.3 285.7 285.7 285.3 284.5 283.2 282.0 280.7 280.3 279.9 ][ 266.7 + 266.7 263.0 263.4 263.8 263.8 263.4 262.5 261.3 260.5 259.2 258.4 258.0 258.0 258.4 258.8 259.6 ]plong + s[ 290.7 289.5 288.6 288.2 288.2 288.6 289.5 290.7 291.6 292.8 293.7 294.1 294.1 293.7 292.8 291.6 290.7 ][ 266.7 + 266.3 265.1 263.0 261.7 259.6 258.4 258.0 258.0 258.4 259.6 261.7 263.0 265.1 266.3 266.7 266.7 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 277.79 307.60 moveto[ 277.8 277.8 296.2 296.2 ][ 307.6 319.7 319.7 307.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 286.1 312.2 279.9 312.2 284.1 318.0 3 pls + 284.1 309.3 284.1 318.0 2 pls + s[ 290.7 289.5 288.6 288.2 288.2 288.6 289.5 290.7 291.6 292.8 293.7 294.1 294.1 293.7 292.8 291.6 290.7 ][ 318.0 + 317.6 316.4 314.3 313.0 310.9 309.7 309.3 309.3 309.7 310.9 313.0 314.3 316.4 317.6 318.0 318.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 277.79 353.57 moveto[ 277.8 277.8 296.2 296.2 ][ 353.6 365.7 365.7 353.6 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 280.7 285.3 282.8 284.1 284.9 285.3 285.7 285.7 285.3 284.5 283.2 282.0 280.7 280.3 279.9 ][ 364.0 364.0 360.7 + 360.7 360.3 359.8 358.6 357.8 356.5 355.7 355.2 355.2 355.7 356.1 356.9 ]plong + s[ 290.7 289.5 288.6 288.2 288.2 288.6 289.5 290.7 291.6 292.8 293.7 294.1 294.1 293.7 292.8 291.6 290.7 ][ 364.0 + 363.6 362.3 360.3 359.0 356.9 355.7 355.2 355.2 355.7 356.9 359.0 360.3 362.3 363.6 364.0 364.0 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 277.79 398.71 moveto[ 277.8 277.8 296.2 296.2 ][ 398.7 410.8 410.8 398.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 280.3 280.3 280.7 281.1 282.0 283.6 284.5 284.9 285.3 285.3 284.9 284.1 279.9 285.7 ][ 407.1 407.5 408.3 408.7 + 409.2 409.2 408.7 408.3 407.5 406.6 405.8 404.6 400.4 400.4 ]plong + s[ 290.7 289.5 288.6 288.2 288.2 288.6 289.5 290.7 291.6 292.8 293.7 294.1 294.1 293.7 292.8 291.6 290.7 ][ 409.2 + 408.7 407.5 405.4 404.1 402.1 400.8 400.4 400.4 400.8 402.1 404.1 405.4 407.5 408.7 409.2 409.2 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 256.90 448.83 moveto[ 256.9 256.9 296.2 296.2 ][ 448.8 460.9 460.9 448.8 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 259.8 264.4 261.9 263.2 264.0 264.4 264.8 264.8 264.4 263.6 262.3 261.1 259.8 259.4 259.0 ][ 459.3 459.3 455.9 + 455.9 455.5 455.1 453.8 453.0 451.8 450.9 450.5 450.5 450.9 451.3 452.2 ]plong + 270.7 450.5 270.7 459.3 269.4 458.0 268.6 457.6 4 pls + 276.5 456.3 277.0 455.9 276.5 455.5 276.1 455.9 276.5 456.3 5 pls + 276.5 451.3 277.0 450.9 276.5 450.5 276.1 450.9 276.5 451.3 5 pls + 283.2 450.5 283.2 459.3 282.0 458.0 281.1 457.6 4 pls + s[ 290.7 289.5 288.6 288.2 288.2 288.6 289.5 290.7 291.6 292.8 293.7 294.1 294.1 293.7 292.8 291.6 290.7 ][ 459.3 + 458.9 457.6 455.5 454.3 452.2 450.9 450.5 450.5 450.9 452.2 454.3 455.5 457.6 458.9 459.3 459.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 298.86 61.95 moveto[ 298.9 298.9 317.2 317.2 ][ 62.0 74.1 74.1 62.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + s[ 301.4 301.4 301.8 302.2 303.0 304.7 305.5 306.0 306.4 306.4 306.0 305.1 301.0 306.8 ][ 70.3 70.7 71.6 72.0 + 72.4 72.4 72.0 71.6 70.7 69.9 69.1 67.8 63.6 63.6 ]plong + s[ 309.7 309.7 310.1 310.6 311.4 313.1 313.9 314.3 314.7 314.7 314.3 313.5 309.3 315.2 ][ 70.3 70.7 71.6 72.0 + 72.4 72.4 72.0 71.6 70.7 69.9 69.1 67.8 63.6 63.6 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 336.78 61.95 moveto[ 336.8 336.8 374.8 374.8 ][ 62.0 74.1 74.1 62.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 341.0 63.6 341.0 72.4 339.7 71.1 338.9 70.7 4 pls + s[ 346.4 346.4 346.8 347.2 348.1 349.7 350.6 351.0 351.4 351.4 351.0 350.1 346.0 351.8 ][ 70.3 70.7 71.6 72.0 + 72.4 72.4 72.0 71.6 70.7 69.9 69.1 67.8 63.6 63.6 ]plong + 355.2 69.5 355.6 69.1 355.2 68.6 354.7 69.1 355.2 69.5 5 pls + 355.2 64.5 355.6 64.0 355.2 63.6 354.7 64.0 355.2 64.5 5 pls + s[ 358.9 358.9 359.3 359.7 360.6 362.3 363.1 363.5 363.9 363.9 363.5 362.7 358.5 364.3 ][ 70.3 70.7 71.6 72.0 + 72.4 72.4 72.0 71.6 70.7 69.9 69.1 67.8 63.6 63.6 ]plong + s[ 369.4 368.1 367.3 366.8 366.8 367.3 368.1 369.4 370.2 371.4 372.3 372.7 372.7 372.3 371.4 370.2 369.4 ][ 72.4 + 72.0 70.7 68.6 67.4 65.3 64.0 63.6 63.6 64.0 65.3 67.4 68.6 70.7 72.0 72.4 72.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 395.64 61.95 moveto[ 395.6 395.6 412.8 412.8 ][ 62.0 74.1 74.1 62.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 399.8 63.6 399.8 72.4 398.6 71.1 397.7 70.7 4 pls + s[ 406.9 405.7 405.2 405.2 405.7 406.5 408.2 409.4 410.3 410.7 410.7 410.3 409.8 408.6 406.9 405.7 405.2 404.8 + 404.8 405.2 406.1 407.3 409.0 409.8 410.3 410.3 409.8 408.6 406.9 ][ 72.4 72.0 71.1 70.3 69.5 69.1 68.6 + 68.2 67.4 66.5 65.3 64.5 64.0 63.6 63.6 64.0 64.5 65.3 66.5 67.4 68.2 68.6 69.1 69.5 70.3 + 71.1 72.0 72.4 72.4 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 445.96 61.95 moveto[ 446.0 446.0 463.1 463.1 ][ 62.0 74.1 74.1 62.0 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 450.1 63.6 450.1 72.4 448.9 71.1 448.1 70.7 4 pls + s[ 460.6 460.2 458.9 458.1 456.8 456.0 455.6 455.6 456.0 456.8 458.1 458.5 459.7 460.6 461.0 461.0 460.6 459.7 + 458.5 458.1 456.8 456.0 455.6 ][ 71.1 72.0 72.4 72.4 72.0 70.7 68.6 66.5 64.9 64.0 63.6 63.6 64.0 + 64.9 66.1 66.5 67.8 68.6 69.1 69.1 68.6 67.8 66.5 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 232.81 219.05 moveto[ 232.8 217.4 217.4 232.8 ][ 219.1 219.1 323.9 323.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 228.2 221.1 219.4 221.1 2 pls + s[ 219.4 219.4 219.9 220.3 221.1 222.0 222.8 223.2 223.6 223.6 ][ 221.1 224.9 226.2 226.6 227.0 227.0 226.6 226.2 + 224.9 221.1 ]plong + 228.2 227.0 223.6 224.1 2 pls + 219.4 229.5 219.0 229.9 219.4 230.3 219.9 229.9 219.4 229.5 5 pls + 228.2 229.9 222.4 229.9 2 pls + s[ 222.4 229.1 230.3 230.7 231.1 231.1 230.7 ][ 237.8 237.8 237.4 237.0 236.2 234.9 234.1 ]plong + s[ 223.6 222.8 222.4 222.4 222.8 223.6 224.9 225.7 227.0 227.8 228.2 228.2 227.8 227.0 ][ 237.8 237.0 236.2 234.9 + 234.1 233.3 232.8 232.8 233.3 234.1 234.9 236.2 237.0 237.8 ]plong + 228.2 241.2 219.4 241.2 2 pls + s[ 224.0 222.8 222.4 222.4 222.8 224.0 228.2 ][ 241.2 242.4 243.3 244.5 245.4 245.8 245.8 ]plong + 228.2 251.6 228.2 250.8 227.8 250.0 226.5 249.5 219.4 249.5 5 pls + 222.4 251.2 222.4 248.3 2 pls + 228.2 265.4 222.4 265.4 2 pls + s[ 223.6 222.8 222.4 222.4 222.8 223.6 224.9 225.7 227.0 227.8 228.2 228.2 227.8 227.0 ][ 265.4 264.6 263.7 262.5 + 261.7 260.8 260.4 260.4 260.8 261.7 262.5 263.7 264.6 265.4 ]plong + s[ 223.6 222.8 222.4 222.4 222.8 223.6 224.5 224.9 225.3 225.7 226.5 227.0 227.8 228.2 228.2 227.8 227.0 ][ 272.9 + 272.5 271.3 270.0 268.8 268.3 268.8 269.6 271.7 272.5 272.9 272.9 272.5 271.3 270.0 268.8 268.3 ]plong + s[ 223.6 222.8 222.4 222.4 222.8 223.6 224.9 225.7 227.0 227.8 228.2 228.2 227.8 227.0 ][ 280.5 279.6 278.8 277.5 + 276.7 275.9 275.4 275.4 275.9 276.7 277.5 278.8 279.6 280.5 ]plong + s[ 224.9 224.9 224.0 223.2 222.8 222.4 222.4 222.8 223.6 224.9 225.7 227.0 227.8 228.2 228.2 227.8 227.0 ][ 283.0 + 288.0 288.0 287.6 287.1 286.3 285.0 284.2 283.4 283.0 283.0 283.4 284.2 285.0 286.3 287.1 288.0 ]plong + 228.2 290.9 222.4 290.9 2 pls + s[ 224.0 222.8 222.4 222.4 222.8 224.0 228.2 ][ 290.9 292.1 293.0 294.2 295.1 295.5 295.5 ]plong + s[ 223.6 222.8 222.4 222.4 222.8 223.6 224.5 224.9 225.3 225.7 226.5 227.0 227.8 228.2 228.2 227.8 227.0 ][ 303.0 + 302.6 301.3 300.1 298.8 298.4 298.8 299.7 301.8 302.6 303.0 303.0 302.6 301.3 300.1 298.8 298.4 ]plong + 219.4 305.5 219.0 305.9 219.4 306.3 219.9 305.9 219.4 305.5 5 pls + 228.2 305.9 222.4 305.9 2 pls + s[ 222.4 222.8 223.6 224.9 225.7 227.0 227.8 228.2 228.2 227.8 227.0 225.7 224.9 223.6 222.8 222.4 222.4 ][ 310.9 + 310.1 309.3 308.9 308.9 309.3 310.1 310.9 312.2 313.0 313.9 314.3 314.3 313.9 313.0 312.2 310.9 ]plong + 228.2 317.2 222.4 317.2 2 pls + s[ 224.0 222.8 222.4 222.4 222.8 224.0 228.2 ][ 317.2 318.5 319.3 320.5 321.4 321.8 321.8 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 323.50 34.94 moveto[ 323.5 323.5 466.3 466.3 ][ 34.9 60.0 60.0 34.9 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 327.7 38.3 327.7 55.8 2 pls + s[ 327.7 333.5 336.0 337.7 338.5 339.4 339.4 338.5 337.7 336.0 333.5 327.7 ][ 55.8 55.8 55.0 53.3 51.6 49.1 + 45.0 42.5 40.8 39.1 38.3 38.3 ]plong + s[ 344.4 354.4 354.4 353.6 352.7 351.1 348.6 346.9 345.2 344.4 344.4 345.2 346.9 348.6 351.1 352.7 354.4 ][ 45.0 + 45.0 46.6 48.3 49.1 50.0 50.0 49.1 47.5 45.0 43.3 40.8 39.1 38.3 38.3 39.1 40.8 ]plong + s[ 369.4 367.8 366.1 363.6 361.9 360.3 359.4 359.4 360.3 361.9 363.6 366.1 367.8 369.4 ][ 47.5 49.1 50.0 50.0 + 49.1 47.5 45.0 43.3 40.8 39.1 38.3 38.3 39.1 40.8 ]plong + 375.3 38.3 375.3 55.8 2 pls + 381.1 55.8 382.0 56.7 382.8 55.8 382.0 55.0 381.1 55.8 5 pls + 382.0 38.3 382.0 50.0 2 pls + 388.7 38.3 388.7 50.0 2 pls + s[ 388.7 391.2 392.8 395.3 397.0 397.8 397.8 ][ 46.6 49.1 50.0 50.0 49.1 46.6 38.3 ]plong + 413.7 38.3 413.7 50.0 2 pls + s[ 413.7 412.0 410.4 407.9 406.2 404.5 403.7 403.7 404.5 406.2 407.9 410.4 412.0 413.7 ][ 47.5 49.1 50.0 50.0 + 49.1 47.5 45.0 43.3 40.8 39.1 38.3 38.3 39.1 40.8 ]plong + 425.4 38.3 423.7 38.3 422.1 39.1 421.2 41.6 421.2 55.8 5 pls + 424.6 50.0 418.7 50.0 2 pls + 429.6 55.8 430.4 56.7 431.3 55.8 430.4 55.0 429.6 55.8 5 pls + 430.4 38.3 430.4 50.0 2 pls + s[ 440.4 438.8 437.1 436.3 436.3 437.1 438.8 440.4 443.0 444.6 446.3 447.1 447.1 446.3 444.6 443.0 440.4 ][ 50.0 + 49.1 47.5 45.0 43.3 40.8 39.1 38.3 38.3 39.1 40.8 43.3 45.0 47.5 49.1 50.0 50.0 ]plong + 453.0 38.3 453.0 50.0 2 pls + s[ 453.0 455.5 457.2 459.7 461.3 462.2 462.2 ][ 46.6 49.1 50.0 50.0 49.1 46.6 38.3 ]plong +restore save + 177.732 54.298 177.732 488.679 612.113 488.679 612.113 54.298 setclipbox + 1.00000 1.00000 1.00000 1 0.24 clnstat + 1 fastat +save +gsave + 360.88 476.72 moveto[ 360.9 360.9 435.2 435.2 ][ 476.7 488.8 488.8 476.7 ]fagen fasoldo +grestore +restore +restore save + 0.000 0.000 0.000 542.976 789.846 542.976 789.846 0.000 setclipbox + 0.00000 0.00000 0.00000 1 0.36 clnstat + 363.0 478.4 366.3 487.2 2 pls + 369.7 478.4 366.3 487.2 2 pls + 368.4 481.3 364.2 481.3 2 pls + 378.4 478.4 378.4 487.2 2 pls + 383.9 487.2 378.4 487.2 2 pls + 381.8 483.0 378.4 483.0 2 pls + 385.9 478.4 385.9 487.2 2 pls + 391.0 478.4 391.0 487.2 2 pls + 393.9 487.2 388.0 487.2 2 pls + s[ 401.4 400.6 399.3 397.6 396.4 395.5 395.5 396.0 396.4 397.2 399.7 400.6 401.0 401.4 401.4 400.6 399.3 397.6 + 396.4 395.5 ][ 485.9 486.7 487.2 487.2 486.7 485.9 485.1 484.2 483.8 483.4 482.6 482.2 481.7 480.9 479.6 478.8 + 478.4 478.4 478.8 479.6 ]plong + 413.5 478.4 412.7 478.4 411.8 478.8 411.4 480.1 411.4 487.2 5 pls + 413.1 484.2 410.2 484.2 2 pls + s[ 415.6 420.6 420.6 420.2 419.8 418.9 417.7 416.9 416.0 415.6 415.6 416.0 416.9 417.7 418.9 419.8 420.6 ][ 481.7 + 481.7 482.6 483.4 483.8 484.2 484.2 483.8 483.0 481.7 480.9 479.6 478.8 478.4 478.4 478.8 479.6 ]plong + s[ 427.7 427.3 426.0 424.8 423.5 423.1 423.5 424.4 426.5 427.3 427.7 427.7 427.3 426.0 424.8 423.5 423.1 ][ 483.0 + 483.8 484.2 484.2 483.8 483.0 482.2 481.7 481.3 480.9 480.1 479.6 478.8 478.4 478.4 478.8 479.6 ]plong + 433.1 478.4 432.3 478.4 431.5 478.8 431.1 480.1 431.1 487.2 5 pls + 432.7 484.2 429.8 484.2 2 pls +showpage restore +%%PageTrailer +%%PageBoundingBox: 8 8 551 798 +end +%%Trailer +%%EndFile +ENDEPSFILE +showpage +%%Trailer diff --git a/astbad.c b/astbad.c new file mode 100644 index 0000000..8fec052 --- /dev/null +++ b/astbad.c @@ -0,0 +1,181 @@ +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ +#include "pointset.h" /* declaration of AST__BAD etc */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include + +/* Local Constants: */ +#define BUFF_LEN ( 2 * DBL_DIG + 20 ) /* Buffer length */ +#define IEEE_DIG 17 /* Minimum number of digits required by + IEEE for conversion from binary to + string and back again to be an + identity. */ + +/* Prototypes for local functions */ +static void printdval( double ); +static void printfval( float ); + +/* Main function. */ +/* ============== */ +int main( int argc, char *argv[] ) { +/* +*+ +* Name: +* astbad + +* Purpose: +* Generate a string representing an AST floating point constant. + +* Invocation: +* astbad + +* Type: +* C program. + +* Description: +* This program writes a string to standard output containing +* a formatted decimal representation of a specified C floating point +* constant defined by AST. This is intended for use in defining these +* constants for use from languages other than C. +* +* The value written should contain sufficient decimal digits so +* that a routine that uses it to generate a value in another +* language will produce exactly the same value as a C program +* using the same macro. + +* Arguments: +* value = LITERAL +* The name of the constant to be printed: AST__BAD, AST__NAN or +* AST__NANF. If not supplied, AST__BAD is printed. + +* Copyright: +* Copyright (C) 2009-2011 Science & Technology Facilities Council. +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) +* TIMJ: Tim Jenness (JAC, Hawaii) + +* History: +* 18-NOV-1997 (RFWS); +* Original version. +* 24-OCT-2000 (DSB): +* Ensure that the number of digits used is at least the minimum +* required by IEEE for a conversion from binary to string and back +* to binary to be an identity. +* 31-MAR-2009 (TIMJ): +* Does not take any arguments so don't try to read arguments. +* 18-JAN-2011 (DSB): +* Extend to print other floating point constants as well as +* AST__BAD. +*- +*/ + +/* Local Variables; */ + const char *name; /* Pointer to name of constant to be printed */ + +/* Get the name of the constant to be printed. */ + if( argc > 1 ) { + name = argv[1]; + } else { + name = "AST__BAD"; + } + +/* Print it. */ + if( !strcmp( name, "AST__BAD" ) ) { + printdval( AST__BAD ); + + } else if( !strcmp( name, "AST__NAN" ) ) { + printdval( AST__NAN ); + + } else if( !strcmp( name, "AST__NANF" ) ) { + printfval( AST__NANF ); + +/* Issue an error message if the argument is unknown. */ + } else { + (void) fprintf( stderr, "astbad: Unknown constant requested: %s\n", + name ); + } + +/* Exit. */ + return 0; +} + + +/* Print a double precision value to standard output */ +static void printdval( double val ){ + +/* Local Variables: */ + char buff[ BUFF_LEN + 1 ]; /* Buffer for formatted string */ + double newval; /* Value read back from string */ + int digits; /* Number of digits of precision */ + +/* Vary the precision over a reasonable range to see how many decimal + digits are required. The initial number of digits is the larger of + DBL_DIG and IEEE_DIG. */ + for ( digits = ( DBL_DIG > IEEE_DIG )?DBL_DIG:IEEE_DIG; + digits <= ( 2 * DBL_DIG ); digits++ ) { + +/* Format the value using this precision and then read it back. */ + (void) sprintf( buff, "%.*G", digits, val ); + (void) sscanf( buff, "%lg", &newval ); + +/* Quit looping when the original value is read back. */ + if ( newval == val ) break; + } + +/* Write the value to standard output, with one extra digit for good + measure. */ + (void) printf( "%.*G\n", digits + 1, val ); +} + +/* Print a single precision value to standard output */ +static void printfval( float val ){ + +/* Local Variables: */ + char buff[ BUFF_LEN + 1 ]; /* Buffer for formatted string */ + float newval; /* Value read back from string */ + int digits; /* Number of digits of precision */ + +/* Vary the precision over a reasonable range to see how many decimal + digits are required. The initial number of digits is FLT_DIG. */ + for ( digits = FLT_DIG; digits <= ( 2 * FLT_DIG ); digits++ ) { + +/* Format the value using this precision and then read it back. */ + (void) sprintf( buff, "%.*G", digits, val ); + (void) sscanf( buff, "%g", &newval ); + +/* Quit looping when the original value is read back. */ + if ( newval == val ) break; + } + +/* Write the value to standard output, with one extra digit for good + measure. */ + (void) printf( "%.*G\n", digits + 1, val ); + +} + diff --git a/axis.c b/axis.c new file mode 100644 index 0000000..9aa9fc6 --- /dev/null +++ b/axis.c @@ -0,0 +1,3444 @@ +/* +*class++ +* Name: +* Axis + +* Purpose: +* Store axis information. + +* Constructor Function: +* None. + +* Description: +* The Axis class is used to store information associated with a +* particular axis of a Frame. It is used internally by the AST +* library and has no constructor function. You should encounter it +c only within textual output (e.g. from astWrite). +f only within textual output (e.g. from AST_WRITE). + +* Inheritance: +* The Axis class inherits from the Object class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: B.S. Berry (Starlink) + +* History: +* 1-MAR-1996 (RFWS): +* Original version. +* 10-SEP-1996 (RFWS): +* Added I/O facilities. +* 11-SEP-1996 (RFWS): +* Added astAxisGap (written by DSB). +* 25-FEB-1998 (RFWS): +* Added astAxisUnformat. +* 29-AUG-2001 (DSB): +* Added AxisDistance and AxisOffset. +* 20-OCT-2002 (DSB): +* Added Top and Bottom attributes. +* 8-JAN-2003 (DSB): +* - Changed private InitVtab method to protected astInitAxisVtab +* method. +* - Include descriptive label for units string within a Dump. +* 24-JAN-2004 (DSB): +* - Added astAxisFields. +* - Added argument "fmt" to definition of AxisAbbrev. +* 3-FEB-2004 (DSB): +* - Added "log" formatting using the "&" flag character in the +* Format string. +* 15-SEP-2004 (DSB): +* - If a format string is set which includes a wildcard precision +* value (".*"), then use the Digits value to determine the precision +* to be used. +* - If the conversion code is of integer type (e.g. "%d") cast value +* to integer before printing. +* 2-FEB-2005 (DSB): +* - Avoid using astStore to allocate more storage than is supplied +* in the "data" pointer. This can cause access violations since +* astStore will then read beyond the end of the "data" area. +* 15-MAR-2005 (DSB): +* - Avoid exponents in log format labels which are close to zero but +* not quite zero. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 30-JUN-2006 (DSB): +* Guard against a null "str1" value in AxisAbbrev. +* 17-APR-2015 (DSB): +* Added astAxisCentre. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Axis + + +/* Header files. */ +/* ============= */ +#include "ast_err.h" /* Error code definitions */ + +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Object interface (parent class) */ +#include "pointset.h" /* Sets of coordinates (for AST__BAD) */ +#include "channel.h" /* I/O channels */ +#include "axis.h" /* Interface definition for this class */ +#include "unit.h" /* Definitions of physical units */ +#include "globals.h" /* Thread-safe global data access */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* Plain text equivalents. */ +static const char *log_txt = "10^"; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetDefaultFormat_Buff[ 0 ] = 0; \ + globals->AxisFormat_Buff[ 0 ] = 0; \ + globals->GetAxisNormUnit_Buff[ 0 ] = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Axis) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Axis,Class_Init) +#define class_vtab astGLOBAL(Axis,Class_Vtab) +#define getdefaultformat_buff astGLOBAL(Axis,GetDefaultFormat_Buff) +#define axisformat_buff astGLOBAL(Axis,AxisFormat_Buff) +#define getaxisnormunit_buff astGLOBAL(Axis,GetAxisNormUnit_Buff) +#define getattrib_buff astGLOBAL(Axis,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getdefaultformat_buff[ AST__AXIS_GETDEFAULTFORMAT_BUFF_LEN + 1 ]; +static char axisformat_buff[ AST__AXIS_GETDEFAULTFORMAT_BUFF_LEN + 1 ]; +static char getaxisnormunit_buff[ AST__AXIS_GETAXISNORMUNIT_BUFF_LEN + 1 ]; +static char getattrib_buff[ AST__AXIS_GETATTRIB_BUFF_LEN + 1 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstAxisVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstAxis *astAxisId_( const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static const char *AxisAbbrev( AstAxis *, const char *, const char *, const char *, int * ); +static const char *AxisFormat( AstAxis *, double, int * ); +static int GetObjSize( AstObject *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetAxisFormat( AstAxis *, int * ); +static const char *GetAxisLabel( AstAxis *, int * ); +static const char *GetAxisSymbol( AstAxis *, int * ); +static const char *GetAxisUnit( AstAxis *, int * ); +static const char *GetAxisInternalUnit( AstAxis *, int * ); +static const char *GetAxisNormUnit( AstAxis *, int * ); +static const char *GetDefaultFormat( AstAxis *, int * ); +static char *ParseAxisFormat( const char *, int, int *, int *, int *, int *, int * ); +static double AxisDistance( AstAxis *, double, double, int * ); +static double AxisCentre( AstAxis *, double, double, int * ); +static double AxisGap( AstAxis *, double, int *, int * ); +static double AxisOffset( AstAxis *, double, double, int * ); +static int AxisFields( AstAxis *, const char *, const char *, int, char **, int *, double *, int * ); +static int AxisIn( AstAxis *, double, double, double, int, int * ); +static int AxisUnformat( AstAxis *, const char *, double *, int * ); +static int GetAxisDigits( AstAxis *, int * ); +static int GetAxisDirection( AstAxis *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestAxisDigits( AstAxis *, int * ); +static int TestAxisDirection( AstAxis *, int * ); +static int TestAxisFormat( AstAxis *, int * ); +static int TestAxisLabel( AstAxis *, int * ); +static int TestAxisSymbol( AstAxis *, int * ); +static int TestAxisUnit( AstAxis *, int * ); +static int TestAxisInternalUnit( AstAxis *, int * ); +static int TestAxisNormUnit( AstAxis *, int * ); +static void AxisNorm( AstAxis *, double *, int * ); +static void AxisOverlay( AstAxis *, AstAxis *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearAxisDigits( AstAxis *, int * ); +static void ClearAxisDirection( AstAxis *, int * ); +static void ClearAxisFormat( AstAxis *, int * ); +static void ClearAxisLabel( AstAxis *, int * ); +static void ClearAxisSymbol( AstAxis *, int * ); +static void ClearAxisUnit( AstAxis *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetAxisDigits( AstAxis *, int, int * ); +static void SetAxisDirection( AstAxis *, int, int * ); +static void SetAxisFormat( AstAxis *, const char *, int * ); +static void SetAxisLabel( AstAxis *, const char *, int * ); +static void SetAxisSymbol( AstAxis *, const char *, int * ); +static void SetAxisUnit( AstAxis *, const char *, int * ); + +static double GetAxisTop( AstAxis *, int * ); +static int TestAxisTop( AstAxis *, int * ); +static void ClearAxisTop( AstAxis *, int * ); +static void SetAxisTop( AstAxis *, double, int * ); + +static double GetAxisBottom( AstAxis *, int * ); +static int TestAxisBottom( AstAxis *, int * ); +static void ClearAxisBottom( AstAxis *, int * ); +static void SetAxisBottom( AstAxis *, double, int * ); + + +/* Member functions. */ +/* ================= */ +static const char *AxisAbbrev( AstAxis *this, const char *fmt, + const char *str1, const char *str2, int *status ) { +/* +*+ +* Name: +* astAxisAbbrev + +* Purpose: +* Abbreviate a formatted Axis value by skipping leading fields. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* const char *astAxisAbbrev( AstAxis *this, const char *fmt, +* const char *str1, const char *str2 ) + +* Class Membership: +* Axis method. + +* Description: +* This function compares two Axis values that have been formatted +* (using astAxisFormat) and determines if they have any redundant +* leading fields (i.e. leading fields in common which can be +* suppressed when tabulating the values or plotting them on the +* axis of a graph). + +* Parameters: +* this +* Pointer to the Axis. +* fmt +* Pointer to a constant null-terminated string containing the +* format specifier used to format the two values. +* str1 +* Pointer to a constant null-terminated string containing the +* first formatted value. If this is null, the returned pointer +* points to the start of the final field in str2. +* str2 +* Pointer to a constant null-terminated string containing the +* second formatted value. + +* Returned Value: +* A pointer into the "str2" string which locates the first +* character in the first field that differs between the two +* formatted values. +* +* If the two values have no leading fields in common, the returned +* value will point at the start of string "str2". If the two +* values are equal, it will point at the terminating null at the +* end of this string. + +* Notes: +* - This function assumes that the format specification used was +* the same when both values were formatted. +* - A pointer to the start of "str2" will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *result; /* Result pointer to return */ + +/* Initialise. */ + result = str2; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* In the Axis class, there is only one field in a formatted value. + We return the value of "str2", unless the two strings are + identical, in which case we return a pointer to the final null in + "str2". */ + if( str1 && !strcmp( str1, str2 ) ) result += strlen( str2 ); + +/* Return the result. */ + return result; +} + +static double AxisDistance( AstAxis *this, double v1, double v2, int *status ) { +/* +*+ +* Name: +* astAxisDistance + +* Purpose: +* Find the distance between two axis values. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* AxisDistance( AstAxis *this, double v1, double v2 ) + +* Class Membership: +* Axis method. + +* Description: +* This function returns a signed value representing the axis increment +* from axis value v1 to axis value v2. +* +* For a simple Axis, this is a trivial operation. But for other +* derived classes of Axis (such as a SkyAxis) this is not the case. + +* Parameters: +* this +* Pointer to the Axis. +* v1 +* The first axis value +* v2 +* The second axis value + +* Returned Value: +* The axis increment from v1 to v2. + +* Notes: +* - A value of AST__BAD is returned if either axis value is AST__BAD. +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + double result; /* Returned gap size */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check both axis values are OK, and form the returned increment. */ + if( v1 != AST__BAD && v2 != AST__BAD ) result = v2 - v1; + +/* Return the result. */ + return result; +} + +static int AxisFields( AstAxis *this, const char *fmt0, const char *str, + int maxfld, char **fields, int *nc, double *val, int *status ) { +/* +*+ +* Name: +* astAxisFields + +* Purpose: +* Identify numerical fields within a formatted Axis value. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* int astAxisFields( AstAxis *this, const char *fmt0, const char *str, +* int maxfld, char **fields, int *nc, double *val ) + +* Class Membership: +* Axis member function. + +* Description: +* This function identifies the numerical fields within an Axis value +* that have been formatted using astAxisFormat. It assumes that the +* value was formatted using the supplied format string. It also +* returns the equivalent floating point value. + +* Parameters: +* this +* Pointer to the Axis. +* fmt0 +* Pointer to a constant null-terminated string containing the +* format used when creating "str". +* str +* Pointer to a constant null-terminated string containing the +* formatted value. +* maxfld +* The maximum number of fields to identify within "str". +* fields +* A pointer to an array of at least "maxfld" character pointers. +* Each element is returned holding a pointer to the start of the +* corresponding field in "str" (in the order in which they occur +* within "str"), or NULL if no corresponding field can be found. +* nc +* A pointer to an array of at least "maxfld" integers. Each +* element is returned holding the number of characters in the +* corresponding field, or zero if no corresponding field can be +* found. +* val +* Pointer to a location at which to store the value +* equivalent to the returned field values. If this is NULL, +* it is ignored. + +* Returned Value: +* The number of fields succesfully identified and returned. + +* Notes: +* - Leading and trailing spaces are ignored. +* - If the formatted value is not consistent with the supplied format +* string, then a value of zero will be returned, "fields" will be +* returned holding NULLs, "nc" will be returned holding zeros, and +* "val" is returned holding VAL__BAD. +* - Fields are counted from the start of the formatted string. If the +* string contains more than "maxfld" fields, then trailing fields are +* ignored. +* - If this function is invoked with the global error status set, or +* if it should fail for any reason, then a value of zero will be returned +* as the function value, and "fields", "nc" and "val" will be returned +* holding their supplied values +*- +*/ + +/* Local Variables: */ + char log_esc[ 50 ]; /* Buffer for graphical delimiter string */ + const char *fmt; /* Pointer to parsed Format string */ + const char *log_del; /* Pointer to delimiter string */ + const char *p; /* Pointer to next character */ + double value; /* Equivalent radians value */ + int ifld; /* Field index */ + int integ; /* Cast axis value to integer before printing? */ + int len; /* Length of formatted string */ + int log; /* Format as "10**x"? */ + int n; /* Number of characters read */ + int neg; /* Negate final value? */ + int result; /* Result fields count to return */ + int sign; /* Include leading sign in front of "10**x"? */ + int space; /* Include leading space in front of "10**x"? */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + for( ifld = 0; ifld < maxfld; ifld++ ) { + fields[ ifld ] = NULL; + nc[ ifld ] = 0; + } + if( val ) *val = AST__BAD; + +/* Parse the Format string. This returns a collection of flags indicating + if any AST specific formatting features are specified in the Format + string. It also returns a pointer to a new Format string which is a + standard C printf format specifier. Currently the only flags are "log" + which indicates if the value should be formatted as "10**x" using + the graphical escape sequences defined within the Plot class to produce + "x" as a superscript of "10", "sign" which is used with log to indicate + if a sign should always be included infront of the "10", and "space" + which indicates if a leading space should be included infronyt of "10" if + no sign is included. */ + fmt = ParseAxisFormat( fmt0, astGetAxisDigits( this ), &log, &sign, + &space, &integ, status ); + fmt = astFree( (void *) fmt ); + + if( astOK ) { + +/* Obtain the length of the formatted string. */ + len = (int) strlen( str ); + +/* First deal with "log" format. */ + if( log ) { + +/* We need room for at least 2 fields. */ + if( maxfld > 1 ) { + +/* Return a pointer to the first non-blank character. */ + p = str; + while( *p == ' ' ) p++; + fields[ 0 ] = (char *) p; + +/* If the first non-blank character is a minus sign, note it and skip it. */ + neg = 0; + if( *p == '-' ) { + neg = 1; + p++; + +/* If the first non-blank character is a plus sign, and skip it. */ + } else if( *p == '+' ) { + p++; + } + +/* Select the delimter.*/ + if( astEscapes( -1 ) ) { + astTuneC( "exdel", NULL, log_esc, sizeof( log_esc ) ); + log_del = log_esc; + } else { + log_del = log_txt; + } + +/* Check the remaining string starts with the correct delimiter. If + so, store the number of characters in the first field and skip over the + delimiter. */ + n = 0; + if( strstr( p, log_del ) == p ) { + nc[ 0 ] = p + 2 - fields[ 0 ]; + p += strlen( log_del ); + +/* Attempt to read a floating point value from the start of the remaining + string. */ + if( 1 == sscanf( p, "%lg%n", &value, &n ) ) { + +/* If succesfull, store the returned values. */ + result = 2; + fields[ 1 ] = (char *) p; + nc[ 1 ] = n; + if( val ) { + *val = pow( 10.0, value ); + if( neg ) *val = -(*val); + } + +/* Otherwise, see if the string starts with */ + } else if( strstr( p, "" ) == p ) { + +/* If succesfull, store the returned values. */ + result = 2; + fields[ 1 ] = (char *) p; + nc[ 1 ] = 5; + if( val ) *val = 0.0; + } + +/* Zero is never formatted as an exponent. If the string starts with zero, + return a single zero field. */ + } else if( 1 == sscanf( p, "%lg%n", &value, &n ) ) { + if( value == 0.0 ) { + result = 1; + nc[ 0 ] = p + n - fields[ 0 ]; + if( val ) *val = 0.0; + } + } + } + +/* Now deal with normal decimal format */ + } else { + +/* Attempt to read a floating point value from the formatted string. */ + if ( n = 0, + ( 1 == sscanf( str, "%lg %n", &value, &n ) ) + && ( n >= len ) && maxfld > 0 ) { + +/* If succesful, return a pointer to the first non-blank character. */ + p = str; + while( *p == ' ' ) p++; + fields[ 0 ] = (char *) p; + +/* Find the last non-blank character. */ + p += len; + while( p[ -1 ] == ' ' ) p--; + +/* Return the number of characters in the field. */ + nc[ 0 ] = p - fields[ 0 ]; + +/* Return the field value. */ + if( val ) *val = value; + +/* Indicate that we are returning one field. */ + result = 1; + } + } + } + +/* Return the result. */ + return result; +} + +static const char *AxisFormat( AstAxis *this, double value, int *status ) { +/* +*+ +* Name: +* astAxisFormat + +* Purpose: +* Format a coordinate value for an Axis. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "axis.h" +* const char *astAxisFormat( AstAxis *this, double value ) + +* Class Membership: +* Axis method. + +* Description: +* This function returns a pointer to a string containing the formatted +* (character) version of a coordinate value for an Axis. The formatting +* applied is that specified by a previous invocation of the +* astSetAxisFormat method. A suitable default format is applied if +* necessary. + +* Parameters: +* this +* Pointer to the Axis. +* value +* The coordinate value to be formatted. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted value. + +* Notes: +* - The returned string pointer may point at memory allocated within +* the Axis object, or at static memory. The contents of the string may be +* over-written or the pointer may become invalid following a further +* invocation of the same function or deletion of the Axis. A copy of the +* string should therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Constants: */ +#define ERRBUF_LEN 80 + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *errstat; /* Pointer for system error message */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + char log_esc[ 50 ]; /* Buffer for graphical delimiter string */ + const char *fmt0; /* Pointer to original Format string */ + const char *fmt; /* Pointer to parsed Format string */ + const char *log_del; /* Pointer to delimiter string */ + const char *result; /* Pointer to formatted value */ + double x; /* The value to be formatted by sprintf */ + int integ; /* Cast axis value to integer before printing? */ + int log; /* Format as "10**x"? */ + int nc; /* Total number of characters written */ + int ncc; /* Number of characters written */ + int sign; /* Include leading sign in front of "10**x"? */ + int space; /* Include leading space in front of "10**x"? */ + int stat; /* Value of errno after error */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + nc = 0; + x = value; + +/* Check if a bad coordinate value was supplied and return a pointer to an + appropriate string if necessary. */ + if ( value == AST__BAD ) { + result = ""; + +/* Otherwise, obtain a pointer to the Format string. Note a private member + function is used here in preference to an object method. This is because the + syntax of the Format string may be extended by derived classes and we do not + want to obtain a string that we cannot interpret here (where we are + restricted to C format specifiers capable of formatting double values). + Classes that extend the syntax should provide their own astAxisFormat method + and may need to store the string in a separate location. The original + location should not be re-used as the string it contains may be needed by + the Axis astOverlay method when overlaying attributes on to another Axis + object. */ + } else { + fmt0 = GetAxisFormat( this, status ); + +/* Parse the Format string. This returns a collection of flags indicating + if any AST specific formatting features are specified in the Format + string. It also returns a pointer to a new Format string which is a + standard C printf format specifier. Currently the only flags are "log" + which indicates if the value should be formatted as "10**x" using + the graphical escape sequences defined within the Plot class to produce + "x" as a superscript of "10", "sign" which is used with log to indicate + if a sign should always be included infront of the "10", and "space" + which indicates if a leading space should be included infronyt of "10" + if no sign is included. It also modifies ".*" precision fields by + replacing the "*" by the current vale of the Digits attribute. */ + fmt = ParseAxisFormat( fmt0, astGetAxisDigits( this ), &log, &sign, + &space, &integ, status ); + if( astOK ) { + +/* Format zero normally. */ + if( value == 0.0 ) log = 0; + +/* If log format is required, find the value of the exponent "x", and + initialise the returned string to hold the exponent and the graphical + escape sequence which produces a superscript. Otherwise just format the + supplied value. */ + if( log ) { + + if( sign ) { + axisformat_buff[ 0 ] ='+'; + nc = 1; + + } else if( space ) { + axisformat_buff[ 0 ] =' '; + nc = 1; + } + + if( value > 0 ) { + x = log10( integ ? (int) value : value ); + + } else { + x = log10( integ ? (int) -value : -value ); + axisformat_buff[ 0 ] ='-'; + nc = 1; + } + + if( astEscapes( -1 ) ) { + astTuneC( "exdel", NULL, log_esc, sizeof( log_esc ) ); + log_del = log_esc; + } else { + log_del = log_txt; + } + + nc += sprintf( axisformat_buff + nc, "%s", log_del ); + +/* Round small exponents to zero. */ + if( fabs( x ) < 1.0E-10 ) x = 0.0; + } + } + +/* Clear errno and attempt to format the value as if the Format string were + a standard "sprintf" format. */ + if ( astOK ) { + errno = 0; + if( integ ) { + ncc = sprintf( axisformat_buff + nc, fmt, (int) x ); + } else { + ncc = sprintf( axisformat_buff + nc, fmt, x ); + } + nc += ncc; + +/* If log format is being used, terminate the string with an escape + sequence which resets the graphical attributes to what they were at the + start of the string. */ + if( log ) nc += sprintf( axisformat_buff + nc, "%%+" ); + +/* The possibilities for error detection are limited here, but check if an + error value was returned and report an error. Include information from + errno if it was set. */ + if ( ncc < 0 ) { + stat = errno; + if( stat ) { +#if HAVE_STRERROR_R + strerror_r( stat, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( stat ); +#endif + } else { + *errbuf = 0; + errstat = errbuf; + } + astError( AST__FMTER, "astAxisFormat(%s): Error formatting a " + "coordinate value of %1.*G%s%s.", status, astGetClass( this ), + DBL_DIG, value, stat? " - " : "", errstat ); + astError( AST__FMTER, "The format string was \"%s\".", status, fmt ); + +/* Also check that the result buffer did not overflow. If it did, memory will + probably have been corrupted but this cannot be prevented with "sprintf". + Report the error and abort. */ + } else if ( nc > AST__AXIS_AXISFORMAT_BUFF_LEN ) { + astError( AST__FMTER, "astAxisFormat(%s): Internal buffer " + "overflow while formatting a coordinate value of %1.*G " + "- result exceeds %d characters.", status, astGetClass( this ), + DBL_DIG, value, AST__AXIS_AXISFORMAT_BUFF_LEN ); + astError( AST__FMTER, "The format string was \"%s\".", status, fmt ); + +/* If succesfull, return a pointer to the buffer. */ + } else { + result = axisformat_buff; + } + } + +/* Free resources. */ + fmt = astFree( (void *) fmt ); + + } + +/* Return the result. */ + return result; + +} +#undef ERRBUF_LEN + +static double AxisCentre( AstAxis *this, double value, double gap, int *status ) { +/* +*+ +* Name: +* astAxisCentre + +* Purpose: +* Find a "nice" central value for tabulating Axis values. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* double astAxisCentre( AstAxis *this, double value, double gap ) + +* Class Membership: +* Axis method. + +* Description: +* This function returns an axis value which produces a nice formatted +* value suitable for a major tick mark on a plot axis. + +* Parameters: +* this +* Pointer to the Axis. +* value +* An arbitrary axis value in the section that is being plotted. +* gap +* The gap size. + +* Returned Value: +* The nice central axis value. + +* Notes: +* - A value of zero is returned if the supplied gap size is zero. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + double result; /* Returned central axis value */ + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The returned central value is an integral number of gaps away from the + origin and is close to the supplied axis value. This would result in + the origin being at a major tick mark. */ + if( gap != 0.0 && gap != AST__BAD && value != AST__BAD ) { + result = gap*floor( 0.5 + value/gap ); + } + +/* Return the result. */ + return result; +} + +static double AxisGap( AstAxis *this, double gap, int *ntick, int *status ) { +/* +*+ +* Name: +* astAxisGap + +* Purpose: +* Find a "nice" gap for tabulating Axis values. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* double astAxisGap( AstAxis *this, double gap, int *ntick ) + +* Class Membership: +* Axis method. + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted Axis values, the returned gap size being as +* close as possible to the supplied target gap size. It also +* returns a convenient number of divisions into which the gap can +* be divided. + +* Parameters: +* this +* Pointer to the Axis. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the supplied gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + double absgap; /* Absolute supplied gap size */ + double b; /* Decimal step size */ + double result; /* Returned gap size */ + int index; /* Index into tables */ + int positive; /* Value is positive (or zero)? */ + +/* Local Data: */ + static double table1[ 10 ] = /* Table of nice decimal gaps */ + { 1.0, 2.0, 2.0, 5.0, 5.0, 5.0, 5.0, 10.0, 10.0, 10.0 }; + static int table2[ 10 ] = /* Table giving number of divisions */ + { 5, 4, 4, 5, 5, 5, 5, 5, 5, 5 }; + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the supplied gap size is not zero. */ + if ( gap != 0.0 ) { + +/* Determine if the supplied gap size is positive and obtain its + absolute value. */ + positive = ( gap >= 0.0 ); + absgap = positive ? gap : -gap; + +/* Obtain a value which has a 1 at the position of the most + significant decimal digit in the target gap size and zeros at all + other positions. */ + b = pow( 10.0, floor( log10( absgap ) ) ); + +/* This value is the basic "step size". Find the nearest whole number + of steps in the supplied gap, and then use the look-up-table in + "table1" to find the closest acceptable gap size. Convert this gap + size back to an absolute value by multiplying by the step size. */ + index = (int) ( absgap / b + 0.5 ) - 1; + result = b * table1[ index ]; + +/* If the target gap was negative, negate the result. */ + if( !positive ) result = -result; + +/* Store the number of divisions in the gap. */ + if ( ntick ) *ntick = table2[ index ]; + } + +/* Return the result. */ + return result; +} + +static int AxisIn( AstAxis *this, double lo, double hi, double val, int closed, int *status ){ +/* +*+ +* Name: +* astAxisIn + +* Purpose: +* Test if an axis value lies within a given interval. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* int AxisIn( AstAxis *this, double lo, double hi, double val, int closed ) + +* Class Membership: +* Axis member function. + +* Description: +* This function returns non-zero if a given axis values lies within a +* given axis interval. + +* Parameters: +* this +* Pointer to the Axis. +* lo +* The lower axis limit of the interval. +* hi +* The upper axis limit of the interval. +* val +* The axis value to be tested. +* closed +* If non-zero, then the lo and hi axis values are themselves +* considered to be within the interval. Otherwise they are outside. + +* Returned Value: +* Non-zero if the test value is inside the interval. + +* Class Applicability: +* Axis +* Uses simple Euclidean test +* SkyAxis +* All angles which are numerically between "lo" and "hi" are within +* the interval. Angle outside this range are also within the interval +* if they can be brought into the range by addition or subtraction +* of a multiple of 2.PI. +*- +*/ + +/* For speed, omit the astOK check since no pointers are being used. */ + if( closed ) { + return ( lo <= val && val <= hi ); + } else { + return ( lo < val && val < hi ); + } +} + +static void AxisNorm( AstAxis *this, double *value, int *status ) { +/* +*+ +* Name: +* astAxisNorm + +* Purpose: +* Normalise an Axis coordinate value. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "axis.h" +* void astAxisNorm( AstAxis *this, double *value ) + +* Class Membership: +* Axis method. + +* Description: +* This function converts an Axis coordinate value which might +* potentially be unsuitable for display to a user (for instance, +* may lie outside the expected range of values) into an acceptable +* alternative value suitable for display. +* +* Typically, for axes that represent cyclic values such as angles, +* this function wraps an arbitrary coordinate value so that it +* lies within the first cycle (say zero to 2*pi). For an ordinary +* linear Axis, without constraints, this function will typically +* return the original value unchanged. + +* Parameters: +* this +* Pointer to the Axis. +* value +* Pointer to the coordinate value to be normalised, which will +* be modified in place. +*- +*/ + +/* In the Axis class there are no constraints, so simply return + without action. */ + return; +} + +static double AxisOffset( AstAxis *this, double v1, double dist, int *status ) { +/* +*+ +* Name: +* astAxisOffset + +* Purpose: +* Add an increment onto a supplied axis value. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* AxisOffset( AstAxis *this, double v1, double dist ) + +* Class Membership: +* Axis method. + +* Description: +* This function returns an axis value formed by adding a signed axis +* increment onto a supplied axis value. +* +* For a simple Axis, this is a trivial operation. But for other +* derived classes of Axis (such as a SkyAxis) this is not the case. + +* Parameters: +* this +* Pointer to the Axis. +* v1 +* The supplied axis value +* dist +* The axis increment + +* Returned Value: +* The axis value which is the specified increment away from v1. + +* Notes: +* - A value of AST__BAD is returned if either axis value is AST__BAD. +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + double result; /* Returned gap size */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check both axis values are OK, and form the returned axis value. */ + if( v1 != AST__BAD && dist != AST__BAD ) result = v1 + dist; + +/* Return the result. */ + return result; +} + +static void AxisOverlay( AstAxis *template, AstAxis *result, int *status ) { +/* +*+ +* Name: +* astAxisOverlay + +* Purpose: +* Overlay the attributes of a template Axis on to another Axis. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* void astAxisOverlay( AstAxis *template, AstAxis *result ) + +* Class Membership: +* Axis method. + +* Description: +* This function overlays attributes of one Axis (the "template") on to +* another Axis, so as to over-ride selected attributes of that second +* Axis. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which an Axis acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. + +* Parameters: +* template +* Pointer to the template Axis, for which values should have been +* explicitly set for any attribute which is to be transferred. +* result +* Pointer to the Axis which is to receive the new attribute values. + +* Returned Value: +* void +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Define a macro to overlay a single attribute. This tests if the attribute + is set in the template Axis. If it is, its value is obtained and set in the + result Axis also. */ +#define OVERLAY(par) \ + if ( astTestAxis##par( template ) ) { \ + astSetAxis##par( result, astGetAxis##par( template ) ); \ + } +/* Overlay each Axis attribute in turn. */ + OVERLAY(Digits); + OVERLAY(Direction); + OVERLAY(Label); + OVERLAY(Symbol); + OVERLAY(Unit); + +/* Handle the Format string slightly differently by using a private member + function to obtain it. This is necessary in case derived classes have + extended the string syntax (see the AxisFormat function for more + details). */ + if ( TestAxisFormat( template, status ) ) { + SetAxisFormat( result, GetAxisFormat( template, status ), status ); + } + +/* Undefine macros local to this function. */ +#undef OVERLAY +} + +static int AxisUnformat( AstAxis *this, const char *string, double *value, int *status ) { +/* +*+ +* Name: +* astAxisUnformat + +* Purpose: +* Read a formatted coordinate value for an Axis. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "axis.h" +* int astAxisUnformat( AstAxis *this, const char *string, double *value ) + +* Class Membership: +* Axis method. + +* Description: +* This function reads a formatted coordinate value for an Axis +* (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the Axis. +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will be +* returned. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*- +*/ + +/* Local Variables: */ + double coord; /* Coordinate value read */ + int nc; /* Number of characters read */ + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* See if the string can be read as a floating point number. If so, + return its value. Also obtain the number of characters read, + including any leading and trailing white space. */ + if ( 1 == astSscanf( string, "%lf %n", &coord, &nc ) ) { + *value = coord; + +/* Otherwise, see if the string starts with "", allowing mixed + case and leading, embedded and trailing white space. If so, return + the value AST__BAD. */ + } else if ( nc = 0, + ( 0 == astSscanf( string, " < %*1[Bb] %*1[Aa] %*1[Dd] > %n", &nc ) + && ( nc > 0 ) ) ) { + *value = AST__BAD; + +/* If the string cannot be read, return a function result of zero. */ + } else { + nc = 0; + } + +/* Return the number of characters read. */ + return nc; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Axis member function (over-rides the astClearAttrib protected +* method inherited from the Object class). + +* Description: +* This function clears the value of a specified attribute for an +* Axis, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Axis. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstAxis *this; /* Pointer to the Axis structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Axis structure. */ + this = (AstAxis *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Digits. */ +/* ------- */ + if ( !strcmp( attrib, "digits" ) ) { + astClearAxisDigits( this ); + +/* Direction. */ +/* ---------- */ + } else if ( !strcmp( attrib, "direction" ) ) { + astClearAxisDirection( this ); + +/* Format. */ +/* ------- */ + } else if ( !strcmp( attrib, "format" ) ) { + astClearAxisFormat( this ); + +/* Label. */ +/* ------ */ + } else if ( !strcmp( attrib, "label" ) ) { + astClearAxisLabel( this ); + +/* Top. */ +/* ---- */ + } else if ( !strcmp( attrib, "top" ) ) { + astClearAxisTop( this ); + +/* Bottom. */ +/* ------- */ + } else if ( !strcmp( attrib, "bottom" ) ) { + astClearAxisBottom( this ); + +/* Symbol. */ +/* ------- */ + } else if ( !strcmp( attrib, "symbol" ) ) { + astClearAxisSymbol( this ); + +/* Unit. */ +/* ----- */ + } else if ( !strcmp( attrib, "unit" ) ) { + astClearAxisUnit( this ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute name matches any of the read-only attributes + of this class. If it does, then report an error. */ + } else if ( !strcmp( attrib, "normunit" ) || + !strcmp( attrib, "internalunit" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static const char *GetAxisInternalUnit( AstAxis *this, int *status ){ +/* +*+ +* Name: +* astGetAxisInternalUnit + +* Purpose: +* Return the unit string for unformatted Axis values + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* const char *astGetAxisInternalUnit( AstAxis *this ){ + +* Class Membership: +* Axis method. + +* Description: +* This function returns the axis InternalUnit attribute. For basic +* axes, the InternalUnit and Unit attributes are the same. + +* Parameters: +* this +* Pointer to the Axis. + +* Returned Value: +* - Pointer to a null-terminated string containing the internal +* unit string. + +* Notes: +* - The returned pointer points to a static memory buffer. The +* contents of this buffer will be over-written on each invocation of +* this function. A copy of the returned string should therefore be +* taken if it will be needed later. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + return astGetAxisUnit( this ); +} + +static const char *GetAxisNormUnit( AstAxis *this, int *status ){ +/* +*+ +* Name: +* astGetAxisNormUnit + +* Purpose: +* Return the normalised Unit attribute for an Axis. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "axis.h" +* const char *astGetAxisNormUnit( AstAxis *this ){ + +* Class Membership: +* Axis method. + +* Description: +* This function normalised and returns the axis Unit attribute. +* Normalisation refers to transformations such as "s*(m/s)" -> "m". + +* Parameters: +* this +* Pointer to the Axis. + +* Returned Value: +* - Pointer to a null-terminated string containing the normalised +* unit string. + +* Notes: +* - The returned pointer points to a static memory buffer. The +* contents of this buffer will be over-written on each invocation of +* this function. A copy of the returned string should therefore be +* taken if it will be needed later. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const char *result; /* Pointer to dynamic memory holding returned text */ + int nc; /* Length of normalised Unit string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Get the Axis Unit attrribute and normalise it. */ + result = astUnitNormaliser( astGetAxisUnit( this ) ); + +/* If successful, check that the resulting string will fit in the buffer. + If not, report an error. */ + if( result ) { + nc = strlen( result ); + if( nc > AST__AXIS_GETAXISNORMUNIT_BUFF_LEN ) { + astError( AST__FMTER, "astGetAxisNormUnit(%s): Internal buffer " + "overflow while normalising the units string '%s' " + "- result exceeds %d characters.", status, astGetClass( this ), + result, AST__AXIS_GETAXISNORMUNIT_BUFF_LEN ); + result = astFree( (void *) result ); + +/* If so, copy it into the static buffer and free the dynamic memory returned + by astUnitNormaliser. */ + } else { + strcpy( getaxisnormunit_buff, result ); + } + astFree( (void *) result ); + + result = getaxisnormunit_buff; + } + +/* Return the answer. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Axis member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Axis, +* in bytes. + +* Parameters: +* this +* Pointer to the Axis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstAxis *this; /* Pointer to Axis structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the Axis structure. */ + this = (AstAxis *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astTSizeOf( this->label ); + result += astTSizeOf( this->format ); + result += astTSizeOf( this->symbol ); + result += astTSizeOf( this->unit ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Axis member function (over-rides the protected astGetAttrib +* method inherited from the Object class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for an Axis, formatted as a character string. + +* Parameters: +* this +* Pointer to the Axis. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Axis, or at static memory. The contents of the string +* may be over-written or the pointer may become invalid following +* a further invocation of the same function or any modification of +* the Axis. A copy of the string should therefore be made if +* necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstAxis*this; /* Pointer to the Axis structure */ + const char *result; /* Pointer value to return */ + double dval; /* Double attribute value */ + int digits; /* Digits attribute value */ + int direction; /* Direction attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Axis structure. */ + this = (AstAxis *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an + appropriate format. Set "result" to point at the result string. */ + +/* Digits. */ +/* ------- */ + if ( !strcmp( attrib, "digits" ) ) { + digits = astGetAxisDigits( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", digits ); + result = getattrib_buff; + } + +/* Direction. */ +/* ---------- */ + } else if ( !strcmp( attrib, "direction" ) ) { + direction = astGetAxisDirection( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", direction ); + result = getattrib_buff; + } + +/* Top. */ +/* ---- */ + } else if ( !strcmp( attrib, "top" ) ) { + dval = astGetAxisTop( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Bottom. */ +/* ------- */ + } else if ( !strcmp( attrib, "bottom" ) ) { + dval = astGetAxisBottom( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Format. */ +/* ------- */ + } else if ( !strcmp( attrib, "format" ) ) { + result = astGetAxisFormat( this ); + +/* Label. */ +/* ------ */ + } else if ( !strcmp( attrib, "label" ) ) { + result = astGetAxisLabel( this ); + +/* Symbol. */ +/* ------- */ + } else if ( !strcmp( attrib, "symbol" ) ) { + result = astGetAxisSymbol( this ); + +/* Unit. */ +/* ----- */ + } else if ( !strcmp( attrib, "unit" ) ) { + result = astGetAxisUnit( this ); + +/* NormUnit. */ +/* --------- */ + } else if ( !strcmp( attrib, "normunit" ) ) { + result = astGetAxisNormUnit( this ); + +/* InternalUnit. */ +/* ------------- */ + } else if ( !strcmp( attrib, "internalunit" ) ) { + result = astGetAxisInternalUnit( this ); + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static const char *GetDefaultFormat( AstAxis *this, int *status ){ +/* +* Name: +* GetDefaultFormat + +* Purpose: +* Return a pointer to a string holding the default Format value. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* const char *GetDefaultFormat( AstAxis *this, int *status ) + +* Class Membership: +* Axis member function + +* Description: +* This function returns a pointer to a string holding the default +* Format value, which is based on the current Digits value. + +* Parameters: +* this +* A pointer to the Axis structure. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a static null-terminated character string containing +* the default Format string. + +* Notes: +* - A null string will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Check the global error status. */ + if ( !astOK ) return ""; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Create the default format value and store it in the "format_buff" + static variable. */ + (void) sprintf( getdefaultformat_buff, "%%1.%dG", astGetAxisDigits( this ) ); + +/* Return a pointer to the "format_buff" static variable. */ + return getdefaultformat_buff; +} + +void astInitAxisVtab_( AstAxisVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitAxisVtab + +* Purpose: +* Initialise a virtual function table for an Axis. + +* Type: +* Protected function. + +* Synopsis: +* #include "axis.h" +* void astInitAxisVtab( AstAxisVtab *vtab, const char *name ) + +* Class Membership: +* Axis vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Axis class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitObjectVtab( (AstObjectVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAAxis) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstObjectVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->AxisAbbrev = AxisAbbrev; + vtab->AxisFields = AxisFields; + vtab->AxisFormat = AxisFormat; + vtab->AxisDistance = AxisDistance; + vtab->AxisOffset = AxisOffset; + vtab->AxisCentre = AxisCentre; + vtab->AxisGap = AxisGap; + vtab->AxisIn = AxisIn; + vtab->AxisNorm = AxisNorm; + vtab->AxisOverlay = AxisOverlay; + vtab->AxisUnformat = AxisUnformat; + vtab->ClearAxisDigits = ClearAxisDigits; + vtab->ClearAxisDirection = ClearAxisDirection; + vtab->ClearAxisFormat = ClearAxisFormat; + vtab->ClearAxisLabel = ClearAxisLabel; + vtab->ClearAxisSymbol = ClearAxisSymbol; + vtab->ClearAxisUnit = ClearAxisUnit; + vtab->GetAxisDigits = GetAxisDigits; + vtab->GetAxisDirection = GetAxisDirection; + vtab->GetAxisFormat = GetAxisFormat; + vtab->GetAxisLabel = GetAxisLabel; + vtab->GetAxisSymbol = GetAxisSymbol; + vtab->GetAxisUnit = GetAxisUnit; + vtab->GetAxisInternalUnit = GetAxisInternalUnit; + vtab->GetAxisNormUnit = GetAxisNormUnit; + vtab->SetAxisDigits = SetAxisDigits; + vtab->SetAxisDirection = SetAxisDirection; + vtab->SetAxisFormat = SetAxisFormat; + vtab->SetAxisLabel = SetAxisLabel; + vtab->SetAxisSymbol = SetAxisSymbol; + vtab->SetAxisUnit = SetAxisUnit; + vtab->TestAxisDigits = TestAxisDigits; + vtab->TestAxisDirection = TestAxisDirection; + vtab->TestAxisFormat = TestAxisFormat; + vtab->TestAxisLabel = TestAxisLabel; + vtab->TestAxisSymbol = TestAxisSymbol; + vtab->TestAxisUnit = TestAxisUnit; + vtab->TestAxisInternalUnit = TestAxisInternalUnit; + vtab->TestAxisNormUnit = TestAxisNormUnit; + + vtab->ClearAxisTop = ClearAxisTop; + vtab->GetAxisTop = GetAxisTop; + vtab->SetAxisTop = SetAxisTop; + vtab->TestAxisTop = TestAxisTop; + + vtab->ClearAxisBottom = ClearAxisBottom; + vtab->GetAxisBottom = GetAxisBottom; + vtab->SetAxisBottom = SetAxisBottom; + vtab->TestAxisBottom = TestAxisBottom; + +/* Save the inherited pointers to methods that will be extended, and replace + them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + +/* Declare the destructor, copy constructor and dump function. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "Axis", "Coordinate axis" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static char *ParseAxisFormat( const char *fmt0, int digs, int *log, int *sign, + int *lspace, int *integ, int *status ){ +/* +* Name: +* ParseAxisFormat + +* Purpose: +* Parse the Format string for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* char *ParseAxisFormat( const char *fmt0, int digs, int *log, int *sign, +* int *lspace, int *integ, int *status ) + +* Class Membership: +* Axis member function + +* Description: +* This function returns a collection of flags indicating if any AST +* specific formatting features are specified in the supplied Format +* string. It also returns a pointer to a new Format string which is a +* standard C printf format specifier. + +* Parameters: +* fmt0 +* The value of the Format attribute. +* digs +* The default number of digits of precision to use. This is used +* if the given format specifier includes a wildcard precision (".*"). +* In this case, the returned format specifier will be modified to +* include an explicit precision value equal to the supplied value +* of "digs". +* log +* Pointer to an integer in which to store a flag indicating if the +* if the axis value should be formatted as "10**x" using the graphical +* escape sequences defined within the Plot class to produce "x" as a +* superscript of "10". A non-zero value will be returned if the +* supplied Format string has a '&' character in its printf +* field (that is, between the leading '%' sign and the optional +* printf field width). +* sign +* Pointer to an integer in which to store a flag indicating if a +* sign character ('+' or '-') should always be included in front +* of the "10" if "log" is returned non-zero. If "log" is returned +* zero, then "sign" will also be zero. If "log" is non-zero, then +* a non-zero value for "sign" will be returned if the supplied Format +* string has a '+' character in its printf field (that is, +* between the leading '%' sign and the optional printf field width). +* lspace +* Pointer to an integer in which to store a flag indicating if a +* leading space should be included in front of the "10" if "log" is +* returned non-zero and "sign" is returned zero. Otherwise, "lspace" +* will also be zero. If "log" is non-zero, then a non-zero value for +* "lspace" will be returned if the supplied Format string has a ' ' +* character in its printf field (that is, between the leading +* '%' sign and the optional printf field width). +* integ +* Pointer to an integer in which to store a flag indicating if the +* returned format specifier includes an integer conversion code +* (e.g. %d) or floating point conversion code (e.g. "%.7G"). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a dynamically allocated null-terminated string containing +* the modified Format string. This will be a copy of the supplied +* Format string, but with any '&' flag removed. Any '+' or ' ' flag will +* also be removed if "log" is returned as non-zero. An explicit +* precision field will be included if the supplied format includes a +* ".*" precision field. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + char *a; /* Pointer to next char read from original format */ + char *b; /* Pointer to next char to write to new format */ + char *c; /* Pointer to next char read from original format */ + char *new; /* Pointer to new returned string */ + char *perc; /* Pointer to percent sign */ + char *result; /* Pointer to the returned string */ + int hash; /* Was a '#' flag found? */ + int len; /* Used length of format string */ + int minus; /* Was a '-' flag found? */ + int plus; /* Was a '+' flag found? */ + int rlen; /* Length of result */ + int space; /* Was a ' ' flag found? */ + +/* Initialise. */ + result = NULL; + *log = 0; + *sign = 0; + *lspace = 0; + *integ = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Take a copy of the supplied string. Check the pointer can be used + safely. */ + len = astChrLen( fmt0 ); + result = astStore( NULL, fmt0, len + 1 ); + if( astOK ) { + result[ len ] = 0; + +/* Find the first percent sign. Do nothing if none is found. */ + perc = strchr( result, '%' ); + if( perc ) { + +/* Check each character following the percent sign until one is found + which is not a legal printf flag, or a legal AST extension flag. Note + which ones are present. */ + minus = 0; + plus = 0; + space = 0; + hash = 0; + + a = perc; + while( ++a ){ + if( *a == '-' ){ + minus = 1; + } else if( *a == '+' ){ + plus = 1; + } else if( *a == ' ' ){ + space = 1; + } else if( *a == '#' ){ + hash = 1; + } else if( *a == '&' ){ + *log = 1; + } else { + break; + } + } + +/* If no '&' flag was found just return the unaltered copy of the + supplied Format string. Otherwise, remove any '+' or ' ' flag. */ + if( *log ) { + if( plus ) *sign = 1; + if( space ) *lspace = 1; + +/* Append any remaining flag characters to the output string. */ + perc++; + if( minus ) *(perc++) = '-'; + if( hash ) *(perc++) = '#'; + +/* Copy the remaining characters down to fill up the gap left by the + removed flags. */ + while( *a ) *(perc++) = *(a++); + +/* Terminate the returned string. */ + *perc = 0; + + } + } + } + +/* If the format specifier being returned does include a ".*" precision, + replace the "*" with the value of the Digits attribute. */ + if( result ) { + +/* Find the first percent sign. Do nothing if none is found. */ + a = strchr( result, '%' ); + if( a ) { + +/* Check each character following the percent sign until one is found + which is not a legal printf flag. */ + while( ++a ){ + if( *a != '-' && *a != '+' && *a != ' ' && *a != '#' ) break; + } + +/* Skip any field width (a decimal integer) following the flags. */ + a--; + while( ++a ) { + if( !isdigit( *a ) ) break; + } + +/* Get a pointer to the next alphabetic character. This will be the + conversion code. If it an integer code, return *integ non-zero. */ + c = a - 1; + while( ++c ) { + if( isalpha( *c ) ) { + if( *c == 'd' || *c == 'i' || *c == 'u' || *c == 'o' || + *c == 'x' || *c == 'X' || *c == 'c' ) *integ = 1; + break; + } + } + +/* Go back to the end of the field width. If the next two characters are + "." and "*", change the asterisk to the supplied "digs" value. */ + if( a[ 0 ] == '.' && a[ 1 ] == '*' ) { + +/* Allocate memory to hold the extended format string (allowing 20 + characters for formatting the digs value - just in case something like + INT_MAX is supplied by mistake), and store the existing string in it. */ + rlen = strlen( result ); + new = astMalloc( rlen + 22 ); + if( new ) memcpy( new, result, rlen + 1 ); + +/* Put the precision into the new string, following the field width. */ + b = new + ( a - result ); + b += sprintf( b, ".%d", digs ); + +/* Copy the remainder of the original format string to the new format + string. */ + if( a[ 2 ] != 0 ) strcpy( b, a + 2 ); + +/* Use the new format string in place of the old.*/ + astFree( result ); + result = new; + } + } + } + +/* Return the result. */ + return result; + +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Axis member function (over-rides the protected astSetAttrib +* method inherited from the Object class). + +* Description: +* This function assigns an attribute value for an Axis, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Axis. +* setting +* Pointer to a null terminated string specifying the new +* attribute value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstAxis *this; /* Pointer to Axis structure */ + double dval; /* Double attribute value */ + int digits; /* Number of digits of precision */ + int direction; /* Plot axis in normal direction? */ + int format; /* Offset of Format string */ + int label; /* Offset of Label string */ + int len; /* Length of setting string */ + int nc; /* Number of characters read from setting */ + int symbol; /* Offset of Symbol string */ + int unit; /* Offset of Unit string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Axis structure. */ + this = (AstAxis *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Digits. */ +/* ------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "digits= %d %n", &digits, &nc ) ) + && ( nc >= len ) ) { + astSetAxisDigits( this, digits ); + +/* Direction. */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "direction= %d %n", &direction, &nc ) ) + && ( nc >= len ) ) { + astSetAxisDirection( this, direction ); + +/* Top. */ +/* ---- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "top= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetAxisTop( this, dval ); + +/* Bottom. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "bottom= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetAxisBottom( this, dval ); + +/* Format. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "format=%n%*[^\n]%n", &format, &nc ) ) + && ( nc >= len ) ) { + astSetAxisFormat( this, setting + format ); + +/* Label. */ +/* ------ */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "label=%n%*[^\n]%n", &label, &nc ) ) + && ( nc >= len ) ) { + astSetAxisLabel( this, setting + label ); + +/* Symbol. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "symbol=%n%*[^\n]%n", &symbol, &nc ) ) + && ( nc >= len ) ) { + astSetAxisSymbol( this, setting + symbol ); + +/* Unit. */ +/* ----- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "unit=%n%*[^\n]%n", &unit, &nc ) ) + && ( nc >= len ) ) { + astSetAxisUnit( this, setting + unit ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* Use this macro to report an error if a read-only attribute has been + specified. */ + } else if ( MATCH( "normunit" ) || + MATCH( "internalunit" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass any unrecognised attribute setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Axis member function (over-rides the astTestAttrib protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate +* whether a value has been set for one of an Axis' attributes. + +* Parameters: +* this +* Pointer to the Axis. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstAxis *this; /* Pointer to the Axis structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Axis structure. */ + this = (AstAxis *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* Digits. */ +/* ------- */ + if ( !strcmp( attrib, "digits" ) ) { + result = astTestAxisDigits( this ); + +/* Direction. */ +/* ---------- */ + } else if ( !strcmp( attrib, "direction" ) ) { + result = astTestAxisDirection( this ); + +/* Top. */ +/* ---- */ + } else if ( !strcmp( attrib, "top" ) ) { + result = astTestAxisTop( this ); + +/* Bottom. */ +/* ------- */ + } else if ( !strcmp( attrib, "bottom" ) ) { + result = astTestAxisBottom( this ); + +/* Format. */ +/* ------- */ + } else if ( !strcmp( attrib, "format" ) ) { + result = astTestAxisFormat( this ); + +/* Label. */ +/* ------ */ + } else if ( !strcmp( attrib, "label" ) ) { + result = astTestAxisLabel( this ); + +/* Symbol. */ +/* ------- */ + } else if ( !strcmp( attrib, "symbol" ) ) { + result = astTestAxisSymbol( this ); + +/* Unit. */ +/* ----- */ + } else if ( !strcmp( attrib, "unit" ) ) { + result = astTestAxisUnit( this ); + +/* InternalUnit. */ +/* --------- */ + } else if ( !strcmp( attrib, "internalunit" ) ) { + result = astTestAxisInternalUnit( this ); + +/* NormUnit. */ +/* --------- */ + } else if ( !strcmp( attrib, "normunit" ) ) { + result = astTestAxisNormUnit( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int TestAxisInternalUnit( AstAxis *this, int *status ){ +/* +* Name: +* TestAxisInternalUnit + +* Purpose: +* Test if a InternalUnit attribute value is set for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* int TestAxisInternalUnit( AstAxis *this, int *status ) + +* Class Membership: +* Axis member function + +* Description: +* This function returns a boolean result (0 or 1) to indicate +* whether a value has been set for the InternalUnit string. + +* Parameters: +* this +* Pointer to the Axis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Tell the world that we know what value to use for InternalUnit if and + only if a value has been set for Unit. */ + return astTestAxisUnit( this ); +} + +static int TestAxisNormUnit( AstAxis *this, int *status ){ +/* +* Name: +* TestAxisNormUnit + +* Purpose: +* Test if a NormUnit attribute value is set for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* int TestAxisNormUnit( AstAxis *this, int *status ) + +* Class Membership: +* Axis member function + +* Description: +* This function returns a boolean result (0 or 1) to indicate +* whether a value has been set for the NormUnit string. + +* Parameters: +* this +* Pointer to the Axis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + + return astTestAxisUnit( this ); +} + + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with this + class using the macros defined for this purpose in the "object.h" file. For + a description of each attribute, see the class interface (in the associated + .h file). */ + +/* Digits. */ +/* ------- */ +/* Clear the Digits value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Axis,AxisDigits,digits,-INT_MAX) + +/* Supply a default of 7 digits if no value has been set. */ +astMAKE_GET(Axis,AxisDigits,int,0,( this->digits != -INT_MAX ? + this->digits : 7 )) + +/* Constrain the Digits value being set to be at least 1. */ +astMAKE_SET(Axis,AxisDigits,int,digits,( value > 1 ? value : 1 )) + +/* The Digits value is set if it is not -INT_MAX. */ +astMAKE_TEST(Axis,AxisDigits,( this->digits != -INT_MAX )) + +/* Direction. */ +/* ---------- */ +/* Clear the Direction value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Axis,AxisDirection,direction,-INT_MAX) + +/* Supply a default value of 1 if the Direction value is not set. */ +astMAKE_GET(Axis,AxisDirection,int,0,( this->direction != -INT_MAX ? + this->direction : 1 )) + +/* Set a Direction value of 1 if any non-zero value is supplied. */ +astMAKE_SET(Axis,AxisDirection,int,direction,( value != 0 )) + +/* The Direction value is set if it is not -INT_MAX. */ +astMAKE_TEST(Axis,AxisDirection,( this->direction != -INT_MAX )) + +/* Top. */ +/* -----*/ +/* Clear the Top Direction value by setting it to AST__BAD. */ +astMAKE_CLEAR(Axis,AxisTop,top,AST__BAD) + +/* Supply a default value of DBL_MAX if the Top value is not set.*/ +astMAKE_GET(Axis,AxisTop,double,0,( this->top != AST__BAD ? this->top : DBL_MAX)) + +/* Set the Top value. */ +astMAKE_SET(Axis,AxisTop,double,top,(value)) + +/* The Top value is set if it is not AST__BAD. */ +astMAKE_TEST(Axis,AxisTop,( this->top != AST__BAD )) + +/* Bottom. */ +/* --------*/ +/* Clear the Bottom Direction value by setting it to AST__BAD. */ +astMAKE_CLEAR(Axis,AxisBottom,bottom,AST__BAD) + +/* Supply a default value of -DBL_MAX if the Bottom value is not set.*/ +astMAKE_GET(Axis,AxisBottom,double,0.0,( this->bottom != AST__BAD ? this->bottom : -DBL_MAX)) + +/* Set the Bottom value. */ +astMAKE_SET(Axis,AxisBottom,double,bottom,(value)) + +/* The Bottom value is set if it is not AST__BAD. */ +astMAKE_TEST(Axis,AxisBottom,( this->bottom != AST__BAD )) + +/* Format. */ +/* ------- */ +/* Clear the Format value by freeing the allocated memory and assigning a NULL + pointer. */ +astMAKE_CLEAR(Axis,AxisFormat,format,astFree( this->format )) + +/* If the Format value is not set, return a pointer to a default Format + string. */ +astMAKE_GET(Axis,AxisFormat,const char *,NULL,( this->format ? this->format : + GetDefaultFormat( this, status ) ) ) + +/* Set a Format value by freeing any previously allocated memory, allocating + new memory, storing the string and saving the pointer to the copy. */ +astMAKE_SET(Axis,AxisFormat,const char *,format,astStore( this->format, value, + strlen( value ) + (size_t) 1 )) + +/* The Format value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Axis,AxisFormat,( this->format != NULL )) + +/* Label. */ +/* ------ */ +/* Clear the Label value by freeing the allocated memory and assigning a NULL + pointer. */ +astMAKE_CLEAR(Axis,AxisLabel,label,astFree( this->label )) + +/* If the Label value is not set, supply a default value by way of a pointer + to the constant string "Coordinate Axis". */ +astMAKE_GET(Axis,AxisLabel,const char *,NULL,( this->label ? this->label : + "Coordinate axis" )) + +/* Set a Label value by freeing any previously allocated memory, allocating + new memory, storing the string and saving the pointer to the copy. */ +astMAKE_SET(Axis,AxisLabel,const char *,label,astStore( this->label, value, + strlen( value ) + (size_t) 1 )) + +/* The Label value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Axis,AxisLabel,( this->label != NULL )) + +/* Symbol. */ +/* ------- */ +/* Clear the Symbol value by freeing the allocated memory and assigning a NULL + pointer. */ +astMAKE_CLEAR(Axis,AxisSymbol,symbol,astFree( this->symbol )) + +/* If the Symbol value is not set, supply a default value by way of a pointer + to the constant string "x". */ +astMAKE_GET(Axis,AxisSymbol,const char *,NULL,( this->symbol ? this->symbol : + "x" )) + +/* Set a Symbol value by freeing any previously allocated memory, allocating + new memory, storing the string and saving the pointer to the copy. */ +astMAKE_SET(Axis,AxisSymbol,const char *,symbol,astStore( this->symbol, value, + strlen( value ) + (size_t) 1 )) + +/* The Symbol value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Axis,AxisSymbol,( this->symbol != NULL )) + +/* Unit. */ +/* ----- */ +/* Clear the Unit value by freeing the allocated memory and assigning a NULL + pointer. */ +astMAKE_CLEAR(Axis,AxisUnit,unit,astFree( this->unit )) + +/* If the Unit value is not set, supply a default value by way of a pointer + to the constant string "". */ +astMAKE_GET(Axis,AxisUnit,const char *,NULL,( this->unit ? this->unit : "" )) + +/* Set a Unit value by freeing any previously allocated memory, allocating + new memory, storing the string and saving the pointer to the copy. */ +astMAKE_SET(Axis,AxisUnit,const char *,unit,astStore( this->unit, value, + strlen( value ) + (size_t) 1 )) + +/* The Unit value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Axis,AxisUnit,( this->unit != NULL )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Axis objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Axis objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstAxis *in; /* Pointer to input Axis */ + AstAxis *out; /* Pointer to output Axis */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Axis structures. */ + in = (AstAxis *) objin; + out = (AstAxis *) objout; + +/* For safety, first clear any references to the input memory from + the output Axis. */ + out->format = NULL; + out->label = NULL; + out->symbol = NULL; + out->unit = NULL; + +/* Make copies of the allocated strings and Objects. */ + if ( in->label ) out->label = astStore( NULL, in->label, + strlen( in->label ) + (size_t) 1 ); + if ( in->format ) out->format = astStore( NULL, in->format, + strlen( in->format ) + (size_t) 1 ); + if ( in->symbol ) out->symbol = astStore( NULL, in->symbol, + strlen( in->symbol ) + (size_t) 1 ); + if ( in->unit ) out->unit = astStore( NULL, in->unit, + strlen( in->unit ) + (size_t) 1 ); + +/* If an error occurred, clean up by freeing all memory allocated above. */ + if ( !astOK ) { + out->format = astFree( out->format ); + out->label = astFree( out->label ); + out->symbol = astFree( out->symbol ); + out->unit = astFree( out->unit ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Axis objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Axis objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstAxis *this; /* Pointer to Axis */ + +/* Obtain a pointer to the Axis structure. */ + this = (AstAxis *) obj; + +/* Free all allocated memory. */ + this->format = astFree( this->format ); + this->label = astFree( this->label ); + this->symbol = astFree( this->symbol ); + this->unit = astFree( this->unit ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Axis objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Axis class to an output Channel. + +* Parameters: +* this +* Pointer to the Axis whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstAxis *this; /* Pointer to the Axis structure */ + char comment[ 80 ]; /* Buffer for comment string */ + const char *sval; /* Pointer to string value */ + const char *lab; /* Pointer to unit label */ + double dval; /* Double value */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Axis structure. */ + this = (AstAxis *) this_object; + +/* Write out values representing the instance variables for the + Axis class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Label. */ +/* ------ */ + set = TestAxisLabel( this, status ); + sval = set ? GetAxisLabel( this, status ) : astGetAxisLabel( this ); + astWriteString( channel, "Label", set, 1, sval, "Axis Label" ); + +/* Symbol. */ +/* ------- */ + set = TestAxisSymbol( this, status ); + sval = set ? GetAxisSymbol( this, status ) : astGetAxisSymbol( this ); + astWriteString( channel, "Symbol", set, 1, sval, "Axis symbol" ); + +/* Unit. */ +/* ----- */ + set = TestAxisUnit( this, status ); + sval = set ? GetAxisUnit( this, status ) : astGetAxisUnit( this ); + +/* Get any label associated with the unit string. */ + lab = astUnitLabel( sval ); + +/* Construct a comment including the above label (but only if it is not + the same as the unit string) . */ + if( lab && strcmp( lab, sval ) ) { + (void) sprintf( comment, "Axis units (%s)", lab ); + } else { + (void) sprintf( comment, "Axis units" ); + } + +/* Write out the Unit value. */ + astWriteString( channel, "Unit", set, 0, sval, comment ); + +/* Digits. */ +/* ------- */ + set = TestAxisDigits( this, status ); + ival = set ? GetAxisDigits( this, status ) : astGetAxisDigits( this ); + astWriteInt( channel, "Digits", set, 0, ival, + "Default formatting precision" ); + +/* Format. */ +/* ------- */ + set = TestAxisFormat( this, status ); + sval = set ? GetAxisFormat( this, status ) : astGetAxisFormat( this ); + astWriteString( channel, "Format", set, 0, sval, "Format specifier" ); + +/* Direction. */ +/* ---------- */ + set = TestAxisDirection( this, status ); + ival = set ? GetAxisDirection( this, status ) : astGetAxisDirection( this ); + astWriteInt( channel, "Dirn", set, 0, ival, + ival ? "Plot in conventional direction (hint)" : + "Plot in reverse direction (hint)" ); +/* Top. */ +/* ---- */ + set = TestAxisTop( this, status ); + dval = set ? GetAxisTop( this, status ) : astGetAxisTop( this ); + astWriteDouble( channel, "Top", set, 0, dval, "Maximum legal axis value" ); + +/* Bottom. */ +/* ------- */ + set = TestAxisBottom( this, status ); + dval = set ? GetAxisBottom( this, status ) : astGetAxisBottom( this ); + astWriteDouble( channel, "Bottom", set, 0, dval, "Minimum legal axis value" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAAxis and astCheckAxis functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Axis,Object) +astMAKE_CHECK(Axis) + +AstAxis *astAxis_( const char *options, int *status, ...) { +/* +*+ +* Name: +* astAxis + +* Purpose: +* Create an Axis. + +* Type: +* Public function. + +* Synopsis: +* #include "axis.h" +* AstAxis *astAxis( const char *options, int *status, ... ) + +* Class Membership: +* Axis constructor. + +* Description: +* This function creates a new Axis and optionally initialises its +* attributes. + +* Parameters: +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new Axis. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new Axis. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstAxis *new; /* Pointer to new Axis */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the Axis, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitAxis( NULL, sizeof( AstAxis ), !class_init, &class_vtab, + "Axis" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new Axis' + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Axis. */ + return new; +} + +AstAxis *astAxisId_( const char *options, ... ) { +/* +* Name: +* astAxisId_ + +* Purpose: +* Create an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* AstAxis *astAxisId_( const char *options, ... ); + +* Class Membership: +* Axis constructor. + +* Description: +* This function implements the external (public) interface to the +* astAxis constructor function. It returns an ID value (instead of +* a true C pointer) to external users, and must be provided +* because astAxis_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astAxis_ directly, so it must be a re-implementation of +* it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astAxis_. + +* Returned Value: +* The ID value associated with the new Axis. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstAxis *new; /* Pointer to new Axis */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the Axis, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitAxis( NULL, sizeof( AstAxis ), !class_init, &class_vtab, + "Axis" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new Axis' + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Axis. */ + return astMakeId( new ); +} + +AstAxis *astInitAxis_( void *mem, size_t size, int init, + AstAxisVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitAxis + +* Purpose: +* Initialise an Axis. + +* Type: +* Protected function. + +* Synopsis: +* #include "axis.h" +* AstAxis *astInitAxis( void *mem, size_t size, int init, +* AstAxisVtab *vtab, const char *name ) + +* Class Membership: +* Axis initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Axis object. It allocates memory (if necessary) to accommodate +* the Axis plus any additional data associated with the derived class. +* It then initialises an Axis structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for an Axis at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Axis is to be created. This +* must be of sufficient size to accommodate the Axis data +* (sizeof(Axis)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Axis (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Axis +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Axis's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Axis. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astClass +* method). + +* Returned Value: +* A pointer to the new Axis. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstAxis *new; /* Pointer to new Axis */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitAxisVtab( vtab, name ); + +/* Initialise an Object structure (the parent class) as the first component + within the Axis structure, allocating memory if necessary. */ + new = (AstAxis *) astInitObject( mem, size, 0, (AstObjectVtab *) vtab, + name ); + + if ( astOK ) { + +/* Initialise the Axis data. */ +/* ------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->digits = -INT_MAX; + new->direction = -INT_MAX; + new->format = NULL; + new->label = NULL; + new->symbol = NULL; + new->unit = NULL; + new->top = AST__BAD; + new->bottom = AST__BAD; + +/* If an error occurred, clean up by deleting the new Axis. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Axis. */ + return new; +} + +AstAxis *astLoadAxis_( void *mem, size_t size, + AstAxisVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadAxis + +* Purpose: +* Load an Axis. + +* Type: +* Protected function. + +* Synopsis: +* #include "axis.h" +* AstAxis *astLoadAxis( void *mem, size_t size, +* AstAxisVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Axis loader. + +* Description: +* This function is provided to load a new Axis using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Axis structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Axis at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the Axis is to be +* loaded. This must be of sufficient size to accommodate the +* Axis data (sizeof(Axis)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Axis (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Axis structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstAxis) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Axis. If this is NULL, a pointer +* to the (static) virtual function table for the Axis class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Axis" is used instead. + +* Returned Value: +* A pointer to the new Axis. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstAxis *new; /* Pointer to the new Axis */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Axis. In this case the + Axis belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstAxis ); + vtab = &class_vtab; + name = "Axis"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitAxisVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Axis. */ + new = astLoadObject( mem, size, (AstObjectVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Axis" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Label. */ +/* ------ */ +/* Note that string values do not require any additional processing. */ + new->label = astReadString( channel, "label", NULL ); + +/* Symbol. */ +/* ------- */ + new->symbol = astReadString( channel, "symbol", NULL ); + +/* Unit. */ +/* ----- */ + new->unit = astReadString( channel, "unit", NULL ); + +/* Digits. */ +/* ------- */ + new->digits = astReadInt( channel, "digits", -INT_MAX ); + if ( TestAxisDigits( new, status ) ) SetAxisDigits( new, new->digits, status ); + +/* Format. */ +/* ------- */ + new->format = astReadString( channel, "format", NULL ); + +/* Direction. */ +/* ---------- */ + new->direction = astReadInt( channel, "dirn", -INT_MAX ); + if ( TestAxisDirection( new, status ) ) SetAxisDirection( new, new->direction, status ); + +/* Top. */ +/* ---- */ + new->top = astReadDouble( channel, "top", AST__BAD ); + if ( TestAxisTop( new, status ) ) SetAxisTop( new, new->top, status ); + +/* Bottom. */ +/* ---- */ + new->bottom = astReadDouble( channel, "bottom", AST__BAD ); + if ( TestAxisBottom( new, status ) ) SetAxisBottom( new, new->bottom, status ); + +/* If an error occurred, clean up by deleting the new Axis. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Axis pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* External interfaces for the attribute access functions are generated + automatically by the macros that implement the access functions themselves. + Hence, we need only provide external interfaces for a few additional + functions here. */ +const char *astAxisAbbrev_( AstAxis *this, const char *fmt, + const char *str1, const char *str2, int *status ) { + if ( !astOK ) return str2; + return (**astMEMBER(this,Axis,AxisAbbrev))( this, fmt, str1, str2, status ); +} +const char *astAxisFormat_( AstAxis *this, double value, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Axis,AxisFormat))( this, value, status ); +} +double astAxisDistance_( AstAxis *this, double v1, double v2, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Axis,AxisDistance))( this, v1, v2, status ); +} +double astAxisOffset_( AstAxis *this, double v1, double dist, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Axis,AxisOffset))( this, v1, dist, status ); +} +double astAxisCentre_( AstAxis *this, double value, double gap, int *status ) { + if ( !astOK ) return 0.0; + return (**astMEMBER(this,Axis,AxisCentre))( this, value, gap, status ); +} +double astAxisGap_( AstAxis *this, double gap, int *ntick, int *status ) { + if ( !astOK ) return 0.0; + return (**astMEMBER(this,Axis,AxisGap))( this, gap, ntick, status ); +} +void astAxisNorm_( AstAxis *this, double *value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Axis,AxisNorm))( this, value, status ); +} +void astAxisOverlay_( AstAxis *template, AstAxis *result, int *status ) { + if ( !astOK ) return; + (**astMEMBER(template,Axis,AxisOverlay))( template, result, status ); +} +int astAxisUnformat_( AstAxis *this, const char *string, double *value, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Axis,AxisUnformat))( this, string, value, status ); +} +int astAxisFields_( AstAxis *this, const char *fmt, const char *str, + int maxfld, char **fields, int *nc, double *val, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Axis,AxisFields))( this, fmt, str, maxfld, fields, nc, val, status ); +} +int astAxisIn_( AstAxis *this, double lo, double hi, double val, int closed, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Axis,AxisIn))( this, lo, hi, val, closed, status ); +} +const char *astGetAxisNormUnit_( AstAxis *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Axis,GetAxisNormUnit))( this, status ); +} +int astTestAxisNormUnit_( AstAxis *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Axis,TestAxisNormUnit))( this, status ); +} +const char *astGetAxisInternalUnit_( AstAxis *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Axis,GetAxisInternalUnit))( this, status ); +} +int astTestAxisInternalUnit_( AstAxis *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Axis,TestAxisInternalUnit))( this, status ); +} + + + + + + + + + diff --git a/axis.h b/axis.h new file mode 100644 index 0000000..53c7062 --- /dev/null +++ b/axis.h @@ -0,0 +1,621 @@ +#if !defined( AXIS_INCLUDED ) /* Include this file only once */ +#define AXIS_INCLUDED +/* +*+ +* Name: +* axis.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Axis class. + +* Invocation: +* #include "axis.h" + +* Description: +* This include file defines the interface to the Axis class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Axis class implements the basic behaviour of a coordinate +* axis, several of which may be assembled to represent a +* coordinate system. + +* Inheritance: +* The Axis class inherits from the Object class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* Bottom (double) +* Lowest legal value for axis. +* Digits (integer) +* Specifies how many digits of precision are required by +* default when a coordinate value for an Axis is formatted +* (e.g. using the astAxisFormat method). The Digits value acts +* as a default only and is over-ridden if a Format string is +* specified explicitly (using the astSetAxisFormat method). The +* default supplied by the Axis class for the Digits attribute +* itself is 7. +* Direction (integer) +* Specifies how coordinate values for an Axis should be +* displayed. By default, it has the value one, indicating that +* they should be shown in the conventional sense +* (i.e. increasing left to right for an abscissa and bottom to +* top for an ordinate). If set to zero, this attribute +* indicates that the direction should be reversed (as would +* often be done for an astronomical magnitude or a right +* ascension axis, for example). +* Format (string) +* Specifies the format to be used to display coordinate values +* for an Axis (i.e. to convert them from binary to character +* form). The interpretation of this string (e.g. by derived +* classes) is left to the astAxisFormat method, but the Axis +* class interprets this parameter as a C "printf" format string +* which should be capable of formatting a single coordinate +* value stored as a double (e.g. "%1.7G"). If no Format string +* is set, the default supplied by the Axis class is based on +* the value of the Digits attribute. +* Label (string) +* Specifies the label to be attached to an Axis when it is +* represented in (e.g.) a graph. It is intended purely for +* interpretation by human readers and not by software. The +* default supplied by the Axis class is the string "Coordinate +* Axis". +* Symbol (string) +* Specifies the symbol to be used to represent coordinate +* values for an Axis in "short form", such as in algebraic +* expressions where a full description of the Axis would be +* inappropriate. Examples include "RA" and "Dec" (for Right +* Ascension and Declination). The default supplied by the Axis +* class is the string "x". +* Top (double) +* Highest legal value for axis. +* Unit (string) +* Describes the units used to represent coordinate values on an +* Axis. The default supplied by the Axis class is an empty +* string "". + +* Methods Over-Ridden: +* Public: +* None. + +* Protected: +* astSetAttrib +* Set an attribute value for an Axis. + +* New Methods Defined: +* Public: +* astAxisFormat +* Format a coordinate value for an Axis. +* astAxisNorm +* Normalise an Axis coordinate value. +* astAxisUnformat +* Read a formatted coordinate value for an Axis. + +* Protected: +* astAxisAbbrev +* Abbreviate a formatted Axis value by skipping leading fields. +* astAxisDistance +* Find the distance between two axis values. +* astAxisFields +* Identify the fields within a formatted SkyAxis value. +* astAxisCentre +* Find a "nice" central axis value. +* astAxisGap +* Find a "nice" gap for tabulating Axis values. +* astAxisOffset +* Add an increment onto a supplied axis value. +* astAxisOverlay +* Overlay the attributes of a template Axis on to another Axis. +* astClearAxisDigits +* Clear the Digits attribute for an Axis. +* astClearAxisDirection +* Clear the Direction attribute for an Axis. +* astClearAxisFormat +* Clear the Format attribute for an Axis. +* astClearAxisLabel +* Clear the Label attribute for an Axis. +* astClearAxisSymbol +* Clear the Symbol attribute for an Axis. +* astClearAxisUnit +* Clear the Unit attribute for an Axis. +* astGetAxisDigits +* Get the value of the Digits attribute for an Axis. +* astGetAxisDirection +* Get the value of the Direction attribute for an Axis. +* astGetAxisFormat +* Get a pointer to the Format attribute for an Axis. +* astGetAxisLabel +* Get a pointer to the Label attribute for an Axis. +* astGetAxisSymbol +* Get a pointer to the Symbol attribute for an Axis. +* astGetAxisUnit +* Get a pointer to the Unit attribute for an Axis. +* astSetAxisDigits +* Set the value of the Digits attribute for an Axis. +* astSetAxisDirection +* Set the value of the Direction attribute for an Axis. +* astSetAxisFormat +* Set the value of the Format attribute for an Axis. +* astSetAxisLabel +* Set the value of the Label attribute for an Axis. +* astSetAxisSymbol +* Set the value of the Symbol attribute for an Axis. +* astSetAxisUnit +* Set the value of the Unit attribute for an Axis. +* astTestAxisDigits +* Test whether a value has been set for the Digits attribute of an +* Axis. +* astTestAxisDirection +* Test whether a value has been set for the Direction attribute of an +* Axis. +* astTestAxisFormat +* Test whether a value has been set for the Format attribute of an +* Axis. +* astTestAxisLabel +* Test whether a value has been set for the Label attribute of an +* Axis. +* astTestAxisSymbol +* Test whether a value has been set for the Symbol attribute of an +* Axis. +* astTestAxisUnit +* Test whether a value has been set for the Unit attribute of an +* Axis. + +* Other Class Functions: +* Public: +* astAxis +* Create an Axis. +* astIsAAxis +* Test class membership. + +* Protected: +* astCheckAxis +* Validate class membership. +* astInitAxis +* Initialise an Axis. +* astLoadAxis +* Load an Axis. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstAxis +* Axis object type. + +* Protected: +* AstAxisVtab +* Axis virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: B.S. Berry (Starlink) + +* History: +* 1-MAR-1996 (RFWS): +* Original version. +* 25-APR-1996 (RFWS): +* Made all attribute access functions protected. +* 10-SEP-1996 (RFWS): +* Added I/O facilities. +* 11-SEP-1996 (RFWS): +* Added astAxisGap (written by DSB). +* 25-FEB-1998 (RFWS): +* Added astAxisUnformat. +* 29-AUG-2001 (DSB): +* Added AxisDistance and AxisOffset. +* 10-OCT-2002 (DSB): +* Added Top and Bottom. +* 8-JAN-2003 (DSB): +* Added protected astInitAxisVtab method. +* 17-APR-2015 (DSB): +* Added astAxisCentre. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#if defined(astCLASS) /* Protected */ +#include "channel.h" +#endif + + +/* Macros */ +/* ====== */ +#if defined(astCLASS) +#define AST__AXIS_GETDEFAULTFORMAT_BUFF_LEN 50 +#define AST__AXIS_AXISFORMAT_BUFF_LEN 127 +#define AST__AXIS_GETAXISNORMUNIT_BUFF_LEN 127 +#define AST__AXIS_GETATTRIB_BUFF_LEN 50 +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* Axis structure. */ +/* --------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstAxis { + +/* Attributes inherited from the parent class. */ + AstObject object; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + char *label; /* Pointer to label string */ + char *format; /* Pointer to format string */ + char *symbol; /* Pointer to symbol string */ + char *unit; /* Pointer to unit string */ + int digits; /* Default digits of precision */ + int direction; /* Plot in conventional direction? */ + double top; /* Highest legal axis value */ + double bottom; /* Lowest legal axis value */ +} AstAxis; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ + +typedef struct AstAxisVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstObjectVtab object_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + const char *(* AxisAbbrev)( AstAxis *, const char *, const char *, const char *, int * ); + const char *(* AxisFormat)( AstAxis *, double, int * ); + const char *(* GetAxisFormat)( AstAxis *, int * ); + const char *(* GetAxisLabel)( AstAxis *, int * ); + const char *(* GetAxisSymbol)( AstAxis *, int * ); + const char *(* GetAxisUnit)( AstAxis *, int * ); + const char *(* GetAxisInternalUnit)( AstAxis *, int * ); + const char *(* GetAxisNormUnit)( AstAxis *, int * ); + double (* AxisCentre)( AstAxis *, double, double, int * ); + double (* AxisGap)( AstAxis *, double, int *, int * ); + double (* AxisDistance)( AstAxis *, double, double, int * ); + double (* AxisOffset)( AstAxis *, double, double, int * ); + int (* AxisIn)( AstAxis *, double, double, double, int, int * ); + int (* AxisFields)( AstAxis *, const char *, const char *, int, char **, int *, double *, int * ); + int (* AxisUnformat)( AstAxis *, const char *, double *, int * ); + int (* GetAxisDigits)( AstAxis *, int * ); + int (* GetAxisDirection)( AstAxis *, int * ); + int (* TestAxisDigits)( AstAxis *, int * ); + int (* TestAxisDirection)( AstAxis *, int * ); + int (* TestAxisFormat)( AstAxis *, int * ); + int (* TestAxisLabel)( AstAxis *, int * ); + int (* TestAxisSymbol)( AstAxis *, int * ); + int (* TestAxisUnit)( AstAxis *, int * ); + int (* TestAxisInternalUnit)( AstAxis *, int * ); + int (* TestAxisNormUnit)( AstAxis *, int * ); + void (* AxisNorm)( AstAxis *, double *, int * ); + void (* AxisOverlay)( AstAxis *, AstAxis *, int * ); + void (* ClearAxisDigits)( AstAxis *, int * ); + void (* ClearAxisDirection)( AstAxis *, int * ); + void (* ClearAxisFormat)( AstAxis *, int * ); + void (* ClearAxisLabel)( AstAxis *, int * ); + void (* ClearAxisSymbol)( AstAxis *, int * ); + void (* ClearAxisUnit)( AstAxis *, int * ); + void (* SetAxisDigits)( AstAxis *, int, int * ); + void (* SetAxisDirection)( AstAxis *, int, int * ); + void (* SetAxisFormat)( AstAxis *, const char *, int * ); + void (* SetAxisLabel)( AstAxis *, const char *, int * ); + void (* SetAxisSymbol)( AstAxis *, const char *, int * ); + void (* SetAxisUnit)( AstAxis *, const char *, int * ); + + double (* GetAxisTop)( AstAxis *, int * ); + int (* TestAxisTop)( AstAxis *, int * ); + void (* ClearAxisTop)( AstAxis *, int * ); + void (* SetAxisTop)( AstAxis *, double, int * ); + + double (* GetAxisBottom)( AstAxis *, int * ); + int (* TestAxisBottom)( AstAxis *, int * ); + void (* ClearAxisBottom)( AstAxis *, int * ); + void (* SetAxisBottom)( AstAxis *, double, int * ); + +} AstAxisVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstAxisGlobals { + AstAxisVtab Class_Vtab; + int Class_Init; + char GetDefaultFormat_Buff[ AST__AXIS_GETDEFAULTFORMAT_BUFF_LEN + 1 ]; + char AxisFormat_Buff[ AST__AXIS_AXISFORMAT_BUFF_LEN + 1 ]; + char GetAxisNormUnit_Buff[ AST__AXIS_GETAXISNORMUNIT_BUFF_LEN + 1 ]; + char GetAttrib_Buff[ AST__AXIS_GETATTRIB_BUFF_LEN + 1 ]; +} AstAxisGlobals; + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Axis) /* Check class membership */ +astPROTO_ISA(Axis) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstAxis *astAxis_( const char *, int *, ...); +#else +AstAxis *astAxisId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstAxis *astInitAxis_( void *, size_t, int, AstAxisVtab *, const char *, int * ); + +/* Vtab initialiser. */ +void astInitAxisVtab_( AstAxisVtab *, const char *, int * ); + +/* Loader. */ +AstAxis *astLoadAxis_( void *, size_t, AstAxisVtab *, const char *, + AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitAxisGlobals_( AstAxisGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +const char *astAxisFormat_( AstAxis *, double, int * ); +int astAxisUnformat_( AstAxis *, const char *, double *, int * ); +void astAxisNorm_( AstAxis *, double *, int * ); + +#if defined(astCLASS) /* Protected */ +const char *astAxisAbbrev_( AstAxis *, const char *, const char *, const char *, int * ); +const char *astGetAxisFormat_( AstAxis *, int * ); +const char *astGetAxisLabel_( AstAxis *, int * ); +const char *astGetAxisSymbol_( AstAxis *, int * ); +const char *astGetAxisUnit_( AstAxis *, int * ); +const char *astGetAxisNormUnit_( AstAxis *, int * ); +const char *astGetAxisInternalUnit_( AstAxis *, int * ); +double astAxisCentre_( AstAxis *, double, double, int * ); +double astAxisGap_( AstAxis *, double, int *, int * ); +double astAxisDistance_( AstAxis *, double, double, int * ); +double astAxisOffset_( AstAxis *, double, double, int * ); +int astGetAxisDigits_( AstAxis *, int * ); +int astGetAxisDirection_( AstAxis *, int * ); +int astTestAxisDigits_( AstAxis *, int * ); +int astTestAxisDirection_( AstAxis *, int * ); +int astAxisFields_( AstAxis *, const char *, const char *, int, char **, int *, double *, int * ); +int astAxisIn_( AstAxis *, double, double, double, int, int * ); +int astTestAxisFormat_( AstAxis *, int * ); +int astTestAxisLabel_( AstAxis *, int * ); +int astTestAxisSymbol_( AstAxis *, int * ); +int astTestAxisUnit_( AstAxis *, int * ); +int astTestAxisNormUnit_( AstAxis *, int * ); +int astTestAxisInternalUnit_( AstAxis *, int * ); +void astAxisOverlay_( AstAxis *, AstAxis *, int * ); +void astClearAxisDigits_( AstAxis *, int * ); +void astClearAxisDirection_( AstAxis *, int * ); +void astClearAxisFormat_( AstAxis *, int * ); +void astClearAxisLabel_( AstAxis *, int * ); +void astClearAxisSymbol_( AstAxis *, int * ); +void astClearAxisUnit_( AstAxis *, int * ); +void astSetAxisDigits_( AstAxis *, int, int * ); +void astSetAxisDirection_( AstAxis *, int, int * ); +void astSetAxisFormat_( AstAxis *, const char *, int * ); +void astSetAxisLabel_( AstAxis *, const char *, int * ); +void astSetAxisSymbol_( AstAxis *, const char *, int * ); +void astSetAxisUnit_( AstAxis *, const char *, int * ); + +double astGetAxisTop_( AstAxis *, int * ); +int astTestAxisTop_( AstAxis *, int * ); +void astClearAxisTop_( AstAxis *, int * ); +void astSetAxisTop_( AstAxis *, double, int * ); + +double astGetAxisBottom_( AstAxis *, int * ); +int astTestAxisBottom_( AstAxis *, int * ); +void astClearAxisBottom_( AstAxis *, int * ); +void astSetAxisBottom_( AstAxis *, double, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckAxis(this) astINVOKE_CHECK(Axis,this,0) +#define astVerifyAxis(this) astINVOKE_CHECK(Axis,this,1) + +/* Test class membership. */ +#define astIsAAxis(this) astINVOKE_ISA(Axis,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astAxis astINVOKE(F,astAxis_) +#else +#define astAxis astINVOKE(F,astAxisId_) +#endif + +#if defined(astCLASS) /* Protected. */ + +/* Initialiser. */ +#define astInitAxis(mem,size,init,vtab,name) \ +astINVOKE(O,astInitAxis_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitAxisVtab(vtab,name) astINVOKE(V,astInitAxisVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadAxis(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadAxis_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckAxis to validate Axis pointers before + use. This provides a contextual error report if a pointer to the + wrong sort of object is supplied. */ +#define astAxisFormat(this,value) \ +astINVOKE(V,astAxisFormat_(astCheckAxis(this),value,STATUS_PTR)) +#define astAxisNorm(this,value) \ +astINVOKE(V,astAxisNorm_(astCheckAxis(this),value,STATUS_PTR)) +#define astAxisUnformat(this,string,value) \ +astINVOKE(V,astAxisUnformat_(astCheckAxis(this),string,value,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astAxisAbbrev(this,fmt,str1,str2) \ +astINVOKE(V,astAxisAbbrev_(astCheckAxis(this),fmt,str1,str2,STATUS_PTR)) +#define astAxisCentre(this,value,gap) \ +astINVOKE(V,astAxisCentre_(astCheckAxis(this),value,gap,STATUS_PTR)) +#define astAxisGap(this,gap,ntick) \ +astINVOKE(V,astAxisGap_(astCheckAxis(this),gap,ntick,STATUS_PTR)) +#define astAxisFields(this,fmt,str,maxfld,fields,nc,val) \ +astINVOKE(V,astAxisFields_(astCheckAxis(this),fmt,str,maxfld,fields,nc,val,STATUS_PTR)) +#define astAxisIn(this,lo,hi,val,closed) \ +astINVOKE(V,astAxisIn_(astCheckAxis(this),lo,hi,val,closed,STATUS_PTR)) +#define astAxisDistance(this,v1,v2) \ +astINVOKE(V,astAxisDistance_(astCheckAxis(this),v1,v2,STATUS_PTR)) +#define astAxisOffset(this,v1,dist) \ +astINVOKE(V,astAxisOffset_(astCheckAxis(this),v1,dist,STATUS_PTR)) +#define astAxisOverlay(template,result) \ +astINVOKE(V,astAxisOverlay_(astCheckAxis(template),astCheckAxis(result),STATUS_PTR)) +#define astClearAxisDigits(this) \ +astINVOKE(V,astClearAxisDigits_(astCheckAxis(this),STATUS_PTR)) +#define astClearAxisDirection(this) \ +astINVOKE(V,astClearAxisDirection_(astCheckAxis(this),STATUS_PTR)) +#define astClearAxisFormat(this) \ +astINVOKE(V,astClearAxisFormat_(astCheckAxis(this),STATUS_PTR)) +#define astClearAxisLabel(this) \ +astINVOKE(V,astClearAxisLabel_(astCheckAxis(this),STATUS_PTR)) +#define astClearAxisSymbol(this) \ +astINVOKE(V,astClearAxisSymbol_(astCheckAxis(this),STATUS_PTR)) +#define astClearAxisUnit(this) \ +astINVOKE(V,astClearAxisUnit_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisDigits(this) \ +astINVOKE(V,astGetAxisDigits_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisDirection(this) \ +astINVOKE(V,astGetAxisDirection_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisFormat(this) \ +astINVOKE(V,astGetAxisFormat_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisLabel(this) \ +astINVOKE(V,astGetAxisLabel_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisSymbol(this) \ +astINVOKE(V,astGetAxisSymbol_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisUnit(this) \ +astINVOKE(V,astGetAxisUnit_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisNormUnit(this) \ +astINVOKE(V,astGetAxisInternalUnit_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisInternalUnit(this) \ +astINVOKE(V,astGetAxisInternalUnit_(astCheckAxis(this),STATUS_PTR)) +#define astSetAxisDigits(this,digits) \ +astINVOKE(V,astSetAxisDigits_(astCheckAxis(this),digits,STATUS_PTR)) +#define astSetAxisDirection(this,direction) \ +astINVOKE(V,astSetAxisDirection_(astCheckAxis(this),direction,STATUS_PTR)) +#define astSetAxisFormat(this,format) \ +astINVOKE(V,astSetAxisFormat_(astCheckAxis(this),format,STATUS_PTR)) +#define astSetAxisLabel(this,label) \ +astINVOKE(V,astSetAxisLabel_(astCheckAxis(this),label,STATUS_PTR)) +#define astSetAxisSymbol(this,symbol) \ +astINVOKE(V,astSetAxisSymbol_(astCheckAxis(this),symbol,STATUS_PTR)) +#define astSetAxisUnit(this,unit) \ +astINVOKE(V,astSetAxisUnit_(astCheckAxis(this),unit,STATUS_PTR)) +#define astTestAxisDigits(this) \ +astINVOKE(V,astTestAxisDigits_(astCheckAxis(this),STATUS_PTR)) +#define astTestAxisDirection(this) \ +astINVOKE(V,astTestAxisDirection_(astCheckAxis(this),STATUS_PTR)) +#define astTestAxisFormat(this) \ +astINVOKE(V,astTestAxisFormat_(astCheckAxis(this),STATUS_PTR)) +#define astTestAxisLabel(this) \ +astINVOKE(V,astTestAxisLabel_(astCheckAxis(this),STATUS_PTR)) +#define astTestAxisSymbol(this) \ +astINVOKE(V,astTestAxisSymbol_(astCheckAxis(this),STATUS_PTR)) +#define astTestAxisUnit(this) \ +astINVOKE(V,astTestAxisUnit_(astCheckAxis(this),STATUS_PTR)) +#define astTestAxisNormUnit(this) \ +astINVOKE(V,astTestAxisNormUnit_(astCheckAxis(this),STATUS_PTR)) +#define astTestAxisInternalUnit(this) \ +astINVOKE(V,astTestAxisInternalUnit_(astCheckAxis(this),STATUS_PTR)) + +#define astClearAxisTop(this) \ +astINVOKE(V,astClearAxisTop_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisTop(this) \ +astINVOKE(V,astGetAxisTop_(astCheckAxis(this),STATUS_PTR)) +#define astSetAxisTop(this,top) \ +astINVOKE(V,astSetAxisTop_(astCheckAxis(this),top,STATUS_PTR)) +#define astTestAxisTop(this) \ +astINVOKE(V,astTestAxisTop_(astCheckAxis(this),STATUS_PTR)) + +#define astClearAxisBottom(this) \ +astINVOKE(V,astClearAxisBottom_(astCheckAxis(this),STATUS_PTR)) +#define astGetAxisBottom(this) \ +astINVOKE(V,astGetAxisBottom_(astCheckAxis(this),STATUS_PTR)) +#define astSetAxisBottom(this,bottom) \ +astINVOKE(V,astSetAxisBottom_(astCheckAxis(this),bottom,STATUS_PTR)) +#define astTestAxisBottom(this) \ +astINVOKE(V,astTestAxisBottom_(astCheckAxis(this),STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..b1b79e9 --- /dev/null +++ b/bootstrap @@ -0,0 +1,134 @@ +#! /bin/sh - +# original bootstrap file, installed by starconf 1.3, rnum=1003000 +# If you _need_ to change this file, delete `original' in the line above, +# or else starconf may overwrite it with an updated version. +# +# bootstrap.installed. Generated from bootstrap.installed.in by configure. +# +# Bootstrap a checked-out component of the Starlink software tree. +# Run this script in a freshly checked-out directory to bring the +# system to the point where you can just type ./configure;make +# +# Usage: +# ./bootstrap + + +# This script should be installed, by starconf, in all `component +# directories'. A `component directory' is a directory which has a +# component.xml.in file in it. All component directories will have a +# manifest file created and installed in .../manifests; non-component +# directories will not have manifest files. Everything that's +# installed should be installed as part of some component +# or other. +# +# The ./bootstrap scripts will stop recursing when they find a +# component.xml.in file. They'll warn if they find a component.xml.in +# file in any AC_CONFIG_SUBDIRS directory, but ignore it, and exit +# with an error if they do not find a component.xml.in file and there +# are no AC_CONFIG_SUBDIRS directories in which to search further. +# That is, the tree of directories which the top-level bootstrap +# traverses should have component.xml.in files at or above all its +# leaves. + + +# The starconf below might update bootstrap, if a newer version is +# available. Unfortunately, this confuses sh, which appears _not_ to +# keep open the script it's reading, but to reopen it afresh, or reseek +# within the file, for each line (or something like that!?). +# So rewrite this script to a temporary file and exec it. +tempfile="${TMP-/tmp}/$0-$$.tmp" +rm -f $tempfile +echo "trap 'rm -f $tempfile' 0" >$tempfile # remove temporary at exit +sed '1,/^--TRAMPOLINE--/d' $0 >>$tempfile # strip out the trampoline +exec /bin/sh $tempfile # exec the temporary +--TRAMPOLINE-- + + +echo "Bootstrapping `pwd` ..." + +if test ! -f configure.ac; then + echo "bootstrap: No configure.ac in directory `pwd`" >&2 + exit 1 +fi + +subdirs=`autoconf --trace=AC_CONFIG_SUBDIRS:$% configure.ac` + +if test -f component.xml.in; then + + if starconf --show buildsupport >/dev/null 2>&1; then + + # starconf is in the path + echo "...using starconf in " `starconf --show buildsupport` + starconf || exit 1 + + else + + # The temptation here is to use ./starconf.status to find the + # starconf that it came from and invoke that explicitly. Don't do + # this, however: we don't want to be too clever, and it's better + # to be consistent with the way the autotools behave (the first + # one in your path is the one that works, and they don't have this + # sort of `phone home' cleverness in them). + + echo "bootstrap error: The starconf application is not in your path" + + # This doesn't stop us being helpful, however. + if test -f ./starconf.status; then + starconf_home=`./starconf.status --show buildsupport` + echo "This directory was last bootstrapped with $starconf_home/bin/starconf" + fi + + exit 1 + fi + + # Check that there are no component.xml.in files in any subdirectories + if test -n "$subdirs"; then + for d in $subdirs + do + if test -d "$d" && test -f "$d/component.xml.in"; then + echo "bootstrap: warning: ignoring child $d/component.xml.in" >&2 + fi + done + fi + + # If STAR_SUPPRESS_AUTORECONF is true in the environment, then we + # suppress the call of `autoreconf'. This is here _only_ so that + # the top-level bootstrap file can suppress multiple calls of this + # in bootstrap scripts in its children. This mechanism must not + # be used by users, as it is likely to change without warning. + if ${STAR_SUPPRESS_AUTORECONF-false}; then + echo "Suppressing autoreconf in" `pwd` + else + echo autoreconf --install --symlink + autoreconf --install --symlink || exit 1 + fi + +else + + # This is not a component directory, so simply recurse into the children. + + # ...if there are any, that is. + if test -z "$subdirs"; then + echo "bootstrap: error: non-component directory `pwd` has no subdirs" >&2 + exit 1 + fi + + # Bootstrap the child directories mentioned in AC_CONFIG_SUBDIRS. + # These bootstrap files must exist. + for d in $subdirs + do + if test -d "$d"; then + echo "Bootstrapping $d..." + if test -f $d/bootstrap; then + # good... + (cd $d; /bin/sh ./bootstrap) + else + echo "bootstrap: no file $d/bootstrap" >&2 + exit 1 + fi + fi + done + +fi + +exit 0 diff --git a/box.c b/box.c new file mode 100644 index 0000000..99aa780 --- /dev/null +++ b/box.c @@ -0,0 +1,5062 @@ +/* +*class++ +* Name: +* Box + +* Purpose: +* A box region with sides parallel to the axes of a Frame. + +* Constructor Function: +c astBox +f AST_BOX + +* Description: +* The Box class implements a Region which represents a box with sides +* parallel to the axes of a Frame (i.e. an area which encloses a given +* range of values on each axis). A Box is similar to an Interval, the +* only real difference being that the Interval class allows some axis +* limits to be unspecified. Note, a Box will only look like a box if +* the Frame geometry is approximately flat. For instance, a Box centred +* close to a pole in a SkyFrame will look more like a fan than a box +* (the Polygon class can be used to create a box-like region close to a +* pole). + +* Inheritance: +* The Box class inherits from the Region class. + +* Attributes: +* The Box class does not define any new attributes beyond +* those which are applicable to all Regions. + +* Functions: +c The Box class does not define any new functions beyond those +f The Box class does not define any new routines beyond those +* which are applicable to all Regions. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2008-2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-MAR-2004 (DSB): +* Original version. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 5-JUN-2007 (DSB): +* Improve astSimplify algorithm. +* 6-JUN-2007 (DSB): +* Change the iterating algorithm in MakeGrid so that it uses +* pixel index rather than axis value. This is more robust against +* rounding errors. +* 9-OCT-2007 (DSB): +* - Fix bug in RegBaseMesh that could cause incorrect meshes for 2D +* Boxes. +* - In RegBaseMesh, use flat geometry if all axes come from simple +* frames or from 1-dimensional specialist frames. +* 26-MAY-2008 (DSB): +* Fix bug in RegBaseMesh that caused an error to be reported if +* the Box occupies a single point. +* 20-JAN-2009 (DSB): +* Over-ride astRegBasePick. +* 26-JAN-2009 (DSB): +* Over-ride astMapMerge. +* 12-JUL-2009 (DSB): +* Modify Simplify so that if a Box can be split into two +* simpler components and then joined together into a Prism, it +* does so. This is because being able to express a Region in +* its current Frame is more important than having the simplest +* possible structure. +* 28-FEB-2011 (DSB): +* Do not assume the first axis value is good in function BestBox. +* 22-MAR-2011 (DSB): +* Improve uniformity of points within grid produced by astRegBaseGrid. +* 16-JUL-2013 (DSB): +* Use a more robust algorithm for determining the order of the +* vertices whan a Box is simplified to a Polygon. The old method +* sometimes resulted in an unbounded "inside-out" polygon. +* 4-NOV-2013 (DSB): +* Modify RegPins so that it can handle uncertainty regions that straddle +* a discontinuity. Previously, such uncertainty Regions could have a huge +* bounding box resulting in matching region being far too big. +* 10-APR-2014 (DSB): +* More work (in function Cache() ) on handling uncertainty regions that straddle +* a discontinuity. This time ensure that the extent of the box on each axis takes +* account of the possibly cyclic nature of the base Frame. +* 25-4-2016 (DSB): +* Remove the unused box shrinking facility (a hang over from the +* days when the RegBaseGrid function operated by creating multiple +* meshes on the surface of the box, shrinking the box each time). +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Box + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "cmpmap.h" /* Compound Mappings */ +#include "cmpframe.h" /* Compound Frames */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "box.h" /* Interface definition for this class */ +#include "polygon.h" /* Interface definition for this class */ +#include "mapping.h" /* Position mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Axis permutation Mappings */ +#include "interval.h" /* Axis interval regions */ +#include "nullregion.h" /* Empty regions */ +#include "pointlist.h" /* List of points in a Frame */ +#include "prism.h" /* Extruded regions */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static void (* parent_setnegated)( AstRegion *, int, int * ); +static void (* parent_setclosed)( AstRegion *, int, int * ); +static void (* parent_clearnegated)( AstRegion *, int * ); +static void (* parent_clearclosed)( AstRegion *, int * ); +static void (* parent_setunc)( AstRegion *, AstRegion *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static void (* parent_resetcache)( AstRegion *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Box) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Box,Class_Init) +#define class_vtab astGLOBAL(Box,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstBoxVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstBox *astBoxId_( void *, int, const double[], const double[], void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstBox *BestBox( AstFrame *, AstPointSet *, AstRegion *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseGrid( AstRegion *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *MergeBox( AstBox *, AstRegion *, int, int * ); +static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * ); +static double *GeoCorner( AstFrame *, int, double *, double *, double *, int * ); +static double *GeoLengths( AstFrame *, int, double *, double *, double *, int * ); +static double *RegCentre( AstRegion *this, double *, double **, int, int, int * ); +static int GetObjSize( AstObject *, int * ); +static int MakeGrid( int, double **, int, double *, double *, int *, int, int, double, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static void BoxPoints( AstBox *, double *, double *, int *); +static void Cache( AstBox *, int, int * ); +static void ClearClosed( AstRegion *, int * ); +static void ClearNegated( AstRegion *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void RegBaseBox( AstRegion *this, double *, double *, int * ); +static void ResetCache( AstRegion *this, int * ); +static void SetClosed( AstRegion *, int, int * ); +static void SetNegated( AstRegion *, int, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); +static void SetUnc( AstRegion *, AstRegion *, int * ); + +/* Member functions. */ +/* ================= */ + +void BoxPoints( AstBox *this, double *centre, double *corner, int *status) { +/* +*+ +* Name: +* astBoxPoints + +* Purpose: +* Return the defining points of a Box. + +* Type: +* Protected function. + +* Synopsis: +* #include "box.h" +* astBoxPoints( AstBox *this, double *centre, double *corner ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns the axis values at the points defining the +* supplied Box. + +* Parameters: +* this +* Pointer to the Box. +* centre +* A pointer to an array in which to return the centre position of +* the Box, in the base Frame of the encapsulated FrameSet. +* corner +* A pointer to an array in which to return the position of a corner +* of the Box, in the base Frame of the encapsulated FrameSet. + +* Notes: +* - It is assumed that the length of the supplied arrays is at least +* equal to the number of axes in the base frame of the encapsulated +* FrameSet. +*- +*/ + +/* Local Variables: */ + AstPointSet *pset; + double **ptr; + int nc; + int i; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Get a pointer to the PointSet holding the points defining the Box. */ + pset = ((AstRegion *) this)->points; + +/* Get a pointer to the PointSet's data arrays. */ + ptr = astGetPoints( pset ); + +/* See how many axes each point in the PointSet has. */ + nc = astGetNcoord( pset ); + +/* Copy the centre and corner positions form the PointSet into the + supplied arrays. */ + for( i = 0; i < nc; i++ ) { + centre[ i ] = ptr[ i ] [ 0 ]; + corner[ i ] = ptr[ i ] [ 1 ]; + } + +} + +static void ClearClosed( AstRegion *this, int *status ){ +/* +* Name: +* ClearClosed + +* Purpose: +* Clear the Closed attribute of a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void ClearClosed( AstRegion *this, int *status ) + +* Class Membership: +* Box member function (over-rides the protected astClearClosed +* method inherited from the Region class). + +* Description: +* This function clears the Closed attribute of the supplied Region. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int old; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the original attribute value */ + old = astGetClosed( this ); + +/* Invoke the clear method inherited from the parent Region class */ + (*parent_clearclosed)( this, status ); + +/* If the new value is not the same as the old value, inidcatethat we + need to re-calculate the cached information in the Box. */ + if( astGetClosed( this ) != old ) astResetCache( this ); +} + +static void ClearNegated( AstRegion *this, int *status ){ +/* +* Name: +* ClearNegated + +* Purpose: +* Clear the Negated attribute of a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void ClearNegated( AstRegion *this, int *status ) + +* Class Membership: +* Box member function (over-rides the protected astClearNegated +* method inherited from the Region class). + +* Description: +* This function clears the Negated attribute of the supplied Region. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int old; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the original attribute value */ + old = astGetNegated( this ); + +/* Invoke the clear method inherited from the parent Region class */ + (*parent_clearnegated)( this, status ); + +/* If the new value is not the same as the old value, inidcatethat we + need to re-calculate the cached information in the Box. */ + if( astGetNegated( this ) != old ) astResetCache( this ); +} + +static AstBox *BestBox( AstFrame *frm, AstPointSet *mesh, AstRegion *unc, int *status ){ +/* +* Name: +* BestBox + +* Purpose: +* Find the best fitting Box through a given mesh of points. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstBox *BestBox( AstFrame *frm, AstPointSet *mesh, AstRegion *unc, int *status ) + +* Class Membership: +* Box member function + +* Description: +* This function finds the best fitting Box through a given mesh of points. + +* Parameters: +* frm +* Defines the geometry of the axes. +* mesh +* Pointer to a PointSet holding the mesh of points. They are +* assumed to be in the Frame represented by "unc". +* unc +* A Region representing the uncertainty associated with each point +* on the mesh. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the best fitting Region. It will inherit the positional +* uncertainty and Frame represented by "unc". NULL is returned if all +* the supplied positions are bad. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Local Variables: */ + AstBox *result; + double **ptr; + double *axval; + double *blbnd; + double *bubnd; + double *lbnd; + double *p; + double *ubnd; + double eps; + double lb; + double lim2; + double lim; + double liml; + double limu; + double mxl; + double mxu; + double org; + double sxl2; + double sxl; + double sxu2; + double sxu; + double ub; + double p0; + double d; + double dinc; + int ic; + int ip; + int nc; + int np; + int nxl; + int nxu; + int ok; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get no. of points in the mesh, and the number of axis values per point. */ + np = astGetNpoint( mesh ); + nc = astGetNcoord( mesh ); + +/* Get pointers to the axis values. */ + ptr = astGetPoints( mesh ); + +/* Allocate work space. */ + lbnd = astMalloc( sizeof( double )*(size_t) nc ); + ubnd = astMalloc( sizeof( double )*(size_t) nc ); + + blbnd = astMalloc( sizeof( double )*(size_t) nc ); + bubnd = astMalloc( sizeof( double )*(size_t) nc ); + + axval = astMalloc( sizeof( double )*(size_t) np ); + +/* Check pointers can be used safely */ + if( axval ) { + +/* Get the bounding box of the uncertainty region. */ + astGetRegionBounds( unc, lbnd, ubnd ); + +/* We fit the box one axis at a time. */ + ok = 1; + for( ic = 0; ic < nc; ic++ ) { + +/* Find the first good value on this axis. */ + for( ip = 0; ip < np; ip++ ) { + org = ptr[ ic ][ ip ]; + axval[ ip ] = org; + if( org != AST__BAD ) break; + } + +/* Abort if all the axis values werebad. */ + if( ip >= np ) { + ok = 0; + break; + } + +/* Find the upper and lower limits of the supplied mesh on this axis. */ + lb = 0.0; + ub = 0.0; + ip++; + p = ptr[ ic ] + ip; + p0 = org; + d = 0.0; + for( ; ip < np; ip++, p++ ) { + dinc = astAxDistance( frm, ic + 1, p0, *p ); + if( dinc != AST__BAD ) { + d += dinc; + if( d < lb ) lb = d; + if( d > ub ) ub = d; + } + axval[ ip ] = org + d; + p0 = *p; + } + +/* Now convert these relative offsets to actual axis values. */ + lb += org; + ub += org; + +/* Now scan the list of axis values again, looking for values which are + "close to" either lower or upper bound. These will be the points which + are on the faces of the box which are orthogonal to the current axis. Here + "close to" means within 20 times the uncertainty associated with this + axis, or one tenth of the box size (which ever is smaller). We find the + mean and standard deviation of such "close" values, and then do a + single sigma-clipping iteration (at 3-sigma) to get rid of any values + which are on one of the other faces of the box. */ + + lim = 20*( ubnd[ ic ] - lbnd[ ic ] ); + lim2 = 0.1*( ub - lb ); + if( lim2 < lim ) lim = lim2; + + sxl = 0.0; + sxl2 = 0.0; + nxl = 0; + sxu = 0.0; + sxu2 = 0.0; + nxu = 0; + + p = axval; + for( ip = 0; ip < np; ip++, p++ ) { + if( *p != AST__BAD ) { + if( fabs( *p - lb ) <= lim ) { + sxl += *p; + sxl2 += (*p)*(*p); + nxl++; + } else if( fabs( *p - ub ) <= lim ) { + sxu += *p; + sxu2 += (*p)*(*p); + nxu++; + } + } + } + + if( nxl > 0 ) { + mxl = sxl/nxl; + liml = sxl2/nxl - mxl*mxl; + eps = 100*mxl*DBL_EPSILON; + if( liml < eps*eps ) { + liml = eps; + } else { + liml = 3.0*sqrt( liml ); + } + } else { + mxl = lb; + liml = 0.0; + } + + if( nxu > 0 ) { + mxu = sxu/nxu; + limu = sxu2/nxu - mxu*mxu; + eps = 100*mxu*DBL_EPSILON; + if( limu < eps*eps ) { + limu = eps; + } else { + limu = 3.0*sqrt( limu ); + } + + } else { + mxu = ub; + limu = 0.0; + } + + sxl = 0.0; + nxl = 0; + sxu = 0.0; + nxu = 0; + + p = axval; + for( ip = 0; ip < np; ip++, p++ ) { + if( *p != AST__BAD ) { + if( fabs( *p - mxl ) <= liml ) { + sxl += *p; + nxl++; + } else if( fabs( *p - mxu ) <= limu ) { + sxu += *p; + nxu++; + } + } + } + + if( nxl > 0 ) { + mxl = sxl/nxl; + } else { + mxl = lb; + } + + if( nxu > 0 ) { + mxu = sxu/nxu; + } else { + mxu = ub; + } + +/* The resulting mean axis values are the bounds of the required box on + the current axis.*/ + blbnd[ ic ] = mxl; + bubnd[ ic ] = mxu; + } + +/* If possible, create the returned Box. */ + if( ok ) result = astBox( unc, 1, blbnd, bubnd, unc, " ", status ); + } + +/* Free resources */ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + blbnd = astFree( blbnd ); + bubnd = astFree( bubnd ); + axval = astFree( axval ); + +/* Return NULL if anything went wrong. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result.*/ + return result; +} + +static void Cache( AstBox *this, int lohi, int *status ){ +/* +* Name: +* Cache + +* Purpose: +* Calculate intermediate values and cache them in the Box structure. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void Cache( AstRegion *this, int lohi, int *status ) + +* Class Membership: +* Box member function + +* Description: +* This function uses the PointSet stored in the parent Region to calculate +* some intermediate values which are useful in other methods. These +* values are stored within the Box structure. + +* Parameters: +* this +* Pointer to the Box. +* lohi +* Are the lo and hi arrays to be used? +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *frm; + AstPointSet *pset; + AstRegion *unc; + double **ptr; + double *centre; + double *extent; + double *hi; + double *lbnd_unc; + double *geolen; + double *lo; + double *ubnd_unc; + double wid; + int i; + int nc; + +/* Check the global error status. Also return if the currently cached + information is usable. */ + if ( !astOK || !this->stale ) return; + +/* Get the number of base Frame axes. */ + nc = astGetNin( ((AstRegion *)this)->frameset ); + +/* Allocate memory to store the half-width of the box on each axis. */ + extent = (double *) astMalloc( sizeof( double )*(size_t) nc ); + +/* Allocate memory to store the centre of the box on each axis. */ + centre = (double *) astMalloc( sizeof( double )*(size_t) nc ); + +/* Allocate memory to store the high and low bounds. */ + hi = (double *) astMalloc( sizeof( double )*(size_t) nc ); + lo = (double *) astMalloc( sizeof( double )*(size_t) nc ); + +/* Memory to store the uncertainty bounding box */ + lbnd_unc = astMalloc( sizeof( double)*(size_t) nc ); + ubnd_unc = astMalloc( sizeof( double)*(size_t) nc ); + +/* Memory to store the geodesic half-dimensions of the box. */ + geolen = astMalloc( sizeof( double)*(size_t) nc ); + +/* Get pointers to the coordinate data in the parent Region structure. */ + pset = ((AstRegion *) this)->points; + ptr = astGetPoints( pset ); + +/* Check pointers can be used safely. */ + if( ptr ) { + +/* Store the centre and corner axis values. */ + for( i = 0; i < nc; i++ ) { + centre[ i ] = ptr[ i ][ 0 ]; + hi[ i ] = ptr[ i ][ 1 ]; + } + +/* Calculate the geodesic half-dimensions of the box. */ + frm = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + GeoLengths( frm, nc, centre, hi, geolen, status ); + +/* Calculate the half-width and store in the above array. Also store the + lower and upper bounds. */ + for( i = 0; i < nc; i++ ) { + extent[ i ] = fabs( astAxDistance( frm, i + 1, ptr[ i ][ 1 ], + centre[ i ] ) ); + lo[ i ] = centre[ i ] - extent[ i ]; + hi[ i ] = centre[ i ] + extent[ i ]; + } + + frm = astAnnul( frm ); + +/* Store the pointers to these arrays in the Box structure, and indicate + the information is usable. */ + if( astOK ) { + astFree( this->extent ); + astFree( this->centre ); + astFree( this->lo ); + astFree( this->hi ); + astFree( this->geolen ); + this->extent = extent; + this->centre = centre; + this->lo = lo; + this->hi = hi; + this->geolen = geolen; + this->stale = 0; + extent = NULL; + centre = NULL; + lo = NULL; + hi = NULL; + geolen = NULL; + } + +/* If lo and hi values are to be used, ensure they are expanded to at + least the width of an uncertainty box. */ + if( lohi ) { + +/* If we are dealing with an unnegated closed Box or a negated open + Box, ensure that the box does not have zero width on any axis. We do + this by ensuring that the extent on all axes is at least half the + width of the bounding box of the uncertainty Region. */ + if( astGetNegated( this ) != astGetClosed( this ) ) { + +/* Get the bounding box of the uncertainty Region in the base Frame of + the supplied Box. */ + unc = astGetUncFrm( this, AST__BASE ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + +/* Ensure the extents are at least half the width of the uncertainty + bounding box. */ + for ( i = 0; i < nc; i++ ) { + wid = 0.5*( ubnd_unc[ i ] - lbnd_unc[ i ] ); + if( this->extent[ i ] < wid ) { + this->extent[ i ] = wid; + this->lo[ i ] = this->centre[ i ] - wid; + this->hi[ i ] = this->centre[ i ] + wid; + } + } + +/* Free resources. */ + unc = astAnnul( unc ); + } + } + } + +/* Annul the memory allocated above if an error occurred. */ + if( !astOK ) { + extent = astFree( extent ); + centre = astFree( centre ); + lo = astFree( lo ); + hi = astFree( hi ); + } + +/* Free other resources */ + lbnd_unc = astFree( lbnd_unc ); + ubnd_unc = astFree( ubnd_unc ); + +} + +static double *GeoCorner( AstFrame *frm, int nc, double *centre, + double *geolen, double *corner, int *status ){ +/* +* Name: +* GeoCorner + +* Purpose: +* Find the corner position implied by the supplied centre position +* and geodesic box dimensions. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* double *GeoCorner( AstFrame *frm, int nc, double *centre, +* double *geolen, double *corner, int *status ) + +* Class Membership: +* Box member function + +* Description: +* This function returns the corner position that is implied by the +* supplied centre position and geodesic box dimensions. The returned +* corner position is found by offsetting away from the supplied +* centre position along each axis in turn, by the geodesic distance +* specified in "geolen". + +* Parameters: +* frm +* Defines the geometry of the axes. +* nc +* The number of Frame axes. +* centre +* Pointer to an array holding the box centre axis values. +* geolen +* Pointer to an array holding the geodesic distance corresponding +* to each half axis of the box. +* corner +* Pointer to an array in which to store the axis values at the +* returned corner position. If this is NULL a new array is +* allocated and a pointer to it returned as the function value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the array holding the corner axis values. If a non-NULL +* value is supplied for parameter "corner", then the same value will +* be returned as the function value. Otherwise, the returned value +* will be a pointer to a newly allocated array that should be freed +* using astFree when no longer needed. + +*/ + +/* Local Variables: */ + double *p1; + double *p2; + double *p3; + double *pt; + double *result; + double *work1; + double *work2; + double off; + double off0; + int i; + +/* Initialise */ + result = corner; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Ensure we have a results array. */ + if( ! result ) result = astMalloc( sizeof( double )*nc ); + +/* Also allocate two work arrays to hold a single position. */ + work1 = astMalloc( sizeof( double )*nc ); + work2 = astMalloc( sizeof( double )*nc ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Select which array to use as the initial results array so that the + final results end up in the returned array. */ + if( ( nc % 2 ) == 0 ) { + p1 = result; + p2 = work1; + p3 = work2; + } else { + p1 = work2; + p2 = work1; + p3 = result; + } + +/* Initialise the current corner position to be at the centre of the box. */ + for( i = 0; i < nc; i++ ) p1[ i ] = centre[ i ]; + +/* Loop round offsetting along each side of the box. */ + for( i = 0; i < nc; i++ ) { + +/* In the p2 array put the axis values at a point which is offset + slightly along the current axis away from the current "corner" + position (p1). */ + memcpy( p2, p1, sizeof( double )*nc ); + + if( geolen[ i ] != 0.0 ) { + off = 0.0001*fabs( geolen[ i ] ); + } else { + off = 1.0E-6; + } + + off0 = fabs( 1.0E-10*centre[ i ] ); + if( off < off0 ) off = off0; + p2[ i ] += off; + +/* Offset away from the current corner position (p1) towards the position + found above (p2), moving by the geodesic distance supplied for this axis. + Put the resulting axis values in p3. */ + astOffset( frm, p1, p2, geolen[ i ], p3 ); + +/* Swap the p3 and p1 arrays so that the offset position found above (p3) + becomes the starting position (p1) for the next offset. */ + pt = p1; + p1 = p3; + p3 = pt; + } + } + +/* Free resources */ + work1 = astFree( work1 ); + work2 = astFree( work2 ); + +/* Return the result */ + return result; +} + +static double *GeoLengths( AstFrame *frm, int nc, double *centre, + double *corner, double *geolen, int *status ){ +/* +* Name: +* GeoLengths + +* Purpose: +* Find the geodesic dimensions of a box. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* double *GeoLengths( AstFrame *frm, int nc, double *centre, +* double *corner, double *geolen, int *status ) + +* Class Membership: +* Box member function + +* Description: +* This function returns half the geodesic distance along each edge of +* the supplied box. + +* Parameters: +* frm +* Defines the geometry of the axes. +* nc +* The number of Frame axes. +* centre +* Pointer to an array holding rhe box centre axis values. +* corner +* Pointer to an array holding the axis values at the corner +* position. +* geolen +* Pointer to an array in which to return the geodesic distances +* corresponding to each half axis of the box. If this is NULL a +* new array is allocated and a pointer to it returned as the +* function value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the array holding the geodesic half-dimensions of the box. +* If a non-NULL value is supplied for parameter "corner", then the same +* value will be returned as the function value. Otherwise, the +* returned value will be a pointer to a newly allocated array that +* should be freed using astFree when no longer needed. + +*/ + +/* Local Variables: */ + double *result; + double *p1; + double *p2; + int i; + +/* Initialise */ + result = geolen; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Ensure we have a results array. */ + if( ! result ) result = astMalloc( sizeof( double )*nc ); + +/* Also allocate two work arrays to hold a single position. */ + p1 = astMalloc( sizeof( double )*nc ); + p2 = astMalloc( sizeof( double )*nc ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Initialise the coords as the start and end of the line. */ + memcpy( p1, centre, sizeof( double )*nc ); + memcpy( p2, centre, sizeof( double )*nc ); + +/* Loop round finding the geodesic half-length of each side of the box. */ + for( i = 0; i < nc; i++ ) { + +/* The end of the line is the same as the start of the line, except that + it has the corner value for the current axis. */ + p2[ i ] = corner[ i ]; + +/* Find and return the geodesic distance along the line (i.e. from p1 to + p2). */ + result[ i ] = astDistance( frm, p1, p2 ); + +/* The start of the next line wil lbe at the end of the current line. */ + p1[ i ] = corner[ i ]; + } + } + +/* Free resources */ + p1 = astFree( p1 ); + p2 = astFree( p2 ); + +/* Return the result */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Box member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Box, +* in bytes. + +* Parameters: +* this +* Pointer to the Box. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstBox *this; /* Pointer to Box structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the Box structure. */ + this = (AstBox *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astTSizeOf( this->extent ); + result += astTSizeOf( this->centre ); + result += astTSizeOf( this->lo ); + result += astTSizeOf( this->hi ); + result += astTSizeOf( this->geolen ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitBoxVtab_( AstBoxVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitBoxVtab + +* Purpose: +* Initialise a virtual function table for a Box. + +* Type: +* Protected function. + +* Synopsis: +* #include "box.h" +* void astInitBoxVtab( AstBoxVtab *vtab, const char *name ) + +* Class Membership: +* Box vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Box class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsABox) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->BoxPoints = BoxPoints; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_setnegated = region->SetNegated; + region->SetNegated = SetNegated; + + parent_setunc = region->SetUnc; + region->SetUnc = SetUnc; + + parent_setclosed = region->SetClosed; + region->SetClosed = SetClosed; + + parent_clearnegated = region->ClearNegated; + region->ClearNegated = ClearNegated; + + parent_clearclosed = region->ClearClosed; + region->ClearClosed = ClearClosed; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_resetcache = region->ResetCache; + region->ResetCache = ResetCache; + + mapping->MapMerge = MapMerge; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + region->RegBaseGrid = RegBaseGrid; + region->RegBaseMesh = RegBaseMesh; + region->RegBasePick = RegBasePick; + region->RegBaseBox = RegBaseBox; + region->RegPins = RegPins; + region->RegTrace = RegTrace; + region->RegCentre = RegCentre; + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "Box", "Axis intervals" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MakeGrid( int naxes, double **ptr, int ip, double *lbnd, + double *ubnd, int *np_axes, int np_axis, int iaxis, + double axval, int *status ){ +/* +* Name: +* MakeGrid + +* Purpose: +* Create a grid covering the entire volume of a specified box. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* int MakeGrid( int naxes, double **ptr, int ip, double *lbnd, +* double *ubnd, int *np_axes, int np_axis, int iaxis, +* double axval, int *status ) + +* Class Membership: +* Box member function + +* Description: +* This function creates an evenly sampled grid covering a given +* volume of n-D space, putting the coordinates at the sample points +* into a supplied array and returning the number of samples added. +* Optionally, the volume can be assumed to have a constant value on +* a specified axis. + +* Parameters: +* naxes +* The number of axes. +* ptr +* Pointer to an array with "naxes" elements. Each element is a +* pointer to an array in which to store the values for the axis. +* ip +* The index of the first point to be added to the "ptr" arrays. +* lbnd +* Pointer to an array containing the lower axis bounds of the +* volume to be sampled. +* ubnd +* Pointer to an array containing the upper axis bounds of the +* volume to be sampled. +* np_axes +* Pointer to an array with one element for every axis, giving the +* number of samples along each axis (except, optionally, the axis +* specified by "iaxis"). The first sample (sample 0) for each axis +* will be at the lower bound given in "lbnd" and the last sample +* (sample "np_axes[iax]-1") will be at the upper bound given in +* "ubnd". If NULL, then the single scalar avalue given by +* "np_axis" is used for all axes. +* np_axis +* The constant number of samples along every axis (except, optionally, +* the axis specified by "iaxis"). The first sample (sample 0) for each +* axis will be at the lower bound given in "lbnd" and the last sample +* (sample "np_axis-1") will be at the upper bound given in "ubnd". +* The supplied value is only used if "np_axes" is NULL. +* iaxis +* The index of an axis which has constant value in the volume, or +* -1 if all axes are to span the full volume given by lbnd/ubnd. +* The values in "lbnd" and "ubnd" are ignored for this axis and all +* sample positions will have the axis value given by "axval". +* axval +* The constant value for the axis with index "iaxis". Ignored if +* "iaxis" is -1. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of points added to the "ptr" arrays. + +*/ + +/* Local Variables: */ + double *step; /* Pointer to array holding axis step sizes */ + int *maxi; /* Pointer to array of maximum index values */ + int *pi; /* Pointer to array holding current sample indices */ + int i; /* Axis index */ + int ipp; /* Index of next point */ + int nsamp; /* Number of samples along the axis */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Allocate memory to hold the max indices on each axis. */ + maxi = astMalloc( sizeof( int )*(size_t) naxes ); + +/* Allocate memory to hold the indices of the current position.*/ + pi = astMalloc( sizeof( int )*(size_t) naxes ); + +/* Allocate memory to hold the step size for each axis. */ + step = astMalloc( sizeof( double )*(size_t) naxes ); + if( astOK ) { + +/* For every axis, set up the step size, initialise the current position to + the lower bound, and store a modified upper limit which includes some + safety marging to allow for rounding errors. */ + for( i = 0; i < naxes; i++ ) { + nsamp = np_axes ? np_axes[ i ] : np_axis; + step[ i ] = ( ubnd[ i ] - lbnd[ i ] )/( nsamp - 1 ); + pi[ i ] = 0; + maxi[ i ] = nsamp - 1; + } + + if( iaxis >= 0 ) { + maxi[ iaxis ] = 0; + step[ iaxis ] = 0.0; + pi[ iaxis ] = 0; + } + +/* Initialise the index of the next position to store. */ + ipp = ip; + +/* Loop round adding points to the array until the whole volume has been + done. */ + i = 0; + while( i < naxes ) { + +/* Add the current point to the supplied array,and increment the index of + the next point to add. */ + for( i = 0; i < naxes; i++ ) { + if( i == iaxis ) { + ptr[ i ][ ipp ] = axval; + } else { + ptr[ i ][ ipp ] = lbnd[ i ] + pi[ i ]*step[ i ]; + } + } + ipp++; + +/* We now move the current position on to the next sample */ + i = 0; + while( i < naxes ) { + pi[ i ]++; + if( pi[ i ] > maxi[ i ] ) { + pi[ i ] = 0; + i++; + } else { + break; + } + } + } + } else { + ipp = ip; + } + +/* Free resources. */ + maxi = astFree( maxi ); + pi = astFree( pi ); + step = astFree( step ); + +/* Return the result. */ + return astOK ? ( ipp - ip ): 0 ; +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a Box. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* Box method (over-rides the protected astMapMerge method +* inherited from the Region class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated Box in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated Box with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated Box which is to be merged with +* its neighbours. This should be a cloned copy of the Box +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* Box it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated Box resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstBox *oldbox; /* Pointer to supplied Box */ + AstMapping *map; /* Pointer to adjacent Mapping */ + AstMapping *new; /* Simplified or merged Region */ + int i1; /* Index of first Mapping merged */ + int i; /* Loop counter */ + int result; /* Result value to return */ + +/* Initialise. */ + result = -1; + i1 = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Box. */ + oldbox = (AstBox *) this; + +/* First of all, see if the Box can be replaced by a simpler Region, + without reference to the neighbouring Regions in the list. */ +/* =====================================================================*/ + +/* Try to simplify the box. If the pointer value has changed, we assume + some simplification took place. */ + new = astSimplify( oldbox ); + if( new != (AstMapping *) oldbox ) { + +/* Annul the Box pointer in the list and replace it with the new Region + pointer, and indicate that the forward transformation of the returned + Region should be used (not really needed but keeps things clean). */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = new; + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the Box itself could not be simplified, see if it can be merged + with the Regions on either side of it in the list. We can only merge + in parallel. */ +/* =====================================================================*/ + } else if( ! series ){ + new = astAnnul( new ); + +/* Attempt to merge the Box with its lower neighbour (if any). */ + if( where > 0 ) { + i1 = where - 1; + map = ( *map_list )[ where - 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergeBox( oldbox, (AstRegion *) map, 0, + status ); + } + } + +/* If this did not produced a merged Region, attempt to merge the Box with its + upper neighbour (if any). */ + if( !new && where < *nmap - 1 ) { + i1 = where; + map = ( *map_list )[ where + 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergeBox( oldbox, (AstRegion *) map, 1, + status ); + } + } + +/* If succesfull... */ + if( new ){ + +/* Annul the first of the two Mappings, and replace it with the merged + Region. Also clear the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = new; + ( *invert_list )[ i1 ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i1 + 1 ] ); + for ( i = i1 + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + } + + } else { + new = astAnnul( new ); + } + +/* Return the result. */ + return result; +} + +static AstRegion *MergeBox( AstBox *this, AstRegion *reg, int boxfirst, + int *status ) { +/* +* Name: +* MergeBox + +* Purpose: +* Attempt to merge a Box with another Region to form a Region of higher +* dimensionality. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstRegion *MergeBox( AstBox *this, AstRegion *reg, int boxfirst, int *status ) + +* Class Membership: +* Box member function. + +* Description: +* This function attempts to combine the supplied Regions together +* into a Region of higher dimensionality. + +* Parameters: +* this +* Pointer to a Box. +* reg +* Pointer to another Region. +* boxfirst +* If non-zero, then the Box axes are put first in the new Region. +* Otherwise, the other Region's axes are put first. +* status +* Pointer to the inherited status value. + +* Returned Value: +* A pointer to a new region, or NULL if the supplied Regions could +* not be merged. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* Pointer to base Frame for "result" */ + AstFrame *cfrm; /* Pointer to current Frame for "result" */ + AstFrame *frm_reg; /* Pointer to Frame from "reg" */ + AstFrame *frm_this; /* Pointer to Frame from "this" */ + AstMapping *bcmap; /* Base->current Mapping for "result" */ + AstMapping *map_reg; /* Base->current Mapping from "reg" */ + AstMapping *map_this; /* Base->current Mapping from "this" */ + AstMapping *sbunc; /* Simplified uncertainty */ + AstPointSet *pset_new; /* PointSet holding PointList axis values for new */ + AstPointSet *pset_reg; /* PointSet holding PointList axis values for reg */ + AstRegion *bunc; /* Base Frame uncertainty Region */ + AstRegion *new; /* Pointer to new Interval in base Frame */ + AstRegion *result; /* Pointer to returned Interval in current Frame */ + AstRegion *unc_reg; /* Current Frame uncertainty Region from "reg" */ + AstRegion *unc_this; /* Current Frame uncertainty Region from "this" */ + double **ptr_new; /* Pointers to arrays holding new axis values */ + double **ptr_reg; /* Pointers to arrays holding reg axis values */ + double *centre; /* Array to hold box centre axis values */ + double *corner; /* Array to hold box corner axis values */ + double *lbnd; /* Array to hold lower axis bounds */ + double *p; /* Pointer to next input value */ + double *q; /* Pointer to next output value */ + double *ubnd; /* Array to hold upper axis bounds */ + double fac_reg; /* Ratio of used to default MeshSize for "reg" */ + double fac_this; /* Ratio of used to default MeshSize for "this" */ + double temp; /* Temporary storage */ + int i; /* Loop count */ + int j; /* Loop count */ + int msz_reg; /* Original MeshSize for "reg" */ + int msz_reg_set; /* Was MeshSize originally set for "reg"? */ + int msz_this; /* Original MeshSize for "this" */ + int msz_this_set; /* Was MeshSize originally set for "this"? */ + int nax; /* Number of axes in "result" */ + int nax_reg; /* Number of axes in "reg" */ + int nax_this; /* Number of axes in "this" */ + int neg_reg; /* Negated attribute value for other supplied Region */ + int neg_this; /* Negated attribute value for supplied Box */ + int npnt; /* Number of points in PointList */ + int ok; /* Can supplied Regions be merged? */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get the Closed attributes of the two Regions. They must be the same in + each Region if we are to merge the Regions. In addition, in order to + merge, either both Regions must have a defined uncertainty, or neither + Region must have a defined Uncertainty. */ + if( astGetClosed( this ) == astGetClosed( reg ) && + astTestUnc( this ) == astTestUnc( reg ) ) { + +/* Get the Nagated attributes of the two Regions. */ + neg_this = astGetNegated( this ); + neg_reg = astGetNegated( reg ); + +/* Get the number of axes in the two supplied Regions. */ + nax_reg = astGetNaxes( reg ); + nax_this = astGetNaxes( this ); + +/* If the Regions can be combined, get the number of axes the + combination will have. */ + nax = nax_reg + nax_this; + +/* Get the base Frames from the two Region FrameSets, and combine them + into a single CmpFrame that will be used to create any new Region. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + frm_reg = astGetFrame( reg->frameset, AST__BASE ); + + if( boxfirst ) { + bfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + bfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + +/* Indicate we do not yet have a merged Region. */ + new = NULL; + +/* First attempt to merge with another Box. The result will be a Box. Both + Boxes must be un-negated. */ + if( astIsABox( reg ) && !neg_this && !neg_reg ) { + +/* Allocate memory to store the centre and corner of the returned Box. */ + centre = astMalloc( sizeof( double )*(size_t) nax ); + corner = astMalloc( sizeof( double )*(size_t) nax ); + +/* Copy the centres and corners from the supplied Boxes into the above + arrays, in the requested order. */ + if( boxfirst ) { + astBoxPoints( this, centre, corner ); + astBoxPoints( reg, centre + nax_this, corner + nax_this ); + } else { + astBoxPoints( reg, centre, corner ); + astBoxPoints( this, centre + nax_reg, corner + nax_reg ); + } + +/* Create the new Box, initially with no uncertainty. */ + new = (AstRegion *) astBox( bfrm, 0, centre, corner, NULL, "", + status ); + +/* Free resources .*/ + centre = astFree( centre ); + corner = astFree( corner ); + +/* Now attempt to merge with an Interval. The result will be an Interval. + Both Intervals must be un-negated. */ + } else if( astIsAInterval( reg ) && !neg_this && !neg_reg ) { + +/* Allocate memory to store the bounds of the returned Interval. */ + lbnd = astMalloc( sizeof( double )*(size_t) nax ); + ubnd = astMalloc( sizeof( double )*(size_t) nax ); + +/* Copy the centre and corner from the supplied Box into the required part + of the above arrays. */ + if( boxfirst ) { + centre = lbnd; + corner = ubnd; + } else { + centre = lbnd + nax_reg; + corner = ubnd + nax_reg; + } + astBoxPoints( this, centre, corner ); + +/* Convert these centre and corner positions into upper and lower bounds. */ + if( astOK ) { + for( i = 0; i < nax_this; i++ ) { + centre[ i ] = 2*centre[ i ] - corner[ i ]; + if( centre[ i ] > corner[ i ] ) { + temp = centre[ i ]; + centre[ i ] = corner[ i ]; + corner[ i ] = temp; + } + } + } + +/* Get the bounds from the interval and add them into the above arrays. */ + if( boxfirst ) { + astIntervalPoints( reg, lbnd + nax_this, ubnd + nax_this ); + } else { + astIntervalPoints( reg, lbnd, ubnd ); + } + +/* Create the new Interval, initially with no uncertainty. */ + new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "", + status ); + +/* Free resources .*/ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* Now attempt to merge with a NullRegion. The result will be an + Interval. The NullRegion must be negated and the Box must not. */ + } else if( astIsANullRegion( reg ) && !neg_this && neg_reg ) { + +/* Allocate memory to store the bounds of the returned Interval. */ + lbnd = astMalloc( sizeof( double )*(size_t) nax ); + ubnd = astMalloc( sizeof( double )*(size_t) nax ); + +/* Copy the centre and corner from the supplied Box into the required part + of the above arrays. */ + if( boxfirst ) { + centre = lbnd; + corner = ubnd; + } else { + centre = lbnd + nax_reg; + corner = ubnd + nax_reg; + } + astBoxPoints( this, centre, corner ); + +/* Convert these centre and corner positions into upper and lower bounds. */ + if( astOK ) { + for( i = 0; i < nax_this; i++ ) { + centre[ i ] = 2*centre[ i ] - corner[ i ]; + if( centre[ i ] > corner[ i ] ) { + temp = centre[ i ]; + centre[ i ] = corner[ i ]; + corner[ i ] = temp; + } + } + +/* Fill the other axes with bad values to indicate they are unbounded. */ + if( boxfirst ) { + for( i = nax_this; i < nax; i++ ) { + lbnd[ i ] = AST__BAD; + ubnd[ i ] = AST__BAD; + } + } else { + for( i = 0; i < nax_reg; i++ ) { + lbnd[ i ] = AST__BAD; + ubnd[ i ] = AST__BAD; + } + } + +/* Create the new Interval, initially with no uncertainty. */ + new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "", + status ); + } + +/* Free resources .*/ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* Now attempt to merge with a PointList. The result will be a PointList. + Both Regions must be un-negated. */ + } else if( astIsAPointList( reg ) && !neg_this && !neg_reg ) { + +/* We can only do this if the Box has zero width on each axis (i.e. + represents a point). Get the Box centre and corner. */ + centre = astMalloc( sizeof( double )*(size_t) nax_this ); + corner = astMalloc( sizeof( double )*(size_t) nax_this ); + astBoxPoints( this, centre, corner ); + +/* Get the size of the Box's uncertainty region. */ + lbnd = astMalloc( sizeof( double )*(size_t) nax_this ); + ubnd = astMalloc( sizeof( double )*(size_t) nax_this ); + bunc = astGetUncFrm( this, AST__BASE ); + astGetRegionBounds( bunc, lbnd, ubnd ); + +/* Set "ok" to zero if the Box does not have zero width on any axis. Here + "zero width" means a width less than half the uncertainty on the axis. */ + if( astOK ) { + + ok = 1; + for( i = 0; i < nax_this; i++ ) { + if( fabs( centre[ i ] - corner[ i ] ) > + 0.25*fabs( ubnd[ i ] - lbnd[ i ] ) ) { + ok = 0; + break; + } + } + +/* If the Box is a point, we go on to create a new PointList. */ + if( ok ) { + +/* Get a PointSet holding the axis values in the supplied PointList data. + Also get the number of points in the PointSet and pointers to the arrays + holding the axis values. */ + astPointListPoints( reg, &pset_reg ); + npnt = astGetNpoint( pset_reg ); + ptr_reg = astGetPoints( pset_reg ); + +/* Create a new PointSet with room for the same number of points, but + with the extra required axes. Get pointers to its axis arrays. */ + pset_new = astPointSet( npnt, nax, "", status ); + ptr_new = astGetPoints( pset_new ); + +/* Copy the PointList axis values into the new PointSet, and then include + the extra axis values defined by the Box to each point. */ + if( astOK ) { + + for( j = 0; j < nax_reg; j++ ) { + p = ptr_reg[ j ]; + q = ptr_new[ boxfirst ? nax_this + j : j ]; + for( i = 0; i < npnt; i++ ) *(q++) = *(p++); + } + + for( j = 0; j < nax_this; j++ ) { + p = centre + j; + q = ptr_new[ boxfirst ? j : nax_reg + j ]; + for( i = 0; i < npnt; i++ ) *(q++) = *p; + } + +/* Create the new PointList, initially with no uncertainty. */ + new = (AstRegion *) astPointList( bfrm, pset_new, NULL, + "", status ); + } + +/* Free resources .*/ + pset_new = astAnnul( pset_new ); + pset_reg = astAnnul( pset_reg ); + } + } + centre = astFree( centre ); + corner = astFree( corner ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + bunc = astAnnul( bunc ); + + } + +/* If a new Region was created above, propagate remaining attributes of + the supplied Region to it. */ + if( new ) { + astRegOverlay( new, this, 1 ); + +/* The above Prism constructors create the Prism with the correct value + for the Nagated attribute (i.e. zero). Ensure the above call to + astRegOverlay has not changed this. */ + astClearNegated( new ); + +/* If both the supplied Regions have uncertainty, assign the new Region an + uncertainty. */ + if( astTestUnc( this ) && astTestUnc( reg ) ) { + +/* Get the uncertainties from the two supplied Regions. */ + unc_this = astGetUncFrm( this, AST__BASE ); + unc_reg = astGetUncFrm( reg, AST__BASE ); + +/* Combine them into a single Region (a Prism), in the correct order. */ + if( boxfirst ) { + bunc = (AstRegion *) astPrism( unc_this, unc_reg, "", status ); + } else { + bunc = (AstRegion *) astPrism( unc_reg, unc_this, "", status ); + } + +/* Attempt to simplify the Prism. */ + sbunc = astSimplify( bunc ); + +/* Use the simplified Prism as the uncertainty for the returned Region. */ + astSetUnc( new, sbunc ); + +/* Free resources. */ + sbunc = astAnnul( sbunc ); + bunc = astAnnul( bunc ); + unc_reg = astAnnul( unc_reg ); + unc_this = astAnnul( unc_this ); + } + +/* Get the current Frames from the two Region FrameSets, and combine them + into a single CmpFrame. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__CURRENT ); + frm_reg = astGetFrame( reg->frameset, AST__CURRENT ); + + if( boxfirst ) { + cfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + cfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + +/* Get the base -> current Mappings from the two Region FrameSets, and + combine them into a single parallel CmpMap that connects bfrm and cfrm. */ + map_this = astGetMapping( ((AstRegion *) this)->frameset, AST__BASE, + AST__CURRENT ); + map_reg = astGetMapping( reg->frameset, AST__BASE, AST__CURRENT ); + + if( boxfirst ) { + bcmap = (AstMapping *) astCmpMap( map_this, map_reg, 0, "", + status ); + } else { + bcmap = (AstMapping *) astCmpMap( map_reg, map_this, 0, "", + status ); + } + +/* Map the new Region into the new current Frame. */ + result = astMapRegion( new, bcmap, cfrm ); + +/* The filling factor in the returned is the product of the filling + factors for the two supplied Regions. */ + if( astTestFillFactor( reg ) || astTestFillFactor( this ) ) { + astSetFillFactor( result, astGetFillFactor( reg )* + astGetFillFactor( this ) ); + } + +/* If the MeshSize value is set in either supplied Region, set a value + for the returned Region which scales the default value by the + product of the scaling factors for the two supplied Regions. First see + if either MeshSize value is set. */ + msz_this_set = astTestMeshSize( this ); + msz_reg_set = astTestMeshSize( reg ); + if( msz_this_set || msz_reg_set ) { + +/* If so, get the two MeshSize values (one of which may be a default + value), and then clear them so that the default value will be returned + in future. */ + msz_this = astGetMeshSize( this ); + msz_reg = astGetMeshSize( reg ); + astClearMeshSize( this ); + astClearMeshSize( reg ); + +/* Get the ratio of the used MeshSize to the default MeshSize for both + Regions. */ + fac_this = (double)msz_this/(double)astGetMeshSize( this ); + fac_reg = (double)msz_reg/(double)astGetMeshSize( reg ); + +/* The MeshSize of the returned Returned is the default value scaled by + the product of the two ratios found above. */ + astSetMeshSize( result, fac_this*fac_reg*astGetMeshSize( result ) ); + +/* Re-instate the original MeshSize values for the supplied Regions (if + set) */ + if( msz_this_set ) astSetMeshSize( this, msz_this ); + if( msz_reg_set ) astSetMeshSize( reg, msz_reg ); + } + +/* Free remaining resources */ + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + map_this = astAnnul( map_this ); + map_reg = astAnnul( map_reg ); + bcmap = astAnnul( bcmap ); + new = astAnnul( new ); + cfrm = astAnnul( cfrm ); + } + bfrm = astAnnul( bfrm ); + } + +/* If an error has occurred, annul the returned pointer. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* Box member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstBox *this; /* Pointer to Box structure */ + double axcen; /* Central axis value */ + double axlen; /* Half width of box on axis */ + int i; /* Axis index */ + int nc; /* No. of axes in base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Box structure */ + this = (AstBox *) this_region; + +/* Ensure cached information is up to date. */ + Cache( this, 0, status ); + +/* Get the number of base Frame axes in the Region. */ + nc = astGetNin( this_region->frameset ); + +/* The first point is the centre of the box, the second point is the half + size of the box on each axis.*/ + for( i = 0; i < nc; i++ ) { + axcen = this->centre[ i ]; + axlen = this->extent[ i ]; + lbnd[ i ] = axcen - axlen; + ubnd[ i ] = axcen + axlen; + } +} + +static AstPointSet *RegBaseGrid( AstRegion *this, int *status ){ +/* +* Name: +* RegBaseGrid + +* Purpose: +* Return a PointSet containing points spread through the volume of a +* Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstPointSet *RegBaseGrid( AstRegion *this, int *status ) + +* Class Membership: +* Box member function (over-rides the astRegBaseGrid protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a set of points spread +* through the volume of the supplied Box. The points refer to the base +* Frame of the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. If the Region is unbounded, a NULL pointer +* will be returned. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Base Frame in encapsulated FrameSet */ + AstPointSet *result; /* Returned pointer */ + double *lbnd; /* Pointer to array of lower bounds of box */ + double *ubnd; /* Pointer to array of upper bounds of box */ + int meshsize; /* Requested size of grid */ + int naxes; /* No. of axes in base Frame */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return NULL; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created grid, return it. */ + if( this->basegrid ) { + result = astClone( this->basegrid ); + +/* Otherwise, create a new one, but only if the Box is bounded. */ + } else if( astGetBounded( this ) ) { + +/* Get the base Frame in the Region's FrameSet. */ + frm = astGetFrame( this->frameset, AST__BASE ); + +/* Get the number of axes in the base Frame */ + naxes = astGetNaxes( frm ); + +/* Get the bounds of the Region in the base Frame. */ + lbnd = astMalloc( sizeof( double )*(size_t) naxes ); + ubnd = astMalloc( sizeof( double )*(size_t) naxes ); + astRegBaseBox( this, lbnd, ubnd ); + +/* Get the number of points which would be used to create a boundary + mesh. We use the same number to determine the number of points in the + grid. */ + meshsize = astGetMeshSize( this ); + +/* Create the PointSet holding the grid. */ + result = astFrameGrid( frm, meshsize, lbnd, ubnd ); + +/* Save the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK && result ) this->basegrid = astClone( result ); + +/* Free remaining resources. */ + frm = astAnnul( frm ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + } + +/* Annul the result if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result */ + return result; +} + +static AstPointSet *RegBaseMesh( AstRegion *this, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstPointSet *RegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* Box member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the accuracies which were +* supplied when the Region was created. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Local Constants: */ +#define NP_EDGE 50 /* No. of points for determining geodesic + length of each axis. */ + +/* Local Variables: */ + AstFrame *frm; /* Base Frame in encapsulated FrameSet */ + AstFrame *pfrm0; /* Primary Frame defining axis 0 */ + AstFrame *pfrm1; /* Primary Frame defining axis 1 */ + AstPointSet *result; /* Returned pointer */ + const char *class0; /* Pointer to class string from pfrm0 */ + const char *class1; /* Pointer to class string from pfrm1 */ + double **ptr; /* Pointers to data */ + double *ax; /* Pointer to next first axis value */ + double *ay; /* Pointer to next second axis value */ + double *lbnd; /* Pointer to array of lower bounds of box */ + double *ubnd; /* Pointer to array of lower bounds of box */ + double c[ 5 ][ 2 ]; /* Positions of corners for 2D boxes */ + double dx; /* Increment along first axis of 2D box */ + double dy; /* Increment along second axis of 2D box */ + double edge_len[ 4 ]; /* Length of each edge of 2D boundary */ + double lax; /* Previous value on first axis */ + double lay; /* Previous value on second axis */ + double len; /* Length of edge of 2D boundary */ + double p0[ 2 ]; /* Position in 2D Frame */ + double p1[ 2 ]; /* Position in 2D Frame */ + double ppd; /* Points per unit distance */ + double total_len; /* Total length of 2D boundary */ + int flat; /* Assume Frame geometry is flat? */ + int i; /* Point index */ + int iaxis; /* Axis index */ + int iedge; /* Edge index */ + int ip; /* Index of next point */ + int metric; /* Does Frame have a usable metric? */ + int nax0; /* No. of axes in first primary Frame */ + int nax1; /* No. of axes in second primary Frame */ + int naxes; /* No. of axes in base Frame */ + int np; /* No. of points in returned PointSet */ + int np0; /* No. of points per edge */ + int np_axis; /* No. of points per axis in ND box */ + int np_edge[ 4 ]; /* No. of points per edge in 2D box */ + int paxis; /* Axis index in primary Frame */ + int single; /* Does the Box occupy a single point? */ + +/* Initialise */ + result= NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this->basemesh ) { + result = astClone( this->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* Get the base Frame in the Region's FrameSet. */ + frm = astGetFrame( this->frameset, AST__BASE ); + +/* Get the number of axes in the base Frame */ + naxes = astGetNaxes( frm ); + +/* Get the bounds of the Region in the base Frame. */ + lbnd = astMalloc( sizeof( double )*(size_t) naxes ); + ubnd = astMalloc( sizeof( double )*(size_t) naxes ); + astRegBaseBox( this, lbnd, ubnd ); + +/* Get the requested number of points to put on the mesh. */ + np = astGetMeshSize( this ); + +/* See if the box occupies a single point. */ + single = 1; + for( i = 0; i < naxes; i++ ) { + if( ubnd[ i ] > lbnd[ i ] ) { + single = 0; + break; + } + } + +/* If so, we return a PointSet holding a single point. */ + if( single ) { + result = astPointSet( 1, naxes, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + for( i = 0; i < naxes; i++ ) { + ptr[ i ][ 0 ] = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] ); + } + } + +/* Otherwise, first deal with 1-D boxes. */ + } else if( naxes == 1 ) { + +/* The boundary of a 1-D box consists of 2 points - the two extreme values. + Create a PointSet to hold 2 1-D values, and store the extreme values. */ + result = astPointSet( 2, 1, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + ptr[ 0 ][ 0 ] = lbnd[ 0 ]; + ptr[ 0 ][ 1 ] = ubnd[ 0 ]; + } + +/* Now deal with 2-D boxes. */ + } else if( naxes == 2 ){ + +/* Store the coords of each corner for easy access. */ + c[ 0 ][ 0 ] = lbnd[ 0 ]; + c[ 0 ][ 1 ] = lbnd[ 1 ]; + c[ 1 ][ 0 ] = lbnd[ 0 ]; + c[ 1 ][ 1 ] = ubnd[ 1 ]; + c[ 2 ][ 0 ] = ubnd[ 0 ]; + c[ 2 ][ 1 ] = ubnd[ 1 ]; + c[ 3 ][ 0 ] = ubnd[ 0 ]; + c[ 3 ][ 1 ] = lbnd[ 1 ]; + c[ 4 ][ 0 ] = lbnd[ 0 ]; + c[ 4 ][ 1 ] = lbnd[ 1 ]; + +/* See if we can assume that the frame has flat geometry. This is the case + if both axes belong to simple Frames, or are 1D, but may not be the case + if either axis does not belong to a simple Frame that has more than 1 + axis. */ + astPrimaryFrame( frm, 0, &pfrm0, &paxis ); + astPrimaryFrame( frm, 1, &pfrm1, &paxis ); + class0 = astGetClass( pfrm0 ); + class1 = astGetClass( pfrm1 ); + nax0 = astGetNaxes( pfrm0 ); + nax1 = astGetNaxes( pfrm1 ); + if( astOK ) { + flat = ( !strcmp( class0, "Frame" ) || nax0 == 1 ) && + ( !strcmp( class1, "Frame" ) || nax1 == 1 ); + } else { + flat = 0; + } + +/* Our choice of distribution of points depends on whether the axes have the + same units or not. If the axes have the same units, or if they share the + same primary Frame, then we assume that the axes span some space in which + a single "distance" is defined. If not, (e.g. for a Frame representing + frequency in Hz on one axis - typical value 1.0E10, and slit position in + metres on the other axis - typical value 1.0E-2) we assume that "distance" + is defined differently for each axis. The primary frame requirement is + because some classes of Frame (e.g. SkyFrame) have a defined distance, + even though the Units attributes for its axes may differ (since Units + refers to the external representation - e.g. hours and degrees for a + SkyFrame - rather than the internal representation). */ + if( astOK && !strcmp( astGetUnit( frm, 0 ), astGetUnit( frm, 1 ) ) ) { + metric = 1; + } else { + metric = ( pfrm0 == pfrm1 ); + } + +/* If we have a usable metric, distribute the points according to the aspect + ratio of the box (i.e. the shorter box sides gets fewer points). */ + if( metric ){ + +/* Find the approximate geodesic length of each edge of the box. Since + the edges of the box may not correspond to single geodesic (e.g. a + line of constant latitude is not a geodesic), we do this by testing + a set of points along each edge of the box and finding the total of the + geodesic distances between each pair of adjacent points. Initialise the + total length round all edges. */ + total_len = 0.0; + +/* Do each edge in turn.*/ + for( iedge = 0; iedge < 4; iedge++ ) { + +/* Find the increment in x and y between adjacent points along this edge. + We put a point at each end of the edge. Note, one of dx or dy will be + zero since the edges are parallel to the axes. */ + dx = ( c[ iedge + 1 ][ 0 ] - c[ iedge ][ 0 ] )/( NP_EDGE - 1 ); + dy = ( c[ iedge + 1 ][ 1 ] - c[ iedge ][ 1 ] )/( NP_EDGE - 1 ); + +/* Store the coords of the first point. */ + p0[ 0 ] = c[ iedge ][ 0 ]; + p0[ 1 ] = c[ iedge ][ 1 ]; + +/* Initialise the length of this edge and loop round the remaining points. */ + len = 0.0; + for( i = 1; i < NP_EDGE; i++ ) { + +/* Save the position of the previous point. */ + p1[ 0 ] = p0[ 0 ]; + p1[ 1 ] = p0[ 1 ]; + +/* Find the position of the new point. */ + p0[ 0 ] = p1[ 0 ] + dx; + p0[ 1 ] = p1[ 1 ] + dy; + +/* Increment the length of the edge by the geodesic distance from this point + to the previous point. If the Frame is flat, this is simple (given that we + know that one of dx and dy must be zero). */ + if( flat ) { + len += fabs( dx + dy ); + } else { + len += astDistance( frm, p0, p1 ); + } + } + +/* Save the length of this edge, and also form the total length round all + edges. */ + edge_len[ iedge ] = len; + total_len += len; + } + +/* Find the average number of points per unit geodesic distance around the + boundary. */ + if( total_len > 0.0 ) { + ppd = np / total_len; + +/* Use this, with the geodesic length of each edge found above, to find the + number of points to put along each edge of the boundary, and update the + total number of points to use. In the returned boundary we put a point at + the start of each edge but not at the end (since the end of one edge will + be the start of the next edge). */ + np = 0; + for( iedge = 0; iedge < 4; iedge++ ) { + np_edge[ iedge ] = (int) ( edge_len[ iedge ]*ppd ); + if( np_edge[ iedge ] == 0 ) np_edge[ iedge ] = 1; + np += np_edge[ iedge ]; + } + +/* Report error if total length round box is zero. */ + } else if( astOK ) { + astError( AST__INTER, "astRegBaseMesh(%s): Distance around " + "box perimeter is zero (internal AST programming " + "error).", status, astGetClass( this ) ); + } + +/* If the Frame has no usable metric, give an equal number of points + (equal to a quarter of the total) to all 4 sides of the box. */ + } else { + np0 = (int) ( 0.25*np ); + np = 0; + for( iedge = 0; iedge < 4; iedge++ ) { + np_edge[ iedge ] = np0; + np += np_edge[ iedge ]; + } + } + +/* Create a PointSet with enough room and get a pointer to its data arrays. */ + result = astPointSet( np, 2, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Initialise pointers to the first element for each axis in the returned + PointSet. */ + ax = ptr[ 0 ]; + ay = ptr[ 1 ]; + +/* Loop round each edge. */ + for( iedge = 0; iedge < 4; iedge++ ) { + +/* Find the increment in x and y between adjacent points along this edge. */ + dx = ( c[ iedge + 1 ][ 0 ] - c[ iedge ][ 0 ] )/ np_edge[ iedge ]; + dy = ( c[ iedge + 1 ][ 1 ] - c[ iedge ][ 1 ] )/ np_edge[ iedge ]; + +/* Store the first point. */ + lax = c[ iedge ][ 0 ]; + lay = c[ iedge ][ 1 ]; + *(ax++) = lax; + *(ay++) = lay; + +/* Loop round the remaining points, incrementing the pointers at the same + time. */ + for( i = 1; i < np_edge[ iedge ]; i++, ax++, ay++ ) { + +/* Find the position of the new point and store it. */ + lax += dx; + lay += dy; + *ax = lax; + *ay = lay; + } + } + } + +/* Free resources. */ + pfrm0 = astAnnul( pfrm0 ); + pfrm1 = astAnnul( pfrm1 ); + +/* Now deal with boxes with more than 2 dimensions. */ + } else { + +/* Number of samples along each edge of the hyper-cube. */ + np_axis = 1 + (int) pow( np/(2*naxes), 1.0/(naxes-1) ); + if( np_axis < 2 ) np_axis = 2; + +/* Each face of the hyper-cube will have np_axis**(naxes-1) points, and there + are 2*naxes faces. Create a PointSet with the correct number of + points. */ + np = 2*naxes; + for( iaxis = 1; iaxis < naxes; iaxis++ ) np *= np_axis; + result = astPointSet( np, naxes, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Initialise the index of the next point to add into the PointSet. */ + ip = 0; + +/* Create the upper and lower faces for each axis in turn. */ + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + +/* First do the upper face for this axis. */ + ip += MakeGrid( naxes, ptr, ip, lbnd, ubnd, NULL, np_axis, + iaxis, ubnd[ iaxis ], status ); + +/* Now do the lower face for this axis. */ + ip += MakeGrid( naxes, ptr, ip, lbnd, ubnd, NULL, np_axis, + iaxis, lbnd[ iaxis ], status ); + } + +/* Remove any unused space at the end of the PointSet. */ + if( ip < np ) astSetNpoint( result, ip ); + } + } + +/* Same the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK && result ) this->basemesh = astClone( result ); + +/* Free resources. */ + frm = astAnnul( frm ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; + +/* Undefine macros local to this function. */ +#undef NP_EDGE + +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, const int *axes, + int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* Box member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* The base Frame in the supplied Region */ + AstFrame *frm; /* The base Frame in the returned Region */ + AstPointSet *pset; /* Holds axis values defining the supplied Region */ + AstRegion *bunc; /* The uncertainty in the supplied Region */ + AstRegion *result; /* Returned Region */ + AstRegion *unc; /* The uncertainty in the returned Region */ + double **ptr; /* Holds axis values defining the supplied Region */ + double *cen; /* Base Frm axis values at centre of returned Box */ + double *cor; /* Base Frm axis values at a corner of returned Box */ + int i; /* Index of axis within returned Region */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the base Frame of the encapsulated FrameSet. */ + bfrm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Create a Frame by picking the selected axes from the base Frame of the + encapsulated FrameSet. */ + frm = astPickAxes( bfrm, naxes, axes, NULL ); + +/* Get the uncertainty Region (if any) within the base Frame of the supplied + Region, and select the required axes from it. If the resulting Object + is not a Region, annul it so that the returned Region will have no + uncertainty. */ + if( astTestUnc( this_region ) ) { + bunc = astGetUncFrm( this_region, AST__BASE ); + unc = astPickAxes( bunc, naxes, axes, NULL ); + bunc = astAnnul( bunc ); + + if( ! astIsARegion( unc ) ) unc = astAnnul( unc ); + + } else { + unc = NULL; + } + +/* Get pointers to the coordinate data in the parent Region structure. */ + pset = this_region->points; + ptr = astGetPoints( pset ); + +/* Get space to hold the centre and corner of the Box in the new Frame. */ + cen = astMalloc( sizeof( *cen )*naxes ); + cor = astMalloc( sizeof( *cor )*naxes ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the centre and corner axis values for the selected axes into the + arrays allocated above. */ + for( i = 0; i < naxes; i++ ) { + cen[ i ] = ptr[ axes[ i ] ][ 0 ]; + cor[ i ] = ptr[ axes[ i ] ][ 1 ]; + } + +/* Create the new Box. */ + result = (AstRegion *) astBox( frm, 0, cen, cor, unc, "", status ); + } + +/* Free resources */ + frm = astAnnul( frm ); + bfrm = astAnnul( bfrm ); + if( unc ) unc = astAnnul( unc ); + cen = astFree( cen ); + cor = astFree( cor ); + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static double *RegCentre( AstRegion *this_region, double *cen, double **ptr, + int index, int ifrm, int *status ){ +/* +* Name: +* RegCentre + +* Purpose: +* Re-centre a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* double *RegCentre( AstRegion *this, double *cen, double **ptr, +* int index, int ifrm, int *status ) + +* Class Membership: +* Box member function (over-rides the astRegCentre protected +* method inherited from the Region class). + +* Description: +* This function shifts the centre of the supplied Region to a +* specified position, or returns the current centre of the Region. + +* Parameters: +* this +* Pointer to the Region. +* cen +* Pointer to an array of axis values, giving the new centre. +* Supply a NULL value for this in order to use "ptr" and "index" to +* specify the new centre. +* ptr +* Pointer to an array of points, one for each axis in the Region. +* Each pointer locates an array of axis values. This is the format +* returned by the PointSet method astGetPoints. Only used if "cen" +* is NULL. +* index +* The index of the point within the arrays identified by "ptr" at +* which is stored the coords for the new centre position. Only used +* if "cen" is NULL. +* ifrm +* Should be AST__BASE or AST__CURRENT. Indicates whether the centre +* position is supplied and returned in the base or current Frame of +* the FrameSet encapsulated within "this". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If both "cen" and "ptr" are NULL then a pointer to a newly +* allocated dynamic array is returned which contains the centre +* coords of the Region. This array should be freed using astFree when +* no longer needed. If either of "ptr" or "cen" is not NULL, then a +* NULL pointer is returned. + +* Notes: +* - Some Region sub-classes do not have a centre. Such classes will report +* an AST__INTER error code if this method is called with either "ptr" or +* "cen" not NULL. If "ptr" and "cen" are both NULL, then no error is +* reported if this method is invoked on a Region of an unsuitable class, +* but NULL is always returned. + +*/ + +/* Local Variables: */ + AstBox *this; /* Pointer to Box structure */ + AstFrame *frm; /* Pointer to Box's base Frame */ + double **rptr; /* Data pointers for Region PointSet */ + double *bc; /* Base Frame centre position */ + double *corner; /* Array holding corner axis values */ + double *result; /* Returned pointer */ + double *tmp; /* Temporary array pointer */ + double axval; /* Axis value */ + int ic; /* Coordinate index */ + int ncb; /* Number of base frame coordinate values per point */ + int ncc; /* Number of current frame coordinate values per point */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Box structure. */ + this = (AstBox *) this_region; + +/* First ensure cached information (which includes the centre coords) + is up to date. */ + Cache( this, 0, status ); + +/* Get the number of axis values per point in the base and current Frames. */ + ncb = astGetNin( this_region->frameset ); + ncc = astGetNout( this_region->frameset ); + +/* If the centre coords are to be returned, return either a copy of the + base Frame centre coords, or transform the base Frame centre coords + into the current Frame. First ensure cached information (which + includes the centre coords) is up to date. */ + if( !ptr && !cen ) { + if( ifrm == AST__CURRENT ) { + result = astRegTranPoint( this_region, this->centre, 1, 1 ); + } else { + result = astStore( NULL, this->centre, sizeof( double )*ncb ); + } + +/* Otherwise, we store the supplied new centre coords and return a NULL + pointer. */ + } else { + +/* Get a pointer to the axis values stored in the Region structure. */ + rptr = astGetPoints( this_region->points ); + +/* Check pointers can be used safely */ + if( astOK ) { + +/* If the centre position was supplied in the current Frame, find the + corresponding base Frame position... */ + if( ifrm == AST__CURRENT ) { + if( cen ) { + bc = astRegTranPoint( this_region, cen, 1, 0 ); + } else { + tmp = astMalloc( sizeof( double)*(size_t)ncc ); + if( astOK ) { + for( ic = 0; ic < ncc; ic++ ) tmp[ ic ] = ptr[ ic ][ index ]; + } + bc = astRegTranPoint( this_region, tmp, 1, 0 ); + tmp = astFree( tmp ); + } + +/* Replace any bad centre values with their current values. */ + for( ic = 0; ic < ncb; ic++ ) { + if( bc[ ic ] == AST__BAD ) bc[ ic ] = this->centre[ ic ]; + } + +/* If the centre position was supplied in the base Frame, store the + centre coords in this->centre, skipping bad values. */ + } else { + bc = this->centre; + for( ic = 0; ic < ncb; ic++ ) { + axval = cen ? cen[ ic ] : ptr[ ic ][ index ]; + if( axval != AST__BAD ) bc[ ic ] = axval; + } + } + +/* Find the coordinates at the new box corner. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + corner = GeoCorner( frm, ncb, bc, this->geolen, NULL, status ); + frm = astAnnul( frm ); + +/* ... and change the coords in the parent Region structure. */ + for( ic = 0; ic < ncb; ic++ ) { + rptr[ ic ][ 0 ] = bc[ ic ]; + rptr[ ic ][ 1 ] = corner[ ic ]; + } + +/* Free resources */ + if( ifrm == AST__CURRENT ) bc = astFree( bc ); + corner = astFree( corner ); + +/* Indicate the cached info in the Box structure is out of date. */ + astResetCache( this ); + +/* Any base Frame mesh or grid is now no good, so annul it. */ + if( this_region->basemesh ) this_region->basemesh = astAnnul( this_region->basemesh ); + if( this_region->basegrid ) this_region->basegrid = astAnnul( this_region->basegrid ); + + } + } + + +/* Return the result. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Box. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* Box member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Box. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Box "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Box. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstBox *large_box; /* Box slightly larger than "this" */ + AstBox *small_box; /* Box slightly smaller than "this" */ + AstBox *this; /* Pointer to the Box structure. */ + AstFrame *frm; /* Base Frame in supplied Box */ + AstPointSet *ps1; /* Points masked by larger Box */ + AstPointSet *ps2; /* Points masked by larger and smaller Boxes */ + AstRegion *tunc; /* Uncertainity Region from "this" */ + double **ptr; /* Pointer to axis values in "ps2" */ + double *large; /* A corner position in the larger Box */ + double *safe; /* An interior point in "this" */ + double *lbnd_tunc; /* Lower bounds of "this" uncertainty Region */ + double *lbnd_unc; /* Lower bounds of supplied uncertainty Region */ + double *p; /* Pointer to next axis value */ + double *small; /* A corner position in the smaller Box */ + double *ubnd_tunc; /* Upper bounds of "this" uncertainty Region */ + double *ubnd_unc; /* Upper bounds of supplied uncertainty Region */ + double *wid; /* Widths of "this" border */ + int i; /* Axis index */ + int j; /* Point index */ + int nc; /* No. of axes in Box base frame */ + int np; /* No. of supplied points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the Box structure. */ + this = (AstBox *) this_region; + +/* Ensure cached information is up to date. */ + Cache( this, 0, status ); + +/* Get the number of base Frame axes in the Box, and check the supplied + PointSet has the same number of axis values per point. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + nc = astGetNaxes( frm ); + if( astGetNcoord( pset ) != nc && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axis " + "values per point (%d) in the supplied PointSet - should be " + "%d (internal AST programming error).", status, astGetClass( this ), + astGetNcoord( pset ), nc ); + } + +/* Get the number of axes in the uncertainty Region and check it is the + same as above. */ + if( unc && astGetNaxes( unc ) != nc && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axes (%d) " + "in the supplied uncertainty Region - should be " + "%d (internal AST programming error).", status, astGetClass( this ), + astGetNaxes( unc ), nc ); + } + +/* Get the centre of the region in the base Frame. We use this as a "safe" + interior point within the region. */ + safe = astRegCentre( this, NULL, NULL, 0, AST__BASE ); + +/* We now find the maximum distance on each axis that a point can be from the + boundary of the Box for it still to be considered to be on the boundary. + First get the Region which defines the uncertainty within the Box being + checked (in its base Frame), re-centre it on the interior point found + above (to avoid problems if the uncertainty region straddles a + discontinuity), and get its bounding box. */ + tunc = astGetUncFrm( this, AST__BASE ); + if( safe ) astRegCentre( tunc, safe, NULL, 0, AST__CURRENT ); + lbnd_tunc = astMalloc( sizeof( double )*(size_t) nc ); + ubnd_tunc = astMalloc( sizeof( double )*(size_t) nc ); + astGetRegionBounds( tunc, lbnd_tunc, ubnd_tunc ); + +/* Also get the Region which defines the uncertainty of the supplied + points and get its bounding box. First re-centre the uncertainty at the + interior position to avoid problems from uncertainties that straddle a + discontinuity. */ + if( unc ) { + if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT ); + lbnd_unc = astMalloc( sizeof( double )*(size_t) nc ); + ubnd_unc = astMalloc( sizeof( double )*(size_t) nc ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + } else { + lbnd_unc = NULL; + ubnd_unc = NULL; + } + +/* The required border width for each axis is half of the total width + of the two bounding boxes. Use a zero sized box "unc" if no box was + supplied. */ + wid = astMalloc( sizeof( double )*(size_t) nc ); + large = astMalloc( sizeof( double )*(size_t) nc ); + small = astMalloc( sizeof( double )*(size_t) nc ); + if( astOK ) { + if( unc ) { + for( i = 0; i < nc; i++ ) { + wid[ i ] = 0.5*( fabs( astAxDistance( frm, i + 1, lbnd_tunc[ i ], ubnd_tunc[ i ] ) ) + + fabs( astAxDistance( frm, i + 1, lbnd_unc[ i ], ubnd_unc[ i ] ) ) ); + } + } else { + for( i = 0; i < nc; i++ ) { + wid[ i ] = fabs( 0.5*astAxDistance( frm, i + 1, lbnd_tunc[ i ], ubnd_tunc[ i ] ) ); + } + } + +/* Create two new Boxes, one of which is larger than "this" by the widths + found above, and the other of which is smaller than "this" by the widths + found above. */ + for( i = 0; i < nc; i++ ) { + large[ i ] = this->centre[ i ] + this->extent[ i ] + wid[ i ]; + small[ i ] = this->extent[ i ] - wid[ i ]; + if( small[ i ] < 0.0 ) small[ i ] = 0.0; + small[ i ] += this->centre[ i ]; + } + + large_box = astBox( frm, 0, this->centre, large, NULL, "", status ); + small_box = astBox( frm, 0, this->centre, small, NULL, "", status ); + +/* Negate the smaller region.*/ + astNegate( small_box ); + +/* Points are on the boundary of "this" if they are inside both the large + box and the negated smallbox. First transform the supplied PointSet + using the large box, then transform them using the negated smaller Box. */ + ps1 = astTransform( large_box, pset, 1, NULL ); + ps2 = astTransform( small_box, ps1, 1, NULL ); + +/* Get a point to the resulting axis values, and the number of axis + values per axis. */ + ptr = astGetPoints( ps2 ); + np = astGetNpoint( ps2 ); + +/* If a mask array is to be returned, create one. */ + if( mask ) { + *mask = astMalloc( sizeof(int)*(size_t) np ); + +/* Check all the resulting points, setting mask values for all of them. */ + if( astOK ) { + +/* Initialise the mask elements on the basis of the first axis values */ + result = 1; + p = ptr[ 0 ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } else { + (*mask)[ j ] = 1; + } + } + +/* Now check for bad values on other axes. */ + for( i = 1; i < nc; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } + } + } + } + +/* If no output mask is to be made, we can break out of the check as soon + as the first bad value is found. */ + } else if( astOK ) { + result = 1; + for( i = 0; i < nc && result; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + break; + } + } + } + } + +/* Free resources. */ + large_box = astAnnul( large_box ); + small_box = astAnnul( small_box ); + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + } + + tunc = astAnnul( tunc ); + frm = astAnnul( frm ); + lbnd_tunc = astFree( lbnd_tunc ); + ubnd_tunc = astFree( ubnd_tunc ); + if( unc ) lbnd_unc = astFree( lbnd_unc ); + if( unc ) ubnd_unc = astFree( ubnd_unc ); + wid = astFree( wid ); + large = astFree( large ); + small = astFree( small ); + safe = astFree( safe ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr, + int *status ){ +/* +*+ +* Name: +* RegTrace + +* Purpose: +* Return requested positions on the boundary of a 2D Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* int astTraceRegion( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* Box member function (overrides the astTraceRegion method +* inherited from the parent Region class). + +* Description: +* This function returns positions on the boundary of the supplied +* Region, if possible. The required positions are indicated by a +* supplied list of scalar parameter values in the range zero to one. +* Zero corresponds to some arbitrary starting point on the boundary, +* and one corresponds to the end (which for a closed region will be +* the same place as the start). + +* Parameters: +* this +* Pointer to the Region. +* n +* The number of positions to return. If this is zero, the function +* returns without action (but the returned function value still +* indicates if the method is supported or not). +* dist +* Pointer to an array of "n" scalar parameter values in the range +* 0 to 1.0. +* ptr +* A pointer to an array of pointers. The number of elements in +* this array should equal tthe number of axes in the Frame spanned +* by the Region. Each element of the array should be a pointer to +* an array of "n" doubles, in which to return the "n" values for +* the corresponding axis. The contents of the arrays are unchanged +* if the supplied Region belongs to a class that does not +* implement this method. + +* Returned Value: +* Non-zero if the astTraceRegion method is implemented by the class +* of Region supplied, and zero if not. + +*- +*/ + +/* Local Variables; */ + AstMapping *map; + AstPointSet *bpset; + AstPointSet *cpset; + double **bptr; + double d; + double lbnd[ 2 ]; + double ubnd[ 2 ]; + int i; + int ncur; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( ! astOK ) return result; + +/* Check it is 2-dimensional. */ + if( astGetNin( this_region->frameset ) == 2 ) result = 1; + +/* Check we have some points to find. */ + if( result && n > 0 ) { + +/* We first determine the required positions in the base Frame of the + Region, and then transform them into the current Frame. Get the + base->current Mapping, and the number of current Frame axes. */ + map = astGetMapping( this_region->frameset, AST__BASE, AST__CURRENT ); + +/* If it's a UnitMap we do not need to do the transformation, so put the + base Frame positions directly into the supplied arrays. */ + if( astIsAUnitMap( map ) ) { + bpset = NULL; + bptr = ptr; + ncur = 2; + +/* Otherwise, create a PointSet to hold the base Frame positions. */ + } else { + bpset = astPointSet( n, 2, " ", status ); + bptr = astGetPoints( bpset ); + ncur = astGetNout( map ); + } + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Get the bounds of the Region in the base Frame. */ + astRegBaseBox( this_region, lbnd, ubnd ); + +/* Loop round each point. Each edge of the box covers a parameteric + distance of 0.25, regardless of the aspect ratio of the box. */ + for( i = 0; i < n; i++ ) { + +/* The right hand edge starts at 0.75 (parameter increases top to bottom). */ + d = 4*dist[ i ] - 3; + if( d > 0 ) { + bptr[ 0 ][ i ] = ubnd[ 0 ]; + bptr[ 1 ][ i ] = ( 1.0 - d )*ubnd[ 1 ] + d*lbnd[ 1 ]; + +/* The top edge starts at 0.5 (parameter increases left to right). */ + } else { + d += 1.0; + if( d > 0 ) { + bptr[ 0 ][ i ] = ( 1.0 - d )*lbnd[ 0 ] + d*ubnd[ 0 ]; + bptr[ 1 ][ i ] = ubnd[ 1 ]; + +/* The left hand edge starts at 0.25 (parameter increases bottom to top). */ + } else { + d += 1.0; + if( d > 0 ) { + bptr[ 0 ][ i ] = lbnd[ 0 ]; + bptr[ 1 ][ i ] = ( 1.0 - d )*lbnd[ 1 ] + d*ubnd[ 1 ]; + +/* The bottom edge starts at 0.0 (parameter increases right to left). */ + } else { + d += 1.0; + bptr[ 0 ][ i ] = ( 1.0 - d )*ubnd[ 0 ] + d*lbnd[ 0 ]; + bptr[ 1 ][ i ] = lbnd[ 1 ]; + } + } + } + } + } + +/* If required, transform the base frame positions into the current + Frame, storing them in the supplied array. Then free resources. */ + if( bpset ) { + cpset = astPointSet( n, ncur, " ", status ); + astSetPoints( cpset, ptr ); + + (void) astTransform( map, bpset, 1, cpset ); + + cpset = astAnnul( cpset ); + bpset = astAnnul( bpset ); + } + +/* Free remaining resources. */ + map = astAnnul( map ); + } + +/* Return the result. */ + return result; +} + +static void ResetCache( AstRegion *this, int *status ){ +/* +* Name: +* ResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void ResetCache( AstRegion *this, int *status ) + +* Class Membership: +* Region member function (overrides the astResetCache method +* inherited from the parent Region class). + +* Description: +* This function clears cached information from the supplied Region +* structure. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + if( this ) { + ((AstBox *) this )->stale = 1; + (*parent_resetcache)( this, status ); + } +} + +static void SetClosed( AstRegion *this, int value, int *status ){ +/* +* Name: +* SetClosed + +* Purpose: +* Set a value for the Closed attribute of a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void SetClosed( AstRegion *this, int value, int *status ) + +* Class Membership: +* Box member function (over-rides the protected astSetClosed +* method inherited from the Region class). + +* Description: +* This function sets a new value for the Closed attribute of a Region. + +* Parameters: +* this +* Pointer to the Region. +* value +* The new attribute value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int old; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the original attribute value */ + old = astGetClosed( this ); + +/* Invoke the set method inherited from the parent Region class */ + (*parent_setclosed)( this, value, status ); + +/* If the new value is not the same as the old value, indicate that we + need to re-calculate the cached information in the Box. */ + if( value != old ) astResetCache( this ); +} + +static void SetNegated( AstRegion *this, int value, int *status ){ +/* +* Name: +* SetNegated + +* Purpose: +* Set a value for the Negated attribute of a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void SetNegated( AstRegion *this, int value, int *status ) + +* Class Membership: +* Box member function (over-rides the protected astSetNegated +* method inherited from the Region class). + +* Description: +* This function sets a new value for the Negated attribute of a Region. + +* Parameters: +* this +* Pointer to the Region. +* value +* The new attribute value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int old; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the original attribute value */ + old = astGetNegated( this ); + +/* Invoke the set method inherited from the parent Region class */ + (*parent_setnegated)( this, value, status ); + +/* If the new value is not the same as the old value, indicate that we + need to re-calculate the cached information in the Box. */ + if( value != old ) astResetCache( this ); +} + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* Box method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* Indicate that we need to re-calculate the cached information in the Box. */ + astResetCache( this_region ); +} + +static void SetUnc( AstRegion *this, AstRegion *unc, int *status ){ +/* +* Name: +* SetUnc + +* Purpose: +* Store uncertainty information in a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* void SetUnc( AstRegion *this, AstRegion *unc, int *status ) + +* Class Membership: +* Box method (over-rides the astSetUnc method inherited from the +* Region class). + +* Description: +* Each Region (of any class) can have an "uncertainty" which specifies +* the uncertainties associated with the boundary of the Region. This +* information is supplied in the form of a second Region. The uncertainty +* in any point on the boundary of a Region is found by shifting the +* associated "uncertainty" Region so that it is centred at the boundary +* point being considered. The area covered by the shifted uncertainty +* Region then represents the uncertainty in the boundary position. +* The uncertainty is assumed to be the same for all points. +* +* The uncertainty is usually specified when the Region is created, but +* this function allows it to be changed at any time. + +* Parameters: +* this +* Pointer to the Region which is to be assigned a new uncertainty. +* unc +* Pointer to the new uncertainty Region. This must be either a Box, +* a Circle or an Ellipse. A deep copy of the supplied Region will be +* taken, so subsequent changes to the uncertainty Region using the +* supplied pointer will have no effect on the Region "this". +* status +* Pointer to the inherited status variable. +*/ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Invoke the astSetUnc method inherited from the parent Region class. */ + (*parent_setunc)( this, unc, status ); + +/* Indicate that we need to re-calculate the cached information in the Box. */ + astResetCache( this ); +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Box method (over-rides the astSimplify method inherited +* from the Region class). + +* Description: +* This function invokes the parent Region Simplify method, and then +* performs any further region-specific simplification. +* +* If the Mapping from base to current Frame is not a UnitMap, this +* will include attempting to fit a new Region to the boundary defined +* in the current Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstBox *box; /* Pointer to Box structure */ + AstBox *newbox; /* Pointer to simpler Box */ + AstFrame *frm; /* Pointer to current Frame */ + AstMapping *map; /* Base -> current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstPointSet *basemesh; /* Mesh of base Frame positions */ + AstPointSet *mesh; /* Mesh of current Frame positions */ + AstPointSet *ps1; /* Box corners in base Frame */ + AstPointSet *ps2; /* Box corners in current Frame */ + AstPolygon *newpoly; /* New Polygon to replace Box */ + AstRegion *prism; /* Prism combining all axes */ + AstRegion *new; /* Pointer to simplified Region */ + AstRegion *this; /* Pointer to supplied Region structure */ + AstRegion *unc; /* Pointer to uncertainty Region */ + double **ptr1; /* Pointers to axis values in ps1 */ + double **ptr2; /* Pointers to axis values in ps2 */ + double *constants; /* Axis constants array */ + double *lbnd; /* Lower bounds for new Box */ + double *ubnd; /* Upper bounds for new Box */ + double corners[8]; /* Box corners in current Frame */ + double k; /* Axis constant value */ + double lb; /* Lower axis bound */ + double ub; /* Upper axis bound */ + int *inperm; /* Input axis permutation array */ + int *outperm; /* Output axis permutation array */ + int closed; /* Was original Region closed? */ + int feed; /* Source of value for current axis */ + int right_handed; /* Is the new Frame right handed? */ + int ic; /* Axis index */ + int isInterval; /* Is the simplified Box an Interval */ + int isNull; /* Is the simplified Box a NullRegion? */ + int neg; /* Was original Region negated? */ + int nin; /* No. of base Frame axes (Mapping inputs) */ + int nout; /* No. of current Frame axes (Mapping outputs) */ + int simpler; /* Has some simplication taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_mapping; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. */ + new = (AstRegion *) (*parent_simplify)( this_mapping, status ); + +/* Note if any simplification took place. This is assumed to be the case + if the pointer returned by the above call is different to the supplied + pointer. */ + simpler = ( new != this ); + +/* Get the Mapping from base to current Frame. */ + map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT ); + +/* Get the number of inputs and outputs for the PermMap */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* If the Mapping from base to current Frame is a PermMap, we now explicitly + how to swap the axes of the Box to produce either a new Box or an + Interval. */ + if( astIsAPermMap( map ) ){ + +/* See if the new Box is Negated and/or Closed.*/ + neg = astGetNegated( new ); + closed = astGetClosed( new ); + +/* Get a pointer to the Box structure. */ + newbox = (AstBox *) new; + +/* Ensure cached information is up to date. */ + Cache( newbox, 0, status ); + +/* Get the input and output permutation arrays and the array of constants + from the PermMap. */ + inperm = astGetInPerm( map ); + outperm = astGetOutPerm( map ); + constants = astGetConstants( map ); + +/* Allocate memory to hold the axis bounds for the box in the current + Frame. */ + lbnd = astMalloc( sizeof( double )*(size_t) nout ); + ubnd = astMalloc( sizeof( double )*(size_t) nout ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Set flags indicating that the Box can be simplified, and does not result + in a NullRegion or an Interval. */ + simpler = 1; + isNull = 0; + isInterval = 0; + +/* Check each output (which corresponds to an axis in the current Frame + of the Box). */ + for( ic = 0; ic < nout; ic++ ) { + +/* Find the input (a base Frame axis) which feeds this output when the + forward transformation of the PermMap is used. */ + feed = outperm[ ic ]; + +/* If this output is fed a constant (i.e. does not depend on any of the + inputs), then the output axis limits are equal to this constant. */ + if( feed < 0 ) { + lbnd[ ic ] = constants[ (-feed) - 1 ]; + ubnd[ ic ] = constants[ (-feed) - 1 ]; + +/* If this output is fed the constant AST__BAD (i.e. does not depend on any of + the inputs), then the output axis is unbounded and we will consequently + create an Interval rather than a Box. */ + } else if( feed >= nin ) { + lbnd[ ic ] = AST__BAD; + ubnd[ ic ] = AST__BAD; + +/* If this output is fed the value of an input, then the output limits + are equal to the corresponding input limits. */ + } else { + lbnd[ ic ] = newbox->centre[ feed ] - newbox->extent[ feed ]; + ubnd[ ic ] = newbox->centre[ feed ] + newbox->extent[ feed ]; + } + +/* If either bound is missing we will produce an Interval rather than a + Box. */ + if( lbnd[ ic ] == AST__BAD || ubnd[ ic ] == AST__BAD ) { + isInterval = 1; + } + } + +/* If any base Frame axes are not present in the current Frame, we need + to check that the PermMap selects a slice which intersects the base + Frame box. If the slice does not intersect the base Frame box, then + the resulting Region willbe NullRegion. To do this, we check each + element in the input axis permutation array, "inperm". */ + for( ic = 0; ic < nin; ic++ ) { + +/* Find the output (a current Frame axis) which feeds this input when the + inverse transformation of the PermMap is used. */ + feed = inperm[ ic ]; + +/* If this input is fed a constant (i.e. does not depend on any of the + outputs), then we must check that this constant is within the range of + axis values covered by the Box. If not then the simplified Box represents + a slice through the coordinate system which does not pass through the + original Box. In this case the simplified Region is a NullRegion + instead of a Box. */ + if( feed < 0 ) { + k = constants[ (-feed) - 1 ]; + lb = newbox->centre[ ic ] - newbox->extent[ ic ]; + ub = newbox->centre[ ic ] + newbox->extent[ ic ]; + + if( closed == neg ) { + isNull = ( k <= lb || k >= ub ); + } else { + isNull = ( k < lb || k > ub ); + } + +/* If this input is fed the constant AST__BAD (i.e. does not depend on any of + the outputs), then the input axis is unbounded and will consequently + extend beyond the Box. In this case the simplified Region will be a + NullRegion. */ + } else if( feed >= nout ) { + isNull = 1; + +/* If this input is fed the value of an output, then check that the + relationship is bi-directional. If it is not, we cannot simplify the + Box. */ + } else if( outperm[ feed ] != ic ) { + simpler = 0; + break; + } + } + +/* If the Box can be simplified, create a new Region of an appropriate + class. */ + if( simpler ) { + +/* Get any uncertainty from the supplied Box (it will already have been + simplified by the parent Simplify method). */ + unc = astTestUnc( new ) ? astGetUncFrm( new, AST__CURRENT ) : NULL; + +/* Get the Frame represented by the Region. */ + frm = astGetFrame( new->frameset, AST__CURRENT ); + +/* We can now replace the original Region with the simplified Region so annul + the original pointer. */ + new = astAnnul( new ); + +/* Create a new Region of the required class. */ + if( isNull ) { + new = (AstRegion *) astNullRegion( frm, unc, "", status ); + + } else if( isInterval ){ + new = (AstRegion *) astInterval( frm, lbnd, ubnd, unc, "", status ); + + } else { + new = (AstRegion *) astBox( frm, 1, lbnd, ubnd, unc, "", status ); + } + +/* If the original box was Negated. Negate the new one. */ + if( neg ) astNegate( new ); + +/* Free resources */ + if( unc ) unc = astAnnul( unc ); + frm = astAnnul( frm ); + + } + } + +/* Free resources */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + constants = astFree( constants ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* If the Mapping from base to current Frame is not a PermMap or a UnitMap, we + attempt to simplify the Box by re-defining it within its current Frame. + Transforming the box from its base to its current FRame may result in + the region no longer being a box. We test this by transforming a set of + bounds on the box boundary. */ + } else if( !astIsAUnitMap( map ) ){ + +/* Get pointer to current Frame. */ + frm = astGetFrame( new->frameset, AST__CURRENT ); + +/* Get a mesh of points covering the Box in its current Frame. */ + mesh = astRegMesh( new ); + +/* Get the Region describing the positional uncertainty within the Box in + its current Frame. */ + unc = astGetUncFrm( new, AST__CURRENT ); + +/* Find the best fitting box (defined in the current Frame) through these + points */ + newbox = BestBox( frm, mesh, unc, status ); + +/* See if all points within this mesh fall on the boundary of the best + fitting Box, to within the uncertainty of the Region. */ + if( newbox && astRegPins( newbox, mesh, NULL, NULL ) ) { + +/* If so, check that the inverse is true (we need to transform the + simplified boxes mesh into the base Frame of he original box for use by + astRegPins). */ + (void) astAnnul( mesh ); + mesh = astRegMesh( newbox ); + basemesh = astTransform( map, mesh, 0, NULL ); + if( astRegPins( new, basemesh, NULL, NULL ) ) { + +/* If so, use the new Box in place of the original Box. */ + (void) astAnnul( new ); + new = astClone( newbox ); + simpler = 1; + } + +/* Free resources. */ + basemesh = astAnnul( basemesh ); + +/* If the transformed Box is not itself a Box, see if it can be + represented accurately by a Polygon. This is only possible for + 2-dimensional Boxes. */ + } else if( nin == 2 && nout == 2 ) { + +/* Create a PointSet holding the base Frame axis values at the four + corners of the Box. */ + ps1 = astPointSet( 4, 2, "", status ); + ptr1 = astGetPoints( ps1 ); + if( astOK ) { + box = (AstBox *) new; + Cache( box, 0, status ); + +/* The order in which the polygon vertices are stored determines whether + the interior or exterior of the polygon forms the inside of the + Region. We want the inside to be the interior. First create a Polygon + in which the vertices are stored in clockwise order within the + new coordinate Frame. If the new Polygon is not bounded, use + anti-clockwise order. */ + for( right_handed = 0; right_handed < 2; right_handed++ ) { + + if( right_handed ) { + ptr1[ 0 ][ 0 ] = box->centre[ 0 ] - box->extent[ 0 ]; + ptr1[ 1 ][ 0 ] = box->centre[ 1 ] + box->extent[ 1 ]; + + ptr1[ 0 ][ 1 ] = box->centre[ 0 ] - box->extent[ 0 ]; + ptr1[ 1 ][ 1 ] = box->centre[ 1 ] - box->extent[ 1 ]; + + ptr1[ 0 ][ 2 ] = box->centre[ 0 ] + box->extent[ 0 ]; + ptr1[ 1 ][ 2 ] = box->centre[ 1 ] - box->extent[ 1 ]; + + ptr1[ 0 ][ 3 ] = box->centre[ 0 ] + box->extent[ 0 ]; + ptr1[ 1 ][ 3 ] = box->centre[ 1 ] + box->extent[ 1 ]; + + } else { + ptr1[ 0 ][ 3 ] = box->centre[ 0 ] - box->extent[ 0 ]; + ptr1[ 1 ][ 3 ] = box->centre[ 1 ] + box->extent[ 1 ]; + + ptr1[ 0 ][ 2 ] = box->centre[ 0 ] - box->extent[ 0 ]; + ptr1[ 1 ][ 2 ] = box->centre[ 1 ] - box->extent[ 1 ]; + + ptr1[ 0 ][ 1 ] = box->centre[ 0 ] + box->extent[ 0 ]; + ptr1[ 1 ][ 1 ] = box->centre[ 1 ] - box->extent[ 1 ]; + + ptr1[ 0 ][ 0 ] = box->centre[ 0 ] + box->extent[ 0 ]; + ptr1[ 1 ][ 0 ] = box->centre[ 1 ] + box->extent[ 1 ]; + } + +/* Transform the Box corners into the current Frame. */ + ps2 = astTransform( map, ps1, 1, NULL ); + ptr2 = astGetPoints( ps2 ); + if( astOK ) { + +/* Create a Polygon from these points. */ + for( ic = 0; ic < 4; ic++ ) { + corners[ ic ] = ptr2[ 0 ][ ic ]; + corners[ 4 + ic ] = ptr2[ 1 ][ ic ]; + } + newpoly = astPolygon( frm, 4, 4, corners, unc, "", status ); + +/* If the Polygon is bounded, break out of the loop. */ + if( astGetBounded( newpoly ) ) break; + } + +/* Free resources. */ + newpoly = astAnnul( newpoly ); + ps2 = astAnnul( ps2 ); + } + +/* See if all points within the Box mesh fall on the boundary of this + Polygon, to within the uncertainty of the Region. */ + if( astRegPins( newpoly, mesh, NULL, NULL ) ) { + +/* If so, use the new Polygon in place of the original Box. */ + (void) astAnnul( new ); + new = astClone( newpoly ); + simpler = 1; + } + +/* Free resources. */ + newpoly = astAnnul( newpoly ); + } + + ps1 = astAnnul( ps1 ); + } + +/* If we have yet been able to produce a simpler region, we now try + splitting the Box into two separate Boxes defined in separate + coordinate Frames. If either of these two Boxes can be simplified, + create a Prism containing the two simplified Boxes, and attempt to + simplify the Prism. Otherwise a clone of "new" is returned by + astConvertToPrism. */ + if( !simpler ) { + prism = astConvertToPrism( new ); + + if( prism != new ) { + simpler = 1; + (void) astAnnul( new ); + new = prism; + + } else { + prism = astAnnul( prism ); + } + } + + frm = astAnnul( frm ); + mesh = astAnnul( mesh ); + unc = astAnnul( unc ); + if( newbox ) newbox = astAnnul( newbox ); + } + + map = astAnnul( map ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. + If the supplied Region had no uncertainty, ensure the returned Region + has no uncertainty. Otherwise, return a clone of the supplied pointer. */ + if( simpler ){ + astRegOverlay( new, this, 1 ); + result = (AstMapping *) new; + + } else { + new = astAnnul( new ); + result = astClone( this ); + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a Box to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Box member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a Box and a set of points encapsulated in a +* PointSet and transforms the points by setting axis values to +* AST__BAD for all points which are outside the region. Points inside +* the region are copied unchanged from input to output. + +* Parameters: +* this +* Pointer to the Box. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The forward and inverse transformations are identical for a +* Region. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of axes in the Frame represented by the Box. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstBox *box; /* Pointer to Box */ + AstFrame *frm; /* Pointer to base Frame in FrameSet */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + AstRegion *reg; /* Pointer to Region */ + double **ptr_out; /* Pointer to output coordinate data */ + double **ptr_tmp; /* Pointer to base Frame coordinate data */ + double axval; /* Input axis value */ + int closed; /* Is the boundary part of the Region? */ + int coord; /* Zero-based index for coordinates */ + int ncoord_out; /* No. of coordinates per output point */ + int ncoord_tmp; /* No. of coordinates per base Frame point */ + int neg; /* Is the Box negated?*/ + int npoint; /* No. of points */ + int ok; /* Is the point inside the Region? */ + int point; /* Loop counter for points */ + + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain pointers to the Regionand to the Box. */ + reg = (AstRegion *) this; + box = (AstBox *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, + containing a copy of the input PointSet. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet to transform the supplied positions + from the current Frame in the encapsulated FrameSet (the Frame + represented by the Region), to the base Frame (the Frame in which the + Region is defined). This call also returns a pointer to the base Frame + of the encapsulated FrameSet. Note, the returned pointer may be a + clone of the "in" pointer, and so we must be carefull not to modify the + contents of the returned PointSet. */ + pset_tmp = astRegTransform( reg, in, 0, NULL, &frm ); + +/* Determine the numbers of points and coordinates per point from the base + Frame PointSet and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( pset_tmp ); + ncoord_tmp = astGetNcoord( pset_tmp ); + ptr_tmp = astGetPoints( pset_tmp ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* See if the boundary is part of the Region. */ + closed = astGetClosed( reg ); + +/* See if the Box is negated */ + neg = astGetNegated( reg ); + +/* Ensire the cached information is up to date. */ + Cache( box, 1, status ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* The logic used to combine axis values for negated and un-negated boxes + is different. For negated boxes, a position is in the region if *any + one* axis is not "close" to the box centre. */ + if( neg ) { + +/* Loop round each point */ + for ( point = 0; point < npoint; point++ ) { + +/* Assume the point is outside the Region (since the Region is + negated, this means assuming it is within the box). */ + ok = 0; + +/* Loop round each axis value at this point. We break as soon as we find + a bad axis value or an axis value which is outside the box. */ + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + +/* The point is definiately not in the Region if any input axis value is bad. */ + axval = ptr_tmp[ coord ][ point ]; + if( axval == AST__BAD ) { + break; + +/* Otherwise check the current axis value, depending on whether the + boundary is included in the Region or not. Break as soon as an axis + value is found which is outside the box limits (i.e. in the Region). */ + } else if( !astAxIn( frm, coord, box->lo[ coord ], box->hi[ coord ], + axval, !closed ) ) { + ok = 1; + break; + } + } + +/* If this point is not inside the Region store bad output axis values. */ + if( !ok ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + +/* For un-negated boxes, a position is in the region if *all* axes are "close" + to the box centre. */ + } else { + +/* Loop round each point */ + for ( point = 0; point < npoint; point++ ) { + +/* Assume the point is within the Region (i.e.inside the box). */ + ok = 1; + +/* Loop round each axis value at this point. We break when we find a bad + input point or if any axis value is outside the box. */ + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + +/* The point is not in the Region if any input axis value is bad. */ + axval = ptr_tmp[ coord ][ point ]; + if( axval == AST__BAD ) { + ok = 0; + break; + +/* Otherwise check the current axis value, depending on whether the + boundary is included in the Region or not. Break as soon as an axis + value is found which is outside the box limits (i.e. outside the Region). */ + } else if( !astAxIn( frm, coord, box->lo[ coord ], box->hi[ coord ], + axval, closed ) ) { + ok = 0; + break; + } + } + +/* If this point is outside the Region store bad output axis values. */ + if( !ok ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + } + +/* Free resources */ + pset_tmp = astAnnul( pset_tmp ); + frm = astAnnul( frm ); + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Region objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Region objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstBox *in; /* Pointer to input Box */ + AstBox *out; /* Pointer to output Box */ + int nax; /* Number of base Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Boxs. */ + in = (AstBox *) objin; + out = (AstBox *) objout; + +/* For safety, first clear any references to the input memory from + the output Box. */ + out->extent = NULL; + out->centre = NULL; + out->lo = NULL; + out->hi = NULL; + out->geolen = NULL; + +/* Copy dynamic memory contents */ + nax = astGetNin( ((AstRegion *) in)->frameset ); + out->extent = astStore( NULL, in->extent, + sizeof( double )*(size_t)nax ); + out->centre = astStore( NULL, in->centre, + sizeof( double )*(size_t)nax ); + out->lo = astStore( NULL, in->lo, + sizeof( double )*(size_t)nax ); + out->hi = astStore( NULL, in->hi, + sizeof( double )*(size_t)nax ); + out->geolen = astStore( NULL, in->geolen, + sizeof( double )*(size_t)nax ); +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Box objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Box objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstBox *this; /* Pointer to Box */ + +/* Obtain a pointer to the Box structure. */ + this = (AstBox *) obj; + +/* Annul all resources. */ + this->extent = astFree( this->extent ); + this->centre = astFree( this->centre ); + this->lo = astFree( this->lo ); + this->hi = astFree( this->hi ); + this->geolen = astFree( this->geolen ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Box objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Box class to an output Channel. + +* Parameters: +* this +* Pointer to the Box whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Write out values representing the instance variables for the + Box class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsABox and astCheckBox functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Box,Region) +astMAKE_CHECK(Box) + +AstBox *astBox_( void *frame_void, int form, const double point1[], + const double point2[], AstRegion *unc, const char *options, int *status, ...) { +/* +*++ +* Name: +c astBox +f AST_BOX + +* Purpose: +* Create a Box. + +* Type: +* Public function. + +* Synopsis: +c #include "box.h" +c AstBox *astBox( AstFrame *frame, int form, const double point1[], +c const double point2[], AstRegion *unc, +c const char *options, ... ) +f RESULT = AST_BOX( FRAME, FORM, POINT1, POINT2, UNC, OPTIONS, STATUS ) + +* Class Membership: +* Box constructor. + +* Description: +* This function creates a new Box and optionally initialises its +* attributes. +* +* The Box class implements a Region which represents a box with sides +* parallel to the axes of a Frame (i.e. an area which encloses a given +* range of values on each axis). A Box is similar to an Interval, the +* only real difference being that the Interval class allows some axis +* limits to be unspecified. Note, a Box will only look like a box if +* the Frame geometry is approximately flat. For instance, a Box centred +* close to a pole in a SkyFrame will look more like a fan than a box +* (the Polygon class can be used to create a box-like region close to a +* pole). + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame in which the region is defined. A deep +* copy is taken of the supplied Frame. This means that any +* subsequent changes made to the Frame using the supplied pointer +* will have no effect the Region. +c form +f FORM = INTEGER (Given) +* Indicates how the box is described by the remaining parameters. +* A value of zero indicates that the box is specified by a centre +* position and a corner position. A value of one indicates that the +* box is specified by a two opposite corner positions. +c point1 +f POINT1( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). If +c "form" +f FORM +* is zero, this array should contain the coordinates at the centre of +* the box. +c If "form" +f If FORM +* is one, it should contain the coordinates at the corner of the box +* which is diagonally opposite the corner specified by +c "point2". +f POINT2. +c point2 +f POINT2( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates at any corner of the +* box. +c unc +f UNC = INTEGER (Given) +* An optional pointer to an existing Region which specifies the +* uncertainties associated with the boundary of the Box being created. +* The uncertainty in any point on the boundary of the Box is found by +* shifting the supplied "uncertainty" Region so that it is centred at +* the boundary point being considered. The area covered by the +* shifted uncertainty Region then represents the uncertainty in the +* boundary position. The uncertainty is assumed to be the same for +* all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created Box. Alternatively, +f a null Object pointer (AST__NULL) +c a NULL Object pointer +* may be supplied, in which case a default uncertainty is used +* equivalent to a box 1.0E-6 of the size of the Box being created. +* +* The uncertainty Region has two uses: 1) when the +c astOverlap +f AST_OVERLAP +* function compares two Regions for equality the uncertainty +* Region is used to determine the tolerance on the comparison, and 2) +* when a Region is mapped into a different coordinate system and +* subsequently simplified (using +c astSimplify), +f AST_SIMPLIFY), +* the uncertainties are used to determine if the transformed boundary +* can be accurately represented by a specific shape of Region. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Box. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Box. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astBox() +f AST_BOX = INTEGER +* A pointer to the new Box. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstBox *new; /* Pointer to new Box */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the supplied Frame structure. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the Box, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitBox( NULL, sizeof( AstBox ), !class_init, &class_vtab, + "Box", frame, form, point1, point2, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Box's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Box. */ + return new; +} + +AstBox *astBoxId_( void *frame_void, int form, const double point1[], + const double point2[], void *unc_void, const char *options, + ... ) { +/* +* Name: +* astBoxId_ + +* Purpose: +* Create a Box. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstBox *astBoxId_( AstFrame *frame, int form, const double point1[], +* const double point2[], AstRegion *unc, +* const char *options, ... ) + +* Class Membership: +* Box constructor. + +* Description: +* This function implements the external (public) interface to the +* astBox constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astBox_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astBox_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astBox_. + +* Returned Value: +* The ID value associated with the new Box. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstBox *new; /* Pointer to new Box */ + AstRegion *unc; /* Pointer to Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Frame pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Obtain a Region pointer from the supplied "unc" ID and validate the + pointer to ensure it identifies a valid Region . */ + unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL; + +/* Initialise the Box, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitBox( NULL, sizeof( AstBox ), !class_init, &class_vtab, + "Box", frame, form, point1, point2, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Box's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Box. */ + return astMakeId( new ); +} + +AstBox *astInitBox_( void *mem, size_t size, int init, AstBoxVtab *vtab, + const char *name, AstFrame *frame, int form, + const double point1[], const double point2[], + AstRegion *unc, int *status ) { +/* +*+ +* Name: +* astInitBox + +* Purpose: +* Initialise a Box. + +* Type: +* Protected function. + +* Synopsis: +* #include "box.h" +* AstBox *astInitBox_( void *mem, size_t size, int init, AstBoxVtab *vtab, +* const char *name, AstFrame *frame, int form, +* const double point1[], const double point2[], +* AstRegion *unc ) + +* Class Membership: +* Box initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Box object. It allocates memory (if necessary) to accommodate +* the Box plus any additional data associated with the derived class. +* It then initialises a Box structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Box at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Box is to be initialised. +* This must be of sufficient size to accommodate the Box data +* (sizeof(Box)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Box (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Box +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Box's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Box. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame in which the region is defined. +* form +* Indicates how the box is described by the remaining parameters. +* A value of zero indicates that the box is specified by a centre +* position and a corner position. A value of one indicates that the +* box is specified by a two opposite corner positions. +* point1 +* An array of double, with one element for each Frame axis (Naxes +* attribute). If "form" is zero, this array should contain the +* coordinates at the centre of the box. If "form" is one, it should +* contain the coordinates at the corner of the box which is diagonally +* opposite the corner specified by "point2". +* point2 +* An array of double, with one element for each Frame axis (Naxes +* attribute) containing the coordinates at any of the corners of +* the box. +* unc +* A pointer to a Region which specifies the uncertainty in the +* supplied positions (all points on the boundary of the new Box +* being initialised are assumed to have the same uncertainty). A NULL +* pointer can be supplied, in which case default uncertainties equal to +* 1.0E-6 of the dimensions of the new Box's bounding box are used. +* If an uncertainty Region is supplied, it must be either a Box, a +* Circle or an Ellipse, and its encapsulated Frame must be related +* to the Frame supplied for parameter "frame" (i.e. astConvert +* should be able to find a Mapping between them). Two positions +* the "frame" Frame are considered to be co-incident if their +* uncertainty Regions overlap. The centre of the supplied +* uncertainty Region is immaterial since it will be re-centred on the +* point being tested before use. A deep copy is taken of the supplied +* Region. + +* Returned Value: +* A pointer to the new Box. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstBox *new; /* Pointer to new Box */ + AstPointSet *pset; /* PointSet to pass to Region initialiser */ + double **ptr; /* Pointer to coords data in pset */ + int i; /* axis index */ + int nc; /* No. of axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitBoxVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Get the number of axis values required for each position. */ + nc = astGetNaxes( frame ); + +/* Create a PointSet to hold the supplied values, and get points to the + data arrays. */ + pset = astPointSet( 2, nc, "", status ); + ptr = astGetPoints( pset ); + +/* Copy the supplied coordinates into the PointSet, checking that no bad + values have been supplied. */ + for( i = 0; astOK && i < nc; i++ ) { + if( point1[ i ] == AST__BAD ) { + astError( AST__BADIN, "astInitBox(%s): The value of axis %d is " + "undefined at point 1 of the box.", status, name, i + 1 ); + break; + } + if( point2[ i ] == AST__BAD ) { + astError( AST__BADIN, "astInitBox(%s): The value of axis %d is " + "undefined at point 2 of the box.", status, name, i + 1 ); + break; + } + ptr[ i ][ 0 ] = point1[ i ]; + ptr[ i ][ 1 ] = point2[ i ]; + } + +/* If two corners were supplied, find and store the centre. */ + if( form == 1 ) { + for( i = 0; i < nc; i++ ) { + ptr[ i ][ 0 ] = 0.5*( point1[ i ] + point2[ i ] ); + } + } + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Initialise a Region structure (the parent class) as the first component + within the Box structure, allocating memory if necessary. */ + new = (AstBox *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frame, pset, unc ); + + if ( astOK ) { + +/* Initialise the Box data. */ +/* ------------------------ */ + new->extent = NULL; + new->centre = NULL; + new->lo = NULL; + new->hi = NULL; + new->geolen = NULL; + new->stale = 1; + +/* If an error occurred, clean up by deleting the new Box. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free resources. */ + pset = astAnnul( pset ); + +/* Return a pointer to the new Box. */ + return new; +} + +AstBox *astLoadBox_( void *mem, size_t size, AstBoxVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadBox + +* Purpose: +* Load a Box. + +* Type: +* Protected function. + +* Synopsis: +* #include "box.h" +* AstBox *astLoadBox( void *mem, size_t size, AstBoxVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Box loader. + +* Description: +* This function is provided to load a new Box using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Box structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Box at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Box is to be +* loaded. This must be of sufficient size to accommodate the +* Box data (sizeof(Box)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Box (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Box structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstBox) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Box. If this is NULL, a pointer +* to the (static) virtual function table for the Box class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Box" is used instead. + +* Returned Value: +* A pointer to the new Box. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstBox *new; /* Pointer to the new Box */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Box. In this case the + Box belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstBox ); + vtab = &class_vtab; + name = "Box"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitBoxVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Box. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Box" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* Initialise Box data */ + new->extent = NULL; + new->centre = NULL; + new->lo = NULL; + new->hi = NULL; + new->geolen = NULL; + new->stale = 1; + +/* If an error occurred, clean up by deleting the new Box. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Box pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + +void astBoxPoints_( AstBox *this, double *centre, double *corner, + int *status) { + if ( !astOK ) return; + (**astMEMBER(this,Box,BoxPoints))( this, centre, corner, status ); + return; +} + + + + + + + + + + + + + + + diff --git a/box.h b/box.h new file mode 100644 index 0000000..46dcf6c --- /dev/null +++ b/box.h @@ -0,0 +1,234 @@ +#if !defined( BOX_INCLUDED ) /* Include this file only once */ +#define BOX_INCLUDED +/* +*+ +* Name: +* box.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Box class. + +* Invocation: +* #include "box.h" + +* Description: +* This include file defines the interface to the Box class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Box class implement a Region which represents a simple interval +* on each axis of the encapsulated Frame + +* Inheritance: +* The Box class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-MAR-2003 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* Box structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstBox { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *extent; /* Original axis half-widths */ + double *centre; /* Box centre coords */ + double *lo; /* Low limits */ + double *hi; /* High limits */ + double *geolen; /* Geodesic half-dimensions of box */ + int stale; /* Is other info out of date? */ +} AstBox; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstBoxVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* BoxPoints)( AstBox *, double *, double *, int *); +} AstBoxVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstBoxGlobals { + AstBoxVtab Class_Vtab; + int Class_Init; +} AstBoxGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitBoxGlobals_( AstBoxGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Box) /* Check class membership */ +astPROTO_ISA(Box) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstBox *astBox_( void *, int, const double[], const double[], AstRegion *, const char *, int *, ...); +#else +AstBox *astBoxId_( void *, int, const double[], const double[], AstRegion *, const char *, ... )__attribute__((format(printf,6,7))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstBox *astInitBox_( void *, size_t, int, AstBoxVtab *, + const char *, AstFrame *, int, const double[], + const double[], AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitBoxVtab_( AstBoxVtab *, const char *, int * ); + +/* Loader. */ +AstBox *astLoadBox_( void *, size_t, AstBoxVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +void astBoxPoints_( AstBox *, double *, double *, int *); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckBox(this) astINVOKE_CHECK(Box,this,0) +#define astVerifyBox(this) astINVOKE_CHECK(Box,this,1) + +/* Test class membership. */ +#define astIsABox(this) astINVOKE_ISA(Box,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astBox astINVOKE(F,astBox_) +#else +#define astBox astINVOKE(F,astBoxId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitBox(mem,size,init,vtab,name,frame,form,p1,p2,unc) \ +astINVOKE(O,astInitBox_(mem,size,init,vtab,name,frame,form,p1,p2,unc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitBoxVtab(vtab,name) astINVOKE(V,astInitBoxVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadBox(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadBox_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckBox to validate Box pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astBoxPoints(this,centre,corner) astINVOKE(V,astBoxPoints_(astCheckBox(this),centre,corner,STATUS_PTR)) +#endif +#endif + + + + + diff --git a/builddocs.in b/builddocs.in new file mode 100644 index 0000000..fc8c453 --- /dev/null +++ b/builddocs.in @@ -0,0 +1,144 @@ +#! /bin/sh - + +srcdir=. +perldir=`dirname @PERL@` +if test -z "$SST_DIR"; then + if test -x starconf.status; then + finder=`./starconf.status --show finder` + test -n "$finder" && prolat_path=`$finder --path prolat` + fi + if test -z "$prolat_path" -a -f $STARLINK/bin/sst/prolat; then + prolat_path=$STARLINK/bin/sst/prolat + fi + if test -z "$prolat_path"; then + # not all shells have the which builtin, but try anyway + prolat_path=`which prolat 2>/dev/null` + fi + if test -z "$prolat_path"; then + echo "SST_DIR isn't set, and can't find prolat" >&2 + exit 1 + fi + SST_DIR=`dirname $prolat_path` + export SST_DIR + echo "SST_DIR set to $SST_DIR" +fi +PATH="${srcdir}:${perldir}:${PATH}" +export PATH + +goodsource="${srcdir}/axis.c \ + ${srcdir}/box.c \ + ${srcdir}/channel.c \ + ${srcdir}/circle.c \ + ${srcdir}/cmpframe.c \ + ${srcdir}/cmpmap.c \ + ${srcdir}/cmpregion.c \ + ${srcdir}/dsbspecframe.c \ + ${srcdir}/dssmap.c \ + ${srcdir}/ellipse.c \ + ${srcdir}/error.c \ + ${srcdir}/fchannel.c \ + ${srcdir}/fitschan.c \ + ${srcdir}/fitstable.c \ + ${srcdir}/fluxframe.c \ + ${srcdir}/frame.c \ + ${srcdir}/frameset.c \ + ${srcdir}/grismmap.c \ + ${srcdir}/interval.c \ + ${srcdir}/intramap.c \ + ${srcdir}/keymap.c \ + ${srcdir}/lutmap.c \ + ${srcdir}/mapping.c \ + ${srcdir}/mathmap.c \ + ${srcdir}/matrixmap.c \ + ${srcdir}/nullregion.c \ + ${srcdir}/object.c \ + ${srcdir}/pcdmap.c \ + ${srcdir}/permmap.c \ + ${srcdir}/plot.c \ + ${srcdir}/plot3d.c \ + ${srcdir}/pointlist.c \ + ${srcdir}/pointset.c \ + ${srcdir}/polygon.c \ + ${srcdir}/polymap.c \ + ${srcdir}/prism.c \ + ${srcdir}/normmap.c \ + ${srcdir}/ratemap.c \ + ${srcdir}/region.c \ + ${srcdir}/shiftmap.c \ + ${srcdir}/sphmap.c \ + ${srcdir}/skyaxis.c \ + ${srcdir}/skyframe.c \ + ${srcdir}/slamap.c \ + ${srcdir}/specframe.c \ + ${srcdir}/specfluxframe.c \ + ${srcdir}/specmap.c \ + ${srcdir}/stc.c \ + ${srcdir}/stcresourceprofile.c \ + ${srcdir}/stcsearchlocation.c \ + ${srcdir}/stccatalogentrylocation.c \ + ${srcdir}/stcobsdatalocation.c \ + ${srcdir}/stcschan.c \ + ${srcdir}/table.c \ + ${srcdir}/timeframe.c \ + ${srcdir}/timemap.c \ + ${srcdir}/tranmap.c \ + ${srcdir}/selectormap.c \ + ${srcdir}/switchmap.c \ + ${srcdir}/unitmap.c \ + ${srcdir}/wcsmap.c \ + ${srcdir}/winmap.c \ + ${srcdir}/xmlchan.c \ + ${srcdir}/zoommap.c" + +goodsource="${goodsource} \ + ${srcdir}/ast_link \ + ${srcdir}/ast_link_adam" + +#chmod +x ${srcdir}/addcopyright +#chmod +x ${srcdir}/addlinks +#chmod +x ${srcdir}/addversion +#chmod +x ${srcdir}/doincludes +#chmod +x ${srcdir}/getatt +#chmod +x ${srcdir}/selectfc + +rm -f .sst.tmp + + getatt -class -f ${goodsource} >f_classes.tex + cat getatt.labels >global_f.labels; rm -f getatt.labels + getatt -class ${goodsource} >c_classes.tex + cat getatt.labels >global_c.labels; rm -f getatt.labels + getatt -att -f ${goodsource} >f_attribs.tex + cat getatt.labels >>global_f.labels; rm -f getatt.labels + getatt -att ${goodsource} >c_attribs.tex + cat getatt.labels >>global_c.labels; rm -f getatt.labels + getatt -f ${goodsource} >f_routines.tex + cat getatt.labels >>global_f.labels; rm -f getatt.labels + getatt ${goodsource} >c_routines.tex + cat getatt.labels >>global_c.labels; rm -f getatt.labels + + getatt memory.c > memory_routines.tex + cat getatt.labels >>global_c.labels; rm -f getatt.labels + + getatt -u -f ${goodsource} >f_commands.tex + cat getatt.labels >>global_f.labels; rm -f getatt.labels + getatt -u ${goodsource} >c_commands.tex + cat getatt.labels >>global_c.labels; rm -f getatt.labels + + ln -f global_f.labels global.labels + doincludes ${srcdir}/sun_master.tex \ + | selectfc -f | addlinks | addcopyright | addversion >sun210.tex + ln -f global_c.labels global.labels + doincludes ${srcdir}/sun_master.tex \ + | selectfc | addlinks | addcopyright | addversion >sun211.tex + + rm -f global.labels global_f.labels global_c.labels + rm -f c_attribs.tex c_classes.tex c_routines.tex c_commands.tex + rm -f f_attribs.tex f_classes.tex f_routines.tex f_commands.tex + rm -f memory_routines.tex + + + TEXINPUTS=@STARLINK@/share/latexsupport//:${TEXINPUTS} pdflatex -interaction=nonstopmode sun210 + TEXINPUTS=@STARLINK@/share/latexsupport//:${TEXINPUTS} pdflatex -interaction=nonstopmode sun210 + TEXINPUTS=@STARLINK@/share/latexsupport//:${TEXINPUTS} pdflatex -interaction=nonstopmode sun211 + TEXINPUTS=@STARLINK@/share/latexsupport//:${TEXINPUTS} pdflatex -interaction=nonstopmode sun211 + diff --git a/buildhyperdocs b/buildhyperdocs new file mode 100755 index 0000000..23a07e1 --- /dev/null +++ b/buildhyperdocs @@ -0,0 +1,11 @@ + +chmod +x ${AST_REF}/addcopyright +copyright="`echo '' | ${AST_REF}/addcopyright`" + +rm -f -r sun210.htx sun211.htx +star2html -c "${copyright}" sun210 +star2html -c "${copyright}" sun211 + +export HTX_PATH +HTX_PATH=.:/star/docs:/star/help +hlink diff --git a/c2f77.c b/c2f77.c new file mode 100644 index 0000000..1801a55 --- /dev/null +++ b/c2f77.c @@ -0,0 +1,125 @@ +/* +* Name: +* c2f77.c + +* Purpose: +* Implement the interface between the C and FORTRAN 77 languages. + +* Description: +* This file implements language-specific functions which support +* the FORTRAN 77 interface to the AST library. +* +* Note that this module is not a class implementation, although it +* resembles one. + +* Notes: +* - Some of the functions in this module are potentially platform +* dependent and may need to be re-implemented when porting the AST +* library to new platforms. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 15-NOV-1996 (RFWS): +* Original version (based on work by DSB and on code from the +* Starlink CNF library). +*/ + +/* Define the astCLASS macro (even although this is not a class + implementation) to obtain access to protected interfaces. */ +#define astCLASS + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" /* Error reporting facilities */ +#include "c2f77.h" /* Interface to this module */ + +/* Function implementations. */ +/* ========================= */ +void astStringExport_( const char *source_c, char *dest_f, int dest_len ) { +/* +*+ +* Name: +* astStringExport + +* Purpose: +* Export a C string to a FORTRAN string. + +* Type: +* Protected function. + +* Synopsis: +* #include "c2f77.h" +* void astStringExport( const char *source_c, char *dest_f, int dest_len ) + +* Description: +* This function creates a FORTRAN string from a C string, storing +* it in the supplied memory. If the C string is shorter than the +* space allocated for the FORTRAN string, then it is padded with +* blanks. If the C string is longer than the space allocated for +* the FORTRAN string, then the string is truncated. + +* Parameters: +* source_c +* A pointer to the input C string. +* dest_f +* A pointer to the output FORTRAN string. +* dest_len +* The length of the output FORTRAN string. + +* Notes: +* - This function is potentially platform-specific. For example, +* if FORTRAN strings were passed by descriptor, then the +* descriptor address would be passed as "dest_f" and this must +* then be used to locate the actual FORTRAN character data. +* - This function is described as protected but is in fact +* available through the public interface so that it may be used in +* constructing the FORTRAN 77 public interface. +* - This is the UNIX version of this function. +*- +*/ + +/* Local Variables:*/ + int i; /* Loop counter for characters */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Copy the characters of the input C string to the output FORTRAN + string, taking care not to go beyond the end of the FORTRAN + string.*/ + for ( i = 0; source_c[ i ] && ( i < dest_len ); i++ ) { + dest_f[ i ] = source_c[ i ]; + } + +/* Fill the rest of the output FORTRAN string with blanks. */ + for ( ; i < dest_len; i++ ) dest_f[ i ] = ' '; +} diff --git a/c2f77.h b/c2f77.h new file mode 100644 index 0000000..c50edac --- /dev/null +++ b/c2f77.h @@ -0,0 +1,166 @@ +#if !defined( C2F77_INCLUDED ) /* Include this file only once */ +#define C2F77_INCLUDED +/* +*+ +* Name: +* c2f77.h + +* Purpose: +* Define the interface to the c2f77 module. + +* Description: +* This file defines language-specific functions which support the +* FORTRAN 77 interface to the AST library. +* +* Note that this module is not a class implementation, although it +* resembles one. + +* Functions Defined: +* Public: +* None. +* +* Protected: +* astStringExport +* Export a C string to a FORTRAN string. + +* Macros Defined: +* Public: +* None. +* +* Protected: +* astWatchSTATUS +* Execute C code while watching a FORTRAN STATUS variable. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 15-NOV-1996 (RFWS): +* Original version. +* 16-JUL-1997 (RFWS): +* Added astWatchSTATUS. +* 13-JUN-2001 (DSB): +* Make astStringExport available to F77 interface modules as well +* as AST classes. +*- +*/ + +/* Macros. */ +/* ======= */ +/* +*+ +* Name: +* astWatchSTATUS + +* Type: +* Protected macro. + +* Purpose: +* Execute C code while watching a FORTRAN STATUS variable. + +* Synopsis: +* #include "c2f77.h" +* astWatchSTATUS(code) + +* Description: +* This macro expands to code which executes the C code supplied +* via the "code" argument in a new C scope (delimited by +* {...}). The code supplied executes while the AST error status is +* equated to a variable called STATUS, which is an error status +* argument passed from a FORTRAN routine using the macros defined +* in the "f77.h" include file. +* +* The effect of this is roughly as if the astWatch function had +* been used to locally declare the FORTRAN STATUS argument as a +* new AST error status variable, except that this macro also works +* if STATUS is not an int. + +* Parameters: +* code +* The C code to be executed. + +* Examples: +* F77_SUBROUTINE(ast_doit)( INTEGER(STATUS) ) { +* astWatchSTATUS( +* astDoit(); +* ) +* } +* Causes the astDoit function to be invoked as if the AST error +* status were equated to the STATUS argument passed from +* FORTRAN. Typically, if STATUS is set to an error value, +* astDoit would detect this by means of the astOK macro and +* would not then execute. If an error occurs in astDoit, +* causing the AST error status to be set, then that value is +* transferred to STATUS after the C code has executed (i.e. at +* the end of the astWatchSTATUS macro). + +* Notes: +* - The FORTRAN argument must be called STATUS and must appear in +* the C function's parameter list as an argument of the INTEGER() +* macro defined in the "f77.h" include file. +* - The C code supplied executes in a new scope, in which +* automatic variables may be declared. However, such variables +* will not exist after the macro's expansion has been executed. +* - The AST error status variable and its value remain unchanged +* after the expansion of this macro has executed. +*- +*/ + +/* Define the macro. */ +#define astWatchSTATUS(code) \ +\ +/* Begin a new C scope. */ \ +{ \ +\ +/* Ensure that a pointer to the STATUS argument exists. */ \ + GENPTR_INTEGER(STATUS) \ +\ +/* Store the STATUS value in a local int. */ \ + int ast_local_status = *STATUS; \ + int *status = &ast_local_status; \ +\ +/* Make this int the AST error status variable, saving the address of \ + the previous variable. */ \ + int *ast_previous_status = astWatch( &ast_local_status ); \ +\ +/* Execute the code supplied using the new error status variable. */ \ + code \ +\ +/* Restore the original error status variable. */ \ + (void) astWatch( ast_previous_status ); \ +\ +/* Return the final error status to STATUS. */ \ + *STATUS = ast_local_status; \ +} + +/* Function prototypes. */ +/* ==================== */ +void astStringExport_( const char *, char *, int ); + +/* Function interfaces. */ +/* ==================== */ +/* These wrap up the functions defined by this module to make them + easier to use. */ +#define astStringExport astStringExport_ +#endif diff --git a/cexpand b/cexpand new file mode 100755 index 0000000..b40957a --- /dev/null +++ b/cexpand @@ -0,0 +1,32 @@ +#!/bin/tcsh +#+ + +# Name: +# cexpand + +# Purpose: +# Expand a C source file using the C pre-processor and re-format it +# into a readable form. The output is written to a file with the same +# base-name as the input file, but with a file type of ".cpp". + +# Usage: +# % cexpand fred.c + +#- + +if( "$1" == "" ) then + echo "Usage: cexpand " + exit +endif + +set file = `basename $1 .c` +cpp -CC -P $file.c -DTHREAD_SAFE -DHAVE_CONFIG_H -I. -I.. -I/star/include > aaxx +indent aaxx -kr -o bbxx +cat bbxx | sed -e 's#/\*#\n/\*#g' > ccxx +cat ccxx | cexpand.pl > ddxx +indent ddxx -kr -o $file.cpp +rm aaxx bbxx ccxx ddxx + +echo "Output in $file.cpp" +echo "" + diff --git a/cexpand.pl b/cexpand.pl new file mode 100755 index 0000000..0795c04 --- /dev/null +++ b/cexpand.pl @@ -0,0 +1,22 @@ +#!/star/Perl/bin/perl + while( $line = ) { + chomp( $line ); + if( $line =~ /^\/\*(.*)\*\/\s*$/ ) { + print "\n/*"; + $len = 0; + foreach $word ( split '\s+', $1 ) { + $len += length( $word ) + 1; + if( $len > 70 ) { + print "\n "; + $len = length( $word ) + 1; + } + print " $word"; + } + print " */\n"; + + } else { + print "$line\n"; + } + + } + diff --git a/channel.c b/channel.c new file mode 100644 index 0000000..8a285b3 --- /dev/null +++ b/channel.c @@ -0,0 +1,6458 @@ +/* +*class++ +* Name: +* Channel + +* Purpose: +* Basic (textual) I/O channel. + +* Constructor Function: +c astChannel +f AST_CHANNEL + +* Description: +* The Channel class implements low-level input/output for the AST +* library. Writing an Object to a Channel will generate a textual +* representation of that Object, and reading from a Channel will +* create a new Object from its textual representation. +* +* Normally, when you use a Channel, you should provide "source" +c and "sink" functions which connect it to an external data store +f and "sink" routines which connect it to an external data store +* by reading and writing the resulting text. By default, however, +* a Channel will read from standard input and write to standard +* output. Alternatively, a Channel can be told to read or write from +* specific text files using the SinkFile and SourceFile attributes, +* in which case no sink or source function need be supplied. + +* Inheritance: +* The Channel class inherits from the Object class. + +* Attributes: +* In addition to those attributes common to all Objects, every +* Channel also has the following attributes: +* +* - Comment: Include textual comments in output? +* - Full: Set level of output detail +* - Indent: Indentation increment between objects +* - ReportLevel: Selects the level of error reporting +* - SinkFile: The path to a file to which the Channel should write +* - Skip: Skip irrelevant data? +* - SourceFile: The path to a file from which the Channel should read +* - Strict: Generate errors instead of warnings? + +* Functions: +c In addition to those functions applicable to all Objects, the +c following functions may also be applied to all Channels: +f In addition to those routines applicable to all Objects, the +f following routines may also be applied to all Channels: +* +c - astWarnings: Return warnings from the previous read or write +c - astPutChannelData: Store data to pass to source or sink functions +c - astRead: Read an Object from a Channel +c - astWrite: Write an Object to a Channel +f - AST_WARNINGS: Return warnings from the previous read or write +f - AST_READ: Read an Object from a Channel +f - AST_WRITE: Write an Object to a Channel + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 12-AUG-1996 (RFWS): +* Original version. +* 6-SEP-1996: +* Finished initial implementation. +* 11-DEC-1996 (RFWS): +* Added support for foreign language source and sink functions. +* 28-APR-1997 (RFWS): +* Prevent "-0" being written (use "0" instead). +* 27-NOV-2002 (DSB): +* Added astWriteInvocations. +* 8-JAN-2003 (DSB): +* - Changed private InitVtab method to protected astInitChannelVtab +* method. +* - Modified to use protected Vtab initialisation methods when +* loading an Object. +* 1-NOV-2003 (DSB): +* Change the initialiser so that it accepts source and sink +* wrapper functions as arguments (for use by derived classes). +* 16-AUG-2006 (DSB): +* - Document non-destructive nature of unsuccessful astRead calls +* on a FitsChan. +* 3-OCT-2008 (DSB): +* Added "Strict" attribute. +* 11-DEC-2008 (DSB): +* Added astPutChannelData and astChannelData functions. +* 16-JAN-2009 (DSB): +* Added astAddWarning and astWarnings. +* 11-JUN-2009 (DSB): +* Enable astChannelData to be used from within astRead. +* 7-DEC-2009 (DSB): +* Added Indent attribute. +* 12-FEB-2010 (DSB): +* Represent AST__BAD externally using the string "". +* 23-JUN-2011 (DSB): +* Added attributes SinkFile and SourceFile. +* 2-OCT-2012 (DSB): +* Report an error if an Inf or NaN value is read from the external +* source. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Channel + +/* Define a string containing the maximum length of keywords used to + identify values in the external representation of data. This is + deliberately kept small so as to simplify integration with + standards such as FITS. */ +#define MAX_NAME "8" + +/* Max length of string returned by GetAttrib */ +#define GETATTRIB_BUFF_LEN 50 + +/* String used to represent AST__BAD externally. */ +#define BAD_STRING "" + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "channel.h" /* Interface definition for this class */ +#include "loader.h" /* Interface to the global loader */ +#include "keymap.h" /* Storing arbitrary data in an AST Object */ +#include "pointset.h" /* For AST__BAD */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->AstReadClassData_Msg = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->Items_Written = 0; \ + globals->Current_Indent = 0; \ + globals->Nest = -1; \ + globals->Nwrite_Invoc = 0; \ + globals->Object_Class = NULL; \ + globals->Values_List = NULL; \ + globals->Values_Class = NULL; \ + globals->Values_OK = NULL; \ + globals->End_Of_Object = NULL; \ + globals->Channel_Data = NULL; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Channel) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Channel,Class_Init) +#define class_vtab astGLOBAL(Channel,Class_Vtab) +#define astreadclassdata_msg astGLOBAL(Channel,AstReadClassData_Msg) +#define getattrib_buff astGLOBAL(Channel,GetAttrib_Buff) +#define items_written astGLOBAL(Channel,Items_Written) +#define current_indent astGLOBAL(Channel,Current_Indent) +#define nest astGLOBAL(Channel,Nest) +#define nwrite_invoc astGLOBAL(Channel,Nwrite_Invoc) +#define object_class astGLOBAL(Channel,Object_Class) +#define values_list astGLOBAL(Channel,Values_List) +#define values_class astGLOBAL(Channel,Values_Class) +#define values_ok astGLOBAL(Channel,Values_OK) +#define end_of_object astGLOBAL(Channel,End_Of_Object) +#define channel_data astGLOBAL(Channel,Channel_Data) + + + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 ); +#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Contextual error message reported in astReadClassData? */ +static int astreadclassdata_msg = 0; + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ]; + +/* Count of the number of output items written since the last "Begin" + or "IsA" item. */ +static int items_written = 0; + +/* Amount of indentation to be applied to the next output item. */ +static int current_indent = 0; + +/* Nesting level, used to keep track of data associated with building + Objects when they contain other Objects. */ +static int nest = -1; + +/* The number of times astWrite has been invoked. */ +static int nwrite_invoc = 0; + +/* Pointer to a user-supplied block of memory to be made available to + source or sink functions via the astChannelData function. */ +static void *channel_data = NULL; + +/*** + The following items are all pointers to dynamically allocated + arrays (stacks) that grow as necessary to accommodate one element + for each level of nesting (one more than the value of "nest"). +***/ + +/* Stack of pointers to null-terminated character strings giving the + names of the classes of the Objects being built at each nesting + level. */ +static char **object_class = NULL; + +/* Stack of pointers to the elements designated as the "heads" of + circular, doubly linked lists of name-value associations. */ +static AstChannelValue **values_list = NULL; + +/* Stack of pointers to null-terminated character strings giving the + names of the classes for which the values held in the values lists + are intended. */ +static char **values_class = NULL; + +/* Stack of flags indicating whether the values held in the values + lists are intended for the class loaders currently executing to + build Objects at each nesting level. */ +static int *values_ok = NULL; + +/* Stack of flags indicating whether "End" items have been read for + the Objects being built at each nesting level. */ +static int *end_of_object = NULL; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstChannelVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 +#define LOCK_MUTEX3 +#define UNLOCK_MUTEX3 + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstChannel *astChannelForId_( const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), + const char *, int * ), + const char *, ... ); +AstChannel *astChannelId_( const char *(*)( void ), void (*)( const char * ), const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstObject *Read( AstChannel *, int * ); +static AstObject *ReadObject( AstChannel *, const char *, AstObject *, int * ); +static AstChannelValue *FreeValue( AstChannelValue *, int * ); +static AstChannelValue *LookupValue( const char *, int * ); +static AstKeyMap *Warnings( AstChannel *, int * ); +static char *GetNextText( AstChannel *, int * ); +static char *InputTextItem( AstChannel *, int * ); +static char *ReadString( AstChannel *, const char *, const char *, int * ); +static char *SourceWrap( const char *(*)( void ), int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static double ReadDouble( AstChannel *, const char *, double, int * ); +static int GetComment( AstChannel *, int * ); +static int GetFull( AstChannel *, int * ); +static int GetSkip( AstChannel *, int * ); +static int GetStrict( AstChannel *, int * ); +static int ReadInt( AstChannel *, const char *, int, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestComment( AstChannel *, int * ); +static int TestFull( AstChannel *, int * ); +static int TestSkip( AstChannel *, int * ); +static int TestStrict( AstChannel *, int * ); +static int Use( AstChannel *, int, int, int * ); +static int Write( AstChannel *, AstObject *, int * ); +static void AddWarning( AstChannel *, int, const char *, const char *, int * ); +static void AppendValue( AstChannelValue *, AstChannelValue **, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearComment( AstChannel *, int * ); +static void ClearFull( AstChannel *, int * ); +static void ClearSkip( AstChannel *, int * ); +static void ClearStrict( AstChannel *, int * ); +static void ClearValues( AstChannel *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GetNextData( AstChannel *, int, char **, char **, int * ); +static void OutputTextItem( AstChannel *, const char *, int * ); +static void PutChannelData( AstChannel *, void *, int * ); +static void PutNextText( AstChannel *, const char *, int * ); +static void ReadClassData( AstChannel *, const char *, int * ); +static void RemoveValue( AstChannelValue *, AstChannelValue **, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetComment( AstChannel *, int, int * ); +static void SetFull( AstChannel *, int, int * ); +static void SetSkip( AstChannel *, int, int * ); +static void SetStrict( AstChannel *, int, int * ); +static void SinkWrap( void (*)( const char * ), const char *, int * ); +static void Unquote( AstChannel *, char *, int * ); +static void WriteBegin( AstChannel *, const char *, const char *, int * ); +static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * ); +static void WriteEnd( AstChannel *, const char *, int * ); +static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * ); +static void WriteIsA( AstChannel *, const char *, const char *, int * ); +static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * ); +static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * ); + +static int GetReportLevel( AstChannel *, int * ); +static int TestReportLevel( AstChannel *, int * ); +static void ClearReportLevel( AstChannel *, int * ); +static void SetReportLevel( AstChannel *, int, int * ); + +static int GetIndent( AstChannel *, int * ); +static int TestIndent( AstChannel *, int * ); +static void ClearIndent( AstChannel *, int * ); +static void SetIndent( AstChannel *, int, int * ); + +static const char *GetSourceFile( AstChannel *, int * ); +static int TestSourceFile( AstChannel *, int * ); +static void ClearSourceFile( AstChannel *, int * ); +static void SetSourceFile( AstChannel *, const char *, int * ); + +static const char *GetSinkFile( AstChannel *, int * ); +static int TestSinkFile( AstChannel *, int * ); +static void ClearSinkFile( AstChannel *, int * ); +static void SetSinkFile( AstChannel *, const char *, int * ); + +/* Member functions. */ +/* ================= */ +static void AddWarning( AstChannel *this, int level, const char *msg, + const char *method, int *status ) { +/* +*+ +* Name: +* astAddWarning + +* Purpose: +* Add a warning to a Channel. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astAddWarning( AstChannel *this, int level, const char *msg, +* const char *method, int status, ... ) + +* Class Membership: +* Channel method. + +* Description: +* This function stores a warning message inside a Channel. These +* messages can be retirieved using astWarnings. + +* Parameters: +* this +* Pointer to the Channel. +* level +* Ignore the warning if the ReportLevel attribute value is less +* than "level". +* msg +* The wanting message to store. It may contain printf format +* specifiers. If a NULL pointer is supplied, all warnings +* currently stored in the Channel are removed. +* method +* The method name. +* status +* Inherited status value. +* ... +* Extra values to substitute into the message string as +* replacements for the printf format specifiers. +*- + +* Note: The expansion of the printf format specifiers is done in the +* astAddWarning_ wrapper function. The AddWarning functions defined by +* each class receives the fully expanded message and does not have a +* variable argument list. The variable argument list is included in the +* above prologue in order to document the wrapper function. + +*/ + +/* Local Variables: */ + int i; /* Message index */ + char *a; /* Pointer to copy of message */ + +/* If a NULL pointer was supplied, free all warnings currently in the + Channel. Do this before checking the inherited status so that it works + even if an error has occurred. */ + if( !msg ) { + for( i = 0; i < this->nwarn; i++ ) { + (this->warnings)[ i ] = astFree( (this->warnings)[ i ] ); + } + this->warnings = astFree( this->warnings ); + this->nwarn = 0; + return; + } + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Only proceed if the message level is sufficiently important. */ + if( astGetReportLevel( this ) >= level ) { + +/* If we are being strict, issue an error rather than a warning. */ + if( astGetStrict( this ) ) { + if( astOK ) { + astError( AST__BADIN, "%s(%s): %s", status, method, + astGetClass( this ), msg ); + } + +/* Otherwise, we store a copy of the message in the Channel. */ + } else { + +/* Allocate memory and store a copy of th supplied string in it. */ + a = astStore( NULL, msg, strlen( msg ) + 1 ); + +/* Expand the array of warning pointers in ther Channel structure. */ + this->warnings = astGrow( this->warnings, this->nwarn + 1, + sizeof( char * ) ); + +/* If all is OK so far, store the new warning pointer, and increment the + number of warnings in the Channel. */ + if( astOK ) { + (this->warnings)[ (this->nwarn)++ ] = a; + +/* Otherwise, attempt to free the memory holding the copy of the warning. */ + } else { + a = astFree( a ); + } + } + } +} + +static void AppendValue( AstChannelValue *value, AstChannelValue **head, int *status ) { +/* +* Name: +* AppendValue + +* Purpose: +* Append a Value structure to a list. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void AppendValue( AstChannelValue *value, AstChannelValue **head, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function appends a Value structure to a doubly linked +* circular list of such structures. The new list element is +* inserted just in front of the element occupying the "head of +* list" position (i.e. it becomes the new last element in the +* list). + +* Parameters: +* value +* Pointer to the new element. This must not already be in the +* list. +* head +* Address of a pointer to the element at the head of the list +* (this pointer should be NULL if the list is initially +* empty). This pointer will only be updated if a new element is +* being added to an empty list. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error chacking and does not +* generate errors. +*/ + +/* If the list is initially empty, the sole new element points at + itself. */ + if ( !*head ) { + value->flink = value; + value->blink = value; + +/* Update the list head to identify the new element. */ + *head = value; + +/* Otherwise, insert the new element in front of the element at the + head of the list. */ + } else { + value->flink = *head; + value->blink = ( *head )->blink; + ( *head )->blink = value; + value->blink->flink = value; + } +} + +void *astChannelData_( void ) { +/* +c++ +* Name: +* astChannelData + +* Purpose: +* Return a pointer to user-supplied data stored with a Channel. + +* Type: +* Public macro. + +* Synopsis: +* #include "channel.h" +* void *astChannelData + +* Class Membership: +* Channel macro. + +* Description: +* This macro is intended to be used within the source or sink +* functions associated with a Channel. It returns any pointer +* previously stored in the Channel (that is, the Channel that has +* invoked the source or sink function) using astPutChannelData. +* +* This mechanism is a thread-safe alternative to passing file +* descriptors, etc, via static global variables. + +* Returned Value: +* astChannelData +* The pointer previously stored with the Channel using +* astPutChannelData. A NULL pointer will be returned if no such +* pointer has been stored with the Channel. + +* Applicability: +* Channel +* This macro applies to all Channels. + +* Notes: +* - This routine is not available in the Fortran 77 interface to +* the AST library. +c-- +*/ + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + return channel_data; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Channel. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Channel member function (over-rides the astClearAttrib protected +* method inherited from the Object class). + +* Description: +* This function clears the value of a specified attribute for a +* Channel, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Channel. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstChannel *this; /* Pointer to the Channel structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Channel structure. */ + this = (AstChannel *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Comment. */ +/* -------- */ + if ( !strcmp( attrib, "comment" ) ) { + astClearComment( this ); + +/* Full. */ +/* ----- */ + } else if ( !strcmp( attrib, "full" ) ) { + astClearFull( this ); + +/* Indent. */ +/* ------- */ + } else if ( !strcmp( attrib, "indent" ) ) { + astClearIndent( this ); + +/* ReportLevel. */ +/* ------------ */ + } else if ( !strcmp( attrib, "reportlevel" ) ) { + astClearReportLevel( this ); + +/* Skip. */ +/* ----- */ + } else if ( !strcmp( attrib, "skip" ) ) { + astClearSkip( this ); + +/* SourceFile. */ +/* ----------- */ + } else if ( !strcmp( attrib, "sourcefile" ) ) { + astClearSourceFile( this ); + +/* SinkFile. */ +/* --------- */ + } else if ( !strcmp( attrib, "sinkfile" ) ) { + astClearSinkFile( this ); + +/* Strict. */ +/* ------- */ + } else if ( !strcmp( attrib, "strict" ) ) { + astClearStrict( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearValues( AstChannel *this, int *status ) { +/* +* Name: +* ClearValues + +* Purpose: +* Clear the current values list. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void ClearValues( AstChannel *this, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function clears any (un-read) Value structures remaining in +* the current values list (i.e. at the current nesting level). It +* should be invoked after all required values have been read. +* +* If the values list has not been read, or if any remaining values +* are found (i.e. the list is not empty) then this indicates an +* unrecognised input class or an input value that has not been +* read by a class loader. This implies an error in the loader, or +* bad input data, so an error is reported. +* +* All resources used by any remaining Value structures are freed +* and the values list is left in an empty state. + +* Parameters: +* this +* Pointer to the Channel being read. This is only used for +* constructing error messages. It must not be NULL. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if the global error +* status is set on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstChannelValue **head; /* Address of pointer to values list */ + AstChannelValue *value; /* Pointer to value list element */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* If "values_class" is non-NULL, then the values list has previously + been filled with Values for a class. */ + if ( values_class[ nest ] ) { + +/* If "values_ok" is zero, however, then these Values have not yet + been read by a class loader. This must be due to a bad class name + associated with them or because the class data are not available in + the correct order. If we are using strict error reporting, then report + an error (unless the error status is already set). */ + if ( astGetStrict( this ) && !values_ok[ nest ] && astOK ) { + astError( AST__BADIN, + "astRead(%s): Invalid class structure in input data.", status, + astGetClass( this ) ); + astError( AST__BADIN, + "Class \"%s\" is invalid or out of order within a %s.", status, + values_class[ nest ], object_class[ nest ] ); + } + +/* Free the memory holding the class string. */ + values_class[ nest ] = astFree( values_class[ nest ] ); + } + +/* Reset the "values_ok" flag. */ + values_ok[ nest ] = 0; + +/* Now clear any Values remaining in the values list. Obtain the + address of the pointer to the head of this list (at the current + nesting level) and loop to remove Values from the list while it is + not empty. */ + head = values_list + nest; + while ( *head ) { + +/* Obtain a pointer to the first element. */ + value = *head; + +/* Issue a warning. */ + if ( value->is_object ) { + astAddWarning( this, 1, "The Object \"%s = <%s>\" was " + "not recognised as valid input.", "astRead", status, + value->name, astGetClass( value->ptr.object ) ); + } else { + astAddWarning( this, 1, "The value \"%s = %s\" was not " + "recognised as valid input.", "astRead", status, + value->name, value->ptr.string ); + } + +/* Remove the Value structure from the list (which updates the head of + list pointer) and free its resources. */ + RemoveValue( value, head, status ); + value = FreeValue( value, status ); + } +} + +static AstChannelValue *FreeValue( AstChannelValue *value, int *status ) { +/* +* Name: +* FreeValue + +* Purpose: +* Free a dynamically allocated Value structure. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* AstChannelValue *FreeValue( AstChannelValue *value, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function frees a dynamically allocated Value structure, +* releasing all resources used by it. The structure contents must +* have been correctly initialised. + +* Parameters: +* value +* Pointer to the Value structure to be freed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A NULL pointer is always returned. + +* Notes: +* - This function attempts to execute even if the global error +* status is set on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +*/ + +/* Check that a non-NULL pointer has been supplied. */ + if ( value ) { + +/* If the "name" component has been allocated, then free it. */ + if ( value->name ) value->name = astFree( value->name ); + +/* If the "ptr" component identifies an Object, then annul the Object + pointer. */ + if ( value->is_object ) { + if ( value->ptr.object ) { + value->ptr.object = astAnnul( value->ptr.object ); + } + +/* Otherwise, if it identifies a string, then free the string. */ + } else { + if ( value->ptr.string ) { + value->ptr.string = astFree( value->ptr.string ); + } + } + +/* Free the Value structure itself. */ + value = astFree( value ); + } + +/* Return a NULL pointer. */ + return NULL; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Channel. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Channel member function (over-rides the protected astGetAttrib +* method inherited from the Object class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Channel, formatted as a character string. + +* Parameters: +* this +* Pointer to the Channel. +* attrib +* Pointer to a null terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Channel, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Channel. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstChannel *this; /* Pointer to the Channel structure */ + const char *result; /* Pointer value to return */ + int comment; /* Comment attribute value */ + int full; /* Full attribute value */ + int indent; /* Indent attribute value */ + int report_level; /* ReportLevel attribute value */ + int skip; /* Skip attribute value */ + int strict; /* Report errors insead of warnings? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Channel structure. */ + this = (AstChannel *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Comment. */ +/* -------- */ + if ( !strcmp( attrib, "comment" ) ) { + comment = astGetComment( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", comment ); + result = getattrib_buff; + } + +/* Full. */ +/* ----- */ + } else if ( !strcmp( attrib, "full" ) ) { + full = astGetFull( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", full ); + result = getattrib_buff; + } + +/* Indent. */ +/* ------- */ + } else if ( !strcmp( attrib, "indent" ) ) { + indent = astGetIndent( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", indent ); + result = getattrib_buff; + } + +/* ReportLevel. */ +/* ------------ */ + } else if ( !strcmp( attrib, "reportlevel" ) ) { + report_level = astGetReportLevel( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", report_level ); + result = getattrib_buff; + } + +/* Skip. */ +/* ----- */ + } else if ( !strcmp( attrib, "skip" ) ) { + skip = astGetSkip( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", skip ); + result = getattrib_buff; + } + +/* SourceFile. */ +/* ----------- */ + } else if ( !strcmp( attrib, "sourcefile" ) ) { + result = astGetSourceFile( this ); + +/* SinkFile. */ +/* --------- */ + } else if ( !strcmp( attrib, "sinkfile" ) ) { + result = astGetSinkFile( this ); + +/* Strict. */ +/* ------- */ + } else if ( !strcmp( attrib, "strict" ) ) { + strict = astGetStrict( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", strict ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static void GetNextData( AstChannel *this, int skip, char **name, + char **val, int *status ) { +/* +*+ +* Name: +* astGetNextData + +* Purpose: +* Read the next item of data from a data source. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astGetNextData( AstChannel *this, int skip, char **name, +* char **val ) + +* Class Membership: +* Channel method. + +* Description: +* This function reads the next item of input data from a data +* source associated with a Channel and returns the result. +* +* It is layered conceptually on the astGetNextText method, but +* instead of returning the raw input text, it decodes it and +* returns name/value pairs ready for use. Note that in some +* derived classes, where the data are not stored as text, this +* function may not actually use astGetNextText, but will access +* the data directly. + +* Parameters: +* this +* Pointer to the Channel. +* skip +* A non-zero value indicates that a new Object is to be read, +* and that all input data up to the next "Begin" item are to be +* skipped in order to locate it. This is useful if the data +* source contains AST objects interspersed with other data (but +* note that these other data cannot appear inside AST Objects, +* only between them). +* +* A zero value indicates that all input data are significant +* and the next item will therefore be read and an attempt made +* to interpret it whatever it contains. Any other data +* inter-mixed with AST Objects will then result in an error. +* name +* An address at which to store a pointer to a null-terminated +* dynamically allocated string containing the name of the next +* item in the input data stream. This name will be in lower +* case with no surrounding white space. It is the callers +* responsibilty to free the memory holding this string (using +* astFree) when it is no longer required. +* +* A NULL pointer value will be returned (without error) to +* indicate when there are no further input data items to be +* read. +* val +* An address at which to store a pointer to a null-terminated +* dynamically allocated string containing the value associated +* with the next item in the input data stream. No case +* conversion is performed on this string and all white space is +* potentially significant. It is the callers responsibilty to +* free the memory holding this string (using astFree) when it +* is no longer required. +* +* The returned pointer will be NULL if an Object data item is +* read (see the "Data Representation" section). + +* Data Representation: +* The returned data items fall into the following categories: +* +* - Begin: Identified by the name string "begin", this indicates +* the start of an Object definition. The associated value string +* gives the class name of the Object being defined. +* +* - IsA: Identified by the name string "isa", this indicates the +* end of the data associated with a particular class structure +* within the definiton of a larger Object. The associated value +* string gives the name of the class whose data have just been +* read. +* +* - End: Identified by the name string "end", this indicates the +* end of the data associated with a complete Object +* definition. The associated value string gives the class name of +* the Object whose definition is being ended. +* +* - Non-Object: Identified by any other name string plus a +* non-NULL "val" pointer, this gives the value of a non-Object +* structure component (instance variable). The name identifies +* which instance variable it is (within the context of the class +* whose data are being read) and the value is encoded as a string. +* +* - Object: Identified by any other name string plus a NULL "val" +* pointer, this identifies the value of an Object structure +* component (instance variable). The name identifies which +* instance variable it is (within the context of the class whose +* data are being read) and the value is given by subsequent data +* items (so the next item should be a "Begin" item). + +* Notes: +* - NULL pointer values will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +* - This method is provided primarily so that derived classes may +* over-ride it in order to read from alternative data sources. It +* provides a higher-level interface than astGetNextText, so is +* suitable for classes that either need to read textual data in a +* different format, or to read from non-textual data sources. +*- +*/ + +/* Local Variables: */ + char *line; /* Pointer to input text line */ + int done; /* Data item read? */ + int i; /* Loop counter for string characters */ + int len; /* Length of input text line */ + int nc1; /* Offset to start of first field */ + int nc2; /* Offset to end of first field */ + int nc3; /* Offset to start of second field */ + int nc; /* Number of charaters read by "astSscanf" */ + +/* Initialise the returned values. */ + *name = NULL; + *val = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Read the next input line as text (the loop is needed to allow + initial lines to be skipped if the "skip" flag is set). */ + done = 0; + while ( !done && ( line = InputTextItem( this, status ) ) && astOK ) { + +/* If OK, determine the line length. */ + len = strlen( line ); + +/* Non-Object value. */ +/* ----------------- */ +/* Test for lines of the form " name = value" (or similar), where the + name is no more than MAX_NAME characters long (the presence of a + value on the right hand side indicates that this is a non-Object + value, encoded as a string). Ignore these lines if the "skip" flag + is set. */ + if ( nc = 0, + ( !skip + && ( 0 == astSscanf( line, + " %n%*" MAX_NAME "[^ \t=]%n = %n%*[^\n]%n", + &nc1, &nc2, &nc3, &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Extract the name and value fields. */ + *name = astString( line + nc1, nc2 - nc1 ); + *val = astString( line + nc3, len - nc3 ); + +/* If OK, truncate the value to remove any trailing white space. */ + if ( astOK ) { + i = len - nc3 - 1; + while ( ( i >= 0 ) && isspace( ( *val )[ i ] ) ) i--; + ( *val )[ i + 1 ] = '\0'; + +/* Also remove any quotes from the string. */ + Unquote( this, *val, status ); + } + +/* Object value. */ +/* ------------- */ +/* Test for lines of the form " name = " (or similar), where the name + is no more than MAX_NAME characters long (the absence of a value on + the right hand side indicates that this is an Object, whose + definition follows on subsequent input lines). Ignore these lines + if the "skip" flag is set. */ + } else if ( nc = 0, + ( !skip + && ( 0 == astSscanf( line, + " %n%*" MAX_NAME "[^ \t=]%n = %n", + &nc1, &nc2, &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Extract the name field but leave the value pointer as NULL. */ + *name = astString( line + nc1, nc2 - nc1 ); + +/* Begin. */ +/* ------ */ +/* Test for lines of the form " Begin Class " (or similar). */ + } else if ( nc = 0, + ( ( 0 == astSscanf( line, + " %*1[Bb]%*1[Ee]%*1[Gg]%*1[Ii]%*1[Nn] %n%*s%n %n", + &nc1, &nc2, &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Set the returned name to "begin" and extract the associated class + name for the value. Store both of these in dynamically allocated + strings. */ + *name = astString( "begin", 5 ); + *val = astString( line + nc1, nc2 - nc1 ); + +/* IsA. */ +/* ---- */ +/* Test for lines of the form " IsA Class " (or similar). Ignore these + lies if the "skip" flag is set. */ + } else if ( nc = 0, + ( !skip + && ( 0 == astSscanf( line, + " %*1[Ii]%*1[Ss]%*1[Aa] %n%*s%n %n", + &nc1, &nc2, &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Set the returned name to "isa" and extract the associated class + name for the value. */ + *name = astString( "isa", 3 ); + *val = astString( line + nc1, nc2 - nc1 ); + +/* End. */ +/* ---- */ +/* Test for lines of the form " End Class " (or similar). Ignore these + lines if the "skip" flag is set. */ + } else if ( nc = 0, + ( !skip + && ( 0 == astSscanf( line, + " %*1[Ee]%*1[Nn]%*1[Dd] %n%*s%n %n", + &nc1, &nc2, &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* If found, set the returned name to "end" and extract the associated + class name for the value. */ + *name = astString( "end", 3 ); + *val = astString( line + nc1, nc2 - nc1 ); + +/* If the input line didn't match any of the above and the "skip" flag + is not set, then report an error. */ + } else if ( !skip ) { + astError( AST__BADIN, + "astRead(%s): Cannot interpret the input data: \"%s\".", status, + astGetClass( this ), line ); + } + +/* Free the memory holding the input data as text. */ + line = astFree( line ); + } + +/* If successful, convert the name to lower case. */ + if ( astOK && *name ) { + for ( i = 0; ( *name )[ i ]; i++ ) { + ( *name )[ i ] = tolower( ( *name )[ i ] ); + } + } + +/* If an error occurred, ensure that any memory allocated is freed and + that NULL pointer values are returned. */ + if ( !astOK ) { + *name = astFree( *name ); + *val = astFree( *val ); + } +} + +static char *GetNextText( AstChannel *this, int *status ) { +/* +*+ +* Name: +* GetNextText + +* Purpose: +* Read the next line of input text from a data source. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* char *astGetNextText( AstChannel *this ) + +* Class Membership: +* Channel method. + +* Description: +* This function reads the next "raw" input line of text from the +* data source associated with a Channel. +* +* Each line is returned as a pointer to a null-terminated string +* held in dynamic memory, and it is the caller's responsibility to +* free this memory (using astFree) when it is no longer +* required. A NULL pointer is returned if there are no more input +* lines to be read. + +* Parameters: +* this +* Pointer to the Channel. + +* Returned Value: +* Pointer to a null-terminated string containing the input line +* (held in dynamically allocated memory, which must be freed by +* the caller when no longer required). A NULL pointer is returned +* if there are no more input lines to be read. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - This method is provided primarily so that derived classes may +* over-ride it in order to read from alternative (textual) data +* sources. +*- +*/ + +/* Local Constants: */ +#define MIN_CHARS 81 /* Initial size for allocating memory */ +#define ERRBUF_LEN 80 + +/* Local Variables: */ + FILE *fd; /* Input file descriptor */ + char *errstat; /* Pointer for system error message */ + char *line; /* Pointer to line data to be returned */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + const char *sink_file; /* Path to output sink file */ + const char *source_file; /* Path to source file */ + int c; /* Input character */ + int len; /* Length of input line */ + int readstat; /* "errno" value set by "getchar" */ + int size; /* Size of allocated memory */ + +/* Initialise. */ + line = NULL; + +/* Check the global error status. */ + if ( !astOK ) return line; + +/* If the SourceFile attribute of the Channel specifies an input file, + but no input file has yet been opened, open it now. Report an error if + it is the same as the sink file. */ + if( astTestSourceFile( this ) && !this->fd_in ) { + source_file = astGetSourceFile( this ); + + if( this->fd_out ) { + sink_file = astGetSinkFile( this ); + if( astOK && !strcmp( sink_file, source_file ) ) { + astError( AST__RDERR, "astRead(%s): Failed to open input " + "SourceFile '%s' - the file is currently being used " + "as the output SinkFile.", status, astGetClass( this ), + source_file ); + } + } + + if( astOK ) { + this->fd_in = fopen( source_file, "r" ); + if( !this->fd_in ) { + if ( errno ) { +#if HAVE_STRERROR_R + strerror_r( errno, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( errno ); +#endif + astError( AST__RDERR, "astRead(%s): Failed to open input " + "SourceFile '%s' - %s.", status, astGetClass( this ), + source_file, errstat ); + } else { + astError( AST__RDERR, "astRead(%s): Failed to open input " + "SourceFile '%s'.", status, astGetClass( this ), + source_file ); + } + } + + } + } + +/* Source function defined, but no input file. */ +/* ------------------------------------------- */ +/* If no active input file descriptor is stored in the Channel, but + a source function (and its wrapper function) is defined for the + Channel, use the wrapper function to invoke the source function to + read a line of input text. This is returned in a dynamically + allocated string. */ + if ( !this->fd_in && this->source && this->source_wrap ) { + +/* About to call an externally supplied function which may not be + thread-safe, so lock a mutex first. Also store the channel data + pointer in a global variable so that it can be accessed in the source + function using macro astChannelData. */ + astStoreChannelData( this ); + LOCK_MUTEX3; + line = ( *this->source_wrap )( this->source, status ); + UNLOCK_MUTEX3; + +/* Input file defined, or no source function. */ +/* ------------------------------------------ */ +/* Read the line from the input file or from standard input. */ + } else if( astOK ) { + c = '\0'; + len = 0; + size = 0; + +/* Choose the file descriptor to use. */ + fd = this->fd_in ? this->fd_in : stdin; + +/* Loop to read input characters, saving any "errno" value that may be + set by "getchar" if an error occurs. Quit if an end of file (or + error) occurs or if a newline character is read. */ + while ( errno = 0, c = getc( fd ), readstat = errno, + ( c != EOF ) && ( c != '\n' ) ) { + +/* If no memory has yet been allocated to hold the line, allocate some + now, using MIN_CHARS as the initial line length. */ + if ( !line ) { + line = astMalloc( sizeof( char ) * (size_t) MIN_CHARS ); + size = MIN_CHARS; + +/* If memory has previously been allocated, extend it when necessary + to hold the new input character (plus a terminating null) and note + the new size. */ + } else if ( ( len + 2 ) > size ) { + line = astGrow( line, len + 2, sizeof( char ) ); + if ( !astOK ) break; + size = (int) astSizeOf( line ); + } + +/* Store the character just read. */ + line[ len++ ] = c; + } + +/* If the above loop completed without setting the global error + status, check the last character read and use "ferror" to see if a + read error occurred. If so, report the error, using the saved + "errno" value (but only if one was set). */ + if ( astOK && ( c == EOF ) && ferror( fd ) ) { + if ( readstat ) { +#if HAVE_STRERROR_R + strerror_r( readstat, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( readstat ); +#endif + astError( AST__RDERR, + "astRead(%s): Read error on standard input - %s.", status, + astGetClass( this ), errstat ); + } else { + astError( AST__RDERR, + "astRead(%s): Read error on standard input.", status, + astGetClass( this ) ); + } + } + +/* If an empty line has been read, allocate memory to hold an empty + string. */ + if ( !line && ( c == '\n' ) ) { + line = astMalloc( sizeof( char ) ); + } + +/* If memory has been allocated and there has been no error, + null-terminate the string of input characters. */ + if ( line ) { + if ( astOK ) { + line[ len ] = '\0'; + +/* If there has been an error, free the allocated memory. */ + } else { + line = astFree( line ); + } + } + } + + +/* Return the result pointer. */ + return line; + +/* Undefine macros local to this function. */ +#undef MIN_CHARS +#undef ERRBUF_LEN +} + +static AstKeyMap *Warnings( AstChannel *this, int *status ){ +/* +*++ +* Name: +c astWarnings +f AST_WARNINGS + +* Purpose: +* Returns any warnings issued by the previous read or write operation. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "channel.h" +c AstKeyMap *astWarnings( AstChannel *this ) +f RESULT = AST_WARNINGS( THIS, STATUS ) + +* Class Membership: +* Channel member function. + +* Description: +* This function returns an AST KeyMap object holding the text of any +* warnings issued as a result of the previous invocation of the +c astRead or astWrite +f AST_READ or AST_WRITE +* function on the Channel. If no warnings were issued, a +c a NULL value +f AST__NULL +* will be returned. +* +* Such warnings are non-fatal and will not prevent the +* read or write operation succeeding. However, the converted object +* may not be identical to the original object in all respects. +* Differences which would usually be deemed as insignificant in most +* usual cases will generate a warning, whereas more significant +* differences will generate an error. +* +* The "Strict" attribute allows this warning facility to be switched +* off, so that a fatal error is always reported for any conversion +* error. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Channel. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astWarnings() +f AST_WARNINGS = INTEGER +* A pointer to the KeyMap holding the warning messages, or +c NULL +f AST__NULL +* if no warnings were issued during the previous read operation. + +* Applicability: +* Channel +* The basic Channel class generates a warning when ever an +* un-recognised item is encountered whilst reading an Object from +* an external data source. If Strict is zero (the default), then +* unexpected items in the Object description are simply ignored, +* and any remaining items are used to construct the returned +* Object. If Strict is non-zero, an error will be reported and a +* NULL Object pointer returned if any unexpected items are +* encountered. +* +* As AST continues to be developed, new attributes are added +* occasionally to selected classes. If an older version of AST is +* used to read external Object descriptions created by a more +* recent version of AST, then the Channel class will, by default, +* ignore the new attributes, using the remaining attributes to +* construct the Object. This is usually a good thing. However, +* since external Object descriptions are often stored in plain +* text, it is possible to edit them using a text editor. This +* gives rise to the possibility of genuine errors in the +* description due to finger-slips, typos, or simple +* mis-understanding. Such inappropriate attributes will be ignored +* if Strict is left at its default zero value. This will cause the +* mis-spelled attribute to revert to its default value, +* potentially causing subtle changes in the behaviour of +* application software. If such an effect is suspected, the Strict +* attribute can be set non-zero, resulting in the erroneous +* attribute being identified in an error message. +* FitsChan +* The returned KeyMap will contain warnings for all conditions +* listed in the Warnings attribute. +* XmlChan +* Reports conversion errors that result in what are usally +* insignificant changes. + +* Notes: +* - The returned KeyMap uses keys of the form "Warning_1", +* "Warning_2", etc. +* - A value of +c NULL will be returned if this function is invoked with the AST +c error status set, +f AST__NULL will be returned if this function is invoked with STATUS +f set to an error value, +* or if it should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstKeyMap *result; + char key[ 20 ]; + int i; + +/* Check the global status, and supplied keyword name. */ + result = NULL; + if( !astOK ) return result; + +/* Check there are some warnings to return. */ + if( this->nwarn && this->warnings ) { + +/* Create the KeyMap. */ + result = astKeyMap( "", status ); + +/* Loop round all warnings, adding them into the KeyMap. */ + for( i = 0; i < this->nwarn; i++ ){ + sprintf( key, "Warning_%d", i + 1 ); + astMapPut0C( result, key, (this->warnings)[ i ], " " ); + } + } + +/* Return the KeyMap. */ + return result; +} + +AstChannel *astInitChannel_( void *mem, size_t size, int init, + AstChannelVtab *vtab, const char *name, + const char *(* source)( void ), + char *(* source_wrap)( const char *(*)( void ), int * ), + void (* sink)( const char * ), + void (* sink_wrap)( void (*)( const char * ), + const char *, int * ), int *status ) { +/* +*+ +* Name: +* astInitChannel + +* Purpose: +* Initialise a Channel. + +* Type: +* Protected function. + +* Synopsis: +* #include "channel.h" +* AstChannel *astInitChannel( void *mem, size_t size, int init, +* AstChannelVtab *vtab, const char *name, +* const char *(* source)( void ), +* char *(* source_wrap)( const char *(*)( void ), int * ), +* void (* sink)( const char * ), +* void (* sink_wrap)( void (*)( const char * ), +* const char *, int * ) ) + +* Class Membership: +* Channel initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new Channel object. It allocates memory (if +* necessary) to accommodate the Channel plus any additional data +* associated with the derived class. It then initialises a +* Channel structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for a Channel at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Channel is to be +* initialised. This must be of sufficient size to accommodate +* the Channel data (sizeof(Channel)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Channel (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Channel structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A boolean flag indicating if the Channel's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Channel. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* source +* Pointer to a "source" function which will be used to obtain +* lines of input text. Generally, this will be obtained by +* casting a pointer to a source function which is compatible +* with the "source_wrap" wrapper function (below). The pointer +* should later be cast back to its original type by the +* "source_wrap" function before the function is invoked. +* +* If "source" is NULL, the Channel will read from standard +* input instead. +* source_wrap +* Pointer to a function which can be used to invoke the +* "source" function supplied (above). This wrapper function is +* necessary in order to hide variations in the nature of the +* source function, such as may arise when it is supplied by a +* foreign (non-C) language interface. +* +* The single parameter of the "source_wrap" function is a +* pointer to the "source" function, and it should cast this +* function pointer (as necessary) and invoke the function with +* appropriate arguments to obtain the next line of input +* text. The "source_wrap" function should then return a pointer +* to a dynamically allocated, null terminated string containing +* the text that was read. The string will be freed (using +* astFree) when no longer required and the "source_wrap" +* function need not concern itself with this. A NULL pointer +* should be returned if there is no more input to read. +* +* If "source_wrap" is NULL, the Channel will read from standard +* input instead. +* sink +* Pointer to a "sink" function which will be used to deliver +* lines of output text. Generally, this will be obtained by +* casting a pointer to a sink function which is compatible with +* the "sink_wrap" wrapper function (below). The pointer should +* later be cast back to its original type by the "sink_wrap" +* function before the function is invoked. +* +* If "sink" is NULL, the Channel will write to standard output +* instead. +* sink_wrap +* Pointer to a function which can be used to invoke the "sink" +* function supplied (above). This wrapper function is necessary +* in order to hide variations in the nature of the sink +* function, such as may arise when it is supplied by a foreign +* (non-C) language interface. +* +* The first parameter of the "sink_wrap" function is a pointer +* to the "sink" function, and the second parameter is a pointer +* to a const, null-terminated character string containing the +* text to be written. The "sink_wrap" function should cast the +* "sink" function pointer (as necessary) and invoke the +* function with appropriate arguments to deliver the line of +* output text. The "sink_wrap" function then returns void. +* +* If "sink_wrap" is NULL, the Channel will write to standard +* output instead. + +* Returned Value: +* A pointer to the new Channel. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstChannel *new; /* Pointer to new Channel */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitChannelVtab( vtab, name ); + +/* Initialise an Object structure (the parent class) as the first + component within the Channel structure, allocating memory if + necessary. */ + new = (AstChannel *) astInitObject( mem, size, 0, + (AstObjectVtab *) vtab, name ); + + if ( astOK ) { + +/* Initialise the Channel data. */ +/* ---------------------------- */ +/* Save the pointers to the source and sink functions and the wrapper + functions that invoke them. */ + new->source = source; + new->source_wrap = source_wrap; + new->sink = sink; + new->sink_wrap = sink_wrap; + +/* Indicate no input or output files have been associated with the + Channel. */ + new->fd_in = NULL; + new->fn_in = NULL; + new->fd_out = NULL; + new->fn_out = NULL; + +/* Set all attributes to their undefined values. */ + new->comment = -INT_MAX; + new->full = -INT_MAX; + new->indent = -INT_MAX; + new->report_level = -INT_MAX; + new->skip = -INT_MAX; + new->strict = -INT_MAX; + new->data = NULL; + new->warnings = NULL; + new->nwarn = 0; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +void astInitChannelVtab_( AstChannelVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitChannelVtab + +* Purpose: +* Initialise a virtual function table for a Channel. + +* Type: +* Protected function. + +* Synopsis: +* #include "channel.h" +* void astInitChannelVtab( AstChannelVtab *vtab, const char *name ) + +* Class Membership: +* Channel vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Channel class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitObjectVtab( (AstObjectVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAChannel) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstObjectVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->AddWarning = AddWarning; + vtab->ClearComment = ClearComment; + vtab->ClearFull = ClearFull; + vtab->ClearSkip = ClearSkip; + vtab->ClearStrict = ClearStrict; + vtab->GetComment = GetComment; + vtab->GetFull = GetFull; + vtab->GetNextData = GetNextData; + vtab->GetNextText = GetNextText; + vtab->GetSkip = GetSkip; + vtab->GetStrict = GetStrict; + vtab->Warnings = Warnings; + vtab->PutNextText = PutNextText; + vtab->Read = Read; + vtab->ReadClassData = ReadClassData; + vtab->ReadDouble = ReadDouble; + vtab->ReadInt = ReadInt; + vtab->ReadObject = ReadObject; + vtab->ReadString = ReadString; + vtab->SetComment = SetComment; + vtab->SetFull = SetFull; + vtab->SetSkip = SetSkip; + vtab->SetStrict = SetStrict; + vtab->TestComment = TestComment; + vtab->TestFull = TestFull; + vtab->TestSkip = TestSkip; + vtab->TestStrict = TestStrict; + vtab->Write = Write; + vtab->WriteBegin = WriteBegin; + vtab->WriteDouble = WriteDouble; + vtab->WriteEnd = WriteEnd; + vtab->WriteInt = WriteInt; + vtab->WriteIsA = WriteIsA; + vtab->WriteObject = WriteObject; + vtab->WriteString = WriteString; + vtab->PutChannelData = PutChannelData; + + vtab->ClearReportLevel = ClearReportLevel; + vtab->GetReportLevel = GetReportLevel; + vtab->SetReportLevel = SetReportLevel; + vtab->TestReportLevel = TestReportLevel; + + vtab->ClearIndent = ClearIndent; + vtab->GetIndent = GetIndent; + vtab->SetIndent = SetIndent; + vtab->TestIndent = TestIndent; + + vtab->ClearSourceFile = ClearSourceFile; + vtab->GetSourceFile = GetSourceFile; + vtab->SetSourceFile = SetSourceFile; + vtab->TestSourceFile = TestSourceFile; + + vtab->ClearSinkFile = ClearSinkFile; + vtab->GetSinkFile = GetSinkFile; + vtab->SetSinkFile = SetSinkFile; + vtab->TestSinkFile = TestSinkFile; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + +/* Declare the destructor and copy constructor. */ + astSetDelete( (AstObjectVtab *) vtab, Delete ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + +/* Declare the Dump function for this class. There is no destructor or + copy constructor. */ + astSetDump( vtab, Dump, "Channel", "Basic I/O Channel" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static char *InputTextItem( AstChannel *this, int *status ) { +/* +* Name: +* InputTextItem + +* Purpose: +* Read the next item from a data source as text. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* char *InputTextItem( AstChannel *this ) + +* Class Membership: +* Channel member function. + +* Description: +* This function reads the next input data item as text from the +* data source associated with a Channel. It is similar to the +* astGetNextText method (which it invokes), except that it strips +* off any comments along with leading and trailing white +* space. Input lines which are empty or do not contain significant +* characters (e.g. all comment) are skipped, so that only +* significant lines are returned. +* +* Each line is returned as a pointer to a null-terminated string +* held in dynamic memory, and it is the caller's responsibility to +* free this memory (using astFree) when it is no longer +* required. A NULL pointer is returned if there are no more input +* lines to be read. + +* Parameters: +* this +* Pointer to the Channel. + +* Returned Value: +* Pointer to a null-terminated string containing the input line +* (held in dynamically allocated memory, which must be freed by +* the caller when no longer required). A NULL pointer is returned +* if there are no more input lines to be read. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + char *line; /* Pointer to line data to be returned */ + int i; /* Loop counter for line characters */ + int j; /* Counter for characters */ + int len; /* Length of result line */ + int nonspace; /* Non-space character encountered? */ + int quoted; /* Character is inside quotes? */ + +/* Initialise. */ + line = NULL; + +/* Check the global error status. */ + if ( !astOK ) return line; + +/* Loop to read input lines until one is found which contains useful + characters or end of file is reached (or a read error occurs). */ + while ( !line && ( line = astGetNextText( this ) ) && astOK ) { + +/* Loop to remove comments and leading and trailing white space. */ + len = 0; + nonspace = 0; + quoted = 0; + for ( i = j = 0; line[ i ]; i++ ) { + +/* Note quote characters and ignore all text after the first unquoted + comment character. */ + if ( line[ i ] == '"' ) quoted = !quoted; + if ( ( line[ i ] == '#' ) && !quoted ) break; + +/* Note the first non-space character and ignore everything before + it. */ + if ( ( nonspace = nonspace || !isspace( line[ i ] ) ) ) { + +/* Move each character to its new position in the string. */ + line[ j++ ] = line[ i ]; + +/* Note the final length of the string (ignoring trailing spaces). */ + if ( !isspace( line[ i ] ) ) len = j; + } + } + +/* If the string is not empty, terminate it. */ + if ( len ) { + line[ len ] = '\0'; + +/* Otherwise, free the memory used for the string so that another + input line will be read. */ + } else { + line = astFree( line ); + } + } + +/* Return the result pointer. */ + return line; + +/* Undefine macros local to this function. */ +#undef MIN_CHARS +} + +static AstChannelValue *LookupValue( const char *name, int *status ) { +/* +* Name: +* LookupValue + +* Purpose: +* Look up a Value structure by name. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* AstChannelValue *LookupValue( const char *name ) + +* Class Membership: +* Channel member function. + +* Description: +* This function searches the current values list (i.e. at the +* current nesting level) to identify a Value structure with a +* specified name. If one is found, it is removed from the list and +* a pointer to it is returned. If no suitable Value can be found, +* a NULL pointer is returned instead. + +* Parameters: +* name +* Pointer to a constant null-terminated character string +* containing the name of the required Value. This must be in +* lower case with no surrounding white space. Note that names +* longer than NAME_MAX characters will not match any Value. + +* Returned value: +* Pointer to the required Value structure, or NULL if no suitable +* Value exists. + +* Notes: +* - The returned pointer refers to a dynamically allocated +* structure and it is the callers responsibility to free this when +* no longer required. The FreeValue function must be used for this +* purpose. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstChannelValue **head; /* Address of head of list pointer */ + AstChannelValue *result; /* Pointer value to return */ + AstChannelValue *value; /* Pointer to list element */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Check that the "values_ok" flag is set. If not, the Values in the + values list belong to a different class to that of the current + class loader, so we cannot return any Value. */ + if ( values_ok[ nest ] ) { + +/* Obtain the address of the current "head of list" pointer for the + values list (at the current nesting level). */ + head = values_list + nest; + +/* Obtain the head of list pointer itself and check the list is not + empty. */ + if ( ( value = *head ) ) { + +/* Loop to inspect each list element. */ + while ( 1 ) { + +/* If a name match is found, remove the element from the list, return + a pointer to it and quit searching. */ + if ( !strcmp( name, value->name ) ) { + RemoveValue( value, head, status ); + result = value; + break; + } + +/* Follow the list until we return to the head. */ + value = value->flink; + if ( value == *head ) break; + } + } + } + +/* Return the result. */ + return result; +} + +static void OutputTextItem( AstChannel *this, const char *line, int *status ) { +/* +* Name: +* OutputTextItem + +* Purpose: +* Output a data item formatted as text. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void OutputTextItem( AstChannel *this, const char *line, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function outputs a data item formatted as a text string to +* a data sink associated with a Channel. It keeps track of the +* number of items written. + +* Parameters: +* this +* Pointer to the Channel. +* line +* Pointer to a constant null-terminated string containing the +* data item to be output (no newline character should be +* appended). +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Write out the line of text using the astPutNextText method (which + may be over-ridden). */ + astPutNextText( this, line ); + +/* If successful, increment the count of items written. */ + if ( astOK ) items_written++; +} + +static void PutChannelData( AstChannel *this, void *data, int *status ) { +/* +c++ +* Name: +* astPutChannelData + +* Purpose: +* Store arbitrary data to be passed to a source or sink function. + +* Type: +* Public function. + +* Synopsis: +* #include "channel.h" +* void astPutChannelData( AstChannel *this, void *data ) + +* Class Membership: +* Channel method. + +* Description: +* This function stores a supplied arbitrary pointer in the Channel. +* When a source or sink function is invoked by the Channel, the +* invoked function can use the astChannelData macro to retrieve the +* pointer. This provides a thread-safe alternative to passing file +* descriptors, etc, via global static variables. + +* Parameters: +* this +* Pointer to the Channel. +* data +* A pointer to be made available to the source and sink functions +* via the astChannelData macro. May be NULL. + +* Applicability: +* Channel +* All Channels have this function. + +* Notes: +* - This routine is not available in the Fortran 77 interface to +* the AST library. +c-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the pointer. */ + this->data = data; +} + +static void PutNextText( AstChannel *this, const char *line, int *status ) { +/* +*+ +* Name: +* astPutNextText + +* Purpose: +* Write a line of output text to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astPutNextText( AstChannel *this, const char *line ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes an output line of text to a data sink +* associated with a Channel. + +* Parameters: +* this +* Pointer to the Channel. +* line +* Pointer to a constant null-terminated string containing the +* line of output text to be written (no newline character +* should be appended). + +* Notes: +* - This method is provided primarily so that derived classes may +* over-ride it in order to write to alternative (textual) data +* sinks. +*- +*/ + +/* Local Constants: */ +#define ERRBUF_LEN 80 + +/* Local Variables: */ + char *errstat; /* Pointer for system error message */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + const char *sink_file; /* Path to output sink file */ + const char *source_file; /* Path to output source file */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If the SinkFile attribute of the Channel specifies an output file, + but no output file has yet been opened, open it now. Report an error + if it is the same as the source file. */ + if( astTestSinkFile( this ) && !this->fd_out ) { + sink_file = astGetSinkFile( this ); + + if( this->fd_out ) { + source_file = astGetSourceFile( this ); + if( astOK && !strcmp( sink_file, source_file ) ) { + astError( AST__WRERR, "astWrite(%s): Failed to open output " + "SinkFile '%s' - the file is currently being used " + "as the input SourceFile.", status, astGetClass( this ), + sink_file ); + } + } + + if( astOK ) { + this->fd_out = fopen( sink_file, "w" ); + if( !this->fd_out ) { + if ( errno ) { +#if HAVE_STRERROR_R + strerror_r( errno, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( errno ); +#endif + astError( AST__WRERR, "astWrite(%s): Failed to open output " + "SinkFile '%s' - %s.", status, astGetClass( this ), + sink_file, errstat ); + } else { + astError( AST__WRERR, "astWrite(%s): Failed to open output " + "SinkFile '%s'.", status, astGetClass( this ), + sink_file ); + } + } + } + } + +/* Check no error occurred above. */ + if( astOK ) { + +/* If an active output file descriptor is stored in the channel, write + the text to it, with a newline appended. */ + if( this->fd_out ) { + (void) fprintf( this->fd_out, "%s\n", line ); + +/* Otherwise, if a sink function (and its wrapper function) is defined for + the Channel, use the wrapper function to invoke the sink function to + output the text line. Since we are about to call an externally supplied + function which may not be thread-safe, lock a mutex first. Also store + the channel data pointer in a global variable so that it can be accessed + in the source function using macro astChannelData. */ + } else if ( this->sink && this->sink_wrap ) { + astStoreChannelData( this ); + LOCK_MUTEX2; + ( *this->sink_wrap )( *this->sink, line, status ); + UNLOCK_MUTEX2; + +/* Otherwise, simply write the text to standard output with a newline + appended. */ + } else { + (void) printf( "%s\n", line ); + } + } +} + +static AstObject *Read( AstChannel *this, int *status ) { +/* +*++ +* Name: +c astRead +f AST_READ + +* Purpose: +* Read an Object from a Channel. + +* Type: +* Public function. + +* Synopsis: +c #include "channel.h" +c AstObject *astRead( AstChannel *this ) +f RESULT = AST_READ( THIS, STATUS ) + +* Class Membership: +* Channel method. + +* Description: +* This function reads the next Object from a Channel and returns a +* pointer to the new Object. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Channel. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astRead() +f AST_READ = INTEGER +* A pointer to the new Object. The class to which this will +* belong is determined by the input data, so is not known in +* advance. + +* Applicability: +* FitsChan +c All successful use of astRead on a FitsChan is destructive, so that +f All successful use of AST_READ on a FitsChan is destructive, so that +* FITS header cards are consumed in the process of reading an Object, +* and are removed from the FitsChan (this deletion can be prevented +* for specific cards by calling the FitsChan +c astRetainFits function). +f AST_RETAINFITS routine). +* An unsuccessful call of +c astRead +f AST_READ +* (for instance, caused by the FitsChan not containing the necessary +* FITS headers cards needed to create an Object) results in the +* contents of the FitsChan being left unchanged. +* StcsChan +* The AST Object returned by a successful use of +c astRead +f AST_READ +* on an StcsChan, will be either a Region or a KeyMap, depending +* on the values of the StcsArea, StcsCoords and StcsProps +* attributes. See the documentation for these attributes for further +* information. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned, without +* error, if the Channel contains no further Objects to be read. +* - A null Object pointer will also be returned if this function +c is invoked with the AST error status set, or if it should fail +f is invoked with STATUS set to an error value, or if it should fail +* for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstLoaderType *loader; /* Pointer to loader for Object */ + AstObject *new; /* Pointer to new Object */ + char *class; /* Pointer to Object class name string */ + char *name; /* Pointer to data item name */ + int skip; /* Skip non-AST data? */ + int top; /* Reading top-level Object definition? */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Determine if we are reading a top-level (i.e. user-level) Object + definition, as opposed to the definition of an Object contained + within another Object. This is indicated by the current nesting + level. */ + top = ( nest == -1 ); + +/* If reading a top-level object, determine if data lying in between + Object definitions in the input data stream are to be skipped. */ + skip = ( top && astGetSkip( this ) ); + +/* Read the next input data item. If we are reading a top-level Object + definition, skip any unrelated data beforehand. Otherwise read the + data strictly as it comes (there should be no unrelated data + embedded within Object definitions themselves). */ + astGetNextData( this, skip, &name, &class ); + +/* If no suitable data item was found (and no error occurred), we have + reached the end of data. For a top-level Object a NULL Object + pointer is simply returned, but for a nested Object this indicates + that part of the Object definition is missing, so report an + error. */ + if ( astOK ) { + if ( !name ) { + if ( !top ) { + astError( AST__EOCHN, + "astRead(%s): End of input encountered while trying to " + "read an AST Object.", status, astGetClass( this ) ); + } + +/* If a data item was found, check it is a "Begin" item. If not, there + is a data item missing, so report an error and free all memory. */ + } else if ( strcmp( name, "begin" ) ) { + astError( AST__BADIN, + "astRead(%s): Missing \"Begin\" when expecting an Object.", status, + astGetClass( this ) ); + name = astFree( name ); + if ( class ) class = astFree( class ); + +/* If the required "Begin" item was found, free the memory used for the + name string. */ + } else { + name = astFree( name ); + +/* Use the associated class name to locate the loader for that + class. This function will then be used to build the Object. */ + loader = astGetLoader( class, status ); + +/* Extend all necessary stack arrays to accommodate entries for the + next nesting level (this allocates space if none has yet been + allocated). */ + end_of_object = astGrow( end_of_object, nest + 2, sizeof( int ) ); + object_class = astGrow( object_class, nest + 2, sizeof( char * ) ); + values_class = astGrow( values_class, nest + 2, sizeof( char * ) ); + values_list = astGrow( values_list, nest + 2, sizeof( AstChannelValue * ) ); + values_ok = astGrow( values_ok, nest + 2, sizeof( int ) ); + +/* If an error occurred, free the memory used by the class string, + which will not now be used. */ + if ( !astOK ) { + class = astFree( class ); + +/* Otherwise, increment the nesting level and initialise the new stack + elements for this new level. This includes clearing the + "end_of_object" flag so that ReadClassData can read more data, and + storing the class name of the object we are about to read. */ + } else { + nest++; + end_of_object[ nest ] = 0; + object_class[ nest ] = class; + values_class[ nest ] = NULL; + values_list[ nest ] = NULL; + values_ok[ nest ] = 0; + +/* Invoke the loader, which reads the Object definition from the input + data stream and builds the Object. Supply NULL/zero values to the + loader so that it will substitute values appropriate to its own + class. */ + new = (*loader)( NULL, (size_t) 0, NULL, NULL, this, status ); + +/* Clear the values list for the current nesting level. If the list + has not been read or any Values remain in it, an error will + result. */ + ClearValues( this, status ); + +/* If no error has yet occurred, check that the "end_of_object" flag + has been set. If not, the input data were not correctly terminated, + so report an error. */ + if ( astOK && !end_of_object[ nest ] ) { + astError( AST__BADIN, + "astRead(%s): Unexpected end of input (missing end " + "of %s).", status, + astGetClass( this ), object_class[ nest ] ); + } + +/* If an error occurred, report contextual information. Only do this + for top-level Objects to avoid multple messages. */ + if ( !astOK && top ) { + astError( astStatus, "Error while reading a %s from a %s.", status, + class, astGetClass( this ) ); + } + +/* Clear the Object's class string, freeing the associated memory + (note this is the memory allocated for the "class" string + earlier). */ + object_class[ nest ] = astFree( object_class[ nest ] ); + +/* Restore the previous nesting level. */ + nest--; + } + +/* Once the top-level Object has been built, free the memory used by + the stack arrays. */ + if ( top ) { + end_of_object = astFree( end_of_object ); + object_class = astFree( object_class ); + values_class = astFree( values_class ); + values_list = astFree( values_list ); + values_ok = astFree( values_ok ); + } + } + } + +/* If an error occurred, clean up by deleting the new Object and + return a NULL pointer. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the pointer to the new Object. */ + return new; +} + +static void ReadClassData( AstChannel *this, const char *class, int *status ) { +/* +*+ +* Name: +* astReadClassData + +* Purpose: +* Read values from a data source for a class loader. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astReadClassData( AstChannel *this, const char *class ) + +* Class Membership: +* Channel method. + +* Description: +* This function reads the data for a class from the data source +* associated with a Channel, so as to provide values for +* initialising the instance variables of that class as part of +* building a complete Object. This function should be invoked by +* the loader for each class immediately before it attempts to read +* these values. +* +* The values read are placed into the current values list by this +* function. They may then be read from this list by the class +* loader making calls to astReadDouble, astReadInt, astReadObject +* and astReadString. The order in which values are read by the +* loader is unimportant (although using the same order for reading +* as for writing will usually be more efficient) and values are +* removed from the list as they are read. + +* Parameters: +* this +* Pointer to the Channel. +* class +* A pointer to a constant null-terminated string containing the +* name of the class whose loader is requesting the data (note +* this is not usually the same as the class name of the Object +* being built). This value allows the class structure of the +* input data to be validated. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstObject *object; /* Pointer to new Object */ + AstChannelValue *value; /* Pointer to Value structure */ + char *name; /* Pointer to data item name string */ + char *val; /* Pointer to data item value string */ + int done; /* All class data read? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* If the "values_ok" flag is set, this indicates that the values list + (at the current nesting level) has been filled by a previous + invocation of this function and has then been read by the + appropriate class loader. In this case, clear any entries which may + remain in the current values list. If any such entries are found, + they represent input data that were not read, so an error will + result. */ + if ( values_ok[ nest ] ) ClearValues( this, status ); + +/* If "values_class" is non-NULL, this indicates that the values list + (at the current nesting level) has been filled by a previous + invocation of this function, but that the values belong to a class + whose loader has not yet tried to read them. In this case, we must + continue to keep the values until they are needed, so we do not + read any more input data this time. */ + if ( values_class[ nest ] ) { + +/* If the class to which the previously saved values belong matches + the class we now want values for, set the "values_ok" flag. This + then allows the values to be accessed (by LookupValue). */ + values_ok[ nest ] = !strcmp( values_class[ nest ], class ); + +/* If the current values list is empty, we must read in values for the + next class that appears in the input data. However, first check + that the "end_of_object" flag has not been set. If it has, we have + already reached the end of this Object's data, so there is some + kind of problem with the order in which class loaders have been + invoked. This will probably never happen, but report an error if + necessary. */ + } else if ( end_of_object[ nest ] ) { + astError( AST__LDERR, + "astRead(%s): Invalid attempt to read further %s data " + "following an end of %s.", status, + astGetClass( this ), class, object_class[ nest ] ); + astError( AST__LDERR, + "Perhaps the wrong class loader has been invoked?" , status); + +/* If we need new values, loop to read input data items until the end + of the data for a class is reached. */ + } else { + done = 0; + while ( astOK && !done ) { + +/* Read the next input data item. */ + astGetNextData( this, 0, &name, &val ); + if ( astOK ) { + +/* Unexpected end of input. */ +/* ------------------------ */ +/* If no "name" value is returned, we have reached the end of the + input data stream without finding the required end of class + terminator, so report an error. */ + if ( !name ) { + astError( AST__EOCHN, + "astRead(%s): Unexpected end of input (missing end " + "of %s).", status, + astGetClass( this ), object_class[ nest ] ); + +/* "IsA" item. */ +/* ----------- */ +/* Otherwise, if an "IsA" item was read, it indicates the end of the + data for a class. Store the pointer to the name of this class in + "values_class" and note whether this is the class whose data we + wanted in "values_ok". If the data we have read do not belong to + the class we wanted, they will simply be kept until the right class + comes looking for them. */ + } else if ( !strcmp( name, "isa" ) ) { + values_class[ nest ] = val; + values_ok[ nest ] = !strcmp( val, class ); + +/* Free the memory holding the name string. */ + name = astFree( name ); + +/* Note we have finished reading class data. */ + done = 1; + +/* "End" item. */ +/* ----------- */ +/* If an "End" item was read, it indicates the end of the data both + for a class and for the current Object definition as a whole. Set + the "end_of_object" flag (for the current nesting level) which + prevents any further data being read for this Object. This flag is + also used (by Read) to check that an "End" item was actually + read. */ + } else if ( !strcmp( name, "end" ) ) { + end_of_object[ nest ] = 1; + +/* Check that the class name in the "End" item matches that of the + Object being built. If so, store the pointer to the name of this + class in "values_class" and note whether this is the class whose + data we wanted in "values_ok". If the data we have read do not + belong to the class we wanted, they will simply be kept until the + right class comes looking for them. */ + if ( !strcmp( val, object_class[ nest ] ) ) { + values_class[ nest ] = val; + values_ok[ nest ] = !strcmp( class, val ); + +/* If the "End" item contains the wrong class name (i.e. not matching + the corresponding "Begin" item), then report an error. */ + } else { + astError( AST__BADIN, + "astRead(%s): Bad class structure in input data.", status, + astGetClass( this ) ); + astError( AST__BADIN, + "End of %s read when expecting end of %s.", status, + val, object_class[ nest ] ); + +/* Free the memory used by the class string, which will not now be + used. */ + val = astFree( val ); + } + +/* Free the memory holding the name string. */ + name = astFree( name ); + +/* Note we have finished reading class data. */ + done = 1; + +/* String value. */ +/* ------------- */ +/* If any other name is obtained and "val" is not NULL, we have read a + non-Object value, encoded as a string. Allocate memory for a Value + structure to describe it. */ + } else if ( val ) { + value = astMalloc( sizeof( AstChannelValue ) ); + if ( astOK ) { + +/* Store pointers to the name and value string in the Value structure + and note this is not an Object value. */ + value->name = name; + value->ptr.string = val; + value->is_object = 0; + +/* Append the Value structure to the values list for the current + nesting level. */ + AppendValue( value, values_list + nest, status ); + +/* If an error occurred, free the memory holding the "name" and "val" + strings. */ + } else { + name = astFree( name ); + val = astFree( val ); + } + +/* Object value. */ +/* ------------- */ +/* If "val" is NULL, we have read an Object item, and the Object + definition should follow. Allocate memory for a Value structure to + describe it. */ + } else { + value = astMalloc( sizeof( AstChannelValue ) ); + +/* Invoke astRead to read the Object definition from subsequent data + items and to build the Object, returning a pointer to it. This will + result in recursive calls to the current function, but as these + will use higher nesting levels they will not interfere with the + current invocation. */ + astreadclassdata_msg = 0; + object = astRead( this ); + if ( astOK ) { + +/* Store pointers to the name and Object in the Value structure and + note this is an Object value. */ + value->name = name; + value->ptr.object = object; + value->is_object = 1; + +/* Append the Value structure to the values list for the current + nesting level. */ + AppendValue( value, values_list + nest, status ); + +/* If an error occurred, report a contextual error maessage and set + the "astreadclassdata_msg" flag (this prevents multiple messages if this function is + invoked recursively to deal with nested Objects). */ + } else { + if ( !astreadclassdata_msg ) { + astError( astStatus, + "Failed to read the \"%s\" Object value.", status, + name ); + astreadclassdata_msg = 1; + } + +/* Free the memory holding the "name" string and any Value structure + that was allocated. */ + name = astFree( name ); + value = astFree( value ); + } + } + } + } + } +} + +static double ReadDouble( AstChannel *this, const char *name, double def, int *status ) { +/* +*+ +* Name: +* astReadDouble + +* Purpose: +* Read a double value as part of loading a class. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* double astReadDouble( AstChannel *this, const char *name, double def ) + +* Class Membership: +* Channel method. + +* Description: +* This function searches the current values list of a Channel to +* identify a double value with a specified name. If such a value +* is found, it is returned, otherwise a default value is returned +* instead. +* +* This function should only be invoked from within the loader +* function associated with a class, in order to return a double +* value to be assigned to an instance variable. It must be +* preceded by a call to the astReadClassData function, which loads +* the values associated with the class into the current values +* list from the input data source. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated character string +* containing the name of the required value. This must be in +* lower case with no surrounding white space. Note that names +* longer than 6 characters will not match any value. +* def +* If no suitable value can be found (e.g. it is absent from the +* data stream being read), then this value will be returned +* instead. + +* Returned Value: +* The required value, or the default if the value was not found. + +* Notes: +* - A value of 0.0 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstChannelValue *value; /* Pointer to required Value structure */ + double result; /* Value to be returned */ + int nc; /* Number of characters read by astSscanf */ + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Search for a Value structure with the required name in the current + values list.*/ + value = LookupValue( name, status ); + if ( astOK ) { + +/* If a Value was found, check that it describes a string (as opposed + to an Object). */ + if ( value ) { + if ( !value->is_object ) { + +/* If so, then attempt to decode the string to give a double value, + checking that the entire string is read (and checking for the magic string + used to represent bad values). If this fails, then the wrong name has + probably been given, or the input data are corrupt, so report an error. */ + nc = 0; + if ( ( 0 == astSscanf( value->ptr.string, " " BAD_STRING " %n", + &nc ) ) + && ( nc >= (int) strlen( value->ptr.string ) ) ) { + result = AST__BAD; + + } else if ( !( ( 1 == astSscanf( value->ptr.string, " %lf %n", + &result, &nc ) ) + && ( nc >= (int) strlen( value->ptr.string ) ) ) ) { + astError( AST__BADIN, + "astRead(%s): The value \"%s = %s\" cannot " + "be read as a double precision floating point " + "number.", status, astGetClass( this ), + value->name, value->ptr.string ); + + } else if( !astISFINITE( result ) ) { + astError( AST__BADIN, + "astRead(%s): Illegal double precision floating " + "point value \"%s\" read for \"%s\".", status, + astGetClass( this ), value->ptr.string, value->name ); + + } + +/* Report a similar error if the Value does not describe a string. */ + } else { + astError( AST__BADIN, + "astRead(%s): The Object \"%s = <%s>\" cannot " + "be read as a double precision floating point number.", status, + astGetClass( this ), + value->name, astGetClass( value->ptr.object ) ); + } + +/* Free the Value structure and the resources it points at. */ + value = FreeValue( value, status ); + +/* If no suitable Value structure was found, then use the default + value instead. */ + } else { + result = def; + } + } + +/* Return the result. */ + return result; +} + +static int ReadInt( AstChannel *this, const char *name, int def, int *status ) { +/* +*+ +* Name: +* astReadInt + +* Purpose: +* Read an int value as part of loading a class. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* int astReadInt( AstChannel *this, const char *name, int def ) + +* Class Membership: +* Channel method. + +* Description: +* This function searches the current values list of a Channel to +* identify an int value with a specified name. If such a value is +* found, it is returned, otherwise a default value is returned +* instead. +* +* This function should only be invoked from within the loader +* function associated with a class, in order to return an int +* value to be assigned to an instance variable. It must be +* preceded by a call to the astReadClassData function, which loads +* the values associated with the class into the current values +* list from the input data source. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated character string +* containing the name of the required value. This must be in +* lower case with no surrounding white space. Note that names +* longer than 6 characters will not match any value. +* def +* If no suitable value can be found (e.g. it is absent from the +* data stream being read), then this value will be returned +* instead. + +* Returned Value: +* The required value, or the default if the value was not found. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstChannelValue *value; /* Pointer to required Value structure */ + int nc; /* Number of characters read by astSscanf */ + int result; /* Value to be returned */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Search for a Value structure with the required name in the current + values list.*/ + value = LookupValue( name, status ); + if ( astOK ) { + +/* If a Value was found, check that it describes a string (as opposed + to an Object). */ + if ( value ) { + if ( !value->is_object ) { + +/* If so, then attempt to decode the string to give an int value, + checking that the entire string is read. If this fails, then the + wrong name has probably been given, or the input data are corrupt, + so report an error. */ + nc = 0; + if ( !( ( 1 == astSscanf( value->ptr.string, " %d %n", + &result, &nc ) ) + && ( nc >= (int) strlen( value->ptr.string ) ) ) ) { + astError( AST__BADIN, + "astRead(%s): The value \"%s = %s\" cannot " + "be read as an integer.", status, astGetClass( this ), + value->name, value->ptr.string ); + } + +/* Report a similar error if the Value does not describe a string. */ + } else { + astError( AST__BADIN, + "astRead(%s): The Object \"%s = <%s>\" cannot " + "be read as an integer.", status, astGetClass( this ), + value->name, astGetClass( value->ptr.object ) ); + } + +/* Free the Value structure and the resources it points at. */ + value = FreeValue( value, status ); + +/* If no suitable Value structure was found, then use the default + value instead. */ + } else { + result = def; + } + } + +/* Return the result. */ + return result; +} + +static AstObject *ReadObject( AstChannel *this, const char *name, + AstObject *def, int *status ) { +/* +*+ +* Name: +* astReadObject + +* Purpose: +* Read a (sub)Object as part of loading a class. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* AstObject *astReadObject( AstChannel *this, const char *name, +* AstObject *def ) + +* Class Membership: +* Channel method. + +* Description: +* This function searches the current values list of a Channel to +* identify an Object with a specified name. If such an Object is +* found, a pointer to it is returned, otherwise a default pointer +* is returned instead. +* +* This function should only be invoked from within the loader +* function associated with a class, in order to return an Object +* pointer value to be assigned to an instance variable. It must be +* preceded by a call to the astReadClassData function, which loads +* the values associated with the class into the current values +* list from the input data source. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated character string +* containing the name of the required Object. This must be in +* lower case with no surrounding white space. Note that names +* longer than 6 characters will not match any Object. +* def +* If no suitable Object can be found (e.g. the Object is absent +* from the data stream being read), then a clone of this +* default Object pointer will be returned instead (or NULL if +* this default pointer is NULL). + +* Returned Value: +* A pointer to the Object, or a clone of the default pointer if +* the Object was not found. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstObject *result; /* Pointer value to return */ + AstChannelValue *value; /* Pointer to required Value structure */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Search for a Value structure with the required name in the current + values list.*/ + value = LookupValue( name, status ); + if ( astOK ) { + +/* If a Value was found, check that it describes an Object (as opposed to + a string). */ + if ( value ) { + if ( value->is_object ) { + +/* If so, then extract the Object pointer, replacing it with NULL. */ + result = value->ptr.object; + value->ptr.object = NULL; + +/* If the Value does not describe an Object, then the wrong name has + probably been given, or the input data are corrupt, so report an + error. */ + } else { + astError( AST__BADIN, + "astRead(%s): The value \"%s = %s\" cannot be " + "read as an Object.", status, astGetClass( this ), + value->name, value->ptr.string ); + } + +/* Free the Value structure and the resources it points at. */ + value = FreeValue( value, status ); + +/* If no suitable Value structure was found, clone the default + pointer, if given. */ + } else if ( def ) { + result = astClone( def ); + } + } + +/* Return the result. */ + return result; +} + +static char *ReadString( AstChannel *this, const char *name, + const char *def, int *status ) { +/* +*+ +* Name: +* astReadString + +* Purpose: +* Read a string value as part of loading a class. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* char *astReadString( AstChannel *this, const char *name, +* const char *def ) + +* Class Membership: +* Channel method. + +* Description: +* This function searches the current values list of a Channel to +* identify a string value with a specified name. If such a value +* is found, a pointer to the string is returned, otherwise a +* pointer to a copy of a default string is returned instead. +* +* This function should only be invoked from within the loader +* function associated with a class, in order to return a string +* pointer value to be assigned to an instance variable. It must be +* preceded by a call to the astReadClassData function, which loads +* the values associated with the class into the current values +* list from the input data source. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated character string +* containing the name of the required value. This must be in +* lower case with no surrounding white space. Note that names +* longer than 6 characters will not match any value. +* def +* If no suitable string can be found (e.g. the value is absent +* from the data stream being read), then a dynamically +* allocated copy of the null-terminated string pointed at by +* "def" will be made, and a pointer to this copy will be +* returned instead (or NULL if this default pointer is NULL). + +* Returned Value: +* A pointer to a dynamically allocated null-terminated string +* containing the value required, or to a copy of the default +* string if the value was not found (or NULL if the "def" pointer +* was NULL). + +* Notes: +* - It is the caller's responsibility to arrange for the memory +* holding the returned string to be freed (using astFree) when it +* is no longer required. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstChannelValue *value; /* Pointer to required Value structure */ + char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Search for a Value structure with the required name in the current + values list.*/ + value = LookupValue( name, status ); + if ( astOK ) { + +/* If a Value was found, check that it describes a string (as opposed + to an Object). */ + if ( value ) { + if ( !value->is_object ) { + +/* If so, then extract the string pointer, replacing it with NULL. */ + result = value->ptr.string; + value->ptr.string = NULL; + +/* If the Value does not describe a string, then the wrong name has + probably been given, or the input data are corrupt, so report an + error. */ + } else { + astError( AST__BADIN, + "astRead(%s): The Object \"%s = <%s>\" cannot " + "be read as a string.", status, astGetClass( this ), + value->name, astGetClass( value->ptr.object ) ); + } + +/* Free the Value structure and the resources it points at. */ + value = FreeValue( value, status ); + +/* If no suitable Value structure was found, then make a dynamic copy + of the default string (if given) and return a pointer to this. */ + } else if ( def ) { + result = astStore( NULL, def, strlen( def ) + (size_t) 1 ); + } + } + +/* Return the result. */ + return result; +} + +static void RemoveValue( AstChannelValue *value, AstChannelValue **head, int *status ) { +/* +* Name: +* RemoveValue + +* Purpose: +* Remove a Value structure from a circular linked list. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void RemoveValue( AstChannelValue *value, AstChannelValue **head, int *status ); + +* Class Membership: +* Channel member function. + +* Description: +* This function removes a Value structure from a doubly linked +* circular list of such structures. The "head of list" pointer is +* updated to point at the element following the one removed. + +* Parameters: +* value +* Pointer to the structure to be removed (note that this must +* actually be in the list, although this function does not +* check). +* head +* Address of a pointer to the element at the head of the +* list. This pointer will be updated to point at the list +* element that follows the one removed. If the list becomes +* empty, the pointer will be set to NULL. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error chacking and does not +* generate errors. +*/ + +/* Remove the Value structure from the list by re-establishing links + between the elements on either side of it. */ + value->blink->flink = value->flink; + value->flink->blink = value->blink; + +/* Update the head of list pointer to identify the following + element. */ + *head = value->flink; + +/* If the head of list identifies the removed element, then note that + the list is now empty. */ + if ( *head == value ) *head = NULL; + +/* Make the removed element point at itself. */ + value->flink = value; + value->blink = value; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Channel. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* Channel member function (over-rides the astSetAttrib protected +* method inherited from the Object class). + +* Description: +* This function assigns an attribute value for a Channel, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Channel. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstChannel *this; /* Pointer to the Channel structure */ + int comment; /* Comment attribute value */ + int full; /* Full attribute value */ + int indent; /* Indent attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by "astSscanf" */ + int report_level; /* Skip attribute value */ + int skip; /* Skip attribute value */ + int sourcefile; /* Offset of SourceFile string */ + int sinkfile; /* Offset of SinkFile string */ + int strict; /* Report errors instead of warnings? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Channel structure. */ + this = (AstChannel *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Comment. */ +/* ---------*/ + if ( nc = 0, + ( 1 == astSscanf( setting, "comment= %d %n", &comment, &nc ) ) + && ( nc >= len ) ) { + astSetComment( this, comment ); + +/* Full. */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "full= %d %n", &full, &nc ) ) + && ( nc >= len ) ) { + astSetFull( this, full ); + +/* Indent. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "indent= %d %n", &indent, &nc ) ) + && ( nc >= len ) ) { + astSetIndent( this, indent ); + +/* ReportLavel. */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "reportlevel= %d %n", &report_level, &nc ) ) + && ( nc >= len ) ) { + astSetReportLevel( this, report_level ); + +/* Skip. */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "skip= %d %n", &skip, &nc ) ) + && ( nc >= len ) ) { + astSetSkip( this, skip ); + +/* SinkFile. */ +/* --------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "sinkfile=%n%*[^\n]%n", &sinkfile, &nc ) ) + && ( nc >= len ) ) { + astSetSinkFile( this, setting + sinkfile ); + +/* SourceFile. */ +/* ----------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "sourcefile=%n%*[^\n]%n", &sourcefile, &nc ) ) + && ( nc >= len ) ) { + astSetSourceFile( this, setting + sourcefile ); + +/* Strict. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "strict= %d %n", &strict, &nc ) ) + && ( nc >= len ) ) { + astSetStrict( this, strict ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) { +/* +* Name: +* SinkWrap + +* Purpose: +* Wrapper function to invoke a C Channel sink function. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function invokes the sink function whose pointer is +* supplied in order to write an output line to an external data +* store. + +* Parameters: +* sink +* Pointer to a sink function, whose single parameter is a +* pointer to a const, null-terminated string containing the +* text to be written, and which returns void. This is the form +* of Channel sink function employed by the C language interface +* to the AST library. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the sink function. */ + ( *sink )( line ); +} + +static char *SourceWrap( const char *(* source)( void ), int *status ) { +/* +* Name: +* SourceWrap + +* Purpose: +* Wrapper function to invoke a C Channel source function. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* char *SourceWrap( const char *, int *status(* source)( void ) ) + +* Class Membership: +* Channel member function. + +* Description: +* This function invokes the source function whose pointer is +* supplied in order to read the next input line from an external +* data store. It then returns a pointer to a dynamic string +* containing a copy of the text that was read. + +* Parameters: +* source +* Pointer to a source function, with no parameters, that +* returns a pointer to a const, null-terminated string +* containing the text that it read. This is the form of Channel +* source function employed by the C language interface to the +* AST library. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated, null terminated string +* containing a copy of the text that was read. This string must be +* freed by the caller (using astFree) when no longer required. +* +* A NULL pointer will be returned if there is no more input text +* to read. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + char *result; /* Pointer value to return */ + const char *line; /* Pointer to input line */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the source function to read the next input line and return a + pointer to the resulting string. */ + line = ( *source )(); + +/* If a string was obtained, make a dynamic copy of it and save the + resulting pointer. */ + if ( line ) result = astString( line, (int) strlen( line ) ); + +/* Return the result. */ + return result; +} + +void astStoreChannelData_( AstChannel *this, int *status ) { +/* +*+ +* Name: +* astStoreChannelData + +* Purpose: +* Store the Channel's channel-data pointer in a thread-specific +* global variable. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* astStoreChannelData( AstChannel *this ) + +* Class Membership: +* Channel method. + +* Description: +* This function stores the Channel's channel-data pointer (if any) +* established by the previous call to astPutChannelData, in a +* thread-specific global variable from where the astChannelData macro +* can access it. + +* Parameters: +* this +* Pointer to the Channel. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Store the pointer int he global variable. */ + channel_data = this->data; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Channel. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Channel member function (over-rides the astTestAttrib protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Channel's attributes. + +* Parameters: +* this +* Pointer to the Channel. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstChannel *this; /* Pointer to the Channel structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Channel structure. */ + this = (AstChannel *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* Comment. */ +/* -------- */ + if ( !strcmp( attrib, "comment" ) ) { + result = astTestComment( this ); + +/* Full. */ +/* ----- */ + } else if ( !strcmp( attrib, "full" ) ) { + result = astTestFull( this ); + +/* Indent. */ +/* ------- */ + } else if ( !strcmp( attrib, "indent" ) ) { + result = astTestIndent( this ); + +/* ReportLevel. */ +/* ------------ */ + } else if ( !strcmp( attrib, "reportlevel" ) ) { + result = astTestReportLevel( this ); + +/* Skip. */ +/* ----- */ + } else if ( !strcmp( attrib, "skip" ) ) { + result = astTestSkip( this ); + +/* SourceFile. */ +/* ----------- */ + } else if ( !strcmp( attrib, "sourcefile" ) ) { + result = astTestSourceFile( this ); + +/* SinkFile. */ +/* ----------- */ + } else if ( !strcmp( attrib, "sinkfile" ) ) { + result = astTestSinkFile( this ); + +/* Strict. */ +/* ------- */ + } else if ( !strcmp( attrib, "strict" ) ) { + result = astTestStrict( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static void Unquote( AstChannel *this, char *str, int *status ) { +/* +* Name: +* Unquote + +* Purpose: +* Remove quotes from a (possibly) quoted string. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* void Unquote( AstChannel *this, char *str, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function removes one layer of quote characters (") from a +* string which is possibly quoted. Any quotes within quotes (which +* should have been doubled when the string was originally quoted) +* are also converted back to single quotes again. +* +* The quotes need not start or end at the ends of the string, and +* there may be any number of quoted sections within the string. No +* error results if the string does not contain any quotes at all +* (it is simply returned unchanged), but an error results if any +* unmatched quotes are found. + +* Parameters: +* this +* Pointer to a Channel. This is only used for constructing error +* messages and has no influence on the string processing. +* str +* Pointer to the null-terminated string to be processed. This +* is modified in place. The new string starts at the same +* location as the original but has a new null character +* appended if necessary (it will usually be shorter than the +* original). +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int i; /* Loop counter for "input" characters */ + int j; /* Counter for "output" characters */ + int quoted; /* Inside a quoted string? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Loop to inspect each character in the string. */ + quoted = 0; + for ( i = j = 0; str[ i ]; i++ ) { + +/* Non-quote characters are simply copied to their new location in the + string. */ + if ( str[ i ] != '"' ) { + str[ j++ ] = str[ i ]; + +/* If a quote character '"' is encountered and we are not already in a + quoted string, then note the start of a quoted string (and discard + the quote). */ + } else if ( !quoted ) { + quoted = 1; + +/* If a quote character is encountered inside a quoted string, then + check if the next character is also a quote. If so, convert this + double quote to a single one. */ + } else if ( str[ i + 1 ] == '"' ) { + str[ j++ ] = '"'; + i++; + +/* If a single quote character is encountered inside a quoted string, + then note the end of the quoted string (and discard the quote). */ + } else { + quoted = 0; + } + } + +/* Append a null to terminate the processed string. */ + str[ j ] = '\0'; + +/* If the "quoted" flag is still set, then there were unmatched + quotes, so report an error. */ + if ( quoted ) { + astError( AST__UNMQT, + "astRead(%s): Unmatched quotes in input data: %s.", status, + astGetClass( this ), str ); + } +} + +static int Use( AstChannel *this, int set, int helpful, int *status ) { +/* +* Name: +* Use + +* Purpose: +* Decide whether to write a value to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "channel.h" +* int Use( AstChannel *this, int set, int helpful, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function decides whether a value supplied by a class "Dump" +* function, via a call to one of the astWrite... protected +* methods, should actually be written to the data sink associated +* with a Channel. +* +* This decision is based on the settings of the "set" and +* "helpful" flags supplied to the astWrite... method, plus the +* attribute settings of the Channel. + +* Parameters: +* this +* A pointer to the Channel. +* set +* The "set" flag supplied. +* helpful +* The "helpful" value supplied. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the value should be written out, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int full; /* Full attribute value */ + int result; /* Result value to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If "set" is non-zero, then so is the result ("set" values must + always be written out). */ + result = ( set != 0 ); + +/* Otherwise, obtain the value of the Channel's Full attribute. */ + if ( !set ) { + full = astGetFull( this ); + +/* If Full is positive, display all values, if zero, display only + "helpful" values, if negative, display no (un-"set") values. */ + if ( astOK ) result = ( ( helpful && ( full > -1 ) ) || ( full > 0 ) ); + } + +/* Return the result. */ + return result; +} + +static int Write( AstChannel *this, AstObject *object, int *status ) { +/* +*++ +* Name: +c astWrite +f AST_WRITE + +* Purpose: +* Write an Object to a Channel. + +* Type: +* Public function. + +* Synopsis: +c #include "channel.h" +c int astWrite( AstChannel *this, AstObject *object ) +f RESULT = AST_WRITE( THIS, OBJECT, STATUS ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes an Object to a Channel, appending it to any +* previous Objects written to that Channel. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Channel. +c object +f OBJECT = INTEGER (Given) +* Pointer to the Object which is to be written. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astWrite() +f AST_WRITE = INTEGER +* The number of Objects written to the Channel by this +c invocation of astWrite (normally, this will be one). +f invocation of AST_WRITE (normally, this will be one). + +* Applicability: +* FitsChan +* If the FitsChan uses a foreign encoding (e.g. FITS-WCS) rather +* than the native AST encoding, then storing values in the +* FitsChan for keywords NAXIS1, NAXIS2, etc., before invoking +c astWrite +f AST_WRITE +* can help to produce a successful write. + +* Notes: +* - A value of zero will be returned if this function is invoked +c with the AST error status set, or if it should fail for any +f with STATUS set to an error value, or if it should fail for any +* reason. +* - Invoking this function will usually cause the sink function +* associated with the channel to be called in order to transfer a +* textual description of the supplied object to some external data +* store. However, the FitsChan class behaves differently. Invoking +* this function on a FitsChan causes new FITS header cards to be +* added to an internal buffer (the sink function is not invoked). +* This buffer is written out through the sink function only when the +* FitsChan is deleted. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* The work of this function is actually performed by the protected + astDump method of the Object. The fact that this is further + encapsulated within the astWrite method (which belongs to the + Channel) is simply a trick to allow it to be over-ridden either by + a derived Channel, or a derived Object (or both), and hence to + adapt to the nature of either argument. */ + astDump( object, this ); + +/* Return the number of Objects written. */ + return astOK ? 1 : 0; +} + +static void WriteBegin( AstChannel *this, const char *class, + const char *comment, int *status ) { +/* +*+ +* Name: +* astWriteBegin + +* Purpose: +* Write a "Begin" data item to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astWriteBegin( AstChannel *this, const char *class, +* const char *comment ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes a "Begin" data item to the data sink +* associated with a Channel, so as to begin the output of a new +* Object definition. + +* Parameters: +* this +* Pointer to the Channel. +* class +* Pointer to a constant null-terminated string containing the +* name of the class to which the Object belongs. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the "Begin" +* item. Normally, this will describe the purpose of the Object. + +* Notes: +* - The comment supplied may not actually be used, depending on +* the nature of the Channel supplied. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *line; /* Pointer to dynamic output string */ + int i; /* Loop counter for indentation characters */ + int nc; /* Number of output characters */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Start building a dynamic string with an initial space. Then add + further spaces to suit the current indentation level. */ + line = astAppendString( NULL, &nc, " " ); + for ( i = 0; i < current_indent; i++ ) { + line = astAppendString( line, &nc, " " ); + } + +/* Append the "Begin" keyword followed by the class name. */ + line = astAppendString( line, &nc, "Begin " ); + line = astAppendString( line, &nc, class ); + +/* If required, also append the comment. */ + if ( astGetComment( this ) && *comment ) { + line = astAppendString( line, &nc, " \t# " ); + line = astAppendString( line, &nc, comment ); + } + +/* Write out the resulting line of text. */ + OutputTextItem( this, line, status ); + +/* Free the dynamic string. */ + line = astFree( line ); + +/* Increment the indentation level and clear the count of items written + for this Object. */ + current_indent += astGetIndent( this ); + items_written = 0; +} + +static void WriteDouble( AstChannel *this, const char *name, + int set, int helpful, + double value, const char *comment, int *status ) { +/* +*+ +* Name: +* astWriteDouble + +* Purpose: +* Write a double value to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astWriteDouble( AstChannel *this, const char *name, +* int set, int helpful, +* double value, const char *comment ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes a named double value, representing the +* value of a class instance variable, to the data sink associated +* with a Channel. It is intended for use by class "Dump" functions +* when writing out class information which will subsequently be +* re-read. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* Channel's Full attribute is set - either to permit all values +* to be shown, or to suppress non-essential information +* entirely. +* value +* The value to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the Channel supplied and the setting of its +* Comment attribute. +*- +*/ + +/* Local Constants: */ +#define BUFF_LEN 100 /* Size of local formatting buffer */ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *line; /* Pointer to dynamic output string */ + char buff[ BUFF_LEN + 1 ]; /* Local formatting buffer */ + int i; /* Loop counter for indentation characters */ + int nc; /* Number of output characters */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Use the "set" and "helpful" flags, along with the Channel's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Start building a dynamic string with an initial space, or a comment + character if "set" is zero. Then add further spaces to suit the + current indentation level. */ + line = astAppendString( NULL, &nc, set ? " " : "#" ); + for ( i = 0; i < current_indent; i++ ) { + line = astAppendString( line, &nc, " " ); + } + +/* Append the name string followed by " = ". */ + line = astAppendString( line, &nc, name ); + line = astAppendString( line, &nc, " = " ); + +/* Format the value as a string and append this. Make sure "-0" isn't + produced. Use a magic string to represent bad values. */ + if( value != AST__BAD ) { + (void) sprintf( buff, "%.*g", DBL_DIG, value ); + if ( !strcmp( buff, "-0" ) ) { + buff[ 0 ] = '0'; + buff[ 1 ] = '\0'; + } + } else { + strcpy( buff, BAD_STRING ); + } + line = astAppendString( line, &nc, buff ); + +/* If required, also append the comment. */ + if ( astGetComment( this ) && *comment ) { + line = astAppendString( line, &nc, " \t# " ); + line = astAppendString( line, &nc, comment ); + } + +/* Write out the resulting line of text. */ + OutputTextItem( this, line, status ); + +/* Free the dynamic string. */ + line = astFree( line ); + } + +/* Undefine macros local to this function. */ +#undef BUFF_LEN +} + +static void WriteEnd( AstChannel *this, const char *class, int *status ) { +/* +*+ +* Name: +* astWriteEnd + +* Purpose: +* Write an "End" data item to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astWriteEnd( AstChannel *this, const char *class ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes an "End" data item to the data sink +* associated with a Channel. This item delimits the end of an +* Object definition. + +* Parameters: +* this +* Pointer to the Channel. +* class +* Pointer to a constant null-terminated string containing the +* class name of the Object whose definition is being terminated +* by the "End" item. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *line; /* Pointer to dynamic output string */ + int i; /* Loop counter for indentation characters */ + int nc; /* Number of output characters */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Decrement the indentation level so that the "End" item matches the + corresponding "Begin" item. */ + current_indent -= astGetIndent( this ); + +/* Start building a dynamic string with an initial space. Then add + further spaces to suit the current indentation level. */ + line = astAppendString( NULL, &nc, " " ); + for ( i = 0; i < current_indent; i++ ) { + line = astAppendString( line, &nc, " " ); + } + +/* Append the "End" keyword followed by the class name. */ + line = astAppendString( line, &nc, "End " ); + line = astAppendString( line, &nc, class ); + +/* Write out the resulting line of text. */ + OutputTextItem( this, line, status ); + +/* Free the dynamic string. */ + line = astFree( line ); +} + +static void WriteInt( AstChannel *this, const char *name, int set, int helpful, + int value, const char *comment, int *status ) { +/* +*+ +* Name: +* astWriteInt + +* Purpose: +* Write an integer value to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astWriteInt( AstChannel *this, const char *name, +* int set, int helpful, +* int value, const char *comment ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes a named integer value, representing the +* value of a class instance variable, to the data sink associated +* with a Channel. It is intended for use by class "Dump" functions +* when writing out class information which will subsequently be +* re-read. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* Channel's Full attribute is set - either to permit all values +* to be shown, or to suppress non-essential information +* entirely. +* value +* The value to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the Channel supplied and the setting of its +* Comment attribute. +*- +*/ + +/* Local Constants: */ +#define BUFF_LEN 50 /* Size of local formatting buffer */ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *line; /* Pointer to dynamic output string */ + char buff[ BUFF_LEN + 1 ]; /* Local formatting buffer */ + int i; /* Loop counter for indentation characters */ + int nc; /* Number of output characters */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Use the "set" and "helpful" flags, along with the Channel's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Start building a dynamic string with an initial space, or a comment + character if "set" is zero. Then add further spaces to suit the + current indentation level. */ + line = astAppendString( NULL, &nc, set ? " " : "#" ); + for ( i = 0; i < current_indent; i++ ) { + line = astAppendString( line, &nc, " " ); + } + +/* Append the name string followed by " = ". */ + line = astAppendString( line, &nc, name ); + line = astAppendString( line, &nc, " = " ); + +/* Format the value as a decimal string and append this. */ + (void) sprintf( buff, "%d", value ); + line = astAppendString( line, &nc, buff ); + +/* If required, also append the comment. */ + if ( astGetComment( this ) && *comment ) { + line = astAppendString( line, &nc, " \t# " ); + line = astAppendString( line, &nc, comment ); + } + +/* Write out the resulting line of text. */ + OutputTextItem( this, line, status ); + +/* Free the dynamic string. */ + line = astFree( line ); + } + +/* Undefine macros local to this function. */ +#undef BUFF_LEN +} + +int astWriteInvocations_( int *status ){ +/* +*+ +* Name: +* astWriteInvocations + +* Purpose: +* Returns the number of invocations of the astWrite method. + +* Type: +* Protected function. + +* Synopsis: +* #include "channel.h" +* int astWriteInvocations + +* Class Membership: +* Channel method. + +* Description: +* This function returns the number of invocations of astWrite which +* have been made so far, excluding those made from within the +* astWriteObject method. An example of its use is to allow a Dump +* function to determine if a sub-object has already been dumped +* during the current invocation of astWrite. See the Dump method for +* the AstUnit class as an example. +*- +*/ + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + return nwrite_invoc; +} + +static void WriteIsA( AstChannel *this, const char *class, + const char *comment, int *status ) { +/* +*+ +* Name: +* astWriteIsA + +* Purpose: +* Write an "IsA" data item to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astWriteIsA( AstChannel *this, const char *class, +* const char *comment ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes an "IsA" data item to the data sink +* associated with a Channel. This item delimits the end of the +* data associated with the instance variables of a class, as part +* of an overall Object definition. + +* Parameters: +* this +* Pointer to the Channel. +* class +* Pointer to a constant null-terminated string containing the +* name of the class whose data are terminated by the "IsA" +* item. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the "IsA" +* item. Normally, this will describe the purpose of the class +* whose data are being terminated. + +* Notes: +* - The comment supplied may not actually be used, depending on +* the nature of the Channel supplied. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *line; /* Pointer to dynamic output string */ + int i; /* Loop counter for indentation characters */ + int indent_inc; /* Indentation increment */ + int nc; /* Number of output characters */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Output an "IsA" item only if there has been at least one item + written since the last "Begin" or "IsA" item, or if the Full + attribute for the Channel is greater than zero (requesting maximum + information). */ + if ( items_written || astGetFull( this ) > 0 ) { + +/* Start building a dynamic string with an initial space. Then add + further spaces to suit the current indentation level, but reduced + by one to allow the "IsA" item to match the "Begin" and "End" items + which enclose it. */ + indent_inc = astGetIndent( this ); + line = astAppendString( NULL, &nc, " " ); + for ( i = 0; i < ( current_indent - indent_inc ); i++ ) { + line = astAppendString( line, &nc, " " ); + } + +/* Append the "IsA" keyword followed by the class name. */ + line = astAppendString( line, &nc, "IsA " ); + line = astAppendString( line, &nc, class ); + +/* If required, also append the comment. */ + if ( astGetComment( this ) && *comment ) { + line = astAppendString( line, &nc, " \t# " ); + line = astAppendString( line, &nc, comment ); + } + +/* Write out the resulting line of text. */ + OutputTextItem( this, line, status ); + +/* Free the dynamic string. */ + line = astFree( line ); + +/* Clear the count of items written for this class. */ + items_written = 0; + } +} + +static void WriteObject( AstChannel *this, const char *name, + int set, int helpful, + AstObject *value, const char *comment, int *status ) { +/* +*+ +* Name: +* astWriteObject + +* Purpose: +* Write an Object as a value to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astWriteObject( AstChannel *this, const char *name, +* int set, int helpful, +* AstObject *value, const char *comment ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes an Object as a named value, representing +* the value of a class instance variable, to the data sink +* associated with a Channel. It is intended for use by class +* "Dump" functions when writing out class information which will +* subsequently be re-read. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* Channel's Full attribute is set - either to permit all values +* to be shown, or to suppress non-essential information +* entirely. +* value +* A Pointer to the Object to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the Channel supplied and the setting of its +* Comment attribute. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *line; /* Pointer to dynamic output string */ + int i; /* Loop counter for indentation characters */ + int indent_inc; /* Indentation increment */ + int nc; /* Number of output characters */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Use the "set" and "helpful" flags, along with the Channel's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Start building a dynamic string with an initial space, or a comment + character if "set" is zero. Then add further spaces to suit the + current indentation level. */ + line = astAppendString( NULL, &nc, set ? " " : "#" ); + for ( i = 0; i < current_indent; i++ ) { + line = astAppendString( line, &nc, " " ); + } + +/* Append the name string followed by " =". The absence of a value on + the right hand side indicates an Object value, whose definition + follows. */ + line = astAppendString( line, &nc, name ); + line = astAppendString( line, &nc, " =" ); + +/* If required, also append the comment. */ + if ( astGetComment( this ) && *comment ) { + line = astAppendString( line, &nc, " \t# " ); + line = astAppendString( line, &nc, comment ); + } + +/* Write out the resulting line of text. */ + OutputTextItem( this, line, status ); + +/* Free the dynamic string. */ + line = astFree( line ); + +/* If the value is not a default, write the Object to the Channel as + well, suitably indented (this is omitted if the value is commented + out). */ + if ( set ) { + indent_inc = astGetIndent( this ); + current_indent += indent_inc; + (void) astWrite( this, value ); + current_indent -= indent_inc; + } + } +} + +static void WriteString( AstChannel *this, const char *name, + int set, int helpful, + const char *value, const char *comment, int *status ) { +/* +*+ +* Name: +* astWriteString + +* Purpose: +* Write a string value to a data sink. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "channel.h" +* void astWriteString( AstChannel *this, const char *name, +* int set, int helpful, +* const char *value, const char *comment ) + +* Class Membership: +* Channel method. + +* Description: +* This function writes a named string value, representing the +* value of a class instance variable, to the data sink associated +* with a Channel. It is intended for use by class "Dump" functions +* when writing out class information which will subsequently be +* re-read. + +* Parameters: +* this +* Pointer to the Channel. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* Channel's Full attribute is set - either to permit all values +* to be shown, or to suppress non-essential information +* entirely. +* value +* Pointer to a constant null-terminated string containing the +* value to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the Channel supplied and the setting of its +* Comment attribute. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *line; /* Pointer to dynamic output string */ + int i; /* Loop counter for characters */ + int nc; /* Number of output characters */ + int quote; /* Quote character found? */ + int size; /* Size of allocated memory */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Use the "set" and "helpful" flags, along with the Channel's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Start building a dynamic string with an initial space, or a comment + character if "set" is zero. Then add further spaces to suit the + current indentation level. */ + line = astAppendString( NULL, &nc, set ? " " : "#" ); + for ( i = 0; i < current_indent; i++ ) { + line = astAppendString( line, &nc, " " ); + } + +/* Append the name string followed by " = " and an opening quote + character (the string will be quoted to protect leading and + trailing spaces). */ + line = astAppendString( line, &nc, name ); + line = astAppendString( line, &nc, " = \"" ); + +/* We now append the value string, but must inspect each character so + that quotes (appearing inside quotes) can be doubled. Determine the + current size of memory allocated for the dynamic string. */ + size = (int) astSizeOf( line ); + +/* Loop to inspect each character and see if it is a quote. */ + for ( i = 0; value[ i ]; i++ ) { + quote = ( value[ i ] == '"' ); + +/* If more memory is required, extend the dynamic string (allowing for + doubling of quotes and the final null) and save its new size. */ + if ( nc + 2 + quote > size ) { + line = astGrow( line, nc + 2 + quote, sizeof( char ) ); + if ( astOK ) { + size = (int) astSizeOf( line ); + +/* Quit if an error occurs. */ + } else { + break; + } + } + +/* Append the value character to the dynamic string, duplicating each + quote character. */ + line[ nc++ ] = value[ i ]; + if ( quote ) line[ nc++ ] = '"'; + } + +/* Append the closing quote. */ + line = astAppendString( line, &nc, "\"" ); + +/* If required, also append the comment. */ + if ( astGetComment( this ) && *comment ) { + line = astAppendString( line, &nc, " \t# " ); + line = astAppendString( line, &nc, comment ); + } + +/* Write out the resulting line of text. */ + OutputTextItem( this, line, status ); + +/* Free the dynamic string. */ + line = astFree( line ); + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. */ + +/* +*att++ +* Name: +* SourceFile + +* Purpose: +* Input file from which to read data. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the name of a file from which the Channel +* should read data. If specified it is used in preference to any source +* function specified when the Channel was created. +* +* Assigning a new value to this attribute will cause any previously +* opened SourceFile to be closed. The first subsequent call to +c astRead +f AST_READ +* will attempt to open the new file (an error will be reported if the +* file cannot be opened), and read data from it. All subsequent call to +c astRead +f AST_READ +* will read data from the new file, until the SourceFile attribute is +* cleared or changed. +* +* Clearing the attribute causes any open SourceFile to be closed. All +* subsequent data reads will use the source function specified when the +* Channel was created, or will read from standard input if no source +* function was specified. +* +* If no value has been assigned to SourceFile, a null string will be +* returned if an attempt is made to get the attribute value. + +* Notes: +* - Any open SourceFile is closed when the Channel is deleted. +* - If the Channel is copied or dumped +c (using astCopy or astShow) +f (using AST_COPY or AST_SHOW) +* the SourceFile attribute is left in a cleared state in the output +* Channel (i.e. the value of the SourceFile attribute is not copied). + +* Applicability: +* FitsChan +* In the case of a FitsChan, the specified SourceFile supplements +* the source function specified when the FitsChan was created, +* rather than replacing the source function. The source file +* should be a text file (not a FITS file) containing one header per +* line. When a value is assigned to SourceFile, the file is opened +* and read immediately, and all headers read from the file are +* appended to the end of any header already in the FitsChan. The file +* is then closed. Clearing the SourceFile attribute has no further +* effect, other than nullifying the string (i.e. the file name) +* associated with the attribute. + +*att-- +*/ + +/* Clear the SourceFile value by closing any open file, freeing the + allocated memory and assigning a NULL pointer. */ +astMAKE_CLEAR(Channel,SourceFile,fn_in,((this->fd_in=(this->fd_in?(fclose(this->fd_in),NULL):NULL)),astFree(this->fn_in))) + +/* If the SourceFile value is not set, supply a default in the form of a + pointer to the constant string "". */ +astMAKE_GET(Channel,SourceFile,const char *,NULL,( this->fn_in ? this->fn_in : "" )) + +/* Set a SourceFile value by closing any open file, freeing any previously + allocated memory, allocating new memory, storing the string and saving + the pointer to the copy. */ +astMAKE_SET(Channel,SourceFile,const char *,fn_in,((this->fd_in=(this->fd_in?(fclose(this->fd_in),NULL):NULL)),astStore( this->fn_in, value, strlen( value ) + (size_t) 1 ))) + +/* The SourceFile value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Channel,SourceFile,( this->fn_in != NULL )) + +/* +*att++ +* Name: +* SinkFile + +* Purpose: +* Output file to which to data should be written. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the name of a file to which the Channel +* should write data. If specified it is used in preference to any sink +* function specified when the Channel was created. +* +* Assigning a new value to this attribute will cause any previously +* opened SinkFile to be closed. The first subsequent call to +c astWrite +f AST_WRITE +* will attempt to open the new file (an error will be reported if the +* file cannot be opened), and write data to it. All subsequent call to +c astWrite +f AST_WRITE +* will write data to the new file, until the SinkFile attribute is +* cleared or changed. +* +* Clearing the attribute causes any open SinkFile to be closed. All +* subsequent data writes will use the sink function specified when the +* Channel was created, or will write to standard output if no sink +* function was specified. +* +* If no value has been assigned to SinkFile, a null string will be +* returned if an attempt is made to get the attribute value. + +* Notes: +* - A new SinkFile will over-write any existing file with the same +* name unless the existing file is write protected, in which case an +* error will be reported. +* - Any open SinkFile is closed when the Channel is deleted. +* - If the Channel is copied or dumped +c (using astCopy or astShow) +f (using AST_COPY or AST_SHOW) +* the SinkFile attribute is left in a cleared state in the output +* Channel (i.e. the value of the SinkFile attribute is not copied). + +* Applicability: +* FitsChan +* When the FitsChan is destroyed, any headers in the FitsChan will be +* written out to the sink file, if one is specified (if not, the +* sink function used when the FitsChan was created is used). The +* sink file is a text file (not a FITS file) containing one header +* per line. + +*att-- +*/ + +/* Clear the SinkFile value by closing any open file, freeing the allocated + memory and assigning a NULL pointer. */ +astMAKE_CLEAR(Channel,SinkFile,fn_out,((this->fd_out=(this->fd_out?(fclose(this->fd_out),NULL):NULL)),astFree(this->fn_out))) + +/* If the SinkFile value is not set, supply a default in the form of a + pointer to the constant string "". */ +astMAKE_GET(Channel,SinkFile,const char *,NULL,( this->fn_out ? this->fn_out : "" )) + +/* Set a SinkFile value by closing any open file, freeing any previously + allocated memory, allocating new memory, storing the string and saving + the pointer to the copy. */ +astMAKE_SET(Channel,SinkFile,const char *,fn_out,((this->fd_out=(this->fd_out?(fclose(this->fd_out),NULL):NULL)),astStore( this->fn_out, value, strlen( value ) + (size_t) 1 ))) + +/* The SinkFile value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Channel,SinkFile,( this->fn_out != NULL )) + + +/* +*att++ +* Name: +* Comment + +* Purpose: +* Include textual comments in output? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This is a boolean attribute which controls whether textual +* comments are to be included in the output generated by a +* Channel. If included, they will describe what each item of +* output represents. +* +* If Comment is non-zero, then comments will be included. If +* it is zero, comments will be omitted. + +* Applicability: +* Channel +* The default value is non-zero for a normal Channel. +* FitsChan +* The default value is non-zero for a FitsChan. +* XmlChan +* The default value is zero for an XmlChan. + +*att-- +*/ + +/* This is a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of one. */ +astMAKE_CLEAR(Channel,Comment,comment,-INT_MAX) +astMAKE_GET(Channel,Comment,int,0,( this->comment != -INT_MAX ? this->comment : 1 )) +astMAKE_SET(Channel,Comment,int,comment,( value != 0 )) +astMAKE_TEST(Channel,Comment,( this->comment != -INT_MAX )) + +/* +*att++ +* Name: +* Full + +* Purpose: +* Set level of output detail. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute is a three-state flag and takes values of -1, 0 +* or +1. It controls the amount of information included in the +* output generated by a Channel. +* +* If Full is zero, then a modest amount of +* non-essential but useful information will be included in the +* output. If Full is negative, all non-essential information will +* be suppressed to minimise the amount of output, while if it is +* positive, the output will include the maximum amount of detailed +* information about the Object being written. + +* Applicability: +* Channel +* The default value is zero for a normal Channel. +* FitsChan +* The default value is zero for a FitsChan. +* XmlChan +* The default value is -1 for an XmlChan. +* StcsChan +* The default value is zero for an StcsChan. Set a positive value +* to cause default values to be included in STC-S descriptions. + +* Notes: +* - All positive values supplied for this attribute are converted +* to +1 and all negative values are converted to -1. +*att-- +*/ + +/* This ia a 3-state value (-1, 0 or +1) with a value of -INT_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(Channel,Full,full,-INT_MAX) +astMAKE_GET(Channel,Full,int,0,( this->full != -INT_MAX ? this->full : 0 )) +astMAKE_SET(Channel,Full,int,full,( value > 0 ? 1 : ( value < 0 ? -1 : 0 ) )) +astMAKE_TEST(Channel,Full,( this->full != -INT_MAX )) + +/* +*att++ +* Name: +* Indent + +* Purpose: +* Specifies the indentation to use in text produced by a Channel. + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the indentation within the output text produced by +f the AST_WRITE function. +c the astWrite function. +* It gives the increase in the indentation for each level in the object +* heirarchy. If it is set to zero, no indentation will be used. [3] + +* Applicability: +* Channel +* The default value is zero for a basic Channel. +* FitsChan +* The FitsChan class ignores this attribute. +* StcsChan +* The default value for an StcsChan is zero, which causes the entire +* STC-S description is written out by a single invocation of the sink +* function. The text supplied to the sink function will not contain +* any linefeed characters, and each pair of adjacent words will be +* separated by a single space. The text may thus be arbitrarily large +* and the StcsLength attribute is ignored. +* +* If Indent is non-zero, then the text is written out via multiple +* calls to the sink function, each call corresponding to a single +* "line" of text (although no line feed characters will be inserted +* by AST). The complete STC-S description is broken into lines so that: +* +* - the line length specified by attribute StcsLength is not exceeded +* - each sub-phrase (time, space, etc.) starts on a new line +* - each argument in a compound spatial region starts on a new line +* +* If this causes a sub-phrase to extend to two or more lines, then the +* second and subsequent lines will be indented by three spaces compared +* to the first line. In addition, lines within a compound spatial region +* will have extra indentation to highlight the nesting produced by the +* parentheses. Each new level of nesting will be indented by a further +* three spaces. +f +f Note, the default value of zero is unlikely to be appropriate when +f an StcsChan is used within Fortran code. In this case, Indent +f should usually be set non-zero, and the StcsLength attribute set to +f the size of the CHARACTER variable used to +f receive the text returned by AST_GETLINE within the sink function. +f This avoids the possibility of long lines being truncated invisibly +f within AST_GETLINE. +* XmlChan +* The default value for an XmlChan is zero, which results in no +* linefeeds or indentation strings being added to output text. +* If any non-zero value is assigned to Indent, then extra linefeed and +* space characters will be inserted as necessary to ensure that each +* XML tag starts on a new line, and each tag will be indented by +* a further 3 spaces to show its depth in the containment hierarchy. +*att-- +*/ + +/* This is an integer value with a value of -INT_MAX when undefined, + yielding a default of 3. Sub-classes may over-ride theis default. */ +astMAKE_CLEAR(Channel,Indent,indent,-INT_MAX) +astMAKE_GET(Channel,Indent,int,3,( this->indent != -INT_MAX ? this->indent : 3 )) +astMAKE_SET(Channel,Indent,int,indent,value) +astMAKE_TEST(Channel,Indent,( this->indent != -INT_MAX )) + +/* +*att++ +* Name: +* ReportLevel + +* Purpose: +* Determines which read/write conditions are reported. + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute determines which, if any, of the conditions that occur +* whilst reading or writing an Object should be reported. These +* conditions will generate either a fatal error or a warning, as +* determined by attribute Strict. ReportLevel can take any of the +* following values: +* +* 0 - Do not report any conditions. +* +* 1 - Report only conditions where significant information content has been +* changed. For instance, an unsupported time-scale has been replaced by a +* supported near-equivalent time-scale. Another example is if a basic +* Channel unexpected encounters data items that may have been introduced +* by later versions of AST. +* +* 2 - Report the above, and in addition report significant default +* values. For instance, if no time-scale was specified when reading an +* Object from an external data source, report the default time-scale +* that is being used. +* +* 3 - Report the above, and in addition report any other potentially +* interesting conditions that have no significant effect on the +* conversion. For instance, report if a time-scale of "TT" +* (terrestrial time) is used in place of "ET" (ephemeris time). This +* change has no signficiant effect because ET is the predecessor of, +* and is continuous with, TT. Synonyms such as "IAT" and "TAI" are +* another example. +* +* The default value is 1. Note, there are many other conditions that +* can occur whilst reading or writing an Object that completely +* prevent the conversion taking place. Such conditions will always +* generate errors, irrespective of the ReportLevel and Strict attributes. + +* Applicability: +* Channel +* All Channels have this attribute. +* FitsChan +* All the conditions selected by the FitsChan Warnings attribute are +* reported at level 1. +*att-- +*/ + +/* This is an integer value with a value of -INT_MAX when undefined, + yielding a default of one. */ +astMAKE_CLEAR(Channel,ReportLevel,report_level,-INT_MAX) +astMAKE_GET(Channel,ReportLevel,int,1,( this->report_level != -INT_MAX ? this->report_level : 1 )) +astMAKE_SET(Channel,ReportLevel,int,report_level,value) +astMAKE_TEST(Channel,ReportLevel,( this->report_level != -INT_MAX )) + +/* +*att++ +* Name: +* Skip + +* Purpose: +* Skip irrelevant data? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This is a boolean attribute which indicates whether the Object +* data being read through a Channel are inter-mixed with other, +* irrelevant, external data. +* +* If Skip is zero (the default), then the source of input data is +* expected to contain descriptions of AST Objects and comments and +* nothing else (if anything else is read, an error will +* result). If Skip is non-zero, then any non-Object data +* encountered between Objects will be ignored and simply skipped +* over in order to reach the next Object. + +* Applicability: +* Channel +* All Channels have this attribute. +* FitsChan +* The FitsChan class sets the default value of this attribute +* to 1, so that all irrelevant FITS headers will normally be +* ignored. +*att-- +*/ + +/* This ia a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(Channel,Skip,skip,-INT_MAX) +astMAKE_GET(Channel,Skip,int,0,( this->skip != -INT_MAX ? this->skip : 0 )) +astMAKE_SET(Channel,Skip,int,skip,( value != 0 )) +astMAKE_TEST(Channel,Skip,( this->skip != -INT_MAX )) + +/* +*att++ +* Name: +* Strict + +* Purpose: +* Report an error if any unexpeted data items are found? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This is a boolean attribute which indicates whether a warning +* rather than an error should be issed for insignificant conversion +* problems. If it is set non-zero, then fatal errors are issued +* instead of warnings, resulting in the +c AST error status being set. +f inherited STATUS variable being set to an error value. +* If Strict is zero (the default), then execution continues after minor +* conversion problems, and a warning message is added to the Channel +* structure. Such messages can be retrieved using the +c astWarnings +f AST_WARNINGS +* function. + +* Notes: +* - This attribute was introduced in AST version 5.0. Prior to this +* version of AST unexpected data items read by a basic Channel always +* caused an error to be reported. So applications linked against +* versions of AST prior to version 5.0 may not be able to read Object +* descriptions created by later versions of AST, if the Object's class +* description has changed. + +* Applicability: +* Channel +* All Channels have this attribute. +*att-- +*/ + +/* This ia a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(Channel,Strict,strict,-INT_MAX) +astMAKE_GET(Channel,Strict,int,0,( this->strict != -INT_MAX ? this->strict : 0 )) +astMAKE_SET(Channel,Strict,int,strict,( value != 0 )) +astMAKE_TEST(Channel,Strict,( this->strict != -INT_MAX )) + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Channel objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Channel objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstChannel *this; /* Pointer to Channel */ + +/* Obtain a pointer to the Channel structure. */ + this = (AstChannel *) obj; + +/* Free memory used to store warnings. */ + astAddWarning( this, 0, NULL, NULL, status ); + +/* Close any open input or output files. */ + if( this->fd_in ) fclose( this->fd_in ); + if( this->fd_out ) fclose( this->fd_out ); + +/* Free file name memory. */ + this->fn_in = astFree( this->fn_in ); + this->fn_out = astFree( this->fn_out ); +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Channel objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Channel objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstChannel *out; /* Pointer to output Channel */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Channels. */ + out = (AstChannel *) objout; + +/* Just clear any references to the input memory from the output Channel. */ + out->warnings = NULL; + out->nwarn = 0; + out->fd_in = NULL; + out->fn_in = NULL; + out->fd_out = NULL; + out->fn_out = NULL; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Channel objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Channel class to an output Channel. + +* Parameters: +* this +* Pointer to the Object whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstChannel *this; /* Pointer to the Channel structure */ + const char *comment; /* Pointer to comment string */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Channel structure. */ + this = (AstChannel *) this_object; + +/* Write out values representing the instance variables for the + Channel class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Indent */ +/* ------------ */ + set = TestIndent( this, status ); + ival = set ? GetIndent( this, status ) : astGetIndent( this ); + astWriteInt( channel, "Indnt", set, 0, ival, "Indentation increment" ); + +/* ReportLevel. */ +/* ------------ */ + set = TestReportLevel( this, status ); + ival = set ? GetReportLevel( this, status ) : astGetReportLevel( this ); + astWriteInt( channel, "RpLev", set, 0, ival, "Error reporting level" ); + +/* Skip. */ +/* ----- */ + set = TestSkip( this, status ); + ival = set ? GetSkip( this, status ) : astGetSkip( this ); + astWriteInt( channel, "Skip", set, 0, ival, + ival ? "Ignore data between Objects" : + "No data allowed between Objects" ); + +/* Strict. */ +/* ------- */ + set = TestStrict( this, status ); + ival = set ? GetStrict( this, status ) : astGetStrict( this ); + astWriteInt( channel, "Strict", set, 0, ival, + ival ? "Report errors insead of warnings" : + "Report warnings instead of errors" ); + +/* Full. */ +/* ----- */ + set = TestFull( this, status ); + ival = set ? GetFull( this, status ) : astGetFull( this ); + if ( ival < 0 ) { + comment = "Suppress non-essential output"; + }else if ( ival == 0 ) { + comment = "Output standard information"; + } else { + comment = "Output maximum information"; + } + astWriteInt( channel, "Full", set, 0, ival, comment ); + +/* Comment. */ +/* -------- */ + set = TestComment( this, status ); + ival = set ? GetComment( this, status ) : astGetComment( this ); + astWriteInt( channel, "Comm", set, 0, ival, + ival ? "Display comments" : + "Omit comments" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAChannel and astCheckChannel functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Channel,Object) +astMAKE_CHECK(Channel) + +AstChannel *astChannel_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, int *status, ...) { +/* +*+ +* Name: +* astChannel + +* Purpose: +* Create a Channel. + +* Type: +* Protected function. + +* Synopsis: +* #include "channel.h" +* AstChannel *astChannel( const char *(* source)( void ), +* void (* sink)( const char * ), +* const char *options, ..., int *status ) + +* Class Membership: +* Channel constructor. + +* Description: +* This function creates a new Channel and optionally initialises +* its attributes. +* +* A Channel implements low-level input/output for the AST library. +* Writing an Object to a Channel (using astWrite) will generate a +* textual representation of that Object, and reading from a +* Channel (using astRead) will create a new Object from its +* textual representation. +* +* Normally, when you use a Channel, you should provide "source" +* and "sink" functions which connect it to an external data store +* by reading and writing the resulting text. By default, however, +* a Channel will read from standard input and write to standard +* output. + +* Parameters: +* source +* Pointer to a "source" function that takes no arguments and +* returns a pointer to a null-terminated string. +* +* This function will be used by the Channel to obtain lines of +* input text. On each invocation, it should return a pointer to +* the next input line read from some external data store, and a +* NULL pointer when there are no more lines to read. +* +* If "source" is NULL, the Channel will read from standard +* input instead. +* sink +* Pointer to a "sink" function that takes a pointer to a +* null-terminated string as an argument and returns void. +* +* This function will be used by the Channel to deliver lines of +* output text. On each invocation, it should deliver the +* contents of the string supplied to some external data store. +* +* If "sink" is NULL, the Channel will write to standard output +* instead. +* options +* Pointer to a null-terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new Channel. The syntax used is identical to +* that for the astSet function and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of additional arguments may follow it in +* order to supply values to be substituted for these +* specifiers. The rules for supplying these are identical to +* those for the astSet function (and for the C "printf" +* function). + +* Returned Value: +* astChannel() +* A pointer to the new Channel. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*- + +* Implementation Notes: +* - This function implements the basic Channel constructor which +* is available via the protected interface to the Channel class. +* A public interface is provided by the astChannelId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstChannel *new; /* Pointer to new Channel */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the Channel, allocating memory and initialising the + virtual function table as well if necessary. Supply pointers to + (local) wrapper functions that can invoke the source and sink + functions with appropriate arguments for the C language. */ + new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab, + "Channel", source, SourceWrap, sink, SinkWrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + Channel's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Channel. */ + return new; +} + +AstChannel *astChannelId_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, ... ) { +/* +*++ +* Name: +c astChannel +f AST_CHANNEL + +* Purpose: +* Create a Channel. + +* Type: +* Public function. + +* Synopsis: +c #include "channel.h" +c AstChannel *astChannel( const char *(* source)( void ), +c void (* sink)( const char * ), +c const char *options, ... ) +f RESULT = AST_CHANNEL( SOURCE, SINK, OPTIONS, STATUS ) + +* Class Membership: +* Channel constructor. + +* Description: +* This function creates a new Channel and optionally initialises +* its attributes. +* +* A Channel implements low-level input/output for the AST library. +c Writing an Object to a Channel (using astWrite) will generate a +f Writing an Object to a Channel (using AST_WRITE) will generate a +* textual representation of that Object, and reading from a +c Channel (using astRead) will create a new Object from its +f Channel (using AST_READ) will create a new Object from its +* textual representation. +* +* Normally, when you use a Channel, you should provide "source" +c and "sink" functions which connect it to an external data store +f and "sink" routines which connect it to an external data store +* by reading and writing the resulting text. By default, however, +* a Channel will read from standard input and write to standard +* output. Alternatively, a Channel can be told to read or write from +* specific text files using the SinkFile and SourceFile attributes, +* in which case no sink or source function need be supplied. + +* Parameters: +c source +f SOURCE = SUBROUTINE (Given) +c Pointer to a source function that takes no arguments and +c returns a pointer to a null-terminated string. If no value +c has been set for the SourceFile attribute, this function +c will be used by the Channel to obtain lines of input text. On +c each invocation, it should return a pointer to the next input +c line read from some external data store, and a NULL pointer +c when there are no more lines to read. +c +c If "source" is NULL and no value has been set for the SourceFile +c attribute, the Channel will read from standard input instead. +f A source routine, which is a subroutine which takes a single +f integer error status argument. If no value has been set +f for the SourceFile attribute, this routine will be used by +f the Channel to obtain lines of input text. On each +f invocation, it should read the next input line from some +f external data store, and then return the resulting text to +f the AST library by calling AST_PUTLINE. It should supply a +f negative line length when there are no more lines to read. +f If an error occurs, it should set its own error status +f argument to an error value before returning. +f +f If the null routine AST_NULL is suppied as the SOURCE value, +f and no value has been set for the SourceFile attribute, +f the Channel will read from standard input instead. +c sink +f SINK = SUBROUTINE (Given) +c Pointer to a sink function that takes a pointer to a +c null-terminated string as an argument and returns void. +c If no value has been set for the SinkFile attribute, this +c function will be used by the Channel to deliver lines of +c output text. On each invocation, it should deliver the +c contents of the string supplied to some external data store. +c +c If "sink" is NULL, and no value has been set for the SinkFile +c attribute, the Channel will write to standard output instead. +f A sink routine, which is a subroutine which takes a single +f integer error status argument. If no value has been set +f for the SinkFile attribute, this routine will be used by +f the Channel to deliver lines of output text. On each +f invocation, it should obtain the next output line from the +f AST library by calling AST_GETLINE, and then deliver the +f resulting text to some external data store. If an error +f occurs, it should set its own error status argument to an +f error value before returning. +f +f If the null routine AST_NULL is suppied as the SINK value, +f and no value has been set for the SinkFile attribute, +f the Channel will write to standard output instead. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Channel. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Channel. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astChannel() +f AST_CHANNEL = INTEGER +* A pointer to the new Channel. + +* Notes: +c - Application code can pass arbitrary data (such as file +c descriptors, etc) to source and sink functions using the +c astPutChannelData function. The source or sink function should use +c the astChannelData macro to retrieve this data. +f - The names of the routines supplied for the SOURCE and SINK +f arguments should appear in EXTERNAL statements in the Fortran +f routine which invokes AST_CHANNEL. However, this is not generally +f necessary for the null routine AST_NULL (so long as the AST_PAR +f include file has been used). +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +f - Note that the null routine AST_NULL (one underscore) is +f different to AST__NULL (two underscores), which is the null Object +f pointer. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astChannel constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astChannel_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astChanel_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstChannel *new; /* Pointer to new Channel */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the Channel, allocating memory and initialising the + virtual function table as well if necessary. Supply pointers to + (local) wrapper functions that can invoke the source and sink + functions with appropriate arguments for the C language. */ + new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab, + "Channel", source, SourceWrap, sink, SinkWrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + Channel's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Channel. */ + return astMakeId( new ); +} + +AstChannel *astChannelForId_( const char *(* source)( void ), + char *(* source_wrap)( const char *(*)( void ), int * ), + void (* sink)( const char * ), + void (* sink_wrap)( void (*)( const char * ), + const char *, int * ), + const char *options, ... ) { +/* +*+ +* Name: +* astChannelFor + +* Purpose: +* Initialise a Channel from a foreign language interface. + +* Type: +* Public function. + +* Synopsis: +* #include "channel.h" +* AstChannel *astChannelFor( const char *(* source)( void ), +* char *(* source_wrap)( const char *(*) +* ( void ), int * ), +* void (* sink)( const char * ), +* void (* sink_wrap)( void (*)( const char * ), +* const char *, int * ), +* const char *options, ... ) + +* Class Membership: +* Channel constructor. + +* Description: +* This function creates a new Channel from a foreign language +* interface and optionally initialises its attributes. +* +* A Channel implements low-level input/output for the AST library. +* Writing an Object to a Channel (using astWrite) will generate a +* textual representation of that Object, and reading from a +* Channel (using astRead) will create a new Object from its +* textual representation. +* +* Normally, when you use a Channel, you should provide "source" +* and "sink" functions which connect it to an external data store +* by reading and writing the resulting text. This function also +* requires you to provide "wrapper" functions which will invoke +* the source and sink functions. By default, however, a Channel +* will read from standard input and write to standard output. + +* Parameters: +* source +* Pointer to a "source" function which will be used to obtain +* lines of input text. Generally, this will be obtained by +* casting a pointer to a source function which is compatible +* with the "source_wrap" wrapper function (below). The pointer +* should later be cast back to its original type by the +* "source_wrap" function before the function is invoked. +* +* If "source" is NULL, the Channel will read from standard +* input instead. +* source_wrap +* Pointer to a function which can be used to invoke the +* "source" function supplied (above). This wrapper function is +* necessary in order to hide variations in the nature of the +* source function, such as may arise when it is supplied by a +* foreign (non-C) language interface. +* +* The single parameter of the "source_wrap" function is a +* pointer to the "source" function, and it should cast this +* function pointer (as necessary) and invoke the function with +* appropriate arguments to obtain the next line of input +* text. The "source_wrap" function should then return a pointer +* to a dynamically allocated, null terminated string containing +* the text that was read. The string will be freed (using +* astFree) when no longer required and the "source_wrap" +* function need not concern itself with this. A NULL pointer +* should be returned if there is no more input to read. +* +* If "source_wrap" is NULL, the Channel will read from standard +* input instead. +* sink +* Pointer to a "sink" function which will be used to deliver +* lines of output text. Generally, this will be obtained by +* casting a pointer to a sink function which is compatible with +* the "sink_wrap" wrapper function (below). The pointer should +* later be cast back to its original type by the "sink_wrap" +* function before the function is invoked. +* +* If "sink" is NULL, the Channel will write to standard output +* instead. +* sink_wrap +* Pointer to a function which can be used to invoke the "sink" +* function supplied (above). This wrapper function is necessary +* in order to hide variations in the nature of the sink +* function, such as may arise when it is supplied by a foreign +* (non-C) language interface. +* +* The first parameter of the "sink_wrap" function is a pointer +* to the "sink" function, and the second parameter is a pointer +* to a const, null-terminated character string containing the +* text to be written. The "sink_wrap" function should cast the +* "sink" function pointer (as necessary) and invoke the +* function with appropriate arguments to deliver the line of +* output text. The "sink_wrap" function then returns void. +* +* If "sink_wrap" is NULL, the Channel will write to standard +* output instead. +* options +* Pointer to a null-terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new Channel. The syntax used is identical to +* that for the astSet function and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of additional arguments may follow it in +* order to supply values to be substituted for these +* specifiers. The rules for supplying these are identical to +* those for the astSet function (and for the C "printf" +* function). + +* Returned Value: +* astChannelFor() +* A pointer to the new Channel. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +* - This function is only available through the public interface +* to the Channel class (not the protected interface) and is +* intended solely for use in implementing foreign language +* interfaces to this class. +*- + +* Implememtation Notes: +* - This function behaves exactly like astChannelId_, in that it +* returns ID values and not true C pointers, but it has two +* additional arguments. These are pointers to the "wrapper +* functions" which are needed to accommodate foreign language +* interfaces. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstChannel *new; /* Pointer to new Channel */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise the Channel, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab, + "Channel", source, source_wrap, sink, sink_wrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + Channel's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Channel. */ + return astMakeId( new ); +} + +AstChannel *astLoadChannel_( void *mem, size_t size, + AstChannelVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadChannel + +* Purpose: +* Load a Channel. + +* Type: +* Protected function. + +* Synopsis: +* #include "channel.h" +* AstChannel *astLoadChannel( void *mem, size_t size, +* AstChannelVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Channel loader. + +* Description: +* This function is provided to load a new Channel using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Channel structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Channel at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the Channel is to be +* loaded. This must be of sufficient size to accommodate the +* Channel data (sizeof(Channel)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Channel (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Channel structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstChannel) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Channel. If this is NULL, a pointer +* to the (static) virtual function table for the Channel class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Channel" is used instead. + +* Returned Value: +* A pointer to the new Channel. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstChannel *new; /* Pointer to the new Channel */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Channel. In this case the + Channel belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstChannel ); + vtab = &class_vtab; + name = "Channel"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitChannelVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Channel. */ + new = astLoadObject( mem, size, (AstObjectVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Channel" ); + +/* Set the pointers to the source and sink functions, and their + wrapper functions, to NULL (we cannot restore these since they + refer to process-specific addresses). */ + new->source = NULL; + new->source_wrap = NULL; + new->sink = NULL; + new->sink_wrap = NULL; + +/* We do not have any data to pass to the source and sink functions. */ + new->data = NULL; + +/* No warnings yet. */ + new->warnings = NULL; + new->nwarn = 0; + +/* Indicate no input or output files have been associated with the + Channel. */ + new->fd_in = NULL; + new->fn_in = NULL; + new->fd_out = NULL; + new->fn_out = NULL; + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Indent. */ +/* ------- */ + new->indent = astReadInt( channel, "indnt", -INT_MAX ); + if ( TestIndent( new, status ) ) SetIndent( new, new->indent, status ); + +/* ReportLevel. */ +/* ------------ */ + new->report_level = astReadInt( channel, "rplev", -INT_MAX ); + if ( TestReportLevel( new, status ) ) SetReportLevel( new, + new->report_level, + status ); + +/* Skip. */ +/* ----- */ + new->skip = astReadInt( channel, "skip", -INT_MAX ); + if ( TestSkip( new, status ) ) SetSkip( new, new->skip, status ); + +/* Strict. */ +/* ------- */ + new->strict = astReadInt( channel, "strict", -INT_MAX ); + if ( TestStrict( new, status ) ) SetStrict( new, new->strict, status ); + +/* Full. */ +/* ----- */ + new->full = astReadInt( channel, "full", -INT_MAX ); + if ( TestFull( new, status ) ) SetFull( new, new->full, status ); + +/* Comment. */ +/* -------- */ + new->comment = astReadInt( channel, "comm", -INT_MAX ); + if ( TestComment( new, status ) ) SetComment( new, new->comment, status ); + +/* If an error occurred, clean up by deleting the new Channel. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Channel pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions + defined by this class. Each simply checks the global error status + and then locates and executes the appropriate member function, + using the function pointer stored in the object's virtual function + table (this pointer is located using the astMEMBER macro defined in + "object.h"). + + Note that the member function may not be the one defined here, as + it may have been over-ridden by a derived class. However, it should + still have the same interface. */ +void astGetNextData_( AstChannel *this, int begin, char **name, char **val, int *status ) { + *name = NULL; + *val = NULL; + if ( !astOK ) return; + (**astMEMBER(this,Channel,GetNextData))( this, begin, name, val, status ); +} +char *astGetNextText_( AstChannel *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Channel,GetNextText))( this, status ); +} +void astPutNextText_( AstChannel *this, const char *line, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,PutNextText))( this, line, status ); +} +AstObject *astRead_( AstChannel *this, int *status ) { + if ( !astOK ) return NULL; + astAddWarning( this, 0, NULL, NULL, status ); + return (**astMEMBER(this,Channel,Read))( this, status ); +} +void astReadClassData_( AstChannel *this, const char *class, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,ReadClassData))( this, class, status ); +} +double astReadDouble_( AstChannel *this, const char *name, double def, int *status ) { + if ( !astOK ) return 0.0; + return (**astMEMBER(this,Channel,ReadDouble))( this, name, def, status ); +} +int astReadInt_( AstChannel *this, const char *name, int def, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Channel,ReadInt))( this, name, def, status ); +} +AstObject *astReadObject_( AstChannel *this, const char *name, + AstObject *def, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Channel,ReadObject))( this, name, def, status ); +} +char *astReadString_( AstChannel *this, const char *name, const char *def, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Channel,ReadString))( this, name, def, status ); +} +void astWriteBegin_( AstChannel *this, const char *class, + const char *comment, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,WriteBegin))( this, class, comment, status ); +} +void astWriteDouble_( AstChannel *this, const char *name, int set, int helpful, + double value, const char *comment, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,WriteDouble))( this, name, set, helpful, value, + comment, status ); +} +void astWriteEnd_( AstChannel *this, const char *class, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,WriteEnd))( this, class, status ); +} +void astWriteInt_( AstChannel *this, const char *name, int set, int helpful, + int value, const char *comment, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,WriteInt))( this, name, set, helpful, value, + comment, status ); +} +void astWriteIsA_( AstChannel *this, const char *class, const char *comment, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,WriteIsA))( this, class, comment, status ); +} +void astWriteString_( AstChannel *this, const char *name, int set, int helpful, + const char *value, const char *comment, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,WriteString))( this, name, set, helpful, value, + comment, status ); +} +void astPutChannelData_( AstChannel *this, void *data, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Channel,PutChannelData))( this, data, status ); +} + +AstKeyMap *astWarnings_( AstChannel *this, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,Channel,Warnings))( this, status ); +} + +/* Because of the variable argument list, we need to work a bit harder on + astAddWarning. Functions that provide implementations of the + astAddWarning method recieve the fully expanded message and so do not + need a variable argument list. */ + +void astAddWarning_( void *this_void, int level, const char *fmt, + const char *method, int *status, ... ) { + AstChannel *this; + char buff[ 201 ]; + va_list args; + int nc; + + this = astCheckChannel( this_void ); + + if( fmt ) { + if( astOK ) { + va_start( args, status ); + nc = vsprintf( buff, fmt, args ); + va_end( args ); + if( nc > 200 ) { + astError( AST__INTER, "astAddWarning(%s): Message buffer size " + "exceeded (internal AST programming error).", + status, astGetClass( this ) ); + } else { + (**astMEMBER(this,Channel,AddWarning))( this, level, buff, method, status ); + } + } + } else { + (**astMEMBER(this,Channel,AddWarning))( this, level, NULL, method, status ); + } +} + +/* Count the number of times astWrite is invoked (excluding invocations + made from within the astWriteObject method - see below). The count is + done here so that invocations of astWrite within a sub-class will be + included. */ +int astWrite_( AstChannel *this, AstObject *object, int *status ) { + astDECLARE_GLOBALS + if ( !astOK ) return 0; + astGET_GLOBALS(this); + nwrite_invoc++; + astAddWarning( this, 0, NULL, NULL, status ); + return (**astMEMBER(this,Channel,Write))( this, object, status ); +} + +/* We do not want to count invocations of astWrite made from within the + astWriteObject method. So decrement the number of invocations first + (this assumes that each invocation of astWriteObject will only invoke + astWrite once). */ +void astWriteObject_( AstChannel *this, const char *name, int set, + int helpful, AstObject *value, const char *comment, int *status ) { + astDECLARE_GLOBALS + if ( !astOK ) return; + astGET_GLOBALS(this); + nwrite_invoc--; + (**astMEMBER(this,Channel,WriteObject))( this, name, set, helpful, value, + comment, status ); +} + + + + + diff --git a/channel.h b/channel.h new file mode 100644 index 0000000..1969ea3 --- /dev/null +++ b/channel.h @@ -0,0 +1,687 @@ +/* +*+ +* Name: +* channel.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Channel class. + +* Invocation: +* #include "channel.h" + +* Description: +* This include file defines the interface to the Channel class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* A Channel is the basic form of AST I/O channel, through which +* Objects may be written and later read back. It causes I/O to +* take place using a textual format via standard input and +* standard output. +* +* Writing to a Channel will result in a textual representation of +* an Object being produced on standard output. Reading from a +* Channel will causes a textual description of an Object to be +* read from standard input, and that Object to be +* re-created. Channel I/O is stream based, and multiple objects +* may be written or read in succession through the same Channel. A +* null Object pointer is returned if there is no more input to +* read. + +* Inheritance: +* The Channel class inherits from the Object class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* Comment (integer) +* A boolean value (0 or 1) which controls whether comments are +* to be included in textual output generated by a Channel. If +* this value is non-zero (the default), then comments will be +* included. If it is zero, comments will be omitted. +* Full (integer) +* A three-state flag (taking values -1, 0 or +1) which controls +* the amount of information included in textual output +* generated by a Channel. If this value is zero (the default), +* then a modest amount of non-essential but useful information +* will be included along with the output. If Full is negative, +* all non-essential information will be suppressed, while if it +* is positive, the output will include the maximum amount of +* information about the Object being written. +* Skip (integer) +* A boolean value which indicates whether the Objects being +* read through a Channel are inter-mixed with other external +* data. If this value is zero (the default), then the source of +* input data is expected to contain descriptions of AST Objects +* and comments and nothing else (if anything else is read, an +* error will result). If Skip is non-zero, then any non-Object +* data encountered between Objects will simply be skipped over +* in order to reach the next Object. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astClearAttrib +* Clear an attribute value for a Mapping. +* astGetAttrib +* Get an attribute value for a Mapping. +* astSetAttrib +* Set an attribute value for a Mapping. +* astTestAttrib +* Test if an attribute value has been set for a Mapping. + +* New Methods Defined: +* Public: +* astRead +* Read an Object from a Channel. +* astWrite +* Write an Object to a Channel. +* +* Protected: +* astClearComment +* Clear the Comment attribute for a Channel. +* astClearFull +* Clear the Full attribute for a Channel. +* astClearSkip +* Clear the Skip attribute for a Channel. +* astGetComment +* Get the value of the Comment attribute for a Channel. +* astGetFull +* Get the value of the Full attribute for a Channel. +* astGetNextData +* Read the next item of data from a data source. +* astGetNextText +* Read the next line of input text from a data source. +* astGetSkip +* Get the value of the Skip attribute for a Channel. +* astPutNextText +* Write a line of output text to a data sink. +* astReadClassData +* Read values from a data source for a class loader. +* astReadDouble +* Read a double value as part of loading a class. +* astReadInt +* Read an int value as part of loading a class. +* astReadObject +* Read a (sub)Object as part of loading a class. +* astReadString +* Read a string value as part of loading a class. +* astSetComment +* Set the value of the Comment attribute for a Channel. +* astSetFull +* Set the value of the Full attribute for a Channel. +* astSetSkip +* Set the value of the Skip attribute for a Channel. +* astTestComment +* Test whether a value has been set for the Comment attribute of a +* Channel. +* astTestFull +* Test whether a value has been set for the Full attribute of a +* Channel. +* astTestSkip +* Test whether a value has been set for the Skip attribute of a +* Channel. +* astWriteBegin +* Write a "Begin" data item to a data sink. +* astWriteDouble +* Write a double value to a data sink. +* astWriteEnd +* Write an "End" data item to a data sink. +* astWriteInt +* Write an integer value to a data sink. +* astWriteIsA +* Write an "IsA" data item to a data sink. +* astWriteObject +* Write an Object as a value to a data sink. +* astWriteString +* Write a string value to a data sink. + +* Other Class Functions: +* Public: +* astChannel +* Create a Channel. +* astChannelFor +* Create a Channel from a foreign language interface. +* astIsAChannel +* Test class membership. +* +* Protected: +* astCheckChannel +* Validate class membership. +* astInitChannel +* Initialise a Channel. +* astInitChannelVtab +* Initialise the virtual function table for the Channel class. +* astLoadChannel +* Load a Channel. + +* Type Definitions: +* Public: +* AstChannel +* Channel object type. +* +* Protected: +* AstChannelVtab +* Channel virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 12-AUG-1996 (RFWS): +* Original version. +* 12-DEC-1996 (RFWS): +* Added the astChannelFor function. +* 11-NOV-2002 (DSB): +* Added astWriteInvocations. +* 8-JAN-2003 (DSB): +* Added protected astInitAxisVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ + +/* Note that the usual setting of the CHANNEL_INCLUDED flag, which + prevents this file being included more than once, must be deferred + until after including the "object.h" file. This is because + "object.h" needs to include the present interface definition (as a + form of "forward reference") in order to have access to I/O + Channels itself. */ +#if !defined( CHANNEL_INCLUDED ) +#define CHANNEL_INCLUDED + +/* C header files. */ +/* --------------- */ +#include + +/* Macros */ +/* ====== */ +/* Define constants used to size global arrays in this module. */ +#define AST__CHANNEL_GETATTRIB_BUFF_LEN 50 + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ + +/* The astWarnings function returns a KeyMap pointer, but we cannot + include keymap.h here to define the AstKeyMap type since keymap.h + itself include sthis file. So we make a temporary declaration which + will be replaced by the full one when the keymap.h file is included. */ +struct AstKeyMap; + +/* Channel structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstChannel { + +/* Attributes inherited from the parent class. */ + AstObject object; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + const char *(* source)( void ); /* Pointer to source function */ + char *(* source_wrap)( const char *(*)(void), int * ); + /* Source wrapper function pointer */ + void (* sink)( const char * ); /* Pointer to sink function */ + void (* sink_wrap)( void (*)( const char *), const char *, int * ); + /* Sink wrapper function pointer */ + int comment; /* Output comments? */ + int full; /* Set max/normal/min information level */ + int skip; /* Skip data between Objects? */ + int indent; /* Indentation increment in astWrite output */ + int report_level; /* Skip data between Objects? */ + int strict; /* Report unexpected data items? */ + void *data; /* Data to pass to source/sink functions */ + char **warnings; /* Array of warning strings */ + int nwarn; /* Size of warnings array */ + FILE *fd_in; /* Descriptor for source text file */ + char *fn_in; /* Full path for source text file */ + FILE *fd_out; /* Descriptor for sink text file */ + char *fn_out; /* Full path for sink text file */ +} AstChannel; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstChannelVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstObjectVtab object_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + struct AstKeyMap *(* Warnings)( AstChannel *, int * ); + AstObject *(* Read)( AstChannel *, int * ); + AstObject *(* ReadObject)( AstChannel *, const char *, AstObject *, int * ); + char *(* GetNextText)( AstChannel *, int * ); + char *(* ReadString)( AstChannel *, const char *, const char *, int * ); + double (* ReadDouble)( AstChannel *, const char *, double, int * ); + int (* GetComment)( AstChannel *, int * ); + int (* GetFull)( AstChannel *, int * ); + int (* GetStrict)( AstChannel *, int * ); + int (* ReadInt)( AstChannel *, const char *, int, int * ); + int (* TestComment)( AstChannel *, int * ); + int (* TestFull)( AstChannel *, int * ); + int (* TestStrict)( AstChannel *, int * ); + int (* Write)( AstChannel *, AstObject *, int * ); + void (* AddWarning)( AstChannel *, int, const char *, const char *, int * ); + void (* ClearComment)( AstChannel *, int * ); + void (* ClearFull)( AstChannel *, int * ); + void (* ClearStrict)( AstChannel *, int * ); + void (* GetNextData)( AstChannel *, int, char **, char **, int * ); + void (* PutChannelData)( AstChannel *, void *, int * ); + void (* PutNextText)( AstChannel *, const char *, int * ); + void (* ReadClassData)( AstChannel *, const char *, int * ); + void (* SetComment)( AstChannel *, int, int * ); + void (* SetFull)( AstChannel *, int, int * ); + void (* SetStrict)( AstChannel *, int, int * ); + void (* WriteBegin)( AstChannel *, const char *, const char *, int * ); + void (* WriteDouble)( AstChannel *, const char *, int, int, double, const char *, int * ); + void (* WriteEnd)( AstChannel *, const char *, int * ); + void (* WriteInt)( AstChannel *, const char *, int, int, int, const char *, int * ); + void (* WriteIsA)( AstChannel *, const char *, const char *, int * ); + void (* WriteObject)( AstChannel *, const char *, int, int, AstObject *, const char *, int * ); + void (* WriteString)( AstChannel *, const char *, int, int, const char *, const char *, int * ); + + int (* GetSkip)( AstChannel *, int * ); + int (* TestSkip)( AstChannel *, int * ); + void (* ClearSkip)( AstChannel *, int * ); + void (* SetSkip)( AstChannel *, int, int * ); + + int (* GetReportLevel)( AstChannel *, int * ); + int (* TestReportLevel)( AstChannel *, int * ); + void (* ClearReportLevel)( AstChannel *, int * ); + void (* SetReportLevel)( AstChannel *, int, int * ); + + int (* GetIndent)( AstChannel *, int * ); + int (* TestIndent)( AstChannel *, int * ); + void (* ClearIndent)( AstChannel *, int * ); + void (* SetIndent)( AstChannel *, int, int * ); + + const char *(* GetSourceFile)( AstChannel *, int * ); + int (* TestSourceFile)( AstChannel *, int * ); + void (* ClearSourceFile)( AstChannel *, int * ); + void (* SetSourceFile)( AstChannel *, const char *, int * ); + + const char *(* GetSinkFile)( AstChannel *, int * ); + int (* TestSinkFile)( AstChannel *, int * ); + void (* ClearSinkFile)( AstChannel *, int * ); + void (* SetSinkFile)( AstChannel *, const char *, int * ); +} AstChannelVtab; + +/* Define a private structure type used to store linked lists of + name-value associations. */ +typedef struct AstChannelValue { + struct AstChannelValue *flink; /* Link to next element */ + struct AstChannelValue *blink; /* Link to previous element */ + char *name; /* Pointer to name string */ + union { /* Holds pointer to value */ + char *string; /* Pointer to string value */ + AstObject *object; /* Pointer to Object value */ + } ptr; + int is_object; /* Whether value is an Object (else string) */ +} AstChannelValue; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstChannelGlobals { + AstChannelVtab Class_Vtab; + int Class_Init; + int AstReadClassData_Msg; + char GetAttrib_Buff[ AST__CHANNEL_GETATTRIB_BUFF_LEN + 1 ]; + int Items_Written; + int Current_Indent; + int Nest; + int Nwrite_Invoc; + char **Object_Class; + AstChannelValue **Values_List; + char **Values_Class; + int *Values_OK; + int *End_Of_Object; + void *Channel_Data; +} AstChannelGlobals; + +#endif + + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Channel) /* Check class membership */ +astPROTO_ISA(Channel) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstChannel *astChannel_( const char *(*)( void ), void (*)( const char * ), + const char *, int *, ...); +#else +AstChannel *astChannelId_( const char *(*)( void ), void (*)( const char * ), + const char *, ... )__attribute__((format(printf,3,4))); +AstChannel *astChannelForId_( const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), + const char *, int * ), + const char *, ... ); +#endif + + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstChannel *astInitChannel_( void *, size_t, int, AstChannelVtab *, + const char *, const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), + const char *, int * ), int * ); + + +/* Vtab initialiser. */ +void astInitChannelVtab_( AstChannelVtab *, const char *, int * ); + +/* Loader. */ +AstChannel *astLoadChannel_( void *, size_t, AstChannelVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitChannelGlobals_( AstChannelGlobals * ); +#endif +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstObject *astRead_( AstChannel *, int * ); +int astWrite_( AstChannel *, AstObject *, int * ); +void astPutChannelData_( AstChannel *, void *, int * ); +void *astChannelData_( void ); +struct AstKeyMap *astWarnings_( AstChannel *, int * ); + +char *astSourceWrap_( const char *(*)( void ), int * ); +void astSinkWrap_( void (*)( const char * ), const char *, int * ); + +# if defined(astCLASS) /* Protected */ +void astStoreChannelData_( AstChannel *, int * ); +AstObject *astReadObject_( AstChannel *, const char *, AstObject *, int * ); +char *astGetNextText_( AstChannel *, int * ); +char *astReadString_( AstChannel *, const char *, const char *, int * ); +double astReadDouble_( AstChannel *, const char *, double, int * ); +int astGetComment_( AstChannel *, int * ); +int astGetFull_( AstChannel *, int * ); +int astGetStrict_( AstChannel *, int * ); +int astReadInt_( AstChannel *, const char *, int, int * ); +int astTestComment_( AstChannel *, int * ); +int astTestFull_( AstChannel *, int * ); +int astTestStrict_( AstChannel *, int * ); +void astAddWarning_( void *, int, const char *, const char *, int *, ... )__attribute__((format(printf,3,6))); +void astClearComment_( AstChannel *, int * ); +void astClearFull_( AstChannel *, int * ); +void astClearStrict_( AstChannel *, int * ); +void astGetNextData_( AstChannel *, int, char **, char **, int * ); +void astPutNextText_( AstChannel *, const char *, int * ); +void astReadClassData_( AstChannel *, const char *, int * ); +void astSetComment_( AstChannel *, int, int * ); +void astSetFull_( AstChannel *, int, int * ); +void astSetStrict_( AstChannel *, int, int * ); +void astWriteBegin_( AstChannel *, const char *, const char *, int * ); +void astWriteDouble_( AstChannel *, const char *, int, int, double, const char *, int * ); +void astWriteEnd_( AstChannel *, const char *, int * ); +void astWriteInt_( AstChannel *, const char *, int, int, int, const char *, int * ); +int astWriteInvocations_( int * ); +void astWriteIsA_( AstChannel *, const char *, const char *, int * ); +void astWriteObject_( AstChannel *, const char *, int, int, AstObject *, const char *, int * ); +void astWriteString_( AstChannel *, const char *, int, int, const char *, const char *, int * ); + +int astGetSkip_( AstChannel *, int * ); +int astTestSkip_( AstChannel *, int * ); +void astClearSkip_( AstChannel *, int * ); +void astSetSkip_( AstChannel *, int, int * ); + +int astGetReportLevel_( AstChannel *, int * ); +int astTestReportLevel_( AstChannel *, int * ); +void astClearReportLevel_( AstChannel *, int * ); +void astSetReportLevel_( AstChannel *, int, int * ); + +int astGetIndent_( AstChannel *, int * ); +int astTestIndent_( AstChannel *, int * ); +void astClearIndent_( AstChannel *, int * ); +void astSetIndent_( AstChannel *, int, int * ); + +const char *astGetSourceFile_( AstChannel *, int * ); +int astTestSourceFile_( AstChannel *, int * ); +void astClearSourceFile_( AstChannel *, int * ); +void astSetSourceFile_( AstChannel *, const char *, int * ); + +const char *astGetSinkFile_( AstChannel *, int * ); +int astTestSinkFile_( AstChannel *, int * ); +void astClearSinkFile_( AstChannel *, int * ); +void astSetSinkFile_( AstChannel *, const char *, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them to + validate their own arguments. We must use a cast when passing object + pointers (so that they can accept objects from derived classes). */ + +/* Check class membership. */ +#define astCheckChannel(this) astINVOKE_CHECK(Channel,this,0) +#define astVerifyChannel(this) astINVOKE_CHECK(Channel,this,1) + +/* Test class membership. */ +#define astIsAChannel(this) astINVOKE_ISA(Channel,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astChannel astINVOKE(F,astChannel_) +#else +#define astChannel astINVOKE(F,astChannelId_) +#define astChannelFor astINVOKE(F,astChannelForId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitChannel(mem,size,init,vtab,name,source,source_wrap,sink,sink_wrap) \ +astINVOKE(O,astInitChannel_(mem,size,init,vtab,name,source,source_wrap,sink,sink_wrap,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitChannelVtab(vtab,name) astINVOKE(V,astInitChannelVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadChannel(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadChannel_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to member functions. */ +/* ------------------------------- */ +/* Here we make use of astCheckChannel to validate Channel pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astRead(this) \ +astINVOKE(O,astRead_(astCheckChannel(this),STATUS_PTR)) +#define astWrite(this,object) \ +astINVOKE(V,astWrite_(astCheckChannel(this),astCheckObject(object),STATUS_PTR)) +#define astPutChannelData(this,data) \ +astINVOKE(V,astPutChannelData_(astCheckChannel(this),data,STATUS_PTR)) +#define astWarnings(this) \ +astINVOKE(O,astWarnings_(astCheckChannel(this),STATUS_PTR)) + +#define astSourceWrap astSourceWrap_ +#define astSinkWrap astSinkWrap_ +#define astChannelData astChannelData_() + +#if defined(astCLASS) /* Protected */ +#define astAddWarning astAddWarning_ +#define astStoreChannelData(this) \ +astStoreChannelData_(astCheckChannel(this),STATUS_PTR) + +#define astClearComment(this) \ +astINVOKE(V,astClearComment_(astCheckChannel(this),STATUS_PTR)) +#define astClearFull(this) \ +astINVOKE(V,astClearFull_(astCheckChannel(this),STATUS_PTR)) +#define astClearStrict(this) \ +astINVOKE(V,astClearStrict_(astCheckChannel(this),STATUS_PTR)) +#define astGetComment(this) \ +astINVOKE(V,astGetComment_(astCheckChannel(this),STATUS_PTR)) +#define astGetFull(this) \ +astINVOKE(V,astGetFull_(astCheckChannel(this),STATUS_PTR)) +#define astGetNextData(this,begin,name,val) \ +astINVOKE(V,astGetNextData_(astCheckChannel(this),begin,name,val,STATUS_PTR)) +#define astGetNextText(this) \ +astINVOKE(V,astGetNextText_(astCheckChannel(this),STATUS_PTR)) +#define astGetStrict(this) \ +astINVOKE(V,astGetStrict_(astCheckChannel(this),STATUS_PTR)) +#define astPutNextText(this,line) \ +astINVOKE(V,astPutNextText_(astCheckChannel(this),line,STATUS_PTR)) +#define astReadClassData(this,class) \ +astINVOKE(V,astReadClassData_(astCheckChannel(this),class,STATUS_PTR)) +#define astReadDouble(this,name,def) \ +astINVOKE(V,astReadDouble_(astCheckChannel(this),name,def,STATUS_PTR)) +#define astReadInt(this,name,def) \ +astINVOKE(V,astReadInt_(astCheckChannel(this),name,def,STATUS_PTR)) +#define astReadObject(this,name,def) \ +astINVOKE(O,astReadObject_(astCheckChannel(this),name,(def)?astCheckObject(def):NULL,STATUS_PTR)) +#define astReadString(this,name,def) \ +astINVOKE(V,astReadString_(astCheckChannel(this),name,def,STATUS_PTR)) +#define astSetComment(this,value) \ +astINVOKE(V,astSetComment_(astCheckChannel(this),value,STATUS_PTR)) +#define astSetFull(this,value) \ +astINVOKE(V,astSetFull_(astCheckChannel(this),value,STATUS_PTR)) +#define astSetStrict(this,value) \ +astINVOKE(V,astSetStrict_(astCheckChannel(this),value,STATUS_PTR)) +#define astTestComment(this) \ +astINVOKE(V,astTestComment_(astCheckChannel(this),STATUS_PTR)) +#define astTestFull(this) \ +astINVOKE(V,astTestFull_(astCheckChannel(this),STATUS_PTR)) +#define astTestStrict(this) \ +astINVOKE(V,astTestStrict_(astCheckChannel(this),STATUS_PTR)) +#define astWriteBegin(this,class,comment) \ +astINVOKE(V,astWriteBegin_(astCheckChannel(this),class,comment,STATUS_PTR)) +#define astWriteDouble(this,name,set,helpful,value,comment) \ +astINVOKE(V,astWriteDouble_(astCheckChannel(this),name,set,helpful,value,comment,STATUS_PTR)) +#define astWriteEnd(this,class) \ +astINVOKE(V,astWriteEnd_(astCheckChannel(this),class,STATUS_PTR)) +#define astWriteInt(this,name,set,helpful,value,comment) \ +astINVOKE(V,astWriteInt_(astCheckChannel(this),name,set,helpful,value,comment,STATUS_PTR)) +#define astWriteIsA(this,class,comment) \ +astINVOKE(V,astWriteIsA_(astCheckChannel(this),class,comment,STATUS_PTR)) +#define astWriteObject(this,name,set,helpful,value,comment) \ +astINVOKE(V,astWriteObject_(astCheckChannel(this),name,set,helpful,astCheckObject(value),comment,STATUS_PTR)) +#define astWriteString(this,name,set,helpful,value,comment) \ +astINVOKE(V,astWriteString_(astCheckChannel(this),name,set,helpful,value,comment,STATUS_PTR)) + +#define astWriteInvocations astWriteInvocations_(STATUS_PTR) + +#define astClearSkip(this) \ +astINVOKE(V,astClearSkip_(astCheckChannel(this),STATUS_PTR)) +#define astGetSkip(this) \ +astINVOKE(V,astGetSkip_(astCheckChannel(this),STATUS_PTR)) +#define astSetSkip(this,value) \ +astINVOKE(V,astSetSkip_(astCheckChannel(this),value,STATUS_PTR)) +#define astTestSkip(this) \ +astINVOKE(V,astTestSkip_(astCheckChannel(this),STATUS_PTR)) + +#define astClearReportLevel(this) \ +astINVOKE(V,astClearReportLevel_(astCheckChannel(this),STATUS_PTR)) +#define astGetReportLevel(this) \ +astINVOKE(V,astGetReportLevel_(astCheckChannel(this),STATUS_PTR)) +#define astSetReportLevel(this,value) \ +astINVOKE(V,astSetReportLevel_(astCheckChannel(this),value,STATUS_PTR)) +#define astTestReportLevel(this) \ +astINVOKE(V,astTestReportLevel_(astCheckChannel(this),STATUS_PTR)) + +#define astClearIndent(this) \ +astINVOKE(V,astClearIndent_(astCheckChannel(this),STATUS_PTR)) +#define astGetIndent(this) \ +astINVOKE(V,astGetIndent_(astCheckChannel(this),STATUS_PTR)) +#define astSetIndent(this,value) \ +astINVOKE(V,astSetIndent_(astCheckChannel(this),value,STATUS_PTR)) +#define astTestIndent(this) \ +astINVOKE(V,astTestIndent_(astCheckChannel(this),STATUS_PTR)) + +#define astClearSourceFile(this) \ +astINVOKE(V,astClearSourceFile_(astCheckChannel(this),STATUS_PTR)) +#define astGetSourceFile(this) \ +astINVOKE(V,astGetSourceFile_(astCheckChannel(this),STATUS_PTR)) +#define astSetSourceFile(this,value) \ +astINVOKE(V,astSetSourceFile_(astCheckChannel(this),value,STATUS_PTR)) +#define astTestSourceFile(this) \ +astINVOKE(V,astTestSourceFile_(astCheckChannel(this),STATUS_PTR)) + +#define astClearSinkFile(this) \ +astINVOKE(V,astClearSinkFile_(astCheckChannel(this),STATUS_PTR)) +#define astGetSinkFile(this) \ +astINVOKE(V,astGetSinkFile_(astCheckChannel(this),STATUS_PTR)) +#define astSetSinkFile(this,value) \ +astINVOKE(V,astSetSinkFile_(astCheckChannel(this),value,STATUS_PTR)) +#define astTestSinkFile(this) \ +astINVOKE(V,astTestSinkFile_(astCheckChannel(this),STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/circle.c b/circle.c new file mode 100644 index 0000000..c795b98 --- /dev/null +++ b/circle.c @@ -0,0 +1,2900 @@ +/* +*class++ +* Name: +* Circle + +* Purpose: +* A circular or spherical region within a Frame. + +* Constructor Function: +c astCircle +f AST_CIRCLE + +* Description: +* The Circle class implements a Region which represents a circle or +* sphere within a Frame. + +* Inheritance: +* The Circle class inherits from the Region class. + +* Attributes: +* The Circle class does not define any new attributes beyond +* those which are applicable to all Regions. + +* Functions: +c In addition to those functions applicable to all Regions, the +c following functions may also be applied to all Circles: +f In addition to those routines applicable to all Regions, the +f following routines may also be applied to all Circles: +* +c - astCirclePars: Get the geometric parameters of the Circle +f - AST_CIRCLEPARS: Get the geometric parameters of the Circle + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 31-AUG-2004 (DSB): +* Original version. +* 4-NOV-2013 (DSB): +* Modify RegPins so that it can handle uncertainty regions that straddle +* a discontinuity. Previously, such uncertainty Regions could have a huge +* bounding box resulting in matching region being far too big. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Circle + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "box.h" /* Box Regions */ +#include "wcsmap.h" /* Definitons of AST__DPI etc */ +#include "circle.h" /* Interface definition for this class */ +#include "ellipse.h" /* Interface definition for ellipse class */ +#include "mapping.h" /* Position mappings */ +#include "unitmap.h" /* Unit Mapping */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static void (* parent_resetcache)( AstRegion *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Circle) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Circle,Class_Init) +#define class_vtab astGLOBAL(Circle,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstCircleVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstCircle *astCircleId_( void *, int, const double[], const double[], void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double *CircumPoint( AstFrame *, int, const double *, double, int * ); +static double *RegCentre( AstRegion *this, double *, double **, int, int, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static void Cache( AstCircle *, int * ); +static void CalcPars( AstFrame *, AstPointSet *, double *, double *, double *, int * ); +static void CirclePars( AstCircle *, double *, double *, double *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void RegBaseBox( AstRegion *this, double *, double *, int * ); +static void ResetCache( AstRegion *this, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); + +/* Member functions. */ +/* ================= */ + +AstRegion *astBestCircle_( AstPointSet *mesh, double *cen, AstRegion *unc, int *status ){ +/* +*+ +* Name: +* astBestCircle + +* Purpose: +* Find the best fitting Circle through a given mesh of points. + +* Type: +* Protected function. + +* Synopsis: +* #include "circle.h" +* AstRegion *astBestCircle( AstPointSet *mesh, double *cen, AstRegion *unc ) + +* Class Membership: +* Circle member function + +* Description: +* This function finds the best fitting Circle through a given mesh of +* points. + +* Parameters: +* mesh +* Pointer to a PointSet holding the mesh of points. They are +* assumed to be in the Frame represented by "unc". +* cen +* Pointer to an array holding the coordinates of the new Circle +* centre. +* unc +* A Region representing the uncertainty associated with each point +* on the mesh. + +* Returned Value: +* Pointer to the best fitting Circle. It will inherit the positional +* uncertainty and Frame represented by "unc". + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + AstRegion *result; + double *p; + double rad; + double **ptr; + double d; + double s2r; + double p0; + int ic; + int ip; + int n; + int nc; + int np; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get no. of points in the mesh, and the number of axis values per point. */ + np = astGetNpoint( mesh ); + nc = astGetNcoord( mesh ); + +/* Get pointers to the axis values. */ + ptr = astGetPoints( mesh ); + +/* Check pointers can be used safely */ + if( astOK ) { + +/* We find ther sum of the squared axis increments from the supplied + centre to each of the supplied points. Initialise the sum to zero. */ + s2r = 0.0; + n = 0; + +/* Loop round all axes. */ + for( ic = 0; ic < nc; ic++ ) { + p = ptr[ ic ]; + p0 = cen[ ic ]; + +/* Loop round all values for this axis. */ + for( ip = 0; ip < np; ip++, p++ ) { + if( *p != AST__BAD ) { + +/* Increment the sums */ + d = *p - p0; + s2r += d*d; + n++; + + } + } + } + +/* Find the RMS distance of the points from the supplied centre. This is + the radius of the best fitting circle. */ + if( n > 0 ) { + rad = sqrt( nc*s2r/n ); + +/* Create the returned Region. */ + result = (AstRegion *) astCircle( unc, 1, cen, &rad, unc, "", status ); + } + } + +/* Return NULL if anything went wrong. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result.*/ + return result; +} + +static void Cache( AstCircle *this, int *status ){ +/* +* Name: +* Cache + +* Purpose: +* Calculate intermediate values and cache them in the Circle structure. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* void Cache( AstCircle *this, int *status ) + +* Class Membership: +* Circle member function + +* Description: +* This function uses the PointSet stored in the parent Region to calculate +* some intermediate values which are useful in other methods. These +* values are stored within the Circle structure. + +* Parameters: +* this +* Pointer to the Circle. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *frm; + double *centre; + double *lb; + double *ub; + double radius; + int i; + int nc; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do Nothing if the cached information is up to date. */ + if( this->stale ) { + +/* Get a pointer to the base Frame and the number of base axes. */ + frm = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + nc = astGetNaxes( frm ); + +/* Allocate memory to hold the centre coords. */ + centre = astMalloc( sizeof( double )*astGetNaxes( frm ) ); + +/* Get the radius and centre of the Circle in the base Frame, using the + centre and circumference positions stored in the parent Region structure. */ + CalcPars( frm, ( (AstRegion *) this)->points, centre, &radius, NULL, + status ); + +/* Allocate memory to store the base frame bounding box. This is just + initialised here. It is set properly when the astRegBaseMesh + function is called. This box should not be used unless the "basemesh" + component of the parent Region structure is set to a non-null value. */ + lb = (double *) astMalloc( sizeof( double )*(size_t) nc ); + ub = (double *) astMalloc( sizeof( double )*(size_t) nc ); + +/* Initialise the bounding box. */ + for( i = 0; astOK && i < nc; i++ ) { + lb[ i ] = -DBL_MAX; + ub[ i ] = DBL_MAX; + } + +/* If everything went OK, store these values in the Circle structure. */ + if( astOK ) { + this->radius = radius; + + astFree( this->centre ); + this->centre = centre; + centre = NULL; + + astFree( this->lb ); + this->lb = lb; + lb = NULL; + + astFree( this->ub ); + this->ub = ub; + ub = NULL; + } + +/* Free resources */ + frm = astAnnul( frm ); + if( centre ) centre = astFree( centre ); + +/* Indicate cached information is up to date. */ + this->stale = 0; + } +} + +static void CalcPars( AstFrame *frm, AstPointSet *pset, double *centre, + double *radius, double *p1, int *status ){ +/* +* Name: +* CalcPars + +* Purpose: +* Calculate the geometric parameters of the supplied Circle. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* double *CalcPars( AstFrame *frm, AstPointSet *pset, double *centre, +* double *radius, double *p1, int *status ) + +* Class Membership: +* Circle member function + +* Description: +* This function uses the supplied PointSet to calculate the geometric +* parameters that describe the a crcle. These values are returned in +* a newly allocated dynamic array. + +* Parameters: +* frm +* Pointer to the Frame in which the circle is defined. +* pset +* Pointer to a PointSet. The first point should be the circle +* centre, and the second point should be a point on the circle +* circumference. +* centre +* An array in which to return the axis values at the circle centre. +* The length of this array should be no less than the number of +* axes in "frm". +* radius +* Pointer to a double in which to return the circle radius, +* expressed as a geodesic distance in the supplied Frame. +* p1 +* An array in which to return the coordinates of a point on the +* circumference of the circle. The length of this array should be +* no less than the number of axes in "frm". Can be NULL if the +* circumference position is not needed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double **ptr; + double *circum; + int i; + int nc; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get and the number of axes. */ + nc = astGetNaxes( frm ); + +/* Get pointers to the coordinate data in the supplied PointSet. */ + ptr = astGetPoints( pset ); + +/* If no p1 array was supplied, create a temporary work array to hold the + circumference position. */ + if( !p1 ) { + circum = astMalloc( sizeof( double )*nc ); + } else { + circum = p1; + } + +/* Check pointers can be used safely. */ + if( ptr ) { + +/* Copy the two points in to the allocated memory. */ + for( i = 0; i < nc; i++ ) { + centre[ i ] = ptr[ i ][ 0 ]; + circum[ i ] = ptr[ i ][ 1 ]; + } + +/* Return the geodesic distance between these two points as the radius. */ + *radius = astDistance( frm, centre, circum ); + } + +/* Free any work array. */ + if( !p1 ) circum = astFree( circum ); +} + +static void CirclePars( AstCircle *this, double *centre, double *radius, + double *p1, int *status ){ +/* +*++ +* Name: +c astCirclePars +f AST_CIRCLEPARS + +* Purpose: +* Returns the geometric parameters of an Circle. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "circle.h" +c void astCirclePars( AstCircle *this, double *centre, double *radius, +c double *p1 ) +f CALL AST_CIRCLEPARS( THIS, CENTRE, RADIUS, P1, STATUS ) + +* Class Membership: +* Region method. + +* Description: +c This function +f This routine +* returns the geometric parameters describing the supplied Circle. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c centre +f CENTRE( * ) = DOUBLE PRECISION (Returned) +c Pointer to an array +f An array +* in which to return the coordinates of the Circle centre. +* The length of this array should be no less than the number of +* axes in the associated coordinate system. +c radius +f RADIUS = DOUBLE PRECISION (Returned) +* Returned holding the radius of the Circle, as an geodesic +* distance in the associated coordinate system. +c p1 +f P1( * ) = DOUBLE PRECISION (Returned) +c Pointer to an array +f An array +* in which to return the coordinates of a point on the +* circumference of the Circle. The length of this array should be +* no less than the number of axes in the associated coordinate system. +c A NULL pointer can be supplied if the circumference position is +c not needed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the coordinate system represented by the Circle has been +* changed since it was first created, the returned parameters refer +* to the new (changed) coordinate system, rather than the original +* coordinate system. Note however that if the transformation from +* original to new coordinate system is non-linear, the shape +* represented by the supplied Circle object may not be an accurate +* circle. +*-- +*/ + +/* Local Variables: */ + AstRegion *this_region; /* Parent Region pointer */ + AstFrame *frm; /* Current Frame represented by the Circle */ + AstPointSet *pset; /* PointSet holding PointList axis values */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Store a pointer to the parent region structure. */ + this_region = (AstRegion *) this; + +/* Transform the base Frame axis values into the current Frame. */ + pset = astTransform( this_region->frameset, this_region->points, 1, NULL ); + +/* Get the Circle frame. */ + frm = astGetFrame( this_region->frameset, AST__CURRENT ); + +/* Calculate the required parameters. */ + CalcPars( frm, pset, centre, radius, p1, status ); + +/* Free resources */ + frm = astAnnul( frm ); + pset = astAnnul( pset ); +} + +static double *CircumPoint( AstFrame *frm, int nax, const double *centre, + double radius, int *status ){ +/* +* Name: +* CircumPoint + +* Purpose: +* Find a point on the circumference of the circle. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* double *CircumPoint( AstFrame *frm, int nax, const double *centre, +* double radius, int *status ) + +* Class Membership: +* Circle member function + +* Description: +* This function returns a dynamically allocated array containing the +* axis values at a point on the circumference of the circle specified +* by a given centre and radius. The returned point is the point at +* which the circle crosses the first axis. + +* Parameters: +* frm +* Pointer to the Frame in which the circle is defined. +* nax +* The number of axes in the Frame. +* centre +* An array holding the axis values at the circle centre. +* radius +* The circle radius, expressed as a geodesic distance in the +* supplied Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a 1D array holding the axis values at the point where +* the circle crosses the first frame axis. The length of this array +* will equal the number of axes in the supsplied Frame. It should be +* freed using astFree when no longer needed. + +*/ + +/* Local Variables: */ + double *circum; + double *work; + int i; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Allocate the returned array. */ + circum = astMalloc( sizeof( double)*(size_t) nax ); + +/* Allocate work space */ + work = astMalloc( sizeof( double)*(size_t) nax ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Find the coords of a point that is offset away from the centre + position along the first axis. We use the supplied radius value as a + convenient offset length, but the actual length used is not critical. */ + for( i = 0; i < nax; i++ ) work[ i ] = centre[ i ]; + work[ 0 ] = astAxOffset( frm, 1, work[ 0 ], radius ); + +/* Offset away from the centre position, towards the position found + above, going the distance specified by the supplied radius. */ + astOffset( frm, centre, work, radius, (double *) circum ); + } + +/* Free resources. */ + work = astFree( work ); + +/* Return the result. */ + return circum; +} + +void astInitCircleVtab_( AstCircleVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitCircleVtab + +* Purpose: +* Initialise a virtual function table for a Circle. + +* Type: +* Protected function. + +* Synopsis: +* #include "circle.h" +* void astInitCircleVtab( AstCircleVtab *vtab, const char *name ) + +* Class Membership: +* Circle vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Circle class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsACircle) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->CirclePars = CirclePars; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_resetcache = region->ResetCache; + region->ResetCache = ResetCache; + + region->RegPins = RegPins; + region->RegTrace = RegTrace; + region->RegBaseMesh = RegBaseMesh; + region->RegBaseBox = RegBaseBox; + region->RegCentre = RegCentre; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "Circle", "Circular or spherical region" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* Circle member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCircle *this; /* Pointer to Circle structure */ + AstFrame *frm; /* Pointer to base Frame */ + const char *class; /* Pointer to class name */ + int i; /* Axis index */ + int nb; /* No. of axes in base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Circle structure */ + this = (AstCircle *) this_region; + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* Get a pointer to the base Frame in the Region, and get the number of + axes. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + nb = astGetNaxes( frm ); + +/* If the Frame is a simple Frame, we can assume plane geometry. */ + class = astGetClass( frm ); + if( class && !strcmp( class, "Frame" ) ) { + for( i = 0; i < nb; i++ ) { + lbnd[ i ] = ( this->centre )[ i ] - this->radius; + ubnd[ i ] = ( this->centre )[ i ] + this->radius; + } + +/* If the Frame is not a simple Frame we cannot assume plane geometry. */ + } else { + +/* The bounding box of the mesh returned by astRegBaseMesh is used as the + bounding box of the Circle. These bounds are cached in the Circle + structure by astRegBaseMesh. Ensure astRegBaseMesh has been invoked, + so that it is safe to use the cached bounding box. */ + if( !this_region->basemesh ) (void) astAnnul( astRegBaseMesh( this ) ); + +/* Store the bounding box. */ + for( i = 0; i < nb; i++ ) { + lbnd[ i ] = this->lb[ i ]; + ubnd[ i ] = this->ub[ i ]; + } + } + +/* Free resources. */ + frm = astAnnul( frm ); +} + +static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* Circle member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the accuracies which were +* supplied when the Region was created. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Local Constants: */ +#define NP_EDGE 50 /* No. of points for determining geodesic */ + +/* Local Variables: */ + AstBox *box; /* Bounding box for this Circle */ + AstCircle *this; /* The Circle structure */ + AstRegion *reg; /* Copy of supplied Circle */ + AstFrame *frm; /* Base Frame in encapsulated FrameSet */ + AstPointSet *result; /* Returned pointer */ + double **ptr; /* Pointers to data */ + double *p1; /* Pointer to array holding a single point */ + double *p2; /* Pointer to array holding a single point */ + double angle; /* Angular position of point */ + double delta; /* Angular separation of points */ + double dist; /* Offset along an axis */ + double lbx; /* Lower x bound of mesh bounding box */ + double lby; /* Lower y bound of mesh bounding box */ + double p[ 2 ]; /* Position in 2D Frame */ + double ubx; /* Upper x bound of mesh bounding box */ + double uby; /* Upper y bound of mesh bounding box */ + int i; /* Point index */ + int j; /* Axis index */ + int naxes; /* No. of axes in base Frame */ + int np; /* No. of points in returned PointSet */ + +/* Initialise */ + result= NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this_region->basemesh ) { + result = astClone( this_region->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* Get a pointer to the Circle structure. */ + this = (AstCircle *) this_region; + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Get the number of axes in the base Frame */ + naxes = astGetNaxes( frm ); + +/* Get the requested number of points to put on the mesh. */ + np = astGetMeshSize( this ); + +/* Ensure cached information is available. */ + Cache( (AstCircle *) this, status ); + +/* First deal with 1-D "circles" (where we ignore MeshSize). */ + if( naxes == 1 ) { + +/* The boundary of a 1-D circle consists of 2 points - the two extreme values. + Create a PointSet to hold 2 1-D values, and store the extreme values. */ + result = astPointSet( 2, 1, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + ptr[ 0 ][ 0 ] = ( this->centre )[ 0 ] - this->radius; + ptr[ 0 ][ 1 ] = ( this->centre )[ 0 ] + this->radius; + } + +/* Store the bounding box in the Circle structure. */ + this->lb[ 0 ] = ptr[ 0 ][ 0 ]; + this->ub[ 0 ] = ptr[ 0 ][ 1 ]; + +/* Now deal with 2-D circles. */ + } else if( naxes == 2 ){ + +/* Store the angular increment between points. */ + delta = 2*AST__DPI/np; + +/* Create a suitable PointSet to hold the returned positions. */ + result = astPointSet( np, 2, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Initialise the bounding box of the mesh points. */ + lbx = DBL_MAX; + ubx = -DBL_MAX; + lby = DBL_MAX; + uby = -DBL_MAX; + +/* Loop round each point. */ + angle = 0.0; + for( i = 0; i < np; i++ ) { + +/* Work out where the end of the radius vector at this angle is, and + store in the returned PointSet. */ + astOffset2( frm, this->centre, angle, this->radius, p ); + ptr[ 0 ][ i ] = p[ 0 ]; + ptr[ 1 ][ i ] = p[ 1 ]; + +/* Update the bounds of the mesh bounding box. The box is expressed in + terms of axis offsets from the centre, in order to avoid problems with + boxes that cross RA=0 or RA=12h */ + if( p[ 0 ] != AST__BAD && p[ 1 ] != AST__BAD ){ + dist = astAxDistance( frm, 1, this->centre[ 0 ], p[ 0 ] ); + if( dist < lbx ) { + lbx = dist; + } else if( dist > ubx ) { + ubx = dist; + } + dist = astAxDistance( frm, 2, this->centre[ 1 ], p[ 1 ] ); + if( dist < lby ) { + lby = dist; + } else if( dist > uby ) { + uby = dist; + } + } + +/* Increment the angular position of the next mesh point. */ + angle += delta; + } + +/* Store the bounding box in the Circle structure. */ + this->lb[ 0 ] = this->centre[ 0 ] + lbx; + this->lb[ 1 ] = this->centre[ 1 ] + lby; + this->ub[ 0 ] = this->centre[ 0 ] + ubx; + this->ub[ 1 ] = this->centre[ 1 ] + uby; + } + +/* Now deal with circles with more than 2 dimensions. Producing an evenly + spread mesh of points over a sphere is a complex task (see e.g. + http://www.eso.org/science/healpix/ ). This implementation does not + attempt to produce a genuinely even spread. Instead it simply uses the + mesh for the bounding box of the sphere, and projects each point on to + the surface of the sphere. */ + } else { + +/* Allocate memory to hold an approximation of the circle bounding box. */ + p1 = astMalloc( sizeof( double )*(size_t) naxes ); + p2 = astMalloc( sizeof( double )*(size_t) naxes ); + +/* Get an approximation to the bounding box, and initialise the real + bounding box of the mesh points. */ + if( astOK ) { + memcpy( p1, this->centre, sizeof( double )*(size_t) naxes ); + for( j = 0; j < naxes; j++ ) { + p1[ j ] += this->radius; + astOffset( frm, this->centre, p1, this->radius, p2 ); + p1[ j ] = this->centre[ j ]; + this->ub[ j ] = p2[ j ]; + } + } + +/* Create a Box region which just encompasses the circle. */ + box = astBox( frm, 0, this->centre, this->ub, NULL, "", status ); + +/* Get a mesh covering this box. */ + astSetMeshSize( box, np ); + result = astRegBaseMesh( box ); + ptr = astGetPoints( result ); + np = astGetNpoint( result ); + +/* Allocate memory for a single point */ + if( astOK ) { + +/* Initialise the real bounding box of the mesh points. */ + for( j = 0; j < naxes; j++ ) { + this->lb[ j ] = DBL_MAX; + this->ub[ j ] = -DBL_MAX; + } + +/* Move each point in this mesh radially so that its distance from the centre + equals the radius of this Circle. */ + for( i = 0; i < np; i++ ) { + for( j = 0; j < naxes; j++ ) p1[ j ] = ptr[ j ][ i ]; + astOffset( frm, this->centre, p1, this->radius, p2 ); + + for( j = 0; j < naxes; j++ ) { + ptr[ j ][ i ] = p2[ j ]; + +/* Update the bounds of the mesh bounding box. */ + if( p2[ j ] != AST__BAD ){ + if( p2[ j ] < this->lb[ j ] ) { + this->lb[ j ] = p2[ j ]; + } else if( p2[ j ] > this->ub[ j ] ) { + this->ub[ j ] = p2[ j ]; + } + } + } + } + } + +/* Same the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK && result ) this_region->basemesh = astClone( result ); + +/* Free resources. */ + p1 = astFree( p1 ); + p2 = astFree( p2 ); + box = astAnnul( box ); + } + +/* Extend the bounding box if it contains any singularies. The astNormBox + requires a Mapping which can be used to test points in the base Frame. + Create a copy of the Circle and then set its FrameSet so that the current + Frame in the copy is the same as the base Frame in the original. */ + reg = astCopy( this ); + astSetRegFS( reg, frm ); + astSetNegated( reg, 0 ); + +/* Normalise this box. */ + astNormBox( frm, this->lb, this->ub, reg ); + +/* Free resources. */ + reg = astAnnul( reg ); + frm = astAnnul( frm ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static double *RegCentre( AstRegion *this_region, double *cen, double **ptr, + int index, int ifrm, int *status ){ +/* +* Name: +* RegCentre + +* Purpose: +* Re-centre a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* double *RegCentre( AstRegion *this, double *cen, double **ptr, +* int index, int ifrm, int *status ) + +* Class Membership: +* Circle member function (over-rides the astRegCentre protected +* method inherited from the Region class). + +* Description: +* This function shifts the centre of the supplied Region to a +* specified position, or returns the current centre of the Region. + +* Parameters: +* this +* Pointer to the Region. +* cen +* Pointer to an array of axis values, giving the new centre. +* Supply a NULL value for this in order to use "ptr" and "index" to +* specify the new centre. +* ptr +* Pointer to an array of pointers, one for each axis in the Region. +* Each pointer locates an array of axis values. This is the format +* returned by the PointSet method astGetPoints. Only used if "cen" +* is NULL. +* index +* The index of the point within the arrays identified by "ptr" at +* which is stored the coords for the new centre position. Only used +* if "cen" is NULL. +* ifrm +* Should be AST__BASE or AST__CURRENT. Indicates whether the centre +* position is supplied and returned in the base or current Frame of +* the FrameSet encapsulated within "this". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If both "cen" and "ptr" are NULL then a pointer to a newly +* allocated dynamic array is returned which contains the centre +* coords of the Region. This array should be freed using astFree when +* no longer needed. If either of "ptr" or "cen" is not NULL, then a +* NULL pointer is returned. + +* Notes: +* - Some Region sub-classes do not have a centre. Such classes will report +* an AST__INTER error code if this method is called. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to base Frame */ + AstCircle *this; /* Pointer to Circle structure */ + double **rptr; /* Data pointers for Region PointSet */ + double *bc; /* Base Frame centre position */ + double *circum; /* Base frame circumference position */ + double *result; /* Returned pointer */ + double *tmp; /* Temporary array pointer */ + double axval; /* Axis value */ + int ic; /* Coordinate index */ + int ncb; /* Number of base frame coordinate values per point */ + int ncc; /* Number of current frame coordinate values per point */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Circle structure. */ + this = (AstCircle *) this_region; + +/* Get the number of axis values per point in the base and current Frames. */ + ncb = astGetNin( this_region->frameset ); + ncc = astGetNout( this_region->frameset ); + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* If the centre coords are to be returned, return either a copy of the + base Frame centre coords, or transform the base Frame centre coords + into the current Frame. */ + if( !ptr && !cen ) { + if( ifrm == AST__CURRENT ) { + result = astRegTranPoint( this_region, this->centre, 1, 1 ); + } else { + result = astStore( NULL, this->centre, sizeof( double )*ncb ); + } + +/* Otherwise, we store the supplied new centre coords and return a NULL + pointer. */ + } else { + +/* Get a pointer to the base Frame in the Region's FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Get a pointer to the axis values stored in the Region structure. */ + rptr = astGetPoints( this_region->points ); + +/* Check pointers can be used safely */ + if( astOK ) { + +/* If the centre position was supplied in the current Frame, find the + corresponding base Frame position... */ + if( ifrm == AST__CURRENT ) { + if( cen ) { + bc = astRegTranPoint( this_region, cen, 1, 0 ); + } else { + tmp = astMalloc( sizeof( double)*(size_t)ncc ); + if( astOK ) { + for( ic = 0; ic < ncc; ic++ ) tmp[ ic ] = ptr[ ic ][ index ]; + } + bc = astRegTranPoint( this_region, tmp, 1, 0 ); + tmp = astFree( tmp ); + } + +/* Replace any bad centre values with their current values. */ + for( ic = 0; ic < ncb; ic++ ) { + if( bc[ ic ] == AST__BAD ) bc[ ic ] = this->centre[ ic ]; + } + +/* ... and change the coords in the parent Region structure and the cached + coords in the Circle structure. */ + circum = CircumPoint( frm, ncb, bc, this->radius, status ); + if( circum ) { + for( ic = 0; ic < ncb; ic++ ) { + rptr[ ic ][ 0 ] = bc[ ic ]; + rptr[ ic ][ 1 ] = circum[ ic ]; + this->centre[ ic ] = bc[ ic ]; + } + } + +/* Free resources */ + circum = astFree( circum ); + bc = astFree( bc ); + +/* If the centre position was supplied in the base Frame, use the + supplied "cen" or "ptr" pointer directly to change the coords in the + parent Region structure and the cached coords in the Circle structure. */ + } else { + for( ic = 0; ic < ncb; ic++ ) { + axval = cen ? cen[ ic ] : ptr[ ic ][ index ]; + if( axval != AST__BAD ) this->centre[ ic ] = axval; + } + + circum = CircumPoint( frm, ncb, this->centre, this->radius, + status ); + if( circum ) { + for( ic = 0; ic < ncb; ic++ ) { + rptr[ ic ][ 0 ] = this->centre[ ic ]; + rptr[ ic ][ 1 ] = circum[ ic ]; + } + circum = astFree( circum ); + } + } + } + +/* Free resources */ + frm = astAnnul( frm ); + } + +/* Return the result. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Circle. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ){ + +* Class Membership: +* Circle member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Circle. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Circle "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Circle. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstCircle *large_circle; /* Circle slightly larger than "this" */ + AstCircle *small_circle; /* Circle slightly smaller than "this" */ + AstCircle *this; /* Pointer to the Circle structure. */ + AstFrame *frm; /* Base Frame in supplied Circle */ + AstPointSet *ps1; /* Points masked by larger Circle */ + AstPointSet *ps2; /* Points masked by larger and smaller Circlees */ + AstRegion *tunc; /* Uncertainity Region from "this" */ + double **ptr; /* Pointer to axis values in "ps2" */ + double *lbnd_tunc; /* Lower bounds of "this" uncertainty Region */ + double *lbnd_unc; /* Lower bounds of supplied uncertainty Region */ + double *p; /* Pointer to next axis value */ + double *safe; /* An interior point in "this" */ + double *ubnd_tunc; /* Upper bounds of "this" uncertainty Region */ + double *ubnd_unc; /* Upper bounds of supplied uncertainty Region */ + double drad; /* Radius increment corresponding to border width */ + double l1; /* Length of bounding box diagonal */ + double l2; /* Length of bounding box diagonal */ + double rad; /* Radius of Circle */ + int i; /* Axis index */ + int j; /* Point index */ + int nc; /* No. of axes in Circle base frame */ + int np; /* No. of supplied points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the Circle structure. */ + this = (AstCircle *) this_region; + +/* Get the number of base Frame axes in the Circle, and check the supplied + PointSet has the same number of axis values per point. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + nc = astGetNaxes( frm ); + if( astGetNcoord( pset ) != nc && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axis " + "values per point (%d) in the supplied PointSet - should be " + "%d (internal AST programming error).", status, astGetClass( this ), + astGetNcoord( pset ), nc ); + } + +/* Get the number of axes in the uncertainty Region and check it is the + same as above. */ + if( unc && astGetNaxes( unc ) != nc && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axes (%d) " + "in the supplied uncertainty Region - should be " + "%d (internal AST programming error).", status, astGetClass( this ), + astGetNaxes( unc ), nc ); + } + +/* Get the centre of the region in the base Frame. We use this as a "safe" + interior point within the region. */ + safe = astRegCentre( this, NULL, NULL, 0, AST__BASE ); + +/* We now find the maximum distance on each axis that a point can be from the + boundary of the Circle for it still to be considered to be on the boundary. + First get the Region which defines the uncertainty within the Circle being + checked (in its base Frame), re-centre it on the interior point found + above (to avoid problems if the uncertainty region straddles a + discontinuity), and get its bounding box. */ + tunc = astGetUncFrm( this, AST__BASE ); + if( safe ) astRegCentre( tunc, safe, NULL, 0, AST__CURRENT ); + lbnd_tunc = astMalloc( sizeof( double )*(size_t) nc ); + ubnd_tunc = astMalloc( sizeof( double )*(size_t) nc ); + astGetRegionBounds( tunc, lbnd_tunc, ubnd_tunc ); + +/* Find the geodesic length within the base Frame of "this" of the diagonal of + the bounding box. */ + l1 = astDistance( frm, lbnd_tunc, ubnd_tunc ); + +/* Also get the Region which defines the uncertainty of the supplied + points and get its bounding box. First re-centre the uncertainty at the + interior position to avoid problems from uncertainties that straddle a + discontinuity. */ + if( unc ) { + if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT ); + lbnd_unc = astMalloc( sizeof( double )*(size_t) nc ); + ubnd_unc = astMalloc( sizeof( double )*(size_t) nc ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + +/* Find the geodesic length of the diagonal of this bounding box. */ + l2 = astDistance( frm, lbnd_unc, ubnd_unc ); + +/* Use a zero sized box "unc" if no box was supplied. */ + } else { + lbnd_unc = NULL; + ubnd_unc = NULL; + l2 = 0.0; + } + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* The required border width is half of the total diagonal of the two bounding + boxes. */ + if( astOK ) { + drad = 0.5*( l1 + l2 ); + +/* Create two new Circle, one of which is larger than "this" by the amount + found above, and the other of which is smaller than "this" by the amount + found above. */ + rad = this->radius + 0.5*drad; + large_circle = astCircle( frm, 1, this->centre, &rad, NULL, "", status ); + rad = this->radius - 0.5*drad; + small_circle = astCircle( frm, 1, this->centre, &rad, NULL, "", status ); + +/* Negate the smaller region.*/ + astNegate( small_circle ); + +/* Points are on the boundary of "this" if they are inside both the large + Circle and the negated small Circle. First transform the supplied PointSet + using the large Circle, then transform them using the negated smaller + Circle. */ + ps1 = astTransform( large_circle, pset, 1, NULL ); + ps2 = astTransform( small_circle, ps1, 1, NULL ); + +/* Get a point to the resulting axis values, and the number of axis + values per axis. */ + ptr = astGetPoints( ps2 ); + np = astGetNpoint( ps2 ); + +/* If a mask array is to be returned, create one. */ + if( mask ) { + *mask = astMalloc( sizeof(int)*(size_t) np ); + +/* Check all the resulting points, setting mask values for all of them. */ + if( astOK ) { + +/* Initialise the mask elements on the basis of the first axis values */ + result = 1; + p = ptr[ 0 ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } else { + (*mask)[ j ] = 1; + } + } + +/* Now check for bad values on other axes. */ + for( i = 1; i < nc; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } + } + } + } + +/* If no output mask is to be made, we can break out of the check as soon + as the first bad value is found. */ + } else if( astOK ) { + result = 1; + for( i = 0; i < nc && result; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + break; + } + } + } + } + +/* Free resources. */ + large_circle = astAnnul( large_circle ); + small_circle = astAnnul( small_circle ); + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + } + + tunc = astAnnul( tunc ); + frm = astAnnul( frm ); + lbnd_tunc = astFree( lbnd_tunc ); + ubnd_tunc = astFree( ubnd_tunc ); + if( unc ) lbnd_unc = astFree( lbnd_unc ); + if( unc ) ubnd_unc = astFree( ubnd_unc ); + safe = astFree( safe ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr, + int *status ){ +/* +*+ +* Name: +* RegTrace + +* Purpose: +* Return requested positions on the boundary of a 2D Region. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* int astTraceRegion( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* Circle member function (overrides the astTraceRegion method +* inherited from the parent Region class). + +* Description: +* This function returns positions on the boundary of the supplied +* Region, if possible. The required positions are indicated by a +* supplied list of scalar parameter values in the range zero to one. +* Zero corresponds to some arbitrary starting point on the boundary, +* and one corresponds to the end (which for a closed region will be +* the same place as the start). + +* Parameters: +* this +* Pointer to the Region. +* n +* The number of positions to return. If this is zero, the function +* returns without action (but the returned function value still +* indicates if the method is supported or not). +* dist +* Pointer to an array of "n" scalar parameter values in the range +* 0 to 1.0. +* ptr +* A pointer to an array of pointers. The number of elements in +* this array should equal tthe number of axes in the Frame spanned +* by the Region. Each element of the array should be a pointer to +* an array of "n" doubles, in which to return the "n" values for +* the corresponding axis. The contents of the arrays are unchanged +* if the supplied Region belongs to a class that does not +* implement this method. + +* Returned Value: +* Non-zero if the astTraceRegion method is implemented by the class +* of Region supplied, and zero if not. + +*- +*/ + +/* Local Variables; */ + AstCircle *this; + AstFrame *frm; + AstMapping *map; + AstPointSet *bpset; + AstPointSet *cpset; + double **bptr; + double angle; + double p[ 2 ]; + int i; + int ncur; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( ! astOK ) return result; + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Check it is 2-dimensional. */ + if( astGetNaxes( frm ) == 2 ) result = 1; + +/* Check we have some points to find. */ + if( result && n > 0 ) { + +/* Get a pointer to the Circle structure. */ + this = (AstCircle *) this_region; + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* We first determine the required positions in the base Frame of the + Region, and then transform them into the current Frame. Get the + base->current Mapping, and the number of current Frame axes. */ + map = astGetMapping( this_region->frameset, AST__BASE, AST__CURRENT ); + +/* If it's a UnitMap we do not need to do the transformation, so put the + base Frame positions directly into the supplied arrays. */ + if( astIsAUnitMap( map ) ) { + bpset = NULL; + bptr = ptr; + ncur = 2; + +/* Otherwise, create a PointSet to hold the base Frame positions. */ + } else { + bpset = astPointSet( n, 2, " ", status ); + bptr = astGetPoints( bpset ); + ncur = astGetNout( map ); + } + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Loop round each point. Get the angle around the circle, and offset + along that angle to find the point that is one radius away from the + centre. Copy the results into the required arrays. */ + for( i = 0; i < n; i++ ) { + angle = dist[ i ]*2*AST__DPI; + astOffset2( frm, this->centre, angle, this->radius, p ); + bptr[ 0 ][ i ] = p[ 0 ]; + bptr[ 1 ][ i ] = p[ 1 ]; + } + + } + +/* If required, transform the base frame positions into the current + Frame, storing them in the supplied array. Then free resources. */ + if( bpset ) { + cpset = astPointSet( n, ncur, " ", status ); + astSetPoints( cpset, ptr ); + + (void) astTransform( map, bpset, 1, cpset ); + + cpset = astAnnul( cpset ); + bpset = astAnnul( bpset ); + } + +/* Free remaining resources. */ + map = astAnnul( map ); + } + frm = astAnnul( frm ); + +/* Return the result. */ + return result; +} + +static void ResetCache( AstRegion *this, int *status ){ +/* +* Name: +* ResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* void ResetCache( AstRegion *this, int *status ) + +* Class Membership: +* Region member function (overrides the astResetCache method +* inherited from the parent Region class). + +* Description: +* This function clears cached information from the supplied Region +* structure. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + if( this ) { + ( (AstCircle *) this )->stale = 1; + (*parent_resetcache)( this, status ); + } +} + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* Circle method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* Re-calculate cached information. */ + astResetCache( this_region ); +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Circle method (over-rides the astSimplify method inherited +* from the Region class). + +* Description: +* This function invokes the parent Region Simplify method, and then +* performs any further region-specific simplification. +* +* If the Mapping from base to current Frame is not a UnitMap, this +* will include attempting to fit a new Region to the boundary defined +* in the current Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstMapping *map; /* Base -> current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstPointSet *mesh; /* Mesh of current Frame positions */ + AstPointSet *ps2; /* Circle PointSet in current Frame */ + AstRegion *new; /* Pointer to simplified Region */ + AstRegion *newreg; /* Equivalent circle or ellipse */ + AstRegion *this; /* Pointer to supplied Region structure */ + AstRegion *unc; /* Pointer to uncertainty Region */ + double **ptr2; /* Pointer axis values in "ps2" */ + double *cen; /* Pointer to array holding new centre coords */ + int ic; /* Axis index */ + int nc; /* No. of axis values per point */ + int ok; /* Was the new centre found OK? */ + int simpler; /* Has some simplication taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the supplied Region structure. */ + this = (AstRegion *) this_mapping; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. */ + new = (AstRegion *) (*parent_simplify)( this_mapping, status ); + +/* Note if any simplification took place. This is assumed to be the case + if the pointer returned by the above call is different to the supplied + pointer. */ + simpler = ( new != this ); + +/* If the Mapping from base to current Frame is not a UnitMap, we attempt + to simplify the Circle by re-defining it within its current Frame. + Transforming the Circle from its base to its current Frame may result in + the region no longer being a circle. We test this by transforming a set of + bounds on the Circle boundary. */ + map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT ); + if( !astIsAUnitMap( map ) ){ + +/* Get a mesh of points covering the Circle in its current Frame. */ + mesh = astRegMesh( new ); + +/* Get the Region describing the positional uncertainty within the Circle in + its current Frame. */ + unc = astGetUncFrm( new, AST__CURRENT ); + +/* Transform the PointSet holding the circle centre into the current + Frame, and copy the axis values into a new array. */ + ps2 = astRegTransform( this, this->points, 1, NULL, NULL ); + nc = astGetNcoord( ps2 ); + cen = astMalloc( sizeof( double )*(size_t) nc ); + ptr2 = astGetPoints( ps2 ); + if( astOK ) { + + ok = 1; + for( ic = 0; ic < nc; ic++ ) { + cen[ ic ] = ptr2[ ic ][ 0 ]; + if( cen[ ic ] == AST__BAD ) ok = 0; + } + +/* Find the best fitting Circle (defined in the current Frame) through these + points */ + newreg = ok ? astBestCircle( mesh, cen, unc ) : NULL; + +/* See if all points within this mesh fall on the boundary of the best + fitting Circle, to within the uncertainty of the Region. */ + if( newreg && astRegPins( newreg, mesh, NULL, NULL ) ) { + +/* If so, use the new Circle in place of the original. */ + (void) astAnnul( new ); + new = astClone( newreg ); + +/* Otherwise, if the region is 2-d we see if an Ellipse can represent the + mesh. */ + } else if( ok && nc == 2 ){ + +/* Find the best fitting Ellipse (defined in the current Frame) through these + points */ + if( newreg ) (void) astAnnul( newreg ); + newreg = astBestEllipse( mesh, cen, unc ); + +/* See if all points within this mesh fall on the boundary of the best + fitting Ellipse, to within the uncertainty of the Region. */ + if( newreg && astRegPins( newreg, mesh, NULL, NULL ) ) { + +/* If so, use the new Ellipse in place of the original. */ + (void) astAnnul( new ); + new = astClone( newreg ); + simpler = 1; + } + } + +/* Free resources. */ + if( newreg ) newreg = astAnnul( newreg ); + } + + ps2 = astAnnul( ps2 ); + cen = astFree( cen ); + mesh = astAnnul( mesh ); + unc = astAnnul( unc ); + } + map = astAnnul( map ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. + If the supplied Region had no uncertainty, ensure the returned Region + has no uncertainty. Otherwise, return a clone of the supplied pointer. */ + if( simpler ){ + astRegOverlay( new, this, 1 ); + result = (AstMapping *) new; + + } else { + new = astAnnul( new ); + result = astClone( this ); + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a Circle to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Circle member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a Circle and a set of points encapsulated in a +* PointSet and transforms the points by setting axis values to +* AST__BAD for all points which are outside the region. Points inside +* the region are copied unchanged from input to output. + +* Parameters: +* this +* Pointer to the Circle. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The forward and inverse transformations are identical for a +* Region. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of axes in the Frame represented by the Circle. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstCircle *this; /* Pointer to Circle */ + AstFrame *frm; /* Pointer to base Frame in FrameSet */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_out; /* Pointer to output coordinate data */ + double **ptr_tmp; /* Pointer to base Frame coordinate data */ + double *work; /* Pointer to array holding single base point */ + double d; /* Base-Frame distance from centre to point */ + int closed; /* Is the boundary part of the Region? */ + int coord; /* Zero-based index for coordinates */ + int inside; /* Is the point inside the Region? */ + int ncoord_out; /* No. of coordinates per output point */ + int ncoord_tmp; /* No. of coordinates per base Frame point */ + int neg; /* Has the Region been negated? */ + int npoint; /* No. of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the Circle structure. */ + this = (AstCircle *) this_mapping; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, + containing a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet to transform the supplied positions + from the current Frame in the encapsulated FrameSet (the Frame + represented by the Region), to the base Frame (the Frame in which the + Region is defined). This call also returns a pointer to the base Frame + of the encapsulated FrameSet. Note, the returned pointer may be a + clone of the "in" pointer, and so we must be carefull not to modify the + contents of the returned PointSet. */ + pset_tmp = astRegTransform( this, in, 0, NULL, &frm ); + +/* Determine the numbers of points and coordinates per point from the base + Frame PointSet and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( pset_tmp ); + ncoord_tmp = astGetNcoord( pset_tmp ); + ptr_tmp = astGetPoints( pset_tmp ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* Get work space for one base Frame position */ + work = astMalloc( sizeof( double )*(size_t) ncoord_tmp ); + +/* See if the boundary is part of the Region. */ + closed = astGetClosed( this ); + +/* See if the Region has been negated. */ + neg = astGetNegated( this ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* Loop round each point */ + for ( point = 0; point < npoint; point++ ) { + +/* Copy the base Frame position into a work array. */ + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + work[ coord ] = ptr_tmp[ coord ][ point ]; + } + +/* Find the geodesic distance from the centre of the Circle in the base + Frame. */ + d = astDistance( frm, this->centre, work ); + +/* Now consider whether this radius value puts the point in or out of the + Circle. */ + if( d != AST__BAD ){ + if( neg ) { + if( closed ) { + inside = ( d >= this->radius ); + } else { + inside = ( d > this->radius ); + } + } else { + if( closed ) { + inside = ( d <= this->radius ); + } else { + inside = ( d < this->radius ); + } + } + } else { + inside = 0; + } + +/* If the point is outside, store bad output values. */ + if( !inside ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + +/* Free resources */ + work = astFree( work ); + pset_tmp = astAnnul( pset_tmp ); + frm = astAnnul( frm ); + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Region objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Region objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstCircle *in; /* Pointer to input Circle */ + AstCircle *out; /* Pointer to output Circle */ + int nax; /* Number of base Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Circles. */ + in = (AstCircle *) objin; + out = (AstCircle *) objout; + +/* For safety, first clear any references to the input memory from + the output Circle. */ + out->centre = NULL; + out->lb = NULL; + out->ub = NULL; + +/* Copy dynamic memory contents */ + nax = astGetNin( ((AstRegion *) in)->frameset ); + out->centre = astStore( NULL, in->centre, + sizeof( double )*(size_t)nax ); + out->lb = astStore( NULL, in->lb, sizeof( double )*(size_t)nax ); + out->ub = astStore( NULL, in->ub, sizeof( double )*(size_t)nax ); +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Circle objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Circle objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstCircle *this; /* Pointer to Circle */ + +/* Obtain a pointer to the Circle structure. */ + this = (AstCircle *) obj; + +/* Annul all resources. */ + this->centre = astFree( this->centre ); + this->lb = astFree( this->lb ); + this->ub = astFree( this->ub ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Circle objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Circle class to an output Channel. + +* Parameters: +* this +* Pointer to the Circle whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstCircle *this; /* Pointer to the Circle structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Circle structure. */ + this = (AstCircle *) this_object; + +/* Write out values representing the instance variables for the + Circle class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsACircle and astCheckCircle functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Circle,Region) +astMAKE_CHECK(Circle) + +AstCircle *astCircle_( void *frame_void, int form, const double centre[], + const double point[], AstRegion *unc, + const char *options, int *status, ...) { +/* +*++ +* Name: +c astCircle +f AST_CIRCLE + +* Purpose: +* Create a Circle. + +* Type: +* Public function. + +* Synopsis: +c #include "circle.h" +c AstCircle *astCircle( AstFrame *frame, int form, const double centre[], +c const double point[], AstRegion *unc, +c const char *options, ... ) +f RESULT = AST_CIRCLE( FRAME, FORM, CENTRE, POINT, UNC, OPTIONS, STATUS ) + +* Class Membership: +* Circle constructor. + +* Description: +* This function creates a new Circle and optionally initialises its +* attributes. +* +* A Circle is a Region which represents a circle or sphere within the +* supplied Frame. + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame in which the region is defined. A deep +* copy is taken of the supplied Frame. This means that any +* subsequent changes made to the Frame using the supplied pointer +* will have no effect the Region. +c form +f FORM = INTEGER (Given) +* Indicates how the circle is described by the remaining parameters. +* A value of zero indicates that the circle is specified by a +* centre position and a position on the circumference. A value of one +* indicates that the circle is specified by a centre position and a +* scalar radius. +c centre +f CENTRE( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates at the centre of +* the circle or sphere. +c point +f POINT( * ) = DOUBLE PRECISION (Given) +c If "form" +f If FORM +* is zero, then this array should have one element for each Frame +* axis (Naxes attribute), and should be supplied holding the +* coordinates at a point on the circumference of the circle or sphere. +c If "form" +f If FORM +* is one, then this array should have one element only which should +* be supplied holding the scalar radius of the circle or sphere, +* as a geodesic distance within the Frame. +c unc +f UNC = INTEGER (Given) +* An optional pointer to an existing Region which specifies the +* uncertainties associated with the boundary of the Circle being created. +* The uncertainty in any point on the boundary of the Circle is found by +* shifting the supplied "uncertainty" Region so that it is centred at +* the boundary point being considered. The area covered by the +* shifted uncertainty Region then represents the uncertainty in the +* boundary position. The uncertainty is assumed to be the same for +* all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created Circle. Alternatively, +f a null Object pointer (AST__NULL) +c a NULL Object pointer +* may be supplied, in which case a default uncertainty is used +* equivalent to a box 1.0E-6 of the size of the Circle being created. +* +* The uncertainty Region has two uses: 1) when the +c astOverlap +f AST_OVERLAP +* function compares two Regions for equality the uncertainty +* Region is used to determine the tolerance on the comparison, and 2) +* when a Region is mapped into a different coordinate system and +* subsequently simplified (using +c astSimplify), +f AST_SIMPLIFY), +* the uncertainties are used to determine if the transformed boundary +* can be accurately represented by a specific shape of Region. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Circle. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Circle. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astCircle() +f AST_CIRCLE = INTEGER +* A pointer to the new Circle. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstCircle *new; /* Pointer to new Circle */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the supplied Frame structure. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the Circle, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCircle( NULL, sizeof( AstCircle ), !class_init, &class_vtab, + "Circle", frame, form, centre, point, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Circle's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Circle. */ + return new; +} + +AstCircle *astCircleId_( void *frame_void, int form, const double centre[], + const double point[], void *unc_void, + const char *options, ... ) { +/* +* Name: +* astCircleId_ + +* Purpose: +* Create a Circle. + +* Type: +* Private function. + +* Synopsis: +* #include "circle.h" +* AstCircle *astCircleId_( AstFrame *frame, int form, const double centre[], +* const double point[], AstRegion *unc, +* const char *options, ... ) + +* Class Membership: +* Circle constructor. + +* Description: +* This function implements the external (public) interface to the +* astCircle constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astCircle_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astCircle_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astCircle_. + +* Returned Value: +* The ID value associated with the new Circle. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstCircle *new; /* Pointer to new Circle */ + AstRegion *unc; /* Pointer to Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Frame pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Obtain a Region pointer from the supplied "unc" ID and validate the + pointer to ensure it identifies a valid Region . */ + unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL; + +/* Initialise the Circle, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCircle( NULL, sizeof( AstCircle ), !class_init, &class_vtab, + "Circle", frame, form, centre, point, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Circle's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Circle. */ + return astMakeId( new ); +} + +AstCircle *astInitCircle_( void *mem, size_t size, int init, AstCircleVtab *vtab, + const char *name, AstFrame *frame, int form, + const double centre[], const double point[], + AstRegion *unc, int *status ) { +/* +*+ +* Name: +* astInitCircle + +* Purpose: +* Initialise a Circle. + +* Type: +* Protected function. + +* Synopsis: +* #include "circle.h" +* AstCircle *astInitCircle_( void *mem, size_t size, int init, AstCircleVtab *vtab, +* const char *name, AstFrame *frame, int form, +* const double centre[], const double point[], +* AstRegion *unc ) + +* Class Membership: +* Circle initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Circle object. It allocates memory (if necessary) to accommodate +* the Circle plus any additional data associated with the derived class. +* It then initialises a Circle structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Circle at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Circle is to be initialised. +* This must be of sufficient size to accommodate the Circle data +* (sizeof(Circle)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Circle (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Circle +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Circle's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Circle. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame in which the region is defined. +* form +* Indicates how the "point" parameter should be interpreted. +* Should be either 0 or 1. +* centre +* An array of double, with one element for each Frame axis (Naxes +* attribute) containing the coordinates of the circle centre. +* point +* If "form" is zero, this should be an array of double, with one +* element for each Frame axis (Naxes attribute) containing the +* coordinates at any point on the circumference. If "form" is one, +* this should be the address of a double containing the scalar +* radius of the circle or sphere. +* unc +* A pointer to a Region which specifies the uncertainty in the +* supplied positions (all points on the boundary of the new Circle +* being initialised are assumed to have the same uncertainty). A NULL +* pointer can be supplied, in which case default uncertainties equal to +* 1.0E-6 of the dimensions of the new Circle's bounding box are used. +* If an uncertainty Region is supplied, it must be either a Box, a +* Circle or an Ellipse, and its encapsulated Frame must be related +* to the Frame supplied for parameter "frame" (i.e. astConvert +* should be able to find a Mapping between them). Two positions +* the "frame" Frame are considered to be co-incident if their +* uncertainty Regions overlap. The centre of the supplied +* uncertainty Region is immaterial since it will be re-centred on the +* point being tested before use. A deep copy is taken of the supplied +* Region. + +* Returned Value: +* A pointer to the new Circle. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstCircle *new; /* Pointer to new Circle */ + AstPointSet *pset; /* PointSet to pass to Region initialiser */ + const double *circum; /* Pointer to circumference position */ + double **ptr; /* Pointer to coords data in pset */ + int i; /* Axis index */ + int nc; /* No. of axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitCircleVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the supplied value for "form" is legal. */ + if( form != 0 && form != 1 && astOK ) { + astError( AST__BADIN, "astInitCircle(%s): The value supplied for " + "parameter \"form\" (%d) is illegal - it should be 0 or 1 " + "(programming error).", status, name, form ); + } + +/* Get the number of axis values required for each position. */ + nc = astGetNaxes( frame ); + +/* If the circle radius has been supplied, find a point on the circle + circumference. */ + if( form == 1 ) { + circum = CircumPoint( frame, nc, centre, *point, status ); + +/* Otherwise, use the supplied circumference point. */ + } else { + circum = point; + } + +/* Create a PointSet to hold the centre position, and a point on the + circumference, and get points to the data arrays. */ + pset = astPointSet( 2, nc, "", status ); + ptr = astGetPoints( pset ); + +/* Copy the centre and circumference coordinates into the PointSet, checking + that no bad values have been supplied. */ + for( i = 0; astOK && i < nc; i++ ) { + if( centre[ i ] == AST__BAD ) { + astError( AST__BADIN, "astInitCircle(%s): The value of axis %d is " + "undefined at the circle centre.", status, name, i + 1 ); + } + if( astOK && circum[ i ] == AST__BAD ) { + astError( AST__BADIN, "astInitCircle(%s): The value of axis %d is " + "undefined on the circumference of the circle.", status, name, i + 1 ); + } + ptr[ i ][ 0 ] = centre[ i ]; + ptr[ i ][ 1 ] = circum[ i ]; + } + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Initialise a Region structure (the parent class) as the first component + within the Circle structure, allocating memory if necessary. */ + new = (AstCircle *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frame, pset, unc ); + + if ( astOK ) { + +/* Initialise the Circle data. */ +/* ------------------------ */ + new->stale = 1; + new->centre = NULL; + new->lb = NULL; + new->ub = NULL; + Cache( new, status ); + +/* If an error occurred, clean up by deleting the new Circle. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free resources. */ + pset = astAnnul( pset ); + if( form == 1 ) circum = astFree( (void *) circum ); + +/* Return a pointer to the new Circle. */ + return new; +} + +AstCircle *astLoadCircle_( void *mem, size_t size, AstCircleVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadCircle + +* Purpose: +* Load a Circle. + +* Type: +* Protected function. + +* Synopsis: +* #include "circle.h" +* AstCircle *astLoadCircle( void *mem, size_t size, AstCircleVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Circle loader. + +* Description: +* This function is provided to load a new Circle using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Circle structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Circle at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Circle is to be +* loaded. This must be of sufficient size to accommodate the +* Circle data (sizeof(Circle)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Circle (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Circle structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstCircle) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Circle. If this is NULL, a pointer +* to the (static) virtual function table for the Circle class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Circle" is used instead. + +* Returned Value: +* A pointer to the new Circle. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCircle *new; /* Pointer to the new Circle */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Circle. In this case the + Circle belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstCircle ); + vtab = &class_vtab; + name = "Circle"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitCircleVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Circle. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Circle" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* Cache intermediate results in the Circle structure */ + new->centre = NULL; + new->lb = NULL; + new->ub = NULL; + new->stale = 1; + Cache( new, status ); + +/* If an error occurred, clean up by deleting the new Circle. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Circle pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + +void astCirclePars_( AstCircle *this, double *centre, double *radius, + double *p1, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Circle,CirclePars))( this, centre, radius, + p1, status ); +} + + + + + + diff --git a/circle.h b/circle.h new file mode 100644 index 0000000..a69f8ea --- /dev/null +++ b/circle.h @@ -0,0 +1,241 @@ +#if !defined( CIRCLE_INCLUDED ) /* Include this file only once */ +#define CIRCLE_INCLUDED +/* +*+ +* Name: +* circle.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Circle class. + +* Invocation: +* #include "circle.h" + +* Description: +* This include file defines the interface to the Circle class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Circle class implement a Region which represents a simple interval +* on each axis of the encapsulated Frame + +* Inheritance: +* The Circle class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 31-AUG-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* Circle structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstCircle { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *centre; /* Circle centre coords */ + double radius; /* Circle radius */ + double *lb; /* Lower limits of mesh bounding box */ + double *ub; /* Upper limit of mesh bounding box */ + int stale; /* Is Cached information stale? */ + +} AstCircle; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstCircleVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* CirclePars)( AstCircle *, double *, double *, double *, int * ); +} AstCircleVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstCircleGlobals { + AstCircleVtab Class_Vtab; + int Class_Init; +} AstCircleGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitCircleGlobals_( AstCircleGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Circle) /* Check class membership */ +astPROTO_ISA(Circle) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstCircle *astCircle_( void *, int, const double[], const double[], AstRegion *, const char *, int *, ...); +#else +AstCircle *astCircleId_( void *, int, const double[], const double[], + AstRegion *, const char *, ... )__attribute__((format(printf,6,7))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstCircle *astInitCircle_( void *, size_t, int, AstCircleVtab *, + const char *, AstFrame *, int, const double[], + const double[], AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitCircleVtab_( AstCircleVtab *, const char *, int * ); + +/* Loader. */ +AstCircle *astLoadCircle_( void *, size_t, AstCircleVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astCirclePars_( AstCircle *, double *, double *, double *, int * ); + +# if defined(astCLASS) /* Protected */ +AstRegion *astBestCircle_( AstPointSet *, double *, AstRegion *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckCircle(this) astINVOKE_CHECK(Circle,this,0) +#define astVerifyCircle(this) astINVOKE_CHECK(Circle,this,1) + +/* Test class membership. */ +#define astIsACircle(this) astINVOKE_ISA(Circle,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astCircle astINVOKE(F,astCircle_) +#else +#define astCircle astINVOKE(F,astCircleId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitCircle(mem,size,init,vtab,name,frame,form,p1,p2,unc) \ +astINVOKE(O,astInitCircle_(mem,size,init,vtab,name,frame,form,p1,p2,unc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitCircleVtab(vtab,name) astINVOKE(V,astInitCircleVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadCircle(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadCircle_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckCircle to validate Circle pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astCirclePars(this,centre,radius,p1) \ +astINVOKE(V,astCirclePars_(astCheckCircle(this),centre,radius,p1,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astBestCircle(pset,cen,unc) astBestCircle_(pset,cen,unc,STATUS_PTR) +#endif +#endif + + + + + diff --git a/cminpack/CopyrightMINPACK.txt b/cminpack/CopyrightMINPACK.txt new file mode 100644 index 0000000..ae7984d --- /dev/null +++ b/cminpack/CopyrightMINPACK.txt @@ -0,0 +1,52 @@ +Minpack Copyright Notice (1999) University of Chicago. All rights reserved + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of source code must retain the above +copyright notice, this list of conditions and the following +disclaimer. + +2. Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials +provided with the distribution. + +3. The end-user documentation included with the +redistribution, if any, must include the following +acknowledgment: + + "This product includes software developed by the + University of Chicago, as Operator of Argonne National + Laboratory. + +Alternately, this acknowledgment may appear in the software +itself, if and wherever such third-party acknowledgments +normally appear. + +4. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" +WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDER, THE +UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND +THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE +OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY +OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR +USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF +THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) +DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION +UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL +BE CORRECTED. + +5. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT +HOLDER, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF +ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, +INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF +ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF +PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER +SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT +(INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, +EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE +POSSIBILITY OF SUCH LOSS OR DAMAGES. + diff --git a/cminpack/README.md b/cminpack/README.md new file mode 100644 index 0000000..66bbc6f --- /dev/null +++ b/cminpack/README.md @@ -0,0 +1,128 @@ +C/C++ Minpack [![Build Status](https://api.travis-ci.org/devernay/cminpack.png?branch=master)](https://travis-ci.org/devernay/cminpack) +========== + +This is a C version of the minpack minimization package. +It has been derived from the fortran code using f2c and +some limited manual editing. Note that you need to link +against libf2c to use this version of minpack. Extern "C" +linkage permits the package routines to be called from C++. +Check ftp://netlib.bell-labs.com/netlib/f2c for the latest +f2c version. For general minpack info and test programs, see +the accompanying readme.txt and http://www.netlib.org/minpack/. + +Type `make` to compile and `make install` to install in /usr/local +or modify the makefile to suit your needs. + +This software has been tested on a RedHat 7.3 Linux machine - +usual 'use at your own risk' warnings apply. + +Manolis Lourakis -- lourakis at ics forth gr, July 2002 + Institute of Computer Science, + Foundation for Research and Technology - Hellas + Heraklion, Crete, Greece + +Repackaging by Frederic Devernay -- frederic dot devernay at m4x dot org + +The project home page is at http://devernay.free.fr/hacks/cminpack/ + +History +------ + +* version 1.3.3 (04/02/2014):: + - Add documentation and examples abouts how to add box constraints to the variables. + - continuous integration https://travis-ci.org/devernay/cminpack + +* version 1.3.2 (27/10/2013): + - Minor change in the CMake build: also set SOVERSION. + +* version 1.3.1 (02/10/2013): + - Fix CUDA examples compilation, and remove non-free files. + +* version 1.3.0 (09/06/2012): + - Optionally use LAPACK and CBLAS in lmpar, qrfac, and qrsolv. Added + "make lapack" to build the LAPACK-based cminpack and "make + checklapack" to test it (results of the test may depend on the + underlying LAPACK and BLAS implementations). + On 64-bits architectures, the preprocessor symbol __LP64__ must be + defined (see cminpackP.h) if the LAPACK library uses the LP64 + interface (i.e. 32-bits integer, vhereas the ILP interface uses 64 + bits integers). + +* version 1.2.2 (16/05/2012): + - Update Makefiles and documentation (see "Using CMinpack" above) for + easier building and testing. + +* version 1.2.1 (15/05/2012): + - The library can now be built as double, float or half + versions. Standard tests in the "examples" directory can now be + lauched using "make check" (to run common tests, including against + the float version), "make checkhalf" (to test the half version) and + "make checkfail" (to run all the tests, even those that fail). + +* version 1.2.0 (14/05/2012): +- Added original FORTRAN sources for better testing (type "make" in + directory fortran, then "make" in examples and follow the + instructions). Added driver tests lmsdrv, chkdrv, hyjdrv, + hybdrv. Typing "make alltest" in the examples directory will run all + possible test combinations (make sure you have gfortran installed). + +* version 1.1.5 (04/05/2012): + - cminpack now works in CUDA, thanks to Jordi Bataller Mascarell, type + "make" in the "cuda" subdir (be careful, though: this is a + straightforward port from C, and each problem is solved using a + single thread). cminpack can now also be compiled with + single-precision floating point computation (define + __cminpack_real__ to float when compiling and using the + library). Fix cmake support for CMINPACK_LIB_INSTALL_DIR. Update the + reference files for tests. + +* version 1.1.4 (30/10/2011): + - Translated all the Levenberg-Marquardt code (lmder, lmdif, lmstr, + lmder1, lmdif1, lmstr1, lmpar, qrfac, qrsolv, fdjac2, chkder) to use + C-style indices. + +* version 1.1.3 (16/03/2011): + - Minor fix: Change non-standard strnstr() to strstr() in + genf77tests.c. + +* version 1.1.2 (07/01/2011): + - Fix Windows DLL building (David Graeff) and document covar in + cminpack.h. + +* version 1.1.1 (04/12/2010): + - Complete rewrite of the C functions (without trailing underscore in + the function name). Using the original FORTRAN code, the original + algorithms structure was recovered, and many goto's were converted + to if...then...else. The code should now be both more readable and + easier to optimize, both for humans and for compilers. Added lmddrv + and lmfdrv test drivers, which test a lot of difficult functions + (these functions are explained in Testing Unconstrained Optimization + Software by Moré et al.). Also added the pkg-config files to the + cmake build, as well as an "uninstall" target, contributed by + Geoffrey Biggs. + +* version 1.0.4 (18/10/2010): + - Support for shared library building using CMake, thanks to Goeffrey + Biggs and Radu Bogdan Rusu from Willow Garage. Shared libraries can be + enabled using cmake options, as in; + cmake -DUSE_FPIC=ON -DSHARED_LIBS=ON -DBUILD_EXAMPLES=OFF path_to_sources + +* version 1.0.3 (18/03/2010): + - Added CMake support. + - XCode build is now Universal. + - Added tfdjac2_ and tfdjac2c examples, which test the accuracy of a + finite-differences approximation of the Jacobian. + - Bug fix in tlmstr1 (signaled by Thomas Capricelli). + +* version 1.0.2 (27/02/2009): + - Added Xcode and Visual Studio project files + +* version 1.0.1 (17/12/2007): + - bug fix in covar() and covar_(), the computation of tolr caused a + segfault (signaled by Timo Hartmann). + +* version 1.0.0 (24/04/2007): + - Added fortran and C examples + - Added documentation from Debian man pages + - Wrote pure C version + - Added covar() and covar_(), and use it in tlmdef/tlmdif diff --git a/cminpack/cminpack.h b/cminpack/cminpack.h new file mode 100644 index 0000000..6d3f757 --- /dev/null +++ b/cminpack/cminpack.h @@ -0,0 +1,370 @@ +/* Header file for cminpack, by Frederic Devernay. + The documentation for all functions can be found in the file + minpack-documentation.txt from the distribution, or in the source + code of each function. */ + +#ifndef __CMINPACK_H__ +#define __CMINPACK_H__ + +/* The default floating-point type is "double" for C/C++ and "float" for CUDA, + but you can change this by defining one of the following symbols when + compiling the library, and before including cminpack.h when using it: + __cminpack_double__ for double + __cminpack_float__ for float + __cminpack_half__ for half from the OpenEXR library (in this case, you must + compile cminpack with a C++ compiler) +*/ +#ifdef __cminpack_double__ +#define __cminpack_real__ double +#endif + +#ifdef __cminpack_float__ +#define __cminpack_real__ float +#endif + +#ifdef __cminpack_half__ +#include +#define __cminpack_real__ half +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Cmake will define cminpack_EXPORTS on Windows when it +configures to build a shared library. If you are going to use +another build system on windows or create the visual studio +projects by hand you need to define cminpack_EXPORTS when +building a DLL on windows. +*/ +#if defined (__GNUC__) +#define CMINPACK_DECLSPEC_EXPORT __declspec(__dllexport__) +#define CMINPACK_DECLSPEC_IMPORT __declspec(__dllimport__) +#endif +#if defined (_MSC_VER) || defined (__BORLANDC__) +#define CMINPACK_DECLSPEC_EXPORT __declspec(dllexport) +#define CMINPACK_DECLSPEC_IMPORT __declspec(dllimport) +#endif +#ifdef __WATCOMC__ +#define CMINPACK_DECLSPEC_EXPORT __export +#define CMINPACK_DECLSPEC_IMPORT __import +#endif +#ifdef __IBMC__ +#define CMINPACK_DECLSPEC_EXPORT _Export +#define CMINPACK_DECLSPEC_IMPORT _Import +#endif + +#if !defined(CMINPACK_NO_DLL) && (defined(__WIN32__) || defined(WIN32) || defined (_WIN32)) +#if defined(cminpack_EXPORTS) || defined(CMINPACK_EXPORTS) || defined(CMINPACK_DLL_EXPORTS) + #define CMINPACK_EXPORT CMINPACK_DECLSPEC_EXPORT + #else + #define CMINPACK_EXPORT CMINPACK_DECLSPEC_IMPORT + #endif /* cminpack_EXPORTS */ +#else /* defined (_WIN32) */ + #define CMINPACK_EXPORT +#endif + +#if defined(__CUDA_ARCH__) || defined(__CUDACC__) +#define __cminpack_attr__ __device__ +#ifndef __cminpack_real__ +#define __cminpack_float__ +#define __cminpack_real__ float +#endif +#define __cminpack_type_fcn_nn__ __cminpack_attr__ int fcn_nn +#define __cminpack_type_fcnder_nn__ __cminpack_attr__ int fcnder_nn +#define __cminpack_type_fcn_mn__ __cminpack_attr__ int fcn_mn +#define __cminpack_type_fcnder_mn__ __cminpack_attr__ int fcnder_mn +#define __cminpack_type_fcnderstr_mn__ __cminpack_attr__ int fcnderstr_mn +#define __cminpack_decl_fcn_nn__ +#define __cminpack_decl_fcnder_nn__ +#define __cminpack_decl_fcn_mn__ +#define __cminpack_decl_fcnder_mn__ +#define __cminpack_decl_fcnderstr_mn__ +#define __cminpack_param_fcn_nn__ +#define __cminpack_param_fcnder_nn__ +#define __cminpack_param_fcn_mn__ +#define __cminpack_param_fcnder_mn__ +#define __cminpack_param_fcnderstr_mn__ +#else +#define __cminpack_attr__ +#ifndef __cminpack_real__ +#define __cminpack_double__ +#define __cminpack_real__ double +#endif +#define __cminpack_type_fcn_nn__ typedef int (*cminpack_func_nn) +#define __cminpack_type_fcnder_nn__ typedef int (*cminpack_funcder_nn) +#define __cminpack_type_fcn_mn__ typedef int (*cminpack_func_mn) +#define __cminpack_type_fcnder_mn__ typedef int (*cminpack_funcder_mn) +#define __cminpack_type_fcnderstr_mn__ typedef int (*cminpack_funcderstr_mn) +#define __cminpack_decl_fcn_nn__ cminpack_func_nn fcn_nn, +#define __cminpack_decl_fcnder_nn__ cminpack_funcder_nn fcnder_nn, +#define __cminpack_decl_fcn_mn__ cminpack_func_mn fcn_mn, +#define __cminpack_decl_fcnder_mn__ cminpack_funcder_mn fcnder_mn, +#define __cminpack_decl_fcnderstr_mn__ cminpack_funcderstr_mn fcnderstr_mn, +#define __cminpack_param_fcn_nn__ fcn_nn, +#define __cminpack_param_fcnder_nn__ fcnder_nn, +#define __cminpack_param_fcn_mn__ fcn_mn, +#define __cminpack_param_fcnder_mn__ fcnder_mn, +#define __cminpack_param_fcnderstr_mn__ fcnderstr_mn, +#endif + +#ifdef __cminpack_double__ +#define __cminpack_func__(func) func +#endif + +#ifdef __cminpack_float__ +#define __cminpack_func__(func) s ## func +#endif + +#ifdef __cminpack_half__ +#define __cminpack_func__(func) h ## func +#endif + +/* Declarations for minpack */ + +/* Function types: */ +/* The first argument can be used to store extra function parameters, thus */ +/* avoiding the use of global variables. */ +/* the iflag parameter is input-only (with respect to the FORTRAN */ +/* version), the output iflag value is the return value of the function. */ +/* If iflag=0, the function shoulkd just print the current values (see */ +/* the nprint parameters below). */ + +/* for hybrd1 and hybrd: */ +/* calculate the functions at x and */ +/* return this vector in fvec. */ +/* return a negative value to terminate hybrd1/hybrd */ +__cminpack_type_fcn_nn__(void *p, int n, const __cminpack_real__ *x, __cminpack_real__ *fvec, int iflag ); + +/* for hybrj1 and hybrj */ +/* if iflag = 1 calculate the functions at x and */ +/* return this vector in fvec. do not alter fjac. */ +/* if iflag = 2 calculate the jacobian at x and */ +/* return this matrix in fjac. do not alter fvec. */ +/* return a negative value to terminate hybrj1/hybrj */ +__cminpack_type_fcnder_nn__(void *p, int n, const __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ *fjac, + int ldfjac, int iflag ); + +/* for lmdif1 and lmdif */ +/* calculate the functions at x and */ +/* return this vector in fvec. */ +/* if iflag = 1 the result is used to compute the residuals. */ +/* if iflag = 2 the result is used to compute the Jacobian by finite differences. */ +/* Jacobian computation requires exactly n function calls with iflag = 2. */ +/* return a negative value to terminate lmdif1/lmdif */ +__cminpack_type_fcn_mn__(void *p, int m, int n, const __cminpack_real__ *x, __cminpack_real__ *fvec, + int iflag ); + +/* for lmder1 and lmder */ +/* if iflag = 1 calculate the functions at x and */ +/* return this vector in fvec. do not alter fjac. */ +/* if iflag = 2 calculate the jacobian at x and */ +/* return this matrix in fjac. do not alter fvec. */ +/* return a negative value to terminate lmder1/lmder */ +__cminpack_type_fcnder_mn__(void *p, int m, int n, const __cminpack_real__ *x, __cminpack_real__ *fvec, + __cminpack_real__ *fjac, int ldfjac, int iflag ); + +/* for lmstr1 and lmstr */ +/* if iflag = 1 calculate the functions at x and */ +/* return this vector in fvec. */ +/* if iflag = i calculate the (i-1)-st row of the */ +/* jacobian at x and return this vector in fjrow. */ +/* return a negative value to terminate lmstr1/lmstr */ +__cminpack_type_fcnderstr_mn__(void *p, int m, int n, const __cminpack_real__ *x, __cminpack_real__ *fvec, + __cminpack_real__ *fjrow, int iflag ); + + + + + + +/* MINPACK functions: */ +/* the info parameter was removed from most functions: the return */ +/* value of the function is used instead. */ +/* The argument 'p' can be used to store extra function parameters, thus */ +/* avoiding the use of global variables. You can also think of it as a */ +/* 'this' pointer a la C++. */ + +/* find a zero of a system of N nonlinear functions in N variables by + a modification of the Powell hybrid method (Jacobian calculated by + a forward-difference approximation) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(hybrd1)( __cminpack_decl_fcn_nn__ + void *p, int n, __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ tol, + __cminpack_real__ *wa, int lwa ); + +/* find a zero of a system of N nonlinear functions in N variables by + a modification of the Powell hybrid method (Jacobian calculated by + a forward-difference approximation, more general). */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(hybrd)( __cminpack_decl_fcn_nn__ + void *p, int n, __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ xtol, int maxfev, + int ml, int mu, __cminpack_real__ epsfcn, __cminpack_real__ *diag, int mode, + __cminpack_real__ factor, int nprint, int *nfev, + __cminpack_real__ *fjac, int ldfjac, __cminpack_real__ *r, int lr, __cminpack_real__ *qtf, + __cminpack_real__ *wa1, __cminpack_real__ *wa2, __cminpack_real__ *wa3, __cminpack_real__ *wa4); + +/* find a zero of a system of N nonlinear functions in N variables by + a modification of the Powell hybrid method (user-supplied Jacobian) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(hybrj1)( __cminpack_decl_fcnder_nn__ void *p, int n, __cminpack_real__ *x, + __cminpack_real__ *fvec, __cminpack_real__ *fjac, int ldfjac, __cminpack_real__ tol, + __cminpack_real__ *wa, int lwa ); + +/* find a zero of a system of N nonlinear functions in N variables by + a modification of the Powell hybrid method (user-supplied Jacobian, + more general) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(hybrj)( __cminpack_decl_fcnder_nn__ void *p, int n, __cminpack_real__ *x, + __cminpack_real__ *fvec, __cminpack_real__ *fjac, int ldfjac, __cminpack_real__ xtol, + int maxfev, __cminpack_real__ *diag, int mode, __cminpack_real__ factor, + int nprint, int *nfev, int *njev, __cminpack_real__ *r, + int lr, __cminpack_real__ *qtf, __cminpack_real__ *wa1, __cminpack_real__ *wa2, + __cminpack_real__ *wa3, __cminpack_real__ *wa4 ); + +/* minimize the sum of the squares of nonlinear functions in N + variables by a modification of the Levenberg-Marquardt algorithm + (Jacobian calculated by a forward-difference approximation) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(lmdif1)( __cminpack_decl_fcn_mn__ + void *p, int m, int n, __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ tol, + int *iwa, __cminpack_real__ *wa, int lwa ); + +/* minimize the sum of the squares of nonlinear functions in N + variables by a modification of the Levenberg-Marquardt algorithm + (Jacobian calculated by a forward-difference approximation, more + general) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(lmdif)( __cminpack_decl_fcn_mn__ + void *p, int m, int n, __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ ftol, + __cminpack_real__ xtol, __cminpack_real__ gtol, int maxfev, __cminpack_real__ epsfcn, + __cminpack_real__ *diag, int mode, __cminpack_real__ factor, int nprint, + int *nfev, __cminpack_real__ *fjac, int ldfjac, int *ipvt, + __cminpack_real__ *qtf, __cminpack_real__ *wa1, __cminpack_real__ *wa2, __cminpack_real__ *wa3, + __cminpack_real__ *wa4 ); + +/* minimize the sum of the squares of nonlinear functions in N + variables by a modification of the Levenberg-Marquardt algorithm + (user-supplied Jacobian) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(lmder1)( __cminpack_decl_fcnder_mn__ + void *p, int m, int n, __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ *fjac, + int ldfjac, __cminpack_real__ tol, int *ipvt, + __cminpack_real__ *wa, int lwa ); + +/* minimize the sum of the squares of nonlinear functions in N + variables by a modification of the Levenberg-Marquardt algorithm + (user-supplied Jacobian, more general) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(lmder)( __cminpack_decl_fcnder_mn__ + void *p, int m, int n, __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ *fjac, + int ldfjac, __cminpack_real__ ftol, __cminpack_real__ xtol, __cminpack_real__ gtol, + int maxfev, __cminpack_real__ *diag, int mode, __cminpack_real__ factor, + int nprint, int *nfev, int *njev, int *ipvt, + __cminpack_real__ *qtf, __cminpack_real__ *wa1, __cminpack_real__ *wa2, __cminpack_real__ *wa3, + __cminpack_real__ *wa4 ); + +/* minimize the sum of the squares of nonlinear functions in N + variables by a modification of the Levenberg-Marquardt algorithm + (user-supplied Jacobian, minimal storage) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(lmstr1)( __cminpack_decl_fcnderstr_mn__ void *p, int m, int n, + __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ *fjac, int ldfjac, + __cminpack_real__ tol, int *ipvt, __cminpack_real__ *wa, int lwa ); + +/* minimize the sum of the squares of nonlinear functions in N + variables by a modification of the Levenberg-Marquardt algorithm + (user-supplied Jacobian, minimal storage, more general) */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(lmstr)( __cminpack_decl_fcnderstr_mn__ void *p, int m, + int n, __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ *fjac, + int ldfjac, __cminpack_real__ ftol, __cminpack_real__ xtol, __cminpack_real__ gtol, + int maxfev, __cminpack_real__ *diag, int mode, __cminpack_real__ factor, + int nprint, int *nfev, int *njev, int *ipvt, + __cminpack_real__ *qtf, __cminpack_real__ *wa1, __cminpack_real__ *wa2, __cminpack_real__ *wa3, + __cminpack_real__ *wa4 ); + +__cminpack_attr__ +void CMINPACK_EXPORT __cminpack_func__(chkder)( int m, int n, const __cminpack_real__ *x, __cminpack_real__ *fvec, __cminpack_real__ *fjac, + int ldfjac, __cminpack_real__ *xp, __cminpack_real__ *fvecp, int mode, + __cminpack_real__ *err ); + +__cminpack_attr__ +__cminpack_real__ CMINPACK_EXPORT __cminpack_func__(dpmpar)( int i ); + +__cminpack_attr__ +__cminpack_real__ CMINPACK_EXPORT __cminpack_func__(enorm)( int n, const __cminpack_real__ *x ); + +/* compute a forward-difference approximation to the m by n jacobian + matrix associated with a specified problem of m functions in n + variables. */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(fdjac2)(__cminpack_decl_fcn_mn__ + void *p, int m, int n, __cminpack_real__ *x, const __cminpack_real__ *fvec, __cminpack_real__ *fjac, + int ldfjac, __cminpack_real__ epsfcn, __cminpack_real__ *wa); + +/* compute a forward-difference approximation to the n by n jacobian + matrix associated with a specified problem of n functions in n + variables. if the jacobian has a banded form, then function + evaluations are saved by only approximating the nonzero terms. */ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(fdjac1)(__cminpack_decl_fcn_nn__ + void *p, int n, __cminpack_real__ *x, const __cminpack_real__ *fvec, __cminpack_real__ *fjac, int ldfjac, + int ml, int mu, __cminpack_real__ epsfcn, __cminpack_real__ *wa1, + __cminpack_real__ *wa2); + +/* compute inverse(JtJ) after a run of lmdif or lmder. The covariance matrix is obtained + by scaling the result by enorm(y)**2/(m-n). If JtJ is singular and k = rank(J), the + pseudo-inverse is computed, and the result has to be scaled by enorm(y)**2/(m-k). */ +__cminpack_attr__ +void CMINPACK_EXPORT __cminpack_func__(covar)(int n, __cminpack_real__ *r, int ldr, + const int *ipvt, __cminpack_real__ tol, __cminpack_real__ *wa); + +/* covar1 estimates the variance-covariance matrix: + C = sigma**2 (JtJ)**+ + where (JtJ)**+ is the inverse of JtJ or the pseudo-inverse of JtJ (in case J does not have full rank), + and sigma**2 = fsumsq / (m - k) + where fsumsq is the residual sum of squares and k is the rank of J. + The function returns 0 if J has full rank, else the rank of J. +*/ +__cminpack_attr__ +int CMINPACK_EXPORT __cminpack_func__(covar1)(int m, int n, __cminpack_real__ fsumsq, __cminpack_real__ *r, int ldr, + const int *ipvt, __cminpack_real__ tol, __cminpack_real__ *wa); + +/* internal MINPACK subroutines */ +__cminpack_attr__ +void __cminpack_func__(dogleg)(int n, const __cminpack_real__ *r, int lr, + const __cminpack_real__ *diag, const __cminpack_real__ *qtb, __cminpack_real__ delta, __cminpack_real__ *x, + __cminpack_real__ *wa1, __cminpack_real__ *wa2); +__cminpack_attr__ +void __cminpack_func__(qrfac)(int m, int n, __cminpack_real__ *a, int + lda, int pivot, int *ipvt, int lipvt, __cminpack_real__ *rdiag, + __cminpack_real__ *acnorm, __cminpack_real__ *wa); +__cminpack_attr__ +void __cminpack_func__(qrsolv)(int n, __cminpack_real__ *r, int ldr, + const int *ipvt, const __cminpack_real__ *diag, const __cminpack_real__ *qtb, __cminpack_real__ *x, + __cminpack_real__ *sdiag, __cminpack_real__ *wa); +__cminpack_attr__ +void __cminpack_func__(qform)(int m, int n, __cminpack_real__ *q, int + ldq, __cminpack_real__ *wa); +__cminpack_attr__ +void __cminpack_func__(r1updt)(int m, int n, __cminpack_real__ *s, int + ls, const __cminpack_real__ *u, __cminpack_real__ *v, __cminpack_real__ *w, int *sing); +__cminpack_attr__ +void __cminpack_func__(r1mpyq)(int m, int n, __cminpack_real__ *a, int + lda, const __cminpack_real__ *v, const __cminpack_real__ *w); +__cminpack_attr__ +void __cminpack_func__(lmpar)(int n, __cminpack_real__ *r, int ldr, + const int *ipvt, const __cminpack_real__ *diag, const __cminpack_real__ *qtb, __cminpack_real__ delta, + __cminpack_real__ *par, __cminpack_real__ *x, __cminpack_real__ *sdiag, __cminpack_real__ *wa1, + __cminpack_real__ *wa2); +__cminpack_attr__ +void __cminpack_func__(rwupdt)(int n, __cminpack_real__ *r, int ldr, + const __cminpack_real__ *w, __cminpack_real__ *b, __cminpack_real__ *alpha, __cminpack_real__ *cos, + __cminpack_real__ *sin); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __CMINPACK_H__ */ diff --git a/cminpack/cminpackP.h b/cminpack/cminpackP.h new file mode 100644 index 0000000..4e8ba7b --- /dev/null +++ b/cminpack/cminpackP.h @@ -0,0 +1,62 @@ +/* Internal header file for cminpack, by Frederic Devernay. */ +#ifndef __CMINPACKP_H__ +#define __CMINPACKP_H__ + +#ifndef __CMINPACK_H__ +#error "cminpackP.h in an internal cminpack header, and must be included after all other headers (including cminpack.h)" +#endif + +#if (defined (USE_CBLAS) || defined (USE_LAPACK)) && !defined (__cminpack_double__) +#error "cminpack can use cblas and lapack only in double precision mode" +#endif + +#ifdef USE_CBLAS +#ifdef __APPLE__ +#include +#else +#include +#endif +#define __cminpack_enorm__(n,x) cblas_dnrm2(n,x,1) +#else +#define __cminpack_enorm__(n,x) __cminpack_func__(enorm)(n,x) +#endif + +#ifdef USE_LAPACK +#ifdef __APPLE__ +#include +#else +#if defined(__LP64__) /* In LP64 match sizes with the 32 bit ABI */ +typedef int __CLPK_integer; +typedef int __CLPK_logical; +typedef float __CLPK_real; +typedef double __CLPK_doublereal; +typedef __CLPK_logical (*__CLPK_L_fp)(); +typedef int __CLPK_ftnlen; +#else +typedef long int __CLPK_integer; +typedef long int __CLPK_logical; +typedef float __CLPK_real; +typedef double __CLPK_doublereal; +typedef __CLPK_logical (*__CLPK_L_fp)(); +typedef long int __CLPK_ftnlen; +#endif +//extern void dlartg_(double *f, double *g, double *cs, double *sn, double *r__); +int dlartg_(__CLPK_doublereal *f, __CLPK_doublereal *g, __CLPK_doublereal *cs, + __CLPK_doublereal *sn, __CLPK_doublereal *r__) +//extern void dgeqp3_(int *m, int *n, double *a, int *lda, int *jpvt, double *tau, double *work, int *lwork, int *info); +int dgeqp3_(__CLPK_integer *m, __CLPK_integer *n, __CLPK_doublereal *a, __CLPK_integer * + lda, __CLPK_integer *jpvt, __CLPK_doublereal *tau, __CLPK_doublereal *work, __CLPK_integer *lwork, + __CLPK_integer *info) +//extern void dgeqrf_(int *m, int *n, double *a, int *lda, double *tau, double *work, int *lwork, int *info); +int dgeqrf_(__CLPK_integer *m, __CLPK_integer *n, __CLPK_doublereal *a, __CLPK_integer * + lda, __CLPK_doublereal *tau, __CLPK_doublereal *work, __CLPK_integer *lwork, __CLPK_integer *info) +#endif +#endif + +#define real __cminpack_real__ +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define max(a,b) ((a) >= (b) ? (a) : (b)) +#define TRUE_ (1) +#define FALSE_ (0) + +#endif /* !__CMINPACKP_H__ */ diff --git a/cminpack/dpmpar.c b/cminpack/dpmpar.c new file mode 100644 index 0000000..81c6fcd --- /dev/null +++ b/cminpack/dpmpar.c @@ -0,0 +1,201 @@ +#include "cminpack.h" +#include +#include "cminpackP.h" + +#define double_EPSILON DBL_EPSILON +#define double_MIN DBL_MIN +#define double_MAX DBL_MAX +#define float_EPSILON FLT_EPSILON +#define float_MIN FLT_MIN +#define float_MAX FLT_MAX +#define half_EPSILON HALF_EPSILON +#define half_MIN HALF_NRM_MIN +#define half_MAX HALF_MAX + +#define DPMPAR(type,X) _DPMPAR(type,X) +#define _DPMPAR(type,X) type ## _ ## X + +__cminpack_attr__ +real __cminpack_func__(dpmpar)(int i) +{ +/* ********** */ + +/* Function dpmpar */ + +/* This function provides double precision machine parameters */ +/* when the appropriate set of data statements is activated (by */ +/* removing the c from column 1) and all other data statements are */ +/* rendered inactive. Most of the parameter values were obtained */ +/* from the corresponding Bell Laboratories Port Library function. */ + +/* The function statement is */ + +/* double precision function dpmpar(i) */ + +/* where */ + +/* i is an integer input variable set to 1, 2, or 3 which */ +/* selects the desired machine parameter. If the machine has */ +/* t base b digits and its smallest and largest exponents are */ +/* emin and emax, respectively, then these parameters are */ + +/* dpmpar(1) = b**(1 - t), the machine precision, */ + +/* dpmpar(2) = b**(emin - 1), the smallest magnitude, */ + +/* dpmpar(3) = b**emax*(1 - b**(-t)), the largest magnitude. */ + +/* Argonne National Laboratory. MINPACK Project. November 1996. */ +/* Burton S. Garbow, Kenneth E. Hillstrom, Jorge J. More' */ + +/* ********** */ + +/* Machine constants for the IBM 360/370 series, */ +/* the Amdahl 470/V6, the ICL 2900, the Itel AS/6, */ +/* the Xerox Sigma 5/7/9 and the Sel systems 85/86. */ + +/* data mcheps(1),mcheps(2) / z34100000, z00000000 / */ +/* data minmag(1),minmag(2) / z00100000, z00000000 / */ +/* data maxmag(1),maxmag(2) / z7fffffff, zffffffff / */ + +/* Machine constants for the Honeywell 600/6000 series. */ + +/* data mcheps(1),mcheps(2) / o606400000000, o000000000000 / */ +/* data minmag(1),minmag(2) / o402400000000, o000000000000 / */ +/* data maxmag(1),maxmag(2) / o376777777777, o777777777777 / */ + +/* Machine constants for the CDC 6000/7000 series. */ + +/* data mcheps(1) / 15614000000000000000b / */ +/* data mcheps(2) / 15010000000000000000b / */ + +/* data minmag(1) / 00604000000000000000b / */ +/* data minmag(2) / 00000000000000000000b / */ + +/* data maxmag(1) / 37767777777777777777b / */ +/* data maxmag(2) / 37167777777777777777b / */ + +/* Machine constants for the PDP-10 (KA processor). */ + +/* data mcheps(1),mcheps(2) / "114400000000, "000000000000 / */ +/* data minmag(1),minmag(2) / "033400000000, "000000000000 / */ +/* data maxmag(1),maxmag(2) / "377777777777, "344777777777 / */ + +/* Machine constants for the PDP-10 (KI processor). */ + +/* data mcheps(1),mcheps(2) / "104400000000, "000000000000 / */ +/* data minmag(1),minmag(2) / "000400000000, "000000000000 / */ +/* data maxmag(1),maxmag(2) / "377777777777, "377777777777 / */ + +/* Machine constants for the PDP-11. */ + +/* data mcheps(1),mcheps(2) / 9472, 0 / */ +/* data mcheps(3),mcheps(4) / 0, 0 / */ + +/* data minmag(1),minmag(2) / 128, 0 / */ +/* data minmag(3),minmag(4) / 0, 0 / */ + +/* data maxmag(1),maxmag(2) / 32767, -1 / */ +/* data maxmag(3),maxmag(4) / -1, -1 / */ + +/* Machine constants for the Burroughs 6700/7700 systems. */ + +/* data mcheps(1) / o1451000000000000 / */ +/* data mcheps(2) / o0000000000000000 / */ + +/* data minmag(1) / o1771000000000000 / */ +/* data minmag(2) / o7770000000000000 / */ + +/* data maxmag(1) / o0777777777777777 / */ +/* data maxmag(2) / o7777777777777777 / */ + +/* Machine constants for the Burroughs 5700 system. */ + +/* data mcheps(1) / o1451000000000000 / */ +/* data mcheps(2) / o0000000000000000 / */ + +/* data minmag(1) / o1771000000000000 / */ +/* data minmag(2) / o0000000000000000 / */ + +/* data maxmag(1) / o0777777777777777 / */ +/* data maxmag(2) / o0007777777777777 / */ + +/* Machine constants for the Burroughs 1700 system. */ + +/* data mcheps(1) / zcc6800000 / */ +/* data mcheps(2) / z000000000 / */ + +/* data minmag(1) / zc00800000 / */ +/* data minmag(2) / z000000000 / */ + +/* data maxmag(1) / zdffffffff / */ +/* data maxmag(2) / zfffffffff / */ + +/* Machine constants for the Univac 1100 series. */ + +/* data mcheps(1),mcheps(2) / o170640000000, o000000000000 / */ +/* data minmag(1),minmag(2) / o000040000000, o000000000000 / */ +/* data maxmag(1),maxmag(2) / o377777777777, o777777777777 / */ + +/* Machine constants for the Data General Eclipse S/200. */ + +/* Note - it may be appropriate to include the following card - */ +/* static dmach(3) */ + +/* data minmag/20k,3*0/,maxmag/77777k,3*177777k/ */ +/* data mcheps/32020k,3*0/ */ + +/* Machine constants for the Harris 220. */ + +/* data mcheps(1),mcheps(2) / '20000000, '00000334 / */ +/* data minmag(1),minmag(2) / '20000000, '00000201 / */ +/* data maxmag(1),maxmag(2) / '37777777, '37777577 / */ + +/* Machine constants for the Cray-1. */ + +/* data mcheps(1) / 0376424000000000000000b / */ +/* data mcheps(2) / 0000000000000000000000b / */ + +/* data minmag(1) / 0200034000000000000000b / */ +/* data minmag(2) / 0000000000000000000000b / */ + +/* data maxmag(1) / 0577777777777777777777b / */ +/* data maxmag(2) / 0000007777777777777776b / */ + +/* Machine constants for the Prime 400. */ + +/* data mcheps(1),mcheps(2) / :10000000000, :00000000123 / */ +/* data minmag(1),minmag(2) / :10000000000, :00000100000 / */ +/* data maxmag(1),maxmag(2) / :17777777777, :37777677776 / */ + +/* Machine constants for the VAX-11. */ + +/* data mcheps(1),mcheps(2) / 9472, 0 / */ +/* data minmag(1),minmag(2) / 128, 0 / */ +/* data maxmag(1),maxmag(2) / -32769, -1 / */ + +/* Machine constants for IEEE machines. */ + +/* data dmach(1) /2.22044604926d-16/ */ +/* data dmach(2) /2.22507385852d-308/ */ +/* data dmach(3) /1.79769313485d+308/ */ + + switch(i) { + case 1: + return DPMPAR(real,EPSILON); /* 2.2204460492503131e-16 | 1.19209290e-07F */ + case 2: + return DPMPAR(real,MIN); /* 2.2250738585072014e-308 | 1.17549435e-38F */ + default: + return DPMPAR(real,MAX); /* 1.7976931348623157e+308 | 3.40282347e+38F */ + } + +/* Last card of function dpmpar. */ + +} /* dpmpar_ */ + +#undef mcheps +#undef maxmag +#undef minmag +#undef dmach + + diff --git a/cminpack/enorm.c b/cminpack/enorm.c new file mode 100644 index 0000000..ad10824 --- /dev/null +++ b/cminpack/enorm.c @@ -0,0 +1,157 @@ +#include "cminpack.h" +#include +#include "cminpackP.h" + +/* + About the values for rdwarf and rgiant. + + The original values, both in signe-precision FORTRAN source code and in double-precision code were: +#define rdwarf 3.834e-20 +#define rgiant 1.304e19 + See for example: + http://www.netlib.org/slatec/src/denorm.f + http://www.netlib.org/slatec/src/enorm.f + However, rdwarf is smaller than sqrt(FLT_MIN) = 1.0842021724855044e-19, so that rdwarf**2 will + underflow. This contradicts the constraints expressed in the comments below. + + We changed these constants to be sqrt(dpmpar(2))*0.9 and sqrt(dpmpar(3))*0.9, as proposed by the + implementation found in MPFIT http://cow.physics.wisc.edu/~craigm/idl/fitting.html +*/ + +#define double_dwarf (1.4916681462400413e-154*0.9) +#define double_giant (1.3407807929942596e+154*0.9) +#define float_dwarf (1.0842021724855044e-19f*0.9f) +#define float_giant (1.8446743523953730e+19f*0.9f) +#define half_dwarf (2.4414062505039999e-4f*0.9f) +#define half_giant (255.93749236874225497222f*0.9f) + +#define dwarf(type) _dwarf(type) +#define _dwarf(type) type ## _dwarf +#define giant(type) _giant(type) +#define _giant(type) type ## _giant + +#define rdwarf dwarf(real) +#define rgiant giant(real) + +__cminpack_attr__ +real __cminpack_func__(enorm)(int n, const real *x) +{ +#ifdef USE_CBLAS + return cblas_dnrm2(n, x, 1); +#else /* !USE_CBLAS */ + /* System generated locals */ + real ret_val, d1; + + /* Local variables */ + int i; + real s1, s2, s3, xabs, x1max, x3max, agiant, floatn; + +/* ********** */ + +/* function enorm */ + +/* given an n-vector x, this function calculates the */ +/* euclidean norm of x. */ + +/* the euclidean norm is computed by accumulating the sum of */ +/* squares in three different sums. the sums of squares for the */ +/* small and large components are scaled so that no overflows */ +/* occur. non-destructive underflows are permitted. underflows */ +/* and overflows do not occur in the computation of the unscaled */ +/* sum of squares for the intermediate components. */ +/* the definitions of small, intermediate and large components */ +/* depend on two constants, rdwarf and rgiant. the main */ +/* restrictions on these constants are that rdwarf**2 not */ +/* underflow and rgiant**2 not overflow. the constants */ +/* given here are suitable for every known computer. */ + +/* the function statement is */ + +/* double precision function enorm(n,x) */ + +/* where */ + +/* n is a positive integer input variable. */ + +/* x is an input array of length n. */ + +/* subprograms called */ + +/* fortran-supplied ... dabs,dsqrt */ + +/* argonne national laboratory. minpack project. march 1980. */ +/* burton s. garbow, kenneth e. hillstrom, jorge j. more */ + +/* ********** */ + + s1 = 0.; + s2 = 0.; + s3 = 0.; + x1max = 0.; + x3max = 0.; + floatn = (real) (n); + agiant = rgiant / floatn; + for (i = 0; i < n; ++i) { + xabs = fabs(x[i]); + if (xabs <= rdwarf || xabs >= agiant) { + if (xabs > rdwarf) { + +/* sum for large components. */ + + if (xabs > x1max) { + /* Computing 2nd power */ + d1 = x1max / xabs; + s1 = 1. + s1 * (d1 * d1); + x1max = xabs; + } else { + /* Computing 2nd power */ + d1 = xabs / x1max; + s1 += d1 * d1; + } + } else { + +/* sum for small components. */ + + if (xabs > x3max) { + /* Computing 2nd power */ + d1 = x3max / xabs; + s3 = 1. + s3 * (d1 * d1); + x3max = xabs; + } else { + if (xabs != 0.) { + /* Computing 2nd power */ + d1 = xabs / x3max; + s3 += d1 * d1; + } + } + } + } else { + +/* sum for intermediate components. */ + + /* Computing 2nd power */ + s2 += xabs * xabs; + } + } + +/* calculation of norm. */ + + if (s1 != 0.) { + ret_val = x1max * sqrt(s1 + (s2 / x1max) / x1max); + } else { + if (s2 != 0.) { + if (s2 >= x3max) { + ret_val = sqrt(s2 * (1. + (x3max / s2) * (x3max * s3))); + } else { + ret_val = sqrt(x3max * ((s2 / x3max) + (x3max * s3))); + } + } else { + ret_val = x3max * sqrt(s3); + } + } + return ret_val; + +/* last card of function enorm. */ +#endif /* !USE_CBLAS */ +} /* enorm_ */ + diff --git a/cminpack/lmder.c b/cminpack/lmder.c new file mode 100644 index 0000000..7f57428 --- /dev/null +++ b/cminpack/lmder.c @@ -0,0 +1,526 @@ +#include "cminpack.h" +#include +#include "cminpackP.h" + +__cminpack_attr__ +int __cminpack_func__(lmder)(__cminpack_decl_fcnder_mn__ void *p, int m, int n, real *x, + real *fvec, real *fjac, int ldfjac, real ftol, + real xtol, real gtol, int maxfev, real * + diag, int mode, real factor, int nprint, + int *nfev, int *njev, int *ipvt, real *qtf, + real *wa1, real *wa2, real *wa3, real *wa4) +{ + /* Initialized data */ + +#define p1 .1 +#define p5 .5 +#define p25 .25 +#define p75 .75 +#define p0001 1e-4 + + /* System generated locals */ + real d1, d2; + + /* Local variables */ + int i, j, l; + real par, sum; + int iter; + real temp, temp1, temp2; + int iflag; + real delta = 0.; + real ratio; + real fnorm, gnorm, pnorm, xnorm = 0., fnorm1, actred, dirder, + epsmch, prered; + int info; + +/* ********** */ + +/* subroutine lmder */ + +/* the purpose of lmder is to minimize the sum of the squares of */ +/* m nonlinear functions in n variables by a modification of */ +/* the levenberg-marquardt algorithm. the user must provide a */ +/* subroutine which calculates the functions and the jacobian. */ + +/* the subroutine statement is */ + +/* subroutine lmder(fcn,m,n,x,fvec,fjac,ldfjac,ftol,xtol,gtol, */ +/* maxfev,diag,mode,factor,nprint,info,nfev, */ +/* njev,ipvt,qtf,wa1,wa2,wa3,wa4) */ + +/* where */ + +/* fcn is the name of the user-supplied subroutine which */ +/* calculates the functions and the jacobian. fcn must */ +/* be declared in an external statement in the user */ +/* calling program, and should be written as follows. */ + +/* subroutine fcn(m,n,x,fvec,fjac,ldfjac,iflag) */ +/* integer m,n,ldfjac,iflag */ +/* double precision x(n),fvec(m),fjac(ldfjac,n) */ +/* ---------- */ +/* if iflag = 1 calculate the functions at x and */ +/* return this vector in fvec. do not alter fjac. */ +/* if iflag = 2 calculate the jacobian at x and */ +/* return this matrix in fjac. do not alter fvec. */ +/* ---------- */ +/* return */ +/* end */ + +/* the value of iflag should not be changed by fcn unless */ +/* the user wants to terminate execution of lmder. */ +/* in this case set iflag to a negative integer. */ + +/* m is a positive integer input variable set to the number */ +/* of functions. */ + +/* n is a positive integer input variable set to the number */ +/* of variables. n must not exceed m. */ + +/* x is an array of length n. on input x must contain */ +/* an initial estimate of the solution vector. on output x */ +/* contains the final estimate of the solution vector. */ + +/* fvec is an output array of length m which contains */ +/* the functions evaluated at the output x. */ + +/* fjac is an output m by n array. the upper n by n submatrix */ +/* of fjac contains an upper triangular matrix r with */ +/* diagonal elements of nonincreasing magnitude such that */ + +/* t t t */ +/* p *(jac *jac)*p = r *r, */ + +/* where p is a permutation matrix and jac is the final */ +/* calculated jacobian. column j of p is column ipvt(j) */ +/* (see below) of the identity matrix. the lower trapezoidal */ +/* part of fjac contains information generated during */ +/* the computation of r. */ + +/* ldfjac is a positive integer input variable not less than m */ +/* which specifies the leading dimension of the array fjac. */ + +/* ftol is a nonnegative input variable. termination */ +/* occurs when both the actual and predicted relative */ +/* reductions in the sum of squares are at most ftol. */ +/* therefore, ftol measures the relative error desired */ +/* in the sum of squares. */ + +/* xtol is a nonnegative input variable. termination */ +/* occurs when the relative error between two consecutive */ +/* iterates is at most xtol. therefore, xtol measures the */ +/* relative error desired in the approximate solution. */ + +/* gtol is a nonnegative input variable. termination */ +/* occurs when the cosine of the angle between fvec and */ +/* any column of the jacobian is at most gtol in absolute */ +/* value. therefore, gtol measures the orthogonality */ +/* desired between the function vector and the columns */ +/* of the jacobian. */ + +/* maxfev is a positive integer input variable. termination */ +/* occurs when the number of calls to fcn with iflag = 1 */ +/* has reached maxfev. */ + +/* diag is an array of length n. if mode = 1 (see */ +/* below), diag is internally set. if mode = 2, diag */ +/* must contain positive entries that serve as */ +/* multiplicative scale factors for the variables. */ + +/* mode is an integer input variable. if mode = 1, the */ +/* variables will be scaled internally. if mode = 2, */ +/* the scaling is specified by the input diag. other */ +/* values of mode are equivalent to mode = 1. */ + +/* factor is a positive input variable used in determining the */ +/* initial step bound. this bound is set to the product of */ +/* factor and the euclidean norm of diag*x if nonzero, or else */ +/* to factor itself. in most cases factor should lie in the */ +/* interval (.1,100.).100. is a generally recommended value. */ + +/* nprint is an integer input variable that enables controlled */ +/* printing of iterates if it is positive. in this case, */ +/* fcn is called with iflag = 0 at the beginning of the first */ +/* iteration and every nprint iterations thereafter and */ +/* immediately prior to return, with x, fvec, and fjac */ +/* available for printing. fvec and fjac should not be */ +/* altered. if nprint is not positive, no special calls */ +/* of fcn with iflag = 0 are made. */ + +/* info is an integer output variable. if the user has */ +/* terminated execution, info is set to the (negative) */ +/* value of iflag. see description of fcn. otherwise, */ +/* info is set as follows. */ + +/* info = 0 improper input parameters. */ + +/* info = 1 both actual and predicted relative reductions */ +/* in the sum of squares are at most ftol. */ + +/* info = 2 relative error between two consecutive iterates */ +/* is at most xtol. */ + +/* info = 3 conditions for info = 1 and info = 2 both hold. */ + +/* info = 4 the cosine of the angle between fvec and any */ +/* column of the jacobian is at most gtol in */ +/* absolute value. */ + +/* info = 5 number of calls to fcn with iflag = 1 has */ +/* reached maxfev. */ + +/* info = 6 ftol is too small. no further reduction in */ +/* the sum of squares is possible. */ + +/* info = 7 xtol is too small. no further improvement in */ +/* the approximate solution x is possible. */ + +/* info = 8 gtol is too small. fvec is orthogonal to the */ +/* columns of the jacobian to machine precision. */ + +/* nfev is an integer output variable set to the number of */ +/* calls to fcn with iflag = 1. */ + +/* njev is an integer output variable set to the number of */ +/* calls to fcn with iflag = 2. */ + +/* ipvt is an integer output array of length n. ipvt */ +/* defines a permutation matrix p such that jac*p = q*r, */ +/* where jac is the final calculated jacobian, q is */ +/* orthogonal (not stored), and r is upper triangular */ +/* with diagonal elements of nonincreasing magnitude. */ +/* column j of p is column ipvt(j) of the identity matrix. */ + +/* qtf is an output array of length n which contains */ +/* the first n elements of the vector (q transpose)*fvec. */ + +/* wa1, wa2, and wa3 are work arrays of length n. */ + +/* wa4 is a work array of length m. */ + +/* subprograms called */ + +/* user-supplied ...... fcn */ + +/* minpack-supplied ... dpmpar,enorm,lmpar,qrfac */ + +/* fortran-supplied ... dabs,dmax1,dmin1,dsqrt,mod */ + +/* argonne national laboratory. minpack project. march 1980. */ +/* burton s. garbow, kenneth e. hillstrom, jorge j. more */ + +/* ********** */ + +/* epsmch is the machine precision. */ + + epsmch = __cminpack_func__(dpmpar)(1); + + info = 0; + iflag = 0; + *nfev = 0; + *njev = 0; + +/* check the input parameters for errors. */ + + if (n <= 0 || m < n || ldfjac < m || ftol < 0. || xtol < 0. || + gtol < 0. || maxfev <= 0 || factor <= 0.) { + goto TERMINATE; + } + if (mode == 2) { + for (j = 0; j < n; ++j) { + if (diag[j] <= 0.) { + goto TERMINATE; + } + } + } + +/* evaluate the function at the starting point */ +/* and calculate its norm. */ + + iflag = fcnder_mn(p, m, n, x, fvec, fjac, ldfjac, 1); + *nfev = 1; + if (iflag < 0) { + goto TERMINATE; + } + fnorm = __cminpack_enorm__(m, fvec); + +/* initialize levenberg-marquardt parameter and iteration counter. */ + + par = 0.; + iter = 1; + +/* beginning of the outer loop. */ + + for (;;) { + +/* calculate the jacobian matrix. */ + + iflag = fcnder_mn(p, m, n, x, fvec, fjac, ldfjac, 2); + ++(*njev); + if (iflag < 0) { + goto TERMINATE; + } + +/* if requested, call fcn to enable printing of iterates. */ + + if (nprint > 0) { + iflag = 0; + if ((iter - 1) % nprint == 0) { + iflag = fcnder_mn(p, m, n, x, fvec, fjac, ldfjac, 0); + } + if (iflag < 0) { + goto TERMINATE; + } + } + +/* compute the qr factorization of the jacobian. */ + + __cminpack_func__(qrfac)(m, n, fjac, ldfjac, TRUE_, ipvt, n, + wa1, wa2, wa3); + +/* on the first iteration and if mode is 1, scale according */ +/* to the norms of the columns of the initial jacobian. */ + + if (iter == 1) { + if (mode != 2) { + for (j = 0; j < n; ++j) { + diag[j] = wa2[j]; + if (wa2[j] == 0.) { + diag[j] = 1.; + } + } + } + +/* on the first iteration, calculate the norm of the scaled x */ +/* and initialize the step bound delta. */ + + for (j = 0; j < n; ++j) { + wa3[j] = diag[j] * x[j]; + } + xnorm = __cminpack_enorm__(n, wa3); + delta = factor * xnorm; + if (delta == 0.) { + delta = factor; + } + } + +/* form (q transpose)*fvec and store the first n components in */ +/* qtf. */ + + for (i = 0; i < m; ++i) { + wa4[i] = fvec[i]; + } + for (j = 0; j < n; ++j) { + if (fjac[j + j * ldfjac] != 0.) { + sum = 0.; + for (i = j; i < m; ++i) { + sum += fjac[i + j * ldfjac] * wa4[i]; + } + temp = -sum / fjac[j + j * ldfjac]; + for (i = j; i < m; ++i) { + wa4[i] += fjac[i + j * ldfjac] * temp; + } + } + fjac[j + j * ldfjac] = wa1[j]; + qtf[j] = wa4[j]; + } + +/* compute the norm of the scaled gradient. */ + + gnorm = 0.; + if (fnorm != 0.) { + for (j = 0; j < n; ++j) { + l = ipvt[j]-1; + if (wa2[l] != 0.) { + sum = 0.; + for (i = 0; i <= j; ++i) { + sum += fjac[i + j * ldfjac] * (qtf[i] / fnorm); + } + /* Computing MAX */ + d1 = fabs(sum / wa2[l]); + gnorm = max(gnorm,d1); + } + } + } + +/* test for convergence of the gradient norm. */ + + if (gnorm <= gtol) { + info = 4; + } + if (info != 0) { + goto TERMINATE; + } + +/* rescale if necessary. */ + + if (mode != 2) { + for (j = 0; j < n; ++j) { + /* Computing MAX */ + d1 = diag[j], d2 = wa2[j]; + diag[j] = max(d1,d2); + } + } + +/* beginning of the inner loop. */ + + do { + +/* determine the levenberg-marquardt parameter. */ + + __cminpack_func__(lmpar)(n, fjac, ldfjac, ipvt, diag, qtf, delta, + &par, wa1, wa2, wa3, wa4); + +/* store the direction p and x + p. calculate the norm of p. */ + + for (j = 0; j < n; ++j) { + wa1[j] = -wa1[j]; + wa2[j] = x[j] + wa1[j]; + wa3[j] = diag[j] * wa1[j]; + } + pnorm = __cminpack_enorm__(n, wa3); + +/* on the first iteration, adjust the initial step bound. */ + + if (iter == 1) { + delta = min(delta,pnorm); + } + +/* evaluate the function at x + p and calculate its norm. */ + + iflag = fcnder_mn(p, m, n, wa2, wa4, fjac, ldfjac, 1); + ++(*nfev); + if (iflag < 0) { + goto TERMINATE; + } + fnorm1 = __cminpack_enorm__(m, wa4); + +/* compute the scaled actual reduction. */ + + actred = -1.; + if (p1 * fnorm1 < fnorm) { + /* Computing 2nd power */ + d1 = fnorm1 / fnorm; + actred = 1. - d1 * d1; + } + +/* compute the scaled predicted reduction and */ +/* the scaled directional derivative. */ + + for (j = 0; j < n; ++j) { + wa3[j] = 0.; + l = ipvt[j]-1; + temp = wa1[l]; + for (i = 0; i <= j; ++i) { + wa3[i] += fjac[i + j * ldfjac] * temp; + } + } + temp1 = __cminpack_enorm__(n, wa3) / fnorm; + temp2 = (sqrt(par) * pnorm) / fnorm; + prered = temp1 * temp1 + temp2 * temp2 / p5; + dirder = -(temp1 * temp1 + temp2 * temp2); + +/* compute the ratio of the actual to the predicted */ +/* reduction. */ + + ratio = 0.; + if (prered != 0.) { + ratio = actred / prered; + } + +/* update the step bound. */ + + if (ratio <= p25) { + if (actred >= 0.) { + temp = p5; + } else { + temp = p5 * dirder / (dirder + p5 * actred); + } + if (p1 * fnorm1 >= fnorm || temp < p1) { + temp = p1; + } + /* Computing MIN */ + d1 = pnorm / p1; + delta = temp * min(delta,d1); + par /= temp; + } else { + if (par == 0. || ratio >= p75) { + delta = pnorm / p5; + par = p5 * par; + } + } + +/* test for successful iteration. */ + + if (ratio >= p0001) { + +/* successful iteration. update x, fvec, and their norms. */ + + for (j = 0; j < n; ++j) { + x[j] = wa2[j]; + wa2[j] = diag[j] * x[j]; + } + for (i = 0; i < m; ++i) { + fvec[i] = wa4[i]; + } + xnorm = __cminpack_enorm__(n, wa2); + fnorm = fnorm1; + ++iter; + } + +/* tests for convergence. */ + + if (fabs(actred) <= ftol && prered <= ftol && p5 * ratio <= 1.) { + info = 1; + } + if (delta <= xtol * xnorm) { + info = 2; + } + if (fabs(actred) <= ftol && prered <= ftol && p5 * ratio <= 1. && info == 2) { + info = 3; + } + if (info != 0) { + goto TERMINATE; + } + +/* tests for termination and stringent tolerances. */ + + if (*nfev >= maxfev) { + info = 5; + } + if (fabs(actred) <= epsmch && prered <= epsmch && p5 * ratio <= 1.) { + info = 6; + } + if (delta <= epsmch * xnorm) { + info = 7; + } + if (gnorm <= epsmch) { + info = 8; + } + if (info != 0) { + goto TERMINATE; + } + +/* end of the inner loop. repeat if iteration unsuccessful. */ + + } while (ratio < p0001); + +/* end of the outer loop. */ + + } +TERMINATE: + +/* termination, either normal or user imposed. */ + + if (iflag < 0) { + info = iflag; + } + if (nprint > 0) { + fcnder_mn(p, m, n, x, fvec, fjac, ldfjac, 0); + } + return info; + +/* last card of subroutine lmder. */ + +} /* lmder_ */ + diff --git a/cminpack/lmder1.c b/cminpack/lmder1.c new file mode 100644 index 0000000..581462e --- /dev/null +++ b/cminpack/lmder1.c @@ -0,0 +1,167 @@ +#include "cminpack.h" +#include "cminpackP.h" + +__cminpack_attr__ +int __cminpack_func__(lmder1)(__cminpack_decl_fcnder_mn__ void *p, int m, int n, real *x, + real *fvec, real *fjac, int ldfjac, real tol, + int *ipvt, real *wa, int lwa) +{ + /* Initialized data */ + + const real factor = 100.; + + /* Local variables */ + int mode, nfev, njev; + real ftol, gtol, xtol; + int maxfev, nprint; + int info; + +/* ********** */ + +/* subroutine lmder1 */ + +/* the purpose of lmder1 is to minimize the sum of the squares of */ +/* m nonlinear functions in n variables by a modification of the */ +/* levenberg-marquardt algorithm. this is done by using the more */ +/* general least-squares solver lmder. the user must provide a */ +/* subroutine which calculates the functions and the jacobian. */ + +/* the subroutine statement is */ + +/* subroutine lmder1(fcn,m,n,x,fvec,fjac,ldfjac,tol,info, */ +/* ipvt,wa,lwa) */ + +/* where */ + +/* fcn is the name of the user-supplied subroutine which */ +/* calculates the functions and the jacobian. fcn must */ +/* be declared in an external statement in the user */ +/* calling program, and should be written as follows. */ + +/* subroutine fcn(m,n,x,fvec,fjac,ldfjac,iflag) */ +/* integer m,n,ldfjac,iflag */ +/* double precision x(n),fvec(m),fjac(ldfjac,n) */ +/* ---------- */ +/* if iflag = 1 calculate the functions at x and */ +/* return this vector in fvec. do not alter fjac. */ +/* if iflag = 2 calculate the jacobian at x and */ +/* return this matrix in fjac. do not alter fvec. */ +/* ---------- */ +/* return */ +/* end */ + +/* the value of iflag should not be changed by fcn unless */ +/* the user wants to terminate execution of lmder1. */ +/* in this case set iflag to a negative integer. */ + +/* m is a positive integer input variable set to the number */ +/* of functions. */ + +/* n is a positive integer input variable set to the number */ +/* of variables. n must not exceed m. */ + +/* x is an array of length n. on input x must contain */ +/* an initial estimate of the solution vector. on output x */ +/* contains the final estimate of the solution vector. */ + +/* fvec is an output array of length m which contains */ +/* the functions evaluated at the output x. */ + +/* fjac is an output m by n array. the upper n by n submatrix */ +/* of fjac contains an upper triangular matrix r with */ +/* diagonal elements of nonincreasing magnitude such that */ + +/* t t t */ +/* p *(jac *jac)*p = r *r, */ + +/* where p is a permutation matrix and jac is the final */ +/* calculated jacobian. column j of p is column ipvt(j) */ +/* (see below) of the identity matrix. the lower trapezoidal */ +/* part of fjac contains information generated during */ +/* the computation of r. */ + +/* ldfjac is a positive integer input variable not less than m */ +/* which specifies the leading dimension of the array fjac. */ + +/* tol is a nonnegative input variable. termination occurs */ +/* when the algorithm estimates either that the relative */ +/* error in the sum of squares is at most tol or that */ +/* the relative error between x and the solution is at */ +/* most tol. */ + +/* info is an integer output variable. if the user has */ +/* terminated execution, info is set to the (negative) */ +/* value of iflag. see description of fcn. otherwise, */ +/* info is set as follows. */ + +/* info = 0 improper input parameters. */ + +/* info = 1 algorithm estimates that the relative error */ +/* in the sum of squares is at most tol. */ + +/* info = 2 algorithm estimates that the relative error */ +/* between x and the solution is at most tol. */ + +/* info = 3 conditions for info = 1 and info = 2 both hold. */ + +/* info = 4 fvec is orthogonal to the columns of the */ +/* jacobian to machine precision. */ + +/* info = 5 number of calls to fcn with iflag = 1 has */ +/* reached 100*(n+1). */ + +/* info = 6 tol is too small. no further reduction in */ +/* the sum of squares is possible. */ + +/* info = 7 tol is too small. no further improvement in */ +/* the approximate solution x is possible. */ + +/* ipvt is an integer output array of length n. ipvt */ +/* defines a permutation matrix p such that jac*p = q*r, */ +/* where jac is the final calculated jacobian, q is */ +/* orthogonal (not stored), and r is upper triangular */ +/* with diagonal elements of nonincreasing magnitude. */ +/* column j of p is column ipvt(j) of the identity matrix. */ + +/* wa is a work array of length lwa. */ + +/* lwa is a positive integer input variable not less than 5*n+m. */ + +/* subprograms called */ + +/* user-supplied ...... fcn */ + +/* minpack-supplied ... lmder */ + +/* argonne national laboratory. minpack project. march 1980. */ +/* burton s. garbow, kenneth e. hillstrom, jorge j. more */ + +/* ********** */ + +/* check the input parameters for errors. */ + + if (n <= 0 || m < n || ldfjac < m || tol < 0. || lwa < n * 5 + m) { + return 0; + } + +/* call lmder. */ + + maxfev = (n + 1) * 100; + ftol = tol; + xtol = tol; + gtol = 0.; + mode = 1; + nprint = 0; + info = __cminpack_func__(lmder)(__cminpack_param_fcnder_mn__ p, m, n, x, fvec, fjac, ldfjac, + ftol, xtol, gtol, maxfev, wa, mode, factor, nprint, + &nfev, &njev, ipvt, &wa[n], &wa[(n << 1)], & + wa[n * 3], &wa[(n << 2)], &wa[n * 5]); + if (info == 8) { + info = 4; + } + return info; + +/* last card of subroutine lmder1. */ + +} /* lmder1_ */ + diff --git a/cminpack/lmpar.c b/cminpack/lmpar.c new file mode 100644 index 0000000..108e687 --- /dev/null +++ b/cminpack/lmpar.c @@ -0,0 +1,338 @@ +/* lmpar.f -- translated by f2c (version 20020621). + You must link the resulting object file with the libraries: + -lf2c -lm (in that order) +*/ + +#include "cminpack.h" +#include +#include "cminpackP.h" + +__cminpack_attr__ +void __cminpack_func__(lmpar)(int n, real *r, int ldr, + const int *ipvt, const real *diag, const real *qtb, real delta, + real *par, real *x, real *sdiag, real *wa1, + real *wa2) +{ + /* Initialized data */ + +#define p1 .1 +#define p001 .001 + + /* System generated locals */ + real d1, d2; + + /* Local variables */ + int j, l; + real fp; + real parc, parl; + int iter; + real temp, paru, dwarf; + int nsing; + real gnorm; + real dxnorm; + +/* ********** */ + +/* subroutine lmpar */ + +/* given an m by n matrix a, an n by n nonsingular diagonal */ +/* matrix d, an m-vector b, and a positive number delta, */ +/* the problem is to determine a value for the parameter */ +/* par such that if x solves the system */ + +/* a*x = b , sqrt(par)*d*x = 0 , */ + +/* in the least squares sense, and dxnorm is the euclidean */ +/* norm of d*x, then either par is zero and */ + +/* (dxnorm-delta) .le. 0.1*delta , */ + +/* or par is positive and */ + +/* abs(dxnorm-delta) .le. 0.1*delta . */ + +/* this subroutine completes the solution of the problem */ +/* if it is provided with the necessary information from the */ +/* qr factorization, with column pivoting, of a. that is, if */ +/* a*p = q*r, where p is a permutation matrix, q has orthogonal */ +/* columns, and r is an upper triangular matrix with diagonal */ +/* elements of nonincreasing magnitude, then lmpar expects */ +/* the full upper triangle of r, the permutation matrix p, */ +/* and the first n components of (q transpose)*b. on output */ +/* lmpar also provides an upper triangular matrix s such that */ + +/* t t t */ +/* p *(a *a + par*d*d)*p = s *s . */ + +/* s is employed within lmpar and may be of separate interest. */ + +/* only a few iterations are generally needed for convergence */ +/* of the algorithm. if, however, the limit of 10 iterations */ +/* is reached, then the output par will contain the best */ +/* value obtained so far. */ + +/* the subroutine statement is */ + +/* subroutine lmpar(n,r,ldr,ipvt,diag,qtb,delta,par,x,sdiag, */ +/* wa1,wa2) */ + +/* where */ + +/* n is a positive integer input variable set to the order of r. */ + +/* r is an n by n array. on input the full upper triangle */ +/* must contain the full upper triangle of the matrix r. */ +/* on output the full upper triangle is unaltered, and the */ +/* strict lower triangle contains the strict upper triangle */ +/* (transposed) of the upper triangular matrix s. */ + +/* ldr is a positive integer input variable not less than n */ +/* which specifies the leading dimension of the array r. */ + +/* ipvt is an integer input array of length n which defines the */ +/* permutation matrix p such that a*p = q*r. column j of p */ +/* is column ipvt(j) of the identity matrix. */ + +/* diag is an input array of length n which must contain the */ +/* diagonal elements of the matrix d. */ + +/* qtb is an input array of length n which must contain the first */ +/* n elements of the vector (q transpose)*b. */ + +/* delta is a positive input variable which specifies an upper */ +/* bound on the euclidean norm of d*x. */ + +/* par is a nonnegative variable. on input par contains an */ +/* initial estimate of the levenberg-marquardt parameter. */ +/* on output par contains the final estimate. */ + +/* x is an output array of length n which contains the least */ +/* squares solution of the system a*x = b, sqrt(par)*d*x = 0, */ +/* for the output par. */ + +/* sdiag is an output array of length n which contains the */ +/* diagonal elements of the upper triangular matrix s. */ + +/* wa1 and wa2 are work arrays of length n. */ + +/* subprograms called */ + +/* minpack-supplied ... dpmpar,enorm,qrsolv */ + +/* fortran-supplied ... dabs,dmax1,dmin1,dsqrt */ + +/* argonne national laboratory. minpack project. march 1980. */ +/* burton s. garbow, kenneth e. hillstrom, jorge j. more */ + +/* ********** */ + +/* dwarf is the smallest positive magnitude. */ + + dwarf = __cminpack_func__(dpmpar)(2); + +/* compute and store in x the gauss-newton direction. if the */ +/* jacobian is rank-deficient, obtain a least squares solution. */ + + nsing = n; + for (j = 0; j < n; ++j) { + wa1[j] = qtb[j]; + if (r[j + j * ldr] == 0. && nsing == n) { + nsing = j; + } + if (nsing < n) { + wa1[j] = 0.; + } + } +# ifdef USE_CBLAS + cblas_dtrsv(CblasColMajor, CblasUpper, CblasNoTrans, CblasNonUnit, nsing, r, ldr, wa1, 1); +# else + if (nsing >= 1) { + int k; + for (k = 1; k <= nsing; ++k) { + j = nsing - k; + wa1[j] /= r[j + j * ldr]; + temp = wa1[j]; + if (j >= 1) { + int i; + for (i = 0; i < j; ++i) { + wa1[i] -= r[i + j * ldr] * temp; + } + } + } + } +# endif + for (j = 0; j < n; ++j) { + l = ipvt[j]-1; + x[l] = wa1[j]; + } + +/* initialize the iteration counter. */ +/* evaluate the function at the origin, and test */ +/* for acceptance of the gauss-newton direction. */ + + iter = 0; + for (j = 0; j < n; ++j) { + wa2[j] = diag[j] * x[j]; + } + dxnorm = __cminpack_enorm__(n, wa2); + fp = dxnorm - delta; + if (fp <= p1 * delta) { + goto TERMINATE; + } + +/* if the jacobian is not rank deficient, the newton */ +/* step provides a lower bound, parl, for the zero of */ +/* the function. otherwise set this bound to zero. */ + + parl = 0.; + if (nsing >= n) { + for (j = 0; j < n; ++j) { + l = ipvt[j]-1; + wa1[j] = diag[l] * (wa2[l] / dxnorm); + } +# ifdef USE_CBLAS + cblas_dtrsv(CblasColMajor, CblasUpper, CblasTrans, CblasNonUnit, n, r, ldr, wa1, 1); +# else + for (j = 0; j < n; ++j) { + real sum = 0.; + if (j >= 1) { + int i; + for (i = 0; i < j; ++i) { + sum += r[i + j * ldr] * wa1[i]; + } + } + wa1[j] = (wa1[j] - sum) / r[j + j * ldr]; + } +# endif + temp = __cminpack_enorm__(n, wa1); + parl = fp / delta / temp / temp; + } + +/* calculate an upper bound, paru, for the zero of the function. */ + + for (j = 0; j < n; ++j) { + real sum; +# ifdef USE_CBLAS + sum = cblas_ddot(j+1, &r[j*ldr], 1, qtb, 1); +# else + int i; + sum = 0.; + for (i = 0; i <= j; ++i) { + sum += r[i + j * ldr] * qtb[i]; + } +# endif + l = ipvt[j]-1; + wa1[j] = sum / diag[l]; + } + gnorm = __cminpack_enorm__(n, wa1); + paru = gnorm / delta; + if (paru == 0.) { + paru = dwarf / min(delta,(real)p1) /* / p001 ??? */; + } + +/* if the input par lies outside of the interval (parl,paru), */ +/* set par to the closer endpoint. */ + + *par = max(*par,parl); + *par = min(*par,paru); + if (*par == 0.) { + *par = gnorm / dxnorm; + } + +/* beginning of an iteration. */ + + for (;;) { + ++iter; + +/* evaluate the function at the current value of par. */ + + if (*par == 0.) { + /* Computing MAX */ + d1 = dwarf, d2 = p001 * paru; + *par = max(d1,d2); + } + temp = sqrt(*par); + for (j = 0; j < n; ++j) { + wa1[j] = temp * diag[j]; + } + __cminpack_func__(qrsolv)(n, r, ldr, ipvt, wa1, qtb, x, sdiag, wa2); + for (j = 0; j < n; ++j) { + wa2[j] = diag[j] * x[j]; + } + dxnorm = __cminpack_enorm__(n, wa2); + temp = fp; + fp = dxnorm - delta; + +/* if the function is small enough, accept the current value */ +/* of par. also test for the exceptional cases where parl */ +/* is zero or the number of iterations has reached 10. */ + + if (fabs(fp) <= p1 * delta || (parl == 0. && fp <= temp && temp < 0.) || iter == 10) { + goto TERMINATE; + } + +/* compute the newton correction. */ + +# ifdef USE_CBLAS + for (j = 0; j < nsing; ++j) { + l = ipvt[j]-1; + wa1[j] = diag[l] * (wa2[l] / dxnorm); + } + for (j = nsing; j < n; ++j) { + wa1[j] = 0.; + } + /* exchange the diagonal of r with sdiag */ + cblas_dswap(n, r, ldr+1, sdiag, 1); + /* solve lower(r).x = wa1, result id put in wa1 */ + cblas_dtrsv(CblasColMajor, CblasLower, CblasNoTrans, CblasNonUnit, nsing, r, ldr, wa1, 1); + /* exchange the diagonal of r with sdiag */ + cblas_dswap( n, r, ldr+1, sdiag, 1); +# else /* !USE_CBLAS */ + for (j = 0; j < n; ++j) { + l = ipvt[j]-1; + wa1[j] = diag[l] * (wa2[l] / dxnorm); + } + for (j = 0; j < n; ++j) { + wa1[j] /= sdiag[j]; + temp = wa1[j]; + if (n > j+1) { + int i; + for (i = j+1; i < n; ++i) { + wa1[i] -= r[i + j * ldr] * temp; + } + } + } +# endif /* !USE_CBLAS */ + temp = __cminpack_enorm__(n, wa1); + parc = fp / delta / temp / temp; + +/* depending on the sign of the function, update parl or paru. */ + + if (fp > 0.) { + parl = max(parl,*par); + } + if (fp < 0.) { + paru = min(paru,*par); + } + +/* compute an improved estimate for par. */ + + /* Computing MAX */ + d1 = parl, d2 = *par + parc; + *par = max(d1,d2); + +/* end of an iteration. */ + + } +TERMINATE: + +/* termination. */ + + if (iter == 0) { + *par = 0.; + } + +/* last card of subroutine lmpar. */ + +} /* lmpar_ */ + diff --git a/cminpack/qrfac.c b/cminpack/qrfac.c new file mode 100644 index 0000000..1573772 --- /dev/null +++ b/cminpack/qrfac.c @@ -0,0 +1,285 @@ +#include "cminpack.h" +#include +#ifdef USE_LAPACK +#include +#include +#include +#endif +#include "cminpackP.h" + +__cminpack_attr__ +void __cminpack_func__(qrfac)(int m, int n, real *a, int + lda, int pivot, int *ipvt, int lipvt, real *rdiag, + real *acnorm, real *wa) +{ +#ifdef USE_LAPACK + __CLPK_integer m_ = m; + __CLPK_integer n_ = n; + __CLPK_integer lda_ = lda; + __CLPK_integer *jpvt; + + int i, j, k; + double t; + double* tau = wa; + const __CLPK_integer ltau = m > n ? n : m; + __CLPK_integer lwork = -1; + __CLPK_integer info = 0; + double* work; + + if (pivot) { + assert( lipvt >= n ); + if (sizeof(__CLPK_integer) != sizeof(ipvt[0])) { + jpvt = malloc(n*sizeof(__CLPK_integer)); + } else { + /* __CLPK_integer is actually an int, just do a cast */ + jpvt = (__CLPK_integer *)ipvt; + } + /* set all columns free */ + memset(jpvt, 0, sizeof(int)*n); + } + + /* query optimal size of work */ + lwork = -1; + if (pivot) { + dgeqp3_(&m_,&n_,a,&lda_,jpvt,tau,tau,&lwork,&info); + lwork = (int)tau[0]; + assert( lwork >= 3*n+1 ); + } else { + dgeqrf_(&m_,&n_,a,&lda_,tau,tau,&lwork,&info); + lwork = (int)tau[0]; + assert( lwork >= 1 && lwork >= n ); + } + + assert( info == 0 ); + + /* alloc work area */ + work = (double *)malloc(sizeof(double)*lwork); + assert(work != NULL); + + /* set acnorm first (from the doc of qrfac, acnorm may point to the same area as rdiag) */ + if (acnorm != rdiag) { + for (j = 0; j < n; ++j) { + acnorm[j] = __cminpack_enorm__(m, &a[j * lda]); + } + } + + /* QR decomposition */ + if (pivot) { + dgeqp3_(&m_,&n_,a,&lda_,jpvt,tau,work,&lwork,&info); + } else { + dgeqrf_(&m_,&n_,a,&lda_,tau,work,&lwork,&info); + } + assert(info == 0); + + /* set rdiag, before the diagonal is replaced */ + memset(rdiag, 0, sizeof(double)*n); + for(i=0 ; i rdiag[kmax]) { + kmax = k; + } + } + if (kmax != j) { + for (i = 0; i < m; ++i) { + temp = a[i + j * lda]; + a[i + j * lda] = a[i + kmax * lda]; + a[i + kmax * lda] = temp; + } + rdiag[kmax] = rdiag[j]; + wa[kmax] = wa[j]; + k = ipvt[j]; + ipvt[j] = ipvt[kmax]; + ipvt[kmax] = k; + } + } + +/* compute the householder transformation to reduce the */ +/* j-th column of a to a multiple of the j-th unit vector. */ + + ajnorm = __cminpack_enorm__(m - (j+1) + 1, &a[j + j * lda]); + if (ajnorm != 0.) { + if (a[j + j * lda] < 0.) { + ajnorm = -ajnorm; + } + for (i = j; i < m; ++i) { + a[i + j * lda] /= ajnorm; + } + a[j + j * lda] += 1.; + +/* apply the transformation to the remaining columns */ +/* and update the norms. */ + + jp1 = j + 1; + if (n > jp1) { + for (k = jp1; k < n; ++k) { + sum = 0.; + for (i = j; i < m; ++i) { + sum += a[i + j * lda] * a[i + k * lda]; + } + temp = sum / a[j + j * lda]; + for (i = j; i < m; ++i) { + a[i + k * lda] -= temp * a[i + j * lda]; + } + if (pivot && rdiag[k] != 0.) { + temp = a[j + k * lda] / rdiag[k]; + /* Computing MAX */ + d1 = 1. - temp * temp; + rdiag[k] *= sqrt((max((real)0.,d1))); + /* Computing 2nd power */ + d1 = rdiag[k] / wa[k]; + if (p05 * (d1 * d1) <= epsmch) { + rdiag[k] = __cminpack_enorm__(m - (j+1), &a[jp1 + k * lda]); + wa[k] = rdiag[k]; + } + } + } + } + } + rdiag[j] = -ajnorm; + } + +/* last card of subroutine qrfac. */ +#endif /* !USE_LAPACK */ +} /* qrfac_ */ + diff --git a/cminpack/qrsolv.c b/cminpack/qrsolv.c new file mode 100644 index 0000000..6ab9e98 --- /dev/null +++ b/cminpack/qrsolv.c @@ -0,0 +1,218 @@ +#include "cminpack.h" +#include +#include "cminpackP.h" + +__cminpack_attr__ +void __cminpack_func__(qrsolv)(int n, real *r, int ldr, + const int *ipvt, const real *diag, const real *qtb, real *x, + real *sdiag, real *wa) +{ + /* Initialized data */ + +#define p5 .5 +#define p25 .25 + + /* Local variables */ + int i, j, k, l; + real cos, sin, sum, temp; + int nsing; + real qtbpj; + +/* ********** */ + +/* subroutine qrsolv */ + +/* given an m by n matrix a, an n by n diagonal matrix d, */ +/* and an m-vector b, the problem is to determine an x which */ +/* solves the system */ + +/* a*x = b , d*x = 0 , */ + +/* in the least squares sense. */ + +/* this subroutine completes the solution of the problem */ +/* if it is provided with the necessary information from the */ +/* qr factorization, with column pivoting, of a. that is, if */ +/* a*p = q*r, where p is a permutation matrix, q has orthogonal */ +/* columns, and r is an upper triangular matrix with diagonal */ +/* elements of nonincreasing magnitude, then qrsolv expects */ +/* the full upper triangle of r, the permutation matrix p, */ +/* and the first n components of (q transpose)*b. the system */ +/* a*x = b, d*x = 0, is then equivalent to */ + +/* t t */ +/* r*z = q *b , p *d*p*z = 0 , */ + +/* where x = p*z. if this system does not have full rank, */ +/* then a least squares solution is obtained. on output qrsolv */ +/* also provides an upper triangular matrix s such that */ + +/* t t t */ +/* p *(a *a + d*d)*p = s *s . */ + +/* s is computed within qrsolv and may be of separate interest. */ + +/* the subroutine statement is */ + +/* subroutine qrsolv(n,r,ldr,ipvt,diag,qtb,x,sdiag,wa) */ + +/* where */ + +/* n is a positive integer input variable set to the order of r. */ + +/* r is an n by n array. on input the full upper triangle */ +/* must contain the full upper triangle of the matrix r. */ +/* on output the full upper triangle is unaltered, and the */ +/* strict lower triangle contains the strict upper triangle */ +/* (transposed) of the upper triangular matrix s. */ + +/* ldr is a positive integer input variable not less than n */ +/* which specifies the leading dimension of the array r. */ + +/* ipvt is an integer input array of length n which defines the */ +/* permutation matrix p such that a*p = q*r. column j of p */ +/* is column ipvt(j) of the identity matrix. */ + +/* diag is an input array of length n which must contain the */ +/* diagonal elements of the matrix d. */ + +/* qtb is an input array of length n which must contain the first */ +/* n elements of the vector (q transpose)*b. */ + +/* x is an output array of length n which contains the least */ +/* squares solution of the system a*x = b, d*x = 0. */ + +/* sdiag is an output array of length n which contains the */ +/* diagonal elements of the upper triangular matrix s. */ + +/* wa is a work array of length n. */ + +/* subprograms called */ + +/* fortran-supplied ... dabs,dsqrt */ + +/* argonne national laboratory. minpack project. march 1980. */ +/* burton s. garbow, kenneth e. hillstrom, jorge j. more */ + +/* ********** */ + +/* copy r and (q transpose)*b to preserve input and initialize s. */ +/* in particular, save the diagonal elements of r in x. */ + + for (j = 0; j < n; ++j) { + for (i = j; i < n; ++i) { + r[i + j * ldr] = r[j + i * ldr]; + } + x[j] = r[j + j * ldr]; + wa[j] = qtb[j]; + } + +/* eliminate the diagonal matrix d using a givens rotation. */ + + for (j = 0; j < n; ++j) { + +/* prepare the row of d to be eliminated, locating the */ +/* diagonal element using p from the qr factorization. */ + + l = ipvt[j]-1; + if (diag[l] != 0.) { + for (k = j; k < n; ++k) { + sdiag[k] = 0.; + } + sdiag[j] = diag[l]; + +/* the transformations to eliminate the row of d */ +/* modify only a single element of (q transpose)*b */ +/* beyond the first n, which is initially zero. */ + + qtbpj = 0.; + for (k = j; k < n; ++k) { + +/* determine a givens rotation which eliminates the */ +/* appropriate element in the current row of d. */ + + if (sdiag[k] != 0.) { +# ifdef USE_LAPACK + dlartg_( &r[k + k * ldr], &sdiag[k], &cos, &sin, &temp ); +# else /* !USE_LAPACK */ + if (fabs(r[k + k * ldr]) < fabs(sdiag[k])) { + real cotan; + cotan = r[k + k * ldr] / sdiag[k]; + sin = p5 / sqrt(p25 + p25 * (cotan * cotan)); + cos = sin * cotan; + } else { + real tan; + tan = sdiag[k] / r[k + k * ldr]; + cos = p5 / sqrt(p25 + p25 * (tan * tan)); + sin = cos * tan; + } + +/* compute the modified diagonal element of r and */ +/* the modified element of ((q transpose)*b,0). */ + +# endif /* !USE_LAPACK */ + temp = cos * wa[k] + sin * qtbpj; + qtbpj = -sin * wa[k] + cos * qtbpj; + wa[k] = temp; + +/* accumulate the tranformation in the row of s. */ +# ifdef USE_CBLAS + cblas_drot( n-k, &r[k + k * ldr], 1, &sdiag[k], 1, cos, sin ); +# else /* !USE_CBLAS */ + r[k + k * ldr] = cos * r[k + k * ldr] + sin * sdiag[k]; + if (n > k+1) { + for (i = k+1; i < n; ++i) { + temp = cos * r[i + k * ldr] + sin * sdiag[i]; + sdiag[i] = -sin * r[i + k * ldr] + cos * sdiag[i]; + r[i + k * ldr] = temp; + } + } +# endif /* !USE_CBLAS */ + } + } + } + +/* store the diagonal element of s and restore */ +/* the corresponding diagonal element of r. */ + + sdiag[j] = r[j + j * ldr]; + r[j + j * ldr] = x[j]; + } + +/* solve the triangular system for z. if the system is */ +/* singular, then obtain a least squares solution. */ + + nsing = n; + for (j = 0; j < n; ++j) { + if (sdiag[j] == 0. && nsing == n) { + nsing = j; + } + if (nsing < n) { + wa[j] = 0.; + } + } + if (nsing >= 1) { + for (k = 1; k <= nsing; ++k) { + j = nsing - k; + sum = 0.; + if (nsing > j+1) { + for (i = j+1; i < nsing; ++i) { + sum += r[i + j * ldr] * wa[i]; + } + } + wa[j] = (wa[j] - sum) / sdiag[j]; + } + } + +/* permute the components of z back to components of x. */ + + for (j = 0; j < n; ++j) { + l = ipvt[j]-1; + x[l] = wa[j]; + } + return; + +/* last card of subroutine qrsolv. */ + +} /* qrsolv_ */ + diff --git a/cmpframe.c b/cmpframe.c new file mode 100644 index 0000000..09b5f73 --- /dev/null +++ b/cmpframe.c @@ -0,0 +1,10654 @@ +/* +*class++ +* Name: +* CmpFrame + +* Purpose: +* Compound Frame. + +* Constructor Function: +c astCmpFrame +f AST_CMPFRAME + +* Description: +* A CmpFrame is a compound Frame which allows two component Frames +* (of any class) to be merged together to form a more complex +* Frame. The axes of the two component Frames then appear together +* in the resulting CmpFrame (those of the first Frame, followed by +* those of the second Frame). +* +* Since a CmpFrame is itself a Frame, it can be used as a +* component in forming further CmpFrames. Frames of arbitrary +* complexity may be built from simple individual Frames in this +* way. +* +* Also since a Frame is a Mapping, a CmpFrame can also be used as a +* Mapping. Normally, a CmpFrame is simply equivalent to a UnitMap, +* but if either of the component Frames within a CmpFrame is a Region +* (a sub-class of Frame), then the CmpFrame will use the Region as a +* Mapping when transforming values for axes described by the Region. +* Thus input axis values corresponding to positions which are outside the +* Region will result in bad output axis values. + +* Inheritance: +* The CmpFrame class inherits from the Frame class. + +* Attributes: +* The CmpFrame class does not define any new attributes beyond +* those which are applicable to all Frames. However, the attributes +* of the component Frames can be accessed as if they were attributes +* of the CmpFrame. For instance, if a CmpFrame contains a SpecFrame +* and a SkyFrame, then the CmpFrame will recognise the "Equinox" +* attribute and forward access requests to the component SkyFrame. +* Likewise, it will recognise the "RestFreq" attribute and forward +* access requests to the component SpecFrame. An axis index can +* optionally be appended to the end of any attribute name, in which +* case the request to access the attribute will be forwarded to the +* primary Frame defining the specified axis. + +* Functions: +c The CmpFrame class does not define any new functions beyond those +f The CmpFrame class does not define any new routines beyond those +* which are applicable to all Frames. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 4-MAR-1996 (RFWS): +* Original version. +* 27-FEB-1997 (RFWS): +* Improved public prologues. +* 25-FEB-1998 (RFWS): +* Over-ride the astUnformat method. +* 6-APR-1998 (RFWS): +* Fixed bug in returned value of GenAxisSelection. +* 22-SEP-1998 (RFWS): +* Fixed bug in Match function - was not checking Domain values +* for equality. +* 11-JUN-1999 (RFWS): +* Fixed bug in GenAxisSelection- some selections were being omitted. +* 5-FEB-2001 (DSB): +* Ensure that Title and Domain values appropriate to a CmpFrame +* are preserved if a CmpFrame result is generated by SubFrame. +* 27-FEB-2001 (DSB): +* Modified Match so that all the frames have some axes in order to +* match. Otherwise, null pointers are created (for zero axes), +* resulting in a seg vio. +* 21-JUN-2001 (DSB): +* Added astAngle. +* 7-SEP-2001 (DSB): +* Added astResolve. +* 26-SEP-2001 (DSB): +* Over-ride the astDecompose method. +* 20-DEC-2002 (DSB): +* Allows any attribute of a component frame to be accessed as though +* it were an attribute of the CmpFrame by including an axis index in +* the attribute name (e.g. "System(3)"). +* 8-JAN-2003 (DSB): +* - Changed private InitVtab method to protected astInitCmpFrameVtab +* method. +* - Override astGetAttrib, astClearAttrib, astTestAttrib, +* astSetAttrib to allow attributes to be set for individual +* axes. +* - Override astGetEpoch astGetSystem, astGetAlignSystem. +* astValidateSystem, astSystemString, astSystemCode. +* 27-FEB-2003 (DSB): +* - Modify the default Domain name for a CmpFrame to be the +* domains of the two subFrames separated by a "-". +* 24-JAN-2004 (DSB): +* o Override the astFields method. +* o Added argument "fmt" to Abbrev. +* 24-MAR-2004 (DSB): +* Over-ride the astSimplify and astTransform methods. +* 8-SEP-2004 (DSB): +* Over-ride astResolvePoints method. +* 21-JAN-2005 (DSB): +* Over-ride the astGetActiveUnit and astSetActiveUnit methods. +* 23-FEB-2005 (DSB): +* Modify GetDomain to avoid over-writing the static "buff" array +* if called recursively. +* 29-MAR-2005 (DSB): +* Override astSetEpoch and astClearEpoch by implementations which +* propagate the changed epoch value to the component Frames. +* 5-APR-2005 (DSB): +* Correct error checking in Clear/Get/Set/TestAttrib. +* 12-MAY-2005 (DSB): +* Override astNormBox method. +* 12-AUG-2005 (DSB): +* Override astSetObsLat/Lon and astClearObslat/Lon by implementations +* which propagate the changed value to the component Frames. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 3-APR-2006 (DSB): +* Modify Match so that an attempt is made to align the target with +* each of the two component Frames if the target cannot be matched +* with the CmpFrame as a whole. +* 3-MAY-2006 (DSB): +* Fix bug in Match that could cause segvio when matching a target +* against the second component of a CmpFrame. +* 31-OCT-2006 (DSB): +* Over-ride the SetFrameFlags method. +* 1-NOV-2005 (DSB): +* Override astSetDut1, astGetDut1 and astClearDut1. +* 15-MAR-2007 (DSB): +* Override astClearAlignSystem by an implementation that clears +* AlignSystem in the component Frames. +* 7-FEB-2008 (DSB): +* Allow the MaxAxes and MinAxes attributes to be specified for a +* CmpFrame (rather than just being the sum of the attribute values +* in the component frames). This enables, for instance, a (detector +* index,mjd) frame to match with a ((velocity,detector index),mjd) +* frame. +* 5-MAY-2009 (DSB): +* In GetAttrib, if an index is included in the attribute name, attempt +* to use the GetAttrib method of the primary frame before using the +* parent GetAttrib method. This is because the Frame getattrib +* method will dissociate axes from their parent class. Thus, a +* SkyAxis attribute such as AsTime will come out wrong since its +* value is managed by the SkyFrame class rather than the SkyAxis +* class. +* 18-JUN-2009 (DSB): +* Override astSetObsAlt and astClearObsAlt. +* 29-SEP-2009 (DSB): +* Ensure the astMatch method provided by this class honours the +* PreserveAxes, MaxAxes and MinAxes attribute settings. +* 22-MAR-2011 (DSB): +* Override astFrameGrid method. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +* 10-FEB-2015 (DSB): +* When checking attribute settings for attribute names that end with +* an axis index, stop looking for the axis index when the first equals +* sign is encountered. +* 26-MAR-2015 (DSB): +* Increase size of "buf2" buffer in SetAttrib, and trap buffer overflow. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS CmpFrame + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__COMP +#define LAST_SYSTEM AST__COMP + +/* Define macros to implement member functions for accessing axis + attributes. */ +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a function to clear an attribute value for a CmpFrame axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "cmpframe.h" +* MAKE_CLEAR(attribute) + +* Class Membership: +* Defined by the CmpFrame class. + +* Description: +* This macro expands to an implementation of a private member +* function of the form: +* +* static void Clear( AstFrame *this, int axis ) +* +* which clears an attribute value for a specified axis of a CmpFrame. + +* Parameters: +* attribute +* The name of the attribute to be cleared, as it appears in the +* function name (e.g. Label in "ClearLabel"). + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* void astClear( AstFrame *this, int axis ) +* +* which clears the required attribute for a Frame object. +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute) \ +static void Clear##attribute( AstFrame *this_frame, int axis, int *status ) { \ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ \ + int naxes1; /* Number of axes in frame1 */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the CmpFrame structure. */ \ + this = (AstCmpFrame *) this_frame; \ +\ +/* Validate and alidateAxispermute the axis index supplied. */ \ + axis = astValidateAxis( this, axis, 1, "astSet" #attribute ); \ +\ +/* Determine the number of axes in the first component Frame. */ \ + naxes1 = astGetNaxes( this->frame1 ); \ + if ( astOK ) { \ +\ +/* Decide which Frame contains the axis and invoke its astClear... method to \ + clear the attribute value. */ \ + if ( axis < naxes1 ) { \ + astClear##attribute( this->frame1, axis ); \ + } else { \ + astClear##attribute( this->frame2, axis - naxes1 ); \ + } \ + } \ +} + +/* +* Name: +* MAKE_GET + +* Purpose: +* Implement a function to get an attribute value for a CmpFrame axis. + +* Type: +* Private macro. + +* Synopsis: +# #include "cmpframe.h" +* MAKE_GET(attribute,type,bad_value,default,assign_default) + +* Class Membership: +* Defined by the CmpFrame class. + +* Description: +* This macro expands to an implementation of a private member +* function of the form: +* +* static Get( AstFrame *this, int axis ) +* +* which gets an attribute value for a specified axis of a +* CmpFrame. + +* Parameters: +* attribute +* The name of the attribute whose value is to be obtained, as +* it appears in the function name (e.g. Label in "GetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, +* or if the function fails. +* default +* A boolean (int) value that indicates whether a new default +* value should be returned if the requested attribute has not +* been set for the appropriate axis of the appropriate +* component Frame. If this value is zero, the component Frame's +* default (for the appropriate axis) will be used instead. +* assign_default +* An expression that evaluates to the new default value to be +* assigned. This value is ignored if "default" is zero, but a +* valid (e.g. constant) value should nevertheless be supplied. + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* astGet( AstFrame *this, int axis ) +* +* which gets the required attribute for a Frame object. +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_GET(attribute,type,bad_value,default,assign_default) \ +static type Get##attribute( AstFrame *this_frame, int axis, int *status ) { \ + astDECLARE_GLOBALS /* Declare the thread specific global data */ \ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ \ + AstFrame *frame; /* Pointer to Frame containing axis */\ + int axis_p; /* Permuted axis index */ \ + int naxes1; /* Number of axes in frame1 */ \ + int set; /* Digits attribute set? */ \ + type result; /* Result value to return */ \ + \ +/* Initialise. */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Get a pointer to the structure holding thread-specific global data. */ \ + astGET_GLOBALS(this_frame); \ +\ +/* Obtain a pointer to the CmpFrame structure. */ \ + this = (AstCmpFrame *) this_frame; \ +\ +/* Validate and permute the axis index supplied. */ \ + axis_p = astValidateAxis( this, axis, 1, "astGet" #attribute ); \ +\ +/* Determine the number of axes in the first component Frame. */ \ + naxes1 = astGetNaxes( this->frame1 ); \ + if ( astOK ) { \ +\ +/* Decide which Frame contains the axis and adjust the axis index if \ + necessary. */ \ + frame = ( axis_p < naxes1 ) ? this->frame1 : this->frame2; \ + axis_p = ( axis_p < naxes1 ) ? axis_p : axis_p - naxes1; \ +\ +/* Since the component Frame is "managed" by the enclosing CmpFrame, we next \ + test if any Frame attributes which may affect the result are undefined \ + (i.e. have not been explicitly set). If so, we over-ride them, giving \ + them temporary values dictated by the CmpFrame. Only the Digits attribute \ + is relevant here. */ \ + set = astTestDigits( frame ); \ + if ( !set ) astSetDigits( frame, astGetDigits( this ) ); \ +\ +/* If the default value is to be over-ridden, test if the Frame's axis \ + attribute has been set. Then, if required, obtain the attribute value from \ + the Frame. */ \ + if ( !(default) || astTest##attribute( frame, axis_p ) ) { \ + result = astGet##attribute( frame, axis_p ); \ +\ +/* If required, assign the new default value. */ \ + } else { \ + result = (assign_default); \ + } \ +\ +/* Clear Frame attributes which were temporarily over-ridden. */ \ + if ( !set ) astClearDigits( frame ); \ + } \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Implement a function to set an attribute value for a CmpFrame axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "cmpframe.h" +* MAKE_SET(attribute,type) + +* Class Membership: +* Defined by the CmpFrame class. + +* Description: +* This macro expands to an implementation of a private member +* function of the form: +* +* static void Set( AstFrame *this, int axis, value ) +* +* which sets an attribute value for a specified axis of a CmpFrame. + +* Parameters: +* attribute +* The name of the attribute to be set, as it appears in the +* function name (e.g. Label in "SetLabel"). +* type +* The C type of the attribute. + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* void astSet( AstFrame *this, int axis, value ) +* +* which sets the required attribute for a Frame object. +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,type) \ +static void Set##attribute( AstFrame *this_frame, int axis, type value, int *status ) { \ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ \ + int naxes1; /* Number of axes in frame1 */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the CmpFrame structure. */ \ + this = (AstCmpFrame *) this_frame; \ +\ +/* Validate and permute the axis index supplied. */ \ + axis = astValidateAxis( this, axis, 1, "astSet" #attribute ); \ +\ +/* Determine the number of axes in the first component Frame. */ \ + naxes1 = astGetNaxes( this->frame1 ); \ + if ( astOK ) { \ +\ +/* Decide which Frame contains the axis and invoke its astSet... method to \ + set the attribute value. */ \ + if ( axis < naxes1 ) { \ + astSet##attribute( this->frame1, axis, value ); \ + } else { \ + astSet##attribute( this->frame2, axis - naxes1, value ); \ + } \ + } \ +} + +/* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a function to test if an attribute is set for a CmpFrame axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "cmpframe.h" +* MAKE_TEST(attribute) + +* Class Membership: +* Defined by the CmpFrame class. + +* Description: +* This macro expands to an implementation of a private member +* function of the form: +* +* static int Test( AstFrame *this, int axis ) +* +* which tests whether an attribute value is set for a specified +* axis of a CmpFrame. + +* Parameters: +* attribute +* The name of the attribute to be tested, as it appears in the +* function name (e.g. Label in "TestLabel"). + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* int astTest( AstFrame *this, int axis ) +* +* which tests the required attribute for a Frame object. +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_TEST(attribute) \ +static int Test##attribute( AstFrame *this_frame, int axis, int *status ) { \ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ \ + int naxes1; /* Number of axes in frame1 */ \ + int result; /* Result value to return */ \ +\ +/* Initialise. */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Obtain a pointer to the CmpFrame structure. */ \ + this = (AstCmpFrame *) this_frame; \ +\ +/* Validate and permute the axis index supplied. */ \ + axis = astValidateAxis( this, axis, 1, "astSet" #attribute ); \ +\ +/* Determine the number of axes in the first component Frame. */ \ + naxes1 = astGetNaxes( this->frame1 ); \ + if ( astOK ) { \ +\ +/* Decide which Frame contains the axis and invoke its astTest... method to \ + test the attribute. */ \ + if ( axis < naxes1 ) { \ + result = astTest##attribute( this->frame1, axis ); \ + } else { \ + result = astTest##attribute( this->frame2, axis - naxes1 ); \ + } \ + } \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "pointset.h" /* Sets of points */ +#include "object.h" /* Base Object class */ +#include "mapping.h" /* Coordinate Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Coordinate permutation Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "axis.h" /* Coordinate axes */ +#include "frame.h" /* Parent Frame class */ +#include "cmpframe.h" /* Interface definition for this class */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstSystemType (* parent_getalignsystem)( AstFrame *, int * ); +static AstSystemType (* parent_getsystem)( AstFrame *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getdomain)( AstFrame *, int * ); +static const char *(* parent_gettitle)( AstFrame *, int * ); +static double (* parent_angle)( AstFrame *, const double[], const double[], const double[], int * ); +static double (* parent_getdut1)( AstFrame *, int * ); +static double (* parent_getepoch)( AstFrame *, int * ); +static double (* parent_getobsalt)( AstFrame *, int * ); +static double (* parent_getobslat)( AstFrame *, int * ); +static double (* parent_getobslon)( AstFrame *, int * ); +static int (* parent_getactiveunit)( AstFrame *, int * ); +static int (* parent_getmaxaxes)( AstFrame *, int * ); +static int (* parent_getminaxes)( AstFrame *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_getusedefs)( AstObject *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearalignsystem)( AstFrame *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_cleardut1)( AstFrame *, int * ); +static void (* parent_clearepoch)( AstFrame *, int * ); +static void (* parent_clearobsalt)( AstFrame *, int * ); +static void (* parent_clearobslat)( AstFrame *, int * ); +static void (* parent_clearobslon)( AstFrame *, int * ); +static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * ); +static void (* parent_setactiveunit)( AstFrame *, int, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_setdut1)( AstFrame *, double, int * ); +static void (* parent_setepoch)( AstFrame *, double, int * ); +static void (* parent_setframeflags)( AstFrame *, int, int * ); +static void (* parent_setobsalt)( AstFrame *, double, int * ); +static void (* parent_setobslat)( AstFrame *, double, int * ); +static void (* parent_setobslon)( AstFrame *, double, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->Label_Buff[ 0 ] = 0; \ + globals->Symbol_Buff[ 0 ] = 0; \ + globals->GetDomain_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(CmpFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(CmpFrame,Class_Init) +#define class_vtab astGLOBAL(CmpFrame,Class_Vtab) +#define getdomain_buff astGLOBAL(CmpFrame,GetDomain_Buff) +#define gettitle_buff astGLOBAL(CmpFrame,GetTitle_Buff) +#define label_buff astGLOBAL(CmpFrame,Label_Buff) +#define symbol_buff astGLOBAL(CmpFrame,Symbol_Buff) +#define qsort_axes astGLOBAL(CmpFrame,qsort_axes) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Pointer to axis index array accessed by "qsort". */ +static int *qsort_axes; + +/* Default Label string buffer */ +static char label_buff[ 101 ]; + +/* Default Symbol buffer */ +static char symbol_buff[ 51 ]; + +/* Buffer for returned domain name in GetDomain */ +static char getdomain_buff[ 101 ]; + +/* Buffer for returned title in GetTitle */ +static char gettitle_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstCmpFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstCmpFrame *astCmpFrameId_( void *, void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstAxis *GetAxis( AstFrame *, int, int * ); +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstObject *Cast( AstObject *, AstObject *, int * ); +static AstPointSet *FrameGrid( AstFrame *, int, const double *, const double *, int * ); +static AstPointSet *ResolvePoints( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static AstSystemType GetSystem( AstFrame *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static const char *Abbrev( AstFrame *, int, const char *, const char *, const char *, int * ); +static const char *Format( AstFrame *, int, double, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetFormat( AstFrame *, int, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static const int *GetPerm( AstFrame *, int * ); +static double Angle( AstFrame *, const double[], const double[], const double[], int * ); +static double Distance( AstFrame *, const double[], const double[], int * ); +static double Centre( AstFrame *, int, double, double, int * ); +static double Gap( AstFrame *, int, double, int *, int * ); +static int ComponentMatch( AstCmpFrame *, AstFrame *, int, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int Fields( AstFrame *, int, const char *, const char *, int, char **, int *, double *, int * ); +static int GenAxisSelection( int, int, int [], int * ); +static int GetActiveUnit( AstFrame *, int * ); +static int GetDirection( AstFrame *, int, int * ); +static int GetMaxAxes( AstFrame *, int * ); +static int GetMinAxes( AstFrame *, int * ); +static int GetNaxes( AstFrame *, int * ); +static int GetObjSize( AstObject *, int * ); +static int GetUseDefs( AstObject *, int * ); +static int GoodPerm( int, const int [], int, const int [], int * ); +static int IsUnitFrame( AstFrame *, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int PartMatch( AstCmpFrame *, AstFrame *, int, int, const int [], int, const int [], int **, int **, AstMapping **, AstFrame **, int * ); +static int QsortCmpAxes( const void *, const void * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestDirection( AstFrame *, int, int * ); +static int TestFormat( AstFrame *, int, int * ); +static int TestLabel( AstFrame *, int, int * ); +static int TestSymbol( AstFrame *, int, int * ); +static int TestUnit( AstFrame *, int, int * ); +static int Unformat( AstFrame *, int, const char *, double *, int * ); +static void AddExtraAxes( int, int [], int, int, int, int * ); +static void ClearDirection( AstFrame *, int, int * ); +static void ClearFormat( AstFrame *, int, int * ); +static void ClearLabel( AstFrame *, int, int * ); +static void ClearSymbol( AstFrame *, int, int * ); +static void ClearUnit( AstFrame *, int, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void MatchAxesX( AstFrame *, AstFrame *, int *, int * ); +static void Norm( AstFrame *, double [], int * ); +static void NormBox( AstFrame *, double[], double[], AstMapping *, int * ); +static void Offset( AstFrame *, const double [], const double [], double, double [], int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void PartitionSelection( int, const int [], const int [], int, int, int [], int, int * ); +static void PermAxes( AstFrame *, const int[], int * ); +static void PrimaryFrame( AstFrame *, int, AstFrame **, int *, int * ); +static void RenumberAxes( int, int [], int * ); +static void Resolve( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); +static void SetActiveUnit( AstFrame *, int, int * ); +static void SetAxis( AstFrame *, int, AstAxis *, int * ); +static void SetDirection( AstFrame *, int, int, int * ); +static void SetFormat( AstFrame *, int, const char *, int * ); +static void SetFrameFlags( AstFrame *, int, int * ); +static void SetLabel( AstFrame *, int, const char *, int * ); +static void SetSymbol( AstFrame *, int, const char *, int * ); +static void SetUnit( AstFrame *, int, const char *, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static double GetEpoch( AstFrame *, int * ); +static void ClearEpoch( AstFrame *, int * ); +static void SetEpoch( AstFrame *, double, int * ); + +static double GetDut1( AstFrame *, int * ); +static void ClearDut1( AstFrame *, int * ); +static void SetDut1( AstFrame *, double, int * ); + +static double GetObsLon( AstFrame *, int * ); +static void ClearObsLon( AstFrame *, int * ); +static void SetObsLon( AstFrame *, double, int * ); + +static double GetObsLat( AstFrame *, int * ); +static void ClearObsLat( AstFrame *, int * ); +static void SetObsLat( AstFrame *, double, int * ); + +static double GetObsAlt( AstFrame *, int * ); +static void ClearObsAlt( AstFrame *, int * ); +static void SetObsAlt( AstFrame *, double, int * ); + +static void ClearAlignSystem( AstFrame *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static const char *Abbrev( AstFrame *this_frame, int axis, const char *fmt, + const char *str1, const char *str2, int *status ) { +/* +* Name: +* Abbrev + +* Purpose: +* Abbreviate a formatted CmpFrame axis value by skipping leading fields. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* const char *Abbrev( AstFrame *this, int axis, const char *fmt, +* const char *str1, const char *str2, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astAbbrev +* method inherited from the Frame class). + +* Description: +* This function compares two CmpFrame axis values that have been +* formatted (using astFormat) and determines if they have any +* redundant leading fields (i.e. leading fields in common which +* can be suppressed when tabulating the values or plotting them on +* the axis of a graph). + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis +* The number of the CmpFrame axis for which the values have +* been formatted (axis numbering starts at zero for the first +* axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format specification used to format the two values. +* str1 +* Pointer to a constant null-terminated string containing the +* first formatted value. If this is null, the returned pointer +* points to the start of the final field in str2. +* str2 +* Pointer to a constant null-terminated string containing the +* second formatted value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer into the "str2" string which locates the first +* character in the first field that differs between the two +* formatted values. +* +* If the two values have no leading fields in common, the returned +* value will point at the start of string "str2". If the two +* values are equal, it will point at the terminating null at the +* end of this string. + +* Notes: +* - This function assumes that the format specification used was +* the same when both values were formatted and that they both +* apply to the same CmpFrame axis. +* - A pointer to the start of "str2" will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstFrame *frame; /* Pointer to Frame containing axis */ + const char *result; /* Pointer value to return */ + int naxes1; /* Number of axes in frame1 */ + int set; /* Digits attribute set? */ + +/* Initialise. */ + result = str2; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astAbbrev" ); + +/* Determine the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which component Frame contains the axis and adjust the axis + index if necessary. */ + frame = ( axis < naxes1 ) ? this->frame1 : this->frame2; + axis = ( axis < naxes1 ) ? axis : axis - naxes1; + +/* Since the component Frame is "managed" by the enclosing CmpFrame, + we next test if any Frame attributes which may affect the result + are undefined (i.e. have not been explicitly set). If so, we + over-ride them, giving them temporary values dictated by the + CmpFrame. Only the Digits attribute is relevant here. */ + set = astTestDigits( frame ); + if ( !set ) astSetDigits( frame, astGetDigits( this ) ); + +/* Invoke the Frame's astAbbrev method to perform the processing. */ + result = astAbbrev( frame, axis, fmt, str1, str2 ); + +/* Clear Frame attributes which were temporarily over-ridden. */ + if ( !set ) astClearDigits( frame ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = str2; + +/* Return the result. */ + return result; +} + +static void AddExtraAxes( int naxes, int axes[], int i1, int i2, + int following, int *status ) { +/* +* Name: +* AddExtraAxes + +* Purpose: +* Add extra axis indices in place of missing ones. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void AddExtraAxes( int naxes, int axes[], int i1, int i2, +* int following, int *status ) + +* Class Membership: +* CmpFrame member function. + +* Description: +* This function takes an array of axis indices that refer to the +* axes of a Frame, and which may have values missing (denoted by +* an index of -1). It replaces each occurrence of -1 by a new axis +* index (and re-numbers the others to avoid duplication) in such a +* way that the new indices introduced are "associated" with +* certain of the pre-existing indices, by virtue of being numbered +* consecutively with them. +* +* The purpose of this operation is to establish the relative +* location of new axes in relation to the pre-existing ones. +* +* Normally, each new axis will be associated with (i.e. numbered +* one more than) the pre-existing index which precedes +* it. However, if the "following" parameter is non-zero, it will +* instead be associated with (numbered one less than) the one +* which follows it. If there is no preceding (or following) +* pre-existing index, the following (or preceding) one is used +* instead. If several adjacent occurrences of -1 must be replaced, +* they are numbered consecutively in their order of occurrence. + +* Parameters: +* naxes +* The number of axis indices in the array. +* axes +* The array containing the axis indices. +* i1 +* Index of the first element of the array to be processed. +* i2 +* Index of the last element of the array to be processed. +* following +* Boolean flag to determine if new indices are associated with +* the preceding index (if zero) or the following index (if +* non-zero). +* status +* Pointer to the inherited status variable. + +* Notes: +* - The values of "i1" and "i2" dictate the range of array +* elements where values of -1 will be replaced, but all array +* elements are candidates for renumbering in order to avoid +* duplicate axis indices. +* - This function aims to establish the location of new axes only +* by means of the relative numerical value of the indices assigned +* to them. It does not constrain the actual indices assigned in +* any further way. +* - Because axis indices are always incremented (never +* decremented) in order to avoid duplicates, where a number of new +* indices have been introduced, the maximum index present in the +* result array may exceed the original maximum. +* - Some axis indices may remain unused (i.e. not present) in the +* result array. +*/ + +/* Local Variables: */ + int end; /* Loop termination value */ + int extra; /* Index to apply to next "extra" axis */ + int found; /* Default value found? */ + int i; /* Main loop counter */ + int inc; /* Loop increment value */ + int j; /* Loop counter for eliminating duplicates */ + int start; /* Loop starting value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise the default index of the next extra axis to add. This + will apply only if there are no valid axis indices from which to + obtain a better default. */ + extra = 0; + +/* Initialise loop parameters so as to scan the axis indices provided + in either the forward or reverse direction, according to the value + of "following". Start with the section of the array being processed, + but continue looking for a default right up to the end of the array + (this prevents the current section being numbered inconsistently + with respect to adjacent ones that may already have been + processed). */ + start = following ? i2 : i1; + end = following ? -1 : naxes; + inc = following ? -1 : 1; + +/* Search for the first (in whichever direction this is) valid axis + index and use it to set a new default index for the next extra axis + to add. If scanning forward, use the valid axis index (causing any + preceding extra axis to displace it upwards). If scanning + backwards, use one more than the valid axis index (causing any + following extra axis to tag on the end). */ + found = 0; + for ( i = start; i != end; i += inc ) { + if ( axes[ i ] != -1 ) { + found = 1; + extra = axes[ i ] + ( following ? 1 : 0 ); + break; + } + } + +/* If no default has yet been found, repeat the above process by + scanning in the opposite direction (by inverting the "following" + value used). Again, this prevents inconsistency with neighbouring + regions. This time a default must be found unless the entire array + is filled with -1's (in which case a default of zero is used). */ + if ( !found ) { + start = !following ? i2 : i1; + end = !following ? -1 : naxes; + inc = !following ? -1 : 1; + for ( i = start; i != end; i += inc ) { + if ( axes[ i ] != -1 ) { + extra = axes[ i ] + ( !following ? 1 : 0 ); + break; + } + } + } + +/* Reset the loop parameters to scan just the region of interest in + the original (correct) direction. */ + start = following ? i2 : i1; + end = following ? i1 - 1 : i2 + 1; + inc = following ? -1 : 1; + +/* Identify those indices which are not valid. */ + for ( i = start; i != end; i += inc ) { + if ( axes[ i ] == -1 ) { + +/* We wish to assign the value "extra" in place of this invalid axis + index. However, this may duplicate an index already present, so + increment by one all valid indices which are not less than the new + index. This eliminates any possibility duplication, although it may + leave an axis index value unused (if no duplication would actually + have occurred). */ + for ( j = 0; j < naxes; j++ ) { + if ( axes[ j ] != -1 ) { + if ( axes[ j ] >= extra ) axes[ j ]++; + } + } + +/* We can now assign the new axis index. */ + axes[ i ] = extra; + +/* Assign the value to be used for the next extra axis index. If + scanning forward, this will be one more than the last one used (so + it will follow it). If scanning backwards, it is equal to the last + one (so that it will displace the last one upwards). */ + extra += ( following ? 0 : 1 ); + +/* When a valid axis index is encountered, reset the value to be used + for the next extra axis index. If scanning forward, this is one + more than the last valid index (so the extra axis will follow + it). If scanning backwards, it is equal to the last valid index (so + it will displace the valid index upwards). */ + } else { + extra = axes[ i ] + ( following ? 0 : 1 ); + } + } +} + +static double Angle( AstFrame *this_frame, const double a[], + const double b[], const double c[], int *status ) { +/* +* Name: +* Angle + +* Purpose: +* Calculate the angle subtended by two points at a third point. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double Angle( AstFrame *this_frame, const double a[], +* const double b[], const double c[], int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astAngle method +* inherited from the Frame class). + +* Description: +* This function finds the angle at point B between the line joining +* points A and B, and the line joining points C and B, in the context +* of a CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* a +* An array of double, with one element for each CmpFrame axis, +* containing the coordinates of the first point. +* b +* An array of double, with one element for each CmpFrame axis, +* containing the coordinates of the second point. +* c +* An array of double, with one element for each CmpFrame axis, +* containing the coordinates of the third point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The required angle, or AST__BAD if the angle is undefined. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + AstFrame *pframe; /* Pointer to the primary Frame for an axis */ + const int *perm; /* Pointer to axis permutation array */ + double *pa; /* Permuted coordinates for point a */ + double *pb; /* Permuted coordinates for point b */ + double *pc; /* Permuted coordinates for point c */ + double ang1; /* Angle between input points in frame1 */ + double ang2; /* Angle between input points in frame2 */ + double result; /* Required angle */ + int axis; /* Loop counter for axes */ + int iscart; /* Is the CmpFrame a Cartesian system? */ + int naxes1; /* Number of axes in frame1 */ + int naxes; /* Total number of axes in CmpFrame */ + int paxis; /* Axis index within primary Frame */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain the number of axes in the CmpFrame. */ + naxes = astGetNaxes( this ); + +/* See if all axes within the CmpFrame belong to a simple Frame, in which + case we assume that the CmpFrame describes a Cartesian coordinate system. */ + iscart = 1; + for( axis = 0; axis < naxes; axis++ ){ + PrimaryFrame( this_frame, axis, &pframe, &paxis, status ); + if( strcmp( astGetClass( pframe ), "Frame" ) ) { + iscart = 0; + pframe = astAnnul( pframe ); + break; + } + pframe = astAnnul( pframe ); + } + +/* If the CmpFrame describes a Cartesian coordinate system, we can use the + Angle method from the parent Frame class. */ + if( iscart ) { + result = (*parent_angle)( this_frame, a, b, c, status ); + +/* If the CmpFrame is not Cartesian... */ + } else { + +/* Obtain a pointer to the CmpFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* Get workspace. */ + pa = (double *) astMalloc( sizeof(double)*naxes ); + pb = (double *) astMalloc( sizeof(double)*naxes ); + pc = (double *) astMalloc( sizeof(double)*naxes ); + +/* If OK, apply the axis permutation array to obtain the coordinates in the + required order. */ + if( astOK ) { + for( axis = 0; axis < naxes; axis++ ) { + pa[ perm[ axis ] ] = a[ axis ]; + pb[ perm[ axis ] ] = b[ axis ]; + pc[ perm[ axis ] ] = c[ axis ]; + } + +/* Obtain the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + +/* Project the two input points into the two component Frames and + determine the angle between the points in each Frame. */ + ang1 = astAngle( this->frame1, pa, pb, pc ); + ang2 = astAngle( this->frame2, pa + naxes1, pb + naxes1, + pc + naxes1 ); + +/* The required angle is defined if one and only one of the two component + frames gives a defined angle between the two points. */ + if( ang1 == AST__BAD ) { + result = ang2; + } else if( ang2 == AST__BAD ) { + result = ang1; + } + } + +/* Free the workspace */ + pa = (double *) astFree( (void *) pa ); + pb = (double *) astFree( (void *) pb ); + pc = (double *) astFree( (void *) pc ); + } + +/* Return the result. */ + return result; +} + +static AstObject *Cast( AstObject *this_object, AstObject *obj, int *status ) { +/* +* Name: +* Cast + +* Purpose: +* Cast an Object into an instance of a sub-class. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstObject *Cast( AstObject *this, AstObject *obj, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astCast +* method inherited from the Frame class). + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. Specifically, if "this" and "new" are +* of the same class, a copy of "this" is returned. If "this" is an +* instance of a subclass of "obj", then a copy of the component +* of "this" that matches the class of "obj" is returned. Otherwise, +* a NULL pointer is returned without error. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. NULL if "this" is not a sub-class of +* "obj", or if an error occurs. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables; */ + AstAxis *newaxis; + AstFrame *cfrm; + AstFrame *this; + AstObject *new; + astDECLARE_GLOBALS + int generation_gap; + int i; + int naxes; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* See how many steps up the class inheritance ladder it is from "obj" + to this class (CmpFrame). A positive value is returned if CmpFrame + is a sub-class of "obj". A negative value is returned if "obj" is + a sub-class of CmpFrame. Zero is returned if "obj" is a CmpFrame. + AST__COUSIN is returned if "obj" is not on the same line of descent + as CmpFrame. */ + generation_gap = astClassCompare( (AstObjectVtab *) &class_vtab, + astVTAB( obj ) ); + +/* If "obj" is a CmpFrame or a sub-class of CmpFrame, we can cast by + truncating the vtab for "this" so that it matches the vtab of "obJ", + and then taking a deep copy of "this". */ + if( generation_gap <= 0 && generation_gap != AST__COUSIN ) { + new = astCastCopy( this_object, obj ); + +/* If "obj" is not a CmpFrame or a sub-class of CmpFrame (e.g. it's a + Frame), we create a basic Frame containing the same axes and attributes + as the CmpFrame, and then attempt to cast this Frame into the class + indicated by "obj". */ + } else { + this = (AstFrame *) this_object; + +/* Create a basic Frame with the right number of axes. */ + naxes = astGetNaxes( this ); + cfrm = astFrame( naxes, " ", status ); + +/* Replace the Axis structures in the basic Frame with those in the + CmpFrame. */ + for( i = 0; i < naxes; i++ ) { + newaxis = astGetAxis( this, i ); + astSetAxis( cfrm, i, newaxis ); + newaxis = astAnnul( newaxis ); + } + +/* Overlay the properties of the CmpFrame onto the basic Frame. */ + astOverlay( this, NULL, cfrm ); + +/* Try to cast the basic Frame to the class of "obj". */ + new = astCast( cfrm, obj ); + +/* Annull the basic Frame. */ + cfrm = astAnnul( cfrm ); + } + +/* Return the new pointer. */ + return new; +} + +static double Centre( AstFrame *this_frame, int axis, double value, double gap, int *status ) { +/* +* Name: +* Centre + +* Purpose: +* Find a "nice" central value for tabulating CmpFrame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double Centre( AstFrame *this_frame, int axis, double value, +* double gap, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astCentre method +* inherited from the Frame class). + +* Description: +* This function returns an axis value which produces a nice formatted +* value suitable for a major tick mark on a plot axis, close to the +* supplied axis value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which a central value +* is to be found. +* value +* An arbitrary axis value in the section that is being plotted. +* gap +* The gap size. + +* Returned Value: +* The nice central axis value. + +* Notes: +* - A value of zero is returned if the supplied gap size is zero. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstFrame *frame; /* Pointer to Frame containing axis */ + double result; /* Result value to return */ + int naxes1; /* Number of axes in frame1 */ + int set1; /* Digits attribute set? */ + int set2; /* Format attribute set? */ + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astCentre" ); + +/* Determine the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which component Frame contains the axis and adjust the axis + index if necessary. */ + frame = ( axis < naxes1 ) ? this->frame1 : this->frame2; + axis = ( axis < naxes1 ) ? axis : axis - naxes1; + +/* Since the component Frame is "managed" by the enclosing CmpFrame, + we next test if any Frame attributes which may affect the result + are undefined (i.e. have not been explicitly set). If so, we + over-ride them, giving them temporary values dictated by the + CmpFrame. Only the Digits and Format attributes are relevant here. */ + set1 = astTestDigits( frame ); + if ( !set1 ) astSetDigits( frame, astGetDigits( this ) ); + + set2 = astTestFormat( frame, axis ); + if ( !set2 ) astSetFormat( frame, axis, astGetFormat( this, axis ) ); + +/* Invoke the Frame's astCentre method to find the central value. */ + result = astCentre( frame, axis, value, gap ); + +/* Clear Frame attributes which were temporarily over-ridden. */ + if ( !set1 ) astClearDigits( frame ); + if ( !set2 ) astClearFormat( frame, axis ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static void ClearAlignSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearAlignSystem + +* Purpose: +* Clear the value of the AlignSystem attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearAlignSystem( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearAlignSystem method +* inherited from the Frame class). + +* Description: +* This function clears the AlignSystem value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to clear the CmpFrame AlignSystem value. */ + (*parent_clearalignsystem)( this_frame, status ); + +/* Now clear the AlignSystem attribute in the two component Frames. */ + astClearAlignSystem( this->frame1 ); + astClearAlignSystem( this->frame2 ); +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* CmpFrame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the CmpFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + char buf1[80]; /* For for un-indexed attribute name */ + char buf2[80]; /* For for indexed attribute name */ + int axis; /* Sipplied (1-based) axis index */ + int len; /* Length of attrib string */ + int nc; /* Number of characters used so dar */ + int oldrep; /* Original error reporting state */ + int paxis; /* Index of primary Frame axis */ + int ok; /* Has the attribute been accessed succesfully? */ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Indicate we have not yet acessed the attribute succesfully. */ + ok = 0; + +/* First check the supplied attribute name against each of the attribute + names defined by this class. In fact there is nothing to do here + since the CmpFrame class currently defines no extra attributes, but + this may change in the future. */ + if( 0 ) { + + + +/* If the attribute is not a CmpFrame specific attribute... */ + } else if( astOK ) { + +/* We want to allow easy access to the attributes of the component Frames. + That is, we do not want it to be necessary to extract a Frame from + its parent CmpFrame in order to access its attributes. For this reason + we first temporarily switch off error reporting so that if an attempt + to access the attribute fails, we can try a different approach. */ + oldrep = astReporting( 0 ); + +/* Our first attempt is to see if the attribute is recognised by the parent + class (Frame). */ + (*parent_clearattrib)( this_object, attrib, status ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise, clear the error condition so that we can try a different + approach. */ + } else { + astClearStatus; + +/* If the attribute is qualified by an axis index, try accessing it as an + attribute of the primary Frame containing the specified index. */ + if ( nc = 0, + ( 2 == astSscanf( attrib, "%[^(](%d)%n", buf1, &axis, &nc ) ) + && ( nc >= len ) ) { + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + if( astOK ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astClear" ); + +/* Create a new attribute with the same name but with the axis index + appropriate to the primary Frame. */ + sprintf( buf2, "%s(%d)", buf1, paxis + 1 ); + +/* Attempt to access the attribute. */ + astClearAttrib( pfrm, buf2 ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise clear the status value, and try again without any axis index. */ + } else { + astClearStatus; + astClearAttrib( pfrm, buf1 ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + } + +/* Free the primary frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* If the attribute is not qualified by an axis index, try accessing it + using the primary Frame of each axis in turn. */ + } else { + +/* Loop round all axes attribute. */ + for( axis = 0; axis < astGetNaxes( this ); axis++ ) { + +/* Get the primary Frame containing this axis. */ + astPrimaryFrame( this, axis, &pfrm, &paxis ); + +/* Attempt to access the attribute as an attribute of the primary Frame. */ + astClearAttrib( pfrm, attrib ); + +/* Free the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + } + } + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + + } + +/* Report an error if the attribute could not be accessed. */ + if( !ok && astOK ) { + astError( AST__BADAT, "astClear: The %s given does not have an attribute " + "called \"%s\".", status, astGetClass( this ), attrib ); + } + +} + +static void ClearDut1( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearDut1 + +* Purpose: +* Clear the value of the Dut1 attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearDut1( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearDut1 method +* inherited from the Frame class). + +* Description: +* This function clears the Dut1 value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to clear the CmpFrame Dut1 value. */ + (*parent_cleardut1)( this_frame, status ); + +/* Now clear the Dut1 attribute in the two component Frames. */ + astClearDut1( this->frame1 ); + astClearDut1( this->frame2 ); +} + +static void ClearEpoch( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearEpoch + +* Purpose: +* Clear the value of the Epoch attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearEpoch( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearEpoch method +* inherited from the Frame class). + +* Description: +* This function clears the Epoch value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to clear the CmpFrame epoch. */ + (*parent_clearepoch)( this_frame, status ); + +/* Now clear the Epoch attribute in the two component Frames. */ + astClearEpoch( this->frame1 ); + astClearEpoch( this->frame2 ); +} + +static void ClearObsAlt( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearObsAlt + +* Purpose: +* Clear the value of the ObsAlt attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearObsAlt( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearObsAlt method +* inherited from the Frame class). + +* Description: +* This function clears the ObsAlt value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to clear the CmpFrame ObsAlt. */ + (*parent_clearobsalt)( this_frame, status ); + +/* Now clear the ObsAlt attribute in the two component Frames. */ + astClearObsAlt( this->frame1 ); + astClearObsAlt( this->frame2 ); +} + +static void ClearObsLat( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearObsLat + +* Purpose: +* Clear the value of the ObsLat attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearObsLat( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearObsLat method +* inherited from the Frame class). + +* Description: +* This function clears the ObsLat value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to clear the CmpFrame ObsLat. */ + (*parent_clearobslat)( this_frame, status ); + +/* Now clear the ObsLat attribute in the two component Frames. */ + astClearObsLat( this->frame1 ); + astClearObsLat( this->frame2 ); +} + +static void ClearObsLon( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearObsLon + +* Purpose: +* Clear the value of the ObsLon attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void ClearObsLon( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astClearObsLon method +* inherited from the Frame class). + +* Description: +* This function clears the ObsLon value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to clear the CmpFrame ObsLon. */ + (*parent_clearobslon)( this_frame, status ); + +/* Now clear the ObsLon attribute in the two component Frames. */ + astClearObsLon( this->frame1 ); + astClearObsLon( this->frame2 ); +} + +static int ComponentMatch( AstCmpFrame *template, AstFrame *target, int matchsub, + int icomp, int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* ComponentMatch + +* Purpose: +* Determine if conversion is possible between a component Frame in a +* template CmpFrame and another target Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int ComponentMatch( AstCmpFrame *template, AstFrame *target, int matchsub, +* int icomp, int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* CmpFrame member function + +* Description: +* This function is like astMatch, but it compares the supplied target +* Frame with a specified component Frame of the supplied template +* CmpFrame, rather than with the entire template CmpFrame. If a match +* is found, the returned Mapping, Frame and axis lists are adjusted so +* that they refer to the entire template CmpFrame. + +* Parameters: +* template +* Pointer to the template CmpFrame. This describes the +* coordinate system (or set of possible coordinate systems) +* into which we wish to convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate +* system in which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case (i.e. if the +* target is of a more specialised class than the template). In +* this latter case, the target is cast down to the class of the +* template. +* icomp +* The index of the component Frame to use within the template +* CmpFrame; 0 or 1. +* template_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* template CmpFrame axis from which it is derived. If it is not +* derived from any template axis, a value of -1 will be +* returned instead. +* target_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* target Frame axis from which it is derived. If it is not +* derived from any target axis, a value of -1 will be returned +* instead. +* map +* Address of a location where a pointer to a new Mapping will +* be returned if the requested coordinate conversion is +* possible. If returned, the forward transformation of this +* Mapping may be used to convert coordinates between the +* "target" Frame and the "result" Frame (see below) and the +* inverse transformation will convert in the opposite +* direction. +* result +* Address of a location where a pointer to a new Frame will be +* returned if the requested coordinate conversion is +* possible. If returned, this Frame describes the coordinate +* system that results from applying the returned Mapping +* (above) to the "target" coordinate system. In general, this +* Frame will combine attributes from (and will therefore be +* more specific than) both the target Frame and the template +* CmpFrame. In particular, when the template allows the +* possibility of transformaing to any one of a set of +* alternative coordinate systems, the "result" Frame will +* indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate +* conversion is possible. Otherwise zero is returned (this will +* not in itself result in an error condition). + +* Notes: +* - By default, the "result" Frame will have its number of axes +* and axis order determined by the "template" CmpFrame. However, +* if the PreserveAxes attribute of the template CmpFrame is +* non-zero, then the axis count and axis order of the "target" +* Frame will be used instead. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *ctemplate; + AstFrame *fother; + AstFrame *fresult; + AstMapping *fmap; + AstPermMap *pm; + const int *perm; + int *ftarget_axes; + int *ftemplate_axes; + int *inperm; + int *operm; + int *outperm; + int axis; + int match; + int nax1; + int nax2; + int naxr; + int prax; + int praxo; + int result_naxes; + int template_naxes; + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Get a pointer to the requested component Frame of the template CmpFrame. */ + ctemplate = icomp ? template->frame2 :template->frame1; + +/* Temporarily set the component Frame PreserveAxes value to that of the + template CmpFrame. PreserveAxes determines whether astMatch returns a + result Frame that looks like the template or the target. */ + if( astTestPreserveAxes( ctemplate ) ) { + praxo = astGetPreserveAxes( ctemplate ) ? 1 : 0; + } else { + praxo = -1; + } + prax = astGetPreserveAxes( template ); + astSetPreserveAxes( ctemplate, prax ); + +/* Attempt to find a match between the axes of the supplied target Frame + and the axes of the selected component Frame in the template. */ + match = astMatch( ctemplate, target, matchsub, &ftemplate_axes, &ftarget_axes, + &fmap, &fresult ); + +/* Restore the original PreserveAxes value in the component template + Frame. */ + if( praxo == -1 ) { + astClearPreserveAxes( ctemplate ); + } else { + astSetPreserveAxes( ctemplate, praxo ); + } + +/* If a match was found, we need to adjust the Mapping, Frame and axis + lists returned by the above call to astMatch so that they refer to the + full template CmpFrame or target (depending on PreserveAxes). */ + if( match ) { + +/* Get the number of axes in each component Frame and the total number of + axes in the template CmpFrame. */ + nax1 = astGetNaxes( template->frame1 ); + nax2 = astGetNaxes( template->frame2 ); + template_naxes = nax1 + nax2; + +/* Get the axis permutation array from the template and get its inverse. + The "perm" array holds the internal axis index at each external axis + index. The "operm" array holds the external axis index at each + internal axis index. */ + perm = astGetPerm( template ); + operm = astMalloc( sizeof( int )*(size_t)template_naxes ); + if( astOK) { + for( axis = 0; axis < template_naxes; axis++ ) { + operm[ perm[ axis ] ] = axis; + } + +/* The PreserveAxes attribute is used by astMatch to decide whether the + result Frame should inherit its axes from the target frame or the + template frame. First deal with cases where the order and count of axes + in the result frame is the same as the target. */ + if( prax ) { + +/* Return the result Frame and Mapping unchanged since they already refer + to the full target Frame used in the above call to astMatch. */ + *result = astClone( fresult ); + *map = astClone( fmap ); + +/* Also return the lists of target axes unchanged. */ + *target_axes = ftarget_axes; + +/* The values in the template axes list refer to the component template + Frame, but we want to return values that refer to the full template + CmpFrame. This involve sup to two setps 1) for the second component + Frame only, increase the axis numbers by the number of axes in the + first component Frame, and 2) take account of any axis permutation in + the template. First allocate memory for the returned list (which, + because PreserveAxes is zero, will have an entry for each template axis). */ + *template_axes = astMalloc( sizeof( int )*template_naxes ); + +/* Now, if the second component of the template has been selected, increment + the template axes so that they give the internal axis indices of the + second component Frame within the CmpFrame. The first component axes + will be unchanged. */ + result_naxes = astGetNaxes( fresult ); + if( icomp ) { + for( axis = 0; axis < result_naxes; axis++ ) { + ftemplate_axes[ axis ] += nax1; + } + } + +/* Now copy the internal axis value into the returned array, modifying them + in the process from internal to external axis ordering. */ + for( axis = 0; axis < result_naxes; axis++ ) { + (*template_axes)[ axis ] = operm[ ftemplate_axes[ axis ] ]; + } + +/* If the order and count of axes in the result frame is the same as the + template CmpFrame... */ + } else { + +/* We need to adjust the Mapping, Frame and axis lists returned by the + above call to astMatch so that they refer to the supplied template + CmpFrame rather than to the selected component Frame. Get the number + of axes in the result Frame returned by astMatch (naxr) and the number + in the result Frame returned by this function (result-naxes). */ + naxr = astGetNaxes( fresult ); + result_naxes = ( icomp ? nax1 : nax2 ) + naxr; + +/* Create the full result Frame by combining the partial result Frame + returned by astMatch above with the other component Frame from the + template. */ + if( icomp ) { + fother = astCopy( template->frame1 ); + *result = (AstFrame *) astCmpFrame( fother, fresult, "", status ); + } else { + fother = astCopy( template->frame2 ); + *result = (AstFrame *) astCmpFrame( fresult, fother, "", status ); + } + fother = astAnnul( fother ); + +/* Modify the Mapping returned by the above call to astMatch so that it + produces positions within the full result Frame created above. */ + if( icomp ) { + inperm = astMalloc( sizeof( int )*(size_t) naxr ); + outperm = astMalloc( sizeof( int )*(size_t) result_naxes ); + if( astOK ) { + for( axis = 0; axis < nax1; axis++ ) outperm[ axis ] = -1; + for( axis = 0; axis < naxr; axis++ ) { + outperm[ axis + nax1 ] = axis; + inperm[ axis ] = axis + nax1; + } + } + + } else { + inperm = NULL; + outperm = NULL; + } + + pm = astPermMap( naxr, inperm, result_naxes, outperm, NULL, "", status ); + *map = (AstMapping *) astCmpMap( fmap, pm, 1, "", status ); + +/* Free resources. */ + pm = astAnnul( pm ); + if( inperm ) inperm = astFree( inperm ); + if( outperm ) outperm = astFree( outperm ); + +/* Allocate memory for the returned list of axes. */ + *template_axes = astMalloc( sizeof( int )*(size_t)result_naxes ); + *target_axes = astMalloc( sizeof( int )*(size_t)result_naxes ); + +/* The axis indices returned by astMatch above will refer to the selected + component Frame rather than the permuted (i.e. external) axis indices for + the template CmpFrame. Change the template axes list so that they describe + the axes in the full result Frame in terms of the external template axis + numbering. This involves shifting the indices for the second component + Frame to leave room for the axes of the first component Frame, and + also permuting the axis indices from internal to external order. */ + if( icomp ) { + for( axis = 0; axis < nax1; axis++ ) { + (*template_axes)[ axis ] = operm[ axis ]; + } + + for( ; axis < result_naxes; axis++ ) { + (*template_axes)[ axis ] = operm[ nax1 + ftemplate_axes[ axis - nax1 ] ]; + } + + } else { + for( axis = 0; axis < nax1; axis++ ) { + (*template_axes)[ axis ] = operm[ ftemplate_axes[ axis ] ]; + } + + for( ; axis < result_naxes; axis++ ) { + (*template_axes)[ axis ] = operm[ axis ]; + } + } + +/* Change the target axes list so that they describe the axes in the + full result Frame (this just means padding with -1 to indicate that + the extra axes do not correspond to any axis in the target). */ + for( axis = 0; axis < naxr; axis++ ) { + (*target_axes)[ axis ] = ftarget_axes[ axis ]; + } + + for( ; axis < result_naxes; axis++ ) { + (*target_axes)[ axis ] = -1; + } + +/* Free resources */ + ftarget_axes = astFree( ftarget_axes ); + } + } + + operm = astFree( operm ); + ftemplate_axes = astFree( ftemplate_axes ); + fmap = astAnnul( fmap ); + fresult = astAnnul( fresult ); + + } + +/* If an error occurred, free all allocated memory, annul the result + Object pointers and clear all returned values. */ + if ( !astOK ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + *map = astAnnul( *map ); + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void Decompose( AstMapping *this_cmpframe, AstMapping **map1, + AstMapping **map2, int *series, int *invert1, + int *invert2, int *status ) { +/* +* +* Name: +* Decompose + +* Purpose: +* Decompose a CmpFrame into two component CmpFrames. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void Decompose( AstMapping *this, AstMapping **map1, +* AstMapping **map2, int *series, +* int *invert1, int *invert2, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astDecompose +* method inherited from the Mapping class). + +* Description: +* This function returns pointers to two Mappings which, when applied +* either in series or parallel, are equivalent to the supplied Mapping. +* +* Since the Frame class inherits from the Mapping class, Frames can +* be considered as special types of Mappings and so this method can +* be used to decompose either CmpMaps or CmpFrames. + +* Parameters: +* this +* Pointer to the Mapping. +* map1 +* Address of a location to receive a pointer to first component +* Mapping. +* map2 +* Address of a location to receive a pointer to second component +* Mapping. +* series +* Address of a location to receive a value indicating if the +* component Mappings are applied in series or parallel. A non-zero +* value means that the supplied Mapping is equivalent to applying map1 +* followed by map2 in series. A zero value means that the supplied +* Mapping is equivalent to applying map1 to the lower numbered axes +* and map2 to the higher numbered axes, in parallel. +* invert1 +* The value of the Invert attribute to be used with map1. +* invert2 +* The value of the Invert attribute to be used with map2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component rames using the returned +* pointers will be reflected in the supplied CmpFrame. + +*- +*/ + + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpFrame *) this_cmpframe; + +/* The components Frames of a CmpFrame are considered to be parallel + Mappings. */ + if( series ) *series = 0; + +/* The Frames are returned in their original order whether or not the + CmpFrame has been inverted. */ + if( map1 ) *map1 = astClone( this->frame1 ); + if( map2 ) *map2 = astClone( this->frame2 ); + +/* If the CmpFrame has been inverted, return inverted Invert flags. */ + if( astGetInvert( this ) ) { + if( invert1 ) *invert1 = astGetInvert( this->frame1 ) ? 0 : 1; + if( invert2 ) *invert2 = astGetInvert( this->frame2 ) ? 0 : 1; + +/* If the CmpFrame has not been inverted, return the current Invert flags. */ + } else { + if( invert1 ) *invert1 = astGetInvert( this->frame1 ); + if( invert2 ) *invert2 = astGetInvert( this->frame2 ); + } +} + +static double Distance( AstFrame *this_frame, + const double point1[], const double point2[], int *status ) { +/* +* Name: +* Distance + +* Purpose: +* Calculate the distance between two points. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double Distance( AstFrame *this, +* const double point1[], const double point2[], int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astDistance method +* inherited from the Frame class). + +* Description: +* This function finds the distance between two points whose +* CmpFrame coordinates are given. The distance calculated is that +* along the geodesic curve that joins the two points. This is +* computed as the Cartesian sum of the distances between the +* points when their coordinates are projected into each of the +* CmpFrame's component Frames. + +* Parameters: +* this +* Pointer to the CmpFrame. +* point1 +* An array of double, with one element for each CmpFrame axis, +* containing the coordinates of the first point. +* point2 +* An array of double, with one element for each CmpFrame axis, +* containing the coordinates of the second point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The distance between the two points. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + const int *perm; /* Axis permutation array */ + double *p1; /* Pointer to permuted point1 coordinates */ + double *p2; /* Pointer to permuted point2 coordinates */ + double dist1; /* Distance in frame1 */ + double dist2; /* Distance in frame2 */ + double result; /* Value to return */ + int axis; /* Loop counter for axes */ + int naxes1; /* Number of axes in frame1 */ + int naxes; /* Number of axes in CmpFrame */ + int ok; /* No "bad" coordinates found? */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain a pointer to the CmpFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* Obtain the number of axes in the CmpFrame and in the first + component Frame. */ + naxes = astGetNaxes( this ); + naxes1 = astGetNaxes( this->frame1 ); + +/* Allocate memory to hold the permuted coordinates of each point. */ + p1 = astMalloc( sizeof( double ) * (size_t) naxes ); + p2 = astMalloc( sizeof( double ) * (size_t) naxes ); + if ( astOK ) { + +/* Examine the coordinates of both points and note if any coordinate + value is "bad". */ + ok = 1; + for ( axis = 0; axis < naxes; axis++ ) { + if ( ( point1[ axis ] == AST__BAD ) || + ( point2[ axis ] == AST__BAD ) ) { + ok = 0; + break; + +/* Permute good coordinates using the CmpFrame's axis permutation + array to put them into the order required internally (i.e. by the + two component Frames). */ + } else { + p1[ perm[ axis ] ] = point1[ axis ]; + p2[ perm[ axis ] ] = point2[ axis ]; + } + } + +/* If no "bad" coordinates were found, obtain the distance between the + two points when their coordinates are projected into each component + Frame. */ + if ( ok ) { + dist1 = astDistance( this->frame1, p1, p2 ); + dist2 = astDistance( this->frame2, p1 + naxes1, p2 + naxes1 ); + +/* If the distances found were OK, compute the distance between the + two points as the Cartesian sum of the two component distances. */ + if ( astOK && ( dist1 != AST__BAD ) && ( dist2 != AST__BAD ) ) { + result = sqrt( ( dist1 * dist1 ) + ( dist2 * dist2 ) ); + } + } + } + +/* Free the memory used for the permuted coordinates. */ + p1 = astFree( p1 ); + p2 = astFree( p2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static int Fields( AstFrame *this_frame, int axis, const char *fmt, + const char *str, int maxfld, char **fields, + int *nc, double *val, int *status ) { +/* +*+ +* Name: +* astFields + +* Purpose: +* Identify numerical fields within a formatted CmpFrame axis value. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "cmpframe.h" +* int astFields( AstFrame *this, int axis, const char *fmt, +* const char *str, int maxfld, char **fields, +* int *nc, double *val ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astFields +* method inherited from the Frame class). + +* Description: +* This function identifies the numerical fields within a CmpFrame axis +* value that has been formatted using astAxisFormat. It assumes that +* the value was formatted using the supplied format string. It also +* returns the equivalent floating point value. + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis +* The number of the CmpFrame axis for which the values have been +* formatted (axis numbering starts at zero for the first axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format used when creating "str". +* str +* Pointer to a constant null-terminated string containing the +* formatted value. +* maxfld +* The maximum number of fields to identify within "str". +* fields +* A pointer to an array of at least "maxfld" character pointers. +* Each element is returned holding a pointer to the start of the +* corresponding field in "str" (in the order in which they occur +* within "str"), or NULL if no corresponding field can be found. +* nc +* A pointer to an array of at least "maxfld" integers. Each +* element is returned holding the number of characters in the +* corresponding field, or zero if no corresponding field can be +* found. +* val +* Pointer to a location at which to store the value +* equivalent to the returned field values. If this is NULL, +* it is ignored. + +* Returned Value: +* The number of fields succesfully identified and returned. + +* Notes: +* - Leading and trailing spaces are ignored. +* - If the formatted value is not consistent with the supplied format +* string, then a value of zero will be returned, "fields" will be +* returned holding NULLs, "nc" will be returned holding zeros, and +* "val" is returned holding VAL__BAD. +* - Fields are counted from the start of the formatted string. If the +* string contains more than "maxfld" fields, then trailing fields are +* ignored. +* - If this function is invoked with the global error status set, or +* if it should fail for any reason, then a value of zero will be returned +* as the function value, and "fields", "nc" and "val" will be returned +* holding their supplied values +*- +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstFrame *frame; /* Pointer to Frame containing axis */ + int naxes1; /* Number of axes in frame1 */ + int result; /* Result field count to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astFields" ); + +/* Determine the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which component Frame contains the axis and adjust the axis + index if necessary. */ + frame = ( axis < naxes1 ) ? this->frame1 : this->frame2; + axis = ( axis < naxes1 ) ? axis : axis - naxes1; + +/* Invoke the Frame's astFields method to perform the processing. */ + result = astFields( frame, axis, fmt, str, maxfld, fields, + nc, val ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static const char *Format( AstFrame *this_frame, int axis, double value, int *status ) { +/* +* Name: +* Format + +* Purpose: +* Format a coordinate value for a CmpFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* const char *Format( AstFrame *this, int axis, double value, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astFormat method +* inherited from the Frame class). + +* Description: +* This function returns a pointer to a string containing the +* formatted (character) version of a coordinate value for a +* CmpFrame axis. The formatting applied is that specified by a +* previous invocation of the astSetFormat method (or a default +* format appropriate to the axis in question). + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis +* The number of the axis (zero-based) for which formatting is +* to be performed. +* value +* The coordinate value to be formatted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted +* value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstFrame *frame; /* Pointer to Frame containing axis */ + const char *result; /* Pointer value to return */ + int naxes1; /* Number of axes in frame1 */ + int set; /* Digits attribute set? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astFormat" ); + +/* Determine the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which component Frame contains the axis and adjust the axis + index if necessary. */ + frame = ( axis < naxes1 ) ? this->frame1 : this->frame2; + axis = ( axis < naxes1 ) ? axis : axis - naxes1; + +/* Since the component Frame is "managed" by the enclosing CmpFrame, + we next test if any Frame attributes which may affect the result + are undefined (i.e. have not been explicitly set). If so, we + over-ride them, giving them temporary values dictated by the + CmpFrame. Only the Digits attribute is relevant here. */ + set = astTestDigits( frame ); + if ( !set ) astSetDigits( frame, astGetDigits( this ) ); + +/* Invoke the Frame's astFormat method to format the value. */ + result = astFormat( frame, axis, value ); + +/* Clear Frame attributes which were temporarily over-ridden. */ + if ( !set ) astClearDigits( frame ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static AstPointSet *FrameGrid( AstFrame *this_object, int size, const double *lbnd, + const double *ubnd, int *status ){ +/* +* Name: +* FrameGrid + +* Purpose: +* Return a grid of points covering a rectangular area of a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstPointSet *FrameGrid( AstFrame *this_frame, int size, +* const double *lbnd, const double *ubnd, +* int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astFrameGrid +* method inherited from the Frame class). + +* Description: +* This function returns a PointSet containing positions spread +* approximately evenly throughtout a specified rectangular area of +* the Frame. + +* Parameters: +* this +* Pointer to the Frame. +* size +* The preferred number of points in the returned PointSet. The +* actual number of points in the returned PointSet may be +* different, but an attempt is made to stick reasonably closely to +* the supplied value. +* lbnd +* Pointer to an array holding the lower bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. +* ubnd +* Pointer to an array holding the upper bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. + +* Returned Value: +* A pointer to a new PointSet holding the grid of points. + +* Notes: +* - A NULL pointer is returned if an error occurs. +*/ + +/* Local Variables: */ + AstCmpFrame *this; + AstPointSet *ps1; + AstPointSet *ps2; + AstPointSet *result; + const int *perm; + double **ptr1; + double **ptr2; + double **ptr; + double *lbnd1; + double *lbnd2; + double *p; + double *ubnd1; + double *ubnd2; + double v; + int axis; + int iax1; + int iax2; + int iaxis; + int ip1; + int ip2; + int nax1; + int nax2; + int naxes; + int npoint1; + int npoint2; + int npoint; + int size1; + int size2; + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Get the number of axes in each component Frame, and the total number + of axes. */ + nax1 = astGetNaxes( this->frame1 ); + nax2 = astGetNaxes( this->frame2 ); + naxes = nax1 + nax2; + +/* Allocate memory to hold bounds for each component Frame */ + lbnd1 = astMalloc( nax1*sizeof( double ) ); + ubnd1 = astMalloc( nax1*sizeof( double ) ); + lbnd2 = astMalloc( nax2*sizeof( double ) ); + ubnd2 = astMalloc( nax2*sizeof( double ) ); + +/* Obtain a pointer to the CmpFrame's axis permutation array. This array + holds the original axis index for each current Frame axis index. */ + perm = astGetPerm( this ); + +/* Check pointers can be used safely, and check the supplied size value + is good. */ + if( astOK && size > 0 ) { + +/* Copy the supplied bounds into the work arrays, permuting them in the + process so that they use the internal axis numbering of the two + component Frames. */ + for( axis = 0; axis < naxes; axis++ ) { + iaxis = perm[ axis ]; + if( iaxis < nax1 ) { + lbnd1[ iaxis ] = lbnd[ axis ]; + ubnd1[ iaxis ] = ubnd[ axis ]; + } else { + iaxis -= nax1; + lbnd2[ iaxis ] = lbnd[ axis ]; + ubnd2[ iaxis ] = ubnd[ axis ]; + } + } + +/* Get the target number of points to be used in the grid that covers the + first Frame. */ + size1 = (int)( pow( size, (double)nax1/(double)naxes ) + 0.5 ); + +/* Get the target number of points to be used in the grid that covers the + second Frame. */ + size2 = (int)( (double)size/(double)size1 + 0.5 ); + +/* Get the grids covering the two component Frames, and get the actual sizes + of the resulting PointSets. */ + ps1 = astFrameGrid( this->frame1, size1, lbnd1, ubnd1 ); + ptr1 = astGetPoints( ps1 ); + npoint1 = astGetNpoint( ps1 ); + + ps2 = astFrameGrid( this->frame2, size2, lbnd2, ubnd2 ); + ptr2 = astGetPoints( ps2 ); + npoint2 = astGetNpoint( ps2 ); + +/* Get the number of points in the returned FrameSet, and then create a + PointSet large enough to hold them. */ + npoint = npoint1*npoint2; + result = astPointSet( npoint, naxes, " ", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* For every point in the first Frame's PointSet, duplicate the second + Frame's entire PointSet, using the first Frame's axis values. */ + for( ip1 = 0; ip1 < npoint1; ip1++ ) { + for( iax1 = 0; iax1 < nax1; iax1++ ) { + p = ptr[ iax1 ]; + v = ptr1[ iax1 ][ ip1 ]; + for( ip2 = 0; ip2 < npoint2; ip2++ ) { + *(p++) = v; + } + ptr[ iax1 ] = p; + } + for( iax2 = 0; iax2 < nax2; iax2++ ) { + memcpy( ptr[ iax2 + nax1 ], ptr2[ iax2 ], npoint2*sizeof( double ) ); + ptr[ iax2 + nax1 ] += npoint2*sizeof( double ); + } + } + +/* Permute the returned PointSet so that it uses external axis numbering. */ + astPermPoints( result, 1, perm ); + } + +/* Free resources. */ + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + +/* Report error if supplied values were bad. */ + } else if( astOK ) { + astError( AST__ATTIN, "astFrameGrid(%s): The supplied grid " + "size (%d) is invalid (programming error).", + status, astGetClass( this ), size ); + } + +/* Free resources. */ + lbnd1 = astFree( lbnd1 ); + ubnd1 = astFree( ubnd1 ); + lbnd2 = astFree( lbnd2 ); + ubnd2 = astFree( ubnd2 ); + +/* Annul the returned PointSet if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the PointSet holding the grid. */ + return result; +} + +static double Gap( AstFrame *this_frame, int axis, double gap, int *ntick, int *status ) { +/* +* Name: +* Gap + +* Purpose: +* Find a "nice" gap for tabulating CmpFrame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astGap method +* inherited from the Frame class). + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted values for a CmpFrame axis, the returned gap +* size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstFrame *frame; /* Pointer to Frame containing axis */ + double result; /* Result value to return */ + int naxes1; /* Number of axes in frame1 */ + int set; /* Digits attribute set? */ + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astGap" ); + +/* Determine the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which component Frame contains the axis and adjust the axis + index if necessary. */ + frame = ( axis < naxes1 ) ? this->frame1 : this->frame2; + axis = ( axis < naxes1 ) ? axis : axis - naxes1; + +/* Since the component Frame is "managed" by the enclosing CmpFrame, + we next test if any Frame attributes which may affect the result + are undefined (i.e. have not been explicitly set). If so, we + over-ride them, giving them temporary values dictated by the + CmpFrame. Only the Digits attribute is relevant here. */ + set = astTestDigits( frame ); + if ( !set ) astSetDigits( frame, astGetDigits( this ) ); + +/* Invoke the Frame's astGap method to find the gap size. */ + result = astGap( frame, axis, gap, ntick ); + +/* Clear Frame attributes which were temporarily over-ridden. */ + if ( !set ) astClearDigits( frame ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied CmpFrame, +* in bytes. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->frame1 ); + result += astGetObjSize( this->frame2 ); + result += astTSizeOf( this->perm ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetAlignSystem + +* Purpose: +* Obtain the AlignSystem attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetAlignSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the AlignSystem attribute for a CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The AlignSystem value. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If a AlignSystem attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestAlignSystem( this ) ) { + result = (*parent_getalignsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__COMP; + } + +/* Return the result. */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "CmpFrame.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a CmpFrame, formatted as a character string. + +* Parameters: +* this +* Pointer to the CmpFrame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the CmpFrame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the CmpFrame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + char buf1[80]; /* For for un-indexed attribute name */ + char buf2[80]; /* For for indexed attribute name */ + const char *result; /* Pointer value to return */ + int axis; /* Supplied (1-base) axis index */ + int len; /* Length of attrib string */ + int nc; /* Length of string used so far */ + int ok; /* Has the attribute been accessed succesfully? */ + int oldrep; /* Original error reporting state */ + int paxis; /* Index of primary Frame axis */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Indicate we have not yet acessed the attribute succesfully. */ + ok = 0; + +/* First check the supplied attribute name against each of the attribute + names defined by this class. In fact there is nothing to do here + since the CmpFrame class currently defines no extra attributes, but + this may change in the future. */ + if( 0 ) { + +/* If the attribute is not a CmpFrame specific attribute... */ + } else if( astOK ) { + +/* We want to allow easy access to the attributes of the component Frames. + That is, we do not want it to be necessary to extract a Frame from + its parent CmpFrame in order to access its attributes. For this reason + we first temporarily switch off error reporting so that if an attempt + to access the attribute fails, we can try a different approach. */ + oldrep = astReporting( 0 ); + +/* If the attribute is qualified by an axis index, try accessing it as an + attribute of the primary Frame containing the specified index. */ + if ( nc = 0, + ( 2 == astSscanf( attrib, "%[^(](%d)%n", buf1, &axis, &nc ) ) + && ( nc >= len ) ) { + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + if( astOK ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astGet" ); + +/* Create a new attribute with the same name but with the axis index + appropriate to the primary Frame. */ + sprintf( buf2, "%s(%d)", buf1, paxis + 1 ); + +/* Attempt to access the attribute. */ + result = astGetAttrib( pfrm, buf2 ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise clear the status value, and try again without any axis index. */ + } else { + astClearStatus; + result = astGetAttrib( pfrm, buf1 ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + } + +/* Free the primary frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* If the attribute is not qualified by an axis index, try accessing it + using the parent Frame method. */ + } else if( astOK ){ + result = (*parent_getattrib)( this_object, attrib, status ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise, clear the error condition so that we can try a different + approach. */ + } else { + astClearStatus; + +/* Next try accessing it using the primary Frame of each axis in turn. + Loop round all axes, until one is found which defines the specified + attribute. */ + for( axis = 0; axis < astGetNaxes( this ) && !ok; axis++ ) { + +/* Get the primary Frame containing this axis. */ + astPrimaryFrame( this, axis, &pfrm, &paxis ); + +/* Attempt to access the attribute as an attribute of the primary Frame. */ + result = astGetAttrib( pfrm, attrib ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + +/* Free the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + + } + } + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + + } + +/* Report an error if the attribute could not be accessed. */ + if( !ok && astOK ) { + astError( AST__BADAT, "astGet: The %s given does not have an attribute " + "called \"%s\".", status, astGetClass( this ), attrib ); + } + +/* Return the result. */ + return result; + +} + +static int GenAxisSelection( int naxes, int nselect, int axes[], int *status ) { +/* +* Name: +* GenAxisSelection + +* Purpose: +* Generate a sequence of axis selections. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GenAxisSelection( int naxes, int nselect, int axes[], int *status ) + +* Class Membership: +* CmpFrame member function. + +* Description: +* This function generates a sequence of axis selections covering +* all possible ways of selecting a specified number of axes from a +* Frame. + +* Parameters: +* naxes +* The number of axes in the Frame. +* nselect +* The number of axes to be selected (between zero and "naxes"). +* axes +* An array with "nselect" elements. On entry it should contain +* the (zero-based) indices of the initial set of distinct axes +* to be selected, in increasing order (initiallly this should +* just be the sequence [0,1,...nselect-1]). On exit, these +* indices will be updated to identify the next possible axis +* selection. +* +* By invoking the function repeatedly, and passing this array +* each time, all possible selections will be covered. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a new axis selection has been returned. Zero if all +* possible selections have already been returned (in which case +* the selection returned this time is not valid and should not be +* used). + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int i; /* Loop counter for axes */ + int iselect; /* Selection index */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Start with the first axis index and loop until the selection has + been updated. */ + iselect = 0; + while ( 1 ) { + +/* Increment the current axis index if it is the final one or it can + be incremented without equalling the one which follows (this ensures + the indices remain in increasing order). */ + if ( ( iselect == ( nselect - 1 ) ) || + ( axes[ iselect + 1 ] > ( axes[ iselect ] + 1 ) ) ) { + axes[ iselect ]++; + +/* After incrementing an index, reset all previous indices to their + starting values. */ + for ( i = 0; i < iselect; i++ ) axes[ i ] = i; + break; + +/* If this axis index can't be incremented, consider the next one. + Quit if we go beyond the end of the selection array. */ + } else if ( ++iselect >= nselect ) { + break; + } + } + +/* Return a result to indicate if we've reached the final selection + (when the final axis index goes out of range). */ + return ( nselect > 0 ) && ( axes[ nselect - 1 ] < naxes ); +} + +static AstAxis *GetAxis( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetAxis + +* Purpose: +* Obtain a pointer to a specified Axis from a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstAxis *GetAxis( AstFrame *this, int axis, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetAxis method +* inherited from the Frame class). + +* Description: +* This function returns a pointer to the Axis object associated +* with one of the axes of a CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis +* The number of the axis (zero-based) for which an Axis pointer +* is required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the requested Axis object. + +* Notes: +* - The reference count of the requested Axis object will be +* incremented by one to reflect the additional pointer returned by +* this function. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Vaiables: */ + AstAxis *result; /* Pointer value to return */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + int naxes1; /* Number of axes for frame1 */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astGetAxis" ); + +/* Obtain the number of axes for frame1. */ + naxes1 = astGetNaxes( this->frame1 ); + +/* Decide which Frame the axis belongs to and obtain the required + Axis pointer. */ + if ( axis < naxes1 ) { + result = astGetAxis( this->frame1, axis ); + } else { + result = astGetAxis( this->frame2, axis - naxes1 ); + } + +/* Return the result. */ + return result; +} + +static const char *GetDomain( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDomain + +* Purpose: +* Obtain a pointer to the Domain attribute string for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* const char *GetDomain( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetDomain protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the Domain attribute string +* for a CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Domain value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the CmpFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + char *dom1; /* Pointer to first sub domain */ + char *dom2; /* Pointer to second sub domain */ + const char *result; /* Pointer value to return */ + const char *t; /* Temporary pointer */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If a Domain attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestDomain( this ) ) { + result = (*parent_getdomain)( this_frame, status ); + +/* Otherwise, provide a pointer to a suitable default string. */ + } else { + +/* Get the Domain value for the two component Frames and store new + copies of them. This is necessary because the component Frames may + themselves be CmpFrames, resulting in this function being called + recursively and so causing the static "getdomain_buff" array to be used in + multiple contexts. */ + t = astGetDomain( this->frame1 ); + dom1 = t ? astStore( NULL, t, strlen(t) + 1 ) : NULL; + t = astGetDomain( this->frame2 ); + dom2 = t ? astStore( NULL, t, strlen(t) + 1 ) : NULL; + + if( dom2 ) { + if( strlen( dom1 ) > 0 || strlen( dom2 ) > 0 ) { + sprintf( (char *) getdomain_buff, "%s-%s", dom1, dom2 ); + result = getdomain_buff; + } else { + result = "CMP"; + } + } + + dom1 = astFree( dom1 ); + dom2 = astFree( dom2 ); + } + +/* Return the result. */ + return result; +} + +static int GetMaxAxes( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetMaxAxes + +* Purpose: +* Get a value for the MaxAxes attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GetMaxAxes( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetMaxAxes method +* inherited from the Frame class). + +* Description: +* This function returns a value for the MaxAxes attribute of a +* CmpFrame. A large default value is supplied that is much larger +* than the maximum likely number of axes in a Frame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The MaxAxes attribute value. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If a value has been set explicitly for the CmpFrame, return it. + Otherwise returned a large default value. */ + if( astTestMaxAxes( this ) ) { + result = (*parent_getmaxaxes)( this_frame, status ); + } else { + result = 1000000; + } + +/* Return the result. */ + return result; +} + +static int GetMinAxes( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetMinAxes + +* Purpose: +* Get a value for the MinAxes attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GetMinAxes( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetMinAxes method +* inherited from the Frame class). + +* Description: +* This function returns a value for the MinAxes attribute of a +* CmpFrame. A default value of zero is used. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The MinAxes attribute value. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If a value has been set explicitly for the CmpFrame, return it. + Otherwise returned a default value of zero. */ + if( astTestMinAxes( this ) ) { + result = (*parent_getminaxes)( this_frame, status ); + } else { + result = 0; + } + +/* Return the result. */ + return result; +} + +static double GetDut1( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDut1 + +* Purpose: +* Get a value for the Dut1 attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double GetDut1( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetDut1 method +* inherited from the Frame class). + +* Description: +* This function returns a value for the Dut1 attribute of a +* CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Dut1 attribute value. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + double result; /* Result value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If an Dut1 attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestDut1( this ) ) { + result = (*parent_getdut1)( this_frame, status ); + +/* Otherwise, if the Dut1 value is set in the first component Frame, + return it. */ + } else if( astTestDut1( this->frame1 ) ){ + result = astGetDut1( this->frame1 ); + +/* Otherwise, if the Dut1 value is set in the second component Frame, + return it. */ + } else if( astTestDut1( this->frame2 ) ){ + result = astGetDut1( this->frame2 ); + +/* Otherwise, return the default Dut1 value from the first component + Frame. */ + } else { + result = astGetDut1( this->frame1 ); + } + +/* Return the result. */ + return result; +} + +static double GetEpoch( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetEpoch + +* Purpose: +* Get a value for the Epoch attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double GetEpoch( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetEpoch method +* inherited from the Frame class). + +* Description: +* This function returns a value for the Epoch attribute of a +* CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Epoch attribute value. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + double result; /* Result value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If an Epoch attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestEpoch( this ) ) { + result = (*parent_getepoch)( this_frame, status ); + +/* Otherwise, if the Epoch value is set in the first component Frame, + return it. */ + } else if( astTestEpoch( this->frame1 ) ){ + result = astGetEpoch( this->frame1 ); + +/* Otherwise, if the Epoch value is set in the second component Frame, + return it. */ + } else if( astTestEpoch( this->frame2 ) ){ + result = astGetEpoch( this->frame2 ); + +/* Otherwise, return the default Epoch value from the first component + Frame. */ + } else { + result = astGetEpoch( this->frame1 ); + } + +/* Return the result. */ + return result; +} + +static double GetObsAlt( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetObsAlt + +* Purpose: +* Get a value for the ObsAlt attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double GetObsAlt( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetObsAlt method +* inherited from the Frame class). + +* Description: +* This function returns a value for the ObsAlt attribute of a +* CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The ObsAlt attribute value. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + double result; /* Result value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If an ObsAlt attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestObsAlt( this ) ) { + result = (*parent_getobsalt)( this_frame, status ); + +/* Otherwise, if the ObsAlt value is set in the first component Frame, + return it. */ + } else if( astTestObsAlt( this->frame1 ) ){ + result = astGetObsAlt( this->frame1 ); + +/* Otherwise, if the ObsAlt value is set in the second component Frame, + return it. */ + } else if( astTestObsAlt( this->frame2 ) ){ + result = astGetObsAlt( this->frame2 ); + +/* Otherwise, return the default ObsAlt value from the first component + Frame. */ + } else { + result = astGetObsAlt( this->frame1 ); + } + +/* Return the result. */ + return result; +} + +static double GetObsLat( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetObsLat + +* Purpose: +* Get a value for the ObsLat attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double GetObsLat( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetObsLat method +* inherited from the Frame class). + +* Description: +* This function returns a value for the ObsLat attribute of a +* CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The ObsLat attribute value. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + double result; /* Result value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If an ObsLat attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestObsLat( this ) ) { + result = (*parent_getobslat)( this_frame, status ); + +/* Otherwise, if the ObsLat value is set in the first component Frame, + return it. */ + } else if( astTestObsLat( this->frame1 ) ){ + result = astGetObsLat( this->frame1 ); + +/* Otherwise, if the ObsLat value is set in the second component Frame, + return it. */ + } else if( astTestObsLat( this->frame2 ) ){ + result = astGetObsLat( this->frame2 ); + +/* Otherwise, return the default ObsLat value from the first component + Frame. */ + } else { + result = astGetObsLat( this->frame1 ); + } + +/* Return the result. */ + return result; +} + +static double GetObsLon( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetObsLon + +* Purpose: +* Get a value for the ObsLon attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* double GetObsLon( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetObsLon method +* inherited from the Frame class). + +* Description: +* This function returns a value for the ObsLon attribute of a +* CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The ObsLon attribute value. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + double result; /* Result value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If an ObsLon attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestObsLon( this ) ) { + result = (*parent_getobslon)( this_frame, status ); + +/* Otherwise, if the ObsLon value is set in the first component Frame, + return it. */ + } else if( astTestObsLon( this->frame1 ) ){ + result = astGetObsLon( this->frame1 ); + +/* Otherwise, if the ObsLon value is set in the second component Frame, + return it. */ + } else if( astTestObsLon( this->frame2 ) ){ + result = astGetObsLon( this->frame2 ); + +/* Otherwise, return the default ObsLon value from the first component + Frame. */ + } else { + result = astGetObsLon( this->frame1 ); + } + +/* Return the result. */ + return result; +} + +static int GetNaxes( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetNaxes + +* Purpose: +* Determine how many axes a CmpFrame has. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GetNaxes( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetNaxes method +* inherited from the Frame class). + +* Description: +* This function returns the number of axes for a CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of CmpFrame axes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + int naxes1; /* Number of axes for frame1 */ + int naxes2; /* Number of axes for frame2 */ + int result; /* Number of CmpFrame axes */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain the number of axes for each component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + naxes2 = astGetNaxes( this->frame2 ); + +/* If OK, calculate the total number of axes. */ + if ( astOK ) result = naxes1 + naxes2; + +/* Return the result. */ + return result; +} + +static const int *GetPerm( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetPerm + +* Purpose: +* Access the axis permutation array for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* const int *astGetPerm( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astGetPerm +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the axis permutation array +* for a CmpFrame. This array constitutes a lookup-table that +* converts between an axis number supplied externally and the +* corresponding index in the CmpFrame's internal data. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the CmpFrame's axis permutation array (a constant +* array of int). Each element of this contains the (zero-based) +* internal axis index to be used in place of the external index +* which is used to address the permutation array. If the CmpFrame +* has zero axes, this pointer will be NULL. + +* Notes: +* - This protected method is provided to assist class +* implementations which need to implement axis-dependent +* extensions to CmpFrame methods, and which therefore need to know +* how a CmpFrames's external axis index is converted for internal +* use. +* - The pointer returned by this function gives direct access to +* data internal to the CmpFrame object. It remains valid only so +* long as the CmpFrame exists. The permutation array contents may +* be modified by other functions which operate on the CmpFrame and +* this may render the returned pointer invalid. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Implementation Notes: +* - This function performs essentially the same operation as the +* Frame member function which it over-rides. However, it returns a +* pointer to the "perm" array held in the CmpFrame structure +* (rather than the one in the parent Frame structure). This +* duplication of the array is necessary because the one in the +* Frame structure is of zero length, the number of axes in the +* Frame structure having been set to zero to prevent unnecessary +* allocation of Axis objects which are not needed by the CmpFrame. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Return a pointer to the axis permutation array. */ + return this->perm; +} + +static AstSystemType GetSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetSystem + +* Purpose: +* Obtain the System attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstSystemType GetSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the System attribute for a CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If a System attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestSystem( this ) ) { + result = (*parent_getsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__COMP; + } + +/* Return the result. */ + return result; +} + +static const char *GetTitle( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetTitle + +* Purpose: +* Obtain a pointer to the Title attribute string for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* const char *GetTitle( AstFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetTitle protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the Title attribute string for +* a CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Title value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the CmpFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* If a Title attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestTitle( this ) ) { + result = (*parent_gettitle)( this_frame, status ); + +/* Otherwise, create a suitable default string and return a pointer to + this. */ + } else { + (void) sprintf( gettitle_buff, "%d-d compound coordinate system", + astGetNaxes( this ) ); + if ( astOK ) result = gettitle_buff; + } + +/* Return the result. */ + return result; + +} + +static int GetUseDefs( AstObject *this_object, int *status ) { +/* +* Name: +* GetUseDefs + +* Purpose: +* Get a value for the UseDefs attribute of a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GetUseDefs( AstCmpFrame *this, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetUseDefs method +* inherited from the Frame class). + +* Description: +* This function returns a value for the UseDefs attribute of a +* CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The UseDefs attribute value. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* If an UseDefs attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestUseDefs( this ) ) { + result = (*parent_getusedefs)( this_object, status ); + +/* Otherwise, use the UseDefs value in the first component Frame as the + default. */ + } else { + result = (*parent_getusedefs)( (AstObject *) this->frame1, status ); + } + +/* Return the result. */ + return result; +} + +static int GoodPerm( int ncoord_in, const int inperm[], + int ncoord_out, const int outperm[], int *status ) { +/* +* Name: +* GoodPerm + +* Purpose: +* Test if a PermMap will be non-null. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GoodPerm( int ncoord_in, const int inperm[], +* int ncoord_out, const int outperm[], int *status ) + +* Class Membership: +* CmpFrame member function. + +* Description: +* This function tests if a pair of permutation arrays will, when +* used to create a PermMap, result in a PermMap which has a +* non-null effect (i.e. one which is not simply equivalent to a +* unit Mapping). + +* Parameters: +* ncoord_in +* The number of input coordinates for the PermMap. +* inperm +* The input permutation array for the PermMap (with "ncoord_in" +* elements). +* ncoord_out +* The number of output coordinates for the PermMap. +* outperm +* The output permutation array for the PermMap (with +* "ncoord_out" elements). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the PermMap would be equivalent to a unit Mapping, +* otherwise one. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int axis; /* Loop counter for axes */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* First test if the number of input and output coordinates are + different. */ + result = ( ncoord_in != ncoord_out ); + +/* If they are not, examine the contents of the "inperm" array. */ + if ( !result ) { + for ( axis = 0; axis < ncoord_in; axis++ ) { + +/* We have a non-null Mapping if any element of this array selects an + output axis with a different index to the input axis (or selects an + invalid axis or a constant). */ + if ( inperm[ axis ] != axis ) { + result = 1; + break; + } + } + } + +/* If the Mapping still appears to be null, also examine the "outperm" + array in the same way. */ + if ( !result ) { + for ( axis = 0; axis < ncoord_out; axis++ ) { + if ( outperm[ axis ] != axis ) { + result = 1; + break; + } + } + } + +/* Return the result. */ + return result; +} + +void astInitCmpFrameVtab_( AstCmpFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitCmpFrameVtab + +* Purpose: +* Initialise a virtual function table for a CmpFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpframe.h" +* void astInitCmpFrameVtab( AstCmpFrameVtab *vtab, const char *name ) + +* Class Membership: +* CmpFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the CmpFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsACmpFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + mapping = (AstMappingVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_getusedefs = object->GetUseDefs; + object->GetUseDefs = GetUseDefs; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + mapping->RemoveRegions = RemoveRegions; + mapping->Simplify = Simplify; + mapping->Transform = Transform; + + parent_getdomain = frame->GetDomain; + frame->GetDomain = GetDomain; + + parent_gettitle = frame->GetTitle; + frame->GetTitle = GetTitle; + + parent_getepoch = frame->GetEpoch; + frame->GetEpoch = GetEpoch; + + parent_setepoch = frame->SetEpoch; + frame->SetEpoch = SetEpoch; + + parent_clearepoch = frame->ClearEpoch; + frame->ClearEpoch = ClearEpoch; + + parent_getdut1 = frame->GetDut1; + frame->GetDut1 = GetDut1; + + parent_setdut1 = frame->SetDut1; + frame->SetDut1 = SetDut1; + + parent_cleardut1 = frame->ClearDut1; + frame->ClearDut1 = ClearDut1; + + parent_getobslon = frame->GetObsLon; + frame->GetObsLon = GetObsLon; + + parent_setobslon = frame->SetObsLon; + frame->SetObsLon = SetObsLon; + + parent_clearobslon = frame->ClearObsLon; + frame->ClearObsLon = ClearObsLon; + + parent_getobslat = frame->GetObsLat; + frame->GetObsLat = GetObsLat; + + parent_setobslat = frame->SetObsLat; + frame->SetObsLat = SetObsLat; + + parent_clearobslat = frame->ClearObsLat; + frame->ClearObsLat = ClearObsLat; + + parent_getobsalt = frame->GetObsAlt; + frame->GetObsAlt = GetObsAlt; + + parent_setobsalt = frame->SetObsAlt; + frame->SetObsAlt = SetObsAlt; + + parent_clearobsalt = frame->ClearObsAlt; + frame->ClearObsAlt = ClearObsAlt; + + parent_angle = frame->Angle; + frame->Angle = Angle; + + parent_getsystem = frame->GetSystem; + frame->GetSystem = GetSystem; + + parent_getalignsystem = frame->GetAlignSystem; + frame->GetAlignSystem = GetAlignSystem; + + parent_clearalignsystem = frame->ClearAlignSystem; + frame->ClearAlignSystem = ClearAlignSystem; + + parent_overlay = frame->Overlay; + frame->Overlay = Overlay; + + parent_setactiveunit = frame->SetActiveUnit; + frame->SetActiveUnit = SetActiveUnit; + + parent_getactiveunit = frame->GetActiveUnit; + frame->GetActiveUnit = GetActiveUnit; + + parent_setframeflags = frame->SetFrameFlags; + frame->SetFrameFlags = SetFrameFlags; + + parent_getmaxaxes = frame->GetMaxAxes; + frame->GetMaxAxes = GetMaxAxes; + + parent_getminaxes = frame->GetMinAxes; + frame->GetMinAxes = GetMinAxes; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Cast = Cast; + mapping->Decompose = Decompose; + frame->Abbrev = Abbrev; + frame->ClearDirection = ClearDirection; + frame->ClearFormat = ClearFormat; + frame->ClearLabel = ClearLabel; + frame->ClearSymbol = ClearSymbol; + frame->ClearUnit = ClearUnit; + frame->Distance = Distance; + frame->Fields = Fields; + frame->Format = Format; + frame->FrameGrid = FrameGrid; + frame->Centre = Centre; + frame->Gap = Gap; + frame->GetAxis = GetAxis; + frame->GetDirection = GetDirection; + frame->GetFormat = GetFormat; + frame->GetLabel = GetLabel; + frame->GetNaxes = GetNaxes; + frame->GetPerm = GetPerm; + frame->GetSymbol = GetSymbol; + frame->GetUnit = GetUnit; + frame->IsUnitFrame = IsUnitFrame; + frame->Match = Match; + frame->Norm = Norm; + frame->NormBox = NormBox; + frame->Offset = Offset; + frame->PermAxes = PermAxes; + frame->PrimaryFrame = PrimaryFrame; + frame->Resolve = Resolve; + frame->ResolvePoints = ResolvePoints; + frame->SetAxis = SetAxis; + frame->SetDirection = SetDirection; + frame->SetFormat = SetFormat; + frame->SetLabel = SetLabel; + frame->SetSymbol = SetSymbol; + frame->SetUnit = SetUnit; + frame->SubFrame = SubFrame; + frame->TestDirection = TestDirection; + frame->TestFormat = TestFormat; + frame->TestLabel = TestLabel; + frame->TestSymbol = TestSymbol; + frame->TestUnit = TestUnit; + frame->Unformat = Unformat; + frame->ValidateSystem = ValidateSystem; + frame->SystemString = SystemString; + frame->SystemCode = SystemCode; + frame->MatchAxesX = MatchAxesX; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "CmpFrame", + "Compound coordinate system description" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int IsUnitFrame( AstFrame *this_frame, int *status ){ +/* +* Name: +* IsUnitFrame + +* Purpose: +* Is this Frame equivalent to a UnitMap? + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int IsUnitFrame( AstFrame *this, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astIsUnitFrame +* method inherited from the Frame class). + +* Description: +* This function returns a flag indicating if the supplied Frame is +* equivalent to a UnitMap when treated as a Mapping (note, the Frame +* class inherits from Mapping and therefore every Frame is also a Mapping). + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the supplied Frame is equivalent to +* a UnitMap when treated as a Mapping. + +*- +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Return the result. */ + return astIsUnitFrame( this->frame1 ) && astIsUnitFrame( this->frame2 ); +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->frame1, mode, extra, fail ); + if( !result ) result = astManageLock( this->frame2, mode, extra, fail ); + + return result; + +} +#endif + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astMatch +* method inherited from the Frame class). + +* Description: +* This function matches a "template" CmpFrame to a "target" Frame +* and determines whether it is possible to convert coordinates +* between them. If it is, a Mapping that performs the +* transformation is returned along with a new Frame that describes +* the coordinate system that results when this Mapping is applied +* to the "target" coordinate system. In addition, information is +* returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" Frame and +* "template" CmpFrame from which they are derived. + +* Parameters: +* template +* Pointer to the template CmpFrame. This describes the +* coordinate system (or set of possible coordinate systems) +* into which we wish to convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate +* system in which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case (i.e. if the +* target is of a more specialised class than the template). In +* this latter case, the target is cast down to the class of the +* template. NOTE, this argument is handled by the global method +* wrapper function "astMatch_", rather than by the class-specific +* implementations of this method. +* template_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* template CmpFrame axis from which it is derived. If it is not +* derived from any template axis, a value of -1 will be +* returned instead. +* target_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* target Frame axis from which it is derived. If it is not +* derived from any target axis, a value of -1 will be returned +* instead. +* map +* Address of a location where a pointer to a new Mapping will +* be returned if the requested coordinate conversion is +* possible. If returned, the forward transformation of this +* Mapping may be used to convert coordinates between the +* "target" Frame and the "result" Frame (see below) and the +* inverse transformation will convert in the opposite +* direction. +* result +* Address of a location where a pointer to a new Frame will be +* returned if the requested coordinate conversion is +* possible. If returned, this Frame describes the coordinate +* system that results from applying the returned Mapping +* (above) to the "target" coordinate system. In general, this +* Frame will combine attributes from (and will therefore be +* more specific than) both the target Frame and the template +* CmpFrame. In particular, when the template allows the +* possibility of transformaing to any one of a set of +* alternative coordinate systems, the "result" Frame will +* indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate +* conversion is possible. Otherwise zero is returned (this will +* not in itself result in an error condition). + +* Notes: +* - By default, the "result" Frame will have its number of axes +* and axis order determined by the "template" CmpFrame. However, +* if the PreserveAxes attribute of the template CmpFrame is +* non-zero, then the axis count and axis order of the "target" +* Frame will be used instead. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *template; /* Pointer to template CmpFrame structure */ + char *template_domain; /* Pointer to copy of template domain */ + const char *ptr; /* Pointer to domain string */ + const char *target_domain; /* Pointer to target domain string */ + int *axes1; /* Pointer to axis selection 1 */ + int *axes2; /* Pointer to axis selection 2 */ + int *used; /* Pointer to flags array */ + int axis2; /* Index for axis selection 2 */ + int axis; /* Index for axis arrays */ + int last_target; /* Last target axis association */ + int last_template; /* Last template axis associateion */ + int match; /* Match obtained (returned result)? */ + int maxax1; /* MaxAxes attribute for component 1 */ + int maxax2; /* MaxAxes attribute for component 2 */ + int maxax; /* Max axes that can be matched by template */ + int minax1; /* MinAxes attribute for component 1 */ + int minax2; /* MinAxes attribute for component 2 */ + int minax; /* Min axes that can be matched by template */ + int naxes1; /* Number of axes assigned to component 1 */ + int naxes2; /* Number of axes assigned to component 2 */ + int naxes; /* Total number of target axes */ + int naxes_max1; /* First estimate of naxes_max */ + int naxes_max2; /* Second estimate of naxes_max */ + int naxes_max; /* Max number of axes to match component 1 */ + int naxes_min1; /* First estimate of naxes_min */ + int naxes_min2; /* Second estimate of naxes_min */ + int naxes_min; /* Min number of axes to match component 1 */ + int permute; /* Permute attribute for template */ + int result_naxes; /* Number of result Frame axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the template CmpFrame structure. */ + template = (AstCmpFrame *) template_frame; + +/* Further initialisation to avoid compiler warnings. */ + naxes_min = 0; + naxes_max = 0; + +/* Obtain the maximum number of axes that the template CmpFrame, and each + component Frame of the template CmpFrame, can match. If the MaxAxes + attribute is set for the template, use it and assume that each + component Frame can match any number of axes. */ + if( astTestMaxAxes( template ) ) { + maxax = astGetMaxAxes( template ); + maxax1 = 100000; + maxax2 = 100000; + } else { + maxax1 = astGetMaxAxes( template->frame1 ); + maxax2 = astGetMaxAxes( template->frame2 ); + maxax = maxax1 + maxax2; + } + +/* Do the same for the minimum number of axes that can be matched by the + template CmpFrame. */ + if( astTestMinAxes( template ) ) { + minax = astGetMinAxes( template ); + minax1 = 1; + minax2 = 1; + } else { + minax1 = astGetMinAxes( template->frame1 ); + minax2 = astGetMinAxes( template->frame2 ); + minax = minax1 + minax2; + } + +/* Obtain the number of axes in the target Frame and test to see if it + is possible for the template to match it on the basis of axis + counts. */ + naxes = astGetNaxes( target ); + match = ( naxes >= minax && naxes <= maxax ); + +/* The next requirement is that all the frames have some axes. */ + if( naxes == 0 || maxax1 == 0 || maxax2 == 0 ) match = 0; + +/* The next requirement is that if the template CmpFrame has its + Domain attribute defined, then the target Frame must also have the + same Domain (although it need not be set - the default will + do). First check if the template has a domain. */ + if ( astOK && match ) { + if ( astTestDomain( template ) ) { + +/* Obtain a pointer to the template domain. Then allocate memory and + make a copy of it (this is necessary as we will next inquire the + domain of the target and may over-write the buffer holding the + template's domain). */ + ptr = astGetDomain( template ); + if ( astOK ) { + template_domain = astStore( NULL, ptr, + strlen( ptr ) + (size_t) 1 ); + +/* Obtain a pointer to the target domain. */ + target_domain = astGetDomain( target ); + +/* Compare the domain strings for equality. Then free the memory + allocated above. */ + match = astOK && !strcmp( template_domain, target_domain ); + template_domain = astFree( template_domain ); + } + } + } + +/* If a match still appears possible, determine the minimum number of + target axes that will have to match the first component Frame of + the template CmpFrame. */ + if ( astOK && match ) { + naxes_min1 = minax1; + naxes_min2 = naxes - maxax2; + naxes_min = ( naxes_min1 > naxes_min2 ) ? naxes_min1 : naxes_min2; + +/* Also determine the maximum number of target axes that may match + this component of the template. */ + naxes_max1 = maxax1; + naxes_max2 = naxes - minax2; + naxes_max = ( naxes_max1 < naxes_max2 ) ? naxes_max1 : naxes_max2; + +/* No match possible if the number of axes are inconsistent. */ + if( naxes_min > naxes_max ) match = 0; + } + +/* If a match is still possible, allocate workspace. */ + if( match ) { + axes1 = astMalloc( sizeof( int ) * (size_t) naxes ); + axes2 = astMalloc( sizeof( int ) * (size_t) naxes ); + used = astMalloc( sizeof( int ) * (size_t) naxes ); + +/* Obtain the value of the template's Permute attribute. */ + permute = astGetPermute( template ); + if ( astOK ) { + +/* Loop to consider all possible choices of the number of template + axes that might match the first component Frame of the template, + and derive the corresponding number of axes that must match the + second component at the same time. */ + for ( naxes1 = naxes_max; naxes1 >= naxes_min; naxes1-- ) { + naxes2 = naxes - naxes1; + +/* Initialise the selection of target axes that we will attempt to + match against the first template component (to [0,1,2,...]). */ + for ( axis = 0; axis < naxes1; axis++ ) axes1[ axis ] = axis; + +/* Loop to consider all possible selections with this number of axes, + until a match is found. */ + while ( 1 ) { + +/* Initialise an array of flags to zero for each target axis. Then set + the flag to 1 for each axis which is in the first selection.*/ + for ( axis = 0; axis < naxes; axis++ ) used[ axis ] = 0; + for( axis = 0; axis < naxes1; axis++ ) { + used[ axes1[ axis ] ] = 1; + } + +/* Generate the second selection by including all target axes that are + not in the first selection. */ + axis2 = 0; + for ( axis = 0; axis < naxes; axis++ ) { + if ( !used[ axis ] ) axes2[ axis2++ ] = axis; + } + +/* Attempt to match the target axes partitioned in this way to the two + template components. */ + match = PartMatch( template, target, matchsub, + naxes1, axes1, naxes2, axes2, + template_axes, target_axes, map, result, status ); + +/* If a match was obtained but the template's Permute attribute is zero, + then we must check to see if the match involves permuting the target + axes. */ + if ( astOK && match && !permute ) { + +/* Obtain the number of result Frame axes. */ + result_naxes = astGetNaxes( *result ); + +/* Loop to check the target and template axis associations for all the + result Frame axes. The match will only be accepted if both of these + are monotonically increasing (indicating no axis permutation) after + allowing for any absent associations . */ + last_template = -1; + last_target = -1; + for ( axis = 0; axis < result_naxes; axis++ ) { + +/* Check the template axis association against the previous value, + omitting any axes witout valid associations. */ + if ( ( *template_axes )[ axis ] != -1 ) { + if ( ( *template_axes )[ axis ] <= last_template ) { + match = 0; + break; + +/* Update the previous association value. */ + } else { + last_template = ( *template_axes )[ axis ]; + } + } + +/* Repeat this process for the target axis associations. */ + if ( ( *target_axes )[ axis ] != -1 ) { + if ( ( *target_axes )[ axis ] <= last_target ) { + match = 0; + break; + } else { + last_target = ( *target_axes )[ axis ]; + } + } + } + +/* If the match was rejected because it involves an axis permutation, + then free the allocated memory and annul the Object pointers + associated with the match. */ + if ( !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + *map = astAnnul( *map ); + *result = astAnnul( *result ); + } + } + +/* If an error occurred or a match was found, quit searching, + otherwise generate the next axis selection and try that + instead. Quit if there are no more selections to try. */ + if ( !astOK || match || + !GenAxisSelection( naxes, naxes1, axes1, status ) ) break; + } + +/* Quit the outer loop if an error occurs or a match is found. */ + if ( !astOK || match ) break; + } + } + +/* Free the workspace arrays. */ + axes1 = astFree( axes1 ); + axes2 = astFree( axes2 ); + used = astFree( used ); + } + +/* If the target did not match the supplied template CmpFrame, see if it + will match either of the component Frames. First try matching it against + the first component Frame. */ + if( !match ) match = ComponentMatch( template, target, matchsub, 0, + template_axes, target_axes, map, result, + status ); + +/* If we still dont have a mcth, try matching it against the second + component Frame. */ + if( !match ) match = ComponentMatch( template, target, matchsub, 1, + template_axes, target_axes, map, + result, status ); + +/* If an error occurred, free all allocated memory, annul the result + Object pointers and clear all returned values. */ + if ( !astOK ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + *map = astAnnul( *map ); + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void MatchAxesX( AstFrame *frm2_frame, AstFrame *frm1, int *axes, + int *status ) { +/* +* Name: +* MatchAxesX + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void MatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes ) +* int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astMatchAxesX +* method inherited from the Frame class). + +* Description: +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +* frm2 +* Pointer to the second Frame. +* frm1 +* Pointer to the first Frame. +* axes +* Pointer to an integer array in which to return the indices of +* the axes (within the first Frame) that correspond to each axis +* within the second Frame. Axis indices start at 1. A value of zero +* will be stored in the returned array for each axis in the second +* Frame that has no corresponding axis in the first Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the second Frame. +* status +* Pointer to inherited status value. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping +* can be found between them using astFindFrame or astConvert. Thus, +* "corresponding axes" are not necessarily identical. For instance, +* SkyFrame axes in two Frames will match even if they describe +* different celestial coordinate systems +*/ + +/* Local Variables: */ + AstCmpFrame *frm2; + const int *perm; + int *work; + int i; + int nax2; + int nax1; + int nax; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpFrame. */ + frm2 = (AstCmpFrame *) frm2_frame; + +/* Get the number of axes in the two component Frames, and the total + number of axes in the CmpFrame. */ + nax2 = astGetNaxes( frm2->frame1 ); + nax1 = astGetNaxes( frm2->frame2 ); + nax = nax2 + nax1; + +/* Allocate a work array to hold the unpermuted axis indices */ + work = astMalloc( sizeof( int )*nax ); + if( astOK ) { + +/* Use the astMatchAxes method to match axes in the first component Frame + within CmpFrame "frm2". Write the associated axis indices into the first + part of the work array. */ + astMatchAxes( frm1, frm2->frame1, work ); + +/* Use the MatchAxes method to match axes in the second component + Frame. Write the associated axis indices into the work array + following the end of the values already in there. */ + astMatchAxes( frm1, frm2->frame2, work + nax2 ); + +/* Obtain a pointer to the CmpFrame's axis permutation array. The index + into "perm" represents the external axis index, and the value held in + each element of "perm" represents the corresponding internal axis index. */ + perm = astGetPerm( frm2 ); + if( astOK ) { + +/* Copy the frm2 axis indices from the work array into the returned "axes" + array, permuting their order into the external axis order of the + CmpFrame. */ + for( i = 0; i < nax; i++ ) axes[ i ] = work[ perm[ i ] ]; + } + +/* Free resources */ + work = astFree( work ); + } +} + +static void Norm( AstFrame *this_frame, double value[], int *status ) { +/* +* Name: +* Norm + +* Purpose: +* Normalise a set of CmpFrame coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void Norm( AstAxis *this, double value[], int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astNorm method +* inherited from the Frame class). + +* Description: +* This function converts a set of CmpFrame coordinate values, +* which might potentially be unsuitable for display to a user (for +* instance, may lie outside the expected range of values) into a +* set of acceptable alternative values suitable for display. + +* Parameters: +* this +* Pointer to the CmpFrame. +* value +* An array of double, with one element for each CmpFrame axis. +* This should contain the initial set of coordinate values, +* which will be modified in place. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + const int *perm; /* Axis permutation array */ + double *v; /* Pointer to permuted coordinates */ + int axis; /* Loop counter for axes */ + int naxes1; /* Number of axes in frame1 */ + int naxes; /* Number of axes in CmpFrame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain a pointer to the CmpFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* Obtain the number of axes in the CmpFrame and in the first + component Frame. */ + naxes = astGetNaxes( this ); + naxes1 = astGetNaxes( this->frame1 ); + +/* Allocate memory to hold the permuted coordinates. */ + v = astMalloc( sizeof( double ) * (size_t) naxes ); + if ( astOK ) { + +/* Permute the coordinates using the CmpFrame's axis permutation array + to put them into the order required internally (i.e. by the two + component Frames). */ + for ( axis = 0; axis < naxes; axis++ ) v[ perm[ axis ] ] = value[ axis ]; + +/* Invoke the astNorm method of both component Frames, passing the + relevant (permuted) coordinate values for normalisation. */ + astNorm( this->frame1, v ); + astNorm( this->frame2, v + naxes1 ); + +/* Copy the normalised values back into the original coordinate array, + un-permuting them in the process. */ + for ( axis = 0; axis < naxes; axis++ ) value[ axis ] = v[ perm[ axis ] ]; + } + +/* Free the memory used for the permuted coordinates. */ + v = astFree( v ); +} + +static void NormBox( AstFrame *this_frame, double lbnd[], double ubnd[], + AstMapping *reg, int *status ) { +/* +* Name: +* NormBox + +* Purpose: +* Extend a box to include effect of any singularities in the Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void astNormBox( AstFrame *this, double lbnd[], double ubnd[], +* AstMapping *reg, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astNormBox method inherited +* from the Frame class). + +* Description: +* This function modifies a supplied box to include the effect of any +* singularities in the co-ordinate system represented by the Frame. +* For a normal Cartesian coordinate system, the box will be returned +* unchanged. Other classes of Frame may do other things. For instance, +* a SkyFrame will check to see if the box contains either the north +* or south pole and extend the box appropriately. + +* Parameters: +* this +* Pointer to the Frame. +* lbnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* lower axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* ubnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* upper axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* reg +* A Mapping which should be used to test if any singular points are +* inside or outside the box. The Mapping should leave an input +* position unchanged if the point is inside the box, and should +* set all bad if the point is outside the box. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstCmpFrame *this; + AstCmpMap *m1; + AstCmpMap *m2; + AstCmpMap *m3; + AstCmpMap *m4; + AstCmpMap *m5; + AstCmpMap *m6; + AstPermMap *pm1; + AstPermMap *pm2; + AstPermMap *pm3; + const int *perm; + double *vl; + double *vu; + int *inperm; + int axis; + int naxes1; + int naxes; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain a pointer to the CmpFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* Obtain the number of axes in the CmpFrame and in the first + component Frame. */ + naxes = astGetNaxes( this ); + naxes1 = astGetNaxes( this->frame1 ); + +/* Allocate memory to hold the permuted coordinates. */ + vl = astMalloc( sizeof( double ) * (size_t) naxes ); + vu = astMalloc( sizeof( double ) * (size_t) naxes ); + inperm = astMalloc( sizeof( int ) * (size_t) naxes ); + if( inperm ) { + +/* Permute the coordinates using the CmpFrame's axis permutation array + to put them into the order required internally (i.e. by the two + component Frames). */ + for ( axis = 0; axis < naxes; axis++ ) { + vl[ perm[ axis ] ] = lbnd[ axis ]; + vu[ perm[ axis ] ] = ubnd[ axis ]; + } + +/* Create a PermMap with a forward transformation which reorders a position + which uses internal axis ordering into a position which uses external axis + ordering. */ + pm1 = astPermMap( naxes, NULL, naxes, perm, NULL, "", status ); + +/* Put it in front of the supplied Mapping. The combination transforms an + input internal position into an output external position. */ + m1 = astCmpMap( pm1, reg, 1, "", status ); + +/* Invert it and add it to the end. This combination now transforms an + input internal position into an output internal position. */ + astInvert( pm1 ); + m2 = astCmpMap( m1, pm1, 1, "", status ); + +/* Create a PermMap with a forward transformation which copies the lower + naxes1 inputs to the same outputs, and supplies AST__BAD for the other + outputs. */ + for( axis = 0; axis < naxes1; axis++ ) inperm[ axis ] = axis; + pm2 = astPermMap( naxes1, inperm, naxes, NULL, NULL, "", status ); + +/* Put it in front of the Mapping created above, then invert it and add + it at the end. */ + m3 = astCmpMap( pm2, m2, 1, "", status ); + astInvert( pm2 ); + m4 = astCmpMap( m3, pm2, 1, "", status ); + +/* Invoke the astNormBox method of the first component Frame, passing the + relevant (permuted) coordinate values for normalisation. */ + astNormBox( this->frame1, vl, vu, m4 ); + +/* Create a PermMap with a forward transformation which copies the upper + inputs to the same outputs, and supplied AST__BAD for the other + outputs. */ + for( axis = 0; axis < naxes - naxes1; axis++ ) inperm[ axis ] = naxes1 + axis; + pm3 = astPermMap( naxes1, inperm, naxes, NULL, NULL, "", status ); + +/* Put it in front of the Mapping created above, then invert it and add + it at the end. */ + m5 = astCmpMap( pm3, m2, 1, "", status ); + astInvert( pm3 ); + m6 = astCmpMap( m5, pm3, 1, "", status ); + +/* Invoke the astNormBox method of the seond component Frame, passing the + relevant (permuted) coordinate values for normalisation. */ + astNormBox( this->frame2, vl + naxes1, vu + naxes1, m6 ); + +/* Copy the normalised values back into the original coordinate array, + un-permuting them in the process. */ + for ( axis = 0; axis < naxes; axis++ ) { + lbnd[ axis ] = vl[ perm[ axis ] ]; + ubnd[ axis ] = vu[ perm[ axis ] ]; + } + +/* Free resources. */ + pm1 = astAnnul( pm1 ); + pm2 = astAnnul( pm2 ); + pm3 = astAnnul( pm3 ); + m1 = astAnnul( m1 ); + m2 = astAnnul( m2 ); + m3 = astAnnul( m3 ); + m4 = astAnnul( m4 ); + m5 = astAnnul( m5 ); + m6 = astAnnul( m6 ); + } + inperm = astFree( inperm ); + vl = astFree( vl ); + vu = astFree( vu ); +} + +static void Offset( AstFrame *this_frame, const double point1[], + const double point2[], double offset, double point3[], int *status ) { +/* +* Name: +* Offset + +* Purpose: +* Calculate an offset along a geodesic curve. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void Offset( AstFrame *this, +* const double point1[], const double point2[], +* double offset, double point3[], int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astOffset method +* inherited from the Frame class). + +* Description: +* This function finds the CmpFrame coordinate values of a point +* which is offset a specified distance along the geodesic curve +* between two other points. + +* Parameters: +* this +* Pointer to the CmpFrame. +* point1 +* An array of double, with one element for each CmpFrame axis. +* This should contain the coordinates of the point marking the +* start of the geodesic curve. +* point2 +* An array of double, with one element for each CmpFrame axis. +* This should contain the coordinates of the point marking the +* end of the geodesic curve. +* offset +* The required offset from the first point along the geodesic +* curve. If this is positive, it will be towards the second +* point. If it is negative, it will be in the opposite +* direction. This offset need not imply a position lying +* between the two points given, as the curve will be +* extrapolated if necessary. +* point3 +* An array of double, with one element for each CmpFrame axis +* in which the coordinates of the required point will be +* returned. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - "Bad" coordinate values will also be returned if the two +* points supplied are coincident (or otherwise fail to uniquely +* specify a geodesic curve) but the requested offset is non-zero. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double *p1; /* Permuted coordinates for point1 */ + double *p2; /* Permuted coordinates for point2 */ + double *p3; /* Permuted coordinates for point3 */ + double dist1; /* Distance between input points in frame1 */ + double dist2; /* Distance between input points in frame2 */ + double dist; /* Total distance between input points */ + double offset1; /* Offset distance required in frame1 */ + double offset2; /* Offset distance required in frame2 */ + int axis; /* Loop counter for axes */ + int bad; /* Set bad output coordinates? */ + int naxes1; /* Number of axes in frame1 */ + int naxes; /* Total number of axes in CmpFrame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain the number of axes in the CmpFrame. */ + naxes = astGetNaxes( this ); + +/* Obtain a pointer to the CmpFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* Allocate workspace. */ + p1 = astMalloc( sizeof( double ) * (size_t) naxes ); + p2 = astMalloc( sizeof( double ) * (size_t) naxes ); + p3 = astMalloc( sizeof( double ) * (size_t) naxes ); + +/* Initialise variables to avoid compiler warnings. */ + dist1 = 0.0; + dist2 = 0.0; + offset1 = 0.0; + offset2 = 0.0; + naxes1 = 0; + +/* Initialise a flag to indicate whether "bad" coordinates should be + returned. */ + bad = 0; + +/* Check that all the coordinates of both input points are OK. If not, + set the "bad" flag and quit checking. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + if ( ( point1[ axis ] == AST__BAD ) || + ( point2[ axis ] == AST__BAD ) ) { + bad = 1; + break; + +/* If the coordinates are OK, apply the axis permutation array to + obtain them in the required order. */ + } else { + p1[ perm[ axis ] ] = point1[ axis ]; + p2[ perm[ axis ] ] = point2[ axis ]; + } + } + } + +/* If OK, obtain the number of axes in the first component Frame. */ + if ( astOK && !bad ) { + naxes1 = astGetNaxes( this->frame1 ); + +/* Project the two input points into the two component Frames and + determine the distance between the points in each Frame. */ + dist1 = astDistance( this->frame1, p1, p2 ); + dist2 = astDistance( this->frame2, p1 + naxes1, p2 + naxes1 ); + +/* Check that the returned distances are not bad. */ + if ( astOK ) bad = ( ( dist1 == AST__BAD ) || ( dist2 == AST__BAD ) ); + } + +/* If OK, calculate the total distance between the two points. */ + if ( astOK && !bad ) { + dist = sqrt( dist1 * dist1 + dist2 * dist2 ); + +/* If the points are co-incident, but "offset" is non-zero, then set + the "bad" flag. */ + if ( dist == 0.0 ) { + if ( offset != 0.0 ) { + bad = 1; + +/* Otherwise, set the offset distance required in each Frame to + zero. */ + } else { + offset1 = 0.0; + offset2 = 0.0; + } + +/* If the points are not co-incident, divide the total offset required + between each component Frame in such a way that the path being + followed will pass through the second point. */ + } else { + offset1 = offset * dist1 / dist; + offset2 = offset * dist2 / dist; + } + } + +/* If OK, apply the separate offsets to each component Frame. */ + if ( astOK && !bad ) { + astOffset( this->frame1, p1, p2, offset1, p3 ); + astOffset( this->frame2, p1 + naxes1, p2 + naxes1, offset2, + p3 + naxes1 ); + +/* Copy the resulting coordinates into the output array "point3", + permuting them back into the required order. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + point3[ axis ] = p3[ perm[ axis ] ]; + +/* If any of the result coordinates is bad, set the "bad" flag and + quit copying. */ + if ( point3[ axis ] == AST__BAD ) { + bad = 1; + break; + } + } + } + } + +/* Free the workspace arrays. */ + p1 = astFree( p1 ); + p2 = astFree( p2 ); + p3 = astFree( p3 ); + +/* If no error has occurred, but bad coordinates must be returned, + then set these in the output array. */ + if ( astOK && bad ) { + for ( axis = 0; axis < naxes; axis++ ) point3[ axis ] = AST__BAD; + } +} + +static void Overlay( AstFrame *template_frame, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template CmpFrame on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astOverlay +* method inherited from the Frame class). + +* Description: +* This function overlays attributes from a CmpFrame on to another Frame, +* so as to over-ride selected attributes of that second Frame. Normally +* only those attributes which have been specifically set in the template +* will be transferred. This implements a form of defaulting, in which +* a Frame acquires attributes from the template, but retains its +* original attributes (as the default) if new values have not previously +* been explicitly set in the template. + +* Parameters: +* template +* Pointer to the template CmpFrame, for whose current Frame +* values should have been explicitly set for any attribute +* which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of +* the "result" Frame (see below). For each axis in the result +* frame, the corresponding element of this array should contain +* the (zero-based) index of the axis in the current Frame of +* the template CmpFrame to which it corresponds. This array is +* used to establish from which template Frame axis any +* axis-dependent attributes should be obtained. +* +* If any axis in the result Frame is not associated with a +* template Frame axis, the corresponding element of this array +* should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstCmpFrame *res; /* Pointer to the result CmpFrame structure */ + AstCmpFrame *template; /* Pointer to the template CmpFrame structure */ + AstFrame *sub1; /* Template subframe for 1st result subframe */ + AstFrame *sub2; /* Template subframe for 2nd result subframe */ + const int *perm; /* Template axis permutation array */ + const int *rperm; /* Result axis permutation array */ + int *axes1; /* Axis associations with template frame1 */ + int *axes2; /* Axis associations with template frame2 */ + int done; /* Have attributes been overlayed yet? */ + int i; /* Index of result axis */ + int icmp; /* Internal template axis number */ + int isfirst; /* Res. subframe -> 1st template subframe? */ + int issecond; /* Res. subframe -> 2nd template subframe? */ + int j; /* Index of template axis */ + int nc1; /* Number of axes in template frame1 */ + int nres1; /* Number of axes in first result subframe */ + int nres2; /* Number of axes in second result subframe */ + int nres; /* Number of axes in result Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + template = (AstCmpFrame *) template_frame; + +/* Get the axis permutation array for the template CmpFrame. */ + perm = astGetPerm( template ); + +/* Get the number of axes in the first component Frame in the template + CmpFrame. */ + nc1 = astGetNaxes( template->frame1 ); + +/* Indicate we have not yet overlayed any attributes. */ + done = 0; + +/* If the result Frame is a CmpFrame... */ + if( astIsACmpFrame( result ) ) { + +/* Get the number of axes in the two component Frames of the result CmpFrame. */ + res = (AstCmpFrame *) result; + nres1 = astGetNaxes( res->frame1 ); + nres2 = astGetNaxes( res->frame2 ); + +/* Get the total number of axes in the result CmpFrame. */ + nres = nres1 + nres2; + +/* Get the axis permutation array for the result CmpFrame. */ + rperm = astGetPerm( result ); + +/* Allocate memory for two new axes arrays, one for each result sub-frame. */ + axes1 = astMalloc( sizeof(int)*(size_t)nres1 ); + axes2 = astMalloc( sizeof(int)*(size_t)nres2 ); + if( astOK ) { + +/* Assume that there is a 1-to-1 correspondence between axes in the + subframes of the result and template CmpFrame. That is, all the axes + in each result sub-frame are overlayed from the same template sub-frame. */ + done = 1; + +/* Loop round each axis in the first result sub-frame. */ + isfirst = 0; + issecond = 0; + for( i = 0; i < nres1; i++ ) { + +/* Find the external result CmpFrame axis index (j) for internal axis i. */ + for( j = 0; j < nres; j++ ) { + if( rperm[ j ] == i ) break; + } + +/* Get the internal axis number within the template CmpFrame which + provides attribute values for the current result axis. */ + icmp = perm[ template_axes ? template_axes[ j ] : j ]; + +/* If this template axis is in the first template subframe, store the + corresponding internal frame axis index in "axes1" and set a flag + indicating that the first result subframe corresponds to the first + template subframe. If the correspondance has already been established, + but is broken by this axis, then set "done" false and exit the axis + loop. */ + if( icmp < nc1 ) { + if( issecond ) { + done = 0; + break; + } else { + isfirst = 1; + axes1[ i ] = icmp; + } + + } else { + if( isfirst ) { + done = 0; + break; + } else { + issecond = 1; + axes1[ i ] = icmp - nc1; + } + } + } + +/* Save a pointer to the template subframe which is associated with the first + result subframe.*/ + sub1 = isfirst ? template->frame1 :template->frame2; + +/* Now do the same for the axes in the second result sub-frame. */ + isfirst = 0; + issecond = 0; + for( i = 0; i < nres2; i++ ) { + for( j = 0; j < nres; j++ ) { + if( rperm[ j ] == i + nres1 ) break; + } + + icmp = perm[ template_axes ? template_axes[ j ] : j ]; + + if( icmp < nc1 ) { + if( issecond ) { + done = 0; + break; + } else { + isfirst = 1; + axes2[ i ] = icmp; + } + + } else { + if( isfirst ) { + done = 0; + break; + } else { + issecond = 1; + axes2[ i ] = icmp - nc1; + } + } + } + +/* Save a pointer to the template subframe which is associated with the + second result subframe.*/ + sub2 = isfirst ? template->frame1 :template->frame2; + +/* If the two used template subframes are the same, something has gone + wrong. */ + if( sub1 == sub2 ) done = 0; + +/* If all axes within each result subframe are associated with the same + template subframe we continue to use the subframe astOverlay methods. */ + if( done ) { + +/* Overlay the first result subframe. */ + astOverlay( sub1, axes1, res->frame1 ); + astOverlay( sub2, axes2, res->frame2 ); + } + } + +/* Free the axes arrays. */ + axes1 = astFree( axes1 ); + axes2 = astFree( axes2 ); + } + +/* If we have not yet overlayed any attributes... */ + if( !done ) { + +/* Get the number of axes in the result Frame. */ + nres = astGetNaxes( result ); + +/* Allocate memory for two new template_axes arrays. */ + axes1 = astMalloc( sizeof(int)*(size_t)nres ); + axes2 = astMalloc( sizeof(int)*(size_t)nres ); + if( astOK ) { + +/* Set elements to -1 in "axes1" if they do not refer to the first component + Frame in the template CmpFrame. Likewise, set elements to -1 in "axes2" if + they do not refer to the second component Frame in the template CmpFrame. */ + for( i = 0; i < nres; i++ ) { + +/* Get the internal axis number within the template CmpFrame which + provides attribute values for the current results axis. */ + icmp = perm[ template_axes ? template_axes[ i ] : i ]; + +/* If this template axis is in the first component Frame, store the + corresponding internal frame axis index in "axes1" and set "axis2" to + -1. */ + if( icmp < nc1 ) { + axes1[ i ] = icmp; + axes2[ i ] = -1; + +/* If this template axis is in the second component Frame, store the + corresponding internal frame axis index in "axes2" and set "axis1" to + -1. */ + } else { + axes1[ i ] = -1; + axes2[ i ] = icmp - nc1; + } + } + +/* Now use the astOverlay methods of the two component Frames to overlay + attributes onto the appropriate axes of the results Frame. */ + astOverlay( template->frame1, axes1, result ); + astOverlay( template->frame2, axes2, result ); + } + +/* Free the axes arrays. */ + axes1 = astFree( axes1 ); + axes2 = astFree( axes2 ); + } +} + +static void PartitionSelection( int nselect, const int select[], + const int perm[], int naxes1, int naxes2, + int iframe[], int following, int *status ) { +/* +* Name: +* PartitionSelection + +* Purpose: +* Partition a CmpFrame axis selection into two component Frame selections. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void PartitionSelection( int nselect, const int select[], +* const int perm[], int naxes1, int naxes2, +* int iframe[], int following, int *status ) + +* Class Membership: +* CmpFrame member function. + +* Description: +* This function accepts an array containing the indices of axes +* which are to be selected from a CmpFrame, and partitions these +* indices to indicate which must be selected from each of the +* CmpFrame's two component Frames. +* +* This operation is trivial if all the axis indices supplied refer +* to valid CmpFrame axes. However, if some of them do not (these +* should generally be set to -1), this function assigns these +* "extra" axes to one or other of the component Frames by +* associating them with the axes selected immediately before (or +* after). Allowance is made for the possibility that several +* consecutive selected axes may be "extra" ones, or even that they +* may all be. The CmpFrame's axis permutation array is also taken +* into account. + +* Parameters: +* nselect +* The number of axes to be selected. +* select +* An array containing the (zero-based) indices of the CmpFrame +* axes to be selected, or -1 where "extra" axes are required. +* perm +* The CmpFrame's axis permutation array. +* naxes1 +* The number of axes in the CmpFrame's first component Frame. +* naxes2 +* The number of axes in the CmpFrame's second component Frame. +* iframe +* An array with "nselect" elements in which to return a number +* (either 1 or 2) to indicate to which component Frame (frame1 +* or frame2) each selected axis belongs. +* following +* If this is zero, "extra" axes will be associated with the +* preceding normal selected axis which appears in the "select" +* array (if any), otherwise they will be associated with the +* following normal selected axis. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int end; /* Loop termination value */ + int ifr; /* Choice of Frame for next "extra" axis */ + int inc; /* Loop increment value */ + int iselect; /* Loop counter for axis selections */ + int naxes; /* Total number of CmpFrame axes */ + int start; /* Loop starting value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the total number of CmpFrame axes. */ + naxes = naxes1 + naxes2; + +/* Loop through each axis selection and identify those which refer to + valid CmpFrame axes. */ + for ( iselect = 0; iselect < nselect; iselect++ ) { + if ( ( select[ iselect ] >= 0 ) && ( select[ iselect ] < naxes ) ) { + +/* For these selections (only), enter a flag into the "iframe" array + which indicates which component Frame the selected axis resides + in. Permute each axis index before deciding this. */ + iframe[ iselect ] = 1 + ( perm[ select[ iselect ] ] >= naxes1 ); + } + } + +/* Set up a start, end, and increment value for looping through the + array of axis selections forward (if "following" is 0) or backwards + (otherwise). */ + start = following ? nselect - 1 : 0; + end = following ? -1 : nselect; + inc = following ? -1 : 1; + +/* Set the default choice of component Frame. This will be used if + there are no normal axis selections to guide the choice at all. */ + ifr = following ? 2 : 1; + +/* Search for the first normal axis selection so that we can replace + this default, if possible. (Here, "first" actually means "last" if + "following" is set, because we will then be scanning the array of + selections in reverse.) */ + for ( iselect = start; iselect != end; iselect += inc ) { + +/* Identify normal axis selections and obtain the choice of component + Frame for the first one found. The resulting value "ifr" will be + used for initial (or final, if "following" is set) "extra" + selections for which no earlier normal selection exists - see + below. */ + if ( ( select[ iselect ] >= 0 ) && ( select[ iselect ] < naxes ) ) { + ifr = iframe[ iselect ]; + break; + } + } + +/* Loop through the selections again to allocate a choice of Frame to + the "extra" selected axes. */ + for ( iselect = start; iselect != end; iselect += inc ) { + +/* Remember the component Frame used by the most recently encountered + normal axis selection. */ + if ( ( select[ iselect ] >= 0 ) && ( select[ iselect ] < naxes ) ) { + ifr = iframe[ iselect ]; + +/* For "extra" axes, allocate the most recent Frame choice. The + default choice (found above) will apply if no "most recent" choice + has been encountered. */ + } else { + iframe[ iselect ] = ifr; + } + } +} + +static int PartMatch( AstCmpFrame *template, AstFrame *target, + int matchsub, int naxes1, const int axes1[], + int naxes2, const int axes2[], + int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* PartMatch + +* Purpose: +* Match a CmpFrame template to partitioned target axes. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int PartMatch( AstCmpFrame *template, AstFrame *target, +* int matchsub, int naxes1, const int axes1[], +* int naxes2, const int axes2[], +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* CmpFrame member function. + +* Description: +* This function matches a "template" CmpFrame to a "target" Frame +* and determines whether it is possible to convert coordinates +* between them. If it is, a Mapping that performs the +* transformation is returned along with a new Frame that describes +* the coordinate system that results when this Mapping is applied +* to the "target" coordinate system. In addition, information is +* returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" Frame and +* "template" CmpFrame from which they are derived. +* +* To simplify the matching process for a CmpFrame template, this +* function requires the caller to specify how the axes of the +* target Frame should be partitioned between the two component +* Frames of the template. The function attempts to find a match +* using this axis partitioning only. In general, the way in which +* the target axes must be partitioned is not known in advance, so +* this function must be invoked several times with alternative +* partitioning before a match will be found. + +* Parameters: +* template +* Pointer to the template CmpFrame. This describes the +* coordinate system (or set of possible coordinate systems) +* into which we wish to convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate +* system in which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case (i.e. if the +* target is of a more specialised class than the template). In +* this latter case, the target is cast down to the class of the +* template. +* naxes1 +* The number of target axes to be matched against the first +* component Frame of the template CmpFrame. +* axes1 +* An array with "naxes1" elements containing the (zero-based) +* indices of the target axes to be matched against the first +* component Frame. Order is not significant. +* naxes2 +* The number of target axes to be matched against the second +* component Frame of the template CmpFrame. +* axes2 +* An array with "naxes2" elements containing the (zero-based) +* indices of the target axes to be matched against the second +* component Frame. Order is not significant. +* template_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* template CmpFrame axis from which it is derived. If it is not +* derived from any template axis, a value of -1 will be +* returned instead. +* target_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* target Frame axis from which it is derived. If it is not +* derived from any target Frame axis, a value of -1 will be +* returned instead. +* map +* Address of a location where a pointer to a new Mapping will +* be returned if the requested coordinate conversion is +* possible. If returned, the forward transformation of this +* Mapping may be used to convert coordinates between the +* "target" Frame and the "result" Frame (see below) and the +* inverse transformation will convert in the opposite +* direction. +* result +* Address of a location where a pointer to a new Frame will be +* returned if the requested coordinate conversion is +* possible. If returned, this Frame describes the coordinate +* system that results from applying the returned Mapping +* (above) to the "target" coordinate system. In general, this +* Frame will combine attributes from (and will therefore be +* more specific than) both the target Frame and the template +* CmpFrame. In particular, when the template allows the +* possibility of transformaing to any one of a set of +* alternative coordinate systems, the "result" Frame will +* indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate +* conversion is possible. Otherwise zero is returned (this will +* not in itself result in an error condition). + +* Notes: +* - The "axes1" and "axes2" arrays should not contain any axis +* indices in common and should, taken together, list all the axes +* of the target Frame. +* - By default, the "result" Frame will have its number of axes +* and axis order determined by the "template" CmpFrame. However, +* if the PreserveAxes attribute of the template is non-zero, then +* the axis count and axis order of the "target" Frame will be used +* instead. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *frame1; /* Pointer to first sub-Frame from target */ + AstFrame *frame2; /* Pointer to second sub-Frame from target */ + AstFrame *result1; /* Result Frame pointer from first match */ + AstFrame *result2; /* Result Frame pointer from second match */ + AstFrame *tmp_frame; /* Temporary Frame pointer */ + AstMapping *junk_map; /* Mapping pointer returned by astSubFrame */ + AstMapping *map1; /* Mapping pointer from first match */ + AstMapping *map2; /* Mapping pointer from second match */ + AstMapping *permmap; /* Pointer to PermMap */ + AstMapping *tmp_map; /* Temporary Mapping pointer */ + const int *perm; /* Template axis permutation array pointer */ + int *inperm; /* Pointer to temporary permutation array */ + int *invperm; /* Inverse axis permutation array pointer */ + int *outperm; /* Pointer to temporary permutation array */ + int *pick; /* Pointer to array of axis selections */ + int *result_order; /* Relative result axis order array pointer */ + int *result_perm; /* Result axis permutation array pointer */ + int *target_assoc; /* Target axis association array pointer */ + int *target_axes1; /* Target axis associations from 1st match */ + int *target_axes2; /* Target axis associations from 2nd match */ + int *template_assoc; /* Template axis association array pointer */ + int *template_axes1; /* Template axis associations, 1st match */ + int *template_axes2; /* Template axis associations, 2nd match */ + int first; /* Axis in 1st component? */ + int full_axis; /* Result Frame axis index, before sub-set */ + int match1; /* First match successful? */ + int match2; /* Second match successful? */ + int match; /* Both matches successful? (result) */ + int match_end1; /* MatchEnd attribute for component 1 */ + int match_end2; /* MatchEnd attribute for component 2 */ + int match_end; /* MatchEnd attribute for template */ + int match_end_set; /* Component MatchEnd attribute set? */ + int output_axis; /* Output axis index */ + int part_result_axis; /* Result Frame component axis index */ + int part_target_axis; /* Target Frame component axis index */ + int part_template_axis; /* Template CmpFrame component axis index */ + int permute_set; /* Component Permute attribute set? */ + int permute_value; /* Component Permute attribute value */ + int preserve_axes; /* Template PreserveAxes attribute value */ + int preserve_axes_set; /* Component PreserveAxes attribute set? */ + int ref_naxes; /* Number of reference Frame axes */ + int result_axis; /* Result Frame axis index */ + int result_naxes1; /* Number of result Frame axes, component 1 */ + int result_naxes2; /* Number of result Frame axes, component 2 */ + int result_naxes; /* Total number of result Frame axes */ + int target_axis; /* Target Frame axis index */ + int target_naxes; /* Number of target Frame axes */ + int template_axis; /* Template CmpFrame axis index */ + int template_naxes1; /* Number of template axes, component 1 */ + int template_naxes; /* Total number of template axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Initialise other variables to avoid compiler errors. */ + ref_naxes = 0; + +/* Select the required sub-Frames from the target. */ +/* ----------------------------------------------- */ +/* We first create two sub-Frames (that can be matched against the two + template component Frames) by selecting the two specified sets of + axes from the target. This is done without overlaying any template + attributes. Annul the Mappings produced by this process, as these + are not needed. */ + + frame1 = NULL; + junk_map = NULL; + (void) astSubFrame( target, NULL, naxes1, axes1, NULL, &junk_map, &frame1 ); + if( junk_map ) junk_map = astAnnul( junk_map ); + + frame2 = NULL; + junk_map = NULL; + (void) astSubFrame( target, NULL, naxes2, axes2, NULL, &junk_map, &frame2 ); + if( junk_map ) junk_map = astAnnul( junk_map ); + +/* Match the sub-Frames with the template component Frames. */ +/* -------------------------------------------------------- */ +/* We now have two sub-Frames obtained from the target, and will + attempt to match these with the component Frames contained within + the template CmpFrame. */ + +/* Before using each template component Frame, see whether any of its + attributes that control matching is "un-set". If so, over-ride it + with the attribute value of the template CmpFrame as a whole. */ + match_end_set = astTestMatchEnd( template->frame1 ); + if ( !match_end_set ) { + astSetMatchEnd( template->frame1, astGetMatchEnd( template ) ); + } + preserve_axes_set = astTestPreserveAxes( template->frame1 ); + if ( !preserve_axes_set ) { + astSetPreserveAxes( template->frame1, astGetPreserveAxes( template ) ); + } + +/* We must also temporarily set the Permute attribute to 1 (this is + normally the default, but might have been set otherwise). This is + needed so that permutations of the target axes will be considered. + Without this, the order in which the axes are presented is + significant and we would have to test all the permutations. If the + Permute attribute of the template CmpFrame as a whole is zero, then + the resulting match may still have to be rejected, but this must be + done at a higher level. */ + permute_set = astTestPermute( template->frame1 ); + permute_value = ( permute_set ) ? astGetPermute( template->frame1 ) : 0; + astSetPermute( template->frame1, 1 ); + +/* Test for a match with the first template component Frame. */ + match1 = astMatch( template->frame1, frame1, matchsub, + &template_axes1, &target_axes1, &map1, &result1 ); + +/* Clear the attribute values again afterwards if necessary. */ + if ( !match_end_set ) astClearMatchEnd( template->frame1 ); + if ( !preserve_axes_set ) astClearPreserveAxes( template->frame1 ); + +/* Also restore the original Permute attribute setting. */ + if ( permute_set ) { + astSetPermute( template->frame1, permute_value ); + } else { + astClearPermute( template->frame1 ); + } + +/* Repeat the whole process for the second component Frame. */ + match_end_set = astTestMatchEnd( template->frame2 ); + if ( !match_end_set ) { + astSetMatchEnd( template->frame2, astGetMatchEnd( template ) ); + } + preserve_axes_set = astTestPreserveAxes( template->frame2 ); + if ( !preserve_axes_set ) { + astSetPreserveAxes( template->frame2, astGetPreserveAxes( template ) ); + } + permute_set = astTestPermute( template->frame2 ); + if ( permute_set ) permute_value = astGetPermute( template->frame2 ); + astSetPermute( template->frame2, 1 ); + + match2 = astMatch( template->frame2, frame2, matchsub, + &template_axes2, &target_axes2, &map2, &result2 ); + + if ( !match_end_set ) astClearMatchEnd( template->frame2 ); + if ( !preserve_axes_set ) astClearPreserveAxes( template->frame2 ); + if ( permute_set ) { + astSetPermute( template->frame2, permute_value ); + } else { + astClearPermute( template->frame2 ); + } + +/* See if both matches were successful. */ + if ( astOK && match1 && match2 ) { + match = 1; + +/* Obtain the number of target axes. */ + target_naxes = astGetNaxes( target ); + +/* Obtain the number of axes in each of the result Frames produced by + the matching operation. */ + result_naxes1 = astGetNaxes( result1 ); + result_naxes2 = astGetNaxes( result2 ); + +/* Obtain the number of axes in the first template component Frame and + in the template CmpFrame as a whole. */ + template_naxes1 = astGetNaxes( template->frame1 ); + template_naxes = astGetNaxes( template ); + +/* Obtain the value of the MatchEnd attribute for each of the + template's component Frames and for the template CmpFrame as a + whole. */ + match_end1 = astGetMatchEnd( template->frame1 ); + match_end2 = astGetMatchEnd( template->frame2 ); + match_end = astGetMatchEnd( template ); + +/* Obtain a pointer to the template CmpFrame's axis permutation + array. Allocate space for a further array and fill it with the + inverse of this axis permutation. */ + perm = astGetPerm( template ); + invperm = astMalloc( sizeof( int ) * (size_t) template_naxes ); + if ( astOK ) { + for ( template_axis = 0; template_axis < template_naxes; + template_axis++ ) { + invperm[ perm[ template_axis ] ] = template_axis; + } + } + +/* Generate template and target axis associations. */ +/* ----------------------------------------------- */ +/* We now construct two arrays which identify the axis associations + between the result axes (in the order obtained from the matching + process above) and the axes of the template and target. This + involves tracing back through several steps. */ + +/* First calculate the total number of result axes and allocate memory + for the association arrays. */ + result_naxes = result_naxes1 + result_naxes2; + template_assoc = astMalloc( sizeof( int ) * (size_t) result_naxes ); + target_assoc = astMalloc( sizeof( int ) * (size_t) result_naxes ); + if ( astOK ) { + +/* Produce associations for each result axis in turn. */ + for ( result_axis = 0; result_axis < result_naxes; result_axis++ ) { + +/* Decide whether this result axis is contained in the first (or + second) individual result Frame. */ + first = ( result_axis < result_naxes1 ); + +/* Obtain the index of the axis within the individual result Frame. + This involves adjusting for the axis numbering offset of the second + result Frame if necessary. */ + part_result_axis = first ? result_axis : + result_axis - result_naxes1; + +/* Find the template and target axis associations for this axis by + looking them up in the association arrays returned from the + matching process. This gives axis indices that apply to the + individual template/target Frames supplied as input to the matching + process. */ + part_template_axis = first ? template_axes1[ part_result_axis ] : + template_axes2[ part_result_axis ]; + part_target_axis = first ? target_axes1[ part_result_axis ] : + target_axes2[ part_result_axis ]; + +/* Check that the resulting template association identifies a valid + template axis. */ + if ( part_template_axis != -1 ) { + +/* If so, obtain the template axis index. This involves adjusting for + the axis numbering offset of the second template component Frame + (if necessary) and then applying the inverse template axis + permutation to convert to the external template axis + numbering. Store the result in the template association array. */ + template_assoc[ result_axis ] = + invperm[ first ? part_template_axis : + part_template_axis + template_naxes1 ]; + +/* Indicate if there is no template axis association by storing an + index of -1. */ + } else { + template_assoc[ result_axis ] = -1; + } + +/* Similarly, check that the target association identifies a valid + target axis. */ + if ( part_target_axis != -1 ) { + +/* If so, obtain the target axis index. This simply involves using the + axis selection arrays provided by the caller to look up which + target axes were involved in the matching process. */ + target_assoc[ result_axis ] = + first ? axes1[ part_target_axis ] : + axes2[ part_target_axis ]; + +/* Indicate if there is no target axis association by storing an index + of -1. */ + } else { + target_assoc[ result_axis ] = -1; + } + } + } + +/* Free the inverse axis permutation array. */ + invperm = astFree( invperm ); + +/* Create the output Frame. */ +/* ------------------------ */ +/* Initialise. */ + result_order = NULL; + result_perm = NULL; + +/* Construct the basis of the final result Frame by combining the two + individual result Frames (from the matching process) using a + CmpFrame. */ + if ( astOK ) { + *result = (AstFrame *) astCmpFrame( result1, result2, "", status ); + +/* The next step is to permute the result Frame's axis order so that + it corresponds with the axis order of the "reference Frame". The + reference Frame is either the template or the target, depending on + whether the template's PreserveAxes attribute is non-zero. Obtain + the value of this attribute. */ + preserve_axes = astGetPreserveAxes( template ); + +/* Decide how many axes the reference Frame contains. */ + ref_naxes = preserve_axes ? target_naxes : template_naxes; + +/* Make a copy of the axis association array that refers to the + reference Frame. */ + result_order = astStore( NULL, + preserve_axes ? target_assoc : + template_assoc, + sizeof( int ) * (size_t) result_naxes ); + +/* The intention is to use this axis association array to permute the + result axes into the same order as the reference Frame's axes. It + is not that simple, however, because some of the axis associations + may be null (i.e. result axes may exist that are not associated + with reference axes) and they may also be incomplete (i.e. not + every reference axis may be associated with a result axis). + + This prevents us from permuting the result axis order using this + array directly, essentially because we haven't yet defined where + any "extra" result axes (those with no association) should appear + in the final axis order. */ + +/* To overcome this, we replace all the null (-1) entries in the + "result_order" array with new values which define their position + relative to the other entries. This also involves re-numbering + other entries to avoid clashes. The new numbers assigned depend on + the MatchEnd attribute for each of the template component Frames, + so we handle the associations for each of these components + separately. */ + AddExtraAxes( result_naxes, result_order, + 0, result_naxes1 - 1, match_end1, status ); + AddExtraAxes( result_naxes, result_order, + result_naxes1, result_naxes - 1, match_end2, status ); + +/* There may now be some reference Frame axes which are not referenced + in this array, so we renumber the entries starting at zero (but + preserving their relative order) so that there are no missing + values due to these. */ + RenumberAxes( result_naxes, result_order, status ); + +/* The resulting "result_order" array no longer describes the original + reference Frame axis associations, but is now suitable for + permuting the result axes into the required order. However, we + require the inverse of this permutation, so allocate an array and + fill it with the inverse. */ + result_perm = astMalloc( sizeof( int ) * (size_t) result_naxes ); + if ( astOK ) { + for ( result_axis = 0; result_axis < result_naxes; + result_axis++ ) { + result_perm[ result_order[ result_axis ] ] = result_axis; + } + } + +/* Apply the inverse permutation to the result CmpFrame to put its + axes into the required order. */ + astPermAxes( *result, result_perm ); + +/* Check if the number of result Frame axes differs from the number of + reference axes. This can arise if the PreserveAxes attribute of + either template component Frame is set to a value that differs from + that of the template CmpFrame as a whole. If this is the case, we + must select a sub-set (or super-set) of the result axes, so that we + end up with the same number of axes as the reference Frame. */ + if ( ref_naxes != result_naxes ) { + +/* Allocate an array to hold the indices of the axes required. */ + pick = astMalloc( sizeof( int ) * (size_t) ref_naxes ); + if ( astOK ) { + +/* Generate the axis indices, using the template CmpFrame's MatchEnd + attribute to decide which ones to use. */ + for ( output_axis = 0; output_axis < ref_naxes; + output_axis++ ) { + full_axis = + match_end ? output_axis + ( result_naxes - ref_naxes ) : + output_axis; + +/* If the index is valid (i.e. the required axis is available), store + it. Otherwise, use an index of -1, which requests that new + (default) axes be supplied where needed. */ + if ( ( full_axis >= 0 ) && ( full_axis < result_naxes ) ) { + pick[ output_axis ] = full_axis; + } else { + pick[ output_axis ] = -1; + } + } + } + +/* Pick the required axes from the result Frame and replace it with + the new one. */ + tmp_frame = astPickAxes( *result, ref_naxes, pick, NULL ); + *result = astAnnul( *result ); + *result = tmp_frame; + +/* Free the array of axis indices. */ + pick = astFree( pick ); + } + } + +/* Create output axis association arrays. */ +/* -------------------------------------- */ +/* We now construct the two arrays that are returned to identify which + template and target axes (if any) are associated with each final + result Frame axis. Allocate memory for these arrays. */ + if ( astOK ) { + *target_axes = astMalloc( sizeof( int ) * (size_t) ref_naxes ); + *template_axes = astMalloc( sizeof( int ) * (size_t) ref_naxes ); + if ( astOK ) { + +/* For each output axis, obtain the original result axis index (before + any sub-set or super-set of the output axes was selected). */ + for ( output_axis = 0; output_axis < ref_naxes; output_axis++ ) { + full_axis = + match_end ? output_axis + ( result_naxes - ref_naxes ) : + output_axis; + +/* Derive the result axis index before the axes were permuted into + their final order. */ + if ( ( full_axis >= 0 ) && ( full_axis < result_naxes ) ) { + result_axis = result_perm[ full_axis ]; + +/* Use this axis index and the axis association arrays generated + earlier to obtain the required associations, and store these in the + output arrays. */ + ( *template_axes )[ output_axis ] = + template_assoc[ result_axis ]; + ( *target_axes )[ output_axis ] = + target_assoc[ result_axis ]; + +/* Store a value of -1 if there is no association. */ + } else { + ( *template_axes )[ output_axis ] = -1; + ( *target_axes )[ output_axis ] = -1; + } + } + } + } + +/* Free the original (un-permuted) axis association arrays. */ + template_assoc = astFree( template_assoc ); + target_assoc = astFree( target_assoc ); + +/* Create the output Mapping. */ +/* -------------------------- */ +/* Construct the basis of the final output Mapping by combining the + Mappings produced by the individual matching processes in parallel, + using a CmpMap. */ + *map = (AstMapping *) astCmpMap( map1, map2, 0, "", status ); + +/* It is now necessary to prefix and suffix this CmpMap with two + PermMaps, which correct the input and output axis order to + correspond with the target and result Frame axes. + + At the target end, this reflects the partitioning of the target + axes into two groups, as specified by the caller. At the result + end, it reflects the axis permutation applied (above) to put the + final result Frame axes into the required order, together with the + selection of any sub-set or super-set of these axes. */ + +/* Allocate memory for permutation arrays to describe the prefix + PermMap. */ + inperm = astMalloc( sizeof( int ) * (size_t) target_naxes ); + outperm = astMalloc( sizeof( int ) * (size_t) target_naxes ); + if ( astOK ) { + +/* Consider the target axes in the order that they were supplied to + the matching processes (i.e. the order that corresponds with the + input coordinates of the CmpMap produced above). */ + for ( target_axis = 0; target_axis < target_naxes; target_axis++ ) { + +/* Decide whether each axis belongs to the first (or second) selected + group of target axes. */ + first = ( target_axis < naxes1 ); + +/* Obtain the index of the target axis within the group. This involves + allowing for the numbering offset of the second group if + necessary. */ + part_target_axis = first ? target_axis : + target_axis - naxes1; + +/* Obtain the original target axis index by looking up the axis in the + appropriate axis selection array provided by the caller. */ + outperm[ target_axis ] = first ? axes1[ part_target_axis ] : + axes2[ part_target_axis ]; + +/* Fill the "inperm" array with the inverse of this permutation. */ + inperm[ outperm[ target_axis ] ] = target_axis; + } + } + +/* If the permutation is not null, use these permutation arrays to + construct the required prefix PermMap. */ + if ( GoodPerm( target_naxes, inperm, target_naxes, outperm, status ) ) { + permmap = (AstMapping *) astPermMap( target_naxes, inperm, + target_naxes, outperm, + NULL, "", status ); + +/* Add the PermMap as a prefix to the result Mapping and then annul + the original Mapping pointers. */ + tmp_map = (AstMapping *) astCmpMap( permmap, *map, 1, "", status ); + (void) astAnnul( *map ); + *map = tmp_map; + permmap = astAnnul( permmap ); + } + +/* Free the permutation arrays. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + +/* Allocate memory for permutation arrays to describe the suffix + PermMap. */ + inperm = astMalloc( sizeof( int ) * (size_t) result_naxes ); + outperm = astMalloc( sizeof( int ) * (size_t) ref_naxes ); + if ( astOK ) { + +/* Initialise the "inperm" array. */ + for ( result_axis = 0; result_axis < result_naxes; result_axis++ ) { + inperm[ result_axis ] = -1; + } + +/* For each output axis, obtain the index of the corresponding result + axis before any sub-set or super-set was selected. */ + for ( output_axis = 0; output_axis < ref_naxes; output_axis++ ) { + full_axis = + match_end ? output_axis + ( result_naxes - ref_naxes ) : + output_axis; + +/* Store the axis index before the result axes were permuted, and also + construct the inverse permutation. */ + if ( ( full_axis >= 0 ) && ( full_axis < result_naxes ) ) { + outperm[ output_axis ] = result_perm[ full_axis ]; + inperm[ outperm[ output_axis ] ] = output_axis; + +/* Note which output axes do not exist in the result Frame + (e.g. because a super-set was selected). */ + } else { + outperm[ output_axis ] = -1; + } + } + } + +/* If the permutation is not null, use these permutation arrays to + construct the required suffix PermMap. */ + if ( GoodPerm( target_naxes, inperm, target_naxes, outperm, status ) ) { + permmap = (AstMapping *) astPermMap( result_naxes, inperm, + ref_naxes, outperm, + NULL, "", status ); + +/* Add the PermMap as a suffix to the result Mapping and then annul + the original Mapping pointers. */ + tmp_map = (AstMapping *) astCmpMap( *map, permmap, 1, "", status ); + (void) astAnnul( *map ); + *map = tmp_map; + permmap = astAnnul( permmap ); + } + +/* Free the permutation arrays. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + +/* Free the result axis permutation arrays. */ + result_order = astFree( result_order ); + result_perm = astFree( result_perm ); + } + +/* If necessary, free the results of the first matching process. */ + if ( match1 ) { + template_axes1 = astFree( template_axes1 ); + target_axes1 = astFree( target_axes1 ); + map1 = astAnnul( map1 ); + result1 = astAnnul( result1 ); + } + +/* If necessary, free the results of the second matching process. */ + if ( match2 ) { + template_axes2 = astFree( template_axes2 ); + target_axes2 = astFree( target_axes2 ); + map2 = astAnnul( map2 ); + result2 = astAnnul( result2 ); + } + +/* Annul the pointers to the sub-Frames selected from the target. */ + frame1 = astAnnul( frame1 ); + frame2 = astAnnul( frame2 ); + +/* If an error occurred, free all allocated memory, annul the result + Object pointers and clear all returned values. */ + if ( !astOK ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + *map = astAnnul( *map );; + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void PermAxes( AstFrame *this_frame, const int perm[], int *status ) { +/* +* Name: +* PermAxes + +* Purpose: +* Permute the order of a CmpFrame's axes. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void astPermAxes( AstFrame *this, const int perm[], int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astPermAxes method +* inherited from the Frame class). + +* Description: +* This function permutes the order in which a CmpFrame's axes occur. + +* Parameters: +* this +* Pointer to the CmpFrame. +* perm +* An array of int (with one element for each axis of the +* CmpFrame) which lists the axes in their new order. Each +* element of this array should be a (zero-based) axis index +* identifying the axes according to their old (un-permuted) +* order. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Only genuine permutations of the axis order are permitted, so +* each axis must be referenced exactly once in the "perm" array. +* - If more than one axis permutation is applied to a CmpFrame, +* the effects are cumulative. + +* Implementation Notes: +* - This function performs essentially the same operation as the +* Frame member function which it over-rides. However, it operates +* on a "perm" array held in the CmpFrame structure (rather than +* the one in the parent Frame structure). This duplication of the +* array is necessary because the one in the Frame structure is of +* zero length, the number of axes in the Frame structure having +* been set to zero to prevent unnecessary allocation of Axis +* objects which are not needed by the CmpFrame. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + int *old; /* Pointer to copy of old permutation array */ + int axis; /* Loop counter for CmpFrame axes */ + int naxes; /* Number of CmpFrame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate the permutation array, to check that it describes a + genuine permutation. */ + astCheckPerm( this, perm, "astPermAxes" ); + +/* Obtain the number of CmpFrame axes. */ + naxes = astGetNaxes( this ); + +/* Allocate memory and use it to store a copy of the old permutation + array for the CmpFrame. */ + old = astStore( NULL, this->perm, sizeof( int ) * (size_t) naxes ); + +/* Apply the new axis permutation cumulatively to the old one and + store the result in the CmpFrame. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + this->perm[ axis ] = old[ perm[ axis ] ]; + } + } + +/* Free the temporary copy of the old array. */ + old = astFree( old ); +} + +static void PrimaryFrame( AstFrame *this_frame, int axis1, + AstFrame **frame, int *axis2, int *status ) { +/* +* Name: +* PrimaryFrame + +* Purpose: +* Uniquely identify a primary Frame and one of its axes. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void astPrimaryFrame( AstFrame *this, int axis1, AstFrame **frame, +* int *axis2, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected +* astPrimaryFrame method inherited from the Frame class). + +* Description: +* This function returns information about the underlying (primary) +* Frame corresponding to a specified CmpFrame axis. + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis1 +* An axis index (zero-based) identifying the CmpFrame axis for +* which information is required. +* frame +* Address of a location to receive a pointer to the underlying +* (primary) Frame to which the requested axis belongs +* (i.e. this will not be a compound Frame). +* axis2 +* Pointer to an int which is to receive the (zero-based) axis +* index within "frame" which identifies the axis being referred +* to, using the axis order that applied when the primary Frame +* was originally constructed (i.e. this function undoes all +* subsequent axis pemutations and the effects of combining +* Frames, in order to reveal the original underlying axis +* order). +* status +* Pointer to the inherited status variable. + +* Notes: +* - This protected method is provided so that class +* implementations can distinguish the axes of Frames from one +* another (e.g. can distinguish a longitude axis as being +* different from a latitide axis) even after their order has been +* permuted and they have been combined with axes from other +* Frames. +* - The reference count of the primary Frame will be incremented +* by one to reflect the new pointer returned. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + int naxes1; /* Number of axes in frame1 */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis1 = astValidateAxis( this, axis1, 1, "astPrimaryFrame" ); + +/* Obtain the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which Frame contains the axis and invoke its astPrimaryFrame + method to obtain the required information. */ + if ( axis1 < naxes1 ) { + astPrimaryFrame( this->frame1, axis1, frame, axis2 ); + } else { + astPrimaryFrame( this->frame2, axis1 - naxes1, frame, axis2 ); + } + } +} + +static int QsortCmpAxes( const void *a, const void *b ) { +/* +* Name: +* QsortCmpAxes + +* Purpose: +* Compare two axis indices for "qsort". + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int QsortCmpAxes( const void *a, const void *b ) + +* Class Membership: +* CmpFrame member function. + +* Description: +* This is a service function for the C RTL routine "qsort". It +* takes the two values supplied and interprets them as integer +* indices into the static "qsort_axes" array. It compares the +* values of these two array elements and returns the result +* required by "qsort". +* +* This function is used when sorting an array of indices so that +* they access the "qsort_axes" array in ascending order. + +* Parameters: +* As required by "qsort". + +* Returned Value: +* As required by "qsort". +*/ + +/* Local Variables. */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + int result; /* Result value to return */ + int val_a; /* First axis index */ + int val_b; /* Second axis index */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Convert the values passed by "qsort" into integer array indices and + use these to access the "qsort_axes" array (this pointer to the + array being assigned by the caller of "qsort"). Extract the two + values being compared. */ + val_a = qsort_axes[ *( (const int *) a ) ]; + val_b = qsort_axes[ *( (const int *) b ) ]; + +/* Compare the two values as required by "qsort". */ + if ( val_a < val_b ) { + result = -1; + } else if ( val_a == val_b ) { + result = 0; + } else { + result = 1; + } + +/* Return the result. */ + return result; +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* CmpFrame method (over-rides the astRemoveRegions method inherited +* from the Frame class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a CmpMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel CmpMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the CmpFrame class invokes the +* astRemoveRegions method on the two component Frames, and joins +* the results together into a new CmpFrame. This replaces any Regions +* with their equivalent Frames. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *new; /* Pointer to new CmpFrame */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstFrame *newfrm1; /* New first component Frame */ + AstFrame *newfrm2; /* New second component Frame */ + AstMapping *result; /* Result pointer to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpFrame. */ + this = (AstCmpFrame *) this_mapping; + +/* Invoke the astRemoveRegions method on the two component Frames. */ + newfrm1 = astRemoveRegions( this->frame1 ); + newfrm2 = astRemoveRegions( this->frame2 ); + +/* If neither component was modified, just return a clone of the supplied + pointer. */ + if( this->frame1 == newfrm1 && this->frame2 == newfrm2 ) { + result = astClone( this ); + +/* Annul new new Frame pointers. */ + newfrm1 = astAnnul( newfrm1 ); + newfrm2 = astAnnul( newfrm2 ); + +/* Otherwise, we need to create a new CmpFrame to return. */ + } else { + +/* Make a copy of the supplied CmpFrame so that the new CmpFrame retains + any attribute settings of the supplied CmpFrame. */ + new = astCopy( this ); + result = (AstMapping *) new; + +/* Replace the two component Frames with the simplified Frames. */ + (void) astAnnul( new->frame1 ); + (void) astAnnul( new->frame2 ); + new->frame1 = (AstFrame *) newfrm1; + new->frame2 = (AstFrame *) newfrm2; + } + +/* Annul the returned Mapping if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void RenumberAxes( int naxes, int axes[], int *status ) { +/* +* Name: +* RenumberAxes + +* Purpose: +* Renumber axis indices to eliminate missing ones. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void RenumberAxes( int naxes, int axes[], int *status ) + +* Class Membership: +* CmpFrame member function. + +* Description: +* This function takes an array containing a list of (zero-based) +* axis indices referring to the axes of a Frame, some of whose +* axes may not be referenced. It renumbers the axis indices, to +* eliminate any which are missing (i.e. not referenced), while +* preserving the original order. It does this by replacing each +* axis index by its rank (starting at zero) when the indices are +* sorted into ascending order. + +* Parameters: +* naxes +* The number of axis indices present. +* axes +* An array, with "naxes" elements, containing the indices. This +* is modified by this function to contain the new indices. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + int *work; /* Pointer to workspace array */ + int i; /* Loop counter */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Allocate workspace. */ + work = astMalloc( sizeof( int ) * (size_t) naxes ); + if ( astOK ) { + +/* Fill the workspace with indices which address the axis index values + in their natural order. */ + for ( i = 0; i < naxes; i++ ) work[ i ] = i; + +/* Make the "axes" values available to the C RTL function "qsort" via + the static "qsort_axes" pointer. Then use "qsort" to permute the + contents of "work" so that it addresses the axis indices in + ascending order. */ + qsort_axes = axes; + qsort( work, (size_t) naxes, sizeof( int ), QsortCmpAxes ); + +/* Use the result to replace each axis index by its rank when sorted + into ascending order (starting with zero). */ + for ( i = 0; i < naxes; i++ ) axes[ work[ i ] ] = i; + } + +/* Free the workspace array. */ + work = astFree( work ); +} + +static void Resolve( AstFrame *this_frame, const double point1[], + const double point2[], const double point3[], + double point4[], double *d1, double *d2, int *status ){ +/* +* Name: +* Resolve + +* Purpose: +* Resolve a vector into two orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void Resolve( AstFrame *this, const double point1[], +* const double point2[], const double point3[], +* double point4[], double *d1, double *d2, int *status ); + +* Class Membership: +* CmpFrame member function (over-rides the astOffset method +* inherited from the Frame class). + +* Description: +* This function resolves a vector into two perpendicular components. +* The vector from point 1 to point 2 is used as the basis vector. +* The vector from point 1 to point 3 is resolved into components +* parallel and perpendicular to this basis vector. The lengths of the +* two components are returned, together with the position of closest +* aproach of the basis vector to point 3. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vector to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* point3 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the vector to be +* resolved. +* point4 +* An array of double, with one element for each Frame axis +* in which the coordinates of the point of closest approach of the +* basis vector to point 3 will be returned. +* d1 +* The address of a location at which to return the distance from +* point 1 to point 4 (that is, the length of the component parallel +* to the basis vector). Positive values are in the same sense as +* movement from point 1 to point 2. +* d2 +* The address of a location at which to return the distance from +* point 4 to point 3 (that is, the length of the component +* perpendicular to the basis vector). The returned value is always +* positive. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Each vector used in this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the required +* output values are undefined. +*-- +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double *p1; /* Permuted coordinates for point1 */ + double *p2; /* Permuted coordinates for point2 */ + double *p3; /* Permuted coordinates for point3 */ + double *p4; /* Permuted coordinates for point4 */ + double d1a; /* Parallel distance in frame1 */ + double d1b; /* Parallel distance in frame2 */ + double d2a; /* Perpendicular distance in frame1 */ + double d2b; /* Perpendicular distance in frame2 */ + double d; /* Total length of basis vector */ + double da; /* Length of basis vector in frame1 */ + double db; /* Length of basis vector in frame2 */ + int axis; /* Loop counter for axes */ + int bad; /* Set bad output coordinates? */ + int naxes1; /* Number of axes in frame1 */ + int naxes; /* Total number of axes in CmpFrame */ + +/* Check the global error status. */ + *d1 = AST__BAD; + *d2 = AST__BAD; + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain the number of axes in the CmpFrame. */ + naxes = astGetNaxes( this ); + +/* Obtain a pointer to the CmpFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* Allocate workspace. */ + p1 = astMalloc( sizeof( double ) * (size_t) naxes ); + p2 = astMalloc( sizeof( double ) * (size_t) naxes ); + p3 = astMalloc( sizeof( double ) * (size_t) naxes ); + p4 = astMalloc( sizeof( double ) * (size_t) naxes ); + +/* Initialise a flag to indicate whether "bad" coordinates should be + returned. */ + bad = 0; + +/* Initialise ther variables to avoid compiler warnings. */ + da = 0.0; + db = 0.0; + +/* Check that all the coordinates of both input points are OK. If not, + set the "bad" flag and quit checking. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + if ( ( point1[ axis ] == AST__BAD ) || + ( point2[ axis ] == AST__BAD ) || + ( point3[ axis ] == AST__BAD ) ) { + bad = 1; + break; + +/* If the coordinates are OK, apply the axis permutation array to + obtain them in the required order. */ + } else { + p1[ perm[ axis ] ] = point1[ axis ]; + p2[ perm[ axis ] ] = point2[ axis ]; + p3[ perm[ axis ] ] = point3[ axis ]; + } + } + } + +/* If OK, obtain the number of axes in the first component Frame. */ + if ( astOK && !bad ) { + naxes1 = astGetNaxes( this->frame1 ); + +/* Find the projection of the required parallel distance into each of the + two Frames. */ + astResolve( this->frame1, p1, p2, p3, p4, &d1a, &d2a ); + astResolve( this->frame2, p1 + naxes1, p2 + naxes1, p3 + naxes1, + p4 + naxes1, &d1b, &d2b ); + +/* Project the first two input points into the two component Frames and + determine the length of the basis vector in each Frame. */ + da = astDistance( this->frame1, p1, p2 ); + db = astDistance( this->frame2, p1 + naxes1, p2 + naxes1 ); + +/* Check that the returned distances are not bad. */ + if ( astOK ) bad = ( bad || ( da == AST__BAD ) || ( db == AST__BAD ) ); + +/* We can tolerate a bad parallel distance within a sub-Frame if the + basis vector has zero length in the sub-Frame, because the bad + parallel distance will have zero weight in the calculation. Set such + bad parallel distanced arbitrarily to zero. */ + if( d1a == AST__BAD && da == 0.0 ) d1a = 0.0; + if( d1b == AST__BAD && db == 0.0 ) d1b = 0.0; + +/* Check that the final parallel distances are not bad. */ + if ( astOK ) bad = ( bad || ( d1a == AST__BAD ) || ( d1b == AST__BAD ) ); + + } + +/* If OK, calculate the total distance between the two points. */ + if ( astOK && !bad ) { + d = sqrt( da * da + db * db ); + +/* If the points are co-incident, then set the "bad" flag. */ + if ( d == 0.0 ) { + bad = 1; + +/* If the points are not co-incident, combine the parallel distances for + the individual Frames into a single parallel distance for the entire + CmpFrame. */ + } else { + *d1 = ( da*d1a + db*d1b )/d; + +/* Offset this distance away from point 1 towards point 2 to get point 4. */ + astOffset( this, point1, point2, *d1, point4 ); + +/* Now find the perpendicular distance (the distance between point4 and + point3). */ + *d2 = astDistance( this, point4, point3 ); + + } + } + +/* Free the workspace arrays. */ + p1 = astFree( p1 ); + p2 = astFree( p2 ); + p3 = astFree( p3 ); + p4 = astFree( p4 ); + +/* If no error has occurred, but bad coordinates must be returned, + then set these in the output array. */ + if ( astOK && bad ) { + *d1 = AST__BAD; + *d2 = AST__BAD; + for ( axis = 0; axis < naxes; axis++ ) point4[ axis ] = AST__BAD; + } + +} + +static AstPointSet *ResolvePoints( AstFrame *this_frame, const double point1[], + const double point2[], AstPointSet *in, + AstPointSet *out, int *status ) { +/* +* Name: +* ResolvePoints + +* Purpose: +* Resolve a set of vectors into orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstPointSet *ResolvePoints( AstFrame *this, const double point1[], +* const double point2[], AstPointSet *in, +* AstPointSet *out ) + +* Class Membership: +* CmpFrame member function (over-rides the astResolvePoints method +* inherited from the Frame class). + +* Description: +* This function takes a CmpFrame and a set of vectors encapsulated +* in a PointSet, and resolves each one into two orthogonal components, +* returning these two components in another PointSet. +* +* This is exactly the same as the public astResolve method, except +* that this method allows many vectors to be processed in a single call, +* thus reducing the computational cost of overheads of many +* individual calls to astResolve. + +* Parameters: +* this +* Pointer to the CmpFrame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vectors to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* in +* Pointer to the PointSet holding the ends of the vectors to be +* resolved. +* out +* Pointer to a PointSet which will hold the length of the two +* resolved components. A NULL value may also be given, in which +* case a new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. The first axis will +* hold the lengths of the vector components parallel to the basis vector. +* These values will be signed (positive values are in the same sense as +* movement from point 1 to point 2. The second axis will hold the lengths +* of the vector components perpendicular to the basis vector. These +* values will always be positive. + +* Notes: +* - The number of coordinate values per point in the input +* PointSet must match the number of axes in the supplied Frame. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and 2 coordinate values per point. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + AstPointSet *in1; /* Pointer to input PointSet for frame1 */ + AstPointSet *in2; /* Pointer to input PointSet for frame2 */ + AstPointSet *out1; /* Pointer to output PointSet for frame1 */ + AstPointSet *out2; /* Pointer to output PointSet for frame2 */ + AstPointSet *result; /* Pointer to output PointSet */ + const int *perm; /* Pointer to axis permutation array */ + double **ptr_in; /* Pointers to input axis values */ + double **ptr_out1; /* Pointers to frame1 component lengths */ + double **ptr_out2; /* Pointers to frame2 component lengths */ + double **ptr_out; /* Pointers to returned component lengths */ + double *d1; /* Pointer to next parallel component value */ + double *d1_1; /* arallel distance in frame1 */ + double *d1_2; /* Parallel distance in frame2 */ + double *d2; /* Pointer to next perpendicular component value */ + double *d2_1; /* Perpendicular distance in frame1 */ + double *d2_2; /* Perpendicular distance in frame2 */ + double *p1; /* Permuted coordinates for point1 */ + double *p2; /* Permuted coordinates for point2 */ + double *p3; /* Supplied vector */ + double *p4; /* Closest approach to supplied vector */ + double b1; /* Length of basis vector in frame1 */ + double b2; /* Length of basis vector in frame2 */ + double b; /* Length of basis vector */ + int axis; /* Loop counter for axes */ + int ipoint; /* Index of next point */ + int nax; /* Number of Frame axes */ + int naxes1; /* Number of axes in frame1 */ + int naxes2; /* Number of axes in frame2 */ + int ncoord_in; /* Number of input PointSet coordinates */ + int ncoord_out; /* Number of coordinates in output PointSet */ + int npoint; /* Number of points to transform */ + int npoint_out; /* Number of points in output PointSet */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialise to prevent compiler "uninitialised use" messages. */ + d1 = NULL; + d2 = NULL; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Obtain the number of axes in the two component Frames */ + naxes1 = astGetNaxes( this->frame1 ); + naxes2 = astGetNaxes( this->frame2 ); + +/* For the total number of axes. */ + nax = naxes1 + naxes2; + +/* Obtain the number of input vectors to resolve and the number of coordinate + values per vector. */ + npoint = astGetNpoint( in ); + ncoord_in = astGetNcoord( in ); + +/* If OK, check that the number of input coordinates matches the number + required by the Frame. Report an error if these numbers do not match. */ + if ( astOK && ( ncoord_in != nax ) ) { + astError( AST__NCPIN, "astResolvePoints(%s): Bad number of coordinate " + "values (%d) in input %s.", status, astGetClass( this ), ncoord_in, + astGetClass( in ) ); + astError( AST__NCPIN, "The %s given requires %d coordinate value(s) for " + "each input point.", status, astGetClass( this ), nax ); + } + +/* If still OK, and a non-NULL pointer has been given for the output PointSet, + then obtain the number of points and number of coordinates per point for + this PointSet. */ + if ( astOK && out ) { + npoint_out = astGetNpoint( out ); + ncoord_out = astGetNcoord( out ); + +/* Check that the dimensions of this PointSet are adequate to accommodate the + output coordinate values and report an error if they are not. */ + if ( astOK ) { + if ( npoint_out < npoint ) { + astError( AST__NOPTS, "astResolvePoints(%s): Too few points (%d) in " + "output %s.", status, astGetClass( this ), npoint_out, + astGetClass( out ) ); + astError( AST__NOPTS, "The %s needs space to hold %d transformed " + "point(s).", status, astGetClass( this ), npoint ); + } else if ( ncoord_out < 2 ) { + astError( AST__NOCTS, "astResolvePoints(%s): Too few coordinate " + "values per point (%d) in output %s.", status, + astGetClass( this ), ncoord_out, astGetClass( out ) ); + astError( AST__NOCTS, "The %s supplied needs space to store 2 " + "coordinate value(s) per transformed point.", status, + astGetClass( this ) ); + } + } + } + +/* If all the validation stages are passed successfully, and a NULL output + pointer was given, then create a new PointSet to encapsulate the output + coordinate data. */ + if ( astOK ) { + if ( !out ) { + result = astPointSet( npoint, 2, "", status ); + +/* Otherwise, use the PointSet supplied. */ + } else { + result = out; + } + } + +/* Store points to the first two axis arrays in the returned PointSet. */ + ptr_out = astGetPoints( result ); + if( astOK ) { + d1 = ptr_out[ 0 ]; + d2 = ptr_out[ 1 ]; + } + +/* Obtain a pointer to the CmpFrame's axis permutation array. This array + holds the original axis index for each current Frame axis index. */ + perm = astGetPerm( this ); + +/* Temporarily permute the coordinates within the supplied PointSet back + in to the axis order which existed when the CmpFrame was created. */ + astPermPoints( in, 0, perm ); + +/* Extract the axis values relevant to each of the two sub-Frames from the + point1 and point2 arrays, at the same time undoing any axis permutation + applied to the CmpFrame as a whole. */ + p1 = astMalloc( sizeof( double )*( size_t )nax ); + p2 = astMalloc( sizeof( double )*( size_t )nax ); + if( astOK ) { + for( axis = 0; axis < nax; axis++ ) { + p1[ perm[ axis ] ] = point1[ axis ]; + p2[ perm[ axis ] ] = point2[ axis ]; + } + } + +/* Project the first two input points into the two component Frames and + determine the length of the basis vector in each Frame. */ + b1 = astDistance( this->frame1, p1, p2 ); + b2 = astDistance( this->frame2, p1 + naxes1, p2 + naxes1 ); + +/* If either of these distances is bad or if both are zero, then fill the + returned PointSet with bad values. */ + if( b1 == AST__BAD || b2 == AST__BAD || ( b1 == 0.0 && b2 == 0.0 ) ) { + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++ ) { + *d1 = AST__BAD; + *d2 = AST__BAD; + } + +/* Otherwise we continue to calculate the resolved components */ + } else if( astOK ){ + +/* Calculate the total distance between the two points. */ + b = sqrt( b1*b1 + b2*b2 ); + +/* Create PointSets holding the input values which refer to each of the + two component Frames. */ + in1 = astPointSet( npoint, naxes1, "", status ); + in2 = astPointSet( npoint, naxes2, "", status ); + +/* Associated the appropriate subset of the data in the supplied input + PointSet with each of these two PointSets. */ + astSetSubPoints( in, 0, 0, in1 ); + astSetSubPoints( in, 0, naxes1, in2 ); + +/* Invoke the astResolvePoints method on each of the sub-Frames. These + invocations create two new PointSets containing the output values. */ + out1 = astResolvePoints( this->frame1, p1, p2, in1, NULL ); + out2 = astResolvePoints( this->frame2, p1 + naxes1, p2 + naxes1, in2, NULL ); + +/* Get pointers to the axis values in these pointsets. */ + ptr_out1 = astGetPoints( out1 ); + ptr_out2 = astGetPoints( out2 ); + +/* More work space */ + p3 = astMalloc( sizeof( double )*( size_t )nax ); + p4 = astMalloc( sizeof( double )*( size_t )nax ); + +/* Get pointers to the input axis values (these are still permuted to + undo any axis permutation applied to the CmpFrame). */ + ptr_in = astGetPoints( in ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Get pointers to the parallel (d1) and perpendiclar (d2) components + within the two sub-Frames (_1 and _2). */ + d1_1 = ptr_out1[ 0 ]; + d2_1 = ptr_out1[ 1 ]; + d1_2 = ptr_out2[ 0 ]; + d2_2 = ptr_out2[ 1 ]; + +/* Loop round each supplied vector. */ + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++, + d1_1++, d2_1++, + d1_2++, d2_2++ ) { + +/* We can tolerate a bad parallel distance within a sub-Frame if the + basis vector has zero length in the sub-Frame, because the bad + parallel distance will have zero weight in the calculation. Set such + bad parallel distanced arbitrarily to zero. */ + if( *d1_1 == AST__BAD && b1 == 0.0 ) *d1_1 = 0.0; + if( *d1_2 == AST__BAD && b2 == 0.0 ) *d1_2 = 0.0; + +/* Combine the parallel distances for the individual Frames into a single + parallel distance for the entire CmpFrame. */ + if( *d1_1 != AST__BAD && *d1_2 != AST__BAD ) { + *d1 = ( b1*(*d1_1) + b2*(*d1_2) )/b; + +/* Offset this distance away from point 1 towards point 2 to get point 4. */ + astOffset( this, p1, p2, *d1, p4 ); + +/* Now find the perpendicular distance (the distance between point4 and + point3). */ + for( axis = 0; axis < nax; axis++ ) p3[ axis ] = ptr_in[ axis ][ ipoint ]; + *d2 = astDistance( this, p4, p3 ); + + } else { + *d1 = AST__BAD; + *d2 = AST__BAD; + } + } + } + +/* Free resources */ + in1 = astAnnul( in1 ); + in2 = astAnnul( in2 ); + out1 = astAnnul( out1 ); + out2 = astAnnul( out2 ); + p3 = astFree( p3 ); + p4 = astFree( p4 ); + } + +/* Free resources */ + p1 = astFree( p1 ); + p2 = astFree( p2 ); + +/* Re-instate the original ordering of the coordinates within the + supplied PointSet. */ + astPermPoints( in, 1, perm ); + +/* Annul the returned PointSet if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void SetActiveUnit( AstFrame *this_frame, int value, int *status ){ +/* +* Name: +* SetActiveUnit + +* Purpose: +* Specify how the Unit attribute should be used. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetActiveUnit( AstFrame *this, int value, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetActiveUnit method +* inherited from the Frame class). + +* Description: +* This function sets the current value of the ActiveUnit flag for a +* CmpFrame, which controls how the Frame behaves when it is used (by +* astFindFrame) as a template to match another (target) Frame, or is +* used as the "to" Frame by astConvert. It determines if the Mapping +* between the template and target Frames should take differences in +* axis units into account. + +* Parameters: +* this +* Pointer to the CmpFrame. +* value +* The new value to use. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to set the ActiveUnitFlag for the CmpFrame, + then set the same value for the component Frames. */ + (*parent_setactiveunit)( this_frame, value, status ); + astSetActiveUnit( ((AstCmpFrame *)this_frame)->frame1, value ); + astSetActiveUnit( ((AstCmpFrame *)this_frame)->frame2, value ); +} + +static void SetFrameFlags( AstFrame *this_frame, int value, int *status ){ +/* +* Name: +* SetFrameFlags + +* Purpose: +* Set flags that control current Frame behaviour. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetFrameFlags( AstFrame *this, int value, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetFrameFlags method +* inherited from the Frame class). + +* Description: +* This function sets values for the bit mask of flags that control +* how the CmpFrame behaves. It ensures that both component Frames use +* the the same bitmask as the parent CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* value +* The new value to use. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to set the FrameFlags for the CmpFrame, + then set the same value for the component Frames. */ + (*parent_setframeflags)( this_frame, value, status ); + astSetFrameFlags( ((AstCmpFrame *)this_frame)->frame1, value ); + astSetFrameFlags( ((AstCmpFrame *)this_frame)->frame2, value ); +} + +static int GetActiveUnit( AstFrame *this_frame, int *status ){ +/* +* Name: +* GetActiveUnit + +* Purpose: +* Determines how the Unit attribute will be used. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int GetActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astGetActiveUnit method +* inherited from the Frame class). + +* Description: +* This function returns the current value of the ActiveUnit flag for a +* CmpFrame. See the description of the astSetActiveUnit function +* for a description of the ActiveUnit flag. + +* Parameters: +* this +* Pointer to the CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The current value of the ActiveUnit flag. + +*/ + +/* Local Variables; */ + int result; /* The ActiveUnit flag for the CmpFrame */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If the ActiveUnit value has been set for the CmpFrame use the parent + implementation to get its value. */ + if( astTestActiveUnit( this_frame ) ) { + result = (*parent_getactiveunit)( this_frame, status ); + +/* Otherwise, the default is determined by the component Frames. If both + components have active units, the default for the CmpFrame is "on" */ + } else { + result = astGetActiveUnit( ((AstCmpFrame *)this_frame)->frame1 ) || + astGetActiveUnit( ((AstCmpFrame *)this_frame)->frame2 ); + } + +/* Return the result */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* CmpFrame member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a CmpFrame, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the CmpFrame. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This protected method is intended to be invoked by the Object astSet +* method and makes additional attributes accessible to it. +*/ + +#define BUF_LEN 1024 + +/* Local Vaiables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + char buf1[BUF_LEN]; /* For for un-indexed attribute name */ + char buf2[BUF_LEN]; /* For for indexed attribute name */ + int axis; /* Supplied (1-base) axis index */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + int oldrep; /* Original error reporting state */ + int paxis; /* Index of primary Frame axis */ + int ok; /* Have we accessed the attribute succesfully? */ + int value; /* Offset to start fo value string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Indicate we have not yet acessed the attribute succesfully. */ + ok = 0; + +/* First check the supplied attribute name against each of the attribute + names defined by this class. In fact there is nothing to do here + since the CmpFrame class currently defines no extra attributes, but + this may change in the future. */ + if( 0 ) { + + + +/* If the attribute is not a CmpFrame specific attribute... */ + } else if( astOK ) { + +/* We want to allow easy access to the attributes of the component Frames. + That is, we do not want it to be necessary to extract a Frame from + its parent CmpFrame in order to access its attributes. For this reason + we first temporarily switch off error reporting so that if an attempt + to access the attribute fails, we can try a different approach. */ + oldrep = astReporting( 0 ); + +/* Our first attempt is to see if the attribute is recognised by the parent + class (Frame). */ + (*parent_setattrib)( this_object, setting, status ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise, clear the error condition so that we can try a different + approach. */ + } else { + astClearStatus; + +/* If the attribute is qualified by an axis index, try accessing it as an + attribute of the primary Frame containing the specified index. */ + if ( nc = 0, + ( 2 == astSscanf( setting, "%[^(=](%d)= %n%*s %n", buf1, &axis, + &value, &nc ) ) && ( nc >= len ) ) { + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + if( astOK ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astSet" ); + +/* Create a new setting with the same name but with the axis index + appropriate to the primary Frame. */ + nc = sprintf( buf2, "%s(%d)=%s", buf1, paxis + 1, + setting+value ); + if( nc < BUF_LEN ) { + +/* Attempt to access the attribute. */ + astSetAttrib( pfrm, buf2 ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise clear the status value, and try again without any axis index. */ + } else { + astClearStatus; + sprintf( buf2, "%s=%s", buf1, setting+value ); + astSetAttrib( pfrm, buf2 ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + } + +/* Buffer overflow */ + } else if( astOK ) { + astError( AST__INTER, "SetAttrib(CmpFrame): Buffer " + "over-flow (internal AST programming error).", + status ); + } + +/* Free the primary frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* If the attribute is not qualified by an axis index, try accessing it + using the primary Frame of each axis in turn. */ + } else { + +/* Loop round all axes attribute. */ + for( axis = 0; axis < astGetNaxes( this ); axis++ ) { + +/* Get the primary Frame containing this axis. */ + astPrimaryFrame( this, axis, &pfrm, &paxis ); + +/* Attempt to access the attribute as an attribute of the primary Frame. */ + astSetAttrib( pfrm, setting ); + +/* Free the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + } + } + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + + } + +/* Report an error if the attribute could not be accessed. */ + if( !ok && astOK ) { + astError( AST__BADAT, "astSet: The attribute setting \"%s\" is invalid " + "for the given %s.", status, setting, astGetClass( this ) ); + } + +#undef BUF_LEN +} + +static void SetAxis( AstFrame *this_frame, int axis, AstAxis *newaxis, int *status ) { +/* +* Name: +* SetAxis + +* Purpose: +* Set a new Axis for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void astSetAxis( AstFrame *this, int axis, AstAxis *newaxis, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetAxis method +* inherited from the Frame class). + +* Description: +* This function allows a new Axis object to be associated with one +* of the axes of a CmpFrame, replacing the previous one. Each Axis +* object contains a description of the quantity represented along +* one of the CmpFrame's axes, so this function allows this +* description to be exchanged for another one. + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis +* The index (zero-based) of the CmpFrame axis whose associated +* Axis object is to be replaced. +* newaxis +* Pointer to the new Axis object. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + int naxes1; /* Number of axes in frame1 */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astSetAxis" ); + +/* Determine the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which Frame contains the axis and invoke its astSetAxis + method to set the new Axis. */ + if ( axis < naxes1 ) { + astSetAxis( this->frame1, axis, newaxis ); + } else { + astSetAxis( this->frame2, axis - naxes1, newaxis ); + } + } +} + +static void SetDut1( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetDut1 + +* Purpose: +* Set the value of the Dut1 attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetDut1( AstFrame *this, double val, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetDut1 method +* inherited from the Frame class). + +* Description: +* This function sets the Dut1 value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* val +* New Dut1 value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to set the CmpFrame Dut1 value. */ + (*parent_setdut1)( this_frame, val, status ); + +/* Now set the Dut1 attribute in the two component Frames. */ + astSetDut1( this->frame1, val ); + astSetDut1( this->frame2, val ); +} + +static void SetEpoch( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetEpoch + +* Purpose: +* Set the value of the Epoch attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetEpoch( AstFrame *this, double val, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetEpoch method +* inherited from the Frame class). + +* Description: +* This function sets the Epoch value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* val +* New Epoch value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to set the CmpFrame epoch. */ + (*parent_setepoch)( this_frame, val, status ); + +/* Now set the Epoch attribute in the two component Frames. */ + astSetEpoch( this->frame1, val ); + astSetEpoch( this->frame2, val ); +} + +static void SetObsAlt( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetObsAlt + +* Purpose: +* Set the value of the ObsAlt attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetObsAlt( AstFrame *this, double val, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetObsAlt method +* inherited from the Frame class). + +* Description: +* This function sets the ObsAlt value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* val +* New ObsAlt value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to set the CmpFrame ObsAlt. */ + (*parent_setobsalt)( this_frame, val, status ); + +/* Now set the ObsAlt attribute in the two component Frames. */ + astSetObsAlt( this->frame1, val ); + astSetObsAlt( this->frame2, val ); +} + +static void SetObsLat( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetObsLat + +* Purpose: +* Set the value of the ObsLat attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetObsLat( AstFrame *this, double val, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetObsLat method +* inherited from the Frame class). + +* Description: +* This function sets the ObsLat value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* val +* New ObsLat value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to set the CmpFrame ObsLat. */ + (*parent_setobslat)( this_frame, val, status ); + +/* Now set the ObsLat attribute in the two component Frames. */ + astSetObsLat( this->frame1, val ); + astSetObsLat( this->frame2, val ); +} + +static void SetObsLon( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetObsLon + +* Purpose: +* Set the value of the ObsLon attribute for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* void SetObsLon( AstFrame *this, double val, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSetObsLon method +* inherited from the Frame class). + +* Description: +* This function sets the ObsLon value in the component Frames as +* well as this CmpFrame. + +* Parameters: +* this +* Pointer to the CmpFrame. +* val +* New ObsLon value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Invoke the parent method to set the CmpFrame ObsLon. */ + (*parent_setobslon)( this_frame, val, status ); + +/* Now set the ObsLon attribute in the two component Frames. */ + astSetObsLon( this->frame1, val ); + astSetObsLon( this->frame2, val ); +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* CmpFrame method (over-rides the astSimplify method inherited +* from the Frame class). + +* Description: +* This function simplifies the Mapping represented by a CmpFrame, +* by using the astSimplify method on each of the component Frames and +* combining the resulting Mappings together. + +* Parameters: +* this +* Pointer to the original CmpFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new pointer to the simplified CmpFrame. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *new; /* Pointer to new CmpFrame structure */ + AstCmpFrame *this; /* Pointer to original CmpFrame structure */ + AstMapping *map1; /* Intermediate Mapping */ + AstMapping *map2; /* Intermediate Mapping */ + AstMapping *result; /* Result pointer to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_mapping; + +/* Simplify each of the component Frames. */ + map1 = astSimplify( this->frame1 ); + map2 = astSimplify( this->frame2 ); + +/* Did any usable simplification occur? */ + if( astIsAFrame( map1 ) && astIsAFrame( map2 ) && + ( map1 != (AstMapping *) this->frame1 || + map2 != (AstMapping *) this->frame2 ) ) { + +/* Make a copy of the supplied CmpFrame. */ + new = astCopy( this ); + result = (AstMapping *) new; + +/* Replace the two component Frames with the simplified Frames. */ + (void) astAnnul( new->frame1 ); + (void) astAnnul( new->frame2 ); + new->frame1 = (AstFrame *) map1; + new->frame2 = (AstFrame *) map2; + +/* If no simplication took place, annul the Mapping pointers and return a + clone of the supplied pointer. */ + } else { + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + result= astClone( this ); + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSystemCode method +* inherited from the Frame class). + +* Description: +* This function converts a string used for the external +* description of a coordinate system into a CmpFrame +* coordinate system type code (System attribute value). It is the +* inverse of the astSystemString function. + +* Parameters: +* this +* The Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the coordinate +* system description was not recognised. This does not produce an +* error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" string against each possibility and assign the + result. The CmpFrame class only supports a single system "Compound". */ + if ( astChrMatch( "Compound", system ) ) { + result = AST__COMP; + } + +/* Return the result. */ + return result; +} + +static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astSystemString method +* inherited from the Frame class). + +* Description: +* This function converts a CmpFrame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* The Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. (Where possible, return the same string as would be + used in the FITS WCS representation of the coordinate system). A + CmpFrame only allows a single System value, "Compound". */ + switch ( system ) { + case AST__COMP: + result = "Compound"; + break; + } + +/* Return the result pointer. */ + return result; +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a CmpFrame and convert to the new coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the protected astSubFrame +* method inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the +* axes from a "target" CmpFrame and creates a new Frame with +* copies of the selected axes assembled in the requested order. It +* then optionally overlays the attributes of a "template" Frame on +* to the result. It returns both the resulting Frame and a Mapping +* that describes how to convert between the coordinate systems +* described by the target and result Frames. If necessary, this +* Mapping takes account of any differences in the Frames' +* attributes due to the influence of the template. + +* Parameters: +* target +* Pointer to the target CmpFrame, from which axes are to be selected. +* template +* Pointer to the template Frame, from which new attributes for +* the result Frame are to be obtained. Optionally, this may be +* NULL, in which case no overlaying of template attributes will +* be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This +* number may be greater than or less than the number of axes in +* this Frame (or equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving +* a list of the (zero-based) axis indices of the axes to be +* selected from the target CmpFrame. The order in which these +* are given determines the order in which the axes appear in +* the result Frame. If any of the values in this array is set +* to -1, the corresponding result axis will not be derived from +* the target Frame, but will be assigned default attributes +* instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This +* should contain a list of the template axes (given as +* zero-based axis indices) with which the axes of the result +* Frame are to be associated. This array determines which axes +* are used when overlaying axis-dependent attributes of the +* template on to the result. If any element of this array is +* set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not +* used and a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned +* Mapping. The forward transformation of this Mapping will +* describe how to convert coordinates from the coordinate +* system described by the target CmpFrame to that described by +* the result Frame. The inverse transformation will convert in +* the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is +* possible between the target and the result Frame. Otherwise zero +* is returned and *map and *result are returned as NULL (but this +* will not in itself result in an error condition). In general, +* coordinate conversion should always be possible if no template +* Frame is supplied but may not always be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Implementation Deficiencies: +* - It is not clear that the method of handling "extra" axes is +* the best one, nor is the method of setting the "following" flag +* necessarily correct. However, it is also not obvious that this +* feature will ever be needed, so improvements have been left +* until the requirement is clearer. +*/ + +/* Local Variables: */ + AstCmpFrame *target; /* Pointer to target CmpFrame structure */ + AstFrame *sub_result1; /* Pointer to result Frame for frame1 */ + AstFrame *sub_result2; /* Pointer to result Frame for frame2 */ + AstMapping *permmap_pref; /* Pointer to PermMap used as a prefix */ + AstMapping *permmap_suff; /* Pointer to PermMap used as a suffix */ + AstMapping *sub_map1; /* Pointer to Mapping from frame1 */ + AstMapping *sub_map2; /* Pointer to Mapping from frame2 */ + AstMapping *sub_map; /* Pointer to combined component Mappings */ + AstMapping *tmp_map; /* Temporary Mapping pointer */ + const int *perm; /* Pointer to axis permutation array */ + int *frame_choice; /* Pointer to flag array for partitioning */ + int *inperm_pref; /* Pointer to prefix permutation array */ + int *inperm_suff; /* Pointer to suffix permutation array */ + int *outperm_pref; /* Pointer to prefix permutation array */ + int *outperm_suff; /* Pointer to suffix permutation array */ + int *target_axes1; /* Pointer to frame1 axis selection array */ + int *target_axes2; /* Pointer to frame2 axis selection array */ + int *template_axes1; /* Pointer to frame1 template axis array */ + int *template_axes2; /* Pointer to frame2 template axis array */ + int axis_p; /* Permuted axis index */ + int following; /* Associate extra axis and following axis? */ + int i1; /* Count of axes obtained from frame1 */ + int i2; /* Count of axes obtained from frame2 */ + int match; /* Result value to return */ + int n1; /* Number of axes obtained from frame1 */ + int n2; /* Number of axes obtained from frame2 */ + int naxes1; /* Number of axes in frame1 */ + int naxes2; /* Number of axes in frame2 */ + int naxes; /* Number of axes in target */ + int result_axis; /* Result axis index */ + int target_axis; /* Target axis index */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the target CmpFrame structure. */ + target = (AstCmpFrame *) target_frame; + +/* Obtain the number of axes in the target CmpFrame and in each of its + component Frames. */ + naxes = astGetNaxes( target ); + naxes1 = astGetNaxes( target->frame1 ); + naxes2 = astGetNaxes( target->frame2 ); + +/* Iinitialise variables to avoid compiler warnings. */ + template_axes1 = NULL; + template_axes2 = NULL; + n1 = 0; + n2 = 0; + +/* Obtain the axis permutation array for the target CmpFrame. */ + perm = astGetPerm( target ); + +/* Determine how any "extra" axes should be associated with existing + axes (i.e. whether to associate with the preceding or following + axis). */ + following = astGetMatchEnd( target ); + +/* Split selected axes into two groups. */ +/* ------------------------------------ */ +/* Allocate a workspace array to hold the choice of component Frame + for each selected target axis. */ + frame_choice = astMalloc( sizeof( int ) * (size_t) result_naxes ); + +/* Obtain an array of flags indicating whether each selected target + axis should be obtained from the first or second component + Frame. */ + PartitionSelection( result_naxes, target_axes, perm, naxes1, naxes2, + frame_choice, following, status ); + +/* Allocate two arrays to hold the axis indices that refer to each of + the component Frames. The maximum number of indices is given by + "result_naxes" (if all the selected axes come from one component + Frame alone). */ + target_axes1 = astMalloc( sizeof( int ) * (size_t) result_naxes ); + target_axes2 = astMalloc( sizeof( int ) * (size_t) result_naxes ); + +/* If a template Frame has been provided, allocate similar arrays to + hold the indices of the two groups of template axes. */ + if ( template ) { + template_axes1 = astMalloc( sizeof( int ) * (size_t) result_naxes ); + template_axes2 = astMalloc( sizeof( int ) * (size_t) result_naxes ); + } + +/* Initialise the count of axes selected from each component Frame. */ + if ( astOK ) { + n1 = n2 = 0; + +/* Loop through each axis index to be selected from the CmpFrame. */ + for ( result_axis = 0; result_axis < result_naxes; result_axis++ ) { + target_axis = target_axes[ result_axis ]; + +/* Determine if the index refers to a valid CmpFrame axis. If it does, + then permute the index, otherwise set it to -1. */ + if ( ( target_axis >= 0 ) && ( target_axis < naxes ) ) { + axis_p = perm[ target_axis ]; + } else { + axis_p = -1; + } + +/* If the axis is to be selected from the first component Frame, store + the index of the axis to be selected. Also store the associated + template axis index (if any). */ + if ( frame_choice[ result_axis ] == 1 ) { + target_axes1[ n1 ] = axis_p; + if ( template ) { + template_axes1[ n1 ] = template_axes[ result_axis ]; + } + +/* Count the axes selected from the first component Frame. */ + n1++; + +/* If the axis is to be selected from the second component Frame, + store the index of the index to be selected (adjusting for the + offset in axis numbering). Also store the associated template axis + index (if any) and count the axes selected. */ + } else { + target_axes2[ n2 ] = ( axis_p == -1 ) ? -1 : axis_p - naxes1; + if ( template ) { + template_axes2[ n2 ] = template_axes[ result_axis ]; + } + n2++; + } + } + } + +/* Select from first component Frame only. */ +/* --------------------------------------- */ +/* If all the selected axes come from the first component Frame, use + that Frame's astSubFrame method to select them (and overlay the + template attributes if required). */ + if ( astOK ) { + if ( n1 && !n2 ) { + sub_map1 = NULL; + match = astSubFrame( target->frame1, template, n1, target_axes1, + template_axes1, &sub_map1, result ); + +/* If this is successful, the "result" Frame will be ready to return + and "sub_map1" will point at a Mapping that converts from the first + component Frame to the "result" Frame. We must now modify this + mapping to account for the CmpFrame's axis permutation array + (i.e. make it refer back to the CmpFrame's original axis order). */ + if ( astOK && match ) { + +/* To do this we must prefix the Mapping with a PermMap which converts + between the target CmpFrame axes and those of the first component + Frame. Allocate space for the permutation arrays required. */ + inperm_pref = astMalloc( sizeof( int ) * (size_t) naxes ); + outperm_pref = astMalloc( sizeof( int ) * (size_t) naxes1 ); + if ( astOK ) { + +/* Permute each target axis index. */ + for ( target_axis = 0; target_axis < naxes; target_axis++ ) { + axis_p = perm[ target_axis ]; + +/* Set up arrays that describe this permutation and its inverse. */ + if ( axis_p < naxes1 ) { + inperm_pref[ target_axis ] = axis_p; + outperm_pref[ axis_p ] = target_axis; + +/* Note which target axes do not correspond with axes in the first + component Frame and assign -1 (so the PermMap will assign "bad" + coordinate values to these axes). */ + } else { + inperm_pref[ target_axis ] = -1; + } + } + +/* Use these permutation arrays to construct the PermMap. Prefix this + to the Mapping obtained earlier to give the final Mapping to be + returned. */ + permmap_pref = + (AstMapping *) astPermMap( naxes, inperm_pref, + naxes1, outperm_pref, NULL, "", status ); + *map = (AstMapping *) astCmpMap( permmap_pref, sub_map1, 1, "", status ); + +/* Annul the PermMap pointer. */ + permmap_pref = astAnnul( permmap_pref ); + } + +/* Free the permutation arrays and annul the original Mapping pointer. */ + inperm_pref = astFree( inperm_pref ); + outperm_pref = astFree( outperm_pref ); + sub_map1 = astAnnul( sub_map1 ); + } + +/* Select from second component Frame only. */ +/* ---------------------------------------- */ +/* If all the selected axes come from the second component Frame, use + that Frame's astSubFrame method to select them (and overlay the + template attributes if required). */ + } else if ( n2 && !n1 ) { + sub_map2 = NULL; + match = astSubFrame( target->frame2, template, n2, target_axes2, + template_axes2, &sub_map2, result ); + +/* If this is successful, the "result" Frame will be ready to return + and "sub_map2" will point at a Mapping that converts from the second + component Frame to the "result" Frame. We must now modify this + mapping to account for the CmpFrame's axis permutation array + (i.e. make it refer back to the CmpFrame's original axis order). */ + if ( astOK && match ) { + +/* To do this we must prefix the Mapping with a PermMap which converts + between the target CmpFrame axes and those of the second component + Frame. Allocate space for the permutation arrays required. */ + inperm_pref = astMalloc( sizeof( int ) * (size_t) naxes ); + outperm_pref = astMalloc( sizeof( int ) * (size_t) naxes2 ); + if ( astOK ) { + +/* Permute each target axis index. */ + for ( target_axis = 0; target_axis < naxes; target_axis++ ) { + axis_p = perm[ target_axis ]; + +/* Set up arrays that describe this permutation and its inverse, + allowing for the shift in axis numbering for the second component + Frame. */ + if ( axis_p >= naxes1 ) { + inperm_pref[ target_axis ] = axis_p - naxes1; + outperm_pref[ axis_p - naxes1 ] = target_axis; + +/* Note which target axes do not correspond with axes in the second + component Frame and assign -1 (so the PermMap will assign "bad" + coordinate values to these axes). */ + } else { + inperm_pref[ target_axis ] = -1; + } + } + +/* Use these permutation arrays to construct the PermMap. Prefix this + to the Mapping obtained earlier to give the final Mapping to be + returned. */ + permmap_pref = + (AstMapping *) astPermMap( naxes, inperm_pref, + naxes2, outperm_pref, NULL, "", status ); + + *map = (AstMapping *) astCmpMap( permmap_pref, sub_map2, 1, "", status ); + +/* Annul the PermMap pointer. */ + permmap_pref = astAnnul( permmap_pref ); + } + +/* Free the permutation arrays and annul the original Mapping pointer. */ + inperm_pref = astFree( inperm_pref ); + outperm_pref = astFree( outperm_pref ); + sub_map2 = astAnnul( sub_map2 ); + } + +/* Select from both component Frames. */ +/* ---------------------------------- */ +/* If the selected axes come from both component Frames, then use both + Frames' astSubFrame methods to select the required axes from each + of them (and overlay the template attributes if required). */ + } else { + sub_map1 = NULL; + sub_map2 = NULL; + sub_result1 = NULL; + sub_result2 = NULL; + match = astSubFrame( target->frame1, template, n1, target_axes1, + template_axes1, &sub_map1, &sub_result1 ); + if ( match ) { + match = astSubFrame( target->frame2, template, n2, target_axes2, + template_axes2, &sub_map2, &sub_result2 ); + } + +/* If this is successful, the two "result" Frames will need to be + combined together (in a CmpFrame) in order to produce the required + result, and the two accompanying Mappings will also need to be + applied in parallel (in a CmpMap). However, the axis order + resulting from this will still not match that required. + + On the target side, this is because of the target's axis + permutation array. On the result side, it is because the result + axes cannot be inter-mingled (as may be required) simply by joining + the Frames and Mappings in parallel. The resulting CmpFrame axes + will therefore need permuting into the required final order. */ + if ( astOK && match ) { + +/* In addition, the Mappings will need to be both prefixed and + suffixed with suitable PermMaps which re-order the axes. Allocate + space for the permutation arrays required. */ + inperm_pref = astMalloc( sizeof( int ) * (size_t) naxes ); + outperm_pref = astMalloc( sizeof( int ) * (size_t) naxes ); + inperm_suff = astMalloc( sizeof( int ) * (size_t) result_naxes ); + outperm_suff = astMalloc( sizeof( int ) * (size_t) result_naxes ); + if ( astOK ) { + +/* Set up permutation arrays to construct the prefix PermMap. This + simply represents the target CmpFrame's axis permutation array and + its inverse. */ + for ( target_axis = 0; target_axis < naxes; target_axis++ ) { + axis_p = perm[ target_axis ]; + inperm_pref[ target_axis ] = axis_p; + outperm_pref[ axis_p ] = target_axis; + } + +/* Set up permutation arrays to construct the suffix PermMap. This + represents the way the original axis selections were partitioned + between the two component frames. */ + i1 = i2 = 0; + for ( result_axis = 0; result_axis < result_naxes; + result_axis++ ) { + +/* For each result axis derived from the first component Frame, set up + permutation array elements to link the output axis with the next + component Frame axis. Count the number of component Frame axes + used. */ + if ( frame_choice[ result_axis ] == 1 ) { + inperm_suff[ i1 ] = result_axis; + outperm_suff[ result_axis ] = i1; + i1++; + +/* Similarly link the axes derived from the second component Frame + with the appropriate axes of that Frame. */ + } else { + inperm_suff[ n1 + i2 ] = result_axis; + outperm_suff[ result_axis ] = n1 + i2; + i2++; + } + } + +/* Combine the Mappings supplied by the two component Frames in + parallel. */ + sub_map = (AstMapping *) astCmpMap( sub_map1, sub_map2, 0, "", status ); + +/* Create the PermMaps which are to be used as a prefix and a suffix. */ + permmap_pref = + (AstMapping *) astPermMap( naxes, inperm_pref, + naxes, outperm_pref, NULL, "", status ); + permmap_suff = + (AstMapping *) astPermMap( result_naxes, inperm_suff, + result_naxes, outperm_suff, + NULL, "", status ); + +/* Add the prefix and suffix PermMaps. */ + tmp_map = (AstMapping *) astCmpMap( permmap_pref, sub_map, + 1, "", status ); + *map = (AstMapping *) astCmpMap( tmp_map, permmap_suff, 1, "", status ); + +/* Annul the Mapping pointers that are no longer required. */ + sub_map = astAnnul( sub_map ); + permmap_pref = astAnnul( permmap_pref ); + permmap_suff = astAnnul( permmap_suff ); + tmp_map = astAnnul( tmp_map ); + +/* Create the result CmpFrame by combining the two component result + Frames and permuting the resulting axes into the required order. */ + *result = (AstFrame *) astCmpFrame( sub_result1, sub_result2, + "", status ); + astPermAxes( *result, outperm_suff ); + +/* ADDED BY DSB (5-FEB-2001). Without this, properties of the target frame + (most importantly, Domain) are not transferred to the result frame. + This results in Frames not matching which should match. + =================================================================== */ + +/* If the result CmpFrame includes all the axes of the target CmpFrame, + then it should inherit any Domain and Title attributes set in the target + CmpFrame. */ + if( result_naxes == naxes ) { + + if( astTestDomain( target ) ) { + astSetDomain( *result, astGetDomain( target ) ); + } + + if( astTestTitle( target ) ) { + astSetTitle( *result, astGetTitle( target ) ); + } + } + +/* End of DSB insertion (5/2/01). + =================================================================== */ + } + +/* Free the temporary permutation arrays. */ + inperm_pref = astFree( inperm_pref ); + inperm_suff = astFree( inperm_suff ); + outperm_pref = astFree( outperm_pref ); + outperm_suff = astFree( outperm_suff ); + } + +/* Annul the Mapping and Frame pointers obtained from each component + Frame. */ + if( sub_map1 ) sub_map1 = astAnnul( sub_map1 ); + if( sub_map2 ) sub_map2 = astAnnul( sub_map2 ); + if( sub_result1 ) sub_result1 = astAnnul( sub_result1 ); + if( sub_result2 ) sub_result2 = astAnnul( sub_result2 ); + } + } + +/* Free the workspace used to store the choice of component Frame and the + axis indices for each component Frame. */ + frame_choice = astFree( frame_choice ); + target_axes1 = astFree( target_axes1 ); + target_axes2 = astFree( target_axes2 ); + +/* If necessary, also free the memory used for the template axis + indices. */ + if ( template ) { + template_axes1 = astFree( template_axes1 ); + template_axes2 = astFree( template_axes2 ); + } + +/* If an error occurred, clean up by annulling the result pointers and + returning appropriate null values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a CmpFrame's attributes. + +* Parameters: +* this +* Pointer to the CmpFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + char buf1[80]; /* For for un-indexed attribute name */ + char buf2[80]; /* For for indexed attribute name */ + int axis; /* Supplied (1-base) axis index */ + int len; /* Length of attrib string */ + int nc; /* Length of string used so far */ + int oldrep; /* Original error reporting state */ + int paxis; /* Index of primary Frame axis */ + int result; /* Result value to return */ + int ok; /* Has the attribute been accessed succesfully? */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Indicate we have not yet acessed the attribute succesfully. */ + ok = 0; + +/* First check the supplied attribute name against each of the attribute + names defined by this class. In fact there is nothing to do here + since the CmpFrame class currently defines no extra attributes, but + this may change in the future. */ + if( 0 ) { + + + +/* If the attribute is not a CmpFrame specific attribute... */ + } else if( astOK ) { + +/* We want to allow easy access to the attributes of the component Frames. + That is, we do not want it to be necessary to extract a Frame from + its parent CmpFrame in order to access its attributes. For this reason + we first temporarily switch off error reporting so that if an attempt + to access the attribute fails, we can try a different approach. */ + oldrep = astReporting( 0 ); + +/* Our first attempt is to see if the attribute is recognised by the parent + class (Frame). */ + result = (*parent_testattrib)( this_object, attrib, status ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise, clear the error condition so that we can try a different + approach. */ + } else { + astClearStatus; + +/* If the attribute is qualified by an axis index, try accessing it as an + attribute of the primary Frame containing the specified index. */ + if ( nc = 0, + ( 2 == astSscanf( attrib, "%[^(](%d)%n", buf1, &axis, &nc ) ) + && ( nc >= len ) ) { + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + if( astOK ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astTest" ); + +/* Create a new attribute with the same name but with the axis index + appropriate to the primary Frame. */ + sprintf( buf2, "%s(%d)", buf1, paxis + 1 ); + +/* Attempt to access the attribute. */ + result = astTestAttrib( pfrm, buf2 ); + +/* Indicate success. */ + if( astOK ) { + ok = 1; + +/* Otherwise clear the status value, and try again without any axis index. */ + } else { + astClearStatus; + result = astTestAttrib( pfrm, buf1 ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + } + +/* Free the primary frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* If the attribute is not qualified by an axis index, try accessing it + using the primary Frame of each axis in turn. */ + } else { + +/* Loop round all axes, until one is found which defines the specified + attribute. */ + for( axis = 0; axis < astGetNaxes( this ) && !ok; axis++ ) { + +/* Get the primary Frame containing this axis. */ + astPrimaryFrame( this, axis, &pfrm, &paxis ); + +/* Attempt to access the attribute as an attribute of the primary Frame. */ + result = astTestAttrib( pfrm, attrib ); + +/* Indicate success, or clear the status value. */ + if( astOK ) { + ok = 1; + } else { + astClearStatus; + } + +/* Free the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + + } + } + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + + } + +/* Report an error if the attribute could not be accessed. */ + if( !ok && astOK ) { + astError( AST__BADAT, "astTest: The %s given does not have an attribute " + "called \"%s\".", status, astGetClass( this ), attrib ); + } + +/* Return the result. */ + return result; + +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astTransform method +* inherited from the Frame class). + +* Description: +* This function takes a CmpFrame and a set of points encapsulated +* in a PointSet, and applies the coordinate transformation equivalent +* to the CmpFrame (this will normally be a UnitMap but may not be if +* the CmpFrame contains any Regions). + +* Parameters: +* this +* Pointer to the CmpFrame. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The number of coordinate values per point in the input +* PointSet must match the number of axes in the CmpFrame. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and coordinate values per point to +* accommodate the result (e.g. the number of CmpFrame axes). Any +* excess space will be ignored. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to original CmpFrame structure */ + AstCmpMap *map2; /* Intermediate Mapping */ + AstCmpMap *map; /* Equivalent Mapping */ + AstPermMap *pmap; /* Intermediate PermMap */ + AstPointSet *result; /* Pointer value to return */ + const int *inperm; /* Pointer to axis permutation array */ + int *outperm; /* Pointer to inverse axis permutation array */ + int i; /* External axis index */ + int naxes; /* Number of axes in CmpFrame */ + int perm; /* Is there an axis permutation to undo? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_mapping; + +/* Form a parallel CmpMap from the two component Frames. */ + map = astCmpMap( this->frame1, this->frame2, 0, "", status ); + +/* The above CmpMap does not take into account any axis permutation + which has been applied to the CmpFrame as a whole (as opposed to axis + permutations applied to the individual component Frames, which are taken + care of by the Transform methods of the individual Frames). Therefore + we need to modify the Mapping by adding a PermMap at the start which + converts from external axis numbering to internal axis numbering, and a + corresponding PermMap at the end which converts from internal to external + axis numbering. Obtain the number of axes in the CmpFrame */ + naxes = astGetNaxes( this ); + +/* Obtain a pointer to the CmpFrame's axis permutation array. This + contains internal axis numbers and is indexed by external axis number. */ + inperm = astGetPerm( this ); + +/* Check if there is any axis permutation to be performed. */ + perm = 0; + for( i = 0; i < naxes; i++ ) { + if( inperm[ i ] != i ) { + perm = 1; + break; + } + } + +/* If so, create an array holding the inverse permutation - one which + contains external axis numbers and is indexed by internal axis number. */ + if( perm ) { + outperm = astMalloc( sizeof( int )*(size_t) naxes ); + if( astOK ) for( i = 0; i < naxes; i++ ) outperm[ inperm[ i ] ] = i; + +/* Create a PermMap from these permutation arrays. The forward + transformation maps from external axis indices to internal axis + indices. */ + pmap = astPermMap( naxes, inperm, naxes, outperm, NULL, "", status ); + outperm = astFree( outperm ); + +/* Combine this PermMap with the CmpMap created above, adding it in the + forward direction at the start and in the inverse direction at the end. */ + map2 = astCmpMap( pmap, map, 1, "", status ); + map = astAnnul( map ); + astInvert( pmap ); + map = astCmpMap( map2, pmap, 1, "", status ); + map2 = astAnnul( map2 ); + pmap = astAnnul( pmap ); + + } + +/* Apply the Mapping to the input PointSet. */ + result = astTransform( map, in, forward, out ); + +/* Annul the Mapping pointer. */ + map = astAnnul( map ); + +/* If an error has occurred and a new PointSet may have been created, then + clean up by annulling it. In any case, ensure that a NULL result is + returned.*/ + if ( !astOK ) { + if ( !out ) result = astAnnul( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static int Unformat( AstFrame *this_frame, int axis, const char *string, + double *value, int *status ) { +/* +* Name: +* Unformat + +* Purpose: +* Read a formatted coordinate value for a CmpFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* int Unformat( AstFrame *this, int axis, const char *string, +* double *value, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the public astUnformat +* method inherited from the Frame class). + +* Description: +* This function reads a formatted coordinate value for a CmpFrame +* axis (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the CmpFrame. +* axis +* The number of the CmpFrame axis for which the coordinate +* value is to be read (axis numbering starts at zero for the +* first axis). +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will be +* returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + AstFrame *frame; /* Pointer to Frame containing axis */ + double coord; /* Coordinate value read */ + int naxes1; /* Number of axes in frame1 */ + int nc; /* Number of characters read */ + int set; /* Digits attribute set? */ + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_frame; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astUnformat" ); + +/* Determine the number of axes in the first component Frame. */ + naxes1 = astGetNaxes( this->frame1 ); + if ( astOK ) { + +/* Decide which component Frame contains the axis and adjust the axis + index if necessary. */ + frame = ( axis < naxes1 ) ? this->frame1 : this->frame2; + axis = ( axis < naxes1 ) ? axis : axis - naxes1; + +/* Since the component Frame is "managed" by the enclosing CmpFrame, + we next test if any Frame attributes which may affect the result + are undefined (i.e. have not been explicitly set). If so, we + over-ride them, giving them temporary values dictated by the + CmpFrame. Only the Digits attribute is potentially relevant + here. */ + set = astTestDigits( frame ); + if ( !set ) astSetDigits( frame, astGetDigits( this ) ); + +/* Invoke the Frame's astUnformat method to read the coordinate value. */ + nc = astUnformat( frame, axis, string, &coord ); + +/* Clear Frame attributes which were temporarily over-ridden. */ + if ( !set ) astClearDigits( frame ); + } + +/* If an error occurred, clear the number of characters read. */ + if ( !astOK ) { + nc = 0; + +/* Otherwise, if characters were read, return the coordinate value. */ + } else if ( nc ) { + *value = coord; + } + +/* Return the number of chracters read. */ + return nc; +} + +static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) { +/* +* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a CmpFrame's System attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "cmpframe.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* CmpFrame member function (over-rides the astValidateSystem method +* inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST__BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the value is out of bounds, report an error. */ + if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) { + astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System " + "or AlignSystem attribute of a %s.", status, method, + astGetClass( this ), (int) system, astGetClass( this ) ); + +/* Otherwise, return the supplied value. */ + } else { + result = system; + } + +/* Return the result. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + the axes of a CmpFrame using the private macros defined for this + purpose at the start of this file. */ + +/* Direction(axis). */ +/* ---------------- */ +MAKE_CLEAR(Direction) +MAKE_GET(Direction,int,0,0,0) +MAKE_SET(Direction,int) +MAKE_TEST(Direction) + +/* Format(axis). */ +/* ------------- */ +MAKE_CLEAR(Format) +MAKE_GET(Format,const char *,NULL,0,NULL) +MAKE_SET(Format,const char *) +MAKE_TEST(Format) + +/* Label(axis). */ +/* ------------ */ +MAKE_CLEAR(Label) + +/* Over-ride the default axis labels produced by Frame class objects + and substitute the axis numbering of the enclosing CmpFrame + instead. */ +static const char *label_class; +MAKE_GET(Label,const char *,NULL,( label_class = astGetClass( frame ), + ( astOK && !strcmp( label_class, + "Frame" ) ) ), + ( (void) sprintf( label_buff, "Axis %d", axis + 1 ), label_buff )) +MAKE_SET(Label,const char *) +MAKE_TEST(Label) + +/* Symbol(axis). */ +/* ------------- */ +MAKE_CLEAR(Symbol) + +/* Over-ride the default axis symbols produced by Frame class objects + and substitute the axis numbering of the enclosing CmpFrame + instead. */ +static const char *symbol_class; +MAKE_GET(Symbol,const char *,NULL,( symbol_class = astGetClass( frame ), + ( astOK && !strcmp( symbol_class, + "Frame" ) ) ), + ( (void) sprintf( symbol_buff, "x%d", axis + 1 ), symbol_buff )) +MAKE_SET(Symbol,const char *) +MAKE_TEST(Symbol) + +/* Unit(axis). */ +/* ----------- */ +MAKE_CLEAR(Unit) +MAKE_GET(Unit,const char *,NULL,0,NULL) +MAKE_SET(Unit,const char *) +MAKE_TEST(Unit) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for CmpFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for CmpFrame objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstCmpFrame *in; /* Pointer to input CmpFrame */ + AstCmpFrame *out; /* Pointer to output CmpFrame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output CmpFrames. */ + in = (AstCmpFrame *) objin; + out = (AstCmpFrame *) objout; + +/* Copy the two component Frames. */ + out->frame1 = astCopy( in->frame1 ); + out->frame2 = astCopy( in->frame2 ); + +/* Determine the number of axes and copy the axis permutation + array. */ + out->perm = astStore( NULL, in->perm, sizeof( int ) * + (size_t) GetNaxes( (AstFrame *) in, status ) ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for CmpFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for CmpFrame objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error +* status is set. +*/ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to CmpFrame structure */ + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) obj; + +/* Annul the two component Frame pointers. */ + if ( this->frame1 ) this->frame1 = astAnnul( this->frame1 ); + if ( this->frame2 ) this->frame2 = astAnnul( this->frame2 ); + +/* Free the axis permutation array. */ + if ( this->perm ) this->perm = astFree( this->perm ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for CmpFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the CmpFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the CmpFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 150 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstCmpFrame *this; /* Pointer to the CmpFrame structure */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment strings */ + char key[ KEY_LEN + 1 ]; /* Buffer for keywords */ + int axis; /* Loop counter for CmpFrame axes */ + int full; /* Full attribute value */ + int full_set; /* Full attribute set? */ + int ival; /* Integer value */ + int naxes; /* Number of CmpFrame axes */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpFrame structure. */ + this = (AstCmpFrame *) this_object; + +/* Write out values representing the instance variables for the + CmpFrame class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Axis permutation array. */ +/* ----------------------- */ +/* Obtain the number of CmpFrame axes. */ + naxes = GetNaxes( (AstFrame *) this, status ); + +/* Write out the CmpFrame axis permutation array value for each axis, + converting to 1-based axis numbering. */ + for ( axis = 0; axis < naxes; axis++ ) { + set = ( this->perm[ axis ] != axis ); + ival = this->perm[ axis ] + 1; + +/* Create a keyword and comment appropriate to the axis. */ + (void) sprintf( key, "Axp%d", axis + 1 ); + if ( set ) { + (void) sprintf( comment, + "Axis %d permuted to use internal axis %d", + axis + 1, ival ); + } else { + (void) sprintf( comment, "Axis %d not permuted", axis + 1 ); + } + astWriteInt( channel, key, set, 0, ival, comment ); + } + +/* Component Frames. */ +/* ----------------- */ +/* Temporarily set the Channel's Full attribute to -1 (unless it is +1 + to start with), remembering the original setting. This prevents any + unnecessary "un-set" Frame values being output that would otherwise + simply duplicate the CmpFrame's attributes which have already been + written. "Set" Frame values are still written, however (and all + values are written if Full is set to 1). */ + full_set = astTestFull( channel ); + full = astGetFull( channel ); + if ( full <= 0 ) astSetFull( channel, -1 ); + +/* Write out Object descriptions for the two component Frames. */ + astWriteObject( channel, "FrameA", 1, 1, this->frame1, + "First component Frame" ); + astWriteObject( channel, "FrameB", 1, 1, this->frame2, + "Second component Frame" ); + +/* Restore the Channel's original Full attribute setting. */ + if ( full_set ) { + astSetFull( channel, full ); + } else { + astClearFull( channel ); + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsACmpFrame and astCheckCmpFrame functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(CmpFrame,Frame) +astMAKE_CHECK(CmpFrame) + +AstCmpFrame *astCmpFrame_( void *frame1_void, void *frame2_void, + const char *options, int *status, ...) { +/* +*++ +* Name: +c astCmpFrame +f AST_CMPFRAME + +* Purpose: +* Create a CmpFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "cmpframe.h" +c AstCmpFrame *astCmpFrame( AstFrame *frame1, AstFrame *frame2, +c const char *options, ... ) +f RESULT = AST_CMPFRAME( FRAME1, FRAME2, OPTIONS, STATUS ) + +* Class Membership: +* CmpFrame constructor. + +* Description: +* This function creates a new CmpFrame and optionally initialises +* its attributes. +* +* A CmpFrame is a compound Frame which allows two component Frames +* (of any class) to be merged together to form a more complex +* Frame. The axes of the two component Frames then appear together +* in the resulting CmpFrame (those of the first Frame, followed by +* those of the second Frame). +* +* Since a CmpFrame is itself a Frame, it can be used as a +* component in forming further CmpFrames. Frames of arbitrary +* complexity may be built from simple individual Frames in this +* way. +* +* Also since a Frame is a Mapping, a CmpFrame can also be used as a +* Mapping. Normally, a CmpFrame is simply equivalent to a UnitMap, +* but if either of the component Frames within a CmpFrame is a Region +* (a sub-class of Frame), then the CmpFrame will use the Region as a +* Mapping when transforming values for axes described by the Region. +* Thus input axis values corresponding to positions which are outside the +* Region will result in bad output axis values. + +* Parameters: +c frame1 +f FRAME1 = INTEGER (Given) +* Pointer to the first component Frame. +c frame2 +f FRAME2 = INTEGER (Given) +* Pointer to the second component Frame. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new CmpFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new CmpFrame. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astCmpFrame() +f AST_CMPFRAME = INTEGER +* A pointer to the new CmpFrame. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- + +* Implementation Notes: +* - This function implements the basic CmpFrame constructor which +* is available via the protected interface to the CmpFrame class. +* A public interface is provided by the astCmpFrameId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "frame1" and "frame2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpFrame *new; /* Pointer to new CmpFrame */ + AstFrame *frame1; /* Pointer to first Frame structure */ + AstFrame *frame2; /* Pointer to second Frame structure */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + new = NULL; + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Frame structures provided. */ + frame1 = astCheckFrame( frame1_void ); + frame2 = astCheckFrame( frame2_void ); + if ( astOK ) { + +/* Initialise the CmpFrame, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpFrame( NULL, sizeof( AstCmpFrame ), !class_init, + &class_vtab, "CmpFrame", frame1, frame2 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + CmpFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new CmpFrame. */ + return new; +} + +AstCmpFrame *astCmpFrameId_( void *frame1_void, void *frame2_void, + const char *options, ... ) { +/* +* Name: +* astCmpFrameId_ + +* Purpose: +* Create a CmpFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpframe.h" +* AstCmpFrame *astCmpFrameId_( void *frame1_void, void *frame2_void, +* const char *options, ... ) + +* Class Membership: +* CmpFrame constructor. + +* Description: +* This function implements the external (public) interface to the +* astCmpFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astCmpFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). For the same reason, the "frame1" and "frame2" +* parameters are of type (void *) and are converted and validated +* within the function itself. +* +* The variable argument list also prevents this function from +* invoking astCmpFrame_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. + +* Parameters: +* As for astCmpFrame_. + +* Returned Value: +* The ID value associated with the new CmpFrame. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpFrame *new; /* Pointer to new CmpFrame */ + AstFrame *frame1; /* Pointer to first Frame structure */ + AstFrame *frame2; /* Pointer to second Frame structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + new = NULL; + if ( !astOK ) return new; + +/* Obtain the Frame pointers from the ID's supplied and validate the + pointers to ensure they identify valid Frames. */ + frame1 = astVerifyFrame( astMakePointer( frame1_void ) ); + frame2 = astVerifyFrame( astMakePointer( frame2_void ) ); + if ( astOK ) { + +/* Initialise the CmpFrame, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpFrame( NULL, sizeof( AstCmpFrame ), !class_init, + &class_vtab, "CmpFrame", frame1, frame2 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + CmpFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new CmpFrame. */ + return astMakeId( new ); +} + +AstCmpFrame *astInitCmpFrame_( void *mem, size_t size, int init, + AstCmpFrameVtab *vtab, const char *name, + AstFrame *frame1, AstFrame *frame2, int *status ) { +/* +*+ +* Name: +* astInitCmpFrame + +* Purpose: +* Initialise a CmpFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpframe.h" +* AstCmpFrame *astInitCmpFrame( void *mem, size_t size, int init, +* AstCmpFrameVtab *vtab, const char *name, +* AstFrame *frame1, AstFrame *frame2 ) + +* Class Membership: +* CmpFrame initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new CmpFrame object. It allocates memory (if +* necessary) to accommodate the CmpFrame plus any additional data +* associated with the derived class. It then initialises a +* CmpFrame structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for a CmpFrame at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the CmpFrame is to be +* created. This must be of sufficient size to accommodate the +* CmpFrame data (sizeof(CmpFrame)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the CmpFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the CmpFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A logical flag indicating if the CmpFrame's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new CmpFrame. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the Object astClass function). +* frame1 +* Pointer to the first Frame to be included in the new CmpFrame. +* frame2 +* Pointer to the second Frame to be included in the new CmpFrame. + +* Returned Value: +* A pointer to the new CmpFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstCmpFrame *new; /* Pointer to new CmpFrame */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of CmpFrame axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitCmpFrameVtab( vtab, name ); + +/* Initialise a Frame structure (the parent class) as the first + component within the CmpFrame structure, allocating memory if + necessary. Set the number of Frame axes to zero, since all axis + information is stored within the component Frames. */ + new = (AstCmpFrame *) astInitFrame( mem, size, 0, (AstFrameVtab *) vtab, + name, 0 ); + + if ( astOK ) { + +/* Initialise the CmpFrame data. */ +/* ----------------------------- */ +/* Clone the component Frame pointers. */ + new->frame1 = astClone( frame1 ); + new->frame2 = astClone( frame2 ); + +/* Determine the number of CmpFrame axes. */ + naxes = astGetNaxes( frame1 ) + astGetNaxes( frame2 ); + +/* Allocate memory to hold the axis permutation array and initialise + this array. */ + new->perm = astMalloc( sizeof( int ) * (size_t) naxes ); + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) new->perm[ axis ] = axis; + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstCmpFrame *astLoadCmpFrame_( void *mem, size_t size, + AstCmpFrameVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadCmpFrame + +* Purpose: +* Load a CmpFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpframe.h" +* AstCmpFrame *astLoadCmpFrame( void *mem, size_t size, +* AstCmpFrameVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* CmpFrame loader. + +* Description: +* This function is provided to load a new CmpFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* CmpFrame structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the CmpFrame is to be +* loaded. This must be of sufficient size to accommodate the +* CmpFrame data (sizeof(CmpFrame)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the CmpFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the CmpFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstCmpFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new CmpFrame. If this is NULL, a pointer +* to the (static) virtual function table for the CmpFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "CmpFrame" is used instead. + +* Returned Value: +* A pointer to the new CmpFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstCmpFrame *new; /* Pointer to the new CmpFrame */ + char key[ KEY_LEN + 1 ]; /* Buffer for keywords */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of CmpFrame axes */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this CmpFrame. In this case the + CmpFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstCmpFrame ); + vtab = &class_vtab; + name = "CmpFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitCmpFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built CmpFrame. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "CmpFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Component Frames. */ +/* ----------------- */ +/* Read both component Frames, supplying a default 1-dimensional Frame + if necessary. */ + new->frame1 = astReadObject( channel, "framea", NULL ); + if ( !new->frame1 ) new->frame1 = astFrame( 1, "", status ); + + new->frame2 = astReadObject( channel, "frameb", NULL ); + if ( !new->frame2 ) new->frame2 = astFrame( 1, "", status ); + +/* Axis permutation array. */ +/* ----------------------- */ +/* Obtain the number of CmpFrame axes and allocate memory to hold the + axis permutation array. */ + naxes = GetNaxes( (AstFrame *) new, status ); + new->perm = astMalloc( sizeof( int ) * (size_t) naxes ); + +/* If OK, loop to read the array value for each axis. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + +/* Convert from 1-based to zero-based axis numbering at this + point. The default is the "un-permuted" value. */ + sprintf( key, "axp%d", axis + 1 ); + new->perm[ axis ] = astReadInt( channel, key, axis + 1 ) - 1; + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + +/* If an error occurred, clean up by deleting the new CmpFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new CmpFrame pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + diff --git a/cmpframe.h b/cmpframe.h new file mode 100644 index 0000000..f692aa1 --- /dev/null +++ b/cmpframe.h @@ -0,0 +1,428 @@ +#if !defined( CMPFRAME_INCLUDED ) /* Include this file only once */ +#define CMPFRAME_INCLUDED +/* +*+ +* Name: +* cmpframe.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the CmpFrame class. + +* Invocation: +* #include "cmpframe.h" + +* Description: +* This include file defines the interface to the CmpFrame class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. +* +* A CmpFrame is a compound Frame which allows two component Frames +* (of any class) to be merged together to form a more complex +* Frame. The axes of the two component Frames then appear together +* in the resulting CmpFrame (those of the first Frame, followed by +* those of the second Frame). +* +* Since a CmpFrame is itself a Frame, it can be used as a +* component in forming further CmpFrames. Frames of arbitrary +* complexity may be built from simple individual Frames in this +* way. + +* Inheritance: +* The CmpFrame class inherits from the Frame class. + +* Attributes Over-Ridden: +* Domain (string) +* A string which may be used to identify a CmpFrame and used as +* an additional key when matching a target CmpFrame with a +* template. The CmpFrame class re-defines the default value to +* "CMP". +* MaxAxes (integer) +* Specifies the maximum number of axes in a target Frame that +* can be matched when using the CmpFrame as a template. The +* CmpFrame class sets this to be the sum of the MaxAxes +* attribute values for the two component Frames contained by +* the CmpFrame. Any attempt to alter this value (other than +* through the component Frames) is simply ignored. +* MinAxes (integer) +* Specifies the minimum number of axes in a target Frame that +* can be matched when using the CmpFrame as a template. The +* CmpFrame class sets this to be the sum of the MinAxes +* attribute values for the two component Frames contained by +* the CmpFrame. Any attempt to alter this value (other than +* through the component Frames) is simply ignored. +* Naxes (integer) +* A read-only attribute that gives the number of axes in a +* CmpFrame (i.e. the number of dimensions of the space which +* the CmpFrame describes). This value is determined when the +* CmpFrame is created and is equal to the sum of the Naxes +* attributes of the two component Frames. +* Title (string) +* Specifies a string to be used as a title on (e.g.) graphs to +* describe the coordinate system which the CmpFrame +* represents. The CmpFrame class re-defines the default value +* to "-D Compound Coordinate System", where is the +* number of CmpFrame axes. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* astDistance +* Calculate the distance between two points. +* astFormat +* Format a coordinate value for a CmpFrame axis. +* astNorm +* Normalise a set of CmpFrame coordinates. +* astOffset +* Calculate an offset along a geodesic curve. +* astPermAxes +* Permute the order of a CmpFrame's axes. +* astUnformat +* Read a formatted coordinate value for a CmpFrame axis. +* +* Protected: +* astAbbrev +* Abbreviate a formatted CmpFrame axis value by skipping leading +* fields. +* astClearDirection +* Clear the value of the Direction attribute for a CmpFrame axis. +* astClearFormat +* Clear the value of the Format attribute for a CmpFrame axis. +* astClearLabel +* Clear the value of the Label attribute for a CmpFrame axis. +* astClearMaxAxes +* Clear the value of the MaxAxes attribute for a CmpFrame. +* astClearMinAxes +* Clear the value of the MinAxes attribute for a CmpFrame. +* astClearSymbol +* Clear the value of the Symbol attribute for a CmpFrame axis. +* astClearUnit +* Clear the value of the Unit attribute for a CmpFrame axis. +* astGap +* Find a "nice" gap for tabulating CmpFrame axis values. +* astGetAxis +* Obtain a pointer to a specified Axis from a CmpFrame. +* astGetDirection +* Obtain the value of the Direction attribute for a CmpFrame axis. +* astGetDomain +* Obtain a pointer to the Domain attribute string for a CmpFrame. +* astGetFormat +* Obtain the value of the Format attribute for a CmpFrame axis. +* astGetLabel +* Obtain the value of the Label attribute for a CmpFrame axis. +* astGetMaxAxes +* Obtain the value of the MaxAxes attribute for a CmpFrame. +* astGetMinAxes +* Obtain the value of the MinAxes attribute for a CmpFrame. +* astGetNaxes +* Obtain the value of the Naxes attribute for a CmpFrame. +* astGetPerm +* Access the axis permutation array for a CmpFrame. +* astGetSymbol +* Obtain the value of the Symbol attribute for a CmpFrame axis. +* astGetTitle +* Obtain a pointer to the Title attribute string for a CmpFrame. +* astGetUnit +* Obtain the value of the Unit attribute for a CmpFrame axis. +* astMatch +* Determine if conversion is possible between two coordinate +* systems. +* astPrimaryFrame +* Uniquely identify a primary Frame and one of its axes. +* astSetAxis +* Set a new Axis for a CmpFrame. +* astSetDirection +* Set the value of the Direction attribute for a CmpFrame axis. +* astSetFormat +* Set the value of the Format attribute for a CmpFrame axis. +* astSetLabel +* Set the value of the Label attribute for a CmpFrame axis. +* astSetMaxAxes +* Set a value for the MaxAxes attribute of a CmpFrame. +* astSetMinAxes +* Set a value for the MinAxes attribute of a CmpFrame. +* astSetSymbol +* Set the value of the Symbol attribute for a CmpFrame axis. +* astSetUnit +* Set the value of the Unit attribute for a CmpFrame axis. +* astSubFrame +* Select axes from a CmpFrame and convert to the new coordinate +* system. +* astTestDirection +* Test if a Direction attribute value has been set for a CmpFrame +* axis. +* astTestFormat +* Test if a Format attribute value has been set for a CmpFrame axis. +* astTestLabel +* Test if a Label attribute value has been set for a CmpFrame axis. +* astTestMaxAxes +* Test if a value has been set for the MaxAxes attribute of a +* CmpFrame. +* astTestMinAxes +* Test if a value has been set for the MinAxes attribute of a +* CmpFrame. +* astTestSymbol +* Test if a Symbol attribute value has been set for a CmpFrame axis. +* astTestUnit +* Test if a Unit attribute value has been set for a CmpFrame axis. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsACmpFrame +* Test class membership. +* astCmpFrame +* Create a CmpFrame. +* +* Protected: +* astCheckCmpFrame +* Validate class membership. +* astInitCmpFrame +* Initialise a CmpFrame. +* astInitCmpFrameVtab +* Initialise the virtual function table for the CmpFrame class. +* astLoadCmpFrame +* Load a CmpFrame. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstCmpFrame +* CmpFrame object type. +* +* Protected: +* AstCmpFrameVtab +* CmpFrame virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 6-MAR-1996 (RFWS): +* Original version. +* 27-FEB-1997 (RFWS): +* Improved the prologue. +* 25-FEB-1998 (RFWS): +* Over-ride the astUnformat method. +* 8-JAN-2003 (DSB): +* Added protected astInitCmpFrameVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "frame.h" /* Parent Frame class */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ------- */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) /* Protected */ + +/* The legal System values recognized by this class of Frame. */ +#define AST__COMP 0 + +#endif + +/* Type Definitions. */ +/* ================= */ +/* CmpFrame structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstCmpFrame { + +/* Attributes inherited from the parent class. */ + AstFrame frame; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstFrame *frame1; /* First component frame */ + AstFrame *frame2; /* Second component Frame */ + int *perm; /* Pointer to axis permutation array */ +} AstCmpFrame; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstCmpFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + +} AstCmpFrameVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstCmpFrameGlobals { + AstCmpFrameVtab Class_Vtab; + int Class_Init; + int *qsort_axes; + char Label_Buff[ 101 ]; + char Symbol_Buff[ 51 ]; + char GetDomain_Buff[ 101 ]; + char GetTitle_Buff[ 101 ]; +} AstCmpFrameGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(CmpFrame) /* Check class membership */ +astPROTO_ISA(CmpFrame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstCmpFrame *astCmpFrame_( void *, void *, const char *, int *, ...); +#else +AstCmpFrame *astCmpFrameId_( void *, void *, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstCmpFrame *astInitCmpFrame_( void *, size_t, int, AstCmpFrameVtab *, + const char *, AstFrame *, AstFrame *, int * ); + +/* Vtab initialiser. */ +void astInitCmpFrameVtab_( AstCmpFrameVtab *, const char *, int * ); + +/* Loader. */ +AstCmpFrame *astLoadCmpFrame_( void *, size_t, AstCmpFrameVtab *, + const char *, AstChannel *, int * ); +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitCmpFrameGlobals_( AstCmpFrameGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckCmpFrame(this) astINVOKE_CHECK(CmpFrame,this,0) +#define astVerifyCmpFrame(this) astINVOKE_CHECK(CmpFrame,this,1) + +/* Test class membership. */ +#define astIsACmpFrame(this) astINVOKE_ISA(CmpFrame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astCmpFrame astINVOKE(F,astCmpFrame_) +#else +#define astCmpFrame astINVOKE(F,astCmpFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitCmpFrame(mem,size,init,vtab,name,frame1,frame2) \ +astINVOKE(O,astInitCmpFrame_(mem,size,init,vtab,name,astCheckFrame(frame1),astCheckFrame(frame2),STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitCmpFrameVtab(vtab,name) astINVOKE(V,astInitCmpFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadCmpFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadCmpFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckCmpFrame to validate CmpFrame pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#endif + + + + + diff --git a/cmpframe.pdf b/cmpframe.pdf new file mode 100644 index 0000000..8d81909 Binary files /dev/null and b/cmpframe.pdf differ diff --git a/cmpmap.c b/cmpmap.c new file mode 100644 index 0000000..a9a5b96 --- /dev/null +++ b/cmpmap.c @@ -0,0 +1,4739 @@ +/* +*class++ +* Name: +* CmpMap + +* Purpose: +* Compound Mapping. + +* Constructor Function: +c astCmpMap +f AST_CMPMAP + +* Description: +* A CmpMap is a compound Mapping which allows two component +* Mappings (of any class) to be connected together to form a more +* complex Mapping. This connection may either be "in series" +* (where the first Mapping is used to transform the coordinates of +* each point and the second mapping is then applied to the +* result), or "in parallel" (where one Mapping transforms the +* earlier coordinates for each point and the second Mapping +* simultaneously transforms the later coordinates). +* +* Since a CmpMap is itself a Mapping, it can be used as a +* component in forming further CmpMaps. Mappings of arbitrary +* complexity may be built from simple individual Mappings in this +* way. + +* Inheritance: +* The CmpMap class inherits from the Mapping class. + +* Attributes: +* The CmpMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The CmpMap class does not define any new functions beyond those +f The CmpMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 1-FEB-1996 (RFWS): +* Original version. +* 25-SEP-1996 (RFWS): +* Implemented external interface and I/O facilities. +* 12-DEC-1996 (RFWS): +* Over-ride the astMapList method. +* 13-DEC-1996 (RFWS): +* Over-ride the astSimplify method. +* 4-JUN-1997 (RFWS): +* Eliminate any simplification when MapList is used. Instead, +* over-ride the MapMerge method and implement all +* simplification in this. +* 24-MAR-1998 (RFWS): +* Fixed bug in testing of simplified invert flag in Simplify. +* 15-APR-1998 (RFWS): +* Improved the MapMerge method to allow parallel combinations +* of series CmpMaps to be replaced by series combinations of +* parallel CmpMaps, and vice versa. +* 26-SEP-2001 (DSB): +* Over-ride the astDecompose method. +* 8-JAN-2003 (DSB): +* - Changed private InitVtab method to protected astInitCmpMapVtab +* method. +* 8-JAN-2003 (DSB): +* - Modified MapMerge so that a parallel CmpMap can swap with a +* suitable PermMap lower neighbour. +* 23-APR-2004 (DSB): +* - Modified Simplify to avoid infinite loops. +* 27-APR-2004 (DSB): +* - Correction to MapMerge to prevent segvio if CmpMap and PermMap +* cannot be swapped. +* 4-OCT-2004 (DSB): +* Modify astMapList to return flag indicating presence of inverted +* CmpMaps in supplied Mapping. +* 20-APR-2005 (DSB): +* Modify MapMerge so that it will attempt to merge the first +* and second CmpMaps in a list of series CmpMaps. +* 8-FEB-2006 (DSB): +* Corrected logic within MapMerge for cases where a PermMap is +* followed by a parallel CmpMap. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 14-MAR-2006 (DSB): +* - When checking for patterns in the simplification process, +* require at least 30 samples in the waveform for evidence of a +* pattern. +* - Override astEqual. +* - The constructor no longer reports an error if the resulting +* CmpMap cannot transform points in either direction. This is +* because it may be possible to simplify such a CmpMap and the +* simplified Mapping may have defined transformations. E.g. if a +* Mapping which has only a forward transformation is combined in +* series with its own inverse, the combination will simplify to a +* UnitMap (usually). +* 9-MAY-2006 (DSB): +* - In Simplify, remove checks for patterns in the number of atomic +* mappings when calling astSimplify recursively. +* 23-AUG-2006 (DSB): +* - In Simplify, add checks for re-appearance of a Mapping that is +* already being simplified at a higher levelin the call stack. +* 18-APR-2007 (DSB): +* In Simplify: if the returned Mapping is not a CmpMap, always copy +* the returned component Mapping (rather than cloning it) so that +* the returned Mapping is not affected if user code subsequently +* inverts the component Mapping via some other pointer. +* 12-MAR-2008 (DSB): +* Modify MapSplit so that attempts to split the inverse +* transformation if it cannot split the forward transformation. +* 30-JUL-2009 (DSB): +* Ensure the PermMap has equal number of inputs and outputs when +* swapping a PermMap and a CmpMap in astMapMerge. +* 3-JAN-2011 (DSB): +* In MapSplit, certain classes of Mapping (e.g. PermMaps) can +* produce a returned Mapping with zero outputs. Consider such +* Mappings to be unsplitable. +* 11-JAN-2011 (DSB): +* Improve simplification of serial combinations of parellel CmpMaps. +* 25-JAN-2011 (DSB): +* Big improvement to the efficiency of the astMapSplit method. +* 24-JAN-2012 (DSB): +* If efficient MapSplit fails to split (e.g. due to the presence +* of PermMaps), then revert to the older slower method. +* 5-FEB-2013 (DSB): +* Take account of Invert flags when combining parallel CmpMaps in +* series. +* 29-APR-2013 (DSB): +* In MapList, use the astDoNotSimplify method to check that it is +* OK to expand the CmpMap. +* 23-APR-2015 (DSB): +* In Simplify, prevent mappings that are known to cause infinite +* loops from being nominated for simplification. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS CmpMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "permmap.h" /* Coordinate permutation Mappings */ +#include "unitmap.h" /* Unit transformations */ +#include "cmpmap.h" /* Interface definition for this class */ +#include "frameset.h" /* Interface definition for FrameSets */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int (* parent_maplist)( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->Simplify_Depth = 0; \ + globals->Simplify_Stackmaps = NULL; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(CmpMap) + +#define class_init astGLOBAL(CmpMap,Class_Init) +#define class_vtab astGLOBAL(CmpMap,Class_Vtab) +#define simplify_depth astGLOBAL(CmpMap,Simplify_Depth) +#define simplify_stackmaps astGLOBAL(CmpMap,Simplify_Stackmaps) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static int simplify_depth = 0; +static AstMapping **simplify_stackmaps = NULL; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstCmpMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstCmpMap *astCmpMapId_( void *, void *, int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *CombineMaps( AstMapping *, int, AstMapping *, int, int, int * ); +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int *MapSplit0( AstMapping *, int, const int *, AstMapping **, int, int * ); +static int *MapSplit1( AstMapping *, int, const int *, AstMapping **, int * ); +static int *MapSplit2( AstMapping *, int, const int *, AstMapping **, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int MapList( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int PatternCheck( int, int, int **, int *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int GetObjSize( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two CmpMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two CmpMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a CmpMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the CmpMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpMap *that; + AstCmpMap *this; + AstMapping **that_map_list; + AstMapping **this_map_list; + int *that_invert_list; + int *this_invert_list; + int i; + int result; + int that_inv; + int that_nmap; + int this_inv; + int this_nmap; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two CmpMap structures. */ + this = (AstCmpMap *) this_object; + that = (AstCmpMap *) that_object; + +/* Check the second object is a CmpMap. We know the first is a + CmpMap since we have arrived at this implementation of the virtual + function. */ + if( astIsACmpMap( that ) ) { + +/* Check they are both either parallel or series. */ + if( that->series == that->series ) { + +/* Decompose the first CmpMap into a sequence of Mappings to be applied in + series or parallel, as appropriate, and an associated list of + Invert flags. */ + this_nmap = 0; + this_map_list = NULL; + this_invert_list = NULL; + astMapList( (AstMapping *) this, this->series, astGetInvert( this ), + &this_nmap, &this_map_list, &this_invert_list ); + +/* Similarly decompose the second CmpMap. */ + that_nmap = 0; + that_map_list = NULL; + that_invert_list = NULL; + astMapList( (AstMapping *) that, that->series, astGetInvert( that ), + &that_nmap, &that_map_list, &that_invert_list ); + +/* Check the decompositions yielded the same number of component + Mappings. */ + if( that_nmap == this_nmap ) { + +/* Check equality of every component. */ + for( i = 0; i < this_nmap; i++ ) { + +/* Temporarily set the Mapping Invert flags to the required values, + saving the original values so that they can be re-instated later.*/ + this_inv = astGetInvert( this_map_list[ i ] ); + astSetInvert( this_map_list[ i ], this_invert_list[ i ] ); + that_inv = astGetInvert( that_map_list[ i ] ); + astSetInvert( that_map_list[ i ], that_invert_list[ i ] ); + +/* Compare the two component Mappings for equality. */ + result = astEqual( this_map_list[ i ], that_map_list[ i ] ); + +/* Re-instate the original Invert flags. */ + astSetInvert( this_map_list[ i ], this_inv ); + astSetInvert( that_map_list[ i ], that_inv ); + +/* Leave the loop if the Mappings are not equal. */ + if( !result ) break; + } + } + +/* Free resources */ + for( i = 0; i < this_nmap; i++ ) { + this_map_list[ i ] = astAnnul( this_map_list[ i ] ); + } + this_map_list = astFree( this_map_list ); + this_invert_list = astFree( this_invert_list ); + + for( i = 0; i < that_nmap; i++ ) { + that_map_list[ i ] = astAnnul( that_map_list[ i ] ); + } + that_map_list = astFree( that_map_list ); + that_invert_list = astFree( that_invert_list ); + + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a CmpMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the protected astGetIsLinear +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsLinear attribute for a +* Frame, which is one if both component Mappings have a value of 1 +* for the IsLinear attribute. + +* Parameters: +* this +* Pointer to the CmpqMap. +* status +* Pointer to the inherited status variable. +*/ + AstCmpMap *this; + this = (AstCmpMap *) this_mapping; + return astGetIsLinear( this->map1 ) && astGetIsLinear( this->map2 ); +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied CmpMap, +* in bytes. + +* Parameters: +* this +* Pointer to the CmpMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpMap *this; /* Pointer to CmpMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the CmpMap structure. */ + this = (AstCmpMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->map1 ); + result += astGetObjSize( this->map2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static AstMapping *CombineMaps( AstMapping *mapping1, int invert1, + AstMapping *mapping2, int invert2, + int series, int *status ) { +/* +* Name: +* CombineMaps + +* Purpose: +* Combine two Mappings with specified Invert flags into a CmpMap. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* AstMapping *CombineMaps( AstMapping *mapping1, int invert1, +* AstMapping *mapping2, int invert2, +* int series, int *status ) + +* Class Membership: +* CmpMap member function. + +* Description: +* This function combines two Mappings into a CmpMap (compound +* Mapping) as if their Invert flags were set to specified values +* when the CmpMap is created. However, the individual Mappings are +* returned with their Invert flag values unchanged from their +* original state. + +* Parameters: +* mapping1 +* Pointer to the first Mapping. +* invert1 +* The (boolean) Invert flag value required for the first Mapping. +* mapping2 +* Pointer to the second Mapping. +* invert2 +* The (boolean) Invert flag value required for the second Mapping. +* series +* Whether the Mappings are to be combined in series (as opposed to +* in parallel). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the resulting compound Mapping (a CmpMap). + +* Notes: +* - This function is a wrap-up for the astCmpMap constructor and +* temporarily assigns the required Invert flag values while +* creating the required CmpMap. However, it also takes account of +* the possibility that the two Mapping pointers supplied may point +* at the same Mapping. +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *map1; /* First temporary Mapping pointer */ + AstMapping *map2; /* Second temporary Mapping pointer */ + AstMapping *result; /* Pointer to result Mapping */ + int copy; /* Copy needed? */ + int inv1; /* First original Invert flag value */ + int inv2; /* Second original Invert flag value */ + int set1; /* First Invert flag originally set? */ + int set2; /* Second Invert flag originally set? */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Limit incoming values to 0 or 1. */ + invert1 = ( invert1 != 0 ); + invert2 = ( invert2 != 0 ); + +/* Obtain the Invert flag values for each Mapping. */ + inv1 = astGetInvert( mapping1 ); + inv2 = astGetInvert( mapping2 ); + +/* Also determine if these values are explicitly set. */ + set1 = astTestInvert( mapping1 ); + set2 = astTestInvert( mapping2 ); + +/* If both Mappings are actually the same but we need different Invert + flag values to be set, then this can only be achieved by making a + copy. Note if this is necessary. */ + copy = ( ( mapping1 == mapping2 ) && ( invert1 != invert2 ) ); + +/* Clone the first Mapping pointer. Do likewise for the second but + make a copy instead if necessary. */ + map1 = astClone( mapping1 ); + map2 = copy ? astCopy( mapping2 ) : astClone( mapping2 ); + +/* If the Invert value for the first Mapping needs changing, make the + change. */ + if ( invert1 != inv1 ) { + if ( invert1 ) { + astSetInvert( map1, 1 ); + } else { + astClearInvert( map1 ); + } + } + +/* Similarly, change the Invert flag for the second Mapping if + necessary. */ + if ( invert2 != inv2 ) { + if ( invert2 ) { + astSetInvert( map2, 1 ); + } else { + astClearInvert( map2 ); + } + } + +/* Combine the two Mappings into a CmpMap. */ + result = (AstMapping *) astCmpMap( map1, map2, series, "", status ); + +/* If the first Mapping's Invert value was changed, restore it to its + original state. */ + if ( invert1 != inv1 ) { + if ( set1 ) { + astSetInvert( map1, inv1 ); + } else { + astClearInvert( map1 ); + } + } + +/* Similarly, restore the second Mapping's Invert value if + necessary. This step is not needed, however, if a copy was made. */ + if ( ( invert2 != inv2 ) && !copy ) { + if ( set2 ) { + astSetInvert( map2, inv2 ); + } else { + astClearInvert( map2 ); + } + } + +/* Annul the temporary Mapping pointers. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + +/* If an error occurred, then annul the result pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void Decompose( AstMapping *this_mapping, AstMapping **map1, + AstMapping **map2, int *series, int *invert1, + int *invert2, int *status ) { +/* +* +* Name: +* Decompose + +* Purpose: +* Decompose a Mapping into two component Mappings. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void Decompose( AstMapping *this, AstMapping **map1, +* AstMapping **map2, int *series, +* int *invert1, int *invert2, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the protected astDecompose +* method inherited from the Mapping class). + +* Description: +* This function returns pointers to two Mappings which, when applied +* either in series or parallel, are equivalent to the supplied Mapping. +* +* Since the Frame class inherits from the Mapping class, Frames can +* be considered as special types of Mappings and so this method can +* be used to decompose either CmpMaps or CmpFrames. + +* Parameters: +* this +* Pointer to the Mapping. +* map1 +* Address of a location to receive a pointer to first component +* Mapping. +* map2 +* Address of a location to receive a pointer to second component +* Mapping. +* series +* Address of a location to receive a value indicating if the +* component Mappings are applied in series or parallel. A non-zero +* value means that the supplied Mapping is equivalent to applying map1 +* followed by map2 in series. A zero value means that the supplied +* Mapping is equivalent to applying map1 to the lower numbered axes +* and map2 to the higher numbered axes, in parallel. +* invert1 +* The value of the Invert attribute to be used with map1. +* invert2 +* The value of the Invert attribute to be used with map2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component Mappings using the returned +* pointers will be reflected in the supplied Mapping. + +*- +*/ + + +/* Local Variables: */ + AstCmpMap *this; /* Pointer to CmpMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpMap *) this_mapping; + +/* First deal with series mappings. */ + if( this->series ) { + if( series ) *series = 1; + +/* If the CmpMap has been inverted, return the Mappings in reverse + order with inverted Invert falgs. */ + if( astGetInvert( this ) ) { + if( map1 ) *map1 = astClone( this->map2 ); + if( map2 ) *map2 = astClone( this->map1 ); + if( invert1 ) *invert1 = this->invert2 ? 0 : 1; + if( invert2 ) *invert2 = this->invert1 ? 0 : 1; + +/* If the CmpMap has not been inverted, return the Mappings in their + original order with their original Invert flags. */ + } else { + if( map1 ) *map1 = astClone( this->map1 ); + if( map2 ) *map2 = astClone( this->map2 ); + if( invert1 ) *invert1 = this->invert1; + if( invert2 ) *invert2 = this->invert2; + } + +/* Now deal with parallel mappings. */ + } else { + if( series ) *series = 0; + +/* The mappings are returned in their original order whether or not the + CmpMap has been inverted. */ + if( map1 ) *map1 = astClone( this->map1 ); + if( map2 ) *map2 = astClone( this->map2 ); + +/* If the CmpMap has been inverted, return inverted Invert flags. */ + if( astGetInvert( this ) ) { + if( invert1 ) *invert1 = this->invert1 ? 0 : 1; + if( invert2 ) *invert2 = this->invert2 ? 0 : 1; + +/* If the CmpMap has not been inverted, return the original Invert flags. */ + } else { + if( invert1 ) *invert1 = this->invert1; + if( invert2 ) *invert2 = this->invert2; + } + + } +} + +void astInitCmpMapVtab_( AstCmpMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitCmpMapVtab + +* Purpose: +* Initialise a virtual function table for a CmpMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpmap.h" +* void astInitCmpMapVtab( AstCmpMapVtab *vtab, const char *name ) + +* Class Membership: +* CmpMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the CmpMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsACmpMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* None. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_maplist = mapping->MapList; + mapping->MapList = MapList; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_mapsplit = mapping->MapSplit; + mapping->MapSplit = MapSplit; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->Decompose = Decompose; + mapping->MapMerge = MapMerge; + mapping->Simplify = Simplify; + mapping->RemoveRegions = RemoveRegions; + mapping->GetIsLinear = GetIsLinear; + +/* For some reason the CmpMap implementation of astRate can be immensely + slow for complex Mapping, so it's currently disable until such time as + I have time to sort it out. + + mapping->Rate = Rate; +*/ + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "CmpMap", "Compound Mapping" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstCmpMap *this; /* Pointer to CmpMap structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the CmpMap structure. */ + this = (AstCmpMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->map1, mode, extra, fail ); + if( !result ) result = astManageLock( this->map2, mode, extra, fail ); + + return result; + +} +#endif + +static int MapList( AstMapping *this_mapping, int series, int invert, + int *nmap, AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapList + +* Purpose: +* Decompose a CmpMap into a sequence of simpler Mappings. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapList( AstMapping *this, int series, int invert, int *nmap, +* AstMapping ***map_list, int **invert_list ) + +* Class Membership: +* CmpMap member function (over-rides the protected astMapList +* method inherited from the Maping class). + +* Description: +* This function decomposes a CmpMap into a sequence of simpler +* Mappings which may be applied in sequence to achieve the same +* effect. The CmpMap is decomposed as far as possible, but it is +* not guaranteed that this will necessarily yield any more than +* one Mapping, which may actually be the original CmpMap supplied. +* +* This function is provided to support both the simplification of +* CmpMaps, and the analysis of CmpMap structure so that particular +* forms can be recognised. + +* Parameters: +* this +* Pointer to the CmpMap to be decomposed (the CmpMap is not +* actually modified by this function). +* series +* If this value is non-zero, an attempt will be made to +* decompose the CmpMap into a sequence of equivalent Mappings +* which can be applied in series (i.e. one after the other). If +* it is zero, the decomposition will instead yield Mappings +* which can be applied in parallel (i.e. on successive sub-sets +* of the input/output coordinates). +* invert +* The value to which the CmpMap's Invert attribute is to be +* (notionally) set before performing the +* decomposition. Normally, the value supplied here will be the +* actual Invert value obtained from the CmpMap (e.g. using +* astGetInvert). Sometimes, however, when a CmpMap is +* encapsulated within another structure, that structure may +* retain an Invert value (in order to prevent external +* interference) which should be used instead. +* +* Note that the actual Invert value of the CmpMap supplied is +* not used (or modified) by this function. +* nmap +* The address of an int which holds a count of the number of +* individual Mappings in the decomposition. On entry, this +* should count the number of Mappings already in the +* "*map_list" array (below). On exit, it is updated to include +* any new Mappings appended by this function. +* map_list +* Address of a pointer to an array of Mapping pointers. On +* entry, this array pointer should either be NULL (if no +* Mappings have yet been obtained) or should point at a +* dynamically allocated array containing Mapping pointers +* ("*nmap" in number) which have been obtained from a previous +* invocation of this function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Mapping pointers that result from the decomposition +* requested. These pointers will be appended to any previously +* present, and the array pointer will be updated as necessary +* to refer to the enlarged array (any space released by the +* original array will be freed automatically). +* +* The new Mapping pointers returned will identify a sequence of +* Mappings which, when applied in order, will perform a forward +* transformation equivalent to that of the original CmpMap +* (after its Invert flag has first been set to the value +* requested above). The Mappings should be applied in series or +* in parallel according to the type of decomposition requested. +* +* All the Mapping pointers returned by this function should be +* annulled by the caller, using astAnnul, when no longer +* required. The dynamic array holding these pointers should +* also be freed, using astFree. +* invert_list +* Address of a pointer to an array of int. On entry, this array +* pointer should either be NULL (if no Mappings have yet been +* obtained) or should point at a dynamically allocated array +* containing Invert attribute values ("*nmap" in number) which +* have been obtained from a previous invocation of this +* function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Invert attribute values that result from the +* decomposition requested. These values will be appended to any +* previously present, and the array pointer will be updated as +* necessary to refer to the enlarged array (any space released +* by the original array will be freed automatically). +* +* The new Invert values returned identify the values which must +* be assigned to the Invert attributes of the corresponding +* Mappings (whose pointers are in the "*map_list" array) before +* they are applied. Note that these values may differ from the +* actual Invert attribute values of these Mappings, which are +* not relevant. +* +* The dynamic array holding these values should be freed by the +* caller, using astFree, when no longer required. + +* Returned Value: +* A non-zero value is returned if the supplied Mapping contained any +* inverted CmpMaps. + +* Notes: +* - It is unspecified to what extent the original CmpMap and the +* individual (decomposed) Mappings are +* inter-dependent. Consequently, the individual Mappings cannot be +* modified without risking modification of the original CmpMap. +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then the *nmap value, the +* list of Mapping pointers and the list of Invert values will all +* be returned unchanged. +*/ + +/* Local Variables: */ + AstCmpMap *this; /* Pointer to CmpMap structure */ + int invert1; /* Invert flag for first component Mapping */ + int invert2; /* Invert flag for second component Mapping */ + int r1; /* Value returned from first map list */ + int r2; /* Value returned from second map list */ + int result; /* Returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpMap *) this_mapping; + +/* Check if the CmpMap combines its component Mappings in the same way + (series or parallel) as the decomposition requires. Also, do not + expand CmpMaps that are not appropriate for simplification. */ + if ( this->series == series && !astDoNotSimplify( this ) ) { + +/* If so, obtain the Invert attribute values to be applied to each + component Mapping. */ + invert1 = this->invert1; + invert2 = this->invert2; + +/* If the CmpMap itself is inverted, also invert the Invert values to be + applied to its components. */ + if ( invert ) { + invert1 = !invert1; + invert2 = !invert2; + } + +/* If the component Mappings are applied in series, then concatenate + the Mapping lists obtained from each of them. Do this in reverse + order if the CmpMap is inverted, since the second Mapping would be + applied first in this case. */ + if ( series ) { + if ( !invert ) { + r1 = astMapList( this->map1, series, invert1, + nmap, map_list, invert_list ); + r2 = astMapList( this->map2, series, invert2, + nmap, map_list, invert_list ); + } else { + r1 = astMapList( this->map2, series, invert2, + nmap, map_list, invert_list ); + r2 = astMapList( this->map1, series, invert1, + nmap, map_list, invert_list ); + } + +/* If the component Mappings are applied in parallel, then concatenate + the Mapping lists obtained from each of them. In this case, + inverting the CmpMap has no effect on the order in which they are + applied. */ + } else { + r1 = astMapList( this->map1, series, invert1, + nmap, map_list, invert_list ); + r2 = astMapList( this->map2, series, invert2, + nmap, map_list, invert_list ); + } + +/* Did we find any inverted CmpMaps? */ + result = invert || r1 || r2; + +/* If the CmpMap does not combine its components in the way required + by the decomposition (series or parallel), then we cannot decompose + it. In this case it must be appended to the Mapping list as a + single entity. We can use the parent class method to do this. */ + } else { + result = ( *parent_maplist )( this_mapping, series, invert, nmap, + map_list, invert_list, status ); + } + + return result; +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a CmpMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list ) + +* Class Membership: +* CmpMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated CmpMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated CmpMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated CmpMap which is to be merged with +* its neighbours. This should be a cloned copy of the CmpMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* CmpMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated CmpMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpMap *cmpmap1; /* Pointer to first CmpMap */ + AstCmpMap *cmpmap2; /* Pointer to second CmpMap */ + AstCmpMap *cmpmap; /* Pointer to nominated CmpMap */ + AstCmpMap *new_cm; /* Pointer to new CmpMap */ + AstMapping **map_list1; /* Pointer to list of cmpmap1 component Mappings */ + AstMapping **map_list2; /* Pointer to list of cmpmap2 component Mappings */ + AstMapping **new_map_list; /* Extended Mapping list */ + AstMapping *map; /* Pointer to nominated CmpMap */ + AstMapping *new1; /* Pointer to new CmpMap */ + AstMapping *new2; /* Pointer to new CmpMap */ + AstMapping *new; /* Pointer to replacement Mapping */ + AstMapping *simp1; /* Pointer to simplified Mapping */ + AstMapping *simp2; /* Pointer to simplified Mapping */ + AstMapping *submap1; /* A subset of mappings from cmpmap1 */ + AstMapping *submap2; /* A subset of mappings from cmpmap2 */ + AstMapping *tmap2; /* Temporary Mapping */ + AstMapping *tmap; /* Temporary Mapping */ + AstPermMap *new_pm; /* Pointer to new PermMap */ + AstPermMap *permmap1; /* Pointer to first PermMap */ + AstUnitMap *unit; /* UnitMap that feeds const PermMap i/p's */ + const char *class; /* Pointer to Mapping class string */ + double *conperm; /* Pointer to PermMap constants array */ + double *const_new; /* Pointer to new PermMap constants array */ + double *p; /* Pointer to PermMap input position */ + double *q; /* Pointer to PermMap output position */ + double *qa; /* Pointer to 1st component output position */ + double *qb; /* Pointer to 2nd component output position */ + int *inperm; /* Pointer to copy of PermMap inperm array */ + int *inperm_new; /* Pointer to new PermMap inperm array */ + int *invert_list1; /* Pointer to list of cmpmap1 invert values */ + int *invert_list2; /* Pointer to list of cmpmap2 invert values */ + int *new_invert_list; /* Extended Invert flag list */ + int *outperm; /* Pointer to copy of PermMap outperm array */ + int *outperm_new; /* Pointer to new PermMap outperm array */ + int aconstants; /* Are all 1st component outputs constant? */ + int bconstants; /* Are all 2nd component outputs constant? */ + int canswap; /* Can nominated Mapping swap with lower neighbour? */ + int i; /* Coordinate index */ + int iconid; /* Constant identifier in supplied PermMap */ + int imap1; /* Index of first Mapping */ + int imap2; /* Index of second Mapping */ + int imap; /* Loop counter for Mappings */ + int invert1; /* Invert flag for first CmpMap */ + int invert1a; /* Invert flag for sub-Mapping */ + int invert1b; /* Invert flag for sub-Mapping */ + int invert2; /* Invert flag for second CmpMap */ + int invert2a; /* Invert flag for sub-Mapping */ + int invert2b; /* Invert flag for sub-Mapping */ + int invert; /* Invert attribute value */ + int j; /* Coordinate index */ + int jmap1; /* Index of next component Mapping in cmpmap1 */ + int jmap2; /* Index of next component Mapping in cmpmap2 */ + int new_invert; /* New Invert attribute value */ + int nin2a; /* No. input coordinates for sub-Mapping */ + int nin2b; /* No. input coordinates for sub-Mapping */ + int nmap1; /* Number of Mappings in cmpmap1 */ + int nmap2; /* Number of Mappings in cmpmap2 */ + int nout2a; /* No. of outputs for 1st component Mapping */ + int nout2b; /* No. of outputs for 2nd component Mapping */ + int npin; /* No. of inputs for original PermMap */ + int npin_new; /* No. of inputs for new PermMap */ + int npout; /* No. of outputs for original PermMap */ + int npout_new; /* No. of outputs for new PermMap */ + int nunit; /* No. of PermMap i/p's fed by UnitMap */ + int oconid; /* Constant identifier in returned PermMap */ + int result; /* Result value to return */ + int set; /* Invert attribute set? */ + int simpler; /* Simplification possible? */ + int subin2; /* Number of inputs of submap2 */ + int subinv1; /* Invert attribute to use with submap1 */ + int subinv2; /* Invert attribute to use with submap2 */ + int subout1; /* Number of outputs of submap1 */ + +/* Initialise.*/ + result = -1; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Simplify the CmpMap on its own. */ +/* =============================== */ +/* Obtain a pointer to the nominated Mapping (which is a CmpMap). */ + map = ( *map_list )[ where ]; + cmpmap = (AstCmpMap *) map; + +/* Determine if the Mapping's Invert attribute is set and obtain its + value. */ + set = astTestInvert( map ); + invert = astGetInvert( map ); + +/* If necessary, change the Invert attribute to the value we want. We + do this so that simplification (below) has a chance to absorb a + non-zero Invert value into the implementation of the simplified + Mapping (the preference being to have an Invert value of zero after + simplification, if possible). */ + if ( invert != ( *invert_list )[ where ] ) { + astSetInvert( map, ( *invert_list )[ where ] ); + } + +/* Simplify the Mapping and obtain the new Invert value. */ + new = astSimplify( map ); + new_invert = astGetInvert( new ); + +/* If necessary, restore the original Mapping's Invert attribute to + its initial state. */ + if ( invert != ( *invert_list )[ where ] ) { + if ( set ) { + astSetInvert( map, invert ); + } else { + astClearInvert( map ); + } + } + +/* We must now determine if simplification has occurred. Since this is + internal code, we can compare the two Mapping pointers directly to + see whether "astSimplify" just cloned the pointer we gave it. If it + did, then simplification was probably not possible, but check to + see if the Invert attribute has changed to be sure. */ + if ( astOK ) { + simpler = ( new != map ) || ( new_invert != ( *invert_list )[ where ] ); + +/* If simplification was successful, annul the original pointer in the + Mapping list and replace it with the new one, together with its + invert flag. */ + if ( simpler ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = new; + ( *invert_list )[ where ] = new_invert; + +/* Return the result. */ + result = where; + +/* Otherwise, annul the new Mapping pointer. */ + } else { + new = astAnnul( new ); + +/* If the nominated CmpMap is a series CmpMap and the sequence of + Mappings are being combined in series, or if the nominated CmpMap is + a parallel CmpMap and the sequence of Mappings are being combined in + parallel, replace the single CmpMap with the two component Mappings. */ + if( ( series && cmpmap->series ) || + ( !series && !cmpmap->series ) ) { + +/* We are increasing the number of Mappings in the list, so we need to create + new, larger, arrays to hold the list of Mapping pointers and invert flags. */ + new_map_list = astMalloc( ( *nmap + 1 )*sizeof( AstMapping * ) ); + new_invert_list = astMalloc( ( *nmap + 1 )*sizeof( int ) ); + if( astOK ) { + +/* Copy the values prior to the nominated CmpMap. */ + for( i = 0; i < where; i++ ) { + new_map_list[ i ] = astClone( ( *map_list )[ i ] ); + new_invert_list[ i ] = ( *invert_list )[ i ]; + } + +/* Next insert the two components of the nominated CmpMap */ + new_map_list[ where ] = astClone( cmpmap->map1 ); + new_invert_list[ where ] = cmpmap->invert1; + new_map_list[ where + 1 ] = astClone( cmpmap->map2 ); + new_invert_list[ where + 1 ] = cmpmap->invert2; + +/* Now copy any values after the nominated CmpMap. */ + for( i = where + 1; i < *nmap; i++ ) { + new_map_list[ i + 1 ] = astClone( ( *map_list )[ i ] ); + new_invert_list[ i + 1 ] = ( *invert_list )[ i ]; + } + +/* Now annul the Object pointers in the supplied map list. */ + for( i = 0; i < *nmap; i++ ) { + (* map_list )[ i ] = astAnnul( ( *map_list )[ i ] ); + } + +/* Free the memory holding the supplied Mapping and invert flag lists. */ + astFree( *map_list ); + astFree( *invert_list ); + +/* Return pointers to the new extended lists. */ + *map_list = new_map_list; + *invert_list = new_invert_list; + +/* Increase the number of Mappings in the list, and the index of + the first modified Mapping. */ + (*nmap)++; + result = where; + +/* Indicate some simplification has taken place */ + simpler = 1; + } + } + } + +/* If no simplification has been done, merge adjacent CmpMaps. */ +/* ========================================================== */ +/* If the CmpMap would not simplify on its own, we now look for a + neighbouring CmpMap with which it might merge. We use the previous + Mapping, if suitable, since this will normally also have been fully + simplified on its own. Check if a previous Mapping exists. */ + if( !simpler ) { + if ( astOK && *nmap > 1 ) { + +/* Obtain the indices of the two potential Mappings to be merged. imap1 + is the first Mapping, imap2 is the second. imapc is the CmpMap, imapn is + the neighbouring Mapping. */ + if( where == 0 ) { + imap1 = 0; + imap2 = 1; + } else { + imap1 = where - 1; + imap2 = where; + } + +/* Obtain the Class string of the neighbouring Mapping and determine if it + is a CmpMap. */ + class = astGetClass( ( *map_list )[ (where>0)?where-1:1 ] ); + if ( astOK && !strcmp( class, "CmpMap" ) ) { + +/* If suitable, obtain pointers to the two CmpMaps. */ + cmpmap1 = (AstCmpMap *) ( *map_list )[ imap1 ]; + cmpmap2 = (AstCmpMap *) ( *map_list )[ imap2 ]; + +/* Obtain the associated invert flag values. */ + invert1 = ( *invert_list )[ imap1 ]; + invert2 = ( *invert_list )[ imap2 ]; + +/* Extract the invert flags associated with each CmpMap sub-Mapping + and combine these with the flag values obtained above so as to give + the invert flag to be used with each individual sub-Mapping. */ + invert1a = cmpmap1->invert1; + invert1b = cmpmap1->invert2; + if ( invert1 ) { + invert1a = !invert1a; + invert1b = !invert1b; + } + invert2a = cmpmap2->invert1; + invert2b = cmpmap2->invert2; + if ( invert2 ) { + invert2a = !invert2a; + invert2b = !invert2b; + } + +/* Series CmpMaps in parallel. */ +/* =========================== */ +/* Now check if the CmpMaps can be merged. This may be possible if we + are examining a list of Mappings combined in parallel and the two + adjacent CmpMaps both combine their sub-Mappings in series. */ + if ( !series && cmpmap1->series && cmpmap2->series ) { + +/* Form two new parallel CmpMaps with the sub-Mappings re-arranged so + that when combined in series these new CmpMaps are equivalent to + the original ones. In doing this, we must take account of the + invert flags which apply to each sub-Mapping and also of the fact + that the order in which the sub-Mappings are applied depends on the + invert flags of the original CmpMaps. */ + new1 = CombineMaps( invert1 ? cmpmap1->map2 : cmpmap1->map1, + invert1 ? invert1b : invert1a, + invert2 ? cmpmap2->map2 : cmpmap2->map1, + invert2 ? invert2b : invert2a, 0, status ); + new2 = CombineMaps( invert1 ? cmpmap1->map1 : cmpmap1->map2, + invert1 ? invert1a : invert1b, + invert2 ? cmpmap2->map1 : cmpmap2->map2, + invert2 ? invert2a : invert2b, 0, status ); + +/* Having converted the parallel combination of series CmpMaps into a + pair of equivalent parallel CmpMaps that can be combined in series, + try and simplify each of these new CmpMaps. */ + simp1 = astSimplify( new1 ); + simp2 = astSimplify( new2 ); + +/* Test if either could be simplified by checking if its pointer value + has changed. Also check if the Invert attribute has changed (not + strictly necessary, but a useful safety feature in case of any + rogue code which just changes this attribute instead of issuing a + new pointer). */ + simpler = ( simp1 != new1 ) || ( simp2 != new2 ) || + astGetInvert( simp1 ) || astGetInvert( simp2 ); + +/* If either CmpMap was simplified, then combine the resulting + Mappings in series to give the replacement CmpMap. */ + if ( simpler ) new = + (AstMapping *) astCmpMap( simp1, simp2, 1, "", status ); + +/* Annul the temporary Mapping pointers. */ + new1 = astAnnul( new1 ); + new2 = astAnnul( new2 ); + simp1 = astAnnul( simp1 ); + simp2 = astAnnul( simp2 ); + +/* Parallel CmpMaps in series. */ +/* =========================== */ +/* A pair of adjacent CmpMaps can also potentially be merged if we are + examining a list of Mappings combined in series and the two + adjacent CmpMaps both combine their sub-Mappings in parallel. */ + } else if ( series && !cmpmap1->series && !cmpmap2->series ) { + +/* Expand each of the two adjacent CmpMaps into a list of Mappings to be + combined in parallel. */ + map_list1 = map_list2 = NULL; + invert_list1 = invert_list2 = NULL; + nmap1 = nmap2 = 0; + (void) astMapList( (AstMapping *) cmpmap1, 0, invert1, + &nmap1, &map_list1, &invert_list1 ); + (void) astMapList( (AstMapping *) cmpmap2, 0, invert2, + &nmap2, &map_list2, &invert_list2 ); + +/* We want to divide each of these lists into N sub-lists so that the + outputs of the Mappings in the i'th sub-list from cmpmap1 can feed + (i.e. equal in number) the inputs of the Mappings in the i'th sub-list + from cmpmap2. If such a sub-list contains more than one Mapping we + combine them together into a parallel CmpMap. Initialise a flag to + indicate that we have not yet found any genuine simplification. */ + simpler = 0; + +/* Initialise the index of the next Mapping to be added into each + sublist. */ + jmap1 = jmap2 = 0; + +/* Indicate both sublists are currently empty. */ + subout1 = subin2 = 0; + new = submap1 = submap2 = NULL; + subinv1 = subinv2 = 0; + +/* Loop round untill all Mappings have been used. */ + while( jmap1 <= nmap1 && jmap2 <= nmap2 && astOK ) { + +/* Note the number of outputs from submap1 and the number of inputs to + submap2. If the Invert flag is not set to the required value for + either Mapping, then inputs become outputs and vice-versa, so swap Nin + and Nout. */ + if( !submap1 ) { + subout1 = 0; + } else if( subinv1 == astGetInvert( submap1 ) ) { + subout1 = astGetNout( submap1 ); + } else { + subout1 = astGetNin( submap1 ); + } + + if( !submap2 ) { + subin2 = 0; + } else if( subinv2 == astGetInvert( submap2 ) ) { + subin2 = astGetNin( submap2 ); + } else { + subin2 = astGetNout( submap2 ); + } + +/* If sublist for cmpmap1 has too few outputs, add the next Mapping from + the cmpmap1 list into the submap1 sublist. */ + if( subout1 < subin2 ) { + tmap = CombineMaps( submap1, subinv1, + map_list1[ jmap1 ], + invert_list1[ jmap1 ], 0, status ); + (void) astAnnul( submap1 ); + submap1 = tmap; + subinv1 = 0; + jmap1++; + +/* If sublist for cmpmap2 has too few inputs, add the next Mapping from + the cmpmap2 list into the submap2 sublist. */ + } else if( subin2 < subout1 ) { + tmap = CombineMaps( submap2, subinv2, + map_list2[ jmap2 ], + invert_list2[ jmap2 ], 0, status ); + (void) astAnnul( submap2 ); + submap2 = tmap; + subinv2 = 0; + jmap2++; + +/* If submap1 can now feed submap2, combine them in series, and attempt to + simplify it. */ + } else { + +/* Check this is not the first pass (when we do not have a submap1 or + submap2). */ + if( submap1 && submap2 ) { + +/* Combine the Mappings in series and simplify. */ + tmap = CombineMaps( submap1, subinv1, submap2, + subinv2, 1, status ); + submap1 = astAnnul( submap1 ); + submap2 = astAnnul( submap2 ); + tmap2 = astSimplify( tmap ); + tmap = astAnnul( tmap ); + +/* Note if any simplification took place. */ + if( tmap != tmap2 || + astGetInvert( tmap ) != astGetInvert( tmap2 ) ) + simpler = 1; + +/* Add the simplifed Mapping into the total merged Mapping (a parallel + CmpMap). */ + if( !new ) { + new = tmap2; + } else { + tmap = (AstMapping *) astCmpMap( new, tmap2, 0, + " ", status ); + tmap2 = astAnnul( tmap2 ); + (void) astAnnul( new ); + new = tmap; + } + } + +/* Reset submap1 to be the next Mapping from the cmpmap1 map list. First, + save its old Invert flag and set it to the required value. */ + if( jmap1 < nmap1 ) { + submap1 = astClone( map_list1[ jmap1 ] ); + subinv1 = invert_list1[ jmap1 ]; + jmap1++; + } else { + break; + } + +/* Do the same for the second list. */ + if( jmap2 < nmap2 ) { + submap2 = astClone( map_list2[ jmap2 ] ); + subinv2 = invert_list2[ jmap2 ]; + jmap2++; + } else { + break; + } + } + } + +/* Free the lists of Mapping pointers and invert flags. */ + if( map_list1 ) { + for( jmap1 = 0; jmap1 < nmap1; jmap1++ ) { + map_list1[ jmap1 ] = astAnnul( map_list1[ jmap1 ] ); + } + map_list1 = astFree( map_list1 ); + } + invert_list1 = astFree( invert_list1 ); + + if( map_list2 ) { + for( jmap2 = 0; jmap2 < nmap2; jmap2++ ) { + map_list2[ jmap2 ] = astAnnul( map_list2[ jmap2 ] ); + } + map_list2 = astFree( map_list2 ); + } + invert_list2 = astFree( invert_list2 ); + + } + } + +/* Update Mapping list. */ +/* ==================== */ +/* If adjacent CmpMaps can be combined, then annul the original pointers. */ + if ( astOK && simpler ) { + ( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] ); + ( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] ); + +/* Insert the pointer to the replacement CmpMap and initialise its + invert flag. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Loop to close the resulting gap by moving subsequent elements down + in the arrays. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - 1 ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - 1 ] = ( *invert_list )[ imap ]; + } + +/* Clear the vacated elements at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = imap1; + } + } + } + } + +/* If we are merging the Mappings in series, and if the nominated CmpMap + is a parallel CmpMap, and if the lower neighbour is a PermMap, it may + be possible to swap the PermMap and the CmpMap. This may allow one of + the two swapped Mappings to merge with its new neighbour. + ==================================================================== */ + +/* Only do this if no simplification occurred above, and if the Mappings + are being merged in series, and if the nominated Mapping is not the + first in the list. */ + if( result == -1 && where > 0 ){ + +/* Obtain the indices of the two potential Mappings to be swapped. */ + imap1 = where - 1; + imap2 = where; + +/* Obtain a pointer to the CmpMap. */ + cmpmap2 = (AstCmpMap *) ( *map_list )[ imap2 ]; + +/* Obtain the Class string of the first (previous) Mapping and + determine if it is a PermMap. Also check that the nominated Mapping is + a parallel CmpMap. */ + class = astGetClass( ( *map_list )[ imap1 ] ); + if ( astOK && !strcmp( class, "PermMap" ) && !cmpmap2->series) { + +/* Indicate we have no new Mapping to store. */ + new = NULL; + +/* If suitable, obtain a pointer to the PermMap. */ + permmap1 = (AstPermMap *) ( *map_list )[ imap1 ]; + +/* Obtain the current values of the Invert attribute in the Mappings. */ + invert1 = astGetInvert( permmap1 ); + invert2 = astGetInvert( cmpmap2 ); + +/* Temporarily set the Invert attributes of both Mappings to the values + supplied in the "invert_list" parameter. */ + astSetInvert( permmap1, ( *invert_list )[ imap1 ] ); + astSetInvert( cmpmap2, ( *invert_list )[ imap2 ] ); + +/* Get the number of inputs and outputs for the PermMap.*/ + npout = astGetNout( permmap1 ); + npin = astGetNin( permmap1 ); + +/* Get the number of inputs and outputs for the two components of the + nominated parallel CmpMap. */ + nin2a = astGetNin( cmpmap2->map1 ); + nin2b = astGetNin( cmpmap2->map2 ); + nout2a = astGetNout( cmpmap2->map1 ); + nout2b = astGetNout( cmpmap2->map2 ); + +/* Get the input and output axis permutation arrays and the constants + array from the PermMap */ + inperm =astGetInPerm( permmap1 ); + outperm =astGetOutPerm( permmap1 ); + conperm = astGetConstants( permmap1 ); + +/* In order to swap the Mappings, the PermMap outputs which feed the + inputs of the first component of the parallel CmpMap must be copied + from a contiguous block at the end of the list of PermMap inputs, or + must all be assigned constant values. Likewise, the PermMap outputs which + feed the inputs of the second component of the parallel CmpMap must be + copied from a contiguous block at the beggining of the list of PermMap + inputs or must be assigned constant values. Also, there must be a + one-to-one correspondance between inputs and outputs in the PermMap. + Check that the first block of nin2a PermMap outputs are copied from + the last block of nin2a PermMap inputs (and vica-versa) or are constant. */ + canswap = ( npin == npout ); + aconstants = ( outperm[ 0 ] < 0 ); + + for( i = 0, j = npin - nin2a; i < nin2a; i++, j++ ) { + if( aconstants ) { + if( outperm[ i ] >= 0 ) { + canswap = 0; + break; + } + + } else if( outperm[ i ] != j || inperm[ j ] != i ) { + canswap = 0; + break; + } + } + +/* Check that the first block of nin2b PermMap inputs are copied from + the last block of nin2b PermMap outputs, and vica-versa. */ + bconstants = ( outperm[ nin2a ] < 0 ); + for( i = 0, j = npout - nin2b; i < nin2b; i++, j++ ) { + if( bconstants ) { + if( outperm[ j ] >= 0 ) { + canswap = 0; + break; + } + } else if( inperm[ i ] != j || outperm[ j ] != i ) { + canswap = 0; + break; + } + } + +/* If the Mappings can be swapped.. */ + new_pm = NULL; + new_cm = NULL; + qa = NULL; + qb = NULL; + if( canswap ) { + +/* Temporarily set the Invert attributes of the component Mappings to the + values they had when the CmpMap was created. */ + invert2a = astGetInvert( cmpmap2->map1 ); + invert2b = astGetInvert( cmpmap2->map2 ); + astSetInvert( cmpmap2->map1, cmpmap2->invert1 ); + astSetInvert( cmpmap2->map2, cmpmap2->invert2 ); + +/* If any PermMap outputs are constant, we will need the results of + transforming these constants using the CmpMap which follows. */ + if( aconstants || bconstants ) { + +/* Transform a set of bad inputs using the PermMap. This will assign the + PermMap constant to any fixed outputs. */ + p = astMalloc( sizeof( double )*(size_t) npin ); + q = astMalloc( sizeof( double )*(size_t) npout ); + qa = astMalloc( sizeof( double )*(size_t) nout2a ); + qb = astMalloc( sizeof( double )*(size_t) nout2b ); + if( astOK ) { + for( i = 0; i < npin; i++ ) p[ i ] = AST__BAD; + astTranN( permmap1, 1, npin, 1, p, 1, npout, 1, q ); + +/* Transform the PermMap outputs using the two component Mappings in the + CmpMap. */ + astTranN( cmpmap2->map1, 1, nin2a, 1, q, 1, nout2a, 1, qa ); + astTranN( cmpmap2->map2, 1, nin2b, 1, q + nin2a, 1, nout2b, 1, qb ); + + } + p = astFree( p ); + q = astFree( q ); + } + +/* If necessary, create a UnitMap to replace a Mapping which has constant + outputs. The number of axes for the UnitMap is chosen to give the + correct total number of inputs for the final parallel CmpMap. At the + same time determine the number of inputs needed by the final PermMap. */ + if( aconstants ) { + nunit = npin - nin2b; + npin_new = nout2b + nunit; + } else if( bconstants ) { + nunit = npin - nin2a; + npin_new = nout2a + nunit; + } else { + nunit = 0; + npin_new = nout2a + nout2b; + } + unit = nunit ? astUnitMap( nunit, "", status ) : NULL; + +/* Determine the number of outputs for the final PermMap and allocate memory + for its permutation arrays. */ + npout_new = nout2a + nout2b; + outperm_new = astMalloc( sizeof( int )*(size_t) npout_new ); + inperm_new = astMalloc( sizeof( int )*(size_t) npin_new ); + const_new = astMalloc( sizeof( double )*(size_t) ( npout_new + npin_new ) ); + if( astOK ) { + oconid = 0; + +/* First assign permutations for the second component Mapping, if used. */ + if( !bconstants ) { + for( i = 0, j = npout_new - nout2b; i < nout2b; i++,j++ ) { + inperm_new[ i ] = j; + outperm_new[ j ] = i; + } + +/* Otherwise, store constants */ + } else { + + for( i = 0; i < nunit; i++ ){ + iconid = inperm[ i ]; + if( iconid >= npout ) { + inperm_new[ i ] = npout_new; + + } else if( iconid >= 0 ) { + astError( AST__INTER, "astMapMerge(CmpMap): Swapped PermMap " + "input is not constant (internal AST programming " + "error)." , status); + break; + + } else { + inperm_new[ i ] = --oconid; + const_new[ -( oconid + 1 ) ] = conperm[ -( iconid + 1 ) ]; + } + } + + for( i = 0, j = npout_new - nout2b; i < nout2b; i++,j++ ) { + outperm_new[ j ] = --oconid; + const_new[ -( oconid + 1 ) ] = qb[ i ]; + } + + } + +/* Now assign permutations for the first component Mapping, if used. */ + if( !aconstants ) { + for( i = 0, j = npin_new - nout2a; i < nout2a; i++,j++ ) { + inperm_new[ j ] = i; + outperm_new[ i ] = j; + } + +/* Otherwise, store constants */ + } else { + + for( i = nout2b; i < npin_new; i++ ){ + iconid = inperm[ i - nout2b + nin2b ]; + if( iconid >= npout ) { + inperm_new[ i ] = npout_new; + + } else if( iconid >= 0 ) { + astError( AST__INTER, "astMapMerge(CmpMap): Swapped PermMap " + "input is not constant (internal AST programming " + "error)." , status); + break; + + } else { + inperm_new[ i ] = --oconid; + const_new[ -( oconid + 1 ) ] = conperm[ -( iconid + 1 ) ]; + } + } + + for( i = 0; i < nout2a; i++ ) { + outperm_new[ i ] = --oconid; + const_new[ -( oconid + 1 ) ] = qa[ i ]; + } + + } + +/* Create the new PermMap */ + new_pm = astPermMap( npin_new, inperm_new, npout_new, + outperm_new, const_new, "", status ); + +/* Create the new CmpMap.*/ + if( aconstants ) { + if( unit ) { + new_cm = astCmpMap( cmpmap2->map2, unit, 0, "", status ); + } else { + new_cm = astCopy( cmpmap2->map2 ); + } + + } else if( bconstants ) { + if( unit ) { + new_cm = astCmpMap( unit, cmpmap2->map1, 0, "", status ); + } else { + new_cm = astCopy( cmpmap2->map1 ); + } + + } else{ + new_cm = astCmpMap( cmpmap2->map2, cmpmap2->map1, 0, "", status ); + } + + } + +/* Free Memory. */ + if( unit ) unit = astAnnul( unit ); + outperm_new = astFree( outperm_new ); + inperm_new = astFree( inperm_new ); + const_new = astFree( const_new ); + if( aconstants || bconstants ) { + qa = astFree( qa ); + qb = astFree( qb ); + } + +/* Re-instate the original Invert attributes in the component Mappings. */ + astSetInvert( cmpmap2->map1, invert2a ); + astSetInvert( cmpmap2->map2, invert2b ); + + } + +/* Release the arrays holding the input and output permutation arrays + and constants copied from the PermMap. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + conperm = astFree( conperm ); + +/* Re-instate the original values of the Invert attributes of both + Mappings. */ + astSetInvert( permmap1, invert1 ); + astSetInvert( cmpmap2, invert2 ); + +/* If the Mappings can be swapped... */ + if( astOK && canswap ) { + +/* Annul the supplied pointer to the two Mappings. */ + ( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] ); + ( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] ); + +/* Store the new PermMap pointer in the slot previously occupied by the + nominated CmpMap pointer. Likewise, store the invert flag. */ + ( *map_list )[ imap2 ] = (AstMapping *) new_pm; + ( *invert_list )[ imap2 ] = astGetInvert( new_pm ); + +/* Store the new PermMap pointer in the slot previously occupied by the + nominated CmpMap pointer. Likewise, store the invert flag. */ + ( *map_list )[ imap1 ] = (AstMapping *) new_cm; + ( *invert_list )[ imap1 ] = astGetInvert( new_cm ); + +/* Return the index of the first modified element. */ + result = imap1; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static int *MapSplit1( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit1 + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* int *MapSplit1( AstMapping *this, int nin, const int *in, AstMapping **map ) + +* Class Membership: +* CmpMap method + +* Description: +* This function performs the work for the astMapSplit method. It +* first invokes the astMapSplit method to see if the forward +* transformation of the supplied Mapping (not necessarily a CmpMap) +* can be split as requested. If this is not possible it invokes MapSplit2 +* which attempts an inverse approach to the problem. For each possible +* sub-sets of the Mapping outputs it call astMapSplit to see if the +* sub-set of outputs are generated from the selected inputs. + +* Parameters: +* this +* Pointer to the Mapping to be split. It is not assumed to be a CmpMap. +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied Mapping, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied Mapping has no subset of outputs which +* depend only on the selected inputs. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied Mapping. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + int *result; /* Axis order to return */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* First see if the forward transformation can be split as requested. */ + result = astMapSplit( this, nin, in, map ); + +/* If forward transformation could not be split, we attempt to split the + inverse transformation by selecting every possible sub-set of Mapping + outputs until one is found which is fed by the requested mapping inputs. */ + if( !result ) result = MapSplit2( this, nin, in, map, status ); + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static int *MapSplit2( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit2 + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* int *MapSplit2( AstMapping *this, int nin, const int *in, AstMapping **map ) + +* Class Membership: +* CmpMap method + +* Description: +* This function attempts to split the supplied Mapping using an +* inverse approach to the problem. For each possible sub-sets of the +* Mapping outputs it call astMapSplit to see if the sub-set of outputs +* are generated from the selected inputs. + +* Parameters: +* this +* Pointer to the Mapping to be split. It is not assumed to be a CmpMap. +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied Mapping, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied Mapping has no subset of outputs which +* depend only on the selected inputs. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied Mapping. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstMapping *map2; /* Subset Mapping */ + AstMapping *this2; /* Inverted copy of the supplied Mapping */ + int *out; /* Selected output indices */ + int *result; /* Axis order to return */ + int *result2; /* Axis order for current output subset */ + int i; /* Loop count */ + int iscmp; /* Is "this" a CmpMap? */ + int j; /* Loop count */ + int mout; /* Number of selected outputs */ + int nin2; /* Number of inputs fed by current outputs */ + int nout; /* The number of outputs from the supplied Mapping */ + int ok; /* Are all required inputs fed by current outputs? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the number of Mapping outputs. */ + nout = astGetNout( this ); + +/* Get an inverted copy of the Mapping. We do this rather than inverting + the supplied Maping in case an error occurs which may leave the + supplied Mapping inverted. */ + this2 = astCopy( this ); + astInvert( this2 ); + +/* Note if the Mapping is a CmpMap. */ + iscmp = astIsACmpMap( this ); + +/* Allocate memory to hold the selected output indices. */ + out = astMalloc( nout*sizeof( int ) ); + +/* Loop round all useful subset sizes. */ + if( out ) { + for( mout = 1; mout < nout && !result; mout++ ) { + +/* Initialise the first subset of outputs to check at the current subset + size. */ + for( i = 0; i < mout; i++ ) out[ i ] = 0; + +/* Loop round all ways of picking a subset of "mout" outputs from the total + available "nout" outputs. */ + while( ! result ) { + +/* Skip this subset if it refers to any axis index more than once. */ + ok = 1; + for( i = 1; i < mout && ok; i++ ) { + for( j = 0; j < i; j++ ) { + if( out[ i ] == out[ j ] ) { + ok = 0; + break; + } + } + } + if( ok ) { + +/* Attempt to split the inverted Mapping using the current subset of + outputs. Take care to avoid an infinite loop if "this" is a CmpMap. */ + if( iscmp ) { + result2 = MapSplit0( this2, mout, out, &map2, 1, status ); + } else { + result2 = astMapSplit( this2, mout, out, &map2 ); + } + +/* If succesful... */ + if( result2 ) { + +/* See if the inputs that feed the current subset of outputs are the same + as the inputs specified by the caller (and in the same order). */ + nin2 = astGetNout( map2 ); + ok = ( nin2 == nin ); + if( ok ) { + for( i = 0; i < nin; i++ ) { + if( in[ i ] != result2[ i ] ) { + ok = 0; + break; + } + } + } + +/* If so, set up the values returned to the caller. */ + if( ok ) { + result = astStore( result, out, mout*sizeof(int) ); + astInvert( map2 ); + *map = astClone( map2 ); + } + +/* Free resources. */ + result2 = astFree( result2 ); + map2 = astAnnul( map2 ); + } + } + +/* Increment the first axis index. */ + i = 0; + out[ i ]++; + +/* If the incremented axis index is now too high, reset it to zero and + increment the next higher axis index. Do this until an incremented axis + index is not too high. */ + while( out[ i ] == nout ) { + out[ i++ ] = 0; + + if( i < mout ) { + out[ i ]++; + } else { + break; + } + } + +/* If all subsets have been checked break out of the loop. */ + if( i == mout ) break; + + } + } + } + +/* Free resources. */ + out = astFree( out ); + this2 = astAnnul( this2 ); + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static int *MapSplit0( AstMapping *this_mapping, int nin, const int *in, + AstMapping **map, int reentry, int *status ){ +/* +* Name: +* MapSplit0 + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* CmpMap. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* int *MapSplit0( AstMapping *this, int nin, const int *in, +* AstMapping **map, int reentry, int *status ) + +* Class Membership: +* CmpMap method + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing CmpMap. This is only possible if the specified inputs +* correspond to some subset of the CmpMap outputs. That is, there +* must exist a subset of the CmpMap outputs for which each output +* depends only on the selected CmpMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied CmpMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the CmpMap to be split (the CmpMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied CmpMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied CmpMap has no subset of outputs which +* depend only on the selected inputs. +* reentry +* Set to zero if this is a top level entry, and non-zero if it is +* a recursive entry. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied CmpMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstCmpMap *this; + AstMapping **map_list; + AstMapping *amap; + AstMapping *bmap; + AstPermMap *pmap; + int *aout; + int *cin; + int *cout; + int *inp; + int *invert_list; + int *outp; + int *p; + int *result; + int doperm; + int i; + int ibot; + int ibotout; + int iin; + int imap; + int iout; + int itop; + int j; + int naout; + int ncin; + int ncout; + int nmap; + int npin; + int npout; + int ok; + int old_inv; + int t; + + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpMap structure. */ + this = (AstCmpMap *) this_mapping; + +/* Get the number of inputs and outputs in the supplied CmpMap. */ + npin = astGetNin( this ); + npout = astGetNout( this ); + +/* Check all input axis indices are valid. */ + ok = 1; + for( i = 0; i < nin; i++ ) { + if( in[ i ] < 0 || in[ i ] >= npin ) { + ok = 0; + break; + } + } + +/* If OK, proceed. */ + if( ok ) { + +/* Initialise dynamic arrays of Mapping pointers and associated Invert + flags. */ + nmap = 0; + map_list = NULL; + invert_list = NULL; + +/* Decompose the CmpMap into a sequence of Mappings to be applied in + series or parallel, as appropriate, and an associated list of + Invert flags. */ + (void) astMapList( this_mapping, this->series, astGetInvert( this ), + &nmap, &map_list, &invert_list ); + +/* First handle lists of Mapping in series. */ + if( this->series ) { + +/* Initialise the array of inputs to be split from the next component + Mapping. */ + ncin = nin; + cin = astStore( NULL, in, sizeof( int )*nin ); + +/* Loop round all the component Mappings that are combined in series to form + the supplied CmpMap. */ + for( imap = 0; imap < nmap && cin; imap++ ) { + +/* Temporarily reset the Invert attribute within the commponent Mapping back + to the value it had when the CmpMap was created. */ + old_inv = astGetInvert( map_list[ imap ] ); + astSetInvert( map_list[ imap ], invert_list[ imap ] ); + +/* Attempt to split the component Mapping using the current list of + inputs. */ + cout = MapSplit1( map_list[ imap ], ncin, cin, &amap, status ); + +/* If the split could be done... */ + if( amap ) { + +/* The outputs that correspond to the picked inputs become the inputs to + be picked from the next component Mapping. */ + (void) astFree( cin ); + cin = cout; + ncin = astGetNout( amap ); + +/* Combine the split Mapping in series with the earlier split Mappings. */ + if( *map ) { + bmap = (AstMapping *) astCmpMap( *map, amap, 1, " ", status ); + amap = astAnnul( amap ); + (void) astAnnul( *map ); + *map = bmap; + } else { + *map = amap; + } + +/* If the split could not be done, free the array of Mapping inputs to + indicate that no more component Mappings need be checked. */ + } else { + cin = astFree( cin ); + cout = astFree( cout ); + } + +/* Re-instate the original value of the Invert attribute within the + commponent Mapping. */ + astSetInvert( map_list[ imap ], old_inv ); + } + +/* Return the final array of output indices. */ + result = cin; + +/* Now handle lists of Mapping in parallel. */ + } else { + +/* Allocate work space. */ + outp = astMalloc( sizeof(int)*(size_t)nin ); + inp = astMalloc( sizeof(int)*(size_t)nin ); + cin = astMalloc( sizeof(int)*(size_t)npin ); + cout = astMalloc( sizeof(int)*(size_t)npout ); + if( astOK ) { + +/* The caller may have selected the Mapping inputs in any order, so we + need to create a PermMap which will permute the inputs from the + requested order to the order used by the CmpMap. First fill the outperm + work array with its own indices. */ + for( i = 0; i < nin; i++ ) outp[ i ] = i; + +/* Sort the outperm work array so that it accesses the array of input indices + in ascending order */ + for( j = nin - 1; j > 0; j-- ) { + p = outp; + for( i = 0; i < j; i++,p++ ) { + if( in[ p[0] ] > in[ p[1] ] ) { + t = p[0]; + p[0] = p[1]; + p[1] = t; + } + } + } + +/* Create the inperm array which is the inverse of the above outperm + array. Note if the permutation is necessary. */ + doperm = 0; + for( i = 0; i < nin; i++ ) { + if( outp[ i ] != i ) doperm = 1; + inp[ outp[ i ] ] = i; + } + +/* Create a PermMap which reorders the inputs into ascending order. */ + pmap = doperm ? astPermMap( nin, inp, nin, outp, NULL, "", status ) : NULL; + +/* Store the sorted input indices in the inp work array. */ + for( i = 0; i < nin; i++ ) { + inp[ i ] = in[ outp[ i ] ]; + } + +/* Initialise the index within the supplied CmpMap of the last (highest) + input in the current component Mapping. */ + itop = -1; + +/* Initialise the index within the supplied CmpMap of the first (lowest) + output for the current component Mapping. */ + ibotout = 0; + +/* Initialise the index within the supplied CmpMap of the current picked input. */ + iin = 0; + +/* Initialise the index of the next returned output index. */ + ncout = 0; + +/* Loop round all the component Mappings that are combined in series to form + the supplied CmpMap. */ + for( imap = 0; imap < nmap && cout; imap++ ) { + +/* Temporarily reset the Invert attribute within the component Mapping back + to the value it had when the CmpMap was created. */ + old_inv = astGetInvert( map_list[ imap ] ); + astSetInvert( map_list[ imap ], invert_list[ imap ] ); + +/* Get the index within the supplied CmpMap of the first (lowest) input in + the current component Mapping. */ + ibot = itop + 1; + +/* Get the index within the supplied CmpMap of the last (highest) input in + the current component Mapping. */ + itop += astGetNin( map_list[ imap ] ); + +/* Get the zero-based indices of the required inputs that feed the current + component Mapping. */ + ncin = 0; + while( iin < nin && inp[ iin ] <= itop ) { + cin[ ncin++ ] = inp[ iin++ ] - ibot; + } + +/* Skip components from which no inputs are being picked. */ + if( ncin > 0 ) { + +/* Attempt to split the component Mapping using the current list of inputs. */ + aout = MapSplit1( map_list[ imap ], ncin, cin, &amap, + status ); + +/* If successful... */ + if( amap ) { + +/* Correct the output indices so that they refer to the numbering scheme + of the total CmpMap, and append to the total list of output indices. */ + naout = astGetNout( amap ); + for( iout = 0; iout < naout; iout++ ) { + cout[ ncout++ ] = aout[ iout ] + ibotout; + } + +/* Combine the split Mapping in parallel with the earlier split Mappings. */ + if( *map ) { + bmap = (AstMapping *) astCmpMap( *map, amap, 0, " ", + status ); + amap = astAnnul( amap ); + (void) astAnnul( *map ); + *map = bmap; + } else { + *map = amap; + } + +/* If the component Mapping could not be split, free the cout array to + indicate that no more component Mappings need be considered. */ + } else { + cout = astFree( cout ); + } + +/* Free remaining resources. */ + aout = astFree( aout ); + } + +/* Update the index within the supplied CmpMap of the first (lowest) output in + the next component Mapping. */ + ibotout += astGetNout( map_list[ imap ] ); + +/* Re-instate the original value of the Invert attribute within the + commponent Mapping. */ + astSetInvert( map_list[ imap ], old_inv ); + } + +/* If the requested inputs could be split from the total CmpMap, add in any + PermMap needed to re-order the inputs. */ + if( cout && ncout ){ + if( doperm ) { + bmap = (AstMapping *) astCmpMap( pmap, *map, 1, "", status ); + (void) astAnnul( *map ); + *map = bmap; + } + +/* Also return the list of output indices. */ + result = cout; + cout = NULL; + } + +/* Free remaining resources. */ + if( pmap ) pmap = astAnnul( pmap ); + } + outp = astFree( outp ); + inp = astFree( inp ); + cin = astFree( cin ); + cout = astFree( cout ); + } + +/* Loop to annul all the Mapping pointers in the list. */ + for ( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] ); + +/* Free the dynamic arrays. */ + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + + } + +/* Mappings that have no outputs cannot be used. */ + if( !result && *map ) *map = astAnnul( *map ); + +/* If the above method failed to split the CmpMap, we attempt to split the + inverse transformation by selecting every possible sub-set of Mapping + outputs until one is found which is fed by the requested mapping inputs. */ + if( !result && !reentry ) result = MapSplit2( this_mapping, nin, in, map, + status ); + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static int *MapSplit( AstMapping *this, int nin, const int *in, + AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* CmpMap. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, +* AstMapping **map, int *status ) + +* Class Membership: +* CmpMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function is the main entry point for the astMapSplit method. +* It is a simple wrapper for MapSplit0 which calls MapSplit0 +* indicating that this is a top-level entry. + +* Parameters: +* this +* Pointer to the CmpMap to be split (the CmpMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied CmpMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied CmpMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied CmpMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + return MapSplit0( this, nin, in, map, 0, status ); +} + +static int PatternCheck( int val, int check, int **list, int *list_len, int *status ){ +/* +* Name: +* Looping + +* Purpose: +* Check for repeating patterns in a set of integer values. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* int PatternCheck( int val, int nmap, int **mlist, int **nlist, int *list_len ) + +* Class Membership: +* CmpMap member function. + +* Description: +* This function appends a supplied integer to a dynamic list, creating +* or expanding the list if necessary.It then optionally, check the +* list for evidence of repeating patterns. If such a pattern is +* found, its wavelength is returned. + +* Parameters: +* val +* The integer value to add to the list. +* check +* Should a check for reating patterns be performed? +* list +* Address of a location at which is stored a pointer to an array +* holding the values supplied on previous invocations of this +* function. If a NULL pointer is supplied a new array is allocated. +* On exit, the supplied value is appended to the end of the array. The +* array is extended as necessary. The returned pointer should be +* freed using astFree when no longer needed. +* list_len +* Address of a location at which is stored the number of elements +* in the "list" array. + +* Returned Value: +* A non-zero "wavelength" value is returned if there is a repeating +* pattern is found in the "list" array. Otherwise, zero is returned. +* The "wavelength" is the number of integer values which constitute a +* single instance of the pattern. + +* Notes: +* - A value of 1 is returned if this function is invoked with the AST +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int *wave[ 30 ]; /* Pointers to start of waves */ + int iat; /* Index of elements added by this invocation */ + int jat; /* Index of element condiered next */ + int jlo; /* Earliest "mlist" entry to consider */ + int k; /* Index of element within pattern */ + int mxwave; /* Max pattern length to consider */ + int iwave; /* Index of current wave */ + int nwave; /* Number of waves required to mark a pattern */ + int result; /* Returned flag */ + int wavelen; /* Current pattern length */ + +/* Check the global status. */ + if ( !astOK ) return 1; + +/* Initialise */ + result = 0; + +/* If no array has been supplied, create a new array. */ + if( !(*list) ) { + *list = astMalloc( 100*sizeof( int ) ); + *list_len = 0; + } + +/* Store the new value in the array, extending it if necessary. */ + iat = (*list_len)++; + *list = astGrow( *list, *list_len, sizeof( int ) ); + if( astOK ) { + (*list)[ iat ] = val; + +/* If required, determine the maximum "wavelength" for looping patterns to be + checked, and store the earliest list entry to consider. We take 3 complete + patterns as evidence of looping, but we only do the check when the + list length is at least 30. */ + if( check && *list_len > 29 ){ + mxwave = iat/3; + if( mxwave > 50 ) mxwave = 50; + jlo = iat - 3*mxwave; + +/* Search backwards from the end of "list" looking for the most recent + occurence of the supplied "val" value. Limit the search to + wavelengths of no more than the above limit. */ + jat = iat - 1; + while( jat >= jlo ) { + if( (*list)[ jat ] == val ) { + +/* When an earlier occurrence of "val" is found, see if the values + which precede it are the same as the values which precede the new + element if "list" added by this invocation. We use 3 complete + patterns as evidence of looping, unless the wavelength is 1 in which + case we use 30 patterns (this is because wavelengths of 1 can occur + in short sequences legitamately). */ + wavelen = iat - jat; + + if( wavelen == 1 ) { + nwave = 30; + if( nwave > iat ) nwave = iat; + } else { + nwave = 3; + } + + if( nwave*wavelen <= *list_len ) { + result = wavelen; + wave[ 0 ] = *list + *list_len - wavelen; + for( iwave = 1; iwave < nwave; iwave++ ) { + wave[ iwave ] = wave[ iwave - 1 ] - wavelen; + } + + for( k = 0; k < wavelen; k++ ) { + for( iwave = 1; iwave < nwave; iwave++ ) { + if( *wave[ iwave ] != *wave[ 0 ] ) { + result = 0; + break; + } + wave[ iwave ]++; + } + wave[ 0 ]++; + } + } + +/* Break if we have found a repeating pattern. */ + if( result ) break; + + } + jat--; + } + } + } + + if( !astOK ) result= 1; + +/* Return the result.*/ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* CmpMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + +/* Local Variables: */ + AstMapping *c1; + AstMapping *c2; + AstCmpMap *map; + double result; + int old_inv1; + int old_inv2; + int nin1; + int nin2; + double *at2; + double r1; + double r2; + int nout1; + int i; + +/* Check inherited status */ + if( !astOK ) return AST__BAD; + +/* Get a pointer to the CmpMap structure. */ + map = (AstCmpMap *) this; + +/* Note the current Invert flags of the two component Mappings. */ + old_inv1 = astGetInvert( map->map1 ); + old_inv2 = astGetInvert( map->map2 ); + +/* Temporarily reset them to the values they had when the CmpMap was + created. */ + astSetInvert( map->map1, map->invert1 ); + astSetInvert( map->map2, map->invert2 ); + +/* If the CmpMap itself has been inverted, invert the component Mappings. + Also note the order in which the Mappings should be applied if in series. */ + if( !astGetInvert( this ) ) { + c1 = map->map1; + c2 = map->map2; + } else { + c1 = map->map2; + c2 = map->map1; + astInvert( c1 ); + astInvert( c2 ); + } + +/* First deal with Mappings in series. */ + if( map->series ) { + +/* Get the number of inputs to the two component Mappings. */ + nin1 = astGetNin( c1 ); + nin2 = astGetNin( c2 ); + +/* Allocate workspace to hold the result of transforming the supplied "at" + position using the first component. */ + at2 = astMalloc( sizeof( double )*(size_t) nin2 ); + +/* Transform the supplied "at" position using the first component. */ + astTranN( c1, 1, nin1, 1, at, 1, nin2, 1, at2 ); + +/* The required rate of change is the sum of the products of the rate of + changes of the two component mappings, summed over all the output axes + of the first componment. */ + result = 0.0; + for( i = 0; i < nin2; i++ ) { + +/* Find the rate of change of output "i" of the first component with + respect to input "ax2" at the supplied "at" position. */ + r1 = astRate( c1, at, i, ax2 ); + +/* Find the rate of change of output "ax1" of the second component with + respect to input "i" at the transformed "at2" position. */ + r2 = astRate( c2, at2, ax1, i ); + +/* If both are good, increment the ryunning total by the product of the + two rates. Otherwise, break. */ + if( r1 != AST__BAD && r2 != AST__BAD ) { + result += r1*r2; + } else { + result = AST__BAD; + break; + } + } + +/* Free the workspace. */ + at2 = astFree( at2 ); + +/* Now deal with Mappings in parallel. */ + } else { + +/* Get the number of inputs and outputs for the lower component Mappings. */ + nin1 = astGetNin( map->map1 ); + nout1 = astGetNout( map->map1 ); + +/* If both input and output relate to the lower component Mappings, use its + astRate method. */ + if( ax1 < nout1 && ax2 < nin1 ) { + result = astRate( map->map1, at, ax1, ax2 ); + +/* If both input and output relate to the upper component Mappings, use its + astRate method. */ + } else if( ax1 >= nout1 && ax2 >= nin1 ) { + result = astRate( map->map2, at + nin1, ax1 - nout1, ax2 - nin1 ); + +/* If input and output relate to different component Mappings, return + zero. */ + } else { + result = 0.0; + } + } + +/* Reinstate the original Invert flags of the component Mappings .*/ + astSetInvert( map->map1, old_inv1 ); + astSetInvert( map->map2, old_inv2 ); + +/* Return the result. */ + return result; +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* CmpMap method (over-rides the astRemoveRegions method inherited +* from the Mapping class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a CmpMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel CmpMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the CmpMap class invokes the +* astRemoveRegions method on the two component Mappings, and joins +* the results together into a new CmpMap. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstCmpMap *new; /* Pointer to new CmpMap */ + AstCmpMap *this; /* Pointer to CmpMap structure */ + AstMapping *newmap1; /* New first component Mapping */ + AstMapping *newmap2; /* New second component Mapping */ + AstMapping *result; /* Result pointer to return */ + int nax; /* Number of Frame axes */ + int unit1; /* Is new first Mapping a UnitMap? */ + int unit2; /* Is new second Mapping a UnitMap? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpMap. */ + this = (AstCmpMap *) this_mapping; + +/* Invoke the astRemoveRegions method on the two component Mappings. */ + newmap1 = astRemoveRegions( this->map1 ); + newmap2 = astRemoveRegions( this->map2 ); + +/* If neither component was modified, just return a clone of the supplied + pointer. */ + if( this->map1 == newmap1 && this->map2 == newmap2 ) { + result = astClone( this ); + +/* Otherwise, we need to create a new Mapping to return. */ + } else { + +/* The implementation of the astRemoveRegions method provided by the + Region class returns a Frame rather than a UnitMap. But we need + Mappings here, not Frames. So if either of these new Mappings is + a Frame, replace it with an equivalent UnitMap. Also, get flags + indicating if either Mapping is a UnitMap.*/ + if( astIsAFrame( newmap1 ) ) { + nax = astGetNin( newmap1 ); + (void) astAnnul( newmap1 ); + newmap1 = (AstMapping *) astUnitMap( nax, " ", status ); + unit1 = 1; + } else { + unit1 = astIsAUnitMap( newmap1 ); + } + + if( astIsAFrame( newmap2 ) ) { + nax = astGetNin( newmap2 ); + (void) astAnnul( newmap2 ); + newmap2 = (AstMapping *) astUnitMap( nax, " ", status ); + unit2 = 1; + } else { + unit2 = astIsAUnitMap( newmap2 ); + } + +/* First handle series CmpMaps. */ + if( this->series ) { + +/* Otherwise, if the second new Mapping is a UnitMap, return a copy of the + first new Mapping (with the original Invert attribute) since the second + one will have no effect. */ + if( unit1 ) { + result = astCopy( newmap2 ); + astSetInvert( result, this->invert2 ); + if( astGetInvert( this ) ) astInvert( result ); + +/* Otherwise, if the second new Mapping is a UnitMap, return a copy of the + first new Mapping (with the original Invert attribute) since the second + one will have no effect. */ + } else if( unit2 ) { + result = astCopy( newmap1 ); + astSetInvert( result, this->invert1 ); + if( astGetInvert( this ) ) astInvert( result ); + +/* If neither of the new Mappings is a UnitMap, return a new CmpMap + containing the two new Mappings. We take a deep copy of the supplied + CmpMap and then modify the Mappings os that we retain any extra + information (such as invert flags) in the supplied CmpMap. */ + } else { + new = astCopy( this ); + (void) astAnnul( new->map1 ); + (void) astAnnul( new->map2 ); + new->map1 = astClone( newmap1 ); + new->map2 = astClone( newmap2 ); + result = (AstMapping *) new; + } + +/* Now handle parallel CmpMaps. */ + } else { + +/* If both new Mappings are UnitMaps, return an equivalent UnitMap. */ + if( unit1 && unit2 ) { + result = (AstMapping *) astUnitMap( astGetNin( newmap1 ) + + astGetNin( newmap2 ), " ", + status ); + +/* Otherwise, return a new CmpMap containing the two new Mappings. */ + } else { + new = astCopy( this ); + (void) astAnnul( new->map1 ); + (void) astAnnul( new->map2 ); + new->map1 = astClone( newmap1 ); + new->map2 = astClone( newmap2 ); + result = (AstMapping *) new; + } + } + } + +/* Free resources. */ + newmap1 = astAnnul( newmap1 ); + newmap2 = astAnnul( newmap2 ); + +/* Annul the returned Mapping if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* CmpMap method (over-rides the astSimplify method inherited from +* the Mapping class). + +* Description: +* This function simplifies a CmpMap to eliminate redundant +* computational steps, or to merge separate steps which can be +* performed more efficiently in a single operation. + +* Parameters: +* this +* Pointer to the original Mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new pointer to the (possibly simplified) Mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpMap *this; /* Pointer to CmpMap structure */ + AstMapping **map_list; /* Mapping array pointer */ + AstMapping *map; /* Pointer to cloned Mapping pointer */ + AstMapping *result; /* Result pointer to return */ + AstMapping *tmp; /* Temporary Mapping pointer */ + int *invert_list; /* Invert array pointer */ + int *mlist; /* Point to list of modified Mapping indices */ + int *nlist; /* Point to list of Mapping counts */ + int i; /* Loop counter for Mappings */ + int improved; /* Simplification achieved? */ + int invert; /* Invert attribute value */ + int invert_n; /* Invert value for final Mapping */ + int mlist_len; /* No. of entries in mlist */ + int nlist_len; /* No. of entries in nlist */ + int modified; /* Index of first modified Mapping */ + int nmap; /* Mapping count */ + int nominated; /* Index of nominated Mapping */ + int set; /* Invert attribute set? */ + int set_n; /* Invert set for final Mapping? */ + int simpler; /* Simplification possible? */ + int t; /* Temporary storage */ + int wlen1; /* Pattern wavelength for "modified" values */ + int wlen2; /* Pattern wavelength for "nmap" values */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_mapping); + +/* It is possible for the astSimplify method to be called recursively from + within astSimplify. It is also possible that the Mapping being + simplified by the current invocation is the same as the Mapping being + simplified by some recursive invocation higher up the call stack. If + this happens we will get into an infinite loop, since we already know + that simplifying the supplied Mapping will involve (eventually) a + recursive call to astSimplify with the same Mapping. To avoid this + looping, we note the Mappings supplied at each depth and first compare + the supplied Mapping with the Mappings which are currently being + simplified higher up the call stack. If the supplied Mapping is + already being simplified at a higher level, then we return immediately + without doing any simplification. Otherwise, we record the supplied + Mapping pointer in a static list so that it is available to subsequent + recursive invocations of this function. First compare the supplied + Mapping with the Mappingsbeing simpliied higher up. Return without + action if a match is found. */ + for( i = 0; i < simplify_depth; i++ ) { + if( astEqual( this_mapping, simplify_stackmaps[ i ] ) ) { + return astClone( this_mapping ); + } + } + +/* We have further work to do, so increment the recursion depth, extend + the simplify_stackmaps array, and store the new Mapping in it for future use. */ + simplify_depth++; + simplify_stackmaps = astGrow( simplify_stackmaps, simplify_depth, sizeof( AstMapping * ) ); + if( astOK ) { + simplify_stackmaps[ simplify_depth - 1 ] = astClone( this_mapping ); + } + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpMap *) this_mapping; + +/* Initialise dynamic arrays of Mapping pointers and associated Invert + flags. */ + nmap = 0; + map_list = NULL; + invert_list = NULL; + +/* Decompose the CmpMap into a sequence of Mappings to be applied in + series or parallel, as appropriate, and an associated list of + Invert flags. If any inverted CmpMaps are found in the Mapping, then + we can at least simplify the returned Mapping by swapping and + inverting the components. Set "simpler" to indicate this. */ + simpler = astMapList( this_mapping, this->series, astGetInvert( this ), &nmap, + &map_list, &invert_list ); + +/* Each Mapping has a flag that indicates if the mapping is frozen (i.e. cannot + be nominated for simplification). Mappings become frozen if nominating them + would create an infinite loop in which neighbouring mappings argue as to + their form. Freezing a mapping prevents the frozen mapping contributing any + further to the argument, so the other Mapping "wins" the argument. + Ensure no Mappings are frozen to begin with. */ + for( i = 0; i < nmap; i++ ) { + map_list[ i ]->flags &= ~AST__FROZEN_FLAG; + } + +/* Initialise pointers to memory used to hold lists of the modified + Mapping index and the number of mappings after each call of + astMapMerge. */ + mlist = NULL; + nlist = NULL; + +/* Loop to simplify the sequence until a complete pass through it has + been made without producing any improvement. */ + improved = 1; + while ( astOK && improved ) { + improved = 0; + +/* Loop to nominate each Mapping in the sequence in turn. */ + nominated = 0; + while ( astOK && ( nominated < nmap ) ) { + +/* If the current nominated mapping has been frozen, then we do not allow + it to suggest changes to the mapping sequence. Instead, just increment + the index of the next mapping to be checked and continue on to the next + pass round the while loop. */ + if( map_list[ nominated ]->flags & AST__FROZEN_FLAG ) { + nominated++; + continue; + } + +/* Clone a pointer to the nominated Mapping and attempt to merge it + with its neighbours. Annul the cloned pointer afterwards. */ + map = astClone( map_list[ nominated ] ); + modified = astMapMerge( map, nominated, this->series, + &nmap, &map_list, &invert_list ); + map = astAnnul( map ); + +/* Move on to nominate the next Mapping in the sequence. */ + nominated++; + +/* Note if any simplification occurred above. */ + if( modified >= 0 ) { + +/* Append the index of the first modified Mapping in the list and and check + that there is no repreating pattern in the list. If there is, we are + probably in a loop where one mapping class is making a change, and another + is undoing the change. The Looping function returns the "wavelength" + of any pattern found. If a pattern was discovered, we ignore it unless + there is also a pattern in the "nmap" values - the wavelengths of the + two patterns must be related by a integer factor. */ + wlen1 = PatternCheck( modified, 1, &mlist, &mlist_len, status ); + wlen2 = PatternCheck( nmap, wlen1, &nlist, &nlist_len, status ); + if( wlen1 && wlen2 ) { + +/* Ensure wlen2 is larger than or equal to wlen1. */ + if( wlen1 > wlen2 ) { + t = wlen1; + wlen1 = wlen2; + wlen2 = t; + } + +/* See if wlen2 is an integer multiple of wlen1. If not, ignore the + patterns. */ + if( ( wlen2 % wlen1 ) != 0 ) wlen1 = 0; + } + +/* If a repeating pattern is occurring, set the frozen flag in order to + prevent the modified mapping from being modified any more. */ + if( wlen1 > 0 ) { + map_list[ modified ]->flags |= AST__FROZEN_FLAG; + +/* Otherwise, indicate we have improved the mapping and go round to test + the next nominated mapping. */ + } else { + improved = 1; + simpler = 1; + +/* If the simplification resulted in modification of an earlier + Mapping than would normally be considered next, then go back to + consider the modified one first. */ + if ( modified < nominated ) nominated = modified; + } + } + } + } + +/* Free resources */ + mlist = astFree( mlist ); + nlist = astFree( nlist ); + +/* Construct the output Mapping. */ +/* ============================= */ +/* If no simplification occurred above, then simply clone a pointer to + the original Mapping. */ + if ( astOK ) { + if ( !simpler ) { + result = astClone( this ); + +/* Otherwise, we must construct the result from the contents of the + Mapping list. */ + } else { + +/* If the simplified Mapping list has only a single element, then the + output Mapping will not be a CmpMap. In this case, we cannot + necessarily set the Invert flag of the Mapping to the value we want + (because we must not modify the Mapping itself. */ + if ( nmap == 1 ) { + +/* We must make a copy. Cloning is no good (even if the Mapping already + has the Invert attribute value we want), since we want the returned + Mapping to be independent of the original component Mappings, so that + if user code inverts a component Mapping (via some other pre-existing + pointer), the returned simplified Mapping is not affected. */ + result = astCopy( map_list[ 0 ] ); + +/* Either clear the copy's Invert attribute, or set it to 1, as + required. */ + if ( invert_list[ 0 ] ) { + astSetInvert( result, 1 ); + } else { + astClearInvert( result ); + } + +/* If the simplified Mapping sequence has more than one element, the + output Mapping will be a CmpMap. In this case, we can set each + individual Mapping element to have the Invert attribute value we + want, so long as we return these attribute values to their original + state again afterwards (once a Mapping is encapsulated inside a + CmpMap, further external changes to its Invert attribute do not + affect the behaviour of the CmpMap). */ + } else { + +/* Determine if the Invert attribute for the last Mapping is set, and + obtain its value. */ + set_n = astTestInvert( map_list[ nmap - 1 ] ); + invert_n = astGetInvert( map_list[ nmap - 1 ] ); + +/* Set this attribute to the value we want. */ + astSetInvert( map_list[ nmap - 1 ], invert_list[ nmap - 1 ] ); + +/* Loop through the Mapping sequence in reverse to merge it into an + equivalent CmpMap. */ + for ( i = nmap - 1; i >= 0; i-- ) { + +/* Simply clone the pointer to the last Mapping in the sequence (which + will be encountered first). */ + if ( !result ) { + result = astClone( map_list[ i ] ); + +/* For subsequent Mappings, test if the Invert attribute is set and + save its value. */ + } else { + set = astTestInvert( map_list[ i ] ); + invert = astGetInvert( map_list[ i ] ); + +/* Set this attribute to the value required. */ + astSetInvert( map_list[ i ], invert_list[ i ] ); + +/* Combine the Mapping with the CmpMap formed so far and replace the + result pointer with the new pointer this produces, annulling the + previous pointer. */ + tmp = (AstMapping *) astCmpMap( map_list[ i ], result, + this->series, "", status ); + (void) astAnnul( result ); + result = tmp; + +/* Restore the Invert attribute of the Mapping to its original + state. */ + if ( !set ) { + astClearInvert( map_list[ i ] ); + } else { + astSetInvert( map_list[ i ], invert ); + } + } + } + +/* When all the Mappings have been merged into the CmpMap, restore the + state of the Invert attribute for the final Mapping in the + sequence. */ + if ( !set_n ) { + astClearInvert( map_list[ nmap - 1 ] ); + } else { + astSetInvert( map_list[ nmap - 1 ], invert_n ); + } + } + } + } + +/* Clean up. */ +/* ========= */ +/* Loop to annul all the Mapping pointers in the simplified list. */ + for ( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] ); + +/* Free the dynamic arrays. */ + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + +/* Decrement the recursion depth and free the pointer to the supplied + Mapping currently stored at the end of the simplify_stackmaps array. */ + simplify_depth--; + if( astOK ) { + simplify_stackmaps[ simplify_depth ] = astAnnul( simplify_stackmaps[ simplify_depth ] ); + } + +/* If we are now at depth zero, free the simplify_stackmaps array. */ + if( simplify_depth == 0 ) simplify_stackmaps = astFree( simplify_stackmaps ); + +/* If an error occurred, annul the returned Mapping. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a CmpMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a CmpMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Mapping. +* This implies applying each of the CmpMap's component Mappings in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the CmpMap. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the CmpMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstCmpMap *map; /* Pointer to CmpMap to be applied */ + AstPointSet *result; /* Pointer to output PointSet */ + AstPointSet *temp1; /* Pointer to temporary PointSet */ + AstPointSet *temp2; /* Pointer to temporary PointSet */ + AstPointSet *temp; /* Pointer to temporary PointSet */ + int forward1; /* Use forward direction for Mapping 1? */ + int forward2; /* Use forward direction for Mapping 2? */ + int ipoint1; /* Index of first point in batch */ + int ipoint2; /* Index of last point in batch */ + int nin1; /* No. input coordinates for Mapping 1 */ + int nin2; /* No. input coordinates for Mapping 2 */ + int nin; /* No. input coordinates supplied */ + int nout1; /* No. output coordinates for Mapping 1 */ + int nout2; /* No. output coordinates for Mapping 2 */ + int nout; /* No. output coordinates supplied */ + int np; /* Number of points in batch */ + int npoint; /* Number of points to be transformed */ + +/* Local Constants: */ + const int nbatch = 2048; /* Maximum points in a batch */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the CmpMap. */ + map = (AstCmpMap *) this; + +/* Apply the parent Mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We now extend the parent astTransform method by applying the component + Mappings of the CmpMap to generate the output coordinate values. */ + +/* Determine whether to apply the forward or inverse Mapping, according to the + direction specified and whether the Mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Check if either component Mapping's inversion flag has changed since it was + used to construct the CmpMap. Set a "forward" flag for each Mapping to + change the direction we will use, to compensate if necessary. (Such changes + may have occurred if other pointers to the component Mappings are in + circulation). */ + forward1 = forward; + forward2 = forward; + if ( map->invert1 != astGetInvert( map->map1 ) ) forward1 = !forward1; + if ( map->invert2 != astGetInvert( map->map2 ) ) forward2 = !forward2; + +/* Determine the number of points being transformed. */ + npoint = astGetNpoint( in ); + +/* Mappings in series. */ +/* ------------------- */ +/* If required, use the two component Mappings in series. To do this, we must + apply one Mapping followed by the other, which means storing an intermediate + result. Since this function may be invoked recursively and have to store an + intermediate result on each occasion, the memory required may become + excessive when transforming large numbers of points. To overcome this, we + split the points up into smaller batches. */ + if ( astOK ) { + if ( map->series ) { + +/* Obtain the numbers of input and output coordinates. */ + nin = astGetNcoord( in ); + nout = astGetNcoord( result ); + +/* Loop to process all the points in batches, of maximum size nbatch points. */ + for ( ipoint1 = 0; ipoint1 < npoint; ipoint1 += nbatch ) { + +/* Calculate the index of the final point in the batch and deduce the number of + points (np) to be processed in this batch. */ + ipoint2 = ipoint1 + nbatch - 1; + if ( ipoint2 > npoint - 1 ) ipoint2 = npoint - 1; + np = ipoint2 - ipoint1 + 1; + +/* Create temporary PointSets to describe the input and output points for this + batch. */ + temp1 = astPointSet( np, nin, "", status ); + temp2 = astPointSet( np, nout, "", status ); + +/* Associate the required subsets of the input and output coordinates with the + two PointSets. */ + astSetSubPoints( in, ipoint1, 0, temp1 ); + astSetSubPoints( result, ipoint1, 0, temp2 ); + +/* Apply the two Mappings in sequence and in the required order and direction. + Store the intermediate result in a temporary PointSet (temp) which is + created by the first Mapping applied. */ + if ( forward ) { + temp = astTransform( map->map1, temp1, forward1, NULL ); + (void) astTransform( map->map2, temp, forward2, temp2 ); + } else { + temp = astTransform( map->map2, temp1, forward2, NULL ); + (void) astTransform( map->map1, temp, forward1, temp2 ); + } + +/* Delete the temporary PointSets after processing each batch of points. */ + temp = astDelete( temp ); + temp1 = astDelete( temp1 ); + temp2 = astDelete( temp2 ); + +/* Quit processing batches if an error occurs. */ + if ( !astOK ) break; + } + +/* Mappings in parallel. */ +/* --------------------- */ +/* If required, use the two component Mappings in parallel. Since we do not + need to allocate any memory to hold intermediate coordinate values here, + there is no need to process the points in batches. */ + } else { + +/* Get the effective number of input and output coordinates per point for each + Mapping (taking account of the direction in which each will be used to + transform points). */ + nin1 = forward1 ? astGetNin( map->map1 ) : astGetNout( map->map1 ); + nout1 = forward1 ? astGetNout( map->map1 ) : astGetNin( map->map1 ); + nin2 = forward2 ? astGetNin( map->map2 ) : astGetNout( map->map2 ); + nout2 = forward2 ? astGetNout( map->map2 ) : astGetNin( map->map2 ); + +/* Create temporary PointSets to describe the input and output coordinates for + the first Mapping. */ + temp1 = astPointSet( npoint, nin1, "", status ); + temp2 = astPointSet( npoint, nout1, "", status ); + +/* Associate the required subsets of the input and output coordinates with + these PointSets. */ + astSetSubPoints( in, 0, 0, temp1 ); + astSetSubPoints( result, 0, 0, temp2 ); + +/* Use the astTransform method to apply the coordinate transformation described + by the first Mapping. */ + (void) astTransform( map->map1, temp1, forward1, temp2 ); + +/* Delete the temporary PointSets. */ + temp1 = astDelete( temp1 ); + temp2 = astDelete( temp2 ); + +/* Create a new pair of temporary PointSets to describe the input and output + coordinates for the second Mapping, and associate the required subsets of + the input and output coordinates with these PointSets. */ + temp1 = astPointSet( npoint, nin2, "", status ); + temp2 = astPointSet( npoint, nout2, "", status ); + astSetSubPoints( in, 0, nin1, temp1 ); + astSetSubPoints( result, 0, nout1, temp2 ); + +/* Apply the coordinate transformation described by the second Mapping. */ + (void) astTransform( map->map2, temp1, forward2, temp2 ); + +/* Delete the two temporary PointSets. */ + temp1 = astDelete( temp1 ); + temp2 = astDelete( temp2 ); + } + } + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for CmpMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for CmpMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Mappings within the CmpMap. +*/ + +/* Local Variables: */ + AstCmpMap *in; /* Pointer to input CmpMap */ + AstCmpMap *out; /* Pointer to output CmpMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output CmpMaps. */ + in = (AstCmpMap *) objin; + out = (AstCmpMap *) objout; + +/* For safety, start by clearing any references to the input component + Mappings from the output CmpMap. */ + out->map1 = NULL; + out->map2 = NULL; + +/* Make copies of these Mappings and store pointers to them in the output + CmpMap structure. */ + out->map1 = astCopy( in->map1 ); + out->map2 = astCopy( in->map2 ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for CmpMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for CmpMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstCmpMap *this; /* Pointer to CmpMap */ + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpMap *) obj; + +/* Annul the pointers to the component Mappings. */ + this->map1 = astAnnul( this->map1 ); + this->map2 = astAnnul( this->map2 ); + +/* Clear the remaining CmpMap variables. */ + this->invert1 = 0; + this->invert2 = 0; + this->series = 0; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for CmpMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the CmpMap class to an output Channel. + +* Parameters: +* this +* Pointer to the CmpMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstCmpMap *this; /* Pointer to the CmpMap structure */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpMap *) this_object; + +/* Write out values representing the instance variables for the CmpMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Series. */ +/* ------- */ + ival = this->series; + set = ( ival == 0 ); + astWriteInt( channel, "Series", set, 0, ival, + ival ? "Component Mappings applied in series" : + "Component Mappings applied in parallel" ); + +/* First Invert flag. */ +/* ------------------ */ + ival = this->invert1; + set = ( ival != 0 ); + astWriteInt( channel, "InvA", set, 0, ival, + ival ? "First Mapping used in inverse direction" : + "First Mapping used in forward direction" ); + +/* Second Invert flag. */ +/* ------------------- */ + ival = this->invert2; + set = ( ival != 0 ); + astWriteInt( channel, "InvB", set, 0, ival, + ival ? "Second Mapping used in inverse direction" : + "Second Mapping used in forward direction" ); + +/* First Mapping. */ +/* -------------- */ + astWriteObject( channel, "MapA", 1, 1, this->map1, + "First component Mapping" ); + +/* Second Mapping. */ +/* --------------- */ + astWriteObject( channel, "MapB", 1, 1, this->map2, + "Second component Mapping" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsACmpMap and astCheckCmpMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(CmpMap,Mapping) +astMAKE_CHECK(CmpMap) + +AstCmpMap *astCmpMap_( void *map1_void, void *map2_void, int series, + const char *options, int *status, ...) { +/* +*+ +* Name: +* astCmpMap + +* Purpose: +* Create a CmpMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpmap.h" +* AstCmpMap *astCmpMap( AstMapping *map1, AstMapping *map2, int series, +* const char *options, ... ) + +* Class Membership: +* CmpMap constructor. + +* Description: +* This function creates a new CmpMap and optionally initialises its +* attributes. + +* Parameters: +* map1 +* Pointer to the first Mapping. +* map2 +* Pointer to the second Mapping. +* series +* If a non-zero value is given, the two Mappings will be connected +* together in series. A zero value requests that they be connected in +* parallel. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new CmpMap. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new CmpMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic CmpMap constructor which is +* available via the protected interface to the CmpMap class. A +* public interface is provided by the astCmpMapId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "map1" and "map2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpMap *new; /* Pointer to new CmpMap */ + AstMapping *map1; /* Pointer to first Mapping structure */ + AstMapping *map2; /* Pointer to second Mapping structure */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Mapping structures provided. */ + map1 = astCheckMapping( map1_void ); + map2 = astCheckMapping( map2_void ); + if ( astOK ) { + +/* Initialise the CmpMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpMap( NULL, sizeof( AstCmpMap ), !class_init, &class_vtab, + "CmpMap", map1, map2, series ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new CmpMap's + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new CmpMap. */ + return new; +} + +AstCmpMap *astCmpMapId_( void *map1_void, void *map2_void, int series, + const char *options, ... ) { +/* +*++ +* Name: +c astCmpMap +f AST_CMPMAP + +* Purpose: +* Create a CmpMap. + +* Type: +* Public function. + +* Synopsis: +c #include "cmpmap.h" +c AstCmpMap *astCmpMap( AstMapping *map1, AstMapping *map2, int series, +c const char *options, ... ) +f RESULT = AST_CMPMAP( MAP1, MAP2, SERIES, OPTIONS, STATUS ) + +* Class Membership: +* CmpMap constructor. + +* Description: +* This function creates a new CmpMap and optionally initialises +* its attributes. +* +* A CmpMap is a compound Mapping which allows two component +* Mappings (of any class) to be connected together to form a more +* complex Mapping. This connection may either be "in series" +* (where the first Mapping is used to transform the coordinates of +* each point and the second mapping is then applied to the +* result), or "in parallel" (where one Mapping transforms the +* earlier coordinates for each point and the second Mapping +* simultaneously transforms the later coordinates). +* +* Since a CmpMap is itself a Mapping, it can be used as a +* component in forming further CmpMaps. Mappings of arbitrary +* complexity may be built from simple individual Mappings in this +* way. + +* Parameters: +c map1 +f MAP1 = INTEGER (Given) +* Pointer to the first component Mapping. +c map2 +f MAP2 = INTEGER (Given) +* Pointer to the second component Mapping. +c series +f SERIES = LOGICAL (Given) +c If a non-zero value is given for this parameter, the two +c component Mappings will be connected in series. A zero +c value requests that they are connected in parallel. +f If a .TRUE. value is given for this argument, the two +f component Mappings will be connected in series. A +f .FALSE. value requests that they are connected in parallel. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new CmpMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new CmpMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astCmpMap() +f AST_CMPMAP = INTEGER +* A pointer to the new CmpMap. + +* Notes: +* - If the component Mappings are connected in series, then using +* the resulting CmpMap to transform coordinates will cause the +* first Mapping to be applied, followed by the second Mapping. If +* the inverse CmpMap transformation is requested, the two +* component Mappings will be applied in both the reverse order and +* the reverse direction. +* - When connecting two component Mappings in series, the number +* of output coordinates generated by the first Mapping (its Nout +* attribute) must equal the number of input coordinates accepted +* by the second Mapping (its Nin attribute). +* - If the component Mappings of a CmpMap are connected in +* parallel, then the first Mapping will be used to transform the +* earlier input coordinates for each point (and to produce the +* earlier output coordinates) and the second Mapping will be used +* simultaneously to transform the remaining input coordinates (to +* produce the remaining output coordinates for each point). If the +* inverse transformation is requested, each Mapping will still be +* applied to the same coordinates, but in the reverse direction. +* - When connecting two component Mappings in parallel, there is +* no restriction on the number of input and output coordinates for +* each Mapping. +c - Note that the component Mappings supplied are not copied by +c astCmpMap (the new CmpMap simply retains a reference to +c them). They may continue to be used for other purposes, but +c should not be deleted. If a CmpMap containing a copy of its +c component Mappings is required, then a copy of the CmpMap should +c be made using astCopy. +f - Note that the component Mappings supplied are not copied by +f AST_CMPMAP (the new CmpMap simply retains a reference to +f them). They may continue to be used for other purposes, but +f should not be deleted. If a CmpMap containing a copy of its +f component Mappings is required, then a copy of the CmpMap should +f be made using AST_COPY. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astCmpMap constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astCmpMap_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "map1" and "map2" parameters +* are of type (void *) and are converted from an ID value to a +* pointer and validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astCmpMap_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpMap *new; /* Pointer to new CmpMap */ + AstMapping *map1; /* Pointer to first Mapping structure */ + AstMapping *map2; /* Pointer to second Mapping structure */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain the Mapping pointers from the ID's supplied and validate the + pointers to ensure they identify valid Mappings. */ + map1 = astVerifyMapping( astMakePointer( map1_void ) ); + map2 = astVerifyMapping( astMakePointer( map2_void ) ); + if ( astOK ) { + +/* Initialise the CmpMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpMap( NULL, sizeof( AstCmpMap ), !class_init, &class_vtab, + "CmpMap", map1, map2, series ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new CmpMap's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new CmpMap. */ + return astMakeId( new ); +} + +AstCmpMap *astInitCmpMap_( void *mem, size_t size, int init, + AstCmpMapVtab *vtab, const char *name, + AstMapping *map1, AstMapping *map2, int series, int *status ) { +/* +*+ +* Name: +* astInitCmpMap + +* Purpose: +* Initialise a CmpMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpmap.h" +* AstCmpMap *astInitCmpMap( void *mem, size_t size, int init, +* AstCmpMapVtab *vtab, const char *name, +* AstMapping *map1, AstMapping *map2, +* int series ) + +* Class Membership: +* CmpMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new CmpMap object. It allocates memory (if necessary) to +* accommodate the CmpMap plus any additional data associated with the +* derived class. It then initialises a CmpMap structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a CmpMap at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the CmpMap is to be initialised. +* This must be of sufficient size to accommodate the CmpMap data +* (sizeof(CmpMap)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the CmpMap (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* CmpMap structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the CmpMap's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new CmpMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* map1 +* Pointer to the first Mapping. +* map2 +* Pointer to the second Mapping. +* series +* If a non-zero value is given, the two Mappings will be connected +* together in series. A zero value requests that they be connected in +* parallel. + +* Returned Value: +* A pointer to the new CmpMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstCmpMap *new; /* Pointer to new CmpMap */ + int map_f; /* Forward transformation defined? */ + int map_i; /* Inverse transformation defined? */ + int nin2; /* No. input coordinates for Mapping 2 */ + int nin; /* No. input coordinates for CmpMap */ + int nout1; /* No. output coordinates for Mapping 1 */ + int nout; /* No. output coordinates for CmpMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitCmpMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Determine in which directions each component Mapping is able to transform + coordinates. Combine these results to obtain a result for the overall + CmpMap. */ + map_f = astGetTranForward( map1 ) && astGetTranForward( map2 ); + map_i = astGetTranInverse( map1 ) && astGetTranInverse( map2 ); + if ( astOK ) { + +/* If connecting the Mappings in series, check that the number of coordinates + are compatible and report an error if they are not. */ + if ( series ) { + nout1 = astGetNout( map1 ); + nin2 = astGetNin( map2 ); + if ( astOK && ( nout1 != nin2 ) ) { + astError( AST__INNCO, "astInitCmpMap(%s): The number of output " + "coordinates per point (%d) for the first Mapping " + "supplied does not match the number of input " + "coordinates (%d) for the second Mapping.", status, name, nout1, + nin2 ); + } + } + } + +/* If OK, determine the total number of input and output coordinates per point + for the CmpMap. */ + if ( astOK ) { + if ( series ) { + nin = astGetNin( map1 ); + nout = astGetNout( map2 ); + } else { + nin = astGetNin( map1 ) + astGetNin( map2 ); + nout = astGetNout( map1 ) + astGetNout( map2 ); + } + + } else { + nin = 0; + nout = 0; + } + +/* Initialise a Mapping structure (the parent class) as the first component + within the CmpMap structure, allocating memory if necessary. Specify + the number of input and output coordinates and in which directions the + Mapping should be defined. */ + if ( astOK ) { + new = (AstCmpMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nout, map_f, map_i ); + + if ( astOK ) { + +/* Initialise the CmpMap data. */ +/* --------------------------- */ +/* Store pointers to the component Mappings. Extract Mappings if + FrameSets are provided. */ + if( astIsAFrameSet( map1 ) ) { + new->map1 = astGetMapping( (AstFrameSet *) map1, AST__BASE, + AST__CURRENT ); + } else { + new->map1 = astClone( map1 ); + } + + if( astIsAFrameSet( map2 ) ) { + new->map2 = astGetMapping( (AstFrameSet *) map2, AST__BASE, + AST__CURRENT ); + } else { + new->map2 = astClone( map2 ); + } + + +/* Save the initial values of the inversion flags for these Mappings. */ + new->invert1 = astGetInvert( new->map1 ); + new->invert2 = astGetInvert( new->map2 ); + +/* Note whether the Mappings are joined in series (instead of in parallel), + constraining this flag to be 0 or 1. */ + new->series = ( series != 0 ); + +/* If an error occurred, clean up by annulling the Mapping pointers and + deleting the new object. */ + if ( !astOK ) { + new->map1 = astAnnul( new->map1 ); + new->map2 = astAnnul( new->map2 ); + new = astDelete( new ); + } + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstCmpMap *astLoadCmpMap_( void *mem, size_t size, + AstCmpMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadCmpMap + +* Purpose: +* Load a CmpMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpmap.h" +* AstCmpMap *astLoadCmpMap( void *mem, size_t size, +* AstCmpMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* CmpMap loader. + +* Description: +* This function is provided to load a new CmpMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* CmpMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a CmpMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the CmpMap is to be +* loaded. This must be of sufficient size to accommodate the +* CmpMap data (sizeof(CmpMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the CmpMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the CmpMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstCmpMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new CmpMap. If this is NULL, a pointer to +* the (static) virtual function table for the CmpMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "CmpMap" is used instead. + +* Returned Value: +* A pointer to the new CmpMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpMap *new; /* Pointer to the new CmpMap */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this CmpMap. In this case the + CmpMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstCmpMap ); + vtab = &class_vtab; + name = "CmpMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitCmpMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built CmpMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "CmpMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Series. */ +/* ------- */ + new->series = astReadInt( channel, "series", 1 ); + new->series = ( new->series != 0 ); + +/* First Invert flag. */ +/* ------------------ */ + new->invert1 = astReadInt( channel, "inva", 0 ); + new->invert1 = ( new->invert1 != 0 ); + +/* Second Invert flag. */ +/* ------------------- */ + new->invert2 = astReadInt( channel, "invb", 0 ); + new->invert2 = ( new->invert2 != 0 ); + +/* First Mapping. */ +/* -------------- */ + new->map1 = astReadObject( channel, "mapa", NULL ); + +/* Second Mapping. */ +/* --------------- */ + new->map2 = astReadObject( channel, "mapb", NULL ); + +/* If an error occurred, clean up by deleting the new CmpMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new CmpMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* None. */ + + + + + + + + diff --git a/cmpmap.h b/cmpmap.h new file mode 100644 index 0000000..ed305bb --- /dev/null +++ b/cmpmap.h @@ -0,0 +1,300 @@ +#if !defined( CMPMAP_INCLUDED ) /* Include this file only once */ +#define CMPMAP_INCLUDED +/* +*+ +* Name: +* cmpmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the CmpMap class. + +* Invocation: +* #include "cmpmap.h" + +* Description: +* This include file defines the interface to the CmpMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* A CmpMap is a compound Mapping which allows two component +* Mappings (of any class) to be connected together to form a more +* complex Mapping. This connection may either be "in series" +* (where the first Mapping is used to transform the coordinates of +* each point and the second mapping is then applied to the +* result), or "in parallel" (where one Mapping transforms the +* earlier coordinates for each point and the second Mapping +* simultaneously transforms the later coordinates). +* +* Since a CmpMap is itself a Mapping, it can be used as a +* component in forming further CmpMaps. Mappings of arbitrary +* complexity may be built from simple individual Mappings in this +* way. + +* Inheritance: +* The CmpMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* astSimplify +* Simplify a CmpMap. +* +* Protected: +* astMapList +* Decompose a CmpMap into a sequence of simpler Mappings. +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsACmpMap +* Test class membership. +* astCmpMap +* Create a CmpMap. +* +* Protected: +* astCheckCmpMap +* Validate class membership. +* astInitCmpMap +* Initialise a CmpMap. +* astInitCmpMapVtab +* Initialise the virtual function table for the CmpMap class. +* astLoadCmpMap +* Load a CmpMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstCmpMap +* CmpMap object type. +* +* Protected: +* AstCmpMapVtab +* CmpMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 6-FEB-1996 (RFWS): +* Original version. +* 25-SEP-1996 (RFWS): +* Implemented external interface and I/O facilities. +* 13-DEC-1996 (RFWS): +* Over-ride the astSimplify method. +* 8-JAN-2003 (DSB): +* Added protected astInitCmpMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* CmpMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstCmpMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstMapping *map1; /* Pointer to first Mapping */ + AstMapping *map2; /* Pointer to second Mapping */ + char invert1; /* Inversion flag for first Mapping */ + char invert2; /* Inversion flag for second Mapping */ + char series; /* Connect in series (else in parallel)? */ +} AstCmpMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstCmpMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +/* None. */ +} AstCmpMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstCmpMapGlobals { + AstCmpMapVtab Class_Vtab; + int Class_Init; + int Simplify_Depth; + AstMapping **Simplify_Stackmaps; +} AstCmpMapGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(CmpMap) /* Check class membership */ +astPROTO_ISA(CmpMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstCmpMap *astCmpMap_( void *, void *, int, const char *, int *, ...); +#else +AstCmpMap *astCmpMapId_( void *, void *, int, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstCmpMap *astInitCmpMap_( void *, size_t, int, AstCmpMapVtab *, + const char *, AstMapping *, AstMapping *, int, int * ); + +/* Vtab initialiser. */ +void astInitCmpMapVtab_( AstCmpMapVtab *, const char *, int * ); + +/* Loader. */ +AstCmpMap *astLoadCmpMap_( void *, size_t, AstCmpMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitCmpMapGlobals_( AstCmpMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +/* None. */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckCmpMap(this) astINVOKE_CHECK(CmpMap,this,0) +#define astVerifyCmpMap(this) astINVOKE_CHECK(CmpMap,this,1) + +/* Test class membership. */ +#define astIsACmpMap(this) astINVOKE_ISA(CmpMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astCmpMap astINVOKE(F,astCmpMap_) +#else +#define astCmpMap astINVOKE(F,astCmpMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitCmpMap(mem,size,init,vtab,name,map1,map2,series) \ +astINVOKE(O,astInitCmpMap_(mem,size,init,vtab,name,astCheckMapping(map1),astCheckMapping(map2),series,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitCmpMapVtab(vtab,name) astINVOKE(V,astInitCmpMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadCmpMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadCmpMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckCmpMap to validate CmpMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +/* None. */ +#endif + + + + + diff --git a/cmpregion.c b/cmpregion.c new file mode 100644 index 0000000..c3b5b07 --- /dev/null +++ b/cmpregion.c @@ -0,0 +1,5127 @@ +/* +*class++ +* Name: +* CmpRegion + +* Purpose: +* A combination of two regions within a single Frame + +* Constructor Function: +c astCmpRegion +f AST_CMPREGION + +* Description: +* A CmpRegion is a Region which allows two component +* Regions (of any class) to be combined to form a more complex +* Region. This combination may be performed a boolean AND, OR +* or XOR (exclusive OR) operator. If the AND operator is +* used, then a position is inside the CmpRegion only if it is +* inside both of its two component Regions. If the OR operator is +* used, then a position is inside the CmpRegion if it is inside +* either (or both) of its two component Regions. If the XOR operator +* is used, then a position is inside the CmpRegion if it is inside +* one but not both of its two component Regions. Other operators can +* be formed by negating one or both component Regions before using +* them to construct a new CmpRegion. +* +* The two component Region need not refer to the same coordinate +* Frame, but it must be possible for the +c astConvert +f AST_CONVERT +* function to determine a Mapping between them (an error will be +* reported otherwise when the CmpRegion is created). For instance, +* a CmpRegion may combine a Region defined within an ICRS SkyFrame +* with a Region defined within a Galactic SkyFrame. This is +* acceptable because the SkyFrame class knows how to convert between +* these two systems, and consequently the +c astConvert +f AST_CONVERT +* function will also be able to convert between them. In such cases, +* the second component Region will be mapped into the coordinate Frame +* of the first component Region, and the Frame represented by the +* CmpRegion as a whole will be the Frame of the first component Region. +* +* Since a CmpRegion is itself a Region, it can be used as a +* component in forming further CmpRegions. Regions of arbitrary +* complexity may be built from simple individual Regions in this +* way. + +* Inheritance: +* The CmpRegion class inherits from the Region class. + +* Attributes: +* The CmpRegion class does not define any new attributes beyond those +* which are applicable to all Regions. + +* Functions: +c The CmpRegion class does not define any new functions beyond those +f The CmpRegion class does not define any new routines beyond those +* which are applicable to all Regions. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 7-OCT-2004 (DSB): +* Original version. +* 28-MAY-2007 (DSB): +* - Corrected RegBaseMesh. +* - In RegBaseBox, if the CmpRegion is bounded find the box by +* finding the extreme position sin a mesh covering the boundary. +* 20-JAN-2009 (DSB): +* Over-ride astRegBasePick. +* 19-MAR-2009 (DSB): +* Over-ride the astDecompose method. +* 8-SEP-2009 (DSB): +* Fix logic in RegTrace. +* 9-SEP-2009 (DSB): +* - Added astCmpRegionList +* - Added support for XOR +* - Override astGetObjSize. +* 27-APR-2012 (DSB): +* - Cache the bounded property. +* - Speed up plotting of CmpRegions by using the cached negation +* of a Region instead of setting the Regions's Negated flag (which +* causes the Region's cache to be cleared). +* 30-APR-2012 (DSB): +* Use geodesic distance to measure distances around the two component +* Regions when tracing the border. Previously, a distance normalised +* from zero to one was used for both component Regions, but this gives +* greater priority to Regions higher in the CmpRegion nesting order, +* resulting in a high chance that lower Regions will not be seen. +* 7-JUN-2012 (DSB): +* Override astRegSplit method. +* 21-NOV-2012 (DSB): +* Map the regions returned by RegSplit into the current Frame of the +* CmpRegion. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS CmpRegion + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "nullregion.h" /* Boundless Regions */ +#include "cmpregion.h" /* Interface definition for this class */ +#include "unitmap.h" /* Unit Mapings */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *(* parent_getdefunc)( AstRegion *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static void (* parent_setclosed)( AstRegion *, int, int * ); +static void (* parent_setmeshsize)( AstRegion *, int, int * ); +static void (* parent_clearclosed)( AstRegion *, int * ); +static void (* parent_clearmeshsize)( AstRegion *, int * ); +static double (*parent_getfillfactor)( AstRegion *, int * ); +static void (*parent_regsetattrib)( AstRegion *, const char *, char **, int * ); +static void (*parent_regclearattrib)( AstRegion *, const char *, char **, int * ); +static void (* parent_resetcache)( AstRegion *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(CmpRegion) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(CmpRegion,Class_Init) +#define class_vtab astGLOBAL(CmpRegion,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstCmpRegionVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstCmpRegion *astCmpRegionId_( void *, void *, int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *GetDefUnc( AstRegion *, int * ); +static AstRegion *MatchRegion( AstRegion *, int, AstRegion *, const char *, int * ); +static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * ); +static AstRegion **RegSplit( AstRegion *, int *, int * ); +static double GetFillFactor( AstRegion *, int * ); +static int CmpRegionList( AstCmpRegion *, int *, AstRegion ***, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetBounded( AstRegion *, int * ); +static int GetObjSize( AstObject *, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static void ClearClosed( AstRegion *, int * ); +static void ClearMeshSize( AstRegion *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GetRegions( AstCmpRegion *, AstRegion **, AstRegion **, int *, int *, int *, int * ); +static void RegBaseBox( AstRegion *, double *, double *, int * ); +static void RegBaseBox2( AstRegion *, double *, double *, int * ); +static void RegClearAttrib( AstRegion *, const char *, char **, int * ); +static void RegSetAttrib( AstRegion *, const char *, char **, int * ); +static void ResetCache( AstRegion *this, int * ); +static void SetBreakInfo( AstCmpRegion *, int, int * ); +static void SetClosed( AstRegion *, int, int * ); +static void SetMeshSize( AstRegion *, int, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); +static void XORCheck( AstCmpRegion *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Member functions. */ +/* ================= */ +int CmpRegionList( AstCmpRegion *this, int *nreg, AstRegion ***reg_list, + int *status ) { +/* +*+ +* Name: +* astCmpRegionList + +* Purpose: +* Decompose a CmpRegion into a sequence of simpler Regions. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "cmpregion.h" +* int astCmpRegionList( AstCmpRegion *this, int *nreg, +* AstRegion ***reg_list, int *status ) + +* Class Membership: +* CmpRegion method. + +* Description: +* This function decomposes a CmpRegion into a sequence of simpler +* Regions which may be applied in sequence to achieve the same +* effect. + +* Parameters: +* this +* Pointer to the CmpRegion to be decomposed (the CmpRegion is not +* actually modified by this function). +* nreg +* The address of an int which holds a count of the number of +* individual Regions in the decomposition. On entry, this +* should count the number of Regions already in the +* "*reg_list" array (below). On exit, it is updated to include +* any new Regions appended by this function. +* reg_list +* Address of a pointer to an array of Region pointers. On +* entry, this array pointer should either be NULL (if no +* Regions have yet been obtained) or should point at a +* dynamically allocated array containing Region pointers +* ("*nreg" in number) which have been obtained from a previous +* invocation of this function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Region pointers that result from the decomposition +* requested. These pointers will be appended to any previously +* present, and the array pointer will be updated as necessary +* to refer to the enlarged array (any space released by the +* original array will be freed automatically). +* +* The new Region pointers returned will identify a sequence of +* Region which, when applied in order, will represent an area +* equivalent to that of the original Region. +* +* All the Region pointers returned by this function should be +* annulled by the caller, using astAnnul, when no longer +* required. The dynamic array holding these pointers should +* also be freed, using astFree. + +* Returned Value: +* An integer identifying the boolean operation that should be used to +* combine the Regions returned in "reg_list". This will be AST__AND +* or AST__OR. + +*- +*/ + +/* Local Variables: */ + AstCmpRegion *cmpreg; + int add; + int result; + +/* Check the global error status. */ + if ( !astOK ) return AST__AND; + +/* Check if this CmpRegion has an equivalent XOR representation. Is so, + store details of the XOR representation in the CmpRegion. */ + XORCheck( this, status ); + +/* The CmpRegion class only has full support for AND and OR operators. + However, it can also represent XOR operators, but it does this by + an equivalent set of AND and OR operators. When an XOR CmpRegion is + created, the original supplied argument regions are stored in + "this->xor1" and "this->xor2", and the component Regions placed in the + new CmpRegion are actually CmpRegions that implement the equivalent + of an XOR operation, using AND and OR operators. We want to hide this + to the outside world, so if the supplied CmpRegion represents an XOR + operation, add the XOR regions to the returned list, and return an + XOR operator. */ + if( this->xor1 ) { + *reg_list = astGrow( *reg_list, *nreg + 2, sizeof( AstRegion * ) ); + if( astOK ) { + ( *reg_list )[ (*nreg)++ ] = astClone( this->xor1 ); + ( *reg_list )[ (*nreg)++ ] = astClone( this->xor2 ); + } + result = AST__XOR; + +/* For AND and OR operators, we deal with the component Regions directly. */ + } else { + +/* If the first component of the supplied CmpRegion is itself a CmpRegion + that uses the same boolean operator as "this", call this function + recursively to add its component Regions to the returned list. */ + add = 1; + if( astIsACmpRegion( this->region1 ) ) { + cmpreg = (AstCmpRegion *) this->region1; + if( cmpreg->oper == this->oper ) { + (void) CmpRegionList( cmpreg, nreg, reg_list, status ); + add = 0; + } + } + +/* Otherwise, add the component Region directly into the returned list of + Regions. */ + if( add ) { + *reg_list = astGrow( *reg_list, *nreg + 1, sizeof( AstRegion * ) ); + if( astOK ) { + ( *reg_list )[ *nreg ] = astClone( this->region1 ); + ( *nreg )++; + } + } + +/* Do the same for the second component region */ + add = 1; + if( astIsACmpRegion( this->region2 ) ) { + cmpreg = (AstCmpRegion *) this->region2; + if( cmpreg->oper == this->oper ) { + (void) CmpRegionList( cmpreg, nreg, reg_list, status ); + add = 0; + } + } + + if( add ) { + *reg_list = astGrow( *reg_list, *nreg + 1, sizeof( AstRegion * ) ); + if( astOK ) { + ( *reg_list )[ *nreg ] = astClone( this->region2 ); + ( *nreg )++; + } + } + + result = this->oper; + } + +/* Return the boolean operator used to combine the regions in the + returned array. */ + return result; +} + +static void Decompose( AstMapping *this_mapping, AstMapping **map1, + AstMapping **map2, int *series, int *invert1, + int *invert2, int *status ) { +/* +* +* Name: +* Decompose + +* Purpose: +* Decompose a CmpRegion into two component Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void Decompose( AstMapping *this, AstMapping **map1, +* AstMapping **map2, int *series, +* int *invert1, int *invert2, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the protected astDecompose +* method inherited from the Mapping class). + +* Description: +* This function returns pointers to two Mappings which, when applied +* either in series or parallel, are equivalent to the supplied Mapping. +* +* Since the Frame class inherits from the Mapping class, Frames can +* be considered as special types of Mappings and so this method can +* be used to decompose either CmpMaps, CmpFrames, CmpRegions or Prisms. + +* Parameters: +* this +* Pointer to the Mapping. +* map1 +* Address of a location to receive a pointer to first component +* Mapping. +* map2 +* Address of a location to receive a pointer to second component +* Mapping. +* series +* Address of a location to receive a value indicating if the +* component Mappings are applied in series or parallel. A non-zero +* value means that the supplied Mapping is equivalent to applying map1 +* followed by map2 in series. A zero value means that the supplied +* Mapping is equivalent to applying map1 to the lower numbered axes +* and map2 to the higher numbered axes, in parallel. +* invert1 +* The value of the Invert attribute to be used with map1. +* invert2 +* The value of the Invert attribute to be used with map2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component rames using the returned +* pointers will be reflected in the supplied CmpFrame. + +*- +*/ + + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpRegion *) this_mapping; + +/* The components Frames of a CmpRegion are considered to be series + Mappings. */ + if( series ) *series = 1; + +/* The Frames are returned in their original order whether or not the + CmpRegion has been inverted. */ + if( map1 ) *map1 = astClone( this->region1 ); + if( map2 ) *map2 = astClone( this->region2 ); + +/* The invert flags dont mean anything for a Region, but we return them + anyway. If the CmpRegion has been inverted, return inverted Invert flags. */ + if( astGetInvert( this ) ) { + if( invert1 ) *invert1 = astGetInvert( this->region1 ) ? 0 : 1; + if( invert2 ) *invert2 = astGetInvert( this->region2 ) ? 0 : 1; + +/* If the CmpRegion has not been inverted, return the current Invert flags. */ + } else { + if( invert1 ) *invert1 = astGetInvert( this->region1 ); + if( invert2 ) *invert2 = astGetInvert( this->region2 ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Objects are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int Equal( AstObject *this_object, AstObject *that_object, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astEqual protected +* method inherited from the Region class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two CmpRegions are equivalent. + +* Parameters: +* this +* Pointer to the first CmpRegion. +* that +* Pointer to the second CmpRegion. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the CmpRegions are equivalent, zero otherwise. + +* Notes: +* - The CmpRegions are equivalent if their component Regions are +* equivalent and if they have the same boolean operation, negation +* and closed flags. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpRegion *that; + AstCmpRegion *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent Region class. This checks + that the Objects are both of the same class, and have the same Negated + and Closed flags (amongst other things). */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Obtain pointers to the two CmpRegion structures. */ + this = (AstCmpRegion *) this_object; + that = (AstCmpRegion *) that_object; + +/* Test their first component Regions for equality. */ + if( astEqual( this->region1, that->region1 ) ) { + +/* Test their second component Regions for equality. */ + if( astEqual( this->region2, that->region2 ) ) { + +/* Test their boolean operator for equality. */ + if( this->oper == that->oper ) result = 1; + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Define a function to set an attribute value for a CmpRegion. + +* Type: +* Private macro. + +* Synopsis: +* #include "cmpregion.h" +* MAKE_SET(attribute,lattribute,type) + +* Class Membership: +* Defined by the CmpRegion class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstRegion *this, value ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the component Regions. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,lattribute,type) \ +static void Set##attribute( AstRegion *this_region, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to set the value in the parent Region structure. */ \ + (*parent_set##lattribute)( this_region, value, status ); \ +\ +/* Also set the value in the two component Regions. */ \ + this = (AstCmpRegion *) this_region; \ + astSet##attribute( this->region1, value ); \ + astSet##attribute( this->region2, value ); \ +} + +/* Use the above macro to create accessors for the MeshSize and Closed attributes. */ +MAKE_SET(MeshSize,meshsize,int) +MAKE_SET(Closed,closed,int) + +/* Undefine the macro. */ +#undef MAKE_SET + +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Define a function to clear an attribute value for a CmpRegion. + +* Type: +* Private macro. + +* Synopsis: +* #include "cmpregion.h" +* MAKE_CLEAR(attribute,lattribute) + +* Class Membership: +* Defined by the CmpRegion class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear( AstRegion *this ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the component Regions. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute,lattribute) \ +static void Clear##attribute( AstRegion *this_region, int *status ) { \ +\ +/* Local Variables: */ \ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to clear the value in the parent Region structure. */ \ + (*parent_clear##lattribute)( this_region, status ); \ +\ +/* Also clear the value in the two component Regions. */ \ + this = (AstCmpRegion *) this_region; \ + astClear##attribute( this->region1 ); \ + astClear##attribute( this->region2 ); \ +} + +/* Use the above macro to create accessors for the MeshSize and Closed attributes. */ +MAKE_CLEAR(MeshSize,meshsize) +MAKE_CLEAR(Closed,closed) + +/* Undefine the macro. */ +#undef MAKE_CLEAR + +static int GetBounded( AstRegion *this_region, int *status ) { +/* +* Name: +* GetBounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int GetBounded( AstRegion *this, int *status ) + +* Class Membership: +* CmpRegion method (over-rides the astGetBounded method inherited from +* the Region class). + +* Description: +* This function returns a flag indicating if the Region is bounded. +* The implementation provided by the base Region class is suitable +* for Region sub-classes representing the inside of a single closed +* curve (e.g. Circle, Ellipse, Box, etc). Other sub-classes (such as +* CmpRegion, PointList, etc ) may need to provide their own +* implementations. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Region is bounded. Zero otherwise. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + int neg1; /* Negated flag to use with first component */ + int neg2; /* Negated flag to use with second component */ + int oper; /* Combination operator */ + int overlap; /* Nature of overlap between components */ + int reg1b; /* Is the first component Region bounded?*/ + int reg2b; /* Is the second component Region bounded?*/ + int result; /* Returned result */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Only calculated a new value if there is no cached value in the Region. */ + if( this->bounded == -INT_MAX ) { + +/* Get the component Regions, how they should be combined, and the + Negated values which should be used with them. The returned values + take account of whether the supplied CmpRegion has itself been Negated + or not. The returned Regions represent regions within the base Frame + of the FrameSet encapsulated by the parent Region structure. */ + GetRegions( this, ®1, ®2, &oper, &neg1, &neg2, status ); + +/* If the first component Region does not have the required value for + its "Negated" attribute, use the negation of "reg1" in place of "reg1" + itself. */ + if( neg1 != astGetNegated( reg1 ) ) { + AstRegion *tmp = astGetNegation( reg1 ); + (void) astAnnul( reg1 ); + reg1 = tmp; + } + +/* If the second component Region does not have the required value for + its "Negated" attribute, use the negation of "reg2" in place of "reg2" + itself. */ + if( neg2 != astGetNegated( reg2 ) ) { + AstRegion *tmp = astGetNegation( reg2 ); + (void) astAnnul( reg2 ); + reg2 = tmp; + } + +/* See if either of the component Regions is bounded. */ + reg1b = astGetBounded( reg1 ); + reg2b = astGetBounded( reg2 ); + +/* If the regions are ANDed... */ + if( oper == AST__AND ) { + +/* If either one of the two components are bounded, then the AND region is + bounded. */ + if( reg1b || reg2b ) { + result = 1; + +/* If neither of the two components is bounded, then the AND region is + unbounded if there is partial or no overlap between them and is bounded + otherwise. */ + } else { + overlap = astOverlap( reg1, reg2 ); + if( overlap == 1 || overlap == 4 || overlap == 6 ) { + result = 0; + } else { + result = 1; + } + } + +/* If the regions are ORed... */ + } else { + +/* If either one of the two components is unbounded, then the OR region is + unbounded. */ + if( !reg1b || !reg2b ) { + result = 0; + +/* If both of the two components are bounded, then the OR region is also + bounded. */ + } else { + result = 1; + } + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + +/* Cache the value in the CmpRegion. */ + this->bounded = astOK ? result : -INT_MAX; + } + +/* Return zero if an error occurred. Otherwise, return the cached value. */ + if( astOK ) { + result = ( this->bounded == -INT_MAX ) ? 0 : this->bounded; + } else { + result = 0; + } + +/* Return the required pointer. */ + return result; +} + +static double GetFillFactor( AstRegion *this_region, int *status ) { +/* +* Name: +* GetFillFactor + +* Purpose: +* Obtain the value of the FillFactor attribute for a CmpRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* double GetFillFactor( AstRegion *this, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astGetFillFactor method inherited +* from the Region class). + +* Description: +* This function returns the value of the FillFactor attribute for a +* CmpRegion. A suitable default value is returned if no value has +* previously been set. + +* Parameters: +* this +* Pointer to the CmpRegion. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The FillFactor value to use. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; + double result; + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Initialise. */ + result = AST__BAD; + +/* Obtain a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* See if a FillFactor value has been set. If so, use the parent + astGetFillFactor method to obtain it. */ + if ( astTestFillFactor( this ) ) { + result = (*parent_getfillfactor)( this_region, status ); + +/* Otherwise, we will generate a default value equal to the FillFactor values + of the first component Region. */ + } else { + result = astGetFillFactor( this->region1 ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied CmpRegion, +* in bytes. + +* Parameters: +* this +* Pointer to the CmpRegion. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the CmpRegion structure. */ + this = (AstCmpRegion *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->region1 ); + result += astGetObjSize( this->region2 ); + if( this->xor1 ) result += astGetObjSize( this->xor1 ); + if( this->xor2 ) result += astGetObjSize( this->xor2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void GetRegions( AstCmpRegion *this, AstRegion **reg1, AstRegion **reg2, + int *oper, int *neg1, int *neg2, int *status ) { +/* +* +* Name: +* GetRegions + +* Purpose: +* Get the component Regions of a CmpRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void GetRegions( AstCmpRegion *this, AstRegion **reg1, AstRegion **reg2, +* int *oper, int *neg1, int *neg2, int *status ) + +* Class Membership: +* CmpRegion member function + +* Description: +* This function returns pointers to two Regions which, when applied +* using the returned boolean operator, are equivalent to the supplied +* Region. If the CmpRegion has been negated, then the returned operator +* and "negated" flags will be set such that they represent the +* negated CmpRegion. +* +* The current Frames in both the returned component Regions will be +* equivalent to the base Frame in the FrameSet encapsulated by the +* parent Region structure. + +* Parameters: +* this +* Pointer to the CmpRegion. +* reg1 +* Address of a location to receive a pointer to first component +* Region. The current Frame in this region will be equivalent to +* the base Frame in the FrameSet +* reg2 +* Address of a location to receive a pointer to second component +* Region. +* oper +* Address of a location to receive a value indicating how the +* component Regions are combined together. This will be one of +* AST__AND or AST__OR +* neg1 +* The value of the Negated attribute to be used with reg1. +* neg2 +* The value of the Negated attribute to be used with reg2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component Regions using the returned +* pointers will be reflected in the supplied CmpRegion. + +*- +*/ + +/* Initialise */ + if( reg1 ) *reg1 = NULL; + if( reg2 ) *reg2 = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Return the component Region pointers. */ + if( reg1 ) *reg1 = astClone( this->region1 ); + if( reg2 ) *reg2 = astClone( this->region2 ); + +/* Initialise the other returned items. Note, the CmpRegion initialiser + stored a deep copy of the supplied component Regions, and so we do not + need to worry about attributes of the components having been changed + after the creation of the CmpRegion. This is different to the CmpMap + class which merely clones its supplied component pointers and so has + to save copies of the original Invert settings within the CmpMap + structure. */ + if( oper ) *oper = this->oper; + if( neg1 ) *neg1 = astGetNegated( this->region1 ); + if( neg2 ) *neg2 = astGetNegated( this->region2 ); + +/* If the CmpRegion has been inverted, we modify the boolean operator and + negation flags so that they reflect the inverted CmpRegion. */ + if( astGetNegated( this ) ) { + +/* If the component Regions are combined using AND, then the negated + CmpRegion combines its negated components using OR. */ + if( this->oper == AST__AND ){ + if( oper ) *oper = AST__OR; + if( neg1 ) *neg1 = *neg1 ? 0 : 1; + if( neg2 ) *neg2 = *neg2 ? 0 : 1; + +/* If the component Regions are combined using OR, then the negated CmpRegion + combines its negated components using AND. */ + } else if( this->oper == AST__OR ){ + if( oper ) *oper = AST__AND; + if( neg1 ) *neg1 = *neg1 ? 0 : 1; + if( neg2 ) *neg2 = *neg2 ? 0 : 1; + + } else if( astOK ) { + astError( AST__INTER, "GetRegions(%s): The %s refers to an unknown " + "boolean operator with identifier %d (internal AST " + "programming error).", status, astGetClass( this ), + astGetClass( this ), this->oper ); + } + } +} + +static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) { +/* +* Name: +* GetDefUnc + +* Purpose: +* Obtain a pointer to the default uncertainty Region for a given Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstRegion *GetDefUnc( AstRegion *this ) + +* Class Membership: +* CmpRegion method (over-rides the astGetDefUnc method inherited from +* the Region class). + +* This function returns a pointer to a Region which represents the +* default uncertainty associated with a position on the boundary of the +* given Region. The returned Region refers to the base Frame within the +* FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstRegion *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* If the first component region has non-default uncertainty, use it as + the default uncertainty for the CmpRegion. Note, the current Frame of + an uncertainty Region is assumed to be the same as the base Frame in the + CmpRegion. */ + if( astTestUnc( this->region1 ) ) { + result = astGetUncFrm( this->region1, AST__CURRENT ); + +/* Otherwise, if the second component region has non-default uncertainty, + use it as the default uncertainty for the CmpRegion. */ + } else if( astTestUnc( this->region2 ) ) { + result = astGetUncFrm( this->region2, AST__CURRENT ); + +/* Otherwise, use the parent method to determine the default uncertainty. */ + } else { + result = (* parent_getdefunc)( this_region, status ); + } + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +void astInitCmpRegionVtab_( AstCmpRegionVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitCmpRegionVtab + +* Purpose: +* Initialise a virtual function table for a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* void astInitCmpRegionVtab( AstCmpRegionVtab *vtab, const char *name ) + +* Class Membership: +* CmpRegion vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the CmpRegion class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsACmpRegion) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + + vtab->CmpRegionList = CmpRegionList; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_getdefunc = region->GetDefUnc; + region->GetDefUnc = GetDefUnc; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_resetcache = region->ResetCache; + region->ResetCache = ResetCache; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_clearclosed = region->ClearClosed; + region->ClearClosed = ClearClosed; + + parent_clearmeshsize = region->ClearMeshSize; + region->ClearMeshSize = ClearMeshSize; + + parent_setclosed = region->SetClosed; + region->SetClosed = SetClosed; + + parent_setmeshsize = region->SetMeshSize; + region->SetMeshSize = SetMeshSize; + + parent_getfillfactor = region->GetFillFactor; + region->GetFillFactor = GetFillFactor; + + parent_regsetattrib = region->RegSetAttrib; + region->RegSetAttrib = RegSetAttrib; + + parent_regclearattrib = region->RegClearAttrib; + region->RegClearAttrib = RegClearAttrib; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping->Decompose = Decompose; + region->RegBaseBox = RegBaseBox; + region->RegBaseBox2 = RegBaseBox2; + region->RegBaseMesh = RegBaseMesh; + region->RegSplit = RegSplit; + region->RegPins = RegPins; + region->RegTrace = RegTrace; + region->GetBounded = GetBounded; + region->RegBasePick = RegBasePick; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "CmpRegion", "Combination of two Regions" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the CmpRegion structure. */ + this = (AstCmpRegion *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->region1, mode, extra, fail ); + if( !result ) result = astManageLock( this->region2, mode, extra, fail ); + + return result; + +} +#endif + +static AstRegion *MatchRegion( AstRegion *this, int ifrm, AstRegion *that, + const char *method, int *status ) { +/* +* Name: +* MatchRegion + +* Purpose: +* Map a Region into the Frame of another Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstRegion *MatchRegion( AstRegion *this, int ifrm, AstRegion *that, +* const char *method, int *status ) + +* Class Membership: +* CmpRegion method. + +* Description: +* This function returns a pointer to a new Region which is a copy of +* "that" mapped into either the base or current Frame of "this". + +* Parameters: +* this +* Pointer to a Region defining the Frame of the returned Region. +* ifrm +* The index of a Frame within the FrameSet encapsulated by "this". +* The returned Region will refer to the requested Frame. It should +* be either AST__CURRENT or AST__BASE. +* that +* Pointer to a Region defining the shape and extent of the +* returned Region. +* method +* Pointer to a string holding the calling method.This is only used +* in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a new Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame from "fs" */ + AstFrameSet *fs; /* FrameSet connecting that to this */ + AstMapping *map; /* Base->Current Mapping from "fs" */ + AstRegion *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. Also return NULL if no Regions were + supplied. */ + if ( !astOK || !this || !that ) return result; + +/* Temporarily invert "this" if we are matching its base Frame (since the + astConvert method matches current Frames). */ + if( ifrm == AST__BASE ) astInvert( this ); + +/* Find a FrameSet connecting the current Frames of the two Regions */ + fs = astConvert( that, this, "" ); + +/* Re-instate the original Frame indices in "this" if required. */ + if( ifrm == AST__BASE ) astInvert( this ); + +/* Check a conversion path was found. */ + if( fs ) { + +/* Get the Frame and Mapping form the FrameSet. */ + frm = astGetFrame( fs, AST__CURRENT ); + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Re-map the Region. */ + result = astMapRegion( that, map, frm ); + +/* Free resources. */ + frm = astAnnul( frm ); + map = astAnnul( map ); + fs = astAnnul( fs ); + +/* Report an error if there is no conversion between the two Frames. */ + } else { + astError( AST__INTER, "%s(%s): MatchRegion cannot convert between " + "the two supplied coordinate Frames (internal AST " + "programming error).", status, method, astGetClass( this ) ); + } + +/* Annul the returned pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstPointSet *ps; /* Mesh pointset */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **ptr; /* Pointer to mesh data */ + double *clbnd1; /* Point to 1st comp lower bounds array */ + double *clbnd2; /* Point to 2nd comp lower bounds array */ + double *cubnd1; /* Point to 1st comp upper bounds array */ + double *cubnd2; /* Point to 2nd comp upper bounds array */ + double *p; /* Pointer to next coordinate value */ + double lb; /* Lower limit */ + double ub; /* Upper limit */ + int i; /* Axis index */ + int icoord; /* Coordinate index */ + int inc1; /* First component interval is included? */ + int inc2; /* Second component interval is included? */ + int ipoint; /* Point index */ + int nax; /* Number of axes in Frame */ + int ncoord; /* Number of coords */ + int neg1; /* First component negated? */ + int neg2; /* Second component negated? */ + int npoint; /* Number of points */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure */ + this = (AstCmpRegion *) this_region; + +/* If the CmpRegion is bounded, we find the bounding box using a mesh of + points spread evenly over the boundary of the CmpRegion. */ + if( astGetBounded( this ) ) { + ps = astRegBaseMesh( this_region ); + ptr = astGetPoints( ps ); + ncoord = astGetNcoord( ps ); + npoint = astGetNpoint( ps ); + + if( astOK ) { + for( icoord = 0; icoord < ncoord; icoord++ ) { + lbnd[ icoord ] = DBL_MAX; + ubnd[ icoord ] = -DBL_MAX; + p = ptr[ icoord ]; + for( ipoint = 0; ipoint < npoint; ipoint++, p++ ) { + if( *p != AST__BAD ) { + if( *p < lbnd[ icoord ] ) lbnd[ icoord ] = *p; + if( *p > ubnd[ icoord ] ) ubnd[ icoord ] = *p; + } + } + } + } + ps = astAnnul( ps ); + +/* If the CmpRegion is not bounded we look at each axis individually. */ + } else { + +/* Get pointers to the component Regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* Get their negated flags */ + neg1 = astGetNegated( reg1 ); + neg2 = astGetNegated( reg2 ); + +/* The base Frame of the parent Region structure is the current Frame of + the component Regions. Get the no. of axes in this Frame. */ + nax = astGetNaxes( reg1 ); + +/* Get the bounding boxes of the component Regions in this Frame. */ + clbnd1 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd1 = astMalloc( sizeof( double )*(size_t) nax ); + clbnd2 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd2 = astMalloc( sizeof( double )*(size_t) nax ); + if( astOK ) { + astGetRegionBounds( reg1, clbnd1, cubnd1 ); + astGetRegionBounds( reg2, clbnd2, cubnd2 ); + +/* Loop round every axis. */ + for( i = 0; i < nax; i++ ) { + +/* If the first component Region has been negated, the lower and upper + bounds from the first component are the bounds of an *excluded* axis + interval, not an included interval. If either of the bounds are + infinite, we can swap it to an included interval. If both bounds are + finite, we cannot convert to an included interval. In this case, we + assume that the gap will be filled at some point on another axis, if + there is more than 1 axis, and convert it to an unbouded included + interval. */ + inc1 = 1; + if( neg1 ) { + lb = clbnd1[ i ]; + ub = cubnd1[ i ]; + if( lb == -DBL_MAX ) clbnd1[ i ] = ub; + if( ub == DBL_MAX ) cubnd1[ i ] = lb; + if( lb != -DBL_MAX && ub != DBL_MAX ) { + if( nax == 1 ) { + inc1 = 0; + } else { + clbnd1[ i ] = -DBL_MAX; + cubnd1[ i ] = DBL_MAX; + } + } + } + +/* Likewise attempt to convert an excluded interval into an included + interval for the second component Region. */ + inc2 = 1; + if( neg2 ) { + lb = clbnd2[ i ]; + ub = cubnd2[ i ]; + if( lb == -DBL_MAX ) clbnd2[ i ] = ub; + if( ub == DBL_MAX ) cubnd2[ i ] = lb; + if( lb != -DBL_MAX && ub != DBL_MAX ) { + if( nax == 1 ) { + inc2 = 0; + } else { + clbnd2[ i ] = -DBL_MAX; + cubnd2[ i ] = DBL_MAX; + } + } + } + +/* If the component Regions are combined using AND, find the overlap of + the axis intervals. This depends on whether the intervals are included + or excluded. */ + if( this->oper == AST__AND ) { + + if( inc1 ) { + if( inc2 ) { + lbnd[ i ] = astMAX( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ] = astMIN( cubnd1[ i ], cubnd2[ i ] ); + } else { + lbnd[ i ] = clbnd1[ i ] < clbnd2[ i ] ? clbnd1[ i ] : cubnd2[ i ]; + ubnd[ i ] = cubnd1[ i ] > cubnd2[ i ] ? cubnd1[ i ] : clbnd2[ i ]; + } + } else { + if( inc2 ) { + lbnd[ i ] = clbnd2[ i ] < clbnd1[ i ] ? clbnd2[ i ] : cubnd1[ i ]; + ubnd[ i ] = cubnd2[ i ] > cubnd1[ i ] ? cubnd2[ i ] : clbnd1[ i ]; + } else { + lbnd[ i ] = clbnd1[ i ] < clbnd2[ i ] ? clbnd1[ i ] : cubnd2[ i ]; + ubnd[ i ] = cubnd1[ i ] > cubnd2[ i ] ? cubnd1[ i ] : clbnd2[ i ]; + } + } + +/* If the component Regions are not combined using AND, find the union of + the axis intervals. */ + } else { + if( inc1 && inc2 ) { + lbnd[ i ] = astMIN( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ] = astMAX( cubnd1[ i ], cubnd2[ i ] ); + } else { + lbnd[ i ] = -DBL_MAX; + ubnd[ i ] = DBL_MAX; + } + } + } + } + +/* Free resources. */ + clbnd1 = astFree( clbnd1 ); + cubnd1 = astFree( cubnd1 ); + clbnd2 = astFree( clbnd2 ); + cubnd2 = astFree( cubnd2 ); + } +} + +static void RegBaseBox2( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox2 + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void RegBaseBox2( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegBaseBox2 protected +* method inherited from the Region class). + +* Description: +* This function is similar to astRegBaseBox in that it returns the +* upper and lower axis bounds of a Region in the base Frame of the +* encapsulated FrameSet. But, in addition to assuming that the +* supplied Region has not been negated, it also assumes that any +* component Regions contained within the supplied Region have not been +* negated. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double *clbnd1; /* Point to 1st comp lower bounds array */ + double *clbnd2; /* Point to 2nd comp lower bounds array */ + double *cubnd1; /* Point to 1st comp upper bounds array */ + double *cubnd2; /* Point to 2nd comp upper bounds array */ + int i; /* Axis index */ + int nax; /* Number of axes in Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure */ + this = (AstCmpRegion *) this_region; + +/* Get pointers to the component Regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* The base Frame of the parent Region structure is the current Frame of + the component Regions. Get the no. of axes in this Frame. */ + nax = astGetNaxes( reg1 ); + +/* Get the bounding boxes of the component Regions in this Frame. */ + clbnd1 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd1 = astMalloc( sizeof( double )*(size_t) nax ); + clbnd2 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd2 = astMalloc( sizeof( double )*(size_t) nax ); + if( astOK ) { + astGetRegionBounds2( reg1, clbnd1, cubnd1 ); + astGetRegionBounds2( reg2, clbnd2, cubnd2 ); + +/* How we combine the two bounding boxes depends on the boolean operator + associated with this CmpRegion. For AND find the overlap of the two + bounding boxes. For other operators find the union. */ + if( this->oper == AST__AND ) { + for( i = 0; i < nax; i++ ) { + lbnd[ i ]= astMAX( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ]= astMIN( cubnd1[ i ], cubnd2[ i ] ); + } + + } else { + for( i = 0; i < nax; i++ ) { + lbnd[ i ]= astMIN( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ]= astMAX( cubnd1[ i ], cubnd2[ i ] ); + } + } + } + +/* Free resources. */ + clbnd1 = astFree( clbnd1 ); + cubnd1 = astFree( cubnd1 ); + clbnd2 = astFree( clbnd2 ); + cubnd2 = astFree( cubnd2 ); + +} + +static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. Annul the pointer using astAnnul when it +* is no longer needed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + + +/* Local Variables: */ + AstCmpRegion *this; /* The CmpRegion structure */ + AstPointSet *mesh1; /* PointSet holding mesh for 1st component */ + AstPointSet *mesh1b; /* Mesh for 1st component mapped by 2nd comp. */ + AstPointSet *mesh2; /* PointSet holding mesh for 2nd component */ + AstPointSet *mesh2b; /* Mesh for 2nd component mapped by 1st comp. */ + AstPointSet *result; /* Returned pointer */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **ptr1; /* Pointer to array of mesh1b axis value pointers */ + double **ptr2; /* Pointer to array of mesh2b axis value pointers */ + double **ptr; /* Pointer to array of total axis value pointers */ + double *lbnd; /* Pointer to array of bounding box lower bounds */ + double *ubnd; /* Pointer to array of bounding box upper bounds */ + double v; /* Axis value */ + int hasMesh1; /* Does 1st component Region have a mesh? */ + int hasMesh2; /* Does 2nd component Region have a mesh? */ + int ic; /* Axis index */ + int ip; /* Input point index */ + int jp; /* Output point index */ + int nc; /* No. of axis values per point */ + int np1; /* No. of points in mesh1b */ + int np2; /* No. of points in mesh2b */ + int np; /* No. of points in returned PointSet */ + int ok; /* Were all axis values good at this point? */ + +/* Initialise */ + result= NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this_region->basemesh ) { + result = astClone( this_region->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* Get pointers to the component regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* A mesh can only be produced for a Region if it is bounded when either + negated or un-negated. See if meshes can be produced for the component + Regions. */ + hasMesh1 = astGetBounded( reg1 ); + if( !hasMesh1 ){ + astNegate( reg1 ); + hasMesh1 = astGetBounded( reg1 ); + astNegate( reg1 ); + } + + hasMesh2 = astGetBounded( reg2 ); + if( !hasMesh2 ){ + astNegate( reg2 ); + hasMesh2 = astGetBounded( reg2 ); + astNegate( reg2 ); + } + +/* If neither Region has a mesh we cannot produce a mesh. */ + if( !hasMesh1 && !hasMesh2 && astOK ) { + astError( AST__INTER, "astRegBaseMesh(%s): No mesh can be " + "produced for the %s bacause neither of its component " + "Regions has a mesh (internal AST programming error).", status, + astGetClass( this ), astGetClass( this ) ); + +/* If only one Region has a mesh, we can produce a mesh so long as the + boolean operator is not OR. */ + } else if( ( !hasMesh1 || !hasMesh2 ) && this->oper == AST__OR && astOK ) { + astError( AST__INTER, "astRegBaseMesh(%s): No mesh can be produced " + "for the %s bacause one its component Regions has no " + "mesh and the union of the Regions is required (internal " + "AST programming error).", status, astGetClass( this ), astGetClass( this ) ); + } + +/* Allocate memory to hold a bounding box in the base Frame of the CmpRegion. */ + nc = astGetNin( this_region->frameset ); + lbnd = astMalloc( sizeof( double )*(size_t) nc ); + ubnd = astMalloc( sizeof( double )*(size_t) nc ); + +/* Get current Frame meshes covering the two component Regions (the current + Frame of the component Regions is the same as the base Frame of the parent + Region). We now know that at least one Region has a mesh. If the other + one does not have a mesh we may be able to create a mesh by taking the + intersection of the Region with the bounding box of the bounded Region. */ + if( hasMesh1 ) { + mesh1 = astRegMesh( reg1 ); + if( hasMesh2 ) { + mesh2 = astRegMesh( reg2 ); + } else { + astGetRegionBounds( reg1, lbnd, ubnd ); + mesh2 = astBndMesh( reg2, lbnd, ubnd ); + } + + } else { + mesh2 = astRegMesh( reg2 ); + astGetRegionBounds( reg2, lbnd, ubnd ); + mesh1 = astBndMesh( reg1, lbnd, ubnd ); + } + +/* If the CmpRegion represents the intersection of the two component Regions + (AND operator), the total mesh is the sum of the component mesh points + which are inside the other component region. If the CmpRegion represents + the union of the two component Regions (OR operator), the total mesh is + the sum of the component mesh points which are outside the other component + region. So temporarily negate the component Regions if they are + combined using OR. */ + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* Transform the mesh for the first component using the second component + as a Mapping. Mesh points outside (or inside if "oper" is OR) the bounds + of the second component will be set bad. */ + mesh1b = astTransform( reg2, mesh1, 1, NULL ); + +/* Transform the mesh for the second component using the first component + as a Mapping. Mesh points outside (or inside if "oper" is OR) the bounds + of the first component will be set bad. */ + mesh2b = astTransform( reg1, mesh2, 1, NULL ); + +/* If required, negate them again to bring them back to their original state.*/ + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* The required mesh contains all the good points form both mesh1b and + mesh2b (i.e. all boundary points which are inside -or inside if "oper" + is OR- the other component Region). Create a PointSet assuming that all + points are good. First allocate an array to hold pointers to the arrays + holding coordinate values for each axis. */ + nc = astGetNcoord( mesh1b ); + np1 = astGetNpoint( mesh1b ); + np2 = astGetNpoint( mesh2b ); + np = np1 + np2; + result = astPointSet( np, nc, "", status ); + ptr = astGetPoints( result ); + +/* Get points to the axis values of the mapped meshes. */ + ptr1 = astGetPoints( mesh1b ); + ptr2 = astGetPoints( mesh2b ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Initialise the index of the next point in the total mesh. */ + jp = 0; + +/* Loop round all the points in the transformed mesh for the first + component. */ + for( ip = 0; ip < np1; ip++ ) { + +/* Assume this point has good axis values */ + ok = 1; + +/* Copy the axis values into the total mesh. Break if a bad axis value is + found. */ + for( ic = 0; ic < nc; ic++ ) { + v = ptr1[ ic ][ ip ]; + if( v != AST__BAD ) { + ptr[ ic ][ jp ] = v; + } else { + ok = 0; + break; + } + } + +/* If no bad axis values were found, increment the index of the next + point in the total mesh. */ + if( ok ) jp++; + } + +/* Now similarly copy the good values from the second transformed mesh onto + the end of the total mesh array. */ + for( ip = 0; ip < np2; ip++ ) { + ok = 1; + for( ic = 0; ic < nc; ic++ ) { + v = ptr2[ ic ][ ip ]; + if( v != AST__BAD ) { + ptr[ ic ][ jp ] = v; + } else { + ok = 0; + break; + } + } + if( ok ) jp++; + } + +/* If the total mesh contains no good points, we will create a PointSet + holding a single bad position. */ + if( jp == 0 ) { + np = 1; + for( ic = 0; ic < nc; ic++ ) ptr[ ic ][ 0 ] = AST__BAD; + } else { + np = jp; + } + +/* Adjust the size of the returned PointSet to exclude the extra space + caused by any axis values being bad in the transformed meshes. */ + astSetNpoint( result, np ); + + } + +/* Free resources. */ + mesh1 = astAnnul( mesh1 ); + mesh2 = astAnnul( mesh2 ); + mesh1b = astAnnul( mesh1b ); + mesh2b = astAnnul( mesh2b ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* Save the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK && result ) this_region->basemesh = astClone( result ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, + const int *axes, int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstFrame *frm1; /* Axes picked from the 1st encapsulated Region */ + AstFrame *frm2; /* Axes picked from the 2nd encapsulated Region */ + AstRegion *result; /* Returned Region */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion information. */ + this = (AstCmpRegion *) this_region; + +/* Both encapsulated regions refer to the same Frame (the base Frame of + the parent Region), so attempt to pick the requested axs from them. + If the resulting Frames are not Regions, we cannot pick the requested + axes so return the NULL Frame pointer initialised above. */ + frm1 = astPickAxes( this->region1, naxes, axes, NULL ); + if( astIsARegion( frm1 ) ) { + frm2 = astPickAxes( this->region2, naxes, axes, NULL ); + if( astIsARegion( frm2 ) ) { + +/* Create the new CmpRegion. */ + result = (AstRegion *) astCmpRegion( (AstRegion *) frm1, + (AstRegion *) frm2, + this->oper, "", status ); + } + +/* Free resources */ + frm2 = astAnnul( frm2 ); + } + frm1 = astAnnul( frm1 ); + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given CmpRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given CmpRegion. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied CmpRegion "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the CmpRegion. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstCmpRegion *this; /* Pointer to the CmpRegion structure. */ + AstPointSet *pset1; /* Points masked by 1st component Region */ + AstPointSet *pset2; /* Points masked by 2nd component Region */ + AstPointSet *psetb1; /* Points in base Frame of 1st component Region */ + AstPointSet *psetb2; /* Points in base Frame of 2nd component Region */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + AstRegion *unc1; /* Base Frame uncertainty in 1st component Region */ + AstRegion *unc2; /* Base Frame uncertainty in 2nd component Region */ + double **ptr1; /* Pointer to axis values in "pset1" */ + double **ptr2; /* Pointer to axis values in "pset2" */ + double *p1; /* Pointer to next axis zero value for pset1 */ + double *p2; /* Pointer to next axis zero value for pset2 */ + int *mask1; /* Mask for first component boundary */ + int *mask2; /* Mask for second component boundary */ + int ip; /* Point index */ + int np; /* Number of points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Get pointers to the two component Regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* Get a mask which indicates if each supplied point is on or off the + boundary of the first component Region. astRegPins expects its "pset" + argument to contain positions in the base Frame of the Region, so + we must first transform the supplied points into the base Frame of + "reg1". We must also map the uncertainty into the base Frame of the + component Region. */ + psetb1 = astRegTransform( reg1, pset, 0, NULL, NULL ); + unc1 = MatchRegion( reg1, AST__BASE, unc, "astRegPins", status ); + astRegPins( reg1, psetb1, unc1, &mask1 ); + +/* Likewise, get a mask which indicates if each supplied point is on or off + the boundary of the second component Region. */ + psetb2 = astRegTransform( reg2, pset, 0, NULL, NULL ); + unc2 = MatchRegion( reg2, AST__BASE, unc, "astRegPins", status ); + astRegPins( reg2, psetb2, unc2, &mask2 ); + +/* The criteria for a point to be on the boundary of the CmpRegion depend + on the boolean operator being used. If component regions A and B are + ANDed together, then a point is on the boundary of the CmpRegion if + either 1) it is on the boundary of A and inside B, or 2) it is on the + boundary of B and inside A. If the component regions are ORed together, + then a point is on the boundary of the CmpRegion if either 1) it is on + the boundary of A and outside B, or 2) it is on the boundary of B and + outside A. Either we need to transform the supplied PointSet using each + component Region as a Mapping. But if using OR we temporarily negate + the Regions. */ + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + pset1 = astTransform( reg1, pset, 1, NULL ); + pset2 = astTransform( reg2, pset, 1, NULL ); + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* Get pointers to the axis values in these PointSets */ + ptr1 = astGetPoints( pset1 ); + ptr2 = astGetPoints( pset2 ); + +/* If required, create an output mask array */ + np = astGetNpoint( pset ); + if( mask ) *mask = astMalloc( sizeof(int)*(size_t) np ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* We can use the values for the first axis to indicate if a point is + inside or outside a Region. So store pointers to the first axis arrays. */ + p1 = ptr1[ 0 ]; + p2 = ptr2[ 0 ]; + +/* Assume all points are on the boundary of the CmpRegion. */ + result = 1; + +/* If we are creating an output mask, we must check every point. Otherwise + we can stop checking when we find the first point which is not on the + boundary of the CmpRegion. */ + if( mask ) { + + for( ip = 0; ip < np; ip++ ) { + if( ( mask1[ ip ] && p2[ ip ] != AST__BAD ) || + ( mask2[ ip ] && p1[ ip ] != AST__BAD ) ){ + (*mask)[ ip ] = 1; + } else { + (*mask)[ ip ] = 0; + result = 0; + } + } + + } else { + + for( ip = 0; ip < np; ip++ ) { + if( ( !mask1[ ip ] || p2[ ip ] == AST__BAD ) && + ( !mask2[ ip ] || p1[ ip ] == AST__BAD ) ){ + result = 0; + break; + } + } + } + } + +/* Free resources */ + mask1 = astFree( mask1 ); + mask2 = astFree( mask2 ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + psetb1 = astAnnul( psetb1 ); + psetb2 = astAnnul( psetb2 ); + if( unc1 ) unc1 = astAnnul( unc1 ); + if( unc2 ) unc2 = astAnnul( unc2 ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static void RegSetAttrib( AstRegion *this_region, const char *setting, + char **base_setting, int *status ) { +/* +* Name: +* RegSetAttrib + +* Purpose: +* Set an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void RegSetAttrib( AstRegion *this, const char *setting, +* char **base_setting, int *status ) + +* Class Membership: +* CmpRegion method (over-rides the astRegSetAttrib method inherited from +* the Region class). + +* Description: +* This function assigns an attribute value to both the base and +* current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* setting +* Pointer to a null terminated attribute setting string. NOTE, IT +* SHOULD BE ENTIRELY LOWER CASE. The supplied string will be +* interpreted using the public interpretation implemented by +* astSetAttrib. This can be different to the interpretation of the +* protected accessor functions. For instance, the public +* interpretation of an unqualified floating point value for the +* Epoch attribute is to interpet the value as a gregorian year, +* but the protected interpretation is to interpret the value as an +* MJD. +* base_setting +* Address of a location at which to return a pointer to the null +* terminated attribute setting string which was applied to the +* base Frame of the encapsulated FrameSet. This may differ from +* the supplied setting if the supplied setting contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; + char *bset; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Use the RegSetAttrib method inherited from the parent class to apply the + setting to the current and base Frames in the FrameSet encapsulated by the + parent Region structure. */ + (*parent_regsetattrib)( this_region, setting, &bset, status ); + +/* Now apply the base Frame setting to the component Regions (the current + Frame within the component Regions is equivalent to the base Frame in the + parent Region structure). Annul any "attribute unknown" error that results + from attempting to do this. */ + if( astOK ) { + rep = astReporting( 0 ); + astRegSetAttrib( this->region1, bset, NULL ); + astRegSetAttrib( this->region2, bset, NULL ); + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + } + +/* If required, return the base Frame setting string, otherwise free it. */ + if( base_setting ) { + *base_setting = bset; + } else { + bset = astFree( bset ); + } +} + +static AstRegion **RegSplit( AstRegion *this_region, int *nlist, int *status ){ +/* +*+ +* Name: +* RegSplit + +* Purpose: +* Split a Region into a list of disjoint component Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstRegion **astRegSplit( AstRegion *this, int *nlist ) + +* Class Membership: +* CmpRegion member function (overrides the astRegSplit method +* inherited from the parent Region class). + +* Description: +* This function splits the supplied Region into a set of disjoint +* component Regions. If the Region cannot be split, then the returned +* array contains only one pointer - a clone of the supplied Region +* pointer. + +* Parameters: +* this +* Pointer to the Region. +* nlist +* Pointer to an int in which to return the number of elements in +* the returned array. + +* Returned Value: +* Pointer to dynamically alloctaed memory holding an array of Region +* pointers. The length of this array is given by the value returned +* in "*nlist". The pointers in the returned array should be annulled +* using astAnnul when no longer needed, and the memory used to hold +* the array should be freed using astFree. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables; */ + AstCmpRegion *new; + AstCmpRegion *this; + AstFrame *frm; + AstFrameSet *fs; + AstMapping *map; + AstRegion **cmplist; + AstRegion **result; + AstRegion *cmpreg; + AstRegion *new_reg; + int icomp; + int ifirst; + int ilist; + int iw; + int jcomp; + int ncomp; + int nn; + int unbounded; + +/* Initialise. */ + result = NULL; + *nlist = 0; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Indicate we have not yet found any unbounded component regions. */ + unbounded = 0; + +/* Can only split non-inverted CmpRegions that combine their components + using the OR operator. */ + if( this->oper == AST__OR && !astGetNegated( this->region1 ) && + !astGetNegated( this->region2 ) ) { + +/* Process each of the two component Regions in turn. */ + for( icomp = 0; icomp < 2 && !unbounded; icomp++ ) { + cmpreg = icomp ? this->region2 : this->region1; + +/* Create a set of disjoint Regions that are equivalent to the current + component Region, and loop round them. */ + cmplist = astRegSplit( cmpreg, &ncomp ); + for( jcomp = 0; jcomp < ncomp; jcomp++ ) { + +/* If any of the components are unbounds, we cannot split the supplied + Region. */ + unbounded = unbounded || !astGetBounded( cmplist[ jcomp ] ); + if( ! unbounded ) { + +/* Initialise the index within the returned list of the first Region that + overlaps the current disjoint component Region. */ + ifirst = -1; + +/* Loop round all the Regions currently in the returned list. */ + for( ilist = 0; ilist < *nlist; ilist++ ) { + if( result[ ilist ] ) { + +/* See if the current disjoint component overlaps the current entry in + the returned list. */ + if( astOverlap( cmplist[ jcomp ], result[ ilist ] ) > 1 ) { + +/* If this is the first overlap found for the current disjoint component, + form a CmpRegion that combines the two overlapping Regions, and use it + to replace the current entry in the returned list. */ + if( ifirst == -1 ) { + new = astCmpRegion( cmplist[ jcomp ], result[ ilist ], + AST__OR, " ", status ); + (void) astAnnul( result[ ilist ] ); + result[ ilist ] = (AstRegion *) new; + +/* Note the index within the returned list of the first Region that overlaps + the current disjoint component Region. */ + ifirst = ilist; + +/* If this is the second or later overlap, add the overlapping returned Region + into the CmpRegion that it is stored at index "ifirsT" in the returned + list. */ + } else { + new = astCmpRegion( result[ ilist ], result[ ifirst ], + AST__OR, " ", status ); + result[ ilist ] = astAnnul( result[ ilist ] ); + (void) astAnnul( result[ ifirst ] ); + result[ ifirst ] = (AstRegion *) new; + } + } + } + } + +/* If the current disjoint component does not overlap any of the Regions + already in the returned list, append the current disjoint component to + the end of the returned list. */ + if( ifirst == -1 ) { + ilist = (*nlist)++; + result = astGrow( result, *nlist, sizeof( *result ) ); + if( astOK ) result[ ilist ] = astClone( cmplist[ jcomp ] ); + } + } + +/* Annul the pointer to the disjoint component Region. */ + cmplist[ jcomp ] = astAnnul( cmplist[ jcomp ] ); + } + +/* Free the mnemory holding the list of disjoint components. */ + cmplist = astFree( cmplist ); + } + } + +/* If any unbounded components were found, ensure the returned list is + empty. */ + if( unbounded && result ) { + for( ilist = 0; ilist < *nlist; ilist++ ) { + if( result[ ilist ] ) result[ ilist ] = astAnnul( result[ ilist ] ); + } + result = astFree( result ); + *nlist = 0; + +/* Otherwise, shuffle later entries down to fill any NULL slots in the returned + list. */ + } else if( result ){ + nn = *nlist; + iw = 0; + for( ilist = 0; ilist < nn; ilist++ ) { + if( result[ ilist ] ) result[ iw++ ] = result[ ilist ]; + } + *nlist = iw; + } + +/* If this CmpRegion cannot be split, the returned list just holds a + clone of the Region pointer. */ + if( !result ) { + result = astMalloc( sizeof( *result ) ); + if( astOK ) { + result[ 0 ] = astClone( this ); + *nlist = 1; + } + } + +/* Remap any returned Regions so that they are defined within the same + coordinate system as the supplied Region. */ + if( result && *nlist > 0 ) { + fs = this_region->frameset; + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + frm = astGetFrame( fs, AST__CURRENT ); + for( ilist = 0; ilist < *nlist; ilist++ ) { + new_reg = astMapRegion( result[ ilist ], map, frm ); + (void) astAnnul( result[ ilist ] ); + result[ ilist ] = new_reg; + } + map = astAnnul( map ); + frm = astAnnul( frm ); + } + +/* Free all returned pointers if an error has occurred. */ + if( !astOK && result ) { + for( ilist = 0; ilist < *nlist; ilist++ ) { + result[ ilist ] = astAnnul( result[ ilist ] ); + } + result = astFree( result ); + *nlist = 0; + } + +/* Return the result. */ + return result; +} + +static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr, + int *status ){ +/* +*+ +* Name: +* RegTrace + +* Purpose: +* Return requested positions on the boundary of a 2D Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int astRegTrace( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* CmpRegion member function (overrides the astRegTrace method +* inherited from the parent Region class). + +* Description: +* This function returns positions on the boundary of the supplied +* Region, if possible. The required positions are indicated by a +* supplied list of scalar parameter values in the range zero to one. +* Zero corresponds to some arbitrary starting point on the boundary, +* and one corresponds to the end (which for a closed region will be +* the same place as the start). + +* Parameters: +* this +* Pointer to the Region. +* n +* The number of positions to return. If this is zero, the function +* returns without action (but the returned function value still +* indicates if the method is supported or not). +* dist +* Pointer to an array of "n" scalar parameter values in the range +* 0 to 1.0. +* ptr +* A pointer to an array of pointers. The number of elements in +* this array should equal tthe number of axes in the Frame spanned +* by the Region. Each element of the array should be a pointer to +* an array of "n" doubles, in which to return the "n" values for +* the corresponding axis. The contents of the arrays are unchanged +* if the supplied Region belongs to a class that does not +* implement this method. + +* Returned Value: +* Non-zero if the astRegTrace method is implemented by the class +* of Region supplied, and zero if not. + +* Notes: +* - The current algorithm results in the boundary of the CmpRegion +* being dis-contiguous - supplied distance values from zero up to some +* mid-value correspond to positions on the first component Region, and +* higher distance (up to 1.0) correspond to points on the second +* component Region. + +*- +*/ + +/* Local Variables; */ + AstCmpRegion *this; + AstFrame *frm; + AstMapping *map; + AstPointSet *bpset; + AstPointSet *cpset; + AstRegion *ureg1; + AstRegion *ureg2; + double **bptr; + int i; + int j; + int ncur; + int result; + double *rval; + double *off; + double *r1d; + double *r2d; + double *r1ptr[ 2 ]; + double *r2ptr[ 2 ]; + double **r1ptrb; + double **r2ptrb; + double dbreak; + double dtot; + double x; + double x0; + int r1n; + int r2n; + AstPointSet *r1pset; + AstPointSet *r2pset; + AstPointSet *r1psetb; + AstPointSet *r2psetb; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( ! astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Check it is 2-dimensional. */ + result = 1; + if( astGetNaxes( frm ) != 2 ) result = 0; + +/* Check the component Regions can be traced. */ + if( !astRegTrace( this->region1, 0, NULL, NULL ) || + !astRegTrace( this->region1, 0, NULL, NULL ) ) result = 0; + +/* Check we have some points to find. */ + if( result && n > 0 ) { + +/* We first determine the required positions in the base Frame of the + Region, and then transform them into the current Frame. Get the + base->current Mapping, and the number of current Frame axes. */ + map = astGetMapping( this_region->frameset, AST__BASE, AST__CURRENT ); + +/* If it's a UnitMap we do not need to do the transformation, so put the + base Frame positions directly into the supplied arrays. */ + if( astIsAUnitMap( map ) ) { + bpset = NULL; + bptr = ptr; + ncur = 2; + +/* Otherwise, create a PointSet to hold the base Frame positions. */ + } else { + bpset = astPointSet( n, 2, " ", status ); + bptr = astGetPoints( bpset ); + ncur = astGetNout( map ); + } + + r1d = astMalloc( sizeof( double )*n ); + r2d = astMalloc( sizeof( double )*n ); + +/* Ensure information about the breaks in the boundary of each component + region is available within the CmpRegion structure. These breaks are + the points at which the two boundaries cross. */ + SetBreakInfo( this, 0, status ); + SetBreakInfo( this, 1, status ); + +/* Get the constants needed to convert the supplied distances (normalised + so that the border of the entire CmpRegion has a length of 1.0), into + geodesic distances around the border of each component Region. */ + dtot = this->d0[ 0 ] + this->d0[ 1 ]; + dbreak = this->d0[ 0 ]/dtot; + +/* Initialise here to avoid compiler warnings. */ + r1n = 0; + r2n = 0; + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Loop round all supplied distances, determining if they represent a + position on the first or second component Region. */ + for( i = 0; i < n; i++ ) { + +/* If the current distance represents a point in the second component + Region... */ + if( dist[ i ] > dbreak ) { + +/* Find the correspond distance around the used sections of the second + component region (normalised so that the entire border of the + component region has a length of "this->d0[1]"). */ + x0 = ( dist[ i ] - dbreak )*dtot; + x = x0; + +/* Convert this into the correspond distance around the entire border of + the second component region (normalised so that the entire border of the + component region has unit length). */ + rval = this->rvals[ 1 ]; + off = this->offs[ 1 ]; + + for( j = 0; j < this->nbreak[ 1 ]; j++,rval++,off++ ) { + if( *rval >= x0 ) break; + x += *off; + } + +/* Store this as the next distance to move around the second component + Region, normalising it to the range 0 to 1 as required by astRegTrace. */ + r2d[ r2n++ ] = x/this->dtot[ 1 ]; + +/* Now we do the same if the current distance corresponds to a position + in the first component Region. */ + } else { + + x0 = dist[ i ]*dtot; + x = x0; + + rval = this->rvals[ 0 ]; + off = this->offs[ 0 ]; + + for( j = 0; j < this->nbreak[ 0 ]; j++,rval++,off++ ) { + if( *rval >= x0 ) break; + x += *off; + } + + r1d[ r1n++ ] = x/this->dtot[ 0 ]; + + } + + } + } + +/* Allocate memory to hold the axis values at the corresponding positions + in the first component Region. */ + r1ptr[ 0 ] = astMalloc( sizeof( double )*r1n ); + r1ptr[ 1 ] = astMalloc( sizeof( double )*r1n ); + +/* Allocate memory to hold the axis values at the corresponding positions + in the second component Region. */ + r2ptr[ 0 ] = astMalloc( sizeof( double )*r2n ); + r2ptr[ 1 ] = astMalloc( sizeof( double )*r2n ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Find the axis values at each of the required positions that fall in + the first component Region. Negate it first if needed to ensure the + Region is bounded (not guaranteed, but likely). */ + if( astGetBounded( this->region1 ) ) { + (void) astRegTrace( this->region1, r1n, r1d, r1ptr ); + } else { + AstRegion *negation = astGetNegation( this->region1 ); + (void) astRegTrace( negation, r1n, r1d, r1ptr ); + negation = astAnnul( negation ); + } + +/* Do the same for the second component Region. */ + if( astGetBounded( this->region2 ) ) { + (void) astRegTrace( this->region2, r2n, r2d, r2ptr ); + } else { + AstRegion *negation = astGetNegation( this->region2 ); + (void) astRegTrace( negation, r2n, r2d, r2ptr ); + negation = astAnnul( negation ); + } + +/* The arrays of positions returned by the above calls to astRegTrace may + include points that should not be there (e.g. points on the boundary + of one component region that should have been blanked due to being inside + the second component region - if the regions are ORed together). This + is a consequence of the relatively low value of the "NP" local constant + in function SetBreakInfo. So we now refine the positions to exclude + any such unwanted positions. + + If the two component Regions are ANDed together, we want to remove the + positions from the boundary of the required component Region that fall + outside the other region. We can do this by simply using the other Region + as a Mapping. If the two component Regions are ORed together, we want to + remove the position that fall within (rather than outside) the other + Region. To do this we need to negate the other region first. */ + if( this->oper == AST__OR ) { + ureg1 = astGetNegation( this->region1 ); + ureg2 = astGetNegation( this->region2 ); + } else { + ureg1 = astClone( this->region1 ); + ureg2 = astClone( this->region2 ); + } + +/* Now transform the points on the boundary of the first Region in order + to set invalid those positions which are not on the boundary of the + supplied CmpRegion. */ + if( r1n > 0 ) { + r1pset = astPointSet( r1n, 2, " ", status ); + astSetPoints( r1pset, r1ptr ); + r1psetb = astTransform( ureg2, r1pset, 1, NULL ); + r1ptrb = astGetPoints( r1psetb ); + } else { + r1pset = NULL; + r1psetb = NULL; + r1ptrb = NULL; + } + +/* Now transform the points on the boundary of the second Region in order + to set invalid those positions which are not on the boundary of the + supplied CmpRegion. */ + if( r2n > 0 ) { + r2pset = astPointSet( r2n, 2, " ", status ); + astSetPoints( r2pset, r2ptr ); + r2psetb = astTransform( ureg1, r2pset, 1, NULL ); + r2ptrb = astGetPoints( r2psetb ); + } else { + r2pset = NULL; + r2psetb = NULL; + r2ptrb = NULL; + } + +/* Free the begation pointers. */ + ureg1 = astAnnul( ureg1 ); + ureg2 = astAnnul( ureg2 ); + +/* Check pointer can be used safely. */ + if( astOK ) { + +/* Copy the boundary positions from each component Region into a single + PointSet. These positions are in the base Frame of the CmpRegion. */ + r1n = 0; + r2n = 0; + for( i = 0; i < n; i++ ) { + if( dist[ i ] > dbreak ) { + bptr[ 0 ][ i ] = r2ptrb[ 0 ][ r2n ]; + bptr[ 1 ][ i ] = r2ptrb[ 1 ][ r2n++ ]; + } else { + bptr[ 0 ][ i ] = r1ptrb[ 0 ][ r1n ]; + bptr[ 1 ][ i ] = r1ptrb[ 1 ][ r1n++ ]; + } + } + + } + +/* Free resources. */ + if( r1pset ) r1pset = astAnnul( r1pset ); + if( r2pset ) r2pset = astAnnul( r2pset ); + if( r1psetb ) r1psetb = astAnnul( r1psetb ); + if( r2psetb ) r2psetb = astAnnul( r2psetb ); + + } + +/* If required, transform the base frame positions into the current + Frame of the CmpRegion, storing them in the supplied array. Then + free resources. */ + if( bpset ) { + cpset = astPointSet( n, ncur, " ", status ); + astSetPoints( cpset, ptr ); + + (void) astTransform( map, bpset, 1, cpset ); + + cpset = astAnnul( cpset ); + bpset = astAnnul( bpset ); + } + +/* Free remaining resources. */ + map = astAnnul( map ); + } + frm = astAnnul( frm ); + +/* Return the result. */ + return result; +} + +static void RegClearAttrib( AstRegion *this_region, const char *attrib, + char **base_attrib, int *status ) { +/* +* Name: +* RegClearAttrib + +* Purpose: +* Clear an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void RegClearAttrib( AstRegion *this, const char *attrib, +* char **base_attrib, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegClearAttrib method +* inherited from the Region class). + +* Description: +* This function clears the value of a named attribute in both the base +* and current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* attrib +* Pointer to a null terminated string holding the attribute name. +* NOTE, IT SHOULD BE ENTIRELY LOWER CASE. +* base_attrib +* Address of a location at which to return a pointer to the null +* terminated string holding the attribute name which was cleared in +* the base Frame of the encapsulated FrameSet. This may differ from +* the supplied attribute if the supplied attribute contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; + char *batt; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Use the RegClearAttrib method inherited from the parent class to clear the + attribute in the current and base Frames in the FrameSet encapsulated by + the parent Region structure. */ + (*parent_regclearattrib)( this_region, attrib, &batt, status ); + +/* Now clear the base Frame attribute to the component Regions (the current + Frame within the component Regions is equivalent to the base Frame in the + parent Region structure). Annul any "attribute unknown" error that results + from attempting to do this. */ + if( astOK ) { + rep = astReporting( 0 ); + astRegClearAttrib( this->region1, batt, NULL ); + astRegClearAttrib( this->region2, batt, NULL ); + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + } + +/* If required, return the base Frame attribute name, otherwise free it. */ + if( base_attrib ) { + *base_attrib = batt; + } else { + batt = astFree( batt ); + } +} + +static void ResetCache( AstRegion *this_region, int *status ){ +/* +* Name: +* ResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void ResetCache( AstRegion *this, int *status ) + +* Class Membership: +* Region member function (overrides the astResetCache method +* inherited from the parent Region class). + +* Description: +* This function clears cached information from the supplied Region +* structure. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables *: */ + AstCmpRegion *this; + int i; + +/* Check a Region was supplied. */ + if( this_region ) { + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Clear information cached in the CmpRegion structure. */ + for( i = 0; i < 2; i++ ) { + this->rvals[ i ] = astFree( this->rvals[ i ] ); + this->offs[ i ] = astFree( this->offs[ i ] ); + this->nbreak[ i ] = 0; + this->d0[ i ] = AST__BAD; + this->dtot[ i ] = AST__BAD; + } + + this->bounded = -INT_MAX; + +/* Clear information cached in the component regions. */ + if( this->region1 ) astResetCache( this->region1 ); + if( this->region2 ) astResetCache( this->region2 ); + +/* Clear information cached in the parent Region structure. */ + (*parent_resetcache)( this_region, status ); + } +} + +static void SetBreakInfo( AstCmpRegion *this, int comp, int *status ){ +/* +* Name: +* SetBreakInfo + +* Purpose: +* Ensure that a CmpRegion has information about the breaks in the +* boundaries of one of the two component Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void SetBreakInfo( AstCmpRegion *this, int comp, int *status ) + +* Class Membership: +* CmpRegion method. + +* Description: +* This function returns without action if the supplied CmpRegion +* already contains break information for the specified component Region. +* Otherwise, it creates the required information and stores it in the +* CmpRegion. +* +* Each component Region in the CmpRegion has a boundary. But in +* general only part of the boundary of a component Region will also +* be included in the CmpRegion boundary. Thus the component Region +* boundary can be broken up into sections; sections that form part +* of the CmpRegion boundary, and sections that do not. This function +* stores information about the breaks between these sections. +* +* The complete boundary of a component Region is parameterised by a +* geodesic distance that goes from 0.0 to the value found by this +* function and stored in this->dtot (the total geodesic distance +* around the border). This function find the ranges of this parameter +* that correspond to the sections of the boundary that are also on the +* CmpRegion boundary, and thus finds the total length that the component +* boundary contributes to the CmpRegion boundary. This length is stored +* in "this->d0" (a two element array, one for each component Region). +* +* It also find two arrays "this->rvals" and "this->offs" that allow a +* distance value in the range 0.0 to "this->d0" (i.e. a distance +* measured by skipping over the parts of the component boundary that +* are not on the CmpRegion boundary), to be converted into the +* corresponding distance value in the range 0.0 to "this->dtot" (i.e. a +* distance measured round the complete component boundary, including the +* parts not on the CmpRegion boundary). + +* Parameters: +* this +* Pointer to a CmpRegion. +* comp +* Zero or one, indicating which component Region is to be checked. +* status +* Pointer to the inherited status variable. + +*/ + +/* The number of points to be spread evenly over the entire boundary of the + component Region. */ +#define NP 101 + +/* Local Variables: */ + AstFrame *frm; + AstPointSet *pset1; + AstPointSet *pset2; + AstRegion *other; + AstRegion *reg; + AstRegion *uother; + double **ptr2; + double **ptr1; + double *d; + double *offs; + double *p0; + double *p1; + double *p; + double *q; + double *rvals; + double delta; + double dist; + double pnt1[ 2 ]; + double pnt2[ 2 ]; + double rbad; + double rval; + double totdist; + int i; + int j; + int nn; + int prevgood; + +/* Check inherited status */ + if( !astOK ) return; + +/* If the information describing breaks in the component boundary has not + yet been set up, do so now. */ + if( this->d0[ comp ] == AST__BAD ) { + +/* Get a pointer to the component Region for which break information is + required. */ + reg = comp ? this->region2 : this->region1; + +/* Check the component class implements the astRegTrace method. */ + if( astRegTrace( reg, 0, NULL, NULL ) ) { + +/* Create a pointSet to hold axis values at evenly spaced positions along + the entire boundary of the selected component region. */ + pset1 = astPointSet( NP, 2, " ", status ); + ptr1 = astGetPoints( pset1 ); + +/* Allocate memory to hold an array of corresponding scalar distances around + the boundary. */ + d = astMalloc( NP*sizeof( double ) ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Get the distance increment between points (at this point the distances + are normalised so that the entire boundary has unit length, as + required by astRegTrace). */ + delta = 1.0/( NP - 1 ); + +/* Set up the array of evenly spaced distances around the boundary of the + component region. */ + for( i = 0; i < NP; i++ ) d[ i ] = i*delta; + +/* Get the corresponding Frame positions. If the Region is unbounded + (e.g. a negated circle, etc), then negate it first in the hope that + this may produced a bounded Region. */ + if( astGetBounded( reg ) ) { + (void) astRegTrace( reg, NP, d, ptr1 ); + } else { + AstRegion *negation = astGetNegation( reg ); + (void) astRegTrace( negation, NP, d, ptr1 ); + negation = astAnnul( negation ); + } + +/* Get a pointer to the other component Region. */ + other = comp ? this->region1 : this->region2; + +/* If the two component Regions are ANDed together, we want to remove the + positions from the boundary of the required component Region that fall + outside the other region. We can do this by simply using the other Region + as a Mapping. If the two component Regions are ORed together, we want to + remove the position that fall within (rather than outside) the other + Region. To do this we need to negate the other region first. */ + if( this->oper == AST__OR ) { + uother = astGetNegation( other ); + } else { + uother = astClone( other ); + } + +/* Now transform the points on the boundary of the selected Region in + order to set invalid those positions which are not on the boundary of + the supplied CmpRegion. */ + pset2 = astTransform( uother, pset1, 1, NULL ); + +/* Annul the negation pointer */ + uother = astAnnul( uother ); + +/* Modify the distance array by setting invalid each element that is not + on the boundary of the CmpRegion. */ + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + p = ptr2[ 0 ]; + q = ptr2[ 1 ]; + for( i = 0; i < NP; i++,p++,q++ ) { + if( *p == AST__BAD || *q == AST__BAD ) d[ i ] = AST__BAD; + } + +/* At each good/bad junction in this list, extend the good section by one + point. This ensures that the good sections of the curve do in fact + touch each other (they may in fact overlap a little but that does not + matter). */ + prevgood = ( d[ 0 ] != AST__BAD ); + for( i = 1; i < NP; i++,p++,q++ ) { + if( d[ i ] == AST__BAD ) { + if( prevgood ) d[ i ] = i*delta; + prevgood = 0; + + } else { + if( !prevgood ) d[ i - 1 ] = ( i - 1 )*delta; + prevgood = 1; + } + } + +/* Find the total geodesic distance around the border. This is only an + approximation but it is only used to give a relative weight to this + component within the CmpFrame, and so does not need to be very accurate. */ + frm = astGetFrame( reg->frameset, AST__CURRENT ); + p0 = ptr1[ 0 ]; + p1 = ptr1[ 1 ]; + totdist = 0; + pnt1[ 0 ] = *(p0++); + pnt1[ 1 ] = *(p1++); + for( i = 1; i < NP; i++ ) { + pnt2[ 0 ] = *(p0++); + pnt2[ 1 ] = *(p1++); + dist = astDistance( frm, pnt1, pnt2 ); + if( dist != AST__BAD ) totdist += dist; + pnt1[ 0 ] = pnt2[ 0 ]; + pnt1[ 1 ] = pnt2[ 1 ]; + } + +/* Change delta so that it represents a geodesic distance, rather than a + normalised distance in the range zero to one. Working in geodesic distance + (e.g. Radians on a SkyFrame) prevents Regions higher up in a complex nested + CmpRegion being given higher priority than a lower Region. */ + delta *= totdist; + +/* Now create two arrays - "rvals" holds the distance travelled around + the used parts of the border at which breaks occur, "offs" holds the jump + in distance around the complete border at each break. The distance + around the complete border is normalised to the range [0.0,1.0]. + Therefore the total distance around the used parts of the border will in + general be less than 1.0 */ + if( d[ 0 ] == AST__BAD ) { + nn = 1; + j = 0; + rvals = astMalloc( sizeof( double ) ); + offs = astMalloc( sizeof( double ) ); + if( astOK ) rvals[ 0 ] = -0.5*delta; + rbad = 0.5; + prevgood = 0; + rval = -0.5*delta; + + } else { + nn = 0; + rvals = NULL; + offs = NULL; + prevgood = 1; + rbad = 0.0; + rval = 0.0; + } + + for( i = 1; i < NP; i++,p++,q++ ) { + + if( d[ i ] == AST__BAD ) { + if( prevgood ) { + j = nn++; + rvals = astGrow( rvals, nn, sizeof( double ) ); + offs = astGrow( offs, nn, sizeof( double ) ); + if( astOK ) { + rvals[ j ] = rval + 0.5*delta; + rbad = 0.0; + } else { + break; + } + prevgood = 0; + } + + rbad += 1.0; + + } else { + if( !prevgood ) { + offs[ j ] = rbad*delta; + prevgood = 1; + } + rval += delta; + } + } + + if( !prevgood ) { + rval += 0.5*delta; + offs[ j ] = rbad*delta; + } + +/* Record the information in the CmpRegion structure. */ + this->rvals[ comp ] = rvals; + this->offs[ comp ] = offs; + this->nbreak[ comp ] = nn; + this->d0[ comp ] = rval; + this->dtot[ comp ] = totdist; + } + +/* Free resources. */ + pset2 = astAnnul( pset2 ); + } + + pset1 = astAnnul( pset1 ); + d = astFree( d ); + + } + } +} + +#undef NP + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* CmpRegion method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstRegion *creg; /* Pointer to component Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* If either component Region has a dummy FrameSet use this method + recursively to give them the same FrameSet. */ + creg = ((AstCmpRegion *) this_region )->region1; + if( creg && !astGetRegionFS( creg ) ) astSetRegFS( creg, frm ); + + creg = ((AstCmpRegion *) this_region )->region2; + if( creg && !astGetRegionFS( creg ) ) astSetRegFS( creg, frm ); + +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* CmpRegion method (over-rides the astSimplify method inherited from +* the Region class). + +* Description: +* This function simplifies a CmpRegion to eliminate redundant +* computational steps, or to merge separate steps which can be +* performed more efficiently in a single operation. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new pointer to the (possibly simplified) Region. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. + +* Deficiencies: +* - Currently, this function does not attempt to map the component +* Regions into the current Frame of the parent Region structure. +* Both components should be mapped into the current Frame, and if the +* resulting base->current Mappings in *both* remapped component Regions are +* UnitMaps, then a new CmpRegion should be created from the re-mapped +* Regions. +*/ + +/* Local Variables: */ + AstCmpRegion *newb; /* New CmpRegion defined in base Frame */ + AstCmpRegion *newc; /* New CmpRegion defined in current Frame */ + AstFrame *frm; /* Current Frame */ + AstMapping *map; /* Base->current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstRegion *csreg1; /* Copy of simplified first component Region */ + AstRegion *csreg2; /* Copy of simplified second component Region */ + AstRegion *nullreg; /* Null or infinfite Region */ + AstRegion *othereg; /* Non-Null and non-infinfite Region */ + AstRegion *reg1; /* First component Region */ + AstRegion *reg2; /* Second component Region */ + AstRegion *sreg1; /* Simplified first component Region */ + AstRegion *sreg2; /* Simplified second component Region */ + int neg1; /* Negated flag to use with first component */ + int neg2; /* Negated flag to use with second component */ + int oper; /* Boolean operator used to combine components */ + int overlap; /* Nature of overlap between components */ + int simpler; /* Has any simplification taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. The + returned pointer identifies a region within the current Frame of the + FrameSet encapsulated by the parent Region structure. Note this by + storing the pointer in the "newc" ("c" for "current") variable. */ + newc = (AstCmpRegion *) (*parent_simplify)( this_mapping, status ); + +/* Note if any simplification took place. This is assumed to be the case + if the pointer returned by the above call is different to the supplied + pointer. */ + simpler = ( (AstMapping *) newc != this_mapping ); + +/* Below we may create a new simplified region which identifies a region + within the base Frame of the FrameSet encapsulated by the parent Region + structure. Such a result will need to be mapped into the current Frame + before being returned. The "newb" variable ("b" for "base") will be + used to store a pointer to such a result. Initialise this variable to + indicate that we do not yet have a base Frame result. */ + newb = NULL; + +/* Get the component Regions, how they should be combined, and the + Negated values which should be used with them. The returned values + take account of whether the supplied CmpRegion has itself been Negated + or not. The returned Regions represent regions within the base Frame + of the FrameSet encapsulated by the parent Region structure. */ + GetRegions( newc, ®1, ®2, &oper, &neg1, &neg2, status ); + +/* If the first component Region does not have the required value for + its "Negated" attribute, use the negation of "reg1" in place of "reg1" + itself. */ + if( neg1 != astGetNegated( reg1 ) ) { + AstRegion *tmp = astGetNegation( reg1 ); + (void) astAnnul( reg1 ); + reg1 = tmp; + } + +/* If the second component Region does not have the required value for + its "Negated" attribute, use the negation of "reg2" in place of "reg2" + itself. */ + if( neg2 != astGetNegated( reg2 ) ) { + AstRegion *tmp = astGetNegation( reg2 ); + (void) astAnnul( reg2 ); + reg2 = tmp; + } + +/* Simplify each of the two components. */ + sreg1 = astSimplify( reg1 ); + sreg2 = astSimplify( reg2 ); + +/* Note if any simplification took place. */ + simpler = simpler || ( sreg1 != reg1 || sreg2 != reg2 ); + +/* If either component is null or infinite we can exclude it from the + returned Region. */ + if( astIsANullRegion( sreg1 ) || astIsANullRegion( sreg2 ) ) { + +/* Get a pointer to the non-null Region. The following is still valid + even if both regions are null or infinite. */ + if( astIsANullRegion( sreg1 ) ){ + nullreg = sreg1; + othereg = sreg2; + } else { + nullreg = sreg2; + othereg = sreg1; + } + +/* If null.. */ + if( !astGetNegated( nullreg ) ){ + if( oper == AST__AND ) { + newb = (AstCmpRegion *) astNullRegion( othereg, + astGetUnc( othereg, 0 ), "", status ); + + } else if( oper == AST__OR ) { + newb = astCopy( othereg ); + + } else { + astError( AST__INTER, "astSimplify(%s): The %s refers to an " + "unknown boolean operator with identifier %d (internal " + "AST programming error).", status, astGetClass( newc ), + astGetClass( newc ), oper ); + } + +/* If infinite.. */ + } else { + if( oper == AST__AND ) { + newb = astCopy( othereg ); + + } else if( oper == AST__OR ) { + newb = (AstCmpRegion *) astNullRegion( othereg, + astGetUnc( othereg, 0 ), "negated=1", status ); + + } else { + astError( AST__INTER, "astSimplify(%s): The %s refers to an " + "unknown boolean operator with identifier %d (internal " + "AST programming error).", status, astGetClass( newc ), + astGetClass( newc ), oper ); + } + } + +/* Flag that we have done some simplication.*/ + simpler = 1; + +/* If neither component is null or infinite, see if it is possible to + remove one or both of the components on the basis of the overlap + between them. */ + } else { + overlap = astOverlap( sreg1, sreg2 ); + +/* If the components have no overlap, and they are combined using AND, then + the CmpRegion is null. */ + if( ( overlap == 1 || overlap == 6 ) && oper == AST__AND ) { + newb = (AstCmpRegion *) astNullRegion( sreg1, astGetUnc( sreg1, 0 ), + "", status ); + simpler = 1; + +/* If one component is the negation of the other component, and they are + combined using OR, then the CmpRegion is infinite. This is represented + by a negated null region.*/ + } else if( overlap == 6 && oper == AST__OR ) { + newb = (AstCmpRegion *) astNullRegion( sreg1, astGetUnc( sreg1, 0 ), + "negated=1", status ); + simpler = 1; + +/* If the two components are identical... */ + } else if( overlap == 5 ) { + simpler = 1; + +/* If combined with AND or OR, the CmpRegion can be replaced by the first + (or second) component Region. */ + if( oper == AST__AND || oper == AST__OR ) { + newb = astCopy( sreg1 ); + } else { + astError( AST__INTER, "astSimplify(%s): The %s refers to an " + "unknown boolean operator with identifier %d (internal " + "AST programming error).", status, astGetClass( newc ), + astGetClass( newc ), oper ); + } + +/* If the first component is entirely contained within the second + component, and they are combined using AND or OR, then the CmpRegion + can be replaced by the first or second component. */ + } else if( overlap == 2 && ( oper == AST__AND || oper == AST__OR ) ){ + newb = astCopy( ( oper == AST__AND ) ? sreg1 : sreg2 ); + simpler = 1; + +/* If the second component is entirely contained within the first + component, and they are combined using AND or OR, then the CmpRegion + can be replaced by the second or first component. */ + } else if( overlap == 3 && ( oper == AST__AND || oper == AST__OR ) ){ + newb = astCopy( ( oper == AST__AND ) ? sreg2 : sreg1 ); + simpler = 1; + +/* Otherwise, no further simplication is possible, so either create a new + CmpRegion or leave the "newb" pointer NULL (which will cause "newc" to + be used), depending on whether the components were simplified. */ + } else if( simpler ){ + csreg1 = astCopy( sreg1 ); + csreg2 = astCopy( sreg2 ); + newb = astCmpRegion( csreg1, csreg2, oper, "", status ); + csreg1 = astAnnul( csreg1 ); + csreg2 = astAnnul( csreg2 ); + + } + } + +/* If any simplification took place, decide whether to use the "newc" or + "newb" pointer for the returned Mapping. If "newb" is non-NULL we use + it, otherwise we use "newc". If "newb" is used we must first map the + result Region from the base Frame of the FrameSet encapsulated + by the parent Region structure, to the current Frame. */ + if( simpler ) { + if( newb ){ + frm = astGetFrame( ((AstRegion *) newc)->frameset, AST__CURRENT ); + map = astGetMapping( ((AstRegion *) newc)->frameset, AST__BASE, AST__CURRENT ); + result = astMapRegion( newb, map, frm ); + frm = astAnnul( frm ); + map = astAnnul( map ); + newb = astAnnul( newb ); + } else { + result = astClone( newc ); + } + +/* If no simplification took place, return a clone of the supplied pointer. */ + } else { + result = astClone( this_mapping ); + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + sreg1 = astAnnul( sreg1 ); + sreg2 = astAnnul( sreg2 ); + newc = astAnnul( newc ); + +/* If an error occurred, annul the returned Mapping. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a CmpRegion to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astTransform method inherited +* from the Region class). + +* Description: +* This function takes a CmpRegion and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Region. +* This implies applying each of the CmpRegion's component Regions in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the CmpRegion. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the CmpRegion being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ + AstPointSet *ps1; /* Pointer to PointSet for first component */ + AstPointSet *ps2; /* Pointer to PointSet for second component */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **ptr1; /* Pointer to first component axis values */ + double **ptr2; /* Pointer to second component axis values */ + double **ptr_out; /* Pointer to output coordinate data */ + int coord; /* Zero-based index for coordinates */ + int good; /* Is the point inside the CmpRegion? */ + int ncoord_out; /* No. of coordinates per output point */ + int ncoord_tmp; /* No. of coordinates per base Frame point */ + int neg1; /* Negated value for first component Region */ + int neg2; /* Negated value for second component Region */ + int npoint; /* No. of points */ + int oper; /* Boolean operator to use */ + int point; /* Loop counter for points */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a Pointer to the CmpRegion structure */ + this = (AstCmpRegion *) this_mapping; + +/* Get the component Regions, how they should be combined, and the + Negated values which should be used with them. The returned values + take account of whether the supplied CmpRegion has itself been Negated + or not. The returned Regions represent regions within the base Frame + of the FrameSet encapsulated by the parent Region structure. */ + GetRegions( this, ®1, ®2, &oper, &neg1, &neg2, status ); + +/* If the first component Region does not have the required value for + its "Negated" attribute, use the negation of "reg1" in place of "reg1" + itself. */ + if( neg1 != astGetNegated( reg1 ) ) { + AstRegion *tmp = astGetNegation( reg1 ); + (void) astAnnul( reg1 ); + reg1 = tmp; + } + +/* If the second component Region does not have the required value for + its "Negated" attribute, use the negation of "reg2" in place of "reg2" + itself. */ + if( neg2 != astGetNegated( reg2 ) ) { + AstRegion *tmp = astGetNegation( reg2 ); + (void) astAnnul( reg2 ); + reg2 = tmp; + } + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, containing + a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet in the parent Region structure to + transform the supplied positions from the current Frame in the + encapsulated FrameSet (the Frame represented by the CmpRegion), to the + base Frame (the Frame in which the component Regions are defined). Note, + the returned pointer may be a clone of the "in" pointer, and so we + must be carefull not to modify the contents of the returned PointSet. */ + pset_tmp = astRegTransform( this, in, 0, NULL, NULL ); + +/* Now transform this PointSet using each of the two component Regions in + turn. */ + ps1 = astTransform( reg1, pset_tmp, 0, NULL ); + ps2 = astTransform( reg2, pset_tmp, 0, NULL ); + +/* Determine the numbers of points and coordinates per point for these base + Frame PointSets and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( pset_tmp ); + ncoord_tmp = astGetNcoord( pset_tmp ); + ptr1 = astGetPoints( ps1 ); + ptr2 = astGetPoints( ps2 ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* First deal with ANDed Regions */ + if( oper == AST__AND ) { + for ( point = 0; point < npoint; point++ ) { + good = 0; + + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + if( ptr1[ coord ][ point ] != AST__BAD && + ptr2[ coord ][ point ] != AST__BAD ) { + good = 1; + break; + } + } + + if( !good ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + +/* Now deal with ORed Regions */ + } else if( oper == AST__OR ) { + for ( point = 0; point < npoint; point++ ) { + good = 0; + + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + if( ptr1[ coord ][ point ] != AST__BAD || + ptr2[ coord ][ point ] != AST__BAD ) { + good = 1; + break; + } + } + + if( !good ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + +/* Report error for any unknown operator. */ + } else if( astOK ) { + astError( AST__INTER, "astTransform(%s): The %s refers to an unknown " + "boolean operator with identifier %d (internal AST " + "programming error).", status, astGetClass( this ), + astGetClass( this ), oper ); + } + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + pset_tmp = astAnnul( pset_tmp ); + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void XORCheck( AstCmpRegion *this, int *status ) { +/* +* Name: +* XORCheck + +* Purpose: +* Check if the supplied CmpRegion represents an XOR operation. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void XORCheck( AstCmpRegion *this, int *status ) + +* Class Membership: +* CmpRegion method + +* Decription: +* This function analyses the component Regions within the supplied +* CmpRegion to see if the CmpRegion is equivalent to an XOR operation +* on two other Regions. If it is, teh Regions that are XORed are +* stored in the supplied CmpRegion. + +* Parameters: +* this +* Pointer to the CmpRegion. + +*/ + +/* Local Variables: */ + AstCmpRegion *cmpreg1; + AstCmpRegion *cmpreg2; + int xor; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If the CmpRegion is already known to be an XOR operation, return + without action. */ + if( this->xor1 ) return; + +/* To be equivalent to an XOR operation, the supplied CmpRegion must be an + OR operation and each component Region must be a CmpRegion. */ + if( this->oper == AST__OR && astIsACmpRegion( this->region1 ) + && astIsACmpRegion( this->region2 ) ) { + cmpreg1 = (AstCmpRegion *) this->region1; + cmpreg2 = (AstCmpRegion *) this->region2; + +/* Each component CmpRegion must be an AND operation. */ + if( cmpreg1->oper == AST__AND && cmpreg2->oper == AST__AND ) { + +/* Temporarily negate the first component of the first CmpRegion. */ + astNegate( cmpreg1->region1 ); + +/* Initially, assume the supplied CmpRegion is not equivalent to an XOR + operation. */ + xor = 0; + +/* This negated region must be equal to one of the two component Regions + in the second component CmpRegion. Check the first. */ + if( astEqual( cmpreg1->region1, cmpreg2->region1 ) ) { + +/* We now check that the other two Regions are equal (after negating the + first). If so, set "xor" non-zero. */ + astNegate( cmpreg1->region2 ); + if( astEqual( cmpreg1->region2, cmpreg2->region2 ) ) xor = 1; + astNegate( cmpreg1->region2 ); + +/* Do equiovalent checks the other way round. */ + } else if( astEqual( cmpreg1->region1, cmpreg2->region2 ) ) { + astNegate( cmpreg1->region2 ); + if( astEqual( cmpreg1->region2, cmpreg2->region1 ) ) xor = 1; + astNegate( cmpreg1->region2 ); + } + +/* Re-instate the original state of the Negated attribute in the first + component of the first CmpRegion. */ + astNegate( cmpreg1->region1 ); + +/* If the supplied CmpRegion is equivalent to an XOR operation, store + copies of the components in the supplied CmpRegion. */ + if( xor ) { + this->xor1 = astCopy( cmpreg1->region1 ); + this->xor2 = astCopy( cmpreg1->region2 ); + +/* We need to negate one of these two Region (it doesn't matter which), + and we choose to negate which ever of them is already negated (so that + it becomes un-negated). */ + if( astGetNegated( this->xor1 ) ) { + astNegate( this->xor1 ); + } else { + astNegate( this->xor2 ); + } + } + } + } +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for CmpRegion objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for CmpRegion objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Regions within the CmpRegion. +*/ + +/* Local Variables: */ + AstCmpRegion *in; /* Pointer to input CmpRegion */ + AstCmpRegion *out; /* Pointer to output CmpRegion */ + int i; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output CmpRegions. */ + in = (AstCmpRegion *) objin; + out = (AstCmpRegion *) objout; + +/* For safety, start by clearing any memory references in the output + Region that were copied from the input Region. */ + out->region1 = NULL; + out->region2 = NULL; + out->xor1 = NULL; + out->xor2 = NULL; + + for( i = 0; i < 2; i++ ) { + out->rvals[ i ] = NULL; + out->offs[ i ] = NULL; + } + +/* Make copies of these Regions and store pointers to them in the output + CmpRegion structure. */ + out->region1 = astCopy( in->region1 ); + out->region2 = astCopy( in->region2 ); + if( in->xor1 ) out->xor1 = astCopy( in->xor1 ); + if( in->xor2 ) out->xor2 = astCopy( in->xor2 ); + +/* Copy cached arrays. */ + for( i = 0; i < 2; i++ ) { + out->rvals[ i ] = astStore( NULL, in->rvals[ i ], in->nbreak[ i ]*sizeof( **in->rvals ) ); + out->offs[ i ] = astStore( NULL, in->offs[ i ], in->nbreak[ i ]*sizeof( **in->offs ) ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for CmpRegion objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for CmpRegion objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion */ + int i; + +/* Obtain a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) obj; + +/* Free arrays holding cached information. */ + for( i = 0; i < 2; i++ ) { + this->rvals[ i ] = astFree( this->rvals[ i ] ); + this->offs[ i ] = astFree( this->offs[ i ] ); + } + +/* Annul the pointers to the component Regions. */ + this->region1 = astAnnul( this->region1 ); + this->region2 = astAnnul( this->region2 ); + if( this->xor1 ) this->xor1 = astAnnul( this->xor1 ); + if( this->xor2 ) this->xor2 = astAnnul( this->xor2 ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for CmpRegion objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the CmpRegion class to an output Channel. + +* Parameters: +* this +* Pointer to the CmpRegion whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstRegion *reg1; /* First Region to include in dump */ + AstRegion *reg2; /* Second Region to include in dump */ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ + const char *comment; /* Pointer to comment string */ + int ival; /* Integer value */ + int oper; /* The operator to include in the dump */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_object; + +/* Check if this CmpRegion has an equivalent XOR representation. Is so, + store details of the XOR representation in the CmpRegion. */ + XORCheck( this, status ); + +/* Choose the operator and component regions to include in the dump. If + the CmpRegion originally used an XOR operator, then save the XORed + regions. Otherwise, store the real component Regions. */ + if( this->xor1 ) { + oper = AST__XOR; + reg1 = this->xor1; + reg2 = this->xor2; + } else { + oper = this->oper; + reg1 = this->region1; + reg2 = this->region2; + } + +/* Write out values representing the instance variables for the CmpRegion + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Oper */ +/* ------- */ + ival = oper; + if( ival == AST__AND ) { + comment = "Regions combined using Boolean AND"; + } else if( ival == AST__OR ) { + comment = "Regions combined using Boolean OR"; + } else if( ival == AST__XOR ) { + comment = "Regions combined using Boolean XOR"; + } else { + comment = "Regions combined using unknown operator"; + } + astWriteInt( channel, "Operator", 1, 0, ival, comment ); + +/* First Region. */ +/* -------------- */ + astWriteObject( channel, "RegionA", 1, 1, reg1, + "First component Region" ); + +/* Second Region. */ +/* --------------- */ + astWriteObject( channel, "RegionB", 1, 1, reg2, + "Second component Region" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsACmpRegion and astCheckCmpRegion functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(CmpRegion,Region) +astMAKE_CHECK(CmpRegion) + +AstCmpRegion *astCmpRegion_( void *region1_void, void *region2_void, int oper, + const char *options, int *status, ...) { +/* +*+ +* Name: +* astCmpRegion + +* Purpose: +* Create a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* AstCmpRegion *astCmpRegion( AstRegion *region1, AstRegion *region2, +* int oper, const char *options, ..., int *status ) + +* Class Membership: +* CmpRegion constructor. + +* Description: +* This function creates a new CmpRegion and optionally initialises its +* attributes. + +* Parameters: +* region1 +* Pointer to the first Region. +* region2 +* Pointer to the second Region. +* oper +* The boolean operator with which to combine the two Regions. Either +* AST__AND or AST__OR. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new CmpRegion. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new CmpRegion. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic CmpRegion constructor which is +* available via the protected interface to the CmpRegion class. A +* public interface is provided by the astCmpRegionId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "region1" and "region2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpRegion *new; /* Pointer to new CmpRegion */ + AstRegion *region1; /* Pointer to first Region structure */ + AstRegion *region2; /* Pointer to second Region structure */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Region structures provided. */ + region1 = astCheckRegion( region1_void ); + region2 = astCheckRegion( region2_void ); + if ( astOK ) { + +/* Initialise the CmpRegion, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpRegion( NULL, sizeof( AstCmpRegion ), !class_init, + &class_vtab, "CmpRegion", region1, region2, + oper ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new CmpRegion's + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new CmpRegion. */ + return new; +} + +AstCmpRegion *astCmpRegionId_( void *region1_void, void *region2_void, + int oper, const char *options, ... ) { +/* +*++ +* Name: +c astCmpRegion +f AST_CMPREGION + +* Purpose: +* Create a CmpRegion. + +* Type: +* Public function. + +* Synopsis: +c #include "cmpregion.h" +c AstCmpRegion *astCmpRegion( AstRegion *region1, AstRegion *region2, +c int oper, const char *options, ... ) +f RESULT = AST_CMPREGION( REGION1, REGION2, OPER, OPTIONS, STATUS ) + +* Class Membership: +* CmpRegion constructor. + +* Description: +* This function creates a new CmpRegion and optionally initialises +* its attributes. +* +* A CmpRegion is a Region which allows two component +* Regions (of any class) to be combined to form a more complex +* Region. This combination may be performed a boolean AND, OR +* or XOR (exclusive OR) operator. If the AND operator is +* used, then a position is inside the CmpRegion only if it is +* inside both of its two component Regions. If the OR operator is +* used, then a position is inside the CmpRegion if it is inside +* either (or both) of its two component Regions. If the XOR operator +* is used, then a position is inside the CmpRegion if it is inside +* one but not both of its two component Regions. Other operators can +* be formed by negating one or both component Regions before using +* them to construct a new CmpRegion. +* +* The two component Region need not refer to the same coordinate +* Frame, but it must be possible for the +c astConvert +f AST_CONVERT +* function to determine a Mapping between them (an error will be +* reported otherwise when the CmpRegion is created). For instance, +* a CmpRegion may combine a Region defined within an ICRS SkyFrame +* with a Region defined within a Galactic SkyFrame. This is +* acceptable because the SkyFrame class knows how to convert between +* these two systems, and consequently the +c astConvert +f AST_CONVERT +* function will also be able to convert between them. In such cases, +* the second component Region will be mapped into the coordinate Frame +* of the first component Region, and the Frame represented by the +* CmpRegion as a whole will be the Frame of the first component Region. +* +* Since a CmpRegion is itself a Region, it can be used as a +* component in forming further CmpRegions. Regions of arbitrary +* complexity may be built from simple individual Regions in this +* way. + +* Parameters: +c region1 +f REGION1 = INTEGER (Given) +* Pointer to the first component Region. +c region2 +f REGION2 = INTEGER (Given) +* Pointer to the second component Region. This Region will be +* transformed into the coordinate Frame of the first region before +* use. An error will be reported if this is not possible. +c oper +f OPER = INTEGER (Given) +* The boolean operator with which to combine the two Regions. This +* must be one of the symbolic constants AST__AND, AST__OR or AST__XOR. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new CmpRegion. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new CmpRegion. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astCmpRegion() +f AST_CMPREGION = INTEGER +* A pointer to the new CmpRegion. + +* Notes: +* - If one of the supplied Regions has an associated uncertainty, +* that uncertainty will also be used for the returned CmpRegion. +* If both supplied Regions have associated uncertainties, the +* uncertainty associated with the first Region will be used for the +* returned CmpRegion. +* - Deep copies are taken of the supplied Regions. This means that +* any subsequent changes made to the component Regions using the +* supplied pointers will have no effect on the CmpRegion. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astCmpRegion constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astCmpRegion_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "region1" and "region2" parameters +* are of type (void *) and are converted from an ID value to a +* pointer and validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astCmpRegion_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpRegion *new; /* Pointer to new CmpRegion */ + AstRegion *region1; /* Pointer to first Region structure */ + AstRegion *region2; /* Pointer to second Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain the Region pointers from the ID's supplied and validate the + pointers to ensure they identify valid Regions. */ + region1 = astVerifyRegion( astMakePointer( region1_void ) ); + region2 = astVerifyRegion( astMakePointer( region2_void ) ); + if ( astOK ) { + +/* Initialise the CmpRegion, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpRegion( NULL, sizeof( AstCmpRegion ), !class_init, + &class_vtab, "CmpRegion", region1, region2, + oper ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new CmpRegion's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new CmpRegion. */ + return astMakeId( new ); +} + +AstCmpRegion *astInitCmpRegion_( void *mem, size_t size, int init, + AstCmpRegionVtab *vtab, const char *name, + AstRegion *region1, AstRegion *region2, + int oper, int *status ) { +/* +*+ +* Name: +* astInitCmpRegion + +* Purpose: +* Initialise a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* AstCmpRegion *astInitCmpRegion_( void *mem, size_t size, int init, +* AstCmpRegionVtab *vtab, const char *name, +* AstRegion *region1, AstRegion *region2, +* int oper ) + +* Class Membership: +* CmpRegion initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new CmpRegion object. It allocates memory (if necessary) to +* accommodate the CmpRegion plus any additional data associated with the +* derived class. It then initialises a CmpRegion structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a CmpRegion at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the CmpRegion is to be initialised. +* This must be of sufficient size to accommodate the CmpRegion data +* (sizeof(CmpRegion)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the CmpRegion (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* CmpRegion structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the CmpRegion's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new CmpRegion. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* region1 +* Pointer to the first Region. +* region2 +* Pointer to the second Region. +* oper +* The boolean operator to use. Must be one of AST__AND, AST__OR or +* AST__XOR. + +* Returned Value: +* A pointer to the new CmpRegion. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstCmpRegion *new; /* Pointer to new CmpRegion */ + AstFrame *frm; /* Frame encapsulated by first Region */ + AstFrameSet *fs; /* FrameSet connecting supplied Regions */ + AstMapping *map; /* Mapping between two supplied Regions */ + AstMapping *smap; /* Simplified Mapping between two supplied Regions */ + AstRegion *new_reg1; /* Replacement for first region */ + AstRegion *new_reg2; /* Replacement for second region */ + AstRegion *reg1; /* First Region to store in the CmpRegion */ + AstRegion *reg2; /* Second Region to store in the CmpRegion */ + AstRegion *xor1; /* Copy of first supplied Region or NULL */ + AstRegion *xor2; /* Copy of second supplied Region or NULL */ + int i; /* Loop count */ + int used_oper; /* The boolean operation actually used */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitCmpRegionVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the supplied oper value. */ + if( oper != AST__AND && oper != AST__OR && oper != AST__XOR && astOK ) { + astError( AST__INTRD, "astInitCmpRegion(%s): Illegal " + "boolean operator value (%d) supplied.", status, name, oper ); + } + +/* Take copies of the supplied Regions. */ + reg1 = astCopy( region1 ); + reg2 = astCopy( region2 ); + +/* Get the Mapping from the second to the first Region. */ + fs = astConvert( reg2, reg1, "" ); + +/* Report an error if not possible. */ + if( fs == NULL ) { + frm = NULL; + if( astOK ) astError( AST__INTRD, "astInitCmpRegion(%s): No Mapping can " + "be found between the two supplied Regions.", status, name ); + +/* Otherwise, map the second Region into the Frame of the first (unless + they are already in the same Frame). This results in both component + Frames having the same current Frame. This current Frame is used as the + encapsulated Frame within the parent Region structure. */ + } else { + frm = astGetFrame( fs, AST__CURRENT ); + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + smap = astSimplify( map ); + if( !astIsAUnitMap( smap ) ) { + new_reg2 = astMapRegion( reg2, smap, frm ); + (void) astAnnul( reg2 ); + reg2 = new_reg2; + } + smap = astAnnul( smap ); + map = astAnnul( map ); + fs = astAnnul( fs ); + } + +/* The CmpRegion class does not implement XOR directly (as it does for + AND and OR). Instead, when requested to create an XOR CmpRegion, it + creates a CmpRegion that uses AND and OR to simulate XOR. The top + level XOR CmpRegion actually uses AST__OR and the two component + regions within it are CmpRegions formed by combing the two supplied + Regions (one being negated first) using AND. Create the required + component Regions. */ + if( oper == AST__XOR ) { + astNegate( reg1 ); + new_reg1 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, " ", + status ); + astNegate( reg1 ); + + astNegate( reg2 ); + new_reg2 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, " ", + status ); + astNegate( reg2 ); + + xor1 = reg1; + xor2 = reg2; + + reg1 = new_reg1; + reg2 = new_reg2; + + used_oper = AST__OR; + +/* For AND and OR, use the supplied operator. */ + } else { + xor1 = NULL; + xor2 = NULL; + used_oper = oper; + } + +/* Initialise a Region structure (the parent class) as the first component + within the CmpRegion structure, allocating memory if necessary. A NULL + PointSet is suppled as the two component Regions will perform the function + of defining the Region shape. The base Frame of the FrameSet in the + parent Region structure will be the same as the current Frames of the + FrameSets in the two component Regions. */ + if ( astOK ) { + new = (AstCmpRegion *) astInitRegion( mem, size, 0, + (AstRegionVtab *) vtab, name, + frm, NULL, NULL ); + +/* Initialise the CmpRegion data. */ +/* --------------------------- */ +/* Store pointers to the component Regions. */ + new->region1 = astClone( reg1 ); + new->region2 = astClone( reg2 ); + +/* Note the operator used to combine the somponent Regions. */ + new->oper = used_oper; + +/* If we are creating an XOR CmpRegion, save copies of the supplied + Regions (i.e. the supplied Regions which are XORed). These will not + be the same as "reg1" and "reg2" since each of those two regions will + be CmpRegions that combine the supplied Regions using AST__AND. */ + if( oper == AST__XOR ) { + new->xor1 = xor1; + new->xor2 = xor2; + } else { + new->xor1 = NULL; + new->xor2 = NULL; + } + +/* Initialised cached values to show they have not yet been found. */ + for( i = 0; i < 2; i++ ) { + new->rvals[ i ] = NULL; + new->offs[ i ] = NULL; + new->nbreak[ i ] = 0; + new->d0[ i ] = AST__BAD; + new->dtot[ i ] = AST__BAD; + } + new->bounded = -INT_MAX; + +/* If the base->current Mapping in the FrameSet within each component Region + is a UnitMap, then the FrameSet does not need to be included in the + Dump of the new CmpRegion. Set the RegionFS attribute of the component + Region to zero to flag this. */ + map = astGetMapping( reg1->frameset, AST__BASE, AST__CURRENT ); + if( astIsAUnitMap( map ) ) astSetRegionFS( reg1, 0 ); + map = astAnnul( map ); + + map = astGetMapping( reg2->frameset, AST__BASE, AST__CURRENT ); + if( astIsAUnitMap( map ) ) astSetRegionFS( reg2, 0 ); + map = astAnnul( map ); + +/* Copy attribute values from the first component Region to the parent + Region. */ + if( astTestMeshSize( new->region1 ) ) { + astSetMeshSize( new, astGetMeshSize( new->region1 ) ); + } + if( astTestClosed( new->region1 ) ) { + astSetClosed( new, astGetClosed( new->region1 ) ); + } + +/* If an error occurred, clean up by annulling the Region pointers and + deleting the new object. */ + if ( !astOK ) { + new->region1 = astAnnul( new->region1 ); + new->region2 = astAnnul( new->region2 ); + new = astDelete( new ); + } + } + +/* Free resources */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + if( frm ) frm = astAnnul( frm ); + +/* Return a pointer to the new object. */ + return new; +} + +AstCmpRegion *astLoadCmpRegion_( void *mem, size_t size, + AstCmpRegionVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadCmpRegion + +* Purpose: +* Load a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* AstCmpRegion *astLoadCmpRegion( void *mem, size_t size, +* AstCmpRegionVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* CmpRegion loader. + +* Description: +* This function is provided to load a new CmpRegion using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* CmpRegion structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a CmpRegion at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the CmpRegion is to be +* loaded. This must be of sufficient size to accommodate the +* CmpRegion data (sizeof(CmpRegion)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the CmpRegion (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the CmpRegion structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstCmpRegion) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new CmpRegion. If this is NULL, a pointer to +* the (static) virtual function table for the CmpRegion class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "CmpRegion" is used instead. + +* Returned Value: +* A pointer to the new CmpRegion. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstCmpRegion *new; /* Pointer to the new CmpRegion */ + AstRegion *reg1; /* First Region read from dump */ + AstRegion *reg2; /* Second Region read from dump */ + AstFrame *f1; /* Base Frame in parent Region */ + AstRegion *creg; /* Pointer to component Region */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int i; /* Loop count */ + int oper; /* The operator to include in the dump */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this CmpRegion. In this case the + CmpRegion belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstCmpRegion ); + vtab = &class_vtab; + name = "CmpRegion"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitCmpRegionVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built CmpRegion. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "CmpRegion" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Operator */ +/* -------- */ + oper = astReadInt( channel, "operator", AST__AND ); + +/* First Region. */ +/* -------------- */ + reg1 = astReadObject( channel, "regiona", NULL ); + +/* Second Region. */ +/* --------------- */ + reg2 = astReadObject( channel, "regionb", NULL ); + +/* Initialised cached values to show they have not yet been found. */ + for( i = 0; i < 2; i++ ) { + new->rvals[ i ] = NULL; + new->offs[ i ] = NULL; + new->nbreak[ i ] = 0; + new->d0[ i ] = AST__BAD; + new->dtot[ i ] = AST__BAD; + } + new->bounded = -INT_MAX; + +/* The CmpRegion class does not implement XOR directly (as it does for + AND and OR). Instead, when requested to create an XOR CmpRegion, it + creates a CmpRegion that uses AND and OR to simulate XOR. The top + level XOR CmpRegion actually uses AST__OR and the two component + regions within it are CmpRegions formed by combing the two supplied + Regions (one being negated first) using AND. Create the required + component Regions. */ + if( oper == AST__XOR ) { + astNegate( reg1 ); + new->region1 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, + " ", status ); + astNegate( reg1 ); + + astNegate( reg2 ); + new->region2 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, + " ", status ); + astNegate( reg2 ); + + new->xor1 = reg1; + new->xor2 = reg2; + + new->oper = AST__OR; + +/* For AND and OR, use the supplied Regions and operator. */ + } else { + new->region1 = reg1; + new->region2 = reg2; + new->xor1 = NULL; + new->xor2 = NULL; + new->oper = oper; + } + +/* If either component Region has a dummy FrameSet rather than the correct + FrameSet, the correct FrameSet will have copies of the base Frame of the + new CmpRegion as both its current and base Frames, connected by a UnitMap + (this is equivalent to a FrameSet containing a single Frame). However if + the new CmpRegion being loaded has itself got a dummy FrameSet, then we do + not do this since we do not yet know what the correct FrameSet is. In this + case we wait until the parent Region invokes the astSetRegFS method on the + new CmpRegion. */ + if( !astRegDummyFS( new ) ) { + f1 = astGetFrame( ((AstRegion *) new)->frameset, AST__BASE ); + creg = new->region1; + if( astRegDummyFS( creg ) ) astSetRegFS( creg, f1 ); + creg = new->region2; + if( astRegDummyFS( creg ) ) astSetRegFS( creg, f1 ); + f1 = astAnnul( f1 ); + } + +/* If an error occurred, clean up by deleting the new CmpRegion. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new CmpRegion pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +int astCmpRegionList_( AstCmpRegion *this, int *nreg, AstRegion ***reg_list, + int *status ) { + if ( !astOK ) return AST__AND; + return (**astMEMBER(this,CmpRegion,CmpRegionList))( this, nreg, reg_list, + status ); +} + + + + + + diff --git a/cmpregion.h b/cmpregion.h new file mode 100644 index 0000000..92d7898 --- /dev/null +++ b/cmpregion.h @@ -0,0 +1,251 @@ +#if !defined( CMPREGION_INCLUDED ) /* Include this file only once */ +#define CMPREGION_INCLUDED +/* +*+ +* Name: +* cmpregion.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the CmpRegion class. + +* Invocation: +* #include "cmpregion.h" + +* Description: +* This include file defines the interface to the CmpRegion class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The CmpRegion class implement a Region which represents a simple interval +* on each axis of the encapsulated Frame + +* Inheritance: +* The CmpRegion class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 11-OCT-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ------- */ +/* Boolean operators */ + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST__AND 1 +#define AST__OR 2 +#define AST__XOR 3 + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* CmpRegion structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstCmpRegion { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstRegion *region1; /* First component Region */ + AstRegion *region2; /* Second component Region */ + int oper; /* Boolean operator */ + double *rvals[ 2 ]; /* Used boundary length at each break */ + double *offs[ 2 ]; /* Jump at each break */ + int nbreak[ 2 ]; /* Number of breaks */ + double d0[ 2 ]; /* Total used boundary length */ + double dtot[ 2 ]; /* Total boundary length */ + AstRegion *xor1; /* First XORed Region */ + AstRegion *xor2; /* Second XORed Region */ + int bounded; /* Is this CmpRegion bounded? */ +} AstCmpRegion; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstCmpRegionVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* CmpRegionList)( AstCmpRegion *, int *, AstRegion ***, int * ); + +} AstCmpRegionVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstCmpRegionGlobals { + AstCmpRegionVtab Class_Vtab; + int Class_Init; +} AstCmpRegionGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitCmpRegionGlobals_( AstCmpRegionGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(CmpRegion) /* Check class membership */ +astPROTO_ISA(CmpRegion) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstCmpRegion *astCmpRegion_( void *, void *, int, const char *, int *, ...); +#else +AstCmpRegion *astCmpRegionId_( void *, void *, int, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstCmpRegion *astInitCmpRegion_( void *, size_t, int, AstCmpRegionVtab *, + const char *, AstRegion *, AstRegion *, int, int * ); + +/* Vtab initialiser. */ +void astInitCmpRegionVtab_( AstCmpRegionVtab *, const char *, int * ); + +/* Loader. */ +AstCmpRegion *astLoadCmpRegion_( void *, size_t, AstCmpRegionVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +int astCmpRegionList_( AstCmpRegion *, int *, AstRegion ***, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckCmpRegion(this) astINVOKE_CHECK(CmpRegion,this,0) +#define astVerifyCmpRegion(this) astINVOKE_CHECK(CmpRegion,this,1) + +/* Test class membership. */ +#define astIsACmpRegion(this) astINVOKE_ISA(CmpRegion,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astCmpRegion astINVOKE(F,astCmpRegion_) +#else +#define astCmpRegion astINVOKE(F,astCmpRegionId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitCmpRegion(mem,size,init,vtab,name,reg1,reg2,oper) \ +astINVOKE(O,astInitCmpRegion_(mem,size,init,vtab,name,reg1,reg2,oper,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitCmpRegionVtab(vtab,name) astINVOKE(V,astInitCmpRegionVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadCmpRegion(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadCmpRegion_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckCmpRegion to validate CmpRegion pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astCmpRegionList(this,nreg,reg_list) \ +astINVOKE(V,astCmpRegionList_(this,nreg,reg_list,STATUS_PTR)) +#endif +#endif + + + + + diff --git a/complex.pdf b/complex.pdf new file mode 100644 index 0000000..2d16739 Binary files /dev/null and b/complex.pdf differ diff --git a/component.xml b/component.xml new file mode 100644 index 0000000..2b6b2ba --- /dev/null +++ b/component.xml @@ -0,0 +1,44 @@ + + + + + 8.2.0 + libraries/ast + WCS library + +

The AST library provides a comprehensive range of facilities for + attaching world coordinate systems to astronomical data, for + retrieving and interpreting that information in a variety of formats, + including FITS-WCS, and for generating graphical output based on it.

+

This library should be of interest to anyone writing + astronomical applications which need to manipulate coordinate system + data, especially celestial or spectral coordinate systems. AST + is portable and environment-independent.

+
+ + ssthtxmessgen + + + + Rodney Warren-Smith + rfws + + + David Berry + dsb + dsb@ast.man.ac.uk + owner + + + Norman Gray + nxg + norman@astro.gla.ac.uk + + + sun210 sun211 + starlink@jiscmail.ac.uk + + + Council for the Central Laboratory of the Research Councils + +
diff --git a/component.xml.in b/component.xml.in new file mode 100644 index 0000000..35f79c2 --- /dev/null +++ b/component.xml.in @@ -0,0 +1,44 @@ + + + + + @PACKAGE_VERSION@ + libraries/ast + WCS library + +

The AST library provides a comprehensive range of facilities for + attaching world coordinate systems to astronomical data, for + retrieving and interpreting that information in a variety of formats, + including FITS-WCS, and for generating graphical output based on it.

+

This library should be of interest to anyone writing + astronomical applications which need to manipulate coordinate system + data, especially celestial or spectral coordinate systems. AST + is portable and environment-independent.

+
+ + @STAR_DEPENDENCIES_CHILDREN@ + + + + Rodney Warren-Smith + rfws + + + David Berry + dsb + dsb@ast.man.ac.uk + owner + + + Norman Gray + nxg + norman@astro.gla.ac.uk + + + @STAR_DOCUMENTATION@ + @PACKAGE_BUGREPORT@ + + + Council for the Central Laboratory of the Research Councils + +
diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..2708d45 --- /dev/null +++ b/configure.ac @@ -0,0 +1,215 @@ +dnl Process this file with autoconf to produce a configure script +AC_REVISION($Revision$) + +dnl This configure file is known to work with autoconf-2.57, +dnl automake versions 1.6.3 and 1.7.5, and libtool versions 1.4.2 and +dnl 1.5. It should work with autoconf versions 2.50 or better, and +dnl automake 1.6 or better. + +dnl Initialisation: package name and version number +AC_INIT([ast],[8.2.0],[starlink@jiscmail.ac.uk]) +AC_CONFIG_AUX_DIR([build-aux]) + +dnl Require autoconf-2.50 at least +AC_PREREQ([2.69]) +dnl Require Starlink automake +AM_INIT_AUTOMAKE([1.8.2-starlink subdir-objects]) + +dnl Sanity-check: name a file in the source directory +AC_CONFIG_SRCDIR([ast_link.in]) + +# Include defaults for Starlink configurations +STAR_DEFAULTS + +# See if the --with-starmem option has been provided. This sets the +# preprocesor macro HAVE_STAR_MEM_H. +AC_ARG_WITH( + [starmem], + AS_HELP_STRING([--with-starmem],[use starmem library for memory management]), + AC_DEFINE([HAVE_STAR_MEM_H],[1],[Use the starmem library for memory management]), +) + +# See if the --with-memdebug option has been provided. This sets the +# preprocesor macro MEM_DEBUG which enables facilities used to track +# down memory leaks, etc. +AC_ARG_WITH( + [memdebug], + AS_HELP_STRING([--with-memdebug],[enable AST memory leak debugging functions]), + AC_DEFINE([MEM_DEBUG],[1],[enable AST memory leak debugging functions in memory.c]), +) + +# See if the --with-external_pal option has been provided. This sets the +# preprocesor macro EXTERNAL_PAL which prevents use of the PAL & ERFA +# library functions that are included in the AST distribution. Suitable +# link options are used within ast_link(_adam) scripts to pull in libpal. +AC_ARG_WITH([external_pal], + [ --with-external_pal Use external PAL and ERFA libraries], + if test "$withval" = "yes"; then + external_pal="1" + else + external_pal="0" + fi, + external_pal="0") +AC_SUBST( EXTERNAL_PAL, $external_pal ) +if test "$external_pal" = "1"; then + AC_SUBST( LIBPAL, "-lpal" ) + AC_DEFINE([EXTERNAL_PAL],[1],[use external PAL and ERFA libraries]), +else + AC_SUBST( LIBPAL, "" ) +fi +AM_CONDITIONAL(EXTERNAL_PAL, test x$external_pal = x1) + + +# Checks for programs +AC_PROG_CC +AC_PROG_CPP +LT_INIT +AC_PROG_LN_S + +# If --with-pic=no is set we should honour that. +AM_CONDITIONAL(NOPIC, test x$pic_mode = xno) + +# Conditional defining whether we build with POSIX thread support. +AC_ARG_WITH([pthreads], + [ --without-pthreads Build package without POSIX threads support], + if test "$withval" = "yes"; then + use_pthreads="1" + else + use_pthreads="0" + fi, + use_pthreads="1") +if test "$use_pthreads" = "1"; then +AC_CHECK_LIB([pthread], [pthread_create], ,[use_pthreads="0"]) +fi +AM_CONDITIONAL(NOTHREADS, test x$use_pthreads = x0) +AC_SUBST(THREADS, $use_pthreads) + +# See which variadic function API to use +AC_CHECK_HEADERS(stdarg.h varargs.h, break) + +# Can we use backtrace? +AC_CHECK_HEADERS([execinfo.h]) +AC_CHECK_FUNCS([backtrace]) + +# Do we have reentrant strerror and strtok? +AC_CHECK_FUNCS([strerror_r strtok_r]) + +# Do we have vsnprintf? +AC_CHECK_FUNCS([vsnprintf]) + +# See if we have long doubles (used by the Mapping and Region classes) +AC_CHECK_TYPES([long double]) + +# See if we have 64 bit integers. +AC_CHECK_TYPES([int64_t, uint64_t]) + +# Calculate alternative 64 bit integer sizes +AC_CHECK_SIZEOF(long) +AC_CHECK_SIZEOF(long long) + +# Conditional defining whether we want to support Fortran libraries +# (e.g. pgplot) and applications. +AC_ARG_WITH([fortran], + [ --without-fortran Build package without Fortran support], + if test "$withval" = "yes"; then + use_fortran="1" + else + use_fortran="0" + fi, + use_fortran="1") +AM_CONDITIONAL(NOFORTRAN, test x$use_fortran = x0) +AC_SUBST(FORTRAN, $use_fortran) + +# ast_link needs to be able to link against the Fortran runtime if +# necessary +if test "$use_fortran" = "1"; then +AC_PROG_FC +AC_FC_LIBRARY_LDFLAGS +fi + +# Find an absolute path to the Perl binary, augmenting the path with the +# location of the Starlink Perl build. If this fails, then set @PERL@ +# to the backup `/usr/bin/env perl', which will find Perl if it exists +# in the path at runtime. +AC_PATH_PROG(PERL, perl, [/usr/bin/env perl], [$STARLINK/Perl/bin:$PATH]) + +# Function and declaration checks +AC_CHECK_FUNCS([isnan]) +AC_CHECK_DECLS([isnan],,,[#include + ]) +AC_CHECK_FUNCS([isfinite]) +AC_CHECK_DECLS([isfinite],,,[#include + ]) +STAR_DECLARE_DEPENDENCIES(sourceset, [sst htx]) + +# Perform the check that configures f77.h.in for the return type of REAL +# Fortran functions. On 64-bit g77 with f2c compatibility this is double +# not float. +if test "$use_fortran" = "1"; then +STAR_CNF_F2C_COMPATIBLE + +# Determine the type of Fortran character string lengths. +STAR_CNF_TRAIL_TYPE +fi + +# Declare the message file. +STAR_MESSGEN(ast_err.msg) + +# Test for non-ANSI behaviour in sscanf on some platforms, reported by +# Bill Joye. Also check for bad MINGW sscanf. That returns nc=0 in the +# System test. +AC_MSG_CHECKING([whether the sscanf function is ANSI-compatible]) +AC_RUN_IFELSE([AC_LANG_SOURCE([[ + +#include +#include + +int main() { + char foo[] = " name 1111.1 "; + char mingw[] = "system= FK4_NO_E"; + + char bar[8]; + float ff; + int system; + int nc; + int r; + + nc = 0; + r = sscanf(foo, " %s %f %n", bar, &ff, &nc); + + if ( nc == 13 ) { + nc = 0; + r = sscanf( mingw, "system= %n%*s %n", &system, &nc ); + if ( nc != 0 ) nc = 13; + } + exit( ( nc != 13 ) ? 0 : 1 ); +} + +]])],[ + AC_MSG_RESULT([no]);AC_DEFINE([HAVE_NONANSI_SSCANF],[1],[The sscanf shows the non-ANSI behaviour reported by Bill Joye]) +],[AC_MSG_RESULT([yes])],[ + AC_DEFINE([HAVE_NONANSI_SSCANF],[1],[The sscanf may show the non-ANSI behaviour reported by Bill Joye]) +]) + +# Declare the documentation. We need to do complicated things to +# satisfy these targets, so give a non-null value +# for the second argument, to suppress automatic generation of +# rules. +STAR_LATEX_DOCUMENTATION([sun210 sun211], [sun210.pdf sun210.tex sun211.pdf sun211.tex sun210.htx_tar sun211.htx_tar]) +STAR_PREDIST_SOURCES(sun_master.tex) +STAR_CHECK_PROGS(star2html) +STAR_CHECK_PROGS(prolat, sst) # prolat is part of SST + +AC_CONFIG_HEADERS(config.h) + +AC_CONFIG_FILES(Makefile component.xml ast_link ast_link_adam object.h) +if test "$use_fortran" = "1"; then +AC_CONFIG_FILES(f77.h) +fi + +AC_CONFIG_FILES([ast_cpp], [chmod +x ast_cpp]) +# Following are files which are substituted by the Makefile at +# distribution time, rather than by configure. They are not distributed. +STAR_PREDIST_SOURCES(version.h.in builddocs.in addversion.in) + +AC_OUTPUT diff --git a/devtools/make_simtest b/devtools/make_simtest new file mode 100644 index 0000000..87412ed --- /dev/null +++ b/devtools/make_simtest @@ -0,0 +1,4 @@ +gcc -g -o simtest simtest.c -DTHREAD_SAFE -I$MYGIT/libraries/ast \ + -L$GITSTAR/lib `chr_link` `ast_link -ems` `err_link` + + diff --git a/devtools/simtest.c b/devtools/simtest.c new file mode 100644 index 0000000..e36634c --- /dev/null +++ b/devtools/simtest.c @@ -0,0 +1,864 @@ +#define astCLASS simtest + +#include +#include +#include "globals.h" +#include "object.h" +#include "fitschan.h" +#include "mapping.h" +#include "cmpmap.h" +#include "error.h" +#include "winmap.h" +#include "tranmap.h" +#include "frameset.h" +#include "pointset.h" +#include "f77.h" +#include "memory.h" +#include "switchmap.h" +#include "unitmap.h" + +void DisplayList( AstMapping *, int, int, int * ); +void sink( const char * ); +const char *source( void ); +void tran( AstMapping *, int * ); +void diff( AstMapping *, int * ); +void tranm( AstMapping **, int *, int, int, int * ); + +extern F77_SUBROUTINE(kpg1_asreg)( INTEGER(status) ); + + +FILE *fd=NULL; + +main(){ + char file[ 50 ], text[ 100 ]; + const char *class, *enc0; + char *attrs, *kvalue; + AstWinMap *winmap; + AstFitsChan *fc, *fc2; + AstChannel *ch, *channel; + AstMapping *map, *map2, *tmap; + AstObject *obj, *obj2; + int opt, nin, nout, i; + DECLARE_INTEGER(lstatus); + int *status; + + lstatus = 0; + status = &lstatus; + +/* F77_CALL(kpg1_asreg)( INTEGER_ARG(&lstatus) ); */ + + printf( "Input FITS or AST file [| fitschan attrs] > "); + scanf( "%s", file ); + + attrs = strchr( file, '|' ); + if( attrs ) { + *attrs = 0; + attrs++; + } else { + attrs = "Carlin=0"; + } + + fd = fopen( file, "r" ); + if( fd ){ + ch = (AstChannel *) astFitsChan( NULL, sink, attrs, status ); + if( astOK ) { + fc = (AstFitsChan *) ch; + while( astOK && fgets( text, 100, fd ) ){ + astPutFits( fc, text, 0 ); + } + astClearCard( fc ); + + enc0 = astGetC( fc, "encoding" ); + printf("Using %s encoding\n", enc0 ); + obj = astRead( fc ); + } + if( !obj || !astOK ){ + printf("Attempting to read file %s as an AST file...\n", file ); + astClearStatus; + ch = astChannel( source, NULL, "", status ); + + fclose( fd ); + fd = NULL; + fd = fopen( file, "r" ); + obj = astRead( ch ); + if( obj != NULL ) { + fc = NULL; + } else { + astClearStatus; + printf("Reverting to FitsChan and using a dummy UnitMap. \n", file ); + obj = (AstObject *) astUnitMap( 1, "", status ); + astAnnul( ch ); + ch = (AstChannel *) fc; + } + + } + fclose( fd ); + fd = NULL; + } else { + astError( 1, "Cannot open file '%s'.", status, file ); + } + + class = obj ? astGetClass( obj ) : NULL; + if( obj && class ){ + printf( "%s %s has been read from file '%s'.\n", + strspn( class, "AEIOUaeiou" )?"An":"A", class, file ); + } + + if( obj && astIsAFitsChan( obj ) ) { + fc = (AstFitsChan *) obj; + astClearCard( fc ); + enc0 = astGetC( fc, "encoding" ); + printf("Using %s encoding\n", enc0 ); + obj = astRead( fc ); + } + + if( obj && astIsAMapping( obj ) ){ + map = (AstMapping *) obj; + + while( astOK ){ + printf("\n" + "0 - Quit\n" + "1 - Transform points\n" + "2 - Simplify\n" + "3 - Display\n" + "4 - Write\n" + "5 - Shift and scale\n" + "6 - Set mapping attributes\n" + "7 - Get mapping attributes\n" + "8 - Dump the FitsChan\n" + "9 - Differentiate\n" + "10 - Find a FITS keyword\n" + "\n" + "Option > " ); + opt = 0; + scanf( "%d", &opt ); + + if( opt == 0 ){ + break; + + } else if( opt == 1 ){ + tran( map, status ); + + } else if( opt == 2 ){ + map2 = astSimplify( map ); + map = astAnnul( map ); + map = map2; + obj = (AstObject *) map2; + + } else if( opt == 3 ){ + if( astIsAFrameSet( map ) ) { + tmap = astGetMapping( map, AST__BASE, AST__CURRENT ); + } else { + tmap = astClone( map ); + } + DisplayList( tmap, 1, astGetInvert( tmap ), status ); + tmap = astAnnul( tmap ); + + } else if( opt == 4 ){ + printf( "\n Output FITS file > "); + scanf( "%s", file ); + fd = fopen( file, "w" ); + if( fd ){ + if( fc ) { + printf(" Encoding (orig=%s)> ",enc0 ); + scanf( "%s", text ); + if( !strcmp( enc0, text ) && fc != NULL ){ + printf("Using existing FitsChan.\n"); + fc2 = astClone( fc ); + fc = astAnnul( fc ); + ch = NULL; + astClearCard( fc2 ); + astSet( fc2, "encoding=%s", status, text ); + } else { + printf("Creating new FitsChan.\n"); + fc2 = astFitsChan( NULL, sink, "encoding=%s", status, text ); + astPutFits( fc2, "NAXIS1 = 1", 1 ); + astPutFits( fc2, "NAXIS2 = 1", 1 ); + astPutFits( fc2, "NAXIS3 = 1", 1 ); + astPutFits( fc2, "NAXIS4 = 1", 1 ); + } + } else { + printf(" Encoding for new FitsChan > " ); + scanf( "%s", text ); + fc2 = astFitsChan( NULL, sink, "encoding=%s", status, text ); + astPutFits( fc2, "NAXIS1 = 1", 1 ); + astPutFits( fc2, "NAXIS2 = 1", 1 ); + astPutFits( fc2, "NAXIS3 = 1", 1 ); + astPutFits( fc2, "NAXIS4 = 1", 1 ); + } + if( !astWrite( fc2, obj ) ){ + printf("No object written to file '%s'.\n", file ); + } + fc2 = astAnnul( fc2 ); + fclose( fd ); + fd = NULL; + } else { + astError( 1, "Cannot open file '%s'.", status, file ); + } + + } else if( opt == 5 ){ + nin = astGetNin( map ); + winmap = astWinMap( nin, NULL, NULL, NULL, NULL, "", status ); + + printf( "\n Enter %d axis shift values > ", nin ); + for( i = 0; i < nin; i++ ) scanf( "%lg", (winmap->a) + i ); + + printf( "\n Enter %d axis scale values > ", nin ); + for( i = 0; i < nin; i++ ) scanf( "%lg", (winmap->b) + i ); + + if( !strcmp( class, "FrameSet" ) ){ + astInvert( winmap ); + astRemapFrame( obj, AST__BASE, winmap ); + } else { + map2 = (AstMapping *) astCmpMap( winmap, map, 1, "", status ); + map = astAnnul( map ); + map = map2; + obj = (AstObject *) map2; + } + winmap = astAnnul( winmap ); + + } else if( opt == 6 ) { + printf("Enter attribute specifications: "); + scanf( "%s", text ); + astSet( map, text, status ); + if( !astOK ) astClearStatus; + + } else if( opt == 7 ) { + printf("Enter attribute name: "); + scanf( "%s", text ); + printf( "%s = %s\n", text, astGetC( map, text ) ); + if( !astOK ) astClearStatus; + + } else if( opt == 8 ) { + printf( "\n Output text file > "); + scanf( "%s", file ); + fd = fopen( file, "w" ); + if( fd ){ + channel = astChannel( NULL, sink, "Full=1", status ); + astDump( ch, channel ); + channel = astAnnul( channel ); + fclose( fd ); + fd = NULL; + } else { + printf("Failed to open output file '%s'.\n", file ); + perror(" "); + } + + } else if( opt == 9 ){ + diff( map, status ); + + } else if( opt == 10 ){ + if( fc ) { + printf("Enter FITS keyword: "); + scanf( "%s", text ); + if( astGetFitsS( fc, text, &kvalue ) ) { + printf("%s = '%s'\n", text, kvalue ); + } else { + printf("%s not found\n", text ); + } + } else { + printf("No FITS keywords are available\n", text ); + } + } + } + + } else { + astError( 1, "Object should be a Mapping).", status ); + } + + if( ch ) ch = astAnnul( ch ); + obj = astAnnul( obj ); + +} + +void DisplayList( AstMapping *map0, int series, int invert, int *status ){ + int nmap, i, inv, res, junk; + AstMapping *mi, *mf, **map_list, *m, *m2, *map, *smap, *tm1, *tm2; + AstCmpMap *cm; + AstTranMap *tm; + AstChannel *channel; + int invi, invf, *invert_list, opt, old_inv, nin, in[20], *out, nout; + int isswitch, tfwd, tinv; + const char *class; + char file[ 50 ]; + double dv; + + nmap = 0; + map_list = NULL; + invert_list = NULL; + map = astClone( map0 ); + + isswitch = astIsASwitchMap( map ); + if( isswitch ) { + astSwitchList( (AstSwitchMap *) map, invert, &nmap, &map_list, + &invert_list, status ); + } else { + astMapList( map, series, invert, &nmap, &map_list, &invert_list ); + } + + while( 1 ){ + if( isswitch ) { + printf("\nSwitchMap: (fwd selector, inv selector, routes )\n" ); + printf(" -3 - \n"); + } else { + printf("\nMappings in %s:\n", series?"series":"parallel" ); + printf(" -8 - \n"); + printf(" -7 - \n"); + printf(" -6 - \n"); + printf(" -5 - \n"); + printf(" -4 - \n"); + printf(" -3 - \n"); + printf(" -2 - \n"); + printf(" -1 - \n"); + } + printf(" 0 - \n"); + for( i = 0; i < nmap; i++ ){ + m = map_list[ i ]; + class = m ? astGetClass( m ) :"(NULL)"; + printf( " %d - %s\n", i + 1, class ); + } + printf("\n Option > ", 0 ); + opt = 0; + scanf("%d", &opt ); + + if ( opt == -8 ) { + + for( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] ); + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + nmap = 0; + + invert = ( invert == 0 ); + if( isswitch ) { + astSwitchList( (AstSwitchMap *) map, invert, &nmap, + &map_list, &invert_list, status ); + } else { + astMapList( map, series, invert, &nmap, &map_list, &invert_list ); + } + + } else if ( opt == -7 && !isswitch ) { + printf( "\n Output text file > "); + scanf( "%s", file ); + fd = fopen( file, "w" ); + if( fd ){ + channel = astChannel( NULL, sink, "", status ); + for( i = 0; i < nmap; i++ ) { + inv = astGetInvert( map_list[ i ] ); + astSetInvert( map_list[ i ], invert_list[ i ] ); + astDump( map_list[ i ], channel ); + astSetInvert( map_list[ i ], inv ); + } + channel = astAnnul( channel ); + fclose( fd ); + fd = NULL; + } else { + printf("Failed to open output file.\n" ); + } + + } else if( opt == -6 && !isswitch ) { + old_inv = astGetInvert( map ); + astSetInvert( map, invert ); + + printf( "\n Pick how many input axes (max=%d)? > ", + astGetNin( map ) ); + scanf( "%lg", &dv ); + nin = dv; + + printf( "\n Enter %d zero-based axis indices > ", nin ); + for( i = 0; i < nin; i++ ) { + scanf( "%lg", &dv ); + in[ i ] = dv; + } + + out = astMapSplit( map, nin, in, &smap ); + astSetInvert( map, old_inv ); + + if( out ) { + printf( "Corresponding outputs are: " ); + nout = astGetNout( smap ); + for( i = 0; i < nout; i++ ) printf( "%d ", out[ i ] ); + printf("\n"); + out = astFree( out ); + map = astAnnul( map ); + map = smap; + } else { + printf( "Selected inputs do not correspond to a subset of outputs\n"); + } + + for( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] ); + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + + nmap = 0; + astMapList( map, series, invert, &nmap, &map_list, &invert_list ); + + } else if( opt == -5 && !isswitch ) { + old_inv = astGetInvert( map ); + astSetInvert( map, invert ); + smap = astSimplify( map ); + if( smap == map ) printf("\n No simplification performed\n" ); + astSetInvert( map, old_inv ); + map = astAnnul( map ); + map = smap; + + for( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] ); + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + + nmap = 0; + astMapList( map, series, invert, &nmap, &map_list, &invert_list ); + + } else if( opt == -4 && !isswitch ) { + printf("\n Mapping index > ", 0 ); + scanf("%d", &i ); + i--; + res = astMapMerge( map_list[ i ], i, series, &nmap, + &map_list, &invert_list ); + if( res != -1 ) { + printf( "Index of first modified Mapping: %d\n", res + 1 ); + } else { + printf( "No mappings modified\n" ); + } + + } else if( opt == -3 && series && !isswitch ) { + + if( invert == astGetInvert( map ) ) { + tfwd = astGetTranForward( map ); + tinv = astGetTranInverse( map ); + } else { + tinv = astGetTranForward( map ); + tfwd = astGetTranInverse( map ); + } + + if( !tfwd ) { + printf( "\n No forward transformation available\n" ); + + } else if( !tinv ) { + printf( "\n No inverse transformation available\n" ); + tranm( map_list, invert_list, nmap, 0, status ); + + } else { + tranm( map_list, invert_list, nmap, 1, status ); + + } + + } else if( opt == -3 && isswitch ) { + + if( invert == astGetInvert( map ) ) { + tfwd = astGetTranForward( map ); + tinv = astGetTranInverse( map ); + } else { + tinv = astGetTranForward( map ); + tfwd = astGetTranInverse( map ); + } + + if( !tfwd ) { + printf( "\n No forward transformation available\n" ); + + } else if( !tinv ) { + printf( "\n No inverse transformation available\n" ); + tranm( &map, &invert, 1, 0, status ); + + } else { + tranm( &map, &invert, 1, 1, status ); + + } + + } else if( opt == -3 && !series && !isswitch ) { + old_inv = astGetInvert( map ); + astSetInvert( map, invert ); + tran( map, status ); + astSetInvert( map, old_inv ); + + } else if ( opt == -2 && !isswitch ) { + + for( i = 0; i < nmap; i++ ) { + inv = astGetInvert( map_list[ i ] ); + astSetInvert( map_list[ i ], invert_list[ i ] ); + if( i == 0 ) { + tm1 = astClone( map_list[ 0 ] ); + } else { + tm2 = (AstMapping *) astCmpMap( tm1, map_list[ i ], series, "", status ); + tm1 = astAnnul( tm1 ); + tm1 = tm2; + } + astSetInvert( map_list[ i ], inv ); + } + + printf( "\n Output text file > "); + scanf( "%s", file ); + fd = fopen( file, "w" ); + if( fd ){ + channel = astChannel( NULL, sink, "", status ); + old_inv = astGetInvert( tm1 ); + astSetInvert( tm1, invert ); + astDump( tm1, channel ); + astSetInvert( tm1, old_inv ); + channel = astAnnul( channel ); + fclose( fd ); + fd = NULL; + } else { + printf("Failed to open output file.\n" ); + } + tm1= astAnnul( tm1 ); + + } else if ( opt == -1 && !isswitch ) { + + + for( i = 0; i < nmap; i++ ) { + inv = astGetInvert( map_list[ i ] ); + astSetInvert( map_list[ i ], invert_list[ i ] ); + if( i == 0 ) { + tm1 = astClone( map_list[ 0 ] ); + } else { + tm2 = (AstMapping *) astCmpMap( tm1, map_list[ i ], + series, "", status ); + tm1 = astAnnul( tm1 ); + tm1 = tm2; + } + astSetInvert( map_list[ i ], inv ); + } + + channel = astChannel( NULL, NULL, "Full=1", status ); + old_inv = astGetInvert( tm1); + astSetInvert( tm1, invert ); + astDump( tm1, channel ); + astSetInvert( tm1, old_inv ); + channel = astAnnul( channel ); + tm1= astAnnul( tm1 ); + + } else if( opt > 0 && opt <= nmap && map_list[ opt - 1 ] ){ + m2 = map_list[ opt - 1 ]; + inv = invert_list[ opt - 1 ]; + class = astGetClass( m2 ); + + if( m2 && astIsAFrameSet( m2 ) ){ + m = astGetMapping( m2, AST__BASE, AST__CURRENT ); + class = astGetClass( m ); + + } else if( astIsATranMap( m2 ) ){ + + astDecompose( m2, &mf, &mi, &invf, &invi, &junk ); + + if( inv ) { + m = mi; + inv = invi; + } else { + m = mf; + inv = invf; + } + class = astGetClass( m ); + + } else { + m = astClone( m2 ); + } + + if( strcmp( class, "CmpMap" ) ){ + DisplayList( m, 1, inv, status ); + } else { + cm = (AstCmpMap *) m; + DisplayList( m, cm->series, inv, status ); + } + } else { + break; + } + } + + for( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] ); + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + nmap = 0; + +} + + +void sink( const char *text ){ + if( fd && text ) fprintf( fd, "%s\n", text ); +} + +const char *source( void ){ + static char text[2000]; + static char *ptext; + if( fd ) { + ptext = fgets( text, 2000, fd ); + } else { + ptext = NULL; + } + return ptext; +} + +void tran( AstMapping *map, int *status ){ + char text[2000]; + AstFrame *infrm, *outfrm; + AstPointSet *pset1, *pset2; + double **ptr1, **ptr2; + int nin, nout, i, ok; + + if( !astGetTranForward( map ) ) { + printf( "\n No forward transformation available\n" ); + return; + } + + if( !astGetTranInverse( map ) ) { + printf( "\n No inverse transformation available\n" ); + } + + nin = astGetNin( map ); + nout = astGetNout( map ); + pset1 = astPointSet( 1, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + pset2 = astPointSet( 1, nout, "", status ); + ptr2 = astGetPoints( pset2 ); + + if( astIsAFrameSet( map ) ) { + infrm = astGetFrame( map, AST__BASE ); + outfrm = astGetFrame( map, AST__CURRENT ); + } else if( astIsAFrame( map ) ) { + infrm = (AstFrame *) map; + outfrm = (AstFrame *) map; + } else { + infrm = NULL; + outfrm = NULL; + } + + if( astOK ){ + ok = 0; + if( infrm != NULL ) { + ok = 1; + for( i = 0; i < nin; i++ ) { + printf( "\n Enter %s value (or ) > ", astGetLabel( infrm, i ) ); + scanf( "%s", text ); + if( !astUnformat( infrm, i, text, ptr1[ i ] ) ) { + ok = 0; + printf( "Bad formatted value supplied! Now try unformatted axis values...\n"); + break; + } + } + } + + if( !ok ) { + printf( "\n Enter %d axis values (BAD=999) > ", nin ); + for( i = 0; i < nin; i++ ) { + scanf( "%lg", ptr1[ i ] ); + if( ptr1[ i ][ 0 ] == 999.0 ) ptr1[ i ][ 0 ] = AST__BAD; + } + } + + astTransform( map, pset1, 1, pset2 ); + printf( "\n Forward: [ " ); + for( i = 0; i < nin; i++ ) { + if( ptr1[ i ][ 0 ] == AST__BAD ) { + printf( "999.0 " ); + } else { + printf( "%.*g ", DBL_DIG, ptr1[ i ][ 0 ] ); + } + } + + printf( "] -> [ " ); + for( i = 0; i < nout; i++ ) { + if( ptr2[ i ][ 0 ] == AST__BAD ) { + printf( "999.0 " ); + } else { + printf( "%.*g ", DBL_DIG, ptr2[ i ][ 0 ] ); + } + } + printf( "]\n" ); + + if( infrm != NULL && outfrm != NULL ) { + printf( " ( " ); + for( i = 0; i < nin; i++ ) { + printf( "%s ", astFormat( infrm, i, ptr1[ i ][ 0 ] ) ); + } + printf( ") -> ( " ); + for( i = 0; i < nout; i++ ) { + printf( "%s ", astFormat( outfrm, i, ptr2[ i ][ 0 ] ) ); + } + printf( ") formatted\n" ); + } + + + if( astGetTranInverse( map ) ) { + astTransform( map, pset2, 0, pset1 ); + printf( "\n Inverse: [ " ); + for( i = 0; i < nout; i++ ) { + if( ptr2[ i ][ 0 ] == AST__BAD ) { + printf( "999.0 " ); + } else { + printf( "%.*g ", DBL_DIG, ptr2[ i ][ 0 ] ); + } + } + + printf( "] -> [ " ); + for( i = 0; i < nin; i++ ) { + if( ptr1[ i ][ 0 ] == AST__BAD ) { + printf( "999.0 " ); + } else { + printf( "%.*g ", DBL_DIG, ptr1[ i ][ 0 ] ); + } + } + printf( "]\n" ); + + + if( infrm != NULL && outfrm != NULL ) { + printf( " ( " ); + for( i = 0; i < nout; i++ ) { + printf( "%s ", astFormat( outfrm, i, ptr2[ i ][ 0 ] ) ); + } + printf( ") -> ( " ); + for( i = 0; i < nin; i++ ) { + printf( "%s ", astFormat( infrm, i, ptr1[ i ][ 0 ] ) ); + } + printf( ") formatted\n" ); + } + } + } + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); +} + +void tranm( AstMapping **map, int *inv_list, int nmap, int inv, int *status ){ + AstPointSet *pset1, *pset2; + double **ptr1, **ptr2; + int nin, nout, i, j, inv0; + + inv0 = astGetInvert( map[ 0 ] ); + astSetInvert( map[ 0 ], inv_list[ 0 ] ); + nin = astGetNin( map[ 0 ] ); + astSetInvert( map[ 0 ], inv0 ); + + pset1 = astPointSet( 1, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + + if( astOK ){ + printf( "\n Enter %d axis values (BAD=999) > ", nin ); + for( i = 0; i < nin; i++ ) { + scanf( "%lg", ptr1[ i ] ); + if( ptr1[ i ][ 0 ] == 999.0 ) ptr1[ i ][ 0 ] = AST__BAD; + } + + printf("Forward...\n"); + for( j=0; j=0 && inv; j-- ) { + inv0 = astGetInvert( map[ j ] ); + astSetInvert( map[ j ], inv_list[ j ] ); + + nin = astGetNin( map[ j ] ); + pset2 = astPointSet( 1, nin, "", status ); + ptr2 = astGetPoints( pset2 ); + + astTransform( map[ j ], pset1, 0, pset2 ); + + printf( " Mapping %d gave [ ", j+1 ); + for( i = 0; i < nin; i++ ) { + if( ptr2[ i ][ 0 ] == AST__BAD ) { + printf( "999.0 " ); + } else { + printf( "%.*g ", DBL_DIG, ptr2[ i ][ 0 ] ); + } + } + printf( "]\n" ); + + pset1 = astAnnul( pset1 ); + astSetInvert( map[ j ], inv0 ); + + pset1 = pset2; + ptr1 = ptr2; + nout = nin; + } + + pset1 = astAnnul( pset1 ); + + } +} + + + + +void diff( AstMapping *map, int *status ){ + char text[200]; + AstFrame *infrm, *outfrm; + int nin, nout, i, ok, ax1, ax2; + double at[2000]; + + + nin = astGetNin( map ); + nout = astGetNout( map ); + + if( astIsAFrameSet( map ) ) { + infrm = astGetFrame( map, AST__BASE ); + outfrm = astGetFrame( map, AST__CURRENT ); + } else if( astIsAFrame( map ) ) { + infrm = (AstFrame *) map; + outfrm = (AstFrame *) map; + } else { + infrm = NULL; + outfrm = NULL; + } + + if( astOK ){ + ok = 0; + if( infrm != NULL ) { + ok = 1; + for( i = 0; i < nin; i++ ) { + printf( "\n Enter %s value (or ) > ", astGetLabel( infrm, i ) ); + scanf( "%s", text ); + if( !astUnformat( infrm, i, text, at + i ) ) { + ok = 0; + printf( "Bad formatted value supplied! Now try unformatted axis values...\n"); + break; + } + } + } + + if( !ok ) { + printf( "\n Enter %d axis values (BAD=999) > ", nin ); + for( i = 0; i < nin; i++ ) { + scanf( "%lg", at + i ); + if( at[ i ] == 999.0 ) at[ i ] = AST__BAD; + printf("(got %.*g)\n", DBL_DIG, at[ i ] ); + } + + } + + for( i = 0; i < nin; i++ ) { + printf("(%d: got %.*g)\n", i, DBL_DIG, at[ i ] ); + } + + printf( "\n Enter output to differentiate (zero-based index): > " ); + scanf( "%d", &ax1 ); + printf( "\n Enter input to vary (zero-based index): > " ); + scanf( "%d", &ax2 ); + printf( "%.*g \n", DBL_DIG, astRate( map, at, ax1, ax2 ) ); + } +} + diff --git a/doincludes b/doincludes new file mode 100755 index 0000000..656b0ac --- /dev/null +++ b/doincludes @@ -0,0 +1,22 @@ +#! /usr/bin/env perl + +# Reads a Latex file which contains \include{file} commands and writes +# the document to standard output with the text of the included files +# inserted. + +# Read input lines. + while ( <> ) { + +# Spot the \include{file} lines and extract the file name. + if ( ( $file ) = /^ *\\include{(.*)} *$/ ) { + +# Read the contents of the included file. + open( INCLUDE, $file . ".tex" ); + while ( ) { print; } + close( INCLUDE ); + +# Output other lines unchanged. + } else { + print; + } + } diff --git a/dsbspecframe.c b/dsbspecframe.c new file mode 100644 index 0000000..20a8ed2 --- /dev/null +++ b/dsbspecframe.c @@ -0,0 +1,3266 @@ +/* +*class++ +* Name: +* DSBSpecFrame + +* Purpose: +* Dual sideband spectral coordinate system description. + +* Constructor Function: +c astDSBSpecFrame +f AST_DSBSPECFRAME + +* Description: +* A DSBSpecFrame is a specialised form of SpecFrame which represents +* positions in a spectrum obtained using a dual sideband instrument. +* Such an instrument produces a spectrum in which each point contains +* contributions from two distinctly different frequencies, one from +* the "lower side band" (LSB) and one from the "upper side band" (USB). +* Corresponding LSB and USB frequencies are connected by the fact +* that they are an equal distance on either side of a fixed central +* frequency known as the "Local Oscillator" (LO) frequency. +* +* When quoting a position within such a spectrum, it is necessary to +* indicate whether the quoted position is the USB position or the +* corresponding LSB position. The SideBand attribute provides this +* indication. Another option that the SideBand attribute provides is +* to represent a spectral position by its topocentric offset from the +* LO frequency. +* +* In practice, the LO frequency is specified by giving the distance +* from the LO frequency to some "central" spectral position. Typically +* this central position is that of some interesting spectral feature. +* The distance from this central position to the LO frequency is known +* as the "intermediate frequency" (IF). The value supplied for IF can +* be a signed value in order to indicate whether the LO frequency is +* above or below the central position. + +* Inheritance: +* The DSBSpecFrame class inherits from the SpecFrame class. + +* Attributes: +* In addition to those attributes common to all SpecFrames, every +* DSBSpecFrame also has the following attributes: +* +* - AlignSideBand: Should alignment occur between sidebands? +* - DSBCentre: The central position of interest. +* - IF: The intermediate frequency used to define the LO frequency. +* - ImagFreq: The image sideband equivalent of the rest frequency. +* - SideBand: Indicates which sideband the DSBSpecFrame represents. + +* Functions: +c The DSBSpecFrame class does not define any new functions beyond those +f The DSBSpecFrame class does not define any new routines beyond those +* which are applicable to all SpecFrames. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) + +* History: +* 5-AUG-2004 (DSB): +* Original version. +* 7-OCT-2004 (DSB): +* Fixed SetAttrib code which assigns values to SideBand. Previously +* all supplied values were ignored, leaving SideBand unchanged. +* 2-SEP-2005 (DSB): +* Allow conversion in any Domain within TopoMap (sometimes +* SpecFrames have a new Domain set which is not equal to SPECTRUM"). +* 12-SEP-2005 (DSB): +* Set all attributes required to described the RestFreq value +* before determining Mapping from RestFreq to ImagFreq in +* GetImageFreq. +* 2-DEC-2005 (DSB): +* Change default Domain from SPECTRUM to DSBSPECTRUM +* 3-APR-2006 (DSB): +* Fix memory leak in astLoadDSBSpecFrame. +* 6-OCT-2006 (DSB): +* Guard against annulling null pointers in subFrame. +* 27-OCT-2006 (DSB): +* Added AlignSideBand attribute. +* 31-OCT-2006 (DSB): +* Use AlignSideBand attribute in SubFrame only if we are not +* currently restoring a FrameSet's integrity. +* 31-JAN-2007 (DSB): +* Modified so that a DSBSpecFrame can be used as a template to find a +* DSBSpecFrame (or SpecFrame) contained within a CmpFrame. This +* involves changes in Match. +* 1-MAY-2007 (DSB): +* The default for AlignSideband has been changed from 1 to 0. +* 8-MAY-2007 (DSB): +* Correct initialisation of alignsideband in astInitDSBSpecFrame_. +* 19-OCT-2007 (DSB): +* Ignore SideBand alignment if the AlignSideBand attribute is zero +* in either the target or the template. +* 16-JAN-2007 (DSB): +* Modify SubFrame so that DSBSpecFrames are aligned in the +* observed sideband (LSB or USB) rather than always being aligned +* in the USB. +* 12-FEB-2010 (DSB): +* Report an error if the local oscillator frequency looks silly +* (specifically, if it less than the absolute intermediate frequency). +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +*class-- + +* Implementation Deficiencies: +* - The default values for System and StdOfRest inherited from the +* SpecFrame class are "Wave" and "Heliocentric". These are not +* usually what is wanted for a DSB instrument. Defaults such as +* "Freq" and "Topo" may be more appropriate. However, changing the +* defaults inherited from SpecFrame may cause problems in the +* astConvert algorithm. The astConvertX function in frame.c includes +* the following implementation deficiency warning: "One likely +* problem is with attributes which default in both the source and +* destination Frames. This means they also default in the common +* coordinate system. If these default values were to differ when +* matching different target Frames, however, we would be in trouble, +* because the common coordinate system would not then be remaining +* constant. The longer-term solution to this is probably to provide +* some mechanism to "fix" all attribute values for a Frame, by taking +* any attributes that are un-set and explicitly setting a firm value +* (equal to the default) so they cannot then change". So the defaults +* should probably be left unchanged until this fix is made. + +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS DSBSpecFrame + +#define BADSB -9999 +#define FIRST_SB -1 +#define LSB -1 +#define LO 0 +#define USB 1 +#define LAST_SB 1 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "object.h" /* Base Object class */ +#include "channel.h" /* I/O channels */ +#include "specframe.h" /* Spectral frames (parent class) */ +#include "unit.h" /* Unit handling */ +#include "cmpmap.h" /* Compound Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "winmap.h" /* Window Mappings */ +#include "dsbspecframe.h" /* Interface definition for this class */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getlabel)( AstFrame *, int, int * ); +static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * ); +static const char *(* parent_getdomain)( AstFrame *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetLabel_Buff[ 0 ] = 0; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(DSBSpecFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(DSBSpecFrame,Class_Init) +#define class_vtab astGLOBAL(DSBSpecFrame,Class_Vtab) +#define getattrib_buff astGLOBAL(DSBSpecFrame,GetAttrib_Buff) +#define getlabel_buff astGLOBAL(DSBSpecFrame,GetLabel_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Define the thread-specific globals for this class. */ + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ 101 ]; + +/* Default Label string buffer */ +static char getlabel_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstDSBSpecFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstDSBSpecFrame *astDSBSpecFrameId_( const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ + +static AstMapping *TopoMap( AstDSBSpecFrame *, int, const char *, int * ); +static AstMapping *ToLOMapping( AstDSBSpecFrame *, const char *, int * )__attribute__((unused)); +static AstMapping *ToLSBMapping( AstDSBSpecFrame *, const char *, int * ); +static AstMapping *ToUSBMapping( AstDSBSpecFrame *, const char *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static double GetImagFreq( AstDSBSpecFrame *, int * ); +static double GetLO( AstDSBSpecFrame *, const char *, const char *, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void VerifyAttrs( AstDSBSpecFrame *, const char *, const char *, const char *, int * ); +static const char *GetDomain( AstFrame *, int * ); + +static double GetIF( AstDSBSpecFrame *, int * ); +static int TestIF( AstDSBSpecFrame *, int * ); +static void ClearIF( AstDSBSpecFrame *, int * ); +static void SetIF( AstDSBSpecFrame *, double, int * ); + +static double GetDSBCentre( AstDSBSpecFrame *, int * ); +static int TestDSBCentre( AstDSBSpecFrame *, int * ); +static void ClearDSBCentre( AstDSBSpecFrame *, int * ); +static void SetDSBCentre( AstDSBSpecFrame *, double, int * ); + +static int GetSideBand( AstDSBSpecFrame *, int * ); +static int TestSideBand( AstDSBSpecFrame *, int * ); +static void ClearSideBand( AstDSBSpecFrame *, int * ); +static void SetSideBand( AstDSBSpecFrame *, int, int * ); + +static int GetAlignSideBand( AstDSBSpecFrame *, int * ); +static int TestAlignSideBand( AstDSBSpecFrame *, int * ); +static void ClearAlignSideBand( AstDSBSpecFrame *, int * ); +static void SetAlignSideBand( AstDSBSpecFrame *, int, int * ); + + +/* Member functions. */ +/* ================= */ +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a DSBSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the astClearAttrib protected +* method inherited from the SpecFrame class). + +* Description: +* This function clears the value of a specified attribute for a +* DSBSpecFrame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstDSBSpecFrame *this; /* Pointer to the DSBSpecFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the DSBSpecFrame structure. */ + this = (AstDSBSpecFrame *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* DSBCentre. */ +/* ---------- */ + if ( !strcmp( attrib, "dsbcentre" ) ) { + astClearDSBCentre( this ); + +/* IF */ +/* -- */ + } else if ( !strcmp( attrib, "if" ) ) { + astClearIF( this ); + +/* SideBand */ +/* -------- */ + } else if ( !strcmp( attrib, "sideband" ) ) { + astClearSideBand( this ); + +/* AlignSideBand */ +/* ------------- */ + } else if ( !strcmp( attrib, "alignsideband" ) ) { + astClearAlignSideBand( this ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute name matches any of the read-only attributes + of this class. If it does, then report an error. */ + } else if ( !strcmp( attrib, "imagfreq" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a DSBSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the protected astGetAttrib +* method inherited from the SpecFrame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a DSBSpecFrame, formatted as a character string. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the DSBSpecFrame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the DSBSpecFrame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstDSBSpecFrame *this; /* Pointer to the DSBSpecFrame structure */ + AstMapping *tmap; /* Ptr to Mapping from topofreq to this */ + const char *result; /* Pointer value to return */ + double dval; /* Attribute value */ + double dtemp; /* Attribute value */ + int ival; /* Attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstDSBSpecFrame *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* DSBCentre */ +/* --------- */ + if ( !strcmp( attrib, "dsbcentre" ) ) { + +/* Get the value as topocentric frequency in Hz. */ + dval = astGetDSBCentre( this ); + +/* Find the Mapping from topocentric frequency in Hz to the spectral system + described by this SpecFrame. */ + tmap = TopoMap( this, 0, "astGetAttrib", status ); + if ( astOK ) { + +/* Transform the internal value from topocentric frequency into the required + system. */ + astTran1( tmap, 1, &dval, 1, &dtemp ); + if( dtemp == AST__BAD ) { + astError( AST__INTER, "astGetAttrib(%s): Cannot convert DSBCentre " + "value from topocentric frequency to the required " + "system.", status, astGetClass( this ) ); + } else { + +/* Format it. */ + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dtemp ); + result = getattrib_buff; + } + tmap = astAnnul( tmap ); + } + +/* IF */ +/* -- */ + } else if ( !strcmp( attrib, "if" ) ) { + dval = astGetIF( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval*1.0E-9 ); + result = getattrib_buff; + } + +/* ImagFreq */ +/* -------- */ + } else if ( !strcmp( attrib, "imagfreq" ) ) { + dval = astGetImagFreq( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval*1.0E-9 ); + result = getattrib_buff; + } + +/* SideBand */ +/* -------- */ + } else if ( !strcmp( attrib, "sideband" ) ) { + ival = astGetSideBand( this ); + if ( astOK ) { + result = ( ival == USB ) ? "USB" : (( ival == LO ) ? "LO" : "LSB" ); + } + +/* AlignSideBand */ +/* ------------- */ + } else if ( !strcmp( attrib, "alignsideband" ) ) { + ival = astGetAlignSideBand( this ) ? 1 : 0; + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static const char *GetDomain( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDomain + +* Purpose: +* Obtain a pointer to the Domain attribute string for a DSBSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* const char *GetDomain( AstFrame *this, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the astGetDomain protected +* method inherited from the SpecFrame class). + +* Description: +* This function returns a pointer to the Domain attribute string +* for a DSBSpecFrame. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Domain value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the DSBSpecFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstDSBSpecFrame *this; /* Pointer to DSBSpecFrame structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the DSBSpecFrame structure. */ + this = (AstDSBSpecFrame *) this_frame; + +/* If a Domain attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestDomain( this ) ) { + result = (*parent_getdomain)( this_frame, status ); + +/* Otherwise, provide a pointer to a suitable default string. */ + } else { + result = "DSBSPECTRUM"; + } + +/* Return the result. */ + return result; +} + +static double GetImagFreq( AstDSBSpecFrame *this, int *status ) { +/* +*+ +* Name: +* astGetImagFreq + +* Purpose: +* Get the value of the ImagFreq attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "dsbspecframe.h" +* double GetImagFreq( AstDSBSpecFrame *this ) + +* Class Membership: +* DSBSpecFrame method. + +* Description: +* This function returns the image sideband frequency corresponding to +* the rest frequency. + +* Parameters: +* this +* Pointer to the Frame. + +* Returned Value: +* The required frequency, in Hz. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstDSBSpecFrame *rf_frame;/* DSBSpecFrame describing the rest frequency */ + AstMapping *map; /* Pointer to "Observed to Image" mapping */ + double result; /* The returned frequency */ + double rf; /* Rest frequency in observed sideband */ + int sb; /* SideBand value */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* The RestFreq attribute is an observed sideband frequency in the + source's standard of rest, measured in Hz. Temporaily set attributes + to these values. Create a copy of the supplied DSBSpecFrame and set + its attributes to these values. */ + rf_frame = astCopy( this ); + astSetStdOfRest( rf_frame, AST__SCSOR ); + astSetSystem( rf_frame, AST__FREQ ); + astSetUnit( rf_frame, 0, "Hz" ); + astSetC( rf_frame, "SideBand", "observed" ); + +/* Create a Mapping which transforms positions from the observed to the + image sideband. */ + sb = astGetSideBand( rf_frame ); + if( sb == USB ) { + map = ToLSBMapping( rf_frame, "astGetImagFreq", status ); + + } else if( sb == LSB ) { + map = ToUSBMapping( rf_frame, "astGetImagFreq", status ); + + } else { + map = NULL; + astError( AST__INTER, "astGetImagFreq(%s): Illegal sideband value " + "(%d) encountered (internal AST programming error).", status, + astGetClass( this ), sb ); + } + +/* Get the rest frequency in Hz, and transform it using the above Mapping. */ + rf = astGetRestFreq( rf_frame ); + astTran1( map, 1, &rf, 1, &result ); + +/* Free resources */ + map = astAnnul( map ); + rf_frame = astAnnul( rf_frame ); + +/* If an error has occurrred, return AST__BAD. */ + if( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; + +} + +static const char *GetLabel( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetLabel + +* Purpose: +* Access the Label string for a DSBSpecFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* const char *GetLabel( AstFrame *this, int axis, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the astGetLabel method inherited +* from the SpecFrame class). + +* Description: +* This function returns a pointer to the Label string for a specified axis +* of a DSBSpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *result; /* Pointer to label string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetLabel" ); + +/* Invoke the parent astGetLabel method to obtain a pointer to it. */ + result = (*parent_getlabel)( this, axis, status ); + +/* Check if this is a default value. If so, append a string indicating + the sideband. */ + if ( !astTestLabel( this, axis ) ) { + +/* If OK, supply a pointer to a suitable default label string. */ + sprintf( getlabel_buff, "%s (%s)", result, astGetAttrib( this, "sideband" ) ); + result = getlabel_buff; + } + +/* Return the result. */ + return result; + +} + +static double GetLO( AstDSBSpecFrame *this, const char *check_msg, + const char *method, int *status ) { +/* +* Name: +* GetLO + +* Purpose: +* Get the Local Oscillator frequency. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* double GetLO( AstDSBSpecFrame *this, const char *check_msg, +* const char *method, int *status ) + +* Class Membership: +* DSBSpecFrame method. + +* Description: +* This function returns the local oscillator frequency in topocentric +* frequency. + +* Parameters: +* this +* Pointer to the Frame. +* check_msg +* If not NULL, an error will be reported if either the DSBCentre +* or IF attribute has not been set to an explicit value. In this +* case, the error message will include the supplied text. +* method +* The name of the calling method - used in error messages. +* status +* Pointer to the inherited status value. + +* Returned Value: +* The local oscillator frequency, in Hz. + +* Notes: +* - An error is reported if the local oscillator frequency looks +* un-physical (specifically, if it is less than the absolute value of +* the intermediate frequency). +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + double f_if; /* Intermediate frequency (topo,HZ) */ + double result; /* The returned frequency */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* If required, check that explicit values have been assigned to the required + attributes (i.e. report an error if a default value would be used for + either attribute). */ + if( check_msg ) VerifyAttrs( this, check_msg, "IF DSBCentre", method, + status ); + +/* The local oscillator is the sum of the intermediate frequency and the + observation centre frequency. */ + f_if = astGetIF( this ); + result = astGetDSBCentre( this ) + f_if; + +/* Check the local oscillator frequency is no smaller than the absolute + intermediate frequency. */ + if( result < fabs( f_if ) && astOK ) { + astError( AST__ATTIN, "%s(%s): The local oscillator frequency (%g Hz) " + "is too low (less than the intermediate frequency: %g Hz).", + status, method, astGetClass( this ), result, fabs( f_if ) ); + astError( AST__ATTIN, " This could be caused by a bad value for" + " either the IF attribute (currently %g Hz) or the DSBCentre " + "attribute (currently %g Hz).", status, f_if, + astGetDSBCentre( this ) ); + } + +/* If an error has occurrred, return AST__BAD. */ + if( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +void astInitDSBSpecFrameVtab_( AstDSBSpecFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitDSBSpecFrameVtab + +* Purpose: +* Initialise a virtual function table for a DSBSpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "dsbspecframe.h" +* void astInitDSBSpecFrameVtab( AstDSBSpecFrameVtab *vtab, const char *name ) + +* Class Membership: +* DSBSpecFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the DSBSpecFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitSpecFrameVtab( (AstSpecFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsADSBSpecFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstSpecFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->ClearDSBCentre = ClearDSBCentre; + vtab->TestDSBCentre = TestDSBCentre; + vtab->GetDSBCentre = GetDSBCentre; + vtab->SetDSBCentre = SetDSBCentre; + + vtab->ClearIF = ClearIF; + vtab->TestIF = TestIF; + vtab->GetIF = GetIF; + vtab->SetIF = SetIF; + + vtab->ClearSideBand = ClearSideBand; + vtab->TestSideBand = TestSideBand; + vtab->GetSideBand = GetSideBand; + vtab->SetSideBand = SetSideBand; + + vtab->ClearAlignSideBand = ClearAlignSideBand; + vtab->TestAlignSideBand = TestAlignSideBand; + vtab->GetAlignSideBand = GetAlignSideBand; + vtab->SetAlignSideBand = SetAlignSideBand; + + vtab->GetImagFreq = GetImagFreq; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_getdomain = frame->GetDomain; + frame->GetDomain = GetDomain; + + parent_overlay = frame->Overlay; + frame->Overlay = Overlay; + + parent_match = frame->Match; + frame->Match = Match; + + parent_subframe = frame->SubFrame; + frame->SubFrame = SubFrame; + + parent_getlabel = frame->GetLabel; + frame->GetLabel = GetLabel; + +/* Declare the class delete function.*/ + astSetDump( vtab, Dump, "DSBSpecFrame", "Dual sideband spectral axis" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the protected astMatch method +* inherited from the SpecFrame class). + +* Description: +* This function matches a "template" DSBSpecFrame to a "target" Frame and +* determines whether it is possible to convert coordinates between them. +* If it is, a mapping that performs the transformation is returned along +* with a new Frame that describes the coordinate system that results when +* this mapping is applied to the "target" coordinate system. In addition, +* information is returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" and "template" +* Frames from which they are derived. + +* Parameters: +* template +* Pointer to the template DSBSpecFrame. This describes the coordinate +* system (or set of possible coordinate systems) into which we wish to +* convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate system in +* which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the template DSBSpecFrame axis from +* which it is derived. If it is not derived from any template +* DSBSpecFrame axis, a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the target Frame axis from which it +* is derived. If it is not derived from any target Frame axis, a value +* of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will be +* returned if the requested coordinate conversion is possible. If +* returned, the forward transformation of this Mapping may be used to +* convert coordinates between the "target" Frame and the "result" +* Frame (see below) and the inverse transformation will convert in the +* opposite direction. +* result +* Address of a location where a pointer to a new Frame will be returned +* if the requested coordinate conversion is possible. If returned, this +* Frame describes the coordinate system that results from applying the +* returned Mapping (above) to the "target" coordinate system. In +* general, this Frame will combine attributes from (and will therefore +* be more specific than) both the target and the template Frames. In +* particular, when the template allows the possibility of transformaing +* to any one of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate conversion is +* possible. Otherwise zero is returned (this will not in itself result in +* an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* This implementation addresses the matching of a DSBSpecFrame class +* object to any other class of Frame. A DSBSpecFrame will match any class +* of DSBSpecFrame (i.e. possibly from a derived class) but will not match +* a less specialised class of Frame (except for a SpecFrame). +*/ + +/* Local Variables: */ + AstDSBSpecFrame *template; /* Pointer to template DSBSpecFrame structure */ + AstFrame *frame0; /* Pointer to Frame underlying axis 0 */ + int iaxis0; /* Axis index underlying axis 0 */ + int match; /* Coordinate conversion possible? */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the template DSBSpecFrame structure. */ + template = (AstDSBSpecFrame *) template_frame; + +/* The first criterion for a match is that the template matches as a + SpecFrame class object. This ensures that the number of axes (1) and + domain, class, etc. of the target Frame are suitable. Invoke the parent + "astMatch" method to verify this. */ + match = (*parent_match)( template_frame, target, matchsub, + template_axes, target_axes, map, result, status ); + +/* If a match was found, the target Frame must be (or contain) a SpecFrame, + but this target SpecFrame may be a simple SpecFrame rather than a + DSBSpecFrame. We use the returned objects directly if the target + SpecFrame is not a DSBSpecFrame. So if a DSBSpecFrame and a base + SpecFrame are aligned, this will result in the DSBSpecFrame behaving as + a normal SpecFrame. */ + if ( astOK && match ) { + +/* Get the primary Frame associated with the matching target axis. */ + astPrimaryFrame( target, (*target_axes)[ 0 ], &frame0, &iaxis0 ); + +/* Skip this next section, thus retaining the values returned by the + parent Match method above, if the target axis is not a DSBSpecFrame. */ + if( astIsADSBSpecFrame( frame0 ) ) { + +/* Annul the returned objects, which are not needed, but keep the axis + association arrays which already hold the correct values. */ + *map = astAnnul( *map ); + *result = astAnnul( *result ); + +/* Use the target's "astSubFrame" method to create a new Frame (the + result Frame) with a copy of of the target axis. This process also + overlays the template attributes on to the target Frame and returns a + Mapping between the target and result Frames which effects the required + coordinate conversion. */ + match = astSubFrame( target, template, 1, *target_axes, *template_axes, + map, result ); + } + +/* Free resources. */ + frame0 = astAnnul( frame0 ); + + } + +/* If an error occurred, or conversion to the result Frame's coordinate + system was not possible, then free all memory, annul the returned + objects, and reset the returned value. */ + if ( !astOK || !match ) { + if( *template_axes ) *template_axes = astFree( *template_axes ); + if( *target_axes ) *target_axes = astFree( *target_axes ); + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void Overlay( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template DSBSpecFrame on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the protected astOverlay method +* inherited from the SpecFrame class). + +* Description: +* This function overlays attributes of a DSBSpecFrame (the "template") on to +* another Frame, so as to over-ride selected attributes of that second +* Frame. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which a Frame acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. +* +* Note that if the result Frame is a DSBSpecFrame and a change of spectral +* coordinate system occurs as a result of overlaying its System +* attribute, then some of its original attribute values may no +* longer be appropriate (e.g. the Title, or attributes describing +* its axes). In this case, these will be cleared before overlaying +* any new values. + +* Parameters: +* template +* Pointer to the template DSBSpecFrame, for which values should have been +* explicitly set for any attribute which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of the +* "result" Frame (see below). For each axis in the result frame, the +* corresponding element of this array should contain the (zero-based) +* index of the template axis to which it corresponds. This array is used +* to establish from which template axis any axis-dependent attributes +* should be obtained. +* +* If any axis in the result Frame is not associated with a template +* axis, the corresponding element of this array should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - In general, if the result Frame is not from the same class as the +* template DSBSpecFrame, or from a class derived from it, then attributes may +* exist in the template DSBSpecFrame which do not exist in the result Frame. +* In this case, these attributes will not be transferred. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent class astOverlay method to transfer attributes inherited + from the parent class. */ + (*parent_overlay)( template, template_axes, result, status ); + +/* Check if the result Frame is a DSBSpecFrame or from a class derived from + DSBSpecFrame. If not, we cannot transfer DSBSpecFrame attributes to it as it is + insufficiently specialised. In this case simply omit these attributes. */ + if( astIsADSBSpecFrame( result ) && astOK ) { + +/* Define macros that test whether an attribute is set in the template and, + if so, transfers its value to the result. */ +#define OVERLAY(attribute) \ + if ( astTest##attribute( template ) ) { \ + astSet##attribute( result, astGet##attribute( template ) ); \ + } + +/* Use the macro to transfer each DSBSpecFrame attribute in turn. */ + OVERLAY(DSBCentre) + OVERLAY(IF) + OVERLAY(SideBand) + OVERLAY(AlignSideBand) + } + +/* Undefine macros local to this function. */ +#undef OVERLAY +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a DSBSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the astSetAttrib protected +* method inherited from the SpecFrame class). + +* Description: +* This function assigns an attribute value for a DSBSpecFrame, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstDSBSpecFrame *this; /* Pointer to the DSBSpecFrame structure */ + AstMapping *tmap; /* Ptr to Mapping from this to topofreq */ + AstMapping *umap; /* Ptr to Mapping between units */ + double dtemp; /* Attribute value */ + double dval; /* Attribute value */ + int ival; /* Attribute value */ + int len; /* Length of setting string */ + int nc; /* Used length */ + int off; /* Offset to start of string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the DSBSpecFrame structure. */ + this = (AstDSBSpecFrame *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* DSBCentre */ +/* --------- */ + if ( strstr( setting, "dsbcentre=" ) ) { + +/* Without any units indication - assume it is supplied in the system of + the DSBSpecFrame. */ + int ok = 0; + if( nc = 0, + ( 1 == astSscanf( setting, "dsbcentre= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + ok = 1; + +/* With units indication. Is there a Mapping from the supplied units to the + units used by the DSBSpecFrame? If so, use the Mapping to convert the + supplied value to the required units. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "dsbcentre= %lg %n%*s %n", &dval, &off, &nc ) ) + && ( nc >= len ) ) { + + if( ( umap = astUnitMapper( setting + off, astGetUnit( this, 0 ), NULL, NULL ) ) ) { + astTran1( umap, 1, &dval, 1, &dtemp ); + dval = dtemp; + umap = astAnnul( umap ); + if( astOK && dval != AST__BAD ) ok = 1; + +/* Otherwise report an error. */ + } else if( astOK ) { + astError( AST__ATTIN, "astSetAttrib(%s): Value supplied for " + "attribute \"DSBCentre\" (%s) uses units which are " + "inappropriate for the current spectral system (%s).", status, + astGetClass( this ), setting + 10, + astGetTitle( this ) ); + } + } + +/* Convert the value from the supplied system to topocentric frequency in + Hx, and store. */ + if( ok ) { + +/* Find the Mapping from the spectral system described by this SpecFrame to + topocentric frequency in Hz. */ + tmap = TopoMap( this, 1, "astSetAttrib", status ); + if ( astOK ) { + +/* Transform the supplied value to topocentric frequency. */ + astTran1( tmap, 1, &dval, 1, &dtemp ); + if( dtemp == AST__BAD ) { + astError( AST__ATTIN, "astSetAttrib(%s): The setting \"%s\" is " + "invalid for a %s.", status, astGetClass( this ), setting, + astGetClass( this ) ); + } else { + +/* Store it. */ + astSetDSBCentre( this, dtemp ); + + } + tmap = astAnnul( tmap ); + } + + } else if( astOK ) { + astError( AST__ATTIN, "astSetAttrib(%s): The setting \"%s\" is " + "invalid for a %s.", status, astGetClass( this ), setting, + astGetClass( this ) ); + } + +/* IF */ +/* -- */ +/* Without any units indication - assume GHz. Convert to Hz for storage. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "if= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetIF( this, dval*1.0E9 ); + +/* With units indication. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "if= %lg %n%*s %n", &dval, &off, &nc ) ) + && ( nc >= len ) ) { + +/* Is there a Mapping from the supplied units to Hz? If so, use the + Mapping to convert the supplied value to Hz. */ + if( ( umap = astUnitMapper( setting + off, "Hz", NULL, NULL ) ) ) { + astTran1( umap, 1, &dval, 1, &dtemp ); + umap = astAnnul( umap ); + +/* Set the intermediate frequency. */ + astSetIF( this, dtemp ); + +/* Otherwise report an error. */ + } else if( astOK ) { + astError( AST__ATTIN, "astSetAttrib(%s): Intermediate frequency given " + "in an inappropriate system of units \"%g %s\".", status, + astGetClass( this ), dval, setting + off ); + } + +/* SideBand */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "sideband= %n%*s %n", &ival, &nc ) ) + && ( nc >= len ) ) { + + if( astChrMatch( "usb", setting+ival ) ) { + astSetSideBand( this, USB ); + + } else if( astChrMatch( "lsb", setting+ival ) ) { + astSetSideBand( this, LSB ); + + } else if( astChrMatch( "lo", setting+ival ) ) { + astSetSideBand( this, LO ); + + } else if( astChrMatch( "observed", setting+ival ) ) { + astSetSideBand( this, ( astGetIF( this ) > 0 ) ? LSB : USB ); + + } else if( astChrMatch( "image", setting+ival ) ) { + astSetSideBand( this, ( astGetIF( this ) <= 0 ) ? LSB : USB ); + + } else { + astError( AST__ATTIN, "astSetAttrib(%s): The setting \"%s\" is " + "invalid for a %s.", status, astGetClass( this ), setting, + astGetClass( this ) ); + } + +/* AlignSideBand */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "alignsideband= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetAlignSideBand( this, ival ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* Use this macro to report an error if a read-only attribute has been + specified. */ + } else if ( MATCH( "imagfreq" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a DSBSpecFrame and convert to the new coordinate +* system. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the protected astSubFrame +* method inherited from the SpecFrame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the axes +* from a "target" DSBSpecFrame and creates a new Frame with copies of +* the selected axes assembled in the requested order. It then +* optionally overlays the attributes of a "template" Frame on to the +* result. It returns both the resulting Frame and a Mapping that +* describes how to convert between the coordinate systems described by +* the target and result Frames. If necessary, this Mapping takes +* account of any differences in the Frames' attributes due to the +* influence of the template. + +* Parameters: +* target +* Pointer to the target DSBSpecFrame, from which axes are to be +* selected. +* template +* Pointer to the template Frame, from which new attributes for the +* result Frame are to be obtained. Optionally, this may be NULL, in +* which case no overlaying of template attributes will be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This number may +* be greater than or less than the number of axes in this Frame (or +* equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving a list +* of the (zero-based) axis indices of the axes to be selected from the +* target DSBSpecFrame. The order in which these are given determines +* the order in which the axes appear in the result Frame. If any of the +* values in this array is set to -1, the corresponding result axis will +* not be derived from the target Frame, but will be assigned default +* attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This should +* contain a list of the template axes (given as zero-based axis indices) +* with which the axes of the result Frame are to be associated. This +* array determines which axes are used when overlaying axis-dependent +* attributes of the template on to the result. If any element of this +* array is set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not used and +* a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned Mapping. +* The forward transformation of this Mapping will describe how to +* convert coordinates from the coordinate system described by the target +* DSBSpecFrame to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is possible +* between the target and the result Frame. Otherwise zero is returned and +* *map and *result are returned as NULL (but this will not in itself +* result in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not always +* be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* - This implementation addresses the selection of axes from a +* DSBSpecFrame object. This results in another object of the same class +* only if the single DSBSpecFrame axis is selected exactly once. +* Otherwise, the result is a Frame class object which inherits the +* DSBSpecFrame's axis information (if appropriate) but none of the other +* properties of a DSBSpecFrame. +* - In the event that a DSBSpecFrame results, the returned Mapping will +* take proper account of the relationship between the target and result +* coordinate systems. +* - In the event that a Frame class object results, the returned Mapping +* will only represent a selection/permutation of axes. + +* Implementation Deficiencies: +* - Any axis selection is currently permitted. Probably this should be +* restricted so that each axis can only be selected once. The +* astValidateAxisSelection method will do this but currently there are bugs +* in the CmpFrame class that cause axis selections which will not pass this +* test. Install the validation when these are fixed. +*/ + +/* Local Variables: */ + AstDSBSpecFrame *dsbresult;/* Pointer to the DSBSpecFrame result Frame */ + AstDSBSpecFrame *dsbtarget;/* Pointer to the DSBSpecFrame target Frame */ + AstMapping *map1; /* Intermediate Mapping */ + AstMapping *map2; /* Intermediate Mapping */ + AstMapping *map3; /* Intermediate Mapping */ + int alignsb; /* Use sidebands to align the Frames? */ + int match; /* Coordinate conversion is possible? */ + int obs_sb; /* The observed sideband value */ + int old_sb; /* The original Sideband value */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Invoke the astSubFrame method inherited from the parent SpecFrame + class. This will (if possible) create a result Frame which is a + DSBSpecFrame (since the supplied target Frame is a DSBSpecFrame). + However, the Mapping from target to result Frame will take no account + of any differences in the values of the attributes specific to the + DSBSpecFrame class. */ + match = (*parent_subframe)( target_frame, template, result_naxes, + target_axes, template_axes, map, result, status ); + +/* If a match occurred, and the result and template Frames are both + DSBSpecFrames, we now modify the Mapping to take account of + DSBSpecFrame-specific attributes. */ + if( match && template && astIsADSBSpecFrame( template ) && + astIsADSBSpecFrame( *result ) ) { + +/* Get pointers to the two DSBSpecFrames */ + dsbtarget = (AstDSBSpecFrame *) target_frame; + +/* See whether alignment occurs between sidebands. If the current call to + this function is part of the process of restoring a FrameSet's integrity + following changes to the FrameSet's current Frame, then we ignore the + setting of the AlignSideBand attributes and use 1. This ensures that + when the SideBand attribute (for instance) is changed via a FrameSet + pointer, the Mappings within the FrameSet are modified to produce + frequencies in the new SideBand. In most other cases, astronomers + usually want to align the DSBSpecFrames as if they were basic SpecFrames + (that is, ignoring the setting of the SideBand attribute). */ + if( astGetFrameFlags( target_frame ) & AST__INTFLAG ) { + alignsb = 1; + } else { + alignsb = astGetAlignSideBand( dsbtarget ) && + astGetAlignSideBand( (AstDSBSpecFrame *) template ); + } + +/* If we are aligning the sidebands we need to modify the Mapping + returned above by the parent SubFrame method. The existing Mapping + will convert between the spectral systems represented by the two + DSBSpecFrames but will not take account of any difference in + sidebands. */ + if( alignsb ) { + +/* We assume that alignment occurs in the observed sideband. Determine + which side band is the observed sideband in the target. */ + old_sb = astGetSideBand( dsbtarget ); + astSetC( dsbtarget, "SideBand", "observed" ); + obs_sb = astGetSideBand( dsbtarget ); + astSetSideBand( dsbtarget, old_sb ); + +/* Create a Mapping which transforms positions from the target to an exact + copy of the target in which the SideBand attribute is set to the + observed (USB or LSB) sideband. This will be a UnitMap if the target + already represents the observed sideband. */ + if( obs_sb == USB ) { + map1 = ToUSBMapping( dsbtarget, "astSubFrame", status ); + + } else if( obs_sb == LSB ) { + map1 = ToLSBMapping( dsbtarget, "astSubFrame", status ); + + } else { + map1 = NULL; + astError( AST__INTER, "astGetImagFreq(%s): Illegal sideband value " + "(%d) encountered (internal AST programming error).", status, + astGetClass( target_frame ), obs_sb ); + } + +/* Determine which side band is the observed sideband in the result. */ + dsbresult = (AstDSBSpecFrame *) *result; + old_sb = astGetSideBand( dsbresult ); + astSetC( dsbresult, "SideBand", "observed" ); + obs_sb = astGetSideBand( dsbresult ); + astSetSideBand( dsbresult, old_sb ); + +/* Create a Mapping which transforms positions from the result to an exact + copy of the result in which the SideBand attribute is set to the + obserfed sideband. This will be a UnitMap if the target already represents + the observed sideband. */ + if( obs_sb == USB ) { + map2 = ToUSBMapping( dsbresult, "astSubFrame", status ); + + } else if( obs_sb == LSB ) { + map2 = ToLSBMapping( dsbresult, "astSubFrame", status ); + + } else { + map2 = NULL; + astError( AST__INTER, "astGetImagFreq(%s): Illegal sideband value " + "(%d) encountered (internal AST programming error).", status, + astGetClass( target_frame ), obs_sb ); + } + +/* Invert it to get the mapping from the observed sideband to the result. */ + astInvert( map2 ); + +/* Form a Mapping which first maps target values to the observed sideband, + then applies the Mapping returned by the parent SubFrame method in + order to convert between spectral systems, and then converts from the + observed sideband to the SideBand of the result. */ + map3 = (AstMapping *) astCmpMap( map1, *map, 1, "", status ); + map1 = astAnnul( map1 ); + *map = astAnnul( *map ); + map1 = (AstMapping *) astCmpMap( map3, map2, 1, "", status ); + map3 = astAnnul( map3 ); + map2 = astAnnul( map2 ); + +/* Returned the simplified Mapping. */ + *map = astSimplify( map1 ); + map1 = astAnnul( map1 ); + } + } + +/* If an error occurred or no match was found, annul the returned + objects and reset the returned result. */ + if ( !astOK || !match ) { + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; + +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a DSBSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* DSBSpecFrame member function (over-rides the astTestAttrib protected +* method inherited from the SpecFrame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a DSBSpecFrame's attributes. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstDSBSpecFrame *this; /* Pointer to the DSBSpecFrame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the DSBSpecFrame structure. */ + this = (AstDSBSpecFrame *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* DSBCentre */ +/* --------- */ + if ( !strcmp( attrib, "dsbcentre" ) ) { + result = astTestDSBCentre( this ); + +/* IF */ +/* -- */ + } else if ( !strcmp( attrib, "if" ) ) { + result = astTestIF( this ); + +/* SideBand */ +/* -------- */ + } else if ( !strcmp( attrib, "sideband" ) ) { + result = astTestSideBand( this ); + +/* AlignSideBand */ +/* ------------- */ + } else if ( !strcmp( attrib, "alignsideband" ) ) { + result = astTestAlignSideBand( this ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute name matches any of the read-only attributes + of this class. If it does, then return zero. */ + } else if ( !strcmp( attrib, "imagfreq" ) ) { + result = 0; + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstMapping *ToLOMapping( AstDSBSpecFrame *this, const char *method, int *status ){ +/* +* Name: +* ToLOMapping + +* Purpose: +* Create a Mapping which transforms a DSBSpecFrame to offset from the +* local oscillator frequency. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* AstMapping *ToLOMapping( AstDSBSpecFrame *this, const char *method, int *status ) + +* Class Membership: +* DSBSpecFrame member function + +* Description: +* This function returns a pointer to a new Mapping which transforms +* positions in the supplied DSBSpecFrame into an offset from the local +* oscillator frequency. This will be a UnitMap if the DSBSpecFrame +* already represents offset from the local oscillator frequency. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* method +* Pointer to a null-terminated string containing the name of the +* public invoking method. This is only used in the construction of +* error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a new Mapping. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *fmap; /* LSB to USB (topo freq) */ + AstMapping *map1; /* This to USB (topo freq) */ + AstMapping *map2; /* This (LSB) to This (USB) */ + AstMapping *result; /* Pointer to the returned Mapping */ + AstMapping *tmap; /* This to topocentric freq */ + double f_lo; /* Local oscillator freq (topo Hz) */ + double f_in_a; /* First LSB or USB freq */ + double f_in_b; /* Second LSB or USB freq */ + double f_out_a; /* First LO freq */ + double f_out_b; /* Second LO freq */ + int sb; /* SideBand value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the DSBSpecFrame already represents LO offset, return a UnitMap.*/ + sb = astGetSideBand( this ); + if( sb == LO ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + +/* If the DSBSpecFrame represents the USB or LSB, create a suitable WinMap. */ + } else { + +/* Find the Mapping from the spectral system described by this SpecFrame to + topocentric frequency in Hz. */ + tmap = TopoMap( this, 1, method, status ); + +/* Calculate the local oscillator frequency (topocentric in Hertz). */ + f_lo = GetLO( this, "create a Mapping to upper sideband", + "astGetImagFreq", status ); + +/* Create a 1D WinMap which converts f_in to f_out. */ + if( sb == LSB ) { + f_in_a = 0.0; + f_in_b = f_lo; + f_out_a = f_lo; + f_out_b = 0.0; + } else { + f_in_a = 0.0; + f_in_b = -f_lo; + f_out_a = f_lo; + f_out_b = 0.0; + } + + fmap = (AstMapping *) astWinMap( 1, &f_in_a, &f_in_b, &f_out_a, &f_out_b, "", status ); + +/* Construct the Mapping: input to f_in, f_in to f_out, f_out to input */ + map1 = (AstMapping *) astCmpMap( tmap, fmap, 1, "", status ); + astInvert( tmap ); + map2 = (AstMapping *) astCmpMap( map1, tmap, 1, "", status ); + +/* Simplify */ + result = astSimplify( map2 ); + +/* Free resources */ + tmap = astAnnul( tmap ); + fmap = astAnnul( fmap ); + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + } + +/* Return NULL if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; + +} + +static AstMapping *ToLSBMapping( AstDSBSpecFrame *this, const char *method, int *status ){ +/* +* Name: +* ToLSBMapping + +* Purpose: +* Create a Mapping which transforms a DSBSpecFrame to the lower +* sideband. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* AstMapping *ToLSBMapping( AstDSBSpecFrame *this, const char *method, int *status ) + +* Class Membership: +* DSBSpecFrame member function + +* Description: +* This function returns a pointer to a new Mapping which transforms +* positions in the supplied DSBSpecFrame to the lower sideband. This +* will be a UnitMap if the DSBSpecFrame already represents the lower +* sideband. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* method +* Pointer to a null-terminated string containing the name of the +* public invoking method. This is only used in the construction of +* error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a new Mapping. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *fmap; /* LSB to USB (topo freq) */ + AstMapping *map1; /* This to USB (topo freq) */ + AstMapping *map2; /* This (LSB) to This (USB) */ + AstMapping *result; /* Pointer to the returned Mapping */ + AstMapping *tmap; /* This to topocentric freq */ + double f_lo; /* Local oscillator freq (topo Hz) */ + double f_out_a; /* First LSB freq */ + double f_out_b; /* Second LSB freq */ + double f_in_a; /* First USB or LO freq */ + double f_in_b; /* Second USB or LO freq */ + int sb; /* SideBand value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the DSBSpecFrame already represents the LSB, return a UnitMap.*/ + sb = astGetSideBand( this ); + if( sb == LSB ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + +/* If the DSBSpecFrame represents the USB or LO offset, create a suitable + WinMap. */ + } else { + +/* Find the Mapping from the spectral system described by this SpecFrame to + topocentric frequency in Hz. */ + tmap = TopoMap( this, 1, method, status ); + +/* Calculate the local oscillator frequency (topocentric in Hertz). */ + f_lo = GetLO( this, "create a Mapping to lower sideband", + "astGetImagFreq", status ); + +/* Create a 1D WinMap which converts USB or LO to LSB. */ + if( sb == USB ) { + f_in_a = 0.0; + f_in_b = 2*f_lo; + f_out_a = 2*f_lo; + f_out_b = 0.0; + } else { + f_in_a = 0.0; + f_in_b = f_lo; + f_out_a = f_lo; + f_out_b = 0.0; + } + + fmap = (AstMapping *) astWinMap( 1, &f_in_a, &f_in_b, &f_out_a, &f_out_b, "", status ); + +/* Construct the Mapping: input to f_in, f_in to f_out, f_out to input */ + map1 = (AstMapping *) astCmpMap( tmap, fmap, 1, "", status ); + astInvert( tmap ); + map2 = (AstMapping *) astCmpMap( map1, tmap, 1, "", status ); + +/* Simplify */ + result = astSimplify( map2 ); + +/* Free resources */ + tmap = astAnnul( tmap ); + fmap = astAnnul( fmap ); + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + } + +/* Return NULL if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; + +} + +static AstMapping *TopoMap( AstDSBSpecFrame *this, int forward, + const char *method, int *status ){ +/* +* Name: +* TopoMap + +* Purpose: +* Create a Mapping which transforms a DSBSpecFrame to topocentric +* frequency. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* AstMapping *TopoMap( AstDSBSpecFrame *this, int forward, +* const char *method, int *status ) + +* Class Membership: +* DSBSpecFrame member function + +* Description: +* This function returns a pointer to a new Mapping which transforms +* positions in the supplied DSBSpecFrame to the corresponding +* topocentric frequency values in Hz (or the inverse of this). + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* forward +* If zero, the calcuated Mapping is inverted before being returned. +* method +* Pointer to a null-terminated string containing the name of the +* public invoking method. This is only used in the construction of +* error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a new Mapping. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *result; /* The returned Mapping */ + AstFrameSet *fs; /* FrameSet connecting tf1 and tf2 */ + AstSpecFrame *tf1; /* SpecFrame corresponding to this DSBSpecFrame */ + AstSpecFrame *tf2; /* Topocentric frequency SpecFrame */ + int template_axis; /* The axis to overlay */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Make a SpecFrame and then overlay the SpecFrame attributes of this + DSBSpecFrame onto the new SpecFrame. This means it inherits the current + values of things like ObsLon and ObsLat. */ + tf1 = astSpecFrame( "", status ); + template_axis = 0; + (*parent_overlay)( (AstFrame *) this, &template_axis, (AstFrame *) tf1, status ); + +/* Copy this new SpecFrame and set its attributes to describe topocentric + frequency in Hz. Ensure that alignment occurs in the topocentric Frame. */ + astSetAlignStdOfRest( tf1, AST__TPSOR); + tf2 = astCopy( tf1 ); + astSetSystem( tf2, AST__FREQ ); + astSetStdOfRest( tf2, AST__TPSOR ); + astSetUnit( tf2, 0, "Hz" ); + +/* Find the Mapping from the spectral system described by this SpecFrame to + topocentric frequency in Hz. */ + fs = astConvert( tf1, tf2, "" ); + if ( astOK ) { + if( !fs ) { + astError( AST__INTER, "%s(%s): Cannot convert DSBCentre " + "value from the supplied system to topocentric frequency " + "(internal AST programming error).", status, method, + astGetClass( this ) ); + } else { + result = astGetMapping( fs, AST__BASE, AST__CURRENT ); + if( !forward ) astInvert( result ); + } + fs = astAnnul( fs ); + } + +/* Free resources */ + tf1 = astAnnul( tf1 ); + tf2 = astAnnul( tf2 ); + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; + +} + +static AstMapping *ToUSBMapping( AstDSBSpecFrame *this, const char *method, int *status ){ +/* +* Name: +* ToUSBMapping + +* Purpose: +* Create a Mapping which transforms a DSBSpecFrame to the upper +* sideband. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* AstMapping *ToUSBMapping( AstDSBSpecFrame *this, const char *method, int *status ) + +* Class Membership: +* DSBSpecFrame member function + +* Description: +* This function returns a pointer to a new Mapping which transforms +* positions in the supplied DSBSpecFrame to the upper sideband. This +* will be a UnitMap if the DSBSpecFrame already represents the upper +* sideband. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* method +* Pointer to a null-terminated string containing the name of the +* public invoking method. This is only used in the construction of +* error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a new Mapping. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *fmap; /* LSB to USB (topo freq) */ + AstMapping *map1; /* This to USB (topo freq) */ + AstMapping *map2; /* This (LSB) to This (USB) */ + AstMapping *result; /* Pointer to the returned Mapping */ + AstMapping *tmap; /* This to topocentric freq */ + double f_lo; /* Local oscillator freq (topo Hz) */ + double f_in_a; /* First LSB or LO freq */ + double f_in_b; /* Second LSB or LO freq */ + double f_out_a; /* First USB freq */ + double f_out_b; /* Second USB freq */ + int sb; /* SideBand value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the DSBSpecFrame already represents the USB, return a UnitMap.*/ + sb = astGetSideBand( this ); + if( sb == USB ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + +/* If the DSBSpecFrame represents the LSB, or LO offset, create a suitable + WinMap. */ + } else { + +/* Find the Mapping from the spectral system described by this SpecFrame to + topocentric frequency in Hz. */ + tmap = TopoMap( this, 1, method, status ); + +/* Calculate the local oscillator frequency (topocentric in Hertz). */ + f_lo = GetLO( this, "create a Mapping to upper sideband", + "astGetImagFreq", status ); + +/* Create a 1D WinMap which converts f_in to f_out. */ + if( sb == LSB ) { + f_in_a = 0.0; + f_in_b = 2*f_lo; + f_out_a = 2*f_lo; + f_out_b = 0.0; + } else { + f_in_a = 0.0; + f_in_b = -f_lo; + f_out_a = f_lo; + f_out_b = 0.0; + } + + fmap = (AstMapping *) astWinMap( 1, &f_in_a, &f_in_b, &f_out_a, &f_out_b, "", status ); + +/* Construct the Mapping: input to f_in, f_in to f_out, f_out to input */ + map1 = (AstMapping *) astCmpMap( tmap, fmap, 1, "", status ); + astInvert( tmap ); + map2 = (AstMapping *) astCmpMap( map1, tmap, 1, "", status ); + +/* Simplify */ + result = astSimplify( map2 ); + +/* Free resources */ + tmap = astAnnul( tmap ); + fmap = astAnnul( fmap ); + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + } + +/* Return NULL if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; + +} + +static void VerifyAttrs( AstDSBSpecFrame *this, const char *purp, + const char *attrs, const char *method, int *status ) { +/* +* Name: +* VerifyAttrs + +* Purpose: +* Verify that usable attribute values are available. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* void VerifyAttrs( AstDSBSpecFrame *this, const char *purp, +* const char *attrs, const char *method, int *status ) + +* Class Membership: +* DSBSpecFrame member function + +* Description: +* This function tests each attribute listed in "attrs". It returns +* without action if 1) an explicit value has been set for each attribute +* or 2) the UseDefs attribute of the supplied DSBSpecFrame is non-zero. +* +* If UseDefs is zero (indicating that default values should not be +* used for attributes), and any of the named attributes does not have +* an explicitly set value, then an error is reported. + +* Parameters: +* this +* Pointer to the DSBSpecFrame. +* purp +* Pointer to a text string containing a message which will be +* included in any error report. This shouldindicate the purpose +* for which the attribute value is required. +* attrs +* A string holding a space separated list of attribute names. +* method +* A string holding the name of the calling method for use in error +* messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + const char *a; + const char *desc; + const char *p; + int len; + int set; + int state; + +/* Check inherited status */ + if( !astOK ) return; + +/* If the DSBSpecFrame has a non-zero value for its UseDefs attribute, then + all attributes are assumed to have usable values, since the defaults + will be used if no explicit value has been set. So we only need to do + any checks if UseDefs is zero. */ + if( !astGetUseDefs( this ) ) { + +/* Initialise variables to avoid compiler warnings. */ + a = NULL; + desc = NULL; + len = 0; + set = 0; + +/* Loop round the "attrs" string identifying the start and length of each + non-blank word in the string. */ + state = 0; + p = attrs; + while( 1 ) { + if( state == 0 ) { + if( !isspace( *p ) ) { + a = p; + len = 1; + state = 1; + } + } else { + if( isspace( *p ) || !*p ) { + +/* The end of a word has just been reached. Compare it to each known + attribute value. Get a flag indicating if the attribute has a set + value, and a string describing the attribute.*/ + if( len > 0 ) { + + if( !strncmp( "DSBCentre", a, len ) ) { + set = astTestDSBCentre( this ); + desc = "central position of interest"; + + } else if( !strncmp( "IF", a, len ) ) { + set = astTestIF( this ); + desc = "intermediate frequency"; + + } else { + astError( AST__INTER, "VerifyAttrs(DSBSpecFrame): " + "Unknown attribute name \"%.*s\" supplied (AST " + "internal programming error).", status, len, a ); + } + +/* If the attribute does not have a set value, report an error. */ + if( !set && astOK ) { + astError( AST__NOVAL, "%s(%s): Cannot %s.", status, method, + astGetClass( this ), purp ); + astError( AST__NOVAL, "No value has been set for " + "the AST \"%.*s\" attribute (%s).", status, len, a, + desc ); + } + +/* Continue the word search algorithm. */ + } + len = 0; + state = 0; + } else { + len++; + } + } + if( !*(p++) ) break; + } + } +} + + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* ImagFreq + +* Purpose: +* The image sideband equivalent of the rest frequency. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point, read-only. + +* Description: +* This is a read-only attribute giving the frequency which +* corresponds to the rest frequency but is in the opposite sideband. + +* The value is calculated by first transforming the rest frequency +* (given by the RestFreq attribute) from the standard of rest of the +* source (given by the SourceVel and SourceVRF attributes) to the +* standard of rest of the observer (i.e. the topocentric standard of +* rest). The resulting topocentric frequency is assumed to be in the +* same sideband as the value given for the DSBCentre attribute (the +* "observed" sideband), and is transformed to the other sideband (the +* "image" sideband). The new frequency is converted back to the standard +* of rest of the source, and the resulting value is returned as the +* attribute value, in units of GHz. + +* Applicability: +* DSBSpecFrame +* All DSBSpecFrames have this attribute. + +*att-- +*/ + + +/* +*att++ +* Name: +* DSBCentre + +* Purpose: +* The central position of interest in a dual sideband spectrum. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the central position of interest in a dual +* sideband spectrum. Its sole use is to determine the local oscillator +* frequency (the frequency which marks the boundary between the lower +* and upper sidebands). See the description of the IF (intermediate +* frequency) attribute for details of how the local oscillator frequency +* is calculated. The sideband containing this central position is +* referred to as the "observed" sideband, and the other sideband as +* the "image" sideband. +* +* The value is accessed as a position in the spectral system +* represented by the SpecFrame attributes inherited by this class, but +* is stored internally as topocentric frequency. Thus, if the System +* attribute of the DSBSpecFrame is set to "VRAD", the Unit attribute +* set to "m/s" and the StdOfRest attribute set to "LSRK", then values +* for the DSBCentre attribute should be supplied as radio velocity in +* units of "m/s" relative to the kinematic LSR (alternative units may +* be used by appending a suitable units string to the end of the value). +* This value is then converted to topocentric frequency and stored. If +* (say) the Unit attribute is subsequently changed to "km/s" before +* retrieving the current value of the DSBCentre attribute, the stored +* topocentric frequency will be converted back to LSRK radio velocity, +* this time in units of "km/s", before being returned. +* +* The default value for this attribute is 30 GHz. + +* Applicability: +* DSBSpecFrame +* All DSBSpecFrames have this attribute. + +* Note: +* - The attributes which define the transformation to or from topocentric +* frequency should be assigned their correct values before accessing +* this attribute. These potentially include System, Unit, StdOfRest, +* ObsLon, ObsLat, ObsAlt, Epoch, RefRA, RefDec and RestFreq. + +*att-- +*/ +/* The central frequency (topocentric frequency in Hz). */ +astMAKE_CLEAR(DSBSpecFrame,DSBCentre,dsbcentre,AST__BAD) +astMAKE_GET(DSBSpecFrame,DSBCentre,double,3.0E10,((this->dsbcentre!=AST__BAD)?this->dsbcentre:3.0E10)) +astMAKE_SET(DSBSpecFrame,DSBCentre,double,dsbcentre,value) +astMAKE_TEST(DSBSpecFrame,DSBCentre,( this->dsbcentre != AST__BAD )) + + +/* +*att++ +* Name: +* IF + +* Purpose: +* The intermediate frequency in a dual sideband spectrum. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the (topocentric) intermediate frequency in +* a dual sideband spectrum. Its sole use is to determine the local +* oscillator (LO) frequency (the frequency which marks the boundary +* between the lower and upper sidebands). The LO frequency is +* equal to the sum of the centre frequency and the intermediate +* frequency. Here, the "centre frequency" is the topocentric +* frequency in Hz corresponding to the current value of the DSBCentre +* attribute. The value of the IF attribute may be positive or +* negative: a positive value results in the LO frequency being above +* the central frequency, whilst a negative IF value results in the LO +* frequency being below the central frequency. The sign of the IF +* attribute value determines the default value for the SideBand +* attribute. +* +* When setting a new value for this attribute, the units in which the +* frequency value is supplied may be indicated by appending a suitable +* string to the end of the formatted value. If the units are not +* specified, then the supplied value is assumed to be in units of GHz. +* For instance, the following strings all result in an IF of 4 GHz being +* used: "4.0", "4.0 GHz", "4.0E9 Hz", etc. +* +* When getting the value of this attribute, the returned value is +* always in units of GHz. The default value for this attribute is 4 GHz. + +* Applicability: +* DSBSpecFrame +* All DSBSpecFrames have this attribute. + +*att-- +*/ +/* The intermediate frequency (topocentric in Hz). */ +astMAKE_CLEAR(DSBSpecFrame,IF,ifr,AST__BAD) +astMAKE_GET(DSBSpecFrame,IF,double,4.0E9,((this->ifr!=AST__BAD)?this->ifr:4.0E9)) +astMAKE_SET(DSBSpecFrame,IF,double,ifr,value) +astMAKE_TEST(DSBSpecFrame,IF,( this->ifr != AST__BAD )) + +/* +*att++ +* Name: +* SideBand + +* Purpose: +* Indicates which sideband a dual sideband spectrum represents. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute indicates whether the DSBSpecFrame currently +* represents its lower or upper sideband, or an offset from the local +* oscillator frequency. When querying the current value, the returned +* string is always one of "usb" (for upper sideband), "lsb" (for lower +* sideband), or "lo" (for offset from the local oscillator frequency). +* When setting a new value, any of the strings "lsb", "usb", "observed", +* "image" or "lo" may be supplied (case insensitive). The "observed" +* sideband is which ever sideband (upper or lower) contains the central +* spectral position given by attribute DSBCentre, and the "image" +* sideband is the other sideband. It is the sign of the IF attribute +* which determines if the observed sideband is the upper or lower +* sideband. The default value for SideBand is the observed sideband. + +* Applicability: +* DSBSpecFrame +* All DSBSpecFrames have this attribute. + +*att-- +*/ +/* Protected access to the SideBand attribute uses BADSB to indicate + "unset". Other negative values mean "LSB", zero means "LO" and + positive values mean "USB". */ +astMAKE_CLEAR(DSBSpecFrame,SideBand,sideband,BADSB) +astMAKE_SET(DSBSpecFrame,SideBand,int,sideband,((value<0)?LSB:((value==0)?LO:USB))) +astMAKE_TEST(DSBSpecFrame,SideBand,( this->sideband != BADSB )) +astMAKE_GET(DSBSpecFrame,SideBand,int,USB,(this->sideband == BADSB ? ((astGetIF( this )>0)?LSB:USB):this->sideband)) + +/* +*att++ +* Name: +* AlignSideBand + +* Purpose: +* Should the SideBand attribute be taken into account when aligning +* this DSBSpecFrame with another DSBSpecFrame? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls how a DSBSpecFrame behaves when an attempt +* is made to align it with another DSBSpecFrame using +c astFindFrame or astConvert. +f AST_FINDFRAME or AST_CONVERT. +* If both DSBSpecFrames have a non-zero value for AlignSideBand, the +* value of the SideBand attribute in each DSBSpecFrame is used so that +* alignment occurs between sidebands. That is, if one DSBSpecFrame +* represents USB and the other represents LSB then +c astFindFrame and astConvert +f AST_FINDFRAME and AST_CONVERT +* will recognise that the DSBSpecFrames represent different sidebands +* and will take this into account when constructing the Mapping that +* maps positions in one DSBSpecFrame into the other. If AlignSideBand +* in either DSBSpecFrame is set to zero, then the values of the SideBand +* attributes are ignored. In the above example, this would result in a +* frequency in the first DSBSpecFrame being mapped onto the same +* frequency in the second DSBSpecFrame, even though those frequencies +* refer to different sidebands. In other words, if either AlignSideBand +* attribute is zero, then the two DSBSpecFrames aligns like basic +* SpecFrames. The default value for AlignSideBand is zero. +* +c When astFindFrame or astConvert +f When AST_FINDFRAME or AST_CONVERT +* is used on two DSBSpecFrames (potentially describing different spectral +* coordinate systems and/or sidebands), it returns a Mapping which can be +* used to transform a position in one DSBSpecFrame into the corresponding +* position in the other. The Mapping is made up of the following steps in +* the indicated order: +* +* - If both DSBSpecFrames have a value of 1 for the AlignSideBand +* attribute, map values from the target's current sideband (given by its +* SideBand attribute) to the observed sideband (whether USB or LSB). If +* the target already represents the observed sideband, this step will +* leave the values unchanged. If either of the two DSBSpecFrames have a +* value of zero for its AlignSideBand attribute, then this step is omitted. +* +* - Map the values from the spectral system of the target to the spectral +* system of the template. This Mapping takes into account all the +* inherited SpecFrame attributes such as System, StdOfRest, Unit, etc. +* +* - If both DSBSpecFrames have a value of 1 for the AlignSideBand +* attribute, map values from the result's observed sideband to the +* result's current sideband (given by its SideBand attribute). If the +* result already represents the observed sideband, this step will leave +* the values unchanged. If either of the two DSBSpecFrames have a value +* of zero for its AlignSideBand attribute, then this step is omitted. + +* Applicability: +* DSBSpecFrame +* All DSBSpecFrames have this attribute. + +*att-- +*/ +/* The AlignSideBand value has a value of -1 when not set yielding a + default of 0. */ +astMAKE_TEST(DSBSpecFrame,AlignSideBand,( this->alignsideband != -1 )) +astMAKE_CLEAR(DSBSpecFrame,AlignSideBand,alignsideband,-1) +astMAKE_GET(DSBSpecFrame,AlignSideBand,int,-1,((this->alignsideband==-1)?0:this->alignsideband) ) +astMAKE_SET(DSBSpecFrame,AlignSideBand,int,alignsideband,(value?1:0)) + +/* Copy constructor. */ +/* ----------------- */ +/* None needed */ + +/* Destructor. */ +/* ----------- */ +/* None needed */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for DSBSpecFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the DSBSpecFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the DSBSpecFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstDSBSpecFrame *this; /* Pointer to the DSBSpecFrame structure */ + const char *cval; /* Attribute value */ + const char *comm; /* Attribute comment */ + double dval; /* Attribute value */ + int ival; /* Attribute value */ + int set; /* Is attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the DSBSpecFrame structure. */ + this = (AstDSBSpecFrame *) this_object; + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* DSBCentre */ +/* --------- */ + set = TestDSBCentre( this, status ); + dval = set ? GetDSBCentre( this, status ) : astGetDSBCentre( this ); + astWriteDouble( channel, "DSBCen", set, 1, dval, "Central frequency (Hz topo)" ); + +/* IF */ +/* -- */ + set = TestIF( this, status ); + dval = set ? GetIF( this, status ) : astGetIF( this ); + astWriteDouble( channel, "IF", set, 1, dval, "Intermediate frequency (Hz)" ); + +/* SideBand */ +/* -------- */ + set = TestSideBand( this, status ); + ival = set ? GetSideBand( this, status ) : astGetSideBand( this ); + if( ival == LSB ) { + cval = "LSB"; + comm = "Represents lower sideband"; + + } else if( ival == LO ) { + cval = "LO"; + comm = "Represents offset from LO frequency"; + + } else { + cval = "USB"; + comm = "Represents upper sideband"; + } + astWriteString( channel, "SideBn", set, 1, cval, comm ); + +/* AlignSideBand */ +/* ------------- */ + set = TestAlignSideBand( this, status ); + ival = set ? GetAlignSideBand( this, status ) : astGetAlignSideBand( this ); + astWriteInt( channel, "AlSdBn", set, 1, ival, "Align sidebands?" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsADSBSpecFrame and astCheckDSBSpecFrame functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(DSBSpecFrame,SpecFrame) +astMAKE_CHECK(DSBSpecFrame) + +AstDSBSpecFrame *astDSBSpecFrame_( const char *options, int *status, ...) { +/* +*++ +* Name: +c astDSBSpecFrame +f AST_DSBSPECFRAME + +* Purpose: +* Create a DSBSpecFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "dsbspecframe.h" +c AstDSBSpecFrame *astDSBSpecFrame( const char *options, ... ) +f RESULT = AST_DSBSPECFRAME( OPTIONS, STATUS ) + +* Class Membership: +* DSBSpecFrame constructor. + +* Description: +* This function creates a new DSBSpecFrame and optionally initialises its +* attributes. +* +* A DSBSpecFrame is a specialised form of SpecFrame which represents +* positions in a spectrum obtained using a dual sideband instrument. +* Such an instrument produces a spectrum in which each point contains +* contributions from two distinctly different frequencies, one from +* the "lower side band" (LSB) and one from the "upper side band" (USB). +* Corresponding LSB and USB frequencies are connected by the fact +* that they are an equal distance on either side of a fixed central +* frequency known as the "Local Oscillator" (LO) frequency. +* +* When quoting a position within such a spectrum, it is necessary to +* indicate whether the quoted position is the USB position or the +* corresponding LSB position. The SideBand attribute provides this +* indication. Another option that the SideBand attribute provides is +* to represent a spectral position by its topocentric offset from the +* LO frequency. +* +* In practice, the LO frequency is specified by giving the distance +* from the LO frequency to some "central" spectral position. Typically +* this central position is that of some interesting spectral feature. +* The distance from this central position to the LO frequency is known +* as the "intermediate frequency" (IF). The value supplied for IF can +* be a signed value in order to indicate whether the LO frequency is +* above or below the central position. + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new DSBSpecFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new DSBSpecFrame. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astDSBSpecFrame() +f AST_DSBSPECFRAME = INTEGER +* A pointer to the new DSBSpecFrame. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstDSBSpecFrame *new; /* Pointer to new DSBSpecFrame */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the DSBSpecFrame, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitDSBSpecFrame( NULL, sizeof( AstDSBSpecFrame ), !class_init, &class_vtab, + "DSBSpecFrame" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new DSBSpecFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new DSBSpecFrame. */ + return new; +} + +AstDSBSpecFrame *astDSBSpecFrameId_( const char *options, ... ) { +/* +* Name: +* astDSBSpecFrameId_ + +* Purpose: +* Create a DSBSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "dsbspecframe.h" +* AstDSBSpecFrame *astDSBSpecFrameId_( const char *options, ... ) + +* Class Membership: +* DSBSpecFrame constructor. + +* Description: +* This function implements the external (public) interface to the +* astDSBSpecFrame constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astDSBSpecFrame_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astDSBSpecFrame_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astDSBSpecFrame_. + +* Returned Value: +* The ID value associated with the new DSBSpecFrame. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstDSBSpecFrame *new; /* Pointer to new DSBSpecFrame */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the DSBSpecFrame, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitDSBSpecFrame( NULL, sizeof( AstDSBSpecFrame ), !class_init, &class_vtab, + "DSBSpecFrame" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new DSBSpecFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new DSBSpecFrame. */ + return astMakeId( new ); +} + +AstDSBSpecFrame *astInitDSBSpecFrame_( void *mem, size_t size, int init, + AstDSBSpecFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitDSBSpecFrame + +* Purpose: +* Initialise a DSBSpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "dsbspecframe.h" +* AstDSBSpecFrame *astInitDSBSpecFrame( void *mem, size_t size, int init, +* AstDSBSpecFrameVtab *vtab, const char *name ) + +* Class Membership: +* DSBSpecFrame initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new DSBSpecFrame object. It allocates memory (if necessary) to accommodate +* the DSBSpecFrame plus any additional data associated with the derived class. +* It then initialises a DSBSpecFrame structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a DSBSpecFrame at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the DSBSpecFrame is to be initialised. +* This must be of sufficient size to accommodate the DSBSpecFrame data +* (sizeof(DSBSpecFrame)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the DSBSpecFrame (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the DSBSpecFrame +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the DSBSpecFrame's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new DSBSpecFrame. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). + +* Returned Value: +* A pointer to the new DSBSpecFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstDSBSpecFrame *new; /* Pointer to new DSBSpecFrame */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitDSBSpecFrameVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a SpecFrame structure (the parent class) as the first component + within the DSBSpecFrame structure, allocating memory if necessary. Specify that + the SpecFrame should be defined in both the forward and inverse directions. */ + new = (AstDSBSpecFrame *) astInitSpecFrame( mem, size, 0, + (AstSpecFrameVtab *) vtab, name ); + if ( astOK ) { + +/* Initialise the DSBSpecFrame data. */ +/* --------------------------------- */ + new->dsbcentre = AST__BAD; + new->ifr = AST__BAD; + new->sideband = BADSB; + new->alignsideband = -1; + +/* If an error occurred, clean up by deleting the new DSBSpecFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new DSBSpecFrame. */ + return new; +} + +AstDSBSpecFrame *astLoadDSBSpecFrame_( void *mem, size_t size, + AstDSBSpecFrameVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadDSBSpecFrame + +* Purpose: +* Load a DSBSpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "dsbspecframe.h" +* AstDSBSpecFrame *astLoadDSBSpecFrame( void *mem, size_t size, +* AstDSBSpecFrameVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* DSBSpecFrame loader. + +* Description: +* This function is provided to load a new DSBSpecFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* DSBSpecFrame structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a DSBSpecFrame at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the DSBSpecFrame is to be +* loaded. This must be of sufficient size to accommodate the +* DSBSpecFrame data (sizeof(DSBSpecFrame)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the DSBSpecFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the DSBSpecFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstDSBSpecFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new DSBSpecFrame. If this is NULL, a pointer +* to the (static) virtual function table for the DSBSpecFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "DSBSpecFrame" is used instead. + +* Returned Value: +* A pointer to the new DSBSpecFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstDSBSpecFrame *new; /* Pointer to the new DSBSpecFrame */ + char *text; /* Pointer to string value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this DSBSpecFrame. In this case the + DSBSpecFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstDSBSpecFrame ); + vtab = &class_vtab; + name = "DSBSpecFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitDSBSpecFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built DSBSpecFrame. */ + new = astLoadSpecFrame( mem, size, (AstSpecFrameVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "DSBSpecFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* DSBCentre */ +/* --------- */ + new->dsbcentre = astReadDouble( channel, "dsbcen", AST__BAD ); + if ( TestDSBCentre( new, status ) ) SetDSBCentre( new, new->dsbcentre, status ); + +/* IF */ +/* -- */ + new->ifr = astReadDouble( channel, "if", AST__BAD ); + if ( TestIF( new, status ) ) SetIF( new, new->ifr, status ); + +/* SideBand */ +/* -------- */ + text = astReadString( channel, "sidebn", " " ); + if( astOK ) { + if( !strcmp( text, " " ) ) { + new->sideband = BADSB; + } else if( !strcmp( text, "USB" ) ) { + new->sideband = USB; + } else if( !strcmp( text, "LSB" ) ) { + new->sideband = LSB; + } else if( !strcmp( text, "LO" ) ) { + new->sideband = LO; + } else { + astError( AST__ATTIN, "astRead(%s): Invalid SideBand description " + "\"%s\".", status, astGetClass( channel ), text ); + } + if ( TestSideBand( new, status ) ) SetSideBand( new, new->sideband, status ); + text = astFree( text ); + } + +/* AlignSideBand */ +/* ------------- */ + new->alignsideband = astReadInt( channel, "alsdbn", -1 ); + if( TestAlignSideBand( new, status ) ) SetAlignSideBand( new, new->alignsideband, status ); + +/* If an error occurred, clean up by deleting the new DSBSpecFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new DSBSpecFrame pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +double astGetImagFreq_( AstDSBSpecFrame *this, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,DSBSpecFrame,GetImagFreq))( this, status ); +} + + + + + + diff --git a/dsbspecframe.h b/dsbspecframe.h new file mode 100644 index 0000000..87617f5 --- /dev/null +++ b/dsbspecframe.h @@ -0,0 +1,298 @@ +#if !defined( DSBSPECFRAME_INCLUDED ) /* Include this file only once */ +#define DSBSPECFRAME_INCLUDED +/* +*+ +* Name: +* dsbspecframe.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the DSBSpecFrame class. + +* Invocation: +* #include "dsbspecframe.h" + +* Description: +* This include file defines the interface to the DSBSpecFrame class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The DSBSpecFrame class inherits from the SpecFrame class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 5-AUG-2004 (DSB): +* Original version. +* 27-OCT-2006 (DSB): +* Added AlignSideBand. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "specframe.h" /* Spectral coord systems (parent class) */ + +/* C header files. */ +/* --------------- */ + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* DSBSpecFrame structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstDSBSpecFrame { + +/* Attributes inherited from the parent class. */ + AstSpecFrame specframe; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double dsbcentre; /* Centre frequency */ + double ifr; /* Intermediate frequency */ + int sideband; /* Current sideband */ + int alignsideband; /* Aligns sidebands? */ + +} AstDSBSpecFrame; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstDSBSpecFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstSpecFrameVtab specframe_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + double (* GetDSBCentre)( AstDSBSpecFrame *, int * ); + int (* TestDSBCentre)( AstDSBSpecFrame *, int * ); + void (* ClearDSBCentre)( AstDSBSpecFrame *, int * ); + void (* SetDSBCentre)( AstDSBSpecFrame *, double, int * ); + + double (* GetIF)( AstDSBSpecFrame *, int * ); + int (* TestIF)( AstDSBSpecFrame *, int * ); + void (* ClearIF)( AstDSBSpecFrame *, int * ); + void (* SetIF)( AstDSBSpecFrame *, double, int * ); + + int (* GetSideBand)( AstDSBSpecFrame *, int * ); + int (* TestSideBand)( AstDSBSpecFrame *, int * ); + void (* ClearSideBand)( AstDSBSpecFrame *, int * ); + void (* SetSideBand)( AstDSBSpecFrame *, int, int * ); + + int (* GetAlignSideBand)( AstDSBSpecFrame *, int * ); + int (* TestAlignSideBand)( AstDSBSpecFrame *, int * ); + void (* ClearAlignSideBand)( AstDSBSpecFrame *, int * ); + void (* SetAlignSideBand)( AstDSBSpecFrame *, int, int * ); + + double (* GetImagFreq)( AstDSBSpecFrame *, int * ); + +} AstDSBSpecFrameVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstDSBSpecFrameGlobals { + AstDSBSpecFrameVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; + char GetLabel_Buff[ 101 ]; +} AstDSBSpecFrameGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(DSBSpecFrame) /* Check class membership */ +astPROTO_ISA(DSBSpecFrame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstDSBSpecFrame *astDSBSpecFrame_( const char *, int *, ...); +#else +AstDSBSpecFrame *astDSBSpecFrameId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstDSBSpecFrame *astInitDSBSpecFrame_( void *, size_t, int, AstDSBSpecFrameVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitDSBSpecFrameVtab_( AstDSBSpecFrameVtab *, const char *, int * ); + +/* Loader. */ +AstDSBSpecFrame *astLoadDSBSpecFrame_( void *, size_t, AstDSBSpecFrameVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitDSBSpecFrameGlobals_( AstDSBSpecFrameGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ + double astGetDSBCentre_( AstDSBSpecFrame *, int * ); + int astTestDSBCentre_( AstDSBSpecFrame *, int * ); + void astClearDSBCentre_( AstDSBSpecFrame *, int * ); + void astSetDSBCentre_( AstDSBSpecFrame *, double, int * ); + + double astGetIF_( AstDSBSpecFrame *, int * ); + int astTestIF_( AstDSBSpecFrame *, int * ); + void astClearIF_( AstDSBSpecFrame *, int * ); + void astSetIF_( AstDSBSpecFrame *, double, int * ); + + int astGetSideBand_( AstDSBSpecFrame *, int * ); + int astTestSideBand_( AstDSBSpecFrame *, int * ); + void astClearSideBand_( AstDSBSpecFrame *, int * ); + void astSetSideBand_( AstDSBSpecFrame *, int, int * ); + + int astGetAlignSideBand_( AstDSBSpecFrame *, int * ); + int astTestAlignSideBand_( AstDSBSpecFrame *, int * ); + void astClearAlignSideBand_( AstDSBSpecFrame *, int * ); + void astSetAlignSideBand_( AstDSBSpecFrame *, int, int * ); + + double astGetImagFreq_( AstDSBSpecFrame *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckDSBSpecFrame(this) astINVOKE_CHECK(DSBSpecFrame,this,0) +#define astVerifyDSBSpecFrame(this) astINVOKE_CHECK(DSBSpecFrame,this,1) + +/* Test class membership. */ +#define astIsADSBSpecFrame(this) astINVOKE_ISA(DSBSpecFrame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astDSBSpecFrame astINVOKE(F,astDSBSpecFrame_) +#else +#define astDSBSpecFrame astINVOKE(F,astDSBSpecFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define \ +astInitDSBSpecFrame(mem,size,init,vtab,name) \ +astINVOKE(O,astInitDSBSpecFrame_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitDSBSpecFrameVtab(vtab,name) astINVOKE(V,astInitDSBSpecFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadDSBSpecFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadDSBSpecFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckDSBSpecFrame to validate DSBSpecFrame pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ + +#define astGetDSBCentre(this) \ +astINVOKE(V,astGetDSBCentre_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astTestDSBCentre(this) \ +astINVOKE(V,astTestDSBCentre_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astClearDSBCentre(this) \ +astINVOKE(V,astClearDSBCentre_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astSetDSBCentre(this,val) \ +astINVOKE(V,astSetDSBCentre_(astCheckDSBSpecFrame(this),val,STATUS_PTR)) + +#define astGetIF(this) \ +astINVOKE(V,astGetIF_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astTestIF(this) \ +astINVOKE(V,astTestIF_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astClearIF(this) \ +astINVOKE(V,astClearIF_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astSetIF(this,val) \ +astINVOKE(V,astSetIF_(astCheckDSBSpecFrame(this),val,STATUS_PTR)) + +#define astGetSideBand(this) \ +astINVOKE(V,astGetSideBand_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astTestSideBand(this) \ +astINVOKE(V,astTestSideBand_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astClearSideBand(this) \ +astINVOKE(V,astClearSideBand_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astSetSideBand(this,val) \ +astINVOKE(V,astSetSideBand_(astCheckDSBSpecFrame(this),val,STATUS_PTR)) + +#define astGetAlignSideBand(this) \ +astINVOKE(V,astGetAlignSideBand_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astTestAlignSideBand(this) \ +astINVOKE(V,astTestAlignSideBand_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astClearAlignSideBand(this) \ +astINVOKE(V,astClearAlignSideBand_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#define astSetAlignSideBand(this,val) \ +astINVOKE(V,astSetAlignSideBand_(astCheckDSBSpecFrame(this),val,STATUS_PTR)) + +#define astGetImagFreq(this) \ +astINVOKE(V,astGetImagFreq_(astCheckDSBSpecFrame(this),STATUS_PTR)) +#endif +#endif + + + + + diff --git a/dssmap.c b/dssmap.c new file mode 100644 index 0000000..b869d57 --- /dev/null +++ b/dssmap.c @@ -0,0 +1,2283 @@ +/* +*class++ +* Name: +* DssMap + +* Purpose: +* Map points using a Digitised Sky Survey plate solution. + +* Constructor Function: +* The DssMap class does not have a constructor function. A DssMap +* is created only as a by-product of reading a FrameSet (using +c astRead) from a FitsChan which contains FITS header cards +f AST_READ) from a FitsChan which contains FITS header cards +* describing a DSS plate solution, and whose Encoding attribute is +* set to "DSS". The result of such a read, if successful, is a +* FrameSet whose base and current Frames are related by a DssMap. + +* Description: +* The DssMap class implements a Mapping which transforms between +* 2-dimensional pixel coordinates and an equatorial sky coordinate +* system (right ascension and declination) using a Digitised Sky +* Survey (DSS) astrometric plate solution. +* +* The input coordinates are pixel numbers along the first and +* second dimensions of an image, where the centre of the first +* pixel is located at (1,1) and the spacing between pixel centres +* is unity. +* +* The output coordinates are right ascension and declination in +* radians. The celestial coordinate system used (FK4, FK5, etc.) +* is unspecified, and will usually be indicated by appropriate +* keywords in a FITS header. + +* Inheritance: +* The DssMap class inherits from the Mapping class. + +* Attributes: +* The DssMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The DssMap class does not define any new functions beyond those +f The DssMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . +* (except for code supplied by Doug Mink, as noted in this file) + +* Authors: +* DSB: D.S. Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink, RAL) + +* History: +* 18-FEB-1997 (DSB): +* Original version. +* 30-JUN-1997 (DSB): +* astDssFits and astDssMap made protected instead of public. +* 15-JUL-1997 (RFWS): +* Tidied public prologues. +* 5-SEP-197 (RFWS): +* Added prototypes for platepos and platepix. +* 4-NOV-1997 (DSB): +* o A copy of the supplied FitsChan is no longer stored inside +* the DssMap. The FitsChan returned by DssFits is now derived from +* the wcs information stored in the SAOimage "WorldCoor" structure +* (stored within the DssMap), and only contains the keywords +* necessary to reconstruct the DssMap. +* o The external representation of a DssMap is now stored in a set +* of scalar values, rather than a FitsChan. +* 22-DEC-1997 (DSB): +* Bug fixed in MapMerge which caused a core dump when a +* DssMap/WinMap combination is succesfully simplified. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitDssMapVtab +* method. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 10-MAY-2006 (DSB): +* Override astEqual. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS DssMap + +/* Macro which returns the nearest integer to a given floating point + value. */ +#define NINT(x) (int)((x)+(((x)>0.0)?0.5:-0.5)) + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "memory.h" /* Memory allocation facilities */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "fitschan.h" /* Manipulation of FITS header cards */ +#include "wcsmap.h" /* Degrees/radians conversion factors */ +#include "winmap.h" /* Shift and scale mappings */ +#include "dssmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(DssMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(DssMap,Class_Init) +#define class_vtab astGLOBAL(DssMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstDssMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstFitsChan *DssFits( AstDssMap *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int platepix( double, double, struct WorldCoor *, double *, double * ); +static int platepos( double, double, struct WorldCoor *, double *, double * ); +static struct WorldCoor *BuildWcs( AstFitsChan *, const char *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *obj, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int Equal( AstObject *, AstObject *, int * ); + +static int GetObjSize( AstObject *, int * ); +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two DssMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "dssmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* DssMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two DssMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a DssMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the DssMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstDssMap *that; + AstDssMap *this; + int i; + int nin; + int nout; + int result; + struct WorldCoor *this_wcs; + struct WorldCoor *that_wcs; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two DssMap structures. */ + this = (AstDssMap *) this_object; + that = (AstDssMap *) that_object; + +/* Check the second object is a DssMap. We know the first is a + DssMap since we have arrived at this implementation of the virtual + function. */ + if( astIsADssMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two DssMaps differ, it may still be possible + for them to be equivalent. First compare the DssMaps if their Invert + flags are the same. In this case all the attributes of the two DssMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + + this_wcs = ( struct WorldCoor *) this->wcs; + that_wcs = ( struct WorldCoor *) that->wcs; + + if( this_wcs->x_pixel_offset == that_wcs->x_pixel_offset && + this_wcs->y_pixel_offset == that_wcs->y_pixel_offset && + this_wcs->ppo_coeff[2] == that_wcs->ppo_coeff[2] && + this_wcs->ppo_coeff[5] == that_wcs->ppo_coeff[5] && + this_wcs->x_pixel_size == that_wcs->x_pixel_size && + this_wcs->y_pixel_size == that_wcs->y_pixel_size && + this_wcs->plate_dec == that_wcs->plate_dec && + this_wcs->plate_ra == that_wcs->plate_ra ) { + + result = 1; + for( i = 0; i < 13; i++ ) { + if( this_wcs->amd_x_coeff[i] != that_wcs->amd_x_coeff[i] || + this_wcs->amd_y_coeff[i] != that_wcs->amd_y_coeff[i] ) { + result = 0; + break; + } + } + + } + +/* If the Invert flags for the two DssMaps differ, the attributes of the two + DssMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a DssMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "dssmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* DssMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied DssMap, +* in bytes. + +* Parameters: +* this +* Pointer to the DssMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstDssMap *this; /* Pointer to DssMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the DssMap structure. */ + this = (AstDssMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->wcs ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + + +static struct WorldCoor *BuildWcs( AstFitsChan *fits, const char *method, + const char *class, int *status ) { +/* +* Name: +* BuildWcs + +* Purpose: +* Copy DSS plate fit information from a FitsChan to a SAOimage +* WorldCoor structure. + +* Type: +* Private function. + +* Synopsis: +* #include "dssmap.h" +* struct WorldCoor *BuildWcs( AstFitsChan *fits, const char *method, +* const char *class ) + +* Class Membership: +* DssMap member function. + +* Description: +* This creates a WorldCoor structure and copies the required data +* from the supplied FitsChan into the new WorldCoor structure. Note, +* only those components of the WorldCoor structure which are needed to +* transform between pixel and sky coordinates are initialised in the +* returned structure. + +* Parameters: +* fits +* Pointer to the FitsChan containing the FITS header describing +* the DSS plate fit. +* method +* The calling method (for error messages). +* class +* The object class (for error messages). + +* Returned Value: +* A pointer to the new WorldCoor structure. This should be freed +* using astFree when no longer needed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or +* if this function should fail for any reason. + +*/ + +/* Local Variables: */ + char name_buff[ 10 ]; /* Buffer for keyword name */ + char *name; /* Pointer to jeyword name string */ + char *ckeyval; /* Pointer to string keyword value */ + struct WorldCoor *ret; /* Pointer to the returned structure */ + double rah,ram,ras; /* Centre RA hours, minutes and seconds */ + double dsign; /* Sign of centre dec */ + double decd,decm,decs; /* Centre Dec degrees, minutes, seconds */ + double dec_deg; /* Centre Dec in degrees */ + double ra_hours; /* Centre RA in hours */ + int i; /* Coefficient index */ + +/* Check the local error status. */ + if ( !astOK ) return NULL; + +/* Get memory to hold the returned structure. */ + ret = (struct WorldCoor *) astMalloc( sizeof( struct WorldCoor ) ); + +/* Check the memory can be used. */ + if( astOK ){ + +/* The following code is based on the "wcsinit" function in SAOimage file + wcs.c. Note, only the values needed in the platepos and platepix + functions are set up. The FITS keywords are accessed in the order in + which they are usually stored in a FITS file. This will cut down the + time spent searching for keywords. Report an error if any required + keyword is not found. */ + +/* Plate center RA */ + rah = 0.0; + ram = 0.0; + ras = 0.0; + + name = "PLTRAH"; + if( !astGetFitsF( fits, name, &rah ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + name = "PLTRAM"; + if( !astGetFitsF( fits, name, &ram ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + name = "PLTRAS"; + if( !astGetFitsF( fits, name, &ras ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + ra_hours = rah + (ram / (double)60.0) + (ras / (double)3600.0); + ret->plate_ra = AST__DD2R*15.0*ra_hours; + + +/* Plate center Dec */ + name = "PLTDECSN"; + if( !astGetFitsS( fits, name, &ckeyval ) && astOK ){ + dsign = 1.0; + + } else { + if( *ckeyval == '-' ){ + dsign = -1.0; + } else { + dsign = 1.0; + } + + } + + decd = 0.0; + decm = 0.0; + decs = 0.0; + + name = "PLTDECD"; + if( !astGetFitsF( fits, name, &decd ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + name = "PLTDECM"; + if( !astGetFitsF( fits, name, &decm ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + name = "PLTDECS"; + if( !astGetFitsF( fits, name, &decs ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + dec_deg = dsign * (decd+(decm/(double)60.0)+(decs/(double)3600.0)); + ret->plate_dec = AST__DD2R*dec_deg; + +/* Plate Scale arcsec per mm */ + name = "PLTSCALE"; + if( !astGetFitsF( fits, name, &ret->plate_scale ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + +/* X and Y corners (in pixels) */ + name = "CNPIX1"; + if( !astGetFitsF( fits, name, &ret->x_pixel_offset ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + name = "CNPIX2"; + if( !astGetFitsF( fits, name, &ret->y_pixel_offset ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + +/* X and Y pixel sizes (microns). */ + name = "XPIXELSZ"; + if( !astGetFitsF( fits, name, &ret->x_pixel_size ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + + name = "YPIXELSZ"; + if( !astGetFitsF( fits, name, &ret->y_pixel_size ) && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied for the " + "FITS keyword '%s'.", status, method, class, name ); + } + +/* Orientation Coefficients. Only report an error if PPO3 or PPO6 are + missing (these are the only two which are actually used). Assume a + value of zero for any of the others which are missing. */ + name = name_buff; + for ( i = 0; i < 6; i++ ) { + sprintf( name_buff, "PPO%d", i + 1 ); + if( !astGetFitsF( fits, name, &ret->ppo_coeff[i] ) ) { + ret->ppo_coeff[i] = 0.0; + if( ( i == 2 || i == 5 ) && astOK ) { + astError( AST__BDFTS, "%s(%s): No value has been supplied " + "for the FITS keyword '%s'.", status, method, class, + name ); + break; + } + } + } + +/* Plate solution x and y coefficients. Report an error if any of + coefficients 1 to 14 are missing. Assume a value of zero for any + others which are missing. */ + name = name_buff; + for( i = 0; i < 19; i++ ){ + sprintf( name_buff, "AMDX%d", i + 1 ); + if( !astGetFitsF( fits, name, &ret->amd_x_coeff[i] ) ) { + ret->amd_x_coeff[i] = 0.0; + if( i < 13 && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied " + "for the FITS keyword '%s'.", status, method, class, name ); + break; + } + } + } + + for( i = 0; i < 19; i++ ){ + sprintf( name_buff, "AMDY%d", i + 1 ); + if( !astGetFitsF( fits, name, &ret->amd_y_coeff[i] ) ){ + ret->amd_y_coeff[i] = 0.0; + if( i < 13 && astOK ){ + astError( AST__BDFTS, "%s(%s): No value has been supplied " + "for the FITS keyword '%s'.", status, method, class, name ); + break; + } + } + } + +/* If anything went wrong, free the returned structure. */ + if( !astOK ) ret = (struct WorldCoor *) astFree( (void *) ret ); + } + +/* Return the pointer. */ + return ret; +} + +static AstFitsChan *DssFits( AstDssMap *this, int *status ) { +/* +*+ +* Name: +* astDssFits + +* Purpose: +* Return a pointer to a FitsChan describing a DssMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "dssmap.h" +* AstFitsChan *DssFits( AstDssMap *this ) + +* Class Membership: +* DssMap method. + +* Description: +* This function returns a pointer to a DSS-encoded FitsChan containing +* cards generated from the information stored with the DssMap. The +* keywords contained in the FitsChan are those which would ne needed to +* re-create the DssMap (see astDSSMap). + +* Parameters: +* this +* Pointer to the DssMap. + +* Returned Value: +* astDssFits() +* A pointer to the FitsChan. + +* Notes: +* - The returned pointer should be annuled using astAnnul when no longer +* needed. +* - A value of NULL will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstFitsChan *ret; /* Pointer to the returned FitsChan */ + char *comm; /* Pointer to keyword comment string */ + char *name; /* Pointer to keyword name string */ + char name_buff[ 10 ]; /* Buffer for keyword name */ + double dec; /* Centre Dec in degrees */ + double decd,decm,decs; /* Centre Dec degrees, minutes, seconds */ + double ra; /* Centre RA in hours */ + double rah,ram,ras; /* Centre RA hours, minutes and seconds */ + int i; /* Coefficient index */ + struct WorldCoor *wcs; /* WCS information from the DssMap */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Store a pointer to the WCS information stored in the DSSMap. */ + wcs = (struct WorldCoor *) this->wcs, + +/* Create a new empty FitsChan, using DSS encoding. */ + ret = astFitsChan( NULL, NULL, "Encoding=DSS", status ); + +/* Create the keyword values and stored them in the returned FitsChan... */ + +/* Plate centre RA. */ + ra = wcs->plate_ra/( AST__DD2R*15.0 ); + ra = modf( ra, &rah ); + ra = modf( 60.0*ra, &ram ); + ras = 60.0*ra; + + astSetFitsI( ret, "PLTRAH", NINT( rah ), "Plate centre RA", 0 ); + astSetFitsI( ret, "PLTRAM", NINT( ram ), " ", 0 ); + astSetFitsF( ret, "PLTRAS", ras, " ", 0 ); + +/* Plate centre DEC. */ + dec = wcs->plate_dec/AST__DD2R; + if( dec < 0.0 ) { + dec = -dec; + astSetFitsS( ret, "PLTDECSN", "-", "Plate centre DEC", 0 ); + } else { + astSetFitsS( ret, "PLTDECSN", "+", "Plate centre DEC", 0 ); + } + + dec = modf( dec, &decd ); + dec = modf( 60.0*dec, &decm ); + decs = 60.0*dec; + + astSetFitsI( ret, "PLTDECD", NINT( decd ), " ", 0 ); + astSetFitsI( ret, "PLTDECM", NINT( decm ), " ", 0 ); + astSetFitsF( ret, "PLTDECS", decs, " ", 0 ); + +/* Plate Scale arcsec per mm */ + astSetFitsF( ret, "PLTSCALE", wcs->plate_scale, "Plate Scale arcsec per mm", + 0 ); + +/* X and Y corners (in pixels) */ + astSetFitsI( ret, "CNPIX1", NINT( wcs->x_pixel_offset ), + "X corner (pixels)", 0 ); + astSetFitsI( ret, "CNPIX2", NINT( wcs->y_pixel_offset ), + "Y corner", 0 ); + +/* X and Y pixel sizes (microns). */ + astSetFitsF( ret, "XPIXELSZ", wcs->x_pixel_size, + "X pixel size (microns)", 0 ); + astSetFitsF( ret, "YPIXELSZ", wcs->y_pixel_size, + "Y pixel size (microns)", 0 ); + +/* Orientation Coefficients. */ + name = name_buff; + comm = "Orientation Coefficients"; + for ( i = 0; i < 6; i++ ) { + sprintf( name_buff, "PPO%d", i + 1 ); + astSetFitsF( ret, name, wcs->ppo_coeff[i], comm, 0 ); + comm = " "; + } + +/* Plate solution x and y coefficients. */ + comm = "Plate solution x coefficients"; + for( i = 0; i < 19; i++ ){ + sprintf( name_buff, "AMDX%d", i + 1 ); + astSetFitsF( ret, name, wcs->amd_x_coeff[i], comm, 0 ); + comm = " "; + } + + comm = "Plate solution y coefficients"; + for( i = 0; i < 19; i++ ){ + sprintf( name_buff, "AMDY%d", i + 1 ); + astSetFitsF( ret, name, wcs->amd_y_coeff[i], comm, 0 ); + comm = " "; + } + +/* Return a pointer to the FitsChan. */ + return ret; +} + +void astInitDssMapVtab_( AstDssMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitDssMapVtab + +* Purpose: +* Initialise a virtual function table for a DssMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "dssmap.h" +* void astInitDssMapVtab( AstDssMapVtab *vtab, const char *name ) + +* Class Membership: +* DssMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the DssMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsADssMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->DssFits = DssFits; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + mapping = (AstMappingVtab *) vtab; + object = (AstObjectVtab *) vtab; + + parent_transform = mapping->Transform; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the class dump, copy and delete function. */ + astSetDump( object, Dump, "DssMap", "DSS plate fit mapping" ); + astSetCopy( object, Copy ); + astSetDelete( object, Delete ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a DssMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* DssMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated DssMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated DssMap with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated DssMap which is to be merged with +* its neighbours. This should be a cloned copy of the DssMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* DssMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated DssMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstDssMap *dm; /* Pointer to supplied DssMap */ + AstDssMap *dmnew; /* Pointer to replacement DssMap */ + AstFitsChan *fits; /* FITS headers for replacement DssMap */ + AstFitsChan *fits_dss;/* FITS headers for supplied DssMap */ + AstWinMap *wm; /* Pointer to the adjacent WinMap */ + double *a; /* Pointer to shift terms */ + double *b; /* Pointer to scale terms */ + double cnpix1; /* X pixel origin */ + double cnpix2; /* Y pixel origin */ + double xpixelsz; /* X pixel size */ + double ypixelsz; /* Y pixel size */ + int i; /* Loop counter */ + int ok; /* All FITS keywords found? */ + int old_winv; /* original Invert value for supplied WinMap */ + int result; /* Result value to return */ + int wmi; /* Index of adjacent WinMap in map list */ + struct WorldCoor *wcs;/* Pointer to SAOimage wcs structure */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The only simplification easily possible is if a WinMap maps the pixel + coordinates prior to a DssMap. If the DssMap has not been inverted, the + WinMap must be applied before the DssMap, otherwise the WinMap must be + applied after the DssMap. */ + if( series ){ + + if( !( *invert_list )[ where ] ){ + wmi = where - 1; + } else { + wmi = where + 1; + } + + if( wmi >= 0 && wmi < *nmap ){ + if( !strcmp( astGetClass( ( *map_list )[ wmi ] ), "WinMap" ) ){ + +/* Temporarily set the Invert attribute of the WinMap to the supplied value. */ + wm = (AstWinMap *) ( *map_list )[ wmi ]; + old_winv = astGetInvert( wm ); + astSetInvert( wm, ( *invert_list )[ wmi ] ); + +/* Get a copy of the scale and shift terms from the WinMap. */ + astWinTerms( wm, &a, &b ); + +/* Check that the scale and shift terms are usable. */ + if( astOK && + a[ 0 ] != AST__BAD && b[ 0 ] != AST__BAD && b[ 0 ] != 0.0 && + a[ 1 ] != AST__BAD && b[ 1 ] != AST__BAD && b[ 1 ] != 0.0 ){ + +/* Get the values of the keywords which define the origin and scales of + the DssMap pixel coordinate system. */ + dm = (AstDssMap *) ( *map_list )[ where ]; + wcs = (struct WorldCoor *) ( dm->wcs ); + + cnpix1 = wcs->x_pixel_offset; + cnpix2 = wcs->y_pixel_offset; + xpixelsz = wcs->x_pixel_size; + ypixelsz = wcs->y_pixel_size; + +/* Calculate new values which take into account the WinMap. */ + if( wmi == where - 1 ){ + xpixelsz *= b[ 0 ]; + ypixelsz *= b[ 1 ]; + cnpix1 = 0.5 + ( cnpix1 + a[ 0 ] - 0.5 )/b[ 0 ]; + cnpix2 = 0.5 + ( cnpix2 + a[ 1 ] - 0.5 )/b[ 1 ]; + + } else { + xpixelsz /= b[ 0 ]; + ypixelsz /= b[ 1 ]; + cnpix1 = b[ 0 ]*( cnpix1 - 0.5 ) - a[ 0 ] + 0.5; + cnpix2 = b[ 1 ]*( cnpix2 - 0.5 ) - a[ 1 ] + 0.5; + } + +/* The CNPIX1 and CNPIX2 keywords are integer keywords. Therefore, we can + only do the simplification if the new values are integer to a good + approximation. We use one hundredth of a pixel. */ + if( fabs( cnpix1 - NINT( cnpix1 ) ) < 0.01 && + fabs( cnpix2 - NINT( cnpix2 ) ) < 0.01 ){ + +/* Get a copy of the FitsChan holding the header cards which define the + DssMap. */ + fits_dss = astDssFits( dm ); + fits = astCopy( fits_dss ); + fits_dss = astAnnul( fits_dss ); + +/* Update the value of each of the changed keywords. */ + ok = 1; + + astClearCard( fits ); + if( astFindFits( fits, "CNPIX1", NULL, 0 ) ){ + astSetFitsI( fits, "CNPIX1", NINT( cnpix1 ), NULL, 1 ); + } else { + ok = 0; + } + + astClearCard( fits ); + if( astFindFits( fits, "CNPIX2", NULL, 0 ) ){ + astSetFitsI( fits, "CNPIX2", NINT( cnpix2 ), NULL, 1 ); + } else { + ok = 0; + } + + astClearCard( fits ); + if( astFindFits( fits, "XPIXELSZ", NULL, 0 ) ){ + astSetFitsF( fits, "XPIXELSZ", xpixelsz, NULL, 1 ); + } else { + ok = 0; + } + + astClearCard( fits ); + if( astFindFits( fits, "YPIXELSZ", NULL, 0 ) ){ + astSetFitsF( fits, "YPIXELSZ", ypixelsz, NULL, 1 ); + } else { + ok = 0; + } + +/* If all the keywords were updated succesfully, create the new DssMap + based on the modified FITS header cards. */ + if( ok ){ + dmnew = astDssMap( fits, "", status ); + +/* Anull the DssMap pointer in the list and replace it with the new one. + The invert flag is left unchanged. */ + dm = astAnnul( dm ); + ( *map_list )[ where ] = (AstMapping *) dmnew; + +/* Annul the WinMap pointer in the list, and shuffle any remaining + Mappings down to fill the gap. */ + wm = astAnnul( wm ); + for ( i = wmi + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = astMIN( wmi, where ); + + } + +/* Annul the FitsChan holding the modified header cards. */ + fits = astAnnul( fits ); + } + } + +/* Free the arrays holding scale and shift terms from the WinMap. */ + a = (double *) astFree( (void *) a ); + b = (double *) astFree( (void *) b ); + +/* Reinstate the original setting of the Invert attribute of the WinMap (if + it still exists). */ + if( wm ) astSetInvert( wm, old_winv ); + + } + } + } + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a DssMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "dssmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* DssMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a DssMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required DSS +* plate fit. + +* Parameters: +* this +* Pointer to the DssMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* be 2. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstDssMap *map; /* Pointer to DssMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *aa; /* Pointer to next longitude value */ + double *bb; /* Pointer to next latitude value */ + double *xx; /* Pointer to next pixel X value */ + double *yy; /* Pointer to next pixel Y value */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the DssMap. */ + map = (AstDssMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points from the input PointSet and obtain + pointers for accessing the input and output coordinate values. */ + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* First deal with forward transformations. */ + if( forward ){ + +/* Store pointers to the next value on each axis. */ + xx = ptr_in[ 0 ]; + yy = ptr_in[ 1 ]; + aa = ptr_out[ 0 ]; + bb = ptr_out[ 1 ]; + +/* Loop to apply the plate fit to all the points, checking for (and + propagating) bad values in the process. */ + for ( point = 0; point < npoint; point++ ) { + if( *xx != AST__BAD && *yy != AST__BAD ){ + +/* If the pixel position is transformed succesfully, convert the returned + RA/DEC from degrees to radians. Otherwise, store bad values. NB, + platepos returns zero for success. */ + if( !platepos( *xx, *yy, (struct WorldCoor *) map->wcs, + aa, bb ) ){ + (*aa) *= AST__DD2R; + (*bb) *= AST__DD2R; + + } else { + *aa = AST__BAD; + *bb = AST__BAD; + } + + } else { + *aa = AST__BAD; + *bb = AST__BAD; + } + +/* Move on to the next point. */ + xx++; + yy++; + aa++; + bb++; + } + +/* Now deal with inverse transformations in the same way. */ + } else { + aa = ptr_in[ 0 ]; + bb = ptr_in[ 1 ]; + xx = ptr_out[ 0 ]; + yy = ptr_out[ 1 ]; + + for ( point = 0; point < npoint; point++ ) { + if( *aa != AST__BAD && *bb != AST__BAD ){ + + if( platepix( AST__DR2D*(*aa), AST__DR2D*(*bb), + (struct WorldCoor *) map->wcs, xx, yy ) ){ + *xx = AST__BAD; + *yy = AST__BAD; + } + + } else { + *xx = AST__BAD; + *yy = AST__BAD; + } + + xx++; + yy++; + aa++; + bb++; + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for DssMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for DssMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + + +/* Local Variables: */ + AstDssMap *in; /* Pointer to input DssMap */ + AstDssMap *out; /* Pointer to output DssMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output DssMaps. */ + in = (AstDssMap *) objin; + out = (AstDssMap *) objout; + +/* Store a copy of the input SAOIMAGE WorldCoor structure in the output. */ + out->wcs = astStore( NULL, in->wcs, sizeof( struct WorldCoor ) ); + + return; + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for DssMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for DssMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstDssMap *this; /* Pointer to DssMap */ + +/* Obtain a pointer to the DssMap structure. */ + this = (AstDssMap *) obj; + +/* Free the SAOIMAGE WorldCoor structure. */ + this->wcs = astFree( this->wcs ); + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for DssMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the DssMap class to an output Channel. + +* Parameters: +* this +* Pointer to the DssMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + + AstDssMap *this; /* Pointer to the DssMap structure */ + struct WorldCoor *wcs; /* Pointer to SAOimage wcs structure */ + char name_buff[ 11 ]; /* Buffer for keyword string */ + int i; /* Coefficient index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the DssMap structure. */ + this = (AstDssMap *) this_object; + +/* Get a pointer to the WorldCoor structure holding the description of the + DssMap. */ + wcs = (struct WorldCoor *) ( this->wcs ); + +/* Write out values representing the contents of the WorldCoor structure. + Only the components which are required to re-create the DssMap are + written out. */ + astWriteDouble( channel, "PltRA", 1, 1, wcs->plate_ra, "Plate centre RA (radians)" ); + astWriteDouble( channel, "PltDec", 1, 1, wcs->plate_dec, "Plate centre Dec (radians)" ); + astWriteDouble( channel, "PltScl", 1, 1, wcs->plate_scale, "Plate scale (arcsec/mm)" ); + astWriteDouble( channel, "CNPix1", 1, 1, wcs->x_pixel_offset, "X Pixel offset (pixels)" ); + astWriteDouble( channel, "CNPix2", 1, 1, wcs->y_pixel_offset, "Y Pixel offset (pixels)" ); + astWriteDouble( channel, "XPixSz", 1, 1, wcs->x_pixel_size, "X Pixel size (microns)" ); + astWriteDouble( channel, "YPixSz", 1, 1, wcs->y_pixel_size, "Y Pixel size (microns)" ); + + for( i = 0; i < 6; i++ ) { + sprintf( name_buff, "PPO%d", i + 1 ); + astWriteDouble( channel, name_buff, 1, 1, wcs->ppo_coeff[i], + "Orientation coefficients" ); + } + + for( i = 0; i < 19; i++ ) { + sprintf( name_buff, "AMDX%d", i + 1 ); + astWriteDouble( channel, name_buff, 1, 1, wcs->amd_x_coeff[i], + "Plate solution X coefficients" ); + } + + for( i = 0; i < 19; i++ ) { + sprintf( name_buff, "AMDY%d", i + 1 ); + astWriteDouble( channel, name_buff, 1, 1, wcs->amd_y_coeff[i], + "Plate solution Y coefficients" ); + } + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsADssMap and astCheckDssMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(DssMap,Mapping) +astMAKE_CHECK(DssMap) + +AstDssMap *astDssMap_( void *fits_void, const char *options, int *status, ...) { +/* +*+ +* Name: +* astDssMap + +* Purpose: +* Create a DssMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "dssmap.h" +* AstDssMap *astDssMap( AstFitsChan *fits, const char *options, int *status, ... ) + +* Class Membership: +* DssMap constructor. + +* Description: +* This function creates a new DssMap and optionally initialises its +* attributes. +* +* A DssMap is a Mapping which uses a Digitised Sky Survey plate fit to +* transform a set of points from pixel coordinates to equatorial +* coordinates (i.e. Right Ascension and Declination). + +* Parameters: +* fits +* A pointer to a FitsChan holding a set of FITS header cards +* describing the plate fit to be used. The FitsChan may contain +* other header cards which will be ignored, and it is unchanged on +* exit. The required information is copied from the FitsChan, and +* so the supplied FitsChan may subsequently be changed or deleted +* without changing the DssMap. +* options +* Pointer to a null-terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new DssMap. The syntax used is identical to +* that for the astSet function and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of additional arguments may follow it in +* order to supply values to be substituted for these +* specifiers. The rules for supplying these are identical to +* those for the astSet function (and for the C "printf" +* function). + +* Returned Value: +* astDssMap() +* A pointer to the new DssMap. + +* Attributes: +* The DssMap class has no additional attributes over and above those +* common to all Mappings. + +* Notes: +* - The supplied FitsChan must contain values for the following FITS +* keywords: CNPIX1, CNPIX2, PPO3, PPO6, XPIXELSZ, YPIXELSZ, PLTRAH, +* PLTRAM, PLTRAS, PLTDECD, PLTDECM, PLTDECS, PLTDECSN, PLTSCALE, +* AMDX1, AMDX2, ..., AMDX13, AMDY1, AMDY2, ..., AMDY13. +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsChan *fits; /* Pointer to supplied FitsChan */ + AstDssMap *new; /* Pointer to new DssMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + new = NULL; + if ( !astOK ) return new; + +/* Obtain and validate a pointer to the FitsChan structure provided. */ + fits = astCheckFitsChan( fits_void ); + if ( astOK ) { + +/* Initialise the DssMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitDssMap( NULL, sizeof( AstDssMap ), !class_init, &class_vtab, + "DssMap", fits ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new DssMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new DssMap. */ + return new; +} + +AstDssMap *astInitDssMap_( void *mem, size_t size, int init, + AstDssMapVtab *vtab, const char *name, + AstFitsChan *fits, int *status ) { +/* +*+ +* Name: +* astInitDssMap + +* Purpose: +* Initialise a DssMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "dssmap.h" +* AstDssMap *astInitDssMap( void *mem, size_t size, int init, +* AstDssMapVtab *vtab, const char *name, +* AstFitsChan *fits ) + +* Class Membership: +* DssMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new DssMap object. It allocates memory (if necessary) to accommodate +* the DssMap plus any additional data associated with the derived class. +* It then initialises a DssMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a DssMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the DssMap is to be initialised. +* This must be of sufficient size to accommodate the DssMap data +* (sizeof(DssMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the DssMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the DssMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the DssMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new DssMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* fits +* Pointer to a FitsChan containing the DSS FITS Header. + +* Returned Value: +* A pointer to the new DssMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstDssMap *new; /* Pointer to new DssMap */ + struct WorldCoor *wcs; /* Pointer to SAOIMAGE wcs structure */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitDssMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Create a structure holding the information required by the SAOIMAGE + "platepos" function. The required values are extracted from the + supplied FitsChan. An error is reported and NULL returned if any required + keywords are missing or unusable. */ + if ( ( wcs = BuildWcs( fits, "astInitDssMap", name, status ) ) ) { + +/* Initialise a 2-D Mapping structure (the parent class) as the first component + within the DssMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstDssMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 2, 2, 1, 1 ); + + if ( astOK ) { + +/* Initialise the DssMap data. */ +/* --------------------------- */ +/* Store a copy of the SAOIMAGE wcs structure. */ + new->wcs = astStore( NULL, (void *) wcs, sizeof( struct WorldCoor ) ); + +/* If an error occurred, clean up by deleting the new DssMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Free the SAOIMAGE wcs structure. */ + wcs = (struct WorldCoor *) astFree( (void *) wcs ); + + } + +/* Return a pointer to the new DssMap. */ + return new; +} + +AstDssMap *astLoadDssMap_( void *mem, size_t size, + AstDssMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadDssMap + +* Purpose: +* Load a DssMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "dssmap.h" +* AstDssMap *astLoadDssMap( void *mem, size_t size, +* AstDssMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* DssMap loader. + +* Description: +* This function is provided to load a new DssMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* DssMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a DssMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the DssMap is to be +* loaded. This must be of sufficient size to accommodate the +* DssMap data (sizeof(DssMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the DssMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the DssMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstDssMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new DssMap. If this is NULL, a pointer +* to the (static) virtual function table for the DssMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "DssMap" is used instead. + +* Returned Value: +* A pointer to the new DssMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstDssMap *new; /* Pointer to the new DssMap */ + char name_buff[ 11 ]; /* Buffer for item name */ + int i; /* Coefficient index */ + struct WorldCoor *wcs; /* Pointer to Wcs information */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this DssMap. In this case the + DssMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstDssMap ); + vtab = &class_vtab; + name = "DssMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitDssMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built DssMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "DssMap" ); + +/* Allocate memory to hold the WorldCoor structure which holds the wcs + information in a form usable by the SAOimage projection functions. */ + new->wcs = astMalloc( sizeof(struct WorldCoor) ); + if( astOK ) { + +/* Get a pointer to the WorldCoor structure holding the description of the + DssMap. */ + wcs = (struct WorldCoor *) ( new->wcs ); + +/* Read the values representing the contents of the WorldCoor structure. + Only the components which are required to re-create the DssMap are + read. */ + wcs->plate_ra = astReadDouble( channel, "pltra", AST__BAD ); + if( wcs->plate_ra == AST__BAD && astOK ){ + astError( AST__RDERR, "astRead(DssMap): 'PltRA' object (Plate " + "centre RA) missing from input." , status); + } + + wcs->plate_dec = astReadDouble( channel, "pltdec", AST__BAD ); + if( wcs->plate_dec == AST__BAD && astOK ){ + astError( AST__RDERR, "astRead(DssMap): 'PltDec' object (Plate " + "centre Dec) missing from input." , status); + } + + wcs->plate_scale = astReadDouble( channel, "pltscl", AST__BAD ); + if( wcs->plate_scale == AST__BAD && astOK ){ + astError( AST__RDERR, "astRead(DssMap): 'PltScl' object (Plate " + "scale) missing from input." , status); + } + + wcs->x_pixel_offset = astReadDouble( channel, "cnpix1", AST__BAD ); + if( wcs->x_pixel_offset == AST__BAD && astOK ){ + astError( AST__RDERR, "astRead(DssMap): 'CNPix1' object (X pixel " + "offset) missing from input." , status); + } + + wcs->y_pixel_offset = astReadDouble( channel, "cnpix2", AST__BAD ); + if( wcs->y_pixel_offset == AST__BAD && astOK ){ + astError( AST__RDERR, "astRead(DssMap): 'CNPix2' object (Y pixel " + "offset) missing from input." , status); + } + + wcs->x_pixel_size = astReadDouble( channel, "xpixsz", AST__BAD ); + if( wcs->x_pixel_size == AST__BAD && astOK ){ + astError( AST__RDERR, "astRead(DssMap): 'XPixSz' object (X pixel " + "size) missing from input." , status); + } + + wcs->y_pixel_size = astReadDouble( channel, "ypixsz", AST__BAD ); + if( wcs->y_pixel_size == AST__BAD && astOK ){ + astError( AST__RDERR, "astRead(DssMap): 'YPixSz' object (Y pixel " + "size) missing from input." , status); + } + + for( i = 0; i < 6 && astOK; i++ ) { + sprintf( name_buff, "ppo%d", i + 1 ); + wcs->ppo_coeff[i] = astReadDouble( channel, name_buff, AST__BAD ); + if( wcs->ppo_coeff[i] == AST__BAD ){ + if( i == 2 || i == 5 ) { + if( astOK ) astError( AST__RDERR, "astRead(DssMap): 'PPO%d' " + "object (orientation coefficient %d) " + "missing from input.", status, i + 1, i + 1 ); + } else { + wcs->ppo_coeff[i] = 0.0; + } + } + } + + for( i = 0; i < 19 && astOK; i++ ) { + sprintf( name_buff, "amdx%d", i + 1 ); + wcs->amd_x_coeff[i] = astReadDouble( channel, name_buff, AST__BAD ); + if( wcs->amd_x_coeff[i] == AST__BAD ){ + if( i < 13 ){ + if( astOK ) astError( AST__RDERR, "astRead(DssMap): 'AMDX%d' " + "object (plate solution X coefficient " + "%d) missing from input.", status, i + 1, i + 1 ); + } else { + wcs->amd_x_coeff[i] = 0.0; + } + } + } + + for( i = 0; i < 19 && astOK; i++ ) { + sprintf( name_buff, "amdy%d", i + 1 ); + wcs->amd_y_coeff[i] = astReadDouble( channel, name_buff, AST__BAD ); + if( wcs->amd_y_coeff[i] == AST__BAD ){ + if( i < 13 ){ + if( astOK ) astError( AST__RDERR, "astRead(DssMap): 'AMDY%d' " + "object (plate solution Y coefficient " + "%d) missing from input.", status, i + 1, i + 1 ); + } else { + wcs->amd_y_coeff[i] = 0.0; + } + } + } + } + +/* If an error occurred, clean up by deleting the new DssMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new DssMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +AstFitsChan *astDssFits_( AstDssMap *this, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,DssMap,DssFits))( this, status ); +} + +/* The code which follows in this file is covered by the following + statement of terms and conditions, which differ from the terms and + conditions which apply above. + +*************************************************************************** +* +* Copyright: 1988 Smithsonian Astrophysical Observatory +* You may do anything you like with these files except remove +* this copyright. The Smithsonian Astrophysical Observatory +* makes no representations about the suitability of this +* software for any purpose. It is provided "as is" without +* express or implied warranty. +* +***************************************************************************** +*/ + +/* >>>>>>>>>>>>>>>>>>>>>> platepos.c <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */ + +/* File saoimage/wcslib/platepos.c + * February 25, 1996 + * By Doug Mink, Harvard-Smithsonian Center for Astrophysics + + * Module: platepos.c (Plate solution WCS conversion + * Purpose: Compute WCS from Digital Sky Survey plate fit + * Subroutine: platepos() converts from pixel location to RA,Dec + * Subroutine: platepix() converts from RA,Dec to pixel location + + These functions are based on the astrmcal.c portion of GETIMAGE by + J. Doggett and the documentation distributed with the Digital Sky Survey. + + >>>>>>> STARLINK VERSION <<<<<<<< + +*/ + +/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + Changed by R.F. Warren-Smith (Starlink) to make the function static. */ + +static int +platepos (xpix, ypix, wcs, xpos, ypos) + +/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */ + +/* Routine to determine accurate position for pixel coordinates */ +/* returns 0 if successful otherwise 1 = angle too large for projection; */ +/* based on amdpos() from getimage */ + +/* Input: */ +double xpix; /* x pixel number (RA or long without rotation) */ +double ypix; /* y pixel number (dec or lat without rotation) */ +struct WorldCoor *wcs; /* WCS parameter structure */ + +/* Output: */ +double *xpos; /* Right ascension or longitude in degrees */ +double *ypos; /* Declination or latitude in degrees */ + +{ + double x, y, xmm, ymm, xmm2, ymm2, xmm3, ymm3, x2y2; + double xi, xir, eta, etar, raoff, ra, dec; + double cond2r = 1.745329252e-2; + double cons2r = 206264.8062470964; + double twopi = 6.28318530717959; + double ctan, ccos; + +/* Ignore magnitude and color terms + double mag = 0.0; + double color = 0.0; */ + +/* Convert from image pixels to plate pixels */ + x = xpix + wcs->x_pixel_offset - 1.0 + 0.5; + y = ypix + wcs->y_pixel_offset - 1.0 + 0.5; + +/* Convert from pixels to millimeters */ + xmm = (wcs->ppo_coeff[2] - x * wcs->x_pixel_size) / 1000.0; + ymm = (y * wcs->y_pixel_size - wcs->ppo_coeff[5]) / 1000.0; + xmm2 = xmm * xmm; + ymm2 = ymm * ymm; + xmm3 = xmm * xmm2; + ymm3 = ymm * ymm2; + x2y2 = xmm2 + ymm2; + +/* Compute coordinates from x,y and plate model */ + + xi = wcs->amd_x_coeff[ 0]*xmm + wcs->amd_x_coeff[ 1]*ymm + + wcs->amd_x_coeff[ 2] + wcs->amd_x_coeff[ 3]*xmm2 + + wcs->amd_x_coeff[ 4]*xmm*ymm + wcs->amd_x_coeff[ 5]*ymm2 + + wcs->amd_x_coeff[ 6]*(x2y2) + wcs->amd_x_coeff[ 7]*xmm3 + + wcs->amd_x_coeff[ 8]*xmm2*ymm + wcs->amd_x_coeff[ 9]*xmm*ymm2 + + wcs->amd_x_coeff[10]*ymm3 + wcs->amd_x_coeff[11]*xmm*(x2y2) + + wcs->amd_x_coeff[12]*xmm*x2y2*x2y2; + +/* Ignore magnitude and color terms + + wcs->amd_x_coeff[13]*mag + wcs->amd_x_coeff[14]*mag*mag + + wcs->amd_x_coeff[15]*mag*mag*mag + wcs->amd_x_coeff[16]*mag*xmm + + wcs->amd_x_coeff[17]*mag*x2y2 + wcs->amd_x_coeff[18]*mag*xmm*x2y2 + + wcs->amd_x_coeff[19]*color; */ + + eta = wcs->amd_y_coeff[ 0]*ymm + wcs->amd_y_coeff[ 1]*xmm + + wcs->amd_y_coeff[ 2] + wcs->amd_y_coeff[ 3]*ymm2 + + wcs->amd_y_coeff[ 4]*xmm*ymm + wcs->amd_y_coeff[ 5]*xmm2 + + wcs->amd_y_coeff[ 6]*(x2y2) + wcs->amd_y_coeff[ 7]*ymm3 + + wcs->amd_y_coeff[ 8]*ymm2*xmm + wcs->amd_y_coeff[ 9]*ymm*xmm2 + + wcs->amd_y_coeff[10]*xmm3 + wcs->amd_y_coeff[11]*ymm*(x2y2) + + wcs->amd_y_coeff[12]*ymm*x2y2*x2y2; + +/* Ignore magnitude and color terms + + wcs->amd_y_coeff[13]*mag + wcs->amd_y_coeff[14]*mag*mag + + wcs->amd_y_coeff[15]*mag*mag*mag + wcs->amd_y_coeff[16]*mag*ymm + + wcs->amd_y_coeff[17]*mag*x2y2) + wcs->amd_y_coeff[18]*mag*ymm*x2y2 + + wcs->amd_y_coeff[19]*color; */ + +/* Convert to radians */ + + xir = xi / cons2r; + etar = eta / cons2r; + +/* Convert to RA and Dec */ + + ctan = tan (wcs->plate_dec); + ccos = cos (wcs->plate_dec); + raoff = atan2 (xir / ccos, 1.0 - etar * ctan); + ra = raoff + wcs->plate_ra; + if (ra < 0.0) ra = ra + twopi; + *xpos = ra / cond2r; + + dec = atan (cos (raoff) / ((1.0 - (etar * ctan)) / (etar + ctan))); + *ypos = dec / cond2r; + return 0; +} + + +/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + Changed by R.F. Warren-Smith (Starlink) to make the function static. */ + +static int +platepix (xpos, ypos, wcs, xpix, ypix) + +/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */ + +/* Routine to determine pixel coordinates for sky position */ +/* returns 0 if successful otherwise 1 = angle too large for projection; */ +/* based on amdinv() from getimage */ + +/* Input: */ +double xpos; /* Right ascension or longitude in degrees */ +double ypos; /* Declination or latitude in degrees */ +struct WorldCoor *wcs; /* WCS parameter structure */ + +/* Output: */ +double *xpix; /* x pixel number (RA or long without rotation) */ +double *ypix; /* y pixel number (dec or lat without rotation) */ + +{ + double div,xi,eta,x,y,xy,x2,y2,x2y,y2x,x3,y3,x4,y4,x2y2,cjunk,dx,dy; + double sypos,cypos,syplate,cyplate,sxdiff,cxdiff; + double f,fx,fy,g,gx,gy, xmm, ymm; + double conr2s = 206264.8062470964; + double tolerance = 0.0000005; + int max_iterations = 50; + int i; + double xr, yr; /* position in radians */ + double cond2r = 1.745329252e-2; + +/* Convert RA and Dec in radians to standard coordinates on a plate */ + xr = xpos * cond2r; + yr = ypos * cond2r; + sypos = sin (yr); + cypos = cos (yr); + syplate = sin (wcs->plate_dec); + cyplate = cos (wcs->plate_dec); + sxdiff = sin (xr - wcs->plate_ra); + cxdiff = cos (xr - wcs->plate_ra); + div = (sypos * syplate) + (cypos * cyplate * cxdiff); + xi = cypos * sxdiff * conr2s / div; + eta = ((sypos * cyplate) - (cypos * syplate * cxdiff)) * conr2s / div; + +/* Set initial value for x,y */ + xmm = xi / wcs->plate_scale; + ymm = eta / wcs->plate_scale; + +/* Iterate by Newton's method */ + for (i = 0; i < max_iterations; i++) { + + /* X plate model */ + xy = xmm * ymm; + x2 = xmm * xmm; + y2 = ymm * ymm; + x2y = x2 * ymm; + y2x = y2 * xmm; + x2y2 = x2 + y2; + cjunk = x2y2 * x2y2; + x3 = x2 * xmm; + y3 = y2 * ymm; + x4 = x2 * x2; + y4 = y2 * y2; + f = wcs->amd_x_coeff[0]*xmm + wcs->amd_x_coeff[1]*ymm + + wcs->amd_x_coeff[2] + wcs->amd_x_coeff[3]*x2 + + wcs->amd_x_coeff[4]*xy + wcs->amd_x_coeff[5]*y2 + + wcs->amd_x_coeff[6]*x2y2 + wcs->amd_x_coeff[7]*x3 + + wcs->amd_x_coeff[8]*x2y + wcs->amd_x_coeff[9]*y2x + + wcs->amd_x_coeff[10]*y3 + wcs->amd_x_coeff[11]*xmm*x2y2 + + wcs->amd_x_coeff[12]*xmm*cjunk; + /* magnitude and color terms ignored + + wcs->amd_x_coeff[13]*mag + + wcs->amd_x_coeff[14]*mag*mag + wcs->amd_x_coeff[15]*mag*mag*mag + + wcs->amd_x_coeff[16]*mag*xmm + wcs->amd_x_coeff[17]*mag*(x2+y2) + + wcs->amd_x_coeff[18]*mag*xmm*(x2+y2) + wcs->amd_x_coeff[19]*color; + */ + + /* Derivative of X model wrt x */ + fx = wcs->amd_x_coeff[0] + wcs->amd_x_coeff[3]*2.0*xmm + + wcs->amd_x_coeff[4]*ymm + wcs->amd_x_coeff[6]*2.0*xmm + + wcs->amd_x_coeff[7]*3.0*x2 + wcs->amd_x_coeff[8]*2.0*xy + + wcs->amd_x_coeff[9]*y2 + wcs->amd_x_coeff[11]*(3.0*x2+y2) + + wcs->amd_x_coeff[12]*(5.0*x4 +6.0*x2*y2+y4); + /* magnitude and color terms ignored + wcs->amd_x_coeff[16]*mag + wcs->amd_x_coeff[17]*mag*2.0*xmm + + wcs->amd_x_coeff[18]*mag*(3.0*x2+y2); + */ + + /* Derivative of X model wrt y */ + fy = wcs->amd_x_coeff[1] + wcs->amd_x_coeff[4]*xmm + + wcs->amd_x_coeff[5]*2.0*ymm + wcs->amd_x_coeff[6]*2.0*ymm + + wcs->amd_x_coeff[8]*x2 + wcs->amd_x_coeff[9]*2.0*xy + + wcs->amd_x_coeff[10]*3.0*y2 + wcs->amd_x_coeff[11]*2.0*xy + + wcs->amd_x_coeff[12]*4.0*xy*x2y2; + /* magnitude and color terms ignored + wcs->amd_x_coeff[17]*mag*2.0*ymm + + wcs->amd_x_coeff[18]*mag*2.0*xy; + */ + + /* Y plate model */ + g = wcs->amd_y_coeff[0]*ymm + wcs->amd_y_coeff[1]*xmm + + wcs->amd_y_coeff[2] + wcs->amd_y_coeff[3]*y2 + + wcs->amd_y_coeff[4]*xy + wcs->amd_y_coeff[5]*x2 + + wcs->amd_y_coeff[6]*x2y2 + wcs->amd_y_coeff[7]*y3 + + wcs->amd_y_coeff[8]*y2x + wcs->amd_y_coeff[9]*x2y + + wcs->amd_y_coeff[10]*x3 + wcs->amd_y_coeff[11]*ymm*x2y2 + + wcs->amd_y_coeff[12]*ymm*cjunk; + /* magnitude and color terms ignored + wcs->amd_y_coeff[13]*mag + wcs->amd_y_coeff[14]*mag*mag + + wcs->amd_y_coeff[15]*mag*mag*mag + wcs->amd_y_coeff[16]*mag*ymm + + wcs->amd_y_coeff[17]*mag*x2y2 + + wcs->amd_y_coeff[18]*mag*ymm*x2y2 + wcs->amd_y_coeff[19]*color; + */ + + /* Derivative of Y model wrt x */ + gx = wcs->amd_y_coeff[1] + wcs->amd_y_coeff[4]*ymm + + wcs->amd_y_coeff[5]*2.0*xmm + wcs->amd_y_coeff[6]*2.0*xmm + + wcs->amd_y_coeff[8]*y2 + wcs->amd_y_coeff[9]*2.0*xy + + wcs->amd_y_coeff[10]*3.0*x2 + wcs->amd_y_coeff[11]*2.0*xy + + wcs->amd_y_coeff[12]*4.0*xy*x2y2; + /* magnitude and color terms ignored + wcs->amd_y_coeff[17]*mag*2.0*xmm + + wcs->amd_y_coeff[18]*mag*ymm*2.0*xmm; + */ + + /* Derivative of Y model wrt y */ + gy = wcs->amd_y_coeff[0] + wcs->amd_y_coeff[3]*2.0*ymm + + wcs->amd_y_coeff[4]*xmm + wcs->amd_y_coeff[6]*2.0*ymm + + wcs->amd_y_coeff[7]*3.0*y2 + wcs->amd_y_coeff[8]*2.0*xy + + wcs->amd_y_coeff[9]*x2 + wcs->amd_y_coeff[11]*(x2+3.0*y2) + + wcs->amd_y_coeff[12]*(5.0*y4 + 6.0*x2*y2 + x4); + /* magnitude and color terms ignored + wcs->amd_y_coeff[16]*mag + wcs->amd_y_coeff[17]*mag*2.0*ymm + + wcs->amd_y_coeff[18]*mag*(x2+3.0*y2); + */ + + f = f - xi; + g = g - eta; + dx = ((-f * gy) + (g * fy)) / ((fx * gy) - (fy * gx)); + dy = ((-g * fx) + (f * gx)) / ((fx * gy) - (fy * gx)); + xmm = xmm + dx; + ymm = ymm + dy; + if ((fabs(dx) < tolerance) && (fabs(dy) < tolerance)) break; + } + +/* Convert mm from plate center to plate pixels */ + x = (wcs->ppo_coeff[2] - xmm*1000.0) / wcs->x_pixel_size; + y = (wcs->ppo_coeff[5] + ymm*1000.0) / wcs->y_pixel_size; + +/* Convert from plate pixels to image pixels */ + *xpix = x - wcs->x_pixel_offset + 1.0 - 0.5; + *ypix = y - wcs->y_pixel_offset + 1.0 - 0.5; + +/* If position is off of the image, return offscale code */ + +/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + Commented out by D.Berry (Starlink) in order to remove dependancy + on NAXIS1/NAXIS2 keywords >>>>>>>> + + if (*xpix < 0.5 || *xpix > wcs->nxpix+0.5) + return -1; + if (*ypix < 0.5 || *ypix > wcs->nypix+0.5) + return -1; + +>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> */ + + return 0; +} +/* Mar 6 1995 Original version of this code + May 4 1995 Fix eta cross terms which were all in y + Jun 21 1995 Add inverse routine + Oct 17 1995 Fix inverse routine (degrees -> radians) + Nov 7 1995 Add half pixel to image coordinates to get astrometric + plate coordinates + Feb 26 1996 Fix plate to image pixel conversion error + Feb 18 1997 Modified by D.S. Berry (Starlink) to avoid use of the image + dimensions stored in wcs->nxpix and wcs->nypix. + Sep 5 1997 Modified by R.F. Warren-Smith (Starlink) to make the + platepos and platepix functions static. + */ + + + + diff --git a/dssmap.h b/dssmap.h new file mode 100644 index 0000000..b7550de --- /dev/null +++ b/dssmap.h @@ -0,0 +1,401 @@ +#if !defined( DSSMAP_INCLUDED ) /* Include this file only once */ +#define DSSMAP_INCLUDED +/* +*+ +* Name: +* dssmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the DssMap class. + +* Invocation: +* #include "dssmap.h" + +* Description: +* This include file defines the interface to the DssMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The DssMap class implements Mappings which use a Digitised Sky +* Survey plate fit to transform between pixel coordinates and +* Equatorial coordinates. + +* Inheritance: +* The DssMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astTransform +* Apply a DssMap to transform a set of points. + +* New Methods Defined: +* Public: +* astDssFits +* Create a FitsChan holding a FITS description of the DSS plate fit. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsADssMap +* Test class membership. +* astDssMap +* Create a DssMap. +* +* Protected: +* astCheckDssMap +* Validate class membership. +* astInitDssMap +* Initialise a DssMap. +* astInitDssMapVtab +* Initialise the virtual function table for the DssMap class. +* astLoadDssMap +* Load a DssMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstDssMap +* DssMap object type. +* +* Protected: +* AstDssMapVtab +* DssMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . +* (except for code supplied by Doug Mink, as noted in this file) + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 18-FEB-1997 (DSB): +* Original version. +* 30-JUN-1997 (DSB): +* All public functions made protected. +* 4-NOV-1997 (DSB): +* Removed copy of supplied FitsChan from DssMap structure. +* 8-JAN-2003 (DSB): +* Added protected astInitDssMapVtab method. +* 21-OCT-2004 (DSB): +* Removed wcstools prototypes which clash with the MS Windows +* runtime library. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#if defined(astCLASS) /* Protected */ + +/* The code within this #if...#endif block is covered by the following + statement of terms and conditions, which differ from the terms and + conditions which apply elsewhere in this file. + +*************************************************************************** +* +* Copyright: 1988 Smithsonian Astrophysical Observatory +* You may do anything you like with these files except remove +* this copyright. The Smithsonian Astrophysical Observatory +* makes no representations about the suitability of this +* software for any purpose. It is provided "as is" without +* express or implied warranty. +* +***************************************************************************** +*/ + +/* >>>>>>>>>>>>>>>>>> SAOimage wcs.h header file <<<<<<<<<<<<<<<<<< */ + +/* libwcs/wcs.h + November 1, 1996 + By Doug Mink, Harvard-Smithsonian Center for Astrophysics */ + +struct WorldCoor { + double xref; /* x reference coordinate value (deg) */ + double yref; /* y reference coordinate value (deg) */ + double xrefpix; /* x reference pixel */ + double yrefpix; /* y reference pixel */ + double xinc; /* x coordinate increment (deg) */ + double yinc; /* y coordinate increment (deg) */ + double rot; /* rotation (deg) (from N through E) */ + double crot,srot; /* Cosine and sine of rotation angle */ + double cd11,cd12,cd21,cd22; + /* rotation matrix */ + double dc11,dc12,dc21,dc22; + /* inverse rotation matrix */ + double equinox; /* Equinox of coordinates default to 1950.0 */ + double epoch; /* Epoch of coordinates default to equinox */ + double nxpix; /* Number of pixels in X-dimension of image */ + double nypix; /* Number of pixels in Y-dimension of image */ + double plate_ra; /* Right ascension of plate center */ + double plate_dec; /* Declination of plate center */ + double plate_scale; /* Plate scale in arcsec/mm */ + double x_pixel_offset; /* X pixel offset of image lower right */ + double y_pixel_offset; /* Y pixel offset of image lower right */ + double x_pixel_size; /* X pixel_size */ + double y_pixel_size; /* Y pixel_size */ + double ppo_coeff[6]; + double amd_x_coeff[20]; /* X coefficients for plate model */ + double amd_y_coeff[20]; /* Y coefficients for plate model */ + double xpix; /* x (RA) coordinate (pixels) */ + double ypix; /* y (dec) coordinate (pixels) */ + double xpos; /* x (RA) coordinate (deg) */ + double ypos; /* y (dec) coordinate (deg) */ + int pcode; /* projection code (1-8) */ + int changesys; /* 1 for FK4->FK5, 2 for FK5->FK4 */ + /* 3 for FK4->galactic, 4 for FK5->galactic */ + int printsys; /* 1 to print coordinate system, else 0 */ + int ndec; /* Number of decimal places in PIX2WCST */ + int degout; /* 1 to always print degrees in PIX2WCST */ + int tabsys; /* 1 to put tab between RA & Dec, else 0 */ + int rotmat; /* 0 if CDELT, CROTA; 1 if CD */ + int coorflip; /* 0 if x=RA, y=Dec; 1 if x=Dec, y=RA */ + int offscl; /* 0 if OK, 1 if offscale */ + int plate_fit; /* 1 if plate fit, else 0 */ + int wcson; /* 1 if WCS is set, else 0 */ + char c1type[8]; /* 1st coordinate type code: + RA--, GLON, ELON */ + char c2type[8]; /* 2nd coordinate type code: + DEC-, GLAT, ELAT */ + char ptype[8]; /* projection type code: + -SIN, -TAN, -ARC, -NCP, -GLS, -MER, -AIT */ + char radecsys[16]; /* Reference frame: FK4, FK4-NO-E, FK5, GAPPT*/ + char sysout[16]; /* Reference frame for output: FK4, FK5 */ + char center[32]; /* Center coordinates (with frame) */ + char search_format[120]; /* search command format */ + /* where %s is replaced by WCS coordinates */ +}; + +#ifndef PI +#define PI 3.141592653589793 +#endif + +/* Conversions among hours of RA, degrees and radians. */ +#define degrad(x) ((x)*PI/180.) +#define raddeg(x) ((x)*180./PI) +#define hrdeg(x) ((x)*15.) +#define deghr(x) ((x)/15.) +#define hrrad(x) degrad(hrdeg(x)) +#define radhr(x) deghr(raddeg(x)) + + +/* WCS subroutines in wcs.c */ + +/* >>>>> DSB: Prototypes for "subroutines in wcs.c" have been removed since + they clash with prototypes defined by the MS windows runtime library and + are not needed by AST. */ + +/* Oct 26 1994 New file + * Dec 21 1994 Add rotation matrix + * Dec 22 1994 Add flag for coordinate reversal + + * Mar 6 1995 Add parameters for Digital Sky Survey plate fit + * Jun 8 1995 Add parameters for coordinate system change + * Jun 21 1995 Add parameter for plate scale + * Jul 6 1995 Add parameter to note whether WCS is set + * Aug 8 1995 Add parameter to note whether to print coordinate system + * Oct 16 1995 Add parameters to save image dimensions and center coordinates + + * Feb 15 1996 Add coordinate conversion functions + * Feb 20 1996 Add flag for tab tables + * Apr 26 1996 Add epoch of positions (actual date of image) + * Jul 5 1996 Add subroutine declarations + * Jul 19 1996 Add WCSFULL declaration + * Aug 5 1996 Add WCSNINIT to initialize WCS for non-terminated header + * Oct 31 1996 Add DCnn inverse rotation matrix + * Nov 1 1996 Add NDEC number of decimal places in output + */ +/* >>>>>>>>>>>>>>>>>>>> End of SAOimage wcs.h header file <<<<<<<<<<<<<<<< */ +#endif + +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "fitschan.h" /* Storage for FITS header cards */ + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Type Definitions. */ +/* ================= */ +/* DssMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstDssMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + void *wcs; /* Pointer to structure holding plate fit info */ + +} AstDssMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstDssMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstFitsChan *(* DssFits)( AstDssMap *, int * ); + +} AstDssMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstDssMapGlobals { + AstDssMapVtab Class_Vtab; + int Class_Init; +} AstDssMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitDssMapGlobals_( AstDssMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(DssMap) /* Check class membership */ +astPROTO_ISA(DssMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstDssMap *astDssMap_( void *, const char *, int *, ...); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstDssMap *astInitDssMap_( void *, size_t, int, AstDssMapVtab *, + const char *, AstFitsChan *, int * ); + +/* Vtab initialiser. */ +void astInitDssMapVtab_( AstDssMapVtab *, const char *, int * ); + +/* Loader. */ +AstDssMap *astLoadDssMap_( void *, size_t, AstDssMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +AstFitsChan *astDssFits_( AstDssMap *, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckDssMap(this) astINVOKE_CHECK(DssMap,this,0) +#define astVerifyDssMap(this) astINVOKE_CHECK(DssMap,this,1) + +/* Test class membership. */ +#define astIsADssMap(this) astINVOKE_ISA(DssMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astDssMap astINVOKE(F,astDssMap_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitDssMap(mem,size,init,vtab,name,fits) \ +astINVOKE(O,astInitDssMap_(mem,size,init,vtab,name,astCheckFitsChan(fits),STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitDssMapVtab(vtab,name) astINVOKE(V,astInitDssMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadDssMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadDssMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckDssMap to validate DssMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astDssFits(this) astINVOKE(O,astDssFits_(astCheckDssMap(this),STATUS_PTR)) +#endif +#endif + + + + + diff --git a/ellipse.c b/ellipse.c new file mode 100644 index 0000000..1e23724 --- /dev/null +++ b/ellipse.c @@ -0,0 +1,3055 @@ +/* +*class++ +* Name: +* Ellipse + +* Purpose: +* An elliptical region within a 2-dimensional Frame. + +* Constructor Function: +c astEllipse +f AST_ELLIPSE + +* Description: +* The Ellipse class implements a Region which represents a ellipse +* within a 2-dimensional Frame. + +* Inheritance: +* The Ellipse class inherits from the Region class. + +* Attributes: +* The Ellipse class does not define any new attributes beyond +* those which are applicable to all Regions. + +* Functions: +c In addition to those functions applicable to all Regions, the +c following functions may also be applied to all Ellipses: +f In addition to those routines applicable to all Regions, the +f following routines may also be applied to all Ellipses: +* +c - astEllipsePars: Get the geometric parameters of the Ellipse +f - AST_ELLIPSEPARS: Get the geometric parameters of the Ellipse + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 7-SEP-2004 (DSB): +* Original version. +* 4-NOV-2013 (DSB): +* Modify RegPins so that it can handle uncertainty regions that straddle +* a discontinuity. Previously, such uncertainty Regions could have a huge +* bounding box resulting in matching region being far too big. +* 6-JAN-2014 (DSB): +* Ensure cached information is available in RegCentre even if no new +* centre is supplied. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Ellipse + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "box.h" /* Box Regions */ +#include "wcsmap.h" /* Definitons of AST__DPI etc */ +#include "circle.h" /* Interface definition for circle class */ +#include "ellipse.h" /* Interface definition for this class */ +#include "mapping.h" /* Position mappings */ +#include "unitmap.h" /* Unit Mapping */ +#include "pal.h" /* Positional astronomy library */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static void (* parent_resetcache)( AstRegion *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Ellipse) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Ellipse,Class_Init) +#define class_vtab astGLOBAL(Ellipse,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstEllipseVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstEllipse *astEllipseId_( void *, int, const double[2], const double[2], const double[2], void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double *RegCentre( AstRegion *this, double *, double **, int, int, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static void Cache( AstEllipse *, int * ); +static void CalcPars( AstFrame *, double[2], double[2], double[2], double *, double *, double *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void EllipsePars( AstEllipse *, double[2], double *, double *, double *, double[2], double[2], int * ); +static void RegBaseBox( AstRegion *this, double *, double *, int * ); +static void ResetCache( AstRegion *this, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); + +/* Member functions. */ +/* ================= */ + +AstRegion *astBestEllipse_( AstPointSet *mesh, double *cen, AstRegion *unc, int *status ){ +/* +*+ +* Name: +* astBestEllipse + +* Purpose: +* Find the best fitting Ellipse through a given mesh of points. + +* Type: +* Protected function. + +* Synopsis: +* #include "ellipse.h" +* AstRegion *astBestEllipse( AstPointSet *mesh, double *cen, AstRegion *unc ) + +* Class Membership: +* Ellipse member function + +* Description: +* This function finds the best fitting Ellipse through a given mesh of +* points. Ellispes are always 2-dimensional. + +* Parameters: +* mesh +* Pointer to a PointSet holding the mesh of points. They are +* assumed to be in the Frame represented by "unc". +* cen +* Pointer to an array holding the coordinates of the new Ellipse +* centre. +* unc +* A Region representing the uncertainty associated with each point +* on the mesh. + +* Returned Value: +* Pointer to the best fitting Ellipse. It will inherit the positional +* uncertainty and Frame represented by "unc". + +* Implementation Deficiencies: +* - The method used by this function is not very accurate, and assumes +* that the supplied mesh provides uniform coverage of the entire ellipse. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + AstFrame *frm; + AstPointSet *ps2; + AstRegion *result; + double **ptr2; + double **ptr; + double *ang; + double *dist; + double *px; + double *py; + double a0; + double a; + double aa[2]; + double at; + double b; + double c0; + double c1; + double c2; + double c; + double d; + double den; + double e; + double f; + double mn; + double mx; + double p[2]; + double pa[2]; + double pb[2]; + double r1; + double r2; + double r3; + double smn; + double t1; + double t2; + double t3; + int ip; + int maxat; + int np; + double sw; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get no. of points in the mesh. */ + np = astGetNpoint( mesh ); + +/* Get pointers to the axis values. */ + ptr = astGetPoints( mesh ); + +/* Allocate work space */ + dist = astMalloc( sizeof( double )*(size_t) np ); + ang = astMalloc( sizeof( double )*(size_t) np ); + +/* Get a pointer to the Frame represented by "unc". This is the Frame to + which the supplied mesh points refer. */ + frm = astGetFrame( unc->frameset, AST__CURRENT ); + +/* Check pointers can be used safely */ + if( astOK ) { + +/* Find the first mesh point which is at a non-zero distance from the + centre. */ + px = ptr[ 0 ]; + py = ptr[ 1 ]; + for( ip = 0; ip < np; ip++, px++, py++ ) { + p[ 0 ] = *px; + p[ 1 ] = *py; + dist[ ip ] = astDistance( frm, cen, p ); + if( dist[ ip ] != AST__BAD && dist[ ip ] != 0.0 ) { + break; + } else { + ang[ ip ] = AST__BAD; + dist[ ip ] = AST__BAD; + } + } + +/* Find a point which is this distance away from the centre along the second + axis. This point is used to define zero angle when calling astAngle + below. */ + astOffset2( frm, cen, 0.0, dist[ ip ], aa ); + ang[ ip ] = astAngle( frm, aa, cen, p ); + +/* Get the distance from the centre to each of the remaining mesh points. Also + find the orientation of the radial lines through the centre to each mesh + point. At the same time, find the index of the point with the largest + radial distance. */ + maxat = ip; + r2 = dist[ maxat ]; + ip++; + px++; + py++; + for( ; ip < np; ip++, px++, py++ ) { + p[ 0 ] = *px; + p[ 1 ] = *py; + dist[ ip ] = astDistance( frm, cen, p ); + ang[ ip ] = astAngle( frm, aa, cen, p ); + if( dist[ ip ] != AST__BAD && dist[ ip ] > r2 ) { + r2 = dist[ ip ]; + maxat = ip; + } + } + +/* Find the higher index neighbouring point, wrapping back to the start + of the list when the end is reached. Note the radius and position angle + at this neighbouring point. */ + t2 = 0.0; + r3 = AST__BAD; + t3 = AST__BAD; + a0 = ang[ maxat ]; + for( ip = maxat + 1; ip < np; ip++ ) { + if( dist[ ip ] != AST__BAD ) { + r3 = dist[ ip ]; + t3 = palDrange( ang[ ip ] - a0 ); + break; + } + } + if( r3 == AST__BAD ) { + for( ip = 0; ip < maxat; ip++ ) { + if( dist[ ip ] != AST__BAD ) { + r3 = dist[ ip ]; + t3 = palDrange( ang[ ip ] - a0 ); + break; + } + } + } + +/* Find the lower index neighbouring point, wrapping back to the end + of the list when the start is reached. Note the radius and position angle + at this neighbouring point. */ + r1 = AST__BAD; + t1 = AST__BAD; + for( ip = maxat - 1; ip > -1; ip-- ) { + if( dist[ ip ] != AST__BAD ) { + r1 = dist[ ip ]; + t1 = palDrange( ang[ ip ] - a0 ); + break; + } + } + if( r1 == AST__BAD ) { + for( ip = np - 1; ip > maxat; ip-- ) { + if( dist[ ip ] != AST__BAD ) { + r1 = dist[ ip ]; + t1 = palDrange( ang[ ip ] - a0 ); + break; + } + } + } + +/* Fit a quadratic through the three pairs of (radius,angle) values. The + centre point (r2,t2) is the point which is furthest from the centre, + and the other two are the neighbouring points found above. */ + a = r2 - r1; + b = t2 - t1; + c = t2*t2 - t1*t1; + d = r3 - r2; + e = t3 - t2; + f = t3*t3 - t2*t2; + + den = c*e - b*f; + if( den != 0.0 ) { + +/* The co-efficients of the interpolating polynomial... */ + c1 = ( d*c - a*f )/den; + c2 = ( a*e - d*b )/den; + c0 = r1 - c1*t1 - c2*t1*t1; + +/* Find the largest radius (the turning point of the quadratic), and the + angle at which it occurs. */ + if( c2 < 0.0 ) { + mx = ( 4*c0*c2 - c1*c1 )/( 4*c2 ); + at = a0 - c1/( 2*c2 ); + } else { + mx = r2; + at = a0 - t2; + } + +/* This point is the end of the ellipse primary axis. Find its (x,y) + coords, and store in "pa". */ + astOffset2( frm, cen, at, mx, pa ); + +/* Resolve all the supplied points into components parallel and + perpendicular to the line joining the centre and "pa". */ + ps2 = astResolvePoints( frm, cen, pa, mesh, NULL ); + ptr2 = astGetPoints( ps2 ); + if( astOK ) { + +/* For each other mesh point, work out the length of the secondary + axis which would result if we used that point to define the ellipse. + Find the mean of these secondary axis lengths, weighted by the length + of the y component to reduce influence of poor conditioning at very + low y. */ + smn = 0.0; + sw = 0.0; + px = ptr2[ 0 ]; + py = ptr2[ 1 ]; + for( ip = 0; ip < np; ip++, px++, py++ ) { + if( *px != AST__BAD && *py != AST__BAD ) { + den = mx*mx - (*px)*(*px); + if( den > 0.0 ) { + smn += fabs( mx*(*py)*(*py) )/sqrt( den ); + sw += fabs( *py ); + } + } + } + + if( sw > 0 ) { + mn = smn/sw; + +/* Find the coords at the end of the mean secondary axis. */ + astOffset2( frm, cen, at + AST__DPIBY2, mn, pb ); + +/* Create the Ellipse to return. */ + result = (AstRegion *) astEllipse( frm, 0, cen, pa, pb, unc, "", status ); + } + } + +/* Free resources. */ + ps2 = astAnnul( ps2 ); + + } + } + + dist = astFree( dist ); + ang = astFree( ang ); + frm = astAnnul( frm ); + +/* Return NULL if anything went wrong. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result.*/ + return result; +} + +void astInitEllipseVtab_( AstEllipseVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitEllipseVtab + +* Purpose: +* Initialise a virtual function table for a Ellipse. + +* Type: +* Protected function. + +* Synopsis: +* #include "ellipse.h" +* void astInitEllipseVtab( AstEllipseVtab *vtab, const char *name ) + +* Class Membership: +* Ellipse vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Ellipse class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAEllipse) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->EllipsePars = EllipsePars; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_resetcache = region->ResetCache; + region->ResetCache = ResetCache; + + region->RegPins = RegPins; + region->RegBaseMesh = RegBaseMesh; + region->RegBaseBox = RegBaseBox; + region->RegCentre = RegCentre; + region->RegTrace = RegTrace; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "Ellipse", "Elliptical region" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void Cache( AstEllipse *this, int *status ){ +/* +* Name: +* Cache + +* Purpose: +* Calculate intermediate values and cache them in the Ellipse structure. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* void Cache( AstEllipse *this, int *status ) + +* Class Membership: +* Ellipse member function + +* Description: +* This function uses the PointSet stored in the parent Region to calculate +* some intermediate values which are useful in other methods. These +* values are stored within the Ellipse structure. + +* Parameters: +* this +* Pointer to the Ellipse. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to base Frame in Ellipse */ + double **ptr; /* Pointer to data in the encapsulated PointSet */ + double *centre; /* Array holding centre coords */ + double *point1; /* Array holding coords at end of primary axis */ + double *point2; /* Array holding coords at another point on ellipse */ + double a; /* The half-length of the primary axis */ + double angle; /* Orientation of primary axis */ + double b; /* The half-length of the secondary axis */ + int i; /* Axis index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do Nothing if the cached information is up to date. */ + if( this->stale ) { + +/* Get a pointer to the base Frame. */ + frm = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + +/* Allocate memory. */ + centre = (double *) astMalloc( sizeof( double )*2 ); + point1 = (double *) astMalloc( sizeof( double )*2 ); + point2 = (double *) astMalloc( sizeof( double )*2 ); + +/* Get pointers to the coordinate data in the parent Region structure. */ + ptr = astGetPoints( ((AstRegion *) this)->points ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the points in to the allocated memory. */ + for( i = 0; i < 2; i++ ) { + centre[ i ] = ptr[ i ][ 0 ]; + point1[ i ] = ptr[ i ][ 1 ]; + point2[ i ] = ptr[ i ][ 2 ]; + } + +/* Calculate the geometric parameters of the ellipse. */ + CalcPars( frm, centre, point1, point2, &a, &b, &angle, status ); + +/* Check the returned values. */ + if( a <= 0.0 || a == AST__BAD || b <= 0.0 || b == AST__BAD ) { + if( astOK ) astError( AST__BADIN, "astInitEllipse(%s): The " + "supplied points do not determine an " + "ellipse.", status, astGetClass( this ) ); + } + +/* Store useful things in the Ellipse structure. */ + if( astOK ) { + astFree( this->centre ); + this->centre = centre; + centre = NULL; + + astFree( this->point1 ); + this->point1 = point1; + point1 = NULL; + + this->a = a; + this->b = b; + this->angle = angle; + } + } + +/* Initialise the bounding box. This is set properly when the astRegBaseMesh + function is called. These variables should not be used unless the + "basemesh" component of the parent Region structure is set to a non-null + value. */ + this->lbx = -DBL_MAX; + this->ubx = DBL_MAX; + this->lby = -DBL_MAX; + this->uby = DBL_MAX; + +/* Free resources */ + frm = astAnnul( frm ); + if( centre ) centre = astFree( centre ); + if( point1 ) point1 = astFree( point1 ); + point2 = astFree( point2 ); + +/* Indicate cached information is up to date. */ + this->stale = 0; + + } +} + +static void CalcPars( AstFrame *frm, double centre[2], double point1[2], + double point2[2], double *a, double *b, + double *angle, int *status ){ +/* +* Name: +* CalcPars + +* Purpose: +* Calculate ellipse parameters. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* void CalcPars( AstFrame *frm, double centre[2], double point1[2], +* double point2[2], double *a, double *b, double *angle, +* int *status ) + +* Class Membership: +* Ellipse member function + +* Description: +* This function uses the supplied positions to calculate the +* geometric parameters of an ellipse. + +* Parameters: +* frm +* Pointer to the Frame in which the positions are defined. +* centre; +* Array holding centre coords. +* point1 +* Array holding coords at end of primary axis +* point2 +* Array holding coords at another point on ellipse. On exit it +* holds the coords at the end of the secondary axis. +* a +* Pointer to location at which to store the half-length of the +* primary axis. +* b +* Pointer to location at which to store the half-length of the +* secondary axis. +* angle +* Pointer to location at which to store the angle from the +* positive direction of the second Frame axis to the primary +* ellipse axis, in radians. Rotation from the second to the first +* Frame axis is positive. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double point3[ 2 ]; /* Array holding a point on the primary axis */ + double x; /* The offset parallel to the primary axis */ + double y; /* The offset perpendicular to the primary axis */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the geodesic distance between the centre and point 1 (the end of + the primary axis of the ellipse). This is the half length of the + primary axis of the ellipse (the axis which joins the centre position to + point 1). */ + *a = astDistance( frm, centre, point1 ); + +/* Find the point (point3) on the primary axis which is closest to point 2, + and thus get the geodesic offsets (resolved parallel and perpendicular to + the primary axis) between the centre and point 2. */ + if( *a > 0.0 ) { + astResolve( frm, centre, point1, point2, point3, &x, &y ); + +/* Find the half-length of the secondary ellipse axis. */ + if( astOK ) { + *b = (*a)*(*a) - x*x; + if( *b > 0.0 ) *b = (*a)*y/sqrt( *b ); + } else { + *b = *a; + } + +/* Find the angle from the positive direction of the second axis to the + primary ellipse axis. */ + point3[ 0 ] = centre[ 0 ]; + point3[ 1 ] = centre[ 1 ] + fabs( 0.1*(*a) ); + *angle = astAngle( frm, point3, centre, point1 ); + +/* Find the end point of the secondary axis. */ + (void) astOffset2( frm, centre, *angle + AST__DPIBY2, *b, point2 ); + } +} + +static void EllipsePars( AstEllipse *this, double centre[2], double *a, + double *b, double *angle, double p1[2], + double p2[2], int *status ){ +/* +*++ +* Name: +c astEllipsePars +f AST_ELLIPSEPARS + +* Purpose: +* Returns the geometric parameters of an Ellipse. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ellipse.h" +c void astEllipsePars( AstEllipse *this, double centre[2], double *a, +c double *b, double *angle, double p1[2], double p2[2] ) +f CALL AST_ELLIPSEPARS( THIS, CENTRE, A, B, ANGLE, P1, P2, STATUS ) + +* Class Membership: +* Region method. + +* Description: +c This function +f This routine +* returns the geometric parameters describing the supplied ellipse. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c centre +f CENTRE( 2 ) = DOUBLE PRECISION (Returned) +* The coordinates of the Ellipse centre are returned in this arrays. +c a +f A = DOUBLE PRECISION (Returned) +* Returned holding the half-length of the first axis of the +* ellipse. +c b +f B = DOUBLE PRECISION (Returned) +* Returned holding the half-length of the second axis of the +* ellipse. +c angle +f ANGLE = DOUBLE PRECISION (Returned) +* If the coordinate system in which the Ellipse is defined has +* axes (X,Y), then +c "*angle" +f ANGLE +* is returned holding the angle from the positive direction of +* the Y axis to the first axis of the ellipse, in radians. +* Positive rotation is in the same sense as rotation from the +* positive direction of Y to the positive direction of X. +c p1 +f P1( 2 ) = DOUBLE PRECISION (Returned) +* An array in which to return the coordinates at one of the two ends +* of the first axis of the ellipse. +c A NULL pointer can be supplied if these coordinates are not needed. +c p2 +f P2( 2 ) = DOUBLE PRECISION (Returned) +* An array in which to return the coordinates at one of the two ends +* of the second axis of the ellipse. +c A NULL pointer can be supplied if these coordinates are not needed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the coordinate system represented by the Ellipse has been +* changed since it was first created, the returned parameters refer +* to the new (changed) coordinate system, rather than the original +* coordinate system. Note however that if the transformation from +* original to new coordinate system is non-linear, the shape +* represented by the supplied Ellipse object may not be an accurate +* ellipse. +* - Values of AST__BAD are returned for the parameters without error +* if the ellipse is degenerate or undefined. +*-- +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame represented by the Ellipse */ + AstPointSet *pset; /* PointSet holding PointList axis values */ + AstRegion *this_region; /* Parent Region pointer */ + double **ptr; /* Pointer to axes values in the PointList */ + double *point1; /* Pointer to "p1" or "buf1" */ + double *point2; /* Pointer to "p2" or "buf2" */ + double buf1[2]; /* Local substitute array for "p1" */ + double buf2[2]; /* Local substitute array for "p2" */ + int i; /* Axis index */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Store a pointer to the parent region structure. */ + this_region = (AstRegion *) this; + +/* Transform the base Frame axis values into the current Frame. */ + pset = astTransform( this_region->frameset, this_region->points, 1, NULL ); + +/* Get pointers to the coordinate data. */ + ptr = astGetPoints( pset ); + +/* Choose the arrays to use - supplied arrays if possible, local arrays + otherwise. */ + if( p1 ) { + point1 = p1; + } else { + point1 = buf1; + } + if( p2 ) { + point2 = p2; + } else { + point2 = buf2; + } + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the points in to separate arrays. */ + for( i = 0; i < 2; i++ ) { + centre[ i ] = ptr[ i ][ 0 ]; + point1[ i ] = ptr[ i ][ 1 ]; + point2[ i ] = ptr[ i ][ 2 ]; + } + +/* Get the Ellipse frame. */ + frm = astGetFrame( this_region->frameset, AST__CURRENT ); + +/* Calculate the geometric parameters of the ellipse. */ + CalcPars( frm, centre, point1, point2, a, b, angle, status ); + +/* Ensure no zero values are returned. */ + if( *a <= 0.0 || *b <= 0.0 ) { + *a = AST__BAD; + *b = AST__BAD; + *angle = AST__BAD; + } + +/* Free resources */ + frm = astAnnul( frm ); + } + pset = astAnnul( pset ); +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* Ellipse member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstEllipse *this; /* Pointer to Ellipse structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Ellipse structure */ + this = (AstEllipse *) this_region; + +/* The bounding box of the mesh returned by astRegBaseMesh is used as the + bounding box of the Ellipse. These bounds are cached in the Ellipse + structure by astRegBaseMesh. Ensure astRegBaseMesh has been invoked, + so that it is safe to use the cached bounding box. */ + if( !this_region->basemesh ) (void) astAnnul( astRegBaseMesh( this ) ); + +/* Store the bounding box. */ + lbnd[ 0 ] = this->lbx; + ubnd[ 0 ] = this->ubx; + lbnd[ 1 ] = this->lby; + ubnd[ 1 ] = this->uby; + +} + +static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* Ellipse member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. Annul the pointer using astAnnul when it +* is no longer needed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Local Constants: */ +#define NP_EDGE 50 /* No. of points for determining geodesic */ + +/* Local Variables: */ + AstEllipse *this; /* The Ellipse structure */ + AstFrame *frm; /* Base Frame in encapsulated FrameSet */ + AstPointSet *result; /* Returned pointer */ + AstRegion *reg; /* Copy of supplied Ellipse */ + double **ptr; /* Pointers to data */ + double ang; /* Position angular of primary axis at "dx" */ + double angle; /* Ellipse parametric angle at point */ + double delta; /* Angular separation of points */ + double dist; /* Offset along an axis */ + double dx; /* Primary axis offset */ + double dy; /* Secondary axis offset */ + double lbnd[2]; /* Lower bounding box bounds */ + double lbx; /* Lower x bound of mesh bounding box */ + double lby; /* Lower y bound of mesh bounding box */ + double p2[ 2 ]; /* Position in 2D Frame */ + double p[ 2 ]; /* Position in 2D Frame */ + double ubnd[2]; /* Upper bounding box bounds */ + double ubx; /* Upper x bound of mesh bounding box */ + double uby; /* Upper y bound of mesh bounding box */ + int i; /* Point index */ + int np; /* No. of points in returned PointSet */ + +/* Initialise */ + result= NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this_region->basemesh ) { + result = astClone( this_region->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* Initialise the bounding box of the mesh points. */ + lbx = DBL_MAX; + ubx = -DBL_MAX; + lby = DBL_MAX; + uby = -DBL_MAX; + +/* Get a pointer to the Ellipse structure. */ + this = (AstEllipse *) this_region; + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Get the requested number of points to put on the mesh. */ + np = astGetMeshSize( this ); + +/* Store the angular increment between points. */ + delta = 2*AST__DPI/np; + +/* Create a suitable PointSet to hold the returned positions. */ + result = astPointSet( np, 2, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Loop round each point. The angle is the parametric angle, phi, where + the ellipse is defined by: + + dx = a.cos( phi ) + dy = a.sin( phi ) + + measured from the primary ellipse. Positive in the sense of rotation from + axis 2 to axis 1. */ + angle = 0.0; + for( i = 0; i < np; i++ ) { + +/* Find the offsets from the centre. "dx" is geodesic distance along the + primary axis, and dy is geodesic distance along the secondary axis. */ + dx = this->a*cos( angle ); + dy = this->b*sin( angle ); + +/* Now find the point which corresponds to this dx and dy, taking account + of the potential spherical geometry of hte coordinate system. First + move a distance "dx" from the centre along the primary axis. The + function value returned is the direction of the geodesic curve at the + end point. That is, the angle (in radians) between the positive direction + of the second axis and the continuation of the geodesic curve at the + requested end point. */ + ang = astOffset2( frm, this->centre, this->angle, dx, p ); + +/* Now move a distance "dy" from the point found above at right angles to + the primary axis. */ + astOffset2( frm, p, ang + AST__DPIBY2, dy, p2 ); + +/* Store the resulting axis values. */ + ptr[ 0 ][ i ] = p2[ 0 ]; + ptr[ 1 ][ i ] = p2[ 1 ]; + +/* Update the bounds of the mesh bounding box. The box is expressed in + terms of axis offsets from the centre, in order to avoid problems with + boxes that cross RA=0 or RA=12h */ + if( p2[ 0 ] != AST__BAD && p2[ 1 ] != AST__BAD ){ + + dist = astAxDistance( frm, 1, this->centre[ 0 ], p2[ 0 ] ); + if( dist < lbx ) { + lbx = dist; + } else if( dist > ubx ) { + ubx = dist; + } + + dist = astAxDistance( frm, 1, this->centre[ 1 ], p2[ 1 ] ); + if( dist < lby ) { + lby = dist; + } else if( dist > uby ) { + uby = dist; + } + } + +/* Increment the angular position of the next mesh point. */ + angle += delta; + } + } + +/* Save the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. Also cache + the bounding box in the Ellipse structure. */ + if( astOK && result ) { + this_region->basemesh = astClone( result ); + +/* Extend the bounding box if it contains any singularies. The astNormBox + requires a Mapping which can be used to test points in the base Frame. + Create a copy of the Circle and then set its FrameSet so that the current + Frame in the copy is the same as the base Frame in the original. */ + reg = astCopy( this ); + astSetRegFS( reg, frm ); + astSetNegated( reg, 0 ); + +/* Normalise this box. */ + lbnd[ 0 ] = this->centre[ 0 ] + lbx; + lbnd[ 1 ] = this->centre[ 1 ] + lby; + ubnd[ 0 ] = this->centre[ 0 ] + ubx; + ubnd[ 1 ] = this->centre[ 1 ] + uby; + astNormBox( frm, lbnd, ubnd, reg ); + +/* Save this box */ + this->lbx = lbnd[ 0 ]; + this->ubx = ubnd[ 0 ]; + this->lby = lbnd[ 1 ]; + this->uby = ubnd[ 1 ]; + +/* Free resources. */ + reg = astAnnul( reg ); + } + frm = astAnnul( frm ); + + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static double *RegCentre( AstRegion *this_region, double *cen, double **ptr, + int index, int ifrm, int *status ){ +/* +* Name: +* RegCentre + +* Purpose: +* Re-centre a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* double *RegCentre( AstRegion *this, double *cen, double **ptr, +* int index, int ifrm, int *status ) + +* Class Membership: +* Ellipse member function (over-rides the astRegCentre protected +* method inherited from the Region class). + +* Description: +* This function shifts the centre of the supplied Region to a +* specified position, or returns the current centre of the Region. + +* Parameters: +* this +* Pointer to the Region. +* cen +* Pointer to an array of axis values, giving the new centre. +* Supply a NULL value for this in order to use "ptr" and "index" to +* specify the new centre. +* ptr +* Pointer to an array of points, one for each axis in the Region. +* Each pointer locates an array of axis values. This is the format +* returned by the PointSet method astGetPoints. Only used if "cen" +* is NULL. +* index +* The index of the point within the arrays identified by "ptr" at +* which is stored the coords for the new centre position. Only used +* if "cen" is NULL. +* ifrm +* Should be AST__BASE or AST__CURRENT. Indicates whether the centre +* position is supplied and returned in the base or current Frame of +* the FrameSet encapsulated within "this". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If both "cen" and "ptr" are NULL then a pointer to a newly +* allocated dynamic array is returned which contains the centre +* coords of the Region. This array should be freed using astFree when +* no longer needed. If either of "ptr" or "cen" is not NULL, then a +* NULL pointer is returned. + +* Notes: +* - Some Region sub-classes do not have a centre. Such classes will report +* an AST__INTER error code if this method is called. +*/ + +/* Local Variables: */ + AstEllipse *this; /* Pointer to Ellipse structure */ + AstFrame *frm; /* Base Frame */ + double **rptr; /* Data pointers for Region PointSet */ + double *bc; /* Base Frame centre position */ + double *result; /* Returned pointer */ + double *tmp; /* Temporary array pointer */ + double a[ 2 ]; /* Original position */ + double angle; /* Orietentation of offset from old to new centre */ + double axval; /* Axis value */ + double b[ 2 ]; /* New position */ + double dist; /* Distance from old to new centre */ + double newcen[ 2 ]; /* New centre */ + int ic; /* Coordinate index */ + int ip; /* Position index */ + int ncb; /* Number of base frame coordinate values per point */ + int ncc; /* Number of current frame coordinate values per point */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Ellipse structure. */ + this = (AstEllipse *) this_region; + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* Get the number of axis values per point in the current Frame. */ + ncc = astGetNout( this_region->frameset ); + +/* An ellipse always has 2 base frame axes. */ + ncb = 2; + +/* If the centre coords are to be returned, return either a copy of the + base Frame centre coords, or transform the base Frame centre coords + into the current Frame. */ + if( !ptr && !cen ) { + if( ifrm == AST__CURRENT ) { + result = astRegTranPoint( this_region, this->centre, 1, 1 ); + } else { + result = astStore( NULL, this->centre, sizeof( double )*ncb ); + } + +/* Otherwise, we store the supplied new centre coords and return a NULL + pointer. */ + } else { + +/* Get a pointer to the axis values stored in the Region structure. */ + rptr = astGetPoints( this_region->points ); + +/* Check pointers can be used safely */ + if( astOK ) { + +/* If the centre position was supplied in the current Frame, find the + corresponding base Frame position... */ + if( ifrm == AST__CURRENT ) { + if( cen ) { + bc = astRegTranPoint( this_region, cen, 1, 0 ); + } else { + tmp = astMalloc( sizeof( double)*(size_t)ncc ); + if( astOK ) { + for( ic = 0; ic < ncc; ic++ ) tmp[ ic ] = ptr[ ic ][ index ]; + } + bc = astRegTranPoint( this_region, tmp, 1, 0 ); + tmp = astFree( tmp ); + } + +/* Replace any bad centre values with their current values. */ + for( ic = 0; ic < ncb; ic++ ) { + if( bc[ ic ] == AST__BAD ) bc[ ic ] = this->centre[ ic ]; + } + +/* If the centre position was supplied in the base Frame, use the + supplied "cen" or "ptr" pointer directly to change the coords in the + parent Region structure and the cached coords in the Ellipse structure. */ + } else { + bc = newcen; + for( ic = 0; ic < ncb; ic++ ) { + axval = cen ? cen[ ic ] : ptr[ ic ][ index ]; + newcen[ ic ] = ( axval != AST__BAD ) ? axval : this->centre[ ic ]; + } + } + +/* Find the direction and length of the offset between the old and new + centre. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + angle = astAxAngle( frm, this->centre, bc, 2 ); + dist = astDistance( frm, this->centre, bc ); + +/* Shift each point in the parent Region structure by the same length and + direction. */ + for( ip = 0; ip < 3; ip++ ) { + a[ 0 ] = rptr[ ip ][ 0 ]; + a[ 1 ] = rptr[ ip ][ 1 ]; + astOffset2( frm, a, angle, dist, b ); + rptr[ ip ][ 0 ] = b[ 0 ]; + rptr[ ip ][ 1 ] = b[ 1 ]; + } + +/* Indicate that the cache is stale. */ + astResetCache( this ); + +/* Free resources */ + frm = astAnnul( frm ); + if( bc != newcen ) bc = astFree( bc ); + } + } + +/* Return the result. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Ellipse. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* Ellipse member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Ellipse. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Ellipse "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Ellipse. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstEllipse *large_ellipse; /* Ellipse slightly larger than "this" */ + AstEllipse *small_ellipse; /* Ellipse slightly smaller than "this" */ + AstEllipse *this; /* Pointer to the Ellipse structure. */ + AstFrame *frm; /* Base Frame in supplied Ellipse */ + AstPointSet *ps1; /* Points masked by larger Ellipse */ + AstPointSet *ps2; /* Points masked by larger and smaller Ellipsees */ + AstRegion *tunc; /* Uncertainity Region from "this" */ + double **ptr; /* Pointer to axis values in "ps2" */ + double *p; /* Pointer to next axis value */ + double *safe; /* An interior point in "this" */ + double drad; /* Radius increment corresponding to border width */ + double l1; /* Length of bounding box diagonal */ + double l2; /* Length of bounding box diagonal */ + double lbnd_tunc[2]; /* Lower bounds of "this" uncertainty Region */ + double lbnd_unc[2]; /* Lower bounds of supplied uncertainty Region */ + double lim; /* Smallest semi-minor/major axis length */ + double p1[2]; /* New ellipse axis lengths */ + double ubnd_tunc[2]; /* Upper bounds of "this" uncertainty Region */ + double ubnd_unc[2]; /* Upper bounds of supplied uncertainty Region */ + int i; /* Axis index */ + int j; /* Point index */ + int np; /* No. of supplied points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the Ellipse structure. */ + this = (AstEllipse *) this_region; + +/* Check the supplied PointSet has 2 axis values per point. */ + if( astGetNcoord( pset ) != 2 && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axis " + "values per point (%d) in the supplied PointSet - should be " + "2 (internal AST programming error).", status, astGetClass( this ), + astGetNcoord( pset ) ); + } + +/* Get the number of axes in the uncertainty Region and check it is also 2. */ + if( unc && astGetNaxes( unc ) != 2 && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axes (%d) " + "in the supplied uncertainty Region - should be 2 " + "(internal AST programming error).", status, astGetClass( this ), + astGetNaxes( unc ) ); + } + +/* Get the centre of the region in the base Frame. We use this as a "safe" + interior point within the region. */ + safe = astRegCentre( this, NULL, NULL, 0, AST__BASE ); + +/* We now find the maximum distance on each axis that a point can be from the + boundary of the Ellipse for it still to be considered to be on the boundary. + First get the Region which defines the uncertainty within the Ellipse being + checked (in its base Frame), re-centre it on the interior point found + above (to avoid problems if the uncertainty region straddles a + discontinuity), and get its bounding box. */ + tunc = astGetUncFrm( this, AST__BASE ); + if( safe ) astRegCentre( tunc, safe, NULL, 0, AST__CURRENT ); + astGetRegionBounds( tunc, lbnd_tunc, ubnd_tunc ); + +/* Find the geodesic length within the base Frame of "this" of the diagonal of + the bounding box. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + l1 = astDistance( frm, lbnd_tunc, ubnd_tunc ); + +/* Also get the Region which defines the uncertainty of the supplied + points and get its bounding box. First re-centre the uncertainty at the + interior position to avoid problems from uncertainties that straddle a + discontinuity. */ + if( unc ) { + if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + +/* Find the geodesic length of the diagonal of this bounding box. */ + l2 = astDistance( frm, lbnd_unc, ubnd_unc ); + +/* Assume zero uncertainty if no "unc" Region was supplied. */ + } else { + l2 = 0.0; + } + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* The required border width is half of the total diagonal of the two bounding + boxes. */ + if( astOK ) { + drad = 0.5*( l1 + l2 ); + +/* Create two new Ellipse, one of which is larger than "this" by the amount + found above, and the other of which is smaller than "this" by the amount + found above. */ + p1[ 0 ] = this->a + 0.5*drad; + p1[ 1 ] = this->b + 0.5*drad; + large_ellipse = astEllipse( frm, 1, this->centre, p1, &(this->angle), + NULL, " ", status ); + + p1[ 0 ] = this->a - 0.5*drad; + p1[ 1 ] = this->b - 0.5*drad; + lim = 1.0E-6*drad; + if( p1[ 0 ] < lim ) p1[ 0 ] = lim; + if( p1[ 1 ] < lim ) p1[ 1 ] = lim; + small_ellipse = astEllipse( frm, 1, this->centre, p1, &(this->angle), + NULL, " ", status ); + +/* Negate the smaller region.*/ + astNegate( small_ellipse ); + +/* Points are on the boundary of "this" if they are inside both the large + Ellipse and the negated small Ellipse. First transform the supplied PointSet + using the large Ellipse, then transform them using the negated smaller + Ellipse. */ + ps1 = astTransform( large_ellipse, pset, 1, NULL ); + ps2 = astTransform( small_ellipse, ps1, 1, NULL ); + +/* Get a point to the resulting axis values, and the number of axis + values per axis. */ + ptr = astGetPoints( ps2 ); + np = astGetNpoint( ps2 ); + +/* If a mask array is to be returned, create one. */ + if( mask ) { + *mask = astMalloc( sizeof(int)*(size_t) np ); + +/* Check all the resulting points, setting mask values for all of them. */ + if( astOK ) { + +/* Initialise the mask elements on the basis of the first axis values */ + result = 1; + p = ptr[ 0 ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } else { + (*mask)[ j ] = 1; + } + } + +/* Now check for bad values on other axes. */ + for( i = 1; i < 2; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } + } + } + } + +/* If no output mask is to be made, we can break out of the check as soon + as the first bad value is found. */ + } else if( astOK ) { + result = 1; + for( i = 0; i < 2 && result; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + break; + } + } + } + } + +/* Free resources. */ + large_ellipse = astAnnul( large_ellipse ); + small_ellipse = astAnnul( small_ellipse ); + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + } + + tunc = astAnnul( tunc ); + frm = astAnnul( frm ); + safe = astFree( safe ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr, + int *status ){ +/* +*+ +* Name: +* RegTrace + +* Purpose: +* Return requested positions on the boundary of a 2D Region. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* int astTraceRegion( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* Ellipse member function (overrides the astTraceRegion method +* inherited from the parent Region class). + +* Description: +* This function returns positions on the boundary of the supplied +* Region, if possible. The required positions are indicated by a +* supplied list of scalar parameter values in the range zero to one. +* Zero corresponds to some arbitrary starting point on the boundary, +* and one corresponds to the end (which for a closed region will be +* the same place as the start). + +* Parameters: +* this +* Pointer to the Region. +* n +* The number of positions to return. If this is zero, the function +* returns without action (but the returned function value still +* indicates if the method is supported or not). +* dist +* Pointer to an array of "n" scalar parameter values in the range +* 0 to 1.0. +* ptr +* A pointer to an array of pointers. The number of elements in +* this array should equal tthe number of axes in the Frame spanned +* by the Region. Each element of the array should be a pointer to +* an array of "n" doubles, in which to return the "n" values for +* the corresponding axis. The contents of the arrays are unchanged +* if the supplied Region belongs to a class that does not +* implement this method. + +* Returned Value: +* Non-zero if the astTraceRegion method is implemented by the class +* of Region supplied, and zero if not. + +*- +*/ + +/* Local Variables; */ + AstEllipse *this; + AstFrame *frm; + AstMapping *map; + AstPointSet *bpset; + AstPointSet *cpset; + double **bptr; + double ang; + double angle; + double dx; + double dy; + double p2[ 2 ]; + double p[ 2 ]; + int i; + int ncur; + +/* Check inherited status, and the number of points to return, returning + a non-zero value to indicate that this class supports the astRegTrace + method. */ + if( ! astOK || n == 0 ) return 1; + +/* Get a pointer to the Ellipse structure. */ + this = (AstEllipse *) this_region; + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* We first determine the required positions in the base Frame of the + Region, and then transform them into the current Frame. Get the + base->current Mapping, and the number of current Frame axes. */ + map = astGetMapping( this_region->frameset, AST__BASE, AST__CURRENT ); + +/* If it's a UnitMap we do not need to do the transformation, so put the + base Frame positions directly into the supplied arrays. */ + if( astIsAUnitMap( map ) ) { + bpset = NULL; + bptr = ptr; + ncur = 2; + +/* Otherwise, create a PointSet to hold the base Frame positions (known + to be 2D since this is an ellipse). */ + } else { + bpset = astPointSet( n, 2, " ", status ); + bptr = astGetPoints( bpset ); + ncur = astGetNout( map ); + } + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Loop round each point. */ + for( i = 0; i < n; i++ ) { + +/* The supplied scalar parameter values are the parametric angles, phi, + where the ellipse is defined by: + + dx = a.cos( phi ) + dy = a.sin( phi ) + + measured from the primary ellipse. Positive in the sense of rotation from + axis 2 to axis 1. */ + angle = dist[ i ]*2*AST__DPI; + +/* Find the offsets from the centre. "dx" is geodesic distance along the + primary axis, and dy is geodesic distance along the secondary axis. */ + dx = this->a*cos( angle ); + dy = this->b*sin( angle ); + +/* Now find the point which corresponds to this dx and dy, taking account + of the potential spherical geometry of hte coordinate system. First + move a distance "dx" from the centre along the primary axis. The + function value returned is the direction of the geodesic curve at the + end point. That is, the angle (in radians) between the positive direction + of the second axis and the continuation of the geodesic curve at the + requested end point. */ + ang = astOffset2( frm, this->centre, this->angle, dx, p ); + +/* Now move a distance "dy" from the point found above at right angles to + the primary axis. */ + astOffset2( frm, p, ang + AST__DPIBY2, dy, p2 ); + +/* Store the resulting axis values. */ + bptr[ 0 ][ i ] = p2[ 0 ]; + bptr[ 1 ][ i ] = p2[ 1 ]; + } + } + +/* If required, transform the base frame positions into the current + Frame, storing them in the supplied array. Then free resources. */ + if( bpset ) { + cpset = astPointSet( n, ncur, " ", status ); + astSetPoints( cpset, ptr ); + + (void) astTransform( map, bpset, 1, cpset ); + + cpset = astAnnul( cpset ); + bpset = astAnnul( bpset ); + } + +/* Free remaining resources. */ + map = astAnnul( map ); + frm = astAnnul( frm ); + +/* Return a non-zero value to indicate that this class supports the + astRegTrace method. */ + return 1; +} + +static void ResetCache( AstRegion *this, int *status ){ +/* +* Name: +* ResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* void ResetCache( AstRegion *this, int *status ) + +* Class Membership: +* Region member function (overrides the astResetCache method +* inherited from the parent Region class). + +* Description: +* This function clears cached information from the supplied Region +* structure. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + if( this ) { + ( (AstEllipse *) this )->stale = 1; + (*parent_resetcache)( this, status ); + } +} + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* Ellipse method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* Indicate that cached information will need to be re-calculated before + it is next used. */ + astResetCache( this_region ); +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Ellipse method (over-rides the astSimplify method inherited +* from the Region class). + +* Description: +* This function invokes the parent Region Simplify method, and then +* performs any further region-specific simplification. +* +* If the Mapping from base to current Frame is not a UnitMap, this +* will include attempting to fit a new Region to the boundary defined +* in the current Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstMapping *map; /* Base -> current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstPointSet *mesh; /* Mesh of current Frame positions */ + AstPointSet *ps2; /* Ellipse PointSet in current Frame */ + AstRegion *new; /* Pointer to simplified Region */ + AstRegion *newreg; /* Equivalent circle or ellipse */ + AstRegion *this; /* Pointer to supplied Region structure */ + AstRegion *unc; /* Pointer to uncertainty Region */ + double **ptr2; /* Pointer axis values in "ps2" */ + double *cen; /* Pointer to array holding new centre coords */ + int ic; /* Axis index */ + int nc; /* No. of axis values per point */ + int ok; /* Was the new centre found OK? */ + int simpler; /* Has some simplication taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the supplied Region structure. */ + this = (AstRegion *) this_mapping; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. */ + new = (AstRegion *) (*parent_simplify)( this_mapping, status ); + +/* Note if any simplification took place. This is assumed to be the case + if the pointer returned by the above call is different to the supplied + pointer. */ + simpler = ( new != this ); + +/* We attempt to simplify the Ellipse by re-defining it within its current + Frame. Transforming the Ellipse from its base to its current Frame may + result in the region no longer being an ellipse. We test this by + transforming a set of bounds on the Ellipse boundary. */ + map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT ); + +/* Get a mesh of points covering the Ellipse in its current Frame. */ + mesh = astRegMesh( new ); + +/* Get the Region describing the positional uncertainty within the Ellipse in + its current Frame. */ + unc = astGetUncFrm( new, AST__CURRENT ); + +/* Transform the PointSet holding the ellipse centre into the current + Frame, and copy the axis values into a new array. */ + ps2 = astRegTransform( this, this->points, 1, NULL, NULL ); + nc = astGetNcoord( ps2 ); + cen = astMalloc( sizeof( double )*(size_t) nc ); + ptr2 = astGetPoints( ps2 ); + if( astOK ) { + ok = 1; + for( ic = 0; ic < nc; ic++ ) { + cen[ ic ] = ptr2[ ic ][ 0 ]; + if( cen[ ic ] == AST__BAD ) ok = 0; + } + +/* Find the best fitting Circle (defined in the current Frame) through these + points */ + newreg = ok ? astBestCircle( mesh, cen, unc ) : NULL; + +/* See if all points within this mesh fall on the boundary of the best + fitting Circle, to within the uncertainty of the Region. */ + if( newreg && astRegPins( newreg, mesh, NULL, NULL ) ) { + +/* If so, use the new Circle in place of the original Region. */ + (void) astAnnul( new ); + new = astClone( newreg ); + simpler =1; + +/* Otherwise, if the region is 2-d we see if an Ellipse can represent the + mesh. */ + } else if( ok && nc == 2 ){ + +/* Find the best fitting Ellipse (defined in the current Frame) through these + points */ + if( newreg ) (void) astAnnul( newreg ); + newreg = astBestEllipse( mesh, cen, unc ); + +/* See if all points within this mesh fall on the boundary of the best + fitting Ellipse, to within the uncertainty of the Region. */ + if( newreg && astRegPins( newreg, mesh, NULL, NULL ) ) { + +/* If so, use the new Ellipse in place of the original Region. */ + (void) astAnnul( new ); + new = astClone( newreg ); + simpler = 1; + } + } + +/* Free resources. */ + if( newreg ) newreg = astAnnul( newreg ); + } + + ps2 = astAnnul( ps2 ); + cen = astFree( cen ); + mesh = astAnnul( mesh ); + unc = astAnnul( unc ); + map = astAnnul( map ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. + If the supplied Region had no uncertainty, ensure the returned Region + has no uncertainty. Otherwise, return a clone of the supplied pointer. */ + if( simpler ){ + astRegOverlay( new, this, 1 ); + result = (AstMapping *) new; + + } else { + new = astAnnul( new ); + result = astClone( this ); + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a Ellipse to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Ellipse member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a Ellipse and a set of points encapsulated in a +* PointSet and transforms the points by setting axis values to +* AST__BAD for all points which are outside the region. Points inside +* the region are copied unchanged from input to output. + +* Parameters: +* this +* Pointer to the Ellipse. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The forward and inverse transformations are identical for a +* Region. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of axes in the Frame represented by the Ellipse. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstEllipse *this; /* Pointer to Ellipse */ + AstFrame *frm; /* Pointer to base Frame in FrameSet */ + AstPointSet *pset_res; /* Pointer to PointSet holding resolved components */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_out; /* Pointer to output coordinate data */ + double **ptr_res; /* Pointer to resolved components coordinate data */ + double *px; /* Pointer to array of primary axis components */ + double *py; /* Pointer to array of secondary axis components */ + double c1; /* Constant */ + double c2; /* Constant */ + double d; /* Elliptical distance to current point */ + int closed; /* Is the boundary part of the Region? */ + int coord; /* Zero-based index for coordinates */ + int inside; /* Is the point inside the Region? */ + int ncoord_out; /* No. of coordinates per output point */ + int neg; /* Has the Region been negated? */ + int npoint; /* No. of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the Ellipse structure. */ + this = (AstEllipse *) this_mapping; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, + containing a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet to transform the supplied positions + from the current Frame in the encapsulated FrameSet (the Frame + represented by the Region), to the base Frame (the Frame in which the + Region is defined). This call also returns a pointer to the base Frame + of the encapsulated FrameSet. Note, the returned pointer may be a + clone of the "in" pointer, and so we must be carefull not to modify the + contents of the returned PointSet. */ + pset_tmp = astRegTransform( this, in, 0, NULL, &frm ); + +/* Resolve all the base Frame positions into components parallel to and + perpendicular to the primary axis, relative to the ellipse centre. The + components are returned in a new PointSet. */ + pset_res = astResolvePoints( frm, this->centre, this->point1, pset_tmp, NULL ); + +/* Determine the numbers of points from the component PointSet and obtain + pointers for accessing the component and output coordinate values. */ + npoint = astGetNpoint( pset_res ); + ptr_res = astGetPoints( pset_res ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* See if the boundary is part of the Region. */ + closed = astGetClosed( this ); + +/* See if the Region has been negated. */ + neg = astGetNegated( this ); + +/* Form some frequently needed constants. */ + c1 = 1.0/(this->a*this->a); + c2 = 1.0/(this->b*this->b); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + px = ptr_res[ 0 ]; + py = ptr_res[ 1 ]; + +/* Loop round each point */ + for ( point = 0; point < npoint; point++, px++, py++ ) { + +/* Bad input points result in bad output points */ + if( *px == AST__BAD || *py == AST__BAD ) { + inside = 0; + +/* If the input points are good... */ + } else { + +/* Find the elliptical distance from the centre to the supplied point (the + ellipse circumference has an "elliptical distance" of 1.0 at all points).*/ + d = c1*(*px)*(*px) + c2*(*py)*(*py); + +/* Now consider whether this radius value puts the point in or out of the + Ellipse. */ + if( d != AST__BAD ){ + if( neg ) { + if( closed ) { + inside = ( d >= 1.0 ); + } else { + inside = ( d > 1.0 ); + } + } else { + if( closed ) { + inside = ( d <= 1.0 ); + } else { + inside = ( d < 1.0 ); + } + } + } else { + inside = 0; + } + } + +/* If the point is outside, store bad output values. */ + if( !inside ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + +/* Free resources */ + pset_tmp = astAnnul( pset_tmp ); + pset_res = astAnnul( pset_res ); + frm = astAnnul( frm ); + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Ellipse objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Ellipse objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstEllipse *in; /* Pointer to input Ellipse */ + AstEllipse *out; /* Pointer to output Ellipse */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Ellipses. */ + in = (AstEllipse *) objin; + out = (AstEllipse *) objout; + +/* For safety, first clear any references to the input memory from + the output Ellipse. */ + out->centre = NULL; + out->point1 = NULL; + +/* Copy dynamic memory contents */ + out->centre = astStore( NULL, in->centre, sizeof( double )*2 ); + out->point1 = astStore( NULL, in->point1, sizeof( double )*2 ); +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Ellipse objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Ellipse objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstEllipse *this; /* Pointer to Ellipse */ + +/* Obtain a pointer to the Ellipse structure. */ + this = (AstEllipse *) obj; + +/* Annul all resources. */ + this->centre = astFree( this->centre ); + this->point1 = astFree( this->point1 ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Ellipse objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Ellipse class to an output Channel. + +* Parameters: +* this +* Pointer to the Ellipse whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstEllipse *this; /* Pointer to the Ellipse structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Ellipse structure. */ + this = (AstEllipse *) this_object; + +/* Write out values representing the instance variables for the + Ellipse class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAEllipse and astCheckEllipse functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Ellipse,Region) +astMAKE_CHECK(Ellipse) + +AstEllipse *astEllipse_( void *frame_void, int form, const double centre[2], + const double point1[2], const double point2[2], + AstRegion *unc, const char *options, int *status, ...) { +/* +*++ +* Name: +c astEllipse +f AST_ELLIPSE + +* Purpose: +* Create a Ellipse. + +* Type: +* Public function. + +* Synopsis: +c #include "ellipse.h" +c AstEllipse *astEllipse( AstFrame *frame, int form, const double centre[2], +c const double point1[2], const double point2[2], +c AstRegion *unc, const char *options, ... ) +f RESULT = AST_ELLIPSE( FRAME, FORM, CENTRE, POINT1, POINT2, UNC, OPTIONS, +f STATUS ) + +* Class Membership: +* Ellipse constructor. + +* Description: +* This function creates a new Ellipse and optionally initialises its +* attributes. +* +* A Ellipse is a Region which represents a elliptical area within the +* supplied 2-dimensional Frame. + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame in which the region is defined. It must +* have exactly 2 axes. A deep copy is taken of the supplied Frame. +* This means that any subsequent changes made to the Frame using the +* supplied pointer will have no effect the Region. +c form +f FORM = INTEGER (Given) +* Indicates how the ellipse is described by the remaining parameters. +* A value of zero indicates that the ellipse is specified by a +* centre position and two positions on the circumference. A value of +* one indicates that the ellipse is specified by its centre position, +* the half-lengths of its two axes, and the orientation of its first +* axis. +c centre +f CENTRE( 2 ) = DOUBLE PRECISION (Given) +c An array of 2 doubles, +f An array +* containing the coordinates at the centre of +* the ellipse. +c point1 +f POINT1( 2 ) = DOUBLE PRECISION (Given) +c An array of 2 doubles. If "form" +f If FORM +* is zero, this array should contain the coordinates of one of the four +* points where an axis of the ellipse crosses the circumference of the +* ellipse. +c If "form" +f If FORM +* is one, it should contain the lengths of semi-major and +* semi-minor axes of the ellipse, given as geodesic distances +* within the Frame. +c point2 +f POINT2( 2 ) = DOUBLE PRECISION (Given) +c An array of 2 doubles. If "form" +f If FORM +* is zero, this array should containing the coordinates at some other +* point on the circumference of the ellipse, distinct from +c "point1". If "form" +f POINT1. If FORM +* is one, the first element of this array should hold the angle +* between the second axis of the Frame and the first ellipse axis +* (i.e. the ellipse axis which is specified first in the +c "point1" +f POINT1 +* array), and the second element will be ignored. The angle should be +* given in radians, measured positive in the same sense as rotation +* from the positive direction of the second Frame axis to the positive +* direction of the first Frame axis. +c unc +f UNC = INTEGER (Given) +* An optional pointer to an existing Region which specifies the +* uncertainties associated with the boundary of the Ellipse being created. +* The uncertainty in any point on the boundary of the Ellipse is found by +* shifting the supplied "uncertainty" Region so that it is centred at +* the boundary point being considered. The area covered by the +* shifted uncertainty Region then represents the uncertainty in the +* boundary position. The uncertainty is assumed to be the same for +* all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created Ellipse. Alternatively, +f a null Object pointer (AST__NULL) +c a NULL Object pointer +* may be supplied, in which case a default uncertainty is used +* equivalent to a box 1.0E-6 of the size of the Ellipse being created. +* +* The uncertainty Region has two uses: 1) when the +c astOverlap +f AST_OVERLAP +* function compares two Regions for equality the uncertainty +* Region is used to determine the tolerance on the comparison, and 2) +* when a Region is mapped into a different coordinate system and +* subsequently simplified (using +c astSimplify), +f AST_SIMPLIFY), +* the uncertainties are used to determine if the transformed boundary +* can be accurately represented by a specific shape of Region. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Ellipse. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Ellipse. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astEllipse() +f AST_ELLIPSE = INTEGER +* A pointer to the new Ellipse. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstEllipse *new; /* Pointer to new Ellipse */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the supplied Frame structure. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the Ellipse, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitEllipse( NULL, sizeof( AstEllipse ), !class_init, &class_vtab, + "Ellipse", frame, form, centre, point1, point2, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Ellipse's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Ellipse. */ + return new; +} + +AstEllipse *astEllipseId_( void *frame_void, int form, const double centre[2], + const double point1[2], const double point2[2], + void *unc_void, const char *options, ... ) { +/* +* Name: +* astEllipseId_ + +* Purpose: +* Create a Ellipse. + +* Type: +* Private function. + +* Synopsis: +* #include "ellipse.h" +* AstEllipse *astEllipseId( void *frame_void, int form, const double centre[2], +* const double point1[2], const double point2[2], +* void *unc_void, const char *options, ..., int *status ) + +* Class Membership: +* Ellipse constructor. + +* Description: +* This function implements the external (public) interface to the +* astEllipse constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astEllipse_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astEllipse_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astEllipse_. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The ID value associated with the new Ellipse. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstEllipse *new; /* Pointer to new Ellipse */ + AstRegion *unc; /* Pointer to Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Frame pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Obtain a Region pointer from the supplied "unc" ID and validate the + pointer to ensure it identifies a valid Region . */ + unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL; + +/* Initialise the Ellipse, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitEllipse( NULL, sizeof( AstEllipse ), !class_init, &class_vtab, + "Ellipse", frame, form, centre, point1, point2, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Ellipse's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Ellipse. */ + return astMakeId( new ); +} + +AstEllipse *astInitEllipse_( void *mem, size_t size, int init, AstEllipseVtab *vtab, + const char *name, AstFrame *frame, int form, + const double centre[2], const double point1[2], + const double point2[2], AstRegion *unc, int *status ){ +/* +*+ +* Name: +* astInitEllipse + +* Purpose: +* Initialise a Ellipse. + +* Type: +* Protected function. + +* Synopsis: +* #include "ellipse.h" +* AstEllipse *astInitEllipse( void *mem, size_t size, int init, +* AstEllipseVtab *vtab, const char *name, +* AstFrame *frame, const double centre[2], +* const double point1[2], const double point2[2], +* AstRegion *unc ) + +* Class Membership: +* Ellipse initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Ellipse object. It allocates memory (if necessary) to accommodate +* the Ellipse plus any additional data associated with the derived class. +* It then initialises a Ellipse structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Ellipse at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Ellipse is to be initialised. +* This must be of sufficient size to accommodate the Ellipse data +* (sizeof(Ellipse)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Ellipse (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Ellipse +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Ellipse's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Ellipse. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame in which the region is defined. +* form +* Indicates how the "point" parameter should be interpreted. +* Should be either 0 or 1. +* centre +* An array of double, with one element for each Frame axis (Naxes +* attribute) containing the coordinates of the ellipse centre. +* point1 +* An array of double, with one element for each Frame axis (Naxes +* attribute). If "form" is zero, it should contain the coordinates at +* the end of one of the axes of the ellipse. If "form" is one, it +* should contain the semi-major and semi-minor axes of the ellipse. +* point2 +* An array of double, with one element for each Frame axis (Naxes +* attribute). If "form" is zero, it should contain the coordinates at +* some other point on the circumference of the ellipse. If "form" is +* one, element [1] is ignored and element [0] should contain the +* angle from the second frame axis to the first ellipse axis, given in +* radians, measured positive in the same sense as rotation from the +* positive direction of the second Frame axis to the positive +* direction of the first Frame axis. The "first" ellipse axis is +* whichever of the semi-major or semi-minor axis is specified first in +* the "point1" array. +* unc +* A pointer to a Region which specifies the uncertainty in the +* supplied positions (all points on the boundary of the new Ellipse +* being initialised are assumed to have the same uncertainty). A NULL +* pointer can be supplied, in which case default uncertainties equal to +* 1.0E-6 of the dimensions of the new Ellipse's bounding box are used. +* If an uncertainty Region is supplied, it must be either a Box, a +* Circle or an Ellipse, and its encapsulated Frame must be related +* to the Frame supplied for parameter "frame" (i.e. astConvert +* should be able to find a Mapping between them). Two positions +* the "frame" Frame are considered to be co-incident if their +* uncertainty Regions overlap. The centre of the supplied +* uncertainty Region is immaterial since it will be re-centred on the +* point being tested before use. A deep copy is taken of the supplied +* Region. + +* Returned Value: +* A pointer to the new Ellipse. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstEllipse *new; /* Pointer to new Ellipse */ + AstPointSet *pset; /* PointSet to pass to Region initialiser */ + double **ptr; /* Pointer to coords data in pset */ + const double *p1; /* Pointer to circumference point 1 */ + const double *p2; /* Pointer to circumference point 2 */ + int i; /* axis index */ + int nc; /* No. of axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitEllipseVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the supplied value for "form" is legal. */ + if( form != 0 && form != 1 && astOK ) { + astError( AST__BADIN, "astInitEllipse(%s): The value supplied for " + "parameter \"form\" (%d) is illegal - it should be 0 or 1 " + "(programming error).", status, name, form ); + } + +/* Get the number of axis values required for each position. */ + nc = astGetNaxes( frame ); + +/* Report an error if the Frame is not 2-dimensional. */ + if( nc != 2 ) { + astError( AST__BADIN, "astInitEllipse(%s): The supplied %s has %d " + "axes - ellipses must have exactly 2 axes.", status, name, + astGetClass( frame ), nc ); + } + +/* If the ellipse is specified by axis lengths and orientation, find two + points on the circumference (ends of the two ellipse axes). */ + if( form == 1 ) { + p1 = astMalloc( sizeof( double )*2 ); + p2 = astMalloc( sizeof( double )*2 ); + if( astOK ) { + astOffset2( frame, centre, *point2, point1[ 0 ], (double *) p1 ); + astOffset2( frame, centre, *point2 + AST__DPIBY2, point1[ 1 ], + (double *) p2 ); + } + +/* If the ellipse is specified by two points on the circumference, use + them. */ + } else { + p1 = point1; + p2 = point2; + } + +/* Create a PointSet to hold the supplied values, and get points to the + data arrays. */ + pset = astPointSet( 3, nc, " ", status ); + ptr = astGetPoints( pset ); + +/* Copy the supplied coordinates into the PointSet, checking that no bad + values have been supplied. */ + for( i = 0; astOK && i < nc; i++ ) { + if( centre[ i ] == AST__BAD ) { + astError( AST__BADIN, "astInitEllipse(%s): The value of axis %d is " + "undefined at the ellipse centre.", status, name, i + 1 ); + } + if( astOK && p1[ i ] == AST__BAD ) { + astError( AST__BADIN, "astInitEllipse(%s): The value of axis %d is " + "undefined at point 1 on the circumference of " + "the ellipse.", status, name, i + 1 ); + } + if( astOK && p2[ i ] == AST__BAD ) { + astError( AST__BADIN, "astInitEllipse(%s): The value of axis %d is " + "undefined at point 2 on the circumference of " + "the ellipse.", status, name, i + 1 ); + } + ptr[ i ][ 0 ] = centre[ i ]; + ptr[ i ][ 1 ] = p1[ i ]; + ptr[ i ][ 2 ] = p2[ i ]; + } + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Initialise a Region structure (the parent class) as the first component + within the Ellipse structure, allocating memory if necessary. */ + new = (AstEllipse *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frame, pset, unc ); + + if ( astOK ) { + +/* Initialise the Ellipse data. */ +/* ------------------------ */ + new->stale = 1; + +/* If an error occurred, clean up by deleting the new Ellipse. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free resources. */ + pset = astAnnul( pset ); + if( form == 1 ) { + p1 = astFree( (void *) p1 ); + p2 = astFree( (void *) p2 ); + } + +/* Return a pointer to the new Ellipse. */ + return new; +} + +AstEllipse *astLoadEllipse_( void *mem, size_t size, AstEllipseVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadEllipse + +* Purpose: +* Load a Ellipse. + +* Type: +* Protected function. + +* Synopsis: +* #include "ellipse.h" +* AstEllipse *astLoadEllipse( void *mem, size_t size, AstEllipseVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Ellipse loader. + +* Description: +* This function is provided to load a new Ellipse using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Ellipse structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Ellipse at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Ellipse is to be +* loaded. This must be of sufficient size to accommodate the +* Ellipse data (sizeof(Ellipse)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Ellipse (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Ellipse structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstEllipse) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Ellipse. If this is NULL, a pointer +* to the (static) virtual function table for the Ellipse class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Ellipse" is used instead. + +* Returned Value: +* A pointer to the new Ellipse. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstEllipse *new; /* Pointer to the new Ellipse */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Ellipse. In this case the + Ellipse belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstEllipse ); + vtab = &class_vtab; + name = "Ellipse"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitEllipseVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Ellipse. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Ellipse" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* Indicate that no cache intermediate results are yet available in the + Ellipse structure */ + new->stale = 1; + +/* If an error occurred, clean up by deleting the new Ellipse. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Ellipse pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astEllipsePars_( AstEllipse *this, double centre[2], double *a, + double *b, double *angle, double p1[2], double p2[2], + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Ellipse,EllipsePars))( this, centre, a, b, + angle, p1, p2, status ); +} + + + + + + diff --git a/ellipse.h b/ellipse.h new file mode 100644 index 0000000..cdd46f7 --- /dev/null +++ b/ellipse.h @@ -0,0 +1,244 @@ +#if !defined( ELLIPSE_INCLUDED ) /* Include this file only once */ +#define ELLIPSE_INCLUDED +/* +*+ +* Name: +* ellipse.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Ellipse class. + +* Invocation: +* #include "ellipse.h" + +* Description: +* This include file defines the interface to the Ellipse class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Ellipse class implement a Region which represents a simple interval +* on each axis of the encapsulated Frame + +* Inheritance: +* The Ellipse class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 7-SEP-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* Ellipse structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstEllipse { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *centre; /* Ellipse centre coords */ + double *point1; /* Point at end of primary axis */ + double angle; /* Orientation of primary axis */ + double a; /* Half-length of primary axis */ + double b; /* Half-length of secondary axis */ + double lbx; /* Lower x limit of mesh bounding box */ + double ubx; /* Upper y limit of mesh bounding box */ + double lby; /* Lower x limit of mesh bounding box */ + double uby; /* Upper y limit of mesh bounding box */ + int stale; /* Is cached information stale? */ + +} AstEllipse; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstEllipseVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* EllipsePars)( AstEllipse *, double[2], double *, double *, double *, double[2], double[2], int * ); + +} AstEllipseVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstEllipseGlobals { + AstEllipseVtab Class_Vtab; + int Class_Init; +} AstEllipseGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitEllipseGlobals_( AstEllipseGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Ellipse) /* Check class membership */ +astPROTO_ISA(Ellipse) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstEllipse *astEllipse_( void *, int, const double[2], const double[2], const double[2], AstRegion *, const char *, int *, ...); +#else +AstEllipse *astEllipseId_( void *, int, const double[2], const double[2], const double[2], AstRegion *, const char *, ... )__attribute__((format(printf,7,8))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstEllipse *astInitEllipse_( void *, size_t, int, AstEllipseVtab *, + const char *, AstFrame *, int, const double[2], + const double[2], const double[2], AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitEllipseVtab_( AstEllipseVtab *, const char *, int * ); + +/* Loader. */ +AstEllipse *astLoadEllipse_( void *, size_t, AstEllipseVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astEllipsePars_( AstEllipse *, double[2], double *, double *, double *, double[2], double[2], int * ); +# if defined(astCLASS) /* Protected */ +AstRegion *astBestEllipse_( AstPointSet *, double *, AstRegion *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckEllipse(this) astINVOKE_CHECK(Ellipse,this,0) +#define astVerifyEllipse(this) astINVOKE_CHECK(Ellipse,this,1) + +/* Test class membership. */ +#define astIsAEllipse(this) astINVOKE_ISA(Ellipse,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astEllipse astINVOKE(F,astEllipse_) +#else +#define astEllipse astINVOKE(F,astEllipseId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitEllipse(mem,size,init,vtab,name,frame,form,p1,p2,p3,unc) \ +astINVOKE(O,astInitEllipse_(mem,size,init,vtab,name,frame,form,p1,p2,p3,unc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitEllipseVtab(vtab,name) astINVOKE(V,astInitEllipseVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadEllipse(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadEllipse_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckEllipse to validate Ellipse pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +#define astEllipsePars(this,centre,a,b,angle,p1,p2) \ +astINVOKE(V,astEllipsePars_(astCheckEllipse(this),centre,a,b,angle,p1,p2,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astBestEllipse(pset,cen,unc) astBestEllipse_(pset,cen,unc,STATUS_PTR) +#endif +#endif + + + + + diff --git a/ems.h b/ems.h new file mode 100644 index 0000000..5b6fef3 --- /dev/null +++ b/ems.h @@ -0,0 +1,185 @@ +/*+ + * Name: + * ems.h + + * Purpose: + * EMS_ C interface header file. + + * Language: + * Starlink ANSI C + + * Description: + * This include file contains the function prototypes for all + * EMS C interface routines and defines EMS__VERSN to be the major + * version number + + * Authors: + * PCTR: P.C.T. Rees (STARLINK) + * AJC: A.J.Chipperfield (STARLINK) + * TIMJ: Tim Jenness (JAC, Hawaii) + * {enter_new_authors_here} + + * History: + * 19-SEP-1990 (PCTR): + * Original version. + * 21-JUN-1991 (PCTR): + * Made all given character strings type "const". + * 5-OCT-1993 (PCTR): + * Updated for Vn. 1.2-3 + * 28-SEP-1994 (AJC): + * V1.4 Added ems_facer_c and ems_errno_c + * 21-JUN-1995 (AJC): + * V1.5 Added ems1_starf_c + * 13-MAY-1999 (AJC): + * Added the emsXxx form of name + * and #define old_names = new_names + * Removed ems_tune/gtune/show/_c + * Added ems1_get_facility_error + * 27-JUL-2001 (AJC): + * Removed emsFmtx + * Add emsExpnd, emsTune + * 13-AUG-2001 (AJC): + * Removed emsFioer + * #define EMS__VERSN + * 20-SEP-2001 (AJC): + * Added emsSetnc and point ems_setc_c at it + * 3-MAR-2006 (TIMJ): + * Add emsSetu / emsSetp / emsSeti64 + * 30-JUL-2008 (PWD): + * Added emsGtune. + * 31-JUL-2008 (PWD): + * Added emsStune and changed emsGtune to return the value as a result. + * Marked emsTune as deprecated. + * 10-SEP-2008 (TIMJ): + * Remove fortran prototypes. Should not be in a public include file. + * 16-SEP-2008 (TIMJ): + * Remove 3 arg version of emsSetc + * {enter_changes_here} + + * Bugs: + * {note_any_bugs_here} + + *- */ + +#ifndef EMS_DEFINED +#define EMS_DEFINED + +/* ANSI types */ +#include +#include +#include + + +/* EMS Major Version */ +#define EMS__VERSN 2 + +/* Function Prototypes: */ +void emsAnnul( int *status ); + +void emsBegin( int *status ); + +void emsEload( char *param, + int *parlen, + char *opstr, + int *oplen, + int *status ); + +void emsEnd( int * status ); + +void emsErrno( const char *token, + int errval ); + +void emsExpnd( const char *text, + char *opstr, + const int maxlen, + int *oplen, + int *status ); + +void emsFacer( const char *token, + int status ); + +int emsGtune( const char *key, + int *status ); + +void emsLevel( int *level ); + +void emsMark( void ); + +void emsMload( const char *msg, + const char *text, + char *opstr, + int *oplen, + int *status ); + +void emsRenew( void ); + +void emsRep( const char *err, + const char *text, + int *status ); + +void emsRlse( void ); + +void emsSetc( const char *token, + const char *cvalue ); + +void emsSetnc( const char *token, + const char *cvalue, + int mxchar ); + +void emsSetd( const char *token, + double dvalue ); + +void emsSeti( const char *token, + int ivalue ); + +void emsSeti64( const char *token, + int64_t ivalue ); + +void emsSetl( const char *token, + int lvalue ); + +void emsSetr( const char *token, + float rvalue ); + +void emsSetp( const char *token, + void * pvalue ); + +void emsSetu( const char *token, + unsigned int ivalue ); + +void emsStat( int *status ); + +void emsSyser( const char *token, + int systat ); + +int emsStune( const char *key, + const int value, + int *status ); + +/* Deprecated function. */ +void emsTune( const char *key, + const int value, + int *status ); + +/* Internal Functions */ +/* Not for general use */ +int ems1Starf( const char *envar, + const char *relpath, + const char *acmode, + char **filename, + int *pathlen ); + +void ems1_get_facility_error( unsigned int errcode, + char **facility_name, + char **error_ident, + char **error_text ); + +/* Required by MERS. Not to be used by anyone else */ + +void ems1Rform( const char *text, const int maxlen, int *iposn, char *string, int *strlength ); + +void ems1Gesc( const char *escchr, const char *string, int *iposn ); + +void ems1Gnam( const char *string, int *iposn, char *name, int *namlen, int *status); + +#endif /* EMS_DEFINED */ diff --git a/erfa.h b/erfa.h new file mode 100644 index 0000000..1f98296 --- /dev/null +++ b/erfa.h @@ -0,0 +1,72 @@ +#if !defined( ERFA_INCLUDED ) /* Include this file only once */ +#define ERFA_INCLUDED +/* +*+ +* Name: +* erfa.h + +* Purpose: +* Function prototypes for ERFA routines. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Include file + +* Description: +* Function prototypes for ERFA routines. Note, the +* --with-external_pal configuration option implies that an external +* ERFA library will also be used. + +* Authors: +* DSBJ: David S Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 23-FEB-2012 (DSB): +* Initial version. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 3 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +* USA. + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +/* Include configuration results in order to get any definition for the + EXTERNAL_PAL macro. This macro is set if the --with-external_pal + option was set when AST was configured. */ +#if HAVE_CONFIG_H +#include +#endif + +/* If we not using an external ERFA library, rename all ERFA functions so + that references to "iauXxx" below get translated to "astIauXxx". */ +#ifndef EXTERNAL_PAL +#include "erfa2ast.h" +#endif + +/* Include the prototypes defined in the erfa header file. */ +#include "erfa/erfa.h" + +#endif diff --git a/erfa/INFO b/erfa/INFO new file mode 100644 index 0000000..0719bcc --- /dev/null +++ b/erfa/INFO @@ -0,0 +1,19 @@ +ERFA has received explicit permission from the IAU SOFA Board to re-license +and re-copyright ERFA from SOFA. The email providing permission is shown here: + +> The IAU Standards Of Fundamental Astronomy Board approves the +> relicensing of a changed SOFA library by the NumFOCUS Foundation to use +> a "Three Clause BSD" license. The changed, relicensed version shall +> differ from the SOFA version in that all function names shall change to +> use "era" as a prefix in place of "iau", and that the SOFA Board shall +> be removed as a copyright holder in the relicensed version. + +> Catherine +> Chair, IAU SOFA Board +> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +> HM Nautical Almanac Office +> United Kingdom Hydrographic Office +> Admiralty Way +> Taunton TA1 2DN +> Catherine.Hohenkerk@UKHO.gov.uk +> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/erfa/LICENSE b/erfa/LICENSE new file mode 100644 index 0000000..8b3e92c --- /dev/null +++ b/erfa/LICENSE @@ -0,0 +1,53 @@ +Copyright (C) 2013-2014, NumFOCUS Foundation. +All rights reserved. + +This library is derived, with permission, from the International +Astronomical Union's "Standards of Fundamental Astronomy" library, +available from http://www.iausofa.org. + +The ERFA version is intended to retain identical +functionality to the SOFA library, but made distinct through +different function and file names, as set out in the SOFA license +conditions. The SOFA original has a role as a reference standard +for the IAU and IERS, and consequently redistribution is permitted only +in its unaltered state. The ERFA version is not subject to this +restriction and therefore can be included in distributions which do not +support the concept of "read only" software. + +Although the intent is to replicate the SOFA API (other than replacement of +prefix names) and results (with the exception of bugs; any that are +discovered will be fixed), SOFA is not responsible for any errors found +in this version of the library. + +If you wish to acknowledge the SOFA heritage, please acknowledge that +you are using a library derived from SOFA, rather than SOFA itself. + + +TERMS AND CONDITIONS + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1 Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2 Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3 Neither the name of the Standards Of Fundamental Astronomy Board, the + International Astronomical Union nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/erfa/Makefile.am b/erfa/Makefile.am new file mode 100644 index 0000000..b763cb9 --- /dev/null +++ b/erfa/Makefile.am @@ -0,0 +1,46 @@ +lib_LTLIBRARIES = liberfa.la +liberfa_la_SOURCES = a2af.c a2tf.c ab.c af2a.c anp.c anpm.c apcg13.c \ +apcg.c apci13.c apci.c apco13.c apco.c apcs13.c apcs.c aper13.c \ +aper.c apio13.c apio.c atci13.c atciq.c atciqn.c atciqz.c atco13.c \ +atic13.c aticq.c aticqn.c atio13.c atioq.c atoc13.c atoi13.c atoiq.c \ +bi00.c bp00.c bp06.c bpn2xy.c c2i00a.c c2i00b.c c2i06a.c c2ibpn.c \ +c2ixy.c c2ixys.c c2s.c c2t00a.c c2t00b.c c2t06a.c c2tcio.c c2teqx.c \ +c2tpe.c c2txy.c cal2jd.c cp.c cpv.c cr.c d2dtf.c d2tf.c dat.c dtdb.c \ +dtf2d.c ee00a.c ee00b.c ee00.c ee06a.c eect00.c eform.c eo06a.c \ +eors.c epb2jd.c epb.c epj2jd.c epj.c epv00.c eqeq94.c era00.c \ +fad03.c fae03.c faf03.c faju03.c fal03.c falp03.c fama03.c \ +fame03.c fane03.c faom03.c fapa03.c fasa03.c faur03.c fave03.c \ +fk52h.c fk5hip.c fk5hz.c fw2m.c fw2xy.c gc2gd.c gc2gde.c gd2gc.c \ +gd2gce.c gmst00.c gmst06.c gmst82.c gst00a.c gst00b.c gst06a.c \ +gst06.c gst94.c h2fk5.c hfk5z.c ir.c jd2cal.c jdcalf.c ld.c \ +ldn.c ldsun.c num00a.c num00b.c num06a.c numat.c nut00a.c nut00b.c \ +nut06a.c nut80.c nutm80.c obl06.c obl80.c p06e.c p2pv.c p2s.c pap.c \ +pas.c pb06.c pdp.c pfw06.c plan94.c pmat00.c pmat06.c pmat76.c \ +pm.c pmp.c pmpx.c pmsafe.c pn00a.c pn00b.c pn00.c pn06a.c \ +pn06.c pn.c pnm00a.c pnm00b.c pnm06a.c pnm80.c pom00.c ppp.c \ +ppsp.c pr00.c prec76.c pv2p.c pv2s.c pvdpv.c pvm.c pvmpv.c pvppv.c \ +pvstar.c pvtob.c pvu.c pvup.c pvxpv.c pxp.c refco.c rm2v.c rv2m.c \ +rx.c rxp.c rxpv.c rxr.c ry.c rz.c s00a.c s00b.c s00.c \ +s06a.c s06.c s2c.c s2p.c s2pv.c s2xpv.c sepp.c seps.c sp00.c \ +starpm.c starpv.c sxp.c sxpv.c taitt.c taiut1.c taiutc.c tcbtdb.c \ +tcgtt.c tdbtcb.c tdbtt.c tf2a.c tf2d.c tr.c trxp.c trxpv.c tttai.c \ +tttcg.c tttdb.c ttut1.c ut1tai.c ut1tt.c ut1utc.c utctai.c utcut1.c \ +xy06.c xys00a.c xys00b.c xys06a.c zp.c zpv.c zr.c + +include_HEADERS = erfa.h erfam.h + +## Version info is in current : revision : age form +## A library supports interfaces from current downto current - age +## Revision is the version of the current interface + +## VI_ALL is set in the macro ERFA_LIB_VERSION_INFO in configure.ac + +liberfa_la_LDFLAGS = -version-info $(VI_ALL) + + +## Check program +TESTS = t_erfa_c +check_PROGRAMS = t_erfa_c +t_erfa_c_SOURCES = t_erfa_c.c +AM_CPPFLAGS = -I$(top_srcdir) +LDADD = $(top_builddir)/src/liberfa.la diff --git a/erfa/README.rst b/erfa/README.rst new file mode 100644 index 0000000..bab79b8 --- /dev/null +++ b/erfa/README.rst @@ -0,0 +1,77 @@ +This is the source code repository for ERFA (Essential Routines for +Fundamental Astronomy). ERFA is a C library containing key algorithms for +astronomy, and is based on the `SOFA library `_ published by the International +Astronomical Union (IAU). + +ERFA is intended to replicate the functionality of SOFA (aside from possible +bugfixes in ERFA that have not yet been included in SOFA), but is licensed +under a three-clause BSD license to enable its compatibility with a wide +range of open source licenses. Permission for this release has been +obtained from the SOFA board, and is avilable in the ``LICENSE`` file included +in this source distribution. + +Differences from SOFA +--------------------- + +This version of ERFA (v1.1.0) is based on SOFA version "20131202_b", with the +differences outlined below. + +ERFA branding +^^^^^^^^^^^^^ + +All references to "SOFA" in the source code have been changed to ERFA, and +functions have the prefix ``era`` instead of ``iau``. + +C macro prefixes +^^^^^^^^^^^^^^^^ + +All C macros used in ERFA are the same as their SOFA equivalents, but with an +``ERFA_`` prefix to prevent namespace collisions. + +Building and installing ERFA +---------------------------- + +To build and install a released version of ERFA in your OS's standard +location, simply do:: + + ./configure + make + make install + +If you want to run the tests to make sure ERFA built correctly, before +installing do:: + + make check + + +For developers +^^^^^^^^^^^^^^ + +If you are using a developer version from github, you will need to first do +``./bootstrap.sh`` before the above commands. This requires ``autoconf`` and +``libtool``. + +If you wish to build against the ERFA static library without installing, you +will find it in ``$ERFAROOT/src/.libs/liberfa.a`` after running ``make``. + +Creating a single-file version of the source code +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Alternatively, if you wish to bundle the ERFA source code with a separate +package, you can use the ``source_flattener.py`` script from the +`erfa-fetch repository`_ to combine +the ERFA source code into just two files: a ``erfa.c`` source file, and an +``erfa.h`` include file. You should run this script like this:: + + cd /path/to/erfa-source-code + python /path/to/erfa-fetch/source_flattener.py src -n erfa + +If possible, however, it is recommended that you provide an option to use any +copy of the ERFA library that is already installed on the system. + +Travis build status +------------------- +.. image:: https://travis-ci.org/liberfa/erfa.png + :target: https://travis-ci.org/liberfa/erfa + +.. _erfa-fetch repository: https://github.com/liberfa/erfa-fetch diff --git a/erfa/RELEASE.rst b/erfa/RELEASE.rst new file mode 100644 index 0000000..c17fd7b --- /dev/null +++ b/erfa/RELEASE.rst @@ -0,0 +1,177 @@ +Instructions for releasing ERFA +=============================== + +* Clone the ERFA repository from github (if you haven't already done so), + and change to the ERFA directory. + +* Make sure you are on the "master" branch from the "liberfa" github + repository and have the latest version (if you have a fresh clone, this + should already be the case). + +* If a new version of SOFA exists, run `sofa_deriver.py` from the `erfa-fetch + repository`_ in its own directory. That will create a directory called `erfa` + inside the `erfa-fetch` directory, and you should copy its contents to the + `src` directory of `erfa`. Use ``git diff`` in `erfa` to inspect the changes, + and then commit and push them to github. + +* Update the version number in the `AC_INIT` macro of `configure.ac` to + the version number you are about to release, and also update the version + mentioned in `README.rst`. Follow the instructions in + `Version numbering` below. + +* Update the version info of the shared library in the `ERFA_LIB_VERSION_INFO` + macro of `configure.ac`. Follow the instructions in `Version numbering` below. + +* Commit these changes using ``git commit``, with a commit message like + ``Preparing release v0.0.1``. + +* Run `./bootstrap.sh`: you need `automake`, `autoconf` and `libtool` + installed. If no errors appear, this will create a new `./configure` + file. + +* Run ``./configure``, which should create a `Makefile` in the top level + directory and in ./src + +* Run ``make check``, which will build the library and run the tests - + make sure they pass before proceeding. + +* Run ``make distcheck``: this creates the distribution tarball, + unpackages it and runs the check inside the untarred directory. + The resulting tarball will be named e.g., `erfa-0.0.1.tar.gz` and + will be placed in the working directory. + +* Tag the current commit with the version number. A signed tag is preferred if you have an a signing key (e.g., do ``git tag -s v0.0.1``). + +* Push up your changes and the new tag to github: + ``git push --tags origin master``. (The command here assumes the git remote + "origin" points to the "liberfa/erfa" repository. If not, substitute the + appropriate name.) + +* Go to the "liberfa/erfa" repository for the github page, and click on the + "releases" button, and then the release corresponding to the tag you just + made. + +* Click on the "Draft release notes or downloads" button (or it might be "Edit release"). Put the version number as + the title (e.g., ``v0.0.1``)and for the description put + ``See `README.rst` for release notes.`` + +* Upload the tarball you created (e.g., `erfa-0.0.1.tar.gz`) by dropping it + in the area that says "Attach binaries for this release by dropping them + here." + +* Click the "Publish release" button. + +* Update the release listing on Github Pages to include this release: + Do ``git checkout gh-pages``, add a new ``
  • ...
  • `` entry for the + release in `index.html`, do ``git commit``, and then + ``git push origin gh-pages``. + +Version numbering +================= + +ERFA needs to provide two different version numbers. You need to update both. +The first is the +**package version number** or **version number** proper. ERFA uses +`semantic versioning `_ to create this number. +For more on this choice, see +`liberfa/erfa#6 `_. + +The second number is `shared library version info`. When a program has been +linked with the ERFA shared library, the dynamic linker checks the version +info of the library requested by the program with those of the libraries +present if the system. This version info is important to binary distributions +(such as Linux distributions). ERFA uses `libtool versioning `_. + + +Package version number +---------------------- + +Semantic versioning dictates how to change the version number according to +changes to the API of the library. In the case of ERFA the API is: + + * The public C macros defined in erfam.h + * The names, return types, number of arguments and types of the functions in erfa.h + +To update the package version, the release manager has to check the relevant +information about the release, such as: + + * upstream SOFA documentation in http://www.iausofa.org/current_changes.html + * relevant bug reports in the github project page + +If the version is given in the form MAJOR.MINOR.PATCH, then + + * if there is a backwards incompatible change (function removed, types of + arguments altered, macros renamed...) then increase MAJOR by one and set + the others to zero. + * else if there is backwards compatible change (new function added or + new macro added) then do not change MAJOR, increase MINOR by one and + set PATCH to zero. + * else + you are either fixing a bug or making other improvements. Increase + patch by one and do not change the others. + +Change the version number in the `AC_INIT` macro and in `README.rst` + +Shared library version info +--------------------------- + +For the shared library version info, we are only interested in a subset of +the API, the **interfaces of the shared library**. As the C macros are +interpolated away at compile time, the interfaces in the ERFA +shared library are: + + * The names, return types, number of arguments and types of the functions + +Again, the release manager has to review the relevant information: + + * upstream SOFA documentation in http://www.iausofa.org/current_changes.html + * relevant bug reports in the github project page + +The shared library version info is stored in three numbers called *current*, *revision* and *age*. These numbers appear in the macro `ERFA_LIB_VERSION_INFO` in +the mentioned order. + +If the version is given in the form CURRENT,REVISION,AGE then + + * if there is a backwards incompatible change (function removed, types of + arguments altered...) then increase CURRENT by one and set + the others to zero (c,r,a -> c+1,0,0). + * else if there is backwards compatible change (new function added) + then increase both CURRENT and AGE by one, set REVISON to zero + (c,r,a -> c+1,0,a+1). + * else if the library code has been modified at all + then increase REVISION by one (c,r,a -> c,r+1,a) + * else + do not change the version info (c,r,a -> c,r,a) + +Change the verion info in `ERFA_LIB_VERSION_INFO` + +Examples +--------- +We start with ERFA version 1.0.0 and library version info 0,0,0 + +* SOFA makes a new release. A function is added and two functions change their + arguments. This is a backawars incompatible change, so the new package will + have version 2.0.0 and the shared library version info will be 1,0,0 + +* We forgot to add README.rst to the release. We make a new one. The change + is a bugfix (no API changes), the new release will be 2.0.1. The shared + library version is not modified (no changes in the library source code). + +* SOFA makes a new release. They just add a new function. The new package + version will be 2.1.0. The shared library info will be 2,0,1 (both current + and age are incremented). + +* SOFA makes a new relase fixing some bugs in the code without changing the + API. New package version is 2.1.1. The shared library version is 2,1,1 + +* A contributor finds a bug in ERFA. The fix doesn't change the API. New + package version is 2.1.2. The shared library version is 2,2,1 + +* SOFA makes a new release incorporating the bug fix and adding new functions. + The new package version is 2.2.0. The shared library version is 3,0,2 + +* SOFA makes a new release removing functions. This is a backawars + incompatible change, so the new package will + have version 3.0.0 and the shared library version info will be 4,0,0 + +.. _erfa-fetch repository: https://github.com/liberfa/erfa-fetch diff --git a/erfa/a2af.c b/erfa/a2af.c new file mode 100644 index 0000000..3c4b1c7 --- /dev/null +++ b/erfa/a2af.c @@ -0,0 +1,129 @@ +#include "erfa.h" + +void eraA2af(int ndp, double angle, char *sign, int idmsf[4]) +/* +** - - - - - - - - +** e r a A 2 a f +** - - - - - - - - +** +** Decompose radians into degrees, arcminutes, arcseconds, fraction. +** +** Given: +** ndp int resolution (Note 1) +** angle double angle in radians +** +** Returned: +** sign char '+' or '-' +** idmsf int[4] degrees, arcminutes, arcseconds, fraction +** +** Called: +** eraD2tf decompose days to hms +** +** Notes: +** +** 1) The argument ndp is interpreted as follows: +** +** ndp resolution +** : ...0000 00 00 +** -7 1000 00 00 +** -6 100 00 00 +** -5 10 00 00 +** -4 1 00 00 +** -3 0 10 00 +** -2 0 01 00 +** -1 0 00 10 +** 0 0 00 01 +** 1 0 00 00.1 +** 2 0 00 00.01 +** 3 0 00 00.001 +** : 0 00 00.000... +** +** 2) The largest positive useful value for ndp is determined by the +** size of angle, the format of doubles on the target platform, and +** the risk of overflowing idmsf[3]. On a typical platform, for +** angle up to 2pi, the available floating-point precision might +** correspond to ndp=12. However, the practical limit is typically +** ndp=9, set by the capacity of a 32-bit int, or ndp=4 if int is +** only 16 bits. +** +** 3) The absolute value of angle may exceed 2pi. In cases where it +** does not, it is up to the caller to test for and handle the +** case where angle is very nearly 2pi and rounds up to 360 degrees, +** by testing for idmsf[0]=360 and setting idmsf[0-3] to zero. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Hours to degrees * radians to turns */ + const double F = 15.0 / ERFA_D2PI; + + +/* Scale then use days to h,m,s function. */ + eraD2tf(ndp, angle*F, sign, idmsf); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/a2tf.c b/erfa/a2tf.c new file mode 100644 index 0000000..e91370c --- /dev/null +++ b/erfa/a2tf.c @@ -0,0 +1,125 @@ +#include "erfa.h" + +void eraA2tf(int ndp, double angle, char *sign, int ihmsf[4]) +/* +** - - - - - - - - +** e r a A 2 t f +** - - - - - - - - +** +** Decompose radians into hours, minutes, seconds, fraction. +** +** Given: +** ndp int resolution (Note 1) +** angle double angle in radians +** +** Returned: +** sign char '+' or '-' +** ihmsf int[4] hours, minutes, seconds, fraction +** +** Called: +** eraD2tf decompose days to hms +** +** Notes: +** +** 1) The argument ndp is interpreted as follows: +** +** ndp resolution +** : ...0000 00 00 +** -7 1000 00 00 +** -6 100 00 00 +** -5 10 00 00 +** -4 1 00 00 +** -3 0 10 00 +** -2 0 01 00 +** -1 0 00 10 +** 0 0 00 01 +** 1 0 00 00.1 +** 2 0 00 00.01 +** 3 0 00 00.001 +** : 0 00 00.000... +** +** 2) The largest positive useful value for ndp is determined by the +** size of angle, the format of doubles on the target platform, and +** the risk of overflowing ihmsf[3]. On a typical platform, for +** angle up to 2pi, the available floating-point precision might +** correspond to ndp=12. However, the practical limit is typically +** ndp=9, set by the capacity of a 32-bit int, or ndp=4 if int is +** only 16 bits. +** +** 3) The absolute value of angle may exceed 2pi. In cases where it +** does not, it is up to the caller to test for and handle the +** case where angle is very nearly 2pi and rounds up to 24 hours, +** by testing for ihmsf[0]=24 and setting ihmsf[0-3] to zero. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Scale then use days to h,m,s function. */ + eraD2tf(ndp, angle/ERFA_D2PI, sign, ihmsf); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ab.c b/erfa/ab.c new file mode 100644 index 0000000..f662103 --- /dev/null +++ b/erfa/ab.c @@ -0,0 +1,137 @@ +#include "erfa.h" + +void eraAb(double pnat[3], double v[3], double s, double bm1, + double ppr[3]) +/* +** - - - - - - +** e r a A b +** - - - - - - +** +** Apply aberration to transform natural direction into proper +** direction. +** +** Given: +** pnat double[3] natural direction to the source (unit vector) +** v double[3] observer barycentric velocity in units of c +** s double distance between the Sun and the observer (au) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** +** Returned: +** ppr double[3] proper direction to source (unit vector) +** +** Notes: +** +** 1) The algorithm is based on Expr. (7.40) in the Explanatory +** Supplement (Urban & Seidelmann 2013), but with the following +** changes: +** +** o Rigorous rather than approximate normalization is applied. +** +** o The gravitational potential term from Expr. (7) in +** Klioner (2003) is added, taking into account only the Sun's +** contribution. This has a maximum effect of about +** 0.4 microarcsecond. +** +** 2) In almost all cases, the maximum accuracy will be limited by the +** supplied velocity. For example, if the ERFA eraEpv00 function is +** used, errors of up to 5 microarcseconds could occur. +** +** References: +** +** Urban, S. & Seidelmann, P. K. (eds), Explanatory Supplement to +** the Astronomical Almanac, 3rd ed., University Science Books +** (2013). +** +** Klioner, Sergei A., "A practical relativistic model for micro- +** arcsecond astrometry in space", Astr. J. 125, 1580-1597 (2003). +** +** Called: +** eraPdp scalar product of two p-vectors +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int i; + double pdv, w1, w2, r2, w, p[3], r; + + + pdv = eraPdp(pnat, v); + w1 = 1.0 + pdv/(1.0 + bm1); + w2 = ERFA_SRS/s; + r2 = 0.0; + for (i = 0; i < 3; i++) { + w = pnat[i]*bm1 + w1*v[i] + w2*(v[i] - pdv*pnat[i]); + p[i] = w; + r2 = r2 + w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + ppr[i] = p[i]/r; + } + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/af2a.c b/erfa/af2a.c new file mode 100644 index 0000000..c75e40f --- /dev/null +++ b/erfa/af2a.c @@ -0,0 +1,116 @@ +#include "erfa.h" +#include + +int eraAf2a(char s, int ideg, int iamin, double asec, double *rad) +/* +** - - - - - - - - +** e r a A f 2 a +** - - - - - - - - +** +** Convert degrees, arcminutes, arcseconds to radians. +** +** Given: +** s char sign: '-' = negative, otherwise positive +** ideg int degrees +** iamin int arcminutes +** asec double arcseconds +** +** Returned: +** rad double angle in radians +** +** Returned (function value): +** int status: 0 = OK +** 1 = ideg outside range 0-359 +** 2 = iamin outside range 0-59 +** 3 = asec outside range 0-59.999... +** +** Notes: +** +** 1) The result is computed even if any of the range checks fail. +** +** 2) Negative ideg, iamin and/or asec produce a warning status, but +** the absolute value is used in the conversion. +** +** 3) If there are multiple errors, the status value reflects only the +** first, the smallest taking precedence. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Compute the interval. */ + *rad = ( s == '-' ? -1.0 : 1.0 ) * + ( 60.0 * ( 60.0 * ( (double) abs(ideg) ) + + ( (double) abs(iamin) ) ) + + fabs(asec) ) * ERFA_DAS2R; + +/* Validate arguments and return status. */ + if ( ideg < 0 || ideg > 359 ) return 1; + if ( iamin < 0 || iamin > 59 ) return 2; + if ( asec < 0.0 || asec >= 60.0 ) return 3; + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/anp.c b/erfa/anp.c new file mode 100644 index 0000000..70d45ef --- /dev/null +++ b/erfa/anp.c @@ -0,0 +1,91 @@ +#include "erfa.h" + +double eraAnp(double a) +/* +** - - - - - - - +** e r a A n p +** - - - - - - - +** +** Normalize angle into the range 0 <= a < 2pi. +** +** Given: +** a double angle (radians) +** +** Returned (function value): +** double angle in range 0-2pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double w; + + + w = fmod(a, ERFA_D2PI); + if (w < 0) w += ERFA_D2PI; + + return w; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/anpm.c b/erfa/anpm.c new file mode 100644 index 0000000..db87a45 --- /dev/null +++ b/erfa/anpm.c @@ -0,0 +1,91 @@ +#include "erfa.h" + +double eraAnpm(double a) +/* +** - - - - - - - - +** e r a A n p m +** - - - - - - - - +** +** Normalize angle into the range -pi <= a < +pi. +** +** Given: +** a double angle (radians) +** +** Returned (function value): +** double angle in range +/-pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double w; + + + w = fmod(a, ERFA_D2PI); + if (fabs(w) >= ERFA_DPI) w -= ERFA_DSIGN(ERFA_D2PI, a); + + return w; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apcg.c b/erfa/apcg.c new file mode 100644 index 0000000..7ee6fa3 --- /dev/null +++ b/erfa/apcg.c @@ -0,0 +1,181 @@ +#include "erfa.h" + +void eraApcg(double date1, double date2, + double ebpv[2][3], double ehp[3], + eraASTROM *astrom) +/* +** - - - - - - - - +** e r a A p c g +** - - - - - - - - +** +** For a geocentric observer, prepare star-independent astrometry +** parameters for transformations between ICRS and GCRS coordinates. +** The Earth ephemeris is supplied by the caller. +** +** The parameters produced by this function are required in the +** parallax, light deflection and aberration parts of the astrometric +** transformation chain. +** +** Given: +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** ebpv double[2][3] Earth barycentric pos/vel (au, au/day) +** ehp double[3] Earth heliocentric position (au) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double unchanged +** refa double unchanged +** refb double unchanged +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) All the vectors are with respect to BCRS axes. +** +** 3) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 4) The context structure astrom produced by this function is used by +** eraAtciq* and eraAticq*. +** +** Called: +** eraApcs astrometry parameters, ICRS-GCRS, space observer +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Geocentric observer */ + double pv[2][3] = { { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }; + + +/* Compute the star-independent astrometry parameters. */ + eraApcs(date1, date2, pv, ebpv, ehp, astrom); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apcg13.c b/erfa/apcg13.c new file mode 100644 index 0000000..baa00be --- /dev/null +++ b/erfa/apcg13.c @@ -0,0 +1,184 @@ +#include "erfa.h" + +void eraApcg13(double date1, double date2, eraASTROM *astrom) +/* +** - - - - - - - - - - +** e r a A p c g 1 3 +** - - - - - - - - - - +** +** For a geocentric observer, prepare star-independent astrometry +** parameters for transformations between ICRS and GCRS coordinates. +** The caller supplies the date, and ERFA models are used to predict +** the Earth ephemeris. +** +** The parameters produced by this function are required in the +** parallax, light deflection and aberration parts of the astrometric +** transformation chain. +** +** Given: +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double unchanged +** refa double unchanged +** refb double unchanged +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) All the vectors are with respect to BCRS axes. +** +** 3) In cases where the caller wishes to supply his own Earth +** ephemeris, the function eraApcg can be used instead of the present +** function. +** +** 4) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 5) The context structure astrom produced by this function is used by +** eraAtciq* and eraAticq*. +** +** Called: +** eraEpv00 Earth position and velocity +** eraApcg astrometry parameters, ICRS-GCRS, geocenter +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double ehpv[2][3], ebpv[2][3]; + + +/* Earth barycentric & heliocentric position/velocity (au, au/d). */ + (void) eraEpv00(date1, date2, ehpv, ebpv); + +/* Compute the star-independent astrometry parameters. */ + eraApcg(date1, date2, ebpv, ehpv[0], astrom); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apci.c b/erfa/apci.c new file mode 100644 index 0000000..ee84865 --- /dev/null +++ b/erfa/apci.c @@ -0,0 +1,190 @@ +#include "erfa.h" + +void eraApci(double date1, double date2, + double ebpv[2][3], double ehp[3], + double x, double y, double s, + eraASTROM *astrom) +/* +** - - - - - - - - +** e r a A p c i +** - - - - - - - - +** +** For a terrestrial observer, prepare star-independent astrometry +** parameters for transformations between ICRS and geocentric CIRS +** coordinates. The Earth ephemeris and CIP/CIO are supplied by the +** caller. +** +** The parameters produced by this function are required in the +** parallax, light deflection, aberration, and bias-precession-nutation +** parts of the astrometric transformation chain. +** +** Given: +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** ebpv double[2][3] Earth barycentric position/velocity (au, au/day) +** ehp double[3] Earth heliocentric position (au) +** x,y double CIP X,Y (components of unit vector) +** s double the CIO locator s (radians) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double unchanged +** refa double unchanged +** refb double unchanged +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) All the vectors are with respect to BCRS axes. +** +** 3) In cases where the caller does not wish to provide the Earth +** ephemeris and CIP/CIO, the function eraApci13 can be used instead +** of the present function. This computes the required quantities +** using other ERFA functions. +** +** 4) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 5) The context structure astrom produced by this function is used by +** eraAtciq* and eraAticq*. +** +** Called: +** eraApcg astrometry parameters, ICRS-GCRS, geocenter +** eraC2ixys celestial-to-intermediate matrix, given X,Y and s +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Star-independent astrometry parameters for geocenter. */ + eraApcg(date1, date2, ebpv, ehp, astrom); + +/* CIO based BPN matrix. */ + eraC2ixys(x, y, s, astrom->bpn); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apci13.c b/erfa/apci13.c new file mode 100644 index 0000000..d1e7e8d --- /dev/null +++ b/erfa/apci13.c @@ -0,0 +1,202 @@ +#include "erfa.h" + +void eraApci13(double date1, double date2, + eraASTROM *astrom, double *eo) +/* +** - - - - - - - - - - +** e r a A p c i 1 3 +** - - - - - - - - - - +** +** For a terrestrial observer, prepare star-independent astrometry +** parameters for transformations between ICRS and geocentric CIRS +** coordinates. The caller supplies the date, and ERFA models are used +** to predict the Earth ephemeris and CIP/CIO. +** +** The parameters produced by this function are required in the +** parallax, light deflection, aberration, and bias-precession-nutation +** parts of the astrometric transformation chain. +** +** Given: +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double unchanged +** refa double unchanged +** refb double unchanged +** eo double* equation of the origins (ERA-GST) +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) All the vectors are with respect to BCRS axes. +** +** 3) In cases where the caller wishes to supply his own Earth +** ephemeris and CIP/CIO, the function eraApci can be used instead +** of the present function. +** +** 4) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 5) The context structure astrom produced by this function is used by +** eraAtciq* and eraAticq*. +** +** Called: +** eraEpv00 Earth position and velocity +** eraPnm06a classical NPB matrix, IAU 2006/2000A +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS06 the CIO locator s, given X,Y, IAU 2006 +** eraApci astrometry parameters, ICRS-CIRS +** eraEors equation of the origins, given NPB matrix and s +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double ehpv[2][3], ebpv[2][3], r[3][3], x, y, s; + + +/* Earth barycentric & heliocentric position/velocity (au, au/d). */ + (void) eraEpv00(date1, date2, ehpv, ebpv); + +/* Form the equinox based BPN matrix, IAU 2006/2000A. */ + eraPnm06a(date1, date2, r); + +/* Extract CIP X,Y. */ + eraBpn2xy(r, &x, &y); + +/* Obtain CIO locator s. */ + s = eraS06(date1, date2, x, y); + +/* Compute the star-independent astrometry parameters. */ + eraApci(date1, date2, ebpv, ehpv[0], x, y, s, astrom); + +/* Equation of the origins. */ + *eo = eraEors(r, s); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apco.c b/erfa/apco.c new file mode 100644 index 0000000..6681412 --- /dev/null +++ b/erfa/apco.c @@ -0,0 +1,264 @@ +#include "erfa.h" + +void eraApco(double date1, double date2, + double ebpv[2][3], double ehp[3], + double x, double y, double s, double theta, + double elong, double phi, double hm, + double xp, double yp, double sp, + double refa, double refb, + eraASTROM *astrom) +/* +** - - - - - - - - +** e r a A p c o +** - - - - - - - - +** +** For a terrestrial observer, prepare star-independent astrometry +** parameters for transformations between ICRS and observed +** coordinates. The caller supplies the Earth ephemeris, the Earth +** rotation information and the refraction constants as well as the +** site coordinates. +** +** Given: +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** ebpv double[2][3] Earth barycentric PV (au, au/day, Note 2) +** ehp double[3] Earth heliocentric P (au, Note 2) +** x,y double CIP X,Y (components of unit vector) +** s double the CIO locator s (radians) +** theta double Earth rotation angle (radians) +** elong double longitude (radians, east +ve, Note 3) +** phi double latitude (geodetic, radians, Note 3) +** hm double height above ellipsoid (m, geodetic, Note 3) +** xp,yp double polar motion coordinates (radians, Note 4) +** sp double the TIO locator s' (radians, Note 4) +** refa double refraction constant A (radians, Note 5) +** refb double refraction constant B (radians, Note 5) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) The vectors eb, eh, and all the astrom vectors, are with respect +** to BCRS axes. +** +** 3) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN +** CONVENTION: the longitude required by the present function is +** right-handed, i.e. east-positive, in accordance with geographical +** convention. +** +** 4) xp and yp are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions), measured along the +** meridians 0 and 90 deg west respectively. sp is the TIO locator +** s', in radians, which positions the Terrestrial Intermediate +** Origin on the equator. For many applications, xp, yp and +** (especially) sp can be set to zero. +** +** Internally, the polar motion is stored in a form rotated onto the +** local meridian. +** +** 5) The refraction constants refa and refb are for use in a +** dZ = A*tan(Z)+B*tan^3(Z) model, where Z is the observed +** (i.e. refracted) zenith distance and dZ is the amount of +** refraction. +** +** 6) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** 7) In cases where the caller does not wish to provide the Earth +** Ephemeris, the Earth rotation information and refraction +** constants, the function eraApco13 can be used instead of the +** present function. This starts from UTC and weather readings etc. +** and computes suitable values using other ERFA functions. +** +** 8) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 9) The context structure astrom produced by this function is used by +** eraAtioq, eraAtoiq, eraAtciq* and eraAticq*. +** +** Called: +** eraAper astrometry parameters: update ERA +** eraC2ixys celestial-to-intermediate matrix, given X,Y and s +** eraPvtob position/velocity of terrestrial station +** eraTrxpv product of transpose of r-matrix and pv-vector +** eraApcs astrometry parameters, ICRS-GCRS, space observer +** eraCr copy r-matrix +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double sl, cl, r[3][3], pvc[2][3], pv[2][3]; + + +/* Longitude with adjustment for TIO locator s'. */ + astrom->along = elong + sp; + +/* Polar motion, rotated onto the local meridian. */ + sl = sin(astrom->along); + cl = cos(astrom->along); + astrom->xpl = xp*cl - yp*sl; + astrom->ypl = xp*sl + yp*cl; + +/* Functions of latitude. */ + astrom->sphi = sin(phi); + astrom->cphi = cos(phi); + +/* Refraction constants. */ + astrom->refa = refa; + astrom->refb = refb; + +/* Local Earth rotation angle. */ + eraAper(theta, astrom); + +/* Disable the (redundant) diurnal aberration step. */ + astrom->diurab = 0.0; + +/* CIO based BPN matrix. */ + eraC2ixys(x, y, s, r); + +/* Observer's geocentric position and velocity (m, m/s, CIRS). */ + eraPvtob(elong, phi, hm, xp, yp, sp, theta, pvc); + +/* Rotate into GCRS. */ + eraTrxpv(r, pvc, pv); + +/* ICRS <-> GCRS parameters. */ + eraApcs(date1, date2, pv, ebpv, ehp, astrom); + +/* Store the CIO based BPN matrix. */ + eraCr(r, astrom->bpn ); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apco13.c b/erfa/apco13.c new file mode 100644 index 0000000..e0374db --- /dev/null +++ b/erfa/apco13.c @@ -0,0 +1,287 @@ +#include "erfa.h" + +int eraApco13(double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tc, double rh, double wl, + eraASTROM *astrom, double *eo) +/* +** - - - - - - - - - - +** e r a A p c o 1 3 +** - - - - - - - - - - +** +** For a terrestrial observer, prepare star-independent astrometry +** parameters for transformations between ICRS and observed +** coordinates. The caller supplies UTC, site coordinates, ambient air +** conditions and observing wavelength, and ERFA models are used to +** obtain the Earth ephemeris, CIP/CIO and refraction constants. +** +** The parameters produced by this function are required in the +** parallax, light deflection, aberration, and bias-precession-nutation +** parts of the ICRS/CIRS transformations. +** +** Given: +** utc1 double UTC as a 2-part... +** utc2 double ...quasi Julian Date (Notes 1,2) +** dut1 double UT1-UTC (seconds, Note 3) +** elong double longitude (radians, east +ve, Note 4) +** phi double latitude (geodetic, radians, Note 4) +** hm double height above ellipsoid (m, geodetic, Notes 4,6) +** xp,yp double polar motion coordinates (radians, Note 5) +** phpa double pressure at the observer (hPa = mB, Note 6) +** tc double ambient temperature at the observer (deg C) +** rh double relative humidity at the observer (range 0-1) +** wl double wavelength (micrometers, Note 7) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** eo double* equation of the origins (ERA-GST) +** +** Returned (function value): +** int status: +1 = dubious year (Note 2) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** However, JD cannot unambiguously represent UTC during a leap +** second unless special measures are taken. The convention in the +** present function is that the JD day represents UTC days whether +** the length is 86399, 86400 or 86401 SI seconds. +** +** Applications should use the function eraDtf2d to convert from +** calendar date and time of day into 2-part quasi Julian Date, as +** it implements the leap-second-ambiguity convention just +** described. +** +** 2) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the +** future to be trusted. See eraDat for further details. +** +** 3) UT1-UTC is tabulated in IERS bulletins. It increases by exactly +** one second at the end of each positive UTC leap second, +** introduced in order to keep UT1-UTC within +/- 0.9s. n.b. This +** practice is under review, and in the future UT1-UTC may grow +** essentially without limit. +** +** 4) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN: the +** longitude required by the present function is east-positive +** (i.e. right-handed), in accordance with geographical convention. +** +** 5) The polar motion xp,yp can be obtained from IERS bulletins. The +** values are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions 2003), measured along the +** meridians 0 and 90 deg west respectively. For many +** applications, xp and yp can be set to zero. +** +** Internally, the polar motion is stored in a form rotated onto +** the local meridian. +** +** 6) If hm, the height above the ellipsoid of the observing station +** in meters, is not known but phpa, the pressure in hPa (=mB), is +** available, an adequate estimate of hm can be obtained from the +** expression +** +** hm = -29.3 * tsl * log ( phpa / 1013.25 ); +** +** where tsl is the approximate sea-level air temperature in K +** (See Astrophysical Quantities, C.W.Allen, 3rd edition, section +** 52). Similarly, if the pressure phpa is not known, it can be +** estimated from the height of the observing station, hm, as +** follows: +** +** phpa = 1013.25 * exp ( -hm / ( 29.3 * tsl ) ); +** +** Note, however, that the refraction is nearly proportional to +** the pressure and that an accurate phpa value is important for +** precise work. +** +** 7) The argument wl specifies the observing wavelength in +** micrometers. The transition from optical to radio is assumed to +** occur at 100 micrometers (about 3000 GHz). +** +** 8) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** 9) In cases where the caller wishes to supply his own Earth +** ephemeris, Earth rotation information and refraction constants, +** the function eraApco can be used instead of the present function. +** +** 10) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 11) The context structure astrom produced by this function is used +** by eraAtioq, eraAtoiq, eraAtciq* and eraAticq*. +** +** Called: +** eraUtctai UTC to TAI +** eraTaitt TAI to TT +** eraUtcut1 UTC to UT1 +** eraEpv00 Earth position and velocity +** eraPnm06a classical NPB matrix, IAU 2006/2000A +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS06 the CIO locator s, given X,Y, IAU 2006 +** eraEra00 Earth rotation angle, IAU 2000 +** eraSp00 the TIO locator s', IERS 2000 +** eraRefco refraction constants for given ambient conditions +** eraApco astrometry parameters, ICRS-observed +** eraEors equation of the origins, given NPB matrix and s +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + double tai1, tai2, tt1, tt2, ut11, ut12, ehpv[2][3], ebpv[2][3], + r[3][3], x, y, s, theta, sp, refa, refb; + + +/* UTC to other time scales. */ + j = eraUtctai(utc1, utc2, &tai1, &tai2); + if ( j < 0 ) return -1; + j = eraTaitt(tai1, tai2, &tt1, &tt2); + j = eraUtcut1(utc1, utc2, dut1, &ut11, &ut12); + if ( j < 0 ) return -1; + +/* Earth barycentric & heliocentric position/velocity (au, au/d). */ + (void) eraEpv00(tt1, tt2, ehpv, ebpv); + +/* Form the equinox based BPN matrix, IAU 2006/2000A. */ + eraPnm06a(tt1, tt2, r); + +/* Extract CIP X,Y. */ + eraBpn2xy(r, &x, &y); + +/* Obtain CIO locator s. */ + s = eraS06(tt1, tt2, x, y); + +/* Earth rotation angle. */ + theta = eraEra00(ut11, ut12); + +/* TIO locator s'. */ + sp = eraSp00(tt1, tt2); + +/* Refraction constants A and B. */ + eraRefco(phpa, tc, rh, wl, &refa, &refb); + +/* Compute the star-independent astrometry parameters. */ + eraApco(tt1, tt2, ebpv, ehpv[0], x, y, s, theta, + elong, phi, hm, xp, yp, sp, refa, refb, astrom); + +/* Equation of the origins. */ + *eo = eraEors(r, s); + +/* Return any warning status. */ + return j; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apcs.c b/erfa/apcs.c new file mode 100644 index 0000000..b872ca6 --- /dev/null +++ b/erfa/apcs.c @@ -0,0 +1,233 @@ +#include "erfa.h" + +void eraApcs(double date1, double date2, double pv[2][3], + double ebpv[2][3], double ehp[3], + eraASTROM *astrom) +/* +** - - - - - - - - +** e r a A p c s +** - - - - - - - - +** +** For an observer whose geocentric position and velocity are known, +** prepare star-independent astrometry parameters for transformations +** between ICRS and GCRS. The Earth ephemeris is supplied by the +** caller. +** +** The parameters produced by this function are required in the space +** motion, parallax, light deflection and aberration parts of the +** astrometric transformation chain. +** +** Given: +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** pv double[2][3] observer's geocentric pos/vel (m, m/s) +** ebpv double[2][3] Earth barycentric PV (au, au/day) +** ehp double[3] Earth heliocentric P (au) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double unchanged +** refa double unchanged +** refb double unchanged +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) All the vectors are with respect to BCRS axes. +** +** 3) Providing separate arguments for (i) the observer's geocentric +** position and velocity and (ii) the Earth ephemeris is done for +** convenience in the geocentric, terrestrial and Earth orbit cases. +** For deep space applications it maybe more convenient to specify +** zero geocentric position and velocity and to supply the +** observer's position and velocity information directly instead of +** with respect to the Earth. However, note the different units: +** m and m/s for the geocentric vectors, au and au/day for the +** heliocentric and barycentric vectors. +** +** 4) In cases where the caller does not wish to provide the Earth +** ephemeris, the function eraApcs13 can be used instead of the +** present function. This computes the Earth ephemeris using the +** ERFA function eraEpv00. +** +** 5) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 6) The context structure astrom produced by this function is used by +** eraAtciq* and eraAticq*. +** +** Called: +** eraCp copy p-vector +** eraPm modulus of p-vector +** eraPn decompose p-vector into modulus and direction +** eraIr initialize r-matrix to identity +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* au/d to m/s */ + const double AUDMS = ERFA_DAU/ERFA_DAYSEC; + +/* Light time for 1 AU (day) */ + const double CR = ERFA_AULT/ERFA_DAYSEC; + + int i; + double dp, dv, pb[3], vb[3], ph[3], v2, w; + + +/* Time since reference epoch, years (for proper motion calculation). */ + astrom->pmt = ( (date1 - ERFA_DJ00) + date2 ) / ERFA_DJY; + +/* Adjust Earth ephemeris to observer. */ + for (i = 0; i < 3; i++) { + dp = pv[0][i] / ERFA_DAU; + dv = pv[1][i] / AUDMS; + pb[i] = ebpv[0][i] + dp; + vb[i] = ebpv[1][i] + dv; + ph[i] = ehp[i] + dp; + } + +/* Barycentric position of observer (au). */ + eraCp(pb, astrom->eb); + +/* Heliocentric direction and distance (unit vector and au). */ + eraPn(ph, &astrom->em, astrom->eh); + +/* Barycentric vel. in units of c, and reciprocal of Lorenz factor. */ + v2 = 0.0; + for (i = 0; i < 3; i++) { + w = vb[i] * CR; + astrom->v[i] = w; + v2 += w*w; + } + astrom->bm1 = sqrt(1.0 - v2); + +/* Reset the NPB matrix. */ + eraIr(astrom->bpn); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apcs13.c b/erfa/apcs13.c new file mode 100644 index 0000000..79e56ec --- /dev/null +++ b/erfa/apcs13.c @@ -0,0 +1,191 @@ +#include "erfa.h" + +void eraApcs13(double date1, double date2, double pv[2][3], + eraASTROM *astrom) +/* +** - - - - - - - - - - +** e r a A p c s 1 3 +** - - - - - - - - - - +** +** For an observer whose geocentric position and velocity are known, +** prepare star-independent astrometry parameters for transformations +** between ICRS and GCRS. The Earth ephemeris is from ERFA models. +** +** The parameters produced by this function are required in the space +** motion, parallax, light deflection and aberration parts of the +** astrometric transformation chain. +** +** Given: +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** pv double[2][3] observer's geocentric pos/vel (Note 3) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double unchanged +** refa double unchanged +** refb double unchanged +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) All the vectors are with respect to BCRS axes. +** +** 3) The observer's position and velocity pv are geocentric but with +** respect to BCRS axes, and in units of m and m/s. No assumptions +** are made about proximity to the Earth, and the function can be +** used for deep space applications as well as Earth orbit and +** terrestrial. +** +** 4) In cases where the caller wishes to supply his own Earth +** ephemeris, the function eraApcs can be used instead of the present +** function. +** +** 5) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 6) The context structure astrom produced by this function is used by +** eraAtciq* and eraAticq*. +** +** Called: +** eraEpv00 Earth position and velocity +** eraApcs astrometry parameters, ICRS-GCRS, space observer +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double ehpv[2][3], ebpv[2][3]; + + +/* Earth barycentric & heliocentric position/velocity (au, au/d). */ + (void) eraEpv00(date1, date2, ehpv, ebpv); + +/* Compute the star-independent astrometry parameters. */ + eraApcs(date1, date2, pv, ebpv, ehpv[0], astrom); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/aper.c b/erfa/aper.c new file mode 100644 index 0000000..4bd92b4 --- /dev/null +++ b/erfa/aper.c @@ -0,0 +1,162 @@ +#include "erfa.h" + +void eraAper(double theta, eraASTROM *astrom) +/* +** - - - - - - - - +** e r a A p e r +** - - - - - - - - +** +** In the star-independent astrometry parameters, update only the +** Earth rotation angle, supplied by the caller explicitly. +** +** Given: +** theta double Earth rotation angle (radians, Note 2) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double not used +** eb double[3] not used +** eh double[3] not used +** em double not used +** v double[3] not used +** bm1 double not used +** bpn double[3][3] not used +** along double longitude + s' (radians) +** xpl double not used +** ypl double not used +** sphi double not used +** cphi double not used +** diurab double not used +** eral double not used +** refa double not used +** refb double not used +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double unchanged +** eb double[3] unchanged +** eh double[3] unchanged +** em double unchanged +** v double[3] unchanged +** bm1 double unchanged +** bpn double[3][3] unchanged +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double "local" Earth rotation angle (radians) +** refa double unchanged +** refb double unchanged +** +** Notes: +** +** 1) This function exists to enable sidereal-tracking applications to +** avoid wasteful recomputation of the bulk of the astrometry +** parameters: only the Earth rotation is updated. +** +** 2) For targets expressed as equinox based positions, such as +** classical geocentric apparent (RA,Dec), the supplied theta can be +** Greenwich apparent sidereal time rather than Earth rotation +** angle. +** +** 3) The function eraAper13 can be used instead of the present +** function, and starts from UT1 rather than ERA itself. +** +** 4) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + astrom->eral = theta + astrom->along; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/aper13.c b/erfa/aper13.c new file mode 100644 index 0000000..29a2e68 --- /dev/null +++ b/erfa/aper13.c @@ -0,0 +1,181 @@ +#include "erfa.h" + +void eraAper13(double ut11, double ut12, eraASTROM *astrom) +/* +** - - - - - - - - - - +** e r a A p e r 1 3 +** - - - - - - - - - - +** +** In the star-independent astrometry parameters, update only the +** Earth rotation angle. The caller provides UT1, (n.b. not UTC). +** +** Given: +** ut11 double UT1 as a 2-part... +** ut12 double ...Julian Date (Note 1) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double not used +** eb double[3] not used +** eh double[3] not used +** em double not used +** v double[3] not used +** bm1 double not used +** bpn double[3][3] not used +** along double longitude + s' (radians) +** xpl double not used +** ypl double not used +** sphi double not used +** cphi double not used +** diurab double not used +** eral double not used +** refa double not used +** refb double not used +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double unchanged +** eb double[3] unchanged +** eh double[3] unchanged +** em double unchanged +** v double[3] unchanged +** bm1 double unchanged +** bpn double[3][3] unchanged +** along double unchanged +** xpl double unchanged +** ypl double unchanged +** sphi double unchanged +** cphi double unchanged +** diurab double unchanged +** eral double "local" Earth rotation angle (radians) +** refa double unchanged +** refb double unchanged +** +** Notes: +** +** 1) The UT1 date (n.b. not UTC) ut11+ut12 is a Julian Date, +** apportioned in any convenient way between the arguments ut11 and +** ut12. For example, JD(UT1)=2450123.7 could be expressed in any +** of these ways, among others: +** +** ut11 ut12 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. The date & time method is +** best matched to the algorithm used: maximum precision is +** delivered when the ut11 argument is for 0hrs UT1 on the day in +** question and the ut12 argument lies in the range 0 to 1, or vice +** versa. +** +** 2) If the caller wishes to provide the Earth rotation angle itself, +** the function eraAper can be used instead. One use of this +** technique is to substitute Greenwich apparent sidereal time and +** thereby to support equinox based transformations directly. +** +** 3) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** Called: +** eraAper astrometry parameters: update ERA +** eraEra00 Earth rotation angle, IAU 2000 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraAper(eraEra00(ut11,ut12), astrom); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apio.c b/erfa/apio.c new file mode 100644 index 0000000..a42222f --- /dev/null +++ b/erfa/apio.c @@ -0,0 +1,213 @@ +#include "erfa.h" + +void eraApio(double sp, double theta, + double elong, double phi, double hm, double xp, double yp, + double refa, double refb, + eraASTROM *astrom) +/* +** - - - - - - - - +** e r a A p i o +** - - - - - - - - +** +** For a terrestrial observer, prepare star-independent astrometry +** parameters for transformations between CIRS and observed +** coordinates. The caller supplies the Earth orientation information +** and the refraction constants as well as the site coordinates. +** +** Given: +** sp double the TIO locator s' (radians, Note 1) +** theta double Earth rotation angle (radians) +** elong double longitude (radians, east +ve, Note 2) +** phi double geodetic latitude (radians, Note 2) +** hm double height above ellipsoid (m, geodetic Note 2) +** xp,yp double polar motion coordinates (radians, Note 3) +** refa double refraction constant A (radians, Note 4) +** refb double refraction constant B (radians, Note 4) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double unchanged +** eb double[3] unchanged +** eh double[3] unchanged +** em double unchanged +** v double[3] unchanged +** bm1 double unchanged +** bpn double[3][3] unchanged +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Notes: +** +** 1) sp, the TIO locator s', is a tiny quantity needed only by the +** most precise applications. It can either be set to zero or +** predicted using the ERFA function eraSp00. +** +** 2) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN: the +** longitude required by the present function is east-positive +** (i.e. right-handed), in accordance with geographical convention. +** +** 3) The polar motion xp,yp can be obtained from IERS bulletins. The +** values are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions 2003), measured along the +** meridians 0 and 90 deg west respectively. For many applications, +** xp and yp can be set to zero. +** +** Internally, the polar motion is stored in a form rotated onto the +** local meridian. +** +** 4) The refraction constants refa and refb are for use in a +** dZ = A*tan(Z)+B*tan^3(Z) model, where Z is the observed +** (i.e. refracted) zenith distance and dZ is the amount of +** refraction. +** +** 5) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** 6) In cases where the caller does not wish to provide the Earth +** rotation information and refraction constants, the function +** eraApio13 can be used instead of the present function. This +** starts from UTC and weather readings etc. and computes suitable +** values using other ERFA functions. +** +** 7) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 8) The context structure astrom produced by this function is used by +** eraAtioq and eraAtoiq. +** +** Called: +** eraPvtob position/velocity of terrestrial station +** eraAper astrometry parameters: update ERA +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double sl, cl, pv[2][3]; + + +/* Longitude with adjustment for TIO locator s'. */ + astrom->along = elong + sp; + +/* Polar motion, rotated onto the local meridian. */ + sl = sin(astrom->along); + cl = cos(astrom->along); + astrom->xpl = xp*cl - yp*sl; + astrom->ypl = xp*sl + yp*cl; + +/* Functions of latitude. */ + astrom->sphi = sin(phi); + astrom->cphi = cos(phi); + +/* Observer's geocentric position and velocity (m, m/s, CIRS). */ + eraPvtob(elong, phi, hm, xp, yp, sp, theta, pv); + +/* Magnitude of diurnal aberration vector. */ + astrom->diurab = sqrt(pv[1][0]*pv[1][0]+pv[1][1]*pv[1][1]) / ERFA_CMPS; + +/* Refraction constants. */ + astrom->refa = refa; + astrom->refb = refb; + +/* Local Earth rotation angle. */ + eraAper(theta, astrom); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/apio13.c b/erfa/apio13.c new file mode 100644 index 0000000..b5e2248 --- /dev/null +++ b/erfa/apio13.c @@ -0,0 +1,259 @@ +#include "erfa.h" + +int eraApio13(double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tc, double rh, double wl, + eraASTROM *astrom) +/* +** - - - - - - - - - - +** e r a A p i o 1 3 +** - - - - - - - - - - +** +** For a terrestrial observer, prepare star-independent astrometry +** parameters for transformations between CIRS and observed +** coordinates. The caller supplies UTC, site coordinates, ambient air +** conditions and observing wavelength. +** +** Given: +** utc1 double UTC as a 2-part... +** utc2 double ...quasi Julian Date (Notes 1,2) +** dut1 double UT1-UTC (seconds) +** elong double longitude (radians, east +ve, Note 3) +** phi double geodetic latitude (radians, Note 3) +** hm double height above ellipsoid (m, geodetic Notes 4,6) +** xp,yp double polar motion coordinates (radians, Note 5) +** phpa double pressure at the observer (hPa = mB, Note 6) +** tc double ambient temperature at the observer (deg C) +** rh double relative humidity at the observer (range 0-1) +** wl double wavelength (micrometers, Note 7) +** +** Returned: +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double unchanged +** eb double[3] unchanged +** eh double[3] unchanged +** em double unchanged +** v double[3] unchanged +** bm1 double unchanged +** bpn double[3][3] unchanged +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Returned (function value): +** int status: +1 = dubious year (Note 2) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** However, JD cannot unambiguously represent UTC during a leap +** second unless special measures are taken. The convention in the +** present function is that the JD day represents UTC days whether +** the length is 86399, 86400 or 86401 SI seconds. +** +** Applications should use the function eraDtf2d to convert from +** calendar date and time of day into 2-part quasi Julian Date, as +** it implements the leap-second-ambiguity convention just +** described. +** +** 2) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the future +** to be trusted. See eraDat for further details. +** +** 3) UT1-UTC is tabulated in IERS bulletins. It increases by exactly +** one second at the end of each positive UTC leap second, +** introduced in order to keep UT1-UTC within +/- 0.9s. n.b. This +** practice is under review, and in the future UT1-UTC may grow +** essentially without limit. +** +** 4) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN: the +** longitude required by the present function is east-positive +** (i.e. right-handed), in accordance with geographical convention. +** +** 5) The polar motion xp,yp can be obtained from IERS bulletins. The +** values are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions 2003), measured along the +** meridians 0 and 90 deg west respectively. For many applications, +** xp and yp can be set to zero. +** +** Internally, the polar motion is stored in a form rotated onto +** the local meridian. +** +** 6) If hm, the height above the ellipsoid of the observing station +** in meters, is not known but phpa, the pressure in hPa (=mB), is +** available, an adequate estimate of hm can be obtained from the +** expression +** +** hm = -29.3 * tsl * log ( phpa / 1013.25 ); +** +** where tsl is the approximate sea-level air temperature in K +** (See Astrophysical Quantities, C.W.Allen, 3rd edition, section +** 52). Similarly, if the pressure phpa is not known, it can be +** estimated from the height of the observing station, hm, as +** follows: +** +** phpa = 1013.25 * exp ( -hm / ( 29.3 * tsl ) ); +** +** Note, however, that the refraction is nearly proportional to the +** pressure and that an accurate phpa value is important for +** precise work. +** +** 7) The argument wl specifies the observing wavelength in +** micrometers. The transition from optical to radio is assumed to +** occur at 100 micrometers (about 3000 GHz). +** +** 8) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** 9) In cases where the caller wishes to supply his own Earth +** rotation information and refraction constants, the function +** eraApc can be used instead of the present function. +** +** 10) This is one of several functions that inserts into the astrom +** structure star-independent parameters needed for the chain of +** astrometric transformations ICRS <-> GCRS <-> CIRS <-> observed. +** +** The various functions support different classes of observer and +** portions of the transformation chain: +** +** functions observer transformation +** +** eraApcg eraApcg13 geocentric ICRS <-> GCRS +** eraApci eraApci13 terrestrial ICRS <-> CIRS +** eraApco eraApco13 terrestrial ICRS <-> observed +** eraApcs eraApcs13 space ICRS <-> GCRS +** eraAper eraAper13 terrestrial update Earth rotation +** eraApio eraApio13 terrestrial CIRS <-> observed +** +** Those with names ending in "13" use contemporary ERFA models to +** compute the various ephemerides. The others accept ephemerides +** supplied by the caller. +** +** The transformation from ICRS to GCRS covers space motion, +** parallax, light deflection, and aberration. From GCRS to CIRS +** comprises frame bias and precession-nutation. From CIRS to +** observed takes account of Earth rotation, polar motion, diurnal +** aberration and parallax (unless subsumed into the ICRS <-> GCRS +** transformation), and atmospheric refraction. +** +** 11) The context structure astrom produced by this function is used +** by eraAtioq and eraAtoiq. +** +** Called: +** eraUtctai UTC to TAI +** eraTaitt TAI to TT +** eraUtcut1 UTC to UT1 +** eraSp00 the TIO locator s', IERS 2000 +** eraEra00 Earth rotation angle, IAU 2000 +** eraRefco refraction constants for given ambient conditions +** eraApio astrometry parameters, CIRS-observed +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + double tai1, tai2, tt1, tt2, ut11, ut12, sp, theta, refa, refb; + + +/* UTC to other time scales. */ + j = eraUtctai(utc1, utc2, &tai1, &tai2); + if ( j < 0 ) return -1; + j = eraTaitt(tai1, tai2, &tt1, &tt2); + j = eraUtcut1(utc1, utc2, dut1, &ut11, &ut12); + if ( j < 0 ) return -1; + +/* TIO locator s'. */ + sp = eraSp00(tt1, tt2); + +/* Earth rotation angle. */ + theta = eraEra00(ut11, ut12); + +/* Refraction constants A and B. */ + eraRefco(phpa, tc, rh, wl, &refa, &refb); + +/* CIRS <-> observed astrometry parameters. */ + eraApio(sp, theta, elong, phi, hm, xp, yp, refa, refb, astrom); + +/* Return any warning status. */ + return j; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atci13.c b/erfa/atci13.c new file mode 100644 index 0000000..c85c2be --- /dev/null +++ b/erfa/atci13.c @@ -0,0 +1,159 @@ +#include "erfa.h" + +void eraAtci13(double rc, double dc, + double pr, double pd, double px, double rv, + double date1, double date2, + double *ri, double *di, double *eo) +/* +** - - - - - - - - - - +** e r a A t c i 1 3 +** - - - - - - - - - - +** +** Transform ICRS star data, epoch J2000.0, to CIRS. +** +** Given: +** rc double ICRS right ascension at J2000.0 (radians, Note 1) +** dc double ICRS declination at J2000.0 (radians, Note 1) +** pr double RA proper motion (radians/year; Note 2) +** pd double Dec proper motion (radians/year) +** px double parallax (arcsec) +** rv double radial velocity (km/s, +ve if receding) +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 3) +** +** Returned: +** ri,di double* CIRS geocentric RA,Dec (radians) +** eo double* equation of the origins (ERA-GST, Note 5) +** +** Notes: +** +** 1) Star data for an epoch other than J2000.0 (for example from the +** Hipparcos catalog, which has an epoch of J1991.25) will require a +** preliminary call to eraPmsafe before use. +** +** 2) The proper motion in RA is dRA/dt rather than cos(Dec)*dRA/dt. +** +** 3) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.8g could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.8g 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 4) The available accuracy is better than 1 milliarcsecond, limited +** mainly by the precession-nutation model that is used, namely +** IAU 2000A/2006. Very close to solar system bodies, additional +** errors of up to several milliarcseconds can occur because of +** unmodeled light deflection; however, the Sun's contribution is +** taken into account, to first order. The accuracy limitations of +** the ERFA function eraEpv00 (used to compute Earth position and +** velocity) can contribute aberration errors of up to +** 5 microarcseconds. Light deflection at the Sun's limb is +** uncertain at the 0.4 mas level. +** +** 5) Should the transformation to (equinox based) apparent place be +** required rather than (CIO based) intermediate place, subtract the +** equation of the origins from the returned right ascension: +** RA = RI - EO. (The eraAnp function can then be applied, as +** required, to keep the result in the conventional 0-2pi range.) +** +** Called: +** eraApci13 astrometry parameters, ICRS-CIRS, 2013 +** eraAtciq quick ICRS to CIRS +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Star-independent astrometry parameters */ + eraASTROM astrom; + + +/* The transformation parameters. */ + eraApci13(date1, date2, &astrom, eo); + +/* ICRS (epoch J2000.0) to CIRS. */ + eraAtciq(rc, dc, pr, pd, px, rv, &astrom, ri, di); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atciq.c b/erfa/atciq.c new file mode 100644 index 0000000..095a6b0 --- /dev/null +++ b/erfa/atciq.c @@ -0,0 +1,154 @@ +#include "erfa.h" + +void eraAtciq(double rc, double dc, + double pr, double pd, double px, double rv, + eraASTROM *astrom, double *ri, double *di) +/* +** - - - - - - - - - +** e r a A t c i q +** - - - - - - - - - +** +** Quick ICRS, epoch J2000.0, to CIRS transformation, given precomputed +** star-independent astrometry parameters. +** +** Use of this function is appropriate when efficiency is important and +** where many star positions are to be transformed for one date. The +** star-independent parameters can be obtained by calling one of the +** functions eraApci[13], eraApcg[13], eraApco[13] or eraApcs[13]. +** +** If the parallax and proper motions are zero the eraAtciqz function +** can be used instead. +** +** Given: +** rc,dc double ICRS RA,Dec at J2000.0 (radians) +** pr double RA proper motion (radians/year; Note 3) +** pd double Dec proper motion (radians/year) +** px double parallax (arcsec) +** rv double radial velocity (km/s, +ve if receding) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Returned: +** ri,di double CIRS RA,Dec (radians) +** +** Notes: +** +** 1) All the vectors are with respect to BCRS axes. +** +** 2) Star data for an epoch other than J2000.0 (for example from the +** Hipparcos catalog, which has an epoch of J1991.25) will require a +** preliminary call to eraPmsafe before use. +** +** 3) The proper motion in RA is dRA/dt rather than cos(Dec)*dRA/dt. +** +** Called: +** eraPmpx proper motion and parallax +** eraLdsun light deflection by the Sun +** eraAb stellar aberration +** eraRxp product of r-matrix and pv-vector +** eraC2s p-vector to spherical +** eraAnp normalize angle into range 0 to 2pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double pco[3], pnat[3], ppr[3], pi[3], w; + + +/* Proper motion and parallax, giving BCRS coordinate direction. */ + eraPmpx(rc, dc, pr, pd, px, rv, astrom->pmt, astrom->eb, pco); + +/* Light deflection by the Sun, giving BCRS natural direction. */ + eraLdsun(pco, astrom->eh, astrom->em, pnat); + +/* Aberration, giving GCRS proper direction. */ + eraAb(pnat, astrom->v, astrom->em, astrom->bm1, ppr); + +/* Bias-precession-nutation, giving CIRS proper direction. */ + eraRxp(astrom->bpn, ppr, pi); + +/* CIRS RA,Dec. */ + eraC2s(pi, &w, di); + *ri = eraAnp(w); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atciqn.c b/erfa/atciqn.c new file mode 100644 index 0000000..9a381f8 --- /dev/null +++ b/erfa/atciqn.c @@ -0,0 +1,191 @@ +#include "erfa.h" + +void eraAtciqn(double rc, double dc, double pr, double pd, + double px, double rv, eraASTROM *astrom, + int n, eraLDBODY b[], double *ri, double *di) +/* +** - - - - - - - - - - +** e r a A t c i q n +** - - - - - - - - - - +** +** Quick ICRS, epoch J2000.0, to CIRS transformation, given precomputed +** star-independent astrometry parameters plus a list of light- +** deflecting bodies. +** +** Use of this function is appropriate when efficiency is important and +** where many star positions are to be transformed for one date. The +** star-independent parameters can be obtained by calling one of the +** functions eraApci[13], eraApcg[13], eraApco[13] or eraApcs[13]. +** +** +** If the only light-deflecting body to be taken into account is the +** Sun, the eraAtciq function can be used instead. If in addition the +** parallax and proper motions are zero, the eraAtciqz function can be +** used. +** +** Given: +** rc,dc double ICRS RA,Dec at J2000.0 (radians) +** pr double RA proper motion (radians/year; Note 3) +** pd double Dec proper motion (radians/year) +** px double parallax (arcsec) +** rv double radial velocity (km/s, +ve if receding) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** n int number of bodies (Note 3) +** b eraLDBODY[n] data for each of the n bodies (Notes 3,4): +** bm double mass of the body (solar masses, Note 5) +** dl double deflection limiter (Note 6) +** pv [2][3] barycentric PV of the body (au, au/day) +** +** Returned: +** ri,di double CIRS RA,Dec (radians) +** +** Notes: +** +** 1) Star data for an epoch other than J2000.0 (for example from the +** Hipparcos catalog, which has an epoch of J1991.25) will require a +** preliminary call to eraPmsafe before use. +** +** 2) The proper motion in RA is dRA/dt rather than cos(Dec)*dRA/dt. +** +** 3) The struct b contains n entries, one for each body to be +** considered. If n = 0, no gravitational light deflection will be +** applied, not even for the Sun. +** +** 4) The struct b should include an entry for the Sun as well as for +** any planet or other body to be taken into account. The entries +** should be in the order in which the light passes the body. +** +** 5) In the entry in the b struct for body i, the mass parameter +** b[i].bm can, as required, be adjusted in order to allow for such +** effects as quadrupole field. +** +** 6) The deflection limiter parameter b[i].dl is phi^2/2, where phi is +** the angular separation (in radians) between star and body at +** which limiting is applied. As phi shrinks below the chosen +** threshold, the deflection is artificially reduced, reaching zero +** for phi = 0. Example values suitable for a terrestrial +** observer, together with masses, are as follows: +** +** body i b[i].bm b[i].dl +** +** Sun 1.0 6e-6 +** Jupiter 0.00095435 3e-9 +** Saturn 0.00028574 3e-10 +** +** 7) For efficiency, validation of the contents of the b array is +** omitted. The supplied masses must be greater than zero, the +** position and velocity vectors must be right, and the deflection +** limiter greater than zero. +** +** Called: +** eraPmpx proper motion and parallax +** eraLdn light deflection by n bodies +** eraAb stellar aberration +** eraRxp product of r-matrix and pv-vector +** eraC2s p-vector to spherical +** eraAnp normalize angle into range 0 to 2pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double pco[3], pnat[3], ppr[3], pi[3], w; + + +/* Proper motion and parallax, giving BCRS coordinate direction. */ + eraPmpx(rc, dc, pr, pd, px, rv, astrom->pmt, astrom->eb, pco); + +/* Light deflection, giving BCRS natural direction. */ + eraLdn(n, b, astrom->eb, pco, pnat); + +/* Aberration, giving GCRS proper direction. */ + eraAb(pnat, astrom->v, astrom->em, astrom->bm1, ppr); + +/* Bias-precession-nutation, giving CIRS proper direction. */ + eraRxp(astrom->bpn, ppr, pi); + +/* CIRS RA,Dec. */ + eraC2s(pi, &w, di); + *ri = eraAnp(w); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atciqz.c b/erfa/atciqz.c new file mode 100644 index 0000000..983db53 --- /dev/null +++ b/erfa/atciqz.c @@ -0,0 +1,153 @@ +#include "erfa.h" + +void eraAtciqz(double rc, double dc, eraASTROM *astrom, + double *ri, double *di) +/* +** - - - - - - - - - - +** e r a A t c i q z +** - - - - - - - - - - +** +** Quick ICRS to CIRS transformation, given precomputed star- +** independent astrometry parameters, and assuming zero parallax and +** proper motion. +** +** Use of this function is appropriate when efficiency is important and +** where many star positions are to be transformed for one date. The +** star-independent parameters can be obtained by calling one of the +** functions eraApci[13], eraApcg[13], eraApco[13] or eraApcs[13]. +** +** The corresponding function for the case of non-zero parallax and +** proper motion is eraAtciq. +** +** Given: +** rc,dc double ICRS astrometric RA,Dec (radians) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Returned: +** ri,di double CIRS RA,Dec (radians) +** +** Note: +** +** All the vectors are with respect to BCRS axes. +** +** References: +** +** Urban, S. & Seidelmann, P. K. (eds), Explanatory Supplement to +** the Astronomical Almanac, 3rd ed., University Science Books +** (2013). +** +** Klioner, Sergei A., "A practical relativistic model for micro- +** arcsecond astrometry in space", Astr. J. 125, 1580-1597 (2003). +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraLdsun light deflection due to Sun +** eraAb stellar aberration +** eraRxp product of r-matrix and p-vector +** eraC2s p-vector to spherical +** eraAnp normalize angle into range +/- pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double pco[3], pnat[3], ppr[3], pi[3], w; + + +/* BCRS coordinate direction (unit vector). */ + eraS2c(rc, dc, pco); + +/* Light deflection by the Sun, giving BCRS natural direction. */ + eraLdsun(pco, astrom->eh, astrom->em, pnat); + +/* Aberration, giving GCRS proper direction. */ + eraAb(pnat, astrom->v, astrom->em, astrom->bm1, ppr); + +/* Bias-precession-nutation, giving CIRS proper direction. */ + eraRxp(astrom->bpn, ppr, pi); + +/* CIRS RA,Dec. */ + eraC2s(pi, &w, di); + *ri = eraAnp(w); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atco13.c b/erfa/atco13.c new file mode 100644 index 0000000..c27fe3b --- /dev/null +++ b/erfa/atco13.c @@ -0,0 +1,243 @@ +#include "erfa.h" + +int eraAtco13(double rc, double dc, + double pr, double pd, double px, double rv, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tc, double rh, double wl, + double *aob, double *zob, double *hob, + double *dob, double *rob, double *eo) +/* +** - - - - - - - - - - +** e r a A t c o 1 3 +** - - - - - - - - - - +** +** ICRS RA,Dec to observed place. The caller supplies UTC, site +** coordinates, ambient air conditions and observing wavelength. +** +** ERFA models are used for the Earth ephemeris, bias-precession- +** nutation, Earth orientation and refraction. +** +** Given: +** rc,dc double ICRS right ascension at J2000.0 (radians, Note 1) +** pr double RA proper motion (radians/year; Note 2) +** pd double Dec proper motion (radians/year) +** px double parallax (arcsec) +** rv double radial velocity (km/s, +ve if receding) +** utc1 double UTC as a 2-part... +** utc2 double ...quasi Julian Date (Notes 3-4) +** dut1 double UT1-UTC (seconds, Note 5) +** elong double longitude (radians, east +ve, Note 6) +** phi double latitude (geodetic, radians, Note 6) +** hm double height above ellipsoid (m, geodetic, Notes 6,8) +** xp,yp double polar motion coordinates (radians, Note 7) +** phpa double pressure at the observer (hPa = mB, Note 8) +** tc double ambient temperature at the observer (deg C) +** rh double relative humidity at the observer (range 0-1) +** wl double wavelength (micrometers, Note 9) +** +** Returned: +** aob double* observed azimuth (radians: N=0,E=90) +** zob double* observed zenith distance (radians) +** hob double* observed hour angle (radians) +** dob double* observed declination (radians) +** rob double* observed right ascension (CIO-based, radians) +** eo double* equation of the origins (ERA-GST) +** +** Returned (function value): +** int status: +1 = dubious year (Note 4) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) Star data for an epoch other than J2000.0 (for example from the +** Hipparcos catalog, which has an epoch of J1991.25) will require +** a preliminary call to eraPmsafe before use. +** +** 2) The proper motion in RA is dRA/dt rather than cos(Dec)*dRA/dt. +** +** 3) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** However, JD cannot unambiguously represent UTC during a leap +** second unless special measures are taken. The convention in the +** present function is that the JD day represents UTC days whether +** the length is 86399, 86400 or 86401 SI seconds. +** +** Applications should use the function eraDtf2d to convert from +** calendar date and time of day into 2-part quasi Julian Date, as +** it implements the leap-second-ambiguity convention just +** described. +** +** 4) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the +** future to be trusted. See eraDat for further details. +** +** 5) UT1-UTC is tabulated in IERS bulletins. It increases by exactly +** one second at the end of each positive UTC leap second, +** introduced in order to keep UT1-UTC within +/- 0.9s. n.b. This +** practice is under review, and in the future UT1-UTC may grow +** essentially without limit. +** +** 6) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN: the +** longitude required by the present function is east-positive +** (i.e. right-handed), in accordance with geographical convention. +** +** 7) The polar motion xp,yp can be obtained from IERS bulletins. The +** values are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions 2003), measured along the +** meridians 0 and 90 deg west respectively. For many +** applications, xp and yp can be set to zero. +** +** 8) If hm, the height above the ellipsoid of the observing station +** in meters, is not known but phpa, the pressure in hPa (=mB), +** is available, an adequate estimate of hm can be obtained from +** the expression +** +** hm = -29.3 * tsl * log ( phpa / 1013.25 ); +** +** where tsl is the approximate sea-level air temperature in K +** (See Astrophysical Quantities, C.W.Allen, 3rd edition, section +** 52). Similarly, if the pressure phpa is not known, it can be +** estimated from the height of the observing station, hm, as +** follows: +** +** phpa = 1013.25 * exp ( -hm / ( 29.3 * tsl ) ); +** +** Note, however, that the refraction is nearly proportional to +** the pressure and that an accurate phpa value is important for +** precise work. +** +** 9) The argument wl specifies the observing wavelength in +** micrometers. The transition from optical to radio is assumed to +** occur at 100 micrometers (about 3000 GHz). +** +** 10) The accuracy of the result is limited by the corrections for +** refraction, which use a simple A*tan(z) + B*tan^3(z) model. +** Providing the meteorological parameters are known accurately and +** there are no gross local effects, the predicted observed +** coordinates should be within 0.05 arcsec (optical) or 1 arcsec +** (radio) for a zenith distance of less than 70 degrees, better +** than 30 arcsec (optical or radio) at 85 degrees and better +** than 20 arcmin (optical) or 30 arcmin (radio) at the horizon. +** +** Without refraction, the complementary functions eraAtco13 and +** eraAtoc13 are self-consistent to better than 1 microarcsecond +** all over the celestial sphere. With refraction included, +** consistency falls off at high zenith distances, but is still +** better than 0.05 arcsec at 85 degrees. +** +** 11) "Observed" Az,ZD means the position that would be seen by a +** perfect geodetically aligned theodolite. (Zenith distance is +** used rather than altitude in order to reflect the fact that no +** allowance is made for depression of the horizon.) This is +** related to the observed HA,Dec via the standard rotation, using +** the geodetic latitude (corrected for polar motion), while the +** observed HA and RA are related simply through the Earth rotation +** angle and the site longitude. "Observed" RA,Dec or HA,Dec thus +** means the position that would be seen by a perfect equatorial +** with its polar axis aligned to the Earth's axis of rotation. +** +** 12) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** Called: +** eraApco13 astrometry parameters, ICRS-observed, 2013 +** eraAtciq quick ICRS to CIRS +** eraAtioq quick ICRS to observed +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + eraASTROM astrom; + double ri, di; + + +/* Star-independent astrometry parameters. */ + j = eraApco13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom, eo); + +/* Abort if bad UTC. */ + if ( j < 0 ) return j; + +/* Transform ICRS to CIRS. */ + eraAtciq(rc, dc, pr, pd, px, rv, &astrom, &ri, &di); + +/* Transform CIRS to observed. */ + eraAtioq(ri, di, &astrom, aob, zob, hob, dob, rob); + +/* Return OK/warning status. */ + return j; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atic13.c b/erfa/atic13.c new file mode 100644 index 0000000..35dcd13 --- /dev/null +++ b/erfa/atic13.c @@ -0,0 +1,152 @@ +#include "erfa.h" + +void eraAtic13(double ri, double di, double date1, double date2, + double *rc, double *dc, double *eo) +/* +** - - - - - - - - - - +** e r a A t i c 1 3 +** - - - - - - - - - - +** +** Transform star RA,Dec from geocentric CIRS to ICRS astrometric. +** +** Given: +** ri,di double CIRS geocentric RA,Dec (radians) +** date1 double TDB as a 2-part... +** date2 double ...Julian Date (Note 1) +** +** Returned: +** rc,dc double ICRS astrometric RA,Dec (radians) +** eo double equation of the origins (ERA-GST, Note 4) +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. For most +** applications of this function the choice will not be at all +** critical. +** +** TT can be used instead of TDB without any significant impact on +** accuracy. +** +** 2) Iterative techniques are used for the aberration and light +** deflection corrections so that the functions eraAtic13 (or +** eraAticq) and eraAtci13 (or eraAtciq) are accurate inverses; +** even at the edge of the Sun's disk the discrepancy is only about +** 1 nanoarcsecond. +** +** 3) The available accuracy is better than 1 milliarcsecond, limited +** mainly by the precession-nutation model that is used, namely +** IAU 2000A/2006. Very close to solar system bodies, additional +** errors of up to several milliarcseconds can occur because of +** unmodeled light deflection; however, the Sun's contribution is +** taken into account, to first order. The accuracy limitations of +** the ERFA function eraEpv00 (used to compute Earth position and +** velocity) can contribute aberration errors of up to +** 5 microarcseconds. Light deflection at the Sun's limb is +** uncertain at the 0.4 mas level. +** +** 4) Should the transformation to (equinox based) J2000.0 mean place +** be required rather than (CIO based) ICRS coordinates, subtract the +** equation of the origins from the returned right ascension: +** RA = RI - EO. (The eraAnp function can then be applied, as +** required, to keep the result in the conventional 0-2pi range.) +** +** Called: +** eraApci13 astrometry parameters, ICRS-CIRS, 2013 +** eraAticq quick CIRS to ICRS astrometric +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Star-independent astrometry parameters */ + eraASTROM astrom; + + +/* Star-independent astrometry parameters. */ + eraApci13(date1, date2, &astrom, eo); + +/* CIRS to ICRS astrometric. */ + eraAticq(ri, di, &astrom, rc, dc); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/aticq.c b/erfa/aticq.c new file mode 100644 index 0000000..2401836 --- /dev/null +++ b/erfa/aticq.c @@ -0,0 +1,199 @@ +#include "erfa.h" + +void eraAticq(double ri, double di, eraASTROM *astrom, + double *rc, double *dc) +/* +** - - - - - - - - - +** e r a A t i c q +** - - - - - - - - - +** +** Quick CIRS RA,Dec to ICRS astrometric place, given the star- +** independent astrometry parameters. +** +** Use of this function is appropriate when efficiency is important and +** where many star positions are all to be transformed for one date. +** The star-independent astrometry parameters can be obtained by +** calling one of the functions eraApci[13], eraApcg[13], eraApco[13] +** or eraApcs[13]. +** +** Given: +** ri,di double CIRS RA,Dec (radians) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Returned: +** rc,dc double ICRS astrometric RA,Dec (radians) +** +** Notes: +** +** 1) Only the Sun is taken into account in the light deflection +** correction. +** +** 2) Iterative techniques are used for the aberration and light +** deflection corrections so that the functions eraAtic13 (or +** eraAticq) and eraAtci13 (or eraAtciq) are accurate inverses; +** even at the edge of the Sun's disk the discrepancy is only about +** 1 nanoarcsecond. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraTrxp product of transpose of r-matrix and p-vector +** eraZp zero p-vector +** eraAb stellar aberration +** eraLdsun light deflection by the Sun +** eraC2s p-vector to spherical +** eraAnp normalize angle into range +/- pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j, i; + double pi[3], ppr[3], pnat[3], pco[3], w, d[3], before[3], r2, r, + after[3]; + + +/* CIRS RA,Dec to Cartesian. */ + eraS2c(ri, di, pi); + +/* Bias-precession-nutation, giving GCRS proper direction. */ + eraTrxp(astrom->bpn, pi, ppr); + +/* Aberration, giving GCRS natural direction. */ + eraZp(d); + for (j = 0; j < 2; j++) { + r2 = 0.0; + for (i = 0; i < 3; i++) { + w = ppr[i] - d[i]; + before[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + before[i] /= r; + } + eraAb(before, astrom->v, astrom->em, astrom->bm1, after); + r2 = 0.0; + for (i = 0; i < 3; i++) { + d[i] = after[i] - before[i]; + w = ppr[i] - d[i]; + pnat[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + pnat[i] /= r; + } + } + +/* Light deflection by the Sun, giving BCRS coordinate direction. */ + eraZp(d); + for (j = 0; j < 5; j++) { + r2 = 0.0; + for (i = 0; i < 3; i++) { + w = pnat[i] - d[i]; + before[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + before[i] /= r; + } + eraLdsun(before, astrom->eh, astrom->em, after); + r2 = 0.0; + for (i = 0; i < 3; i++) { + d[i] = after[i] - before[i]; + w = pnat[i] - d[i]; + pco[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + pco[i] /= r; + } + } + +/* ICRS astrometric RA,Dec. */ + eraC2s(pco, &w, dc); + *rc = eraAnp(w); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/aticqn.c b/erfa/aticqn.c new file mode 100644 index 0000000..3c633a8 --- /dev/null +++ b/erfa/aticqn.c @@ -0,0 +1,237 @@ +#include "erfa.h" + +void eraAticqn(double ri, double di, eraASTROM *astrom, + int n, eraLDBODY b[], double *rc, double *dc) +/* +** - - - - - - - - - +** e r a A t i c q n +** - - - - - - - - - +** +** Quick CIRS to ICRS astrometric place transformation, given the star- +** independent astrometry parameters plus a list of light-deflecting +** bodies. +** +** Use of this function is appropriate when efficiency is important and +** where many star positions are all to be transformed for one date. +** The star-independent astrometry parameters can be obtained by +** calling one of the functions eraApci[13], eraApcg[13], eraApco[13] +** or eraApcs[13]. +* +* If the only light-deflecting body to be taken into account is the +* Sun, the eraAticq function can be used instead. +** +** Given: +** ri,di double CIRS RA,Dec (radians) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** n int number of bodies (Note 3) +** b eraLDBODY[n] data for each of the n bodies (Notes 3,4): +** bm double mass of the body (solar masses, Note 5) +** dl double deflection limiter (Note 6) +** pv [2][3] barycentric PV of the body (au, au/day) +** +** Returned: +** rc,dc double ICRS astrometric RA,Dec (radians) +** +** Notes: +** +** 1) Iterative techniques are used for the aberration and light +** deflection corrections so that the functions eraAticqn and +** eraAtciqn are accurate inverses; even at the edge of the Sun's +** disk the discrepancy is only about 1 nanoarcsecond. +** +** 2) If the only light-deflecting body to be taken into account is the +** Sun, the eraAticq function can be used instead. +** +** 3) The struct b contains n entries, one for each body to be +** considered. If n = 0, no gravitational light deflection will be +** applied, not even for the Sun. +** +** 4) The struct b should include an entry for the Sun as well as for +** any planet or other body to be taken into account. The entries +** should be in the order in which the light passes the body. +** +** 5) In the entry in the b struct for body i, the mass parameter +** b[i].bm can, as required, be adjusted in order to allow for such +** effects as quadrupole field. +** +** 6) The deflection limiter parameter b[i].dl is phi^2/2, where phi is +** the angular separation (in radians) between star and body at +** which limiting is applied. As phi shrinks below the chosen +** threshold, the deflection is artificially reduced, reaching zero +** for phi = 0. Example values suitable for a terrestrial +** observer, together with masses, are as follows: +** +** body i b[i].bm b[i].dl +** +** Sun 1.0 6e-6 +** Jupiter 0.00095435 3e-9 +** Saturn 0.00028574 3e-10 +** +** 7) For efficiency, validation of the contents of the b array is +** omitted. The supplied masses must be greater than zero, the +** position and velocity vectors must be right, and the deflection +** limiter greater than zero. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraTrxp product of transpose of r-matrix and p-vector +** eraZp zero p-vector +** eraAb stellar aberration +** eraLdn light deflection by n bodies +** eraC2s p-vector to spherical +** eraAnp normalize angle into range +/- pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j, i; + double pi[3], ppr[3], pnat[3], pco[3], w, d[3], before[3], r2, r, + after[3]; + + +/* CIRS RA,Dec to Cartesian. */ + eraS2c(ri, di, pi); + +/* Bias-precession-nutation, giving GCRS proper direction. */ + eraTrxp(astrom->bpn, pi, ppr); + +/* Aberration, giving GCRS natural direction. */ + eraZp(d); + for (j = 0; j < 2; j++) { + r2 = 0.0; + for (i = 0; i < 3; i++) { + w = ppr[i] - d[i]; + before[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + before[i] /= r; + } + eraAb(before, astrom->v, astrom->em, astrom->bm1, after); + r2 = 0.0; + for (i = 0; i < 3; i++) { + d[i] = after[i] - before[i]; + w = ppr[i] - d[i]; + pnat[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + pnat[i] /= r; + } + } + +/* Light deflection, giving BCRS coordinate direction. */ + eraZp(d); + for (j = 0; j < 5; j++) { + r2 = 0.0; + for (i = 0; i < 3; i++) { + w = pnat[i] - d[i]; + before[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + before[i] /= r; + } + eraLdn(n, b, astrom->eb, before, after); + r2 = 0.0; + for (i = 0; i < 3; i++) { + d[i] = after[i] - before[i]; + w = pnat[i] - d[i]; + pco[i] = w; + r2 += w*w; + } + r = sqrt(r2); + for (i = 0; i < 3; i++) { + pco[i] /= r; + } + } + +/* ICRS astrometric RA,Dec. */ + eraC2s(pco, &w, dc); + *rc = eraAnp(w); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atio13.c b/erfa/atio13.c new file mode 100644 index 0000000..36a6452 --- /dev/null +++ b/erfa/atio13.c @@ -0,0 +1,222 @@ +#include "erfa.h" + +int eraAtio13(double ri, double di, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tc, double rh, double wl, + double *aob, double *zob, double *hob, + double *dob, double *rob) +/* +** - - - - - - - - - - +** e r a A t i o 1 3 +** - - - - - - - - - - +** +** CIRS RA,Dec to observed place. The caller supplies UTC, site +** coordinates, ambient air conditions and observing wavelength. +** +** Given: +** ri double CIRS right ascension (CIO-based, radians) +** di double CIRS declination (radians) +** utc1 double UTC as a 2-part... +** utc2 double ...quasi Julian Date (Notes 1,2) +** dut1 double UT1-UTC (seconds, Note 3) +** elong double longitude (radians, east +ve, Note 4) +** phi double geodetic latitude (radians, Note 4) +** hm double height above ellipsoid (m, geodetic Notes 4,6) +** xp,yp double polar motion coordinates (radians, Note 5) +** phpa double pressure at the observer (hPa = mB, Note 6) +** tc double ambient temperature at the observer (deg C) +** rh double relative humidity at the observer (range 0-1) +** wl double wavelength (micrometers, Note 7) +** +** Returned: +** aob double* observed azimuth (radians: N=0,E=90) +** zob double* observed zenith distance (radians) +** hob double* observed hour angle (radians) +** dob double* observed declination (radians) +** rob double* observed right ascension (CIO-based, radians) +** +** Returned (function value): +** int status: +1 = dubious year (Note 2) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** However, JD cannot unambiguously represent UTC during a leap +** second unless special measures are taken. The convention in the +** present function is that the JD day represents UTC days whether +** the length is 86399, 86400 or 86401 SI seconds. +** +** Applications should use the function eraDtf2d to convert from +** calendar date and time of day into 2-part quasi Julian Date, as +** it implements the leap-second-ambiguity convention just +** described. +** +** 2) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the +** future to be trusted. See eraDat for further details. +** +** 3) UT1-UTC is tabulated in IERS bulletins. It increases by exactly +** one second at the end of each positive UTC leap second, +** introduced in order to keep UT1-UTC within +/- 0.9s. n.b. This +** practice is under review, and in the future UT1-UTC may grow +** essentially without limit. +** +** 4) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN: the +** longitude required by the present function is east-positive +** (i.e. right-handed), in accordance with geographical convention. +** +** 5) The polar motion xp,yp can be obtained from IERS bulletins. The +** values are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions 2003), measured along the +** meridians 0 and 90 deg west respectively. For many +** applications, xp and yp can be set to zero. +** +** 6) If hm, the height above the ellipsoid of the observing station +** in meters, is not known but phpa, the pressure in hPa (=mB), is +** available, an adequate estimate of hm can be obtained from the +** expression +** +** hm = -29.3 * tsl * log ( phpa / 1013.25 ); +** +** where tsl is the approximate sea-level air temperature in K +** (See Astrophysical Quantities, C.W.Allen, 3rd edition, section +** 52). Similarly, if the pressure phpa is not known, it can be +** estimated from the height of the observing station, hm, as +** follows: +** +** phpa = 1013.25 * exp ( -hm / ( 29.3 * tsl ) ); +** +** Note, however, that the refraction is nearly proportional to +** the pressure and that an accurate phpa value is important for +** precise work. +** +** 7) The argument wl specifies the observing wavelength in +** micrometers. The transition from optical to radio is assumed to +** occur at 100 micrometers (about 3000 GHz). +** +** 8) "Observed" Az,ZD means the position that would be seen by a +** perfect geodetically aligned theodolite. (Zenith distance is +** used rather than altitude in order to reflect the fact that no +** allowance is made for depression of the horizon.) This is +** related to the observed HA,Dec via the standard rotation, using +** the geodetic latitude (corrected for polar motion), while the +** observed HA and RA are related simply through the Earth rotation +** angle and the site longitude. "Observed" RA,Dec or HA,Dec thus +** means the position that would be seen by a perfect equatorial +** with its polar axis aligned to the Earth's axis of rotation. +** +** 9) The accuracy of the result is limited by the corrections for +** refraction, which use a simple A*tan(z) + B*tan^3(z) model. +** Providing the meteorological parameters are known accurately and +** there are no gross local effects, the predicted astrometric +** coordinates should be within 0.05 arcsec (optical) or 1 arcsec +** (radio) for a zenith distance of less than 70 degrees, better +** than 30 arcsec (optical or radio) at 85 degrees and better +** than 20 arcmin (optical) or 30 arcmin (radio) at the horizon. +** +** 10) The complementary functions eraAtio13 and eraAtoi13 are self- +** consistent to better than 1 microarcsecond all over the +** celestial sphere. +** +** 11) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** Called: +** eraApio13 astrometry parameters, CIRS-observed, 2013 +** eraAtioq quick ICRS to observed +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + eraASTROM astrom; + + +/* Star-independent astrometry parameters for CIRS->observed. */ + j = eraApio13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom); + +/* Abort if bad UTC. */ + if ( j < 0 ) return j; + +/* Transform CIRS to observed. */ + eraAtioq(ri, di, &astrom, aob, zob, hob, dob, rob); + +/* Return OK/warning status. */ + return j; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atioq.c b/erfa/atioq.c new file mode 100644 index 0000000..be3dfbc --- /dev/null +++ b/erfa/atioq.c @@ -0,0 +1,244 @@ +#include "erfa.h" + +void eraAtioq(double ri, double di, eraASTROM *astrom, + double *aob, double *zob, + double *hob, double *dob, double *rob) +/* +** - - - - - - - - - +** e r a A t i o q +** - - - - - - - - - +** +** Quick CIRS to observed place transformation. +** +** Use of this function is appropriate when efficiency is important and +** where many star positions are all to be transformed for one date. +** The star-independent astrometry parameters can be obtained by +** calling eraApio[13] or eraApco[13]. +** +** Given: +** ri double CIRS right ascension +** di double CIRS declination +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Returned: +** aob double* observed azimuth (radians: N=0,E=90) +** zob double* observed zenith distance (radians) +** hob double* observed hour angle (radians) +** dob double* observed declination (radians) +** rob double* observed right ascension (CIO-based, radians) +** +** Notes: +** +** 1) This function returns zenith distance rather than altitude in +** order to reflect the fact that no allowance is made for +** depression of the horizon. +** +** 2) The accuracy of the result is limited by the corrections for +** refraction, which use a simple A*tan(z) + B*tan^3(z) model. +** Providing the meteorological parameters are known accurately and +** there are no gross local effects, the predicted observed +** coordinates should be within 0.05 arcsec (optical) or 1 arcsec +** (radio) for a zenith distance of less than 70 degrees, better +** than 30 arcsec (optical or radio) at 85 degrees and better +** than 20 arcmin (optical) or 30 arcmin (radio) at the horizon. +** +** Without refraction, the complementary functions eraAtioq and +** eraAtoiq are self-consistent to better than 1 microarcsecond all +** over the celestial sphere. With refraction included, consistency +** falls off at high zenith distances, but is still better than +** 0.05 arcsec at 85 degrees. +** +** 3) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** 4) The CIRS RA,Dec is obtained from a star catalog mean place by +** allowing for space motion, parallax, the Sun's gravitational lens +** effect, annual aberration and precession-nutation. For star +** positions in the ICRS, these effects can be applied by means of +** the eraAtci13 (etc.) functions. Starting from classical "mean +** place" systems, additional transformations will be needed first. +** +** 5) "Observed" Az,El means the position that would be seen by a +** perfect geodetically aligned theodolite. This is obtained from +** the CIRS RA,Dec by allowing for Earth orientation and diurnal +** aberration, rotating from equator to horizon coordinates, and +** then adjusting for refraction. The HA,Dec is obtained by +** rotating back into equatorial coordinates, and is the position +** that would be seen by a perfect equatorial with its polar axis +** aligned to the Earth's axis of rotation. Finally, the RA is +** obtained by subtracting the HA from the local ERA. +** +** 6) The star-independent CIRS-to-observed-place parameters in ASTROM +** may be computed with eraApio[13] or eraApco[13]. If nothing has +** changed significantly except the time, eraAper[13] may be used to +** perform the requisite adjustment to the astrom structure. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraC2s p-vector to spherical +** eraAnp normalize angle into range 0 to 2pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Minimum cos(alt) and sin(alt) for refraction purposes */ + const double CELMIN = 1e-6; + const double SELMIN = 0.05; + + double v[3], x, y, z, xhd, yhd, zhd, f, xhdt, yhdt, zhdt, + xaet, yaet, zaet, azobs, r, tz, w, del, cosdel, + xaeo, yaeo, zaeo, zdobs, hmobs, dcobs, raobs; + +/*--------------------------------------------------------------------*/ + +/* CIRS RA,Dec to Cartesian -HA,Dec. */ + eraS2c(ri-astrom->eral, di, v); + x = v[0]; + y = v[1]; + z = v[2]; + +/* Polar motion. */ + xhd = x + astrom->xpl*z; + yhd = y - astrom->ypl*z; + zhd = z - astrom->xpl*x + astrom->ypl*y; + +/* Diurnal aberration. */ + f = ( 1.0 - astrom->diurab*yhd ); + xhdt = f * xhd; + yhdt = f * ( yhd + astrom->diurab ); + zhdt = f * zhd; + +/* Cartesian -HA,Dec to Cartesian Az,El (S=0,E=90). */ + xaet = astrom->sphi*xhdt - astrom->cphi*zhdt; + yaet = yhdt; + zaet = astrom->cphi*xhdt + astrom->sphi*zhdt; + +/* Azimuth (N=0,E=90). */ + azobs = ( xaet != 0.0 || yaet != 0.0 ) ? atan2(yaet,-xaet) : 0.0; + +/* ---------- */ +/* Refraction */ +/* ---------- */ + +/* Cosine and sine of altitude, with precautions. */ + r = sqrt(xaet*xaet + yaet*yaet); + r = r > CELMIN ? r : CELMIN; + z = zaet > SELMIN ? zaet : SELMIN; + +/* A*tan(z)+B*tan^3(z) model, with Newton-Raphson correction. */ + tz = r/z; + w = astrom->refb*tz*tz; + del = ( astrom->refa + w ) * tz / + ( 1.0 + ( astrom->refa + 3.0*w ) / ( z*z ) ); + +/* Apply the change, giving observed vector. */ + cosdel = 1.0 - del*del/2.0; + f = cosdel - del*z/r; + xaeo = xaet*f; + yaeo = yaet*f; + zaeo = cosdel*zaet + del*r; + +/* Observed ZD. */ + zdobs = atan2(sqrt(xaeo*xaeo+yaeo*yaeo), zaeo); + +/* Az/El vector to HA,Dec vector (both right-handed). */ + v[0] = astrom->sphi*xaeo + astrom->cphi*zaeo; + v[1] = yaeo; + v[2] = - astrom->cphi*xaeo + astrom->sphi*zaeo; + +/* To spherical -HA,Dec. */ + eraC2s ( v, &hmobs, &dcobs ); + +/* Right ascension (with respect to CIO). */ + raobs = astrom->eral + hmobs; + +/* Return the results. */ + *aob = eraAnp(azobs); + *zob = zdobs; + *hob = -hmobs; + *dob = dcobs; + *rob = eraAnp(raobs); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atoc13.c b/erfa/atoc13.c new file mode 100644 index 0000000..071494b --- /dev/null +++ b/erfa/atoc13.c @@ -0,0 +1,233 @@ +#include "erfa.h" + +int eraAtoc13(const char *type, double ob1, double ob2, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tc, double rh, double wl, + double *rc, double *dc) +/* +** - - - - - - - - - - +** e r a A t o c 1 3 +** - - - - - - - - - - +** +** Observed place at a groundbased site to to ICRS astrometric RA,Dec. +** The caller supplies UTC, site coordinates, ambient air conditions +** and observing wavelength. +** +** Given: +** type char[] type of coordinates - "R", "H" or "A" (Notes 1,2) +** ob1 double observed Az, HA or RA (radians; Az is N=0,E=90) +** ob2 double observed ZD or Dec (radians) +** utc1 double UTC as a 2-part... +** utc2 double ...quasi Julian Date (Notes 3,4) +** dut1 double UT1-UTC (seconds, Note 5) +** elong double longitude (radians, east +ve, Note 6) +** phi double geodetic latitude (radians, Note 6) +** hm double height above ellipsoid (m, geodetic Notes 6,8) +** xp,yp double polar motion coordinates (radians, Note 7) +** phpa double pressure at the observer (hPa = mB, Note 8) +** tc double ambient temperature at the observer (deg C) +** rh double relative humidity at the observer (range 0-1) +** wl double wavelength (micrometers, Note 9) +** +** Returned: +** rc,dc double ICRS astrometric RA,Dec (radians) +** +** Returned (function value): +** int status: +1 = dubious year (Note 4) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) "Observed" Az,ZD means the position that would be seen by a +** perfect geodetically aligned theodolite. (Zenith distance is +** used rather than altitude in order to reflect the fact that no +** allowance is made for depression of the horizon.) This is +** related to the observed HA,Dec via the standard rotation, using +** the geodetic latitude (corrected for polar motion), while the +** observed HA and RA are related simply through the Earth rotation +** angle and the site longitude. "Observed" RA,Dec or HA,Dec thus +** means the position that would be seen by a perfect equatorial +** with its polar axis aligned to the Earth's axis of rotation. +** +** 2) Only the first character of the type argument is significant. +** "R" or "r" indicates that ob1 and ob2 are the observed right +** ascension and declination; "H" or "h" indicates that they are +** hour angle (west +ve) and declination; anything else ("A" or +** "a" is recommended) indicates that ob1 and ob2 are azimuth +** (north zero, east 90 deg) and zenith distance. +** +** 3) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** However, JD cannot unambiguously represent UTC during a leap +** second unless special measures are taken. The convention in the +** present function is that the JD day represents UTC days whether +** the length is 86399, 86400 or 86401 SI seconds. +** +** Applications should use the function eraDtf2d to convert from +** calendar date and time of day into 2-part quasi Julian Date, as +** it implements the leap-second-ambiguity convention just +** described. +** +** 4) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the +** future to be trusted. See eraDat for further details. +** +** 5) UT1-UTC is tabulated in IERS bulletins. It increases by exactly +** one second at the end of each positive UTC leap second, +** introduced in order to keep UT1-UTC within +/- 0.9s. n.b. This +** practice is under review, and in the future UT1-UTC may grow +** essentially without limit. +** +** 6) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN: the +** longitude required by the present function is east-positive +** (i.e. right-handed), in accordance with geographical convention. +** +** 7) The polar motion xp,yp can be obtained from IERS bulletins. The +** values are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions 2003), measured along the +** meridians 0 and 90 deg west respectively. For many +** applications, xp and yp can be set to zero. +** +** 8) If hm, the height above the ellipsoid of the observing station +** in meters, is not known but phpa, the pressure in hPa (=mB), is +** available, an adequate estimate of hm can be obtained from the +** expression +** +** hm = -29.3 * tsl * log ( phpa / 1013.25 ); +** +** where tsl is the approximate sea-level air temperature in K +** (See Astrophysical Quantities, C.W.Allen, 3rd edition, section +** 52). Similarly, if the pressure phpa is not known, it can be +** estimated from the height of the observing station, hm, as +** follows: +** +** phpa = 1013.25 * exp ( -hm / ( 29.3 * tsl ) ); +** +** Note, however, that the refraction is nearly proportional to +** the pressure and that an accurate phpa value is important for +** precise work. +** +** 9) The argument wl specifies the observing wavelength in +** micrometers. The transition from optical to radio is assumed to +** occur at 100 micrometers (about 3000 GHz). +** +** 10) The accuracy of the result is limited by the corrections for +** refraction, which use a simple A*tan(z) + B*tan^3(z) model. +** Providing the meteorological parameters are known accurately and +** there are no gross local effects, the predicted astrometric +** coordinates should be within 0.05 arcsec (optical) or 1 arcsec +** (radio) for a zenith distance of less than 70 degrees, better +** than 30 arcsec (optical or radio) at 85 degrees and better +** than 20 arcmin (optical) or 30 arcmin (radio) at the horizon. +** +** Without refraction, the complementary functions eraAtco13 and +** eraAtoc13 are self-consistent to better than 1 microarcsecond +** all over the celestial sphere. With refraction included, +** consistency falls off at high zenith distances, but is still +** better than 0.05 arcsec at 85 degrees. +** +** 11) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** Called: +** eraApco13 astrometry parameters, ICRS-observed +** eraAtoiq quick observed to CIRS +** eraAticq quick CIRS to ICRS +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + eraASTROM astrom; + double eo, ri, di; + + +/* Star-independent astrometry parameters. */ + j = eraApco13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom, &eo); + +/* Abort if bad UTC. */ + if ( j < 0 ) return j; + +/* Transform observed to CIRS. */ + eraAtoiq(type, ob1, ob2, &astrom, &ri, &di); + +/* Transform CIRS to ICRS. */ + eraAticq(ri, di, &astrom, rc, dc); + +/* Return OK/warning status. */ + return j; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atoi13.c b/erfa/atoi13.c new file mode 100644 index 0000000..87d68c9 --- /dev/null +++ b/erfa/atoi13.c @@ -0,0 +1,228 @@ +#include "erfa.h" + +int eraAtoi13(const char *type, double ob1, double ob2, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tc, double rh, double wl, + double *ri, double *di) +/* +** - - - - - - - - - - +** e r a A t o i 1 3 +** - - - - - - - - - - +** +** Observed place to CIRS. The caller supplies UTC, site coordinates, +** ambient air conditions and observing wavelength. +** +** Given: +** type char[] type of coordinates - "R", "H" or "A" (Notes 1,2) +** ob1 double observed Az, HA or RA (radians; Az is N=0,E=90) +** ob2 double observed ZD or Dec (radians) +** utc1 double UTC as a 2-part... +** utc2 double ...quasi Julian Date (Notes 3,4) +** dut1 double UT1-UTC (seconds, Note 5) +** elong double longitude (radians, east +ve, Note 6) +** phi double geodetic latitude (radians, Note 6) +** hm double height above the ellipsoid (meters, Notes 6,8) +** xp,yp double polar motion coordinates (radians, Note 7) +** phpa double pressure at the observer (hPa = mB, Note 8) +** tc double ambient temperature at the observer (deg C) +** rh double relative humidity at the observer (range 0-1) +** wl double wavelength (micrometers, Note 9) +** +** Returned: +** ri double* CIRS right ascension (CIO-based, radians) +** di double* CIRS declination (radians) +** +** Returned (function value): +** int status: +1 = dubious year (Note 2) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) "Observed" Az,ZD means the position that would be seen by a +** perfect geodetically aligned theodolite. (Zenith distance is +** used rather than altitude in order to reflect the fact that no +** allowance is made for depression of the horizon.) This is +** related to the observed HA,Dec via the standard rotation, using +** the geodetic latitude (corrected for polar motion), while the +** observed HA and RA are related simply through the Earth rotation +** angle and the site longitude. "Observed" RA,Dec or HA,Dec thus +** means the position that would be seen by a perfect equatorial +** with its polar axis aligned to the Earth's axis of rotation. +** +** 2) Only the first character of the type argument is significant. +** "R" or "r" indicates that ob1 and ob2 are the observed right +** ascension and declination; "H" or "h" indicates that they are +** hour angle (west +ve) and declination; anything else ("A" or +** "a" is recommended) indicates that ob1 and ob2 are azimuth +** (north zero, east 90 deg) and zenith distance. +** +** 3) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** However, JD cannot unambiguously represent UTC during a leap +** second unless special measures are taken. The convention in the +** present function is that the JD day represents UTC days whether +** the length is 86399, 86400 or 86401 SI seconds. +** +** Applications should use the function eraDtf2d to convert from +** calendar date and time of day into 2-part quasi Julian Date, as +** it implements the leap-second-ambiguity convention just +** described. +** +** 4) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the +** future to be trusted. See eraDat for further details. +** +** 5) UT1-UTC is tabulated in IERS bulletins. It increases by exactly +** one second at the end of each positive UTC leap second, +** introduced in order to keep UT1-UTC within +/- 0.9s. n.b. This +** practice is under review, and in the future UT1-UTC may grow +** essentially without limit. +** +** 6) The geographical coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. TAKE CARE WITH THE LONGITUDE SIGN: the +** longitude required by the present function is east-positive +** (i.e. right-handed), in accordance with geographical convention. +** +** 7) The polar motion xp,yp can be obtained from IERS bulletins. The +** values are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions 2003), measured along the +** meridians 0 and 90 deg west respectively. For many +** applications, xp and yp can be set to zero. +** +** 8) If hm, the height above the ellipsoid of the observing station +** in meters, is not known but phpa, the pressure in hPa (=mB), is +** available, an adequate estimate of hm can be obtained from the +** expression +** +** hm = -29.3 * tsl * log ( phpa / 1013.25 ); +** +** where tsl is the approximate sea-level air temperature in K +** (See Astrophysical Quantities, C.W.Allen, 3rd edition, section +** 52). Similarly, if the pressure phpa is not known, it can be +** estimated from the height of the observing station, hm, as +** follows: +** +** phpa = 1013.25 * exp ( -hm / ( 29.3 * tsl ) ); +** +** Note, however, that the refraction is nearly proportional to +** the pressure and that an accurate phpa value is important for +** precise work. +** +** 9) The argument wl specifies the observing wavelength in +** micrometers. The transition from optical to radio is assumed to +** occur at 100 micrometers (about 3000 GHz). +** +** 10) The accuracy of the result is limited by the corrections for +** refraction, which use a simple A*tan(z) + B*tan^3(z) model. +** Providing the meteorological parameters are known accurately and +** there are no gross local effects, the predicted astrometric +** coordinates should be within 0.05 arcsec (optical) or 1 arcsec +** (radio) for a zenith distance of less than 70 degrees, better +** than 30 arcsec (optical or radio) at 85 degrees and better +** than 20 arcmin (optical) or 30 arcmin (radio) at the horizon. +** +** Without refraction, the complementary functions eraAtio13 and +** eraAtoi13 are self-consistent to better than 1 microarcsecond +** all over the celestial sphere. With refraction included, +** consistency falls off at high zenith distances, but is still +** better than 0.05 arcsec at 85 degrees. +** +** 12) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** Called: +** eraApio13 astrometry parameters, CIRS-observed, 2013 +** eraAtoiq quick observed to CIRS +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + eraASTROM astrom; + + +/* Star-independent astrometry parameters for CIRS->observed. */ + j = eraApio13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom); + +/* Abort if bad UTC. */ + if ( j < 0 ) return j; + +/* Transform observed to CIRS. */ + eraAtoiq(type, ob1, ob2, &astrom, ri, di); + +/* Return OK/warning status. */ + return j; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/atoiq.c b/erfa/atoiq.c new file mode 100644 index 0000000..646f9ae --- /dev/null +++ b/erfa/atoiq.c @@ -0,0 +1,260 @@ +#include "erfa.h" + +void eraAtoiq(const char *type, + double ob1, double ob2, eraASTROM *astrom, + double *ri, double *di) +/* +** - - - - - - - - - +** e r a A t o i q +** - - - - - - - - - +** +** Quick observed place to CIRS, given the star-independent astrometry +** parameters. +** +** Use of this function is appropriate when efficiency is important and +** where many star positions are all to be transformed for one date. +** The star-independent astrometry parameters can be obtained by +** calling eraApio[13] or eraApco[13]. +** +** Given: +** type char[] type of coordinates: "R", "H" or "A" (Note 1) +** ob1 double observed Az, HA or RA (radians; Az is N=0,E=90) +** ob2 double observed ZD or Dec (radians) +** astrom eraASTROM* star-independent astrometry parameters: +** pmt double PM time interval (SSB, Julian years) +** eb double[3] SSB to observer (vector, au) +** eh double[3] Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** v double[3] barycentric observer velocity (vector, c) +** bm1 double sqrt(1-|v|^2): reciprocal of Lorenz factor +** bpn double[3][3] bias-precession-nutation matrix +** along double longitude + s' (radians) +** xpl double polar motion xp wrt local meridian (radians) +** ypl double polar motion yp wrt local meridian (radians) +** sphi double sine of geodetic latitude +** cphi double cosine of geodetic latitude +** diurab double magnitude of diurnal aberration vector +** eral double "local" Earth rotation angle (radians) +** refa double refraction constant A (radians) +** refb double refraction constant B (radians) +** +** Returned: +** ri double* CIRS right ascension (CIO-based, radians) +** di double* CIRS declination (radians) +** +** Notes: +** +** 1) "Observed" Az,El means the position that would be seen by a +** perfect geodetically aligned theodolite. This is related to +** the observed HA,Dec via the standard rotation, using the geodetic +** latitude (corrected for polar motion), while the observed HA and +** RA are related simply through the Earth rotation angle and the +** site longitude. "Observed" RA,Dec or HA,Dec thus means the +** position that would be seen by a perfect equatorial with its +** polar axis aligned to the Earth's axis of rotation. By removing +** from the observed place the effects of atmospheric refraction and +** diurnal aberration, the CIRS RA,Dec is obtained. +** +** 2) Only the first character of the type argument is significant. +** "R" or "r" indicates that ob1 and ob2 are the observed right +** ascension and declination; "H" or "h" indicates that they are +** hour angle (west +ve) and declination; anything else ("A" or +** "a" is recommended) indicates that ob1 and ob2 are azimuth (north +** zero, east 90 deg) and zenith distance. (Zenith distance is used +** rather than altitude in order to reflect the fact that no +** allowance is made for depression of the horizon.) +** +** 3) The accuracy of the result is limited by the corrections for +** refraction, which use a simple A*tan(z) + B*tan^3(z) model. +** Providing the meteorological parameters are known accurately and +** there are no gross local effects, the predicted observed +** coordinates should be within 0.05 arcsec (optical) or 1 arcsec +** (radio) for a zenith distance of less than 70 degrees, better +** than 30 arcsec (optical or radio) at 85 degrees and better than +** 20 arcmin (optical) or 30 arcmin (radio) at the horizon. +** +** Without refraction, the complementary functions eraAtioq and +** eraAtoiq are self-consistent to better than 1 microarcsecond all +** over the celestial sphere. With refraction included, consistency +** falls off at high zenith distances, but is still better than +** 0.05 arcsec at 85 degrees. +** +** 4) It is advisable to take great care with units, as even unlikely +** values of the input parameters are accepted and processed in +** accordance with the models used. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraC2s p-vector to spherical +** eraAnp normalize angle into range 0 to 2pi +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int c; + double c1, c2, sphi, cphi, ce, xaeo, yaeo, zaeo, v[3], + xmhdo, ymhdo, zmhdo, az, sz, zdo, refa, refb, tz, dref, + zdt, xaet, yaet, zaet, xmhda, ymhda, zmhda, + f, xhd, yhd, zhd, xpl, ypl, w, hma; + + +/* Coordinate type. */ + c = (int) type[0]; + +/* Coordinates. */ + c1 = ob1; + c2 = ob2; + +/* Sin, cos of latitude. */ + sphi = astrom->sphi; + cphi = astrom->cphi; + +/* Standardize coordinate type. */ + if ( c == 'r' || c == 'R' ) { + c = 'R'; + } else if ( c == 'h' || c == 'H' ) { + c = 'H'; + } else { + c = 'A'; + } + +/* If Az,ZD, convert to Cartesian (S=0,E=90). */ + if ( c == 'A' ) { + ce = sin(c2); + xaeo = - cos(c1) * ce; + yaeo = sin(c1) * ce; + zaeo = cos(c2); + + } else { + + /* If RA,Dec, convert to HA,Dec. */ + if ( c == 'R' ) c1 = astrom->eral - c1; + + /* To Cartesian -HA,Dec. */ + eraS2c ( -c1, c2, v ); + xmhdo = v[0]; + ymhdo = v[1]; + zmhdo = v[2]; + + /* To Cartesian Az,El (S=0,E=90). */ + xaeo = sphi*xmhdo - cphi*zmhdo; + yaeo = ymhdo; + zaeo = cphi*xmhdo + sphi*zmhdo; + } + +/* Azimuth (S=0,E=90). */ + az = ( xaeo != 0.0 || yaeo != 0.0 ) ? atan2(yaeo,xaeo) : 0.0; + +/* Sine of observed ZD, and observed ZD. */ + sz = sqrt ( xaeo*xaeo + yaeo*yaeo ); + zdo = atan2 ( sz, zaeo ); + +/* +** Refraction +** ---------- +*/ + +/* Fast algorithm using two constant model. */ + refa = astrom->refa; + refb = astrom->refb; + tz = sz / zaeo; + dref = ( refa + refb*tz*tz ) * tz; + zdt = zdo + dref; + +/* To Cartesian Az,ZD. */ + ce = sin(zdt); + xaet = cos(az) * ce; + yaet = sin(az) * ce; + zaet = cos(zdt); + +/* Cartesian Az,ZD to Cartesian -HA,Dec. */ + xmhda = sphi*xaet + cphi*zaet; + ymhda = yaet; + zmhda = - cphi*xaet + sphi*zaet; + +/* Diurnal aberration. */ + f = ( 1.0 + astrom->diurab*ymhda ); + xhd = f * xmhda; + yhd = f * ( ymhda - astrom->diurab ); + zhd = f * zmhda; + +/* Polar motion. */ + xpl = astrom->xpl; + ypl = astrom->ypl; + w = xpl*xhd - ypl*yhd + zhd; + v[0] = xhd - xpl*w; + v[1] = yhd + ypl*w; + v[2] = w - ( xpl*xpl + ypl*ypl ) * zhd; + +/* To spherical -HA,Dec. */ + eraC2s(v, &hma, di); + +/* Right ascension. */ + *ri = eraAnp(astrom->eral + hma); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/bi00.c b/erfa/bi00.c new file mode 100644 index 0000000..8d0a303 --- /dev/null +++ b/erfa/bi00.c @@ -0,0 +1,125 @@ +#include "erfa.h" + +void eraBi00(double *dpsibi, double *depsbi, double *dra) +/* +** - - - - - - - - +** e r a B i 0 0 +** - - - - - - - - +** +** Frame bias components of IAU 2000 precession-nutation models (part +** of MHB2000 with additions). +** +** Returned: +** dpsibi,depsbi double longitude and obliquity corrections +** dra double the ICRS RA of the J2000.0 mean equinox +** +** Notes: +** +** 1) The frame bias corrections in longitude and obliquity (radians) +** are required in order to correct for the offset between the GCRS +** pole and the mean J2000.0 pole. They define, with respect to the +** GCRS frame, a J2000.0 mean pole that is consistent with the rest +** of the IAU 2000A precession-nutation model. +** +** 2) In addition to the displacement of the pole, the complete +** description of the frame bias requires also an offset in right +** ascension. This is not part of the IAU 2000A model, and is from +** Chapront et al. (2002). It is returned in radians. +** +** 3) This is a supplemented implementation of one aspect of the IAU +** 2000A nutation model, formally adopted by the IAU General +** Assembly in 2000, namely MHB2000 (Mathews et al. 2002). +** +** References: +** +** Chapront, J., Chapront-Touze, M. & Francou, G., Astron. +** Astrophys., 387, 700, 2002. +** +** Mathews, P.M., Herring, T.A., Buffet, B.A., "Modeling of nutation +** and precession New nutation series for nonrigid Earth and +** insights into the Earth's interior", J.Geophys.Res., 107, B4, +** 2002. The MHB2000 code itself was obtained on 9th September 2002 +** from ftp://maia.usno.navy.mil/conv2000/chapter5/IAU2000A. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* The frame bias corrections in longitude and obliquity */ + const double DPBIAS = -0.041775 * ERFA_DAS2R, + DEBIAS = -0.0068192 * ERFA_DAS2R; + +/* The ICRS RA of the J2000.0 equinox (Chapront et al., 2002) */ + const double DRA0 = -0.0146 * ERFA_DAS2R; + + +/* Return the results (which are fixed). */ + *dpsibi = DPBIAS; + *depsbi = DEBIAS; + *dra = DRA0; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/bp00.c b/erfa/bp00.c new file mode 100644 index 0000000..b626322 --- /dev/null +++ b/erfa/bp00.c @@ -0,0 +1,181 @@ +#include "erfa.h" + +void eraBp00(double date1, double date2, + double rb[3][3], double rp[3][3], double rbp[3][3]) +/* +** - - - - - - - - +** e r a B p 0 0 +** - - - - - - - - +** +** Frame bias and precession, IAU 2000. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rb double[3][3] frame bias matrix (Note 2) +** rp double[3][3] precession matrix (Note 3) +** rbp double[3][3] bias-precession matrix (Note 4) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix rb transforms vectors from GCRS to mean J2000.0 by +** applying frame bias. +** +** 3) The matrix rp transforms vectors from J2000.0 mean equator and +** equinox to mean equator and equinox of date by applying +** precession. +** +** 4) The matrix rbp transforms vectors from GCRS to mean equator and +** equinox of date by applying frame bias then precession. It is +** the product rp x rb. +** +** 5) It is permissible to re-use the same array in the returned +** arguments. The arrays are filled in the order given. +** +** Called: +** eraBi00 frame bias components, IAU 2000 +** eraPr00 IAU 2000 precession adjustments +** eraIr initialize r-matrix to identity +** eraRx rotate around X-axis +** eraRy rotate around Y-axis +** eraRz rotate around Z-axis +** eraCr copy r-matrix +** eraRxr product of two r-matrices +** +** Reference: +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* J2000.0 obliquity (Lieske et al. 1977) */ + const double EPS0 = 84381.448 * ERFA_DAS2R; + + double t, dpsibi, depsbi, dra0, psia77, oma77, chia, + dpsipr, depspr, psia, oma, rbw[3][3]; + + +/* Interval between fundamental epoch J2000.0 and current date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Frame bias. */ + eraBi00(&dpsibi, &depsbi, &dra0); + +/* Precession angles (Lieske et al. 1977) */ + psia77 = (5038.7784 + (-1.07259 + (-0.001147) * t) * t) * t * ERFA_DAS2R; + oma77 = EPS0 + ((0.05127 + (-0.007726) * t) * t) * t * ERFA_DAS2R; + chia = ( 10.5526 + (-2.38064 + (-0.001125) * t) * t) * t * ERFA_DAS2R; + +/* Apply IAU 2000 precession corrections. */ + eraPr00(date1, date2, &dpsipr, &depspr); + psia = psia77 + dpsipr; + oma = oma77 + depspr; + +/* Frame bias matrix: GCRS to J2000.0. */ + eraIr(rbw); + eraRz(dra0, rbw); + eraRy(dpsibi*sin(EPS0), rbw); + eraRx(-depsbi, rbw); + eraCr(rbw, rb); + +/* Precession matrix: J2000.0 to mean of date. */ + eraIr(rp); + eraRx(EPS0, rp); + eraRz(-psia, rp); + eraRx(-oma, rp); + eraRz(chia, rp); + +/* Bias-precession matrix: GCRS to mean of date. */ + eraRxr(rp, rbw, rbp); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/bp06.c b/erfa/bp06.c new file mode 100644 index 0000000..18d07b7 --- /dev/null +++ b/erfa/bp06.c @@ -0,0 +1,152 @@ +#include "erfa.h" + +void eraBp06(double date1, double date2, + double rb[3][3], double rp[3][3], double rbp[3][3]) +/* +** - - - - - - - - +** e r a B p 0 6 +** - - - - - - - - +** +** Frame bias and precession, IAU 2006. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rb double[3][3] frame bias matrix (Note 2) +** rp double[3][3] precession matrix (Note 3) +** rbp double[3][3] bias-precession matrix (Note 4) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix rb transforms vectors from GCRS to mean J2000.0 by +** applying frame bias. +** +** 3) The matrix rp transforms vectors from mean J2000.0 to mean of +** date by applying precession. +** +** 4) The matrix rbp transforms vectors from GCRS to mean of date by +** applying frame bias then precession. It is the product rp x rb. +** +** 5) It is permissible to re-use the same array in the returned +** arguments. The arrays are filled in the order given. +** +** Called: +** eraPfw06 bias-precession F-W angles, IAU 2006 +** eraFw2m F-W angles to r-matrix +** eraPmat06 PB matrix, IAU 2006 +** eraTr transpose r-matrix +** eraRxr product of two r-matrices +** eraCr copy r-matrix +** +** References: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gamb, phib, psib, epsa, rbpw[3][3], rbt[3][3]; + + +/* B matrix. */ + eraPfw06(ERFA_DJM0, ERFA_DJM00, &gamb, &phib, &psib, &epsa); + eraFw2m(gamb, phib, psib, epsa, rb); + +/* PxB matrix (temporary). */ + eraPmat06(date1, date2, rbpw); + +/* P matrix. */ + eraTr(rb, rbt); + eraRxr(rbpw, rbt, rp); + +/* PxB matrix. */ + eraCr(rbpw, rbp); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/bpn2xy.c b/erfa/bpn2xy.c new file mode 100644 index 0000000..0424601 --- /dev/null +++ b/erfa/bpn2xy.c @@ -0,0 +1,109 @@ +#include "erfa.h" + +void eraBpn2xy(double rbpn[3][3], double *x, double *y) +/* +** - - - - - - - - - - +** e r a B p n 2 x y +** - - - - - - - - - - +** +** Extract from the bias-precession-nutation matrix the X,Y coordinates +** of the Celestial Intermediate Pole. +** +** Given: +** rbpn double[3][3] celestial-to-true matrix (Note 1) +** +** Returned: +** x,y double Celestial Intermediate Pole (Note 2) +** +** Notes: +** +** 1) The matrix rbpn transforms vectors from GCRS to true equator (and +** CIO or equinox) of date, and therefore the Celestial Intermediate +** Pole unit vector is the bottom row of the matrix. +** +** 2) The arguments x,y are components of the Celestial Intermediate +** Pole unit vector in the Geocentric Celestial Reference System. +** +** Reference: +** +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 +** (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Extract the X,Y coordinates. */ + *x = rbpn[2][0]; + *y = rbpn[2][1]; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2i00a.c b/erfa/c2i00a.c new file mode 100644 index 0000000..a41552d --- /dev/null +++ b/erfa/c2i00a.c @@ -0,0 +1,148 @@ +#include "erfa.h" + +void eraC2i00a(double date1, double date2, double rc2i[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 i 0 0 a +** - - - - - - - - - - +** +** Form the celestial-to-intermediate matrix for a given date using the +** IAU 2000A precession-nutation model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rc2i double[3][3] celestial-to-intermediate matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix rc2i is the first stage in the transformation from +** celestial to terrestrial coordinates: +** +** [TRS] = RPOM * R_3(ERA) * rc2i * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), ERA is the Earth +** Rotation Angle and RPOM is the polar motion matrix. +** +** 3) A faster, but slightly less accurate result (about 1 mas), can be +** obtained by using instead the eraC2i00b function. +** +** Called: +** eraPnm00a classical NPB matrix, IAU 2000A +** eraC2ibpn celestial-to-intermediate matrix, given NPB matrix +** +** References: +** +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 +** (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3]; + + +/* Obtain the celestial-to-true matrix (IAU 2000A). */ + eraPnm00a(date1, date2, rbpn); + +/* Form the celestial-to-intermediate matrix. */ + eraC2ibpn(date1, date2, rbpn, rc2i); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2i00b.c b/erfa/c2i00b.c new file mode 100644 index 0000000..5f95a8b --- /dev/null +++ b/erfa/c2i00b.c @@ -0,0 +1,148 @@ +#include "erfa.h" + +void eraC2i00b(double date1, double date2, double rc2i[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 i 0 0 b +** - - - - - - - - - - +** +** Form the celestial-to-intermediate matrix for a given date using the +** IAU 2000B precession-nutation model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rc2i double[3][3] celestial-to-intermediate matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix rc2i is the first stage in the transformation from +** celestial to terrestrial coordinates: +** +** [TRS] = RPOM * R_3(ERA) * rc2i * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), ERA is the Earth +** Rotation Angle and RPOM is the polar motion matrix. +** +** 3) The present function is faster, but slightly less accurate (about +** 1 mas), than the eraC2i00a function. +** +** Called: +** eraPnm00b classical NPB matrix, IAU 2000B +** eraC2ibpn celestial-to-intermediate matrix, given NPB matrix +** +** References: +** +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 +** (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3]; + + +/* Obtain the celestial-to-true matrix (IAU 2000B). */ + eraPnm00b(date1, date2, rbpn); + +/* Form the celestial-to-intermediate matrix. */ + eraC2ibpn(date1, date2, rbpn, rc2i); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2i06a.c b/erfa/c2i06a.c new file mode 100644 index 0000000..b8561e5 --- /dev/null +++ b/erfa/c2i06a.c @@ -0,0 +1,145 @@ +#include "erfa.h" + +void eraC2i06a(double date1, double date2, double rc2i[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 i 0 6 a +** - - - - - - - - - - +** +** Form the celestial-to-intermediate matrix for a given date using the +** IAU 2006 precession and IAU 2000A nutation models. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rc2i double[3][3] celestial-to-intermediate matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix rc2i is the first stage in the transformation from +** celestial to terrestrial coordinates: +** +** [TRS] = RPOM * R_3(ERA) * rc2i * [CRS] +** +** = RC2T * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), ERA is the Earth +** Rotation Angle and RPOM is the polar motion matrix. +** +** Called: +** eraPnm06a classical NPB matrix, IAU 2006/2000A +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS06 the CIO locator s, given X,Y, IAU 2006 +** eraC2ixys celestial-to-intermediate matrix, given X,Y and s +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), 2004, IERS Conventions (2003), +** IERS Technical Note No. 32, BKG +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3], x, y, s; + + +/* Obtain the celestial-to-true matrix (IAU 2006/2000A). */ + eraPnm06a(date1, date2, rbpn); + +/* Extract the X,Y coordinates. */ + eraBpn2xy(rbpn, &x, &y); + +/* Obtain the CIO locator. */ + s = eraS06(date1, date2, x, y); + +/* Form the celestial-to-intermediate matrix. */ + eraC2ixys(x, y, s, rc2i); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2ibpn.c b/erfa/c2ibpn.c new file mode 100644 index 0000000..74cd10b --- /dev/null +++ b/erfa/c2ibpn.c @@ -0,0 +1,151 @@ +#include "erfa.h" + +void eraC2ibpn(double date1, double date2, double rbpn[3][3], + double rc2i[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 i b p n +** - - - - - - - - - - +** +** Form the celestial-to-intermediate matrix for a given date given +** the bias-precession-nutation matrix. IAU 2000. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** rbpn double[3][3] celestial-to-true matrix (Note 2) +** +** Returned: +** rc2i double[3][3] celestial-to-intermediate matrix (Note 3) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix rbpn transforms vectors from GCRS to true equator (and +** CIO or equinox) of date. Only the CIP (bottom row) is used. +** +** 3) The matrix rc2i is the first stage in the transformation from +** celestial to terrestrial coordinates: +** +** [TRS] = RPOM * R_3(ERA) * rc2i * [CRS] +** +** = RC2T * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), ERA is the Earth +** Rotation Angle and RPOM is the polar motion matrix. +** +** 4) Although its name does not include "00", This function is in fact +** specific to the IAU 2000 models. +** +** Called: +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraC2ixy celestial-to-intermediate matrix, given X,Y +** +** References: +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double x, y; + + +/* Extract the X,Y coordinates. */ + eraBpn2xy(rbpn, &x, &y); + +/* Form the celestial-to-intermediate matrix (n.b. IAU 2000 specific). */ + eraC2ixy(date1, date2, x, y, rc2i); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2ixy.c b/erfa/c2ixy.c new file mode 100644 index 0000000..95451b1 --- /dev/null +++ b/erfa/c2ixy.c @@ -0,0 +1,140 @@ +#include "erfa.h" + +void eraC2ixy(double date1, double date2, double x, double y, + double rc2i[3][3]) +/* +** - - - - - - - - - +** e r a C 2 i x y +** - - - - - - - - - +** +** Form the celestial to intermediate-frame-of-date matrix for a given +** date when the CIP X,Y coordinates are known. IAU 2000. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** x,y double Celestial Intermediate Pole (Note 2) +** +** Returned: +** rc2i double[3][3] celestial-to-intermediate matrix (Note 3) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The Celestial Intermediate Pole coordinates are the x,y components +** of the unit vector in the Geocentric Celestial Reference System. +** +** 3) The matrix rc2i is the first stage in the transformation from +** celestial to terrestrial coordinates: +** +** [TRS] = RPOM * R_3(ERA) * rc2i * [CRS] +** +** = RC2T * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), ERA is the Earth +** Rotation Angle and RPOM is the polar motion matrix. +** +** 4) Although its name does not include "00", This function is in fact +** specific to the IAU 2000 models. +** +** Called: +** eraC2ixys celestial-to-intermediate matrix, given X,Y and s +** eraS00 the CIO locator s, given X,Y, IAU 2000A +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ + +{ +/* Compute s and then the matrix. */ + eraC2ixys(x, y, eraS00(date1, date2, x, y), rc2i); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2ixys.c b/erfa/c2ixys.c new file mode 100644 index 0000000..c676df8 --- /dev/null +++ b/erfa/c2ixys.c @@ -0,0 +1,132 @@ +#include "erfa.h" + +void eraC2ixys(double x, double y, double s, double rc2i[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 i x y s +** - - - - - - - - - - +** +** Form the celestial to intermediate-frame-of-date matrix given the CIP +** X,Y and the CIO locator s. +** +** Given: +** x,y double Celestial Intermediate Pole (Note 1) +** s double the CIO locator s (Note 2) +** +** Returned: +** rc2i double[3][3] celestial-to-intermediate matrix (Note 3) +** +** Notes: +** +** 1) The Celestial Intermediate Pole coordinates are the x,y +** components of the unit vector in the Geocentric Celestial +** Reference System. +** +** 2) The CIO locator s (in radians) positions the Celestial +** Intermediate Origin on the equator of the CIP. +** +** 3) The matrix rc2i is the first stage in the transformation from +** celestial to terrestrial coordinates: +** +** [TRS] = RPOM * R_3(ERA) * rc2i * [CRS] +** +** = RC2T * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), ERA is the Earth +** Rotation Angle and RPOM is the polar motion matrix. +** +** Called: +** eraIr initialize r-matrix to identity +** eraRz rotate around Z-axis +** eraRy rotate around Y-axis +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double r2, e, d; + + +/* Obtain the spherical angles E and d. */ + r2 = x*x + y*y; + e = (r2 != 0.0) ? atan2(y, x) : 0.0; + d = atan(sqrt(r2 / (1.0 - r2))); + +/* Form the matrix. */ + eraIr(rc2i); + eraRz(e, rc2i); + eraRy(d, rc2i); + eraRz(-(e+s), rc2i); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2s.c b/erfa/c2s.c new file mode 100644 index 0000000..c5feb6c --- /dev/null +++ b/erfa/c2s.c @@ -0,0 +1,105 @@ +#include "erfa.h" + +void eraC2s(double p[3], double *theta, double *phi) +/* +** - - - - - - - +** e r a C 2 s +** - - - - - - - +** +** P-vector to spherical coordinates. +** +** Given: +** p double[3] p-vector +** +** Returned: +** theta double longitude angle (radians) +** phi double latitude angle (radians) +** +** Notes: +** +** 1) The vector p can have any magnitude; only its direction is used. +** +** 2) If p is null, zero theta and phi are returned. +** +** 3) At either pole, zero theta is returned. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double x, y, z, d2; + + + x = p[0]; + y = p[1]; + z = p[2]; + d2 = x*x + y*y; + + *theta = (d2 == 0.0) ? 0.0 : atan2(y, x); + *phi = (z == 0.0) ? 0.0 : atan2(z, sqrt(d2)); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2t00a.c b/erfa/c2t00a.c new file mode 100644 index 0000000..7d15e3e --- /dev/null +++ b/erfa/c2t00a.c @@ -0,0 +1,163 @@ +#include "erfa.h" + +void eraC2t00a(double tta, double ttb, double uta, double utb, + double xp, double yp, double rc2t[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 t 0 0 a +** - - - - - - - - - - +** +** Form the celestial to terrestrial matrix given the date, the UT1 and +** the polar motion, using the IAU 2000A nutation model. +** +** Given: +** tta,ttb double TT as a 2-part Julian Date (Note 1) +** uta,utb double UT1 as a 2-part Julian Date (Note 1) +** xp,yp double coordinates of the pole (radians, Note 2) +** +** Returned: +** rc2t double[3][3] celestial-to-terrestrial matrix (Note 3) +** +** Notes: +** +** 1) The TT and UT1 dates tta+ttb and uta+utb are Julian Dates, +** apportioned in any convenient way between the arguments uta and +** utb. For example, JD(UT1)=2450123.7 could be expressed in any of +** these ways, among others: +** +** uta utb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. In the case of uta,utb, the +** date & time method is best matched to the Earth rotation angle +** algorithm used: maximum precision is delivered when the uta +** argument is for 0hrs UT1 on the day in question and the utb +** argument lies in the range 0 to 1, or vice versa. +** +** 2) The arguments xp and yp are the coordinates (in radians) of the +** Celestial Intermediate Pole with respect to the International +** Terrestrial Reference System (see IERS Conventions 2003), +** measured along the meridians to 0 and 90 deg west respectively. +** +** 3) The matrix rc2t transforms from celestial to terrestrial +** coordinates: +** +** [TRS] = RPOM * R_3(ERA) * RC2I * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), RC2I is the +** celestial-to-intermediate matrix, ERA is the Earth rotation +** angle and RPOM is the polar motion matrix. +** +** 4) A faster, but slightly less accurate result (about 1 mas), can +** be obtained by using instead the eraC2t00b function. +** +** Called: +** eraC2i00a celestial-to-intermediate matrix, IAU 2000A +** eraEra00 Earth rotation angle, IAU 2000 +** eraSp00 the TIO locator s', IERS 2000 +** eraPom00 polar motion matrix +** eraC2tcio form CIO-based celestial-to-terrestrial matrix +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rc2i[3][3], era, sp, rpom[3][3]; + + +/* Form the celestial-to-intermediate matrix for this TT (IAU 2000A). */ + eraC2i00a(tta, ttb, rc2i ); + +/* Predict the Earth rotation angle for this UT1. */ + era = eraEra00(uta, utb); + +/* Estimate s'. */ + sp = eraSp00(tta, ttb); + +/* Form the polar motion matrix. */ + eraPom00(xp, yp, sp, rpom); + +/* Combine to form the celestial-to-terrestrial matrix. */ + eraC2tcio(rc2i, era, rpom, rc2t); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2t00b.c b/erfa/c2t00b.c new file mode 100644 index 0000000..5d228bb --- /dev/null +++ b/erfa/c2t00b.c @@ -0,0 +1,159 @@ +#include "erfa.h" + +void eraC2t00b(double tta, double ttb, double uta, double utb, + double xp, double yp, double rc2t[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 t 0 0 b +** - - - - - - - - - - +** +** Form the celestial to terrestrial matrix given the date, the UT1 and +** the polar motion, using the IAU 2000B nutation model. +** +** Given: +** tta,ttb double TT as a 2-part Julian Date (Note 1) +** uta,utb double UT1 as a 2-part Julian Date (Note 1) +** xp,yp double coordinates of the pole (radians, Note 2) +** +** Returned: +** rc2t double[3][3] celestial-to-terrestrial matrix (Note 3) +** +** Notes: +** +** 1) The TT and UT1 dates tta+ttb and uta+utb are Julian Dates, +** apportioned in any convenient way between the arguments uta and +** utb. For example, JD(UT1)=2450123.7 could be expressed in any of +** these ways, among others: +** +** uta utb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. In the case of uta,utb, the +** date & time method is best matched to the Earth rotation angle +** algorithm used: maximum precision is delivered when the uta +** argument is for 0hrs UT1 on the day in question and the utb +** argument lies in the range 0 to 1, or vice versa. +** +** 2) The arguments xp and yp are the coordinates (in radians) of the +** Celestial Intermediate Pole with respect to the International +** Terrestrial Reference System (see IERS Conventions 2003), +** measured along the meridians to 0 and 90 deg west respectively. +** +** 3) The matrix rc2t transforms from celestial to terrestrial +** coordinates: +** +** [TRS] = RPOM * R_3(ERA) * RC2I * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), RC2I is the +** celestial-to-intermediate matrix, ERA is the Earth rotation +** angle and RPOM is the polar motion matrix. +** +** 4) The present function is faster, but slightly less accurate (about +** 1 mas), than the eraC2t00a function. +** +** Called: +** eraC2i00b celestial-to-intermediate matrix, IAU 2000B +** eraEra00 Earth rotation angle, IAU 2000 +** eraPom00 polar motion matrix +** eraC2tcio form CIO-based celestial-to-terrestrial matrix +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rc2i[3][3], era, rpom[3][3]; + + +/* Form the celestial-to-intermediate matrix for this TT (IAU 2000B). */ + eraC2i00b(tta, ttb, rc2i); + +/* Predict the Earth rotation angle for this UT1. */ + era = eraEra00(uta, utb); + +/* Form the polar motion matrix (neglecting s'). */ + eraPom00(xp, yp, 0.0, rpom); + +/* Combine to form the celestial-to-terrestrial matrix. */ + eraC2tcio(rc2i, era, rpom, rc2t); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2t06a.c b/erfa/c2t06a.c new file mode 100644 index 0000000..ea98d9d --- /dev/null +++ b/erfa/c2t06a.c @@ -0,0 +1,161 @@ +#include "erfa.h" + +void eraC2t06a(double tta, double ttb, double uta, double utb, + double xp, double yp, double rc2t[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 t 0 6 a +** - - - - - - - - - - +** +** Form the celestial to terrestrial matrix given the date, the UT1 and +** the polar motion, using the IAU 2006 precession and IAU 2000A +** nutation models. +** +** Given: +** tta,ttb double TT as a 2-part Julian Date (Note 1) +** uta,utb double UT1 as a 2-part Julian Date (Note 1) +** xp,yp double coordinates of the pole (radians, Note 2) +** +** Returned: +** rc2t double[3][3] celestial-to-terrestrial matrix (Note 3) +** +** Notes: +** +** 1) The TT and UT1 dates tta+ttb and uta+utb are Julian Dates, +** apportioned in any convenient way between the arguments uta and +** utb. For example, JD(UT1)=2450123.7 could be expressed in any of +** these ways, among others: +** +** uta utb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. In the case of uta,utb, the +** date & time method is best matched to the Earth rotation angle +** algorithm used: maximum precision is delivered when the uta +** argument is for 0hrs UT1 on the day in question and the utb +** argument lies in the range 0 to 1, or vice versa. +** +** 2) The arguments xp and yp are the coordinates (in radians) of the +** Celestial Intermediate Pole with respect to the International +** Terrestrial Reference System (see IERS Conventions 2003), +** measured along the meridians to 0 and 90 deg west respectively. +** +** 3) The matrix rc2t transforms from celestial to terrestrial +** coordinates: +** +** [TRS] = RPOM * R_3(ERA) * RC2I * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), RC2I is the +** celestial-to-intermediate matrix, ERA is the Earth rotation +** angle and RPOM is the polar motion matrix. +** +** Called: +** eraC2i06a celestial-to-intermediate matrix, IAU 2006/2000A +** eraEra00 Earth rotation angle, IAU 2000 +** eraSp00 the TIO locator s', IERS 2000 +** eraPom00 polar motion matrix +** eraC2tcio form CIO-based celestial-to-terrestrial matrix +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), 2004, IERS Conventions (2003), +** IERS Technical Note No. 32, BKG +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rc2i[3][3], era, sp, rpom[3][3]; + + +/* Form the celestial-to-intermediate matrix for this TT. */ + eraC2i06a(tta, ttb, rc2i); + +/* Predict the Earth rotation angle for this UT1. */ + era = eraEra00(uta, utb); + +/* Estimate s'. */ + sp = eraSp00(tta, ttb); + +/* Form the polar motion matrix. */ + eraPom00(xp, yp, sp, rpom); + +/* Combine to form the celestial-to-terrestrial matrix. */ + eraC2tcio(rc2i, era, rpom, rc2t); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2tcio.c b/erfa/c2tcio.c new file mode 100644 index 0000000..aa2ba29 --- /dev/null +++ b/erfa/c2tcio.c @@ -0,0 +1,131 @@ +#include "erfa.h" + +void eraC2tcio(double rc2i[3][3], double era, double rpom[3][3], + double rc2t[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 t c i o +** - - - - - - - - - - +** +** Assemble the celestial to terrestrial matrix from CIO-based +** components (the celestial-to-intermediate matrix, the Earth Rotation +** Angle and the polar motion matrix). +** +** Given: +** rc2i double[3][3] celestial-to-intermediate matrix +** era double Earth rotation angle (radians) +** rpom double[3][3] polar-motion matrix +** +** Returned: +** rc2t double[3][3] celestial-to-terrestrial matrix +** +** Notes: +** +** 1) This function constructs the rotation matrix that transforms +** vectors in the celestial system into vectors in the terrestrial +** system. It does so starting from precomputed components, namely +** the matrix which rotates from celestial coordinates to the +** intermediate frame, the Earth rotation angle and the polar motion +** matrix. One use of the present function is when generating a +** series of celestial-to-terrestrial matrices where only the Earth +** Rotation Angle changes, avoiding the considerable overhead of +** recomputing the precession-nutation more often than necessary to +** achieve given accuracy objectives. +** +** 2) The relationship between the arguments is as follows: +** +** [TRS] = RPOM * R_3(ERA) * rc2i * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003). +** +** Called: +** eraCr copy r-matrix +** eraRz rotate around Z-axis +** eraRxr product of two r-matrices +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), 2004, IERS Conventions (2003), +** IERS Technical Note No. 32, BKG +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double r[3][3]; + + +/* Construct the matrix. */ + eraCr(rc2i, r); + eraRz(era, r); + eraRxr(rpom, r, rc2t); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2teqx.c b/erfa/c2teqx.c new file mode 100644 index 0000000..57649ee --- /dev/null +++ b/erfa/c2teqx.c @@ -0,0 +1,131 @@ +#include "erfa.h" + +void eraC2teqx(double rbpn[3][3], double gst, double rpom[3][3], + double rc2t[3][3]) +/* +** - - - - - - - - - - +** e r a C 2 t e q x +** - - - - - - - - - - +** +** Assemble the celestial to terrestrial matrix from equinox-based +** components (the celestial-to-true matrix, the Greenwich Apparent +** Sidereal Time and the polar motion matrix). +** +** Given: +** rbpn double[3][3] celestial-to-true matrix +** gst double Greenwich (apparent) Sidereal Time (radians) +** rpom double[3][3] polar-motion matrix +** +** Returned: +** rc2t double[3][3] celestial-to-terrestrial matrix (Note 2) +** +** Notes: +** +** 1) This function constructs the rotation matrix that transforms +** vectors in the celestial system into vectors in the terrestrial +** system. It does so starting from precomputed components, namely +** the matrix which rotates from celestial coordinates to the +** true equator and equinox of date, the Greenwich Apparent Sidereal +** Time and the polar motion matrix. One use of the present function +** is when generating a series of celestial-to-terrestrial matrices +** where only the Sidereal Time changes, avoiding the considerable +** overhead of recomputing the precession-nutation more often than +** necessary to achieve given accuracy objectives. +** +** 2) The relationship between the arguments is as follows: +** +** [TRS] = rpom * R_3(gst) * rbpn * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003). +** +** Called: +** eraCr copy r-matrix +** eraRz rotate around Z-axis +** eraRxr product of two r-matrices +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double r[3][3]; + + +/* Construct the matrix. */ + eraCr(rbpn, r); + eraRz(gst, r); + eraRxr(rpom, r, rc2t); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2tpe.c b/erfa/c2tpe.c new file mode 100644 index 0000000..1894b7b --- /dev/null +++ b/erfa/c2tpe.c @@ -0,0 +1,176 @@ +#include "erfa.h" + +void eraC2tpe(double tta, double ttb, double uta, double utb, + double dpsi, double deps, double xp, double yp, + double rc2t[3][3]) +/* +** - - - - - - - - - +** e r a C 2 t p e +** - - - - - - - - - +** +** Form the celestial to terrestrial matrix given the date, the UT1, +** the nutation and the polar motion. IAU 2000. +** +** Given: +** tta,ttb double TT as a 2-part Julian Date (Note 1) +** uta,utb double UT1 as a 2-part Julian Date (Note 1) +** dpsi,deps double nutation (Note 2) +** xp,yp double coordinates of the pole (radians, Note 3) +** +** Returned: +** rc2t double[3][3] celestial-to-terrestrial matrix (Note 4) +** +** Notes: +** +** 1) The TT and UT1 dates tta+ttb and uta+utb are Julian Dates, +** apportioned in any convenient way between the arguments uta and +** utb. For example, JD(UT1)=2450123.7 could be expressed in any of +** these ways, among others: +** +** uta utb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. In the case of uta,utb, the +** date & time method is best matched to the Earth rotation angle +** algorithm used: maximum precision is delivered when the uta +** argument is for 0hrs UT1 on the day in question and the utb +** argument lies in the range 0 to 1, or vice versa. +** +** 2) The caller is responsible for providing the nutation components; +** they are in longitude and obliquity, in radians and are with +** respect to the equinox and ecliptic of date. For high-accuracy +** applications, free core nutation should be included as well as +** any other relevant corrections to the position of the CIP. +** +** 3) The arguments xp and yp are the coordinates (in radians) of the +** Celestial Intermediate Pole with respect to the International +** Terrestrial Reference System (see IERS Conventions 2003), +** measured along the meridians to 0 and 90 deg west respectively. +** +** 4) The matrix rc2t transforms from celestial to terrestrial +** coordinates: +** +** [TRS] = RPOM * R_3(GST) * RBPN * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), RBPN is the +** bias-precession-nutation matrix, GST is the Greenwich (apparent) +** Sidereal Time and RPOM is the polar motion matrix. +** +** 5) Although its name does not include "00", This function is in fact +** specific to the IAU 2000 models. +** +** Called: +** eraPn00 bias/precession/nutation results, IAU 2000 +** eraGmst00 Greenwich mean sidereal time, IAU 2000 +** eraSp00 the TIO locator s', IERS 2000 +** eraEe00 equation of the equinoxes, IAU 2000 +** eraPom00 polar motion matrix +** eraC2teqx form equinox-based celestial-to-terrestrial matrix +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double epsa, rb[3][3], rp[3][3], rbp[3][3], rn[3][3], + rbpn[3][3], gmst, ee, sp, rpom[3][3]; + + +/* Form the celestial-to-true matrix for this TT. */ + eraPn00(tta, ttb, dpsi, deps, &epsa, rb, rp, rbp, rn, rbpn); + +/* Predict the Greenwich Mean Sidereal Time for this UT1 and TT. */ + gmst = eraGmst00(uta, utb, tta, ttb); + +/* Predict the equation of the equinoxes given TT and nutation. */ + ee = eraEe00(tta, ttb, epsa, dpsi); + +/* Estimate s'. */ + sp = eraSp00(tta, ttb); + +/* Form the polar motion matrix. */ + eraPom00(xp, yp, sp, rpom); + +/* Combine to form the celestial-to-terrestrial matrix. */ + eraC2teqx(rbpn, gmst + ee, rpom, rc2t); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/c2txy.c b/erfa/c2txy.c new file mode 100644 index 0000000..bae827e --- /dev/null +++ b/erfa/c2txy.c @@ -0,0 +1,168 @@ +#include "erfa.h" + +void eraC2txy(double tta, double ttb, double uta, double utb, + double x, double y, double xp, double yp, + double rc2t[3][3]) +/* +** - - - - - - - - - +** e r a C 2 t x y +** - - - - - - - - - +** +** Form the celestial to terrestrial matrix given the date, the UT1, +** the CIP coordinates and the polar motion. IAU 2000. +** +** Given: +** tta,ttb double TT as a 2-part Julian Date (Note 1) +** uta,utb double UT1 as a 2-part Julian Date (Note 1) +** x,y double Celestial Intermediate Pole (Note 2) +** xp,yp double coordinates of the pole (radians, Note 3) +** +** Returned: +** rc2t double[3][3] celestial-to-terrestrial matrix (Note 4) +** +** Notes: +** +** 1) The TT and UT1 dates tta+ttb and uta+utb are Julian Dates, +** apportioned in any convenient way between the arguments uta and +** utb. For example, JD(UT1)=2450123.7 could be expressed in any o +** these ways, among others: +** +** uta utb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. In the case of uta,utb, the +** date & time method is best matched to the Earth rotation angle +** algorithm used: maximum precision is delivered when the uta +** argument is for 0hrs UT1 on the day in question and the utb +** argument lies in the range 0 to 1, or vice versa. +** +** 2) The Celestial Intermediate Pole coordinates are the x,y +** components of the unit vector in the Geocentric Celestial +** Reference System. +** +** 3) The arguments xp and yp are the coordinates (in radians) of the +** Celestial Intermediate Pole with respect to the International +** Terrestrial Reference System (see IERS Conventions 2003), +** measured along the meridians to 0 and 90 deg west respectively. +** +** 4) The matrix rc2t transforms from celestial to terrestrial +** coordinates: +** +** [TRS] = RPOM * R_3(ERA) * RC2I * [CRS] +** +** = rc2t * [CRS] +** +** where [CRS] is a vector in the Geocentric Celestial Reference +** System and [TRS] is a vector in the International Terrestrial +** Reference System (see IERS Conventions 2003), ERA is the Earth +** Rotation Angle and RPOM is the polar motion matrix. +** +** 5) Although its name does not include "00", This function is in fact +** specific to the IAU 2000 models. +** +** Called: +** eraC2ixy celestial-to-intermediate matrix, given X,Y +** eraEra00 Earth rotation angle, IAU 2000 +** eraSp00 the TIO locator s', IERS 2000 +** eraPom00 polar motion matrix +** eraC2tcio form CIO-based celestial-to-terrestrial matrix +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rc2i[3][3], era, sp, rpom[3][3]; + + +/* Form the celestial-to-intermediate matrix for this TT. */ + eraC2ixy(tta, ttb, x, y, rc2i); + +/* Predict the Earth rotation angle for this UT1. */ + era = eraEra00(uta, utb); + +/* Estimate s'. */ + sp = eraSp00(tta, ttb); + +/* Form the polar motion matrix. */ + eraPom00(xp, yp, sp, rpom); + +/* Combine to form the celestial-to-terrestrial matrix. */ + eraC2tcio(rc2i, era, rpom, rc2t); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/cal2jd.c b/erfa/cal2jd.c new file mode 100644 index 0000000..e0a3439 --- /dev/null +++ b/erfa/cal2jd.c @@ -0,0 +1,148 @@ +#include "erfa.h" + +int eraCal2jd(int iy, int im, int id, double *djm0, double *djm) +/* +** - - - - - - - - - - +** e r a C a l 2 j d +** - - - - - - - - - - +** +** Gregorian Calendar to Julian Date. +** +** Given: +** iy,im,id int year, month, day in Gregorian calendar (Note 1) +** +** Returned: +** djm0 double MJD zero-point: always 2400000.5 +** djm double Modified Julian Date for 0 hrs +** +** Returned (function value): +** int status: +** 0 = OK +** -1 = bad year (Note 3: JD not computed) +** -2 = bad month (JD not computed) +** -3 = bad day (JD computed) +** +** Notes: +** +** 1) The algorithm used is valid from -4800 March 1, but this +** implementation rejects dates before -4799 January 1. +** +** 2) The Julian Date is returned in two pieces, in the usual ERFA +** manner, which is designed to preserve time resolution. The +** Julian Date is available as a single number by adding djm0 and +** djm. +** +** 3) In early eras the conversion is from the "Proleptic Gregorian +** Calendar"; no account is taken of the date(s) of adoption of +** the Gregorian Calendar, nor is the AD/BC numbering convention +** observed. +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 12.92 (p604). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j, ly, my; + long iypmy; + +/* Earliest year allowed (4800BC) */ + const int IYMIN = -4799; + +/* Month lengths in days */ + static const int mtab[] + = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + +/* Preset status. */ + j = 0; + +/* Validate year and month. */ + if (iy < IYMIN) return -1; + if (im < 1 || im > 12) return -2; + +/* If February in a leap year, 1, otherwise 0. */ + ly = ((im == 2) && !(iy%4) && (iy%100 || !(iy%400))); + +/* Validate day, taking into account leap years. */ + if ( (id < 1) || (id > (mtab[im-1] + ly))) j = -3; + +/* Return result. */ + my = (im - 14) / 12; + iypmy = (long) (iy + my); + *djm0 = ERFA_DJM0; + *djm = (double)((1461L * (iypmy + 4800L)) / 4L + + (367L * (long) (im - 2 - 12 * my)) / 12L + - (3L * ((iypmy + 4900L) / 100L)) / 4L + + (long) id - 2432076L); + +/* Return status. */ + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/cp.c b/erfa/cp.c new file mode 100644 index 0000000..3b9614a --- /dev/null +++ b/erfa/cp.c @@ -0,0 +1,89 @@ +#include "erfa.h" + +void eraCp(double p[3], double c[3]) +/* +** - - - - - - +** e r a C p +** - - - - - - +** +** Copy a p-vector. +** +** Given: +** p double[3] p-vector to be copied +** +** Returned: +** c double[3] copy +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + c[0] = p[0]; + c[1] = p[1]; + c[2] = p[2]; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/cpv.c b/erfa/cpv.c new file mode 100644 index 0000000..8423102 --- /dev/null +++ b/erfa/cpv.c @@ -0,0 +1,91 @@ +#include "erfa.h" + +void eraCpv(double pv[2][3], double c[2][3]) +/* +** - - - - - - - +** e r a C p v +** - - - - - - - +** +** Copy a position/velocity vector. +** +** Given: +** pv double[2][3] position/velocity vector to be copied +** +** Returned: +** c double[2][3] copy +** +** Called: +** eraCp copy p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraCp(pv[0], c[0]); + eraCp(pv[1], c[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/cr.c b/erfa/cr.c new file mode 100644 index 0000000..651bdb5 --- /dev/null +++ b/erfa/cr.c @@ -0,0 +1,92 @@ +#include "erfa.h" + +void eraCr(double r[3][3], double c[3][3]) +/* +** - - - - - - +** e r a C r +** - - - - - - +** +** Copy an r-matrix. +** +** Given: +** r double[3][3] r-matrix to be copied +** +** Returned: +** char[] double[3][3] copy +** +** Called: +** eraCp copy p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraCp(r[0], c[0]); + eraCp(r[1], c[1]); + eraCp(r[2], c[2]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/d2dtf.c b/erfa/d2dtf.c new file mode 100644 index 0000000..d9b8f90 --- /dev/null +++ b/erfa/d2dtf.c @@ -0,0 +1,245 @@ +#include "erfa.h" +#include + +int eraD2dtf(const char *scale, int ndp, double d1, double d2, + int *iy, int *im, int *id, int ihmsf[4]) +/* +** - - - - - - - - - +** e r a D 2 d t f +** - - - - - - - - - +** +** Format for output a 2-part Julian Date (or in the case of UTC a +** quasi-JD form that includes special provision for leap seconds). +** +** Given: +** scale char[] time scale ID (Note 1) +** ndp int resolution (Note 2) +** d1,d2 double time as a 2-part Julian Date (Notes 3,4) +** +** Returned: +** iy,im,id int year, month, day in Gregorian calendar (Note 5) +** ihmsf int[4] hours, minutes, seconds, fraction (Note 1) +** +** Returned (function value): +** int status: +1 = dubious year (Note 5) +** 0 = OK +** -1 = unacceptable date (Note 6) +** +** Notes: +** +** 1) scale identifies the time scale. Only the value "UTC" (in upper +** case) is significant, and enables handling of leap seconds (see +** Note 4). +** +** 2) ndp is the number of decimal places in the seconds field, and can +** have negative as well as positive values, such as: +** +** ndp resolution +** -4 1 00 00 +** -3 0 10 00 +** -2 0 01 00 +** -1 0 00 10 +** 0 0 00 01 +** 1 0 00 00.1 +** 2 0 00 00.01 +** 3 0 00 00.001 +** +** The limits are platform dependent, but a safe range is -5 to +9. +** +** 3) d1+d2 is Julian Date, apportioned in any convenient way between +** the two arguments, for example where d1 is the Julian Day Number +** and d2 is the fraction of a day. In the case of UTC, where the +** use of JD is problematical, special conventions apply: see the +** next note. +** +** 4) JD cannot unambiguously represent UTC during a leap second unless +** special measures are taken. The ERFA internal convention is that +** the quasi-JD day represents UTC days whether the length is 86399, +** 86400 or 86401 SI seconds. In the 1960-1972 era there were +** smaller jumps (in either direction) each time the linear UTC(TAI) +** expression was changed, and these "mini-leaps" are also included +** in the ERFA convention. +** +** 5) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the future +** to be trusted. See eraDat for further details. +** +** 6) For calendar conventions and limitations, see eraCal2jd. +** +** Called: +** eraJd2cal JD to Gregorian calendar +** eraD2tf decompose days to hms +** eraDat delta(AT) = TAI-UTC +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int leap; + char s; + int iy1, im1, id1, js, iy2, im2, id2, ihmsf1[4], i; + double a1, b1, fd, dat0, dat12, w, dat24, dleap; + + +/* The two-part JD. */ + a1 = d1; + b1 = d2; + +/* Provisional calendar date. */ + js = eraJd2cal(a1, b1, &iy1, &im1, &id1, &fd); + if ( js ) return -1; + +/* Is this a leap second day? */ + leap = 0; + if ( ! strcmp(scale,"UTC") ) { + + /* TAI-UTC at 0h today. */ + js = eraDat(iy1, im1, id1, 0.0, &dat0); + if ( js < 0 ) return -1; + + /* TAI-UTC at 12h today (to detect drift). */ + js = eraDat(iy1, im1, id1, 0.5, &dat12); + if ( js < 0 ) return -1; + + /* TAI-UTC at 0h tomorrow (to detect jumps). */ + js = eraJd2cal(a1+1.5, b1-fd, &iy2, &im2, &id2, &w); + if ( js ) return -1; + js = eraDat(iy2, im2, id2, 0.0, &dat24); + if ( js < 0 ) return -1; + + /* Any sudden change in TAI-UTC (seconds). */ + dleap = dat24 - (2.0*dat12 - dat0); + + /* If leap second day, scale the fraction of a day into SI. */ + leap = (dleap != 0.0); + if (leap) fd += fd * dleap/ERFA_DAYSEC; + } + +/* Provisional time of day. */ + eraD2tf ( ndp, fd, &s, ihmsf1 ); + +/* Has the (rounded) time gone past 24h? */ + if ( ihmsf1[0] > 23 ) { + + /* Yes. We probably need tomorrow's calendar date. */ + js = eraJd2cal(a1+1.5, b1-fd, &iy2, &im2, &id2, &w); + if ( js ) return -1; + + /* Is today a leap second day? */ + if ( ! leap ) { + + /* No. Use 0h tomorrow. */ + iy1 = iy2; + im1 = im2; + id1 = id2; + ihmsf1[0] = 0; + ihmsf1[1] = 0; + ihmsf1[2] = 0; + + } else { + + /* Yes. Are we past the leap second itself? */ + if ( ihmsf1[2] > 0 ) { + + /* Yes. Use tomorrow but allow for the leap second. */ + iy1 = iy2; + im1 = im2; + id1 = id2; + ihmsf1[0] = 0; + ihmsf1[1] = 0; + ihmsf1[2] = 0; + + } else { + + /* No. Use 23 59 60... today. */ + ihmsf1[0] = 23; + ihmsf1[1] = 59; + ihmsf1[2] = 60; + } + + /* If rounding to 10s or coarser always go up to new day. */ + if ( ndp < 0 && ihmsf1[2] == 60 ) { + iy1 = iy2; + im1 = im2; + id1 = id2; + ihmsf1[0] = 0; + ihmsf1[1] = 0; + ihmsf1[2] = 0; + } + } + } + +/* Results. */ + *iy = iy1; + *im = im1; + *id = id1; + for ( i = 0; i < 4; i++ ) { + ihmsf[i] = ihmsf1[i]; + } + +/* Status. */ + return js; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/d2tf.c b/erfa/d2tf.c new file mode 100644 index 0000000..132dfe9 --- /dev/null +++ b/erfa/d2tf.c @@ -0,0 +1,169 @@ +#include "erfa.h" + +void eraD2tf(int ndp, double days, char *sign, int ihmsf[4]) +/* +** - - - - - - - - +** e r a D 2 t f +** - - - - - - - - +** +** Decompose days to hours, minutes, seconds, fraction. +** +** Given: +** ndp int resolution (Note 1) +** days double interval in days +** +** Returned: +** sign char '+' or '-' +** ihmsf int[4] hours, minutes, seconds, fraction +** +** Notes: +** +** 1) The argument ndp is interpreted as follows: +** +** ndp resolution +** : ...0000 00 00 +** -7 1000 00 00 +** -6 100 00 00 +** -5 10 00 00 +** -4 1 00 00 +** -3 0 10 00 +** -2 0 01 00 +** -1 0 00 10 +** 0 0 00 01 +** 1 0 00 00.1 +** 2 0 00 00.01 +** 3 0 00 00.001 +** : 0 00 00.000... +** +** 2) The largest positive useful value for ndp is determined by the +** size of days, the format of double on the target platform, and +** the risk of overflowing ihmsf[3]. On a typical platform, for +** days up to 1.0, the available floating-point precision might +** correspond to ndp=12. However, the practical limit is typically +** ndp=9, set by the capacity of a 32-bit int, or ndp=4 if int is +** only 16 bits. +** +** 3) The absolute value of days may exceed 1.0. In cases where it +** does not, it is up to the caller to test for and handle the +** case where days is very nearly 1.0 and rounds up to 24 hours, +** by testing for ihmsf[0]=24 and setting ihmsf[0-3] to zero. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int nrs, n; + double rs, rm, rh, a, w, ah, am, as, af; + + +/* Handle sign. */ + *sign = (char) ( ( days >= 0.0 ) ? '+' : '-' ); + +/* Interval in seconds. */ + a = ERFA_DAYSEC * fabs(days); + +/* Pre-round if resolution coarser than 1s (then pretend ndp=1). */ + if (ndp < 0) { + nrs = 1; + for (n = 1; n <= -ndp; n++) { + nrs *= (n == 2 || n == 4) ? 6 : 10; + } + rs = (double) nrs; + w = a / rs; + a = rs * ERFA_DNINT(w); + } + +/* Express the unit of each field in resolution units. */ + nrs = 1; + for (n = 1; n <= ndp; n++) { + nrs *= 10; + } + rs = (double) nrs; + rm = rs * 60.0; + rh = rm * 60.0; + +/* Round the interval and express in resolution units. */ + a = ERFA_DNINT(rs * a); + +/* Break into fields. */ + ah = a / rh; + ah = ERFA_DINT(ah); + a -= ah * rh; + am = a / rm; + am = ERFA_DINT(am); + a -= am * rm; + as = a / rs; + as = ERFA_DINT(as); + af = a - as * rs; + +/* Return results. */ + ihmsf[0] = (int) ah; + ihmsf[1] = (int) am; + ihmsf[2] = (int) as; + ihmsf[3] = (int) af; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/dat.c b/erfa/dat.c new file mode 100644 index 0000000..e97cc7b --- /dev/null +++ b/erfa/dat.c @@ -0,0 +1,298 @@ +#include "erfa.h" + +int eraDat(int iy, int im, int id, double fd, double *deltat ) +/* +** - - - - - - - +** e r a D a t +** - - - - - - - +** +** For a given UTC date, calculate delta(AT) = TAI-UTC. +** +** :------------------------------------------: +** : : +** : IMPORTANT : +** : : +** : A new version of this function must be : +** : produced whenever a new leap second is : +** : announced. There are four items to : +** : change on each such occasion: : +** : : +** : 1) A new line must be added to the set : +** : of statements that initialize the : +** : array "changes". : +** : : +** : 2) The constant IYV must be set to the : +** : current year. : +** : : +** : 3) The "Latest leap second" comment : +** : below must be set to the new leap : +** : second date. : +** : : +** : 4) The "This revision" comment, later, : +** : must be set to the current date. : +** : : +** : Change (2) must also be carried out : +** : whenever the function is re-issued, : +** : even if no leap seconds have been : +** : added. : +** : : +** : Latest leap second: 2012 June 30 : +** : : +** :__________________________________________: +** +** Given: +** iy int UTC: year (Notes 1 and 2) +** im int month (Note 2) +** id int day (Notes 2 and 3) +** fd double fraction of day (Note 4) +** +** Returned: +** deltat double TAI minus UTC, seconds +** +** Returned (function value): +** int status (Note 5): +** 1 = dubious year (Note 1) +** 0 = OK +** -1 = bad year +** -2 = bad month +** -3 = bad day (Note 3) +** -4 = bad fraction (Note 4) +** +** Notes: +** +** 1) UTC began at 1960 January 1.0 (JD 2436934.5) and it is improper +** to call the function with an earlier date. If this is attempted, +** zero is returned together with a warning status. +** +** Because leap seconds cannot, in principle, be predicted in +** advance, a reliable check for dates beyond the valid range is +** impossible. To guard against gross errors, a year five or more +** after the release year of the present function (see the constant +** IYV) is considered dubious. In this case a warning status is +** returned but the result is computed in the normal way. +** +** For both too-early and too-late years, the warning status is +1. +** This is distinct from the error status -1, which signifies a year +** so early that JD could not be computed. +** +** 2) If the specified date is for a day which ends with a leap second, +** the UTC-TAI value returned is for the period leading up to the +** leap second. If the date is for a day which begins as a leap +** second ends, the UTC-TAI returned is for the period following the +** leap second. +** +** 3) The day number must be in the normal calendar range, for example +** 1 through 30 for April. The "almanac" convention of allowing +** such dates as January 0 and December 32 is not supported in this +** function, in order to avoid confusion near leap seconds. +** +** 4) The fraction of day is used only for dates before the +** introduction of leap seconds, the first of which occurred at the +** end of 1971. It is tested for validity (0 to 1 is the valid +** range) even if not used; if invalid, zero is used and status -4 +** is returned. For many applications, setting fd to zero is +** acceptable; the resulting error is always less than 3 ms (and +** occurs only pre-1972). +** +** 5) The status value returned in the case where there are multiple +** errors refers to the first error detected. For example, if the +** month and day are 13 and 32 respectively, status -2 (bad month) +** will be returned. +** +** 6) In cases where a valid result is not available, zero is returned. +** +** References: +** +** 1) For dates from 1961 January 1 onwards, the expressions from the +** file ftp://maia.usno.navy.mil/ser7/tai-utc.dat are used. +** +** 2) The 5ms timestep at 1961 January 1 is taken from 2.58.1 (p87) of +** the 1992 Explanatory Supplement. +** +** Called: +** eraCal2jd Gregorian calendar to JD +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Release year for this version of eraDat */ + enum { IYV = 2013}; + +/* Reference dates (MJD) and drift rates (s/day), pre leap seconds */ + static const double drift[][2] = { + { 37300.0, 0.0012960 }, + { 37300.0, 0.0012960 }, + { 37300.0, 0.0012960 }, + { 37665.0, 0.0011232 }, + { 37665.0, 0.0011232 }, + { 38761.0, 0.0012960 }, + { 38761.0, 0.0012960 }, + { 38761.0, 0.0012960 }, + { 38761.0, 0.0012960 }, + { 38761.0, 0.0012960 }, + { 38761.0, 0.0012960 }, + { 38761.0, 0.0012960 }, + { 39126.0, 0.0025920 }, + { 39126.0, 0.0025920 } + }; + +/* Number of Delta(AT) expressions before leap seconds were introduced */ + enum { NERA1 = (int) (sizeof drift / sizeof (double) / 2) }; + +/* Dates and Delta(AT)s */ + static const struct { + int iyear, month; + double delat; + } changes[] = { + { 1960, 1, 1.4178180 }, + { 1961, 1, 1.4228180 }, + { 1961, 8, 1.3728180 }, + { 1962, 1, 1.8458580 }, + { 1963, 11, 1.9458580 }, + { 1964, 1, 3.2401300 }, + { 1964, 4, 3.3401300 }, + { 1964, 9, 3.4401300 }, + { 1965, 1, 3.5401300 }, + { 1965, 3, 3.6401300 }, + { 1965, 7, 3.7401300 }, + { 1965, 9, 3.8401300 }, + { 1966, 1, 4.3131700 }, + { 1968, 2, 4.2131700 }, + { 1972, 1, 10.0 }, + { 1972, 7, 11.0 }, + { 1973, 1, 12.0 }, + { 1974, 1, 13.0 }, + { 1975, 1, 14.0 }, + { 1976, 1, 15.0 }, + { 1977, 1, 16.0 }, + { 1978, 1, 17.0 }, + { 1979, 1, 18.0 }, + { 1980, 1, 19.0 }, + { 1981, 7, 20.0 }, + { 1982, 7, 21.0 }, + { 1983, 7, 22.0 }, + { 1985, 7, 23.0 }, + { 1988, 1, 24.0 }, + { 1990, 1, 25.0 }, + { 1991, 1, 26.0 }, + { 1992, 7, 27.0 }, + { 1993, 7, 28.0 }, + { 1994, 7, 29.0 }, + { 1996, 1, 30.0 }, + { 1997, 7, 31.0 }, + { 1999, 1, 32.0 }, + { 2006, 1, 33.0 }, + { 2009, 1, 34.0 }, + { 2012, 7, 35.0 } + }; + +/* Number of Delta(AT) changes */ + const int NDAT = sizeof changes / sizeof changes[0]; + +/* Miscellaneous local variables */ + int j, i, m; + double da, djm0, djm; + + +/* Initialize the result to zero. */ + *deltat = da = 0.0; + +/* If invalid fraction of a day, set error status and give up. */ + if (fd < 0.0 || fd > 1.0) return -4; + +/* Convert the date into an MJD. */ + j = eraCal2jd(iy, im, id, &djm0, &djm); + +/* If invalid year, month, or day, give up. */ + if (j < 0) return j; + +/* If pre-UTC year, set warning status and give up. */ + if (iy < changes[0].iyear) return 1; + +/* If suspiciously late year, set warning status but proceed. */ + if (iy > IYV + 5) j = 1; + +/* Combine year and month to form a date-ordered integer... */ + m = 12*iy + im; + +/* ...and use it to find the preceding table entry. */ + for (i = NDAT-1; i >=0; i--) { + if (m >= (12 * changes[i].iyear + changes[i].month)) break; + } + +/* Get the Delta(AT). */ + da = changes[i].delat; + +/* If pre-1972, adjust for drift. */ + if (i < NERA1) da += (djm + fd - drift[i][0]) * drift[i][1]; + +/* Return the Delta(AT) value. */ + *deltat = da; + +/* Return the status. */ + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/dtdb.c b/erfa/dtdb.c new file mode 100644 index 0000000..9f387e9 --- /dev/null +++ b/erfa/dtdb.c @@ -0,0 +1,1222 @@ +#include "erfa.h" + +double eraDtdb(double date1, double date2, + double ut, double elong, double u, double v) +/* +** - - - - - - - - +** e r a D t d b +** - - - - - - - - +** +** An approximation to TDB-TT, the difference between barycentric +** dynamical time and terrestrial time, for an observer on the Earth. +** +** The different time scales - proper, coordinate and realized - are +** related to each other: +** +** TAI <- physically realized +** : +** offset <- observed (nominally +32.184s) +** : +** TT <- terrestrial time +** : +** rate adjustment (L_G) <- definition of TT +** : +** TCG <- time scale for GCRS +** : +** "periodic" terms <- eraDtdb is an implementation +** : +** rate adjustment (L_C) <- function of solar-system ephemeris +** : +** TCB <- time scale for BCRS +** : +** rate adjustment (-L_B) <- definition of TDB +** : +** TDB <- TCB scaled to track TT +** : +** "periodic" terms <- -eraDtdb is an approximation +** : +** TT <- terrestrial time +** +** Adopted values for the various constants can be found in the IERS +** Conventions (McCarthy & Petit 2003). +** +** Given: +** date1,date2 double date, TDB (Notes 1-3) +** ut double universal time (UT1, fraction of one day) +** elong double longitude (east positive, radians) +** u double distance from Earth spin axis (km) +** v double distance north of equatorial plane (km) +** +** Returned (function value): +** double TDB-TT (seconds) +** +** Notes: +** +** 1) The date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** Although the date is, formally, barycentric dynamical time (TDB), +** the terrestrial dynamical time (TT) can be used with no practical +** effect on the accuracy of the prediction. +** +** 2) TT can be regarded as a coordinate time that is realized as an +** offset of 32.184s from International Atomic Time, TAI. TT is a +** specific linear transformation of geocentric coordinate time TCG, +** which is the time scale for the Geocentric Celestial Reference +** System, GCRS. +** +** 3) TDB is a coordinate time, and is a specific linear transformation +** of barycentric coordinate time TCB, which is the time scale for +** the Barycentric Celestial Reference System, BCRS. +** +** 4) The difference TCG-TCB depends on the masses and positions of the +** bodies of the solar system and the velocity of the Earth. It is +** dominated by a rate difference, the residual being of a periodic +** character. The latter, which is modeled by the present function, +** comprises a main (annual) sinusoidal term of amplitude +** approximately 0.00166 seconds, plus planetary terms up to about +** 20 microseconds, and lunar and diurnal terms up to 2 microseconds. +** These effects come from the changing transverse Doppler effect +** and gravitational red-shift as the observer (on the Earth's +** surface) experiences variations in speed (with respect to the +** BCRS) and gravitational potential. +** +** 5) TDB can be regarded as the same as TCB but with a rate adjustment +** to keep it close to TT, which is convenient for many applications. +** The history of successive attempts to define TDB is set out in +** Resolution 3 adopted by the IAU General Assembly in 2006, which +** defines a fixed TDB(TCB) transformation that is consistent with +** contemporary solar-system ephemerides. Future ephemerides will +** imply slightly changed transformations between TCG and TCB, which +** could introduce a linear drift between TDB and TT; however, any +** such drift is unlikely to exceed 1 nanosecond per century. +** +** 6) The geocentric TDB-TT model used in the present function is that of +** Fairhead & Bretagnon (1990), in its full form. It was originally +** supplied by Fairhead (private communications with P.T.Wallace, +** 1990) as a Fortran subroutine. The present C function contains an +** adaptation of the Fairhead code. The numerical results are +** essentially unaffected by the changes, the differences with +** respect to the Fairhead & Bretagnon original being at the 1e-20 s +** level. +** +** The topocentric part of the model is from Moyer (1981) and +** Murray (1983), with fundamental arguments adapted from +** Simon et al. 1994. It is an approximation to the expression +** ( v / c ) . ( r / c ), where v is the barycentric velocity of +** the Earth, r is the geocentric position of the observer and +** c is the speed of light. +** +** By supplying zeroes for u and v, the topocentric part of the +** model can be nullified, and the function will return the Fairhead +** & Bretagnon result alone. +** +** 7) During the interval 1950-2050, the absolute accuracy is better +** than +/- 3 nanoseconds relative to time ephemerides obtained by +** direct numerical integrations based on the JPL DE405 solar system +** ephemeris. +** +** 8) It must be stressed that the present function is merely a model, +** and that numerical integration of solar-system ephemerides is the +** definitive method for predicting the relationship between TCG and +** TCB and hence between TT and TDB. +** +** References: +** +** Fairhead, L., & Bretagnon, P., Astron.Astrophys., 229, 240-247 +** (1990). +** +** IAU 2006 Resolution 3. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Moyer, T.D., Cel.Mech., 23, 33 (1981). +** +** Murray, C.A., Vectorial Astrometry, Adam Hilger (1983). +** +** Seidelmann, P.K. et al., Explanatory Supplement to the +** Astronomical Almanac, Chapter 2, University Science Books (1992). +** +** Simon, J.L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G. & Laskar, J., Astron.Astrophys., 282, 663-683 (1994). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, tsol, w, elsun, emsun, d, elj, els, wt, w0, w1, w2, w3, w4, + wf, wj; + int j; + +/* +** ===================== +** Fairhead et al. model +** ===================== +** +** 787 sets of three coefficients. +** +** Each set is +** amplitude (microseconds) +** frequency (radians per Julian millennium since J2000.0) +** phase (radians) +** +** Sets 1-474 are the T**0 terms +** " 475-679 " " T**1 +** " 680-764 " " T**2 +** " 765-784 " " T**3 +** " 785-787 " " T**4 +*/ + + static const double fairhd[787][3] = { + /* 1, 10 */ + { 1656.674564e-6, 6283.075849991, 6.240054195 }, + { 22.417471e-6, 5753.384884897, 4.296977442 }, + { 13.839792e-6, 12566.151699983, 6.196904410 }, + { 4.770086e-6, 529.690965095, 0.444401603 }, + { 4.676740e-6, 6069.776754553, 4.021195093 }, + { 2.256707e-6, 213.299095438, 5.543113262 }, + { 1.694205e-6, -3.523118349, 5.025132748 }, + { 1.554905e-6, 77713.771467920, 5.198467090 }, + { 1.276839e-6, 7860.419392439, 5.988822341 }, + { 1.193379e-6, 5223.693919802, 3.649823730 }, + /* 11, 20 */ + { 1.115322e-6, 3930.209696220, 1.422745069 }, + { 0.794185e-6, 11506.769769794, 2.322313077 }, + { 0.447061e-6, 26.298319800, 3.615796498 }, + { 0.435206e-6, -398.149003408, 4.349338347 }, + { 0.600309e-6, 1577.343542448, 2.678271909 }, + { 0.496817e-6, 6208.294251424, 5.696701824 }, + { 0.486306e-6, 5884.926846583, 0.520007179 }, + { 0.432392e-6, 74.781598567, 2.435898309 }, + { 0.468597e-6, 6244.942814354, 5.866398759 }, + { 0.375510e-6, 5507.553238667, 4.103476804 }, + /* 21, 30 */ + { 0.243085e-6, -775.522611324, 3.651837925 }, + { 0.173435e-6, 18849.227549974, 6.153743485 }, + { 0.230685e-6, 5856.477659115, 4.773852582 }, + { 0.203747e-6, 12036.460734888, 4.333987818 }, + { 0.143935e-6, -796.298006816, 5.957517795 }, + { 0.159080e-6, 10977.078804699, 1.890075226 }, + { 0.119979e-6, 38.133035638, 4.551585768 }, + { 0.118971e-6, 5486.777843175, 1.914547226 }, + { 0.116120e-6, 1059.381930189, 0.873504123 }, + { 0.137927e-6, 11790.629088659, 1.135934669 }, + /* 31, 40 */ + { 0.098358e-6, 2544.314419883, 0.092793886 }, + { 0.101868e-6, -5573.142801634, 5.984503847 }, + { 0.080164e-6, 206.185548437, 2.095377709 }, + { 0.079645e-6, 4694.002954708, 2.949233637 }, + { 0.062617e-6, 20.775395492, 2.654394814 }, + { 0.075019e-6, 2942.463423292, 4.980931759 }, + { 0.064397e-6, 5746.271337896, 1.280308748 }, + { 0.063814e-6, 5760.498431898, 4.167901731 }, + { 0.048042e-6, 2146.165416475, 1.495846011 }, + { 0.048373e-6, 155.420399434, 2.251573730 }, + /* 41, 50 */ + { 0.058844e-6, 426.598190876, 4.839650148 }, + { 0.046551e-6, -0.980321068, 0.921573539 }, + { 0.054139e-6, 17260.154654690, 3.411091093 }, + { 0.042411e-6, 6275.962302991, 2.869567043 }, + { 0.040184e-6, -7.113547001, 3.565975565 }, + { 0.036564e-6, 5088.628839767, 3.324679049 }, + { 0.040759e-6, 12352.852604545, 3.981496998 }, + { 0.036507e-6, 801.820931124, 6.248866009 }, + { 0.036955e-6, 3154.687084896, 5.071801441 }, + { 0.042732e-6, 632.783739313, 5.720622217 }, + /* 51, 60 */ + { 0.042560e-6, 161000.685737473, 1.270837679 }, + { 0.040480e-6, 15720.838784878, 2.546610123 }, + { 0.028244e-6, -6286.598968340, 5.069663519 }, + { 0.033477e-6, 6062.663207553, 4.144987272 }, + { 0.034867e-6, 522.577418094, 5.210064075 }, + { 0.032438e-6, 6076.890301554, 0.749317412 }, + { 0.030215e-6, 7084.896781115, 3.389610345 }, + { 0.029247e-6, -71430.695617928, 4.183178762 }, + { 0.033529e-6, 9437.762934887, 2.404714239 }, + { 0.032423e-6, 8827.390269875, 5.541473556 }, + /* 61, 70 */ + { 0.027567e-6, 6279.552731642, 5.040846034 }, + { 0.029862e-6, 12139.553509107, 1.770181024 }, + { 0.022509e-6, 10447.387839604, 1.460726241 }, + { 0.020937e-6, 8429.241266467, 0.652303414 }, + { 0.020322e-6, 419.484643875, 3.735430632 }, + { 0.024816e-6, -1194.447010225, 1.087136918 }, + { 0.025196e-6, 1748.016413067, 2.901883301 }, + { 0.021691e-6, 14143.495242431, 5.952658009 }, + { 0.017673e-6, 6812.766815086, 3.186129845 }, + { 0.022567e-6, 6133.512652857, 3.307984806 }, + /* 71, 80 */ + { 0.016155e-6, 10213.285546211, 1.331103168 }, + { 0.014751e-6, 1349.867409659, 4.308933301 }, + { 0.015949e-6, -220.412642439, 4.005298270 }, + { 0.015974e-6, -2352.866153772, 6.145309371 }, + { 0.014223e-6, 17789.845619785, 2.104551349 }, + { 0.017806e-6, 73.297125859, 3.475975097 }, + { 0.013671e-6, -536.804512095, 5.971672571 }, + { 0.011942e-6, 8031.092263058, 2.053414715 }, + { 0.014318e-6, 16730.463689596, 3.016058075 }, + { 0.012462e-6, 103.092774219, 1.737438797 }, + /* 81, 90 */ + { 0.010962e-6, 3.590428652, 2.196567739 }, + { 0.015078e-6, 19651.048481098, 3.969480770 }, + { 0.010396e-6, 951.718406251, 5.717799605 }, + { 0.011707e-6, -4705.732307544, 2.654125618 }, + { 0.010453e-6, 5863.591206116, 1.913704550 }, + { 0.012420e-6, 4690.479836359, 4.734090399 }, + { 0.011847e-6, 5643.178563677, 5.489005403 }, + { 0.008610e-6, 3340.612426700, 3.661698944 }, + { 0.011622e-6, 5120.601145584, 4.863931876 }, + { 0.010825e-6, 553.569402842, 0.842715011 }, + /* 91, 100 */ + { 0.008666e-6, -135.065080035, 3.293406547 }, + { 0.009963e-6, 149.563197135, 4.870690598 }, + { 0.009858e-6, 6309.374169791, 1.061816410 }, + { 0.007959e-6, 316.391869657, 2.465042647 }, + { 0.010099e-6, 283.859318865, 1.942176992 }, + { 0.007147e-6, -242.728603974, 3.661486981 }, + { 0.007505e-6, 5230.807466803, 4.920937029 }, + { 0.008323e-6, 11769.853693166, 1.229392026 }, + { 0.007490e-6, -6256.777530192, 3.658444681 }, + { 0.009370e-6, 149854.400134205, 0.673880395 }, + /* 101, 110 */ + { 0.007117e-6, 38.027672636, 5.294249518 }, + { 0.007857e-6, 12168.002696575, 0.525733528 }, + { 0.007019e-6, 6206.809778716, 0.837688810 }, + { 0.006056e-6, 955.599741609, 4.194535082 }, + { 0.008107e-6, 13367.972631107, 3.793235253 }, + { 0.006731e-6, 5650.292110678, 5.639906583 }, + { 0.007332e-6, 36.648562930, 0.114858677 }, + { 0.006366e-6, 4164.311989613, 2.262081818 }, + { 0.006858e-6, 5216.580372801, 0.642063318 }, + { 0.006919e-6, 6681.224853400, 6.018501522 }, + /* 111, 120 */ + { 0.006826e-6, 7632.943259650, 3.458654112 }, + { 0.005308e-6, -1592.596013633, 2.500382359 }, + { 0.005096e-6, 11371.704689758, 2.547107806 }, + { 0.004841e-6, 5333.900241022, 0.437078094 }, + { 0.005582e-6, 5966.683980335, 2.246174308 }, + { 0.006304e-6, 11926.254413669, 2.512929171 }, + { 0.006603e-6, 23581.258177318, 5.393136889 }, + { 0.005123e-6, -1.484472708, 2.999641028 }, + { 0.004648e-6, 1589.072895284, 1.275847090 }, + { 0.005119e-6, 6438.496249426, 1.486539246 }, + /* 121, 130 */ + { 0.004521e-6, 4292.330832950, 6.140635794 }, + { 0.005680e-6, 23013.539539587, 4.557814849 }, + { 0.005488e-6, -3.455808046, 0.090675389 }, + { 0.004193e-6, 7234.794256242, 4.869091389 }, + { 0.003742e-6, 7238.675591600, 4.691976180 }, + { 0.004148e-6, -110.206321219, 3.016173439 }, + { 0.004553e-6, 11499.656222793, 5.554998314 }, + { 0.004892e-6, 5436.993015240, 1.475415597 }, + { 0.004044e-6, 4732.030627343, 1.398784824 }, + { 0.004164e-6, 12491.370101415, 5.650931916 }, + /* 131, 140 */ + { 0.004349e-6, 11513.883316794, 2.181745369 }, + { 0.003919e-6, 12528.018664345, 5.823319737 }, + { 0.003129e-6, 6836.645252834, 0.003844094 }, + { 0.004080e-6, -7058.598461315, 3.690360123 }, + { 0.003270e-6, 76.266071276, 1.517189902 }, + { 0.002954e-6, 6283.143160294, 4.447203799 }, + { 0.002872e-6, 28.449187468, 1.158692983 }, + { 0.002881e-6, 735.876513532, 0.349250250 }, + { 0.003279e-6, 5849.364112115, 4.893384368 }, + { 0.003625e-6, 6209.778724132, 1.473760578 }, + /* 141, 150 */ + { 0.003074e-6, 949.175608970, 5.185878737 }, + { 0.002775e-6, 9917.696874510, 1.030026325 }, + { 0.002646e-6, 10973.555686350, 3.918259169 }, + { 0.002575e-6, 25132.303399966, 6.109659023 }, + { 0.003500e-6, 263.083923373, 1.892100742 }, + { 0.002740e-6, 18319.536584880, 4.320519510 }, + { 0.002464e-6, 202.253395174, 4.698203059 }, + { 0.002409e-6, 2.542797281, 5.325009315 }, + { 0.003354e-6, -90955.551694697, 1.942656623 }, + { 0.002296e-6, 6496.374945429, 5.061810696 }, + /* 151, 160 */ + { 0.003002e-6, 6172.869528772, 2.797822767 }, + { 0.003202e-6, 27511.467873537, 0.531673101 }, + { 0.002954e-6, -6283.008539689, 4.533471191 }, + { 0.002353e-6, 639.897286314, 3.734548088 }, + { 0.002401e-6, 16200.772724501, 2.605547070 }, + { 0.003053e-6, 233141.314403759, 3.029030662 }, + { 0.003024e-6, 83286.914269554, 2.355556099 }, + { 0.002863e-6, 17298.182327326, 5.240963796 }, + { 0.002103e-6, -7079.373856808, 5.756641637 }, + { 0.002303e-6, 83996.847317911, 2.013686814 }, + /* 161, 170 */ + { 0.002303e-6, 18073.704938650, 1.089100410 }, + { 0.002381e-6, 63.735898303, 0.759188178 }, + { 0.002493e-6, 6386.168624210, 0.645026535 }, + { 0.002366e-6, 3.932153263, 6.215885448 }, + { 0.002169e-6, 11015.106477335, 4.845297676 }, + { 0.002397e-6, 6243.458341645, 3.809290043 }, + { 0.002183e-6, 1162.474704408, 6.179611691 }, + { 0.002353e-6, 6246.427287062, 4.781719760 }, + { 0.002199e-6, -245.831646229, 5.956152284 }, + { 0.001729e-6, 3894.181829542, 1.264976635 }, + /* 171, 180 */ + { 0.001896e-6, -3128.388765096, 4.914231596 }, + { 0.002085e-6, 35.164090221, 1.405158503 }, + { 0.002024e-6, 14712.317116458, 2.752035928 }, + { 0.001737e-6, 6290.189396992, 5.280820144 }, + { 0.002229e-6, 491.557929457, 1.571007057 }, + { 0.001602e-6, 14314.168113050, 4.203664806 }, + { 0.002186e-6, 454.909366527, 1.402101526 }, + { 0.001897e-6, 22483.848574493, 4.167932508 }, + { 0.001825e-6, -3738.761430108, 0.545828785 }, + { 0.001894e-6, 1052.268383188, 5.817167450 }, + /* 181, 190 */ + { 0.001421e-6, 20.355319399, 2.419886601 }, + { 0.001408e-6, 10984.192351700, 2.732084787 }, + { 0.001847e-6, 10873.986030480, 2.903477885 }, + { 0.001391e-6, -8635.942003763, 0.593891500 }, + { 0.001388e-6, -7.046236698, 1.166145902 }, + { 0.001810e-6, -88860.057071188, 0.487355242 }, + { 0.001288e-6, -1990.745017041, 3.913022880 }, + { 0.001297e-6, 23543.230504682, 3.063805171 }, + { 0.001335e-6, -266.607041722, 3.995764039 }, + { 0.001376e-6, 10969.965257698, 5.152914309 }, + /* 191, 200 */ + { 0.001745e-6, 244287.600007027, 3.626395673 }, + { 0.001649e-6, 31441.677569757, 1.952049260 }, + { 0.001416e-6, 9225.539273283, 4.996408389 }, + { 0.001238e-6, 4804.209275927, 5.503379738 }, + { 0.001472e-6, 4590.910180489, 4.164913291 }, + { 0.001169e-6, 6040.347246017, 5.841719038 }, + { 0.001039e-6, 5540.085789459, 2.769753519 }, + { 0.001004e-6, -170.672870619, 0.755008103 }, + { 0.001284e-6, 10575.406682942, 5.306538209 }, + { 0.001278e-6, 71.812653151, 4.713486491 }, + /* 201, 210 */ + { 0.001321e-6, 18209.330263660, 2.624866359 }, + { 0.001297e-6, 21228.392023546, 0.382603541 }, + { 0.000954e-6, 6282.095528923, 0.882213514 }, + { 0.001145e-6, 6058.731054289, 1.169483931 }, + { 0.000979e-6, 5547.199336460, 5.448375984 }, + { 0.000987e-6, -6262.300454499, 2.656486959 }, + { 0.001070e-6, -154717.609887482, 1.827624012 }, + { 0.000991e-6, 4701.116501708, 4.387001801 }, + { 0.001155e-6, -14.227094002, 3.042700750 }, + { 0.001176e-6, 277.034993741, 3.335519004 }, + /* 211, 220 */ + { 0.000890e-6, 13916.019109642, 5.601498297 }, + { 0.000884e-6, -1551.045222648, 1.088831705 }, + { 0.000876e-6, 5017.508371365, 3.969902609 }, + { 0.000806e-6, 15110.466119866, 5.142876744 }, + { 0.000773e-6, -4136.910433516, 0.022067765 }, + { 0.001077e-6, 175.166059800, 1.844913056 }, + { 0.000954e-6, -6284.056171060, 0.968480906 }, + { 0.000737e-6, 5326.786694021, 4.923831588 }, + { 0.000845e-6, -433.711737877, 4.749245231 }, + { 0.000819e-6, 8662.240323563, 5.991247817 }, + /* 221, 230 */ + { 0.000852e-6, 199.072001436, 2.189604979 }, + { 0.000723e-6, 17256.631536341, 6.068719637 }, + { 0.000940e-6, 6037.244203762, 6.197428148 }, + { 0.000885e-6, 11712.955318231, 3.280414875 }, + { 0.000706e-6, 12559.038152982, 2.824848947 }, + { 0.000732e-6, 2379.164473572, 2.501813417 }, + { 0.000764e-6, -6127.655450557, 2.236346329 }, + { 0.000908e-6, 131.541961686, 2.521257490 }, + { 0.000907e-6, 35371.887265976, 3.370195967 }, + { 0.000673e-6, 1066.495477190, 3.876512374 }, + /* 231, 240 */ + { 0.000814e-6, 17654.780539750, 4.627122566 }, + { 0.000630e-6, 36.027866677, 0.156368499 }, + { 0.000798e-6, 515.463871093, 5.151962502 }, + { 0.000798e-6, 148.078724426, 5.909225055 }, + { 0.000806e-6, 309.278322656, 6.054064447 }, + { 0.000607e-6, -39.617508346, 2.839021623 }, + { 0.000601e-6, 412.371096874, 3.984225404 }, + { 0.000646e-6, 11403.676995575, 3.852959484 }, + { 0.000704e-6, 13521.751441591, 2.300991267 }, + { 0.000603e-6, -65147.619767937, 4.140083146 }, + /* 241, 250 */ + { 0.000609e-6, 10177.257679534, 0.437122327 }, + { 0.000631e-6, 5767.611978898, 4.026532329 }, + { 0.000576e-6, 11087.285125918, 4.760293101 }, + { 0.000674e-6, 14945.316173554, 6.270510511 }, + { 0.000726e-6, 5429.879468239, 6.039606892 }, + { 0.000710e-6, 28766.924424484, 5.672617711 }, + { 0.000647e-6, 11856.218651625, 3.397132627 }, + { 0.000678e-6, -5481.254918868, 6.249666675 }, + { 0.000618e-6, 22003.914634870, 2.466427018 }, + { 0.000738e-6, 6134.997125565, 2.242668890 }, + /* 251, 260 */ + { 0.000660e-6, 625.670192312, 5.864091907 }, + { 0.000694e-6, 3496.032826134, 2.668309141 }, + { 0.000531e-6, 6489.261398429, 1.681888780 }, + { 0.000611e-6, -143571.324284214, 2.424978312 }, + { 0.000575e-6, 12043.574281889, 4.216492400 }, + { 0.000553e-6, 12416.588502848, 4.772158039 }, + { 0.000689e-6, 4686.889407707, 6.224271088 }, + { 0.000495e-6, 7342.457780181, 3.817285811 }, + { 0.000567e-6, 3634.621024518, 1.649264690 }, + { 0.000515e-6, 18635.928454536, 3.945345892 }, + /* 261, 270 */ + { 0.000486e-6, -323.505416657, 4.061673868 }, + { 0.000662e-6, 25158.601719765, 1.794058369 }, + { 0.000509e-6, 846.082834751, 3.053874588 }, + { 0.000472e-6, -12569.674818332, 5.112133338 }, + { 0.000461e-6, 6179.983075773, 0.513669325 }, + { 0.000641e-6, 83467.156352816, 3.210727723 }, + { 0.000520e-6, 10344.295065386, 2.445597761 }, + { 0.000493e-6, 18422.629359098, 1.676939306 }, + { 0.000478e-6, 1265.567478626, 5.487314569 }, + { 0.000472e-6, -18.159247265, 1.999707589 }, + /* 271, 280 */ + { 0.000559e-6, 11190.377900137, 5.783236356 }, + { 0.000494e-6, 9623.688276691, 3.022645053 }, + { 0.000463e-6, 5739.157790895, 1.411223013 }, + { 0.000432e-6, 16858.482532933, 1.179256434 }, + { 0.000574e-6, 72140.628666286, 1.758191830 }, + { 0.000484e-6, 17267.268201691, 3.290589143 }, + { 0.000550e-6, 4907.302050146, 0.864024298 }, + { 0.000399e-6, 14.977853527, 2.094441910 }, + { 0.000491e-6, 224.344795702, 0.878372791 }, + { 0.000432e-6, 20426.571092422, 6.003829241 }, + /* 281, 290 */ + { 0.000481e-6, 5749.452731634, 4.309591964 }, + { 0.000480e-6, 5757.317038160, 1.142348571 }, + { 0.000485e-6, 6702.560493867, 0.210580917 }, + { 0.000426e-6, 6055.549660552, 4.274476529 }, + { 0.000480e-6, 5959.570433334, 5.031351030 }, + { 0.000466e-6, 12562.628581634, 4.959581597 }, + { 0.000520e-6, 39302.096962196, 4.788002889 }, + { 0.000458e-6, 12132.439962106, 1.880103788 }, + { 0.000470e-6, 12029.347187887, 1.405611197 }, + { 0.000416e-6, -7477.522860216, 1.082356330 }, + /* 291, 300 */ + { 0.000449e-6, 11609.862544012, 4.179989585 }, + { 0.000465e-6, 17253.041107690, 0.353496295 }, + { 0.000362e-6, -4535.059436924, 1.583849576 }, + { 0.000383e-6, 21954.157609398, 3.747376371 }, + { 0.000389e-6, 17.252277143, 1.395753179 }, + { 0.000331e-6, 18052.929543158, 0.566790582 }, + { 0.000430e-6, 13517.870106233, 0.685827538 }, + { 0.000368e-6, -5756.908003246, 0.731374317 }, + { 0.000330e-6, 10557.594160824, 3.710043680 }, + { 0.000332e-6, 20199.094959633, 1.652901407 }, + /* 301, 310 */ + { 0.000384e-6, 11933.367960670, 5.827781531 }, + { 0.000387e-6, 10454.501386605, 2.541182564 }, + { 0.000325e-6, 15671.081759407, 2.178850542 }, + { 0.000318e-6, 138.517496871, 2.253253037 }, + { 0.000305e-6, 9388.005909415, 0.578340206 }, + { 0.000352e-6, 5749.861766548, 3.000297967 }, + { 0.000311e-6, 6915.859589305, 1.693574249 }, + { 0.000297e-6, 24072.921469776, 1.997249392 }, + { 0.000363e-6, -640.877607382, 5.071820966 }, + { 0.000323e-6, 12592.450019783, 1.072262823 }, + /* 311, 320 */ + { 0.000341e-6, 12146.667056108, 4.700657997 }, + { 0.000290e-6, 9779.108676125, 1.812320441 }, + { 0.000342e-6, 6132.028180148, 4.322238614 }, + { 0.000329e-6, 6268.848755990, 3.033827743 }, + { 0.000374e-6, 17996.031168222, 3.388716544 }, + { 0.000285e-6, -533.214083444, 4.687313233 }, + { 0.000338e-6, 6065.844601290, 0.877776108 }, + { 0.000276e-6, 24.298513841, 0.770299429 }, + { 0.000336e-6, -2388.894020449, 5.353796034 }, + { 0.000290e-6, 3097.883822726, 4.075291557 }, + /* 321, 330 */ + { 0.000318e-6, 709.933048357, 5.941207518 }, + { 0.000271e-6, 13095.842665077, 3.208912203 }, + { 0.000331e-6, 6073.708907816, 4.007881169 }, + { 0.000292e-6, 742.990060533, 2.714333592 }, + { 0.000362e-6, 29088.811415985, 3.215977013 }, + { 0.000280e-6, 12359.966151546, 0.710872502 }, + { 0.000267e-6, 10440.274292604, 4.730108488 }, + { 0.000262e-6, 838.969287750, 1.327720272 }, + { 0.000250e-6, 16496.361396202, 0.898769761 }, + { 0.000325e-6, 20597.243963041, 0.180044365 }, + /* 331, 340 */ + { 0.000268e-6, 6148.010769956, 5.152666276 }, + { 0.000284e-6, 5636.065016677, 5.655385808 }, + { 0.000301e-6, 6080.822454817, 2.135396205 }, + { 0.000294e-6, -377.373607916, 3.708784168 }, + { 0.000236e-6, 2118.763860378, 1.733578756 }, + { 0.000234e-6, 5867.523359379, 5.575209112 }, + { 0.000268e-6, -226858.238553767, 0.069432392 }, + { 0.000265e-6, 167283.761587465, 4.369302826 }, + { 0.000280e-6, 28237.233459389, 5.304829118 }, + { 0.000292e-6, 12345.739057544, 4.096094132 }, + /* 341, 350 */ + { 0.000223e-6, 19800.945956225, 3.069327406 }, + { 0.000301e-6, 43232.306658416, 6.205311188 }, + { 0.000264e-6, 18875.525869774, 1.417263408 }, + { 0.000304e-6, -1823.175188677, 3.409035232 }, + { 0.000301e-6, 109.945688789, 0.510922054 }, + { 0.000260e-6, 813.550283960, 2.389438934 }, + { 0.000299e-6, 316428.228673312, 5.384595078 }, + { 0.000211e-6, 5756.566278634, 3.789392838 }, + { 0.000209e-6, 5750.203491159, 1.661943545 }, + { 0.000240e-6, 12489.885628707, 5.684549045 }, + /* 351, 360 */ + { 0.000216e-6, 6303.851245484, 3.862942261 }, + { 0.000203e-6, 1581.959348283, 5.549853589 }, + { 0.000200e-6, 5642.198242609, 1.016115785 }, + { 0.000197e-6, -70.849445304, 4.690702525 }, + { 0.000227e-6, 6287.008003254, 2.911891613 }, + { 0.000197e-6, 533.623118358, 1.048982898 }, + { 0.000205e-6, -6279.485421340, 1.829362730 }, + { 0.000209e-6, -10988.808157535, 2.636140084 }, + { 0.000208e-6, -227.526189440, 4.127883842 }, + { 0.000191e-6, 415.552490612, 4.401165650 }, + /* 361, 370 */ + { 0.000190e-6, 29296.615389579, 4.175658539 }, + { 0.000264e-6, 66567.485864652, 4.601102551 }, + { 0.000256e-6, -3646.350377354, 0.506364778 }, + { 0.000188e-6, 13119.721102825, 2.032195842 }, + { 0.000185e-6, -209.366942175, 4.694756586 }, + { 0.000198e-6, 25934.124331089, 3.832703118 }, + { 0.000195e-6, 4061.219215394, 3.308463427 }, + { 0.000234e-6, 5113.487598583, 1.716090661 }, + { 0.000188e-6, 1478.866574064, 5.686865780 }, + { 0.000222e-6, 11823.161639450, 1.942386641 }, + /* 371, 380 */ + { 0.000181e-6, 10770.893256262, 1.999482059 }, + { 0.000171e-6, 6546.159773364, 1.182807992 }, + { 0.000206e-6, 70.328180442, 5.934076062 }, + { 0.000169e-6, 20995.392966449, 2.169080622 }, + { 0.000191e-6, 10660.686935042, 5.405515999 }, + { 0.000228e-6, 33019.021112205, 4.656985514 }, + { 0.000184e-6, -4933.208440333, 3.327476868 }, + { 0.000220e-6, -135.625325010, 1.765430262 }, + { 0.000166e-6, 23141.558382925, 3.454132746 }, + { 0.000191e-6, 6144.558353121, 5.020393445 }, + /* 381, 390 */ + { 0.000180e-6, 6084.003848555, 0.602182191 }, + { 0.000163e-6, 17782.732072784, 4.960593133 }, + { 0.000225e-6, 16460.333529525, 2.596451817 }, + { 0.000222e-6, 5905.702242076, 3.731990323 }, + { 0.000204e-6, 227.476132789, 5.636192701 }, + { 0.000159e-6, 16737.577236597, 3.600691544 }, + { 0.000200e-6, 6805.653268085, 0.868220961 }, + { 0.000187e-6, 11919.140866668, 2.629456641 }, + { 0.000161e-6, 127.471796607, 2.862574720 }, + { 0.000205e-6, 6286.666278643, 1.742882331 }, + /* 391, 400 */ + { 0.000189e-6, 153.778810485, 4.812372643 }, + { 0.000168e-6, 16723.350142595, 0.027860588 }, + { 0.000149e-6, 11720.068865232, 0.659721876 }, + { 0.000189e-6, 5237.921013804, 5.245313000 }, + { 0.000143e-6, 6709.674040867, 4.317625647 }, + { 0.000146e-6, 4487.817406270, 4.815297007 }, + { 0.000144e-6, -664.756045130, 5.381366880 }, + { 0.000175e-6, 5127.714692584, 4.728443327 }, + { 0.000162e-6, 6254.626662524, 1.435132069 }, + { 0.000187e-6, 47162.516354635, 1.354371923 }, + /* 401, 410 */ + { 0.000146e-6, 11080.171578918, 3.369695406 }, + { 0.000180e-6, -348.924420448, 2.490902145 }, + { 0.000148e-6, 151.047669843, 3.799109588 }, + { 0.000157e-6, 6197.248551160, 1.284375887 }, + { 0.000167e-6, 146.594251718, 0.759969109 }, + { 0.000133e-6, -5331.357443741, 5.409701889 }, + { 0.000154e-6, 95.979227218, 3.366890614 }, + { 0.000148e-6, -6418.140930027, 3.384104996 }, + { 0.000128e-6, -6525.804453965, 3.803419985 }, + { 0.000130e-6, 11293.470674356, 0.939039445 }, + /* 411, 420 */ + { 0.000152e-6, -5729.506447149, 0.734117523 }, + { 0.000138e-6, 210.117701700, 2.564216078 }, + { 0.000123e-6, 6066.595360816, 4.517099537 }, + { 0.000140e-6, 18451.078546566, 0.642049130 }, + { 0.000126e-6, 11300.584221356, 3.485280663 }, + { 0.000119e-6, 10027.903195729, 3.217431161 }, + { 0.000151e-6, 4274.518310832, 4.404359108 }, + { 0.000117e-6, 6072.958148291, 0.366324650 }, + { 0.000165e-6, -7668.637425143, 4.298212528 }, + { 0.000117e-6, -6245.048177356, 5.379518958 }, + /* 421, 430 */ + { 0.000130e-6, -5888.449964932, 4.527681115 }, + { 0.000121e-6, -543.918059096, 6.109429504 }, + { 0.000162e-6, 9683.594581116, 5.720092446 }, + { 0.000141e-6, 6219.339951688, 0.679068671 }, + { 0.000118e-6, 22743.409379516, 4.881123092 }, + { 0.000129e-6, 1692.165669502, 0.351407289 }, + { 0.000126e-6, 5657.405657679, 5.146592349 }, + { 0.000114e-6, 728.762966531, 0.520791814 }, + { 0.000120e-6, 52.596639600, 0.948516300 }, + { 0.000115e-6, 65.220371012, 3.504914846 }, + /* 431, 440 */ + { 0.000126e-6, 5881.403728234, 5.577502482 }, + { 0.000158e-6, 163096.180360983, 2.957128968 }, + { 0.000134e-6, 12341.806904281, 2.598576764 }, + { 0.000151e-6, 16627.370915377, 3.985702050 }, + { 0.000109e-6, 1368.660252845, 0.014730471 }, + { 0.000131e-6, 6211.263196841, 0.085077024 }, + { 0.000146e-6, 5792.741760812, 0.708426604 }, + { 0.000146e-6, -77.750543984, 3.121576600 }, + { 0.000107e-6, 5341.013788022, 0.288231904 }, + { 0.000138e-6, 6281.591377283, 2.797450317 }, + /* 441, 450 */ + { 0.000113e-6, -6277.552925684, 2.788904128 }, + { 0.000115e-6, -525.758811831, 5.895222200 }, + { 0.000138e-6, 6016.468808270, 6.096188999 }, + { 0.000139e-6, 23539.707386333, 2.028195445 }, + { 0.000146e-6, -4176.041342449, 4.660008502 }, + { 0.000107e-6, 16062.184526117, 4.066520001 }, + { 0.000142e-6, 83783.548222473, 2.936315115 }, + { 0.000128e-6, 9380.959672717, 3.223844306 }, + { 0.000135e-6, 6205.325306007, 1.638054048 }, + { 0.000101e-6, 2699.734819318, 5.481603249 }, + /* 451, 460 */ + { 0.000104e-6, -568.821874027, 2.205734493 }, + { 0.000103e-6, 6321.103522627, 2.440421099 }, + { 0.000119e-6, 6321.208885629, 2.547496264 }, + { 0.000138e-6, 1975.492545856, 2.314608466 }, + { 0.000121e-6, 137.033024162, 4.539108237 }, + { 0.000123e-6, 19402.796952817, 4.538074405 }, + { 0.000119e-6, 22805.735565994, 2.869040566 }, + { 0.000133e-6, 64471.991241142, 6.056405489 }, + { 0.000129e-6, -85.827298831, 2.540635083 }, + { 0.000131e-6, 13613.804277336, 4.005732868 }, + /* 461, 470 */ + { 0.000104e-6, 9814.604100291, 1.959967212 }, + { 0.000112e-6, 16097.679950283, 3.589026260 }, + { 0.000123e-6, 2107.034507542, 1.728627253 }, + { 0.000121e-6, 36949.230808424, 6.072332087 }, + { 0.000108e-6, -12539.853380183, 3.716133846 }, + { 0.000113e-6, -7875.671863624, 2.725771122 }, + { 0.000109e-6, 4171.425536614, 4.033338079 }, + { 0.000101e-6, 6247.911759770, 3.441347021 }, + { 0.000113e-6, 7330.728427345, 0.656372122 }, + { 0.000113e-6, 51092.726050855, 2.791483066 }, + /* 471, 480 */ + { 0.000106e-6, 5621.842923210, 1.815323326 }, + { 0.000101e-6, 111.430161497, 5.711033677 }, + { 0.000103e-6, 909.818733055, 2.812745443 }, + { 0.000101e-6, 1790.642637886, 1.965746028 }, + + /* T */ + { 102.156724e-6, 6283.075849991, 4.249032005 }, + { 1.706807e-6, 12566.151699983, 4.205904248 }, + { 0.269668e-6, 213.299095438, 3.400290479 }, + { 0.265919e-6, 529.690965095, 5.836047367 }, + { 0.210568e-6, -3.523118349, 6.262738348 }, + { 0.077996e-6, 5223.693919802, 4.670344204 }, + /* 481, 490 */ + { 0.054764e-6, 1577.343542448, 4.534800170 }, + { 0.059146e-6, 26.298319800, 1.083044735 }, + { 0.034420e-6, -398.149003408, 5.980077351 }, + { 0.032088e-6, 18849.227549974, 4.162913471 }, + { 0.033595e-6, 5507.553238667, 5.980162321 }, + { 0.029198e-6, 5856.477659115, 0.623811863 }, + { 0.027764e-6, 155.420399434, 3.745318113 }, + { 0.025190e-6, 5746.271337896, 2.980330535 }, + { 0.022997e-6, -796.298006816, 1.174411803 }, + { 0.024976e-6, 5760.498431898, 2.467913690 }, + /* 491, 500 */ + { 0.021774e-6, 206.185548437, 3.854787540 }, + { 0.017925e-6, -775.522611324, 1.092065955 }, + { 0.013794e-6, 426.598190876, 2.699831988 }, + { 0.013276e-6, 6062.663207553, 5.845801920 }, + { 0.011774e-6, 12036.460734888, 2.292832062 }, + { 0.012869e-6, 6076.890301554, 5.333425680 }, + { 0.012152e-6, 1059.381930189, 6.222874454 }, + { 0.011081e-6, -7.113547001, 5.154724984 }, + { 0.010143e-6, 4694.002954708, 4.044013795 }, + { 0.009357e-6, 5486.777843175, 3.416081409 }, + /* 501, 510 */ + { 0.010084e-6, 522.577418094, 0.749320262 }, + { 0.008587e-6, 10977.078804699, 2.777152598 }, + { 0.008628e-6, 6275.962302991, 4.562060226 }, + { 0.008158e-6, -220.412642439, 5.806891533 }, + { 0.007746e-6, 2544.314419883, 1.603197066 }, + { 0.007670e-6, 2146.165416475, 3.000200440 }, + { 0.007098e-6, 74.781598567, 0.443725817 }, + { 0.006180e-6, -536.804512095, 1.302642751 }, + { 0.005818e-6, 5088.628839767, 4.827723531 }, + { 0.004945e-6, -6286.598968340, 0.268305170 }, + /* 511, 520 */ + { 0.004774e-6, 1349.867409659, 5.808636673 }, + { 0.004687e-6, -242.728603974, 5.154890570 }, + { 0.006089e-6, 1748.016413067, 4.403765209 }, + { 0.005975e-6, -1194.447010225, 2.583472591 }, + { 0.004229e-6, 951.718406251, 0.931172179 }, + { 0.005264e-6, 553.569402842, 2.336107252 }, + { 0.003049e-6, 5643.178563677, 1.362634430 }, + { 0.002974e-6, 6812.766815086, 1.583012668 }, + { 0.003403e-6, -2352.866153772, 2.552189886 }, + { 0.003030e-6, 419.484643875, 5.286473844 }, + /* 521, 530 */ + { 0.003210e-6, -7.046236698, 1.863796539 }, + { 0.003058e-6, 9437.762934887, 4.226420633 }, + { 0.002589e-6, 12352.852604545, 1.991935820 }, + { 0.002927e-6, 5216.580372801, 2.319951253 }, + { 0.002425e-6, 5230.807466803, 3.084752833 }, + { 0.002656e-6, 3154.687084896, 2.487447866 }, + { 0.002445e-6, 10447.387839604, 2.347139160 }, + { 0.002990e-6, 4690.479836359, 6.235872050 }, + { 0.002890e-6, 5863.591206116, 0.095197563 }, + { 0.002498e-6, 6438.496249426, 2.994779800 }, + /* 531, 540 */ + { 0.001889e-6, 8031.092263058, 3.569003717 }, + { 0.002567e-6, 801.820931124, 3.425611498 }, + { 0.001803e-6, -71430.695617928, 2.192295512 }, + { 0.001782e-6, 3.932153263, 5.180433689 }, + { 0.001694e-6, -4705.732307544, 4.641779174 }, + { 0.001704e-6, -1592.596013633, 3.997097652 }, + { 0.001735e-6, 5849.364112115, 0.417558428 }, + { 0.001643e-6, 8429.241266467, 2.180619584 }, + { 0.001680e-6, 38.133035638, 4.164529426 }, + { 0.002045e-6, 7084.896781115, 0.526323854 }, + /* 541, 550 */ + { 0.001458e-6, 4292.330832950, 1.356098141 }, + { 0.001437e-6, 20.355319399, 3.895439360 }, + { 0.001738e-6, 6279.552731642, 0.087484036 }, + { 0.001367e-6, 14143.495242431, 3.987576591 }, + { 0.001344e-6, 7234.794256242, 0.090454338 }, + { 0.001438e-6, 11499.656222793, 0.974387904 }, + { 0.001257e-6, 6836.645252834, 1.509069366 }, + { 0.001358e-6, 11513.883316794, 0.495572260 }, + { 0.001628e-6, 7632.943259650, 4.968445721 }, + { 0.001169e-6, 103.092774219, 2.838496795 }, + /* 551, 560 */ + { 0.001162e-6, 4164.311989613, 3.408387778 }, + { 0.001092e-6, 6069.776754553, 3.617942651 }, + { 0.001008e-6, 17789.845619785, 0.286350174 }, + { 0.001008e-6, 639.897286314, 1.610762073 }, + { 0.000918e-6, 10213.285546211, 5.532798067 }, + { 0.001011e-6, -6256.777530192, 0.661826484 }, + { 0.000753e-6, 16730.463689596, 3.905030235 }, + { 0.000737e-6, 11926.254413669, 4.641956361 }, + { 0.000694e-6, 3340.612426700, 2.111120332 }, + { 0.000701e-6, 3894.181829542, 2.760823491 }, + /* 561, 570 */ + { 0.000689e-6, -135.065080035, 4.768800780 }, + { 0.000700e-6, 13367.972631107, 5.760439898 }, + { 0.000664e-6, 6040.347246017, 1.051215840 }, + { 0.000654e-6, 5650.292110678, 4.911332503 }, + { 0.000788e-6, 6681.224853400, 4.699648011 }, + { 0.000628e-6, 5333.900241022, 5.024608847 }, + { 0.000755e-6, -110.206321219, 4.370971253 }, + { 0.000628e-6, 6290.189396992, 3.660478857 }, + { 0.000635e-6, 25132.303399966, 4.121051532 }, + { 0.000534e-6, 5966.683980335, 1.173284524 }, + /* 571, 580 */ + { 0.000543e-6, -433.711737877, 0.345585464 }, + { 0.000517e-6, -1990.745017041, 5.414571768 }, + { 0.000504e-6, 5767.611978898, 2.328281115 }, + { 0.000485e-6, 5753.384884897, 1.685874771 }, + { 0.000463e-6, 7860.419392439, 5.297703006 }, + { 0.000604e-6, 515.463871093, 0.591998446 }, + { 0.000443e-6, 12168.002696575, 4.830881244 }, + { 0.000570e-6, 199.072001436, 3.899190272 }, + { 0.000465e-6, 10969.965257698, 0.476681802 }, + { 0.000424e-6, -7079.373856808, 1.112242763 }, + /* 581, 590 */ + { 0.000427e-6, 735.876513532, 1.994214480 }, + { 0.000478e-6, -6127.655450557, 3.778025483 }, + { 0.000414e-6, 10973.555686350, 5.441088327 }, + { 0.000512e-6, 1589.072895284, 0.107123853 }, + { 0.000378e-6, 10984.192351700, 0.915087231 }, + { 0.000402e-6, 11371.704689758, 4.107281715 }, + { 0.000453e-6, 9917.696874510, 1.917490952 }, + { 0.000395e-6, 149.563197135, 2.763124165 }, + { 0.000371e-6, 5739.157790895, 3.112111866 }, + { 0.000350e-6, 11790.629088659, 0.440639857 }, + /* 591, 600 */ + { 0.000356e-6, 6133.512652857, 5.444568842 }, + { 0.000344e-6, 412.371096874, 5.676832684 }, + { 0.000383e-6, 955.599741609, 5.559734846 }, + { 0.000333e-6, 6496.374945429, 0.261537984 }, + { 0.000340e-6, 6055.549660552, 5.975534987 }, + { 0.000334e-6, 1066.495477190, 2.335063907 }, + { 0.000399e-6, 11506.769769794, 5.321230910 }, + { 0.000314e-6, 18319.536584880, 2.313312404 }, + { 0.000424e-6, 1052.268383188, 1.211961766 }, + { 0.000307e-6, 63.735898303, 3.169551388 }, + /* 601, 610 */ + { 0.000329e-6, 29.821438149, 6.106912080 }, + { 0.000357e-6, 6309.374169791, 4.223760346 }, + { 0.000312e-6, -3738.761430108, 2.180556645 }, + { 0.000301e-6, 309.278322656, 1.499984572 }, + { 0.000268e-6, 12043.574281889, 2.447520648 }, + { 0.000257e-6, 12491.370101415, 3.662331761 }, + { 0.000290e-6, 625.670192312, 1.272834584 }, + { 0.000256e-6, 5429.879468239, 1.913426912 }, + { 0.000339e-6, 3496.032826134, 4.165930011 }, + { 0.000283e-6, 3930.209696220, 4.325565754 }, + /* 611, 620 */ + { 0.000241e-6, 12528.018664345, 3.832324536 }, + { 0.000304e-6, 4686.889407707, 1.612348468 }, + { 0.000259e-6, 16200.772724501, 3.470173146 }, + { 0.000238e-6, 12139.553509107, 1.147977842 }, + { 0.000236e-6, 6172.869528772, 3.776271728 }, + { 0.000296e-6, -7058.598461315, 0.460368852 }, + { 0.000306e-6, 10575.406682942, 0.554749016 }, + { 0.000251e-6, 17298.182327326, 0.834332510 }, + { 0.000290e-6, 4732.030627343, 4.759564091 }, + { 0.000261e-6, 5884.926846583, 0.298259862 }, + /* 621, 630 */ + { 0.000249e-6, 5547.199336460, 3.749366406 }, + { 0.000213e-6, 11712.955318231, 5.415666119 }, + { 0.000223e-6, 4701.116501708, 2.703203558 }, + { 0.000268e-6, -640.877607382, 0.283670793 }, + { 0.000209e-6, 5636.065016677, 1.238477199 }, + { 0.000193e-6, 10177.257679534, 1.943251340 }, + { 0.000182e-6, 6283.143160294, 2.456157599 }, + { 0.000184e-6, -227.526189440, 5.888038582 }, + { 0.000182e-6, -6283.008539689, 0.241332086 }, + { 0.000228e-6, -6284.056171060, 2.657323816 }, + /* 631, 640 */ + { 0.000166e-6, 7238.675591600, 5.930629110 }, + { 0.000167e-6, 3097.883822726, 5.570955333 }, + { 0.000159e-6, -323.505416657, 5.786670700 }, + { 0.000154e-6, -4136.910433516, 1.517805532 }, + { 0.000176e-6, 12029.347187887, 3.139266834 }, + { 0.000167e-6, 12132.439962106, 3.556352289 }, + { 0.000153e-6, 202.253395174, 1.463313961 }, + { 0.000157e-6, 17267.268201691, 1.586837396 }, + { 0.000142e-6, 83996.847317911, 0.022670115 }, + { 0.000152e-6, 17260.154654690, 0.708528947 }, + /* 641, 650 */ + { 0.000144e-6, 6084.003848555, 5.187075177 }, + { 0.000135e-6, 5756.566278634, 1.993229262 }, + { 0.000134e-6, 5750.203491159, 3.457197134 }, + { 0.000144e-6, 5326.786694021, 6.066193291 }, + { 0.000160e-6, 11015.106477335, 1.710431974 }, + { 0.000133e-6, 3634.621024518, 2.836451652 }, + { 0.000134e-6, 18073.704938650, 5.453106665 }, + { 0.000134e-6, 1162.474704408, 5.326898811 }, + { 0.000128e-6, 5642.198242609, 2.511652591 }, + { 0.000160e-6, 632.783739313, 5.628785365 }, + /* 651, 660 */ + { 0.000132e-6, 13916.019109642, 0.819294053 }, + { 0.000122e-6, 14314.168113050, 5.677408071 }, + { 0.000125e-6, 12359.966151546, 5.251984735 }, + { 0.000121e-6, 5749.452731634, 2.210924603 }, + { 0.000136e-6, -245.831646229, 1.646502367 }, + { 0.000120e-6, 5757.317038160, 3.240883049 }, + { 0.000134e-6, 12146.667056108, 3.059480037 }, + { 0.000137e-6, 6206.809778716, 1.867105418 }, + { 0.000141e-6, 17253.041107690, 2.069217456 }, + { 0.000129e-6, -7477.522860216, 2.781469314 }, + /* 661, 670 */ + { 0.000116e-6, 5540.085789459, 4.281176991 }, + { 0.000116e-6, 9779.108676125, 3.320925381 }, + { 0.000129e-6, 5237.921013804, 3.497704076 }, + { 0.000113e-6, 5959.570433334, 0.983210840 }, + { 0.000122e-6, 6282.095528923, 2.674938860 }, + { 0.000140e-6, -11.045700264, 4.957936982 }, + { 0.000108e-6, 23543.230504682, 1.390113589 }, + { 0.000106e-6, -12569.674818332, 0.429631317 }, + { 0.000110e-6, -266.607041722, 5.501340197 }, + { 0.000115e-6, 12559.038152982, 4.691456618 }, + /* 671, 680 */ + { 0.000134e-6, -2388.894020449, 0.577313584 }, + { 0.000109e-6, 10440.274292604, 6.218148717 }, + { 0.000102e-6, -543.918059096, 1.477842615 }, + { 0.000108e-6, 21228.392023546, 2.237753948 }, + { 0.000101e-6, -4535.059436924, 3.100492232 }, + { 0.000103e-6, 76.266071276, 5.594294322 }, + { 0.000104e-6, 949.175608970, 5.674287810 }, + { 0.000101e-6, 13517.870106233, 2.196632348 }, + { 0.000100e-6, 11933.367960670, 4.056084160 }, + + /* T^2 */ + { 4.322990e-6, 6283.075849991, 2.642893748 }, + /* 681, 690 */ + { 0.406495e-6, 0.000000000, 4.712388980 }, + { 0.122605e-6, 12566.151699983, 2.438140634 }, + { 0.019476e-6, 213.299095438, 1.642186981 }, + { 0.016916e-6, 529.690965095, 4.510959344 }, + { 0.013374e-6, -3.523118349, 1.502210314 }, + { 0.008042e-6, 26.298319800, 0.478549024 }, + { 0.007824e-6, 155.420399434, 5.254710405 }, + { 0.004894e-6, 5746.271337896, 4.683210850 }, + { 0.004875e-6, 5760.498431898, 0.759507698 }, + { 0.004416e-6, 5223.693919802, 6.028853166 }, + /* 691, 700 */ + { 0.004088e-6, -7.113547001, 0.060926389 }, + { 0.004433e-6, 77713.771467920, 3.627734103 }, + { 0.003277e-6, 18849.227549974, 2.327912542 }, + { 0.002703e-6, 6062.663207553, 1.271941729 }, + { 0.003435e-6, -775.522611324, 0.747446224 }, + { 0.002618e-6, 6076.890301554, 3.633715689 }, + { 0.003146e-6, 206.185548437, 5.647874613 }, + { 0.002544e-6, 1577.343542448, 6.232904270 }, + { 0.002218e-6, -220.412642439, 1.309509946 }, + { 0.002197e-6, 5856.477659115, 2.407212349 }, + /* 701, 710 */ + { 0.002897e-6, 5753.384884897, 5.863842246 }, + { 0.001766e-6, 426.598190876, 0.754113147 }, + { 0.001738e-6, -796.298006816, 2.714942671 }, + { 0.001695e-6, 522.577418094, 2.629369842 }, + { 0.001584e-6, 5507.553238667, 1.341138229 }, + { 0.001503e-6, -242.728603974, 0.377699736 }, + { 0.001552e-6, -536.804512095, 2.904684667 }, + { 0.001370e-6, -398.149003408, 1.265599125 }, + { 0.001889e-6, -5573.142801634, 4.413514859 }, + { 0.001722e-6, 6069.776754553, 2.445966339 }, + /* 711, 720 */ + { 0.001124e-6, 1059.381930189, 5.041799657 }, + { 0.001258e-6, 553.569402842, 3.849557278 }, + { 0.000831e-6, 951.718406251, 2.471094709 }, + { 0.000767e-6, 4694.002954708, 5.363125422 }, + { 0.000756e-6, 1349.867409659, 1.046195744 }, + { 0.000775e-6, -11.045700264, 0.245548001 }, + { 0.000597e-6, 2146.165416475, 4.543268798 }, + { 0.000568e-6, 5216.580372801, 4.178853144 }, + { 0.000711e-6, 1748.016413067, 5.934271972 }, + { 0.000499e-6, 12036.460734888, 0.624434410 }, + /* 721, 730 */ + { 0.000671e-6, -1194.447010225, 4.136047594 }, + { 0.000488e-6, 5849.364112115, 2.209679987 }, + { 0.000621e-6, 6438.496249426, 4.518860804 }, + { 0.000495e-6, -6286.598968340, 1.868201275 }, + { 0.000456e-6, 5230.807466803, 1.271231591 }, + { 0.000451e-6, 5088.628839767, 0.084060889 }, + { 0.000435e-6, 5643.178563677, 3.324456609 }, + { 0.000387e-6, 10977.078804699, 4.052488477 }, + { 0.000547e-6, 161000.685737473, 2.841633844 }, + { 0.000522e-6, 3154.687084896, 2.171979966 }, + /* 731, 740 */ + { 0.000375e-6, 5486.777843175, 4.983027306 }, + { 0.000421e-6, 5863.591206116, 4.546432249 }, + { 0.000439e-6, 7084.896781115, 0.522967921 }, + { 0.000309e-6, 2544.314419883, 3.172606705 }, + { 0.000347e-6, 4690.479836359, 1.479586566 }, + { 0.000317e-6, 801.820931124, 3.553088096 }, + { 0.000262e-6, 419.484643875, 0.606635550 }, + { 0.000248e-6, 6836.645252834, 3.014082064 }, + { 0.000245e-6, -1592.596013633, 5.519526220 }, + { 0.000225e-6, 4292.330832950, 2.877956536 }, + /* 741, 750 */ + { 0.000214e-6, 7234.794256242, 1.605227587 }, + { 0.000205e-6, 5767.611978898, 0.625804796 }, + { 0.000180e-6, 10447.387839604, 3.499954526 }, + { 0.000229e-6, 199.072001436, 5.632304604 }, + { 0.000214e-6, 639.897286314, 5.960227667 }, + { 0.000175e-6, -433.711737877, 2.162417992 }, + { 0.000209e-6, 515.463871093, 2.322150893 }, + { 0.000173e-6, 6040.347246017, 2.556183691 }, + { 0.000184e-6, 6309.374169791, 4.732296790 }, + { 0.000227e-6, 149854.400134205, 5.385812217 }, + /* 751, 760 */ + { 0.000154e-6, 8031.092263058, 5.120720920 }, + { 0.000151e-6, 5739.157790895, 4.815000443 }, + { 0.000197e-6, 7632.943259650, 0.222827271 }, + { 0.000197e-6, 74.781598567, 3.910456770 }, + { 0.000138e-6, 6055.549660552, 1.397484253 }, + { 0.000149e-6, -6127.655450557, 5.333727496 }, + { 0.000137e-6, 3894.181829542, 4.281749907 }, + { 0.000135e-6, 9437.762934887, 5.979971885 }, + { 0.000139e-6, -2352.866153772, 4.715630782 }, + { 0.000142e-6, 6812.766815086, 0.513330157 }, + /* 761, 770 */ + { 0.000120e-6, -4705.732307544, 0.194160689 }, + { 0.000131e-6, -71430.695617928, 0.000379226 }, + { 0.000124e-6, 6279.552731642, 2.122264908 }, + { 0.000108e-6, -6256.777530192, 0.883445696 }, + + /* T^3 */ + { 0.143388e-6, 6283.075849991, 1.131453581 }, + { 0.006671e-6, 12566.151699983, 0.775148887 }, + { 0.001480e-6, 155.420399434, 0.480016880 }, + { 0.000934e-6, 213.299095438, 6.144453084 }, + { 0.000795e-6, 529.690965095, 2.941595619 }, + { 0.000673e-6, 5746.271337896, 0.120415406 }, + /* 771, 780 */ + { 0.000672e-6, 5760.498431898, 5.317009738 }, + { 0.000389e-6, -220.412642439, 3.090323467 }, + { 0.000373e-6, 6062.663207553, 3.003551964 }, + { 0.000360e-6, 6076.890301554, 1.918913041 }, + { 0.000316e-6, -21.340641002, 5.545798121 }, + { 0.000315e-6, -242.728603974, 1.884932563 }, + { 0.000278e-6, 206.185548437, 1.266254859 }, + { 0.000238e-6, -536.804512095, 4.532664830 }, + { 0.000185e-6, 522.577418094, 4.578313856 }, + { 0.000245e-6, 18849.227549974, 0.587467082 }, + /* 781, 787 */ + { 0.000180e-6, 426.598190876, 5.151178553 }, + { 0.000200e-6, 553.569402842, 5.355983739 }, + { 0.000141e-6, 5223.693919802, 1.336556009 }, + { 0.000104e-6, 5856.477659115, 4.239842759 }, + + /* T^4 */ + { 0.003826e-6, 6283.075849991, 5.705257275 }, + { 0.000303e-6, 12566.151699983, 5.407132842 }, + { 0.000209e-6, 155.420399434, 1.989815753 } + }; + + +/* Time since J2000.0 in Julian millennia. */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJM; + +/* ================= */ +/* Topocentric terms */ +/* ================= */ + +/* Convert UT to local solar time in radians. */ + tsol = fmod(ut, 1.0) * ERFA_D2PI + elong; + +/* FUNDAMENTAL ARGUMENTS: Simon et al. 1994. */ + +/* Combine time argument (millennia) with deg/arcsec factor. */ + w = t / 3600.0; + +/* Sun Mean Longitude. */ + elsun = fmod(280.46645683 + 1296027711.03429 * w, 360.0) * ERFA_DD2R; + +/* Sun Mean Anomaly. */ + emsun = fmod(357.52910918 + 1295965810.481 * w, 360.0) * ERFA_DD2R; + +/* Mean Elongation of Moon from Sun. */ + d = fmod(297.85019547 + 16029616012.090 * w, 360.0) * ERFA_DD2R; + +/* Mean Longitude of Jupiter. */ + elj = fmod(34.35151874 + 109306899.89453 * w, 360.0) * ERFA_DD2R; + +/* Mean Longitude of Saturn. */ + els = fmod(50.07744430 + 44046398.47038 * w, 360.0) * ERFA_DD2R; + +/* TOPOCENTRIC TERMS: Moyer 1981 and Murray 1983. */ + wt = + 0.00029e-10 * u * sin(tsol + elsun - els) + + 0.00100e-10 * u * sin(tsol - 2.0 * emsun) + + 0.00133e-10 * u * sin(tsol - d) + + 0.00133e-10 * u * sin(tsol + elsun - elj) + - 0.00229e-10 * u * sin(tsol + 2.0 * elsun + emsun) + - 0.02200e-10 * v * cos(elsun + emsun) + + 0.05312e-10 * u * sin(tsol - emsun) + - 0.13677e-10 * u * sin(tsol + 2.0 * elsun) + - 1.31840e-10 * v * cos(elsun) + + 3.17679e-10 * u * sin(tsol); + +/* ===================== */ +/* Fairhead et al. model */ +/* ===================== */ + +/* T**0 */ + w0 = 0; + for (j = 473; j >= 0; j--) { + w0 += fairhd[j][0] * sin(fairhd[j][1] * t + fairhd[j][2]); + } + +/* T**1 */ + w1 = 0; + for (j = 678; j >= 474; j--) { + w1 += fairhd[j][0] * sin(fairhd[j][1] * t + fairhd[j][2]); + } + +/* T**2 */ + w2 = 0; + for (j = 763; j >= 679; j--) { + w2 += fairhd[j][0] * sin(fairhd[j][1] * t + fairhd[j][2]); + } + +/* T**3 */ + w3 = 0; + for (j = 783; j >= 764; j--) { + w3 += fairhd[j][0] * sin(fairhd[j][1] * t + fairhd[j][2]); + } + +/* T**4 */ + w4 = 0; + for (j = 786; j >= 784; j--) { + w4 += fairhd[j][0] * sin(fairhd[j][1] * t + fairhd[j][2]); + } + +/* Multiply by powers of T and combine. */ + wf = t * (t * (t * (t * w4 + w3) + w2) + w1) + w0; + +/* Adjustments to use JPL planetary masses instead of IAU. */ + wj = 0.00065e-6 * sin(6069.776754 * t + 4.021194) + + 0.00033e-6 * sin( 213.299095 * t + 5.543132) + + (-0.00196e-6 * sin(6208.294251 * t + 5.696701)) + + (-0.00173e-6 * sin( 74.781599 * t + 2.435900)) + + 0.03638e-6 * t * t; + +/* ============ */ +/* Final result */ +/* ============ */ + +/* TDB-TT in seconds. */ + w = wt + wf + wj; + + return w; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/dtf2d.c b/erfa/dtf2d.c new file mode 100644 index 0000000..551d1c4 --- /dev/null +++ b/erfa/dtf2d.c @@ -0,0 +1,212 @@ +#include "erfa.h" +#include + +int eraDtf2d(const char *scale, int iy, int im, int id, + int ihr, int imn, double sec, double *d1, double *d2) +/* +** - - - - - - - - - +** e r a D t f 2 d +** - - - - - - - - - +** +** Encode date and time fields into 2-part Julian Date (or in the case +** of UTC a quasi-JD form that includes special provision for leap +** seconds). +** +** Given: +** scale char[] time scale ID (Note 1) +** iy,im,id int year, month, day in Gregorian calendar (Note 2) +** ihr,imn int hour, minute +** sec double seconds +** +** Returned: +** d1,d2 double 2-part Julian Date (Notes 3,4) +** +** Returned (function value): +** int status: +3 = both of next two +** +2 = time is after end of day (Note 5) +** +1 = dubious year (Note 6) +** 0 = OK +** -1 = bad year +** -2 = bad month +** -3 = bad day +** -4 = bad hour +** -5 = bad minute +** -6 = bad second (<0) +** +** Notes: +** +** 1) scale identifies the time scale. Only the value "UTC" (in upper +** case) is significant, and enables handling of leap seconds (see +** Note 4). +** +** 2) For calendar conventions and limitations, see eraCal2jd. +** +** 3) The sum of the results, d1+d2, is Julian Date, where normally d1 +** is the Julian Day Number and d2 is the fraction of a day. In the +** case of UTC, where the use of JD is problematical, special +** conventions apply: see the next note. +** +** 4) JD cannot unambiguously represent UTC during a leap second unless +** special measures are taken. The ERFA internal convention is that +** the quasi-JD day represents UTC days whether the length is 86399, +** 86400 or 86401 SI seconds. In the 1960-1972 era there were +** smaller jumps (in either direction) each time the linear UTC(TAI) +** expression was changed, and these "mini-leaps" are also included +** in the ERFA convention. +** +** 5) The warning status "time is after end of day" usually means that +** the sec argument is greater than 60.0. However, in a day ending +** in a leap second the limit changes to 61.0 (or 59.0 in the case +** of a negative leap second). +** +** 6) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the future +** to be trusted. See eraDat for further details. +** +** 7) Only in the case of continuous and regular time scales (TAI, TT, +** TCG, TCB and TDB) is the result d1+d2 a Julian Date, strictly +** speaking. In the other cases (UT1 and UTC) the result must be +** used with circumspection; in particular the difference between +** two such results cannot be interpreted as a precise time +** interval. +** +** Called: +** eraCal2jd Gregorian calendar to JD +** eraDat delta(AT) = TAI-UTC +** eraJd2cal JD to Gregorian calendar +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int js, iy2, im2, id2; + double dj, w, day, seclim, dat0, dat12, dat24, dleap, time; + + +/* Today's Julian Day Number. */ + js = eraCal2jd(iy, im, id, &dj, &w); + if ( js ) return js; + dj += w; + +/* Day length and final minute length in seconds (provisional). */ + day = ERFA_DAYSEC; + seclim = 60.0; + +/* Deal with the UTC leap second case. */ + if ( ! strcmp(scale,"UTC") ) { + + /* TAI-UTC at 0h today. */ + js = eraDat(iy, im, id, 0.0, &dat0); + if ( js < 0 ) return js; + + /* TAI-UTC at 12h today (to detect drift). */ + js = eraDat(iy, im, id, 0.5, &dat12); + if ( js < 0 ) return js; + + /* TAI-UTC at 0h tomorrow (to detect jumps). */ + js = eraJd2cal ( dj, 1.5, &iy2, &im2, &id2, &w); + if ( js ) return js; + js = eraDat(iy2, im2, id2, 0.0, &dat24); + if ( js < 0 ) return js; + + /* Any sudden change in TAI-UTC between today and tomorrow. */ + dleap = dat24 - (2.0*dat12 - dat0); + + /* If leap second day, correct the day and final minute lengths. */ + day += dleap; + if ( ihr == 23 && imn == 59 ) seclim += dleap; + + /* End of UTC-specific actions. */ + } + +/* Validate the time. */ + if ( ihr >= 0 && ihr <= 23 ) { + if ( imn >= 0 && imn <= 59 ) { + if ( sec >= 0 ) { + if ( sec >= seclim ) { + js += 2; + } + } else { + js = -6; + } + } else { + js = -5; + } + } else { + js = -4; + } + if ( js < 0 ) return js; + +/* The time in days. */ + time = ( 60.0 * ( (double) ( 60 * ihr + imn ) ) + sec ) / day; + +/* Return the date and time. */ + *d1 = dj; + *d2 = time; + +/* Status. */ + return js; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ee00.c b/erfa/ee00.c new file mode 100644 index 0000000..d66981d --- /dev/null +++ b/erfa/ee00.c @@ -0,0 +1,137 @@ +#include "erfa.h" + +double eraEe00(double date1, double date2, double epsa, double dpsi) +/* +** - - - - - - - - +** e r a E e 0 0 +** - - - - - - - - +** +** The equation of the equinoxes, compatible with IAU 2000 resolutions, +** given the nutation in longitude and the mean obliquity. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** epsa double mean obliquity (Note 2) +** dpsi double nutation in longitude (Note 3) +** +** Returned (function value): +** double equation of the equinoxes (Note 4) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The obliquity, in radians, is mean of date. +** +** 3) The result, which is in radians, operates in the following sense: +** +** Greenwich apparent ST = GMST + equation of the equinoxes +** +** 4) The result is compatible with the IAU 2000 resolutions. For +** further details, see IERS Conventions 2003 and Capitaine et al. +** (2002). +** +** Called: +** eraEect00 equation of the equinoxes complementary terms +** +** References: +** +** Capitaine, N., Wallace, P.T. and McCarthy, D.D., "Expressions to +** implement the IAU 2000 definition of UT1", Astronomy & +** Astrophysics, 406, 1135-1149 (2003) +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double ee; + + +/* Equation of the equinoxes. */ + ee = dpsi * cos(epsa) + eraEect00(date1, date2); + + return ee; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ee00a.c b/erfa/ee00a.c new file mode 100644 index 0000000..c21d65d --- /dev/null +++ b/erfa/ee00a.c @@ -0,0 +1,144 @@ +#include "erfa.h" + +double eraEe00a(double date1, double date2) +/* +** - - - - - - - - - +** e r a E e 0 0 a +** - - - - - - - - - +** +** Equation of the equinoxes, compatible with IAU 2000 resolutions. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double equation of the equinoxes (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The result, which is in radians, operates in the following sense: +** +** Greenwich apparent ST = GMST + equation of the equinoxes +** +** 3) The result is compatible with the IAU 2000 resolutions. For +** further details, see IERS Conventions 2003 and Capitaine et al. +** (2002). +** +** Called: +** eraPr00 IAU 2000 precession adjustments +** eraObl80 mean obliquity, IAU 1980 +** eraNut00a nutation, IAU 2000A +** eraEe00 equation of the equinoxes, IAU 2000 +** +** References: +** +** Capitaine, N., Wallace, P.T. and McCarthy, D.D., "Expressions to +** implement the IAU 2000 definition of UT1", Astronomy & +** Astrophysics, 406, 1135-1149 (2003). +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsipr, depspr, epsa, dpsi, deps, ee; + + +/* IAU 2000 precession-rate adjustments. */ + eraPr00(date1, date2, &dpsipr, &depspr); + +/* Mean obliquity, consistent with IAU 2000 precession-nutation. */ + epsa = eraObl80(date1, date2) + depspr; + +/* Nutation in longitude. */ + eraNut00a(date1, date2, &dpsi, &deps); + +/* Equation of the equinoxes. */ + ee = eraEe00(date1, date2, epsa, dpsi); + + return ee; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ee00b.c b/erfa/ee00b.c new file mode 100644 index 0000000..b3c7984 --- /dev/null +++ b/erfa/ee00b.c @@ -0,0 +1,150 @@ +#include "erfa.h" + +double eraEe00b(double date1, double date2) +/* +** - - - - - - - - - +** e r a E e 0 0 b +** - - - - - - - - - +** +** Equation of the equinoxes, compatible with IAU 2000 resolutions but +** using the truncated nutation model IAU 2000B. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double equation of the equinoxes (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The result, which is in radians, operates in the following sense: +** +** Greenwich apparent ST = GMST + equation of the equinoxes +** +** 3) The result is compatible with the IAU 2000 resolutions except +** that accuracy has been compromised for the sake of speed. For +** further details, see McCarthy & Luzum (2001), IERS Conventions +** 2003 and Capitaine et al. (2003). +** +** Called: +** eraPr00 IAU 2000 precession adjustments +** eraObl80 mean obliquity, IAU 1980 +** eraNut00b nutation, IAU 2000B +** eraEe00 equation of the equinoxes, IAU 2000 +** +** References: +** +** Capitaine, N., Wallace, P.T. and McCarthy, D.D., "Expressions to +** implement the IAU 2000 definition of UT1", Astronomy & +** Astrophysics, 406, 1135-1149 (2003) +** +** McCarthy, D.D. & Luzum, B.J., "An abridged model of the +** precession-nutation of the celestial pole", Celestial Mechanics & +** Dynamical Astronomy, 85, 37-49 (2003) +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsipr, depspr, epsa, dpsi, deps, ee; + + +/* IAU 2000 precession-rate adjustments. */ + eraPr00(date1, date2, &dpsipr, &depspr); + +/* Mean obliquity, consistent with IAU 2000 precession-nutation. */ + epsa = eraObl80(date1, date2) + depspr; + +/* Nutation in longitude. */ + eraNut00b(date1, date2, &dpsi, &deps); + +/* Equation of the equinoxes. */ + ee = eraEe00(date1, date2, epsa, dpsi); + + return ee; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ee06a.c b/erfa/ee06a.c new file mode 100644 index 0000000..9eb15ed --- /dev/null +++ b/erfa/ee06a.c @@ -0,0 +1,131 @@ +#include "erfa.h" + +double eraEe06a(double date1, double date2) +/* +** - - - - - - - - - +** e r a E e 0 6 a +** - - - - - - - - - +** +** Equation of the equinoxes, compatible with IAU 2000 resolutions and +** IAU 2006/2000A precession-nutation. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double equation of the equinoxes (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The result, which is in radians, operates in the following sense: +** +** Greenwich apparent ST = GMST + equation of the equinoxes +** +** Called: +** eraAnpm normalize angle into range +/- pi +** eraGst06a Greenwich apparent sidereal time, IAU 2006/2000A +** eraGmst06 Greenwich mean sidereal time, IAU 2006 +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), 2004, IERS Conventions (2003), +** IERS Technical Note No. 32, BKG +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gst06a, gmst06, ee; + + +/* Apparent and mean sidereal times. */ + gst06a = eraGst06a(0.0, 0.0, date1, date2); + gmst06 = eraGmst06(0.0, 0.0, date1, date2); + +/* Equation of the equinoxes. */ + ee = eraAnpm(gst06a - gmst06); + + return ee; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/eect00.c b/erfa/eect00.c new file mode 100644 index 0000000..d2af497 --- /dev/null +++ b/erfa/eect00.c @@ -0,0 +1,291 @@ +#include "erfa.h" + +double eraEect00(double date1, double date2) +/* +** - - - - - - - - - - +** e r a E e c t 0 0 +** - - - - - - - - - - +** +** Equation of the equinoxes complementary terms, consistent with +** IAU 2000 resolutions. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double complementary terms (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The "complementary terms" are part of the equation of the +** equinoxes (EE), classically the difference between apparent and +** mean Sidereal Time: +** +** GAST = GMST + EE +** +** with: +** +** EE = dpsi * cos(eps) +** +** where dpsi is the nutation in longitude and eps is the obliquity +** of date. However, if the rotation of the Earth were constant in +** an inertial frame the classical formulation would lead to +** apparent irregularities in the UT1 timescale traceable to side- +** effects of precession-nutation. In order to eliminate these +** effects from UT1, "complementary terms" were introduced in 1994 +** (IAU, 1994) and took effect from 1997 (Capitaine and Gontier, +** 1993): +** +** GAST = GMST + CT + EE +** +** By convention, the complementary terms are included as part of +** the equation of the equinoxes rather than as part of the mean +** Sidereal Time. This slightly compromises the "geometrical" +** interpretation of mean sidereal time but is otherwise +** inconsequential. +** +** The present function computes CT in the above expression, +** compatible with IAU 2000 resolutions (Capitaine et al., 2002, and +** IERS Conventions 2003). +** +** Called: +** eraFal03 mean anomaly of the Moon +** eraFalp03 mean anomaly of the Sun +** eraFaf03 mean argument of the latitude of the Moon +** eraFad03 mean elongation of the Moon from the Sun +** eraFaom03 mean longitude of the Moon's ascending node +** eraFave03 mean longitude of Venus +** eraFae03 mean longitude of Earth +** eraFapa03 general accumulated precession in longitude +** +** References: +** +** Capitaine, N. & Gontier, A.-M., Astron. Astrophys., 275, +** 645-650 (1993) +** +** Capitaine, N., Wallace, P.T. and McCarthy, D.D., "Expressions to +** implement the IAU 2000 definition of UT1", Astronomy & +** Astrophysics, 406, 1135-1149 (2003) +** +** IAU Resolution C7, Recommendation 3 (1994) +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Time since J2000.0, in Julian centuries */ + double t; + +/* Miscellaneous */ + int i, j; + double a, s0, s1; + +/* Fundamental arguments */ + double fa[14]; + +/* Returned value. */ + double eect; + +/* ----------------------------------------- */ +/* The series for the EE complementary terms */ +/* ----------------------------------------- */ + + typedef struct { + int nfa[8]; /* coefficients of l,l',F,D,Om,LVe,LE,pA */ + double s, c; /* sine and cosine coefficients */ + } TERM; + +/* Terms of order t^0 */ + static const TERM e0[] = { + + /* 1-10 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, 2640.96e-6, -0.39e-6 }, + {{ 0, 0, 0, 0, 2, 0, 0, 0}, 63.52e-6, -0.02e-6 }, + {{ 0, 0, 2, -2, 3, 0, 0, 0}, 11.75e-6, 0.01e-6 }, + {{ 0, 0, 2, -2, 1, 0, 0, 0}, 11.21e-6, 0.01e-6 }, + {{ 0, 0, 2, -2, 2, 0, 0, 0}, -4.55e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 3, 0, 0, 0}, 2.02e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 1, 0, 0, 0}, 1.98e-6, 0.00e-6 }, + {{ 0, 0, 0, 0, 3, 0, 0, 0}, -1.72e-6, 0.00e-6 }, + {{ 0, 1, 0, 0, 1, 0, 0, 0}, -1.41e-6, -0.01e-6 }, + {{ 0, 1, 0, 0, -1, 0, 0, 0}, -1.26e-6, -0.01e-6 }, + + /* 11-20 */ + {{ 1, 0, 0, 0, -1, 0, 0, 0}, -0.63e-6, 0.00e-6 }, + {{ 1, 0, 0, 0, 1, 0, 0, 0}, -0.63e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 3, 0, 0, 0}, 0.46e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 1, 0, 0, 0}, 0.45e-6, 0.00e-6 }, + {{ 0, 0, 4, -4, 4, 0, 0, 0}, 0.36e-6, 0.00e-6 }, + {{ 0, 0, 1, -1, 1, -8, 12, 0}, -0.24e-6, -0.12e-6 }, + {{ 0, 0, 2, 0, 0, 0, 0, 0}, 0.32e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 2, 0, 0, 0}, 0.28e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 3, 0, 0, 0}, 0.27e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 1, 0, 0, 0}, 0.26e-6, 0.00e-6 }, + + /* 21-30 */ + {{ 0, 0, 2, -2, 0, 0, 0, 0}, -0.21e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -3, 0, 0, 0}, 0.19e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -1, 0, 0, 0}, 0.18e-6, 0.00e-6 }, + {{ 0, 0, 0, 0, 0, 8,-13, -1}, -0.10e-6, 0.05e-6 }, + {{ 0, 0, 0, 2, 0, 0, 0, 0}, 0.15e-6, 0.00e-6 }, + {{ 2, 0, -2, 0, -1, 0, 0, 0}, -0.14e-6, 0.00e-6 }, + {{ 1, 0, 0, -2, 1, 0, 0, 0}, 0.14e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 2, 0, 0, 0}, -0.14e-6, 0.00e-6 }, + {{ 1, 0, 0, -2, -1, 0, 0, 0}, 0.14e-6, 0.00e-6 }, + {{ 0, 0, 4, -2, 4, 0, 0, 0}, 0.13e-6, 0.00e-6 }, + + /* 31-33 */ + {{ 0, 0, 2, -2, 4, 0, 0, 0}, -0.11e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -3, 0, 0, 0}, 0.11e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -1, 0, 0, 0}, 0.11e-6, 0.00e-6 } + }; + +/* Terms of order t^1 */ + static const TERM e1[] = { + {{ 0, 0, 0, 0, 1, 0, 0, 0}, -0.87e-6, 0.00e-6 } + }; + +/* Number of terms in the series */ + const int NE0 = (int) (sizeof e0 / sizeof (TERM)); + const int NE1 = (int) (sizeof e1 / sizeof (TERM)); + +/*--------------------------------------------------------------------*/ + +/* Interval between fundamental epoch J2000.0 and current date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Fundamental Arguments (from IERS Conventions 2003) */ + +/* Mean anomaly of the Moon. */ + fa[0] = eraFal03(t); + +/* Mean anomaly of the Sun. */ + fa[1] = eraFalp03(t); + +/* Mean longitude of the Moon minus that of the ascending node. */ + fa[2] = eraFaf03(t); + +/* Mean elongation of the Moon from the Sun. */ + fa[3] = eraFad03(t); + +/* Mean longitude of the ascending node of the Moon. */ + fa[4] = eraFaom03(t); + +/* Mean longitude of Venus. */ + fa[5] = eraFave03(t); + +/* Mean longitude of Earth. */ + fa[6] = eraFae03(t); + +/* General precession in longitude. */ + fa[7] = eraFapa03(t); + +/* Evaluate the EE complementary terms. */ + s0 = 0.0; + s1 = 0.0; + + for (i = NE0-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)(e0[i].nfa[j]) * fa[j]; + } + s0 += e0[i].s * sin(a) + e0[i].c * cos(a); + } + + for (i = NE1-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)(e1[i].nfa[j]) * fa[j]; + } + s1 += e1[i].s * sin(a) + e1[i].c * cos(a); + } + + eect = (s0 + s1 * t ) * ERFA_DAS2R; + + return eect; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/eform.c b/erfa/eform.c new file mode 100644 index 0000000..2c30412 --- /dev/null +++ b/erfa/eform.c @@ -0,0 +1,155 @@ +#include "erfa.h" + +int eraEform ( int n, double *a, double *f ) +/* +** - - - - - - - - - +** e r a E f o r m +** - - - - - - - - - +** +** Earth reference ellipsoids. +** +** Given: +** n int ellipsoid identifier (Note 1) +** +** Returned: +** a double equatorial radius (meters, Note 2) +** f double flattening (Note 2) +** +** Returned (function value): +** int status: 0 = OK +** -1 = illegal identifier (Note 3) +** +** Notes: +** +** 1) The identifier n is a number that specifies the choice of +** reference ellipsoid. The following are supported: +** +** n ellipsoid +** +** 1 ERFA_WGS84 +** 2 ERFA_GRS80 +** 3 ERFA_WGS72 +** +** The n value has no significance outside the ERFA software. For +** convenience, symbols ERFA_WGS84 etc. are defined in erfam.h. +** +** 2) The ellipsoid parameters are returned in the form of equatorial +** radius in meters (a) and flattening (f). The latter is a number +** around 0.00335, i.e. around 1/298. +** +** 3) For the case where an unsupported n value is supplied, zero a and +** f are returned, as well as error status. +** +** References: +** +** Department of Defense World Geodetic System 1984, National +** Imagery and Mapping Agency Technical Report 8350.2, Third +** Edition, p3-2. +** +** Moritz, H., Bull. Geodesique 66-2, 187 (1992). +** +** The Department of Defense World Geodetic System 1972, World +** Geodetic System Committee, May 1974. +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** p220. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Look up a and f for the specified reference ellipsoid. */ + switch ( n ) { + + case ERFA_WGS84: + *a = 6378137.0; + *f = 1.0 / 298.257223563; + break; + + case ERFA_GRS80: + *a = 6378137.0; + *f = 1.0 / 298.257222101; + break; + + case ERFA_WGS72: + *a = 6378135.0; + *f = 1.0 / 298.26; + break; + + default: + + /* Invalid identifier. */ + *a = 0.0; + *f = 0.0; + return -1; + + } + +/* OK status. */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/eo06a.c b/erfa/eo06a.c new file mode 100644 index 0000000..86ec8cb --- /dev/null +++ b/erfa/eo06a.c @@ -0,0 +1,140 @@ +#include "erfa.h" + +double eraEo06a(double date1, double date2) +/* +** - - - - - - - - - +** e r a E o 0 6 a +** - - - - - - - - - +** +** Equation of the origins, IAU 2006 precession and IAU 2000A nutation. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double equation of the origins in radians +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The equation of the origins is the distance between the true +** equinox and the celestial intermediate origin and, equivalently, +** the difference between Earth rotation angle and Greenwich +** apparent sidereal time (ERA-GST). It comprises the precession +** (since J2000.0) in right ascension plus the equation of the +** equinoxes (including the small correction terms). +** +** Called: +** eraPnm06a classical NPB matrix, IAU 2006/2000A +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS06 the CIO locator s, given X,Y, IAU 2006 +** eraEors equation of the origins, given NPB matrix and s +** +** References: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double r[3][3], x, y, s, eo; + + +/* Classical nutation x precession x bias matrix. */ + eraPnm06a(date1, date2, r); + +/* Extract CIP coordinates. */ + eraBpn2xy(r, &x, &y); + +/* The CIO locator, s. */ + s = eraS06(date1, date2, x, y); + +/* Solve for the EO. */ + eo = eraEors(r, s); + + return eo; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/eors.c b/erfa/eors.c new file mode 100644 index 0000000..c16d452 --- /dev/null +++ b/erfa/eors.c @@ -0,0 +1,117 @@ +#include "erfa.h" + +double eraEors(double rnpb[3][3], double s) +/* +** - - - - - - - - +** e r a E o r s +** - - - - - - - - +** +** Equation of the origins, given the classical NPB matrix and the +** quantity s. +** +** Given: +** rnpb double[3][3] classical nutation x precession x bias matrix +** s double the quantity s (the CIO locator) +** +** Returned (function value): +** double the equation of the origins in radians. +** +** Notes: +** +** 1) The equation of the origins is the distance between the true +** equinox and the celestial intermediate origin and, equivalently, +** the difference between Earth rotation angle and Greenwich +** apparent sidereal time (ERA-GST). It comprises the precession +** (since J2000.0) in right ascension plus the equation of the +** equinoxes (including the small correction terms). +** +** 2) The algorithm is from Wallace & Capitaine (2006). +** +** References: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** Wallace, P. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double x, ax, xs, ys, zs, p, q, eo; + + +/* Evaluate Wallace & Capitaine (2006) expression (16). */ + x = rnpb[2][0]; + ax = x / (1.0 + rnpb[2][2]); + xs = 1.0 - ax * x; + ys = -ax * rnpb[2][1]; + zs = -x; + p = rnpb[0][0] * xs + rnpb[0][1] * ys + rnpb[0][2] * zs; + q = rnpb[1][0] * xs + rnpb[1][1] * ys + rnpb[1][2] * zs; + eo = ((p != 0) || (q != 0)) ? s - atan2(q, p) : s; + + return eo; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/epb.c b/erfa/epb.c new file mode 100644 index 0000000..cd50ea8 --- /dev/null +++ b/erfa/epb.c @@ -0,0 +1,100 @@ +#include "erfa.h" + +double eraEpb(double dj1, double dj2) +/* +** - - - - - - - +** e r a E p b +** - - - - - - - +** +** Julian Date to Besselian Epoch. +** +** Given: +** dj1,dj2 double Julian Date (see note) +** +** Returned (function value): +** double Besselian Epoch. +** +** Note: +** +** The Julian Date is supplied in two pieces, in the usual ERFA +** manner, which is designed to preserve time resolution. The +** Julian Date is available as a single number by adding dj1 and +** dj2. The maximum resolution is achieved if dj1 is 2451545.0 +** (J2000.0). +** +** Reference: +** +** Lieske, J.H., 1979. Astron.Astrophys., 73, 282. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* J2000.0-B1900.0 (2415019.81352) in days */ + const double D1900 = 36524.68648; + + return 1900.0 + ((dj1 - ERFA_DJ00) + (dj2 + D1900)) / ERFA_DTY; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/epb2jd.c b/erfa/epb2jd.c new file mode 100644 index 0000000..0cee330 --- /dev/null +++ b/erfa/epb2jd.c @@ -0,0 +1,100 @@ +#include "erfa.h" + +void eraEpb2jd(double epb, double *djm0, double *djm) +/* +** - - - - - - - - - - +** e r a E p b 2 j d +** - - - - - - - - - - +** +** Besselian Epoch to Julian Date. +** +** Given: +** epb double Besselian Epoch (e.g. 1957.3) +** +** Returned: +** djm0 double MJD zero-point: always 2400000.5 +** djm double Modified Julian Date +** +** Note: +** +** The Julian Date is returned in two pieces, in the usual ERFA +** manner, which is designed to preserve time resolution. The +** Julian Date is available as a single number by adding djm0 and +** djm. +** +** Reference: +** +** Lieske, J.H., 1979, Astron.Astrophys. 73, 282. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + *djm0 = ERFA_DJM0; + *djm = 15019.81352 + (epb - 1900.0) * ERFA_DTY; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/epj.c b/erfa/epj.c new file mode 100644 index 0000000..1a2b957 --- /dev/null +++ b/erfa/epj.c @@ -0,0 +1,102 @@ +#include "erfa.h" + +double eraEpj(double dj1, double dj2) +/* +** - - - - - - - +** e r a E p j +** - - - - - - - +** +** Julian Date to Julian Epoch. +** +** Given: +** dj1,dj2 double Julian Date (see note) +** +** Returned (function value): +** double Julian Epoch +** +** Note: +** +** The Julian Date is supplied in two pieces, in the usual ERFA +** manner, which is designed to preserve time resolution. The +** Julian Date is available as a single number by adding dj1 and +** dj2. The maximum resolution is achieved if dj1 is 2451545.0 +** (J2000.0). +** +** Reference: +** +** Lieske, J.H., 1979, Astron.Astrophys. 73, 282. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double epj; + + + epj = 2000.0 + ((dj1 - ERFA_DJ00) + dj2) / ERFA_DJY; + + return epj; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/epj2jd.c b/erfa/epj2jd.c new file mode 100644 index 0000000..8fe1479 --- /dev/null +++ b/erfa/epj2jd.c @@ -0,0 +1,100 @@ +#include "erfa.h" + +void eraEpj2jd(double epj, double *djm0, double *djm) +/* +** - - - - - - - - - - +** e r a E p j 2 j d +** - - - - - - - - - - +** +** Julian Epoch to Julian Date. +** +** Given: +** epj double Julian Epoch (e.g. 1996.8) +** +** Returned: +** djm0 double MJD zero-point: always 2400000.5 +** djm double Modified Julian Date +** +** Note: +** +** The Julian Date is returned in two pieces, in the usual ERFA +** manner, which is designed to preserve time resolution. The +** Julian Date is available as a single number by adding djm0 and +** djm. +** +** Reference: +** +** Lieske, J.H., 1979, Astron.Astrophys. 73, 282. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + *djm0 = ERFA_DJM0; + *djm = ERFA_DJM00 + (epj - 2000.0) * 365.25; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/epv00.c b/erfa/epv00.c new file mode 100644 index 0000000..56d5fc8 --- /dev/null +++ b/erfa/epv00.c @@ -0,0 +1,2598 @@ +#include "erfa.h" + +int eraEpv00(double date1, double date2, + double pvh[2][3], double pvb[2][3]) +/* +** - - - - - - - - - +** e r a E p v 0 0 +** - - - - - - - - - +** +** Earth position and velocity, heliocentric and barycentric, with +** respect to the Barycentric Celestial Reference System. +** +** Given: +** date1,date2 double TDB date (Note 1) +** +** Returned: +** pvh double[2][3] heliocentric Earth position/velocity +** pvb double[2][3] barycentric Earth position/velocity +** +** Returned (function value): +** int status: 0 = OK +** +1 = warning: date outside +** the range 1900-2100 AD +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, among +** others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. However, +** the accuracy of the result is more likely to be limited by the +** algorithm itself than the way the date has been expressed. +** +** n.b. TT can be used instead of TDB in most applications. +** +** 2) On return, the arrays pvh and pvb contain the following: +** +** pvh[0][0] x } +** pvh[0][1] y } heliocentric position, AU +** pvh[0][2] z } +** +** pvh[1][0] xdot } +** pvh[1][1] ydot } heliocentric velocity, AU/d +** pvh[1][2] zdot } +** +** pvb[0][0] x } +** pvb[0][1] y } barycentric position, AU +** pvb[0][2] z } +** +** pvb[1][0] xdot } +** pvb[1][1] ydot } barycentric velocity, AU/d +** pvb[1][2] zdot } +** +** The vectors are with respect to the Barycentric Celestial +** Reference System. The time unit is one day in TDB. +** +** 3) The function is a SIMPLIFIED SOLUTION from the planetary theory +** VSOP2000 (X. Moisson, P. Bretagnon, 2001, Celes. Mechanics & +** Dyn. Astron., 80, 3/4, 205-213) and is an adaptation of original +** Fortran code supplied by P. Bretagnon (private comm., 2000). +** +** 4) Comparisons over the time span 1900-2100 with this simplified +** solution and the JPL DE405 ephemeris give the following results: +** +** RMS max +** Heliocentric: +** position error 3.7 11.2 km +** velocity error 1.4 5.0 mm/s +** +** Barycentric: +** position error 4.6 13.4 km +** velocity error 1.4 4.9 mm/s +** +** Comparisons with the JPL DE406 ephemeris show that by 1800 and +** 2200 the position errors are approximately double their 1900-2100 +** size. By 1500 and 2500 the deterioration is a factor of 10 and +** by 1000 and 3000 a factor of 60. The velocity accuracy falls off +** at about half that rate. +** +** 5) It is permissible to use the same array for pvh and pvb, which +** will receive the barycentric values. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* +** Matrix elements for orienting the analytical model to DE405. +** +** The corresponding Euler angles are: +** +** d ' " +** 1st rotation - 23 26 21.4091 about the x-axis (obliquity) +** 2nd rotation + 0.0475 about the z-axis (RA offset) +** +** These were obtained empirically, by comparisons with DE405 over +** 1900-2100. +*/ + static const double am12 = 0.000000211284, + am13 = -0.000000091603, + am21 = -0.000000230286, + am22 = 0.917482137087, + am23 = -0.397776982902, + am32 = 0.397776982902, + am33 = 0.917482137087; + +/* +** ---------------------- +** Ephemeris Coefficients +** ---------------------- +** +** The ephemeris consists of harmonic terms for predicting (i) the Sun +** to Earth vector and (ii) the Solar-System-barycenter to Sun vector +** respectively. The coefficients are stored in arrays which, although +** 1-demensional, contain groups of three. Each triplet of +** coefficients is the amplitude, phase and frequency for one term in +** the model, and each array contains the number of terms called for by +** the model. +** +** There are eighteen such arrays, named as follows: +** +** array model power of T component +** +** e0x Sun-to-Earth 0 x +** e0y Sun-to-Earth 0 y +** e0z Sun-to-Earth 0 z +** +** e1x Sun-to-Earth 1 x +** e1y Sun-to-Earth 1 y +** e1z Sun-to-Earth 1 z +** +** e2x Sun-to-Earth 2 x +** e2y Sun-to-Earth 2 y +** e2z Sun-to-Earth 2 z +** +** s0x SSB-to-Sun 0 x +** s0y SSB-to-Sun 0 y +** s0z SSB-to-Sun 0 z +** +** s1x SSB-to-Sun 1 x +** s1y SSB-to-Sun 1 y +** s1z SSB-to-Sun 1 z +** +** s2x SSB-to-Sun 2 x +** s2y SSB-to-Sun 2 y +** s2z SSB-to-Sun 2 z +*/ + +/* Sun-to-Earth, T^0, X */ + static const double e0x[] = { + 0.9998292878132e+00, 0.1753485171504e+01, 0.6283075850446e+01, + 0.8352579567414e-02, 0.1710344404582e+01, 0.1256615170089e+02, + 0.5611445335148e-02, 0.0000000000000e+00, 0.0000000000000e+00, + 0.1046664295572e-03, 0.1667225416770e+01, 0.1884922755134e+02, + 0.3110842534677e-04, 0.6687513390251e+00, 0.8399684731857e+02, + 0.2552413503550e-04, 0.5830637358413e+00, 0.5296909721118e+00, + 0.2137207845781e-04, 0.1092330954011e+01, 0.1577343543434e+01, + 0.1680240182951e-04, 0.4955366134987e+00, 0.6279552690824e+01, + 0.1679012370795e-04, 0.6153014091901e+01, 0.6286599010068e+01, + 0.1445526946777e-04, 0.3472744100492e+01, 0.2352866153506e+01, + + 0.1091038246184e-04, 0.3689845786119e+01, 0.5223693906222e+01, + 0.9344399733932e-05, 0.6073934645672e+01, 0.1203646072878e+02, + 0.8993182910652e-05, 0.3175705249069e+01, 0.1021328554739e+02, + 0.5665546034116e-05, 0.2152484672246e+01, 0.1059381944224e+01, + 0.6844146703035e-05, 0.1306964099750e+01, 0.5753384878334e+01, + 0.7346610905565e-05, 0.4354980070466e+01, 0.3981490189893e+00, + 0.6815396474414e-05, 0.2218229211267e+01, 0.4705732307012e+01, + 0.6112787253053e-05, 0.5384788425458e+01, 0.6812766822558e+01, + 0.4518120711239e-05, 0.6087604012291e+01, 0.5884926831456e+01, + 0.4521963430706e-05, 0.1279424524906e+01, 0.6256777527156e+01, + + 0.4497426764085e-05, 0.5369129144266e+01, 0.6309374173736e+01, + 0.4062190566959e-05, 0.5436473303367e+00, 0.6681224869435e+01, + 0.5412193480192e-05, 0.7867838528395e+00, 0.7755226100720e+00, + 0.5469839049386e-05, 0.1461440311134e+01, 0.1414349524433e+02, + 0.5205264083477e-05, 0.4432944696116e+01, 0.7860419393880e+01, + 0.2149759935455e-05, 0.4502237496846e+01, 0.1150676975667e+02, + 0.2279109618501e-05, 0.1239441308815e+01, 0.7058598460518e+01, + 0.2259282939683e-05, 0.3272430985331e+01, 0.4694002934110e+01, + 0.2558950271319e-05, 0.2265471086404e+01, 0.1216800268190e+02, + 0.2561581447555e-05, 0.1454740653245e+01, 0.7099330490126e+00, + + 0.1781441115440e-05, 0.2962068630206e+01, 0.7962980379786e+00, + 0.1612005874644e-05, 0.1473255041006e+01, 0.5486777812467e+01, + 0.1818630667105e-05, 0.3743903293447e+00, 0.6283008715021e+01, + 0.1818601377529e-05, 0.6274174354554e+01, 0.6283142985870e+01, + 0.1554475925257e-05, 0.1624110906816e+01, 0.2513230340178e+02, + 0.2090948029241e-05, 0.5852052276256e+01, 0.1179062909082e+02, + 0.2000176345460e-05, 0.4072093298513e+01, 0.1778984560711e+02, + 0.1289535917759e-05, 0.5217019331069e+01, 0.7079373888424e+01, + 0.1281135307881e-05, 0.4802054538934e+01, 0.3738761453707e+01, + 0.1518229005692e-05, 0.8691914742502e+00, 0.2132990797783e+00, + + 0.9450128579027e-06, 0.4601859529950e+01, 0.1097707878456e+02, + 0.7781119494996e-06, 0.1844352816694e+01, 0.8827390247185e+01, + 0.7733407759912e-06, 0.3582790154750e+01, 0.5507553240374e+01, + 0.7350644318120e-06, 0.2695277788230e+01, 0.1589072916335e+01, + 0.6535928827023e-06, 0.3651327986142e+01, 0.1176985366291e+02, + 0.6324624183656e-06, 0.2241302375862e+01, 0.6262300422539e+01, + 0.6298565300557e-06, 0.4407122406081e+01, 0.6303851278352e+01, + 0.8587037089179e-06, 0.3024307223119e+01, 0.1672837615881e+03, + 0.8299954491035e-06, 0.6192539428237e+01, 0.3340612434717e+01, + 0.6311263503401e-06, 0.2014758795416e+01, 0.7113454667900e-02, + + 0.6005646745452e-06, 0.3399500503397e+01, 0.4136910472696e+01, + 0.7917715109929e-06, 0.2493386877837e+01, 0.6069776770667e+01, + 0.7556958099685e-06, 0.4159491740143e+01, 0.6496374930224e+01, + 0.6773228244949e-06, 0.4034162934230e+01, 0.9437762937313e+01, + 0.5370708577847e-06, 0.1562219163734e+01, 0.1194447056968e+01, + 0.5710804266203e-06, 0.2662730803386e+01, 0.6282095334605e+01, + 0.5709824583726e-06, 0.3985828430833e+01, 0.6284056366286e+01, + 0.5143950896447e-06, 0.1308144688689e+01, 0.6290189305114e+01, + 0.5088010604546e-06, 0.5352817214804e+01, 0.6275962395778e+01, + 0.4960369085172e-06, 0.2644267922349e+01, 0.6127655567643e+01, + + 0.4803137891183e-06, 0.4008844192080e+01, 0.6438496133249e+01, + 0.5731747768225e-06, 0.3794550174597e+01, 0.3154687086868e+01, + 0.4735947960579e-06, 0.6107118308982e+01, 0.3128388763578e+01, + 0.4808348796625e-06, 0.4771458618163e+01, 0.8018209333619e+00, + 0.4115073743137e-06, 0.3327111335159e+01, 0.8429241228195e+01, + 0.5230575889287e-06, 0.5305708551694e+01, 0.1336797263425e+02, + 0.5133977889215e-06, 0.5784230738814e+01, 0.1235285262111e+02, + 0.5065815825327e-06, 0.2052064793679e+01, 0.1185621865188e+02, + 0.4339831593868e-06, 0.3644994195830e+01, 0.1726015463500e+02, + 0.3952928638953e-06, 0.4930376436758e+01, 0.5481254917084e+01, + + 0.4898498111942e-06, 0.4542084219731e+00, 0.9225539266174e+01, + 0.4757490209328e-06, 0.3161126388878e+01, 0.5856477690889e+01, + 0.4727701669749e-06, 0.6214993845446e+00, 0.2544314396739e+01, + 0.3800966681863e-06, 0.3040132339297e+01, 0.4265981595566e+00, + 0.3257301077939e-06, 0.8064977360087e+00, 0.3930209696940e+01, + 0.3255810528674e-06, 0.1974147981034e+01, 0.2146165377750e+01, + 0.3252029748187e-06, 0.2845924913135e+01, 0.4164311961999e+01, + 0.3255505635308e-06, 0.3017900824120e+01, 0.5088628793478e+01, + 0.2801345211990e-06, 0.6109717793179e+01, 0.1256967486051e+02, + 0.3688987740970e-06, 0.2911550235289e+01, 0.1807370494127e+02, + + 0.2475153429458e-06, 0.2179146025856e+01, 0.2629832328990e-01, + 0.3033457749150e-06, 0.1994161050744e+01, 0.4535059491685e+01, + 0.2186743763110e-06, 0.5125687237936e+01, 0.1137170464392e+02, + 0.2764777032774e-06, 0.4822646860252e+00, 0.1256262854127e+02, + 0.2199028768592e-06, 0.4637633293831e+01, 0.1255903824622e+02, + 0.2046482824760e-06, 0.1467038733093e+01, 0.7084896783808e+01, + 0.2611209147507e-06, 0.3044718783485e+00, 0.7143069561767e+02, + 0.2286079656818e-06, 0.4764220356805e+01, 0.8031092209206e+01, + 0.1855071202587e-06, 0.3383637774428e+01, 0.1748016358760e+01, + 0.2324669506784e-06, 0.6189088449251e+01, 0.1831953657923e+02, + + 0.1709528015688e-06, 0.5874966729774e+00, 0.4933208510675e+01, + 0.2168156875828e-06, 0.4302994009132e+01, 0.1044738781244e+02, + 0.2106675556535e-06, 0.3800475419891e+01, 0.7477522907414e+01, + 0.1430213830465e-06, 0.1294660846502e+01, 0.2942463415728e+01, + 0.1388396901944e-06, 0.4594797202114e+01, 0.8635942003952e+01, + 0.1922258844190e-06, 0.4943044543591e+00, 0.1729818233119e+02, + 0.1888460058292e-06, 0.2426943912028e+01, 0.1561374759853e+03, + 0.1789449386107e-06, 0.1582973303499e+00, 0.1592596075957e+01, + 0.1360803685374e-06, 0.5197240440504e+01, 0.1309584267300e+02, + 0.1504038014709e-06, 0.3120360916217e+01, 0.1649636139783e+02, + + 0.1382769533389e-06, 0.6164702888205e+01, 0.7632943190217e+01, + 0.1438059769079e-06, 0.1437423770979e+01, 0.2042657109477e+02, + 0.1326303260037e-06, 0.3609688799679e+01, 0.1213955354133e+02, + 0.1159244950540e-06, 0.5463018167225e+01, 0.5331357529664e+01, + 0.1433118149136e-06, 0.6028909912097e+01, 0.7342457794669e+01, + 0.1234623148594e-06, 0.3109645574997e+01, 0.6279485555400e+01, + 0.1233949875344e-06, 0.3539359332866e+01, 0.6286666145492e+01, + 0.9927196061299e-07, 0.1259321569772e+01, 0.7234794171227e+01, + 0.1242302191316e-06, 0.1065949392609e+01, 0.1511046609763e+02, + 0.1098402195201e-06, 0.2192508743837e+01, 0.1098880815746e+02, + + 0.1158191395315e-06, 0.4054411278650e+01, 0.5729506548653e+01, + 0.9048475596241e-07, 0.5429764748518e+01, 0.9623688285163e+01, + 0.8889853269023e-07, 0.5046586206575e+01, 0.6148010737701e+01, + 0.1048694242164e-06, 0.2628858030806e+01, 0.6836645152238e+01, + 0.1112308378646e-06, 0.4177292719907e+01, 0.1572083878776e+02, + 0.8631729709901e-07, 0.1601345232557e+01, 0.6418140963190e+01, + 0.8527816951664e-07, 0.2463888997513e+01, 0.1471231707864e+02, + 0.7892139456991e-07, 0.3154022088718e+01, 0.2118763888447e+01, + 0.1051782905236e-06, 0.4795035816088e+01, 0.1349867339771e+01, + 0.1048219943164e-06, 0.2952983395230e+01, 0.5999216516294e+01, + + 0.7435760775143e-07, 0.5420547991464e+01, 0.6040347114260e+01, + 0.9869574106949e-07, 0.3695646753667e+01, 0.6566935184597e+01, + 0.9156886364226e-07, 0.3922675306609e+01, 0.5643178611111e+01, + 0.7006834356188e-07, 0.1233968624861e+01, 0.6525804586632e+01, + 0.9806170182601e-07, 0.1919542280684e+01, 0.2122839202813e+02, + 0.9052289673607e-07, 0.4615902724369e+01, 0.4690479774488e+01, + 0.7554200867893e-07, 0.1236863719072e+01, 0.1253985337760e+02, + 0.8215741286498e-07, 0.3286800101559e+00, 0.1097355562493e+02, + 0.7185178575397e-07, 0.5880942158367e+01, 0.6245048154254e+01, + 0.7130726476180e-07, 0.7674871987661e+00, 0.6321103546637e+01, + + 0.6650894461162e-07, 0.6987129150116e+00, 0.5327476111629e+01, + 0.7396888823688e-07, 0.3576824794443e+01, 0.5368044267797e+00, + 0.7420588884775e-07, 0.5033615245369e+01, 0.2354323048545e+02, + 0.6141181642908e-07, 0.9449927045673e+00, 0.1296430071988e+02, + 0.6373557924058e-07, 0.6206342280341e+01, 0.9517183207817e+00, + 0.6359474329261e-07, 0.5036079095757e+01, 0.1990745094947e+01, + 0.5740173582646e-07, 0.6105106371350e+01, 0.9555997388169e+00, + 0.7019864084602e-07, 0.7237747359018e+00, 0.5225775174439e+00, + 0.6398054487042e-07, 0.3976367969666e+01, 0.2407292145756e+02, + 0.7797092650498e-07, 0.4305423910623e+01, 0.2200391463820e+02, + + 0.6466760000900e-07, 0.3500136825200e+01, 0.5230807360890e+01, + 0.7529417043890e-07, 0.3514779246100e+01, 0.1842262939178e+02, + 0.6924571140892e-07, 0.2743457928679e+01, 0.1554202828031e+00, + 0.6220798650222e-07, 0.2242598118209e+01, 0.1845107853235e+02, + 0.5870209391853e-07, 0.2332832707527e+01, 0.6398972393349e+00, + 0.6263953473888e-07, 0.2191105358956e+01, 0.6277552955062e+01, + 0.6257781390012e-07, 0.4457559396698e+01, 0.6288598745829e+01, + 0.5697304945123e-07, 0.3499234761404e+01, 0.1551045220144e+01, + 0.6335438746791e-07, 0.6441691079251e+00, 0.5216580451554e+01, + 0.6377258441152e-07, 0.2252599151092e+01, 0.5650292065779e+01, + + 0.6484841818165e-07, 0.1992812417646e+01, 0.1030928125552e+00, + 0.4735551485250e-07, 0.3744672082942e+01, 0.1431416805965e+02, + 0.4628595996170e-07, 0.1334226211745e+01, 0.5535693017924e+00, + 0.6258152336933e-07, 0.4395836159154e+01, 0.2608790314060e+02, + 0.6196171366594e-07, 0.2587043007997e+01, 0.8467247584405e+02, + 0.6159556952126e-07, 0.4782499769128e+01, 0.2394243902548e+03, + 0.4987741172394e-07, 0.7312257619924e+00, 0.7771377146812e+02, + 0.5459280703142e-07, 0.3001376372532e+01, 0.6179983037890e+01, + 0.4863461189999e-07, 0.3767222128541e+01, 0.9027992316901e+02, + 0.5349912093158e-07, 0.3663594450273e+01, 0.6386168663001e+01, + + 0.5673725607806e-07, 0.4331187919049e+01, 0.6915859635113e+01, + 0.4745485060512e-07, 0.5816195745518e+01, 0.6282970628506e+01, + 0.4745379005326e-07, 0.8323672435672e+00, 0.6283181072386e+01, + 0.4049002796321e-07, 0.3785023976293e+01, 0.6254626709878e+01, + 0.4247084014515e-07, 0.2378220728783e+01, 0.7875671926403e+01, + 0.4026912363055e-07, 0.2864103423269e+01, 0.6311524991013e+01, + 0.4062935011774e-07, 0.2415408595975e+01, 0.3634620989887e+01, + 0.5347771048509e-07, 0.3343479309801e+01, 0.2515860172507e+02, + 0.4829494136505e-07, 0.2821742398262e+01, 0.5760498333002e+01, + 0.4342554404599e-07, 0.5624662458712e+01, 0.7238675589263e+01, + + 0.4021599184361e-07, 0.5557250275009e+00, 0.1101510648075e+02, + 0.4104900474558e-07, 0.3296691780005e+01, 0.6709674010002e+01, + 0.4376532905131e-07, 0.3814443999443e+01, 0.6805653367890e+01, + 0.3314590480650e-07, 0.3560229189250e+01, 0.1259245002418e+02, + 0.3232421839643e-07, 0.5185389180568e+01, 0.1066495398892e+01, + 0.3541176318876e-07, 0.3921381909679e+01, 0.9917696840332e+01, + 0.3689831242681e-07, 0.4190658955386e+01, 0.1192625446156e+02, + 0.3890605376774e-07, 0.5546023371097e+01, 0.7478166569050e-01, + 0.3038559339780e-07, 0.6231032794494e+01, 0.1256621883632e+02, + 0.3137083969782e-07, 0.6207063419190e+01, 0.4292330755499e+01, + + 0.4024004081854e-07, 0.1195257375713e+01, 0.1334167431096e+02, + 0.3300234879283e-07, 0.1804694240998e+01, 0.1057540660594e+02, + 0.3635399155575e-07, 0.5597811343500e+01, 0.6208294184755e+01, + 0.3032668691356e-07, 0.3191059366530e+01, 0.1805292951336e+02, + 0.2809652069058e-07, 0.4094348032570e+01, 0.3523159621801e-02, + 0.3696955383823e-07, 0.5219282738794e+01, 0.5966683958112e+01, + 0.3562894142503e-07, 0.1037247544554e+01, 0.6357857516136e+01, + 0.3510598524148e-07, 0.1430020816116e+01, 0.6599467742779e+01, + 0.3617736142953e-07, 0.3002911403677e+01, 0.6019991944201e+01, + 0.2624524910730e-07, 0.2437046757292e+01, 0.6702560555334e+01, + + 0.2535824204490e-07, 0.1581594689647e+01, 0.3141537925223e+02, + 0.3519787226257e-07, 0.5379863121521e+01, 0.2505706758577e+03, + 0.2578406709982e-07, 0.4904222639329e+01, 0.1673046366289e+02, + 0.3423887981473e-07, 0.3646448997315e+01, 0.6546159756691e+01, + 0.2776083886467e-07, 0.3307829300144e+01, 0.1272157198369e+02, + 0.3379592818379e-07, 0.1747541251125e+01, 0.1494531617769e+02, + 0.3050255426284e-07, 0.1784689432607e-01, 0.4732030630302e+01, + 0.2652378350236e-07, 0.4420055276260e+01, 0.5863591145557e+01, + 0.2374498173768e-07, 0.3629773929208e+01, 0.2388894113936e+01, + 0.2716451255140e-07, 0.3079623706780e+01, 0.1202934727411e+02, + + 0.3038583699229e-07, 0.3312487903507e+00, 0.1256608456547e+02, + 0.2220681228760e-07, 0.5265520401774e+01, 0.1336244973887e+02, + 0.3044156540912e-07, 0.4766664081250e+01, 0.2908881142201e+02, + 0.2731859923561e-07, 0.5069146530691e+01, 0.1391601904066e+02, + 0.2285603018171e-07, 0.5954935112271e+01, 0.6076890225335e+01, + 0.2025006454555e-07, 0.4061789589267e+01, 0.4701116388778e+01, + 0.2012597519804e-07, 0.2485047705241e+01, 0.6262720680387e+01, + 0.2003406962258e-07, 0.4163779209320e+01, 0.6303431020504e+01, + 0.2207863441371e-07, 0.6923839133828e+00, 0.6489261475556e+01, + 0.2481374305624e-07, 0.5944173595676e+01, 0.1204357418345e+02, + + 0.2130923288870e-07, 0.4641013671967e+01, 0.5746271423666e+01, + 0.2446370543391e-07, 0.6125796518757e+01, 0.1495633313810e+00, + 0.1932492759052e-07, 0.2234572324504e+00, 0.1352175143971e+02, + 0.2600122568049e-07, 0.4281012405440e+01, 0.4590910121555e+01, + 0.2431754047488e-07, 0.1429943874870e+00, 0.1162474756779e+01, + 0.1875902869209e-07, 0.9781803816948e+00, 0.6279194432410e+01, + 0.1874381139426e-07, 0.5670368130173e+01, 0.6286957268481e+01, + 0.2156696047173e-07, 0.2008985006833e+01, 0.1813929450232e+02, + 0.1965076182484e-07, 0.2566186202453e+00, 0.4686889479442e+01, + 0.2334816372359e-07, 0.4408121891493e+01, 0.1002183730415e+02, + + 0.1869937408802e-07, 0.5272745038656e+01, 0.2427287361862e+00, + 0.2436236460883e-07, 0.4407720479029e+01, 0.9514313292143e+02, + 0.1761365216611e-07, 0.1943892315074e+00, 0.1351787002167e+02, + 0.2156289480503e-07, 0.1418570924545e+01, 0.6037244212485e+01, + 0.2164748979255e-07, 0.4724603439430e+01, 0.2301353951334e+02, + 0.2222286670853e-07, 0.2400266874598e+01, 0.1266924451345e+02, + 0.2070901414929e-07, 0.5230348028732e+01, 0.6528907488406e+01, + 0.1792745177020e-07, 0.2099190328945e+01, 0.6819880277225e+01, + 0.1841802068445e-07, 0.3467527844848e+00, 0.6514761976723e+02, + 0.1578401631718e-07, 0.7098642356340e+00, 0.2077542790660e-01, + + 0.1561690152531e-07, 0.5943349620372e+01, 0.6272439236156e+01, + 0.1558591045463e-07, 0.7040653478980e+00, 0.6293712464735e+01, + 0.1737356469576e-07, 0.4487064760345e+01, 0.1765478049437e+02, + 0.1434755619991e-07, 0.2993391570995e+01, 0.1102062672231e+00, + 0.1482187806654e-07, 0.2278049198251e+01, 0.1052268489556e+01, + 0.1424812827089e-07, 0.1682114725827e+01, 0.1311972100268e+02, + 0.1380282448623e-07, 0.3262668602579e+01, 0.1017725758696e+02, + 0.1811481244566e-07, 0.3187771221777e+01, 0.1887552587463e+02, + 0.1504446185696e-07, 0.5650162308647e+01, 0.7626583626240e-01, + 0.1740776154137e-07, 0.5487068607507e+01, 0.1965104848470e+02, + + 0.1374339536251e-07, 0.5745688172201e+01, 0.6016468784579e+01, + 0.1761377477704e-07, 0.5748060203659e+01, 0.2593412433514e+02, + 0.1535138225795e-07, 0.6226848505790e+01, 0.9411464614024e+01, + 0.1788140543676e-07, 0.6189318878563e+01, 0.3301902111895e+02, + 0.1375002807996e-07, 0.5371812884394e+01, 0.6327837846670e+00, + 0.1242115758632e-07, 0.1471687569712e+01, 0.3894181736510e+01, + 0.1450977333938e-07, 0.4143836662127e+01, 0.1277945078067e+02, + 0.1297579575023e-07, 0.9003477661957e+00, 0.6549682916313e+01, + 0.1462667934821e-07, 0.5760505536428e+01, 0.1863592847156e+02, + 0.1381774374799e-07, 0.1085471729463e+01, 0.2379164476796e+01, + + 0.1682333169307e-07, 0.5409870870133e+01, 0.1620077269078e+02, + 0.1190812918837e-07, 0.1397205174601e+01, 0.1149965630200e+02, + 0.1221434762106e-07, 0.9001804809095e+00, 0.1257326515556e+02, + 0.1549934644860e-07, 0.4262528275544e+01, 0.1820933031200e+02, + 0.1252138953050e-07, 0.1411642012027e+01, 0.6993008899458e+01, + 0.1237078905387e-07, 0.2844472403615e+01, 0.2435678079171e+02, + 0.1446953389615e-07, 0.5295835522223e+01, 0.3813291813120e-01, + 0.1388446457170e-07, 0.4969428135497e+01, 0.2458316379602e+00, + 0.1019339179228e-07, 0.2491369561806e+01, 0.6112403035119e+01, + 0.1258880815343e-07, 0.4679426248976e+01, 0.5429879531333e+01, + + 0.1297768238261e-07, 0.1074509953328e+01, 0.1249137003520e+02, + 0.9913505718094e-08, 0.4735097918224e+01, 0.6247047890016e+01, + 0.9830453155969e-08, 0.4158649187338e+01, 0.6453748665772e+01, + 0.1192615865309e-07, 0.3438208613699e+01, 0.6290122169689e+01, + 0.9835874798277e-08, 0.1913300781229e+01, 0.6319103810876e+01, + 0.9639087569277e-08, 0.9487683644125e+00, 0.8273820945392e+01, + 0.1175716107001e-07, 0.3228141664287e+01, 0.6276029531202e+01, + 0.1018926508678e-07, 0.2216607854300e+01, 0.1254537627298e+02, + 0.9500087869225e-08, 0.2625116459733e+01, 0.1256517118505e+02, + 0.9664192916575e-08, 0.5860562449214e+01, 0.6259197520765e+01, + + 0.9612858712203e-08, 0.7885682917381e+00, 0.6306954180126e+01, + 0.1117645675413e-07, 0.3932148831189e+01, 0.1779695906178e+02, + 0.1158864052160e-07, 0.9995605521691e+00, 0.1778273215245e+02, + 0.9021043467028e-08, 0.5263769742673e+01, 0.6172869583223e+01, + 0.8836134773563e-08, 0.1496843220365e+01, 0.1692165728891e+01, + 0.1045872200691e-07, 0.7009039517214e+00, 0.2204125344462e+00, + 0.1211463487798e-07, 0.4041544938511e+01, 0.8257698122054e+02, + 0.8541990804094e-08, 0.1447586692316e+01, 0.6393282117669e+01, + 0.1038720703636e-07, 0.4594249718112e+00, 0.1550861511662e+02, + 0.1126722351445e-07, 0.3925550579036e+01, 0.2061856251104e+00, + + 0.8697373859631e-08, 0.4411341856037e+01, 0.9491756770005e+00, + 0.8869380028441e-08, 0.2402659724813e+01, 0.3903911373650e+01, + 0.9247014693258e-08, 0.1401579743423e+01, 0.6267823317922e+01, + 0.9205062930950e-08, 0.5245978000814e+01, 0.6298328382969e+01, + 0.8000745038049e-08, 0.3590803356945e+01, 0.2648454860559e+01, + 0.9168973650819e-08, 0.2470150501679e+01, 0.1498544001348e+03, + 0.1075444949238e-07, 0.1328606161230e+01, 0.3694923081589e+02, + 0.7817298525817e-08, 0.6162256225998e+01, 0.4804209201333e+01, + 0.9541469226356e-08, 0.3942568967039e+01, 0.1256713221673e+02, + 0.9821910122027e-08, 0.2360246287233e+00, 0.1140367694411e+02, + + 0.9897822023777e-08, 0.4619805634280e+01, 0.2280573557157e+02, + 0.7737289283765e-08, 0.3784727847451e+01, 0.7834121070590e+01, + 0.9260204034710e-08, 0.2223352487601e+01, 0.2787043132925e+01, + 0.7320252888486e-08, 0.1288694636874e+01, 0.6282655592598e+01, + 0.7319785780946e-08, 0.5359869567774e+01, 0.6283496108294e+01, + 0.7147219933778e-08, 0.5516616675856e+01, 0.1725663147538e+02, + 0.7946502829878e-08, 0.2630459984567e+01, 0.1241073141809e+02, + 0.9001711808932e-08, 0.2849815827227e+01, 0.6281591679874e+01, + 0.8994041507257e-08, 0.3795244450750e+01, 0.6284560021018e+01, + 0.8298582787358e-08, 0.5236413127363e+00, 0.1241658836951e+02, + + 0.8526596520710e-08, 0.4794605424426e+01, 0.1098419223922e+02, + 0.8209822103197e-08, 0.1578752370328e+01, 0.1096996532989e+02, + 0.6357049861094e-08, 0.5708926113761e+01, 0.1596186371003e+01, + 0.7370473179049e-08, 0.3842402530241e+01, 0.4061219149443e+01, + 0.7232154664726e-08, 0.3067548981535e+01, 0.1610006857377e+03, + 0.6328765494903e-08, 0.1313930030069e+01, 0.1193336791622e+02, + 0.8030064908595e-08, 0.3488500408886e+01, 0.8460828644453e+00, + 0.6275464259232e-08, 0.1532061626198e+01, 0.8531963191132e+00, + 0.7051897446325e-08, 0.3285859929993e+01, 0.5849364236221e+01, + 0.6161593705428e-08, 0.1477341999464e+01, 0.5573142801433e+01, + + 0.7754683957278e-08, 0.1586118663096e+01, 0.8662240327241e+01, + 0.5889928990701e-08, 0.1304887868803e+01, 0.1232342296471e+02, + 0.5705756047075e-08, 0.4555333589350e+01, 0.1258692712880e+02, + 0.5964178808332e-08, 0.3001762842062e+01, 0.5333900173445e+01, + 0.6712446027467e-08, 0.4886780007595e+01, 0.1171295538178e+02, + 0.5941809275464e-08, 0.4701509603824e+01, 0.9779108567966e+01, + 0.5466993627395e-08, 0.4588357817278e+01, 0.1884211409667e+02, + 0.6340512090980e-08, 0.1164543038893e+01, 0.5217580628120e+02, + 0.6325505710045e-08, 0.3919171259645e+01, 0.1041998632314e+02, + 0.6164789509685e-08, 0.2143828253542e+01, 0.6151533897323e+01, + + 0.5263330812430e-08, 0.6066564434241e+01, 0.1885275071096e+02, + 0.5597087780221e-08, 0.2926316429472e+01, 0.4337116142245e+00, + 0.5396556236817e-08, 0.3244303591505e+01, 0.6286362197481e+01, + 0.5396615148223e-08, 0.3404304703662e+01, 0.6279789503410e+01, + 0.7091832443341e-08, 0.8532377803192e+00, 0.4907302013889e+01, + 0.6572352589782e-08, 0.4901966774419e+01, 0.1176433076753e+02, + 0.5960236060795e-08, 0.1874672315797e+01, 0.1422690933580e-01, + 0.5125480043511e-08, 0.3735726064334e+01, 0.1245594543367e+02, + 0.5928241866410e-08, 0.4502033899935e+01, 0.6414617803568e+01, + 0.5249600357424e-08, 0.4372334799878e+01, 0.1151388321134e+02, + + 0.6059171276087e-08, 0.2581617302908e+01, 0.6062663316000e+01, + 0.5295235081662e-08, 0.2974811513158e+01, 0.3496032717521e+01, + 0.5820561875933e-08, 0.1796073748244e+00, 0.2838593341516e+00, + 0.4754696606440e-08, 0.1981998136973e+01, 0.3104930017775e+01, + 0.6385053548955e-08, 0.2559174171605e+00, 0.6133512519065e+01, + 0.6589828273941e-08, 0.2750967106776e+01, 0.4087944051283e+02, + 0.5383376567189e-08, 0.6325947523578e+00, 0.2248384854122e+02, + 0.5928941683538e-08, 0.1672304519067e+01, 0.1581959461667e+01, + 0.4816060709794e-08, 0.3512566172575e+01, 0.9388005868221e+01, + 0.6003381586512e-08, 0.5610932219189e+01, 0.5326786718777e+01, + + 0.5504225393105e-08, 0.4037501131256e+01, 0.6503488384892e+01, + 0.5353772620129e-08, 0.6122774968240e+01, 0.1735668374386e+03, + 0.5786253768544e-08, 0.5527984999515e+01, 0.1350651127443e+00, + 0.5065706702002e-08, 0.9980765573624e+00, 0.1248988586463e+02, + 0.5972838885276e-08, 0.6044489493203e+01, 0.2673594526851e+02, + 0.5323585877961e-08, 0.3924265998147e+01, 0.4171425416666e+01, + 0.5210772682858e-08, 0.6220111376901e+01, 0.2460261242967e+02, + 0.4726549040535e-08, 0.3716043206862e+01, 0.7232251527446e+01, + 0.6029425105059e-08, 0.8548704071116e+00, 0.3227113045244e+03, + 0.4481542826513e-08, 0.1426925072829e+01, 0.5547199253223e+01, + + 0.5836024505068e-08, 0.7135651752625e-01, 0.7285056171570e+02, + 0.4137046613272e-08, 0.5330767643283e+01, 0.1087398597200e+02, + 0.5171977473924e-08, 0.4494262335353e+00, 0.1884570439172e+02, + 0.5694429833732e-08, 0.2952369582215e+01, 0.9723862754494e+02, + 0.4009158925298e-08, 0.3500003416535e+01, 0.6244942932314e+01, + 0.4784939596873e-08, 0.6196709413181e+01, 0.2929661536378e+02, + 0.3983725022610e-08, 0.5103690031897e+01, 0.4274518229222e+01, + 0.3870535232462e-08, 0.3187569587401e+01, 0.6321208768577e+01, + 0.5140501213951e-08, 0.1668924357457e+01, 0.1232032006293e+02, + 0.3849034819355e-08, 0.4445722510309e+01, 0.1726726808967e+02, + + 0.4002383075060e-08, 0.5226224152423e+01, 0.7018952447668e+01, + 0.3890719543549e-08, 0.4371166550274e+01, 0.1491901785440e+02, + 0.4887084607881e-08, 0.5973556689693e+01, 0.1478866649112e+01, + 0.3739939287592e-08, 0.2089084714600e+01, 0.6922973089781e+01, + 0.5031925918209e-08, 0.4658371936827e+01, 0.1715706182245e+02, + 0.4387748764954e-08, 0.4825580552819e+01, 0.2331413144044e+03, + 0.4147398098865e-08, 0.3739003524998e+01, 0.1376059875786e+02, + 0.3719089993586e-08, 0.1148941386536e+01, 0.6297302759782e+01, + 0.3934238461056e-08, 0.1559893008343e+01, 0.7872148766781e+01, + 0.3672471375622e-08, 0.5516145383612e+01, 0.6268848941110e+01, + + 0.3768911277583e-08, 0.6116053700563e+01, 0.4157198507331e+01, + 0.4033388417295e-08, 0.5076821746017e+01, 0.1567108171867e+02, + 0.3764194617832e-08, 0.8164676232075e+00, 0.3185192151914e+01, + 0.4840628226284e-08, 0.1360479453671e+01, 0.1252801878276e+02, + 0.4949443923785e-08, 0.2725622229926e+01, 0.1617106187867e+03, + 0.4117393089971e-08, 0.6054459628492e+00, 0.5642198095270e+01, + 0.3925754020428e-08, 0.8570462135210e+00, 0.2139354194808e+02, + 0.3630551757923e-08, 0.3552067338279e+01, 0.6294805223347e+01, + 0.3627274802357e-08, 0.3096565085313e+01, 0.6271346477544e+01, + 0.3806143885093e-08, 0.6367751709777e+00, 0.1725304118033e+02, + + 0.4433254641565e-08, 0.4848461503937e+01, 0.7445550607224e+01, + 0.3712319846576e-08, 0.1331950643655e+01, 0.4194847048887e+00, + 0.3849847534783e-08, 0.4958368297746e+00, 0.9562891316684e+00, + 0.3483955430165e-08, 0.2237215515707e+01, 0.1161697602389e+02, + 0.3961912730982e-08, 0.3332402188575e+01, 0.2277943724828e+02, + 0.3419978244481e-08, 0.5785600576016e+01, 0.1362553364512e+02, + 0.3329417758177e-08, 0.9812676559709e-01, 0.1685848245639e+02, + 0.4207206893193e-08, 0.9494780468236e+00, 0.2986433403208e+02, + 0.3268548976410e-08, 0.1739332095686e+00, 0.5749861718712e+01, + 0.3321880082685e-08, 0.1423354800666e+01, 0.6279143387820e+01, + + 0.4503173010852e-08, 0.2314972675293e+00, 0.1385561574497e+01, + 0.4316599090954e-08, 0.1012646782616e+00, 0.4176041334900e+01, + 0.3283493323850e-08, 0.5233306881265e+01, 0.6287008313071e+01, + 0.3164033542343e-08, 0.4005597257511e+01, 0.2099539292909e+02, + 0.4159720956725e-08, 0.5365676242020e+01, 0.5905702259363e+01, + 0.3565176892217e-08, 0.4284440620612e+01, 0.3932462625300e-02, + 0.3514440950221e-08, 0.4270562636575e+01, 0.7335344340001e+01, + 0.3540596871909e-08, 0.5953553201060e+01, 0.1234573916645e+02, + 0.2960769905118e-08, 0.1115180417718e+01, 0.2670964694522e+02, + 0.2962213739684e-08, 0.3863811918186e+01, 0.6408777551755e+00, + + 0.3883556700251e-08, 0.1268617928302e+01, 0.6660449441528e+01, + 0.2919225516346e-08, 0.4908605223265e+01, 0.1375773836557e+01, + 0.3115158863370e-08, 0.3744519976885e+01, 0.3802769619140e-01, + 0.4099438144212e-08, 0.4173244670532e+01, 0.4480965020977e+02, + 0.2899531858964e-08, 0.5910601428850e+01, 0.2059724391010e+02, + 0.3289733429855e-08, 0.2488050078239e+01, 0.1081813534213e+02, + 0.3933075612875e-08, 0.1122363652883e+01, 0.3773735910827e+00, + 0.3021403764467e-08, 0.4951973724904e+01, 0.2982630633589e+02, + 0.2798598949757e-08, 0.5117057845513e+01, 0.1937891852345e+02, + 0.3397421302707e-08, 0.6104159180476e+01, 0.6923953605621e+01, + + 0.3720398002179e-08, 0.1184933429829e+01, 0.3066615496545e+02, + 0.3598484186267e-08, 0.3505282086105e+01, 0.6147450479709e+01, + 0.3694594027310e-08, 0.2286651088141e+01, 0.2636725487657e+01, + 0.2680444152969e-08, 0.1871816775482e+00, 0.6816289982179e+01, + 0.3497574865641e-08, 0.3143251755431e+01, 0.6418701221183e+01, + 0.3130274129494e-08, 0.2462167316018e+01, 0.1235996607578e+02, + 0.3241119069551e-08, 0.4256374004686e+01, 0.1652265972112e+02, + 0.2601960842061e-08, 0.4970362941425e+01, 0.1045450126711e+02, + 0.2690601527504e-08, 0.2372657824898e+01, 0.3163918923335e+00, + 0.2908688152664e-08, 0.4232652627721e+01, 0.2828699048865e+02, + + 0.3120456131875e-08, 0.3925747001137e+00, 0.2195415756911e+02, + 0.3148855423384e-08, 0.3093478330445e+01, 0.1172006883645e+02, + 0.3051044261017e-08, 0.5560948248212e+01, 0.6055599646783e+01, + 0.2826006876660e-08, 0.5072790310072e+01, 0.5120601093667e+01, + 0.3100034191711e-08, 0.4998530231096e+01, 0.1799603123222e+02, + 0.2398771640101e-08, 0.2561739802176e+01, 0.6255674361143e+01, + 0.2384002842728e-08, 0.4087420284111e+01, 0.6310477339748e+01, + 0.2842146517568e-08, 0.2515048217955e+01, 0.5469525544182e+01, + 0.2847674371340e-08, 0.5235326497443e+01, 0.1034429499989e+02, + 0.2903722140764e-08, 0.1088200795797e+01, 0.6510552054109e+01, + + 0.3187610710605e-08, 0.4710624424816e+01, 0.1693792562116e+03, + 0.3048869992813e-08, 0.2857975896445e+00, 0.8390110365991e+01, + 0.2860216950984e-08, 0.2241619020815e+01, 0.2243449970715e+00, + 0.2701117683113e-08, 0.6651573305272e-01, 0.6129297044991e+01, + 0.2509891590152e-08, 0.1285135324585e+01, 0.1044027435778e+02, + 0.2623200252223e-08, 0.2981229834530e+00, 0.6436854655901e+01, + 0.2622541669202e-08, 0.6122470726189e+01, 0.9380959548977e+01, + 0.2818435667099e-08, 0.4251087148947e+01, 0.5934151399930e+01, + 0.2365196797465e-08, 0.3465070460790e+01, 0.2470570524223e+02, + 0.2358704646143e-08, 0.5791603815350e+01, 0.8671969964381e+01, + + 0.2388299481390e-08, 0.4142483772941e+01, 0.7096626156709e+01, + 0.1996041217224e-08, 0.2101901889496e+01, 0.1727188400790e+02, + 0.2687593060336e-08, 0.1526689456959e+01, 0.7075506709219e+02, + 0.2618913670810e-08, 0.2397684236095e+01, 0.6632000300961e+01, + 0.2571523050364e-08, 0.5751929456787e+00, 0.6206810014183e+01, + 0.2582135006946e-08, 0.5595464352926e+01, 0.4873985990671e+02, + 0.2372530190361e-08, 0.5092689490655e+01, 0.1590676413561e+02, + 0.2357178484712e-08, 0.4444363527851e+01, 0.3097883698531e+01, + 0.2451590394723e-08, 0.3108251687661e+01, 0.6612329252343e+00, + 0.2370045949608e-08, 0.2608133861079e+01, 0.3459636466239e+02, + + 0.2268997267358e-08, 0.3639717753384e+01, 0.2844914056730e-01, + 0.1731432137906e-08, 0.1741898445707e+00, 0.2019909489111e+02, + 0.1629869741622e-08, 0.3902225646724e+01, 0.3035599730800e+02, + 0.2206215801974e-08, 0.4971131250731e+01, 0.6281667977667e+01, + 0.2205469554680e-08, 0.1677462357110e+01, 0.6284483723224e+01, + 0.2148792362509e-08, 0.4236259604006e+01, 0.1980482729015e+02, + 0.1873733657847e-08, 0.5926814998687e+01, 0.2876692439167e+02, + 0.2026573758959e-08, 0.4349643351962e+01, 0.2449240616245e+02, + 0.1807770325110e-08, 0.5700940482701e+01, 0.2045286941806e+02, + 0.1881174408581e-08, 0.6601286363430e+00, 0.2358125818164e+02, + + 0.1368023671690e-08, 0.2211098592752e+01, 0.2473415438279e+02, + 0.1720017916280e-08, 0.4942488551129e+01, 0.1679593901136e+03, + 0.1702427665131e-08, 0.1452233856386e+01, 0.3338575901272e+03, + 0.1414032510054e-08, 0.5525357721439e+01, 0.1624205518357e+03, + 0.1652626045364e-08, 0.4108794283624e+01, 0.8956999012000e+02, + 0.1642957769686e-08, 0.7344335209984e+00, 0.5267006960365e+02, + 0.1614952403624e-08, 0.3541213951363e+01, 0.3332657872986e+02, + 0.1535988291188e-08, 0.4031094072151e+01, 0.3852657435933e+02, + 0.1593193738177e-08, 0.4185136203609e+01, 0.2282781046519e+03, + 0.1074569126382e-08, 0.1720485636868e+01, 0.8397383534231e+02, + + 0.1074408214509e-08, 0.2758613420318e+01, 0.8401985929482e+02, + 0.9700199670465e-09, 0.4216686842097e+01, 0.7826370942180e+02, + 0.1258433517061e-08, 0.2575068876639e+00, 0.3115650189215e+03, + 0.1240303229539e-08, 0.4800844956756e+00, 0.1784300471910e+03, + 0.9018345948127e-09, 0.3896756361552e+00, 0.5886454391678e+02, + 0.1135301432805e-08, 0.3700805023550e+00, 0.7842370451713e+02, + 0.9215887951370e-09, 0.4364579276638e+01, 0.1014262087719e+03, + 0.1055401054147e-08, 0.2156564222111e+01, 0.5660027930059e+02, + 0.1008725979831e-08, 0.5454015785234e+01, 0.4245678405627e+02, + 0.7217398104321e-09, 0.1597772562175e+01, 0.2457074661053e+03, + + 0.6912033134447e-09, 0.5824090621461e+01, 0.1679936946371e+03, + 0.6833881523549e-09, 0.3578778482835e+01, 0.6053048899753e+02, + 0.4887304205142e-09, 0.3724362812423e+01, 0.9656299901946e+02, + 0.5173709754788e-09, 0.5422427507933e+01, 0.2442876000072e+03, + 0.4671353097145e-09, 0.2396106924439e+01, 0.1435713242844e+03, + 0.5652608439480e-09, 0.2804028838685e+01, 0.8365903305582e+02, + 0.5604061331253e-09, 0.1638816006247e+01, 0.8433466158131e+02, + 0.4712723365400e-09, 0.8979003224474e+00, 0.3164282286739e+03, + 0.4909967465112e-09, 0.3210426725516e+01, 0.4059982187939e+03, + 0.4771358267658e-09, 0.5308027211629e+01, 0.1805255418145e+03, + + 0.3943451445989e-09, 0.2195145341074e+01, 0.2568537517081e+03, + 0.3952109120244e-09, 0.5081189491586e+01, 0.2449975330562e+03, + 0.3788134594789e-09, 0.4345171264441e+01, 0.1568131045107e+03, + 0.3738330190479e-09, 0.2613062847997e+01, 0.3948519331910e+03, + 0.3099866678136e-09, 0.2846760817689e+01, 0.1547176098872e+03, + 0.2002962716768e-09, 0.4921360989412e+01, 0.2268582385539e+03, + 0.2198291338754e-09, 0.1130360117454e+00, 0.1658638954901e+03, + 0.1491958330784e-09, 0.4228195232278e+01, 0.2219950288015e+03, + 0.1475384076173e-09, 0.3005721811604e+00, 0.3052819430710e+03, + 0.1661626624624e-09, 0.7830125621203e+00, 0.2526661704812e+03, + + 0.9015823460025e-10, 0.3807792942715e+01, 0.4171445043968e+03 }; + +/* Sun-to-Earth, T^0, Y */ + static const double e0y[] = { + 0.9998921098898e+00, 0.1826583913846e+00, 0.6283075850446e+01, + -0.2442700893735e-01, 0.0000000000000e+00, 0.0000000000000e+00, + 0.8352929742915e-02, 0.1395277998680e+00, 0.1256615170089e+02, + 0.1046697300177e-03, 0.9641423109763e-01, 0.1884922755134e+02, + 0.3110841876663e-04, 0.5381140401712e+01, 0.8399684731857e+02, + 0.2570269094593e-04, 0.5301016407128e+01, 0.5296909721118e+00, + 0.2147389623610e-04, 0.2662510869850e+01, 0.1577343543434e+01, + 0.1680344384050e-04, 0.5207904119704e+01, 0.6279552690824e+01, + 0.1679117312193e-04, 0.4582187486968e+01, 0.6286599010068e+01, + 0.1440512068440e-04, 0.1900688517726e+01, 0.2352866153506e+01, + + 0.1135139664999e-04, 0.5273108538556e+01, 0.5223693906222e+01, + 0.9345482571018e-05, 0.4503047687738e+01, 0.1203646072878e+02, + 0.9007418719568e-05, 0.1605621059637e+01, 0.1021328554739e+02, + 0.5671536712314e-05, 0.5812849070861e+00, 0.1059381944224e+01, + 0.7451401861666e-05, 0.2807346794836e+01, 0.3981490189893e+00, + 0.6393470057114e-05, 0.6029224133855e+01, 0.5753384878334e+01, + 0.6814275881697e-05, 0.6472990145974e+00, 0.4705732307012e+01, + 0.6113705628887e-05, 0.3813843419700e+01, 0.6812766822558e+01, + 0.4503851367273e-05, 0.4527804370996e+01, 0.5884926831456e+01, + 0.4522249141926e-05, 0.5991783029224e+01, 0.6256777527156e+01, + + 0.4501794307018e-05, 0.3798703844397e+01, 0.6309374173736e+01, + 0.5514927480180e-05, 0.3961257833388e+01, 0.5507553240374e+01, + 0.4062862799995e-05, 0.5256247296369e+01, 0.6681224869435e+01, + 0.5414900429712e-05, 0.5499032014097e+01, 0.7755226100720e+00, + 0.5463153987424e-05, 0.6173092454097e+01, 0.1414349524433e+02, + 0.5071611859329e-05, 0.2870244247651e+01, 0.7860419393880e+01, + 0.2195112094455e-05, 0.2952338617201e+01, 0.1150676975667e+02, + 0.2279139233919e-05, 0.5951775132933e+01, 0.7058598460518e+01, + 0.2278386100876e-05, 0.4845456398785e+01, 0.4694002934110e+01, + 0.2559088003308e-05, 0.6945321117311e+00, 0.1216800268190e+02, + + 0.2561079286856e-05, 0.6167224608301e+01, 0.7099330490126e+00, + 0.1792755796387e-05, 0.1400122509632e+01, 0.7962980379786e+00, + 0.1818715656502e-05, 0.4703347611830e+01, 0.6283142985870e+01, + 0.1818744924791e-05, 0.5086748900237e+01, 0.6283008715021e+01, + 0.1554518791390e-05, 0.5331008042713e-01, 0.2513230340178e+02, + 0.2063265737239e-05, 0.4283680484178e+01, 0.1179062909082e+02, + 0.1497613520041e-05, 0.6074207826073e+01, 0.5486777812467e+01, + 0.2000617940427e-05, 0.2501426281450e+01, 0.1778984560711e+02, + 0.1289731195580e-05, 0.3646340599536e+01, 0.7079373888424e+01, + 0.1282657998934e-05, 0.3232864804902e+01, 0.3738761453707e+01, + + 0.1528915968658e-05, 0.5581433416669e+01, 0.2132990797783e+00, + 0.1187304098432e-05, 0.5453576453694e+01, 0.9437762937313e+01, + 0.7842782928118e-06, 0.2823953922273e+00, 0.8827390247185e+01, + 0.7352892280868e-06, 0.1124369580175e+01, 0.1589072916335e+01, + 0.6570189360797e-06, 0.2089154042840e+01, 0.1176985366291e+02, + 0.6324967590410e-06, 0.6704855581230e+00, 0.6262300422539e+01, + 0.6298289872283e-06, 0.2836414855840e+01, 0.6303851278352e+01, + 0.6476686465855e-06, 0.4852433866467e+00, 0.7113454667900e-02, + 0.8587034651234e-06, 0.1453511005668e+01, 0.1672837615881e+03, + 0.8068948788113e-06, 0.9224087798609e+00, 0.6069776770667e+01, + + 0.8353786011661e-06, 0.4631707184895e+01, 0.3340612434717e+01, + 0.6009324532132e-06, 0.1829498827726e+01, 0.4136910472696e+01, + 0.7558158559566e-06, 0.2588596800317e+01, 0.6496374930224e+01, + 0.5809279504503e-06, 0.5516818853476e+00, 0.1097707878456e+02, + 0.5374131950254e-06, 0.6275674734960e+01, 0.1194447056968e+01, + 0.5711160507326e-06, 0.1091905956872e+01, 0.6282095334605e+01, + 0.5710183170746e-06, 0.2415001635090e+01, 0.6284056366286e+01, + 0.5144373590610e-06, 0.6020336443438e+01, 0.6290189305114e+01, + 0.5103108927267e-06, 0.3775634564605e+01, 0.6275962395778e+01, + 0.4960654697891e-06, 0.1073450946756e+01, 0.6127655567643e+01, + + 0.4786385689280e-06, 0.2431178012310e+01, 0.6438496133249e+01, + 0.6109911263665e-06, 0.5343356157914e+01, 0.3154687086868e+01, + 0.4839898944024e-06, 0.5830833594047e-01, 0.8018209333619e+00, + 0.4734822623919e-06, 0.4536080134821e+01, 0.3128388763578e+01, + 0.4834741473290e-06, 0.2585090489754e+00, 0.7084896783808e+01, + 0.5134858581156e-06, 0.4213317172603e+01, 0.1235285262111e+02, + 0.5064004264978e-06, 0.4814418806478e+00, 0.1185621865188e+02, + 0.3753476772761e-06, 0.1599953399788e+01, 0.8429241228195e+01, + 0.4935264014283e-06, 0.2157417556873e+01, 0.2544314396739e+01, + 0.3950929600897e-06, 0.3359394184254e+01, 0.5481254917084e+01, + + 0.4895849789777e-06, 0.5165704376558e+01, 0.9225539266174e+01, + 0.4215241688886e-06, 0.2065368800993e+01, 0.1726015463500e+02, + 0.3796773731132e-06, 0.1468606346612e+01, 0.4265981595566e+00, + 0.3114178142515e-06, 0.3615638079474e+01, 0.2146165377750e+01, + 0.3260664220838e-06, 0.4417134922435e+01, 0.4164311961999e+01, + 0.3976996123008e-06, 0.4700866883004e+01, 0.5856477690889e+01, + 0.2801459672924e-06, 0.4538902060922e+01, 0.1256967486051e+02, + 0.3638931868861e-06, 0.1334197991475e+01, 0.1807370494127e+02, + 0.2487013269476e-06, 0.3749275558275e+01, 0.2629832328990e-01, + 0.3034165481994e-06, 0.4236622030873e+00, 0.4535059491685e+01, + + 0.2676278825586e-06, 0.5970848007811e+01, 0.3930209696940e+01, + 0.2764903818918e-06, 0.5194636754501e+01, 0.1256262854127e+02, + 0.2485149930507e-06, 0.1002434207846e+01, 0.5088628793478e+01, + 0.2199305540941e-06, 0.3066773098403e+01, 0.1255903824622e+02, + 0.2571106500435e-06, 0.7588312459063e+00, 0.1336797263425e+02, + 0.2049751817158e-06, 0.3444977434856e+01, 0.1137170464392e+02, + 0.2599707296297e-06, 0.1873128542205e+01, 0.7143069561767e+02, + 0.1785018072217e-06, 0.5015891306615e+01, 0.1748016358760e+01, + 0.2324833891115e-06, 0.4618271239730e+01, 0.1831953657923e+02, + 0.1709711119545e-06, 0.5300003455669e+01, 0.4933208510675e+01, + + 0.2107159351716e-06, 0.2229819815115e+01, 0.7477522907414e+01, + 0.1750333080295e-06, 0.6161485880008e+01, 0.1044738781244e+02, + 0.2000598210339e-06, 0.2967357299999e+01, 0.8031092209206e+01, + 0.1380920248681e-06, 0.3027007923917e+01, 0.8635942003952e+01, + 0.1412460470299e-06, 0.6037597163798e+01, 0.2942463415728e+01, + 0.1888459803001e-06, 0.8561476243374e+00, 0.1561374759853e+03, + 0.1788370542585e-06, 0.4869736290209e+01, 0.1592596075957e+01, + 0.1360893296167e-06, 0.3626411886436e+01, 0.1309584267300e+02, + 0.1506846530160e-06, 0.1550975377427e+01, 0.1649636139783e+02, + 0.1800913376176e-06, 0.2075826033190e+01, 0.1729818233119e+02, + + 0.1436261390649e-06, 0.6148876420255e+01, 0.2042657109477e+02, + 0.1220227114151e-06, 0.4382583879906e+01, 0.7632943190217e+01, + 0.1337883603592e-06, 0.2036644327361e+01, 0.1213955354133e+02, + 0.1159326650738e-06, 0.3892276994687e+01, 0.5331357529664e+01, + 0.1352853128569e-06, 0.1447950649744e+01, 0.1673046366289e+02, + 0.1433408296083e-06, 0.4457854692961e+01, 0.7342457794669e+01, + 0.1234701666518e-06, 0.1538818147151e+01, 0.6279485555400e+01, + 0.1234027192007e-06, 0.1968523220760e+01, 0.6286666145492e+01, + 0.1244024091797e-06, 0.5779803499985e+01, 0.1511046609763e+02, + 0.1097934945516e-06, 0.6210975221388e+00, 0.1098880815746e+02, + + 0.1254611329856e-06, 0.2591963807998e+01, 0.1572083878776e+02, + 0.1158247286784e-06, 0.2483612812670e+01, 0.5729506548653e+01, + 0.9039078252960e-07, 0.3857554579796e+01, 0.9623688285163e+01, + 0.9108024978836e-07, 0.5826368512984e+01, 0.7234794171227e+01, + 0.8887068108436e-07, 0.3475694573987e+01, 0.6148010737701e+01, + 0.8632374035438e-07, 0.3059070488983e-01, 0.6418140963190e+01, + 0.7893186992967e-07, 0.1583194837728e+01, 0.2118763888447e+01, + 0.8297650201172e-07, 0.8519770534637e+00, 0.1471231707864e+02, + 0.1019759578988e-06, 0.1319598738732e+00, 0.1349867339771e+01, + 0.1010037696236e-06, 0.9937860115618e+00, 0.6836645152238e+01, + + 0.1047727548266e-06, 0.1382138405399e+01, 0.5999216516294e+01, + 0.7351993881086e-07, 0.3833397851735e+01, 0.6040347114260e+01, + 0.9868771092341e-07, 0.2124913814390e+01, 0.6566935184597e+01, + 0.7007321959390e-07, 0.5946305343763e+01, 0.6525804586632e+01, + 0.6861411679709e-07, 0.4574654977089e+01, 0.7238675589263e+01, + 0.7554519809614e-07, 0.5949232686844e+01, 0.1253985337760e+02, + 0.9541880448335e-07, 0.3495242990564e+01, 0.2122839202813e+02, + 0.7185606722155e-07, 0.4310113471661e+01, 0.6245048154254e+01, + 0.7131360871710e-07, 0.5480309323650e+01, 0.6321103546637e+01, + 0.6651142021039e-07, 0.5411097713654e+01, 0.5327476111629e+01, + + 0.8538618213667e-07, 0.1827849973951e+01, 0.1101510648075e+02, + 0.8634954288044e-07, 0.5443584943349e+01, 0.5643178611111e+01, + 0.7449415051484e-07, 0.2011535459060e+01, 0.5368044267797e+00, + 0.7421047599169e-07, 0.3464562529249e+01, 0.2354323048545e+02, + 0.6140694354424e-07, 0.5657556228815e+01, 0.1296430071988e+02, + 0.6353525143033e-07, 0.3463816593821e+01, 0.1990745094947e+01, + 0.6221964013447e-07, 0.1532259498697e+01, 0.9517183207817e+00, + 0.5852480257244e-07, 0.1375396598875e+01, 0.9555997388169e+00, + 0.6398637498911e-07, 0.2405645801972e+01, 0.2407292145756e+02, + 0.7039744069878e-07, 0.5397541799027e+01, 0.5225775174439e+00, + + 0.6977997694382e-07, 0.4762347105419e+01, 0.1097355562493e+02, + 0.7460629558396e-07, 0.2711944692164e+01, 0.2200391463820e+02, + 0.5376577536101e-07, 0.2352980430239e+01, 0.1431416805965e+02, + 0.7530607893556e-07, 0.1943940180699e+01, 0.1842262939178e+02, + 0.6822928971605e-07, 0.4337651846959e+01, 0.1554202828031e+00, + 0.6220772380094e-07, 0.6716871369278e+00, 0.1845107853235e+02, + 0.6586950799043e-07, 0.2229714460505e+01, 0.5216580451554e+01, + 0.5873800565771e-07, 0.7627013920580e+00, 0.6398972393349e+00, + 0.6264346929745e-07, 0.6202785478961e+00, 0.6277552955062e+01, + 0.6257929115669e-07, 0.2886775596668e+01, 0.6288598745829e+01, + + 0.5343536033409e-07, 0.1977241012051e+01, 0.4690479774488e+01, + 0.5587849781714e-07, 0.1922923484825e+01, 0.1551045220144e+01, + 0.6905100845603e-07, 0.3570757164631e+01, 0.1030928125552e+00, + 0.6178957066649e-07, 0.5197558947765e+01, 0.5230807360890e+01, + 0.6187270224331e-07, 0.8193497368922e+00, 0.5650292065779e+01, + 0.5385664291426e-07, 0.5406336665586e+01, 0.7771377146812e+02, + 0.6329363917926e-07, 0.2837760654536e+01, 0.2608790314060e+02, + 0.4546018761604e-07, 0.2933580297050e+01, 0.5535693017924e+00, + 0.6196091049375e-07, 0.4157871494377e+01, 0.8467247584405e+02, + 0.6159555108218e-07, 0.3211703561703e+01, 0.2394243902548e+03, + + 0.4995340539317e-07, 0.1459098102922e+01, 0.4732030630302e+01, + 0.5457031243572e-07, 0.1430457676136e+01, 0.6179983037890e+01, + 0.4863461418397e-07, 0.2196425916730e+01, 0.9027992316901e+02, + 0.5342947626870e-07, 0.2086612890268e+01, 0.6386168663001e+01, + 0.5674296648439e-07, 0.2760204966535e+01, 0.6915859635113e+01, + 0.4745783120161e-07, 0.4245368971862e+01, 0.6282970628506e+01, + 0.4745676961198e-07, 0.5544725787016e+01, 0.6283181072386e+01, + 0.4049796869973e-07, 0.2213984363586e+01, 0.6254626709878e+01, + 0.4248333596940e-07, 0.8075781952896e+00, 0.7875671926403e+01, + 0.4027178070205e-07, 0.1293268540378e+01, 0.6311524991013e+01, + + 0.4066543943476e-07, 0.3986141175804e+01, 0.3634620989887e+01, + 0.4858863787880e-07, 0.1276112738231e+01, 0.5760498333002e+01, + 0.5277398263530e-07, 0.4916111741527e+01, 0.2515860172507e+02, + 0.4105635656559e-07, 0.1725805864426e+01, 0.6709674010002e+01, + 0.4376781925772e-07, 0.2243642442106e+01, 0.6805653367890e+01, + 0.3235827894693e-07, 0.3614135118271e+01, 0.1066495398892e+01, + 0.3073244740308e-07, 0.2460873393460e+01, 0.5863591145557e+01, + 0.3088609271373e-07, 0.5678431771790e+01, 0.9917696840332e+01, + 0.3393022279836e-07, 0.3814017477291e+01, 0.1391601904066e+02, + 0.3038686508802e-07, 0.4660216229171e+01, 0.1256621883632e+02, + + 0.4019677752497e-07, 0.5906906243735e+01, 0.1334167431096e+02, + 0.3288834998232e-07, 0.9536146445882e+00, 0.1620077269078e+02, + 0.3889973794631e-07, 0.3942205097644e+01, 0.7478166569050e-01, + 0.3050438987141e-07, 0.1624810271286e+01, 0.1805292951336e+02, + 0.3601142564638e-07, 0.4030467142575e+01, 0.6208294184755e+01, + 0.3689015557141e-07, 0.3648878818694e+01, 0.5966683958112e+01, + 0.3563471893565e-07, 0.5749584017096e+01, 0.6357857516136e+01, + 0.2776183170667e-07, 0.2630124187070e+01, 0.3523159621801e-02, + 0.2922350530341e-07, 0.1790346403629e+01, 0.1272157198369e+02, + 0.3511076917302e-07, 0.6142198301611e+01, 0.6599467742779e+01, + + 0.3619351007632e-07, 0.1432421386492e+01, 0.6019991944201e+01, + 0.2561254711098e-07, 0.2302822475792e+01, 0.1259245002418e+02, + 0.2626903942920e-07, 0.8660470994571e+00, 0.6702560555334e+01, + 0.2550187397083e-07, 0.6069721995383e+01, 0.1057540660594e+02, + 0.2535873526138e-07, 0.1079020331795e-01, 0.3141537925223e+02, + 0.3519786153847e-07, 0.3809066902283e+01, 0.2505706758577e+03, + 0.3424651492873e-07, 0.2075435114417e+01, 0.6546159756691e+01, + 0.2372676630861e-07, 0.2057803120154e+01, 0.2388894113936e+01, + 0.2710980779541e-07, 0.1510068488010e+01, 0.1202934727411e+02, + 0.3038710889704e-07, 0.5043617528901e+01, 0.1256608456547e+02, + + 0.2220364130585e-07, 0.3694793218205e+01, 0.1336244973887e+02, + 0.3025880825460e-07, 0.5450618999049e-01, 0.2908881142201e+02, + 0.2784493486864e-07, 0.3381164084502e+01, 0.1494531617769e+02, + 0.2294414142438e-07, 0.4382309025210e+01, 0.6076890225335e+01, + 0.2012723294724e-07, 0.9142212256518e+00, 0.6262720680387e+01, + 0.2036357831958e-07, 0.5676172293154e+01, 0.4701116388778e+01, + 0.2003474823288e-07, 0.2592767977625e+01, 0.6303431020504e+01, + 0.2207144900109e-07, 0.5404976271180e+01, 0.6489261475556e+01, + 0.2481664905135e-07, 0.4373284587027e+01, 0.1204357418345e+02, + 0.2674949182295e-07, 0.5859182188482e+01, 0.4590910121555e+01, + + 0.2450554720322e-07, 0.4555381557451e+01, 0.1495633313810e+00, + 0.2601975986457e-07, 0.3933165584959e+01, 0.1965104848470e+02, + 0.2199860022848e-07, 0.5227977189087e+01, 0.1351787002167e+02, + 0.2448121172316e-07, 0.4858060353949e+01, 0.1162474756779e+01, + 0.1876014864049e-07, 0.5690546553605e+01, 0.6279194432410e+01, + 0.1874513219396e-07, 0.4099539297446e+01, 0.6286957268481e+01, + 0.2156380842559e-07, 0.4382594769913e+00, 0.1813929450232e+02, + 0.1981691240061e-07, 0.1829784152444e+01, 0.4686889479442e+01, + 0.2329992648539e-07, 0.2836254278973e+01, 0.1002183730415e+02, + 0.1765184135302e-07, 0.2803494925833e+01, 0.4292330755499e+01, + + 0.2436368366085e-07, 0.2836897959677e+01, 0.9514313292143e+02, + 0.2164089203889e-07, 0.6127522446024e+01, 0.6037244212485e+01, + 0.1847755034221e-07, 0.3683163635008e+01, 0.2427287361862e+00, + 0.1674798769966e-07, 0.3316993867246e+00, 0.1311972100268e+02, + 0.2222542124356e-07, 0.8294097805480e+00, 0.1266924451345e+02, + 0.2071074505925e-07, 0.3659492220261e+01, 0.6528907488406e+01, + 0.1608224471835e-07, 0.4774492067182e+01, 0.1352175143971e+02, + 0.1857583439071e-07, 0.2873120597682e+01, 0.8662240327241e+01, + 0.1793018836159e-07, 0.5282441177929e+00, 0.6819880277225e+01, + 0.1575391221692e-07, 0.1320789654258e+01, 0.1102062672231e+00, + + 0.1840132009557e-07, 0.1917110916256e+01, 0.6514761976723e+02, + 0.1760917288281e-07, 0.2972635937132e+01, 0.5746271423666e+01, + 0.1561779518516e-07, 0.4372569261981e+01, 0.6272439236156e+01, + 0.1558687885205e-07, 0.5416424926425e+01, 0.6293712464735e+01, + 0.1951359382579e-07, 0.3094448898752e+01, 0.2301353951334e+02, + 0.1569144275614e-07, 0.2802103689808e+01, 0.1765478049437e+02, + 0.1479130389462e-07, 0.2136435020467e+01, 0.2077542790660e-01, + 0.1467828510764e-07, 0.7072627435674e+00, 0.1052268489556e+01, + 0.1627627337440e-07, 0.3947607143237e+01, 0.6327837846670e+00, + 0.1503498479758e-07, 0.4079248909190e+01, 0.7626583626240e-01, + + 0.1297967708237e-07, 0.6269637122840e+01, 0.1149965630200e+02, + 0.1374416896634e-07, 0.4175657970702e+01, 0.6016468784579e+01, + 0.1783812325219e-07, 0.1476540547560e+01, 0.3301902111895e+02, + 0.1525884228756e-07, 0.4653477715241e+01, 0.9411464614024e+01, + 0.1451067396763e-07, 0.2573001128225e+01, 0.1277945078067e+02, + 0.1297713111950e-07, 0.5612799618771e+01, 0.6549682916313e+01, + 0.1462784012820e-07, 0.4189661623870e+01, 0.1863592847156e+02, + 0.1384185980007e-07, 0.2656915472196e+01, 0.2379164476796e+01, + 0.1221497599801e-07, 0.5612515760138e+01, 0.1257326515556e+02, + 0.1560574525896e-07, 0.4783414317919e+01, 0.1887552587463e+02, + + 0.1544598372036e-07, 0.2694431138063e+01, 0.1820933031200e+02, + 0.1531678928696e-07, 0.4105103489666e+01, 0.2593412433514e+02, + 0.1349321503795e-07, 0.3082437194015e+00, 0.5120601093667e+01, + 0.1252030290917e-07, 0.6124072334087e+01, 0.6993008899458e+01, + 0.1459243816687e-07, 0.3733103981697e+01, 0.3813291813120e-01, + 0.1226103625262e-07, 0.1267127706817e+01, 0.2435678079171e+02, + 0.1019449641504e-07, 0.4367790112269e+01, 0.1725663147538e+02, + 0.1380789433607e-07, 0.3387201768700e+01, 0.2458316379602e+00, + 0.1019453421658e-07, 0.9204143073737e+00, 0.6112403035119e+01, + 0.1297929434405e-07, 0.5786874896426e+01, 0.1249137003520e+02, + + 0.9912677786097e-08, 0.3164232870746e+01, 0.6247047890016e+01, + 0.9829386098599e-08, 0.2586762413351e+01, 0.6453748665772e+01, + 0.1226807746104e-07, 0.6239068436607e+01, 0.5429879531333e+01, + 0.1192691755997e-07, 0.1867380051424e+01, 0.6290122169689e+01, + 0.9836499227081e-08, 0.3424716293727e+00, 0.6319103810876e+01, + 0.9642862564285e-08, 0.5661372990657e+01, 0.8273820945392e+01, + 0.1165184404862e-07, 0.5768367239093e+01, 0.1778273215245e+02, + 0.1175794418818e-07, 0.1657351222943e+01, 0.6276029531202e+01, + 0.1018948635601e-07, 0.6458292350865e+00, 0.1254537627298e+02, + 0.9500383606676e-08, 0.1054306140741e+01, 0.1256517118505e+02, + + 0.1227512202906e-07, 0.2505278379114e+01, 0.2248384854122e+02, + 0.9664792009993e-08, 0.4289737277000e+01, 0.6259197520765e+01, + 0.9613285666331e-08, 0.5500597673141e+01, 0.6306954180126e+01, + 0.1117906736211e-07, 0.2361405953468e+01, 0.1779695906178e+02, + 0.9611378640782e-08, 0.2851310576269e+01, 0.2061856251104e+00, + 0.8845354852370e-08, 0.6208777705343e+01, 0.1692165728891e+01, + 0.1054046966600e-07, 0.5413091423934e+01, 0.2204125344462e+00, + 0.1215539124483e-07, 0.5613969479755e+01, 0.8257698122054e+02, + 0.9932460955209e-08, 0.1106124877015e+01, 0.1017725758696e+02, + 0.8785804715043e-08, 0.2869224476477e+01, 0.9491756770005e+00, + + 0.8538084097562e-08, 0.6159640899344e+01, 0.6393282117669e+01, + 0.8648994369529e-08, 0.1374901198784e+01, 0.4804209201333e+01, + 0.1039063219067e-07, 0.5171080641327e+01, 0.1550861511662e+02, + 0.8867983926439e-08, 0.8317320304902e+00, 0.3903911373650e+01, + 0.8327495955244e-08, 0.3605591969180e+01, 0.6172869583223e+01, + 0.9243088356133e-08, 0.6114299196843e+01, 0.6267823317922e+01, + 0.9205657357835e-08, 0.3675153683737e+01, 0.6298328382969e+01, + 0.1033269714606e-07, 0.3313328813024e+01, 0.5573142801433e+01, + 0.8001706275552e-08, 0.2019980960053e+01, 0.2648454860559e+01, + 0.9171858254191e-08, 0.8992015524177e+00, 0.1498544001348e+03, + + 0.1075327150242e-07, 0.2898669963648e+01, 0.3694923081589e+02, + 0.9884866689828e-08, 0.4946715904478e+01, 0.1140367694411e+02, + 0.9541835576677e-08, 0.2371787888469e+01, 0.1256713221673e+02, + 0.7739903376237e-08, 0.2213775190612e+01, 0.7834121070590e+01, + 0.7311962684106e-08, 0.3429378787739e+01, 0.1192625446156e+02, + 0.9724904869624e-08, 0.6195878564404e+01, 0.2280573557157e+02, + 0.9251628983612e-08, 0.6511509527390e+00, 0.2787043132925e+01, + 0.7320763787842e-08, 0.6001083639421e+01, 0.6282655592598e+01, + 0.7320296650962e-08, 0.3789073265087e+01, 0.6283496108294e+01, + 0.7947032271039e-08, 0.1059659582204e+01, 0.1241073141809e+02, + + 0.9005277053115e-08, 0.1280315624361e+01, 0.6281591679874e+01, + 0.8995601652048e-08, 0.2224439106766e+01, 0.6284560021018e+01, + 0.8288040568796e-08, 0.5234914433867e+01, 0.1241658836951e+02, + 0.6359381347255e-08, 0.4137989441490e+01, 0.1596186371003e+01, + 0.8699572228626e-08, 0.1758411009497e+01, 0.6133512519065e+01, + 0.6456797542736e-08, 0.5919285089994e+01, 0.1685848245639e+02, + 0.7424573475452e-08, 0.5414616938827e+01, 0.4061219149443e+01, + 0.7235671196168e-08, 0.1496516557134e+01, 0.1610006857377e+03, + 0.8104015182733e-08, 0.1919918242764e+01, 0.8460828644453e+00, + 0.8098576535937e-08, 0.3819615855458e+01, 0.3894181736510e+01, + + 0.6275292346625e-08, 0.6244264115141e+01, 0.8531963191132e+00, + 0.6052432989112e-08, 0.5037731872610e+00, 0.1567108171867e+02, + 0.5705651535817e-08, 0.2984557271995e+01, 0.1258692712880e+02, + 0.5789650115138e-08, 0.6087038140697e+01, 0.1193336791622e+02, + 0.5512132153377e-08, 0.5855668994076e+01, 0.1232342296471e+02, + 0.7388890819102e-08, 0.2443128574740e+01, 0.4907302013889e+01, + 0.5467593991798e-08, 0.3017561234194e+01, 0.1884211409667e+02, + 0.6388519802999e-08, 0.5887386712935e+01, 0.5217580628120e+02, + 0.6106777149944e-08, 0.3483461059895e+00, 0.1422690933580e-01, + 0.7383420275489e-08, 0.5417387056707e+01, 0.2358125818164e+02, + + 0.5505208141738e-08, 0.2848193644783e+01, 0.1151388321134e+02, + 0.6310757462877e-08, 0.2349882520828e+01, 0.1041998632314e+02, + 0.6166904929691e-08, 0.5728575944077e+00, 0.6151533897323e+01, + 0.5263442042754e-08, 0.4495796125937e+01, 0.1885275071096e+02, + 0.5591828082629e-08, 0.1355441967677e+01, 0.4337116142245e+00, + 0.5397051680497e-08, 0.1673422864307e+01, 0.6286362197481e+01, + 0.5396992745159e-08, 0.1833502206373e+01, 0.6279789503410e+01, + 0.6572913000726e-08, 0.3331122065824e+01, 0.1176433076753e+02, + 0.5123421866413e-08, 0.2165327142679e+01, 0.1245594543367e+02, + 0.5930495725999e-08, 0.2931146089284e+01, 0.6414617803568e+01, + + 0.6431797403933e-08, 0.4134407994088e+01, 0.1350651127443e+00, + 0.5003182207604e-08, 0.3805420303749e+01, 0.1096996532989e+02, + 0.5587731032504e-08, 0.1082469260599e+01, 0.6062663316000e+01, + 0.5935263407816e-08, 0.8384333678401e+00, 0.5326786718777e+01, + 0.4756019827760e-08, 0.3552588749309e+01, 0.3104930017775e+01, + 0.6599951172637e-08, 0.4320826409528e+01, 0.4087944051283e+02, + 0.5902606868464e-08, 0.4811879454445e+01, 0.5849364236221e+01, + 0.5921147809031e-08, 0.9942628922396e-01, 0.1581959461667e+01, + 0.5505382581266e-08, 0.2466557607764e+01, 0.6503488384892e+01, + 0.5353771071862e-08, 0.4551978748683e+01, 0.1735668374386e+03, + + 0.5063282210946e-08, 0.5710812312425e+01, 0.1248988586463e+02, + 0.5926120403383e-08, 0.1333998428358e+01, 0.2673594526851e+02, + 0.5211016176149e-08, 0.4649315360760e+01, 0.2460261242967e+02, + 0.5347075084894e-08, 0.5512754081205e+01, 0.4171425416666e+01, + 0.4872609773574e-08, 0.1308025299938e+01, 0.5333900173445e+01, + 0.4727711321420e-08, 0.2144908368062e+01, 0.7232251527446e+01, + 0.6029426018652e-08, 0.5567259412084e+01, 0.3227113045244e+03, + 0.4321485284369e-08, 0.5230667156451e+01, 0.9388005868221e+01, + 0.4476406760553e-08, 0.6134081115303e+01, 0.5547199253223e+01, + 0.5835268277420e-08, 0.4783808492071e+01, 0.7285056171570e+02, + + 0.5172183602748e-08, 0.5161817911099e+01, 0.1884570439172e+02, + 0.5693571465184e-08, 0.1381646203111e+01, 0.9723862754494e+02, + 0.4060634965349e-08, 0.3876705259495e+00, 0.4274518229222e+01, + 0.3967398770473e-08, 0.5029491776223e+01, 0.3496032717521e+01, + 0.3943754005255e-08, 0.1923162955490e+01, 0.6244942932314e+01, + 0.4781323427824e-08, 0.4633332586423e+01, 0.2929661536378e+02, + 0.3871483781204e-08, 0.1616650009743e+01, 0.6321208768577e+01, + 0.5141741733997e-08, 0.9817316704659e-01, 0.1232032006293e+02, + 0.4002385978497e-08, 0.3656161212139e+01, 0.7018952447668e+01, + 0.4901092604097e-08, 0.4404098713092e+01, 0.1478866649112e+01, + + 0.3740932630345e-08, 0.5181188732639e+00, 0.6922973089781e+01, + 0.4387283718538e-08, 0.3254859566869e+01, 0.2331413144044e+03, + 0.5019197802033e-08, 0.3086773224677e+01, 0.1715706182245e+02, + 0.3834931695175e-08, 0.2797882673542e+01, 0.1491901785440e+02, + 0.3760413942497e-08, 0.2892676280217e+01, 0.1726726808967e+02, + 0.3719717204628e-08, 0.5861046025739e+01, 0.6297302759782e+01, + 0.4145623530149e-08, 0.2168239627033e+01, 0.1376059875786e+02, + 0.3932788425380e-08, 0.6271811124181e+01, 0.7872148766781e+01, + 0.3686377476857e-08, 0.3936853151404e+01, 0.6268848941110e+01, + 0.3779077950339e-08, 0.1404148734043e+01, 0.4157198507331e+01, + + 0.4091334550598e-08, 0.2452436180854e+01, 0.9779108567966e+01, + 0.3926694536146e-08, 0.6102292739040e+01, 0.1098419223922e+02, + 0.4841000253289e-08, 0.6072760457276e+01, 0.1252801878276e+02, + 0.4949340130240e-08, 0.1154832815171e+01, 0.1617106187867e+03, + 0.3761557737360e-08, 0.5527545321897e+01, 0.3185192151914e+01, + 0.3647396268188e-08, 0.1525035688629e+01, 0.6271346477544e+01, + 0.3932405074189e-08, 0.5570681040569e+01, 0.2139354194808e+02, + 0.3631322501141e-08, 0.1981240601160e+01, 0.6294805223347e+01, + 0.4130007425139e-08, 0.2050060880201e+01, 0.2195415756911e+02, + 0.4433905965176e-08, 0.3277477970321e+01, 0.7445550607224e+01, + + 0.3851814176947e-08, 0.5210690074886e+01, 0.9562891316684e+00, + 0.3485807052785e-08, 0.6653274904611e+00, 0.1161697602389e+02, + 0.3979772816991e-08, 0.1767941436148e+01, 0.2277943724828e+02, + 0.3402607460500e-08, 0.3421746306465e+01, 0.1087398597200e+02, + 0.4049993000926e-08, 0.1127144787547e+01, 0.3163918923335e+00, + 0.3420511182382e-08, 0.4214794779161e+01, 0.1362553364512e+02, + 0.3640772365012e-08, 0.5324905497687e+01, 0.1725304118033e+02, + 0.3323037987501e-08, 0.6135761838271e+01, 0.6279143387820e+01, + 0.4503141663637e-08, 0.1802305450666e+01, 0.1385561574497e+01, + 0.4314560055588e-08, 0.4812299731574e+01, 0.4176041334900e+01, + + 0.3294226949110e-08, 0.3657547059723e+01, 0.6287008313071e+01, + 0.3215657197281e-08, 0.4866676894425e+01, 0.5749861718712e+01, + 0.4129362656266e-08, 0.3809342558906e+01, 0.5905702259363e+01, + 0.3137762976388e-08, 0.2494635174443e+01, 0.2099539292909e+02, + 0.3514010952384e-08, 0.2699961831678e+01, 0.7335344340001e+01, + 0.3327607571530e-08, 0.3318457714816e+01, 0.5436992986000e+01, + 0.3541066946675e-08, 0.4382703582466e+01, 0.1234573916645e+02, + 0.3216179847052e-08, 0.5271066317054e+01, 0.3802769619140e-01, + 0.2959045059570e-08, 0.5819591585302e+01, 0.2670964694522e+02, + 0.3884040326665e-08, 0.5980934960428e+01, 0.6660449441528e+01, + + 0.2922027539886e-08, 0.3337290282483e+01, 0.1375773836557e+01, + 0.4110846382042e-08, 0.5742978187327e+01, 0.4480965020977e+02, + 0.2934508411032e-08, 0.2278075804200e+01, 0.6408777551755e+00, + 0.3966896193000e-08, 0.5835747858477e+01, 0.3773735910827e+00, + 0.3286695827610e-08, 0.5838898193902e+01, 0.3932462625300e-02, + 0.3720643094196e-08, 0.1122212337858e+01, 0.1646033343740e+02, + 0.3285508906174e-08, 0.9182250996416e+00, 0.1081813534213e+02, + 0.3753880575973e-08, 0.5174761973266e+01, 0.5642198095270e+01, + 0.3022129385587e-08, 0.3381611020639e+01, 0.2982630633589e+02, + 0.2798569205621e-08, 0.3546193723922e+01, 0.1937891852345e+02, + + 0.3397872070505e-08, 0.4533203197934e+01, 0.6923953605621e+01, + 0.3708099772977e-08, 0.2756168198616e+01, 0.3066615496545e+02, + 0.3599283541510e-08, 0.1934395469918e+01, 0.6147450479709e+01, + 0.3688702753059e-08, 0.7149920971109e+00, 0.2636725487657e+01, + 0.2681084724003e-08, 0.4899819493154e+01, 0.6816289982179e+01, + 0.3495993460759e-08, 0.1572418915115e+01, 0.6418701221183e+01, + 0.3130770324995e-08, 0.8912190180489e+00, 0.1235996607578e+02, + 0.2744353821941e-08, 0.3800821940055e+01, 0.2059724391010e+02, + 0.2842732906341e-08, 0.2644717440029e+01, 0.2828699048865e+02, + 0.3046882682154e-08, 0.3987793020179e+01, 0.6055599646783e+01, + + 0.2399072455143e-08, 0.9908826440764e+00, 0.6255674361143e+01, + 0.2384306274204e-08, 0.2516149752220e+01, 0.6310477339748e+01, + 0.2977324500559e-08, 0.5849195642118e+01, 0.1652265972112e+02, + 0.3062835258972e-08, 0.1681660100162e+01, 0.1172006883645e+02, + 0.3109682589231e-08, 0.5804143987737e+00, 0.2751146787858e+02, + 0.2903920355299e-08, 0.5800768280123e+01, 0.6510552054109e+01, + 0.2823221989212e-08, 0.9241118370216e+00, 0.5469525544182e+01, + 0.3187949696649e-08, 0.3139776445735e+01, 0.1693792562116e+03, + 0.2922559771655e-08, 0.3549440782984e+01, 0.2630839062450e+00, + 0.2436302066603e-08, 0.4735540696319e+01, 0.3946258593675e+00, + + 0.3049473043606e-08, 0.4998289124561e+01, 0.8390110365991e+01, + 0.2863682575784e-08, 0.6709515671102e+00, 0.2243449970715e+00, + 0.2641750517966e-08, 0.5410978257284e+01, 0.2986433403208e+02, + 0.2704093466243e-08, 0.4778317207821e+01, 0.6129297044991e+01, + 0.2445522177011e-08, 0.6009020662222e+01, 0.1171295538178e+02, + 0.2623608810230e-08, 0.5010449777147e+01, 0.6436854655901e+01, + 0.2079259704053e-08, 0.5980943768809e+01, 0.2019909489111e+02, + 0.2820225596771e-08, 0.2679965110468e+01, 0.5934151399930e+01, + 0.2365221950927e-08, 0.1894231148810e+01, 0.2470570524223e+02, + 0.2359682077149e-08, 0.4220752950780e+01, 0.8671969964381e+01, + + 0.2387577137206e-08, 0.2571783940617e+01, 0.7096626156709e+01, + 0.1982102089816e-08, 0.5169765997119e+00, 0.1727188400790e+02, + 0.2687502389925e-08, 0.6239078264579e+01, 0.7075506709219e+02, + 0.2207751669135e-08, 0.2031184412677e+01, 0.4377611041777e+01, + 0.2618370214274e-08, 0.8266079985979e+00, 0.6632000300961e+01, + 0.2591951887361e-08, 0.8819350522008e+00, 0.4873985990671e+02, + 0.2375055656248e-08, 0.3520944177789e+01, 0.1590676413561e+02, + 0.2472019978911e-08, 0.1551431908671e+01, 0.6612329252343e+00, + 0.2368157127199e-08, 0.4178610147412e+01, 0.3459636466239e+02, + 0.1764846605693e-08, 0.1506764000157e+01, 0.1980094587212e+02, + + 0.2291769608798e-08, 0.2118250611782e+01, 0.2844914056730e-01, + 0.2209997316943e-08, 0.3363255261678e+01, 0.2666070658668e+00, + 0.2292699097923e-08, 0.4200423956460e+00, 0.1484170571900e-02, + 0.1629683015329e-08, 0.2331362582487e+01, 0.3035599730800e+02, + 0.2206492862426e-08, 0.3400274026992e+01, 0.6281667977667e+01, + 0.2205746568257e-08, 0.1066051230724e+00, 0.6284483723224e+01, + 0.2026310767991e-08, 0.2779066487979e+01, 0.2449240616245e+02, + 0.1762977622163e-08, 0.9951450691840e+00, 0.2045286941806e+02, + 0.1368535049606e-08, 0.6402447365817e+00, 0.2473415438279e+02, + 0.1720598775450e-08, 0.2303524214705e+00, 0.1679593901136e+03, + + 0.1702429015449e-08, 0.6164622655048e+01, 0.3338575901272e+03, + 0.1414033197685e-08, 0.3954561185580e+01, 0.1624205518357e+03, + 0.1573768958043e-08, 0.2028286308984e+01, 0.3144167757552e+02, + 0.1650705184447e-08, 0.2304040666128e+01, 0.5267006960365e+02, + 0.1651087618855e-08, 0.2538461057280e+01, 0.8956999012000e+02, + 0.1616409518983e-08, 0.5111054348152e+01, 0.3332657872986e+02, + 0.1537175173581e-08, 0.5601130666603e+01, 0.3852657435933e+02, + 0.1593191980553e-08, 0.2614340453411e+01, 0.2282781046519e+03, + 0.1499480170643e-08, 0.3624721577264e+01, 0.2823723341956e+02, + 0.1493807843235e-08, 0.4214569879008e+01, 0.2876692439167e+02, + + 0.1074571199328e-08, 0.1496911744704e+00, 0.8397383534231e+02, + 0.1074406983417e-08, 0.1187817671922e+01, 0.8401985929482e+02, + 0.9757576855851e-09, 0.2655703035858e+01, 0.7826370942180e+02, + 0.1258432887565e-08, 0.4969896184844e+01, 0.3115650189215e+03, + 0.1240336343282e-08, 0.5192460776926e+01, 0.1784300471910e+03, + 0.9016107005164e-09, 0.1960356923057e+01, 0.5886454391678e+02, + 0.1135392360918e-08, 0.5082427809068e+01, 0.7842370451713e+02, + 0.9216046089565e-09, 0.2793775037273e+01, 0.1014262087719e+03, + 0.1061276615030e-08, 0.3726144311409e+01, 0.5660027930059e+02, + 0.1010110596263e-08, 0.7404080708937e+00, 0.4245678405627e+02, + + 0.7217424756199e-09, 0.2697449980577e-01, 0.2457074661053e+03, + 0.6912003846756e-09, 0.4253296276335e+01, 0.1679936946371e+03, + 0.6871814664847e-09, 0.5148072412354e+01, 0.6053048899753e+02, + 0.4887158016343e-09, 0.2153581148294e+01, 0.9656299901946e+02, + 0.5161802866314e-09, 0.3852750634351e+01, 0.2442876000072e+03, + 0.5652599559057e-09, 0.1233233356270e+01, 0.8365903305582e+02, + 0.4710812608586e-09, 0.5610486976767e+01, 0.3164282286739e+03, + 0.4909977500324e-09, 0.1639629524123e+01, 0.4059982187939e+03, + 0.4772641839378e-09, 0.3737100368583e+01, 0.1805255418145e+03, + 0.4487562567153e-09, 0.1158417054478e+00, 0.8433466158131e+02, + + 0.3943441230497e-09, 0.6243502862796e+00, 0.2568537517081e+03, + 0.3952236913598e-09, 0.3510377382385e+01, 0.2449975330562e+03, + 0.3788898363417e-09, 0.5916128302299e+01, 0.1568131045107e+03, + 0.3738329328831e-09, 0.1042266763456e+01, 0.3948519331910e+03, + 0.2451199165151e-09, 0.1166788435700e+01, 0.1435713242844e+03, + 0.2436734402904e-09, 0.3254726114901e+01, 0.2268582385539e+03, + 0.2213605274325e-09, 0.1687210598530e+01, 0.1658638954901e+03, + 0.1491521204829e-09, 0.2657541786794e+01, 0.2219950288015e+03, + 0.1474995329744e-09, 0.5013089805819e+01, 0.3052819430710e+03, + 0.1661939475656e-09, 0.5495315428418e+01, 0.2526661704812e+03, + + 0.9015946748003e-10, 0.2236989966505e+01, 0.4171445043968e+03 }; + +/* Sun-to-Earth, T^0, Z */ + static const double e0z[] = { + 0.2796207639075e-05, 0.3198701560209e+01, 0.8433466158131e+02, + 0.1016042198142e-05, 0.5422360395913e+01, 0.5507553240374e+01, + 0.8044305033647e-06, 0.3880222866652e+01, 0.5223693906222e+01, + 0.4385347909274e-06, 0.3704369937468e+01, 0.2352866153506e+01, + 0.3186156414906e-06, 0.3999639363235e+01, 0.1577343543434e+01, + 0.2272412285792e-06, 0.3984738315952e+01, 0.1047747311755e+01, + 0.1645620103007e-06, 0.3565412516841e+01, 0.5856477690889e+01, + 0.1815836921166e-06, 0.4984507059020e+01, 0.6283075850446e+01, + 0.1447461676364e-06, 0.3702753570108e+01, 0.9437762937313e+01, + 0.1430760876382e-06, 0.3409658712357e+01, 0.1021328554739e+02, + + 0.1120445753226e-06, 0.4829561570246e+01, 0.1414349524433e+02, + 0.1090232840797e-06, 0.2080729178066e+01, 0.6812766822558e+01, + 0.9715727346551e-07, 0.3476295881948e+01, 0.4694002934110e+01, + 0.1036267136217e-06, 0.4056639536648e+01, 0.7109288135493e+02, + 0.8752665271340e-07, 0.4448159519911e+01, 0.5753384878334e+01, + 0.8331864956004e-07, 0.4991704044208e+01, 0.7084896783808e+01, + 0.6901658670245e-07, 0.4325358994219e+01, 0.6275962395778e+01, + 0.9144536848998e-07, 0.1141826375363e+01, 0.6620890113188e+01, + 0.7205085037435e-07, 0.3624344170143e+01, 0.5296909721118e+00, + 0.7697874654176e-07, 0.5554257458998e+01, 0.1676215758509e+03, + + 0.5197545738384e-07, 0.6251760961735e+01, 0.1807370494127e+02, + 0.5031345378608e-07, 0.2497341091913e+01, 0.4705732307012e+01, + 0.4527110205840e-07, 0.2335079920992e+01, 0.6309374173736e+01, + 0.4753355798089e-07, 0.7094148987474e+00, 0.5884926831456e+01, + 0.4296951977516e-07, 0.1101916352091e+01, 0.6681224869435e+01, + 0.3855341568387e-07, 0.1825495405486e+01, 0.5486777812467e+01, + 0.5253930970990e-07, 0.4424740687208e+01, 0.7860419393880e+01, + 0.4024630496471e-07, 0.5120498157053e+01, 0.1336797263425e+02, + 0.4061069791453e-07, 0.6029771435451e+01, 0.3930209696940e+01, + 0.3797883804205e-07, 0.4435193600836e+00, 0.3154687086868e+01, + + 0.2933033225587e-07, 0.5124157356507e+01, 0.1059381944224e+01, + 0.3503000930426e-07, 0.5421830162065e+01, 0.6069776770667e+01, + 0.3670096214050e-07, 0.4582101667297e+01, 0.1219403291462e+02, + 0.2905609437008e-07, 0.1926566420072e+01, 0.1097707878456e+02, + 0.2466827821713e-07, 0.6090174539834e+00, 0.6496374930224e+01, + 0.2691647295332e-07, 0.1393432595077e+01, 0.2200391463820e+02, + 0.2150554667946e-07, 0.4308671715951e+01, 0.5643178611111e+01, + 0.2237481922680e-07, 0.8133968269414e+00, 0.8635942003952e+01, + 0.1817741038157e-07, 0.3755205127454e+01, 0.3340612434717e+01, + 0.2227820762132e-07, 0.2759558596664e+01, 0.1203646072878e+02, + + 0.1944713772307e-07, 0.5699645869121e+01, 0.1179062909082e+02, + 0.1527340520662e-07, 0.1986749091746e+01, 0.3981490189893e+00, + 0.1577282574914e-07, 0.3205017217983e+01, 0.5088628793478e+01, + 0.1424738825424e-07, 0.6256747903666e+01, 0.2544314396739e+01, + 0.1616563121701e-07, 0.2601671259394e+00, 0.1729818233119e+02, + 0.1401210391692e-07, 0.4686939173506e+01, 0.7058598460518e+01, + 0.1488726974214e-07, 0.2815862451372e+01, 0.2593412433514e+02, + 0.1692626442388e-07, 0.4956894109797e+01, 0.1564752902480e+03, + 0.1123571582910e-07, 0.2381192697696e+01, 0.3738761453707e+01, + 0.9903308606317e-08, 0.4294851657684e+01, 0.9225539266174e+01, + + 0.9174533187191e-08, 0.3075171510642e+01, 0.4164311961999e+01, + 0.8645985631457e-08, 0.5477534821633e+00, 0.8429241228195e+01, + -0.1085876492688e-07, 0.0000000000000e+00, 0.0000000000000e+00, + 0.9264309077815e-08, 0.5968571670097e+01, 0.7079373888424e+01, + 0.8243116984954e-08, 0.1489098777643e+01, 0.1044738781244e+02, + 0.8268102113708e-08, 0.3512977691983e+01, 0.1150676975667e+02, + 0.9043613988227e-08, 0.1290704408221e+00, 0.1101510648075e+02, + 0.7432912038789e-08, 0.1991086893337e+01, 0.2608790314060e+02, + 0.8586233727285e-08, 0.4238357924414e+01, 0.2986433403208e+02, + 0.7612230060131e-08, 0.2911090150166e+01, 0.4732030630302e+01, + + 0.7097787751408e-08, 0.1908938392390e+01, 0.8031092209206e+01, + 0.7640237040175e-08, 0.6129219000168e+00, 0.7962980379786e+00, + 0.7070445688081e-08, 0.1380417036651e+01, 0.2146165377750e+01, + 0.7690770957702e-08, 0.1680504249084e+01, 0.2122839202813e+02, + 0.8051292542594e-08, 0.5127423484511e+01, 0.2942463415728e+01, + 0.5902709104515e-08, 0.2020274190917e+01, 0.7755226100720e+00, + 0.5134567496462e-08, 0.2606778676418e+01, 0.1256615170089e+02, + 0.5525802046102e-08, 0.1613011769663e+01, 0.8018209333619e+00, + 0.5880724784221e-08, 0.4604483417236e+01, 0.4690479774488e+01, + 0.5211699081370e-08, 0.5718964114193e+01, 0.8827390247185e+01, + + 0.4891849573562e-08, 0.3689658932196e+01, 0.2132990797783e+00, + 0.5150246069997e-08, 0.4099769855122e+01, 0.6480980550449e+02, + 0.5102434319633e-08, 0.5660834602509e+01, 0.3379454372902e+02, + 0.5083405254252e-08, 0.9842221218974e+00, 0.4136910472696e+01, + 0.4206562585682e-08, 0.1341363634163e+00, 0.3128388763578e+01, + 0.4663249683579e-08, 0.8130132735866e+00, 0.5216580451554e+01, + 0.4099474416530e-08, 0.5791497770644e+01, 0.4265981595566e+00, + 0.4628251220767e-08, 0.1249802769331e+01, 0.1572083878776e+02, + 0.5024068728142e-08, 0.4795684802743e+01, 0.6290189305114e+01, + 0.5120234327758e-08, 0.3810420387208e+01, 0.5230807360890e+01, + + 0.5524029815280e-08, 0.1029264714351e+01, 0.2397622045175e+03, + 0.4757415718860e-08, 0.3528044781779e+01, 0.1649636139783e+02, + 0.3915786131127e-08, 0.5593889282646e+01, 0.1589072916335e+01, + 0.4869053149991e-08, 0.3299636454433e+01, 0.7632943190217e+01, + 0.3649365703729e-08, 0.1286049002584e+01, 0.6206810014183e+01, + 0.3992493949002e-08, 0.3100307589464e+01, 0.2515860172507e+02, + 0.3320247477418e-08, 0.6212683940807e+01, 0.1216800268190e+02, + 0.3287123739696e-08, 0.4699118445928e+01, 0.7234794171227e+01, + 0.3472776811103e-08, 0.2630507142004e+01, 0.7342457794669e+01, + 0.3423253294767e-08, 0.2946432844305e+01, 0.9623688285163e+01, + + 0.3896173898244e-08, 0.1224834179264e+01, 0.6438496133249e+01, + 0.3388455337924e-08, 0.1543807616351e+01, 0.1494531617769e+02, + 0.3062704716523e-08, 0.1191777572310e+01, 0.8662240327241e+01, + 0.3270075600400e-08, 0.5483498767737e+01, 0.1194447056968e+01, + 0.3101209215259e-08, 0.8000833804348e+00, 0.3772475342596e+02, + 0.2780883347311e-08, 0.4077980721888e+00, 0.5863591145557e+01, + 0.2903605931824e-08, 0.2617490302147e+01, 0.1965104848470e+02, + 0.2682014743119e-08, 0.2634703158290e+01, 0.7238675589263e+01, + 0.2534360108492e-08, 0.6102446114873e+01, 0.6836645152238e+01, + 0.2392564882509e-08, 0.3681820208691e+01, 0.5849364236221e+01, + + 0.2656667254856e-08, 0.6216045388886e+01, 0.6133512519065e+01, + 0.2331242096773e-08, 0.5864949777744e+01, 0.4535059491685e+01, + 0.2287898363668e-08, 0.4566628532802e+01, 0.7477522907414e+01, + 0.2336944521306e-08, 0.2442722126930e+01, 0.1137170464392e+02, + 0.3156632236269e-08, 0.1626628050682e+01, 0.2509084901204e+03, + 0.2982612402766e-08, 0.2803604512609e+01, 0.1748016358760e+01, + 0.2774031674807e-08, 0.4654002897158e+01, 0.8223916695780e+02, + 0.2295236548638e-08, 0.4326518333253e+01, 0.3378142627421e+00, + 0.2190714699873e-08, 0.4519614578328e+01, 0.2908881142201e+02, + 0.2191495845045e-08, 0.3012626912549e+01, 0.1673046366289e+02, + + 0.2492901628386e-08, 0.1290101424052e+00, 0.1543797956245e+03, + 0.1993778064319e-08, 0.3864046799414e+01, 0.1778984560711e+02, + 0.1898146479022e-08, 0.5053777235891e+01, 0.2042657109477e+02, + 0.1918280127634e-08, 0.2222470192548e+01, 0.4165496312290e+02, + 0.1916351061607e-08, 0.8719067257774e+00, 0.7737595720538e+02, + 0.1834720181466e-08, 0.4031491098040e+01, 0.2358125818164e+02, + 0.1249201523806e-08, 0.5938379466835e+01, 0.3301902111895e+02, + 0.1477304050539e-08, 0.6544722606797e+00, 0.9548094718417e+02, + 0.1264316431249e-08, 0.2059072853236e+01, 0.8399684731857e+02, + 0.1203526495039e-08, 0.3644813532605e+01, 0.4558517281984e+02, + + 0.9221681059831e-09, 0.3241815055602e+01, 0.7805158573086e+02, + 0.7849278367646e-09, 0.5043812342457e+01, 0.5217580628120e+02, + 0.7983392077387e-09, 0.5000024502753e+01, 0.1501922143975e+03, + 0.7925395431654e-09, 0.1398734871821e-01, 0.9061773743175e+02, + 0.7640473285886e-09, 0.5067111723130e+01, 0.4951538251678e+02, + 0.5398937754482e-09, 0.5597382200075e+01, 0.1613385000004e+03, + 0.5626247550193e-09, 0.2601338209422e+01, 0.7318837597844e+02, + 0.5525197197855e-09, 0.5814832109256e+01, 0.1432335100216e+03, + 0.5407629837898e-09, 0.3384820609076e+01, 0.3230491187871e+03, + 0.3856739119801e-09, 0.1072391840473e+01, 0.2334791286671e+03, + + 0.3856425239987e-09, 0.2369540393327e+01, 0.1739046517013e+03, + 0.4350867755983e-09, 0.5255575751082e+01, 0.1620484330494e+03, + 0.3844113924996e-09, 0.5482356246182e+01, 0.9757644180768e+02, + 0.2854869155431e-09, 0.9573634763143e+00, 0.1697170704744e+03, + 0.1719227671416e-09, 0.1887203025202e+01, 0.2265204242912e+03, + 0.1527846879755e-09, 0.3982183931157e+01, 0.3341954043900e+03, + 0.1128229264847e-09, 0.2787457156298e+01, 0.3119028331842e+03 }; + +/* Sun-to-Earth, T^1, X */ + static const double e1x[] = { + 0.1234046326004e-05, 0.0000000000000e+00, 0.0000000000000e+00, + 0.5150068824701e-06, 0.6002664557501e+01, 0.1256615170089e+02, + 0.1290743923245e-07, 0.5959437664199e+01, 0.1884922755134e+02, + 0.1068615564952e-07, 0.2015529654209e+01, 0.6283075850446e+01, + 0.2079619142538e-08, 0.1732960531432e+01, 0.6279552690824e+01, + 0.2078009243969e-08, 0.4915604476996e+01, 0.6286599010068e+01, + 0.6206330058856e-09, 0.3616457953824e+00, 0.4705732307012e+01, + 0.5989335313746e-09, 0.3802607304474e+01, 0.6256777527156e+01, + 0.5958495663840e-09, 0.2845866560031e+01, 0.6309374173736e+01, + 0.4866923261539e-09, 0.5213203771824e+01, 0.7755226100720e+00, + + 0.4267785823142e-09, 0.4368189727818e+00, 0.1059381944224e+01, + 0.4610675141648e-09, 0.1837249181372e-01, 0.7860419393880e+01, + 0.3626989993973e-09, 0.2161590545326e+01, 0.5753384878334e+01, + 0.3563071194389e-09, 0.1452631954746e+01, 0.5884926831456e+01, + 0.3557015642807e-09, 0.4470593393054e+01, 0.6812766822558e+01, + 0.3210412089122e-09, 0.5195926078314e+01, 0.6681224869435e+01, + 0.2875473577986e-09, 0.5916256610193e+01, 0.2513230340178e+02, + 0.2842913681629e-09, 0.1149902426047e+01, 0.6127655567643e+01, + 0.2751248215916e-09, 0.5502088574662e+01, 0.6438496133249e+01, + 0.2481432881127e-09, 0.2921989846637e+01, 0.5486777812467e+01, + + 0.2059885976560e-09, 0.3718070376585e+01, 0.7079373888424e+01, + 0.2015522342591e-09, 0.5979395259740e+01, 0.6290189305114e+01, + 0.1995364084253e-09, 0.6772087985494e+00, 0.6275962395778e+01, + 0.1957436436943e-09, 0.2899210654665e+01, 0.5507553240374e+01, + 0.1651609818948e-09, 0.6228206482192e+01, 0.1150676975667e+02, + 0.1822980550699e-09, 0.1469348746179e+01, 0.1179062909082e+02, + 0.1675223159760e-09, 0.3813910555688e+01, 0.7058598460518e+01, + 0.1706491764745e-09, 0.3004380506684e+00, 0.7113454667900e-02, + 0.1392952362615e-09, 0.1440393973406e+01, 0.7962980379786e+00, + 0.1209868266342e-09, 0.4150425791727e+01, 0.4694002934110e+01, + + 0.1009827202611e-09, 0.3290040429843e+01, 0.3738761453707e+01, + 0.1047261388602e-09, 0.4229590090227e+01, 0.6282095334605e+01, + 0.1047006652004e-09, 0.2418967680575e+01, 0.6284056366286e+01, + 0.9609993143095e-10, 0.4627943659201e+01, 0.6069776770667e+01, + 0.9590900593873e-10, 0.1894393939924e+01, 0.4136910472696e+01, + 0.9146249188071e-10, 0.2010647519562e+01, 0.6496374930224e+01, + 0.8545274480290e-10, 0.5529846956226e-01, 0.1194447056968e+01, + 0.8224377881194e-10, 0.1254304102174e+01, 0.1589072916335e+01, + 0.6183529510410e-10, 0.3360862168815e+01, 0.8827390247185e+01, + 0.6259255147141e-10, 0.4755628243179e+01, 0.8429241228195e+01, + + 0.5539291694151e-10, 0.5371746955142e+01, 0.4933208510675e+01, + 0.7328259466314e-10, 0.4927699613906e+00, 0.4535059491685e+01, + 0.6017835843560e-10, 0.5776682001734e-01, 0.1255903824622e+02, + 0.7079827775243e-10, 0.4395059432251e+01, 0.5088628793478e+01, + 0.5170358878213e-10, 0.5154062619954e+01, 0.1176985366291e+02, + 0.4872301838682e-10, 0.6289611648973e+00, 0.6040347114260e+01, + 0.5249869411058e-10, 0.5617272046949e+01, 0.3154687086868e+01, + 0.4716172354411e-10, 0.3965901800877e+01, 0.5331357529664e+01, + 0.4871214940964e-10, 0.4627507050093e+01, 0.1256967486051e+02, + 0.4598076850751e-10, 0.6023631226459e+01, 0.6525804586632e+01, + + 0.4562196089485e-10, 0.4138562084068e+01, 0.3930209696940e+01, + 0.4325493872224e-10, 0.1330845906564e+01, 0.7632943190217e+01, + 0.5673781176748e-10, 0.2558752615657e+01, 0.5729506548653e+01, + 0.3961436642503e-10, 0.2728071734630e+01, 0.7234794171227e+01, + 0.5101868209058e-10, 0.4113444965144e+01, 0.6836645152238e+01, + 0.5257043167676e-10, 0.6195089830590e+01, 0.8031092209206e+01, + 0.5076613989393e-10, 0.2305124132918e+01, 0.7477522907414e+01, + 0.3342169352778e-10, 0.5415998155071e+01, 0.1097707878456e+02, + 0.3545881983591e-10, 0.3727160564574e+01, 0.4164311961999e+01, + 0.3364063738599e-10, 0.2901121049204e+00, 0.1137170464392e+02, + + 0.3357039670776e-10, 0.1652229354331e+01, 0.5223693906222e+01, + 0.4307412268687e-10, 0.4938909587445e+01, 0.1592596075957e+01, + 0.3405769115435e-10, 0.2408890766511e+01, 0.3128388763578e+01, + 0.3001926198480e-10, 0.4862239006386e+01, 0.1748016358760e+01, + 0.2778264787325e-10, 0.5241168661353e+01, 0.7342457794669e+01, + 0.2676159480666e-10, 0.3423593942199e+01, 0.2146165377750e+01, + 0.2954273399939e-10, 0.1881721265406e+01, 0.5368044267797e+00, + 0.3309362888795e-10, 0.1931525677349e+01, 0.8018209333619e+00, + 0.2810283608438e-10, 0.2414659495050e+01, 0.5225775174439e+00, + 0.3378045637764e-10, 0.4238019163430e+01, 0.1554202828031e+00, + + 0.2558134979840e-10, 0.1828225235805e+01, 0.5230807360890e+01, + 0.2273755578447e-10, 0.5858184283998e+01, 0.7084896783808e+01, + 0.2294176037690e-10, 0.4514589779057e+01, 0.1726015463500e+02, + 0.2533506099435e-10, 0.2355717851551e+01, 0.5216580451554e+01, + 0.2716685375812e-10, 0.2221003625100e+01, 0.8635942003952e+01, + 0.2419043435198e-10, 0.5955704951635e+01, 0.4690479774488e+01, + 0.2521232544812e-10, 0.1395676848521e+01, 0.5481254917084e+01, + 0.2630195021491e-10, 0.5727468918743e+01, 0.2629832328990e-01, + 0.2548395840944e-10, 0.2628351859400e-03, 0.1349867339771e+01 }; + +/* Sun-to-Earth, T^1, Y */ + static const double e1y[] = { + 0.9304690546528e-06, 0.0000000000000e+00, 0.0000000000000e+00, + 0.5150715570663e-06, 0.4431807116294e+01, 0.1256615170089e+02, + 0.1290825411056e-07, 0.4388610039678e+01, 0.1884922755134e+02, + 0.4645466665386e-08, 0.5827263376034e+01, 0.6283075850446e+01, + 0.2079625310718e-08, 0.1621698662282e+00, 0.6279552690824e+01, + 0.2078189850907e-08, 0.3344713435140e+01, 0.6286599010068e+01, + 0.6207190138027e-09, 0.5074049319576e+01, 0.4705732307012e+01, + 0.5989826532569e-09, 0.2231842216620e+01, 0.6256777527156e+01, + 0.5961360812618e-09, 0.1274975769045e+01, 0.6309374173736e+01, + 0.4874165471016e-09, 0.3642277426779e+01, 0.7755226100720e+00, + + 0.4283834034360e-09, 0.5148765510106e+01, 0.1059381944224e+01, + 0.4652389287529e-09, 0.4715794792175e+01, 0.7860419393880e+01, + 0.3751707476401e-09, 0.6617207370325e+00, 0.5753384878334e+01, + 0.3559998806198e-09, 0.6155548875404e+01, 0.5884926831456e+01, + 0.3558447558857e-09, 0.2898827297664e+01, 0.6812766822558e+01, + 0.3211116927106e-09, 0.3625813502509e+01, 0.6681224869435e+01, + 0.2875609914672e-09, 0.4345435813134e+01, 0.2513230340178e+02, + 0.2843109704069e-09, 0.5862263940038e+01, 0.6127655567643e+01, + 0.2744676468427e-09, 0.3926419475089e+01, 0.6438496133249e+01, + 0.2481285237789e-09, 0.1351976572828e+01, 0.5486777812467e+01, + + 0.2060338481033e-09, 0.2147556998591e+01, 0.7079373888424e+01, + 0.2015822358331e-09, 0.4408358972216e+01, 0.6290189305114e+01, + 0.2001195944195e-09, 0.5385829822531e+01, 0.6275962395778e+01, + 0.1953667642377e-09, 0.1304933746120e+01, 0.5507553240374e+01, + 0.1839744078713e-09, 0.6173567228835e+01, 0.1179062909082e+02, + 0.1643334294845e-09, 0.4635942997523e+01, 0.1150676975667e+02, + 0.1768051018652e-09, 0.5086283558874e+01, 0.7113454667900e-02, + 0.1674874205489e-09, 0.2243332137241e+01, 0.7058598460518e+01, + 0.1421445397609e-09, 0.6186899771515e+01, 0.7962980379786e+00, + 0.1255163958267e-09, 0.5730238465658e+01, 0.4694002934110e+01, + + 0.1013945281961e-09, 0.1726055228402e+01, 0.3738761453707e+01, + 0.1047294335852e-09, 0.2658801228129e+01, 0.6282095334605e+01, + 0.1047103879392e-09, 0.8481047835035e+00, 0.6284056366286e+01, + 0.9530343962826e-10, 0.3079267149859e+01, 0.6069776770667e+01, + 0.9604637611690e-10, 0.3258679792918e+00, 0.4136910472696e+01, + 0.9153518537177e-10, 0.4398599886584e+00, 0.6496374930224e+01, + 0.8562458214922e-10, 0.4772686794145e+01, 0.1194447056968e+01, + 0.8232525360654e-10, 0.5966220721679e+01, 0.1589072916335e+01, + 0.6150223411438e-10, 0.1780985591923e+01, 0.8827390247185e+01, + 0.6272087858000e-10, 0.3184305429012e+01, 0.8429241228195e+01, + + 0.5540476311040e-10, 0.3801260595433e+01, 0.4933208510675e+01, + 0.7331901699361e-10, 0.5205948591865e+01, 0.4535059491685e+01, + 0.6018528702791e-10, 0.4770139083623e+01, 0.1255903824622e+02, + 0.5150530724804e-10, 0.3574796899585e+01, 0.1176985366291e+02, + 0.6471933741811e-10, 0.2679787266521e+01, 0.5088628793478e+01, + 0.5317460644174e-10, 0.9528763345494e+00, 0.3154687086868e+01, + 0.4832187748783e-10, 0.5329322498232e+01, 0.6040347114260e+01, + 0.4716763555110e-10, 0.2395235316466e+01, 0.5331357529664e+01, + 0.4871509139861e-10, 0.3056663648823e+01, 0.1256967486051e+02, + 0.4598417696768e-10, 0.4452762609019e+01, 0.6525804586632e+01, + + 0.5674189533175e-10, 0.9879680872193e+00, 0.5729506548653e+01, + 0.4073560328195e-10, 0.5939127696986e+01, 0.7632943190217e+01, + 0.5040994945359e-10, 0.4549875824510e+01, 0.8031092209206e+01, + 0.5078185134679e-10, 0.7346659893982e+00, 0.7477522907414e+01, + 0.3769343537061e-10, 0.1071317188367e+01, 0.7234794171227e+01, + 0.4980331365299e-10, 0.2500345341784e+01, 0.6836645152238e+01, + 0.3458236594757e-10, 0.3825159450711e+01, 0.1097707878456e+02, + 0.3578859493602e-10, 0.5299664791549e+01, 0.4164311961999e+01, + 0.3370504646419e-10, 0.5002316301593e+01, 0.1137170464392e+02, + 0.3299873338428e-10, 0.2526123275282e+01, 0.3930209696940e+01, + + 0.4304917318409e-10, 0.3368078557132e+01, 0.1592596075957e+01, + 0.3402418753455e-10, 0.8385495425800e+00, 0.3128388763578e+01, + 0.2778460572146e-10, 0.3669905203240e+01, 0.7342457794669e+01, + 0.2782710128902e-10, 0.2691664812170e+00, 0.1748016358760e+01, + 0.2711725179646e-10, 0.4707487217718e+01, 0.5296909721118e+00, + 0.2981760946340e-10, 0.3190260867816e+00, 0.5368044267797e+00, + 0.2811672977772e-10, 0.3196532315372e+01, 0.7084896783808e+01, + 0.2863454474467e-10, 0.2263240324780e+00, 0.5223693906222e+01, + 0.3333464634051e-10, 0.3498451685065e+01, 0.8018209333619e+00, + 0.3312991747609e-10, 0.5839154477412e+01, 0.1554202828031e+00, + + 0.2813255564006e-10, 0.8268044346621e+00, 0.5225775174439e+00, + 0.2665098083966e-10, 0.3934021725360e+01, 0.5216580451554e+01, + 0.2349795705216e-10, 0.5197620913779e+01, 0.2146165377750e+01, + 0.2330352293961e-10, 0.2984999231807e+01, 0.1726015463500e+02, + 0.2728001683419e-10, 0.6521679638544e+00, 0.8635942003952e+01, + 0.2484061007669e-10, 0.3468955561097e+01, 0.5230807360890e+01, + 0.2646328768427e-10, 0.1013724533516e+01, 0.2629832328990e-01, + 0.2518630264831e-10, 0.6108081057122e+01, 0.5481254917084e+01, + 0.2421901455384e-10, 0.1651097776260e+01, 0.1349867339771e+01, + 0.6348533267831e-11, 0.3220226560321e+01, 0.8433466158131e+02 }; + +/* Sun-to-Earth, T^1, Z */ + static const double e1z[] = { + 0.2278290449966e-05, 0.3413716033863e+01, 0.6283075850446e+01, + 0.5429458209830e-07, 0.0000000000000e+00, 0.0000000000000e+00, + 0.1903240492525e-07, 0.3370592358297e+01, 0.1256615170089e+02, + 0.2385409276743e-09, 0.3327914718416e+01, 0.1884922755134e+02, + 0.8676928342573e-10, 0.1824006811264e+01, 0.5223693906222e+01, + 0.7765442593544e-10, 0.3888564279247e+01, 0.5507553240374e+01, + 0.7066158332715e-10, 0.5194267231944e+01, 0.2352866153506e+01, + 0.7092175288657e-10, 0.2333246960021e+01, 0.8399684731857e+02, + 0.5357582213535e-10, 0.2224031176619e+01, 0.5296909721118e+00, + 0.3828035865021e-10, 0.2156710933584e+01, 0.6279552690824e+01, + + 0.3824857220427e-10, 0.1529755219915e+01, 0.6286599010068e+01, + 0.3286995181628e-10, 0.4879512900483e+01, 0.1021328554739e+02 }; + +/* Sun-to-Earth, T^2, X */ + static const double e2x[] = { + -0.4143818297913e-10, 0.0000000000000e+00, 0.0000000000000e+00, + 0.2171497694435e-10, 0.4398225628264e+01, 0.1256615170089e+02, + 0.9845398442516e-11, 0.2079720838384e+00, 0.6283075850446e+01, + 0.9256833552682e-12, 0.4191264694361e+01, 0.1884922755134e+02, + 0.1022049384115e-12, 0.5381133195658e+01, 0.8399684731857e+02 }; + +/* Sun-to-Earth, T^2, Y */ + static const double e2y[] = { + 0.5063375872532e-10, 0.0000000000000e+00, 0.0000000000000e+00, + 0.2173815785980e-10, 0.2827805833053e+01, 0.1256615170089e+02, + 0.1010231999920e-10, 0.4634612377133e+01, 0.6283075850446e+01, + 0.9259745317636e-12, 0.2620612076189e+01, 0.1884922755134e+02, + 0.1022202095812e-12, 0.3809562326066e+01, 0.8399684731857e+02 }; + +/* Sun-to-Earth, T^2, Z */ + static const double e2z[] = { + 0.9722666114891e-10, 0.5152219582658e+01, 0.6283075850446e+01, + -0.3494819171909e-11, 0.0000000000000e+00, 0.0000000000000e+00, + 0.6713034376076e-12, 0.6440188750495e+00, 0.1256615170089e+02 }; + +/* SSB-to-Sun, T^0, X */ + static const double s0x[] = { + 0.4956757536410e-02, 0.3741073751789e+01, 0.5296909721118e+00, + 0.2718490072522e-02, 0.4016011511425e+01, 0.2132990797783e+00, + 0.1546493974344e-02, 0.2170528330642e+01, 0.3813291813120e-01, + 0.8366855276341e-03, 0.2339614075294e+01, 0.7478166569050e-01, + 0.2936777942117e-03, 0.0000000000000e+00, 0.0000000000000e+00, + 0.1201317439469e-03, 0.4090736353305e+01, 0.1059381944224e+01, + 0.7578550887230e-04, 0.3241518088140e+01, 0.4265981595566e+00, + 0.1941787367773e-04, 0.1012202064330e+01, 0.2061856251104e+00, + 0.1889227765991e-04, 0.3892520416440e+01, 0.2204125344462e+00, + 0.1937896968613e-04, 0.4797779441161e+01, 0.1495633313810e+00, + + 0.1434506110873e-04, 0.3868960697933e+01, 0.5225775174439e+00, + 0.1406659911580e-04, 0.4759766557397e+00, 0.5368044267797e+00, + 0.1179022300202e-04, 0.7774961520598e+00, 0.7626583626240e-01, + 0.8085864460959e-05, 0.3254654471465e+01, 0.3664874755930e-01, + 0.7622752967615e-05, 0.4227633103489e+01, 0.3961708870310e-01, + 0.6209171139066e-05, 0.2791828325711e+00, 0.7329749511860e-01, + 0.4366435633970e-05, 0.4440454875925e+01, 0.1589072916335e+01, + 0.3792124889348e-05, 0.5156393842356e+01, 0.7113454667900e-02, + 0.3154548963402e-05, 0.6157005730093e+01, 0.4194847048887e+00, + 0.3088359882942e-05, 0.2494567553163e+01, 0.6398972393349e+00, + + 0.2788440902136e-05, 0.4934318747989e+01, 0.1102062672231e+00, + 0.3039928456376e-05, 0.4895077702640e+01, 0.6283075850446e+01, + 0.2272258457679e-05, 0.5278394064764e+01, 0.1030928125552e+00, + 0.2162007057957e-05, 0.5802978019099e+01, 0.3163918923335e+00, + 0.1767632855737e-05, 0.3415346595193e-01, 0.1021328554739e+02, + 0.1349413459362e-05, 0.2001643230755e+01, 0.1484170571900e-02, + 0.1170141900476e-05, 0.2424750491620e+01, 0.6327837846670e+00, + 0.1054355266820e-05, 0.3123311487576e+01, 0.4337116142245e+00, + 0.9800822461610e-06, 0.3026258088130e+01, 0.1052268489556e+01, + 0.1091203749931e-05, 0.3157811670347e+01, 0.1162474756779e+01, + + 0.6960236715913e-06, 0.8219570542313e+00, 0.1066495398892e+01, + 0.5689257296909e-06, 0.1323052375236e+01, 0.9491756770005e+00, + 0.6613172135802e-06, 0.2765348881598e+00, 0.8460828644453e+00, + 0.6277702517571e-06, 0.5794064466382e+01, 0.1480791608091e+00, + 0.6304884066699e-06, 0.7323555380787e+00, 0.2243449970715e+00, + 0.4897850467382e-06, 0.3062464235399e+01, 0.3340612434717e+01, + 0.3759148598786e-06, 0.4588290469664e+01, 0.3516457698740e-01, + 0.3110520548195e-06, 0.1374299536572e+01, 0.6373574839730e-01, + 0.3064708359780e-06, 0.4222267485047e+01, 0.1104591729320e-01, + 0.2856347168241e-06, 0.3714202944973e+01, 0.1510475019529e+00, + + 0.2840945514288e-06, 0.2847972875882e+01, 0.4110125927500e-01, + 0.2378951599405e-06, 0.3762072563388e+01, 0.2275259891141e+00, + 0.2714229481417e-06, 0.1036049980031e+01, 0.2535050500000e-01, + 0.2323551717307e-06, 0.4682388599076e+00, 0.8582758298370e-01, + 0.1881790512219e-06, 0.4790565425418e+01, 0.2118763888447e+01, + 0.2261353968371e-06, 0.1669144912212e+01, 0.7181332454670e-01, + 0.2214546389848e-06, 0.3937717281614e+01, 0.2968341143800e-02, + 0.2184915594933e-06, 0.1129169845099e+00, 0.7775000683430e-01, + 0.2000164937936e-06, 0.4030009638488e+01, 0.2093666171530e+00, + 0.1966105136719e-06, 0.8745955786834e+00, 0.2172315424036e+00, + + 0.1904742332624e-06, 0.5919743598964e+01, 0.2022531624851e+00, + 0.1657399705031e-06, 0.2549141484884e+01, 0.7358765972222e+00, + 0.1574070533987e-06, 0.5277533020230e+01, 0.7429900518901e+00, + 0.1832261651039e-06, 0.3064688127777e+01, 0.3235053470014e+00, + 0.1733615346569e-06, 0.3011432799094e+01, 0.1385174140878e+00, + 0.1549124014496e-06, 0.4005569132359e+01, 0.5154640627760e+00, + 0.1637044713838e-06, 0.1831375966632e+01, 0.8531963191132e+00, + 0.1123420082383e-06, 0.1180270407578e+01, 0.1990721704425e+00, + 0.1083754165740e-06, 0.3414101320863e+00, 0.5439178814476e+00, + 0.1156638012655e-06, 0.6130479452594e+00, 0.5257585094865e+00, + + 0.1142548785134e-06, 0.3724761948846e+01, 0.5336234347371e+00, + 0.7921463895965e-07, 0.2435425589361e+01, 0.1478866649112e+01, + 0.7428600285231e-07, 0.3542144398753e+01, 0.2164800718209e+00, + 0.8323211246747e-07, 0.3525058072354e+01, 0.1692165728891e+01, + 0.7257595116312e-07, 0.1364299431982e+01, 0.2101180877357e+00, + 0.7111185833236e-07, 0.2460478875808e+01, 0.4155522422634e+00, + 0.6868090383716e-07, 0.4397327670704e+01, 0.1173197218910e+00, + 0.7226419974175e-07, 0.4042647308905e+01, 0.1265567569334e+01, + 0.6955642383177e-07, 0.2865047906085e+01, 0.9562891316684e+00, + 0.7492139296331e-07, 0.5014278994215e+01, 0.1422690933580e-01, + + 0.6598363128857e-07, 0.2376730020492e+01, 0.6470106940028e+00, + 0.7381147293385e-07, 0.3272990384244e+01, 0.1581959461667e+01, + 0.6402909624032e-07, 0.5302290955138e+01, 0.9597935788730e-01, + 0.6237454263857e-07, 0.5444144425332e+01, 0.7084920306520e-01, + 0.5241198544016e-07, 0.4215359579205e+01, 0.5265099800692e+00, + 0.5144463853918e-07, 0.1218916689916e+00, 0.5328719641544e+00, + 0.5868164772299e-07, 0.2369402002213e+01, 0.7871412831580e-01, + 0.6233195669151e-07, 0.1254922242403e+01, 0.2608790314060e+02, + 0.6068463791422e-07, 0.5679713760431e+01, 0.1114304132498e+00, + 0.4359361135065e-07, 0.6097219641646e+00, 0.1375773836557e+01, + + 0.4686510366826e-07, 0.4786231041431e+01, 0.1143987543936e+00, + 0.3758977287225e-07, 0.1167368068139e+01, 0.1596186371003e+01, + 0.4282051974778e-07, 0.1519471064319e+01, 0.2770348281756e+00, + 0.5153765386113e-07, 0.1860532322984e+01, 0.2228608264996e+00, + 0.4575129387188e-07, 0.7632857887158e+00, 0.1465949902372e+00, + 0.3326844933286e-07, 0.1298219485285e+01, 0.5070101000000e-01, + 0.3748617450984e-07, 0.1046510321062e+01, 0.4903339079539e+00, + 0.2816756661499e-07, 0.3434522346190e+01, 0.2991266627620e+00, + 0.3412750405039e-07, 0.2523766270318e+01, 0.3518164938661e+00, + 0.2655796761776e-07, 0.2904422260194e+01, 0.6256703299991e+00, + + 0.2963597929458e-07, 0.5923900431149e+00, 0.1099462426779e+00, + 0.2539523734781e-07, 0.4851947722567e+01, 0.1256615170089e+02, + 0.2283087914139e-07, 0.3400498595496e+01, 0.6681224869435e+01, + 0.2321309799331e-07, 0.5789099148673e+01, 0.3368040641550e-01, + 0.2549657649750e-07, 0.3991856479792e-01, 0.1169588211447e+01, + 0.2290462303977e-07, 0.2788567577052e+01, 0.1045155034888e+01, + 0.1945398522914e-07, 0.3290896998176e+01, 0.1155361302111e+01, + 0.1849171512638e-07, 0.2698060129367e+01, 0.4452511715700e-02, + 0.1647199834254e-07, 0.3016735644085e+01, 0.4408250688924e+00, + 0.1529530765273e-07, 0.5573043116178e+01, 0.6521991896920e-01, + + 0.1433199339978e-07, 0.1481192356147e+01, 0.9420622223326e+00, + 0.1729134193602e-07, 0.1422817538933e+01, 0.2108507877249e+00, + 0.1716463931346e-07, 0.3469468901855e+01, 0.2157473718317e+00, + 0.1391206061378e-07, 0.6122436220547e+01, 0.4123712502208e+00, + 0.1404746661924e-07, 0.1647765641936e+01, 0.4258542984690e-01, + 0.1410452399455e-07, 0.5989729161964e+01, 0.2258291676434e+00, + 0.1089828772168e-07, 0.2833705509371e+01, 0.4226656969313e+00, + 0.1047374564948e-07, 0.5090690007331e+00, 0.3092784376656e+00, + 0.1358279126532e-07, 0.5128990262836e+01, 0.7923417740620e-01, + 0.1020456476148e-07, 0.9632772880808e+00, 0.1456308687557e+00, + + 0.1033428735328e-07, 0.3223779318418e+01, 0.1795258541446e+01, + 0.1412435841540e-07, 0.2410271572721e+01, 0.1525316725248e+00, + 0.9722759371574e-08, 0.2333531395690e+01, 0.8434341241180e-01, + 0.9657334084704e-08, 0.6199270974168e+01, 0.1272681024002e+01, + 0.1083641148690e-07, 0.2864222292929e+01, 0.7032915397480e-01, + 0.1067318403838e-07, 0.5833458866568e+00, 0.2123349582968e+00, + 0.1062366201976e-07, 0.4307753989494e+01, 0.2142632012598e+00, + 0.1236364149266e-07, 0.2873917870593e+01, 0.1847279083684e+00, + 0.1092759489593e-07, 0.2959887266733e+01, 0.1370332435159e+00, + 0.8912069362899e-08, 0.5141213702562e+01, 0.2648454860559e+01, + + 0.9656467707970e-08, 0.4532182462323e+01, 0.4376440768498e+00, + 0.8098386150135e-08, 0.2268906338379e+01, 0.2880807454688e+00, + 0.7857714675000e-08, 0.4055544260745e+01, 0.2037373330570e+00, + 0.7288455940646e-08, 0.5357901655142e+01, 0.1129145838217e+00, + 0.9450595950552e-08, 0.4264926963939e+01, 0.5272426800584e+00, + 0.9381718247537e-08, 0.7489366976576e-01, 0.5321392641652e+00, + 0.7079052646038e-08, 0.1923311052874e+01, 0.6288513220417e+00, + 0.9259004415344e-08, 0.2970256853438e+01, 0.1606092486742e+00, + 0.8259801499742e-08, 0.3327056314697e+01, 0.8389694097774e+00, + 0.6476334355779e-08, 0.2954925505727e+01, 0.2008557621224e+01, + + 0.5984021492007e-08, 0.9138753105829e+00, 0.2042657109477e+02, + 0.5989546863181e-08, 0.3244464082031e+01, 0.2111650433779e+01, + 0.6233108606023e-08, 0.4995232638403e+00, 0.4305306221819e+00, + 0.6877299149965e-08, 0.2834987233449e+01, 0.9561746721300e-02, + 0.8311234227190e-08, 0.2202951835758e+01, 0.3801276407308e+00, + 0.6599472832414e-08, 0.4478581462618e+01, 0.1063314406849e+01, + 0.6160491096549e-08, 0.5145858696411e+01, 0.1368660381889e+01, + 0.6164772043891e-08, 0.3762976697911e+00, 0.4234171675140e+00, + 0.6363248684450e-08, 0.3162246718685e+01, 0.1253008786510e-01, + 0.6448587520999e-08, 0.3442693302119e+01, 0.5287268506303e+00, + + 0.6431662283977e-08, 0.8977549136606e+00, 0.5306550935933e+00, + 0.6351223158474e-08, 0.4306447410369e+01, 0.5217580628120e+02, + 0.5476721393451e-08, 0.3888529177855e+01, 0.2221856701002e+01, + 0.5341772572619e-08, 0.2655560662512e+01, 0.7466759693650e-01, + 0.5337055758302e-08, 0.5164990735946e+01, 0.7489573444450e-01, + 0.5373120816787e-08, 0.6041214553456e+01, 0.1274714967946e+00, + 0.5392351705426e-08, 0.9177763485932e+00, 0.1055449481598e+01, + 0.6688495850205e-08, 0.3089608126937e+01, 0.2213766559277e+00, + 0.5072003660362e-08, 0.4311316541553e+01, 0.2132517061319e+00, + 0.5070726650455e-08, 0.5790675464444e+00, 0.2133464534247e+00, + + 0.5658012950032e-08, 0.2703945510675e+01, 0.7287631425543e+00, + 0.4835509924854e-08, 0.2975422976065e+01, 0.7160067364790e-01, + 0.6479821978012e-08, 0.1324168733114e+01, 0.2209183458640e-01, + 0.6230636494980e-08, 0.2860103632836e+01, 0.3306188016693e+00, + 0.4649239516213e-08, 0.4832259763403e+01, 0.7796265773310e-01, + 0.6487325792700e-08, 0.2726165825042e+01, 0.3884652414254e+00, + 0.4682823682770e-08, 0.6966602455408e+00, 0.1073608853559e+01, + 0.5704230804976e-08, 0.5669634104606e+01, 0.8731175355560e-01, + 0.6125413585489e-08, 0.1513386538915e+01, 0.7605151500000e-01, + 0.6035825038187e-08, 0.1983509168227e+01, 0.9846002785331e+00, + + 0.4331123462303e-08, 0.2782892992807e+01, 0.4297791515992e+00, + 0.4681107685143e-08, 0.5337232886836e+01, 0.2127790306879e+00, + 0.4669105829655e-08, 0.5837133792160e+01, 0.2138191288687e+00, + 0.5138823602365e-08, 0.3080560200507e+01, 0.7233337363710e-01, + 0.4615856664534e-08, 0.1661747897471e+01, 0.8603097737811e+00, + 0.4496916702197e-08, 0.2112508027068e+01, 0.7381754420900e-01, + 0.4278479042945e-08, 0.5716528462627e+01, 0.7574578717200e-01, + 0.3840525503932e-08, 0.6424172726492e+00, 0.3407705765729e+00, + 0.4866636509685e-08, 0.4919244697715e+01, 0.7722995774390e-01, + 0.3526100639296e-08, 0.2550821052734e+01, 0.6225157782540e-01, + + 0.3939558488075e-08, 0.3939331491710e+01, 0.5268983110410e-01, + 0.4041268772576e-08, 0.2275337571218e+01, 0.3503323232942e+00, + 0.3948761842853e-08, 0.1999324200790e+01, 0.1451108196653e+00, + 0.3258394550029e-08, 0.9121001378200e+00, 0.5296435984654e+00, + 0.3257897048761e-08, 0.3428428660869e+01, 0.5297383457582e+00, + 0.3842559031298e-08, 0.6132927720035e+01, 0.9098186128426e+00, + 0.3109920095448e-08, 0.7693650193003e+00, 0.3932462625300e-02, + 0.3132237775119e-08, 0.3621293854908e+01, 0.2346394437820e+00, + 0.3942189421510e-08, 0.4841863659733e+01, 0.3180992042600e-02, + 0.3796972285340e-08, 0.1814174994268e+01, 0.1862120789403e+00, + + 0.3995640233688e-08, 0.1386990406091e+01, 0.4549093064213e+00, + 0.2875013727414e-08, 0.9178318587177e+00, 0.1905464808669e+01, + 0.3073719932844e-08, 0.2688923811835e+01, 0.3628624111593e+00, + 0.2731016580075e-08, 0.1188259127584e+01, 0.2131850110243e+00, + 0.2729549896546e-08, 0.3702160634273e+01, 0.2134131485323e+00, + 0.3339372892449e-08, 0.7199163960331e+00, 0.2007689919132e+00, + 0.2898833764204e-08, 0.1916709364999e+01, 0.5291709230214e+00, + 0.2894536549362e-08, 0.2424043195547e+01, 0.5302110212022e+00, + 0.3096872473843e-08, 0.4445894977497e+01, 0.2976424921901e+00, + 0.2635672326810e-08, 0.3814366984117e+01, 0.1485980103780e+01, + + 0.3649302697001e-08, 0.2924200596084e+01, 0.6044726378023e+00, + 0.3127954585895e-08, 0.1842251648327e+01, 0.1084620721060e+00, + 0.2616040173947e-08, 0.4155841921984e+01, 0.1258454114666e+01, + 0.2597395859860e-08, 0.1158045978874e+00, 0.2103781122809e+00, + 0.2593286172210e-08, 0.4771850408691e+01, 0.2162200472757e+00, + 0.2481823585747e-08, 0.4608842558889e+00, 0.1062562936266e+01, + 0.2742219550725e-08, 0.1538781127028e+01, 0.5651155736444e+00, + 0.3199558469610e-08, 0.3226647822878e+00, 0.7036329877322e+00, + 0.2666088542957e-08, 0.1967991731219e+00, 0.1400015846597e+00, + 0.2397067430580e-08, 0.3707036669873e+01, 0.2125476091956e+00, + + 0.2376570772738e-08, 0.1182086628042e+01, 0.2140505503610e+00, + 0.2547228007887e-08, 0.4906256820629e+01, 0.1534957940063e+00, + 0.2265575594114e-08, 0.3414949866857e+01, 0.2235935264888e+00, + 0.2464381430585e-08, 0.4599122275378e+01, 0.2091065926078e+00, + 0.2433408527044e-08, 0.2830751145445e+00, 0.2174915669488e+00, + 0.2443605509076e-08, 0.4212046432538e+01, 0.1739420156204e+00, + 0.2319779262465e-08, 0.9881978408630e+00, 0.7530171478090e-01, + 0.2284622835465e-08, 0.5565347331588e+00, 0.7426161660010e-01, + 0.2467268750783e-08, 0.5655708150766e+00, 0.2526561439362e+00, + 0.2808513492782e-08, 0.1418405053408e+01, 0.5636314030725e+00, + + 0.2329528932532e-08, 0.4069557545675e+01, 0.1056200952181e+01, + 0.9698639532817e-09, 0.1074134313634e+01, 0.7826370942180e+02 }; + +/* SSB-to-Sun, T^0, Y */ + static const double s0y[] = { + 0.4955392320126e-02, 0.2170467313679e+01, 0.5296909721118e+00, + 0.2722325167392e-02, 0.2444433682196e+01, 0.2132990797783e+00, + 0.1546579925346e-02, 0.5992779281546e+00, 0.3813291813120e-01, + 0.8363140252966e-03, 0.7687356310801e+00, 0.7478166569050e-01, + 0.3385792683603e-03, 0.0000000000000e+00, 0.0000000000000e+00, + 0.1201192221613e-03, 0.2520035601514e+01, 0.1059381944224e+01, + 0.7587125720554e-04, 0.1669954006449e+01, 0.4265981595566e+00, + 0.1964155361250e-04, 0.5707743963343e+01, 0.2061856251104e+00, + 0.1891900364909e-04, 0.2320960679937e+01, 0.2204125344462e+00, + 0.1937373433356e-04, 0.3226940689555e+01, 0.1495633313810e+00, + + 0.1437139941351e-04, 0.2301626908096e+01, 0.5225775174439e+00, + 0.1406267683099e-04, 0.5188579265542e+01, 0.5368044267797e+00, + 0.1178703080346e-04, 0.5489483248476e+01, 0.7626583626240e-01, + 0.8079835186041e-05, 0.1683751835264e+01, 0.3664874755930e-01, + 0.7623253594652e-05, 0.2656400462961e+01, 0.3961708870310e-01, + 0.6248667483971e-05, 0.4992775362055e+01, 0.7329749511860e-01, + 0.4366353695038e-05, 0.2869706279678e+01, 0.1589072916335e+01, + 0.3829101568895e-05, 0.3572131359950e+01, 0.7113454667900e-02, + 0.3175733773908e-05, 0.4535372530045e+01, 0.4194847048887e+00, + 0.3092437902159e-05, 0.9230153317909e+00, 0.6398972393349e+00, + + 0.2874168812154e-05, 0.3363143761101e+01, 0.1102062672231e+00, + 0.3040119321826e-05, 0.3324250895675e+01, 0.6283075850446e+01, + 0.2699723308006e-05, 0.2917882441928e+00, 0.1030928125552e+00, + 0.2134832683534e-05, 0.4220997202487e+01, 0.3163918923335e+00, + 0.1770412139433e-05, 0.4747318496462e+01, 0.1021328554739e+02, + 0.1377264209373e-05, 0.4305058462401e+00, 0.1484170571900e-02, + 0.1127814538960e-05, 0.8538177240740e+00, 0.6327837846670e+00, + 0.1055608090130e-05, 0.1551800742580e+01, 0.4337116142245e+00, + 0.9802673861420e-06, 0.1459646735377e+01, 0.1052268489556e+01, + 0.1090329461951e-05, 0.1587351228711e+01, 0.1162474756779e+01, + + 0.6959590025090e-06, 0.5534442628766e+01, 0.1066495398892e+01, + 0.5664914529542e-06, 0.6030673003297e+01, 0.9491756770005e+00, + 0.6607787763599e-06, 0.4989507233927e+01, 0.8460828644453e+00, + 0.6269725742838e-06, 0.4222951804572e+01, 0.1480791608091e+00, + 0.6301889697863e-06, 0.5444316669126e+01, 0.2243449970715e+00, + 0.4891042662861e-06, 0.1490552839784e+01, 0.3340612434717e+01, + 0.3457083123290e-06, 0.3030475486049e+01, 0.3516457698740e-01, + 0.3032559967314e-06, 0.2652038793632e+01, 0.1104591729320e-01, + 0.2841133988903e-06, 0.1276744786829e+01, 0.4110125927500e-01, + 0.2855564444432e-06, 0.2143368674733e+01, 0.1510475019529e+00, + + 0.2765157135038e-06, 0.5444186109077e+01, 0.6373574839730e-01, + 0.2382312465034e-06, 0.2190521137593e+01, 0.2275259891141e+00, + 0.2808060365077e-06, 0.5735195064841e+01, 0.2535050500000e-01, + 0.2332175234405e-06, 0.9481985524859e-01, 0.7181332454670e-01, + 0.2322488199659e-06, 0.5180499361533e+01, 0.8582758298370e-01, + 0.1881850258423e-06, 0.3219788273885e+01, 0.2118763888447e+01, + 0.2196111392808e-06, 0.2366941159761e+01, 0.2968341143800e-02, + 0.2183810335519e-06, 0.4825445110915e+01, 0.7775000683430e-01, + 0.2002733093326e-06, 0.2457148995307e+01, 0.2093666171530e+00, + 0.1967111767229e-06, 0.5586291545459e+01, 0.2172315424036e+00, + + 0.1568473250543e-06, 0.3708003123320e+01, 0.7429900518901e+00, + 0.1852528314300e-06, 0.4310638151560e+01, 0.2022531624851e+00, + 0.1832111226447e-06, 0.1494665322656e+01, 0.3235053470014e+00, + 0.1746805502310e-06, 0.1451378500784e+01, 0.1385174140878e+00, + 0.1555730966650e-06, 0.1068040418198e+01, 0.7358765972222e+00, + 0.1554883462559e-06, 0.2442579035461e+01, 0.5154640627760e+00, + 0.1638380568746e-06, 0.2597913420625e+00, 0.8531963191132e+00, + 0.1159938593640e-06, 0.5834512021280e+01, 0.1990721704425e+00, + 0.1083427965695e-06, 0.5054033177950e+01, 0.5439178814476e+00, + 0.1156480369431e-06, 0.5325677432457e+01, 0.5257585094865e+00, + + 0.1141308860095e-06, 0.2153403923857e+01, 0.5336234347371e+00, + 0.7913146470946e-07, 0.8642846847027e+00, 0.1478866649112e+01, + 0.7439752463733e-07, 0.1970628496213e+01, 0.2164800718209e+00, + 0.7280277104079e-07, 0.6073307250609e+01, 0.2101180877357e+00, + 0.8319567719136e-07, 0.1954371928334e+01, 0.1692165728891e+01, + 0.7137705549290e-07, 0.8904989440909e+00, 0.4155522422634e+00, + 0.6900825396225e-07, 0.2825717714977e+01, 0.1173197218910e+00, + 0.7245757216635e-07, 0.2481677513331e+01, 0.1265567569334e+01, + 0.6961165696255e-07, 0.1292955312978e+01, 0.9562891316684e+00, + 0.7571804456890e-07, 0.3427517575069e+01, 0.1422690933580e-01, + + 0.6605425721904e-07, 0.8052192701492e+00, 0.6470106940028e+00, + 0.7375477357248e-07, 0.1705076390088e+01, 0.1581959461667e+01, + 0.7041664951470e-07, 0.4848356967891e+00, 0.9597935788730e-01, + 0.6322199535763e-07, 0.3878069473909e+01, 0.7084920306520e-01, + 0.5244380279191e-07, 0.2645560544125e+01, 0.5265099800692e+00, + 0.5143125704988e-07, 0.4834486101370e+01, 0.5328719641544e+00, + 0.5871866319373e-07, 0.7981472548900e+00, 0.7871412831580e-01, + 0.6300822573871e-07, 0.5979398788281e+01, 0.2608790314060e+02, + 0.6062154271548e-07, 0.4108655402756e+01, 0.1114304132498e+00, + 0.4361912339976e-07, 0.5322624319280e+01, 0.1375773836557e+01, + + 0.4417005920067e-07, 0.6240817359284e+01, 0.2770348281756e+00, + 0.4686806749936e-07, 0.3214977301156e+01, 0.1143987543936e+00, + 0.3758892132305e-07, 0.5879809634765e+01, 0.1596186371003e+01, + 0.5151351332319e-07, 0.2893377688007e+00, 0.2228608264996e+00, + 0.4554683578572e-07, 0.5475427144122e+01, 0.1465949902372e+00, + 0.3442381385338e-07, 0.5992034796640e+01, 0.5070101000000e-01, + 0.2831093954933e-07, 0.5367350273914e+01, 0.3092784376656e+00, + 0.3756267090084e-07, 0.5758171285420e+01, 0.4903339079539e+00, + 0.2816374679892e-07, 0.1863718700923e+01, 0.2991266627620e+00, + 0.3419307025569e-07, 0.9524347534130e+00, 0.3518164938661e+00, + + 0.2904250494239e-07, 0.5304471615602e+01, 0.1099462426779e+00, + 0.2471734511206e-07, 0.1297069793530e+01, 0.6256703299991e+00, + 0.2539620831872e-07, 0.3281126083375e+01, 0.1256615170089e+02, + 0.2281017868007e-07, 0.1829122133165e+01, 0.6681224869435e+01, + 0.2275319473335e-07, 0.5797198160181e+01, 0.3932462625300e-02, + 0.2547755368442e-07, 0.4752697708330e+01, 0.1169588211447e+01, + 0.2285979669317e-07, 0.1223205292886e+01, 0.1045155034888e+01, + 0.1913386560994e-07, 0.1757532993389e+01, 0.1155361302111e+01, + 0.1809020525147e-07, 0.4246116108791e+01, 0.3368040641550e-01, + 0.1649213300201e-07, 0.1445162890627e+01, 0.4408250688924e+00, + + 0.1834972793932e-07, 0.1126917567225e+01, 0.4452511715700e-02, + 0.1439550648138e-07, 0.6160756834764e+01, 0.9420622223326e+00, + 0.1487645457041e-07, 0.4358761931792e+01, 0.4123712502208e+00, + 0.1731729516660e-07, 0.6134456753344e+01, 0.2108507877249e+00, + 0.1717747163567e-07, 0.1898186084455e+01, 0.2157473718317e+00, + 0.1418190430374e-07, 0.4180286741266e+01, 0.6521991896920e-01, + 0.1404844134873e-07, 0.7654053565412e-01, 0.4258542984690e-01, + 0.1409842846538e-07, 0.4418612420312e+01, 0.2258291676434e+00, + 0.1090948346291e-07, 0.1260615686131e+01, 0.4226656969313e+00, + 0.1357577323612e-07, 0.3558248818690e+01, 0.7923417740620e-01, + + 0.1018154061960e-07, 0.5676087241256e+01, 0.1456308687557e+00, + 0.1412073972109e-07, 0.8394392632422e+00, 0.1525316725248e+00, + 0.1030938326496e-07, 0.1653593274064e+01, 0.1795258541446e+01, + 0.1180081567104e-07, 0.1285802592036e+01, 0.7032915397480e-01, + 0.9708510575650e-08, 0.7631889488106e+00, 0.8434341241180e-01, + 0.9637689663447e-08, 0.4630642649176e+01, 0.1272681024002e+01, + 0.1068910429389e-07, 0.5294934032165e+01, 0.2123349582968e+00, + 0.1063716179336e-07, 0.2736266800832e+01, 0.2142632012598e+00, + 0.1234858713814e-07, 0.1302891146570e+01, 0.1847279083684e+00, + 0.8912631189738e-08, 0.3570415993621e+01, 0.2648454860559e+01, + + 0.1036378285534e-07, 0.4236693440949e+01, 0.1370332435159e+00, + 0.9667798501561e-08, 0.2960768892398e+01, 0.4376440768498e+00, + 0.8108314201902e-08, 0.6987781646841e+00, 0.2880807454688e+00, + 0.7648364324628e-08, 0.2499017863863e+01, 0.2037373330570e+00, + 0.7286136828406e-08, 0.3787426951665e+01, 0.1129145838217e+00, + 0.9448237743913e-08, 0.2694354332983e+01, 0.5272426800584e+00, + 0.9374276106428e-08, 0.4787121277064e+01, 0.5321392641652e+00, + 0.7100226287462e-08, 0.3530238792101e+00, 0.6288513220417e+00, + 0.9253056659571e-08, 0.1399478925664e+01, 0.1606092486742e+00, + 0.6636432145504e-08, 0.3479575438447e+01, 0.1368660381889e+01, + + 0.6469975312932e-08, 0.1383669964800e+01, 0.2008557621224e+01, + 0.7335849729765e-08, 0.1243698166898e+01, 0.9561746721300e-02, + 0.8743421205855e-08, 0.3776164289301e+01, 0.3801276407308e+00, + 0.5993635744494e-08, 0.5627122113596e+01, 0.2042657109477e+02, + 0.5981008479693e-08, 0.1674336636752e+01, 0.2111650433779e+01, + 0.6188535145838e-08, 0.5214925208672e+01, 0.4305306221819e+00, + 0.6596074017566e-08, 0.2907653268124e+01, 0.1063314406849e+01, + 0.6630815126226e-08, 0.2127643669658e+01, 0.8389694097774e+00, + 0.6156772830040e-08, 0.5082160803295e+01, 0.4234171675140e+00, + 0.6446960563014e-08, 0.1872100916905e+01, 0.5287268506303e+00, + + 0.6429324424668e-08, 0.5610276103577e+01, 0.5306550935933e+00, + 0.6302232396465e-08, 0.1592152049607e+01, 0.1253008786510e-01, + 0.6399244436159e-08, 0.2746214421532e+01, 0.5217580628120e+02, + 0.5474965172558e-08, 0.2317666374383e+01, 0.2221856701002e+01, + 0.5339293190692e-08, 0.1084724961156e+01, 0.7466759693650e-01, + 0.5334733683389e-08, 0.3594106067745e+01, 0.7489573444450e-01, + 0.5392665782110e-08, 0.5630254365606e+01, 0.1055449481598e+01, + 0.6682075673789e-08, 0.1518480041732e+01, 0.2213766559277e+00, + 0.5079130495960e-08, 0.2739765115711e+01, 0.2132517061319e+00, + 0.5077759793261e-08, 0.5290711290094e+01, 0.2133464534247e+00, + + 0.4832037368310e-08, 0.1404473217200e+01, 0.7160067364790e-01, + 0.6463279674802e-08, 0.6038381695210e+01, 0.2209183458640e-01, + 0.6240592771560e-08, 0.1290170653666e+01, 0.3306188016693e+00, + 0.4672013521493e-08, 0.3261895939677e+01, 0.7796265773310e-01, + 0.6500650750348e-08, 0.1154522312095e+01, 0.3884652414254e+00, + 0.6344161389053e-08, 0.6206111545062e+01, 0.7605151500000e-01, + 0.4682518370646e-08, 0.5409118796685e+01, 0.1073608853559e+01, + 0.5329460015591e-08, 0.1202985784864e+01, 0.7287631425543e+00, + 0.5701588675898e-08, 0.4098715257064e+01, 0.8731175355560e-01, + 0.6030690867211e-08, 0.4132033218460e+00, 0.9846002785331e+00, + + 0.4336256312655e-08, 0.1211415991827e+01, 0.4297791515992e+00, + 0.4688498808975e-08, 0.3765479072409e+01, 0.2127790306879e+00, + 0.4675578609335e-08, 0.4265540037226e+01, 0.2138191288687e+00, + 0.4225578112158e-08, 0.5237566010676e+01, 0.3407705765729e+00, + 0.5139422230028e-08, 0.1507173079513e+01, 0.7233337363710e-01, + 0.4619995093571e-08, 0.9023957449848e-01, 0.8603097737811e+00, + 0.4494776255461e-08, 0.5414930552139e+00, 0.7381754420900e-01, + 0.4274026276788e-08, 0.4145735303659e+01, 0.7574578717200e-01, + 0.5018141789353e-08, 0.3344408829055e+01, 0.3180992042600e-02, + 0.4866163952181e-08, 0.3348534657607e+01, 0.7722995774390e-01, + + 0.4111986020501e-08, 0.4198823597220e+00, 0.1451108196653e+00, + 0.3356142784950e-08, 0.5609144747180e+01, 0.1274714967946e+00, + 0.4070575554551e-08, 0.7028411059224e+00, 0.3503323232942e+00, + 0.3257451857278e-08, 0.5624697983086e+01, 0.5296435984654e+00, + 0.3256973703026e-08, 0.1857842076707e+01, 0.5297383457582e+00, + 0.3830771508640e-08, 0.4562887279931e+01, 0.9098186128426e+00, + 0.3725024005962e-08, 0.2358058692652e+00, 0.1084620721060e+00, + 0.3136763921756e-08, 0.2049731526845e+01, 0.2346394437820e+00, + 0.3795147256194e-08, 0.2432356296933e+00, 0.1862120789403e+00, + 0.2877342229911e-08, 0.5631101279387e+01, 0.1905464808669e+01, + + 0.3076931798805e-08, 0.1117615737392e+01, 0.3628624111593e+00, + 0.2734765945273e-08, 0.5899826516955e+01, 0.2131850110243e+00, + 0.2733405296885e-08, 0.2130562964070e+01, 0.2134131485323e+00, + 0.2898552353410e-08, 0.3462387048225e+00, 0.5291709230214e+00, + 0.2893736103681e-08, 0.8534352781543e+00, 0.5302110212022e+00, + 0.3095717734137e-08, 0.2875061429041e+01, 0.2976424921901e+00, + 0.2636190425832e-08, 0.2242512846659e+01, 0.1485980103780e+01, + 0.3645512095537e-08, 0.1354016903958e+01, 0.6044726378023e+00, + 0.2808173547723e-08, 0.6705114365631e-01, 0.6225157782540e-01, + 0.2625012866888e-08, 0.4775705748482e+01, 0.5268983110410e-01, + + 0.2572233995651e-08, 0.2638924216139e+01, 0.1258454114666e+01, + 0.2604238824792e-08, 0.4826358927373e+01, 0.2103781122809e+00, + 0.2596886385239e-08, 0.3200388483118e+01, 0.2162200472757e+00, + 0.3228057304264e-08, 0.5384848409563e+01, 0.2007689919132e+00, + 0.2481601798252e-08, 0.5173373487744e+01, 0.1062562936266e+01, + 0.2745977498864e-08, 0.6250966149853e+01, 0.5651155736444e+00, + 0.2669878833811e-08, 0.4906001352499e+01, 0.1400015846597e+00, + 0.3203986611711e-08, 0.5034333010005e+01, 0.7036329877322e+00, + 0.3354961227212e-08, 0.6108262423137e+01, 0.4549093064213e+00, + 0.2400407324558e-08, 0.2135399294955e+01, 0.2125476091956e+00, + + 0.2379905859802e-08, 0.5893721933961e+01, 0.2140505503610e+00, + 0.2550844302187e-08, 0.3331940762063e+01, 0.1534957940063e+00, + 0.2268824211001e-08, 0.1843418461035e+01, 0.2235935264888e+00, + 0.2464700891204e-08, 0.3029548547230e+01, 0.2091065926078e+00, + 0.2436814726024e-08, 0.4994717970364e+01, 0.2174915669488e+00, + 0.2443623894745e-08, 0.2645102591375e+01, 0.1739420156204e+00, + 0.2318701783838e-08, 0.5700547397897e+01, 0.7530171478090e-01, + 0.2284448700256e-08, 0.5268898905872e+01, 0.7426161660010e-01, + 0.2468848123510e-08, 0.5276280575078e+01, 0.2526561439362e+00, + 0.2814052350303e-08, 0.6130168623475e+01, 0.5636314030725e+00, + + 0.2243662755220e-08, 0.6631692457995e+00, 0.8886590321940e-01, + 0.2330795855941e-08, 0.2499435487702e+01, 0.1056200952181e+01, + 0.9757679038404e-09, 0.5796846023126e+01, 0.7826370942180e+02 }; + +/* SSB-to-Sun, T^0, Z */ + static const double s0z[] = { + 0.1181255122986e-03, 0.4607918989164e+00, 0.2132990797783e+00, + 0.1127777651095e-03, 0.4169146331296e+00, 0.5296909721118e+00, + 0.4777754401806e-04, 0.4582657007130e+01, 0.3813291813120e-01, + 0.1129354285772e-04, 0.5758735142480e+01, 0.7478166569050e-01, + -0.1149543637123e-04, 0.0000000000000e+00, 0.0000000000000e+00, + 0.3298730512306e-05, 0.5978801994625e+01, 0.4265981595566e+00, + 0.2733376706079e-05, 0.7665413691040e+00, 0.1059381944224e+01, + 0.9426389657270e-06, 0.3710201265838e+01, 0.2061856251104e+00, + 0.8187517749552e-06, 0.3390675605802e+00, 0.2204125344462e+00, + 0.4080447871819e-06, 0.4552296640088e+00, 0.5225775174439e+00, + + 0.3169973017028e-06, 0.3445455899321e+01, 0.5368044267797e+00, + 0.2438098615549e-06, 0.5664675150648e+01, 0.3664874755930e-01, + 0.2601897517235e-06, 0.1931894095697e+01, 0.1495633313810e+00, + 0.2314558080079e-06, 0.3666319115574e+00, 0.3961708870310e-01, + 0.1962549548002e-06, 0.3167411699020e+01, 0.7626583626240e-01, + 0.2180518287925e-06, 0.1544420746580e+01, 0.7113454667900e-02, + 0.1451382442868e-06, 0.1583756740070e+01, 0.1102062672231e+00, + 0.1358439007389e-06, 0.5239941758280e+01, 0.6398972393349e+00, + 0.1050585898028e-06, 0.2266958352859e+01, 0.3163918923335e+00, + 0.1050029870186e-06, 0.2711495250354e+01, 0.4194847048887e+00, + + 0.9934920679800e-07, 0.1116208151396e+01, 0.1589072916335e+01, + 0.1048395331560e-06, 0.3408619600206e+01, 0.1021328554739e+02, + 0.8370147196668e-07, 0.3810459401087e+01, 0.2535050500000e-01, + 0.7989856510998e-07, 0.3769910473647e+01, 0.7329749511860e-01, + 0.5441221655233e-07, 0.2416994903374e+01, 0.1030928125552e+00, + 0.4610812906784e-07, 0.5858503336994e+01, 0.4337116142245e+00, + 0.3923022803444e-07, 0.3354170010125e+00, 0.1484170571900e-02, + 0.2610725582128e-07, 0.5410600646324e+01, 0.6327837846670e+00, + 0.2455279767721e-07, 0.6120216681403e+01, 0.1162474756779e+01, + 0.2375530706525e-07, 0.6055443426143e+01, 0.1052268489556e+01, + + 0.1782967577553e-07, 0.3146108708004e+01, 0.8460828644453e+00, + 0.1581687095238e-07, 0.6255496089819e+00, 0.3340612434717e+01, + 0.1594657672461e-07, 0.3782604300261e+01, 0.1066495398892e+01, + 0.1563448615040e-07, 0.1997775733196e+01, 0.2022531624851e+00, + 0.1463624258525e-07, 0.1736316792088e+00, 0.3516457698740e-01, + 0.1331585056673e-07, 0.4331941830747e+01, 0.9491756770005e+00, + 0.1130634557637e-07, 0.6152017751825e+01, 0.2968341143800e-02, + 0.1028949607145e-07, 0.2101792614637e+00, 0.2275259891141e+00, + 0.1024074971618e-07, 0.4071833211074e+01, 0.5070101000000e-01, + 0.8826956060303e-08, 0.4861633688145e+00, 0.2093666171530e+00, + + 0.8572230171541e-08, 0.5268190724302e+01, 0.4110125927500e-01, + 0.7649332643544e-08, 0.5134543417106e+01, 0.2608790314060e+02, + 0.8581673291033e-08, 0.2920218146681e+01, 0.1480791608091e+00, + 0.8430589300938e-08, 0.3604576619108e+01, 0.2172315424036e+00, + 0.7776165501012e-08, 0.3772942249792e+01, 0.6373574839730e-01, + 0.8311070234408e-08, 0.6200412329888e+01, 0.3235053470014e+00, + 0.6927365212582e-08, 0.4543353113437e+01, 0.8531963191132e+00, + 0.6791574208598e-08, 0.2882188406238e+01, 0.7181332454670e-01, + 0.5593100811839e-08, 0.1776646892780e+01, 0.7429900518901e+00, + 0.4553381853021e-08, 0.3949617611240e+01, 0.7775000683430e-01, + + 0.5758000450068e-08, 0.3859251775075e+01, 0.1990721704425e+00, + 0.4281283457133e-08, 0.1466294631206e+01, 0.2118763888447e+01, + 0.4206935661097e-08, 0.5421776011706e+01, 0.1104591729320e-01, + 0.4213751641837e-08, 0.3412048993322e+01, 0.2243449970715e+00, + 0.5310506239878e-08, 0.5421641370995e+00, 0.5154640627760e+00, + 0.3827450341320e-08, 0.8887314524995e+00, 0.1510475019529e+00, + 0.4292435241187e-08, 0.1405043757194e+01, 0.1422690933580e-01, + 0.3189780702289e-08, 0.1060049293445e+01, 0.1173197218910e+00, + 0.3226611928069e-08, 0.6270858897442e+01, 0.2164800718209e+00, + 0.2893897608830e-08, 0.5117563223301e+01, 0.6470106940028e+00, + + 0.3239852024578e-08, 0.4079092237983e+01, 0.2101180877357e+00, + 0.2956892222200e-08, 0.1594917021704e+01, 0.3092784376656e+00, + 0.2980177912437e-08, 0.5258787667564e+01, 0.4155522422634e+00, + 0.3163725690776e-08, 0.3854589225479e+01, 0.8582758298370e-01, + 0.2662262399118e-08, 0.3561326430187e+01, 0.5257585094865e+00, + 0.2766689135729e-08, 0.3180732086830e+00, 0.1385174140878e+00, + 0.2411600278464e-08, 0.3324798335058e+01, 0.5439178814476e+00, + 0.2483527695131e-08, 0.4169069291947e+00, 0.5336234347371e+00, + 0.7788777276590e-09, 0.1900569908215e+01, 0.5217580628120e+02 }; + +/* SSB-to-Sun, T^1, X */ + static const double s1x[] = { + -0.1296310361520e-07, 0.0000000000000e+00, 0.0000000000000e+00, + 0.8975769009438e-08, 0.1128891609250e+01, 0.4265981595566e+00, + 0.7771113441307e-08, 0.2706039877077e+01, 0.2061856251104e+00, + 0.7538303866642e-08, 0.2191281289498e+01, 0.2204125344462e+00, + 0.6061384579336e-08, 0.3248167319958e+01, 0.1059381944224e+01, + 0.5726994235594e-08, 0.5569981398610e+01, 0.5225775174439e+00, + 0.5616492836424e-08, 0.5057386614909e+01, 0.5368044267797e+00, + 0.1010881584769e-08, 0.3473577116095e+01, 0.7113454667900e-02, + 0.7259606157626e-09, 0.3651858593665e+00, 0.6398972393349e+00, + 0.8755095026935e-09, 0.1662835408338e+01, 0.4194847048887e+00, + + 0.5370491182812e-09, 0.1327673878077e+01, 0.4337116142245e+00, + 0.5743773887665e-09, 0.4250200846687e+01, 0.2132990797783e+00, + 0.4408103140300e-09, 0.3598752574277e+01, 0.1589072916335e+01, + 0.3101892374445e-09, 0.4887822983319e+01, 0.1052268489556e+01, + 0.3209453713578e-09, 0.9702272295114e+00, 0.5296909721118e+00, + 0.3017228286064e-09, 0.5484462275949e+01, 0.1066495398892e+01, + 0.3200700038601e-09, 0.2846613338643e+01, 0.1495633313810e+00, + 0.2137637279911e-09, 0.5692163292729e+00, 0.3163918923335e+00, + 0.1899686386727e-09, 0.2061077157189e+01, 0.2275259891141e+00, + 0.1401994545308e-09, 0.4177771136967e+01, 0.1102062672231e+00, + + 0.1578057810499e-09, 0.5782460597335e+01, 0.7626583626240e-01, + 0.1237713253351e-09, 0.5705900866881e+01, 0.5154640627760e+00, + 0.1313076837395e-09, 0.5163438179576e+01, 0.3664874755930e-01, + 0.1184963304860e-09, 0.3054804427242e+01, 0.6327837846670e+00, + 0.1238130878565e-09, 0.2317292575962e+01, 0.3961708870310e-01, + 0.1015959527736e-09, 0.2194643645526e+01, 0.7329749511860e-01, + 0.9017954423714e-10, 0.2868603545435e+01, 0.1990721704425e+00, + 0.8668024955603e-10, 0.4923849675082e+01, 0.5439178814476e+00, + 0.7756083930103e-10, 0.3014334135200e+01, 0.9491756770005e+00, + 0.7536503401741e-10, 0.2704886279769e+01, 0.1030928125552e+00, + + 0.5483308679332e-10, 0.6010983673799e+01, 0.8531963191132e+00, + 0.5184339620428e-10, 0.1952704573291e+01, 0.2093666171530e+00, + 0.5108658712030e-10, 0.2958575786649e+01, 0.2172315424036e+00, + 0.5019424524650e-10, 0.1736317621318e+01, 0.2164800718209e+00, + 0.4909312625978e-10, 0.3167216416257e+01, 0.2101180877357e+00, + 0.4456638901107e-10, 0.7697579923471e+00, 0.3235053470014e+00, + 0.4227030350925e-10, 0.3490910137928e+01, 0.6373574839730e-01, + 0.4095456040093e-10, 0.5178888984491e+00, 0.6470106940028e+00, + 0.4990537041422e-10, 0.3323887668974e+01, 0.1422690933580e-01, + 0.4321170010845e-10, 0.4288484987118e+01, 0.7358765972222e+00, + + 0.3544072091802e-10, 0.6021051579251e+01, 0.5265099800692e+00, + 0.3480198638687e-10, 0.4600027054714e+01, 0.5328719641544e+00, + 0.3440287244435e-10, 0.4349525970742e+01, 0.8582758298370e-01, + 0.3330628322713e-10, 0.2347391505082e+01, 0.1104591729320e-01, + 0.2973060707184e-10, 0.4789409286400e+01, 0.5257585094865e+00, + 0.2932606766089e-10, 0.5831693799927e+01, 0.5336234347371e+00, + 0.2876972310953e-10, 0.2692638514771e+01, 0.1173197218910e+00, + 0.2827488278556e-10, 0.2056052487960e+01, 0.2022531624851e+00, + 0.2515028239756e-10, 0.7411863262449e+00, 0.9597935788730e-01, + 0.2853033744415e-10, 0.3948481024894e+01, 0.2118763888447e+01 }; + +/* SSB-to-Sun, T^1, Y */ + static const double s1y[] = { + 0.8989047573576e-08, 0.5840593672122e+01, 0.4265981595566e+00, + 0.7815938401048e-08, 0.1129664707133e+01, 0.2061856251104e+00, + 0.7550926713280e-08, 0.6196589104845e+00, 0.2204125344462e+00, + 0.6056556925895e-08, 0.1677494667846e+01, 0.1059381944224e+01, + 0.5734142698204e-08, 0.4000920852962e+01, 0.5225775174439e+00, + 0.5614341822459e-08, 0.3486722577328e+01, 0.5368044267797e+00, + 0.1028678147656e-08, 0.1877141024787e+01, 0.7113454667900e-02, + 0.7270792075266e-09, 0.5077167301739e+01, 0.6398972393349e+00, + 0.8734141726040e-09, 0.9069550282609e-01, 0.4194847048887e+00, + 0.5377371402113e-09, 0.6039381844671e+01, 0.4337116142245e+00, + + 0.4729719431571e-09, 0.2153086311760e+01, 0.2132990797783e+00, + 0.4458052820973e-09, 0.5059830025565e+01, 0.5296909721118e+00, + 0.4406855467908e-09, 0.2027971692630e+01, 0.1589072916335e+01, + 0.3101659310977e-09, 0.3317677981860e+01, 0.1052268489556e+01, + 0.3016749232545e-09, 0.3913703482532e+01, 0.1066495398892e+01, + 0.3198541352656e-09, 0.1275513098525e+01, 0.1495633313810e+00, + 0.2142065389871e-09, 0.5301351614597e+01, 0.3163918923335e+00, + 0.1902615247592e-09, 0.4894943352736e+00, 0.2275259891141e+00, + 0.1613410990871e-09, 0.2449891130437e+01, 0.1102062672231e+00, + 0.1576992165097e-09, 0.4211421447633e+01, 0.7626583626240e-01, + + 0.1241637259894e-09, 0.4140803368133e+01, 0.5154640627760e+00, + 0.1313974830355e-09, 0.3591920305503e+01, 0.3664874755930e-01, + 0.1181697118258e-09, 0.1506314382788e+01, 0.6327837846670e+00, + 0.1238239742779e-09, 0.7461405378404e+00, 0.3961708870310e-01, + 0.1010107068241e-09, 0.6271010795475e+00, 0.7329749511860e-01, + 0.9226316616509e-10, 0.1259158839583e+01, 0.1990721704425e+00, + 0.8664946419555e-10, 0.3353244696934e+01, 0.5439178814476e+00, + 0.7757230468978e-10, 0.1447677295196e+01, 0.9491756770005e+00, + 0.7693168628139e-10, 0.1120509896721e+01, 0.1030928125552e+00, + 0.5487897454612e-10, 0.4439380426795e+01, 0.8531963191132e+00, + + 0.5196118677218e-10, 0.3788856619137e+00, 0.2093666171530e+00, + 0.5110853339935e-10, 0.1386879372016e+01, 0.2172315424036e+00, + 0.5027804534813e-10, 0.1647881805466e+00, 0.2164800718209e+00, + 0.4922485922674e-10, 0.1594315079862e+01, 0.2101180877357e+00, + 0.6155599524400e-10, 0.0000000000000e+00, 0.0000000000000e+00, + 0.4447147832161e-10, 0.5480720918976e+01, 0.3235053470014e+00, + 0.4144691276422e-10, 0.1931371033660e+01, 0.6373574839730e-01, + 0.4099950625452e-10, 0.5229611294335e+01, 0.6470106940028e+00, + 0.5060541682953e-10, 0.1731112486298e+01, 0.1422690933580e-01, + 0.4293615946300e-10, 0.2714571038925e+01, 0.7358765972222e+00, + + 0.3545659845763e-10, 0.4451041444634e+01, 0.5265099800692e+00, + 0.3479112041196e-10, 0.3029385448081e+01, 0.5328719641544e+00, + 0.3438516493570e-10, 0.2778507143731e+01, 0.8582758298370e-01, + 0.3297341285033e-10, 0.7898709807584e+00, 0.1104591729320e-01, + 0.2972585818015e-10, 0.3218785316973e+01, 0.5257585094865e+00, + 0.2931707295017e-10, 0.4260731012098e+01, 0.5336234347371e+00, + 0.2897198149403e-10, 0.1120753978101e+01, 0.1173197218910e+00, + 0.2832293240878e-10, 0.4597682717827e+00, 0.2022531624851e+00, + 0.2864348326612e-10, 0.2169939928448e+01, 0.9597935788730e-01, + 0.2852714675471e-10, 0.2377659870578e+01, 0.2118763888447e+01 }; + +/* SSB-to-Sun, T^1, Z */ + static const double s1z[] = { + 0.5444220475678e-08, 0.1803825509310e+01, 0.2132990797783e+00, + 0.3883412695596e-08, 0.4668616389392e+01, 0.5296909721118e+00, + 0.1334341434551e-08, 0.0000000000000e+00, 0.0000000000000e+00, + 0.3730001266883e-09, 0.5401405918943e+01, 0.2061856251104e+00, + 0.2894929197956e-09, 0.4932415609852e+01, 0.2204125344462e+00, + 0.2857950357701e-09, 0.3154625362131e+01, 0.7478166569050e-01, + 0.2499226432292e-09, 0.3657486128988e+01, 0.4265981595566e+00, + 0.1937705443593e-09, 0.5740434679002e+01, 0.1059381944224e+01, + 0.1374894396320e-09, 0.1712857366891e+01, 0.5368044267797e+00, + 0.1217248678408e-09, 0.2312090870932e+01, 0.5225775174439e+00, + + 0.7961052740870e-10, 0.5283368554163e+01, 0.3813291813120e-01, + 0.4979225949689e-10, 0.4298290471860e+01, 0.4194847048887e+00, + 0.4388552286597e-10, 0.6145515047406e+01, 0.7113454667900e-02, + 0.2586835212560e-10, 0.3019448001809e+01, 0.6398972393349e+00 }; + +/* SSB-to-Sun, T^2, X */ + static const double s2x[] = { + 0.1603551636587e-11, 0.4404109410481e+01, 0.2061856251104e+00, + 0.1556935889384e-11, 0.4818040873603e+00, 0.2204125344462e+00, + 0.1182594414915e-11, 0.9935762734472e+00, 0.5225775174439e+00, + 0.1158794583180e-11, 0.3353180966450e+01, 0.5368044267797e+00, + 0.9597358943932e-12, 0.5567045358298e+01, 0.2132990797783e+00, + 0.6511516579605e-12, 0.5630872420788e+01, 0.4265981595566e+00, + 0.7419792747688e-12, 0.2156188581957e+01, 0.5296909721118e+00, + 0.3951972655848e-12, 0.1981022541805e+01, 0.1059381944224e+01, + 0.4478223877045e-12, 0.0000000000000e+00, 0.0000000000000e+00 }; + +/* SSB-to-Sun, T^2, Y */ + static const double s2y[] = { + 0.1609114495091e-11, 0.2831096993481e+01, 0.2061856251104e+00, + 0.1560330784946e-11, 0.5193058213906e+01, 0.2204125344462e+00, + 0.1183535479202e-11, 0.5707003443890e+01, 0.5225775174439e+00, + 0.1158183066182e-11, 0.1782400404928e+01, 0.5368044267797e+00, + 0.1032868027407e-11, 0.4036925452011e+01, 0.2132990797783e+00, + 0.6540142847741e-12, 0.4058241056717e+01, 0.4265981595566e+00, + 0.7305236491596e-12, 0.6175401942957e+00, 0.5296909721118e+00, + -0.5580725052968e-12, 0.0000000000000e+00, 0.0000000000000e+00, + 0.3946122651015e-12, 0.4108265279171e+00, 0.1059381944224e+01 }; + +/* SSB-to-Sun, T^2, Z */ + static const double s2z[] = { + 0.3749920358054e-12, 0.3230285558668e+01, 0.2132990797783e+00, + 0.2735037220939e-12, 0.6154322683046e+01, 0.5296909721118e+00 }; + +/* Pointers to coefficient arrays, in x,y,z sets */ + static const double *ce0[] = { e0x, e0y, e0z }, + *ce1[] = { e1x, e1y, e1z }, + *ce2[] = { e2x, e2y, e2z }, + *cs0[] = { s0x, s0y, s0z }, + *cs1[] = { s1x, s1y, s1z }, + *cs2[] = { s2x, s2y, s2z }; + const double *coeffs; + +/* Numbers of terms for each component of the model, in x,y,z sets */ + static const int ne0[3] = {(int)(sizeof e0x / sizeof (double) / 3), + (int)(sizeof e0y / sizeof (double) / 3), + (int)(sizeof e0z / sizeof (double) / 3) }, + ne1[3] = {(int)(sizeof e1x / sizeof (double) / 3), + (int)(sizeof e1y / sizeof (double) / 3), + (int)(sizeof e1z / sizeof (double) / 3) }, + ne2[3] = {(int)(sizeof e2x / sizeof (double) / 3), + (int)(sizeof e2y / sizeof (double) / 3), + (int)(sizeof e2z / sizeof (double) / 3) }, + ns0[3] = {(int)(sizeof s0x / sizeof (double) / 3), + (int)(sizeof s0y / sizeof (double) / 3), + (int)(sizeof s0z / sizeof (double) / 3) }, + ns1[3] = {(int)(sizeof s1x / sizeof (double) / 3), + (int)(sizeof s1y / sizeof (double) / 3), + (int)(sizeof s1z / sizeof (double) / 3) }, + ns2[3] = {(int)(sizeof s2x / sizeof (double) / 3), + (int)(sizeof s2y / sizeof (double) / 3), + (int)(sizeof s2z / sizeof (double) / 3) }; + int nterms; + +/* Miscellaneous */ + int jstat, i, j; + double t, t2, xyz, xyzd, a, b, c, ct, p, cp, + ph[3], vh[3], pb[3], vb[3], x, y, z; + +/*--------------------------------------------------------------------*/ + +/* Time since reference epoch, Julian years. */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJY; + t2 = t*t; + +/* Set status. */ + jstat = fabs(t) <= 100.0 ? 0 : 1; + +/* X then Y then Z. */ + for (i = 0; i < 3; i++) { + + /* Initialize position and velocity component. */ + xyz = 0.0; + xyzd = 0.0; + + /* ------------------------------------------------ */ + /* Obtain component of Sun to Earth ecliptic vector */ + /* ------------------------------------------------ */ + + /* Sun to Earth, T^0 terms. */ + coeffs = ce0[i]; + nterms = ne0[i]; + for (j = 0; j < nterms; j++) { + a = *coeffs++; + b = *coeffs++; + c = *coeffs++; + p = b + c*t; + xyz += a*cos(p); + xyzd -= a*c*sin(p); + } + + /* Sun to Earth, T^1 terms. */ + coeffs = ce1[i]; + nterms = ne1[i]; + for (j = 0; j < nterms; j++) { + a = *coeffs++; + b = *coeffs++; + c = *coeffs++; + ct = c*t; + p = b + ct; + cp = cos(p); + xyz += a*t*cp; + xyzd += a*( cp - ct*sin(p) ); + } + + /* Sun to Earth, T^2 terms. */ + coeffs = ce2[i]; + nterms = ne2[i]; + for (j = 0; j < nterms; j++) { + a = *coeffs++; + b = *coeffs++; + c = *coeffs++; + ct = c*t; + p = b + ct; + cp = cos(p); + xyz += a*t2*cp; + xyzd += a*t*( 2.0*cp - ct*sin(p) ); + } + + /* Heliocentric Earth position and velocity component. */ + ph[i] = xyz; + vh[i] = xyzd / ERFA_DJY; + + /* ------------------------------------------------ */ + /* Obtain component of SSB to Earth ecliptic vector */ + /* ------------------------------------------------ */ + + /* SSB to Sun, T^0 terms. */ + coeffs = cs0[i]; + nterms = ns0[i]; + for (j = 0; j < nterms; j++) { + a = *coeffs++; + b = *coeffs++; + c = *coeffs++; + p = b + c*t; + xyz += a*cos(p); + xyzd -= a*c*sin(p); + } + + /* SSB to Sun, T^1 terms. */ + coeffs = cs1[i]; + nterms = ns1[i]; + for (j = 0; j < nterms; j++) { + a = *coeffs++; + b = *coeffs++; + c = *coeffs++; + ct = c*t; + p = b + ct; + cp = cos(p); + xyz += a*t*cp; + xyzd += a*(cp - ct*sin(p)); + } + + /* SSB to Sun, T^2 terms. */ + coeffs = cs2[i]; + nterms = ns2[i]; + for (j = 0; j < nterms; j++) { + a = *coeffs++; + b = *coeffs++; + c = *coeffs++; + ct = c*t; + p = b + ct; + cp = cos(p); + xyz += a*t2*cp; + xyzd += a*t*(2.0*cp - ct*sin(p)); + } + + /* Barycentric Earth position and velocity component. */ + pb[i] = xyz; + vb[i] = xyzd / ERFA_DJY; + + /* Next Cartesian component. */ + } + +/* Rotate from ecliptic to BCRS coordinates. */ + + x = ph[0]; + y = ph[1]; + z = ph[2]; + pvh[0][0] = x + am12*y + am13*z; + pvh[0][1] = am21*x + am22*y + am23*z; + pvh[0][2] = am32*y + am33*z; + + x = vh[0]; + y = vh[1]; + z = vh[2]; + pvh[1][0] = x + am12*y + am13*z; + pvh[1][1] = am21*x + am22*y + am23*z; + pvh[1][2] = am32*y + am33*z; + + x = pb[0]; + y = pb[1]; + z = pb[2]; + pvb[0][0] = x + am12*y + am13*z; + pvb[0][1] = am21*x + am22*y + am23*z; + pvb[0][2] = am32*y + am33*z; + + x = vb[0]; + y = vb[1]; + z = vb[2]; + pvb[1][0] = x + am12*y + am13*z; + pvb[1][1] = am21*x + am22*y + am23*z; + pvb[1][2] = am32*y + am33*z; + +/* Return the status. */ + return jstat; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/eqeq94.c b/erfa/eqeq94.c new file mode 100644 index 0000000..669515d --- /dev/null +++ b/erfa/eqeq94.c @@ -0,0 +1,141 @@ +#include "erfa.h" + +double eraEqeq94(double date1, double date2) +/* +** - - - - - - - - - - +** e r a E q e q 9 4 +** - - - - - - - - - - +** +** Equation of the equinoxes, IAU 1994 model. +** +** Given: +** date1,date2 double TDB date (Note 1) +** +** Returned (function value): +** double equation of the equinoxes (Note 2) +** +** Notes: +** +** 1) The date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The result, which is in radians, operates in the following sense: +** +** Greenwich apparent ST = GMST + equation of the equinoxes +** +** Called: +** eraAnpm normalize angle into range +/- pi +** eraNut80 nutation, IAU 1980 +** eraObl80 mean obliquity, IAU 1980 +** +** References: +** +** IAU Resolution C7, Recommendation 3 (1994). +** +** Capitaine, N. & Gontier, A.-M., 1993, Astron. Astrophys., 275, +** 645-650. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, om, dpsi, deps, eps0, ee; + + +/* Interval between fundamental epoch J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Longitude of the mean ascending node of the lunar orbit on the */ +/* ecliptic, measured from the mean equinox of date. */ + om = eraAnpm((450160.280 + (-482890.539 + + (7.455 + 0.008 * t) * t) * t) * ERFA_DAS2R + + fmod(-5.0 * t, 1.0) * ERFA_D2PI); + +/* Nutation components and mean obliquity. */ + eraNut80(date1, date2, &dpsi, &deps); + eps0 = eraObl80(date1, date2); + +/* Equation of the equinoxes. */ + ee = dpsi*cos(eps0) + ERFA_DAS2R*(0.00264*sin(om) + 0.000063*sin(om+om)); + + return ee; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/era00.c b/erfa/era00.c new file mode 100644 index 0000000..47f642c --- /dev/null +++ b/erfa/era00.c @@ -0,0 +1,145 @@ +#include "erfa.h" + +double eraEra00(double dj1, double dj2) +/* +** - - - - - - - - - +** e r a E r a 0 0 +** - - - - - - - - - +** +** Earth rotation angle (IAU 2000 model). +** +** Given: +** dj1,dj2 double UT1 as a 2-part Julian Date (see note) +** +** Returned (function value): +** double Earth rotation angle (radians), range 0-2pi +** +** Notes: +** +** 1) The UT1 date dj1+dj2 is a Julian Date, apportioned in any +** convenient way between the arguments dj1 and dj2. For example, +** JD(UT1)=2450123.7 could be expressed in any of these ways, +** among others: +** +** dj1 dj2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. The date & time method is +** best matched to the algorithm used: maximum precision is +** delivered when the dj1 argument is for 0hrs UT1 on the day in +** question and the dj2 argument lies in the range 0 to 1, or vice +** versa. +** +** 2) The algorithm is adapted from Expression 22 of Capitaine et al. +** 2000. The time argument has been expressed in days directly, +** and, to retain precision, integer contributions have been +** eliminated. The same formulation is given in IERS Conventions +** (2003), Chap. 5, Eq. 14. +** +** Called: +** eraAnp normalize angle into range 0 to 2pi +** +** References: +** +** Capitaine N., Guinot B. and McCarthy D.D, 2000, Astron. +** Astrophys., 355, 398-405. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double d1, d2, t, f, theta; + + +/* Days since fundamental epoch. */ + if (dj1 < dj2) { + d1 = dj1; + d2 = dj2; + } else { + d1 = dj2; + d2 = dj1; + } + t = d1 + (d2- ERFA_DJ00); + +/* Fractional part of T (days). */ + f = fmod(d1, 1.0) + fmod(d2, 1.0); + +/* Earth rotation angle at this UT1. */ + theta = eraAnp(ERFA_D2PI * (f + 0.7790572732640 + + 0.00273781191135448 * t)); + + return theta; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/erfa.h b/erfa/erfa.h new file mode 100644 index 0000000..b59d9e0 --- /dev/null +++ b/erfa/erfa.h @@ -0,0 +1,504 @@ +#ifndef ERFAHDEF +#define ERFAHDEF + +/* +** - - - - - - - +** e r f a . h +** - - - - - - - +** +** Prototype function declarations for ERFA library. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ + +#include "erfam.h" +#include "math.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Astronomy/Calendars */ +int eraCal2jd(int iy, int im, int id, double *djm0, double *djm); +double eraEpb(double dj1, double dj2); +void eraEpb2jd(double epb, double *djm0, double *djm); +double eraEpj(double dj1, double dj2); +void eraEpj2jd(double epj, double *djm0, double *djm); +int eraJd2cal(double dj1, double dj2, + int *iy, int *im, int *id, double *fd); +int eraJdcalf(int ndp, double dj1, double dj2, int iymdf[4]); + +/* Astronomy/Astrometry */ +void eraAb(double pnat[3], double v[3], double s, double bm1, + double ppr[3]); +void eraApcg(double date1, double date2, + double ebpv[2][3], double ehp[3], + eraASTROM *astrom); +void eraApcg13(double date1, double date2, eraASTROM *astrom); +void eraApci(double date1, double date2, + double ebpv[2][3], double ehp[3], + double x, double y, double s, + eraASTROM *astrom); +void eraApci13(double date1, double date2, + eraASTROM *astrom, double *eo); +void eraApco(double date1, double date2, + double ebpv[2][3], double ehp[3], + double x, double y, double s, double theta, + double elong, double phi, double hm, + double xp, double yp, double sp, + double refa, double refb, + eraASTROM *astrom); +int eraApco13(double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tk, double rh, double wl, + eraASTROM *astrom, double *eo); +void eraApcs(double date1, double date2, double pv[2][3], + double ebpv[2][3], double ehp[3], + eraASTROM *astrom); +void eraApcs13(double date1, double date2, double pv[2][3], + eraASTROM *astrom); +void eraAper(double theta, eraASTROM *astrom); +void eraAper13(double ut11, double ut12, eraASTROM *astrom); +void eraApio(double sp, double theta, + double elong, double phi, double hm, double xp, double yp, + double refa, double refb, + eraASTROM *astrom); +int eraApio13(double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tk, double rh, double wl, + eraASTROM *astrom); +void eraAtci13(double rc, double dc, + double pr, double pd, double px, double rv, + double date1, double date2, + double *ri, double *di, double *eo); +void eraAtciq(double rc, double dc, double pr, double pd, + double px, double rv, eraASTROM *astrom, + double *ri, double *di); +void eraAtciqn(double rc, double dc, double pr, double pd, + double px, double rv, eraASTROM *astrom, + int n, eraLDBODY b[], double *ri, double *di); +void eraAtciqz(double rc, double dc, eraASTROM *astrom, + double *ri, double *di); +int eraAtco13(double rc, double dc, + double pr, double pd, double px, double rv, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tk, double rh, double wl, + double *aob, double *zob, double *hob, + double *dob, double *rob, double *eo); +void eraAtic13(double ri, double di, + double date1, double date2, + double *rc, double *dc, double *eo); +void eraAticq(double ri, double di, eraASTROM *astrom, + double *rc, double *dc); +void eraAticqn(double ri, double di, eraASTROM *astrom, + int n, eraLDBODY b[], double *rc, double *dc); +int eraAtio13(double ri, double di, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tk, double rh, double wl, + double *aob, double *zob, double *hob, + double *dob, double *rob); +void eraAtioq(double ri, double di, eraASTROM *astrom, + double *aob, double *zob, + double *hob, double *dob, double *rob); +int eraAtoc13(const char *type, double ob1, double ob2, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tk, double rh, double wl, + double *rc, double *dc); +int eraAtoi13(const char *type, double ob1, double ob2, + double utc1, double utc2, double dut1, + double elong, double phi, double hm, double xp, double yp, + double phpa, double tk, double rh, double wl, + double *ri, double *di); +void eraAtoiq(const char *type, + double ob1, double ob2, eraASTROM *astrom, + double *ri, double *di); +void eraLd(double bm, double p[3], double q[3], double e[3], + double em, double dlim, double p1[3]); +void eraLdn(int n, eraLDBODY b[], double ob[3], double sc[3], + double sn[3]); +void eraLdsun(double p[3], double e[3], double em, double p1[3]); +void eraPmpx(double rc, double dc, double pr, double pd, + double px, double rv, double pmt, double vob[3], + double pco[3]); +int eraPmsafe(double ra1, double dec1, double pmr1, double pmd1, + double px1, double rv1, + double ep1a, double ep1b, double ep2a, double ep2b, + double *ra2, double *dec2, double *pmr2, double *pmd2, + double *px2, double *rv2); +void eraPvtob(double elong, double phi, double hm, + double xp, double yp, double sp, double theta, + double pv[2][3]); +void eraRefco(double phpa, double tk, double rh, double wl, + double *refa, double *refb); + +/* Astronomy/Ephemerides */ +int eraEpv00(double date1, double date2, + double pvh[2][3], double pvb[2][3]); +int eraPlan94(double date1, double date2, int np, double pv[2][3]); + +/* Astronomy/FundamentalArgs */ +double eraFad03(double t); +double eraFae03(double t); +double eraFaf03(double t); +double eraFaju03(double t); +double eraFal03(double t); +double eraFalp03(double t); +double eraFama03(double t); +double eraFame03(double t); +double eraFane03(double t); +double eraFaom03(double t); +double eraFapa03(double t); +double eraFasa03(double t); +double eraFaur03(double t); +double eraFave03(double t); + +/* Astronomy/PrecNutPolar */ +void eraBi00(double *dpsibi, double *depsbi, double *dra); +void eraBp00(double date1, double date2, + double rb[3][3], double rp[3][3], double rbp[3][3]); +void eraBp06(double date1, double date2, + double rb[3][3], double rp[3][3], double rbp[3][3]); +void eraBpn2xy(double rbpn[3][3], double *x, double *y); +void eraC2i00a(double date1, double date2, double rc2i[3][3]); +void eraC2i00b(double date1, double date2, double rc2i[3][3]); +void eraC2i06a(double date1, double date2, double rc2i[3][3]); +void eraC2ibpn(double date1, double date2, double rbpn[3][3], + double rc2i[3][3]); +void eraC2ixy(double date1, double date2, double x, double y, + double rc2i[3][3]); +void eraC2ixys(double x, double y, double s, double rc2i[3][3]); +void eraC2t00a(double tta, double ttb, double uta, double utb, + double xp, double yp, double rc2t[3][3]); +void eraC2t00b(double tta, double ttb, double uta, double utb, + double xp, double yp, double rc2t[3][3]); +void eraC2t06a(double tta, double ttb, double uta, double utb, + double xp, double yp, double rc2t[3][3]); +void eraC2tcio(double rc2i[3][3], double era, double rpom[3][3], + double rc2t[3][3]); +void eraC2teqx(double rbpn[3][3], double gst, double rpom[3][3], + double rc2t[3][3]); +void eraC2tpe(double tta, double ttb, double uta, double utb, + double dpsi, double deps, double xp, double yp, + double rc2t[3][3]); +void eraC2txy(double tta, double ttb, double uta, double utb, + double x, double y, double xp, double yp, + double rc2t[3][3]); +double eraEo06a(double date1, double date2); +double eraEors(double rnpb[3][3], double s); +void eraFw2m(double gamb, double phib, double psi, double eps, + double r[3][3]); +void eraFw2xy(double gamb, double phib, double psi, double eps, + double *x, double *y); +void eraNum00a(double date1, double date2, double rmatn[3][3]); +void eraNum00b(double date1, double date2, double rmatn[3][3]); +void eraNum06a(double date1, double date2, double rmatn[3][3]); +void eraNumat(double epsa, double dpsi, double deps, double rmatn[3][3]); +void eraNut00a(double date1, double date2, double *dpsi, double *deps); +void eraNut00b(double date1, double date2, double *dpsi, double *deps); +void eraNut06a(double date1, double date2, double *dpsi, double *deps); +void eraNut80(double date1, double date2, double *dpsi, double *deps); +void eraNutm80(double date1, double date2, double rmatn[3][3]); +double eraObl06(double date1, double date2); +double eraObl80(double date1, double date2); +void eraP06e(double date1, double date2, + double *eps0, double *psia, double *oma, double *bpa, + double *bqa, double *pia, double *bpia, + double *epsa, double *chia, double *za, double *zetaa, + double *thetaa, double *pa, + double *gam, double *phi, double *psi); +void eraPb06(double date1, double date2, + double *bzeta, double *bz, double *btheta); +void eraPfw06(double date1, double date2, + double *gamb, double *phib, double *psib, double *epsa); +void eraPmat00(double date1, double date2, double rbp[3][3]); +void eraPmat06(double date1, double date2, double rbp[3][3]); +void eraPmat76(double date1, double date2, double rmatp[3][3]); +void eraPn00(double date1, double date2, double dpsi, double deps, + double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]); +void eraPn00a(double date1, double date2, + double *dpsi, double *deps, double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]); +void eraPn00b(double date1, double date2, + double *dpsi, double *deps, double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]); +void eraPn06(double date1, double date2, double dpsi, double deps, + double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]); +void eraPn06a(double date1, double date2, + double *dpsi, double *deps, double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]); +void eraPnm00a(double date1, double date2, double rbpn[3][3]); +void eraPnm00b(double date1, double date2, double rbpn[3][3]); +void eraPnm06a(double date1, double date2, double rnpb[3][3]); +void eraPnm80(double date1, double date2, double rmatpn[3][3]); +void eraPom00(double xp, double yp, double sp, double rpom[3][3]); +void eraPr00(double date1, double date2, double *dpsipr, double *depspr); +void eraPrec76(double ep01, double ep02, double ep11, double ep12, + double *zeta, double *z, double *theta); +double eraS00(double date1, double date2, double x, double y); +double eraS00a(double date1, double date2); +double eraS00b(double date1, double date2); +double eraS06(double date1, double date2, double x, double y); +double eraS06a(double date1, double date2); +double eraSp00(double date1, double date2); +void eraXy06(double date1, double date2, double *x, double *y); +void eraXys00a(double date1, double date2, + double *x, double *y, double *s); +void eraXys00b(double date1, double date2, + double *x, double *y, double *s); +void eraXys06a(double date1, double date2, + double *x, double *y, double *s); + +/* Astronomy/RotationAndTime */ +double eraEe00(double date1, double date2, double epsa, double dpsi); +double eraEe00a(double date1, double date2); +double eraEe00b(double date1, double date2); +double eraEe06a(double date1, double date2); +double eraEect00(double date1, double date2); +double eraEqeq94(double date1, double date2); +double eraEra00(double dj1, double dj2); +double eraGmst00(double uta, double utb, double tta, double ttb); +double eraGmst06(double uta, double utb, double tta, double ttb); +double eraGmst82(double dj1, double dj2); +double eraGst00a(double uta, double utb, double tta, double ttb); +double eraGst00b(double uta, double utb); +double eraGst06(double uta, double utb, double tta, double ttb, + double rnpb[3][3]); +double eraGst06a(double uta, double utb, double tta, double ttb); +double eraGst94(double uta, double utb); + +/* Astronomy/SpaceMotion */ +int eraPmsafe(double ra1, double dec1, double pmr1, double pmd1, + double px1, double rv1, double ep1a, double ep1b, + double ep2a, double ep2b, double *ra2, double *dec2, + double *pmr2, double *pmd2, double *px2, double *rv2); +int eraPvstar(double pv[2][3], double *ra, double *dec, + double *pmr, double *pmd, double *px, double *rv); +int eraStarpv(double ra, double dec, + double pmr, double pmd, double px, double rv, + double pv[2][3]); + +/* Astronomy/StarCatalogs */ +void eraFk52h(double r5, double d5, + double dr5, double dd5, double px5, double rv5, + double *rh, double *dh, + double *drh, double *ddh, double *pxh, double *rvh); +void eraFk5hip(double r5h[3][3], double s5h[3]); +void eraFk5hz(double r5, double d5, double date1, double date2, + double *rh, double *dh); +void eraH2fk5(double rh, double dh, + double drh, double ddh, double pxh, double rvh, + double *r5, double *d5, + double *dr5, double *dd5, double *px5, double *rv5); +void eraHfk5z(double rh, double dh, double date1, double date2, + double *r5, double *d5, double *dr5, double *dd5); +int eraStarpm(double ra1, double dec1, + double pmr1, double pmd1, double px1, double rv1, + double ep1a, double ep1b, double ep2a, double ep2b, + double *ra2, double *dec2, + double *pmr2, double *pmd2, double *px2, double *rv2); + +/* Astronomy/GeodeticGeocentric */ +int eraEform(int n, double *a, double *f); +int eraGc2gd(int n, double xyz[3], + double *elong, double *phi, double *height); +int eraGc2gde(double a, double f, double xyz[3], + double *elong, double *phi, double *height); +int eraGd2gc(int n, double elong, double phi, double height, + double xyz[3]); +int eraGd2gce(double a, double f, + double elong, double phi, double height, double xyz[3]); +void eraPvtob(double elong, double phi, double height, double xp, + double yp, double sp, double theta, double pv[2][3]); + +/* Astronomy/Timescales */ +int eraD2dtf(const char *scale, int ndp, double d1, double d2, + int *iy, int *im, int *id, int ihmsf[4]); +int eraDat(int iy, int im, int id, double fd, double *deltat); +double eraDtdb(double date1, double date2, + double ut, double elong, double u, double v); +int eraDtf2d(const char *scale, int iy, int im, int id, + int ihr, int imn, double sec, double *d1, double *d2); +int eraTaitt(double tai1, double tai2, double *tt1, double *tt2); +int eraTaiut1(double tai1, double tai2, double dta, + double *ut11, double *ut12); +int eraTaiutc(double tai1, double tai2, double *utc1, double *utc2); +int eraTcbtdb(double tcb1, double tcb2, double *tdb1, double *tdb2); +int eraTcgtt(double tcg1, double tcg2, double *tt1, double *tt2); +int eraTdbtcb(double tdb1, double tdb2, double *tcb1, double *tcb2); +int eraTdbtt(double tdb1, double tdb2, double dtr, + double *tt1, double *tt2); +int eraTttai(double tt1, double tt2, double *tai1, double *tai2); +int eraTttcg(double tt1, double tt2, double *tcg1, double *tcg2); +int eraTttdb(double tt1, double tt2, double dtr, + double *tdb1, double *tdb2); +int eraTtut1(double tt1, double tt2, double dt, + double *ut11, double *ut12); +int eraUt1tai(double ut11, double ut12, double dta, + double *tai1, double *tai2); +int eraUt1tt(double ut11, double ut12, double dt, + double *tt1, double *tt2); +int eraUt1utc(double ut11, double ut12, double dut1, + double *utc1, double *utc2); +int eraUtctai(double utc1, double utc2, double *tai1, double *tai2); +int eraUtcut1(double utc1, double utc2, double dut1, + double *ut11, double *ut12); + +/* VectorMatrix/AngleOps */ +void eraA2af(int ndp, double angle, char *sign, int idmsf[4]); +void eraA2tf(int ndp, double angle, char *sign, int ihmsf[4]); +int eraAf2a(char s, int ideg, int iamin, double asec, double *rad); +double eraAnp(double a); +double eraAnpm(double a); +void eraD2tf(int ndp, double days, char *sign, int ihmsf[4]); +int eraTf2a(char s, int ihour, int imin, double sec, double *rad); +int eraTf2d(char s, int ihour, int imin, double sec, double *days); + +/* VectorMatrix/BuildRotations */ +void eraRx(double phi, double r[3][3]); +void eraRy(double theta, double r[3][3]); +void eraRz(double psi, double r[3][3]); + +/* VectorMatrix/CopyExtendExtract */ +void eraCp(double p[3], double c[3]); +void eraCpv(double pv[2][3], double c[2][3]); +void eraCr(double r[3][3], double c[3][3]); +void eraP2pv(double p[3], double pv[2][3]); +void eraPv2p(double pv[2][3], double p[3]); + +/* VectorMatrix/Initialization */ +void eraIr(double r[3][3]); +void eraZp(double p[3]); +void eraZpv(double pv[2][3]); +void eraZr(double r[3][3]); + +/* VectorMatrix/MatrixOps */ +void eraRxr(double a[3][3], double b[3][3], double atb[3][3]); +void eraTr(double r[3][3], double rt[3][3]); + +/* VectorMatrix/MatrixVectorProducts */ +void eraRxp(double r[3][3], double p[3], double rp[3]); +void eraRxpv(double r[3][3], double pv[2][3], double rpv[2][3]); +void eraTrxp(double r[3][3], double p[3], double trp[3]); +void eraTrxpv(double r[3][3], double pv[2][3], double trpv[2][3]); + +/* VectorMatrix/RotationVectors */ +void eraRm2v(double r[3][3], double w[3]); +void eraRv2m(double w[3], double r[3][3]); + +/* VectorMatrix/SeparationAndAngle */ +double eraPap(double a[3], double b[3]); +double eraPas(double al, double ap, double bl, double bp); +double eraSepp(double a[3], double b[3]); +double eraSeps(double al, double ap, double bl, double bp); + +/* VectorMatrix/SphericalCartesian */ +void eraC2s(double p[3], double *theta, double *phi); +void eraP2s(double p[3], double *theta, double *phi, double *r); +void eraPv2s(double pv[2][3], + double *theta, double *phi, double *r, + double *td, double *pd, double *rd); +void eraS2c(double theta, double phi, double c[3]); +void eraS2p(double theta, double phi, double r, double p[3]); +void eraS2pv(double theta, double phi, double r, + double td, double pd, double rd, + double pv[2][3]); + +/* VectorMatrix/VectorOps */ +double eraPdp(double a[3], double b[3]); +double eraPm(double p[3]); +void eraPmp(double a[3], double b[3], double amb[3]); +void eraPn(double p[3], double *r, double u[3]); +void eraPpp(double a[3], double b[3], double apb[3]); +void eraPpsp(double a[3], double s, double b[3], double apsb[3]); +void eraPvdpv(double a[2][3], double b[2][3], double adb[2]); +void eraPvm(double pv[2][3], double *r, double *s); +void eraPvmpv(double a[2][3], double b[2][3], double amb[2][3]); +void eraPvppv(double a[2][3], double b[2][3], double apb[2][3]); +void eraPvu(double dt, double pv[2][3], double upv[2][3]); +void eraPvup(double dt, double pv[2][3], double p[3]); +void eraPvxpv(double a[2][3], double b[2][3], double axb[2][3]); +void eraPxp(double a[3], double b[3], double axb[3]); +void eraS2xpv(double s1, double s2, double pv[2][3], double spv[2][3]); +void eraSxp(double s, double p[3], double sp[3]); +void eraSxpv(double s, double pv[2][3], double spv[2][3]); + +#ifdef __cplusplus +} +#endif + +#endif + + +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/erfam.h b/erfa/erfam.h new file mode 100644 index 0000000..f6d8b71 --- /dev/null +++ b/erfa/erfam.h @@ -0,0 +1,208 @@ +#ifndef ERFAMHDEF +#define ERFAMHDEF + +/* +** - - - - - - - - +** e r f a m . h +** - - - - - - - - +** +** Macros used by ERFA library. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ + +/* Star-independent astrometry parameters */ +typedef struct { + double pmt; /* PM time interval (SSB, Julian years) */ + double eb[3]; /* SSB to observer (vector, au) */ + double eh[3]; /* Sun to observer (unit vector) */ + double em; /* distance from Sun to observer (au) */ + double v[3]; /* barycentric observer velocity (vector, c) */ + double bm1; /* sqrt(1-|v|^2): reciprocal of Lorenz factor */ + double bpn[3][3]; /* bias-precession-nutation matrix */ + double along; /* longitude + s' + dERA(DUT) (radians) */ + double phi; /* geodetic latitude (radians) */ + double xpl; /* polar motion xp wrt local meridian (radians) */ + double ypl; /* polar motion yp wrt local meridian (radians) */ + double sphi; /* sine of geodetic latitude */ + double cphi; /* cosine of geodetic latitude */ + double diurab; /* magnitude of diurnal aberration vector */ + double eral; /* "local" Earth rotation angle (radians) */ + double refa; /* refraction constant A (radians) */ + double refb; /* refraction constant B (radians) */ +} eraASTROM; +/* (Vectors eb, eh, em and v are all with respect to BCRS axes.) */ + +/* Body parameters for light deflection */ +typedef struct { + double bm; /* mass of the body (solar masses) */ + double dl; /* deflection limiter (radians^2/2) */ + double pv[2][3]; /* barycentric PV of the body (au, au/day) */ +} eraLDBODY; + +/* Pi */ +#define ERFA_DPI (3.141592653589793238462643) + +/* 2Pi */ +#define ERFA_D2PI (6.283185307179586476925287) + +/* Radians to degrees */ +#define ERFA_DR2D (57.29577951308232087679815) + +/* Degrees to radians */ +#define ERFA_DD2R (1.745329251994329576923691e-2) + +/* Radians to arcseconds */ +#define ERFA_DR2AS (206264.8062470963551564734) + +/* Arcseconds to radians */ +#define ERFA_DAS2R (4.848136811095359935899141e-6) + +/* Seconds of time to radians */ +#define ERFA_DS2R (7.272205216643039903848712e-5) + +/* Arcseconds in a full circle */ +#define ERFA_TURNAS (1296000.0) + +/* Milliarcseconds to radians */ +#define ERFA_DMAS2R (ERFA_DAS2R / 1e3) + +/* Length of tropical year B1900 (days) */ +#define ERFA_DTY (365.242198781) + +/* Seconds per day. */ +#define ERFA_DAYSEC (86400.0) + +/* Days per Julian year */ +#define ERFA_DJY (365.25) + +/* Days per Julian century */ +#define ERFA_DJC (36525.0) + +/* Days per Julian millennium */ +#define ERFA_DJM (365250.0) + +/* Reference epoch (J2000.0), Julian Date */ +#define ERFA_DJ00 (2451545.0) + +/* Julian Date of Modified Julian Date zero */ +#define ERFA_DJM0 (2400000.5) + +/* Reference epoch (J2000.0), Modified Julian Date */ +#define ERFA_DJM00 (51544.5) + +/* 1977 Jan 1.0 as MJD */ +#define ERFA_DJM77 (43144.0) + +/* TT minus TAI (s) */ +#define ERFA_TTMTAI (32.184) + +/* Astronomical unit (m) */ +#define ERFA_DAU (149597870e3) + +/* Speed of light (m/s) */ +#define ERFA_CMPS 299792458.0 + +/* Light time for 1 au (s) */ +#define ERFA_AULT 499.004782 + +/* Speed of light (AU per day) */ +#define ERFA_DC (ERFA_DAYSEC / ERFA_AULT) + +/* L_G = 1 - d(TT)/d(TCG) */ +#define ERFA_ELG (6.969290134e-10) + +/* L_B = 1 - d(TDB)/d(TCB), and TDB (s) at TAI 1977/1/1.0 */ +#define ERFA_ELB (1.550519768e-8) +#define ERFA_TDB0 (-6.55e-5) + +/* Schwarzschild radius of the Sun (au) */ +/* = 2 * 1.32712440041e20 / (2.99792458e8)^2 / 1.49597870700e11 */ +#define ERFA_SRS 1.97412574336e-8 + +/* ERFA_DINT(A) - truncate to nearest whole number towards zero (double) */ +#define ERFA_DINT(A) ((A)<0.0?ceil(A):floor(A)) + +/* ERFA_DNINT(A) - round to nearest whole number (double) */ +#define ERFA_DNINT(A) ((A)<0.0?ceil((A)-0.5):floor((A)+0.5)) + +/* ERFA_DSIGN(A,B) - magnitude of A with sign of B (double) */ +#define ERFA_DSIGN(A,B) ((B)<0.0?-fabs(A):fabs(A)) + +/* max(A,B) - larger (most +ve) of two numbers (generic) */ +#define ERFA_GMAX(A,B) (((A)>(B))?(A):(B)) + +/* min(A,B) - smaller (least +ve) of two numbers (generic) */ +#define ERFA_GMIN(A,B) (((A)<(B))?(A):(B)) + +/* Reference ellipsoids */ +#define ERFA_WGS84 1 +#define ERFA_GRS80 2 +#define ERFA_WGS72 3 + +#endif + + +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fad03.c b/erfa/fad03.c new file mode 100644 index 0000000..5f10c0f --- /dev/null +++ b/erfa/fad03.c @@ -0,0 +1,112 @@ +#include "erfa.h" + +double eraFad03(double t) +/* +** - - - - - - - - - +** e r a F a d 0 3 +** - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean elongation of the Moon from the Sun. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double D, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** is from Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean elongation of the Moon from the Sun (IERS Conventions 2003). */ + a = fmod( 1072260.703692 + + t * ( 1602961601.2090 + + t * ( - 6.3706 + + t * ( 0.006593 + + t * ( - 0.00003169 ) ) ) ), ERFA_TURNAS ) * ERFA_DAS2R; + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fae03.c b/erfa/fae03.c new file mode 100644 index 0000000..c332ee4 --- /dev/null +++ b/erfa/fae03.c @@ -0,0 +1,111 @@ +#include "erfa.h" + +double eraFae03(double t) +/* +** - - - - - - - - - +** e r a F a e 0 3 +** - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Earth. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Earth, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** comes from Souchay et al. (1999) after Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Earth (IERS Conventions 2003). */ + a = fmod(1.753470314 + 628.3075849991 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/faf03.c b/erfa/faf03.c new file mode 100644 index 0000000..74e8e6b --- /dev/null +++ b/erfa/faf03.c @@ -0,0 +1,115 @@ +#include "erfa.h" + +double eraFaf03(double t) +/* +** - - - - - - - - - +** e r a F a f 0 3 +** - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of the Moon minus mean longitude of the ascending +** node. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double F, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** is from Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of the Moon minus that of the ascending node */ +/* (IERS Conventions 2003). */ + a = fmod( 335779.526232 + + t * ( 1739527262.8478 + + t * ( - 12.7512 + + t * ( - 0.001037 + + t * ( 0.00000417 ) ) ) ), ERFA_TURNAS ) * ERFA_DAS2R; + + return a; + + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/faju03.c b/erfa/faju03.c new file mode 100644 index 0000000..28988b7 --- /dev/null +++ b/erfa/faju03.c @@ -0,0 +1,111 @@ +#include "erfa.h" + +double eraFaju03(double t) +/* +** - - - - - - - - - - +** e r a F a j u 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Jupiter. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Jupiter, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** comes from Souchay et al. (1999) after Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Jupiter (IERS Conventions 2003). */ + a = fmod(0.599546497 + 52.9690962641 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fal03.c b/erfa/fal03.c new file mode 100644 index 0000000..648f733 --- /dev/null +++ b/erfa/fal03.c @@ -0,0 +1,112 @@ +#include "erfa.h" + +double eraFal03(double t) +/* +** - - - - - - - - - +** e r a F a l 0 3 +** - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean anomaly of the Moon. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double l, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** is from Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean anomaly of the Moon (IERS Conventions 2003). */ + a = fmod( 485868.249036 + + t * ( 1717915923.2178 + + t * ( 31.8792 + + t * ( 0.051635 + + t * ( - 0.00024470 ) ) ) ), ERFA_TURNAS ) * ERFA_DAS2R; + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/falp03.c b/erfa/falp03.c new file mode 100644 index 0000000..c299837 --- /dev/null +++ b/erfa/falp03.c @@ -0,0 +1,112 @@ +#include "erfa.h" + +double eraFalp03(double t) +/* +** - - - - - - - - - - +** e r a F a l p 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean anomaly of the Sun. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double l', radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** is from Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean anomaly of the Sun (IERS Conventions 2003). */ + a = fmod( 1287104.793048 + + t * ( 129596581.0481 + + t * ( - 0.5532 + + t * ( 0.000136 + + t * ( - 0.00001149 ) ) ) ), ERFA_TURNAS ) * ERFA_DAS2R; + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fama03.c b/erfa/fama03.c new file mode 100644 index 0000000..770c82b --- /dev/null +++ b/erfa/fama03.c @@ -0,0 +1,111 @@ +#include "erfa.h" + +double eraFama03(double t) +/* +** - - - - - - - - - - +** e r a F a m a 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Mars. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Mars, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** comes from Souchay et al. (1999) after Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Mars (IERS Conventions 2003). */ + a = fmod(6.203480913 + 334.0612426700 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fame03.c b/erfa/fame03.c new file mode 100644 index 0000000..74caff8 --- /dev/null +++ b/erfa/fame03.c @@ -0,0 +1,111 @@ +#include "erfa.h" + +double eraFame03(double t) +/* +** - - - - - - - - - - +** e r a F a m e 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Mercury. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Mercury, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** comes from Souchay et al. (1999) after Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Mercury (IERS Conventions 2003). */ + a = fmod(4.402608842 + 2608.7903141574 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fane03.c b/erfa/fane03.c new file mode 100644 index 0000000..66b13e6 --- /dev/null +++ b/erfa/fane03.c @@ -0,0 +1,108 @@ +#include "erfa.h" + +double eraFane03(double t) +/* +** - - - - - - - - - - +** e r a F a n e 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Neptune. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Neptune, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** is adapted from Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Neptune (IERS Conventions 2003). */ + a = fmod(5.311886287 + 3.8133035638 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/faom03.c b/erfa/faom03.c new file mode 100644 index 0000000..b349466 --- /dev/null +++ b/erfa/faom03.c @@ -0,0 +1,113 @@ +#include "erfa.h" + +double eraFaom03(double t) +/* +** - - - - - - - - - - +** e r a F a o m 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of the Moon's ascending node. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double Omega, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** is from Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of the Moon's ascending node */ +/* (IERS Conventions 2003). */ + a = fmod( 450160.398036 + + t * ( - 6962890.5431 + + t * ( 7.4722 + + t * ( 0.007702 + + t * ( - 0.00005939 ) ) ) ), ERFA_TURNAS ) * ERFA_DAS2R; + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fapa03.c b/erfa/fapa03.c new file mode 100644 index 0000000..3db1ac8 --- /dev/null +++ b/erfa/fapa03.c @@ -0,0 +1,112 @@ +#include "erfa.h" + +double eraFapa03(double t) +/* +** - - - - - - - - - - +** e r a F a p a 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** general accumulated precession in longitude. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double general precession in longitude, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003). It +** is taken from Kinoshita & Souchay (1990) and comes originally +** from Lieske et al. (1977). +** +** References: +** +** Kinoshita, H. and Souchay J. 1990, Celest.Mech. and Dyn.Astron. +** 48, 187 +** +** Lieske, J.H., Lederle, T., Fricke, W. & Morando, B. 1977, +** Astron.Astrophys. 58, 1-16 +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* General accumulated precession in longitude. */ + a = (0.024381750 + 0.00000538691 * t) * t; + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fasa03.c b/erfa/fasa03.c new file mode 100644 index 0000000..1f19e2b --- /dev/null +++ b/erfa/fasa03.c @@ -0,0 +1,111 @@ +#include "erfa.h" + +double eraFasa03(double t) +/* +** - - - - - - - - - - +** e r a F a s a 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Saturn. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Saturn, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** comes from Souchay et al. (1999) after Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Saturn (IERS Conventions 2003). */ + a = fmod(0.874016757 + 21.3299104960 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/faur03.c b/erfa/faur03.c new file mode 100644 index 0000000..f15a9b8 --- /dev/null +++ b/erfa/faur03.c @@ -0,0 +1,108 @@ +#include "erfa.h" + +double eraFaur03(double t) +/* +** - - - - - - - - - - +** e r a F a u r 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Uranus. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Uranus, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** is adapted from Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Uranus (IERS Conventions 2003). */ + a = fmod(5.481293872 + 7.4781598567 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fave03.c b/erfa/fave03.c new file mode 100644 index 0000000..0f60dd3 --- /dev/null +++ b/erfa/fave03.c @@ -0,0 +1,111 @@ +#include "erfa.h" + +double eraFave03(double t) +/* +** - - - - - - - - - - +** e r a F a v e 0 3 +** - - - - - - - - - - +** +** Fundamental argument, IERS Conventions (2003): +** mean longitude of Venus. +** +** Given: +** t double TDB, Julian centuries since J2000.0 (Note 1) +** +** Returned (function value): +** double mean longitude of Venus, radians (Note 2) +** +** Notes: +** +** 1) Though t is strictly TDB, it is usually more convenient to use +** TT, which makes no significant difference. +** +** 2) The expression used is as adopted in IERS Conventions (2003) and +** comes from Souchay et al. (1999) after Simon et al. (1994). +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double a; + + +/* Mean longitude of Venus (IERS Conventions 2003). */ + a = fmod(3.176146697 + 1021.3285546211 * t, ERFA_D2PI); + + return a; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fk52h.c b/erfa/fk52h.c new file mode 100644 index 0000000..59bdaa1 --- /dev/null +++ b/erfa/fk52h.c @@ -0,0 +1,152 @@ +#include "erfa.h" + +void eraFk52h(double r5, double d5, + double dr5, double dd5, double px5, double rv5, + double *rh, double *dh, + double *drh, double *ddh, double *pxh, double *rvh) +/* +** - - - - - - - - - +** e r a F k 5 2 h +** - - - - - - - - - +** +** Transform FK5 (J2000.0) star data into the Hipparcos system. +** +** Given (all FK5, equinox J2000.0, epoch J2000.0): +** r5 double RA (radians) +** d5 double Dec (radians) +** dr5 double proper motion in RA (dRA/dt, rad/Jyear) +** dd5 double proper motion in Dec (dDec/dt, rad/Jyear) +** px5 double parallax (arcsec) +** rv5 double radial velocity (km/s, positive = receding) +** +** Returned (all Hipparcos, epoch J2000.0): +** rh double RA (radians) +** dh double Dec (radians) +** drh double proper motion in RA (dRA/dt, rad/Jyear) +** ddh double proper motion in Dec (dDec/dt, rad/Jyear) +** pxh double parallax (arcsec) +** rvh double radial velocity (km/s, positive = receding) +** +** Notes: +** +** 1) This function transforms FK5 star positions and proper motions +** into the system of the Hipparcos catalog. +** +** 2) The proper motions in RA are dRA/dt rather than +** cos(Dec)*dRA/dt, and are per year rather than per century. +** +** 3) The FK5 to Hipparcos transformation is modeled as a pure +** rotation and spin; zonal errors in the FK5 catalog are not +** taken into account. +** +** 4) See also eraH2fk5, eraFk5hz, eraHfk5z. +** +** Called: +** eraStarpv star catalog data to space motion pv-vector +** eraFk5hip FK5 to Hipparcos rotation and spin +** eraRxp product of r-matrix and p-vector +** eraPxp vector product of two p-vectors +** eraPpp p-vector plus p-vector +** eraPvstar space motion pv-vector to star catalog data +** +** Reference: +** +** F.Mignard & M.Froeschle, Astron. Astrophys. 354, 732-739 (2000). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int i; + double pv5[2][3], r5h[3][3], s5h[3], wxp[3], vv[3], pvh[2][3]; + + +/* FK5 barycentric position/velocity pv-vector (normalized). */ + eraStarpv(r5, d5, dr5, dd5, px5, rv5, pv5); + +/* FK5 to Hipparcos orientation matrix and spin vector. */ + eraFk5hip(r5h, s5h); + +/* Make spin units per day instead of per year. */ + for ( i = 0; i < 3; s5h[i++] /= 365.25 ); + +/* Orient the FK5 position into the Hipparcos system. */ + eraRxp(r5h, pv5[0], pvh[0]); + +/* Apply spin to the position giving an extra space motion component. */ + eraPxp(pv5[0], s5h, wxp); + +/* Add this component to the FK5 space motion. */ + eraPpp(wxp, pv5[1], vv); + +/* Orient the FK5 space motion into the Hipparcos system. */ + eraRxp(r5h, vv, pvh[1]); + +/* Hipparcos pv-vector to spherical. */ + eraPvstar(pvh, rh, dh, drh, ddh, pxh, rvh); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fk5hip.c b/erfa/fk5hip.c new file mode 100644 index 0000000..77299f3 --- /dev/null +++ b/erfa/fk5hip.c @@ -0,0 +1,135 @@ +#include "erfa.h" + +void eraFk5hip(double r5h[3][3], double s5h[3]) +/* +** - - - - - - - - - - +** e r a F k 5 h i p +** - - - - - - - - - - +** +** FK5 to Hipparcos rotation and spin. +** +** Returned: +** r5h double[3][3] r-matrix: FK5 rotation wrt Hipparcos (Note 2) +** s5h double[3] r-vector: FK5 spin wrt Hipparcos (Note 3) +** +** Notes: +** +** 1) This function models the FK5 to Hipparcos transformation as a +** pure rotation and spin; zonal errors in the FK5 catalogue are +** not taken into account. +** +** 2) The r-matrix r5h operates in the sense: +** +** P_Hipparcos = r5h x P_FK5 +** +** where P_FK5 is a p-vector in the FK5 frame, and P_Hipparcos is +** the equivalent Hipparcos p-vector. +** +** 3) The r-vector s5h represents the time derivative of the FK5 to +** Hipparcos rotation. The units are radians per year (Julian, +** TDB). +** +** Called: +** eraRv2m r-vector to r-matrix +** +** Reference: +** +** F.Mignard & M.Froeschle, Astron. Astrophys. 354, 732-739 (2000). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double v[3]; + +/* FK5 wrt Hipparcos orientation and spin (radians, radians/year) */ + double epx, epy, epz; + double omx, omy, omz; + + + epx = -19.9e-3 * ERFA_DAS2R; + epy = -9.1e-3 * ERFA_DAS2R; + epz = 22.9e-3 * ERFA_DAS2R; + + omx = -0.30e-3 * ERFA_DAS2R; + omy = 0.60e-3 * ERFA_DAS2R; + omz = 0.70e-3 * ERFA_DAS2R; + +/* FK5 to Hipparcos orientation expressed as an r-vector. */ + v[0] = epx; + v[1] = epy; + v[2] = epz; + +/* Re-express as an r-matrix. */ + eraRv2m(v, r5h); + +/* Hipparcos wrt FK5 spin expressed as an r-vector. */ + s5h[0] = omx; + s5h[1] = omy; + s5h[2] = omz; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fk5hz.c b/erfa/fk5hz.c new file mode 100644 index 0000000..7481783 --- /dev/null +++ b/erfa/fk5hz.c @@ -0,0 +1,169 @@ +#include "erfa.h" + +void eraFk5hz(double r5, double d5, double date1, double date2, + double *rh, double *dh) +/* +** - - - - - - - - - +** e r a F k 5 h z +** - - - - - - - - - +** +** Transform an FK5 (J2000.0) star position into the system of the +** Hipparcos catalogue, assuming zero Hipparcos proper motion. +** +** Given: +** r5 double FK5 RA (radians), equinox J2000.0, at date +** d5 double FK5 Dec (radians), equinox J2000.0, at date +** date1,date2 double TDB date (Notes 1,2) +** +** Returned: +** rh double Hipparcos RA (radians) +** dh double Hipparcos Dec (radians) +** +** Notes: +** +** 1) This function converts a star position from the FK5 system to +** the Hipparcos system, in such a way that the Hipparcos proper +** motion is zero. Because such a star has, in general, a non-zero +** proper motion in the FK5 system, the function requires the date +** at which the position in the FK5 system was determined. +** +** 2) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 3) The FK5 to Hipparcos transformation is modeled as a pure +** rotation and spin; zonal errors in the FK5 catalogue are not +** taken into account. +** +** 4) The position returned by this function is in the Hipparcos +** reference system but at date date1+date2. +** +** 5) See also eraFk52h, eraH2fk5, eraHfk5z. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraFk5hip FK5 to Hipparcos rotation and spin +** eraSxp multiply p-vector by scalar +** eraRv2m r-vector to r-matrix +** eraTrxp product of transpose of r-matrix and p-vector +** eraPxp vector product of two p-vectors +** eraC2s p-vector to spherical +** eraAnp normalize angle into range 0 to 2pi +** +** Reference: +** +** F.Mignard & M.Froeschle, 2000, Astron.Astrophys. 354, 732-739. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, p5e[3], r5h[3][3], s5h[3], vst[3], rst[3][3], p5[3], + ph[3], w; + + +/* Interval from given date to fundamental epoch J2000.0 (JY). */ + t = - ((date1 - ERFA_DJ00) + date2) / ERFA_DJY; + +/* FK5 barycentric position vector. */ + eraS2c(r5, d5, p5e); + +/* FK5 to Hipparcos orientation matrix and spin vector. */ + eraFk5hip(r5h, s5h); + +/* Accumulated Hipparcos wrt FK5 spin over that interval. */ + eraSxp(t, s5h, vst); + +/* Express the accumulated spin as a rotation matrix. */ + eraRv2m(vst, rst); + +/* Derotate the vector's FK5 axes back to date. */ + eraTrxp(rst, p5e, p5); + +/* Rotate the vector into the Hipparcos system. */ + eraRxp(r5h, p5, ph); + +/* Hipparcos vector to spherical. */ + eraC2s(ph, &w, dh); + *rh = eraAnp(w); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fw2m.c b/erfa/fw2m.c new file mode 100644 index 0000000..a66bf22 --- /dev/null +++ b/erfa/fw2m.c @@ -0,0 +1,143 @@ +#include "erfa.h" + +void eraFw2m(double gamb, double phib, double psi, double eps, + double r[3][3]) +/* +** - - - - - - - - +** e r a F w 2 m +** - - - - - - - - +** +** Form rotation matrix given the Fukushima-Williams angles. +** +** Given: +** gamb double F-W angle gamma_bar (radians) +** phib double F-W angle phi_bar (radians) +** psi double F-W angle psi (radians) +** eps double F-W angle epsilon (radians) +** +** Returned: +** r double[3][3] rotation matrix +** +** Notes: +** +** 1) Naming the following points: +** +** e = J2000.0 ecliptic pole, +** p = GCRS pole, +** E = ecliptic pole of date, +** and P = CIP, +** +** the four Fukushima-Williams angles are as follows: +** +** gamb = gamma = epE +** phib = phi = pE +** psi = psi = pEP +** eps = epsilon = EP +** +** 2) The matrix representing the combined effects of frame bias, +** precession and nutation is: +** +** NxPxB = R_1(-eps).R_3(-psi).R_1(phib).R_3(gamb) +** +** 3) Three different matrices can be constructed, depending on the +** supplied angles: +** +** o To obtain the nutation x precession x frame bias matrix, +** generate the four precession angles, generate the nutation +** components and add them to the psi_bar and epsilon_A angles, +** and call the present function. +** +** o To obtain the precession x frame bias matrix, generate the +** four precession angles and call the present function. +** +** o To obtain the frame bias matrix, generate the four precession +** angles for date J2000.0 and call the present function. +** +** The nutation-only and precession-only matrices can if necessary +** be obtained by combining these three appropriately. +** +** Called: +** eraIr initialize r-matrix to identity +** eraRz rotate around Z-axis +** eraRx rotate around X-axis +** +** Reference: +** +** Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Construct the matrix. */ + eraIr(r); + eraRz(gamb, r); + eraRx(phib, r); + eraRz(-psi, r); + eraRx(-eps, r); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/fw2xy.c b/erfa/fw2xy.c new file mode 100644 index 0000000..0ef2a74 --- /dev/null +++ b/erfa/fw2xy.c @@ -0,0 +1,130 @@ +#include "erfa.h" + +void eraFw2xy(double gamb, double phib, double psi, double eps, + double *x, double *y) +/* +** - - - - - - - - - +** e r a F w 2 x y +** - - - - - - - - - +** +** CIP X,Y given Fukushima-Williams bias-precession-nutation angles. +** +** Given: +** gamb double F-W angle gamma_bar (radians) +** phib double F-W angle phi_bar (radians) +** psi double F-W angle psi (radians) +** eps double F-W angle epsilon (radians) +** +** Returned: +** x,y double CIP unit vector X,Y +** +** Notes: +** +** 1) Naming the following points: +** +** e = J2000.0 ecliptic pole, +** p = GCRS pole +** E = ecliptic pole of date, +** and P = CIP, +** +** the four Fukushima-Williams angles are as follows: +** +** gamb = gamma = epE +** phib = phi = pE +** psi = psi = pEP +** eps = epsilon = EP +** +** 2) The matrix representing the combined effects of frame bias, +** precession and nutation is: +** +** NxPxB = R_1(-epsA).R_3(-psi).R_1(phib).R_3(gamb) +** +** The returned values x,y are elements [2][0] and [2][1] of the +** matrix. Near J2000.0, they are essentially angles in radians. +** +** Called: +** eraFw2m F-W angles to r-matrix +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** +** Reference: +** +** Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double r[3][3]; + + +/* Form NxPxB matrix. */ + eraFw2m(gamb, phib, psi, eps, r); + +/* Extract CIP X,Y. */ + eraBpn2xy(r, x, y); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gc2gd.c b/erfa/gc2gd.c new file mode 100644 index 0000000..ff5cc5d --- /dev/null +++ b/erfa/gc2gd.c @@ -0,0 +1,143 @@ +#include "erfa.h" + +int eraGc2gd ( int n, double xyz[3], + double *elong, double *phi, double *height ) +/* +** - - - - - - - - - +** e r a G c 2 g d +** - - - - - - - - - +** +** Transform geocentric coordinates to geodetic using the specified +** reference ellipsoid. +** +** Given: +** n int ellipsoid identifier (Note 1) +** xyz double[3] geocentric vector (Note 2) +** +** Returned: +** elong double longitude (radians, east +ve, Note 3) +** phi double latitude (geodetic, radians, Note 3) +** height double height above ellipsoid (geodetic, Notes 2,3) +** +** Returned (function value): +** int status: 0 = OK +** -1 = illegal identifier (Note 3) +** -2 = internal error (Note 3) +** +** Notes: +** +** 1) The identifier n is a number that specifies the choice of +** reference ellipsoid. The following are supported: +** +** n ellipsoid +** +** 1 ERFA_WGS84 +** 2 ERFA_GRS80 +** 3 ERFA_WGS72 +** +** The n value has no significance outside the ERFA software. For +** convenience, symbols ERFA_WGS84 etc. are defined in erfam.h. +** +** 2) The geocentric vector (xyz, given) and height (height, returned) +** are in meters. +** +** 3) An error status -1 means that the identifier n is illegal. An +** error status -2 is theoretically impossible. In all error cases, +** all three results are set to -1e9. +** +** 4) The inverse transformation is performed in the function eraGd2gc. +** +** Called: +** eraEform Earth reference ellipsoids +** eraGc2gde geocentric to geodetic transformation, general +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + double a, f; + + +/* Obtain reference ellipsoid parameters. */ + j = eraEform ( n, &a, &f ); + +/* If OK, transform x,y,z to longitude, geodetic latitude, height. */ + if ( j == 0 ) { + j = eraGc2gde ( a, f, xyz, elong, phi, height ); + if ( j < 0 ) j = -2; + } + +/* Deal with any errors. */ + if ( j < 0 ) { + *elong = -1e9; + *phi = -1e9; + *height = -1e9; + } + +/* Return the status. */ + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gc2gde.c b/erfa/gc2gde.c new file mode 100644 index 0000000..6afa064 --- /dev/null +++ b/erfa/gc2gde.c @@ -0,0 +1,208 @@ +#include "erfa.h" + +int eraGc2gde ( double a, double f, double xyz[3], + double *elong, double *phi, double *height ) +/* +** - - - - - - - - - - +** e r a G c 2 g d e +** - - - - - - - - - - +** +** Transform geocentric coordinates to geodetic for a reference +** ellipsoid of specified form. +** +** Given: +** a double equatorial radius (Notes 2,4) +** f double flattening (Note 3) +** xyz double[3] geocentric vector (Note 4) +** +** Returned: +** elong double longitude (radians, east +ve) +** phi double latitude (geodetic, radians) +** height double height above ellipsoid (geodetic, Note 4) +** +** Returned (function value): +** int status: 0 = OK +** -1 = illegal f +** -2 = illegal a +** +** Notes: +** +** 1) This function is based on the GCONV2H Fortran subroutine by +** Toshio Fukushima (see reference). +** +** 2) The equatorial radius, a, can be in any units, but meters is +** the conventional choice. +** +** 3) The flattening, f, is (for the Earth) a value around 0.00335, +** i.e. around 1/298. +** +** 4) The equatorial radius, a, and the geocentric vector, xyz, +** must be given in the same units, and determine the units of +** the returned height, height. +** +** 5) If an error occurs (status < 0), elong, phi and height are +** unchanged. +** +** 6) The inverse transformation is performed in the function +** eraGd2gce. +** +** 7) The transformation for a standard ellipsoid (such as ERFA_WGS84) can +** more conveniently be performed by calling eraGc2gd, which uses a +** numerical code to identify the required A and F values. +** +** Reference: +** +** Fukushima, T., "Transformation from Cartesian to geodetic +** coordinates accelerated by Halley's method", J.Geodesy (2006) +** 79: 689-693 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double aeps2, e2, e4t, ec2, ec, b, x, y, z, p2, absz, p, s0, pn, zc, + c0, c02, c03, s02, s03, a02, a0, a03, d0, f0, b0, s1, + cc, s12, cc2; + + +/* ------------- */ +/* Preliminaries */ +/* ------------- */ + +/* Validate ellipsoid parameters. */ + if ( f < 0.0 || f >= 1.0 ) return -1; + if ( a <= 0.0 ) return -2; + +/* Functions of ellipsoid parameters (with further validation of f). */ + aeps2 = a*a * 1e-32; + e2 = (2.0 - f) * f; + e4t = e2*e2 * 1.5; + ec2 = 1.0 - e2; + if ( ec2 <= 0.0 ) return -1; + ec = sqrt(ec2); + b = a * ec; + +/* Cartesian components. */ + x = xyz[0]; + y = xyz[1]; + z = xyz[2]; + +/* Distance from polar axis squared. */ + p2 = x*x + y*y; + +/* Longitude. */ + *elong = p2 != 0.0 ? atan2(y, x) : 0.0; + +/* Unsigned z-coordinate. */ + absz = fabs(z); + +/* Proceed unless polar case. */ + if ( p2 > aeps2 ) { + + /* Distance from polar axis. */ + p = sqrt(p2); + + /* Normalization. */ + s0 = absz / a; + pn = p / a; + zc = ec * s0; + + /* Prepare Newton correction factors. */ + c0 = ec * pn; + c02 = c0 * c0; + c03 = c02 * c0; + s02 = s0 * s0; + s03 = s02 * s0; + a02 = c02 + s02; + a0 = sqrt(a02); + a03 = a02 * a0; + d0 = zc*a03 + e2*s03; + f0 = pn*a03 - e2*c03; + + /* Prepare Halley correction factor. */ + b0 = e4t * s02 * c02 * pn * (a0 - ec); + s1 = d0*f0 - b0*s0; + cc = ec * (f0*f0 - b0*c0); + + /* Evaluate latitude and height. */ + *phi = atan(s1/cc); + s12 = s1 * s1; + cc2 = cc * cc; + *height = (p*cc + absz*s1 - a * sqrt(ec2*s12 + cc2)) / + sqrt(s12 + cc2); + } else { + + /* Exception: pole. */ + *phi = ERFA_DPI / 2.0; + *height = absz - b; + } + +/* Restore sign of latitude. */ + if ( z < 0 ) *phi = -*phi; + +/* OK status. */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gd2gc.c b/erfa/gd2gc.c new file mode 100644 index 0000000..d6bc399 --- /dev/null +++ b/erfa/gd2gc.c @@ -0,0 +1,142 @@ +#include "erfa.h" + +int eraGd2gc ( int n, double elong, double phi, double height, + double xyz[3] ) +/* +** - - - - - - - - - +** e r a G d 2 g c +** - - - - - - - - - +** +** Transform geodetic coordinates to geocentric using the specified +** reference ellipsoid. +** +** Given: +** n int ellipsoid identifier (Note 1) +** elong double longitude (radians, east +ve) +** phi double latitude (geodetic, radians, Note 3) +** height double height above ellipsoid (geodetic, Notes 2,3) +** +** Returned: +** xyz double[3] geocentric vector (Note 2) +** +** Returned (function value): +** int status: 0 = OK +** -1 = illegal identifier (Note 3) +** -2 = illegal case (Note 3) +** +** Notes: +** +** 1) The identifier n is a number that specifies the choice of +** reference ellipsoid. The following are supported: +** +** n ellipsoid +** +** 1 ERFA_WGS84 +** 2 ERFA_GRS80 +** 3 ERFA_WGS72 +** +** The n value has no significance outside the ERFA software. For +** convenience, symbols ERFA_WGS84 etc. are defined in erfam.h. +** +** 2) The height (height, given) and the geocentric vector (xyz, +** returned) are in meters. +** +** 3) No validation is performed on the arguments elong, phi and +** height. An error status -1 means that the identifier n is +** illegal. An error status -2 protects against cases that would +** lead to arithmetic exceptions. In all error cases, xyz is set +** to zeros. +** +** 4) The inverse transformation is performed in the function eraGc2gd. +** +** Called: +** eraEform Earth reference ellipsoids +** eraGd2gce geodetic to geocentric transformation, general +** eraZp zero p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j; + double a, f; + + +/* Obtain reference ellipsoid parameters. */ + j = eraEform ( n, &a, &f ); + +/* If OK, transform longitude, geodetic latitude, height to x,y,z. */ + if ( j == 0 ) { + j = eraGd2gce ( a, f, elong, phi, height, xyz ); + if ( j != 0 ) j = -2; + } + +/* Deal with any errors. */ + if ( j != 0 ) eraZp ( xyz ); + +/* Return the status. */ + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gd2gce.c b/erfa/gd2gce.c new file mode 100644 index 0000000..5aae512 --- /dev/null +++ b/erfa/gd2gce.c @@ -0,0 +1,146 @@ +#include "erfa.h" + +int eraGd2gce ( double a, double f, double elong, double phi, + double height, double xyz[3] ) +/* +** - - - - - - - - - - +** e r a G d 2 g c e +** - - - - - - - - - - +** +** Transform geodetic coordinates to geocentric for a reference +** ellipsoid of specified form. +** +** Given: +** a double equatorial radius (Notes 1,4) +** f double flattening (Notes 2,4) +** elong double longitude (radians, east +ve) +** phi double latitude (geodetic, radians, Note 4) +** height double height above ellipsoid (geodetic, Notes 3,4) +** +** Returned: +** xyz double[3] geocentric vector (Note 3) +** +** Returned (function value): +** int status: 0 = OK +** -1 = illegal case (Note 4) +** Notes: +** +** 1) The equatorial radius, a, can be in any units, but meters is +** the conventional choice. +** +** 2) The flattening, f, is (for the Earth) a value around 0.00335, +** i.e. around 1/298. +** +** 3) The equatorial radius, a, and the height, height, must be +** given in the same units, and determine the units of the +** returned geocentric vector, xyz. +** +** 4) No validation is performed on individual arguments. The error +** status -1 protects against (unrealistic) cases that would lead +** to arithmetic exceptions. If an error occurs, xyz is unchanged. +** +** 5) The inverse transformation is performed in the function +** eraGc2gde. +** +** 6) The transformation for a standard ellipsoid (such as ERFA_WGS84) can +** more conveniently be performed by calling eraGd2gc, which uses a +** numerical code to identify the required a and f values. +** +** References: +** +** Green, R.M., Spherical Astronomy, Cambridge University Press, +** (1985) Section 4.5, p96. +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 4.22, p202. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double sp, cp, w, d, ac, as, r; + + +/* Functions of geodetic latitude. */ + sp = sin(phi); + cp = cos(phi); + w = 1.0 - f; + w = w * w; + d = cp*cp + w*sp*sp; + if ( d <= 0.0 ) return -1; + ac = a / sqrt(d); + as = w * ac; + +/* Geocentric vector. */ + r = (ac + height) * cp; + xyz[0] = r * cos(elong); + xyz[1] = r * sin(elong); + xyz[2] = (as + height) * sp; + +/* Success. */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gmst00.c b/erfa/gmst00.c new file mode 100644 index 0000000..8b3d51e --- /dev/null +++ b/erfa/gmst00.c @@ -0,0 +1,154 @@ +#include "erfa.h" + +double eraGmst00(double uta, double utb, double tta, double ttb) +/* +** - - - - - - - - - - +** e r a G m s t 0 0 +** - - - - - - - - - - +** +** Greenwich mean sidereal time (model consistent with IAU 2000 +** resolutions). +** +** Given: +** uta,utb double UT1 as a 2-part Julian Date (Notes 1,2) +** tta,ttb double TT as a 2-part Julian Date (Notes 1,2) +** +** Returned (function value): +** double Greenwich mean sidereal time (radians) +** +** Notes: +** +** 1) The UT1 and TT dates uta+utb and tta+ttb respectively, are both +** Julian Dates, apportioned in any convenient way between the +** argument pairs. For example, JD=2450123.7 could be expressed in +** any of these ways, among others: +** +** Part A Part B +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable (in the case of UT; the TT is not at all critical +** in this respect). The J2000 and MJD methods are good compromises +** between resolution and convenience. For UT, the date & time +** method is best matched to the algorithm that is used by the Earth +** Rotation Angle function, called internally: maximum precision is +** delivered when the uta argument is for 0hrs UT1 on the day in +** question and the utb argument lies in the range 0 to 1, or vice +** versa. +** +** 2) Both UT1 and TT are required, UT1 to predict the Earth rotation +** and TT to predict the effects of precession. If UT1 is used for +** both purposes, errors of order 100 microarcseconds result. +** +** 3) This GMST is compatible with the IAU 2000 resolutions and must be +** used only in conjunction with other IAU 2000 compatible +** components such as precession-nutation and equation of the +** equinoxes. +** +** 4) The result is returned in the range 0 to 2pi. +** +** 5) The algorithm is from Capitaine et al. (2003) and IERS +** Conventions 2003. +** +** Called: +** eraEra00 Earth rotation angle, IAU 2000 +** eraAnp normalize angle into range 0 to 2pi +** +** References: +** +** Capitaine, N., Wallace, P.T. and McCarthy, D.D., "Expressions to +** implement the IAU 2000 definition of UT1", Astronomy & +** Astrophysics, 406, 1135-1149 (2003) +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, gmst; + + +/* TT Julian centuries since J2000.0. */ + t = ((tta - ERFA_DJ00) + ttb) / ERFA_DJC; + +/* Greenwich Mean Sidereal Time, IAU 2000. */ + gmst = eraAnp(eraEra00(uta, utb) + + ( 0.014506 + + ( 4612.15739966 + + ( 1.39667721 + + ( -0.00009344 + + ( 0.00001882 ) + * t) * t) * t) * t) * ERFA_DAS2R); + + return gmst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gmst06.c b/erfa/gmst06.c new file mode 100644 index 0000000..a79045d --- /dev/null +++ b/erfa/gmst06.c @@ -0,0 +1,145 @@ +#include "erfa.h" + +double eraGmst06(double uta, double utb, double tta, double ttb) +/* +** - - - - - - - - - - +** e r a G m s t 0 6 +** - - - - - - - - - - +** +** Greenwich mean sidereal time (consistent with IAU 2006 precession). +** +** Given: +** uta,utb double UT1 as a 2-part Julian Date (Notes 1,2) +** tta,ttb double TT as a 2-part Julian Date (Notes 1,2) +** +** Returned (function value): +** double Greenwich mean sidereal time (radians) +** +** Notes: +** +** 1) The UT1 and TT dates uta+utb and tta+ttb respectively, are both +** Julian Dates, apportioned in any convenient way between the +** argument pairs. For example, JD=2450123.7 could be expressed in +** any of these ways, among others: +** +** Part A Part B +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable (in the case of UT; the TT is not at all critical +** in this respect). The J2000 and MJD methods are good compromises +** between resolution and convenience. For UT, the date & time +** method is best matched to the algorithm that is used by the Earth +** rotation angle function, called internally: maximum precision is +** delivered when the uta argument is for 0hrs UT1 on the day in +** question and the utb argument lies in the range 0 to 1, or vice +** versa. +** +** 2) Both UT1 and TT are required, UT1 to predict the Earth rotation +** and TT to predict the effects of precession. If UT1 is used for +** both purposes, errors of order 100 microarcseconds result. +** +** 3) This GMST is compatible with the IAU 2006 precession and must not +** be used with other precession models. +** +** 4) The result is returned in the range 0 to 2pi. +** +** Called: +** eraEra00 Earth rotation angle, IAU 2000 +** eraAnp normalize angle into range 0 to 2pi +** +** Reference: +** +** Capitaine, N., Wallace, P.T. & Chapront, J., 2005, +** Astron.Astrophys. 432, 355 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, gmst; + + +/* TT Julian centuries since J2000.0. */ + t = ((tta - ERFA_DJ00) + ttb) / ERFA_DJC; + +/* Greenwich mean sidereal time, IAU 2006. */ + gmst = eraAnp(eraEra00(uta, utb) + + ( 0.014506 + + ( 4612.156534 + + ( 1.3915817 + + ( -0.00000044 + + ( -0.000029956 + + ( -0.0000000368 ) + * t) * t) * t) * t) * t) * ERFA_DAS2R); + + return gmst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gmst82.c b/erfa/gmst82.c new file mode 100644 index 0000000..5ddf032 --- /dev/null +++ b/erfa/gmst82.c @@ -0,0 +1,160 @@ +#include "erfa.h" + +double eraGmst82(double dj1, double dj2) +/* +** - - - - - - - - - - +** e r a G m s t 8 2 +** - - - - - - - - - - +** +** Universal Time to Greenwich mean sidereal time (IAU 1982 model). +** +** Given: +** dj1,dj2 double UT1 Julian Date (see note) +** +** Returned (function value): +** double Greenwich mean sidereal time (radians) +** +** Notes: +** +** 1) The UT1 date dj1+dj2 is a Julian Date, apportioned in any +** convenient way between the arguments dj1 and dj2. For example, +** JD(UT1)=2450123.7 could be expressed in any of these ways, +** among others: +** +** dj1 dj2 +** +** 2450123.7 0 (JD method) +** 2451545 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. The date & time method is +** best matched to the algorithm used: maximum accuracy (or, at +** least, minimum noise) is delivered when the dj1 argument is for +** 0hrs UT1 on the day in question and the dj2 argument lies in the +** range 0 to 1, or vice versa. +** +** 2) The algorithm is based on the IAU 1982 expression. This is +** always described as giving the GMST at 0 hours UT1. In fact, it +** gives the difference between the GMST and the UT, the steady +** 4-minutes-per-day drawing-ahead of ST with respect to UT. When +** whole days are ignored, the expression happens to equal the GMST +** at 0 hours UT1 each day. +** +** 3) In this function, the entire UT1 (the sum of the two arguments +** dj1 and dj2) is used directly as the argument for the standard +** formula, the constant term of which is adjusted by 12 hours to +** take account of the noon phasing of Julian Date. The UT1 is then +** added, but omitting whole days to conserve accuracy. +** +** Called: +** eraAnp normalize angle into range 0 to 2pi +** +** References: +** +** Transactions of the International Astronomical Union, +** XVIII B, 67 (1983). +** +** Aoki et al., Astron. Astrophys. 105, 359-361 (1982). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Coefficients of IAU 1982 GMST-UT1 model */ + double A = 24110.54841 - ERFA_DAYSEC / 2.0; + double B = 8640184.812866; + double C = 0.093104; + double D = -6.2e-6; + +/* Note: the first constant, A, has to be adjusted by 12 hours */ +/* because the UT1 is supplied as a Julian date, which begins */ +/* at noon. */ + + double d1, d2, t, f, gmst; + + +/* Julian centuries since fundamental epoch. */ + if (dj1 < dj2) { + d1 = dj1; + d2 = dj2; + } else { + d1 = dj2; + d2 = dj1; + } + t = (d1 + (d2 - ERFA_DJ00)) / ERFA_DJC; + +/* Fractional part of JD(UT1), in seconds. */ + f = ERFA_DAYSEC * (fmod(d1, 1.0) + fmod(d2, 1.0)); + +/* GMST at this UT1. */ + gmst = eraAnp(ERFA_DS2R * ((A + (B + (C + D * t) * t) * t) + f)); + + return gmst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gst00a.c b/erfa/gst00a.c new file mode 100644 index 0000000..e54bb0b --- /dev/null +++ b/erfa/gst00a.c @@ -0,0 +1,147 @@ +#include "erfa.h" + +double eraGst00a(double uta, double utb, double tta, double ttb) +/* +** - - - - - - - - - - +** e r a G s t 0 0 a +** - - - - - - - - - - +** +** Greenwich apparent sidereal time (consistent with IAU 2000 +** resolutions). +** +** Given: +** uta,utb double UT1 as a 2-part Julian Date (Notes 1,2) +** tta,ttb double TT as a 2-part Julian Date (Notes 1,2) +** +** Returned (function value): +** double Greenwich apparent sidereal time (radians) +** +** Notes: +** +** 1) The UT1 and TT dates uta+utb and tta+ttb respectively, are both +** Julian Dates, apportioned in any convenient way between the +** argument pairs. For example, JD=2450123.7 could be expressed in +** any of these ways, among others: +** +** Part A Part B +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable (in the case of UT; the TT is not at all critical +** in this respect). The J2000 and MJD methods are good compromises +** between resolution and convenience. For UT, the date & time +** method is best matched to the algorithm that is used by the Earth +** Rotation Angle function, called internally: maximum precision is +** delivered when the uta argument is for 0hrs UT1 on the day in +** question and the utb argument lies in the range 0 to 1, or vice +** versa. +** +** 2) Both UT1 and TT are required, UT1 to predict the Earth rotation +** and TT to predict the effects of precession-nutation. If UT1 is +** used for both purposes, errors of order 100 microarcseconds +** result. +** +** 3) This GAST is compatible with the IAU 2000 resolutions and must be +** used only in conjunction with other IAU 2000 compatible +** components such as precession-nutation. +** +** 4) The result is returned in the range 0 to 2pi. +** +** 5) The algorithm is from Capitaine et al. (2003) and IERS +** Conventions 2003. +** +** Called: +** eraGmst00 Greenwich mean sidereal time, IAU 2000 +** eraEe00a equation of the equinoxes, IAU 2000A +** eraAnp normalize angle into range 0 to 2pi +** +** References: +** +** Capitaine, N., Wallace, P.T. and McCarthy, D.D., "Expressions to +** implement the IAU 2000 definition of UT1", Astronomy & +** Astrophysics, 406, 1135-1149 (2003) +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gmst00, ee00a, gst; + + + gmst00 = eraGmst00(uta, utb, tta, ttb); + ee00a = eraEe00a(tta, ttb); + gst = eraAnp(gmst00 + ee00a); + + return gst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gst00b.c b/erfa/gst00b.c new file mode 100644 index 0000000..d34ae67 --- /dev/null +++ b/erfa/gst00b.c @@ -0,0 +1,155 @@ +#include "erfa.h" + +double eraGst00b(double uta, double utb) +/* +** - - - - - - - - - - +** e r a G s t 0 0 b +** - - - - - - - - - - +** +** Greenwich apparent sidereal time (consistent with IAU 2000 +** resolutions but using the truncated nutation model IAU 2000B). +** +** Given: +** uta,utb double UT1 as a 2-part Julian Date (Notes 1,2) +** +** Returned (function value): +** double Greenwich apparent sidereal time (radians) +** +** Notes: +** +** 1) The UT1 date uta+utb is a Julian Date, apportioned in any +** convenient way between the argument pair. For example, +** JD=2450123.7 could be expressed in any of these ways, among +** others: +** +** uta utb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. For UT, the date & time +** method is best matched to the algorithm that is used by the Earth +** Rotation Angle function, called internally: maximum precision is +** delivered when the uta argument is for 0hrs UT1 on the day in +** question and the utb argument lies in the range 0 to 1, or vice +** versa. +** +** 2) The result is compatible with the IAU 2000 resolutions, except +** that accuracy has been compromised for the sake of speed and +** convenience in two respects: +** +** . UT is used instead of TDB (or TT) to compute the precession +** component of GMST and the equation of the equinoxes. This +** results in errors of order 0.1 mas at present. +** +** . The IAU 2000B abridged nutation model (McCarthy & Luzum, 2001) +** is used, introducing errors of up to 1 mas. +** +** 3) This GAST is compatible with the IAU 2000 resolutions and must be +** used only in conjunction with other IAU 2000 compatible +** components such as precession-nutation. +** +** 4) The result is returned in the range 0 to 2pi. +** +** 5) The algorithm is from Capitaine et al. (2003) and IERS +** Conventions 2003. +** +** Called: +** eraGmst00 Greenwich mean sidereal time, IAU 2000 +** eraEe00b equation of the equinoxes, IAU 2000B +** eraAnp normalize angle into range 0 to 2pi +** +** References: +** +** Capitaine, N., Wallace, P.T. and McCarthy, D.D., "Expressions to +** implement the IAU 2000 definition of UT1", Astronomy & +** Astrophysics, 406, 1135-1149 (2003) +** +** McCarthy, D.D. & Luzum, B.J., "An abridged model of the +** precession-nutation of the celestial pole", Celestial Mechanics & +** Dynamical Astronomy, 85, 37-49 (2003) +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gmst00, ee00b, gst; + + + gmst00 = eraGmst00(uta, utb, uta, utb); + ee00b = eraEe00b(uta, utb); + gst = eraAnp(gmst00 + ee00b); + + return gst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gst06.c b/erfa/gst06.c new file mode 100644 index 0000000..79a1c37 --- /dev/null +++ b/erfa/gst06.c @@ -0,0 +1,149 @@ +#include "erfa.h" + +double eraGst06(double uta, double utb, double tta, double ttb, + double rnpb[3][3]) +/* +** - - - - - - - - - +** e r a G s t 0 6 +** - - - - - - - - - +** +** Greenwich apparent sidereal time, IAU 2006, given the NPB matrix. +** +** Given: +** uta,utb double UT1 as a 2-part Julian Date (Notes 1,2) +** tta,ttb double TT as a 2-part Julian Date (Notes 1,2) +** rnpb double[3][3] nutation x precession x bias matrix +** +** Returned (function value): +** double Greenwich apparent sidereal time (radians) +** +** Notes: +** +** 1) The UT1 and TT dates uta+utb and tta+ttb respectively, are both +** Julian Dates, apportioned in any convenient way between the +** argument pairs. For example, JD=2450123.7 could be expressed in +** any of these ways, among others: +** +** Part A Part B +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable (in the case of UT; the TT is not at all critical +** in this respect). The J2000 and MJD methods are good compromises +** between resolution and convenience. For UT, the date & time +** method is best matched to the algorithm that is used by the Earth +** rotation angle function, called internally: maximum precision is +** delivered when the uta argument is for 0hrs UT1 on the day in +** question and the utb argument lies in the range 0 to 1, or vice +** versa. +** +** 2) Both UT1 and TT are required, UT1 to predict the Earth rotation +** and TT to predict the effects of precession-nutation. If UT1 is +** used for both purposes, errors of order 100 microarcseconds +** result. +** +** 3) Although the function uses the IAU 2006 series for s+XY/2, it is +** otherwise independent of the precession-nutation model and can in +** practice be used with any equinox-based NPB matrix. +** +** 4) The result is returned in the range 0 to 2pi. +** +** Called: +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS06 the CIO locator s, given X,Y, IAU 2006 +** eraAnp normalize angle into range 0 to 2pi +** eraEra00 Earth rotation angle, IAU 2000 +** eraEors equation of the origins, given NPB matrix and s +** +** Reference: +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double x, y, s, era, eors, gst; + + +/* Extract CIP coordinates. */ + eraBpn2xy(rnpb, &x, &y); + +/* The CIO locator, s. */ + s = eraS06(tta, ttb, x, y); + +/* Greenwich apparent sidereal time. */ + era = eraEra00(uta, utb); + eors = eraEors(rnpb, s); + gst = eraAnp(era - eors); + + return gst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gst06a.c b/erfa/gst06a.c new file mode 100644 index 0000000..fb38999 --- /dev/null +++ b/erfa/gst06a.c @@ -0,0 +1,140 @@ +#include "erfa.h" + +double eraGst06a(double uta, double utb, double tta, double ttb) +/* +** - - - - - - - - - - +** e r a G s t 0 6 a +** - - - - - - - - - - +** +** Greenwich apparent sidereal time (consistent with IAU 2000 and 2006 +** resolutions). +** +** Given: +** uta,utb double UT1 as a 2-part Julian Date (Notes 1,2) +** tta,ttb double TT as a 2-part Julian Date (Notes 1,2) +** +** Returned (function value): +** double Greenwich apparent sidereal time (radians) +** +** Notes: +** +** 1) The UT1 and TT dates uta+utb and tta+ttb respectively, are both +** Julian Dates, apportioned in any convenient way between the +** argument pairs. For example, JD=2450123.7 could be expressed in +** any of these ways, among others: +** +** Part A Part B +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable (in the case of UT; the TT is not at all critical +** in this respect). The J2000 and MJD methods are good compromises +** between resolution and convenience. For UT, the date & time +** method is best matched to the algorithm that is used by the Earth +** rotation angle function, called internally: maximum precision is +** delivered when the uta argument is for 0hrs UT1 on the day in +** question and the utb argument lies in the range 0 to 1, or vice +** versa. +** +** 2) Both UT1 and TT are required, UT1 to predict the Earth rotation +** and TT to predict the effects of precession-nutation. If UT1 is +** used for both purposes, errors of order 100 microarcseconds +** result. +** +** 3) This GAST is compatible with the IAU 2000/2006 resolutions and +** must be used only in conjunction with IAU 2006 precession and +** IAU 2000A nutation. +** +** 4) The result is returned in the range 0 to 2pi. +** +** Called: +** eraPnm06a classical NPB matrix, IAU 2006/2000A +** eraGst06 Greenwich apparent ST, IAU 2006, given NPB matrix +** +** Reference: +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rnpb[3][3], gst; + + +/* Classical nutation x precession x bias matrix, IAU 2000A. */ + eraPnm06a(tta, ttb, rnpb); + +/* Greenwich apparent sidereal time. */ + gst = eraGst06(uta, utb, tta, ttb, rnpb); + + return gst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/gst94.c b/erfa/gst94.c new file mode 100644 index 0000000..79f7ba8 --- /dev/null +++ b/erfa/gst94.c @@ -0,0 +1,140 @@ +#include "erfa.h" + +double eraGst94(double uta, double utb) +/* +** - - - - - - - - - +** e r a G s t 9 4 +** - - - - - - - - - +** +** Greenwich apparent sidereal time (consistent with IAU 1982/94 +** resolutions). +** +** Given: +** uta,utb double UT1 as a 2-part Julian Date (Notes 1,2) +** +** Returned (function value): +** double Greenwich apparent sidereal time (radians) +** +** Notes: +** +** 1) The UT1 date uta+utb is a Julian Date, apportioned in any +** convenient way between the argument pair. For example, +** JD=2450123.7 could be expressed in any of these ways, among +** others: +** +** uta utb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 and MJD methods are good compromises +** between resolution and convenience. For UT, the date & time +** method is best matched to the algorithm that is used by the Earth +** Rotation Angle function, called internally: maximum precision is +** delivered when the uta argument is for 0hrs UT1 on the day in +** question and the utb argument lies in the range 0 to 1, or vice +** versa. +** +** 2) The result is compatible with the IAU 1982 and 1994 resolutions, +** except that accuracy has been compromised for the sake of +** convenience in that UT is used instead of TDB (or TT) to compute +** the equation of the equinoxes. +** +** 3) This GAST must be used only in conjunction with contemporaneous +** IAU standards such as 1976 precession, 1980 obliquity and 1982 +** nutation. It is not compatible with the IAU 2000 resolutions. +** +** 4) The result is returned in the range 0 to 2pi. +** +** Called: +** eraGmst82 Greenwich mean sidereal time, IAU 1982 +** eraEqeq94 equation of the equinoxes, IAU 1994 +** eraAnp normalize angle into range 0 to 2pi +** +** References: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** IAU Resolution C7, Recommendation 3 (1994) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gmst82, eqeq94, gst; + + + gmst82 = eraGmst82(uta, utb); + eqeq94 = eraEqeq94(uta, utb); + gst = eraAnp(gmst82 + eqeq94); + + return gst; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/h2fk5.c b/erfa/h2fk5.c new file mode 100644 index 0000000..f7132f4 --- /dev/null +++ b/erfa/h2fk5.c @@ -0,0 +1,157 @@ +#include "erfa.h" + +void eraH2fk5(double rh, double dh, + double drh, double ddh, double pxh, double rvh, + double *r5, double *d5, + double *dr5, double *dd5, double *px5, double *rv5) +/* +** - - - - - - - - - +** e r a H 2 f k 5 +** - - - - - - - - - +** +** Transform Hipparcos star data into the FK5 (J2000.0) system. +** +** Given (all Hipparcos, epoch J2000.0): +** rh double RA (radians) +** dh double Dec (radians) +** drh double proper motion in RA (dRA/dt, rad/Jyear) +** ddh double proper motion in Dec (dDec/dt, rad/Jyear) +** pxh double parallax (arcsec) +** rvh double radial velocity (km/s, positive = receding) +** +** Returned (all FK5, equinox J2000.0, epoch J2000.0): +** r5 double RA (radians) +** d5 double Dec (radians) +** dr5 double proper motion in RA (dRA/dt, rad/Jyear) +** dd5 double proper motion in Dec (dDec/dt, rad/Jyear) +** px5 double parallax (arcsec) +** rv5 double radial velocity (km/s, positive = receding) +** +** Notes: +** +** 1) This function transforms Hipparcos star positions and proper +** motions into FK5 J2000.0. +** +** 2) The proper motions in RA are dRA/dt rather than +** cos(Dec)*dRA/dt, and are per year rather than per century. +** +** 3) The FK5 to Hipparcos transformation is modeled as a pure +** rotation and spin; zonal errors in the FK5 catalog are not +** taken into account. +** +** 4) See also eraFk52h, eraFk5hz, eraHfk5z. +** +** Called: +** eraStarpv star catalog data to space motion pv-vector +** eraFk5hip FK5 to Hipparcos rotation and spin +** eraRv2m r-vector to r-matrix +** eraRxp product of r-matrix and p-vector +** eraTrxp product of transpose of r-matrix and p-vector +** eraPxp vector product of two p-vectors +** eraPmp p-vector minus p-vector +** eraPvstar space motion pv-vector to star catalog data +** +** Reference: +** +** F.Mignard & M.Froeschle, Astron. Astrophys. 354, 732-739 (2000). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int i; + double pvh[2][3], r5h[3][3], s5h[3], sh[3], wxp[3], vv[3], pv5[2][3]; + + +/* Hipparcos barycentric position/velocity pv-vector (normalized). */ + eraStarpv(rh, dh, drh, ddh, pxh, rvh, pvh); + +/* FK5 to Hipparcos orientation matrix and spin vector. */ + eraFk5hip(r5h, s5h); + +/* Make spin units per day instead of per year. */ + for ( i = 0; i < 3; s5h[i++] /= 365.25 ); + +/* Orient the spin into the Hipparcos system. */ + eraRxp(r5h, s5h, sh); + +/* De-orient the Hipparcos position into the FK5 system. */ + eraTrxp(r5h, pvh[0], pv5[0]); + +/* Apply spin to the position giving an extra space motion component. */ + eraPxp(pvh[0], sh, wxp); + +/* Subtract this component from the Hipparcos space motion. */ + eraPmp(pvh[1], wxp, vv); + +/* De-orient the Hipparcos space motion into the FK5 system. */ + eraTrxp(r5h, vv, pv5[1]); + +/* FK5 pv-vector to spherical. */ + eraPvstar(pv5, r5, d5, dr5, dd5, px5, rv5); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/hfk5z.c b/erfa/hfk5z.c new file mode 100644 index 0000000..d2c442e --- /dev/null +++ b/erfa/hfk5z.c @@ -0,0 +1,184 @@ +#include "erfa.h" + +void eraHfk5z(double rh, double dh, double date1, double date2, + double *r5, double *d5, double *dr5, double *dd5) +/* +** - - - - - - - - - +** e r a H f k 5 z +** - - - - - - - - - +** +** Transform a Hipparcos star position into FK5 J2000.0, assuming +** zero Hipparcos proper motion. +** +** Given: +** rh double Hipparcos RA (radians) +** dh double Hipparcos Dec (radians) +** date1,date2 double TDB date (Note 1) +** +** Returned (all FK5, equinox J2000.0, date date1+date2): +** r5 double RA (radians) +** d5 double Dec (radians) +** dr5 double FK5 RA proper motion (rad/year, Note 4) +** dd5 double Dec proper motion (rad/year, Note 4) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The proper motion in RA is dRA/dt rather than cos(Dec)*dRA/dt. +** +** 3) The FK5 to Hipparcos transformation is modeled as a pure rotation +** and spin; zonal errors in the FK5 catalogue are not taken into +** account. +** +** 4) It was the intention that Hipparcos should be a close +** approximation to an inertial frame, so that distant objects have +** zero proper motion; such objects have (in general) non-zero +** proper motion in FK5, and this function returns those fictitious +** proper motions. +** +** 5) The position returned by this function is in the FK5 J2000.0 +** reference system but at date date1+date2. +** +** 6) See also eraFk52h, eraH2fk5, eraFk5zhz. +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraFk5hip FK5 to Hipparcos rotation and spin +** eraRxp product of r-matrix and p-vector +** eraSxp multiply p-vector by scalar +** eraRxr product of two r-matrices +** eraTrxp product of transpose of r-matrix and p-vector +** eraPxp vector product of two p-vectors +** eraPv2s pv-vector to spherical +** eraAnp normalize angle into range 0 to 2pi +** +** Reference: +** +** F.Mignard & M.Froeschle, 2000, Astron.Astrophys. 354, 732-739. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, ph[3], r5h[3][3], s5h[3], sh[3], vst[3], + rst[3][3], r5ht[3][3], pv5e[2][3], vv[3], + w, r, v; + + +/* Time interval from fundamental epoch J2000.0 to given date (JY). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJY; + +/* Hipparcos barycentric position vector (normalized). */ + eraS2c(rh, dh, ph); + +/* FK5 to Hipparcos orientation matrix and spin vector. */ + eraFk5hip(r5h, s5h); + +/* Rotate the spin into the Hipparcos system. */ + eraRxp(r5h, s5h, sh); + +/* Accumulated Hipparcos wrt FK5 spin over that interval. */ + eraSxp(t, s5h, vst); + +/* Express the accumulated spin as a rotation matrix. */ + eraRv2m(vst, rst); + +/* Rotation matrix: accumulated spin, then FK5 to Hipparcos. */ + eraRxr(r5h, rst, r5ht); + +/* De-orient & de-spin the Hipparcos position into FK5 J2000.0. */ + eraTrxp(r5ht, ph, pv5e[0]); + +/* Apply spin to the position giving a space motion. */ + eraPxp(sh, ph, vv); + +/* De-orient & de-spin the Hipparcos space motion into FK5 J2000.0. */ + eraTrxp(r5ht, vv, pv5e[1]); + +/* FK5 position/velocity pv-vector to spherical. */ + eraPv2s(pv5e, &w, d5, &r, dr5, dd5, &v); + *r5 = eraAnp(w); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ir.c b/erfa/ir.c new file mode 100644 index 0000000..ea077d6 --- /dev/null +++ b/erfa/ir.c @@ -0,0 +1,92 @@ +#include "erfa.h" + +void eraIr(double r[3][3]) +/* +** - - - - - - +** e r a I r +** - - - - - - +** +** Initialize an r-matrix to the identity matrix. +** +** Returned: +** r double[3][3] r-matrix +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + r[0][0] = 1.0; + r[0][1] = 0.0; + r[0][2] = 0.0; + r[1][0] = 0.0; + r[1][1] = 1.0; + r[1][2] = 0.0; + r[2][0] = 0.0; + r[2][1] = 0.0; + r[2][2] = 1.0; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/jd2cal.c b/erfa/jd2cal.c new file mode 100644 index 0000000..16c73c6 --- /dev/null +++ b/erfa/jd2cal.c @@ -0,0 +1,164 @@ +#include "erfa.h" + +int eraJd2cal(double dj1, double dj2, + int *iy, int *im, int *id, double *fd) +/* +** - - - - - - - - - - +** e r a J d 2 c a l +** - - - - - - - - - - +** +** Julian Date to Gregorian year, month, day, and fraction of a day. +** +** Given: +** dj1,dj2 double Julian Date (Notes 1, 2) +** +** Returned (arguments): +** iy int year +** im int month +** id int day +** fd double fraction of day +** +** Returned (function value): +** int status: +** 0 = OK +** -1 = unacceptable date (Note 3) +** +** Notes: +** +** 1) The earliest valid date is -68569.5 (-4900 March 1). The +** largest value accepted is 1e9. +** +** 2) The Julian Date is apportioned in any convenient way between +** the arguments dj1 and dj2. For example, JD=2450123.7 could +** be expressed in any of these ways, among others: +** +** dj1 dj2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** 3) In early eras the conversion is from the "proleptic Gregorian +** calendar"; no account is taken of the date(s) of adoption of +** the Gregorian calendar, nor is the AD/BC numbering convention +** observed. +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 12.92 (p604). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Minimum and maximum allowed JD */ + const double DJMIN = -68569.5; + const double DJMAX = 1e9; + + long jd, l, n, i, k; + double dj, d1, d2, f1, f2, f, d; + + +/* Verify date is acceptable. */ + dj = dj1 + dj2; + if (dj < DJMIN || dj > DJMAX) return -1; + +/* Copy the date, big then small, and re-align to midnight. */ + if (dj1 >= dj2) { + d1 = dj1; + d2 = dj2; + } else { + d1 = dj2; + d2 = dj1; + } + d2 -= 0.5; + +/* Separate day and fraction. */ + f1 = fmod(d1, 1.0); + f2 = fmod(d2, 1.0); + f = fmod(f1 + f2, 1.0); + if (f < 0.0) f += 1.0; + d = floor(d1 - f1) + floor(d2 - f2) + floor(f1 + f2 - f); + jd = (long) floor(d) + 1L; + +/* Express day in Gregorian calendar. */ + l = jd + 68569L; + n = (4L * l) / 146097L; + l -= (146097L * n + 3L) / 4L; + i = (4000L * (l + 1L)) / 1461001L; + l -= (1461L * i) / 4L - 31L; + k = (80L * l) / 2447L; + *id = (int) (l - (2447L * k) / 80L); + l = k / 11L; + *im = (int) (k + 2L - 12L * l); + *iy = (int) (100L * (n - 49L) + i + l); + *fd = f; + + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/jdcalf.c b/erfa/jdcalf.c new file mode 100644 index 0000000..3bd54ab --- /dev/null +++ b/erfa/jdcalf.c @@ -0,0 +1,170 @@ +#include "erfa.h" + +int eraJdcalf(int ndp, double dj1, double dj2, int iymdf[4]) +/* +** - - - - - - - - - - +** e r a J d c a l f +** - - - - - - - - - - +** +** Julian Date to Gregorian Calendar, expressed in a form convenient +** for formatting messages: rounded to a specified precision. +** +** Given: +** ndp int number of decimal places of days in fraction +** dj1,dj2 double dj1+dj2 = Julian Date (Note 1) +** +** Returned: +** iymdf int[4] year, month, day, fraction in Gregorian +** calendar +** +** Returned (function value): +** int status: +** -1 = date out of range +** 0 = OK +** +1 = NDP not 0-9 (interpreted as 0) +** +** Notes: +** +** 1) The Julian Date is apportioned in any convenient way between +** the arguments dj1 and dj2. For example, JD=2450123.7 could +** be expressed in any of these ways, among others: +** +** dj1 dj2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** 2) In early eras the conversion is from the "Proleptic Gregorian +** Calendar"; no account is taken of the date(s) of adoption of +** the Gregorian Calendar, nor is the AD/BC numbering convention +** observed. +** +** 3) Refer to the function eraJd2cal. +** +** 4) NDP should be 4 or less if internal overflows are to be +** avoided on machines which use 16-bit integers. +** +** Called: +** eraJd2cal JD to Gregorian calendar +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 12.92 (p604). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int j, js; + double denom, d1, d2, f1, f2, f; + + +/* Denominator of fraction (e.g. 100 for 2 decimal places). */ + if ((ndp >= 0) && (ndp <= 9)) { + j = 0; + denom = pow(10.0, ndp); + } else { + j = 1; + denom = 1.0; + } + +/* Copy the date, big then small, and realign to midnight. */ + if (dj1 >= dj2) { + d1 = dj1; + d2 = dj2; + } else { + d1 = dj2; + d2 = dj1; + } + d2 -= 0.5; + +/* Separate days and fractions. */ + f1 = fmod(d1, 1.0); + f2 = fmod(d2, 1.0); + d1 = floor(d1 - f1); + d2 = floor(d2 - f2); + +/* Round the total fraction to the specified number of places. */ + f = floor((f1+f2)*denom + 0.5) / denom; + +/* Re-assemble the rounded date and re-align to noon. */ + d2 += f + 0.5; + +/* Convert to Gregorian calendar. */ + js = eraJd2cal(d1, d2, &iymdf[0], &iymdf[1], &iymdf[2], &f); + if (js == 0) { + iymdf[3] = (int) (f * denom); + } else { + j = js; + } + +/* Return the status. */ + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ld.c b/erfa/ld.c new file mode 100644 index 0000000..c732ab7 --- /dev/null +++ b/erfa/ld.c @@ -0,0 +1,161 @@ +#include "erfa.h" + +void eraLd(double bm, double p[3], double q[3], double e[3], + double em, double dlim, double p1[3]) +/* +** - - - - - - +** e r a L d +** - - - - - - +** +** Apply light deflection by a solar-system body, as part of +** transforming coordinate direction into natural direction. +** +** Given: +** bm double mass of the gravitating body (solar masses) +** p double[3] direction from observer to source (unit vector) +** q double[3] direction from body to source (unit vector) +** e double[3] direction from body to observer (unit vector) +** em double distance from body to observer (au) +** dlim double deflection limiter (Note 4) +** +** Returned: +** p1 double[3] observer to deflected source (unit vector) +** +** Notes: +** +** 1) The algorithm is based on Expr. (70) in Klioner (2003) and +** Expr. (7.63) in the Explanatory Supplement (Urban & Seidelmann +** 2013), with some rearrangement to minimize the effects of machine +** precision. +** +** 2) The mass parameter bm can, as required, be adjusted in order to +** allow for such effects as quadrupole field. +** +** 3) The barycentric position of the deflecting body should ideally +** correspond to the time of closest approach of the light ray to +** the body. +** +** 4) The deflection limiter parameter dlim is phi^2/2, where phi is +** the angular separation (in radians) between source and body at +** which limiting is applied. As phi shrinks below the chosen +** threshold, the deflection is artificially reduced, reaching zero +** for phi = 0. +** +** 5) The returned vector p1 is not normalized, but the consequential +** departure from unit magnitude is always negligible. +** +** 6) The arguments p and p1 can be the same array. +** +** 7) To accumulate total light deflection taking into account the +** contributions from several bodies, call the present function for +** each body in succession, in decreasing order of distance from the +** observer. +** +** 8) For efficiency, validation is omitted. The supplied vectors must +** be of unit magnitude, and the deflection limiter non-zero and +** positive. +** +** References: +** +** Urban, S. & Seidelmann, P. K. (eds), Explanatory Supplement to +** the Astronomical Almanac, 3rd ed., University Science Books +** (2013). +** +** Klioner, Sergei A., "A practical relativistic model for micro- +** arcsecond astrometry in space", Astr. J. 125, 1580-1597 (2003). +** +** Called: +** eraPdp scalar product of two p-vectors +** eraPxp vector product of two p-vectors +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int i; + double qpe[3], qdqpe, w, eq[3], peq[3]; + + +/* q . (q + e). */ + for (i = 0; i < 3; i++) { + qpe[i] = q[i] + e[i]; + } + qdqpe = eraPdp(q, qpe); + +/* 2 x G x bm / ( em x c^2 x ( q . (q + e) ) ). */ + w = bm * ERFA_SRS / em / ERFA_GMAX(qdqpe,dlim); + +/* p x (e x q). */ + eraPxp(e, q, eq); + eraPxp(p, eq, peq); + +/* Apply the deflection. */ + for (i = 0; i < 3; i++) { + p1[i] = p[i] + w*peq[i]; + } + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ldn.c b/erfa/ldn.c new file mode 100644 index 0000000..cd56f48 --- /dev/null +++ b/erfa/ldn.c @@ -0,0 +1,183 @@ +#include "erfa.h" + +void eraLdn(int n, eraLDBODY b[], double ob[3], double sc[3], + double sn[3]) +/*+ +** - - - - - - - +** e r a L d n +** - - - - - - - +** +** For a star, apply light deflection by multiple solar-system bodies, +** as part of transforming coordinate direction into natural direction. +** +** Given: +** n int number of bodies (note 1) +** b eraLDBODY[n] data for each of the n bodies (Notes 1,2): +** bm double mass of the body (solar masses, Note 3) +** dl double deflection limiter (Note 4) +** pv [2][3] barycentric PV of the body (au, au/day) +** ob double[3] barycentric position of the observer (au) +** sc double[3] observer to star coord direction (unit vector) +** +** Returned: +** sn double[3] observer to deflected star (unit vector) +** +** 1) The array b contains n entries, one for each body to be +** considered. If n = 0, no gravitational light deflection will be +** applied, not even for the Sun. +** +** 2) The array b should include an entry for the Sun as well as for +** any planet or other body to be taken into account. The entries +** should be in the order in which the light passes the body. +** +** 3) In the entry in the b array for body i, the mass parameter +** b[i].bm can, as required, be adjusted in order to allow for such +** effects as quadrupole field. +** +** 4) The deflection limiter parameter b[i].dl is phi^2/2, where phi is +** the angular separation (in radians) between star and body at +** which limiting is applied. As phi shrinks below the chosen +** threshold, the deflection is artificially reduced, reaching zero +** for phi = 0. Example values suitable for a terrestrial +** observer, together with masses, are as follows: +** +** body i b[i].bm b[i].dl +** +** Sun 1.0 6e-6 +** Jupiter 0.00095435 3e-9 +** Saturn 0.00028574 3e-10 +** +** 5) For cases where the starlight passes the body before reaching the +** observer, the body is placed back along its barycentric track by +** the light time from that point to the observer. For cases where +** the body is "behind" the observer no such shift is applied. If +** a different treatment is preferred, the user has the option of +** instead using the eraLd function. Similarly, eraLd can be used +** for cases where the source is nearby, not a star. +** +** 6) The returned vector sn is not normalized, but the consequential +** departure from unit magnitude is always negligible. +** +** 7) The arguments sc and sn can be the same array. +** +** 8) For efficiency, validation is omitted. The supplied masses must +** be greater than zero, the position and velocity vectors must be +** right, and the deflection limiter greater than zero. +** +** Reference: +** +** Urban, S. & Seidelmann, P. K. (eds), Explanatory Supplement to +** the Astronomical Almanac, 3rd ed., University Science Books +** (2013), Section 7.2.4. +** +** Called: +** eraCp copy p-vector +** eraPdp scalar product of two p-vectors +** eraPmp p-vector minus p-vector +** eraPpsp p-vector plus scaled p-vector +** eraPn decompose p-vector into modulus and direction +** eraLd light deflection by a solar-system body +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Light time for 1 AU (days) */ + const double CR = ERFA_AULT/ERFA_DAYSEC; + + int i; + double v[3], dt, ev[3], em, e[3]; + + +/* Star direction prior to deflection. */ + eraCp(sc, sn); + +/* Body by body. */ + for ( i = 0; i < n; i++ ) { + + /* Body to observer vector at epoch of observation (au). */ + eraPmp ( ob, b[i].pv[0], v ); + + /* Minus the time since the light passed the body (days). */ + dt = eraPdp(sn,v) * CR; + + /* Neutralize if the star is "behind" the observer. */ + dt = ERFA_GMIN(dt, 0.0); + + /* Backtrack the body to the time the light was passing the body. */ + eraPpsp(v, -dt, b[i].pv[1], ev); + + /* Body to observer vector as magnitude and direction. */ + eraPn(ev, &em, e); + + /* Apply light deflection for this body. */ + eraLd ( b[i].bm, sn, sn, e, em, b[i].dl, sn ); + + /* Next body. */ + } + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ldsun.c b/erfa/ldsun.c new file mode 100644 index 0000000..c29d608 --- /dev/null +++ b/erfa/ldsun.c @@ -0,0 +1,105 @@ +#include "erfa.h" + +void eraLdsun(double p[3], double e[3], double em, double p1[3]) +/* +** - - - - - - - - - +** e r a L d s u n +** - - - - - - - - - +** +** Light deflection by the Sun. +** +** Given: +** p double[3] direction from observer to source (unit vector) +** e double[3] direction from Sun to observer (unit vector) +** em double distance from Sun to observer (au) +** +** Returned: +** p1 double[3] observer to deflected source (unit vector) +** +** Notes: +** +** 1) The source is presumed to be sufficiently distant that its +** directions seen from the Sun and the observer are essentially +** the same. +** +** 2) The deflection is restrained when the angle between the star and +** the center of the Sun is less than about 9 arcsec, falling to +** zero for zero separation. (The chosen threshold is within the +** solar limb for all solar-system applications.) +** +** 3) The arguments p and p1 can be the same array. +** +** Called: +** eraLd light deflection by a solar-system body +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraLd(1.0, p, p, e, em, 1e-9, p1); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/num00a.c b/erfa/num00a.c new file mode 100644 index 0000000..2cb19d4 --- /dev/null +++ b/erfa/num00a.c @@ -0,0 +1,130 @@ +#include "erfa.h" + +void eraNum00a(double date1, double date2, double rmatn[3][3]) +/* +** - - - - - - - - - - +** e r a N u m 0 0 a +** - - - - - - - - - - +** +** Form the matrix of nutation for a given date, IAU 2000A model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rmatn double[3][3] nutation matrix +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(true) = rmatn * V(mean), where +** the p-vector V(true) is with respect to the true equatorial triad +** of date and the p-vector V(mean) is with respect to the mean +** equatorial triad of date. +** +** 3) A faster, but slightly less accurate result (about 1 mas), can be +** obtained by using instead the eraNum00b function. +** +** Called: +** eraPn00a bias/precession/nutation, IAU 2000A +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 3.222-3 (p114). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsi, deps, epsa, rb[3][3], rp[3][3], rbp[3][3], rbpn[3][3]; + + +/* Obtain the required matrix (discarding other results). */ + eraPn00a(date1, date2, + &dpsi, &deps, &epsa, rb, rp, rbp, rmatn, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/num00b.c b/erfa/num00b.c new file mode 100644 index 0000000..87e4361 --- /dev/null +++ b/erfa/num00b.c @@ -0,0 +1,130 @@ +#include "erfa.h" + +void eraNum00b(double date1, double date2, double rmatn[3][3]) +/* +** - - - - - - - - - - +** e r a N u m 0 0 b +** - - - - - - - - - - +** +** Form the matrix of nutation for a given date, IAU 2000B model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rmatn double[3][3] nutation matrix +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(true) = rmatn * V(mean), where +** the p-vector V(true) is with respect to the true equatorial triad +** of date and the p-vector V(mean) is with respect to the mean +** equatorial triad of date. +** +** 3) The present function is faster, but slightly less accurate (about +** 1 mas), than the eraNum00a function. +** +** Called: +** eraPn00b bias/precession/nutation, IAU 2000B +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 3.222-3 (p114). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsi, deps, epsa, rb[3][3], rp[3][3], rbp[3][3], rbpn[3][3]; + + +/* Obtain the required matrix (discarding other results). */ + eraPn00b(date1, date2, + &dpsi, &deps, &epsa, rb, rp, rbp, rmatn, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/num06a.c b/erfa/num06a.c new file mode 100644 index 0000000..824e487 --- /dev/null +++ b/erfa/num06a.c @@ -0,0 +1,134 @@ +#include "erfa.h" + +void eraNum06a(double date1, double date2, double rmatn[3][3]) +/* +** - - - - - - - - - - +** e r a N u m 0 6 a +** - - - - - - - - - - +** +** Form the matrix of nutation for a given date, IAU 2006/2000A model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rmatn double[3][3] nutation matrix +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(true) = rmatn * V(mean), where +** the p-vector V(true) is with respect to the true equatorial triad +** of date and the p-vector V(mean) is with respect to the mean +** equatorial triad of date. +** +** Called: +** eraObl06 mean obliquity, IAU 2006 +** eraNut06a nutation, IAU 2006/2000A +** eraNumat form nutation matrix +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 3.222-3 (p114). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double eps, dp, de; + + +/* Mean obliquity. */ + eps = eraObl06(date1, date2); + +/* Nutation components. */ + eraNut06a(date1, date2, &dp, &de); + +/* Nutation matrix. */ + eraNumat(eps, dp, de, rmatn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/numat.c b/erfa/numat.c new file mode 100644 index 0000000..6023271 --- /dev/null +++ b/erfa/numat.c @@ -0,0 +1,118 @@ +#include "erfa.h" + +void eraNumat(double epsa, double dpsi, double deps, double rmatn[3][3]) +/* +** - - - - - - - - - +** e r a N u m a t +** - - - - - - - - - +** +** Form the matrix of nutation. +** +** Given: +** epsa double mean obliquity of date (Note 1) +** dpsi,deps double nutation (Note 2) +** +** Returned: +** rmatn double[3][3] nutation matrix (Note 3) +** +** Notes: +** +** +** 1) The supplied mean obliquity epsa, must be consistent with the +** precession-nutation models from which dpsi and deps were obtained. +** +** 2) The caller is responsible for providing the nutation components; +** they are in longitude and obliquity, in radians and are with +** respect to the equinox and ecliptic of date. +** +** 3) The matrix operates in the sense V(true) = rmatn * V(mean), +** where the p-vector V(true) is with respect to the true +** equatorial triad of date and the p-vector V(mean) is with +** respect to the mean equatorial triad of date. +** +** Called: +** eraIr initialize r-matrix to identity +** eraRx rotate around X-axis +** eraRz rotate around Z-axis +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 3.222-3 (p114). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Build the rotation matrix. */ + eraIr(rmatn); + eraRx(epsa, rmatn); + eraRz(-dpsi, rmatn); + eraRx(-(epsa + deps), rmatn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/nut00a.c b/erfa/nut00a.c new file mode 100644 index 0000000..bb63d72 --- /dev/null +++ b/erfa/nut00a.c @@ -0,0 +1,2056 @@ +#include "erfa.h" + +void eraNut00a(double date1, double date2, double *dpsi, double *deps) +/* +** - - - - - - - - - - +** e r a N u t 0 0 a +** - - - - - - - - - - +** +** Nutation, IAU 2000A model (MHB2000 luni-solar and planetary nutation +** with free core nutation omitted). +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsi,deps double nutation, luni-solar + planetary (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The nutation components in longitude and obliquity are in radians +** and with respect to the equinox and ecliptic of date. The +** obliquity at J2000.0 is assumed to be the Lieske et al. (1977) +** value of 84381.448 arcsec. +** +** Both the luni-solar and planetary nutations are included. The +** latter are due to direct planetary nutations and the +** perturbations of the lunar and terrestrial orbits. +** +** 3) The function computes the MHB2000 nutation series with the +** associated corrections for planetary nutations. It is an +** implementation of the nutation part of the IAU 2000A precession- +** nutation model, formally adopted by the IAU General Assembly in +** 2000, namely MHB2000 (Mathews et al. 2002), but with the free +** core nutation (FCN - see Note 4) omitted. +** +** 4) The full MHB2000 model also contains contributions to the +** nutations in longitude and obliquity due to the free-excitation +** of the free-core-nutation during the period 1979-2000. These FCN +** terms, which are time-dependent and unpredictable, are NOT +** included in the present function and, if required, must be +** independently computed. With the FCN corrections included, the +** present function delivers a pole which is at current epochs +** accurate to a few hundred microarcseconds. The omission of FCN +** introduces further errors of about that size. +** +** 5) The present function provides classical nutation. The MHB2000 +** algorithm, from which it is adapted, deals also with (i) the +** offsets between the GCRS and mean poles and (ii) the adjustments +** in longitude and obliquity due to the changed precession rates. +** These additional functions, namely frame bias and precession +** adjustments, are supported by the ERFA functions eraBi00 and +** eraPr00. +** +** 6) The MHB2000 algorithm also provides "total" nutations, comprising +** the arithmetic sum of the frame bias, precession adjustments, +** luni-solar nutation and planetary nutation. These total +** nutations can be used in combination with an existing IAU 1976 +** precession implementation, such as eraPmat76, to deliver GCRS- +** to-true predictions of sub-mas accuracy at current dates. +** However, there are three shortcomings in the MHB2000 model that +** must be taken into account if more accurate or definitive results +** are required (see Wallace 2002): +** +** (i) The MHB2000 total nutations are simply arithmetic sums, +** yet in reality the various components are successive Euler +** rotations. This slight lack of rigor leads to cross terms +** that exceed 1 mas after a century. The rigorous procedure +** is to form the GCRS-to-true rotation matrix by applying the +** bias, precession and nutation in that order. +** +** (ii) Although the precession adjustments are stated to be with +** respect to Lieske et al. (1977), the MHB2000 model does +** not specify which set of Euler angles are to be used and +** how the adjustments are to be applied. The most literal +** and straightforward procedure is to adopt the 4-rotation +** epsilon_0, psi_A, omega_A, xi_A option, and to add DPSIPR +** to psi_A and DEPSPR to both omega_A and eps_A. +** +** (iii) The MHB2000 model predates the determination by Chapront +** et al. (2002) of a 14.6 mas displacement between the +** J2000.0 mean equinox and the origin of the ICRS frame. It +** should, however, be noted that neglecting this displacement +** when calculating star coordinates does not lead to a +** 14.6 mas change in right ascension, only a small second- +** order distortion in the pattern of the precession-nutation +** effect. +** +** For these reasons, the ERFA functions do not generate the "total +** nutations" directly, though they can of course easily be +** generated by calling eraBi00, eraPr00 and the present function +** and adding the results. +** +** 7) The MHB2000 model contains 41 instances where the same frequency +** appears multiple times, of which 38 are duplicates and three are +** triplicates. To keep the present code close to the original MHB +** algorithm, this small inefficiency has not been corrected. +** +** Called: +** eraFal03 mean anomaly of the Moon +** eraFaf03 mean argument of the latitude of the Moon +** eraFaom03 mean longitude of the Moon's ascending node +** eraFame03 mean longitude of Mercury +** eraFave03 mean longitude of Venus +** eraFae03 mean longitude of Earth +** eraFama03 mean longitude of Mars +** eraFaju03 mean longitude of Jupiter +** eraFasa03 mean longitude of Saturn +** eraFaur03 mean longitude of Uranus +** eraFapa03 general accumulated precession in longitude +** +** References: +** +** Chapront, J., Chapront-Touze, M. & Francou, G. 2002, +** Astron.Astrophys. 387, 700 +** +** Lieske, J.H., Lederle, T., Fricke, W. & Morando, B. 1977, +** Astron.Astrophys. 58, 1-16 +** +** Mathews, P.M., Herring, T.A., Buffet, B.A. 2002, J.Geophys.Res. +** 107, B4. The MHB_2000 code itself was obtained on 9th September +** 2002 from ftp//maia.usno.navy.mil/conv2000/chapter5/IAU2000A. +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Wallace, P.T., "Software for Implementing the IAU 2000 +** Resolutions", in IERS Workshop 5.1 (2002) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int i; + double t, el, elp, f, d, om, arg, dp, de, sarg, carg, + al, af, ad, aom, alme, alve, alea, alma, + alju, alsa, alur, alne, apa, dpsils, depsls, + dpsipl, depspl; + +/* Units of 0.1 microarcsecond to radians */ + const double U2R = ERFA_DAS2R / 1e7; + +/* ------------------------- */ +/* Luni-Solar nutation model */ +/* ------------------------- */ + +/* The units for the sine and cosine coefficients are */ +/* 0.1 microarcsecond and the same per Julian century */ + + static const struct { + int nl,nlp,nf,nd,nom; /* coefficients of l,l',F,D,Om */ + double sp,spt,cp; /* longitude sin, t*sin, cos coefficients */ + double ce,cet,se; /* obliquity cos, t*cos, sin coefficients */ + } xls[] = { + + /* 1- 10 */ + { 0, 0, 0, 0, 1, + -172064161.0, -174666.0, 33386.0, 92052331.0, 9086.0, 15377.0}, + { 0, 0, 2,-2, 2, + -13170906.0, -1675.0, -13696.0, 5730336.0, -3015.0, -4587.0}, + { 0, 0, 2, 0, 2,-2276413.0,-234.0,2796.0,978459.0,-485.0, 1374.0}, + { 0, 0, 0, 0, 2,2074554.0, 207.0, -698.0,-897492.0,470.0, -291.0}, + { 0, 1, 0, 0, 0,1475877.0,-3633.0,11817.0,73871.0,-184.0,-1924.0}, + { 0, 1, 2,-2, 2,-516821.0,1226.0, -524.0,224386.0,-677.0, -174.0}, + { 1, 0, 0, 0, 0, 711159.0, 73.0, -872.0, -6750.0, 0.0, 358.0}, + { 0, 0, 2, 0, 1,-387298.0,-367.0, 380.0, 200728.0, 18.0, 318.0}, + { 1, 0, 2, 0, 2,-301461.0, -36.0, 816.0, 129025.0,-63.0, 367.0}, + { 0,-1, 2,-2, 2, 215829.0,-494.0, 111.0, -95929.0,299.0, 132.0}, + + /* 11-20 */ + { 0, 0, 2,-2, 1, 128227.0, 137.0, 181.0, -68982.0, -9.0, 39.0}, + {-1, 0, 2, 0, 2, 123457.0, 11.0, 19.0, -53311.0, 32.0, -4.0}, + {-1, 0, 0, 2, 0, 156994.0, 10.0, -168.0, -1235.0, 0.0, 82.0}, + { 1, 0, 0, 0, 1, 63110.0, 63.0, 27.0, -33228.0, 0.0, -9.0}, + {-1, 0, 0, 0, 1, -57976.0, -63.0, -189.0, 31429.0, 0.0, -75.0}, + {-1, 0, 2, 2, 2, -59641.0, -11.0, 149.0, 25543.0,-11.0, 66.0}, + { 1, 0, 2, 0, 1, -51613.0, -42.0, 129.0, 26366.0, 0.0, 78.0}, + {-2, 0, 2, 0, 1, 45893.0, 50.0, 31.0, -24236.0,-10.0, 20.0}, + { 0, 0, 0, 2, 0, 63384.0, 11.0, -150.0, -1220.0, 0.0, 29.0}, + { 0, 0, 2, 2, 2, -38571.0, -1.0, 158.0, 16452.0,-11.0, 68.0}, + + /* 21-30 */ + { 0,-2, 2,-2, 2, 32481.0, 0.0, 0.0, -13870.0, 0.0, 0.0}, + {-2, 0, 0, 2, 0, -47722.0, 0.0, -18.0, 477.0, 0.0, -25.0}, + { 2, 0, 2, 0, 2, -31046.0, -1.0, 131.0, 13238.0,-11.0, 59.0}, + { 1, 0, 2,-2, 2, 28593.0, 0.0, -1.0, -12338.0, 10.0, -3.0}, + {-1, 0, 2, 0, 1, 20441.0, 21.0, 10.0, -10758.0, 0.0, -3.0}, + { 2, 0, 0, 0, 0, 29243.0, 0.0, -74.0, -609.0, 0.0, 13.0}, + { 0, 0, 2, 0, 0, 25887.0, 0.0, -66.0, -550.0, 0.0, 11.0}, + { 0, 1, 0, 0, 1, -14053.0, -25.0, 79.0, 8551.0, -2.0, -45.0}, + {-1, 0, 0, 2, 1, 15164.0, 10.0, 11.0, -8001.0, 0.0, -1.0}, + { 0, 2, 2,-2, 2, -15794.0, 72.0, -16.0, 6850.0,-42.0, -5.0}, + + /* 31-40 */ + { 0, 0,-2, 2, 0, 21783.0, 0.0, 13.0, -167.0, 0.0, 13.0}, + { 1, 0, 0,-2, 1, -12873.0, -10.0, -37.0, 6953.0, 0.0, -14.0}, + { 0,-1, 0, 0, 1, -12654.0, 11.0, 63.0, 6415.0, 0.0, 26.0}, + {-1, 0, 2, 2, 1, -10204.0, 0.0, 25.0, 5222.0, 0.0, 15.0}, + { 0, 2, 0, 0, 0, 16707.0, -85.0, -10.0, 168.0, -1.0, 10.0}, + { 1, 0, 2, 2, 2, -7691.0, 0.0, 44.0, 3268.0, 0.0, 19.0}, + {-2, 0, 2, 0, 0, -11024.0, 0.0, -14.0, 104.0, 0.0, 2.0}, + { 0, 1, 2, 0, 2, 7566.0, -21.0, -11.0, -3250.0, 0.0, -5.0}, + { 0, 0, 2, 2, 1, -6637.0, -11.0, 25.0, 3353.0, 0.0, 14.0}, + { 0,-1, 2, 0, 2, -7141.0, 21.0, 8.0, 3070.0, 0.0, 4.0}, + + /* 41-50 */ + { 0, 0, 0, 2, 1, -6302.0, -11.0, 2.0, 3272.0, 0.0, 4.0}, + { 1, 0, 2,-2, 1, 5800.0, 10.0, 2.0, -3045.0, 0.0, -1.0}, + { 2, 0, 2,-2, 2, 6443.0, 0.0, -7.0, -2768.0, 0.0, -4.0}, + {-2, 0, 0, 2, 1, -5774.0, -11.0, -15.0, 3041.0, 0.0, -5.0}, + { 2, 0, 2, 0, 1, -5350.0, 0.0, 21.0, 2695.0, 0.0, 12.0}, + { 0,-1, 2,-2, 1, -4752.0, -11.0, -3.0, 2719.0, 0.0, -3.0}, + { 0, 0, 0,-2, 1, -4940.0, -11.0, -21.0, 2720.0, 0.0, -9.0}, + {-1,-1, 0, 2, 0, 7350.0, 0.0, -8.0, -51.0, 0.0, 4.0}, + { 2, 0, 0,-2, 1, 4065.0, 0.0, 6.0, -2206.0, 0.0, 1.0}, + { 1, 0, 0, 2, 0, 6579.0, 0.0, -24.0, -199.0, 0.0, 2.0}, + + /* 51-60 */ + { 0, 1, 2,-2, 1, 3579.0, 0.0, 5.0, -1900.0, 0.0, 1.0}, + { 1,-1, 0, 0, 0, 4725.0, 0.0, -6.0, -41.0, 0.0, 3.0}, + {-2, 0, 2, 0, 2, -3075.0, 0.0, -2.0, 1313.0, 0.0, -1.0}, + { 3, 0, 2, 0, 2, -2904.0, 0.0, 15.0, 1233.0, 0.0, 7.0}, + { 0,-1, 0, 2, 0, 4348.0, 0.0, -10.0, -81.0, 0.0, 2.0}, + { 1,-1, 2, 0, 2, -2878.0, 0.0, 8.0, 1232.0, 0.0, 4.0}, + { 0, 0, 0, 1, 0, -4230.0, 0.0, 5.0, -20.0, 0.0, -2.0}, + {-1,-1, 2, 2, 2, -2819.0, 0.0, 7.0, 1207.0, 0.0, 3.0}, + {-1, 0, 2, 0, 0, -4056.0, 0.0, 5.0, 40.0, 0.0, -2.0}, + { 0,-1, 2, 2, 2, -2647.0, 0.0, 11.0, 1129.0, 0.0, 5.0}, + + /* 61-70 */ + {-2, 0, 0, 0, 1, -2294.0, 0.0, -10.0, 1266.0, 0.0, -4.0}, + { 1, 1, 2, 0, 2, 2481.0, 0.0, -7.0, -1062.0, 0.0, -3.0}, + { 2, 0, 0, 0, 1, 2179.0, 0.0, -2.0, -1129.0, 0.0, -2.0}, + {-1, 1, 0, 1, 0, 3276.0, 0.0, 1.0, -9.0, 0.0, 0.0}, + { 1, 1, 0, 0, 0, -3389.0, 0.0, 5.0, 35.0, 0.0, -2.0}, + { 1, 0, 2, 0, 0, 3339.0, 0.0, -13.0, -107.0, 0.0, 1.0}, + {-1, 0, 2,-2, 1, -1987.0, 0.0, -6.0, 1073.0, 0.0, -2.0}, + { 1, 0, 0, 0, 2, -1981.0, 0.0, 0.0, 854.0, 0.0, 0.0}, + {-1, 0, 0, 1, 0, 4026.0, 0.0, -353.0, -553.0, 0.0, -139.0}, + { 0, 0, 2, 1, 2, 1660.0, 0.0, -5.0, -710.0, 0.0, -2.0}, + + /* 71-80 */ + {-1, 0, 2, 4, 2, -1521.0, 0.0, 9.0, 647.0, 0.0, 4.0}, + {-1, 1, 0, 1, 1, 1314.0, 0.0, 0.0, -700.0, 0.0, 0.0}, + { 0,-2, 2,-2, 1, -1283.0, 0.0, 0.0, 672.0, 0.0, 0.0}, + { 1, 0, 2, 2, 1, -1331.0, 0.0, 8.0, 663.0, 0.0, 4.0}, + {-2, 0, 2, 2, 2, 1383.0, 0.0, -2.0, -594.0, 0.0, -2.0}, + {-1, 0, 0, 0, 2, 1405.0, 0.0, 4.0, -610.0, 0.0, 2.0}, + { 1, 1, 2,-2, 2, 1290.0, 0.0, 0.0, -556.0, 0.0, 0.0}, + {-2, 0, 2, 4, 2, -1214.0, 0.0, 5.0, 518.0, 0.0, 2.0}, + {-1, 0, 4, 0, 2, 1146.0, 0.0, -3.0, -490.0, 0.0, -1.0}, + { 2, 0, 2,-2, 1, 1019.0, 0.0, -1.0, -527.0, 0.0, -1.0}, + + /* 81-90 */ + { 2, 0, 2, 2, 2, -1100.0, 0.0, 9.0, 465.0, 0.0, 4.0}, + { 1, 0, 0, 2, 1, -970.0, 0.0, 2.0, 496.0, 0.0, 1.0}, + { 3, 0, 0, 0, 0, 1575.0, 0.0, -6.0, -50.0, 0.0, 0.0}, + { 3, 0, 2,-2, 2, 934.0, 0.0, -3.0, -399.0, 0.0, -1.0}, + { 0, 0, 4,-2, 2, 922.0, 0.0, -1.0, -395.0, 0.0, -1.0}, + { 0, 1, 2, 0, 1, 815.0, 0.0, -1.0, -422.0, 0.0, -1.0}, + { 0, 0,-2, 2, 1, 834.0, 0.0, 2.0, -440.0, 0.0, 1.0}, + { 0, 0, 2,-2, 3, 1248.0, 0.0, 0.0, -170.0, 0.0, 1.0}, + {-1, 0, 0, 4, 0, 1338.0, 0.0, -5.0, -39.0, 0.0, 0.0}, + { 2, 0,-2, 0, 1, 716.0, 0.0, -2.0, -389.0, 0.0, -1.0}, + + /* 91-100 */ + {-2, 0, 0, 4, 0, 1282.0, 0.0, -3.0, -23.0, 0.0, 1.0}, + {-1,-1, 0, 2, 1, 742.0, 0.0, 1.0, -391.0, 0.0, 0.0}, + {-1, 0, 0, 1, 1, 1020.0, 0.0, -25.0, -495.0, 0.0, -10.0}, + { 0, 1, 0, 0, 2, 715.0, 0.0, -4.0, -326.0, 0.0, 2.0}, + { 0, 0,-2, 0, 1, -666.0, 0.0, -3.0, 369.0, 0.0, -1.0}, + { 0,-1, 2, 0, 1, -667.0, 0.0, 1.0, 346.0, 0.0, 1.0}, + { 0, 0, 2,-1, 2, -704.0, 0.0, 0.0, 304.0, 0.0, 0.0}, + { 0, 0, 2, 4, 2, -694.0, 0.0, 5.0, 294.0, 0.0, 2.0}, + {-2,-1, 0, 2, 0, -1014.0, 0.0, -1.0, 4.0, 0.0, -1.0}, + { 1, 1, 0,-2, 1, -585.0, 0.0, -2.0, 316.0, 0.0, -1.0}, + + /* 101-110 */ + {-1, 1, 0, 2, 0, -949.0, 0.0, 1.0, 8.0, 0.0, -1.0}, + {-1, 1, 0, 1, 2, -595.0, 0.0, 0.0, 258.0, 0.0, 0.0}, + { 1,-1, 0, 0, 1, 528.0, 0.0, 0.0, -279.0, 0.0, 0.0}, + { 1,-1, 2, 2, 2, -590.0, 0.0, 4.0, 252.0, 0.0, 2.0}, + {-1, 1, 2, 2, 2, 570.0, 0.0, -2.0, -244.0, 0.0, -1.0}, + { 3, 0, 2, 0, 1, -502.0, 0.0, 3.0, 250.0, 0.0, 2.0}, + { 0, 1,-2, 2, 0, -875.0, 0.0, 1.0, 29.0, 0.0, 0.0}, + {-1, 0, 0,-2, 1, -492.0, 0.0, -3.0, 275.0, 0.0, -1.0}, + { 0, 1, 2, 2, 2, 535.0, 0.0, -2.0, -228.0, 0.0, -1.0}, + {-1,-1, 2, 2, 1, -467.0, 0.0, 1.0, 240.0, 0.0, 1.0}, + + /* 111-120 */ + { 0,-1, 0, 0, 2, 591.0, 0.0, 0.0, -253.0, 0.0, 0.0}, + { 1, 0, 2,-4, 1, -453.0, 0.0, -1.0, 244.0, 0.0, -1.0}, + {-1, 0,-2, 2, 0, 766.0, 0.0, 1.0, 9.0, 0.0, 0.0}, + { 0,-1, 2, 2, 1, -446.0, 0.0, 2.0, 225.0, 0.0, 1.0}, + { 2,-1, 2, 0, 2, -488.0, 0.0, 2.0, 207.0, 0.0, 1.0}, + { 0, 0, 0, 2, 2, -468.0, 0.0, 0.0, 201.0, 0.0, 0.0}, + { 1,-1, 2, 0, 1, -421.0, 0.0, 1.0, 216.0, 0.0, 1.0}, + {-1, 1, 2, 0, 2, 463.0, 0.0, 0.0, -200.0, 0.0, 0.0}, + { 0, 1, 0, 2, 0, -673.0, 0.0, 2.0, 14.0, 0.0, 0.0}, + { 0,-1,-2, 2, 0, 658.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + + /* 121-130 */ + { 0, 3, 2,-2, 2, -438.0, 0.0, 0.0, 188.0, 0.0, 0.0}, + { 0, 0, 0, 1, 1, -390.0, 0.0, 0.0, 205.0, 0.0, 0.0}, + {-1, 0, 2, 2, 0, 639.0, -11.0, -2.0, -19.0, 0.0, 0.0}, + { 2, 1, 2, 0, 2, 412.0, 0.0, -2.0, -176.0, 0.0, -1.0}, + { 1, 1, 0, 0, 1, -361.0, 0.0, 0.0, 189.0, 0.0, 0.0}, + { 1, 1, 2, 0, 1, 360.0, 0.0, -1.0, -185.0, 0.0, -1.0}, + { 2, 0, 0, 2, 0, 588.0, 0.0, -3.0, -24.0, 0.0, 0.0}, + { 1, 0,-2, 2, 0, -578.0, 0.0, 1.0, 5.0, 0.0, 0.0}, + {-1, 0, 0, 2, 2, -396.0, 0.0, 0.0, 171.0, 0.0, 0.0}, + { 0, 1, 0, 1, 0, 565.0, 0.0, -1.0, -6.0, 0.0, 0.0}, + + /* 131-140 */ + { 0, 1, 0,-2, 1, -335.0, 0.0, -1.0, 184.0, 0.0, -1.0}, + {-1, 0, 2,-2, 2, 357.0, 0.0, 1.0, -154.0, 0.0, 0.0}, + { 0, 0, 0,-1, 1, 321.0, 0.0, 1.0, -174.0, 0.0, 0.0}, + {-1, 1, 0, 0, 1, -301.0, 0.0, -1.0, 162.0, 0.0, 0.0}, + { 1, 0, 2,-1, 2, -334.0, 0.0, 0.0, 144.0, 0.0, 0.0}, + { 1,-1, 0, 2, 0, 493.0, 0.0, -2.0, -15.0, 0.0, 0.0}, + { 0, 0, 0, 4, 0, 494.0, 0.0, -2.0, -19.0, 0.0, 0.0}, + { 1, 0, 2, 1, 2, 337.0, 0.0, -1.0, -143.0, 0.0, -1.0}, + { 0, 0, 2, 1, 1, 280.0, 0.0, -1.0, -144.0, 0.0, 0.0}, + { 1, 0, 0,-2, 2, 309.0, 0.0, 1.0, -134.0, 0.0, 0.0}, + + /* 141-150 */ + {-1, 0, 2, 4, 1, -263.0, 0.0, 2.0, 131.0, 0.0, 1.0}, + { 1, 0,-2, 0, 1, 253.0, 0.0, 1.0, -138.0, 0.0, 0.0}, + { 1, 1, 2,-2, 1, 245.0, 0.0, 0.0, -128.0, 0.0, 0.0}, + { 0, 0, 2, 2, 0, 416.0, 0.0, -2.0, -17.0, 0.0, 0.0}, + {-1, 0, 2,-1, 1, -229.0, 0.0, 0.0, 128.0, 0.0, 0.0}, + {-2, 0, 2, 2, 1, 231.0, 0.0, 0.0, -120.0, 0.0, 0.0}, + { 4, 0, 2, 0, 2, -259.0, 0.0, 2.0, 109.0, 0.0, 1.0}, + { 2,-1, 0, 0, 0, 375.0, 0.0, -1.0, -8.0, 0.0, 0.0}, + { 2, 1, 2,-2, 2, 252.0, 0.0, 0.0, -108.0, 0.0, 0.0}, + { 0, 1, 2, 1, 2, -245.0, 0.0, 1.0, 104.0, 0.0, 0.0}, + + /* 151-160 */ + { 1, 0, 4,-2, 2, 243.0, 0.0, -1.0, -104.0, 0.0, 0.0}, + {-1,-1, 0, 0, 1, 208.0, 0.0, 1.0, -112.0, 0.0, 0.0}, + { 0, 1, 0, 2, 1, 199.0, 0.0, 0.0, -102.0, 0.0, 0.0}, + {-2, 0, 2, 4, 1, -208.0, 0.0, 1.0, 105.0, 0.0, 0.0}, + { 2, 0, 2, 0, 0, 335.0, 0.0, -2.0, -14.0, 0.0, 0.0}, + { 1, 0, 0, 1, 0, -325.0, 0.0, 1.0, 7.0, 0.0, 0.0}, + {-1, 0, 0, 4, 1, -187.0, 0.0, 0.0, 96.0, 0.0, 0.0}, + {-1, 0, 4, 0, 1, 197.0, 0.0, -1.0, -100.0, 0.0, 0.0}, + { 2, 0, 2, 2, 1, -192.0, 0.0, 2.0, 94.0, 0.0, 1.0}, + { 0, 0, 2,-3, 2, -188.0, 0.0, 0.0, 83.0, 0.0, 0.0}, + + /* 161-170 */ + {-1,-2, 0, 2, 0, 276.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 2, 1, 0, 0, 0, -286.0, 0.0, 1.0, 6.0, 0.0, 0.0}, + { 0, 0, 4, 0, 2, 186.0, 0.0, -1.0, -79.0, 0.0, 0.0}, + { 0, 0, 0, 0, 3, -219.0, 0.0, 0.0, 43.0, 0.0, 0.0}, + { 0, 3, 0, 0, 0, 276.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0, 0, 2,-4, 1, -153.0, 0.0, -1.0, 84.0, 0.0, 0.0}, + { 0,-1, 0, 2, 1, -156.0, 0.0, 0.0, 81.0, 0.0, 0.0}, + { 0, 0, 0, 4, 1, -154.0, 0.0, 1.0, 78.0, 0.0, 0.0}, + {-1,-1, 2, 4, 2, -174.0, 0.0, 1.0, 75.0, 0.0, 0.0}, + { 1, 0, 2, 4, 2, -163.0, 0.0, 2.0, 69.0, 0.0, 1.0}, + + /* 171-180 */ + {-2, 2, 0, 2, 0, -228.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {-2,-1, 2, 0, 1, 91.0, 0.0, -4.0, -54.0, 0.0, -2.0}, + {-2, 0, 0, 2, 2, 175.0, 0.0, 0.0, -75.0, 0.0, 0.0}, + {-1,-1, 2, 0, 2, -159.0, 0.0, 0.0, 69.0, 0.0, 0.0}, + { 0, 0, 4,-2, 1, 141.0, 0.0, 0.0, -72.0, 0.0, 0.0}, + { 3, 0, 2,-2, 1, 147.0, 0.0, 0.0, -75.0, 0.0, 0.0}, + {-2,-1, 0, 2, 1, -132.0, 0.0, 0.0, 69.0, 0.0, 0.0}, + { 1, 0, 0,-1, 1, 159.0, 0.0, -28.0, -54.0, 0.0, 11.0}, + { 0,-2, 0, 2, 0, 213.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {-2, 0, 0, 4, 1, 123.0, 0.0, 0.0, -64.0, 0.0, 0.0}, + + /* 181-190 */ + {-3, 0, 0, 0, 1, -118.0, 0.0, -1.0, 66.0, 0.0, 0.0}, + { 1, 1, 2, 2, 2, 144.0, 0.0, -1.0, -61.0, 0.0, 0.0}, + { 0, 0, 2, 4, 1, -121.0, 0.0, 1.0, 60.0, 0.0, 0.0}, + { 3, 0, 2, 2, 2, -134.0, 0.0, 1.0, 56.0, 0.0, 1.0}, + {-1, 1, 2,-2, 1, -105.0, 0.0, 0.0, 57.0, 0.0, 0.0}, + { 2, 0, 0,-4, 1, -102.0, 0.0, 0.0, 56.0, 0.0, 0.0}, + { 0, 0, 0,-2, 2, 120.0, 0.0, 0.0, -52.0, 0.0, 0.0}, + { 2, 0, 2,-4, 1, 101.0, 0.0, 0.0, -54.0, 0.0, 0.0}, + {-1, 1, 0, 2, 1, -113.0, 0.0, 0.0, 59.0, 0.0, 0.0}, + { 0, 0, 2,-1, 1, -106.0, 0.0, 0.0, 61.0, 0.0, 0.0}, + + /* 191-200 */ + { 0,-2, 2, 2, 2, -129.0, 0.0, 1.0, 55.0, 0.0, 0.0}, + { 2, 0, 0, 2, 1, -114.0, 0.0, 0.0, 57.0, 0.0, 0.0}, + { 4, 0, 2,-2, 2, 113.0, 0.0, -1.0, -49.0, 0.0, 0.0}, + { 2, 0, 0,-2, 2, -102.0, 0.0, 0.0, 44.0, 0.0, 0.0}, + { 0, 2, 0, 0, 1, -94.0, 0.0, 0.0, 51.0, 0.0, 0.0}, + { 1, 0, 0,-4, 1, -100.0, 0.0, -1.0, 56.0, 0.0, 0.0}, + { 0, 2, 2,-2, 1, 87.0, 0.0, 0.0, -47.0, 0.0, 0.0}, + {-3, 0, 0, 4, 0, 161.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-1, 1, 2, 0, 1, 96.0, 0.0, 0.0, -50.0, 0.0, 0.0}, + {-1,-1, 0, 4, 0, 151.0, 0.0, -1.0, -5.0, 0.0, 0.0}, + + /* 201-210 */ + {-1,-2, 2, 2, 2, -104.0, 0.0, 0.0, 44.0, 0.0, 0.0}, + {-2,-1, 2, 4, 2, -110.0, 0.0, 0.0, 48.0, 0.0, 0.0}, + { 1,-1, 2, 2, 1, -100.0, 0.0, 1.0, 50.0, 0.0, 0.0}, + {-2, 1, 0, 2, 0, 92.0, 0.0, -5.0, 12.0, 0.0, -2.0}, + {-2, 1, 2, 0, 1, 82.0, 0.0, 0.0, -45.0, 0.0, 0.0}, + { 2, 1, 0,-2, 1, 82.0, 0.0, 0.0, -45.0, 0.0, 0.0}, + {-3, 0, 2, 0, 1, -78.0, 0.0, 0.0, 41.0, 0.0, 0.0}, + {-2, 0, 2,-2, 1, -77.0, 0.0, 0.0, 43.0, 0.0, 0.0}, + {-1, 1, 0, 2, 2, 2.0, 0.0, 0.0, 54.0, 0.0, 0.0}, + { 0,-1, 2,-1, 2, 94.0, 0.0, 0.0, -40.0, 0.0, 0.0}, + + /* 211-220 */ + {-1, 0, 4,-2, 2, -93.0, 0.0, 0.0, 40.0, 0.0, 0.0}, + { 0,-2, 2, 0, 2, -83.0, 0.0, 10.0, 40.0, 0.0, -2.0}, + {-1, 0, 2, 1, 2, 83.0, 0.0, 0.0, -36.0, 0.0, 0.0}, + { 2, 0, 0, 0, 2, -91.0, 0.0, 0.0, 39.0, 0.0, 0.0}, + { 0, 0, 2, 0, 3, 128.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-2, 0, 4, 0, 2, -79.0, 0.0, 0.0, 34.0, 0.0, 0.0}, + {-1, 0,-2, 0, 1, -83.0, 0.0, 0.0, 47.0, 0.0, 0.0}, + {-1, 1, 2, 2, 1, 84.0, 0.0, 0.0, -44.0, 0.0, 0.0}, + { 3, 0, 0, 0, 1, 83.0, 0.0, 0.0, -43.0, 0.0, 0.0}, + {-1, 0, 2, 3, 2, 91.0, 0.0, 0.0, -39.0, 0.0, 0.0}, + + /* 221-230 */ + { 2,-1, 2, 0, 1, -77.0, 0.0, 0.0, 39.0, 0.0, 0.0}, + { 0, 1, 2, 2, 1, 84.0, 0.0, 0.0, -43.0, 0.0, 0.0}, + { 0,-1, 2, 4, 2, -92.0, 0.0, 1.0, 39.0, 0.0, 0.0}, + { 2,-1, 2, 2, 2, -92.0, 0.0, 1.0, 39.0, 0.0, 0.0}, + { 0, 2,-2, 2, 0, -94.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 2,-1, 1, 68.0, 0.0, 0.0, -36.0, 0.0, 0.0}, + { 0,-2, 0, 0, 1, -61.0, 0.0, 0.0, 32.0, 0.0, 0.0}, + { 1, 0, 2,-4, 2, 71.0, 0.0, 0.0, -31.0, 0.0, 0.0}, + { 1,-1, 0,-2, 1, 62.0, 0.0, 0.0, -34.0, 0.0, 0.0}, + {-1,-1, 2, 0, 1, -63.0, 0.0, 0.0, 33.0, 0.0, 0.0}, + + /* 231-240 */ + { 1,-1, 2,-2, 2, -73.0, 0.0, 0.0, 32.0, 0.0, 0.0}, + {-2,-1, 0, 4, 0, 115.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-1, 0, 0, 3, 0, -103.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-2,-1, 2, 2, 2, 63.0, 0.0, 0.0, -28.0, 0.0, 0.0}, + { 0, 2, 2, 0, 2, 74.0, 0.0, 0.0, -32.0, 0.0, 0.0}, + { 1, 1, 0, 2, 0, -103.0, 0.0, -3.0, 3.0, 0.0, -1.0}, + { 2, 0, 2,-1, 2, -69.0, 0.0, 0.0, 30.0, 0.0, 0.0}, + { 1, 0, 2, 1, 1, 57.0, 0.0, 0.0, -29.0, 0.0, 0.0}, + { 4, 0, 0, 0, 0, 94.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + { 2, 1, 2, 0, 1, 64.0, 0.0, 0.0, -33.0, 0.0, 0.0}, + + /* 241-250 */ + { 3,-1, 2, 0, 2, -63.0, 0.0, 0.0, 26.0, 0.0, 0.0}, + {-2, 2, 0, 2, 1, -38.0, 0.0, 0.0, 20.0, 0.0, 0.0}, + { 1, 0, 2,-3, 1, -43.0, 0.0, 0.0, 24.0, 0.0, 0.0}, + { 1, 1, 2,-4, 1, -45.0, 0.0, 0.0, 23.0, 0.0, 0.0}, + {-1,-1, 2,-2, 1, 47.0, 0.0, 0.0, -24.0, 0.0, 0.0}, + { 0,-1, 0,-1, 1, -48.0, 0.0, 0.0, 25.0, 0.0, 0.0}, + { 0,-1, 0,-2, 1, 45.0, 0.0, 0.0, -26.0, 0.0, 0.0}, + {-2, 0, 0, 0, 2, 56.0, 0.0, 0.0, -25.0, 0.0, 0.0}, + {-2, 0,-2, 2, 0, 88.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1, 0,-2, 4, 0, -75.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 251-260 */ + { 1,-2, 0, 0, 0, 85.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 0, 1, 1, 49.0, 0.0, 0.0, -26.0, 0.0, 0.0}, + {-1, 2, 0, 2, 0, -74.0, 0.0, -3.0, -1.0, 0.0, -1.0}, + { 1,-1, 2,-2, 1, -39.0, 0.0, 0.0, 21.0, 0.0, 0.0}, + { 1, 2, 2,-2, 2, 45.0, 0.0, 0.0, -20.0, 0.0, 0.0}, + { 2,-1, 2,-2, 2, 51.0, 0.0, 0.0, -22.0, 0.0, 0.0}, + { 1, 0, 2,-1, 1, -40.0, 0.0, 0.0, 21.0, 0.0, 0.0}, + { 2, 1, 2,-2, 1, 41.0, 0.0, 0.0, -21.0, 0.0, 0.0}, + {-2, 0, 0,-2, 1, -42.0, 0.0, 0.0, 24.0, 0.0, 0.0}, + { 1,-2, 2, 0, 2, -51.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + + /* 261-270 */ + { 0, 1, 2, 1, 1, -42.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + { 1, 0, 4,-2, 1, 39.0, 0.0, 0.0, -21.0, 0.0, 0.0}, + {-2, 0, 4, 2, 2, 46.0, 0.0, 0.0, -18.0, 0.0, 0.0}, + { 1, 1, 2, 1, 2, -53.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + { 1, 0, 0, 4, 0, 82.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + { 1, 0, 2, 2, 0, 81.0, 0.0, -1.0, -4.0, 0.0, 0.0}, + { 2, 0, 2, 1, 2, 47.0, 0.0, 0.0, -19.0, 0.0, 0.0}, + { 3, 1, 2, 0, 2, 53.0, 0.0, 0.0, -23.0, 0.0, 0.0}, + { 4, 0, 2, 0, 1, -45.0, 0.0, 0.0, 22.0, 0.0, 0.0}, + {-2,-1, 2, 0, 0, -44.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + + /* 271-280 */ + { 0, 1,-2, 2, 1, -33.0, 0.0, 0.0, 16.0, 0.0, 0.0}, + { 1, 0,-2, 1, 0, -61.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 0,-1,-2, 2, 1, 28.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + { 2,-1, 0,-2, 1, -38.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + {-1, 0, 2,-1, 2, -33.0, 0.0, 0.0, 21.0, 0.0, 0.0}, + { 1, 0, 2,-3, 2, -60.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 2,-2, 3, 48.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + { 0, 0, 2,-3, 1, 27.0, 0.0, 0.0, -14.0, 0.0, 0.0}, + {-1, 0,-2, 2, 1, 38.0, 0.0, 0.0, -20.0, 0.0, 0.0}, + { 0, 0, 2,-4, 2, 31.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + + /* 281-290 */ + {-2, 1, 0, 0, 1, -29.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + {-1, 0, 0,-1, 1, 28.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + { 2, 0, 2,-4, 2, -32.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + { 0, 0, 4,-4, 4, 45.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + { 0, 0, 4,-4, 2, -44.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + {-1,-2, 0, 2, 1, 28.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + {-2, 0, 0, 3, 0, -51.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0,-2, 2, 1, -36.0, 0.0, 0.0, 20.0, 0.0, 0.0}, + {-3, 0, 2, 2, 2, 44.0, 0.0, 0.0, -19.0, 0.0, 0.0}, + {-3, 0, 2, 2, 1, 26.0, 0.0, 0.0, -14.0, 0.0, 0.0}, + + /* 291-300 */ + {-2, 0, 2, 2, 0, -60.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 2,-1, 0, 0, 1, 35.0, 0.0, 0.0, -18.0, 0.0, 0.0}, + {-2, 1, 2, 2, 2, -27.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + { 1, 1, 0, 1, 0, 47.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 0, 1, 4,-2, 2, 36.0, 0.0, 0.0, -15.0, 0.0, 0.0}, + {-1, 1, 0,-2, 1, -36.0, 0.0, 0.0, 20.0, 0.0, 0.0}, + { 0, 0, 0,-4, 1, -35.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + { 1,-1, 0, 2, 1, -37.0, 0.0, 0.0, 19.0, 0.0, 0.0}, + { 1, 1, 0, 2, 1, 32.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + {-1, 2, 2, 2, 2, 35.0, 0.0, 0.0, -14.0, 0.0, 0.0}, + + /* 301-310 */ + { 3, 1, 2,-2, 2, 32.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + { 0,-1, 0, 4, 0, 65.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 2,-1, 0, 2, 0, 47.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 0, 0, 4, 0, 1, 32.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + { 2, 0, 4,-2, 2, 37.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + {-1,-1, 2, 4, 1, -30.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + { 1, 0, 0, 4, 1, -32.0, 0.0, 0.0, 16.0, 0.0, 0.0}, + { 1,-2, 2, 2, 2, -31.0, 0.0, 0.0, 13.0, 0.0, 0.0}, + { 0, 0, 2, 3, 2, 37.0, 0.0, 0.0, -16.0, 0.0, 0.0}, + {-1, 1, 2, 4, 2, 31.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + + /* 311-320 */ + { 3, 0, 0, 2, 0, 49.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-1, 0, 4, 2, 2, 32.0, 0.0, 0.0, -13.0, 0.0, 0.0}, + { 1, 1, 2, 2, 1, 23.0, 0.0, 0.0, -12.0, 0.0, 0.0}, + {-2, 0, 2, 6, 2, -43.0, 0.0, 0.0, 18.0, 0.0, 0.0}, + { 2, 1, 2, 2, 2, 26.0, 0.0, 0.0, -11.0, 0.0, 0.0}, + {-1, 0, 2, 6, 2, -32.0, 0.0, 0.0, 14.0, 0.0, 0.0}, + { 1, 0, 2, 4, 1, -29.0, 0.0, 0.0, 14.0, 0.0, 0.0}, + { 2, 0, 2, 4, 2, -27.0, 0.0, 0.0, 12.0, 0.0, 0.0}, + { 1, 1,-2, 1, 0, 30.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-3, 1, 2, 1, 2, -11.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + + /* 321-330 */ + { 2, 0,-2, 0, 2, -21.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + {-1, 0, 0, 1, 2, -34.0, 0.0, 0.0, 15.0, 0.0, 0.0}, + {-4, 0, 2, 2, 1, -10.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {-1,-1, 0, 1, 0, -36.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 0,-2, 2, 2, -9.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + { 1, 0, 0,-1, 2, -12.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + { 0,-1, 2,-2, 3, -21.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + {-2, 1, 2, 0, 0, -29.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 0, 0, 2,-2, 4, -15.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-2,-2, 0, 2, 0, -20.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 331-340 */ + {-2, 0,-2, 4, 0, 28.0, 0.0, 0.0, 0.0, 0.0, -2.0}, + { 0,-2,-2, 2, 0, 17.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 2, 0,-2, 1, -22.0, 0.0, 0.0, 12.0, 0.0, 0.0}, + { 3, 0, 0,-4, 1, -14.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + {-1, 1, 2,-2, 2, 24.0, 0.0, 0.0, -11.0, 0.0, 0.0}, + { 1,-1, 2,-4, 1, 11.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + { 1, 1, 0,-2, 2, 14.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {-3, 0, 2, 0, 0, 24.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-3, 0, 2, 0, 2, 18.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + {-2, 0, 0, 1, 0, -38.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 341-350 */ + { 0, 0,-2, 1, 0, -31.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-3, 0, 0, 2, 1, -16.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {-1,-1,-2, 2, 0, 29.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 2,-4, 1, -18.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + { 2, 1, 0,-4, 1, -10.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + { 0, 2, 0,-2, 1, -17.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + { 1, 0, 0,-3, 1, 9.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {-2, 0, 2,-2, 2, 16.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {-2,-1, 0, 0, 1, 22.0, 0.0, 0.0, -12.0, 0.0, 0.0}, + {-4, 0, 0, 2, 0, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 351-360 */ + { 1, 1, 0,-4, 1, -13.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {-1, 0, 2,-4, 1, -17.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + { 0, 0, 4,-4, 1, -14.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + { 0, 3, 2,-2, 2, 0.0, 0.0, 0.0, -7.0, 0.0, 0.0}, + {-3,-1, 0, 4, 0, 14.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-3, 0, 0, 4, 1, 19.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + { 1,-1,-2, 2, 0, -34.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 0, 2, 2, -20.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + { 1,-2, 0, 0, 1, 9.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + { 1,-1, 0, 0, 2, -18.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + + /* 361-370 */ + { 0, 0, 0, 1, 2, 13.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + {-1,-1, 2, 0, 0, 17.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1,-2, 2,-2, 2, -12.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + { 0,-1, 2,-1, 1, 15.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + {-1, 0, 2, 0, 3, -11.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 1, 1, 0, 0, 2, 13.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + {-1, 1, 2, 0, 0, -18.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 2, 0, 0, 0, -35.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 2, 2, 0, 2, 9.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {-1, 0, 4,-2, 1, -19.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + + /* 371-380 */ + { 3, 0, 2,-4, 2, -26.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + { 1, 2, 2,-2, 1, 8.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + { 1, 0, 4,-4, 2, -10.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {-2,-1, 0, 4, 1, 10.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + { 0,-1, 0, 2, 2, -21.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {-2, 1, 0, 4, 0, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2,-1, 2, 2, 1, 9.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + { 2, 0,-2, 2, 0, -29.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0, 0, 1, 1, -19.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + { 0, 1, 0, 2, 2, 12.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + + /* 381-390 */ + { 1,-1, 2,-1, 2, 22.0, 0.0, 0.0, -9.0, 0.0, 0.0}, + {-2, 0, 4, 0, 1, -10.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + { 2, 1, 0, 0, 1, -20.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + { 0, 1, 2, 0, 0, -20.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0,-1, 4,-2, 2, -17.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + { 0, 0, 4,-2, 4, 15.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 0, 2, 2, 0, 1, 8.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {-3, 0, 0, 6, 0, 14.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 0, 4, 1, -12.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + { 1,-2, 0, 2, 0, 25.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 391-400 */ + {-1, 0, 0, 4, 2, -13.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {-1,-2, 2, 2, 1, -14.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {-1, 0, 0,-2, 2, 13.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + { 1, 0,-2,-2, 1, -17.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + { 0, 0,-2,-2, 1, -12.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + {-2, 0,-2, 0, 1, -10.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + { 0, 0, 0, 3, 1, 10.0, 0.0, 0.0, -6.0, 0.0, 0.0}, + { 0, 0, 0, 3, 0, -15.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 1, 0, 4, 0, -22.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 2, 2, 0, 28.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + + /* 401-410 */ + {-2, 0, 2, 3, 2, 15.0, 0.0, 0.0, -7.0, 0.0, 0.0}, + { 1, 0, 0, 2, 2, 23.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + { 0,-1, 2, 1, 2, 12.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + { 3,-1, 0, 0, 0, 29.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 2, 0, 0, 1, 0, -25.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 1,-1, 2, 0, 0, 22.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 0, 2, 1, 0, -18.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0, 2, 0, 3, 15.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 3, 1, 0, 0, 0, -23.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 3,-1, 2,-2, 2, 12.0, 0.0, 0.0, -5.0, 0.0, 0.0}, + + /* 411-420 */ + { 2, 0, 2,-1, 1, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + { 1, 1, 2, 0, 0, -19.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 0, 4,-1, 2, -10.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + { 1, 2, 2, 0, 2, 21.0, 0.0, 0.0, -9.0, 0.0, 0.0}, + {-2, 0, 0, 6, 0, 23.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 0,-1, 0, 4, 1, -16.0, 0.0, 0.0, 8.0, 0.0, 0.0}, + {-2,-1, 2, 4, 1, -19.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + { 0,-2, 2, 2, 1, -22.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + { 0,-1, 2, 2, 0, 27.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-1, 0, 2, 3, 1, 16.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + + /* 421-430 */ + {-2, 1, 2, 4, 2, 19.0, 0.0, 0.0, -8.0, 0.0, 0.0}, + { 2, 0, 0, 2, 2, 9.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + { 2,-2, 2, 0, 2, -9.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {-1, 1, 2, 3, 2, -9.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + { 3, 0, 2,-1, 2, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + { 4, 0, 2,-2, 1, 18.0, 0.0, 0.0, -9.0, 0.0, 0.0}, + {-1, 0, 0, 6, 0, 16.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-1,-2, 2, 4, 2, -10.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {-3, 0, 2, 6, 2, -23.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + {-1, 0, 2, 4, 0, 16.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + + /* 431-440 */ + { 3, 0, 0, 2, 1, -12.0, 0.0, 0.0, 6.0, 0.0, 0.0}, + { 3,-1, 2, 0, 1, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + { 3, 0, 2, 0, 0, 30.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 1, 0, 4, 0, 2, 24.0, 0.0, 0.0, -10.0, 0.0, 0.0}, + { 5, 0, 2,-2, 2, 10.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + { 0,-1, 2, 4, 1, -16.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + { 2,-1, 2, 2, 1, -16.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + { 0, 1, 2, 4, 2, 17.0, 0.0, 0.0, -7.0, 0.0, 0.0}, + { 1,-1, 2, 4, 2, -24.0, 0.0, 0.0, 10.0, 0.0, 0.0}, + { 3,-1, 2, 2, 2, -12.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + + /* 441-450 */ + { 3, 0, 2, 2, 1, -24.0, 0.0, 0.0, 11.0, 0.0, 0.0}, + { 5, 0, 2, 0, 2, -23.0, 0.0, 0.0, 9.0, 0.0, 0.0}, + { 0, 0, 2, 6, 2, -13.0, 0.0, 0.0, 5.0, 0.0, 0.0}, + { 4, 0, 2, 2, 2, -15.0, 0.0, 0.0, 7.0, 0.0, 0.0}, + { 0,-1, 1,-1, 1, 0.0, 0.0,-1988.0, 0.0, 0.0,-1679.0}, + {-1, 0, 1, 0, 3, 0.0, 0.0, -63.0, 0.0, 0.0, -27.0}, + { 0,-2, 2,-2, 3, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0,-1, 0, 1, 0.0, 0.0, 5.0, 0.0, 0.0, 4.0}, + { 2,-2, 0,-2, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {-1, 0, 1, 0, 2, 0.0, 0.0, 364.0, 0.0, 0.0, 176.0}, + + /* 451-460 */ + {-1, 0, 1, 0, 1, 0.0, 0.0,-1044.0, 0.0, 0.0, -891.0}, + {-1,-1, 2,-1, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {-2, 2, 0, 2, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-1, 0, 1, 0, 0, 0.0, 0.0, 330.0, 0.0, 0.0, 0.0}, + {-4, 1, 2, 2, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-3, 0, 2, 1, 1, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-2,-1, 2, 0, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 1, 0,-2, 1, 1, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 2,-1,-2, 0, 1, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-4, 0, 2, 2, 0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 461-470 */ + {-3, 1, 0, 3, 0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 0,-1, 2, 0, 0.0, 0.0, 5.0, 0.0, 0.0, 0.0}, + { 0,-2, 0, 0, 2, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 0,-2, 0, 0, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-3, 0, 0, 3, 0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2,-1, 0, 2, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-1, 0,-2, 3, 0, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-4, 0, 0, 4, 0, -12.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2, 1,-2, 0, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 2,-1, 0,-2, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + + /* 471-480 */ + { 0, 0, 1,-1, 0, -5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 2, 0, 1, 0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2, 1, 2, 0, 2, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 1, 1, 0,-1, 1, 7.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + { 1, 0, 1,-2, 1, 0.0, 0.0, -12.0, 0.0, 0.0, -10.0}, + { 0, 2, 0, 0, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 1,-1, 2,-3, 1, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-1, 1, 2,-1, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-2, 0, 4,-2, 2, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-2, 0, 4,-2, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + + /* 481-490 */ + {-2,-2, 0, 2, 1, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {-2, 0,-2, 4, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 2, 2,-4, 1, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 1, 1, 2,-4, 2, 7.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {-1, 2, 2,-2, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 2, 0, 0,-3, 1, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-1, 2, 0, 0, 1, -5.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 0, 0, 0,-2, 0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 2,-2, 2, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1, 1, 0, 0, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + + /* 491-500 */ + { 0, 0, 0,-1, 2, -8.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-2, 1, 0, 1, 0, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1,-2, 0,-2, 1, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 1, 0,-2, 0, 2, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-3, 1, 0, 2, 0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 1,-2, 2, 0, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 0, 0, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {-3, 0, 0, 2, 0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-3,-1, 0, 2, 0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2, 0, 2,-6, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + + /* 501-510 */ + { 0, 1, 2,-4, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 2, 0, 0,-4, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-2, 1, 2,-2, 1, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0,-1, 2,-4, 1, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 0, 1, 0,-2, 2, 9.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {-1, 0, 0,-2, 0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2, 0,-2,-2, 1, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-4, 0, 2, 0, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1,-1, 0,-1, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0, 0,-2, 0, 2, 9.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + + /* 511-520 */ + {-3, 0, 0, 1, 0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 0,-2, 1, 0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2, 0,-2, 2, 1, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 0, 0,-4, 2, 0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2,-1,-2, 2, 0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0, 2,-6, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1, 0, 2,-4, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 1, 0, 0,-4, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 2, 1, 2,-4, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 2, 1, 2,-4, 1, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + + /* 521-530 */ + { 0, 1, 4,-4, 4, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 4,-4, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {-1,-1,-2, 4, 0, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-3, 0, 2, 0, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 0,-2, 4, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-2,-1, 0, 3, 0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 0,-2, 3, 0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2, 0, 0, 3, 1, -5.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 0,-1, 0, 1, 0, -13.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-3, 0, 2, 2, 0, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 531-540 */ + { 1, 1,-2, 2, 0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 1, 0, 2, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 1,-2, 2,-2, 1, 10.0, 0.0, 13.0, 6.0, 0.0, -5.0}, + { 0, 0, 1, 0, 2, 0.0, 0.0, 30.0, 0.0, 0.0, 14.0}, + { 0, 0, 1, 0, 1, 0.0, 0.0, -162.0, 0.0, 0.0, -138.0}, + { 0, 0, 1, 0, 0, 0.0, 0.0, 75.0, 0.0, 0.0, 0.0}, + {-1, 2, 0, 2, 1, -7.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + { 0, 0, 2, 0, 2, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-2, 0, 2, 0, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 2, 0, 0,-1, 1, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + + /* 541-550 */ + { 3, 0, 0,-2, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 1, 0, 2,-2, 3, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 2, 0, 0, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 2, 0, 2,-3, 2, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1, 1, 4,-2, 2, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-2,-2, 0, 4, 0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0,-3, 0, 2, 0, 9.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 0,-2, 4, 0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 0, 3, 0, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2, 0, 0, 4, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + + /* 551-560 */ + {-1, 0, 0, 3, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 2,-2, 0, 0, 0, 7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1,-1, 0, 1, 0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 0, 0, 2, 0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0,-2, 2, 0, 1, -6.0, 0.0, -3.0, 3.0, 0.0, 1.0}, + {-1, 0, 1, 2, 1, 0.0, 0.0, -3.0, 0.0, 0.0, -2.0}, + {-1, 1, 0, 3, 0, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-1, 2, 1, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 0,-1, 2, 0, 0, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2, 1, 2, 2, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + + /* 561-570 */ + { 2,-2, 2,-2, 2, -1.0, 0.0, 3.0, 3.0, 0.0, -1.0}, + { 1, 1, 0, 1, 1, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 1, 0, 1, 0, 1, 0.0, 0.0, -13.0, 0.0, 0.0, -11.0}, + { 1, 0, 1, 0, 0, 3.0, 0.0, 6.0, 0.0, 0.0, 0.0}, + { 0, 2, 0, 2, 0, -7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2,-1, 2,-2, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 0,-1, 4,-2, 1, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 0, 0, 4,-2, 3, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 4,-2, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 4, 0, 2,-4, 2, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + + /* 571-580 */ + { 2, 2, 2,-2, 2, 8.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 2, 0, 4,-4, 2, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1,-2, 0, 4, 0, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1,-3, 2, 2, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {-3, 0, 2, 4, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-3, 0, 2,-2, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1,-1, 0,-2, 1, 8.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + {-3, 0, 0, 0, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-3, 0,-2, 2, 0, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 0,-4, 1, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + + /* 581-590 */ + {-2, 1, 0,-2, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-4, 0, 0, 0, 1, -8.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + {-1, 0, 0,-4, 1, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-3, 0, 0,-2, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0, 0, 0, 3, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-1, 1, 0, 4, 1, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 1,-2, 2, 0, 1, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 0, 1, 0, 3, 0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-1, 0, 2, 2, 3, 6.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 0, 0, 2, 2, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + + /* 591-600 */ + {-2, 0, 2, 2, 2, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1, 1, 2, 2, 0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 3, 0, 0, 0, 2, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 2, 1, 0, 1, 0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2,-1, 2,-1, 2, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 0, 0, 2, 0, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0, 0, 3, 0, 3, 0.0, 0.0, -26.0, 0.0, 0.0, -11.0}, + { 0, 0, 3, 0, 2, 0.0, 0.0, -10.0, 0.0, 0.0, -5.0}, + {-1, 2, 2, 2, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {-1, 0, 4, 0, 0, -13.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 601-610 */ + { 1, 2, 2, 0, 1, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 3, 1, 2,-2, 1, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 1, 1, 4,-2, 2, 7.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {-2,-1, 0, 6, 0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0,-2, 0, 4, 0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-2, 0, 0, 6, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-2,-2, 2, 4, 2, -6.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0,-3, 2, 2, 2, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0, 0, 0, 4, 2, -7.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-1,-1, 2, 3, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + + /* 611-620 */ + {-2, 0, 2, 4, 0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2,-1, 0, 2, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 1, 0, 0, 3, 0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 0, 4, 1, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 0, 1, 0, 4, 0, -11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1,-1, 2, 1, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 0, 0, 2, 2, 3, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0, 2, 2, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-1, 0, 2, 2, 2, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-2, 0, 4, 2, 1, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + + /* 621-630 */ + { 2, 1, 0, 2, 1, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 2, 1, 0, 2, 0, -12.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2,-1, 2, 0, 0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0, 2, 1, 0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 1, 2, 2, 0, -4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2, 0, 2, 0, 3, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 3, 0, 2, 0, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 1, 0, 2, 0, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 1, 0, 3, 0, 3, 0.0, 0.0, -5.0, 0.0, 0.0, -2.0}, + { 1, 1, 2, 1, 1, -7.0, 0.0, 0.0, 4.0, 0.0, 0.0}, + + /* 631-640 */ + { 0, 2, 2, 2, 2, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 2, 1, 2, 0, 0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2, 0, 4,-2, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 4, 1, 2,-2, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + {-1,-1, 0, 6, 0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {-3,-1, 2, 6, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + {-1, 0, 0, 6, 1, -5.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-3, 0, 2, 6, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 1,-1, 0, 4, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 1,-1, 0, 4, 0, 12.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 641-650 */ + {-2, 0, 2, 5, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 1,-2, 2, 2, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 3,-1, 0, 2, 0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1,-1, 2, 2, 0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 0, 2, 3, 1, 5.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + {-1, 1, 2, 4, 1, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 0, 1, 2, 3, 2, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-1, 0, 4, 2, 1, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 2, 0, 2, 1, 1, 6.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 5, 0, 0, 0, 0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 651-660 */ + { 2, 1, 2, 1, 2, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 1, 0, 4, 0, 1, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 3, 1, 2, 0, 1, 7.0, 0.0, 0.0, -4.0, 0.0, 0.0}, + { 3, 0, 4,-2, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + {-2,-1, 2, 6, 2, -5.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0, 0, 0, 6, 0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0,-2, 2, 4, 2, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + {-2, 0, 2, 6, 1, -6.0, 0.0, 0.0, 3.0, 0.0, 0.0}, + { 2, 0, 0, 4, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 2, 0, 0, 4, 0, 10.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + + /* 661-670 */ + { 2,-2, 2, 2, 2, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 0, 0, 2, 4, 0, 7.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 1, 0, 2, 3, 2, 7.0, 0.0, 0.0, -3.0, 0.0, 0.0}, + { 4, 0, 0, 2, 0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 2, 0, 2, 2, 0, 11.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + { 0, 0, 4, 2, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 4,-1, 2, 0, 2, -6.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 3, 0, 2, 1, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 2, 1, 2, 2, 1, 3.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 4, 1, 2, 0, 2, 5.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + + /* 671-678 */ + {-1,-1, 2, 6, 2, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + {-1, 0, 2, 6, 1, -4.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 1,-1, 2, 4, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0}, + { 1, 1, 2, 4, 2, 4.0, 0.0, 0.0, -2.0, 0.0, 0.0}, + { 3, 1, 2, 2, 2, 3.0, 0.0, 0.0, -1.0, 0.0, 0.0}, + { 5, 0, 2, 0, 1, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 2,-1, 2, 4, 2, -3.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + { 2, 0, 2, 4, 1, -3.0, 0.0, 0.0, 2.0, 0.0, 0.0} + }; + +/* Number of terms in the luni-solar nutation model */ + const int NLS = (int) (sizeof xls / sizeof xls[0]); + +/* ------------------------ */ +/* Planetary nutation model */ +/* ------------------------ */ + +/* The units for the sine and cosine coefficients are */ +/* 0.1 microarcsecond */ + + static const struct { + int nl, /* coefficients of l, F, D and Omega */ + nf, + nd, + nom, + nme, /* coefficients of planetary longitudes */ + nve, + nea, + nma, + nju, + nsa, + nur, + nne, + npa; /* coefficient of general precession */ + int sp,cp; /* longitude sin, cos coefficients */ + int se,ce; /* obliquity sin, cos coefficients */ + } xpl[] = { + + /* 1-10 */ + { 0, 0, 0, 0, 0, 0, 8,-16, 4, 5, 0, 0, 0, 1440, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, -8, 16,-4,-5, 0, 0, 2, 56,-117, -42, -40}, + { 0, 0, 0, 0, 0, 0, 8,-16, 4, 5, 0, 0, 2, 125, -43, 0, -54}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 2, 2, 0, 5, 0, 0}, + { 0, 0, 0, 0, 0, 0, -4, 8,-1,-5, 0, 0, 2, 3, -7, -3, 0}, + { 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 1, 3, 0, 0, -2}, + { 0, 1,-1, 1, 0, 0, 3, -8, 3, 0, 0, 0, 0, -114, 0, 0, 61}, + {-1, 0, 0, 0, 0, 10, -3, 0, 0, 0, 0, 0, 0, -219, 89, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0,-2, 6,-3, 0, 2, -3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0, -462,1604, 0, 0}, + + /* 11-20 */ + { 0, 1,-1, 1, 0, 0, -5, 8,-3, 0, 0, 0, 0, 99, 0, 0, -53}, + { 0, 0, 0, 0, 0, 0, -4, 8,-3, 0, 0, 0, 1, -3, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 4, -8, 1, 5, 0, 0, 2, 0, 6, 2, 0}, + { 0, 0, 0, 0, 0, -5, 6, 4, 0, 0, 0, 0, 2, 3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 2,-5, 0, 0, 2, -12, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 2,-5, 0, 0, 1, 14,-218, 117, 8}, + { 0, 1,-1, 1, 0, 0, -1, 0, 2,-5, 0, 0, 0, 31,-481, -257, -17}, + { 0, 0, 0, 0, 0, 0, 0, 0, 2,-5, 0, 0, 0, -491, 128, 0, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0,-2, 5, 0, 0, 0,-3084,5123, 2735,1647}, + { 0, 0, 0, 0, 0, 0, 0, 0,-2, 5, 0, 0, 1,-1444,2409,-1286,-771}, + + /* 21-30 */ + { 0, 0, 0, 0, 0, 0, 0, 0,-2, 5, 0, 0, 2, 11, -24, -11, -9}, + { 2,-1,-1, 0, 0, 0, 3, -7, 0, 0, 0, 0, 0, 26, -9, 0, 0}, + { 1, 0,-2, 0, 0, 19,-21, 3, 0, 0, 0, 0, 0, 103, -60, 0, 0}, + { 0, 1,-1, 1, 0, 2, -4, 0,-3, 0, 0, 0, 0, 0, -13, -7, 0}, + { 1, 0,-1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0, -26, -29, -16, 14}, + { 0, 1,-1, 1, 0, 0, -1, 0,-4,10, 0, 0, 0, 9, -27, -14, -5}, + {-2, 0, 2, 1, 0, 0, 2, 0, 0,-5, 0, 0, 0, 12, 0, 0, -6}, + { 0, 0, 0, 0, 0, 3, -7, 4, 0, 0, 0, 0, 0, -7, 0, 0, 0}, + { 0,-1, 1, 0, 0, 0, 1, 0, 1,-1, 0, 0, 0, 0, 24, 0, 0}, + {-2, 0, 2, 1, 0, 0, 2, 0,-2, 0, 0, 0, 0, 284, 0, 0,-151}, + + /* 31-40 */ + {-1, 0, 0, 0, 0, 18,-16, 0, 0, 0, 0, 0, 0, 226, 101, 0, 0}, + {-2, 1, 1, 2, 0, 0, 1, 0,-2, 0, 0, 0, 0, 0, -8, -2, 0}, + {-1, 1,-1, 1, 0, 18,-17, 0, 0, 0, 0, 0, 0, 0, -6, -3, 0}, + {-1, 0, 1, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0, 5, 0, 0, -3}, + { 0, 0, 0, 0, 0, -8, 13, 0, 0, 0, 0, 0, 2, -41, 175, 76, 17}, + { 0, 2,-2, 2, 0, -8, 11, 0, 0, 0, 0, 0, 0, 0, 15, 6, 0}, + { 0, 0, 0, 0, 0, -8, 13, 0, 0, 0, 0, 0, 1, 425, 212, -133, 269}, + { 0, 1,-1, 1, 0, -8, 12, 0, 0, 0, 0, 0, 0, 1200, 598, 319,-641}, + { 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, 0, 235, 334, 0, 0}, + { 0, 1,-1, 1, 0, 8,-14, 0, 0, 0, 0, 0, 0, 11, -12, -7, -6}, + + /* 41-50 */ + { 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, 1, 5, -6, 3, 3}, + {-2, 0, 2, 1, 0, 0, 2, 0,-4, 5, 0, 0, 0, -5, 0, 0, 3}, + {-2, 0, 2, 2, 0, 3, -3, 0, 0, 0, 0, 0, 0, 6, 0, 0, -3}, + {-2, 0, 2, 0, 0, 0, 2, 0,-3, 1, 0, 0, 0, 15, 0, 0, 0}, + { 0, 0, 0, 1, 0, 3, -5, 0, 2, 0, 0, 0, 0, 13, 0, 0, -7}, + {-2, 0, 2, 0, 0, 0, 2, 0,-4, 3, 0, 0, 0, -6, -9, 0, 0}, + { 0,-1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 266, -78, 0, 0}, + { 0, 0, 0, 1, 0, 0, -1, 2, 0, 0, 0, 0, 0, -460,-435, -232, 246}, + { 0, 1,-1, 2, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0, 15, 7, 0}, + {-1, 1, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0, -3, 0, 0, 2}, + + /* 51-60 */ + {-1, 0, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0, 0, 131, 0, 0}, + {-2, 0, 2, 0, 0, 0, 2, 0,-2,-2, 0, 0, 0, 4, 0, 0, 0}, + {-2, 2, 0, 2, 0, 0, -5, 9, 0, 0, 0, 0, 0, 0, 3, 0, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0, 0,-1, 0, 0, 0, 4, 2, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0, 0, 0, 2, 0, -17, -19, -10, 9}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, -9, -11, 6, -5}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, -6, 0, 0, 3}, + {-1, 0, 1, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, -16, 8, 0, 0}, + { 0,-1, 1, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0}, + + /* 61-70 */ + { 0, 1,-1, 2, 0, 0, -1, 0, 0, 2, 0, 0, 0, 11, 24, 11, -5}, + { 0, 0, 0, 1, 0, 0, -9, 17, 0, 0, 0, 0, 0, -3, -4, -2, 1}, + { 0, 0, 0, 2, 0, -3, 5, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1}, + { 0, 1,-1, 1, 0, 0, -1, 0,-1, 2, 0, 0, 0, 0, -8, -4, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 1,-2, 0, 0, 0, 0, 3, 0, 0}, + { 1, 0,-2, 0, 0, 17,-16, 0,-2, 0, 0, 0, 0, 0, 5, 0, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0, 1,-3, 0, 0, 0, 0, 3, 2, 0}, + {-2, 0, 2, 1, 0, 0, 5, -6, 0, 0, 0, 0, 0, -6, 4, 2, 3}, + { 0,-2, 2, 0, 0, 0, 9,-13, 0, 0, 0, 0, 0, -3, -5, 0, 0}, + { 0, 1,-1, 2, 0, 0, -1, 0, 0, 1, 0, 0, 0, -5, 0, 0, 2}, + + /* 71-80 */ + { 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 24, 13, -2}, + { 0,-1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, -42, 20, 0, 0}, + { 0,-2, 2, 0, 0, 5, -6, 0, 0, 0, 0, 0, 0, -10, 233, 0, 0}, + { 0,-1, 1, 1, 0, 5, -7, 0, 0, 0, 0, 0, 0, -3, 0, 0, 1}, + {-2, 0, 2, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0, 78, -18, 0, 0}, + { 2, 1,-3, 1, 0, -6, 7, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0}, + { 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -3, -1, 0}, + { 0,-1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, -4, -2, 1}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0, 0, 2, 0, 0, 0, -8, -4, -1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, -5, 3, 0}, + + /* 81-90 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, -7, 0, 0, 3}, + { 0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 2, -14, 8, 3, 6}, + { 0, 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 1, 0, 8, -4, 0}, + { 0, 1,-1, 1, 0, 0, -9, 15, 0, 0, 0, 0, 0, 0, 19, 10, 0}, + { 0, 0, 0, 0, 0, 0, 8,-15, 0, 0, 0, 0, 0, 45, -22, 0, 0}, + { 1,-1,-1, 0, 0, 0, 8,-15, 0, 0, 0, 0, 0, -3, 0, 0, 0}, + { 2, 0,-2, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0}, + {-2, 0, 2, 0, 0, 0, 2, 0,-5, 5, 0, 0, 0, 0, 3, 0, 0}, + { 2, 0,-2, 1, 0, 0, -6, 8, 0, 0, 0, 0, 0, 3, 5, 3, -2}, + { 2, 0,-2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0, 89, -16, -9, -48}, + + /* 91-100 */ + {-2, 1, 1, 0, 0, 0, 1, 0,-3, 0, 0, 0, 0, 0, 3, 0, 0}, + {-2, 1, 1, 1, 0, 0, 1, 0,-3, 0, 0, 0, 0, -3, 7, 4, 2}, + {-2, 0, 2, 0, 0, 0, 2, 0,-3, 0, 0, 0, 0, -349, -62, 0, 0}, + {-2, 0, 2, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0, -15, 22, 0, 0}, + {-2, 0, 2, 0, 0, 0, 2, 0,-1,-5, 0, 0, 0, -3, 0, 0, 0}, + {-1, 0, 1, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, -53, 0, 0, 0}, + {-1, 1, 1, 1, 0,-20, 20, 0, 0, 0, 0, 0, 0, 5, 0, 0, -3}, + { 1, 0,-2, 0, 0, 20,-21, 0, 0, 0, 0, 0, 0, 0, -8, 0, 0}, + { 0, 0, 0, 1, 0, 0, 8,-15, 0, 0, 0, 0, 0, 15, -7, -4, -8}, + { 0, 2,-2, 1, 0, 0,-10, 15, 0, 0, 0, 0, 0, -3, 0, 0, 1}, + + /* 101-110 */ + { 0,-1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, -21, -78, 0, 0}, + { 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 20, -70, -37, -11}, + { 0, 1,-1, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 6, 3, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0,-2, 4, 0, 0, 0, 5, 3, 2, -2}, + { 2, 0,-2, 1, 0, -6, 8, 0, 0, 0, 0, 0, 0, -17, -4, -2, 9}, + { 0,-2, 2, 1, 0, 5, -6, 0, 0, 0, 0, 0, 0, 0, 6, 3, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 1, 32, 15, -8, 17}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0,-1, 0, 0, 0, 174, 84, 45, -93}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 11, 56, 0, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, -66, -12, -6, 35}, + + /* 111-120 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 47, 8, 4, -25}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 8, 4, 0}, + { 0, 2,-2, 1, 0, 0, -9, 13, 0, 0, 0, 0, 0, 10, -22, -12, -5}, + { 0, 0, 0, 1, 0, 0, 7,-13, 0, 0, 0, 0, 0, -3, 0, 0, 2}, + {-2, 0, 2, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, -24, 12, 0, 0}, + { 0, 0, 0, 0, 0, 0, 9,-17, 0, 0, 0, 0, 0, 5, -6, 0, 0}, + { 0, 0, 0, 0, 0, 0, -9, 17, 0, 0, 0, 0, 2, 3, 0, 0, -2}, + { 1, 0,-1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0, 4, 3, 1, -2}, + { 1, 0,-1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0, 0, 29, 15, 0}, + { 0, 0, 0, 2, 0, 0, -1, 2, 0, 0, 0, 0, 0, -5, -4, -2, 2}, + + /* 121-130 */ + { 0,-1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 8, -3, -1, -5}, + { 0,-2, 2, 0, 1, 0, -2, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0}, + { 0, 0, 0, 0, 0, 3, -5, 0, 2, 0, 0, 0, 0, 10, 0, 0, 0}, + {-2, 0, 2, 1, 0, 0, 2, 0,-3, 1, 0, 0, 0, 3, 0, 0, -2}, + {-2, 0, 2, 1, 0, 3, -3, 0, 0, 0, 0, 0, 0, -5, 0, 0, 3}, + { 0, 0, 0, 1, 0, 8,-13, 0, 0, 0, 0, 0, 0, 46, 66, 35, -25}, + { 0,-1, 1, 0, 0, 8,-12, 0, 0, 0, 0, 0, 0, -14, 7, 0, 0}, + { 0, 2,-2, 1, 0, -8, 11, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0}, + {-1, 0, 1, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, -5, 0, 0, 0}, + {-1, 0, 0, 1, 0, 18,-16, 0, 0, 0, 0, 0, 0, -68, -34, -18, 36}, + + /* 131-140 */ + { 0, 1,-1, 1, 0, 0, -1, 0,-1, 1, 0, 0, 0, 0, 14, 7, 0}, + { 0, 0, 0, 1, 0, 3, -7, 4, 0, 0, 0, 0, 0, 10, -6, -3, -5}, + {-2, 1, 1, 1, 0, 0, -3, 7, 0, 0, 0, 0, 0, -5, -4, -2, 3}, + { 0, 1,-1, 2, 0, 0, -1, 0,-2, 5, 0, 0, 0, -3, 5, 2, 1}, + { 0, 0, 0, 1, 0, 0, 0, 0,-2, 5, 0, 0, 0, 76, 17, 9, -41}, + { 0, 0, 0, 1, 0, 0, -4, 8,-3, 0, 0, 0, 0, 84, 298, 159, -45}, + { 1, 0, 0, 1, 0,-10, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1}, + { 0, 2,-2, 1, 0, 0, -2, 0, 0, 0, 0, 0, 0, -3, 0, 0, 2}, + {-1, 0, 0, 1, 0, 10, -3, 0, 0, 0, 0, 0, 0, -3, 0, 0, 1}, + { 0, 0, 0, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0, -82, 292, 156, 44}, + + /* 141-150 */ + { 0, 0, 0, 1, 0, 0, 0, 0, 2,-5, 0, 0, 0, -73, 17, 9, 39}, + { 0,-1, 1, 0, 0, 0, 1, 0, 2,-5, 0, 0, 0, -9, -16, 0, 0}, + { 2,-1,-1, 1, 0, 0, 3, -7, 0, 0, 0, 0, 0, 3, 0, -1, -2}, + {-2, 0, 2, 0, 0, 0, 2, 0, 0,-5, 0, 0, 0, -3, 0, 0, 0}, + { 0, 0, 0, 1, 0, -3, 7, -4, 0, 0, 0, 0, 0, -9, -5, -3, 5}, + {-2, 0, 2, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, -439, 0, 0, 0}, + { 1, 0, 0, 1, 0,-18, 16, 0, 0, 0, 0, 0, 0, 57, -28, -15, -30}, + {-2, 1, 1, 1, 0, 0, 1, 0,-2, 0, 0, 0, 0, 0, -6, -3, 0}, + { 0, 1,-1, 2, 0, -8, 12, 0, 0, 0, 0, 0, 0, -4, 0, 0, 2}, + { 0, 0, 0, 1, 0, -8, 13, 0, 0, 0, 0, 0, 0, -40, 57, 30, 21}, + + /* 151-160 */ + { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 1, 23, 7, 3, -13}, + { 0, 1,-1, 1, 0, 0, 0, -2, 0, 0, 0, 0, 0, 273, 80, 43,-146}, + { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, -449, 430, 0, 0}, + { 0, 1,-1, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0, -8, -47, -25, 4}, + { 0, 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 1, 6, 47, 25, -3}, + {-1, 0, 1, 1, 0, 3, -4, 0, 0, 0, 0, 0, 0, 0, 23, 13, 0}, + {-1, 0, 1, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0, -3, 0, 0, 2}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0,-2, 0, 0, 0, 3, -4, -2, -2}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0, 2, 0, 0, 0, -48,-110, -59, 26}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 51, 114, 61, -27}, + + /* 161-170 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, -133, 0, 0, 57}, + { 0, 1,-1, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0}, + { 0, 0, 0, 1, 0, -3, 5, 0, 0, 0, 0, 0, 0, -21, -6, -3, 11}, + { 0, 1,-1, 2, 0, -3, 4, 0, 0, 0, 0, 0, 0, 0, -3, -1, 0}, + { 0, 0, 0, 1, 0, 0, -2, 4, 0, 0, 0, 0, 0, -11, -21, -11, 6}, + { 0, 2,-2, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0, -18,-436, -233, 9}, + { 0,-1, 1, 0, 0, 5, -7, 0, 0, 0, 0, 0, 0, 35, -7, 0, 0}, + { 0, 0, 0, 1, 0, 5, -8, 0, 0, 0, 0, 0, 0, 0, 5, 3, 0}, + {-2, 0, 2, 1, 0, 6, -8, 0, 0, 0, 0, 0, 0, 11, -3, -1, -6}, + { 0, 0, 0, 1, 0, 0, -8, 15, 0, 0, 0, 0, 0, -5, -3, -1, 3}, + + /* 171-180 */ + {-2, 0, 2, 1, 0, 0, 2, 0,-3, 0, 0, 0, 0, -53, -9, -5, 28}, + {-2, 0, 2, 1, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0, 3, 2, 1}, + { 1, 0,-1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0, 4, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 0, 0, 3,-5, 0, 0, 0, 0, -4, 0, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0,-1, 0, 0, 0, 0, -50, 194, 103, 27}, + { 0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 0, 0, 1, -13, 52, 28, 7}, + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -91, 248, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 6, 49, 26, -3}, + { 0, 1,-1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0, -6, -47, -25, 3}, + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5, 3, 0}, + + /* 181-190 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 52, 23, 10, -23}, + { 0, 1,-1, 2, 0, 0, -1, 0, 0,-1, 0, 0, 0, -3, 0, 0, 1}, + { 0, 0, 0, 1, 0, 0, 0, 0, 0,-1, 0, 0, 0, 0, 5, 3, 0}, + { 0,-1, 1, 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, -4, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, -7, 13, 0, 0, 0, 0, 2, -4, 8, 3, 2}, + { 0, 0, 0, 0, 0, 0, 7,-13, 0, 0, 0, 0, 0, 10, 0, 0, 0}, + { 2, 0,-2, 1, 0, 0, -5, 6, 0, 0, 0, 0, 0, 3, 0, 0, -2}, + { 0, 2,-2, 1, 0, 0, -8, 11, 0, 0, 0, 0, 0, 0, 8, 4, 0}, + { 0, 2,-2, 1,-1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 8, 4, 1}, + {-2, 0, 2, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, -4, 0, 0, 0}, + + /* 191-200 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 2,-2, 0, 0, 0, -4, 0, 0, 0}, + { 0, 1,-1, 1, 0, 0, -1, 0, 0, 3, 0, 0, 0, -8, 4, 2, 4}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1, 8, -4, -2, -4}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 15, 7, 0}, + {-2, 0, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, -138, 0, 0, 0}, + { 0, 0, 0, 2, 0, 0, -4, 8,-3, 0, 0, 0, 0, 0, -7, -3, 0}, + { 0, 0, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, -7, -3, 0}, + { 2, 0,-2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0, 54, 0, 0, -29}, + { 0, 1,-1, 2, 0, 0, -1, 0, 2, 0, 0, 0, 0, 0, 10, 4, 0}, + { 0, 1,-1, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, -7, 0, 0, 3}, + + /* 201-210 */ + { 0, 0, 0, 1, 0, 0, 1, -2, 0, 0, 0, 0, 0, -37, 35, 19, 20}, + { 0,-1, 1, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 4, 0, 0}, + { 0,-1, 1, 0, 0, 0, 1, 0, 0,-2, 0, 0, 0, -4, 9, 0, 0}, + { 0, 2,-2, 1, 0, 0, -2, 0, 0, 2, 0, 0, 0, 8, 0, 0, -4}, + { 0, 1,-1, 1, 0, 3, -6, 0, 0, 0, 0, 0, 0, -9, -14, -8, 5}, + { 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 1, -3, -9, -5, 3}, + { 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0, -145, 47, 0, 0}, + { 0, 1,-1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0, -10, 40, 21, 5}, + { 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 1, 11, -49, -26, -7}, + { 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 2,-2150, 0, 0, 932}, + + /* 211-220 */ + { 0, 2,-2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0, -12, 0, 0, 5}, + { 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 0, 2, 85, 0, 0, -37}, + { 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 1, 4, 0, 0, -2}, + { 0, 1,-1, 1, 0, 0, 1, -4, 0, 0, 0, 0, 0, 3, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, -86, 153, 0, 0}, + { 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 1, -6, 9, 5, 3}, + { 0, 1,-1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0, 9, -13, -7, -5}, + { 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 1, -8, 12, 6, 4}, + { 0, 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 2, -51, 0, 0, 22}, + { 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 2, -11,-268, -116, 5}, + + /* 221-230 */ + { 0, 2,-2, 2, 0, -5, 6, 0, 0, 0, 0, 0, 0, 0, 12, 5, 0}, + { 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 2, 0, 7, 3, 0}, + { 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 1, 31, 6, 3, -17}, + { 0, 1,-1, 1, 0, -5, 7, 0, 0, 0, 0, 0, 0, 140, 27, 14, -75}, + { 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 0, 1, 57, 11, 6, -30}, + { 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0, 0, -14, -39, 0, 0}, + { 0, 1,-1, 2, 0, 0, -1, 0,-1, 0, 0, 0, 0, 0, -6, -2, 0}, + { 0, 0, 0, 1, 0, 0, 0, 0,-1, 0, 0, 0, 0, 4, 15, 8, -2}, + { 0,-1, 1, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 0, 4, 0, 0}, + { 0, 2,-2, 1, 0, 0, -2, 0, 1, 0, 0, 0, 0, -3, 0, 0, 1}, + + /* 231-240 */ + { 0, 0, 0, 0, 0, 0, -6, 11, 0, 0, 0, 0, 2, 0, 11, 5, 0}, + { 0, 0, 0, 0, 0, 0, 6,-11, 0, 0, 0, 0, 0, 9, 6, 0, 0}, + { 0, 0, 0, 0,-1, 0, 4, 0, 0, 0, 0, 0, 2, -4, 10, 4, 2}, + { 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0, 0, 0, 5, 3, 0, 0}, + { 2, 0,-2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0, 16, 0, 0, -9}, + {-2, 0, 2, 0, 0, 0, 2, 0, 0,-2, 0, 0, 0, -3, 0, 0, 0}, + { 0, 2,-2, 1, 0, 0, -7, 9, 0, 0, 0, 0, 0, 0, 3, 2, -1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 4,-5, 0, 0, 2, 7, 0, 0, -3}, + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, -25, 22, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 42, 223, 119, -22}, + + /* 241-250 */ + { 0, 1,-1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0, -27,-143, -77, 14}, + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 9, 49, 26, -5}, + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2,-1166, 0, 0, 505}, + { 0, 2,-2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0, -5, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 2, -6, 0, 0, 3}, + { 0, 0, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0, -8, 0, 1, 4}, + { 0,-1, 1, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0}, + { 0, 2,-2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0, 117, 0, 0, -63}, + { 0, 0, 0, 1, 0, 0, 2, -4, 0, 0, 0, 0, 0, -4, 8, 4, 2}, + { 0, 2,-2, 1, 0, 0, -4, 4, 0, 0, 0, 0, 0, 3, 0, 0, -2}, + + /* 251-260 */ + { 0, 1,-1, 2, 0, -5, 7, 0, 0, 0, 0, 0, 0, -5, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0, 31, 0, 0}, + { 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 1, -5, 0, 1, 3}, + { 0, 1,-1, 1, 0, 0, -4, 6, 0, 0, 0, 0, 0, 4, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 1, -4, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 2, -24, -13, -6, 10}, + { 0,-1, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0}, + { 0, 0, 0, 1, 0, 2, -3, 0, 0, 0, 0, 0, 0, 0, -32, -17, 0}, + { 0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 2, 8, 12, 5, -3}, + { 0, 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 1, 3, 0, 0, -1}, + + /* 261-270 */ + { 0, 0, 0, 0, 0, 0, 5, -9, 0, 0, 0, 0, 0, 7, 13, 0, 0}, + { 0,-1, 1, 0, 0, 0, 1, 0,-2, 0, 0, 0, 0, -3, 16, 0, 0}, + { 0, 2,-2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0, 50, 0, 0, -27}, + {-2, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -5, -3, 0}, + { 0,-2, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0}, + { 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0, 1, 0, 5, 3, 1}, + { 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0, 2, 24, 5, 2, -11}, + { 0, 0, 0, 0, 0, -2, 3, 0, 0, 0, 0, 0, 2, 5, -11, -5, -2}, + { 0, 0, 0, 0, 0, -2, 3, 0, 0, 0, 0, 0, 1, 30, -3, -2, -16}, + { 0, 1,-1, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0, 18, 0, 0, -9}, + + /* 271-280 */ + { 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 0, 8, 614, 0, 0}, + { 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 1, 3, -3, -1, -2}, + { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 6, 17, 9, -3}, + { 0, 1,-1, 1, 0, 0, -1, 0, 3, 0, 0, 0, 0, -3, -9, -5, 2}, + { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 0, 6, 3, -1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, -127, 21, 9, 55}, + { 0, 0, 0, 0, 0, 0, 4, -8, 0, 0, 0, 0, 0, 3, 5, 0, 0}, + { 0, 0, 0, 0, 0, 0, -4, 8, 0, 0, 0, 0, 2, -6, -10, -4, 3}, + { 0,-2, 2, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 5, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 2, 16, 9, 4, -7}, + + /* 281-290 */ + { 0, 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 1, 3, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, 0, 0, 22, 0, 0}, + { 0, 0, 0, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0, 0, 19, 10, 0}, + { 0, 2,-2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0, 7, 0, 0, -4}, + { 0, 0, 0, 0, 0, 0, -5, 10, 0, 0, 0, 0, 2, 0, -5, -2, 0}, + { 0, 0, 0, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, -9, 3, 1, 4}, + { 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 2, 17, 0, 0, -7}, + { 0, 0, 0, 0, 0, 0, -3, 5, 0, 0, 0, 0, 1, 0, -3, -2, -1}, + { 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, -20, 34, 0, 0}, + + /* 291-300 */ + { 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 1, -10, 0, 1, 5}, + { 0, 1,-1, 1, 0, 1, -3, 0, 0, 0, 0, 0, 0, -4, 0, 0, 2}, + { 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 0, 22, -87, 0, 0}, + { 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 0, 1, -4, 0, 0, 2}, + { 0, 0, 0, 0, 0, -1, 2, 0, 0, 0, 0, 0, 2, -3, -6, -2, 1}, + { 0, 0, 0, 0, 0, -7, 11, 0, 0, 0, 0, 0, 2, -16, -3, -1, 7}, + { 0, 0, 0, 0, 0, -7, 11, 0, 0, 0, 0, 0, 1, 0, -3, -2, 0}, + { 0,-2, 2, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, -68, 39, 0, 0}, + { 0, 2,-2, 1, 0, -4, 4, 0, 0, 0, 0, 0, 0, 27, 0, 0, -14}, + + /* 301-310 */ + { 0,-1, 1, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, -25, 0, 0, 0}, + { 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 0, 1, -12, -3, -2, 6}, + { 0, 1,-1, 1, 0, -4, 6, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, -4, 7, 0, 0, 0, 0, 0, 2, 3, 66, 29, -1}, + { 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 2, 490, 0, 0,-213}, + { 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 1, -22, 93, 49, 12}, + { 0, 1,-1, 1, 0, -4, 5, 0, 0, 0, 0, 0, 0, -7, 28, 15, 4}, + { 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 0, 1, -3, 13, 7, 2}, + { 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0, 0, -46, 14, 0, 0}, + + /* 311-320 */ + {-2, 0, 2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 0}, + { 0,-1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0}, + { 0, 0, 0, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0, -28, 0, 0, 15}, + { 0, 0, 0, 0, 0, 0, -1, 0, 5, 0, 0, 0, 2, 5, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, 0, 3, 0, 0}, + { 0, 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 2, -11, 0, 0, 5}, + { 0, 0, 0, 0, 0, 0, -7, 12, 0, 0, 0, 0, 2, 0, 3, 1, 0}, + { 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 2, -3, 0, 0, 1}, + { 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 1, 25, 106, 57, -13}, + + /* 321-330 */ + { 0, 1,-1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 5, 21, 11, -3}, + { 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 1485, 0, 0, 0}, + { 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 1, -7, -32, -17, 4}, + { 0, 1,-1, 1, 0, 1, -2, 0, 0, 0, 0, 0, 0, 0, 5, 3, 0}, + { 0, 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 2, -6, -3, -2, 3}, + { 0, 0, 0, 0, 0, 0, -1, 0, 4, 0, 0, 0, 2, 30, -6, -2, -13}, + { 0, 0, 0, 0, 0, 0, 1, 0,-4, 0, 0, 0, 0, -4, 4, 0, 0}, + { 0, 0, 0, 1, 0, -1, 1, 0, 0, 0, 0, 0, 0, -19, 0, 0, 10}, + { 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 2, 0, 4, 2, -1}, + { 0, 0, 0, 0, 0, 0, -6, 10, 0, 0, 0, 0, 0, 0, 3, 0, 0}, + + /* 331-340 */ + { 0, 2,-2, 1, 0, 0, -3, 0, 3, 0, 0, 0, 0, 4, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, -3, 7, 0, 0, 0, 0, 2, 0, -3, -1, 0}, + {-2, 0, 2, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, -5, 8, 0, 0, 0, 0, 2, 5, 3, 1, -2}, + { 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0, 0, 11, 0, 0}, + { 0, 0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 2, 118, 0, 0, -52}, + { 0, 0, 0, 0, 0, 0, -1, 0, 3, 0, 0, 0, 1, 0, -5, -3, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0,-3, 0, 0, 0, 0, -28, 36, 0, 0}, + { 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, 0, 5, -5, 0, 0}, + { 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 0, 1, 14, -59, -31, -8}, + + /* 341-350 */ + { 0, 1,-1, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0, 0, 9, 5, 1}, + { 0, 0, 0, 0, 0, -2, 4, 0, 0, 0, 0, 0, 2, -458, 0, 0, 198}, + { 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 0, 2, 0, -45, -20, 0}, + { 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 0, 1, 9, 0, 0, -5}, + { 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0}, + { 0, 0, 0, 1, 0, 0, 1, 0,-2, 0, 0, 0, 0, 0, -4, -2, -1}, + { 0, 2,-2, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0, 11, 0, 0, -6}, + { 0, 0, 0, 0, 0, 0, -4, 6, 0, 0, 0, 0, 2, 6, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0, -16, 23, 0, 0}, + { 0, 0, 0, 1, 0, 3, -4, 0, 0, 0, 0, 0, 0, 0, -4, -2, 0}, + + /* 351-360 */ + { 0, 0, 0, 0, 0, 0, -1, 0, 2, 0, 0, 0, 2, -5, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 1, 0,-2, 0, 0, 0, 0, -166, 269, 0, 0}, + { 0, 0, 0, 1, 0, 0, 1, 0,-1, 0, 0, 0, 0, 15, 0, 0, -8}, + { 0, 0, 0, 0, 0, -5, 9, 0, 0, 0, 0, 0, 2, 10, 0, 0, -4}, + { 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, -78, 45, 0, 0}, + { 0, 0, 0, 0, 0, -3, 4, 0, 0, 0, 0, 0, 2, 0, -5, -2, 0}, + { 0, 0, 0, 0, 0, -3, 4, 0, 0, 0, 0, 0, 1, 7, 0, 0, -4}, + { 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0, -5, 328, 0, 0}, + { 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 1, 3, 0, 0, -2}, + { 0, 0, 0, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0, 5, 0, 0, -2}, + + /* 361-370 */ + { 0, 0, 0, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0, 0, 3, 1, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 0,-3, 0, 0, 0, -3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 1,-5, 0, 0, 0, -3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, -4, -2, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0,-1223, -26, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 1, 0, 7, 3, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0,-3, 5, 0, 0, 0, 3, 0, 0, 0}, + { 0, 0, 0, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 0,-2, 0, 0, 0, -6, 20, 0, 0}, + { 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, -368, 0, 0, 0}, + + /* 371-380 */ + { 0, 0, 0, 0, 0, 0, 1, 0, 0,-1, 0, 0, 0, -75, 0, 0, 0}, + { 0, 0, 0, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0, 11, 0, 0, -6}, + { 0, 0, 0, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0, 3, 0, 0, -2}, + { 0, 0, 0, 0, 0, -8, 14, 0, 0, 0, 0, 0, 2, -3, 0, 0, 1}, + { 0, 0, 0, 0, 0, 0, 1, 0, 2,-5, 0, 0, 0, -13, -30, 0, 0}, + { 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 0, 21, 3, 0, 0}, + { 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 2, -3, 0, 0, 1}, + { 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, -4, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 8, -27, 0, 0}, + { 0, 0, 0, 0, 0, 0, 3, -8, 3, 0, 0, 0, 0, -19, -11, 0, 0}, + + /* 381-390 */ + { 0, 0, 0, 0, 0, 0, -3, 8,-3, 0, 0, 0, 2, -4, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 1, 0,-2, 5, 0, 0, 2, 0, 5, 2, 0}, + { 0, 0, 0, 0, 0, -8, 12, 0, 0, 0, 0, 0, 2, -6, 0, 0, 2}, + { 0, 0, 0, 0, 0, -8, 12, 0, 0, 0, 0, 0, 0, -8, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 1,-2, 0, 0, 0, -1, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 2, -14, 0, 0, 6}, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 6, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, -74, 0, 0, 32}, + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 2, 0, -3, -1, 0}, + { 0, 2,-2, 1, 0, -5, 5, 0, 0, 0, 0, 0, 0, 4, 0, 0, -2}, + + /* 391-400 */ + { 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 11, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 3, 2, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, -262, 0, 0, 114}, + { 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0}, + { 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 0, 1, -7, 0, 0, 4}, + { 0, 0, 0, 0, 0, -3, 6, 0, 0, 0, 0, 0, 2, 0, -27, -12, 0}, + { 0, 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 2, -19, -8, -4, 8}, + { 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 0, 2, 202, 0, 0, -87}, + { 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 0, 1, -8, 35, 19, 5}, + { 0, 1,-1, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0, 0, 4, 2, 0}, + + /* 401-410 */ + { 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0, 0, 16, -5, 0, 0}, + { 0, 2,-2, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0, 5, 0, 0, -3}, + { 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, -3, 0, 0}, + { 0, 0, 0, 0,-1, 0, 3, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2, -35, -48, -21, 15}, + { 0, 0, 0, 0, 0, 0, -2, 6, 0, 0, 0, 0, 2, -3, -5, -2, 1}, + { 0, 0, 0, 1, 0, 2, -2, 0, 0, 0, 0, 0, 0, 6, 0, 0, -3}, + { 0, 0, 0, 0, 0, 0, -6, 9, 0, 0, 0, 0, 2, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0, 0, -5, 0, 0}, + { 0, 0, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0, 1, 12, 55, 29, -6}, + + /* 411-420 */ + { 0, 1,-1, 1, 0, -2, 1, 0, 0, 0, 0, 0, 0, 0, 5, 3, 0}, + { 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, -598, 0, 0, 0}, + { 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 1, -3, -13, -7, 1}, + { 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 2, -5, -7, -3, 2}, + { 0, 0, 0, 0, 0, 0, -5, 7, 0, 0, 0, 0, 2, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0, 5, -7, 0, 0}, + { 0, 0, 0, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0, 4, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, 16, -6, 0, 0}, + { 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, 0, 8, -3, 0, 0}, + { 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 0, 1, 8, -31, -16, -4}, + + /* 421-430 */ + { 0, 1,-1, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0}, + { 0, 0, 0, 0, 0, -1, 3, 0, 0, 0, 0, 0, 2, 113, 0, 0, -49}, + { 0, 0, 0, 0, 0, -7, 10, 0, 0, 0, 0, 0, 2, 0, -24, -10, 0}, + { 0, 0, 0, 0, 0, -7, 10, 0, 0, 0, 0, 0, 1, 4, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 27, 0, 0, 0}, + { 0, 0, 0, 0, 0, -4, 8, 0, 0, 0, 0, 0, 2, -3, 0, 0, 1}, + { 0, 0, 0, 0, 0, -4, 5, 0, 0, 0, 0, 0, 2, 0, -4, -2, 0}, + { 0, 0, 0, 0, 0, -4, 5, 0, 0, 0, 0, 0, 1, 5, 0, 0, -2}, + { 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0}, + { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 2, -13, 0, 0, 6}, + + /* 431-440 */ + { 0, 0, 0, 0, 0, 0, -2, 0, 5, 0, 0, 0, 2, 5, 0, 0, -2}, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2, -18, -10, -4, 8}, + { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -4, -28, 0, 0}, + { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, -5, 6, 3, 2}, + { 0, 0, 0, 0, 0, -9, 13, 0, 0, 0, 0, 0, 2, -3, 0, 0, 1}, + { 0, 0, 0, 0, 0, 0, -1, 5, 0, 0, 0, 0, 2, -5, -9, -4, 2}, + { 0, 0, 0, 0, 0, 0, -2, 0, 4, 0, 0, 0, 2, 17, 0, 0, -7}, + { 0, 0, 0, 0, 0, 0, 2, 0,-4, 0, 0, 0, 0, 11, 4, 0, 0}, + { 0, 0, 0, 0, 0, 0, -2, 7, 0, 0, 0, 0, 2, 0, -6, -2, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0,-3, 0, 0, 0, 0, 83, 15, 0, 0}, + + /* 441-450 */ + { 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 0, 1, -4, 0, 0, 2}, + { 0, 0, 0, 0, 0, -2, 5, 0, 0, 0, 0, 0, 2, 0,-114, -49, 0}, + { 0, 0, 0, 0, 0, -6, 8, 0, 0, 0, 0, 0, 2, 117, 0, 0, -51}, + { 0, 0, 0, 0, 0, -6, 8, 0, 0, 0, 0, 0, 1, -5, 19, 10, 2}, + { 0, 0, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0}, + { 0, 0, 0, 1, 0, 0, 2, 0,-2, 0, 0, 0, 0, -3, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, -3, 9, 0, 0, 0, 0, 2, 0, -3, -1, 0}, + { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 2, 0, -6, -2, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 393, 3, 0, 0}, + + /* 451-460 */ + { 0, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 1, -4, 21, 11, 2}, + { 0, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 2, -6, 0, -1, 3}, + { 0, 0, 0, 0, 0, -5, 10, 0, 0, 0, 0, 0, 2, -3, 8, 4, 1}, + { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 8, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 2, 18, -29, -13, -8}, + { 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 1, 8, 34, 18, -4}, + { 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 89, 0, 0, 0}, + { 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 1, 3, 12, 6, -1}, + { 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 2, 54, -15, -7, -24}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0,-3, 0, 0, 0, 0, 3, 0, 0}, + + /* 461-470 */ + { 0, 0, 0, 0, 0, 0, -5, 13, 0, 0, 0, 0, 2, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, 0, 2, 0,-1, 0, 0, 0, 0, 0, 35, 0, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0,-1, 0, 0, 0, 2, -154, -30, -13, 67}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0,-2, 0, 0, 0, 15, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0,-2, 0, 0, 1, 0, 4, 2, 0}, + { 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 0, 9, 0, 0}, + { 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 2, 80, -71, -31, -35}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0,-1, 0, 0, 2, 0, -20, -9, 0}, + { 0, 0, 0, 0, 0, 0, -6, 15, 0, 0, 0, 0, 2, 11, 5, 2, -5}, + { 0, 0, 0, 0, 0, -8, 15, 0, 0, 0, 0, 0, 2, 61, -96, -42, -27}, + + /* 471-480 */ + { 0, 0, 0, 0, 0, -3, 9, -4, 0, 0, 0, 0, 2, 14, 9, 4, -6}, + { 0, 0, 0, 0, 0, 0, 2, 0, 2,-5, 0, 0, 2, -11, -6, -3, 5}, + { 0, 0, 0, 0, 0, 0, -2, 8,-1,-5, 0, 0, 2, 0, -3, -1, 0}, + { 0, 0, 0, 0, 0, 0, 6, -8, 3, 0, 0, 0, 2, 123,-415, -180, -53}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, -35}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 7, -32, -17, -4}, + { 0, 1,-1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -9, -5, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, -4, 2, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, -89, 0, 0, 38}, + + /* 481-490 */ + { 0, 0, 0, 0, 0, 0, -6, 16,-4,-5, 0, 0, 2, 0, -86, -19, -6}, + { 0, 0, 0, 0, 0, 0, -2, 8,-3, 0, 0, 0, 2, 0, 0, -19, 6}, + { 0, 0, 0, 0, 0, 0, -2, 8,-3, 0, 0, 0, 2, -123,-416, -180, 53}, + { 0, 0, 0, 0, 0, 0, 6, -8, 1, 5, 0, 0, 2, 0, -3, -1, 0}, + { 0, 0, 0, 0, 0, 0, 2, 0,-2, 5, 0, 0, 2, 12, -6, -3, -5}, + { 0, 0, 0, 0, 0, 3, -5, 4, 0, 0, 0, 0, 2, -13, 9, 4, 6}, + { 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 2, 0, -15, -7, 0}, + { 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 1, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, -8, 11, 0, 0, 0, 0, 0, 2, -62, -97, -42, 27}, + { 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 2, -11, 5, 2, 5}, + + /* 491-500 */ + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 2, 0, -19, -8, 0}, + { 0, 0, 0, 0, 0, 3, -3, 0, 2, 0, 0, 0, 2, -3, 0, 0, 1}, + { 0, 2,-2, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, 4, 2, 0}, + { 0, 1,-1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0}, + { 0, 2,-2, 1, 0, 0, -4, 8,-3, 0, 0, 0, 0, 0, 4, 2, 0}, + { 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 2, -85, -70, -31, 37}, + { 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 2, 163, -12, -5, -72}, + { 0, 0, 0, 0, 0, -3, 7, 0, 0, 0, 0, 0, 2, -63, -16, -7, 28}, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 2, -21, -32, -14, 9}, + { 0, 0, 0, 0, 0, -5, 6, 0, 0, 0, 0, 0, 2, 0, -3, -1, 0}, + + /* 501-510 */ + { 0, 0, 0, 0, 0, -5, 6, 0, 0, 0, 0, 0, 1, 3, 0, 0, -2}, + { 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0}, + { 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 2, 3, 10, 4, -1}, + { 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, 0, -1, 6, 0, 0, 0, 0, 2, 0, -7, -3, 0}, + { 0, 0, 0, 0, 0, 0, 7, -9, 0, 0, 0, 0, 2, 0, -4, -2, 0}, + { 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 0, 6, 19, 0, 0}, + { 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 2, 5,-173, -75, -2}, + { 0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 2, 0, -7, -3, 0}, + { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 2, 7, -12, -5, -3}, + + /* 511-520 */ + { 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 0, 1, -3, 0, 0, 2}, + { 0, 0, 0, 0, 0, -1, 4, 0, 0, 0, 0, 0, 2, 3, -4, -2, -1}, + { 0, 0, 0, 0, 0, -7, 9, 0, 0, 0, 0, 0, 2, 74, 0, 0, -32}, + { 0, 0, 0, 0, 0, -7, 9, 0, 0, 0, 0, 0, 1, -3, 12, 6, 2}, + { 0, 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 2, 26, -14, -6, -11}, + { 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 2, 19, 0, 0, -8}, + { 0, 0, 0, 0, 0, -4, 4, 0, 0, 0, 0, 0, 1, 6, 24, 13, -3}, + { 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0}, + { 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 1, 0, -10, -5, 0}, + { 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 2, 11, -3, -1, -5}, + + /* 521-530 */ + { 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 2, 3, 0, 1, -1}, + { 0, 0, 0, 0, 0, 0, -3, 0, 5, 0, 0, 0, 2, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, -4, 0, 0, 0}, + { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 5, -23, -12, -3}, + { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, -339, 0, 0, 147}, + { 0, 0, 0, 0, 0, -9, 12, 0, 0, 0, 0, 0, 2, 0, -10, -5, 0}, + { 0, 0, 0, 0, 0, 0, 3, 0,-4, 0, 0, 0, 0, 5, 0, 0, 0}, + { 0, 2,-2, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 2, 0, -4, -2, 0}, + { 0, 0, 0, 0, 0, 0, 3, 0,-3, 0, 0, 0, 0, 18, -3, 0, 0}, + + /* 531-540 */ + { 0, 0, 0, 0, 0, 0, 3, 0,-3, 0, 0, 0, 2, 9, -11, -5, -4}, + { 0, 0, 0, 0, 0, -2, 6, 0, 0, 0, 0, 0, 2, -8, 0, 0, 4}, + { 0, 0, 0, 0, 0, -6, 7, 0, 0, 0, 0, 0, 1, 3, 0, 0, -1}, + { 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0}, + { 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 2, 6, -9, -4, -2}, + { 0, 0, 0, 0, 0, 0, 3, 0,-2, 0, 0, 0, 0, -4, -12, 0, 0}, + { 0, 0, 0, 0, 0, 0, 3, 0,-2, 0, 0, 0, 2, 67, -91, -39, -29}, + { 0, 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 2, 30, -18, -8, -13}, + { 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 2, 0,-114, -50, 0}, + + /* 541-550 */ + { 0, 0, 0, 0, 0, 0, 3, 0,-1, 0, 0, 0, 2, 0, 0, 0, 23}, + { 0, 0, 0, 0, 0, 0, 3, 0,-1, 0, 0, 0, 2, 517, 16, 7,-224}, + { 0, 0, 0, 0, 0, 0, 3, 0, 0,-2, 0, 0, 2, 0, -7, -3, 0}, + { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 2, 143, -3, -1, -62}, + { 0, 0, 0, 0, 0, 0, 3, 0, 0,-1, 0, 0, 2, 29, 0, 0, -13}, + { 0, 2,-2, 1, 0, 0, 1, 0,-1, 0, 0, 0, 0, -4, 0, 0, 2}, + { 0, 0, 0, 0, 0, -8, 16, 0, 0, 0, 0, 0, 2, -6, 0, 0, 3}, + { 0, 0, 0, 0, 0, 0, 3, 0, 2,-5, 0, 0, 2, 5, 12, 5, -2}, + { 0, 0, 0, 0, 0, 0, 7, -8, 3, 0, 0, 0, 2, -25, 0, 0, 11}, + { 0, 0, 0, 0, 0, 0, -5, 16,-4,-5, 0, 0, 2, -3, 0, 0, 1}, + + /* 551-560 */ + { 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 2, 0, 4, 2, 0}, + { 0, 0, 0, 0, 0, 0, -1, 8,-3, 0, 0, 0, 2, -22, 12, 5, 10}, + { 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 2, 50, 0, 0, -22}, + { 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 1, 0, 7, 4, 0}, + { 0, 0, 0, 0, 0, -8, 10, 0, 0, 0, 0, 0, 2, 0, 3, 1, 0}, + { 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2, -4, 4, 2, 2}, + { 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2, -5, -11, -5, 2}, + { 0, 0, 0, 0, 0, -3, 8, 0, 0, 0, 0, 0, 2, 0, 4, 2, 0}, + { 0, 0, 0, 0, 0, -5, 5, 0, 0, 0, 0, 0, 1, 4, 17, 9, -2}, + { 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 0, 59, 0, 0, 0}, + + /* 561-570 */ + { 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 1, 0, -4, -2, 0}, + { 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 2, -8, 0, 0, 4}, + { 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 4, -15, -8, -2}, + { 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 370, -8, 0,-160}, + { 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 2, 0, 0, -3, 0}, + { 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 2, 0, 3, 1, 0}, + { 0, 0, 0, 0, 0, 0, 6, -5, 0, 0, 0, 0, 2, -6, 3, 1, 3}, + { 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0}, + { 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 2, -10, 0, 0, 4}, + + /* 571-580 */ + { 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 0, 2, 0, 9, 4, 0}, + { 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 2, 4, 17, 7, -2}, + { 0, 0, 0, 0, 0, -9, 11, 0, 0, 0, 0, 0, 2, 34, 0, 0, -15}, + { 0, 0, 0, 0, 0, -9, 11, 0, 0, 0, 0, 0, 1, 0, 5, 3, 0}, + { 0, 0, 0, 0, 0, 0, 4, 0,-4, 0, 0, 0, 2, -5, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 4, 0,-3, 0, 0, 0, 2, -37, -7, -3, 16}, + { 0, 0, 0, 0, 0, -6, 6, 0, 0, 0, 0, 0, 1, 3, 13, 7, -2}, + { 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0}, + { 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 1, 0, -3, -2, 0}, + { 0, 0, 0, 0, 0, 0, 4, 0,-2, 0, 0, 0, 2, -184, -3, -1, 80}, + + /* 581-590 */ + { 0, 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 2, -3, 0, 0, 1}, + { 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0}, + { 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 1, 0, -10, -6, -1}, + { 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 2, 31, -6, 0, -13}, + { 0, 0, 0, 0, 0, 0, 4, 0,-1, 0, 0, 0, 2, -3, -32, -14, 1}, + { 0, 0, 0, 0, 0, 0, 4, 0, 0,-2, 0, 0, 2, -7, 0, 0, 3}, + { 0, 0, 0, 0, 0, 0, 5, -2, 0, 0, 0, 0, 2, 0, -8, -4, 0}, + { 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 3, -4, 0, 0}, + { 0, 0, 0, 0, 0, 8, -9, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0}, + { 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 0, 2, 0, 3, 1, 0}, + + /* 591-600 */ + { 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 2, 19, -23, -10, 2}, + { 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, -10}, + { 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1, 0, 3, 2, 0}, + { 0, 0, 0, 0, 0, -7, 7, 0, 0, 0, 0, 0, 1, 0, 9, 5, -1}, + { 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0}, + { 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 1, 0, -7, -4, 0}, + { 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 2, 8, -4, 0, -4}, + { 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0}, + { 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0}, + { 0, 0, 0, 0, 0, 0, 5, 0,-4, 0, 0, 0, 2, -3, 0, 0, 1}, + + /* 601-610 */ + { 0, 0, 0, 0, 0, 0, 5, 0,-3, 0, 0, 0, 2, -9, 0, 1, 4}, + { 0, 0, 0, 0, 0, 0, 5, 0,-2, 0, 0, 0, 2, 3, 12, 5, -1}, + { 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 17, -3, -1, 0}, + { 0, 0, 0, 0, 0, -8, 8, 0, 0, 0, 0, 0, 1, 0, 7, 4, 0}, + { 0, 0, 0, 0, 0, 8, -8, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0}, + { 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 1, 0, -5, -3, 0}, + { 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 2, 14, -3, 0, -1}, + { 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0}, + { 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, -5}, + { 0, 0, 0, 0, 0, -9, 9, 0, 0, 0, 0, 0, 1, 0, 5, 3, 0}, + + /* 611-620 */ + { 0, 0, 0, 0, 0, 9, -9, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0}, + { 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 0, 1, 0, -3, -2, 0}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2, 2, 9, 4, 3}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1, 0, 4, 2, 0}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2, 6, 0, 0, -3}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1, 0, 3, 1, 0}, + { 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2, 5, 0, 0, -2}, + + /* 621-630 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 0, -1}, + { 1, 0,-2, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, -3, 0, 0, 0}, + { 1, 0,-2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0}, + { 1, 0,-2, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 7, 0, 0, 0}, + { 1, 0,-2, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, -4, 0, 0, 0}, + {-1, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0}, + {-1, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 6, 0, 0, 0}, + {-1, 0, 2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, -4, 0, 0}, + { 1, 0,-2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, -4, 0, 0}, + {-2, 0, 2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0, 5, 0, 0, 0}, + + /* 631-640 */ + {-1, 0, 0, 0, 0, 0, 2, 0,-3, 0, 0, 0, 0, -3, 0, 0, 0}, + {-1, 0, 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 4, 0, 0, 0}, + {-1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0}, + {-1, 0, 2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0}, + { 1,-1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0}, + {-1, 0, 2, 0, 0, 0, 2, 0,-3, 0, 0, 0, 0, 13, 0, 0, 0}, + {-2, 0, 0, 0, 0, 0, 2, 0,-3, 0, 0, 0, 0, 21, 11, 0, 0}, + { 1, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, -5, 0, 0}, + {-1, 1,-1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -5, -2, 0}, + { 1, 1,-1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 5, 3, 0}, + + /* 641-650 */ + {-1, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, -5, 0, 0}, + {-1, 0, 2, 1, 0, 0, 2, 0,-2, 0, 0, 0, 0, -3, 0, 0, 2}, + { 0, 0, 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 20, 10, 0, 0}, + {-1, 0, 2, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, -34, 0, 0, 0}, + {-1, 0, 2, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0, -19, 0, 0, 0}, + { 1, 0,-2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0, 3, 0, 0, -2}, + { 1, 2,-2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0, -3, 0, 0, 1}, + { 1, 2,-2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0, -6, 0, 0, 3}, + { 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, -4, 0, 0, 0}, + { 1, 0, 0, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 3, 0, 0, 0}, + + /* 651-660 */ + { 0, 0,-2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0}, + { 0, 0,-2, 0, 0, 0, 1, 0,-1, 0, 0, 0, 0, 4, 0, 0, 0}, + { 0, 2, 0, 2, 0, -2, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1}, + { 0, 2, 0, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0, 6, 0, 0, -3}, + { 0, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0, -8, 0, 0, 3}, + { 0, 2, 0, 2, 0, -2, 3, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0}, + { 0, 0, 2, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, -3, 0, 0, 0}, + { 0, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -3, -2, 0}, + { 1, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 126, -63, -27, -55}, + {-1, 2, 0, 2, 0, 10, -3, 0, 0, 0, 0, 0, 0, -5, 0, 1, 2}, + + /* 661-670 */ + { 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, -3, 28, 15, 2}, + { 1, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 5, 0, 1, -2}, + { 0, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, 9, 4, 1}, + { 0, 2, 0, 2, 0, 0, -4, 8,-3, 0, 0, 0, 0, 0, 9, 4, -1}, + {-1, 2, 0, 2, 0, 0, -4, 8,-3, 0, 0, 0, 0, -126, -63, -27, 55}, + { 2, 2,-2, 2, 0, 0, -2, 0, 3, 0, 0, 0, 0, 3, 0, 0, -1}, + { 1, 2, 0, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0, 21, -11, -6, -11}, + { 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -4, 0, 0}, + {-1, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, -21, -11, -6, 11}, + {-2, 2, 2, 2, 0, 0, 2, 0,-2, 0, 0, 0, 0, -3, 0, 0, 1}, + + /* 671-680 */ + { 0, 2, 0, 2, 0, 2, -3, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0}, + { 0, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0, 8, 0, 0, -4}, + { 0, 2, 0, 2, 0, 0, 1, 0,-1, 0, 0, 0, 0, -6, 0, 0, 3}, + { 0, 2, 0, 2, 0, 2, -2, 0, 0, 0, 0, 0, 0, -3, 0, 0, 1}, + {-1, 2, 2, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0, 3, 0, 0, -1}, + { 1, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0, -3, 0, 0, 1}, + {-1, 2, 2, 2, 0, 0, 2, 0,-3, 0, 0, 0, 0, -5, 0, 0, 2}, + { 2, 2, 0, 2, 0, 0, 2, 0,-3, 0, 0, 0, 0, 24, -12, -5, -11}, + { 1, 2, 0, 2, 0, 0, -4, 8,-3, 0, 0, 0, 0, 0, 3, 1, 0}, + { 1, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0, 0, 3, 1, 0}, + + /* 681-687 */ + { 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 2, 0}, + { 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, -24, -12, -5, 10}, + { 2, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 4, 0, -1, -2}, + {-1, 2, 2, 2, 0, 0, 2, 0,-2, 0, 0, 0, 0, 13, 0, 0, -6}, + {-1, 2, 2, 2, 0, 3, -3, 0, 0, 0, 0, 0, 0, 7, 0, 0, -3}, + { 1, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1}, + { 0, 2, 2, 2, 0, 0, 2, 0,-2, 0, 0, 0, 0, 3, 0, 0, -1} + }; + +/* Number of terms in the planetary nutation model */ + const int NPL = (int) (sizeof xpl / sizeof xpl[0]); + +/*--------------------------------------------------------------------*/ + +/* Interval between fundamental date J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* ------------------- */ +/* LUNI-SOLAR NUTATION */ +/* ------------------- */ + +/* Fundamental (Delaunay) arguments */ + +/* Mean anomaly of the Moon (IERS 2003). */ + el = eraFal03(t); + +/* Mean anomaly of the Sun (MHB2000). */ + elp = fmod(1287104.79305 + + t * (129596581.0481 + + t * (-0.5532 + + t * (0.000136 + + t * (-0.00001149)))), ERFA_TURNAS) * ERFA_DAS2R; + +/* Mean longitude of the Moon minus that of the ascending node */ +/* (IERS 2003. */ + f = eraFaf03(t); + +/* Mean elongation of the Moon from the Sun (MHB2000). */ + d = fmod(1072260.70369 + + t * (1602961601.2090 + + t * (-6.3706 + + t * (0.006593 + + t * (-0.00003169)))), ERFA_TURNAS) * ERFA_DAS2R; + +/* Mean longitude of the ascending node of the Moon (IERS 2003). */ + om = eraFaom03(t); + +/* Initialize the nutation values. */ + dp = 0.0; + de = 0.0; + +/* Summation of luni-solar nutation series (in reverse order). */ + for (i = NLS-1; i >= 0; i--) { + + /* Argument and functions. */ + arg = fmod((double)xls[i].nl * el + + (double)xls[i].nlp * elp + + (double)xls[i].nf * f + + (double)xls[i].nd * d + + (double)xls[i].nom * om, ERFA_D2PI); + sarg = sin(arg); + carg = cos(arg); + + /* Term. */ + dp += (xls[i].sp + xls[i].spt * t) * sarg + xls[i].cp * carg; + de += (xls[i].ce + xls[i].cet * t) * carg + xls[i].se * sarg; + } + +/* Convert from 0.1 microarcsec units to radians. */ + dpsils = dp * U2R; + depsls = de * U2R; + +/* ------------------ */ +/* PLANETARY NUTATION */ +/* ------------------ */ + +/* n.b. The MHB2000 code computes the luni-solar and planetary nutation */ +/* in different functions, using slightly different Delaunay */ +/* arguments in the two cases. This behaviour is faithfully */ +/* reproduced here. Use of the IERS 2003 expressions for both */ +/* cases leads to negligible changes, well below */ +/* 0.1 microarcsecond. */ + +/* Mean anomaly of the Moon (MHB2000). */ + al = fmod(2.35555598 + 8328.6914269554 * t, ERFA_D2PI); + +/* Mean longitude of the Moon minus that of the ascending node */ +/*(MHB2000). */ + af = fmod(1.627905234 + 8433.466158131 * t, ERFA_D2PI); + +/* Mean elongation of the Moon from the Sun (MHB2000). */ + ad = fmod(5.198466741 + 7771.3771468121 * t, ERFA_D2PI); + +/* Mean longitude of the ascending node of the Moon (MHB2000). */ + aom = fmod(2.18243920 - 33.757045 * t, ERFA_D2PI); + +/* General accumulated precession in longitude (IERS 2003). */ + apa = eraFapa03(t); + +/* Planetary longitudes, Mercury through Uranus (IERS 2003). */ + alme = eraFame03(t); + alve = eraFave03(t); + alea = eraFae03(t); + alma = eraFama03(t); + alju = eraFaju03(t); + alsa = eraFasa03(t); + alur = eraFaur03(t); + +/* Neptune longitude (MHB2000). */ + alne = fmod(5.321159000 + 3.8127774000 * t, ERFA_D2PI); + +/* Initialize the nutation values. */ + dp = 0.0; + de = 0.0; + +/* Summation of planetary nutation series (in reverse order). */ + for (i = NPL-1; i >= 0; i--) { + + /* Argument and functions. */ + arg = fmod((double)xpl[i].nl * al + + (double)xpl[i].nf * af + + (double)xpl[i].nd * ad + + (double)xpl[i].nom * aom + + (double)xpl[i].nme * alme + + (double)xpl[i].nve * alve + + (double)xpl[i].nea * alea + + (double)xpl[i].nma * alma + + (double)xpl[i].nju * alju + + (double)xpl[i].nsa * alsa + + (double)xpl[i].nur * alur + + (double)xpl[i].nne * alne + + (double)xpl[i].npa * apa, ERFA_D2PI); + sarg = sin(arg); + carg = cos(arg); + + /* Term. */ + dp += (double)xpl[i].sp * sarg + (double)xpl[i].cp * carg; + de += (double)xpl[i].se * sarg + (double)xpl[i].ce * carg; + + } + +/* Convert from 0.1 microarcsec units to radians. */ + dpsipl = dp * U2R; + depspl = de * U2R; + +/* ------- */ +/* RESULTS */ +/* ------- */ + +/* Add luni-solar and planetary components. */ + *dpsi = dpsils + dpsipl; + *deps = depsls + depspl; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/nut00b.c b/erfa/nut00b.c new file mode 100644 index 0000000..ec82bad --- /dev/null +++ b/erfa/nut00b.c @@ -0,0 +1,381 @@ +#include "erfa.h" + +void eraNut00b(double date1, double date2, double *dpsi, double *deps) +/* +** - - - - - - - - - - +** e r a N u t 0 0 b +** - - - - - - - - - - +** +** Nutation, IAU 2000B model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsi,deps double nutation, luni-solar + planetary (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The nutation components in longitude and obliquity are in radians +** and with respect to the equinox and ecliptic of date. The +** obliquity at J2000.0 is assumed to be the Lieske et al. (1977) +** value of 84381.448 arcsec. (The errors that result from using +** this function with the IAU 2006 value of 84381.406 arcsec can be +** neglected.) +** +** The nutation model consists only of luni-solar terms, but +** includes also a fixed offset which compensates for certain long- +** period planetary terms (Note 7). +** +** 3) This function is an implementation of the IAU 2000B abridged +** nutation model formally adopted by the IAU General Assembly in +** 2000. The function computes the MHB_2000_SHORT luni-solar +** nutation series (Luzum 2001), but without the associated +** corrections for the precession rate adjustments and the offset +** between the GCRS and J2000.0 mean poles. +** +** 4) The full IAU 2000A (MHB2000) nutation model contains nearly 1400 +** terms. The IAU 2000B model (McCarthy & Luzum 2003) contains only +** 77 terms, plus additional simplifications, yet still delivers +** results of 1 mas accuracy at present epochs. This combination of +** accuracy and size makes the IAU 2000B abridged nutation model +** suitable for most practical applications. +** +** The function delivers a pole accurate to 1 mas from 1900 to 2100 +** (usually better than 1 mas, very occasionally just outside +** 1 mas). The full IAU 2000A model, which is implemented in the +** function eraNut00a (q.v.), delivers considerably greater accuracy +** at current dates; however, to realize this improved accuracy, +** corrections for the essentially unpredictable free-core-nutation +** (FCN) must also be included. +** +** 5) The present function provides classical nutation. The +** MHB_2000_SHORT algorithm, from which it is adapted, deals also +** with (i) the offsets between the GCRS and mean poles and (ii) the +** adjustments in longitude and obliquity due to the changed +** precession rates. These additional functions, namely frame bias +** and precession adjustments, are supported by the ERFA functions +** eraBi00 and eraPr00. +** +** 6) The MHB_2000_SHORT algorithm also provides "total" nutations, +** comprising the arithmetic sum of the frame bias, precession +** adjustments, and nutation (luni-solar + planetary). These total +** nutations can be used in combination with an existing IAU 1976 +** precession implementation, such as eraPmat76, to deliver GCRS- +** to-true predictions of mas accuracy at current epochs. However, +** for symmetry with the eraNut00a function (q.v. for the reasons), +** the ERFA functions do not generate the "total nutations" +** directly. Should they be required, they could of course easily +** be generated by calling eraBi00, eraPr00 and the present function +** and adding the results. +** +** 7) The IAU 2000B model includes "planetary bias" terms that are +** fixed in size but compensate for long-period nutations. The +** amplitudes quoted in McCarthy & Luzum (2003), namely +** Dpsi = -1.5835 mas and Depsilon = +1.6339 mas, are optimized for +** the "total nutations" method described in Note 6. The Luzum +** (2001) values used in this ERFA implementation, namely -0.135 mas +** and +0.388 mas, are optimized for the "rigorous" method, where +** frame bias, precession and nutation are applied separately and in +** that order. During the interval 1995-2050, the ERFA +** implementation delivers a maximum error of 1.001 mas (not +** including FCN). +** +** References: +** +** Lieske, J.H., Lederle, T., Fricke, W., Morando, B., "Expressions +** for the precession quantities based upon the IAU /1976/ system of +** astronomical constants", Astron.Astrophys. 58, 1-2, 1-16. (1977) +** +** Luzum, B., private communication, 2001 (Fortran code +** MHB_2000_SHORT) +** +** McCarthy, D.D. & Luzum, B.J., "An abridged model of the +** precession-nutation of the celestial pole", Cel.Mech.Dyn.Astron. +** 85, 37-49 (2003) +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J., Astron.Astrophys. 282, 663-683 (1994) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, el, elp, f, d, om, arg, dp, de, sarg, carg, + dpsils, depsls, dpsipl, depspl; + int i; + +/* Units of 0.1 microarcsecond to radians */ + static const double U2R = ERFA_DAS2R / 1e7; + +/* ---------------------------------------- */ +/* Fixed offsets in lieu of planetary terms */ +/* ---------------------------------------- */ + + static const double DPPLAN = -0.135 * ERFA_DMAS2R; + static const double DEPLAN = 0.388 * ERFA_DMAS2R; + +/* --------------------------------------------------- */ +/* Luni-solar nutation: argument and term coefficients */ +/* --------------------------------------------------- */ + +/* The units for the sine and cosine coefficients are */ +/* 0.1 microarcsec and the same per Julian century */ + + static const struct { + int nl,nlp,nf,nd,nom; /* coefficients of l,l',F,D,Om */ + double ps,pst,pc; /* longitude sin, t*sin, cos coefficients */ + double ec,ect,es; /* obliquity cos, t*cos, sin coefficients */ + + } x[] = { + + /* 1-10 */ + { 0, 0, 0, 0,1, + -172064161.0, -174666.0, 33386.0, 92052331.0, 9086.0, 15377.0}, + { 0, 0, 2,-2,2, + -13170906.0, -1675.0, -13696.0, 5730336.0, -3015.0, -4587.0}, + { 0, 0, 2, 0,2,-2276413.0,-234.0, 2796.0, 978459.0,-485.0,1374.0}, + { 0, 0, 0, 0,2,2074554.0, 207.0, -698.0,-897492.0, 470.0,-291.0}, + { 0, 1, 0, 0,0,1475877.0,-3633.0,11817.0, 73871.0,-184.0,-1924.0}, + { 0, 1, 2,-2,2,-516821.0, 1226.0, -524.0, 224386.0,-677.0,-174.0}, + { 1, 0, 0, 0,0, 711159.0, 73.0, -872.0, -6750.0, 0.0, 358.0}, + { 0, 0, 2, 0,1,-387298.0, -367.0, 380.0, 200728.0, 18.0, 318.0}, + { 1, 0, 2, 0,2,-301461.0, -36.0, 816.0, 129025.0, -63.0, 367.0}, + { 0,-1, 2,-2,2, 215829.0, -494.0, 111.0, -95929.0, 299.0, 132.0}, + + /* 11-20 */ + { 0, 0, 2,-2,1, 128227.0, 137.0, 181.0, -68982.0, -9.0, 39.0}, + {-1, 0, 2, 0,2, 123457.0, 11.0, 19.0, -53311.0, 32.0, -4.0}, + {-1, 0, 0, 2,0, 156994.0, 10.0, -168.0, -1235.0, 0.0, 82.0}, + { 1, 0, 0, 0,1, 63110.0, 63.0, 27.0, -33228.0, 0.0, -9.0}, + {-1, 0, 0, 0,1, -57976.0, -63.0, -189.0, 31429.0, 0.0, -75.0}, + {-1, 0, 2, 2,2, -59641.0, -11.0, 149.0, 25543.0, -11.0, 66.0}, + { 1, 0, 2, 0,1, -51613.0, -42.0, 129.0, 26366.0, 0.0, 78.0}, + {-2, 0, 2, 0,1, 45893.0, 50.0, 31.0, -24236.0, -10.0, 20.0}, + { 0, 0, 0, 2,0, 63384.0, 11.0, -150.0, -1220.0, 0.0, 29.0}, + { 0, 0, 2, 2,2, -38571.0, -1.0, 158.0, 16452.0, -11.0, 68.0}, + + /* 21-30 */ + { 0,-2, 2,-2,2, 32481.0, 0.0, 0.0, -13870.0, 0.0, 0.0}, + {-2, 0, 0, 2,0, -47722.0, 0.0, -18.0, 477.0, 0.0, -25.0}, + { 2, 0, 2, 0,2, -31046.0, -1.0, 131.0, 13238.0, -11.0, 59.0}, + { 1, 0, 2,-2,2, 28593.0, 0.0, -1.0, -12338.0, 10.0, -3.0}, + {-1, 0, 2, 0,1, 20441.0, 21.0, 10.0, -10758.0, 0.0, -3.0}, + { 2, 0, 0, 0,0, 29243.0, 0.0, -74.0, -609.0, 0.0, 13.0}, + { 0, 0, 2, 0,0, 25887.0, 0.0, -66.0, -550.0, 0.0, 11.0}, + { 0, 1, 0, 0,1, -14053.0, -25.0, 79.0, 8551.0, -2.0, -45.0}, + {-1, 0, 0, 2,1, 15164.0, 10.0, 11.0, -8001.0, 0.0, -1.0}, + { 0, 2, 2,-2,2, -15794.0, 72.0, -16.0, 6850.0, -42.0, -5.0}, + + /* 31-40 */ + { 0, 0,-2, 2,0, 21783.0, 0.0, 13.0, -167.0, 0.0, 13.0}, + { 1, 0, 0,-2,1, -12873.0, -10.0, -37.0, 6953.0, 0.0, -14.0}, + { 0,-1, 0, 0,1, -12654.0, 11.0, 63.0, 6415.0, 0.0, 26.0}, + {-1, 0, 2, 2,1, -10204.0, 0.0, 25.0, 5222.0, 0.0, 15.0}, + { 0, 2, 0, 0,0, 16707.0, -85.0, -10.0, 168.0, -1.0, 10.0}, + { 1, 0, 2, 2,2, -7691.0, 0.0, 44.0, 3268.0, 0.0, 19.0}, + {-2, 0, 2, 0,0, -11024.0, 0.0, -14.0, 104.0, 0.0, 2.0}, + { 0, 1, 2, 0,2, 7566.0, -21.0, -11.0, -3250.0, 0.0, -5.0}, + { 0, 0, 2, 2,1, -6637.0, -11.0, 25.0, 3353.0, 0.0, 14.0}, + { 0,-1, 2, 0,2, -7141.0, 21.0, 8.0, 3070.0, 0.0, 4.0}, + + /* 41-50 */ + { 0, 0, 0, 2,1, -6302.0, -11.0, 2.0, 3272.0, 0.0, 4.0}, + { 1, 0, 2,-2,1, 5800.0, 10.0, 2.0, -3045.0, 0.0, -1.0}, + { 2, 0, 2,-2,2, 6443.0, 0.0, -7.0, -2768.0, 0.0, -4.0}, + {-2, 0, 0, 2,1, -5774.0, -11.0, -15.0, 3041.0, 0.0, -5.0}, + { 2, 0, 2, 0,1, -5350.0, 0.0, 21.0, 2695.0, 0.0, 12.0}, + { 0,-1, 2,-2,1, -4752.0, -11.0, -3.0, 2719.0, 0.0, -3.0}, + { 0, 0, 0,-2,1, -4940.0, -11.0, -21.0, 2720.0, 0.0, -9.0}, + {-1,-1, 0, 2,0, 7350.0, 0.0, -8.0, -51.0, 0.0, 4.0}, + { 2, 0, 0,-2,1, 4065.0, 0.0, 6.0, -2206.0, 0.0, 1.0}, + { 1, 0, 0, 2,0, 6579.0, 0.0, -24.0, -199.0, 0.0, 2.0}, + + /* 51-60 */ + { 0, 1, 2,-2,1, 3579.0, 0.0, 5.0, -1900.0, 0.0, 1.0}, + { 1,-1, 0, 0,0, 4725.0, 0.0, -6.0, -41.0, 0.0, 3.0}, + {-2, 0, 2, 0,2, -3075.0, 0.0, -2.0, 1313.0, 0.0, -1.0}, + { 3, 0, 2, 0,2, -2904.0, 0.0, 15.0, 1233.0, 0.0, 7.0}, + { 0,-1, 0, 2,0, 4348.0, 0.0, -10.0, -81.0, 0.0, 2.0}, + { 1,-1, 2, 0,2, -2878.0, 0.0, 8.0, 1232.0, 0.0, 4.0}, + { 0, 0, 0, 1,0, -4230.0, 0.0, 5.0, -20.0, 0.0, -2.0}, + {-1,-1, 2, 2,2, -2819.0, 0.0, 7.0, 1207.0, 0.0, 3.0}, + {-1, 0, 2, 0,0, -4056.0, 0.0, 5.0, 40.0, 0.0, -2.0}, + { 0,-1, 2, 2,2, -2647.0, 0.0, 11.0, 1129.0, 0.0, 5.0}, + + /* 61-70 */ + {-2, 0, 0, 0,1, -2294.0, 0.0, -10.0, 1266.0, 0.0, -4.0}, + { 1, 1, 2, 0,2, 2481.0, 0.0, -7.0, -1062.0, 0.0, -3.0}, + { 2, 0, 0, 0,1, 2179.0, 0.0, -2.0, -1129.0, 0.0, -2.0}, + {-1, 1, 0, 1,0, 3276.0, 0.0, 1.0, -9.0, 0.0, 0.0}, + { 1, 1, 0, 0,0, -3389.0, 0.0, 5.0, 35.0, 0.0, -2.0}, + { 1, 0, 2, 0,0, 3339.0, 0.0, -13.0, -107.0, 0.0, 1.0}, + {-1, 0, 2,-2,1, -1987.0, 0.0, -6.0, 1073.0, 0.0, -2.0}, + { 1, 0, 0, 0,2, -1981.0, 0.0, 0.0, 854.0, 0.0, 0.0}, + {-1, 0, 0, 1,0, 4026.0, 0.0, -353.0, -553.0, 0.0,-139.0}, + { 0, 0, 2, 1,2, 1660.0, 0.0, -5.0, -710.0, 0.0, -2.0}, + + /* 71-77 */ + {-1, 0, 2, 4,2, -1521.0, 0.0, 9.0, 647.0, 0.0, 4.0}, + {-1, 1, 0, 1,1, 1314.0, 0.0, 0.0, -700.0, 0.0, 0.0}, + { 0,-2, 2,-2,1, -1283.0, 0.0, 0.0, 672.0, 0.0, 0.0}, + { 1, 0, 2, 2,1, -1331.0, 0.0, 8.0, 663.0, 0.0, 4.0}, + {-2, 0, 2, 2,2, 1383.0, 0.0, -2.0, -594.0, 0.0, -2.0}, + {-1, 0, 0, 0,2, 1405.0, 0.0, 4.0, -610.0, 0.0, 2.0}, + { 1, 1, 2,-2,2, 1290.0, 0.0, 0.0, -556.0, 0.0, 0.0} + }; + +/* Number of terms in the series */ + const int NLS = (int) (sizeof x / sizeof x[0]); + +/*--------------------------------------------------------------------*/ + +/* Interval between fundamental epoch J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* --------------------*/ +/* LUNI-SOLAR NUTATION */ +/* --------------------*/ + +/* Fundamental (Delaunay) arguments from Simon et al. (1994) */ + +/* Mean anomaly of the Moon. */ + el = fmod(485868.249036 + (1717915923.2178) * t, ERFA_TURNAS) * ERFA_DAS2R; + +/* Mean anomaly of the Sun. */ + elp = fmod(1287104.79305 + (129596581.0481) * t, ERFA_TURNAS) * ERFA_DAS2R; + +/* Mean argument of the latitude of the Moon. */ + f = fmod(335779.526232 + (1739527262.8478) * t, ERFA_TURNAS) * ERFA_DAS2R; + +/* Mean elongation of the Moon from the Sun. */ + d = fmod(1072260.70369 + (1602961601.2090) * t, ERFA_TURNAS) * ERFA_DAS2R; + +/* Mean longitude of the ascending node of the Moon. */ + om = fmod(450160.398036 + (-6962890.5431) * t, ERFA_TURNAS) * ERFA_DAS2R; + +/* Initialize the nutation values. */ + dp = 0.0; + de = 0.0; + +/* Summation of luni-solar nutation series (smallest terms first). */ + for (i = NLS-1; i >= 0; i--) { + + /* Argument and functions. */ + arg = fmod( (double)x[i].nl * el + + (double)x[i].nlp * elp + + (double)x[i].nf * f + + (double)x[i].nd * d + + (double)x[i].nom * om, ERFA_D2PI ); + sarg = sin(arg); + carg = cos(arg); + + /* Term. */ + dp += (x[i].ps + x[i].pst * t) * sarg + x[i].pc * carg; + de += (x[i].ec + x[i].ect * t) * carg + x[i].es * sarg; + } + +/* Convert from 0.1 microarcsec units to radians. */ + dpsils = dp * U2R; + depsls = de * U2R; + +/* ------------------------------*/ +/* IN LIEU OF PLANETARY NUTATION */ +/* ------------------------------*/ + +/* Fixed offset to correct for missing terms in truncated series. */ + dpsipl = DPPLAN; + depspl = DEPLAN; + +/* --------*/ +/* RESULTS */ +/* --------*/ + +/* Add luni-solar and planetary components. */ + *dpsi = dpsils + dpsipl; + *deps = depsls + depspl; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/nut06a.c b/erfa/nut06a.c new file mode 100644 index 0000000..8936277 --- /dev/null +++ b/erfa/nut06a.c @@ -0,0 +1,162 @@ +#include "erfa.h" + +void eraNut06a(double date1, double date2, double *dpsi, double *deps) +/* +** - - - - - - - - - - +** e r a N u t 0 6 a +** - - - - - - - - - - +** +** IAU 2000A nutation with adjustments to match the IAU 2006 +** precession. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsi,deps double nutation, luni-solar + planetary (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The nutation components in longitude and obliquity are in radians +** and with respect to the mean equinox and ecliptic of date, +** IAU 2006 precession model (Hilton et al. 2006, Capitaine et al. +** 2005). +** +** 3) The function first computes the IAU 2000A nutation, then applies +** adjustments for (i) the consequences of the change in obliquity +** from the IAU 1980 ecliptic to the IAU 2006 ecliptic and (ii) the +** secular variation in the Earth's dynamical form factor J2. +** +** 4) The present function provides classical nutation, complementing +** the IAU 2000 frame bias and IAU 2006 precession. It delivers a +** pole which is at current epochs accurate to a few tens of +** microarcseconds, apart from the free core nutation. +** +** Called: +** eraNut00a nutation, IAU 2000A +** +** References: +** +** Chapront, J., Chapront-Touze, M. & Francou, G. 2002, +** Astron.Astrophys. 387, 700 +** +** Lieske, J.H., Lederle, T., Fricke, W. & Morando, B. 1977, +** Astron.Astrophys. 58, 1-16 +** +** Mathews, P.M., Herring, T.A., Buffet, B.A. 2002, J.Geophys.Res. +** 107, B4. The MHB_2000 code itself was obtained on 9th September +** 2002 from ftp//maia.usno.navy.mil/conv2000/chapter5/IAU2000A. +** +** Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G., Laskar, J. 1994, Astron.Astrophys. 282, 663-683 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M. 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Wallace, P.T., "Software for Implementing the IAU 2000 +** Resolutions", in IERS Workshop 5.1 (2002) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, fj2, dp, de; + + +/* Interval between fundamental date J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Factor correcting for secular variation of J2. */ + fj2 = -2.7774e-6 * t; + +/* Obtain IAU 2000A nutation. */ + eraNut00a(date1, date2, &dp, &de); + +/* Apply P03 adjustments (Wallace & Capitaine, 2006, Eqs.5). */ + *dpsi = dp + dp * (0.4697e-6 + fj2); + *deps = de + de * fj2; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/nut80.c b/erfa/nut80.c new file mode 100644 index 0000000..c0da4ef --- /dev/null +++ b/erfa/nut80.c @@ -0,0 +1,334 @@ +#include "erfa.h" + +void eraNut80(double date1, double date2, double *dpsi, double *deps) +/* +** - - - - - - - - - +** e r a N u t 8 0 +** - - - - - - - - - +** +** Nutation, IAU 1980 model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsi double nutation in longitude (radians) +** deps double nutation in obliquity (radians) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The nutation components are with respect to the ecliptic of +** date. +** +** Called: +** eraAnpm normalize angle into range +/- pi +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 3.222 (p111). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, el, elp, f, d, om, dp, de, arg, s, c; + int j; + +/* Units of 0.1 milliarcsecond to radians */ + const double U2R = ERFA_DAS2R / 1e4; + +/* ------------------------------------------------ */ +/* Table of multiples of arguments and coefficients */ +/* ------------------------------------------------ */ + +/* The units for the sine and cosine coefficients are 0.1 mas and */ +/* the same per Julian century */ + + static const struct { + int nl,nlp,nf,nd,nom; /* coefficients of l,l',F,D,Om */ + double sp,spt; /* longitude sine, 1 and t coefficients */ + double ce,cet; /* obliquity cosine, 1 and t coefficients */ + } x[] = { + + /* 1-10 */ + { 0, 0, 0, 0, 1, -171996.0, -174.2, 92025.0, 8.9 }, + { 0, 0, 0, 0, 2, 2062.0, 0.2, -895.0, 0.5 }, + { -2, 0, 2, 0, 1, 46.0, 0.0, -24.0, 0.0 }, + { 2, 0, -2, 0, 0, 11.0, 0.0, 0.0, 0.0 }, + { -2, 0, 2, 0, 2, -3.0, 0.0, 1.0, 0.0 }, + { 1, -1, 0, -1, 0, -3.0, 0.0, 0.0, 0.0 }, + { 0, -2, 2, -2, 1, -2.0, 0.0, 1.0, 0.0 }, + { 2, 0, -2, 0, 1, 1.0, 0.0, 0.0, 0.0 }, + { 0, 0, 2, -2, 2, -13187.0, -1.6, 5736.0, -3.1 }, + { 0, 1, 0, 0, 0, 1426.0, -3.4, 54.0, -0.1 }, + + /* 11-20 */ + { 0, 1, 2, -2, 2, -517.0, 1.2, 224.0, -0.6 }, + { 0, -1, 2, -2, 2, 217.0, -0.5, -95.0, 0.3 }, + { 0, 0, 2, -2, 1, 129.0, 0.1, -70.0, 0.0 }, + { 2, 0, 0, -2, 0, 48.0, 0.0, 1.0, 0.0 }, + { 0, 0, 2, -2, 0, -22.0, 0.0, 0.0, 0.0 }, + { 0, 2, 0, 0, 0, 17.0, -0.1, 0.0, 0.0 }, + { 0, 1, 0, 0, 1, -15.0, 0.0, 9.0, 0.0 }, + { 0, 2, 2, -2, 2, -16.0, 0.1, 7.0, 0.0 }, + { 0, -1, 0, 0, 1, -12.0, 0.0, 6.0, 0.0 }, + { -2, 0, 0, 2, 1, -6.0, 0.0, 3.0, 0.0 }, + + /* 21-30 */ + { 0, -1, 2, -2, 1, -5.0, 0.0, 3.0, 0.0 }, + { 2, 0, 0, -2, 1, 4.0, 0.0, -2.0, 0.0 }, + { 0, 1, 2, -2, 1, 4.0, 0.0, -2.0, 0.0 }, + { 1, 0, 0, -1, 0, -4.0, 0.0, 0.0, 0.0 }, + { 2, 1, 0, -2, 0, 1.0, 0.0, 0.0, 0.0 }, + { 0, 0, -2, 2, 1, 1.0, 0.0, 0.0, 0.0 }, + { 0, 1, -2, 2, 0, -1.0, 0.0, 0.0, 0.0 }, + { 0, 1, 0, 0, 2, 1.0, 0.0, 0.0, 0.0 }, + { -1, 0, 0, 1, 1, 1.0, 0.0, 0.0, 0.0 }, + { 0, 1, 2, -2, 0, -1.0, 0.0, 0.0, 0.0 }, + + /* 31-40 */ + { 0, 0, 2, 0, 2, -2274.0, -0.2, 977.0, -0.5 }, + { 1, 0, 0, 0, 0, 712.0, 0.1, -7.0, 0.0 }, + { 0, 0, 2, 0, 1, -386.0, -0.4, 200.0, 0.0 }, + { 1, 0, 2, 0, 2, -301.0, 0.0, 129.0, -0.1 }, + { 1, 0, 0, -2, 0, -158.0, 0.0, -1.0, 0.0 }, + { -1, 0, 2, 0, 2, 123.0, 0.0, -53.0, 0.0 }, + { 0, 0, 0, 2, 0, 63.0, 0.0, -2.0, 0.0 }, + { 1, 0, 0, 0, 1, 63.0, 0.1, -33.0, 0.0 }, + { -1, 0, 0, 0, 1, -58.0, -0.1, 32.0, 0.0 }, + { -1, 0, 2, 2, 2, -59.0, 0.0, 26.0, 0.0 }, + + /* 41-50 */ + { 1, 0, 2, 0, 1, -51.0, 0.0, 27.0, 0.0 }, + { 0, 0, 2, 2, 2, -38.0, 0.0, 16.0, 0.0 }, + { 2, 0, 0, 0, 0, 29.0, 0.0, -1.0, 0.0 }, + { 1, 0, 2, -2, 2, 29.0, 0.0, -12.0, 0.0 }, + { 2, 0, 2, 0, 2, -31.0, 0.0, 13.0, 0.0 }, + { 0, 0, 2, 0, 0, 26.0, 0.0, -1.0, 0.0 }, + { -1, 0, 2, 0, 1, 21.0, 0.0, -10.0, 0.0 }, + { -1, 0, 0, 2, 1, 16.0, 0.0, -8.0, 0.0 }, + { 1, 0, 0, -2, 1, -13.0, 0.0, 7.0, 0.0 }, + { -1, 0, 2, 2, 1, -10.0, 0.0, 5.0, 0.0 }, + + /* 51-60 */ + { 1, 1, 0, -2, 0, -7.0, 0.0, 0.0, 0.0 }, + { 0, 1, 2, 0, 2, 7.0, 0.0, -3.0, 0.0 }, + { 0, -1, 2, 0, 2, -7.0, 0.0, 3.0, 0.0 }, + { 1, 0, 2, 2, 2, -8.0, 0.0, 3.0, 0.0 }, + { 1, 0, 0, 2, 0, 6.0, 0.0, 0.0, 0.0 }, + { 2, 0, 2, -2, 2, 6.0, 0.0, -3.0, 0.0 }, + { 0, 0, 0, 2, 1, -6.0, 0.0, 3.0, 0.0 }, + { 0, 0, 2, 2, 1, -7.0, 0.0, 3.0, 0.0 }, + { 1, 0, 2, -2, 1, 6.0, 0.0, -3.0, 0.0 }, + { 0, 0, 0, -2, 1, -5.0, 0.0, 3.0, 0.0 }, + + /* 61-70 */ + { 1, -1, 0, 0, 0, 5.0, 0.0, 0.0, 0.0 }, + { 2, 0, 2, 0, 1, -5.0, 0.0, 3.0, 0.0 }, + { 0, 1, 0, -2, 0, -4.0, 0.0, 0.0, 0.0 }, + { 1, 0, -2, 0, 0, 4.0, 0.0, 0.0, 0.0 }, + { 0, 0, 0, 1, 0, -4.0, 0.0, 0.0, 0.0 }, + { 1, 1, 0, 0, 0, -3.0, 0.0, 0.0, 0.0 }, + { 1, 0, 2, 0, 0, 3.0, 0.0, 0.0, 0.0 }, + { 1, -1, 2, 0, 2, -3.0, 0.0, 1.0, 0.0 }, + { -1, -1, 2, 2, 2, -3.0, 0.0, 1.0, 0.0 }, + { -2, 0, 0, 0, 1, -2.0, 0.0, 1.0, 0.0 }, + + /* 71-80 */ + { 3, 0, 2, 0, 2, -3.0, 0.0, 1.0, 0.0 }, + { 0, -1, 2, 2, 2, -3.0, 0.0, 1.0, 0.0 }, + { 1, 1, 2, 0, 2, 2.0, 0.0, -1.0, 0.0 }, + { -1, 0, 2, -2, 1, -2.0, 0.0, 1.0, 0.0 }, + { 2, 0, 0, 0, 1, 2.0, 0.0, -1.0, 0.0 }, + { 1, 0, 0, 0, 2, -2.0, 0.0, 1.0, 0.0 }, + { 3, 0, 0, 0, 0, 2.0, 0.0, 0.0, 0.0 }, + { 0, 0, 2, 1, 2, 2.0, 0.0, -1.0, 0.0 }, + { -1, 0, 0, 0, 2, 1.0, 0.0, -1.0, 0.0 }, + { 1, 0, 0, -4, 0, -1.0, 0.0, 0.0, 0.0 }, + + /* 81-90 */ + { -2, 0, 2, 2, 2, 1.0, 0.0, -1.0, 0.0 }, + { -1, 0, 2, 4, 2, -2.0, 0.0, 1.0, 0.0 }, + { 2, 0, 0, -4, 0, -1.0, 0.0, 0.0, 0.0 }, + { 1, 1, 2, -2, 2, 1.0, 0.0, -1.0, 0.0 }, + { 1, 0, 2, 2, 1, -1.0, 0.0, 1.0, 0.0 }, + { -2, 0, 2, 4, 2, -1.0, 0.0, 1.0, 0.0 }, + { -1, 0, 4, 0, 2, 1.0, 0.0, 0.0, 0.0 }, + { 1, -1, 0, -2, 0, 1.0, 0.0, 0.0, 0.0 }, + { 2, 0, 2, -2, 1, 1.0, 0.0, -1.0, 0.0 }, + { 2, 0, 2, 2, 2, -1.0, 0.0, 0.0, 0.0 }, + + /* 91-100 */ + { 1, 0, 0, 2, 1, -1.0, 0.0, 0.0, 0.0 }, + { 0, 0, 4, -2, 2, 1.0, 0.0, 0.0, 0.0 }, + { 3, 0, 2, -2, 2, 1.0, 0.0, 0.0, 0.0 }, + { 1, 0, 2, -2, 0, -1.0, 0.0, 0.0, 0.0 }, + { 0, 1, 2, 0, 1, 1.0, 0.0, 0.0, 0.0 }, + { -1, -1, 0, 2, 1, 1.0, 0.0, 0.0, 0.0 }, + { 0, 0, -2, 0, 1, -1.0, 0.0, 0.0, 0.0 }, + { 0, 0, 2, -1, 2, -1.0, 0.0, 0.0, 0.0 }, + { 0, 1, 0, 2, 0, -1.0, 0.0, 0.0, 0.0 }, + { 1, 0, -2, -2, 0, -1.0, 0.0, 0.0, 0.0 }, + + /* 101-106 */ + { 0, -1, 2, 0, 1, -1.0, 0.0, 0.0, 0.0 }, + { 1, 1, 0, -2, 1, -1.0, 0.0, 0.0, 0.0 }, + { 1, 0, -2, 2, 0, -1.0, 0.0, 0.0, 0.0 }, + { 2, 0, 0, 2, 0, 1.0, 0.0, 0.0, 0.0 }, + { 0, 0, 2, 4, 2, -1.0, 0.0, 0.0, 0.0 }, + { 0, 1, 0, 1, 0, 1.0, 0.0, 0.0, 0.0 } + }; + +/* Number of terms in the series */ + const int NT = (int) (sizeof x / sizeof x[0]); + +/*--------------------------------------------------------------------*/ + +/* Interval between fundamental epoch J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* --------------------- */ +/* Fundamental arguments */ +/* --------------------- */ + +/* Mean longitude of Moon minus mean longitude of Moon's perigee. */ + el = eraAnpm( + (485866.733 + (715922.633 + (31.310 + 0.064 * t) * t) * t) + * ERFA_DAS2R + fmod(1325.0 * t, 1.0) * ERFA_D2PI); + +/* Mean longitude of Sun minus mean longitude of Sun's perigee. */ + elp = eraAnpm( + (1287099.804 + (1292581.224 + (-0.577 - 0.012 * t) * t) * t) + * ERFA_DAS2R + fmod(99.0 * t, 1.0) * ERFA_D2PI); + +/* Mean longitude of Moon minus mean longitude of Moon's node. */ + f = eraAnpm( + (335778.877 + (295263.137 + (-13.257 + 0.011 * t) * t) * t) + * ERFA_DAS2R + fmod(1342.0 * t, 1.0) * ERFA_D2PI); + +/* Mean elongation of Moon from Sun. */ + d = eraAnpm( + (1072261.307 + (1105601.328 + (-6.891 + 0.019 * t) * t) * t) + * ERFA_DAS2R + fmod(1236.0 * t, 1.0) * ERFA_D2PI); + +/* Longitude of the mean ascending node of the lunar orbit on the */ +/* ecliptic, measured from the mean equinox of date. */ + om = eraAnpm( + (450160.280 + (-482890.539 + (7.455 + 0.008 * t) * t) * t) + * ERFA_DAS2R + fmod(-5.0 * t, 1.0) * ERFA_D2PI); + +/* --------------- */ +/* Nutation series */ +/* --------------- */ + +/* Initialize nutation components. */ + dp = 0.0; + de = 0.0; + +/* Sum the nutation terms, ending with the biggest. */ + for (j = NT-1; j >= 0; j--) { + + /* Form argument for current term. */ + arg = (double)x[j].nl * el + + (double)x[j].nlp * elp + + (double)x[j].nf * f + + (double)x[j].nd * d + + (double)x[j].nom * om; + + /* Accumulate current nutation term. */ + s = x[j].sp + x[j].spt * t; + c = x[j].ce + x[j].cet * t; + if (s != 0.0) dp += s * sin(arg); + if (c != 0.0) de += c * cos(arg); + } + +/* Convert results from 0.1 mas units to radians. */ + *dpsi = dp * U2R; + *deps = de * U2R; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/nutm80.c b/erfa/nutm80.c new file mode 100644 index 0000000..37612d3 --- /dev/null +++ b/erfa/nutm80.c @@ -0,0 +1,126 @@ +#include "erfa.h" + +void eraNutm80(double date1, double date2, double rmatn[3][3]) +/* +** - - - - - - - - - - +** e r a N u t m 8 0 +** - - - - - - - - - - +** +** Form the matrix of nutation for a given date, IAU 1980 model. +** +** Given: +** date1,date2 double TDB date (Note 1) +** +** Returned: +** rmatn double[3][3] nutation matrix +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(true) = rmatn * V(mean), +** where the p-vector V(true) is with respect to the true +** equatorial triad of date and the p-vector V(mean) is with +** respect to the mean equatorial triad of date. +** +** Called: +** eraNut80 nutation, IAU 1980 +** eraObl80 mean obliquity, IAU 1980 +** eraNumat form nutation matrix +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsi, deps, epsa; + + +/* Nutation components and mean obliquity. */ + eraNut80(date1, date2, &dpsi, &deps); + epsa = eraObl80(date1, date2); + +/* Build the rotation matrix. */ + eraNumat(epsa, dpsi, deps, rmatn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/obl06.c b/erfa/obl06.c new file mode 100644 index 0000000..d1dad56 --- /dev/null +++ b/erfa/obl06.c @@ -0,0 +1,127 @@ +#include "erfa.h" + +double eraObl06(double date1, double date2) +/* +** - - - - - - - - - +** e r a O b l 0 6 +** - - - - - - - - - +** +** Mean obliquity of the ecliptic, IAU 2006 precession model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double obliquity of the ecliptic (radians, Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The result is the angle between the ecliptic and mean equator of +** date date1+date2. +** +** Reference: +** +** Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, eps0; + + +/* Interval between fundamental date J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Mean obliquity. */ + eps0 = (84381.406 + + (-46.836769 + + ( -0.0001831 + + ( 0.00200340 + + ( -0.000000576 + + ( -0.0000000434) * t) * t) * t) * t) * t) * ERFA_DAS2R; + + return eps0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/obl80.c b/erfa/obl80.c new file mode 100644 index 0000000..c8fbb96 --- /dev/null +++ b/erfa/obl80.c @@ -0,0 +1,127 @@ +#include "erfa.h" + +double eraObl80(double date1, double date2) +/* +** - - - - - - - - - +** e r a O b l 8 0 +** - - - - - - - - - +** +** Mean obliquity of the ecliptic, IAU 1980 model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double obliquity of the ecliptic (radians, Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The result is the angle between the ecliptic and mean equator of +** date date1+date2. +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Expression 3.222-1 (p114). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, eps0; + + +/* Interval between fundamental epoch J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Mean obliquity of date. */ + eps0 = ERFA_DAS2R * (84381.448 + + (-46.8150 + + (-0.00059 + + ( 0.001813) * t) * t) * t); + + return eps0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/p06e.c b/erfa/p06e.c new file mode 100644 index 0000000..d8a3d69 --- /dev/null +++ b/erfa/p06e.c @@ -0,0 +1,330 @@ +#include "erfa.h" + +void eraP06e(double date1, double date2, + double *eps0, double *psia, double *oma, double *bpa, + double *bqa, double *pia, double *bpia, + double *epsa, double *chia, double *za, double *zetaa, + double *thetaa, double *pa, + double *gam, double *phi, double *psi) +/* +** - - - - - - - - +** e r a P 0 6 e +** - - - - - - - - +** +** Precession angles, IAU 2006, equinox based. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (see Note 2): +** eps0 double epsilon_0 +** psia double psi_A +** oma double omega_A +** bpa double P_A +** bqa double Q_A +** pia double pi_A +** bpia double Pi_A +** epsa double obliquity epsilon_A +** chia double chi_A +** za double z_A +** zetaa double zeta_A +** thetaa double theta_A +** pa double p_A +** gam double F-W angle gamma_J2000 +** phi double F-W angle phi_J2000 +** psi double F-W angle psi_J2000 +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) This function returns the set of equinox based angles for the +** Capitaine et al. "P03" precession theory, adopted by the IAU in +** 2006. The angles are set out in Table 1 of Hilton et al. (2006): +** +** eps0 epsilon_0 obliquity at J2000.0 +** psia psi_A luni-solar precession +** oma omega_A inclination of equator wrt J2000.0 ecliptic +** bpa P_A ecliptic pole x, J2000.0 ecliptic triad +** bqa Q_A ecliptic pole -y, J2000.0 ecliptic triad +** pia pi_A angle between moving and J2000.0 ecliptics +** bpia Pi_A longitude of ascending node of the ecliptic +** epsa epsilon_A obliquity of the ecliptic +** chia chi_A planetary precession +** za z_A equatorial precession: -3rd 323 Euler angle +** zetaa zeta_A equatorial precession: -1st 323 Euler angle +** thetaa theta_A equatorial precession: 2nd 323 Euler angle +** pa p_A general precession +** gam gamma_J2000 J2000.0 RA difference of ecliptic poles +** phi phi_J2000 J2000.0 codeclination of ecliptic pole +** psi psi_J2000 longitude difference of equator poles, J2000.0 +** +** The returned values are all radians. +** +** 3) Hilton et al. (2006) Table 1 also contains angles that depend on +** models distinct from the P03 precession theory itself, namely the +** IAU 2000A frame bias and nutation. The quoted polynomials are +** used in other ERFA functions: +** +** . eraXy06 contains the polynomial parts of the X and Y series. +** +** . eraS06 contains the polynomial part of the s+XY/2 series. +** +** . eraPfw06 implements the series for the Fukushima-Williams +** angles that are with respect to the GCRS pole (i.e. the variants +** that include frame bias). +** +** 4) The IAU resolution stipulated that the choice of parameterization +** was left to the user, and so an IAU compliant precession +** implementation can be constructed using various combinations of +** the angles returned by the present function. +** +** 5) The parameterization used by ERFA is the version of the Fukushima- +** Williams angles that refers directly to the GCRS pole. These +** angles may be calculated by calling the function eraPfw06. ERFA +** also supports the direct computation of the CIP GCRS X,Y by +** series, available by calling eraXy06. +** +** 6) The agreement between the different parameterizations is at the +** 1 microarcsecond level in the present era. +** +** 7) When constructing a precession formulation that refers to the GCRS +** pole rather than the dynamical pole, it may (depending on the +** choice of angles) be necessary to introduce the frame bias +** explicitly. +** +** 8) It is permissible to re-use the same variable in the returned +** arguments. The quantities are stored in the stated order. +** +** Reference: +** +** Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351 +** +** Called: +** eraObl06 mean obliquity, IAU 2006 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t; + + +/* Interval between fundamental date J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Obliquity at J2000.0. */ + + *eps0 = 84381.406 * ERFA_DAS2R; + +/* Luni-solar precession. */ + + *psia = ( 5038.481507 + + ( -1.0790069 + + ( -0.00114045 + + ( 0.000132851 + + ( -0.0000000951 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* Inclination of mean equator with respect to the J2000.0 ecliptic. */ + + *oma = *eps0 + ( -0.025754 + + ( 0.0512623 + + ( -0.00772503 + + ( -0.000000467 + + ( 0.0000003337 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* Ecliptic pole x, J2000.0 ecliptic triad. */ + + *bpa = ( 4.199094 + + ( 0.1939873 + + ( -0.00022466 + + ( -0.000000912 + + ( 0.0000000120 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* Ecliptic pole -y, J2000.0 ecliptic triad. */ + + *bqa = ( -46.811015 + + ( 0.0510283 + + ( 0.00052413 + + ( -0.000000646 + + ( -0.0000000172 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* Angle between moving and J2000.0 ecliptics. */ + + *pia = ( 46.998973 + + ( -0.0334926 + + ( -0.00012559 + + ( 0.000000113 + + ( -0.0000000022 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* Longitude of ascending node of the moving ecliptic. */ + + *bpia = ( 629546.7936 + + ( -867.95758 + + ( 0.157992 + + ( -0.0005371 + + ( -0.00004797 + + ( 0.000000072 ) + * t) * t) * t) * t) * t) * ERFA_DAS2R; + +/* Mean obliquity of the ecliptic. */ + + *epsa = eraObl06(date1, date2); + +/* Planetary precession. */ + + *chia = ( 10.556403 + + ( -2.3814292 + + ( -0.00121197 + + ( 0.000170663 + + ( -0.0000000560 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* Equatorial precession: minus the third of the 323 Euler angles. */ + + *za = ( -2.650545 + + ( 2306.077181 + + ( 1.0927348 + + ( 0.01826837 + + ( -0.000028596 + + ( -0.0000002904 ) + * t) * t) * t) * t) * t) * ERFA_DAS2R; + +/* Equatorial precession: minus the first of the 323 Euler angles. */ + + *zetaa = ( 2.650545 + + ( 2306.083227 + + ( 0.2988499 + + ( 0.01801828 + + ( -0.000005971 + + ( -0.0000003173 ) + * t) * t) * t) * t) * t) * ERFA_DAS2R; + +/* Equatorial precession: second of the 323 Euler angles. */ + + *thetaa = ( 2004.191903 + + ( -0.4294934 + + ( -0.04182264 + + ( -0.000007089 + + ( -0.0000001274 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* General precession. */ + + *pa = ( 5028.796195 + + ( 1.1054348 + + ( 0.00007964 + + ( -0.000023857 + + ( 0.0000000383 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + +/* Fukushima-Williams angles for precession. */ + + *gam = ( 10.556403 + + ( 0.4932044 + + ( -0.00031238 + + ( -0.000002788 + + ( 0.0000000260 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + + *phi = *eps0 + ( -46.811015 + + ( 0.0511269 + + ( 0.00053289 + + ( -0.000000440 + + ( -0.0000000176 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + + *psi = ( 5038.481507 + + ( 1.5584176 + + ( -0.00018522 + + ( -0.000026452 + + ( -0.0000000148 ) + * t) * t) * t) * t) * t * ERFA_DAS2R; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/p2pv.c b/erfa/p2pv.c new file mode 100644 index 0000000..6ec1388 --- /dev/null +++ b/erfa/p2pv.c @@ -0,0 +1,92 @@ +#include "erfa.h" + +void eraP2pv(double p[3], double pv[2][3]) +/* +** - - - - - - - - +** e r a P 2 p v +** - - - - - - - - +** +** Extend a p-vector to a pv-vector by appending a zero velocity. +** +** Given: +** p double[3] p-vector +** +** Returned: +** pv double[2][3] pv-vector +** +** Called: +** eraCp copy p-vector +** eraZp zero p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraCp(p, pv[0]); + eraZp(pv[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/p2s.c b/erfa/p2s.c new file mode 100644 index 0000000..2821b49 --- /dev/null +++ b/erfa/p2s.c @@ -0,0 +1,100 @@ +#include "erfa.h" + +void eraP2s(double p[3], double *theta, double *phi, double *r) +/* +** - - - - - - - +** e r a P 2 s +** - - - - - - - +** +** P-vector to spherical polar coordinates. +** +** Given: +** p double[3] p-vector +** +** Returned: +** theta double longitude angle (radians) +** phi double latitude angle (radians) +** r double radial distance +** +** Notes: +** +** 1) If P is null, zero theta, phi and r are returned. +** +** 2) At either pole, zero theta is returned. +** +** Called: +** eraC2s p-vector to spherical +** eraPm modulus of p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraC2s(p, theta, phi); + *r = eraPm(p); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pap.c b/erfa/pap.c new file mode 100644 index 0000000..94c17c2 --- /dev/null +++ b/erfa/pap.c @@ -0,0 +1,148 @@ +#include "erfa.h" + +double eraPap(double a[3], double b[3]) +/* +** - - - - - - - +** e r a P a p +** - - - - - - - +** +** Position-angle from two p-vectors. +** +** Given: +** a double[3] direction of reference point +** b double[3] direction of point whose PA is required +** +** Returned (function value): +** double position angle of b with respect to a (radians) +** +** Notes: +** +** 1) The result is the position angle, in radians, of direction b with +** respect to direction a. It is in the range -pi to +pi. The +** sense is such that if b is a small distance "north" of a the +** position angle is approximately zero, and if b is a small +** distance "east" of a the position angle is approximately +pi/2. +** +** 2) The vectors a and b need not be of unit length. +** +** 3) Zero is returned if the two directions are the same or if either +** vector is null. +** +** 4) If vector a is at a pole, the result is ill-defined. +** +** Called: +** eraPn decompose p-vector into modulus and direction +** eraPm modulus of p-vector +** eraPxp vector product of two p-vectors +** eraPmp p-vector minus p-vector +** eraPdp scalar product of two p-vectors +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double am, au[3], bm, st, ct, xa, ya, za, eta[3], xi[3], a2b[3], pa; + + +/* Modulus and direction of the a vector. */ + eraPn(a, &am, au); + +/* Modulus of the b vector. */ + bm = eraPm(b); + +/* Deal with the case of a null vector. */ + if ((am == 0.0) || (bm == 0.0)) { + st = 0.0; + ct = 1.0; + } else { + + /* The "north" axis tangential from a (arbitrary length). */ + xa = a[0]; + ya = a[1]; + za = a[2]; + eta[0] = -xa * za; + eta[1] = -ya * za; + eta[2] = xa*xa + ya*ya; + + /* The "east" axis tangential from a (same length). */ + eraPxp(eta, au, xi); + + /* The vector from a to b. */ + eraPmp(b, a, a2b); + + /* Resolve into components along the north and east axes. */ + st = eraPdp(a2b, xi); + ct = eraPdp(a2b, eta); + + /* Deal with degenerate cases. */ + if ((st == 0.0) && (ct == 0.0)) ct = 1.0; + } + +/* Position angle. */ + pa = atan2(st, ct); + + return pa; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pas.c b/erfa/pas.c new file mode 100644 index 0000000..a71da1b --- /dev/null +++ b/erfa/pas.c @@ -0,0 +1,105 @@ +#include "erfa.h" + +double eraPas(double al, double ap, double bl, double bp) +/* +** - - - - - - - +** e r a P a s +** - - - - - - - +** +** Position-angle from spherical coordinates. +** +** Given: +** al double longitude of point A (e.g. RA) in radians +** ap double latitude of point A (e.g. Dec) in radians +** bl double longitude of point B +** bp double latitude of point B +** +** Returned (function value): +** double position angle of B with respect to A +** +** Notes: +** +** 1) The result is the bearing (position angle), in radians, of point +** B with respect to point A. It is in the range -pi to +pi. The +** sense is such that if B is a small distance "east" of point A, +** the bearing is approximately +pi/2. +** +** 2) Zero is returned if the two points are coincident. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dl, x, y, pa; + + + dl = bl - al; + y = sin(dl) * cos(bp); + x = sin(bp) * cos(ap) - cos(bp) * sin(ap) * cos(dl); + pa = ((x != 0.0) || (y != 0.0)) ? atan2(y, x) : 0.0; + + return pa; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pb06.c b/erfa/pb06.c new file mode 100644 index 0000000..dc8d60d --- /dev/null +++ b/erfa/pb06.c @@ -0,0 +1,153 @@ +#include "erfa.h" + +void eraPb06(double date1, double date2, + double *bzeta, double *bz, double *btheta) +/* +** - - - - - - - - +** e r a P b 0 6 +** - - - - - - - - +** +** This function forms three Euler angles which implement general +** precession from epoch J2000.0, using the IAU 2006 model. Frame +** bias (the offset between ICRS and mean J2000.0) is included. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** bzeta double 1st rotation: radians cw around z +** bz double 3rd rotation: radians cw around z +** btheta double 2nd rotation: radians ccw around y +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The traditional accumulated precession angles zeta_A, z_A, +** theta_A cannot be obtained in the usual way, namely through +** polynomial expressions, because of the frame bias. The latter +** means that two of the angles undergo rapid changes near this +** date. They are instead the results of decomposing the +** precession-bias matrix obtained by using the Fukushima-Williams +** method, which does not suffer from the problem. The +** decomposition returns values which can be used in the +** conventional formulation and which include frame bias. +** +** 3) The three angles are returned in the conventional order, which +** is not the same as the order of the corresponding Euler +** rotations. The precession-bias matrix is +** R_3(-z) x R_2(+theta) x R_3(-zeta). +** +** 4) Should zeta_A, z_A, theta_A angles be required that do not +** contain frame bias, they are available by calling the ERFA +** function eraP06e. +** +** Called: +** eraPmat06 PB matrix, IAU 2006 +** eraRz rotate around Z-axis +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double r[3][3], r31, r32; + + +/* Precession matrix via Fukushima-Williams angles. */ + eraPmat06(date1, date2, r); + +/* Solve for z. */ + *bz = atan2(r[1][2], r[0][2]); + +/* Remove it from the matrix. */ + eraRz(*bz, r); + +/* Solve for the remaining two angles. */ + *bzeta = atan2 (r[1][0], r[1][1]); + r31 = r[2][0]; + r32 = r[2][1]; + *btheta = atan2(-ERFA_DSIGN(sqrt(r31 * r31 + r32 * r32), r[0][2]), + r[2][2]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pdp.c b/erfa/pdp.c new file mode 100644 index 0000000..2b55422 --- /dev/null +++ b/erfa/pdp.c @@ -0,0 +1,93 @@ +#include "erfa.h" + +double eraPdp(double a[3], double b[3]) +/* +** - - - - - - - +** e r a P d p +** - - - - - - - +** +** p-vector inner (=scalar=dot) product. +** +** Given: +** a double[3] first p-vector +** b double[3] second p-vector +** +** Returned (function value): +** double a . b +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double w; + + + w = a[0] * b[0] + + a[1] * b[1] + + a[2] * b[2]; + + return w; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pfw06.c b/erfa/pfw06.c new file mode 100644 index 0000000..645dd5c --- /dev/null +++ b/erfa/pfw06.c @@ -0,0 +1,174 @@ +#include "erfa.h" + +void eraPfw06(double date1, double date2, + double *gamb, double *phib, double *psib, double *epsa) +/* +** - - - - - - - - - +** e r a P f w 0 6 +** - - - - - - - - - +** +** Precession angles, IAU 2006 (Fukushima-Williams 4-angle formulation). +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** gamb double F-W angle gamma_bar (radians) +** phib double F-W angle phi_bar (radians) +** psib double F-W angle psi_bar (radians) +** epsa double F-W angle epsilon_A (radians) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) Naming the following points: +** +** e = J2000.0 ecliptic pole, +** p = GCRS pole, +** E = mean ecliptic pole of date, +** and P = mean pole of date, +** +** the four Fukushima-Williams angles are as follows: +** +** gamb = gamma_bar = epE +** phib = phi_bar = pE +** psib = psi_bar = pEP +** epsa = epsilon_A = EP +** +** 3) The matrix representing the combined effects of frame bias and +** precession is: +** +** PxB = R_1(-epsa).R_3(-psib).R_1(phib).R_3(gamb) +** +** 4) The matrix representing the combined effects of frame bias, +** precession and nutation is simply: +** +** NxPxB = R_1(-epsa-dE).R_3(-psib-dP).R_1(phib).R_3(gamb) +** +** where dP and dE are the nutation components with respect to the +** ecliptic of date. +** +** Reference: +** +** Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351 +** +** Called: +** eraObl06 mean obliquity, IAU 2006 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t; + + +/* Interval between fundamental date J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* P03 bias+precession angles. */ + *gamb = ( -0.052928 + + ( 10.556378 + + ( 0.4932044 + + ( -0.00031238 + + ( -0.000002788 + + ( 0.0000000260 ) + * t) * t) * t) * t) * t) * ERFA_DAS2R; + *phib = ( 84381.412819 + + ( -46.811016 + + ( 0.0511268 + + ( 0.00053289 + + ( -0.000000440 + + ( -0.0000000176 ) + * t) * t) * t) * t) * t) * ERFA_DAS2R; + *psib = ( -0.041775 + + ( 5038.481484 + + ( 1.5584175 + + ( -0.00018522 + + ( -0.000026452 + + ( -0.0000000148 ) + * t) * t) * t) * t) * t) * ERFA_DAS2R; + *epsa = eraObl06(date1, date2); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/plan94.c b/erfa/plan94.c new file mode 100644 index 0000000..7191204 --- /dev/null +++ b/erfa/plan94.c @@ -0,0 +1,523 @@ +#include "erfa.h" + +int eraPlan94(double date1, double date2, int np, double pv[2][3]) +/* +** - - - - - - - - - - +** e r a P l a n 9 4 +** - - - - - - - - - - +** +** Approximate heliocentric position and velocity of a nominated major +** planet: Mercury, Venus, EMB, Mars, Jupiter, Saturn, Uranus or +** Neptune (but not the Earth itself). +** +** Given: +** date1 double TDB date part A (Note 1) +** date2 double TDB date part B (Note 1) +** np int planet (1=Mercury, 2=Venus, 3=EMB, 4=Mars, +** 5=Jupiter, 6=Saturn, 7=Uranus, 8=Neptune) +** +** Returned (argument): +** pv double[2][3] planet p,v (heliocentric, J2000.0, AU,AU/d) +** +** Returned (function value): +** int status: -1 = illegal NP (outside 1-8) +** 0 = OK +** +1 = warning: year outside 1000-3000 +** +2 = warning: failed to converge +** +** Notes: +** +** 1) The date date1+date2 is in the TDB time scale (in practice TT can +** be used) and is a Julian Date, apportioned in any convenient way +** between the two arguments. For example, JD(TDB)=2450123.7 could +** be expressed in any of these ways, among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. The limited +** accuracy of the present algorithm is such that any of the methods +** is satisfactory. +** +** 2) If an np value outside the range 1-8 is supplied, an error status +** (function value -1) is returned and the pv vector set to zeroes. +** +** 3) For np=3 the result is for the Earth-Moon Barycenter. To obtain +** the heliocentric position and velocity of the Earth, use instead +** the ERFA function eraEpv00. +** +** 4) On successful return, the array pv contains the following: +** +** pv[0][0] x } +** pv[0][1] y } heliocentric position, AU +** pv[0][2] z } +** +** pv[1][0] xdot } +** pv[1][1] ydot } heliocentric velocity, AU/d +** pv[1][2] zdot } +** +** The reference frame is equatorial and is with respect to the +** mean equator and equinox of epoch J2000.0. +** +** 5) The algorithm is due to J.L. Simon, P. Bretagnon, J. Chapront, +** M. Chapront-Touze, G. Francou and J. Laskar (Bureau des +** Longitudes, Paris, France). From comparisons with JPL +** ephemeris DE102, they quote the following maximum errors +** over the interval 1800-2050: +** +** L (arcsec) B (arcsec) R (km) +** +** Mercury 4 1 300 +** Venus 5 1 800 +** EMB 6 1 1000 +** Mars 17 1 7700 +** Jupiter 71 5 76000 +** Saturn 81 13 267000 +** Uranus 86 7 712000 +** Neptune 11 1 253000 +** +** Over the interval 1000-3000, they report that the accuracy is no +** worse than 1.5 times that over 1800-2050. Outside 1000-3000 the +** accuracy declines. +** +** Comparisons of the present function with the JPL DE200 ephemeris +** give the following RMS errors over the interval 1960-2025: +** +** position (km) velocity (m/s) +** +** Mercury 334 0.437 +** Venus 1060 0.855 +** EMB 2010 0.815 +** Mars 7690 1.98 +** Jupiter 71700 7.70 +** Saturn 199000 19.4 +** Uranus 564000 16.4 +** Neptune 158000 14.4 +** +** Comparisons against DE200 over the interval 1800-2100 gave the +** following maximum absolute differences. (The results using +** DE406 were essentially the same.) +** +** L (arcsec) B (arcsec) R (km) Rdot (m/s) +** +** Mercury 7 1 500 0.7 +** Venus 7 1 1100 0.9 +** EMB 9 1 1300 1.0 +** Mars 26 1 9000 2.5 +** Jupiter 78 6 82000 8.2 +** Saturn 87 14 263000 24.6 +** Uranus 86 7 661000 27.4 +** Neptune 11 2 248000 21.4 +** +** 6) The present ERFA re-implementation of the original Simon et al. +** Fortran code differs from the original in the following respects: +** +** * C instead of Fortran. +** +** * The date is supplied in two parts. +** +** * The result is returned only in equatorial Cartesian form; +** the ecliptic longitude, latitude and radius vector are not +** returned. +** +** * The result is in the J2000.0 equatorial frame, not ecliptic. +** +** * More is done in-line: there are fewer calls to subroutines. +** +** * Different error/warning status values are used. +** +** * A different Kepler's-equation-solver is used (avoiding +** use of double precision complex). +** +** * Polynomials in t are nested to minimize rounding errors. +** +** * Explicit double constants are used to avoid mixed-mode +** expressions. +** +** None of the above changes affects the result significantly. +** +** 7) The returned status indicates the most serious condition +** encountered during execution of the function. Illegal np is +** considered the most serious, overriding failure to converge, +** which in turn takes precedence over the remote date warning. +** +** Called: +** eraAnp normalize angle into range 0 to 2pi +** +** Reference: Simon, J.L, Bretagnon, P., Chapront, J., +** Chapront-Touze, M., Francou, G., and Laskar, J., +** Astron. Astrophys. 282, 663 (1994). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Gaussian constant */ + static const double GK = 0.017202098950; + +/* Sin and cos of J2000.0 mean obliquity (IAU 1976) */ + static const double SINEPS = 0.3977771559319137; + static const double COSEPS = 0.9174820620691818; + +/* Maximum number of iterations allowed to solve Kepler's equation */ + static const int KMAX = 10; + + int jstat, i, k; + double t, da, dl, de, dp, di, dom, dmu, arga, argl, am, + ae, dae, ae2, at, r, v, si2, xq, xp, tl, xsw, + xcw, xm2, xf, ci2, xms, xmc, xpxq2, x, y, z; + +/* Planetary inverse masses */ + static const double amas[] = { 6023600.0, /* Mercury */ + 408523.5, /* Venus */ + 328900.5, /* EMB */ + 3098710.0, /* Mars */ + 1047.355, /* Jupiter */ + 3498.5, /* Saturn */ + 22869.0, /* Uranus */ + 19314.0 }; /* Neptune */ + +/* +** Tables giving the mean Keplerian elements, limited to t^2 terms: +** +** a semi-major axis (AU) +** dlm mean longitude (degree and arcsecond) +** e eccentricity +** pi longitude of the perihelion (degree and arcsecond) +** dinc inclination (degree and arcsecond) +** omega longitude of the ascending node (degree and arcsecond) +*/ + + static const double a[][3] = { + { 0.3870983098, 0.0, 0.0 }, /* Mercury */ + { 0.7233298200, 0.0, 0.0 }, /* Venus */ + { 1.0000010178, 0.0, 0.0 }, /* EMB */ + { 1.5236793419, 3e-10, 0.0 }, /* Mars */ + { 5.2026032092, 19132e-10, -39e-10 }, /* Jupiter */ + { 9.5549091915, -0.0000213896, 444e-10 }, /* Saturn */ + { 19.2184460618, -3716e-10, 979e-10 }, /* Uranus */ + { 30.1103868694, -16635e-10, 686e-10 } /* Neptune */ + }; + + static const double dlm[][3] = { + { 252.25090552, 5381016286.88982, -1.92789 }, + { 181.97980085, 2106641364.33548, 0.59381 }, + { 100.46645683, 1295977422.83429, -2.04411 }, + { 355.43299958, 689050774.93988, 0.94264 }, + { 34.35151874, 109256603.77991, -30.60378 }, + { 50.07744430, 43996098.55732, 75.61614 }, + { 314.05500511, 15424811.93933, -1.75083 }, + { 304.34866548, 7865503.20744, 0.21103 } + }; + + static const double e[][3] = { + { 0.2056317526, 0.0002040653, -28349e-10 }, + { 0.0067719164, -0.0004776521, 98127e-10 }, + { 0.0167086342, -0.0004203654, -0.0000126734 }, + { 0.0934006477, 0.0009048438, -80641e-10 }, + { 0.0484979255, 0.0016322542, -0.0000471366 }, + { 0.0555481426, -0.0034664062, -0.0000643639 }, + { 0.0463812221, -0.0002729293, 0.0000078913 }, + { 0.0094557470, 0.0000603263, 0.0 } + }; + + static const double pi[][3] = { + { 77.45611904, 5719.11590, -4.83016 }, + { 131.56370300, 175.48640, -498.48184 }, + { 102.93734808, 11612.35290, 53.27577 }, + { 336.06023395, 15980.45908, -62.32800 }, + { 14.33120687, 7758.75163, 259.95938 }, + { 93.05723748, 20395.49439, 190.25952 }, + { 173.00529106, 3215.56238, -34.09288 }, + { 48.12027554, 1050.71912, 27.39717 } + }; + + static const double dinc[][3] = { + { 7.00498625, -214.25629, 0.28977 }, + { 3.39466189, -30.84437, -11.67836 }, + { 0.0, 469.97289, -3.35053 }, + { 1.84972648, -293.31722, -8.11830 }, + { 1.30326698, -71.55890, 11.95297 }, + { 2.48887878, 91.85195, -17.66225 }, + { 0.77319689, -60.72723, 1.25759 }, + { 1.76995259, 8.12333, 0.08135 } + }; + + static const double omega[][3] = { + { 48.33089304, -4515.21727, -31.79892 }, + { 76.67992019, -10008.48154, -51.32614 }, + { 174.87317577, -8679.27034, 15.34191 }, + { 49.55809321, -10620.90088, -230.57416 }, + { 100.46440702, 6362.03561, 326.52178 }, + { 113.66550252, -9240.19942, -66.23743 }, + { 74.00595701, 2669.15033, 145.93964 }, + { 131.78405702, -221.94322, -0.78728 } + }; + +/* Tables for trigonometric terms to be added to the mean elements of */ +/* the semi-major axes */ + + static const double kp[][9] = { + { 69613, 75645, 88306, 59899, 15746, 71087, 142173, 3086, 0 }, + { 21863, 32794, 26934, 10931, 26250, 43725, 53867, 28939, 0 }, + { 16002, 21863, 32004, 10931, 14529, 16368, 15318, 32794, 0 }, + { 6345, 7818, 15636, 7077, 8184, 14163, 1107, 4872, 0 }, + { 1760, 1454, 1167, 880, 287, 2640, 19, 2047, 1454 }, + { 574, 0, 880, 287, 19, 1760, 1167, 306, 574 }, + { 204, 0, 177, 1265, 4, 385, 200, 208, 204 }, + { 0, 102, 106, 4, 98, 1367, 487, 204, 0 } + }; + + static const double ca[][9] = { + { 4, -13, 11, -9, -9, -3, -1, 4, 0 }, + { -156, 59, -42, 6, 19, -20, -10, -12, 0 }, + { 64, -152, 62, -8, 32, -41, 19, -11, 0 }, + { 124, 621, -145, 208, 54, -57, 30, 15, 0 }, + { -23437, -2634, 6601, 6259, -1507,-1821, 2620, -2115, -1489 }, + { 62911,-119919, 79336,17814,-24241,12068, 8306, -4893, 8902 }, + { 389061,-262125,-44088, 8387,-22976,-2093, -615, -9720, 6633 }, + { -412235,-157046,-31430,37817, -9740, -13, -7449, 9644, 0 } + }; + + static const double sa[][9] = { + { -29, -1, 9, 6, -6, 5, 4, 0, 0 }, + { -48, -125, -26, -37, 18, -13, -20, -2, 0 }, + { -150, -46, 68, 54, 14, 24, -28, 22, 0 }, + { -621, 532, -694, -20, 192, -94, 71, -73, 0 }, + { -14614,-19828, -5869, 1881, -4372, -2255, 782, 930, 913 }, + { 139737, 0, 24667, 51123, -5102, 7429, -4095, -1976, -9566 }, + { -138081, 0, 37205,-49039,-41901,-33872,-27037,-12474, 18797 }, + { 0, 28492,133236, 69654, 52322,-49577,-26430, -3593, 0 } + }; + +/* Tables giving the trigonometric terms to be added to the mean */ +/* elements of the mean longitudes */ + + static const double kq[][10] = { + { 3086,15746,69613,59899,75645,88306, 12661, 2658, 0, 0 }, + { 21863,32794,10931, 73, 4387,26934, 1473, 2157, 0, 0 }, + { 10,16002,21863,10931, 1473,32004, 4387, 73, 0, 0 }, + { 10, 6345, 7818, 1107,15636, 7077, 8184, 532, 10, 0 }, + { 19, 1760, 1454, 287, 1167, 880, 574, 2640, 19, 1454 }, + { 19, 574, 287, 306, 1760, 12, 31, 38, 19, 574 }, + { 4, 204, 177, 8, 31, 200, 1265, 102, 4, 204 }, + { 4, 102, 106, 8, 98, 1367, 487, 204, 4, 102 } + }; + + static const double cl[][10] = { + { 21, -95, -157, 41, -5, 42, 23, 30, 0, 0 }, + { -160, -313, -235, 60, -74, -76, -27, 34, 0, 0 }, + { -325, -322, -79, 232, -52, 97, 55, -41, 0, 0 }, + { 2268, -979, 802, 602, -668, -33, 345, 201, -55, 0 }, + { 7610, -4997,-7689,-5841,-2617, 1115,-748,-607, 6074, 354 }, + { -18549, 30125,20012, -730, 824, 23,1289,-352, -14767, -2062 }, + { -135245,-14594, 4197,-4030,-5630,-2898,2540,-306, 2939, 1986 }, + { 89948, 2103, 8963, 2695, 3682, 1648, 866,-154, -1963, -283 } + }; + + static const double sl[][10] = { + { -342, 136, -23, 62, 66, -52, -33, 17, 0, 0 }, + { 524, -149, -35, 117, 151, 122, -71, -62, 0, 0 }, + { -105, -137, 258, 35, -116, -88,-112, -80, 0, 0 }, + { 854, -205, -936, -240, 140, -341, -97, -232, 536, 0 }, + { -56980, 8016, 1012, 1448,-3024,-3710, 318, 503, 3767, 577 }, + { 138606,-13478,-4964, 1441,-1319,-1482, 427, 1236, -9167, -1918 }, + { 71234,-41116, 5334,-4935,-1848, 66, 434, -1748, 3780, -701 }, + { -47645, 11647, 2166, 3194, 679, 0,-244, -419, -2531, 48 } + }; + +/*--------------------------------------------------------------------*/ + +/* Validate the planet number. */ + if ((np < 1) || (np > 8)) { + jstat = -1; + + /* Reset the result in case of failure. */ + for (k = 0; k < 2; k++) { + for (i = 0; i < 3; i++) { + pv[k][i] = 0.0; + } + } + + } else { + + /* Decrement the planet number to start at zero. */ + np--; + + /* Time: Julian millennia since J2000.0. */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJM; + + /* OK status unless remote date. */ + jstat = fabs(t) <= 1.0 ? 0 : 1; + + /* Compute the mean elements. */ + da = a[np][0] + + (a[np][1] + + a[np][2] * t) * t; + dl = (3600.0 * dlm[np][0] + + (dlm[np][1] + + dlm[np][2] * t) * t) * ERFA_DAS2R; + de = e[np][0] + + ( e[np][1] + + e[np][2] * t) * t; + dp = eraAnpm((3600.0 * pi[np][0] + + (pi[np][1] + + pi[np][2] * t) * t) * ERFA_DAS2R); + di = (3600.0 * dinc[np][0] + + (dinc[np][1] + + dinc[np][2] * t) * t) * ERFA_DAS2R; + dom = eraAnpm((3600.0 * omega[np][0] + + (omega[np][1] + + omega[np][2] * t) * t) * ERFA_DAS2R); + + /* Apply the trigonometric terms. */ + dmu = 0.35953620 * t; + for (k = 0; k < 8; k++) { + arga = kp[np][k] * dmu; + argl = kq[np][k] * dmu; + da += (ca[np][k] * cos(arga) + + sa[np][k] * sin(arga)) * 1e-7; + dl += (cl[np][k] * cos(argl) + + sl[np][k] * sin(argl)) * 1e-7; + } + arga = kp[np][8] * dmu; + da += t * (ca[np][8] * cos(arga) + + sa[np][8] * sin(arga)) * 1e-7; + for (k = 8; k < 10; k++) { + argl = kq[np][k] * dmu; + dl += t * (cl[np][k] * cos(argl) + + sl[np][k] * sin(argl)) * 1e-7; + } + dl = fmod(dl, ERFA_D2PI); + + /* Iterative soln. of Kepler's equation to get eccentric anomaly. */ + am = dl - dp; + ae = am + de * sin(am); + k = 0; + dae = 1.0; + while (k < KMAX && fabs(dae) > 1e-12) { + dae = (am - ae + de * sin(ae)) / (1.0 - de * cos(ae)); + ae += dae; + k++; + if (k == KMAX-1) jstat = 2; + } + + /* True anomaly. */ + ae2 = ae / 2.0; + at = 2.0 * atan2(sqrt((1.0 + de) / (1.0 - de)) * sin(ae2), + cos(ae2)); + + /* Distance (AU) and speed (radians per day). */ + r = da * (1.0 - de * cos(ae)); + v = GK * sqrt((1.0 + 1.0 / amas[np]) / (da * da * da)); + + si2 = sin(di / 2.0); + xq = si2 * cos(dom); + xp = si2 * sin(dom); + tl = at + dp; + xsw = sin(tl); + xcw = cos(tl); + xm2 = 2.0 * (xp * xcw - xq * xsw); + xf = da / sqrt(1 - de * de); + ci2 = cos(di / 2.0); + xms = (de * sin(dp) + xsw) * xf; + xmc = (de * cos(dp) + xcw) * xf; + xpxq2 = 2 * xp * xq; + + /* Position (J2000.0 ecliptic x,y,z in AU). */ + x = r * (xcw - xm2 * xp); + y = r * (xsw + xm2 * xq); + z = r * (-xm2 * ci2); + + /* Rotate to equatorial. */ + pv[0][0] = x; + pv[0][1] = y * COSEPS - z * SINEPS; + pv[0][2] = y * SINEPS + z * COSEPS; + + /* Velocity (J2000.0 ecliptic xdot,ydot,zdot in AU/d). */ + x = v * (( -1.0 + 2.0 * xp * xp) * xms + xpxq2 * xmc); + y = v * (( 1.0 - 2.0 * xq * xq) * xmc - xpxq2 * xms); + z = v * (2.0 * ci2 * (xp * xms + xq * xmc)); + + /* Rotate to equatorial. */ + pv[1][0] = x; + pv[1][1] = y * COSEPS - z * SINEPS; + pv[1][2] = y * SINEPS + z * COSEPS; + + } + +/* Return the status. */ + return jstat; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pm.c b/erfa/pm.c new file mode 100644 index 0000000..3ad503a --- /dev/null +++ b/erfa/pm.c @@ -0,0 +1,85 @@ +#include "erfa.h" + +double eraPm(double p[3]) +/* +** - - - - - - +** e r a P m +** - - - - - - +** +** Modulus of p-vector. +** +** Given: +** p double[3] p-vector +** +** Returned (function value): +** double modulus +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + return sqrt( p[0]*p[0] + p[1]*p[1] + p[2]*p[2] ); + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pmat00.c b/erfa/pmat00.c new file mode 100644 index 0000000..275184a --- /dev/null +++ b/erfa/pmat00.c @@ -0,0 +1,127 @@ +#include "erfa.h" + +void eraPmat00(double date1, double date2, double rbp[3][3]) +/* +** - - - - - - - - - - +** e r a P m a t 0 0 +** - - - - - - - - - - +** +** Precession matrix (including frame bias) from GCRS to a specified +** date, IAU 2000 model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rbp double[3][3] bias-precession matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(date) = rbp * V(GCRS), where +** the p-vector V(GCRS) is with respect to the Geocentric Celestial +** Reference System (IAU, 2000) and the p-vector V(date) is with +** respect to the mean equatorial triad of the given date. +** +** Called: +** eraBp00 frame bias and precession matrices, IAU 2000 +** +** Reference: +** +** IAU: Trans. International Astronomical Union, Vol. XXIVB; Proc. +** 24th General Assembly, Manchester, UK. Resolutions B1.3, B1.6. +** (2000) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rb[3][3], rp[3][3]; + + +/* Obtain the required matrix (discarding others). */ + eraBp00(date1, date2, rb, rp, rbp); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pmat06.c b/erfa/pmat06.c new file mode 100644 index 0000000..28d48f7 --- /dev/null +++ b/erfa/pmat06.c @@ -0,0 +1,131 @@ +#include "erfa.h" + +void eraPmat06(double date1, double date2, double rbp[3][3]) +/* +** - - - - - - - - - - +** e r a P m a t 0 6 +** - - - - - - - - - - +** +** Precession matrix (including frame bias) from GCRS to a specified +** date, IAU 2006 model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rbp double[3][3] bias-precession matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(date) = rbp * V(GCRS), where +** the p-vector V(GCRS) is with respect to the Geocentric Celestial +** Reference System (IAU, 2000) and the p-vector V(date) is with +** respect to the mean equatorial triad of the given date. +** +** Called: +** eraPfw06 bias-precession F-W angles, IAU 2006 +** eraFw2m F-W angles to r-matrix +** +** References: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gamb, phib, psib, epsa; + + +/* Bias-precession Fukushima-Williams angles. */ + eraPfw06(date1, date2, &gamb, &phib, &psib, &epsa); + +/* Form the matrix. */ + eraFw2m(gamb, phib, psib, epsa, rbp); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pmat76.c b/erfa/pmat76.c new file mode 100644 index 0000000..eeeecd2 --- /dev/null +++ b/erfa/pmat76.c @@ -0,0 +1,150 @@ +#include "erfa.h" + +void eraPmat76(double date1, double date2, double rmatp[3][3]) +/* +** - - - - - - - - - - +** e r a P m a t 7 6 +** - - - - - - - - - - +** +** Precession matrix from J2000.0 to a specified date, IAU 1976 model. +** +** Given: +** date1,date2 double ending date, TT (Note 1) +** +** Returned: +** rmatp double[3][3] precession matrix, J2000.0 -> date1+date2 +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(date) = RMATP * V(J2000), +** where the p-vector V(J2000) is with respect to the mean +** equatorial triad of epoch J2000.0 and the p-vector V(date) +** is with respect to the mean equatorial triad of the given +** date. +** +** 3) Though the matrix method itself is rigorous, the precession +** angles are expressed through canonical polynomials which are +** valid only for a limited time span. In addition, the IAU 1976 +** precession rate is known to be imperfect. The absolute accuracy +** of the present formulation is better than 0.1 arcsec from +** 1960AD to 2040AD, better than 1 arcsec from 1640AD to 2360AD, +** and remains below 3 arcsec for the whole of the period +** 500BC to 3000AD. The errors exceed 10 arcsec outside the +** range 1200BC to 3900AD, exceed 100 arcsec outside 4200BC to +** 5600AD and exceed 1000 arcsec outside 6800BC to 8200AD. +** +** Called: +** eraPrec76 accumulated precession angles, IAU 1976 +** eraIr initialize r-matrix to identity +** eraRz rotate around Z-axis +** eraRy rotate around Y-axis +** eraCr copy r-matrix +** +** References: +** +** Lieske, J.H., 1979, Astron.Astrophys. 73, 282. +** equations (6) & (7), p283. +** +** Kaplan,G.H., 1981. USNO circular no. 163, pA2. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double zeta, z, theta, wmat[3][3]; + + +/* Precession Euler angles, J2000.0 to specified date. */ + eraPrec76(ERFA_DJ00, 0.0, date1, date2, &zeta, &z, &theta); + +/* Form the rotation matrix. */ + eraIr( wmat); + eraRz( -zeta, wmat); + eraRy( theta, wmat); + eraRz( -z, wmat); + eraCr( wmat, rmatp); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pmp.c b/erfa/pmp.c new file mode 100644 index 0000000..d5c1f83 --- /dev/null +++ b/erfa/pmp.c @@ -0,0 +1,94 @@ +#include "erfa.h" + +void eraPmp(double a[3], double b[3], double amb[3]) +/* +** - - - - - - - +** e r a P m p +** - - - - - - - +** +** P-vector subtraction. +** +** Given: +** a double[3] first p-vector +** b double[3] second p-vector +** +** Returned: +** amb double[3] a - b +** +** Note: +** It is permissible to re-use the same array for any of the +** arguments. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + amb[0] = a[0] - b[0]; + amb[1] = a[1] - b[1]; + amb[2] = a[2] - b[2]; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pmpx.c b/erfa/pmpx.c new file mode 100644 index 0000000..1659edc --- /dev/null +++ b/erfa/pmpx.c @@ -0,0 +1,153 @@ +#include "erfa.h" + +void eraPmpx(double rc, double dc, double pr, double pd, + double px, double rv, double pmt, double pob[3], + double pco[3]) +/* +** - - - - - - - - +** e r a P m p x +** - - - - - - - - +** +** Proper motion and parallax. +** +** Given: +** rc,dc double ICRS RA,Dec at catalog epoch (radians) +** pr double RA proper motion (radians/year; Note 1) +** pd double Dec proper motion (radians/year) +** px double parallax (arcsec) +** rv double radial velocity (km/s, +ve if receding) +** pmt double proper motion time interval (SSB, Julian years) +** pob double[3] SSB to observer vector (au) +** +** Returned: +** pco double[3] coordinate direction (BCRS unit vector) +** +** Notes: +** +** 1) The proper motion in RA is dRA/dt rather than cos(Dec)*dRA/dt. +** +** 2) The proper motion time interval is for when the starlight +** reaches the solar system barycenter. +** +** 3) To avoid the need for iteration, the Roemer effect (i.e. the +** small annual modulation of the proper motion coming from the +** changing light time) is applied approximately, using the +** direction of the star at the catalog epoch. +** +** References: +** +** 1984 Astronomical Almanac, pp B39-B41. +** +** Urban, S. & Seidelmann, P. K. (eds), Explanatory Supplement to +** the Astronomical Almanac, 3rd ed., University Science Books +** (2013), Section 7.2. +** +** Called: +** eraPdp scalar product of two p-vectors +** eraPn decompose p-vector into modulus and direction +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Km/s to au/year */ + const double VF = ERFA_DAYSEC*ERFA_DJM/ERFA_DAU; + +/* Light time for 1 au, Julian years */ + const double AULTY = ERFA_AULT/ERFA_DAYSEC/ERFA_DJY; + + int i; + double sr, cr, sd, cd, x, y, z, p[3], dt, pxr, w, pdz, pm[3]; + + +/* Spherical coordinates to unit vector (and useful functions). */ + sr = sin(rc); + cr = cos(rc); + sd = sin(dc); + cd = cos(dc); + p[0] = x = cr*cd; + p[1] = y = sr*cd; + p[2] = z = sd; + +/* Proper motion time interval (y) including Roemer effect. */ + dt = pmt + eraPdp(p,pob)*AULTY; + +/* Space motion (radians per year). */ + pxr = px * ERFA_DAS2R; + w = VF * rv * pxr; + pdz = pd * z; + pm[0] = - pr*y - pdz*cr + w*x; + pm[1] = pr*x - pdz*sr + w*y; + pm[2] = pd*cd + w*z; + +/* Coordinate direction of star (unit vector, BCRS). */ + for (i = 0; i < 3; i++) { + p[i] += dt*pm[i] - pxr*pob[i]; + } + eraPn(p, &w, pco); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pmsafe.c b/erfa/pmsafe.c new file mode 100644 index 0000000..fa71e47 --- /dev/null +++ b/erfa/pmsafe.c @@ -0,0 +1,206 @@ +#include "erfa.h" + +int eraPmsafe(double ra1, double dec1, double pmr1, double pmd1, + double px1, double rv1, + double ep1a, double ep1b, double ep2a, double ep2b, + double *ra2, double *dec2, double *pmr2, double *pmd2, + double *px2, double *rv2) +/* +** - - - - - - - - - - +** e r a P m s a f e +** - - - - - - - - - - +** +** Star proper motion: update star catalog data for space motion, with +** special handling to handle the zero parallax case. +** +** Given: +** ra1 double right ascension (radians), before +** dec1 double declination (radians), before +** pmr1 double RA proper motion (radians/year), before +** pmd1 double Dec proper motion (radians/year), before +** px1 double parallax (arcseconds), before +** rv1 double radial velocity (km/s, +ve = receding), before +** ep1a double "before" epoch, part A (Note 1) +** ep1b double "before" epoch, part B (Note 1) +** ep2a double "after" epoch, part A (Note 1) +** ep2b double "after" epoch, part B (Note 1) +** +** Returned: +** ra2 double right ascension (radians), after +** dec2 double declination (radians), after +** pmr2 double RA proper motion (radians/year), after +** pmd2 double Dec proper motion (radians/year), after +** px2 double parallax (arcseconds), after +** rv2 double radial velocity (km/s, +ve = receding), after +** +** Returned (function value): +** int status: +** -1 = system error (should not occur) +** 0 = no warnings or errors +** 1 = distance overridden (Note 6) +** 2 = excessive velocity (Note 7) +** 4 = solution didn't converge (Note 8) +** else = binary logical OR of the above warnings +** +** Notes: +** +** 1) The starting and ending TDB epochs ep1a+ep1b and ep2a+ep2b are +** Julian Dates, apportioned in any convenient way between the two +** parts (A and B). For example, JD(TDB)=2450123.7 could be +** expressed in any of these ways, among others: +** +** epNa epNb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** resolution. The MJD method and the date & time methods are both +** good compromises between resolution and convenience. +** +** 2) In accordance with normal star-catalog conventions, the object's +** right ascension and declination are freed from the effects of +** secular aberration. The frame, which is aligned to the catalog +** equator and equinox, is Lorentzian and centered on the SSB. +** +** The proper motions are the rate of change of the right ascension +** and declination at the catalog epoch and are in radians per TDB +** Julian year. +** +** The parallax and radial velocity are in the same frame. +** +** 3) Care is needed with units. The star coordinates are in radians +** and the proper motions in radians per Julian year, but the +** parallax is in arcseconds. +** +** 4) The RA proper motion is in terms of coordinate angle, not true +** angle. If the catalog uses arcseconds for both RA and Dec proper +** motions, the RA proper motion will need to be divided by cos(Dec) +** before use. +** +** 5) Straight-line motion at constant speed, in the inertial frame, is +** assumed. +** +** 6) An extremely small (or zero or negative) parallax is overridden +** to ensure that the object is at a finite but very large distance, +** but not so large that the proper motion is equivalent to a large +** but safe speed (about 0.1c using the chosen constant). A warning +** status of 1 is added to the status if this action has been taken. +** +** 7) If the space velocity is a significant fraction of c (see the +** constant VMAX in the function eraStarpv), it is arbitrarily set +** to zero. When this action occurs, 2 is added to the status. +** +** 8) The relativistic adjustment carried out in the eraStarpv function +** involves an iterative calculation. If the process fails to +** converge within a set number of iterations, 4 is added to the +** status. +** +** Called: +** eraSeps angle between two points +** eraStarpm update star catalog data for space motion +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Minimum allowed parallax (arcsec) */ + const double PXMIN = 5e-7; + +/* Factor giving maximum allowed transverse speed of about 1% c */ + const double F = 326.0; + + int jpx, j; + double pm, px1a; + + +/* Proper motion in one year (radians). */ + pm = eraSeps(ra1, dec1, ra1+pmr1, dec1+pmd1); + +/* Override the parallax to reduce the chances of a warning status. */ + jpx = 0; + px1a = px1; + pm *= F; + if (px1a < pm) {jpx = 1; px1a = pm;} + if (px1a < PXMIN) {jpx = 1; px1a = PXMIN;} + +/* Carry out the transformation using the modified parallax. */ + j = eraStarpm(ra1, dec1, pmr1, pmd1, px1a, rv1, + ep1a, ep1b, ep2a, ep2b, + ra2, dec2, pmr2, pmd2, px2, rv2); + +/* Revise and return the status. */ + if (! j%2) j += jpx; + return j; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pn.c b/erfa/pn.c new file mode 100644 index 0000000..d8e27b4 --- /dev/null +++ b/erfa/pn.c @@ -0,0 +1,118 @@ +#include "erfa.h" + +void eraPn(double p[3], double *r, double u[3]) +/* +** - - - - - - +** e r a P n +** - - - - - - +** +** Convert a p-vector into modulus and unit vector. +** +** Given: +** p double[3] p-vector +** +** Returned: +** r double modulus +** u double[3] unit vector +** +** Notes: +** +** 1) If p is null, the result is null. Otherwise the result is a unit +** vector. +** +** 2) It is permissible to re-use the same array for any of the +** arguments. +** +** Called: +** eraPm modulus of p-vector +** eraZp zero p-vector +** eraSxp multiply p-vector by scalar +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double w; + + +/* Obtain the modulus and test for zero. */ + w = eraPm(p); + if (w == 0.0) { + + /* Null vector. */ + eraZp(u); + + } else { + + /* Unit vector. */ + eraSxp(1.0/w, p, u); + } + +/* Return the modulus. */ + *r = w; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pn00.c b/erfa/pn00.c new file mode 100644 index 0000000..9063aba --- /dev/null +++ b/erfa/pn00.c @@ -0,0 +1,186 @@ +#include "erfa.h" + +void eraPn00(double date1, double date2, double dpsi, double deps, + double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]) +/* +** - - - - - - - - +** e r a P n 0 0 +** - - - - - - - - +** +** Precession-nutation, IAU 2000 model: a multi-purpose function, +** supporting classical (equinox-based) use directly and CIO-based +** use indirectly. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** dpsi,deps double nutation (Note 2) +** +** Returned: +** epsa double mean obliquity (Note 3) +** rb double[3][3] frame bias matrix (Note 4) +** rp double[3][3] precession matrix (Note 5) +** rbp double[3][3] bias-precession matrix (Note 6) +** rn double[3][3] nutation matrix (Note 7) +** rbpn double[3][3] GCRS-to-true matrix (Note 8) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The caller is responsible for providing the nutation components; +** they are in longitude and obliquity, in radians and are with +** respect to the equinox and ecliptic of date. For high-accuracy +** applications, free core nutation should be included as well as +** any other relevant corrections to the position of the CIP. +** +** 3) The returned mean obliquity is consistent with the IAU 2000 +** precession-nutation models. +** +** 4) The matrix rb transforms vectors from GCRS to J2000.0 mean +** equator and equinox by applying frame bias. +** +** 5) The matrix rp transforms vectors from J2000.0 mean equator and +** equinox to mean equator and equinox of date by applying +** precession. +** +** 6) The matrix rbp transforms vectors from GCRS to mean equator and +** equinox of date by applying frame bias then precession. It is +** the product rp x rb. +** +** 7) The matrix rn transforms vectors from mean equator and equinox of +** date to true equator and equinox of date by applying the nutation +** (luni-solar + planetary). +** +** 8) The matrix rbpn transforms vectors from GCRS to true equator and +** equinox of date. It is the product rn x rbp, applying frame +** bias, precession and nutation in that order. +** +** 9) It is permissible to re-use the same array in the returned +** arguments. The arrays are filled in the order given. +** +** Called: +** eraPr00 IAU 2000 precession adjustments +** eraObl80 mean obliquity, IAU 1980 +** eraBp00 frame bias and precession matrices, IAU 2000 +** eraCr copy r-matrix +** eraNumat form nutation matrix +** eraRxr product of two r-matrices +** +** Reference: +** +** Capitaine, N., Chapront, J., Lambert, S. and Wallace, P., +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsipr, depspr, rbpw[3][3], rnw[3][3]; + + +/* IAU 2000 precession-rate adjustments. */ + eraPr00(date1, date2, &dpsipr, &depspr); + +/* Mean obliquity, consistent with IAU 2000 precession-nutation. */ + *epsa = eraObl80(date1, date2) + depspr; + +/* Frame bias and precession matrices and their product. */ + eraBp00(date1, date2, rb, rp, rbpw); + eraCr(rbpw, rbp); + +/* Nutation matrix. */ + eraNumat(*epsa, dpsi, deps, rnw); + eraCr(rnw, rn); + +/* Bias-precession-nutation matrix (classical). */ + eraRxr(rnw, rbpw, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pn00a.c b/erfa/pn00a.c new file mode 100644 index 0000000..8544cec --- /dev/null +++ b/erfa/pn00a.c @@ -0,0 +1,172 @@ +#include "erfa.h" + +void eraPn00a(double date1, double date2, + double *dpsi, double *deps, double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]) +/* +** - - - - - - - - - +** e r a P n 0 0 a +** - - - - - - - - - +** +** Precession-nutation, IAU 2000A model: a multi-purpose function, +** supporting classical (equinox-based) use directly and CIO-based +** use indirectly. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsi,deps double nutation (Note 2) +** epsa double mean obliquity (Note 3) +** rb double[3][3] frame bias matrix (Note 4) +** rp double[3][3] precession matrix (Note 5) +** rbp double[3][3] bias-precession matrix (Note 6) +** rn double[3][3] nutation matrix (Note 7) +** rbpn double[3][3] GCRS-to-true matrix (Notes 8,9) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The nutation components (luni-solar + planetary, IAU 2000A) in +** longitude and obliquity are in radians and with respect to the +** equinox and ecliptic of date. Free core nutation is omitted; +** for the utmost accuracy, use the eraPn00 function, where the +** nutation components are caller-specified. For faster but +** slightly less accurate results, use the eraPn00b function. +** +** 3) The mean obliquity is consistent with the IAU 2000 precession. +** +** 4) The matrix rb transforms vectors from GCRS to J2000.0 mean +** equator and equinox by applying frame bias. +** +** 5) The matrix rp transforms vectors from J2000.0 mean equator and +** equinox to mean equator and equinox of date by applying +** precession. +** +** 6) The matrix rbp transforms vectors from GCRS to mean equator and +** equinox of date by applying frame bias then precession. It is +** the product rp x rb. +** +** 7) The matrix rn transforms vectors from mean equator and equinox +** of date to true equator and equinox of date by applying the +** nutation (luni-solar + planetary). +** +** 8) The matrix rbpn transforms vectors from GCRS to true equator and +** equinox of date. It is the product rn x rbp, applying frame +** bias, precession and nutation in that order. +** +** 9) The X,Y,Z coordinates of the IAU 2000A Celestial Intermediate +** Pole are elements (3,1-3) of the GCRS-to-true matrix, +** i.e. rbpn[2][0-2]. +** +** 10) It is permissible to re-use the same array in the returned +** arguments. The arrays are filled in the order given. +** +** Called: +** eraNut00a nutation, IAU 2000A +** eraPn00 bias/precession/nutation results, IAU 2000 +** +** Reference: +** +** Capitaine, N., Chapront, J., Lambert, S. and Wallace, P., +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Nutation. */ + eraNut00a(date1, date2, dpsi, deps); + +/* Remaining results. */ + eraPn00(date1, date2, *dpsi, *deps, epsa, rb, rp, rbp, rn, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pn00b.c b/erfa/pn00b.c new file mode 100644 index 0000000..959d6d4 --- /dev/null +++ b/erfa/pn00b.c @@ -0,0 +1,172 @@ +#include "erfa.h" + +void eraPn00b(double date1, double date2, + double *dpsi, double *deps, double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]) +/* +** - - - - - - - - - +** e r a P n 0 0 b +** - - - - - - - - - +** +** Precession-nutation, IAU 2000B model: a multi-purpose function, +** supporting classical (equinox-based) use directly and CIO-based +** use indirectly. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsi,deps double nutation (Note 2) +** epsa double mean obliquity (Note 3) +** rb double[3][3] frame bias matrix (Note 4) +** rp double[3][3] precession matrix (Note 5) +** rbp double[3][3] bias-precession matrix (Note 6) +** rn double[3][3] nutation matrix (Note 7) +** rbpn double[3][3] GCRS-to-true matrix (Notes 8,9) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The nutation components (luni-solar + planetary, IAU 2000B) in +** longitude and obliquity are in radians and with respect to the +** equinox and ecliptic of date. For more accurate results, but +** at the cost of increased computation, use the eraPn00a function. +** For the utmost accuracy, use the eraPn00 function, where the +** nutation components are caller-specified. +** +** 3) The mean obliquity is consistent with the IAU 2000 precession. +** +** 4) The matrix rb transforms vectors from GCRS to J2000.0 mean +** equator and equinox by applying frame bias. +** +** 5) The matrix rp transforms vectors from J2000.0 mean equator and +** equinox to mean equator and equinox of date by applying +** precession. +** +** 6) The matrix rbp transforms vectors from GCRS to mean equator and +** equinox of date by applying frame bias then precession. It is +** the product rp x rb. +** +** 7) The matrix rn transforms vectors from mean equator and equinox +** of date to true equator and equinox of date by applying the +** nutation (luni-solar + planetary). +** +** 8) The matrix rbpn transforms vectors from GCRS to true equator and +** equinox of date. It is the product rn x rbp, applying frame +** bias, precession and nutation in that order. +** +** 9) The X,Y,Z coordinates of the IAU 2000B Celestial Intermediate +** Pole are elements (3,1-3) of the GCRS-to-true matrix, +** i.e. rbpn[2][0-2]. +** +** 10) It is permissible to re-use the same array in the returned +** arguments. The arrays are filled in the stated order. +** +** Called: +** eraNut00b nutation, IAU 2000B +** eraPn00 bias/precession/nutation results, IAU 2000 +** +** Reference: +** +** Capitaine, N., Chapront, J., Lambert, S. and Wallace, P., +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003). +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Nutation. */ + eraNut00b(date1, date2, dpsi, deps); + +/* Remaining results. */ + eraPn00(date1, date2, *dpsi, *deps, epsa, rb, rp, rbp, rn, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pn06.c b/erfa/pn06.c new file mode 100644 index 0000000..0d23528 --- /dev/null +++ b/erfa/pn06.c @@ -0,0 +1,196 @@ +#include "erfa.h" + +void eraPn06(double date1, double date2, double dpsi, double deps, + double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]) +/* +** - - - - - - - - +** e r a P n 0 6 +** - - - - - - - - +** +** Precession-nutation, IAU 2006 model: a multi-purpose function, +** supporting classical (equinox-based) use directly and CIO-based use +** indirectly. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** dpsi,deps double nutation (Note 2) +** +** Returned: +** epsa double mean obliquity (Note 3) +** rb double[3][3] frame bias matrix (Note 4) +** rp double[3][3] precession matrix (Note 5) +** rbp double[3][3] bias-precession matrix (Note 6) +** rn double[3][3] nutation matrix (Note 7) +** rbpn double[3][3] GCRS-to-true matrix (Note 8) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The caller is responsible for providing the nutation components; +** they are in longitude and obliquity, in radians and are with +** respect to the equinox and ecliptic of date. For high-accuracy +** applications, free core nutation should be included as well as +** any other relevant corrections to the position of the CIP. +** +** 3) The returned mean obliquity is consistent with the IAU 2006 +** precession. +** +** 4) The matrix rb transforms vectors from GCRS to J2000.0 mean +** equator and equinox by applying frame bias. +** +** 5) The matrix rp transforms vectors from J2000.0 mean equator and +** equinox to mean equator and equinox of date by applying +** precession. +** +** 6) The matrix rbp transforms vectors from GCRS to mean equator and +** equinox of date by applying frame bias then precession. It is +** the product rp x rb. +** +** 7) The matrix rn transforms vectors from mean equator and equinox +** of date to true equator and equinox of date by applying the +** nutation (luni-solar + planetary). +** +** 8) The matrix rbpn transforms vectors from GCRS to true equator and +** equinox of date. It is the product rn x rbp, applying frame +** bias, precession and nutation in that order. +** +** 9) The X,Y,Z coordinates of the Celestial Intermediate Pole are +** elements (3,1-3) of the GCRS-to-true matrix, i.e. rbpn[2][0-2]. +** +** 10) It is permissible to re-use the same array in the returned +** arguments. The arrays are filled in the stated order. +** +** Called: +** eraPfw06 bias-precession F-W angles, IAU 2006 +** eraFw2m F-W angles to r-matrix +** eraCr copy r-matrix +** eraTr transpose r-matrix +** eraRxr product of two r-matrices +** +** References: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gamb, phib, psib, eps, r1[3][3], r2[3][3], rt[3][3]; + + +/* Bias-precession Fukushima-Williams angles of J2000.0 = frame bias. */ + eraPfw06(ERFA_DJM0, ERFA_DJM00, &gamb, &phib, &psib, &eps); + +/* B matrix. */ + eraFw2m(gamb, phib, psib, eps, r1); + eraCr(r1, rb); + +/* Bias-precession Fukushima-Williams angles of date. */ + eraPfw06(date1, date2, &gamb, &phib, &psib, &eps); + +/* Bias-precession matrix. */ + eraFw2m(gamb, phib, psib, eps, r2); + eraCr(r2, rbp); + +/* Solve for precession matrix. */ + eraTr(r1, rt); + eraRxr(r2, rt, rp); + +/* Equinox-based bias-precession-nutation matrix. */ + eraFw2m(gamb, phib, psib + dpsi, eps + deps, r1); + eraCr(r1, rbpn); + +/* Solve for nutation matrix. */ + eraTr(r2, rt); + eraRxr(r1, rt, rn); + +/* Obliquity, mean of date. */ + *epsa = eps; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pn06a.c b/erfa/pn06a.c new file mode 100644 index 0000000..5a7f958 --- /dev/null +++ b/erfa/pn06a.c @@ -0,0 +1,162 @@ +#include "erfa.h" + +void eraPn06a(double date1, double date2, + double *dpsi, double *deps, double *epsa, + double rb[3][3], double rp[3][3], double rbp[3][3], + double rn[3][3], double rbpn[3][3]) +/* +** - - - - - - - - - +** e r a P n 0 6 a +** - - - - - - - - - +** +** Precession-nutation, IAU 2006/2000A models: a multi-purpose function, +** supporting classical (equinox-based) use directly and CIO-based use +** indirectly. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsi,deps double nutation (Note 2) +** epsa double mean obliquity (Note 3) +** rb double[3][3] frame bias matrix (Note 4) +** rp double[3][3] precession matrix (Note 5) +** rbp double[3][3] bias-precession matrix (Note 6) +** rn double[3][3] nutation matrix (Note 7) +** rbpn double[3][3] GCRS-to-true matrix (Notes 8,9) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The nutation components (luni-solar + planetary, IAU 2000A) in +** longitude and obliquity are in radians and with respect to the +** equinox and ecliptic of date. Free core nutation is omitted; +** for the utmost accuracy, use the eraPn06 function, where the +** nutation components are caller-specified. +** +** 3) The mean obliquity is consistent with the IAU 2006 precession. +** +** 4) The matrix rb transforms vectors from GCRS to mean J2000.0 by +** applying frame bias. +** +** 5) The matrix rp transforms vectors from mean J2000.0 to mean of +** date by applying precession. +** +** 6) The matrix rbp transforms vectors from GCRS to mean of date by +** applying frame bias then precession. It is the product rp x rb. +** +** 7) The matrix rn transforms vectors from mean of date to true of +** date by applying the nutation (luni-solar + planetary). +** +** 8) The matrix rbpn transforms vectors from GCRS to true of date +** (CIP/equinox). It is the product rn x rbp, applying frame bias, +** precession and nutation in that order. +** +** 9) The X,Y,Z coordinates of the IAU 2006/2000A Celestial +** Intermediate Pole are elements (3,1-3) of the GCRS-to-true +** matrix, i.e. rbpn[2][0-2]. +** +** 10) It is permissible to re-use the same array in the returned +** arguments. The arrays are filled in the stated order. +** +** Called: +** eraNut06a nutation, IAU 2006/2000A +** eraPn06 bias/precession/nutation results, IAU 2006 +** +** Reference: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Nutation. */ + eraNut06a(date1, date2, dpsi, deps); + +/* Remaining results. */ + eraPn06(date1, date2, *dpsi, *deps, epsa, rb, rp, rbp, rn, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pnm00a.c b/erfa/pnm00a.c new file mode 100644 index 0000000..a53b3c7 --- /dev/null +++ b/erfa/pnm00a.c @@ -0,0 +1,130 @@ +#include "erfa.h" + +void eraPnm00a(double date1, double date2, double rbpn[3][3]) +/* +** - - - - - - - - - - +** e r a P n m 0 0 a +** - - - - - - - - - - +** +** Form the matrix of precession-nutation for a given date (including +** frame bias), equinox-based, IAU 2000A model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rbpn double[3][3] classical NPB matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(date) = rbpn * V(GCRS), where +** the p-vector V(date) is with respect to the true equatorial triad +** of date date1+date2 and the p-vector V(GCRS) is with respect to +** the Geocentric Celestial Reference System (IAU, 2000). +** +** 3) A faster, but slightly less accurate result (about 1 mas), can be +** obtained by using instead the eraPnm00b function. +** +** Called: +** eraPn00a bias/precession/nutation, IAU 2000A +** +** Reference: +** +** IAU: Trans. International Astronomical Union, Vol. XXIVB; Proc. +** 24th General Assembly, Manchester, UK. Resolutions B1.3, B1.6. +** (2000) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsi, deps, epsa, rb[3][3], rp[3][3], rbp[3][3], rn[3][3]; + + +/* Obtain the required matrix (discarding other results). */ + eraPn00a(date1, date2, &dpsi, &deps, &epsa, rb, rp, rbp, rn, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pnm00b.c b/erfa/pnm00b.c new file mode 100644 index 0000000..9acecbe --- /dev/null +++ b/erfa/pnm00b.c @@ -0,0 +1,130 @@ +#include "erfa.h" + +void eraPnm00b(double date1, double date2, double rbpn[3][3]) +/* +** - - - - - - - - - - +** e r a P n m 0 0 b +** - - - - - - - - - - +** +** Form the matrix of precession-nutation for a given date (including +** frame bias), equinox-based, IAU 2000B model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rbpn double[3][3] bias-precession-nutation matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(date) = rbpn * V(GCRS), where +** the p-vector V(date) is with respect to the true equatorial triad +** of date date1+date2 and the p-vector V(GCRS) is with respect to +** the Geocentric Celestial Reference System (IAU, 2000). +** +** 3) The present function is faster, but slightly less accurate (about +** 1 mas), than the eraPnm00a function. +** +** Called: +** eraPn00b bias/precession/nutation, IAU 2000B +** +** Reference: +** +** IAU: Trans. International Astronomical Union, Vol. XXIVB; Proc. +** 24th General Assembly, Manchester, UK. Resolutions B1.3, B1.6. +** (2000) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dpsi, deps, epsa, rb[3][3], rp[3][3], rbp[3][3], rn[3][3]; + + +/* Obtain the required matrix (discarding other results). */ + eraPn00b(date1, date2, &dpsi, &deps, &epsa, rb, rp, rbp, rn, rbpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pnm06a.c b/erfa/pnm06a.c new file mode 100644 index 0000000..7801234 --- /dev/null +++ b/erfa/pnm06a.c @@ -0,0 +1,133 @@ +#include "erfa.h" + +void eraPnm06a(double date1, double date2, double rnpb[3][3]) +/* +** - - - - - - - - - - +** e r a P n m 0 6 a +** - - - - - - - - - - +** +** Form the matrix of precession-nutation for a given date (including +** frame bias), IAU 2006 precession and IAU 2000A nutation models. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** rnpb double[3][3] bias-precession-nutation matrix (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(date) = rnpb * V(GCRS), where +** the p-vector V(date) is with respect to the true equatorial triad +** of date date1+date2 and the p-vector V(GCRS) is with respect to +** the Geocentric Celestial Reference System (IAU, 2000). +** +** Called: +** eraPfw06 bias-precession F-W angles, IAU 2006 +** eraNut06a nutation, IAU 2006/2000A +** eraFw2m F-W angles to r-matrix +** +** Reference: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double gamb, phib, psib, epsa, dp, de; + + +/* Fukushima-Williams angles for frame bias and precession. */ + eraPfw06(date1, date2, &gamb, &phib, &psib, &epsa); + +/* Nutation components. */ + eraNut06a(date1, date2, &dp, &de); + +/* Equinox based nutation x precession x bias matrix. */ + eraFw2m(gamb, phib, psib + dp, epsa + de, rnpb); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pnm80.c b/erfa/pnm80.c new file mode 100644 index 0000000..ced1834 --- /dev/null +++ b/erfa/pnm80.c @@ -0,0 +1,135 @@ +#include "erfa.h" + +void eraPnm80(double date1, double date2, double rmatpn[3][3]) +/* +** - - - - - - - - - +** e r a P n m 8 0 +** - - - - - - - - - +** +** Form the matrix of precession/nutation for a given date, IAU 1976 +** precession model, IAU 1980 nutation model. +** +** Given: +** date1,date2 double TDB date (Note 1) +** +** Returned: +** rmatpn double[3][3] combined precession/nutation matrix +** +** Notes: +** +** 1) The TDB date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TDB)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The matrix operates in the sense V(date) = rmatpn * V(J2000), +** where the p-vector V(date) is with respect to the true equatorial +** triad of date date1+date2 and the p-vector V(J2000) is with +** respect to the mean equatorial triad of epoch J2000.0. +** +** Called: +** eraPmat76 precession matrix, IAU 1976 +** eraNutm80 nutation matrix, IAU 1980 +** eraRxr product of two r-matrices +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992), +** Section 3.3 (p145). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rmatp[3][3], rmatn[3][3]; + + +/* Precession matrix, J2000.0 to date. */ + eraPmat76(date1, date2, rmatp); + +/* Nutation matrix. */ + eraNutm80(date1, date2, rmatn); + +/* Combine the matrices: PN = N x P. */ + eraRxr(rmatn, rmatp, rmatpn); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pom00.c b/erfa/pom00.c new file mode 100644 index 0000000..3606a90 --- /dev/null +++ b/erfa/pom00.c @@ -0,0 +1,124 @@ +#include "erfa.h" + +void eraPom00(double xp, double yp, double sp, double rpom[3][3]) +/* +** - - - - - - - - - - +** e r a P o m 0 0 +** - - - - - - - - - - +** +** Form the matrix of polar motion for a given date, IAU 2000. +** +** Given: +** xp,yp double coordinates of the pole (radians, Note 1) +** sp double the TIO locator s' (radians, Note 2) +** +** Returned: +** rpom double[3][3] polar-motion matrix (Note 3) +** +** Notes: +** +** 1) The arguments xp and yp are the coordinates (in radians) of the +** Celestial Intermediate Pole with respect to the International +** Terrestrial Reference System (see IERS Conventions 2003), +** measured along the meridians to 0 and 90 deg west respectively. +** +** 2) The argument sp is the TIO locator s', in radians, which +** positions the Terrestrial Intermediate Origin on the equator. It +** is obtained from polar motion observations by numerical +** integration, and so is in essence unpredictable. However, it is +** dominated by a secular drift of about 47 microarcseconds per +** century, and so can be taken into account by using s' = -47*t, +** where t is centuries since J2000.0. The function eraSp00 +** implements this approximation. +** +** 3) The matrix operates in the sense V(TRS) = rpom * V(CIP), meaning +** that it is the final rotation when computing the pointing +** direction to a celestial source. +** +** Called: +** eraIr initialize r-matrix to identity +** eraRz rotate around Z-axis +** eraRy rotate around Y-axis +** eraRx rotate around X-axis +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Construct the matrix. */ + eraIr(rpom); + eraRz(sp, rpom); + eraRy(-xp, rpom); + eraRx(-yp, rpom); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ppp.c b/erfa/ppp.c new file mode 100644 index 0000000..87ff430 --- /dev/null +++ b/erfa/ppp.c @@ -0,0 +1,94 @@ +#include "erfa.h" + +void eraPpp(double a[3], double b[3], double apb[3]) +/* +** - - - - - - - +** e r a P p p +** - - - - - - - +** +** P-vector addition. +** +** Given: +** a double[3] first p-vector +** b double[3] second p-vector +** +** Returned: +** apb double[3] a + b +** +** Note: +** It is permissible to re-use the same array for any of the +** arguments. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + apb[0] = a[0] + b[0]; + apb[1] = a[1] + b[1]; + apb[2] = a[2] + b[2]; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ppsp.c b/erfa/ppsp.c new file mode 100644 index 0000000..fa86b00 --- /dev/null +++ b/erfa/ppsp.c @@ -0,0 +1,103 @@ +#include "erfa.h" + +void eraPpsp(double a[3], double s, double b[3], double apsb[3]) +/* +** - - - - - - - - +** e r a P p s p +** - - - - - - - - +** +** P-vector plus scaled p-vector. +** +** Given: +** a double[3] first p-vector +** s double scalar (multiplier for b) +** b double[3] second p-vector +** +** Returned: +** apsb double[3] a + s*b +** +** Note: +** It is permissible for any of a, b and apsb to be the same array. +** +** Called: +** eraSxp multiply p-vector by scalar +** eraPpp p-vector plus p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double sb[3]; + + +/* s*b. */ + eraSxp(s, b, sb); + +/* a + s*b. */ + eraPpp(a, sb, apsb); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pr00.c b/erfa/pr00.c new file mode 100644 index 0000000..3477ad3 --- /dev/null +++ b/erfa/pr00.c @@ -0,0 +1,151 @@ +#include "erfa.h" + +void eraPr00(double date1, double date2, double *dpsipr, double *depspr) +/* +** - - - - - - - - +** e r a P r 0 0 +** - - - - - - - - +** +** Precession-rate part of the IAU 2000 precession-nutation models +** (part of MHB2000). +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** dpsipr,depspr double precession corrections (Notes 2,3) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The precession adjustments are expressed as "nutation +** components", corrections in longitude and obliquity with respect +** to the J2000.0 equinox and ecliptic. +** +** 3) Although the precession adjustments are stated to be with respect +** to Lieske et al. (1977), the MHB2000 model does not specify which +** set of Euler angles are to be used and how the adjustments are to +** be applied. The most literal and straightforward procedure is to +** adopt the 4-rotation epsilon_0, psi_A, omega_A, xi_A option, and +** to add dpsipr to psi_A and depspr to both omega_A and eps_A. +** +** 4) This is an implementation of one aspect of the IAU 2000A nutation +** model, formally adopted by the IAU General Assembly in 2000, +** namely MHB2000 (Mathews et al. 2002). +** +** References: +** +** Lieske, J.H., Lederle, T., Fricke, W. & Morando, B., "Expressions +** for the precession quantities based upon the IAU (1976) System of +** Astronomical Constants", Astron.Astrophys., 58, 1-16 (1977) +** +** Mathews, P.M., Herring, T.A., Buffet, B.A., "Modeling of nutation +** and precession New nutation series for nonrigid Earth and +** insights into the Earth's interior", J.Geophys.Res., 107, B4, +** 2002. The MHB2000 code itself was obtained on 9th September 2002 +** from ftp://maia.usno.navy.mil/conv2000/chapter5/IAU2000A. +** +** Wallace, P.T., "Software for Implementing the IAU 2000 +** Resolutions", in IERS Workshop 5.1 (2002). +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t; + +/* Precession and obliquity corrections (radians per century) */ + static const double PRECOR = -0.29965 * ERFA_DAS2R, + OBLCOR = -0.02524 * ERFA_DAS2R; + + +/* Interval between fundamental epoch J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Precession rate contributions with respect to IAU 1976/80. */ + *dpsipr = PRECOR * t; + *depspr = OBLCOR * t; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/prec76.c b/erfa/prec76.c new file mode 100644 index 0000000..ef6cc96 --- /dev/null +++ b/erfa/prec76.c @@ -0,0 +1,157 @@ +#include "erfa.h" + +void eraPrec76(double date01, double date02, double date11, double date12, + double *zeta, double *z, double *theta) +/* +** - - - - - - - - - - +** e r a P r e c 7 6 +** - - - - - - - - - - +** +** IAU 1976 precession model. +** +** This function forms the three Euler angles which implement general +** precession between two dates, using the IAU 1976 model (as for the +** FK5 catalog). +** +** Given: +** date01,date02 double TDB starting date (Note 1) +** date11,date12 double TDB ending date (Note 1) +** +** Returned: +** zeta double 1st rotation: radians cw around z +** z double 3rd rotation: radians cw around z +** theta double 2nd rotation: radians ccw around y +** +** Notes: +** +** 1) The dates date01+date02 and date11+date12 are Julian Dates, +** apportioned in any convenient way between the arguments daten1 +** and daten2. For example, JD(TDB)=2450123.7 could be expressed in +** any of these ways, among others: +** +** daten1 daten2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in cases +** where the loss of several decimal digits of resolution is +** acceptable. The J2000 method is best matched to the way the +** argument is handled internally and will deliver the optimum +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** The two dates may be expressed using different methods, but at +** the risk of losing some resolution. +** +** 2) The accumulated precession angles zeta, z, theta are expressed +** through canonical polynomials which are valid only for a limited +** time span. In addition, the IAU 1976 precession rate is known to +** be imperfect. The absolute accuracy of the present formulation +** is better than 0.1 arcsec from 1960AD to 2040AD, better than +** 1 arcsec from 1640AD to 2360AD, and remains below 3 arcsec for +** the whole of the period 500BC to 3000AD. The errors exceed +** 10 arcsec outside the range 1200BC to 3900AD, exceed 100 arcsec +** outside 4200BC to 5600AD and exceed 1000 arcsec outside 6800BC to +** 8200AD. +** +** 3) The three angles are returned in the conventional order, which +** is not the same as the order of the corresponding Euler +** rotations. The precession matrix is +** R_3(-z) x R_2(+theta) x R_3(-zeta). +** +** Reference: +** +** Lieske, J.H., 1979, Astron.Astrophys. 73, 282, equations +** (6) & (7), p283. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t0, t, tas2r, w; + + +/* Interval between fundamental epoch J2000.0 and start date (JC). */ + t0 = ((date01 - ERFA_DJ00) + date02) / ERFA_DJC; + +/* Interval over which precession required (JC). */ + t = ((date11 - date01) + (date12 - date02)) / ERFA_DJC; + +/* Euler angles. */ + tas2r = t * ERFA_DAS2R; + w = 2306.2181 + (1.39656 - 0.000139 * t0) * t0; + + *zeta = (w + ((0.30188 - 0.000344 * t0) + 0.017998 * t) * t) * tas2r; + + *z = (w + ((1.09468 + 0.000066 * t0) + 0.018203 * t) * t) * tas2r; + + *theta = ((2004.3109 + (-0.85330 - 0.000217 * t0) * t0) + + ((-0.42665 - 0.000217 * t0) - 0.041833 * t) * t) * tas2r; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pv2p.c b/erfa/pv2p.c new file mode 100644 index 0000000..4218ee0 --- /dev/null +++ b/erfa/pv2p.c @@ -0,0 +1,90 @@ +#include "erfa.h" + +void eraPv2p(double pv[2][3], double p[3]) +/* +** - - - - - - - - +** e r a P v 2 p +** - - - - - - - - +** +** Discard velocity component of a pv-vector. +** +** Given: +** pv double[2][3] pv-vector +** +** Returned: +** p double[3] p-vector +** +** Called: +** eraCp copy p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraCp(pv[0], p); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pv2s.c b/erfa/pv2s.c new file mode 100644 index 0000000..7ed8db2 --- /dev/null +++ b/erfa/pv2s.c @@ -0,0 +1,153 @@ +#include "erfa.h" + +void eraPv2s(double pv[2][3], + double *theta, double *phi, double *r, + double *td, double *pd, double *rd) +/* +** - - - - - - - - +** e r a P v 2 s +** - - - - - - - - +** +** Convert position/velocity from Cartesian to spherical coordinates. +** +** Given: +** pv double[2][3] pv-vector +** +** Returned: +** theta double longitude angle (radians) +** phi double latitude angle (radians) +** r double radial distance +** td double rate of change of theta +** pd double rate of change of phi +** rd double rate of change of r +** +** Notes: +** +** 1) If the position part of pv is null, theta, phi, td and pd +** are indeterminate. This is handled by extrapolating the +** position through unit time by using the velocity part of +** pv. This moves the origin without changing the direction +** of the velocity component. If the position and velocity +** components of pv are both null, zeroes are returned for all +** six results. +** +** 2) If the position is a pole, theta, td and pd are indeterminate. +** In such cases zeroes are returned for all three. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double x, y, z, xd, yd, zd, rxy2, rxy, r2, rtrue, rw, xyp; + + +/* Components of position/velocity vector. */ + x = pv[0][0]; + y = pv[0][1]; + z = pv[0][2]; + xd = pv[1][0]; + yd = pv[1][1]; + zd = pv[1][2]; + +/* Component of r in XY plane squared. */ + rxy2 = x*x + y*y; + +/* Modulus squared. */ + r2 = rxy2 + z*z; + +/* Modulus. */ + rtrue = sqrt(r2); + +/* If null vector, move the origin along the direction of movement. */ + rw = rtrue; + if (rtrue == 0.0) { + x = xd; + y = yd; + z = zd; + rxy2 = x*x + y*y; + r2 = rxy2 + z*z; + rw = sqrt(r2); + } + +/* Position and velocity in spherical coordinates. */ + rxy = sqrt(rxy2); + xyp = x*xd + y*yd; + if (rxy2 != 0.0) { + *theta = atan2(y, x); + *phi = atan2(z, rxy); + *td = (x*yd - y*xd) / rxy2; + *pd = (zd*rxy2 - z*xyp) / (r2*rxy); + } else { + *theta = 0.0; + *phi = (z != 0.0) ? atan2(z, rxy) : 0.0; + *td = 0.0; + *pd = 0.0; + } + *r = rtrue; + *rd = (rw != 0.0) ? (xyp + z*zd) / rw : 0.0; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvdpv.c b/erfa/pvdpv.c new file mode 100644 index 0000000..2f01707 --- /dev/null +++ b/erfa/pvdpv.c @@ -0,0 +1,111 @@ +#include "erfa.h" + +void eraPvdpv(double a[2][3], double b[2][3], double adb[2]) +/* +** - - - - - - - - - +** e r a P v d p v +** - - - - - - - - - +** +** Inner (=scalar=dot) product of two pv-vectors. +** +** Given: +** a double[2][3] first pv-vector +** b double[2][3] second pv-vector +** +** Returned: +** adb double[2] a . b (see note) +** +** Note: +** +** If the position and velocity components of the two pv-vectors are +** ( ap, av ) and ( bp, bv ), the result, a . b, is the pair of +** numbers ( ap . bp , ap . bv + av . bp ). The two numbers are the +** dot-product of the two p-vectors and its derivative. +** +** Called: +** eraPdp scalar product of two p-vectors +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double adbd, addb; + + +/* a . b = constant part of result. */ + adb[0] = eraPdp(a[0], b[0]); + +/* a . bdot */ + adbd = eraPdp(a[0], b[1]); + +/* adot . b */ + addb = eraPdp(a[1], b[0]); + +/* Velocity part of result. */ + adb[1] = adbd + addb; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvm.c b/erfa/pvm.c new file mode 100644 index 0000000..cfd9c3e --- /dev/null +++ b/erfa/pvm.c @@ -0,0 +1,95 @@ +#include "erfa.h" + +void eraPvm(double pv[2][3], double *r, double *s) +/* +** - - - - - - - +** e r a P v m +** - - - - - - - +** +** Modulus of pv-vector. +** +** Given: +** pv double[2][3] pv-vector +** +** Returned: +** r double modulus of position component +** s double modulus of velocity component +** +** Called: +** eraPm modulus of p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Distance. */ + *r = eraPm(pv[0]); + +/* Speed. */ + *s = eraPm(pv[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvmpv.c b/erfa/pvmpv.c new file mode 100644 index 0000000..f91f5fe --- /dev/null +++ b/erfa/pvmpv.c @@ -0,0 +1,96 @@ +#include "erfa.h" + +void eraPvmpv(double a[2][3], double b[2][3], double amb[2][3]) +/* +** - - - - - - - - - +** e r a P v m p v +** - - - - - - - - - +** +** Subtract one pv-vector from another. +** +** Given: +** a double[2][3] first pv-vector +** b double[2][3] second pv-vector +** +** Returned: +** amb double[2][3] a - b +** +** Note: +** It is permissible to re-use the same array for any of the +** arguments. +** +** Called: +** eraPmp p-vector minus p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraPmp(a[0], b[0], amb[0]); + eraPmp(a[1], b[1], amb[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvppv.c b/erfa/pvppv.c new file mode 100644 index 0000000..4ab89a7 --- /dev/null +++ b/erfa/pvppv.c @@ -0,0 +1,96 @@ +#include "erfa.h" + +void eraPvppv(double a[2][3], double b[2][3], double apb[2][3]) +/* +** - - - - - - - - - +** e r a P v p p v +** - - - - - - - - - +** +** Add one pv-vector to another. +** +** Given: +** a double[2][3] first pv-vector +** b double[2][3] second pv-vector +** +** Returned: +** apb double[2][3] a + b +** +** Note: +** It is permissible to re-use the same array for any of the +** arguments. +** +** Called: +** eraPpp p-vector plus p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraPpp(a[0], b[0], apb[0]); + eraPpp(a[1], b[1], apb[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvstar.c b/erfa/pvstar.c new file mode 100644 index 0000000..49b1b67 --- /dev/null +++ b/erfa/pvstar.c @@ -0,0 +1,216 @@ +#include "erfa.h" + +int eraPvstar(double pv[2][3], double *ra, double *dec, + double *pmr, double *pmd, double *px, double *rv) +/* +** - - - - - - - - - - +** e r a P v s t a r +** - - - - - - - - - - +** +** Convert star position+velocity vector to catalog coordinates. +** +** Given (Note 1): +** pv double[2][3] pv-vector (AU, AU/day) +** +** Returned (Note 2): +** ra double right ascension (radians) +** dec double declination (radians) +** pmr double RA proper motion (radians/year) +** pmd double Dec proper motion (radians/year) +** px double parallax (arcsec) +** rv double radial velocity (km/s, positive = receding) +** +** Returned (function value): +** int status: +** 0 = OK +** -1 = superluminal speed (Note 5) +** -2 = null position vector +** +** Notes: +** +** 1) The specified pv-vector is the coordinate direction (and its rate +** of change) for the date at which the light leaving the star +** reached the solar-system barycenter. +** +** 2) The star data returned by this function are "observables" for an +** imaginary observer at the solar-system barycenter. Proper motion +** and radial velocity are, strictly, in terms of barycentric +** coordinate time, TCB. For most practical applications, it is +** permissible to neglect the distinction between TCB and ordinary +** "proper" time on Earth (TT/TAI). The result will, as a rule, be +** limited by the intrinsic accuracy of the proper-motion and +** radial-velocity data; moreover, the supplied pv-vector is likely +** to be merely an intermediate result (for example generated by the +** function eraStarpv), so that a change of time unit will cancel +** out overall. +** +** In accordance with normal star-catalog conventions, the object's +** right ascension and declination are freed from the effects of +** secular aberration. The frame, which is aligned to the catalog +** equator and equinox, is Lorentzian and centered on the SSB. +** +** Summarizing, the specified pv-vector is for most stars almost +** identical to the result of applying the standard geometrical +** "space motion" transformation to the catalog data. The +** differences, which are the subject of the Stumpff paper cited +** below, are: +** +** (i) In stars with significant radial velocity and proper motion, +** the constantly changing light-time distorts the apparent proper +** motion. Note that this is a classical, not a relativistic, +** effect. +** +** (ii) The transformation complies with special relativity. +** +** 3) Care is needed with units. The star coordinates are in radians +** and the proper motions in radians per Julian year, but the +** parallax is in arcseconds; the radial velocity is in km/s, but +** the pv-vector result is in AU and AU/day. +** +** 4) The proper motions are the rate of change of the right ascension +** and declination at the catalog epoch and are in radians per Julian +** year. The RA proper motion is in terms of coordinate angle, not +** true angle, and will thus be numerically larger at high +** declinations. +** +** 5) Straight-line motion at constant speed in the inertial frame is +** assumed. If the speed is greater than or equal to the speed of +** light, the function aborts with an error status. +** +** 6) The inverse transformation is performed by the function eraStarpv. +** +** Called: +** eraPn decompose p-vector into modulus and direction +** eraPdp scalar product of two p-vectors +** eraSxp multiply p-vector by scalar +** eraPmp p-vector minus p-vector +** eraPm modulus of p-vector +** eraPpp p-vector plus p-vector +** eraPv2s pv-vector to spherical +** eraAnp normalize angle into range 0 to 2pi +** +** Reference: +** +** Stumpff, P., 1985, Astron.Astrophys. 144, 232-240. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double r, x[3], vr, ur[3], vt, ut[3], bett, betr, d, w, del, + usr[3], ust[3], a, rad, decd, rd; + + +/* Isolate the radial component of the velocity (AU/day, inertial). */ + eraPn(pv[0], &r, x); + vr = eraPdp(x, pv[1]); + eraSxp(vr, x, ur); + +/* Isolate the transverse component of the velocity (AU/day, inertial). */ + eraPmp(pv[1], ur, ut); + vt = eraPm(ut); + +/* Special-relativity dimensionless parameters. */ + bett = vt / ERFA_DC; + betr = vr / ERFA_DC; + +/* The inertial-to-observed correction terms. */ + d = 1.0 + betr; + w = 1.0 - betr*betr - bett*bett; + if (d == 0.0 || w < 0) return -1; + del = sqrt(w) - 1.0; + +/* Apply relativistic correction factor to radial velocity component. */ + w = (betr != 0) ? (betr - del) / (betr * d) : 1.0; + eraSxp(w, ur, usr); + +/* Apply relativistic correction factor to tangential velocity */ +/* component. */ + eraSxp(1.0/d, ut, ust); + +/* Combine the two to obtain the observed velocity vector (AU/day). */ + eraPpp(usr, ust, pv[1]); + +/* Cartesian to spherical. */ + eraPv2s(pv, &a, dec, &r, &rad, &decd, &rd); + if (r == 0.0) return -2; + +/* Return RA in range 0 to 2pi. */ + *ra = eraAnp(a); + +/* Return proper motions in radians per year. */ + *pmr = rad * ERFA_DJY; + *pmd = decd * ERFA_DJY; + +/* Return parallax in arcsec. */ + *px = ERFA_DR2AS / r; + +/* Return radial velocity in km/s. */ + *rv = 1e-3 * rd * ERFA_DAU / ERFA_DAYSEC; + +/* OK status. */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvtob.c b/erfa/pvtob.c new file mode 100644 index 0000000..f808be7 --- /dev/null +++ b/erfa/pvtob.c @@ -0,0 +1,162 @@ +#include "erfa.h" + +void eraPvtob(double elong, double phi, double hm, + double xp, double yp, double sp, double theta, + double pv[2][3]) +/* +** - - - - - - - - - +** e r a P v t o b +** - - - - - - - - - +** +** Position and velocity of a terrestrial observing station. +** +** Given: +** elong double longitude (radians, east +ve, Note 1) +** phi double latitude (geodetic, radians, Note 1) +** hm double height above ref. ellipsoid (geodetic, m) +** xp,yp double coordinates of the pole (radians, Note 2) +** sp double the TIO locator s' (radians, Note 2) +** theta double Earth rotation angle (radians, Note 3) +** +** Returned: +** pv double[2][3] position/velocity vector (m, m/s, CIRS) +** +** Notes: +** +** 1) The terrestrial coordinates are with respect to the ERFA_WGS84 +** reference ellipsoid. +** +** 2) xp and yp are the coordinates (in radians) of the Celestial +** Intermediate Pole with respect to the International Terrestrial +** Reference System (see IERS Conventions), measured along the +** meridians 0 and 90 deg west respectively. sp is the TIO locator +** s', in radians, which positions the Terrestrial Intermediate +** Origin on the equator. For many applications, xp, yp and +** (especially) sp can be set to zero. +** +** 3) If theta is Greenwich apparent sidereal time instead of Earth +** rotation angle, the result is with respect to the true equator +** and equinox of date, i.e. with the x-axis at the equinox rather +** than the celestial intermediate origin. +** +** 4) The velocity units are meters per UT1 second, not per SI second. +** This is unlikely to have any practical consequences in the modern +** era. +** +** 5) No validation is performed on the arguments. Error cases that +** could lead to arithmetic exceptions are trapped by the eraGd2gc +** function, and the result set to zeros. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Urban, S. & Seidelmann, P. K. (eds), Explanatory Supplement to +** the Astronomical Almanac, 3rd ed., University Science Books +** (2013), Section 7.4.3.3. +** +** Called: +** eraGd2gc geodetic to geocentric transformation +** eraPom00 polar motion matrix +** eraTrxp product of transpose of r-matrix and p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Earth rotation rate in radians per UT1 second */ + const double OM = 1.00273781191135448 * ERFA_D2PI / ERFA_DAYSEC; + + double xyzm[3], rpm[3][3], xyz[3], x, y, z, s, c; + + +/* Geodetic to geocentric transformation (ERFA_WGS84). */ + (void) eraGd2gc(1, elong, phi, hm, xyzm); + +/* Polar motion and TIO position. */ + eraPom00(xp, yp, sp, rpm); + eraTrxp(rpm, xyzm, xyz); + x = xyz[0]; + y = xyz[1]; + z = xyz[2]; + +/* Functions of ERA. */ + s = sin(theta); + c = cos(theta); + +/* Position. */ + pv[0][0] = c*x - s*y; + pv[0][1] = s*x + c*y; + pv[0][2] = z; + +/* Velocity. */ + pv[1][0] = OM * ( -s*x - c*y ); + pv[1][1] = OM * ( c*x - s*y ); + pv[1][2] = 0.0; + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvu.c b/erfa/pvu.c new file mode 100644 index 0000000..8b469d0 --- /dev/null +++ b/erfa/pvu.c @@ -0,0 +1,102 @@ +#include "erfa.h" + +void eraPvu(double dt, double pv[2][3], double upv[2][3]) +/* +** - - - - - - - +** e r a P v u +** - - - - - - - +** +** Update a pv-vector. +** +** Given: +** dt double time interval +** pv double[2][3] pv-vector +** +** Returned: +** upv double[2][3] p updated, v unchanged +** +** Notes: +** +** 1) "Update" means "refer the position component of the vector +** to a new date dt time units from the existing date". +** +** 2) The time units of dt must match those of the velocity. +** +** 3) It is permissible for pv and upv to be the same array. +** +** Called: +** eraPpsp p-vector plus scaled p-vector +** eraCp copy p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraPpsp(pv[0], dt, pv[1], upv[0]); + eraCp(pv[1], upv[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvup.c b/erfa/pvup.c new file mode 100644 index 0000000..d820be8 --- /dev/null +++ b/erfa/pvup.c @@ -0,0 +1,97 @@ +#include "erfa.h" + +void eraPvup(double dt, double pv[2][3], double p[3]) +/* +** - - - - - - - - +** e r a P v u p +** - - - - - - - - +** +** Update a pv-vector, discarding the velocity component. +** +** Given: +** dt double time interval +** pv double[2][3] pv-vector +** +** Returned: +** p double[3] p-vector +** +** Notes: +** +** 1) "Update" means "refer the position component of the vector to a +** new date dt time units from the existing date". +** +** 2) The time units of dt must match those of the velocity. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + p[0] = pv[0][0] + dt * pv[1][0]; + p[1] = pv[0][1] + dt * pv[1][1]; + p[2] = pv[0][2] + dt * pv[1][2]; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pvxpv.c b/erfa/pvxpv.c new file mode 100644 index 0000000..fcea173 --- /dev/null +++ b/erfa/pvxpv.c @@ -0,0 +1,116 @@ +#include "erfa.h" + +void eraPvxpv(double a[2][3], double b[2][3], double axb[2][3]) +/* +** - - - - - - - - - +** e r a P v x p v +** - - - - - - - - - +** +** Outer (=vector=cross) product of two pv-vectors. +** +** Given: +** a double[2][3] first pv-vector +** b double[2][3] second pv-vector +** +** Returned: +** axb double[2][3] a x b +** +** Notes: +** +** 1) If the position and velocity components of the two pv-vectors are +** ( ap, av ) and ( bp, bv ), the result, a x b, is the pair of +** vectors ( ap x bp, ap x bv + av x bp ). The two vectors are the +** cross-product of the two p-vectors and its derivative. +** +** 2) It is permissible to re-use the same array for any of the +** arguments. +** +** Called: +** eraCpv copy pv-vector +** eraPxp vector product of two p-vectors +** eraPpp p-vector plus p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double wa[2][3], wb[2][3], axbd[3], adxb[3]; + + +/* Make copies of the inputs. */ + eraCpv(a, wa); + eraCpv(b, wb); + +/* a x b = position part of result. */ + eraPxp(wa[0], wb[0], axb[0]); + +/* a x bdot + adot x b = velocity part of result. */ + eraPxp(wa[0], wb[1], axbd); + eraPxp(wa[1], wb[0], adxb); + eraPpp(axbd, adxb, axb[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/pxp.c b/erfa/pxp.c new file mode 100644 index 0000000..32a224f --- /dev/null +++ b/erfa/pxp.c @@ -0,0 +1,103 @@ +#include "erfa.h" + +void eraPxp(double a[3], double b[3], double axb[3]) +/* +** - - - - - - - +** e r a P x p +** - - - - - - - +** +** p-vector outer (=vector=cross) product. +** +** Given: +** a double[3] first p-vector +** b double[3] second p-vector +** +** Returned: +** axb double[3] a x b +** +** Note: +** It is permissible to re-use the same array for any of the +** arguments. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double xa, ya, za, xb, yb, zb; + + + xa = a[0]; + ya = a[1]; + za = a[2]; + xb = b[0]; + yb = b[1]; + zb = b[2]; + axb[0] = ya*zb - za*yb; + axb[1] = za*xb - xa*zb; + axb[2] = xa*yb - ya*xb; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/refco.c b/erfa/refco.c new file mode 100644 index 0000000..8c228d1 --- /dev/null +++ b/erfa/refco.c @@ -0,0 +1,262 @@ +#include "erfa.h" + +void eraRefco(double phpa, double tc, double rh, double wl, + double *refa, double *refb) +/* +** - - - - - - - - - +** e r a R e f c o +** - - - - - - - - - +** +** Determine the constants A and B in the atmospheric refraction model +** dZ = A tan Z + B tan^3 Z. +** +** Z is the "observed" zenith distance (i.e. affected by refraction) +** and dZ is what to add to Z to give the "topocentric" (i.e. in vacuo) +** zenith distance. +** +** Given: +** phpa double pressure at the observer (hPa = millibar) +** tc double ambient temperature at the observer (deg C) +** rh double relative humidity at the observer (range 0-1) +** wl double wavelength (micrometers) +** +** Returned: +** refa double* tan Z coefficient (radians) +** refb double* tan^3 Z coefficient (radians) +** +** Notes: +** +** 1) The model balances speed and accuracy to give good results in +** applications where performance at low altitudes is not paramount. +** Performance is maintained across a range of conditions, and +** applies to both optical/IR and radio. +** +** 2) The model omits the effects of (i) height above sea level (apart +** from the reduced pressure itself), (ii) latitude (i.e. the +** flattening of the Earth), (iii) variations in tropospheric lapse +** rate and (iv) dispersive effects in the radio. +** +** The model was tested using the following range of conditions: +** +** lapse rates 0.0055, 0.0065, 0.0075 deg/meter +** latitudes 0, 25, 50, 75 degrees +** heights 0, 2500, 5000 meters ASL +** pressures mean for height -10% to +5% in steps of 5% +** temperatures -10 deg to +20 deg with respect to 280 deg at SL +** relative humidity 0, 0.5, 1 +** wavelengths 0.4, 0.6, ... 2 micron, + radio +** zenith distances 15, 45, 75 degrees +** +** The accuracy with respect to raytracing through a model +** atmosphere was as follows: +** +** worst RMS +** +** optical/IR 62 mas 8 mas +** radio 319 mas 49 mas +** +** For this particular set of conditions: +** +** lapse rate 0.0065 K/meter +** latitude 50 degrees +** sea level +** pressure 1005 mb +** temperature 280.15 K +** humidity 80% +** wavelength 5740 Angstroms +** +** the results were as follows: +** +** ZD raytrace eraRefco Saastamoinen +** +** 10 10.27 10.27 10.27 +** 20 21.19 21.20 21.19 +** 30 33.61 33.61 33.60 +** 40 48.82 48.83 48.81 +** 45 58.16 58.18 58.16 +** 50 69.28 69.30 69.27 +** 55 82.97 82.99 82.95 +** 60 100.51 100.54 100.50 +** 65 124.23 124.26 124.20 +** 70 158.63 158.68 158.61 +** 72 177.32 177.37 177.31 +** 74 200.35 200.38 200.32 +** 76 229.45 229.43 229.42 +** 78 267.44 267.29 267.41 +** 80 319.13 318.55 319.10 +** +** deg arcsec arcsec arcsec +** +** The values for Saastamoinen's formula (which includes terms +** up to tan^5) are taken from Hohenkerk and Sinclair (1985). +** +** 3) A wl value in the range 0-100 selects the optical/IR case and is +** wavelength in micrometers. Any value outside this range selects +** the radio case. +** +** 4) Outlandish input parameters are silently limited to +** mathematically safe values. Zero pressure is permissible, and +** causes zeroes to be returned. +** +** 5) The algorithm draws on several sources, as follows: +** +** a) The formula for the saturation vapour pressure of water as +** a function of temperature and temperature is taken from +** Equations (A4.5-A4.7) of Gill (1982). +** +** b) The formula for the water vapour pressure, given the +** saturation pressure and the relative humidity, is from +** Crane (1976), Equation (2.5.5). +** +** c) The refractivity of air is a function of temperature, +** total pressure, water-vapour pressure and, in the case +** of optical/IR, wavelength. The formulae for the two cases are +** developed from Hohenkerk & Sinclair (1985) and Rueger (2002). +** +** d) The formula for beta, the ratio of the scale height of the +** atmosphere to the geocentric distance of the observer, is +** an adaption of Equation (9) from Stone (1996). The +** adaptations, arrived at empirically, consist of (i) a small +** adjustment to the coefficient and (ii) a humidity term for the +** radio case only. +** +** e) The formulae for the refraction constants as a function of +** n-1 and beta are from Green (1987), Equation (4.31). +** +** References: +** +** Crane, R.K., Meeks, M.L. (ed), "Refraction Effects in the Neutral +** Atmosphere", Methods of Experimental Physics: Astrophysics 12B, +** Academic Press, 1976. +** +** Gill, Adrian E., "Atmosphere-Ocean Dynamics", Academic Press, +** 1982. +** +** Green, R.M., "Spherical Astronomy", Cambridge University Press, +** 1987. +** +** Hohenkerk, C.Y., & Sinclair, A.T., NAO Technical Note No. 63, +** 1985. +** +** Rueger, J.M., "Refractive Index Formulae for Electronic Distance +** Measurement with Radio and Millimetre Waves", in Unisurv Report +** S-68, School of Surveying and Spatial Information Systems, +** University of New South Wales, Sydney, Australia, 2002. +** +** Stone, Ronald C., P.A.S.P. 108, 1051-1058, 1996. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int optic; + double p, t, r, w, ps, pw, tk, wlsq, gamma, beta; + + +/* Decide whether optical/IR or radio case: switch at 100 microns. */ + optic = ( wl <= 100.0 ); + +/* Restrict parameters to safe values. */ + t = ERFA_GMAX ( tc, -150.0 ); + t = ERFA_GMIN ( t, 200.0 ); + p = ERFA_GMAX ( phpa, 0.0 ); + p = ERFA_GMIN ( p, 10000.0 ); + r = ERFA_GMAX ( rh, 0.0 ); + r = ERFA_GMIN ( r, 1.0 ); + w = ERFA_GMAX ( wl, 0.1 ); + w = ERFA_GMIN ( w, 1e6 ); + +/* Water vapour pressure at the observer. */ + if ( p > 0.0 ) { + ps = pow ( 10.0, ( 0.7859 + 0.03477*t ) / + ( 1.0 + 0.00412*t ) ) * + ( 1.0 + p * ( 4.5e-6 + 6e-10*t*t ) ); + pw = r * ps / ( 1.0 - (1.0-r)*ps/p ); + } else { + pw = 0.0; + } + +/* Refractive index minus 1 at the observer. */ + tk = t + 273.15; + if ( optic ) { + wlsq = w * w; + gamma = ( ( 77.53484e-6 + + ( 4.39108e-7 + 3.666e-9/wlsq ) / wlsq ) * p + - 11.2684e-6*pw ) / tk; + } else { + gamma = ( 77.6890e-6*p - ( 6.3938e-6 - 0.375463/tk ) * pw ) / tk; + } + +/* Formula for beta from Stone, with empirical adjustments. */ + beta = 4.4474e-6 * tk; + if ( ! optic ) beta -= 0.0074 * pw * beta; + +/* Refraction constants from Green. */ + *refa = gamma * ( 1.0 - beta ); + *refb = - gamma * ( beta - gamma / 2.0 ); + +/* Finished. */ + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/rm2v.c b/erfa/rm2v.c new file mode 100644 index 0000000..7cf27db --- /dev/null +++ b/erfa/rm2v.c @@ -0,0 +1,120 @@ +#include "erfa.h" + +void eraRm2v(double r[3][3], double w[3]) +/* +** - - - - - - - - +** e r a R m 2 v +** - - - - - - - - +** +** Express an r-matrix as an r-vector. +** +** Given: +** r double[3][3] rotation matrix +** +** Returned: +** w double[3] rotation vector (Note 1) +** +** Notes: +** +** 1) A rotation matrix describes a rotation through some angle about +** some arbitrary axis called the Euler axis. The "rotation vector" +** returned by this function has the same direction as the Euler axis, +** and its magnitude is the angle in radians. (The magnitude and +** direction can be separated by means of the function eraPn.) +** +** 2) If r is null, so is the result. If r is not a rotation matrix +** the result is undefined; r must be proper (i.e. have a positive +** determinant) and real orthogonal (inverse = transpose). +** +** 3) The reference frame rotates clockwise as seen looking along +** the rotation vector from the origin. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double x, y, z, s2, c2, phi, f; + + + x = r[1][2] - r[2][1]; + y = r[2][0] - r[0][2]; + z = r[0][1] - r[1][0]; + s2 = sqrt(x*x + y*y + z*z); + if (s2 != 0) { + c2 = r[0][0] + r[1][1] + r[2][2] - 1.0; + phi = atan2(s2, c2); + f = phi / s2; + w[0] = x * f; + w[1] = y * f; + w[2] = z * f; + } else { + w[0] = 0.0; + w[1] = 0.0; + w[2] = 0.0; + } + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/rv2m.c b/erfa/rv2m.c new file mode 100644 index 0000000..22eeff8 --- /dev/null +++ b/erfa/rv2m.c @@ -0,0 +1,127 @@ +#include "erfa.h" + +void eraRv2m(double w[3], double r[3][3]) +/* +** - - - - - - - - +** e r a R v 2 m +** - - - - - - - - +** +** Form the r-matrix corresponding to a given r-vector. +** +** Given: +** w double[3] rotation vector (Note 1) +** +** Returned: +** r double[3][3] rotation matrix +** +** Notes: +** +** 1) A rotation matrix describes a rotation through some angle about +** some arbitrary axis called the Euler axis. The "rotation vector" +** supplied to This function has the same direction as the Euler +** axis, and its magnitude is the angle in radians. +** +** 2) If w is null, the unit matrix is returned. +** +** 3) The reference frame rotates clockwise as seen looking along the +** rotation vector from the origin. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double x, y, z, phi, s, c, f; + + +/* Euler angle (magnitude of rotation vector) and functions. */ + x = w[0]; + y = w[1]; + z = w[2]; + phi = sqrt(x*x + y*y + z*z); + s = sin(phi); + c = cos(phi); + f = 1.0 - c; + +/* Euler axis (direction of rotation vector), perhaps null. */ + if (phi != 0.0) { + x /= phi; + y /= phi; + z /= phi; + } + +/* Form the rotation matrix. */ + r[0][0] = x*x*f + c; + r[0][1] = x*y*f + z*s; + r[0][2] = x*z*f - y*s; + r[1][0] = y*x*f - z*s; + r[1][1] = y*y*f + c; + r[1][2] = y*z*f + x*s; + r[2][0] = z*x*f + y*s; + r[2][1] = z*y*f - x*s; + r[2][2] = z*z*f + c; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/rx.c b/erfa/rx.c new file mode 100644 index 0000000..2cfa460 --- /dev/null +++ b/erfa/rx.c @@ -0,0 +1,119 @@ +#include "erfa.h" + +void eraRx(double phi, double r[3][3]) +/* +** - - - - - - +** e r a R x +** - - - - - - +** +** Rotate an r-matrix about the x-axis. +** +** Given: +** phi double angle (radians) +** +** Given and returned: +** r double[3][3] r-matrix, rotated +** +** Notes: +** +** 1) Calling this function with positive phi incorporates in the +** supplied r-matrix r an additional rotation, about the x-axis, +** anticlockwise as seen looking towards the origin from positive x. +** +** 2) The additional rotation can be represented by this matrix: +** +** ( 1 0 0 ) +** ( ) +** ( 0 + cos(phi) + sin(phi) ) +** ( ) +** ( 0 - sin(phi) + cos(phi) ) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double s, c, a10, a11, a12, a20, a21, a22; + + + s = sin(phi); + c = cos(phi); + + a10 = c*r[1][0] + s*r[2][0]; + a11 = c*r[1][1] + s*r[2][1]; + a12 = c*r[1][2] + s*r[2][2]; + a20 = - s*r[1][0] + c*r[2][0]; + a21 = - s*r[1][1] + c*r[2][1]; + a22 = - s*r[1][2] + c*r[2][2]; + + r[1][0] = a10; + r[1][1] = a11; + r[1][2] = a12; + r[2][0] = a20; + r[2][1] = a21; + r[2][2] = a22; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/rxp.c b/erfa/rxp.c new file mode 100644 index 0000000..124ca30 --- /dev/null +++ b/erfa/rxp.c @@ -0,0 +1,108 @@ +#include "erfa.h" + +void eraRxp(double r[3][3], double p[3], double rp[3]) +/* +** - - - - - - - +** e r a R x p +** - - - - - - - +** +** Multiply a p-vector by an r-matrix. +** +** Given: +** r double[3][3] r-matrix +** p double[3] p-vector +** +** Returned: +** rp double[3] r * p +** +** Note: +** It is permissible for p and rp to be the same array. +** +** Called: +** eraCp copy p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double w, wrp[3]; + int i, j; + + +/* Matrix r * vector p. */ + for (j = 0; j < 3; j++) { + w = 0.0; + for (i = 0; i < 3; i++) { + w += r[j][i] * p[i]; + } + wrp[j] = w; + } + +/* Return the result. */ + eraCp(wrp, rp); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/rxpv.c b/erfa/rxpv.c new file mode 100644 index 0000000..fee78ca --- /dev/null +++ b/erfa/rxpv.c @@ -0,0 +1,95 @@ +#include "erfa.h" + +void eraRxpv(double r[3][3], double pv[2][3], double rpv[2][3]) +/* +** - - - - - - - - +** e r a R x p v +** - - - - - - - - +** +** Multiply a pv-vector by an r-matrix. +** +** Given: +** r double[3][3] r-matrix +** pv double[2][3] pv-vector +** +** Returned: +** rpv double[2][3] r * pv +** +** Note: +** It is permissible for pv and rpv to be the same array. +** +** Called: +** eraRxp product of r-matrix and p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraRxp(r, pv[0], rpv[0]); + eraRxp(r, pv[1], rpv[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/rxr.c b/erfa/rxr.c new file mode 100644 index 0000000..db01937 --- /dev/null +++ b/erfa/rxr.c @@ -0,0 +1,108 @@ +#include "erfa.h" + +void eraRxr(double a[3][3], double b[3][3], double atb[3][3]) +/* +** - - - - - - - +** e r a R x r +** - - - - - - - +** +** Multiply two r-matrices. +** +** Given: +** a double[3][3] first r-matrix +** b double[3][3] second r-matrix +** +** Returned: +** atb double[3][3] a * b +** +** Note: +** It is permissible to re-use the same array for any of the +** arguments. +** +** Called: +** eraCr copy r-matrix +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int i, j, k; + double w, wm[3][3]; + + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + w = 0.0; + for (k = 0; k < 3; k++) { + w += a[i][k] * b[k][j]; + } + wm[i][j] = w; + } + } + eraCr(wm, atb); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ry.c b/erfa/ry.c new file mode 100644 index 0000000..8259496 --- /dev/null +++ b/erfa/ry.c @@ -0,0 +1,119 @@ +#include "erfa.h" + +void eraRy(double theta, double r[3][3]) +/* +** - - - - - - +** e r a R y +** - - - - - - +** +** Rotate an r-matrix about the y-axis. +** +** Given: +** theta double angle (radians) +** +** Given and returned: +** r double[3][3] r-matrix, rotated +** +** Notes: +** +** 1) Calling this function with positive theta incorporates in the +** supplied r-matrix r an additional rotation, about the y-axis, +** anticlockwise as seen looking towards the origin from positive y. +** +** 2) The additional rotation can be represented by this matrix: +** +** ( + cos(theta) 0 - sin(theta) ) +** ( ) +** ( 0 1 0 ) +** ( ) +** ( + sin(theta) 0 + cos(theta) ) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double s, c, a00, a01, a02, a20, a21, a22; + + + s = sin(theta); + c = cos(theta); + + a00 = c*r[0][0] - s*r[2][0]; + a01 = c*r[0][1] - s*r[2][1]; + a02 = c*r[0][2] - s*r[2][2]; + a20 = s*r[0][0] + c*r[2][0]; + a21 = s*r[0][1] + c*r[2][1]; + a22 = s*r[0][2] + c*r[2][2]; + + r[0][0] = a00; + r[0][1] = a01; + r[0][2] = a02; + r[2][0] = a20; + r[2][1] = a21; + r[2][2] = a22; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/rz.c b/erfa/rz.c new file mode 100644 index 0000000..f511503 --- /dev/null +++ b/erfa/rz.c @@ -0,0 +1,119 @@ +#include "erfa.h" + +void eraRz(double psi, double r[3][3]) +/* +** - - - - - - +** e r a R z +** - - - - - - +** +** Rotate an r-matrix about the z-axis. +** +** Given: +** psi double angle (radians) +** +** Given and returned: +** r double[3][3] r-matrix, rotated +** +** Notes: +** +** 1) Calling this function with positive psi incorporates in the +** supplied r-matrix r an additional rotation, about the z-axis, +** anticlockwise as seen looking towards the origin from positive z. +** +** 2) The additional rotation can be represented by this matrix: +** +** ( + cos(psi) + sin(psi) 0 ) +** ( ) +** ( - sin(psi) + cos(psi) 0 ) +** ( ) +** ( 0 0 1 ) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double s, c, a00, a01, a02, a10, a11, a12; + + + s = sin(psi); + c = cos(psi); + + a00 = c*r[0][0] + s*r[1][0]; + a01 = c*r[0][1] + s*r[1][1]; + a02 = c*r[0][2] + s*r[1][2]; + a10 = - s*r[0][0] + c*r[1][0]; + a11 = - s*r[0][1] + c*r[1][1]; + a12 = - s*r[0][2] + c*r[1][2]; + + r[0][0] = a00; + r[0][1] = a01; + r[0][2] = a02; + r[1][0] = a10; + r[1][1] = a11; + r[1][2] = a12; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s00.c b/erfa/s00.c new file mode 100644 index 0000000..46f3386 --- /dev/null +++ b/erfa/s00.c @@ -0,0 +1,380 @@ +#include "erfa.h" + +double eraS00(double date1, double date2, double x, double y) +/* +** - - - - - - - +** e r a S 0 0 +** - - - - - - - +** +** The CIO locator s, positioning the Celestial Intermediate Origin on +** the equator of the Celestial Intermediate Pole, given the CIP's X,Y +** coordinates. Compatible with IAU 2000A precession-nutation. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** x,y double CIP coordinates (Note 3) +** +** Returned (function value): +** double the CIO locator s in radians (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The CIO locator s is the difference between the right ascensions +** of the same point in two systems: the two systems are the GCRS +** and the CIP,CIO, and the point is the ascending node of the +** CIP equator. The quantity s remains below 0.1 arcsecond +** throughout 1900-2100. +** +** 3) The series used to compute s is in fact for s+XY/2, where X and Y +** are the x and y components of the CIP unit vector; this series +** is more compact than a direct series for s would be. This +** function requires X,Y to be supplied by the caller, who is +** responsible for providing values that are consistent with the +** supplied date. +** +** 4) The model is consistent with the IAU 2000A precession-nutation. +** +** Called: +** eraFal03 mean anomaly of the Moon +** eraFalp03 mean anomaly of the Sun +** eraFaf03 mean argument of the latitude of the Moon +** eraFad03 mean elongation of the Moon from the Sun +** eraFaom03 mean longitude of the Moon's ascending node +** eraFave03 mean longitude of Venus +** eraFae03 mean longitude of Earth +** eraFapa03 general accumulated precession in longitude +** +** References: +** +** Capitaine, N., Chapront, J., Lambert, S. and Wallace, P., +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Time since J2000.0, in Julian centuries */ + double t; + +/* Miscellaneous */ + int i, j; + double a, w0, w1, w2, w3, w4, w5; + +/* Fundamental arguments */ + double fa[8]; + +/* Returned value */ + double s; + +/* --------------------- */ +/* The series for s+XY/2 */ +/* --------------------- */ + + typedef struct { + int nfa[8]; /* coefficients of l,l',F,D,Om,LVe,LE,pA */ + double s, c; /* sine and cosine coefficients */ + } TERM; + +/* Polynomial coefficients */ + static const double sp[] = { + + /* 1-6 */ + 94.00e-6, + 3808.35e-6, + -119.94e-6, + -72574.09e-6, + 27.70e-6, + 15.61e-6 + }; + +/* Terms of order t^0 */ + static const TERM s0[] = { + + /* 1-10 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, -2640.73e-6, 0.39e-6 }, + {{ 0, 0, 0, 0, 2, 0, 0, 0}, -63.53e-6, 0.02e-6 }, + {{ 0, 0, 2, -2, 3, 0, 0, 0}, -11.75e-6, -0.01e-6 }, + {{ 0, 0, 2, -2, 1, 0, 0, 0}, -11.21e-6, -0.01e-6 }, + {{ 0, 0, 2, -2, 2, 0, 0, 0}, 4.57e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 3, 0, 0, 0}, -2.02e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 1, 0, 0, 0}, -1.98e-6, 0.00e-6 }, + {{ 0, 0, 0, 0, 3, 0, 0, 0}, 1.72e-6, 0.00e-6 }, + {{ 0, 1, 0, 0, 1, 0, 0, 0}, 1.41e-6, 0.01e-6 }, + {{ 0, 1, 0, 0, -1, 0, 0, 0}, 1.26e-6, 0.01e-6 }, + + /* 11-20 */ + {{ 1, 0, 0, 0, -1, 0, 0, 0}, 0.63e-6, 0.00e-6 }, + {{ 1, 0, 0, 0, 1, 0, 0, 0}, 0.63e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 3, 0, 0, 0}, -0.46e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 1, 0, 0, 0}, -0.45e-6, 0.00e-6 }, + {{ 0, 0, 4, -4, 4, 0, 0, 0}, -0.36e-6, 0.00e-6 }, + {{ 0, 0, 1, -1, 1, -8, 12, 0}, 0.24e-6, 0.12e-6 }, + {{ 0, 0, 2, 0, 0, 0, 0, 0}, -0.32e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 2, 0, 0, 0}, -0.28e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 3, 0, 0, 0}, -0.27e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 1, 0, 0, 0}, -0.26e-6, 0.00e-6 }, + + /* 21-30 */ + {{ 0, 0, 2, -2, 0, 0, 0, 0}, 0.21e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -3, 0, 0, 0}, -0.19e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -1, 0, 0, 0}, -0.18e-6, 0.00e-6 }, + {{ 0, 0, 0, 0, 0, 8,-13, -1}, 0.10e-6, -0.05e-6 }, + {{ 0, 0, 0, 2, 0, 0, 0, 0}, -0.15e-6, 0.00e-6 }, + {{ 2, 0, -2, 0, -1, 0, 0, 0}, 0.14e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 2, 0, 0, 0}, 0.14e-6, 0.00e-6 }, + {{ 1, 0, 0, -2, 1, 0, 0, 0}, -0.14e-6, 0.00e-6 }, + {{ 1, 0, 0, -2, -1, 0, 0, 0}, -0.14e-6, 0.00e-6 }, + {{ 0, 0, 4, -2, 4, 0, 0, 0}, -0.13e-6, 0.00e-6 }, + + /* 31-33 */ + {{ 0, 0, 2, -2, 4, 0, 0, 0}, 0.11e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -3, 0, 0, 0}, -0.11e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -1, 0, 0, 0}, -0.11e-6, 0.00e-6 } + }; + +/* Terms of order t^1 */ + static const TERM s1[] ={ + + /* 1-3 */ + {{ 0, 0, 0, 0, 2, 0, 0, 0}, -0.07e-6, 3.57e-6 }, + {{ 0, 0, 0, 0, 1, 0, 0, 0}, 1.71e-6, -0.03e-6 }, + {{ 0, 0, 2, -2, 3, 0, 0, 0}, 0.00e-6, 0.48e-6 } + }; + +/* Terms of order t^2 */ + static const TERM s2[] ={ + + /* 1-10 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, 743.53e-6, -0.17e-6 }, + {{ 0, 0, 2, -2, 2, 0, 0, 0}, 56.91e-6, 0.06e-6 }, + {{ 0, 0, 2, 0, 2, 0, 0, 0}, 9.84e-6, -0.01e-6 }, + {{ 0, 0, 0, 0, 2, 0, 0, 0}, -8.85e-6, 0.01e-6 }, + {{ 0, 1, 0, 0, 0, 0, 0, 0}, -6.38e-6, -0.05e-6 }, + {{ 1, 0, 0, 0, 0, 0, 0, 0}, -3.07e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 2, 0, 0, 0}, 2.23e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 1, 0, 0, 0}, 1.67e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 2, 0, 0, 0}, 1.30e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -2, 0, 0, 0}, 0.93e-6, 0.00e-6 }, + + /* 11-20 */ + {{ 1, 0, 0, -2, 0, 0, 0, 0}, 0.68e-6, 0.00e-6 }, + {{ 0, 0, 2, -2, 1, 0, 0, 0}, -0.55e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -2, 0, 0, 0}, 0.53e-6, 0.00e-6 }, + {{ 0, 0, 0, 2, 0, 0, 0, 0}, -0.27e-6, 0.00e-6 }, + {{ 1, 0, 0, 0, 1, 0, 0, 0}, -0.27e-6, 0.00e-6 }, + {{ 1, 0, -2, -2, -2, 0, 0, 0}, -0.26e-6, 0.00e-6 }, + {{ 1, 0, 0, 0, -1, 0, 0, 0}, -0.25e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 1, 0, 0, 0}, 0.22e-6, 0.00e-6 }, + {{ 2, 0, 0, -2, 0, 0, 0, 0}, -0.21e-6, 0.00e-6 }, + {{ 2, 0, -2, 0, -1, 0, 0, 0}, 0.20e-6, 0.00e-6 }, + + /* 21-25 */ + {{ 0, 0, 2, 2, 2, 0, 0, 0}, 0.17e-6, 0.00e-6 }, + {{ 2, 0, 2, 0, 2, 0, 0, 0}, 0.13e-6, 0.00e-6 }, + {{ 2, 0, 0, 0, 0, 0, 0, 0}, -0.13e-6, 0.00e-6 }, + {{ 1, 0, 2, -2, 2, 0, 0, 0}, -0.12e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 0, 0, 0, 0}, -0.11e-6, 0.00e-6 } + }; + +/* Terms of order t^3 */ + static const TERM s3[] ={ + + /* 1-4 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, 0.30e-6, -23.51e-6 }, + {{ 0, 0, 2, -2, 2, 0, 0, 0}, -0.03e-6, -1.39e-6 }, + {{ 0, 0, 2, 0, 2, 0, 0, 0}, -0.01e-6, -0.24e-6 }, + {{ 0, 0, 0, 0, 2, 0, 0, 0}, 0.00e-6, 0.22e-6 } + }; + +/* Terms of order t^4 */ + static const TERM s4[] ={ + + /* 1-1 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, -0.26e-6, -0.01e-6 } + }; + +/* Number of terms in the series */ + const int NS0 = (int) (sizeof s0 / sizeof (TERM)); + const int NS1 = (int) (sizeof s1 / sizeof (TERM)); + const int NS2 = (int) (sizeof s2 / sizeof (TERM)); + const int NS3 = (int) (sizeof s3 / sizeof (TERM)); + const int NS4 = (int) (sizeof s4 / sizeof (TERM)); + +/*--------------------------------------------------------------------*/ + +/* Interval between fundamental epoch J2000.0 and current date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Fundamental Arguments (from IERS Conventions 2003) */ + +/* Mean anomaly of the Moon. */ + fa[0] = eraFal03(t); + +/* Mean anomaly of the Sun. */ + fa[1] = eraFalp03(t); + +/* Mean longitude of the Moon minus that of the ascending node. */ + fa[2] = eraFaf03(t); + +/* Mean elongation of the Moon from the Sun. */ + fa[3] = eraFad03(t); + +/* Mean longitude of the ascending node of the Moon. */ + fa[4] = eraFaom03(t); + +/* Mean longitude of Venus. */ + fa[5] = eraFave03(t); + +/* Mean longitude of Earth. */ + fa[6] = eraFae03(t); + +/* General precession in longitude. */ + fa[7] = eraFapa03(t); + +/* Evaluate s. */ + w0 = sp[0]; + w1 = sp[1]; + w2 = sp[2]; + w3 = sp[3]; + w4 = sp[4]; + w5 = sp[5]; + + for (i = NS0-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s0[i].nfa[j] * fa[j]; + } + w0 += s0[i].s * sin(a) + s0[i].c * cos(a); + } + + for (i = NS1-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s1[i].nfa[j] * fa[j]; + } + w1 += s1[i].s * sin(a) + s1[i].c * cos(a); + } + + for (i = NS2-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s2[i].nfa[j] * fa[j]; + } + w2 += s2[i].s * sin(a) + s2[i].c * cos(a); + } + + for (i = NS3-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s3[i].nfa[j] * fa[j]; + } + w3 += s3[i].s * sin(a) + s3[i].c * cos(a); + } + + for (i = NS4-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s4[i].nfa[j] * fa[j]; + } + w4 += s4[i].s * sin(a) + s4[i].c * cos(a); + } + + s = (w0 + + (w1 + + (w2 + + (w3 + + (w4 + + w5 * t) * t) * t) * t) * t) * ERFA_DAS2R - x*y/2.0; + + return s; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s00a.c b/erfa/s00a.c new file mode 100644 index 0000000..39376df --- /dev/null +++ b/erfa/s00a.c @@ -0,0 +1,152 @@ +#include "erfa.h" + +double eraS00a(double date1, double date2) +/* +** - - - - - - - - +** e r a S 0 0 a +** - - - - - - - - +** +** The CIO locator s, positioning the Celestial Intermediate Origin on +** the equator of the Celestial Intermediate Pole, using the IAU 2000A +** precession-nutation model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double the CIO locator s in radians (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The CIO locator s is the difference between the right ascensions +** of the same point in two systems. The two systems are the GCRS +** and the CIP,CIO, and the point is the ascending node of the +** CIP equator. The CIO locator s remains a small fraction of +** 1 arcsecond throughout 1900-2100. +** +** 3) The series used to compute s is in fact for s+XY/2, where X and Y +** are the x and y components of the CIP unit vector; this series +** is more compact than a direct series for s would be. The present +** function uses the full IAU 2000A nutation model when predicting +** the CIP position. Faster results, with no significant loss of +** accuracy, can be obtained via the function eraS00b, which uses +** instead the IAU 2000B truncated model. +** +** Called: +** eraPnm00a classical NPB matrix, IAU 2000A +** eraBnp2xy extract CIP X,Y from the BPN matrix +** eraS00 the CIO locator s, given X,Y, IAU 2000A +** +** References: +** +** Capitaine, N., Chapront, J., Lambert, S. and Wallace, P., +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3], x, y, s; + + +/* Bias-precession-nutation-matrix, IAU 2000A. */ + eraPnm00a(date1, date2, rbpn); + +/* Extract the CIP coordinates. */ + eraBpn2xy(rbpn, &x, &y); + +/* Compute the CIO locator s, given the CIP coordinates. */ + s = eraS00(date1, date2, x, y); + + return s; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s00b.c b/erfa/s00b.c new file mode 100644 index 0000000..21aecf4 --- /dev/null +++ b/erfa/s00b.c @@ -0,0 +1,152 @@ +#include "erfa.h" + +double eraS00b(double date1, double date2) +/* +** - - - - - - - - +** e r a S 0 0 b +** - - - - - - - - +** +** The CIO locator s, positioning the Celestial Intermediate Origin on +** the equator of the Celestial Intermediate Pole, using the IAU 2000B +** precession-nutation model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double the CIO locator s in radians (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The CIO locator s is the difference between the right ascensions +** of the same point in two systems. The two systems are the GCRS +** and the CIP,CIO, and the point is the ascending node of the +** CIP equator. The CIO locator s remains a small fraction of +** 1 arcsecond throughout 1900-2100. +** +** 3) The series used to compute s is in fact for s+XY/2, where X and Y +** are the x and y components of the CIP unit vector; this series +** is more compact than a direct series for s would be. The present +** function uses the IAU 2000B truncated nutation model when +** predicting the CIP position. The function eraS00a uses instead +** the full IAU 2000A model, but with no significant increase in +** accuracy and at some cost in speed. +** +** Called: +** eraPnm00b classical NPB matrix, IAU 2000B +** eraBnp2xy extract CIP X,Y from the BPN matrix +** eraS00 the CIO locator s, given X,Y, IAU 2000A +** +** References: +** +** Capitaine, N., Chapront, J., Lambert, S. and Wallace, P., +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3], x, y, s; + + +/* Bias-precession-nutation-matrix, IAU 2000B. */ + eraPnm00b(date1, date2, rbpn); + +/* Extract the CIP coordinates. */ + eraBpn2xy(rbpn, &x, &y); + +/* Compute the CIO locator s, given the CIP coordinates. */ + s = eraS00(date1, date2, x, y); + + return s; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s06.c b/erfa/s06.c new file mode 100644 index 0000000..e92b501 --- /dev/null +++ b/erfa/s06.c @@ -0,0 +1,377 @@ +#include "erfa.h" + +double eraS06(double date1, double date2, double x, double y) +/* +** - - - - - - - +** e r a S 0 6 +** - - - - - - - +** +** The CIO locator s, positioning the Celestial Intermediate Origin on +** the equator of the Celestial Intermediate Pole, given the CIP's X,Y +** coordinates. Compatible with IAU 2006/2000A precession-nutation. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** x,y double CIP coordinates (Note 3) +** +** Returned (function value): +** double the CIO locator s in radians (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The CIO locator s is the difference between the right ascensions +** of the same point in two systems: the two systems are the GCRS +** and the CIP,CIO, and the point is the ascending node of the +** CIP equator. The quantity s remains below 0.1 arcsecond +** throughout 1900-2100. +** +** 3) The series used to compute s is in fact for s+XY/2, where X and Y +** are the x and y components of the CIP unit vector; this series +** is more compact than a direct series for s would be. This +** function requires X,Y to be supplied by the caller, who is +** responsible for providing values that are consistent with the +** supplied date. +** +** 4) The model is consistent with the "P03" precession (Capitaine et +** al. 2003), adopted by IAU 2006 Resolution 1, 2006, and the +** IAU 2000A nutation (with P03 adjustments). +** +** Called: +** eraFal03 mean anomaly of the Moon +** eraFalp03 mean anomaly of the Sun +** eraFaf03 mean argument of the latitude of the Moon +** eraFad03 mean elongation of the Moon from the Sun +** eraFaom03 mean longitude of the Moon's ascending node +** eraFave03 mean longitude of Venus +** eraFae03 mean longitude of Earth +** eraFapa03 general accumulated precession in longitude +** +** References: +** +** Capitaine, N., Wallace, P.T. & Chapront, J., 2003, Astron. +** Astrophys. 432, 355 +** +** McCarthy, D.D., Petit, G. (eds.) 2004, IERS Conventions (2003), +** IERS Technical Note No. 32, BKG +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Time since J2000.0, in Julian centuries */ + double t; + +/* Miscellaneous */ + int i, j; + double a, w0, w1, w2, w3, w4, w5; + +/* Fundamental arguments */ + double fa[8]; + +/* Returned value */ + double s; + +/* --------------------- */ +/* The series for s+XY/2 */ +/* --------------------- */ + + typedef struct { + int nfa[8]; /* coefficients of l,l',F,D,Om,LVe,LE,pA */ + double s, c; /* sine and cosine coefficients */ + } TERM; + +/* Polynomial coefficients */ + static const double sp[] = { + + /* 1-6 */ + 94.00e-6, + 3808.65e-6, + -122.68e-6, + -72574.11e-6, + 27.98e-6, + 15.62e-6 + }; + +/* Terms of order t^0 */ + static const TERM s0[] = { + + /* 1-10 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, -2640.73e-6, 0.39e-6 }, + {{ 0, 0, 0, 0, 2, 0, 0, 0}, -63.53e-6, 0.02e-6 }, + {{ 0, 0, 2, -2, 3, 0, 0, 0}, -11.75e-6, -0.01e-6 }, + {{ 0, 0, 2, -2, 1, 0, 0, 0}, -11.21e-6, -0.01e-6 }, + {{ 0, 0, 2, -2, 2, 0, 0, 0}, 4.57e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 3, 0, 0, 0}, -2.02e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 1, 0, 0, 0}, -1.98e-6, 0.00e-6 }, + {{ 0, 0, 0, 0, 3, 0, 0, 0}, 1.72e-6, 0.00e-6 }, + {{ 0, 1, 0, 0, 1, 0, 0, 0}, 1.41e-6, 0.01e-6 }, + {{ 0, 1, 0, 0, -1, 0, 0, 0}, 1.26e-6, 0.01e-6 }, + + /* 11-20 */ + {{ 1, 0, 0, 0, -1, 0, 0, 0}, 0.63e-6, 0.00e-6 }, + {{ 1, 0, 0, 0, 1, 0, 0, 0}, 0.63e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 3, 0, 0, 0}, -0.46e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 1, 0, 0, 0}, -0.45e-6, 0.00e-6 }, + {{ 0, 0, 4, -4, 4, 0, 0, 0}, -0.36e-6, 0.00e-6 }, + {{ 0, 0, 1, -1, 1, -8, 12, 0}, 0.24e-6, 0.12e-6 }, + {{ 0, 0, 2, 0, 0, 0, 0, 0}, -0.32e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 2, 0, 0, 0}, -0.28e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 3, 0, 0, 0}, -0.27e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 1, 0, 0, 0}, -0.26e-6, 0.00e-6 }, + + /* 21-30 */ + {{ 0, 0, 2, -2, 0, 0, 0, 0}, 0.21e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -3, 0, 0, 0}, -0.19e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -1, 0, 0, 0}, -0.18e-6, 0.00e-6 }, + {{ 0, 0, 0, 0, 0, 8,-13, -1}, 0.10e-6, -0.05e-6 }, + {{ 0, 0, 0, 2, 0, 0, 0, 0}, -0.15e-6, 0.00e-6 }, + {{ 2, 0, -2, 0, -1, 0, 0, 0}, 0.14e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 2, 0, 0, 0}, 0.14e-6, 0.00e-6 }, + {{ 1, 0, 0, -2, 1, 0, 0, 0}, -0.14e-6, 0.00e-6 }, + {{ 1, 0, 0, -2, -1, 0, 0, 0}, -0.14e-6, 0.00e-6 }, + {{ 0, 0, 4, -2, 4, 0, 0, 0}, -0.13e-6, 0.00e-6 }, + + /* 31-33 */ + {{ 0, 0, 2, -2, 4, 0, 0, 0}, 0.11e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -3, 0, 0, 0}, -0.11e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -1, 0, 0, 0}, -0.11e-6, 0.00e-6 } + }; + +/* Terms of order t^1 */ + static const TERM s1[] = { + + /* 1 - 3 */ + {{ 0, 0, 0, 0, 2, 0, 0, 0}, -0.07e-6, 3.57e-6 }, + {{ 0, 0, 0, 0, 1, 0, 0, 0}, 1.73e-6, -0.03e-6 }, + {{ 0, 0, 2, -2, 3, 0, 0, 0}, 0.00e-6, 0.48e-6 } + }; + +/* Terms of order t^2 */ + static const TERM s2[] = { + + /* 1-10 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, 743.52e-6, -0.17e-6 }, + {{ 0, 0, 2, -2, 2, 0, 0, 0}, 56.91e-6, 0.06e-6 }, + {{ 0, 0, 2, 0, 2, 0, 0, 0}, 9.84e-6, -0.01e-6 }, + {{ 0, 0, 0, 0, 2, 0, 0, 0}, -8.85e-6, 0.01e-6 }, + {{ 0, 1, 0, 0, 0, 0, 0, 0}, -6.38e-6, -0.05e-6 }, + {{ 1, 0, 0, 0, 0, 0, 0, 0}, -3.07e-6, 0.00e-6 }, + {{ 0, 1, 2, -2, 2, 0, 0, 0}, 2.23e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 1, 0, 0, 0}, 1.67e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 2, 0, 0, 0}, 1.30e-6, 0.00e-6 }, + {{ 0, 1, -2, 2, -2, 0, 0, 0}, 0.93e-6, 0.00e-6 }, + + /* 11-20 */ + {{ 1, 0, 0, -2, 0, 0, 0, 0}, 0.68e-6, 0.00e-6 }, + {{ 0, 0, 2, -2, 1, 0, 0, 0}, -0.55e-6, 0.00e-6 }, + {{ 1, 0, -2, 0, -2, 0, 0, 0}, 0.53e-6, 0.00e-6 }, + {{ 0, 0, 0, 2, 0, 0, 0, 0}, -0.27e-6, 0.00e-6 }, + {{ 1, 0, 0, 0, 1, 0, 0, 0}, -0.27e-6, 0.00e-6 }, + {{ 1, 0, -2, -2, -2, 0, 0, 0}, -0.26e-6, 0.00e-6 }, + {{ 1, 0, 0, 0, -1, 0, 0, 0}, -0.25e-6, 0.00e-6 }, + {{ 1, 0, 2, 0, 1, 0, 0, 0}, 0.22e-6, 0.00e-6 }, + {{ 2, 0, 0, -2, 0, 0, 0, 0}, -0.21e-6, 0.00e-6 }, + {{ 2, 0, -2, 0, -1, 0, 0, 0}, 0.20e-6, 0.00e-6 }, + + /* 21-25 */ + {{ 0, 0, 2, 2, 2, 0, 0, 0}, 0.17e-6, 0.00e-6 }, + {{ 2, 0, 2, 0, 2, 0, 0, 0}, 0.13e-6, 0.00e-6 }, + {{ 2, 0, 0, 0, 0, 0, 0, 0}, -0.13e-6, 0.00e-6 }, + {{ 1, 0, 2, -2, 2, 0, 0, 0}, -0.12e-6, 0.00e-6 }, + {{ 0, 0, 2, 0, 0, 0, 0, 0}, -0.11e-6, 0.00e-6 } + }; + +/* Terms of order t^3 */ + static const TERM s3[] = { + + /* 1-4 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, 0.30e-6, -23.42e-6 }, + {{ 0, 0, 2, -2, 2, 0, 0, 0}, -0.03e-6, -1.46e-6 }, + {{ 0, 0, 2, 0, 2, 0, 0, 0}, -0.01e-6, -0.25e-6 }, + {{ 0, 0, 0, 0, 2, 0, 0, 0}, 0.00e-6, 0.23e-6 } + }; + +/* Terms of order t^4 */ + static const TERM s4[] = { + + /* 1-1 */ + {{ 0, 0, 0, 0, 1, 0, 0, 0}, -0.26e-6, -0.01e-6 } + }; + +/* Number of terms in the series */ + static const int NS0 = (int) (sizeof s0 / sizeof (TERM)); + static const int NS1 = (int) (sizeof s1 / sizeof (TERM)); + static const int NS2 = (int) (sizeof s2 / sizeof (TERM)); + static const int NS3 = (int) (sizeof s3 / sizeof (TERM)); + static const int NS4 = (int) (sizeof s4 / sizeof (TERM)); + +/*--------------------------------------------------------------------*/ + +/* Interval between fundamental epoch J2000.0 and current date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Fundamental Arguments (from IERS Conventions 2003) */ + +/* Mean anomaly of the Moon. */ + fa[0] = eraFal03(t); + +/* Mean anomaly of the Sun. */ + fa[1] = eraFalp03(t); + +/* Mean longitude of the Moon minus that of the ascending node. */ + fa[2] = eraFaf03(t); + +/* Mean elongation of the Moon from the Sun. */ + fa[3] = eraFad03(t); + +/* Mean longitude of the ascending node of the Moon. */ + fa[4] = eraFaom03(t); + +/* Mean longitude of Venus. */ + fa[5] = eraFave03(t); + +/* Mean longitude of Earth. */ + fa[6] = eraFae03(t); + +/* General precession in longitude. */ + fa[7] = eraFapa03(t); + +/* Evaluate s. */ + w0 = sp[0]; + w1 = sp[1]; + w2 = sp[2]; + w3 = sp[3]; + w4 = sp[4]; + w5 = sp[5]; + + for (i = NS0-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s0[i].nfa[j] * fa[j]; + } + w0 += s0[i].s * sin(a) + s0[i].c * cos(a); + } + + for (i = NS1-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s1[i].nfa[j] * fa[j]; + } + w1 += s1[i].s * sin(a) + s1[i].c * cos(a); + } + + for (i = NS2-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s2[i].nfa[j] * fa[j]; + } + w2 += s2[i].s * sin(a) + s2[i].c * cos(a); + } + + for (i = NS3-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s3[i].nfa[j] * fa[j]; + } + w3 += s3[i].s * sin(a) + s3[i].c * cos(a); + } + + for (i = NS4-1; i >= 0; i--) { + a = 0.0; + for (j = 0; j < 8; j++) { + a += (double)s4[i].nfa[j] * fa[j]; + } + w4 += s4[i].s * sin(a) + s4[i].c * cos(a); + } + + s = (w0 + + (w1 + + (w2 + + (w3 + + (w4 + + w5 * t) * t) * t) * t) * t) * ERFA_DAS2R - x*y/2.0; + + return s; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s06a.c b/erfa/s06a.c new file mode 100644 index 0000000..6fac640 --- /dev/null +++ b/erfa/s06a.c @@ -0,0 +1,154 @@ +#include "erfa.h" + +double eraS06a(double date1, double date2) +/* +** - - - - - - - - +** e r a S 0 6 a +** - - - - - - - - +** +** The CIO locator s, positioning the Celestial Intermediate Origin on +** the equator of the Celestial Intermediate Pole, using the IAU 2006 +** precession and IAU 2000A nutation models. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double the CIO locator s in radians (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The CIO locator s is the difference between the right ascensions +** of the same point in two systems. The two systems are the GCRS +** and the CIP,CIO, and the point is the ascending node of the +** CIP equator. The CIO locator s remains a small fraction of +** 1 arcsecond throughout 1900-2100. +** +** 3) The series used to compute s is in fact for s+XY/2, where X and Y +** are the x and y components of the CIP unit vector; this series is +** more compact than a direct series for s would be. The present +** function uses the full IAU 2000A nutation model when predicting +** the CIP position. +** +** Called: +** eraPnm06a classical NPB matrix, IAU 2006/2000A +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS06 the CIO locator s, given X,Y, IAU 2006 +** +** References: +** +** Capitaine, N., Chapront, J., Lambert, S. and Wallace, P., +** "Expressions for the Celestial Intermediate Pole and Celestial +** Ephemeris Origin consistent with the IAU 2000A precession- +** nutation model", Astron.Astrophys. 400, 1145-1154 (2003) +** +** n.b. The celestial ephemeris origin (CEO) was renamed "celestial +** intermediate origin" (CIO) by IAU 2006 Resolution 2. +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** McCarthy, D. D., Petit, G. (eds.), 2004, IERS Conventions (2003), +** IERS Technical Note No. 32, BKG +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rnpb[3][3], x, y, s; + + +/* Bias-precession-nutation-matrix, IAU 20006/2000A. */ + eraPnm06a(date1, date2, rnpb); + +/* Extract the CIP coordinates. */ + eraBpn2xy(rnpb, &x, &y); + +/* Compute the CIO locator s, given the CIP coordinates. */ + s = eraS06(date1, date2, x, y); + + return s; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s2c.c b/erfa/s2c.c new file mode 100644 index 0000000..6812fe2 --- /dev/null +++ b/erfa/s2c.c @@ -0,0 +1,94 @@ +#include "erfa.h" + +void eraS2c(double theta, double phi, double c[3]) +/* +** - - - - - - - +** e r a S 2 c +** - - - - - - - +** +** Convert spherical coordinates to Cartesian. +** +** Given: +** theta double longitude angle (radians) +** phi double latitude angle (radians) +** +** Returned: +** c double[3] direction cosines +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double cp; + + + cp = cos(phi); + c[0] = cos(theta) * cp; + c[1] = sin(theta) * cp; + c[2] = sin(phi); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s2p.c b/erfa/s2p.c new file mode 100644 index 0000000..69b0415 --- /dev/null +++ b/erfa/s2p.c @@ -0,0 +1,97 @@ +#include "erfa.h" + +void eraS2p(double theta, double phi, double r, double p[3]) +/* +** - - - - - - - +** e r a S 2 p +** - - - - - - - +** +** Convert spherical polar coordinates to p-vector. +** +** Given: +** theta double longitude angle (radians) +** phi double latitude angle (radians) +** r double radial distance +** +** Returned: +** p double[3] Cartesian coordinates +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraSxp multiply p-vector by scalar +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double u[3]; + + + eraS2c(theta, phi, u); + eraSxp(r, u, p); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s2pv.c b/erfa/s2pv.c new file mode 100644 index 0000000..cb9b8d2 --- /dev/null +++ b/erfa/s2pv.c @@ -0,0 +1,112 @@ +#include "erfa.h" + +void eraS2pv(double theta, double phi, double r, + double td, double pd, double rd, + double pv[2][3]) +/* +** - - - - - - - - +** e r a S 2 p v +** - - - - - - - - +** +** Convert position/velocity from spherical to Cartesian coordinates. +** +** Given: +** theta double longitude angle (radians) +** phi double latitude angle (radians) +** r double radial distance +** td double rate of change of theta +** pd double rate of change of phi +** rd double rate of change of r +** +** Returned: +** pv double[2][3] pv-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double st, ct, sp, cp, rcp, x, y, rpd, w; + + + st = sin(theta); + ct = cos(theta); + sp = sin(phi); + cp = cos(phi); + rcp = r * cp; + x = rcp * ct; + y = rcp * st; + rpd = r * pd; + w = rpd*sp - cp*rd; + + pv[0][0] = x; + pv[0][1] = y; + pv[0][2] = r * sp; + pv[1][0] = -y*td - w*ct; + pv[1][1] = x*td - w*st; + pv[1][2] = rpd*cp + sp*rd; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/s2xpv.c b/erfa/s2xpv.c new file mode 100644 index 0000000..909e2ee --- /dev/null +++ b/erfa/s2xpv.c @@ -0,0 +1,96 @@ +#include "erfa.h" + +void eraS2xpv(double s1, double s2, double pv[2][3], double spv[2][3]) +/* +** - - - - - - - - - +** e r a S 2 x p v +** - - - - - - - - - +** +** Multiply a pv-vector by two scalars. +** +** Given: +** s1 double scalar to multiply position component by +** s2 double scalar to multiply velocity component by +** pv double[2][3] pv-vector +** +** Returned: +** spv double[2][3] pv-vector: p scaled by s1, v scaled by s2 +** +** Note: +** It is permissible for pv and spv to be the same array. +** +** Called: +** eraSxp multiply p-vector by scalar +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraSxp(s1, pv[0], spv[0]); + eraSxp(s2, pv[1], spv[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/sepp.c b/erfa/sepp.c new file mode 100644 index 0000000..04993fa --- /dev/null +++ b/erfa/sepp.c @@ -0,0 +1,114 @@ +#include "erfa.h" + +double eraSepp(double a[3], double b[3]) +/* +** - - - - - - - - +** e r a S e p p +** - - - - - - - - +** +** Angular separation between two p-vectors. +** +** Given: +** a double[3] first p-vector (not necessarily unit length) +** b double[3] second p-vector (not necessarily unit length) +** +** Returned (function value): +** double angular separation (radians, always positive) +** +** Notes: +** +** 1) If either vector is null, a zero result is returned. +** +** 2) The angular separation is most simply formulated in terms of +** scalar product. However, this gives poor accuracy for angles +** near zero and pi. The present algorithm uses both cross product +** and dot product, to deliver full accuracy whatever the size of +** the angle. +** +** Called: +** eraPxp vector product of two p-vectors +** eraPm modulus of p-vector +** eraPdp scalar product of two p-vectors +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double axb[3], ss, cs, s; + + +/* Sine of angle between the vectors, multiplied by the two moduli. */ + eraPxp(a, b, axb); + ss = eraPm(axb); + +/* Cosine of the angle, multiplied by the two moduli. */ + cs = eraPdp(a, b); + +/* The angle. */ + s = ((ss != 0.0) || (cs != 0.0)) ? atan2(ss, cs) : 0.0; + + return s; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/seps.c b/erfa/seps.c new file mode 100644 index 0000000..46dd412 --- /dev/null +++ b/erfa/seps.c @@ -0,0 +1,102 @@ +#include "erfa.h" + +double eraSeps(double al, double ap, double bl, double bp) +/* +** - - - - - - - - +** e r a S e p s +** - - - - - - - - +** +** Angular separation between two sets of spherical coordinates. +** +** Given: +** al double first longitude (radians) +** ap double first latitude (radians) +** bl double second longitude (radians) +** bp double second latitude (radians) +** +** Returned (function value): +** double angular separation (radians) +** +** Called: +** eraS2c spherical coordinates to unit vector +** eraSepp angular separation between two p-vectors +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double ac[3], bc[3], s; + + +/* Spherical to Cartesian. */ + eraS2c(al, ap, ac); + eraS2c(bl, bp, bc); + +/* Angle between the vectors. */ + s = eraSepp(ac, bc); + + return s; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/sp00.c b/erfa/sp00.c new file mode 100644 index 0000000..c586ce0 --- /dev/null +++ b/erfa/sp00.c @@ -0,0 +1,127 @@ +#include "erfa.h" + +double eraSp00(double date1, double date2) +/* +** - - - - - - - - +** e r a S p 0 0 +** - - - - - - - - +** +** The TIO locator s', positioning the Terrestrial Intermediate Origin +** on the equator of the Celestial Intermediate Pole. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned (function value): +** double the TIO locator s' in radians (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The TIO locator s' is obtained from polar motion observations by +** numerical integration, and so is in essence unpredictable. +** However, it is dominated by a secular drift of about +** 47 microarcseconds per century, which is the approximation +** evaluated by the present function. +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double t, sp; + + +/* Interval between fundamental epoch J2000.0 and current date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Approximate s'. */ + sp = -47e-6 * t * ERFA_DAS2R; + + return sp; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/starpm.c b/erfa/starpm.c new file mode 100644 index 0000000..e82662a --- /dev/null +++ b/erfa/starpm.c @@ -0,0 +1,214 @@ +#include "erfa.h" + +int eraStarpm(double ra1, double dec1, + double pmr1, double pmd1, double px1, double rv1, + double ep1a, double ep1b, double ep2a, double ep2b, + double *ra2, double *dec2, + double *pmr2, double *pmd2, double *px2, double *rv2) +/* +** - - - - - - - - - - +** e r a S t a r p m +** - - - - - - - - - - +** +** Star proper motion: update star catalog data for space motion. +** +** Given: +** ra1 double right ascension (radians), before +** dec1 double declination (radians), before +** pmr1 double RA proper motion (radians/year), before +** pmd1 double Dec proper motion (radians/year), before +** px1 double parallax (arcseconds), before +** rv1 double radial velocity (km/s, +ve = receding), before +** ep1a double "before" epoch, part A (Note 1) +** ep1b double "before" epoch, part B (Note 1) +** ep2a double "after" epoch, part A (Note 1) +** ep2b double "after" epoch, part B (Note 1) +** +** Returned: +** ra2 double right ascension (radians), after +** dec2 double declination (radians), after +** pmr2 double RA proper motion (radians/year), after +** pmd2 double Dec proper motion (radians/year), after +** px2 double parallax (arcseconds), after +** rv2 double radial velocity (km/s, +ve = receding), after +** +** Returned (function value): +** int status: +** -1 = system error (should not occur) +** 0 = no warnings or errors +** 1 = distance overridden (Note 6) +** 2 = excessive velocity (Note 7) +** 4 = solution didn't converge (Note 8) +** else = binary logical OR of the above warnings +** +** Notes: +** +** 1) The starting and ending TDB dates ep1a+ep1b and ep2a+ep2b are +** Julian Dates, apportioned in any convenient way between the two +** parts (A and B). For example, JD(TDB)=2450123.7 could be +** expressed in any of these ways, among others: +** +** epna epnb +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) In accordance with normal star-catalog conventions, the object's +** right ascension and declination are freed from the effects of +** secular aberration. The frame, which is aligned to the catalog +** equator and equinox, is Lorentzian and centered on the SSB. +** +** The proper motions are the rate of change of the right ascension +** and declination at the catalog epoch and are in radians per TDB +** Julian year. +** +** The parallax and radial velocity are in the same frame. +** +** 3) Care is needed with units. The star coordinates are in radians +** and the proper motions in radians per Julian year, but the +** parallax is in arcseconds. +** +** 4) The RA proper motion is in terms of coordinate angle, not true +** angle. If the catalog uses arcseconds for both RA and Dec proper +** motions, the RA proper motion will need to be divided by cos(Dec) +** before use. +** +** 5) Straight-line motion at constant speed, in the inertial frame, +** is assumed. +** +** 6) An extremely small (or zero or negative) parallax is interpreted +** to mean that the object is on the "celestial sphere", the radius +** of which is an arbitrary (large) value (see the eraStarpv +** function for the value used). When the distance is overridden in +** this way, the status, initially zero, has 1 added to it. +** +** 7) If the space velocity is a significant fraction of c (see the +** constant VMAX in the function eraStarpv), it is arbitrarily set +** to zero. When this action occurs, 2 is added to the status. +** +** 8) The relativistic adjustment carried out in the eraStarpv function +** involves an iterative calculation. If the process fails to +** converge within a set number of iterations, 4 is added to the +** status. +** +** Called: +** eraStarpv star catalog data to space motion pv-vector +** eraPvu update a pv-vector +** eraPdp scalar product of two p-vectors +** eraPvstar space motion pv-vector to star catalog data +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double pv1[2][3], tl1, dt, pv[2][3], r2, rdv, v2, c2mv2, tl2, + pv2[2][3]; + int j1, j2, j; + + +/* RA,Dec etc. at the "before" epoch to space motion pv-vector. */ + j1 = eraStarpv(ra1, dec1, pmr1, pmd1, px1, rv1, pv1); + +/* Light time when observed (days). */ + tl1 = eraPm(pv1[0]) / ERFA_DC; + +/* Time interval, "before" to "after" (days). */ + dt = (ep2a - ep1a) + (ep2b - ep1b); + +/* Move star along track from the "before" observed position to the */ +/* "after" geometric position. */ + eraPvu(dt + tl1, pv1, pv); + +/* From this geometric position, deduce the observed light time (days) */ +/* at the "after" epoch (with theoretically unneccessary error check). */ + r2 = eraPdp(pv[0], pv[0]); + rdv = eraPdp(pv[0], pv[1]); + v2 = eraPdp(pv[1], pv[1]); + c2mv2 = ERFA_DC*ERFA_DC - v2; + if (c2mv2 <= 0) return -1; + tl2 = (-rdv + sqrt(rdv*rdv + c2mv2*r2)) / c2mv2; + +/* Move the position along track from the observed place at the */ +/* "before" epoch to the observed place at the "after" epoch. */ + eraPvu(dt + (tl1 - tl2), pv1, pv2); + +/* Space motion pv-vector to RA,Dec etc. at the "after" epoch. */ + j2 = eraPvstar(pv2, ra2, dec2, pmr2, pmd2, px2, rv2); + +/* Final status. */ + j = (j2 == 0) ? j1 : -1; + + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/starpv.c b/erfa/starpv.c new file mode 100644 index 0000000..c79fe77 --- /dev/null +++ b/erfa/starpv.c @@ -0,0 +1,273 @@ +#include "erfa.h" + +int eraStarpv(double ra, double dec, + double pmr, double pmd, double px, double rv, + double pv[2][3]) +/* +** - - - - - - - - - - +** e r a S t a r p v +** - - - - - - - - - - +** +** Convert star catalog coordinates to position+velocity vector. +** +** Given (Note 1): +** ra double right ascension (radians) +** dec double declination (radians) +** pmr double RA proper motion (radians/year) +** pmd double Dec proper motion (radians/year) +** px double parallax (arcseconds) +** rv double radial velocity (km/s, positive = receding) +** +** Returned (Note 2): +** pv double[2][3] pv-vector (AU, AU/day) +** +** Returned (function value): +** int status: +** 0 = no warnings +** 1 = distance overridden (Note 6) +** 2 = excessive speed (Note 7) +** 4 = solution didn't converge (Note 8) +** else = binary logical OR of the above +** +** Notes: +** +** 1) The star data accepted by this function are "observables" for an +** imaginary observer at the solar-system barycenter. Proper motion +** and radial velocity are, strictly, in terms of barycentric +** coordinate time, TCB. For most practical applications, it is +** permissible to neglect the distinction between TCB and ordinary +** "proper" time on Earth (TT/TAI). The result will, as a rule, be +** limited by the intrinsic accuracy of the proper-motion and +** radial-velocity data; moreover, the pv-vector is likely to be +** merely an intermediate result, so that a change of time unit +** would cancel out overall. +** +** In accordance with normal star-catalog conventions, the object's +** right ascension and declination are freed from the effects of +** secular aberration. The frame, which is aligned to the catalog +** equator and equinox, is Lorentzian and centered on the SSB. +** +** 2) The resulting position and velocity pv-vector is with respect to +** the same frame and, like the catalog coordinates, is freed from +** the effects of secular aberration. Should the "coordinate +** direction", where the object was located at the catalog epoch, be +** required, it may be obtained by calculating the magnitude of the +** position vector pv[0][0-2] dividing by the speed of light in +** AU/day to give the light-time, and then multiplying the space +** velocity pv[1][0-2] by this light-time and adding the result to +** pv[0][0-2]. +** +** Summarizing, the pv-vector returned is for most stars almost +** identical to the result of applying the standard geometrical +** "space motion" transformation. The differences, which are the +** subject of the Stumpff paper referenced below, are: +** +** (i) In stars with significant radial velocity and proper motion, +** the constantly changing light-time distorts the apparent proper +** motion. Note that this is a classical, not a relativistic, +** effect. +** +** (ii) The transformation complies with special relativity. +** +** 3) Care is needed with units. The star coordinates are in radians +** and the proper motions in radians per Julian year, but the +** parallax is in arcseconds; the radial velocity is in km/s, but +** the pv-vector result is in AU and AU/day. +** +** 4) The RA proper motion is in terms of coordinate angle, not true +** angle. If the catalog uses arcseconds for both RA and Dec proper +** motions, the RA proper motion will need to be divided by cos(Dec) +** before use. +** +** 5) Straight-line motion at constant speed, in the inertial frame, +** is assumed. +** +** 6) An extremely small (or zero or negative) parallax is interpreted +** to mean that the object is on the "celestial sphere", the radius +** of which is an arbitrary (large) value (see the constant PXMIN). +** When the distance is overridden in this way, the status, +** initially zero, has 1 added to it. +** +** 7) If the space velocity is a significant fraction of c (see the +** constant VMAX), it is arbitrarily set to zero. When this action +** occurs, 2 is added to the status. +** +** 8) The relativistic adjustment involves an iterative calculation. +** If the process fails to converge within a set number (IMAX) of +** iterations, 4 is added to the status. +** +** 9) The inverse transformation is performed by the function +** eraPvstar. +** +** Called: +** eraS2pv spherical coordinates to pv-vector +** eraPm modulus of p-vector +** eraZp zero p-vector +** eraPn decompose p-vector into modulus and direction +** eraPdp scalar product of two p-vectors +** eraSxp multiply p-vector by scalar +** eraPmp p-vector minus p-vector +** eraPpp p-vector plus p-vector +** +** Reference: +** +** Stumpff, P., 1985, Astron.Astrophys. 144, 232-240. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ +/* Smallest allowed parallax */ + static const double PXMIN = 1e-7; + +/* Largest allowed speed (fraction of c) */ + static const double VMAX = 0.5; + +/* Maximum number of iterations for relativistic solution */ + static const int IMAX = 100; + + int i, iwarn; + double w, r, rd, rad, decd, v, x[3], usr[3], ust[3], + vsr, vst, betst, betsr, bett, betr, + dd, ddel, ur[3], ut[3], + d = 0.0, del = 0.0, /* to prevent */ + odd = 0.0, oddel = 0.0, /* compiler */ + od = 0.0, odel = 0.0; /* warnings */ + + +/* Distance (AU). */ + if (px >= PXMIN) { + w = px; + iwarn = 0; + } else { + w = PXMIN; + iwarn = 1; + } + r = ERFA_DR2AS / w; + +/* Radial velocity (AU/day). */ + rd = ERFA_DAYSEC * rv * 1e3 / ERFA_DAU; + +/* Proper motion (radian/day). */ + rad = pmr / ERFA_DJY; + decd = pmd / ERFA_DJY; + +/* To pv-vector (AU,AU/day). */ + eraS2pv(ra, dec, r, rad, decd, rd, pv); + +/* If excessive velocity, arbitrarily set it to zero. */ + v = eraPm(pv[1]); + if (v / ERFA_DC > VMAX) { + eraZp(pv[1]); + iwarn += 2; + } + +/* Isolate the radial component of the velocity (AU/day). */ + eraPn(pv[0], &w, x); + vsr = eraPdp(x, pv[1]); + eraSxp(vsr, x, usr); + +/* Isolate the transverse component of the velocity (AU/day). */ + eraPmp(pv[1], usr, ust); + vst = eraPm(ust); + +/* Special-relativity dimensionless parameters. */ + betsr = vsr / ERFA_DC; + betst = vst / ERFA_DC; + +/* Determine the inertial-to-observed relativistic correction terms. */ + bett = betst; + betr = betsr; + for (i = 0; i < IMAX; i++) { + d = 1.0 + betr; + del = sqrt(1.0 - betr*betr - bett*bett) - 1.0; + betr = d * betsr + del; + bett = d * betst; + if (i > 0) { + dd = fabs(d - od); + ddel = fabs(del - odel); + if ((i > 1) && (dd >= odd) && (ddel >= oddel)) break; + odd = dd; + oddel = ddel; + } + od = d; + odel = del; + } + if (i >= IMAX) iwarn += 4; + +/* Replace observed radial velocity with inertial value. */ + w = (betsr != 0.0) ? d + del / betsr : 1.0; + eraSxp(w, usr, ur); + +/* Replace observed tangential velocity with inertial value. */ + eraSxp(d, ust, ut); + +/* Combine the two to obtain the inertial space velocity. */ + eraPpp(ur, ut, pv[1]); + +/* Return the status. */ + return iwarn; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/sxp.c b/erfa/sxp.c new file mode 100644 index 0000000..14e5b27 --- /dev/null +++ b/erfa/sxp.c @@ -0,0 +1,93 @@ +#include "erfa.h" + +void eraSxp(double s, double p[3], double sp[3]) +/* +** - - - - - - - +** e r a S x p +** - - - - - - - +** +** Multiply a p-vector by a scalar. +** +** Given: +** s double scalar +** p double[3] p-vector +** +** Returned: +** sp double[3] s * p +** +** Note: +** It is permissible for p and sp to be the same array. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + sp[0] = s * p[0]; + sp[1] = s * p[1]; + sp[2] = s * p[2]; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/sxpv.c b/erfa/sxpv.c new file mode 100644 index 0000000..0dcc03a --- /dev/null +++ b/erfa/sxpv.c @@ -0,0 +1,94 @@ +#include "erfa.h" + +void eraSxpv(double s, double pv[2][3], double spv[2][3]) +/* +** - - - - - - - - +** e r a S x p v +** - - - - - - - - +** +** Multiply a pv-vector by a scalar. +** +** Given: +** s double scalar +** pv double[2][3] pv-vector +** +** Returned: +** spv double[2][3] s * pv +** +** Note: +** It is permissible for pv and spv to be the same array +** +** Called: +** eraS2xpv multiply pv-vector by two scalars +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraS2xpv(s, s, pv, spv); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/t_erfa_c.c b/erfa/t_erfa_c.c new file mode 100644 index 0000000..47c7a7c --- /dev/null +++ b/erfa/t_erfa_c.c @@ -0,0 +1,9311 @@ +#include +#include + +static int verbose = 0; + +/* +** - - - - - - - - - +** t _ e r f a _ c +** - - - - - - - - - +** +** Validate the ERFA C functions. +** +** Each ERFA function is at least called and a usually quite basic test +** is performed. Successful completion is signalled by a confirming +** message. Failure of a given function or group of functions results +** in error messages. +** +** All messages go to stdout. +** +** This revision: 2013 November 7 +** +*/ + +static void viv(int ival, int ivalok, + const char *func, const char *test, int *status) +/* +** - - - - +** v i v +** - - - - +** +** Validate an integer result. +** +** Internal function used by t_erfa_c program. +** +** Given: +** ival int value computed by function under test +** ivalok int correct value +** func char[] name of function under test +** test char[] name of individual test +** +** Given and returned: +** status int set to TRUE if test fails +** +** This revision: 2013 August 7 +*/ +{ + if (ival != ivalok) { + *status = 1; + printf("%s failed: %s want %d got %d\n", + func, test, ivalok, ival); + } else if (verbose) { + printf("%s passed: %s want %d got %d\n", + func, test, ivalok, ival); + } + +} + +static void vvd(double val, double valok, double dval, + const char *func, const char *test, int *status) +/* +** - - - - +** v v d +** - - - - +** +** Validate a double result. +** +** Internal function used by t_erfa_c program. +** +** Given: +** val double value computed by function under test +** valok double expected value +** dval double maximum allowable error +** func char[] name of function under test +** test char[] name of individual test +** +** Given and returned: +** status int set to TRUE if test fails +** +** This revision: 2013 August 7 +*/ +{ + double a, f; /* absolute and fractional error */ + + + a = val - valok; + if (fabs(a) > dval) { + f = fabs(valok / a); + *status = 1; + printf("%s failed: %s want %.20g got %.20g (1/%.3g)\n", + func, test, valok, val, f); + } else if (verbose) { + printf("%s passed: %s want %.20g got %.20g\n", + func, test, valok, val); + } + +} + +static void t_a2af(int *status) +/* +** - - - - - - - +** t _ a 2 a f +** - - - - - - - +** +** Test eraA2af function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraA2af, viv +** +** This revision: 2013 August 7 +*/ +{ + int idmsf[4]; + char s; + + + eraA2af(4, 2.345, &s, idmsf); + + viv(s, '+', "eraA2af", "s", status); + + viv(idmsf[0], 134, "eraA2af", "0", status); + viv(idmsf[1], 21, "eraA2af", "1", status); + viv(idmsf[2], 30, "eraA2af", "2", status); + viv(idmsf[3], 9706, "eraA2af", "3", status); + +} + +static void t_a2tf(int *status) +/* +** - - - - - - - +** t _ a 2 t f +** - - - - - - - +** +** Test eraA2tf function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraA2tf, viv +** +** This revision: 2013 August 7 +*/ +{ + int ihmsf[4]; + char s; + + + eraA2tf(4, -3.01234, &s, ihmsf); + + viv((int)s, '-', "eraA2tf", "s", status); + + viv(ihmsf[0], 11, "eraA2tf", "0", status); + viv(ihmsf[1], 30, "eraA2tf", "1", status); + viv(ihmsf[2], 22, "eraA2tf", "2", status); + viv(ihmsf[3], 6484, "eraA2tf", "3", status); + +} + +static void t_ab(int *status) +/* +** - - - - - +** t _ a b +** - - - - - +** +** Test eraAb function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAb, vvd +** +** This revision: 2013 October 1 +*/ +{ + double pnat[3], v[3], s, bm1, ppr[3]; + + + pnat[0] = -0.76321968546737951; + pnat[1] = -0.60869453983060384; + pnat[2] = -0.21676408580639883; + v[0] = 2.1044018893653786e-5; + v[1] = -8.9108923304429319e-5; + v[2] = -3.8633714797716569e-5; + s = 0.99980921395708788; + bm1 = 0.99999999506209258; + + eraAb(pnat, v, s, bm1, ppr); + + vvd(ppr[0], -0.7631631094219556269, 1e-12, "eraAb", "1", status); + vvd(ppr[1], -0.6087553082505590832, 1e-12, "eraAb", "2", status); + vvd(ppr[2], -0.2167926269368471279, 1e-12, "eraAb", "3", status); + +} + +static void t_af2a(int *status) +/* +** - - - - - - - +** t _ a f 2 a +** - - - - - - - +** +** Test eraAf2a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAf2a, viv +** +** This revision: 2013 August 7 +*/ +{ + double a; + int j; + + + j = eraAf2a('-', 45, 13, 27.2, &a); + + vvd(a, -0.7893115794313644842, 1e-12, "eraAf2a", "a", status); + viv(j, 0, "eraAf2a", "j", status); + +} + +static void t_anp(int *status) +/* +** - - - - - - +** t _ a n p +** - - - - - - +** +** Test eraAnp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAnp, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraAnp(-0.1), 6.183185307179586477, 1e-12, "eraAnp", "", status); +} + +static void t_anpm(int *status) +/* +** - - - - - - - +** t _ a n p m +** - - - - - - - +** +** Test eraAnpm function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAnpm, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraAnpm(-4.0), 2.283185307179586477, 1e-12, "eraAnpm", "", status); +} + +static void t_apcg(int *status) +/* +** - - - - - - - +** t _ a p c g +** - - - - - - - +** +** Test eraApcg function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApcg, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, ebpv[2][3], ehp[3]; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + ebpv[0][0] = 0.901310875; + ebpv[0][1] = -0.417402664; + ebpv[0][2] = -0.180982288; + ebpv[1][0] = 0.00742727954; + ebpv[1][1] = 0.0140507459; + ebpv[1][2] = 0.00609045792; + ehp[0] = 0.903358544; + ehp[1] = -0.415395237; + ehp[2] = -0.180084014; + + eraApcg(date1, date2, ebpv, ehp, &astrom); + + vvd(astrom.pmt, 12.65133794027378508, 1e-11, + "eraApcg", "pmt", status); + vvd(astrom.eb[0], 0.901310875, 1e-12, + "eraApcg", "eb(1)", status); + vvd(astrom.eb[1], -0.417402664, 1e-12, + "eraApcg", "eb(2)", status); + vvd(astrom.eb[2], -0.180982288, 1e-12, + "eraApcg", "eb(3)", status); + vvd(astrom.eh[0], 0.8940025429324143045, 1e-12, + "eraApcg", "eh(1)", status); + vvd(astrom.eh[1], -0.4110930268679817955, 1e-12, + "eraApcg", "eh(2)", status); + vvd(astrom.eh[2], -0.1782189004872870264, 1e-12, + "eraApcg", "eh(3)", status); + vvd(astrom.em, 1.010465295811013146, 1e-12, + "eraApcg", "em", status); + vvd(astrom.v[0], 0.4289638897813379954e-4, 1e-16, + "eraApcg", "v(1_", status); + vvd(astrom.v[1], 0.8115034021720941898e-4, 1e-16, + "eraApcg", "v(2)", status); + vvd(astrom.v[2], 0.3517555123437237778e-4, 1e-16, + "eraApcg", "v(3)", status); + vvd(astrom.bm1, 0.9999999951686013336, 1e-12, + "eraApcg", "bm1", status); + vvd(astrom.bpn[0][0], 1.0, 0.0, + "eraApcg", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0.0, 0.0, + "eraApcg", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0.0, 0.0, + "eraApcg", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], 0.0, 0.0, + "eraApcg", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 1.0, 0.0, + "eraApcg", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], 0.0, 0.0, + "eraApcg", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], 0.0, 0.0, + "eraApcg", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0.0, 0.0, + "eraApcg", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 1.0, 0.0, + "eraApcg", "bpn(3,3)", status); + +} + +static void t_apcg13(int *status) +/* +** - - - - - - - - - +** t _ a p c g 1 3 +** - - - - - - - - - +** +** Test eraApcg13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApcg13, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + + eraApcg13(date1, date2, &astrom); + + vvd(astrom.pmt, 12.65133794027378508, 1e-11, + "eraApcg13", "pmt", status); + vvd(astrom.eb[0], 0.9013108747340644755, 1e-12, + "eraApcg13", "eb(1)", status); + vvd(astrom.eb[1], -0.4174026640406119957, 1e-12, + "eraApcg13", "eb(2)", status); + vvd(astrom.eb[2], -0.1809822877867817771, 1e-12, + "eraApcg13", "eb(3)", status); + vvd(astrom.eh[0], 0.8940025429255499549, 1e-12, + "eraApcg13", "eh(1)", status); + vvd(astrom.eh[1], -0.4110930268331896318, 1e-12, + "eraApcg13", "eh(2)", status); + vvd(astrom.eh[2], -0.1782189006019749850, 1e-12, + "eraApcg13", "eh(3)", status); + vvd(astrom.em, 1.010465295964664178, 1e-12, + "eraApcg13", "em", status); + vvd(astrom.v[0], 0.4289638897157027528e-4, 1e-16, + "eraApcg13", "v(1)", status); + vvd(astrom.v[1], 0.8115034002544663526e-4, 1e-16, + "eraApcg13", "v(2)", status); + vvd(astrom.v[2], 0.3517555122593144633e-4, 1e-16, + "eraApcg13", "v(3)", status); + vvd(astrom.bm1, 0.9999999951686013498, 1e-12, + "eraApcg13", "bm1", status); + vvd(astrom.bpn[0][0], 1.0, 0.0, + "eraApcg13", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0.0, 0.0, + "eraApcg13", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0.0, 0.0, + "eraApcg13", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], 0.0, 0.0, + "eraApcg13", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 1.0, 0.0, + "eraApcg13", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], 0.0, 0.0, + "eraApcg13", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], 0.0, 0.0, + "eraApcg13", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0.0, 0.0, + "eraApcg13", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 1.0, 0.0, + "eraApcg13", "bpn(3,3)", status); + +} + +static void t_apci(int *status) +/* +** - - - - - - - +** t _ a p c i +** - - - - - - - +** +** Test eraApci function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApci, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, ebpv[2][3], ehp[3], x, y, s; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + ebpv[0][0] = 0.901310875; + ebpv[0][1] = -0.417402664; + ebpv[0][2] = -0.180982288; + ebpv[1][0] = 0.00742727954; + ebpv[1][1] = 0.0140507459; + ebpv[1][2] = 0.00609045792; + ehp[0] = 0.903358544; + ehp[1] = -0.415395237; + ehp[2] = -0.180084014; + x = 0.0013122272; + y = -2.92808623e-5; + s = 3.05749468e-8; + + eraApci(date1, date2, ebpv, ehp, x, y, s, &astrom); + + vvd(astrom.pmt, 12.65133794027378508, 1e-11, + "eraApci", "pmt", status); + vvd(astrom.eb[0], 0.901310875, 1e-12, + "eraApci", "eb(1)", status); + vvd(astrom.eb[1], -0.417402664, 1e-12, + "eraApci", "eb(2)", status); + vvd(astrom.eb[2], -0.180982288, 1e-12, + "eraApci", "eb(3)", status); + vvd(astrom.eh[0], 0.8940025429324143045, 1e-12, + "eraApci", "eh(1)", status); + vvd(astrom.eh[1], -0.4110930268679817955, 1e-12, + "eraApci", "eh(2)", status); + vvd(astrom.eh[2], -0.1782189004872870264, 1e-12, + "eraApci", "eh(3)", status); + vvd(astrom.em, 1.010465295811013146, 1e-12, + "eraApci", "em", status); + vvd(astrom.v[0], 0.4289638897813379954e-4, 1e-16, + "eraApci", "v(1)", status); + vvd(astrom.v[1], 0.8115034021720941898e-4, 1e-16, + "eraApci", "v(2)", status); + vvd(astrom.v[2], 0.3517555123437237778e-4, 1e-16, + "eraApci", "v(3)", status); + vvd(astrom.bm1, 0.9999999951686013336, 1e-12, + "eraApci", "bm1", status); + vvd(astrom.bpn[0][0], 0.9999991390295159156, 1e-12, + "eraApci", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0.4978650072505016932e-7, 1e-12, + "eraApci", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0.1312227200000000000e-2, 1e-12, + "eraApci", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], -0.1136336653771609630e-7, 1e-12, + "eraApci", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 0.9999999995713154868, 1e-12, + "eraApci", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], -0.2928086230000000000e-4, 1e-12, + "eraApci", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], -0.1312227200895260194e-2, 1e-12, + "eraApci", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0.2928082217872315680e-4, 1e-12, + "eraApci", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 0.9999991386008323373, 1e-12, + "eraApci", "bpn(3,3)", status); + +} + +static void t_apci13(int *status) +/* +** - - - - - - - - - +** t _ a p c i 1 3 +** - - - - - - - - - +** +** Test eraApci13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApci13, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, eo; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + + eraApci13(date1, date2, &astrom, &eo); + + vvd(astrom.pmt, 12.65133794027378508, 1e-11, + "eraApci13", "pmt", status); + vvd(astrom.eb[0], 0.9013108747340644755, 1e-12, + "eraApci13", "eb(1)", status); + vvd(astrom.eb[1], -0.4174026640406119957, 1e-12, + "eraApci13", "eb(2)", status); + vvd(astrom.eb[2], -0.1809822877867817771, 1e-12, + "eraApci13", "eb(3)", status); + vvd(astrom.eh[0], 0.8940025429255499549, 1e-12, + "eraApci13", "eh(1)", status); + vvd(astrom.eh[1], -0.4110930268331896318, 1e-12, + "eraApci13", "eh(2)", status); + vvd(astrom.eh[2], -0.1782189006019749850, 1e-12, + "eraApci13", "eh(3)", status); + vvd(astrom.em, 1.010465295964664178, 1e-12, + "eraApci13", "em", status); + vvd(astrom.v[0], 0.4289638897157027528e-4, 1e-16, + "eraApci13", "v(1)", status); + vvd(astrom.v[1], 0.8115034002544663526e-4, 1e-16, + "eraApci13", "v(2)", status); + vvd(astrom.v[2], 0.3517555122593144633e-4, 1e-16, + "eraApci13", "v(3)", status); + vvd(astrom.bm1, 0.9999999951686013498, 1e-12, + "eraApci13", "bm1", status); + vvd(astrom.bpn[0][0], 0.9999992060376761710, 1e-12, + "eraApci13", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0.4124244860106037157e-7, 1e-12, + "eraApci13", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0.1260128571051709670e-2, 1e-12, + "eraApci13", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], -0.1282291987222130690e-7, 1e-12, + "eraApci13", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 0.9999999997456835325, 1e-12, + "eraApci13", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], -0.2255288829420524935e-4, 1e-12, + "eraApci13", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], -0.1260128571661374559e-2, 1e-12, + "eraApci13", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0.2255285422953395494e-4, 1e-12, + "eraApci13", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 0.9999992057833604343, 1e-12, + "eraApci13", "bpn(3,3)", status); + vvd(eo, -0.2900618712657375647e-2, 1e-12, + "eraApci13", "eo", status); + +} + +static void t_apco(int *status) +/* +** - - - - - - - +** t _ a p c o +** - - - - - - - +** +** Test eraApco function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApco, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, ebpv[2][3], ehp[3], x, y, s, + theta, elong, phi, hm, xp, yp, sp, refa, refb; + eraASTROM astrom; + + + date1 = 2456384.5; + date2 = 0.970031644; + ebpv[0][0] = -0.974170438; + ebpv[0][1] = -0.211520082; + ebpv[0][2] = -0.0917583024; + ebpv[1][0] = 0.00364365824; + ebpv[1][1] = -0.0154287319; + ebpv[1][2] = -0.00668922024; + ehp[0] = -0.973458265; + ehp[1] = -0.209215307; + ehp[2] = -0.0906996477; + x = 0.0013122272; + y = -2.92808623e-5; + s = 3.05749468e-8; + theta = 3.14540971; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + sp = -3.01974337e-11; + refa = 0.000201418779; + refb = -2.36140831e-7; + + eraApco(date1, date2, ebpv, ehp, x, y, s, + theta, elong, phi, hm, xp, yp, sp, + refa, refb, &astrom); + + vvd(astrom.pmt, 13.25248468622587269, 1e-11, + "eraApco", "pmt", status); + vvd(astrom.eb[0], -0.9741827110630897003, 1e-12, + "eraApco", "eb(1)", status); + vvd(astrom.eb[1], -0.2115130190135014340, 1e-12, + "eraApco", "eb(2)", status); + vvd(astrom.eb[2], -0.09179840186968295686, 1e-12, + "eraApco", "eb(3)", status); + vvd(astrom.eh[0], -0.9736425571689670428, 1e-12, + "eraApco", "eh(1)", status); + vvd(astrom.eh[1], -0.2092452125848862201, 1e-12, + "eraApco", "eh(2)", status); + vvd(astrom.eh[2], -0.09075578152261439954, 1e-12, + "eraApco", "eh(3)", status); + vvd(astrom.em, 0.9998233241710617934, 1e-12, + "eraApco", "em", status); + vvd(astrom.v[0], 0.2078704985147609823e-4, 1e-16, + "eraApco", "v(1)", status); + vvd(astrom.v[1], -0.8955360074407552709e-4, 1e-16, + "eraApco", "v(2)", status); + vvd(astrom.v[2], -0.3863338980073114703e-4, 1e-16, + "eraApco", "v(3)", status); + vvd(astrom.bm1, 0.9999999950277561600, 1e-12, + "eraApco", "bm1", status); + vvd(astrom.bpn[0][0], 0.9999991390295159156, 1e-12, + "eraApco", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0.4978650072505016932e-7, 1e-12, + "eraApco", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0.1312227200000000000e-2, 1e-12, + "eraApco", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], -0.1136336653771609630e-7, 1e-12, + "eraApco", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 0.9999999995713154868, 1e-12, + "eraApco", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], -0.2928086230000000000e-4, 1e-12, + "eraApco", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], -0.1312227200895260194e-2, 1e-12, + "eraApco", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0.2928082217872315680e-4, 1e-12, + "eraApco", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 0.9999991386008323373, 1e-12, + "eraApco", "bpn(3,3)", status); + vvd(astrom.along, -0.5278008060301974337, 1e-12, + "eraApco", "along", status); + vvd(astrom.xpl, 0.1133427418174939329e-5, 1e-17, + "eraApco", "xpl", status); + vvd(astrom.ypl, 0.1453347595745898629e-5, 1e-17, + "eraApco", "ypl", status); + vvd(astrom.sphi, -0.9440115679003211329, 1e-12, + "eraApco", "sphi", status); + vvd(astrom.cphi, 0.3299123514971474711, 1e-12, + "eraApco", "cphi", status); + vvd(astrom.diurab, 0, 0, + "eraApco", "diurab", status); + vvd(astrom.eral, 2.617608903969802566, 1e-12, + "eraApco", "eral", status); + vvd(astrom.refa, 0.2014187790000000000e-3, 1e-15, + "eraApco", "refa", status); + vvd(astrom.refb, -0.2361408310000000000e-6, 1e-18, + "eraApco", "refb", status); + +} + +static void t_apco13(int *status) +/* +** - - - - - - - - - +** t _ a p c o 1 3 +** - - - - - - - - - +** +** Test eraApco13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApco13, vvd, viv +** +** This revision: 2013 October 4 +*/ +{ + double utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, eo; + eraASTROM astrom; + int j; + + + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + + j = eraApco13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom, &eo); + + vvd(astrom.pmt, 13.25248468622475727, 1e-11, + "eraApco13", "pmt", status); + vvd(astrom.eb[0], -0.9741827107321449445, 1e-12, + "eraApco13", "eb(1)", status); + vvd(astrom.eb[1], -0.2115130190489386190, 1e-12, + "eraApco13", "eb(2)", status); + vvd(astrom.eb[2], -0.09179840189515518726, 1e-12, + "eraApco13", "eb(3)", status); + vvd(astrom.eh[0], -0.9736425572586866640, 1e-12, + "eraApco13", "eh(1)", status); + vvd(astrom.eh[1], -0.2092452121602867431, 1e-12, + "eraApco13", "eh(2)", status); + vvd(astrom.eh[2], -0.09075578153903832650, 1e-12, + "eraApco13", "eh(3)", status); + vvd(astrom.em, 0.9998233240914558422, 1e-12, + "eraApco13", "em", status); + vvd(astrom.v[0], 0.2078704986751370303e-4, 1e-16, + "eraApco13", "v(1)", status); + vvd(astrom.v[1], -0.8955360100494469232e-4, 1e-16, + "eraApco13", "v(2)", status); + vvd(astrom.v[2], -0.3863338978840051024e-4, 1e-16, + "eraApco13", "v(3)", status); + vvd(astrom.bm1, 0.9999999950277561368, 1e-12, + "eraApco13", "bm1", status); + vvd(astrom.bpn[0][0], 0.9999991390295147999, 1e-12, + "eraApco13", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0.4978650075315529277e-7, 1e-12, + "eraApco13", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0.001312227200850293372, 1e-12, + "eraApco13", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], -0.1136336652812486604e-7, 1e-12, + "eraApco13", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 0.9999999995713154865, 1e-12, + "eraApco13", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], -0.2928086230975367296e-4, 1e-12, + "eraApco13", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], -0.001312227201745553566, 1e-12, + "eraApco13", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0.2928082218847679162e-4, 1e-12, + "eraApco13", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 0.9999991386008312212, 1e-12, + "eraApco13", "bpn(3,3)", status); + vvd(astrom.along, -0.5278008060301974337, 1e-12, + "eraApco13", "along", status); + vvd(astrom.xpl, 0.1133427418174939329e-5, 1e-17, + "eraApco13", "xpl", status); + vvd(astrom.ypl, 0.1453347595745898629e-5, 1e-17, + "eraApco13", "ypl", status); + vvd(astrom.sphi, -0.9440115679003211329, 1e-12, + "eraApco13", "sphi", status); + vvd(astrom.cphi, 0.3299123514971474711, 1e-12, + "eraApco13", "cphi", status); + vvd(astrom.diurab, 0, 0, + "eraApco13", "diurab", status); + vvd(astrom.eral, 2.617608909189066140, 1e-12, + "eraApco13", "eral", status); + vvd(astrom.refa, 0.2014187785940396921e-3, 1e-15, + "eraApco13", "refa", status); + vvd(astrom.refb, -0.2361408314943696227e-6, 1e-18, + "eraApco13", "refb", status); + vvd(eo, -0.003020548354802412839, 1e-14, + "eraApco13", "eo", status); + viv(j, 0, "eraApco13", "j", status); + +} + +static void t_apcs(int *status) +/* +** - - - - - - - +** t _ a p c s +** - - - - - - - +** +** Test eraApcs function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApcs, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, pv[2][3], ebpv[2][3], ehp[3]; + eraASTROM astrom; + + + date1 = 2456384.5; + date2 = 0.970031644; + pv[0][0] = -1836024.09; + pv[0][1] = 1056607.72; + pv[0][2] = -5998795.26; + pv[1][0] = -77.0361767; + pv[1][1] = -133.310856; + pv[1][2] = 0.0971855934; + ebpv[0][0] = -0.974170438; + ebpv[0][1] = -0.211520082; + ebpv[0][2] = -0.0917583024; + ebpv[1][0] = 0.00364365824; + ebpv[1][1] = -0.0154287319; + ebpv[1][2] = -0.00668922024; + ehp[0] = -0.973458265; + ehp[1] = -0.209215307; + ehp[2] = -0.0906996477; + + eraApcs(date1, date2, pv, ebpv, ehp, &astrom); + + vvd(astrom.pmt, 13.25248468622587269, 1e-11, + "eraApcs", "pmt", status); + vvd(astrom.eb[0], -0.9741827110630456169, 1e-12, + "eraApcs", "eb(1)", status); + vvd(astrom.eb[1], -0.2115130190136085494, 1e-12, + "eraApcs", "eb(2)", status); + vvd(astrom.eb[2], -0.09179840186973175487, 1e-12, + "eraApcs", "eb(3)", status); + vvd(astrom.eh[0], -0.9736425571689386099, 1e-12, + "eraApcs", "eh(1)", status); + vvd(astrom.eh[1], -0.2092452125849967195, 1e-12, + "eraApcs", "eh(2)", status); + vvd(astrom.eh[2], -0.09075578152266466572, 1e-12, + "eraApcs", "eh(3)", status); + vvd(astrom.em, 0.9998233241710457140, 1e-12, + "eraApcs", "em", status); + vvd(astrom.v[0], 0.2078704985513566571e-4, 1e-16, + "eraApcs", "v(1)", status); + vvd(astrom.v[1], -0.8955360074245006073e-4, 1e-16, + "eraApcs", "v(2)", status); + vvd(astrom.v[2], -0.3863338980073572719e-4, 1e-16, + "eraApcs", "v(3)", status); + vvd(astrom.bm1, 0.9999999950277561601, 1e-12, + "eraApcs", "bm1", status); + vvd(astrom.bpn[0][0], 1, 0, + "eraApcs", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0, 0, + "eraApcs", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0, 0, + "eraApcs", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], 0, 0, + "eraApcs", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 1, 0, + "eraApcs", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], 0, 0, + "eraApcs", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], 0, 0, + "eraApcs", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0, 0, + "eraApcs", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 1, 0, + "eraApcs", "bpn(3,3)", status); + +} + +static void t_apcs13(int *status) +/* +** - - - - - - - - - +** t _ a p c s 1 3 +** - - - - - - - - - +** +** Test eraApcs13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApcs13, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, pv[2][3]; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + pv[0][0] = -6241497.16; + pv[0][1] = 401346.896; + pv[0][2] = -1251136.04; + pv[1][0] = -29.264597; + pv[1][1] = -455.021831; + pv[1][2] = 0.0266151194; + + eraApcs13(date1, date2, pv, &astrom); + + vvd(astrom.pmt, 12.65133794027378508, 1e-11, + "eraApcs13", "pmt", status); + vvd(astrom.eb[0], 0.9012691529023298391, 1e-12, + "eraApcs13", "eb(1)", status); + vvd(astrom.eb[1], -0.4173999812023068781, 1e-12, + "eraApcs13", "eb(2)", status); + vvd(astrom.eb[2], -0.1809906511146821008, 1e-12, + "eraApcs13", "eb(3)", status); + vvd(astrom.eh[0], 0.8939939101759726824, 1e-12, + "eraApcs13", "eh(1)", status); + vvd(astrom.eh[1], -0.4111053891734599955, 1e-12, + "eraApcs13", "eh(2)", status); + vvd(astrom.eh[2], -0.1782336880637689334, 1e-12, + "eraApcs13", "eh(3)", status); + vvd(astrom.em, 1.010428384373318379, 1e-12, + "eraApcs13", "em", status); + vvd(astrom.v[0], 0.4279877278327626511e-4, 1e-16, + "eraApcs13", "v(1)", status); + vvd(astrom.v[1], 0.7963255057040027770e-4, 1e-16, + "eraApcs13", "v(2)", status); + vvd(astrom.v[2], 0.3517564000441374759e-4, 1e-16, + "eraApcs13", "v(3)", status); + vvd(astrom.bm1, 0.9999999952947981330, 1e-12, + "eraApcs13", "bm1", status); + vvd(astrom.bpn[0][0], 1, 0, + "eraApcs13", "bpn(1,1)", status); + vvd(astrom.bpn[1][0], 0, 0, + "eraApcs13", "bpn(2,1)", status); + vvd(astrom.bpn[2][0], 0, 0, + "eraApcs13", "bpn(3,1)", status); + vvd(astrom.bpn[0][1], 0, 0, + "eraApcs13", "bpn(1,2)", status); + vvd(astrom.bpn[1][1], 1, 0, + "eraApcs13", "bpn(2,2)", status); + vvd(astrom.bpn[2][1], 0, 0, + "eraApcs13", "bpn(3,2)", status); + vvd(astrom.bpn[0][2], 0, 0, + "eraApcs13", "bpn(1,3)", status); + vvd(astrom.bpn[1][2], 0, 0, + "eraApcs13", "bpn(2,3)", status); + vvd(astrom.bpn[2][2], 1, 0, + "eraApcs13", "bpn(3,3)", status); + +} + +static void t_aper(int *status) +/* +** - - - - - - - +** t _ a p e r +** - - - - - - - +* +** Test eraAper function. +* +** Returned: +** status int FALSE = success, TRUE = fail +* +** Called: eraAper, vvd +* +** This revision: 2013 October 3 +*/ +{ + double theta; + eraASTROM astrom; + + + astrom.along = 1.234; + theta = 5.678; + + eraAper(theta, &astrom); + + vvd(astrom.eral, 6.912000000000000000, 1e-12, + "eraAper", "pmt", status); + +} + +static void t_aper13(int *status) +/* +** - - - - - - - - - +** t _ a p e r 1 3 +** - - - - - - - - - +** +** Test eraAper13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAper13, vvd +** +** This revision: 2013 October 3 +*/ +{ + double ut11, ut12; + eraASTROM astrom; + + + astrom.along = 1.234; + ut11 = 2456165.5; + ut12 = 0.401182685; + + eraAper13(ut11, ut12, &astrom); + + vvd(astrom.eral, 3.316236661789694933, 1e-12, + "eraAper13", "pmt", status); + +} + +static void t_apio(int *status) +/* +** - - - - - - - +** t _ a p i o +** - - - - - - - +** +** Test eraApio function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApio, vvd +** +** This revision: 2013 October 3 +*/ +{ + double sp, theta, elong, phi, hm, xp, yp, refa, refb; + eraASTROM astrom; + + + sp = -3.01974337e-11; + theta = 3.14540971; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + refa = 0.000201418779; + refb = -2.36140831e-7; + + eraApio(sp, theta, elong, phi, hm, xp, yp, refa, refb, &astrom); + + vvd(astrom.along, -0.5278008060301974337, 1e-12, + "eraApio", "along", status); + vvd(astrom.xpl, 0.1133427418174939329e-5, 1e-17, + "eraApio", "xpl", status); + vvd(astrom.ypl, 0.1453347595745898629e-5, 1e-17, + "eraApio", "ypl", status); + vvd(astrom.sphi, -0.9440115679003211329, 1e-12, + "eraApio", "sphi", status); + vvd(astrom.cphi, 0.3299123514971474711, 1e-12, + "eraApio", "cphi", status); + vvd(astrom.diurab, 0.5135843661699913529e-6, 1e-12, + "eraApio", "diurab", status); + vvd(astrom.eral, 2.617608903969802566, 1e-12, + "eraApio", "eral", status); + vvd(astrom.refa, 0.2014187790000000000e-3, 1e-15, + "eraApio", "refa", status); + vvd(astrom.refb, -0.2361408310000000000e-6, 1e-18, + "eraApio", "refb", status); + +} + +static void t_apio13(int *status) +/* +** - - - - - - - - - +** t _ a p i o 1 3 +** - - - - - - - - - +** +** Test eraApio13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApio13, vvd, viv +** +** This revision: 2013 October 4 +*/ +{ + double utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tc, rh, wl; + int j; + eraASTROM astrom; + + + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + + j = eraApio13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom); + + vvd(astrom.along, -0.5278008060301974337, 1e-12, + "eraApio13", "along", status); + vvd(astrom.xpl, 0.1133427418174939329e-5, 1e-17, + "eraApio13", "xpl", status); + vvd(astrom.ypl, 0.1453347595745898629e-5, 1e-17, + "eraApio13", "ypl", status); + vvd(astrom.sphi, -0.9440115679003211329, 1e-12, + "eraApio13", "sphi", status); + vvd(astrom.cphi, 0.3299123514971474711, 1e-12, + "eraApio13", "cphi", status); + vvd(astrom.diurab, 0.5135843661699913529e-6, 1e-12, + "eraApio13", "diurab", status); + vvd(astrom.eral, 2.617608909189066140, 1e-12, + "eraApio13", "eral", status); + vvd(astrom.refa, 0.2014187785940396921e-3, 1e-15, + "eraApio13", "refa", status); + vvd(astrom.refb, -0.2361408314943696227e-6, 1e-18, + "eraApio13", "refb", status); + viv(j, 0, "eraApio13", "j", status); + +} + +static void t_atci13(int *status) +/* +** - - - - - - - - - +** t _ a t c i 1 3 +** - - - - - - - - - +** +** Test eraAtci13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAtci13, vvd +** +** This revision: 2013 October 3 +*/ +{ + double rc, dc, pr, pd, px, rv, date1, date2, ri, di, eo; + + + rc = 2.71; + dc = 0.174; + pr = 1e-5; + pd = 5e-6; + px = 0.1; + rv = 55.0; + date1 = 2456165.5; + date2 = 0.401182685; + + eraAtci13(rc, dc, pr, pd, px, rv, date1, date2, &ri, &di, &eo); + + vvd(ri, 2.710121572969038991, 1e-12, + "eraAtci13", "ri", status); + vvd(di, 0.1729371367218230438, 1e-12, + "eraAtci13", "di", status); + vvd(eo, -0.002900618712657375647, 1e-14, + "eraAtci13", "eo", status); + +} + +static void t_atciq(int *status) +/* +** - - - - - - - - +** t _ a t c i q +** - - - - - - - - +** +** Test eraAtciq function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApci13, eraAtciq, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, eo, rc, dc, pr, pd, px, rv, ri, di; + eraASTROM astrom; + + date1 = 2456165.5; + date2 = 0.401182685; + eraApci13(date1, date2, &astrom, &eo); + rc = 2.71; + dc = 0.174; + pr = 1e-5; + pd = 5e-6; + px = 0.1; + rv = 55.0; + + eraAtciq(rc, dc, pr, pd, px, rv, &astrom, &ri, &di); + + vvd(ri, 2.710121572969038991, 1e-12, "eraAtciq", "ri", status); + vvd(di, 0.1729371367218230438, 1e-12, "eraAtciq", "di", status); + +} + +static void t_atciqn(int *status) +/* +** - - - - - - - - - +** t _ a t c i q n +** - - - - - - - - - +** +** Test eraAtciqn function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApci13, eraAtciqn, vvd +** +** This revision: 2013 October 3 +*/ +{ + eraLDBODY b[3]; + double date1, date2, eo, rc, dc, pr, pd, px, rv, ri, di; + eraASTROM astrom; + + date1 = 2456165.5; + date2 = 0.401182685; + eraApci13(date1, date2, &astrom, &eo); + rc = 2.71; + dc = 0.174; + pr = 1e-5; + pd = 5e-6; + px = 0.1; + rv = 55.0; + b[0].bm = 0.00028574; + b[0].dl = 3e-10; + b[0].pv[0][0] = -7.81014427; + b[0].pv[0][1] = -5.60956681; + b[0].pv[0][2] = -1.98079819; + b[0].pv[1][0] = 0.0030723249; + b[0].pv[1][1] = -0.00406995477; + b[0].pv[1][2] = -0.00181335842; + b[1].bm = 0.00095435; + b[1].dl = 3e-9; + b[1].pv[0][0] = 0.738098796; + b[1].pv[0][1] = 4.63658692; + b[1].pv[0][2] = 1.9693136; + b[1].pv[1][0] = -0.00755816922; + b[1].pv[1][1] = 0.00126913722; + b[1].pv[1][2] = 0.000727999001; + b[2].bm = 1.0; + b[2].dl = 6e-6; + b[2].pv[0][0] = -0.000712174377; + b[2].pv[0][1] = -0.00230478303; + b[2].pv[0][2] = -0.00105865966; + b[2].pv[1][0] = 6.29235213e-6; + b[2].pv[1][1] = -3.30888387e-7; + b[2].pv[1][2] = -2.96486623e-7; + + eraAtciqn ( rc, dc, pr, pd, px, rv, &astrom, 3, b, &ri, &di); + + vvd(ri, 2.710122008105325582, 1e-12, "eraAtciqn", "ri", status); + vvd(di, 0.1729371916491459122, 1e-12, "eraAtciqn", "di", status); + +} + +static void t_atciqz(int *status) +/* +** - - - - - - - - - +** t _ a t c i q z +** - - - - - - - - - +** +** Test eraAtciqz function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApci13, eraAtciqz, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, eo, rc, dc, ri, di; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + eraApci13(date1, date2, &astrom, &eo); + rc = 2.71; + dc = 0.174; + + eraAtciqz(rc, dc, &astrom, &ri, &di); + + vvd(ri, 2.709994899247599271, 1e-12, "eraAtciqz", "ri", status); + vvd(di, 0.1728740720983623469, 1e-12, "eraAtciqz", "di", status); + +} + +static void t_atco13(int *status) +/* +** - - - - - - - - - +** t _ a t c o 1 3 +** - - - - - - - - - +** +** Test eraAtco13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAtco13, vvd, viv +** +** This revision: 2013 October 4 +*/ +{ + double rc, dc, pr, pd, px, rv, utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + aob, zob, hob, dob, rob, eo; + int j; + + + rc = 2.71; + dc = 0.174; + pr = 1e-5; + pd = 5e-6; + px = 0.1; + rv = 55.0; + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + + j = eraAtco13(rc, dc, pr, pd, px, rv, + utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, + &aob, &zob, &hob, &dob, &rob, &eo); + + vvd(aob, 0.09251774485358230653, 1e-12, "eraAtco13", "aob", status); + vvd(zob, 1.407661405256767021, 1e-12, "eraAtco13", "zob", status); + vvd(hob, -0.09265154431403157925, 1e-12, "eraAtco13", "hob", status); + vvd(dob, 0.1716626560075591655, 1e-12, "eraAtco13", "dob", status); + vvd(rob, 2.710260453503097719, 1e-12, "eraAtco13", "rob", status); + vvd(eo, -0.003020548354802412839, 1e-14, "eraAtco13", "eo", status); + viv(j, 0, "eraAtco13", "j", status); + +} + +static void t_atic13(int *status) +/* +** - - - - - - - - - +** t _ a t i c 1 3 +** - - - - - - - - - +** +** Test eraAtic13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAtic13, vvd +** +** This revision: 2013 October 3 +*/ +{ + double ri, di, date1, date2, rc, dc, eo; + + + ri = 2.710121572969038991; + di = 0.1729371367218230438; + date1 = 2456165.5; + date2 = 0.401182685; + + eraAtic13(ri, di, date1, date2, &rc, &dc, &eo); + + vvd(rc, 2.710126504531374930, 1e-12, "eraAtic13", "rc", status); + vvd(dc, 0.1740632537628342320, 1e-12, "eraAtic13", "dc", status); + vvd(eo, -0.002900618712657375647, 1e-14, "eraAtic13", "eo", status); + +} + +static void t_aticq(int *status) +/* +** - - - - - - - - +** t _ a t i c q +** - - - - - - - - +** +** Test eraAticq function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApci13, eraAticq, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, eo, ri, di, rc, dc; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + eraApci13(date1, date2, &astrom, &eo); + ri = 2.710121572969038991; + di = 0.1729371367218230438; + + eraAticq(ri, di, &astrom, &rc, &dc); + + vvd(rc, 2.710126504531374930, 1e-12, "eraAticq", "rc", status); + vvd(dc, 0.1740632537628342320, 1e-12, "eraAticq", "dc", status); + +} + +static void t_aticqn(int *status) +/* +** - - - - - - - - - +** t _ a t i c q n +** - - - - - - - - - +** +** Test eraAticqn function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApci13, eraAticqn, vvd +** +** This revision: 2013 October 3 +*/ +{ + double date1, date2, eo, ri, di, rc, dc; + eraLDBODY b[3]; + eraASTROM astrom; + + + date1 = 2456165.5; + date2 = 0.401182685; + eraApci13(date1, date2, &astrom, &eo); + ri = 2.709994899247599271; + di = 0.1728740720983623469; + b[0].bm = 0.00028574; + b[0].dl = 3e-10; + b[0].pv[0][0] = -7.81014427; + b[0].pv[0][1] = -5.60956681; + b[0].pv[0][2] = -1.98079819; + b[0].pv[1][0] = 0.0030723249; + b[0].pv[1][1] = -0.00406995477; + b[0].pv[1][2] = -0.00181335842; + b[1].bm = 0.00095435; + b[1].dl = 3e-9; + b[1].pv[0][0] = 0.738098796; + b[1].pv[0][1] = 4.63658692; + b[1].pv[0][2] = 1.9693136; + b[1].pv[1][0] = -0.00755816922; + b[1].pv[1][1] = 0.00126913722; + b[1].pv[1][2] = 0.000727999001; + b[2].bm = 1.0; + b[2].dl = 6e-6; + b[2].pv[0][0] = -0.000712174377; + b[2].pv[0][1] = -0.00230478303; + b[2].pv[0][2] = -0.00105865966; + b[2].pv[1][0] = 6.29235213e-6; + b[2].pv[1][1] = -3.30888387e-7; + b[2].pv[1][2] = -2.96486623e-7; + + eraAticqn(ri, di, &astrom, 3, b, &rc, &dc); + + vvd(rc, 2.709999575032685412, 1e-12, "eraAtciqn", "rc", status); + vvd(dc, 0.1739999656317778034, 1e-12, "eraAtciqn", "dc", status); + +} + +static void t_atio13(int *status) +/* +** - - - - - - - - - +** t _ a t i o 1 3 +** - - - - - - - - - +** +** Test eraAtio13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAtio13, vvd, viv +** +** This revision: 2013 October 3 +*/ +{ + double ri, di, utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, aob, zob, hob, dob, rob; + int j; + + + ri = 2.710121572969038991; + di = 0.1729371367218230438; + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + + j = eraAtio13(ri, di, utc1, utc2, dut1, elong, phi, hm, + xp, yp, phpa, tc, rh, wl, + &aob, &zob, &hob, &dob, &rob); + + vvd(aob, 0.09233952224794989993, 1e-12, "eraAtio13", "aob", status); + vvd(zob, 1.407758704513722461, 1e-12, "eraAtio13", "zob", status); + vvd(hob, -0.09247619879782006106, 1e-12, "eraAtio13", "hob", status); + vvd(dob, 0.1717653435758265198, 1e-12, "eraAtio13", "dob", status); + vvd(rob, 2.710085107986886201, 1e-12, "eraAtio13", "rob", status); + viv(j, 0, "eraAtio13", "j", status); + +} + +static void t_atioq(int *status) +/* +** - - - - - - - - +** t _ a t i o q +** - - - - - - - - +** +** Test eraAtioq function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraApio13, eraAtioq, vvd, viv +** +** This revision: 2013 October 4 +*/ +{ + double utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, ri, di, aob, zob, hob, dob, rob; + eraASTROM astrom; + + + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + (void) eraApio13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom); + ri = 2.710121572969038991; + di = 0.1729371367218230438; + + eraAtioq(ri, di, &astrom, &aob, &zob, &hob, &dob, &rob); + + vvd(aob, 0.09233952224794989993, 1e-12, "eraAtioq", "aob", status); + vvd(zob, 1.407758704513722461, 1e-12, "eraAtioq", "zob", status); + vvd(hob, -0.09247619879782006106, 1e-12, "eraAtioq", "hob", status); + vvd(dob, 0.1717653435758265198, 1e-12, "eraAtioq", "dob", status); + vvd(rob, 2.710085107986886201, 1e-12, "eraAtioq", "rob", status); + +} + +static void t_atoc13(int *status) +/* +** - - - - - - - - - +** t _ a t o c 1 3 +** - - - - - - - - - +** +** Test eraAtoc13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAtoc13, vvd, viv +** +** This revision: 2013 October 3 +*/ +{ + double utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + ob1, ob2, rc, dc; + int j; + + + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + + ob1 = 2.710085107986886201; + ob2 = 0.1717653435758265198; + j = eraAtoc13 ( "R", ob1, ob2, utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + &rc, &dc); + vvd(rc, 2.709956744661000609, 1e-12, "eraAtoc13", "R/rc", status); + vvd(dc, 0.1741696500895398562, 1e-12, "eraAtoc13", "R/dc", status); + viv(j, 0, "eraAtoc13", "R/j", status); + + ob1 = -0.09247619879782006106; + ob2 = 0.1717653435758265198; + j = eraAtoc13 ( "H", ob1, ob2, utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + &rc, &dc); + vvd(rc, 2.709956744661000609, 1e-12, "eraAtoc13", "H/rc", status); + vvd(dc, 0.1741696500895398562, 1e-12, "eraAtoc13", "H/dc", status); + viv(j, 0, "eraAtoc13", "H/j", status); + + ob1 = 0.09233952224794989993; + ob2 = 1.407758704513722461; + j = eraAtoc13 ( "A", ob1, ob2, utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + &rc, &dc); + vvd(rc, 2.709956744661000609, 1e-12, "eraAtoc13", "A/rc", status); + vvd(dc, 0.1741696500895398565, 1e-12, "eraAtoc13", "A/dc", status); + viv(j, 0, "eraAtoc13", "A/j", status); + +} + +static void t_atoi13(int *status) +/* +** - - - - - - - - - +** t _ a t o i 1 3 +** - - - - - - - - - +** +** Test eraAtoi13 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraAtoi13, vvd, viv +** +** This revision: 2013 October 3 +*/ +{ + double utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tc, rh, wl, + ob1, ob2, ri, di; + int j; + + + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + + ob1 = 2.710085107986886201; + ob2 = 0.1717653435758265198; + j = eraAtoi13 ( "R", ob1, ob2, utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + &ri, &di); + vvd(ri, 2.710121574449135955, 1e-12, "eraAtoi13", "R/ri", status); + vvd(di, 0.1729371839114567725, 1e-12, "eraAtoi13", "R/di", status); + viv(j, 0, "eraAtoi13", "R/J", status); + + ob1 = -0.09247619879782006106; + ob2 = 0.1717653435758265198; + j = eraAtoi13 ( "H", ob1, ob2, utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + &ri, &di); + vvd(ri, 2.710121574449135955, 1e-12, "eraAtoi13", "H/ri", status); + vvd(di, 0.1729371839114567725, 1e-12, "eraAtoi13", "H/di", status); + viv(j, 0, "eraAtoi13", "H/J", status); + + ob1 = 0.09233952224794989993; + ob2 = 1.407758704513722461; + j = eraAtoi13 ( "A", ob1, ob2, utc1, utc2, dut1, + elong, phi, hm, xp, yp, phpa, tc, rh, wl, + &ri, &di); + vvd(ri, 2.710121574449135955, 1e-12, "eraAtoi13", "A/ri", status); + vvd(di, 0.1729371839114567728, 1e-12, "eraAtoi13", "A/di", status); + viv(j, 0, "eraAtoi13", "A/J", status); + +} + +static void t_atoiq(int *status) +/* +** - - - - - - - - +** t _ a t o i q +** - - - - - - - - +* +** Test eraAtoiq function. +* +** Returned: +** status int FALSE = success, TRUE = fail +* +** Called: eraApio13, eraAtoiq, vvd +* +** This revision: 2013 October 4 +*/ +{ + double utc1, utc2, dut1, elong, phi, hm, xp, yp, phpa, tc, rh, wl, + ob1, ob2, ri, di; + eraASTROM astrom; + + + utc1 = 2456384.5; + utc2 = 0.969254051; + dut1 = 0.1550675; + elong = -0.527800806; + phi = -1.2345856; + hm = 2738.0; + xp = 2.47230737e-7; + yp = 1.82640464e-6; + phpa = 731.0; + tc = 12.8; + rh = 0.59; + wl = 0.55; + (void) eraApio13(utc1, utc2, dut1, elong, phi, hm, xp, yp, + phpa, tc, rh, wl, &astrom); + + ob1 = 2.710085107986886201; + ob2 = 0.1717653435758265198; + eraAtoiq("R", ob1, ob2, &astrom, &ri, &di); + vvd(ri, 2.710121574449135955, 1e-12, + "eraAtoiq", "R/ri", status); + vvd(di, 0.1729371839114567725, 1e-12, + "eraAtoiq", "R/di", status); + + ob1 = -0.09247619879782006106; + ob2 = 0.1717653435758265198; + eraAtoiq("H", ob1, ob2, &astrom, &ri, &di); + vvd(ri, 2.710121574449135955, 1e-12, + "eraAtoiq", "H/ri", status); + vvd(di, 0.1729371839114567725, 1e-12, + "eraAtoiq", "H/di", status); + + ob1 = 0.09233952224794989993; + ob2 = 1.407758704513722461; + eraAtoiq("A", ob1, ob2, &astrom, &ri, &di); + vvd(ri, 2.710121574449135955, 1e-12, + "eraAtoiq", "A/ri", status); + vvd(di, 0.1729371839114567728, 1e-12, + "eraAtoiq", "A/di", status); + +} + +static void t_bi00(int *status) +/* +** - - - - - - - +** t _ b i 0 0 +** - - - - - - - +** +** Test eraBi00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraBi00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsibi, depsbi, dra; + + eraBi00(&dpsibi, &depsbi, &dra); + + vvd(dpsibi, -0.2025309152835086613e-6, 1e-12, + "eraBi00", "dpsibi", status); + vvd(depsbi, -0.3306041454222147847e-7, 1e-12, + "eraBi00", "depsbi", status); + vvd(dra, -0.7078279744199225506e-7, 1e-12, + "eraBi00", "dra", status); +} + +static void t_bp00(int *status) +/* +** - - - - - - - +** t _ b p 0 0 +** - - - - - - - +** +** Test eraBp00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraBp00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rb[3][3], rp[3][3], rbp[3][3]; + + + eraBp00(2400000.5, 50123.9999, rb, rp, rbp); + + vvd(rb[0][0], 0.9999999999999942498, 1e-12, + "eraBp00", "rb11", status); + vvd(rb[0][1], -0.7078279744199196626e-7, 1e-16, + "eraBp00", "rb12", status); + vvd(rb[0][2], 0.8056217146976134152e-7, 1e-16, + "eraBp00", "rb13", status); + vvd(rb[1][0], 0.7078279477857337206e-7, 1e-16, + "eraBp00", "rb21", status); + vvd(rb[1][1], 0.9999999999999969484, 1e-12, + "eraBp00", "rb22", status); + vvd(rb[1][2], 0.3306041454222136517e-7, 1e-16, + "eraBp00", "rb23", status); + vvd(rb[2][0], -0.8056217380986972157e-7, 1e-16, + "eraBp00", "rb31", status); + vvd(rb[2][1], -0.3306040883980552500e-7, 1e-16, + "eraBp00", "rb32", status); + vvd(rb[2][2], 0.9999999999999962084, 1e-12, + "eraBp00", "rb33", status); + + vvd(rp[0][0], 0.9999995504864048241, 1e-12, + "eraBp00", "rp11", status); + vvd(rp[0][1], 0.8696113836207084411e-3, 1e-14, + "eraBp00", "rp12", status); + vvd(rp[0][2], 0.3778928813389333402e-3, 1e-14, + "eraBp00", "rp13", status); + vvd(rp[1][0], -0.8696113818227265968e-3, 1e-14, + "eraBp00", "rp21", status); + vvd(rp[1][1], 0.9999996218879365258, 1e-12, + "eraBp00", "rp22", status); + vvd(rp[1][2], -0.1690679263009242066e-6, 1e-14, + "eraBp00", "rp23", status); + vvd(rp[2][0], -0.3778928854764695214e-3, 1e-14, + "eraBp00", "rp31", status); + vvd(rp[2][1], -0.1595521004195286491e-6, 1e-14, + "eraBp00", "rp32", status); + vvd(rp[2][2], 0.9999999285984682756, 1e-12, + "eraBp00", "rp33", status); + + vvd(rbp[0][0], 0.9999995505175087260, 1e-12, + "eraBp00", "rbp11", status); + vvd(rbp[0][1], 0.8695405883617884705e-3, 1e-14, + "eraBp00", "rbp12", status); + vvd(rbp[0][2], 0.3779734722239007105e-3, 1e-14, + "eraBp00", "rbp13", status); + vvd(rbp[1][0], -0.8695405990410863719e-3, 1e-14, + "eraBp00", "rbp21", status); + vvd(rbp[1][1], 0.9999996219494925900, 1e-12, + "eraBp00", "rbp22", status); + vvd(rbp[1][2], -0.1360775820404982209e-6, 1e-14, + "eraBp00", "rbp23", status); + vvd(rbp[2][0], -0.3779734476558184991e-3, 1e-14, + "eraBp00", "rbp31", status); + vvd(rbp[2][1], -0.1925857585832024058e-6, 1e-14, + "eraBp00", "rbp32", status); + vvd(rbp[2][2], 0.9999999285680153377, 1e-12, + "eraBp00", "rbp33", status); +} + +static void t_bp06(int *status) +/* +** - - - - - - - +** t _ b p 0 6 +** - - - - - - - +** +** Test eraBp06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraBp06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rb[3][3], rp[3][3], rbp[3][3]; + + + eraBp06(2400000.5, 50123.9999, rb, rp, rbp); + + vvd(rb[0][0], 0.9999999999999942497, 1e-12, + "eraBp06", "rb11", status); + vvd(rb[0][1], -0.7078368960971557145e-7, 1e-14, + "eraBp06", "rb12", status); + vvd(rb[0][2], 0.8056213977613185606e-7, 1e-14, + "eraBp06", "rb13", status); + vvd(rb[1][0], 0.7078368694637674333e-7, 1e-14, + "eraBp06", "rb21", status); + vvd(rb[1][1], 0.9999999999999969484, 1e-12, + "eraBp06", "rb22", status); + vvd(rb[1][2], 0.3305943742989134124e-7, 1e-14, + "eraBp06", "rb23", status); + vvd(rb[2][0], -0.8056214211620056792e-7, 1e-14, + "eraBp06", "rb31", status); + vvd(rb[2][1], -0.3305943172740586950e-7, 1e-14, + "eraBp06", "rb32", status); + vvd(rb[2][2], 0.9999999999999962084, 1e-12, + "eraBp06", "rb33", status); + + vvd(rp[0][0], 0.9999995504864960278, 1e-12, + "eraBp06", "rp11", status); + vvd(rp[0][1], 0.8696112578855404832e-3, 1e-14, + "eraBp06", "rp12", status); + vvd(rp[0][2], 0.3778929293341390127e-3, 1e-14, + "eraBp06", "rp13", status); + vvd(rp[1][0], -0.8696112560510186244e-3, 1e-14, + "eraBp06", "rp21", status); + vvd(rp[1][1], 0.9999996218880458820, 1e-12, + "eraBp06", "rp22", status); + vvd(rp[1][2], -0.1691646168941896285e-6, 1e-14, + "eraBp06", "rp23", status); + vvd(rp[2][0], -0.3778929335557603418e-3, 1e-14, + "eraBp06", "rp31", status); + vvd(rp[2][1], -0.1594554040786495076e-6, 1e-14, + "eraBp06", "rp32", status); + vvd(rp[2][2], 0.9999999285984501222, 1e-12, + "eraBp06", "rp33", status); + + vvd(rbp[0][0], 0.9999995505176007047, 1e-12, + "eraBp06", "rbp11", status); + vvd(rbp[0][1], 0.8695404617348208406e-3, 1e-14, + "eraBp06", "rbp12", status); + vvd(rbp[0][2], 0.3779735201865589104e-3, 1e-14, + "eraBp06", "rbp13", status); + vvd(rbp[1][0], -0.8695404723772031414e-3, 1e-14, + "eraBp06", "rbp21", status); + vvd(rbp[1][1], 0.9999996219496027161, 1e-12, + "eraBp06", "rbp22", status); + vvd(rbp[1][2], -0.1361752497080270143e-6, 1e-14, + "eraBp06", "rbp23", status); + vvd(rbp[2][0], -0.3779734957034089490e-3, 1e-14, + "eraBp06", "rbp31", status); + vvd(rbp[2][1], -0.1924880847894457113e-6, 1e-14, + "eraBp06", "rbp32", status); + vvd(rbp[2][2], 0.9999999285679971958, 1e-12, + "eraBp06", "rbp33", status); +} + +static void t_bpn2xy(int *status) +/* +** - - - - - - - - - +** t _ b p n 2 x y +** - - - - - - - - - +** +** Test eraBpn2xy function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraBpn2xy, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbpn[3][3], x, y; + + + rbpn[0][0] = 9.999962358680738e-1; + rbpn[0][1] = -2.516417057665452e-3; + rbpn[0][2] = -1.093569785342370e-3; + + rbpn[1][0] = 2.516462370370876e-3; + rbpn[1][1] = 9.999968329010883e-1; + rbpn[1][2] = 4.006159587358310e-5; + + rbpn[2][0] = 1.093465510215479e-3; + rbpn[2][1] = -4.281337229063151e-5; + rbpn[2][2] = 9.999994012499173e-1; + + eraBpn2xy(rbpn, &x, &y); + + vvd(x, 1.093465510215479e-3, 1e-12, "eraBpn2xy", "x", status); + vvd(y, -4.281337229063151e-5, 1e-12, "eraBpn2xy", "y", status); + +} + +static void t_c2i00a(int *status) +/* +** - - - - - - - - - +** t _ c 2 i 0 0 a +** - - - - - - - - - +** +** Test eraC2i00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2i00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rc2i[3][3]; + + + eraC2i00a(2400000.5, 53736.0, rc2i); + + vvd(rc2i[0][0], 0.9999998323037165557, 1e-12, + "eraC2i00a", "11", status); + vvd(rc2i[0][1], 0.5581526348992140183e-9, 1e-12, + "eraC2i00a", "12", status); + vvd(rc2i[0][2], -0.5791308477073443415e-3, 1e-12, + "eraC2i00a", "13", status); + + vvd(rc2i[1][0], -0.2384266227870752452e-7, 1e-12, + "eraC2i00a", "21", status); + vvd(rc2i[1][1], 0.9999999991917405258, 1e-12, + "eraC2i00a", "22", status); + vvd(rc2i[1][2], -0.4020594955028209745e-4, 1e-12, + "eraC2i00a", "23", status); + + vvd(rc2i[2][0], 0.5791308472168152904e-3, 1e-12, + "eraC2i00a", "31", status); + vvd(rc2i[2][1], 0.4020595661591500259e-4, 1e-12, + "eraC2i00a", "32", status); + vvd(rc2i[2][2], 0.9999998314954572304, 1e-12, + "eraC2i00a", "33", status); + +} + +static void t_c2i00b(int *status) +/* +** - - - - - - - - - +** t _ c 2 i 0 0 b +** - - - - - - - - - +** +** Test eraC2i00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2i00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rc2i[3][3]; + + + eraC2i00b(2400000.5, 53736.0, rc2i); + + vvd(rc2i[0][0], 0.9999998323040954356, 1e-12, + "eraC2i00b", "11", status); + vvd(rc2i[0][1], 0.5581526349131823372e-9, 1e-12, + "eraC2i00b", "12", status); + vvd(rc2i[0][2], -0.5791301934855394005e-3, 1e-12, + "eraC2i00b", "13", status); + + vvd(rc2i[1][0], -0.2384239285499175543e-7, 1e-12, + "eraC2i00b", "21", status); + vvd(rc2i[1][1], 0.9999999991917574043, 1e-12, + "eraC2i00b", "22", status); + vvd(rc2i[1][2], -0.4020552974819030066e-4, 1e-12, + "eraC2i00b", "23", status); + + vvd(rc2i[2][0], 0.5791301929950208873e-3, 1e-12, + "eraC2i00b", "31", status); + vvd(rc2i[2][1], 0.4020553681373720832e-4, 1e-12, + "eraC2i00b", "32", status); + vvd(rc2i[2][2], 0.9999998314958529887, 1e-12, + "eraC2i00b", "33", status); + +} + +static void t_c2i06a(int *status) +/* +** - - - - - - - - - +** t _ c 2 i 0 6 a +** - - - - - - - - - +** +** Test eraC2i06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2i06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rc2i[3][3]; + + + eraC2i06a(2400000.5, 53736.0, rc2i); + + vvd(rc2i[0][0], 0.9999998323037159379, 1e-12, + "eraC2i06a", "11", status); + vvd(rc2i[0][1], 0.5581121329587613787e-9, 1e-12, + "eraC2i06a", "12", status); + vvd(rc2i[0][2], -0.5791308487740529749e-3, 1e-12, + "eraC2i06a", "13", status); + + vvd(rc2i[1][0], -0.2384253169452306581e-7, 1e-12, + "eraC2i06a", "21", status); + vvd(rc2i[1][1], 0.9999999991917467827, 1e-12, + "eraC2i06a", "22", status); + vvd(rc2i[1][2], -0.4020579392895682558e-4, 1e-12, + "eraC2i06a", "23", status); + + vvd(rc2i[2][0], 0.5791308482835292617e-3, 1e-12, + "eraC2i06a", "31", status); + vvd(rc2i[2][1], 0.4020580099454020310e-4, 1e-12, + "eraC2i06a", "32", status); + vvd(rc2i[2][2], 0.9999998314954628695, 1e-12, + "eraC2i06a", "33", status); + +} + +static void t_c2ibpn(int *status) +/* +** - - - - - - - - - +** t _ c 2 i b p n +** - - - - - - - - - +** +** Test eraC2ibpn function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2ibpn, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbpn[3][3], rc2i[3][3]; + + + rbpn[0][0] = 9.999962358680738e-1; + rbpn[0][1] = -2.516417057665452e-3; + rbpn[0][2] = -1.093569785342370e-3; + + rbpn[1][0] = 2.516462370370876e-3; + rbpn[1][1] = 9.999968329010883e-1; + rbpn[1][2] = 4.006159587358310e-5; + + rbpn[2][0] = 1.093465510215479e-3; + rbpn[2][1] = -4.281337229063151e-5; + rbpn[2][2] = 9.999994012499173e-1; + + eraC2ibpn(2400000.5, 50123.9999, rbpn, rc2i); + + vvd(rc2i[0][0], 0.9999994021664089977, 1e-12, + "eraC2ibpn", "11", status); + vvd(rc2i[0][1], -0.3869195948017503664e-8, 1e-12, + "eraC2ibpn", "12", status); + vvd(rc2i[0][2], -0.1093465511383285076e-2, 1e-12, + "eraC2ibpn", "13", status); + + vvd(rc2i[1][0], 0.5068413965715446111e-7, 1e-12, + "eraC2ibpn", "21", status); + vvd(rc2i[1][1], 0.9999999990835075686, 1e-12, + "eraC2ibpn", "22", status); + vvd(rc2i[1][2], 0.4281334246452708915e-4, 1e-12, + "eraC2ibpn", "23", status); + + vvd(rc2i[2][0], 0.1093465510215479000e-2, 1e-12, + "eraC2ibpn", "31", status); + vvd(rc2i[2][1], -0.4281337229063151000e-4, 1e-12, + "eraC2ibpn", "32", status); + vvd(rc2i[2][2], 0.9999994012499173103, 1e-12, + "eraC2ibpn", "33", status); + +} + +static void t_c2ixy(int *status) +/* +** - - - - - - - - +** t _ c 2 i x y +** - - - - - - - - +** +** Test eraC2ixy function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2ixy, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y, rc2i[3][3]; + + + x = 0.5791308486706011000e-3; + y = 0.4020579816732961219e-4; + + eraC2ixy(2400000.5, 53736, x, y, rc2i); + + vvd(rc2i[0][0], 0.9999998323037157138, 1e-12, + "eraC2ixy", "11", status); + vvd(rc2i[0][1], 0.5581526349032241205e-9, 1e-12, + "eraC2ixy", "12", status); + vvd(rc2i[0][2], -0.5791308491611263745e-3, 1e-12, + "eraC2ixy", "13", status); + + vvd(rc2i[1][0], -0.2384257057469842953e-7, 1e-12, + "eraC2ixy", "21", status); + vvd(rc2i[1][1], 0.9999999991917468964, 1e-12, + "eraC2ixy", "22", status); + vvd(rc2i[1][2], -0.4020579110172324363e-4, 1e-12, + "eraC2ixy", "23", status); + + vvd(rc2i[2][0], 0.5791308486706011000e-3, 1e-12, + "eraC2ixy", "31", status); + vvd(rc2i[2][1], 0.4020579816732961219e-4, 1e-12, + "eraC2ixy", "32", status); + vvd(rc2i[2][2], 0.9999998314954627590, 1e-12, + "eraC2ixy", "33", status); + +} + +static void t_c2ixys(int *status) +/* +** - - - - - - - - - +** t _ c 2 i x y s +** - - - - - - - - - +** +** Test eraC2ixys function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2ixys, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y, s, rc2i[3][3]; + + + x = 0.5791308486706011000e-3; + y = 0.4020579816732961219e-4; + s = -0.1220040848472271978e-7; + + eraC2ixys(x, y, s, rc2i); + + vvd(rc2i[0][0], 0.9999998323037157138, 1e-12, + "eraC2ixys", "11", status); + vvd(rc2i[0][1], 0.5581984869168499149e-9, 1e-12, + "eraC2ixys", "12", status); + vvd(rc2i[0][2], -0.5791308491611282180e-3, 1e-12, + "eraC2ixys", "13", status); + + vvd(rc2i[1][0], -0.2384261642670440317e-7, 1e-12, + "eraC2ixys", "21", status); + vvd(rc2i[1][1], 0.9999999991917468964, 1e-12, + "eraC2ixys", "22", status); + vvd(rc2i[1][2], -0.4020579110169668931e-4, 1e-12, + "eraC2ixys", "23", status); + + vvd(rc2i[2][0], 0.5791308486706011000e-3, 1e-12, + "eraC2ixys", "31", status); + vvd(rc2i[2][1], 0.4020579816732961219e-4, 1e-12, + "eraC2ixys", "32", status); + vvd(rc2i[2][2], 0.9999998314954627590, 1e-12, + "eraC2ixys", "33", status); + +} + +static void t_c2s(int *status) +/* +** - - - - - - +** t _ c 2 s +** - - - - - - +** +** Test eraC2s function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2s, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3], theta, phi; + + + p[0] = 100.0; + p[1] = -50.0; + p[2] = 25.0; + + eraC2s(p, &theta, &phi); + + vvd(theta, -0.4636476090008061162, 1e-14, "eraC2s", "theta", status); + vvd(phi, 0.2199879773954594463, 1e-14, "eraC2s", "phi", status); + +} + +static void t_c2t00a(int *status) +/* +** - - - - - - - - - +** t _ c 2 t 0 0 a +** - - - - - - - - - +** +** Test eraC2t00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2t00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double tta, ttb, uta, utb, xp, yp, rc2t[3][3]; + + + tta = 2400000.5; + uta = 2400000.5; + ttb = 53736.0; + utb = 53736.0; + xp = 2.55060238e-7; + yp = 1.860359247e-6; + + eraC2t00a(tta, ttb, uta, utb, xp, yp, rc2t); + + vvd(rc2t[0][0], -0.1810332128307182668, 1e-12, + "eraC2t00a", "11", status); + vvd(rc2t[0][1], 0.9834769806938457836, 1e-12, + "eraC2t00a", "12", status); + vvd(rc2t[0][2], 0.6555535638688341725e-4, 1e-12, + "eraC2t00a", "13", status); + + vvd(rc2t[1][0], -0.9834768134135984552, 1e-12, + "eraC2t00a", "21", status); + vvd(rc2t[1][1], -0.1810332203649520727, 1e-12, + "eraC2t00a", "22", status); + vvd(rc2t[1][2], 0.5749801116141056317e-3, 1e-12, + "eraC2t00a", "23", status); + + vvd(rc2t[2][0], 0.5773474014081406921e-3, 1e-12, + "eraC2t00a", "31", status); + vvd(rc2t[2][1], 0.3961832391770163647e-4, 1e-12, + "eraC2t00a", "32", status); + vvd(rc2t[2][2], 0.9999998325501692289, 1e-12, + "eraC2t00a", "33", status); + +} + +static void t_c2t00b(int *status) +/* +** - - - - - - - - - +** t _ c 2 t 0 0 b +** - - - - - - - - - +** +** Test eraC2t00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2t00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double tta, ttb, uta, utb, xp, yp, rc2t[3][3]; + + + tta = 2400000.5; + uta = 2400000.5; + ttb = 53736.0; + utb = 53736.0; + xp = 2.55060238e-7; + yp = 1.860359247e-6; + + eraC2t00b(tta, ttb, uta, utb, xp, yp, rc2t); + + vvd(rc2t[0][0], -0.1810332128439678965, 1e-12, + "eraC2t00b", "11", status); + vvd(rc2t[0][1], 0.9834769806913872359, 1e-12, + "eraC2t00b", "12", status); + vvd(rc2t[0][2], 0.6555565082458415611e-4, 1e-12, + "eraC2t00b", "13", status); + + vvd(rc2t[1][0], -0.9834768134115435923, 1e-12, + "eraC2t00b", "21", status); + vvd(rc2t[1][1], -0.1810332203784001946, 1e-12, + "eraC2t00b", "22", status); + vvd(rc2t[1][2], 0.5749793922030017230e-3, 1e-12, + "eraC2t00b", "23", status); + + vvd(rc2t[2][0], 0.5773467471863534901e-3, 1e-12, + "eraC2t00b", "31", status); + vvd(rc2t[2][1], 0.3961790411549945020e-4, 1e-12, + "eraC2t00b", "32", status); + vvd(rc2t[2][2], 0.9999998325505635738, 1e-12, + "eraC2t00b", "33", status); + +} + +static void t_c2t06a(int *status) +/* +** - - - - - - - - - +** t _ c 2 t 0 6 a +** - - - - - - - - - +** +** Test eraC2t06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2t06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double tta, ttb, uta, utb, xp, yp, rc2t[3][3]; + + + tta = 2400000.5; + uta = 2400000.5; + ttb = 53736.0; + utb = 53736.0; + xp = 2.55060238e-7; + yp = 1.860359247e-6; + + eraC2t06a(tta, ttb, uta, utb, xp, yp, rc2t); + + vvd(rc2t[0][0], -0.1810332128305897282, 1e-12, + "eraC2t06a", "11", status); + vvd(rc2t[0][1], 0.9834769806938592296, 1e-12, + "eraC2t06a", "12", status); + vvd(rc2t[0][2], 0.6555550962998436505e-4, 1e-12, + "eraC2t06a", "13", status); + + vvd(rc2t[1][0], -0.9834768134136214897, 1e-12, + "eraC2t06a", "21", status); + vvd(rc2t[1][1], -0.1810332203649130832, 1e-12, + "eraC2t06a", "22", status); + vvd(rc2t[1][2], 0.5749800844905594110e-3, 1e-12, + "eraC2t06a", "23", status); + + vvd(rc2t[2][0], 0.5773474024748545878e-3, 1e-12, + "eraC2t06a", "31", status); + vvd(rc2t[2][1], 0.3961816829632690581e-4, 1e-12, + "eraC2t06a", "32", status); + vvd(rc2t[2][2], 0.9999998325501747785, 1e-12, + "eraC2t06a", "33", status); + +} + +static void t_c2tcio(int *status) +/* +** - - - - - - - - - +** t _ c 2 t c i o +** - - - - - - - - - +** +** Test eraC2tcio function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2tcio, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rc2i[3][3], era, rpom[3][3], rc2t[3][3]; + + + rc2i[0][0] = 0.9999998323037164738; + rc2i[0][1] = 0.5581526271714303683e-9; + rc2i[0][2] = -0.5791308477073443903e-3; + + rc2i[1][0] = -0.2384266227524722273e-7; + rc2i[1][1] = 0.9999999991917404296; + rc2i[1][2] = -0.4020594955030704125e-4; + + rc2i[2][0] = 0.5791308472168153320e-3; + rc2i[2][1] = 0.4020595661593994396e-4; + rc2i[2][2] = 0.9999998314954572365; + + era = 1.75283325530307; + + rpom[0][0] = 0.9999999999999674705; + rpom[0][1] = -0.1367174580728847031e-10; + rpom[0][2] = 0.2550602379999972723e-6; + + rpom[1][0] = 0.1414624947957029721e-10; + rpom[1][1] = 0.9999999999982694954; + rpom[1][2] = -0.1860359246998866338e-5; + + rpom[2][0] = -0.2550602379741215275e-6; + rpom[2][1] = 0.1860359247002413923e-5; + rpom[2][2] = 0.9999999999982369658; + + + eraC2tcio(rc2i, era, rpom, rc2t); + + vvd(rc2t[0][0], -0.1810332128307110439, 1e-12, + "eraC2tcio", "11", status); + vvd(rc2t[0][1], 0.9834769806938470149, 1e-12, + "eraC2tcio", "12", status); + vvd(rc2t[0][2], 0.6555535638685466874e-4, 1e-12, + "eraC2tcio", "13", status); + + vvd(rc2t[1][0], -0.9834768134135996657, 1e-12, + "eraC2tcio", "21", status); + vvd(rc2t[1][1], -0.1810332203649448367, 1e-12, + "eraC2tcio", "22", status); + vvd(rc2t[1][2], 0.5749801116141106528e-3, 1e-12, + "eraC2tcio", "23", status); + + vvd(rc2t[2][0], 0.5773474014081407076e-3, 1e-12, + "eraC2tcio", "31", status); + vvd(rc2t[2][1], 0.3961832391772658944e-4, 1e-12, + "eraC2tcio", "32", status); + vvd(rc2t[2][2], 0.9999998325501691969, 1e-12, + "eraC2tcio", "33", status); + +} + +static void t_c2teqx(int *status) +/* +** - - - - - - - - - +** t _ c 2 t e q x +** - - - - - - - - - +** +** Test eraC2teqx function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2teqx, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbpn[3][3], gst, rpom[3][3], rc2t[3][3]; + + + rbpn[0][0] = 0.9999989440476103608; + rbpn[0][1] = -0.1332881761240011518e-2; + rbpn[0][2] = -0.5790767434730085097e-3; + + rbpn[1][0] = 0.1332858254308954453e-2; + rbpn[1][1] = 0.9999991109044505944; + rbpn[1][2] = -0.4097782710401555759e-4; + + rbpn[2][0] = 0.5791308472168153320e-3; + rbpn[2][1] = 0.4020595661593994396e-4; + rbpn[2][2] = 0.9999998314954572365; + + gst = 1.754166138040730516; + + rpom[0][0] = 0.9999999999999674705; + rpom[0][1] = -0.1367174580728847031e-10; + rpom[0][2] = 0.2550602379999972723e-6; + + rpom[1][0] = 0.1414624947957029721e-10; + rpom[1][1] = 0.9999999999982694954; + rpom[1][2] = -0.1860359246998866338e-5; + + rpom[2][0] = -0.2550602379741215275e-6; + rpom[2][1] = 0.1860359247002413923e-5; + rpom[2][2] = 0.9999999999982369658; + + eraC2teqx(rbpn, gst, rpom, rc2t); + + vvd(rc2t[0][0], -0.1810332128528685730, 1e-12, + "eraC2teqx", "11", status); + vvd(rc2t[0][1], 0.9834769806897685071, 1e-12, + "eraC2teqx", "12", status); + vvd(rc2t[0][2], 0.6555535639982634449e-4, 1e-12, + "eraC2teqx", "13", status); + + vvd(rc2t[1][0], -0.9834768134095211257, 1e-12, + "eraC2teqx", "21", status); + vvd(rc2t[1][1], -0.1810332203871023800, 1e-12, + "eraC2teqx", "22", status); + vvd(rc2t[1][2], 0.5749801116126438962e-3, 1e-12, + "eraC2teqx", "23", status); + + vvd(rc2t[2][0], 0.5773474014081539467e-3, 1e-12, + "eraC2teqx", "31", status); + vvd(rc2t[2][1], 0.3961832391768640871e-4, 1e-12, + "eraC2teqx", "32", status); + vvd(rc2t[2][2], 0.9999998325501691969, 1e-12, + "eraC2teqx", "33", status); + +} + +static void t_c2tpe(int *status) +/* +** - - - - - - - - +** t _ c 2 t p e +** - - - - - - - - +** +** Test eraC2tpe function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2tpe, vvd +** +** This revision: 2013 August 7 +*/ +{ + double tta, ttb, uta, utb, dpsi, deps, xp, yp, rc2t[3][3]; + + + tta = 2400000.5; + uta = 2400000.5; + ttb = 53736.0; + utb = 53736.0; + deps = 0.4090789763356509900; + dpsi = -0.9630909107115582393e-5; + xp = 2.55060238e-7; + yp = 1.860359247e-6; + + eraC2tpe(tta, ttb, uta, utb, dpsi, deps, xp, yp, rc2t); + + vvd(rc2t[0][0], -0.1813677995763029394, 1e-12, + "eraC2tpe", "11", status); + vvd(rc2t[0][1], 0.9023482206891683275, 1e-12, + "eraC2tpe", "12", status); + vvd(rc2t[0][2], -0.3909902938641085751, 1e-12, + "eraC2tpe", "13", status); + + vvd(rc2t[1][0], -0.9834147641476804807, 1e-12, + "eraC2tpe", "21", status); + vvd(rc2t[1][1], -0.1659883635434995121, 1e-12, + "eraC2tpe", "22", status); + vvd(rc2t[1][2], 0.7309763898042819705e-1, 1e-12, + "eraC2tpe", "23", status); + + vvd(rc2t[2][0], 0.1059685430673215247e-2, 1e-12, + "eraC2tpe", "31", status); + vvd(rc2t[2][1], 0.3977631855605078674, 1e-12, + "eraC2tpe", "32", status); + vvd(rc2t[2][2], 0.9174875068792735362, 1e-12, + "eraC2tpe", "33", status); + +} + +static void t_c2txy(int *status) +/* +** - - - - - - - - +** t _ c 2 t x y +** - - - - - - - - +** +** Test eraC2txy function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraC2txy, vvd +** +** This revision: 2013 August 7 +*/ +{ + double tta, ttb, uta, utb, x, y, xp, yp, rc2t[3][3]; + + + tta = 2400000.5; + uta = 2400000.5; + ttb = 53736.0; + utb = 53736.0; + x = 0.5791308486706011000e-3; + y = 0.4020579816732961219e-4; + xp = 2.55060238e-7; + yp = 1.860359247e-6; + + eraC2txy(tta, ttb, uta, utb, x, y, xp, yp, rc2t); + + vvd(rc2t[0][0], -0.1810332128306279253, 1e-12, + "eraC2txy", "11", status); + vvd(rc2t[0][1], 0.9834769806938520084, 1e-12, + "eraC2txy", "12", status); + vvd(rc2t[0][2], 0.6555551248057665829e-4, 1e-12, + "eraC2txy", "13", status); + + vvd(rc2t[1][0], -0.9834768134136142314, 1e-12, + "eraC2txy", "21", status); + vvd(rc2t[1][1], -0.1810332203649529312, 1e-12, + "eraC2txy", "22", status); + vvd(rc2t[1][2], 0.5749800843594139912e-3, 1e-12, + "eraC2txy", "23", status); + + vvd(rc2t[2][0], 0.5773474028619264494e-3, 1e-12, + "eraC2txy", "31", status); + vvd(rc2t[2][1], 0.3961816546911624260e-4, 1e-12, + "eraC2txy", "32", status); + vvd(rc2t[2][2], 0.9999998325501746670, 1e-12, + "eraC2txy", "33", status); + +} + +static void t_cal2jd(int *status) +/* +** - - - - - - - - - +** t _ c a l 2 j d +** - - - - - - - - - +** +** Test eraCal2jd function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraCal2jd, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + int j; + double djm0, djm; + + + j = eraCal2jd(2003, 06, 01, &djm0, &djm); + + vvd(djm0, 2400000.5, 0.0, "eraCal2jd", "djm0", status); + vvd(djm, 52791.0, 0.0, "eraCal2jd", "djm", status); + + viv(j, 0, "eraCal2jd", "j", status); + +} + +static void t_cp(int *status) +/* +** - - - - - +** t _ c p +** - - - - - +** +** Test eraCp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraCp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3], c[3]; + + + p[0] = 0.3; + p[1] = 1.2; + p[2] = -2.5; + + eraCp(p, c); + + vvd(c[0], 0.3, 0.0, "eraCp", "1", status); + vvd(c[1], 1.2, 0.0, "eraCp", "2", status); + vvd(c[2], -2.5, 0.0, "eraCp", "3", status); +} + +static void t_cpv(int *status) +/* +** - - - - - - +** t _ c p v +** - - - - - - +** +** Test eraCpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraCpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3], c[2][3]; + + + pv[0][0] = 0.3; + pv[0][1] = 1.2; + pv[0][2] = -2.5; + + pv[1][0] = -0.5; + pv[1][1] = 3.1; + pv[1][2] = 0.9; + + eraCpv(pv, c); + + vvd(c[0][0], 0.3, 0.0, "eraCpv", "p1", status); + vvd(c[0][1], 1.2, 0.0, "eraCpv", "p2", status); + vvd(c[0][2], -2.5, 0.0, "eraCpv", "p3", status); + + vvd(c[1][0], -0.5, 0.0, "eraCpv", "v1", status); + vvd(c[1][1], 3.1, 0.0, "eraCpv", "v2", status); + vvd(c[1][2], 0.9, 0.0, "eraCpv", "v3", status); + +} + +static void t_cr(int *status) +/* +** - - - - - +** t _ c r +** - - - - - +** +** Test eraCr function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraCr, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3], c[3][3]; + + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + eraCr(r, c); + + vvd(c[0][0], 2.0, 0.0, "eraCr", "11", status); + vvd(c[0][1], 3.0, 0.0, "eraCr", "12", status); + vvd(c[0][2], 2.0, 0.0, "eraCr", "13", status); + + vvd(c[1][0], 3.0, 0.0, "eraCr", "21", status); + vvd(c[1][1], 2.0, 0.0, "eraCr", "22", status); + vvd(c[1][2], 3.0, 0.0, "eraCr", "23", status); + + vvd(c[2][0], 3.0, 0.0, "eraCr", "31", status); + vvd(c[2][1], 4.0, 0.0, "eraCr", "32", status); + vvd(c[2][2], 5.0, 0.0, "eraCr", "33", status); +} + +static void t_d2dtf(int *status ) +/* +** - - - - - - - - +** t _ d 2 d t f +** - - - - - - - - +** +** Test eraD2dtf function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraD2dtf, viv +** +** This revision: 2013 August 7 +*/ +{ + int j, iy, im, id, ihmsf[4]; + + + j = eraD2dtf("UTC", 5, 2400000.5, 49533.99999, &iy, &im, &id, ihmsf); + + viv(iy, 1994, "eraD2dtf", "y", status); + viv(im, 6, "eraD2dtf", "mo", status); + viv(id, 30, "eraD2dtf", "d", status); + viv(ihmsf[0], 23, "eraD2dtf", "h", status); + viv(ihmsf[1], 59, "eraD2dtf", "m", status); + viv(ihmsf[2], 60, "eraD2dtf", "s", status); + viv(ihmsf[3], 13599, "eraD2dtf", "f", status); + viv(j, 0, "eraD2dtf", "j", status); + +} + +static void t_d2tf(int *status) +/* +** - - - - - - - +** t _ d 2 t f +** - - - - - - - +** +** Test eraD2tf function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraD2tf, viv, vvd +** +** This revision: 2013 August 7 +*/ +{ + int ihmsf[4]; + char s; + + + eraD2tf(4, -0.987654321, &s, ihmsf); + + viv((int)s, '-', "eraD2tf", "s", status); + + viv(ihmsf[0], 23, "eraD2tf", "0", status); + viv(ihmsf[1], 42, "eraD2tf", "1", status); + viv(ihmsf[2], 13, "eraD2tf", "2", status); + viv(ihmsf[3], 3333, "eraD2tf", "3", status); + +} + +static void t_dat(int *status) +/* +** - - - - - - +** t _ d a t +** - - - - - - +** +** Test eraDat function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraDat, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + int j; + double deltat; + + + j = eraDat(2003, 6, 1, 0.0, &deltat); + + vvd(deltat, 32.0, 0.0, "eraDat", "d1", status); + viv(j, 0, "eraDat", "j1", status); + + j = eraDat(2008, 1, 17, 0.0, &deltat); + + vvd(deltat, 33.0, 0.0, "eraDat", "d2", status); + viv(j, 0, "eraDat", "j2", status); + +} + +static void t_dtdb(int *status) +/* +** - - - - - - - +** t _ d t d b +** - - - - - - - +** +** Test eraDtdb function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraDtdb, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dtdb; + + + dtdb = eraDtdb(2448939.5, 0.123, 0.76543, 5.0123, 5525.242, 3190.0); + + vvd(dtdb, -0.1280368005936998991e-2, 1e-15, "eraDtdb", "", status); + +} + +static void t_dtf2d(int *status) +/* +** - - - - - - - - +** t _ d t f 2 d +** - - - - - - - - +** +** Test eraDtf2d function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraDtf2d, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double u1, u2; + int j; + + + j = eraDtf2d("UTC", 1994, 6, 30, 23, 59, 60.13599, &u1, &u2); + + vvd(u1+u2, 2449534.49999, 1e-6, "eraDtf2d", "u", status); + viv(j, 0, "eraDtf2d", "j", status); + +} + +static void t_ee00(int *status) +/* +** - - - - - - - +** t _ e e 0 0 +** - - - - - - - +** +** Test eraEe00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEe00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double epsa, dpsi, ee; + + + epsa = 0.4090789763356509900; + dpsi = -0.9630909107115582393e-5; + + ee = eraEe00(2400000.5, 53736.0, epsa, dpsi); + + vvd(ee, -0.8834193235367965479e-5, 1e-18, "eraEe00", "", status); + +} + +static void t_ee00a(int *status) +/* +** - - - - - - - - +** t _ e e 0 0 a +** - - - - - - - - +** +** Test eraEe00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEe00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double ee; + + + ee = eraEe00a(2400000.5, 53736.0); + + vvd(ee, -0.8834192459222588227e-5, 1e-18, "eraEe00a", "", status); + +} + +static void t_ee00b(int *status) +/* +** - - - - - - - - +** t _ e e 0 0 b +** - - - - - - - - +** +** Test eraEe00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEe00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double ee; + + + ee = eraEe00b(2400000.5, 53736.0); + + vvd(ee, -0.8835700060003032831e-5, 1e-18, "eraEe00b", "", status); + +} + +static void t_ee06a(int *status) +/* +** - - - - - - - - +** t _ e e 0 6 a +** - - - - - - - - +** +** Test eraEe06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEe06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double ee; + + + ee = eraEe06a(2400000.5, 53736.0); + + vvd(ee, -0.8834195072043790156e-5, 1e-15, "eraEe06a", "", status); +} + +static void t_eect00(int *status) +/* +** - - - - - - - - - +** t _ e e c t 0 0 +** - - - - - - - - - +** +** Test eraEect00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEect00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double eect; + + + eect = eraEect00(2400000.5, 53736.0); + + vvd(eect, 0.2046085004885125264e-8, 1e-20, "eraEect00", "", status); + +} + +static void t_eform(int *status) +/* +** - - - - - - - - +** t _ e f o r m +** - - - - - - - - +** +** Test eraEform function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEform, viv, vvd +** +** This revision: 2013 August 7 +*/ +{ + int j; + double a, f; + + j = eraEform(0, &a, &f); + + viv(j, -1, "eraEform", "j0", status); + + j = eraEform(ERFA_WGS84, &a, &f); + + viv(j, 0, "eraEform", "j1", status); + vvd(a, 6378137.0, 1e-10, "eraEform", "a1", status); + vvd(f, 0.0033528106647474807, 1e-18, "eraEform", "f1", status); + + j = eraEform(ERFA_GRS80, &a, &f); + + viv(j, 0, "eraEform", "j2", status); + vvd(a, 6378137.0, 1e-10, "eraEform", "a2", status); + vvd(f, 0.0033528106811823189, 1e-18, "eraEform", "f2", status); + + j = eraEform(ERFA_WGS72, &a, &f); + + viv(j, 0, "eraEform", "j2", status); + vvd(a, 6378135.0, 1e-10, "eraEform", "a3", status); + vvd(f, 0.0033527794541675049, 1e-18, "eraEform", "f3", status); + + j = eraEform(4, &a, &f); + viv(j, -1, "eraEform", "j3", status); +} + +static void t_eo06a(int *status) +/* +** - - - - - - - - +** t _ e o 0 6 a +** - - - - - - - - +** +** Test eraEo06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEo06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double eo; + + + eo = eraEo06a(2400000.5, 53736.0); + + vvd(eo, -0.1332882371941833644e-2, 1e-15, "eraEo06a", "", status); + +} + +static void t_eors(int *status) +/* +** - - - - - - - +** t _ e o r s +** - - - - - - - +** +** Test eraEors function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEors, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rnpb[3][3], s, eo; + + + rnpb[0][0] = 0.9999989440476103608; + rnpb[0][1] = -0.1332881761240011518e-2; + rnpb[0][2] = -0.5790767434730085097e-3; + + rnpb[1][0] = 0.1332858254308954453e-2; + rnpb[1][1] = 0.9999991109044505944; + rnpb[1][2] = -0.4097782710401555759e-4; + + rnpb[2][0] = 0.5791308472168153320e-3; + rnpb[2][1] = 0.4020595661593994396e-4; + rnpb[2][2] = 0.9999998314954572365; + + s = -0.1220040848472271978e-7; + + eo = eraEors(rnpb, s); + + vvd(eo, -0.1332882715130744606e-2, 1e-14, "eraEors", "", status); + +} + +static void t_epb(int *status) +/* +** - - - - - - +** t _ e p b +** - - - - - - +** +** Test eraEpb function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEpb, vvd +** +** This revision: 2013 August 7 +*/ +{ + double epb; + + + epb = eraEpb(2415019.8135, 30103.18648); + + vvd(epb, 1982.418424159278580, 1e-12, "eraEpb", "", status); + +} + +static void t_epb2jd(int *status) +/* +** - - - - - - - - - +** t _ e p b 2 j d +** - - - - - - - - - +** +** Test eraEpb2jd function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEpb2jd, vvd +** +** This revision: 2013 August 7 +*/ +{ + double epb, djm0, djm; + + + epb = 1957.3; + + eraEpb2jd(epb, &djm0, &djm); + + vvd(djm0, 2400000.5, 1e-9, "eraEpb2jd", "djm0", status); + vvd(djm, 35948.1915101513, 1e-9, "eraEpb2jd", "mjd", status); + +} + +static void t_epj(int *status) +/* +** - - - - - - +** t _ e p j +** - - - - - - +** +** Test eraEpj function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEpj, vvd +** +** This revision: 2013 August 7 +*/ +{ + double epj; + + + epj = eraEpj(2451545, -7392.5); + + vvd(epj, 1979.760438056125941, 1e-12, "eraEpj", "", status); + +} + +static void t_epj2jd(int *status) +/* +** - - - - - - - - - +** t _ e p j 2 j d +** - - - - - - - - - +** +** Test eraEpj2jd function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEpj2jd, vvd +** +** This revision: 2013 August 7 +*/ +{ + double epj, djm0, djm; + + + epj = 1996.8; + + eraEpj2jd(epj, &djm0, &djm); + + vvd(djm0, 2400000.5, 1e-9, "eraEpj2jd", "djm0", status); + vvd(djm, 50375.7, 1e-9, "eraEpj2jd", "mjd", status); + +} + +static void t_epv00(int *status) +/* +** - - - - - - - - +** t _ e p v 0 0 +** - - - - - - - - +** +** Test eraEpv00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEpv00, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double pvh[2][3], pvb[2][3]; + int j; + + + j = eraEpv00(2400000.5, 53411.52501161, pvh, pvb); + + vvd(pvh[0][0], -0.7757238809297706813, 1e-14, + "eraEpv00", "ph(x)", status); + vvd(pvh[0][1], 0.5598052241363340596, 1e-14, + "eraEpv00", "ph(y)", status); + vvd(pvh[0][2], 0.2426998466481686993, 1e-14, + "eraEpv00", "ph(z)", status); + + vvd(pvh[1][0], -0.1091891824147313846e-1, 1e-15, + "eraEpv00", "vh(x)", status); + vvd(pvh[1][1], -0.1247187268440845008e-1, 1e-15, + "eraEpv00", "vh(y)", status); + vvd(pvh[1][2], -0.5407569418065039061e-2, 1e-15, + "eraEpv00", "vh(z)", status); + + vvd(pvb[0][0], -0.7714104440491111971, 1e-14, + "eraEpv00", "pb(x)", status); + vvd(pvb[0][1], 0.5598412061824171323, 1e-14, + "eraEpv00", "pb(y)", status); + vvd(pvb[0][2], 0.2425996277722452400, 1e-14, + "eraEpv00", "pb(z)", status); + + vvd(pvb[1][0], -0.1091874268116823295e-1, 1e-15, + "eraEpv00", "vb(x)", status); + vvd(pvb[1][1], -0.1246525461732861538e-1, 1e-15, + "eraEpv00", "vb(y)", status); + vvd(pvb[1][2], -0.5404773180966231279e-2, 1e-15, + "eraEpv00", "vb(z)", status); + + viv(j, 0, "eraEpv00", "j", status); + +} + +static void t_eqeq94(int *status) +/* +** - - - - - - - - - +** t _ e q e q 9 4 +** - - - - - - - - - +** +** Test eraEqeq94 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEqeq94, vvd +** +** This revision: 2013 August 7 +*/ +{ + double eqeq; + + + eqeq = eraEqeq94(2400000.5, 41234.0); + + vvd(eqeq, 0.5357758254609256894e-4, 1e-17, "eraEqeq94", "", status); + +} + +static void t_era00(int *status) +/* +** - - - - - - - - +** t _ e r a 0 0 +** - - - - - - - - +** +** Test eraEra00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraEra00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double era00; + + + era00 = eraEra00(2400000.5, 54388.0); + + vvd(era00, 0.4022837240028158102, 1e-12, "eraEra00", "", status); + +} + +static void t_fad03(int *status) +/* +** - - - - - - - - +** t _ f a d 0 3 +** - - - - - - - - +** +** Test eraFad03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFad03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFad03(0.80), 1.946709205396925672, 1e-12, + "eraFad03", "", status); +} + +static void t_fae03(int *status) +/* +** - - - - - - - - +** t _ f a e 0 3 +** - - - - - - - - +** +** Test eraFae03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFae03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFae03(0.80), 1.744713738913081846, 1e-12, + "eraFae03", "", status); +} + +static void t_faf03(int *status) +/* +** - - - - - - - - +** t _ f a f 0 3 +** - - - - - - - - +** +** Test eraFaf03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFaf03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFaf03(0.80), 0.2597711366745499518, 1e-12, + "eraFaf03", "", status); +} + +static void t_faju03(int *status) +/* +** - - - - - - - - - +** t _ f a j u 0 3 +** - - - - - - - - - +** +** Test eraFaju03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFaju03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFaju03(0.80), 5.275711665202481138, 1e-12, + "eraFaju03", "", status); +} + +static void t_fal03(int *status) +/* +** - - - - - - - - +** t _ f a l 0 3 +** - - - - - - - - +** +** Test eraFal03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFal03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFal03(0.80), 5.132369751108684150, 1e-12, + "eraFal03", "", status); +} + +static void t_falp03(int *status) +/* +** - - - - - - - - - +** t _ f a l p 0 3 +** - - - - - - - - - +** +** Test eraFalp03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFalp03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFalp03(0.80), 6.226797973505507345, 1e-12, + "eraFalp03", "", status); +} + +static void t_fama03(int *status) +/* +** - - - - - - - - - +** t _ f a m a 0 3 +** - - - - - - - - - +** +** Test eraFama03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFama03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFama03(0.80), 3.275506840277781492, 1e-12, + "eraFama03", "", status); +} + +static void t_fame03(int *status) +/* +** - - - - - - - - - +** t _ f a m e 0 3 +** - - - - - - - - - +** +** Test eraFame03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFame03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFame03(0.80), 5.417338184297289661, 1e-12, + "eraFame03", "", status); +} + +static void t_fane03(int *status) +/* +** - - - - - - - - - +** t _ f a n e 0 3 +** - - - - - - - - - +** +** Test eraFane03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFane03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFane03(0.80), 2.079343830860413523, 1e-12, + "eraFane03", "", status); +} + +static void t_faom03(int *status) +/* +** - - - - - - - - - +** t _ f a o m 0 3 +** - - - - - - - - - +** +** Test eraFaom03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFaom03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFaom03(0.80), -5.973618440951302183, 1e-12, + "eraFaom03", "", status); +} + +static void t_fapa03(int *status) +/* +** - - - - - - - - - +** t _ f a p a 0 3 +** - - - - - - - - - +** +** Test eraFapa03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFapa03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFapa03(0.80), 0.1950884762240000000e-1, 1e-12, + "eraFapa03", "", status); +} + +static void t_fasa03(int *status) +/* +** - - - - - - - - - +** t _ f a s a 0 3 +** - - - - - - - - - +** +** Test eraFasa03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFasa03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFasa03(0.80), 5.371574539440827046, 1e-12, + "eraFasa03", "", status); +} + +static void t_faur03(int *status) +/* +** - - - - - - - - - +** t _ f a u r 0 3 +** - - - - - - - - - +** +** Test eraFaur03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFaur03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFaur03(0.80), 5.180636450180413523, 1e-12, + "eraFaur03", "", status); +} + +static void t_fave03(int *status) +/* +** - - - - - - - - - +** t _ f a v e 0 3 +** - - - - - - - - - +** +** Test eraFave03 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFave03, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraFave03(0.80), 3.424900460533758000, 1e-12, + "eraFave03", "", status); +} + +static void t_fk52h(int *status) +/* +** - - - - - - - - +** t _ f k 5 2 h +** - - - - - - - - +** +** Test eraFk52h function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFk52h, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r5, d5, dr5, dd5, px5, rv5, rh, dh, drh, ddh, pxh, rvh; + + + r5 = 1.76779433; + d5 = -0.2917517103; + dr5 = -1.91851572e-7; + dd5 = -5.8468475e-6; + px5 = 0.379210; + rv5 = -7.6; + + eraFk52h(r5, d5, dr5, dd5, px5, rv5, + &rh, &dh, &drh, &ddh, &pxh, &rvh); + + vvd(rh, 1.767794226299947632, 1e-14, + "eraFk52h", "ra", status); + vvd(dh, -0.2917516070530391757, 1e-14, + "eraFk52h", "dec", status); + vvd(drh, -0.19618741256057224e-6,1e-19, + "eraFk52h", "dr5", status); + vvd(ddh, -0.58459905176693911e-5, 1e-19, + "eraFk52h", "dd5", status); + vvd(pxh, 0.37921, 1e-14, + "eraFk52h", "px", status); + vvd(rvh, -7.6000000940000254, 1e-11, + "eraFk52h", "rv", status); + +} + +static void t_fk5hip(int *status) +/* +** - - - - - - - - - +** t _ f k 5 h i p +** - - - - - - - - - +** +** Test eraFk5hip function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFk5hip, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r5h[3][3], s5h[3]; + + + eraFk5hip(r5h, s5h); + + vvd(r5h[0][0], 0.9999999999999928638, 1e-14, + "eraFk5hip", "11", status); + vvd(r5h[0][1], 0.1110223351022919694e-6, 1e-17, + "eraFk5hip", "12", status); + vvd(r5h[0][2], 0.4411803962536558154e-7, 1e-17, + "eraFk5hip", "13", status); + vvd(r5h[1][0], -0.1110223308458746430e-6, 1e-17, + "eraFk5hip", "21", status); + vvd(r5h[1][1], 0.9999999999999891830, 1e-14, + "eraFk5hip", "22", status); + vvd(r5h[1][2], -0.9647792498984142358e-7, 1e-17, + "eraFk5hip", "23", status); + vvd(r5h[2][0], -0.4411805033656962252e-7, 1e-17, + "eraFk5hip", "31", status); + vvd(r5h[2][1], 0.9647792009175314354e-7, 1e-17, + "eraFk5hip", "32", status); + vvd(r5h[2][2], 0.9999999999999943728, 1e-14, + "eraFk5hip", "33", status); + vvd(s5h[0], -0.1454441043328607981e-8, 1e-17, + "eraFk5hip", "s1", status); + vvd(s5h[1], 0.2908882086657215962e-8, 1e-17, + "eraFk5hip", "s2", status); + vvd(s5h[2], 0.3393695767766751955e-8, 1e-17, + "eraFk5hip", "s3", status); + +} + +static void t_fk5hz(int *status) +/* +** - - - - - - - - +** t _ f k 5 h z +** - - - - - - - - +** +** Test eraFk5hz function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFk5hz, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r5, d5, rh, dh; + + + r5 = 1.76779433; + d5 = -0.2917517103; + + eraFk5hz(r5, d5, 2400000.5, 54479.0, &rh, &dh); + + vvd(rh, 1.767794191464423978, 1e-12, "eraFk5hz", "ra", status); + vvd(dh, -0.2917516001679884419, 1e-12, "eraFk5hz", "dec", status); + +} + +static void t_fw2m(int *status) +/* +** - - - - - - - +** t _ f w 2 m +** - - - - - - - +** +** Test eraFw2m function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFw2m, vvd +** +** This revision: 2013 August 7 +*/ +{ + double gamb, phib, psi, eps, r[3][3]; + + + gamb = -0.2243387670997992368e-5; + phib = 0.4091014602391312982; + psi = -0.9501954178013015092e-3; + eps = 0.4091014316587367472; + + eraFw2m(gamb, phib, psi, eps, r); + + vvd(r[0][0], 0.9999995505176007047, 1e-12, + "eraFw2m", "11", status); + vvd(r[0][1], 0.8695404617348192957e-3, 1e-12, + "eraFw2m", "12", status); + vvd(r[0][2], 0.3779735201865582571e-3, 1e-12, + "eraFw2m", "13", status); + + vvd(r[1][0], -0.8695404723772016038e-3, 1e-12, + "eraFw2m", "21", status); + vvd(r[1][1], 0.9999996219496027161, 1e-12, + "eraFw2m", "22", status); + vvd(r[1][2], -0.1361752496887100026e-6, 1e-12, + "eraFw2m", "23", status); + + vvd(r[2][0], -0.3779734957034082790e-3, 1e-12, + "eraFw2m", "31", status); + vvd(r[2][1], -0.1924880848087615651e-6, 1e-12, + "eraFw2m", "32", status); + vvd(r[2][2], 0.9999999285679971958, 1e-12, + "eraFw2m", "33", status); + +} + +static void t_fw2xy(int *status) +/* +** - - - - - - - - +** t _ f w 2 x y +** - - - - - - - - +** +** Test eraFw2xy function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraFw2xy, vvd +** +** This revision: 2013 August 7 +*/ +{ + double gamb, phib, psi, eps, x, y; + + + gamb = -0.2243387670997992368e-5; + phib = 0.4091014602391312982; + psi = -0.9501954178013015092e-3; + eps = 0.4091014316587367472; + + eraFw2xy(gamb, phib, psi, eps, &x, &y); + + vvd(x, -0.3779734957034082790e-3, 1e-14, "eraFw2xy", "x", status); + vvd(y, -0.1924880848087615651e-6, 1e-14, "eraFw2xy", "y", status); + +} + +static void t_gc2gd(int *status) +/* +** - - - - - - - - +** t _ g c 2 g d +** - - - - - - - - +** +** Test eraGc2gd function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGc2gd, viv, vvd +** +** This revision: 2013 August 7 +*/ +{ + int j; + double xyz[] = {2e6, 3e6, 5.244e6}; + double e, p, h; + + j = eraGc2gd(0, xyz, &e, &p, &h); + + viv(j, -1, "eraGc2gd", "j0", status); + + j = eraGc2gd(ERFA_WGS84, xyz, &e, &p, &h); + + viv(j, 0, "eraGc2gd", "j1", status); + vvd(e, 0.98279372324732907, 1e-14, "eraGc2gd", "e1", status); + vvd(p, 0.97160184819075459, 1e-14, "eraGc2gd", "p1", status); + vvd(h, 331.41724614260599, 1e-8, "eraGc2gd", "h1", status); + + j = eraGc2gd(ERFA_GRS80, xyz, &e, &p, &h); + + viv(j, 0, "eraGc2gd", "j2", status); + vvd(e, 0.98279372324732907, 1e-14, "eraGc2gd", "e2", status); + vvd(p, 0.97160184820607853, 1e-14, "eraGc2gd", "p2", status); + vvd(h, 331.41731754844348, 1e-8, "eraGc2gd", "h2", status); + + j = eraGc2gd(ERFA_WGS72, xyz, &e, &p, &h); + + viv(j, 0, "eraGc2gd", "j3", status); + vvd(e, 0.98279372324732907, 1e-14, "eraGc2gd", "e3", status); + vvd(p, 0.97160181811015119, 1e-14, "eraGc2gd", "p3", status); + vvd(h, 333.27707261303181, 1e-8, "eraGc2gd", "h3", status); + + j = eraGc2gd(4, xyz, &e, &p, &h); + + viv(j, -1, "eraGc2gd", "j4", status); +} + +static void t_gc2gde(int *status) +/* +** - - - - - - - - - +** t _ g c 2 g d e +** - - - - - - - - - +** +** Test eraGc2gde function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGc2gde, viv, vvd +** +** This revision: 2013 August 7 +*/ +{ + int j; + double a = 6378136.0, f = 0.0033528; + double xyz[] = {2e6, 3e6, 5.244e6}; + double e, p, h; + + j = eraGc2gde(a, f, xyz, &e, &p, &h); + + viv(j, 0, "eraGc2gde", "j", status); + vvd(e, 0.98279372324732907, 1e-14, "eraGc2gde", "e", status); + vvd(p, 0.97160183775704115, 1e-14, "eraGc2gde", "p", status); + vvd(h, 332.36862495764397, 1e-8, "eraGc2gde", "h", status); +} + +static void t_gd2gc(int *status) +/* +** - - - - - - - - +** t _ g d 2 g c +** - - - - - - - - +** +** Test eraGd2gc function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGd2gc, viv, vvd +** +** This revision: 2013 August 7 +*/ +{ + int j; + double e = 3.1, p = -0.5, h = 2500.0; + double xyz[3]; + + j = eraGd2gc(0, e, p, h, xyz); + + viv(j, -1, "eraGd2gc", "j0", status); + + j = eraGd2gc(ERFA_WGS84, e, p, h, xyz); + + viv(j, 0, "eraGd2gc", "j1", status); + vvd(xyz[0], -5599000.5577049947, 1e-7, "eraGd2gc", "0/1", status); + vvd(xyz[1], 233011.67223479203, 1e-7, "eraGd2gc", "1/1", status); + vvd(xyz[2], -3040909.4706983363, 1e-7, "eraGd2gc", "2/1", status); + + j = eraGd2gc(ERFA_GRS80, e, p, h, xyz); + + viv(j, 0, "eraGd2gc", "j2", status); + vvd(xyz[0], -5599000.5577260984, 1e-7, "eraGd2gc", "0/2", status); + vvd(xyz[1], 233011.6722356703, 1e-7, "eraGd2gc", "1/2", status); + vvd(xyz[2], -3040909.4706095476, 1e-7, "eraGd2gc", "2/2", status); + + j = eraGd2gc(ERFA_WGS72, e, p, h, xyz); + + viv(j, 0, "eraGd2gc", "j3", status); + vvd(xyz[0], -5598998.7626301490, 1e-7, "eraGd2gc", "0/3", status); + vvd(xyz[1], 233011.5975297822, 1e-7, "eraGd2gc", "1/3", status); + vvd(xyz[2], -3040908.6861467111, 1e-7, "eraGd2gc", "2/3", status); + + j = eraGd2gc(4, e, p, h, xyz); + + viv(j, -1, "eraGd2gc", "j4", status); +} + +static void t_gd2gce(int *status) +/* +** - - - - - - - - - +** t _ g d 2 g c e +** - - - - - - - - - +** +** Test eraGd2gce function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGd2gce, viv, vvd +** +** This revision: 2013 August 7 +*/ +{ + int j; + double a = 6378136.0, f = 0.0033528; + double e = 3.1, p = -0.5, h = 2500.0; + double xyz[3]; + + j = eraGd2gce(a, f, e, p, h, xyz); + + viv(j, 0, "eraGd2gce", "j", status); + vvd(xyz[0], -5598999.6665116328, 1e-7, "eraGd2gce", "0", status); + vvd(xyz[1], 233011.63514630572, 1e-7, "eraGd2gce", "1", status); + vvd(xyz[2], -3040909.0517314132, 1e-7, "eraGd2gce", "2", status); +} + +static void t_gmst00(int *status) +/* +** - - - - - - - - - +** t _ g m s t 0 0 +** - - - - - - - - - +** +** Test eraGmst00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGmst00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta; + + + theta = eraGmst00(2400000.5, 53736.0, 2400000.5, 53736.0); + + vvd(theta, 1.754174972210740592, 1e-12, "eraGmst00", "", status); + +} + +static void t_gmst06(int *status) +/* +** - - - - - - - - - +** t _ g m s t 0 6 +** - - - - - - - - - +** +** Test eraGmst06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGmst06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta; + + + theta = eraGmst06(2400000.5, 53736.0, 2400000.5, 53736.0); + + vvd(theta, 1.754174971870091203, 1e-12, "eraGmst06", "", status); + +} + +static void t_gmst82(int *status) +/* +** - - - - - - - - - +** t _ g m s t 8 2 +** - - - - - - - - - +** +** Test eraGmst82 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGmst82, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta; + + + theta = eraGmst82(2400000.5, 53736.0); + + vvd(theta, 1.754174981860675096, 1e-12, "eraGmst82", "", status); + +} + +static void t_gst00a(int *status) +/* +** - - - - - - - - - +** t _ g s t 0 0 a +** - - - - - - - - - +** +** Test eraGst00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGst00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta; + + + theta = eraGst00a(2400000.5, 53736.0, 2400000.5, 53736.0); + + vvd(theta, 1.754166138018281369, 1e-12, "eraGst00a", "", status); + +} + +static void t_gst00b(int *status) +/* +** - - - - - - - - - +** t _ g s t 0 0 b +** - - - - - - - - - +** +** Test eraGst00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGst00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta; + + + theta = eraGst00b(2400000.5, 53736.0); + + vvd(theta, 1.754166136510680589, 1e-12, "eraGst00b", "", status); + +} + +static void t_gst06(int *status) +/* +** - - - - - - - - +** t _ g s t 0 6 +** - - - - - - - - +** +** Test eraGst06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGst06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rnpb[3][3], theta; + + + rnpb[0][0] = 0.9999989440476103608; + rnpb[0][1] = -0.1332881761240011518e-2; + rnpb[0][2] = -0.5790767434730085097e-3; + + rnpb[1][0] = 0.1332858254308954453e-2; + rnpb[1][1] = 0.9999991109044505944; + rnpb[1][2] = -0.4097782710401555759e-4; + + rnpb[2][0] = 0.5791308472168153320e-3; + rnpb[2][1] = 0.4020595661593994396e-4; + rnpb[2][2] = 0.9999998314954572365; + + theta = eraGst06(2400000.5, 53736.0, 2400000.5, 53736.0, rnpb); + + vvd(theta, 1.754166138018167568, 1e-12, "eraGst06", "", status); + +} + +static void t_gst06a(int *status) +/* +** - - - - - - - - - +** t _ g s t 0 6 a +** - - - - - - - - - +** +** Test eraGst06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGst06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta; + + + theta = eraGst06a(2400000.5, 53736.0, 2400000.5, 53736.0); + + vvd(theta, 1.754166137675019159, 1e-12, "eraGst06a", "", status); + +} + +static void t_gst94(int *status) +/* +** - - - - - - - - +** t _ g s t 9 4 +** - - - - - - - - +** +** Test eraGst94 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraGst94, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta; + + + theta = eraGst94(2400000.5, 53736.0); + + vvd(theta, 1.754166136020645203, 1e-12, "eraGst94", "", status); + +} + +static void t_h2fk5(int *status) +/* +** - - - - - - - - +** t _ h 2 f k 5 +** - - - - - - - - +** +** Test eraH2fk5 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraH2fk5, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rh, dh, drh, ddh, pxh, rvh, r5, d5, dr5, dd5, px5, rv5; + + + rh = 1.767794352; + dh = -0.2917512594; + drh = -2.76413026e-6; + ddh = -5.92994449e-6; + pxh = 0.379210; + rvh = -7.6; + + eraH2fk5(rh, dh, drh, ddh, pxh, rvh, + &r5, &d5, &dr5, &dd5, &px5, &rv5); + + vvd(r5, 1.767794455700065506, 1e-13, + "eraH2fk5", "ra", status); + vvd(d5, -0.2917513626469638890, 1e-13, + "eraH2fk5", "dec", status); + vvd(dr5, -0.27597945024511204e-5, 1e-18, + "eraH2fk5", "dr5", status); + vvd(dd5, -0.59308014093262838e-5, 1e-18, + "eraH2fk5", "dd5", status); + vvd(px5, 0.37921, 1e-13, + "eraH2fk5", "px", status); + vvd(rv5, -7.6000001309071126, 1e-10, + "eraH2fk5", "rv", status); + +} + +static void t_hfk5z(int *status) +/* +** - - - - - - - - +** t _ h f k 5 z +** - - - - - - - - +** +** Test eraHfk5z function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraHfk5z, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rh, dh, r5, d5, dr5, dd5; + + + + rh = 1.767794352; + dh = -0.2917512594; + + eraHfk5z(rh, dh, 2400000.5, 54479.0, &r5, &d5, &dr5, &dd5); + + vvd(r5, 1.767794490535581026, 1e-13, + "eraHfk5z", "ra", status); + vvd(d5, -0.2917513695320114258, 1e-14, + "eraHfk5z", "dec", status); + vvd(dr5, 0.4335890983539243029e-8, 1e-22, + "eraHfk5z", "dr5", status); + vvd(dd5, -0.8569648841237745902e-9, 1e-23, + "eraHfk5z", "dd5", status); + +} + +static void t_ir(int *status) +/* +** - - - - - +** t _ i r +** - - - - - +** +** Test eraIr function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraIr, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3]; + + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + eraIr(r); + + vvd(r[0][0], 1.0, 0.0, "eraIr", "11", status); + vvd(r[0][1], 0.0, 0.0, "eraIr", "12", status); + vvd(r[0][2], 0.0, 0.0, "eraIr", "13", status); + + vvd(r[1][0], 0.0, 0.0, "eraIr", "21", status); + vvd(r[1][1], 1.0, 0.0, "eraIr", "22", status); + vvd(r[1][2], 0.0, 0.0, "eraIr", "23", status); + + vvd(r[2][0], 0.0, 0.0, "eraIr", "31", status); + vvd(r[2][1], 0.0, 0.0, "eraIr", "32", status); + vvd(r[2][2], 1.0, 0.0, "eraIr", "33", status); + +} + +static void t_jd2cal(int *status) +/* +** - - - - - - - - - +** t _ j d 2 c a l +** - - - - - - - - - +** +** Test eraJd2cal function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraJd2cal, viv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dj1, dj2, fd; + int iy, im, id, j; + + + dj1 = 2400000.5; + dj2 = 50123.9999; + + j = eraJd2cal(dj1, dj2, &iy, &im, &id, &fd); + + viv(iy, 1996, "eraJd2cal", "y", status); + viv(im, 2, "eraJd2cal", "m", status); + viv(id, 10, "eraJd2cal", "d", status); + vvd(fd, 0.9999, 1e-7, "eraJd2cal", "fd", status); + viv(j, 0, "eraJd2cal", "j", status); + +} + +static void t_jdcalf(int *status) +/* +** - - - - - - - - - +** t _ j d c a l f +** - - - - - - - - - +** +** Test eraJdcalf function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraJdcalf, viv +** +** This revision: 2013 August 7 +*/ +{ + double dj1, dj2; + int iydmf[4], j; + + + dj1 = 2400000.5; + dj2 = 50123.9999; + + j = eraJdcalf(4, dj1, dj2, iydmf); + + viv(iydmf[0], 1996, "eraJdcalf", "y", status); + viv(iydmf[1], 2, "eraJdcalf", "m", status); + viv(iydmf[2], 10, "eraJdcalf", "d", status); + viv(iydmf[3], 9999, "eraJdcalf", "f", status); + + viv(j, 0, "eraJdcalf", "j", status); + +} + +static void t_ld(int *status) +/* +** - - - - - +** t _ l d +** - - - - - +** +** Test eraLd function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLd, vvd +* +** This revision: 2013 October 2 +*/ +{ + double bm, p[3], q[3], e[3], em, dlim, p1[3]; + + + bm = 0.00028574; + p[0] = -0.763276255; + p[1] = -0.608633767; + p[2] = -0.216735543; + q[0] = -0.763276255; + q[1] = -0.608633767; + q[2] = -0.216735543; + e[0] = 0.76700421; + e[1] = 0.605629598; + e[2] = 0.211937094; + em = 8.91276983; + dlim = 3e-10; + + eraLd(bm, p, q, e, em, dlim, p1); + + vvd(p1[0], -0.7632762548968159627, 1e-12, + "eraLd", "1", status); + vvd(p1[1], -0.6086337670823762701, 1e-12, + "eraLd", "2", status); + vvd(p1[2], -0.2167355431320546947, 1e-12, + "eraLd", "3", status); + +} + +static void t_ldn(int *status) +/* +** - - - - - - +** t _ l d n +** - - - - - - +** +** Test eraLdn function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLdn, vvd +** +** This revision: 2013 October 2 +*/ +{ + int n; + eraLDBODY b[3]; + double ob[3], sc[3], sn[3]; + + + n = 3; + b[0].bm = 0.00028574; + b[0].dl = 3e-10; + b[0].pv[0][0] = -7.81014427; + b[0].pv[0][1] = -5.60956681; + b[0].pv[0][2] = -1.98079819; + b[0].pv[1][0] = 0.0030723249; + b[0].pv[1][1] = -0.00406995477; + b[0].pv[1][2] = -0.00181335842; + b[1].bm = 0.00095435; + b[1].dl = 3e-9; + b[1].pv[0][0] = 0.738098796; + b[1].pv[0][1] = 4.63658692; + b[1].pv[0][2] = 1.9693136; + b[1].pv[1][0] = -0.00755816922; + b[1].pv[1][1] = 0.00126913722; + b[1].pv[1][2] = 0.000727999001; + b[2].bm = 1.0; + b[2].dl = 6e-6; + b[2].pv[0][0] = -0.000712174377; + b[2].pv[0][1] = -0.00230478303; + b[2].pv[0][2] = -0.00105865966; + b[2].pv[1][0] = 6.29235213e-6; + b[2].pv[1][1] = -3.30888387e-7; + b[2].pv[1][2] = -2.96486623e-7; + ob[0] = -0.974170437; + ob[1] = -0.2115201; + ob[2] = -0.0917583114; + sc[0] = -0.763276255; + sc[1] = -0.608633767; + sc[2] = -0.216735543; + + eraLdn(n, b, ob, sc, sn); + + vvd(sn[0], -0.7632762579693333866, 1e-12, + "eraLdn", "1", status); + vvd(sn[1], -0.6086337636093002660, 1e-12, + "eraLdn", "2", status); + vvd(sn[2], -0.2167355420646328159, 1e-12, + "eraLdn", "3", status); + +} + +static void t_ldsun(int *status) +/* +** - - - - - - - - +** t _ l d s u n +** - - - - - - - - +** +** Test eraLdsun function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraLdsun, vvd +** +** This revision: 2013 October 2 +*/ +{ + double p[3], e[3], em, p1[3]; + + + p[0] = -0.763276255; + p[1] = -0.608633767; + p[2] = -0.216735543; + e[0] = -0.973644023; + e[1] = -0.20925523; + e[2] = -0.0907169552; + em = 0.999809214; + + eraLdsun(p, e, em, p1); + + vvd(p1[0], -0.7632762580731413169, 1e-12, + "eraLdsun", "1", status); + vvd(p1[1], -0.6086337635262647900, 1e-12, + "eraLdsun", "2", status); + vvd(p1[2], -0.2167355419322321302, 1e-12, + "eraLdsun", "3", status); + +} + +static void t_num00a(int *status) +/* +** - - - - - - - - - +** t _ n u m 0 0 a +** - - - - - - - - - +** +** Test eraNum00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNum00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rmatn[3][3]; + + + eraNum00a(2400000.5, 53736.0, rmatn); + + vvd(rmatn[0][0], 0.9999999999536227949, 1e-12, + "eraNum00a", "11", status); + vvd(rmatn[0][1], 0.8836238544090873336e-5, 1e-12, + "eraNum00a", "12", status); + vvd(rmatn[0][2], 0.3830835237722400669e-5, 1e-12, + "eraNum00a", "13", status); + + vvd(rmatn[1][0], -0.8836082880798569274e-5, 1e-12, + "eraNum00a", "21", status); + vvd(rmatn[1][1], 0.9999999991354655028, 1e-12, + "eraNum00a", "22", status); + vvd(rmatn[1][2], -0.4063240865362499850e-4, 1e-12, + "eraNum00a", "23", status); + + vvd(rmatn[2][0], -0.3831194272065995866e-5, 1e-12, + "eraNum00a", "31", status); + vvd(rmatn[2][1], 0.4063237480216291775e-4, 1e-12, + "eraNum00a", "32", status); + vvd(rmatn[2][2], 0.9999999991671660338, 1e-12, + "eraNum00a", "33", status); + +} + +static void t_num00b(int *status) +/* +** - - - - - - - - - +** t _ n u m 0 0 b +** - - - - - - - - - +** +** Test eraNum00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNum00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rmatn[3][3]; + + eraNum00b(2400000.5, 53736, rmatn); + + vvd(rmatn[0][0], 0.9999999999536069682, 1e-12, + "eraNum00b", "11", status); + vvd(rmatn[0][1], 0.8837746144871248011e-5, 1e-12, + "eraNum00b", "12", status); + vvd(rmatn[0][2], 0.3831488838252202945e-5, 1e-12, + "eraNum00b", "13", status); + + vvd(rmatn[1][0], -0.8837590456632304720e-5, 1e-12, + "eraNum00b", "21", status); + vvd(rmatn[1][1], 0.9999999991354692733, 1e-12, + "eraNum00b", "22", status); + vvd(rmatn[1][2], -0.4063198798559591654e-4, 1e-12, + "eraNum00b", "23", status); + + vvd(rmatn[2][0], -0.3831847930134941271e-5, 1e-12, + "eraNum00b", "31", status); + vvd(rmatn[2][1], 0.4063195412258168380e-4, 1e-12, + "eraNum00b", "32", status); + vvd(rmatn[2][2], 0.9999999991671806225, 1e-12, + "eraNum00b", "33", status); + +} + +static void t_num06a(int *status) +/* +** - - - - - - - - - +** t _ n u m 0 6 a +** - - - - - - - - - +** +** Test eraNum06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNum06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rmatn[3][3]; + + eraNum06a(2400000.5, 53736, rmatn); + + vvd(rmatn[0][0], 0.9999999999536227668, 1e-12, + "eraNum06a", "11", status); + vvd(rmatn[0][1], 0.8836241998111535233e-5, 1e-12, + "eraNum06a", "12", status); + vvd(rmatn[0][2], 0.3830834608415287707e-5, 1e-12, + "eraNum06a", "13", status); + + vvd(rmatn[1][0], -0.8836086334870740138e-5, 1e-12, + "eraNum06a", "21", status); + vvd(rmatn[1][1], 0.9999999991354657474, 1e-12, + "eraNum06a", "22", status); + vvd(rmatn[1][2], -0.4063240188248455065e-4, 1e-12, + "eraNum06a", "23", status); + + vvd(rmatn[2][0], -0.3831193642839398128e-5, 1e-12, + "eraNum06a", "31", status); + vvd(rmatn[2][1], 0.4063236803101479770e-4, 1e-12, + "eraNum06a", "32", status); + vvd(rmatn[2][2], 0.9999999991671663114, 1e-12, + "eraNum06a", "33", status); + +} + +static void t_numat(int *status) +/* +** - - - - - - - - +** t _ n u m a t +** - - - - - - - - +** +** Test eraNumat function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNumat, vvd +** +** This revision: 2013 August 7 +*/ +{ + double epsa, dpsi, deps, rmatn[3][3]; + + + epsa = 0.4090789763356509900; + dpsi = -0.9630909107115582393e-5; + deps = 0.4063239174001678826e-4; + + eraNumat(epsa, dpsi, deps, rmatn); + + vvd(rmatn[0][0], 0.9999999999536227949, 1e-12, + "eraNumat", "11", status); + vvd(rmatn[0][1], 0.8836239320236250577e-5, 1e-12, + "eraNumat", "12", status); + vvd(rmatn[0][2], 0.3830833447458251908e-5, 1e-12, + "eraNumat", "13", status); + + vvd(rmatn[1][0], -0.8836083657016688588e-5, 1e-12, + "eraNumat", "21", status); + vvd(rmatn[1][1], 0.9999999991354654959, 1e-12, + "eraNumat", "22", status); + vvd(rmatn[1][2], -0.4063240865361857698e-4, 1e-12, + "eraNumat", "23", status); + + vvd(rmatn[2][0], -0.3831192481833385226e-5, 1e-12, + "eraNumat", "31", status); + vvd(rmatn[2][1], 0.4063237480216934159e-4, 1e-12, + "eraNumat", "32", status); + vvd(rmatn[2][2], 0.9999999991671660407, 1e-12, + "eraNumat", "33", status); + +} + +static void t_nut00a(int *status) +/* +** - - - - - - - - - +** t _ n u t 0 0 a +** - - - - - - - - - +** +** Test eraNut00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNut00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps; + + + eraNut00a(2400000.5, 53736.0, &dpsi, &deps); + + vvd(dpsi, -0.9630909107115518431e-5, 1e-13, + "eraNut00a", "dpsi", status); + vvd(deps, 0.4063239174001678710e-4, 1e-13, + "eraNut00a", "deps", status); + +} + +static void t_nut00b(int *status) +/* +** - - - - - - - - - +** t _ n u t 0 0 b +** - - - - - - - - - +** +** Test eraNut00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNut00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps; + + + eraNut00b(2400000.5, 53736.0, &dpsi, &deps); + + vvd(dpsi, -0.9632552291148362783e-5, 1e-13, + "eraNut00b", "dpsi", status); + vvd(deps, 0.4063197106621159367e-4, 1e-13, + "eraNut00b", "deps", status); + +} + +static void t_nut06a(int *status) +/* +** - - - - - - - - - +** t _ n u t 0 6 a +** - - - - - - - - - +** +** Test eraNut06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNut06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps; + + + eraNut06a(2400000.5, 53736.0, &dpsi, &deps); + + vvd(dpsi, -0.9630912025820308797e-5, 1e-13, + "eraNut06a", "dpsi", status); + vvd(deps, 0.4063238496887249798e-4, 1e-13, + "eraNut06a", "deps", status); + +} + +static void t_nut80(int *status) +/* +** - - - - - - - - +** t _ n u t 8 0 +** - - - - - - - - +** +** Test eraNut80 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNut80, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps; + + + eraNut80(2400000.5, 53736.0, &dpsi, &deps); + + vvd(dpsi, -0.9643658353226563966e-5, 1e-13, + "eraNut80", "dpsi", status); + vvd(deps, 0.4060051006879713322e-4, 1e-13, + "eraNut80", "deps", status); + +} + +static void t_nutm80(int *status) +/* +** - - - - - - - - - +** t _ n u t m 8 0 +** - - - - - - - - - +** +** Test eraNutm80 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraNutm80, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rmatn[3][3]; + + + eraNutm80(2400000.5, 53736.0, rmatn); + + vvd(rmatn[0][0], 0.9999999999534999268, 1e-12, + "eraNutm80", "11", status); + vvd(rmatn[0][1], 0.8847935789636432161e-5, 1e-12, + "eraNutm80", "12", status); + vvd(rmatn[0][2], 0.3835906502164019142e-5, 1e-12, + "eraNutm80", "13", status); + + vvd(rmatn[1][0], -0.8847780042583435924e-5, 1e-12, + "eraNutm80", "21", status); + vvd(rmatn[1][1], 0.9999999991366569963, 1e-12, + "eraNutm80", "22", status); + vvd(rmatn[1][2], -0.4060052702727130809e-4, 1e-12, + "eraNutm80", "23", status); + + vvd(rmatn[2][0], -0.3836265729708478796e-5, 1e-12, + "eraNutm80", "31", status); + vvd(rmatn[2][1], 0.4060049308612638555e-4, 1e-12, + "eraNutm80", "32", status); + vvd(rmatn[2][2], 0.9999999991684415129, 1e-12, + "eraNutm80", "33", status); + +} + +static void t_obl06(int *status) +/* +** - - - - - - - - +** t _ o b l 0 6 +** - - - - - - - - +** +** Test eraObl06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraObl06, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraObl06(2400000.5, 54388.0), 0.4090749229387258204, 1e-14, + "eraObl06", "", status); +} + +static void t_obl80(int *status) +/* +** - - - - - - - - +** t _ o b l 8 0 +** - - - - - - - - +** +** Test eraObl80 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraObl80, vvd +** +** This revision: 2013 August 7 +*/ +{ + double eps0; + + + eps0 = eraObl80(2400000.5, 54388.0); + + vvd(eps0, 0.4090751347643816218, 1e-14, "eraObl80", "", status); + +} + +static void t_p06e(int *status) +/* +** - - - - - - - +** t _ p 0 6 e +** - - - - - - - +** +** Test eraP06e function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraP06e, vvd +** +** This revision: 2013 August 7 +*/ +{ + double eps0, psia, oma, bpa, bqa, pia, bpia, + epsa, chia, za, zetaa, thetaa, pa, gam, phi, psi; + + + eraP06e(2400000.5, 52541.0, &eps0, &psia, &oma, &bpa, + &bqa, &pia, &bpia, &epsa, &chia, &za, + &zetaa, &thetaa, &pa, &gam, &phi, &psi); + + vvd(eps0, 0.4090926006005828715, 1e-14, + "eraP06e", "eps0", status); + vvd(psia, 0.6664369630191613431e-3, 1e-14, + "eraP06e", "psia", status); + vvd(oma , 0.4090925973783255982, 1e-14, + "eraP06e", "oma", status); + vvd(bpa, 0.5561149371265209445e-6, 1e-14, + "eraP06e", "bpa", status); + vvd(bqa, -0.6191517193290621270e-5, 1e-14, + "eraP06e", "bqa", status); + vvd(pia, 0.6216441751884382923e-5, 1e-14, + "eraP06e", "pia", status); + vvd(bpia, 3.052014180023779882, 1e-14, + "eraP06e", "bpia", status); + vvd(epsa, 0.4090864054922431688, 1e-14, + "eraP06e", "epsa", status); + vvd(chia, 0.1387703379530915364e-5, 1e-14, + "eraP06e", "chia", status); + vvd(za, 0.2921789846651790546e-3, 1e-14, + "eraP06e", "za", status); + vvd(zetaa, 0.3178773290332009310e-3, 1e-14, + "eraP06e", "zetaa", status); + vvd(thetaa, 0.2650932701657497181e-3, 1e-14, + "eraP06e", "thetaa", status); + vvd(pa, 0.6651637681381016344e-3, 1e-14, + "eraP06e", "pa", status); + vvd(gam, 0.1398077115963754987e-5, 1e-14, + "eraP06e", "gam", status); + vvd(phi, 0.4090864090837462602, 1e-14, + "eraP06e", "phi", status); + vvd(psi, 0.6664464807480920325e-3, 1e-14, + "eraP06e", "psi", status); + +} + +static void t_p2pv(int *status) +/* +** - - - - - - - +** t _ p 2 p v +** - - - - - - - +** +** Test eraP2pv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraP2pv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3], pv[2][3]; + + + p[0] = 0.25; + p[1] = 1.2; + p[2] = 3.0; + + pv[0][0] = 0.3; + pv[0][1] = 1.2; + pv[0][2] = -2.5; + + pv[1][0] = -0.5; + pv[1][1] = 3.1; + pv[1][2] = 0.9; + + eraP2pv(p, pv); + + vvd(pv[0][0], 0.25, 0.0, "eraP2pv", "p1", status); + vvd(pv[0][1], 1.2, 0.0, "eraP2pv", "p2", status); + vvd(pv[0][2], 3.0, 0.0, "eraP2pv", "p3", status); + + vvd(pv[1][0], 0.0, 0.0, "eraP2pv", "v1", status); + vvd(pv[1][1], 0.0, 0.0, "eraP2pv", "v2", status); + vvd(pv[1][2], 0.0, 0.0, "eraP2pv", "v3", status); + +} + +static void t_p2s(int *status) +/* +** - - - - - - +** t _ p 2 s +** - - - - - - +** +** Test eraP2s function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraP2s, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3], theta, phi, r; + + + p[0] = 100.0; + p[1] = -50.0; + p[2] = 25.0; + + eraP2s(p, &theta, &phi, &r); + + vvd(theta, -0.4636476090008061162, 1e-12, "eraP2s", "theta", status); + vvd(phi, 0.2199879773954594463, 1e-12, "eraP2s", "phi", status); + vvd(r, 114.5643923738960002, 1e-9, "eraP2s", "r", status); + +} + +static void t_pap(int *status) +/* +** - - - - - - +** t _ p a p +** - - - - - - +** +** Test eraPap function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPap, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3], b[3], theta; + + + a[0] = 1.0; + a[1] = 0.1; + a[2] = 0.2; + + b[0] = -3.0; + b[1] = 1e-3; + b[2] = 0.2; + + theta = eraPap(a, b); + + vvd(theta, 0.3671514267841113674, 1e-12, "eraPap", "", status); + +} + +static void t_pas(int *status) +/* +** - - - - - - +** t _ p a s +** - - - - - - +** +** Test eraPas function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPas, vvd +** +** This revision: 2013 August 7 +*/ +{ + double al, ap, bl, bp, theta; + + + al = 1.0; + ap = 0.1; + bl = 0.2; + bp = -1.0; + + theta = eraPas(al, ap, bl, bp); + + vvd(theta, -2.724544922932270424, 1e-12, "eraPas", "", status); + +} + +static void t_pb06(int *status) +/* +** - - - - - - - +** t _ p b 0 6 +** - - - - - - - +** +** Test eraPb06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPb06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double bzeta, bz, btheta; + + + eraPb06(2400000.5, 50123.9999, &bzeta, &bz, &btheta); + + vvd(bzeta, -0.5092634016326478238e-3, 1e-12, + "eraPb06", "bzeta", status); + vvd(bz, -0.3602772060566044413e-3, 1e-12, + "eraPb06", "bz", status); + vvd(btheta, -0.3779735537167811177e-3, 1e-12, + "eraPb06", "btheta", status); + +} + +static void t_pdp(int *status) +/* +** - - - - - - +** t _ p d p +** - - - - - - +** +** Test eraPdp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPdp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3], b[3], adb; + + + a[0] = 2.0; + a[1] = 2.0; + a[2] = 3.0; + + b[0] = 1.0; + b[1] = 3.0; + b[2] = 4.0; + + adb = eraPdp(a, b); + + vvd(adb, 20, 1e-12, "eraPdp", "", status); + +} + +static void t_pfw06(int *status) +/* +** - - - - - - - - +** t _ p f w 0 6 +** - - - - - - - - +** +** Test eraPfw06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPfw06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double gamb, phib, psib, epsa; + + + eraPfw06(2400000.5, 50123.9999, &gamb, &phib, &psib, &epsa); + + vvd(gamb, -0.2243387670997995690e-5, 1e-16, + "eraPfw06", "gamb", status); + vvd(phib, 0.4091014602391312808, 1e-12, + "eraPfw06", "phib", status); + vvd(psib, -0.9501954178013031895e-3, 1e-14, + "eraPfw06", "psib", status); + vvd(epsa, 0.4091014316587367491, 1e-12, + "eraPfw06", "epsa", status); + +} + +static void t_plan94(int *status) +/* +** - - - - - - - - - +** t _ p l a n 9 4 +** - - - - - - - - - +** +** Test eraPlan94 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPlan94, vvd, viv +** +** This revision: 2013 October 2 +*/ +{ + double pv[2][3]; + int j; + + + j = eraPlan94(2400000.5, 1e6, 0, pv); + + vvd(pv[0][0], 0.0, 0.0, "eraPlan94", "x 1", status); + vvd(pv[0][1], 0.0, 0.0, "eraPlan94", "y 1", status); + vvd(pv[0][2], 0.0, 0.0, "eraPlan94", "z 1", status); + + vvd(pv[1][0], 0.0, 0.0, "eraPlan94", "xd 1", status); + vvd(pv[1][1], 0.0, 0.0, "eraPlan94", "yd 1", status); + vvd(pv[1][2], 0.0, 0.0, "eraPlan94", "zd 1", status); + + viv(j, -1, "eraPlan94", "j 1", status); + + j = eraPlan94(2400000.5, 1e6, 10, pv); + + viv(j, -1, "eraPlan94", "j 2", status); + + j = eraPlan94(2400000.5, -320000, 3, pv); + + vvd(pv[0][0], 0.9308038666832975759, 1e-11, + "eraPlan94", "x 3", status); + vvd(pv[0][1], 0.3258319040261346000, 1e-11, + "eraPlan94", "y 3", status); + vvd(pv[0][2], 0.1422794544481140560, 1e-11, + "eraPlan94", "z 3", status); + + vvd(pv[1][0], -0.6429458958255170006e-2, 1e-11, + "eraPlan94", "xd 3", status); + vvd(pv[1][1], 0.1468570657704237764e-1, 1e-11, + "eraPlan94", "yd 3", status); + vvd(pv[1][2], 0.6406996426270981189e-2, 1e-11, + "eraPlan94", "zd 3", status); + + viv(j, 1, "eraPlan94", "j 3", status); + + j = eraPlan94(2400000.5, 43999.9, 1, pv); + + vvd(pv[0][0], 0.2945293959257430832, 1e-11, + "eraPlan94", "x 4", status); + vvd(pv[0][1], -0.2452204176601049596, 1e-11, + "eraPlan94", "y 4", status); + vvd(pv[0][2], -0.1615427700571978153, 1e-11, + "eraPlan94", "z 4", status); + + vvd(pv[1][0], 0.1413867871404614441e-1, 1e-11, + "eraPlan94", "xd 4", status); + vvd(pv[1][1], 0.1946548301104706582e-1, 1e-11, + "eraPlan94", "yd 4", status); + vvd(pv[1][2], 0.8929809783898904786e-2, 1e-11, + "eraPlan94", "zd 4", status); + + viv(j, 0, "eraPlan94", "j 4", status); + +} + +static void t_pmat00(int *status) +/* +** - - - - - - - - - +** t _ p m a t 0 0 +** - - - - - - - - - +** +** Test eraPmat00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPmat00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbp[3][3]; + + + eraPmat00(2400000.5, 50123.9999, rbp); + + vvd(rbp[0][0], 0.9999995505175087260, 1e-12, + "eraPmat00", "11", status); + vvd(rbp[0][1], 0.8695405883617884705e-3, 1e-14, + "eraPmat00", "12", status); + vvd(rbp[0][2], 0.3779734722239007105e-3, 1e-14, + "eraPmat00", "13", status); + + vvd(rbp[1][0], -0.8695405990410863719e-3, 1e-14, + "eraPmat00", "21", status); + vvd(rbp[1][1], 0.9999996219494925900, 1e-12, + "eraPmat00", "22", status); + vvd(rbp[1][2], -0.1360775820404982209e-6, 1e-14, + "eraPmat00", "23", status); + + vvd(rbp[2][0], -0.3779734476558184991e-3, 1e-14, + "eraPmat00", "31", status); + vvd(rbp[2][1], -0.1925857585832024058e-6, 1e-14, + "eraPmat00", "32", status); + vvd(rbp[2][2], 0.9999999285680153377, 1e-12, + "eraPmat00", "33", status); + +} + +static void t_pmat06(int *status) +/* +** - - - - - - - - - +** t _ p m a t 0 6 +** - - - - - - - - - +** +** Test eraPmat06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPmat06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbp[3][3]; + + + eraPmat06(2400000.5, 50123.9999, rbp); + + vvd(rbp[0][0], 0.9999995505176007047, 1e-12, + "eraPmat06", "11", status); + vvd(rbp[0][1], 0.8695404617348208406e-3, 1e-14, + "eraPmat06", "12", status); + vvd(rbp[0][2], 0.3779735201865589104e-3, 1e-14, + "eraPmat06", "13", status); + + vvd(rbp[1][0], -0.8695404723772031414e-3, 1e-14, + "eraPmat06", "21", status); + vvd(rbp[1][1], 0.9999996219496027161, 1e-12, + "eraPmat06", "22", status); + vvd(rbp[1][2], -0.1361752497080270143e-6, 1e-14, + "eraPmat06", "23", status); + + vvd(rbp[2][0], -0.3779734957034089490e-3, 1e-14, + "eraPmat06", "31", status); + vvd(rbp[2][1], -0.1924880847894457113e-6, 1e-14, + "eraPmat06", "32", status); + vvd(rbp[2][2], 0.9999999285679971958, 1e-12, + "eraPmat06", "33", status); + +} + +static void t_pmat76(int *status) +/* +** - - - - - - - - - +** t _ p m a t 7 6 +** - - - - - - - - - +** +** Test eraPmat76 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPmat76, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rmatp[3][3]; + + + eraPmat76(2400000.5, 50123.9999, rmatp); + + vvd(rmatp[0][0], 0.9999995504328350733, 1e-12, + "eraPmat76", "11", status); + vvd(rmatp[0][1], 0.8696632209480960785e-3, 1e-14, + "eraPmat76", "12", status); + vvd(rmatp[0][2], 0.3779153474959888345e-3, 1e-14, + "eraPmat76", "13", status); + + vvd(rmatp[1][0], -0.8696632209485112192e-3, 1e-14, + "eraPmat76", "21", status); + vvd(rmatp[1][1], 0.9999996218428560614, 1e-12, + "eraPmat76", "22", status); + vvd(rmatp[1][2], -0.1643284776111886407e-6, 1e-14, + "eraPmat76", "23", status); + + vvd(rmatp[2][0], -0.3779153474950335077e-3, 1e-14, + "eraPmat76", "31", status); + vvd(rmatp[2][1], -0.1643306746147366896e-6, 1e-14, + "eraPmat76", "32", status); + vvd(rmatp[2][2], 0.9999999285899790119, 1e-12, + "eraPmat76", "33", status); + +} + +static void t_pm(int *status) +/* +** - - - - - +** t _ p m +** - - - - - +** +** Test eraPm function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPm, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3], r; + + + p[0] = 0.3; + p[1] = 1.2; + p[2] = -2.5; + + r = eraPm(p); + + vvd(r, 2.789265136196270604, 1e-12, "eraPm", "", status); + +} + +static void t_pmp(int *status) +/* +** - - - - - - +** t _ p m p +** - - - - - - +** +** Test eraPmp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPmp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3], b[3], amb[3]; + + + a[0] = 2.0; + a[1] = 2.0; + a[2] = 3.0; + + b[0] = 1.0; + b[1] = 3.0; + b[2] = 4.0; + + eraPmp(a, b, amb); + + vvd(amb[0], 1.0, 1e-12, "eraPmp", "0", status); + vvd(amb[1], -1.0, 1e-12, "eraPmp", "1", status); + vvd(amb[2], -1.0, 1e-12, "eraPmp", "2", status); + +} + +static void t_pmpx(int *status) +/* +** - - - - - - - +** t _ p m p x +** - - - - - - - +** +** Test eraPmpx function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPmpx, vvd +** +** This revision: 2013 October 2 +*/ +{ + double rc, dc, pr, pd, px, rv, pmt, pob[3], pco[3]; + + + rc = 1.234; + dc = 0.789; + pr = 1e-5; + pd = -2e-5; + px = 1e-2; + rv = 10.0; + pmt = 8.75; + pob[0] = 0.9; + pob[1] = 0.4; + pob[2] = 0.1; + + eraPmpx(rc, dc, pr, pd, px, rv, pmt, pob, pco); + + vvd(pco[0], 0.2328137623960308440, 1e-12, + "eraPmpx", "1", status); + vvd(pco[1], 0.6651097085397855317, 1e-12, + "eraPmpx", "2", status); + vvd(pco[2], 0.7095257765896359847, 1e-12, + "eraPmpx", "3", status); + +} + +static void t_pmsafe(int *status) +/* +** - - - - - - - - - +** t _ p m s a f e +** - - - - - - - - - +** +** Test eraPmsafe function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPmsafe, vvd, viv +** +** This revision: 2013 October 2 +*/ +{ + int j; + double ra1, dec1, pmr1, pmd1, px1, rv1, ep1a, ep1b, ep2a, ep2b, + ra2, dec2, pmr2, pmd2, px2, rv2; + + + ra1 = 1.234; + dec1 = 0.789; + pmr1 = 1e-5; + pmd1 = -2e-5; + px1 = 1e-2; + rv1 = 10.0; + ep1a = 2400000.5; + ep1b = 48348.5625; + ep2a = 2400000.5; + ep2b = 51544.5; + + j = eraPmsafe(ra1, dec1, pmr1, pmd1, px1, rv1, + ep1a, ep1b, ep2a, ep2b, + &ra2, &dec2, &pmr2, &pmd2, &px2, &rv2); + + vvd(ra2, 1.234087484501017061, 1e-12, + "eraPmsafe", "ra2", status); + vvd(dec2, 0.7888249982450468574, 1e-12, + "eraPmsafe", "dec2", status); + vvd(pmr2, 0.9996457663586073988e-5, 1e-12, + "eraPmsafe", "pmr2", status); + vvd(pmd2, -0.2000040085106737816e-4, 1e-16, + "eraPmsafe", "pmd2", status); + vvd(px2, 0.9999997295356765185e-2, 1e-12, + "eraPmsafe", "px2", status); + vvd(rv2, 10.38468380113917014, 1e-10, + "eraPmsafe", "rv2", status); + viv ( j, 0, "eraPmsafe", "j", status); + +} + +static void t_pn(int *status) +/* +** - - - - - +** t _ p n +** - - - - - +** +** Test eraPn function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPn, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3], r, u[3]; + + + p[0] = 0.3; + p[1] = 1.2; + p[2] = -2.5; + + eraPn(p, &r, u); + + vvd(r, 2.789265136196270604, 1e-12, "eraPn", "r", status); + + vvd(u[0], 0.1075552109073112058, 1e-12, "eraPn", "u1", status); + vvd(u[1], 0.4302208436292448232, 1e-12, "eraPn", "u2", status); + vvd(u[2], -0.8962934242275933816, 1e-12, "eraPn", "u3", status); + +} + +static void t_pn00(int *status) +/* +** - - - - - - - +** t _ p n 0 0 +** - - - - - - - +** +** Test eraPn00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPn00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps, epsa, + rb[3][3], rp[3][3], rbp[3][3], rn[3][3], rbpn[3][3]; + + + dpsi = -0.9632552291149335877e-5; + deps = 0.4063197106621141414e-4; + + eraPn00(2400000.5, 53736.0, dpsi, deps, + &epsa, rb, rp, rbp, rn, rbpn); + + vvd(epsa, 0.4090791789404229916, 1e-12, "eraPn00", "epsa", status); + + vvd(rb[0][0], 0.9999999999999942498, 1e-12, + "eraPn00", "rb11", status); + vvd(rb[0][1], -0.7078279744199196626e-7, 1e-18, + "eraPn00", "rb12", status); + vvd(rb[0][2], 0.8056217146976134152e-7, 1e-18, + "eraPn00", "rb13", status); + + vvd(rb[1][0], 0.7078279477857337206e-7, 1e-18, + "eraPn00", "rb21", status); + vvd(rb[1][1], 0.9999999999999969484, 1e-12, + "eraPn00", "rb22", status); + vvd(rb[1][2], 0.3306041454222136517e-7, 1e-18, + "eraPn00", "rb23", status); + + vvd(rb[2][0], -0.8056217380986972157e-7, 1e-18, + "eraPn00", "rb31", status); + vvd(rb[2][1], -0.3306040883980552500e-7, 1e-18, + "eraPn00", "rb32", status); + vvd(rb[2][2], 0.9999999999999962084, 1e-12, + "eraPn00", "rb33", status); + + vvd(rp[0][0], 0.9999989300532289018, 1e-12, + "eraPn00", "rp11", status); + vvd(rp[0][1], -0.1341647226791824349e-2, 1e-14, + "eraPn00", "rp12", status); + vvd(rp[0][2], -0.5829880927190296547e-3, 1e-14, + "eraPn00", "rp13", status); + + vvd(rp[1][0], 0.1341647231069759008e-2, 1e-14, + "eraPn00", "rp21", status); + vvd(rp[1][1], 0.9999990999908750433, 1e-12, + "eraPn00", "rp22", status); + vvd(rp[1][2], -0.3837444441583715468e-6, 1e-14, + "eraPn00", "rp23", status); + + vvd(rp[2][0], 0.5829880828740957684e-3, 1e-14, + "eraPn00", "rp31", status); + vvd(rp[2][1], -0.3984203267708834759e-6, 1e-14, + "eraPn00", "rp32", status); + vvd(rp[2][2], 0.9999998300623538046, 1e-12, + "eraPn00", "rp33", status); + + vvd(rbp[0][0], 0.9999989300052243993, 1e-12, + "eraPn00", "rbp11", status); + vvd(rbp[0][1], -0.1341717990239703727e-2, 1e-14, + "eraPn00", "rbp12", status); + vvd(rbp[0][2], -0.5829075749891684053e-3, 1e-14, + "eraPn00", "rbp13", status); + + vvd(rbp[1][0], 0.1341718013831739992e-2, 1e-14, + "eraPn00", "rbp21", status); + vvd(rbp[1][1], 0.9999990998959191343, 1e-12, + "eraPn00", "rbp22", status); + vvd(rbp[1][2], -0.3505759733565421170e-6, 1e-14, + "eraPn00", "rbp23", status); + + vvd(rbp[2][0], 0.5829075206857717883e-3, 1e-14, + "eraPn00", "rbp31", status); + vvd(rbp[2][1], -0.4315219955198608970e-6, 1e-14, + "eraPn00", "rbp32", status); + vvd(rbp[2][2], 0.9999998301093036269, 1e-12, + "eraPn00", "rbp33", status); + + vvd(rn[0][0], 0.9999999999536069682, 1e-12, + "eraPn00", "rn11", status); + vvd(rn[0][1], 0.8837746144872140812e-5, 1e-16, + "eraPn00", "rn12", status); + vvd(rn[0][2], 0.3831488838252590008e-5, 1e-16, + "eraPn00", "rn13", status); + + vvd(rn[1][0], -0.8837590456633197506e-5, 1e-16, + "eraPn00", "rn21", status); + vvd(rn[1][1], 0.9999999991354692733, 1e-12, + "eraPn00", "rn22", status); + vvd(rn[1][2], -0.4063198798559573702e-4, 1e-16, + "eraPn00", "rn23", status); + + vvd(rn[2][0], -0.3831847930135328368e-5, 1e-16, + "eraPn00", "rn31", status); + vvd(rn[2][1], 0.4063195412258150427e-4, 1e-16, + "eraPn00", "rn32", status); + vvd(rn[2][2], 0.9999999991671806225, 1e-12, + "eraPn00", "rn33", status); + + vvd(rbpn[0][0], 0.9999989440499982806, 1e-12, + "eraPn00", "rbpn11", status); + vvd(rbpn[0][1], -0.1332880253640848301e-2, 1e-14, + "eraPn00", "rbpn12", status); + vvd(rbpn[0][2], -0.5790760898731087295e-3, 1e-14, + "eraPn00", "rbpn13", status); + + vvd(rbpn[1][0], 0.1332856746979948745e-2, 1e-14, + "eraPn00", "rbpn21", status); + vvd(rbpn[1][1], 0.9999991109064768883, 1e-12, + "eraPn00", "rbpn22", status); + vvd(rbpn[1][2], -0.4097740555723063806e-4, 1e-14, + "eraPn00", "rbpn23", status); + + vvd(rbpn[2][0], 0.5791301929950205000e-3, 1e-14, + "eraPn00", "rbpn31", status); + vvd(rbpn[2][1], 0.4020553681373702931e-4, 1e-14, + "eraPn00", "rbpn32", status); + vvd(rbpn[2][2], 0.9999998314958529887, 1e-12, + "eraPn00", "rbpn33", status); + +} + +static void t_pn00a(int *status) +/* +** - - - - - - - - +** t _ p n 0 0 a +** - - - - - - - - +** +** Test eraPn00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPn00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps, epsa, + rb[3][3], rp[3][3], rbp[3][3], rn[3][3], rbpn[3][3]; + + + eraPn00a(2400000.5, 53736.0, + &dpsi, &deps, &epsa, rb, rp, rbp, rn, rbpn); + + vvd(dpsi, -0.9630909107115518431e-5, 1e-12, + "eraPn00a", "dpsi", status); + vvd(deps, 0.4063239174001678710e-4, 1e-12, + "eraPn00a", "deps", status); + vvd(epsa, 0.4090791789404229916, 1e-12, "eraPn00a", "epsa", status); + + vvd(rb[0][0], 0.9999999999999942498, 1e-12, + "eraPn00a", "rb11", status); + vvd(rb[0][1], -0.7078279744199196626e-7, 1e-16, + "eraPn00a", "rb12", status); + vvd(rb[0][2], 0.8056217146976134152e-7, 1e-16, + "eraPn00a", "rb13", status); + + vvd(rb[1][0], 0.7078279477857337206e-7, 1e-16, + "eraPn00a", "rb21", status); + vvd(rb[1][1], 0.9999999999999969484, 1e-12, + "eraPn00a", "rb22", status); + vvd(rb[1][2], 0.3306041454222136517e-7, 1e-16, + "eraPn00a", "rb23", status); + + vvd(rb[2][0], -0.8056217380986972157e-7, 1e-16, + "eraPn00a", "rb31", status); + vvd(rb[2][1], -0.3306040883980552500e-7, 1e-16, + "eraPn00a", "rb32", status); + vvd(rb[2][2], 0.9999999999999962084, 1e-12, + "eraPn00a", "rb33", status); + + vvd(rp[0][0], 0.9999989300532289018, 1e-12, + "eraPn00a", "rp11", status); + vvd(rp[0][1], -0.1341647226791824349e-2, 1e-14, + "eraPn00a", "rp12", status); + vvd(rp[0][2], -0.5829880927190296547e-3, 1e-14, + "eraPn00a", "rp13", status); + + vvd(rp[1][0], 0.1341647231069759008e-2, 1e-14, + "eraPn00a", "rp21", status); + vvd(rp[1][1], 0.9999990999908750433, 1e-12, + "eraPn00a", "rp22", status); + vvd(rp[1][2], -0.3837444441583715468e-6, 1e-14, + "eraPn00a", "rp23", status); + + vvd(rp[2][0], 0.5829880828740957684e-3, 1e-14, + "eraPn00a", "rp31", status); + vvd(rp[2][1], -0.3984203267708834759e-6, 1e-14, + "eraPn00a", "rp32", status); + vvd(rp[2][2], 0.9999998300623538046, 1e-12, + "eraPn00a", "rp33", status); + + vvd(rbp[0][0], 0.9999989300052243993, 1e-12, + "eraPn00a", "rbp11", status); + vvd(rbp[0][1], -0.1341717990239703727e-2, 1e-14, + "eraPn00a", "rbp12", status); + vvd(rbp[0][2], -0.5829075749891684053e-3, 1e-14, + "eraPn00a", "rbp13", status); + + vvd(rbp[1][0], 0.1341718013831739992e-2, 1e-14, + "eraPn00a", "rbp21", status); + vvd(rbp[1][1], 0.9999990998959191343, 1e-12, + "eraPn00a", "rbp22", status); + vvd(rbp[1][2], -0.3505759733565421170e-6, 1e-14, + "eraPn00a", "rbp23", status); + + vvd(rbp[2][0], 0.5829075206857717883e-3, 1e-14, + "eraPn00a", "rbp31", status); + vvd(rbp[2][1], -0.4315219955198608970e-6, 1e-14, + "eraPn00a", "rbp32", status); + vvd(rbp[2][2], 0.9999998301093036269, 1e-12, + "eraPn00a", "rbp33", status); + + vvd(rn[0][0], 0.9999999999536227949, 1e-12, + "eraPn00a", "rn11", status); + vvd(rn[0][1], 0.8836238544090873336e-5, 1e-14, + "eraPn00a", "rn12", status); + vvd(rn[0][2], 0.3830835237722400669e-5, 1e-14, + "eraPn00a", "rn13", status); + + vvd(rn[1][0], -0.8836082880798569274e-5, 1e-14, + "eraPn00a", "rn21", status); + vvd(rn[1][1], 0.9999999991354655028, 1e-12, + "eraPn00a", "rn22", status); + vvd(rn[1][2], -0.4063240865362499850e-4, 1e-14, + "eraPn00a", "rn23", status); + + vvd(rn[2][0], -0.3831194272065995866e-5, 1e-14, + "eraPn00a", "rn31", status); + vvd(rn[2][1], 0.4063237480216291775e-4, 1e-14, + "eraPn00a", "rn32", status); + vvd(rn[2][2], 0.9999999991671660338, 1e-12, + "eraPn00a", "rn33", status); + + vvd(rbpn[0][0], 0.9999989440476103435, 1e-12, + "eraPn00a", "rbpn11", status); + vvd(rbpn[0][1], -0.1332881761240011763e-2, 1e-14, + "eraPn00a", "rbpn12", status); + vvd(rbpn[0][2], -0.5790767434730085751e-3, 1e-14, + "eraPn00a", "rbpn13", status); + + vvd(rbpn[1][0], 0.1332858254308954658e-2, 1e-14, + "eraPn00a", "rbpn21", status); + vvd(rbpn[1][1], 0.9999991109044505577, 1e-12, + "eraPn00a", "rbpn22", status); + vvd(rbpn[1][2], -0.4097782710396580452e-4, 1e-14, + "eraPn00a", "rbpn23", status); + + vvd(rbpn[2][0], 0.5791308472168152904e-3, 1e-14, + "eraPn00a", "rbpn31", status); + vvd(rbpn[2][1], 0.4020595661591500259e-4, 1e-14, + "eraPn00a", "rbpn32", status); + vvd(rbpn[2][2], 0.9999998314954572304, 1e-12, + "eraPn00a", "rbpn33", status); + +} + +static void t_pn00b(int *status) +/* +** - - - - - - - - +** t _ p n 0 0 b +** - - - - - - - - +** +** Test eraPn00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPn00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps, epsa, + rb[3][3], rp[3][3], rbp[3][3], rn[3][3], rbpn[3][3]; + + + eraPn00b(2400000.5, 53736.0, &dpsi, &deps, &epsa, + rb, rp, rbp, rn, rbpn); + + vvd(dpsi, -0.9632552291148362783e-5, 1e-12, + "eraPn00b", "dpsi", status); + vvd(deps, 0.4063197106621159367e-4, 1e-12, + "eraPn00b", "deps", status); + vvd(epsa, 0.4090791789404229916, 1e-12, "eraPn00b", "epsa", status); + + vvd(rb[0][0], 0.9999999999999942498, 1e-12, + "eraPn00b", "rb11", status); + vvd(rb[0][1], -0.7078279744199196626e-7, 1e-16, + "eraPn00b", "rb12", status); + vvd(rb[0][2], 0.8056217146976134152e-7, 1e-16, + "eraPn00b", "rb13", status); + + vvd(rb[1][0], 0.7078279477857337206e-7, 1e-16, + "eraPn00b", "rb21", status); + vvd(rb[1][1], 0.9999999999999969484, 1e-12, + "eraPn00b", "rb22", status); + vvd(rb[1][2], 0.3306041454222136517e-7, 1e-16, + "eraPn00b", "rb23", status); + + vvd(rb[2][0], -0.8056217380986972157e-7, 1e-16, + "eraPn00b", "rb31", status); + vvd(rb[2][1], -0.3306040883980552500e-7, 1e-16, + "eraPn00b", "rb32", status); + vvd(rb[2][2], 0.9999999999999962084, 1e-12, + "eraPn00b", "rb33", status); + + vvd(rp[0][0], 0.9999989300532289018, 1e-12, + "eraPn00b", "rp11", status); + vvd(rp[0][1], -0.1341647226791824349e-2, 1e-14, + "eraPn00b", "rp12", status); + vvd(rp[0][2], -0.5829880927190296547e-3, 1e-14, + "eraPn00b", "rp13", status); + + vvd(rp[1][0], 0.1341647231069759008e-2, 1e-14, + "eraPn00b", "rp21", status); + vvd(rp[1][1], 0.9999990999908750433, 1e-12, + "eraPn00b", "rp22", status); + vvd(rp[1][2], -0.3837444441583715468e-6, 1e-14, + "eraPn00b", "rp23", status); + + vvd(rp[2][0], 0.5829880828740957684e-3, 1e-14, + "eraPn00b", "rp31", status); + vvd(rp[2][1], -0.3984203267708834759e-6, 1e-14, + "eraPn00b", "rp32", status); + vvd(rp[2][2], 0.9999998300623538046, 1e-12, + "eraPn00b", "rp33", status); + + vvd(rbp[0][0], 0.9999989300052243993, 1e-12, + "eraPn00b", "rbp11", status); + vvd(rbp[0][1], -0.1341717990239703727e-2, 1e-14, + "eraPn00b", "rbp12", status); + vvd(rbp[0][2], -0.5829075749891684053e-3, 1e-14, + "eraPn00b", "rbp13", status); + + vvd(rbp[1][0], 0.1341718013831739992e-2, 1e-14, + "eraPn00b", "rbp21", status); + vvd(rbp[1][1], 0.9999990998959191343, 1e-12, + "eraPn00b", "rbp22", status); + vvd(rbp[1][2], -0.3505759733565421170e-6, 1e-14, + "eraPn00b", "rbp23", status); + + vvd(rbp[2][0], 0.5829075206857717883e-3, 1e-14, + "eraPn00b", "rbp31", status); + vvd(rbp[2][1], -0.4315219955198608970e-6, 1e-14, + "eraPn00b", "rbp32", status); + vvd(rbp[2][2], 0.9999998301093036269, 1e-12, + "eraPn00b", "rbp33", status); + + vvd(rn[0][0], 0.9999999999536069682, 1e-12, + "eraPn00b", "rn11", status); + vvd(rn[0][1], 0.8837746144871248011e-5, 1e-14, + "eraPn00b", "rn12", status); + vvd(rn[0][2], 0.3831488838252202945e-5, 1e-14, + "eraPn00b", "rn13", status); + + vvd(rn[1][0], -0.8837590456632304720e-5, 1e-14, + "eraPn00b", "rn21", status); + vvd(rn[1][1], 0.9999999991354692733, 1e-12, + "eraPn00b", "rn22", status); + vvd(rn[1][2], -0.4063198798559591654e-4, 1e-14, + "eraPn00b", "rn23", status); + + vvd(rn[2][0], -0.3831847930134941271e-5, 1e-14, + "eraPn00b", "rn31", status); + vvd(rn[2][1], 0.4063195412258168380e-4, 1e-14, + "eraPn00b", "rn32", status); + vvd(rn[2][2], 0.9999999991671806225, 1e-12, + "eraPn00b", "rn33", status); + + vvd(rbpn[0][0], 0.9999989440499982806, 1e-12, + "eraPn00b", "rbpn11", status); + vvd(rbpn[0][1], -0.1332880253640849194e-2, 1e-14, + "eraPn00b", "rbpn12", status); + vvd(rbpn[0][2], -0.5790760898731091166e-3, 1e-14, + "eraPn00b", "rbpn13", status); + + vvd(rbpn[1][0], 0.1332856746979949638e-2, 1e-14, + "eraPn00b", "rbpn21", status); + vvd(rbpn[1][1], 0.9999991109064768883, 1e-12, + "eraPn00b", "rbpn22", status); + vvd(rbpn[1][2], -0.4097740555723081811e-4, 1e-14, + "eraPn00b", "rbpn23", status); + + vvd(rbpn[2][0], 0.5791301929950208873e-3, 1e-14, + "eraPn00b", "rbpn31", status); + vvd(rbpn[2][1], 0.4020553681373720832e-4, 1e-14, + "eraPn00b", "rbpn32", status); + vvd(rbpn[2][2], 0.9999998314958529887, 1e-12, + "eraPn00b", "rbpn33", status); + +} + +static void t_pn06a(int *status) +/* +** - - - - - - - - +** t _ p n 0 6 a +** - - - - - - - - +** +** Test eraPn06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPn06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps, epsa; + double rb[3][3], rp[3][3], rbp[3][3], rn[3][3], rbpn[3][3]; + + + eraPn06a(2400000.5, 53736.0, &dpsi, &deps, &epsa, + rb, rp, rbp, rn, rbpn); + + vvd(dpsi, -0.9630912025820308797e-5, 1e-12, + "eraPn06a", "dpsi", status); + vvd(deps, 0.4063238496887249798e-4, 1e-12, + "eraPn06a", "deps", status); + vvd(epsa, 0.4090789763356509926, 1e-12, "eraPn06a", "epsa", status); + + vvd(rb[0][0], 0.9999999999999942497, 1e-12, + "eraPn06a", "rb11", status); + vvd(rb[0][1], -0.7078368960971557145e-7, 1e-14, + "eraPn06a", "rb12", status); + vvd(rb[0][2], 0.8056213977613185606e-7, 1e-14, + "eraPn06a", "rb13", status); + + vvd(rb[1][0], 0.7078368694637674333e-7, 1e-14, + "eraPn06a", "rb21", status); + vvd(rb[1][1], 0.9999999999999969484, 1e-12, + "eraPn06a", "rb22", status); + vvd(rb[1][2], 0.3305943742989134124e-7, 1e-14, + "eraPn06a", "rb23", status); + + vvd(rb[2][0], -0.8056214211620056792e-7, 1e-14, + "eraPn06a", "rb31", status); + vvd(rb[2][1], -0.3305943172740586950e-7, 1e-14, + "eraPn06a", "rb32", status); + vvd(rb[2][2], 0.9999999999999962084, 1e-12, + "eraPn06a", "rb33", status); + + vvd(rp[0][0], 0.9999989300536854831, 1e-12, + "eraPn06a", "rp11", status); + vvd(rp[0][1], -0.1341646886204443795e-2, 1e-14, + "eraPn06a", "rp12", status); + vvd(rp[0][2], -0.5829880933488627759e-3, 1e-14, + "eraPn06a", "rp13", status); + + vvd(rp[1][0], 0.1341646890569782183e-2, 1e-14, + "eraPn06a", "rp21", status); + vvd(rp[1][1], 0.9999990999913319321, 1e-12, + "eraPn06a", "rp22", status); + vvd(rp[1][2], -0.3835944216374477457e-6, 1e-14, + "eraPn06a", "rp23", status); + + vvd(rp[2][0], 0.5829880833027867368e-3, 1e-14, + "eraPn06a", "rp31", status); + vvd(rp[2][1], -0.3985701514686976112e-6, 1e-14, + "eraPn06a", "rp32", status); + vvd(rp[2][2], 0.9999998300623534950, 1e-12, + "eraPn06a", "rp33", status); + + vvd(rbp[0][0], 0.9999989300056797893, 1e-12, + "eraPn06a", "rbp11", status); + vvd(rbp[0][1], -0.1341717650545059598e-2, 1e-14, + "eraPn06a", "rbp12", status); + vvd(rbp[0][2], -0.5829075756493728856e-3, 1e-14, + "eraPn06a", "rbp13", status); + + vvd(rbp[1][0], 0.1341717674223918101e-2, 1e-14, + "eraPn06a", "rbp21", status); + vvd(rbp[1][1], 0.9999990998963748448, 1e-12, + "eraPn06a", "rbp22", status); + vvd(rbp[1][2], -0.3504269280170069029e-6, 1e-14, + "eraPn06a", "rbp23", status); + + vvd(rbp[2][0], 0.5829075211461454599e-3, 1e-14, + "eraPn06a", "rbp31", status); + vvd(rbp[2][1], -0.4316708436255949093e-6, 1e-14, + "eraPn06a", "rbp32", status); + vvd(rbp[2][2], 0.9999998301093032943, 1e-12, + "eraPn06a", "rbp33", status); + + vvd(rn[0][0], 0.9999999999536227668, 1e-12, + "eraPn06a", "rn11", status); + vvd(rn[0][1], 0.8836241998111535233e-5, 1e-14, + "eraPn06a", "rn12", status); + vvd(rn[0][2], 0.3830834608415287707e-5, 1e-14, + "eraPn06a", "rn13", status); + + vvd(rn[1][0], -0.8836086334870740138e-5, 1e-14, + "eraPn06a", "rn21", status); + vvd(rn[1][1], 0.9999999991354657474, 1e-12, + "eraPn06a", "rn22", status); + vvd(rn[1][2], -0.4063240188248455065e-4, 1e-14, + "eraPn06a", "rn23", status); + + vvd(rn[2][0], -0.3831193642839398128e-5, 1e-14, + "eraPn06a", "rn31", status); + vvd(rn[2][1], 0.4063236803101479770e-4, 1e-14, + "eraPn06a", "rn32", status); + vvd(rn[2][2], 0.9999999991671663114, 1e-12, + "eraPn06a", "rn33", status); + + vvd(rbpn[0][0], 0.9999989440480669738, 1e-12, + "eraPn06a", "rbpn11", status); + vvd(rbpn[0][1], -0.1332881418091915973e-2, 1e-14, + "eraPn06a", "rbpn12", status); + vvd(rbpn[0][2], -0.5790767447612042565e-3, 1e-14, + "eraPn06a", "rbpn13", status); + + vvd(rbpn[1][0], 0.1332857911250989133e-2, 1e-14, + "eraPn06a", "rbpn21", status); + vvd(rbpn[1][1], 0.9999991109049141908, 1e-12, + "eraPn06a", "rbpn22", status); + vvd(rbpn[1][2], -0.4097767128546784878e-4, 1e-14, + "eraPn06a", "rbpn23", status); + + vvd(rbpn[2][0], 0.5791308482835292617e-3, 1e-14, + "eraPn06a", "rbpn31", status); + vvd(rbpn[2][1], 0.4020580099454020310e-4, 1e-14, + "eraPn06a", "rbpn32", status); + vvd(rbpn[2][2], 0.9999998314954628695, 1e-12, + "eraPn06a", "rbpn33", status); + +} + +static void t_pn06(int *status) +/* +** - - - - - - - +** t _ p n 0 6 +** - - - - - - - +** +** Test eraPn06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPn06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsi, deps, epsa, + rb[3][3], rp[3][3], rbp[3][3], rn[3][3], rbpn[3][3]; + + + dpsi = -0.9632552291149335877e-5; + deps = 0.4063197106621141414e-4; + + eraPn06(2400000.5, 53736.0, dpsi, deps, + &epsa, rb, rp, rbp, rn, rbpn); + + vvd(epsa, 0.4090789763356509926, 1e-12, "eraPn06", "epsa", status); + + vvd(rb[0][0], 0.9999999999999942497, 1e-12, + "eraPn06", "rb11", status); + vvd(rb[0][1], -0.7078368960971557145e-7, 1e-14, + "eraPn06", "rb12", status); + vvd(rb[0][2], 0.8056213977613185606e-7, 1e-14, + "eraPn06", "rb13", status); + + vvd(rb[1][0], 0.7078368694637674333e-7, 1e-14, + "eraPn06", "rb21", status); + vvd(rb[1][1], 0.9999999999999969484, 1e-12, + "eraPn06", "rb22", status); + vvd(rb[1][2], 0.3305943742989134124e-7, 1e-14, + "eraPn06", "rb23", status); + + vvd(rb[2][0], -0.8056214211620056792e-7, 1e-14, + "eraPn06", "rb31", status); + vvd(rb[2][1], -0.3305943172740586950e-7, 1e-14, + "eraPn06", "rb32", status); + vvd(rb[2][2], 0.9999999999999962084, 1e-12, + "eraPn06", "rb33", status); + + vvd(rp[0][0], 0.9999989300536854831, 1e-12, + "eraPn06", "rp11", status); + vvd(rp[0][1], -0.1341646886204443795e-2, 1e-14, + "eraPn06", "rp12", status); + vvd(rp[0][2], -0.5829880933488627759e-3, 1e-14, + "eraPn06", "rp13", status); + + vvd(rp[1][0], 0.1341646890569782183e-2, 1e-14, + "eraPn06", "rp21", status); + vvd(rp[1][1], 0.9999990999913319321, 1e-12, + "eraPn06", "rp22", status); + vvd(rp[1][2], -0.3835944216374477457e-6, 1e-14, + "eraPn06", "rp23", status); + + vvd(rp[2][0], 0.5829880833027867368e-3, 1e-14, + "eraPn06", "rp31", status); + vvd(rp[2][1], -0.3985701514686976112e-6, 1e-14, + "eraPn06", "rp32", status); + vvd(rp[2][2], 0.9999998300623534950, 1e-12, + "eraPn06", "rp33", status); + + vvd(rbp[0][0], 0.9999989300056797893, 1e-12, + "eraPn06", "rbp11", status); + vvd(rbp[0][1], -0.1341717650545059598e-2, 1e-14, + "eraPn06", "rbp12", status); + vvd(rbp[0][2], -0.5829075756493728856e-3, 1e-14, + "eraPn06", "rbp13", status); + + vvd(rbp[1][0], 0.1341717674223918101e-2, 1e-14, + "eraPn06", "rbp21", status); + vvd(rbp[1][1], 0.9999990998963748448, 1e-12, + "eraPn06", "rbp22", status); + vvd(rbp[1][2], -0.3504269280170069029e-6, 1e-14, + "eraPn06", "rbp23", status); + + vvd(rbp[2][0], 0.5829075211461454599e-3, 1e-14, + "eraPn06", "rbp31", status); + vvd(rbp[2][1], -0.4316708436255949093e-6, 1e-14, + "eraPn06", "rbp32", status); + vvd(rbp[2][2], 0.9999998301093032943, 1e-12, + "eraPn06", "rbp33", status); + + vvd(rn[0][0], 0.9999999999536069682, 1e-12, + "eraPn06", "rn11", status); + vvd(rn[0][1], 0.8837746921149881914e-5, 1e-14, + "eraPn06", "rn12", status); + vvd(rn[0][2], 0.3831487047682968703e-5, 1e-14, + "eraPn06", "rn13", status); + + vvd(rn[1][0], -0.8837591232983692340e-5, 1e-14, + "eraPn06", "rn21", status); + vvd(rn[1][1], 0.9999999991354692664, 1e-12, + "eraPn06", "rn22", status); + vvd(rn[1][2], -0.4063198798558931215e-4, 1e-14, + "eraPn06", "rn23", status); + + vvd(rn[2][0], -0.3831846139597250235e-5, 1e-14, + "eraPn06", "rn31", status); + vvd(rn[2][1], 0.4063195412258792914e-4, 1e-14, + "eraPn06", "rn32", status); + vvd(rn[2][2], 0.9999999991671806293, 1e-12, + "eraPn06", "rn33", status); + + vvd(rbpn[0][0], 0.9999989440504506688, 1e-12, + "eraPn06", "rbpn11", status); + vvd(rbpn[0][1], -0.1332879913170492655e-2, 1e-14, + "eraPn06", "rbpn12", status); + vvd(rbpn[0][2], -0.5790760923225655753e-3, 1e-14, + "eraPn06", "rbpn13", status); + + vvd(rbpn[1][0], 0.1332856406595754748e-2, 1e-14, + "eraPn06", "rbpn21", status); + vvd(rbpn[1][1], 0.9999991109069366795, 1e-12, + "eraPn06", "rbpn22", status); + vvd(rbpn[1][2], -0.4097725651142641812e-4, 1e-14, + "eraPn06", "rbpn23", status); + + vvd(rbpn[2][0], 0.5791301952321296716e-3, 1e-14, + "eraPn06", "rbpn31", status); + vvd(rbpn[2][1], 0.4020538796195230577e-4, 1e-14, + "eraPn06", "rbpn32", status); + vvd(rbpn[2][2], 0.9999998314958576778, 1e-12, + "eraPn06", "rbpn33", status); + +} + +static void t_pnm00a(int *status) +/* +** - - - - - - - - - +** t _ p n m 0 0 a +** - - - - - - - - - +** +** Test eraPnm00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPnm00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbpn[3][3]; + + + eraPnm00a(2400000.5, 50123.9999, rbpn); + + vvd(rbpn[0][0], 0.9999995832793134257, 1e-12, + "eraPnm00a", "11", status); + vvd(rbpn[0][1], 0.8372384254137809439e-3, 1e-14, + "eraPnm00a", "12", status); + vvd(rbpn[0][2], 0.3639684306407150645e-3, 1e-14, + "eraPnm00a", "13", status); + + vvd(rbpn[1][0], -0.8372535226570394543e-3, 1e-14, + "eraPnm00a", "21", status); + vvd(rbpn[1][1], 0.9999996486491582471, 1e-12, + "eraPnm00a", "22", status); + vvd(rbpn[1][2], 0.4132915262664072381e-4, 1e-14, + "eraPnm00a", "23", status); + + vvd(rbpn[2][0], -0.3639337004054317729e-3, 1e-14, + "eraPnm00a", "31", status); + vvd(rbpn[2][1], -0.4163386925461775873e-4, 1e-14, + "eraPnm00a", "32", status); + vvd(rbpn[2][2], 0.9999999329094390695, 1e-12, + "eraPnm00a", "33", status); + +} + +static void t_pnm00b(int *status) +/* +** - - - - - - - - - +** t _ p n m 0 0 b +** - - - - - - - - - +** +** Test eraPnm00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPnm00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbpn[3][3]; + + + eraPnm00b(2400000.5, 50123.9999, rbpn); + + vvd(rbpn[0][0], 0.9999995832776208280, 1e-12, + "eraPnm00b", "11", status); + vvd(rbpn[0][1], 0.8372401264429654837e-3, 1e-14, + "eraPnm00b", "12", status); + vvd(rbpn[0][2], 0.3639691681450271771e-3, 1e-14, + "eraPnm00b", "13", status); + + vvd(rbpn[1][0], -0.8372552234147137424e-3, 1e-14, + "eraPnm00b", "21", status); + vvd(rbpn[1][1], 0.9999996486477686123, 1e-12, + "eraPnm00b", "22", status); + vvd(rbpn[1][2], 0.4132832190946052890e-4, 1e-14, + "eraPnm00b", "23", status); + + vvd(rbpn[2][0], -0.3639344385341866407e-3, 1e-14, + "eraPnm00b", "31", status); + vvd(rbpn[2][1], -0.4163303977421522785e-4, 1e-14, + "eraPnm00b", "32", status); + vvd(rbpn[2][2], 0.9999999329092049734, 1e-12, + "eraPnm00b", "33", status); + +} + +static void t_pnm06a(int *status) +/* +** - - - - - - - - - +** t _ p n m 0 6 a +** - - - - - - - - - +** +** Test eraPnm06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPnm06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rbpn[3][3]; + + + eraPnm06a(2400000.5, 50123.9999, rbpn); + + vvd(rbpn[0][0], 0.9999995832794205484, 1e-12, + "eraPnm06a", "11", status); + vvd(rbpn[0][1], 0.8372382772630962111e-3, 1e-14, + "eraPnm06a", "12", status); + vvd(rbpn[0][2], 0.3639684771140623099e-3, 1e-14, + "eraPnm06a", "13", status); + + vvd(rbpn[1][0], -0.8372533744743683605e-3, 1e-14, + "eraPnm06a", "21", status); + vvd(rbpn[1][1], 0.9999996486492861646, 1e-12, + "eraPnm06a", "22", status); + vvd(rbpn[1][2], 0.4132905944611019498e-4, 1e-14, + "eraPnm06a", "23", status); + + vvd(rbpn[2][0], -0.3639337469629464969e-3, 1e-14, + "eraPnm06a", "31", status); + vvd(rbpn[2][1], -0.4163377605910663999e-4, 1e-14, + "eraPnm06a", "32", status); + vvd(rbpn[2][2], 0.9999999329094260057, 1e-12, + "eraPnm06a", "33", status); + +} + +static void t_pnm80(int *status) +/* +** - - - - - - - - +** t _ p n m 8 0 +** - - - - - - - - +** +** Test eraPnm80 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPnm80, vvd +** +** This revision: 2013 August 7 +*/ +{ + double rmatpn[3][3]; + + + eraPnm80(2400000.5, 50123.9999, rmatpn); + + vvd(rmatpn[0][0], 0.9999995831934611169, 1e-12, + "eraPnm80", "11", status); + vvd(rmatpn[0][1], 0.8373654045728124011e-3, 1e-14, + "eraPnm80", "12", status); + vvd(rmatpn[0][2], 0.3639121916933106191e-3, 1e-14, + "eraPnm80", "13", status); + + vvd(rmatpn[1][0], -0.8373804896118301316e-3, 1e-14, + "eraPnm80", "21", status); + vvd(rmatpn[1][1], 0.9999996485439674092, 1e-12, + "eraPnm80", "22", status); + vvd(rmatpn[1][2], 0.4130202510421549752e-4, 1e-14, + "eraPnm80", "23", status); + + vvd(rmatpn[2][0], -0.3638774789072144473e-3, 1e-14, + "eraPnm80", "31", status); + vvd(rmatpn[2][1], -0.4160674085851722359e-4, 1e-14, + "eraPnm80", "32", status); + vvd(rmatpn[2][2], 0.9999999329310274805, 1e-12, + "eraPnm80", "33", status); + +} + +static void t_pom00(int *status) +/* +** - - - - - - - - +** t _ p o m 0 0 +** - - - - - - - - +** +** Test eraPom00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPom00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double xp, yp, sp, rpom[3][3]; + + + xp = 2.55060238e-7; + yp = 1.860359247e-6; + sp = -0.1367174580728891460e-10; + + eraPom00(xp, yp, sp, rpom); + + vvd(rpom[0][0], 0.9999999999999674721, 1e-12, + "eraPom00", "11", status); + vvd(rpom[0][1], -0.1367174580728846989e-10, 1e-16, + "eraPom00", "12", status); + vvd(rpom[0][2], 0.2550602379999972345e-6, 1e-16, + "eraPom00", "13", status); + + vvd(rpom[1][0], 0.1414624947957029801e-10, 1e-16, + "eraPom00", "21", status); + vvd(rpom[1][1], 0.9999999999982695317, 1e-12, + "eraPom00", "22", status); + vvd(rpom[1][2], -0.1860359246998866389e-5, 1e-16, + "eraPom00", "23", status); + + vvd(rpom[2][0], -0.2550602379741215021e-6, 1e-16, + "eraPom00", "31", status); + vvd(rpom[2][1], 0.1860359247002414021e-5, 1e-16, + "eraPom00", "32", status); + vvd(rpom[2][2], 0.9999999999982370039, 1e-12, + "eraPom00", "33", status); + +} + +static void t_ppp(int *status) +/* +** - - - - - - +** t _ p p p +** - - - - - - +** +** Test eraPpp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPpp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3], b[3], apb[3]; + + + a[0] = 2.0; + a[1] = 2.0; + a[2] = 3.0; + + b[0] = 1.0; + b[1] = 3.0; + b[2] = 4.0; + + eraPpp(a, b, apb); + + vvd(apb[0], 3.0, 1e-12, "eraPpp", "0", status); + vvd(apb[1], 5.0, 1e-12, "eraPpp", "1", status); + vvd(apb[2], 7.0, 1e-12, "eraPpp", "2", status); + +} + +static void t_ppsp(int *status) +/* +** - - - - - - - +** t _ p p s p +** - - - - - - - +** +** Test eraPpsp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPpsp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3], s, b[3], apsb[3]; + + + a[0] = 2.0; + a[1] = 2.0; + a[2] = 3.0; + + s = 5.0; + + b[0] = 1.0; + b[1] = 3.0; + b[2] = 4.0; + + eraPpsp(a, s, b, apsb); + + vvd(apsb[0], 7.0, 1e-12, "eraPpsp", "0", status); + vvd(apsb[1], 17.0, 1e-12, "eraPpsp", "1", status); + vvd(apsb[2], 23.0, 1e-12, "eraPpsp", "2", status); + +} + +static void t_pr00(int *status) +/* +** - - - - - - - +** t _ p r 0 0 +** - - - - - - - +** +** Test eraPr00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPr00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double dpsipr, depspr; + + eraPr00(2400000.5, 53736, &dpsipr, &depspr); + + vvd(dpsipr, -0.8716465172668347629e-7, 1e-22, + "eraPr00", "dpsipr", status); + vvd(depspr, -0.7342018386722813087e-8, 1e-22, + "eraPr00", "depspr", status); + +} + +static void t_prec76(int *status) +/* +** - - - - - - - - - +** t _ p r e c 7 6 +** - - - - - - - - - +** +** Test eraPrec76 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPrec76, vvd +** +** This revision: 2013 August 7 +*/ +{ + double ep01, ep02, ep11, ep12, zeta, z, theta; + + + ep01 = 2400000.5; + ep02 = 33282.0; + ep11 = 2400000.5; + ep12 = 51544.0; + + eraPrec76(ep01, ep02, ep11, ep12, &zeta, &z, &theta); + + vvd(zeta, 0.5588961642000161243e-2, 1e-12, + "eraPrec76", "zeta", status); + vvd(z, 0.5589922365870680624e-2, 1e-12, + "eraPrec76", "z", status); + vvd(theta, 0.4858945471687296760e-2, 1e-12, + "eraPrec76", "theta", status); + +} + +static void t_pv2p(int *status) +/* +** - - - - - - - +** t _ p v 2 p +** - - - - - - - +** +** Test eraPv2p function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPv2p, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3], p[3]; + + + pv[0][0] = 0.3; + pv[0][1] = 1.2; + pv[0][2] = -2.5; + + pv[1][0] = -0.5; + pv[1][1] = 3.1; + pv[1][2] = 0.9; + + eraPv2p(pv, p); + + vvd(p[0], 0.3, 0.0, "eraPv2p", "1", status); + vvd(p[1], 1.2, 0.0, "eraPv2p", "2", status); + vvd(p[2], -2.5, 0.0, "eraPv2p", "3", status); + +} + +static void t_pv2s(int *status) +/* +** - - - - - - - +** t _ p v 2 s +** - - - - - - - +** +** Test eraPv2s function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPv2s, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3], theta, phi, r, td, pd, rd; + + + pv[0][0] = -0.4514964673880165; + pv[0][1] = 0.03093394277342585; + pv[0][2] = 0.05594668105108779; + + pv[1][0] = 1.292270850663260e-5; + pv[1][1] = 2.652814182060692e-6; + pv[1][2] = 2.568431853930293e-6; + + eraPv2s(pv, &theta, &phi, &r, &td, &pd, &rd); + + vvd(theta, 3.073185307179586515, 1e-12, "eraPv2s", "theta", status); + vvd(phi, 0.1229999999999999992, 1e-12, "eraPv2s", "phi", status); + vvd(r, 0.4559999999999999757, 1e-12, "eraPv2s", "r", status); + vvd(td, -0.7800000000000000364e-5, 1e-16, "eraPv2s", "td", status); + vvd(pd, 0.9010000000000001639e-5, 1e-16, "eraPv2s", "pd", status); + vvd(rd, -0.1229999999999999832e-4, 1e-16, "eraPv2s", "rd", status); + +} + +static void t_pvdpv(int *status) +/* +** - - - - - - - - +** t _ p v d p v +** - - - - - - - - +** +** Test eraPvdpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvdpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[2][3], b[2][3], adb[2]; + + + a[0][0] = 2.0; + a[0][1] = 2.0; + a[0][2] = 3.0; + + a[1][0] = 6.0; + a[1][1] = 0.0; + a[1][2] = 4.0; + + b[0][0] = 1.0; + b[0][1] = 3.0; + b[0][2] = 4.0; + + b[1][0] = 0.0; + b[1][1] = 2.0; + b[1][2] = 8.0; + + eraPvdpv(a, b, adb); + + vvd(adb[0], 20.0, 1e-12, "eraPvdpv", "1", status); + vvd(adb[1], 50.0, 1e-12, "eraPvdpv", "2", status); + +} + +static void t_pvm(int *status) +/* +** - - - - - - +** t _ p v m +** - - - - - - +** +** Test eraPvm function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvm, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3], r, s; + + + pv[0][0] = 0.3; + pv[0][1] = 1.2; + pv[0][2] = -2.5; + + pv[1][0] = 0.45; + pv[1][1] = -0.25; + pv[1][2] = 1.1; + + eraPvm(pv, &r, &s); + + vvd(r, 2.789265136196270604, 1e-12, "eraPvm", "r", status); + vvd(s, 1.214495780149111922, 1e-12, "eraPvm", "s", status); + +} + +static void t_pvmpv(int *status) +/* +** - - - - - - - - +** t _ p v m p v +** - - - - - - - - +** +** Test eraPvmpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvmpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[2][3], b[2][3], amb[2][3]; + + + a[0][0] = 2.0; + a[0][1] = 2.0; + a[0][2] = 3.0; + + a[1][0] = 5.0; + a[1][1] = 6.0; + a[1][2] = 3.0; + + b[0][0] = 1.0; + b[0][1] = 3.0; + b[0][2] = 4.0; + + b[1][0] = 3.0; + b[1][1] = 2.0; + b[1][2] = 1.0; + + eraPvmpv(a, b, amb); + + vvd(amb[0][0], 1.0, 1e-12, "eraPvmpv", "11", status); + vvd(amb[0][1], -1.0, 1e-12, "eraPvmpv", "21", status); + vvd(amb[0][2], -1.0, 1e-12, "eraPvmpv", "31", status); + + vvd(amb[1][0], 2.0, 1e-12, "eraPvmpv", "12", status); + vvd(amb[1][1], 4.0, 1e-12, "eraPvmpv", "22", status); + vvd(amb[1][2], 2.0, 1e-12, "eraPvmpv", "32", status); + +} + +static void t_pvppv(int *status) +/* +** - - - - - - - - +** t _ p v p p v +** - - - - - - - - +** +** Test eraPvppv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvppv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[2][3], b[2][3], apb[2][3]; + + + a[0][0] = 2.0; + a[0][1] = 2.0; + a[0][2] = 3.0; + + a[1][0] = 5.0; + a[1][1] = 6.0; + a[1][2] = 3.0; + + b[0][0] = 1.0; + b[0][1] = 3.0; + b[0][2] = 4.0; + + b[1][0] = 3.0; + b[1][1] = 2.0; + b[1][2] = 1.0; + + eraPvppv(a, b, apb); + + vvd(apb[0][0], 3.0, 1e-12, "eraPvppv", "p1", status); + vvd(apb[0][1], 5.0, 1e-12, "eraPvppv", "p2", status); + vvd(apb[0][2], 7.0, 1e-12, "eraPvppv", "p3", status); + + vvd(apb[1][0], 8.0, 1e-12, "eraPvppv", "v1", status); + vvd(apb[1][1], 8.0, 1e-12, "eraPvppv", "v2", status); + vvd(apb[1][2], 4.0, 1e-12, "eraPvppv", "v3", status); + +} + +static void t_pvstar(int *status) +/* +** - - - - - - - - - +** t _ p v s t a r +** - - - - - - - - - +** +** Test eraPvstar function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvstar, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3], ra, dec, pmr, pmd, px, rv; + int j; + + + pv[0][0] = 126668.5912743160601; + pv[0][1] = 2136.792716839935195; + pv[0][2] = -245251.2339876830091; + + pv[1][0] = -0.4051854035740712739e-2; + pv[1][1] = -0.6253919754866173866e-2; + pv[1][2] = 0.1189353719774107189e-1; + + j = eraPvstar(pv, &ra, &dec, &pmr, &pmd, &px, &rv); + + vvd(ra, 0.1686756e-1, 1e-12, "eraPvstar", "ra", status); + vvd(dec, -1.093989828, 1e-12, "eraPvstar", "dec", status); + vvd(pmr, -0.178323516e-4, 1e-16, "eraPvstar", "pmr", status); + vvd(pmd, 0.2336024047e-5, 1e-16, "eraPvstar", "pmd", status); + vvd(px, 0.74723, 1e-12, "eraPvstar", "px", status); + vvd(rv, -21.6, 1e-11, "eraPvstar", "rv", status); + + viv(j, 0, "eraPvstar", "j", status); + +} + +static void t_pvtob(int *status) +/* +** - - - - - - - - +** t _ p v t o b +** - - - - - - - - +** +** Test eraPvtob function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvtob, vvd +** +** This revision: 2013 October 2 +*/ +{ + double elong, phi, hm, xp, yp, sp, theta, pv[2][3]; + + + elong = 2.0; + phi = 0.5; + hm = 3000.0; + xp = 1e-6; + yp = -0.5e-6; + sp = 1e-8; + theta = 5.0; + + eraPvtob(elong, phi, hm, xp, yp, sp, theta, pv); + + vvd(pv[0][0], 4225081.367071159207, 1e-5, + "eraPvtob", "p(1)", status); + vvd(pv[0][1], 3681943.215856198144, 1e-5, + "eraPvtob", "p(2)", status); + vvd(pv[0][2], 3041149.399241260785, 1e-5, + "eraPvtob", "p(3)", status); + vvd(pv[1][0], -268.4915389365998787, 1e-9, + "eraPvtob", "v(1)", status); + vvd(pv[1][1], 308.0977983288903123, 1e-9, + "eraPvtob", "v(2)", status); + vvd(pv[1][2], 0, 0, + "eraPvtob", "v(3)", status); + +} + +static void t_pvu(int *status) +/* +** - - - - - - +** t _ p v u +** - - - - - - +** +** Test eraPvu function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvu, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3], upv[2][3]; + + + pv[0][0] = 126668.5912743160734; + pv[0][1] = 2136.792716839935565; + pv[0][2] = -245251.2339876830229; + + pv[1][0] = -0.4051854035740713039e-2; + pv[1][1] = -0.6253919754866175788e-2; + pv[1][2] = 0.1189353719774107615e-1; + + eraPvu(2920.0, pv, upv); + + vvd(upv[0][0], 126656.7598605317105, 1e-12, + "eraPvu", "p1", status); + vvd(upv[0][1], 2118.531271155726332, 1e-12, + "eraPvu", "p2", status); + vvd(upv[0][2], -245216.5048590656190, 1e-12, + "eraPvu", "p3", status); + + vvd(upv[1][0], -0.4051854035740713039e-2, 1e-12, + "eraPvu", "v1", status); + vvd(upv[1][1], -0.6253919754866175788e-2, 1e-12, + "eraPvu", "v2", status); + vvd(upv[1][2], 0.1189353719774107615e-1, 1e-12, + "eraPvu", "v3", status); + +} + +static void t_pvup(int *status) +/* +** - - - - - - - +** t _ p v u p +** - - - - - - - +** +** Test eraPvup function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvup, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3], p[3]; + + + pv[0][0] = 126668.5912743160734; + pv[0][1] = 2136.792716839935565; + pv[0][2] = -245251.2339876830229; + + pv[1][0] = -0.4051854035740713039e-2; + pv[1][1] = -0.6253919754866175788e-2; + pv[1][2] = 0.1189353719774107615e-1; + + eraPvup(2920.0, pv, p); + + vvd(p[0], 126656.7598605317105, 1e-12, "eraPvup", "1", status); + vvd(p[1], 2118.531271155726332, 1e-12, "eraPvup", "2", status); + vvd(p[2], -245216.5048590656190, 1e-12, "eraPvup", "3", status); + +} + +static void t_pvxpv(int *status) +/* +** - - - - - - - - +** t _ p v x p v +** - - - - - - - - +** +** Test eraPvxpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPvxpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[2][3], b[2][3], axb[2][3]; + + + a[0][0] = 2.0; + a[0][1] = 2.0; + a[0][2] = 3.0; + + a[1][0] = 6.0; + a[1][1] = 0.0; + a[1][2] = 4.0; + + b[0][0] = 1.0; + b[0][1] = 3.0; + b[0][2] = 4.0; + + b[1][0] = 0.0; + b[1][1] = 2.0; + b[1][2] = 8.0; + + eraPvxpv(a, b, axb); + + vvd(axb[0][0], -1.0, 1e-12, "eraPvxpv", "p1", status); + vvd(axb[0][1], -5.0, 1e-12, "eraPvxpv", "p2", status); + vvd(axb[0][2], 4.0, 1e-12, "eraPvxpv", "p3", status); + + vvd(axb[1][0], -2.0, 1e-12, "eraPvxpv", "v1", status); + vvd(axb[1][1], -36.0, 1e-12, "eraPvxpv", "v2", status); + vvd(axb[1][2], 22.0, 1e-12, "eraPvxpv", "v3", status); + +} + +static void t_pxp(int *status) +/* +** - - - - - - +** t _ p x p +** - - - - - - +** +** Test eraPxp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraPxp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3], b[3], axb[3]; + + + a[0] = 2.0; + a[1] = 2.0; + a[2] = 3.0; + + b[0] = 1.0; + b[1] = 3.0; + b[2] = 4.0; + + eraPxp(a, b, axb); + + vvd(axb[0], -1.0, 1e-12, "eraPxp", "1", status); + vvd(axb[1], -5.0, 1e-12, "eraPxp", "2", status); + vvd(axb[2], 4.0, 1e-12, "eraPxp", "3", status); + +} + +static void t_refco(int *status) +/* +** - - - - - - - - +** t _ r e f c o +** - - - - - - - - +** +** Test eraRefco function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRefco, vvd +** +** This revision: 2013 October 2 +*/ +{ + double phpa, tc, rh, wl, refa, refb; + + + phpa = 800.0; + tc = 10.0; + rh = 0.9; + wl = 0.4; + + eraRefco(phpa, tc, rh, wl, &refa, &refb); + + vvd(refa, 0.2264949956241415009e-3, 1e-15, + "eraRefco", "refa", status); + vvd(refb, -0.2598658261729343970e-6, 1e-18, + "eraRefco", "refb", status); + +} + +static void t_rm2v(int *status) +/* +** - - - - - - - +** t _ r m 2 v +** - - - - - - - +** +** Test eraRm2v function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRm2v, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3], w[3]; + + + r[0][0] = 0.00; + r[0][1] = -0.80; + r[0][2] = -0.60; + + r[1][0] = 0.80; + r[1][1] = -0.36; + r[1][2] = 0.48; + + r[2][0] = 0.60; + r[2][1] = 0.48; + r[2][2] = -0.64; + + eraRm2v(r, w); + + vvd(w[0], 0.0, 1e-12, "eraRm2v", "1", status); + vvd(w[1], 1.413716694115406957, 1e-12, "eraRm2v", "2", status); + vvd(w[2], -1.884955592153875943, 1e-12, "eraRm2v", "3", status); + +} + +static void t_rv2m(int *status) +/* +** - - - - - - - +** t _ r v 2 m +** - - - - - - - +** +** Test eraRv2m function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRv2m, vvd +** +** This revision: 2013 August 7 +*/ +{ + double w[3], r[3][3]; + + + w[0] = 0.0; + w[1] = 1.41371669; + w[2] = -1.88495559; + + eraRv2m(w, r); + + vvd(r[0][0], -0.7071067782221119905, 1e-14, "eraRv2m", "11", status); + vvd(r[0][1], -0.5656854276809129651, 1e-14, "eraRv2m", "12", status); + vvd(r[0][2], -0.4242640700104211225, 1e-14, "eraRv2m", "13", status); + + vvd(r[1][0], 0.5656854276809129651, 1e-14, "eraRv2m", "21", status); + vvd(r[1][1], -0.0925483394532274246, 1e-14, "eraRv2m", "22", status); + vvd(r[1][2], -0.8194112531408833269, 1e-14, "eraRv2m", "23", status); + + vvd(r[2][0], 0.4242640700104211225, 1e-14, "eraRv2m", "31", status); + vvd(r[2][1], -0.8194112531408833269, 1e-14, "eraRv2m", "32", status); + vvd(r[2][2], 0.3854415612311154341, 1e-14, "eraRv2m", "33", status); + +} + +static void t_rx(int *status) +/* +** - - - - - +** t _ r x +** - - - - - +** +** Test eraRx function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRx, vvd +** +** This revision: 2013 August 7 +*/ +{ + double phi, r[3][3]; + + + phi = 0.3456789; + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + eraRx(phi, r); + + vvd(r[0][0], 2.0, 0.0, "eraRx", "11", status); + vvd(r[0][1], 3.0, 0.0, "eraRx", "12", status); + vvd(r[0][2], 2.0, 0.0, "eraRx", "13", status); + + vvd(r[1][0], 3.839043388235612460, 1e-12, "eraRx", "21", status); + vvd(r[1][1], 3.237033249594111899, 1e-12, "eraRx", "22", status); + vvd(r[1][2], 4.516714379005982719, 1e-12, "eraRx", "23", status); + + vvd(r[2][0], 1.806030415924501684, 1e-12, "eraRx", "31", status); + vvd(r[2][1], 3.085711545336372503, 1e-12, "eraRx", "32", status); + vvd(r[2][2], 3.687721683977873065, 1e-12, "eraRx", "33", status); + +} + +static void t_rxp(int *status) +/* +** - - - - - - +** t _ r x p +** - - - - - - +** +** Test eraRxp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRxp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3], p[3], rp[3]; + + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + p[0] = 0.2; + p[1] = 1.5; + p[2] = 0.1; + + eraRxp(r, p, rp); + + vvd(rp[0], 5.1, 1e-12, "eraRxp", "1", status); + vvd(rp[1], 3.9, 1e-12, "eraRxp", "2", status); + vvd(rp[2], 7.1, 1e-12, "eraRxp", "3", status); + +} + +static void t_rxpv(int *status) +/* +** - - - - - - - +** t _ r x p v +** - - - - - - - +** +** Test eraRxpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRxpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3], pv[2][3], rpv[2][3]; + + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + pv[0][0] = 0.2; + pv[0][1] = 1.5; + pv[0][2] = 0.1; + + pv[1][0] = 1.5; + pv[1][1] = 0.2; + pv[1][2] = 0.1; + + eraRxpv(r, pv, rpv); + + vvd(rpv[0][0], 5.1, 1e-12, "eraRxpv", "11", status); + vvd(rpv[1][0], 3.8, 1e-12, "eraRxpv", "12", status); + + vvd(rpv[0][1], 3.9, 1e-12, "eraRxpv", "21", status); + vvd(rpv[1][1], 5.2, 1e-12, "eraRxpv", "22", status); + + vvd(rpv[0][2], 7.1, 1e-12, "eraRxpv", "31", status); + vvd(rpv[1][2], 5.8, 1e-12, "eraRxpv", "32", status); + +} + +static void t_rxr(int *status) +/* +** - - - - - - +** t _ r x r +** - - - - - - +** +** Test eraRxr function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRxr, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3][3], b[3][3], atb[3][3]; + + + a[0][0] = 2.0; + a[0][1] = 3.0; + a[0][2] = 2.0; + + a[1][0] = 3.0; + a[1][1] = 2.0; + a[1][2] = 3.0; + + a[2][0] = 3.0; + a[2][1] = 4.0; + a[2][2] = 5.0; + + b[0][0] = 1.0; + b[0][1] = 2.0; + b[0][2] = 2.0; + + b[1][0] = 4.0; + b[1][1] = 1.0; + b[1][2] = 1.0; + + b[2][0] = 3.0; + b[2][1] = 0.0; + b[2][2] = 1.0; + + eraRxr(a, b, atb); + + vvd(atb[0][0], 20.0, 1e-12, "eraRxr", "11", status); + vvd(atb[0][1], 7.0, 1e-12, "eraRxr", "12", status); + vvd(atb[0][2], 9.0, 1e-12, "eraRxr", "13", status); + + vvd(atb[1][0], 20.0, 1e-12, "eraRxr", "21", status); + vvd(atb[1][1], 8.0, 1e-12, "eraRxr", "22", status); + vvd(atb[1][2], 11.0, 1e-12, "eraRxr", "23", status); + + vvd(atb[2][0], 34.0, 1e-12, "eraRxr", "31", status); + vvd(atb[2][1], 10.0, 1e-12, "eraRxr", "32", status); + vvd(atb[2][2], 15.0, 1e-12, "eraRxr", "33", status); + +} + +static void t_ry(int *status) +/* +** - - - - - +** t _ r y +** - - - - - +** +** Test eraRy function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRy, vvd +** +** This revision: 2013 August 7 +*/ +{ + double theta, r[3][3]; + + + theta = 0.3456789; + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + eraRy(theta, r); + + vvd(r[0][0], 0.8651847818978159930, 1e-12, "eraRy", "11", status); + vvd(r[0][1], 1.467194920539316554, 1e-12, "eraRy", "12", status); + vvd(r[0][2], 0.1875137911274457342, 1e-12, "eraRy", "13", status); + + vvd(r[1][0], 3, 1e-12, "eraRy", "21", status); + vvd(r[1][1], 2, 1e-12, "eraRy", "22", status); + vvd(r[1][2], 3, 1e-12, "eraRy", "23", status); + + vvd(r[2][0], 3.500207892850427330, 1e-12, "eraRy", "31", status); + vvd(r[2][1], 4.779889022262298150, 1e-12, "eraRy", "32", status); + vvd(r[2][2], 5.381899160903798712, 1e-12, "eraRy", "33", status); + +} + +static void t_rz(int *status) +/* +** - - - - - +** t _ r z +** - - - - - +** +** Test eraRz function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraRz, vvd +** +** This revision: 2013 August 7 +*/ +{ + double psi, r[3][3]; + + + psi = 0.3456789; + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + eraRz(psi, r); + + vvd(r[0][0], 2.898197754208926769, 1e-12, "eraRz", "11", status); + vvd(r[0][1], 3.500207892850427330, 1e-12, "eraRz", "12", status); + vvd(r[0][2], 2.898197754208926769, 1e-12, "eraRz", "13", status); + + vvd(r[1][0], 2.144865911309686813, 1e-12, "eraRz", "21", status); + vvd(r[1][1], 0.865184781897815993, 1e-12, "eraRz", "22", status); + vvd(r[1][2], 2.144865911309686813, 1e-12, "eraRz", "23", status); + + vvd(r[2][0], 3.0, 1e-12, "eraRz", "31", status); + vvd(r[2][1], 4.0, 1e-12, "eraRz", "32", status); + vvd(r[2][2], 5.0, 1e-12, "eraRz", "33", status); + +} + +static void t_s00a(int *status) +/* +** - - - - - - - +** t _ s 0 0 a +** - - - - - - - +** +** Test eraS00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double s; + + + s = eraS00a(2400000.5, 52541.0); + + vvd(s, -0.1340684448919163584e-7, 1e-18, "eraS00a", "", status); + +} + +static void t_s00b(int *status) +/* +** - - - - - - - +** t _ s 0 0 b +** - - - - - - - +** +** Test eraS00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double s; + + + s = eraS00b(2400000.5, 52541.0); + + vvd(s, -0.1340695782951026584e-7, 1e-18, "eraS00b", "", status); + +} + +static void t_s00(int *status) +/* +** - - - - - - +** t _ s 0 0 +** - - - - - - +** +** Test eraS00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS00, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y, s; + + + x = 0.5791308486706011000e-3; + y = 0.4020579816732961219e-4; + + s = eraS00(2400000.5, 53736.0, x, y); + + vvd(s, -0.1220036263270905693e-7, 1e-18, "eraS00", "", status); + +} + +static void t_s06a(int *status) +/* +** - - - - - - - +** t _ s 0 6 a +** - - - - - - - +** +** Test eraS06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double s; + + + s = eraS06a(2400000.5, 52541.0); + + vvd(s, -0.1340680437291812383e-7, 1e-18, "eraS06a", "", status); + +} + +static void t_s06(int *status) +/* +** - - - - - - +** t _ s 0 6 +** - - - - - - +** +** Test eraS06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y, s; + + + x = 0.5791308486706011000e-3; + y = 0.4020579816732961219e-4; + + s = eraS06(2400000.5, 53736.0, x, y); + + vvd(s, -0.1220032213076463117e-7, 1e-18, "eraS06", "", status); + +} + +static void t_s2c(int *status) +/* +** - - - - - - +** t _ s 2 c +** - - - - - - +** +** Test eraS2c function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS2c, vvd +** +** This revision: 2013 August 7 +*/ +{ + double c[3]; + + + eraS2c(3.0123, -0.999, c); + + vvd(c[0], -0.5366267667260523906, 1e-12, "eraS2c", "1", status); + vvd(c[1], 0.0697711109765145365, 1e-12, "eraS2c", "2", status); + vvd(c[2], -0.8409302618566214041, 1e-12, "eraS2c", "3", status); + +} + +static void t_s2p(int *status) +/* +** - - - - - - +** t _ s 2 p +** - - - - - - +** +** Test eraS2p function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS2p, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3]; + + + eraS2p(-3.21, 0.123, 0.456, p); + + vvd(p[0], -0.4514964673880165228, 1e-12, "eraS2p", "x", status); + vvd(p[1], 0.0309339427734258688, 1e-12, "eraS2p", "y", status); + vvd(p[2], 0.0559466810510877933, 1e-12, "eraS2p", "z", status); + +} + +static void t_s2pv(int *status) +/* +** - - - - - - - +** t _ s 2 p v +** - - - - - - - +** +** Test eraS2pv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS2pv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3]; + + + eraS2pv(-3.21, 0.123, 0.456, -7.8e-6, 9.01e-6, -1.23e-5, pv); + + vvd(pv[0][0], -0.4514964673880165228, 1e-12, "eraS2pv", "x", status); + vvd(pv[0][1], 0.0309339427734258688, 1e-12, "eraS2pv", "y", status); + vvd(pv[0][2], 0.0559466810510877933, 1e-12, "eraS2pv", "z", status); + + vvd(pv[1][0], 0.1292270850663260170e-4, 1e-16, + "eraS2pv", "vx", status); + vvd(pv[1][1], 0.2652814182060691422e-5, 1e-16, + "eraS2pv", "vy", status); + vvd(pv[1][2], 0.2568431853930292259e-5, 1e-16, + "eraS2pv", "vz", status); + +} + +static void t_s2xpv(int *status) +/* +** - - - - - - - - +** t _ s 2 x p v +** - - - - - - - - +** +** Test eraS2xpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraS2xpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double s1, s2, pv[2][3], spv[2][3]; + + + s1 = 2.0; + s2 = 3.0; + + pv[0][0] = 0.3; + pv[0][1] = 1.2; + pv[0][2] = -2.5; + + pv[1][0] = 0.5; + pv[1][1] = 2.3; + pv[1][2] = -0.4; + + eraS2xpv(s1, s2, pv, spv); + + vvd(spv[0][0], 0.6, 1e-12, "eraS2xpv", "p1", status); + vvd(spv[0][1], 2.4, 1e-12, "eraS2xpv", "p2", status); + vvd(spv[0][2], -5.0, 1e-12, "eraS2xpv", "p3", status); + + vvd(spv[1][0], 1.5, 1e-12, "eraS2xpv", "v1", status); + vvd(spv[1][1], 6.9, 1e-12, "eraS2xpv", "v2", status); + vvd(spv[1][2], -1.2, 1e-12, "eraS2xpv", "v3", status); + +} + +static void t_sepp(int *status) +/* +** - - - - - - - +** t _ s e p p +** - - - - - - - +** +** Test eraSepp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraSepp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double a[3], b[3], s; + + + a[0] = 1.0; + a[1] = 0.1; + a[2] = 0.2; + + b[0] = -3.0; + b[1] = 1e-3; + b[2] = 0.2; + + s = eraSepp(a, b); + + vvd(s, 2.860391919024660768, 1e-12, "eraSepp", "", status); + +} + +static void t_seps(int *status) +/* +** - - - - - - - +** t _ s e p s +** - - - - - - - +** +** Test eraSeps function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraSeps, vvd +** +** This revision: 2013 August 7 +*/ +{ + double al, ap, bl, bp, s; + + + al = 1.0; + ap = 0.1; + + bl = 0.2; + bp = -3.0; + + s = eraSeps(al, ap, bl, bp); + + vvd(s, 2.346722016996998842, 1e-14, "eraSeps", "", status); + +} + +static void t_sp00(int *status) +/* +** - - - - - - - +** t _ s p 0 0 +** - - - - - - - +** +** Test eraSp00 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraSp00, vvd +** +** This revision: 2013 August 7 +*/ +{ + vvd(eraSp00(2400000.5, 52541.0), + -0.6216698469981019309e-11, 1e-12, "eraSp00", "", status); + +} + +static void t_starpm(int *status) +/* +** - - - - - - - - - +** t _ s t a r p m +** - - - - - - - - - +** +** Test eraStarpm function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraStarpm, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double ra1, dec1, pmr1, pmd1, px1, rv1; + double ra2, dec2, pmr2, pmd2, px2, rv2; + int j; + + + ra1 = 0.01686756; + dec1 = -1.093989828; + pmr1 = -1.78323516e-5; + pmd1 = 2.336024047e-6; + px1 = 0.74723; + rv1 = -21.6; + + j = eraStarpm(ra1, dec1, pmr1, pmd1, px1, rv1, + 2400000.5, 50083.0, 2400000.5, 53736.0, + &ra2, &dec2, &pmr2, &pmd2, &px2, &rv2); + + vvd(ra2, 0.01668919069414242368, 1e-13, + "eraStarpm", "ra", status); + vvd(dec2, -1.093966454217127879, 1e-13, + "eraStarpm", "dec", status); + vvd(pmr2, -0.1783662682155932702e-4, 1e-17, + "eraStarpm", "pmr", status); + vvd(pmd2, 0.2338092915987603664e-5, 1e-17, + "eraStarpm", "pmd", status); + vvd(px2, 0.7473533835323493644, 1e-13, + "eraStarpm", "px", status); + vvd(rv2, -21.59905170476860786, 1e-11, + "eraStarpm", "rv", status); + + viv(j, 0, "eraStarpm", "j", status); + +} + +static void t_starpv(int *status) +/* +** - - - - - - - - - +** t _ s t a r p v +** - - - - - - - - - +** +** Test eraStarpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraStarpv, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double ra, dec, pmr, pmd, px, rv, pv[2][3]; + int j; + + + ra = 0.01686756; + dec = -1.093989828; + pmr = -1.78323516e-5; + pmd = 2.336024047e-6; + px = 0.74723; + rv = -21.6; + + j = eraStarpv(ra, dec, pmr, pmd, px, rv, pv); + + vvd(pv[0][0], 126668.5912743160601, 1e-10, + "eraStarpv", "11", status); + vvd(pv[0][1], 2136.792716839935195, 1e-12, + "eraStarpv", "12", status); + vvd(pv[0][2], -245251.2339876830091, 1e-10, + "eraStarpv", "13", status); + + vvd(pv[1][0], -0.4051854035740712739e-2, 1e-13, + "eraStarpv", "21", status); + vvd(pv[1][1], -0.6253919754866173866e-2, 1e-15, + "eraStarpv", "22", status); + vvd(pv[1][2], 0.1189353719774107189e-1, 1e-13, + "eraStarpv", "23", status); + + viv(j, 0, "eraStarpv", "j", status); + +} + +static void t_sxp(int *status) +/* +** - - - - - - +** t _ s x p +** - - - - - - +** +** Test eraSxp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraSxp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double s, p[3], sp[3]; + + + s = 2.0; + + p[0] = 0.3; + p[1] = 1.2; + p[2] = -2.5; + + eraSxp(s, p, sp); + + vvd(sp[0], 0.6, 0.0, "eraSxp", "1", status); + vvd(sp[1], 2.4, 0.0, "eraSxp", "2", status); + vvd(sp[2], -5.0, 0.0, "eraSxp", "3", status); + +} + + +static void t_sxpv(int *status) +/* +** - - - - - - - +** t _ s x p v +** - - - - - - - +** +** Test eraSxpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraSxpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double s, pv[2][3], spv[2][3]; + + + s = 2.0; + + pv[0][0] = 0.3; + pv[0][1] = 1.2; + pv[0][2] = -2.5; + + pv[1][0] = 0.5; + pv[1][1] = 3.2; + pv[1][2] = -0.7; + + eraSxpv(s, pv, spv); + + vvd(spv[0][0], 0.6, 0.0, "eraSxpv", "p1", status); + vvd(spv[0][1], 2.4, 0.0, "eraSxpv", "p2", status); + vvd(spv[0][2], -5.0, 0.0, "eraSxpv", "p3", status); + + vvd(spv[1][0], 1.0, 0.0, "eraSxpv", "v1", status); + vvd(spv[1][1], 6.4, 0.0, "eraSxpv", "v2", status); + vvd(spv[1][2], -1.4, 0.0, "eraSxpv", "v3", status); + +} + +static void t_taitt(int *status) +/* +** - - - - - - - - +** t _ t a i t t +** - - - - - - - - +** +** Test eraTaitt function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTaitt, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double t1, t2; + int j; + + + j = eraTaitt(2453750.5, 0.892482639, &t1, &t2); + + vvd(t1, 2453750.5, 1e-6, "eraTaitt", "t1", status); + vvd(t2, 0.892855139, 1e-12, "eraTaitt", "t2", status); + viv(j, 0, "eraTaitt", "j", status); + +} + +static void t_taiut1(int *status) +/* +** - - - - - - - - - +** t _ t a i u t 1 +** - - - - - - - - - +** +** Test eraTaiut1 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTaiut1, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double u1, u2; + int j; + + + j = eraTaiut1(2453750.5, 0.892482639, -32.6659, &u1, &u2); + + vvd(u1, 2453750.5, 1e-6, "eraTaiut1", "u1", status); + vvd(u2, 0.8921045614537037037, 1e-12, "eraTaiut1", "u2", status); + viv(j, 0, "eraTaiut1", "j", status); + +} + +static void t_taiutc(int *status) +/* +** - - - - - - - - - +** t _ t a i u t c +** - - - - - - - - - +** +** Test eraTaiutc function. +** +** Returned: +** status LOGICAL TRUE = success, FALSE = fail +** +** Called: eraTaiutc, vvd, viv +** +** This revision: 2013 October 3 +*/ +{ + double u1, u2; + int j; + + + j = eraTaiutc(2453750.5, 0.892482639, &u1, &u2); + + vvd(u1, 2453750.5, 1e-6, "eraTaiutc", "u1", status); + vvd(u2, 0.8921006945555555556, 1e-12, "eraTaiutc", "u2", status); + viv(j, 0, "eraTaiutc", "j", status); + +} + +static void t_tcbtdb(int *status) +/* +** - - - - - - - - - +** t _ t c b t d b +** - - - - - - - - - +** +** Test eraTcbtdb function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTcbtdb, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double b1, b2; + int j; + + + j = eraTcbtdb(2453750.5, 0.893019599, &b1, &b2); + + vvd(b1, 2453750.5, 1e-6, "eraTcbtdb", "b1", status); + vvd(b2, 0.8928551362746343397, 1e-12, "eraTcbtdb", "b2", status); + viv(j, 0, "eraTcbtdb", "j", status); + +} + +static void t_tcgtt(int *status) +/* +** - - - - - - - - +** t _ t c g t t +** - - - - - - - - +** +** Test eraTcgtt function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTcgtt, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double t1, t2; + int j; + + + j = eraTcgtt(2453750.5, 0.892862531, &t1, &t2); + + vvd(t1, 2453750.5, 1e-6, "eraTcgtt", "t1", status); + vvd(t2, 0.8928551387488816828, 1e-12, "eraTcgtt", "t2", status); + viv(j, 0, "eraTcgtt", "j", status); + +} + +static void t_tdbtcb(int *status) +/* +** - - - - - - - - - +** t _ t d b t c b +** - - - - - - - - - +** +** Test eraTdbtcb function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTdbtcb, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double b1, b2; + int j; + + + j = eraTdbtcb(2453750.5, 0.892855137, &b1, &b2); + + vvd( b1, 2453750.5, 1e-6, "eraTdbtcb", "b1", status); + vvd( b2, 0.8930195997253656716, 1e-12, "eraTdbtcb", "b2", status); + viv(j, 0, "eraTdbtcb", "j", status); + +} + +static void t_tdbtt(int *status) +/* +** - - - - - - - - +** t _ t d b t t +** - - - - - - - - +** +** Test eraTdbtt function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTdbtt, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double t1, t2; + int j; + + + j = eraTdbtt(2453750.5, 0.892855137, -0.000201, &t1, &t2); + + vvd(t1, 2453750.5, 1e-6, "eraTdbtt", "t1", status); + vvd(t2, 0.8928551393263888889, 1e-12, "eraTdbtt", "t2", status); + viv(j, 0, "eraTdbtt", "j", status); + +} + +static void t_tf2a(int *status) +/* +** - - - - - - - +** t _ t f 2 a +** - - - - - - - +** +** Test eraTf2a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTf2a, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double a; + int j; + + + j = eraTf2a('+', 4, 58, 20.2, &a); + + vvd(a, 1.301739278189537429, 1e-12, "eraTf2a", "a", status); + viv(j, 0, "eraTf2a", "j", status); + +} + +static void t_tf2d(int *status) +/* +** - - - - - - - +** t _ t f 2 d +** - - - - - - - +** +** Test eraTf2d function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTf2d, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double d; + int j; + + + j = eraTf2d(' ', 23, 55, 10.9, &d); + + vvd(d, 0.9966539351851851852, 1e-12, "eraTf2d", "d", status); + viv(j, 0, "eraTf2d", "j", status); + +} + +static void t_tr(int *status) +/* +** - - - - - +** t _ t r +** - - - - - +** +** Test eraTr function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTr, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3], rt[3][3]; + + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + eraTr(r, rt); + + vvd(rt[0][0], 2.0, 0.0, "eraTr", "11", status); + vvd(rt[0][1], 3.0, 0.0, "eraTr", "12", status); + vvd(rt[0][2], 3.0, 0.0, "eraTr", "13", status); + + vvd(rt[1][0], 3.0, 0.0, "eraTr", "21", status); + vvd(rt[1][1], 2.0, 0.0, "eraTr", "22", status); + vvd(rt[1][2], 4.0, 0.0, "eraTr", "23", status); + + vvd(rt[2][0], 2.0, 0.0, "eraTr", "31", status); + vvd(rt[2][1], 3.0, 0.0, "eraTr", "32", status); + vvd(rt[2][2], 5.0, 0.0, "eraTr", "33", status); + +} + +static void t_trxp(int *status) +/* +** - - - - - - - +** t _ t r x p +** - - - - - - - +** +** Test eraTrxp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTrxp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3], p[3], trp[3]; + + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + p[0] = 0.2; + p[1] = 1.5; + p[2] = 0.1; + + eraTrxp(r, p, trp); + + vvd(trp[0], 5.2, 1e-12, "eraTrxp", "1", status); + vvd(trp[1], 4.0, 1e-12, "eraTrxp", "2", status); + vvd(trp[2], 5.4, 1e-12, "eraTrxp", "3", status); + +} + +static void t_trxpv(int *status) +/* +** - - - - - - - - +** t _ t r x p v +** - - - - - - - - +** +** Test eraTrxpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTrxpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3], pv[2][3], trpv[2][3]; + + + r[0][0] = 2.0; + r[0][1] = 3.0; + r[0][2] = 2.0; + + r[1][0] = 3.0; + r[1][1] = 2.0; + r[1][2] = 3.0; + + r[2][0] = 3.0; + r[2][1] = 4.0; + r[2][2] = 5.0; + + pv[0][0] = 0.2; + pv[0][1] = 1.5; + pv[0][2] = 0.1; + + pv[1][0] = 1.5; + pv[1][1] = 0.2; + pv[1][2] = 0.1; + + eraTrxpv(r, pv, trpv); + + vvd(trpv[0][0], 5.2, 1e-12, "eraTrxpv", "p1", status); + vvd(trpv[0][1], 4.0, 1e-12, "eraTrxpv", "p1", status); + vvd(trpv[0][2], 5.4, 1e-12, "eraTrxpv", "p1", status); + + vvd(trpv[1][0], 3.9, 1e-12, "eraTrxpv", "v1", status); + vvd(trpv[1][1], 5.3, 1e-12, "eraTrxpv", "v2", status); + vvd(trpv[1][2], 4.1, 1e-12, "eraTrxpv", "v3", status); + +} + +static void t_tttai(int *status) +/* +** - - - - - - - - +** t _ t t t a i +** - - - - - - - - +** +** Test eraTttai function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTttai, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double a1, a2; + int j; + + + j = eraTttai(2453750.5, 0.892482639, &a1, &a2); + + vvd(a1, 2453750.5, 1e-6, "eraTttai", "a1", status); + vvd(a2, 0.892110139, 1e-12, "eraTttai", "a2", status); + viv(j, 0, "eraTttai", "j", status); + +} + +static void t_tttcg(int *status) +/* +** - - - - - - - - +** t _ t t t c g +** - - - - - - - - +** +** Test eraTttcg function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTttcg, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double g1, g2; + int j; + + + j = eraTttcg(2453750.5, 0.892482639, &g1, &g2); + + vvd( g1, 2453750.5, 1e-6, "eraTttcg", "g1", status); + vvd( g2, 0.8924900312508587113, 1e-12, "eraTttcg", "g2", status); + viv(j, 0, "eraTttcg", "j", status); + +} + +static void t_tttdb(int *status) +/* +** - - - - - - - - +** t _ t t t d b +** - - - - - - - - +** +** Test eraTttdb function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTttdb, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double b1, b2; + int j; + + + j = eraTttdb(2453750.5, 0.892855139, -0.000201, &b1, &b2); + + vvd(b1, 2453750.5, 1e-6, "eraTttdb", "b1", status); + vvd(b2, 0.8928551366736111111, 1e-12, "eraTttdb", "b2", status); + viv(j, 0, "eraTttdb", "j", status); + +} + +static void t_ttut1(int *status) +/* +** - - - - - - - - +** t _ t t u t 1 +** - - - - - - - - +** +** Test eraTtut1 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraTtut1, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double u1, u2; + int j; + + + j = eraTtut1(2453750.5, 0.892855139, 64.8499, &u1, &u2); + + vvd(u1, 2453750.5, 1e-6, "eraTtut1", "u1", status); + vvd(u2, 0.8921045614537037037, 1e-12, "eraTtut1", "u2", status); + viv(j, 0, "eraTtut1", "j", status); + +} + +static void t_ut1tai(int *status) +/* +** - - - - - - - - - +** t _ u t 1 t a i +** - - - - - - - - - +** +** Test eraUt1tai function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraUt1tai, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double a1, a2; + int j; + + + j = eraUt1tai(2453750.5, 0.892104561, -32.6659, &a1, &a2); + + vvd(a1, 2453750.5, 1e-6, "eraUt1tai", "a1", status); + vvd(a2, 0.8924826385462962963, 1e-12, "eraUt1tai", "a2", status); + viv(j, 0, "eraUt1tai", "j", status); + +} + +static void t_ut1tt(int *status) +/* +** - - - - - - - - +** t _ u t 1 t t +** - - - - - - - - +** +** Test eraUt1tt function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraUt1tt, vvd, viv +** +** This revision: 2013 October 3 +*/ +{ + double t1, t2; + int j; + + + j = eraUt1tt(2453750.5, 0.892104561, 64.8499, &t1, &t2); + + vvd(t1, 2453750.5, 1e-6, "eraUt1tt", "t1", status); + vvd(t2, 0.8928551385462962963, 1e-12, "eraUt1tt", "t2", status); + viv(j, 0, "eraUt1tt", "j", status); + +} + +static void t_ut1utc(int *status) +/* +** - - - - - - - - - +** t _ u t 1 u t c +** - - - - - - - - - +** +** Test eraUt1utc function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraUt1utc, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double u1, u2; + int j; + + + j = eraUt1utc(2453750.5, 0.892104561, 0.3341, &u1, &u2); + + vvd(u1, 2453750.5, 1e-6, "eraUt1utc", "u1", status); + vvd(u2, 0.8921006941018518519, 1e-12, "eraUt1utc", "u2", status); + viv(j, 0, "eraUt1utc", "j", status); + +} + +static void t_utctai(int *status) +/* +** - - - - - - - - - +** t _ u t c t a i +** - - - - - - - - - +** +** Test eraUtctai function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraUtctai, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double u1, u2; + int j; + + + j = eraUtctai(2453750.5, 0.892100694, &u1, &u2); + + vvd(u1, 2453750.5, 1e-6, "eraUtctai", "u1", status); + vvd(u2, 0.8924826384444444444, 1e-12, "eraUtctai", "u2", status); + viv(j, 0, "eraUtctai", "j", status); + +} + +static void t_utcut1(int *status) +/* +** - - - - - - - - - +** t _ u t c u t 1 +** - - - - - - - - - +** +** Test eraUtcut1 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraUtcut1, vvd, viv +** +** This revision: 2013 August 7 +*/ +{ + double u1, u2; + int j; + + + j = eraUtcut1(2453750.5, 0.892100694, 0.3341, &u1, &u2); + + vvd(u1, 2453750.5, 1e-6, "eraUtcut1", "u1", status); + vvd(u2, 0.8921045608981481481, 1e-12, "eraUtcut1", "u2", status); + viv(j, 0, "eraUtcut1", "j", status); + +} + +static void t_xy06(int *status) +/* +** - - - - - - - +** t _ x y 0 6 +** - - - - - - - +** +** Test eraXy06 function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraXy06, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y; + + + eraXy06(2400000.5, 53736.0, &x, &y); + + vvd(x, 0.5791308486706010975e-3, 1e-15, "eraXy06", "x", status); + vvd(y, 0.4020579816732958141e-4, 1e-16, "eraXy06", "y", status); + +} + +static void t_xys00a(int *status) +/* +** - - - - - - - - - +** t _ x y s 0 0 a +** - - - - - - - - - +** +** Test eraXys00a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraXys00a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y, s; + + + eraXys00a(2400000.5, 53736.0, &x, &y, &s); + + vvd(x, 0.5791308472168152904e-3, 1e-14, "eraXys00a", "x", status); + vvd(y, 0.4020595661591500259e-4, 1e-15, "eraXys00a", "y", status); + vvd(s, -0.1220040848471549623e-7, 1e-18, "eraXys00a", "s", status); + +} + +static void t_xys00b(int *status) +/* +** - - - - - - - - - +** t _ x y s 0 0 b +** - - - - - - - - - +** +** Test eraXys00b function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraXys00b, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y, s; + + + eraXys00b(2400000.5, 53736.0, &x, &y, &s); + + vvd(x, 0.5791301929950208873e-3, 1e-14, "eraXys00b", "x", status); + vvd(y, 0.4020553681373720832e-4, 1e-15, "eraXys00b", "y", status); + vvd(s, -0.1220027377285083189e-7, 1e-18, "eraXys00b", "s", status); + +} + +static void t_xys06a(int *status) +/* +** - - - - - - - - - +** t _ x y s 0 6 a +** - - - - - - - - - +** +** Test eraXys06a function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraXys06a, vvd +** +** This revision: 2013 August 7 +*/ +{ + double x, y, s; + + + eraXys06a(2400000.5, 53736.0, &x, &y, &s); + + vvd(x, 0.5791308482835292617e-3, 1e-14, "eraXys06a", "x", status); + vvd(y, 0.4020580099454020310e-4, 1e-15, "eraXys06a", "y", status); + vvd(s, -0.1220032294164579896e-7, 1e-18, "eraXys06a", "s", status); + +} + +static void t_zp(int *status) +/* +** - - - - - +** t _ z p +** - - - - - +** +** Test eraZp function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraZp, vvd +** +** This revision: 2013 August 7 +*/ +{ + double p[3]; + + + p[0] = 0.3; + p[1] = 1.2; + p[2] = -2.5; + + eraZp(p); + + vvd(p[0], 0.0, 0.0, "eraZp", "1", status); + vvd(p[1], 0.0, 0.0, "eraZp", "2", status); + vvd(p[2], 0.0, 0.0, "eraZp", "3", status); + +} + +static void t_zpv(int *status) +/* +** - - - - - - +** t _ z p v +** - - - - - - +** +** Test eraZpv function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraZpv, vvd +** +** This revision: 2013 August 7 +*/ +{ + double pv[2][3]; + + + pv[0][0] = 0.3; + pv[0][1] = 1.2; + pv[0][2] = -2.5; + + pv[1][0] = -0.5; + pv[1][1] = 3.1; + pv[1][2] = 0.9; + + eraZpv(pv); + + vvd(pv[0][0], 0.0, 0.0, "eraZpv", "p1", status); + vvd(pv[0][1], 0.0, 0.0, "eraZpv", "p2", status); + vvd(pv[0][2], 0.0, 0.0, "eraZpv", "p3", status); + + vvd(pv[1][0], 0.0, 0.0, "eraZpv", "v1", status); + vvd(pv[1][1], 0.0, 0.0, "eraZpv", "v2", status); + vvd(pv[1][2], 0.0, 0.0, "eraZpv", "v3", status); + +} + +static void t_zr(int *status) +/* +** - - - - - +** t _ z r +** - - - - - +** +** Test eraZr function. +** +** Returned: +** status int FALSE = success, TRUE = fail +** +** Called: eraZr, vvd +** +** This revision: 2013 August 7 +*/ +{ + double r[3][3]; + + + r[0][0] = 2.0; + r[1][0] = 3.0; + r[2][0] = 2.0; + + r[0][1] = 3.0; + r[1][1] = 2.0; + r[2][1] = 3.0; + + r[0][2] = 3.0; + r[1][2] = 4.0; + r[2][2] = 5.0; + + eraZr(r); + + vvd(r[0][0], 0.0, 0.0, "eraZr", "00", status); + vvd(r[1][0], 0.0, 0.0, "eraZr", "01", status); + vvd(r[2][0], 0.0, 0.0, "eraZr", "02", status); + + vvd(r[0][1], 0.0, 0.0, "eraZr", "10", status); + vvd(r[1][1], 0.0, 0.0, "eraZr", "11", status); + vvd(r[2][1], 0.0, 0.0, "eraZr", "12", status); + + vvd(r[0][2], 0.0, 0.0, "eraZr", "20", status); + vvd(r[1][2], 0.0, 0.0, "eraZr", "21", status); + vvd(r[2][2], 0.0, 0.0, "eraZr", "22", status); + +} + +int main(int argc, char *argv[]) +/* +** - - - - - +** m a i n +** - - - - - +** +** This revision: 2013 October 3 +*/ +{ + int status; + + +/* If any command-line argument, switch to verbose reporting. */ + if (argc > 1) { + verbose = 1; + argv[0][0] += 0; /* to avoid compiler warnings */ + } + +/* Preset the &status to FALSE = success. */ + status = 0; + +/* Test all of the ERFA functions. */ + t_a2af(&status); + t_a2tf(&status); + t_ab(&status); + t_af2a(&status); + t_anp(&status); + t_anpm(&status); + t_apcg(&status); + t_apcg13(&status); + t_apci(&status); + t_apci13(&status); + t_apco(&status); + t_apco13(&status); + t_apcs(&status); + t_apcs13(&status); + t_aper(&status); + t_aper13(&status); + t_apio(&status); + t_apio13(&status); + t_atci13(&status); + t_atciq(&status); + t_atciqn(&status); + t_atciqz(&status); + t_atco13(&status); + t_atic13(&status); + t_aticq(&status); + t_aticqn(&status); + t_atio13(&status); + t_atioq(&status); + t_atoc13(&status); + t_atoi13(&status); + t_atoiq(&status); + t_bi00(&status); + t_bp00(&status); + t_bp06(&status); + t_bpn2xy(&status); + t_c2i00a(&status); + t_c2i00b(&status); + t_c2i06a(&status); + t_c2ibpn(&status); + t_c2ixy(&status); + t_c2ixys(&status); + t_c2s(&status); + t_c2t00a(&status); + t_c2t00b(&status); + t_c2t06a(&status); + t_c2tcio(&status); + t_c2teqx(&status); + t_c2tpe(&status); + t_c2txy(&status); + t_cal2jd(&status); + t_cp(&status); + t_cpv(&status); + t_cr(&status); + t_d2dtf(&status); + t_d2tf(&status); + t_dat(&status); + t_dtdb(&status); + t_dtf2d(&status); + t_ee00(&status); + t_ee00a(&status); + t_ee00b(&status); + t_ee06a(&status); + t_eect00(&status); + t_eform(&status); + t_eo06a(&status); + t_eors(&status); + t_epb(&status); + t_epb2jd(&status); + t_epj(&status); + t_epj2jd(&status); + t_epv00(&status); + t_eqeq94(&status); + t_era00(&status); + t_fad03(&status); + t_fae03(&status); + t_faf03(&status); + t_faju03(&status); + t_fal03(&status); + t_falp03(&status); + t_fama03(&status); + t_fame03(&status); + t_fane03(&status); + t_faom03(&status); + t_fapa03(&status); + t_fasa03(&status); + t_faur03(&status); + t_fave03(&status); + t_fk52h(&status); + t_fk5hip(&status); + t_fk5hz(&status); + t_fw2m(&status); + t_fw2xy(&status); + t_gc2gd(&status); + t_gc2gde(&status); + t_gd2gc(&status); + t_gd2gce(&status); + t_gmst00(&status); + t_gmst06(&status); + t_gmst82(&status); + t_gst00a(&status); + t_gst00b(&status); + t_gst06(&status); + t_gst06a(&status); + t_gst94(&status); + t_h2fk5(&status); + t_hfk5z(&status); + t_ir(&status); + t_jd2cal(&status); + t_jdcalf(&status); + t_ld(&status); + t_ldn(&status); + t_ldsun(&status); + t_num00a(&status); + t_num00b(&status); + t_num06a(&status); + t_numat(&status); + t_nut00a(&status); + t_nut00b(&status); + t_nut06a(&status); + t_nut80(&status); + t_nutm80(&status); + t_obl06(&status); + t_obl80(&status); + t_p06e(&status); + t_p2pv(&status); + t_p2s(&status); + t_pap(&status); + t_pas(&status); + t_pb06(&status); + t_pdp(&status); + t_pfw06(&status); + t_plan94(&status); + t_pmat00(&status); + t_pmat06(&status); + t_pmat76(&status); + t_pm(&status); + t_pmp(&status); + t_pmpx(&status); + t_pmsafe(&status); + t_pn(&status); + t_pn00(&status); + t_pn00a(&status); + t_pn00b(&status); + t_pn06a(&status); + t_pn06(&status); + t_pnm00a(&status); + t_pnm00b(&status); + t_pnm06a(&status); + t_pnm80(&status); + t_pom00(&status); + t_ppp(&status); + t_ppsp(&status); + t_pr00(&status); + t_prec76(&status); + t_pv2p(&status); + t_pv2s(&status); + t_pvdpv(&status); + t_pvm(&status); + t_pvmpv(&status); + t_pvppv(&status); + t_pvstar(&status); + t_pvtob(&status); + t_pvu(&status); + t_pvup(&status); + t_pvxpv(&status); + t_pxp(&status); + t_refco(&status); + t_rm2v(&status); + t_rv2m(&status); + t_rx(&status); + t_rxp(&status); + t_rxpv(&status); + t_rxr(&status); + t_ry(&status); + t_rz(&status); + t_s00a(&status); + t_s00b(&status); + t_s00(&status); + t_s06a(&status); + t_s06(&status); + t_s2c(&status); + t_s2p(&status); + t_s2pv(&status); + t_s2xpv(&status); + t_sepp(&status); + t_seps(&status); + t_sp00(&status); + t_starpm(&status); + t_starpv(&status); + t_sxp(&status); + t_sxpv(&status); + t_taitt(&status); + t_taiut1(&status); + t_taiutc(&status); + t_tcbtdb(&status); + t_tcgtt(&status); + t_tdbtcb(&status); + t_tdbtt(&status); + t_tf2a(&status); + t_tf2d(&status); + t_tr(&status); + t_trxp(&status); + t_trxpv(&status); + t_tttai(&status); + t_tttcg(&status); + t_tttdb(&status); + t_ttut1(&status); + t_ut1tai(&status); + t_ut1tt(&status) ; + t_ut1utc(&status); + t_utctai(&status); + t_utcut1(&status); + t_xy06(&status); + t_xys00a(&status); + t_xys00b(&status); + t_xys06a(&status); + t_zp(&status); + t_zpv(&status); + t_zr(&status); + +/* Report, set up an appropriate exit status, and finish. */ + if (status) { + printf("t_erfa_c validation failed!\n"); + } else { + printf("t_erfa_c validation successful\n"); + } + return status; +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/taitt.c b/erfa/taitt.c new file mode 100644 index 0000000..fc0e1b6 --- /dev/null +++ b/erfa/taitt.c @@ -0,0 +1,119 @@ +#include "erfa.h" + +int eraTaitt(double tai1, double tai2, double *tt1, double *tt2) +/* +** - - - - - - - - - +** e r a T a i t t +** - - - - - - - - - +** +** Time scale transformation: International Atomic Time, TAI, to +** Terrestrial Time, TT. +** +** Given: +** tai1,tai2 double TAI as a 2-part Julian Date +** +** Returned: +** tt1,tt2 double TT as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Note: +** +** tai1+tai2 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where tai1 is the Julian +** Day Number and tai2 is the fraction of a day. The returned +** tt1,tt2 follow suit. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* TT minus TAI (days). */ + static const double dtat = ERFA_TTMTAI/ERFA_DAYSEC; + + +/* Result, safeguarding precision. */ + if ( tai1 > tai2 ) { + *tt1 = tai1; + *tt2 = tai2 + dtat; + } else { + *tt1 = tai1 + dtat; + *tt2 = tai2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/taiut1.c b/erfa/taiut1.c new file mode 100644 index 0000000..45fbaff --- /dev/null +++ b/erfa/taiut1.c @@ -0,0 +1,120 @@ +#include "erfa.h" + +int eraTaiut1(double tai1, double tai2, double dta, + double *ut11, double *ut12) +/* +** - - - - - - - - - - +** e r a T a i u t 1 +** - - - - - - - - - - +** +** Time scale transformation: International Atomic Time, TAI, to +** Universal Time, UT1. +** +** Given: +** tai1,tai2 double TAI as a 2-part Julian Date +** dta double UT1-TAI in seconds +** +** Returned: +** ut11,ut12 double UT1 as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) tai1+tai2 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where tai1 is the Julian +** Day Number and tai2 is the fraction of a day. The returned +** UT11,UT12 follow suit. +** +** 2) The argument dta, i.e. UT1-TAI, is an observed quantity, and is +** available from IERS tabulations. +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dtad; + + +/* Result, safeguarding precision. */ + dtad = dta / ERFA_DAYSEC; + if ( tai1 > tai2 ) { + *ut11 = tai1; + *ut12 = tai2 + dtad; + } else { + *ut11 = tai1 + dtad; + *ut12 = tai2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/taiutc.c b/erfa/taiutc.c new file mode 100644 index 0000000..c9f6958 --- /dev/null +++ b/erfa/taiutc.c @@ -0,0 +1,168 @@ +#include "erfa.h" + +int eraTaiutc(double tai1, double tai2, double *utc1, double *utc2) +/* +** - - - - - - - - - - +** e r a T a i u t c +** - - - - - - - - - - +** +** Time scale transformation: International Atomic Time, TAI, to +** Coordinated Universal Time, UTC. +** +** Given: +** tai1,tai2 double TAI as a 2-part Julian Date (Note 1) +** +** Returned: +** utc1,utc2 double UTC as a 2-part quasi Julian Date (Notes 1-3) +** +** Returned (function value): +** int status: +1 = dubious year (Note 4) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) tai1+tai2 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where tai1 is the Julian +** Day Number and tai2 is the fraction of a day. The returned utc1 +** and utc2 form an analogous pair, except that a special convention +** is used, to deal with the problem of leap seconds - see the next +** note. +** +** 2) JD cannot unambiguously represent UTC during a leap second unless +** special measures are taken. The convention in the present +** function is that the JD day represents UTC days whether the +** length is 86399, 86400 or 86401 SI seconds. In the 1960-1972 era +** there were smaller jumps (in either direction) each time the +** linear UTC(TAI) expression was changed, and these "mini-leaps" +** are also included in the ERFA convention. +** +** 3) The function eraD2dtf can be used to transform the UTC quasi-JD +** into calendar date and clock time, including UTC leap second +** handling. +** +** 4) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the future +** to be trusted. See eraDat for further details. +** +** Called: +** eraUtctai UTC to TAI +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int big1; + int i, j; + double a1, a2, u1, u2, g1, g2; + + +/* Put the two parts of the TAI into big-first order. */ + big1 = ( tai1 >= tai2 ); + if ( big1 ) { + a1 = tai1; + a2 = tai2; + } else { + a1 = tai2; + a2 = tai1; + } + +/* Initial guess for UTC. */ + u1 = a1; + u2 = a2; + +/* Iterate (though in most cases just once is enough). */ + for ( i = 0; i < 3; i++ ) { + + /* Guessed UTC to TAI. */ + j = eraUtctai(u1, u2, &g1, &g2); + if ( j < 0 ) return j; + + /* Adjust guessed UTC. */ + u2 += a1 - g1; + u2 += a2 - g2; + } + +/* Return the UTC result, preserving the TAI order. */ + if ( big1 ) { + *utc1 = u1; + *utc2 = u2; + } else { + *utc1 = u2; + *utc2 = u1; + } + +/* Status. */ + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tcbtdb.c b/erfa/tcbtdb.c new file mode 100644 index 0000000..2a76cf3 --- /dev/null +++ b/erfa/tcbtdb.c @@ -0,0 +1,141 @@ +#include "erfa.h" + +int eraTcbtdb(double tcb1, double tcb2, double *tdb1, double *tdb2) +/* +** - - - - - - - - - - +** e r a T c b t d b +** - - - - - - - - - - +** +** Time scale transformation: Barycentric Coordinate Time, TCB, to +** Barycentric Dynamical Time, TDB. +** +** Given: +** tcb1,tcb2 double TCB as a 2-part Julian Date +** +** Returned: +** tdb1,tdb2 double TDB as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) tcb1+tcb2 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where tcb1 is the Julian +** Day Number and tcb2 is the fraction of a day. The returned +** tdb1,tdb2 follow suit. +** +** 2) The 2006 IAU General Assembly introduced a conventional linear +** transformation between TDB and TCB. This transformation +** compensates for the drift between TCB and terrestrial time TT, +** and keeps TDB approximately centered on TT. Because the +** relationship between TT and TCB depends on the adopted solar +** system ephemeris, the degree of alignment between TDB and TT over +** long intervals will vary according to which ephemeris is used. +** Former definitions of TDB attempted to avoid this problem by +** stipulating that TDB and TT should differ only by periodic +** effects. This is a good description of the nature of the +** relationship but eluded precise mathematical formulation. The +** conventional linear relationship adopted in 2006 sidestepped +** these difficulties whilst delivering a TDB that in practice was +** consistent with values before that date. +** +** 3) TDB is essentially the same as Teph, the time argument for the +** JPL solar system ephemerides. +** +** Reference: +** +** IAU 2006 Resolution B3 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* 1977 Jan 1 00:00:32.184 TT, as two-part JD */ + static const double t77td = ERFA_DJM0 + ERFA_DJM77; + static const double t77tf = ERFA_TTMTAI/ERFA_DAYSEC; + +/* TDB (days) at TAI 1977 Jan 1.0 */ + static const double tdb0 = ERFA_TDB0/ERFA_DAYSEC; + + double d; + + +/* Result, safeguarding precision. */ + if ( tcb1 > tcb2 ) { + d = tcb1 - t77td; + *tdb1 = tcb1; + *tdb2 = tcb2 + tdb0 - ( d + ( tcb2 - t77tf ) ) * ERFA_ELB; + } else { + d = tcb2 - t77td; + *tdb1 = tcb1 + tdb0 - ( d + ( tcb1 - t77tf ) ) * ERFA_ELB; + *tdb2 = tcb2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tcgtt.c b/erfa/tcgtt.c new file mode 100644 index 0000000..adc0c41 --- /dev/null +++ b/erfa/tcgtt.c @@ -0,0 +1,118 @@ +#include "erfa.h" + +int eraTcgtt(double tcg1, double tcg2, double *tt1, double *tt2) +/* +** - - - - - - - - - +** e r a T c g t t +** - - - - - - - - - +** +** Time scale transformation: Geocentric Coordinate Time, TCG, to +** Terrestrial Time, TT. +** +** Given: +** tcg1,tcg2 double TCG as a 2-part Julian Date +** +** Returned: +** tt1,tt2 double TT as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Note: +** +** tcg1+tcg2 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where tcg1 is the Julian +** Day Number and tcg22 is the fraction of a day. The returned +** tt1,tt2 follow suit. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003),. +** IERS Technical Note No. 32, BKG (2004) +** +** IAU 2000 Resolution B1.9 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* 1977 Jan 1 00:00:32.184 TT, as MJD */ + static const double t77t = ERFA_DJM77 + ERFA_TTMTAI/ERFA_DAYSEC; + + +/* Result, safeguarding precision. */ + if ( tcg1 > tcg2 ) { + *tt1 = tcg1; + *tt2 = tcg2 - ( ( tcg1 - ERFA_DJM0 ) + ( tcg2 - t77t ) ) * ERFA_ELG; + } else { + *tt1 = tcg1 - ( ( tcg2 - ERFA_DJM0 ) + ( tcg1 - t77t ) ) * ERFA_ELG; + *tt2 = tcg2; + } + +/* OK status. */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tdbtcb.c b/erfa/tdbtcb.c new file mode 100644 index 0000000..aa57f5e --- /dev/null +++ b/erfa/tdbtcb.c @@ -0,0 +1,146 @@ +#include "erfa.h" + +int eraTdbtcb(double tdb1, double tdb2, double *tcb1, double *tcb2) +/* +** - - - - - - - - - - +** e r a T d b t c b +** - - - - - - - - - - +** +** Time scale transformation: Barycentric Dynamical Time, TDB, to +** Barycentric Coordinate Time, TCB. +** +** Given: +** tdb1,tdb2 double TDB as a 2-part Julian Date +** +** Returned: +** tcb1,tcb2 double TCB as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) tdb1+tdb2 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where tdb1 is the Julian +** Day Number and tdb2 is the fraction of a day. The returned +** tcb1,tcb2 follow suit. +** +** 2) The 2006 IAU General Assembly introduced a conventional linear +** transformation between TDB and TCB. This transformation +** compensates for the drift between TCB and terrestrial time TT, +** and keeps TDB approximately centered on TT. Because the +** relationship between TT and TCB depends on the adopted solar +** system ephemeris, the degree of alignment between TDB and TT over +** long intervals will vary according to which ephemeris is used. +** Former definitions of TDB attempted to avoid this problem by +** stipulating that TDB and TT should differ only by periodic +** effects. This is a good description of the nature of the +** relationship but eluded precise mathematical formulation. The +** conventional linear relationship adopted in 2006 sidestepped +** these difficulties whilst delivering a TDB that in practice was +** consistent with values before that date. +** +** 3) TDB is essentially the same as Teph, the time argument for the +** JPL solar system ephemerides. +** +** Reference: +** +** IAU 2006 Resolution B3 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* 1977 Jan 1 00:00:32.184 TT, as two-part JD */ + static const double t77td = ERFA_DJM0 + ERFA_DJM77; + static const double t77tf = ERFA_TTMTAI/ERFA_DAYSEC; + +/* TDB (days) at TAI 1977 Jan 1.0 */ + static const double tdb0 = ERFA_TDB0/ERFA_DAYSEC; + +/* TDB to TCB rate */ + static const double elbb = ERFA_ELB/(1.0-ERFA_ELB); + + double d, f; + + +/* Result, preserving date format but safeguarding precision. */ + if ( tdb1 > tdb2 ) { + d = t77td - tdb1; + f = tdb2 - tdb0; + *tcb1 = tdb1; + *tcb2 = f - ( d - ( f - t77tf ) ) * elbb; + } else { + d = t77td - tdb2; + f = tdb1 - tdb0; + *tcb1 = f + ( d - ( f - t77tf ) ) * elbb; + *tcb2 = tdb2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tdbtt.c b/erfa/tdbtt.c new file mode 100644 index 0000000..2b0d72e --- /dev/null +++ b/erfa/tdbtt.c @@ -0,0 +1,130 @@ +#include "erfa.h" + +int eraTdbtt(double tdb1, double tdb2, double dtr, + double *tt1, double *tt2 ) +/* +** - - - - - - - - - +** e r a T d b t t +** - - - - - - - - - +** +** Time scale transformation: Barycentric Dynamical Time, TDB, to +** Terrestrial Time, TT. +** +** Given: +** tdb1,tdb2 double TDB as a 2-part Julian Date +** dtr double TDB-TT in seconds +** +** Returned: +** tt1,tt2 double TT as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) tdb1+tdb2 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where tdb1 is the Julian +** Day Number and tdb2 is the fraction of a day. The returned +** tt1,tt2 follow suit. +** +** 2) The argument dtr represents the quasi-periodic component of the +** GR transformation between TT and TCB. It is dependent upon the +** adopted solar-system ephemeris, and can be obtained by numerical +** integration, by interrogating a precomputed time ephemeris or by +** evaluating a model such as that implemented in the ERFA function +** eraDtdb. The quantity is dominated by an annual term of 1.7 ms +** amplitude. +** +** 3) TDB is essentially the same as Teph, the time argument for the +** JPL solar system ephemerides. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** IAU 2006 Resolution 3 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dtrd; + + +/* Result, safeguarding precision. */ + dtrd = dtr / ERFA_DAYSEC; + if ( tdb1 > tdb2 ) { + *tt1 = tdb1; + *tt2 = tdb2 - dtrd; + } else { + *tt1 = tdb1 - dtrd; + *tt2 = tdb2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tf2a.c b/erfa/tf2a.c new file mode 100644 index 0000000..3d3535e --- /dev/null +++ b/erfa/tf2a.c @@ -0,0 +1,116 @@ +#include "erfa.h" +#include + +int eraTf2a(char s, int ihour, int imin, double sec, double *rad) +/* +** - - - - - - - - +** e r a T f 2 a +** - - - - - - - - +** +** Convert hours, minutes, seconds to radians. +** +** Given: +** s char sign: '-' = negative, otherwise positive +** ihour int hours +** imin int minutes +** sec double seconds +** +** Returned: +** rad double angle in radians +** +** Returned (function value): +** int status: 0 = OK +** 1 = ihour outside range 0-23 +** 2 = imin outside range 0-59 +** 3 = sec outside range 0-59.999... +** +** Notes: +** +** 1) The result is computed even if any of the range checks fail. +** +** 2) Negative ihour, imin and/or sec produce a warning status, but +** the absolute value is used in the conversion. +** +** 3) If there are multiple errors, the status value reflects only the +** first, the smallest taking precedence. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Compute the interval. */ + *rad = ( s == '-' ? -1.0 : 1.0 ) * + ( 60.0 * ( 60.0 * ( (double) abs(ihour) ) + + ( (double) abs(imin) ) ) + + fabs(sec) ) * ERFA_DS2R; + +/* Validate arguments and return status. */ + if ( ihour < 0 || ihour > 23 ) return 1; + if ( imin < 0 || imin > 59 ) return 2; + if ( sec < 0.0 || sec >= 60.0 ) return 3; + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tf2d.c b/erfa/tf2d.c new file mode 100644 index 0000000..d61f0c8 --- /dev/null +++ b/erfa/tf2d.c @@ -0,0 +1,116 @@ +#include "erfa.h" +#include + +int eraTf2d(char s, int ihour, int imin, double sec, double *days) +/* +** - - - - - - - - +** e r a T f 2 d +** - - - - - - - - +** +** Convert hours, minutes, seconds to days. +** +** Given: +** s char sign: '-' = negative, otherwise positive +** ihour int hours +** imin int minutes +** sec double seconds +** +** Returned: +** days double interval in days +** +** Returned (function value): +** int status: 0 = OK +** 1 = ihour outside range 0-23 +** 2 = imin outside range 0-59 +** 3 = sec outside range 0-59.999... +** +** Notes: +** +** 1) The result is computed even if any of the range checks fail. +** +** 2) Negative ihour, imin and/or sec produce a warning status, but +** the absolute value is used in the conversion. +** +** 3) If there are multiple errors, the status value reflects only the +** first, the smallest taking precedence. +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Compute the interval. */ + *days = ( s == '-' ? -1.0 : 1.0 ) * + ( 60.0 * ( 60.0 * ( (double) abs(ihour) ) + + ( (double) abs(imin) ) ) + + fabs(sec) ) / ERFA_DAYSEC; + +/* Validate arguments and return status. */ + if ( ihour < 0 || ihour > 23 ) return 1; + if ( imin < 0 || imin > 59 ) return 2; + if ( sec < 0.0 || sec >= 60.0 ) return 3; + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tr.c b/erfa/tr.c new file mode 100644 index 0000000..7c97550 --- /dev/null +++ b/erfa/tr.c @@ -0,0 +1,102 @@ +#include "erfa.h" + +void eraTr(double r[3][3], double rt[3][3]) +/* +** - - - - - - +** e r a T r +** - - - - - - +** +** Transpose an r-matrix. +** +** Given: +** r double[3][3] r-matrix +** +** Returned: +** rt double[3][3] transpose +** +** Note: +** It is permissible for r and rt to be the same array. +** +** Called: +** eraCr copy r-matrix +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double wm[3][3]; + int i, j; + + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + wm[i][j] = r[j][i]; + } + } + eraCr(wm, rt); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/trxp.c b/erfa/trxp.c new file mode 100644 index 0000000..82ff8c6 --- /dev/null +++ b/erfa/trxp.c @@ -0,0 +1,102 @@ +#include "erfa.h" + +void eraTrxp(double r[3][3], double p[3], double trp[3]) +/* +** - - - - - - - - +** e r a T r x p +** - - - - - - - - +** +** Multiply a p-vector by the transpose of an r-matrix. +** +** Given: +** r double[3][3] r-matrix +** p double[3] p-vector +** +** Returned: +** trp double[3] r * p +** +** Note: +** It is permissible for p and trp to be the same array. +** +** Called: +** eraTr transpose r-matrix +** eraRxp product of r-matrix and p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double tr[3][3]; + + +/* Transpose of matrix r. */ + eraTr(r, tr); + +/* Matrix tr * vector p -> vector trp. */ + eraRxp(tr, p, trp); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/trxpv.c b/erfa/trxpv.c new file mode 100644 index 0000000..ce071b1 --- /dev/null +++ b/erfa/trxpv.c @@ -0,0 +1,102 @@ +#include "erfa.h" + +void eraTrxpv(double r[3][3], double pv[2][3], double trpv[2][3]) +/* +** - - - - - - - - - +** e r a T r x p v +** - - - - - - - - - +** +** Multiply a pv-vector by the transpose of an r-matrix. +** +** Given: +** r double[3][3] r-matrix +** pv double[2][3] pv-vector +** +** Returned: +** trpv double[2][3] r * pv +** +** Note: +** It is permissible for pv and trpv to be the same array. +** +** Called: +** eraTr transpose r-matrix +** eraRxpv product of r-matrix and pv-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double tr[3][3]; + + +/* Transpose of matrix r. */ + eraTr(r, tr); + +/* Matrix tr * vector pv -> vector trpv. */ + eraRxpv(tr, pv, trpv); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tttai.c b/erfa/tttai.c new file mode 100644 index 0000000..87edb10 --- /dev/null +++ b/erfa/tttai.c @@ -0,0 +1,119 @@ +#include "erfa.h" + +int eraTttai(double tt1, double tt2, double *tai1, double *tai2) +/* +** - - - - - - - - - +** e r a T t t a i +** - - - - - - - - - +** +** Time scale transformation: Terrestrial Time, TT, to International +** Atomic Time, TAI. +** +** Given: +** tt1,tt2 double TT as a 2-part Julian Date +** +** Returned: +** tai1,tai2 double TAI as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Note: +** +** tt1+tt2 is Julian Date, apportioned in any convenient way between +** the two arguments, for example where tt1 is the Julian Day Number +** and tt2 is the fraction of a day. The returned tai1,tai2 follow +** suit. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* TT minus TAI (days). */ + static const double dtat = ERFA_TTMTAI/ERFA_DAYSEC; + + +/* Result, safeguarding precision. */ + if ( tt1 > tt2 ) { + *tai1 = tt1; + *tai2 = tt2 - dtat; + } else { + *tai1 = tt1 - dtat; + *tai2 = tt2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tttcg.c b/erfa/tttcg.c new file mode 100644 index 0000000..dab382f --- /dev/null +++ b/erfa/tttcg.c @@ -0,0 +1,121 @@ +#include "erfa.h" + +int eraTttcg(double tt1, double tt2, double *tcg1, double *tcg2) +/* +** - - - - - - - - - +** e r a T t t c g +** - - - - - - - - - +** +** Time scale transformation: Terrestrial Time, TT, to Geocentric +** Coordinate Time, TCG. +** +** Given: +** tt1,tt2 double TT as a 2-part Julian Date +** +** Returned: +** tcg1,tcg2 double TCG as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Note: +** +** tt1+tt2 is Julian Date, apportioned in any convenient way between +** the two arguments, for example where tt1 is the Julian Day Number +** and tt2 is the fraction of a day. The returned tcg1,tcg2 follow +** suit. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** IAU 2000 Resolution B1.9 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* 1977 Jan 1 00:00:32.184 TT, as MJD */ + static const double t77t = ERFA_DJM77 + ERFA_TTMTAI/ERFA_DAYSEC; + +/* TT to TCG rate */ + static const double elgg = ERFA_ELG/(1.0-ERFA_ELG); + + +/* Result, safeguarding precision. */ + if ( tt1 > tt2 ) { + *tcg1 = tt1; + *tcg2 = tt2 + ( ( tt1 - ERFA_DJM0 ) + ( tt2 - t77t ) ) * elgg; + } else { + *tcg1 = tt1 + ( ( tt2 - ERFA_DJM0 ) + ( tt1 - t77t ) ) * elgg; + *tcg2 = tt2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/tttdb.c b/erfa/tttdb.c new file mode 100644 index 0000000..c94c94d --- /dev/null +++ b/erfa/tttdb.c @@ -0,0 +1,130 @@ +#include "erfa.h" + +int eraTttdb(double tt1, double tt2, double dtr, + double *tdb1, double *tdb2) +/* +** - - - - - - - - - +** e r a T t t d b +** - - - - - - - - - +** +** Time scale transformation: Terrestrial Time, TT, to Barycentric +** Dynamical Time, TDB. +** +** Given: +** tt1,tt2 double TT as a 2-part Julian Date +** dtr double TDB-TT in seconds +** +** Returned: +** tdb1,tdb2 double TDB as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) tt1+tt2 is Julian Date, apportioned in any convenient way between +** the two arguments, for example where tt1 is the Julian Day Number +** and tt2 is the fraction of a day. The returned tdb1,tdb2 follow +** suit. +** +** 2) The argument dtr represents the quasi-periodic component of the +** GR transformation between TT and TCB. It is dependent upon the +** adopted solar-system ephemeris, and can be obtained by numerical +** integration, by interrogating a precomputed time ephemeris or by +** evaluating a model such as that implemented in the ERFA function +** eraDtdb. The quantity is dominated by an annual term of 1.7 ms +** amplitude. +** +** 3) TDB is essentially the same as Teph, the time argument for the JPL +** solar system ephemerides. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** IAU 2006 Resolution 3 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dtrd; + + +/* Result, safeguarding precision. */ + dtrd = dtr / ERFA_DAYSEC; + if ( tt1 > tt2 ) { + *tdb1 = tt1; + *tdb2 = tt2 + dtrd; + } else { + *tdb1 = tt1 + dtrd; + *tdb2 = tt2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ttut1.c b/erfa/ttut1.c new file mode 100644 index 0000000..27dbac8 --- /dev/null +++ b/erfa/ttut1.c @@ -0,0 +1,119 @@ +#include "erfa.h" + +int eraTtut1(double tt1, double tt2, double dt, + double *ut11, double *ut12) +/* +** - - - - - - - - - +** e r a T t u t 1 +** - - - - - - - - - +** +** Time scale transformation: Terrestrial Time, TT, to Universal Time, +** UT1. +** +** Given: +** tt1,tt2 double TT as a 2-part Julian Date +** dt double TT-UT1 in seconds +** +** Returned: +** ut11,ut12 double UT1 as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) tt1+tt2 is Julian Date, apportioned in any convenient way between +** the two arguments, for example where tt1 is the Julian Day Number +** and tt2 is the fraction of a day. The returned ut11,ut12 follow +** suit. +** +** 2) The argument dt is classical Delta T. +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dtd; + + +/* Result, safeguarding precision. */ + dtd = dt / ERFA_DAYSEC; + if ( tt1 > tt2 ) { + *ut11 = tt1; + *ut12 = tt2 - dtd; + } else { + *ut11 = tt1 - dtd; + *ut12 = tt2; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ut1tai.c b/erfa/ut1tai.c new file mode 100644 index 0000000..c21df04 --- /dev/null +++ b/erfa/ut1tai.c @@ -0,0 +1,120 @@ +#include "erfa.h" + +int eraUt1tai(double ut11, double ut12, double dta, + double *tai1, double *tai2) +/* +** - - - - - - - - - - +** e r a U t 1 t a i +** - - - - - - - - - - +** +** Time scale transformation: Universal Time, UT1, to International +** Atomic Time, TAI. +** +** Given: +** ut11,ut12 double UT1 as a 2-part Julian Date +** dta double UT1-TAI in seconds +** +** Returned: +** tai1,tai2 double TAI as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) ut11+ut12 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where ut11 is the Julian +** Day Number and ut12 is the fraction of a day. The returned +** tai1,tai2 follow suit. +** +** 2) The argument dta, i.e. UT1-TAI, is an observed quantity, and is +** available from IERS tabulations. +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dtad; + + +/* Result, safeguarding precision. */ + dtad = dta / ERFA_DAYSEC; + if ( ut11 > ut12 ) { + *tai1 = ut11; + *tai2 = ut12 - dtad; + } else { + *tai1 = ut11 - dtad; + *tai2 = ut12; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ut1tt.c b/erfa/ut1tt.c new file mode 100644 index 0000000..6beeb22 --- /dev/null +++ b/erfa/ut1tt.c @@ -0,0 +1,119 @@ +#include "erfa.h" + +int eraUt1tt(double ut11, double ut12, double dt, + double *tt1, double *tt2) +/* +** - - - - - - - - - +** e r a U t 1 t t +** - - - - - - - - - +** +** Time scale transformation: Universal Time, UT1, to Terrestrial +** Time, TT. +** +** Given: +** ut11,ut12 double UT1 as a 2-part Julian Date +** dt double TT-UT1 in seconds +** +** Returned: +** tt1,tt2 double TT as a 2-part Julian Date +** +** Returned (function value): +** int status: 0 = OK +** +** Notes: +** +** 1) ut11+ut12 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where ut11 is the Julian +** Day Number and ut12 is the fraction of a day. The returned +** tt1,tt2 follow suit. +** +** 2) The argument dt is classical Delta T. +** +** Reference: +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double dtd; + + +/* Result, safeguarding precision. */ + dtd = dt / ERFA_DAYSEC; + if ( ut11 > ut12 ) { + *tt1 = ut11; + *tt2 = ut12 + dtd; + } else { + *tt1 = ut11 + dtd; + *tt2 = ut12; + } + +/* Status (always OK). */ + return 0; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/ut1utc.c b/erfa/ut1utc.c new file mode 100644 index 0000000..4788bb3 --- /dev/null +++ b/erfa/ut1utc.c @@ -0,0 +1,202 @@ +#include "erfa.h" + +int eraUt1utc(double ut11, double ut12, double dut1, + double *utc1, double *utc2) +/* +** - - - - - - - - - - +** e r a U t 1 u t c +** - - - - - - - - - - +** +** Time scale transformation: Universal Time, UT1, to Coordinated +** Universal Time, UTC. +** +** Given: +** ut11,ut12 double UT1 as a 2-part Julian Date (Note 1) +** dut1 double Delta UT1: UT1-UTC in seconds (Note 2) +** +** Returned: +** utc1,utc2 double UTC as a 2-part quasi Julian Date (Notes 3,4) +** +** Returned (function value): +** int status: +1 = dubious year (Note 5) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) ut11+ut12 is Julian Date, apportioned in any convenient way +** between the two arguments, for example where ut11 is the Julian +** Day Number and ut12 is the fraction of a day. The returned utc1 +** and utc2 form an analogous pair, except that a special convention +** is used, to deal with the problem of leap seconds - see Note 3. +** +** 2) Delta UT1 can be obtained from tabulations provided by the +** International Earth Rotation and Reference Systems Service. The +** value changes abruptly by 1s at a leap second; however, close to +** a leap second the algorithm used here is tolerant of the "wrong" +** choice of value being made. +** +** 3) JD cannot unambiguously represent UTC during a leap second unless +** special measures are taken. The convention in the present +** function is that the returned quasi JD day UTC1+UTC2 represents +** UTC days whether the length is 86399, 86400 or 86401 SI seconds. +** +** 4) The function eraD2dtf can be used to transform the UTC quasi-JD +** into calendar date and clock time, including UTC leap second +** handling. +** +** 5) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the future +** to be trusted. See eraDat for further details. +** +** Called: +** eraJd2cal JD to Gregorian calendar +** eraDat delta(AT) = TAI-UTC +** eraCal2jd Gregorian calendar to JD +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int big1; + int i, iy, im, id, js; + double duts, u1, u2, d1, dats1, d2, fd, dats2, ddats, us1, us2, du; + + +/* UT1-UTC in seconds. */ + duts = dut1; + +/* Put the two parts of the UT1 into big-first order. */ + big1 = ( ut11 >= ut12 ); + if ( big1 ) { + u1 = ut11; + u2 = ut12; + } else { + u1 = ut12; + u2 = ut11; + } + +/* See if the UT1 can possibly be in a leap-second day. */ + d1 = u1; + dats1 = 0; + for ( i = -1; i <= 3; i++ ) { + d2 = u2 + (double) i; + if ( eraJd2cal(d1, d2, &iy, &im, &id, &fd) ) return -1; + js = eraDat(iy, im, id, 0.0, &dats2); + if ( js < 0 ) return -1; + if ( i == - 1 ) dats1 = dats2; + ddats = dats2 - dats1; + if ( fabs(ddats) >= 0.5 ) { + + /* Yes, leap second nearby: ensure UT1-UTC is "before" value. */ + if ( ddats * duts >= 0 ) duts -= ddats; + + /* UT1 for the start of the UTC day that ends in a leap. */ + if ( eraCal2jd(iy, im, id, &d1, &d2) ) return -1; + us1 = d1; + us2 = d2 - 1.0 + duts/ERFA_DAYSEC; + + /* Is the UT1 after this point? */ + du = u1 - us1; + du += u2 - us2; + if ( du > 0 ) { + + /* Yes: fraction of the current UTC day that has elapsed. */ + fd = du * ERFA_DAYSEC / ( ERFA_DAYSEC + ddats ); + + /* Ramp UT1-UTC to bring about ERFA's JD(UTC) convention. */ + duts += ddats * ( fd <= 1.0 ? fd : 1.0 ); + } + + /* Done. */ + break; + } + dats1 = dats2; + } + +/* Subtract the (possibly adjusted) UT1-UTC from UT1 to give UTC. */ + u2 -= duts / ERFA_DAYSEC; + +/* Result, safeguarding precision. */ + if ( big1 ) { + *utc1 = u1; + *utc2 = u2; + } else { + *utc1 = u2; + *utc2 = u1; + } + +/* Status. */ + return js; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/utctai.c b/erfa/utctai.c new file mode 100644 index 0000000..44526f0 --- /dev/null +++ b/erfa/utctai.c @@ -0,0 +1,186 @@ +#include "erfa.h" + +int eraUtctai(double utc1, double utc2, double *tai1, double *tai2) +/* +** - - - - - - - - - - +** e r a U t c t a i +** - - - - - - - - - - +** +** Time scale transformation: Coordinated Universal Time, UTC, to +** International Atomic Time, TAI. +** +** Given: +** utc1,utc2 double UTC as a 2-part quasi Julian Date (Notes 1-4) +** +** Returned: +** tai1,tai2 double TAI as a 2-part Julian Date (Note 5) +** +** Returned (function value): +** int status: +1 = dubious year (Note 3) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** 2) JD cannot unambiguously represent UTC during a leap second unless +** special measures are taken. The convention in the present +** function is that the JD day represents UTC days whether the +** length is 86399, 86400 or 86401 SI seconds. In the 1960-1972 era +** there were smaller jumps (in either direction) each time the +** linear UTC(TAI) expression was changed, and these "mini-leaps" +** are also included in the ERFA convention. +** +** 3) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the future +** to be trusted. See eraDat for further details. +** +** 4) The function eraDtf2d converts from calendar date and time of day +** into 2-part Julian Date, and in the case of UTC implements the +** leap-second-ambiguity convention described above. +** +** 5) The returned TAI1,TAI2 are such that their sum is the TAI Julian +** Date. +** +** Called: +** eraJd2cal JD to Gregorian calendar +** eraDat delta(AT) = TAI-UTC +** eraCal2jd Gregorian calendar to JD +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int big1; + int iy, im, id, j, iyt, imt, idt; + double u1, u2, fd, dat0, dat12, w, dat24, dlod, dleap, z1, z2, a2; + + +/* Put the two parts of the UTC into big-first order. */ + big1 = ( utc1 >= utc2 ); + if ( big1 ) { + u1 = utc1; + u2 = utc2; + } else { + u1 = utc2; + u2 = utc1; + } + +/* Get TAI-UTC at 0h today. */ + j = eraJd2cal(u1, u2, &iy, &im, &id, &fd); + if ( j ) return j; + j = eraDat(iy, im, id, 0.0, &dat0); + if ( j < 0 ) return j; + +/* Get TAI-UTC at 12h today (to detect drift). */ + j = eraDat(iy, im, id, 0.5, &dat12); + if ( j < 0 ) return j; + +/* Get TAI-UTC at 0h tomorrow (to detect jumps). */ + j = eraJd2cal(u1+1.5, u2-fd, &iyt, &imt, &idt, &w); + if ( j ) return j; + j = eraDat(iyt, imt, idt, 0.0, &dat24); + if ( j < 0 ) return j; + +/* Separate TAI-UTC change into per-day (DLOD) and any jump (DLEAP). */ + dlod = 2.0 * (dat12 - dat0); + dleap = dat24 - (dat0 + dlod); + +/* Remove any scaling applied to spread leap into preceding day. */ + fd *= (ERFA_DAYSEC+dleap)/ERFA_DAYSEC; + +/* Scale from (pre-1972) UTC seconds to SI seconds. */ + fd *= (ERFA_DAYSEC+dlod)/ERFA_DAYSEC; + +/* Today's calendar date to 2-part JD. */ + if ( eraCal2jd(iy, im, id, &z1, &z2) ) return -1; + +/* Assemble the TAI result, preserving the UTC split and order. */ + a2 = z1 - u1; + a2 += z2; + a2 += fd + dat0/ERFA_DAYSEC; + if ( big1 ) { + *tai1 = u1; + *tai2 = a2; + } else { + *tai1 = a2; + *tai2 = u1; + } + +/* Status. */ + return j; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/utcut1.c b/erfa/utcut1.c new file mode 100644 index 0000000..0ba94bc --- /dev/null +++ b/erfa/utcut1.c @@ -0,0 +1,156 @@ +#include "erfa.h" + +int eraUtcut1(double utc1, double utc2, double dut1, + double *ut11, double *ut12) +/* +** - - - - - - - - - - +** e r a U t c u t 1 +** - - - - - - - - - - +** +** Time scale transformation: Coordinated Universal Time, UTC, to +** Universal Time, UT1. +** +** Given: +** utc1,utc2 double UTC as a 2-part quasi Julian Date (Notes 1-4) +** dut1 double Delta UT1 = UT1-UTC in seconds (Note 5) +** +** Returned: +** ut11,ut12 double UT1 as a 2-part Julian Date (Note 6) +** +** Returned (function value): +** int status: +1 = dubious year (Note 3) +** 0 = OK +** -1 = unacceptable date +** +** Notes: +** +** 1) utc1+utc2 is quasi Julian Date (see Note 2), apportioned in any +** convenient way between the two arguments, for example where utc1 +** is the Julian Day Number and utc2 is the fraction of a day. +** +** 2) JD cannot unambiguously represent UTC during a leap second unless +** special measures are taken. The convention in the present +** function is that the JD day represents UTC days whether the +** length is 86399, 86400 or 86401 SI seconds. +** +** 3) The warning status "dubious year" flags UTCs that predate the +** introduction of the time scale or that are too far in the future +** to be trusted. See eraDat for further details. +** +** 4) The function eraDtf2d converts from calendar date and time of +** day into 2-part Julian Date, and in the case of UTC implements +** the leap-second-ambiguity convention described above. +** +** 5) Delta UT1 can be obtained from tabulations provided by the +** International Earth Rotation and Reference Systems Service. +** It is the caller's responsibility to supply a dut1 argument +** containing the UT1-UTC value that matches the given UTC. +** +** 6) The returned ut11,ut12 are such that their sum is the UT1 Julian +** Date. +** +** References: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Explanatory Supplement to the Astronomical Almanac, +** P. Kenneth Seidelmann (ed), University Science Books (1992) +** +** Called: +** eraJd2cal JD to Gregorian calendar +** eraDat delta(AT) = TAI-UTC +** eraUtctai UTC to TAI +** eraTaiut1 TAI to UT1 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + int iy, im, id, js, jw; + double w, dat, dta, tai1, tai2; + + +/* Look up TAI-UTC. */ + if ( eraJd2cal(utc1, utc2, &iy, &im, &id, &w) ) return -1; + js = eraDat ( iy, im, id, 0.0, &dat); + if ( js < 0 ) return -1; + +/* Form UT1-TAI. */ + dta = dut1 - dat; + +/* UTC to TAI to UT1. */ + jw = eraUtctai(utc1, utc2, &tai1, &tai2); + if ( jw < 0 ) { + return -1; + } else if ( jw > 0 ) { + js = jw; + } + if ( eraTaiut1(tai1, tai2, dta, ut11, ut12) ) return -1; + +/* Status. */ + return js; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/xy06.c b/erfa/xy06.c new file mode 100644 index 0000000..ab7965a --- /dev/null +++ b/erfa/xy06.c @@ -0,0 +1,2767 @@ +#include "erfa.h" + +void eraXy06(double date1, double date2, double *x, double *y) +/* +** - - - - - - - - +** e r a X y 0 6 +** - - - - - - - - +** +** X,Y coordinates of celestial intermediate pole from series based +** on IAU 2006 precession and IAU 2000A nutation. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** x,y double CIP X,Y coordinates (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The X,Y coordinates are those of the unit vector towards the +** celestial intermediate pole. They represent the combined effects +** of frame bias, precession and nutation. +** +** 3) The fundamental arguments used are as adopted in IERS Conventions +** (2003) and are from Simon et al. (1994) and Souchay et al. +** (1999). +** +** 4) This is an alternative to the angles-based method, via the ERFA +** function eraFw2xy and as used in eraXys06a for example. The two +** methods agree at the 1 microarcsecond level (at present), a +** negligible amount compared with the intrinsic accuracy of the +** models. However, it would be unwise to mix the two methods +** (angles-based and series-based) in a single application. +** +** Called: +** eraFal03 mean anomaly of the Moon +** eraFalp03 mean anomaly of the Sun +** eraFaf03 mean argument of the latitude of the Moon +** eraFad03 mean elongation of the Moon from the Sun +** eraFaom03 mean longitude of the Moon's ascending node +** eraFame03 mean longitude of Mercury +** eraFave03 mean longitude of Venus +** eraFae03 mean longitude of Earth +** eraFama03 mean longitude of Mars +** eraFaju03 mean longitude of Jupiter +** eraFasa03 mean longitude of Saturn +** eraFaur03 mean longitude of Uranus +** eraFane03 mean longitude of Neptune +** eraFapa03 general accumulated precession in longitude +** +** References: +** +** Capitaine, N., Wallace, P.T. & Chapront, J., 2003, +** Astron.Astrophys., 412, 567 +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** McCarthy, D. D., Petit, G. (eds.), 2004, IERS Conventions (2003), +** IERS Technical Note No. 32, BKG +** +** Simon, J.L., Bretagnon, P., Chapront, J., Chapront-Touze, M., +** Francou, G. & Laskar, J., Astron.Astrophys., 1994, 282, 663 +** +** Souchay, J., Loysel, B., Kinoshita, H., Folgueira, M., 1999, +** Astron.Astrophys.Supp.Ser. 135, 111 +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + +/* Maximum power of T in the polynomials for X and Y */ + enum { MAXPT = 5 }; + +/* Polynomial coefficients (arcsec, X then Y). */ + static const double xyp[2][MAXPT+1] = { + + { -0.016617, + 2004.191898, + -0.4297829, + -0.19861834, + 0.000007578, + 0.0000059285 + }, + { -0.006951, + -0.025896, + -22.4072747, + 0.00190059, + 0.001112526, + 0.0000001358 + } + }; + +/* Fundamental-argument multipliers: luni-solar terms */ + static const int mfals[][5] = { + + /* 1-10 */ + { 0, 0, 0, 0, 1 }, + { 0, 0, 2, -2, 2 }, + { 0, 0, 2, 0, 2 }, + { 0, 0, 0, 0, 2 }, + { 0, 1, 0, 0, 0 }, + { 0, 1, 2, -2, 2 }, + { 1, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 1 }, + { 1, 0, 2, 0, 2 }, + { 0, 1, -2, 2, -2 }, + + /* 11-20 */ + { 0, 0, 2, -2, 1 }, + { 1, 0, -2, 0, -2 }, + { 1, 0, 0, -2, 0 }, + { 1, 0, 0, 0, 1 }, + { 1, 0, 0, 0, -1 }, + { 1, 0, -2, -2, -2 }, + { 1, 0, 2, 0, 1 }, + { 2, 0, -2, 0, -1 }, + { 0, 0, 0, 2, 0 }, + { 0, 0, 2, 2, 2 }, + + /* 21-30 */ + { 2, 0, 0, -2, 0 }, + { 0, 2, -2, 2, -2 }, + { 2, 0, 2, 0, 2 }, + { 1, 0, 2, -2, 2 }, + { 1, 0, -2, 0, -1 }, + { 2, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 0 }, + { 0, 1, 0, 0, 1 }, + { 1, 0, 0, -2, -1 }, + { 0, 2, 2, -2, 2 }, + + /* 31-40 */ + { 0, 0, 2, -2, 0 }, + { 1, 0, 0, -2, 1 }, + { 0, 1, 0, 0, -1 }, + { 0, 2, 0, 0, 0 }, + { 1, 0, -2, -2, -1 }, + { 1, 0, 2, 2, 2 }, + { 0, 1, 2, 0, 2 }, + { 2, 0, -2, 0, 0 }, + { 0, 0, 2, 2, 1 }, + { 0, 1, -2, 0, -2 }, + + /* 41-50 */ + { 0, 0, 0, 2, 1 }, + { 1, 0, 2, -2, 1 }, + { 2, 0, 0, -2, -1 }, + { 2, 0, 2, -2, 2 }, + { 2, 0, 2, 0, 1 }, + { 0, 0, 0, 2, -1 }, + { 0, 1, -2, 2, -1 }, + { 1, 1, 0, -2, 0 }, + { 2, 0, 0, -2, 1 }, + { 1, 0, 0, 2, 0 }, + + /* 51-60 */ + { 0, 1, 2, -2, 1 }, + { 1, -1, 0, 0, 0 }, + { 0, 1, -1, 1, -1 }, + { 2, 0, -2, 0, -2 }, + { 0, 1, 0, -2, 0 }, + { 1, 0, 0, -1, 0 }, + { 3, 0, 2, 0, 2 }, + { 0, 0, 0, 1, 0 }, + { 1, -1, 2, 0, 2 }, + { 1, 1, -2, -2, -2 }, + + /* 61-70 */ + { 1, 0, -2, 0, 0 }, + { 2, 0, 0, 0, -1 }, + { 0, 1, -2, -2, -2 }, + { 1, 1, 2, 0, 2 }, + { 2, 0, 0, 0, 1 }, + { 1, 1, 0, 0, 0 }, + { 1, 0, -2, 2, -1 }, + { 1, 0, 2, 0, 0 }, + { 1, -1, 0, -1, 0 }, + { 1, 0, 0, 0, 2 }, + + /* 71-80 */ + { 1, 0, -1, 0, -1 }, + { 0, 0, 2, 1, 2 }, + { 1, 0, -2, -4, -2 }, + { 1, -1, 0, -1, -1 }, + { 1, 0, 2, 2, 1 }, + { 0, 2, -2, 2, -1 }, + { 1, 0, 0, 0, -2 }, + { 2, 0, -2, -2, -2 }, + { 1, 1, 2, -2, 2 }, + { 2, 0, -2, -4, -2 }, + + /* 81-90 */ + { 1, 0, -4, 0, -2 }, + { 2, 0, 2, -2, 1 }, + { 1, 0, 0, -1, -1 }, + { 2, 0, 2, 2, 2 }, + { 3, 0, 0, 0, 0 }, + { 1, 0, 0, 2, 1 }, + { 0, 0, 2, -2, -1 }, + { 3, 0, 2, -2, 2 }, + { 0, 0, 4, -2, 2 }, + { 1, 0, 0, -4, 0 }, + + /* 91-100 */ + { 0, 1, 2, 0, 1 }, + { 2, 0, 0, -4, 0 }, + { 1, 1, 0, -2, -1 }, + { 2, 0, -2, 0, 1 }, + { 0, 0, 2, 0, -1 }, + { 0, 1, -2, 0, -1 }, + { 0, 1, 0, 0, 2 }, + { 0, 0, 2, -1, 2 }, + { 0, 0, 2, 4, 2 }, + { 2, 1, 0, -2, 0 }, + + /* 101-110 */ + { 1, 1, 0, -2, 1 }, + { 1, -1, 0, -2, 0 }, + { 1, -1, 0, -1, -2 }, + { 1, -1, 0, 0, 1 }, + { 0, 1, -2, 2, 0 }, + { 0, 1, 0, 0, -2 }, + { 1, -1, 2, 2, 2 }, + { 1, 0, 0, 2, -1 }, + { 1, -1, -2, -2, -2 }, + { 3, 0, 2, 0, 1 }, + + /* 111-120 */ + { 0, 1, 2, 2, 2 }, + { 1, 0, 2, -2, 0 }, + { 1, 1, -2, -2, -1 }, + { 1, 0, 2, -4, 1 }, + { 0, 1, -2, -2, -1 }, + { 2, -1, 2, 0, 2 }, + { 0, 0, 0, 2, 2 }, + { 1, -1, 2, 0, 1 }, + { 1, -1, -2, 0, -2 }, + { 0, 1, 0, 2, 0 }, + + /* 121-130 */ + { 0, 1, 2, -2, 0 }, + { 0, 0, 0, 1, 1 }, + { 1, 0, -2, -2, 0 }, + { 0, 3, 2, -2, 2 }, + { 2, 1, 2, 0, 2 }, + { 1, 1, 0, 0, 1 }, + { 2, 0, 0, 2, 0 }, + { 1, 1, 2, 0, 1 }, + { 1, 0, 0, -2, -2 }, + { 1, 0, -2, 2, 0 }, + + /* 131-140 */ + { 1, 0, -1, 0, -2 }, + { 0, 1, 0, -2, 1 }, + { 0, 1, 0, 1, 0 }, + { 0, 0, 0, 1, -1 }, + { 1, 0, -2, 2, -2 }, + { 1, -1, 0, 0, -1 }, + { 0, 0, 0, 4, 0 }, + { 1, -1, 0, 2, 0 }, + { 1, 0, 2, 1, 2 }, + { 1, 0, 2, -1, 2 }, + + /* 141-150 */ + { 0, 0, 2, 1, 1 }, + { 1, 0, 0, -2, 2 }, + { 1, 0, -2, 0, 1 }, + { 1, 0, -2, -4, -1 }, + { 0, 0, 2, 2, 0 }, + { 1, 1, 2, -2, 1 }, + { 1, 0, -2, 1, -1 }, + { 0, 0, 1, 0, 1 }, + { 2, 0, -2, -2, -1 }, + { 4, 0, 2, 0, 2 }, + + /* 151-160 */ + { 2, -1, 0, 0, 0 }, + { 2, 1, 2, -2, 2 }, + { 0, 1, 2, 1, 2 }, + { 1, 0, 4, -2, 2 }, + { 1, 1, 0, 0, -1 }, + { 2, 0, 2, 0, 0 }, + { 2, 0, -2, -4, -1 }, + { 1, 0, -1, 0, 0 }, + { 1, 0, 0, 1, 0 }, + { 0, 1, 0, 2, 1 }, + + /* 161-170 */ + { 1, 0, -4, 0, -1 }, + { 1, 0, 0, -4, -1 }, + { 2, 0, 2, 2, 1 }, + { 2, 1, 0, 0, 0 }, + { 0, 0, 2, -3, 2 }, + { 1, 2, 0, -2, 0 }, + { 0, 3, 0, 0, 0 }, + { 0, 0, 4, 0, 2 }, + { 0, 0, 2, -4, 1 }, + { 2, 0, 0, -2, -2 }, + + /* 171-180 */ + { 1, 1, -2, -4, -2 }, + { 0, 1, 0, -2, -1 }, + { 0, 0, 0, 4, 1 }, + { 3, 0, 2, -2, 1 }, + { 1, 0, 2, 4, 2 }, + { 1, 1, -2, 0, -2 }, + { 0, 0, 4, -2, 1 }, + { 2, -2, 0, -2, 0 }, + { 2, 1, 0, -2, -1 }, + { 0, 2, 0, -2, 0 }, + + /* 181-190 */ + { 1, 0, 0, -1, 1 }, + { 1, 1, 2, 2, 2 }, + { 3, 0, 0, 0, -1 }, + { 2, 0, 0, -4, -1 }, + { 3, 0, 2, 2, 2 }, + { 0, 0, 2, 4, 1 }, + { 0, 2, -2, -2, -2 }, + { 1, -1, 0, -2, -1 }, + { 0, 0, 2, -1, 1 }, + { 2, 0, 0, 2, 1 }, + + /* 191-200 */ + { 1, -1, -2, 2, -1 }, + { 0, 0, 0, 2, -2 }, + { 2, 0, 0, -4, 1 }, + { 1, 0, 0, -4, 1 }, + { 2, 0, 2, -4, 1 }, + { 4, 0, 2, -2, 2 }, + { 2, 1, -2, 0, -1 }, + { 2, 1, -2, -4, -2 }, + { 3, 0, 0, -4, 0 }, + { 1, -1, 2, 2, 1 }, + + /* 201-210 */ + { 1, -1, -2, 0, -1 }, + { 0, 2, 0, 0, 1 }, + { 1, 2, -2, -2, -2 }, + { 1, 1, 0, -4, 0 }, + { 2, 0, 0, -2, 2 }, + { 0, 2, 2, -2, 1 }, + { 1, 0, 2, 0, -1 }, + { 2, 1, 0, -2, 1 }, + { 2, -1, -2, 0, -1 }, + { 1, -1, -2, -2, -1 }, + + /* 211-220 */ + { 0, 1, -2, 1, -2 }, + { 1, 0, -4, 2, -2 }, + { 0, 1, 2, 2, 1 }, + { 3, 0, 0, 0, 1 }, + { 2, -1, 2, 2, 2 }, + { 0, 1, -2, -4, -2 }, + { 1, 0, -2, -3, -2 }, + { 2, 0, 0, 0, 2 }, + { 1, -1, 0, -2, -2 }, + { 2, 0, -2, 2, -1 }, + + /* 221-230 */ + { 0, 2, -2, 0, -2 }, + { 3, 0, -2, 0, -1 }, + { 2, -1, 2, 0, 1 }, + { 1, 0, -2, -1, -2 }, + { 0, 0, 2, 0, 3 }, + { 2, 0, -4, 0, -2 }, + { 2, 1, 0, -4, 0 }, + { 1, 1, -2, 1, -1 }, + { 0, 2, 2, 0, 2 }, + { 1, -1, 2, -2, 2 }, + + /* 231-240 */ + { 1, -1, 0, -2, 1 }, + { 2, 1, 2, 0, 1 }, + { 1, 0, 2, -4, 2 }, + { 1, 1, -2, 0, -1 }, + { 1, 1, 0, 2, 0 }, + { 1, 0, 0, -3, 0 }, + { 2, 0, 2, -1, 2 }, + { 0, 2, 0, 0, -1 }, + { 2, -1, 0, -2, 0 }, + { 4, 0, 0, 0, 0 }, + + /* 241-250 */ + { 2, 1, -2, -2, -2 }, + { 0, 2, -2, 2, 0 }, + { 1, 0, 2, 1, 1 }, + { 1, 0, -1, 0, -3 }, + { 3, -1, 2, 0, 2 }, + { 2, 0, 2, -2, 0 }, + { 1, -2, 0, 0, 0 }, + { 2, 0, 0, 0, -2 }, + { 1, 0, 0, 4, 0 }, + { 0, 1, 0, 1, 1 }, + + /* 251-260 */ + { 1, 0, 2, 2, 0 }, + { 0, 1, 0, 2, -1 }, + { 0, 1, 0, 1, -1 }, + { 0, 0, 2, -2, 3 }, + { 3, 1, 2, 0, 2 }, + { 1, 1, 2, 1, 2 }, + { 1, 1, -2, 2, -1 }, + { 2, -1, 2, -2, 2 }, + { 1, -2, 2, 0, 2 }, + { 1, 0, 2, -4, 0 }, + + /* 261-270 */ + { 0, 0, 1, 0, 0 }, + { 1, 0, 2, -3, 1 }, + { 1, -2, 0, -2, 0 }, + { 2, 0, 0, 2, -1 }, + { 1, 1, 2, -4, 1 }, + { 4, 0, 2, 0, 1 }, + { 0, 1, 2, 1, 1 }, + { 1, 2, 2, -2, 2 }, + { 2, 0, 2, 1, 2 }, + { 2, 1, 2, -2, 1 }, + + /* 271-280 */ + { 1, 0, 2, -1, 1 }, + { 1, 0, 4, -2, 1 }, + { 1, -1, 2, -2, 1 }, + { 0, 1, 0, -4, 0 }, + { 3, 0, -2, -2, -2 }, + { 0, 0, 4, -4, 2 }, + { 2, 0, -4, -2, -2 }, + { 2, -2, 0, -2, -1 }, + { 1, 0, 2, -2, -1 }, + { 2, 0, -2, -6, -2 }, + + /* 281-290 */ + { 1, 0, -2, 1, -2 }, + { 1, 0, -2, 2, 1 }, + { 1, -1, 0, 2, -1 }, + { 1, 0, -2, 1, 0 }, + { 2, -1, 0, -2, 1 }, + { 1, -1, 0, 2, 1 }, + { 2, 0, -2, -2, 0 }, + { 1, 0, 2, -3, 2 }, + { 0, 0, 0, 4, -1 }, + { 2, -1, 0, 0, 1 }, + + /* 291-300 */ + { 2, 0, 4, -2, 2 }, + { 0, 0, 2, 3, 2 }, + { 0, 1, 4, -2, 2 }, + { 0, 1, -2, 2, 1 }, + { 1, 1, 0, 2, 1 }, + { 1, 0, 0, 4, 1 }, + { 0, 0, 4, 0, 1 }, + { 2, 0, 0, -3, 0 }, + { 1, 0, 0, -1, -2 }, + { 1, -2, -2, -2, -2 }, + + /* 301-310 */ + { 3, 0, 0, 2, 0 }, + { 2, 0, 2, -4, 2 }, + { 1, 1, -2, -4, -1 }, + { 1, 0, -2, -6, -2 }, + { 2, -1, 0, 0, -1 }, + { 2, -1, 0, 2, 0 }, + { 0, 1, 2, -2, -1 }, + { 1, 1, 0, 1, 0 }, + { 1, 2, 0, -2, -1 }, + { 1, 0, 0, 1, -1 }, + + /* 311-320 */ + { 0, 0, 1, 0, 2 }, + { 3, 1, 2, -2, 2 }, + { 1, 0, -4, -2, -2 }, + { 1, 0, 2, 4, 1 }, + { 1, -2, 2, 2, 2 }, + { 1, -1, -2, -4, -2 }, + { 0, 0, 2, -4, 2 }, + { 0, 0, 2, -3, 1 }, + { 2, 1, -2, 0, 0 }, + { 3, 0, -2, -2, -1 }, + + /* 321-330 */ + { 2, 0, 2, 4, 2 }, + { 0, 0, 0, 0, 3 }, + { 2, -1, -2, -2, -2 }, + { 2, 0, 0, -1, 0 }, + { 3, 0, 2, -4, 2 }, + { 2, 1, 2, 2, 2 }, + { 0, 0, 3, 0, 3 }, + { 1, 1, 2, 2, 1 }, + { 2, 1, 0, 0, -1 }, + { 1, 2, 0, -2, 1 }, + + /* 331-340 */ + { 3, 0, 2, 2, 1 }, + { 1, -1, -2, 2, -2 }, + { 1, 1, 0, -1, 0 }, + { 1, 2, 0, 0, 0 }, + { 1, 0, 4, 0, 2 }, + { 1, -1, 2, 4, 2 }, + { 2, 1, 0, 0, 1 }, + { 1, 0, 0, 2, 2 }, + { 1, -1, -2, 2, 0 }, + { 0, 2, -2, -2, -1 }, + + /* 341-350 */ + { 2, 0, -2, 0, 2 }, + { 5, 0, 2, 0, 2 }, + { 3, 0, -2, -6, -2 }, + { 1, -1, 2, -1, 2 }, + { 3, 0, 0, -4, -1 }, + { 1, 0, 0, 1, 1 }, + { 1, 0, -4, 2, -1 }, + { 0, 1, 2, -4, 1 }, + { 1, 2, 2, 0, 2 }, + { 0, 1, 0, -2, -2 }, + + /* 351-360 */ + { 0, 0, 2, -1, 0 }, + { 1, 0, 1, 0, 1 }, + { 0, 2, 0, -2, 1 }, + { 3, 0, 2, 0, 0 }, + { 1, 1, -2, 1, 0 }, + { 2, 1, -2, -4, -1 }, + { 3, -1, 0, 0, 0 }, + { 2, -1, -2, 0, 0 }, + { 4, 0, 2, -2, 1 }, + { 2, 0, -2, 2, 0 }, + + /* 361-370 */ + { 1, 1, 2, -2, 0 }, + { 1, 0, -2, 4, -1 }, + { 1, 0, -2, -2, 1 }, + { 2, 0, 2, -4, 0 }, + { 1, 1, 0, -2, -2 }, + { 1, 1, -2, -2, 0 }, + { 1, 0, 1, -2, 1 }, + { 2, -1, -2, -4, -2 }, + { 3, 0, -2, 0, -2 }, + { 0, 1, -2, -2, 0 }, + + /* 371-380 */ + { 3, 0, 0, -2, -1 }, + { 1, 0, -2, -3, -1 }, + { 0, 1, 0, -4, -1 }, + { 1, -2, 2, -2, 1 }, + { 0, 1, -2, 1, -1 }, + { 1, -1, 0, 0, 2 }, + { 2, 0, 0, 1, 0 }, + { 1, -2, 0, 2, 0 }, + { 1, 2, -2, -2, -1 }, + { 0, 0, 4, -4, 1 }, + + /* 381-390 */ + { 0, 1, 2, 4, 2 }, + { 0, 1, -4, 2, -2 }, + { 3, 0, -2, 0, 0 }, + { 2, -1, 2, 2, 1 }, + { 0, 1, -2, -4, -1 }, + { 4, 0, 2, 2, 2 }, + { 2, 0, -2, -3, -2 }, + { 2, 0, 0, -6, 0 }, + { 1, 0, 2, 0, 3 }, + { 3, 1, 0, 0, 0 }, + + /* 391-400 */ + { 3, 0, 0, -4, 1 }, + { 1, -1, 2, 0, 0 }, + { 1, -1, 0, -4, 0 }, + { 2, 0, -2, 2, -2 }, + { 1, 1, 0, -2, 2 }, + { 4, 0, 0, -2, 0 }, + { 2, 2, 0, -2, 0 }, + { 0, 1, 2, 0, 0 }, + { 1, 1, 0, -4, 1 }, + { 1, 0, 0, -4, -2 }, + + /* 401-410 */ + { 0, 0, 0, 1, 2 }, + { 3, 0, 0, 2, 1 }, + { 1, 1, 0, -4, -1 }, + { 0, 0, 2, 2, -1 }, + { 1, 1, 2, 0, 0 }, + { 1, -1, 2, -4, 1 }, + { 1, 1, 0, 0, 2 }, + { 0, 0, 2, 6, 2 }, + { 4, 0, -2, -2, -1 }, + { 2, 1, 0, -4, -1 }, + + /* 411-420 */ + { 0, 0, 0, 3, 1 }, + { 1, -1, -2, 0, 0 }, + { 0, 0, 2, 1, 0 }, + { 1, 0, 0, 2, -2 }, + { 3, -1, 2, 2, 2 }, + { 3, -1, 2, -2, 2 }, + { 1, 0, 0, -1, 2 }, + { 1, -2, 2, -2, 2 }, + { 0, 1, 0, 2, 2 }, + { 0, 1, -2, -1, -2 }, + + /* 421-430 */ + { 1, 1, -2, 0, 0 }, + { 0, 2, 2, -2, 0 }, + { 3, -1, -2, -1, -2 }, + { 1, 0, 0, -6, 0 }, + { 1, 0, -2, -4, 0 }, + { 2, 1, 0, -4, 1 }, + { 2, 0, 2, 0, -1 }, + { 2, 0, -4, 0, -1 }, + { 0, 0, 3, 0, 2 }, + { 2, 1, -2, -2, -1 }, + + /* 431-440 */ + { 1, -2, 0, 0, 1 }, + { 2, -1, 0, -4, 0 }, + { 0, 0, 0, 3, 0 }, + { 5, 0, 2, -2, 2 }, + { 1, 2, -2, -4, -2 }, + { 1, 0, 4, -4, 2 }, + { 0, 0, 4, -1, 2 }, + { 3, 1, 0, -4, 0 }, + { 3, 0, 0, -6, 0 }, + { 2, 0, 0, 2, 2 }, + + /* 441-450 */ + { 2, -2, 2, 0, 2 }, + { 1, 0, 0, -3, 1 }, + { 1, -2, -2, 0, -2 }, + { 1, -1, -2, -3, -2 }, + { 0, 0, 2, -2, -2 }, + { 2, 0, -2, -4, 0 }, + { 1, 0, -4, 0, 0 }, + { 0, 1, 0, -1, 0 }, + { 4, 0, 0, 0, -1 }, + { 3, 0, 2, -1, 2 }, + + /* 451-460 */ + { 3, -1, 2, 0, 1 }, + { 2, 0, 2, -1, 1 }, + { 1, 2, 2, -2, 1 }, + { 1, 1, 0, 2, -1 }, + { 0, 2, 2, 0, 1 }, + { 3, 1, 2, 0, 1 }, + { 1, 1, 2, 1, 1 }, + { 1, 1, 0, -1, 1 }, + { 1, -2, 0, -2, -1 }, + { 4, 0, 0, -4, 0 }, + + /* 461-470 */ + { 2, 1, 0, 2, 0 }, + { 1, -1, 0, 4, 0 }, + { 0, 1, 0, -2, 2 }, + { 0, 0, 2, 0, -2 }, + { 1, 0, -1, 0, 1 }, + { 3, 0, 2, -2, 0 }, + { 2, 0, 2, 2, 0 }, + { 1, 2, 0, -4, 0 }, + { 1, -1, 0, -3, 0 }, + { 0, 1, 0, 4, 0 }, + + /* 471 - 480 */ + { 0, 1, -2, 0, 0 }, + { 2, 2, 2, -2, 2 }, + { 0, 0, 0, 1, -2 }, + { 0, 2, -2, 0, -1 }, + { 4, 0, 2, -4, 2 }, + { 2, 0, -4, 2, -2 }, + { 2, -1, -2, 0, -2 }, + { 1, 1, 4, -2, 2 }, + { 1, 1, 2, -4, 2 }, + { 1, 0, 2, 3, 2 }, + + /* 481-490 */ + { 1, 0, 0, 4, -1 }, + { 0, 0, 0, 4, 2 }, + { 2, 0, 0, 4, 0 }, + { 1, 1, -2, 2, 0 }, + { 2, 1, 2, 1, 2 }, + { 2, 1, 2, -4, 1 }, + { 2, 0, 2, 1, 1 }, + { 2, 0, -4, -2, -1 }, + { 2, 0, -2, -6, -1 }, + { 2, -1, 2, -1, 2 }, + + /* 491-500 */ + { 1, -2, 2, 0, 1 }, + { 1, -2, 0, -2, 1 }, + { 1, -1, 0, -4, -1 }, + { 0, 2, 2, 2, 2 }, + { 0, 2, -2, -4, -2 }, + { 0, 1, 2, 3, 2 }, + { 0, 1, 0, -4, 1 }, + { 3, 0, 0, -2, 1 }, + { 2, 1, -2, 0, 1 }, + { 2, 0, 4, -2, 1 }, + + /* 501-510 */ + { 2, 0, 0, -3, -1 }, + { 2, -2, 0, -2, 1 }, + { 2, -1, 2, -2, 1 }, + { 1, 0, 0, -6, -1 }, + { 1, -2, 0, 0, -1 }, + { 1, -2, -2, -2, -1 }, + { 0, 1, 4, -2, 1 }, + { 0, 0, 2, 3, 1 }, + { 2, -1, 0, -1, 0 }, + { 1, 3, 0, -2, 0 }, + + /* 511-520 */ + { 0, 3, 0, -2, 0 }, + { 2, -2, 2, -2, 2 }, + { 0, 0, 4, -2, 0 }, + { 4, -1, 2, 0, 2 }, + { 2, 2, -2, -4, -2 }, + { 4, 1, 2, 0, 2 }, + { 4, -1, -2, -2, -2 }, + { 2, 1, 0, -2, -2 }, + { 2, 1, -2, -6, -2 }, + { 2, 0, 0, -1, 1 }, + + /* 521-530 */ + { 2, -1, -2, 2, -1 }, + { 1, 1, -2, 2, -2 }, + { 1, 1, -2, -3, -2 }, + { 1, 0, 3, 0, 3 }, + { 1, 0, -2, 1, 1 }, + { 1, 0, -2, 0, 2 }, + { 1, -1, 2, 1, 2 }, + { 1, -1, 0, 0, -2 }, + { 1, -1, -4, 2, -2 }, + { 0, 3, -2, -2, -2 }, + + /* 531-540 */ + { 0, 1, 0, 4, 1 }, + { 0, 0, 4, 2, 2 }, + { 3, 0, -2, -2, 0 }, + { 2, -2, 0, 0, 0 }, + { 1, 1, 2, -4, 0 }, + { 1, 1, 0, -3, 0 }, + { 1, 0, 2, -3, 0 }, + { 1, -1, 2, -2, 0 }, + { 0, 2, 0, 2, 0 }, + { 0, 0, 2, 4, 0 }, + + /* 541-550 */ + { 1, 0, 1, 0, 0 }, + { 3, 1, 2, -2, 1 }, + { 3, 0, 4, -2, 2 }, + { 3, 0, 2, 1, 2 }, + { 3, 0, 0, 2, -1 }, + { 3, 0, 0, 0, 2 }, + { 3, 0, -2, 2, -1 }, + { 2, 0, 4, -4, 2 }, + { 2, 0, 2, -3, 2 }, + { 2, 0, 0, 4, 1 }, + + /* 551-560 */ + { 2, 0, 0, -3, 1 }, + { 2, 0, -4, 2, -1 }, + { 2, 0, -2, -2, 1 }, + { 2, -2, 2, 2, 2 }, + { 2, -2, 0, -2, -2 }, + { 2, -1, 0, 2, 1 }, + { 2, -1, 0, 2, -1 }, + { 1, 1, 2, 4, 2 }, + { 1, 1, 0, 1, 1 }, + { 1, 1, 0, 1, -1 }, + + /* 561-570 */ + { 1, 1, -2, -6, -2 }, + { 1, 0, 0, -3, -1 }, + { 1, 0, -4, -2, -1 }, + { 1, 0, -2, -6, -1 }, + { 1, -2, 2, 2, 1 }, + { 1, -2, -2, 2, -1 }, + { 1, -1, -2, -4, -1 }, + { 0, 2, 0, 0, 2 }, + { 0, 1, 2, -4, 2 }, + { 0, 1, -2, 4, -1 }, + + /* 571-580 */ + { 5, 0, 0, 0, 0 }, + { 3, 0, 0, -3, 0 }, + { 2, 2, 0, -4, 0 }, + { 1, -1, 2, 2, 0 }, + { 0, 1, 0, 3, 0 }, + { 4, 0, -2, 0, -1 }, + { 3, 0, -2, -6, -1 }, + { 3, 0, -2, -1, -1 }, + { 2, 1, 2, 2, 1 }, + { 2, 1, 0, 2, 1 }, + + /* 581-590 */ + { 2, 0, 2, 4, 1 }, + { 2, 0, 2, -6, 1 }, + { 2, 0, 2, -2, -1 }, + { 2, 0, 0, -6, -1 }, + { 2, -1, -2, -2, -1 }, + { 1, 2, 2, 0, 1 }, + { 1, 2, 0, 0, 1 }, + { 1, 0, 4, 0, 1 }, + { 1, 0, 2, -6, 1 }, + { 1, 0, 2, -4, -1 }, + + /* 591-600 */ + { 1, 0, -1, -2, -1 }, + { 1, -1, 2, 4, 1 }, + { 1, -1, 2, -3, 1 }, + { 1, -1, 0, 4, 1 }, + { 1, -1, -2, 1, -1 }, + { 0, 1, 2, -2, 3 }, + { 3, 0, 0, -2, 0 }, + { 1, 0, 1, -2, 0 }, + { 0, 2, 0, -4, 0 }, + { 0, 0, 2, -4, 0 }, + + /* 601-610 */ + { 0, 0, 1, -1, 0 }, + { 0, 0, 0, 6, 0 }, + { 0, 2, 0, 0, -2 }, + { 0, 1, -2, 2, -3 }, + { 4, 0, 0, 2, 0 }, + { 3, 0, 0, -1, 0 }, + { 3, -1, 0, 2, 0 }, + { 2, 1, 0, 1, 0 }, + { 2, 1, 0, -6, 0 }, + { 2, -1, 2, 0, 0 }, + + /* 611-620 */ + { 1, 0, 2, -1, 0 }, + { 1, -1, 0, 1, 0 }, + { 1, -1, -2, -2, 0 }, + { 0, 1, 2, 2, 0 }, + { 0, 0, 2, -3, 0 }, + { 2, 2, 0, -2, -1 }, + { 2, -1, -2, 0, 1 }, + { 1, 2, 2, -4, 1 }, + { 0, 1, 4, -4, 2 }, + { 0, 0, 0, 3, 2 }, + + /* 621-630 */ + { 5, 0, 2, 0, 1 }, + { 4, 1, 2, -2, 2 }, + { 4, 0, -2, -2, 0 }, + { 3, 1, 2, 2, 2 }, + { 3, 1, 0, -2, 0 }, + { 3, 1, -2, -6, -2 }, + { 3, 0, 0, 0, -2 }, + { 3, 0, -2, -4, -2 }, + { 3, -1, 0, -3, 0 }, + { 3, -1, 0, -2, 0 }, + + /* 631-640 */ + { 2, 1, 2, 0, 0 }, + { 2, 1, 2, -4, 2 }, + { 2, 1, 2, -2, 0 }, + { 2, 1, 0, -3, 0 }, + { 2, 1, -2, 0, -2 }, + { 2, 0, 0, -4, 2 }, + { 2, 0, 0, -4, -2 }, + { 2, 0, -2, -5, -2 }, + { 2, -1, 2, 4, 2 }, + { 2, -1, 0, -2, 2 }, + + /* 641-650 */ + { 1, 3, -2, -2, -2 }, + { 1, 1, 0, 0, -2 }, + { 1, 1, 0, -6, 0 }, + { 1, 1, -2, 1, -2 }, + { 1, 1, -2, -1, -2 }, + { 1, 0, 2, 1, 0 }, + { 1, 0, 0, 3, 0 }, + { 1, 0, 0, -4, 2 }, + { 1, 0, -2, 4, -2 }, + { 1, -2, 0, -1, 0 }, + + /* 651-NFLS */ + { 0, 1, -4, 2, -1 }, + { 1, 0, -2, 0, -3 }, + { 0, 0, 4, -4, 4 } + }; + +/* Number of frequencies: luni-solar */ + static const int NFLS = (int) (sizeof mfals / sizeof (int) / 5); + +/* Fundamental-argument multipliers: planetary terms */ + static const int mfapl[][14] = { + + /* 1-10 */ + { 0, 0, 1, -1, 1, 0, 0, -1, 0, -2, 5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 1, 0, -8, 12, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 8,-16, 4, 5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, -1, 2, 0, 0, 0, 0, 0 }, + + /* 11-20 */ + { 0, 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 2, -5, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -8, 3, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 6, -8, 3, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 0 }, + + /* 21-30 */ + { 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 1, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 1, -1, 1, 0, 0, 0, -2, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, -1, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1 }, + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + + /* 31-40 */ + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 1 }, + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, -1, 0, 0, 0 }, + + /* 41-50 */ + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, -2, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 2 }, + { 1, 0, 0, 0, 0, 0,-18, 16, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 2 }, + + /* 51-60 */ + { 0, 0, 1, -1, 1, 0, -5, 7, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0,-10, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 0, 0, -5, 6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -1, 0, 0, 0, 2 }, + { 1, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1 }, + { 1, 0, -2, 0, -2, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 2, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + + /* 61-70 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 8,-16, 4, 5, 0, 0, -2 }, + { 0, 0, 1, -1, 1, 0, 0, 3, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8,-11, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 8,-16, 4, 5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0 }, + + /* 71-80 */ + { 0, 0, 0, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 8,-15, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, -2, 0, 0, 0, 2 }, + { 0, 0, 1, -1, 1, 0, 0, -5, 8, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 0 }, + + /* 81-90 */ + { 2, 0, 0, -2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0, -1 }, + { 2, 0, 0, -2, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 8,-13, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0, -2, 5, 0, 0, 0 }, + { 1, 0, 0, -1, 0, 0, -3, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2 }, + { 1, 0, 0, 0, -1, 0,-18, 16, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, -5, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + + /* 91-100 */ + { 1, 0, 0, -2, 0, 0, 19,-21, 3, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, -8, 13, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 7, -9, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2 }, + { 1, 0, 0, 0, 1, 0,-18, 16, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 6,-16, 4, 5, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 3, -7, 0, 0, 0, 0, 0, -2 }, + + /* 101-110 */ + { 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1 }, + { 2, 0, 0, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, -1, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 2 }, + + /* 111-120 */ + { 0, 0, 0, 0, 1, 0, 0, 1, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2 }, + { 0, 0, 2, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, -6, 8, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0 }, + + /* 121-130 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8,-10, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0, -2 }, + { 1, 0, 0, -1, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0 }, + + /* 131-140 */ + { 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, -3, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 1, 0, 2, -3, 0, 0, 0, 0, 0, 0 }, + + /* 141-150 */ + { 1, 0, 0, -1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 9,-11, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 8,-15, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -4, 5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, -1, 0, 0, 0, 2 }, + + /* 151-160 */ + { 1, 0, 0, -1, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, -4, 10, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, -2 }, + { 0, 0, 2, -2, 1, 0, -4, 4, 0, 0, 0, 0, 0, 0 }, + + /* 161-170 */ + { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, -1, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 2 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 2, 0 }, + { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -9, 13, 0, 0, 0, 0, 0 }, + { 2, 0, 2, 0, 2, 0, 0, 2, 0, -3, 0, 0, 0, 0 }, + + /* 171-180 */ + { 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 2, 0, 0, -1, 0, 0, 2, 0, 0, 0 }, + { 1, 0, 0, -1, -1, 0, -3, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1 }, + { 1, 0, 2, 0, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 1, 0, -2, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, -2, 4, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0 }, + + /* 181-190 */ + { 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 2, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, -8, 3, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 6,-10, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 7, -8, 3, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 1, 0, -3, 5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, -5, 7, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 1 }, + + /* 191-200 */ + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 7,-10, 0, 0, 0, 0, 0, -2 }, + { 1, 0, 0, -2, 0, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, -5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 1, -1, 1, 0, 0, -9, 15, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, -1, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0 }, + + /* 201-210 */ + { 0, 0, 0, 0, 0, 0, 0, 1, -4, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -1, 0, 0, 2 }, + { 2, 0, 0, -2, 1, 0, -6, 8, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 1, -1, 1, 0, 3, -6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 8,-14, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + + /* 211-220 */ + { 0, 0, 0, 0, 1, 0, 0, 8,-15, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 1, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 2 }, + { 2, 0, -1, -1, 0, 0, 0, 3, -7, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0 }, + + /* 221-230 */ + { 2, 0, 0, -2, 0, 0, 0, -6, 8, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, 0, -5, 6, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -9, 4, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, -2 }, + + /* 231-240 */ + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -4, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1 }, + { 0, 0, 0, 0, 0, 0, 7,-11, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 3, -5, 4, 0, 0, 0, 0, 2 }, + { 0, 0, 1, -1, 0, 0, 0, -1, 0, -1, 1, 0, 0, 0 }, + { 2, 0, 0, 0, 0, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 8,-15, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 2, 0, 0, -2, 2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, -1 }, + + /* 241-250 */ + { 0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 2, -4, 0, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 3, -5, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, -3, 0, 0, 0, 2 }, + { 0, 0, 2, -2, 2, 0, -8, 11, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -2, 0, 0, 0 }, + + /* 251-260 */ + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -9, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 7, -9, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, -2, -2, -2, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 3, -3, 0, 0, 0, 0, 0, 1 }, + + /* 261-270 */ + { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, -5, 0, 0, 2 }, + { 2, 0, 0, -2, -1, 0, 0, -2, 0, 0, 5, 0, 0, 0 }, + { 2, 0, 0, -2, -1, 0, -6, 8, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8, -8, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, -5, 0, 0, 2 }, + { 0, 0, 0, 0, 1, 0, 3, -7, 4, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + + /* 271-280 */ + { 0, 0, 1, -1, 0, 0, 0, -1, 0, -2, 5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 6,-15, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2 }, + { 1, 0, 0, -1, 0, 0, 0, -3, 4, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, -3, 7, -4, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, 0, -2, 0, 0, 0, 2 }, + + /* 281-290 */ + { 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 2, -2, 2, 0, -5, 6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -8, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 6,-11, 0, 0, 0, 0, -2 }, + + /* 291-300 */ + { 0, 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, -1, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 9,-12, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 1, -1, 0, 0, -8, 12, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -2, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, -1 }, + + /* 301-310 */ + { 0, 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 1, -1, -1, 0, 0, 0, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, -5, 0, 0, 0, 0, -2 }, + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 3, -1, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -9, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 2 }, + + /* 311-320 */ + { 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 3, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 2, -4, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1 }, + { 0, 0, 1, -1, 2, 0, 0, -1, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -9, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2 }, + { 0, 0, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + + /* 321-330 */ + { 0, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, 0, -3, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0 }, + { 2, 0, -1, -1, -1, 0, 0, -1, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 4, -3, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 5,-10, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 8,-13, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 2, -2, 1, -1, 0, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 2, 0, 0 }, + + /* 331-340 */ + { 0, 0, 0, 0, 1, 0, 3, -5, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 9, -9, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -8, 11, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -2, 0, 0, 2, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, -1, 2, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 2, -6, 0, 0, 0, 0, 0, -2 }, + + /* 341-350 */ + { 0, 0, 0, 0, 0, 0, 0, 8,-15, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -2, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 7,-13, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, -2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 0, 0, 2 }, + { 0, 0, 2, -2, 1, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8, -8, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 8,-10, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 1 }, + + /* 351-360 */ + { 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -4, 0, 0, 0, 0 }, + { 2, 0, 0, -2, -1, 0, 0, -5, 6, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 0, 0, -2 }, + { 2, 0, -1, -1, -1, 0, 0, 3, -7, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0 }, + + /* 361-370 */ + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 4, -3, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 6,-11, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 1, 0, 0, -6, 8, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -8, 1, 5, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 6, -5, 0, 0, 0, 0, 2 }, + { 1, 0, -2, -2, -2, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 1 }, + + /* 371-380 */ + { 0, 0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, -2, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, -2, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 1, -6, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 3, -5, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 7,-13, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -2, 0, 0, 0, 2 }, + + /* 381-390 */ + { 0, 0, 1, -1, 0, 0, 0, -1, 0, 0, 2, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, -8, 15, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, -2, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 2, 0, -1, -1, -1, 0, 0, -1, 0, 2, 0, 0, 0, 0 }, + { 1, 0, 2, -2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 1, 0, -1, 1, -1, 0,-18, 17, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 2, 0, 0, 1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 2, -2, -1, 0, -5, 6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 2, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + + /* 391-400 */ + { 0, 0, 0, 0, 1, 0, 2, -2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8,-16, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 }, + { 0, 0, 0, 0, 2, 0, 0, -1, 2, 0, 0, 0, 0, 0 }, + { 2, 0, -1, -1, -2, 0, 0, -1, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 6,-10, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, -2, 4, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 2 }, + { 2, 0, 0, -2, -1, 0, 0, -2, 0, 4, -5, 0, 0, 0 }, + + /* 401-410 */ + { 2, 0, 0, -2, -1, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 2, 0, -1, -1, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, -1, -1, 0, 0, -2, 2, 0, 0, 0, 0, 0 }, + { 1, 0, -1, -1, -1, 0, 20,-20, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 1, -2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -2, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 5, -8, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0 }, + + /* 411-420 */ + { 0, 0, 0, 0, 0, 0, 9,-11, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 5, -3, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, -2, 0, 0, 0 }, + { 0, 0, 1, -1, 2, 0, 0, -1, 0, -2, 5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, 0 }, + + /* 421-430 */ + { 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -8, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -6, 0, 0, 0, 0, -2 }, + { 1, 0, 0, -2, 0, 0, 20,-21, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8,-12, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -4, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 2, 0, 0, -1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8,-12, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 9,-17, 0, 0, 0, 0, 0 }, + + /* 431-440 */ + { 0, 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -8, 1, 5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -6, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -7, 0, 0, 0, 0, -2 }, + { 1, 0, 0, -1, 1, 0, 0, -3, 4, 0, 0, 0, 0, 0 }, + { 1, 0, -2, 0, -2, 0,-10, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, -9, 17, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, -4, 0, 0, 0, 0, 0, -2 }, + { 1, 0, -2, -2, -2, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 1, 0, -1, 1, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + + /* 441-450 */ + { 0, 0, 2, -2, 2, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 2, 0, 0, -1, 0, 0, 1, 0, 0, 0 }, + { 0, 0, 1, -1, 2, 0, -5, 7, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 2, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5,-10, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, -4, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, -5, 0, 0, 0, -2 }, + + /* 451-460 */ + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -5, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -2, 5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -2, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 2, -3, 0, 0, 0, 0, 0, 1 }, + { 1, 0, 0, -2, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -7, 4, 0, 0, 0, 0, 0 }, + { 2, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, -1, 0, 0, -1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 1, 0, -2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 6,-10, 0, 0, 0, 0, -2 }, + + /* 461-470 */ + { 1, 0, 0, -1, 1, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, 1, 0, -1, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -3, 0, 3, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, -5, 5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 1, -3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -4, 6, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 0, 0, -1, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -5, 6, 0, 0, 0, 0, 0, 0 }, + + /* 471-480 */ + { 0, 0, 0, 0, 1, 0, 3, -4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 7,-10, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 5, -5, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 3, -8, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 2, -5, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 7, -9, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 2 }, + + /* 481-490 */ + { 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -8, 3, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -2, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -4, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, -1 }, + { 2, 0, 0, -2, -1, 0, 0, -6, 8, 0, 0, 0, 0, 0 }, + { 2, 0, -1, -1, 1, 0, 0, 3, -7, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -7, 9, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0, 0, -1 }, + + /* 491-500 */ + { 0, 0, 1, -1, 2, 0, -8, 12, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, 2, -2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 7, -8, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 1, 0, 0, -5, 6, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, -1, 0, 0, -2, 0, 3, -1, 0, 0, 0 }, + { 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 1, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 1, 0, 0, -2, -1, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + + /* 501-510 */ + { 1, 0, 0, -1, -1, 0, 0, -3, 4, 0, 0, 0, 0, 0 }, + { 1, 0, -1, 0, -1, 0, -3, 5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -4, 4, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, -8, 11, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 0, 0, 0, -9, 13, 0, 0, 0, 0, 0 }, + { 0, 0, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, 1, -4, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, 0, -1, 0, 1, -3, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, 7,-13, 0, 0, 0, 0, 0 }, + + /* 511-520 */ + { 0, 0, 0, 0, 1, 0, 0, 2, 0, -2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, -2, 2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, -3, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 7,-11, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 6, -6, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 4, -2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -4, 0, 0, 0, 0, 0, 1 }, + + /* 521-530 */ + { 0, 0, 0, 0, 0, 0, 1, -4, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 9,-17, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 7, -7, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -8, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, -7, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -4, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + + /* 531-540 */ + { 2, 0, 0, -2, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, 17,-16, 0, -2, 0, 0, 0, 0 }, + { 1, 0, 0, -1, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 0, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 3, 0, -4, 0, 0, 0, 0 }, + + /* 541-550 */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 2 }, + { 2, 0, 0, -2, 0, 0, 0, -4, 4, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 2, 2, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + + /* 551-560 */ + { 1, 0, 0, -2, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 0, 0, -4, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, -2, 2, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, -4, 5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, -3, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 2, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0 }, + + /* 561-570 */ + { 0, 0, 0, 0, 0, 0, 8, -9, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, -5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -2, 0, 0, 0 }, + { 2, 0, -2, -2, -2, 0, 0, -2, 0, 2, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 1, 0,-10, 3, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, -1, 0,-10, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 2, 0, 2, -3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 2, 0, 2, -2, 0, 0, 0, 0, 0, 0 }, + + /* 571-580 */ + { 0, 0, 2, 0, 2, 0, -2, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, 0, 2, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, 0, -1, 0, 2, 0, 0, 0, 0 }, + { 2, 0, 2, -2, 2, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 2, 0, 1, -3, 1, 0, -6, 7, 0, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, 2, -5, 0, 0, 0, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 5, -5, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 1, 5, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 0, 5, 0, 0, 0 }, + + /* 581-590 */ + { 2, 0, 0, -2, 0, 0, 0, -2, 0, 0, 2, 0, 0, 0 }, + { 2, 0, 0, -2, 0, 0, -4, 4, 0, 0, 0, 0, 0, 0 }, + { 2, 0, -2, 0, -2, 0, 0, 5, -9, 0, 0, 0, 0, 0 }, + { 2, 0, -1, -1, 0, 0, 0, -1, 0, 3, 0, 0, 0, 0 }, + { 1, 0, 2, 0, 2, 0, 1, -1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 2, 0, 2, 0, 0, 4, -8, 3, 0, 0, 0, 0 }, + { 1, 0, 2, 0, 2, 0, 0, -4, 8, -3, 0, 0, 0, 0 }, + { 1, 0, 2, 0, 2, 0, -1, 1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 2, -2, 2, 0, -3, 3, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0 }, + + /* 591-600 */ + { 1, 0, 0, 0, 0, 0, 0, -2, 0, 3, 0, 0, 0, 0 }, + { 1, 0, 0, -2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0 }, + { 1, 0, -2, -2, -2, 0, 0, 1, 0, -1, 0, 0, 0, 0 }, + { 1, 0, -1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 1, 0, -1, -1, 0, 0, 0, 8,-15, 0, 0, 0, 0, 0 }, + { 0, 0, 2, 2, 2, 0, 0, 2, 0, -2, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 1, -1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0, -2, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 1, 0, 0,-10, 15, 0, 0, 0, 0, 0 }, + { 0, 0, 2, -2, 0, -1, 0, 2, 0, 0, 0, 0, 0, 0 }, + + /* 601-610 */ + { 0, 0, 1, -1, 2, 0, 0, -1, 0, 0, -1, 0, 0, 0 }, + { 0, 0, 1, -1, 2, 0, -3, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -4, 6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, 0, -1, 0, 0, -2, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, -1, -1, 0, -5, 7, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 2, 0, 0, 0, 2, 0, -2, 0, 0, 0, 0 }, + + /* 611-620 */ + { 0, 0, 0, 2, 0, 0, -2, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 2, 0, -3, 5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 0, -1, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 9,-13, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 8,-14, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 8,-11, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 6, -8, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 6, -7, 0, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0, -2 }, + + /* 621-630 */ + { 0, 0, 0, 0, 0, 0, 5, -6, -4, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 5, -4, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 4, -8, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 4, -5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 3, -3, 0, 2, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 7,-12, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 6, -9, 0, 0, 0, 0, -2 }, + + /* 631-640 */ + { 0, 0, 0, 0, 0, 0, 0, 6, -8, 1, 5, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 6, -4, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 6,-10, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5, 0, -4, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -9, 0, 0, 0, 0, -1 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -8, 3, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -7, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 5, -6, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 5,-16, 4, 5, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 5,-13, 0, 0, 0, 0, -2 }, + + /* 641-650 */ + { 0, 0, 0, 0, 0, 0, 0, 3, 0, -5, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -9, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 3, -7, 0, 0, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -3, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 2, -8, 1, 5, 0, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, -5, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -3, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, -3, 5, 0, 0, 0 }, + + /* 651-NFPL */ + { 0, 0, 0, 0, 0, 0, 0, 1, -3, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -6, 3, 0, -2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 } + }; + +/* Number of frequencies: planetary */ + static const int NFPL = (int) (sizeof mfapl / sizeof (int) / 14); + +/* Pointers into amplitudes array, one pointer per frequency */ + static const int nc[] = { + + /* 1-100 */ + 1, 21, 37, 51, 65, 79, 91, 103, 115, 127, + 139, 151, 163, 172, 184, 196, 207, 219, 231, 240, + 252, 261, 273, 285, 297, 309, 318, 327, 339, 351, + 363, 372, 384, 396, 405, 415, 423, 435, 444, 452, + 460, 467, 474, 482, 490, 498, 506, 513, 521, 528, + 536, 543, 551, 559, 566, 574, 582, 590, 597, 605, + 613, 620, 628, 636, 644, 651, 658, 666, 674, 680, + 687, 695, 702, 710, 717, 725, 732, 739, 746, 753, + 760, 767, 774, 782, 790, 798, 805, 812, 819, 826, + 833, 840, 846, 853, 860, 867, 874, 881, 888, 895, + + /* 101-200 */ + 901, 908, 914, 921, 928, 934, 941, 948, 955, 962, + 969, 976, 982, 989, 996, 1003, 1010, 1017, 1024, 1031, + 1037, 1043, 1050, 1057, 1064, 1071, 1078, 1084, 1091, 1098, + 1104, 1112, 1118, 1124, 1131, 1138, 1145, 1151, 1157, 1164, + 1171, 1178, 1185, 1192, 1199, 1205, 1212, 1218, 1226, 1232, + 1239, 1245, 1252, 1259, 1266, 1272, 1278, 1284, 1292, 1298, + 1304, 1310, 1316, 1323, 1329, 1335, 1341, 1347, 1353, 1359, + 1365, 1371, 1377, 1383, 1389, 1396, 1402, 1408, 1414, 1420, + 1426, 1434, 1440, 1446, 1452, 1459, 1465, 1471, 1477, 1482, + 1488, 1493, 1499, 1504, 1509, 1514, 1520, 1527, 1532, 1538, + + /* 201-300 */ + 1543, 1548, 1553, 1558, 1564, 1569, 1574, 1579, 1584, 1589, + 1594, 1596, 1598, 1600, 1602, 1605, 1608, 1610, 1612, 1617, + 1619, 1623, 1625, 1627, 1629, 1632, 1634, 1640, 1642, 1644, + 1646, 1648, 1650, 1652, 1654, 1658, 1660, 1662, 1664, 1668, + 1670, 1672, 1673, 1675, 1679, 1681, 1683, 1684, 1686, 1688, + 1690, 1693, 1695, 1697, 1701, 1703, 1705, 1707, 1709, 1711, + 1712, 1715, 1717, 1721, 1723, 1725, 1727, 1729, 1731, 1733, + 1735, 1737, 1739, 1741, 1743, 1745, 1747, 1749, 1751, 1753, + 1755, 1757, 1759, 1761, 1762, 1764, 1766, 1768, 1769, 1771, + 1773, 1775, 1777, 1779, 1781, 1783, 1785, 1787, 1788, 1790, + + /* 301-400 */ + 1792, 1794, 1796, 1798, 1800, 1802, 1804, 1806, 1807, 1809, + 1811, 1815, 1817, 1819, 1821, 1823, 1825, 1827, 1829, 1831, + 1833, 1835, 1837, 1839, 1840, 1842, 1844, 1848, 1850, 1852, + 1854, 1856, 1858, 1859, 1860, 1862, 1864, 1866, 1868, 1869, + 1871, 1873, 1875, 1877, 1879, 1881, 1883, 1885, 1887, 1889, + 1891, 1892, 1896, 1898, 1900, 1901, 1903, 1905, 1907, 1909, + 1910, 1911, 1913, 1915, 1919, 1921, 1923, 1927, 1929, 1931, + 1933, 1935, 1937, 1939, 1943, 1945, 1947, 1948, 1949, 1951, + 1953, 1955, 1957, 1958, 1960, 1962, 1964, 1966, 1968, 1970, + 1971, 1973, 1974, 1975, 1977, 1979, 1980, 1981, 1982, 1984, + + /* 401-500 */ + 1986, 1988, 1990, 1992, 1994, 1995, 1997, 1999, 2001, 2003, + 2005, 2007, 2008, 2009, 2011, 2013, 2015, 2017, 2019, 2021, + 2023, 2024, 2025, 2027, 2029, 2031, 2033, 2035, 2037, 2041, + 2043, 2045, 2046, 2047, 2049, 2051, 2053, 2055, 2056, 2057, + 2059, 2061, 2063, 2065, 2067, 2069, 2070, 2071, 2072, 2074, + 2076, 2078, 2080, 2082, 2084, 2086, 2088, 2090, 2092, 2094, + 2095, 2096, 2097, 2099, 2101, 2105, 2106, 2107, 2108, 2109, + 2110, 2111, 2113, 2115, 2119, 2121, 2123, 2125, 2127, 2129, + 2131, 2133, 2135, 2136, 2137, 2139, 2141, 2143, 2145, 2147, + 2149, 2151, 2153, 2155, 2157, 2159, 2161, 2163, 2165, 2167, + + /* 501-600 */ + 2169, 2171, 2173, 2175, 2177, 2179, 2181, 2183, 2185, 2186, + 2187, 2188, 2192, 2193, 2195, 2197, 2199, 2201, 2203, 2205, + 2207, 2209, 2211, 2213, 2217, 2219, 2221, 2223, 2225, 2227, + 2229, 2231, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, + 2241, 2244, 2246, 2248, 2250, 2252, 2254, 2256, 2258, 2260, + 2262, 2264, 2266, 2268, 2270, 2272, 2274, 2276, 2278, 2280, + 2282, 2284, 2286, 2288, 2290, 2292, 2294, 2296, 2298, 2300, + 2302, 2303, 2304, 2305, 2306, 2307, 2309, 2311, 2313, 2315, + 2317, 2319, 2321, 2323, 2325, 2327, 2329, 2331, 2333, 2335, + 2337, 2341, 2343, 2345, 2347, 2349, 2351, 2352, 2355, 2356, + + /* 601-700 */ + 2357, 2358, 2359, 2361, 2363, 2364, 2365, 2366, 2367, 2368, + 2369, 2370, 2371, 2372, 2373, 2374, 2376, 2378, 2380, 2382, + 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2393, + 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, 2402, 2403, + 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, + 2414, 2415, 2417, 2418, 2430, 2438, 2445, 2453, 2460, 2468, + 2474, 2480, 2488, 2496, 2504, 2512, 2520, 2527, 2535, 2543, + 2550, 2558, 2566, 2574, 2580, 2588, 2596, 2604, 2612, 2619, + 2627, 2634, 2642, 2648, 2656, 2664, 2671, 2679, 2685, 2693, + 2701, 2709, 2717, 2725, 2733, 2739, 2747, 2753, 2761, 2769, + + /* 701-800 */ + 2777, 2785, 2793, 2801, 2809, 2817, 2825, 2833, 2841, 2848, + 2856, 2864, 2872, 2878, 2884, 2892, 2898, 2906, 2914, 2922, + 2930, 2938, 2944, 2952, 2958, 2966, 2974, 2982, 2988, 2996, + 3001, 3009, 3017, 3025, 3032, 3039, 3045, 3052, 3059, 3067, + 3069, 3076, 3083, 3090, 3098, 3105, 3109, 3111, 3113, 3120, + 3124, 3128, 3132, 3136, 3140, 3144, 3146, 3150, 3158, 3161, + 3165, 3166, 3168, 3172, 3176, 3180, 3182, 3185, 3189, 3193, + 3194, 3197, 3200, 3204, 3208, 3212, 3216, 3219, 3221, 3222, + 3226, 3230, 3234, 3238, 3242, 3243, 3247, 3251, 3254, 3258, + 3262, 3266, 3270, 3274, 3275, 3279, 3283, 3287, 3289, 3293, + + /* 801-900 */ + 3296, 3300, 3303, 3307, 3311, 3315, 3319, 3321, 3324, 3327, + 3330, 3334, 3338, 3340, 3342, 3346, 3350, 3354, 3358, 3361, + 3365, 3369, 3373, 3377, 3381, 3385, 3389, 3393, 3394, 3398, + 3402, 3406, 3410, 3413, 3417, 3421, 3425, 3429, 3433, 3435, + 3439, 3443, 3446, 3450, 3453, 3457, 3458, 3461, 3464, 3468, + 3472, 3476, 3478, 3481, 3485, 3489, 3493, 3497, 3501, 3505, + 3507, 3511, 3514, 3517, 3521, 3524, 3525, 3527, 3529, 3533, + 3536, 3540, 3541, 3545, 3548, 3551, 3555, 3559, 3563, 3567, + 3569, 3570, 3574, 3576, 3578, 3582, 3586, 3590, 3593, 3596, + 3600, 3604, 3608, 3612, 3616, 3620, 3623, 3626, 3630, 3632, + + /* 901-1000 */ + 3636, 3640, 3643, 3646, 3648, 3652, 3656, 3660, 3664, 3667, + 3669, 3671, 3675, 3679, 3683, 3687, 3689, 3693, 3694, 3695, + 3699, 3703, 3705, 3707, 3710, 3713, 3717, 3721, 3725, 3729, + 3733, 3736, 3740, 3744, 3748, 3752, 3754, 3757, 3759, 3763, + 3767, 3770, 3773, 3777, 3779, 3783, 3786, 3790, 3794, 3798, + 3801, 3805, 3809, 3813, 3817, 3821, 3825, 3827, 3831, 3835, + 3836, 3837, 3840, 3844, 3848, 3852, 3856, 3859, 3863, 3867, + 3869, 3871, 3875, 3879, 3883, 3887, 3890, 3894, 3898, 3901, + 3905, 3909, 3913, 3917, 3921, 3922, 3923, 3924, 3926, 3930, + 3932, 3936, 3938, 3940, 3944, 3948, 3952, 3956, 3959, 3963, + + /* 1001-1100 */ + 3965, 3969, 3973, 3977, 3979, 3981, 3982, 3986, 3989, 3993, + 3997, 4001, 4004, 4006, 4009, 4012, 4016, 4020, 4024, 4026, + 4028, 4032, 4036, 4040, 4044, 4046, 4050, 4054, 4058, 4060, + 4062, 4063, 4064, 4068, 4071, 4075, 4077, 4081, 4083, 4087, + 4089, 4091, 4095, 4099, 4101, 4103, 4105, 4107, 4111, 4115, + 4119, 4123, 4127, 4129, 4131, 4135, 4139, 4141, 4143, 4145, + 4149, 4153, 4157, 4161, 4165, 4169, 4173, 4177, 4180, 4183, + 4187, 4191, 4195, 4198, 4201, 4205, 4209, 4212, 4213, 4216, + 4217, 4221, 4223, 4226, 4230, 4234, 4236, 4240, 4244, 4248, + 4252, 4256, 4258, 4262, 4264, 4266, 4268, 4270, 4272, 4276, + + /* 1101-1200 */ + 4279, 4283, 4285, 4287, 4289, 4293, 4295, 4299, 4300, 4301, + 4305, 4309, 4313, 4317, 4319, 4323, 4325, 4329, 4331, 4333, + 4335, 4337, 4341, 4345, 4349, 4351, 4353, 4357, 4361, 4365, + 4367, 4369, 4373, 4377, 4381, 4383, 4387, 4389, 4391, 4395, + 4399, 4403, 4407, 4411, 4413, 4414, 4415, 4418, 4419, 4421, + 4423, 4427, 4429, 4431, 4433, 4435, 4437, 4439, 4443, 4446, + 4450, 4452, 4456, 4458, 4460, 4462, 4466, 4469, 4473, 4477, + 4481, 4483, 4487, 4489, 4491, 4493, 4497, 4499, 4501, 4504, + 4506, 4510, 4513, 4514, 4515, 4518, 4521, 4522, 4525, 4526, + 4527, 4530, 4533, 4534, 4537, 4541, 4542, 4543, 4544, 4545, + + /* 1201-1300 */ + 4546, 4547, 4550, 4553, 4554, 4555, 4558, 4561, 4564, 4567, + 4568, 4571, 4574, 4575, 4578, 4581, 4582, 4585, 4586, 4588, + 4590, 4592, 4596, 4598, 4602, 4604, 4608, 4612, 4613, 4616, + 4619, 4622, 4623, 4624, 4625, 4626, 4629, 4632, 4633, 4636, + 4639, 4640, 4641, 4642, 4643, 4644, 4645, 4648, 4649, 4650, + 4651, 4652, 4653, 4656, 4657, 4660, 4661, 4664, 4667, 4670, + 4671, 4674, 4675, 4676, 4677, 4678, 4681, 4682, 4683, 4684, + 4687, 4688, 4689, 4692, 4693, 4696, 4697, 4700, 4701, 4702, + 4703, 4704, 4707, 4708, 4711, 4712, 4715, 4716, 4717, 4718, + 4719, 4720, 4721, 4722, 4723, 4726, 4729, 4730, 4733, 4736, + + /* 1301-(NFLS+NFPL) */ + 4737, 4740, 4741, 4742, 4745, 4746, 4749, 4752, 4753 + }; + +/* Amplitude coefficients (microarcsec); indexed using the nc array. */ + static const double a[] = { + + /* 1-105 */ + -6844318.44, 9205236.26,1328.67,1538.18, 205833.11, + 153041.79, -3309.73, 853.32,2037.98, -2301.27, + 81.46, 120.56, -20.39, -15.22, 1.73, -1.61, -0.10, 0.11, + -0.02, -0.02, -523908.04, 573033.42,-544.75,-458.66, + 12814.01, 11714.49, 198.97,-290.91, 155.74,-143.27, + -2.75, -1.03, -1.27, -1.16, 0.00, -0.01, -90552.22, + 97846.69, 111.23, 137.41,2187.91,2024.68, 41.44, -51.26, + 26.92, -24.46, -0.46, -0.28, -0.22, -0.20, 82168.76, + -89618.24, -27.64, -29.05, -2004.36, -1837.32, + -36.07, 48.00, -24.43, 22.41, 0.47, 0.24, 0.20, 0.18, + 58707.02,7387.02, 470.05,-192.40, 164.33, -1312.21, + -179.73, -28.93, -17.36, -1.83, -0.50, 3.57, 0.00, 0.13, + -20557.78, 22438.42, -20.84, -17.40, 501.82, 459.68, + 59.20, -67.30, 6.08, -5.61, -1.36, -1.19, 28288.28, + -674.99, -34.69, 35.80, -15.07,-632.54, -11.19, 0.78, -8.41, + 0.17, 0.01, 0.07, -15406.85, 20069.50, 15.12, + + /* 106-219 */ + 31.80, 448.76, 344.50, -5.77, 1.41, 4.59, -5.02, 0.17, + 0.24, -11991.74, 12902.66, 32.46, 36.70, 288.49, + 268.14, 5.70, -7.06, 3.57, -3.23, -0.06, -0.04, + -8584.95, -9592.72, 4.42, -13.20,-214.50, 192.06, + 23.87, 29.83, 2.54, 2.40, 0.60, -0.48,5095.50, + -6918.22, 7.19, 3.92,-154.91,-113.94, 2.86, -1.04, + -1.52, 1.73, -0.07, -0.10, -4910.93, -5331.13, + 0.76, 0.40,-119.21, 109.81, 2.16, 3.20, 1.46, 1.33, + 0.04, -0.02, -6245.02,-123.48, -6.68, -8.20, -2.76, + 139.64, 2.71, 0.15, 1.86,2511.85, -3323.89, 1.07, + -0.90, -74.33, -56.17, 1.16, -0.01, -0.75, 0.83, -0.02, + -0.04,2307.58,3143.98, -7.52, 7.50, 70.31, -51.60, 1.46, + 0.16, -0.69, -0.79, 0.02, -0.05,2372.58,2554.51, 5.93, + -6.60, 57.12, -53.05, -0.96, -1.24, -0.71, -0.64, -0.01, + -2053.16,2636.13, 5.13, 7.80, 58.94, 45.91, -0.42, + -0.12, 0.61, -0.66, 0.02, 0.03, -1825.49, + + /* 220-339 */ + -2423.59, 1.23, -2.00, -54.19, 40.82, -1.07, -1.02, + 0.54, 0.61, -0.04, 0.04,2521.07,-122.28, -5.97, 2.90, + -2.73, -56.37, -0.82, 0.13, -0.75, -1534.09,1645.01, + 6.29, 6.80, 36.78, 34.30, 0.92, -1.25, 0.46, -0.41, + -0.02, -0.01,1898.27, 47.70, -0.72, 2.50, 1.07, -42.45, + -0.94, 0.02, -0.56, -1292.02, -1387.00, 0.00, + 0.00, -31.01, 28.89, 0.68, 0.00, 0.38, 0.35, -0.01, + -0.01, -1234.96,1323.81, 5.21, 5.90, 29.60, 27.61, + 0.74, -1.22, 0.37, -0.33, -0.02, -0.01,1137.48, + -1233.89, -0.04, -0.30, -27.59, -25.43, -0.61, 1.00, + -0.34, 0.31, 0.01, 0.01,-813.13, -1075.60, 0.40, + 0.30, -24.05, 18.18, -0.40, -0.01, 0.24, 0.27, -0.01, + 0.01,1163.22, -60.90, -2.94, 1.30, -1.36, -26.01, -0.58, + 0.07, -0.35,1029.70, -55.55, -2.63, 1.10, -1.25, -23.02, + -0.52, 0.06, -0.31,-556.26, 852.85, 3.16, -4.48, 19.06, + 12.44, -0.81, -0.27, 0.17, -0.21, 0.00, 0.02,-603.52, + + /* 340-467 */ + -800.34, 0.44, 0.10, -17.90, 13.49, -0.08, -0.01, 0.18, + 0.20, -0.01, 0.01,-628.24, 684.99, -0.64, -0.50, 15.32, + 14.05, 3.18, -4.19, 0.19, -0.17, -0.09, -0.07,-866.48, + -16.26, 0.52, -1.30, -0.36, 19.37, 0.43, -0.01, 0.26, + -512.37, 695.54, -1.47, -1.40, 15.55, 11.46, -0.16, 0.03, + 0.15, -0.17, 0.01, 0.01, 506.65, 643.75, 2.54, -2.62, + 14.40, -11.33, -0.77, -0.06, -0.15, -0.16, 0.00, 0.01, + 664.57, 16.81, -0.40, 1.00, 0.38, -14.86, -3.71, -0.09, + -0.20, 405.91, 522.11, 0.99, -1.50, 11.67, -9.08, -0.25, + -0.02, -0.12, -0.13,-305.78, 326.60, 1.75, 1.90, 7.30, + 6.84, 0.20, -0.04, 300.99,-325.03, -0.44, -0.50, -7.27, + -6.73, -1.01, 0.01, 0.00, 0.08, 0.00, 0.02, 438.51, + 10.47, -0.56, -0.20, 0.24, -9.81, -0.24, 0.01, -0.13, + -264.02, 335.24, 0.99, 1.40, 7.49, 5.90, -0.27, -0.02, + 284.09, 307.03, 0.32, -0.40, 6.87, -6.35, -0.99, -0.01, + -250.54, 327.11, 0.08, 0.40, 7.31, 5.60, -0.30, 230.72, + + /* 468-595 */ + -304.46, 0.08, -0.10, -6.81, -5.16, 0.27, 229.78, 304.17, + -0.60, 0.50, 6.80, -5.14, 0.33, 0.01, 256.30,-276.81, + -0.28, -0.40, -6.19, -5.73, -0.14, 0.01,-212.82, 269.45, + 0.84, 1.20, 6.02, 4.76, 0.14, -0.02, 196.64, 272.05, + -0.84, 0.90, 6.08, -4.40, 0.35, 0.02, 188.95, 272.22, + -0.12, 0.30, 6.09, -4.22, 0.34,-292.37, -5.10, -0.32, + -0.40, -0.11, 6.54, 0.14, 0.01, 161.79,-220.67, 0.24, + 0.10, -4.93, -3.62, -0.08, 261.54, -19.94, -0.95, 0.20, + -0.45, -5.85, -0.13, 0.02, 142.16,-190.79, 0.20, 0.10, + -4.27, -3.18, -0.07, 187.95, -4.11, -0.24, 0.30, -0.09, + -4.20, -0.09, 0.01, 0.00, 0.00, -79.08, 167.90, 0.04, + 0.00, 3.75, 1.77, 121.98, 131.04, -0.08, 0.10, 2.93, + -2.73, -0.06,-172.95, -8.11, -0.40, -0.20, -0.18, 3.87, + 0.09, 0.01,-160.15, -55.30, -14.04, 13.90, -1.23, 3.58, + 0.40, 0.31,-115.40, 123.20, 0.60, 0.70, 2.75, 2.58, + 0.08, -0.01,-168.26, -2.00, 0.20, -0.20, -0.04, 3.76, + + /* 596-723 */ + 0.08,-114.49, 123.20, 0.32, 0.40, 2.75, 2.56, 0.07, + -0.01, 112.14, 120.70, 0.28, -0.30, 2.70, -2.51, -0.07, + -0.01, 161.34, 4.03, 0.20, 0.20, 0.09, -3.61, -0.08, + 91.31, 126.64, -0.40, 0.40, 2.83, -2.04, -0.04, 0.01, + 105.29, 112.90, 0.44, -0.50, 2.52, -2.35, -0.07, -0.01, + 98.69,-106.20, -0.28, -0.30, -2.37, -2.21, -0.06, 0.01, + 86.74,-112.94, -0.08, -0.20, -2.53, -1.94, -0.05,-134.81, + 3.51, 0.20, -0.20, 0.08, 3.01, 0.07, 79.03, 107.31, + -0.24, 0.20, 2.40, -1.77, -0.04, 0.01, 132.81, -10.77, + -0.52, 0.10, -0.24, -2.97, -0.07, 0.01,-130.31, -0.90, + 0.04, 0.00, 0.00, 2.91, -78.56, 85.32, 0.00, 0.00, + 1.91, 1.76, 0.04, 0.00, 0.00, -41.53, 89.10, 0.02, + 0.00, 1.99, 0.93, 66.03, -71.00, -0.20, -0.20, -1.59, + -1.48, -0.04, 60.50, 64.70, 0.36, -0.40, 1.45, -1.35, + -0.04, -0.01, -52.27, -70.01, 0.00, 0.00, -1.57, 1.17, + 0.03, -52.95, 66.29, 0.32, 0.40, 1.48, 1.18, 0.04, + + /* 724-851 */ + -0.01, 51.02, 67.25, 0.00, 0.00, 1.50, -1.14, -0.03, + -55.66, -60.92, 0.16, -0.20, -1.36, 1.24, 0.03, -54.81, + -59.20, -0.08, 0.20, -1.32, 1.23, 0.03, 51.32, -55.60, + 0.00, 0.00, -1.24, -1.15, -0.03, 48.29, 51.80, 0.20, + -0.20, 1.16, -1.08, -0.03, -45.59, -49.00, -0.12, 0.10, + -1.10, 1.02, 0.03, 40.54, -52.69, -0.04, -0.10, -1.18, + -0.91, -0.02, -40.58, -49.51, -1.00, 1.00, -1.11, 0.91, + 0.04, 0.02, -43.76, 46.50, 0.36, 0.40, 1.04, 0.98, + 0.03, -0.01, 62.65, -5.00, -0.24, 0.00, -0.11, -1.40, + -0.03, 0.01, -38.57, 49.59, 0.08, 0.10, 1.11, 0.86, + 0.02, -33.22, -44.04, 0.08, -0.10, -0.98, 0.74, 0.02, + 37.15, -39.90, -0.12, -0.10, -0.89, -0.83, -0.02, 36.68, + -39.50, -0.04, -0.10, -0.88, -0.82, -0.02, -53.22, -3.91, + -0.20, 0.00, -0.09, 1.19, 0.03, 32.43, -42.19, -0.04, + -0.10, -0.94, -0.73, -0.02, -51.00, -2.30, -0.12, -0.10, + 0.00, 1.14, -29.53, -39.11, 0.04, 0.00, -0.87, 0.66, + + /* 852-979 */ + 0.02, 28.50, -38.92, -0.08, -0.10, -0.87, -0.64, -0.02, + 26.54, 36.95, -0.12, 0.10, 0.83, -0.59, -0.01, 26.54, + 34.59, 0.04, -0.10, 0.77, -0.59, -0.02, 28.35, -32.55, + -0.16, 0.20, -0.73, -0.63, -0.01, -28.00, 30.40, 0.00, + 0.00, 0.68, 0.63, 0.01, -27.61, 29.40, 0.20, 0.20, + 0.66, 0.62, 0.02, 40.33, 0.40, -0.04, 0.10, 0.00, + -0.90, -23.28, 31.61, -0.08, -0.10, 0.71, 0.52, 0.01, + 37.75, 0.80, 0.04, 0.10, 0.00, -0.84, 23.66, 25.80, + 0.00, 0.00, 0.58, -0.53, -0.01, 21.01, -27.91, 0.00, + 0.00, -0.62, -0.47, -0.01, -34.81, 2.89, 0.04, 0.00, + 0.00, 0.78, -23.49, -25.31, 0.00, 0.00, -0.57, 0.53, + 0.01, -23.47, 25.20, 0.16, 0.20, 0.56, 0.52, 0.02, + 19.58, 27.50, -0.12, 0.10, 0.62, -0.44, -0.01, -22.67, + -24.40, -0.08, 0.10, -0.55, 0.51, 0.01, -19.97, 25.00, + 0.12, 0.20, 0.56, 0.45, 0.01, 21.28, -22.80, -0.08, + -0.10, -0.51, -0.48, -0.01, -30.47, 0.91, 0.04, 0.00, + + /* 980-1107 */ + 0.00, 0.68, 18.58, 24.00, 0.04, -0.10, 0.54, -0.42, + -0.01, -18.02, 24.40, -0.04, -0.10, 0.55, 0.40, 0.01, + 17.74, 22.50, 0.08, -0.10, 0.50, -0.40, -0.01, -19.41, + 20.70, 0.08, 0.10, 0.46, 0.43, 0.01, -18.64, 20.11, + 0.00, 0.00, 0.45, 0.42, 0.01, -16.75, 21.60, 0.04, + 0.10, 0.48, 0.37, 0.01, -18.42, -20.00, 0.00, 0.00, + -0.45, 0.41, 0.01, -26.77, 1.41, 0.08, 0.00, 0.00, + 0.60, -26.17, -0.19, 0.00, 0.00, 0.00, 0.59, -15.52, + 20.51, 0.00, 0.00, 0.46, 0.35, 0.01, -25.42, -1.91, + -0.08, 0.00, -0.04, 0.57, 0.45, -17.42, 18.10, 0.00, + 0.00, 0.40, 0.39, 0.01, 16.39, -17.60, -0.08, -0.10, + -0.39, -0.37, -0.01, -14.37, 18.91, 0.00, 0.00, 0.42, + 0.32, 0.01, 23.39, -2.40, -0.12, 0.00, 0.00, -0.52, + 14.32, -18.50, -0.04, -0.10, -0.41, -0.32, -0.01, 15.69, + 17.08, 0.00, 0.00, 0.38, -0.35, -0.01, -22.99, 0.50, + 0.04, 0.00, 0.00, 0.51, 0.00, 0.00, 14.47, -17.60, + + /* 1108-1235 */ + -0.01, 0.00, -0.39, -0.32, -13.33, 18.40, -0.04, -0.10, + 0.41, 0.30, 22.47, -0.60, -0.04, 0.00, 0.00, -0.50, + -12.78, -17.41, 0.04, 0.00, -0.39, 0.29, 0.01, -14.10, + -15.31, 0.04, 0.00, -0.34, 0.32, 0.01, 11.98, 16.21, + -0.04, 0.00, 0.36, -0.27, -0.01, 19.65, -1.90, -0.08, + 0.00, 0.00, -0.44, 19.61, -1.50, -0.08, 0.00, 0.00, + -0.44, 13.41, -14.30, -0.04, -0.10, -0.32, -0.30, -0.01, + -13.29, 14.40, 0.00, 0.00, 0.32, 0.30, 0.01, 11.14, + -14.40, -0.04, 0.00, -0.32, -0.25, -0.01, 12.24, -13.38, + 0.04, 0.00, -0.30, -0.27, -0.01, 10.07, -13.81, 0.04, + 0.00, -0.31, -0.23, -0.01, 10.46, 13.10, 0.08, -0.10, + 0.29, -0.23, -0.01, 16.55, -1.71, -0.08, 0.00, 0.00, + -0.37, 9.75, -12.80, 0.00, 0.00, -0.29, -0.22, -0.01, + 9.11, 12.80, 0.00, 0.00, 0.29, -0.20, 0.00, 0.00, + -6.44, -13.80, 0.00, 0.00, -0.31, 0.14, -9.19, -12.00, + 0.00, 0.00, -0.27, 0.21, -10.30, 10.90, 0.08, 0.10, + + /* 1236-1363 */ + 0.24, 0.23, 0.01, 14.92, -0.80, -0.04, 0.00, 0.00, + -0.33, 10.02, -10.80, 0.00, 0.00, -0.24, -0.22, -0.01, + -9.75, 10.40, 0.04, 0.00, 0.23, 0.22, 0.01, 9.67, + -10.40, -0.04, 0.00, -0.23, -0.22, -0.01, -8.28, -11.20, + 0.04, 0.00, -0.25, 0.19, 13.32, -1.41, -0.08, 0.00, + 0.00, -0.30, 8.27, 10.50, 0.04, 0.00, 0.23, -0.19, + 0.00, 0.00, 13.13, 0.00, 0.00, 0.00, 0.00, -0.29, + -12.93, 0.70, 0.04, 0.00, 0.00, 0.29, 7.91, -10.20, + 0.00, 0.00, -0.23, -0.18, -7.84, -10.00, -0.04, 0.00, + -0.22, 0.18, 7.44, 9.60, 0.00, 0.00, 0.21, -0.17, + -7.64, 9.40, 0.08, 0.10, 0.21, 0.17, 0.01, -11.38, + 0.60, 0.04, 0.00, 0.00, 0.25, -7.48, 8.30, 0.00, + 0.00, 0.19, 0.17, -10.98, -0.20, 0.00, 0.00, 0.00, + 0.25, 10.98, 0.20, 0.00, 0.00, 0.00, -0.25, 7.40, + -7.90, -0.04, 0.00, -0.18, -0.17, -6.09, 8.40, -0.04, + 0.00, 0.19, 0.14, -6.94, -7.49, 0.00, 0.00, -0.17, + + /* 1364-1491 */ + 0.16, 6.92, 7.50, 0.04, 0.00, 0.17, -0.15, 6.20, + 8.09, 0.00, 0.00, 0.18, -0.14, -6.12, 7.80, 0.04, + 0.00, 0.17, 0.14, 5.85, -7.50, 0.00, 0.00, -0.17, + -0.13, -6.48, 6.90, 0.08, 0.10, 0.15, 0.14, 0.01, + 6.32, 6.90, 0.00, 0.00, 0.15, -0.14, 5.61, -7.20, + 0.00, 0.00, -0.16, -0.13, 9.07, 0.00, 0.00, 0.00, + 0.00, -0.20, 5.25, 6.90, 0.00, 0.00, 0.15, -0.12, + -8.47, -0.40, 0.00, 0.00, 0.00, 0.19, 6.32, -5.39, + -1.11, 1.10, -0.12, -0.14, 0.02, 0.02, 5.73, -6.10, + -0.04, 0.00, -0.14, -0.13, 4.70, 6.60, -0.04, 0.00, + 0.15, -0.11, -4.90, -6.40, 0.00, 0.00, -0.14, 0.11, + -5.33, 5.60, 0.04, 0.10, 0.13, 0.12, 0.01, -4.81, + 6.00, 0.04, 0.00, 0.13, 0.11, 5.13, 5.50, 0.04, + 0.00, 0.12, -0.11, 4.50, 5.90, 0.00, 0.00, 0.13, + -0.10, -4.22, 6.10, 0.00, 0.00, 0.14, -4.53, 5.70, + 0.00, 0.00, 0.13, 0.10, 4.18, 5.70, 0.00, 0.00, + + /* 1492-1619 */ + 0.13, -4.75, -5.19, 0.00, 0.00, -0.12, 0.11, -4.06, + 5.60, 0.00, 0.00, 0.13, -3.98, 5.60, -0.04, 0.00, + 0.13, 4.02, -5.40, 0.00, 0.00, -0.12, 4.49, -4.90, + -0.04, 0.00, -0.11, -0.10, -3.62, -5.40, -0.16, 0.20, + -0.12, 0.00, 0.01, 4.38, 4.80, 0.00, 0.00, 0.11, + -6.40, -0.10, 0.00, 0.00, 0.00, 0.14, -3.98, 5.00, + 0.04, 0.00, 0.11, -3.82, -5.00, 0.00, 0.00, -0.11, + -3.71, 5.07, 0.00, 0.00, 0.11, 4.14, 4.40, 0.00, + 0.00, 0.10, -6.01, -0.50, -0.04, 0.00, 0.00, 0.13, + -4.04, 4.39, 0.00, 0.00, 0.10, 3.45, -4.72, 0.00, + 0.00, -0.11, 3.31, 4.71, 0.00, 0.00, 0.11, 3.26, + -4.50, 0.00, 0.00, -0.10, -3.26, -4.50, 0.00, 0.00, + -0.10, -3.34, -4.40, 0.00, 0.00, -0.10, -3.74, -4.00, + 3.70, 4.00, 3.34, -4.30, 3.30, -4.30, -3.66, 3.90, + 0.04, 3.66, 3.90, 0.04, -3.62, -3.90, -3.61, 3.90, + -0.20, 5.30, 0.00, 0.00, 0.12, 3.06, 4.30, 3.30, + + /* 1620-1747 */ + 4.00, 0.40, 0.20, 3.10, 4.10, -3.06, 3.90, -3.30, + -3.60, -3.30, 3.36, 0.01, 3.14, 3.40, -4.57, -0.20, + 0.00, 0.00, 0.00, 0.10, -2.70, -3.60, 2.94, -3.20, + -2.90, 3.20, 2.47, -3.40, 2.55, -3.30, 2.80, -3.08, + 2.51, 3.30, -4.10, 0.30, -0.12, -0.10, 4.10, 0.20, + -2.74, 3.00, 2.46, 3.23, -3.66, 1.20, -0.20, 0.20, + 3.74, -0.40, -2.51, -2.80, -3.74, 2.27, -2.90, 0.00, + 0.00, -2.50, 2.70, -2.51, 2.60, -3.50, 0.20, 3.38, + -2.22, -2.50, 3.26, -0.40, 1.95, -2.60, 3.22, -0.40, + -0.04, -1.79, -2.60, 1.91, 2.50, 0.74, 3.05, -0.04, + 0.08, 2.11, -2.30, -2.11, 2.20, -1.87, -2.40, 2.03, + -2.20, -2.03, 2.20, 2.98, 0.00, 0.00, 2.98, -1.71, + 2.40, 2.94, -0.10, -0.12, 0.10, 1.67, 2.40, -1.79, + 2.30, -1.79, 2.20, -1.67, 2.20, 1.79, -2.00, 1.87, + -1.90, 1.63, -2.10, -1.59, 2.10, 1.55, -2.10, -1.55, + 2.10, -2.59, -0.20, -1.75, -1.90, -1.75, 1.90, -1.83, + + /* 1748-1875 */ + -1.80, 1.51, 2.00, -1.51, -2.00, 1.71, 1.80, 1.31, + 2.10, -1.43, 2.00, 1.43, 2.00, -2.43, -1.51, 1.90, + -1.47, 1.90, 2.39, 0.20, -2.39, 1.39, 1.90, 1.39, + -1.80, 1.47, -1.60, 1.47, -1.60, 1.43, -1.50, -1.31, + 1.60, 1.27, -1.60, -1.27, 1.60, 1.27, -1.60, 2.03, + 1.35, 1.50, -1.39, -1.40, 1.95, -0.20, -1.27, 1.49, + 1.19, 1.50, 1.27, 1.40, 1.15, 1.50, 1.87, -0.10, + -1.12, -1.50, 1.87, -1.11, -1.50, -1.11, -1.50, 0.00, + 0.00, 1.19, 1.40, 1.27, -1.30, -1.27, -1.30, -1.15, + 1.40, -1.23, 1.30, -1.23, -1.30, 1.22, -1.29, 1.07, + -1.40, 1.75, -0.20, -1.03, -1.40, -1.07, 1.20, -1.03, + 1.15, 1.07, 1.10, 1.51, -1.03, 1.10, 1.03, -1.10, + 0.00, 0.00, -1.03, -1.10, 0.91, -1.20, -0.88, -1.20, + -0.88, 1.20, -0.95, 1.10, -0.95, -1.10, 1.43, -1.39, + 0.95, -1.00, -0.95, 1.00, -0.80, 1.10, 0.91, -1.00, + -1.35, 0.88, 1.00, -0.83, 1.00, -0.91, 0.90, 0.91, + + /* 1876-2003 */ + 0.90, 0.88, -0.90, -0.76, -1.00, -0.76, 1.00, 0.76, + 1.00, -0.72, 1.00, 0.84, -0.90, 0.84, 0.90, 1.23, + 0.00, 0.00, -0.52, -1.10, -0.68, 1.00, 1.19, -0.20, + 1.19, 0.76, 0.90, 1.15, -0.10, 1.15, -0.10, 0.72, + -0.90, -1.15, -1.15, 0.68, 0.90, -0.68, 0.90, -1.11, + 0.00, 0.00, 0.20, 0.79, 0.80, -1.11, -0.10, 0.00, + 0.00, -0.48, -1.00, -0.76, -0.80, -0.72, -0.80, -1.07, + -0.10, 0.64, 0.80, -0.64, -0.80, 0.64, 0.80, 0.40, + 0.60, 0.52, -0.50, -0.60, -0.80, -0.71, 0.70, -0.99, + 0.99, 0.56, 0.80, -0.56, 0.80, 0.68, -0.70, 0.68, + 0.70, -0.95, -0.64, 0.70, 0.64, 0.70, -0.60, 0.70, + -0.60, -0.70, -0.91, -0.10, -0.51, 0.76, -0.91, -0.56, + 0.70, 0.88, 0.88, -0.63, -0.60, 0.55, -0.60, -0.80, + 0.80, -0.80, -0.52, 0.60, 0.52, 0.60, 0.52, -0.60, + -0.48, 0.60, 0.48, 0.60, 0.48, 0.60, -0.76, 0.44, + -0.60, 0.52, -0.50, -0.52, 0.50, 0.40, 0.60, -0.40, + + /* 2004-2131 */ + -0.60, 0.40, -0.60, 0.72, -0.72, -0.51, -0.50, -0.48, + 0.50, 0.48, -0.50, -0.48, 0.50, -0.48, 0.50, 0.48, + -0.50, -0.48, -0.50, -0.68, -0.68, 0.44, 0.50, -0.64, + -0.10, -0.64, -0.10, -0.40, 0.50, 0.40, 0.50, 0.40, + 0.50, 0.00, 0.00, -0.40, -0.50, -0.36, -0.50, 0.36, + -0.50, 0.60, -0.60, 0.40, -0.40, 0.40, 0.40, -0.40, + 0.40, -0.40, 0.40, -0.56, -0.56, 0.36, -0.40, -0.36, + 0.40, 0.36, -0.40, -0.36, -0.40, 0.36, 0.40, 0.36, + 0.40, -0.52, 0.52, 0.52, 0.32, 0.40, -0.32, 0.40, + -0.32, 0.40, -0.32, 0.40, 0.32, -0.40, -0.32, -0.40, + 0.32, -0.40, 0.28, -0.40, -0.28, 0.40, 0.28, -0.40, + 0.28, 0.40, 0.48, -0.48, 0.48, 0.36, -0.30, -0.36, + -0.30, 0.00, 0.00, 0.20, 0.40, -0.44, 0.44, -0.44, + -0.44, -0.44, -0.44, 0.32, -0.30, 0.32, 0.30, 0.24, + 0.30, -0.12, -0.10, -0.28, 0.30, 0.28, 0.30, 0.28, + 0.30, 0.28, -0.30, 0.28, -0.30, 0.28, -0.30, 0.28, + + /* 2132-2259 */ + 0.30, -0.28, 0.30, 0.40, 0.40, -0.24, 0.30, 0.24, + -0.30, 0.24, -0.30, -0.24, -0.30, 0.24, 0.30, 0.24, + -0.30, -0.24, 0.30, 0.24, -0.30, -0.24, -0.30, 0.24, + -0.30, 0.24, 0.30, -0.24, 0.30, -0.24, 0.30, 0.20, + -0.30, 0.20, -0.30, 0.20, -0.30, 0.20, 0.30, 0.20, + -0.30, 0.20, -0.30, 0.20, 0.30, 0.20, 0.30, -0.20, + -0.30, 0.20, -0.30, 0.20, -0.30, -0.36, -0.36, -0.36, + -0.04, 0.30, 0.12, -0.10, -0.32, -0.24, 0.20, 0.24, + 0.20, 0.20, -0.20, -0.20, -0.20, -0.20, -0.20, 0.20, + 0.20, 0.20, -0.20, 0.20, 0.20, 0.20, 0.20, -0.20, + -0.20, 0.00, 0.00, -0.20, -0.20, -0.20, 0.20, -0.20, + 0.20, 0.20, -0.20, -0.20, -0.20, 0.20, 0.20, 0.20, + 0.20, 0.20, -0.20, 0.20, -0.20, 0.28, 0.28, 0.28, + 0.28, 0.28, 0.28, -0.28, 0.28, 0.12, 0.00, 0.24, + 0.16, -0.20, 0.16, -0.20, 0.16, -0.20, 0.16, 0.20, + -0.16, 0.20, 0.16, 0.20, -0.16, 0.20, -0.16, 0.20, + + /* 2260-2387 */ + -0.16, 0.20, 0.16, -0.20, 0.16, 0.20, 0.16, -0.20, + -0.16, 0.20, -0.16, -0.20, -0.16, 0.20, 0.16, 0.20, + 0.16, -0.20, 0.16, -0.20, 0.16, 0.20, 0.16, 0.20, + 0.16, 0.20, -0.16, -0.20, 0.16, 0.20, -0.16, 0.20, + 0.16, 0.20, -0.16, -0.20, 0.16, -0.20, 0.16, -0.20, + -0.16, -0.20, 0.24, -0.24, -0.24, 0.24, 0.24, 0.12, + 0.20, 0.12, 0.20, -0.12, -0.20, 0.12, -0.20, 0.12, + -0.20, -0.12, 0.20, -0.12, 0.20, -0.12, -0.20, 0.12, + 0.20, 0.12, 0.20, 0.12, -0.20, -0.12, 0.20, 0.12, + -0.20, -0.12, 0.20, 0.12, 0.20, 0.00, 0.00, -0.12, + 0.20, -0.12, 0.20, 0.12, -0.20, -0.12, 0.20, 0.12, + 0.20, 0.00, -0.21, -0.20, 0.00, 0.00, 0.20, -0.20, + -0.20, -0.20, 0.20, -0.16, -0.10, 0.00, 0.17, 0.16, + 0.16, 0.16, 0.16, -0.16, 0.16, 0.16, -0.16, 0.16, + -0.16, 0.16, 0.12, 0.10, 0.12, -0.10, -0.12, 0.10, + -0.12, 0.10, 0.12, -0.10, -0.12, 0.12, -0.12, 0.12, + + /* 2388-2515 */ + -0.12, 0.12, -0.12, -0.12, -0.12, -0.12, -0.12, -0.12, + -0.12, 0.12, 0.12, 0.12, 0.12, -0.12, -0.12, 0.12, + 0.12, 0.12, -0.12, 0.12, -0.12, -0.12, -0.12, 0.12, + -0.12, -0.12, 0.12, 0.00, 0.11, 0.11,-122.67, 164.70, + 203.78, 273.50, 3.58, 2.74, 6.18, -4.56, 0.00, -0.04, + 0.00, -0.07, 57.44, -77.10, 95.82, 128.60, -1.77, -1.28, + 2.85, -2.14, 82.14, 89.50, 0.00, 0.00, 2.00, -1.84, + -0.04, 47.73, -64.10, 23.79, 31.90, -1.45, -1.07, 0.69, + -0.53, -46.38, 50.50, 0.00, 0.00, 1.13, 1.04, 0.02, + -18.38, 0.00, 63.80, 0.00, 0.00, 0.41, 0.00, -1.43, + 59.07, 0.00, 0.00, 0.00, 0.00, -1.32, 57.28, 0.00, + 0.00, 0.00, 0.00, -1.28, -48.65, 0.00, -1.15, 0.00, + 0.00, 1.09, 0.00, 0.03, -18.30, 24.60, -17.30, -23.20, + 0.56, 0.41, -0.51, 0.39, -16.91, 26.90, 8.43, 13.30, + 0.60, 0.38, 0.31, -0.19, 1.23, -1.70, -19.13, -25.70, + -0.03, -0.03, -0.58, 0.43, -0.72, 0.90, -17.34, -23.30, + + /* 2516-2643 */ + 0.03, 0.02, -0.52, 0.39, -19.49, -21.30, 0.00, 0.00, + -0.48, 0.44, 0.01, 20.57, -20.10, 0.64, 0.70, -0.45, + -0.46, 0.00, -0.01, 4.89, 5.90, -16.55, 19.90, 0.14, + -0.11, 0.44, 0.37, 18.22, 19.80, 0.00, 0.00, 0.44, + -0.41, -0.01, 4.89, -5.30, -16.51, -18.00, -0.11, -0.11, + -0.41, 0.37, -17.86, 0.00, 17.10, 0.00, 0.00, 0.40, + 0.00, -0.38, 0.32, 0.00, 24.42, 0.00, 0.00, -0.01, + 0.00, -0.55, -23.79, 0.00, 0.00, 0.00, 0.00, 0.53, + 14.72, -16.00, -0.32, 0.00, -0.36, -0.33, -0.01, 0.01, + 3.34, -4.50, 11.86, 15.90, -0.11, -0.07, 0.35, -0.27, + -3.26, 4.40, 11.62, 15.60, 0.09, 0.07, 0.35, -0.26, + -19.53, 0.00, 5.09, 0.00, 0.00, 0.44, 0.00, -0.11, + -13.48, 14.70, 0.00, 0.00, 0.33, 0.30, 0.01, 10.86, + -14.60, 3.18, 4.30, -0.33, -0.24, 0.09, -0.07, -11.30, + -15.10, 0.00, 0.00, -0.34, 0.25, 0.01, 2.03, -2.70, + 10.82, 14.50, -0.07, -0.05, 0.32, -0.24, 17.46, 0.00, + + /* 2644-2771 */ + 0.00, 0.00, 0.00, -0.39, 16.43, 0.00, 0.52, 0.00, + 0.00, -0.37, 0.00, -0.01, 9.35, 0.00, 13.29, 0.00, + 0.00, -0.21, 0.00, -0.30, -10.42, 11.40, 0.00, 0.00, + 0.25, 0.23, 0.01, 0.44, 0.50, -10.38, 11.30, 0.02, + -0.01, 0.25, 0.23, -14.64, 0.00, 0.00, 0.00, 0.00, + 0.33, 0.56, 0.80, -8.67, 11.70, 0.02, -0.01, 0.26, + 0.19, 13.88, 0.00, -2.47, 0.00, 0.00, -0.31, 0.00, + 0.06, -1.99, 2.70, 7.72, 10.30, 0.06, 0.04, 0.23, + -0.17, -0.20, 0.00, 13.05, 0.00, 0.00, 0.00, 0.00, + -0.29, 6.92, -9.30, 3.34, 4.50, -0.21, -0.15, 0.10, + -0.07, -6.60, 0.00, 10.70, 0.00, 0.00, 0.15, 0.00, + -0.24, -8.04, -8.70, 0.00, 0.00, -0.19, 0.18, -10.58, + 0.00, -3.10, 0.00, 0.00, 0.24, 0.00, 0.07, -7.32, + 8.00, -0.12, -0.10, 0.18, 0.16, 1.63, 1.70, 6.96, + -7.60, 0.03, -0.04, -0.17, -0.16, -3.62, 0.00, 9.86, + 0.00, 0.00, 0.08, 0.00, -0.22, 0.20, -0.20, -6.88, + + /* 2772-2899 */ + -7.50, 0.00, 0.00, -0.17, 0.15, -8.99, 0.00, 4.02, + 0.00, 0.00, 0.20, 0.00, -0.09, -1.07, 1.40, -5.69, + -7.70, 0.03, 0.02, -0.17, 0.13, 6.48, -7.20, -0.48, + -0.50, -0.16, -0.14, -0.01, 0.01, 5.57, -7.50, 1.07, + 1.40, -0.17, -0.12, 0.03, -0.02, 8.71, 0.00, 3.54, + 0.00, 0.00, -0.19, 0.00, -0.08, 0.40, 0.00, 9.27, + 0.00, 0.00, -0.01, 0.00, -0.21, -6.13, 6.70, -1.19, + -1.30, 0.15, 0.14, -0.03, 0.03, 5.21, -5.70, -2.51, + -2.60, -0.13, -0.12, -0.06, 0.06, 5.69, -6.20, -0.12, + -0.10, -0.14, -0.13, -0.01, 2.03, -2.70, 4.53, 6.10, + -0.06, -0.05, 0.14, -0.10, 5.01, 5.50, -2.51, 2.70, + 0.12, -0.11, 0.06, 0.06, -1.91, 2.60, -4.38, -5.90, + 0.06, 0.04, -0.13, 0.10, 4.65, -6.30, 0.00, 0.00, + -0.14, -0.10, -5.29, 5.70, 0.00, 0.00, 0.13, 0.12, + -2.23, -4.00, -4.65, 4.20, -0.09, 0.05, 0.10, 0.10, + -4.53, 6.10, 0.00, 0.00, 0.14, 0.10, 2.47, 2.70, + + /* 2900-3027 */ + -4.46, 4.90, 0.06, -0.06, 0.11, 0.10, -5.05, 5.50, + 0.84, 0.90, 0.12, 0.11, 0.02, -0.02, 4.97, -5.40, + -1.71, 0.00, -0.12, -0.11, 0.00, 0.04, -0.99, -1.30, + 4.22, -5.70, -0.03, 0.02, -0.13, -0.09, 0.99, 1.40, + 4.22, -5.60, 0.03, -0.02, -0.13, -0.09, -4.69, -5.20, + 0.00, 0.00, -0.12, 0.10, -3.42, 0.00, 6.09, 0.00, + 0.00, 0.08, 0.00, -0.14, -4.65, -5.10, 0.00, 0.00, + -0.11, 0.10, 0.00, 0.00, -4.53, -5.00, 0.00, 0.00, + -0.11, 0.10, -2.43, -2.70, -3.82, 4.20, -0.06, 0.05, + 0.10, 0.09, 0.00, 0.00, -4.53, 4.90, 0.00, 0.00, + 0.11, 0.10, -4.49, -4.90, 0.00, 0.00, -0.11, 0.10, + 2.67, -2.90, -3.62, -3.90, -0.06, -0.06, -0.09, 0.08, + 3.94, -5.30, 0.00, 0.00, -0.12, -3.38, 3.70, -2.78, + -3.10, 0.08, 0.08, -0.07, 0.06, 3.18, -3.50, -2.82, + -3.10, -0.08, -0.07, -0.07, 0.06, -5.77, 0.00, 1.87, + 0.00, 0.00, 0.13, 0.00, -0.04, 3.54, -4.80, -0.64, + + /* 3028-3155 */ + -0.90, -0.11, 0.00, -0.02, -3.50, -4.70, 0.68, -0.90, + -0.11, 0.00, -0.02, 5.49, 0.00, 0.00, 0.00, 0.00, + -0.12, 1.83, -2.50, 2.63, 3.50, -0.06, 0.00, 0.08, + 3.02, -4.10, 0.68, 0.90, -0.09, 0.00, 0.02, 0.00, + 0.00, 5.21, 0.00, 0.00, 0.00, 0.00, -0.12, -3.54, + 3.80, 2.70, 3.60, -1.35, 1.80, 0.08, 0.00, 0.04, + -2.90, 3.90, 0.68, 0.90, 0.09, 0.00, 0.02, 0.80, + -1.10, -2.78, -3.70, -0.02, 0.00, -0.08, 4.10, 0.00, + -2.39, 0.00, 0.00, -0.09, 0.00, 0.05, -1.59, 2.10, + 2.27, 3.00, 0.05, 0.00, 0.07, -2.63, 3.50, -0.48, + -0.60, -2.94, -3.20, -2.94, 3.20, 2.27, -3.00, -1.11, + -1.50, -0.07, 0.00, -0.03, -0.56, -0.80, -2.35, 3.10, + 0.00, -0.60, -3.42, 1.90, -0.12, -0.10, 2.63, -2.90, + 2.51, 2.80, -0.64, 0.70, -0.48, -0.60, 2.19, -2.90, + 0.24, -0.30, 2.15, 2.90, 2.15, -2.90, 0.52, 0.70, + 2.07, -2.80, -3.10, 0.00, 1.79, 0.00, 0.00, 0.07, + + /* 3156-3283 */ + 0.00, -0.04, 0.88, 0.00, -3.46, 2.11, 2.80, -0.36, + 0.50, 3.54, -0.20, -3.50, -1.39, 1.50, -1.91, -2.10, + -1.47, 2.00, 1.39, 1.90, 2.07, -2.30, 0.91, 1.00, + 1.99, -2.70, 3.30, 0.00, 0.60, -0.44, -0.70, -1.95, + 2.60, 2.15, -2.40, -0.60, -0.70, 3.30, 0.84, 0.00, + -3.10, -3.10, 0.00, -0.72, -0.32, 0.40, -1.87, -2.50, + 1.87, -2.50, 0.32, 0.40, -0.24, 0.30, -1.87, -2.50, + -0.24, -0.30, 1.87, -2.50, -2.70, 0.00, 1.55, 2.03, + 2.20, -2.98, -1.99, -2.20, 0.12, -0.10, -0.40, 0.50, + 1.59, 2.10, 0.00, 0.00, -1.79, 2.00, -1.03, 1.40, + -1.15, -1.60, 0.32, 0.50, 1.39, -1.90, 2.35, -1.27, + 1.70, 0.60, 0.80, -0.32, -0.40, 1.35, -1.80, 0.44, + 0.00, 2.23, -0.84, 0.90, -1.27, -1.40, -1.47, 1.60, + -0.28, -0.30, -0.28, 0.40, -1.27, -1.70, 0.28, -0.40, + -1.43, -1.50, 0.00, 0.00, -1.27, -1.70, 2.11, -0.32, + -0.40, -1.23, 1.60, 1.19, -1.30, -0.72, -0.80, 0.72, + + /* 3284-3411 */ + -0.80, -1.15, -1.30, -1.35, -1.50, -1.19, -1.60, -0.12, + 0.20, 1.79, 0.00, -0.88, -0.28, 0.40, 1.11, 1.50, + -1.83, 0.00, 0.56, -0.12, 0.10, -1.27, -1.40, 0.00, + 0.00, 1.15, 1.50, -0.12, 0.20, 1.11, 1.50, 0.36, + -0.50, -1.07, -1.40, -1.11, 1.50, 1.67, 0.00, 0.80, + -1.11, 0.00, 1.43, 1.23, -1.30, -0.24, -1.19, -1.30, + -0.24, 0.20, -0.44, -0.90, -0.95, 1.10, 1.07, -1.40, + 1.15, -1.30, 1.03, -1.10, -0.56, -0.60, -0.68, 0.90, + -0.76, -1.00, -0.24, -0.30, 0.95, -1.30, 0.56, 0.70, + 0.84, -1.10, -0.56, 0.00, -1.55, 0.91, -1.30, 0.28, + 0.30, 0.16, -0.20, 0.95, 1.30, 0.40, -0.50, -0.88, + -1.20, 0.95, -1.10, -0.48, -0.50, 0.00, 0.00, -1.07, + 1.20, 0.44, -0.50, 0.95, 1.10, 0.00, 0.00, 0.92, + -1.30, 0.95, 1.00, -0.52, 0.60, 1.59, 0.24, -0.40, + 0.91, 1.20, 0.84, -1.10, -0.44, -0.60, 0.84, 1.10, + -0.44, 0.60, -0.44, 0.60, -0.84, -1.10, -0.80, 0.00, + + /* 3412-3539 */ + 1.35, 0.76, 0.20, -0.91, -1.00, 0.20, -0.30, -0.91, + -1.20, -0.95, 1.00, -0.48, -0.50, 0.88, 1.00, 0.48, + -0.50, -0.95, -1.10, 0.20, -0.20, -0.99, 1.10, -0.84, + 1.10, -0.24, -0.30, 0.20, -0.30, 0.84, 1.10, -1.39, + 0.00, -0.28, -0.16, 0.20, 0.84, 1.10, 0.00, 0.00, + 1.39, 0.00, 0.00, -0.95, 1.00, 1.35, -0.99, 0.00, + 0.88, -0.52, 0.00, -1.19, 0.20, 0.20, 0.76, -1.00, + 0.00, 0.00, 0.76, 1.00, 0.00, 0.00, 0.76, 1.00, + -0.76, 1.00, 0.00, 0.00, 1.23, 0.76, 0.80, -0.32, + 0.40, -0.72, 0.80, -0.40, -0.40, 0.00, 0.00, -0.80, + -0.90, -0.68, 0.90, -0.16, -0.20, -0.16, -0.20, 0.68, + -0.90, -0.36, 0.50, -0.56, -0.80, 0.72, -0.90, 0.44, + -0.60, -0.48, -0.70, -0.16, 0.00, -1.11, 0.32, 0.00, + -1.07, 0.60, -0.80, -0.28, -0.40, -0.64, 0.00, 0.91, + 1.11, 0.64, -0.90, 0.76, -0.80, 0.00, 0.00, -0.76, + -0.80, 1.03, 0.00, -0.36, -0.64, -0.70, 0.36, -0.40, + + /* 3540-3667 */ + 1.07, 0.36, -0.50, -0.52, -0.70, 0.60, 0.00, 0.88, + 0.95, 0.00, 0.48, 0.16, -0.20, 0.60, 0.80, 0.16, + -0.20, -0.60, -0.80, 0.00, -1.00, 0.12, 0.20, 0.16, + -0.20, 0.68, 0.70, 0.59, -0.80, -0.99, -0.56, -0.60, + 0.36, -0.40, -0.68, -0.70, -0.68, -0.70, -0.36, -0.50, + -0.44, 0.60, 0.64, 0.70, -0.12, 0.10, -0.52, 0.60, + 0.36, 0.40, 0.00, 0.00, 0.95, -0.84, 0.00, 0.44, + 0.56, 0.60, 0.32, -0.30, 0.00, 0.00, 0.60, 0.70, + 0.00, 0.00, 0.60, 0.70, -0.12, -0.20, 0.52, -0.70, + 0.00, 0.00, 0.56, 0.70, -0.12, 0.10, -0.52, -0.70, + 0.00, 0.00, 0.88, -0.76, 0.00, -0.44, 0.00, 0.00, + -0.52, -0.70, 0.52, -0.70, 0.36, -0.40, -0.44, -0.50, + 0.00, 0.00, 0.60, 0.60, 0.84, 0.00, 0.12, -0.24, + 0.00, 0.80, -0.56, 0.60, -0.32, -0.30, 0.48, -0.50, + 0.28, -0.30, -0.48, -0.50, 0.12, 0.20, 0.48, -0.60, + 0.48, 0.60, -0.12, 0.20, 0.24, 0.00, 0.76, -0.52, + + /* 3668-3795 */ + -0.60, -0.52, 0.60, 0.48, -0.50, -0.24, -0.30, 0.12, + -0.10, 0.48, 0.60, 0.52, -0.20, 0.36, 0.40, -0.44, + 0.50, -0.24, -0.30, -0.48, -0.60, -0.44, -0.60, -0.12, + 0.10, 0.76, 0.76, 0.20, -0.20, 0.48, 0.50, 0.40, + -0.50, -0.24, -0.30, 0.44, -0.60, 0.44, -0.60, 0.36, + 0.00, -0.64, 0.72, 0.00, -0.12, 0.00, -0.10, -0.40, + -0.60, -0.20, -0.20, -0.44, 0.50, -0.44, 0.50, 0.20, + 0.20, -0.44, -0.50, 0.20, -0.20, -0.20, 0.20, -0.44, + -0.50, 0.64, 0.00, 0.32, -0.36, 0.50, -0.20, -0.30, + 0.12, -0.10, 0.48, 0.50, -0.12, 0.30, -0.36, -0.50, + 0.00, 0.00, 0.48, 0.50, -0.48, 0.50, 0.68, 0.00, + -0.12, 0.56, -0.40, 0.44, -0.50, -0.12, -0.10, 0.24, + 0.30, -0.40, 0.40, 0.64, 0.00, -0.24, 0.64, 0.00, + -0.20, 0.00, 0.00, 0.44, -0.50, 0.44, 0.50, -0.12, + 0.20, -0.36, -0.50, 0.12, 0.00, 0.64, -0.40, 0.50, + 0.00, 0.10, 0.00, 0.00, -0.40, 0.50, 0.00, 0.00, + + /* 3796-3923 */ + -0.40, -0.50, 0.56, 0.00, 0.28, 0.00, 0.10, 0.36, + 0.50, 0.00, -0.10, 0.36, -0.50, 0.36, 0.50, 0.00, + -0.10, 0.24, -0.20, -0.36, -0.40, 0.16, 0.20, 0.40, + -0.40, 0.00, 0.00, -0.36, -0.50, -0.36, -0.50, -0.32, + -0.50, -0.12, 0.10, 0.20, 0.20, -0.36, 0.40, -0.60, + 0.60, 0.28, 0.00, 0.52, 0.12, -0.10, 0.40, 0.40, + 0.00, -0.50, 0.20, -0.20, -0.32, 0.40, 0.16, 0.20, + -0.16, 0.20, 0.32, 0.40, 0.56, 0.00, -0.12, 0.32, + -0.40, -0.16, -0.20, 0.00, 0.00, 0.40, 0.40, -0.40, + -0.40, -0.40, 0.40, -0.36, 0.40, 0.12, 0.10, 0.00, + 0.10, 0.36, 0.40, 0.00, -0.10, 0.36, 0.40, -0.36, + 0.40, 0.00, 0.10, 0.32, 0.00, 0.44, 0.12, 0.20, + 0.28, -0.40, 0.00, 0.00, 0.36, 0.40, 0.32, -0.40, + -0.16, 0.12, 0.10, 0.32, -0.40, 0.20, 0.30, -0.24, + 0.30, 0.00, 0.10, 0.32, 0.40, 0.00, -0.10, -0.32, + -0.40, -0.32, 0.40, 0.00, 0.10, -0.52, -0.52, 0.52, + + /* 3924-4051 */ + 0.32, -0.40, 0.00, 0.00, 0.32, 0.40, 0.32, -0.40, + 0.00, 0.00, -0.32, -0.40, -0.32, 0.40, 0.32, 0.40, + 0.00, 0.00, 0.32, 0.40, 0.00, 0.00, -0.32, -0.40, + 0.00, 0.00, 0.32, 0.40, 0.16, 0.20, 0.32, -0.30, + -0.16, 0.00, -0.48, -0.20, 0.20, -0.28, -0.30, 0.28, + -0.40, 0.00, 0.00, 0.28, -0.40, 0.00, 0.00, 0.28, + -0.40, 0.00, 0.00, -0.28, -0.40, 0.28, 0.40, -0.28, + -0.40, -0.48, -0.20, 0.20, 0.24, 0.30, 0.44, 0.00, + 0.16, 0.24, 0.30, 0.16, -0.20, 0.24, 0.30, -0.12, + 0.20, 0.20, 0.30, -0.16, 0.20, 0.00, 0.00, 0.44, + -0.32, 0.30, 0.24, 0.00, -0.36, 0.36, 0.00, 0.24, + 0.12, -0.20, 0.20, 0.30, -0.12, 0.00, -0.28, 0.30, + -0.24, 0.30, 0.12, 0.10, -0.28, -0.30, -0.28, 0.30, + 0.00, 0.00, -0.28, -0.30, 0.00, 0.00, -0.28, -0.30, + 0.00, 0.00, 0.28, 0.30, 0.00, 0.00, -0.28, -0.30, + -0.28, 0.30, 0.00, 0.00, -0.28, -0.30, 0.00, 0.00, + + /* 4052-4179 */ + 0.28, 0.30, 0.00, 0.00, -0.28, 0.30, 0.28, -0.30, + -0.28, 0.30, 0.40, 0.40, -0.24, 0.30, 0.00, -0.10, + 0.16, 0.00, 0.36, -0.20, 0.30, -0.12, -0.10, -0.24, + -0.30, 0.00, 0.00, -0.24, 0.30, -0.24, 0.30, 0.00, + 0.00, -0.24, 0.30, -0.24, 0.30, 0.24, -0.30, 0.00, + 0.00, 0.24, -0.30, 0.00, 0.00, 0.24, 0.30, 0.24, + -0.30, 0.24, 0.30, -0.24, 0.30, -0.24, 0.30, -0.20, + 0.20, -0.16, -0.20, 0.00, 0.00, -0.32, 0.20, 0.00, + 0.10, 0.20, -0.30, 0.20, -0.20, 0.12, 0.20, -0.16, + 0.20, 0.16, 0.20, 0.20, 0.30, 0.20, 0.30, 0.00, + 0.00, -0.20, 0.30, 0.00, 0.00, 0.20, 0.30, -0.20, + -0.30, -0.20, -0.30, 0.20, -0.30, 0.00, 0.00, 0.20, + 0.30, 0.00, 0.00, 0.20, 0.30, 0.00, 0.00, 0.20, + 0.30, 0.00, 0.00, 0.20, 0.30, 0.00, 0.00, 0.20, + -0.30, 0.00, 0.00, -0.20, -0.30, 0.00, 0.00, -0.20, + 0.30, 0.00, 0.00, -0.20, 0.30, 0.00, 0.00, 0.36, + + /* 4180-4307 */ + 0.00, 0.00, 0.36, 0.12, 0.10, -0.24, 0.20, 0.12, + -0.20, -0.16, -0.20, -0.13, 0.10, 0.22, 0.21, 0.20, + 0.00, -0.28, 0.32, 0.00, -0.12, -0.20, -0.20, 0.12, + -0.10, 0.12, 0.10, -0.20, 0.20, 0.00, 0.00, -0.32, + 0.32, 0.00, 0.00, 0.32, 0.32, 0.00, 0.00, -0.24, + -0.20, 0.24, 0.20, 0.20, 0.00, -0.24, 0.00, 0.00, + -0.24, -0.20, 0.00, 0.00, 0.24, 0.20, -0.24, -0.20, + 0.00, 0.00, -0.24, 0.20, 0.16, -0.20, 0.12, 0.10, + 0.20, 0.20, 0.00, -0.10, -0.12, 0.10, -0.16, -0.20, + -0.12, -0.10, -0.16, 0.20, 0.20, 0.20, 0.00, 0.00, + -0.20, 0.20, -0.20, 0.20, -0.20, 0.20, -0.20, 0.20, + 0.20, -0.20, -0.20, -0.20, 0.00, 0.00, -0.20, 0.20, + 0.20, 0.00, -0.20, 0.00, 0.00, -0.20, 0.20, -0.20, + 0.20, -0.20, -0.20, -0.20, -0.20, 0.00, 0.00, 0.20, + 0.20, 0.20, 0.20, 0.12, -0.20, -0.12, -0.10, 0.28, + -0.28, 0.16, -0.20, 0.00, -0.10, 0.00, 0.10, -0.16, + + /* 4308-4435 */ + 0.20, 0.00, -0.10, -0.16, -0.20, 0.00, -0.10, 0.16, + -0.20, 0.16, -0.20, 0.00, 0.00, 0.16, 0.20, -0.16, + 0.20, 0.00, 0.00, 0.16, 0.20, 0.16, -0.20, 0.16, + -0.20, -0.16, 0.20, 0.16, -0.20, 0.00, 0.00, 0.16, + 0.20, 0.00, 0.00, 0.16, 0.20, 0.00, 0.00, -0.16, + -0.20, 0.16, -0.20, -0.16, -0.20, 0.00, 0.00, -0.16, + -0.20, 0.00, 0.00, -0.16, 0.20, 0.00, 0.00, 0.16, + -0.20, 0.16, 0.20, 0.16, 0.20, 0.00, 0.00, -0.16, + -0.20, 0.00, 0.00, -0.16, -0.20, 0.00, 0.00, 0.16, + 0.20, 0.16, 0.20, 0.00, 0.00, 0.16, 0.20, 0.16, + -0.20, 0.16, 0.20, 0.00, 0.00, -0.16, 0.20, 0.00, + 0.10, 0.12, -0.20, 0.12, -0.20, 0.00, -0.10, 0.00, + -0.10, 0.12, 0.20, 0.00, -0.10, -0.12, 0.20, -0.15, + 0.20, -0.24, 0.24, 0.00, 0.00, 0.24, 0.24, 0.12, + -0.20, -0.12, -0.20, 0.00, 0.00, 0.12, 0.20, 0.12, + -0.20, 0.12, 0.20, 0.12, 0.20, 0.12, 0.20, 0.12, + + /* 4436-4563 */ + -0.20, -0.12, 0.20, 0.00, 0.00, 0.12, 0.20, 0.12, + 0.00, -0.20, 0.00, 0.00, -0.12, -0.20, 0.12, -0.20, + 0.00, 0.00, 0.12, 0.20, -0.12, 0.20, -0.12, 0.20, + 0.12, -0.20, 0.00, 0.00, 0.12, 0.20, 0.20, 0.00, + 0.12, 0.00, 0.00, -0.12, 0.20, 0.00, 0.00, -0.12, + -0.20, 0.00, 0.00, -0.12, -0.20, -0.12, -0.20, 0.00, + 0.00, 0.12, -0.20, 0.12, -0.20, 0.12, 0.20, -0.12, + -0.20, 0.00, 0.00, 0.12, -0.20, 0.12, -0.20, 0.12, + 0.20, 0.12, 0.00, 0.20, -0.12, -0.20, 0.00, 0.00, + 0.12, 0.20, -0.16, 0.00, 0.16, -0.20, 0.20, 0.00, + 0.00, -0.20, 0.00, 0.00, -0.20, 0.20, 0.00, 0.00, + 0.20, 0.20, -0.20, 0.00, 0.00, -0.20, 0.12, 0.00, + -0.16, 0.20, 0.00, 0.00, 0.20, 0.12, -0.10, 0.00, + 0.10, 0.16, -0.16, -0.16, -0.16, -0.16, -0.16, 0.00, + 0.00, -0.16, 0.00, 0.00, -0.16, -0.16, -0.16, 0.00, + 0.00, -0.16, 0.00, 0.00, 0.16, 0.00, 0.00, 0.16, + + /* 4564-4691 */ + 0.00, 0.00, 0.16, 0.16, 0.00, 0.00, -0.16, 0.00, + 0.00, -0.16, -0.16, 0.00, 0.00, 0.16, 0.00, 0.00, + -0.16, -0.16, 0.00, 0.00, -0.16, -0.16, 0.12, 0.10, + 0.12, -0.10, 0.12, 0.10, 0.00, 0.00, 0.12, 0.10, + -0.12, 0.10, 0.00, 0.00, 0.12, 0.10, 0.12, -0.10, + 0.00, 0.00, -0.12, -0.10, 0.00, 0.00, 0.12, 0.10, + 0.12, 0.00, 0.00, 0.12, 0.00, 0.00, -0.12, 0.00, + 0.00, 0.12, 0.12, 0.12, 0.12, 0.12, 0.00, 0.00, + 0.12, 0.00, 0.00, 0.12, 0.12, 0.00, 0.00, 0.12, + 0.00, 0.00, 0.12, -0.12, -0.12, 0.12, 0.12, -0.12, + -0.12, 0.00, 0.00, 0.12, -0.12, 0.12, 0.12, -0.12, + -0.12, 0.00, 0.00, -0.12, -0.12, 0.00, 0.00, -0.12, + 0.12, 0.00, 0.00, 0.12, 0.00, 0.00, 0.12, 0.00, + 0.00, 0.12, -0.12, 0.00, 0.00, -0.12, 0.12, -0.12, + -0.12, 0.12, 0.00, 0.00, 0.12, 0.12, 0.12, -0.12, + 0.00, 0.00, -0.12, -0.12, -0.12, 0.00, 0.00, -0.12, + + /* 4692-NA */ + -0.12, 0.00, 0.00, 0.12, 0.12, 0.00, 0.00, -0.12, + -0.12, -0.12, -0.12, 0.12, 0.00, 0.00, 0.12, -0.12, + 0.00, 0.00, -0.12, -0.12, 0.00, 0.00, 0.12, -0.12, + -0.12, -0.12, -0.12, 0.12, 0.12, -0.12, -0.12, 0.00, + 0.00, -0.12, 0.00, 0.00, -0.12, 0.12, 0.00, 0.00, + 0.12, 0.00, 0.00, -0.12, -0.12, 0.00, 0.00, -0.12, + -0.12, 0.12, 0.00, 0.00, 0.12, 0.12, 0.00, 0.00, + 0.12, 0.00, 0.00, 0.12, 0.12, 0.08, 0.00, 0.04 + }; + +/* Number of amplitude coefficients */ + static const int NA = (int) (sizeof a / sizeof (double)); + +/* Amplitude usage: X or Y, sin or cos, power of T. */ + static const int jaxy[] = {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1}; + static const int jasc[] = {0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0}; + static const int japt[] = {0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4}; + +/* Miscellaneous */ + double t, w, pt[MAXPT+1], fa[14], xypr[2], xypl[2], xyls[2], arg, + sc[2]; + int jpt, i, j, jxy, ialast, ifreq, m, ia, jsc; + +/*--------------------------------------------------------------------*/ + +/* Interval between fundamental date J2000.0 and given date (JC). */ + t = ((date1 - ERFA_DJ00) + date2) / ERFA_DJC; + +/* Powers of T. */ + w = 1.0; + for (jpt = 0; jpt <= MAXPT; jpt++) { + pt[jpt] = w; + w *= t; + } + +/* Initialize totals in X and Y: polynomial, luni-solar, planetary. */ + for (jxy = 0; jxy < 2; jxy++) { + xypr[jxy] = 0.0; + xyls[jxy] = 0.0; + xypl[jxy] = 0.0; + } + +/* --------------------------------- */ +/* Fundamental arguments (IERS 2003) */ +/* --------------------------------- */ + +/* Mean anomaly of the Moon. */ + fa[0] = eraFal03(t); + +/* Mean anomaly of the Sun. */ + fa[1] = eraFalp03(t); + +/* Mean argument of the latitude of the Moon. */ + fa[2] = eraFaf03(t); + +/* Mean elongation of the Moon from the Sun. */ + fa[3] = eraFad03(t); + +/* Mean longitude of the ascending node of the Moon. */ + fa[4] = eraFaom03(t); + +/* Planetary longitudes, Mercury through Neptune. */ + fa[5] = eraFame03(t); + fa[6] = eraFave03(t); + fa[7] = eraFae03(t); + fa[8] = eraFama03(t); + fa[9] = eraFaju03(t); + fa[10] = eraFasa03(t); + fa[11] = eraFaur03(t); + fa[12] = eraFane03(t); + +/* General accumulated precession in longitude. */ + fa[13] = eraFapa03(t); + +/* -------------------------------------- */ +/* Polynomial part of precession-nutation */ +/* -------------------------------------- */ + + for (jxy = 0; jxy < 2; jxy++) { + for (j = MAXPT; j >= 0; j--) { + xypr[jxy] += xyp[jxy][j] * pt[j]; + } + } + +/* ---------------------------------- */ +/* Nutation periodic terms, planetary */ +/* ---------------------------------- */ + +/* Work backwards through the coefficients per frequency list. */ + ialast = NA; + for (ifreq = NFPL-1; ifreq >= 0; ifreq--) { + + /* Obtain the argument functions. */ + arg = 0.0; + for (i = 0; i < 14; i++) { + m = mfapl[ifreq][i]; + if (m != 0) arg += (double)m * fa[i]; + } + sc[0] = sin(arg); + sc[1] = cos(arg); + + /* Work backwards through the amplitudes at this frequency. */ + ia = nc[ifreq+NFLS]; + for (i = ialast; i >= ia; i--) { + + /* Coefficient number (0 = 1st). */ + j = i-ia; + + /* X or Y. */ + jxy = jaxy[j]; + + /* Sin or cos. */ + jsc = jasc[j]; + + /* Power of T. */ + jpt = japt[j]; + + /* Accumulate the component. */ + xypl[jxy] += a[i-1] * sc[jsc] * pt[jpt]; + } + ialast = ia-1; + } + +/* ----------------------------------- */ +/* Nutation periodic terms, luni-solar */ +/* ----------------------------------- */ + +/* Continue working backwards through the number of coefficients list. */ + for (ifreq = NFLS-1; ifreq >= 0; ifreq--) { + + /* Obtain the argument functions. */ + arg = 0.0; + for (i = 0; i < 5; i++) { + m = mfals[ifreq][i]; + if (m != 0) arg += (double)m * fa[i]; + } + sc[0] = sin(arg); + sc[1] = cos(arg); + + /* Work backwards through the amplitudes at this frequency. */ + ia = nc[ifreq]; + for (i = ialast; i >= ia; i--) { + + /* Coefficient number (0 = 1st). */ + j = i-ia; + + /* X or Y. */ + jxy = jaxy[j]; + + /* Sin or cos. */ + jsc = jasc[j]; + + /* Power of T. */ + jpt = japt[j]; + + /* Accumulate the component. */ + xyls[jxy] += a[i-1] * sc[jsc] * pt[jpt]; + } + ialast = ia-1; + } + +/* ------------------------------------ */ +/* Results: CIP unit vector components */ +/* ------------------------------------ */ + + *x = ERFA_DAS2R * (xypr[0] + (xyls[0] + xypl[0]) / 1e6); + *y = ERFA_DAS2R * (xypr[1] + (xyls[1] + xypl[1]) / 1e6); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/xys00a.c b/erfa/xys00a.c new file mode 100644 index 0000000..411627e --- /dev/null +++ b/erfa/xys00a.c @@ -0,0 +1,142 @@ +#include "erfa.h" + +void eraXys00a(double date1, double date2, + double *x, double *y, double *s) +/* +** - - - - - - - - - - +** e r a X y s 0 0 a +** - - - - - - - - - - +** +** For a given TT date, compute the X,Y coordinates of the Celestial +** Intermediate Pole and the CIO locator s, using the IAU 2000A +** precession-nutation model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** x,y double Celestial Intermediate Pole (Note 2) +** s double the CIO locator s (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The Celestial Intermediate Pole coordinates are the x,y +** components of the unit vector in the Geocentric Celestial +** Reference System. +** +** 3) The CIO locator s (in radians) positions the Celestial +** Intermediate Origin on the equator of the CIP. +** +** 4) A faster, but slightly less accurate result (about 1 mas for +** X,Y), can be obtained by using instead the eraXys00b function. +** +** Called: +** eraPnm00a classical NPB matrix, IAU 2000A +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS00 the CIO locator s, given X,Y, IAU 2000A +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3]; + + +/* Form the bias-precession-nutation matrix, IAU 2000A. */ + eraPnm00a(date1, date2, rbpn); + +/* Extract X,Y. */ + eraBpn2xy(rbpn, x, y); + +/* Obtain s. */ + *s = eraS00(date1, date2, *x, *y); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/xys00b.c b/erfa/xys00b.c new file mode 100644 index 0000000..12bf0dc --- /dev/null +++ b/erfa/xys00b.c @@ -0,0 +1,142 @@ +#include "erfa.h" + +void eraXys00b(double date1, double date2, + double *x, double *y, double *s) +/* +** - - - - - - - - - - +** e r a X y s 0 0 b +** - - - - - - - - - - +** +** For a given TT date, compute the X,Y coordinates of the Celestial +** Intermediate Pole and the CIO locator s, using the IAU 2000B +** precession-nutation model. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** x,y double Celestial Intermediate Pole (Note 2) +** s double the CIO locator s (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The Celestial Intermediate Pole coordinates are the x,y +** components of the unit vector in the Geocentric Celestial +** Reference System. +** +** 3) The CIO locator s (in radians) positions the Celestial +** Intermediate Origin on the equator of the CIP. +** +** 4) The present function is faster, but slightly less accurate (about +** 1 mas in X,Y), than the eraXys00a function. +** +** Called: +** eraPnm00b classical NPB matrix, IAU 2000B +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS00 the CIO locator s, given X,Y, IAU 2000A +** +** Reference: +** +** McCarthy, D. D., Petit, G. (eds.), IERS Conventions (2003), +** IERS Technical Note No. 32, BKG (2004) +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3]; + + +/* Form the bias-precession-nutation matrix, IAU 2000A. */ + eraPnm00b(date1, date2, rbpn); + +/* Extract X,Y. */ + eraBpn2xy(rbpn, x, y); + +/* Obtain s. */ + *s = eraS00(date1, date2, *x, *y); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/xys06a.c b/erfa/xys06a.c new file mode 100644 index 0000000..8c3b3d8 --- /dev/null +++ b/erfa/xys06a.c @@ -0,0 +1,142 @@ +#include "erfa.h" + +void eraXys06a(double date1, double date2, + double *x, double *y, double *s) +/* +** - - - - - - - - - - +** e r a X y s 0 6 a +** - - - - - - - - - - +** +** For a given TT date, compute the X,Y coordinates of the Celestial +** Intermediate Pole and the CIO locator s, using the IAU 2006 +** precession and IAU 2000A nutation models. +** +** Given: +** date1,date2 double TT as a 2-part Julian Date (Note 1) +** +** Returned: +** x,y double Celestial Intermediate Pole (Note 2) +** s double the CIO locator s (Note 2) +** +** Notes: +** +** 1) The TT date date1+date2 is a Julian Date, apportioned in any +** convenient way between the two arguments. For example, +** JD(TT)=2450123.7 could be expressed in any of these ways, +** among others: +** +** date1 date2 +** +** 2450123.7 0.0 (JD method) +** 2451545.0 -1421.3 (J2000 method) +** 2400000.5 50123.2 (MJD method) +** 2450123.5 0.2 (date & time method) +** +** The JD method is the most natural and convenient to use in +** cases where the loss of several decimal digits of resolution +** is acceptable. The J2000 method is best matched to the way +** the argument is handled internally and will deliver the +** optimum resolution. The MJD method and the date & time methods +** are both good compromises between resolution and convenience. +** +** 2) The Celestial Intermediate Pole coordinates are the x,y components +** of the unit vector in the Geocentric Celestial Reference System. +** +** 3) The CIO locator s (in radians) positions the Celestial +** Intermediate Origin on the equator of the CIP. +** +** 4) Series-based solutions for generating X and Y are also available: +** see Capitaine & Wallace (2006) and eraXy06. +** +** Called: +** eraPnm06a classical NPB matrix, IAU 2006/2000A +** eraBpn2xy extract CIP X,Y coordinates from NPB matrix +** eraS06 the CIO locator s, given X,Y, IAU 2006 +** +** References: +** +** Capitaine, N. & Wallace, P.T., 2006, Astron.Astrophys. 450, 855 +** +** Wallace, P.T. & Capitaine, N., 2006, Astron.Astrophys. 459, 981 +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + double rbpn[3][3]; + + +/* Form the bias-precession-nutation matrix, IAU 2006/2000A. */ + eraPnm06a(date1, date2, rbpn); + +/* Extract X,Y. */ + eraBpn2xy(rbpn, x, y); + +/* Obtain s. */ + *s = eraS06(date1, date2, *x, *y); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/zp.c b/erfa/zp.c new file mode 100644 index 0000000..37e8cc6 --- /dev/null +++ b/erfa/zp.c @@ -0,0 +1,86 @@ +#include "erfa.h" + +void eraZp(double p[3]) +/* +** - - - - - - +** e r a Z p +** - - - - - - +** +** Zero a p-vector. +** +** Returned: +** p double[3] p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + p[0] = 0.0; + p[1] = 0.0; + p[2] = 0.0; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/zpv.c b/erfa/zpv.c new file mode 100644 index 0000000..2592f60 --- /dev/null +++ b/erfa/zpv.c @@ -0,0 +1,88 @@ +#include "erfa.h" + +void eraZpv(double pv[2][3]) +/* +** - - - - - - - +** e r a Z p v +** - - - - - - - +** +** Zero a pv-vector. +** +** Returned: +** pv double[2][3] pv-vector +** +** Called: +** eraZp zero p-vector +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + eraZp(pv[0]); + eraZp(pv[1]); + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa/zr.c b/erfa/zr.c new file mode 100644 index 0000000..6fc8b24 --- /dev/null +++ b/erfa/zr.c @@ -0,0 +1,92 @@ +#include "erfa.h" + +void eraZr(double r[3][3]) +/* +** - - - - - - +** e r a Z r +** - - - - - - +** +** Initialize an r-matrix to the null matrix. +** +** Returned: +** r double[3][3] r-matrix +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** Derived, with permission, from the SOFA library. See notes at end of file. +*/ +{ + r[0][0] = 0.0; + r[0][1] = 0.0; + r[0][2] = 0.0; + r[1][0] = 0.0; + r[1][1] = 0.0; + r[1][2] = 0.0; + r[2][0] = 0.0; + r[2][1] = 0.0; + r[2][2] = 0.0; + + return; + +} +/*---------------------------------------------------------------------- +** +** +** Copyright (C) 2013-2014, NumFOCUS Foundation. +** All rights reserved. +** +** This library is derived, with permission, from the International +** Astronomical Union's "Standards of Fundamental Astronomy" library, +** available from http://www.iausofa.org. +** +** The ERFA version is intended to retain identical functionality to +** the SOFA library, but made distinct through different function and +** file names, as set out in the SOFA license conditions. The SOFA +** original has a role as a reference standard for the IAU and IERS, +** and consequently redistribution is permitted only in its unaltered +** state. The ERFA version is not subject to this restriction and +** therefore can be included in distributions which do not support the +** concept of "read only" software. +** +** Although the intent is to replicate the SOFA API (other than +** replacement of prefix names) and results (with the exception of +** bugs; any that are discovered will be fixed), SOFA is not +** responsible for any errors found in this version of the library. +** +** If you wish to acknowledge the SOFA heritage, please acknowledge +** that you are using a library derived from SOFA, rather than SOFA +** itself. +** +** +** TERMS AND CONDITIONS +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1 Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** +** 2 Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** +** 3 Neither the name of the Standards Of Fundamental Astronomy Board, +** the International Astronomical Union nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +** FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +** COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +** BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +** ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +** POSSIBILITY OF SUCH DAMAGE. +** +*/ diff --git a/erfa2ast.h b/erfa2ast.h new file mode 100644 index 0000000..b19e0fc --- /dev/null +++ b/erfa2ast.h @@ -0,0 +1,248 @@ +#if !defined( ERFA2AST_INCLUDED ) /* Include this file only once */ +#define ERFA2AST_INCLUDED +/* +* Name: +* erfa2ast.h + +* Type: +* C include file. + +* Purpose: +* Defines new names for symbols exported by the ERFA library. + +* Invocation: +* #include "erfa2ast.h" + +* Description: +* This include file defines a new name for each public function +* defined by the ERFA library. The names defined by ERFA itself are +* of the form "eraXxx" (e.g. eraPmp) - this include file defines +* a macro that translates each such name to the form "astEraXxx" +* (e.g. astEraPmp). This is done so that the names do not clash +* with any external ERFA library with which the application is linked. +* +* It should be included at the start of any AST source file that refers +* to ERFA functions using the standard names (e.g. eraPmp). + +* Copyright: +* Copyright (C) 2012 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 16-FEB-2012 (DSB): +* Original version. +*/ + +/* Rename all ERFA functions called directlty from PAL. */ +#define eraAf2a astEraAf2a +#define eraAnp astEraAnp +#define eraAnpm astEraAnpm +#define eraC2s astEraC2s +#define eraCal2jd astEraCal2jd +#define eraD2tf astEraD2tf +#define eraDat astEraDat +#define eraEe06a astEraEe06a +#define eraEpb astEraEpb +#define eraEpb2jd astEraEpb2jd +#define eraEpj astEraEpj +#define eraEpj2jd astEraEpj2jd +#define eraEpv00 astEraEpv00 +#define eraFk5hz astEraFk5hz +#define eraGd2gc astEraGd2gc +#define eraGmst06 astEraGmst06 +#define eraHfk5z astEraHfk5z +#define eraIr astEraIr +#define eraJd2cal astEraJd2cal +#define eraObl06 astEraObl06 +#define eraP06e astEraP06e +#define eraPap astEraPap +#define eraPas astEraPas +#define eraPdp astEraPdp +#define eraPmat06 astEraPmat06 +#define eraPn astEraPn +#define eraPnm06a astEraPnm06a +#define eraPxp astEraPxp +#define eraRm2v astEraRm2v +#define eraRv2m astEraRv2m +#define eraRx astEraRx +#define eraRxp astEraRxp +#define eraRxpv astEraRxpv +#define eraRxr astEraRxr +#define eraRy astEraRy +#define eraRz astEraRz +#define eraS2c astEraS2c +#define eraSepp astEraSepp +#define eraSeps astEraSeps +#define eraTf2a astEraTf2a +#define eraTf2d astEraTf2d +#define eraTr astEraTr +#define eraTrxp astEraTrxp + + +/* Rename all ERFA functions called internally within the above ERFA + functions. */ +#define eraA2af astEraA2af +#define eraA2tf astEraA2tf +#define eraBi00 astEraBi00 +#define eraBp00 astEraBp00 +#define eraBp06 astEraBp06 +#define eraBpn2xy astEraBpn2xy +#define eraC2i00a astEraC2i00a +#define eraC2i00b astEraC2i00b +#define eraC2i06a astEraC2i06a +#define eraC2ibpn astEraC2ibpn +#define eraC2ixy astEraC2ixy +#define eraC2ixys astEraC2ixys +#define eraC2t00a astEraC2t00a +#define eraC2t00b astEraC2t00b +#define eraC2t06a astEraC2t06a +#define eraC2tcio astEraC2tcio +#define eraC2teqx astEraC2teqx +#define eraC2tpe astEraC2tpe +#define eraC2txy astEraC2txy +#define eraCp astEraCp +#define eraCpv astEraCpv +#define eraCr astEraCr +#define eraD2dtf astEraD2dtf +#define eraDtdb astEraDtdb +#define eraDtf2d astEraDtf2d +#define eraEe00 astEraEe00 +#define eraEe00a astEraEe00a +#define eraEe00b astEraEe00b +#define eraEect00 astEraEect00 +#define eraEform astEraEform +#define eraEo06a astEraEo06a +#define eraEors astEraEors +#define eraEqeq94 astEraEqeq94 +#define eraEra00 astEraEra00 +#define eraFad03 astEraFad03 +#define eraFae03 astEraFae03 +#define eraFaf03 astEraFaf03 +#define eraFaju03 astEraFaju03 +#define eraFal03 astEraFal03 +#define eraFalp03 astEraFalp03 +#define eraFama03 astEraFama03 +#define eraFame03 astEraFame03 +#define eraFane03 astEraFane03 +#define eraFaom03 astEraFaom03 +#define eraFapa03 astEraFapa03 +#define eraFasa03 astEraFasa03 +#define eraFaur03 astEraFaur03 +#define eraFave03 astEraFave03 +#define eraFk52h astEraFk52h +#define eraFk5hip astEraFk5hip +#define eraFw2m astEraFw2m +#define eraFw2xy astEraFw2xy +#define eraGc2gd astEraGc2gd +#define eraGc2gde astEraGc2gde +#define eraGd2gce astEraGd2gce +#define eraGmst00 astEraGmst00 +#define eraGmst82 astEraGmst82 +#define eraGst00a astEraGst00a +#define eraGst00b astEraGst00b +#define eraGst06 astEraGst06 +#define eraGst06a astEraGst06a +#define eraGst94 astEraGst94 +#define eraH2fk5 astEraH2fk5 +#define eraJdcalf astEraJdcalf +#define eraNum00a astEraNum00a +#define eraNum00b astEraNum00b +#define eraNum06a astEraNum06a +#define eraNumat astEraNumat +#define eraNut00a astEraNut00a +#define eraNut00b astEraNut00b +#define eraNut06a astEraNut06a +#define eraNut80 astEraNut80 +#define eraNutm80 astEraNutm80 +#define eraObl80 astEraObl80 +#define eraP2pv astEraP2pv +#define eraP2s astEraP2s +#define eraPb06 astEraPb06 +#define eraPfw06 astEraPfw06 +#define eraPlan94 astEraPlan94 +#define eraPm astEraPm +#define eraPmat00 astEraPmat00 +#define eraPmat76 astEraPmat76 +#define eraPmp astEraPmp +#define eraPn00 astEraPn00 +#define eraPn00a astEraPn00a +#define eraPn00b astEraPn00b +#define eraPn06 astEraPn06 +#define eraPn06a astEraPn06a +#define eraPnm00a astEraPnm00a +#define eraPnm00b astEraPnm00b +#define eraPnm80 astEraPnm80 +#define eraPom00 astEraPom00 +#define eraPpp astEraPpp +#define eraPpsp astEraPpsp +#define eraPr00 astEraPr00 +#define eraPrec76 astEraPrec76 +#define eraPv2p astEraPv2p +#define eraPv2s astEraPv2s +#define eraPvdpv astEraPvdpv +#define eraPvm astEraPvm +#define eraPvmpv astEraPvmpv +#define eraPvppv astEraPvppv +#define eraPvstar astEraPvstar +#define eraPvu astEraPvu +#define eraPvup astEraPvup +#define eraPvxpv astEraPvxpv +#define eraRefco astEraRefco +#define eraS00 astEraS00 +#define eraS00a astEraS00a +#define eraS00b astEraS00b +#define eraS06 astEraS06 +#define eraS06a astEraS06a +#define eraS2p astEraS2p +#define eraS2pv astEraS2pv +#define eraS2xpv astEraS2xpv +#define eraSp00 astEraSp00 +#define eraStarpm astEraStarpm +#define eraStarpv astEraStarpv +#define eraSxp astEraSxp +#define eraSxpv astEraSxpv +#define eraTaitt astEraTaitt +#define eraTaiut1 astEraTaiut1 +#define eraTaiutc astEraTaiutc +#define eraTcbtdb astEraTcbtdb +#define eraTcgtt astEraTcgtt +#define eraTdbtcb astEraTdbtcb +#define eraTdbtt astEraTdbtt +#define eraTrxpv astEraTrxpv +#define eraTttai astEraTttai +#define eraTttcg astEraTttcg +#define eraTttdb astEraTttdb +#define eraTtut1 astEraTtut1 +#define eraUt1tai astEraUt1tai +#define eraUt1tt astEraUt1tt +#define eraUt1utc astEraUt1utc +#define eraUtctai astEraUtctai +#define eraUtcut1 astEraUtcut1 +#define eraXy06 astEraXy06 +#define eraXys00a astEraXys00a +#define eraXys00b astEraXys00b +#define eraXys06a astEraXys06a +#define eraZp astEraZp +#define eraZpv astEraZpv +#define eraZr astEranZr + +#endif diff --git a/erfam.h b/erfam.h new file mode 100644 index 0000000..5190b3e --- /dev/null +++ b/erfam.h @@ -0,0 +1,61 @@ +#if !defined( ERFAM_INCLUDED ) /* Include this file only once */ +#define ERFAM_INCLUDED +/* +*+ +* Name: +* erfam.h + +* Purpose: +* Macros defined by the ERFA library. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Include file + +* Description: +* Macros defined by the ERFA library. This is needed by the pal.c +* file, which includes source files that include "erfam.h" from the +* current directory (i.e. the main AST source directory), not the +* erfa subdirectory. + +* Authors: +* DSBJ: David S Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 23-FEB-2012 (DSB): +* Initial version. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 3 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +* USA. + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +/* Include the macros defined in the corresponding header file in the + erfa subdirectory. */ +#include "erfa/erfam.h" + +#endif diff --git a/err.h b/err.h new file mode 100644 index 0000000..9448d86 --- /dev/null +++ b/err.h @@ -0,0 +1,66 @@ +#if !defined( ERR_INCLUDED ) /* Include this file only once */ +#define ERR_INCLUDED +/* +*+ +* Name: +* err.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the err module. + +* Invocation: +* #include "err.h" + +* Description: +* This include file defines the interface to the err module and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this module. + +* Inheritance: +* The err module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (STARLINK) +* {enter_new_authors_here} + +* History: +* 6-NOV-1996 (DSB): +* Original version. +* {enter_changes_here} +*- +*/ + +/* Function prototypes. */ +/* ==================== */ +#if defined(astCLASS) /* Protected */ +void astPutErr_( int, const char * ); + +/* Function interfaces. */ +/* ==================== */ +#define astPutErr(status,message) astPutErr_(status,message) +#endif + +#endif diff --git a/err_drama.c b/err_drama.c new file mode 100644 index 0000000..643afda --- /dev/null +++ b/err_drama.c @@ -0,0 +1,122 @@ +/* +* Name: +* err_ems.c + +* Purpose: +* Implement the "err" module for the DRAMA ERS error system. + +* Description: +* This file implements an alternative "err" module for the AST +* library. It is used to deliver error messages through the +* DRAMA ERS error message system rather than by the default mechanism. + +* Copyright: +* Copyright (C) 2008 Science and Technology Facilities Council. +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (UCLan) +* TIMJ: Tim Jenness (JAC, Hawaii) +* RFWS: R.F. Warren-Smith (STARLINK) +* {enter_new_authors_here} + +* History: +* 6-NOV-1996 (DSB): +* Original version. +* 16-SEP-2008 (TIMJ): +* Use modern EMS interface +* 13-NOV-2008 (TIMJ): +* Modify for DRAMA +* {enter_changes_here} +*/ + +#if HAVE_CONFIG_H +#include +#endif + +/* Module Macros. */ +/* ============== */ +/* Define the astCLASS macro (even although this is not a class + implementation). This indicates to header files that they should + make "protected" symbols available. */ +#define astCLASS + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "err.h" /* Interface to this module */ + +/* Need to define these for DRAMA. Otherwise we have to include drama.h + in the distribution as well */ +#if HAVE_STDARG_H +# define DSTDARG_OK +#endif +#define ERS_STANDALONE +#include "Ers.h" /* Interface to the Ers system */ + +/* Function implementations. */ +/* ========================= */ +void astPutErr_( int status, const char *message ) { +/* +*+ +* Name: +* astPutErr + +* Purpose: +* Deliver an error message. + +* Type: +* Protected function. + +* Synopsis: +* #include "err.h" +* void astPutErr( int status, const char *message ) + +* Description: +* This function delivers an error message and (optionally) an +* accompanying status value to the user. It may be re-implemented +* in order to deliver error messages in different ways, according +* to the environment in which the AST library is being used. + +* Parameters: +* status +* The error status value. +* message +* A pointer to a null-terminated character string containing +* the error message to be delivered. This should not contain +* newline characters. + +* Notes: +* - This function is documented as "protected" but, in fact, is +* publicly accessible so that it may be re-implemented as +* required. +*- +*/ + +/* Local Variables: */ + StatusType local_status; /* Local status value */ + +/* Make a copy of the status value supplied. Then invoke ems_rep_c to + report the error message through EMS and to associate the error + status with it. Ignore any returned status value. */ + local_status = status; + ErsRep( 0, &local_status, "%s", message ); +} diff --git a/err_ems.c b/err_ems.c new file mode 100644 index 0000000..63bcab6 --- /dev/null +++ b/err_ems.c @@ -0,0 +1,108 @@ +/* +* Name: +* err_ems.c + +* Purpose: +* Implement the "err" module for the EMS error system. + +* Description: +* This file implements an alternative "err" module for the AST +* library. It is used to deliver error messages through the +* Starlink EMS error message system (Starlink System Note 4) +* rather than by the default mechanism. + +* Copyright: +* Copyright (C) 2008 Science and Technology Facilities Council. +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (STARLINK) +* {enter_new_authors_here} + +* History: +* 6-NOV-1996 (DSB): +* Original version. +* 16-SEP-2008 (TIMJ): +* Use modern EMS interface +* {enter_changes_here} +*/ + +/* Module Macros. */ +/* ============== */ +/* Define the astCLASS macro (even although this is not a class + implementation). This indicates to header files that they should + make "protected" symbols available. */ +#define astCLASS + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "err.h" /* Interface to this module */ +#include "ems.h" /* Interface to the EMS system */ + +/* Function implementations. */ +/* ========================= */ +void astPutErr_( int status, const char *message ) { +/* +*+ +* Name: +* astPutErr + +* Purpose: +* Deliver an error message. + +* Type: +* Protected function. + +* Synopsis: +* #include "err.h" +* void astPutErr( int status, const char *message ) + +* Description: +* This function delivers an error message and (optionally) an +* accompanying status value to the user. It may be re-implemented +* in order to deliver error messages in different ways, according +* to the environment in which the AST library is being used. + +* Parameters: +* status +* The error status value. +* message +* A pointer to a null-terminated character string containing +* the error message to be delivered. This should not contain +* newline characters. + +* Notes: +* - This function is documented as "protected" but, in fact, is +* publicly accessible so that it may be re-implemented as +* required. +*- +*/ + +/* Local Variables: */ + int local_status; /* Local status value */ + +/* Make a copy of the status value supplied. Then invoke ems_rep_c to + report the error message through EMS and to associate the error + status with it. Ignore any returned status value. */ + local_status = status; + emsRep( "AST_ERROR", message, &local_status ); +} diff --git a/err_null.c b/err_null.c new file mode 100644 index 0000000..8e84ee5 --- /dev/null +++ b/err_null.c @@ -0,0 +1,108 @@ +/* +* Name: +* err_null.c + +* Purpose: +* Implement the default "err" module. + +* Description: +* This file implements the default "err" module for the AST +* library. It is used to deliver error messages if no alternative +* error delivery mechanism is provided. +* +* To provide an alternative mechanism, re-implement this module +* and link your program against the resulting library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (STARLINK) +* {enter_new_authors_here} + +* History: +* 6-NOV-1996 (DSB): +* Original version. +* {enter_changes_here} +*/ + +/* Module Macros. */ +/* ============== */ +/* Define the astCLASS macro (even although this is not a class + implementation). This indicates to header files that they should + make "protected" symbols available. */ +#define astCLASS + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "err.h" /* Interface to this module */ +#include "error.h" /* Interface to the error module */ + +/* C header files. */ +/* --------------- */ +#include + +/* Function implementations. */ +/* ========================= */ +void astPutErr_( int status_value, const char *message ) { +/* +*+ +* Name: +* astPutErr + +* Purpose: +* Deliver an error message. + +* Type: +* Protected function. + +* Synopsis: +* #include "err.h" +* void astPutErr( int status_value, const char *message ) + +* Description: +* This function delivers an error message and (optionally) an +* accompanying status value to the user. It may be re-implemented +* in order to deliver error messages in different ways, according +* to the environment in which the AST library is being used. + +* Parameters: +* status_value +* The error status value. +* message +* A pointer to a null-terminated character string containing +* the error message to be delivered. This should not contain +* newline characters. + +* Notes: +* - This function is documented as "protected" but, in fact, is +* publicly accessible so that it may be re-implemented as +* required. +*- +*/ + + +/* This is the default implementation. Simply write the message to + standard error with a newline appended. Ignore the status value. */ + int *status = astGetStatusPtr; + (void) fprintf( stderr, "%s%s\n", astOK ? "!! " : "! ", message ); +} diff --git a/error.c b/error.c new file mode 100644 index 0000000..586a8ca --- /dev/null +++ b/error.c @@ -0,0 +1,1024 @@ +/* +* Name: +* error.c + +* Purpose: +* Implement error handling functions. + +* Description: +* This file implements the Error module which provides functions +* for handling error conditions in the AST library. For a +* description of the module and its interface, see the .h file of +* the same name. +* +* Since its initial release, AST has used a global status variable +* rather than adding an explicit status parameter to the argument +* list of each AST function. This caused problems for the thread-safe +* version of AST since each thread needs its own status value. Whilst +* it would have been possible for each function to access a global +* status value via the pthreads "thread speific data key" mechanism, +* the huge number of status checks performed within AST caused this +* option to be slow. Instead AST has been modified so that every +* function has an explicit status pointer parameter. This though +* causes problems in that we cannot change the public interface to +* AST because doing so would break large amounts of external software. +* To get round this, the macros that define the public interface to +* AST have been modified so that they provide a status pointer +* automatically to the function that is being invoked. This is how +* the system works... +* +* All AST functions have an integer inherited status pointer parameter +* called "status". This parameter is "hidden" in AST functions that +* are invoked via macros (typically public and protected functions). +* This means that whilst "int *status" appears explicitly at the end +* of the function argument list (in both prototype and definition), it +* is not included in the prologue documentation, and is not included +* explicitly in the argument list when invoking the function. Instead, +* the macro that is used to invoke the function adds in the required +* status parameter to the function invocation. +* +* Macros which are invoked within AST (the protected interface) expand +* to include ", status" at the end of the function parameter list. For +* backward compatability with previous versions of AST, macros which +* are invoked from outside AST (the public interface) expand to include +* ", astGetStatusPtr" at the end of the function parameter list. The +* astGetStatusPtr function returns a pointer to the interbal AST +* status variable or to the external variable specified via astWatch. +* +* Parameter lists for functions that have variable argument lists +* (such as astError) cannot be handled in this way, since macros cannot +* have variable numbers of arguments. Instead, separate public and +* protected implementations of such functions are provided within AST. +* Protected implementations include an explicit, documented status +* pointer parameter that must be given explicitly when invoking the +* function. Public implementations do not have a status pointer +* parameter. Instead they obtain the status pointer internally using +* astGetStatusPtr. +* +* Private functions are called directly rather than via macros, and so +* they have a documented status pointer parameter that should be +* included explicitly in the parameter list when invoking the +* function. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2008-2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 2-JAN-1996 (RFWS): +* Original version. +* 8-JAN-1996 (RFWS): +* Tidied up. +* 26-JAN-1996 (RFWS): +* Incorporated changes to prologue style. +* 14-JUN-1996 (RFWS): +* Added astAt. +* 20-JUN-1996 (RFWS): +* Added astSetStatus. +* 15-JUL-1996 (RFWS): +* Sorted out the public interface. +* 16-JUL-1996 (RFWS): +* Added astWatch. +* 18-MAR-1998 (RFWS): +* Added notes about functions being available for writing +* foreign language and graphics interfaces, etc. +* 27-NOV-2002 (DSB): +* Added suppression of error reporting using astReporting. +* 11-MAR-2004 (DSB): +* Add facility to astAt to allow astAt to be called from public +* interface without private interface settings over-riding the +* public interface settings. +* 30-MAR-2005 (DSB): +* Added facility to report deferred messages when reporting is +* switched back on. +* 16-FEB-2006 (DSB): +* Improve efficiency by replacing the astOK_ function with a macro +* which tests the value of status variable. The pointer which points +* to the AST status variable are now global rather than static. +* 19-SEP-2008 (DSB): +* Big changes for the thread-safe version of AST. +* 3-FEB-2009 (DSB): +* Added astBacktrace. +*/ + +/* Define the astCLASS macro (even although this is not a class + implementation) to obtain access to protected interfaces. */ +#define astCLASS + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "err.h" /* Interface to the err module */ +#include "error.h" /* Interface to this module */ +#include "globals.h" /* Thread-safe global data access */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Configuration results. */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* Select the appropriate memory management functions. These will be the + system's malloc, free and realloc unless AST was configured with the + "--with-starmem" option, in which case they will be the starmem + malloc, free and realloc. */ +#ifdef HAVE_STAR_MEM_H +# include +# define MALLOC starMalloc +# define FREE starFree +# define REALLOC starRealloc +#else +# define MALLOC malloc +# define FREE free +# define REALLOC realloc +#endif + +/* Include execinfo.h if the backtrace function is available */ +#if HAVE_EXECINFO_H +#include +#endif + + + +/* Module Variables. */ +/* ================= */ + +/* Define macros for accessing all items of thread-safe global data + used by this module. */ +#ifdef THREAD_SAFE + +#define reporting astGLOBAL(Error,Reporting) +#define current_file astGLOBAL(Error,Current_File) +#define current_routine astGLOBAL(Error,Current_Routine) +#define current_line astGLOBAL(Error,Current_Line) +#define foreign_set astGLOBAL(Error,Foreign_Set) +#define message_stack astGLOBAL(Error,Message_Stack) +#define mstack_size astGLOBAL(Error,Mstack_Size) + +/* Since the external astPutErr function may not be thread safe, we need + to ensure that it cannot be invoked simultaneously from two different + threads. So we lock a mutex before each call to astPutErr. */ +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; + +#define INVOKE_ASTPUTERR( status, buff ) \ + ( pthread_mutex_lock( &mutex1 ), \ + astPutErr( (status), (buff) ), \ + (void) pthread_mutex_unlock( &mutex1 ) ) + +/* Define the initial values for the global data for this module. */ +#define GLOBAL_inits \ + globals->Reporting = 1; \ + globals->Current_File = NULL; \ + globals->Current_Routine = NULL; \ + globals->Current_Line = 0; \ + globals->Foreign_Set = 0; \ + globals->Mstack_Size = 0; \ + +/* Create the global initialisation function. */ +astMAKE_INITGLOBALS(Error) + + +/* If thread safety is not needed, declare globals at static variables. */ +/* -------------------------------------------------------------------- */ +#else + +/* Status variable. */ +static int internal_status = 0; /* Internal error status */ +int *starlink_ast_status_ptr = &internal_status; /* Pointer to status variable */ + +/* Reporting flag: delivery of message is supressed if zero. */ +static int reporting = 1; + +/* Error context. */ +static const char *current_file = NULL; /* Current file name pointer */ +static const char *current_routine = NULL; /* Current routine name pointer */ +static int current_line = 0; /* Current line number */ +static int foreign_set = 0; /* Have foreign values been set? */ + +/* Un-reported message stack */ +static char *message_stack[ AST__ERROR_MSTACK_SIZE ]; +static int mstack_size = 0; + +/* If thread-safety is not needed, we can invoke the external astPutErr + function directly. */ +#define INVOKE_ASTPUTERR( status, buff ) \ + astPutErr( (status), (buff) ); + +#endif + + +/* Function prototypes. */ +/* ==================== */ +static void EmptyStack( int, int * ); + +/* Function implementations. */ +/* ========================= */ +void astAt_( const char *routine, const char *file, int line, int forn, + int *status) { +/* +*+ +* Name: +* astAt + +* Purpose: +* Store a routine, file and line number context in case of error. + +* Type: +* Protected function. + +* Synopsis: +* #include "error.h" +* void astAt( const char *routine, const char *file, int line, int forn) + +* Description: +* This function stores a pointer to two strings containing the +* names of a routine and a file, together with an integer line +* number. These values are retained for subsequent use in +* reporting the context of any error that may arise. + +* Parameters: +* routine +* Pointer to a null terminated C string containing a routine +* name (which should reside in static memory). +* file +* Pointer to a null terminated C string containing a file name +* (which should reside in static memory). +* line +* The line number in the file. +* for +* Is this call being made from a foreign language interface? +* If so any values supplied will take precedence of the values +* set by the C interface. + +* Notes: +* - This function returns without action (i.e. without changing +* the stored values) if the global error status is set. It +* performs no other error checking. +* - Any (or all) of the arguments may be omitted by supplying a +* NULL or zero value (as appropriate) and will then not be included +* in any error report. +* - This function is documented as protected because it should not +* be invoked by external code. However, it is available via the +* external C interface so that it may be used when writing (e.g.) +* foreign language or graphics interfaces. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* If the values refer to a foreign interface, or if no foreign values + have yet been set, store the supplied values. */ + if( forn|| !foreign_set ) { + current_routine = routine; + current_file = file; + current_line = line; + } + +/* If the values relate to a foreign interface, set a flag which prevents + local values set later replacing them. */ + foreign_set = forn; +} + +void astBacktrace_( int *status ) { +/* +c+ +* Name: +* astBacktrace + +* Purpose: +* Display a backtrace on standard output. + +* Type: +* Protected macro. + +* Synopsis: +* #include "error.h" +* astBacktrace; + +* Description: +* This macro displays a set of messages on standard output that +* give a backtrace of the caller. It can be useful for debugging AST +* code in situations when it is not easy or possible to use a +* debugger (for instance, when debugging JNIAST). + +* Notes: +* - Only non-static function names are included in the backtrace. +* - This function requires the GNU C library. When called, it will +* just issue a warning if the GNU 'backtrace' function was not +* available when AST was configured. +c- +*/ +#if HAVE_BACKTRACE + +#define MAX_ADDR 100 + +/* Local Variables: */ + char **strings; /* Pointer to array of formated strings */ + char buf[ 120 ]; /* Output line buffer */ + int j; /* String index */ + int np; /* Number of used return addresses */ + void *buffer[ MAX_ADDR ]; /* Array of return addresses */ + +/* Get the array of return addresses. */ + np = backtrace( buffer, MAX_ADDR ); + +/* Convert them into strings. */ + strings = backtrace_symbols( buffer, np ); + +/* If succesful, display them and then free the array. Note we skip the + first one since that will refer to this function. */ + if( strings ) { + INVOKE_ASTPUTERR( astStatus, " " ); + for( j = 1; j < np; j++ ) { + sprintf( buf, "%d: %s", j, strings[j] ); + INVOKE_ASTPUTERR( astStatus, buf ); + } + free( strings ); + INVOKE_ASTPUTERR( astStatus, " " ); + +/* If not succesful, issue a warning. */ + } else { + INVOKE_ASTPUTERR( astStatus, "Cannot convert backtrace addresses into formatted strings" ); + } + +#else + INVOKE_ASTPUTERR( astStatus, "Backtrace functionality is not available " + "on the current operating system." ); +#endif +} + +void astClearStatus_( int *status ) { +/* +c++ +* Name: +* astClearStatus + +* Purpose: +* Clear the AST error status. + +* Type: +* Public macro. + +* Synopsis: +* #include "error.h" +* void astClearStatus + +* Description: +* This macro resets the AST error status to an OK value, +* indicating that an error condition (if any) has been cleared. + +* Notes: +* - If the AST error status is set to an error value (after an +* error), most AST functions will not execute and will simply +* return without action. Using astClearStatus will restore normal +* behaviour. +c-- +*/ + +/* Empty the deferred error stack without displaying the messages on the + stack. */ + EmptyStack( 0, status ); + +/* Reset the error status value. */ + *status = 0; +} + +static void EmptyStack( int display, int *status ) { +/* +* Name: +* EmptyStack + +* Purpose: +* Empty the stack of deferred error messages, optionally displaying +* them. + +* Type: +* Private function. + +* Synopsis: +* #include "error.h" +* void EmptyStack( int display, int *status ) + +* Description: +* This function removes all messages from the stack of deferred error +* messages. If "display" is non-zero it reports them using astPutErr +* before deleting them. + +* Parameters: +* display +* Report messages before deleting them? +* status +* Pointer to the integer holding the inherited status value. + +*/ + +/* Local variables; */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int i; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Loop round all messages on the stack. */ + for( i = 0; i < mstack_size; i++ ) { + +/* Display the message if required. */ + if( display ) INVOKE_ASTPUTERR( astStatus, message_stack[ i ] ); + +/* Free the memory used to hold the message. */ + FREE( message_stack[ i ] ); + message_stack[ i ] = NULL; + } + +/* Reset the stack size to zero. */ + mstack_size = 0; + +} + +void astErrorPublic_( int status_value, const char *fmt, ... ) { +/* +*+ +* Name: +* astError + +* Purpose: +* Set the AST error status and report an error message. + +* Type: +* Protected function. + +* Synopsis: +* #include "error.h" +* void astError( int status_value, const char *fmt, ... ) + +* Description: +* This function sets the AST error status to a specified value and +* reports an associated error message. + +* Parameters: +* status_value +* The new error status value to be set. +* fmt +* Pointer to a null-terminated character string containing the +* format specification for the error message, in the same way +* as for a call to the C "printf" family of functions. +* ... +* Additional optional arguments (as used by e.g. "printf") +* which specify values which are to appear in the error +* message. + +* Notes: +* This function operates just like "printf", except that: +* - The first argument is an error status. +* - The return value is void. +* - A newline is automatically appended to the error message +* (there is no need to add one). +* - This function is documented as protected because it should not +* be invoked by external code. However, it is available via the +* external C interface so that it may be used when writing (e.g.) +* foreign language or graphics interfaces. +*- + +* This is the public implementation of astError. It does not have an + status pointer parameter, but instead obtains the status pointer + explicitly using the astGetStatusPtr function. This is different to + other public functions, which typically have a status pointer parameter + that is supplied via a call to astGetStatusPtr within the associated + interface macro. The benefit of doing it the usual way is that the + public and protected implementations are the same, with the + differences between public and protecte dinterfaces wrapped up in the + associated interface macro. We cannot do this with this function + because of the variale argument list. The prologue for the astError_ + function defines the interface for use internally within AST. + +*/ + +/* Local Constants: */ +#define BUFF_LEN 1023 /* Max. length of an error message */ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char buff[ BUFF_LEN + 1 ]; /* Message buffer */ + int *status; /* Pointer to inherited status value */ + int imess; /* Index into deferred message stack */ + int nc; /* Number of characters written */ + va_list args; /* Variable argument list pointer */ + +/* Initialise the variable argument list pointer. */ + va_start( args, fmt ); + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the integer holding the inherited status value. */ + status = astGetStatusPtr; + +/* If this is the first report of an error (the global status has not + previously been set) and error context information is available, + then construct an error context message. */ + if ( astOK && + ( current_routine || current_file || current_line ) ) { + nc = sprintf( buff, "AST: Error" ); + if ( current_routine ) { + nc += sprintf( buff + nc, " in routine %s", current_routine ); + } + if ( current_line ) { + nc += sprintf( buff + nc, " at line %d", current_line ); + } + if ( current_file ) { + nc += sprintf( buff + nc, " in file %s", current_file ); + } + nc += sprintf( buff + nc, "." ); + +/* Deliver the error message unless reporting has been switched off using + astReporting. In which case store them in a static array. */ + if( reporting ) { + INVOKE_ASTPUTERR( status_value, buff ); + } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){ + imess = mstack_size++; + message_stack[ imess ] = MALLOC( strlen( buff ) + 1 ); + if( message_stack[ imess ] ) { + strcpy( message_stack[ imess ], buff ); + } + } + +/* Set the global status. */ + astSetStatus( status_value ); + } + +/* Write the error message supplied to the formatting buffer. */ + nc = vsprintf( buff, fmt, args ); + +/* Tidy up the argument pointer. */ + va_end( args ); + +/* Deliver the error message unless reporting has been switched off using + astReporting. */ + if( reporting ) { + INVOKE_ASTPUTERR( status_value, buff ); + } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){ + imess = mstack_size++; + message_stack[ imess ] = MALLOC( strlen( buff ) + 1 ); + if( message_stack[ imess ] ) { + strcpy( message_stack[ imess ], buff ); + } + } + +/* Set the error status value. */ + astSetStatus( status_value ); + +/* Undefine macros local to this function. */ +#undef BUFF_LEN +} + +void astError_( int status_value, const char *fmt, int *status, ... ) { +/* +*+ +* Name: +* astError + +* Purpose: +* Set the AST error status and report an error message. + +* Type: +* Protected function. + +* Synopsis: +* #include "error.h" +* void astError( int status_value, const char *fmt, int *status, ... ) + +* Description: +* This function sets the AST error status to a specified value and +* reports an associated error message. + +* Parameters: +* status_value +* The error status value to be set. +* fmt +* Pointer to a null-terminated character string containing the +* format specification for the error message, in the same way +* as for a call to the C "printf" family of functions. +* status +* Pointer to the integer holding the inherited status value. +* ... +* Additional optional arguments (as used by e.g. "printf") +* which specify values which are to appear in the error +* message. + +* Notes: +* This function operates just like "printf", except that: +* - The first argument is an error status. +* - The return value is void. +* - A newline is automatically appended to the error message +* (there is no need to add one). +* - This function is documented as protected because it should not +* be invoked by external code. However, it is available via the +* external C interface so that it may be used when writing (e.g.) +* foreign language or graphics interfaces. +*- + +* This is the protected implementation of astError. It has a status + pointer parameter that is not present in the public form. Different + implementations for protected and public interfaces are required + because of the variable argument list. + +*/ + +/* Local Constants: */ +#define BUFF_LEN 1023 /* Max. length of an error message */ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char buff[ BUFF_LEN + 1 ]; /* Message buffer */ + int imess; /* Index into deferred message stack */ + int nc; /* Number of characters written */ + va_list args; /* Variable argument list pointer */ + +/* Initialise the variable argument list pointer. */ + va_start( args, status ); + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* If this is the first report of an error (the global status has not + previously been set) and error context information is available, + then construct an error context message. */ + if ( astOK && + ( current_routine || current_file || current_line ) ) { + nc = sprintf( buff, "AST: Error" ); + if ( current_routine ) { + nc += sprintf( buff + nc, " in routine %s", current_routine ); + } + if ( current_line ) { + nc += sprintf( buff + nc, " at line %d", current_line ); + } + if ( current_file ) { + nc += sprintf( buff + nc, " in file %s", current_file ); + } + nc += sprintf( buff + nc, "." ); + +/* Deliver the error message unless reporting has been switched off using + astReporting. In which case store them in a static array. */ + if( reporting ) { + INVOKE_ASTPUTERR( status_value, buff ); + } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){ + imess = mstack_size++; + message_stack[ imess ] = MALLOC( strlen( buff ) + 1 ); + if( message_stack[ imess ] ) { + strcpy( message_stack[ imess ], buff ); + } + } + +/* Set the global status. */ + astSetStatus( status_value ); + } + +/* Write the error message supplied to the formatting buffer. */ + nc = vsprintf( buff, fmt, args ); + +/* Tidy up the argument pointer. */ + va_end( args ); + +/* Deliver the error message unless reporting has been switched off using + astReporting. */ + if( reporting ) { + INVOKE_ASTPUTERR( status_value, buff ); + } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){ + imess = mstack_size++; + message_stack[ imess ] = MALLOC( strlen( buff ) + 1 ); + if( message_stack[ imess ] ) { + strcpy( message_stack[ imess ], buff ); + } + } + +/* Set the error status value. */ + astSetStatus( status_value ); + +/* Undefine macros local to this function. */ +#undef BUFF_LEN +} + +int *astGetStatusPtr_(){ +/* +*+ +* Name: +* astGetStatusPtr + +* Purpose: +* Return a pointer to the integer holding the inherited status value. + +* Type: +* Protected function. + +* Synopsis: +* #include "error.h" +* int *astGetStatusPtr; + +* Description: +* This macro returns a pointer to the integer holding the inherited +* status pointer. This will either be an internal global integer +* (possibly stored as thread specific data), or an ineger specified +* via the astWatch function. + +* Returned Value: +* A pointer to the integer holding the inherited status value. + +*- +*/ + +/* The thread-safe version of AST stores the status pointer in thread + specific data, using the key stored in the global variable + "starlink_ast_status_key". */ +#if defined(THREAD_SAFE) + astDECLARE_GLOBALS + AstStatusBlock *sb; + + astGET_GLOBALS(NULL); + sb = (AstStatusBlock *) pthread_getspecific(starlink_ast_status_key); + return sb->status_ptr; + +/* The non thread-safe version of AST stores the status pointer in the + global variable "starlink_ast_status_ptr". */ +#else + return starlink_ast_status_ptr; +#endif +} + +/* +c++ +* Name: +* astOK + +* Purpose: +* Test whether AST functions have been successful. + +* Type: +* Public macro. + +* Synopsis: +* #include "error.h" +* int astOK + +* Description: +* This macro returns a boolean value (0 or 1) to indicate if +* preceding AST functions have completed successfully +* (i.e. without setting the AST error status). If the error status +* is set to an error value, a value of zero is returned, otherwise +* the result is one. + +* Returned Value: +* astOK +* One if the AST error status is OK, otherwise zero. + +* Notes: +* - If the AST error status is set to an error value (after an +* error), most AST functions will not execute and will simply +* return without action. To clear the error status and restore +* normal behaviour, use astClearStatus. +c-- +*/ + + +int astReporting_( int report, int *status ) { +/* +c+ +* Name: +* astReporting + +* Purpose: +* Controls the reporting of error messages. + +* Type: +* Protected function. + +* Synopsis: +* #include "error.h" +* int astReporting( int report ) + +* Description: +* Error messages supplied to astError will only be delivered to the +* underlying error system if the "Reporting" flag is set to a +* non-zero value. Setting this flag to zero suppresses the reporting +* of error messages (the value of the AST error status however is +* unaffected). Instead, the reports are saved in an internal message +* stack. When reporting is switched back on again, any messages on this +* stack of deferred messages will be reported (and the stack emptied) +* if the AST error status is not astOK. Also the stack is emptied each +* time astClearStatus is called (the stacked messages are not displayed +* in this case). + +* Parameters: +* report +* The new value for the Reporting flag. + +* Returned Value: +* The original value of the Reporting flag. + +* Notes: +* - The Reporting flag is initially set to 1. +c- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int oldval; /* Original "reporting" value */ + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Save the original reporting value, and then store the new value. */ + oldval = reporting; + reporting = report; + +/* If we are now reporting errors, flush any messages on the error stack. + This causes the messages to be displayed and the stack emptied. */ + if( reporting ) EmptyStack( 1, status ); + +/* Return the original reporting value. */ + return oldval; +} + +/* +c++ +* Name: +* astSetStatus + +* Purpose: +* Set the AST error status to an explicit value. + +* Type: +* Public function. + +* Synopsis: +* #include "error.h" +* void astSetStatus( int status_value ) + +* Description: +* This function sets the AST error status to the value supplied. +* It does not cause any error message to be produced and should +* not be used as part of normal error reporting. Its purpose is +* simply to communicate to AST that an error has occurred in some +* other item of software. +* +* For example, a source or sink function supplied as an argument +* to astChannel or astFitsChan might use this to signal that an +* input/output error has occurred. AST could then respond by +* terminating the current read or write operation. + +* Parameters: +* status_value +* The new error status value to be set. + +* Notes: +* - If the AST error status is set to an error value, most AST +* functions will not execute and will simply return without +* action. To clear the error status and restore normal behaviour, +* use astClearStatus. +c-- +*/ + +/* +c++ +* Name: +* astStatus + +* Purpose: +* Obtain the current AST error status value. + +* Type: +* Public function. + +* Synopsis: +* #include "error.h" +* int astStatus + +* Description: +* This function returns the current value of the AST error status. + +* Returned Value: +* astStatus +* The AST error status value. + +* Notes: +* - If the AST error status is set to an error value (after an +* error), most AST functions will not execute and will simply +* return without action. To clear the error status and restore +* normal behaviour, use astClearStatus. +c-- +*/ + +int *astWatch_( int *status_ptr ) { +/* +c++ +* Name: +* astWatch + +* Purpose: +* Identify a new error status variable for the AST library. + +* Type: +* Public function. + +* Synopsis: +* #include "error.h" +* int *astWatch( int *status_ptr ) + +* Description: +* This function allows a new error status variable to be accessed +* by the AST library when checking for and reporting error +* conditions. +* +* By default, the library uses an internal integer error status +* which is set to an error value if an error occurs. Use of +* astWatch allows the internal error status to be replaced by an +* integer variable of your choosing, so that the AST library can +* share its error status directly with other code which uses the +* same error detection convention. +* +* If an alternative error status variable is supplied, it is used +* by all related AST functions and macros (e.g. astOK, astStatus +* and astClearStatus). + +* Parameters: +* status_ptr +* Pointer to an int whose value is to be used subsequently as +* the AST inherited status value. If a NULL pointer is supplied, +* the AST library will revert to using its own internal error status. + +* Returned Value: +* astWatch() +* Address of the previous error status variable. This may later +* be passed back to astWatch to restore the previous behaviour +* of the library. (Note that on the first invocation of +* astWatch the returned value will be the address of the +* internal error status variable.) + +* Notes: +* - This function is not available in the FORTRAN 77 interface to +* the AST library. +c-- +*/ + +/* Local Variables: */ + int *result; /* Value to be returned */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +#if defined(THREAD_SAFE) + AstStatusBlock *sb = NULL; +#endif + +/* Ensure that the thread-specific status block has been created and + ininitialised. */ + astGET_GLOBALS(NULL); + +#if defined(THREAD_SAFE) + sb = (AstStatusBlock *) pthread_getspecific( starlink_ast_status_key ); + result = sb->status_ptr; + sb->status_ptr = status_ptr ? status_ptr : &(sb->internal_status); +#else + result = starlink_ast_status_ptr; + starlink_ast_status_ptr = status_ptr ? status_ptr : &internal_status; +#endif + +/* Return the old address. */ + return result; +} + + diff --git a/error.h b/error.h new file mode 100644 index 0000000..28907fc --- /dev/null +++ b/error.h @@ -0,0 +1,327 @@ +#if !defined( ERROR_INCLUDED ) /* Include this file only once */ +#define ERROR_INCLUDED 1 +/* +*+ +* Name: +* error.h + +* Purpose: +* Define the interface to the Error module. + +* Description: +* This module defines functions which implement error handling and +* reporting of error messages from within the AST library. A +* simple public interface is included to allow the AST error +* status to be tested and cleared after an error. +* +* Note that this module is not a class implementation, although it +* resembles one. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2008 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S Berry (Starlink) +* NG: Norman Gray (Starlink) + +* History: +* 2-JAN-1996 (RFWS): +* Original version. +* 26-JAN-1996 (RFWS): +* Added function interfaces. +* 14-JUN-1996 (RFWS): +* Added AST__FAC and astAt. +* 20-JUN-1996 (RFWS): +* Added astSetStatus. +* 16-JUL-1996 (RFWS): +* Added astWatch. +* 18-MAR-1998 (RFWS): +* Make interface available for writing foreign language and +* graphics interfaces, etc. +* 27-NOV-2002 (DSB): +* Added astReporting. +* 14-MAR-2005 (NG): +* Added astAssert +* 20-MAY-2005 (DSB): +* Modified astAssert so that it does nothing if the AST error +* status is already set, and also so that does nothing unless +* the DEBUG macro is defined. +* 16-FEB-2006 (DSB): +* Improve efficiency by replacing the astOK_ function with a macro +* which tests the value of status variable. The pointer which points +* to the status variable are now global rather than static. +* 1-MAR-2006 (DSB): +* Remove astAssert. +* 19-SEP-2008 (DSB) +* Big changes for thread-safe version of AST. +*- +*/ + +/* Suppress "operands are evaluated in unspecified order" warnings from + the Intel icc compiler. These are caused by the astGetStatusPtr_ + function being called several times within each of the macros that + form the public interface for AST. */ +#ifdef __INTEL_COMPILER +#pragma warning(disable:981) +#endif + + +/* Include files. */ +/* ============== */ +#if defined(THREAD_SAFE) +#include +#endif + + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) /* Protected */ + +/* Define a facility number that is unique to this library. The number here + * is the facility code assigned to the AST library, but it doesn't have to + * be this number -- it only has to be probably unique. If that code were + * ever to change, then you can update this number if you feel it's tidier + * that way. */ +#define AST__FAC (1521) + +/* Max number of messages which can be deferred when reporting is + switched off. */ +#define AST__ERROR_MSTACK_SIZE 100 + +#endif + +/* This macro expands to an invocation of a specified function, together + with a call to astAt to record the file and line number at which the + invocation occurs. These are included in public error reports. This is + only done for invocations from outside of AST (i.e. public invocations).*/ +#if defined(astCLASS) || defined(astFORTRAN77) +#define astERROR_INVOKE(function) (function) +#else +#define astERROR_INVOKE(function) (astAt_(NULL,__FILE__,__LINE__,0,astGetStatusPtr),(function)) +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type definitions */ +/* ================ */ + +/* Define a structure to hold information about an error context. */ +typedef struct AstErrorContext { + int reporting; /* Value of error reporting flag at start of context */ + int ok; /* Was the status value OK at start of context? */ + int status; /* The status value at the start of the context */ +} AstErrorContext; + +#if defined(THREAD_SAFE) && ( defined(astCLASS) || defined(astFORTRAN77) ) + +/* Define a structure holding all data items that are global within the + error.c file. */ +typedef struct AstErrorGlobals { + +/* Reporting flag: delivery of message is supressed if zero. */ + int Reporting; + +/* Error context. */ + const char *Current_File; /* Current file name pointer */ + const char *Current_Routine; /* Current routine name pointer */ + int Current_Line; /* Current line number */ + int Foreign_Set; /* Have foreign values been set? */ + +/* Un-reported message stack */ + char *Message_Stack[ AST__ERROR_MSTACK_SIZE ]; + int Mstack_Size; + +} AstErrorGlobals; + +/* Structure to hold the internal status variable, and the status + pointer for a single thread. */ +typedef struct AstStatusBlock { + int internal_status; + int *status_ptr; +} AstStatusBlock; + +#endif + + +/* Function Macros. */ +/* ================ */ + +#if defined(astCLASS) + +/* +*+ +* Name: +* astErrorBegin + +* Purpose: +* Begin a new error reporting context. + +* Type: +* Protected macro. + +* Synopsis: +* #include "error.h" +* astErrorBegin( AstErrorContext *context ); + +* Description: +* This macro starts a new error reporting context. It saves any +* existing error status in the supplied ontext structure, and then +* clears the status value. It also defers further error reporting. +* +* Each invocation of this macro should be followed (eventually) by a +* matching invocation of astErrorEnd. + +* Parameters: +* context +* Pointer to a structure in which to to storeinformation about the +* current error reporting context. This structure should be passed +* unchanged to the corresponding invocation of astErrorEnd. + +*- +*/ +#define astErrorBegin(context) {\ +\ +/* Save the original error status value. */ \ + (context)->status = astStatus; \ +\ +/* Save a flag indicating if the original error status was good. */ \ + (context)->ok = astOK; \ +\ +/* Switch off the immediate delivery of error messages, recording the \ + original value of the reporting flag. */ \ + (context)->reporting = astReporting( 0 ); \ +\ +/* Clear any existing error condition. */ \ + astClearStatus; \ +} + + +/* +*+ +* Name: +* astErrorEnd + +* Purpose: +* End an error reporting context. + +* Type: +* Protected macro. + +* Synopsis: +* #include "error.h" +* astErrorEnd( AstErrorContext *context ); + +* Description: +* This macro ends an error reporting context started using +* astErrorBegin. +* +* Each invocation of this macro should correspond to an earlier +* invocation of astErrorBegin. + +* Parameters: +* context +* Pointer to a structure holding information returned by the +* matching invocation of astErrorBegin. + +*- +*/ +#define astErrorEnd(context) { \ +\ +/* If an error condition existed when astErrorBegin was called, and \ + another error has since occurred, clear the deferred messages \ + reported during the error context without displaying them. */ \ + if( !(context)->ok && !astOK ) astClearStatus; \ +\ +/* Put the error reporting flag back to its original value. This will \ + have the effect of displaying any remaining errors reported within \ + the error context (they will already have been cleared if an error \ + condition existed at the start of the context). */ \ + astReporting( (context)->reporting ); \ +\ +/* If an error condition existed at the start of the context, re-instate \ + the original status value. */ \ + if( !(context)->ok ) astSetStatus( (context)->status ); \ +} +#endif + +/* Function prototypes. */ +/* ==================== */ + +int *astWatch_( int * ); +void astClearStatus_( int * ); +int *astGetStatusPtr_( void )__attribute__((pure)); +void astAt_( const char *, const char *, int, int, int * ); + +#if defined(astCLASS) || defined(astFORTRAN77) /* Protected only */ +int astReporting_( int, int * ); +void astError_( int, const char *, int *, ... )__attribute__((format(printf,2,4))); +void astBacktrace_( int * ); +#if defined(THREAD_SAFE) +void astInitErrorGlobals_( AstErrorGlobals * ); +#endif +#endif + +void astErrorPublic_( int, const char *, ... )__attribute__((format(printf,2,3))); + + + +/* Function interfaces. */ +/* ==================== */ +/* These wrap up the functions defined by this module to make them + easier to use. */ + +#define astWatch(status_ptr) astWatch_(status_ptr) +#define astGetStatusPtr astGetStatusPtr_() +#define astOK (astStatus==0) +#define astSetStatus(status_value) (astStatus=(status_value)) + +#if defined(astCLASS) /* Protected */ + +#define astAt(routine,file,line) astAt_(routine,file,line,0,status) +#define astClearStatus astClearStatus_(status) +#define astStatus (*status) +#define astError astError_ +#define astReporting(report) astReporting_(report,status) +#define astBacktrace astBacktrace_(status) + +#elif defined(astFORTRAN77) + +#define astAt(routine,file,line) astAt_(routine,file,line,1,STATUS) +#define astClearStatus astClearStatus_(status) +#define astStatus (*status) +#define astError astError_ +#define astReporting(report) astReporting_(report,status) + +#else + +#define astAt(routine,file,line) astAt_(routine,file,line,1,astGetStatusPtr) +#define astClearStatus astClearStatus_(astGetStatusPtr) +#define astStatus (*astGetStatusPtr) +#define astError astErrorPublic_ + +#endif + +#endif diff --git a/f77.h.in b/f77.h.in new file mode 100644 index 0000000..3a886fc --- /dev/null +++ b/f77.h.in @@ -0,0 +1,1096 @@ +/* +*+ +* Name: +* f77.h and cnf.h + +* Purpose: +* C - FORTRAN interace macros and prototypes + +* Language: +* C (part ANSI, part not) + +* Type of Module: +* C include file + +* Description: +* For historical reasons two files, F77.h and cnf.h are required +* but the have now been combined and for new code, only one is +* necessary. +* +* This file defines the macros needed to write C functions that are +* designed to be called from FORTRAN programs, and to do so in a +* portable way. Arguments are normally passed by reference from a +* FORTRAN program, and so the F77 macros arrange for a pointer to +* all arguments to be available. This requires no work on most +* machines, but will actually generate the pointers on a machine +* that passes FORTRAN arguments by value. + +* Notes: +* - Macros are provided to handle the conversion of logical data +* values between the way that FORTRAN represents a value and the +* way that C represents it. +* - Macros are provided to convert variables between the FORTRAN and +* C method of representing them. In most cases there is no +* conversion required, the macros just arrange for a pointer to +* the FORTRAN variable to be set appropriately. The possibility that +* FORTRAN and C might use different ways of representing integer +* and floating point values is considered remote, the macros are +* really only there for completeness and to assist in the automatic +* generation of C interfaces. +* - For character variables the macros convert between +* the FORTRAN method of representing them (fixed length, blank +* filled strings) and the C method (variable length, null +* terminated strings) using calls to the CNF functions. + +* Implementation Deficiencies: +* - The macros support the K&R style of function definition, but +* this file may not work with all K&R compilers as it contains +* "#if defined" statements. These could be replaced with #ifdef's +* if necessary. This has not been done as is would make the code +* less clear and the need for support for K&R sytle definitions +* should disappear as ANSI compilers become the default. + +* Copyright: +* Copyright (C) 1991, 1993 Science & Engineering Research Council. +* Copyright (C) 2006 Particle Physics and Astronomy Research Council. +* Copyright (C) 2007,2008 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be +* useful,but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA +* 02110-1301, USA + +* Authors: +* PMA: Peter Allan (Starlink, RAL) +* AJC: Alan Chipperfield (Starlink, RAL) +* TIMJ: Tim Jenness (JAC) +* PWD: Peter W. Draper (JAC, Durham University) +* {enter_new_authors_here} + +* History: +* 23-MAY-1991 (PMA): +* Original version. +* 19-JUN-1991 (PMA): +* Removed VMS versions of IM(EX)PORT_LOGICAL macros that tried +* to convert data representations. +* 24-JUN-1991 (PMA): +* Changed the names of IMPORT macros to GENPTR. +* Removed the EXPORT macros. +* 27-JUN-1991 (PMA): +* Modified DECstation specific stuff to allow use of the c89 +* compiler. +* 8-JUL-1991 (PMA): +* Added macros to call FORTRAN from C. +* 16-OCT-1991 (PMA): +* Remove type_ARRAY2 definitions. +* Remove the length argument from CHARACTER_ARRAY and the +* dimension specifier from GENPTR_type_ARRAY. +* Add extra brackets to F77_ISFALSE and F77_ISTRUE. +* 25-OCT-1991 (PMA): +* Changed "if defined(sun4)" to "if defined(sun)" +* 2-JUN-1992 (PMA): +* Changed "if defined(mips)" to "if defined(ultrix)" to prevent +* those definitions being used on a Silicon Graphics machine. +* 11-JUN-1992 (PMA): +* Changed "if defined(ultrix)" back to "if defined(mips)" so that +* it still works on OSF/1 on a DECstation. +* Add support for general non-ANSI compilers, but not basic K&R +* ones. +* 12-JUN-1992 (PMA): +* Change declaration of dummy scalar arguments to be const +* pointers. Change declaration of dummy array arguments to be +* const pointers. +* 5-JAN-1993 (PMA): +* Changed "if defined(mips)" so that it will recognise a +* DECstation running Ultrix or OSF/1, but not a Silicon Graphics +* workstation. +* Change the definition of F77_BYTE_TYPE to add "signed". +* Redefine this on VMS where signed is invalid syntax. +* Add new types of UBYTE and UWORD. +* 8-JAN-1993 (PMA): +* Fix bug in the definition of CHARACTER_RETURN_VALUE. There was +* an extraneous space. +* Add a macro F77_POINTER_TYPE and use it to define POINTER. +* 13-JAN-1993 (PMA): +* Start to add support for K&R function definitions. These are +* done on a per machine basis. +* 16-APR-1993 (PMA): +* Change the definition of F77_POINTER_TYPE from int to unsigned +* int. +* 7-MAY-1993 (PMA): +* Change from using a null comment as a token concatenation +* operator to using the internal macro _f77_x on non-ANSI +* systems. +* 10-MAY-1993 (PMA): +* Finish adding K&R support. This will form version 2.0 of F77. +* 10-MAY-1993 (PMA): +* Add support for Alpha OSF/1. +* 9-JUL-1993 (PMA): +* Add further POINTER macros: POINTER_ARRAY, +* GENPTR_POINTER_ARRAY, DECLARE_POINTER, DECLARE_POINTER_ARRAY, +* POINTER_ARG, POINTER_ARRAY_ARG, F77_POINTER_FUNCTION, +* KR_POINTER_ARRAY. +* 24-AUG-1993 (PMA): +* Add const to the VMS definitions of CHARACTER and CHARACTER_ARRAY. +* 3-NOV-1993 (PMA): +* Remove K&R stuff to a separate file. +* Released on Unix as version 2.0 of CNF. +* 11-NOV-1993 (PMA): +* Return to using the null comment to concatenate text on non-ANSI +* systems as _f77_x caused problems with the c89 -common flag on +* DECstations. +* 23-JAN-1996 (AJC): +* Add SUBROUTINE, type_FUNCTION, SUBROUTINE_ARG, +* type_FUNCTION_ARG, GENPTR_SUBROUTINE and GENPTR_type_FUNCTION +* required for passed subroutine and function name. +* 29-JAN-1996 (AJC): +* Add the dynamic CHARACTER_ macros +* and CHARACTER_ARG_TYPE +* 22-FEB-1996 (AJC): +* Add CHARACTER_RETURN_ARG +* 23-MAY-1996 (AJC): +* Add DECLARE_CHARACTER_ARRAY_DYN +* F77_CREATE_CHARACTER_ARRAY +* F77_CHARACTER_ARG_TYPE +* 14-JUN-1996 (AJC): +* Add DECLARE_LOGICAL_ARRAY_DYN +* F77_CREATE_LOGICAL_ARRAY +* 21-JUN-1996 (AJC): +* Add cast to _ARRAY_ARGs to allow multidimensional arrays +* 17-MAR-1998 (AJC): +* Add DECLARE, CREATE and FREE dynamic array macros for all types +* Changed CREATE_CHARACTER_ARRAY and CREATE_LOGICAL_ARRAY to use +* number of elements rather than dimensions. +* Add IMPORT, EXPORT and ASSOC macros +* 22-JUL-1998 (AJC): +* Combined F77.h and cnf.h +* 23-SEP-1998 (AJC): +* Input strings for cnf -> const char * +* Input int arrays for cnf -> const int * +* 4-NOV-1998 (AJC): +* Bring cnf prototypes in line with .c routines +* 8-FEB-1999 (AJC): +* Added cnf_mem stuff +* 9-FEB-1999 (AJC): +* Use cnf_cptr/fptr for IMPORT/EXPORT_POINTER +* 16-FEB-1999 (AJC): +* Added missing cnf_fptr prototype +* 23-JUN-1999 (AJC): +* Change cnf_name to cnfName +* and add macros for cnf_name +* 1-DEC-1999 (AJC): +* Add define cnf_free +* 7-JAN-2000 (AJC): +* Correct omission of F77_ASSOC_UBYTE_ARRAY +* Correct F77_EXPORT_UWORD_ARRAY +* 25-AUG-2005 (TIMJ): +* Add cnfInitRTL +* 23-FEB-2006 (TIMJ): +* Add cnfRealloc +* Use starMalloc rather than malloc in F77_CREATE_POINTER_ARRAY +* (since it needs to match what goes on in cnfFree) +* 21-JUN-2006 (PWD): +* Changed to use a different return type for REAL functions. This +* effects g77 under 64-bit, when the f2c bindings expect the return +* value of a REAL function to be a double, not a float. +* 25-SEP-2006 (PWD): +* Introduced F77_CREATE_IMPORT_CHARACTER. Match length of +* F77_CREATE_CHARACTER to result from cnfCref. +* 13-JUL-2007 (PWD): +* Parameterise the type of Fortran character string lengths. Can +* be long. +* 7-OCT-2008 (TIMJ): +* Initialise pointers. +* 11-MAY-2011 (DSB): +* Added F77_LOCK +* {enter_further_changes_here} +* + +* Bugs: +* {note_any_bugs_here} + +*- +------------------------------------------------------------------------------ +*/ +#if !defined(CNF_MACROS) +#define CNF_MACROS + +#include +#include +/* This initial sections defines values for all macros. These are the */ +/* values that are generally appropriate to an ANSI C compiler on Unix. */ +/* For macros that have different values on other systems, the macros */ +/* should be undefined and then redefined in the system specific sections. */ +/* At the end of this section, some macros are redefined if the compiler */ +/* is non-ANSI. */ + +#if defined(__STDC__) || defined(VMS) +#define CNF_CONST const +#else +#define CNF_CONST +#endif + +/* ----- Macros common to calling C from FORTRAN and FORTRAN from C ---- */ + + +/* --- External Names --- */ + +/* Macro to define the name of a Fortran routine or common block. This */ +/* ends in an underscore on many Unix systems. */ + +#define F77_EXTERNAL_NAME(X) X ## _ + + +/* --- Logical Values --- */ + +/* Define the values that are used to represent the logical values TRUE */ +/* and FALSE in Fortran. */ + +#define F77_TRUE 1 +#define F77_FALSE 0 + +/* Define macros that evaluate to C logical values, given a FORTRAN */ +/* logical value. */ + +#define F77_ISTRUE(X) ( X ) +#define F77_ISFALSE(X) ( !( X ) ) + + +/* --- Common Blocks --- */ + +/* Macros used in referring to FORTRAN common blocks. */ + +#define F77_BLANK_COMMON @BLANK_COMMON_SYMBOL@ +#define F77_NAMED_COMMON(B) F77_EXTERNAL_NAME(B) + + + +/* ------------------ Calling C from FORTRAN --------------------------- */ + + +/* --- Data Types --- */ + +/* Define macros for all the Fortran data types (except COMPLEX, which is */ +/* not handled by this package). */ + +#define F77_INTEGER_TYPE int +#define F77_REAL_TYPE float +#define F77_REAL_FUNCTION_TYPE @REAL_FUNCTION_TYPE@ +#define F77_DOUBLE_TYPE double +#define F77_LOGICAL_TYPE int +#define F77_CHARACTER_TYPE char +#define F77_BYTE_TYPE signed char +#define F77_WORD_TYPE short int +#define F77_UBYTE_TYPE unsigned char +#define F77_UWORD_TYPE unsigned short int + +/* Define macros for the type of a CHARACTER and CHARACTER_ARRAY argument */ +#define F77_CHARACTER_ARG_TYPE char +#define F77_CHARACTER_ARRAY_ARG_TYPE char + +/* Define a macro to use when passing arguments that STARLINK FORTRAN */ +/* treats as a pointer. From the point of view of C, this type should be */ +/* (void *), but it is declared as type unsigned int as we actually pass */ +/* an INTEGER from the FORTRAN routine. The distinction is important for */ +/* architectures where the size of an INTEGER is not the same as the size */ +/* of a pointer. */ + +#define F77_POINTER_TYPE unsigned int + + +/* --- Subroutine Names --- */ + +/* This declares that the C function returns a value of void. */ + +#define F77_SUBROUTINE(X) void F77_EXTERNAL_NAME(X) + + +/* --- Function Names --- */ + +/* Macros to define the types and names of functions that return values. */ +/* Due the the different ways that function return values could be */ +/* implemented, it is better not to use functions, but to stick to using */ +/* subroutines. */ + +/* Character functions are implemented, but in a way that cannot be */ +/* guaranteed to be portable although it will work on VMS, SunOS, Ultrix */ +/* and DEC OSF/1. It would be better to return the character value as a */ +/* subroutine argument where possible, rather than use a character */ +/* function. */ + +#define F77_INTEGER_FUNCTION(X) F77_INTEGER_TYPE F77_EXTERNAL_NAME(X) +#define F77_REAL_FUNCTION(X) F77_REAL_FUNCTION_TYPE F77_EXTERNAL_NAME(X) +#define F77_DOUBLE_FUNCTION(X) F77_DOUBLE_TYPE F77_EXTERNAL_NAME(X) +#define F77_LOGICAL_FUNCTION(X) F77_LOGICAL_TYPE F77_EXTERNAL_NAME(X) +#define F77_CHARACTER_FUNCTION(X) void F77_EXTERNAL_NAME(X) +#define F77_BYTE_FUNCTION(X) F77_BYTE_TYPE F77_EXTERNAL_NAME(X) +#define F77_WORD_FUNCTION(X) F77_WORD_TYPE F77_EXTERNAL_NAME(X) +#define F77_UBYTE_FUNCTION(X) F77_UBYTE_TYPE F77_EXTERNAL_NAME(X) +#define F77_UWORD_FUNCTION(X) F77_UWORD_TYPE F77_EXTERNAL_NAME(X) +#define F77_POINTER_FUNCTION(X) F77_POINTER_TYPE F77_EXTERNAL_NAME(X) + + +/* --- Character return value for a function --- */ + +#define CHARACTER_RETURN_VALUE(X) CHARACTER(X) TRAIL(X) +#define CHARACTER_RETURN_ARG(X) CHARACTER_ARG(X) TRAIL_ARG(X) + +/* --- Dummy Arguments --- */ + +/* Macros for defining subroutine arguments. All these macros take a */ +/* single argument; the name of the parameter. On most systems, a numeric */ +/* argument is passed as a pointer. */ + +#define INTEGER(X) F77_INTEGER_TYPE *CNF_CONST X +#define REAL(X) F77_REAL_TYPE *CNF_CONST X +#define DOUBLE(X) F77_DOUBLE_TYPE *CNF_CONST X +#define LOGICAL(X) F77_LOGICAL_TYPE *CNF_CONST X +#define BYTE(X) F77_BYTE_TYPE *CNF_CONST X +#define WORD(X) F77_WORD_TYPE *CNF_CONST X +#define UBYTE(X) F77_UBYTE_TYPE *CNF_CONST X +#define UWORD(X) F77_UWORD_TYPE *CNF_CONST X + +/* Pointer arguments. Define a pointer type for passing pointer values */ +/* between subroutines. */ + +#define POINTER(X) F77_POINTER_TYPE *CNF_CONST X + +/* EXTERNAL arguments. Define a passed subroutine or function name */ +#define SUBROUTINE(X) void (*X)() +#define INTEGER_FUNCTION(X) F77_INTEGER_TYPE (*X)() +#define REAL_FUNCTION(X) F77_REAL_TYPE (*X)() +#define DOUBLE_FUNCTION(X) F77_DOUBLE_TYPE (*X)() +#define LOGICAL_FUNCTION(X) F77_LOGICAL_TYPE (*X)() +#define CHARACTER_FUNCTION(X) F77_CHARACTER_TYPE (*X)() +#define BYTE_FUNCTION(X) F77_BYTE_TYPE (*X)() +#define WORD_FUNCTION(X) F77_WORD_TYPE (*X)() +#define UBYTE_FUNCTION(X) F77_UBYTE_TYPE (*X)() +#define UWORD_FUNCTION(X) F77_UWORD_TYPE (*X)() +#define POINTER_FUNCTION(X) F77_POINTER_TYPE (*X)() + +/* Array arguments. */ + +#define INTEGER_ARRAY(X) F77_INTEGER_TYPE *CNF_CONST X +#define REAL_ARRAY(X) F77_REAL_TYPE *CNF_CONST X +#define DOUBLE_ARRAY(X) F77_DOUBLE_TYPE *CNF_CONST X +#define LOGICAL_ARRAY(X) F77_LOGICAL_TYPE *CNF_CONST X +#define BYTE_ARRAY(X) F77_BYTE_TYPE *CNF_CONST X +#define WORD_ARRAY(X) F77_WORD_TYPE *CNF_CONST X +#define UBYTE_ARRAY(X) F77_UBYTE_TYPE *CNF_CONST X +#define UWORD_ARRAY(X) F77_UWORD_TYPE *CNF_CONST X + +#define POINTER_ARRAY(X) F77_POINTER_TYPE *CNF_CONST X + +/* Macros to handle character arguments. */ + +/* Character arguments can be passed in many ways. The purpose of these */ +/* macros and the GENPTR_CHARACTER macro (defined in the next section) is */ +/* to generate a pointer to a character variable called ARG and an integer */ +/* ARG_length containing the length of ARG. If these two variables are */ +/* available directly from the argument list of the routine, then the */ +/* GENPTR_CHARACTER macro is null, otherwise it works on intermediate */ +/* variables. */ + +#define CHARACTER(X) F77_CHARACTER_TYPE *CNF_CONST X +#define TRAIL(X) ,@TRAIL_TYPE@ X ## _length +#define CHARACTER_ARRAY(X) F77_CHARACTER_TYPE *CNF_CONST X + + +/* --- Getting Pointers to Arguments --- */ + +/* Macros that ensure that a pointer to each argument is available for the */ +/* programmer to use. Usually this means that these macros are null. On */ +/* VMS, a pointer to a character variable has to be generated. If a */ +/* particular machine were to pass arguments by reference, rather than by */ +/* value, then these macros would construct the appropriate pointers. */ + +#define GENPTR_INTEGER(X) +#define GENPTR_REAL(X) +#define GENPTR_DOUBLE(X) +#define GENPTR_CHARACTER(X) +#define GENPTR_LOGICAL(X) +#define GENPTR_BYTE(X) +#define GENPTR_WORD(X) +#define GENPTR_UBYTE(X) +#define GENPTR_UWORD(X) +#define GENPTR_POINTER(X) + +#define GENPTR_INTEGER_ARRAY(X) +#define GENPTR_REAL_ARRAY(X) +#define GENPTR_DOUBLE_ARRAY(X) +#define GENPTR_CHARACTER_ARRAY(X) +#define GENPTR_LOGICAL_ARRAY(X) +#define GENPTR_BYTE_ARRAY(X) +#define GENPTR_WORD_ARRAY(X) +#define GENPTR_UBYTE_ARRAY(X) +#define GENPTR_UWORD_ARRAY(X) +#define GENPTR_POINTER_ARRAY(X) + +#define GENPTR_SUBROUTINE(X) +#define GENPTR_INTEGER_FUNCTION(X) +#define GENPTR_REAL_FUNCTION(X) +#define GENPTR_DOUBLE_FUNCTION(X) +#define GENPTR_CHARACTER_FUNCTION(X) +#define GENPTR_LOGICAL_FUNCTION(X) +#define GENPTR_BYTE_FUNCTION(X) +#define GENPTR_WORD_FUNCTION(X) +#define GENPTR_UBYTE_FUNCTION(X) +#define GENPTR_UWORD_FUNCTION(X) +#define GENPTR_POINTER_FUNCTION(X) + + + +/* ------------------ Calling FORTRAN from C --------------------------- */ + + +/* --- Declare variables --- */ + +#define DECLARE_INTEGER(X) F77_INTEGER_TYPE X +#define DECLARE_REAL(X) F77_REAL_TYPE X +#define DECLARE_DOUBLE(X) F77_DOUBLE_TYPE X +#define DECLARE_LOGICAL(X) F77_LOGICAL_TYPE X +#define DECLARE_BYTE(X) F77_BYTE_TYPE X +#define DECLARE_WORD(X) F77_WORD_TYPE X +#define DECLARE_UBYTE(X) F77_UBYTE_TYPE X +#define DECLARE_UWORD(X) F77_UWORD_TYPE X + +#define DECLARE_POINTER(X) F77_POINTER_TYPE X + +#define DECLARE_CHARACTER(X,L) F77_CHARACTER_TYPE X[L]; \ + const int X##_length = L + + +/* --- Declare arrays --- */ + +#define DECLARE_INTEGER_ARRAY(X,D) F77_INTEGER_TYPE X[D] +#define DECLARE_REAL_ARRAY(X,D) F77_REAL_TYPE X[D] +#define DECLARE_DOUBLE_ARRAY(X,D) F77_DOUBLE_TYPE X[D] +#define DECLARE_LOGICAL_ARRAY(X,D) F77_LOGICAL_TYPE X[D] +#define DECLARE_BYTE_ARRAY(X,D) F77_BYTE_TYPE X[D] +#define DECLARE_WORD_ARRAY(X,D) F77_WORD_TYPE X[D] +#define DECLARE_UBYTE_ARRAY(X,D) F77_UBYTE_TYPE X[D] +#define DECLARE_UWORD_ARRAY(X,D) F77_UWORD_TYPE X[D] +#define DECLARE_POINTER_ARRAY(X,D) F77_POINTER_TYPE X[D] +#define DECLARE_CHARACTER_ARRAY(X,L,D) F77_CHARACTER_TYPE X[D][L]; \ + const int X##_length = L + +/* --- Declare and construct dynamic CHARACTER arguments --- */ +#define DECLARE_CHARACTER_DYN(X) F77_CHARACTER_TYPE *X = NULL;\ + int X##_length = 0 +#define F77_CREATE_CHARACTER(X,L) X=cnfCref(L);\ + X##_length = (L>0?L:1) + +/* Declare Dynamic Fortran arrays */ +#define DECLARE_INTEGER_ARRAY_DYN(X) F77_INTEGER_TYPE *X = NULL +#define DECLARE_REAL_ARRAY_DYN(X) F77_REAL_TYPE *X = NULL +#define DECLARE_DOUBLE_ARRAY_DYN(X) F77_DOUBLE_TYPE *X = NULL +#define DECLARE_LOGICAL_ARRAY_DYN(X) F77_LOGICAL_TYPE *X = NULL +#define DECLARE_BYTE_ARRAY_DYN(X) F77_BYTE_TYPE *X = NULL +#define DECLARE_WORD_ARRAY_DYN(X) F77_WORD_TYPE *X = NULL +#define DECLARE_UBYTE_ARRAY_DYN(X) F77_UBYTE_TYPE *X = NULL +#define DECLARE_UWORD_ARRAY_DYN(X) F77_UWORD_TYPE *X = NULL +#define DECLARE_POINTER_ARRAY_DYN(X) F77_POINTER_TYPE *X = NULL +#define DECLARE_CHARACTER_ARRAY_DYN(X) F77_CHARACTER_TYPE *X = NULL;\ + int X##_length = 0 + +/* Create arrays dynamic Fortran arrays for those types which require */ +/* Separate space for Fortran and C arrays */ +/* Character and logical are already defined */ +/* For most types there is nothing to do */ +#define F77_CREATE_CHARACTER_ARRAY(X,L,N) \ + {int f77dims[1];f77dims[0]=N;X=cnfCrefa(L,1,f77dims);X##_length=L;} +#define F77_CREATE_CHARACTER_ARRAY_M(X,L,N,D) X=cnfCrefa(L,N,D);\ + X##_length = L +#define F77_CREATE_LOGICAL_ARRAY(X,N) \ + {int f77dims[1];f77dims[0]=N;X=cnfCrela(1,f77dims);} +#define F77_CREATE_LOGICAL_ARRAY_M(X,N,D) X=cnfCrela(N,D) +#define F77_CREATE_INTEGER_ARRAY(X,N) +#define F77_CREATE_REAL_ARRAY(X,N) +#define F77_CREATE_DOUBLE_ARRAY(X,N) +#define F77_CREATE_BYTE_ARRAY(X,N) +#define F77_CREATE_UBYTE_ARRAY(X,N) +#define F77_CREATE_WORD_ARRAY(X,N) +#define F77_CREATE_UWORD_ARRAY(X,N) +#define F77_CREATE_POINTER_ARRAY(X,N)\ + X=(F77_POINTER_TYPE *) malloc(N*sizeof(F77_POINTER_TYPE)) + +/* Associate Fortran arrays with C arrays */ +/* These macros ensure that there is space somewhere for the Fortran */ +/* array. They are complemetary to the CREATE_type_ARRAY macros */ +#define F77_ASSOC_CHARACTER_ARRAY(F,C) +#define F77_ASSOC_LOGICAL_ARRAY(F,C) +#define F77_ASSOC_INTEGER_ARRAY(F,C) F=C +#define F77_ASSOC_REAL_ARRAY(F,C) F=C +#define F77_ASSOC_DOUBLE_ARRAY(F,C) F=C +#define F77_ASSOC_BYTE_ARRAY(F,C) F=C +#define F77_ASSOC_UBYTE_ARRAY(F,C) F=C +#define F77_ASSOC_WORD_ARRAY(F,C) F=C +#define F77_ASSOC_UWORD_ARRAY(F,C) F=C +#define F77_ASSOC_POINTER_ARRAY(F,C) + +/* Free created dynamic arrays */ +/* Character and logical are already defined */ +/* For most types there is nothing to do */ +#define F77_FREE_INTEGER(X) +#define F77_FREE_REAL(X) +#define F77_FREE_DOUBLE(X) +#define F77_FREE_BYTE(X) +#define F77_FREE_UBYTE(X) +#define F77_FREE_WORD(X) +#define F77_FREE_UWORD(X) +#define F77_FREE_POINTER(X) cnfFree((void *)X); +#define F77_FREE_CHARACTER(X) cnfFreef( X ) +#define F77_FREE_LOGICAL(X) cnfFree( (char *)X ) + +/* --- IMPORT and EXPORT of values --- */ +/* Export C variables to Fortran variables */ +#define F77_EXPORT_CHARACTER(C,F,L) cnfExprt(C,F,L) +#define F77_EXPORT_DOUBLE(C,F) F=C +#define F77_EXPORT_INTEGER(C,F) F=C +#define F77_EXPORT_LOGICAL(C,F) F=C?F77_TRUE:F77_FALSE +#define F77_EXPORT_REAL(C,F) F=C +#define F77_EXPORT_BYTE(C,F) F=C +#define F77_EXPORT_WORD(C,F) F=C +#define F77_EXPORT_UBYTE(C,F) F=C +#define F77_EXPORT_UWORD(C,F) F=C +#define F77_EXPORT_POINTER(C,F) F=cnfFptr(C) +#define F77_EXPORT_LOCATOR(C,F) cnfExpch(C,F,DAT__SZLOC) + +/* Allow for character strings to be NULL, protects strlen. Note this + * does not allow lengths to differ. */ +#define F77_CREATE_EXPORT_CHARACTER(C,F) \ + if (C) { \ + F77_CREATE_CHARACTER(F,strlen(C)); \ + F77_EXPORT_CHARACTER(C,F,F##_length); \ + } else { \ + F77_CREATE_CHARACTER(F,1); \ + F77_EXPORT_CHARACTER(" ",F,F##_length); \ + } + +/* Export C arrays to Fortran */ +/* Arrays are assumed to be 1-d so just the number of elements is given */ +/* This may be OK for n-d arrays also */ +/* CHARACTER arrays may be represented in C as arrays of arrays of char or */ +/* as arrays of pointers to char (the _P variant) */ +#define F77_EXPORT_CHARACTER_ARRAY(C,LC,F,LF,N) \ + {int f77dims[1];f77dims[0]=N;cnfExprta(C,LC,F,LF,1,f77dims);} +#define F77_EXPORT_CHARACTER_ARRAY_P(C,F,LF,N) \ + {int f77dims[1];f77dims[0]=N;cnfExprtap(C,F,LF,1,f77dims);} +#define F77_EXPORT_DOUBLE_ARRAY(C,F,N) F=(F77_DOUBLE_TYPE *)C +#define F77_EXPORT_INTEGER_ARRAY(C,F,N) F=(F77_INTEGER_TYPE *)C +#define F77_EXPORT_LOGICAL_ARRAY(C,F,N) \ + {int f77dims[1];f77dims[0]=N;cnfExpla(C,F,1,f77dims);} +#define F77_EXPORT_REAL_ARRAY(C,F,N) F=(F77_REAL_TYPE *)C +#define F77_EXPORT_BYTE_ARRAY(C,F,N) F=(F77_BYTE_TYPE *)C +#define F77_EXPORT_WORD_ARRAY(C,F,N) F=(F77_WORD_TYPE *)C +#define F77_EXPORT_UBYTE_ARRAY(C,F,N) F=(F77_UBYTE_TYPE *)C +#define F77_EXPORT_UWORD_ARRAY(C,F,N) F=(F77_UWORD_TYPE * )C +#define F77_EXPORT_POINTER_ARRAY(C,F,N) \ + {int f77i;for (f77i=0;f77i +#endif + + +#undef F77_CHARACTER_ARG_TYPE +#define F77_CHARACTER_ARG_TYPE struct dsc$descriptor_s +#undef F77_CHARACTER_ARRAY_ARG_TYPE +#define F77_CHARACTER_ARRAY_ARG_TYPE struct dsc$descriptor_a +#undef CHARACTER +#define CHARACTER(X) F77_CHARACTER_ARG_TYPE *CNF_CONST X/**/_arg +#undef TRAIL +#define TRAIL(X) +#undef CHARACTER_ARRAY +#define CHARACTER_ARRAY(X) F77_CHARACTER_ARRAY_ARG_TYPE *CNF_CONST X/**/_arg +#undef GENPTR_CHARACTER +#define GENPTR_CHARACTER(X) \ + F77_CHARACTER_TYPE *X = X/**/_arg->dsc$a_pointer; \ + int X/**/_length = X/**/_arg->dsc$w_length; +#undef GENPTR_CHARACTER_ARRAY +#define GENPTR_CHARACTER_ARRAY(X) GENPTR_CHARACTER(X) + + +/* --- Logical Values --- */ + +#undef F77_TRUE +#define F77_TRUE -1 +#undef F77_ISTRUE +#define F77_ISTRUE(X) ( (X)&1 ) +#undef F77_ISFALSE +#define F77_ISFALSE(X) ( ! ( (X)&1 ) ) + + +/* --- Common Blocks --- */ + +#undef F77_BLANK_COMMON +#define F77_BLANK_COMMON $BLANK + + +/* --- Declare Variables --- */ + +#undef DECLARE_CHARACTER +#define DECLARE_CHARACTER(X,L) \ + F77_CHARACTER_TYPE X[L]; const int X/**/_length = L; \ + F77_CHARACTER_ARG_TYPE X/**/_descr = \ + { L, DSC$K_DTYPE_T, DSC$K_CLASS_S, X }; \ + F77_CHARACTER_ARG_TYPE *X/**/_arg = &X/**/_descr +#undef DECLARE_CHARACTER_ARRAY +#define DECLARE_CHARACTER_ARRAY(X,L,D) \ + F77_CHARACTER_TYPE X[D][L]; const int X/**/_length = L; \ + F77_CHARACTER_ARRAY_ARG_TYPE X/**/_descr = \ + { L, DSC$K_DTYPE_T, DSC$K_CLASS_S, X }; \ + F77_CHARACTER_ARRAY_ARG_TYPE *X/**/_arg = &X/**/_descr + + +/* --- The dynamic allocation of character arguments --- */ +#undef DECLARE_CHARACTER_DYN +#define DECLARE_CHARACTER_DYN(X) int X/**/_length;\ + F77_CHARACTER_ARG_TYPE *X/**/_arg;\ + F77_CHARACTER_TYPE *X +#undef DECLARE_CHARACTER_ARRAY_DYN +#define DECLARE_CHARACTER_ARRAY_DYN(X) int X/**/_length;\ + F77_CHARACTER_ARRAY_ARG_TYPE *X/**/_arg;\ + F77_CHARACTER_TYPE *X +#undef F77_CREATE_CHARACTER +#define F77_CREATE_CHARACTER(X,L) X/**/_arg = cnfCref(L);\ + X = X/**/_arg->dsc$a_pointer; \ + X/**/_length = X/**/_arg->dsc$w_length +#undef F77_CREATE_CHARACTER_ARRAY +#define F77_CREATE_CHARACTER_ARRAY(X,L,N) \ + {int f77dims[1];f77dims[0]=N;X/**/_arg=cnfCrefa(L,1,f77dims);X/**/_length=L;} +#define F77_CREATE_CHARACTER_ARRAY_M(X,L,N,D) X/**/_arg = cnfCrefa(L,N,D);\ + X = X/**/_arg->dsc$a_pointer; \ + X/**/_length = X/**/_arg->dsc$w_length +#undef F77_FREE_CHARACTER +#define F77_FREE_CHARACTER(X) cnfFreef( X/**/_arg ) + +/* --- Pass arguments to a FORTRAN routine --- */ + +#undef CHARACTER_ARG +#define CHARACTER_ARG(X) X/**/_arg +#undef CHARACTER_ARRAY_ARG +#define CHARACTER_ARRAY_ARG(X) X/**/_arg +#undef TRAIL_ARG +#define TRAIL_ARG(X) + +#endif /* VMS */ + +/* ----------------------------------------------------------------------- */ + +/*-------------------------- +| DECstation Ultrix (cc) | +| DECstation Ultrix (c89) | +| DECstation OSF/1 | +| Alpha OSF/1 | + --------------------------*/ + +/* Do this complicated set of definitions as a single #if cannot be */ +/* continued across multiple lines. */ + +#if defined(mips) && defined(ultrix) +#define _dec_unix 1 +#endif +#if defined(__mips) && defined(__ultrix) +#define _dec_unix 1 +#endif +#if defined(__mips__) && defined(__osf__) +#define _dec_unix 1 +#endif +#if defined(__alpha) && defined(__osf__) +#define _dec_unix 1 +#endif + +#if _dec_unix + +/* The macros for Ultrix are the same as the standard ones except for ones */ +/* dealing with logical values. The ANSI definitions work with the c89 */ +/* compiler, and the non ANSI definitions work with the cc compiler. */ +/* The same applies to DEC OSF/1, except that its cc compiler is ANSI */ +/* compliant. */ + + +/* --- Logical Values --- */ + +/* Redefine macros that evaluate to a C logical value, given a FORTRAN */ +/* logical value. These definitions are only valid when used with the DEC */ +/* FORTRAN for RISC compiler. If you are using the earlier FORTRAN for */ +/* RISC compiler from MIPS, then these macros should be deleted. */ + +#undef F77_TRUE +#define F77_TRUE -1 +#undef F77_ISTRUE +#define F77_ISTRUE(X) ( (X)&1 ) +#undef F77_ISFALSE +#define F77_ISFALSE(X) ( ! ( (X)&1 ) ) + + +#endif /* DEC Unix */ + +/* +*+ +* Name: +* cnf.h + +* Purpose: +* Function prototypes for cnf routines + +* Language: +* ANSI C + +* Type of Module: +* C include file + +* Description: +* These are the prototype definitions for the functions in the CNF +* library. They are used used in mixing C and FORTRAN programs. + +* Copyright: +* Copyright (C) 1991 Science & Engineering Research Council + +* Authors: +* PMA: Peter Allan (Starlink, RAL) +* AJC: Alan Chipperfield (Starlink, RAL) +* {enter_new_authors_here} + +* History: +* 23-MAY-1991 (PMA): +* Original version. +* 12-JAN-1996 (AJC): +* Add cnf_cref and cnf_freef +* 14-JUN-1996 (AJC): +* Add cnf_crefa, imprta, exprta +* crela, impla, expla +* 18-JUL-1996 (AJC): +* Add impch and expch +* 17-MAR-1998 (AJC): +* Add imprtap and exprtap +* {enter_changes_here} + +* Bugs: +* {note_any_bugs_here} + +*- +------------------------------------------------------------------------------ +*/ +void cnfInitRTL( int, char** ); +void *cnfCalloc( size_t, size_t ); +void cnfCopyf( const char *source_f, int source_len, char *dest_f, + int dest_len ); +void *cnfCptr( F77_POINTER_TYPE ); +char *cnfCreat( int length ); +F77_CHARACTER_ARG_TYPE *cnfCref( int length ); +F77_CHARACTER_ARG_TYPE *cnfCrefa( int length, int ndims, const int *dims ); +char *cnfCreib( const char *source_f, int source_len ); +char *cnfCreim( const char *source_f, int source_len ); +F77_LOGICAL_TYPE *cnfCrela( int ndims, const int *dims ); +void cnfExpch( const char *source_c, char *dest_f, int nchars ); +void cnfExpla( const int *source_c, F77_LOGICAL_TYPE *dest_f, int ndims, + const int *dims ); +void cnfExpn( const char *source_c, int max, char *dest_f, int dest_len ); +void cnfExprt( const char *source_c, char *dest_f, int dest_len ); +void cnfExprta( const char *source_c, int source_len, char *dest_f, + int dest_len, int ndims, const int *dims ); +void cnfExprtap( char *const *source_c, char *dest_f, int dest_len, + int ndims, const int *dims ); +F77_POINTER_TYPE cnfFptr( void *cpointer ); +void cnfFree( void * ); +void cnfFreef( F77_CHARACTER_ARG_TYPE *temp ); +void cnfImpb( const char *source_f, int source_len, char *dest_c ); +void cnfImpbn( const char *source_f, int source_len, int max, char *dest_c ); +void cnfImpch( const char *source_f, int nchars, char *dest_c ); +void cnfImpla( const F77_LOGICAL_TYPE *source_f, int *dest_c, + int ndims, const int *dims ); +void cnfImpn( const char *source_f, int source_len, int max, char *dest_c ); +void cnfImprt( const char *source_f, int source_len, char *dest_c ); +void cnfImprta( const char *source_f, int source_len, char *dest_c, + int dest_len, int ndims, const int *dims ); +void cnfImprtap( const char *source_f, int source_len, char *const *dest_c, + int dest_len, int ndims, const int *dims ); +int cnfLenc( const char *source_c ); +int cnfLenf( const char *source_f, int source_len ); +void *cnfMalloc( size_t ); +void *cnfRealloc( void *, size_t ); +int cnfRegp( void * ); +void cnfUregp( void * ); +void cnfLock( void ); +void cnfUnlock( void ); +#endif + +#ifndef CNF_OLD_DEFINED +#define CNF_OLD_DEFINED +/* Define old names to be new names */ +#define cnf_calloc cnfCalloc +#define cnf_copyf cnfCopyf +#define cnf_cptr cnfCptr +#define cnf_creat cnfCreat +#define cnf_cref cnfCref +#define cnf_crefa cnfCrefa +#define cnf_creib cnfCreib +#define cnf_creim cnfCreim +#define cnf_crela cnfCrela +#define cnf_expch cnfExpch +#define cnf_expla cnfExpla +#define cnf_expn cnfExpn +#define cnf_exprt cnfExprt +#define cnf_exprta cnfExprta +#define cnf_exprtap cnfExprtap +#define cnf_fptr cnfFptr +#define cnf_free cnfFree +#define cnf_freef cnfFreef +#define cnf_impb cnfImpb +#define cnf_impbn cnfImpbn +#define cnf_impch cnfImpch +#define cnf_impla cnfImpla +#define cnf_impn cnfImpn +#define cnf_imprt cnfImprt +#define cnf_imprta cnfImprta +#define cnf_imprtap cnfImprtap +#define cnf_lenc cnfLenc +#define cnf_lenf cnfLenf +#define cnf_malloc cnfMalloc +#define cnf_regp cnfRegp +#define cnf_uregp cnfUregp + +#endif /* CNF_MACROS */ diff --git a/fbox.c b/fbox.c new file mode 100644 index 0000000..8dff34f --- /dev/null +++ b/fbox.c @@ -0,0 +1,110 @@ +/* +*+ +* Name: +* fbox.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Box class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Box class. + +* Routines Defined: +* AST_ISABOX +* AST_BOX + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-MAR-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "box.h" /* C interface to the Box class */ + +F77_LOGICAL_FUNCTION(ast_isabox)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISABOX", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsABox( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_box)( INTEGER(FRAME), + INTEGER(FORM), + DOUBLE_ARRAY(POINT1), + DOUBLE_ARRAY(POINT2), + INTEGER(UNC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_INTEGER(FORM) + GENPTR_DOUBLE_ARRAY(POINT1) + GENPTR_DOUBLE_ARRAY(POINT2) + GENPTR_INTEGER(UNC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_BOX", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astBox( astI2P( *FRAME ), *FORM, POINT1, POINT2, + astI2P( *UNC ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fchannel.c b/fchannel.c new file mode 100644 index 0000000..9f70315 --- /dev/null +++ b/fchannel.c @@ -0,0 +1,473 @@ +/* +*+ +* Name: +* fchannel.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Channel class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Channel class. + +* Routines Defined: +* AST_CHANNEL +* AST_ISACHANNEL +* AST_READ +* AST_WRITE + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 6-SEP-1996 (RFWS): +* Original version. +* 12-DEC-1996 (RFWS): +* Added SOURCE and SINK arguments to AST_CHANNEL. +* 13-NOV-2003 (DSB): +* Made SourceWrap and SinkWrap into protected functions rather +* than private functions, so that they can be used in fxmlchan.c +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "channel.h" /* C interface to the Channel class */ + +#include + +/* Module Variables. */ +/* ================= */ +static char *line_in = NULL; /* Pointer to incoming line of text */ +static const char *line_out = NULL; /* Pointer to outgoing line of text */ + +/* Prototypes for external functions. */ +/* ================================== */ +/* This is the null function defined by the FORTRAN interface in fobject.c. */ +F77_SUBROUTINE(ast_null)( void ); + +/* Source and sink function interfaces. */ +/* ==================================== */ +/* These functions are concerned with allowing FORTRAN implementations + of Channel source and sink functions to be passed to the Channel + class and invoked when necessary by C code in the main class + implementation. All FORTRAN-specific aspects of this interface are + encapsulated here. */ +F77_SUBROUTINE(ast_getline)( CHARACTER(LINE), + INTEGER(L), + INTEGER(STATUS) + TRAIL(LINE) ) { +/* +f++ +* Name: +* AST_GETLINE + +* Purpose: +* Obtain text to be written by a Channel sink routine. + +* Type: +* Public function. + +* Synopsis: +* CALL AST_GETLINE( LINE, L, STATUS ) + +* Description: +* This routine should only be used when implementing a routine +* which will be passed as the SINK argument to AST_CHANNEL. It +* should be used to obtain (from the AST library) each line of +* text which is to be written to the external data sink. One such +* line should be obtained in this way for each invocation of the +* sink routine. + +* Parameters: +* LINE = CHARACTER * ( * ) (Returned) +* The line of text to be written. Depending on the length of +* character variable supplied, the returned text may be +* truncated if necessary. Note, however, that it will not be +* padded with blanks in order to fill this variable. +* L = INTEGER (Returned) +* The number of characters returned, which may be zero. Note +* that characters beyond the L'th character in the LINE +* variable are not modified and may therefore contain junk. +* STATUS = INTEGER (Given and Returned) +* The global status. + +* Notes: +* - This routine is only available in the Fortran interface to the +* AST library. +f-- +*/ + +/* Argument Pointers: */ + GENPTR_CHARACTER(LINE) + GENPTR_INTEGER(L) + +/* Local Variables: */ + int i; /* Loop counter for characters */ + +/* Set the error context and watch the STATUS value. */ + astAt( "AST_GETLINE", NULL, 0 ); + astWatchSTATUS( + +/* Initialise the returned string length. */ + *L = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If there is no outgoing line ready (e.g. if this routine has been + called at an inappropriate point), we simply return + nothing. Otherwise, loop to copy the text into the character + argument supplied, ensuring that its length is not exceeded. */ + if ( line_out ) { + for ( i = 0; line_out[ i ] && ( i < LINE_length ); i++ ) { + LINE[ i ] = line_out[ i ]; + } + +/* Return the number of characters copied. */ + *L = i; + } + ) +} + +F77_SUBROUTINE(ast_putline)( CHARACTER(LINE), + INTEGER(L), + INTEGER(STATUS) + TRAIL(LINE) ) { +/* +f++ +* Name: +* AST_PUTLINE + +* Purpose: +* Store a text line read by a Channel source routine. + +* Type: +* Public function. + +* Synopsis: +* CALL AST_PUTLINE( LINE, L, STATUS ) + +* Description: +* This routine should only be used when implementing a routine +* which will be passed as the SOURCE argument to AST_CHANNEL. It +* should be used to pass back (to the AST library) each line of +* text read from the external data source. One such line should be +* passed back in this way for each invocation of the source +* routine. + +* Parameters: +* LINE = CHARACTER * ( * ) (Given) +* A character string containing the line of input text which +* has been read. +* L = INTEGER (Given) +* The number of characters in the input line, which may be +* zero. If there is no more input available (e.g. an end of +* file has been reached), this value should be set negative and +* this will terminate the read operation on the Channel. +* STATUS = INTEGER (Given and Returned) +* The global status. + +* Notes: +* - This routine is only available in the Fortran interface to the +* AST library. +f-- +*/ + +/* Argument Pointers: */ + GENPTR_CHARACTER(LINE) + GENPTR_INTEGER(L) + +/* Local Variables: */ + int l; /* Number of characters in line */ + +/* Set the error context and watch the STATUS value. */ + astAt( "AST_PUTLINE", NULL, 0 ); + astWatchSTATUS( + +/* Initialise the incoming line pointer. */ + line_in = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the number of characters in the line. */ + l = *L; + +/* Negative values (or STATUS set) indicate end of input. If the value + is not negative, limit the number of characters to the length of + the character variable supplied. */ + if ( l >= 0 ) { + if ( l > LINE_length ) l = LINE_length; + +/* Create a dynamic string and fill it with the incoming data. Store + the resulting pointer, which will be picked up by the SourceWrap + function. */ + line_in = astString( LINE, l ); + } + ) +} + +void astSinkWrap_( void (* sink)( const char * ), const char *line, int *status ) { +/* +*+ +* Name: +* astSinkWrap + +* Purpose: +* Wrapper function to invoke a FORTRAN Channel sink function. + +* Type: +* Protected function. + +* Synopsis: +* void astSinkWrap( void (* sink)( const char * ), const char *line ) + +* Description: +* This function invokes the sink function whose pointer is +* supplied in order to write an output line to an external data +* store. + +* Parameters: +* sink +* Pointer to a sink function. This should result from a cast +* applied to a pointer to a function, with a single FORTRAN +* INTEGER error status argument, that returns void. This is +* the form of Channel sink function employed by the FORTRAN +* language interface to the AST library. +* line +* Pointer to a constant null-terminated string containing the +* line of output text. +*- +*/ + +/* Local Variables; */ + DECLARE_INTEGER(STATUS); /* FORTRAN error status variable */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the pointer to the output text line in the (static) + "line_out" variable, where it will be accessed by the sink function + invoking AST_GETLINE. */ + line_out = line; + +/* Cast the sink function pointer to a pointer to the FORTRAN + subroutine and then invoke it. Transfer the AST error status to and + from the subroutine's error status argument. */ + STATUS = astStatus; + ( *(void (*)()) sink )( INTEGER_ARG(&STATUS) ); + astSetStatus( STATUS ); + +/* Clear the outgoing line pointer. */ + line_out = NULL; +} + +char *astSourceWrap_( const char *(* source)( void ), int *status ) { +/* +*+ +* Name: +* astSourceWrap + +* Purpose: +* Wrapper function to invoke a FORTRAN Channel source function. + +* Type: +* Protected function. + +* Synopsis: +* char *astSourceWrap( const char *(* source)( void ) ) + +* Description: +* This function invokes the source function whose pointer is +* supplied in order to read the next input line from an external +* data store. It then returns a pointer to a dynamic string +* containing a copy of the text that was read. + +* Parameters: +* source +* Pointer to a source function. This should result from a cast +* applied to a pointer to a function, with a single FORTRAN +* INTEGER error status argument, that returns void. This is +* the form of Channel source function employed by the FORTRAN +* language interface to the AST library. + +* Returned Value: +* A pointer to a dynamically allocated, null terminated string +* containing a copy of the text that was read. This string must be +* freed by the caller (using astFree) when no longer required. +* +* A NULL pointer will be returned if there is no more input text +* to read. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*- +*/ + +/* Local Variables: */ + DECLARE_INTEGER(STATUS); /* FORTRAN error status variable */ + char *result; /* Result pointer to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise the incoming line pointer. */ + line_in = NULL; + +/* Cast the source function pointer to a pointer to the FORTRAN + subroutine and then invoke it. Transfer the AST error status to and + from the subroutine's error status argument. */ + STATUS = astStatus; + ( *(void (*)()) source )( INTEGER_ARG(&STATUS) ); + astSetStatus( STATUS ); + +/* This should result in a pointer to a dynamic string containing the + input text being stored in the (static) "line_in" variable as a + result of the source function invoking AST_PUTLINE. Save this + string pointer and clear the original. */ + result = line_in; + line_in = NULL; + +/* If an error occurred, free the returned string. */ + if ( ! astOK ) result = astFree( result ); + +/* Return the result. */ + return result; +} + +/* FORTRAN interface functions. */ +/* ============================ */ +/* These functions implement the remainder of the FORTRAN interface. */ +F77_INTEGER_FUNCTION(ast_channel)( void (* SOURCE)(), + void (* SINK)(), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + const char *(* source)( void ); + int i; + void (* sink)( const char * ); + + astAt( "AST_CHANNEL", NULL, 0 ); + astWatchSTATUS( + +/* Set the source and sink function pointers to NULL if a pointer to + the null routine AST_NULL has been supplied. */ + source = (const char *(*)( void )) SOURCE; + if ( source == (const char *(*)( void )) F77_EXTERNAL_NAME(ast_null) ) { + source = NULL; + } + sink = (void (*)( const char * )) SINK; + if ( sink == (void (*)( const char * )) F77_EXTERNAL_NAME(ast_null) ) { + sink = NULL; + } + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astChannelFor( source, astSourceWrap, sink, astSinkWrap, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isachannel)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISACHANNEL", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAChannel( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_read)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_READ", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astRead( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_write)( INTEGER(THIS), + INTEGER(OBJECT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(OBJECT) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_WRITE", NULL, 0 ); + astWatchSTATUS( + RESULT = astWrite( astI2P( *THIS ), astI2P( *OBJECT ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_warnings)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_WARNINGS", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astWarnings( astI2P( *THIS ) ) ); + ) + return RESULT; +} + + + diff --git a/fcircle.c b/fcircle.c new file mode 100644 index 0000000..b217a4f --- /dev/null +++ b/fcircle.c @@ -0,0 +1,128 @@ +/* +*+ +* Name: +* fcircle.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Circle class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Circle class. + +* Routines Defined: +* AST_ISACIRCLE +* AST_CIRCLE + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 31-AUG-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "circle.h" /* C interface to the Circle class */ + + +F77_LOGICAL_FUNCTION(ast_isacircle)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISACIRCLE", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsACircle( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_circle)( INTEGER(FRAME), + INTEGER(FORM), + DOUBLE_ARRAY(POINT1), + DOUBLE_ARRAY(POINT2), + INTEGER(UNC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_INTEGER(FORM) + GENPTR_DOUBLE_ARRAY(POINT1) + GENPTR_DOUBLE_ARRAY(POINT2) + GENPTR_INTEGER(UNC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_CIRCLE", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astCircle( astI2P( *FRAME ), *FORM, POINT1, POINT2, + astI2P( *UNC ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_circlepars)( INTEGER(THIS), + DOUBLE_ARRAY(CENTRE), + DOUBLE(RADIUS), + DOUBLE_ARRAY(P1), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(CENTRE) + GENPTR_DOUBLE(RADIUS) + GENPTR_DOUBLE_ARRAY(P1) + + astAt( "AST_CIRCLEPARS", NULL, 0 ); + astWatchSTATUS( + astCirclePars( astI2P( *THIS ), CENTRE, RADIUS, P1 ); + ) +} + diff --git a/fcmpframe.c b/fcmpframe.c new file mode 100644 index 0000000..8d99d3b --- /dev/null +++ b/fcmpframe.c @@ -0,0 +1,104 @@ +/* +*+ +* Name: +* fcmpframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST CmpFrame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the CmpFrame class. + +* Routines Defined: +* AST_CMPFRAME +* AST_ISACMPFRAME + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 30-SEP-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "cmpframe.h" /* C interface to the CmpFrame class */ + +F77_LOGICAL_FUNCTION(ast_isacmpframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISACMPFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsACmpFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_cmpframe)( INTEGER(FRAME1), + INTEGER(FRAME2), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME1) + GENPTR_INTEGER(FRAME2) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_CMPFRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astCmpFrame( astI2P( *FRAME1 ), astI2P( *FRAME2 ), + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fcmpmap.c b/fcmpmap.c new file mode 100644 index 0000000..2888519 --- /dev/null +++ b/fcmpmap.c @@ -0,0 +1,106 @@ +/* +*+ +* Name: +* fcmpmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST CmpMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the CmpMap class. + +* Routines Defined: +* AST_ISACMPMAP +* AST_CMPMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 25-SEP-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "cmpmap.h" /* C interface to the CmpMap class */ + +F77_LOGICAL_FUNCTION(ast_isacmpmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISACMPMAP", NULL, 0 ); + RESULT = astIsACmpMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_cmpmap)( INTEGER(MAP1), + INTEGER(MAP2), + LOGICAL(SERIES), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(MAP1) + GENPTR_INTEGER(MAP2) + GENPTR_LOGICAL(SERIES) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_CMPMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astCmpMap( astI2P( *MAP1 ), astI2P( *MAP2 ), + F77_ISTRUE( *SERIES ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fcmpregion.c b/fcmpregion.c new file mode 100644 index 0000000..f494d28 --- /dev/null +++ b/fcmpregion.c @@ -0,0 +1,107 @@ +/* +*+ +* Name: +* fcmpregion.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST CmpRegion class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the CmpRegion class. + +* Routines Defined: +* AST_ISACMPREGION +* AST_CMPREGION + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 12-OCT-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "cmpregion.h" /* C interface to the CmpRegion class */ + + +F77_LOGICAL_FUNCTION(ast_isacmpregion)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISACMPREGION", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsACmpRegion( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_cmpregion)( INTEGER(REG1), + INTEGER(REG2), + INTEGER(OPER), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(REG1) + GENPTR_INTEGER(REG2) + GENPTR_INTEGER(OPER) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_CMPREGION", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astCmpRegion( astI2P( *REG1 ), astI2P( *REG2 ), + *OPER, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fdsbspecframe.c b/fdsbspecframe.c new file mode 100644 index 0000000..f0ec5db --- /dev/null +++ b/fdsbspecframe.c @@ -0,0 +1,100 @@ +/* +*+ +* Name: +* fdsbspecframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST DSBSpecFrame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the DSBSpecFrame class. + +* Routines Defined: +* AST_ISADSBSPECFRAME +* AST_DSBSPECFRAME + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 5-AUG-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "dsbspecframe.h" /* C interface to the DSBSpecFrame class */ + +F77_LOGICAL_FUNCTION(ast_isadsbspecframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISADSBSPECFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsADSBSpecFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_dsbspecframe)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_DSBSPECFRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astDSBSpecFrame( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + diff --git a/fdssmap.c b/fdssmap.c new file mode 100644 index 0000000..8e570de --- /dev/null +++ b/fdssmap.c @@ -0,0 +1,75 @@ +/* +*+ +* Name: +* fdssmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST DssMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the DssMap class. + +* Routines Defined: +* AST_ISADSSMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 19-FEB-1997 (DSB): +* Original version. +* 5-SEP-1997 (RFWS) +* Removed the AST_DSSMAP function (now protected, so not +* required in the Fortran interface). +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "dssmap.h" /* C interface to the DssMap class */ + +F77_LOGICAL_FUNCTION(ast_isadssmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISADSSMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsADssMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} diff --git a/fellipse.c b/fellipse.c new file mode 100644 index 0000000..2570a2c --- /dev/null +++ b/fellipse.c @@ -0,0 +1,136 @@ +/* +*+ +* Name: +* fellipse.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Ellipse class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Ellipse class. + +* Routines Defined: +* AST_ISAELLIPSE +* AST_ELLIPSE + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 31-AUG-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "ellipse.h" /* C interface to the Ellipse class */ + + +F77_LOGICAL_FUNCTION(ast_isaellipse)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAELLIPSE", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAEllipse( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_ellipse)( INTEGER(FRAME), + INTEGER(FORM), + DOUBLE_ARRAY(POINT1), + DOUBLE_ARRAY(POINT2), + DOUBLE_ARRAY(POINT3), + INTEGER(UNC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_INTEGER(FORM) + GENPTR_DOUBLE_ARRAY(POINT1) + GENPTR_DOUBLE_ARRAY(POINT2) + GENPTR_DOUBLE_ARRAY(POINT3) + GENPTR_INTEGER(UNC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_ELLIPSE", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astEllipse( astI2P( *FRAME ), *FORM, POINT1, POINT2, + POINT3, astI2P( *UNC ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_ellipsepars)( INTEGER(THIS), + DOUBLE_ARRAY(CENTRE), + DOUBLE(A), + DOUBLE(B), + DOUBLE(ANGLE), + DOUBLE_ARRAY(P1), + DOUBLE_ARRAY(P2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(CENTRE) + GENPTR_DOUBLE(A) + GENPTR_DOUBLE(B) + GENPTR_DOUBLE(ANGLE) + GENPTR_DOUBLE_ARRAY(P1) + GENPTR_DOUBLE_ARRAY(P2) + + astAt( "AST_ELLIPSEPARS", NULL, 0 ); + astWatchSTATUS( + astEllipsePars( astI2P( *THIS ), CENTRE, A, B, ANGLE, P1, P2 ); + ) +} + diff --git a/ferror.c b/ferror.c new file mode 100644 index 0000000..73ab7eb --- /dev/null +++ b/ferror.c @@ -0,0 +1,58 @@ +/* +*+ +* Name: +* ferror.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Error module. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Error module. + +* Routines Defined: +* None. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 15-JUL-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "error.h" /* C interface to the Error module */ + +/* At present there are no Fortran callable routines in this module. */ diff --git a/fetch b/fetch new file mode 100755 index 0000000..1ca0578 --- /dev/null +++ b/fetch @@ -0,0 +1,5 @@ + +here=${PWD} +cd ${AST_REF} +cp $@ ${here} +cd ${here} diff --git a/ffitschan.c b/ffitschan.c new file mode 100644 index 0000000..fd0d419 --- /dev/null +++ b/ffitschan.c @@ -0,0 +1,1022 @@ +/* +*+ +* Name: +* ffitschan.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST FitsChan class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the FitsChan class. + +* Routines Defined: +* AST_DELFITS +* AST_PURGEWCS +* AST_FINDFITS +* AST_FITSCHAN +* AST_ISAFITSCHAN +* AST_PUTCARDS +* AST_PUTFITS +* AST_RETAINFITS +* AST_SETFITS +* AST_GETFITS + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 11-DEC-1996 (DSB): +* Original version. +* 21-FEB-1997 (DSB): +* Added source and sink functions to AST_FITSCHAN. +* 20-MAR-1997 (DSB): +* Functions for accessing named keywords removed. Others renamed. +* 28-APR-1997 (DSB): +* FindFits and GetFits merged. +* 10-SEP-2004 (TIMJ): +* Only copy the fits header to fortran string if it was found +* by astFindFits. +* 17-NOV-2004 (DSB): +* Added AST_SETFITS +* 7-OCT-2005 (DSB): +* Added AST_GETFITS +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "ast_err.h" /* AST error codes */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "object.h" /* C interface to the base Object class */ +#include "fitschan.h" /* C interface to the FitsChan class */ + +#include +#include + +/* Prototypes for private functions. */ +/* ================================= */ +static char *SourceWrap( const char *(*)( void ), int * ); +static void SinkWrap( void (*)( const char * ), const char *, int * ); +static void TabSourceWrap( void (*)( void ), + AstFitsChan *, const char *, int, int, int * ); + +/* Prototypes for external functions. */ +/* ================================== */ +/* This is the null function defined by the FORTRAN interface in fobject.c. */ +F77_SUBROUTINE(ast_null)( void ); + +/* Source and sink function interfaces. */ +/* ==================================== */ +/* These functions are concerned with allowing FORTRAN implementations + of FitsChan source and sink functions to be passed to the FitsChan + class and invoked when necessary by C code in the main class + implementation. All FORTRAN-specific aspects of this interface are + encapsulated here. */ +static void SinkWrap( void (* sink)( const char * ), const char *line, + int *status ) { +/* +* Name: +* SinkWrap + +* Purpose: +* Wrapper function to invoke a FORTRAN FitsChan sink function. + +* Type: +* Private function. + +* Synopsis: +* static void SinkWrap( void (* sink)( const char * ), const char *line, +* int *status ) + +* Description: +* This function invokes the sink function whose pointer is +* supplied in order to write an output line to an external data +* store. + +* Parameters: +* sink +* Pointer to a sink function. This should result from a cast +* applied to a pointer to a function (with two FORTRAN +* arguments: a character string of length 80 to receive a FITS +* card and an integer error status), that returns void. This +* is the form of FitsChan sink function employed by the FORTRAN +* language interface to the AST library. +* status +* Pointer to inherited status value. +*/ + +/* Local Variables: */ + DECLARE_CHARACTER(CARD,80); + DECLARE_INTEGER(STATUS); + char *d; + const char *c; + int i,lim; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Copy the supplied null terminated string to a fixed length, blank + padded string which can be passed to the Fortran routine. */ + c = line; + d = CARD; + + lim = (int) strlen( line ); + if( lim > 80 ) lim = 80; + + for( i = 0; i < lim; i++ ){ + *(d++) = (*c++); + } + + for( ; i < 80; i++ ){ + *(d++) = ' '; + } + +/* Cast the sink function pointer to a pointer to the FORTRAN + subroutine and then invoke it. Transfer the AST error status to and + from the subroutine's error status argument. */ + STATUS = astStatus; + ( ( void (*)() ) sink )( CHARACTER_ARG(CARD), INTEGER_ARG(&STATUS) + TRAIL_ARG(CARD) ); + astSetStatus( STATUS ); +} + +static char *SourceWrap( const char *(* source)( void ), int *status ) { +/* +* Name: +* SourceWrap + +* Purpose: +* Wrapper function to invoke a FORTRAN FitsChan source function. + +* Type: +* Private function. + +* Synopsis: +* static char *SourceWrap( const char *(* source)( void ), int *status ) + +* Description: +* This function invokes the source function whose pointer is +* supplied in order to read the next input line from an external +* data store. It then returns a pointer to a dynamic string +* containing a copy of the text that was read. + +* Parameters: +* source +* Pointer to a source function. This should result from a cast +* applied to a pointer to a function (with two FORTRAN +* arguments: a character string of length 80 to return a FITS +* card and an integer error status), that returns a Fortran +* integer. This is the form of FitsChan source function +* employed by the FORTRAN language interface to the AST +* library. +* status +* Pointer to inherited status. + +* Returned Value: +* A pointer to a dynamically allocated, null terminated string +* containing a copy of the text that was read. This string must be +* freed by the caller (using astFree) when no longer required. +* +* A NULL pointer will be returned if there is no more input text +* to read. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + DECLARE_CHARACTER(CARD,81); /* Fixed length Fortran string */ + DECLARE_INTEGER(STATUS); /* Fortran error status value */ + char *result; /* Result pointer to return */ + int retval; /* Value returned by source subroutine */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Cast the source function pointer to a pointer to the FORTRAN + function and then invoke it. Transfer the AST error status to and + from the subroutine's error status argument. */ + STATUS = astStatus; + retval = ( *(F77_INTEGER_TYPE (*)()) source )( CHARACTER_ARG(CARD), + INTEGER_ARG(&STATUS) + TRAIL_ARG(CARD) ); + astSetStatus( STATUS ); + +/* If a card was returned, make a dynamic copy of it. */ + if ( astOK && retval ) result = astString( CARD, 80 ); + +/* Return the result. */ + return result; +} + +static void TabSourceWrap( void (*tabsource)( void ), + AstFitsChan *this, const char *extname, + int extver, int extlevel, int *status ){ +/* +* Name: +* TabSourceWrap + +* Purpose: +* Wrapper function to invoke the F77 table source function. + +* Type: +* Private function. + +* Synopsis: +* void TabSourceWrap( void (*tabsource)( void ), +* AstFitsChan *this, const char *extname, +* int extver, int extlevel, int *status ){ + +* Class Membership: +* Channel member function. + +* Description: +* This function invokes the table source function whose pointer is +* supplied in order to read a named FITS binary table from an external +* FITS file. + +* Parameters: +* tabsource +* Pointer to the C tab source function. +* this +* Pointer to the FitsChan. It's reference count will be decremented +* by this function. +* extname +* Pointer to the string holding the name of the FITS extension +* from which a table is to be read. +* extver +* FITS "EXTVER" value for required extension. +* extlevel +* FITS "EXTLEVEL" value for required extension. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + DECLARE_CHARACTER(EXTNAME,80); + DECLARE_INTEGER(THIS_ID); + DECLARE_INTEGER(LSTAT); + DECLARE_INTEGER(EXTVER); + DECLARE_INTEGER(EXTLEVEL); + AstObject *this_id; + char *d; + const char *c; + int i; + int lim; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get an external identifier for the FitsChan. Note, this does not + increment the Object's reference count. Cannot use astClone as we + are in a "public" environment and so astClone would require an object + identifier, not a true C pointer. So the calling function should clone + the pointer before calling this function to avoid the reference count + dropping to zero when the associated identifier is annulled at the end of + this function. */ + this_id = astMakeId( this ); + THIS_ID = astP2I( this_id ); + +/* Export the extver and extlevel values */ + EXTVER = extver; + EXTLEVEL = extlevel; + +/* Copy the supplied null terminated string to a fixed length, blank + padded string which can be passed to the Fortran routine. */ + c = extname; + d = EXTNAME; + + lim = (int) strlen( extname ); + if( lim > 80 ) lim = 80; + + for( i = 0; i < lim; i++ ){ + *(d++) = (*c++); + } + + for( ; i < 80; i++ ){ + *(d++) = ' '; + } + +/* Invoke the table source function (casting it to the F77 API first) to + read the table, and store it in the FitsChan. */ + if( astOK ) { + LSTAT = 0; + ( ( void (*)() ) tabsource )( + INTEGER_ARG(&THIS_ID), CHARACTER_ARG(EXTNAME), INTEGER_ARG(&EXTVER), + INTEGER_ARG(&EXTLEVEL), INTEGER_ARG(&LSTAT) TRAIL_ARG(EXTNAME) ); + } + +/* Report an error if the source function failed. */ + if( LSTAT ) { + if( astOK ) { + astError( AST__NOTAB, "astRead(%s): The table source function failed to read " + "a binary table from extension %s in an external FITS file.", + status, astGetC( this_id, "Class" ), extname ); + } else { + astError( astStatus, "astRead(%s): The table source function failed to read " + "a binary table from extension %s in an external FITS file.", + status, astGetC( this_id, "Class" ), extname ); + } + } + + +/* Free the external identifier for the FitsChan. Note, this decrements + the Object reference count. See comments above. */ + (void) astAnnulId( this_id ); + +} + +/* FORTRAN interface functions. */ +/* ============================ */ +/* These functions implement the remainder of the FORTRAN interface. */ +F77_INTEGER_FUNCTION(ast_fitschan)( F77_INTEGER_TYPE (* SOURCE)(), + void (* SINK)(), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + const char *(* source)( void ); + int i; + void (* sink)( const char * ); + + astAt( "AST_FITSCHAN", NULL, 0 ); + astWatchSTATUS( + +/* Set the source and sink function pointers to NULL if a pointer to + the null routine AST_NULL has been supplied. */ + source = (const char *(*)( void )) SOURCE; + if ( source == (const char *(*)( void )) F77_EXTERNAL_NAME(ast_null) ) { + source = NULL; + } + sink = (void (*)( const char * )) SINK; + if ( sink == (void (*)( const char * )) F77_EXTERNAL_NAME(ast_null) ) { + sink = NULL; + } + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astFitsChanFor( source, SourceWrap, sink, SinkWrap, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isafitschan)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAFITSCHAN", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAFitsChan( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_putcards)( INTEGER(THIS), + CHARACTER(CARDS), + INTEGER(STATUS) + TRAIL(CARDS) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CARDS) + char *cards; + + astAt( "AST_PUTCARDS", NULL, 0 ); + astWatchSTATUS( + cards = astString( CARDS, CARDS_length ); + astPutCards( astI2P( *THIS ), cards ); + (void) astFree( (void *) cards ); + ) +} + +F77_SUBROUTINE(ast_putfits)( INTEGER(THIS), + CHARACTER(CARD), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(CARD) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CARD) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *card; + + astAt( "AST_PUTFITS", NULL, 0 ); + astWatchSTATUS( + card = astString( CARD, CARD_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astPutFits( astI2P( *THIS ), card, overwrite ); + (void) astFree( (void *) card ); + ) +} + +F77_SUBROUTINE(ast_delfits)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_DELFITS", NULL, 0 ); + astWatchSTATUS( + astDelFits( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_purgewcs)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_PURGEWCS", NULL, 0 ); + astWatchSTATUS( + astPurgeWCS( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_retainfits)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_RETAINFITS", NULL, 0 ); + astWatchSTATUS( + astRetainFits( astI2P( *THIS ) ); + ) +} + +F77_LOGICAL_FUNCTION(ast_findfits)( INTEGER(THIS), + CHARACTER(NAME), + CHARACTER(CARD), + LOGICAL(INC), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(CARD) ){ + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_CHARACTER(CARD) + GENPTR_LOGICAL(INC) + F77_LOGICAL_TYPE(RESULT); + int i, len; + char *name; + char card[ 81 ]; + int inc; + + astAt( "AST_FINDFITS", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + inc = F77_ISTRUE( *INC ); + RESULT = astFindFits( astI2P( *THIS ), name, card, inc ) ? + F77_TRUE : F77_FALSE; + i = 0; + if ( astOK && F77_ISTRUE(RESULT) ) { + len = (int) strlen( card ); + for( i = 0; i < CARD_length && i < len; i++ ) CARD[i] = card[i]; + } + for( ; i < CARD_length; i++ ) CARD[i] = ' '; + (void) astFree( (void *) name ); + ) + return RESULT; +} + + +F77_SUBROUTINE(ast_setfitsf)( INTEGER(THIS), + CHARACTER(NAME), + DOUBLE(VALUE), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_DOUBLE(VALUE) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *name, *comment; + + astAt( "AST_SETFITSF", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsF( astI2P( *THIS ), name, *VALUE, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) comment ); + ) +} + +F77_SUBROUTINE(ast_setfitsu)( INTEGER(THIS), + CHARACTER(NAME), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *name, *comment; + + astAt( "AST_SETFITSU", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsU( astI2P( *THIS ), name, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) comment ); + ) +} + +F77_SUBROUTINE(ast_setfitscm)( INTEGER(THIS), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *comment; + + astAt( "AST_SETFITSCM", NULL, 0 ); + astWatchSTATUS( + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsCM( astI2P( *THIS ), comment, overwrite ); + (void) astFree( (void *) comment ); + ) +} + + +F77_SUBROUTINE(ast_setfitsi)( INTEGER(THIS), + CHARACTER(NAME), + INTEGER(VALUE), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_INTEGER(VALUE) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *name, *comment; + + astAt( "AST_SETFITSI", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsI( astI2P( *THIS ), name, *VALUE, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) comment ); + ) +} + + +F77_SUBROUTINE(ast_setfitscf)( INTEGER(THIS), + CHARACTER(NAME), + DOUBLE_ARRAY(VALUE), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_DOUBLE_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *name, *comment; + + astAt( "AST_SETFITSCF", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsCF( astI2P( *THIS ), name, VALUE, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) comment ); + ) +} + + +F77_SUBROUTINE(ast_setfitsci)( INTEGER(THIS), + CHARACTER(NAME), + INTEGER_ARRAY(VALUE), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_INTEGER_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *name, *comment; + + astAt( "AST_SETFITSCI", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsCI( astI2P( *THIS ), name, VALUE, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) comment ); + ) +} + + +F77_SUBROUTINE(ast_setfitsl)( INTEGER(THIS), + CHARACTER(NAME), + LOGICAL(VALUE), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_LOGICAL(VALUE) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite, value; + char *name, *comment; + + astAt( "AST_SETFITSL", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + value = F77_ISTRUE( *VALUE ); + astSetFitsL( astI2P( *THIS ), name, value, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) comment ); + ) +} + + +F77_SUBROUTINE(ast_setfitss)( INTEGER(THIS), + CHARACTER(NAME), + CHARACTER(VALUE), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(VALUE) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_CHARACTER(VALUE) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *name, *comment, *value; + + astAt( "AST_SETFITSS", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + value = astString( VALUE, VALUE_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsS( astI2P( *THIS ), name, value, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) value ); + (void) astFree( (void *) comment ); + ) +} + +F77_SUBROUTINE(ast_setfitscn)( INTEGER(THIS), + CHARACTER(NAME), + CHARACTER(VALUE), + CHARACTER(COMMENT), + LOGICAL(OVERWRITE), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(VALUE) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_CHARACTER(VALUE) + GENPTR_CHARACTER(COMMENT) + GENPTR_LOGICAL(OVERWRITE) + int overwrite; + char *name, *comment, *value; + + astAt( "AST_SETFITSS", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + value = astString( VALUE, VALUE_length ); + comment = astString( COMMENT, COMMENT_length ); + overwrite = F77_ISTRUE( *OVERWRITE ); + astSetFitsCN( astI2P( *THIS ), name, value, comment, overwrite ); + (void) astFree( (void *) name ); + (void) astFree( (void *) value ); + (void) astFree( (void *) comment ); + ) +} + +#define MAKE_AST_GETFITS(f,F,Ftype,X,Xtype) \ +F77_LOGICAL_FUNCTION(ast_getfits##f)( INTEGER(THIS), \ + CHARACTER(NAME), \ + Ftype(VALUE), \ + INTEGER(STATUS) \ + TRAIL(NAME) ){ \ + GENPTR_INTEGER(THIS) \ + GENPTR_CHARACTER(NAME) \ + GENPTR_##Ftype(VALUE) \ + GENPTR_INTEGER(STATUS) \ + F77_LOGICAL_TYPE(RESULT); \ +\ + char *name; \ + Xtype *value; \ +\ + value = (Xtype *) VALUE; \ +\ + astAt( "AST_GETFITS"#F, NULL, 0 ); \ + astWatchSTATUS( \ + name = astString( NAME, NAME_length ); \ + if( name && !strcmp( name, "." ) ) name = astFree( name ); \ + RESULT = astGetFits##X( astI2P( *THIS ), name, value ) ? \ + F77_TRUE : F77_FALSE; \ + (void) astFree( (void *) name ); \ + ) \ + return RESULT; \ +} + +MAKE_AST_GETFITS(f,F,DOUBLE,F,double) +MAKE_AST_GETFITS(i,I,INTEGER,I,int) +MAKE_AST_GETFITS(l,L,LOGICAL,L,int) +#undef MAKE_AST_GETFITS + + +F77_LOGICAL_FUNCTION(ast_testfits)( INTEGER(THIS), + CHARACTER(NAME), + LOGICAL(THERE), + INTEGER(STATUS) + TRAIL(NAME) ){ + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_LOGICAL(THERE) + GENPTR_INTEGER(STATUS) + F77_LOGICAL_TYPE(RESULT); + + char *name; + int there; + + astAt( "AST_TESTFITS", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + RESULT = astTestFits( astI2P( *THIS ), name, &there ) ? + F77_TRUE : F77_FALSE; + (void) astFree( (void *) name ); + ) + *THERE = there ? F77_TRUE : F77_FALSE; + return RESULT; +} + + +#define MAKE_AST_GETFITS(f,F,Ftype,X,Xtype) \ +F77_LOGICAL_FUNCTION(ast_getfits##f)( INTEGER(THIS), \ + CHARACTER(NAME), \ + Ftype##_ARRAY(VALUE), \ + INTEGER(STATUS) \ + TRAIL(NAME) ){ \ + GENPTR_INTEGER(THIS) \ + GENPTR_CHARACTER(NAME) \ + GENPTR_##Ftype##_ARRAY(VALUE) \ + GENPTR_INTEGER(STATUS) \ + F77_LOGICAL_TYPE(RESULT); \ +\ + char *name; \ + Xtype value[2]; \ +\ + astAt( "AST_GETFITS"#F, NULL, 0 ); \ + astWatchSTATUS( \ + name = astString( NAME, NAME_length ); \ + if( name && !strcmp( name, "." ) ) name = astFree( name ); \ + RESULT = astGetFits##X( astI2P( *THIS ), name, value ) ? \ + F77_TRUE : F77_FALSE; \ + VALUE[ 0 ] = (F77_DOUBLE_TYPE) value[ 0 ]; \ + VALUE[ 1 ] = (F77_DOUBLE_TYPE) value[ 1 ]; \ + (void) astFree( (void *) name ); \ + ) \ + return RESULT; \ +} + + +MAKE_AST_GETFITS(cf,CF,DOUBLE,CF,double) +MAKE_AST_GETFITS(ci,CI,INTEGER,CI,int) + +#undef MAKE_AST_GETFITS + +#define MAKE_AST_GETFITS(f,F,X) \ +F77_LOGICAL_FUNCTION(ast_getfits##f)( INTEGER(THIS), \ + CHARACTER(NAME), \ + CHARACTER(VALUE), \ + INTEGER(STATUS) \ + TRAIL(NAME) \ + TRAIL(VALUE) ){ \ + GENPTR_INTEGER(THIS) \ + GENPTR_CHARACTER(NAME) \ + GENPTR_CHARACTER(VALUE) \ + GENPTR_INTEGER(STATUS) \ + F77_LOGICAL_TYPE(RESULT); \ +\ + char *name; \ + int i, len; \ + char *value; \ +\ + astAt( "AST_GETFITS"#F, NULL, 0 ); \ + astWatchSTATUS( \ + name = astString( NAME, NAME_length ); \ + if( name && !strcmp( name, "." ) ) name = astFree( name ); \ + RESULT = astGetFits##X( astI2P( *THIS ), name, &value ) ? \ + F77_TRUE : F77_FALSE; \ + if ( astOK && F77_ISTRUE(RESULT) ) { \ + len = (int) strlen( value ); \ + for( i = 0; i < VALUE_length && i < len; i++ ) VALUE[i] = value[i]; \ + } else { \ + i = 0; \ + } \ + for( ; i < VALUE_length; i++ ) VALUE[i] = ' '; \ + (void) astFree( (void *) name ); \ + ) \ + return RESULT; \ +} + +MAKE_AST_GETFITS(s,S,S) +MAKE_AST_GETFITS(cn,CN,CN) + +#undef MAKE_AST_GETFITS + +F77_SUBROUTINE(ast_readfits)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_READFITS", NULL, 0 ); + astWatchSTATUS( + astReadFits( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_writefits)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_WRITEFITS", NULL, 0 ); + astWatchSTATUS( + astWriteFits( astI2P( *THIS ) ); + ) +} + + +F77_SUBROUTINE(ast_emptyfits)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_EMPTYFITS", NULL, 0 ); + astWatchSTATUS( + astEmptyFits( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_showfits)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_SHOWFITS", NULL, 0 ); + astWatchSTATUS( + astShowFits( astI2P( *THIS ) ); + ) +} + + +F77_INTEGER_FUNCTION(ast_gettables)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETTABLES", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetTables( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_removetables)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + char *key; + + astAt( "AST_REMOVETABLES", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astRemoveTables( astI2P( *THIS ), key ); + (void) astFree( (void *) key ); + ) +} + +F77_SUBROUTINE(ast_puttable)( INTEGER(THIS), + INTEGER(TABLE), + CHARACTER(EXTNAM), + INTEGER(STATUS) + TRAIL(EXTNAM) ){ + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(TABLES) + GENPTR_CHARACTER(EXTNAM) + char *extnam; + + astAt( "AST_PUTTABLE", NULL, 0 ); + astWatchSTATUS( + extnam = astString( EXTNAM, EXTNAM_length ); + astPutTable( astI2P( *THIS ), astI2P( *TABLE ), extnam ); + extnam = astFree( extnam ); + ) +} + +F77_SUBROUTINE(ast_puttables)( INTEGER(THIS), + INTEGER(TABLES), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(TABLES) + + astAt( "AST_PUTTABLES", NULL, 0 ); + astWatchSTATUS( + astPutTables( astI2P( *THIS ), astI2P( *TABLES ) ); + ) +} + +F77_SUBROUTINE(ast_tablesource)( INTEGER(THIS), + void (* SOURCE)(), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + void (* source)( void ); + + astAt( "AST_TABLESOURCE", NULL, 0 ); + astWatchSTATUS( + source = (void (*)( void )) SOURCE; + if ( source == (void (*)( void )) F77_EXTERNAL_NAME(ast_null) ) { + source = NULL; + } + astSetTableSource( astI2P( *THIS ), source, TabSourceWrap ); + ) +} + + diff --git a/ffitstable.c b/ffitstable.c new file mode 100644 index 0000000..9f2db37 --- /dev/null +++ b/ffitstable.c @@ -0,0 +1,234 @@ +/* +*+ +* Name: +* ffitstable.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST FitsTable class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the FitsTable class. + +* Routines Defined: +* AST_ISAFITSTABLE +* AST_FITSTABLE + +* Copyright: +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S.Berry (Starlink) + +* History: +* 25-NOV-2010 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "ast_err.h" /* AST error codes */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "fitstable.h" /* C interface to the FitsTable class */ + +F77_LOGICAL_FUNCTION(ast_isafitstable)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISAFITSTABLE", NULL, 0 ); + RESULT = astIsAFitsTable( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_fitstable)( INTEGER(HEADER), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(HEADER) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_FITSTABLE", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astFitsTable( astI2P( *HEADER ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_gettableheader)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETTABLEHEADER", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetTableHeader( astI2P( *THIS ) ) ); + ) + return RESULT; +} + + +F77_SUBROUTINE(ast_puttableheader)( INTEGER(THIS), + INTEGER(HEADER), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(HEADER) + + astAt( "AST_PUTTABLEHEADER", NULL, 0 ); + astWatchSTATUS( + astPutTableHeader( astI2P( *THIS ), astI2P( *HEADER ) ); + ) +} + +F77_INTEGER_FUNCTION(ast_columnnull)( INTEGER(THIS), + CHARACTER(COLUMN), + LOGICAL(SET), + INTEGER(NEWVAL), + LOGICAL(WASSET), + LOGICAL(HASNULL), + INTEGER(STATUS) + TRAIL(COLUMN) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(COLUMN) + GENPTR_LOGICAL(SET) + GENPTR_INTEGER(NEWVAL) + GENPTR_LOGICAL(WASSET) + GENPTR_LOGICAL(HASNULL) + F77_INTEGER_TYPE(RESULT); + int wasset, hasnull; + char *column; + + astAt( "AST_COLUMNNULL", NULL, 0 ); + astWatchSTATUS( + column = astString( COLUMN, COLUMN_length ); + RESULT = astColumnNull( astI2P( *THIS ), column, + F77_ISTRUE( *SET ) ? 1 : 0, *NEWVAL, + &wasset, &hasnull ); + *WASSET = wasset ? F77_TRUE : F77_FALSE; + *HASNULL = hasnull ? F77_TRUE : F77_FALSE; + astFree( column ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_columnsize)( INTEGER(THIS), + CHARACTER(COLUMN), + INTEGER(STATUS) + TRAIL(COLUMN) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(COLUMN) + F77_INTEGER_TYPE(RESULT); + char *column; + size_t result; + + astAt( "AST_COLUMNSIZE", NULL, 0 ); + astWatchSTATUS( + column = astString( COLUMN, COLUMN_length ); + result = astColumnSize( astI2P( *THIS ), column ); + astFree( column ); + + RESULT = result; + if( (size_t) RESULT != result && astOK ) { + astError( AST__BIGTAB, "AST_COLUMNSIZE(FitsTable): The number of bytes in the " + "column is too large to fit in a Fortran INTEGER.", status ); + } + + ) + return RESULT; +} + +F77_SUBROUTINE(ast_getcolumndata)( INTEGER(THIS), + CHARACTER(COLUMN), + REAL(RNULL), + DOUBLE(DNULL), + INTEGER(MXSIZE), + BYTE_ARRAY(COLDATA), + INTEGER(NELEM), + INTEGER(STATUS) + TRAIL(COLUMN) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(COLUMN) + GENPTR_REAL(RNULL) + GENPTR_DOUBLE(DNULL) + GENPTR_INTEGER(MXSIZE) + GENPTR_BYTE_ARRAY(COLDATA) + GENPTR_INTEGER(NELEM) + char *column; + + astAt( "AST_GETCOLUMNDATA", NULL, 0 ); + astWatchSTATUS( + column = astString( COLUMN, COLUMN_length ); + astGetColumnData( astI2P( *THIS ), column, *RNULL, *DNULL, *MXSIZE, + (void *) COLDATA, NELEM ); + astFree( column ); + ) +} + +F77_SUBROUTINE(ast_putcolumndata)( INTEGER(THIS), + CHARACTER(COLUMN), + INTEGER(CLEN), + INTEGER(SIZE), + BYTE_ARRAY(COLDATA), + INTEGER(STATUS) + TRAIL(COLUMN) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(COLUMN) + GENPTR_INTEGER(CLEN) + GENPTR_INTEGER(SIZE) + GENPTR_BYTE_ARRAY(COLDATA) + char *column; + + astAt( "AST_PUTCOLUMNDATA", NULL, 0 ); + astWatchSTATUS( + column = astString( COLUMN, COLUMN_length ); + astPutColumnData( astI2P( *THIS ), column, *CLEN, *SIZE, (void *) COLDATA ); + astFree( column ); + ) +} + diff --git a/ffluxframe.c b/ffluxframe.c new file mode 100644 index 0000000..ab55e80 --- /dev/null +++ b/ffluxframe.c @@ -0,0 +1,104 @@ +/* +*+ +* Name: +* ffluxframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST FluxFrame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the FluxFrame class. + +* Routines Defined: +* AST_ISAFLUXFRAME +* AST_FLUXFRAME + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 1-DEC-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "fluxframe.h" /* C interface to the FluxFrame class */ + +F77_LOGICAL_FUNCTION(ast_isafluxframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAFLUXFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAFluxFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_fluxframe)( DOUBLE(SPECVAL), + INTEGER(SPECFRM), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_DOUBLE(SPECVAL) + GENPTR_INTEGER(SPECFRM) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_FLUXFRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astFluxFrame( *SPECVAL, astI2P( *SPECFRM ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + diff --git a/fframe.c b/fframe.c new file mode 100644 index 0000000..cae1433 --- /dev/null +++ b/fframe.c @@ -0,0 +1,494 @@ +/* +*+ +* Name: +* fframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Frame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Frame class. + +* Routines Defined: +* AST_ANGLE +* AST_AXANGLE +* AST_AXDISTANCE +* AST_AXOFFSET +* AST_CONVERT +* AST_DISTANCE +* AST_FORMAT +* AST_FRAME +* AST_GETACTIVEUNIT +* AST_INTERSECT +* AST_ISAFRAME +* AST_NORM +* AST_OFFSET +* AST_OFFSET2 +* AST_PERMAXES +* AST_PICKAXES +* AST_RESOLVE +* AST_SETACTIVEUNIT +* AST_UNFORMAT + +* Copyright: +* Copyright (C) 1997-2009 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 23-JUL-1996 (RFWS): +* Original version. +* 16-SEP-1996 (RFWS): +* Added AST_DISTANCE and AST_OFFSET. +* 25-FEB-1998 (RFWS): +* Added AST_UNFORMAT. +* 21-JUN-2001 (DSB): +* Added AST_ANGLE and AST_OFFSET2. +* 29-AUG-2001 (DSB): +* Added AST_AXDISTANCE and AST_AXOFFSET. +* 9-SEP-2001 (DSB): +* Added AST_RESOLVE and AST_BEAR. +* 21-SEP-2001 (DSB): +* Replaced AST_BEAR by AST_AXANGLE. +* 17-DEC-2002 (DSB): +* Added AST_GETACTIVEUNIT and AST_SETACTIVEUNIT. +* 14-JAN-2009 (DSB): +* Added AST_INTERSECT. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "mapping.h" /* C interface to the Mapping class */ +#include "frame.h" /* C interface to the Frame class */ + + +F77_INTEGER_FUNCTION(ast_convert)( INTEGER(FROM), + INTEGER(TO), + CHARACTER(NAMELIST), + INTEGER(STATUS) + TRAIL(NAMELIST) ) { + GENPTR_INTEGER(FROM) + GENPTR_INTEGER(TO) + GENPTR_INTEGER(NAMELIST) + F77_INTEGER_TYPE(RESULT); + char *namelist; + + astAt( "AST_CONVERT", NULL, 0 ); + astWatchSTATUS( + namelist = astString( NAMELIST, NAMELIST_length ); + RESULT = astP2I( astConvert( astI2P( *FROM ), astI2P( *TO ), + namelist ) ); + namelist = astFree( namelist ); + ) + return RESULT; +} + +F77_DOUBLE_FUNCTION(ast_angle)( INTEGER(THIS), + DOUBLE_ARRAY(A), + DOUBLE_ARRAY(B), + DOUBLE_ARRAY(C), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(A) + GENPTR_DOUBLE_ARRAY(B) + GENPTR_DOUBLE_ARRAY(C) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_ANGLE", NULL, 0 ); + astWatchSTATUS( + RESULT = astAngle( astI2P( *THIS ), A, B, C ); + ) + return RESULT; +} + +F77_DOUBLE_FUNCTION(ast_axangle)( INTEGER(THIS), + DOUBLE_ARRAY(A), + DOUBLE_ARRAY(B), + INTEGER(AXIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(A) + GENPTR_DOUBLE_ARRAY(B) + GENPTR_INTEGER(AXIS) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_AXANGLE", NULL, 0 ); + astWatchSTATUS( + RESULT = astAxAngle( astI2P( *THIS ), A, B, *AXIS ); + ) + return RESULT; +} + +F77_DOUBLE_FUNCTION(ast_distance)( INTEGER(THIS), + DOUBLE_ARRAY(POINT1), + DOUBLE_ARRAY(POINT2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(POINT1) + GENPTR_DOUBLE_ARRAY(POINT2) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_DISTANCE", NULL, 0 ); + astWatchSTATUS( + RESULT = astDistance( astI2P( *THIS ), POINT1, POINT2 ); + ) + return RESULT; +} + +F77_DOUBLE_FUNCTION(ast_axdistance)( INTEGER(THIS), + INTEGER(AXIS), + DOUBLE(V1), + DOUBLE(V2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(AXIS) + GENPTR_DOUBLE(V1) + GENPTR_DOUBLE(V2) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_AXDISTANCE", NULL, 0 ); + astWatchSTATUS( + RESULT = astAxDistance( astI2P( *THIS ), *AXIS, *V1, *V2 ); + ) + return RESULT; +} + +F77_DOUBLE_FUNCTION(ast_axoffset)( INTEGER(THIS), + INTEGER(AXIS), + DOUBLE(V1), + DOUBLE(DIST), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(AXIS) + GENPTR_DOUBLE(V1) + GENPTR_DOUBLE(DIST) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_AXOFFSET", NULL, 0 ); + astWatchSTATUS( + RESULT = astAxOffset( astI2P( *THIS ), *AXIS, *V1, *DIST ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_findframe)( INTEGER(TARGET), + INTEGER(TEMPLATE), + CHARACTER(NAMELIST), + INTEGER(STATUS) + TRAIL(NAMELIST) ) { + GENPTR_INTEGER(TARGET) + GENPTR_INTEGER(TEMPLATE) + GENPTR_INTEGER(NAMELIST) + F77_INTEGER_TYPE(RESULT); + char *namelist; + + astAt( "AST_FINDFRAME", NULL, 0 ); + astWatchSTATUS( + namelist = astString( NAMELIST, NAMELIST_length ); + RESULT = astP2I( astFindFrame( astI2P( *TARGET ), astI2P( *TEMPLATE ), + namelist ) ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_matchaxes)( INTEGER(FRM1), + INTEGER(FRM2), + INTEGER_ARRAY(AXES), + INTEGER(STATUS) ) { + GENPTR_INTEGER(FRM1) + GENPTR_INTEGER(FRM2) + GENPTR_INTEGER_ARRAY(AXES) + + astAt( "AST_MATCHAXES", NULL, 0 ); + astWatchSTATUS( + astMatchAxes( astI2P( *FRM1 ), astI2P( *FRM2 ), AXES ); + ) +} + +/* NO_CHAR_FUNCTION indicates that the f77.h method of returning a + character result doesn't work, so add an extra argument instead and + wrap this function up in a normal FORTRAN 77 function (in the file + frame.f). */ +#if NO_CHAR_FUNCTION +F77_SUBROUTINE(ast_format_a)( CHARACTER(RESULT), +#else +F77_SUBROUTINE(ast_format)( CHARACTER_RETURN_VALUE(RESULT), +#endif + INTEGER(THIS), + INTEGER(AXIS), + DOUBLE(VALUE), + INTEGER(STATUS) +#if NO_CHAR_FUNCTION + TRAIL(RESULT) +#endif + ) { + GENPTR_CHARACTER(RESULT) + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(AXIS) + GENPTR_DOUBLE(VALUE) + const char *result; + int i; + + astAt( "AST_FORMAT", NULL, 0 ); + astWatchSTATUS( + result = astFormat( astI2P( *THIS ), *AXIS, *VALUE ); + i = 0; + if ( astOK ) { /* Copy result */ + for ( ; result[ i ] && i < RESULT_length; i++ ) { + RESULT[ i ] = result[ i ]; + } + } + while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */ + ) +} + +F77_INTEGER_FUNCTION(ast_frame)( INTEGER(NAXES), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NAXES) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_FRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exclude any trailing spaces. */ + astChrTrunc( options ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astFrame( *NAXES, "%s", options ) ); + (void) astFree( options ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isaframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_getactiveunit)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_GETACTIVEUNIT", NULL, 0 ); + astWatchSTATUS( + RESULT = astGetActiveUnit( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_setactiveunit)( INTEGER(THIS), + LOGICAL(VALUE), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_LOGICAL(VALUE) + + astAt( "AST_SETACTIVEUNIT", NULL, 0 ); + astWatchSTATUS( + astSetActiveUnit( astI2P( *THIS ), F77_ISTRUE( *VALUE ) ? 1 : 0 ); + ) +} + +F77_SUBROUTINE(ast_norm)( INTEGER(THIS), + DOUBLE_ARRAY(VALUE), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(VALUE) + + astAt( "AST_NORM", NULL, 0 ); + astWatchSTATUS( + astNorm( astI2P( *THIS ), VALUE ); + ) +} + +F77_SUBROUTINE(ast_offset)( INTEGER(THIS), + DOUBLE_ARRAY(POINT1), + DOUBLE_ARRAY(POINT2), + DOUBLE(OFFSET), + DOUBLE_ARRAY(POINT3), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(POINT1) + GENPTR_DOUBLE_ARRAY(POINT2) + GENPTR_DOUBLE(OFFSET) + GENPTR_DOUBLE_ARRAY(POINT3) + + astAt( "AST_OFFSET", NULL, 0 ); + astWatchSTATUS( + astOffset( astI2P( *THIS ), POINT1, POINT2, *OFFSET, POINT3 ); + ) +} + +F77_DOUBLE_FUNCTION(ast_offset2)( INTEGER(THIS), + DOUBLE_ARRAY(POINT1), + DOUBLE(ANGLE), + DOUBLE(OFFSET), + DOUBLE_ARRAY(POINT2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(POINT1) + GENPTR_DOUBLE(ANGLE) + GENPTR_DOUBLE(OFFSET) + GENPTR_DOUBLE_ARRAY(POINT2) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_OFFSET2", NULL, 0 ); + astWatchSTATUS( + RESULT = astOffset2( astI2P( *THIS ), POINT1, *ANGLE, *OFFSET, POINT2 ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_resolve)( INTEGER(THIS), + DOUBLE_ARRAY(POINT1), + DOUBLE_ARRAY(POINT2), + DOUBLE_ARRAY(POINT3), + DOUBLE_ARRAY(POINT4), + DOUBLE(D1), + DOUBLE(D2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(POINT1) + GENPTR_DOUBLE_ARRAY(POINT2) + GENPTR_DOUBLE_ARRAY(POINT3) + GENPTR_DOUBLE_ARRAY(POINT4) + GENPTR_DOUBLE(D1) + GENPTR_DOUBLE(D2) + + astAt( "AST_RESOLVE", NULL, 0 ); + astWatchSTATUS( + astResolve( astI2P( *THIS ), POINT1, POINT2, POINT3, POINT4, D1, D2 ); + ) +} + +F77_SUBROUTINE(ast_intersect)( INTEGER(THIS), + DOUBLE_ARRAY(A1), + DOUBLE_ARRAY(A2), + DOUBLE_ARRAY(B1), + DOUBLE_ARRAY(B2), + DOUBLE_ARRAY(CROSS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(A1) + GENPTR_DOUBLE_ARRAY(A2) + GENPTR_DOUBLE_ARRAY(B1) + GENPTR_DOUBLE_ARRAY(B2) + GENPTR_DOUBLE_ARRAY(CROSS) + + astAt( "AST_INTERSECT", NULL, 0 ); + astWatchSTATUS( + astIntersect( astI2P( *THIS ), A1, A2, B1, B2, CROSS ); + ) +} + +F77_SUBROUTINE(ast_permaxes)( INTEGER(THIS), + INTEGER_ARRAY(PERM), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER_ARRAY(PERM) + + astAt( "AST_PERMAXES", NULL, 0 ); + astWatchSTATUS( + astPermAxes( astI2P( *THIS ), PERM ); + ) +} + +F77_INTEGER_FUNCTION(ast_pickaxes)( INTEGER(THIS), + INTEGER(NAXES), + INTEGER_ARRAY(AXES), + INTEGER(MAP), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NAXES) + GENPTR_INTEGER_ARRAY(AXES) + GENPTR_INTEGER(MAP) + F77_INTEGER_TYPE(RESULT); + AstMapping *map; + + astAt( "AST_PICKAXES", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astPickAxes( astI2P( *THIS ), *NAXES, AXES, &map ) ); + *MAP = astP2I( map ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_unformat)( INTEGER(THIS), + INTEGER(AXIS), + CHARACTER(STRING), + DOUBLE(VALUE), + INTEGER(STATUS) + TRAIL(STRING) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(AXIS) + GENPTR_CHARACTER(STRING) + GENPTR_DOUBLE(VALUE) + GENPTR_INTEGER(STATUS) + F77_INTEGER_TYPE(RESULT); + char *string; + double value; + + astAt( "AST_UNFORMAT", NULL, 0 ); + astWatchSTATUS( + string = astString( STRING, STRING_length ); + + RESULT = astUnformat( astI2P( *THIS ), *AXIS, string, &value ); + *VALUE = value; + (void) astFree( string ); + ) + return RESULT; +} diff --git a/fframeset.c b/fframeset.c new file mode 100644 index 0000000..1698847 --- /dev/null +++ b/fframeset.c @@ -0,0 +1,214 @@ +/* +*+ +* Name: +* fframeset.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST FrameSet class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the FrameSet class. + +* Routines Defined: +* AST_ADDFRAME +* AST_ADDVARIANT +* AST_MIRRORVARIANTS +* AST_FRAMESET +* AST_GETFRAME +* AST_GETMAPPING +* AST_ISAFRAMESET +* AST_REMAPFRAME +* AST_REMOVEFRAME + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 1-AUG-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "mapping.h" /* C interface to the Mapping class */ +#include "frame.h" /* C interface to the Frame class */ +#include "frameset.h" /* C interface to the FrameSet class */ + +F77_SUBROUTINE(ast_addframe)( INTEGER(THIS), + INTEGER(IFRAME), + INTEGER(MAP), + INTEGER(FRAME), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(IFRAME) + GENPTR_INTEGER(MAP) + GENPTR_INTEGER(FRAME) + + astAt( "AST_ADDFRAME", NULL, 0 ); + astWatchSTATUS( + astAddFrame( astI2P( *THIS ), *IFRAME, astI2P( *MAP ), + astI2P( *FRAME ) ); + ) +} + +F77_INTEGER_FUNCTION(ast_frameset)( INTEGER(FRAME), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_FRAMESET", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astFrameSet( astI2P( *FRAME ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_getframe)( INTEGER(THIS), + INTEGER(IFRAME), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(IFRAME) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetFrame( astI2P( *THIS ), *IFRAME ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_getmapping)( INTEGER(THIS), + INTEGER(IFRAME1), + INTEGER(IFRAME2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(IFRAME1) + GENPTR_INTEGER(IFRAME2) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETMAPPING", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetMapping( astI2P( *THIS ), *IFRAME1, *IFRAME2 ) ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isaframeset)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAFRAMESET", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAFrameSet( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_remapframe)( INTEGER(THIS), + INTEGER(IFRAME), + INTEGER(MAP), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(IFRAME) + GENPTR_INTEGER(MAP) + + astAt( "AST_REMAPFRAME", NULL, 0 ); + astWatchSTATUS( + astRemapFrame( astI2P( *THIS ), *IFRAME, astI2P( *MAP ) ); + ) +} + +F77_SUBROUTINE(ast_removeframe)( INTEGER(THIS), + INTEGER(IFRAME), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(IFRAME) + + astAt( "AST_REMOVEFRAME", NULL, 0 ); + astWatchSTATUS( + astRemoveFrame( astI2P( *THIS ), *IFRAME ); + ) +} + +F77_SUBROUTINE(ast_addvariant)( INTEGER(THIS), + INTEGER(MAP), + CHARACTER(NAME), + INTEGER(STATUS) + TRAIL(NAME) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(MAP) + GENPTR_CHARACTER(NAME) + char *name; + + astAt( "AST_ADDVARIANT", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + astAddVariant( astI2P( *THIS ), astI2P( *MAP ), name ); + name = astFree( name ); + ) +} + +F77_SUBROUTINE(ast_mirrorvariants)( INTEGER(THIS), + INTEGER(IFRAME), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(IFRAME) + + astAt( "AST_MIRRORVARIANTS", NULL, 0 ); + astWatchSTATUS( + astMirrorVariants( astI2P( *THIS ), *IFRAME ); + ) +} + diff --git a/fgrismmap.c b/fgrismmap.c new file mode 100644 index 0000000..f26159c --- /dev/null +++ b/fgrismmap.c @@ -0,0 +1,99 @@ +/* +*+ +* Name: +* fgrismmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST GrismMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the GrismMap class. + +* Routines Defined: +* AST_ISAGRISMMAP +* AST_GRISMMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 10-JUL-2003 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "grismmap.h" /* C interface to the GrismMap class */ + +F77_LOGICAL_FUNCTION(ast_isagrismmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAGRISMMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAGrismMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_grismmap)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_GRISMMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astGrismMap( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/finterval.c b/finterval.c new file mode 100644 index 0000000..7023868 --- /dev/null +++ b/finterval.c @@ -0,0 +1,109 @@ +/* +*+ +* Name: +* finterval.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Interval class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Interval class. + +* Routines Defined: +* AST_ISAINTERVAL +* AST_INTERVAL + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 31-AUG-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "interval.h" /* C interface to the Interval class */ + + +F77_LOGICAL_FUNCTION(ast_isainterval)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAINTERVAL", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAInterval( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_interval)( INTEGER(FRAME), + DOUBLE_ARRAY(LBND), + DOUBLE_ARRAY(UBND), + INTEGER(UNC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_DOUBLE_ARRAY(LBND) + GENPTR_DOUBLE_ARRAY(UBND) + GENPTR_INTEGER(UNC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_INTERVAL", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astInterval( astI2P( *FRAME ), LBND, UBND, + astI2P( *UNC ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fintramap.c b/fintramap.c new file mode 100644 index 0000000..cdf3e4e --- /dev/null +++ b/fintramap.c @@ -0,0 +1,332 @@ +/* +*+ +* Name: +* fintramap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST IntraMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the IntraMap class. + +* Routines Defined: +* AST_INTRAMAP +* AST_INTRAREG +* AST_ISAINTRAMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 18-MAR-1998 (RFWS): +* Original version. +* 15-SEP-1999 (RFWS): +* Added a THIS pointer to the external transformation function +* used by an IntraMap. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "intramap.h" /* C interface to the IntraMap class */ + +#include +#include + +/* Prototypes for private functions. */ +/* ================================= */ +static void TranWrap( void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), AstMapping *, int, int, const double *[], int, int, double *[], int * ); + +/* Transformation function interface. */ +/* ================================== */ +/* This is concerned with allowing FORTRAN implementations of + transformation functions to be passed to the IntraMap class and + invoked when necessary by C code in the main class + implementation. All FORTRAN-specific aspects of this interface are + encapsulated here. */ +static void TranWrap( void (* tran)( AstMapping *, int, int, const double *[], + int, int, double *[] ), + AstMapping *this, int npoint, int ncoord_in, + const double *ptr_in[], int forward, int ncoord_out, + double *ptr_out[], int *status ) { +/* +* Name: +* TranWrap + +* Purpose: +* Wrapper function to invoke a FORTRAN transformation function. + +* Type: +* Private function. + +* Synopsis: +* void TranWrap( void (* tran)( AstMapping *, int, int, const double *[], +* int, int, double *[] ), +* AstMapping *this, int npoint, int ncoord_in, +* const double *ptr_in[], int forward, int ncoord_out, +* double *ptr_out[], int *status ) + +* Description: +* This function invokes a FORTRAN implementation of a +* transformation function (which resembles AST_TRANN from the +* Mapping class FORTRAN interface) in order to make it callable by +* C code which would prefer to call a C function that resembles +* astTranP (from the Mapping class C interface). + +* Parameters: +* tran +* Pointer to the FORTRAN transformation function to be invoked. +* This should result from a cast applied to a pointer to a +* function that resembles AST_TRANN (but with the first +* argument omitted). +* this +* An external Mapping ID associated with the internal (true C) pointer +* for the IntraMap whose transformation is being evaluated. +* npoint +* The number of points to be transformed. +* ncoord_in +* The number of coordinates being supplied for each input point +* (i.e. the number of dimensions of the space in which the +* input points reside). +* ptr_in +* An array of pointers to double, with "ncoord_in" +* elements. Element "ptr_in[coord]" should point at the first +* element of an array of double (with "npoint" elements) which +* contain the values of coordinate number "coord" for each +* input (untransformed) point. The value of coordinate number +* "coord" for input point number "point" is therefore given by +* "ptr_in[coord][point]". +* forward +* A non-zero value indicates that the forward coordinate +* transformation is to be applied, while a zero value indicates +* that the inverse transformation should be used. +* ncoord_out +* The number of coordinates being generated for each output +* point (i.e. the number of dimensions of the space in which +* the output points reside). This need not be the same as +* "ncoord_in". +* ptr_out +* An array of pointers to double, with "ncoord_out" +* elements. Element "ptr_out[coord]" should point at the first +* element of an array of double (with "npoint" elements) into +* which the values of coordinate number "coord" for each output +* (transformed) point will be written. The value of coordinate +* number "coord" for output point number "point" will therefore +* be found in "ptr_out[coord][point]". +* status +* Pointer to the inherited status value. +*/ + +/* Local Variables; */ + DECLARE_INTEGER(INDIM); /* First dimension size of input array */ + DECLARE_INTEGER(NCOORD_IN); /* Number of input coordinates */ + DECLARE_INTEGER(NCOORD_OUT); /* Number of output coordinates */ + DECLARE_INTEGER(NPOINT); /* Number of points */ + DECLARE_INTEGER(OUTDIM); /* First dimension size of output array */ + DECLARE_INTEGER(STATUS); /* FORTRAN error status variable */ + DECLARE_INTEGER(THIS); /* External ID for the IntraMap */ + DECLARE_LOGICAL(FORWARD); /* Use forward transformation? */ + F77_DOUBLE_TYPE *IN; /* Input coordinate array for FORTRAN */ + F77_DOUBLE_TYPE *OUT; /* Output coordinate array for FORTRAN */ + int coord; /* Loop counter for coordinates */ + int i; /* Index into FORTRAN arrays */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Assign input values to the arguments for the FORTRAN transformation + function. */ + THIS = astP2I( this ); + NPOINT = npoint; + NCOORD_IN = ncoord_in; + INDIM = npoint; + FORWARD = forward ? F77_TRUE : F77_FALSE; + NCOORD_OUT = ncoord_out; + OUTDIM = npoint; + +/* Since the input/output coordinate values may be stored in separate + arrays, we must move them temporarily into new 2-dimensional + arrays, as required by the FORTRAN transformation function + interface. Allocate memory for these arrays. */ + IN = astMalloc( (size_t) ( npoint * ncoord_in ) * + sizeof( F77_DOUBLE_TYPE ) ); + OUT = astMalloc( (size_t) ( npoint * ncoord_out ) * + sizeof( F77_DOUBLE_TYPE ) ); + +/* If OK, fill the input array with coordinate values. Use "memcpy" to + avoid numerical errors if the data contain junk - this allows the + transformation function to produce the appropriate error instead of + it happening here. */ + if ( astOK ) { + i = 0; + for ( coord = 0; coord < ncoord_in; coord++ ) { + (void) memcpy( IN + i, ptr_in[ coord ], + (size_t) npoint * sizeof( F77_DOUBLE_TYPE ) ); + i += npoint; + } + } + +/* Cast the transformation function pointer to a pointer to the + FORTRAN subroutine and then invoke it. Transfer the AST error + status to and from the subroutine's error status argument. */ + if ( astOK ) { + STATUS = astStatus; + ( *(void (*)()) tran )( INTEGER_ARG(&THIS), + INTEGER_ARG(&NPOINT), + INTEGER_ARG(&NCOORD_IN), + INTEGER_ARG(&INDIM), + DOUBLE_ARRAY_ARG(IN), + LOGICAL_ARG(&FORWARD), + INTEGER_ARG(&NCOORD_OUT), + INTEGER_ARG(&OUTDIM), + DOUBLE_ARRAY_ARG(OUT), + INTEGER_ARG(&STATUS) ); + astSetStatus( STATUS ); + } + + +/* If OK, transfer the transformed coordinate values to the output + arrays. */ + if ( astOK ) { + i = 0; + for ( coord = 0; coord < ncoord_out; coord++ ) { + (void) memcpy( ptr_out[ coord ], OUT + i, + (size_t) npoint * sizeof( F77_DOUBLE_TYPE ) ); + i += npoint; + } + } + +/* Free the temporary arrays. */ + astFree( IN ); + astFree( OUT ); +} + +/* FORTRAN interface functions. */ +/* ============================ */ +/* These functions implement the remainder of the FORTRAN interface. */ +F77_SUBROUTINE(ast_intrareg)( CHARACTER(NAME), + INTEGER(NIN), + INTEGER(NOUT), + void (* TRAN)(), + INTEGER(FLAGS), + CHARACTER(PURPOSE), + CHARACTER(AUTHOR), + CHARACTER(CONTACT), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(PURPOSE) + TRAIL(AUTHOR) + TRAIL(CONTACT) ) { + GENPTR_CHARACTER(NAME) + GENPTR_INTEGER(NIN) + GENPTR_INTEGER(NOUT) + GENPTR_INTEGER(FLAGS) + GENPTR_CHARACTER(PURPOSE) + GENPTR_CHARACTER(AUTHOR) + GENPTR_CHARACTER(CONTACT) + char *name; + void (* tran)( AstMapping *, int, int, const double *[], int, int, + double *[] ); + char *purpose; + char *author; + char *contact; + + astAt( "AST_INTRAREG", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + tran = + (void (*)( AstMapping *, int, int, const double *[], int, int, + double *[] )) TRAN; + purpose = astString( PURPOSE, PURPOSE_length ); + author = astString( AUTHOR, AUTHOR_length ); + contact = astString( CONTACT, CONTACT_length ); + astIntraRegFor( name, *NIN, *NOUT, tran, TranWrap, *FLAGS, purpose, + author, contact ); + astFree( name ); + astFree( purpose ); + astFree( author ); + astFree( contact ); + ) +} + +F77_INTEGER_FUNCTION(ast_intramap)( CHARACTER(NAME), + INTEGER(NIN), + INTEGER(NOUT), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(NAME) + GENPTR_INTEGER(NIN) + GENPTR_INTEGER(NOUT) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *name; + char *options; + int i; + + astAt( "AST_INTRAMAP", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astIntraMap( name, *NIN, *NOUT, "%s", options ) ); + astFree( name ); + astFree( options ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isaintramap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAINTRAMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAIntraMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} diff --git a/fitschan.c b/fitschan.c new file mode 100644 index 0000000..3e85444 --- /dev/null +++ b/fitschan.c @@ -0,0 +1,42623 @@ +/* +*class++ +* Name: +* FitsChan + +* Purpose: +* I/O Channel using FITS header cards to represent Objects. + +* Constructor Function: +c astFitsChan +f AST_FITSCHAN + +* Description: +* A FitsChan is a specialised form of Channel which supports I/O +* operations involving the use of FITS (Flexible Image Transport +* System) header cards. Writing an Object to a FitsChan (using +c astWrite) will, if the Object is suitable, generate a +f AST_WRITE) will, if the Object is suitable, generate a +* description of that Object composed of FITS header cards, and +* reading from a FitsChan will create a new Object from its FITS +* header card description. +* +* While a FitsChan is active, it represents a buffer which may +* contain zero or more 80-character "header cards" conforming to +* FITS conventions. Any sequence of FITS-conforming header cards +* may be stored, apart from the "END" card whose existence is +* merely implied. The cards may be accessed in any order by using +* the FitsChan's integer Card attribute, which identifies a "current" +* card, to which subsequent operations apply. Searches +c based on keyword may be performed (using astFindFits), new +c cards may be inserted (astPutFits, astPutCards, astSetFits) and +c existing ones may be deleted (astDelFits), extracted (astGetFits), +c or changed (astSetFits). +f based on keyword may be performed (using AST_FINDFITS), new +f cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS) and +f existing ones may be deleted (AST_DELFITS), extracted +f (AST_GETFITS) or changed (AST_SETFITS). +* +* When you create a FitsChan, you have the option of specifying +* "source" and "sink" functions which connect it to external data +* stores by reading and writing FITS header cards. If you provide +* a source function, it is used to fill the FitsChan with header cards +* when it is accessed for the first time. If you do not provide a +* source function, the FitsChan remains empty until you explicitly enter +c data into it (e.g. using astPutFits, astPutCards, astWrite +f data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE +* or by using the SourceFile attribute to specifying a text file from +* which headers should be read). When the FitsChan is deleted, any +* remaining header cards in the FitsChan can be saved in either of +* two ways: 1) by specifying a value for the SinkFile attribute (the +* name of a text file to which header cards should be written), or 2) +* by providing a sink function (used to to deliver header cards to an +* external data store). If you do not provide a sink function or a +* value for SinkFile, any header cards remaining when the FitsChan +* is deleted will be lost, so you should arrange to extract them +* first if necessary +c (e.g. using astFindFits or astRead). +f (e.g. using AST_FINDFITS or AST_READ). +* +* Coordinate system information may be described using FITS header +* cards using several different conventions, termed +* "encodings". When an AST Object is written to (or read from) a +* FitsChan, the value of the FitsChan's Encoding attribute +* determines how the Object is converted to (or from) a +* description involving FITS header cards. In general, different +* encodings will result in different sets of header cards to +* describe the same Object. Examples of encodings include the DSS +* encoding (based on conventions used by the STScI Digitised Sky +* Survey data), the FITS-WCS encoding (based on a proposed FITS +* standard) and the NATIVE encoding (a near loss-less way of +* storing AST Objects in FITS headers). +* +* The available encodings differ in the range of Objects they can +* represent, in the number of Object descriptions that can coexist +* in the same FitsChan, and in their accessibility to other +* (external) astronomy applications (see the Encoding attribute +* for details). Encodings are not necessarily mutually exclusive +* and it may sometimes be possible to describe the same Object in +* several ways within a particular set of FITS header cards by +* using several different encodings. +* +c The detailed behaviour of astRead and astWrite, when used with +f The detailed behaviour of AST_READ and AST_WRITE, when used with +* a FitsChan, depends on the encoding in use. In general, however, +c all successful use of astRead is destructive, so that FITS header cards +f all successful use of AST_READ is destructive, so that FITS header cards +* are consumed in the process of reading an Object, and are +* removed from the FitsChan (this deletion can be prevented for +* specific cards by calling the +c astRetainFits function). +f AST_RETAINFITS routine). +* An unsuccessful call of +c astRead +f AST_READ +* (for instance, caused by the FitsChan not containing the necessary +* FITS headers cards needed to create an Object) results in the +* contents of the FitsChan being left unchanged. +* +* If the encoding in use allows only a single Object description +* to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF +c encodings), then write operations using astWrite will +f encodings), then write operations using AST_WRITE will +* over-write any existing Object description using that +* encoding. Otherwise (e.g. the NATIVE encoding), multiple Object +* descriptions are written sequentially and may later be read +* back in the same sequence. + +* Inheritance: +* The FitsChan class inherits from the Channel class. + +* Attributes: +* In addition to those attributes common to all Channels, every + +* FitsChan also has the following attributes: +* +* - AllWarnings: A list of the available conditions +* - Card: Index of current FITS card in a FitsChan +* - CardComm: The comment of the current FITS card in a FitsChan +* - CardName: The keyword name of the current FITS card in a FitsChan +* - CardType: The data type of the current FITS card in a FitsChan +* - CarLin: Ignore spherical rotations on CAR projections? +* - CDMatrix: Use a CD matrix instead of a PC matrix? +* - Clean: Remove cards used whilst reading even if an error occurs? +* - DefB1950: Use FK4 B1950 as default equatorial coordinates? +* - Encoding: System for encoding Objects as FITS headers +* - FitsAxisOrder: Sets the order of WCS axes within new FITS-WCS headers +* - FitsDigits: Digits of precision for floating-point FITS values +* - Iwc: Add a Frame describing Intermediate World Coords? +* - Ncard: Number of FITS header cards in a FitsChan +* - Nkey: Number of unique keywords in a FitsChan +* - TabOK: Should the FITS "-TAB" algorithm be recognised? +* - PolyTan: Use PVi_m keywords to define distorted TAN projection? +* - Warnings: Produces warnings about selected conditions + +* Functions: +c In addition to those functions applicable to all Channels, the +c following functions may also be applied to all FitsChans: +f In addition to those routines applicable to all Channels, the +f following routines may also be applied to all FitsChans: +* +c - astDelFits: Delete the current FITS card in a FitsChan +c - astEmptyFits: Delete all cards in a FitsChan +c - astFindFits: Find a FITS card in a FitsChan by keyword +c - astGetFits: Get a keyword value from a FitsChan +c - astGetTables: Retrieve any FitsTables from a FitsChan +c - astPurgeWCS: Delete all WCS-related cards in a FitsChan +c - astPutCards: Stores a set of FITS header card in a FitsChan +c - astPutFits: Store a FITS header card in a FitsChan +c - astPutTable: Store a single FitsTable in a FitsChan +c - astPutTables: Store multiple FitsTables in a FitsChan +c - astReadFits: Read cards in through the source function +c - astRemoveTables: Remove one or more FitsTables from a FitsChan +c - astRetainFits: Ensure current card is retained in a FitsChan +c - astSetFits: Store a new keyword value in a FitsChan +c - astShowFits: Display the contents of a FitsChan on standard output +c - astTableSource: Register a source function for FITS table access +c - astTestFits: Test if a keyword has a defined value in a FitsChan +c - astWriteFits: Write all cards out to the sink function +f - AST_DELFITS: Delete the current FITS card in a FitsChan +f - AST_EMPTYFITS: Delete all cards in a FitsChan +f - AST_FINDFITS: Find a FITS card in a FitsChan by keyword +f - AST_GETFITS: Get a keyword value from a FitsChan +f - AST_GETTABLES: Retrieve any FitsTables from a FitsChan +f - AST_PURGEWCS: Delete all WCS-related cards in a FitsChan +f - AST_PUTCARDS: Stores a set of FITS header card in a FitsChan +f - AST_PUTFITS: Store a FITS header card in a FitsChan +f - AST_PUTTABLE: Store a single FitsTables in a FitsChan +f - AST_PUTTABLES: Store multiple FitsTables in a FitsChan +f - AST_READFITS: Read cards in through the source function +f - AST_REMOVETABLES: Remove one or more FitsTables from a FitsChan +f - AST_RETAINFITS: Ensure current card is retained in a FitsChan +f - AST_SETFITS: Store a new keyword value in a FitsChan +c - AST_SHOWFITS: Display the contents of a FitsChan on standard output +f - AST_TABLESOURCE: Register a source function for FITS table access +f - AST_TESTFITS: Test if a keyword has a defined value in a FitsChan +f - AST_WRITEFITS: Write all cards out to the sink function + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2008-2011 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink, RAL) +* TIMJ: Tim Jenness (JAC, Hawaii) + +* History: +* 11-DEC-1996 (DSB): +* Original version. +* 20-MAR-1997 (DSB): +* Made keyword setting and getting functions protected instead of +* public. Renamed public methods. Added Ncard attribute. +* 20-MAY-1997 (RFWS): +* Tidied public prologues. +* 30-JUN-1997 (DSB): +* Added support for reading post-2000 DATE-OBS strings. Reading DSS +* or FITS-WCS objects now returns NULL unless the FitsChan is +* positioned at the start-of-file prior to the read. Bug fixed +* which caused Ncard to be returned too large by one. Removed +* dependancy on hard-wired header and footer text in Native +* FitsChans. +* 18-AUG-1997 (DSB): +* Bug fixed in WcsNative which caused incorrect CRVAL values +* to be used if the axes needed permuting. Values assigned to the +* Projection attribute fo the SkyFrames created by astRead. +* 2-SEP-1997 (DSB): +* Added the IRAF convention that EPOCH=0.0 really means EPOCH=1950.0 +* (the EPOCH keyword is deprecated in the new FITS-WCS conventions +* and is taken always as a Besselian epoch). +* 19-SEP-1997 (DSB): +* Corrected interpretation of the FITS CD matrix. +* 25-SEP-1997 (DSB): +* o Fix bug in LinearMap which caused it always to detect a linear +* mapping. For instance, this allowed DssMaps to be erroneously +* written out using FITS-WCS encoding with a CAR projection. +* o Assign a full textual description to SkyFrame's Projection +* attribute instead of a 3 letter acronym. +* o If DATE-OBS >= 1999.0 then DATE-OBS is now written in new +* Y2000 format. For DATE-OBS < 1999.0, the old format is written. +* o Add new attribute CDMatrix to determine whether PC or CD +* matrices should be used when writing objects using FITS-WCS +* encoding. +* o Modified the way floating point values are formatted to omit +* unnecessary leading zeros from the exponent (i.e. E-5 instead of +* E-05). +* o New-line characters at the end of supplied header cards are now +* ignored. +* o Cater for EQUINOX specified as a string prefixed by B or J +* rather than as a floating point value (some HST data does this). +* o Corrected SetValue so that it always inserts comment cards +* rather than over-write existing comment cards. Previously, +* writing a FrameSet to a DSS encoded FitsChan resulted in all +* comments cards being stripped except for the last one. +* o Reading a FrameSet from a DSS-encoded FrameSet now only +* removes the keywords actually required to construct the FrameSet. +* Previously, all keywords were removed. +* o The EPOCH and EQUINOX keywords created when a FrameSet is +* written to a DSS-encoded FitsChan are now determined from the +* epoch and equinox of the current Frame, instead of from a copy +* of the original FitsChan stored within the DssMap. +* o The Encoding and CDMatrix attributes, and keyword types are +* now stored as strings externally instead of integers. +* 11-NOV-1997 (DSB): +* o Assume default of j2000 for DSS EQUINOX value. +* o Check for null object pointers in the interfaces for +* virtual functions which execute even if an error has previously +* occurred. Otherwise, a segmentation violation can occur when +* trying to find the member function pointer. +* o Trailing spaces ignored in Encoding attribute. +* o Bugs fixed in FindWcs and SetValue which resulted in WCS cards +* being written at the wrong place if the supplied FitsChan does not +* contain any WCS keywords. +* o Default for CDMatrix (if no axis rotation keywords can be found) +* changed to 2 (i.e. use "CDi_j" form keywords). +* o Write now leaves the current card unchanged if nothing is +* written to the FitsChan. +* 17-NOV-1997 (RFWS): +* Disabled use of CDmatrix. Fixed initialisation problems in +* astLoadFitsChan. +* 24-NOV-1997 (DSB): +* Replace references to error code AST__OPT with AST__RDERR. +* 28-NOV-1997 (DSB): +* o Function WcsValues modified to prevent it from changing the +* current card. Previously, this could cause new cards to be +* written to the wrong place in a FITS-WCS encoded FitsChan. +* o Description of argument "value" corrected in prologue of +* function SetFits. +* o Argument "lastkey" removed from function SetValue since it +* was never used (it was a relic from a previous method of +* determining where to store new cards). Corresponding changes +* have been made to all the functions which create "lastkey" values +* or pass them on to SetValue (i.e DescWcs, WcsPrimary, WcsSecondary, +* WriteWcs and WriteDss). +* 10-DEC-1997 (DSB): +* Bug fixed which caused the initial character designating the system +* within CTYPE value (eg E in ELON, G in GLON, etc) to be omitted. +* 1-JUN-1998 (DSB): +* CDELT values of zero are now replaced by a small non-zero value +* when creating the "pixel-to-relative physical" transformation +* matrix. Previously, zero CDELT values could cause the matrix to +* be non-invertable. +* 4-SEP-1998 (DSB): +* - Indicate that SphMaps created by this class when using FITS-WCS +* encoding all operate on the unit sphere. This aids simplification. +* - Fix a bug in StoreFits which caused CD matrices to be indexed +* incorrectly (sometimes causing floating exceptions) if they do not +* describe a celestial longitude/latitude system. +* - Changed astFindFits to ignore trailing spaces in the keyword +* template. +* - astSplit changed so that an error is not reported if a textual +* keyword value ends before column 20. +* 7-OCT-1998 (DSB): +* - Corrected test for linearity in LinearMap to include a factor +* of the test vector length. Also LinearMap now uses a simplified +* Mapping. +* 5-NOV-1998 (DSB): +* Added FITS-IRAF encoding. +* 9-NOV-1998 (DSB): +* - Corrected values of macros DSS_ENCODING and MAX_ENCODING. +* - Corrected erroneous success indication in IrafStore. +* - Included checks for bad values in function LinearMap. +* 17-NOV-1998 (DSB): +* The Domain name GRID is now given to the Base Frame in any FrameSets +* created by astRead when using FitsChans with DSS, FITS-WCS or +* FITS-IRAF encodings. +* 18-DEC-1998 (DSB): +* Check for "D" exponents in floating point keyword strings. +* 12-FEB-1998 (DSB): +* Modified EncodeFloat to avoid exceeding the 20 character FITS +* limit wherever possible if FitsDigits is positive. +* 10-MAY-1998 (DSB): +* Bug fixed in astSplit which caused comments associated with string +* keywords to be lost when storing the card in a FitsChan. +* 15-JUN-1999 (DSB): +* Report an error if an unrecognised projection name is supplied. +* 9-DEC-1999 (DSB): +* - Fixed bug in WcsNatPole which could result in longitude values +* being out by 180 degrees for cylindrical projections such as CAR. +* - Only report an "unrecognised projection" error for CTYPE values +* which look like celestial longitude or latitude axes (i.e. if the +* first 4 characters are "RA--", "DEC-", "xLON" or "xLAT", and the +* fifth character is "-"). +* - Added function SpecTrans to translated keywords related to the +* IRAF ZPX projection into keyword for the standard ZPN projection. +* - Add ICRS as a valid value for the RADECSYS keyword. Since the +* SkyFrame class does not yet support ICRS, an FK5 SkyFrame is +* created if RADECSYS=ICRS. +* 16-DEC-1999 (DSB): +* - Modified SpecTrans so that all keywords used to created a +* standard WCS representation from a non-standard one are consumed +* by the astRead operation. +* - Changed the text of ASTWARN cards added to the FitsChan if an +* IRAF ZPX projection is found to require unsupported corrections. +* - Simplified the documentation describing the handling of the IRAF +* ZPX projection. +* - Fixed code which assumed that the 10 FITS-WCS projection +* parameters were PROJP1 -> PROJP10. In fact they are PROJP0 - +* PROJP9. This could cause projection parameter values to be +* incorrectly numbered when they are written out upon deletion of +* the FitsChan. +* 1-FEB-2000 (DSB): +* Check that FITS_IRAF encoding is not being used before using a +* PC matrix when reading WCS information from a header. This is +* important if the header contains both PC and CD matrices. +* 8-FEB-2000 (DSB): +* - Header cards are now only consumed by an astRead operation if the +* operation succeeds (i.e. returns a non-null Object). +* - The original FITS-WCS encoding has been renamed as FITS-PC (to +* indicate the use of a PCiiijjj matrix), and a new FITS-WCS +* encoding has been added. +* - The disabled CDMatrix attribute has been removed. +* - Bug in LinearMap corrected which prevented genuinely linear +* Mappings from being judged to be linear. This bug was previously +* fudged (so it now appears) by the introduction of the test vector +* length factor (see History entry for 7-OCT-1998). This test +* vector length scale factor has consequently now been removed. +* - Added FITS-AIPS encoding. +* - The critical keywords used to select default encoding have been +* changed. +* - Support for common flavours of IRAF TNX projections added. +* - The algorithm used to find a WcsMap in the supplied FrameSet +* has been improved so that compound Mappings which contain complex +* mixtures of parallel and serial Mappings can be translated into +* FITS-WCS encoding. +* - Trailing white space in string keyword values is now retained +* when using foreign encodings to enable correct concatenation where +* a string has been split over several keywords. E.g. if 2 string +* keywords contain a list of formatted numerical values (e.g. IRAF +* WAT... keywords), and the 1st one ends "0.123 " and the next one +* begins "1234.5 ", the trailing space at the end of the first keyword +* is needed to prevent the two numbers being merged into "0.1231234.5". +* Trailing spaces in native encodings is still protected by enclosing +* the whole string in double quotes. +* - The Channel methods WriteString and GetNextData can now save +* and restore strings of arbitary length. This is done by storing +* as much of the string as possible in the usual way, and then +* storing any remaining characters in subsequent CONTINUE cards, +* using the FITSIO conventions. This storage and retrieval of long +* strings is only available for native encodings. +* 19-MAY-2000 (DSB): +* Added attribute Warnings. Lowered DSS in the priority list +* of encodings implemented by GetEncoding. +* 6-OCT-2000 (DSB): +* Increased size of buffers used to store CTYPE values to take +* account of the possiblity of lots of trailing spaces. +* 5-DEC-2000 (DSB): +* Add support for the WCSNAME FITS keyword. +* 12-DEC-2000 (DSB): +* Add a title to each physical, non-celestial coord Frame based on +* its Domain name (if any). +* 3-APR-2001 (DSB): +* - Use an "unknown" celestial coordinate system, instead of a +* Cartesian coordinate system, if the CTYPE keywords specify an +* unknown celestial coordinate system. +* - Do not report an error if there are no CTYPE keywords in the +* header (assume a unit mapping, like in La Palma FITS files). +* - Add a NoCTYPE warning condition. +* - Added AllWarnings attribute. +* - Ensure multiple copies of identical warnings are not produced. +* - Use the Object Ident attribute to store the identifier letter +* associated with each Frame read from a secondary axis description, +* so that they can be given the same letter when they are written +* out to a new FITS file. +* 10-AUG-2001 (DSB): +* - Corrected function value returned by SkySys to be 1 unless an +* error occurs. This error resulted in CAR headers being produced +* by astWrite with CRVAL and CD values till in radians rather than +* degrees. +* - Introduced SplitMap2 in order to guard against producing +* celestial FITS headers for a Mapping which includes more than +* one WcsMap. +* 13-AUG-2001 (DSB): +* - Modified FixNew so that it retains the current card index if possible. +* This fixed a bug which could cause headers written out using Native +* encodings to be non-contiguous. +* - Corrected ComBlock to correctly remove AST comment blocks in +* native encoded fitschans. +* 14-AUG-2001 (DSB): +* - Modified FixUsed so that it it does not set the current card +* back to the start of file if the last card in the FitsChan is +* deleted. +* 16-AUG-2001 (DSB): +* Modified WcsNative to limit reference point latitude to range +* +/-90 degs (previously values outside this range were wrapped +* round onto the opposite meridian). Also added new warning +* condition "badlat". +* 23-AUG-2001 (DSB): +* - Re-write LinearMap to use a least squares fit. +* - Check that CDj_i is not AST__BAD within WcsWithWcs when +* forming the increments along each physical axis. +* 28-SEP-2001 (DSB): +* GoodWarns changed so that no error is reported if a blank list +* of conditions is supplied. +* 12-OCT-2001 (DSB): +* - Added DefB1950 attribute. +* - Corrected equations which calculate CROTA when writing +* FITS-AIPS encodings. +* - Corrected equations which turn a CROTA value into a CD matrix. +* 29-NOV-2001 (DSB): +* Corrected use of "_" and "-" characters when referring to FK4-NO-E +* system in function SkySys. +* 20-FEB-2002 (DSB) +* Added CarLin attribute. +* 8-MAY-2002 (DSB): +* Correct DSSToStore to ignore trailing blanks in the PLTDECSN +* keyword value. +* 9-MAY-2002 (DSB): +* Correct GetCard to avoid infinite loop if the current card has +* been marked as deleted. +* 25-SEP-2002 (DSB): +* AIPSFromStore: use larger of coscro and sincro when determining +* CDELT values. Previously a non-zero coscro was always used, even +* if it was a s small as 1.0E-17. +* 3-OCT-2002 (DSB): +* - SkySys: Corrected calculation of longitude axis index for unknown +* celestial systems. +* - SpecTrans: Corrected check for latcor terms for ZPX projections. +* - WcsFrame: Only store an explicit equinox value in a skyframe if +* it needs one (i.e. if the system is ecliptic or equatorial). +* - WcsWithWcs: For Zenithal projections, always use the default +* LONPOLE value, and absorb any excess rotation caused by this +* into the CD matrix. +* - WcsWithWcs: Improve the check that the native->celestial mapping +* is a pure rotation, allowing for rotations which change the +* handed-ness of the system (if possible). +* - WcsWithWcs: Avoid using LONPOLE keywords when creating headers +* for a zenithal projection. Instead, add the corresponding rotation +* into the CD matrix. +* 22-OCT-2002 (DSB): +* - Retain leading and trailing white space within COMMENT cards. +* - Only use CTYPE comments as axis labels if all non-celestial +* axes have a unique non-blank comment (otherwise use CTYPE +* values as labels). +* - Updated to use latest FITS-WCS projections. This means that the +* "TAN with projection terms" is no longer a standard FITS +* projection. It is now represented using the AST-specific TPN +* projection (until such time as FITS-WCS paper IV is finished). +* - Remove trailing "Z" from DATE-OBS values created by astWrite. +* 14-NOV-2002 (DSB): +* - WcsWithWcs: Corrected to ignore longitude axis returned by +* astPrimaryFrame since it does not take into account any axis +* permutation. +* 26-NOV-2002 (DSB): +* - SpecTrans: Corrected no. of characters copied from CTYPE to PRJ, +* (from 5 to 4), and terminate PRJ correctly. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitFitsChanVtab +* method. +* 22-JAN-2003 (DSB): +* Restructured the functions used for reading FITS_WCS headers to +* make the distinction between the generic parts (pixel->intermediate +* world coordinates) and the specialised parts (e.g. celestial, +* spectral, etc) clearer. +* 31-JAN-2003 (DSB) +* - Added Clean attribute. +* - Corrected initialisation and defaulting of CarLin and DefB1950 +* attributes. +* - Extensive changes to allow foreign encodings to be produced in +* cases where the Base Frame has fewer axes than the Current Frame. +* 12-FEB-2003 (DSB) +* - Modified SetFits so that the existing card comment is retained +* if the new data value equals the existing data value. +* 30-APR-2003 (DSB): +* - Revert to standard "TAN" code for distorted tan projections, +* rather than using the "TPN" code. Also recognise QVi_m (produced +* by AUTOASTROM) as an alternative to PVi_m when reading distorted +* TAN headers. +* 22-MAY-2003 (DSB): +* Modified GetEncoding so that the presence of RADECSYS and/or +* PROJPm is only considered significant if the modern equivalent +* keyword (REDESYS or PVi_m) is *NOT* present. +* 2-JUN-2003 (DSB): +* - Added support for PCi_j kewwords within FITS-WCS encoding +* - Added CDMatrix attribute +* - Changed internal FitsStore usage to use PC/CDELT instead of CD +* (as preparation for FITS-WCS paper IV). +* - Added warning "BadMat". +* 11-JUN-2003 (DSB): +* - Modified WcsNative to use the new SphMap PolarLong attribute +* in order to ensure correct propagation of the longitude CRVAL +* value in cases where the fiducial point is coincident with a pole. +* - Use PVi_3 and PVi_4 for longitude axis "i" (if present) in +* preference to LONPOLE and LATPOLE when reading a FITS-WCS header. +* Note, these projection values are never written out (LONPOLE and +* LATPOLE are written instead). +* - Associate "RADESYS=ICRS" with SkyFrame( "System=ICRS" ), rather +* than SkyFrame( "System=FK5" ). +* - If DefB1950 is zero, use ICRS instead of FK5 as the default RADESYS +* if no EQUINOX is present. +* 1-SEP-2003 (DSB): +* - Modify Dump so that it dumps all cards including those flagged as +* having been read. +* - Added "reset" parameter to FixUsed. +* - WcsMapFrm: store an Ident of ' ' for the primary coordinate +* description (previously Ident was left unset) +* - Default value for DefB1950 attribute now depends on the value +* of the Encoding attribute. +* 15-SEP-2003 (DSB): +* - Added Warnings "BadVal", "Distortion". +* - Ignore FITS-WCS paper IV CTYPE distortion codes (except for +* "-SIP" which is interpreted correctly on reading). +* 22-OCT-2003 (DSB): +* - GetEncoding: If the header contains CDi_j but does not contain +* any of the old IRAF keywords (RADECSYS, etc) then assume FITS-WCS +* encoding. This allows a FITS-WCS header to have both CDi_j *and* +* CROTA keywords. +* 5-JAN-2004 (DSB): +* - SpecTrans: Use 1.0 (instead of the CDELT value) as the +* diagonal PCi_j term for non-celestial axes with associated CROTA +* values. +* 12-JAN-2004 (DSB): +* - CelestialAxes: Initialise "tmap1" pointer to NULL in case of error +* (avoids a segvio happening in the case of an error). +* - AddVersion: Do not attempt to add a Frame into the FITS header +* if the mapping from grid to frame is not invertable. +* - WorldAxes: Initialise the returned "perm" values to safe values, +* and return these values if no basis vectors cen be created. +* 19-JAN-2004 (DSB): +* - When reading a FITS-WCS header, allow all keywords to be defaulted +* as decribed in paper I. +* 27-JAN-2004 (DSB): +* - Modify FitLine to use correlation between actual and estimated +* axis value as the test for linearity. +* - Modify RoundFString to avoid writing beyond the end of the +* supplied buffer if the supplied string contains a long list of 9's. +* 11-MAR-2004 (DSB): +* - Modified SpecTrans to check all axis descriptions for keywords +* to be translated. +* 19-MAR-2004 (DSB): +* - Added astPutCards to support new fits_hdr2str function in +* CFITSIO. +* 25-MAR-2004 (DSB): +* - Corrected bug in astSplit which causes legal cards to be +* rejected because characters beyond the 80 char limit are being +* considered significant. +* - Corrected bug in SpecTrans which caused QV keywords to be +* ignored. +* 15-APR-2004 (DSB): +* - SpecTrans modified to include translation of old "-WAV", "-FRQ" +* and "-VEL" spectral algorithm codes to modern "-X2P" form. +* - WcsFromStore modified to supress creation of WCSAXES keywords +* for un-used axis versions. +* - IsMapLinear modified to improve fit by doing a second least +* squares fit to the residualleft by the first least squares fit. +* 16-APR-2004 (DSB): +* - NonLinSpecWcs: Issue a warning if an illegal non-linear +* spectral code is encountered. +* - Add a BadCTYPE warning condition. +* - Corrected default value for Clean so that it is zero (as +* documented). +* 21-APR-2004 (DSB): +* - FindWcs: Corrected to use correct OBSGEO template. This bug +* caused OBSGEO keywords to be misplaced in written headers. +* 23-APR-2004 (DSB): +* - SplitMap: Modified so that a Mapping which has celestial axes +* with constant values (such as produced by a PermMap) are treated +* as a valid sky coordinate Mapping. +* - AddFrame modified so that WCS Frames with a different number +* of axes ot the pixel Frame can be added into the FrameSet. +* - IRAFFromStore and AIPSFromStore modified so that they do not +* create any output keywords if the number of WCS axes is different +* to the number of pixel axes. +* - Handling of OBSGEO-X/Y/Z corrected again. +* - WCSFromStore modified to avouid writing partial axis descriptions. +* 26-APR-2004 (DSB): +* - Corrected text of output SPECSYS keyword values. +* 17-MAY-2004 (DSB): +* - Added IWC attribute. +* 15-JUN-2004 (DSB): +* - Ensure out-of-bounds longitude CRPIX values for CAR +* projections are wrapped back into bounds. +* 21-JUN-2004 (DSB): +* - Ensure primary MJD-OBS value is used when reading foreign FITS +* headers. +* 7-JUL-2004 (DSB): +* - Issue errors if an un-invertable PC/CD matrix is supplied in a +* FITS-WCS Header. +* 11-JUL-2004 (DSB): +* - Re-factor code for checking spectral axis CTYPE values into +* new function IsSpectral. +* - Modify AIPSFromSTore to create spectral axis keywords if +* possible. +* - Modify SpecTrans to recognize AIPS spectral axis keywords, and +* to convert "HZ" to "Hz". +* - Added FITS-AIPS++ encoding. +* 12-AUG-2004 (DSB): +* - Convert GLS projection codes to equivalent SFL in SpecTrans. +* - Added FITS-CLASS encoding. +* 16-AUG-2004 (DSB): +* - Removed support for paper III keyword VSOURCE, and added +* support for SSYSSRC keyword. +* - Added initial support for CLASS encoding. +* - In FitOK: Changed tolerance for detecting constant values +* from 1.0E-10 to 1.0E-8. +* 17-AUG-2004 (DSB): +* Correct GetFiducialNSC so that the stored values for longitude +* parameters 1 and 2 are ignored unless the value of parameter 0 is +* not zero. +* 19-AUG-2004 (DSB): +* Modify SpecTrans to ignore any CDELT values if the header +* includes some CDi_j values. +* 26-AUG-2004 (DSB): +* Modify astSplit_ to allow floating point keyword values which +* include an exponent to be specified with no decimal point +* (e.g. "2E-4"). +* 27-AUG-2004 (DSB): +* Completed initial attempt at a FITS-CLASS encoding. +* 9-SEP-2004 (DSB): +* Fixed usage of uninitialised values within ReadCrval. +* 13-SEP-2004 (DSB): +* Check the "text" pointer can be used safely before using it in +* DSSToStore. +* 27-SEP-2004 (DSB): +* In SpecTrans, before creating new PCi_j values, check that no +* PCi_j values have been created via an earlier translation. +* 28-SEP-2004 (DSB): +* In AIPSPPFromStore only get projection parameters values if there +* are some celestialaxes. Also allow CROTA to describe rotation of +* non-celestial axes (same for AIPSFromSTore). +* 4-OCT-2004 (DSB): +* Correct rounding of CRPIX in AddVersion to avoid integer overflow. +* 11-NOV-2004 (DSB): +* - WcsFcRead: Avoid issuing warnings about bad keywords which +* have already been translated into equivalent good forms. +* - SpecTrans: If both PROJP and PV keywords are present, use PV +* in favour of PROJP only if the PV values look correct. +* 17-NOV-2004 (DSB): +* - Make astSetFits public. +* 16-MAR-2005 (DSB): +* - Primary OBSGEO-X/Y/Z, MJD-AVG and MJDOBS keywords are associated +* with all axis descriptions and should not have a trailing single +* character indicating an alternate axis set. +* 9-AUG-2005 (DSB): +* In WcsMapFrm, check reffrm is used before annulling it. +* 8-SEP-2005 (DSB): +* - Change "if( a < b < c )" constructs to "if( a < b && b < c )" +* - DSBSetup: correct test on FrameSet pointer state +* - Ensure CLASS keywords written to a FitsChan do not come before +* the final fixed position keyword. +* 9-SEP-2005 (DSB): +* - Added "AZ--" and "EL--" as allowed axis types in FITS-WCS +* ctype values. +* 12-SEP-2005 (DSB): +* - Cast difference between two pointers to (int) +* - CLASSFromStore:Check source velocity is defined before +* storing it in the output header. +* 13-SEP-2005 (DSB): +* - Corrected B1940 to B1950 in AddEncodingFrame. This bug +* prevented some FrameSets being written out using FITS-CLASS. +* - Rationalise the use of the "mapping" pointer in AddVersion. +* - WcsCelestial: Modified so that the FITS reference point is +* stored as the SkyFrame SkyRef attribute value. +* 7-OCT-2005 (DSB): +* Make astGetFits public. +* 30-NOV-2005 (DSB): +* Add support for undefined FITS keyword values. +* 5-DEC-2005 (DSB): +* - Include an IMAGFREQ keyword in the output when writing a +* DSBSpecFrame out using FITS-WCS encoding. +* - Correct test for constant values in FitOK. +* 7-DEC-2005 (DSB): +* Free memory allocated by calls to astReadString. +* 30-JAN-2006 (DSB): +* Modify astSplit so that it does no read the supplied card beyond +* column 80. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 28-FEB-2006 (DSB): +* Correct documentation typo ("NCards" -> "Ncard"). +* 5-APR-2006 (DSB): +* Modify SpecTrans to convert CTYPE="LAMBDA" to CTYPE="WAVE". +* 26-MAY-2006 (DSB): +* Guard against NULL comment pointer when converting RESTFREQ to +* RESTFRQ in SpecTrans. +* 29-JUN-2006 (DSB): +* - Added astRetainFits. +* - Consume VELOSYS FITS-WCS keywords when reading an object. +* - Write out VELOSYS FITS-WCS keywords when writing an object. +* 7-AUG-2006 (DSB): +* Remove trailing spaces from the string returned by astGetFitsS +* if the original string contains 8 or fewer characters. +* 16-AUG-2006 (DSB): +* Document non-destructive nature of unsuccessful astRead calls. +* 17-AUG-2006 (DSB): +* Fix bugs so that the value of the Clean attribute is honoured +* even if an error has occurred. +* 4-SEP-2006 (DSB): +* Modify GetClean so that it ignores the inherited status. +* 20-SEP-2006 (DSB): +* Fix memory leak in WcsSpectral. +* 6-OCT-2006 (DSB): +* Modify IsSpectral and IsAIPSSpectral to allow for CTYPE values that +* are shorter than eight characters. +* 13-OCT-2006 (DSB): +* - Ensure SpecFrames and SkyFrames created from a foreign FITS header +* are consistent in their choice of Epoch. +* - Convert MJD-OBS and MJD-AVG values from TIMESYS timescale to +* TDB before using as the Epoch value in an AstFrame. Use UTC if +* TIMESYS is absent. +* - Convert Epoch values from TDB to UTC before storing as the +* value of an MJD-OBS or MJD-AVG keyword (no TIMESYS keyword is +* written). +* 23-OCT-2006 (DSB): +* Prefer MJD-AVG over MJD-OBS. +* 30-OCT-2006 (DSB): +* In FitOK: Changed lower limit on acceptbale correlation from +* 0.999999 to 0.99999. +* 1-NOV-2006 (DSB): +* - When reading a foreign header that contains a DUT1 keyword, +* use it to set the Dut1 attribute in the SkyFrame. Note, JACH +* store DUT1 in units of days. This may clash with the FITS-WCS +* standard (when its produced). Also note that DUT1 is not written +* out as yet when writing a FrameSet to a foreign FITS header. +* - Correct bug that prevented ZSOURCE keyword being added to the +* output header if the source velocity was negative. +* 9-NOV-2006 (DSB): +* Add STATUS argument to docs for F77 AST_SETx. +* 20-DEC-2006 (DSB): +* Correct FK5 to ICRS in error message issued if no RADESYS or +* EQUINOX is found. +* 16-JAN-2007 (DSB): +* Cast ignored function return values to (void) to avoid compiler +* warnings. +* 31-JAN-2007 (DSB): +* Change SpecTrans to ignore blank unit strings (previously +* converted them to "Hz"). +* 16-APR-2007 (DSB): +* In SplitMat, increase the allowed level of rounding erros from +* 1.0E-10 to 1.0E-7 (to avoid spurious low CDi_j values being +* created that should be zero). +* 30-APR-2007 (DSB): +* - Change DSBSetup so that the central DSBSpecFrame frequency is +* CRVAL and the IF is the difference between CRVAL and LO. +* - Change tolerance in FitOK from 0.99999 to 0.995 to handle data from Nicolas +* Peretto. +* 1-MAY-2007 (DSB): +* - In astSplit, if a keyword value looks like an int but is too long to +* fit in an int, then treat it as a float instead. +* 18-MAY-2007 (DSB): +* In CnvType, use input type rather than output type when checking +* for a COMMENT card. Also, return a null data value buffer for a +* COMMENT card. +* 4-JUN-2007 (DSB): +* In CLASSFromStore, create a DELTAV header even if it is equal to +* the spectral CDELT value. Also, convert spatial reference point +* to (az,el) and write out as headers AZIMUTH and ELEVATIO. +* 9-JUL-2007 (DSB): +* Fixed bug in DSBSetUp - previously, this function assumed that +* the supplied DSBSpecFrame represented frequency, and so gave +* incorrect values for IF and DSBCentre if the header described +* velocity. +* 9-AUG-2007 (DSB): +* Changed GetEncoding so that critcal keywords are ignored if +* there are no CTYPE, CRPIX or CRVAL keywords in the header. +* 10-AUG-2007 (DSB): +* - Changed GetEncoding so that FITS_PC is not returned if there are +* any CDi_j or PCi_j keywords in the header. +* - Added astPurgeWCS method. +* 13-AUG-2007 (DSB): +* - Include the DSS keywords AMDX%d and AMDY%d in FindWCS. +* 16-AUG-2007 (DSB): +* - Force all FITS-CLASS headers to contain frequency axes +* (velocity axes seem not to be recognised properly by CLASS). +* - Change the CLASS "VELO-LSR" header to be the velocity at the +* reference channel, not the source velocity. +* 22-AUG-2007 (DSB): +* - Remove debugging printf statements. +* 20-SEP-2007 (DSB): +* Changed FitOK to check that the RMS residual is not more than +* a fixed small fraction of the pixel size. +* 4-DEC-2007 (DSB): +* Changed CreateKeyword so that it uses a KeyMap to search for +* existing keywords. This is much faster than checking every +* FitsCard in the FitsChan explicitly. +* 18-DEC-2007 (DSB): +* Add keyword VLSR to the CLASS encoding. It holds the same value +* as VELO-LSR, but different versions of class use different names. +* Also write out the DELTAV keyword in the LSR rest frame rather +* than the source rest frame. +* 31-JAN-2008 (DSB): +* Correct calculation of redshift from radio velocity in ClassTrans. +* 25-FEB-2008 (DSB): +* Ensure a SkyFrame represents absolute (rather than offset) +* coords before writing it out in any non-native encoding. +* 28-FEB-2008 (DSB): +* Test for existing of SkyRefIs attribute before accessing it. +* 2-APR-2008 (DSB): +* In CLASSFromStore, adjust the spatial CRVAL and CRPIX values to be +* the centre of the first pixel if the spatial axes are degenerate. +* 17-APR-2008 (DSB): +* Ignore latitude axis PV terms supplied in a TAN header +* (previously, such PV terms were used as polynomial correction +* terms in a TPN projection). +* 30-APR-2008 (DSB): +* SetValue changed so that new keywords are inserted before the +* current card. +* 1-MAY-2008 (DSB): +* Added UndefRead warning. +* 7-MAY-2008 (DSB): +* Correct conversion of CDi_j to PCi_j/CDELT in SpecTrans. +* 8-MAY-2008 (DSB): +* When writing out a FITS-WCS header, allow linear grid->WCS +* mapping to be represented by a CAR projection. +* 9-MAY-2008 (DSB): +* Make class variables IgnoreUsed and MarkNew static. +* 30-JUN-2008 (DSB): +* Improve efficiency of FindWcs. +* 2-JUL-2008 (DSB): +* FitsSof now returns non-zero if the FitsChan is empty. +* 16-JUL-2008 (DSB): +* Plug memory leak caused by failure to free the Warnings +* attribute string when a FitsChan is deleted. +* 24-JUL-2008 (TIMJ): +* Fix buffer overrun in astGetFits when writing the keyword +* to the buffer (occurred if the input string was 80 characters). +* 1-OCT-2008 (DSB): +* When reading a FITS-WCS header, spurious PVi_j keywords no +* longer generate an error. Instead they generate warnings via the +* new "BadPV" warning type. +* 21-NOV-2008 (DSB): +* Do not remove keywords from read headers if they may be of +* relevance to things other than WCS (e.g. MJD-OBS, OBSGEO, etc). +* 2-DEC-2008 (DSB): +* - astGetFits now reports an error if the keyword value is undefined. +* - Add new functions astTestFits and astSetFitsU. +* - Remove use of AST__UNDEF constants. +* - Remove "undefread" warning. +* 16-JAN-2009 (DSB): +* Use astAddWarning to store each warning in the parent Channel +* structure. +* 4-MAR-2009 (DSB): +* DATE-OBS and MJD-OBS cannot have an axis description character. +* 13-MAR-2009 (DSB): +* The VELOSYS value read from the header is never used, so do not +* report an error if VELOSYS has an undefined value. +* 11-JUN-2009 (DSB): +* Delay reading cards from the source until they are actually +* needed. Previously, the source function was called in the +* FitsChan initialiser, but this means it is not possible for +* application code to call astPutChannelData before the source +* function is called. The ReadFromSource function is now called +* at the start of each (nearly) public or protected function to +* ensure the source function has been called (the source function +* pointer in the FitsChan is then nullified to ensure it is not +* called again). +* 18-JUN-2009 (DSB): +* Include the effect of observer height (in the ObsAlt attribute) +* when creating OBSGEO-X/Y/Z headers, and store a value for +* ObsAlt when reading a set of OBSGEO-X/Y/Z headers. +* 2-JUL-2009 (DSB): +* Check FitsChan is not empty at start of FindWcs. +* 7-JUL-2009 (DSB): +* Add new function astSetFitsCM. +* 30-JUL-2009 (DSB): +* Fix axis numbering in SkyPole. +* 12-FEB-2010 (DSB): +* Use "" to represent AST__BAD externally. +* 25-JUN-2010 (DSB): +* Fix problem rounding lots of 9's in RoundFString. The problem +* only affected negative values, and could lead to an extra zero +* being included in the integer part. +* 28-JUN-2010 (DSB): +* Another problem in RoundFString! If the value has a series of +* 9's followed by a series of zeros, with no decimal point (e.g. +* "260579999000"), then the trailing zeros were being lost. +* 16-JUL-2010 (DSB): +* In SpecTrans, avoid over-writing the spatial projection code +* with the spectral projection code. +* 20-JUL-2010 (DSB): +* Correct interpretation of NCP projection code. +* 14-OCT-2010 (DSB): +* - Correct loading of FitsChans that contain UNDEF keywords. +* - Correct translation of spectral units with non-standard +* capitalisation in SpecTrans. +* 10-JAN-2011 (DSB): +* Fix memory leak in MakeIntWorld. +* 13-JAN-2011 (DSB): +* Rename astEmpty ast astEmptyFits and make public. +* 20-JAN-2011 (DSB): +* - Extensive changes to support -TAB algorithm +* - Recovery from a major unrequested reformatting of whitespace by +* my editor! +* 7-FEB-2011 (DSB): +* Put a space between keyword value and slash that starts a comment +* when formatting a FITS header card. +* 11-FEB-2011 (DSB): +* Change meaning of TabOK attribute. It is no longer a simple +* boolean indicating if the -TAB algorithm is supported. Instead +* it gives the value to be used for the EXTVER header - i.e. the +* version number to store with any binary table created as a +* result of calling astWrite. If TabOK is zero or begative, then +* the -TAB algorithm is not supported. This is so that there is +* some way of having multiple binary table extensions with the same +* name (but different EXTVER values). +* 14-FEB-2011 (DSB): +* - Spectral reference point CRVAL records the obs. centre. So for -TAB +* (when CRVAL is set to zero) we need to record the obs centre some +* other way (use the AST-specific AXREF keywords, as for spatial axes). +* - Whether to scale spatial axes from degs to rads depends on +* whether the spatial axes are descirbed by -TAB or not. +* - Relax the linearity requirement in IsMapLinear by a factor of +* 10 to prevent a change in rest frame resulting in a non-linear +* mapping. +* 17-FEB-2011 (DSB): +* Fix bug in axis linearity check (IsMapLinear). +* 22-FEB-2011 (DSB): +* The translations of AIPS non-standard CTYPE values were always +* stored as primary axis description keywords, even if the original +* non-standard CTYPE values were read from an alternative axis +* descriptions. +* 5-APR-2011 (DSB): +* In SpecTrans, correct the MSX CAR projection translation. The +* first pixel starts at GRID=0.5, not GRID=0.0. So the CRPIX value +* needs to be reduced by 0.5 prior to normalisation, and then +* increased by 0.5 after normalisation. +* 23-MAY-2011 (DSB): +* Add support for TNX projections that use Chebyshev polynomials. +* 24-MAY-2011 (DSB): +* - Add support for ZPX projections that include IRAF polynomial +* corrections. +* - Add PolyTan attribute. +* - Fix interpretation of -SIP headers that have no inverse. +* 1-JUN-2011 (DSB): +* In astInitFitsChanVtab, only create the two TimeFrames if they +* have not already been created (fixes scuba2 trac ticket #666). +* 9-JUN-2011 (DSB): +* In WCSFcRead, ignore trailing spaces when reading string values +* for WCS keywords. +* 23-JUN-2011 (DSB): +* - Override the parent astSetSourceFile method so that it reads +* headers from the SourceFile and appends them to the end of the +* FitsChan. +* - On deletion, write out the FitsChan contents to the file +* specified by the SinkFile attribute. If no file is specified, +* use the sink function specified when the FitsChan was created. +* 30-AUG-2011 (DSB): +* - Added astWriteFits and astReadFits. +* - Move the deletion of tables and warnings from Delete to +* EmptyFits. +* 21-SEP-2011 (DSB): +* - In RoundFString, remember to update the pointer to the exponent. +* This bug caused parts of the exponent to dissappear when +* formatting a value that included some trailing zeros and a +* series of adjacent 9's. +* - Added Nkey attribute. +* 22-SEP-2011 (DSB): +* - Added CardType attribute +* - Allow GetFits to be used to get/set the value of the current +* card. +* 4-OCT-2011 (DSB): +* When reading a FITS-WCFS header, if the projection is TPV (as produced +* by SCAMP), change to TPN (the internal AST code for a distorted +* TAN projection). +* 22-NOV-2011 (DSB): +* Allow the "-SIP" code to be used with non-celestial axes. +* 1-FEB-2012 (DSB): +* Write out MJD-OBS in the timescale specified by any TIMESYS +* keyword in the FitsChan, and ensure the TIMESYS value is included +* in the output header. +* 23-FEB-2012 (DSB): +* Use iauGd2gc in place of palGeoc where is saves some calculations. +* 24-FEB-2012 (DSB): +* Move invocation of AddEncodingFrame from Write to end of +* MakeFitsFrameSet. This is so that AddEncodingFrame can take +* advantage of any standardisations (such as adding celestial axes) +* performed by MakeFItsFrameSet. Without this, a FRameSet contain +* a 1D SpecFrame (no celestial axes) would fail to be exported using +* FITS-CLASS encoding. +* 29-FEB-2012 (DSB): +* Fix bug in CLASSFromStore that caused spatial axes added by +* MakeFitsFrameSet to be ignored. +* 2-MAR-2012 (DSB): +* - In CLASSFromSTore, ensure NAXIS2/3 values are stored in teh FitsChan, +* and cater for FrameSets that have only a apectral axis and no celestial +* axes (this prevented the VELO_LSR keyword being created).. +* 7-MAR-2012 (DSB): +* Use iauGc2gd in place of Geod. +* 22-JUN-2012 (DSB): +* - Check for distorted TAN projections that have zero for all PVi_m +* coefficients. Issue a warning and ignore the distortion in such +* cases. +* - Remove all set but unused variables. +* - Convert SAO distorted TAN projections (which use COi_j keywords +* for polynomial coeffs) to TPN. +* 26-JUN-2012 (DSB): +* Correct call to astKeyFields in SAOTrans (thanks to Bill Joye +* for pointing out this error). +* 8-AUG-2012 (DSB): +* Correct assignment to lonpole within CLASSFromStore. +* 10-AUG-2012 (DSB): +* Default DSS keywords CNPIX1 and CNPIX2 to zero if they are +* absent, rather than reporting an error. +* 7-DEC-2012 (DSB): +* - When writing out a FrameSet that uses an SkyFrame to describe a +* generalised spherical coordinate system ("system=unknown"), ensure +* that the generated FITS CTYPE values use FITS-compliant codes +* for the axis type ( "xxLN/xxLT" or "xLON/xLAT" ). +* - Add support for reading and writing offset SkyFrames to +* FITS-WCS. +* 30-JAN-2013 (DSB): +* When reading a FITS-CLASS header, use "VLSR" keyword if +* "VELO-..." is not available. +* 15-APR-2013 (DSB): +* Correct initialisation of missing coefficients When reading a +* SAO plate solution header. +* 16-APR-2013 (DSB): +* When determining default Encoding value, use "VLSR" keyword if +* "VELO-..." is not available. +* 30-MAY-2013 (DSB): +* Prevent seg fault caused by overrunning the coeffs array in +* WATCoeffs in cases where the TNX/ZPX projection is found to be +* of a form that cannot be implemented as a TPN projection. +* 11-JUN-2013 (DSB): +* Fix support for reading GLS projections, and add support for +* rotated GLS projections. +* 28-AUG-2013 (DSB): +* In WcsCelestial, if celestial axes are found with no projection +* code in CTYPE, assume an old-fashioned CAR projection (i.e. no +* rotation from native to WCS coords). Before this change, +* CTYPE = "RA" | "DEC" axes got treated as radians, not degrees. +* 16-SEP-2013 (DSB): +* When exporting alternate offset SkyFrames to FITS-WCS headers, +* correctly test the alternate Frame in the supplied FrameSet, rather +* than the current Frame. +* 24-SEP-2013 (DSB): +* Fix bug in choosing default value for PolyTan attribute. +* 19-OCT-2013 (DSB): +* - In SIPMapping, always ignore any inverse polynomial supplied in +* a SIP header as they seem often to be inaccurate. A new inverse is +* created to replace it. +* - In SIPMapping, only use a fit to the inverted SIP transformation +* if an accuracy of 0.01 pixel can be achieved over an area three +* times the dimensions of the image. Otherwise use an iterative +* inverse for each point. People were seeing bad round-trip errors +* when transforming points outside the image because the fit was +* being used when it was not very accurate. +* 12-NOV-2013 (DSB): +* Added CardName and CardComm attributes. +* 13-NOV-2013 (DSB): +* Use a zero-length string for the CardComm attribute if the card +* has no comment. +* 15-NOV-2013 (DSB): +* - Added method astShowFits. +* - Ensure PurgeWcs removes WCS cards even if an error occurs when +* reading FrameSets from the FitsChan. +* - Change IsMapTab1D to improve chances of a -TAB mapping being found. +* 6-JAN-2014 (DSB): +* - Allow default options for newly created FitsChans to be +* specified by the FITSCHAN_OPTIONS environment variable. +* - Ensure the used CarLin value is not changed by a trailing frequency axis. +* 9-JUL-2014 (DSB): +* Added attribute FitsAxisOrder, which allows an order to be +* specified for WCS axis within FITS headers generated using astWrite. +* 9-SEP-2014 (DSB): +* Modify Split so that any non-printing characters such as +* newlines at the end of the string are ignored. +* 30-SEP-2014 (DSB): +* Modify CnvType to indicate that comment cards cannot be +* converted to any other data type. For instance, this causes +* a warning to be issued if an equals sign is misplaced in a +* WCS-related card (causing it to be treated as a comment card). +* 24-MAR-2015 (DSB): +* Modify SpecTrans to avoid modifying the CRPIC value for CAR +* projections when they do not need to be modified. The princiuple +* is that the bulk of the array should be witin the native longitude +* range [-180,+180]. Prompted by bug report from Bill Joye "yet +* another CAR issue" on 24-MAR-2015 (file CHIPASS_Equ.head in +* ast_tester). +* 27-APR-2015 (DSB): +* Modify MakeFitsFrameSet so that isolated SkyAxes (e.g. +* individual axes that have been oicked from a SkyFrame) are +* re-mapped into degrees before being used. +* 20-APR-2015 (DSB): +* In MakeIntWorld, relax tolerance for checking that each FITS-WCS IWC +* axis is linear, from 0.01 of a pixel to 0.1 of a pixel. +* 6-JUL-2015 (DSB): +* When checking a sub-string, ensure the whole string is at least as +* long as the offset to the start of the sub-string. Without this, you +* can get erroneous sub-string matches by chance, depending on what +* characters happen to be present in memory after the end of the string. +* 11-AUG-2015 (DSB): +* - Fix bug in CheckFitsName that prevented an error from being reported +* if the FITS keyword name contained any illegal printable characters. +* - Add new Warning "badkeyname", and issue such a warning instead +* of an error if illegal characters are found in a keyword name. +* 31-AUG-2015 (DSB): +* In FitLine, use the whole axis rather than 0.1 of the axis (if "dim" +* is supplied). This is because non-linearity can become greater at +* further distances along the axis. In practice, it meant that SIP +* distortion were being treated as linear because the test did not +* explore a large enough region of pixel space. +* 12-OCT-2015 (DSB): +* Only add sky axes to a SpecFrame if the WCS Frame contains no +* other axes other than the SpecFrame (MakeFitsFrameSet). +* 17-OCT-2015 (DSB): +* - Add new Warning "badkeyvalue", and issue such a warning instead +* of an error if the Split function cannot determine the keyword value. +* - Move the check for PLTRAH (i.e. DSS encoding) higher up in GetEncoding. +* This is because some DSS file slaos have CD and/or PC keywords. +* 5-NOV-2015 (DSB): +* Fix bug in MakeFitsFrameSet that could cause an inappropriate +* RefRA and RefDec values to be used when writing out a SpecFrame +* using FITS-WCS. This bug was caused by the assumption that +* changing the current Frame of a FRameSet also changes the Frame +* that was added into the FRameSet. This used to be the case as +* astAddFrame took a clone of the supplied Frame pointer, but it +* now takes a deep copy, so the original Frame and the FrameSet's +* current Frame are now independent of each other. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ + +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS FitsChan + +/* A macro which tests a character to see if it can be used within a FITS + keyword. We include lower case letters here, but they are considered + as equivalent to upper case letter. */ +#define isFits(a) ( islower(a) || isupper(a) || isdigit(a) || (a)=='-' || (a)=='_' ) + +/* A amacro to test if a Frame is a SkyFrame, and is used to describe the + sky (skyframes could be used for other purposes - eg a POLANAL Frame). */ +#define IsASkyFrame(frame) (astIsASkyFrame(frame)&&!strcmp("SKY",astGetDomain(frame))) + +/* Macro which takes a pointer to a FitsCard and returns non-zero if the + card has been used and so should be ignored. */ +#define CARDUSED(card) ( \ + ( ignore_used == 2 && \ + ( (FitsCard *) (card) )->flags & PROVISIONALLY_USED ) || \ + ( ignore_used >= 1 && \ + ( (FitsCard *) (card) )->flags & USED ) ) + +/* Set of characters used to encode a "sequence number" at the end of + FITS keywords in an attempt to make them unique.. */ +#define SEQ_CHARS "_ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +/* A general tolerance for equality between floating point values. */ +#define TOL1 10.0*DBL_EPSILON + +/* A tolerance for equality between angular values in radians. */ +#define TOL2 1.0E-10 + +/* Macro to check for equality of floating point angular values. We cannot + compare bad values directory because of the danger of floating point + exceptions, so bad values are dealt with explicitly. The smallest + significant angle is assumed to be 1E-9 radians (0.0002 arc-seconds).*/ +#define EQUALANG(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=astMAX(1.0E5*(fabs(aa)+fabs(bb))*DBL_EPSILON,1.0E-9)))) + +/* Macro to compare an angle in radians with zero, allowing some tolerance. */ +#define ZEROANG(aa) (fabs(aa)<1.0E-9) + +/* Constants: */ +#define UNKNOWN_ENCODING -1 +#define NATIVE_ENCODING 0 +#define FITSPC_ENCODING 1 +#define DSS_ENCODING 2 +#define FITSWCS_ENCODING 3 +#define FITSIRAF_ENCODING 4 +#define FITSAIPS_ENCODING 5 +#define FITSAIPSPP_ENCODING 6 +#define FITSCLASS_ENCODING 7 +#define MAX_ENCODING 7 +#define UNKNOWN_STRING "UNKNOWN" +#define NATIVE_STRING "NATIVE" +#define FITSPC_STRING "FITS-PC" +#define FITSPC_STRING2 "FITS_PC" +#define DSS_STRING "DSS" +#define FITSWCS_STRING "FITS-WCS" +#define FITSWCS_STRING2 "FITS_WCS" +#define FITSIRAF_STRING "FITS-IRAF" +#define FITSIRAF_STRING2 "FITS_IRAF" +#define FITSAIPS_STRING "FITS-AIPS" +#define FITSAIPS_STRING2 "FITS_AIPS" +#define FITSAIPSPP_STRING "FITS-AIPS++" +#define FITSAIPSPP_STRING2 "FITS_AIPS++" +#define FITSCLASS_STRING "FITS-CLASS" +#define FITSCLASS_STRING2 "FITS_CLASS" +#define INDENT_INC 3 +#define PREVIOUS 0 +#define NEXT 1 +#define HEADER_TEXT "Beginning of AST data for " +#define FOOTER_TEXT "End of AST data for " +#define FITSNAMLEN 8 +#define FITSSTCOL 20 +#define FITSRLCOL 30 +#define FITSIMCOL 50 +#define FITSCOMCOL 32 +#define NORADEC 0 +#define FK4 1 +#define FK4NOE 2 +#define FK5 3 +#define GAPPT 4 +#define ICRS 5 +#define NOCEL 0 +#define RADEC 1 +#define ECLIP 2 +#define GALAC 3 +#define SUPER 4 +#define HECLIP 5 +#define AZEL 6 +#define LONAX -1 +#define NONAX 0 +#define LATAX 1 +#define NDESC 9 +#define MXCTYPELEN 81 +#define ALLWARNINGS " distortion noequinox noradesys nomjd-obs nolonpole nolatpole tnx zpx badcel noctype badlat badmat badval badctype badpv badkeyname badkeyvalue " +#define NPFIT 10 +#define SPD 86400.0 +#define FL 1.0/298.257 /* Reference spheroid flattening factor */ +#define A0 6378140.0 /* Earth equatorial radius (metres) */ + +/* String used to represent AST__BAD externally. */ +#define BAD_STRING "" + +/* Each card in the fitschan has a set of flags associated with it, + stored in different bits of the "flags" item within each FitsCard + structure (note, in AST V1.4 these flags were stored in the "del" + item... Dump and LoadFitsChan will need to be changed to use a + correspondingly changed name for the external representation of this + item). The following flags are currently defined: */ + +/* "USED" - This flag indicates that the the card has been used in the + construction of an AST Object returned by astRead. Such cards should + usually be treated as if they do not exist, i.e. they should not be + used again by subsequent calls to astRead, they should not be recognised + by public FitsChan methods which search the FitsChan for specified + cards, and they should not be written out when the FitsChan is deleted. + This flag was the only flag available in AST V1.4, and was called + "Del" (for "deleted"). Used cards are retained in order to give an + indication of where abouts within the header new cards should be placed + when astWrite is called (i.e. new cards should usually be placed at + the same point within the header as the cards which they replace). */ +#define USED 1 + +/* "PROVISIONALLY_USED" - This flag indicates that the the card is being + considered as a candidate for inclusion in the construction of an AST + Object. If the Object is constructed succesfully, cards flagged as + "provisionally used" will be changed to be flagged as "definitely used" + (using the USED flag). If the Object fails to be constructed + succesfully (if some required cards are missing from the FitsChan + for instance), then "provisionally used" cards will be returned to the + former state which they had prior to the attempt to construct the + object. */ +#define PROVISIONALLY_USED 2 + +/* "NEW" - This flag indicates that the the card has just been added to + the FitsChan and may yet proove to be unrequired. For instance if the + supplied Object is not of an appropriate flavour to be stored using + the requested encoding, all "new" cards which were added before the + inappropriateness was discovered will be removed from the FitsChan. + Two different levels of "newness" are available. */ +#define NEW1 4 +#define NEW2 8 + +/* "PROTECTED" - This flag indicates that the the card should not be + removed form the FitsChan when an Object is read using astRead. If + this flag is not set, then the card will dehave as if it has been + deleted if it was used in the construction of the returned AST Object. */ +#define PROTECTED 16 + +/* Include files. */ +/* ============== */ + +/* Interface definitions. */ +/* ---------------------- */ +#include "channel.h" +#include "cmpframe.h" +#include "cmpmap.h" +#include "dssmap.h" +#include "error.h" +#include "fitschan.h" +#include "frame.h" +#include "frameset.h" +#include "grismmap.h" +#include "lutmap.h" +#include "mathmap.h" +#include "matrixmap.h" +#include "memory.h" +#include "object.h" +#include "permmap.h" +#include "pointset.h" +#include "shiftmap.h" +#include "skyframe.h" +#include "timeframe.h" +#include "keymap.h" +#include "pal.h" +#include "erfa.h" +#include "slamap.h" +#include "specframe.h" +#include "dsbspecframe.h" +#include "specmap.h" +#include "sphmap.h" +#include "unit.h" +#include "unitmap.h" +#include "polymap.h" +#include "wcsmap.h" +#include "winmap.h" +#include "zoommap.h" +#include "globals.h" +#include "fitstable.h" + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Type Definitions */ +/* ================ */ + +/* This structure contains information describing a single FITS header card + in a circular list of such structures. */ +typedef struct FitsCard { + char name[ FITSNAMLEN + 1 ];/* Keyword name (plus terminating null). */ + int type; /* Data type. */ + void *data; /* Pointer to the keyword's data value. */ + char *comment; /* Pointer to a comment for the keyword. */ + int flags; /* Flags for each card */ + size_t size; /* Size of data value */ + struct FitsCard *next; /* Pointer to next structure in list. */ + struct FitsCard *prev; /* Pointer to previous structure in list. */ +} FitsCard; + +/* Structure used to store information derived from the FITS WCS keyword + values in a form more convenient to further processing. Conventions + for units, etc, for values in a FitsStore follow FITS-WCS (e.g. angular + values are stored in degrees, equinox is B or J depending on RADECSYS, + etc). */ +typedef struct FitsStore { + char ****cname; + char ****ctype; + char ****ctype_com; + char ****cunit; + char ****radesys; + char ****wcsname; + char ****specsys; + char ****ssyssrc; + char ****ps; + char ****timesys; + double ***pc; + double ***cdelt; + double ***crpix; + double ***crval; + double ***equinox; + double ***latpole; + double ***lonpole; + double ***mjdobs; + double ***dut1; + double ***mjdavg; + double ***pv; + double ***wcsaxes; + double ***obsgeox; + double ***obsgeoy; + double ***obsgeoz; + double ***restfrq; + double ***restwav; + double ***zsource; + double ***velosys; + double ***asip; + double ***bsip; + double ***apsip; + double ***bpsip; + double ***imagfreq; + double ***axref; + int naxis; + AstKeyMap *tables; + double ***skyref; + double ***skyrefp; + char ****skyrefis; +} FitsStore; + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static void (* parent_setsourcefile)( AstChannel *, const char *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_getfull)( AstChannel *, int * ); +static int (* parent_getskip)( AstChannel *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static int (* parent_write)( AstChannel *, AstObject *, int * ); +static AstObject *(* parent_read)( AstChannel *, int * ); +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Strings to describe each data type. These should be in the order implied + by the corresponding macros (eg AST__FLOAT, etc). */ +static const char *type_names[9] = {"comment", "integer", "floating point", + "string", "complex floating point", + "complex integer", "logical", + "continuation string", "undef" }; + +/* Text values used to represent Encoding values externally. */ + +static const char *xencod[8] = { NATIVE_STRING, FITSPC_STRING, + DSS_STRING, FITSWCS_STRING, + FITSIRAF_STRING, FITSAIPS_STRING, + FITSAIPSPP_STRING, FITSCLASS_STRING }; +/* Define two variables to hold TimeFrames which will be used for converting + MJD values between time scales. */ +static AstTimeFrame *tdbframe = NULL; +static AstTimeFrame *timeframe = NULL; + +/* Max number of characters in a formatted int */ +static int int_dig; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->Items_Written = 0; \ + globals->Write_Nest = -1; \ + globals->Current_Indent = 0; \ + globals->Ignore_Used = 1; \ + globals->Mark_New = 0; \ + globals->CnvType_Text[ 0 ] = 0; \ + globals->CnvType_Text0[ 0 ] = 0; \ + globals->CnvType_Text1[ 0 ] = 0; \ + globals->CreateKeyword_Seq_Nchars = -1; \ + globals->FormatKey_Buff[ 0 ] = 0; \ + globals->FitsGetCom_Sval[ 0 ] = 0; \ + globals->IsSpectral_Ret = NULL; \ + globals->Match_Fmt[ 0 ] = 0; \ + globals->Match_Template = NULL; \ + globals->Match_PA = 0; \ + globals->Match_PB = 0; \ + globals->Match_NA = 0; \ + globals->Match_NB = 0; \ + globals->Match_Nentry = 0; \ + globals->WcsCelestial_Type[ 0 ] = 0; \ + globals->Ignore_Used = 1; \ + globals->Mark_New = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(FitsChan) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(FitsChan,Class_Init) +#define class_vtab astGLOBAL(FitsChan,Class_Vtab) +#define getattrib_buff astGLOBAL(FitsChan,GetAttrib_Buff) +#define items_written astGLOBAL(FitsChan,Items_Written) +#define write_nest astGLOBAL(FitsChan,Write_Nest) +#define current_indent astGLOBAL(FitsChan,Current_Indent) +#define ignore_used astGLOBAL(FitsChan,Ignore_Used) +#define mark_new astGLOBAL(FitsChan,Mark_New) +#define cnvtype_text astGLOBAL(FitsChan,CnvType_Text) +#define cnvtype_text0 astGLOBAL(FitsChan,CnvType_Text0) +#define cnvtype_text1 astGLOBAL(FitsChan,CnvType_Text1) +#define createkeyword_seq_nchars astGLOBAL(FitsChan,CreateKeyword_Seq_Nchars) +#define formatkey_buff astGLOBAL(FitsChan,FormatKey_Buff) +#define fitsgetcom_sval astGLOBAL(FitsChan,FitsGetCom_Sval) +#define isspectral_ret astGLOBAL(FitsChan,IsSpectral_Ret) +#define match_fmt astGLOBAL(FitsChan,Match_Fmt) +#define match_template astGLOBAL(FitsChan,Match_Template) +#define match_pa astGLOBAL(FitsChan,Match_PA) +#define match_pb astGLOBAL(FitsChan,Match_PB) +#define match_na astGLOBAL(FitsChan,Match_NA) +#define match_nb astGLOBAL(FitsChan,Match_NB) +#define match_nentry astGLOBAL(FitsChan,Match_Nentry) +#define wcscelestial_type astGLOBAL(FitsChan,WcsCelestial_Type) +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); +static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 ); +#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 ); +static pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX4 pthread_mutex_lock( &mutex4 ); +#define UNLOCK_MUTEX4 pthread_mutex_unlock( &mutex4 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ AST__FITSCHAN_GETATTRIB_BUFF_LEN + 1 ]; + +/* Buffer for returned text string in CnvType */ +static char cnvtype_text[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + +/* Buffer for real value in CnvType */ +static char cnvtype_text0[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + +/* Buffer for imaginary value in CnvType */ +static char cnvtype_text1[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + +/* Number of output items written since the last "Begin" or "IsA" + output item, and level of Object nesting during recursive + invocation of the astWrite method. */ +static int items_written = 0; +static int write_nest = -1; + +/* Indentation level for indented comments when writing Objects to a + FitsChan. */ +static int current_indent = 0; + +/* Ignore_Used: If 2, then cards which have been marked as either "definitely + used" or "provisionally used" (see the USED flag above) will be ignored + when searching the FitsChan, etc (i.e. they will be treated as if they + have been removed from the FitsChan). If 1, then cards which have been + "definitely used" will be skipped over. If zero then no cards will be + skipped over. */ +static int ignore_used = 1; + +/* Mark_New: If non-zero, then all cards added to the FitsChan will be + marked with both the NEW1 and NEW2 flags (see above). If zero then + new cards will not be marked with either NEW1 or NEW2. */ +static int mark_new = 0; + +/* Number of characters used for encoding */ +static int createkeyword_seq_nchars = -1; + +/* Buffer for value returned by FormatKey */ +static char formatkey_buff[ 10 ]; + +/* Buffer for value returned by FitsGetCom */ +static char fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + +/* Pointer returned by IsSpectral */ +static const char *isspectral_ret = NULL; + +/* Format specifier for reading an integer field in Match */ +static char match_fmt[ 10 ]; + +/* Pointer to start of template in Match */ +static const char *match_template = NULL; + +/* Pointer to first returned field value in Match */ +static int *match_pa = 0; + +/* Pointer to last returned field value in Match */ +static int *match_pb = 0; + +/* No. of characters read from the test string in Match */ +static int match_na = 0; + +/* No. of characters read from the template string in Match */ +static int match_nb = 0; + +/* Number of recursive entries into Match */ +static int match_nentry = 0; + +/* Buffer for celestial system in WcsCelestial */ +static char wcscelestial_type[ 4 ]; + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstFitsChanVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 +#define LOCK_MUTEX3 +#define UNLOCK_MUTEX3 +#define LOCK_MUTEX4 +#define UNLOCK_MUTEX4 +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ + +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstFitsChan *astFitsChanForId_( const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), const char *, int * ), + const char *, ... ); +AstFitsChan *astFitsChanId_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static int GetObjSize( AstObject *, int * ); +static void ClearCard( AstFitsChan *, int * ); +static int GetCard( AstFitsChan *, int * ); +static int TestCard( AstFitsChan *, int * ); +static void SetCard( AstFitsChan *, int, int * ); +static void ClearEncoding( AstFitsChan *, int * ); +static int GetEncoding( AstFitsChan *, int * ); +static int TestEncoding( AstFitsChan *, int * ); +static void SetEncoding( AstFitsChan *, int, int * ); +static void ClearCDMatrix( AstFitsChan *, int * ); +static int GetCDMatrix( AstFitsChan *, int * ); +static int TestCDMatrix( AstFitsChan *, int * ); +static void SetCDMatrix( AstFitsChan *, int, int * ); +static void ClearFitsDigits( AstFitsChan *, int * ); +static int GetFitsDigits( AstFitsChan *, int * ); +static int TestFitsDigits( AstFitsChan *, int * ); +static void SetFitsDigits( AstFitsChan *, int, int * ); +static void ClearFitsAxisOrder( AstFitsChan *, int * ); +static const char *GetFitsAxisOrder( AstFitsChan *, int * ); +static int TestFitsAxisOrder( AstFitsChan *, int * ); +static void SetFitsAxisOrder( AstFitsChan *, const char *, int * ); +static void ClearDefB1950( AstFitsChan *, int * ); +static int GetDefB1950( AstFitsChan *, int * ); +static int TestDefB1950( AstFitsChan *, int * ); +static void SetDefB1950( AstFitsChan *, int, int * ); +static void ClearTabOK( AstFitsChan *, int * ); +static int GetTabOK( AstFitsChan *, int * ); +static int TestTabOK( AstFitsChan *, int * ); +static void SetTabOK( AstFitsChan *, int, int * ); +static void ClearCarLin( AstFitsChan *, int * ); +static int GetCarLin( AstFitsChan *, int * ); +static int TestCarLin( AstFitsChan *, int * ); +static void SetCarLin( AstFitsChan *, int, int * ); +static void ClearPolyTan( AstFitsChan *, int * ); +static int GetPolyTan( AstFitsChan *, int * ); +static int TestPolyTan( AstFitsChan *, int * ); +static void SetPolyTan( AstFitsChan *, int, int * ); +static void ClearIwc( AstFitsChan *, int * ); +static int GetIwc( AstFitsChan *, int * ); +static int TestIwc( AstFitsChan *, int * ); +static void SetIwc( AstFitsChan *, int, int * ); +static void ClearClean( AstFitsChan *, int * ); +static int GetClean( AstFitsChan *, int * ); +static int TestClean( AstFitsChan *, int * ); +static void SetClean( AstFitsChan *, int, int * ); +static void ClearWarnings( AstFitsChan *, int * ); +static const char *GetWarnings( AstFitsChan *, int * ); +static int TestWarnings( AstFitsChan *, int * ); +static void SetWarnings( AstFitsChan *, const char *, int * ); + +static AstFitsChan *SpecTrans( AstFitsChan *, int, const char *, const char *, int * ); +static AstFitsTable *GetNamedTable( AstFitsChan *, const char *, int, int, int, const char *, int * ); +static AstFrameSet *MakeFitsFrameSet( AstFitsChan *, AstFrameSet *, int, int, int, const char *, const char *, int * ); +static AstGrismMap *ExtractGrismMap( AstMapping *, int, AstMapping **, int * ); +static AstKeyMap *GetTables( AstFitsChan *, int * ); +static AstMapping *AddUnitMaps( AstMapping *, int, int, int * ); +static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *, double *, int *, char, FitsStore *, int *, int, const char *, const char *, int * ); +static AstMapping *GrismSpecWcs( char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * ); +static AstMapping *IsMapTab1D( AstMapping *, double, const char *, AstFrame *, double *, int, int, AstFitsTable **, int *, int *, int *, int * ); +static AstMapping *IsMapTab2D( AstMapping *, double, const char *, AstFrame *, double *, int, int, int, int, AstFitsTable **, int *, int *, int *, int *, int *, int *, int *, int *, int * ); +static AstMapping *LinearWcs( FitsStore *, int, char, const char *, const char *, int * ); +static AstMapping *LogAxis( AstMapping *, int, int, double *, double *, double, int * ); +static AstMapping *LogWcs( FitsStore *, int, char, const char *, const char *, int * ); +static AstMapping *MakeColumnMap( AstFitsTable *, const char *, int, int, const char *, const char *, int * ); +static AstMapping *NonLinSpecWcs( AstFitsChan *, char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * ); +static AstMapping *OtherAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * ); +static AstMapping *SIPMapping( double *, FitsStore *, char, int, const char *, const char *, int * ); +static AstMapping *SpectralAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * ); +static AstMapping *TabMapping( AstFitsChan *, FitsStore *, char, int **, const char *, const char *, int *); +static AstMapping *WcsCelestial( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double *, double *, AstSkyFrame **, AstMapping **, int *, const char *, const char *, int * ); +static AstMapping *WcsIntWorld( AstFitsChan *, FitsStore *, char, int, const char *, const char *, int * ); +static AstMapping *WcsMapFrm( AstFitsChan *, FitsStore *, char, AstFrame **, const char *, const char *, int * ); +static AstMapping *WcsNative( AstFitsChan *, FitsStore *, char, AstWcsMap *, int, int, const char *, const char *, int * ); +static AstMapping *WcsOthers( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, const char *, const char *, int * ); +static AstMapping *WcsSpectral( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double, double, AstSkyFrame *, const char *, const char *, int * ); +static AstMapping *ZPXMapping( AstFitsChan *, FitsStore *, char, int, int[2], const char *, const char *, int * ); +static AstMatrixMap *WcsCDeltMatrix( FitsStore *, char, int, const char *, const char *, int * ); +static AstMatrixMap *WcsPCMatrix( FitsStore *, char, int, const char *, const char *, int * ); +static AstObject *FsetFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static AstObject *Read( AstChannel *, int * ); +static AstSkyFrame *WcsSkyFrame( AstFitsChan *, FitsStore *, char, int, char *, int, int, const char *, const char *, int * ); +static AstTimeScaleType TimeSysToAst( AstFitsChan *, const char *, const char *, const char *, int * ); +static AstWinMap *WcsShift( FitsStore *, char, int, const char *, const char *, int * ); +static FitsCard *GetLink( FitsCard *, int, const char *, const char *, int * ); +static FitsStore *FitsToStore( AstFitsChan *, int, const char *, const char *, int * ); +static FitsStore *FreeStore( FitsStore *, int * ); +static FitsStore *FsetToStore( AstFitsChan *, AstFrameSet *, int, double *, int, const char *, const char *, int * ); +static char *CardComm( AstFitsChan *, int * ); +static char *CardName( AstFitsChan *, int * ); +static char *ConcatWAT( AstFitsChan *, int, const char *, const char *, int * ); +static char *FormatKey( const char *, int, int, char, int * ); +static char *GetItemC( char *****, int, int, char, char *, const char *method, const char *class, int * ); +static char *SourceWrap( const char *(*)( void ), int * ); +static char *UnPreQuote( const char *, int * ); +static char GetMaxS( double ****item, int * ); +static const char *GetAllWarnings( AstFitsChan *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetCardComm( AstFitsChan *, int * ); +static const char *GetCardName( AstFitsChan *, int * ); +static const char *GetFitsSor( const char *, int * ); +static const char *IsSpectral( const char *, char[5], char[5], int * ); +static double **OrthVectorSet( int, int, double **, int * ); +static double *Cheb2Poly( double *, int, int, double, double, double, double, int * ); +static double *FitLine( AstMapping *, double *, double *, double *, double, double *, int * ); +static double *OrthVector( int, int, double **, int * ); +static double *ReadCrval( AstFitsChan *, AstFrame *, char, const char *, const char *, int * ); +static double ChooseEpoch( AstFitsChan *, FitsStore *, char, const char *, const char *, int * ); +static double DateObs( const char *, int * ); +static double GetItem( double ****, int, int, char, char *, const char *method, const char *class, int * ); +static double NearestPix( AstMapping *, double, int, int * ); +static double TDBConv( double, int, int, const char *, const char *, int * ); +static int *CardFlags( AstFitsChan *, int * ); +static int AIPSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static int AIPSPPFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static int AddEncodingFrame( AstFitsChan *, AstFrameSet *, int, const char *, const char *, int * ); +static int AddVersion( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, double *, char, int, int, const char *, const char *, int * ); +static int CLASSFromStore( AstFitsChan *, FitsStore *, AstFrameSet *, double *, const char *, const char *, int * ); +static int CardType( AstFitsChan *, int * ); +static int CheckFitsName( AstFitsChan *, const char *, const char *, const char *, int * ); +static int ChrLen( const char *, int * ); +static int CnvType( int, void *, size_t, int, int, void *, const char *, const char *, const char *, int * ); +static int CnvValue( AstFitsChan *, int , int, void *, const char *, int * ); +static int ComBlock( AstFitsChan *, int, const char *, const char *, int * ); +static int CountFields( const char *, char, const char *, const char *, int * ); +static int DSSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static int EncodeFloat( char *, int, int, int, double, int * ); +static int EncodeValue( AstFitsChan *, char *, int, int, const char *, int * ); +static int FindBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * ); +static int FindFits( AstFitsChan *, const char *, char[ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * ); +static int FindKeyCard( AstFitsChan *, const char *, const char *, const char *, int * ); +static int FindLonLatSpecAxes( FitsStore *, char, int *, int *, int *, const char *, const char *, int * ); +static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * ); +static int FitOK( int, double *, double *, double, int * ); +static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, int *perm, int *status ); +static int FitsEof( AstFitsChan *, int * ); +static int FitsFromStore( AstFitsChan *, FitsStore *, int, double *, AstFrameSet *, const char *, const char *, int * ); +static int FitsGetCom( AstFitsChan *, const char *, char **, int * ); +static int FitsSof( AstFitsChan *, int * ); +static int FullForm( const char *, const char *, int, int * ); +static int GetCardType( AstFitsChan *, int * ); +static int GetFiducialWCS( AstWcsMap *, AstMapping *, int, int, double *, double *, int * ); +static int GetFitsCF( AstFitsChan *, const char *, double *, int * ); +static int GetFitsCI( AstFitsChan *, const char *, int *, int * ); +static int GetFitsCN( AstFitsChan *, const char *, char **, int * ); +static int GetFitsF( AstFitsChan *, const char *, double *, int * ); +static int GetFitsI( AstFitsChan *, const char *, int *, int * ); +static int GetFitsL( AstFitsChan *, const char *, int *, int * ); +static int GetFitsS( AstFitsChan *, const char *, char **, int * ); +static int GetFull( AstChannel *, int * ); +static int GetMaxI( double ****item, char, int * ); +static int GetMaxJM( double ****item, char, int * ); +static int GetMaxJMC( char *****item, char, int * ); +static int GetNcard( AstFitsChan *, int * ); +static int GetNkey( AstFitsChan *, int * ); +static int GetSkip( AstChannel *, int * ); +static int GetUsedPolyTan( AstFitsChan *, AstFitsChan *, int, int, char, const char *, const char *, int * ); +static int GetValue( AstFitsChan *, const char *, int, void *, int, int, const char *, const char *, int * ); +static int GetValue2( AstFitsChan *, AstFitsChan *, const char *, int, void *, int, const char *, const char *, int * ); +static int GoodWarns( const char *, int * ); +static int HasAIPSSpecAxis( AstFitsChan *, const char *, const char *, int * ); +static int HasCard( AstFitsChan *, const char *, const char *, const char *, int * ); +static int IRAFFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static int IsAIPSSpectral( const char *, char **, char **, int * ); +static int IsMapLinear( AstMapping *, const double [], const double [], int, int * ); +static int IsSkyOff( AstFrameSet *, int, int * ); +static int KeyFields( AstFitsChan *, const char *, int, int *, int *, int * ); +static int LooksLikeClass( AstFitsChan *, const char *, const char *, int * ); +static int MakeBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * ); +static int MakeIntWorld( AstMapping *, AstFrame *, int *, char, FitsStore *, double *, const char *, const char *, int * ); +static int Match( const char *, const char *, int, int *, int *, const char *, const char *, int * ); +static int MatchChar( char, char, const char *, const char *, const char *, int * ); +static int MatchFront( const char *, const char *, char *, int *, int *, int *, const char *, const char *, const char *, int * ); +static int MoveCard( AstFitsChan *, int, const char *, const char *, int * ); +static int PCFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static int SAOTrans( AstFitsChan *, AstFitsChan *, const char *, const char *, int * ); +static int SearchCard( AstFitsChan *, const char *, const char *, const char *, int * ); +static int SetFits( AstFitsChan *, const char *, void *, int, const char *, int, int * ); +static int Similar( const char *, const char *, int * ); +static int SkySys( AstFitsChan *, AstSkyFrame *, int, int, FitsStore *, int, int, char c, int, const char *, const char *, int * ); +static int Split( AstFitsChan *, const char *, char **, char **, char **, const char *, const char *, int * ); +static int SplitMap( AstMapping *, int, int, int, AstMapping **, AstWcsMap **, AstMapping **, int * ); +static int SplitMap2( AstMapping *, int, AstMapping **, AstWcsMap **, AstMapping **, int * ); +static int SplitMat( int , double *, double *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestFits( AstFitsChan *, const char *, int *, int * ); +static int Use( AstFitsChan *, int, int, int * ); +static int Ustrcmp( const char *, const char *, int * ); +static int Ustrncmp( const char *, const char *, size_t, int * ); +static int WATCoeffs( const char *, int, double **, int **, int *, int * ); +static int WcsFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static int WcsNatPole( AstFitsChan *, AstWcsMap *, double, double, double, double *, double *, double *, int * ); +static int WorldAxes( AstFitsChan *this, AstMapping *, double *, int *, int * ); +static int Write( AstChannel *, AstObject *, int * ); +static void *CardData( AstFitsChan *, size_t *, int * ); +static void AdaptLut( AstMapping *, int, double, double, double, double, double, double **, double **, int *, int * ); +static void AddFrame( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, char, const char *, const char *, int * ); +static void ChangePermSplit( AstMapping *, int * ); +static void CheckZero( char *, double, int, int * ); +static void Chpc1( double *, double *, int, int *, int *, int * ); +static void ClassTrans( AstFitsChan *, AstFitsChan *, int, int, const char *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void CreateKeyword( AstFitsChan *, const char *, char [ FITSNAMLEN + 1 ], int * ); +static void DSBSetUp( AstFitsChan *, FitsStore *, AstDSBSpecFrame *, char, double, const char *, const char *, int * ); +static void DSSToStore( AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static void DelFits( AstFitsChan *, int * ); +static void Delete( AstObject *, int * ); +static void DeleteCard( AstFitsChan *, const char *, const char *, int * ); +static void DistortMaps( AstFitsChan *, FitsStore *, char, int , AstMapping **, AstMapping **, AstMapping **, AstMapping **, const char *, const char *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void EmptyFits( AstFitsChan *, int * ); +static void FindWcs( AstFitsChan *, int, int, int, const char *, const char *, int * ); +static void FixNew( AstFitsChan *, int, int, const char *, const char *, int * ); +static void FixUsed( AstFitsChan *, int, int, int, const char *, const char *, int * ); +static void FormatCard( AstFitsChan *, char *, const char *, int * ); +static void FreeItem( double ****, int * ); +static void FreeItemC( char *****, int * ); +static void GetFiducialNSC( AstWcsMap *, double *, double *, int * ); +static void GetFiducialPPC( AstWcsMap *, double *, double *, int * ); +static void GetNextData( AstChannel *, int, char **, char **, int * ); +static void InsCard( AstFitsChan *, int, const char *, int, void *, const char *, const char *, const char *, int * ); +static void MakeBanner( const char *, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int * ); +static void MakeIndentedComment( int, char, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1], int * ); +static void MakeIntoComment( AstFitsChan *, const char *, const char *, int * ); +static void MakeInvertable( double **, int, double *, int * ); +static void MarkCard( AstFitsChan *, int * ); +static void NewCard( AstFitsChan *, const char *, int, const void *, const char *, int, int * ); +static void PreQuote( const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int * ); +static void PurgeWCS( AstFitsChan *, int * ); +static void PutCards( AstFitsChan *, const char *, int * ); +static void PutFits( AstFitsChan *, const char [ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * ); +static void PutTable( AstFitsChan *, AstFitsTable *, const char *, int * ); +static void PutTables( AstFitsChan *, AstKeyMap *, int * ); +static void ReadFits( AstFitsChan *, int * ); +static void ReadFromSource( AstFitsChan *, int * ); +static void RemoveTables( AstFitsChan *, const char *, int * ); +static void RetainFits( AstFitsChan *, int * ); +static void RoundFString( char *, int, int * ); +static void SetAlgCode( char *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetFitsCF( AstFitsChan *, const char *, double *, const char *, int, int * ); +static void SetFitsCI( AstFitsChan *, const char *, int *, const char *, int, int * ); +static void SetFitsCM( AstFitsChan *, const char *, int, int * ); +static void SetFitsCN( AstFitsChan *, const char *, const char *, const char *, int, int * ); +static void SetFitsCom( AstFitsChan *, const char *, const char *, int, int * ); +static void SetFitsF( AstFitsChan *, const char *, double, const char *, int, int * ); +static void SetFitsI( AstFitsChan *, const char *, int, const char *, int, int * ); +static void SetFitsL( AstFitsChan *, const char *, int, const char *, int, int * ); +static void SetFitsS( AstFitsChan *, const char *, const char *, const char *, int, int * ); +static void SetFitsU( AstFitsChan *, const char *, const char *, int, int * ); +static void SetItem( double ****, int, int, char, double, int * ); +static void SetItemC( char *****, int, int, char, const char *, int * ); +static void SetSourceFile( AstChannel *, const char *, int * ); +static void SetValue( AstFitsChan *, const char *, void *, int, const char *, int * ); +static void ShowFits( AstFitsChan *, int * ); +static void Shpc1( double, double, int, double *, double *, int * ); +static void SinkWrap( void (*)( const char * ), const char *, int * ); +static void SkyPole( AstWcsMap *, AstMapping *, int, int, int *, char, FitsStore *, const char *, const char *, int * ); +static void TableSource( AstFitsChan *, void (*)( AstFitsChan *, const char *, int, int, int * ), int * ); +static void TidyOffsets( AstFrameSet *, int * ); +static void Warn( AstFitsChan *, const char *, const char *, const char *, const char *, int * ); +static void WcsFcRead( AstFitsChan *, AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static void WcsToStore( AstFitsChan *, AstFitsChan *, FitsStore *, const char *, const char *, int * ); +static void WriteBegin( AstChannel *, const char *, const char *, int * ); +static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * ); +static void WriteEnd( AstChannel *, const char *, int * ); +static void WriteFits( AstFitsChan *, int * ); +static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * ); +static void WriteIsA( AstChannel *, const char *, const char *, int * ); +static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * ); +static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * ); +static void WriteToSink( AstFitsChan *, int * ); +static void SetTableSource( AstFitsChan *, + void (*)( void ), + void (*)( void (*)( void ), + AstFitsChan *, const char *, int, int, int * ), int * ); +static void TabSourceWrap( void (*)( void ), + AstFitsChan *, const char *, int, int, int * ); +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ + +static void AdaptLut( AstMapping *map, int npos, double eps, double x0, + double x1, double v0, double v1, double **xtab, + double **vtab, int *nsamp, int *status ){ +/* +* Name: +* AdaptLut + +* Purpose: +* Create a table of optimally sampled values for a Mapping. + +* Type: +* Private function. + +* Synopsis: +* void AdaptLut( AstMapping *map, int npos, double eps, double x0, +* double x1, double v0, double v1, double **xtab, +* double **vtab, int *nsamp, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function returns a look-up table holding samples of the supplied +* 1D mapping. The input values at which the samples are taken are +* returned in the "xtab" array, and the Mapping output values at +* these input values are returned in the "vtab" array. The sample +* spacing is smaller at positions where the output gradient is +* changing more rapidly (i.e. where the output is more non-linear). + +* Parameters: +* map +* Pointer to the Mapping. Should have 1 input and 1 output. +* npos +* The minimum number of samples to place within the interval to be +* sampled, excluding the two end points (which are always sampeld +* anyway). These samples are placed evenly through the [x0,x1] + interval. The interval between adjacent samples will be further +* subdivided if necessary by calling this function recursively. +* eps +* The maximum error in X (i.e. the Mapping input) allowed before +* the supplied interval is subdivided further by a recursive call +* to this function. +* x0 +* The Mapping input value at the start of the interval to be sampled. +* It is assumed that this value is already stored in (*xtab)[0] on +* entry. +* x1 +* The Mapping input value at the end of the interval to be sampled. +* v0 +* The Mapping output value at the start of the interval to be sampled. +* It is assumed that this value is already stored in (*vtab)[0] on +* entry. +* v1 +* The Mapping output value at the end of the interval to be sampled. +* xtab +* Address of a pointer to the array in which to store the Mapping +* input values at which samples were taken. The supplied pointer +* may be changed on exit to point to a larger array. New values +* are added to the end of this array. The initial size of the array +* is given by the supplied value for "*nsamp" +* vtab +* Address of a pointer to the array in which to store the Mapping +* output value at each sample. The supplied pointer may be changed +* on exit to point to a larger array. New values are added to the +* end of this array. The initial size of the array is given by the +* supplied value for "*nsamp". +* nsamp +* Address of an int holding the number of values in the "*xtab" +* and "*ytab" arrays. Updated on exit to include the new values +* added to the arrays by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The size of the returned xtab and vtab arrays. +*/ + +/* Local Variables: */ + double *vv; /* Pointer to Mapping output values */ + double *xx; /* Pointer to Mapping input values */ + double dx; /* Step between sample positions */ + double rg; /* Reciprocal of gradient of (x0,v0)->(x1,v1) line */ + double xx0; /* X at first new sample position */ + int ipos; /* Interior sample index */ + int isamp; /* Index into extended xtab and vtab arrays. */ + int subdivide; /* Subdivide each subinterval? */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Allocate work space. */ + xx = astMalloc( sizeof( double )*npos ); + vv = astMalloc( sizeof( double )*npos ); + if( astOK ) { + +/* Set up the evenly spaced interior sample positions. */ + dx = ( x1 - x0 )/( npos + 1 ); + xx0 = x0 + dx; + for( ipos = 0; ipos < npos; ipos++ ) { + xx[ ipos ] = xx0 + ipos*dx; + } + +/* Find the Mapping output values at these input values. */ + astTran1( map, npos, xx, 1, vv ); + +/* See if any of these samples deviate significantly from the straight line + defined by (x0,v0) and (x1,v1). If any such sample is found, we call + this function recursively to sample the subdivided intervals. First + handle cases where the straight line has zero gradient. */ + subdivide = 0; + if( v0 == v1 ) { + +/* Subdivide if any of the interior sample values are different to the + end values. */ + for( ipos = 0; ipos < npos; ipos++ ) { + if( vv[ ipos ] != v0 ) { + subdivide = 1; + break; + } + } + +/* Now handle cases where the line has non-zero gradient. Subdivide if any + of the interior sample input positions are further than "eps" from the + input position that would give the same output value if the mapping was + linear. */ + } else { + rg = ( x1 - x0 )/( v1 - v0 ); + for( ipos = 0; ipos < npos; ipos++ ) { + if( vv[ ipos ] == AST__BAD || + fabs( rg*( vv[ ipos ] - v0 ) - ( xx[ ipos ] - x0 ) ) > eps ) { + subdivide = 1; + break; + } + } + } + +/* If required, call this function recursively to subdivide each section + of the supplied input interval, and append samples to the returned + arrays. */ + if( subdivide ) { + +/* Do each sub-interval, except the last one. The number of subintervals + is one more than the number of interior samples. */ + for( ipos = 0; ipos < npos; ipos++ ) { + +/* Append samples covering the current subinterval to the ends of the + arrays. */ + AdaptLut( map, npos, eps, x0, xx[ ipos ], v0, vv[ ipos ], + xtab, vtab, nsamp, status ); + +/* Store the starting position for the next sub-interval. */ + x0 = xx[ ipos ]; + v0 = vv[ ipos ]; + } + +/* Now do the final sub-interval. */ + AdaptLut( map, npos, eps, x0, x1, v0, v1, xtab, vtab, nsamp, status ); + +/* If we do not need to subdivide, store the samples in the returned + array, together with the supplied final point. */ + } else { + +/* Extend the arrays. */ + isamp = *nsamp; + *nsamp += npos + 1; + *xtab = astGrow( *xtab, *nsamp, sizeof( double ) ); + *vtab = astGrow( *vtab, *nsamp, sizeof( double ) ); + if( astOK ) { + +/* Store the sample positions and values at the end of the extended + arrays. */ + for( ipos = 0; ipos < npos; ipos++, isamp++ ) { + (*xtab)[ isamp ] = xx[ ipos ]; + (*vtab)[ isamp ] = vv[ ipos ]; + } + (*xtab)[ isamp ] = x1; + (*vtab)[ isamp ] = v1; + } + } + } + +/* Free resources. */ + xx = astFree( xx ); + vv= astFree( vv ); +} + +static int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding, + const char *method, const char *class, int *status ){ + +/* +* Name: +* AddEncodingFrame + +* Purpose: +* Add a Frame which conforms to the requirements of the specified encoding. + +* Type: +* Private function. + +* Synopsis: +* int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function attempts to create a Frame based on the current Frame +* of the supplied FrameSet, which conforms to the requirements of the +* specified Encoding. If created, this Frame is added into the +* FrameSet as the new current Frame, and the index of the original current +* Frame is returned. + +* Parameters: +* this +* Pointer to the FitsChan. +* fs +* Pointer to the FrameSet. +* encoding +* The encoding in use. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The index of the original current Frame in the FrameSet. A value of +* AST__NOFRAME is returned if no new Frame is added to the FrameSet, +* or if an error occurs. +*/ + +/* Local Variables: */ + AstCmpFrame *cmpfrm; /* Pointer to spectral cube frame */ + AstFrame *cfrm; /* Pointer to original current Frame */ + AstFrame *newfrm; /* Frame describing coord system to be used */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + AstFrameSet *fsconv; /* FrameSet converting what we have to what we want */ + AstMapping *map; /* Mapping from what we have to what we want */ + AstSkyFrame *skyfrm; /* Pointer to SkyFrame */ + AstSpecFrame *specfrm; /* Pointer to SpecFrame */ + AstSystemType sys; /* Frame coordinate system */ + int i; /* Axis index */ + int naxc; /* No. of axes in original current Frame */ + int paxis; /* Axis index in primary frame */ + int result; /* Returned value */ + +/* Initialise */ + result = AST__NOFRAME; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the current Frame and note how many axes it has. */ + cfrm = astGetFrame( fs, AST__CURRENT ); + naxc = astGetNaxes( cfrm ); + +/* FITS-CLASS */ +/* ========== */ + if( encoding == FITSCLASS_ENCODING ) { + +/* Try to locate a SpecFrame and a SkyFrame in the current Frame. */ + specfrm = NULL; + skyfrm = NULL; + for( i = 0; i < naxc; i++ ) { + astPrimaryFrame( cfrm, i, &pfrm, &paxis ); + if( astIsASpecFrame( pfrm ) ) { + if( !specfrm ) specfrm = astCopy( pfrm ); + } else if( IsASkyFrame( pfrm ) ) { + if( !skyfrm ) skyfrm = astCopy( pfrm ); + } + pfrm = astAnnul( pfrm ); + } + +/* Cannot do anything if either is missing. */ + if( specfrm && skyfrm ) { + +/* If the spectral axis is not frequency, set it to frequency. Also set + spectral units of "Hz". */ + sys = astGetSystem( specfrm ); + if( sys != AST__FREQ ) { + astSetSystem( specfrm, AST__FREQ ); + sys = AST__FREQ; + } + +/* Ensure the standard of rest is Source and units are "Hz". */ + astSetUnit( specfrm, 0, "Hz" ); + astSetStdOfRest( specfrm, AST__SCSOR ); + +/* The celestial axes must be either FK4, FK5 or galactic. */ + sys = astGetSystem( skyfrm ); + if( sys != AST__FK4 && sys != AST__FK5 && sys != AST__GALACTIC ) { + astSetSystem( skyfrm, AST__FK5 ); + sys = AST__FK5; + } + +/* FK5 systems must be J2000, and FK4 must be B1950. */ + if( sys == AST__FK5 ) { + astSetC( skyfrm, "Equinox", "J2000.0" ); + } else if( sys == AST__FK4 ) { + astSetC( skyfrm, "Equinox", "B1950.0" ); + } + +/* Combine the spectral and celestial Frames into a single CmpFrame with + the spectral axis being the first axis. */ + cmpfrm = astCmpFrame( specfrm, skyfrm, "", status ); + +/* Attempt to obtain the current Frame of the supplied FrameSet to this + new Frame. */ + fsconv = astConvert( cfrm, cmpfrm, "" ); + if( fsconv ) { + +/* Get the Mapping and current Frame from the rconversion FrameSet. */ + newfrm = astGetFrame( fsconv, AST__CURRENT ); + map = astGetMapping( fsconv, AST__BASE, AST__CURRENT ); + +/* Save the original current Frame index. */ + result = astGetCurrent( fs ); + +/* Add the new Frame into the supplied FrameSet using the above Mapping + to connect it to the original current Frame. The new Frame becomes the + current Frame. */ + astAddFrame( fs, AST__CURRENT, map, newfrm ); + +/* Free resources */ + map = astAnnul( map ); + newfrm = astAnnul( newfrm ); + fsconv = astAnnul( fsconv ); + } + +/* Free resources */ + cmpfrm = astAnnul( cmpfrm ); + } + +/* Release resources. */ + if( specfrm ) specfrm = astAnnul( specfrm ); + if( skyfrm ) skyfrm = astAnnul( skyfrm ); + } + +/* Free reources. */ + cfrm = astAnnul( cfrm ); + +/* Return the result */ + return result; +} + +static void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel, + int npix, FitsStore *store, char s, const char *method, + const char *class, int *status ){ +/* +* Name: +* AddFrame + +* Purpose: +* Create a Frame describing a set of axes with a given co-ordinate +* version, and add it to the supplied FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel, +* int npix, FitsStore *store, char s, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A Frame is created describing axis with a specific co-ordinate +* version character, reading information from the supplied FitsStore. +* A suitable Mapping is created to connect the new Frame to the pixel +* (GRID) Frame in the supplied FrameSet, and the Frame is added into +* the FrameSet using this Mapping. + +* Parameters: +* this +* The FitsChan from which the keywords were read. Warning messages +* are added to this FitsChan if the celestial co-ordinate system is +* not recognized. +* fset +* Pointer to the FrameSet to be extended. +* pixel +* The index of the pixel (GRID) Frame within fset. +* npix +* The number of pixel axes. +* store +* The FitsStore containing the required information extracted from +* the FitsChan. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *frame; /* Requested Frame */ + AstMapping *mapping; /* Mapping from pixel to requested Frame */ + AstMapping *tmap; /* Temporary Mapping pointer */ + AstPermMap *pmap; /* PermMap pointer to add or remove axes */ + double con; /* Value to be assigned to missing axes */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int i; /* Axis index */ + int nwcs; /* Number of wcs axes */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Get a Mapping between pixel coordinates and physical coordinates, using + the requested axis descriptions. Also returns a Frame describing the + physical coordinate system. */ + mapping = WcsMapFrm( this, store, s, &frame, method, class, status ); + +/* Add the Frame into the FrameSet, and annul the mapping and frame. If + the new Frame has more axes than the pixel Frame, use a PermMap which + assigns constant value 1.0 to the extra axes. If the new Frame has less + axes than the pixel Frame, use a PermMap which throws away the extra + axes. */ + if( mapping != NULL ) { + nwcs = astGetNin( mapping ); + if( nwcs != npix ) { + inperm = astMalloc( sizeof(int)*(size_t)npix ); + outperm = astMalloc( sizeof(int)*(size_t)nwcs ); + if( astOK ) { + for( i = 0; i < npix; i++ ) { + inperm[ i ] = ( i < nwcs ) ? i : -1; + } + for( i = 0; i < nwcs; i++ ) { + outperm[ i ] = ( i < npix ) ? i : -1; + } + con = 1.0; + pmap = astPermMap( npix, inperm, nwcs, outperm, &con, "", status ); + tmap = (AstMapping *) astCmpMap( pmap, mapping, 1, "", status ); + pmap = astAnnul( pmap ); + (void) astAnnul( mapping ); + mapping = tmap; + } + inperm = astFree( inperm ); + outperm = astFree( outperm ); + } + astAddFrame( fset, pixel, mapping, frame ); + +/* Annul temporary resources. */ + mapping = astAnnul( mapping ); + } + frame = astAnnul( frame ); +} + +static int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs, + FitsStore *store, double *dim, char s, int encoding, + int isoff, const char *method, const char *class, + int *status ){ + +/* +* Name: +* AddVersion + +* Purpose: +* Add values to a FitsStore describing a specified Frame in a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs, +* FitsStore *store, double *dim, char s, int encoding, +* int isoff, const char *method, const char *class, +* int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Values are added to the supplied FitsStore describing the specified +* WCS Frame, and its relationship to the specified pixel Frame. These +* values are based on the standard FITS-WCS conventions. + +* Parameters: +* this +* Pointer to the FitsChan. +* fs +* Pointer to the FrameSet. +* ipix +* The index of the pixel (GRID) Frame within fset. +* iwcs +* The index of the Frame within fset to use as the WCS co-ordinate +* Frame. +* store +* The FitsStore in which to store the information extracted from +* the FrameSet. +* dim +* Pointer to an array of pixel axis dimensions. Individual elements +* will be AST__BAD if dimensions are not known. The number of +* elements should equal the number of axes in the base Frame of the +* supplied FrameSet. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* encoding +* The encoding being used. +* isoff +* If greater than zero, the Frame is an offset SkyFrame and the +* description added to the FitsStore should describe offset coordinates. +* If less than than zero, the Frame is an offset SkyFrame and the +* description added to the FitsStore should describe absolute coordinates. +* If zero, the Frame is an absolute SkyFrame and the description added +* to the FitsSTore should (by necessity) describe absolute coordinates. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Retuned Value: +* A value of 1 is returned if the WCS Frame was succesfully added to +* the FitsStore. A value of zero is returned otherwise. +*/ + +/* Local Variables: */ + AstFrame *wcsfrm; /* WCS Frame */ + AstFrameSet *fset; /* Temporary FrameSet */ + AstMapping *iwcmap; /* Mapping from WCS to IWC Frame */ + AstMapping *mapping; /* Mapping from pixel to WCS Frame */ + AstMapping *pixiwcmap; /* Mapping from pixel to IWC Frame */ + AstMapping *tmap2; /* Temporary Mapping */ + AstMapping *tmap; /* Temporary Mapping */ + const char *old_skyrefis;/* Old value of SkyRefIs attribute */ + double *crvals; /* Pointer to array holding default CRVAL values */ + double cdelt2; /* Sum of squared PC values */ + double cdelt; /* CDELT value for axis */ + double crpix; /* CRPIX value for axis */ + double crval; /* CRVAL value for axis */ + double pc; /* Element of the PC array */ + int *axis_done; /* Flags indicating which axes have been done */ + int *wperm; /* FITS axis for each Mapping output (Frame axis) */ + int fits_i; /* FITS WCS axis index */ + int fits_j; /* FITS pixel axis index */ + int iax; /* Frame axis index */ + int icurr; /* Index of current Frame */ + int nwcs; /* No. of axes in WCS frame */ + int ret; /* Returned value */ + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the frame is a SkyFrame describing offset coordinates, but the + description added to the FitsStore should be for absolute coordinates, + temporarily clear the SkyFrame SkyRefIs attribute. We need to make it + the current Frame first so that we can use the FrameSet to clear the + attribte, so that the SkyFrame will be re-mapped within the FrameSet + to take account of the clearing. For negative isoff values, set the + specific negative value to indicate the original SkyRefIs value. */ + if( isoff < 0 ) { + icurr = astGetCurrent( fs ); + astSetCurrent( fs, iwcs ); + old_skyrefis = astGetC( fs, "SkyRefIs" ); + if( astOK ) { + if( !Ustrcmp( old_skyrefis, "POLE", status ) ) { + isoff = -1; + } else if( !Ustrcmp( old_skyrefis, "ORIGIN", status ) ) { + isoff = -2; + } else { + isoff = -3; + } + } + astClear( fs, "SkyRefIs" ); + astSetCurrent( fs, icurr ); + } else { + old_skyrefis = AST__BAD_REF; + } + +/* Construct a new FrameSet holding the pixel and WCS Frames from the + supplied FrameSet, but in which the current Frame is a copy of the + supplied WCS Frame, but optionally extended to include any extra axes + needed to conform to the FITS model. For instance, if the WCS Frame + consists of a single 1D SpecFrame with a defined celestial reference + position (SpecFrame attributes RefRA and RefDec), then FITS-WCS paper + III requires there to be a pair of celestial axes in the WCS Frame in + which the celestial reference point for the spectral axis is defined. */ + fset = MakeFitsFrameSet( this, fs, ipix, iwcs, encoding, method, class, status ); + +/* If required, re-instate the original value of the SkyRefIs attribute + in the supplied FrameSet. */ + if( old_skyrefis != AST__BAD_REF ) { + astSetCurrent( fs, iwcs ); + astSetC( fs, "SkyRefIs", old_skyrefis ); + astSetCurrent( fs, icurr ); + } + +/* Abort if the FrameSet could not be produced. */ + if( !fset ) return ret; + +/* Get the Mapping from base to current Frame and check its inverse is + defined. Return if not. Note, we can handle non-invertable Mappings if + we are allowed to use the -TAB algorithm. */ + mapping = astGetMapping( fset, AST__BASE, AST__CURRENT ); + wcsfrm = astGetFrame( fset, AST__CURRENT ); + if( !astGetTranInverse( mapping ) && astGetTabOK( this ) <= 0 ) { + mapping = astAnnul( mapping ); + wcsfrm = astAnnul( wcsfrm ); + fset = astAnnul( fset ); + return ret; + } + +/* We now need to choose the "FITS WCS axis" (i.e. the number that is included + in FITS keywords such as CRVAL2) for each axis of the output Frame. + Allocate memory to store these indices. */ + nwcs= astGetNout( mapping ); + wperm = astMalloc( sizeof(int)*(size_t) nwcs ); + +/* Attempt to use the FitsAxisOrder attribute to determine the order. If + this is set to "", then for each WCS axis, we use the index of + the pixel axis which is most closely aligned with it. */ + if( !FitsAxisOrder( this, nwcs, wcsfrm, wperm, status ) && + !WorldAxes( this, mapping, dim, wperm, status ) ) { + wperm = astFree( wperm ); + mapping = astAnnul( mapping ); + wcsfrm = astAnnul( wcsfrm ); + fset = astAnnul( fset ); + return ret; + } + +/* Allocate an array of flags, one for each axis, which indicate if a + description of the corresponding axis has yet been stored in the + FitsStore. Initialise them to indicate that no axes have yet been + described. */ + axis_done = astMalloc( sizeof(int)*(size_t) nwcs ); + if( astOK ) for( iax = 0; iax < nwcs; iax++ ) axis_done[ iax ] = 0; + +/* Get the original reference point from the FitsChan and convert it into + the require WCS Frame. This is used as the default reference point (some + algorithms may choose to ignore this default reference point ). */ + crvals = ReadCrval( this, wcsfrm, s, method, class, status ); + +/* For each class of FITS conventions (celestial, spectral, others), + identify any corresponding axes within the WCS Frame and add + descriptions of them to the FitsStore. These descriptions are in terms + of the FITS keywords defined in the corresponding FITS-WCS paper. Note, + the keywords which descirbed the pixel->IWC mapping (CRPIX, CD, PC, + CDELT) are not stored by these functions, instead each function + returns a Mapping from WCS to IWC coords (these Mappings + pass on axes of the wrong class without change). These Mappings are + combined in series to get the final WCS->IWC Mapping. First do + celestial axes. */ + iwcmap = CelestialAxes( this, fset, dim, wperm, s, store, axis_done, + isoff, method, class, status ); + +/* Now look for spectral axes, and update the iwcmap. */ + tmap = SpectralAxes( this, fset, dim, wperm, s, store, crvals, axis_done, + method, class, status ); + tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status ); + tmap = astAnnul( tmap ); + (void) astAnnul( iwcmap ); + iwcmap = tmap2; + +/* Finally add descriptions of any axes not yet described (they are + assumed to be linear), and update the iwcmap. */ + tmap = OtherAxes( this, fset, dim, wperm, s, store, crvals, axis_done, + method, class, status ); + tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status ); + tmap = astAnnul( tmap ); + (void) astAnnul( iwcmap ); + iwcmap = tmap2; + +/* The "iwcmap" Mapping found above converts from the WCS Frame to the IWC + Frame. Combine the pixel->WCS Mapping with this WCS->IWC Mapping to + get the pixel->IWC Mapping. */ + pixiwcmap = (AstMapping *) astCmpMap( mapping, iwcmap, 1, "", status ); + mapping = astAnnul( mapping ); + iwcmap = astAnnul( iwcmap ); + +/* Now attempt to store values for the keywords describing the pixel->IWC + Mapping (CRPIX, CD, PC, CDELT). This tests that the iwcmap is linear. + Zero is returned if the test fails. */ + ret = MakeIntWorld( pixiwcmap, wcsfrm, wperm, s, store, dim, method, class, + status ); + +/* If succesfull... */ + if( ret ) { + +/* Store the Domain name as the WCSNAME keyword (if set). */ + if( astTestDomain( wcsfrm ) ) { + SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( wcsfrm ), + status ); + } + +/* Store the UT1-UTC correction, if set, converting from seconds to days + (as used by JACH). */ + if( astTestDut1( wcsfrm ) && s == ' ' ) { + SetItem( &(store->dut1), 0, 0, ' ', astGetDut1( wcsfrm )/SPD, status ); + } + +/* Set CRVAL values which are very small compared to the pixel size to + zero. */ + for( iax = 0; iax < nwcs; iax++ ) { + fits_i = wperm[ iax ]; + crval = GetItem( &(store->crval), fits_i, 0, s, NULL, method, class, + status ); + if( crval != AST__BAD ) { + cdelt2 = 0.0; + for( fits_j = 0; fits_j < nwcs; fits_j++ ){ + pc = GetItem( &(store->pc), fits_i, fits_j, s, NULL, method, class, status ); + if( pc == AST__BAD ) pc = ( fits_i == fits_j ) ? 1.0 : 0.0; + cdelt2 += pc*pc; + } + cdelt = GetItem( &(store->cdelt), fits_i, 0, s, NULL, method, class, status ); + if( cdelt == AST__BAD ) cdelt = 1.0; + cdelt2 *= ( cdelt*cdelt ); + if( fabs( crval ) < sqrt( DBL_EPSILON*cdelt2 ) ) { + SetItem( &(store->crval), fits_i, 0, s, 0.0, status ); + } + } + } + +/* Round CRPIX values to the nearest millionth of a pixel. */ + for( iax = 0; iax < nwcs; iax++ ) { + crpix = GetItem( &(store->crpix), 0, iax, s, NULL, method, class, status ); + if( crpix != AST__BAD ) { + SetItem( &(store->crpix), 0, iax, s, + floor( crpix*1.0E6 + 0.5 )*1.0E-6, status ); + } + } + } + +/* Free remaining resources. */ + if( crvals ) crvals = astFree( crvals ); + wcsfrm = astAnnul( wcsfrm ); + pixiwcmap = astAnnul( pixiwcmap ); + axis_done = astFree( axis_done ); + wperm = astFree( wperm ); + fset = astAnnul( fset ); + +/* If an error has occurred, return zero */ + return astOK ? ret : 0; +} + +static AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status ) { +/* +* Name: +* AddUnitMaps + +* Purpose: +* Embed a Mapping within a pair of parallel UnitMaps. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns a Mapping which consists of the supplied Mapping +* in parallel with a pair of UnitMaps so that the first axis of the +* supplied Mapping is at a specified axis number in the returned Mapping. + +* Parameters: +* map +* Pointer to the Mapping. The Mapping must have equal numbers of +* input and output coordinates. +* iax +* The index for the first input of "map" within the returned +* Mapping. +* nax +* The number of axes for the returned Mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A Mapping which has "nax" axes, and in which the "iax" axis +* corresponds to the first axis of "map". Axes lower than "iax" are +* transformed using a UnitMap, and axes higher than the last axis of +* "map" are transformed using a UnitMap. +*/ + +/* Local Variables: */ + AstMapping *ret; /* Returned Mapping */ + AstMapping *tmap0; /* Temporary Mapping */ + AstMapping *tmap1; /* Temporary Mapping */ + AstMapping *tmap2; /* Temporary Mapping */ + int nmap; /* Number of supplied Mapping inputs */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Initialise the returned Mapping to be a clone of the supplied Mapping. */ + ret = astClone( map ); + +/* Note the number of inputs of the supplied Mapping (assumed to be equal + to the number of outputs). */ + nmap = astGetNin( map ); + +/* If necessary produce a parallel CmpMap which combines the Mapping with a + UnitMap representing the axes lower than "iax". */ + if( iax > 0 ) { + tmap0 = (AstMapping *) astUnitMap( iax, "", status ); + tmap1 = (AstMapping *) astCmpMap( tmap0, ret, 0, "", status ); + ret = astAnnul( ret ); + tmap0 = astAnnul( tmap0 ); + ret = tmap1; + } + +/* If necessary produce a parallel CmpMap which combines the Mapping with a + UnitMap representing the axes higher than "iax+nmap". */ + if( iax + nmap < nax ) { + tmap1 = (AstMapping *) astUnitMap( nax - iax - nmap, "", status ); + tmap2 = (AstMapping *) astCmpMap( ret, tmap1, 0, "", status ); + ret = astAnnul( ret ); + tmap1 = astAnnul( tmap1 ); + ret = tmap2; + } + +/* Return the result. */ + return ret; +} + +static int AIPSFromStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ + +/* +* Name: +* AIPSFromStore + +* Purpose: +* Store WCS keywords in a FitsChan using FITS-AIPS encoding. + +* Type: +* Private function. + +* Synopsis: + +* int AIPSFromStore( AstFitsChan *this, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using FITS-AIPS encoding. +* +* AIPS encoding is like FITS-WCS encoding but with the following +* restrictions: +* +* 1) The celestial projection must not have any projection parameters +* which are not set to their default values. The one exception to this +* is that SIN projections are acceptable if the associated projection +* parameter PV_1 is zero and PV_2 = cot( reference point +* latitude). This is encoded using the string "-NCP". The SFL projection +* is encoded using the string "-GLS". Note, the original AIPS WCS +* system only recognised a small subset of the currently available +* projections, but some more recent AIPS-like software recognizes some +* of the new projections included in the FITS-WCS encoding. The AIT, +* GLS and MER can only be written if the CRVAL keywords are zero for +* both longitude and latitude axes. +* +* 2) The celestial axes must be RA/DEC, galactic or ecliptic. +* +* 3) LONPOLE and LATPOLE must take their default values. +* +* 4) Only primary axis descriptions are written out. +* +* 5) EPOCH is written instead of EQUINOX & RADECSYS, and uses the +* IAU 1984 rule ( EPOCH < 1984.0 is treated as a Besselian epoch +* and implies RADECSYS=FK4, EPOCH >= 1984.0 is treated as a +* Julian epoch and implies RADECSYS=FK5). The RADECSYS & EQUINOX +* values in the FitsStore must be consistent with this rule. +* +* 6) Any rotation produced by the PC matrix must be restricted to +* the celestial plane, and must involve no shear. A CROTA keyword +* with associated CDELT values are produced instead of the PC +* matrix. +* +* 7) ICRS is not supported. +* +* 8) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ" +* "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD, +* BARYCENT and GEOCENTR. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + char *comm; /* Pointer to comment string */ + const char *cval; /* Pointer to string keyword value */ + const char *specunit;/* Pointer to corrected spectral units string */ + char combuf[80]; /* Buffer for FITS card comment */ + char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ + char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ + char s; /* Co-ordinate version character */ + char sign[2]; /* Fraction's sign character */ + char spectype[MXCTYPELEN];/* Spectral axis CTYPE */ + double *cdelt; /* Pointer to CDELT array */ + double cdl; /* CDELT term */ + double cdlat_lon; /* Off-diagonal CD element */ + double cdlon_lat; /* Off-diagonal CD element */ + double coscro; /* Cos( CROTA ) */ + double crota; /* CROTA value to use */ + double epoch; /* Epoch of reference equinox */ + double fd; /* Fraction of a day */ + double latval; /* CRVAL for latitude axis */ + double lonval; /* CRVAL for longitude axis */ + double mjd99; /* MJD at start of 1999 */ + double p1, p2; /* Projection parameters */ + double rho_a; /* First estimate of CROTA */ + double rho_b; /* Second estimate of CROTA */ + double sincro; /* Sin( CROTA ) */ + double specfactor; /* Factor for converting internal spectral units */ + double val; /* General purpose value */ + int axlat; /* Index of latitude FITS WCS axis */ + int axlon; /* Index of longitude FITS WCS axis */ + int axrot1; /* Index of first CROTA rotation axis */ + int axrot2; /* Index of second CROTA rotation axis */ + int axspec; /* Index of spectral FITS WCS axis */ + int i; /* Axis index */ + int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ + int iymdf[ 4 ]; /* Year, month, date, fractional day */ + int j; /* Axis index */ + int jj; /* SlaLib status */ + int naxis; /* No. of axes */ + int ok; /* Is FitsSTore OK for IRAF encoding? */ + int prj; /* Projection type */ + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Initialise */ + specunit = ""; + specfactor = 1.0; + +/* First check that the values in the FitsStore conform to the + requirements of the AIPS encoding. Assume they do to begin with. */ + ok = 1; + +/* Just do primary axes. */ + s = ' '; + +/* Look for the primary celestial axes. */ + FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); + +/* If both longitude and latitude axes are present ...*/ + if( axlon >= 0 && axlat >= 0 ) { + +/* Get the CRVAL values for both axes. */ + latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status ); + if( latval == AST__BAD ) ok = 0; + lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status ); + if( lonval == AST__BAD ) ok = 0; + +/* Get the CTYPE values for both axes. Extract the projection type as + specified by the last 4 characters in the latitude CTYPE keyword value. */ + cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else { + strcpy( lontype, cval ); + } + cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + prj = AST__WCSBAD; + } else { + strcpy( lattype, cval ); + prj = astWcsPrjType( cval + 4 ); + } + +/* Check the projection type is OK. */ + if( prj == AST__WCSBAD ){ + ok = 0; + } else if( prj != AST__SIN ){ + +/* There must be no projection parameters. */ + if( GetMaxJM( &(store->pv), ' ', status ) >= 0 ) { + ok = 0; + +/* FITS-AIPS cannot handle the AST-specific TPN projection. */ + } else if( prj == AST__TPN ) { + ok = 0; + +/* For AIT, MER and GLS, check that the reference point is the origin of + the celestial co-ordinate system. */ + } else if( prj == AST__MER || + prj == AST__AIT || + prj == AST__SFL ) { + if( latval != 0.0 || lonval != 0.0 ){ + ok = 0; + +/* Change the new SFL projection code to to the older equivalent GLS */ + } else if( prj == AST__SFL ){ + (void) strcpy( lontype + 4, "-GLS" ); + (void) strcpy( lattype + 4, "-GLS" ); + } + } + +/* SIN projections are only acceptable if the associated projection + parameters are both zero, or if the first is zero and the second + = cot( reference point latitude ) (the latter case is equivalent to + the old NCP projection). */ + } else { + p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status ); + p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status ); + if( p1 == AST__BAD ) p1 = 0.0; + if( p2 == AST__BAD ) p2 = 0.0; + ok = 0; + if( p1 == 0.0 ) { + if( p2 == 0.0 ) { + ok = 1; + } else if( fabs( p2 ) >= 1.0E14 && latval == 0.0 ){ + ok = 1; + (void) strcpy( lontype + 4, "-NCP" ); + (void) strcpy( lattype + 4, "-NCP" ); + } else if( fabs( p2*tan( AST__DD2R*latval ) - 1.0 ) + < 0.01 ){ + ok = 1; + (void) strcpy( lontype + 4, "-NCP" ); + (void) strcpy( lattype + 4, "-NCP" ); + } + } + } + +/* Identify the celestial coordinate system from the first 4 characters of the + longitude CTYPE value. Only RA, galactic longitude, and ecliptic + longitude can be stored using FITS-AIPS. */ + if( ok && strncmp( lontype, "RA--", 4 ) && + strncmp( lontype, "GLON", 4 ) && + strncmp( lontype, "ELON", 4 ) ) ok = 0; + +/* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot + be encoded using FITS-IRAF. */ + if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ) + != AST__BAD || + GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ) + != AST__BAD ) ok = 0; + } + +/* If a spectral axis is present ...*/ + if( ok && axspec >= 0 ) { + +/* Get the CTYPE values for the axis, and find the AIPS equivalent, if + possible. */ + cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else { + if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) { + strcpy( spectype, "FREQ" ); + } else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) { + strcpy( spectype, "VELO" ); + } else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) { + strcpy( spectype, "FELO" ); + } else { + ok = 0; + } + } + +/* If OK, check the SPECSYS value and add the AIPS equivalent onto the + end of the CTYPE value.*/ + cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else if( ok ) { + if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-LSR" ); + } else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-LSD" ); + } else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-HEL" ); + } else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-GEO" ); + } else { + ok = 0; + } + } + +/* If still OK, ensure the spectral axis units are Hz or m/s. */ + cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else if( ok ) { + if( !strcmp( cval, "Hz" ) ) { + specunit = "HZ"; + specfactor = 1.0; + } else if( !strcmp( cval, "kHz" ) ) { + specunit = "HZ"; + specfactor = 1.0E3; + } else if( !strcmp( cval, "MHz" ) ) { + specunit = "HZ"; + specfactor = 1.0E6; + } else if( !strcmp( cval, "GHz" ) ) { + specunit = "HZ"; + specfactor = 1.0E9; + } else if( !strcmp( cval, "m/s" ) ) { + specunit = "m/s"; + specfactor = 1.0; + } else if( !strcmp( cval, "km/s" ) ) { + specunit = "m/s"; + specfactor = 1.0E3; + } else { + ok = 0; + } + } + } + +/* Save the number of axes */ + naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; + +/* If this is different to the value of NAXIS abort since this encoding + does not support WCSAXES keyword. */ + if( naxis != store->naxis ) ok = 0; + +/* Allocate memory to store the CDELT values */ + if( ok ) { + cdelt = (double *) astMalloc( sizeof(double)*naxis ); + if( !cdelt ) ok = 0; + } else { + cdelt = NULL; + } + +/* Check that rotation is restricted to the celestial plane, and extract + the CDELT (diagonal) terms, etc. If there are no celestial + axes, restrict rotation to the first two non-spectral axes. */ + if( axlat < 0 && axlon < 0 ) { + if( axspec >= 0 && naxis > 2 ) { + axrot2 = ( axspec == 0 ) ? 1 : 0; + axrot1 = axrot2 + 1; + if( axrot1 == axspec ) axrot1++; + } else if( naxis > 1 ){ + axrot2 = 0; + axrot1 = 1; + } else { + axrot2 = -1; + axrot1 = -1; + } + } else { + axrot1 = axlon; + axrot2 = axlat; + } + cdlat_lon = 0.0; + cdlon_lat = 0.0; + for( i = 0; i < naxis && ok; i++ ){ + cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( cdl == AST__BAD ) cdl = 1.0; + for( j = 0; j < naxis && ok; j++ ){ + val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; + val *= cdl; + if( i == j ){ + cdelt[ i ] = val; + } else if( i == axrot2 && j == axrot1 ){ + cdlat_lon = val; + } else if( i == axrot1 && j == axrot2 ){ + cdlon_lat = val; + } else if( val != 0.0 ){ + ok = 0; + } + } + } + +/* Find the CROTA and CDELT values for the celestial axes. */ + if( ok && axrot1 >= 0 && axrot2 >= 0 ) { + if( cdlat_lon > 0.0 ) { + rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] ); + } else if( cdlat_lon == 0.0 ) { + rho_a = 0.0; + } else { + rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] ); + } + if( cdlon_lat > 0.0 ) { + rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] ); + } else if( cdlon_lat == 0.0 ) { + rho_b = 0.0; + } else { + rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] ); + } + if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){ + crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) ); + coscro = cos( crota ); + sincro = sin( crota ); + if( fabs( coscro ) > fabs( sincro ) ){ + cdelt[ axrot2 ] /= coscro; + cdelt[ axrot1 ] /= coscro; + } else { + cdelt[ axrot2 ] = -cdlon_lat/sincro; + cdelt[ axrot1 ] = cdlat_lon/sincro; + } + crota *= AST__DR2D; + } else { + ok = 0; + } + } else { + crota = 0.0; + } + +/* Get RADECSYS and the reference equinox (called EPOCH in FITS-AIPS). */ + cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); + epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); + +/* If RADECSYS was available... */ + if( cval ){ + +/* ICRS is not supported in this encoding. */ + if( !strcmp( "ICRS", cval ) ) ok = 0; + +/* If epoch was not available, set a default epoch. */ + if( epoch == AST__BAD ){ + if( !strcmp( "FK4", cval ) ){ + epoch = 1950.0; + } else if( !strcmp( "FK5", cval ) ){ + epoch = 2000.0; + } else { + ok = 0; + } + +/* If an epoch was supplied, check it is consistent with the IAU 1984 + rule. */ + } else { + if( !strcmp( "FK4", cval ) ){ + if( epoch >= 1984.0 ) ok = 0; + } else if( !strcmp( "FK5", cval ) ){ + if( epoch < 1984.0 ) ok = 0; + } else { + ok = 0; + } + } + } + +/* Only create the keywords if the FitsStore conforms to the requirements + of the FITS-AIPS encoding. */ + if( ok ) { + +/* Get and save CRPIX for all pixel axes. These are required, so break + if they are not available. */ + for( j = 0; j < naxis && ok; j++ ){ + val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + } else { + sprintf( combuf, "Reference pixel on axis %d", j + 1 ); + SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val, + AST__FLOAT, combuf, status ); + } + } + +/* Get and save CRVAL for all intermediate axes. These are required, so + break if they are not available. */ + for( i = 0; i < naxis && ok; i++ ){ + val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + } else { + if( i == axspec ) val *= specfactor; + sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); + SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, + AST__FLOAT, combuf, status ); + } + } + +/* Get and save CTYPE for all intermediate axes. These are required, so + break if they are not available. Use the potentially modified versions + saved above for the celestial axes. */ + for( i = 0; i < naxis && ok; i++ ){ + if( i == axlat ) { + cval = lattype; + } else if( i == axlon ) { + cval = lontype; + } else if( i == axspec ) { + cval = spectype; + } else { + cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + } + if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) { + comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + if( !comm ) { + sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); + comm = combuf; + } + SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, + AST__STRING, comm, status ); + } else { + ok = 0; + } + } + +/* CDELT values */ + if( axspec != -1 ) cdelt[ axspec ] *= specfactor; + for( i = 0; i < naxis; i++ ){ + SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i, + AST__FLOAT, "Pixel size", status ); + } + +/* CUNIT values. */ + for( i = 0; i < naxis; i++ ) { + cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); + if( cval ) { + if( i == axspec ) cval = specunit; + sprintf( combuf, "Units for axis %d", i + 1 ); + SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, + combuf, status ); + } + } + +/* CROTA */ + if( axrot2 != -1 ){ + SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota, + AST__FLOAT, "Axis rotation", status ); + } else if( ( axspec == -1 && naxis > 1 ) || + ( axspec != -1 && naxis > 2 ) ) { + SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status ); + } + +/* Reference equinox */ + if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT, + "Epoch of reference equinox", status ); + +/* Date of observation. */ + val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD ) { + +/* The format used for the DATE-OBS keyword depends on the value of the + keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. + Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ + palCaldj( 99, 1, 1, &mjd99, &jj ); + if( val < mjd99 ) { + palDjcal( 0, val, iymdf, &jj ); + sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], + iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); + } else { + palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); + palDd2tf( 3, fd, sign, ihmsf ); + sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", + iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], + ihmsf[2], ihmsf[3] ); + } + +/* Now store the formatted string in the FitsChan. */ + cval = combuf; + SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, + "Date of observation", status ); + } + +/* Spectral stuff.. */ + if( axspec >= 0 ) { + +/* Rest frequency */ + val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ), + &val, AST__FLOAT, "[Hz] Rest frequency", status ); + } + } + +/* Release CDELT workspace */ + if( cdelt ) cdelt = (double *) astFree( (void *) cdelt ); + +/* Return zero or ret depending on whether an error has occurred. */ + return astOK ? ok : 0; +} + +static int AIPSPPFromStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ + +/* +* Name: +* AIPSPPFromStore + +* Purpose: +* Store WCS keywords in a FitsChan using FITS-AIPS++ encoding. + +* Type: +* Private function. + +* Synopsis: + +* int AIPSPPFromStore( AstFitsChan *this, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using FITS-AIPS++ encoding. +* +* AIPS++ encoding is like FITS-WCS encoding but with the following +* restrictions: +* +* 1) The celestial axes must be RA/DEC, galactic or ecliptic. +* +* 2) Only primary axis descriptions are written out. +* +* 3) RADESYS is not written and so the RADECSYS & EQUINOX values in the +* FitsStore must be consistent with the "1984" rule. +* +* 4) Any rotation produced by the PC matrix must be restricted to +* the celestial plane, and must involve no shear. A CROTA keyword +* with associated CDELT values are produced instead of the PC +* matrix. +* +* 5) ICRS is not supported. +* +* 6) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ" +* "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD, +* BARYCENT and GEOCENTR. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + char *comm; /* Pointer to comment string */ + const char *cval; /* Pointer to string keyword value */ + const char *specunit;/* Pointer to corrected spectral units string */ + char combuf[80]; /* Buffer for FITS card comment */ + char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ + char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ + char s; /* Co-ordinate version character */ + char sign[2]; /* Fraction's sign character */ + char spectype[MXCTYPELEN];/* Spectral axis CTYPE */ + double *cdelt; /* Pointer to CDELT array */ + double cdl; /* CDELT term */ + double cdlat_lon; /* Off-diagonal CD element */ + double cdlon_lat; /* Off-diagonal CD element */ + double coscro; /* Cos( CROTA ) */ + double crota; /* CROTA value to use */ + double epoch; /* Epoch of reference equinox */ + double fd; /* Fraction of a day */ + double mjd99; /* MJD at start of 1999 */ + double rho_a; /* First estimate of CROTA */ + double rho_b; /* Second estimate of CROTA */ + double sincro; /* Sin( CROTA ) */ + double specfactor; /* Factor for converting internal spectral units */ + double val; /* General purpose value */ + int axlat; /* Index of latitude FITS WCS axis */ + int axlon; /* Index of longitude FITS WCS axis */ + int axrot1; /* Index of first CROTA rotation axis */ + int axrot2; /* Index of second CROTA rotation axis */ + int axspec; /* Index of spectral FITS WCS axis */ + int i; /* Axis index */ + int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ + int iymdf[ 4 ]; /* Year, month, date, fractional day */ + int j; /* Axis index */ + int jj; /* SlaLib status */ + int m; /* Projection parameter index */ + int maxm; /* Max projection parameter index */ + int naxis; /* No. of axes */ + int ok; /* Is FitsSTore OK for IRAF encoding? */ + int prj; /* Projection type */ + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Initialise */ + specunit = ""; + specfactor = 1.0; + maxm = 0; + +/* First check that the values in the FitsStore conform to the + requirements of the AIPS++ encoding. Assume they do to begin with. */ + ok = 1; + +/* Just do primary axes. */ + s = ' '; + +/* Save the number of axes */ + naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; + +/* Look for the primary celestial and spectral axes. */ + FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); + +/* If both longitude and latitude axes are present ...*/ + if( axlon >= 0 && axlat >= 0 ) { + +/* Get the CTYPE values for both axes. Extract the projection type as + specified by the last 4 characters in the latitude CTYPE keyword value. */ + cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else { + strcpy( lontype, cval ); + } + cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + prj = AST__WCSBAD; + } else { + strcpy( lattype, cval ); + prj = astWcsPrjType( cval + 4 ); + } + +/* FITS-AIPS++ cannot handle the AST-specific TPN projection. */ + if( prj == AST__TPN || prj == AST__WCSBAD ) ok = 0; + +/* Projection parameters. FITS-AIPS++ encoding ignores projection parameters + associated with the longitude axis. The number of parameters is limited to + 10. */ + maxm = GetMaxJM( &(store->pv), ' ', status ); + for( i = 0; i < naxis && ok; i++ ){ + if( i != axlon ) { + for( m = 0; m <= maxm; m++ ){ + val = GetItem( &(store->pv), i, m, s, NULL, method, class, status ); + if( val != AST__BAD ) { + if( i != axlat || m >= 10 ){ + ok = 0; + break; + } + } + } + } + } + +/* Identify the celestial coordinate system from the first 4 characters of the + longitude CTYPE value. Only RA, galactic longitude, and ecliptic + longitude can be stored using FITS-AIPS++. */ + if( ok && strncmp( lontype, "RA--", 4 ) && + strncmp( lontype, "GLON", 4 ) && + strncmp( lontype, "ELON", 4 ) ) ok = 0; + } + +/* If a spectral axis is present ...*/ + if( axspec >= 0 ) { + +/* Get the CTYPE values for the axis, and find the AIPS equivalent, if + possible. */ + cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else { + if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) { + strcpy( spectype, "FREQ" ); + } else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) { + strcpy( spectype, "VELO" ); + } else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) { + strcpy( spectype, "FELO" ); + } else { + ok = 0; + } + } + +/* If OK, check the SPECSYS value and add the AIPS equivalent onto the + end of the CTYPE value.*/ + cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else { + if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-LSR" ); + } else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-LSD" ); + } else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-HEL" ); + } else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) { + strcpy( spectype+4, "-GEO" ); + } else { + ok = 0; + } + } + +/* If still OK, ensure the spectral axis units are Hz or m/s. */ + cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else if( ok ) { + if( !strcmp( cval, "Hz" ) ) { + specunit = "HZ"; + specfactor = 1.0; + } else if( !strcmp( cval, "kHz" ) ) { + specunit = "HZ"; + specfactor = 1.0E3; + } else if( !strcmp( cval, "MHz" ) ) { + specunit = "HZ"; + specfactor = 1.0E6; + } else if( !strcmp( cval, "GHz" ) ) { + specunit = "HZ"; + specfactor = 1.0E9; + } else if( !strcmp( cval, "m/s" ) ) { + specunit = "m/s"; + specfactor = 1.0; + } else if( !strcmp( cval, "km/s" ) ) { + specunit = "m/s"; + specfactor = 1.0E3; + } else { + ok = 0; + } + } + } + +/* If this is different to the value of NAXIS abort since this encoding + does not support WCSAXES keyword. */ + if( naxis != store->naxis ) ok = 0; + +/* Allocate memory to store the CDELT values */ + if( ok ) { + cdelt = (double *) astMalloc( sizeof(double)*naxis ); + if( !cdelt ) ok = 0; + } else { + cdelt = NULL; + } + +/* Check that rotation is restricted to the celestial plane, and extract + the CDELT (diagonal) terms, etc. If there are no celestial + axes, restrict rotation to the first two non-spectral axes. */ + if( axlat < 0 && axlon < 0 ) { + if( axspec >= 0 && naxis > 2 ) { + axrot2 = ( axspec == 0 ) ? 1 : 0; + axrot1 = axrot2 + 1; + if( axrot1 == axspec ) axrot1++; + } else if( naxis > 1 ){ + axrot2 = 0; + axrot1 = 1; + } else { + axrot2 = -1; + axrot1 = -1; + } + } else { + axrot1 = axlon; + axrot2 = axlat; + } + cdlat_lon = 0.0; + cdlon_lat = 0.0; + for( i = 0; i < naxis && ok; i++ ){ + cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( cdl == AST__BAD ) cdl = 1.0; + for( j = 0; j < naxis && ok; j++ ){ + val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; + val *= cdl; + if( i == j ){ + cdelt[ i ] = val; + } else if( i == axrot2 && j == axrot1 ){ + cdlat_lon = val; + } else if( i == axrot1 && j == axrot2 ){ + cdlon_lat = val; + } else if( val != 0.0 ){ + ok = 0; + } + } + } + +/* Find the CROTA and CDELT values for the celestial axes. */ + if( ok && axrot1 >= 0 && axrot2 >= 0 ) { + if( cdlat_lon > 0.0 ) { + rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] ); + } else if( cdlat_lon == 0.0 ) { + rho_a = 0.0; + } else { + rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] ); + } + if( cdlon_lat > 0.0 ) { + rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] ); + } else if( cdlon_lat == 0.0 ) { + rho_b = 0.0; + } else { + rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] ); + } + if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){ + crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) ); + coscro = cos( crota ); + sincro = sin( crota ); + if( fabs( coscro ) > fabs( sincro ) ){ + cdelt[ axrot2 ] /= coscro; + cdelt[ axrot1 ] /= coscro; + } else { + cdelt[ axrot2 ] = -cdlon_lat/sincro; + cdelt[ axrot1 ] = cdlat_lon/sincro; + } + crota *= AST__DR2D; + +/* Use AST__BAD to indicate that CDi_j values should be produced + instead of CROTA/CDELT. (I am told AIPS++ can understand CD matrices) */ + } else { + crota = AST__BAD; + } + } else { + crota = 0.0; + } + +/* Get RADECSYS and the reference equinox. */ + cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); + epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); + +/* If RADECSYS was available... */ + if( cval ){ + +/* ICRS is not supported in this encoding. */ + if( !strcmp( "ICRS", cval ) ) ok = 0; + +/* If epoch was not available, set a default epoch. */ + if( epoch == AST__BAD ){ + if( !strcmp( "FK4", cval ) ){ + epoch = 1950.0; + } else if( !strcmp( "FK5", cval ) ){ + epoch = 2000.0; + } else { + ok = 0; + } + +/* If an equinox was supplied, check it is consistent with the IAU 1984 + rule. */ + } else { + if( !strcmp( "FK4", cval ) ){ + if( epoch >= 1984.0 ) ok = 0; + } else if( !strcmp( "FK5", cval ) ){ + if( epoch < 1984.0 ) ok = 0; + } else { + ok = 0; + } + } + } + +/* Only create the keywords if the FitsStore conforms to the requirements + of the FITS-AIPS++ encoding. */ + if( ok ) { + +/* Get and save CRPIX for all pixel axes. These are required, so break + if they are not available. */ + for( j = 0; j < naxis && ok; j++ ){ + val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + } else { + sprintf( combuf, "Reference pixel on axis %d", j + 1 ); + SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val, + AST__FLOAT, combuf, status ); + } + } + +/* Get and save CRVAL for all intermediate axes. These are required, so + break if they are not available. */ + for( i = 0; i < naxis && ok; i++ ){ + val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + } else { + if( i == axspec ) val *= specfactor; + sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); + SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, + AST__FLOAT, combuf, status ); + } + } + +/* Get and save CTYPE for all intermediate axes. These are required, so + break if they are not available. Use the potentially modified versions + saved above for the celestial axes. */ + for( i = 0; i < naxis && ok; i++ ){ + if( i == axlat ) { + cval = lattype; + } else if( i == axlon ) { + cval = lontype; + } else if( i == axspec ) { + cval = spectype; + } else { + cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + } + if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) { + comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + if( !comm ) { + sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); + comm = combuf; + } + SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, + AST__STRING, comm, status ); + } else { + ok = 0; + } + } + +/* CDELT values */ + if( axspec != -1 ) cdelt[ axspec ] *= specfactor; + for( i = 0; i < naxis; i++ ){ + SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i, + AST__FLOAT, "Pixel size", status ); + } + +/* CUNIT values. [Spectral axis units should be upper-case] */ + for( i = 0; i < naxis; i++ ) { + cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); + if( cval ) { + if( i == axspec ) cval = specunit; + sprintf( combuf, "Units for axis %d", i + 1 ); + SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, + combuf, status ); + } + } + +/* CD matrix. Multiply the row of the PC matrix by the CDELT value. */ + if( crota == AST__BAD ) { + for( i = 0; i < naxis; i++ ) { + cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( cdl == AST__BAD ) cdl = 1.0; + for( j = 0; j < naxis; j++ ){ + val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; + val *= cdl; + if( val != 0.0 ) { + SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val, + AST__FLOAT, "Transformation matrix element", status ); + } + } + } + +/* CROTA */ + } else if( crota != 0.0 ) { + if( axrot2 != -1 ){ + SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota, + AST__FLOAT, "Axis rotation", status ); + } else if( ( axspec == -1 && naxis > 1 ) || + ( axspec != -1 && naxis > 2 ) ) { + SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status ); + } + } + +/* Reference equinox */ + if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT, + "Epoch of reference equinox", status ); + +/* Latitude of native north pole. */ + val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT, + "Latitude of native north pole", status ); + +/* Longitude of native north pole. */ + val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, "LONPOLE", &val, AST__FLOAT, + "Longitude of native north pole", status ); + +/* Date of observation. */ + val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD ) { + +/* The format used for the DATE-OBS keyword depends on the value of the + keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. + Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ + palCaldj( 99, 1, 1, &mjd99, &jj ); + if( val < mjd99 ) { + palDjcal( 0, val, iymdf, &jj ); + sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], + iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); + } else { + palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); + palDd2tf( 3, fd, sign, ihmsf ); + sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", + iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], + ihmsf[2], ihmsf[3] ); + } + +/* Now store the formatted string in the FitsChan. */ + cval = combuf; + SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, + "Date of observation", status ); + } + +/* Projection parameters. */ + if( axlat >= 0 && axlon >= 0 ) { + for( m = 0; m <= maxm; m++ ){ + val = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ), + &val, AST__FLOAT, "Projection parameter", status ); + } + } + +/* Spectral stuff.. */ + if( axspec >= 0 ) { + +/* Rest frequency */ + val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ), + &val, AST__FLOAT, "[Hz] Rest frequency", status ); + } + } + +/* Release CDELT workspace */ + if( cdelt ) cdelt = (double *) astFree( (void *) cdelt ); + +/* Return zero or ret depending on whether an error has occurred. */ + return astOK ? ok : 0; +} + +static char *CardComm( AstFitsChan *this, int *status ){ + +/* +* Name: +* CardComm + +* Purpose: +* Return the keyword comment from the current card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char *CardComm( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns a pointer to a string holding the keyword comment from the +* current card. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the keyword comment, or NULL if the FitsChan is at +* end-of-file, or does not have a comment. + +* Notes: +* - The current card is not changed by this function. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + char *ret; + +/* Check the supplied object. */ + if( !this ) return NULL; + +/* If the current card is defined, store a pointer to its keyword comment. */ + if( this->card ){ + ret = ( (FitsCard *) this->card )->comment; + +/* Otherwise store a NULL pointer. */ + } else { + ret = NULL; + } + +/* Return the answer. */ + return ret; +} + +static void *CardData( AstFitsChan *this, size_t *size, int *status ){ + +/* +* Name: +* CardData + +* Purpose: +* Return a pointer to the keyword data value for the current card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void *CardData( AstFitsChan *this, size_t *size, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns a pointer to keyword data value from the current card. + +* Parameters: +* this +* Pointer to the FitsChan. +* size +* A pointer to a location at which to return the number of bytes +* occupied by the data value. NULL can be supplied if this +* information is not required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the keyword data, or NULL if the FitsChan is at +* end-of-file, or if the keyword does not have any data. + +* Notes: +* - For text data, the returned value for "size" includes the +* terminating null character. +* - The current card is not changed by this function. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + void *ret; + +/* Check the supplied object. */ + if( !this ) return NULL; + +/* If the current card is defined, store a pointer to its keyword data. */ + if( this->card ){ + ret = ( (FitsCard *) this->card )->data; + if( size ) *size = ( (FitsCard *) this->card )->size; + +/* Otherwise store a NULL pointer. */ + } else { + ret = NULL; + if( size ) *size = 0; + } + +/* Return the answer. */ + return ret; +} + +static int *CardFlags( AstFitsChan *this, int *status ){ + +/* +* Name: +* CardFlags + +* Purpose: +* Return a pointer to the flags mask for the current card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int *CardFlags( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns a pointer to the flags mask for the current card. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The pointer to the flags mask. + +* Notes: +* - The current card is not changed by this function. +* - NULL is returned if the current card is not defined. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + int *ret; + +/* Check the supplied object. */ + if( !this ) return NULL; + +/* If the current card is defined, store its deletion flag. */ + if( this->card ){ + ret = &( ( (FitsCard *) this->card )->flags ); + +/* Otherwise store zero. */ + } else { + ret = NULL; + } + +/* Return the answer. */ + return ret; +} + +static char *CardName( AstFitsChan *this, int *status ){ +/* +* Name: +* CardName + +* Purpose: +* Return the keyword name from the current card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char *CardName( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns a pointer to a string holding the keyword name from the +* current card. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the keyword name, or NULL if the FitsChan is at +* end-of-file. + +* Notes: +* - The current card is not changed by this function. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + char *ret; + +/* Check the supplied object. */ + if( !this ) return NULL; + +/* If the current card is defined, store a pointer to its keyword name. */ + if( this->card ){ + ret = ( (FitsCard *) this->card )->name; + +/* Otherwise store a NULL pointer. */ + } else { + ret = NULL; + } + +/* Return the answer. */ + return ret; +} + +static int CardType( AstFitsChan *this, int *status ){ +/* +* Name: +* CardType + +* Purpose: +* Return the keyword type from the current card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int CardType( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns the keyword type from the current card. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The keyword type. + +* Notes: +* - The current card is not changed by this function. +* - AST__NOTYPE is returned if the current card is not defined. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + int ret; + +/* Check the supplied object. */ + if( !this ) return AST__NOTYPE; + +/* If the current card is defined, store the keyword type. */ + if( this->card ){ + ret = ( (FitsCard *) this->card )->type; + +/* Otherwise store AST__NOTYPE. */ + } else { + ret = AST__NOTYPE; + } + +/* Return the answer. */ + return ret; +} + +static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, + int *wperm, char s, FitsStore *store, int *axis_done, + int isoff, const char *method, const char *class, int *status ){ + +/* +* Name: +* CelestialAxes + +* Purpose: +* Add values to a FitsStore describing celestial axes in a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, +* int *wperm, char s, FitsStore *store, int *axis_done, +* int isoff, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The current Frame of the supplied FrameSet is searched for celestial +* axes. If any are found, FITS WCS keyword values describing the axis +* are added to the supplied FitsStore, if possible (the conventions +* of FITS-WCS paper II are used). Note, this function does not store +* values for keywords which define the transformation from pixel +* coords to Intermediate World Coords (CRPIX, PC and CDELT), but a +* Mapping is returned which embodies these values. This Mapping is +* from the current Frame in the FrameSet (WCS coords) to a Frame +* representing IWC. The IWC Frame has the same number of axes as the +* WCS Frame which may be greater than the number of base Frame (i.e. +* pixel) axes. + +* Parameters: +* this +* Pointer to the FitsChan. +* fs +* Pointer to the FrameSet. The base Frame should represent FITS pixel +* coordinates, and the current Frame should represent FITS WCS +* coordinates. The number of base Frame axes should not exceed the +* number of current Frame axes. +* dim +* An array holding the image dimensions in pixels. AST__BAD can be +* supplied for any unknown dimensions. +* wperm +* Pointer to an array of integers with one element for each axis of +* the current Frame. Each element holds the zero-based +* index of the FITS-WCS axis (i.e. the value of "i" in the keyword +* names "CTYPEi", "CRVALi", etc) which describes the Frame axis. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* store +* The FitsStore in which to store the FITS WCS keyword values. +* axis_done +* An array of flags, one for each Frame axis, which indicate if a +* description of the corresponding axis has yet been stored in the +* FitsStore. +* isoff +* If greater than zero, the description to add to the FitsStore +* should describe offset coordinates. If less than zero, the +* description to add to the FitsStore should describe absolute +* coordinates but should include the SkyRefIs, SkyRef and SkyRefP +* attributes. If zero, ignore all offset coordinate info. The +* absolute value indicates the nature of the reference point: +* 1 == "pole", 2 == "origin", otherwise "ignored". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If celestial axes were found which can be described using the +* conventions of FITS-WCS paper II, then a Mapping from the current Frame +* of the supplied FrameSet, to the IWC Frame is returned. Otherwise, +* a UnitMap is returned. Note, the Mapping only defines the IWC +* transformation for celestial axes. Any non-celestial axes are passed +* unchanged by the returned Mapping. +*/ + +/* Local Variables: */ + AstFitsTable *table; /* Pointer to structure holding -TAB table info */ + AstFrame *pframe; /* Primary Frame containing current WCS axis*/ + AstFrame *wcsfrm; /* WCS Frame within FrameSet */ + AstMapping *map1; /* Pointer to pre-WcsMap Mapping */ + AstMapping *map3; /* Pointer to post-WcsMap Mapping */ + AstMapping *map; /* Pixel -> WCS mapping */ + AstMapping *ret; /* Returned Mapping */ + AstMapping *tmap0; /* A temporary Mapping */ + AstMapping *tmap1; /* A temporary Mapping */ + AstMapping *tmap2; /* A temporary Mapping */ + AstMapping *tmap3; /* A temporary Mapping */ + AstMapping *tmap4; /* A temporary Mapping */ + AstSkyFrame *skyfrm; /* The SkyFrame defining current WCS axis */ + AstWcsMap *map2; /* Pointer to WcsMap */ + AstWcsMap *map2b; /* Pointer to WcsMap with cleared lat/lonpole */ + char *cval; /* Pointer to keyword value */ + char *temp; /* Pointer to temporary string */ + double *mat; /* Pointer to matrix diagonal elements */ + double *ppcfid; /* Pointer to array holding PPC at fiducial point */ + double con; /* Constant value for unassigned axes */ + double crval[ 2 ]; /* Psi coords of reference point */ + double pv; /* Projection parameter value */ + double skyfid[ 2 ]; /* Sky coords of fiducial point */ + double val; /* Keyword value */ + int *inperm; /* Input axis permutation array */ + int *outperm; /* Output axis permutation array */ + int *tperm; /* Pointer to new FITS axis numbering array */ + int axlat; /* Index of latitude output from WcsMap */ + int axlon; /* Index of longitude output from WcsMap */ + int extver; /* Table version number for -TAB headers */ + int fits_ilat; /* FITS WCS axis index for latitude axis */ + int fits_ilon; /* FITS WCS axis index for longitude axis */ + int i; /* Loop index */ + int iax; /* Axis index */ + int icolindexlat; /* Index of table column holding lat index vector */ + int icolindexlon; /* Index of table column holding lon index vector */ + int icolmainlat; /* Index of table column holding main lat coord array */ + int icolmainlon; /* Index of table column holding main lon coord array */ + int interplat; /* INterpolation method for latitude look-up tables */ + int interplon; /* INterpolation method for longitude look-up tables */ + int ilat; /* Index of latitude axis within total WCS Frame */ + int ilon; /* Index of longitude axis within total WCS Frame */ + int j; /* Loop index */ + int m; /* Projection parameter index */ + int maxm; /* Largest used "m" value */ + int mlat; /* Index of latitude axis in main lat coord array */ + int mlon; /* Index of longitude axis in main lon coord array */ + int nwcs; /* Number of WCS axes */ + int nwcsmap; /* Number of inputs/outputs for the WcsMap */ + int paxis; /* Axis index within primary Frame */ + int skylataxis; /* Index of latitude axis within SkyFrame */ + int skylonaxis; /* Index of longitude axis within SkyFrame */ + int tpn; /* Is the WCS projectiona TPN projection? */ + +/* Initialise */ + ret = NULL; + +/* Other initialisation to avoid compiler warnings. */ + mlon = 0; + mlat = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Get a pointer to the WCS Frame. */ + wcsfrm = astGetFrame( fs, AST__CURRENT ); + +/* Store the number of WCS axes. */ + nwcs = astGetNout( fs ); + +/* Check each axis in the WCS Frame to see if it is a celestial axis. */ + skyfrm = NULL; + map = NULL; + ilon = -1; + ilat = -1; + for( iax = 0; iax < nwcs; iax++ ) { + +/* Obtain a pointer to the primary Frame containing the current WCS axis. */ + astPrimaryFrame( wcsfrm, iax, &pframe, &paxis ); + +/* If the current axis belongs to a SkyFrame, we have found a celestial + axis. Keep a pointer to it, and note the indices of the celestial axes + within the complete WCS Frame. The MakeFitsFrameSet function will have + ensured that the WCS Frame only contains at most a single SkyFrame. */ + if( IsASkyFrame( pframe ) ) { + if( !skyfrm ) skyfrm = astClone( pframe ); + if( paxis == 0 ) { + ilon = iax; + } else { + ilat = iax; + } + +/* Indicate that this axis has been classified. */ + axis_done[ iax ] = 1; + } + +/* Release resources. */ + pframe = astAnnul( pframe ); + } + +/* Only proceed if we found celestial axes. */ + if( ilon != -1 && ilat != -1 ) { + +/* Note the FITS WCS axis indices for the longitude and latitude axes */ + fits_ilon = wperm[ ilon ]; + fits_ilat = wperm[ ilat ]; + +/* Create an array to hold the Projection Plane Coords corresponding to the + CRVALi keywords. */ + ppcfid = (double *) astMalloc( sizeof( double )*nwcs ); + +/* Get the pixel->wcs Mapping. */ + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Get the table version number to use if we end up using the -TAB + algorithm. This is the set value of the TabOK attribute (if positive). */ + extver = astGetTabOK( this ); + +/* Some of the required FITS Keyword values are defined by the WcsMap + contained within the Mapping. Split the mapping up into a list of serial + component mappings, and locate the first WcsMap in this list. The first + Mapping returned by this call is the result of compounding all the + Mappings up to (but not including) the WcsMap, the second returned Mapping + is the (inverted) WcsMap, and the third returned Mapping is anything + following the WcsMap. Only proceed if one and only one WcsMap is found. */ + if( SplitMap( map, astGetInvert( map ), ilon, ilat, &map1, &map2, &map3, + status ) ){ + +/* Get the indices of the latitude and longitude axes within the SkyFrame + (not necessarily (1,0) because they may have been permuted). */ + skylataxis = astGetLatAxis( skyfrm ); + skylonaxis = astGetLonAxis( skyfrm ); + +/* The reference point in the celestial coordinate system is found by + transforming the fiducial point in native spherical co-ordinates + into WCS coordinates using map3. */ + if( GetFiducialWCS( map2, map3, ilon, ilat, skyfid + skylonaxis, + skyfid + skylataxis, status ) ){ + +/* We also need to find the indices of the longitude and latitude outputs + from the WcsMap. These may not be the same as ilat and ilon because of + axis permutations in "map3". */ + axlon = astGetWcsAxis( map2, 0 ); + axlat = astGetWcsAxis( map2, 1 ); + +/* Normalise the latitude and longitude values at the fiducial point. The + longitude and latitude values found above will be in radians, but after + normalization we convert them to degrees, as expected by other functions + which handle FitsStores. */ + if( skyfid[ skylonaxis ] == AST__BAD ) skyfid[ skylonaxis ] = 0.0; + if( skyfid[ skylataxis ] == AST__BAD ) skyfid[ skylataxis ] = 0.0; + if( ZEROANG( skyfid[ 0 ] ) ) skyfid[ 0 ] = 0.0; + if( ZEROANG( skyfid[ 1 ] ) ) skyfid[ 1 ] = 0.0; + astNorm( skyfrm, skyfid ); + SetItem( &(store->crval), fits_ilon, 0, s, AST__DR2D*skyfid[ skylonaxis ], status ); + SetItem( &(store->crval), fits_ilat, 0, s, AST__DR2D*skyfid[ skylataxis ], status ); + +/* Set a flag if we have a TPN projection. This is an AST-specific + projection which mimicks the old "TAN with correction terms" projection + which was removed from the final version of the FITS-WCS paper II. */ + tpn = ( astGetWcsType( map2 ) == AST__TPN ); + +/* Store the WCS projection parameters. Except for TPN projections, always + exclude parameters 3 and 4 on the longitude axis since these are + reserved to hold copies of LONPOLE and LATPOLE. */ + for( m = 0; m < WCSLIB_MXPAR; m++ ){ + if( astTestPV( map2, axlon, m ) ) { + if( m < 3 || m > 4 || tpn ) { + pv = astGetPV( map2, axlon, m ); + if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilon, m, + s, pv, status ); + } + } + if( astTestPV( map2, axlat, m ) ) { + pv = astGetPV( map2, axlat, m ); + if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilat, m, + s, pv, status ); + } + } + +/* If PVi_0 (for the longitude axis) is non-zero, the Cartesian coordinates + used by the WcsMap (Projection Plane Coordinates, PPC) need to be shifted + to produce Intermediate World Coordinates (IWC). This shift results in + the pixel reference position specified by the CRPIXi values (and which + corresponds to the origin of IWC) mapping on to the fiducial position + specified by the CRVALi values. The required shifts are just the PPC + coordinates of the fiducial point. The AST-specific "TPN" projection uses + longitude projection parameters to define correction terms, and so cannot + use the above convention (which is part of FITS-WCS paper II). Therefore + TPN projections always use zero shift between PPC and IWC. */ + for( iax = 0; iax < nwcs; iax++ ) ppcfid[ iax ] = 0.0; + if( !tpn && astGetPV( map2, axlon, 0 ) != 0.0 ) { + GetFiducialPPC( (AstWcsMap *) map2, ppcfid + ilon, ppcfid + ilat, status ); + if( ppcfid[ ilon ] == AST__BAD ) ppcfid[ ilon ] = 0.0; + if( ppcfid[ ilat ] == AST__BAD ) ppcfid[ ilat ] = 0.0; + ppcfid[ ilon ] *= AST__DR2D; + ppcfid[ ilat ] *= AST__DR2D; + } + +/* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */ + SkySys( this, skyfrm, 1, astGetWcsType( map2 ), store, fits_ilon, + fits_ilat, s, isoff, method, class, status ); + +/* Store the LONPOLE and LATPOLE values in the FitsStore. */ + SkyPole( map2, map3, ilon, ilat, wperm, s, store, method, class, status ); + +/* The values of LONPOLE and LATPOLE stored above (in the FitsStore) will be + ignored by WcsNative if the WcsMap contains set values for projection + parameters PVi_3a and/or PVi_4a (these will be used in preference to + the values in the FitsStore). To avoid this happening we take a copy + of the WcsMap and clear the relevant parameters (but not if the WcsMap is + for a TPN projection because TPN uses PVi_3a and PVi_4a for other + purposes). */ + if( astGetWcsType( map2 ) != AST__TPN ) { + map2b = astCopy( map2 ); + astClearPV( map2b, axlon, 3 ); + astClearPV( map2b, axlon, 4 ); + } else { + map2b = astClone( map2 ); + } + +/* We will now create the Mapping from WCS coords to IWC coords. In fact, + we produce the Mapping from IWC to WCS and then invert it. Create the + first component of this Mapping which implements any shift of origin + from IWC to PPC. */ + tmap0 = (AstMapping *) astShiftMap( nwcs, ppcfid, "", status ); + +/* The next component of this Mapping scales the PPC coords from degrees + to radians on the celestial axes. */ + mat = astMalloc( sizeof( double )*(size_t) nwcs ); + if( astOK ) { + for( iax = 0; iax < nwcs; iax++ ) mat[ iax ] = 1.0; + mat[ ilon ] = AST__DD2R; + mat[ ilat ] = AST__DD2R; + tmap1 = (AstMapping *) astMatrixMap( nwcs, nwcs, 1, mat, "", status ); + mat = astFree( mat ); + } else { + tmap1 = NULL; + } + +/* Now create the Mapping from Native Spherical Coords to WCS. */ + tmap2 = WcsNative( NULL, store, s, map2b, fits_ilon, fits_ilat, + method, class, status ); + +/* Combine the WcsMap with the above Mapping, to get the Mapping from PPC + to WCS. */ + tmap3 = (AstMapping *) astCmpMap( map2b, tmap2, 1, "", status ); + tmap2 = astAnnul( tmap2 ); + +/* If there are more WCS axes than IWC axes, create a UnitMap for the extra + WCS axes and add it in parallel with tmap3. */ + nwcsmap = astGetNin( map3 ); + if( nwcsmap < nwcs ) { + tmap2 = (AstMapping *) astUnitMap( nwcs - nwcsmap, "", status ); + tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 0, "", status ); + tmap3 = astAnnul( tmap3 ); + tmap2 = astAnnul( tmap2 ); + tmap3 = tmap4; + nwcsmap = nwcs; + } + +/* The pixel->wcs mapping may include a PermMap which selects some sub-set + or super-set of the orignal WCS axes. In this case the number of inputs + and outputs for "tmap3" created above may not equal "nwcs". To avoid this, + we embed "tmap3" between 2 PermMaps which select the required axes. */ + if( nwcsmap != nwcs || ilon != axlon || ilat != axlat ) { + inperm = astMalloc( sizeof( int )*(size_t) nwcs ); + outperm = astMalloc( sizeof( int )*(size_t) nwcsmap ); + if( astOK ) { + +/* Indicate that no inputs of the PermMap have yet been assigned to any + outputs */ + for( i = 0; i < nwcs; i++ ) inperm[ i ] = -1; + +/* Assign the WcsMap long/lat axes to the WCS Frame long/lat axes */ + inperm[ ilon ] = axlon; + inperm[ ilat ] = axlat; + +/* Assign the remaining inputs arbitrarily (doesn't matter how we do this + since the WcsMap is effectively a UnitMap on all non-celestial axes). */ + iax = 0; + for( i = 0; i < nwcs; i++ ) { + while( iax == axlon || iax == axlat ) iax++; + if( inperm[ i ] == -1 ) inperm[ i ] = iax++; + } + +/* Do the same for the outputs. */ + for( i = 0; i < nwcsmap; i++ ) outperm[ i ] = -1; + outperm[ axlon ] = ilon; + outperm[ axlat ] = ilat; + iax = 0; + for( i = 0; i < nwcsmap; i++ ) { + while( iax == ilon || iax == ilat ) iax++; + if( outperm[ i ] == -1 ) outperm[ i ] = iax++; + } + +/* Create the PermMap. */ + con = AST__BAD; + tmap2 = (AstMapping *) astPermMap( nwcs, inperm, nwcsmap, + outperm, &con, "", status ); + +/* Sandwich the WcsMap between the PermMap and its inverse. */ + tmap4 = (AstMapping *) astCmpMap( tmap2, tmap3, 1, "", status ); + tmap3 = astAnnul( tmap3 ); + astInvert( tmap2 ); + tmap3 = (AstMapping *) astCmpMap( tmap4, tmap2, 1, "", status ); + tmap2 = astAnnul( tmap2 ); + tmap4 = astAnnul( tmap4 ); + } + inperm = astFree( inperm ); + outperm = astFree( outperm ); + } + +/* Combine these Mappings together. */ + tmap4 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status ); + tmap0 = astAnnul( tmap0 ); + tmap1 = astAnnul( tmap1 ); + ret = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status ); + tmap3 = astAnnul( tmap3 ); + tmap4 = astAnnul( tmap4 ); + +/* Invert this Mapping to get the Mapping from WCS to IWC. */ + astInvert( ret ); + +/* The spherical rotation involved in converting WCS to IWC can result in + inappropriate numbering of the FITS axes. For instance, a LONPOLE + value of 90 degrees causes the IWC axes to be transposed. For this + reason we re-asses the FITS axis numbers assigned to the celestial + axes in order to make the IWC axes as close as possible to the pixel + axes with the same number (but only if the axis order is being + determined automatically). To do this, we need the Mapping from + pixel to IWC, which is formed by concatenating the pixel->WCS + Mapping with the WCS->IWC Mapping. */ + if( astChrMatch( astGetFitsAxisOrder( this ), "" ) ) { + tmap0 = (AstMapping *) astCmpMap( map, ret, 1, "", status ); + +/* Find the outputs of this Mapping which should be associated with each + input. */ + tperm = astMalloc( sizeof(int)*(size_t) nwcs ); + if( ! WorldAxes( this, tmap0, dim, tperm, status ) ) { + ret = astAnnul( ret ); + } + +/* If the index associated with the celestial axes appear to have been + swapped... */ + if( ret && astOK && fits_ilon == tperm[ ilat ] && + fits_ilat == tperm[ ilon ] ) { + +/* Swap the fits axis indices associated with each WCS axis to match. */ + wperm[ ilon ] = fits_ilat; + wperm[ ilat ] = fits_ilon; + +/* Swap the stored CRVAL value for the longitude and latitude axis. */ + val = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status ); + SetItem( &(store->crval), fits_ilat, 0, s, + GetItem( &(store->crval), fits_ilon, 0, s, NULL, + method, class, status ), status ); + SetItem( &(store->crval), fits_ilon, 0, s, val, status ); + +/* Swap the stored CTYPE value for the longitude and latitude axis. */ + cval = GetItemC( &(store->ctype), fits_ilat, 0, s, NULL, method, class, status ); + if( cval ) { + temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 ); + cval = GetItemC( &(store->ctype), fits_ilon, 0, s, NULL, method, class, status ); + if( cval ) { + SetItemC( &(store->ctype), fits_ilat, 0, s, cval, status ); + SetItemC( &(store->ctype), fits_ilon, 0, s, temp, status ); + } + temp = astFree( temp ); + } + +/* Swap the stored CNAME value for the longitude and latitude axis. */ + cval = GetItemC( &(store->cname), fits_ilat, 0, s, NULL, method, class, status ); + if( cval ) { + temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 ); + cval = GetItemC( &(store->cname), fits_ilon, 0, s, NULL, method, class, status ); + if( cval ) { + SetItemC( &(store->cname), fits_ilat, 0, s, cval, status ); + SetItemC( &(store->cname), fits_ilon, 0, s, temp, status ); + } + temp = astFree( temp ); + } + +/* Swap the projection parameters asociated with the longitude and latitude + axes. */ + maxm = GetMaxJM( &(store->pv), s, status ); + for( m = 0; m <= maxm; m++ ){ + val = GetItem( &(store->pv), fits_ilat, m, s, NULL, method, class, status ); + SetItem( &(store->pv), fits_ilat, m, s, + GetItem( &(store->pv), fits_ilon, m, s, NULL, + method, class, status ), status ); + SetItem( &(store->pv), fits_ilon, m, s, val, status ); + } + } + +/* Release resources. */ + tperm = astFree( tperm ); + tmap0 = astAnnul( tmap0 ); + } + map2b = astAnnul( map2b ); + } + +/* Release resources. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + +/* If no WcsMap was found in the pixel->WCS Mapping, it may be possible + to describe the celestial axes using a tabular look-up table (i.e. the + FITS-WCS "_TAB" algorithm). Only do this if the -TAB algorithm is to + be supported. */ + } else if( extver > 0 ) { + +/* Get any pre-existing FitsTable from the FitsStore. This is the table + in which the tabular data will be stored (if the Mapping can be expressed + in -TAB form). */ + if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL; + +/* See if the transformations for the celestial axes can be expressed in -TAB + form. The returned Mapping (if any) is the Mapping from (lon,lat) + (rads) to (psi_lon,psi_lat) (pixels). See FITS-WCS paper III section 6.1.2 + for definition of psi. Scale the values stored in the table from radians + to degrees. */ + tmap0 = IsMapTab2D( map, AST__DR2D, "deg", wcsfrm, dim, ilon, ilat, + fits_ilon, fits_ilat, &table, &icolmainlon, + &icolmainlat, &icolindexlon, &icolindexlat, + &mlon, &mlat, &interplon, &interplat, status ); + if( tmap0 ) { + +/* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */ + SkySys( this, skyfrm, 0, 0, store, fits_ilon, fits_ilat, s, isoff, + method, class, status ); + +/* If possible, choose the two CRVAL values (which are values on the psi + axes) so that transforming them using the Mapping returned by + IsMapTab2D gives the sky reference position stored in the SkyFrame. + Check the SkyFrame has a defined reference position. */ + if( astTestSkyRef( skyfrm, 0 ) && astTestSkyRef( skyfrm, 1 ) ){ + +/* Get the longitude and latitude at the reference point in radians. */ + skyfid[ 0 ] = astGetSkyRef( skyfrm, astGetLonAxis( skyfrm )); + skyfid[ 1 ] = astGetSkyRef( skyfrm, astGetLatAxis( skyfrm )); + +/* We use the WCS->psi Mapping to convert the reference point WCS coords + (rads) into psi coords (pixels). We can only do this if the WCS->psi + Mapping has a defined forward transformation. */ + if( astGetTranForward( tmap0 ) ) { + astTran2( tmap0, 1, skyfid, skyfid + 1, 1, crval, + crval + 1 ); + +/* If the WCS->psi mapping has an undefined forward transformation, then + just store the sky reference point coords (in degs) in keywords + AXREFn, and use 1.0 for the CRVAL values, so that IWC becomes equal + to (psi-1) i.e. (grid coords - 1). This means the reference point is + at grid coords (1.0,1.0). Note this choice of 1.0 for CRVAL is not + arbitrary since it is required by the trick used to create invertable CD + matrix in function MakeInvertable. */ + } else { + SetItem( &(store->axref), fits_ilon, 0, s, + AST__DR2D*skyfid[ 0 ], status ); + SetItem( &(store->axref), fits_ilat, 0, s, + AST__DR2D*skyfid[ 1 ], status ); + crval[ 0 ] = 1.0; + crval[ 1 ] = 1.0; + } + +/* If the SkyFrame has no reference position, use 1.0 for the CRVAL values. */ + } else { + crval[ 0 ] = 1.0; + crval[ 1 ] = 1.0; + } + +/* Create a Mapping that describes the transformation from the lon and lat + psi axes to the lon and lat IWC axes (i.e. a ShiftMap that just subtracts + the CRVAL values from each axis). */ + crval[ 0 ] = -crval[ 0 ]; + crval[ 1 ] = -crval[ 1 ]; + tmap1 = (AstMapping *) astShiftMap( 2, crval, " ", status ); + crval[ 0 ] = -crval[ 0 ]; + crval[ 1 ] = -crval[ 1 ]; + +/* Create a series compound Mapping that applies the Mapping returned + by IsMapTab2D first (the Mapping from WCS to psi), followed by the + Mapping from psi to IWC created above. There-after, use this compound + Mapping in place of the Mapping returned by IsMapTab2D. It maps WCS to + IWC. */ + tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, " ", status ); + (void) astAnnul( tmap0 ); + tmap1 = astAnnul( tmap1 ); + tmap0 = tmap2; + +/* Store the CRVAL values */ + SetItem( &(store->crval), fits_ilon, 0, s, crval[ 0 ], status ); + SetItem( &(store->crval), fits_ilat, 0, s, crval[ 1 ], status ); + +/* Store TAB-specific values in the FitsStore. First the name of the + FITS binary table extension holding the coordinate info. */ + SetItemC( &(store->ps), fits_ilon, 0, s, AST_TABEXTNAME, status ); + SetItemC( &(store->ps), fits_ilat, 0, s, AST_TABEXTNAME, status ); + +/* Next the table version number. This is the set (positive) value for the + TabOK attribute. */ + SetItem( &(store->pv), fits_ilon, 1, s, extver, status ); + SetItem( &(store->pv), fits_ilat, 1, s, extver, status ); + +/* Also store the table version in the binary table header. */ + astSetFitsI( table->header, "EXTVER", extver, "Table version number", + 0 ); + +/* Next the name of the table column containing the main coords array. */ + SetItemC( &(store->ps), fits_ilon, 1, s, + astColumnName( table, icolmainlon ), status ); + SetItemC( &(store->ps), fits_ilat, 1, s, + astColumnName( table, icolmainlat ), status ); + +/* Next the name of the column containing the index array. */ + if( icolindexlon >= 0 ) SetItemC( &(store->ps), fits_ilon, 2, s, + astColumnName( table, icolindexlon ), status ); + if( icolindexlat >= 0 ) SetItemC( &(store->ps), fits_ilat, 2, s, + astColumnName( table, icolindexlat ), status ); + +/* The one-based index of the axes within the coordinate array that + describes FITS WCS axes "fits_ilon" and "fits_ilat". */ + SetItem( &(store->pv), fits_ilon, 3, s, mlon, status ); + SetItem( &(store->pv), fits_ilat, 3, s, mlat, status ); + +/* The interpolation method (an AST extension to the published -TAB + algorithm, communicated through the QVi_4a keyword). */ + SetItem( &(store->pv), fits_ilon, 4, s, interplon, status ); + SetItem( &(store->pv), fits_ilat, 4, s, interplat, status ); + +/* Also store the FitsTable itself in the FitsStore. */ + astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL ); + +/* Allocate space for the arrays that define the permutations required + for the inputs and outputs of a PermMap. */ + inperm = astMalloc( sizeof( double )*nwcs ); + outperm = astMalloc( sizeof( double )*nwcs ); + if( astOK ) { + +/* Create the WCS -> IWC Mapping. First create a parallel CmpMap that + combines the Mapping returned by IsMapTab2D (which transforms the celestial + axes), with a UnitMap which transforms the non-celestial axes. */ + if( nwcs > 2 ) { + tmap1 = (AstMapping *) astUnitMap( nwcs - 2, " ", status ); + tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 0, " ", status ); + tmap1 = astAnnul( tmap1 ); + } else { + tmap2 = astClone( tmap0 ); + } + +/* Now create a PermMap that permutes the inputs of this CmpMap into the + order of the axes in the WCS Frame. */ + outperm[ 0 ] = ilon; + outperm[ 1 ] = ilat; + j = 0; + for( i = 2; i < nwcs; i++ ) { + while( j == ilon || j == ilat ) j++; + outperm[ i ] = j++; + } + for( i = 0; i < nwcs; i++ ) inperm[ outperm[ i ] ] = i; + tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm, + NULL, " ", status ); + +/* Use this PermMap (and its inverse) to permute the inputs (and outputs) + of the parallel CmpMap created above. */ + tmap3 = (AstMapping *) astCmpMap( tmap1, tmap2, 1, " ", status ); + tmap2 = astAnnul( tmap2 ); + astInvert( tmap1 ); + tmap2 = (AstMapping *) astCmpMap( tmap3, tmap1, 1, " ", status ); + tmap1 = astAnnul( tmap1 ); + tmap3 = astAnnul( tmap3 ); + +/* Now create a PermMap that permutes the WCS axes into the FITS axis order. */ + for( i = 0; i < nwcs; i++ ) { + inperm[ i ] = wperm[ i ]; + outperm[ wperm[ i ] ] = i; + } + tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm, + NULL, "", status ); + +/* Use this PermMap to permute the outputs of the "tmap2" Mapping. The + resulting Mapping is the Mapping from the current Frame to IWC and is + the Mapping to be returned as the function value. */ + ret = (AstMapping *) astCmpMap( tmap2, tmap1, 1, " ", status ); + tmap1 = astAnnul( tmap1 ); + tmap2 = astAnnul( tmap2 ); + } + +/* Free remaining resources. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + tmap0 = astAnnul( tmap0 ); + } + if( table ) table = astAnnul( table ); + } + +/* Release resources. */ + ppcfid = astFree( ppcfid ); + } + +/* Release resources. */ + wcsfrm = astAnnul( wcsfrm ); + if( skyfrm ) skyfrm = astAnnul( skyfrm ); + if( map ) map = astAnnul( map ); + +/* If we have a Mapping to return, simplify it. Otherwise, create + a UnitMap to return. */ + if( ret ) { + tmap0 = ret; + ret = astSimplify( tmap0 ); + tmap0 = astAnnul( tmap0 ); + } else { + ret = (AstMapping *) astUnitMap( nwcs, "", status ); + } + +/* Return the result. */ + return ret; +} + +static void ChangePermSplit( AstMapping *map, int *status ){ +/* +* Name: +* ChangePermSplit + +* Purpose: +* Change all PermMaps in a Mapping to use the alternate +* implementation of the astMapSplit method. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void ChangePermSplit( AstMapping *map, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The PemMap class provides two implementations of the astMapSplit +* method. The implementation used by each PermMap is determined by +* the value of the PermMap's "PermSplit" attribute. This function +* searches the supplied Mapping for any PermMaps, and set their +* PermSplit attribute to 1, indicating that the alternate +* implementation of astMapSplit should be used. + +* Parameters: +* map +* Pointer to the Mapping. Modified on exit by setting all +* PermSplit attributes to 1. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstMapping *map1; + AstMapping *map2; + int series; + int invert1; + int invert2; + +/* Check inherited status */ + if( !astOK ) return; + +/* If the supplied Mapping is a PermMap, set its PermSplit attribute + non-zero. */ + if( astIsAPermMap( map ) ) { + astSetPermSplit( map, 1 ); + +/* If the supplied Mapping is not a PermMap, attempt to decompose the + Mapping into two component Mappings. */ + } else { + astDecompose( map, &map1, &map2, &series, &invert1, &invert2 ); + +/* If the Mapping could be decomposed, use this function recursively to + set the PermSplit attributes in each component Mapping. */ + if( map1 && map2 ) { + ChangePermSplit( map1, status ); + ChangePermSplit( map2, status ); + +/* Annul the component Mappings. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + } else if( map1 ) { + map1 = astAnnul( map1 ); + } else if( map2 ) { + map2 = astAnnul( map2 ); + } + } +} + +static double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax, + double ymin, double ymax, int *status ){ +/* +* Name: +* Cheb2Poly + +* Purpose: +* Converts a two-dimensional Chebyshev polynomial to standard form and +* scale the arguments. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax, +* double ymin, double ymax, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* Given the coefficients of a two-dimensional Chebychev polynomial P(u,v), +* find the coefficients of the equivalent standard two-dimensional +* polynomial Q(x,y). The allowed range of u and v is assumed to be the +* unit square, and this maps on to the rectangle in (x,y) given by +* (xmin:xmax,ymin:ymax). + +* Parameters: +* c +* An array of (nx,ny) elements supplied holding the coefficients of +* P, such that the coefficient of (Ti(u)*Tj(v)) is held in element +* (i + j*nx), where "Ti(u)" is the Chebychev polynomial (of the +* first kind) of order "i" evaluated at "u", and "Tj(v)" is the +* Chebychev polynomial of order "j" evaluated at "v". +* nx +* One more than the maximum power of u within P. +* ny +* One more than the maximum power of v within P. +* xmin +* X value corresponding to u = -1 +* xmax +* X value corresponding to u = +1 +* ymin +* Y value corresponding to v = -1 +* ymax +* Y value corresponding to v = +1 +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a dynamically allocated array of (nx,ny) elements holding +* the coefficients of Q, such that the coefficient of (x^i*y^j) is held +* in element (i + j*nx). Free it using astFree when no longer needed. +*/ + +/* Local Variables: */ + double *d; + double *pa; + double *pw; + double *work1; + double *work2; + double *work3; + int *iw1; + int *iw2; + int i; + int j; + +/* Check the status and supplied value pointer. */ + if( !astOK ) return NULL; + +/* Allocate returned array. */ + d = astMalloc( sizeof( *d )*nx*ny ); + +/* Allocate workspace. */ + work1 = astMalloc( sizeof( *work1 )*ny ); + work2 = astMalloc( sizeof( *work2 )*ny ); + work3 = astMalloc( sizeof( *work2 )*nx ); + iw1 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) ); + iw2 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) ); + if( astOK ) { + +/* Thinking of P as a 1D polynomial in v, each coefficient would itself then + be a 1D polynomial in u: + + P = ( c[0] + c[1]*T1(u) + c[2]*T2(u) + ... ) + + ( c[nx] + c[nx+1]*T1(u) + c[nx+2]*T2(u) + ... )*T1(v) + + (c[2*nx] + c[2*nx+1]*T1(u) + c[2*nx+2]*T2(u) + ... )*T2(v) + + ... + (c[(ny-1)*nx] + c[(ny-1)*nx+1]*T1(u) + c[(ny-1)*nx+2]*T2(u) + ... )T{ny-1}(v) + + Use Chpc1 to convert these "polynomial coefficients" to standard + form, storing the result in the corresponding row of "d" . Also, + convert them from u to x. */ + + for( j = 0; j < ny; j++ ) { + Chpc1( c + j*nx, work3, nx, iw1, iw2, status ); + Shpc1( xmin, xmax, nx, work3, d + j*nx, status ); + } + +/* The polynomial value is now: + + ( d[0] + d[1]*x + d[2]*x*x + ... ) + + ( d[nx] + d[nx+1]*x + d[nx+2]*x*x + ... )*T1(v) + + (d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*T2(v) + + ... + (d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*T{ny-1}(v) + + If we rearrange this expression to view it as a 1D polynomial in x, + rather than v, each coefficient of the new 1D polynomial is then + itself a polynomial in v: + + ( d[0] + d[nx]*T1(v) + d[2*nx]*T2(v) + ... d[(ny-1)*nx]*T{ny-1}(v) ) + + ( d[1] + d[nx+1]*T1(v) + d[2*nx+1]*T2(v) + ... d[(ny-1)*nx+1]T{ny-1}(v)... )*x + + ( d[2] + d[nx+2]*T1(v) + d[2*nx+2]*T2(v) + ... d[(ny-1)*nx+2]T{ny-1}(v)... )*x*x + + ... + ( d[nx-1] + d[2*nx-1]*T1(v) + d[3*nx-1]*T2(v) + ... d[ny*nx-1]*T{ny-1}(v) )*x*x*... + + + Now use Chpc1 to convert each of these "polynomial coefficients" + to standard form. We copy each column of the d array into a 1D work array, + use Shpc1 to modify the values in the work array, and then write + the modified values back into the current column of d. Also convert + from v to y. */ + + for( i = 0; i < nx; i++ ) { + pa = d + i; + pw = work1; + for( j = 0; j < ny; j++ ) { + *(pw++) = *pa; + pa += nx; + } + + Chpc1( work1, work2, ny, iw1, iw2, status ); + Shpc1( ymin, ymax, ny, work2, work1, status ); + + pa = d + i; + pw = work1; + for( j = 0; j < ny; j++ ) { + *pa = *(pw++); + pa += nx; + } + } + +/* So the polynomial is now: + + ( d[0] + d[nx]*y + d[2*nx]*y*y + ... d[(ny-1)*nx]*y*y*... ) + + ( d[1] + d[nx+1]*y + d[2*nx+1]*y*y + ... d[(ny-1)*nx+1]*y*y*... )*x + + ( d[2] + d[nx+2]*y + d[2*nx+2]*y*y + ... d[(ny-1)*nx+2]*y*y*... )*x*x + + ... + ( d[nx-1] + d[2*nx-1]*y + d[3*nx-1]*y*y + ... d[ny*nx-1]*y*y*... )*x*x*... + + Re-arranging, this is: + + ( d[0] + d[1]*x + d[2]*x*x + ... ) + + ( d[nx] + d[nx+1]*x + d[nx+2]*x*x + ... )*y + + (d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*y*y + + ... + (d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*y*y*... + + as required. */ + + } + +/* Free the workspace. */ + work1 = astFree( work1 ); + work2 = astFree( work2 ); + work3 = astFree( work3 ); + iw1 = astFree( iw1 ); + iw2 = astFree( iw2 ); + +/* Return the result. */ + return d; +} + +static int CheckFitsName( AstFitsChan *this, const char *name, + const char *method, const char *class, int *status ){ +/* +* Name: +* CheckFitsName + +* Purpose: +* Check a keyword name conforms to FITS standards. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int CheckFitsName( AstFitsChan *this, const char *name, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* FITS keywords must contain between 1 and 8 characters, and each +* character must be an upper-case Latin alphabetic character, a digit, +* an underscore, or a hyphen. Leading, trailing or embedded white space +* is not allowed, with the exception of totally blank or null keyword +* names. +* +* If the supplied keyword name is invalid, either a warning is issued +* (for violations that can be handled - such as illegal characters in +* keywords), or an error is reported (for more major violations such +* as the keyname containing an equals sign). + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a string holding the name to check. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 0 is returned if the supplied name was blank. A value of 1 +* is returned otherwise. + +* Notes: +* - An error is reported if the supplied keyword name does not +* conform to FITS requirements, and zero is returned. +*/ + +/* Local Variables: */ + char buf[100]; /* Buffer for warning text */ + const char *c; /* Pointer to next character in name */ + size_t n; /* No. of characters in supplied name */ + int ret; /* Returned value */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise the returned value to indicate that the supplied name was + blank. */ + ret = 0; + +/* Check that the supplied pointer is not NULL. */ + if( name ){ + +/* Get the number of characters in the name. */ + n = strlen( name ); + +/* Report an error if the name has too many characters in it. */ + if( n > FITSNAMLEN ){ + astError( AST__BDFTS, "%s(%s): The supplied FITS keyword name ('%s') " + "has %d characters. FITS only allows up to %d.", status, method, + class, name, (int) n, FITSNAMLEN ); + +/* If the name has no characters in it, then assume it is a legal blank + keyword name. Otherwise, check that no illegal characters occur in the + name. */ + } else if( n != 0 ) { + +/* Whitespace is only allowed in the special case of a name consisting + entirely of whitespace. Such keywords are used to indicate that the rest + of the card is a comment. Find the first non-whitespace character in the + name. */ + c = name; + while( isspace( ( int ) *(c++) ) ); + +/* If the name is filled entirely with whitespace, then the name is acceptable + as the special case. Otherwise, we need to do more checks. */ + if( c - name - 1 < n ){ + +/* Indicate that the supplied name is not blank. */ + ret = 1; + +/* Loop round every character checking that it is one of the legal characters. + Report an error if any illegal characters are found. */ + c = name; + while( *c ){ + if( !isFits( (int) *c ) ){ + if( *c == '=' ){ + astError( AST__BDFTS, "%s(%s): An equals sign ('=') was found " + "before column %d within a FITS keyword name or header " + "card.", status, method, class, FITSNAMLEN + 1 ); + + } else if( *c < ' ' ) { + sprintf( buf, "The FITS keyword name ('%s') contains an " + "illegal non-printing character (ascii value " + "%d).", name, *c ); + Warn( this, "badkeyname", buf, method, class, status ); + + + } else if( *c > ' ' ) { + sprintf( buf, "The FITS keyword name ('%s') contains an " + "illegal character ('%c').", name, *c ); + Warn( this, "badkeyname", buf, method, class, status ); + } + break; + } + c++; + } + } + } + +/* Report an error if no pointer was supplied. */ + } else if( astOK ){ + astError( AST__INTER, "CheckFitsName(%s): AST internal error; a NULL " + "pointer was supplied for the keyword name. ", status, + astGetClass( this ) ); + } + +/* If an error has occurred, return 0. */ + if( !astOK ) ret = 0; + +/* Return the answer. */ + return ret; +} + +static void CheckZero( char *text, double value, int width, int *status ){ +/* +* Name: +* CheckZero + +* Purpose: +* Ensure that the formatted value zero has no minus sign. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void CheckZero( char *text, double value, int width, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* There is sometimes a problem (perhaps only on DEC UNIX) when formatting +* the floating-point value 0.0 using C. Sometimes it gives the string +* "-0". This function fixed this by checking the first character of +* the supplied string (if the supplied value is zero), and shunting the +* remaining text one character to the right if it is a minus sign. It +* returns without action if the supplied value is not zero. +* +* In addition, this function also rounds out long sequences of +* adjacent zeros or nines in the number. + +* Parameters: +* text +* The formatted value. +* value +* The floating value which was formatted. +* width +* The minimum field width to use. The value is right justified in +* this field width. Ignored if zero. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + char *c; + +/* Return if no text was supplied. */ + if( !text ) return; + +/* If the numerical value is zero, check for the leading minus sign. */ + if( value == 0.0 ) { + +/* Find the first non-space character. */ + c = text; + while( *c && isspace( (int) *c ) ) c++; + +/* If the first non-space character is a minus sign, replace it with a + space. */ + if( *c == '-' ) *c = ' '; + +/* Otherwise, round out sequences of zeros or nines. */ + } else { + RoundFString( text, width, status ); + } +} + +static double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s, + const char *method, const char *class, int *status ){ +/* +* Name: +* ChooseEpoch + +* Purpose: +* Choose a FITS keyword value to use for the AST Epoch attribute. + +* Type: +* Private function. + +* Synopsis: +* double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function returns an MJD value in the TDB timescale, which can +* be used as the Epoch value in an AST Frame. It uses the following +* preference order: secondary MJD-AVG, primary MJD-AVG, secondary MJD-OBS, +* primary MJD-OBS. Note, DATE-OBS keywords are converted into MJD-OBS +* keywords by the SpecTrans function before this function is called. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* A structure containing values for FITS keywords relating to +* the World Coordinate System. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* method +* The calling method. Used only in error messages. +* class +* The object class. Used only in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The MJD value. + +* Notes: +* - A value of AST__BAD is returned if an error occurs, or if none +* of the required keywords can be found in the FitsChan. +*/ + +/* Local Variables: */ + const char *timesys; /* The TIMESYS value in the FitsStore */ + double mjd; /* The returned MJD */ + +/* Initialise the returned value. */ + mjd = AST__BAD; + +/* Check the global status. */ + if( !astOK ) return mjd; + +/* Otherwise, try to get the secondary MJD-AVG value. */ + mjd = GetItem( &(store->mjdavg), 0, 0, s, NULL, method, class, status ); + +/* Otherwise, try to get the primary MJD-AVG value. */ + if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdavg), 0, 0, ' ', NULL, + method, class, status ); + +/* If the secondary MJD-OBS keyword is present in the FitsChan, gets its + value. */ + if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, s, NULL, + method, class, status ); + +/* Otherwise, try to get the primary MJD-OBS value. */ + if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, + method, class, status ); + +/* Now convert the MJD value to the TDB timescale. */ + timesys = GetItemC( &(store->timesys), 0, 0, ' ', NULL, method, class, status ); + mjd = TDBConv( mjd, TimeSysToAst( this, timesys, method, class, status ), + 0, method, class, status ); + +/* Return the answer. */ + return mjd; +} + +static void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status ){ +/* +* Name: +* Chpc1 + +* Purpose: +* Converts a one-dimensional Chebyshev polynomial to standard form. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* Given the coefficients of a one-dimensional Chebychev polynomial P(u), +* find the coefficients of the equivalent standard 1D polynomial Q(u). +* The allowed range of u is assumed to be the unit interval. + +* Parameters: +* c +* An array of n elements supplied holding the coefficients of +* P, such that the coefficient of (Ti(u)) is held in element +* (i), where "Ti(u)" is the Chebychev polynomial (of the +* first kind) of order "i" evaluated at "u". +* d +* An array of n elements returned holding the coefficients of +* Q, such that the coefficient of (u^i) is held in element (i). +* n +* One more than the highest power of u in P. +* w0 +* Pointer to a work array of n elements. +* w1 +* Pointer to a work array of n elements. +* status +* Inherited status value + +* Notes: +* - Vaguely inspired by the Numerical Recipes routine "chebpc". But the +* original had bugs, so I wrote this new version from first principles. + +*/ + +/* Local Variables: */ + int sv; + int j; + int k; + +/* Check inherited status */ + if( !astOK ) return; + +/* Initialise the returned coefficients array. */ + for( j = 0; j < n; j++ ) d[ j ] = 0.0; + +/* Use the recurrence relation + + T{k+1}(x) = 2.x.T{k}(x) - T{k-1}(x). + + w0[i] holds the coefficient of x^i in T{k-1}. w1[i] holds the + coefficient of x^i in T{k}. Initialise them for T0 (="1") and + T1 (="x"). */ + for( j = 0; j < n; j++ ) w0[ j ] = w1[ j ] = 0; + w0[ 0 ] = 1; + w1[ 1 ] = 1; + +/* Update the returned coefficients array to include the T0 and T1 terms. */ + d[ 0 ] = c[ 0 ]; + d[ 1 ] = c[ 1 ]; + +/* Loop round using the above recurrence relation until we have found + T{n-1}. */ + for( k = 1; k < n - 1; k++ ){ + +/* To get the coefficients of T{k+1} shift the contents of w1 up one + element, introducing a zero at the low end, and then double all the + values in w1. Finally subtract off the values in w0. This implements + the above recurrence relationship. Starting at the top end and working + down to the bottom, store a new value for each element of w1. */ + for( j = n - 1; j > 0; j-- ) { + +/* First save the original element of w1 in w0 for use next time. But we + also need the original w0 element later on so save it first. */ + sv = w0[ j ]; + w0[ j ] = w1[ j ]; + +/* Double the lower neighbouring w1 element and subtract off the w0 + element saved above. This forms the new value for w1. */ + w1[ j ] = 2*w1[ j - 1 ] - sv; + } + +/* Introduce a zero into the lowest element of w1, saving the original + value first in w0. Then subtract off the original value of w0. */ + sv = w0[ 0 ]; + w0[ 0 ] = w1[ 0 ]; + w1[ 0 ] = -sv; + +/* W1 now contains the coefficients of T{k+1} in w1, and the coefficients + of T{k} in w0. Multiply these by the supplied coefficient for T{k+1}, + and add them into the returned array. */ + for( j = 0; j <= k + 1; j++ ){ + d[ j ] += c[ k + 1 ]*w1[ j ]; + } + } +} + +static int ChrLen( const char *string, int *status ){ +/* +* Name: +* ChrLen + +* Purpose: +* Return the length of a string excluding any trailing white space. + +* Type: +* Private function. + +* Synopsis: +* int ChrLen( const char *string, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function returns the length of a string excluding any trailing +* white space, or non-printable characters. + +* Parameters: +* string +* Pointer to the string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The length of a string excluding any trailing white space and +* non-printable characters. + +* Notes: +* - A value of zero is returned if a NULL pointer is supplied, or if an +* error has already occurred. +*/ + +/* Local Variables: */ + const char *c; /* Pointer to the next character to check */ + int ret; /* The returned string length */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise the returned string length. */ + ret = 0; + +/* Check a string has been supplied. */ + if( string ){ + +/* Check each character in turn, starting with the last one. */ + ret = strlen( string ); + c = string + ret - 1; + while( ret ){ + if( isprint( (int) *c ) && !isspace( (int) *c ) ) break; + c--; + ret--; + } + } + +/* Return the answer. */ + return ret; +} + +static int CLASSFromStore( AstFitsChan *this, FitsStore *store, + AstFrameSet *fs, double *dim, const char *method, + const char *class, int *status ){ + +/* +* Name: +* CLASSFromStore + +* Purpose: +* Store WCS keywords in a FitsChan using FITS-CLASS encoding. + +* Type: +* Private function. + +* Synopsis: + +* int CLASSFromStore( AstFitsChan *this, FitsStore *store, +* AstFrameSet *fs, double *dim, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using FITS-CLASS encoding. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* fs +* Pointer to the FrameSet from which the values in the FitsStore +* were derived. +* dim +* Pointer to an array holding the main array dimensions (AST__BAD +* if a dimension is not known). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + AstFrame *azelfrm; /* (az,el) frame */ + AstFrame *curfrm; /* Current Frame in supplied FrameSet */ + AstFrame *freqfrm; /* Frame for reference frequency value */ + AstFrame *radecfrm; /* Spatial frame for CRVAL values */ + AstFrame *velofrm; /* Frame for reference velocity value */ + AstFrameSet *fsconv1;/* FrameSet connecting "curfrm" & "radecfrm" */ + AstFrameSet *fsconv2;/* FrameSet connecting "curfrm" & "azelfrm" */ + AstMapping *map1; /* Axis permutation to get (lonaxis,lataxis) = (0,1) */ + AstMapping *map2; /* Mapping from FITS CTYPE to (az,el) */ + AstMapping *map3; /* Mapping from (lon,lat) to (az,el) */ + char *comm; /* Pointer to comment string */ + char *cval; /* Pointer to string keyword value */ + char attbuf[20]; /* Buffer for AST attribute name */ + char combuf[80]; /* Buffer for FITS card comment */ + char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ + char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ + char s; /* Co-ordinate version character */ + char sign[2]; /* Fraction's sign character */ + char spectype[MXCTYPELEN];/* Spectral axis CTYPE */ + double *cdelt; /* Pointer to CDELT array */ + double aval[ 2 ]; /* General purpose array */ + double azel[ 2 ]; /* Reference (az,el) values */ + double cdl; /* CDELT term */ + double crval[ 3 ]; /* CRVAL values converted to rads, etc */ + double delta; /* Spectral axis increment */ + double equ; /* Epoch of reference equinox */ + double fd; /* Fraction of a day */ + double latval; /* CRVAL for latitude axis */ + double lonpole; /* LONPOLE value */ + double lonval; /* CRVAL for longitude axis */ + double mjd99; /* MJD at start of 1999 */ + double p1, p2; /* Projection parameters */ + double radec[ 2 ]; /* Reference (lon,lat) values */ + double rf; /* Rest freq (Hz) */ + double specfactor; /* Factor for converting internal spectral units */ + double val; /* General purpose value */ + double xin[ 3 ]; /* Grid coords at centre of first pixel */ + double xout[ 3 ]; /* WCS coords at centre of first pixel */ + int axlat; /* Index of latitude FITS WCS axis */ + int axlon; /* Index of longitude FITS WCS axis */ + int axspec; /* Index of spectral FITS WCS axis */ + int i; /* Axis index */ + int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ + int iymdf[ 4 ]; /* Year, month, date, fractional day */ + int j; /* Axis index */ + int jj; /* SlaLib status */ + int naxis2; /* Length of pixel axis 2 */ + int naxis3; /* Length of pixel axis 3 */ + int naxis; /* No. of axes */ + int ok; /* Is FitsSTore OK for IRAF encoding? */ + int prj; /* Projection type */ + +/* Other initialisation to avoid compiler warnings. */ + lonval = 0.0; + latval = 0.0; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Initialise */ + specfactor = 1.0; + +/* First check that the values in the FitsStore conform to the + requirements of the CLASS encoding. Assume they do not to begin with. */ + ok = 0; + +/* Just do primary axes. */ + s = ' '; + +/* Look for the primary celestial axes. */ + FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); + +/* Get the current Frame from the supplied FrameSet. */ + curfrm = astGetFrame( fs, AST__CURRENT ); + +/* Spectral and celestial axes must be present in axes 1,2 and 3. */ + if( axspec >= 0 && axspec < 3 && + axlon >= 0 && axlon < 3 && + axlat >= 0 && axlat < 3 ) { + ok = 1; + +/* If the spatial pixel axes are degenerate (i.e. span only a single + pixel), modify the CRPIX and CRVAL values in the FitsStore to put + the reference point at the centre of the one and only spatial pixel. */ + if( store->naxis >= 3 && dim[ axlon ] == 1.0 && dim[ axlat ] == 1.0 ){ + xin[ 0 ] = 1.0; + xin[ 1 ] = 1.0; + xin[ 2 ] = 1.0; + astTranN( fs, 1, 3, 1, xin, 1, 3, 1, xout ); + if( xout[ axlon ] != AST__BAD && xout[ axlat ] != AST__BAD ) { + +/* The indices of the spatial axes in the FITS header may not be the same + as the indices of the spatial axes in the WCS Frame of the supplied + FrameSet. So search the current Frame for longitude and latitude axes, + and store the corresponding elements of the "xout" array for later use. */ + for( i = 0; i < 3; i++ ) { + sprintf( attbuf, "IsLonAxis(%d)", i + 1 ); + if( astHasAttribute( curfrm, attbuf ) ) { + if( astGetI( curfrm, attbuf ) ) { + lonval = xout[ i ]; + } else { + latval = xout[ i ]; + } + } + } + +/* Store them in the FitsStore. */ + SetItem( &(store->crval), axlon, 0, ' ', lonval*AST__DR2D, status ); + SetItem( &(store->crval), axlat, 0, ' ', latval*AST__DR2D, status ); + SetItem( &(store->crpix), 0, axlon, ' ', 1.0, status ); + SetItem( &(store->crpix), 0, axlat, ' ', 1.0, status ); + } + } + +/* Get the CRVAL values for both spatial axes. */ + latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status ); + if( latval == AST__BAD ) ok = 0; + lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status ); + if( lonval == AST__BAD ) ok = 0; + +/* Get the CTYPE values for both axes. Extract the projection type as + specified by the last 4 characters in the latitude CTYPE keyword value. */ + cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else { + strcpy( lontype, cval ); + } + cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + prj = AST__WCSBAD; + } else { + strcpy( lattype, cval ); + prj = astWcsPrjType( cval + 4 ); + } + +/* Check the projection type is OK. */ + if( prj == AST__WCSBAD ){ + ok = 0; + } else if( prj != AST__SIN ){ + +/* Check the projection code is OK. */ + ok = 0; + if( prj == AST__TAN || + prj == AST__ARC || + prj == AST__STG || + prj == AST__AIT || + prj == AST__SFL ) { + ok = 1; + +/* For AIT, and SFL, check that the reference point is the origin of + the celestial co-ordinate system. */ + if( prj == AST__AIT || + prj == AST__SFL ) { + if( latval != 0.0 || lonval != 0.0 ){ + ok = 0; + +/* Change the new SFL projection code to to the older equivalent GLS */ + } else if( prj == AST__SFL ){ + (void) strcpy( lontype + 4, "-GLS" ); + (void) strcpy( lattype + 4, "-GLS" ); + +/* Change the new AIT projection code to to the older equivalent ATF */ + } else if( prj == AST__AIT ){ + (void) strcpy( lontype + 4, "-ATF" ); + (void) strcpy( lattype + 4, "-ATF" ); + } + } + } + +/* SIN projections are only acceptable if the associated projection + parameters are both zero. */ + } else { + p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status ); + p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status ); + if( p1 == AST__BAD ) p1 = 0.0; + if( p2 == AST__BAD ) p2 = 0.0; + ok = ( p1 == 0.0 && p2 == 0.0 ); + } + +/* Identify the celestial coordinate system from the first 4 characters of the + longitude CTYPE value. Only RA and galactic longitude can be stored using + FITS-CLASS. */ + if( ok && strncmp( lontype, "RA--", 4 ) && + strncmp( lontype, "GLON", 4 ) ) ok = 0; + +/* Get the CTYPE values for the spectral axis, and find the CLASS equivalent, + if possible. */ + cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else { + if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) { + strcpy( spectype, "FREQ" ); + } else { + ok = 0; + } + } + +/* If OK, check the SPECSYS value is SOURCE. */ + cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else if( ok ) { + if( strncmp( cval, "SOURCE", astChrLen( cval ) ) ) ok = 0; + } + +/* If still OK, ensure the spectral axis units are Hz. */ + cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + } else if( ok ) { + if( !strcmp( cval, "Hz" ) ) { + specfactor = 1.0; + } else if( !strcmp( cval, "kHz" ) ) { + specfactor = 1.0E3; + } else if( !strcmp( cval, "MHz" ) ) { + specfactor = 1.0E6; + } else if( !strcmp( cval, "GHz" ) ) { + specfactor = 1.0E9; + } else { + ok = 0; + } + } + } + +/* Save the number of WCS axes */ + naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; + +/* If this is larger than 3, ignore the surplus WCS axes. Note, the + above code has checked that the spatial and spectral axes are + WCS axes 0, 1 and 2. */ + if( naxis > 3 ) naxis = 3; + +/* Allocate memory to store the CDELT values */ + if( ok ) { + cdelt = (double *) astMalloc( sizeof(double)*naxis ); + if( !cdelt ) ok = 0; + } else { + cdelt = NULL; + } + +/* Check that there is no rotation, and extract the CDELT (diagonal) terms, + etc. If the spatial axes are degenerate (i.e. cover only a single pixel) + then ignore any rotation. */ + if( !GetValue( this, FormatKey( "NAXIS", axlon + 1, -1, s, status ), AST__INT, + &naxis2, 0, 0, method, class, status ) ) { + naxis2 = 0; + } + if( !GetValue( this, FormatKey( "NAXIS", axlat + 1, -1, s, status ), AST__INT, + &naxis3, 0, 0, method, class, status ) ) { + naxis3 = 0; + } + for( i = 0; i < naxis && ok; i++ ){ + cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( cdl == AST__BAD ) cdl = 1.0; + for( j = 0; j < naxis && ok; j++ ){ + val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; + val *= cdl; + if( i == j ){ + cdelt[ i ] = val; + } else if( val != 0.0 ){ + if( naxis2 != 1 || naxis3 != 1 ) ok = 0; + } + } + } + +/* Get RADECSYS and the reference equinox. */ + cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); + equ = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); + +/* If RADECSYS was available... */ + if( cval ){ + +/* Only FK4 and FK5 are supported in this encoding. */ + if( strcmp( "FK4", cval ) && strcmp( "FK5", cval ) ) ok = 0; + +/* If epoch was not available, set a default epoch. */ + if( equ == AST__BAD ){ + if( !strcmp( "FK4", cval ) ){ + equ = 1950.0; + } else if( !strcmp( "FK5", cval ) ){ + equ = 2000.0; + } else { + ok = 0; + } + +/* If an epoch was supplied, check it is consistent with the IAU 1984 + rule. */ + } else { + if( !strcmp( "FK4", cval ) ){ + if( equ >= 1984.0 ) ok = 0; + } else if( !strcmp( "FK5", cval ) ){ + if( equ < 1984.0 ) ok = 0; + } else { + ok = 0; + } + } + +/* Check we have a rest frequency */ + rf = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); + if( rf == AST__BAD ) ok = 0; + } + +/* If the spatial Frame covers more than a single Frame and requires a LONPOLE + or LATPOLE keyword, it cannot be encoded using FITS-CLASS. However since + FITS-CLASS imposes a no rotation restriction, it can tolerate lonpole + values of +/- 180 degrees. */ + if( ok && ( naxis2 != 1 || naxis3 != 1 ) ) { + lonpole = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); + if( lonpole != AST__BAD && lonpole != -180.0 && lonpole == 180 ) ok = 0; + if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ) + != AST__BAD ) ok = 0; + } + +/* Only create the keywords if the FitsStore conforms to the requirements + of the FITS-CLASS encoding. */ + if( ok ) { + +/* If celestial axes were added by MakeFitsFrameSet, we need to ensure + the header contains 3 main array axes. This is because the CLASS + encoding does not support the WCSAXES keyword. */ + if( store->naxis == 1 ) { + +/* Update the "NAXIS" value to 3 or put a new card in at the start. */ + astClearCard( this ); + i = 3; + SetValue( this, "NAXIS", &i, AST__INT, NULL, status ); + +/* Put NAXIS2/3 after NAXIS1, or after NAXIS if the FitsChan does not contain + NAXIS1. These are set to 1 since the spatial axes are degenerate. */ + if( FindKeyCard( this, "NAXIS1", method, class, status ) ) { + MoveCard( this, 1, method, class, status ); + } + i = 1; + SetValue( this, "NAXIS2", &i, AST__INT, NULL, status ); + SetValue( this, "NAXIS3", &i, AST__INT, NULL, status ); + } + +/* Find the last WCS related card. */ + FindWcs( this, 1, 1, 0, method, class, status ); + +/* Get and save CRPIX for all pixel axes. These are required, so break + if they are not available. */ + for( j = 0; j < naxis && ok; j++ ){ + val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + } else { + sprintf( combuf, "Reference pixel on axis %d", j + 1 ); + SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val, + AST__FLOAT, combuf, status ); + } + } + +/* Get and save CRVAL for all intermediate axes. These are required, so + break if they are not available. Note, the frequency axis CRVAL is + redefined by FITS-CLASS by reducing it by the RESTFREQ value. */ + for( i = 0; i < naxis && ok; i++ ){ + val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + } else { + crval[ i ] = val; + if( i == axspec ) { + val *= specfactor; + val -= rf; + } + sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); + SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, + AST__FLOAT, combuf, status ); + } + } + +/* Get and save CTYPE for all intermediate axes. These are required, so + break if they are not available. Use the potentially modified versions + saved above for the celestial axes. */ + for( i = 0; i < naxis && ok; i++ ){ + if( i == axlat ) { + cval = lattype; + } else if( i == axlon ) { + cval = lontype; + } else if( i == axspec ) { + cval = spectype; + } else { + cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + } + if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) { + comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + if( !comm ) { + sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); + comm = combuf; + } + SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, + AST__STRING, comm, status ); + } else { + ok = 0; + } + } + +/* CDELT values */ + if( axspec != -1 ) cdelt[ axspec ] *= specfactor; + for( i = 0; i < naxis; i++ ){ + SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i, + AST__FLOAT, "Pixel size", status ); + } + +/* Reference equinox */ + if( equ != AST__BAD ) SetValue( this, "EQUINOX", &equ, AST__FLOAT, + "Epoch of reference equinox", status ); + +/* Date of observation. */ + val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD ) { + +/* The format used for the DATE-OBS keyword depends on the value of the + keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. + Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ + palCaldj( 99, 1, 1, &mjd99, &jj ); + if( val < mjd99 ) { + palDjcal( 0, val, iymdf, &jj ); + sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], + iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); + } else { + palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); + palDd2tf( 3, fd, sign, ihmsf ); + sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", + iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], + ihmsf[2], ihmsf[3] ); + } + +/* Now store the formatted string in the FitsChan. */ + cval = combuf; + SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, + "Date of observation", status ); + } + +/* Rest frequency */ + SetValue( this, "RESTFREQ", &rf, AST__FLOAT, "[Hz] Rest frequency", status ); + +/* The image frequency corresponding to the rest frequency (only used for + double sideband data). */ + val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status ); + } + +/* Ensure the FitsChan contains OBJECT and LINE headers */ + if( !HasCard( this, "OBJECT", method, class, status ) ) { + cval = " "; + SetValue( this, "OBJECT", &cval, AST__STRING, NULL, status ); + } + if( !HasCard( this, "LINE", method, class, status ) ) { + cval = " "; + SetValue( this, "LINE", &cval, AST__STRING, NULL, status ); + } + +/* CLASS expects the VELO-LSR keyword to hold the radio velocity of the + reference channel (NOT of the source as I was told!!) with respect to + the LSRK rest frame. The "crval" array holds the frequency of the + reference channel in the source rest frame, so we need to convert this + to get the value for VELO-LSR. Create a SpecFrame describing the + required frame (other attributes such as Epoch etc are left unset and + so will be picked up from the supplied FrameSet). We set MinAxes + and MaxAxes so that the Frame can be used as a template to match the + 1D or 3D current Frame in the supplied FrameSet. */ + velofrm = (AstFrame *) astSpecFrame( "System=vrad,StdOfRest=lsrk," + "Unit=m/s,MinAxes=1,MaxAxes=3", status ); + +/* Find the spectral axis within the current Frame of the supplied + FrameSet, using the above "velofrm" as a template. */ + fsconv1 = astFindFrame( curfrm, velofrm, "" ); + +/* If OK, extract the SpecFrame from the returned FraneSet (this will + have the attribute values that were assigned explicitly to "velofrm" + and will have inherited all unset attributes from the supplied + FrameSet). */ + if( fsconv1 ) { + velofrm = astAnnul( velofrm ); + velofrm = astGetFrame( fsconv1, AST__CURRENT ); + fsconv1 = astAnnul( fsconv1 ); + +/* Take a copy of the velofrm and modify its attributes so that it + describes frequency in the sources rest frame in units of Hz. This is + the system that CLASS expects for the CRVAL3 keyword. */ + freqfrm = astCopy( velofrm ); + astSet( freqfrm, "System=freq,StdOfRest=Source,Unit=Hz", status ); + +/* Get a Mapping from frequency to velocity. */ + fsconv1 = astConvert( freqfrm, velofrm, "" ); + if( fsconv1 ) { + +/* Use this Mapping to convert the spectral crval value from frequency to + velocity. Also convert the value for the neighbouring channel. */ + aval[ 0 ] = crval[ axspec ]*specfactor; + aval[ 1 ] = aval[ 0 ] + cdelt[ axspec ]*specfactor; + astTran1( fsconv1, 2, aval, 1, aval ); + +/* Store the value. Also store it as VLSR since this keyword seems to be + used for the same thing. */ + SetValue( this, "VELO-LSR", aval, AST__FLOAT, "[m/s] Reference velocity", status ); + SetValue( this, "VLSR", aval, AST__FLOAT, "[m/s] Reference velocity", status ); + +/* The DELTAV keyword holds the radio velocity channel spacing in the + LSR. */ + delta = aval[ 1 ] - aval[ 0 ]; + SetValue( this, "DELTAV", &delta, AST__FLOAT, "[m/s] Velocity resolution", status ); + +/* Free remaining resources. */ + fsconv1 = astAnnul( fsconv1 ); + } + } + velofrm = astAnnul( velofrm ); + +/* AZIMUTH and ELEVATIO - the (az,el) equivalent of CRVAL. We need a + Mapping from the CTYPE spatial system to (az,el). This depends on all + the extra info like telescope position, epoch, etc. This info is in + the current Frame in the supplied FrameSet. First get a conversion + from a sky frame with default axis ordering to the supplied Frame. All + the extra info is picked up from the supplied Frame since it is not set + in the template. */ + radecfrm = (AstFrame *) astSkyFrame( "Permute=0,MinAxes=3,MaxAxes=3", status ); + fsconv1 = astFindFrame( curfrm, radecfrm, "" ); + +/* Now get conversion from the an (az,el) Frame to the supplied Frame. */ + azelfrm = (AstFrame *) astSkyFrame( "System=AZEL,Permute=0,MinAxes=3,MaxAxes=3", status ); + fsconv2 = astFindFrame( curfrm, azelfrm, "" ); + +/* If both conversions werew possible, concatenate their Mappings to get + a Mapping from (lon,lat) in the CTYPE system, to (az,el). */ + if( fsconv1 && fsconv2 ) { + map1 = astGetMapping( fsconv1, AST__CURRENT, AST__BASE ); + map2 = astGetMapping( fsconv2, AST__BASE, AST__CURRENT ); + map3 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + +/* Store the CRVAL (ra,dec) values in the default order. */ + radec[ 0 ] = crval[ axlon ]*AST__DD2R; + radec[ 1 ] = crval[ axlat ]*AST__DD2R; + +/* Transform to (az,el), normalise, convert to degrees and store. */ + astTranN( map3, 1, 2, 1, radec, 1, 2, 1, azel ); + if( azel[ 0 ] != AST__BAD && azel[ 1 ] != AST__BAD ) { + astNorm( azelfrm, azel ); + azel[ 0 ] *= AST__DR2D; + azel[ 1 ] *= AST__DR2D; + SetValue( this, "AZIMUTH", azel, AST__FLOAT, "[Deg] Telescope azimuth", status ); + SetValue( this, "ELEVATIO", azel + 1, AST__FLOAT, "[Deg] Telescope elevation", status ); + } + +/* Free resources */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + fsconv1 = astAnnul( fsconv1 ); + fsconv2 = astAnnul( fsconv2 ); + } + radecfrm = astAnnul( radecfrm ); + azelfrm = astAnnul( azelfrm ); + } + curfrm = astAnnul( curfrm ); + +/* Release CDELT workspace */ + if( cdelt ) cdelt = (double *) astFree( (void *) cdelt ); + +/* Return zero or ret depending on whether an error has occurred. */ + return astOK ? ok : 0; +} + +static void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat, + int axlon, const char *method, const char *class, int *status ){ + +/* +* Name: +* ClassTrans + +* Purpose: +* Translated non-standard FITS-CLASS headers into equivalent standard +* ones. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat, +* int axlon, const char *method, const char *class ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function extends the functionality of the SpecTrans function, +* by converting non-standard WCS keywords into standard FITS-WCS +* keywords, using the conventions of the FITS-CLASS encoding. + +* Parameters: +* this +* Pointer to the FitsChan containing the original header cards. +* ret +* Pointer to a FitsChan in which to return the standardised header +* cards. +* axlat +* Zero-based index of the celestial latitude axis. +* axlon +* Zero-based index of the celestial longitude axis. +* method +* Pointer to string holding name of calling method. +* class +* Pointer to a string holding the name of the supplied object class. +*/ + +/* Local Variables: */ + char *cval; /* Pointer to character string */ + char newtype[ 10 ]; /* New CTYPE value */ + const char *keyname; /* Pointer to keyword name */ + const char *ssyssrc; /* Pointer to SSYSSRC keyword value string */ + double crval; /* CRVAL value */ + double restfreq; /* Rest frequency (Hz) */ + double v0; /* Ref channel velocity in source frame */ + double vref; /* Ref channel velocity in LSR or whatever */ + double vsource; /* Source velocity */ + double zsource; /* Source redshift */ + int axspec; /* Index of spectral axis */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the rest frequency. */ + restfreq = AST__BAD; + if( !GetValue2( ret, this, "RESTFRQ", AST__FLOAT, (void *) &restfreq, 0, + method, class, status ) ){ + GetValue2( ret, this, "RESTFREQ", AST__FLOAT, (void *) &restfreq, 0, + method, class, status ); + } + if( restfreq == AST__BAD ) { + astError( AST__BDFTS, "%s(%s): Keyword RESTFREQ not found in CLASS " + "FITS header.", status, method, class ); + } + +/* Get the index of the spectral axis. */ + if( axlat + axlon == 1 ) { + axspec = 2; + } else if( axlat + axlon == 3 ) { + axspec = 0; + } else { + axspec = 1; + } + +/* Get the spectral CTYPE value */ + if( GetValue2( ret, this, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ), + AST__STRING, (void *) &cval, 0, method, class, status ) ){ + +/* We can only handle frequency axes at the moment. */ + if( !astChrMatch( "FREQ", cval ) ) { + astError( AST__BDFTS, "%s(%s): FITS-CLASS keyword %s has value " + "\"%s\" - CLASS support in AST only includes \"FREQ\" axes.", status, + method, class, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ), + cval ); + +/* CRVAL for the spectral axis needs to be incremented by RESTFREQ if the + axis represents frequency. */ + } else { + keyname = FormatKey( "CRVAL", axspec + 1, -1, ' ', status ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &crval, 1, + method, class, status ) ) { + crval += restfreq; + SetValue( ret, keyname, (void *) &crval, AST__FLOAT, NULL, status ); + } + } + +/* CLASS frequency axes describe source frame frequencies. */ + cval = "SOURCE"; + SetValue( ret, "SPECSYS", (void *) &cval, AST__STRING, NULL, status ); + } + +/* If no projection code is supplied for the longitude and latitude axes, + use "-GLS". This will be translated to "-SFL" by SpecTrans. */ + keyname = FormatKey( "CTYPE", axlon + 1, -1, ' ', status ); + if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method, + class, status ) ){ + if( strlen(cval) > 4 && !strncmp( " ", cval + 4, 4 ) ) { + strncpy( newtype, cval, 4 ); + strcpy( newtype + 4, "-GLS" ); + cval = newtype; + SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status ); + } + } + keyname = FormatKey( "CTYPE", axlat + 1, -1, ' ', status ); + if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method, + class, status ) ){ + if( strlen(cval) > 4 && !strncmp( " ", cval + 4, 4 ) ) { + strncpy( newtype, cval, 4 ); + strcpy( newtype + 4, "-GLS" ); + cval = newtype; + SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status ); + } + } + +/* Look for a keyword with name "VELO-...". This specifies the radio velocity + at the reference channel, in a standard of rest specified by the "..." + in the keyword name. If "VELO-..." is not found, look for "VLSR", + which is the same as "VELO-LSR". */ + if( GetValue2( ret, this, "VELO-%3c", AST__FLOAT, (void *) &vref, 0, + method, class, status ) || + GetValue2( ret, this, "VLSR", AST__FLOAT, (void *) &vref, 0, + method, class, status ) ){ + +/* Calculate the radio velocity (in the rest frame of the source) corresponding + to the frequency at the reference channel. */ + v0 = AST__C*( restfreq - crval )/restfreq; + +/* Assume that the source velocity is the difference between this velocity + and the reference channel velocity given by "VELO-..." */ + vsource = vref - v0; + +/* Get the keyword name and find the corresponding SSYSSRC keyword value. */ + keyname = CardName( this, status ); + if( !strcmp( keyname, "VELO-HEL" ) ) { + ssyssrc = "BARYCENT"; + } else if( !strcmp( keyname, "VELO-OBS" ) || !strcmp( keyname, "VELO-TOP" ) ) { + ssyssrc = "TOPOCENT"; + } else if( !strcmp( keyname, "VELO-EAR" ) || !strcmp( keyname, "VELO-GEO" ) ) { + ssyssrc = "GEOCENTR"; + } else { + ssyssrc = "LSRK"; + } + SetValue( ret, "SSYSSRC", (void *) &ssyssrc, AST__STRING, NULL, status ); + +/* Convert from radio velocity to redshift and store as ZSOURCE */ + zsource = ( AST__C / (AST__C - vsource) ) - 1.0; + SetValue( ret, "ZSOURCE", (void *) &zsource, AST__FLOAT, NULL, status ); + } +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the astClearAttrib protected +* method inherited from the Channel class). + +* Description: +* This function clears the value of a specified attribute for a +* FitsChan, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the FitsChan. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Card. */ +/* ----- */ + if ( !strcmp( attrib, "card" ) ) { + astClearCard( this ); + +/* Encoding. */ +/* --------- */ + } else if ( !strcmp( attrib, "encoding" ) ) { + astClearEncoding( this ); + +/* CDMatrix */ +/* -------- */ + } else if ( !strcmp( attrib, "cdmatrix" ) ) { + astClearCDMatrix( this ); + +/* FitsAxisOrder. */ +/* ----------- */ + } else if ( !strcmp( attrib, "fitsaxisorder" ) ) { + astClearFitsAxisOrder( this ); + +/* FitsDigits. */ +/* ----------- */ + } else if ( !strcmp( attrib, "fitsdigits" ) ) { + astClearFitsDigits( this ); + +/* DefB1950 */ +/* -------- */ + } else if ( !strcmp( attrib, "defb1950" ) ) { + astClearDefB1950( this ); + +/* TabOK */ +/* ----- */ + } else if ( !strcmp( attrib, "tabok" ) ) { + astClearTabOK( this ); + +/* CarLin */ +/* ------ */ + } else if ( !strcmp( attrib, "carlin" ) ) { + astClearCarLin( this ); + +/* PolyTan */ +/* ------- */ + } else if ( !strcmp( attrib, "polytan" ) ) { + astClearPolyTan( this ); + +/* Iwc */ +/* --- */ + } else if ( !strcmp( attrib, "iwc" ) ) { + astClearIwc( this ); + +/* Clean */ +/* ----- */ + } else if ( !strcmp( attrib, "clean" ) ) { + astClearClean( this ); + +/* Warnings. */ +/* -------- */ + } else if ( !strcmp( attrib, "warnings" ) ) { + astClearWarnings( this ); + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class. If it does, then report an + error. */ + } else if ( astOK && ( !strcmp( attrib, "ncard" ) || + !strcmp( attrib, "allwarnings" ) ) ){ + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearCard( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* astClearCard + +* Purpose: +* Clear the Card attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* void astClearCard( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function clears the Card attribute for the supplied FitsChan by +* setting it to the index of the first un-used card in the FitsChan. +* This causes the next read operation performed on the FitsChan to +* read the first card. Thus, it is equivalent to "rewinding" the FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*- +*/ + +/* Local Variables; */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Check the supplied FitsChan. If its is empty, return. */ + if ( !this || !(this->head) ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Set the pointer to the current card so that it points to the card at + the head of the list. */ + this->card = this->head; + +/* If the current card has been read into an AST object, move on to the + first card which has not, unless we are not skipping such cards. */ + if( CARDUSED(this->card) ){ + MoveCard( this, 1, "astClearCard", astGetClass( this ), status ); + } +} + +static int CnvValue( AstFitsChan *this, int type, int undef, void *buff, + const char *method, int *status ){ + +/* +* +* Name: +* CnvValue + +* Purpose: +* Convert a data value into a given FITS data type. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int CnvValue( AstFitsChan *this, int type, int undef, void *buff, +* const char *method, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function produces a copy of the data value for the current card +* converted from its stored data type to the supplied data type. + +* Parameters: +* this +* Pointer to the FitsChan. +* type +* The FITS data type in which to return the data value of the +* current card. +* undef +* Determines what happens if the current card has an undefined +* value. If "undef" is zero, an error will be reported identifying +* the undefined keyword value. If "undef" is non-zero, no error is +* reported and the contents of the output buffer are left unchanged. +* buf +* A pointer to a buffer to recieve the converted value. It is the +* responsibility of the caller to ensure that a suitable buffer is +* supplied. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the conversion was not possible (in which case NO error is +* reported), one otherwise. + +* Notes: +* - When converting from floating point to integer, the floating +* point value is truncated using a C cast. +* - Non-zero numerical values are considered TRUE, and zero +* numerical values are considered FALSE. Any string starting with a +* 'T' or a 'Y' (upper or lower case) is considered TRUE, and anything +* starting with an 'F' or an 'N' (upper or lower case) is considered +* FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an +* 'F'. +* - A logical TRUE value is represented as a real numerical value of +* one and the character string "Y". A logical FALSE value is represented +* by a real numerical value of zero and the character string "N". +* - When converting from a string to any numerical value, zero is +* returned if the string is not a formatted value which can be converted +* into the corresponding type using astSscanf. +* - Real and imaginary parts of a complex value should be separated by +* spaces within strings. If a string does contains only a single numerical +* value, it is assumed to be the real part, and the imaginary part is +* assumed to be zero. +* - When converting a complex numerical type to a non-complex numerical +* type, the returned value is derived from the real part only, the +* imaginary part is ignored. +* - Zero is returned if an error has occurred, or if this function +* should fail for any reason. +* - If the supplied value is undefined an error will be reported. +*/ + +/* Local Variables: */ + int otype; /* Stored data type */ + size_t osize; /* Size of stored data */ + void *odata; /* Pointer to stored data */ + +/* Check the global error status, and the supplied buffer. */ + if ( !astOK || !buff ) return 0; + +/* Get the type in which the data value is stored. */ + otype = CardType( this, status ); + +/* Get a pointer to the stored data value, and its size. */ + osize = 0; + odata = CardData( this, &osize, status ); + +/* Do the conversion. */ + return CnvType( otype, odata, osize, type, undef, buff, + CardName( this, status ), method, astGetClass( this ), + status ); +} + +static int CnvType( int otype, void *odata, size_t osize, int type, int undef, + void *buff, const char *name, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* CnvType + +* Purpose: +* Convert a data value into a given FITS data type. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int CnvType( int otype, void *odata, size_t osize, int type, int undef, +* void *buff, const char *name, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function produces a copy of the data value for the current card +* converted from its stored data type to the supplied data type. + +* Parameters: +* otype +* The type of the supplied data value. +* odata +* Pointer to a buffer holding the supplied data value. +* osize +* The size of the data value (in bytes - strings include the +* terminating null). +* type +* The FITS data type in which to return the data value of the +* current card. +* undef +* Determines what happens if the supplied data value type is +* undefined If "undef" is zero, an error will be reported identifying +* the undefined keyword value. If "undef" is non-zero, no error is +* reported and the contents of the output buffer are left unchanged. +* buff +* A pointer to a buffer to recieve the converted value. It is the +* responsibility of the caller to ensure that a suitable buffer is +* supplied. +* name +* A pointer to a string holding a keyword name to include in error +* messages. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the conversion was not possible (in which case NO error is +* reported), one otherwise. + +* Notes: +* - When converting from floating point to integer, the floating +* point value is truncated using a C cast. +* - Non-zero numerical values are considered TRUE, and zero +* numerical values are considered FALSE. Any string starting with a +* 'T' or a 'Y' (upper or lower case) is considered TRUE, and anything +* starting with an 'F' or an 'N' (upper or lower case) is considered +* FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an +* 'F'. +* - A logical TRUE value is represented as a real numerical value of +* one and the character string "Y". A logical FALSE value is represented +* by a real numerical value of zero and the character string "N". +* - When converting from a string to any numerical value, zero is +* returned if the string isn not a formatted value which can be converted +* into the corresponding type using astSscanf. +* - Real and imaginary parts of a complex value should be separated by +* spaces within strings. If a string does contains only a single numerical +* value, it is assumed to be the real part, and the imaginary part is +* assumed to be zero. +* - When converting a complex numerical type to a non-complex numerical +* type, the returned value is derived from the real part only, the +* imaginary part is ignored. +* - Zero is returned if an error has occurred, or if this function +* should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *c; /* Pointer to next character */ + const char *ostring; /* String data value */ + double odouble; /* Double data value */ + int oint; /* Integer data value */ + int ival; /* Integer value read from string */ + int len; /* Length of character string */ + int nc; /* No. of characetsr used */ + int ret; /* Returned success flag */ + +/* Check the global error status, and the supplied buffer. */ + if ( !astOK || !buff ) return 0; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Assume success. */ + ret = 1; + +/* If the supplied data type is undefined, report an error unless the + returned data type is also undefined or an undefined value is + acceptable for the keyword. */ + if( otype == AST__UNDEF ) { + if( type != AST__UNDEF && !undef ) { + ret = 0; + astError( AST__FUNDEF, "The FITS keyword '%s' has an undefined " + "value.", status, name ); + } + +/* If the returned data type is undefined, the returned value is + immaterial, so leave the buffer contents unchanged. */ + } else if( type == AST__UNDEF ) { + +/* If there is no data value and this is not a COMMENT keyword, or if + there is a data value and this is a COMMENT card, conversion is not + possible. */ + } else if( ( odata && otype == AST__COMMENT ) || + ( !odata && otype != AST__COMMENT ) ) { + ret = 0; + +/* If there is no data value (and therefore this is a comment card), + conversion is only possible if the output type is also a comment. */ + } else if( !odata ) { + if( type != AST__COMMENT ) ret = 0; + +/* Otherwise we have a data value, so do each possible combination of + supplied and stored data types... */ + } else { + +/* Convert a AST__FLOAT data value to ... */ + if( otype == AST__FLOAT ){ + odouble = *( (double *) odata ); + if( type == AST__FLOAT ){ + (void) memcpy( buff, odata, osize ); + } else if( type == AST__STRING || type == AST__CONTINUE ){ + if( odouble != AST__BAD ) { + (void) sprintf( cnvtype_text, "%.*g", DBL_DIG, odouble ); + CheckZero( cnvtype_text, odouble, 0, status ); + } else { + strcpy( cnvtype_text, BAD_STRING ); + } + *( (char **) buff ) = cnvtype_text; + } else if( type == AST__INT ){ + *( (int *) buff ) = (int) odouble; + } else if( type == AST__LOGICAL ){ + *( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1; + } else if( type == AST__COMPLEXF ){ + ( (double *) buff )[ 0 ] = odouble; + ( (double *) buff )[ 1 ] = 0.0; + } else if( type == AST__COMPLEXI ){ + ( (int *) buff )[ 0 ] = (int) odouble; + ( (int *) buff )[ 1 ] = 0; + } else if( astOK ){ + ret = 0; + astError( AST__INTER, "CnvType: AST internal programming error - " + "FITS data-type no. %d not yet supported.", status, type ); + } + +/* Convert a AST__STRING data value to ... */ + } else if( otype == AST__STRING || type == AST__CONTINUE ){ + ostring = (char *) odata; + len = (int) strlen( ostring ); + if( type == AST__FLOAT ){ + if( nc = 0, + ( 0 == astSscanf( ostring, BAD_STRING " %n", &nc ) ) + && (nc >= len ) ){ + *( (double *) buff ) = AST__BAD; + } else if( nc = 0, + ( 1 != astSscanf( ostring, "%lf %n", (double *) buff, &nc ) ) + || (nc < len ) ){ + ret = 0; + } + } else if( type == AST__STRING || type == AST__CONTINUE ){ + strncpy( cnvtype_text, (char *) odata, AST__FITSCHAN_FITSCARDLEN ); + *( (char **) buff ) = cnvtype_text; + } else if( type == AST__INT ){ + if( nc = 0, + ( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) ) + || (nc < len ) ){ + ret = 0; + } + } else if( type == AST__LOGICAL ){ + if( nc = 0, + ( 1 == astSscanf( ostring, "%d %n", &ival, &nc ) ) + && (nc >= len ) ){ + *( (int *) buff ) = ival ? 1 : 0; + } else { + c = ostring; + while( *c && isspace( (int) *c ) ) c++; + if( *c == 'y' || *c == 'Y' || *c == 't' || *c == 'T' || + ( *c == '.' && ( c[1] == 't' || c[1] == 'T' ) ) ){ + *( (int *) buff ) = 1; + } else if( *c == 'n' || *c == 'N' || *c == 'f' || *c == 'F' || + ( *c == '.' && ( c[1] == 'f' || c[1] == 'F' ) ) ){ + *( (int *) buff ) = 0; + } else { + ret = 0; + } + } + } else if( type == AST__COMPLEXF ){ + if( nc = 0, + ( 1 != astSscanf( ostring, "%lf %lf %n", (double *) buff, + (double *) buff + 1, &nc ) ) + || (nc < len ) ){ + if( nc = 0, + ( 1 != astSscanf( ostring, "%lf %n", (double *) buff, + &nc ) ) + || (nc < len ) ){ + ret = 0; + } else { + ( (double *) buff )[ 1 ] = 0.0; + } + } + } else if( type == AST__COMPLEXI ){ + if( nc = 0, + ( 1 != astSscanf( ostring, "%d %d %n", (int *) buff, + (int *) buff + 1, &nc ) ) + || (nc < len ) ){ + if( nc = 0, + ( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) ) + || (nc < len ) ){ + ret = 0; + } else { + ( (int *) buff )[ 1 ] = 0; + } + } + } else if( astOK ){ + ret = 0; + astError( AST__INTER, "CnvType: AST internal programming error - " + "FITS data-type no. %d not yet supported.", status, type ); + } + +/* Convert an AST__INT data value to ... */ + } else if( otype == AST__INT ){ + oint = *( (int *) odata ); + if( type == AST__FLOAT ){ + *( (double *) buff ) = (double) oint; + } else if( type == AST__STRING || type == AST__CONTINUE ){ + (void) sprintf( cnvtype_text, "%d", oint ); + *( (char **) buff ) = cnvtype_text; + } else if( type == AST__INT ){ + (void) memcpy( buff, odata, osize ); + } else if( type == AST__LOGICAL ){ + *( (int *) buff ) = oint ? 1 : 0; + } else if( type == AST__COMPLEXF ){ + ( (double *) buff )[ 0 ] = (double) oint; + ( (double *) buff )[ 1 ] = 0.0; + } else if( type == AST__COMPLEXI ){ + ( (int *) buff )[ 0 ] = oint; + ( (int *) buff )[ 1 ] = 0; + } else if( astOK ){ + ret = 0; + astError( AST__INTER, "CnvType: AST internal programming error - " + "FITS data-type no. %d not yet supported.", status, type ); + } + +/* Convert a LOGICAL data value to ... */ + } else if( otype == AST__LOGICAL ){ + oint = *( (int *) odata ); + if( type == AST__FLOAT ){ + *( (double *) buff ) = oint ? 1.0 : 0.0; + } else if( type == AST__STRING || type == AST__CONTINUE ){ + if( oint ){ + strcpy( cnvtype_text, "Y" ); + } else { + strcpy( cnvtype_text, "N" ); + } + *( (char **) buff ) = cnvtype_text; + } else if( type == AST__INT ){ + *( (int *) buff ) = oint; + } else if( type == AST__LOGICAL ){ + (void) memcpy( buff, odata, osize ); + } else if( type == AST__COMPLEXF ){ + ( (double *) buff )[ 0 ] = oint ? 1.0 : 0.0; + ( (double *) buff )[ 1 ] = 0.0; + } else if( type == AST__COMPLEXI ){ + ( (int *) buff )[ 0 ] = oint ? 1 : 0; + ( (int *) buff )[ 1 ] = 0; + } else if( astOK ){ + ret = 0; + astError( AST__INTER, "CnvType: AST internal programming error - " + "FITS data-type no. %d not yet supported.", status, type ); + } + +/* Convert a AST__COMPLEXF data value to ... */ + } else if( otype == AST__COMPLEXF ){ + odouble = ( (double *) odata )[ 0 ]; + if( type == AST__FLOAT ){ + *( (double *) buff ) = odouble; + } else if( type == AST__STRING || type == AST__CONTINUE ){ + (void) sprintf( cnvtype_text0, "%.*g", DBL_DIG, ( (double *) odata )[ 0 ] ); + CheckZero( cnvtype_text0, ( (double *) odata )[ 0 ], 0, status ); + (void) sprintf( cnvtype_text1, "%.*g", DBL_DIG, ( (double *) odata )[ 1 ] ); + CheckZero( cnvtype_text1, ( (double *) odata )[ 1 ], 0, status ); + (void) sprintf( cnvtype_text, "%s %s", cnvtype_text0, cnvtype_text1 ); + *( (char **) buff ) = cnvtype_text; + } else if( type == AST__INT ){ + *( (int *) buff ) = (int) odouble; + } else if( type == AST__LOGICAL ){ + *( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1; + } else if( type == AST__COMPLEXF ){ + (void) memcpy( buff, odata, osize ); + } else if( type == AST__COMPLEXI ){ + ( (int *) buff )[ 0 ] = (int) odouble; + ( (int *) buff )[ 1 ] = (int) ( (double *) odata )[ 1 ]; + } else if( astOK ){ + ret = 0; + astError( AST__INTER, "CnvType: AST internal programming error - " + "FITS data-type no. %d not yet supported.", status, type ); + } + +/* Convert a AST__COMPLEXI data value to ... */ + } else if( otype == AST__COMPLEXI ){ + oint = ( (int *) odata )[ 0 ]; + if( type == AST__FLOAT ){ + *( (double *) buff ) = (double) oint; + } else if( type == AST__STRING || type == AST__CONTINUE ){ + (void) sprintf( cnvtype_text, "%d %d", ( (int *) odata )[ 0 ], + ( (int *) odata )[ 1 ] ); + *( (char **) buff ) = cnvtype_text; + } else if( type == AST__INT ){ + *( (int *) buff ) = oint; + } else if( type == AST__LOGICAL ){ + *( (int *) buff ) = oint ? 1 : 0; + } else if( type == AST__COMPLEXF ){ + ( (double *) buff )[ 0 ] = (double) oint; + ( (double *) buff )[ 1 ] = (double) ( (int *) odata )[ 1 ]; + } else if( type == AST__COMPLEXI ){ + (void) memcpy( buff, odata, osize ); + } else if( astOK ){ + ret = 0; + astError( AST__INTER, "CnvType: AST internal programming error - " + "FITS data-type no. %d not yet supported.", status, type ); + } + } else if( astOK ){ + ret = 0; + astError( AST__INTER, "CnvType: AST internal programming error - " + "FITS data-type no. %d not yet supported.", status, type ); + } + } + return ret; +} + +static int ComBlock( AstFitsChan *this, int incr, const char *method, + const char *class, int *status ){ + +/* +* Name: +* ComBlock + +* Purpose: +* Delete a AST comment block in a Native-encoded FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int ComBlock( AstFitsChan *this, int incr, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function looks for a block of comment cards as defined below, +* and deletes all the cards in the block, if a suitable block is found. +* +* Comment blocks consist of a contiguous sequence of COMMENT cards. The +* text of each card should start and end with the 3 characters "AST". +* The block is delimited above by a card containing all +'s (except +* for the two "AST" strings), and below by a card containing all -'s. +* +* The block is assumed to start on the card which is adjacent to the +* current card on entry. + +* Parameters: +* this +* Pointer to the FitsChan. +* incr +* This should be either +1 or -1, and is the increment between +* adjacent cards in the comment block. A value of +1 means +* that the card following the current card is taken as the first in +* the block, and subsequent cards are checked. The block must then +* end with a line of -'s. If -1 is supplied, then the card +* preceding the current card is taken as the first in the block, +* and preceding cards are checked. The block must then end with +* a row of +'s. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if a block was found and deleted, 0 otherwise. + +* Notes: +* - The pointer to the current card is returned unchanged. +*/ + +/* Local Variables: */ + FitsCard *card0; /* Pointer to current FitsCard on entry */ + char del; /* Delimiter character */ + char *text; /* Pointer to the comment text */ + int i; /* Card index within the block */ + int ncard; /* No. of cards in the block */ + int ret; /* The returned flag */ + size_t len; /* Length of the comment text */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Save the pointer to the current card. */ + card0 = this->card; + +/* Initialise the returned flag to indicate that we have not found a + comment block. */ + ret = 0; + +/* Move on to the first card in the block. If this is not possible (due to + us already being at the start or end of the FitsChan), then return. */ + if( MoveCard( this, incr, method, class, status ) == 1 ) { + +/* Store the character which is used in the delimiter line for the + comment block. */ + del = ( incr == 1 ) ? '-' : '+'; + +/* Initialise the number of cards in the comment block to zero. */ + ncard = 0; + +/* Loop round until the end (or start) of the comment block is found. + Leave the loop if an error occurs. */ + while( astOK ) { + +/* Is this card a comment card? If not, then we have failed to find a + complete comment block. Break out of the loop. */ + if( CardType( this, status ) != AST__COMMENT ) break; + +/* Increment the number of cards in the comment block. */ + ncard++; + +/* Get the text of the comment, and its length. */ + text = CardComm( this, status ); + if( text ){ + len = strlen( text ); + +/* Check the first 3 characters. Break out of the loop if they are not + "AST". */ + if( strncmp( "AST", text, 3 ) ) break; + +/* Check the last 3 characters. Break out of the loop if they are not + "AST". */ + if( strcmp( "AST", text + len - 3 ) ) break; + +/* If the comment is the appropriate block delimiter (a line of +'s or + -'s depending on the direction), then set the flag to indicate that we + have a complete comment block and leave the loop. Allow spaces to be + included. Exclude the "AST" strings at begining and end from the check. */ + ret = 1; + for( i = 3; i < len - 3; i++ ) { + if( text[ i ] != del && text[ i ] != ' ' ) { + ret = 0; + break; + } + } + } + if( ret ) break; + +/* Move on to the next card. If this is not possible (due to us already + being at the start or end of the FitsChan), then break out of the loop. */ + if( MoveCard( this, incr, method, class, status ) == 0 ) break; + } + +/* Re-instate the original current card. */ + this->card = card0; + +/* If we found a complete comment block, mark it (which is equivalent to + deleting it except that memory of the cards location within the + FitsChan is preserved for future use), and then re-instate the original + current card. */ + if( ret && astOK ) { + for( i = 0; i < ncard; i++ ) { + MoveCard( this, incr, method, class, status ); + MarkCard( this, status ); + } + this->card = card0; + } + } + +/* If an error occurred, indicate that coment block has been deleted. */ + if( !astOK ) ret = 0; + return ret; +} + +static char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method, + const char *class, int *status ){ +/* +* Name: +* ConcatWAT + +* Purpose: +* Concatenate all the IRAF "WAT" keywords for an axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function searches the supplied FitsChan for any keywords of +* the form "WATi_j", where i and j are integers and i is equal to the +* supplied "iaxis" value plus one, and concatenates their string +* values into a single string. Such keywords are created by IRAF to +* describe their non-standard ZPX and TNX projections. + +* Parameters: +* this +* The FistChan. +* iaxis +* The zero-based index of the axis to be retrieved. +* method +* The name of the calling method to include in error messages. +* class +* The object type to include in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated, null terminated string +* containing a copy of the concatentated WAT values. This string must +* be freed by the caller (using astFree) when no longer required. +* +* A NULL pointer will be returned if there are no WAT kewyords for +* the requested axis in the FitsChan. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + char keyname[ FITSNAMLEN + 5 ];/* Keyword name */ + char *wat; /* Pointer to a single WAT string */ + char *result; /* Returned string */ + int watlen; /* Length of total WAT string (inc. term null)*/ + int j; /* WAT index */ + size_t size; /* Length of string value */ + +/* Initialise returned value. */ + result = NULL; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Rewind the FitsChan. */ + astClearCard( this ); + +/* Concatenate all the IRAF "WAT" keywords together for this axis. These + keywords are marked as having been used, so that they are not written + out when the FitsChan is deleted. */ + watlen = 1; + j = 1; + size = 0; + sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j ); + while( astOK ) { + +/* Search forward from the current card for the next WAT card. If no + found, try searching again from the start of the FitsChan. If not found + evenm then, break. */ + if( ! FindKeyCard( this, keyname, method, class, status ) ) { + astClearCard( this ); + if( ! FindKeyCard( this, keyname, method, class, status ) ) break; + } + + wat = (char *) CardData( this, &size, status ); + result = (char *) astRealloc( (void *) result, + watlen - 1 + size ); + if( result ) { + strcpy( result + watlen - 1, wat ); + watlen += size - 1; + MarkCard( this, status ); + MoveCard( this, 1, method, class, status ); + j++; + sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j ); + } else { + break; + } + } + +/* Return the result. */ + return result; +} + +static int CountFields( const char *temp, char type, const char *method, + const char *class, int *status ){ +/* +* Name: +* CountFields + +* Purpose: +* Count the number of field specifiers in a template string. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int CountFields( const char *temp, char type, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns the number of fields which include the +* specified character type in the supplied string. + +* Parameters: +* temp +* Pointer to a null terminated string holding the template. +* type +* A single character giving the field type to be counted (e.g. +* 'd', 'c' or 'f'). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of fields. + +* Notes: +* - No error is reported if the parameter "type" is not a valid +* field type specifier, but zero will be returned. +* - An error is reported if the template has any invalid field +* specifiers in it. +* - A value of zero is returned if an error has already occurred, +* or if this function should fail for any reason. +*/ + +/* Local Variables: */ + const char *b; /* Pointer to next template character */ + int nf; /* No. of fields found so far */ + +/* Check global status. */ + if( !astOK ) return 0; + +/* Initialise a pointer to the start of the template string. */ + b = temp; + +/* Initialise the number of fields found so far. */ + nf = 0; + +/* Go through the string. */ + while( *b && astOK ){ + +/* If the current character is a '%', a field is starting. */ + if( *b == '%' ){ + +/* Skip over the field width (if supplied). */ + if( isdigit( (int) *(++b) ) ) b++; + +/* Report an error if the end of the string occurs within the field. */ + if( !*b ) { + astError( AST__BDFMT, "%s(%s): Incomplete field specifier found " + "at end of filter template '%s'.", status, method, class, + temp ); + break; + +/* Report an error if the field type is illegal. */ + } else if( *b != 'd' && *b != 'c' && *b != 'f' ) { + astError( AST__BDFMT, "%s(%s): Illegal field type or width " + "specifier '%c' found in filter template '%s'.", status, + method, class, *b, temp ); + break; + } + +/* Compare the field type with the supplied type, and increment the + number of fields found if it is the correct type. */ + if( *b == type ) nf++; + } + +/* Move on to the next character. */ + b++; + } + +/* If an error has occurred, return 0. */ + if( !astOK ) nf = 0; + +/* Return the answer. */ + return nf; +} + +static void CreateKeyword( AstFitsChan *this, const char *name, + char keyword[ FITSNAMLEN + 1 ], int *status ){ + +/* +* Name: +* CreateKeyword + +* Purpose: +* Create a unique un-used keyword for a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void CreateKeyword( AstFitsChan *this, const char *name, +* char keyword[ FITSNAMLEN + 1 ], int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function takes a name which forms the basis of a FITS +* keyword and appends a sequence number (encoded as a pair of +* legal FITS keyword characters) so as to generate a unique FITS +* keyword which has not previously been used in the FitsChan +* supplied. +* +* It is intended for use when several keywords with the same name +* must be stored in a FitsChan, since to comply strictly with the +* FITS standard keywords should normally be unique (otherwise +* external software which processes the keywords might omit one or +* other of the values). +* +* An attempt is also made to generate keywords in a form that is +* unlikely to clash with those from other sources (in as far as +* this is possible with FITS). In any event, a keyword that +* already appears in the FitsChan will not be re-used. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a constant null-terminated string containing the +* name on which the new keyword should be based. This should be +* a legal FITS keyword in itself, except that it should be at +* least two characters shorter than the maximum length, in +* order to accommodate the sequence number characters. +* +* If this string is too long, it will be silently +* truncated. Mixed case is permitted, as all characters +* supplied are converted to upper case before use. +* keyword +* A character array in which the generated unique keyword will +* be returned, null terminated. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *seq_chars = SEQ_CHARS;/* Pointer to characters used for encoding */ + char seq_char; /* The first sequence character */ + const char *class; /* Object clas */ + int found; /* Keyword entry found in list? */ + int limit; /* Sequence number has reached limit? */ + int nc; /* Number of basic keyword characters */ + int seq; /* The sequence number */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Store the object class. */ + class = astGetClass( this ); + +/* On the first invocation only, determine the number of characters + being used to encode sequence number information and save this + value. */ + if( createkeyword_seq_nchars < 0 ) createkeyword_seq_nchars = (int) strlen( seq_chars ); + +/* Copy the name supplied into the output array, converting to upper + case. Leave space for two characters to encode a sequence + number. Terminate the resulting string. */ + for( nc = 0; ( nc < ( FITSNAMLEN - 2 ) ) && name[ nc ]; nc++ ) { + keyword[ nc ] = toupper( name[ nc ] ); + } + keyword[ nc ] = '\0'; + +/* We now search the list of sequence numbers already allocated to + find the next one to use for this keyword. */ + if( this->keyseq ) { + found = astMapGet0I( this->keyseq, keyword, &seq ); + } else { + found = 0; + this->keyseq = astKeyMap( " ", status ); + } + +/* If the keyword was not found in the list, create a new list entry + to describe it. */ + if( !found ) seq = 0; + +/* If OK, loop to find a new sequence number which results in a FITS + keyword that hasn't already been used to store data in the + FitsChan. */ + if( astOK ) { + while( 1 ) { + +/* Determine if the sequence number just obtained has reached the + upper limit. This is unlikely to happen in practice, but if it + does, we simply re-use this maximum value. Otherwise, we increment + the sequence number last used for this keyword to obtain a new + one. */ + limit = ( seq >= ( createkeyword_seq_nchars * createkeyword_seq_nchars - 1 ) ); + if( !limit ) seq++; + +/* Encode the sequence number into two characters and append them to + the original keyword (with a terminating null). */ + seq_char = seq_chars[ seq / createkeyword_seq_nchars ]; + keyword[ nc ] = seq_char; + keyword[ nc + 1 ] = seq_chars[ seq % createkeyword_seq_nchars ]; + keyword[ nc + 2 ] = '\0'; + +/* If the upper sequence number limit has not been reached, try to + look up the resulting keyword in the FitsChan to see if it has + already been used. Quit searching when a suitable keyword is + found. */ + if ( limit || !HasCard( this, keyword, "astWrite", class, status ) ) break; + } + +/* Store the update sequence number in the keymap. The keys into this + keymap are the base keyword name without the appended sequence string, so + temporaily terminate the returned keyword name to exclude the sequence + string. */ + keyword[ nc ] = '\0'; + astMapPut0I( this->keyseq, keyword, seq, NULL ); + keyword[ nc ] = seq_char; + } +} + +static double DateObs( const char *dateobs, int *status ) { +/* +* Name: +* DateObs + +* Purpose: +* Convert a FITS DATE-OBS keyword value to a MJD. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* double DateObs( const char *dateobs, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Extracts the date and time fields from the supplied string and converts +* them into a modified Julian Date. Supports both old "dd/mm/yy" +* format, and the new "ccyy-mm-ddThh:mm:ss[.sss...]" format. + +* Parameters: +* dateobs +* Pointer to the DATE-OBS string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Modified Julian Date corresponding to the supplied DATE-OBS +* string. + +* Notes: +* - The value AST__BAD is returned (without error) if the supplied +* string does not conform to the requirements of a FITS DATE-OBS value, +* or if an error has already occurred. +*/ + +/* Local Variables: */ + double days; /* The hours, mins and secs as a fraction of a day */ + double ret; /* The returned MJD value */ + double secs; /* The total value of the two seconds fields */ + int dd; /* The day field from the supplied string */ + int fsc; /* The fractional seconds field from the supplied string */ + int hr; /* The hour field from the supplied string */ + int j; /* SLALIB status */ + int len; /* The length of the supplied string */ + int mm; /* The month field from the supplied string */ + int mn; /* The minute field from the supplied string */ + int nc; /* Number of characters used */ + int ok; /* Was the string of a legal format? */ + int rem; /* The least significant digit in fsc */ + int sc; /* The whole seconds field from the supplied string */ + int yy; /* The year field from the supplied string */ + +/* Check the global status. */ + if( !astOK ) return AST__BAD; + +/* Initialise the returned value. */ + ret = AST__BAD; + +/* Save the length of the supplied string. */ + len = (int) strlen( dateobs ); + +/* Extract the year, month, day, hour, minute, second and fractional + seconds fields from the supplied string. Assume initially that the + string does not match any format. */ + ok = 0; + +/* First check for the old "dd/mm/yy" format. */ + if( nc = 0, + ( astSscanf( dateobs, " %2d/%2d/%d %n", &dd, &mm, &yy, &nc ) == 3 ) && + ( nc >= len ) ){ + ok = 1; + hr = 0; + mn = 0; + sc = 0; + fsc = 0; + +/* Otherwise, check for the new short format "ccyy-mm-dd". */ + } else if( nc = 0, + ( astSscanf( dateobs, " %4d-%2d-%2d %n", &yy, &mm, &dd, &nc ) == 3 ) && + ( nc >= len ) ){ + ok = 1; + hr = 0; + mn = 0; + sc = 0; + fsc = 0; + +/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss" without a + fractional seconds field or the trailing Z. */ + } else if( nc = 0, + ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d %n", &yy, &mm, &dd, + &hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len ) ){ + ok = 1; + fsc = 0; + +/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sss" with a + fractional seconds field but without the trailing Z. */ + } else if( nc = 0, + ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%d %n", &yy, &mm, &dd, + &hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len ) ){ + ok = 1; + +/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ssZ" without a + fractional seconds field but with the trailing Z. */ + } else if( nc = 0, + ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2dZ %n", &yy, &mm, &dd, + &hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len ) ){ + ok = 1; + fsc = 0; + +/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sssZ" with a + fractional seconds field and the trailing Z. */ + } else if( nc = 0, + ( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%dZ %n", &yy, &mm, &dd, + &hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len ) ){ + ok = 1; + } + +/* If the supplied string was legal, create a MJD from the separate fields. */ + if( ok ) { + +/* Get the MJD at the start of the day. */ + palCaldj( yy, mm, dd, &ret, &j ); + +/* If succesful, convert the hours, minutes and seconds to a fraction of + a day, and add it onto the MJD found above. */ + if( j == 0 ) { + +/* Obtain a floating point representation of the fractional seconds + field. */ + secs = 0.0; + while ( fsc > 0 ) { + rem = ( fsc % 10 ); + fsc /= 10; + secs = 0.1 * ( secs + (double) rem ); + } + +/* Add on the whole seconds field. */ + secs += (double) sc; + +/*Convert the hours, minutes and seconds to a fractional day. */ + palDtf2d( hr, mn, secs, &days, &j ); + +/* If succesful, add this onto the returned MJD. */ + if( j == 0 ) { + ret = ret + days; + +/* If the conversion to MJD failed, return AST__BAD. */ + } else { + ret = AST__BAD; + } + } else { + ret = AST__BAD; + } + } + +/* Return the result. */ + return ret; +} + +static void DeleteCard( AstFitsChan *this, const char *method, + const char *class, int *status ){ +/* +* Name: +* DeleteCard + +* Purpose: +* Delete the current card from a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void DeleteCard( AstFitsChan *this, const char *method, +* const char *class ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The current card is removed from the circular linked list of structures +* stored in the supplied FitsChan, and the memory used to store the +* structure is then freed. + +* Parameters: +* this +* Pointer to the FitsChan containing the list. +* method +* Name of calling method. +* class +* Object class. + +* Notes: +* - This function returns without action if the FitsChan is +* currently at "end-of-file". +* - The next card becomes the current card. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + FitsCard *card; /* Pointer to the current card */ + FitsCard *next; /* Pointer to next card in list */ + FitsCard *prev; /* Pointer to previous card in list */ + +/* Return if the supplied object or current card is NULL. */ + if( !this || !this->card ) return; + +/* Get a pointer to the card to be deleted (the current card). */ + card = (FitsCard *) this->card; + +/* Remove it from the KeyMap holding all keywords. */ + astMapRemove( this->keywords, card->name ); + +/* Move the current card on to the next card. */ + MoveCard( this, 1, method, class, status ); + +/* Save pointers to the previous and next cards in the list. */ + prev = GetLink( card, PREVIOUS, method, class, status ); + next = GetLink( card, NEXT, method, class, status ); + +/* If the backwards link points back to the supplied card, then it must + be the only one left on the list. */ + if( prev == card ) prev = NULL; + if( next == card ) next = NULL; + +/* If the list head is to be deleted, store a value for the new list + head. */ + if( this->head == (void *) card ) this->head = (void *) next; + +/* Free the memory used to hold the data value. */ + (void) astFree( card->data ); + +/* Free the memory used to hold any comment. */ + if( card->comment ) (void) astFree( (void *) card->comment ); + +/* Free the memory used to hold the whole structure. */ + (void) astFree( (void *) card ); + +/* Fix up the links between the two adjacent cards in the list, unless the + supplied card was the last one in the list. */ + if( prev && next ){ + next->prev = prev; + prev->next = next; + } else { + this->head = NULL; + this->card = NULL; + } + +/* Return. */ + return; +} + +static void DelFits( AstFitsChan *this, int *status ){ + +/* +*++ +* Name: +c astDelFits +f AST_DELFITS + +* Purpose: +* Delete the current FITS card in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c void astDelFits( AstFitsChan *this ) +f CALL AST_DELFITS( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function deletes the current FITS card from a FitsChan. The +f This routine deletes the current FITS card from a FitsChan. The +* current card may be selected using the Card attribute (if its index +c is known) or by using astFindFits (if only the FITS keyword is +f is known) or by using AST_FINDFITS (if only the FITS keyword is +* known). +* +* After deletion, the following card becomes the current card. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - This function returns without action if the FitsChan is +* initially positioned at the "end-of-file" (i.e. if the Card +* attribute exceeds the number of cards in the FitsChan). +* - If there are no subsequent cards in the FitsChan, then the +* Card attribute is left pointing at the "end-of-file" after +* deletion (i.e. is set to one more than the number of cards in +* the FitsChan). +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Delete the current card. The next card will be made the current card. */ + DeleteCard( this, "astDelFits", astGetClass( this ), status ); +} + +static void DistortMaps( AstFitsChan *this, FitsStore *store, char s, + int naxes, AstMapping **map1, AstMapping **map2, + AstMapping **map3, AstMapping **map4, + const char *method, const char *class, int *status ){ +/* +* Name: +* DistortMap + +* Purpose: +* Create a Mapping representing a FITS-WCS Paper IV distortion code. + +* Type: +* Private function. + +* Synopsis: +* void DistortMaps( AstFitsChan *this, FitsStore *store, char s, +* int naxes, AstMapping **map1, AstMapping **map2, +* AstMapping **map3, AstMapping **map4, +* const char *method, const char *class ) + +* Class Membership: +* FitsChan + +* Description: +* This function checks the CTYPE keywords in the supplied FitsStore to see +* if they contain a known distortion code (following the syntax described +* in FITS-WCS paper IV). If so, Mappings are returned which represent the +* distortions to be applied at each stage in the pixel->IWC chain. If +* any distortion codes are found in the FitsStore CTYPE values, whether +* recognised or not, the CTYPE values in the FitsStore are modified to +* remove the distortion code. Warnings about any unknown or inappropriate +* distortion codes are added to the FitsChan. + +* Parameters: +* this +* The FitsChan. ASTWARN cards may be added to this FitsChan if any +* anomalies are found in the keyword values in the FitsStore. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* naxes +* The number of intermediate world coordinate axes (WCSAXES). +* map1 +* Address of a location at which to store a pointer to a Mapping +* which describes any distortion to be applied to pixel +* coordinates, prior to performing the translation specified by the +* CRPIXj keywords. NULL is returned if no distortion is necessary. +* map2 +* Address of a location at which to store a pointer to a Mapping +* which describes any distortion to be applied to translated pixel +* coordinates, prior to performing the PC matrix multiplication. +* NULL is returned if no distortion is necessary. +* map3 +* Address of a location at which to store a pointer to a Mapping +* which describes any distortion to be applied to unscaled IWC +* coordinates, prior to performing the CDELT matrix multiplication. +* NULL is returned if no distortion is necessary. +* map4 +* Address of a location at which to store a pointer to a Mapping +* which describes any distortion to be applied to scaled IWC +* coordinates, after performing the CDELT matrix multiplication. +* NULL is returned if no distortion is necessary. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +*/ + +/* Local Variables: */ + AstMapping *tmap1; /* Mapping pointer */ + AstMapping *tmap2; /* Mapping pointer */ + char *ctype; /* Pointer to CTYPE value */ + char code[ 4 ]; /* Projection code extracted from CTYPE */ + char dist[ 4 ]; /* Distortion code extracted from CTYPE */ + char msgbuf[ 250 ]; /* Buffer for warning message */ + char type[ 5 ]; /* Axis type extracted from CTYPE */ + double *dim; /* Array holding array dimensions */ + int found_axes[ 2 ]; /* Index of axes with the distortion code */ + int i; /* FITS axis index */ + int nc; /* No. of characters in CTYPE without "-SIP" */ + int nfound; /* No. of axes with the distortion code */ + int warned; /* Have any ASTWARN cards been issued? */ + +/* Initialise pointers to the returned Mappings. */ + *map1 = NULL; + *map2 = NULL; + *map3 = NULL; + *map4 = NULL; + +/* Check the global status. */ + if ( !astOK ) return; + +/* Allocate memory to hold the image dimensions. */ + dim = (double *) astMalloc( sizeof(double)*naxes ); + if( dim ){ + +/* Note the image dimensions, if known. If not, store AST__BAD values. */ + for( i = 0; i < naxes; i++ ){ + if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ), + dim + i ) ) dim[ i ] = AST__BAD; + } + +/* First check each known distortion type... */ + +/* "-SIP": Spitzer (http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf) + ============= */ + +/* Spitzer distortion is limited to 2D. Check the first two axes to see if + they have "-SIP" codes at the end of their CTYPE values. If they do, + terminate the ctype string in order to exclude the distortion code (this + is so that later functions do not need to allow for the possibility of a + distortion code being present in the CTYPE value). */ + ctype = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status ); + if( ctype ){ + nc = astChrLen( ctype ) - 4; + if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) { + ctype[ nc ] = 0; + ctype = GetItemC( &(store->ctype), 1, 0, s, NULL, method, class, status ); + if( ctype ) { + nc = astChrLen( ctype ) - 4; + if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) { + ctype[ nc ] = 0; + +/* Create a Mapping describing the distortion (other axes are passed + unchanged by this Mapping), and add it in series with the returned map2 + (Spitzer distortion is applied to the translated pixel coordinates). */ + tmap1 = SIPMapping( dim, store, s, naxes, method, class, status ); + if( ! *map2 ) { + *map2 = tmap1; + } else { + tmap2 = (AstMapping *) astCmpMap( *map2, tmap1, 1, "", status ); + *map2 = astAnnul( *map2 ); + tmap1 = astAnnul( tmap1 ); + *map2 = tmap2; + } + } + } + } + } + +/* Check that the "-SIP" code is not included in any axes other than axes + 0 and 1. Issue a warning if it is, and remove it. */ + warned = 0; + for( i = 2; i < naxes; i++ ){ + ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( ctype ){ + nc = astChrLen( ctype ) - 4; + if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) { + if( !warned ){ + warned = 1; + sprintf( msgbuf, "The \"-SIP\" distortion code can only be " + "used on axes 1 and 2, but was found in keyword " + "%s (='%s'). The distortion will be ignored.", + FormatKey( "CTYPE", i + 1, -1, ' ', status ), ctype ); + Warn( this, "distortion", msgbuf, method, class, status ); + } + ctype[ nc ] = 0; + } + } + } + +/* "-ZPX": IRAF (http://iraf.noao.edu/projects/ccdmosaic/zpx.html) + ============= */ + +/* An IRAF ZPX header uses a ZPX projection within each CTYPE value in place + of the basic ZPN projection. The SpecTrans function converts -ZPX" to + "-ZPN-ZPX" (i.e. a basic projection of ZPN with a distortion code of + "-ZPX"). This function then traps and processes the "-ZPX" distortion + code. */ + +/* Look for axes that have the "-ZPX" code in their CTYPE values. If any + are found, check that there are exactly two such axes, and terminate the + ctype strings in order to exclude the distortion code (this is so that + later functions do not need to allow for the possibility of a distortion + code being present in the CTYPE value)*/ + nfound = 0; + for( i = 0; i < naxes; i++ ){ + ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( ctype && 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){ + if( !strcmp( "ZPX", dist ) ){ + if( nfound < 2 ) found_axes[ nfound ] = i; + nfound++; + ctype[ 8 ] = 0; + } + } + } + +/* Issue a warning if more than two ZPX axes were found. */ + if( nfound > 2 ) { + Warn( this, "distortion", "More than two axes were found " + "with the \"-ZPX\" projection code. A ZPN projection " + "will be used instead.", method, class, status ); + +/* Otherwise, create a Mapping describing the distortion (other axes are passed + unchanged by this Mapping), and add it in series with the returned map4 + (ZPX distortion is applied to the translated, rotated, scaled IWC + coordinates). */ + } else if( nfound == 2 ){ + tmap1 = ZPXMapping( this, store, s, naxes, found_axes, method, + class, status ); + if( ! *map4 ) { + *map4 = tmap1; + } else { + tmap2 = (AstMapping *) astCmpMap( *map4, tmap1, 1, "", status ); + *map4 = astAnnul( *map4 ); + tmap1 = astAnnul( tmap1 ); + *map4 = tmap2; + } + } + +/* (There are currently no other supported distortion codes.) */ + +/* Finally, check all axes looking for any remaining (and therefore + unsupported) distortion codes. Issue a warning about them and remove + them. + =================================================================== */ + +/* Indicate that we have not yet issued a warning. */ + warned = 0; + +/* Do each IWC axis. */ + for( i = 0; i < naxes; i++ ){ + +/* Get the CTYPE value for this axis. */ + ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( ctype ) { + +/* See if has the "4-3-3" form described in FITS-WCS paper IV. */ + if( 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){ + +/* Add an ASTWARN card to the FitsChan. Only issue one warning (this avoids + multiple warnings about the same distortion code in multiple CTYPE values). */ + if( !warned ){ + warned = 1; + sprintf( msgbuf, "The header contains CTYPE values (e.g. " + "%s = '%s') which " + "include a distortion code \"-%s\". AST " + "currently ignores this distortion. The code " + "has been removed from the CTYPE values.", + FormatKey( "CTYPE", i + 1, -1, ' ', status ), ctype, dist ); + Warn( this, "distortion", msgbuf, method, class, status ); + } + +/* Terminate the CTYPE value in the FitsStore in order to exclude the distortion + code. This means that later functions will not need to take account of + distortion codes. */ + ctype[ 8 ] = 0; + } + } + } + } + +/* Free resources. */ + dim = astFree( dim ); +} + +static void DSBSetUp( AstFitsChan *this, FitsStore *store, + AstDSBSpecFrame *dsb, char s, double crval, + const char *method, const char *class, int *status ){ + +/* +* Name: +* DSBSetUp + +* Purpose: +* Modify an AstDSBSpecFrame object to reflect the contents of a FitsStore. + +* Type: +* Private function. + +* Synopsis: + +* void DSBSetUp( AstFitsChan *this, FitsStore *store, +* AstDSBSpecFrame *dsb, char s, double crval, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function sets the attributes of the supplied DSBSpecFrame to +* reflect the values in the supplied FitsStore. + +* Parameters: +* this +* The FitsChan. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* dsb +* Pointer to the DSBSpecFrame. +* s +* Alternate axis code. +* crval +* The spectral CRVAL value, in the spectral system represented by +* the supplied DSBSPecFrame. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This implementation follows the conventions of the FITS-CLASS encoding. +*/ + +/* Local Variables: */ + AstDSBSpecFrame *dsb_src; /* New DSBSpecFrame in which StdOfRest is source */ + AstDSBSpecFrame *dsb_topo;/* New DSBSpecFrame in which StdOfRest is topo */ + AstFrameSet *fs; /* FrameSet connecting two standards of rest */ + double dsbcentre; /* Topocentric reference (CRVAL) frequency */ + double in[2]; /* Source rest and image frequencies */ + double lo; /* Topocentric Local Oscillator frequency */ + double out[2]; /* Topocentric rest and image frequencies */ + +/* Check the global status. */ + if ( !astOK ) return; + +/* In order to determine the topocentric IF, we need the topocentric + frequencies corresponding to the RESTFREQ and IMAGFREQ values in the + FITS header. The values stored in the FITS header are measured in Hz, + in the source's rest frame, so we need a mapping from frequency in the + source rest frame to topocentric frequency. Take a copy of the supplied + DSBSpecFrame and then set its attributes to represent frequency in the + sources rest frame. */ + dsb_src = astCopy( dsb ); + astSetStdOfRest( dsb_src, AST__SCSOR ); + astSetSystem( dsb_src, AST__FREQ ); + astSetUnit( dsb_src, 0, "Hz" ); + +/* Take a copy of this DSBSpecFrame and set its standard of rest to + topocentric. */ + dsb_topo = astCopy( dsb_src ); + astSetStdOfRest( dsb_topo, AST__TPSOR ); + +/* Now get the Mapping between these. */ + fs = astConvert( dsb_src, dsb_topo, "" ); + dsb_src = astAnnul( dsb_src ); + dsb_topo = astAnnul( dsb_topo ); + +/* Check a conversion was found. */ + if( fs != NULL ) { + +/* Use this Mapping to transform the rest frequency and the image + frequency from the standard of rest of the source to that of the + observer. */ + in[ 0 ] = astGetRestFreq( dsb ); + in[ 1 ] = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status ); + astTran1( fs, 2, in, 1, out ); + +/* The intermediate frequency is half the distance between these two + frequencies. Note, the IF value is signed so as to put the rest + frequency in the observed sideband. */ + if( out[ 0 ] != AST__BAD && out[ 1 ] != AST__BAD ) { + +/* Store the spectral CRVAL value as the centre frequency of the + DSBSpecFrame. The public astSetD method interprets the supplied value + as a value in the spectral system described by the other SpecFrame + attributes. */ + astSetD( dsb, "DSBCentre", crval ); + +/* To calculate the topocentric IF we need the topocentric frequency + equivalent of CRVAL. So take a copy of the DSBSpecFrame, then set it to + represent topocentric frequency, and read back the DSBCentre value. */ + dsb_topo = astCopy( dsb ); + astSetStdOfRest( dsb_topo, AST__TPSOR ); + astSetSystem( dsb_topo, AST__FREQ ); + astSetUnit( dsb_topo, 0, "Hz" ); + dsbcentre = astGetD( dsb_topo, "DSBCentre" ); + dsb_topo = astAnnul( dsb_topo ); + +/* We also need the topocentric Local Oscillator frequency. This is + assumed to be half way between the topocentric IMAGFREQ and RESTFREQ + values. */ + lo = 0.5*( out[ 1 ] + out[ 0 ] ); + +/* Set the IF to be the difference between the Local Oscillator frequency + and the CRVAL frequency. */ + astSetIF( dsb, lo - dsbcentre ); + +/* Set the DSBSpecFrame to represent the observed sideband */ + astSetC( dsb, "SideBand", "observed" ); + } + +/* Free resources. */ + fs = astAnnul( fs ); + } +} + +static int DSSFromStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ + +/* +* Name: +* DSSFromStore + +* Purpose: +* Store WCS keywords in a FitsChan using DSS encoding. + +* Type: +* Private function. + +* Synopsis: + +* int DSSFromStore( AstFitsChan *this, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using DSS encoding. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + const char *comm; /* Pointer to comment string */ + char *cval; /* Pointer to string keyword value */ + const char *pltdecsn;/* PLTDECSN keyword value */ + double amdx[20]; /* AMDXi keyword value */ + double amdy[20]; /* AMDYi keyword value */ + double cdelt; /* CDELT element */ + double cnpix1; /* CNPIX1 keyword value */ + double cnpix2; /* CNPIX2 keyword value */ + double pc; /* PC element */ + double pltdecd; /* PLTDECD keyword value */ + double pltdecm; /* PLTDECM keyword value */ + double pltdecs; /* PLTDECS keyword value */ + double pltrah; /* PLTRAH keyword value */ + double pltram; /* PLTRAM keyword value */ + double pltras; /* PLTRAS keyword value */ + double pltscl; /* PLTSCL keyword value */ + double ppo1; /* PPO1 keyword value */ + double ppo2; /* PPO2 keyword value */ + double ppo3; /* PPO3 keyword value */ + double ppo4; /* PPO4 keyword value */ + double ppo5; /* PPO5 keyword value */ + double ppo6; /* PPO6 keyword value */ + double pvx[22]; /* X projection parameter values */ + double pvy[22]; /* Y projection parameter values */ + double val; /* General purpose value */ + double xpixelsz; /* XPIXELSZ keyword value */ + double ypixelsz; /* YPIXELSZ keyword value */ + int i; /* Loop count */ + int gottpn; /* Is the projection a "TPN" projection? */ + int m; /* Parameter index */ + int ret; /* Returned value. */ + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Check the image is 2 dimensional. */ + if( GetMaxJM( &(store->crpix), ' ', status ) != 1 ) return ret; + +/* Check the first axis is RA with a TAN or TPN projection. */ + cval = GetItemC( &(store->ctype), 0, 0, ' ', NULL, method, class, status ); + if( !cval ) return ret; + gottpn = !strcmp( "RA---TPN", cval ); + if( strcmp( "RA---TAN", cval ) && !gottpn ) return ret; + +/* Check the second axis is DEC with a TAN or TPN projection. */ + cval = GetItemC( &(store->ctype), 1, 0, ' ', NULL, method, class, status ); + if( !cval ) return ret; + if( gottpn ) { + if( strcmp( "DEC--TPN", cval ) ) return ret; + } else { + if( strcmp( "DEC--TAN", cval ) ) return ret; + } + +/* Check that LONPOLE is undefined or is 180 degrees. */ + val = GetItem( &(store->lonpole), 0, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD && val != 180.0 ) return ret; + +/* Check that the RA/DEC system is FK5. */ + cval = GetItemC( &(store->radesys), 0, 0, ' ', NULL, method, class, status ); + if( !cval || strcmp( "FK5", cval ) ) return ret; + +/* Check that equinox is not defined or is 2000.0 */ + val = GetItem( &(store->equinox), 0, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD && val != 2000.0 ) return ret; + +/* Get the pixel sizes from the PC/CDELT keywords. They must be defined and + not be zero. */ + cdelt = GetItem( &(store->cdelt), 0, 0, ' ', NULL, method, class, status ); + if( cdelt == AST__BAD ) return ret; + pc = GetItem( &(store->pc), 0, 0, ' ', NULL, method, class, status ); + if( pc == AST__BAD ) pc = 1.0; + xpixelsz = cdelt*pc; + cdelt = GetItem( &(store->cdelt), 1, 0, ' ', NULL, method, class, status ); + if( cdelt == AST__BAD ) return ret; + pc = GetItem( &(store->pc), 1, 1, ' ', NULL, method, class, status ); + if( pc == AST__BAD ) pc = 1.0; + ypixelsz = cdelt*pc; + if( xpixelsz == 0.0 || ypixelsz == 0.0 ) return ret; + xpixelsz *= -1000.0; + ypixelsz *= 1000.0; + +/* Check the off-diagonal PC terms are zero. DSS does not allow any rotation. */ + val = GetItem( &(store->pc), 0, 1, ' ', NULL, method, class, status ); + if( val != AST__BAD && val != 0.0 ) return ret; + val = GetItem( &(store->pc), 1, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD && val != 0.0 ) return ret; + +/* Get the required projection parameter values from the store, supplying + appropriate values if a simple TAN projection is being used. */ + for( m = 0; m < 22; m++ ){ + pvx[ m ] = GetItem( &(store->pv), 0, m, ' ', NULL, method, class, status ); + if( pvx[ m ] == AST__BAD || !gottpn ) pvx[ m ] = ( m == 1 ) ? 1.0 : 0.0; + pvy[ m ] = GetItem( &(store->pv), 1, m, ' ', NULL, method, class, status ); + if( pvy[ m ] == AST__BAD || !gottpn ) pvy[ m ] = ( m == 1 ) ? 1.0 : 0.0; + } + +/* Check that no other projection parameters have been set. */ + if( GetMaxJM( &(store->pv), ' ', status ) > 21 ) return ret; + +/* Check that specific parameters take their required zero value. */ + if( pvx[ 3 ] != 0.0 || pvy[ 3 ] != 0.0 ) return ret; + for( m = 11; m < 17; m++ ){ + if( pvx[ m ] != 0.0 || pvy[ m ] != 0.0 ) return ret; + } + if( pvx[ 18 ] != 0.0 || pvy[ 18 ] != 0.0 ) return ret; + if( pvx[ 20 ] != 0.0 || pvy[ 20 ] != 0.0 ) return ret; + +/* Check that other projection parameters are related correctly. */ + if( !astEQUAL( 2*pvx[ 17 ], pvx[ 19 ] ) ) return ret; + if( !astEQUAL( pvx[ 17 ], pvx[ 21 ] ) ) return ret; + if( !astEQUAL( 2*pvy[ 17 ], pvy[ 19 ] ) ) return ret; + if( !astEQUAL( pvy[ 17 ], pvy[ 21 ] ) ) return ret; + +/* Initialise all polynomial co-efficients to zero. */ + for( m = 0; m < 20; m++ ){ + amdx[ m ] = 0.0; + amdy[ m ] = 0.0; + } + +/* Polynomial co-efficients. There is redundancy here too, so we + arbitrarily choose to leave AMDX/Y7 and AMDX/Y12 set to zero. */ + amdx[ 0 ] = 3600.0*pvx[ 1 ]; + amdx[ 1 ] = 3600.0*pvx[ 2 ]; + amdx[ 2 ] = 3600.0*pvx[ 0 ]; + amdx[ 3 ] = 3600.0*pvx[ 4 ]; + amdx[ 4 ] = 3600.0*pvx[ 5 ]; + amdx[ 5 ] = 3600.0*pvx[ 6 ]; + amdx[ 7 ] = 3600.0*pvx[ 7 ]; + amdx[ 8 ] = 3600.0*pvx[ 8 ]; + amdx[ 9 ] = 3600.0*pvx[ 9 ]; + amdx[ 10 ] = 3600.0*pvx[ 10 ]; + amdx[ 12 ] = 3600.0*pvx[ 17 ]; + amdy[ 0 ] = 3600.0*pvy[ 1 ]; + amdy[ 1 ] = 3600.0*pvy[ 2 ]; + amdy[ 2 ] = 3600.0*pvy[ 0 ]; + amdy[ 3 ] = 3600.0*pvy[ 4 ]; + amdy[ 4 ] = 3600.0*pvy[ 5 ]; + amdy[ 5 ] = 3600.0*pvy[ 6 ]; + amdy[ 7 ] = 3600.0*pvy[ 7 ]; + amdy[ 8 ] = 3600.0*pvy[ 8 ]; + amdy[ 9 ] = 3600.0*pvy[ 9 ]; + amdy[ 10 ] = 3600.0*pvy[ 10 ]; + amdy[ 12 ] = 3600.0*pvy[ 17 ]; + +/* The plate scale is the mean of the first X and Y co-efficients. */ + pltscl = 0.5*( amdx[ 0 ] + amdy[ 0 ] ); + +/* There is redundancy in the DSS encoding. We can choose an arbitrary + pixel corner (CNPIX1, CNPIX2) so long as we use the corresponding origin + for the cartesian co-ordinate system in which the plate centre is + specified (PPO3, PPO6). Arbitrarily set CNPIX1 and CNPIX2 to one. */ + cnpix1 = 1.0; + cnpix2 = 1.0; + +/* Find the corresponding plate centre PPO3 and PPO6 (other co-efficients + are set to zero). */ + ppo1 = 0.0; + ppo2 = 0.0; + val = GetItem( &(store->crpix), 0, 0, ' ', NULL, method, class, status ); + if( val == AST__BAD ) return ret; + ppo3 = xpixelsz*( val + cnpix1 - 0.5 ); + ppo4 = 0.0; + ppo5 = 0.0; + val = GetItem( &(store->crpix), 0, 1, ' ', NULL, method, class, status ); + if( val == AST__BAD ) return ret; + ppo6 = ypixelsz*( val + cnpix2 - 0.5 ); + +/* The reference RA. Get it in degrees. */ + val = GetItem( &(store->crval), 0, 0, ' ', NULL, method, class, status ); + if( val == AST__BAD ) return ret; + +/* Convert to hours and ensure it is in the range 0 to 24 */ + val /= 15.0; + while( val < 0 ) val += 24.0; + while( val >= 24.0 ) val -= 24.0; + +/* Split into hours, mins and seconds. */ + pltrah = (int) val; + val = 60.0*( val - pltrah ); + pltram = (int) val; + pltras = 60.0*( val - pltram ); + +/* The reference DEC. Get it in degrees. */ + val = GetItem( &(store->crval), 1, 0, ' ', NULL, method, class, status ); + if( val == AST__BAD ) return ret; + +/* Ensure it is in the range -180 to +180 */ + while( val < -180.0 ) val += 360.0; + while( val >= 180.0 ) val -= 360.0; + +/* Save the sign. */ + if( val > 0.0 ){ + pltdecsn = "+"; + } else { + pltdecsn = "-"; + val = -val; + } + +/* Split into degrees, mins and seconds. */ + pltdecd = (int) val; + val = 60.0*( val - pltdecd ); + pltdecm = (int) val; + pltdecs = 60.0*( val - pltdecm ); + +/* Store the DSS keywords in the FitsChan. */ + SetValue( this, "CNPIX1", &cnpix1, AST__FLOAT, "X corner (pixels)", status ); + SetValue( this, "CNPIX2", &cnpix2, AST__FLOAT, "Y corner (pixels)", status ); + SetValue( this, "PPO1", &ppo1, AST__FLOAT, "Orientation co-efficients", status ); + SetValue( this, "PPO2", &ppo2, AST__FLOAT, "", status ); + SetValue( this, "PPO3", &ppo3, AST__FLOAT, "", status ); + SetValue( this, "PPO4", &ppo4, AST__FLOAT, "", status ); + SetValue( this, "PPO5", &ppo5, AST__FLOAT, "", status ); + SetValue( this, "PPO6", &ppo6, AST__FLOAT, "", status ); + SetValue( this, "XPIXELSZ", &xpixelsz, AST__FLOAT, "X pixel size (microns)", status ); + SetValue( this, "YPIXELSZ", &ypixelsz, AST__FLOAT, "Y pixel size (microns)", status ); + SetValue( this, "PLTRAH", &pltrah, AST__FLOAT, "RA at plate centre", status ); + SetValue( this, "PLTRAM", &pltram, AST__FLOAT, "", status ); + SetValue( this, "PLTRAS", &pltras, AST__FLOAT, "", status ); + SetValue( this, "PLTDECD", &pltdecd, AST__FLOAT, "DEC at plate centre", status ); + SetValue( this, "PLTDECM", &pltdecm, AST__FLOAT, "", status ); + SetValue( this, "PLTDECS", &pltdecs, AST__FLOAT, "", status ); + SetValue( this, "PLTDECSN", &pltdecsn, AST__STRING, "", status ); + SetValue( this, "PLTSCALE", &pltscl, AST__FLOAT, "Plate scale (arcsec/mm)", status ); + comm = "Plate solution x co-efficients"; + for( i = 0; i < 20; i++ ){ + SetValue( this, FormatKey( "AMDX", i + 1, -1, ' ', status ), amdx + i, + AST__FLOAT, comm, status ); + comm = NULL; + } + comm = "Plate solution y co-efficients"; + for( i = 0; i < 20; i++ ){ + SetValue( this, FormatKey( "AMDY", i + 1, -1, ' ', status ), amdy + i, + AST__FLOAT, comm, status ); + comm = NULL; + } + +/* If no error has occurred, return one. */ + if( astOK ) ret = 1; + +/* Return the answer. */ + return ret; +} + +static void DSSToStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ + +/* +* Name: +* DSSToStore + +* Purpose: +* Extract WCS information from the supplied FitsChan using a DSS +* encoding, and store it in the supplied FitsStore. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void DSSToStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function extracts DSS keywords from the supplied FitsChan, and +* stores the corresponding WCS information in the supplied FitsStore. +* The conversion from DSS encoding to standard WCS encoding is +* described in an ear;y draft of the Calabretta & Greisen paper +* "Representations of celestial coordinates in FITS" (A&A, in prep.), +* and uses the now deprecated "TAN with polynomial corrections", +* which is still supported by the WcsMap class as type AST__TPN. +* Here we use "lambda=1" (i.e. plate co-ordinate are measured in mm, +* not degrees). +* +* It is assumed that DSS images are 2 dimensional. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore structure. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + char *text; /* Pointer to textual keyword value */ + char pltdecsn[11]; /* First 10 non-blank characters from PLTDECSN keyword */ + char keyname[10]; /* Buffer for keyword name */ + double amdx[20]; /* AMDXi keyword value */ + double amdy[20]; /* AMDYi keyword value */ + double cnpix1; /* CNPIX1 keyword value */ + double cnpix2; /* CNPIX2 keyword value */ + double crval2; /* Equivalent CRVAL2 keyword value */ + double dummy; /* Unused keyword value */ + double pltdecd; /* PLTDECD keyword value */ + double pltdecm; /* PLTDECM keyword value */ + double pltdecs; /* PLTDECS keyword value */ + double pltrah; /* PLTRAH keyword value */ + double pltram; /* PLTRAM keyword value */ + double pltras; /* PLTRAS keyword value */ + double ppo3; /* PPO3 keyword value */ + double ppo6; /* PPO6 keyword value */ + double pv; /* Projection parameter value */ + double xpixelsz; /* XPIXELSZ keyword value */ + double ypixelsz; /* YPIXELSZ keyword value */ + int i; /* Loop count */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Get the optional DSS keywords, supplying defaults for any missing keywords. */ + cnpix1 = 0.0; + cnpix2 = 0.0; + GetValue( this, "CNPIX1", AST__FLOAT, &cnpix1, 0, 1, method, class, status ); + GetValue( this, "CNPIX2", AST__FLOAT, &cnpix2, 0, 1, method, class, status ); + +/* Get the required DSS keywords. Report an error if any are missing. */ + GetValue( this, "PPO3", AST__FLOAT, &ppo3, 1, 1, method, class, status ); + GetValue( this, "PPO6", AST__FLOAT, &ppo6, 1, 1, method, class, status ); + GetValue( this, "XPIXELSZ", AST__FLOAT, &xpixelsz, 1, 1, method, class, status ); + GetValue( this, "YPIXELSZ", AST__FLOAT, &ypixelsz, 1, 1, method, class, status ); + GetValue( this, "PLTRAH", AST__FLOAT, &pltrah, 1, 1, method, class, status ); + GetValue( this, "PLTRAM", AST__FLOAT, &pltram, 1, 1, method, class, status ); + GetValue( this, "PLTRAS", AST__FLOAT, &pltras, 1, 1, method, class, status ); + GetValue( this, "PLTDECD", AST__FLOAT, &pltdecd, 1, 1, method, class, status ); + GetValue( this, "PLTDECM", AST__FLOAT, &pltdecm, 1, 1, method, class, status ); + GetValue( this, "PLTDECS", AST__FLOAT, &pltdecs, 1, 1, method, class, status ); + +/* Copy the first 10 non-blank characters from the PLTDECSN keyword. */ + GetValue( this, "PLTDECSN", AST__STRING, &text, 1, 1, method, class, status ); + if( astOK ) { + text += strspn( text, " " ); + text[ strcspn( text, " " ) ] = 0; + strncpy( pltdecsn, text, 10 ); + } + +/* Read other related keywords. We do not need these, but we read them + so that they are not propagated to any output FITS file. */ + GetValue( this, "PLTSCALE", AST__FLOAT, &dummy, 0, 1, method, class, status ); + GetValue( this, "PPO1", AST__FLOAT, &dummy, 0, 1, method, class, status ); + GetValue( this, "PPO2", AST__FLOAT, &dummy, 0, 1, method, class, status ); + GetValue( this, "PPO4", AST__FLOAT, &dummy, 0, 1, method, class, status ); + GetValue( this, "PPO5", AST__FLOAT, &dummy, 0, 1, method, class, status ); + +/* Get the polynomial co-efficients. These can be defaulted if they are + missing, so do not report an error. */ + for( i = 0; i < 20; i++ ){ + (void) sprintf( keyname, "AMDX%d", i + 1 ); + amdx[i] = AST__BAD; + GetValue( this, keyname, AST__FLOAT, amdx + i, 0, 1, method, class, status ); + (void) sprintf( keyname, "AMDY%d", i + 1 ); + amdy[i] = AST__BAD; + GetValue( this, keyname, AST__FLOAT, amdy + i, 0, 1, method, class, status ); + } + +/* Check the above went OK. */ + if( astOK ) { + +/* Calculate and store the equivalent PV projection parameters. */ + if( amdx[2] != AST__BAD ) { + pv = amdx[2]/3600.0; + SetItem( &(store->pv), 0, 0, ' ', pv, status ); + } + if( amdx[0] != AST__BAD ) { + pv = amdx[0]/3600.0; + SetItem( &(store->pv), 0, 1, ' ', pv, status ); + } + if( amdx[1] != AST__BAD ) { + pv = amdx[1]/3600.0; + SetItem( &(store->pv), 0, 2, ' ', pv, status ); + } + if( amdx[3] != AST__BAD && amdx[6] != AST__BAD ) { + pv = ( amdx[3] + amdx[6] )/3600.0; + SetItem( &(store->pv), 0, 4, ' ', pv, status ); + } + if( amdx[4] != AST__BAD ) { + pv = amdx[4]/3600.0; + SetItem( &(store->pv), 0, 5, ' ', pv, status ); + } + if( amdx[5] != AST__BAD && amdx[6] != AST__BAD ) { + pv = ( amdx[5] + amdx[6] )/3600.0; + SetItem( &(store->pv), 0, 6, ' ', pv, status ); + } + if( amdx[7] != AST__BAD && amdx[11] != AST__BAD ) { + pv = ( amdx[7] + amdx[11] )/3600.0; + SetItem( &(store->pv), 0, 7, ' ', pv, status ); + } + if( amdx[8] != AST__BAD ) { + pv = amdx[8]/3600.0; + SetItem( &(store->pv), 0, 8, ' ', pv, status ); + } + if( amdx[9] != AST__BAD && amdx[11] != AST__BAD ) { + pv = ( amdx[9] + amdx[11] )/3600.0; + SetItem( &(store->pv), 0, 9, ' ', pv, status ); + } + if( amdx[10] != AST__BAD ) { + pv = amdx[10]/3600.0; + SetItem( &(store->pv), 0, 10, ' ', pv, status ); + } + if( amdx[12] != AST__BAD ) { + pv = amdx[12]/3600.0; + SetItem( &(store->pv), 0, 17, ' ', pv, status ); + SetItem( &(store->pv), 0, 19, ' ', 2*pv, status ); + SetItem( &(store->pv), 0, 21, ' ', pv, status ); + } + if( amdy[2] != AST__BAD ) { + pv = amdy[2]/3600.0; + SetItem( &(store->pv), 1, 0, ' ', pv, status ); + } + if( amdy[0] != AST__BAD ) { + pv = amdy[0]/3600.0; + SetItem( &(store->pv), 1, 1, ' ', pv, status ); + } + if( amdy[1] != AST__BAD ) { + pv = amdy[1]/3600.0; + SetItem( &(store->pv), 1, 2, ' ', pv, status ); + } + if( amdy[3] != AST__BAD && amdy[6] != AST__BAD ) { + pv = ( amdy[3] + amdy[6] )/3600.0; + SetItem( &(store->pv), 1, 4, ' ', pv, status ); + } + if( amdy[4] != AST__BAD ) { + pv = amdy[4]/3600.0; + SetItem( &(store->pv), 1, 5, ' ', pv, status ); + } + if( amdy[5] != AST__BAD && amdy[6] != AST__BAD ) { + pv = ( amdy[5] + amdy[6] )/3600.0; + SetItem( &(store->pv), 1, 6, ' ', pv, status ); + } + if( amdy[7] != AST__BAD && amdy[11] != AST__BAD ) { + pv = ( amdy[7] + amdy[11] )/3600.0; + SetItem( &(store->pv), 1, 7, ' ', pv, status ); + } + if( amdy[8] != AST__BAD ) { + pv = amdy[8]/3600.0; + SetItem( &(store->pv), 1, 8, ' ', pv, status ); + } + if( amdy[9] != AST__BAD && amdy[11] != AST__BAD ) { + pv = ( amdy[9] + amdy[11] )/3600.0; + SetItem( &(store->pv), 1, 9, ' ', pv, status ); + } + if( amdy[10] != AST__BAD ) { + pv = amdy[10]/3600.0; + SetItem( &(store->pv), 1, 10, ' ', pv, status ); + } + if( amdy[12] != AST__BAD ) { + pv = amdy[12]/3600.0; + SetItem( &(store->pv), 1, 17, ' ', pv, status ); + SetItem( &(store->pv), 1, 19, ' ', 2*pv, status ); + SetItem( &(store->pv), 1, 21, ' ', pv, status ); + } + +/* Calculate and store the equivalent CRPIX values. */ + if( xpixelsz != 0.0 ) { + SetItem( &(store->crpix), 0, 0, ' ', + ( ppo3/xpixelsz ) - cnpix1 + 0.5, status ); + } else if( astOK ){ + astError( AST__BDFTS, "%s(%s): FITS keyword XPIXELSZ has illegal " + "value 0.0", status, method, class ); + } + if( ypixelsz != 0.0 ) { + SetItem( &(store->crpix), 0, 1, ' ', + ( ppo6/ypixelsz ) - cnpix2 + 0.5, status ); + } else if( astOK ){ + astError( AST__BDFTS, "%s(%s): FITS keyword YPIXELSZ has illegal " + "value 0.0", status, method, class ); + } + +/* Calculate and store the equivalent CRVAL values. */ + SetItem( &(store->crval), 0, 0, ' ', + 15.0*( pltrah + pltram/60.0 + pltras/3600.0 ), status ); + crval2 = pltdecd + pltdecm/60.0 + pltdecs/3600.0; + if( !strcmp( pltdecsn, "-") ) crval2 = -crval2; + SetItem( &(store->crval), 1, 0, ' ', crval2, status ); + +/* Calculate and store the equivalent PC matrix. */ + SetItem( &(store->pc), 0, 0, ' ', -0.001*xpixelsz, status ); + SetItem( &(store->pc), 1, 1, ' ', 0.001*ypixelsz, status ); + +/* Set values of 1.0 for the CDELT values. */ + SetItem( &(store->cdelt), 0, 0, ' ', 1.0, status ); + SetItem( &(store->cdelt), 1, 0, ' ', 1.0, status ); + +/* Store remaining constant items */ + SetItem( &(store->lonpole), 0, 0, ' ', 180.0, status ); + SetItem( &(store->equinox), 0, 0, ' ', 2000.0, status ); + SetItemC( &(store->radesys), 0, 0, ' ', "FK5", status ); + SetItem( &(store->wcsaxes), 0, 0, ' ', 2.0, status ); + store->naxis = 2; + SetItemC( &(store->ctype), 0, 0, ' ', "RA---TPN", status ); + SetItemC( &(store->ctype), 1, 0, ' ', "DEC--TPN", status ); + } +} + +static void EmptyFits( AstFitsChan *this, int *status ){ + +/* +*++ +* Name: +c astEmptyFits +f AST_EMPTYFITS + +* Purpose: +* Delete all cards in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c void astEmptyFits( AstFitsChan *this ) +f CALL AST_EMPTYFITS( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* deletes all cards and associated information from a FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - This method simply deletes the cards currently in the FitsChan. +c Unlike astWriteFits, +f Unlike AST_WRITEFITS, +* they are not first written out to the sink function or sink file. +* - Any Tables or warnings stored in the FitsChan are also deleted. +* - This method attempt to execute even if an error has occurred +* previously. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *class; /* Pointer to string holding object class */ + const char *method; /* Pointer to string holding calling method */ + int old_ignore_used; /* Original setting of ignore_used variable */ + +/* Check a FitsChan was supplied. */ + if( !this ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Store the method and class strings. */ + method = "astEmpty"; + class = astGetClass( this ); + +/* Delete all cards from the circular linked list stored in the FitsChan, + starting with the card at the head of the list. */ + old_ignore_used = ignore_used; + ignore_used = 0; + astClearCard( this ); + while( !astFitsEof( this ) ) DeleteCard( this, method, class, status ); + ignore_used = old_ignore_used; + +/* Delete the KeyMap which holds keywords and the latest sequence number + used by each of them. */ + if( this->keyseq ) this->keyseq = astAnnul( this->keyseq ); + +/* Delete the KeyMap holding the keyword names. */ + if( this->keywords ) this->keywords = astAnnul( this->keywords ); + +/* Free any memory used to hold the Warnings attribute value. */ + this->warnings = astFree( this->warnings ); + +/* Other objects in the FitsChan structure. */ + if( this->tables ) this->tables = astAnnul( this->tables ); +} + +static int EncodeFloat( char *buf, int digits, int width, int maxwidth, + double value, int *status ){ +/* +* +* Name: +* EncodeFloat + +* Purpose: +* Formats a floating point value. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int EncodeFloat( char *buf, int digits, int width, int maxwidth, +* double value, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function formats the value using a G format specified in order +* to use the minimum field width (trailing zeros are not printed). +* However, the G specifier does not include a decimal point unless it +* is necessary. FITS requires that floating point values always include +* a decimal point, so this function inserts one, if necessary. + +* Parameters: +* buf +* A character string into which the value is written. +* digits +* The number of digits after the decimal point. If the supplied value +* is negative, the number of digits actually used may be reduced if +* the string would otherwise extend beyond the number of columns +* allowed by the FITS standard. If the value is positive, the +* specified number of digits are always produced, even if it means +* breaking the FITS standard. +* width +* The minimum field width to use. The value is right justified in +* this field width. +* maxwidth +* The maximum field width to use. A value of zero is returned if +* the maximum field width is exceeded. +* value +* The value to format. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The field width actually used, or zero if the value could not be +* formatted. This does not include the trailing null character. + +* Notes: +* - If there is room, a trailing zero is also added following the +* inserted decimal point. +*/ + +/* Local Variables: */ + char *c; + char *w, *r; + int i; + int ldigits; + int n; + int ret; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* The supplied value of "digits" may be negative. Obtain the positive + value giving the initial number of decimal digits to use. */ + ldigits = ( digits > 0 ) ? digits : -digits; + +/* Loop until a suitably encoded value has been obtained. */ + while( 1 ){ + +/* Write the value into the buffer. Most are formatted with a G specifier. + This will result in values between -0.001 and -0.0001 being formatted + without an exponent, and thus occupying (ldigits+6) characters. With + an exponent, these values would be formatted in (ldigits+5) characters + thus saving one character. This is important because the default value + of ldigits is 15, resulting in 21 characters being used by the G + specifier. This is one more than the maximum allowed by the FITS + standard. Using an exponent instead would result in 20 characters + being used without any loss of precision, thus staying within the FITS + limit. Note, the precision used with the E specifier is one less than + with the G specifier because the digit to the left of the decimal place + is significant with the E specifier, and so we only need (ldigits-1) + significant digits to the right of the decimal point. */ + if( value > -0.001 && value < -0.0001 ) { + (void) sprintf( buf, "%*.*E", width, ldigits - 1, value ); + } else { + (void) sprintf( buf, "%*.*G", width, ldigits, value ); + } + +/* Check that the value zero is not encoded with a minus sign (e.g. "-0."). + This also rounds out long sequences of zeros or nines. */ + CheckZero( buf, value, width, status ); + +/* If the formatted value includes an exponent, it will have 2 digits. + If the exponent includes a leading zero, remove it. */ + if( ( w = strstr( buf, "E-0" ) ) ) { + w += 2; + } else if( ( w = strstr( buf, "E+0" ) ) ){ + w += 2; + } else if( ( w = strstr( buf, "E0" ) ) ){ + w += 1; + } + +/* If a leading zero was found, shuffle everything down from the start of + the string by one character, over-writing the redundant zero, and insert + a space at the start of the string. */ + if( w ) { + r = w - 1 ; + while( w != buf ) *(w--) = *(r--); + *w = ' '; + } + +/* If the used field width was too large, reduce it and try again, so + long as we are allowed to change the number of digits being used. */ + ret = strlen( buf ); + if( ret > width && digits < 0 ){ + ldigits -= ( ret - width ); + +/* Otherwise leave the loop. Return zero field width if the maximum field + width was exceeded. */ + } else { + if( ret > maxwidth ) ret = 0; + break; + } + } + +/* If a formatted value was obtained, we need to ensure that the it includes + a decimal point. */ + if( ret ){ + +/* Get a pointer to the first digit in the buffer. */ + c = strpbrk( buf, "0123456789" ); + +/* Something funny is going on if there are no digits in the buffer, + so return a zero field width. */ + if( !c ){ + ret = 0; + +/* Otherwise... */ + } else { + +/* Find the number of digits following and including the first digit. */ + n = strspn( c, "0123456789" ); + +/* If the first non-digit character is a decimal point, do nothing. */ + if( c[ n ] != '.' ){ + +/* If there are two or more leading spaces, move the start of the string + two character to the left, and insert ".0" in the gap created. This + keeps the field right justified within the desired field width. */ + if( buf[ 0 ] == ' ' && buf[ 1 ] == ' ' ){ + for( i = 2; i < c - buf + n; i++ ) buf[ i - 2 ] = buf[ i ]; + c[ n - 2 ] = '.'; + c[ n - 1 ] = '0'; + +/* If there is just one leading space, move the start of the string + one character to the left, and insert "." in the gap created. This + keeps the field right justified within the desired field width. */ + } else if( buf[ 0 ] == ' ' ){ + for( i = 0; i < n; i++ ) c[ i - 1 ] = c[ i ]; + c[ n - 1 ] = '.'; + +/* If there are no leading spaces we need to move the end of the string + to the right. This will result in the string no longer being right + justified in the required field width. Return zero if there is + insufficient room for an extra character. */ + } else { + ret++; + if( ret > maxwidth ){ + ret = 0; + +/* Otherwise, more the end of the string one place to the right and insert + the decimal point. */ + } else { + for( i = strlen( c ); i >= n; i-- ) c[ i + 1 ] = c[ i ]; + c[ n ] = '.'; + } + } + } + } + } + +/* Return the field width. */ + return ret; +} + +static int EncodeValue( AstFitsChan *this, char *buf, int col, int digits, + const char *method, int *status ){ + +/* +* Name: +* EncodeValue + +* Purpose: +* Encode the current card's keyword value into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int EncodeValue( AstFitsChan *this, char *buf, int col, int digits, +* const char *method, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function encodes the keyword value defined in the current card +* of the supplied FitsChan and stores it at the start of the supplied +* buffer. The number of characters placed in the buffer is returned +* (not including a terminating null). + +* Parameters: +* this +* Pointer to the FitsChan. +* buf +* The buffer to receive the formatted value. This should be at least +* 70 characters long. +* col +* The column number within the FITS header card corresponding to the +* start of "buf". +* digits +* The number of digits to use when formatting floating point +* values. If the supplied value is negative, the number of digits +* actually used may be reduced if the string would otherwise extend +* beyond the number of columns allowed by the FITS standard. If the +* value is positive, the specified number of digits are always +* produced, even if it means breaking the FITS standard. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of columns used by the encoded value. + +* Notes: +* - The function returns 0 if an error has already occurred +* or if an error occurs for any reason within this function. +*/ + +/* Local Variables: */ + char *c; /* Pointer to next character */ + char *name; /* Pointer to the keyword name */ + double dval; /* Keyword value */ + void *data; /* Pointer to keyword value */ + int i; /* Loop count */ + int ilen; /* Length of imaginary part */ + int len; /* Returned length */ + int quote; /* Quote character found? */ + int rlen; /* Length of real part */ + int type; /* Data type for keyword in current card */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise returned length. */ + len = 0; + +/* Get the data type of the keyword. */ + type = CardType( this, status ); + +/* Get a pointer to the data value in the current card. */ + data = CardData( this, NULL, status ); + +/* Return if there is no defined value associated with the keyword in the + current card. */ + if( type != AST__UNDEF ) { + +/* Get the name of the keyword. */ + name = CardName( this, status ); + +/* Go through each supported data type (roughly in the order of + decreasing usage)... */ + +/* AST__FLOAT - stored internally in a variable of type "double". Right + justified to column 30 in the header card. */ + if( type == AST__FLOAT ){ + dval = *( (double *) data ); + len = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2, + AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status ); + if( len <= 0 && astOK ) { + astError( AST__BDFTS, "%s(%s): Cannot encode floating point value " + "%g into a FITS header card for keyword '%s'.", status, method, + astGetClass( this ), dval, name ); + } + +/* AST__STRING & AST__CONTINUE - stored internally in a null terminated array of + type "char". The encoded string is enclosed in single quotes, starting + at FITS column 11 and ending in at least column 20. Single quotes + in the string are replaced by two adjacent single quotes. */ + } else if( type == AST__STRING || type == AST__CONTINUE ){ + c = (char *) data; + +/* Enter the opening quote. */ + len = 0; + buf[ len++ ] = '\''; + +/* Inspect each character, looking for quotes. */ + for ( i = 0; c[ i ]; ) { + quote = ( c[ i ] == '\'' ); + +/* If it will not fit into the header card (allowing for doubled + quotes), give up here. */ + if ( len + ( quote ? 2 : 1 ) > AST__FITSCHAN_FITSCARDLEN - col ) break; + +/* Otherwise, copy it into the output buffer and double any quotes. */ + buf[ len++ ] = c[ i ]; + if ( quote ) buf[ len++ ] = '\''; + +/* Look at the next character. */ + i++; + } + +/* Pad the string out to the required minimum length with blanks and + add the final quote. */ + while( len < FITSSTCOL - col ) buf[ len++ ] = ' '; + buf[ len++ ] = '\''; + +/* Inspect any characters that weren't used. If any are non-blank, + report an error. */ + for ( ; c[ i ]; i++ ) { + if ( !isspace( c[ i ] ) ) { + astError( AST__BDFTS, + "%s(%s): Cannot encode string '%s' into a FITS " + "header card for keyword '%s'.", status, method, astGetClass( this ), + (char *) data, name ); + break; + } + } + +/* INTEGER - stored internally in a variable of type "int". Right justified + to column 30 in the header card. */ + } else if( type == AST__INT ){ + len = sprintf( buf, "%*d", FITSRLCOL - col + 1, + *( (int *) data ) ); + if( len < 0 || len > AST__FITSCHAN_FITSCARDLEN - col ) { + astError( AST__BDFTS, "%s(%s): Cannot encode integer value %d into a " + "FITS header card for keyword '%s'.", status, method, astGetClass( this ), + *( (int *) data ), name ); + } + +/* LOGICAL - stored internally in a variable of type "int". Represented by + a "T" or "F" in column 30 of the FITS header card. */ + } else if( type == AST__LOGICAL ){ + for( i = 0; i < FITSRLCOL - col; i++ ) buf[ i ] = ' '; + if( *( (int *) data ) ){ + buf[ FITSRLCOL - col ] = 'T'; + } else { + buf[ FITSRLCOL - col ] = 'F'; + } + len = FITSRLCOL - col + 1; + +/* AST__COMPLEXF - stored internally in an array of two "doubles". The real + part is right justified to FITS column 30. The imaginary part is right + justified to FITS column 50. */ + } else if( type == AST__COMPLEXF ){ + dval = ( (double *) data )[ 0 ]; + rlen = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2, + AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status ); + if( rlen <= 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) { + astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex " + "floating point value [%g,%g] into a FITS header card " + "for keyword '%s'.", status, method, astGetClass( this ), dval, + ( (double *) data )[ 1 ], name ); + } else { + dval = ( (double *) data )[ 1 ]; + ilen = EncodeFloat( buf + rlen, digits, + FITSIMCOL - FITSRLCOL, + AST__FITSCHAN_FITSCARDLEN - col - rlen, dval, status ); + if( ilen <= 0 ) { + astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a " + "complex floating point value [%g,%g] into a FITS header " + "card for keyword '%s'.", status, method, astGetClass( this ), + ( (double *) data )[ 0 ], dval, name ); + } else { + len = ilen + rlen; + } + } + +/* AST__COMPLEXI - stored internally in a an array of two "ints". */ + } else if( type == AST__COMPLEXI ){ + rlen = sprintf( buf, "%*d", FITSRLCOL - col + 1, + ( (int *) data )[ 0 ] ); + if( rlen < 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) { + astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex " + "integer value [%d,%d] into a FITS header card " + "for keyword '%s'.", status, method, astGetClass( this ), + ( (int *) data )[ 0 ], + ( (int *) data )[ 1 ], name ); + } else { + ilen = sprintf( buf + rlen, "%*d", FITSIMCOL - FITSRLCOL + 1, + ( (int *) data )[ 1 ] ); + if( ilen < 0 || ilen > AST__FITSCHAN_FITSCARDLEN - col - rlen ) { + astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a " + "complex integer value [%d,%d] into a FITS header card " + "for keyword '%s'.", status, method, astGetClass( this ), + ( (int *) data )[ 0 ], + ( (int *) data )[ 1 ], name ); + } else { + len = ilen + rlen; + } + } + +/* Report an internal (ast) programming error if the keyword is of none of the + above types. */ + } else if( astOK ){ + astError( AST__INTER, "EncodeValue: AST internal programming error - " + "FITS %s data-type not yet supported.", status, + type_names[ type ] ); + } + } + +/* If an error has occurred, return zero length. */ + if( !astOK ) len = 0; + +/* Return the answer. */ + return len; +} + +static AstGrismMap *ExtractGrismMap( AstMapping *map, int iax, + AstMapping **new_map, int *status ){ +/* +* Name: +* ExtractGrismMap + +* Purpose: +* Extract a GrismMap from the end of the supplied Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstGrismMap *ExtractGrismMap( AstMapping *map, int iax, +* AstMapping **new_map, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function examines the supplied Mapping; if the specified output +* coordinate of the Mapping is created directly by an un-inverted GrismMap, +* then a pointer to the GrismMap is returned as the function value. A new +* Mapping is also returned via parameter "new_map" which is a copy of +* the supplied Mapping, except that the GrismMap is replaced with a +* UnitMap. If no GrismMap is found, NULL is returned for both Mappings. +* The condition that "the specified output coordinate of the Mapping is +* created directly by an un-inverted GrismMap" means that the output +* of the GrismMap is no subsequently modified by any further Mappings +* before being returned as the "iax"th output of the supplied Mapping. +* This means the GrismMap must be "at the end of" a CmpMap, not in +* the middle of the CmpMap. + +* Parameters: +* map +* Pointer to the Mapping to check. +* iax +* The index for the output coordinate to be checked. +* new_map +* Pointer to a location at which to return a pointer to a new +* Mapping which is a copy of "map" except that the GrismMap is +* replaced by a UnitMap. NULL is returned if the specified output +* was not created by a GrismMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The extracted GrismMap, or NULL if the specified output was not +* created by a GrismMap. +*/ + +/* Local Variables: */ + AstMapping *mapa; /* First component Mapping */ + AstMapping *mapb; /* Second component Mapping */ + AstMapping *new_mapa; /* Replacement for first component Mapping */ + AstMapping *new_mapb; /* Replacement for second component Mapping */ + AstGrismMap *ret; /* Returned GrismMap */ + int inva; /* Invert attribute for mapa within the CmpMap */ + int invb; /* Invert attribute for mapb within the CmpMap */ + int na; /* Number of outputs for mapa */ + int old_inva; /* Current Invert attribute for mapa */ + int old_invb; /* Current Invert attribute for mapb */ + int series; /* Are component Mappings applied in series? */ + +/* Initialise */ + ret = NULL; + *new_map = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the supplied Mapping is a GrismMap which has not been inverted, + return it as the function value and return a UnitMap as the new + Mapping. */ + if( astIsAGrismMap( map ) ) { + if( !astGetInvert( map ) ) { + ret = astClone( map ); + *new_map = (AstMapping *) astUnitMap( 1, "", status ); + } + +/* If the supplied Mapping is a CmpMap, get its two component Mappings, + see if they are applied in parallel or series, and get the Invert + attribute values which the component Mappings had at the time the + CmpMap was created. */ + } else if( astIsACmpMap( map ) ) { + astDecompose( map, &mapa, &mapb, &series, &inva, &invb ); + +/* Temporaily reset the Invert attributes of the component Mappings back to + the values they had when the CmpMap was created. */ + old_inva = astGetInvert( mapa ); + old_invb = astGetInvert( mapb ); + astSetInvert( mapa, inva ); + astSetInvert( mapb, invb ); + +/* If the supplied Mapping is a series CmpMap, attempt to extract a + GrismMap from the second component Mapping ("mapb"). The first + component Mapping ("mapa") is unchanged. We do not need to consdier + the first component since we are only interested in GrismMaps which are + at the end of the CmpMap. */ + if( series ) { + ret = ExtractGrismMap( mapb, iax, &new_mapb, status ); + if( ret ) new_mapa = astClone( mapa ); + +/* If the supplied Mapping is a parallel CmpMap, attempt to extract a + GrismMap from the component Mapping which produces output "iax". The + other component Mapping is unchanged. */ + } else { + na = astGetNout( mapa ); + if( iax < na ) { + ret = ExtractGrismMap( mapa, iax, &new_mapa, status ); + if( ret ) new_mapb = astClone( mapb ); + } else { + ret = ExtractGrismMap( mapb, iax - na, &new_mapb, status ); + if( ret ) new_mapa = astClone( mapa ); + } + } + +/* If succesful, create a new CmpMap to return. */ + if( ret ) { + *new_map = (AstMapping *) astCmpMap( new_mapa, new_mapb, series, "", status ); + new_mapa = astAnnul( new_mapa ); + new_mapb = astAnnul( new_mapb ); + } + +/* Re-instate the original Invert attributes of the component Mappings. */ + astSetInvert( mapa, old_inva ); + astSetInvert( mapb, old_invb ); + +/* Annul the component Mapping pointers. */ + mapa = astAnnul( mapa ); + mapb = astAnnul( mapb ); + } + +/* Return the result. */ + return ret; +} + +static int MakeBasisVectors( AstMapping *map, int nin, int nout, + double *g0, AstPointSet *psetg, + AstPointSet *psetw, int *status ){ +/* +* Name: +* MakeBasisVectors + +* Purpose: +* Create a set of basis vectors in grid coordinates + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int MakeBasisVectors( AstMapping *map, int nin, int nout, +* double *g0, AstPointSet *psetg, +* AstPointSet *psetw, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns a set of unit vectors in grid coordinates, +* one for each grid axis. Each unit vector is parallel to the +* corresponding grid axis, and rooted at a specified grid position +* ("g0"). The IWC coordinates corresponding to "g0" and to the end of +* each of the unit vectors are also returned, together with a flag +* indicating if all the IWC coordinate values are good. + +* Parameters: +* map +* A pointer to a Mapping which transforms grid coordinates into +* intermediate world coordinates (IWC). The number of outputs must +* be greater than or equal to the number of inputs. +* nin +* The number of inputs for "map" (i.e. the number of grid axes). +* nout +* The number of outputs for "map" (i.e. the number of IWC axes). +* g0 +* Pointer to an array of holding the grid coordinates at the +* "root" position. +* psetg +* A pointer to a PointSet which can be used to hold the required +* grid positions. This should have room for nin+1 positions. On +* return, the first position holds "g0", and the subsequent "nin" +* positions hold are offset from "g0" by unit vectors along the +* corresponding grid axis. +* psetw +* A pointer to a PointSet which can be used to hold the required +* IWC position. This should also have room for nin+1 positions. On +* return, the values are the IWC coordinates corresponding to the +* grid positions returned in "psetg". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if all the axis values in "psetw" are good. +* Zero is returned otherwise. + +* Notes: +* - Zero is returned if an error occurs. +*/ + +/* Local Variables: */ + double **ptrg; + double **ptrw; + double *c; + int i; + int ii; + int j; + int ret; + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Get pointers to the data in the two supplied PointSets. */ + ptrg = astGetPoints( psetg ); + ptrw = astGetPoints( psetw ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Assume success. */ + ret = 1; + +/* Store the required grid positions in PointSet "pset1". The first + position is the supplied root grid position, g0. The next "nin" + positions are offset from the root position by a unit vector along + each grid axis in turn. Store values for each grid axis in turn. */ + for( i = 0; i < nin; i++ ) { + +/* Get a pointer to the first axis value for this grid axis. */ + c = ptrg[ i ]; + +/* Initially set all values for this axis to the supplied root grid value. */ + for( ii = 0; ii < nin + 1; ii++ ) c[ ii ] = g0[ i ]; + +/* Modify the value corresponding to the vector along this grid axis. */ + c[ i + 1 ] += 1.0; + } + +/* Transform these grid positions in IWC positions using the supplied + Mapping. */ + (void) astTransform( map, psetg, 1, psetw ); + +/* Check that all the transformed positions are good. */ + for( j = 0; j < nout; j++ ) { + c = ptrw[ j ]; + for( ii = 0; ii < nin + 1; ii++, c++ ) { + if( *c == AST__BAD ) { + ret = 0; + break; + } + } + } + } + +/* Return the result. */ + return ret; +} + +static int FindBasisVectors( AstMapping *map, int nin, int nout, + double *dim, AstPointSet *psetg, + AstPointSet *psetw, int *status ){ +/* +* Name: +* FindBasisVectors + +* Purpose: +* Find the a set of basis vectors in grid coordinates + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int FindBasisVectors( AstMapping *map, int nin, int nout, +* double *dim, AstPointSet *psetg, +* AstPointSet *psetw, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns a set of unit vectors in grid coordinates, +* one for each grid axis. Each unit vector is parallel to the +* corresponding grid axis, and rooted at a specified grid position +* ("g0"). The IWC coordinates corresponding to "g0" and to the end of +* each of the unit vectors are also returned, together with a flag +* indicating if all the IWC coordinate values are good. + +* Parameters: +* map +* A pointer to a Mapping which transforms grid coordinates into +* intermediate world coordinates (IWC). The number of outputs must +* be greater than or equal to the number of inputs. +* nin +* The number of inputs for "map" (i.e. the number of grid axes). +* nout +* The number of outputs for "map" (i.e. the number of IWC axes). +* dim +* Array dimensions, in pixels, if known (otherwise supplied a NULL +* pointer to values of AST__BAD). +* psetg +* A pointer to a PointSet which can be used to hold the required +* grid position. This should have room for nin+1 positions. On +* return, the first position holds the "root" position and the +* subsequent "nin" positions hold are offset from root position +* by unit vectors along the corresponding grid axis. +* psetw +* A pointer to a PointSet which can be used to hold the required +* IWC position. This should also have room for nin+1 positions. On +* return, the values are the IWC coordinates corresponding to the +* grid positions returned in "psetg". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if a set of basis vectors was found +* succesfully. Zero is returned otherwise. + +* Notes: +* - Zero is returned if an error occurs. +*/ + +/* Local Variables: */ + double *g0; + double dd; + double ddlim; + int i; + int ii; + int ret; + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Allocate an array to store the candidate root position. */ + g0 = astMalloc( sizeof( double )*(size_t) nin ); + if( astOK ) { + +/* First try the grid centre, if known. */ + ddlim = 0; + ret = 0; + if( dim ) { + ret = 1; + for( i = 0; i < nin; i++ ) { + if( dim[ i ] != AST__BAD ) { + g0[ i ] = 0.5*( 1 + dim[ i ] ); + if( dim[ i ] > ddlim ) ddlim = dim[ i ]; + } else { + ret = 0; + break; + } + } + } + if( ret ) ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); + +/* If this did not produce a set of good IWC positions, try grid position + (1,1,1...). */ + if( !ret ) { + for( i = 0; i < nin; i++ ) g0[ i ] = 1.0; + ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); + } + +/* If this did not produce a set of good IWC positions, try a sequence of + grid positions which move an increasing distance along each grid axis + from (1,1,1,...). Stop when we get further than "ddlim" from the + origin. */ + dd = 10.0; + if( ddlim == 0.0 ) ddlim = 10240.0; + while( !ret && dd <= ddlim ) { + +/* First try positions which extend across the middle of the data set. + If the image dimensions are known, make the line go from the "bottom + left corner" towards the "top right corner", taking the aspect ratio + of the image into account. Otherise, just use a vector of (1,1,1,..) */ + for( i = 0; i < nin; i++ ) { + if( dim && dim[ i ] != AST__BAD ) { + g0[ i ] = dd*dim[ i ]/ddlim; + } else { + g0[ i ] = dd; + } + } + ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); + +/* If the above didn't produce good positions, try moving out along each + grid axis in turn. */ + for( ii = 0; !ret && ii < nin; ii++ ) { + for( i = 0; i < nin; i++ ) g0[ i ] = 1.0; + g0[ ii ] = dd; + ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status ); + } + +/* Go further out from the origin for the next set of tests (if any). */ + dd *= 2.0; + } + } + +/* Free resources. */ + g0 = astFree( g0 ); + +/* Return the result. */ + return ret; +} + +static int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat, + int *axspec, const char *method, const char *class, int *status ) { +/* +* Name: +* FindLonLatSpecAxes + +* Purpose: +* Search the CTYPE values in a FitsStore for celestial and spectral axes. + +* Type: +* Private function. + +* Synopsis: +* int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat, +* int *axspec, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* The supplied FitsStore is searched for axes with a specified axis +* description character which describe celestial longitude or latitude +* or spectral position. + +* Parameters: +* store +* A structure containing values for FITS keywords relating to +* the World Coordinate System. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* axlon +* Address of a location at which to return the index of the +* longitude axis (if found). This is the value of "i" within the +* keyword name "CTYPEi". A value of -1 is returned if no longitude +* axis is found. +* axlat +* Address of a location at which to return the index of the +* latitude axis (if found). This is the value of "i" within the +* keyword name "CTYPEi". A value of -1 is returned if no latitude +* axis is found. +* axspec +* Address of a location at which to return the index of the +* spectral axis (if found). This is the value of "i" within the +* keyword name "CTYPEi". A value of -1 is returned if no spectral +* axis is found. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One is returned if both celestial axes were found. Zero is returned if +* either axis was not found. The presence of a spectral axis does not +* affect the returned value. + +* Notes: +* - If an error occurs, zero is returned. +*/ + +/* Local Variables: */ + char *assys; + char *astype; + char algcode[5]; + char stype[5]; + const char *ctype; + double dval; + int i; + int wcsaxes; + +/* Initialise */ + *axlon = -1; + *axlat = -1; + *axspec = -1; + +/* Check the global status. */ + if ( !astOK ) return 0; + +/* Obtain the number of FITS WCS axes in the header. If the WCSAXES header + was specified, use it. Otherwise assume it is the same as the number + of pixel axes. */ + dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); + if( dval != AST__BAD ) { + wcsaxes = (int) dval + 0.5; + } else { + wcsaxes = store->naxis; + } + +/* Loop round the FITS WCS axes, getting each CTYPE value. */ + for( i = 0; i < wcsaxes && astOK; i++ ){ + ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + +/* Check a value was found. */ + if( ctype ) { + +/* First check for spectral axes, either FITS-WCS or AIPS-like. */ + if( IsSpectral( ctype, stype, algcode, status ) || + IsAIPSSpectral( ctype, &astype, &assys, status ) ) { + *axspec = i; + +/* Otherwise look for celestial axes. Celestial axes must have a "-" as the + fifth character in CTYPE. */ + } else if( ctype[4] == '-' ) { + +/* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE + are "RA--" or "xLON" or "yzLN" ). */ + if( !strncmp( ctype, "RA--", 4 ) || + !strncmp( ctype, "AZ--", 4 ) || + !strncmp( ctype + 1, "LON", 3 ) || + !strncmp( ctype + 2, "LN", 2 ) ){ + *axlon = i; + +/* Otherwise see if it is a latitude axis. */ + } else if( !strncmp( ctype, "DEC-", 4 ) || + !strncmp( ctype, "EL--", 4 ) || + !strncmp( ctype + 1, "LAT", 3 ) || + !strncmp( ctype + 2, "LT", 2 ) ){ + *axlat = i; + } + } + } + } + +/* Indicate failure if an error occurred. */ + if( !astOK ) { + *axlon = -1; + *axlat = -1; + *axspec = -1; + } + +/* Return the result. */ + return ( *axlat != -1 && *axlon != -1 ); +} + +static void FindWcs( AstFitsChan *this, int last, int all, int rewind, + const char *method, const char *class, int *status ){ + +/* +* Name: +* FindWcs + +* Purpose: +* Find the first or last FITS WCS related keyword in a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void FindWcs( AstFitsChan *this, int last, int all, int rewind, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A search is made through the FitsChan for the first or last card +* which relates to a FITS WCS keyword (any encoding). If "last" is +* non-zero, the next card becomes the current card. If "last" is +* zero, the WCS card is left as the current card. Cards marked as +* having been read are included or not, as specified by "all". + +* Parameters: +* this +* Pointer to the FitsChan. +* last +* If non-zero, the last WCS card is searched for. Otherwise, the +* first WCS card is searched for. +* all +* If non-zero, then cards marked as having been read are included +* in the search. Otherwise such cards are ignored. +* rewind +* Only used if "last" is zero (i.e. the first card is being +* searched for). If "rewind" is non-zero, then the search starts +* from the first card in the FitsChan. If zero, the search starts +* from the current card. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The FitsChan is left at end-of-file if no FITS-WCS keyword cards +* are found in the FitsChan. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *keyname; /* Keyword name from current card */ + int nfld; /* Number of fields in keyword template */ + int old_ignore_used; /* Original value of variable ignore_used */ + +/* Check the global status. Also check the FitsChan is not empty. */ + if( !astOK || !this->head ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Indicate that we should, or should not, skip over cards marked as having + been read. */ + old_ignore_used = ignore_used; + ignore_used = all ? 0 : 1; + +/* If required, set the FitsChan to start or end of file. */ + if( last ) { + astSetCard( this, INT_MAX ); + } else if( rewind ) { + astClearCard( this ); + } + +/* If the current card is marked as used, and we are skipping used cards, + move on to the next unused card */ + if( CARDUSED( this->card ) ) MoveCard( this, last?-1:1, method, class, status ); + +/* Check each card moving backwards from the end to the start, or + forwards from the start to the end, until a WCS keyword is found, + or the other end of the FitsChan is reached. */ + while( astOK ){ + +/* Get the keyword name from the current card. */ + keyname = CardName( this, status ); + +/* Save a pointer to the keyword if it is the first non-null, non-comment + card. */ + if( keyname ) { + +/* If it matches any of the WCS keywords, move on one card + and break out of the loop. */ + if( Match( keyname, "CRVAL%d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CRPIX%d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CDELT%d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CROTA%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CTYPE%d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CUNIT%d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PC%3d%3d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CD%3d%3d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CD%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PC%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "LONGPOLE", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "LONPOLE%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "LATPOLE%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PROJP%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PV%d_%d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PS%d_%d%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "EPOCH", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "EQUINOX%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "MJD-OBS", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "DATE-OBS", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "TIMESYS", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "RADECSYS", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "RADESYS%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "C%1dVAL%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "C%1dPIX%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "C%1dELT%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "C%1dYPE%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "C%1dNIT%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CNPIX1", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "CNPIX2", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PPO%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "AMDX%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "AMDY%d", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "XPIXELSZ", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "YPIXELSZ", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTRAH", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTRAM", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTRAS", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTDECD", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTDECM", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTDECS", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTDECSN", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PLTSCALE", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PPO1", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PPO2", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PPO4", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "PPO5", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "WCSNAME%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "SPECSYS%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "SSYSSRC%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "ZSOURCE%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "VELOSYS%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "RESTFRQ%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "MJD_AVG%0c", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "OBSGEO-X", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "OBSGEO-Y", 0, NULL, &nfld, method, class, status ) || + Match( keyname, "OBSGEO-Z", 0, NULL, &nfld, method, class, status ) ) { + if( last ) MoveCard( this, 1, method, class, status ); + break; + } + } + +/* Leave the FitsChan at end-of-file if no WCS cards were found. */ + if( (last && FitsSof( this, status ) ) || + (!last && astFitsEof( this ) ) ) { + astSetCard( this, INT_MAX ); + break; + } else { + MoveCard( this, last?-1:1, method, class, status ); + } + } + +/* Re-instate the original flag indicating if cards marked as having been + read should be skipped over. */ + ignore_used = old_ignore_used; + +/* Return. */ + return; +} + +static int FindString( int n, const char *list[], const char *test, + const char *text, const char *method, + const char *class, int *status ){ +/* +* Name: +* FindString + +* Purpose: +* Find a given string within an array of character strings. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int FindString( int n, const char *list[], const char *test, +* const char *text, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function identifies a supplied string within a supplied +* array of valid strings, and returns the index of the string within +* the array. The test option may not be abbreviated, but case is +* insignificant. + +* Parameters: +* n +* The number of strings in the array pointed to be "list". +* list +* A pointer to an array of legal character strings. +* test +* A candidate string. +* text +* A string giving a description of the object, parameter, +* attribute, etc, to which the test value refers. +* This is only for use in constructing error messages. It should +* start with a lower case letter. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The index of the identified string within the supplied array, starting +* at zero. + +* Notes: +* - A value of -1 is returned if an error has already occurred, or +* if this function should fail for any reason (for instance if the +* supplied option is not specified in the supplied list). +*/ + +/* Local Variables: */ + int ret; /* The returned index */ + +/* Check global status. */ + if( !astOK ) return -1; + +/* Compare the test string with each element of the supplied list. Leave + the loop when a match is found. */ + for( ret = 0; ret < n; ret++ ) { + if( !Ustrcmp( test, list[ ret ], status ) ) break; + } + +/* Report an error if the supplied test string does not match any element + in the supplied list. */ + if( ret >= n && astOK ) { + astError( AST__RDERR, "%s(%s): Illegal value '%s' supplied for %s.", status, + method, class, test, text ); + ret = -1; + } + +/* Return the answer. */ + return ret; +} + +static int FitOK( int n, double *act, double *est, double tol, int *status ) { +/* +* Name: +* FitOK + +* Purpose: +* See if a fit is usable. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int FitOK( int n, double *act, double *est, double tol, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function is supplied with a set of actual data values, and the +* corresponding values estimated by some fitting process. It tests +* that the RMS residual between them is no more than "tol". + +* Parameters: +* n +* Number of data points. +* act +* Pointer to the start of the actual data values. +* est +* Pointer to the start of the estimated data values. +* tol +* The largest acceptable RMS error between "act" and "est". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if the two sets of values agree. Zero is +* returned otherwise. + +* Notes: +* - Zero is returned if an error occurs. +*/ + +/* Local Variables: */ + int ret, i; + double s1, s2; + double *px, *py, diff, mserr; + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Initialise the sum of the squared residuals, and the number summed. */ + s1 = 0.0; + s2 = 0.0; + +/* Initialise pointers to the next actual and estimated values to use. */ + px = act; + py = est; + +/* Loop round all pairs of good actual and estimate value. */ + for( i = 0; i < n; i++, px++, py++ ){ + if( *px != AST__BAD && *py != AST__BAD ) { + +/* Increment the sums need to find the RMS residual between the actual + and estimated values. */ + diff = *px - *py; + s1 += diff*diff; + s2 += 1.0; + } + } + +/* If the sums are usable... */ + if( s2 > 0.0 ) { + +/* Form the mean squared residual, and check if it is less than the + squared error limit. */ + mserr = s1/s2; + if( mserr < tol*tol ) ret = 1; + } + +/* Return the result. */ + return ret; +} + +static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, + int *perm, int *status ){ +/* +* Name: +* FitsAxisOrder + +* Purpose: +* Return the order of WCS axes specified by attribute FitsAxisOrder. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, +* int *perm, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns an array indicating the order of the WCS axes +* within the output FITS header, as specified by the FitsAxisOrder +* attribute. + +* Parameters: +* this +* Pointer to the FitsChan. +* nwcs +* The number of axes in "wcsfrm". +* wcsfrm +* The Frame containing the output WCS axes. +* perm +* Pointer to an array of "nwcs" integers. On exit, element "k" +* of this array holds the zero-based index of the FITS-WCS axis +* (i.e. one less than the value of "i" in the keyword names +* "CTYPEi", "CRVALi", etc) that describes the k'th axis in "wcsfrm". +* In other words, "perm[ast_index] = fits_index". The order is +* determined by the FitsAxisOrder attribute. If this attribute is +* "" or "", then "perm[k]=k" for all k on exit (i.e. +* a unit mapping between axes in "wcsfrm" and the FITS header). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Returns zero if the FitsAxisOrder attribute is ", and +* non-zero otherwise. This is a flag indicating if the returned +* values in "perm" can be used s they are. + +*/ + +/* Local Variables: */ + AstKeyMap *km; /* KeyMap holding axis indices keyed by axis symbols */ + char **words; /* Pointer to array of words from FitsAxisOrder */ + char attr_name[15];/* Attribute name */ + const char *attr; /* Pointer to a string holding the FitsAxisOrder value */ + int i; /* Loop count */ + int j; /* Zero-based axis index */ + int k; /* Zero-based axis index */ + int nword; /* Number of words in FitsAxisOrder */ + int result; /* Retrned value */ + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Initialise the returned array to a unit mapping from Frame axis to + FITS axis. */ + for( i = 0; i < nwcs; i++ ) perm[ i ] = i; + +/* Get the FitsAxisOrder attribute value, and set the returned value to + indicate if it is "". */ + attr = astGetFitsAxisOrder( this ); + result = !astChrMatch( attr, "" ); + +/* Return immediately if it is "" or "". */ + if( result && !astChrMatch( attr, "" ) ) { + +/* Create a KeyMap in which each key is the Symbol for an axis and the + associated value is the zero based index of the axis within "wcsfrm". */ + km = astKeyMap( "KeyCase=0", status ); + for( i = 0; i < nwcs; i++ ){ + sprintf( attr_name, "Symbol(%d)", i + 1 ); + astMapPut0I( km, astGetC( wcsfrm, attr_name ), i, NULL ); + } + +/* Split the FitsAxisOrder value into a collection of space-separated words. */ + words = astChrSplit( attr, &nword ); + +/* Loop round them all. */ + k = 0; + for( i = 0; i < nword; i++ ) { + +/* Get the zero based index within "wcsfrm" of the axis that has a Symbol + equal to the current word from FitsAxisOrder. */ + if( astMapGet0I( km, words[ i ], &j ) ) { + +/* If this "wcsfrm" axis has already been used, report an error. */ + if( j < 0 ) { + if( astOK ) astError( AST__ATTIN, "astWrite(fitschan): " + "attribute FitsAxisOrder (%s) refers to axis " + "%s more than once.", status, attr, words[ i ] ); + +/* Otherwise, set the corresponding element of the returned array, and + ensure this axis cannot be used again by assigning it an index of -1 + in the KeyMap. */ + } else { + perm[ j ] = k++; + astMapPut0I( km, words[ i ], -1, NULL ); + } + } + +/* Free the memory holding the copy of the word. */ + words[ i ] = astFree( words[ i ] ); + } + +/* Report an error if any wcsfrm axes were not included in FitsAxisOrder. */ + if( astOK ) { + for( i = 0; i < nwcs; i++ ){ + sprintf( attr_name, "Symbol(%d)", i + 1 ); + if( astMapGet0I( km, astGetC( wcsfrm, attr_name ), &j ) ) { + if( j >= 0 ) { + astError( AST__ATTIN, "astWrite(fitschan): attribute FitsAxisOrder " + "(%s) does not specify a position for WCS axis '%s'.", + status, attr, astGetC( wcsfrm, attr_name ) ); + break; + } + } + } + } + +/* Free resources. */ + words = astFree( words ); + km = astAnnul( km ); + } + + return result; +} + +static int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding, + double *dim, AstFrameSet *fs, const char *method, + const char *class, int *status ){ + +/* +* Name: +* FitsFromStore + +* Purpose: +* Store WCS keywords in a FitsChan. + +* Type: +* Private function. + +* Synopsis: + +* int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding, +* double *dim, AstFrameSet *fs, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using a specified encoding. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* encoding +* The encoding to use. +* dim +* Pointer to an array holding the array dimensions (AST__BAD +* indicates that the dimenson is not known). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + int ret; + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Set the current card so that it points to the last WCS-related keyword + in the FitsChan (whether previously read or not). Any new WCS related + keywords either over-write pre-existing cards for the same keyword, or + (if no pre-existing card exists) are inserted after the last WCS related + keyword. */ + FindWcs( this, 1, 1, 0, method, class, status ); + +/* Do each non-standard FITS encoding... */ + if( encoding == DSS_ENCODING ){ + ret = DSSFromStore( this, store, method, class, status ); + } else if( encoding == FITSPC_ENCODING ){ + ret = PCFromStore( this, store, method, class, status ); + } else if( encoding == FITSIRAF_ENCODING ){ + ret = IRAFFromStore( this, store, method, class, status ); + } else if( encoding == FITSAIPS_ENCODING ){ + ret = AIPSFromStore( this, store, method, class, status ); + } else if( encoding == FITSAIPSPP_ENCODING ){ + ret = AIPSPPFromStore( this, store, method, class, status ); + } else if( encoding == FITSCLASS_ENCODING ){ + ret = CLASSFromStore( this, store, fs, dim, method, class, status ); + +/* Standard FITS-WCS encoding */ + } else { + ret = WcsFromStore( this, store, method, class, status ); + } + +/* If there are any Tables in the FitsStore move the KeyMap that contains + them from the FitsStore to the FitsChan, from where they can be + retrieved using the public astGetTables method. */ + if( astMapSize( store->tables ) > 0 ) { + if( !this->tables ) this->tables = astKeyMap( " ", status ); + astMapCopy( this->tables, store->tables ); + (void) astAnnul( store->tables ); + store->tables = astKeyMap( " ", status ); + } + +/* If an error has occurred, return zero. */ + if( !astOK ) ret = 0; + +/* Return the answer. */ + return ret; +} + +static FitsStore *FitsToStore( AstFitsChan *this, int encoding, + const char *method, const char *class, int *status ){ + +/* +* Name: +* FitsToStore + +* Purpose: +* Return a pointer to a FitsStore structure containing WCS information +* read from the supplied FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* FitsStore *FitsToStore( AstFitsChan *this, int encoding, +* const char *method, const char *class ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function creates a new FitsStore containing WCS information +* read from the supplied FitsChan using the specified encoding. An +* error is reported and a null pointer returned if the FitsChan does +* not contain usable WCS information with the specified encoding. + +* Parameters: +* this +* Pointer to the FitsChan. +* encoding +* The encoding to use. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. + +* Returned Value: +* A pointer to a new FitsStore, or NULL if an error has occurred. The +* FitsStore should be released using FreeStore function when it is no +* longer needed. +*/ + +/* Local Variables: */ + AstFitsChan *trans; + FitsStore *ret; + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Allocate memory for the new FitsStore, and store NULL pointers in it. */ + ret = (FitsStore *) astMalloc( sizeof(FitsStore) ); + if( ret ) { + ret->cname = NULL; + ret->ctype = NULL; + ret->ctype_com = NULL; + ret->cunit = NULL; + ret->ps = NULL; + ret->radesys = NULL; + ret->wcsname = NULL; + ret->wcsaxes = NULL; + ret->pc = NULL; + ret->cdelt = NULL; + ret->crpix = NULL; + ret->crval = NULL; + ret->equinox = NULL; + ret->latpole = NULL; + ret->lonpole = NULL; + ret->mjdobs = NULL; + ret->mjdavg = NULL; + ret->dut1 = NULL; + ret->pv = NULL; + ret->specsys = NULL; + ret->ssyssrc = NULL; + ret->obsgeox = NULL; + ret->obsgeoy = NULL; + ret->obsgeoz = NULL; + ret->restfrq = NULL; + ret->restwav = NULL; + ret->zsource = NULL; + ret->velosys = NULL; + ret->asip = NULL; + ret->bsip = NULL; + ret->apsip = NULL; + ret->bpsip = NULL; + ret->imagfreq = NULL; + ret->axref = NULL; + ret->naxis = 0; + ret->timesys = NULL; + ret->tables = astKeyMap( " ", status ); + ret->skyref = NULL; + ret->skyrefp = NULL; + ret->skyrefis = NULL; + } + +/* Call the routine apropriate to the encoding. */ + if( encoding == DSS_ENCODING ){ + DSSToStore( this, ret, method, class, status ); + +/* All other foreign encodings are treated as variants of FITS-WCS. */ + } else { + +/* Create a new FitsChan containing standard translations for any + non-standard keywords in the supplied FitsChan. The non-standard + keywords are marked as provisionally read in the supplied FitsChan. */ + trans = SpecTrans( this, encoding, method, class, status ); + +/* Copy the required values to the FitsStore, using keywords in "trans" + in preference to those in "this". */ + WcsToStore( this, trans, ret, method, class, status ); + +/* Delete the temporary FitsChan holding translations of non-standard + keywords. */ + if( trans ) trans = (AstFitsChan *) astDelete( trans ); + +/* Store the number of pixel axes. This is taken as the highest index used + in any primary CRPIX keyword. */ + ret->naxis = GetMaxJM( &(ret->crpix), ' ', status ) + 1; + } + +/* If an error has occurred, free the returned FitsStore, and return a null + pointer. */ + if( !astOK ) ret = FreeStore( ret, status ); + +/* Return the answer. */ + return ret; +} + +static void FreeItem( double ****item, int *status ){ +/* +* Name: +* FreeItem + +* Purpose: +* Frees all dynamically allocated memory associated with a specified +* item in a FitsStore. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void FreeItem( double ****item, int *status ); + +* Class Membership: +* FitsChan member function. + +* Description: +* Frees all dynamically allocated memory associated with the specified +* item in a FitsStore. A NULL pointer is stored in the FitsStore. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->crval) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (j), and the pointer locates an +* array of axis keyword values. These arrays of keyword values have +* one element for every pixel axis (i) or projection parameter (m). +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempt to execute even if an error has occurred. +*/ + +/* Local Variables: */ + int si; /* Integer co-ordinate version index */ + int j; /* Intermediate co-ordinate axis index */ + int oldstatus; /* Old error status value */ + int oldreport; /* Old error reporting value */ + +/* Other initialisation to avoid compiler warnings. */ + oldreport = 0; + +/* Check the supplied pointer */ + if( item && *item ){ + +/* Start a new error reporting context. */ + oldstatus = astStatus; + if( !astOK ) { + oldreport = astReporting( 0 ); + astClearStatus; + } + +/* Loop round each coordinate version. */ + for( si = 0; si < astSizeOf( (void *) *item )/sizeof(double **); + si++ ){ + +/* Check the pointer stored for this co-ordinate version is not null. */ + if( (*item)[si] ) { + +/* Loop round the intermediate axes. */ + for( j = 0; j < astSizeOf( (void *) (*item)[si] )/sizeof(double *); + j++ ){ + +/* Free the pixel axis/parameter index pointer. */ + (*item)[si][j] = (double *) astFree( (void *) (*item)[si][j] ); + } + +/* Free the intermediate axes pointer */ + (*item)[si] = (double **) astFree( (void *) (*item)[si] ); + } + } + +/* Free the co-ordinate versions pointer */ + *item = (double ***) astFree( (void *) *item ); + +/* If there was an error status on entry to this function, re-instate it. + Otherwise, allow any new error status to remain. */ + if( oldstatus ){ + if( !astOK ) astClearStatus; + astSetStatus( oldstatus ); + astReporting( oldreport ); + } + } +} + +static void FreeItemC( char *****item, int *status ){ +/* +* Name: +* FreeItemC + +* Purpose: +* Frees all dynamically allocated memory associated with a specified +* string item in a FitsStore. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void FreeItemC( char *****item, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Frees all dynamically allocated memory associated with the specified +* string item in a FitsStore. A NULL pointer is stored in the FitsStore. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->ctype) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (j), and the pointer locates an +* array of axis keyword values. These arrays of keyword values have +* one element (a char pointyer) for every pixel axis (i) or +* projection parameter (m). +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + int si; /* Integer co-ordinate version index */ + int i; /* Intermediate co-ordinate axis index */ + int jm; /* Pixel co-ordinate axis or parameter index */ + int oldstatus; /* Old error status value */ + int oldreport; /* Old error reporting value */ + +/* Other initialisation to avoid compiler warnings. */ + oldreport = 0; + +/* Check the supplied pointer */ + if( item && *item ){ + +/* Start a new error reporting context. */ + oldstatus = astStatus; + if( !astOK ) { + oldreport = astReporting( 0 ); + astClearStatus; + } + +/* Loop round each coordinate version. */ + for( si = 0; si < astSizeOf( (void *) *item )/sizeof(char ***); + si++ ){ + +/* Check the pointer stored for this co-ordinate version is not null. */ + if( (*item)[si] ) { + +/* Loop round the intermediate axes. */ + for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **); + i++ ){ + +/* Check the pointer stored for this intermediate axis is not null. */ + if( (*item)[si][i] ) { + +/* Loop round the pixel axes or parameter values. */ + for( jm = 0; jm < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *); + jm++ ){ + +/* Free the string. */ + (*item)[si][i][jm] = (char *) astFree( (void *) (*item)[si][i][jm] ); + } + +/* Free the pixel axes/parameter pointer */ + (*item)[si][i] = (char **) astFree( (void *) (*item)[si][i] ); + } + } + +/* Free the intermediate axes pointer */ + (*item)[si] = (char ***) astFree( (void *) (*item)[si] ); + } + } + +/* Free the co-ordinate versions pointer */ + *item = (char ****) astFree( (void *) *item ); + +/* If there was an error status on entry to this function, re-instate it. + Otherwise, allow any new error status to remain. */ + if( oldstatus ){ + if( !astOK ) astClearStatus; + astSetStatus( oldstatus ); + astReporting( oldreport ); + } + } +} + +static FitsStore *FreeStore( FitsStore *store, int *status ){ +/* +* Name: +* FreeStore + +* Purpose: +* Free dynamic arrays stored in a FitsStore structure. + +* Type: +* Private function. + +* Synopsis: +* FitsStore *FreeStore( FitsStore *store, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function frees all dynamically allocated arrays stored in the +* supplied FitsStore structure, and returns a NULL pointer. + +* Parameters: +* store +* Pointer to the structure to clean. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if an error exists on entry. +*/ + +/* Return if no FitsStore was supplied. */ + if( !store ) return NULL; + +/* Free each of the dynamic arrays stored in the FitsStore. */ + FreeItemC( &(store->cname), status ); + FreeItemC( &(store->ctype), status ); + FreeItemC( &(store->ctype_com), status ); + FreeItemC( &(store->cunit), status ); + FreeItemC( &(store->radesys), status ); + FreeItemC( &(store->wcsname), status ); + FreeItemC( &(store->specsys), status ); + FreeItemC( &(store->ssyssrc), status ); + FreeItemC( &(store->ps), status ); + FreeItemC( &(store->timesys), status ); + FreeItem( &(store->pc), status ); + FreeItem( &(store->cdelt), status ); + FreeItem( &(store->crpix), status ); + FreeItem( &(store->crval), status ); + FreeItem( &(store->equinox), status ); + FreeItem( &(store->latpole), status ); + FreeItem( &(store->lonpole), status ); + FreeItem( &(store->mjdobs), status ); + FreeItem( &(store->dut1), status ); + FreeItem( &(store->mjdavg), status ); + FreeItem( &(store->pv), status ); + FreeItem( &(store->wcsaxes), status ); + FreeItem( &(store->obsgeox), status ); + FreeItem( &(store->obsgeoy), status ); + FreeItem( &(store->obsgeoz), status ); + FreeItem( &(store->restfrq), status ); + FreeItem( &(store->restwav), status ); + FreeItem( &(store->zsource), status ); + FreeItem( &(store->velosys), status ); + FreeItem( &(store->asip), status ); + FreeItem( &(store->bsip), status ); + FreeItem( &(store->apsip), status ); + FreeItem( &(store->bpsip), status ); + FreeItem( &(store->imagfreq), status ); + FreeItem( &(store->axref), status ); + store->tables = astAnnul( store->tables ); + FreeItem( &(store->skyref), status ); + FreeItem( &(store->skyrefp), status ); + FreeItemC( &(store->skyrefis), status ); + return (FitsStore *) astFree( (void *) store ); +} + +static char *FormatKey( const char *key, int c1, int c2, char s, int *status ){ +/* +* Name: +* FormatKey + +* Purpose: +* Format a keyword name with indices and co-ordinate version character. + +* Type: +* Private function. + +* Synopsis: +* char *FormatKey( const char *key, int c1, int c2, char s, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function formats a keyword name by including the supplied +* axis/parameter indices and co-ordinate version character. + +* Parameters: +* key +* The base name of the keyword (e.g. "CD", "CRVAL", etc). +* c1 +* An integer value to append to the end of the keyword. Ignored if +* less than zero. +* c2 +* A second integer value to append to the end of the keyword. Ignored if +* less than zero. This second integer is preceded by an underscore. +* s +* The co-ordinate version character to append to the end of the +* final string. Ignored if blank. +* status +* Pointer to the inherited status variable. +* Returned Value; +* A pointer to a static character buffer containing the final string. +* NULL if an error occurs. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + char *ret; + int len; + int nc; + +/* Initialise */ + ret = NULL; + +/* Check inherited status */ + if( !astOK ) return ret; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* No characters stored yet. A value of -1 is used to indicate that an + error has occurred. */ + len = 0; + +/* Store the supplied keyword base name. */ + if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%s", key ) ) >= 0 ){ + len += nc; + } else { + len = -1; + } + +/* If index c1 has been supplied, append it to the end of the string. */ + if( c1 >= 0 ) { + if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%d", c1 ) ) >= 0 ){ + len += nc; + } else { + len = -1; + } + +/* If index c2 has been supplied, append it to the end of the string, + preceded by an underscore. */ + if( c2 >= 0 ) { + if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "_%d", c2 ) ) >= 0 ){ + len += nc; + } else { + len = -1; + } + } + } + +/* If a co-ordinate version character has been supplied, append it to the end + of the string. */ + if( s != ' ' ) { + if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%c", s ) ) >= 0 ){ + len += nc; + } else { + len = -1; + } + } + +/* Report an error if necessary */ + if( len < 0 && astOK ) { + astError( AST__INTER, "FormatKey(fitschan): AST internal error; failed " + "to format the keyword %s with indices %d and %d, and " + "co-ordinate version %c.", status, key, c1, c2, s ); + ret = NULL; + } else { + ret = formatkey_buff; + } + return formatkey_buff; +} + +static AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ +/* +* Name: +* FsetFromStore + +* Purpose: +* Create a FrameSet using the the information previously stored in +* the suppllied FitsStore structure. + +* Type: +* Private function. + +* Synopsis: +* AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function creates a new FrameSet containing WCS information +* stored in the supplied FitsStore. A null pointer is returned and no +* error is reported if this is not possible. + +* Parameters: +* this +* The FitsChan from which the keywords were read. Warning messages +* are added to this FitsChan if the celestial co-ordinate system is +* not recognized. +* store +* Pointer to the FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new FrameSet, or a null pointer if no FrameSet +* could be constructed. + +* Notes: +* - The pixel Frame is given a title of "Pixel Coordinates", and +* each axis in the pixel Frame is given a label of the form "Pixel +* axis ", where is the axis index (starting at one). +* - The FITS CTYPE keyword values are used to set the labels for any +* non-celestial axes in the physical coordinate Frames, and the FITS +* CUNIT keywords are used to set the corresponding units strings. +* - On exit, the pixel Frame is the base Frame, and the physical +* Frame derived from the primary axis descriptions is the current Frame. +* - Extra Frames are added to hold any secondary axis descriptions. All +* axes within such a Frame refer to the same coordinate version ('A', +* 'B', etc). +*/ + +/* Local Variables: */ + AstFrame *frame; /* Pointer to pixel Frame */ + AstFrameSet *ret; /* Pointer to returned FrameSet */ + char buff[ 20 ]; /* Buffer for axis label */ + char s; /* Co-ordinate version character */ + int i; /* Pixel axis index */ + int physical; /* Index of primary physical co-ordinate Frame */ + int pixel; /* Index of pixel Frame in returned FrameSet */ + int use; /* Has this co-ordinate version been used? */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return (AstObject *) ret; + +/* Only proceed if there are some axes. */ + if( store->naxis ) { + +/* Create a Frame describing the pixel coordinate system. Give it the Domain + GRID. */ + frame = astFrame( store->naxis, "Title=Pixel Coordinates,Domain=GRID", status ); + +/* Store labels for each pixel axis. */ + if( astOK ){ + for( i = 0; i < store->naxis; i++ ){ + sprintf( buff, "Pixel axis %d", i + 1 ); + astSetLabel( frame, i, buff ); + } + } + +/* Create the FrameSet initially holding just the pixel coordinate frame + (this becomes the base Frame). */ + ret = astFrameSet( frame, "", status ); + +/* Annul the pointer to the pixel coordinate Frame. */ + frame = astAnnul( frame ); + +/* Get the index of the pixel Frame in the FrameSet. */ + pixel = astGetCurrent( ret ); + +/* Produce the Frame describing the primary axis descriptions, and add it + into the FrameSet. */ + AddFrame( this, ret, pixel, store->naxis, store, ' ', method, class, status ); + +/* Get the index of the primary physical co-ordinate Frame in the FrameSet. */ + physical = astGetCurrent( ret ); + +/* Loop, producing secondary axis Frames for each of the co-ordinate + versions stored in the FitsStore. */ + for( s = 'A'; s <= GetMaxS( &(store->crval), status ) && astOK; s++ ){ + +/* Only use this co-ordinate version character if any of the required + keywords (for any axis) are stored in the FitsStore. */ + use = 0; + for( i = 0; i < store->naxis; i++ ){ + if( GetItem( &(store->crval), i, 0, s, NULL, method, class, status ) != AST__BAD || + GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ) != AST__BAD || + GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ) != NULL ){ + use = 1; + break; + } + } + +/* If this co-ordinate version has been used, add a Frame to the returned + FrameSet holding this co-ordinate version. */ + if( use ) AddFrame( this, ret, pixel, store->naxis, store, s, method, class, status ); + } + +/* Ensure the pixel Frame is the Base Frame and the primary physical + Frame is the Current Frame. */ + astSetBase( ret, pixel ); + astSetCurrent( ret, physical ); + +/* Remove any unneeded Frames that hold a FITS representation of offset + coordinates. */ + TidyOffsets( ret, status ); + +/* If an error has occurred, free the returned FrameSet and return a null + pointer. */ + if( !astOK ) ret = astAnnul( ret ); + } + +/* Return the answer. */ + return (AstObject *) ret; +} + +static FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis, + double *dim, int encoding, const char *class, + const char *method, int *status ){ + +/* +* Name: +* FsetToStore + +* Purpose: +* Fill a FitsStore structure with a description of the supplied +* FrameSet. + +* Type: +* Private function. + +* Synopsis: + +* FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis, +* double *dim, int encoding, const char *class, +* const char *method, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function creates a new FitsStore containing WCS information +* read from the supplied FitsChan using the specified encoding. An +* error is reported and a null pointer returned if the FitsChan does +* not contain usable WCS information with the specified encoding. + +* Parameters: +* this +* Pointer to the FitsChan. +* fset +* Pointer to the FrameSet. +* naxis +* The number of axes in the Base Frame of the supplied FrameSet. +* dim +* Pointer to an array of pixel axis dimensions. Individual elements +* will be AST__BAD if dimensions are not known. +* encoding +* The encoding being used. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a new FitsStore, or NULL if an error has occurred. The +* FitsStore should be released using FreeStore function when it is no +* longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the AST error status set, or if it should fail for any +* reason. +* - The Base Frame in the FrameSet is used as the pixel Frame, and +* the Current Frame is used to create the primary axis descriptions. +* Attempts are made to create secondary axis descriptions for any +* other Frames in the FrameSet (up to a total of 26). +*/ + +/* Local Variables: */ + AstFrame *frame; /* A Frame */ + const char *id; /* Frame Ident string */ + int nfrm; /* Number of Frames in FrameSet */ + char *sid; /* Pointer to array of version letters */ + int frms[ 'Z' + 1 ]; /* Array of Frame indices */ + FitsStore *ret; /* Returned FitsStore */ + char s; /* Next available co-ordinate version character */ + char s0; /* Co-ordinate version character */ + int ibase; /* Base Frame index */ + int icurr; /* Current Frame index */ + int ifrm; /* Next Frame index */ + int isoff; /* Is the Frame an offset SkyFrame? */ + int primok; /* Primary Frame stored succesfully? */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Allocate memory for the new FitsStore, and store NULL pointers in it. */ + ret = (FitsStore *) astMalloc( sizeof(FitsStore) ); + if( astOK ) { + ret->cname = NULL; + ret->ctype = NULL; + ret->ctype_com = NULL; + ret->cunit = NULL; + ret->ps = NULL; + ret->radesys = NULL; + ret->wcsname = NULL; + ret->wcsaxes = NULL; + ret->pc = NULL; + ret->cdelt = NULL; + ret->crpix = NULL; + ret->crval = NULL; + ret->equinox = NULL; + ret->latpole = NULL; + ret->lonpole = NULL; + ret->dut1 = NULL; + ret->mjdobs = NULL; + ret->mjdavg = NULL; + ret->pv = NULL; + ret->specsys = NULL; + ret->ssyssrc = NULL; + ret->obsgeox = NULL; + ret->obsgeoy = NULL; + ret->obsgeoz = NULL; + ret->restfrq = NULL; + ret->restwav = NULL; + ret->zsource = NULL; + ret->velosys = NULL; + ret->asip = NULL; + ret->bsip = NULL; + ret->apsip = NULL; + ret->bpsip = NULL; + ret->imagfreq = NULL; + ret->axref = NULL; + ret->naxis = naxis; + ret->timesys = NULL; + ret->tables = astKeyMap( " ", status ); + ret->skyref = NULL; + ret->skyrefp = NULL; + ret->skyrefis = NULL; + +/* Obtain the index of the Base Frame (i.e. the pixel frame ). */ + ibase = astGetBase( fset ); + +/* Obtain the index of the Current Frame (i.e. the Frame to use as the + primary physical coordinate frame). */ + icurr = astGetCurrent( fset ); + +/* Does the current Frame contain a SkyFrame that describes offset + coordinates? */ + isoff = IsSkyOff( fset, icurr, status ); + +/* Add a description of the primary axes to the FitsStore, based on the + Current Frame in the FrameSet. */ + primok = AddVersion( this, fset, ibase, icurr, ret, dim, ' ', + encoding, isoff, method, class, status ); + +/* Do not add any alternate axis descriptions if the primary axis + descriptions could not be produced. */ + if( primok && astOK ) { + +/* Get the number of Frames in the FrameSet. */ + nfrm = astGetNframe( fset ); + +/* We now need to allocate a version letter to each Frame. Allocate + memory to hold the version letter assigned to each Frame. */ + sid = (char *) astMalloc( ( nfrm + 1 )*sizeof( char ) ); + +/* The frms array has an entry for each of the 26 possible version + letters (starting at A and ending at Z). Each entry holds the index of + the Frame which has been assigned that version character. Initialise + this array to indicate that no version letters have yet been assigned. */ + for( s = 'A'; s <= 'Z'; s++ ) { + frms[ (int) s ] = 0; + } + +/* Loop round all frames (excluding the current and base and IWC Frames which + do not need version letters). If the Frame has an Ident attribute consisting + of a single upper case letter, use it as its version letter unless that + letter has already been given to an earlier frame. IWC Frames are not + written out - identify them by giving them a "sid" value of 1 (an + illegal FITS axis description character). */ + for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ + sid[ ifrm ] = 0; + if( ifrm != icurr && ifrm != ibase ) { + frame = astGetFrame( fset, ifrm ); + if( astChrMatchN( astGetDomain( frame ), "IWC", 3 ) ) { + sid[ ifrm ] = 1; + } else { + id = astGetIdent( frame ); + if( strlen( id ) == 1 && isupper( id[ 0 ] ) ) { + if( frms[ (int) id[ 0 ] ] == 0 ) { + frms[ (int) id[ 0 ] ] = ifrm; + sid[ ifrm ] = id[ 0 ]; + } + } + } + (void) astAnnul( frame ); + } + } + +/* Now go round all the Frames again, looking for Frames which did not + get a version letter assigned to it on the previous loop. Assign them + letters now, selected them from the letters not already assigned + (lowest to highest). */ + s = 'A' - 1; + for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ + if( ifrm != icurr && ifrm != ibase && sid[ ifrm ] != 1 ) { + if( sid[ ifrm ] == 0 ){ + while( frms[ (int) ++s ] != 0 ); + if( s <= 'Z' ) { + sid[ ifrm ] = s; + frms[ (int) s ] = ifrm; + } + } + } + } + +/* If the primary headers describe offset coordinates, create an alternate + axis description for the correspondsing absolute coordinate system. */ + if( isoff && ++s <= 'Z' ) { + (void) AddVersion( this, fset, ibase, icurr, ret, dim, + s, encoding, -1, method, class, status ); + } + +/* Now go through all the other Frames in the FrameSet, attempting to + create alternate axis descriptions for each one. */ + for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ + s0 = sid[ ifrm ]; + if( s0 != 0 && s0 != 1 ) { + +/* Does it contain an offset sky frame? */ + isoff = IsSkyOff( fset, ifrm, status ); + +/* Write out the Frame - offset if it is offset, absolute otherwise. */ + (void) AddVersion( this, fset, ibase, ifrm, ret, dim, + s0, encoding, isoff, method, class, status ); + +/* If the Frame is offset, create an extra alternate axis description for + the correspondsing absolute coordinate system. */ + if( isoff && ++s <= 'Z' ) { + (void) AddVersion( this, fset, ibase, ifrm, ret, dim, + s, encoding, -1, method, class, status ); + } + } + } + +/* Free memory holding version letters */ + sid = (char *) astFree( (void *) sid ); + } + +/* If an error has occurred, or if the primary Frame could not be cerated, + free the returned FitsStore, and return a null pointer. */ + if( !astOK || !primok ) ret = FreeStore( ret, status ); + } + +/* Return the answer. */ + return ret; +} + +static int GetClean( AstFitsChan *this, int *status ) { + +/* +* Name: +* GetClean + +* Purpose: +* Return the value of the Clean attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int GetClean( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns the value of the Clean attribute. Since this +* attribute controls the behaviour of the FitsChan in the event of an +* error condition, it is is necessary to ignore any inherited error +* condition when getting the attribute value. This is why the +* astMAKE_GET macro is not used. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Clean value to use. +*/ + +/* Return if no FitsChan pointer was supplied. */ + if ( !this ) return 0; + +/* Return the attribute value, supplying a default value of 0 (false). */ + return ( this->clean == -1 ) ? 0 : (this->clean ? 1 : 0 ); +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied FitsChan, +* in bytes. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to FitsChan structure */ + FitsCard *card; /* Pointer to next FitsCard */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the FitsChan structure. */ + this = (AstFitsChan *) this_object; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->warnings ); + result += astGetObjSize( this->keyseq ); + result += astGetObjSize( this->keywords ); + result += astGetObjSize( this->tables ); + card = (FitsCard *) ( this->head ); + while( card ) { + result += astTSizeOf( card ); + result += card->size; + result += astTSizeOf( card->comment ); + card = GetLink( card, NEXT, "astGetObjSize", "FitsChan", status ); + if( (void *) card == this->head ) break; + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetCDMatrix( AstFitsChan *this, int *status ){ + +/* +* Name: +* GetCDMatrix + +* Purpose: +* Get the value of the CDMatrix attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int GetCDMatrix( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* If the CDMatrix attribute has been set, then its value is returned. +* Otherwise, the supplied FitsChan is searched for keywords of the +* form CDi_j. If any are found a non-zero value is returned. Otherwise +* a zero value is returned. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The attribute value to use. + +* Notes: +* - A value of zero is returned if an error has already occurred +* or if an error occurs for any reason within this function. +*/ + +/* Local Variables... */ + int ret; /* Returned value */ + int icard; /* Index of current card on entry */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* If a value has been supplied for the CDMatrix attribute, use it. */ + if( astTestCDMatrix( this ) ) { + ret = this->cdmatrix; + +/* Otherwise, check for the existence of CDi_j keywords... */ + } else { + +/* Save the current card index, and rewind the FitsChan. */ + icard = astGetCard( this ); + astClearCard( this ); + +/* If the FitsChan contains any keywords with the format "CDi_j" then return + 1. Otherwise return zero. */ + ret = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL ) ? 1 : 0; + +/* Reinstate the original current card index. */ + astSetCard( this, icard ); + } + +/* Return the result. */ + return astOK ? ret : 0; +} + +static int GetEncoding( AstFitsChan *this, int *status ){ + +/* +* Name: +* GetEncoding + +* Purpose: +* Get the value of the Encoding attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetEncoding( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* If the Encoding attribute has been set, then its value is returned. +* Otherwise, an attempt is made to determine the encoding scheme by +* looking for selected keywords within the FitsChan. Checks are made +* for the following keywords in the order specified, and the +* corresponding encoding is adopted when the first one is found ( where + +* i, j and m are integers and s is a single upper case character): +* +* 1) Any keywords starting with "BEGAST" = Native encoding +* 2) DELTAV and VELO-xxx (or VLSR) keywords = FITS-CLASS. +* 3) Any AIPS spectral CTYPE values: + +* Any of CDi_j, PROJP, LONPOLE, LATPOLE = FITS-AIPS++ encoding: +* None of the above = FITS-AIPS encoding. +* 4) Any keywords matching PCiiijjj = FITS-PC encoding +* 5) Any keywords matching CDiiijjj = FITS-IRAF encoding +* 6) Any keywords matching CDi_j, AND at least one of RADECSYS, PROJPi +* or CmVALi = FITS-IRAF encoding +* 7) Any keywords RADECSYS, PROJPi or CmVALi, and no CDi_j or PCi_j +* keywords, = FITS-PC encoding +* 8) Any keywords matching CROTAi = FITS-AIPS encoding +* 9) Keywords matching CRVALi = FITS-WCS encoding +* 10) The PLTRAH keyword = DSS encoding +* 11) If none of the above keywords are found, Native encoding is assumed. +* +* For cases 2) to 9), a check is also made that the header contains +* at least one of each keyword CTYPE, CRPIX and CRVAL. If not, then +* the checking process continues to the next case. This goes some way +* towards ensuring that the critical keywords used to determine the +* encoding are part of a genuine WCS description and have not just been +* left in the header by accident. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The encoding scheme identifier. + +* Notes: +* - The function returns UNKNOWN_ENCODING if an error has already occurred +* or if an error occurs for any reason within this function. +*/ + +/* Local Variables... */ + int hascd; /* Any CDi_j keywords found? */ + int haspc; /* Any PCi_j keywords found? */ + int haswcs; /* Any CRVAL, CTYPE and CRPIX found? */ + int icard; /* Index of current card on entry */ + int ret; /* Returned value */ + +/* Check the global status. */ + if( !astOK ) return UNKNOWN_ENCODING; + +/* If a value has been supplied for the Encoding attribute, use it. */ + if( astTestEncoding( this ) ) { + ret = this->encoding; + +/* Otherwise, check for the existence of certain critcal keywords... */ + } else { + +/* See if the header contains some CTYPE, CRPIX and CRVAL keywords. */ + haswcs = astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) && + astKeyFields( this, "CRPIX%d", 0, NULL, NULL ) && + astKeyFields( this, "CRVAL%d", 0, NULL, NULL ); + +/* See if there are any CDi_j keywords. */ + hascd = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL ); + +/* See if there are any PCi_j keywords. */ + haspc = astKeyFields( this, "PC%1d_%1d", 0, NULL, NULL ); + +/* Save the current card index, and rewind the FitsChan. */ + icard = astGetCard( this ); + astClearCard( this ); + +/* If the FitsChan contains any keywords starting with "BEGAST", then return + "Native" encoding. */ + if( astKeyFields( this, "BEGAST%2f", 0, NULL, NULL ) ){ + ret = NATIVE_ENCODING; + +/* Otherwise, look for a FITS-CLASS signature... */ + } else if( haswcs && LooksLikeClass( this, "astGetEncoding", "AstFitsChan", status ) ){ + ret = FITSCLASS_ENCODING; + +/* Otherwise, if the FitsChan contains any CTYPE keywords which have the + peculiar form used by AIPS, then use "FITS-AIPS" or "FITS-AIPS++" encoding. */ + } else if( haswcs && HasAIPSSpecAxis( this, "astGetEncoding", "AstFitsChan", status ) ){ + if( hascd || + astKeyFields( this, "PROJP%d", 0, NULL, NULL ) || + astKeyFields( this, "LONPOLE", 0, NULL, NULL ) || + astKeyFields( this, "LATPOLE", 0, NULL, NULL ) ) { + ret = FITSAIPSPP_ENCODING; + } else { + ret = FITSAIPS_ENCODING; + } + +/* Otherwise, if the FitsChan contains the "PLTRAH" keywords, use "DSS" + encoding. */ + } else if( astKeyFields( this, "PLTRAH", 0, NULL, NULL ) ){ + ret = DSS_ENCODING; + +/* Otherwise, if the FitsChan contains any keywords with the format + "PCiiijjj" then return "FITS-PC" encoding. */ + } else if( haswcs && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){ + ret = FITSPC_ENCODING; + +/* Otherwise, if the FitsChan contains any keywords with the format + "CDiiijjj" then return "FITS-IRAF" encoding. */ + } else if( haswcs && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){ + ret = FITSIRAF_ENCODING; + +/* Otherwise, if the FitsChan contains any keywords with the format + "CDi_j" AND there is a RADECSYS. PROJPi or CmVALi keyword, then return + "FITS-IRAF" encoding. If "CDi_j" is present but none of the others + are, return "FITS-WCS" encoding. */ + } else if( haswcs && hascd ) { + if( ( astKeyFields( this, "RADECSYS", 0, NULL, NULL ) && + !astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) || + ( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) && + !astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) || + ( astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL )) ){ + ret = FITSIRAF_ENCODING; + } else { + ret = FITSWCS_ENCODING; + } + +/* Otherwise, if the FitsChan contains any keywords with the format + RADECSYS. PROJPi or CmVALi keyword, then return "FITS-PC" encoding, + so long as there are no FITS-WCS equivalent keywords. */ + } else if( haswcs && !haspc && !hascd && ( + ( astKeyFields( this, "RADECSYS", 0, NULL, NULL ) && + !astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) || + ( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) && + !astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) || + astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL ) ) ) { + ret = FITSPC_ENCODING; + +/* Otherwise, if the FitsChan contains any keywords with the format + "CROTAi" then return "FITS-AIPS" encoding. */ + } else if( haswcs && astKeyFields( this, "CROTA%d", 0, NULL, NULL ) ){ + ret = FITSAIPS_ENCODING; + +/* Otherwise, if the FitsChan contains any keywords with the format + "CRVALi" then return "FITS-WCS" encoding. */ + } else if( haswcs && astKeyFields( this, "CRVAL%d", 0, NULL, NULL ) ){ + ret = FITSWCS_ENCODING; + +/* If none of these conditions is met, assume Native encoding. */ + } else { + ret = NATIVE_ENCODING; + } + +/* Reinstate the original current card index. */ + astSetCard( this, icard ); + } + +/* Return the encoding scheme. */ + return astOK ? ret : UNKNOWN_ENCODING; +} + +static void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status ){ +/* +* Name: +* GetFiducialNSC + +* Purpose: +* Return the Native Spherical Coordinates at the fiducial point of a +* WcsMap projection. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns the native spherical coords corresponding at +* the fiducial point of a WcsMap. +* +* The values of parameters 1 and 2 on the longitude axis of the WcsMap +* are usually used as the native spherical coordinates of the +* fiducial point. The default values for these parameters are equal +* to the native spherical coordinates of the projection reference point. +* The exception is that a TPN projection always uses the default +* values, since the projection parameters are used to store polynomial +* coefficients. + +* Parameters: +* map +* Pointer to the WcsMap. +* phi +* Address of a location at which to return the native spherical +* longitude at the fiducial point (radians). +* theta +* Address of a location at which to return the native spherical +* latitude at the fiducial point (radians). +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int axlon; /* Index of longitude axis */ + +/* Initialise */ + *phi = AST__BAD; + *theta = AST__BAD; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* If this is not a TPN projection get he value of the required + projection parameters (the default values for these are equal to the + fixed native shperical coordinates at the projection reference point). */ + if( astGetWcsType( map ) != AST__TPN ) { + axlon = astGetWcsAxis( map, 0 ); + if( astGetPV( map, axlon, 0 ) != 0.0 ) { + *phi = AST__DD2R*astGetPV( map, axlon, 1 ); + *theta = AST__DD2R*astGetPV( map, axlon, 2 ); + } else { + *phi = astGetNatLon( map ); + *theta = astGetNatLat( map ); + } + +/* If this is a TPN projection, the returned values are always the fixed + native shperical coordinates at the projection reference point). */ + } else { + *phi = astGetNatLon( map ); + *theta = astGetNatLat( map ); + } +} + +static void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status ){ +/* +* Name: +* GetFiducialPPC + +* Purpose: +* Return the IWC at the fiducial point of a WcsMap projection. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns the projection plane coords corresponding to +* the native spherical coords of the fiducial point of a FITS-WCS +* header. Note, projection plane coordinates (PPC) are equal to +* Intermediate World Coordinates (IWC) except for cases where the +* fiducial point does not correspond to the projection reference point. +* In these cases, IWC and PPC will be connected by a translation +* which ensures that the fiducial point corresponds to the origin of +* IWC. +* +* The values of parameters 1 and 2 on the longitude axis of +* the WcsMap are used as the native spherical coordinates of the +* fiducial point. The default values for these parameters are equal +* to the native spherical coordinates of the projection reference point. + +* Parameters: +* map +* Pointer to the WcsMap. +* x0 +* Address of a location at which to return the PPC X axis value at +* the fiducial point (radians). +* y0 +* Address of a location at which to return the PPC Y axis value at +* the fiducial point (radians). +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* Pointer to the native spherical PointSet */ + AstPointSet *pset2; /* Pointer to the intermediate world PointSet */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + int axlat; /* Index of latitude axis */ + int axlon; /* Index of longitude axis */ + int i; /* Loop count */ + int naxes; /* Number of axes */ + +/* Initialise */ + *x0 = AST__BAD; + *y0 = AST__BAD; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Save number of axes in the WcsMap. */ + naxes = astGetNin( map ); + +/* Allocate resources. */ + pset1 = astPointSet( 1, naxes, "", status ); + ptr1 = astGetPoints( pset1 ); + pset2 = astPointSet( 1, naxes, "", status ); + ptr2 = astGetPoints( pset2 ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Get the indices of the longitude and latitude axes in WcsMap. */ + axlon = astGetWcsAxis( map, 0 ); + axlat = astGetWcsAxis( map, 1 ); + +/* Use zero on all non-celestial axes. */ + for( i = 0; i < naxes; i++ ) ptr1[ i ][ 0 ] = 0.0; + +/* Get the native spherical coords at the fiducial point. */ + GetFiducialNSC( map, ptr1[ axlon ], ptr1[ axlat ], status ); + +/* Use the inverse WcsMap to convert the native longitude and latitude of + the fiducial point into PPC (x,y). */ + (void) astTransform( map, pset1, 0, pset2 ); + +/* Return the calculated PPC coords. */ + *x0 = ptr2[ axlon ][ 0 ]; + *y0 = ptr2[ axlat ][ 0 ]; + } + +/* Free resources. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); +} + +static int GetFiducialWCS( AstWcsMap *wcsmap, AstMapping *map2, int colon, + int colat, double *fidlon, double *fidlat, int *status ){ +/* +* Name: +* GetFiducialWCS + +* Purpose: +* Decide on the celestial coordinates of the fiducial point. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetFiducialWCS( AstWcsMap wcsmap, AstMapping map2, int colon, +* int colat, double *fidlon, double *fidlat, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns the celestial longitude and latitude values +* to use for the fiducial point. These are the values stored in FITS +* keywords CRVALi. + +* Parameters: +* wcsmap +* The WcsMap which converts Projection Plane Coordinates into +* native spherical coordinates. The number of outputs from this +* Mapping should match the number of inputs to "map2". +* map2 +* The Mapping which converts native spherical coordinates into WCS +* coordinates. +* colon +* The index of the celestial longitude output from "map2". +* colat +* The index of the celestial latitude output from "map2". +* fidlon +* Pointer to a location at which to return the celestial longitude +* value at the fiducial point. The value is returned in radians. +* fidlat +* Pointer to a location at which to return the celestial latitude +* value at the fiducial point. The value is returned in radians. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the fiducial point longitude or latitude could not be +* determined. One otherwise. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* Pointer to the native spherical PointSet */ + AstPointSet *pset2; /* Pointer to the WCS PointSet */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + int axlat; /* Index of latitude axis */ + int axlon; /* Index of longitude axis */ + int iax; /* Axis index */ + int naxin; /* Number of IWC axes */ + int naxout; /* Number of WCS axes */ + int ret; /* The returned FrameSet */ + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Allocate resources. */ + naxin = astGetNin( map2 ); + naxout = astGetNout( map2 ); + pset1 = astPointSet( 1, naxin, "", status ); + ptr1 = astGetPoints( pset1 ); + pset2 = astPointSet( 1, naxout, "", status ); + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + +/* Get the indices of the latitude and longitude outputs in the WcsMap. + These are not necessarily the same as "colat" and "colon" because "map2" + may contain a PermMap. */ + axlon = astGetWcsAxis( wcsmap, 0 ); + axlat = astGetWcsAxis( wcsmap, 1 ); + +/* Use zero on all non-celestial axes. */ + for( iax = 0; iax < naxin; iax++ ) ptr1[ iax ][ 0 ] = 0.0; + +/* Get the native spherical coords at the fiducial point. */ + GetFiducialNSC( wcsmap, ptr1[ axlon ], ptr1[ axlat ], status ); + +/* The fiducial point in the celestial coordinate system is found by + transforming the fiducial point in native spherical co-ordinates + into absolute physical coordinates using map2. */ + (void) astTransform( map2, pset1, 1, pset2 ); + +/* Store the returned WCS values. */ + *fidlon = ptr2[ colon ][ 0 ]; + *fidlat = ptr2[ colat ][ 0 ]; + +/* Indicate if we have been succesfull. */ + if( astOK && *fidlon != AST__BAD && *fidlat != AST__BAD ) ret = 1; + } + +/* Free resources. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Return the result. */ + return ret; +} + +static const char *GetFitsSor( const char *string, int *status ) { +/* +* Name: +* GetFitsSor + +* Purpose: +* Get the string used to represent an AST spectral standard of rest. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* const char *GetFitsSor( const char *string, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns a pointer to a static string which is the +* FITS equivalent to a given SpecFrame StdOfRest value. + +* Parameters: +* string +* Pointer to a constant null-terminated string containing the +* SpecFrame StdOfRest value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a static null-terminated string containing the FITS +* equivalent to the supplied string. NULL is returned if the supplied +* string has no FITS equivalent. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked wth the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Compare the supplied string with SpecFrame value for which there is a + known FITS equivalent. */ + if( !strcmp( string, "Topocentric" ) ){ + result = "TOPOCENT"; + } else if( !strcmp( string, "Geocentric" )){ + result = "GEOCENTR"; + } else if( !strcmp( string, "Barycentric" )){ + result = "BARYCENT"; + } else if( !strcmp( string, "Heliocentric" )){ + result = "HELIOCEN"; + } else if( !strcmp( string, "LSRK" )){ + result = "LSRK"; + } else if( !strcmp( string, "LSRD" )){ + result = "LSRD"; + } else if( !strcmp( string, "Galactic" )){ + result = "GALACTOC"; + } else if( !strcmp( string, "Local_group" )){ + result = "LOCALGRP"; + } else if( !strcmp( string, "Source" )){ + result = "SOURCE"; + } else { + result = NULL; + } + +/* Return the answer. */ + return result; +} + +static double GetItem( double ****item, int i, int jm, char s, char *name, + const char *method, const char *class, int *status ){ +/* +* Name: +* GetItem + +* Purpose: +* Retrieve a value for a axis keyword value from a FitStore structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* double GetItem( double ****item, int i, int jm, char s, char *name, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The requested keyword value is retrieved from the specified array, +* at a position indicated by the axis and co-ordinate version. +* AST__BAD is returned if the array does not contain the requested +* value. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->crval) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword values. These arrays of keyword values have +* one element for every pixel axis (j) or projection parameter (m). +* i +* The zero based intermediate axis index in the range 0 to 98. Set +* this to zero for keywords (e.g. CRPIX) which are not indexed by +* intermediate axis number. +* jm +* The zero based pixel axis index (in the range 0 to 98) or parameter +* index (in the range 0 to WCSLIB_MXPAR-1). Set this to zero for +* keywords (e.g. CRVAL) which are not indexed by either pixel axis or +* parameter number. +* s +* The co-ordinate version character (A to Z, or space), case +* insensitive +* name +* A string holding a name for the item of information. A NULL +* pointer may be supplied, in which case it is ignored. If a +* non-NULL pointer is supplied, an error is reported if the item +* of information has not been stored, and the supplied name is +* used to identify the information within the error message. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The required keyword value, or AST__BAD if no value has previously +* been stored for the keyword (or if an error has occurred). +*/ + +/* Local Variables: */ + double ret; /* Returned keyword value */ + int si; /* Integer co-ordinate version index */ + +/* Initialise */ + ret = AST__BAD; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Convert the character co-ordinate version into an integer index, and + check it is within range. The primary axis description (s=' ') is + given index zero. 'A' is 1, 'B' is 2, etc. */ + if( s == ' ' ) { + si = 0; + } else if( islower(s) ){ + si = (int) ( s - 'a' ) + 1; + } else { + si = (int) ( s - 'A' ) + 1; + } + if( si < 0 || si > 26 ) { + astError( AST__INTER, "GetItem(fitschan): AST internal error; " + "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); + +/* Check the intermediate axis index is within range. */ + } else if( i < 0 || i > 98 ) { + astError( AST__INTER, "GetItem(fitschan): AST internal error; " + "intermediate axis index %d is invalid.", status, i ); + +/* Check the pixel axis or parameter index is within range. */ + } else if( jm < 0 || jm > 99 ) { + astError( AST__INTER, "GetItem(fitschan): AST internal error; " + "pixel axis or parameter index %d is invalid.", status, jm ); + +/* Otherwise, if the array holding the required keyword is not null, + proceed... */ + } else if( *item ){ + +/* Find the number of coordinate versions in the supplied array. + Only proceed if it encompasses the requested co-ordinate + version. */ + if( astSizeOf( (void *) *item )/sizeof(double **) > si ){ + +/* Find the number of intermediate axes in the supplied array. + Only proceed if it encompasses the requested intermediate axis. */ + if( astSizeOf( (void *) (*item)[si] )/sizeof(double *) > i ){ + +/* Find the number of pixel axes or parameters in the supplied array. + Only proceed if it encompasses the requested index. */ + if( astSizeOf( (void *) (*item)[si][i] )/sizeof(double) > jm ){ + +/* Return the required keyword value. */ + ret = (*item)[si][i][jm]; + } + } + } + } + +/* If required, report an error if the requested item of information has + not been stored. */ + if( ret == AST__BAD && name && astOK ){ + astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status, + method, class, name ); + } + return ret; +} + +static int GetMaxJM( double ****item, char s, int *status ){ +/* +* Name: +* GetMaxJM + +* Purpose: +* Return the largest pixel axis or parameter index stored for an +* numerical axis keyword value in a FitStore structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetMaxJM( double ****item, char s, int *status) + +* Class Membership: +* FitsChan member function. + +* Description: +* The number of pixel axis numbers or projection parameters stored for +* a specified axis keyword is found and returned. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->crpix) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword values. These arrays of keyword values have +* one element for every pixel axis (j) or projection parameter (m). +* s +* The co-ordinate version character (A to Z, or space), case +* insensitive +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The maximum pixel axis number or projection parameter index (zero +* based). +*/ + +/* Local Variables: */ + int jm; /* Number of parameters/pixel axes */ + int i; /* Intermediate axis index */ + int ret; /* Returned axis index */ + int si; /* Integer co-ordinate version index */ + +/* Initialise */ + ret = -1; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the array holding the required keyword is not null, proceed... */ + if( *item ){ + +/* Convert the character co-ordinate version into an integer index, and + check it is within range. The primary axis description (s=' ') is + given index zero. 'A' is 1, 'B' is 2, etc. */ + if( s == ' ' ) { + si = 0; + } else if( islower(s) ){ + si = (int) ( s - 'a' ) + 1; + } else { + si = (int) ( s - 'A' ) + 1; + } + if( si < 0 || si > 26 ) { + astError( AST__INTER, "GetMaxJM(fitschan): AST internal error; " + "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); + return ret; + } + +/* Find the number of coordinate versions in the supplied array. + Only proceed if it encompasses the requested co-ordinate + version. */ + if( astSizeOf( (void *) *item )/sizeof(double **) > si ){ + +/* Check that the pointer to the array of intermediate axis values is not null. */ + if( (*item)[si] ){ + +/* Loop round each used element in this array. */ + for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(double *); + i++ ){ + if( (*item)[si][i] ){ + +/* Get the size of the pixel axis/projection parameter array for the + current intermediate axis, and subtract 1 to get the largest index. */ + jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(double) - 1; + +/* Ignore any trailing unused (AST__BAD) values. */ + while( jm >= 0 && (*item)[si][i][jm] == AST__BAD ) jm--; + +/* Update the returned value if the current value is larger. */ + if( jm > ret ) ret = jm; + } + } + } + } + } + return ret; +} + +static int GetMaxJMC( char *****item, char s, int *status ){ +/* +* Name: +* GetMaxJMC + +* Purpose: +* Return the largest pixel axis or parameter index stored for an +* character-valued axis keyword value in a FitStore structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetMaxJMC( char *****item, char s, int *status) + +* Class Membership: +* FitsChan member function. + +* Description: +* The number of pixel axis numbers or projection parameters stored for +* a specified axis keyword is found and returned. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->ctype) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword string pointers. These arrays of keyword +* string pointers have one element for every pixel axis (j) or +* projection parameter (m). +* s +* The co-ordinate version character (A to Z, or space), case +* insensitive +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The maximum pixel axis number or projection parameter index (zero +* based). +*/ + +/* Local Variables: */ + int jm; /* Number of parameters/pixel axes */ + int i; /* Intermediate axis index */ + int ret; /* Returned axis index */ + int si; /* Integer co-ordinate version index */ + +/* Initialise */ + ret = -1; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the array holding the required keyword is not null, proceed... */ + if( *item ){ + +/* Convert the character co-ordinate version into an integer index, and + check it is within range. The primary axis description (s=' ') is + given index zero. 'A' is 1, 'B' is 2, etc. */ + if( s == ' ' ) { + si = 0; + } else if( islower(s) ){ + si = (int) ( s - 'a' ) + 1; + } else { + si = (int) ( s - 'A' ) + 1; + } + if( si < 0 || si > 26 ) { + astError( AST__INTER, "GetMaxJMC(fitschan): AST internal error; " + "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); + return ret; + } + +/* Find the number of coordinate versions in the supplied array. + Only proceed if it encompasses the requested co-ordinate + version. */ + if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){ + +/* Check that the pointer to the array of intermediate axis values is not null. */ + if( (*item)[si] ){ + +/* Loop round each used element in this array. */ + for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **); + i++ ){ + if( (*item)[si][i] ){ + +/* Get the size of the pixel axis/projection parameter array for the + current intermediate axis, and subtract 1 to get the largest index. */ + jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) - 1; + +/* Ignore any trailing unused (NULL) values. */ + while( jm >= 0 && (*item)[si][i][jm] == NULL ) jm--; + +/* Update the returned value if the current value is larger. */ + if( jm > ret ) ret = jm; + } + } + } + } + } + return ret; +} + +static int GetMaxI( double ****item, char s, int *status ){ +/* +* Name: +* GetMaxI + +* Purpose: +* Return the largest WCS axis index stored for an axis keyword value in +* a FitStore structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetMaxJM( double ****item, char s) + +* Class Membership: +* FitsChan member function. + +* Description: +* The number of Wcs axis numbers stored for a specified axis keyword is +* found and returned. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->crval) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword values. These arrays of keyword values have +* one element for every pixel axis (j) or projection parameter (m). +* s +* The co-ordinate version character (A to Z, or space), case +* insensitive + +* Returned Value: +* The maximum WCS axis index (zero based). +*/ + +/* Local Variables: */ + int ret; /* Returned axis index */ + int si; /* Integer co-ordinate version index */ + +/* Initialise */ + ret = -1; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the array holding the required keyword is not null, proceed... */ + if( *item ){ + +/* Convert the character co-ordinate version into an integer index, and + check it is within range. The primary axis description (s=' ') is + given index zero. 'A' is 1, 'B' is 2, etc. */ + if( s == ' ' ) { + si = 0; + } else if( islower(s) ){ + si = (int) ( s - 'a' ) + 1; + } else { + si = (int) ( s - 'A' ) + 1; + } + if( si < 0 || si > 26 ) { + astError( AST__INTER, "GetMaxI(fitschan): AST internal error; " + "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); + return ret; + } + +/* Find the number of coordinate versions in the supplied array. + Only proceed if it encompasses the requested co-ordinate + version. */ + if( astSizeOf( (void *) *item )/sizeof(double **) > si ){ + +/* Check that the pointer to the array of intermediate axis values is not null. */ + if( (*item)[si] ){ + +/* Get the size of the intermediate axis array and subtract 1 to get the largest + index. */ + ret = astSizeOf( (void *) (*item)[si] )/sizeof(double *) - 1; + +/* Ignore any trailing unused (NULL) values. */ + while( ret >= 0 && (*item)[si][ret] == NULL ) ret--; + } + } + } + return ret; +} + +static char GetMaxS( double ****item, int *status ){ +/* +* Name: +* GetMaxS + +* Purpose: +* Return the largest (i.e. closest to Z) coordinate version character +* stored for a axis keyword value in a FitStore structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char GetMaxS( double ****item, int *status) + +* Class Membership: +* FitsChan member function. + +* Description: +* The largest (i.e. closest to Z) coordinate version character +* stored for a axis keyword value in a FitStore structure is found +* and returned. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->crval) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword values. These arrays of keyword values have +* one element for every pixel axis (j) or projection parameter (m). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The highest coordinate version character. +*/ + +/* Local Variables: */ + char ret; /* Returned axis index */ + int si; /* Integer index into alphabet */ + +/* Initialise */ + ret = ' '; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the array holding the required keyword is not null, proceed... */ + if( *item ){ + +/* Find the length of this array, and subtract 1 to get the largest index + in the array. */ + si = astSizeOf( (void *) *item )/sizeof(double **) - 1; + +/* Ignore any trailing null (i.e. unused) values. */ + while( si >= 0 && !(*item)[si] ) si--; + +/* Store the corresponding character */ + if( si == 0 ) { + ret = ' '; + } else { + ret = 'A' + si - 1; + } + } + return ret; +} + +static char *GetItemC( char *****item, int i, int jm, char s, char *name, + const char *method, const char *class, int *status ){ +/* +* Name: +* GetItemC + +* Purpose: +* Retrieve a string value for a axis keyword value from a FitStore +* structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char *GetItemC( char *****item, int i, int jm, char s, char *name, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The requested keyword string value is retrieved from the specified +* array, at a position indicated by the axis and co-ordinate version. +* NULL is returned if the array does not contain the requested +* value. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->ctype) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword string pointers. These arrays of keyword +* string pointers have one element for every pixel axis (j) or +* projection parameter (m). +* i +* The zero based intermediate axis index in the range 0 to 98. Set +* this to zero for keywords (e.g. CRPIX) which are not indexed by +* intermediate axis number. +* jm +* The zero based pixel axis index (in the range 0 to 98) or parameter +* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for +* keywords (e.g. CTYPE) which are not indexed by either pixel axis or +* parameter number. +* s +* The co-ordinate version character (A to Z, or space), case +* insensitive +* name +* A string holding a name for the item of information. A NULL +* pointer may be supplied, in which case it is ignored. If a +* non-NULL pointer is supplied, an error is reported if the item +* of information has not been stored, and the supplied name is +* used to identify the information within the error message. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the required keyword string value, or NULL if no value +* has previously been stored for the keyword (or if an error has +* occurred). +*/ + +/* Local Variables: */ + char *ret; /* Returned keyword value */ + int si; /* Integer co-ordinate version index */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Convert the character co-ordinate version into an integer index, and + check it is within range. The primary axis description (s=' ') is + given index zero. 'A' is 1, 'B' is 2, etc. */ + if( s == ' ' ) { + si = 0; + } else if( islower(s) ){ + si = (int) ( s - 'a' ) + 1; + } else { + si = (int) ( s - 'A' ) + 1; + } + if( si < 0 || si > 26 ) { + astError( AST__INTER, "GetItemC(fitschan): AST internal error; " + "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); + +/* Check the intermediate axis index is within range. */ + } else if( i < 0 || i > 98 ) { + astError( AST__INTER, "GetItemC(fitschan): AST internal error; " + "intermediate axis index %d is invalid.", status, i ); + +/* Check the pixel axis or parameter index is within range. */ + } else if( jm < 0 || jm > 99 ) { + astError( AST__INTER, "GetItem(fitschan): AST internal error; " + "pixel axis or parameter index %d is invalid.", status, jm ); + +/* Otherwise, if the array holding the required keyword is not null, + proceed... */ + } else if( *item ){ + +/* Find the number of coordinate versions in the supplied array. + Only proceed if it encompasses the requested co-ordinate + version. */ + if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){ + +/* Find the number of intermediate axes in the supplied array. + Only proceed if it encompasses the requested intermediate axis. */ + if( astSizeOf( (void *) (*item)[si] )/sizeof(char **) > i ){ + +/* Find the number of pixel axes or parameters in the supplied array. + Only proceed if it encompasses the requested index. */ + if( astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) > jm ){ + +/* Return the required keyword value. */ + ret = (*item)[si][i][jm]; + } + } + } + } + +/* If required, report an error if the requested item of information has + not been stored. */ + if( !ret && name && astOK ){ + astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status, + method, class, name ); + } + return ret; +} + +static AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname, + int extver, int extlevel, int report, + const char *method, int *status ){ + +/* +* Name: +* GetNamedTable + +* Purpose: +* Return a FitsTable holding the contents of a named FITS binary table. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname, +* int extver, int extlevel, int report, +* const char *method, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* If a table source function has been registered with FitsChan (using +* astTableSource), invoke it to read the required table from the external +* FITS file. If the extension is available in the FITS file, this will +* put a FitsTable into the "tables" KeyMap in the FitsChan structure, +* using the FITS extension name as the key - this will replace any +* FitsTable already present in the KeyMap with the same key. Finally, +* return a pointer to the FitsTable stored in the KeyMap - if any. +* +* This strategy allows the astPutTables or astPutTable method to be used +* as an alternative to registering a table source function with the +* FitsChan. Note, any table read using the source function is used +* in preference to any table stored in the FitsChan by an earlier call +* to astPutTables/astPutTable. + +* Parameters: +* this +* Pointer to the FitsChan. +* extname +* The key associated with the required table - should be the name +* of the FITS extension containing the binary table. +* extver +* The FITS "EXTVER" value for the required table. +* extlevel +* The FITS "EXTLEVEL" value for the required table. +* report +* If non-zero, report an error if the named table is not available. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the FitsTable, or NULL if the table is not avalable. +*/ + +/* Local Variables: */ + AstFitsTable *ret; + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Fitrst attempt to read the required table from the external FITS file. + Only proceed if table source function and wrapper have been supplied + using astTableSource. */ + if( this->tabsource && this->tabsource_wrap ){ + +/* Invoke the table source function asking it to place the required FITS + table in the FitsChan. This is an externally supplied function which may + not be thread-safe, so lock a mutex first. Note, a cloned FitsChan pointer + is sent to the table source function since the table source function will + annul the supplied FitsChan pointer. Also store the channel data + pointer in a global variable so that it can be accessed in the source + function using macro astChannelData. */ + astStoreChannelData( this ); + LOCK_MUTEX2; + ( *this->tabsource_wrap )( this->tabsource, astClone( this ), extname, + extver, extlevel, status ); + UNLOCK_MUTEX2; + } + +/* Now get a pointer to the required FitsTable, stored as an entry in the + "tables" KeyMap. Report an error if required. */ + if( ! (this->tables) || !astMapGet0A( this->tables, extname, &ret ) ){ + if( report && astOK ) { + astError( AST__NOTAB, "%s(%s): Failed to read FITS binary table " + "from extension '%s' (extver=%d, extlevel=%d).", status, + method, astGetClass( this ), extname, extver, extlevel ); + } + } + +/* Return the result. */ + return ret; +} + +static AstKeyMap *GetTables( AstFitsChan *this, int *status ) { + +/* +*++ +* Name: +c astGetTables +f AST_GETTABLES + +* Purpose: +* Retrieve any FitsTables currently in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c AstKeyMap *astGetTables( AstFitsChan *this ) +f RESULT = AST_GETTABLES( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +* If the supplied FitsChan currently contains any tables, then this +* function returns a pointer to a KeyMap. Each entry in the KeyMap +* is a pointer to a FitsTable holding the data for a FITS binary +* table. The key used to access each entry is the FITS extension +* name in which the table should be stored. +* +* Tables can be present in a FitsChan as a result either of using the +c astPutTable (or astPutTables) +f AST_PUTTABLE (or AST_PUTTABLES) +* method to store existing tables in the FitsChan, or of using the +c astWrite +f AST_WRITE +* method to write a FrameSet to the FitsChan. For the later case, if +* the FitsChan "TabOK" attribute is positive and the FrameSet requires +* a look-up table to describe one or more axes, then the "-TAB" +* algorithm code described in FITS-WCS paper III is used and the table +* values are stored in the FitsChan in the form of a FitsTable object +* (see the documentation for the "TabOK" attribute). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetTables() +f AST_GETTABLES = INTEGER +* A pointer to a deep copy of the KeyMap holding the tables currently +* in the FitsChan, or +c NULL +f AST__NULL +* if the FitsChan does not contain any tables. The returned +* pointer should be annulled using +c astAnnul +f AST_ANNUL +* when no longer needed. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstKeyMap *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the FitsChan contains any tables, return a pointer to a copy of + the KeyMap containing them. Otherwise, return a NULL pointer. */ + if( this->tables && astMapSize( this->tables ) > 0 ) { + result = astCopy( this->tables ); + } + +/* Return the result. */ + return result; +} + +static int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax, + int lonax, char s, const char *method, + const char *class, int *status ){ +/* +* Name: +* GetUsedPolyTan + +* Purpose: +* Get the value to use for the PolyTan attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax, +* int lonax, char s, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* If the PolyTan attribute is zero or positive, then its value is +* returned. If it is negative, the supplied FitsChan is searched for +* keywords of the form PVi_m. If any are found on the latitude axis, +* or if any are found on the longitude axis with "m" > 4, +1 is +* returned (meaning "use the distorted TAN conventio"). Otherwise 0 +* is returned (meaning "use the standard TAN convention"). +* +* If all the PVi_m values for m > 0 on either axis are zero, a warning is +* issued and zero is returned. + +* Parameters: +* this +* Pointer to the FitsChan. +* out +* Pointer to a secondary FitsChan. If the PV values in "this" are +* found to be unusable, they will be marked as used in both "this" +* and "out". +* latax +* The one-based index of the latitude axis within the FITS header. +* lonax +* The one-based index of the longitude axis within the FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The attribute value to use. + +* Notes: +* - A value of zero is returned if an error has already occurred +* or if an error occurs for any reason within this function. +*/ + +/* Local Variables... */ + char template[ 20 ]; + double pval; + int lbnd_lat; + int lbnd_lon; + int m; + int nfound1; + int nfound2; + int ok; + int ret; + int ubnd_lat; + int ubnd_lon; + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Get the value of the PolyTan attribute. */ + ret = astGetPolyTan( this ); + +/* If it is negative, we examine the FitsChan to see which convention to + use. */ + if( ret < 0 ) { + ret = 0; + +/* Search the FitsChan for latitude PV cards. */ + if( s != ' ' ) { + sprintf( template, "PV%d_%%d%c", latax, s ); + } else { + sprintf( template, "PV%d_%%d", latax ); + } + nfound1 = astKeyFields( this, template, 1, &ubnd_lat, &lbnd_lat ); + +/* Search the FitsChan for longitude PV cards. */ + if( s != ' ' ) { + sprintf( template, "PV%d_%%d%c", lonax, s ); + } else { + sprintf( template, "PV%d_%%d", lonax ); + } + nfound2 = astKeyFields( this, template, 1, &ubnd_lon, &lbnd_lon ); + +/* If any were found with "m" value greater than 4, assume the distorted + TAN convention is in use. Otherwise assume the stdanrd TAN convention is + in use. */ + if( nfound1 || ( nfound2 && ubnd_lon > 4 ) ) ret = 1; + +/* If the distorted TAN convention is to be used, check that at least one + of the PVi_m values is non-zero on each axis. We ignore the PVi_0 + (constant) terms in this check. */ + if( ret > 0 ) { + +/* Do the latitude axis first, skipping the first (constant) term. Assume + that all latitude pV values are zero until we find one that is not. */ + ok = 0; + for( m = 1; m <= ubnd_lat && !ok; m++ ) { + +/* Form the PVi_m keyword name. */ + if( s != ' ' ) { + sprintf( template, "PV%d_%d%c", latax, m, s ); + } else { + sprintf( template, "PV%d_%d", latax, m ); + } + +/* Get it's value. */ + if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0, + method, class, status ) ) { + +/* If the PVi_m header is not present in the FitsChan, use a default value. */ + pval = ( m == 1 ) ? 1.0 : 0.0; + } + +/* If the PVi_m header has a non-zero value, we can leave the loop. */ + if( pval != 0.0 ) ok = 1; + } + +/* If all the latitude PVi_m values are zero, issue a warning and return + zero, indicating that a simple undistorted TAN projection should be used. */ + if( !ok ) { + Warn( this, "badpv", "This FITS header describes a distorted TAN " + "projection, but all the distortion coefficients (the " + "PVi_m headers) on the latitude axis are zero.", method, + class, status ); + ret = 0; + + +/* Also, delete the PV keywords so that no attempt is made to use them. */ + for( m = 1; m <= ubnd_lat; m++ ) { + if( s != ' ' ) { + sprintf( template, "PV%d_%d%c", latax, m, s ); + } else { + sprintf( template, "PV%d_%d", latax, m ); + } + astClearCard( this ); + if( FindKeyCard( this, template, method, class, status ) ) { + DeleteCard( this, method, class, status ); + } + } + +/* Otherwise, do the same check for the longitude axis. */ + } else { + ok = 0; + for( m = 1; m <= ubnd_lon && !ok; m++ ) { + + if( s != ' ' ) { + sprintf( template, "PV%d_%d%c", lonax, m, s ); + } else { + sprintf( template, "PV%d_%d", lonax, m ); + } + + if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0, + method, class, status ) ) { + + pval = ( m == 1 ) ? 1.0 : 0.0; + } + + if( pval != 0.0 ) ok = 1; + } + + if( !ok ) { + Warn( this, "badpv", "This FITS header describes a distorted TAN " + "projection, but all the distortion coefficients (the " + "PVi_m headers) on the longitude axis are zero.", method, + class, status ); + ret = 0; + + for( m = 1; m <= ubnd_lon; m++ ) { + if( s != ' ' ) { + sprintf( template, "PV%d_%d%c", lonax, m, s ); + } else { + sprintf( template, "PV%d_%d", lonax, m ); + } + astClearCard( this ); + if( FindKeyCard( this, template, method, class, status ) ) { + DeleteCard( this, method, class, status ); + } + } + } + } + } + } + +/* Return the result. */ + return astOK ? ret : 0; +} + +static int GoodWarns( const char *value, int *status ){ +/* +* Name: +* GoodWarns + +* Purpose: +* Checks a string to ensure it is a legal list of warning conditions. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GoodWarns( const char *value, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function checks the supplied string to ensure it contains a space +* separated list of zero or more recognised warning conditions. An +* error is reported if it does not. + +* Parameters: +* value +* The string to check. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if the supplied string is not a legal list of +* conditions, or if an error has already occurred. One is returned +* otherwise. +*/ + +/* Local Variables: */ + char *b; /* Pointer to next buffer element */ + const char *c ; /* Pointer to next character */ + char buf[100]; /* Buffer for condition name */ + int inword; /* Are we in a word? */ + int n; /* Number of conditions supplied */ + int ret; /* Returned value */ + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Report an error and return if the pointer is null. */ + if( !value ){ + astError( AST__ATTIN, "astSetWarnings(fitschan): Null pointer " + "supplied for the Warnings attribute." , status); + return ret; + } + +/* Initialise things */ + inword = 0; + buf[ 0 ] = ' '; + b = buf + 1; + n = 0; + ret = 1; + +/* Loop round each character in the supplied string. */ + for( c = value ; c < value + strlen( value ) + 1; c++ ){ + +/* Have we found the first space or null following a word? */ + if( ( !(*c) || isspace( *c ) ) && inword ){ + +/* Add a space to the end of the buffer and terminate it. */ + *(b++) = ' '; + *b = 0; + +/* Check the word is legal by searching for it in the string of all + conditions, which should be lower case and have spaces at start and end. + The word in the buffer is delimited by spaces and so it will not match + a substring within a condition. If it is legal increment the number of + conditions found. */ + if( strstr( ALLWARNINGS, buf ) ){ + n++; + +/* Otherwise, report an error and break. */ + } else { + ret = 0; + *(--b) = 0; + astError( AST__ATTIN, "astSetWarnings(fitschan): Unknown " + "condition '%s' specified when setting the Warnings " + "attribute.", status, buf + 1 ); + break; + } + +/* Reset the pointer to the next character in the buffer, retaining the + initial space in the buffer. */ + b = buf + 1; + +/* Indicate we are no longer in a word. */ + inword = 0; + +/* Have we found the first non-space, non-null character following a space? */ + } else if( *c && !isspace( *c ) && !inword ){ + +/* Note we are now in a word. */ + inword = 1; + } + +/* If we are in a word, copy the lowercase character to the buffer. */ + if( inword ) *(b++) = tolower( *c ); + } + return ret; +} + +static AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i, + char s, AstSpecFrame *specfrm, + const char *method, const char *class, int *status ) { +/* +* Name: +* GrismSpecWcs + +* Purpose: +* Create a Mapping describing a FITS-WCS grism-dispersion algorithm + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i, char s, +* AstSpecFrame *specfrm, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function uses the contents of the supplied FitsStore to create +* a Mapping which goes from Intermediate World Coordinate (known as "w" +* in the context of FITS-WCS paper III) to the spectral system +* described by the supplied SpecFrame. +* +* The returned Mapping implements the grism "GRA" and "GRI" algorithms +* described in FITS-WCS paper III. + +* Parameters: +* algcode +* Pointer to a string holding the code for the required algorithm +* ("-GRA" or "-GRI"). +* store +* Pointer to the FitsStore structure holding the values to use for +* the WCS keywords. +* i +* The zero-based index of the spectral axis within the FITS header +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* specfrm +* Pointer to the SpecFrame. This specifies the "S" system - the +* system in which the CRVAL kewyords (etc) are specified. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a Mapping, or NULL if an error occurs. +*/ + +/* Local Variables: */ + AstFrameSet *fs; + AstMapping *gmap; + AstMapping *map1; + AstMapping *map2; + AstMapping *map2a; + AstMapping *map2b; + AstMapping *ret; + AstMapping *smap; + AstSpecFrame *wfrm; + double crv; + double dg; + double gcrv; + double pv; + double wcrv; + +/* Check the global status. */ + ret = NULL; + if( !astOK ) return ret; + +/* The returned Mapping will be a CmpMap including a GrismMap. This + GrismMap will produced wavelength as output. We also need the Mapping + from wavelength to the system represented by the supplied SpecFrame. + To get this, we first create a copy of the supplied SpecFrame (in order + to inherit the standard of rest, epoch, etc), and set its System to + wavlength in vacuum (for "-GRI") or air (for "-GRA"), and then use + astConvert to get the Mapping from the SpecFrame system to relevant + form of wavelength. */ + wfrm = astCopy( specfrm ); + astSetSystem( wfrm, strcmp( algcode, "-GRI" )?AST__AIRWAVE:AST__WAVELEN ); + astSetUnit( wfrm, 0, "m" ); + fs = astConvert( specfrm, wfrm, "" ); + if( fs ) { + smap = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + +/* Get the CRVAL value for the spectral axis (this will be in the S system). */ + crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( crv == AST__BAD ) crv = 0.0; + +/* Convert it to the wavelength system (vacuum or air) in metres. */ + astTran1( smap, 1, &crv, 1, &wcrv ); + +/* Create a GrismMap, and then use the projection parameters stored in + the FitsStore to set its attributes (convert degrees values to radians + and supply the defaults specified in FITS-WCS paper III). The FITS + paper specifies units in which these parameters should be stored in a + FITS header - distances are in metres and angles in degrees. */ + gmap = (AstMapping *) astGrismMap( "", status ); + pv = GetItem( &(store->pv), i, 0, s, NULL, method, class, status ); + astSetGrismG( gmap, ( pv != AST__BAD )?pv:0.0 ); + pv = GetItem( &(store->pv), i, 1, s, NULL, method, class, status ); + astSetGrismM( gmap, ( pv != AST__BAD )?(int) ( pv + 0.5 ):0); + pv = GetItem( &(store->pv), i, 2, s, NULL, method, class, status ); + astSetGrismAlpha( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 ); + pv = GetItem( &(store->pv), i, 3, s, NULL, method, class, status ); + astSetGrismNR( gmap, ( pv != AST__BAD )?pv:1.0 ); + pv = GetItem( &(store->pv), i, 4, s, NULL, method, class, status ); + astSetGrismNRP( gmap, ( pv != AST__BAD )?pv:0.0 ); + pv = GetItem( &(store->pv), i, 5, s, NULL, method, class, status ); + astSetGrismEps( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 ); + pv = GetItem( &(store->pv), i, 6, s, NULL, method, class, status ); + astSetGrismTheta( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 ); + +/* Store the reference wavelength found above as an attribute of the + GrismMap. */ + astSetGrismWaveR( gmap, wcrv ); + +/* Invert the GrismMap to get the (Wavelength -> grism parameter) Mapping, and + then combine it with the (S -> Wavelength) Mapping to get the (S -> grism + parameter) Mapping. */ + astInvert( gmap ); + map1 = (AstMapping *) astCmpMap( smap, gmap, 1, "", status ); + +/* Convert the reference point value from wavelength to grism parameter. */ + astTran1( gmap, 1, &wcrv, 1, &gcrv ); + +/* Find the rate of change of grism parameter with respect to the S + system at the reference point, dg/dS. */ + dg = astRate( map1, &crv, 0, 0 ); + if( dg != AST__BAD && dg != 0.0 ) { + +/* FITS-WCS paper II requires headers to be constructed so that dS/dw = 1.0 + at the reference point. Therefore dg/dw = dg/dS. Create a WinMap which + scales and shifts the "w" value to get the grism parameter value. */ + map2a = (AstMapping *) astZoomMap( 1, dg, "", status ); + map2b = (AstMapping *) astShiftMap( 1, &gcrv, "", status ); + map2 = (AstMapping *) astCmpMap( map2a, map2b, 1, "", status ); + map2a = astAnnul( map2a ); + map2b = astAnnul( map2b ); + +/* The Mapping to be returned is the concatenation of the above Mapping + (from w to g) with the Mapping from g to S. */ + astInvert( map1 ); + ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status ); + map2 = astAnnul( map2 ); + } + map1 = astAnnul( map1 ); + smap = astAnnul( smap ); + gmap = astAnnul( gmap ); + } + wfrm = astAnnul( wfrm ); + +/* Return the result */ + return ret; +} + +static int KeyFields( AstFitsChan *this, const char *filter, int maxfld, + int *ubnd, int *lbnd, int *status ){ + +/* +*+ +* Name: +* astKeyFields + +* Purpose: +* Find the ranges taken by integer fields within the keyword names +* in a FitsChan. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* int astKeyFields( AstFitsChan *this, const char *filter, int maxfld, +* int *ubnd, int *lbnd ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns the number of cards within a FitsChan which +* refer to keywords which match the supplied filter template. If the +* filter contains any integer field specifiers (e.g. "%d", "%3d", etc), +* it also returns the upper and lower bounds found for the integer +* fields. + +* Parameters: +* this +* Pointer to the FitsChan. +* filter +* The filter string. +* maxfld +* The size of the "ubnd" and "lbnd" arrays. +* ubnd +* A pointer to an integer array in which to return the +* upper bound found for each integer field in the filter. +* They are stored in the order in which they occur in the filter. +* If the filter contains too many fields to fit in the supplied +* array, the excess trailing fields are ignored. +* lbnd +* A pointer to an integer array in which to return the +* lower bound found for each integer field in the filter. + +* Returned Value: +* astKeyFields() +* The total number of cards matching the supplied filter in the +* FitsChan. + +* Filter Syntax: +* - The criteria for a keyword name to match a filter template are +* as follows: +* - All characters in the template other than "%" (and the field width +* and type specifiers which follow a "%") must be matched by an +* identical character in the test string. + - If a "%" occurs in the template, then the next character in the +* template should be a single digit specifying a field width. If it is +* zero, then the test string may contain zero or more matching characters. +* Otherwise, the test string must contain exactly the specified number +* of matching characters (i.e. 1 to 9). The field width digit may be +* omitted, in which case the test string must contain one or more matching +* characters. The next character in the template specifies the type of +* matching characters and must be one of "d", "c" or "f". Decimal digits +* are matched by "d", all upper (but not lower) case alphabetical +* characters are matched by "c", and all characters which may legally be +* found within a FITS keyword name are matched by "f". + +* Examples: +* - The filter "CRVAL1" accepts the single keyword CRVAL1. +* - The filter "CRVAL%1d" accepts the single keyword CRVAL0, CRVAL1, +* CRVAL2, up to CRVAL9. +* - The filter "CRVAL%d" accepts any keyword consisting of the string +* "CRVAL" followed by any integer value. +* - The filter "CR%0s1" accepts any keyword starting with the string "CR" +* and ending with the character "1" (including CR1). + +* Notes: +* - The entire FitsChan is searched, irrespective of the setting of +* the Card attribute. +* - If "maxfld" is supplied as zero, "ubnd" and "lbnd" are ignored, +* but the number of matching cards is still returned as the function value. +* - If no matching cards are found in the FitsChan, or if there are no +* integer fields in the filter, then the lower and upper bounds are +* returned as zero and -1 (i.e. reversed). +* - If an error has already occured, or if this function should fail +* for any reason, a value of zero is returned for the function value, +* and the lower and upper bounds are set to zero and -1. +*- +*/ + +/* Local Variables: */ + const char *class; /* Object class */ + const char *method; /* Method name */ + int *fields; /* Pointer to array of field values */ + int i; /* Field index */ + int icard; /* Index of current card on entry */ + int nmatch; /* No. of matching cards */ + int nf; /* No. of integer fields in the filter */ + int nfld; /* No. of integer fields in current keyword name */ + +/* Initialise the returned values. */ + nmatch = 0; + for( i = 0; i < maxfld; i++ ){ + lbnd[ i ] = 0; + ubnd[ i ] = -1; + } + nf = 0; + +/* Check the global error status. */ + if ( !astOK || !filter ) return nf; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the method name and object class for use in error messages. */ + method = "astKeyFields"; + class = astGetClass( this ); + +/* Count the number of integer fields in the filter string. */ + nf = CountFields( filter, 'd', method, class, status ); + +/* If this is larger than the supplied arrays, use the size of the arrays + instead. */ + if( nf > maxfld ) nf = maxfld; + +/* Allocate memory to hold the integer field values extracted from + each matching keyword. */ + fields = (int *) astMalloc( sizeof( int )*(size_t) nf ); + +/* Save the current card index, and rewind the FitsChan. */ + icard = astGetCard( this ); + astClearCard( this ); + +/* Check that the FitsChan is not empty and the pointer can be used. */ + if( !astFitsEof( this ) && astOK ){ + +/* Initialise the returned bounds. Any excess elements in the array are left + at the previously initialised values. */ + for( i = 0; i < nf; i++ ){ + lbnd[ i ] = INT_MAX; + ubnd[ i ] = -INT_MAX; + } + +/* Initialise the number of matching keywords. */ + nmatch = 0; + +/* Loop round all the cards in the FitsChan. */ + while( !astFitsEof( this ) && astOK ){ + +/* If the current keyword name matches the filter, update the returned + bounds and increment the number of matches. */ + if( Match( CardName( this, status ), filter, nf, fields, &nfld, + method, class, status ) ){ + for( i = 0; i < nf; i++ ){ + if( fields[ i ] > ubnd[ i ] ) ubnd[ i ] = fields[ i ]; + if( fields[ i ] < lbnd[ i ] ) lbnd[ i ] = fields[ i ]; + } + nmatch++; + } + +/* Move on to the next card. */ + MoveCard( this, 1, method, class, status ); + } + +/* If bounds were not found, returned 0 and -1. */ + for( i = 0; i < nf; i++ ){ + if( lbnd[ i ] == INT_MAX ){ + lbnd[ i ] = 0; + ubnd[ i ] = -1; + } + } + } + +/* Reinstate the original current card index. */ + astSetCard( this, icard ); + +/* Free the memory used to hold the integer field values extracted from + each matching keyword. */ + fields = (int *) astFree( (void *) fields ); + +/* If an error has occurred, returned no matches and reversed bounds. */ + if( !astOK ){ + nmatch = 0; + for( i = 0; i < maxfld; i++ ){ + lbnd[ i ] = 0; + ubnd[ i ] = -1; + } + } + +/* Returned the answer. */ + return nmatch; +} + +static int FindFits( AstFitsChan *this, const char *name, + char card[ AST__FITSCHAN_FITSCARDLEN + 1 ], int inc, int *status ){ + +/* +*++ +* Name: +c astFindFits +f AST_FINDFITS + +* Purpose: +* Find a FITS card in a FitsChan by keyword. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c int astFindFits( AstFitsChan *this, const char *name, char card[ 81 ], +c int inc ) +f RESULT = AST_FINDFITS( THIS, NAME, CARD, INC, STATUS ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function searches for a card in a FitsChan by keyword. The +* search commences at the current card (identified by the Card +* attribute) and ends when a card is found whose FITS keyword +* matches the template supplied, or when the last card in the +* FitsChan has been searched. +* +* If the search is successful (i.e. a card is found which matches +c the template), the contents of the card are (optionally) +f the template), the contents of the card are +* returned and the Card attribute is adjusted to identify the card +* found or, if required, the one following it. If the search is +c not successful, the function returns zero and the Card attribute +f not successful, the function returns .FALSE. and the Card attribute +* is set to the "end-of-file". + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c name +f NAME = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing a +f A character string containing a +* template for the keyword to be found. In the simplest case, +* this should simply be the keyword name (the search is case +* insensitive and trailing spaces are ignored). However, this +* template may also contain "field specifiers" which are +* capable of matching a range of characters (see the "Keyword +* Templates" section for details). In this case, the first card +* with a keyword which matches the template will be found. To +* find the next FITS card regardless of its keyword, you should +* use the template "%f". +c card +f CARD = CHARACTER * ( 80 ) (Returned) +c An array of at least 81 characters (to allow room for a +c terminating null) +f A character variable with at least 80 characters +* in which the FITS card which is found will be returned. If +c the search is not successful (or a NULL pointer is given), a +f the search is not successful, a +* card will not be returned. +c inc +f INC = LOGICAL (Given) +c If this value is zero (and the search is successful), the +f If this value is .FALSE. (and the search is successful), the +* FitsChan's Card attribute will be set to the index of the card +c that was found. If it is non-zero, however, the Card +f that was found. If it is .TRUE., however, the Card +* attribute will be incremented to identify the card which +* follows the one found. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFindFits() +f AST_FINDFITS = LOGICAL +c One if the search was successful, otherwise zero. +f .TRUE. if the search was successful, otherwise .FALSE.. + +* Notes: +* - The search always starts with the current card, as identified +* by the Card attribute. To ensure you search the entire contents +* of a FitsChan, you should first clear the Card attribute (using +c astClear). This effectively "rewinds" the FitsChan. +f AST_CLEAR). This effectively "rewinds" the FitsChan. +* - If a search is unsuccessful, the Card attribute is set to the +* "end-of-file" (i.e. to one more than the number of cards in the +* FitsChan). No error occurs. +c - A value of zero will be returned if this function is invoked +f - A value of .FALSE. will be returned if this function is invoked +* with the AST error status set, or if it should fail for any +* reason. + +* Examples: +c result = astFindFits( fitschan, "%f", card, 1 ); +f RESULT = AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS ) +* Returns the current card in a FitsChan and advances the Card +* attribute to identify the card that follows (the "%f" +* template matches any keyword). +c result = astFindFits( fitschan, "BITPIX", card, 1 ); +f RESULT = AST_FINDFITS( FITSCHAN, 'BITPIX', CARD, .TRUE., STATUS ) +* Searches a FitsChan for a FITS card with the "BITPIX" keyword +* and returns that card. The Card attribute is then incremented +* to identify the card that follows it. +c result = astFindFits( fitschan, "COMMENT", NULL, 0 ); +f RESULT = AST_FINDFITS( FITSCHAN, 'COMMENT', CARD, .FALSE., STATUS ) +* Sets the Card attribute of a FitsChan to identify the next +c COMMENT card (if any). The card itself is not returned. +f COMMENT card (if any) and returns that card. +c result = astFindFits( fitschan, "CRVAL%1d", card, 1 ); +f RESULT = AST_FINDFITS( FITSCHAN, 'CRVAL%1d', CARD, .TRUE., STATUS ) +* Searches a FitsChan for the next card with a keyword of the +* form "CRVALi" (for example, any of the keywords "CRVAL1", +* "CRVAL2" or "CRVAL3" would be matched). The card found (if +* any) is returned, and the Card attribute is then incremented +* to identify the following card (ready to search for another +* keyword with the same form, perhaps). + +* Keyword Templates: +* The templates used to match FITS keywords are normally composed +* of literal characters, which must match the keyword exactly +* (apart from case). However, a template may also contain "field +* specifiers" which can match a range of possible characters. This +* allows you to search for keywords that contain (for example) +* numbers, where the digits comprising the number are not known in +* advance. +* +* A field specifier starts with a "%" character. This is followed +* by an optional single digit (0 to 9) specifying a field +* width. Finally, there is a single character which specifies the + +* type of character to be matched, as follows: +* +* - "c": matches all upper case letters, +* - "d": matches all decimal digits, +* - "f": matches all characters which are permitted within a FITS +* keyword (upper case letters, digits, underscores and hyphens). +* +* If the field width is omitted, the field specifier matches one +* or more characters. If the field width is zero, it matches zero +* or more characters. Otherwise, it matches exactly the number of + +* characters specified. In addition to this: +* +* - The template "%f" will match a blank FITS keyword consisting +* of 8 spaces (as well as matching all other keywords). +* - A template consisting of 8 spaces will match a blank keyword +* (only). +* + +* For example: +* +* - The template "BitPix" will match the keyword "BITPIX" only. +* - The template "crpix%1d" will match keywords consisting of +* "CRPIX" followed by one decimal digit. +* - The template "P%c" will match any keyword starting with "P" +* and followed by one or more letters. +* - The template "E%0f" will match any keyword beginning with "E". +* - The template "%f" will match any keyword at all (including a +* blank one). +*-- +*/ + +/* Local Variables: */ + char *c; /* Pointer to next character to check */ + char *lname; /* Pointer to copy of name without trailing spaces */ + const char *class; /* Object class */ + const char *method; /* Calling method */ + int ret; /* Was a card found? */ + +/* Check the global status, and supplied keyword name. */ + if( !astOK ) return 0; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the calling method and object class. */ + method = "astFindFits"; + class = astGetClass( this ); + +/* Get a local copy of the keyword template. */ + lname = (char *) astStore( NULL, (void *) name, strlen(name) + 1 ); + +/* Terminate it to exclude trailing spaces. */ + c = lname + strlen(lname) - 1; + while( *c == ' ' && c >= lname ) *(c--) = 0; + +/* Use the private FindKeyCard function to find the card and make it the + current card. Always use the supplied current card (if any) if the + template is "%f" or "%0f". */ + if ( !strcmp( lname, "%f" ) || !strcmp( lname, "%0f" ) ){ + ret = astFitsEof( this ) ? 0 : 1; + } else { + ret = FindKeyCard( this, lname, method, class, status ); + } + +/* Only proceed if the card was found. */ + if( ret && astOK ){ + +/* Format the current card if a destination string was supplied. */ + if( card ) FormatCard( this, card, method, status ); + +/* Increment the current card pointer if required. */ + if( inc ) MoveCard( this, 1, method, class, status ); + +/* Indicate that a card has been formatted. */ + ret = 1; + } + +/* Free the memory holding the local copy of the keyword template. */ + lname = (char *) astFree( (void *) lname ); + +/* If an errror has occurred, return zero. */ + if( !astOK ) ret = 0; + +/* Return the answer. */ + return ret; +} + +static int FindKeyCard( AstFitsChan *this, const char *name, + const char *method, const char *class, int *status ){ +/* +* Name: +* FindKeyCard + +* Purpose: +* Find the next card refering to given keyword. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int FindKeyCard( AstFitsChan *this, const char *name, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Finds the next card which refers to the supplied keyword and makes +* it the current card. The search starts with the current card and ends +* when it reaches the last card. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a string holding the keyword template (using the +* syntax expected by the Match function). +* method +* Pointer to string holding name of calling method. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if a card was found refering to the given +* keyword. Otherwise zero is returned. + +* Notes: +* - If a NULL pointer is supplied for "name" then the current card +* is left unchanged. +* - The current card is set to NULL (end-of-file) if no card can be +* found for the supplied keyword. +*/ + +/* Local Variables: */ + int nfld; /* Number of fields in keyword template */ + int ret; /* Was a card found? */ + +/* Check the global status, and supplied keyword name. */ + if( !astOK || !name ) return 0; + +/* Indicate that no card has been found yet. */ + ret = 0; + +/* Search forward through the list until all cards have been checked. */ + while( !astFitsEof( this ) && astOK ){ + +/* Break out of the loop if the keyword name from the current card matches + the supplied keyword name. */ + if( Match( CardName( this, status ), name, 0, NULL, &nfld, method, class, status ) ){ + ret = 1; + break; + +/* Otherwise, move the current card on to the next card. */ + } else { + MoveCard( this, 1, method, class, status ); + } + } + +/* Return. */ + return ret; +} + +static double *FitLine( AstMapping *map, double *g, double *g0, double *w0, + double dim, double *tol, int *status ){ +/* +* Name: +* FitLine + +* Purpose: +* Check a Mapping for linearity. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* double *FitLine( AstMapping *map, double *g, double *g0, double *w0, +* double dim, double *tol, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function applies the supplied Mapping to a set of points along +* a straight line in the input space. It checks to see if the transformed +* positions also lie on a straight line (in the output space). If so, +* it returns the vector along this line in the output space which +* corresponds to a unit vector along the line in the input space. If +* not, a NULL pointer is returned. +* +* The returned vector is found by doing a least squares fit. + +* Parameters: +* map +* A pointer to the Mapping to test. The number of outputs must be +* greater than or equal to the number of inputs. +* g +* A pointer to an array holding a unit vector within the input space +* defining the straight line to be checked. The number of elements +* within this array should equal the number of inputs for "map". +* g0 +* A pointer to an array holding a position within the input space +* giving the central position of the vector "g". The number of elements +* within this array should equal the number of inputs for "map". +* w0 +* A pointer to an array holding a vector within the output space +* which corresponds to "g0". The number of elements within this array +* should equal the number of outputs for "map". +* dim +* The length of the pixel axis, or AST__BAD if unknown. +* tol +* Pointer to an array holding the tolerance for equality on each +* output axis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to dynamically allocated memory holding the required vector +* in the output space. The number of elements in this vector will equal +* the number of outputs for "map". The memory should be freed using +* astFree when no longer needed. If the Mapping is not linear, NULL +* is returned. + +* Notes: +* - NULL is returned if an error occurs. +*/ + +/* Local Constants: */ +#define NPO2 50 +#define NP (2*NPO2+1) + +/* Local Variables: */ + AstPointSet *pset1; + AstPointSet *pset2; + double **ptr1; + double **ptr2; + double *offset; + double *pax; + double *ret; + double *voffset; + double dax; + double denom; + double gap; + double sd2; + double sd; + double sdw; + double sw; + double wmax; + double wmin; + int i; + int j; + int n; + int nin; + int nout; + int ok; + +/* Initialise */ + ret = NULL; + +/* Check the inherited status and supplied axis size. */ + if( !astOK || dim == 0.0 ) return ret; + +/* Get the number of inputs and outputs for the Mapping. Return if the + number of outputs is smaller than the number of inputs. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + if( nout < nin ) return ret; + +/* Check the supplied position is good on all axes. */ + for( j = 0; j < nout; j++ ) { + if( w0[ j ] == AST__BAD ) return ret; + } + +/* We use NP points in the fit. If a value for "dim" has been supplied, + we use points evenly distributed over the whole axis. If not, we use + a gap of 1.0 (corresponds to an axis length of 100 pixels). + Choose the gap. */ + gap = ( dim != AST__BAD ) ? dim/NP : 1.0; + +/* Create PointSets to hold the input and output positions. */ + pset1 = astPointSet( NP, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + pset2 = astPointSet( NP, nout, "", status ); + ptr2 = astGetPoints( pset2 ); + +/* Allocate the returned array. */ + ret = astMalloc( sizeof( double )*(size_t) nout ); + +/* Allocate workspace to hold the constant offsets of the fit. */ + offset = astMalloc( sizeof( double )*(size_t) nout ); + voffset = astMalloc( sizeof( double )*(size_t) nout ); + +/* Indicate we have not yet got a usable returned vector. */ + ok = 0; + +/* Check we can use the pointers safely. */ + if( astOK ) { + +/* Set up the input positions: NP evenly spaced points along a line with + unit direction vector given by "g", centred at position given by "g0". */ + for( j = 0; j < nin; j++ ) { + pax = ptr1[ j ]; + dax = g[ j ]*gap; + for( i = -NPO2; i <= NPO2; i++ ) *(pax++) = g0[ j ] + dax*i; + } + +/* Transform these positions into the output space. */ + (void) astTransform( map, pset1, 1, pset2 ); + +/* Loop over all output axes, finding the component of the returned vector. */ + ok = 1; + for( j = 0; j < nout; j++ ) { + pax = ptr2[ j ]; + +/* Now loop over all the transformed points to form the other required + sums. We also form the sums needed to estimate the variance in the + calculated offset. */ + sdw = 0.0; + sw = 0.0; + sd = 0.0; + sd2 = 0.0; + n = 0; + wmax = -DBL_MAX; + wmin = DBL_MAX; + for( i = -NPO2; i <= NPO2; i++, pax++ ) { + if( *pax != AST__BAD ) { + +/* Increment the required sums. */ + sdw += i*(*pax); + sw += (*pax); + sd += i; + sd2 += i*i; + n++; + if( *pax > wmax ) wmax = *pax; + if( *pax < wmin ) wmin = *pax; + } + } + +/* If a reasonable number of good points were found, find the component of + the returned vector (excluding a scale factor of 1/gap). */ + denom = sd2*n - sd*sd; + if( n > NP/4 && denom != 0.0 ) { + +/* Find the constant scale factor to return for this axis. If the axis + value is constant, return zero. */ + if( wmax > wmin ) { + ret[ j ] = (sdw*n - sw*sd)/denom; + } else { + ret[ j ] = 0.0; + } + +/* Now find the constant offset for this axis. */ + offset[ j ] = (sw*sd2 - sdw*sd)/denom; + } else { + ok = 0; + break; + } + } + +/* Now check that the fit is good enough. Each axis is checked separately. + All axes must be good. */ + if( ok ) { + for( j = 0; j < nout; j++ ) { + +/* Store the axis values implied by the linear fit in the now un-needed ptr1[0] + array. */ + pax = ptr1[ 0 ]; + for( i = -NPO2; i <= NPO2; i++, pax++ ) { + *pax = i*ret[ j ] + offset[ j ]; + } + +/* Test the fit to see if we beleive that the mapping is linear. If + it is, scale the returned value from units of "per gap" to units of + "per pixel". Otherwise,indicate that he returned vector is unusable. */ + if( FitOK( NP, ptr2[ j ], ptr1[ 0 ], tol[ j ], status ) ) { + ret[ j ] /= gap; + } else { + ok = 0; + break; + } + } + } + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Free memory. */ + offset = astFree( offset ); + voffset = astFree( voffset ); + +/* If an error has occurred, or if the returned vector is unusable, + free any returned memory */ + if( !astOK || !ok ) ret = astFree( ret ); + +/* Return the answer. */ + return ret; + +/* Undefine local constants: */ +#undef NP +#undef NPO2 +} + +static int FitsEof( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* astFitsEof + +* Purpose: +* See if the FitsChan is at "end-of-file". + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* int astFitsEof( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* A value of zero is returned if any more cards remain to be read from the +* FitsChan. Otherwise a value of 1 is returned. Thus, it is +* equivalent to testing the FitsChan for an "end-of-file" condition. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* One if no more cards remain to be read, otherwise zero. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*- +*/ + +/* Check the supplied object. */ + if( !this ) return 1; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* If no more cards remain to be read, the current card pointer in the + FitsChan will be NULL. Return an appropriate integer value. */ + return this->card ? 0 : 1; +} + +static int FitsSof( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* FitsSof + +* Purpose: +* See if the FitsChan is at "start-of-file". + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int FitsSof( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A value of 1 is returned if the current card is the first card in +* the FitsChan. Otherwise a value of zero is returned. This function +* is much more efficient than "astGetCard(this) <= 1" . + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the current card is the first card. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +* - A non-zero value is returned if the FitsChan is empty. +*- +*/ + +/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ + if ( !this || !this->head ) return 1; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* If the current card is at the head of the linked list, it is the first + card. */ + return this->card == this->head; +} + +/* +*++ +* Name: +c astGetFits +f AST_GETFITS + +* Purpose: +* Get a named keyword value from a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c int astGetFits( AstFitsChan *this, const char *name, type *value ) +f RESULT = AST_GETFITS( THIS, NAME, VALUE, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +* This is a family of functions which gets a value for a named keyword, +* or the value of the current card, from a FitsChan using one of several +* different data types. The data type of the returned value is selected +* by replacing in the function name by one of the following strings +* representing the recognised FITS data types: +* +* - CF - Complex floating point values. +* - CI - Complex integer values. +* - F - Floating point values. +* - I - Integer values. +* - L - Logical (i.e. boolean) values. +* - S - String values. +* - CN - A "CONTINUE" value, these are treated like string values, but +* are encoded without an equals sign. +* +* The data type of the "value" +c parameter +f argument + +* depends on as follows: +* +c - CF - "double *" (a pointer to a 2 element array to hold the real and +c imaginary parts of the complex value). +c - CI - "int *" (a pointer to a 2 element array to hold the real and +c imaginary parts of the complex value). +c - F - "double *". +c - I - "int *". +c - L - "int *". +c - S - "char **" (a pointer to a static "char" array is returned at the +c location given by the "value" parameter, Note, the stored string +c may change on subsequent invocations of astGetFitsS so a +c permanent copy should be taken of the string if necessary). +c - CN - Like"S". +f - CF - DOUBLE PRECISION(2) (a 2 element array to hold the real and +f imaginary parts of the complex value). +f - CI - INTEGER(2) (a 2 element array to hold the real and imaginary +f parts of the complex value). +f - F - DOUBLE PRECISION. +f - I - INTEGER +f - L - LOGICAL +f - S - CHARACTER +f - CN - CHARACTER + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c name +f NAME = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string +f A character string +* containing the FITS keyword name. This may be a complete FITS +* header card, in which case the keyword to use is extracted from +* it. No more than 80 characters are read from this string. If +c NULL +f a single dot '.' +* is supplied, the value of the current card is returned. +c value +f VALUE = type (Returned) +c A pointer to a +f A +* buffer to receive the keyword value. The data type depends on +* as described above. The conents of the buffer on entry are left +* unchanged if the keyword is not found. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetFits() +f AST_GETFITS = LOGICAL +c A value of zero +f .FALSE. +* is returned if the keyword was not found in the FitsChan (no error +* is reported). Otherwise, a value of +c one +f .TRUE. +* is returned. + +* Notes: +* - If a name is supplied, the card following the current card is +* checked first. If this is not the required card, then the rest of the +* FitsChan is searched, starting with the first card added to the +* FitsChan. Therefore cards should be accessed in the order they are +* stored in the FitsChan (if possible) as this will minimise the time +* spent searching for cards. +* - If the requested card is found, it becomes the current card, +* otherwise the current card is left pointing at the "end-of-file". +* - If the stored keyword value is not of the requested type, it is +* converted into the requested type. +* - If the keyword is found in the FitsChan, but has no associated +* value, an error is reported. If necessary, the +c astTestFits +f AST_TESTFITS +* function can be used to determine if the keyword has a defined +* value in the FitsChan prior to calling this function. +* - An error will be reported if the keyword name does not conform +* to FITS requirements. +c - Zero +* - .FALSE. +* is returned as the function value if an error has already occurred, +* or if this function should fail for any reason. +* - The FITS standard says that string keyword values should be +* padded with trailing spaces if they are shorter than 8 characters. +* For this reason, trailing spaces are removed from the string +* returned by +c astGetFitsS +f AST_GETFITSS +* if the original string (including any trailing spaces) contains 8 +* or fewer characters. Trailing spaces are not removed from longer +* strings. +*-- +*/ + +/* Define a macro which expands to the implementation of the astGetFits + routine for a given data type. */ +#define MAKE_FGET(code,ctype,ftype) \ +static int GetFits##code( AstFitsChan *this, const char *name, ctype value, int *status ){ \ +\ +/* Local Variables: */ \ + const char *class; /* Object class */ \ + const char *method; /* Calling method */ \ + char *lcom; /* Supplied keyword comment */ \ + char *lname; /* Supplied keyword name */ \ + char *lvalue; /* Supplied keyword value */ \ + char *string; /* Pointer to returned string value */ \ + char *c; /* Pointer to next character */ \ + int cl; /* Length of string value */ \ + int ret; /* The returned value */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Ensure the source function has been called */ \ + ReadFromSource( this, status ); \ +\ +/* Store the calling method and object class. */ \ + method = "astGetFits"#code; \ + class = astGetClass( this ); \ +\ +/* Initialise the returned value. */ \ + ret = 0; \ +\ +/* Extract the keyword name from the supplied string. */ \ + if( name ) { \ + (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); \ + } else { \ + lname = NULL; \ + lvalue = NULL; \ + lcom = NULL; \ + } \ +\ +/* Attempt to find a card in the FitsChan refering to this keyword, \ + and make it the current card. Only proceed if a card was found. No \ + need to do the search if the value of the current card is required. */ \ + if( !lname || SearchCard( this, lname, method, class, status ) ){ \ +\ +/* Convert the stored data value to the requested type, and store it in \ + the supplied buffer. */ \ + if( !CnvValue( this, ftype, 0, value, method, status ) && astOK ) { \ + astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword " \ + "'%s' to %s.", status, method, class, \ + CardName( this, status ), type_names[ ftype ] ); \ + } \ +\ +/* If the returned value is a string containing 8 or fewer characters, \ + replace trailing spaces with null characters. */ \ + if( astOK ) { \ + if( ftype == AST__STRING ) { \ + string = *( (char **) value ); \ + if( string ) { \ + cl =strlen( string ); \ + if( cl <= 8 ) { \ + c = string + cl - 1; \ + while( *c == ' ' && c > string ) { \ + *c = 0; \ + c--; \ + } \ + } \ + } \ + } \ +\ +/* Indicate that a value is available. */ \ + ret = 1; \ + } \ +\ + } \ +\ +/* Context error message. */ \ + if( !astOK && lname && *lname ) { \ + astError( astStatus, "%s(%s): Cannot get value for FITS keyword " \ + "'%s'.", status, method, class, lname ); \ + } \ +\ +/* Release the memory used to hold keyword name, value and comment strings. */ \ + lname = (char *) astFree( (void *) lname ); \ + lvalue = (char *) astFree( (void *) lvalue ); \ + lcom = (char *) astFree( (void *) lcom ); \ +\ +/* Return the answer. */ \ + return ret; \ +\ +} + +/* Use the above macro to give defintions for the astGetFits method + for each FITS data type. */ +MAKE_FGET(CF,double *,AST__COMPLEXF) +MAKE_FGET(CI,int *,AST__COMPLEXI) +MAKE_FGET(F,double *,AST__FLOAT) +MAKE_FGET(I,int *,AST__INT) +MAKE_FGET(L,int *,AST__LOGICAL) +MAKE_FGET(S,char **,AST__STRING) +MAKE_FGET(CN,char **,AST__CONTINUE) +#undef MAKE_FGET + +static int FitsGetCom( AstFitsChan *this, const char *name, + char **comment, int *status ){ + +/* +*+ +* Name: +* astFitsGetCom + +* Purpose: +* Get a keyword comment from a FitsChan. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" + +* int astFitsGetCom( AstFitsChan *this, const char *name, +* char **comment ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function gets the comment associated with the next occurrence of +* a named keyword in a FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* A pointer to a +* string holding the keyword name. This may be a complete FITS +* header card, in which case the keyword to use is extracted from +* it. No more than 80 characters are read from this string. +* comment +* A pointer to a location at which to return a pointer to a string +* holding the keyword comment. Note, the stored string will change on +* subsequent invocations of astFitsGetCom so a permanent copy +* should be taken of the string if necessary. + +* Returned Value: +* astFitsGetCom() +* A value of zero is returned if the keyword was not found before +* the end of the FitsChan was reached (no error is reported). +* Otherwise, a value of one is returned. + +* Notes: +* - If a NULL pointer is supplied for "name" then the comment from +* the current card is returned. +* - The returned value is obtained from the next card refering to +* the required keyword, starting the search with the current card. +* Any cards occuring before the current card are not seached. If +* the entire contents of the FitsChan must be searched, then ensure +* the current card is the first card in the FitsChan by clearing the Card +* attribute. This effectively "rewinds" the FitsChan. +* - The current card is updated to become the card following the one +* read by this function. If the card read by this function is the +* last one in the FitsChan, then the current card is left pointing at the +* "end-of-file". +* - An error will be reported if the keyword name does not conform +* to FITS requirements. +* - A NULL pointer is returned for the comment string if the keyword +* has no comment. +* - Zero is returned as the function value if an error has already +* occurred, or if this function should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *method; /* Calling method */ + const char *class; /* Object class */ + char *lcom; /* Supplied keyword comment */ + char *lname; /* Supplied keyword name */ + char *lvalue; /* Supplied keyword value */ + int ret; /* The returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Initialise the returned value. */ + ret = 0; + +/* Store the method name and object class. */ + method = "astFitsGetCom"; + class = astGetClass( this ); + +/* Extract the keyword name from the supplied string (if supplied). */ + if( name ){ + (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); + } else { + lname = NULL; + lcom = NULL; + lvalue = NULL; + } + +/* Find the next card in the FitsChan refering to this keyword. This will + be the current card if no keyword name was supplied. The matching card + is made the current card. Only proceed if a card was found. */ + if( FindKeyCard( this, lname, method, class, status ) ){ + +/* Copy the comment into a static buffer, and return a pointer to it. */ + if( CardComm( this, status ) ){ + (void) strncpy( fitsgetcom_sval, CardComm( this, status ), AST__FITSCHAN_FITSCARDLEN ); + fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN ] = 0; + if( comment ) *comment = fitsgetcom_sval; + } else { + if( comment ) *comment = NULL; + } + +/* Move on to the next card. */ + MoveCard( this, 1, method, class, status ); + +/* Indicate that a value is available. */ + if( astOK ) ret = 1; + } + +/* Release the memory used to hold keyword name, value and comment strings. */ + lname = (char *) astFree( (void *) lname ); + lvalue = (char *) astFree( (void *) lvalue ); + lcom = (char *) astFree( (void *) lcom ); + +/* Return the answer. */ + return ret; +} + +static int SetFits( AstFitsChan *this, const char *keyname, void *value, + int type, const char *comment, int overwrite, int *status ){ + +/* +* Name: +* SetFits + +* Purpose: +* Store a keyword value of any type in a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int SetFits( AstFitsChan *this, const char *keyname, void *value, +* int type, const char *comment, int overwrite, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function stores the supplied value for the supplied keyword +* in the supplied FitsChan, assuming it is of the supplied data type. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* A pointer to a string holding the keyword name. +* value +* A pointer to a buffer holding the keyword value. For strings, +* the buffer should hold the address of a pointer to the character +* string. +* type +* The keyword type. +* comment +* A pointer to a string holding a comment to associated with the +* keyword. If a NULL pointer or a blank string is supplied, then +* any comment included in the string supplied for the "name" parameter +* is used instead. If "name" contains no comment, then any existing +* comment in the card being over-written is retained, or a NULL +* pointer is stored if a new card is being inserted. If the data +* value being stored for the card is the same as the card being +* over-written, then any existing comment is retained. +* overwrite +* If non-zero, the new card formed from the supplied keyword name, +* value and comment string over-writes the current card, and the +* current card is incremented to refer to the next card. If zero, the +* new card is inserted in front of the current card and the current +* card is left unchanged. In either case, if the current card on +* entry points to the "end-of-file", the new card is appended to the +* end of the list. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 0 is returned if the value could not be stored for any +* reason. A value of 1 is returned otherwise. + +* Notes: +* - Nothing is stored in the FitsChan and a value of zero is returned +* (but no error is reported) if an AST__FLOAT value is supplied equal +* to AST__BAD. +*/ + +/* Local Variables: */ + const char *cval; + const char *ecval; + double dval; + double ecdval[ 2 ]; + double edval; + int ecival[ 2 ]; + int eival; + int ival; + int ret; + +/* Check the global status, and the supplied pointer. */ + if( !astOK || !value ) return 0; + +/* Initialise the returned value to indicate that the supplied name was + stored. */ + ret = 1; + +/* Check each data type in turn. */ + if( type == AST__FLOAT ){ + dval = *( (double *) value ); + if( dval != AST__BAD ) { + +/* If the data value has not changed, and the card has a coment, + set the comment pointer NULL so that the existing comment will be + retained. */ + if( overwrite && CnvValue( this, type, 0, &edval, "SetFits", + status ) && + CardComm( this, status ) ) { + if( astEQUAL( edval, dval ) ) comment = NULL; + } + astSetFitsF( this, keyname, dval, comment, overwrite ); + } else { + ret = 0; + } + } else if( type == AST__STRING ){ + cval = *( (char **) value); + if( cval ){ + +/* If the data value has not changed, retain the original comment. */ + if( overwrite && CnvValue( this, type, 0, &ecval, "SetFits", + status ) && + CardComm( this, status ) ) { + if( Similar( ecval, cval, status ) ) comment = NULL; + } + +/* Ignore comments if they are identical to the keyword value. */ + if( comment && !strcmp( cval, comment ) ) comment = NULL; + astSetFitsS( this, keyname, cval, comment, overwrite ); + } else { + ret = 0; + } + } else if( type == AST__CONTINUE ){ + cval = *( (char **) value); + if( cval ){ + astSetFitsCN( this, keyname, cval, comment, overwrite ); + } else { + ret = 0; + } + } else if( type == AST__COMMENT ){ + astSetFitsCom( this, keyname, comment, overwrite ); + } else if( type == AST__INT ){ + ival = *( (int *) value ); + +/* If the data value has not changed, retain the original comment. */ + if( overwrite && CnvValue( this, type, 0, &eival, "SetFits", + status ) && + CardComm( this, status ) ) { + if( eival == ival ) comment = NULL; + } + astSetFitsI( this, keyname, ival, comment, overwrite ); + } else if( type == AST__COMPLEXF ){ + if( ( (double *) value )[0] != AST__BAD && + ( (double *) value )[1] != AST__BAD ) { + +/* If the data value has not changed, retain the original comment. */ + if( overwrite && CnvValue( this, type, 0, ecdval, "SetFits", + status ) && + CardComm( this, status ) ) { + if( astEQUAL( ecdval[ 0 ], ( (double *) value )[ 0 ] ) && + astEQUAL( ecdval[ 1 ], ( (double *) value )[ 1 ] ) ) comment = NULL; + } + astSetFitsCF( this, keyname, (double *) value, comment, overwrite ); + } else { + ret = 0; + } + } else if( type == AST__COMPLEXI ){ + +/* If the data value has not changed, retain the original comment. */ + if( overwrite && CnvValue( this, type, 0, ecival, "SetFits", + status ) && + CardComm( this, status ) ) { + if( ecival[ 0 ] == ( (int *) value )[ 0 ] && + ecival[ 1 ] == ( (int *) value )[ 1 ] ) comment = NULL; + } + astSetFitsCI( this, keyname, (int *) value, comment, overwrite ); + } else if( type == AST__LOGICAL ){ + ival = ( *( (int *) value ) != 0 ); + +/* If the data value has not changed, retain the original comment. */ + if( overwrite && CnvValue( this, type, 0, &eival, "SetFits", + status ) && + CardComm( this, status ) ) { + if( eival == ival ) comment = NULL; + } + astSetFitsL( this, keyname, ival, comment, overwrite ); + } else if( type == AST__UNDEF ){ + if( overwrite && CardType( this, status ) == AST__UNDEF && CardComm( this, status ) ) { + comment = NULL; + } + astSetFitsU( this, keyname, comment, overwrite ); + } + return ret; +} + +/* +*++ +* Name: +c astSetFits +f AST_SETFITS + +* Purpose: +* Store a keyword value in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astSetFits( AstFitsChan *this, const char *name, type value, +c const char *comment, int overwrite ) +f CALL AST_SETFITS( THIS, NAME, VALUE, COMMENT, OVERWRITE, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This is a family of functions which store values for named keywords +f This is a family of routines which store values for named keywords +* within a FitsChan at the current card position. The supplied keyword +* value can either over-write an existing keyword value, or can be +* inserted as a new header card into the FitsChan. +* +c The keyword data type is selected by replacing in the function name +f The keyword data type is selected by replacing in the routine name +* by one of the following strings representing the recognised FITS data + +* types: +* +* - CF - Complex floating point values. +* - CI - Complex integer values. +* - F - Floating point values. +* - I - Integer values. +* - L - Logical (i.e. boolean) values. +* - S - String values. +* - CN - A "CONTINUE" value, these are treated like string values, but +* are encoded without an equals sign. +* + +* The data type of the "value" parameter depends on as follows: +* +c - CF - "double *" (a pointer to a 2 element array holding the real and +c imaginary parts of the complex value). +c - CI - "int *" (a pointer to a 2 element array holding the real and +c imaginary parts of the complex value). +c - F - "double". +c - I - "int". +c - L - "int". +c - S - "const char *". +c - CN - "const char *". +* +f - CF - DOUBLE PRECISION(2) (a 2 element array holding the real and +f imaginary parts of the complex value). +f - CI - INTEGER(2) (a 2 element array holding the real and imaginary +f parts of the complex value). +f - F - DOUBLE PRECISION. +f - I - INTEGER +f - L - LOGICAL +f - S - CHARACTER +f - CN - CHARACTER + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c name +f NAME = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string +f A character string +* containing the FITS keyword name. This may be a complete FITS +* header card, in which case the keyword to use is extracted from +* it. No more than 80 characters are read from this string. +c value +f VALUE = type (Given) +* The keyword value to store with the named keyword. The data type +* of this parameter depends on as described above. +c comment +f COMMENT = CHARACTER * ( * ) (Given) +c A pointer to a null terminated string +f A string +* holding a comment to associated with the keyword. +c If a NULL pointer or +f If +* a blank string is supplied, then any comment included in the string +* supplied for the +c "name" parameter is used instead. If "name" +f NAME parameter is used instead. If NAME +* contains no comment, then any existing comment in the card being +* over-written is retained. Otherwise, no comment is stored with +* the card. +c overwrite +f OVERWRITE = LOGICAL (Given) +c If non-zero, +f If .TRUE., +* the new card formed from the supplied keyword name, value and comment +* string over-writes the current card, and the current card is +* incremented to refer to the next card (see the "Card" attribute). If +c zero, +f .FALSE., +* the new card is inserted in front of the current card and the current +* card is left unchanged. In either case, if the current card on entry +* points to the "end-of-file", the new card is appended to the end of +* the list. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The +c function astSetFitsU +f routine AST_SETFITSU +* can be used to indicate that no value is associated with a keyword. +* - The +c function astSetFitsCM +f routine AST_SETFITSCM +* can be used to store a pure comment card (i.e. a card with a blank +* keyword). +* - To assign a new value for an existing keyword within a FitsChan, +c first find the card describing the keyword using astFindFits, and +c then use one of the astSetFits family to over-write the old value. +f first find the card describing the keyword using AST_FINDFITS, and +f then use one of the AST_SETFITS family to over-write the old value. +* - If, on exit, there are no cards following the card written by +c this function, then the current card is left pointing at the +f this routine, then the current card is left pointing at the +* "end-of-file". +* - An error will be reported if the keyword name does not conform +* to FITS requirements. +*-- +*/ + +/* Define a macro which expands to the implementation of the astSetFits + routine for a given data type. */ +#define MAKE_FSET(code,ctype,ftype,valexp) \ +static void SetFits##code( AstFitsChan *this, const char *name, ctype value, const char *comment, int overwrite, int *status ) { \ +\ +/* Local variables: */ \ + const char *class; /* Object class */ \ + const char *method; /* Calling method */ \ + const char *com; /* Comment to use */ \ + char *lcom; /* Supplied keyword comment */ \ + char *lname; /* Supplied keyword name */ \ + char *lvalue; /* Supplied keyword value */ \ + int free_com; /* Should com be freed before returned? */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Ensure the source function has been called */ \ + ReadFromSource( this, status ); \ +\ +/* Store the object clas and calling method. */ \ + class = astGetClass( this ); \ + method = "astSetFits"#code; \ +\ +/* Extract the keyword name from the supplied string. */ \ + (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); \ +\ +/* Initialise a pointer to the comment to be stored. If the supplied \ + comment is blank, use the comment given with "name". */ \ + com = ChrLen( comment, status ) ? comment : lcom; \ +\ +/* If the comment is still blank, use the existing comment if we are \ + over-writing, or a NULL pointer otherwise. */ \ + free_com = 0; \ + if( !ChrLen( com, status ) ) { \ + com = NULL; \ + if( overwrite ) { \ + if( CardComm( this, status ) ){ \ + com = (const char *) astStore( NULL, (void *) CardComm( this, status ), \ + strlen( CardComm( this, status ) ) + 1 ); \ + free_com = 1; \ + } \ + } \ + } \ +\ +/* Insert the new card. */ \ + InsCard( this, overwrite, lname, ftype, valexp, com, method, class, status ); \ +\ +/* Release the memory used to hold keyword name, value and comment strings. */ \ + lname = (char *) astFree( (void *) lname ); \ + lvalue = (char *) astFree( (void *) lvalue ); \ + lcom = (char *) astFree( (void *) lcom ); \ +\ +/* Release the memory holding the stored comment string, so long as it was \ + allocated within this function. */ \ + if( free_com ) com = (const char *) astFree( (void *) com ); \ +\ +} + +/* Use the above macro to give defintions for the astSetFits method + for each FITS data type. */ +MAKE_FSET(I,int,AST__INT,(void *)&value) +MAKE_FSET(F,double,AST__FLOAT,(void *)&value) +MAKE_FSET(S,const char *,AST__STRING,(void *)value) +MAKE_FSET(CN,const char *,AST__CONTINUE,(void *)value) +MAKE_FSET(CF,double *,AST__COMPLEXF,(void *)value) +MAKE_FSET(CI,int *,AST__COMPLEXI,(void *)value) +MAKE_FSET(L,int,AST__LOGICAL,(void *)&value) +#undef MAKE_FSET + +static void SetFitsU( AstFitsChan *this, const char *name, const char *comment, + int overwrite, int *status ) { + +/* +*++ +* Name: +c astSetFitsU +f AST_SETFITSU + +* Purpose: +* Store an undefined keyword value in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astSetFitsU( AstFitsChan *this, const char *name, +c const char *comment, int overwrite ) +f CALL AST_SETFITSU( THIS, NAME, COMMENT, OVERWRITE, STATUS ) + +* Description: +* This +c function +f routine +* stores an undefined value for a named keyword within +* a FitsChan at the current card position. The new undefined value +* can either over-write an existing keyword value, or can be inserted +* as a new header card into the FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c name +f NAME = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string +f A character string +* containing the FITS keyword name. This may be a complete FITS +* header card, in which case the keyword to use is extracted from +* it. No more than 80 characters are read from this string. +c comment +f COMMENT = CHARACTER * ( * ) (Given) +c A pointer to a null terminated string +f A string +* holding a comment to associated with the keyword. +c If a NULL pointer or +f If +* a blank string is supplied, then any comment included in the string +* supplied for the +c "name" parameter is used instead. If "name" +f NAME parameter is used instead. If NAME +* contains no comment, then any existing comment in the card being +* over-written is retained. Otherwise, no comment is stored with +* the card. +c overwrite +f OVERWRITE = LOGICAL (Given) +c If non-zero, +f If .TRUE., +* the new card formed from the supplied keyword name and comment +* string over-writes the current card, and the current card is +* incremented to refer to the next card (see the "Card" attribute). If +c zero, +f .FALSE., +* the new card is inserted in front of the current card and the current +* card is left unchanged. In either case, if the current card on entry +* points to the "end-of-file", the new card is appended to the end of +* the list. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If, on exit, there are no cards following the card written by +* this function, then the current card is left pointing at the +* "end-of-file". +* - An error will be reported if the keyword name does not conform +* to FITS requirements. +*-- +*/ + +/* Local variables: */ + const char *class; /* Object class */ + const char *method; /* Calling method */ + const char *com; /* Comment to use */ + char *lcom; /* Supplied keyword comment */ + char *lname; /* Supplied keyword name */ + char *lvalue; /* Supplied keyword value */ + int free_com; /* Should com be freed before returned? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the object clas and calling method. */ + class = astGetClass( this ); + method = "astSetFitsU"; + +/* Extract the keyword name from the supplied string. */ + (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); + +/* Initialise a pointer to the comment to be stored. If the supplied + comment is blank, use the comment given with "name". */ + com = ChrLen( comment, status ) ? comment : lcom; + +/* If the comment is still blank, use the existing comment if we are + over-writing, or a NULL pointer otherwise. */ + free_com = 0; + if( !ChrLen( com, status ) ) { + com = NULL; + if( overwrite ) { + if( CardComm( this, status ) ){ + com = (const char *) astStore( NULL, (void *) CardComm( this, status ), + strlen( CardComm( this, status ) ) + 1 ); + free_com = 1; + } + } + } + +/* Insert the new card. */ + InsCard( this, overwrite, lname, AST__UNDEF, NULL, com, method, class, + status ); + +/* Release the memory used to hold keyword name, value and comment strings. */ + lname = (char *) astFree( (void *) lname ); + lvalue = (char *) astFree( (void *) lvalue ); + lcom = (char *) astFree( (void *) lcom ); + +/* Release the memory holding the stored comment string, so long as it was + allocated within this function. */ + if( free_com ) com = (const char *) astFree( (void *) com ); +} + +static void SetFitsCM( AstFitsChan *this, const char *comment, + int overwrite, int *status ) { + +/* +*++ +* Name: +c astSetFitsCM +f AST_SETFITSCM + +* Purpose: +* Store a comment card in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astSetFitsCM( AstFitsChan *this, const char *comment, +c int overwrite ) +f CALL AST_SETFITSCM( THIS, COMMENT, OVERWRITE, STATUS ) + +* Description: +* This +c function +f routine +* stores a comment card ( i.e. a card with no keyword name or equals +* sign) within a FitsChan at the current card position. The new card +* can either over-write an existing card, or can be inserted as a new +* card into the FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c comment +f COMMENT = CHARACTER * ( * ) (Given) +c A pointer to a null terminated string +f A string +* holding the text of the comment card. +c If a NULL pointer or +f If +* a blank string is supplied, then a totally blank card is produced. +c overwrite +f OVERWRITE = LOGICAL (Given) +c If non-zero, +f If .TRUE., +* the new card over-writes the current card, and the current card is +* incremented to refer to the next card (see the "Card" attribute). If +c zero, +f .FALSE., +* the new card is inserted in front of the current card and the current +* card is left unchanged. In either case, if the current card on entry +* points to the "end-of-file", the new card is appended to the end of +* the list. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If, on exit, there are no cards following the card written by +* this function, then the current card is left pointing at the +* "end-of-file". +*-- +*/ + +/* Just call astSetFitsCom with a blank keyword name. */ + astSetFitsCom( this, "", comment, overwrite ); +} + +static void SetFitsCom( AstFitsChan *this, const char *name, + const char *comment, int overwrite, int *status ){ + +/* +*+ +* Name: +* astSetFitsCom + +* Purpose: +* Store a comment for a keyword in a FitsChan. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" + +* void astSetFitsCom( AstFitsChan *this, const char *name, +* const char *comment, int overwrite ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function replaces the comment within an existing card, or +* stores a new comment card within a FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* A pointer to a +* string holding the keyword name. This may be a complete FITS +* header card, in which case the keyword to use is extracted from +* it. No more than 80 characters are read from this string. +* comment +* A pointer to a +* string holding a comment to associated with the keyword. +* If a NULL or +* blank string is supplied, any existing comment associated with +* the keyword is removed. +* overwrite +* If non-zero, the new comment replaces the comment in the current +* card, and the current card is then incremented to refer to the next +* card. If zero, a new comment card is inserted in front of the current +* card and the current card is left unchanged. In either case, if the +* current card on entry points to the "end-of-file", the new card is +* appended to the end of the list. + +* Notes: +* - When replacing an existing comment, any existing keyword value is +* retained only if the supplied keyword name is the same as the keyword +* name in the current card. If the keyword names are different, then +* the new name replaces the old name, and any existing keyword data value +* is deleted. The card thus becomes a comment card with the supplied +* keyword name and comment, but no data value. +* - If, on exit, there are no cards following the card written by +* this function, then the current card is left pointing at the +* "end-of-file". +* - The current card can be set explicitly before calling this function +* either by assigning a value to the Card attribute (if the index of the +* required card is already known), or using astFindFits (if only the +* keyword name is known). +* - An error will be reported if the keyword name does not conform +* to FITS requirements. +*- +*/ + +/* Local variables: */ + const char *class; /* Pointer to object class string */ + const char *method; /* Pointer to calling method string */ + const char *cname; /* The existing keyword name */ + const char *com; /* The comment to use */ + char *lcom; /* Supplied keyword comment */ + char *lname; /* Supplied keyword name */ + char *lvalue; /* Supplied keyword value */ + void *old_data; /* Pointer to the old data value */ + void *data; /* Pointer to data value to be stored */ + size_t size; /* The size of the data value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialisation */ + size = 0; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the calling method and object class. */ + method = "astSetFitsCom"; + class = astGetClass( this ); + +/* Extract the keyword name, etc, from the supplied string. */ + (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); + +/* If a blank comment has been supplied, use NULL instead. */ + com = ChrLen( comment, status )? comment : NULL; + +/* If we are inserting a new card, or over-writing an old card with a + different name, create and store a comment card with the given keyword + name and comment, but no data value. */ + cname = CardName( this, status ); + if( !overwrite || !cname || strcmp( lname, cname ) ){ + InsCard( this, overwrite, lname, AST__COMMENT, NULL, com, method, class, status ); + +/* If we are overwriting an existing keyword comment, use the data type + and value from the existing current card. Note, we have to take a copy + of the old data value because InsCard over-writes by deleting the old + card and then inserting a new one. */ + } else { + old_data = CardData( this, &size, status ); + data = astStore( NULL, old_data, size ); + InsCard( this, 1, lname, CardType( this, status ), data, com, method, class, status ); + data = astFree( data ); + } + +/* Release the memory used to hold keyword name, value and comment strings. */ + lname = (char *) astFree( (void *) lname ); + lvalue = (char *) astFree( (void *) lvalue ); + lcom = (char *) astFree( (void *) lcom ); +} + +static void FixNew( AstFitsChan *this, int flag, int remove, + const char *method, const char *class, int *status ){ + +/* +* +* Name: +* FixNew + +* Purpose: +* Remove "new" flags from the whole FitsChan, and optionally remove +* "new" cards. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void FixNew( AstFitsChan *this, int flag, int remove, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function searches the entire FitsChan for cards which are +* marked as new using the supplied flag (NEW1 or NEW2). If "remove" +* is non-zero, these cards are completely removed from the FitsChan +* (not just marked as used). If "remove" is zero, they are retained +* and the specified flag is cleared. + +* Parameters: +* this +* Pointer to the FitsChan. +* flag +* The flag to use; NEW1 or NEW2. +* remove +* Remove flagged cards from the FitsChan? +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if an error has occurred. +* - If any cards are removed, the current Card is left at "end-of-file" +* on exit. If no cards are removed, the original current card is +* retained. +*- +*/ + +/* Local Variables: */ + int *flags; /* Pointer to flags mask for the current card */ + int icard; /* Index of current card on entry */ + int ndeleted; /* Number of cards deleted by this call */ + +/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ + if ( !this || !this->head ) return; + +/* Save the current card index, and rewind the FitsChan. */ + icard = astGetCard( this ); + astClearCard( this ); + +/* Indicate no cards have yet been deleted. */ + ndeleted = 0; + +/* Loop through the list of FitsCards in the FitsChan until the final + card is reached. */ + while( astOK && this->card ){ + +/* Get a pointer to the flags mask for this card. */ + flags = CardFlags( this, status ); + +/* See if the Card has been marked with the requeste new flag. */ + if( flags && ( (*flags) & flag ) ) { + +/* If requested, remove the card. This will automatically move the + current card on to the next card. */ + if( remove ){ + DeleteCard( this, method, class, status ); + ndeleted++; + +/* Otherwise, clear the flag. */ + } else { + *flags = (*flags) & ~flag; + +/* Increment the card count and move on to the next card. */ + MoveCard( this, 1, method, class, status ); + } + +/* Move on to the next card if this card is not marked with the requested + new flag. */ + } else { + MoveCard( this, 1, method, class, status ); + } + } + +/* If no cards were removed, we can safely re-instate the original + current card. Otherwise, the current card is left at "end-of-file". */ + if( ndeleted == 0 ) astSetCard( this, icard ); + +/* Return */ + return; +} + +static void FixUsed( AstFitsChan *this, int reset, int used, int remove, + const char *method, const char *class, int *status ){ + +/* +* +* Name: +* FixUsed + +* Purpose: +* Remove "provisionally used" flags from the whole FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void FixUsed( AstFitsChan *this, int reset, int used, int remove, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function searches the entire FitsChan for cards which are +* marked as "provisionally used". The "provisionally used" flag is +* cleared for each such card. In addition, if "used" is non-zero then +* each such card is flagged as having been "definitely used". If +* "remove" is non-zero, then all "provisionally used" cards are deleted +* from the FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* reset +* Set all cards so that they are neither provisionally used or +* definitely used. In this case neither the "used" nor the +* "remove" parameter are accssed. +* used +* Have the provisionally used cards definitely been used? +* remove +* Should provisionally used cards be deleted? +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + FitsCard *card0; /* Pointer to current FitsCard */ + int *flags; /* Pointer to flags mask for the current card */ + int old_ignore_used; /* Original value of variable ignore_used */ + int old_status; /* Original inherited status value */ + int rep; /* Original error reporting flag */ + +/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ + if ( !this || !this->head ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Temporarily clear any bad status value and supress error reporting in + this function. */ + old_status = astStatus; + astClearStatus; + rep = astReporting( 0 ); + +/* Indicate that we should not skip over cards marked as having been + read. */ + old_ignore_used = ignore_used; + ignore_used = 0; + +/* Save a pointer to the current card, and the reset the current card to + be the first card. */ + card0 = this->card; + astClearCard( this ); + +/* Loop through the list of FitsCards in the FitsChan until the final + card is reached. */ + while( this->card ){ + +/* Get a pointer to the flags mask for this card. */ + flags = CardFlags( this, status ); + +/* Reset both used flags if required. */ + if( reset ) { + *flags = (*flags) & ~PROVISIONALLY_USED; + *flags = (*flags) & ~USED; + MoveCard( this, 1, method, class, status ); + +/* Otherwise perform the actions indicated by parameters "used" and + "remove". */ + } else { + +/* See if the Card has been provisionally used. */ + if( flags && ( (*flags) & PROVISIONALLY_USED ) ) { + +/* Clear the provisionally used flag. */ + *flags = (*flags) & ~PROVISIONALLY_USED; + +/* If required, set the definitely used flag. */ + if( used ) *flags = (*flags) | USED; + +/* If required, delete the card. The next card is made current. If we are + about to delete the original current card, we need to update the + pointer to the card to be made current at the end of this function. + If we end up back at the head of the chain, indicate that we have + reached the end of file by setting card0 NULL. */ + if( remove ) { + if( card0 == this->card && card0 ) { + card0 = ( (FitsCard *) this->card )->next; + if( (void *) card0 == this->head ) card0 = NULL; + } + DeleteCard( this, method, class, status ); + +/* Otherwise, just move on to the next card. */ + } else { + MoveCard( this, 1, method, class, status ); + } + +/* If this card has not bee provisionally used, move on to the next card. */ + } else { + MoveCard( this, 1, method, class, status ); + } + } + } + +/* Re-instate the original current card. */ + this->card = card0; + +/* If this card is now flagged as definitely used, move forward to the + next un-used card. */ + flags = CardFlags( this, status ); + if( flags && (*flags & USED ) ) { + ignore_used = 1; + MoveCard( this, 1, method, class, status ); + } + +/* Re-instate the original flag indicating if cards marked as having been + read should be skipped over. */ + ignore_used = old_ignore_used; + +/* Re-instate the original status value and error reporting condition. */ + astReporting( rep ); + astSetStatus( old_status ); +} + +static void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status ){ + +/* +* +* Name: +* FormatCard + +* Purpose: +* Formats the current card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function write the current card into the supplied character +* buffer as a complete FITS header card. + +* Parameters: +* this +* Pointer to the FitsChan. +* buf +* A character string into which the header card is written. This +* should be at least 81 characters long. The returned string is +* padded with spaces upto column 80. A terminating null character +* is added. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - An error is reported if the requested header card does not conform to +* FITS standards. +* +*/ + +/* Local Variables: */ + const char *com; /* Pointer to comment string to use */ + int comlen; /* Length of comment string */ + int comstart; /* Column in which to start comment */ + int i; /* Loop counter for characters */ + int len; /* Output string length */ + int digits; /* No. of digits to use when formatting floating point values */ + int type; /* Card data type */ + +/* Check the global error status, and check the current card is defined. */ + if ( !astOK || astFitsEof( this ) ) return; + +/* Get a pointer to the comment to use and determine its length. */ + com = CardComm( this, status ); + comlen = ChrLen( com, status ); + +/* Copy the keyword name to the start of the output buffer, and store + its length. */ + len = (int) strlen( strcpy( buf, CardName( this, status ) ) ); + +/* Pad the name with spaces up to column 8. */ + while ( len < FITSNAMLEN ) buf[ len++ ] = ' '; + +/* If the card contains a keyword value... */ + type = CardType( this, status ); + if( type != AST__COMMENT ){ + +/* Get the number of digits to use when formatting floating point values. */ + digits = astGetFitsDigits( this ); + +/* Put an equals sign in column 9 (or a space if the keyword is a CONTINUE + card), followed by a space in column 10. */ + buf[ len++ ] = ( type == AST__CONTINUE ) ? ' ' : '='; + buf[ len++ ] = ' '; + +/* Format and store the keyword value, starting at column 11 and update the + output string length. */ + len += EncodeValue( this, buf + len, FITSNAMLEN + 3, digits, + method, status ); + +/* If there is a comment, determine which column it should start in so that + it ends in column 80. */ + if( com ){ + comstart = AST__FITSCHAN_FITSCARDLEN - ( comlen - 2 ) + 1; + +/* Adjust the starting column to 32 if possible, avoiding over-writing + the value, or running off the end of the card unless this is + unavoidable. */ + if ( comstart > FITSCOMCOL ) comstart = FITSCOMCOL; + if ( comstart < len + 2 ) comstart = len + 2; + +/* Pad the output buffer with spaces up to the start of the comment. */ + while ( len < comstart - 1 ) buf[ len++ ] = ' '; + +/* Then append "/ " to introduce the comment, truncating if the card + length forces this. */ + for ( i = 0; ( i < 2 ) && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) { + buf[ len++ ] = "/ "[ i ]; + } + } + } + +/* Append any comment, truncating it if the card length forces + this. */ + if ( com ) { + for ( i = 0; com[ i ] && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) { + buf[ len++ ] = com[ i ]; + } + } + +/* Pad with spaces up to the end of the card. */ + while ( len < AST__FITSCHAN_FITSCARDLEN ) buf[ len++ ] = ' '; + +/* Terminate it. */ + buf[ AST__FITSCHAN_FITSCARDLEN ] = 0; +} + +static int FullForm( const char *list, const char *test, int abbrev, int *status ){ +/* +* Name: +* FullForm + +* Purpose: +* Identify the full form of an option string. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int FullForm( const char *list, const char *test, int abbrev, int *status ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function identifies a supplied test option within a supplied +* list of valid options, and returns the index of the option within +* the list. The test option may be abbreviated, and case is +* insignificant. + +* Parameters: +* list +* A list of space separated option strings. +* test +* A candidate option string. +* abbrev +* 1 if abbreviations are to be accepted. Zero otherwise. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The index of the identified option within the supplied list, starting +* at zero. -1 is returned if the option is not recognised, and (if +* abbrev is 1 ) -2 if the option is ambiguous (no errors are reported +* in these cases). If abbrev is zero, the returned index will be the +* index of the first matching string. +* + +* Notes: +* - A value of -1 is returned if an error has already occurred, or +* if this function should fail for any reason. +*/ + +/* Local Variables: */ + char *context; /* Context used by strtok_r */ + char *llist; /* Pointer to a local copy of the options list */ + char *option; /* Pointer to the start of the next option */ + int i; /* Current option index */ + int len; /* Length of supplied option */ + int nmatch; /* Number of matching options */ + int ret; /* The returned index */ + +/* Initialise the answer to indicate that the option has not been + identified. */ + ret = -1; + +/* Avoid compiler warnings. */ + context = NULL; + +/* Check global status. */ + if( !astOK ) return ret; + +/* Take a local copy of the supplied options list. This is necessary since + "strtok" modified the string by inserting null characters. */ + llist = (char *) astStore( NULL, (void *) list, strlen(list) + 1 ); + if( astOK ){ + +/* Save the number of characters in the supplied test option (excluding + trailing spaces). */ + len = ChrLen( test, status ); + +/* Compare the supplied test option against each of the known options in + turn. Count the number of matches. */ + nmatch = 0; +#if HAVE_STRTOK_R + option = strtok_r( llist, " ", &context ); +#else + option = strtok( llist, " " ); +#endif + i = 0; + while( option ){ + +/* If every character in the supplied label matches the corresponding + character in the current test label we have a match. Increment the + number of matches and save the current item index. If abbreviation is + not allowed ensure that the lengths of the strings are equal. */ + if( !Ustrncmp( test, option, len, status ) && ( abbrev || + len == ChrLen( option, status ) ) ) { + nmatch++; + ret = i; + if( !abbrev ) break; + } + +/* Get a pointer to the next option. */ +#if HAVE_STRTOK_R + option = strtok_r( NULL, " ", &context ); +#else + option = strtok( NULL, " " ); +#endif + i++; + } + +/* Return -1 if no match was found. */ + if( !nmatch ){ + ret = -1; + +/* Return -2 if the option was ambiguous. */ + } else if( abbrev && nmatch > 1 ){ + ret = -2; + } + +/* Free the local copy of the options list. */ + llist = (char *) astFree( (void *) llist ); + } + +/* Return the answer. */ + return ret; +} + +static const char *GetAllWarnings( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* astGetAllWarnings + +* Purpose: +* Return a list of all condition names. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* const char *GetAllWarnings( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns a space separated lits of the condition names +* currently recognized by the Warnings attribute. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* A pointer to a static string holding the condition names. + +* Notes: +* - This routine does not check the inherited status. +*- +*/ + +/* Return the result. */ + return ALLWARNINGS; +} +const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { + +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the protected astGetAttrib +* method inherited from the Channel class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a FitsChan, formatted as a character string. + +* Parameters: +* this +* Pointer to the FitsChan. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the FitsChan, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the FitsChan. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + const char *result; /* Pointer value to return */ + int ival; /* Integer attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_object; + +/* Card. */ +/* ----- */ + if ( !strcmp( attrib, "card" ) ) { + ival = astGetCard( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* CardComm. */ +/* --------- */ + } else if ( !strcmp( attrib, "cardcomm" ) ) { + result = astGetCardComm( this ); + +/* CardName. */ +/* --------- */ + } else if ( !strcmp( attrib, "cardname" ) ) { + result = astGetCardName( this ); + +/* CardType. */ +/* --------- */ + } else if ( !strcmp( attrib, "cardtype" ) ) { + ival = astGetCardType( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Encoding. */ +/* --------- */ + } else if ( !strcmp( attrib, "encoding" ) ) { + ival = astGetEncoding( this ); + if ( astOK ) { + if( ival == NATIVE_ENCODING ){ + result = NATIVE_STRING; + } else if( ival == FITSPC_ENCODING ){ + result = FITSPC_STRING; + } else if( ival == FITSIRAF_ENCODING ){ + result = FITSIRAF_STRING; + } else if( ival == FITSAIPS_ENCODING ){ + result = FITSAIPS_STRING; + } else if( ival == FITSAIPSPP_ENCODING ){ + result = FITSAIPSPP_STRING; + } else if( ival == FITSCLASS_ENCODING ){ + result = FITSCLASS_STRING; + } else if( ival == FITSWCS_ENCODING ){ + result = FITSWCS_STRING; + } else if( ival == DSS_ENCODING ){ + result = DSS_STRING; + } else { + result = UNKNOWN_STRING; + } + } + +/* CDMatrix */ +/* -------- */ + } else if ( !strcmp( attrib, "cdmatrix" ) ) { + ival = astGetCDMatrix( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* DefB1950 */ +/* -------- */ + } else if ( !strcmp( attrib, "defb1950" ) ) { + ival = astGetDefB1950( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* TabOK */ +/* ----- */ + } else if ( !strcmp( attrib, "tabok" ) ) { + ival = astGetTabOK( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* CarLin */ +/* ------ */ + } else if ( !strcmp( attrib, "carlin" ) ) { + ival = astGetCarLin( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* PolyTan */ +/* ------- */ + } else if ( !strcmp( attrib, "polytan" ) ) { + ival = astGetPolyTan( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Iwc */ +/* --- */ + } else if ( !strcmp( attrib, "iwc" ) ) { + ival = astGetIwc( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Clean */ +/* ----- */ + } else if ( !strcmp( attrib, "clean" ) ) { + ival = astGetClean( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* FitsAxisOrder. */ +/* -------------- */ + } else if ( !strcmp( attrib, "fitsaxisorder" ) ) { + result = astGetFitsAxisOrder( this ); + +/* FitsDigits. */ +/* ----------- */ + } else if ( !strcmp( attrib, "fitsdigits" ) ) { + ival = astGetFitsDigits( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Ncard. */ +/* ------ */ + } else if ( !strcmp( attrib, "ncard" ) ) { + ival = astGetNcard( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Nkey. */ +/* ----- */ + } else if ( !strcmp( attrib, "nkey" ) ) { + ival = astGetNkey( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* AllWarnings */ +/* ----------- */ + } else if ( !strcmp( attrib, "allwarnings" ) ) { + result = astGetAllWarnings( this ); + +/* Warnings. */ +/* -------- */ + } else if ( !strcmp( attrib, "warnings" ) ) { + result = astGetWarnings( this ); + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetCard( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* astGetCard + +* Purpose: +* Get the value of the Card attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* int astGetCard( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns the value of the Card attribute for the supplied +* FitsChan. This is the index of the next card to be read from the +* FitsChan. The index of the first card is 1. If there are no more +* cards to be read, a value one greater than the number of cards in the +* FitsChan is returned. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* The index of the next card to be read. + +* Notes: +* - A value of zero will be returned if the current card is not defined. +* - This function attempts to execute even if an error has occurred. +*- +*/ + +/* Local Variables: */ + const char *class; /* Pointer to class string */ + const char *method; /* Pointer to method string */ + FitsCard *card0; /* Pointer to current FitsCard */ + int index; /* Index of next FitsCard */ + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Return if no FitsChan was supplied, or if the FitsChan is empty. */ + if ( !this || !this->head ) return 0; + +/* Store the method and object class. */ + method = "astGetCard"; + class = astGetClass( this ); + +/* Save a pointer to the current card, and the reset the current card to + be the first card. */ + card0 = this->card; + astClearCard( this ); + +/* Count through the list of FitsCards in the FitsChan until the original + current card is reached. If the current card is not found (for instance + if it has been marked as deleted and we are currently skipping such cards), + this->card will be left null (end-of-file). */ + index = 1; + while( this->card != card0 && astOK && this->card ){ + +/* Increment the card count and move on to the next card. */ + index++; + MoveCard( this, 1, method, class, status ); + } + +/* Return the card index. */ + return index; +} + +static const char *GetCardComm( AstFitsChan *this, int *status ){ +/* +*+ +* Name: +* GetCardComm + +* Purpose: +* Get the value of the CardComm attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* const char *astGetCardComm( AstFitsChan *this) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns the value of the CardComm attribute for the +* supplied FitsChan. This is the comment for the current card. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* A pointer to a static string holding the comment. A zero-length +* string is returned if the card has no comment. + +* Notes: +* - A value of NULL will be returned if an error has already +* occurred, or if this function should fail for any reason. +*- +*/ + +/* Local Variables */ + const char *result = NULL; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Get the comment for the current card. */ + result = CardComm( this, status ); + +/* Return a zero-length string if the card has no comment. */ + if( astOK && !result ) result = ""; + +/* Return the comment. */ + return result; +} + +static const char *GetCardName( AstFitsChan *this, int *status ){ +/* +*+ +* Name: +* GetCardName + +* Purpose: +* Get the value of the CardName attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* const char *astGetCardName( AstFitsChan *this) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns the value of the CardName attribute for the +* supplied FitsChan. This is the keyword name for the current card. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* A pointer to a static string holding the keyword name. + +* Notes: +* - A value of NULL will be returned if an error has already +* occurred, or if this function should fail for any reason. +*- +*/ + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Return the keyword name of the current card. */ + return CardName( this, status ); +} + +static int GetCardType( AstFitsChan *this, int *status ){ +/* +*+ +* Name: +* GetCardType + +* Purpose: +* Get the value of the CardType attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* int astGetCardType( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns the value of teh CardType attribute for the supplied +* FitsChan. This is the data type of the keyword value for the current card. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* An integer representing the data type of the current card. + +* Notes: +* - A value of AST__NOTYPE will be returned if an error has already +* occurred, or if this function should fail for any reason. +*- +*/ + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Return the data type of the current card. */ + return CardType( this, status ); +} + +static int GetFull( AstChannel *this_channel, int *status ) { +/* +* Name: +* GetFull + +* Purpose: +* Obtain the value of the Full attribute for a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetFull( AstChannel *this, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the protected astGetFull +* method inherited from the Channel class). + +* Description: +* This function return the integer value of the Full attribute for +* a FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Full attribute value. + +* Notes: +* - This function modifies the default Full value from 0 to -1 for +* the benefit of the FitsChan class. This prevents non-essential +* information being written by the astWrite method unless it is +* requested by explicitlt setting a Full value. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + int result; /* Result value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* If the Full attribute us set, obtain its value using the parent class + method. */ + if ( astTestFull( this ) ) { + result = (* parent_getfull)( this_channel, status ); + +/* Otherwise, supply a default value of -1. */ + } else { + result = -1; + } + +/* Return the result. */ + return result; +} + +static FitsCard *GetLink( FitsCard *card, int next, const char *method, + const char *class, int *status ){ +/* +* Name: +* GetLink + +* Purpose: +* Get a pointer to the next or previous card in the list. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* FitsCard *GetLink( FitsCard *card, int next, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns the a pointer to either the next or previous FitsCard +* structure in the circular linked list of such structures stored in a +* FitsChan. A check is performed to ensure that the forward and +* backward links from the supplied card are consistent and an error +* is reported if they are not (so long as no previous error has been +* reported). Memory corruption can result in inconsistent links +* which can result in infinite loops if an attempt is made to scan the +* list. + +* Parameters: +* card +* The current card. +* next +* If non-zero, a pointer to the "next" card is returned. Otherwise +* a pointer to the "previous" card is returned. +* method +* Pointer to string holding the name of the calling method. +* class +* Pointer to string holding the object class. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the required card, or NULL if an error occurs. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + FitsCard *ret; /* Pointer to the returned card */ + +/* Check that the "next" link from the previous card points back to + the current card, and that the "prev" link from the next card points + back to the current card. */ + if( card && ( card->prev->next != card || + card->next->prev != card ) ){ + +/* Report an error so long as no previous error has been reported, and + return a NULL pointer. */ + if( astOK ){ + astError( AST__FCRPT, "%s(%s): A corrupted %s object has been " + "supplied.", status, method, class, class ); + } + ret = NULL; + +/* If the links are good, return a pointer to the required card. */ + } else { + ret = next ? card->next : card->prev; + } + +/* Return the result. */ + return ret; +} + +static int GetNcard( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* astGetNcard + +* Purpose: +* Get the value of the Ncard attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* int astGetNcard( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns the value of the Ncard attribute for the supplied +* FitsChan. This is the number of cards currently in the FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* The number of cards currently in the FitsChan. + +* Notes: +* - A value of zero will be returned if an error has already +* occurred, or if this function should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *class; /* Pointer to class string */ + const char *method; /* Pointer to method string */ + FitsCard *card0; /* Pointer to current card on entry */ + int ncard; /* Number of cards so far */ + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Return zero if an error has already occurred, or no FitsChan was supplied, + or the FitsChan is empty. */ + if ( !astOK || !this || !this->head ) return 0; + +/* Store the method and object class. */ + method = "astGetNcard"; + class = astGetClass( this ); + +/* Save a pointer to the current card, and then reset the current card to + be the first card. */ + card0 = this->card; + astClearCard( this ); + +/* Count through the cards in the FitsChan until the end of file is reached. */ + ncard = 0; + while( astOK && this->card ){ + +/* Increment the card count and move on to the next card. */ + ncard++; + MoveCard( this, 1, method, class, status ); + } + +/* Reset the current card to be the original current card. */ + this->card = card0; + +/* Return the result. */ + return astOK ? ncard : 0; +} + +static int GetNkey( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* astGetNkey + +* Purpose: +* Get the value of the Nkey attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* int astGetNkey( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function returns the value of the Nkey attribute for the supplied +* FitsChan. This is the number of unique keywords currently in the +* FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* The number of unique keywords currently in the FitsChan. + +* Notes: +* - A value of zero will be returned if an error has already +* occurred, or if this function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstKeyMap *km; /* KeyMap holding unique keyword names */ + FitsCard *card0; /* Pointer to current card on entry */ + const char *class; /* Pointer to class string */ + const char *method; /* Pointer to method string */ + int nkey; /* Returned Nkey value */ + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Return zero if an error has already occurred, or no FitsChan was supplied, + or the FitsChan is empty. */ + if ( !astOK || !this || !this->head ) return 0; + +/* Store the method and object class. */ + method = "astGetNkey"; + class = astGetClass( this ); + +/* Create an empty KeyMap to hold the unused keyword names */ + km = astKeyMap( " ", status ); + +/* Save a pointer to the current card, and then reset the current card to + be the first card. */ + card0 = this->card; + astClearCard( this ); + +/* Loop through the cards in the FitsChan until the end of file is reached. */ + while( astOK && this->card ){ + +/* Get the keyword name for the current card and add it to the keymap. */ + astMapPut0I( km, CardName( this, status ), 0, NULL ); + +/* Move on to the next unused card. */ + MoveCard( this, 1, method, class, status ); + } + +/* Reset the current card to be the original current card. */ + this->card = card0; + +/* Get the number of keywords. */ + nkey = astMapSize( km ); + +/* Annull the KeyMap . */ + km = astAnnul( km ); + +/* Return the result. */ + return astOK ? nkey : 0; +} + +static void GetNextData( AstChannel *this_channel, int skip, char **name, + char **val, int *status ) { +/* +* Name: +* GetNextData + +* Purpose: +* Read the next item of data from a data source. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void GetNextData( AstChannel *this, int skip, char **name, char **val ) + +* Class Membership: +* FitsChan member function (over-rides the protected +* astGetNextData method inherited from the Channel class). + +* Description: +* This function reads the next item of input data from a data +* source associated with a FitsChan and returns the result. It +* decodes the data item and returns name/value pairs ready for +* use. + +* Parameters: +* this +* Pointer to the FitsChan. +* skip +* A non-zero value indicates that a new Object is to be read, +* and that all input data up to the next "Begin" item are to be +* skipped in order to locate it. This is useful if the data +* source contains AST objects interspersed with other data (but +* note that these other data cannot appear inside AST Objects, +* only between them). +* +* A zero value indicates that all input data are significant +* and the next item will therefore be read and an attempt made +* to interpret it whatever it contains. Any other data +* inter-mixed with AST Objects will then result in an error. +* name +* An address at which to store a pointer to a null-terminated +* dynamically allocated string containing the name of the next +* item in the input data stream. This name will be in lower +* case with no surrounding white space. It is the callers +* responsibilty to free the memory holding this string (using +* astFree) when it is no longer required. +* +* A NULL pointer value will be returned (without error) to +* indicate when there are no further input data items to be +* read. +* val +* An address at which to store a pointer to a null-terminated +* dynamically allocated string containing the value associated +* with the next item in the input data stream. No case +* conversion is performed on this string and all white space is +* potentially significant. It is the callers responsibilty to +* free the memory holding this string (using astFree) when it +* is no longer required. +* +* The returned pointer will be NULL if an Object data item is +* read (see the "Data Representation" section). + +* Data Representation: + +* The returned data items fall into the following categories: +* +* - Begin: Identified by the name string "begin", this indicates +* the start of an Object definition. The associated value string +* gives the class name of the Object being defined. +* +* - IsA: Identified by the name string "isa", this indicates the +* end of the data associated with a particular class structure +* within the definiton of a larger Object. The associated value +* string gives the name of the class whose data have just been +* read. +* +* - End: Identified by the name string "end", this indicates the +* end of the data associated with a complete Object +* definition. The associated value string gives the class name of +* the Object whose definition is being ended. +* +* - Non-Object: Identified by any other name string plus a +* non-NULL "val" pointer, this gives the value of a non-Object +* structure component (instance variable). The name identifies +* which instance variable it is (within the context of the class +* whose data are being read) and the value is encoded as a string. +* +* - Object: Identified by any other name string plus a NULL "val" +* pointer, this identifies the value of an Object structure +* component (instance variable). The name identifies which +* instance variable it is (within the context of the class whose +* data are being read) and the value is given by subsequent data +* items (so the next item should be a "Begin" item). + +* Notes: +* - NULL pointer values will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Constants: */ +#define BUFF_LEN 100 /* Length of formatting buffer */ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + char *keyword; /* Pointer to current keyword string */ + char *newdata; /* Pointer to stripped string value */ + char *upq; /* Pointer to unprequoted string */ + char buff[ BUFF_LEN + 1 ]; /* Buffer for formatting values */ + const char *class; /* Pointer to object class */ + const char *method; /* Pointer to method name */ + int cont; /* String ends with an ampersand? */ + int done; /* Data item found? */ + int freedata; /* Should the data pointer be freed? */ + int i; /* Loop counter for keyword characters */ + int len; /* Length of current keyword */ + int nc; /* Number of characters read by "astSscanf" */ + int nn; /* No. of characters after UnPreQuoting */ + int type; /* Data type code */ + void *data; /* Pointer to current data value */ + +/* Initialise the returned pointer values. */ + *name = NULL; + *val = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Store the method name and object class. */ + method = "astRead"; + class = astGetClass( this ); + +/* Loop to consider successive cards stored in the FitsChan (starting + at the "current" card) until a valid data item is read or "end of + file" is reached. Also quit the loop if an error occurs. */ + done = 0; + newdata = NULL; + while ( !done && !astFitsEof( this ) && astOK ){ + +/* Obtain the keyword string, data type code and data value pointer + from the current card. */ + keyword = CardName( this, status ); + type = CardType( this, status ); + data = CardData( this, NULL, status ); + +/* Mark all cards as having been used unless we are skipping over cards which + may not be related to AST. */ + if( !skip ) MarkCard( this, status ); + +/* Ignore comment cards. */ + if ( type != AST__COMMENT ) { + +/* Native encoding requires trailing white space to be removed from + string values (so that null strings can be distinguished from blank + strings). Do this now. */ + freedata = 0; + if ( ( type == AST__STRING || type == AST__CONTINUE ) && data ){ + newdata = (char *) astStore( NULL, data, strlen( (char *) data ) + 1 ); + if( newdata ){ + newdata[ ChrLen( data, status ) ] = 0; + data = (void *) newdata; + freedata = 1; + } + } + +/* Obtain the keyword length and test the card to identify the type of + AST data item (if any) that it represents. */ + len = (int) strlen( keyword ); + +/* "Begin" item. */ +/* ------------- */ + +/* This is identified by a string value and a keyword of the form + "BEGASTxx", where "xx" are characters encoding a sequence + number. */ + if ( ( type == AST__STRING ) && + ( nc = 0, + ( 0 == astSscanf( keyword, "BEGAST" + "%*1[" SEQ_CHARS "]" + "%*1[" SEQ_CHARS "]%n", &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Set the returned name to "begin" and extract the associated class + name from the string value. Store both of these in dynamically + allocated strings. */ + *name = astString( "begin", 5 ); + *val = UnPreQuote( (const char *) data, status ); + +/* Indicate that the current card has been used. */ + MarkCard( this, status ); + +/* The "begin" item will be preceded by a header of COMMENT cards. Mark + them as having been used. */ + ComBlock( this, -1, method, class, status ); + +/* "IsA" item. */ +/* ----------- */ + +/* This is identified by a string value and a keyword of the form + "ISAxx", where "xx" are characters encoding a sequence + number. Don't accept the item if we are skipping over cards looking + for a "Begin" item. */ + } else if ( !skip && + ( type == AST__STRING ) && + ( nc = 0, + ( 0 == astSscanf( keyword, + "ISA" + "%*1[" SEQ_CHARS "]" + "%*1[" SEQ_CHARS "]%n", &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Set the returned name to "isa" and extract the associated class + name from the string value. Store both of these in dynamically + allocated strings. */ + *name = astString( "isa", 3 ); + *val = UnPreQuote( (const char *) data, status ); + +/* "End" item. */ +/* ----------- */ + +/* This is identified by a string value and a keyword of the form + "ENDASTxx", where "xx" are characters encoding a sequence + number. Don't accept the item if we are skipping over cards looking + for a "Begin" item. */ + } else if ( !skip && + ( type == AST__STRING ) && + ( nc = 0, + ( 0 == astSscanf( keyword, + "ENDAST" + "%*1[" SEQ_CHARS "]" + "%*1[" SEQ_CHARS "]%n", &nc ) ) + && ( nc >= len ) ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Set the returned name to "end" and extract the associated class + name from the string value. Store both of these in dynamically + allocated strings. */ + *name = astString( "end", 3 ); + *val = UnPreQuote( (const char *) data, status ); + +/* The "end" item eill be followed by a footer of COMMENT cards. Mark + these cards as having been used. */ + ComBlock( this, 1, method, class, status ); + +/* Object or data item. */ +/* -------------------- */ + +/* These are identified by a string, int, or double value, and a + keyword ending in two characters encoding a sequence number. Don't + accept the item if we are skipping over cards looking for a "Begin" + item. */ + } else if ( !skip && + ( ( type == AST__STRING ) || + ( type == AST__INT ) || + ( type == AST__FLOAT ) ) && + ( len > 2 ) && + strchr( SEQ_CHARS, keyword[ len - 1 ] ) && + strchr( SEQ_CHARS, keyword[ len - 2 ] ) ) { + +/* Note we have found a data item. */ + done = 1; + +/* Set the returned name by removing the last two characters from the + keyword and converting to lower case. Store this in a dynamically + allocated string. */ + *name = astString( keyword, len - 2 ); + for ( i = 0; ( *name )[ i ]; i++ ) { + ( *name )[ i ] = tolower( ( *name )[ i ] ); + } + +/* Classify the data type. */ + switch ( type ) { + +/* If the value is a string, test if it is zero-length. If so, this + "null" value indicates an Object data item (whose definition + follows), so leave the returned value pointer as NULL. Otherwise, + we have a string data item, so extract its value and store it in a + dynamically allocated string. */ + case AST__STRING: + if ( *( (char *) data ) ) { + +/* A long string value may be continued on subsequent CONTINUE cards. See + if the current string may be continued. This is the case if the final + non-blank character (before UnPreQuoting) is an ampersand. */ + cont = ( ((char *) data)[ ChrLen( data, status ) - 1 ] == '&' ); + +/* If the string does not end with an ampersand, just UnPreQUote it and + return a copy. */ + if( !cont ) { + *val = UnPreQuote( (const char *) data, status ); + +/* Otherwise, initialise the returned string to hold a copy of the keyword + value. */ + } else { + nc = strlen( (const char *) data ); + *val = astStore( NULL, (const char *) data, nc + 1 ); + +/* Loop round reading any subsequent CONTINUE cards. Leave the loop when + the end-of-file is hit, or an error occurs. */ + while( cont && MoveCard( this, 1, method, class, status ) && + astOK ){ + +/* See if this is a CONTINUE card. If so, get its data pointer. */ + if( CardType( this, status ) == AST__CONTINUE ){ + data = CardData( this, NULL, status ); + +/* See if the CONTINUE card ends with an ampersand (i.e. if there is + a possibility of there being any remaining CONTINUE cards). */ + cont = ( ( (char *) data)[ ChrLen( data, status ) - 1 ] == '&' ); + +/* UnPreQUote it. */ + upq = UnPreQuote( (const char *) data, status ); + if( !astOK ) break; + +/* Expand the memory for the returned string to hold the new string. */ + nn = strlen( upq ); + *val = astRealloc( *val, nc + nn ); + if( !astOK ) break; + +/* Copy the new string into the expanded memory, so that the first + character of the new string over-writes the trailing ampersand + currently in the buffer. */ + strcpy( *val + nc - 1, upq ); + +/* Release the memory holding the UnPreQUoted string . */ + upq = astFree( upq ); + +/* Update the current length of the returned string. */ + nc += nn - 1; + +/* Mark the current card as having been read. */ + MarkCard( this, status ); + +/* Report an error if this is not a CONTINUE card. */ + } else { + astError( AST__BADIN, "%s(%s): One or more " + "FITS \"CONTINUE\" cards are missing " + "after the card for keyword \"%s\".", status, + method, class, keyword ); + } + } + } + } + break; + +/* If the value is an int, format it and store the result in a + dynamically allocated string. */ + case AST__INT: + (void) sprintf( buff, "%d", *( (int *) data ) ); + *val = astString( buff, (int) strlen( buff ) ); + break; + +/* If the value is a double, format it and store the result in a + dynamically allocated string. */ + case AST__FLOAT: + (void) sprintf( buff, "%.*g", DBL_DIG, *( (double *) data ) ); + CheckZero( buff, *( (double *) data ), 0, status ); + *val = astString( buff, (int) strlen( buff ) ); + break; + } + +/* Anything else. */ +/* -------------- */ + +/* If the input line didn't match any of the above and the "skip" flag + is not set, then report an error.. */ + } else if ( !skip ) { + astError( AST__BADIN, + "%s(%s): Cannot interpret the input data given by " + "FITS keyword \"%s\".", status, method, class, keyword ); + } + +/* Free any memory used to hold stripped string data. */ + if( freedata ) newdata = (char *) astFree( (void *) newdata ); + } + +/* Increment the current card. */ + MoveCard( this, 1, method, class, status ); + } + +/* If an error occurred, ensure that any allocated memory is freed and + that NULL pointer values are returned. */ + if ( !astOK ) { + *name = astFree( *name ); + *val = astFree( *val ); + } + +/* Undefine macros local to this function. */ +#undef BUFF_LEN +} + +static int GetSkip( AstChannel *this_channel, int *status ) { +/* +* Name: +* GetSkip + +* Purpose: +* Obtain the value of the Skip attribute for a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int GetSkip( AstChannel *this, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the protected astGetSkip +* method inherited from the Channel class). + +* Description: +* This function return the (boolean) integer value of the Skip +* attribute for a FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Skip attribute value. + +* Notes: +* - This function modifies the default Skip value from 0 to 1 for +* the benefit of the FitsChan class. This default value allows the +* astRead method to skip over unrelated FITS keywords when +* searching for the next Object to read. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + int result; /* Result value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* If the Skip attribute us set, obtain its value using the parent class + method. */ + if ( astTestSkip( this ) ) { + result = (* parent_getskip)( this_channel, status ); + +/* Otherwise, supply a default value of 1. */ + } else { + result = 1; + } + +/* Return the result. */ + return result; +} + +static int GetValue( AstFitsChan *this, const char *keyname, int type, + void *value, int report, int mark, const char *method, + const char *class, int *status ){ +/* +* Name: +* GetValue + +* Purpose: +* Obtain a FITS keyword value. + +* Type: +* Private function. + +* Synopsis: +* int GetValue( AstFitsChan *this, const char *keyname, int type, void *value, +* int report, int mark, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function gets a value for the specified keyword from the +* supplied FitsChan, and stores it in the supplied buffer. Optionally, +* the keyword is marked as having been read into an AST object so that +* it is not written out when the FitsChan is deleted. + +* Parameters: +* this +* A pointer to the FitsChan containing the keyword values to be +* read. +* keyname +* A pointer to a string holding the keyword name. +* type +* The FITS data type in which to return the keyword value. If the +* stored value is not of the requested type, it is converted if +* possible. +* value +* A pointer to a buffer of suitable size to receive the keyword +* value. The supplied value is left unchanged if the keyword is +* not found. +* report +* Should an error be reported if the keyword cannot be found, or +* cannot be converted to the requested type? +* mark +* Should the card be marked as having been used? +* method +* A string holding the name of the calling method. +* class +* A string holding the object class. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the keyword does not exist in "this", or cannot be +* converted to the requested type. One is returned otherwise. + +* Notes: +* - An error is reported if the keyword value is undefined. +* - A value of zero is returned if an error has already occurred, +* or if an error occurs within this function. +*/ + +/* Local Variables: */ + int icard; /* Current card index */ + int ret; /* Returned value */ + +/* Check the status */ + if( !astOK ) return 0; + +/* Save the current card index. */ + icard = astGetCard( this ); + +/* Attempt to find the supplied keyword. */ + ret = SearchCard( this, keyname, method, class, status ); + +/* If the keyword was found, convert the current card's data value and copy + it to the supplied buffer. */ + if( ret ){ + if( CnvValue( this, type, 0, value, method, status ) ) { + +/* If required, mark it as having been read into an AST object. */ + if( mark ) MarkCard( this, status ); + +/* If the value is undefined, report an error if "report" is non-zero. */ + if( type == AST__UNDEF && report && astOK ) { + ret = 0; + astError( AST__FUNDEF, "%s(%s): FITS keyword \"%s\" has no value.", + status, method, class, keyname ); + } + +/* If the value could not be converted to the requested data, type report + an error if reporting is enabled. */ + } else { + ret = 0; + if( report && astOK ){ + astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword '%s' to %s.", + status, method, class, keyname, type_names[ type ] ); + } + } + +/* If the keyword was not found, report an error if "report" is non-zero. */ + } else if( report && astOK ){ + astError( AST__BDFTS, "%s(%s): Unable to find a value for FITS " + "keyword \"%s\".", status, method, class, keyname ); + } + +/* Reinstate the original current card index. */ + astSetCard( this, icard ); + +/* If an error has occurred, return 0. */ + if( !astOK ) ret = 0; + +/* Return the result. */ + return ret; +} + +static int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname, + int type, void *value, int report, const char *method, + const char *class, int *status ){ +/* +* Name: +* GetValue2 + +* Purpose: +* Obtain a FITS keyword value from one of two FitsChans. + +* Type: +* Private function. + +* Synopsis: +* int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname, +* int type, void *value, int report, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function attempts to get a value for the specified keyword from +* the first supplied FitsChan. If this fails (due to the FitsChan not +* containing a value for the ketword) then an attempt is made to get +* a value for the keyword from the second supplied FitsChan. + +* Parameters: +* this1 +* A pointer to the first FitsChan to be used. +* this2 +* A pointer to the second FitsChan to be used. +* keyname +* A pointer to a string holding the keyword name. +* type +* The FITS data type in which to return the keyword value. If the +* stored value is not of the requested type, it is converted if +* possible. +* value +* A pointer to a buffer of suitable size to receive the keyword +* value. The supplied value is left unchanged if the keyword is +* not found. +* report +* Should an error be reported if the keyword cannot be found, or +* cannot be converted to the requested type? +* method +* A string holding the name of the calling method. +* class +* A string holding the object class. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the keyword does not exist in either FitsChan, or cannot be +* converted to the requested type. One is returned otherwise. + +* Notes: +* - A value of zero is returned if an error has already occurred, +* or if an error occurs within this function. +* - If the card is found in the first FitsChan, it is not marked as +* having been used. If the card is found in the second FitsChan, it is +* marked as having been used. +*/ + +/* Local Variables: */ + int ret; /* Returned value */ + +/* Check the status */ + if( !astOK ) return 0; + +/* Try the first FitsChan. If this fails try the second. Do not report + an error if the keyword is not found in the first FitsChan (this will + be done, if required, once the second FitsChan has been searched). */ + ret = GetValue( this1, keyname, type, value, 0, 0, method, class, status ); + if( ! ret ) { + ret = GetValue( this2, keyname, type, value, report, 1, method, class, status ); + } + +/* If an error has occurred, return 0. */ + if( !astOK ) ret = 0; + +/* Return the result. */ + return ret; +} + +static int HasAIPSSpecAxis( AstFitsChan *this, const char *method, + const char *class, int *status ){ + +/* +* Name: +* HasAIPSSpecAxis + +* Purpose: +* Does the FitsChan contain an AIPS spectral CTYPE keyword? + +* Type: +* Private function. + +* Synopsis: + +* int HasAIPSSpecAxis( AstFitsChan *this, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function returns a non-zero value if the FitsCHan contains a +* CTYPE value which conforms to the non-standard system used by AIPS. + +* Parameters: +* this +* A pointer to the FitsChan to be used. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if an AIPS spectral CTYPE keyword was found. +*/ + +/* Local Variables: */ + char *assys; /* AIPS standard of rest type */ + char *astype; /* AIPS spectral type */ + char *cval; /* Pointer to character string */ + int j; /* Current axis index */ + int jhi; /* Highest axis index with a CTYPE */ + int jlo; /* Lowest axis index with a CTYPE */ + int ret; /* Returned value */ + +/* Initialise */ + ret = 0; + +/* Check the status */ + if( !astOK ) return ret; + +/* If the FitsChan contains any CTYPE values, convert the bounds from + one-based to zero-based, and loop round them all. */ + if( astKeyFields( this, "CTYPE%1d", 1, &jhi, &jlo ) ) { + jlo--; + jhi--; + for( j = jlo; j <= jhi; j++ ) { + +/* Get the next CTYPE value. If found, see if it is an AIPS spectral + CTYPE value. */ + if( GetValue( this, FormatKey( "CTYPE", j + 1, -1, ' ', status ), + AST__STRING, (void *) &cval, 0, 0, method, + class, status ) ){ + if( IsAIPSSpectral( cval, &astype, &assys, status ) ) { + ret = 1; + break; + } + } + } + } + +/* If an error has occurred, return 0. */ + if( !astOK ) ret = 0; + +/* Return the result. */ + return ret; +} + +static int HasCard( AstFitsChan *this, const char *name, + const char *method, const char *class, int *status ){ + +/* +* Name: +* HasCard + +* Purpose: +* Check if the FitsChan contains a specified keyword. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int HasCard( AstFitsChan *this, const char *name, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns a non-zero value if the FitsChan contains the given keyword, +* and zero otherwise. The current card is unchanged. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a string holding the keyword name. +* method +* Pointer to string holding name of calling method. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if a card was found refering to the given +* keyword. Otherwise zero is returned. +*/ + +/* Check the supplied pointers (we can rely on astMapHasKey to check the + inherited status). */ + if( !name || !this || !this->keywords ) return 0; + +/* Search the KeyMap holding the keywords currently in the FitsChan, + returning non-zero if the keyword was found. A KeyMap is used because + it uses a hashing algorithm to find the entries and is therefore a lot + quicker than searching through the list of linked FitsCards. */ + return astMapHasKey( this->keywords, name ); +} +void astInitFitsChanVtab_( AstFitsChanVtab *vtab, const char *name, int *status ) { + +/* +*+ +* Name: +* astInitFitsChanVtab + +* Purpose: +* Initialise a virtual function table for a FitsChan. + +* Type: +* Protected function. + +* Synopsis: +* #include "fitschan.h" +* void astInitFitsChanVtab( AstFitsChanVtab *vtab, const char *name ) + +* Class Membership: +* FitsChan vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the FitsChan class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstChannelVtab *channel; /* Pointer to Channel component of Vtab */ + char buf[ 100 ]; /* Buffer large enough to store formatted INT_MAX */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitChannelVtab( (AstChannelVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAFitsChan) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstChannelVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ + +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->PutCards = PutCards; + vtab->PutFits = PutFits; + vtab->DelFits = DelFits; + vtab->GetTables = GetTables; + vtab->PutTables = PutTables; + vtab->PutTable = PutTable; + vtab->TableSource = TableSource; + vtab->SetTableSource = SetTableSource; + vtab->RemoveTables = RemoveTables; + vtab->PurgeWCS = PurgeWCS; + vtab->RetainFits = RetainFits; + vtab->FindFits = FindFits; + vtab->KeyFields = KeyFields; + vtab->ReadFits = ReadFits; + vtab->ShowFits = ShowFits; + vtab->WriteFits = WriteFits; + vtab->EmptyFits = EmptyFits; + vtab->FitsEof = FitsEof; + vtab->GetFitsCF = GetFitsCF; + vtab->GetFitsCI = GetFitsCI; + vtab->GetFitsF = GetFitsF; + vtab->GetFitsI = GetFitsI; + vtab->GetFitsL = GetFitsL; + vtab->TestFits = TestFits; + vtab->GetFitsS = GetFitsS; + vtab->GetFitsCN = GetFitsCN; + vtab->FitsGetCom = FitsGetCom; + vtab->SetFitsCom = SetFitsCom; + vtab->SetFitsCF = SetFitsCF; + vtab->SetFitsCI = SetFitsCI; + vtab->SetFitsF = SetFitsF; + vtab->SetFitsI = SetFitsI; + vtab->SetFitsL = SetFitsL; + vtab->SetFitsU = SetFitsU; + vtab->SetFitsS = SetFitsS; + vtab->SetFitsCN = SetFitsCN; + vtab->SetFitsCM = SetFitsCM; + vtab->ClearCard = ClearCard; + vtab->TestCard = TestCard; + vtab->SetCard = SetCard; + vtab->GetCard = GetCard; + vtab->ClearFitsDigits = ClearFitsDigits; + vtab->TestFitsDigits = TestFitsDigits; + vtab->SetFitsDigits = SetFitsDigits; + vtab->GetFitsDigits = GetFitsDigits; + vtab->ClearFitsAxisOrder = ClearFitsAxisOrder; + vtab->TestFitsAxisOrder = TestFitsAxisOrder; + vtab->SetFitsAxisOrder = SetFitsAxisOrder; + vtab->GetFitsAxisOrder = GetFitsAxisOrder; + vtab->ClearDefB1950 = ClearDefB1950; + vtab->TestDefB1950 = TestDefB1950; + vtab->SetDefB1950 = SetDefB1950; + vtab->GetDefB1950 = GetDefB1950; + vtab->ClearTabOK = ClearTabOK; + vtab->TestTabOK = TestTabOK; + vtab->SetTabOK = SetTabOK; + vtab->GetTabOK = GetTabOK; + vtab->ClearCarLin = ClearCarLin; + vtab->TestCarLin = TestCarLin; + vtab->SetCarLin = SetCarLin; + vtab->GetCarLin = GetCarLin; + vtab->ClearPolyTan = ClearPolyTan; + vtab->TestPolyTan = TestPolyTan; + vtab->SetPolyTan = SetPolyTan; + vtab->GetPolyTan = GetPolyTan; + vtab->ClearIwc = ClearIwc; + vtab->TestIwc = TestIwc; + vtab->SetIwc = SetIwc; + vtab->GetIwc = GetIwc; + vtab->ClearWarnings = ClearWarnings; + vtab->TestWarnings = TestWarnings; + vtab->SetWarnings = SetWarnings; + vtab->GetWarnings = GetWarnings; + vtab->GetCardType = GetCardType; + vtab->GetCardName = GetCardName; + vtab->GetCardComm = GetCardComm; + vtab->GetNcard = GetNcard; + vtab->GetNkey = GetNkey; + vtab->GetAllWarnings = GetAllWarnings; + vtab->ClearEncoding = ClearEncoding; + vtab->TestEncoding = TestEncoding; + vtab->SetEncoding = SetEncoding; + vtab->GetEncoding = GetEncoding; + vtab->ClearClean = ClearClean; + vtab->TestClean = TestClean; + vtab->SetClean = SetClean; + vtab->GetClean = GetClean; + vtab->ClearCDMatrix = ClearCDMatrix; + vtab->TestCDMatrix = TestCDMatrix; + vtab->SetCDMatrix = SetCDMatrix; + vtab->GetCDMatrix = GetCDMatrix; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + channel = (AstChannelVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + parent_write = channel->Write; + channel->Write = Write; + parent_read = channel->Read; + channel->Read = Read; + parent_getskip = channel->GetSkip; + channel->GetSkip = GetSkip; + parent_getfull = channel->GetFull; + channel->GetFull = GetFull; + channel->WriteBegin = WriteBegin; + channel->WriteIsA = WriteIsA; + channel->WriteEnd = WriteEnd; + channel->WriteInt = WriteInt; + channel->WriteDouble = WriteDouble; + channel->WriteString = WriteString; + channel->WriteObject = WriteObject; + channel->GetNextData = GetNextData; + parent_setsourcefile = channel->SetSourceFile; + channel->SetSourceFile = SetSourceFile; + +/* Declare the class dump, copy and delete functions.*/ + astSetDump( vtab, Dump, "FitsChan", "I/O channels to FITS files" ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + astSetDelete( (AstObjectVtab *) vtab, Delete ); + +/* Max number of characters needed to format an int. */ + LOCK_MUTEX4 + sprintf( buf, "%d", INT_MAX ); + int_dig = strlen( buf ); + +/* Create a pair of MJD TimeFrames which will be used for converting to and + from TDB. */ + astBeginPM; + if( !tdbframe ) tdbframe = astTimeFrame( "system=MJD,timescale=TDB", status ); + if( !timeframe ) timeframe = astTimeFrame( "system=MJD", status ); + astEndPM; + UNLOCK_MUTEX4 + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void InsCard( AstFitsChan *this, int overwrite, const char *name, + int type, void *data, const char *comment, + const char *method, const char *class, int *status ){ + +/* +* Name: +* InsCard + +* Purpose: +* Inserts a card into a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void InsCard( AstFitsChan *this, int overwrite, const char *name, +* int type, void *data, const char *comment, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Either appends a new card to a FitsChan, or over-writes an existing +* card, holding the supplied keyword name, value and comment. + +* Parameters: +* this +* Pointer to the FitsChan containing the filters to apply to the +* keyword name. If a NULL pointer is supplied, no filtering is applied. +* overwrite +* If non-zero, the new card over-writes the current card given by +* the "Card" attribute, and the current card is incremented so +* that it refers to the next card. Otherwise, the new card is +* inserted in front of the current card and the current card is +* left unchanged. +* name +* Pointer to a string holding the keyword name of the new card. +* type +* An integer value representing the data type of the keyword. +* data +* Pointer to the data associated with the keyword. +* comment +* Pointer to a null-terminated string holding a comment. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - An error is reported if an attempt is made to change the data type +* of an existing card. +* - If a type of AST__COMMENT is supplied, then any data value (of any +* type) associated with an existing card is left unchanged. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + int flags; /* Flags to assign to new card */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* If the current card is to be over-written, delete the current card (the + next card in the list, if any, will become the new current card). */ + if( overwrite ) DeleteCard( this, method, class, status ); + +/* If requested, set both NEW flags for the new card. */ + flags = ( mark_new ) ? ( NEW1 | NEW2 ): 0; + +/* Insert the new card into the list, just before the current card. */ + NewCard( this, name, type, data, comment, flags, status ); +} + +static int IRAFFromStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ + +/* +* Name: +* IRAFFromStore + +* Purpose: +* Store WCS keywords in a FitsChan using FITS-IRAF encoding. + +* Type: +* Private function. + +* Synopsis: + +* int IRAFFromStore( AstFitsChan *this, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using FITS-IRAF encoding. +* +* IRAF encoding is like FITS-WCS encoding but with the following +* restrictions: +* +* 1) The celestial projection must not have any projection parameters +* which are not set to their default values. The one exception to this +* is that SIN projections are acceptable if the associated projection +* parameter PV_1 is zero and PV_2 = cot( reference point +* latitude). This is encoded using the string "-NCP". The SFL projection +* is encoded using the string "-GLS". Note, the original IRAF WCS +* system only recognised a small subset of the currently available +* projections, but some more recent IRAF-like software recognizes some +* of the new projections included in the FITS-WCS encoding. +* +* 2) The celestial axes must be RA/DEC, galactic or ecliptic. +* +* 3) LONPOLE and LATPOLE cannot be used. +* +* 4) Only primary axis descriptions are written out. +* +* 5) RADECSYS is used in place of RADESYS. +* +* 6) PC/CDELT keywords are not allowed (CD must be used) + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + char *comm; /* Pointer to comment string */ + char *cval; /* Pointer to string keyword value */ + char combuf[80]; /* Buffer for FITS card comment */ + char lattype[MXCTYPELEN];/* Latitude axis CTYPE */ + char lontype[MXCTYPELEN];/* Longitude axis CTYPE */ + char s; /* Co-ordinate version character */ + char sign[2]; /* Fraction's sign character */ + double cdelt; /* A CDELT value */ + double fd; /* Fraction of a day */ + double mjd99; /* MJD at start of 1999 */ + double p1, p2; /* Projection parameters */ + double val; /* General purpose value */ + int axlat; /* Index of latitude FITS WCS axis */ + int axlon; /* Index of longitude FITS WCS axis */ + int axspec; /* Index of spectral FITS WCS axis */ + int i; /* Axis index */ + int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ + int iymdf[ 4 ]; /* Year, month, date, fractional day */ + int j; /* Axis index */ + int jj; /* SlaLib status */ + int naxis; /* No. of axes */ + int ok; /* Is FitsSTore OK for IRAF encoding? */ + int prj; /* Projection type */ + int ret; /* Returned value. */ + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* First check that the values in the FitsStore conform to the + requirements of the IRAF encoding. Assume they do to begin with. */ + ok = 1; + +/* Just do primary axes. */ + s = ' '; + +/* Look for the primary celestial and spectral axes. */ + FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); + +/* If both longitude and latitude axes are present and thereis no + spectral axis...*/ + if( axlon >= 0 && axlat >= 0 ) { + +/* Get the CTYPE values for both axes. */ + cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); + if( !cval ) return ret; + strcpy( lontype, cval ); + cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); + if( !cval ) return ret; + strcpy( lattype, cval ); + +/* Extract the projection type as specified by the last 4 characters + in the CTYPE keyword value. */ + prj = astWcsPrjType( lattype + 4 ); + +/* Check the projection type is OK. Assume not initially. */ + ok = 0; + +/* FITS-IRAF cannot handle the AST-specific TPN projection. */ + if( prj == AST__TPN || prj == AST__WCSBAD ) { + ok = 0; + +/* SIN projections are handled later. */ + } else if( prj != AST__SIN ){ + +/* There must be no projection parameters. */ + if( GetMaxJM( &(store->pv), ' ', status ) == -1 ) ok = 1; + +/* Change the new SFL projection code to to the older equivalent GLS */ + if( prj == AST__SFL ){ + (void) strcpy( lontype + 4, "-GLS" ); + (void) strcpy( lattype + 4, "-GLS" ); + } + +/* SIN projections are only acceptable if the associated projection + parameters are both zero, or if the first is zero and the second + = cot( reference point latitude ) (the latter case is equivalent to + the old NCP projection). */ + } else { + p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status ); + p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status ); + if( p1 == AST__BAD ) p1 = 0.0; + if( p2 == AST__BAD ) p2 = 0.0; + val = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + if( p1 == 0.0 ) { + if( p2 == 0.0 ) { + ok = 1; + } else if( fabs( p2 ) >= 1.0E14 && val == 0.0 ){ + ok = 1; + (void) strcpy( lontype + 4, "-NCP" ); + (void) strcpy( lattype + 4, "-NCP" ); + } else if( fabs( p2*tan( AST__DD2R*val ) - 1.0 ) + < 0.01 ){ + ok = 1; + (void) strcpy( lontype + 4, "-NCP" ); + (void) strcpy( lattype + 4, "-NCP" ); + } + } + } + } + +/* Identify the celestial coordinate system from the first 4 characters of the + longitude CTYPE value. Only RA, galactic longitude, and ecliptic + longitude can be stored using FITS-IRAF. */ + if( strncmp( lontype, "RA--", 4 ) && + strncmp( lontype, "GLON", 4 ) && + strncmp( lontype, "ELON", 4 ) ) ok = 0; + +/* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot + be encoded using FITS-IRAF. */ + if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ) + != AST__BAD || + GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ) + != AST__BAD ) ok = 0; + +/* If there are no celestial axes, the physical Frame can be written out + using FITS-IRAF. */ + } else { + ok = 1; + } + +/* Save the number of axes */ + naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1; + +/* If this is different to the value of NAXIS abort since this encoding + does not support WCSAXES keyword. */ + if( naxis != store->naxis ) ok = 0; + +/* Return if the FitsStore does not conform to IRAF encoding. */ + if( !ok ) return ret; + +/* Get and save CRPIX for all pixel axes. These are required, so return + if they are not available. */ + for( i = 0; i < naxis; i++ ){ + val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ); + if( val == AST__BAD ) return ret; + sprintf( combuf, "Reference pixel on axis %d", i + 1 ); + SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + +/* Get and save CRVAL for all intermediate axes. These are required, so return + if they are not available. */ + for( j = 0; j < naxis; j++ ){ + val = GetItem( &(store->crval), j, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) return ret; + sprintf( combuf, "Value at ref. pixel on axis %d", j + 1 ); + SetValue( this, FormatKey( "CRVAL", j + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + +/* Get and save CTYPE for all intermediate axes. These are required, so return + if they are not available. Use the potentially modified versions saved + above for the celestial axes. */ + for( i = 0; i < naxis; i++ ){ + if( i == axlat ) { + cval = lattype; + } else if( i == axlon ) { + cval = lontype; + } else { + cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( !cval ) return ret; + } + if( strlen(cval) > 4 && !strcmp( cval + 4, "-TAB" ) ) return ret; + comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + if( !comm ) { + sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); + comm = combuf; + } + SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING, + comm, status ); + } + +/* CD matrix (the product of the CDELT and PC matrices). */ + for( i = 0; i < naxis; i++ ){ + cdelt = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( cdelt == AST__BAD ) cdelt = 1.0; + for( j = 0; j < naxis; j++ ){ + val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; + val *= cdelt; + if( val != 0.0 ) { + SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val, + AST__FLOAT, "Transformation matrix element", status ); + } + } + } + +/* Get and save CUNIT for all intermediate axes. These are NOT required, so + do not return if they are not available. */ + for( i = 0; i < naxis; i++ ){ + cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); + if( cval ) { + sprintf( combuf, "Units for axis %d", i + 1 ); + SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, + combuf, status ); + } + } + +/* Get and save RADECSYS. This is NOT required, so do not return if it is + not available. */ + cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); + if( cval ) SetValue( this, "RADECSYS", &cval, AST__STRING, + "Reference frame for RA/DEC values", status ); + +/* Reference equinox */ + val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT, + "Epoch of reference equinox", status ); + +/* Date of observation */ + val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD ) { + +/* The format used for the DATE-OBS keyword depends on the value of the + keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. + Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ + palCaldj( 99, 1, 1, &mjd99, &jj ); + if( val < mjd99 ) { + palDjcal( 0, val, iymdf, &jj ); + sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], + iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); + } else { + palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); + palDd2tf( 3, fd, sign, ihmsf ); + sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", + iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], + ihmsf[2], ihmsf[3] ); + } + +/* Now store the formatted string in the FitsChan. */ + cval = combuf; + SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, + "Date of observation", status ); + } + +/* If we get here we have succeeded. */ + ret = 1; + +/* Return zero or ret depending on whether an error has occurred. */ + return astOK ? ret : 0; +} + +static int IsMapLinear( AstMapping *smap, const double lbnd_in[], + const double ubnd_in[], int coord_out, int *status ) { +/* +* Name: +* IsMapLinear + +* Purpose: +* See if a specified Mapping output is linearly related to the +* Mapping inputs. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int IsMapLinear( AstMapping *smap, const double lbnd_in[], +* const double ubnd_in[], int coord_out, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns a flag indicating if the specified output of the supplied +* Mapping is a linear function of the Mapping inputs. A set of output +* positions are created which are evenly spaced along the specified +* output coordinate. The spacing is chosen so that the entire range +* of the output coordinate is covered in 20 steps. The other output +* coordinates are held fixed at arbitrary values (actually, values +* at which the specified output coordinate achieves its minimum value). +* This set of output positions is transformed into the corresponding +* set of input coordinates using the inverse of the supplied Mapping. +* A least squares linear fit is then made which models each input +* coordinate as a linear function of the specified output coordinate. +* The residual at every point in this fit must be less than some +* small fraction of the total range of the corresponding input +* coordinate for the Mapping to be considered linear. + +* Parameters: +* smap +* Pointer to the Mapping. +* lbnd_in +* Pointer to an array of double, with one element for each +* Mapping input coordinate. This should contain the lower bound +* of the input box in each input dimension. +* ubnd_in +* Pointer to an array of double, with one element for each +* Mapping input coordinate. This should contain the upper bound +* of the input box in each input dimension. +* coord_out +* The zero-based index of the Mapping output which is to be checked. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the specified Mapping output is linear. Zero otherwise. +*/ + +/* Local Constants: */ +#define NP 20 + +/* Local Variables: */ + AstMapping *map; + AstPointSet *pset1; + AstPointSet *pset2; + double **ptr1; + double **ptr2; + double *p; + double *s; + double *xl; + double c; + double d; + double delta; + double in_lbnd; + double in_ubnd; + double lbnd_out; + double m; + double p0; + double pv; + double sn; + double sp; + double sps; + double ss2; + double ss; + double sv; + double tol; + double ubnd_out; + int *ins; + int boxok; + int i; + int j; + int nin; + int nout; + int oldrep; + int ret; + +/* Initialise */ + ret = 0; + +/* Check inherited status */ + if( !astOK ) return ret; + +/* Attempt to split off the required output (in case any of the other + outputs are associated with Mappings that do not have an inverse). */ + astInvert( smap ); + ins = astMapSplit( smap, 1, &coord_out, &map ); + astInvert( smap ); + +/* If successful, check that the output is fed by only one input. */ + if( ins ) { + if( astGetNin( map ) == 1 ) { + +/* If so, invert the map so that it goes from pixel to wcs, and then + modify the supplied arguments so that they refer to the single required + axis. */ + astInvert( map ); + lbnd_in += coord_out; + ubnd_in += coord_out; + coord_out = 0; + +/* If the output was fed by more than one input, annul the split mapping + and use the supplied nmapping. */ + } else { + (void) astAnnul( map ); + map = astClone( smap ); + } + ins = astFree( ins ); + +/* If the supplied Mapping could not be split, use the supplied nmapping. */ + } else { + map = astClone( smap ); + } + +/* Check the Mapping is defined in both directions. */ + if( astGetTranForward( map ) && astGetTranInverse( map ) ) { + +/* Allocate resources. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + xl = astMalloc( sizeof( double )*(size_t) nin ); + pset1 = astPointSet( NP, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + pset2 = astPointSet( NP, nout, "", status ); + ptr2 = astGetPoints( pset2 ); + +/* Call astMapBox in a new error reporting context. */ + boxok = 0; + if( astOK ) { + +/* Temporarily switch off error reporting so that no report is made if + astMapBox cannot find a bounding box (which can legitimately happen with + some non-linear Mappings). */ + oldrep = astReporting( 0 ); + +/* Find the upper and lower bounds on the specified Mapping output. This also + returns the input coords of a point at which the required output has its + lowest value. */ + astMapBox( map, lbnd_in, ubnd_in, 1, coord_out, &lbnd_out, &ubnd_out, + xl, NULL ); + +/* If the box could not be found, clear the error status and pass on. */ + if( !astOK ) { + astClearStatus; + +/* If the box was found OK, flag this and check if the bounds are equal. + If so we cannot use them. In this case create new bounds. */ + } else { + boxok = 1; + if( astEQUAL( lbnd_out, ubnd_out ) ) { + m = 0.5*( lbnd_out + ubnd_out ); + if( fabs( m ) > 1.0E-15 ) { + lbnd_out = 0.9*m; + ubnd_out = 1.1*m; + } else { + lbnd_out = -1.0; + ubnd_out = 1.0; + } + } + } + +/* Re-instate error reporting. */ + astReporting( oldrep ); + } + +/* Check pointers can be used safely and a box was obtained. */ + if( astOK && boxok ) { + +/* Transform the input position returned by astMapBox using the supplied + Mapping to get the corresponding output position. Fill all unused + elements of the PointSet with AST__BAD. */ + for( i = 0; i < nin; i++ ){ + p = ptr1[ i ]; + *(p++) = xl[ i ]; + for( j = 1; j < NP; j++ ) *(p++) = AST__BAD; + } + (void) astTransform( map, pset1, 1, pset2 ); + +/* Now create a set of NP points evenly spaced in output coordinates. The + first point is at the output position found above. Each subsequent + point is incremented by a fixed amount along the specified output + coordinate (the values on all other output coordinates is held fixed). */ + delta = ( ubnd_out - lbnd_out )/ ( NP - 1 ); + for( i = 0; i < nout; i++ ){ + p = ptr2[ i ]; + if( i == coord_out ) { + for( j = 0; j < NP; j++ ) *(p++) = lbnd_out + j*delta; + } else { + p0 = p[ 0 ]; + for( j = 0; j < NP; j++ ) *(p++) = p0; + } + } + +/* Transform these output positions into input positions using the + inverse Mapping. */ + (void) astTransform( map, pset2, 0, pset1 ); + +/* Do a least squares fit to each input coordinate. Each fit gives the + corresponding input coordinate value as a linear function of the + specified output coordinate value. Note, linear function should never + produce bad values so abort if a bad value is found. */ + ret = 1; + s = ptr2[ coord_out ]; + for( i = 0; i < nin; i++ ) { + p = ptr1[ i ]; + +/* Form the required sums. Also find the largest and smallest input + coordinate value achieved. */ + sp = 0.0; + ss = 0.0; + sps = 0.0; + sn = 0.0; + ss2 = 0.0; + in_lbnd = DBL_MAX; + in_ubnd = DBL_MIN; + for( j = 0; j < NP; j++ ) { + sv = s[ j ]; + pv = p[ j ]; + if( pv != AST__BAD && sv != AST__BAD ) { + sp += pv; + ss += sv; + sps += pv*sv; + sn += 1.0; + ss2 += sv*sv; + if( pv < in_lbnd ) in_lbnd = pv; + if( pv > in_ubnd ) in_ubnd = pv; + } else { + sn = 0.0; + break; + } + } + +/* Ignore input axes which are independant of the output axis. */ + if( !astEQUAL( in_lbnd, in_ubnd ) ) { + +/* Calculate the constants "input coord = m*output coord + c" */ + d = ss*ss - sn*ss2; + if( sn > 0.0 && d != 0.0 ) { + m = ( sp*ss - sps*sn )/d; + c = ( sps*ss - sp*ss2 )/d; + +/* Subtract off the fit value form the "p" values to get the residuals of + the fit. */ + for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c; + +/* We now do a least squares fit to the residuals. This second fit is done + because the first least squares fit sometimes leaves the residuals with a + distinct non-zero gradient. We do not need to worry about bad values + here since we have checked above that there are no bad values. Also we + do not need to recalculate sums which only depend on the "s" values since + they have not changed. */ + sp = 0.0; + sps = 0.0; + for( j = 0; j < NP; j++ ) { + pv = p[ j ]; + sp += pv; + sps += pv*s[ j ]; + } + +/* Find the constants in "input residual = m*output coord + c" equation. */ + m = ( sp*ss - sps*sn )/d; + c = ( sps*ss - sp*ss2 )/d; + +/* Subtract off the fit value form the "p residuals" values to get the + residual redisuals of the fit. */ + for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c; + +/* The requirement for a linear relationship is that the absolute residual + between the input coord produced by the above linear fit and the input + coord produced by the actual Mapping should be less than some small + fraction of the total range of input coord value, at every point. Test + this. */ + tol = 1.0E-7*( in_ubnd - in_lbnd ); + for( j = 0; j < NP; j++ ) { + if( fabs( p[ j ] ) > tol ) { + ret = 0; + break; + } + } + } else { + ret = 0; + } + } + if( !ret ) break; + } + } + +/* Free resources. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + xl = astFree( xl ); + } + map = astAnnul( map ); + +/* Return the answer. */ + return ret; +} + +static AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit, + AstFrame *wcsfrm, double *dim, int iax, + int iwcs, AstFitsTable **table, int *icolmain, + int *icolindex, int *interp, int *status ){ +/* +* Name: +* IsMapTab1D + +* Purpose: +* See if a specified Mapping output is related to a single Mapping input +* via a FITS -TAB algorithm. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit, +* AstFrame *wcsfrm, double *dim, int iax, +* int iwcs, AstFitsTable **table, int *icolmain, +* int *icolindex, int *interp, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A specified axis of the supplied Mapping is tested to see if it +* can be represented by the -TAB alogirithm described in FITS-WCS +* paper III. If the test is passed, a Mapping is returned from the +* specified WCS axis to the corresponding psi axis. A FitsTable is +* also created holding the information to be stored in the +* corresponding FITS binary table. +* +* Note, when creating a -TAB header, AST uses grid coords for the psi +* axis. See FITS-WCS paper III section 6.1.2 for a definition of the +* psi axes. + +* Parameters: +* map +* Pointer to the Mapping from pixel coords to WCS coords. +* scale +* A scale factor by which to multiply the axis values stored in the +* returned FitsTable. Note, the returned Mapping is unaffected by +* this scaling factor. +* unit +* Pointer to the unit string to store with the coords column. If +* NULL, the unit string is extracted form the supplied WCS Frame. +* wcsfrm +* Pointer to a Frame describing WCS coords. +* dim +* An array holding the array dimensions in pixels. AST__BAD should +* be supplied for any unknown dimensions. +* iax +* The zero-based index of the Mapping output which is to be checked. +* iwcs +* The zero-based index of the corresponding FITS WCS axis. +* table +* Pointer to a location holding a pointer to the FitsTable describing +* the -TAB look-up table. If "*table" is NULL on entry, a new +* FitsTable will be created and returned, otherwise the supplied +* FitsTable is used. +* icolmain +* The one-based index of the column within "*table" that holds the +* main data array. +* icolindex +* The one-based index of the column within "*table" that holds the +* index vector. Returned equal to -1 if no index is added to the +* table (i.e. if the index is a unt index). +* interp +* The interpolation method (0=linear, other=nearest neighbour). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the specified "map" output can be described using the -TAB +* algorithm of FITS-WCS paper III, then a 1-input/1-output Mapping +* from the specified WCS axis to the corresponding psi axis (which is +* assumed to be equal to grid coords) is returned. NULL is returned +* otherwise, of if an error occurs. +*/ + +/* Local Variables: */ + AstCmpMap *cm; /* CmpMap pointer */ + AstMapping **map_list; /* Mapping array pointer */ + AstMapping *postmap; /* Total Mapping after LutMap */ + AstMapping *premap; /* Total Mapping before LutMap */ + AstMapping *ret; /* Returned WCS axis Mapping */ + AstMapping *tmap; /* Temporary Mapping */ + AstPermMap *pm; /* PermMap pointer */ + char cellname[ 20 ]; /* Buffer for cell name */ + char colname[ 20 ]; /* Buffer for column name */ + double *lut; /* Pointer to table of Y values */ + double *work1; /* Pointer to work array */ + double *work2; /* Pointer to work array */ + double inc; /* X increment between table entries */ + double start; /* X value at first table entry */ + double v[ 2 ]; /* Y values at start and end of interval */ + double x[ 2 ]; /* X values at start and end of interval */ + int *ins; /* Array of "map" input indices */ + int *invert_list; /* Invert array pointer */ + int *outs; /* Array of "map" output indices */ + int dims[ 2 ]; /* Dimensions of the tab coords array */ + int iin; /* Index of Mapping input */ + int ilut; /* Index of the LutMap within the mappings list */ + int imap; /* Index of current Mapping in list */ + int iout; /* Index of Mapping output */ + int jout; /* Index of Mapping output */ + int nin; /* Number of Mapping inputs */ + int nlut; /* Number of elements in "lut" array */ + int nmap; /* Number of Mappings in the list */ + int nout; /* Number of Mapping outputs */ + int ok; /* Were columns added to the table? */ + int old_invert; /* Original value for Mapping's Invert flag */ + int outperm; /* Index of input that feeds the single output */ + +/* Initialise */ + ret = NULL; + *icolmain = -1; + *icolindex = -1; + *interp = 0; + +/* Check inherited status */ + if( !astOK ) return ret; + +/* Ensure we have aunit string. */ + if( !unit ) unit = astGetUnit( wcsfrm, iax ); + +/* Check that the requested mapping output is fed by only one mapping + input, identify that input, and extract the input->output mapping from + the total mapping. Since astMapSplit splits off a specified input, we + need to invert the Mapping first so we can split off a specified output. */ + astInvert( map ); + ins = astMapSplit( map, 1, &iax, &ret ); + astInvert( map ); + +/* If the Mapping could not be split, try a different approach in which + each input is checked in turn to see if it feeds the specified output. */ + if( !ins ) { + +/* Loop round each input of "map". */ + nin = astGetNin( map ); + for( iin = 0; iin < nin && !ins; iin++ ) { + +/* Attempt to find a group of outputs (of "map") that are fed by just + this one input. */ + outs = astMapSplit( map, 1, &iin, &ret ); + +/* If successful, "ret" will be a Mapping with one input corresponding to + input "iin" of "map, and one or more outputs. We loop round these + outputs to see if any of them correspond to output "iax" of "map". */ + if( outs ) { + nout = astGetNout( ret ); + for( iout = 0; iout < nout; iout++ ) { + if( outs[ iout ] == iax ) break; + } + +/* Did input "iin" feed the output "iax" (and possibly other outputs)? */ + if( iout < nout ) { + +/* The "ret" Mapping is now a 1-input (pixel) N-output (WCS) Mapping in which + output "iout" corresponds to output "iax" of Mapping. To be compatible + with the previous approach, we want "ret" to be a 1-input (WCS) to + 1-output (pixel) Mapping in which the input corresponds to output + "iax" of Mapping. To get "ret" into this form, we first append a PermMap + to "ret" that selects a single output ("iout"), and then invert the + whole CmpMap. */ + for( jout = 0; jout < nout; jout++ ) { + outs[ jout ] = -1; + } + outs[ iout ] = 0; + outperm = iout; + + pm = astPermMap( nout, outs, 1, &outperm, NULL, "", status ); + cm = astCmpMap( ret, pm, 1, " ", status ); + (void) astAnnul( ret ); + pm = astAnnul( pm ); + ret = (AstMapping *) cm; + astInvert( ret ); + +/* The earlier approach leves ins[ 0 ] holding the index of the input to + "map" that feeds output iax. Ensure we have this too. */ + ins = outs; + ins[ 0 ] = iin; + +/* Free resources if the current input did not feed the required output. */ + } else { + outs = astFree( outs ); + ret = astAnnul( ret ); + } + } + } + } + +/* If the Mapping still could not be split, try again on a copy of the + Mapping in which all PermMaps provide an alternative implementation of + the astMapSplit method. */ + if( !ins ) { + astInvert( map ); + tmap = astCopy( map ); + ChangePermSplit( tmap, status ); + ins = astMapSplit( tmap, 1, &iax, &ret ); + tmap = astAnnul( tmap ); + astInvert( map ); + } + +/* Assume the Mapping cannot be represented by -TAB */ + ok = 0; + +/* Check a Mapping was returned by astMapSplit. If so, it will be the + mapping from the requested output of "map" (the WCS axis) to the + corresponding input(s) (grid axes). Check only one "map" input feeds the + requested output. */ + if( ins && ret && astGetNout( ret ) == 1 ) { + +/* Invert the Mapping so that the input is grid coord and the output is + WCS coord. */ + astInvert( ret ); + +/* We now search the "ret" mapping for a non-inverted LutMap, splitting ret + up into three serial components: 1) the mappings before the LutMap, 2) the + LutMap itself, and 3) the mappings following the LutMap. First, decompose + the mapping into a list of series mappings. */ + map_list = NULL; + invert_list = NULL; + nmap = 0; + astMapList( ret, 1, astGetInvert( ret ), &nmap, &map_list, + &invert_list ); + +/* Search the list for a non-inverted LutMap. */ + ilut = -1; + for( imap = 0; imap < nmap; imap++ ) { + if( astIsALutMap( map_list[ imap ] ) && !(invert_list[ imap ]) ) { + ilut = imap; + break; + } + } + +/* If a LutMap was found, combine all Mappings before the LutMap into a + single Mapping. Remember to set the Mapping Invert flags temporarily to + the values used within the CmpMap. */ + if( ilut >= 0 ) { + premap = (AstMapping *) astUnitMap( 1, " ", status ); + for( imap = 0; imap < ilut; imap++ ) { + old_invert = astGetInvert( map_list[ imap ] ); + astSetInvert( map_list[ imap ], invert_list[ imap ] ); + tmap = (AstMapping *) astCmpMap( premap, map_list[ imap ], 1, + " ", status ); + astSetInvert( map_list[ imap ], old_invert ); + (void) astAnnul( premap ); + premap = tmap; + } + +/* Also combine all Mappings after the LutMap into a single Mapping, setting + the Mapping Invert flags temporarily to the values used within the + CmpMap. */ + postmap = (AstMapping *) astUnitMap( 1, " ", status ); + for( imap = ilut + 1; imap < nmap; imap++ ) { + old_invert = astGetInvert( map_list[ imap ] ); + astSetInvert( map_list[ imap ], invert_list[ imap ] ); + tmap = (AstMapping *) astCmpMap( postmap, map_list[ imap ], 1, + " ", status ); + astSetInvert( map_list[ imap ], old_invert ); + (void) astAnnul( postmap ); + postmap = tmap; + } + +/* Get the table of values, and other attributes, from the LutMap. */ + lut = astGetLutMapInfo( map_list[ ilut ], &start, &inc, &nlut ); + *interp = astGetLutInterp( map_list[ ilut ] ); + +/* If required, create a FitsTable to hold the returned table info. */ + if( ! *table ) *table = astFitsTable( NULL, "", status ); + ok = 1; + +/* Define the properties of the column in the FitsTable that holds the main + coordinate array. Points on a WCS axis are described by a single value + (wavelength, frequency, or whatever), but the coords array has to be + 2-dimensional, with an initial degenerate axis, as required by FITS-WCS + paper III. */ + dims[ 0 ] = 1; + dims[ 1 ] = nlut; + sprintf( colname, "COORDS%d", iwcs + 1 ); + astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit ); + +/* Get the one-based index of the column just added to the table. */ + *icolmain = astGetNcolumn( *table ); + +/* Get workspace. */ + work1 = astMalloc( nlut*sizeof( double ) ); + if( astOK ) { + +/* Transform the LutMap table values using the post-lutmap mapping to + get the list of WCS values in AST units. */ + astTran1( postmap, nlut, lut, 1, work1 ); + +/* Convert them to FITS units (e.g. celestial axis values should be + converted from radians to degrees). */ + for( ilut = 0; ilut < nlut; ilut++ ) work1[ ilut ] *= scale; + +/* Store them in row 1, column COORDS, in the FitsTable. */ + sprintf( cellname, "COORDS%d(1)", iwcs + 1 ); + astMapPut1D( *table, cellname, nlut, work1, NULL ); + +/* Create an array holding the LutMap input value at the centre of each + table entry. Re-use the "lut" array since we no longer need it. */ + for( ilut = 0; ilut < nlut; ilut++ ) { + lut[ ilut ] = start + ilut*inc; + } + +/* Transform this array using the inverted pre-lutmap mapping to get the + list of grid coord. */ + astTran1( premap, nlut, lut, 0, work1 ); + +/* Test this list to see if they form a unit index (i.e. index(i) == i+1 ). + (not the "+1" is due to the fact that "i" is zero based). */ + for( ilut = 0; ilut < nlut; ilut++ ) { + if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break; + } + +/* if it is not a unit index, we add the index to the table. */ + if( ilut < nlut ) { + +/* Define the properties of the column in the FitsTable that holds the + indexing vector. */ + sprintf( colname, "INDEX%d", iwcs + 1 ); + astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " ); + +/* Get the one-based index of the column just added to the table. */ + *icolindex = astGetNcolumn( *table ); + +/* Store the values in the column. */ + sprintf( cellname, "INDEX%d(1)", iwcs + 1 ); + astMapPut1D( *table, cellname, nlut, work1, NULL ); + } + } + +/* Free resources. */ + work1 = astFree( work1 ); + lut = astFree( lut ); + premap = astAnnul( premap ); + postmap = astAnnul( postmap ); + +/* If no LutMap was found in the Mapping, then we can create a FitsTable + by sampling the full WCS Mapping at selected input (i.e. grid) + positions. But we can only do this if we know the number of pixels + along the WCS axis. */ + } else if( dim[ ins[ 0 ] ] != AST__BAD ) { + +/* Create two works array each holding a single value. The first holds + the grid coords at which the samples are taken. The second holds the + WCS coords at the sampled positions. These arrays are expanded as + required within function AdaptLut. */ + work1 = astMalloc( sizeof( double ) ); + work2 = astMalloc( sizeof( double ) ); + if( astOK ) { + +/* Get the WCS values at the centres of the first and last pixel on + the WCS axis. */ + x[ 0 ] = 1.0; + x[ 1 ] = dim[ ins[ 0 ] ]; + astTran1( ret, 2, x, 1, v ); + +/* Put the lower limit into the work arrays. */ + work1[ 0 ] = x[ 0 ]; + work2[ 0 ] = v[ 0 ]; + nlut = 1; + +/* Expand the arrays by sampling the WCS axis adaptively so that + more samples occur where the WCS value is changing most rapidly. + We require the maximum error introduced by the table to be 0.25 pixels. */ + AdaptLut( ret, 3, 0.25, x[ 0 ], x[ 1 ], v[ 0 ], v[ 1 ], + &work1, &work2, &nlut, status ); + +/* Create a FitsTable to hold the returned table info. */ + if( ! *table ) *table = astFitsTable( NULL, "", status ); + ok = 1; + +/* Define the properties of the column in the FitsTable that holds the main + coordinate array. */ + sprintf( colname, "COORDS%d", iwcs + 1 ); + dims[ 0 ] = 1; + dims[ 1 ] = nlut; + astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit ); + *icolmain = astGetNcolumn( *table ); + +/* Convert the axis values to FITS units (e.g. celestial axis values should be + converted from radians to degrees). */ + for( ilut = 0; ilut < nlut; ilut++ ) work2[ ilut ] *= scale; + +/* Store the scaled axis values in row 1 of the column. */ + sprintf( cellname, "COORDS%d(1)", iwcs + 1 ); + astMapPut1D( *table, cellname, nlut, work2, NULL ); + +/* Test the index vector to see if they form a unit index (i.e. index(i) == + i+1 ). If not the "+1" is due to the fact that "i" is zero based). If not, store + them as the index vector in the FitsTable. */ + for( ilut = 0; ilut < nlut; ilut++ ) { + if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break; + } + +/* If the index vector is not a unit index, define the properties of the + column in the FitsTable that holds the indexing vector. Then store values + in row 1 of the column. */ + if( ilut < nlut ) { + sprintf( colname, "INDEX%d", iwcs + 1 ); + astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " ); + *icolindex = astGetNcolumn( *table ); + sprintf( cellname, "INDEX%d(1)", iwcs + 1 ); + astMapPut1D( *table, cellname, nlut, work1, NULL ); + } + } + +/* Free resources */ + work1 = astFree( work1 ); + work2 = astFree( work2 ); + } + +/* If columns were added to the table, invert the returned Mapping again + so that the input is wcs coord and the output is grid coord. Otherwise, + annul the returned Mapping. */ + if( ok ) { + astInvert( ret ); + } else { + ret = astAnnul( ret ); + } + +/* Loop to annul all the Mapping pointers in the list. */ + for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] ); + +/* Free the dynamic arrays. */ + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + } + +/* Free resources. */ + ins = astFree( ins ); + +/* If an error occurred, free the returned Mapping. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result. */ + return ret; +} + +static AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit, + AstFrame *wcsfrm, double *dim, int iax1, + int iax2, int iwcs1, int iwcs2, + AstFitsTable **table, int *icolmain1, + int *icolmain2, int *icolindex1, + int *icolindex2, int *max1, int *max2, + int *interp1, int *interp2, int *status ){ +/* +* Name: +* IsMapTab2D + +* Purpose: +* See if a specified pair of Mapping outputs are related to a pair of +* Mapping inputs via a FITS -TAB algorithm. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit, +* AstFrame *wcsfrm, double *dim, int iax1, +* int iax2, int iwcs1, int iwcs2, +* AstFitsTable **table, int *icolmain1, +* int *icolmain2, int *icolindex1, +* int *icolindex2, int *max1, int *max2, +* int *interp1, int *interp2, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A specified pair of outputs axes of the supplied Mapping are tested +* to see if they can be represented by the -TAB alogirithm described in +* FITS-WCS paper III. If the test is passed, a Mapping is returned from +* the specified WCS axes to the corresponding psi axes. A FitsTable is +* also created holding the information to be stored in the corresponding +* FITS binary table. Note, when creating a header, AST assumes a unit +* transformaton between psi axes and grid axes (psi axes are defined +* in FITS-WCS paper III section 6.1.2). + +* Parameters: +* map +* Pointer to the Mapping from pixel coords to WCS coords. +* scale +* A scale factor by which to multiply the axis values stored in the +* returned FitsTable. Note, the returned Mapping is unaffected by +* this scaling factor. +* unit +* A unit string for the axis values. If supplied, the same +* string is stored for both axes. If NULL, the unit strings are +* extracted from the relavent axes of the supplied WCS Frame. +* wcsfrm +* Pointer to a Frame describing WCS coords. +* dim +* An array holding the array dimensions in pixels. AST__BAD should +* be supplied for any unknown dimensions. +* iax1 +* The zero-based index of the first Mapping output which is to be +* checked. +* iax2 +* The zero-based index of the second Mapping output which is to be +* checked. +* iwcs1 +* The zero-based index of the FITS WCS axis corresponding to "iax1". +* iwcs2 +* The zero-based index of the FITS WCS axis corresponding to "iax2". +* table +* Pointer to a location holding a pointer to the FitsTable describing +* the -TAB look-up table. If "*table" is NULL on entry, a new +* FitsTable will be created and returned, otherwise the supplied +* FitsTable is used. +* icolmain1 +* The one-based index of the column within "*table" that holds the +* main coord array for the first Mapping output. +* icolmain2 +* The one-based index of the column within "*table" that holds the +* main coord array for the second Mapping output. +* icolindex1 +* The one-based index of the column within "*table" that holds the +* index vector for the first Mapping output. Returned equal to -1 +* if no index is added to the table (e.g. because the index is a +* unit index). +* icolindex2 +* The one-based index of the column within "*table" that holds the +* index vector for the second Mapping output. Returned equal to -1 +* if no index is added to the table (e.g. because the index is a +* unit index). +* max1 +* The one-based index of the dimension describing the first Mapping +* output within the main coord array specified by "icolmain1". +* max2 +* The one-based index of the dimension describing the second Mapping +* output within the main coord array specified by "icolmain1". +* interp1 +* The interpolation method (0=linear, other=nearest neighbour) for +* the first mapping output. +* interp2 +* The interpolation method (0=linear, other=nearest neighbour) for +* the second mapping output. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the specified "map" outputs can be described using the -TAB +* algorithm of FITS-WCS paper III, then a 2-input/2-output Mapping +* from the specified WCS axes to the corresponding psi axes (i.e. +* grid axes) is returned. NULL is returned otherwise, of if an error +* occurs. +*/ + +/* Local Variables: */ + AstMapping *ret1; /* WCS->IWC Mapping for first output */ + AstMapping *ret2; /* WCS->IWC Mapping for second output */ + AstMapping *ret; /* Returned WCS axis Mapping */ + AstMapping *tmap; + AstPermMap *pm; + int *pix_axes; /* Zero-based indices of corresponding pixel axes */ + int wcs_axes[ 2 ]; /* Zero-based indices of selected WCS axes */ + int inperm[ 1 ]; + int outperm[ 2 ]; + +/* Initialise */ + ret = NULL; + +/* Check inherited status */ + if( !astOK ) return ret; + +/* First see if the two required Mapping outputs are separable, in which case + they can be described by two 1D tables. */ + ret1 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax1, iwcs1, table, icolmain1, + icolindex1, interp1, status ); + ret2 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax2, iwcs2, table, icolmain2, + icolindex2, interp2, status ); + +/* If both outputs are seperable... */ + if( ret1 && ret2 ) { + +/* Both axes are stored as the first dimension in the corresponding main + coords array. */ + *max1 = 1; + *max2 = 1; + +/* Get a Mapping from the required pair of WCS axes to the corresponding + pair of grid axes. First try to split the supplied grid->wcs mapping. */ + wcs_axes[ 0 ] = iax1; + wcs_axes[ 1 ] = iax2; + + astInvert( map ); + pix_axes = astMapSplit( map, 2, wcs_axes, &ret ); + astInvert( map ); + + if( pix_axes ) { + pix_axes = astFree( pix_axes ); + if( astGetNout( ret ) > 2 ) { + ret = astAnnul( ret ); + +/* If the two output WCS axes are fed by the same grid axis, we need to + add another pixel axis to form the pair. */ + } else if( astGetNout( ret ) == 1 ) { + inperm[ 0 ] = 0; + outperm[ 0 ] = 0; + outperm[ 1 ] = 0; + pm = astPermMap( 1, inperm, 2, outperm, NULL, " ", status ); + tmap = (AstMapping *) astCmpMap( ret, pm, 1, " ", status ); + ret = astAnnul( ret ); + pm = astAnnul( pm ); + ret = tmap; + } + } + +/* If this was unsuccessful, combine the Mappings returned by IsMapTab1D. + We only do this if the above astMapSplit call failed, since the IsMapTab1D + mappings may well not be independent of each other, and we may end up + sticking together in parallel two mappings that are basically the same + except for ending with PermMapa that select different axes. Is is hard + then to simplify such a parallel CmpMap back into the simpler form + that uses only one of the two identical mappings, without a PermMap. */ + if( !ret ) { + ret = (AstMapping *) astCmpMap( ret1, ret2, 0, " ", status ); + } + +/* Free resources. */ + ret1 = astAnnul( ret1 ); + ret2 = astAnnul( ret2 ); + +/* If only one output is separable, remove the corresponding columns from + the returned table. */ + } else if( ret1 ) { + ret1 = astAnnul( ret1 ); + astRemoveColumn( *table, astColumnName( *table, *icolmain1 ) ); + if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex1 ) ); + } else if( ret2 ) { + ret2 = astAnnul( ret2 ); + astRemoveColumn( *table, astColumnName( *table, *icolmain2 ) ); + if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex2 ) ); + } + +/* If the required Mapping outputs were not separable, create a single + 2D coords array describing both outputs. */ + if( !ret ) { + +/* TO BE DONE... Until then non-separable Mappings will result in a + failure to create a -TAB header. No point in doing this until AST has + an N-dimensional LutMap class (otherwise AST could never read the + resulting FITS header). */ + } + +/* If an error occurred, free the returned Mapping. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result. */ + return ret; +} + +static int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status ){ +/* +* Name: +* IsAIPSSpectral + +* Purpose: +* See if a given CTYPE value describes a FITS-AIPS spectral axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The given CTYPE value is checked to see if it conforms to the +* requirements of a spectral axis CTYPE value as specified by +* FITS-AIPS encoding. If so, the equivalent FITS-WCS CTYPE and +* SPECSYS values are returned. + +* Parameters: +* ctype +* Pointer to a null terminated string holding the CTYPE value to +* check. +* wctype +* The address of a location at which to return a pointer to a +* static string holding the corresponding FITS-WCS CTYPE value. A +* NULL pointer is returned if the supplied CTYPE string is not an +* AIPS spectral CTYPE value. +* wspecsys +* The address of a location at which to return a pointer to a +* static string holding the corresponding FITS-WCS SPECSYS value. A +* NULL pointer is returned if the supplied CTYPE string is not an +* AIPS spectral CTYPE value. +* status +* Pointer to the inherited status variable. + +* Retuned Value: +* Non-zero fi the supplied CTYPE was an AIPS spectral CTYPE value. + +* Note: +* - These translations are also used by the FITS-CLASS encoding. +*/ + +/* Local Variables: */ + int ret; + +/* Initialise */ + ret = 0; + *wctype = NULL; + *wspecsys = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the length of the string is not 8, then it is not an AIPS spectral axis. */ + if( strlen( ctype ) == 8 ) { + +/* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents. + These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!) + or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!) + or "GEO". */ + if( !strncmp( ctype, "FREQ", 4 ) ){ + *wctype = "FREQ "; + } else if( !strncmp( ctype, "VELO", 4 ) ){ + *wctype = "VRAD "; + } else if( !strncmp( ctype, "FELO", 4 ) ){ + *wctype = "VOPT-F2W"; + } else if( !strncmp( ctype, "WAVELENG", 8 ) ){ + *wctype = "WAVE "; + } + if( !strcmp( ctype + 4, "-LSR" ) ){ + *wspecsys = "LSRK"; + } else if( !strcmp( ctype + 4, "LSRK" ) ){ + *wspecsys = "LSRK"; + } else if( !strcmp( ctype + 4, "-LSRK" ) ){ + *wspecsys = "LSRK"; + } else if( !strcmp( ctype + 4, "-LSD" ) ){ + *wspecsys = "LSRD"; + } else if( !strcmp( ctype + 4, "-HEL" ) ){ + *wspecsys = "BARYCENT"; + } else if( !strcmp( ctype + 4, "-EAR" ) || !strcmp( ctype + 4, "-GEO" ) ){ + *wspecsys = "GEOCENTR"; + } else if( !strcmp( ctype + 4, "-OBS" ) || !strcmp( ctype + 4, "-TOP" ) ){ + *wspecsys = "TOPOCENT"; + } + if( *wctype && *wspecsys ) { + ret = 1; + } else { + *wctype = NULL; + *wspecsys = NULL; + } + } + +/* Return the result. */ + return ret; +} + +static int IsSkyOff( AstFrameSet *fset, int iframe, int *status ){ +/* +* Name: +* IsSkyOff + +* Purpose: +* See if a given Frame contains an offset SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int IsSkyOff( AstFrameSet *fset, int iframe, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns a flag indicating if the specified Frame within the +* supplied FrameSet is, or contains, a SkyFrame that represents +* offset coordinates. This is the case if the Frame is a SkyFrame +* and its SkyRefIs attribute is "Pole" or "Origin", or is a CmpFrame +* containing such a SkyFrame. + +* Parameters: +* fset +* The FrameSet. +* iframe +* Index of the Frame to check within "fset" +* status +* Pointer to the inherited status variable. + +* Retuned Value: +* +1 if the Frame is an offset SkyFrame. Zero otherwise. + +* Notes: +* - Zero is returned if an error has already occurred. +*/ + +/* Local Variables: */ + AstFrame *frm; + const char *skyrefis; + int oldrep; + int result; + +/* Initialise. */ + result = 0; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the required Frame in the FrameSet */ + frm = astGetFrame( fset, iframe ); + +/* Since the current Frame may not contain a SkyFrame, we temporarily + switch off error reporting. */ + oldrep = astReporting( 0 ); + +/* Get the SkyRefIs attribute value. */ + skyrefis = astGetC( frm, "SkyRefIs" ); + +/* If it is "Pole" or "Origin", return 1. */ + if( skyrefis && ( !Ustrcmp( skyrefis, "POLE", status ) || + !Ustrcmp( skyrefis, "ORIGIN", status ) ) ) result = 1; + +/* Cancel any error and switch error reporting back on again. */ + astClearStatus; + astReporting( oldrep ); + +/* Annul the Frame pointer. */ + frm = astAnnul( frm ); + +/* Return the result. */ + return result; +} + +static const char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status ) { +/* +* Name: +* IsSpectral + +* Purpose: +* See if a given FITS-WCS CTYPE value describes a spectral axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The given CTYPE value is checked to see if it conforms to the +* requirements of a spectral axis CTYPE value as specified by +* FITS-WCS paper 3. If so, the spectral system and algorithm codes +* are extracted from it and returned, together with the default units +* for the spectral system. + +* Parameters: +* ctype +* Pointer to a null terminated string holding the CTYPE value to +* check. +* stype +* An array in which to return the null-terminated spectral system type +* (e.g. "FREQ", "VELO", "WAVE", etc). A null string is returned if +* the CTYPE value does not describe a spectral axis. +* algcode +* An array in which to return the null-terminated algorithm code +* (e.g. "-LOG", "", "-F2W", etc). A null string is returned if the +* spectral axis is linear. A null string is returned if the CTYPE +* value does not describe a spectral axis. +* status +* Pointer to the inherited status variable. + +* Retuned Value: +* A point to a static string holding the default units associated +* with the spectral system specified by the supplied CTYPE value. +* NULL is returned if the CTYPE value does not describe a spectral +* axis. + +* Notes: +* - The axis is considered to be a spectral axis if the first 4 +* characters form one of the spectral system codes listed in FITS-WCS +* paper 3. The algorithm code is not checked, except to ensure that +* it begins with a minus sign, or is blank. +* - A NULL pointer is returned if an error has already occurred. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + int ctype_len; + +/* Initialise */ + stype[ 0 ] = 0; + algcode[ 0 ] = 0; + +/* Check the inherited status. */ + if( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Initialise more stuff */ + isspectral_ret = NULL; + +/* If the length of the string is less than 4, then it is not a spectral + axis. */ + ctype_len = strlen( ctype ); + if( ctype_len >= 4 ) { + +/* Copy the first 4 characters (the coordinate system described by the + axis) into a null-terminated buffer. */ + strncpy( stype, ctype, 4 ); + stype[ 4 ] = 0; + stype[ astChrLen( stype ) ] = 0; + +/* Copy any remaining characters (the algorithm code) into a null-terminated + buffer. Only copy a maximum of 4 characters. */ + if( ctype_len > 4 ) { + if( ctype_len <= 8 ) { + strcpy( algcode, ctype + 4 ); + } else { + strncpy( algcode, ctype + 4, 4 ); + algcode[ 4 ] = 0; + } + algcode[ astChrLen( algcode ) ] = 0; + } else { + algcode[ 0 ] = 0; + } + +/* See if the first 4 characters of the CTYPE value form one of the legal + spectral coordinate type codes listed in FITS-WCS Paper III. Also note + the default units associated with the system. */ + if( !strcmp( stype, "FREQ" ) ) { + isspectral_ret = "Hz"; + } else if( !strcmp( stype, "ENER" ) ) { + isspectral_ret = "J"; + } else if( !strcmp( stype, "WAVN" ) ) { + isspectral_ret = "/m"; + } else if( !strcmp( stype, "VRAD" ) ) { + isspectral_ret = "m/s"; + } else if( !strcmp( stype, "WAVE" ) ) { + isspectral_ret = "m"; + } else if( !strcmp( stype, "VOPT" ) ) { + isspectral_ret = "m/s"; + } else if( !strcmp( stype, "ZOPT" ) ) { + isspectral_ret = ""; + } else if( !strcmp( stype, "AWAV" ) ) { + isspectral_ret = "m"; + } else if( !strcmp( stype, "VELO" ) ) { + isspectral_ret = "m/s"; + } else if( !strcmp( stype, "BETA" ) ) { + isspectral_ret = ""; + } + +/* Also check that the remaining part of CTYPE (the algorithm code) begins + with a minus sign or is blank. */ + if( algcode[ 0 ] != '-' && strlen( algcode ) > 0 ) isspectral_ret = NULL; + } + +/* Return null strings if the axis is not a spectral axis. */ + if( ! isspectral_ret ) { + stype[ 0 ] = 0; + algcode[ 0 ] = 0; + } + +/* Return the result. */ + return isspectral_ret; +} + +static AstMapping *LinearWcs( FitsStore *store, int i, char s, + const char *method, const char *class, int *status ) { +/* +* Name: +* LinearWcs + +* Purpose: +* Create a Mapping describing a FITS-WCS linear algorithm + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *LinearWcs( FitsStore *store, int i, char s, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function uses the contents of the supplied FitsStore to create +* a Mapping which goes from Intermediate World Coordinate (known as "w" +* in the context of FITS-WCS paper III) to a linearly related axis. +* +* The returned Mapping is a ShiftMap which simply adds on the value of +* CRVALi. + +* Parameters: +* store +* Pointer to the FitsStore structure holding the values to use for +* the WCS keywords. +* i +* The zero-based index of the spectral axis within the FITS header +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a Mapping, or NULL if an error occurs. +*/ + +/* Local Variables: */ + AstMapping *ret; + double crv; + +/* Check the global status. */ + ret = NULL; + if( !astOK ) return ret; + +/* Get the CRVAL value for the specified axis. */ + crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( crv == AST__BAD ) crv = 0.0; + +/* Create a 1D ShiftMap which adds this value onto the IWCS value. */ + if( crv != 0.0 ) { + ret = (AstMapping *) astShiftMap( 1, &crv, "", status ); + } else { + ret = (AstMapping *) astUnitMap( 1, "", status ); + } + return ret; +} + +static AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p, + double *ubnd_p, double crval, int *status ){ +/* +* Name: +* LogAxes + +* Purpose: +* Test a Frame axis to see if it logarithmically spaced in pixel coords. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p, +* double *ubnd_p, double crval ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A specified axis of the supplied Mappinhg is tested to see if it +* corresponds to the form +* +* S = Sr.exp( w/Sr ) +* +* where "w" is one of the Mapping inputs, "S" is the specified +* Mapping output, and "Sr" is the supplied value of "crval". This +* is the form for a FITS log axis as defined in FITS-WCS paper III. +* +* If the above test is passed, a Mapping is returned from "S" to "w" +* (the inverseof the above expression). + +* Parameters: +* map +* Pointer to the Mapping. This will usually be a Mapping from +* pixel coords to WCS coords. +* iax +* The index of the output of "map" which correspoinds to "S". +* nwcs +* The number of outputs from "map". +* lbnd_p +* Pointer to an array of double, with one element for each +* Mapping input coordinate. This should contain the lower bound +* of the input pixel box in each input dimension. +* ubnd_p +* Pointer to an array of double, with one element for each +* Mapping input coordinate. This should contain the upper bound +* of the input pixel box in each input dimension. +* crval +* The reference value ("Sr") to use. Must not be zero. + +* Returned Value: +* If the specified axis is logarithmically spaced, a Mapping with +* "nwcs" inputs and "nwcs" outputs is returned. This Mapping transforms + +* its "iax"th input using the transformation: +* +* w = Sr.Log( S/Sr ) +* +* (where "S" is the Mapping is the "iax"th input and "w" is the +* "iax"th output). Other inputs are copied to the corresponding +* output without change. NULL is returned if the specified axis is +* not logarithmically spaced. +*/ + +/* Local Variables: */ + AstMapping *result; /* Returned Mapping */ + AstMapping *tmap0; /* A temporary Mapping */ + AstMapping *tmap1; /* A temporary Mapping */ + AstMapping *tmap2; /* A temporary Mapping */ + AstMapping *tmap3; /* A temporary Mapping */ + AstMapping *tmap4; /* A temporary Mapping */ + const char *fexps[ 1 ]; /* Forward MathMap expressions */ + const char *iexps[ 1 ]; /* Inverse MathMap expressions */ + +/* Initialise */ + result = NULL; + +/* Check the inherited status and crval value. */ + if( !astOK || crval == 0.0 ) return result; + +/* If the "log" algorithm is appropriate, the supplied axis (s) is related + to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this is the case, + then the log of s will be linearly related to pixel coordinates. To test + this, we create a CmpMap which produces log(s). */ + fexps[ 0 ] = "logs=log(s)"; + iexps[ 0 ] = "s=exp(logs)"; + tmap1 = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps, + "simpfi=1,simpif=1", status ); + tmap2 = AddUnitMaps( tmap1, iax, nwcs, status ); + tmap0 = (AstMapping *) astCmpMap( map, tmap2, 1, "", status ); + tmap2 = astAnnul( tmap2 ); + +/* See if this Mapping is linear. */ + if( IsMapLinear( tmap0, lbnd_p, ubnd_p, iax, status ) ) { + +/* Create the Mapping which defines the IWC axis. This is the Mapping from + WCS to IWCS - "W = Sr.log( S/Sr )". Other axes are left unchanged by the + Mapping. The IWC axis has the same axis index as the WCS axis. */ + tmap2 = (AstMapping *) astZoomMap( 1, 1.0/crval, "", status ); + tmap3 = (AstMapping *) astCmpMap( tmap2, tmap1, 1, "", status ); + tmap2 = astAnnul( tmap2 ); + tmap2 = (AstMapping *) astZoomMap( 1, crval, "", status ); + tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 1, "", status ); + tmap3 = astAnnul( tmap3 ); + tmap2 = astAnnul( tmap2 ); + result = AddUnitMaps( tmap4, iax, nwcs, status ); + tmap4 = astAnnul( tmap4 ); + } + +/* Free resources. */ + tmap0 = astAnnul( tmap0 ); + tmap1 = astAnnul( tmap1 ); + +/* Return the result. */ + return result; +} + +static AstMapping *LogWcs( FitsStore *store, int i, char s, + const char *method, const char *class, int *status ) { +/* +* Name: +* LogWcs + +* Purpose: +* Create a Mapping describing a FITS-WCS logarithmic algorithm + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *LogWcs( FitsStore *store, int i, char s, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function uses the contents of the supplied FitsStore to create +* a Mapping which goes from Intermediate World Coordinate (known as "w" +* in the context of FITS-WCS paper III) to a logarthmic version of w + +* called "S" given by: +* +* S = Sr.exp( w/Sr ) +* +* where Sr is the value of S corresponding to w=0. + +* Parameters: +* store +* Pointer to the FitsStore structure holding the values to use for +* the WCS keywords. +* i +* The zero-based index of the axis within the FITS header +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a Mapping, or NULL if an error occurs. +*/ + +/* Local Variables: */ + AstMapping *ret; + char forexp[ 12 + DBL_DIG*2 ]; + char invexp[ 12 + DBL_DIG*2 ]; + const char *fexps[ 1 ]; + const char *iexps[ 1 ]; + double crv; + +/* Check the global status. */ + ret = NULL; + if( !astOK ) return ret; + +/* Get the CRVAL value for the specified axis. Use a default of zero. */ + crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( crv == AST__BAD ) crv = 0.0; + +/* Create the MathMap, if possible. */ + if( crv != 0.0 ) { + sprintf( forexp, "s=%.*g*exp(w/%.*g)", DBL_DIG, crv, DBL_DIG, crv ); + sprintf( invexp, "w=%.*g*log(s/%.*g)", DBL_DIG, crv, DBL_DIG, crv ); + fexps[ 0 ] = forexp; + iexps[ 0 ] = invexp; + ret = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps, "simpfi=1,simpif=1", status ); + } + +/* Return the result */ + return ret; +} + +static int LooksLikeClass( AstFitsChan *this, const char *method, + const char *class, int *status ){ + +/* +* Name: +* LooksLikeClass + +* Purpose: +* Does the FitsChan seem to use FITS-CLASS encoding? + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int LooksLikeClass( AstFitsChan *this, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns non-zero if the supplied FitsChan probably uses FITS-CLASS +* encoding. This is the case if it contains a DELTAV keyword and a +* keyword of the form VELO-xxx", where xxx is one of the accepted +* standards of rest, or "VLSR". + +* Parameters: +* this +* Pointer to the FitsChan. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the encoding in use lookslike FITS-CLASS. +*/ + +/* Local Variables... */ + int ret; /* Returned value */ + +/* Initialise */ + ret = 0; + +/* Check the global status. */ + if( !astOK ) return ret; + +/* See if there is a "DELTAV" card, and a "VELO-xxx" or "VLSR" card. */ + if( astKeyFields( this, "DELTAV", 0, NULL, NULL ) && ( + astKeyFields( this, "VLSR", 0, NULL, NULL ) || + astKeyFields( this, "VELO-OBS", 0, NULL, NULL ) || + astKeyFields( this, "VELO-HEL", 0, NULL, NULL ) || + astKeyFields( this, "VELO-EAR", 0, NULL, NULL ) || + astKeyFields( this, "VELO-LSR", 0, NULL, NULL ) ) ) { + ret = 1; + } + +/* Return the result. */ + return ret; +} + +static void MakeBanner( const char *prefix, const char *middle, + const char *suffix, + char banner[ AST__FITSCHAN_FITSCARDLEN - + FITSNAMLEN + 1 ], int *status ) { +/* +* Name: +* MakeBanner + +* Purpose: +* Create a string containing a banner comment. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void MakeBanner( const char *prefix, const char *middle, +* const char *suffix, +* char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function creates a string which can be written as a FITS +* comment card to produce a banner heading (or tail) for an AST +* Object when it is written to a FitsChan. The banner will occupy +* the maximum permitted width for text in a FITS comment card. + +* Parameters: +* prefix +* A pointer to a constant null-terminated string containing the +* first part of the text to appear in the banner. +* middle +* A pointer to a constant null-terminated string containing the +* second part of the text to appear in the banner. +* suffix +* A pointer to a constant null-terminated string containing the +* third part of the text to appear in the banner. +* banner +* A character array to receive the null-terminated result string. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The text to appear in the banner is constructed by +* concatenating the three input strings supplied. +*/ + +/* Local Variables: */ + char token[] = "AST"; /* Identifying token */ + int i; /* Loop counter for input characters */ + int len; /* Number of output characters */ + int ltok; /* Length of token string */ + int mxlen; /* Maximum permitted output characters */ + int start; /* Column number where text starts */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Calculate the maximum number of characters that the output banner + can hold and the length of the token string. */ + mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN; + ltok = (int) strlen( token ); + +/* Calculate the column in which to start the text, so that it is + centred in the banner (with 4 non-text characters on each side). */ + start = ltok + 2 + ( mxlen - ltok - 1 - + (int) ( strlen( prefix ) + + strlen( middle ) + + strlen( suffix ) ) - 1 - ltok ) / 2; + if ( start < ltok + 2 ) start = ltok + 2; + +/* Start building the banner with the token string. */ + len = 0; + for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) { + banner[ len++ ] = token[ i ]; + } + +/* Then pad with spaces up to the start of the text. */ + while ( len < start - 1 ) banner[ len++ ] = ' '; + +/* Insert the prefix data, truncating it if it is too long. */ + for ( i = 0; prefix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) { + banner[ len++ ] = prefix[ i ]; + } + +/* Insert the middle data, truncating it if it is too long. */ + for ( i = 0; middle[ i ] && ( len < mxlen - ltok - 1 ); i++ ) { + banner[ len++ ] = middle[ i ]; + } + +/* Insert the suffix data, truncating it if it is too long. */ + for ( i = 0; suffix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) { + banner[ len++ ] = suffix[ i ]; + } + +/* Pad the end of the text with spaces. */ + while ( len < mxlen - ltok ) banner[ len++ ] = ' '; + +/* Finish the banner with the token string. */ + for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) { + banner[ len++ ] = token[ i ]; + } + +/* Terminate the output string. */ + banner[ len ] = '\0'; +} + +static AstMapping *MakeColumnMap( AstFitsTable *table, const char *col, + int isindex, int interp, const char *method, + const char *class, int *status ){ +/* +* Name: +* MakeColumnMap + +* Purpose: +* Create a Mapping describing a look-up table supplied in a cell of a +* FITS binary table. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *MakeColumnMap( AstFitsTable *table, const char *col, +* int isindex, int interp, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns a Mapping representing the array of values +* stored in row 1 of a named column of a supplied FitsTable. The +* array of values is treated as a look-up table following the prescription +* of FITS-WCS paper III (the "-TAB" algorithm). If the array has (N+1) +* dimensions (where N is one or more), the returned Mapping has N +* inputs and N outputs. The inputs correspond to FITS GRID coords +* within the array. FITS-WCS paper III requires that the first dimension +* in the array has a length of "N" and contains the N output values +* at each input values. + +* Parameters: +* table +* Pointer to the Fitstable. +* col +* A string holding the name of the column to use. +* isindex +* Non-zero if the column hold an index array, zero if it holds a +* coordinate array. +* interp +* The value to use for the Interp attribute of the LutMap. A value +* of zero tells the LutMap class to use linear interpolation. Other +* values tell the LutMap class to use nearest neighbour interpolation. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping, or NULL if an error occurs. +*/ + +/* Local Variables: */ + AstMapping *result; + char *key; + double *lut; + int *dims; + int ndim; + int nel; + +/* Initialise */ + result = NULL; + +/* Check the inherited status */ + if( !astOK ) return result; + +/* Get the number of dimensions spanned by the value in the named column. */ + ndim = astGetColumnNdim( table, col ); + +/* First deal with index vectors. */ + if( isindex ) { + +/* FITS-WCS paper II mandates that index arrays must be 1-dimensional. */ + if( ndim != 1 && astOK ) { + astError( AST__BADTAB, "%s(%s): Column '%s' has %d dimensions but it " + "holds an index vector and should therefore be 1-dimensional.", + status, method, class, col, ndim ); + } + +/* Get the length of the index vector. */ + nel = astGetColumnLength( table, col ); + +/* Allocate memory to hold the array values, and to hold the cell key. */ + lut = astMalloc( nel*sizeof( double ) ); + key = astMalloc( strlen( col ) + 5 ); + if( astOK ) { + +/* Create the key for the table cell holding the required array. FITS-WCS + paper III mandates that tables always occur in the first row of the + table (and that the table only has one row). Ignore trailing spaces in + the column name. */ + sprintf( key, "%.*s(1)", (int) astChrLen( col ), col ); + +/* Copy the array values into the above memory. */ + if( astMapGet1D( table, key, nel, &nel, lut ) ) { + +/* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input + corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry). + Ensure the LutMap uses linear interpolation. */ + result = (AstMapping *) astLutMap( nel, lut, 1.0, 1.0, + "LutInterp=%d", status, interp ); + +/* Report an error if the table cell was empty. */ + } else if( astOK ) { + astError( AST__BADTAB, "%s(%s): Row 1 of the binary table " + "contains no value for column '%s'.", status, method, + class, col ); + } + } + +/* Free memory. */ + lut = astFree( lut ); + key = astFree( key ); + +/* Now deal with coordinate arrays. */ + } else { + +/* Get the shape of the array. */ + dims = astMalloc( sizeof( int )*ndim ); + astColumnShape( table, col, ndim, &ndim, dims ); + +/* For coordinate arrays, check the length of the first axis is "ndim-1", as + required by FITS-WCS paper III. */ + if( astOK && dims[ 0 ] != ndim - 1 && !isindex ) { + astError( AST__BADTAB, "%s(%s): The first dimension of the coordinate " + "array has length %d (should be %d since the array has %d " + "dimensions).", status, method, class, dims[ 0 ], ndim - 1, + ndim ); + } + +/* We can currently only handle 1D look-up tables. These are stored in + notionally two-dimensional arrays in which the first dimension is + degenarate (i.e. spans only a single element). */ + if( ndim > 2 ) { + if( astOK ) astError( AST__INTER, "%s(%s): AST can currently only " + "handle 1-dimensional coordinate look-up tables " + "(the supplied table has %d dimensions).", status, + method, class, ndim - 1 ); + +/* Handle 1-dimensional look-up tables. */ + } else if( astOK ){ + +/* Allocate memory to hold the array values, and to hold the cell key. */ + lut = astMalloc( dims[ 1 ]*sizeof( double ) ); + key = astMalloc( strlen( col ) + 5 ); + if( astOK ) { + +/* Create the key for the table cell holding the required array. FITS-WCS + paper III mandates that tables always occur in the first row of the + table (and that the table only has one row). Ignore trailing spaces in + the column name. */ + sprintf( key, "%.*s(1)", (int) astChrLen( col ), col ); + +/* Copy the array values into the above memory. */ + if( astMapGet1D( table, key, dims[ 1 ], dims, lut ) ) { + +/* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input + corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry). + Ensure the LutMap uses linear interpolation. */ + result = (AstMapping *) astLutMap( dims[ 1 ], lut, 1.0, 1.0, + "LutInterp=%d", status, + interp ); + +/* Report an error if the table cell was empty. */ + } else if( astOK ) { + astError( AST__BADTAB, "%s(%s): Row 1 of the binary table " + "contains no value for column '%s'.", status, method, + class, col ); + } + } + +/* Free memory. */ + lut = astFree( lut ); + key = astFree( key ); + } + dims = astFree( dims ); + } + +/* Issue a context message and annul the returned Mapping if an error + has occurred. */ + if( !astOK ) { + astError( astStatus, "%s(%s): Cannot read a look-up table for a " + "tabular WCS axis from column '%s' of a FITS binary table.", + status, method, class, col ); + result = astAnnul( result ); + } + +/* Return the result. */ + return result; +} + +static AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset, + int ipix, int iwcs, int encoding, + const char *method, const char *class, + int *status ) { +/* +* Name: +* MakeFitsFrameSet + +* Purpose: +* Create a FrameSet which conforms to the requirements of the FITS-WCS +* papers. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset, +* int ipix, int iwcs, int encoding, +* const char *method, const char *class, +* int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function constructs a new FrameSet holding the pixel and WCS +* Frames from the supplied FrameSet, but optionally extends the WCS +* Frame to include any extra axes needed to conform to the FITS model. + +* Currently, this function does the following: +* +* - if the WCS Frame contains a 1D spectral Frame with a defined celestial +* reference position (SpecFrame attributes RefRA and RefDec), then +* it ensures that the WCS Frame also contains a pair of celestial +* axes (such axes are added if they do not already exist within the +* supplied WCS Frame). The pixel->WCS Mapping is adjusted accordingly. +* +* - if the WCS Frame contains a spectral axis and a pair of celestial +* axes, then the SpecFrame attributes RefRA and RefDec are set to the +* reference position defined by the celestial axes. The pixel->WCS +* Mapping is adjusted accordingly. +* +* - NULL is returned if the WCS Frame contains more than one spectral +* axis. +* +* - NULL is returned if the WCS Frame contains more than one pair of +* celestial axes. +* +* - Any isolated sky axes (i.e. not contained within a SkyFrame) are +* re-mapped from radians into degrees. + +* Parameters: +* this +* The FitsChan. +* fset +* The FrameSet to check. +* ipix +* The index of the FITS pixel Frame within "fset". +* iwcs +* The index of the WCS Frame within "fset". +* encoding +* The encoding in use. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new FrameSet which confoms to the requirements of the FITS-WCS +* papers. The base Frame in this FrameSet will be the FITS pixel +* Frame, and the current Frame will be the WCS Frame. NULL is +* returned if an error has already occurred, or if the FrameSet cannot +* be produced for any reason. +*/ + +/* Local Variables: */ + AstFitsChan *fc; /* Pointer to temporary FitsChan */ + AstFrame *pframe; /* Pointer to the primary Frame */ + AstFrame *pixfrm; /* Pointer to the FITS pixel Frame */ + AstFrame *tfrm0; /* Pointer to a temporary Frame */ + AstFrame *tfrm; /* Pointer to a temporary Frame */ + AstFrame *wcsfrm; /* Pointer to the FITS WCS Frame */ + AstFrameSet *ret; /* The returned FrameSet */ + AstFrameSet *tfs; /* Pointer to a temporary FrameSet */ + AstMapping *map1; /* Pointer to pre-WcsMap Mapping */ + AstMapping *map3; /* Pointer to post-WcsMap Mapping */ + AstMapping *map; /* Pointer to the pixel->wcs Mapping */ + AstMapping *remap; /* Total Mapping from internal to external units */ + AstMapping *smap; /* Simplified Mapping */ + AstMapping *tmap0; /* Pointer to a temporary Mapping */ + AstMapping *tmap1; /* Pointer to a temporary Mapping */ + AstMapping *tmap2; /* Pointer to a temporary Mapping */ + AstMapping *tmap; /* Pointer to a temporary Mapping */ + AstMapping *umap; /* 1D Mapping from internal to external units */ + AstSpecFrame *skyfrm; /* Pointer to the SkyFrame within WCS Frame */ + AstSpecFrame *specfrm; /* Pointer to the SpecFrame within WCS Frame */ + AstWcsMap *map2; /* Pointer to WcsMap */ + char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* A FITS header card */ + char equinox_attr[ 13 ];/* Name of Equinox attribute for sky axes */ + char system_attr[ 12 ]; /* Name of System attribute for sky axes */ + const char *eqn; /* Pointer to original sky Equinox value */ + const char *extunit; /* External units string */ + const char *intunit; /* Internal units string */ + const char *skysys; /* Pointer to original sky System value */ + double con; /* Constant axis value */ + double reflat; /* Celestial latitude at reference point */ + double reflon; /* Celestial longitude at reference point */ + int *perm; /* Pointer to axis permutation array */ + int iax; /* Axis inex */ + int icurr; /* Index of original current Frame in returned FrameSet */ + int ilat; /* Celestial latitude index within WCS Frame */ + int ilon; /* Celestial longitude index within WCS Frame */ + int npix; /* Number of pixel axes */ + int nwcs; /* Number of WCS axes */ + int ok; /* Is the supplied FrameSet usable? */ + int paxis; /* Axis index within the primary Frame */ + int rep; /* Was error reporting switched on? */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Get copies of the pixel Frame, the WCS Frame and the Mapping. */ + tfrm = astGetFrame( fset, ipix ); + pixfrm = astCopy( tfrm ); + tfrm = astAnnul( tfrm ); + tfrm = astGetFrame( fset, iwcs ); + wcsfrm = astCopy( tfrm ); + tfrm = astAnnul( tfrm ); + tmap = astGetMapping( fset, ipix, iwcs ); + map = astCopy( tmap ); + tmap = astAnnul( tmap ); + +/* Store the number of pixel and WCS axes. */ + npix = astGetNaxes( pixfrm ); + nwcs = astGetNaxes( wcsfrm ); + +/* Search the WCS Frame for SkyFrames and SpecFrames. */ + umap = NULL; + remap = NULL; + specfrm = NULL; + skyfrm = NULL; + ok = 1; + ilat = -1; + ilon = -1; + for( iax = 0; iax < nwcs; iax++ ) { + +/* Obtain a pointer to the primary Frame containing the current WCS axis. */ + astPrimaryFrame( wcsfrm, iax, &pframe, &paxis ); + +/* If the current axis is a SpecFrame, save a pointer to it. If we have already + found a SpecFrame, abort. */ + if( astIsASpecFrame( pframe ) ) { + if( specfrm ) { + ok = 0; + break; + } + specfrm = astClone( pframe ); + +/* If the current axis is a SkyFrame, save a pointer to it, and its WCS + index. If we have already found a different SkyFrame, abort. */ + } else if( IsASkyFrame( pframe ) ) { + if( skyfrm ) { + if( pframe != (AstFrame *) skyfrm ) { + ok = 0; + break; + } + } else { + skyfrm = astClone( pframe ); + } + if( paxis == 0 ) { + ilon = iax; + } else { + ilat = iax; + } + +/* If the internal and external units differ, attempt to remap the axis + into its external units. */ + } else { + +/* Get the string describing the external units (the "Unit" attribute). */ + extunit = astGetUnit( pframe, paxis ); + +/* Get the string describing the internal units (the "InternalUnit" + attribute). */ + intunit = astGetInternalUnit( pframe, paxis ); + +/* If they are the same, we do not need to modify this axis. */ + if( astOK && strcmp( extunit, intunit ) ){ + +/* Otherwise, get the mapping from the internal units to the external + units, if possible. Ignore any error reported by unitmapper. */ + rep = astReporting( 0 ); + umap = astUnitMapper( intunit, extunit, NULL, NULL ); + if( !astOK ) astClearStatus; + astReporting( rep ); + + if( !umap ) { + +/* If the above failed, ensure that the external units are the same as + the internal units (except that internal radians are converted to + external degrees). */ + if( !strcmp( intunit, "rad" ) ) { + umap = (AstMapping *) astZoomMap( 1, AST__DR2D, " ", status ); + extunit = "deg"; + } else { + extunit = intunit; + } + + astSetUnit( wcsfrm, iax, extunit ); + } + } + } + +/* If no change is needed for the mapping for this axis, use a UnitMap. */ + if( !umap ) umap = (AstMapping *) astUnitMap( 1, " ", status ); + +/* Extend the parallel CmpMap to encompass the current axis. */ + if( remap ) { + tmap = (AstMapping *) astCmpMap( remap, umap, 0, " ", status ); + (void) astAnnul( remap ); + remap = tmap; + } else { + remap = astClone( umap ); + } + +/* Free resources. */ + umap = astAnnul( umap ); + pframe = astAnnul( pframe ); + } + +/* See if the pixel->wcs mapping needs to be modified to take account of + any changes to axis units. */ + smap = astSimplify( remap ); + if( ! astIsAUnitMap( smap ) ) { + tmap = (AstMapping *) astCmpMap( map, remap, 1, " ", status ); + map = astAnnul( map ); + remap = astAnnul( remap ); + map = tmap; + } + +/* If the supplied FrameSet is usable... */ + if( ok ) { + +/* If we did not find a SpecFrame, return a FrameSet made from the base + and current Frames in the supplied FrameSet. */ + if( !specfrm ) { + ret = astFrameSet( pixfrm, "", status ); + astAddFrame( ret, AST__BASE, map, wcsfrm ); + +/* If we have a SpecFrame, proceed. */ + } else { + +/* Check that both the RefRA and RefDec attributes of the SpecFrame are set. + If not, return a FrameSet made from the base and current Frames in the + supplied FrameSet. Also do this if the original WCS Frame contains 3 + or more axes (since it is almost always inappropriate to add extra sky + axes in such circumestances). But if the other axes form a skyfram, + then we need to make sure they use the right refrence point. */ + if( !astTestRefRA( specfrm ) || !astTestRefDec( specfrm ) || + ( nwcs > 2 && !skyfrm ) ) { + ret = astFrameSet( pixfrm, "", status ); + astAddFrame( ret, AST__BASE, map, wcsfrm ); + +/* If we have a celestial reference position for the spectral axis, ensure + it is described correctly by a pair of celestial axes. */ + } else { + +/* If the WCS Frame does not contain any celestial axes, we add some now. */ + if( !skyfrm ) { + +/* The easiest way to create the required mapping from pixel to celestial + to create a simple FITS header and read it in via a FitsChan to create a + FrameSet. */ + fc = astFitsChan( NULL, NULL, "", status ); + astPutFits( fc, "CRPIX1 = 0", 0 ); + astPutFits( fc, "CRPIX2 = 0", 0 ); + astPutFits( fc, "CDELT1 = 0.0003", 0 ); + astPutFits( fc, "CDELT2 = 0.0003", 0 ); + astPutFits( fc, "CTYPE1 = 'RA---TAN'", 0 ); + astPutFits( fc, "CTYPE2 = 'DEC--TAN'", 0 ); + astPutFits( fc, "RADESYS = 'FK5'", 0 ); + astPutFits( fc, "EQUINOX = 2000.0", 0 ); + sprintf( card, "CRVAL1 = %.*g", DBL_DIG, + AST__DR2D*astGetRefRA( specfrm ) ); + astPutFits( fc, card, 0 ); + sprintf( card, "CRVAL2 = %.*g", DBL_DIG, + AST__DR2D*astGetRefDec( specfrm ) ); + astPutFits( fc, card, 0 ); + sprintf( card, "MJD-OBS = %.*g", DBL_DIG, + TDBConv( astGetEpoch( specfrm ), AST__UTC, 1, + "astWrite", "FitsChan", status ) ); + astPutFits( fc, card, 0 ); + astClearCard( fc ); + tfs = astRead( fc ); + if( tfs ) { + +/* Create the new pixel->wcs Mapping. First get the 2-input,2-output + Mapping between pixel and sky coords from the above FrameSet. Then add + this Mapping in parallel with the original pixel->wcs Mapping. */ + tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + tmap1 = (AstMapping *) astCmpMap( map, tmap0, 0, "", status ); + tmap0 = astAnnul( tmap0 ); + +/* We now have a (npix+2)-input,(nwcs+2)-output Mapping. We now add a + PermMap in series with this which feeds the constant value 0.0 (the + CRPIX value in the above set of FITS headers) into the 2 pixel axes + corresponding to RA and Dec. This PermMap has npix-inputs and (npix+2) + outputs. The total Mapping then has npix inputs and (nwcs+2) outputs. */ + perm = astMalloc( sizeof( int )*(size_t) ( npix + 2 ) ); + if( astOK ) { + for( iax = 0; iax < npix; iax++ ) perm[ iax ] = iax; + perm[ npix ] = -1; + perm[ npix + 1 ] = -1; + con = 0.0; + tmap0 = (AstMapping *) astPermMap( npix, perm, npix + 2, perm, &con, "", status ); + tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status ); + tmap0 = astAnnul( tmap0 ); + tmap1 = astAnnul( tmap1 ); + +/* We now create the new WCS Frame with the extra RA and Dec axes. This + is just a CmpFrame made up of the original WCS Frame and the new + SkyFrame. */ + tfrm = astGetFrame( tfs, AST__CURRENT ); + tfrm0 = (AstFrame *) astCmpFrame( wcsfrm, tfrm, "", status ); + tfrm = astAnnul( tfrm ); + +/* Construct the returned FrameSet. */ + ret = astFrameSet( pixfrm, "", status ); + astAddFrame( ret, AST__BASE, tmap2, tfrm0 ); + tmap2 = astAnnul( tmap2 ); + tfrm0 = astAnnul( tfrm0 ); + +/* Free remaining resources. */ + perm = astFree( perm ); + } + tfs = astAnnul( tfs ); + } + fc = astAnnul( fc ); + +/* If the WCS Frame does contain celestial axes we make sure that the + SpecFrame uses the same reference point. */ + } else { + +/* The returned FrameSet has no extra Frames (although some attributes + may be changed) so just create a new FrameSet equaivalent to the supplied + FrameSet. */ + tfs = astFrameSet( pixfrm, "", status ); + astAddFrame( tfs, AST__BASE, map, wcsfrm ); + +/* The RefRA and RefDec attributes of the SpecFrame must be set in FK5 + J2000. Therefore we need to know the celestial reference point in + FK5 J2000. Modify the SkyFrame within the FrameSet to represent FK5 + J2000, noting the original sky system and equinox first so that they + can be re-instated (if set) later on. */ + sprintf( system_attr, "System(%d)", ilon + 1 ); + if( astTest( tfs, system_attr ) ) { + skysys = astGetC( tfs, system_attr ); + } else { + skysys = NULL; + } + astSetC( tfs, system_attr, "FK5" ); + sprintf( equinox_attr, "Equinox(%d)", ilon + 1 ); + if( astTest( tfs, equinox_attr ) ) { + eqn = astGetC( tfs, equinox_attr ); + } else { + eqn = NULL; + } + astSetC( tfs, equinox_attr, "J2000" ); + +/* The reference point for the celestial axes is defined by the WcsMap + contained within the Mapping. Split the mapping up into a list of serial + component mappings, and locate the first WcsMap in this list. The first + Mapping returned by this call is the result of compounding all the + Mappings up to (but not including) the WcsMap, the second returned Mapping + is the (inverted) WcsMap, and the third returned Mapping is anything + following the WcsMap. Only proceed if one and only one WcsMap is found. */ + tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + if( SplitMap( tmap0, astGetInvert( tmap0 ), ilon, ilat, &map1, &map2, &map3, status ) ){ + +/* The reference point in the celestial coordinate system is found by + transforming the fiducial point in native spherical co-ordinates + into absolute physical coordinates using map3. */ + if( GetFiducialWCS( map2, map3, ilon, ilat, &reflon, &reflat, status ) ){ + +/* Use reflon and reflat (which represent FK5 J2000 RA and Dec) to set + the values of the SpecFrame RefRA and RefDec attributes. Format the + values first so that we can use the FrameSet astSetC method, and so + maintain the FrameSet integrity. Use "tfs" rather than "wcsfrm" when + calling astFormat, as "wcsfrm" is not affected by the above change + to the current frame of "tfs" (i.e. astAddFrame takes a deep copy of the + supplied Frame). */ + astSetC( tfs, "RefRA", astFormat( tfs, ilon, reflon ) ); + astSetC( tfs, "RefDec", astFormat( tfs, ilat, reflat ) ); + +/* If succesfull, return a pointer to the FrameSet. */ + if( astOK ) ret = astClone( tfs ); + } + +/* Release resources. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + +/* If no WcsMap was found, the celestial axes have no reference point and + so we can retain the original spectral reference point, so just return + the temporary FrameSet. */ + } else if( astOK ) { + ret = astClone( tfs ); + } + tmap0 = astAnnul( tmap0 ); + +/* Re-instate the original sky system and equinox. */ + if( skysys ) astSetC( tfs, system_attr, skysys ); + if( eqn ) astSetC( tfs, equinox_attr, eqn ); + +/* Release resources. */ + tfs = astAnnul( tfs ); + } + } + } + } + +/* Add a new current Frame into the FrameSet which increases the chances of + the requested encoding being usable. The index of the original current + Frame is returned, or AST__NOFRAME if no new Frame was added. */ + icurr = AddEncodingFrame( this, ret, encoding, method, class, status ); + +/* If a new Frame was added, remove the original current Frame. */ + if( icurr != AST__NOFRAME ) astRemoveFrame( ret, icurr ); + +/* Free resources. */ + if( specfrm ) specfrm = astAnnul( specfrm ); + if( skyfrm ) skyfrm = astAnnul( skyfrm ); + pixfrm = astAnnul( pixfrm ); + wcsfrm = astAnnul( wcsfrm ); + map = astAnnul( map ); + +/* Return NULL if an error has occurred. */ + if( !astOK && ret ) ret = astAnnul( ret ); + +/* Return the result. */ + return ret; +} + +static void MakeIndentedComment( int indent, char token, + const char *comment, const char *data, + char string[ AST__FITSCHAN_FITSCARDLEN - + FITSNAMLEN + 1 ], int *status ) { +/* +* Name: +* MakeIndentedComment + +* Purpose: +* Create a comment string containing an indentation bar. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void MakeIndentedComment( int indent, char token, +* const char *comment, const char *data, +* char string[ AST__FITSCHAN_FITSCARDLEN - +* FITSNAMLEN + 1 ], int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function creates a string that may be used as text in a +* FITS comment card. The string contains a textual comment +* preceded by a bar (a line of characters) whose length can be +* used to indicate a level of indentation (in the absence of any +* way of indenting FITS keywords). + +* Parameters: +* indent +* The level of indentation, in characters. +* token +* The character used to form the indentation bar. +* comment +* A pointer to a constant null-terminated string containing the text +* of the comment to be included. +* data +* A pointer to a constant null-terminated string containing any +* textual data to be appended to the comment. +* string +* A character array to receive the output string. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The comment text that appears in the output string is formed by +* concatenating the "comment" and "data" strings. +*/ + +/* Local Variables: */ + int i; /* Loop counter for input characters */ + int len; /* Number of output characters */ + int mxlen; /* Maximum length of output string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Calculate the maximum number of characters that the output string + can accommodate. */ + mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN; + +/* Start the string with "indent" copies of the token character, but + without exceeding the output string length. */ + len = 0; + while ( ( len < indent ) && ( len < mxlen ) ) string[ len++ ] = token; + +/* Pad with spaces up to the start of the comment, if necessary. */ + while ( len < ( FITSCOMCOL - FITSNAMLEN - 1 ) ) { + string[ len++ ] = ' '; + } + +/* Add "/ " to introduce the comment (strictly not necessary as the + whole card will be a comment, but it matches the other non-comment + cards). Truncate if necessary. */ + for ( i = 0; ( i < 2 ) && ( len < mxlen ); i++ ) { + string[ len++ ] = "/ "[ i ]; + } + +/* Append the comment string, truncating it if it is too long. */ + for ( i = 0; comment[ i ] && ( len < mxlen ); i++ ) { + string[ len++ ] = comment[ i ]; + } + +/* Append the data string, again truncating if too long. */ + for ( i = 0; data[ i ] && ( len < mxlen ); i++ ) { + string[ len++ ] = data[ i ]; + } + +/* Terminate the output string. */ + string[ len ] = '\0'; +} + +static void MakeIntoComment( AstFitsChan *this, const char *method, + const char *class, int *status ){ + +/* +* Name: +* MakeIntoComment + +* Purpose: +* Convert a card into a FITS COMMENT card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void MakeIntoComment( AstFitsChan *this, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function formats the card stored just prior to the current card, +* and re-stores it as a COMMENT card. It is used (when writing an Object +* to a FitsChan) to output values that are not "set" and which are +* therefore provided for information only, and should not be read back. +* the COMMENT card has the effect of "commenting out" the value. + +* Parameters: +* this +* Pointer to the FitsChan. +* method +* Calling method. +* class +* Object class. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Character buffer for FITS card data */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Move the current card backwards by one card. */ + MoveCard( this, -1, method, class, status ); + +/* Format the new current card. */ + FormatCard( this, card, method, status ); + +/* Write the resulting string to the FitsChan as the contents of a COMMENT + card, overwriting the existing card. The current card is incremented + by this call so that it refers to the same card as on entry. */ + astSetFitsCom( this, "COMMENT", card, 1 ); +} + +static int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s, + FitsStore *store, double *dim, + const char *method, const char *class, int *status ){ +/* +* Name: +* MakeIntWorld + +* Purpose: +* Create FITS header values which map grid into intermediate world +* coords. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s, +* FitsStore *store, double *dim, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function adds values to the supplied FitsStore which describe +* the transformation from grid (pixel) coords to intermediate world +* coords. The values added to the FitsStore correspond to the CRPIXj, +* PCi_j, CDELTi and WCSAXES keywords, and are determined by examining the +* suppliedMapping, which must be linear with an optional shift of +* origin (otherwise a value of zero is returned). +* +* Much of the complication in the algorithm arises from the need to +* support cases where the supplied Mapping has more outputs than +* inputs. In these case we add some "degenerate" axes to the grid +* coord system, choosing their unit vectors to be orthogonal to all +* the other grid axes. It is assumed that degenerate axes will never +* be used to find a position other than at the axis value of 1.0. +* +* NOTE, appropriate values for CRVAL keywords should have been stored +* in the FitsStore before calling this function (since this function may +* modify them). + +* Parameters: +* cmap +* A pointer to a Mapping which transforms grid coordinates into +* intermediate world coordinates. The number of outputs must be +* greater than or equal to the number of inputs. +* fr +* Pointer to the final WCS coordinate Frame. +* wperm +* Pointer to an array of integers with one element for each axis of +* the "fr" Frame. Each element holds the zero-based index of the +* FITS-WCS axis (i.e. the value of "i" in the keyword names "CTYPEi", +* "CDi_j", etc) which describes the Frame axis. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* store +* A pointer to the FitsStore into which the calculated CRPIX and +* CDi_j values are to be put. +* dim +* An array holding the image dimensions in pixels. AST__BAD can be +* supplied for any unknwon dimensions. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if the CRPIX and CDi_j values are +* succesfully calculated. Zero is returned otherwise. + +* Notes: +* - Zero is returned if an error occurs. +*/ + +/* Local Variables: */ + AstFrame *pfrm; + AstFrame *sfrm; + AstMapping *map; + AstPointSet *psetw; + AstPointSet *psetg; + double **fullmat; + double **partmat; + double **ptrg; + double **ptrw; + double *c; + double *cdelt; + double *cdmat; + double *colvec; + double *d; + double *g; + double *g0; + double *m; + double *mat; + double *tol; + double *w0; + double *y; + double cd; + double crp; + double crv; + double cv; + double det; + double err; + double k; + double mxcv; + double skydiag1; + double skydiag0; + double val; + int *iw; + int *lin; + int *pperm; + int *skycol; + int i; + int ii; + int j; + int jax; + int jj; + int nin; + int nout; + int nwcs; + int paxis; + int ret; + int sing; + int skycol0; + int skycol1; + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Simplify the supplied Mapping to reduce rounding errors when + transforming points. */ + map = astSimplify( cmap ); + +/* Get the number of inputs and outputs for the Mapping. Return if the + number of outputs is smaller than the number of inputs. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + if( nout < nin ) return ret; + +/* Note the number of final World Coordinate axes (not necessarily the + same as "nout", since some intermediate axes may be discarded by a + later PermMap. */ + nwcs = astGetNaxes( fr ); + +/* Allocate work space. */ + g = astMalloc( sizeof(double)*(size_t) nin ); + g0 = astMalloc( sizeof(double)*(size_t) nin ); + w0 = astMalloc( sizeof(double)*(size_t) nout ); + tol = astMalloc( sizeof(double)*(size_t) nout ); + partmat = astMalloc( sizeof(double *)*(size_t) nout ); + lin = astMalloc( sizeof(int)*(size_t) nout ); + pperm = astMalloc( sizeof(int)*(size_t) nout ); + skycol = astMalloc( sizeof(int)*(size_t) nout ); + cdmat = astMalloc( sizeof(double)*(size_t) (nout*nout) ); + cdelt = astMalloc( sizeof(double)*(size_t) nout ); + +/* For safety, initialise all other pointers. */ + if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = NULL; + fullmat = NULL; + +/* Create a PointSet to hold an input (grid) position for each axis, plus + an extra one. Create two other PointSets to hold corresponding + output (IWC) coordinates. */ + psetg = astPointSet( nin + 1, nin, "", status ); + ptrg = astGetPoints( psetg ); + psetw = astPointSet( nin + 1, nout, "", status ); + ptrw = astGetPoints( psetw ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Assume success. */ + ret = 1; + +/* The next section finds a 'root' grid position for which the + corresponding IWC coordinates are all good. It also finds these IWC + coordinates, together with the IWC coordinates of "nin" points which + are a unit distance away from the root grid position along each + grid axis. It also finds an estimate of the rounding error in each + Mapping output. + ================================================================= */ + ret = FindBasisVectors( map, nin, nout, dim, psetg, psetw, status ); + +/* Save the grid root position in "g0". */ + for( j = 0; j < nin; j++ ) g0[ j ] = ptrg[ j ][ 0 ]; + +/* Save the transformed root position in "w0". This is the grid root + position represented as a vector within the Intermediate World + Coordinate system. */ + for( j = 0; j < nout; j++ ) { + w0[ j ] = ptrw[ j ][ 0 ]; + +/* Find the tolerance for positions on the j'th IWC axis. This is one + tenth of the largest change in the j'th IWC axis value caused by + moving out 1 pixel along any grid axis. */ + tol[ j ] = 0.0; + for( i = 0; i < nin; i++ ) { + err = fabs( ptrw[ j ][ i + 1 ] - w0[ j ] ); + if( err > tol[ j ] ) tol[ j ] = err; + } + tol[ j ] *= 0.1; + +/* If the tolerance is zero (e.g. as is produced for degenerate axes), + then use a tolerance equal to a very small fraction of hte degenerate + axis value. If the axis value is zero use a fixed small value. */ + if( tol[ j ] == 0.0 ) tol[ j ] = w0[ j ]*DBL_EPSILON*1.0E5; + if( tol[ j ] == 0.0 ) tol[ j ] = sqrt( DBL_MIN ); + } + +/* The next section finds the CD matrix. + ===================================== */ + +/* Initialise the CD matrix elements to "all missing". */ + for( i = 0; i < nout*nout; i++ ) cdmat[ i ] = AST__BAD; + +/* The elements of column "j" of the CD matrix form a vector (in Intermediate + World Coords) which corresponds to a unit vector along grid axis "j". + We now find these vectors for all the grid axes represented by the + inputs to the supplied Mapping. */ + for( i = 0; i < nin && ret; i++ ) { + +/* Form a unit vector along the current input axis. */ + for( ii = 0; ii < nin; ii++ ) g[ ii ] = 0.0; + g[ i ] = 1.0; + +/* Fit a straight line (within IWC) to the current input axis of the Mapping. + The IWC vector corresponding to a unit vector along the current input axis + is returned if the Mapping is linear. A NULL pointer is returned if the + Mapping is not linear. */ + partmat[ i ] = FitLine( map, g, g0, w0, dim[ i ], tol, status ); + +/* If unsuccesful, indicate failure and break out of the loop. */ + if( !partmat[ i ] ) { + ret = 0; + break; + } + } + +/* If the number of outputs for "map" is larger than the number of inputs, + then we will still be missing some column vectors for the CDi_j matrix + (which has to be square). We invent these such that the they are + orthogonal to all the other column vectors. Only do this if the + Mapping is linear. */ + if( ret ) { + fullmat = OrthVectorSet( nout, nin, partmat, status ); + if( !fullmat ) ret = 0; + } + +/* Check everything is OK. */ + if( ret ) { + +/* Check that the full matrix is invertable, and if not, see if there is + any way to make it invertable. */ + MakeInvertable( fullmat, nout, dim, status ); + +/* Set up an array holding index of the Mapping output corresponding to + each IWC axis (the inverse of "wperm"). Also look for matching pairs of + celestial WCS axes. For the first such pair, note the corresponding + column indices and the diagonal element of the matrix which gives the + scaling for the axis (taking account of the permutation of WCS axes). + Also note if the Mapping from intermediate world coords to final world + coords is linear for each axis (this is assumed to be the case if the + axis is part of a simple Frame). */ + sfrm = NULL; + skydiag0 = AST__BAD; + skydiag1 = AST__BAD; + skycol0 = -1; + skycol1 = -1; + for( i = 0; i < nout; i++ ) { + pperm[ wperm[ i ] ] = i; + astPrimaryFrame( fr, i, &pfrm, &paxis ); + if( IsASkyFrame( pfrm ) ) { + skycol[ wperm[ i ] ] = paxis + 1; + lin[ i ] = 0; + if( !sfrm ) { + sfrm = astClone( pfrm ); + skycol0 = wperm[ i ]; + skydiag0 = fullmat[ skycol0 ][ i ]; + } else if( sfrm == pfrm ) { + skycol1 = wperm[ i ]; + skydiag1 = fullmat[ skycol1 ][ i ]; + } + } else { + skycol[ wperm[ i ] ] = 0; + lin[ i ] = !strcmp( astGetClass( pfrm ), "Frame" ); + } + pfrm = astAnnul( pfrm ); + } + if( sfrm ) sfrm = astAnnul( sfrm ); + +/* We now have the complete CDi_j matrix. Now to find the CRPIX values. + These are the grid coords of the reference point (which corresponds to + the origin of Intermediate World Coords). The "w0" array currently holds + the position of the root position, as a position within IWC, and the + "g0" array holds the corresponding position in grid coordinates. We + also have IWC vectors which correspond to unit vectors on each grid + axis. The CRPIX values are defined by the matrix equation + w0 = fullmat*( g0 - crpix ) + The "g0" array only contains "nin" values. If nout>nin, then the + missing g0 values will be assumed to be zero when we come to find the + CRPIX values below. + We use palDmat to solve this system of simultaneous equations to get + crpix. The "y" array initially holds "w0" but is over-written to hold + "g0 - crpix". */ + mat = astMalloc( sizeof( double )*(size_t)( nout*nout ) ); + y = astMalloc( sizeof( double )*(size_t) nout ); + iw = astMalloc( sizeof( int )*(size_t) nout ); + if( astOK ) { + m = mat; + for( i = 0; i < nout; i++ ) { + for( j = 0; j < nout; j++ ) *(m++) = fullmat[ j ][ i ]; + y[ i ] = w0[ i ]; + } + palDmat( nout, mat, y, &det, &sing, iw ); + } + mat = astFree( mat ); + iw = astFree( iw ); + +/* Loop round all axes, storing the column vector pointer. */ + for( j = 0; j < nout; j++ ) { + colvec = fullmat[ j ]; + +/* Get the CRPIX values from the "y" vector created above by palDmat. + First deal with axes for which there are Mapping inputs. */ + if( j < nin ) { + crp = g0[ j ] - y[ j ]; + +/* If this is a grid axis which has been created to represent a "missing" + input to the mapping, we need to add on 1.0 to the crpix value found + above. This is because the "w0" vector corresponds to a value of zero + on any missing axes, but the FITS grid value for any missing axes is + 1.0. */ + } else { + crp = 1.0 - y[ j ]; + } + +/* Store the CD and CRPIX values for axes which correspond to inputs + of "map". The CD matrix elements are stored in an array and are + converted later to the corresponding PC and CDELT values. */ + if( j < nin || crp == 0.0 ) { + for( i = 0; i < nout; i++ ) { + cdmat[ wperm[ i ]*nout+j ] = colvec[ i ] ; + } + SetItem( &(store->crpix), 0, j, s, crp, status ); + +/* The length of the unit vector along any "degenerate" axes was fixed + arbitrarily at 1.0 by the call to OrthVectorSet. We can probably + choose a more appropriate vector length. The choice shouldn't make any + difference to the transformation, but an appropriate value will look + more natural to human readers. */ + } else { + +/* First, try to arrange for longitude/latitude axis pairs to have the same + scale. Do we have a matching pair of celestial axes? */ + k = AST__BAD; + if( skydiag0 != AST__BAD && skydiag1 != AST__BAD ) { + +/* Is the current column the one which corresponds to the first celestial + axis, and does the other sky column correspond to a Mapping input? */ + if( skycol0 == j && skycol1 < nin ) { + +/* If so, scale this column so that its diagonal element is the negative + of the diagonal element of the other axis. This is on the assumption that + the scales on the two axes should be equal, and that longitude increases + east whilst latitude increases north, and that the CD matrix does not + introduce an axis permutation. */ + if( skydiag0 != 0.0 ) k = -skydiag1/skydiag0; + +/* Now see if the current column the one which corresponds to the second + celestial axis. Do the same as above. */ + } else if( skycol1 == j && skycol0 < nin ) { + if( skydiag1 != 0.0 ) k = -skydiag0/skydiag1; + +/* If neither of the above conditions was met, assume a diagonal element + value of 1.0 degrees for latitude axes, and -1.0 degrees for longitude + axes. */ + } + } + +/* If this failed, the next choice is to arrange for diagonally opposite + elements to be equal and opposite in value. Look for the element of the + column which has the largest diagonally opposite element, and choose a + scaling factor which makes this column element equal to the negative value + of its diagonally opposite element. Be careful to take axis permutations + into account when finding the value of the diagonal element. */ + if( k == AST__BAD ) { + mxcv = 0.0; + ii = pperm[ j ]; + for( i = 0; i < nout; i++ ) { + jj = wperm[ i ]; + if( jj < nin ) { + cv = fullmat[ jj ][ ii ]; + if( !astEQUAL( colvec[ i ], 0.0 ) && fabs( cv ) > mxcv ) { + mxcv = fabs( cv ); + k = -cv/colvec[ i ]; + } + } + } + } + +/* If still no scaling factor is available, use a scaling factor which + produces a diagonal element of 1.0 degree if the corresponding row is a + sky latitude axis, -1.0 degree of sky longitude axes, and 1.0 for other + axes. */ + if( k == AST__BAD && colvec[ pperm[ j ] ] != 0.0 ) { + if( skycol[ j ] ) { + k = AST__DD2R/colvec[ pperm[ j ] ]; + if( skycol[ j ] == 1 ) k = -k; + } else { + k = 1.0/colvec[ pperm[ j ] ]; + } + } + +/* If we still do not have a scaling, use 1.0 (no scaling). */ + if( k == AST__BAD ) k = 1.0; + +/* Now scale and store the column elements. */ + for( i = 0; i < nout; i++ ) { + cdmat[ wperm[ i ]*nout+j ] = k*colvec[ i ]; + } + +/* Find the corresponding modified CRPIX value and store it. */ + crp = 1.0 + ( crp - 1.0 )/k; + SetItem( &(store->crpix), 0, j, s, crp, status ); + } + +/* Free resources */ + if( pfrm ) pfrm = astAnnul( pfrm ); + } + +/* Any "degenerate" axes added in the above process for which the + intermediate->world mapping is linear, and which depend only on one + pixel axis, can be adjusted so that the reference point is at grid + coord 1.0. */ + for( i = 0; i < nout; i++ ) { + if( lin[ i ] ) { + +/* Check only one pixel axis contributes to this intermediate world axis + and find which one it is. */ + jax = -1; + for( j = 0; j < nout; j++ ) { + if( !astEQUAL( fullmat[ j ][ i ], 0.0 ) ) { + if( jax == -1 ) { + jax = j; + } else { + jax = -1; + break; + } + } + } + +/* We only adjust values for "degenerate" axes. */ + if( jax >= nin ) { + +/* Check that this pixel axis only contributes to the single world axis + currently being considered. */ + for( ii = 0; ii < nout; ii++ ) { + if( ii != i ) { + if( !astEQUAL( fullmat[ jax ][ ii ], 0.0 ) ) { + jax = -1; + break; + } + } + } + if( jax != -1 ) { + +/* Get the original CRVAL, CRPIX and CD values. Check they are defined.*/ + crv = GetItem( &(store->crval), wperm[ i ], 0, s, NULL, + method, class, status ); + cd = cdmat[ wperm[ i ]*nout + jax ]; + crp = GetItem( &(store->crpix), 0, jax, s, NULL, method, class, status ); + if( crv != AST__BAD && crp != AST__BAD && + cd != AST__BAD ) { + +/* Modify the CRPIX to be 1.0 and modify the CRVAL value accordingly. */ + SetItem( &(store->crpix), 0, jax, s, 1.0, status ); + SetItem( &(store->crval), wperm[ i ], 0, s, + cd*( 1.0 - crp ) + crv, status ); + } + } + } + } + } + +/* Finally, if there are fewer input axes than output axes, put a value for + the WCSAXES keyword into the store. */ + if( nin < nwcs ) SetItem( &(store->wcsaxes), 0, 0, s, nwcs, status ); + +/* Release resources. */ + y = astFree( y ); + } + +/* Produce and store PC and CDELT values from the above CD matrix */ + SplitMat( nout, cdmat, cdelt, status ); + c = cdmat; + d = cdelt; + for( i = 0; i < nout; i++ ){ + for( j = 0; j < nout; j++ ){ + val = *(c++); + if( i == j ){ + if( astEQUAL( val, 1.0 ) ) val = AST__BAD; + } else { + if( astEQUAL( val, 0.0 ) ) val = AST__BAD; + } + if( val != AST__BAD ) SetItem( &(store->pc), i, j, s, val, status ); + } + SetItem( &(store->cdelt), i, 0, s, *(d++), status ); + } + } + +/* Annul pointsets. */ + psetg = astAnnul( psetg ); + psetw = astAnnul( psetw ); + +/* Free other resources*/ + map = astAnnul( map ); + if( fullmat ) for( j = 0; j < nout; j++ ) fullmat[ j ] = astFree( fullmat[ j ] ); + if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = astFree( partmat[ j ] ); + fullmat = astFree( fullmat ); + partmat = astFree( partmat ); + cdmat = astFree( cdmat ); + cdelt = astFree( cdelt ); + g = astFree( g ); + g0 = astFree( g0 ); + w0 = astFree( w0 ); + tol = astFree( tol ); + lin = astFree( lin ); + skycol = astFree( skycol ); + pperm = astFree( pperm ); + +/* If an error has occurred, return zero. */ + if( !astOK ) ret = 0; + +/* Return the answer. */ + return ret; +} + +static void MakeInvertable( double **fullmat, int n, double *dim, int *status ){ +/* +* Name: +* MakeInvertable + +* Purpose: +* Modify a supplied square CD matrix if possible to make it invertable. + +* Type: +* Private function. + +* Synopsis: +* void MakeInvertable( double **fullmat, int n, double *dim, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A search is made for matrix inputs that have no effect on any +* matrix outputs. if any such matrix inputs are associated with +* degenerate pixel axes (i.e. pixel axes that span only a single +* pixel), then the matrix input should always have the value zero and +* so the corresponding diagonal element of the matrix can be set to +* 1.0 without changing and of the outputs. + +* Parameters: +* fullmat +* A pointer to an array with "n" elements corresponding to the n +* inputs of the matrix, each element being a pointer to an array +* with "n" elements corresponding to the n outputs of the matrix. +* n +* The number of inputs and outputs for the square matrix. +* dim +* Pointer to an array of "n" input (i.e. pixel) axis dimensions. +* Individual elements will be AST__BAD if dimensions are not known. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int i; /* Input index */ + int j; /* Output index */ + int unused; /* Does the current input have no effect on any output? */ + +/* Check inherited status */ + if( !astOK ) return; + +/* Look for any inputs that have no effect on any of the outputs. If such + an input is associated with a degenerate grid axis (i.e. a grid axis + with a dimension of 1.0), then the input value will always be zero and + so the corresponding diagonal element of the matrix can eb set to 1.0 + without affecting the output value (which will always be zero since + zero times anything is zero). Loop over all inputs. */ + for( i = 0; i < n; i++ ) { + +/* Assume this input has no effect on any output. */ + unused = 1; + +/* Loop over all outputs. */ + for( j = 0; j < n; j++ ) { + +/* If the corresponding matrix term is non-zero, the the input will have + an effect on the output, so set the unused flag false and break out of + the output loop. */ + if( fullmat[ i ][ j ] != 0.0 ) { + unused = 0; + break; + } + } + +/* If the input is unused, and it is associated with a degenerate pixel + axis, we can set the corresponding diagonal element of the matrix to + 1.0. */ + if( unused && dim[ i ] == 1.0 ) fullmat[ i ][ i ] = 1.0; + } +} +#if defined(THREAD_SAFE) + +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode + +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: + +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to FitsChan structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NUL. */ + if( ! this_object ) return result; + +/* Obtain a pointers to the FitsChan structure. */ + this = (AstFitsChan *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->keyseq, mode, extra, fail ); + if( !result ) result = astManageLock( this->keywords, mode, extra, fail ); + return result; +} +#endif + +static int Match( const char *test, const char *temp, int maxfld, int *fields, + int *nfld, const char *method, const char *class, int *status ){ +/* +* Name: +* Match + +* Purpose: +* Sees if a test keyword name matches a template. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int Match( const char *test, const char *temp, int maxfld, int *fields, +* int *nfld, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* All characters in the template other than "%" (and the field width +* and type specifiers which follow a "%") must be matched by an +* identical character (ignoring case) in the test string. If a "%" occurs +* in the template, then the next character in the template should be a +* single digit specifying a field width. If it is zero, then the test +* string may contain zero or more matching characters. Otherwise, +* the test string must contain exactly the specified number of matching +* characters (i.e. 1 to 9). The field width digit may be omitted, in +* which case the test string must contain one or more matching +* characters. The next character in the template specifies the type of +* matching characters and must be one of "d", "c" or "f". Decimal digits +* are matched by "d", all upper (but not lower) case alphabetical +* characters are matched by "c", and all characters which are legal within +* a FITS keyword (i.e. upper case letters, digits, underscores and +* hyphens) are matched by "f". + +* Parameters: +* test +* Pointer to a null terminated string holding the keyword name to +* be tested. +* temp +* Pointer to a null terminated string holding the template. +* maxfld +* The maximum number of integer field values which should be +* returned in "fields". +* fields +* A pointer to an array of at least "maxfld" integers. This is +* returned holding the values of any integer fields specified +* in the template. The values are extracted from the test string, +* and stored in the order they appear in the template string. +* nfld +* Pointer to a location at which is returned the total number of +* integer fields in the test string. This may be more than the +* number returned in "fields" if "maxfld" is smaller than "*nfld". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if the test string does not match the template +* string, and one is returned if it does. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char type; /* Field type specifier */ + const char *a; /* Pointer to next test character */ + const char *b; /* Pointer to next template character */ + int extend; /* Can the width of the first field be extended? */ + int i; /* Field index */ + int match; /* Does "test" match "temp"? */ + int nfret; /* No. of fields returned */ + int tmp; /* Field value */ + +/* Check global status. */ + if( !astOK ) return 0; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* On the first entry to this function, indicate that no integer fields + have yet been returned, and save a pointer to the start of the template + string. */ + if( !match_nentry ) { + *nfld = 0; + match_template = temp; + } + +/* Increment the number of entries into this function. */ + match_nentry++; + +/* Initialise pointers to the start of each string. */ + a = test; + b = temp; + +/* Initialise the returned flag to indicate that the two strings do not + match. */ + match = 0; + +/* Check that the initial part of the test string can match the first + field in the template. */ + if( MatchFront( a, b, &type, &extend, &match_na, &match_nb, method, class, match_template, status ) ){ + +/* If it does, increment the pointers to skip over the characters + used up in the comparison. */ + a += match_na; + b += match_nb; + +/* If the ends of both strings have been reached, they match. */ + if( *a == 0 && *b == 0 ){ + match = 1; + +/* Otherwise, if the end of the template has been reached but there are + still characters to be read from the test string, we could still have + a match if all the remaining test characters match an extandable field. */ + } else if( *b == 0 && *a != 0 && extend ){ + +/* Loop until all the matching characters have been read from the end of + the test string. */ + while( *a != 0 && MatchChar( *a, type, method, class, match_template, status ) ) a++; + +/* If we reached the end of the test string, we have a match. */ + if( *a == 0 ) match = 1; + +/* Otherwise, we need to carry on checking the remaining fields. */ + } else { + +/* Call this function recursively to see if the remainder of the + strings match. */ + if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){ + match = 1; + +/* If the remainder of the strings do not match, we may be able to make + them match by using up some extra test characters on the first field. + This can only be done if the first field has an unspecified field width, + and if the next test character if of a type which matches the first + field in the template. */ + } else if( extend ){ + +/* Loop until all the suitable characters have been read from the + test string. Break out of the loop early if we find a field width + which results in the whole string matching. */ + while( MatchChar( *a, type, method, class, match_template, status ) ){ + a++; + if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){ + match = 1; + break; + } + } + } + } + } + +/* If the strings match and the leading field is an integer, decode + the field and store it in the supplied array (if there is room). */ + if( match && type == 'd' && a > test ){ + if( *nfld < maxfld ){ + sprintf( match_fmt, "%%%dd", (int) ( a - test ) ); + astSscanf( test, match_fmt, fields + *nfld ); + } + (*nfld)++; + } + +/* Decrement the number of entries into this function. */ + match_nentry--; + +/* If we are leaving this function for the last time, reverse the + order of the returned integer fields so that they are returned + in the same order that they occur in the template. */ + if( !match_nentry ){ + nfret = ( *nfld < maxfld ) ? (*nfld) : maxfld; + match_pa = fields; + match_pb = fields + nfret - 1; + for( i = 0; i < nfret/2; i++ ){ + tmp = *match_pa; + *(match_pa++) = *match_pb; + *(match_pb--) = tmp; + } + } + +/* Return the result. */ + return match; +} + +static int MatchChar( char test, char type, const char *method, + const char *class, const char *template, int *status ){ +/* +* Name: +* MatchChar + +* Purpose: +* See if a given character is of a specified type. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int MatchChar( char test, char type, const char *method, +* const char *class, const char *template, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function checks that the supplied test character belongs +* to the set of characters specified by the parameter "type". + +* Parameters: +* test +* The character to test. +* type +* The character specifying the set of acceptable characters. This +* should be one of the field type characters accepted by function +* Match (e.g. "d", "c" or "f"). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* template +* Pointer to the start of the whole template string, for use in error +* messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if the test character does not belongs to the +* specified character set, and one is returned if it does. + +* Notes: +* - An error is reported if the type specifier is not legal. +* - Zero is returned if an error has already occurred, or if ths +* function fails for any reason. +*/ + +/* Local Variables: */ + int ret; /* Returned flag */ + +/* Check global status. */ + ret = 0; + if( !astOK ) return ret; + +/* Check for "d" specifiers (digits). */ + if( type == 'd' ){ + ret = isdigit( (int) test ); + +/* Check for "c" specifiers (upper case letters). */ + } else if( type == 'c' ){ + ret = isupper( (int) test ); + +/* Check for "s" specifiers (any legal FITS keyword character). */ + } else if( type == 'f' ){ + ret = isFits( (int) test ); + +/* Report an error for any other specifier. */ + } else if( astOK ){ + ret = 0; + astError( AST__BDFMT, "%s(%s): Illegal field type or width " + "specifier '%c' found in filter template '%s'.", status, + method, class, type, template ); + } + +/* Return the answer. */ + return ret; +} + +static int MatchFront( const char *test, const char *temp, char *type, + int *extend, int *ntest, int *ntemp, + const char *method, const char *class, + const char *template, int *status ){ +/* +* Name: +* MatchFront + +* Purpose: +* Sees if the start of a test string matches the start of a template. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int MatchFront( const char *test, const char *temp, char *type, +* int *extend, int *ntest, int *ntemp, +* const char *method, const char *class, +* const char *template ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function looks for a match between the first field in the +* template string and the string at the start of the test string, +* using the syntax described in function Match. + +* Parameters: +* test +* Pointer to a null terminated string holding the keyword name to +* be tested. +* temp +* Pointer to a null terminated string holding the template. +* type +* Pointer to a location at which to return a character specifying the +* sort of field that was matched. This will be one of the legal field +* types accepted by Match (e.g. "d", "c" or "f"), or null (zero) if +* the first field in the template string was a literal character (i.e. +* did not start with a "%"). +* extend +* Pointer to a location at which to return a flag which will be non-zero +* if the further test characters could be matched by the first field in +* the template. This will be the case if the template field only +* specifies a minimum number of matching characters (i.e. if the field +* width can be extended). For instance, "%d" can be extended, but "%1d" +* cannot. +* ntest +* Pointer to a location at which to return the number of characters +* matched in the test string. This will be the minimum number allowed +* by the template field. +* ntemp +* Pointer to a location at which to return the number of characters +* read from the template string (i.e. the number of characters in the +* field specification). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* template +* Pointer to the start of the whole template string, for use in error +* messages. + +* Returned Value: +* Zero is returned if the test string starts with fewer than the +* minimum number of characters matching the template string, and one +* is returned if it does. + +* Notes: +* - Zero is returned if an error has already occurred, or if this +* function fails for any reason. +*/ + +/* Local Variables: */ + const char *a; /* Pointer to next test character */ + const char *b; /* Pointer to next template character */ + int i; /* Character index */ + int match; /* Does "test" match "temp"? */ + +/* Check global status. */ + if( !astOK ) return 0; + +/* Initialise pointers to the start of each string. */ + a = test; + b = temp; + +/* Initialise the returned value to indicate that the strings match. */ + match = 1; + +/* If the current character in the template is not a % sign, it must + match the current character in the test string (except for case). */ + if( *b != '%' ){ + if( toupper( (int) *b ) != toupper( (int) *a ) ) { + match = 0; + +/* If the characters match, return all the required information. */ + } else { + *type = 0; + *extend = 0; + *ntest = 1; + *ntemp = 1; + } + +/* If the current character of the template is a %, we need to match + a field. */ + } else { + *ntemp = 3; + +/* The next character in the template string determines the field width. + Get the lowest number of characters which must match in the test string, + and set a flag indicating if this lowest limit can be extended. */ + b++; + if( *b == '0' ){ + *ntest = 0; + *extend = 1; + } else if( *b == '1' ){ + *ntest = 1; + *extend = 0; + } else if( *b == '2' ){ + *ntest = 2; + *extend = 0; + } else if( *b == '3' ){ + *ntest = 3; + *extend = 0; + } else if( *b == '4' ){ + *ntest = 4; + *extend = 0; + } else if( *b == '5' ){ + *ntest = 5; + *extend = 0; + } else if( *b == '6' ){ + *ntest = 6; + *extend = 0; + } else if( *b == '7' ){ + *ntest = 7; + *extend = 0; + } else if( *b == '8' ){ + *ntest = 8; + *extend = 0; + } else if( *b == '9' ){ + *ntest = 9; + *extend = 0; + +/* If no field width was given, one or more test characters are matched. + Step back a character so that the current character will be re-used as + the type specifier. */ + } else { + *ntest = 1; + *extend = 1; + b--; + (*ntemp)--; + } + +/* The next template character gives the type of character which should + be matched. */ + b++; + *type = *b; + +/* Report an error if the template string ended within the field + specifier. */ + if( !*b ){ + match = 0; + astError( AST__BDFMT, "%s(%s): Incomplete field specifier found " + "at end of filter template '%s'.", status, method, class, + template ); + +/* Otherwise, check that the test string starts with the minimum allowed + number of characters matching the specified type. */ + } else { + for( i = 0; i < *ntest; i++ ){ + if( !MatchChar( *a, *type, method, class, template, status ) ){ + match = 0; + break; + } + a++; + } + } + } + +/* Return the answer. */ + return match; +} + +static void MarkCard( AstFitsChan *this, int *status ){ + +/* +* Name: +* MarkCard + +* Purpose: +* Mark the current card as having been read into an AST object. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void MarkCard( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The current card is marked as having been "provisionally used" in +* the construction of an AST object. If the Object is constructed +* succesfully, such cards are marked as having been definitely used, +* and they are then considered to have been removed from the FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan containing the list of cards. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The card remains the current card even though it is now marked +* as having been read. +*/ + int flags; + +/* Return if the global error status has been set, or the current card + is not defined. */ + if( !astOK || !this->card ) return; + +/* Set the PROVISIONALLY_USED flag in the current card, but only if the + PROTECTED flag is not set. */ + flags = ( (FitsCard *) this->card )->flags; + if( !( flags & PROTECTED ) ) { + ( (FitsCard *) this->card )->flags = flags | PROVISIONALLY_USED; + } +} + +static int MoveCard( AstFitsChan *this, int move, const char *method, + const char *class, int *status ){ + +/* +* Name: +* MoveCard + +* Purpose: +* Move the current card a given number of cards forward or backwards. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int MoveCard( AstFitsChan *this, int move, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The current card is increment by the given number of cards, ignoring +* cards which have been read into an AST object if the ignore_used flag +* is set non-zero. + +* Parameters: +* this +* Pointer to the FitsChan containing the list of cards. +* move +* The number of cards by which to move the current card. Positive +* values move towards the end-of-file. Negative values move +* towards the start of the file (i.e. the list head). +* method +* Pointer to string holding name of calling method. +* class +* Pointer to string holding object class. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of cards actually moved. This may not always be equal to +* the requested number (for instance, if the end or start of the +* FitsChan is encountered first). + +* Notes: +* - If the end-of-file is reached before the required number of +* cards have been skipped, the current card is set NULL, to indicate +* an end-of-file condition. +* - If the start of the file is reached before the required number of +* cards have been skipped, the current card is left pointing to the +* first usable card. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + FitsCard *card; /* The current card */ + FitsCard *card0; /* The previous non-deleted card */ + int moved; /* The number of cards moved by so far */ + +/* Return if the supplied object is NULL or the FitsChan is + empty, or zero movement is requested. */ + if( !this || !this->head || !move ) return 0; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Get a pointer to the current card. */ + card = (FitsCard *) this->card; + +/* Initialise the number of cards moved so far. */ + moved = 0; + +/* First deal with positive movements (towards the end-of-file). */ + if( move > 0 ){ + +/* Loop round moving on to the next card until the correct number of + moves have been made, or the end-of-file is reached. */ + while( moved < move && card ){ + +/* Get a pointer to the next card in the list, reporting an error if the + links are inconsistent. */ + card = GetLink( card, NEXT, method, class, status ); + +/* If we have moved past the last card and are now pointing back at the + list head, then indicate that we are at end-of-file by setting the + card pointer NULL. */ + if( (void *) card == this->head ){ + card = NULL; + +/* Otherwise, increment the number of cards moved. We ignore cards which + have been read into an AST object if the external "ignore_used" flag is + set. */ + } else if( card ){ + if( !CARDUSED(card) ) moved++; + } + } + +/* Now deal with negative movements (towards the list head), so long as + we are not currently at the list head. */ + } else if( (void *) card != this->head ){ + +/* If we are currently at end-of-file, replace the NULL pointer for the + current card with a pointer to the list head. The first step backwards + will make the last card the current card. */ + if( !card ) card = (FitsCard *) this->head; + +/* Loop round until the correct number of cards have been moved. */ + while( moved < -move && card ){ + +/* If cards which have been read into an AST object are to be included in the + count of moved cards, get a pointer to the previous card in the list, + reporting an error if the links are inconsistent. */ + if( !ignore_used ){ + card = GetLink( card, PREVIOUS, method, class, status ); + +/* If cards which have been read into an AST object are to be ignored... */ + } else { + +/* We need to find the previous card which has not been read into an AST + object. We do not search beyond the start of the list. */ + card0 = GetLink( card, PREVIOUS, method, class, status ); + while( card0 && CARDUSED(card0) && (void *) card0 != this->head ){ + card0 = GetLink( card0, PREVIOUS, method, class, status ); + } + +/* If no such card was found we leave the card where it is. */ + if( card0 && ( card0->flags & USED ) ) { + break; + +/* Otherwise, move back to card found above. */ + } else { + card = card0; + } + } + +/* Increment the number of cards moved. */ + moved++; + +/* If the current card is the list head, break out of the loop. */ + if( (void *) card == this->head ) break; + } + } + +/* Store the new current card. */ + this->card = (void *) card; + +/* Return the answer. */ + return moved; +} + +static double NearestPix( AstMapping *map, double val, int axis, int *status ){ +/* +* Name: +* NearestPix + +* Purpose: +* Find an axis value which corresponds to an integer pixel value. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* double NearestPix( AstMapping *map, double val, int axis, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The supplied axis value is transformed using the inverse of the +* supplied Mapping (other axes are given the value AST__BAD). The +* resulting axis values are rounded to the nearest whole number, and +* then transformed back using the supplied Mapping in the forward +* direction. If the nominated axis value is good, it is returned as +* the function value, otherwise the supplied value is returned unchanged. + +* Parameters: +* map +* A Mapping (usually the input coordinates will correspond to +* pixel coordinates). +* val +* A value for one of the outputs of the "map" Mapping. +* axis +* The index of the Mapping output to which "val" refers. +* status +* Pointer to the inherited status variable. + +* Retuned Value: +* The modified output axis value. +*/ + +/* Local Variables: */ + AstMapping *tmap; /* Mapping to be used */ + AstPointSet *pset1; /* Pixel coords PointSet */ + AstPointSet *pset2; /* WCS coords PointSet */ + double **ptr1; /* Pointer to data in pset1 */ + double **ptr2; /* Pointer to data in pset2 */ + double result; /* Returned value */ + int *ins; /* Array holding input axis indices */ + int i; /* Loop count */ + int nin; /* Number of Mapping inputs */ + int nout; /* Number of Mapping outputs */ + +/* Initialise. */ + result = val; + +/* Check inherited status, and that the supplied value is good. */ + if( !astOK || result == AST__BAD ) return result; + +/* If the supplied Mapping has no inverse, trying splitting off the + transformation for the required axis, which may have an inverse. + If succesful, use the 1-in,1-out Mapping returned by astMapSPlit + instead of the supplied Mapping, and adjust the axis index accordingly. */ + if( !astGetTranInverse( map ) ) { + astInvert( map ); + ins = astMapSplit( map, 1, &axis, &tmap ); + if( tmap ) { + astInvert( tmap ); + axis = 0; + } else { + tmap = astClone( map ); + } + ins = astFree( ins ); + astInvert( map ); + } else { + tmap = astClone( map ); + } + +/* If the Mapping still has no inverse, return the supplied value + unchanged. */ + if( astGetTranInverse( tmap ) ) { + +/* Get the number of input and output coordinates. */ + nin = astGetNin( tmap ); + nout = astGetNout( tmap ); + +/* Create PointSets to hold a single input position and the corresponding + output position. */ + pset1 = astPointSet( 1, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + pset2 = astPointSet( 1, nout, "", status ); + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + +/* Assign AST__BAD values to all output axes, except for the specified + axis, which is given the supplied axis value. */ + for( i = 0; i < nout; i++ ) ptr2[ i ][ 0 ] = AST__BAD; + ptr2[ axis ][ 0 ] = val; + +/* Transform this output position into an input position. */ + (void) astTransform( tmap, pset2, 0, pset1 ); + +/* Round all good axis values in the resulting input position to the nearest + integer. */ + for( i = 0; i < nin; i++ ) { + if( ptr1[ i ][ 0 ] != AST__BAD ) { + ptr1[ i ][ 0 ] = (int) ( ptr1[ i ][ 0 ] + 0.5 ); + } + } + +/* Transform this input position back into output coords. */ + (void) astTransform( tmap, pset1, 1, pset2 ); + +/* If the resulting axis value is good, return it. */ + if( ptr2[ axis ] [ 0 ] != AST__BAD ) result = ptr2[ axis ] [ 0 ]; + } + +/* Free resources. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + } + tmap = astAnnul( tmap ); + +/* Return the result. */ + return result; +} + +static void NewCard( AstFitsChan *this, const char *name, int type, + const void *data, const char *comment, int flags, + int *status ){ + +/* +* Name: +* NewCard + +* Purpose: +* Insert a new card in front of the current card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void NewCard( AstFitsChan *this, const char *name, int type, +* const void *data, const char *comment, int flags, +* int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The supplied keyword name, data type and value, and comment are +* stored in a new FitsCard structure, and this structure is +* inserted into the circular linked list stored in the supplied +* FitsChan. It is inserted in front of the current card. + +* Parameters: +* this +* Pointer to the FitsChan containing the list of cards. +* name +* Pointer to a string holding the keyword name of the new card. +* type +* An integer value representing the data type of the keyword. +* data +* Pointer to the data associated with the keyword. +* comment +* Pointer to a null-terminated string holding a comment. +* flags +* The flags to assign to the card. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The new card is inserted into the list in front of the current card, +* so that the "next" link from the new card points to the current card. +* If the FitsChan is currently at end-of-file (indicated by a NULL +* pointer being stored for the current card), then the card is appended +* to the end of the list. The pointer to the current card is left +* unchanged. +* - Keyword names are converted to upper case before being stored. +* - Any trailing white space in a string value is saved as supplied. +* - Logical values are converted to zero or one before being stored. +* - The "comment" and/or "data" pointers may be supplied as NULL. +*/ + +/* Local Variables: */ + FitsCard *new; /* Pointer to the new card */ + FitsCard *prev; /* Pointer to the previous card in the list */ + char *b; /* Pointer to next stored character */ + const char *a; /* Pointer to next supplied character */ + int lval; /* Logical data value restricted to 0 or 1 */ + int nc; /* No. of characters to store */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get memory to hold the new FitsCard structure. */ + new = (FitsCard *) astMalloc( sizeof( FitsCard ) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Copy the keyword name, converting to upper case. */ + a = name; + b = new->name; + while( *a ) *(b++) = (char) toupper( (int) *(a++) ); + *b = 0; + +/* Ensure that a KeyMap exists to hold the keywords currently in the + FitsChan. */ + if( !this->keywords ) this->keywords = astKeyMap( " ", status ); + +/* Add the keyword name to the KeyMap. The value associated with the + KeyMap entry is not used and is set arbitrarily to zero. */ + astMapPut0I( this->keywords, new->name, 0, NULL ); + +/* Copy the data type. */ + new->type = type; + +/* Copy any data (ignore any data supplied for an UNDEF value). */ + if( data && type != AST__UNDEF ){ + +/* Logical values are converted to zero or one before being stored. */ + if( type == AST__LOGICAL ){ + lval = *( (int *) data ) ? 1 : 0; + new->size = sizeof( int ); + new->data = astStore( NULL, (void *) &lval, sizeof( int ) ); + +/* String values... */ + } else if( type == AST__STRING || type == AST__CONTINUE ){ + +/* Find the number of characters excluding the trailing null character. */ + nc = strlen( data ); + +/* Store the string, reserving room for a terminating null. */ + new->size = (size_t)( nc + 1 ); + new->data = astStore( NULL, (void *) data, (size_t)( nc + 1 ) ); + +/* Terminate it. */ + ( (char *) new->data)[ nc ] = 0; + +/* Other types are stored as supplied. */ + } else if( type == AST__INT ){ + new->size = sizeof( int ); + new->data = astStore( NULL, (void *) data, sizeof( int ) ); + } else if( type == AST__FLOAT ){ + new->size = sizeof( double ); + new->data = astStore( NULL, (void *) data, sizeof( double ) ); + } else if( type == AST__COMPLEXF ){ + if( *( (double *) data ) != AST__BAD ) { + new->size = 2*sizeof( double ); + new->data = astStore( NULL, (void *) data, 2*sizeof( double ) ); + } else { + nc = strlen( BAD_STRING ); + new->size = (size_t)( nc + 1 ); + new->data = astStore( NULL, BAD_STRING, (size_t)( nc + 1 ) ); + ( (char *) new->data)[ nc ] = 0; + } + } else if( type == AST__COMPLEXI ){ + new->size = 2*sizeof( int ); + new->data = astStore( NULL, (void *) data, 2*sizeof( int ) ); + } else { + new->size = 0; + new->data = NULL; + } + } else { + new->size = 0; + new->data = NULL; + } + +/* Find the first non-blank character in the comment, and find the used + length of the remaining string. We retain leading and trailing white + space if the card is a COMMENT card. */ + if( comment ){ + a = comment; + if( type != AST__COMMENT ) { + while( isspace( *a ) ) a++; + nc = ChrLen( a, status ); + } else { + nc = strlen( a ); + } + } else { + nc = 0; + } + +/* Copy any comment, excluding leading and trailing white space unless + this is a COMMENT card */ + if( nc > 0 ){ + new->comment = astStore( NULL, (void *) a, (size_t)( nc + 1 ) ); + ( (char *) new->comment)[ nc ] = 0; + } else { + new->comment = NULL; + } + +/* Set the supplied flag values. */ + new->flags = flags; + +/* Insert the copied card into the list, in front of the current card. If + the current card is the list head, make the new card the list head. */ + if( this->card ){ + prev = ( ( FitsCard *) this->card )->prev; + ( ( FitsCard *) this->card )->prev = new; + new->prev = prev; + prev->next = new; + new->next = (FitsCard *) this->card; + if( this->card == this->head ) this->head = (void *) new; + +/* If the FitsChan is at end-of-file, append the new card to the end of + the list (i.e. insert it just before the list head). */ + } else { + if( this->head ){ + prev = ( (FitsCard *) this->head )->prev; + ( (FitsCard *) this->head )->prev = new; + new->prev = prev; + prev->next = new; + new->next = (FitsCard *) this->head; + +/* If there are no cards in the list, start a new list. */ + } else { + new->prev = new; + new->next = new; + this->head = (void *) new; + this->card = NULL; + } + } + } + +/* Return. */ + return; +} + +static AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode, + FitsStore *store, int i, char s, + AstSpecFrame *specfrm, const char *method, + const char *class, int *status ) { + +/* +* Name: +* NonLinSpecWcs + +* Purpose: +* Create a Mapping describing a FITS-WCS non-linear spectral algorithm + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode, +* FitsStore *store, int i, char s, +* AstSpecFrame *specfrm, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function uses the contents of the supplied FitsStore to create +* a Mapping which goes from Intermediate World Coordinate (known as "w" +* in the context of FITS-WCS paper III) to the spectral system +* described by the supplied SpecFrame. +* +* The returned Mapping implements the non-linear "X2P" algorithms +* described in FITS-WCS paper III. The axis is linearly sampled in +* system "X" but expressed in some other system (specified by the +* supplied SpecFrame). + +* Parameters: +* this +* Pointer to the FitsChan. +* algcode +* Pointer to a string holding the non-linear "-X2P" code for the +* required algorithm. This includes aleading "-" character. +* store +* Pointer to the FitsStore structure holding the values to use for +* the WCS keywords. +* i +* The zero-based index of the spectral axis within the FITS header +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* specfrm +* Pointer to the SpecFrame. This specified the "S" system - the +* system in which the CRVAL kewyords (etc) are specified. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a Mapping, or NULL if an error occurs. +*/ + +/* Local Variables: */ + AstFrameSet *fs; + AstMapping *map1; + AstMapping *ret; + AstSpecFrame *xfrm; + AstMapping *map2; + char buf[ 100 ]; + char pc; + double crv; + double ds; + double in_a; + double in_b; + double out_a; + double out_b; + int ok; + int s_sys; + +/* Check the global status. */ + ret = NULL; + if( !astOK ) return ret; + +/* Identify the spectral "X" system within the "X2P" algorithm code, and + create a SpecFrame describing the X system ("X" is the system in + which the axis is linearly sampled). This is done by copying the + supplied SpecFrame and then setting its System attribute. Copying + the supplied SpecFrame ensures that all the other attributes (RestFreq, + etc.) are set correctly. */ + ok = 1; + xfrm = astCopy( specfrm ); + if( algcode[ 1 ] == 'F' ) { + astSetSystem( xfrm, AST__FREQ ); + astSetUnit( xfrm, 0, "Hz" ); + } else if( algcode[ 1 ] == 'W' ) { + astSetSystem( xfrm, AST__WAVELEN ); + astSetUnit( xfrm, 0, "m" ); + } else if( algcode[ 1 ] == 'V' ) { + astSetSystem( xfrm, AST__VREL ); + astSetUnit( xfrm, 0, "m/s" ); + } else if( algcode[ 1 ] == 'A' ) { + astSetSystem( xfrm, AST__AIRWAVE ); + astSetUnit( xfrm, 0, "m" ); + } else { + ok = 0; + } + +/* If the X system was identified, find a Mapping from the "S" (specfrm) + system to the X system. */ + map1 = NULL; + if( ok ) { + ok = 0; + fs = astConvert( specfrm, xfrm, "" ); + if( fs ) { + map1 = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + ok = 1; + } + +/* Issue a warning if the "P" system is not the correct one for the given + "S" system. We can however continue, sine AST interprets illegal "P" + systems correctly. */ + pc = 0; + s_sys = astGetSystem( specfrm ); + if( s_sys == AST__FREQ || s_sys == AST__ENERGY || + s_sys == AST__WAVENUM || s_sys == AST__VRADIO ) { + pc = 'F'; + } else if( s_sys == AST__WAVELEN || s_sys == AST__VOPTICAL || + s_sys == AST__REDSHIFT ){ + pc = 'W'; + } else if( s_sys == AST__AIRWAVE ) { + pc = 'A'; + } else if( s_sys == AST__BETA || s_sys == AST__VREL ) { + pc = 'V'; + } else if( astOK ) { + pc = algcode[ 3 ]; + astError( AST__INTER, "%s: Function NonLinSpecWcs does not yet " + "support spectral axes of type %s (internal AST " + "programming error).", status, method, astGetC( specfrm, "System" ) ); + } + if( algcode[ 3 ] != pc ) { + sprintf( buf, "The spectral CTYPE value %s%s is not legal - " + "using %s%.3s%c instead.", astGetC( specfrm, "System" ), + algcode, astGetC( specfrm, "System" ), algcode, pc ); + Warn( this, "badctype", buf, method, class, status ); + } + } + +/* If succesfull, use this Mapping to find the reference value (CRVAL) + in the "X" system. */ + if( ok ) { + +/* Get the CRVAL value for the spectral axis (this will be in the S system). */ + crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( crv == AST__BAD ) crv = 0.0; + +/* Convert it to the X system. */ + astTran1( map1, 1, &crv, 1, &crv ); + +/* Invert this Mapping so that it forward transformation goes from X to S. */ + astInvert( map1 ); + +/* Find the rate of change of S with respect to X (dS/dX) at the reference + point (x = crv). */ + ds = astRate( map1, &crv, 0, 0 ); + if( ds != AST__BAD && ds != 0.0 ) { + +/* FITS-WCS paper III says that dS/dw must be 1.0 at the reference point. + Therefore dX/dw = dX/dS at the reference point. Also, since the spectral + axis is linear in X, dX/dw must be constant. Therefore the Mapping from + IWC to X is a WinMap which scales the IWC axis ("w") by dX/dw and adds + on the X value at the reference point. */ + if( crv != 0.0 ) { + in_a = 0.0; + out_a = crv; + in_b = crv*ds; + out_b = 2.0*crv; + map2 = (AstMapping *) astWinMap( 1, &in_a, &in_b, &out_a, &out_b, "", status ); + } else { + map2 = (AstMapping *) astZoomMap( 1, 1.0/ds, "", status ); + } + +/* The Mapping to be returned is the concatenation of the above Mapping + (from w to X) with the Mapping from X to S. */ + ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status ); + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + } + } + xfrm = astAnnul( xfrm ); + +/* Return the result */ + return ret; +} + +static double *OrthVector( int n, int m, double **in, int *status ){ +/* +* Name: +* OrthVector + +* Purpose: +* Find a unit vector which is orthogonal to a set of supplied vectors. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* double *OrthVector( int n, int m, double **in, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A set of M vectors is supplied, each vector being N-dimensional. +* It is assumed that M < N and that the supplied vectors span M +* axes within the N dimensional space. An N-dimensional unit vector is +* returned which is orthogonal to all the supplied vectors. +* +* The required vector is orthogonal to all the supplied vectors. +* Therefore the dot product of the required vector with each of the +* supplied vectors must be zero. This gives us M equations of the + +* form: +* +* a1*r1 + a2*r2 + a3*r3 + .... + aN*rN = 0.0 +* b1*r1 + b2*r2 + b3*r3 + .... + bN*rN = 0.0 +* ... +* +* where (a1,a2,..,aN), (b1,b2,..,bN), ... are the supplied vectors +* and (r1,r2,...,rN) is the required vector. Since M is less +* than N the system of linear simultaneous equations is under +* specified and we need to assign arbitrary values to some of the +* components of the required vector in order to allow the equations +* to be solved. We arbitrarily assume that 1 element of the required +* vector has value 1.0 and (N-M-1) have value zero. The selection of +* *which* elements to set constant is based on the magnitudes of the +* columns of coefficients (a1,b1...), (a2,b2,...), etc. The M components +* of the required vector which are *not* set constant are the ones which +* have coefficient columns with the *largest* magnitude. This choice is +* made in order to minimise the risk of the remaining matrix of +* coefficients being singular (for instance, if a component of the +* required vector has a coefficient of zero in every supplied vector +* then the column magnitude will be zero and that component will be +* set to 1.0). After choosing the M largest columns, the largest +* remaining column is assigned a value of 1.0 in the required vector, +* and all other columns are assigned the value zero in the required + +* vector. This means that the above equations becomes: +* +* a1*r1 + a2*r2 + a3*r3 + .... + aM*rM = -aM+1 +* b1*r1 + b2*r2 + b3*r3 + .... + bM*rM = -bM+1 +* ... +* +* Where the indices are now not direct indices into the supplied and +* returned vectors, but indices into an array of indices which have +* been sorted into column magnitude order. This is now a set of MxM + +* simultaneous linear equations which we can solve using palDmat: +* +* MAT.R = V +* +* where MAT is the the matrix of columns (coefficients) on the left +* hand side of the above set of simultaneous equations, R is the +* required vector (just the components which have *not* been set +* constant), and V is a constant vector equal to the column of values +* on the right hand side in the above set of simultaneous equations. +* The palDmat function solves this equation to obtain R. + +* Parameters: +* n +* The number of dimensions +* m +* The number of supplied vectors. +* in +* A pointer to an array with "m" elements, each element being a +* pointer to an array with "n" elements. Each of these "n" element +* array holds one of the supplied vectors. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The pointer to some newly allocated memory holding the returned N +* dimensional unit vector. The memory should be freed using astFree when +* no longer needed. + +* Notes: +* - NULL is returned if an error occurs. +* - NULL is returned (without error) if the required vector cannot +* be found (.e.g becuase the supplied M vectors span less than M axes). +*/ + +/* Local Variables: */ + double *colmag; + double *d; + double *e; + double *mat; + double *mel; + double *ret; + double *rhs; + double det; + double sl; + int *colperm; + int *iw; + int done; + int i; + int ih; + int ii; + int il; + int j; + int sing; + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Return if any of the M supplied vectors are NULL. */ + for( i = 0; i < m; i++ ) { + if( !in[ i ] ) return ret; + } + +/* Allocate rquired memory. */ + ret = astMalloc( sizeof( double )*(size_t) n ); + rhs = astMalloc( sizeof( double )*(size_t) m ); + mat = astMalloc( sizeof( double )*(size_t) m*m ); + iw = astMalloc( sizeof( int )*(size_t) m ); + colmag = astMalloc( sizeof( double )*(size_t) n ); + colperm = astMalloc( sizeof( int )*(size_t) n ); + +/* Check memory can be used safely. */ + if( astOK ) { + +/* Find the magnitude of each column of coefficients in the full set of + simultaneous linear equations (before setting any components of the + required vector constant). Also initialise the column permutation array + to indicate that the columns are in their original order. The outer + loop loops through the columns and the inner loop loops through rows + (i.e. equations). */ + for( i = 0; i < n; i++ ) { + colperm[ i ] = i; + colmag[ i ] = 0.0; + for( j = 0; j < m; j++ ) { + colmag[ i ] += in[ j ][ i ]*in[ j ][ i ]; + } + } + +/* Now re-arrange the column indices within the permutation array so that + they are in order of decreasing ciolumn magnitude (i.e. colperm[0] will + be left holding the index of the column with the largest magnitude). A + simple bubble sort is used. */ + ii = 1; + done = 0; + while( !done ) { + done = 1; + for( i = ii; i < n; i++ ) { + ih = colperm[ i ]; + il = colperm[ i - 1 ]; + if( colmag[ ih ] > colmag[ il ] ) { + colperm[ i ] = il; + colperm[ i - 1 ] = ih; + done = 0; + } + } + ii++; + } + +/* The first M elements in "colperm" now hold the indices of the + columns which are to be used within the MAT matrix, the next element + of "colperm" hold the index of the column which is to be included in the + V vector (other elements hold the indices of the columns which are + being ignored because they will be mutiplied by a value of zero - the + assumed value of the corresponding components of the returned vector). We + now copy the these values into arrays which can be passed to palDmat. + First, initialise a pointer used to step through the mat array. */ + mel = mat; + +/* Loop through all the supplied vectors. Get a pointer to the first + element of the vector. */ + for( i = 0; i < m; i++ ) { + d = in[ i ]; + +/* Copy the required M elements of this supplied vector into the work array + which will be passed to palDmat. */ + for( j = 0; j < m; j++ ) *(mel++) = d[ colperm[ j ] ]; + +/* Put the next right-hand side value into the "rhs" array. */ + rhs[ i ] = -d[ colperm[ m ] ]; + } + +/* Use palDmat to find the first M elements of the returned array. These + are stored in "rhs", over-writing the original right-hand side values. */ + palDmat( m, mat, rhs, &det, &sing, iw ); + +/* If the supplied vectors span fewer than M axes, the above call will fail. + In this case, annul the returned vector. */ + if( sing != 0 ) { + ret = astFree( ret ); + +/* If succesful, copy the M elements of the solution vector into the + required M elements of the returned vector. Also find the squared length + of the vector. */ + } else { + sl = 0.0; + e = rhs; + for( j = 0; j < m; j++ ) { + sl += (*e)*(*e); + ret[ colperm[ j ] ] = *(e++); + } + +/* Put 1.0 into the next element of the returned vector. */ + sl += 1.0; + ret[ colperm[ m ] ] = 1.0; + +/* Fill up the rest of the returned vector with zeros. */ + for( j = m + 1; j < n; j++ ) ret[ colperm[ j ] ] = 0.0; + +/* Normalise the returned vector so that it is a unit vector.Also ensure + that any zeros are "+0.0" insteasd of "-0.0". */ + e = ret; + sl = sqrt( sl ); + for( j = 0; j < n; e++,j++ ) { + *e /= sl; + if( *e == 0.0 ) *e = 0.0; + } + } + } + +/* Free workspace. */ + rhs = astFree( rhs ); + mat = astFree( mat ); + iw = astFree( iw ); + colmag = astFree( colmag ); + colperm = astFree( colperm ); + +/* Free the returned vector if an error has occurred. */ + if( !astOK ) ret = astFree( ret ); + +/* Return the answer. */ + return ret; +} + +static double **OrthVectorSet( int n, int m, double **in, int *status ){ +/* +* Name: +* OrthVectorSet + +* Purpose: +* Find a set of mutually orthogonal vectors. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* double **OrthVectorSet( int n, int m, double **in, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A set of M vectors is supplied, each vector being N-dimensional. +* It is assumed that the supplied vectors span M axes within the +* N dimensional space. A pointer to a set of N vectors is returned. +* The first M returned vectors are copies of the M supplied vectors. +* The remaining returned vectors are unit vectors chosen to be +* orthogonal to all other vectors in the returned set. + +* Parameters: +* n +* The number of dimensions +* m +* The number of supplied vectors. +* in +* A pointer to an array with "m" elements, each element being a +* pointer to an array with "n" elements. Each of these "n" element +* array holds one of the supplied vectors. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The pointer to some newly allocated memory holding the returned N +* vectors. The pointer locates an array of N elements, each of which +* is a pointer to an array holding the N elements of a single vector. +* The memory (including the inner pointers) should be freed using +* astFree when no longer needed. + +* Notes: +* - NULL is returned if an error occurs. +* - NULL is returned (without error) if the required vectors cannot +* be found (e.g. becuase the supplied M vectors span less than M axes). +*/ + +/* Local Variables: */ + double **ret; + int i; + int bad; + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Allocate required memory. */ + ret = astMalloc( sizeof( double * )*(size_t) n ); + +/* Check memory can be used safely. */ + bad = 0; + if( astOK ) { + +/* Copy the supplied vectors into the returned array. */ + for( i = 0; i < m; i++ ) { + ret[ i ] = astStore( NULL, in[ i ], sizeof( double )*n ); + } + +/* For the remaining vectors, find a vector which is orthogonal to all + the vectors currently in the returned set. */ + for( ; i < n; i++ ) { + ret[ i ] = OrthVector( n, i, ret, status ); + if( !ret[ i ] ) bad = 1; + } + } + +/* Free the returned vectors if an error has occurred. */ + if( bad || !astOK ) { + for( i = 0; ret && i < n; i++ ) ret[ i ] = astFree( ret[ i ] ); + ret = astFree( ret ); + } + +/* Return the answer. */ + return ret; +} + +static AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, + int *wperm, char s, FitsStore *store, + double *crvals, int *axis_done, + const char *method, const char *class, + int *status ){ + +/* +* Name: +* OtherAxes + +* Purpose: +* Add values to a FitsStore describing unknown axes in a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim, +* int *wperm, char s, FitsStore *store, +* double *crvals, int *axis_done, +* const char *method, const char *class, +* int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* FITS WCS keyword values are added to the supplied FitsStore which +* describe any as yet undescribed axes in the supplied FrameSet. These +* axes are assumed to be linear and to follow the conventions +* of FITS-WCS paper I (if in fact they are not linear, then the +* grid->iwc mapping determined by MakeIntWorld will not be linear and +* so the axes will be rejected). +* +* Note, this function does not store values for keywords which define +* the transformation from pixel coords to Intermediate World Coords +* (CRPIX, PC and CDELT), but a Mapping is returned which embodies these +* values. This Mapping is from the current Frame in the FrameSet (WCS +* coords) to a Frame representing IWC. The IWC Frame has the same number +* of axes as the WCS Frame which may be greater than the number of base +* Frame (i.e. pixel) axes. + +* Parameters: +* this +* Pointer to the FitsChan. +* fs +* Pointer to the FrameSet. The base Frame should represent FITS pixel +* coordinates, and the current Frame should represent FITS WCS +* coordinates. The number of base Frame axes should not exceed the +* number of current Frame axes. +* dim +* An array holding the image dimensions in pixels. AST__BAD can be +* supplied for any unknwon dimensions. +* wperm +* Pointer to an array of integers with one element for each axis of +* the current Frame. Each element holds the zero-based +* index of the FITS-WCS axis (i.e. the value of "i" in the keyword +* names "CTYPEi", "CRVALi", etc) which describes the Frame axis. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* store +* The FitsStore in which to store the FITS WCS keyword values. +* crvals +* Pointer to an array holding the default CRVAL value for each +* axis in the WCS Frame. +* axis_done +* An array of flags, one for each Frame axis, which indicate if a +* description of the corresponding axis has yet been stored in the +* FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If any axis descriptions were added to the FitsStore, a Mapping from +* the current Frame of the supplied FrameSet, to the IWC Frame is returned. +* Otherwise, a UnitMap is returned. Note, the Mapping only defines the IWC +* transformation for the described axes. Any other (previously +* described) axes are passed unchanged by the returned Mapping. +*/ + +/* Local Variables: */ + AstFitsTable *table; /* Pointer to structure holding -TAB table info */ + AstFrame *wcsfrm; /* WCS Frame within FrameSet */ + AstMapping *axmap; /* Mapping from WCS to IWC */ + AstMapping *map; /* FITS pixel->WCS Mapping */ + AstMapping *ret; /* Returned Mapping */ + AstMapping *tmap0; /* Pointer to a temporary Mapping */ + AstMapping *tmap1; /* Pointer to a temporary Mapping */ + AstPermMap *pm; /* PermMap pointer */ + AstPointSet *pset1; /* PointSet holding central pixel position */ + AstPointSet *pset2; /* PointSet holding reference WCS position */ + char buf[80]; /* Text buffer */ + const char *lab; /* Pointer to axis Label */ + const char *sym; /* Pointer to axis Symbol */ + double **ptr1; /* Pointer to data for pset1 */ + double **ptr2; /* Pointer to data for pset2 */ + double *lbnd_p; /* Pointer to array of lower pixel bounds */ + double *ubnd_p; /* Pointer to array of upper pixel bounds */ + double crval; /* The value for the FITS CRVAL keyword */ + int *inperm; /* Pointer to permutation array for input axes */ + int *outperm; /* Pointer to permutation array for output axes */ + int extver; /* Table version number for -TAB headers */ + int fits_i; /* FITS WCS axis index */ + int i; /* Loop count */ + int iax; /* WCS Frame axis index */ + int icolindex; /* Index of table column holding index vector */ + int icolmain; /* Index of table column holding main coord array */ + int interp; /* Interpolation method for look-up tables */ + int log_axis; /* Is the axis logarithmically spaced? */ + int nother; /* Number of axes still to be described */ + int npix; /* Number of pixel axes */ + int nwcs; /* Number of WCS axes */ + int tab_axis; /* Can the axis be described by the -TAB algorithm? */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Get the number of WCS axes. */ + nwcs = astGetNaxes( fs ); + +/* Count the number of WCS axes which have not yet been described. */ + nother = 0; + for( iax = 0; iax < nwcs; iax++ ) { + if( ! axis_done[ iax ] ) nother++; + } + +/* Only proceed if there are some axes to described. */ + if( nother ) { + +/* Get a pointer to the WCS Frame. */ + wcsfrm = astGetFrame( fs, AST__CURRENT ); + +/* Get a pointer to the pixel->wcs Mapping. */ + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Store the number of pixel and WCS axes. */ + npix = astGetNin( fs ); + nwcs = astGetNout( fs ); + +/* Store the upper and lower pixel bounds. */ + lbnd_p = astMalloc( sizeof( double )*(size_t) npix ); + ubnd_p = astMalloc( sizeof( double )*(size_t) npix ); + if( astOK ) { + for( iax = 0; iax < npix; iax++ ) { + lbnd_p[ iax ] = 1.0; + ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500; + } + } + +/* Transform the central pixel coords into WCS coords */ + pset1 = astPointSet( 1, npix, "", status ); + ptr1 = astGetPoints( pset1 ); + pset2 = astPointSet( 1, nwcs, "", status ); + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + for( iax = 0; iax < npix; iax++ ) { + ptr1[ iax ][ 0 ] = ( dim[ iax ] != AST__BAD ) ? floor( 0.5*dim[ iax ] ) : 1.0; + } + (void) astTransform( map, pset1, 1, pset2 ); + } + +/* Loop round all WCS axes, producing descriptions of any axes which have not + yet been described. */ + for( iax = 0; iax < nwcs && astOK; iax++ ) { + if( ! axis_done[ iax ] ) { + +/* Get the (one-based) FITS WCS axis index to use for this Frame axis. */ + fits_i = wperm[ iax ]; + +/* Use the supplied default CRVAL value. If bad, use the WCS value + corresponding to the central pixel found above (if this value is bad, + abort). */ + crval = crvals ? crvals[ iax ] : AST__BAD; + if( crval == AST__BAD ) crval = ptr2[ iax ][ 0 ]; + if( crval == AST__BAD ) { + break; + } else { + SetItem( &(store->crval), fits_i, 0, s, crval, status ); + } + +/* Initialise flags indicating the type of the axis. */ + log_axis = 0; + tab_axis = 0; + +/* Get the table version number to use if we end up using the -TAB + algorithm. This is the set value of the TabOK attribute (if positive). */ + extver = astGetTabOK( this ); + +/* See if the axis is linear. If so, create a ShiftMap which subtracts off + the CRVAL value. */ + + if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) { + crval = -crval; + tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status ); + axmap = AddUnitMaps( tmap0, iax, nwcs, status ); + tmap0 = astAnnul( tmap0 ); + crval = -crval; + +/* If it is not linear, see if it is logarithmic. If the "log" algorithm is + appropriate (as defined in FITS-WCS paper III), the supplied Frame (s) is + related to pixel coordinate (p) by + s = Sr.EXP( a*p - b ). If this + is the case, the log of s will be linearly related to pixel coordinates. + Test this. If the test is passed a Mapping is returned from WCS to IWC. */ + } else if( (axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p, + crval, status ) ) ) { + log_axis = 1; + +/* If it is not linear or logarithmic, and the TabOK attribute is + non-zero, describe it using the -TAB algorithm. */ + } else if( extver > 0 ){ + +/* Get any pre-existing FitsTable from the FitsStore. This is the table + in which the tabular data will be stored (if the Mapping can be expressed + in -TAB form). */ + if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL; + +/* See if the Mapping can be expressed in -TAB form. */ + tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i, + &table, &icolmain, &icolindex, &interp, + status ); + if( tmap0 ) { + tab_axis = 1; + +/* The values stored in the table index vector are GRID coords. So we + need to ensure that IWC are equivalent to GRID coords. So set CRVAL + to zero. */ + crval = 0.0; + +/* Store TAB-specific values in the FitsStore. First the name of the + FITS binary table extension holding the coordinate info. */ + SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status ); + +/* Next the table version number. This is the set (positive) value for the + TabOK attribute. */ + SetItem( &(store->pv), fits_i, 1, s, extver, status ); + +/* Also store the table version in the binary table header. */ + astSetFitsI( table->header, "EXTVER", extver, + "Table version number", 0 ); + +/* Next the name of the table column containing the main coords array. */ + SetItemC( &(store->ps), fits_i, 1, s, + astColumnName( table, icolmain ), status ); + +/* Next the name of the column containing the index array */ + if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s, + astColumnName( table, icolindex ), status ); + +/* The interpolation method (an AST extension to the published -TAB + algorithm, communicated through the QVi_4a keyword). */ + SetItem( &(store->pv), fits_i, 4, s, interp, status ); + +/* Also store the FitsTable itself in the FitsStore. */ + astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL ); + +/* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for + the -TAB algorithm). First, get a Mapping that combines the TAB axis + Mapping( tmap0) in parallel with one or two UnitMaps in order to put + the TAB axis at the required index. */ + tmap1 = AddUnitMaps( tmap0, iax, nwcs, status ); + +/* Now get a PermMap that permutes the WCS axes into the FITS axis order. */ + inperm = astMalloc( sizeof( double )*nwcs ); + outperm = astMalloc( sizeof( double )*nwcs ); + if( astOK ) { + for( i = 0; i < nwcs; i++ ) { + inperm[ i ] = wperm[ i ]; + outperm[ wperm[ i ] ] = i; + } + } + pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "", + status ); + +/* Combine these two Mappings in series, to get the Mapping from WCS to + IWC. */ + axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ", + status ); + +/* Free resources. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + pm = astAnnul( pm ); + tmap0 = astAnnul( tmap0 ); + tmap1 = astAnnul( tmap1 ); + } + if( table ) table = astAnnul( table ); + } + +/* If the axis cannot be described by any of the above methods, we + pretend it is linear. This will generate a non-linear PIXEL->IWC + mapping later (in MakeIntWorld) which will cause the write operation + to fail. */ + if( !axmap ) { + crval = -crval; + tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status ); + axmap = AddUnitMaps( tmap0, iax, nwcs, status ); + tmap0 = astAnnul( tmap0 ); + crval = -crval; + } + +/* Combine the Mapping for this axis in series with those of earlier axes. */ + if( ret ) { + tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status ); + (void) astAnnul( ret ); + ret = tmap0; + } else { + ret = astClone( axmap ); + } + +/* Get axis label and symbol. */ + sym = astGetSymbol( wcsfrm, iax ); + lab = astGetLabel( wcsfrm, iax ); + +/* The axis symbols are taken as the CTYPE values. Append "-LOG" or "-TAB" if + the axis is logarithmic or tabular. */ + if( sym && strlen( sym ) ) { + (void) sprintf( buf, "%s", sym ); + } else { + (void) sprintf( buf, "AXIS%d", iax + 1 ); + } + if( log_axis ) { + SetAlgCode( buf, "-LOG", status ); + } else if( tab_axis ) { + SetAlgCode( buf, "-TAB", status ); + } + SetItemC( &(store->ctype), fits_i, 0, s, buf, status ); + +/* The axis labels are taken as the comment for the CTYPE keywords and as + the CNAME keyword (but only if a label has been set and is different to + the symbol). */ + if( lab && lab[ 0 ] && astTestLabel( wcsfrm, iax ) && strcmp( sym, lab ) ) { + SetItemC( &(store->ctype_com), fits_i, 0, s, (char *) lab, status ); + SetItemC( &(store->cname), fits_i, 0, s, (char *) lab, status ); + } else { + sprintf( buf, "Type of co-ordinate on axis %d", iax + 1 ); + SetItemC( &(store->ctype_com), fits_i, 0, s, buf, status ); + } + +/* If a value has been set for the axis units, use it as CUNIT. */ + if( astTestUnit( wcsfrm, iax ) ){ + SetItemC( &(store->cunit), fits_i, 0, s, (char *) astGetUnit( wcsfrm, iax ), status ); + } + +/* Indicate this axis has now been described. */ + axis_done[ iax ] = 1; + +/* Release Resources. */ + axmap = astAnnul( axmap ); + } + } + +/* Release Resources. */ + wcsfrm = astAnnul( wcsfrm ); + map = astAnnul( map ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + lbnd_p = astFree( lbnd_p ); + ubnd_p = astFree( ubnd_p ); + } + +/* If we have a Mapping to return, simplify it. Otherwise, create + a UnitMap to return. */ + if( ret ) { + tmap0 = ret; + ret = astSimplify( tmap0 ); + tmap0 = astAnnul( tmap0 ); + } else { + ret = (AstMapping *) astUnitMap( nwcs, "", status ); + } + +/* Return the result. */ + return ret; +} + +static int PCFromStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ + +/* +* Name: +* PCFromStore + +* Purpose: +* Store WCS keywords in a FitsChan using FITS-PC encoding. + +* Type: +* Private function. + +* Synopsis: +* int PCFromStore( AstFitsChan *this, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using FITS-PC encoding. +* +* Zero is returned if the primary axis descriptions cannot be produced. +* Whether or not secondary axis descriptions can be produced does not +* effect the returned value (i.e. failure to produce a specific set of +* secondary axes does not prevent other axis descriptions from being +* produced). + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + char *comm; /* Pointer to comment string */ + char *cval; /* Pointer to string keyword value */ + char combuf[80]; /* Buffer for FITS card comment */ + char keyname[10]; /* Buffer for keyword name string */ + char primsys[20]; /* Buffer for primnary RADECSYS value */ + char type[MXCTYPELEN];/* Buffer for CTYPE value */ + char s; /* Co-ordinate version character */ + char sign[2]; /* Fraction's sign character */ + char sup; /* Upper limit on s */ + double *c; /* Pointer to next array element */ + double *d; /* Pointer to next array element */ + double *matrix; /* Pointer to Frame PC/CD matrix */ + double *primpc; /* Pointer to primary PC/CD matrix */ + double fd; /* Fraction of a day */ + double mjd99; /* MJD at start of 1999 */ + double primdt; /* Primary mjd-obs value */ + double primeq; /* Primary equinox value */ + double primln; /* Primary lonpole value */ + double primlt; /* Primary latpole value */ + double primpv[10]; /* Primary projection parameter values */ + double val; /* General purpose value */ + int axlat; /* Index of latitude FITS WCS axis */ + int axlon; /* Index of longitude FITS WCS axis */ + int axspec; /* Index of spectral FITS WCS axis */ + int i; /* Axis index */ + int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ + int is; /* Co-ordinate version index */ + int iymdf[ 4 ]; /* Year, month, date, fractional day */ + int j; /* Axis index */ + int jj; /* SlaLib status */ + int m; /* Parameter index */ + int maxm; /* Upper limit on m */ + int naxis; /* No. of axes */ + int nc; /* Length of string */ + int ok; /* Frame written out succesfully? */ + int prj; /* Projection type */ + int ret; /* Returned value. */ + +/* Initialise */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Find the number of co-ordinate versions in the FitsStore. FITS-PC + can only encode 10 axis descriptions (including primary). */ + sup = GetMaxS( &(store->crval), status ); + if( sup > 'I' ) return ret; + +/* Initialise */ + primdt = AST__BAD; + primeq = AST__BAD; + primln = AST__BAD; + primlt = AST__BAD; + +/* Loop round all co-ordinate versions (0-9) */ + primpc = NULL; + for( s = ' '; s <= sup && astOK; s++ ){ + is = s - 'A' + 1; + +/* Assume the Frame can be created succesfully. */ + ok = 1; + +/* Save the number of wcs axes */ + val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + naxis = (int) ( val + 0.5 ); + SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ), + &naxis, AST__INT, "Number of WCS axes", status ); + } else { + naxis = GetMaxJM( &(store->crpix), s, status ) + 1; + } + +/* PC matrix: + --------- */ + +/* This encoding does not allow the PC matrix to be specified for each + version - instead they all share the primary PC matrix. Therefore we + need to check that all versions can use the primary PC matrix. Allocate + memory to hold the PC matrix for this version. */ + matrix = (double *) astMalloc( sizeof(double)*naxis*naxis ); + if( matrix ){ + +/* Fill these array with the values supplied in the FitsStore. */ + c = matrix; + for( i = 0; i < naxis; i++ ){ + for( j = 0; j < naxis; j++ ){ + *c = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( *c == AST__BAD ) *c = ( i == j ) ? 1.0 : 0.0; + c++; + } + } + +/* If we are currently processing the primary axis description, take + a copy of the PC matrix. */ + if( s == ' ' ) { + primpc = (double *) astStore( NULL, (void *) matrix, + sizeof(double)*naxis*naxis ); + +/* Store each matrix element in turn. */ + c = matrix; + for( i = 0; i < naxis; i++ ){ + for( j = 0; j < naxis; j++ ){ + +/* Set the element bad if it takes its default value. */ + val = *(c++); + if( i == j ){ + if( astEQUAL( val, 1.0 ) ) val = AST__BAD; + } else { + if( astEQUAL( val, 0.0 ) ) val = AST__BAD; + } + +/* Only store elements which do not take their default values. */ + if( val != AST__BAD ){ + sprintf( keyname, "PC%.3d%.3d", i + 1, j + 1 ); + SetValue( this, keyname, &val, AST__FLOAT, NULL, status ); + } + } + } + +/* For secondary axis descriptions, a check is made that the PC values are + the same as the primary PC values stored earlier. If not, the current + Frame cannot be stored as a secondary axis description so continue on + to the next Frame. */ + } else { + if( primpc ){ + c = matrix; + d = primpc; + for( i = 0; i < naxis; i++ ){ + for( j = 0; j < naxis; j++ ){ + if( !astEQUAL( *c, *d ) ){ + ok = 0; + } else { + c++; + d++; + } + } + } + +/* Continue with the next Frame if the PC matrix for this Frame is different + to the primary PC matrix. */ + if( !ok ) goto next; + } + } + matrix = (double *) astFree( (void *) matrix ); + } + +/* CDELT: + ------ */ + for( i = 0; i < naxis; i++ ){ + val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + goto next; + } + sprintf( combuf, "Pixel scale on axis %d", i + 1 ); + if( s == ' ' ) { + sprintf( keyname, "CDELT%d", i + 1 ); + } else { + sprintf( keyname, "C%dELT%d", is, i + 1 ); + } + SetValue( this, keyname, &val, AST__FLOAT, combuf, status ); + } + +/* CRPIX: + ------ */ + for( j = 0; j < naxis; j++ ){ + val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + goto next; + } + sprintf( combuf, "Reference pixel on axis %d", j + 1 ); + if( s == ' ' ) { + sprintf( keyname, "CRPIX%d", j + 1 ); + } else { + sprintf( keyname, "C%dPIX%d", is, j + 1 ); + } + SetValue( this, keyname, &val, AST__FLOAT, combuf, status ); + } + +/* CRVAL: + ------ */ + for( i = 0; i < naxis; i++ ){ + val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + goto next; + } + sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); + if( s == ' ' ) { + sprintf( keyname, "CRVAL%d", i + 1 ); + } else { + sprintf( keyname, "C%dVAL%d", is, i + 1 ); + } + SetValue( this, keyname, &val, AST__FLOAT, combuf, status ); + } + +/* CTYPE: + ------ */ + for( i = 0; i < naxis; i++ ){ + cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + nc = strlen( cval ); + if( !cval || ( nc > 4 && !strcmp( cval + 4, "-TAB" ) ) ) { + ok = 0; + goto next; + } + comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + if( !comm ) { + sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); + comm = combuf; + } + if( s == ' ' ) { + sprintf( keyname, "CTYPE%d", i + 1 ); + } else { + sprintf( keyname, "C%dYPE%d", is, i + 1 ); + } + +/* FITS-PC cannot handle celestial axes of type "xxLT" or "xxLN". + Neither can it handle the "-TAB". */ + if( ( nc > 2 && !strncmp( cval + 2, "LT-", 3 ) ) || + ( nc > 2 && !strncmp( cval + 2, "LN-", 3 ) ) || + ( nc > 4 && !strncmp( cval + 4, "-TAB", 4 ) ) ){ + ok = 0; + goto next; + } + +/* Extract the projection type as specified by the last 4 characters + in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial + axes. */ + prj = astWcsPrjType( cval + 4 ); + +/* Change the new SFL projection code to to the older equivalent GLS */ + if( prj == AST__SFL ) { + strcpy( type, cval ); + (void) strcpy( type + 4, "-GLS" ); + cval = type; + } + +/* FITS-PC cannot handle the AST-specific TPN projection. */ + if( prj == AST__TPN ) { + ok = 0; + goto next; + } + +/* Store the CTYPE value */ + SetValue( this, keyname, &cval, AST__STRING, comm, status ); + } + +/* Get and save CUNIT for all intermediate axes. These are NOT required, so + do not pass on if they are not available. */ + for( i = 0; i < naxis; i++ ){ + cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); + if( cval ) { + sprintf( combuf, "Units for axis %d", i + 1 ); + if( s == ' ' ) { + sprintf( keyname, "CUNIT%d", i + 1 ); + } else { + sprintf( keyname, "C%dNIT%d", is, i + 1 ); + } + SetValue( this, keyname, &cval, AST__STRING, combuf, status ); + } + } + +/* Get and save RADESYS. This is NOT required, so do not pass on if it is + not available. If RADECSYS is provided for a secondary axis, it must + be the same as the primary axis RADECSYS value. If it is not, pass on to + the next Frame. */ + cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); + if( cval ) { + if( s == ' ' ) { + strcpy( primsys, cval ); + SetValue( this, "RADECSYS", &cval, AST__STRING, + "Reference frame for RA/DEC values", status ); + } else if( strcmp( cval, primsys ) ) { + ok = 0; + goto next; + } + } + +/* Reference equinox. This is NOT required, so do not pass on if it is + not available. If equinox is provided for a secondary axis, it must + be the same as the primary axis equinox value. If it is not, pass on to + the next Frame. */ + val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); + if( s == ' ' ) { + primeq = val; + if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT, + "Epoch of reference equinox", status ); + } else if( !astEQUAL( val, primeq ) ){ + ok = 0; + goto next; + } + +/* Latitude of native north pole. This is NOT required, so do not pass on + if it is not available. If latpole is provided for a secondary axis, it + must be the same as the primary axis value. If it is not, pass on to + the next Frame. */ + val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); + if( s == ' ' ) { + primlt = val; + if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT, + "Latitude of native north pole", status ); + } else if( !EQUALANG( val, primlt ) ){ + ok = 0; + goto next; + } + +/* Longitude of native north pole. This is NOT required, so do not pass on + if it is not available. If lonpole is provided for a secondary axis, it + must be the same as the primary axis value. If it is not, pass on to + the next Frame. */ + val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); + if( s == ' ' ) { + primln = val; + if( val != AST__BAD ) SetValue( this, "LONGPOLE", &val, AST__FLOAT, + "Longitude of native north pole", status ); + } else if( !EQUALANG( val, primln ) ){ + ok = 0; + goto next; + } + +/* Date of observation. This is NOT required, so do not pass on if it is + not available. If mjd-obs is provided for a secondary axis, it must be + the same as the primary axis value. If it is not, pass on to the next + Frame. */ + val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status ); + if( s == ' ' ) { + primdt = val; + if( val != AST__BAD ) { + SetValue( this, "MJD-OBS", &val, AST__FLOAT, + "Modified Julian Date of observation", status ); + +/* The format used for the DATE-OBS keyword depends on the value of the + keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. + Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ + palCaldj( 99, 1, 1, &mjd99, &jj ); + if( val < mjd99 ) { + palDjcal( 0, val, iymdf, &jj ); + sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], + iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); + } else { + palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); + palDd2tf( 3, fd, sign, ihmsf ); + sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", + iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], + ihmsf[2], ihmsf[3] ); + } + +/* Now store the formatted string in the FitsChan. */ + cval = combuf; + SetValue( this, "DATE-OBS", &cval, AST__STRING, + "Date of observation", status ); + } + } else if( !astEQUAL( val, primdt ) ){ + ok = 0; + goto next; + } + +/* Look for the celestial and spectral axes. */ + FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status ); + +/* If both longitude and latitude axes are present ...*/ + if( axlon >= 0 && axlat >= 0 ) { + +/* Get the CTYPE values for the latitude axis. */ + cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); + +/* Extract the projection type as specified by the last 4 characters + in the CTYPE keyword value. */ + prj = ( cval ) ? astWcsPrjType( cval + 4 ) : AST__WCSBAD; + +/* Projection parameters. If provided for a secondary axis, they must be + the same as the primary axis value. If it is not, pass on to the next + Frame. PC encoding ignores parameters associated with the longitude + axis. The old PC TAN projection did not have any parameters. + Pass on if a TAN projection with parameters is found. The number of + parameters was limited to 10. Pass on if more than 10 are supplied. */ + maxm = GetMaxJM( &(store->pv), ' ', status ); + for( i = 0; i < naxis; i++ ){ + if( i != axlon ) { + for( m = 0; m <= maxm; m++ ){ + val = GetItem( &(store->pv), i, m, s, NULL, method, class, status ); + if( s == ' ' ){ + if( val != AST__BAD ) { + if( i != axlat || prj == AST__TAN || m >= 10 ){ + ok = 0; + goto next; + } else { + SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ), &val, + AST__FLOAT, "Projection parameter", status ); + } + } + if( i == axlat && m < 10 ) primpv[m] = val; + } else { + if( ( ( i != axlat || m >= 10 ) && val != AST__BAD ) || + ( i == axlat && m < 10 && !astEQUAL( val, primpv[m] ) ) ){ + ok = 0; + goto next; + } + } + } + } + } + } + +/* See if a Frame was sucessfully written to the FitsChan. */ +next: + ok = ok && astOK; + +/* If so, indicate we have something to return. */ + if( ok ) ret = 1; + +/* Clear any error status so we can continue to produce the next Frame. + Retain the error if the primary axes could not be produced. After the + primary axes, do the A axes. */ + if( s != ' ' ) { + astClearStatus; + } else { + s = 'A' - 1; + } + +/* Remove the secondary "new" flags from the FitsChan. This flag is + associated with cards which have been added to the FitsChan during + this pass through the main loop in this function. If the Frame was + written out succesfully, just clear the flags. If anything went wrong + with this Frame, remove the flagged cards from the FitsChan. */ + FixNew( this, NEW2, !ok, method, class, status ); + +/* Set the current card so that it points to the last WCS-related keyword + in the FitsChan (whether previously read or not). */ + FindWcs( this, 1, 1, 0, method, class, status ); + } + +/* Annul the array holding the primary PC matrix. */ + primpc = (double *) astFree( (void *) primpc ); + +/* Return zero or ret depending on whether an error has occurred. */ + return astOK ? ret : 0; +} + +static void PreQuote( const char *value, + char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int *status ) { + +/* +* Name: +* PreQuote + +* Purpose: +* Pre-quote FITS character data. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void PreQuote( const char *value, +* char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ] ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function processes a string value in such a way that it can +* be stored as a FITS character value (associated with a keyword) +* and later retrieved unchanged, except for possible truncation. +* +* This pre-processing is necessary because FITS does not regard +* trailing white space as significant, so it is lost. This +* function adds double quote (") characters around the string if +* it is necessary in order to prevent this loss. These quotes are +* also added to zero-length strings and to strings that are +* already quoted (so that the original quotes are not lost when +* they are later un-quoted). +* +* This function will silently truncate any string that is too long +* to be stored as a FITS character value, but will ensure that the +* maximum number of characters are retained, taking account of any +* quoting required. + +* Parameters: +* value +* Pointer to a constant null-terminated string containing the +* input character data to be quoted. All white space is +* significant. +* string +* A character array into which the result string will be +* written, with a terminating null. The maximum number of +* characters from the input string that can be accommodated in +* this is (AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4), but this +* will be reduced if quoting is necessary. + +* Notes: +* - The UnPreQuote function should be used to reverse the effect +* of this function on a string (apart from any truncation). +*/ + +/* Local Variables: */ + int dq; /* Number of double quotes needed */ + int dquotes; /* Final number of double quotes */ + int i; /* Loop counter for input characters */ + int j; /* Counter for output characters */ + int nc; /* Number of characters to be accommodated */ + int sq; /* Number of single quotes needed */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise, setting the default number of double quotes (which + applies to a zero-length string) to 2. */ + dquotes = 2; + nc = 0; + sq = 0; + +/* Loop to consider each input character to see if it will fit into + the result string. */ + for ( i = 0; value[ i ]; i++ ) { + +/* If a single quote character is to be included, count it. When the + string is encoded as FITS character data, these quotes will be + doubled, so will increase the overall string length by one. */ + if ( value[ i ] == '\'' ) sq++; + +/* See how many double quotes are needed around the string (0 or + 2). These are needed if there is trailing white space that needs + protecting (this is not significant in FITS and will be removed), + or if the string already has quotes at either end (in which case an + extra set is needed to prevent the original ones being removed when + it is later un-quoted). Note we do not need to double existing + double quote characters within the string, because the position of + the ends of the string are known (from the quoting supplied by + FITS) so only the first and last characters need be inspected when + un-quoting the string. + In assessing the number of double quotes, assume the string will be + truncated after the current character. */ + dq = ( isspace( value[ i ] ) || + ( ( value[ 0 ] == '"' ) && ( value[ i ] == '"' ) ) ) ? 2 : 0; + +/* See if the length of the resulting string, including the current + character and all necessary quotes, is too long. If so, give up + here. */ + if ( ( nc + 1 + dq + sq ) > + ( AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4 ) ) break; + +/* If the string is not too long, accept the character and note the + number of double quotes needed. */ + nc = i + 1; + dquotes = dq; + } + +/* If double quotes are needed, insert the opening quote into the + output string. */ + j = 0; + if ( dquotes ) string[ j++ ] = '"'; + +/* Follow this with the maximum number of input string characters that + can be accommodated. */ + for ( i = 0; i < nc; i++ ) string[ j++ ] = value[ i ]; + +/* Append the closing quote if necessary and terminate the output + string. */ + if ( dquotes ) string[ j++ ] = '"'; + string[ j ] = '\0'; +} + +static void PurgeWCS( AstFitsChan *this, int *status ){ + +/* +*++ +* Name: +c astPurgeWCS +f AST_PURGEWCS + +* Purpose: +* Delete all cards in the FitsChan describing WCS information. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c void astPurgeWCS( AstFitsChan *this ) +f CALL AST_PURGEWCS( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* deletes all cards in a FitsChan that relate to any of the recognised +* WCS encodings. On exit, the current card is the first remaining card +* in the FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. +*-- +*/ + +/* Local Variables: */ + AstObject *obj; + int oldclean; + +/* Check the global status. */ + if( !astOK ) return; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Ensure the Clean attribute is set so that WCS keywords are removed + even if an error occurs. */ + if( astTestClean( this ) ) { + oldclean = astGetClean( this ); + astSetClean( this, 1 ); + } else { + astSetClean( this, 1 ); + oldclean = -1; + } + +/* Loop round attempting to read AST objects form the FitsChan. This will + flag cards as used that are involved in the creation of these object + (including NATIVE encodings). Ignore any error that ocurs whilst doing + this. */ + astClearCard( this ); + if( astOK ) { + int oldreporting = astReporting( 0 ); + obj = astRead( this ); + while( obj ) { + obj = astAnnul( obj ); + astClearCard( this ); + obj = astRead( this ); + } + if( !astOK ) astClearStatus; + astReporting( oldreporting ); + } + +/* We now loop round to remove any spurious WCS-related cards left in the + FitsChan that did not form part of a complete WCS encoding. Find the + first WCS-related card left in the FitsChan. */ + FindWcs( this, 0, 0, 1, "DeleteWcs", "FitsChan", status ); + +/* Loop round marking each WCS-related card as used until none are left */ + while( this->card && astOK ) { + +/* Mark the current card as having been read. */ + ( (FitsCard*) this->card )->flags = USED; + +/* Find the next WCS-related card. */ + FindWcs( this, 0, 0, 0, "DeleteWcs", "FitsChan", status ); + } + +/* Rewind the FitsChan. */ + astClearCard( this ); + +/* Reset the Clean attribute. */ + if( oldclean == -1 ) { + astClearClean( this ); + } else { + astSetClean( this, oldclean ); + } + +} + +static void PutCards( AstFitsChan *this, const char *cards, int *status ) { + +/* +*++ +* Name: +c astPutCards +f AST_PUTCARDS + +* Purpose: +* Store a set of FITS header cards in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astPutCards( AstFitsChan *this, const char *cards ) +f CALL AST_PUTCARDS( THIS, CARDS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* stores a set of FITS header cards in a FitsChan. The cards are +* supplied concatenated together into a single character string. +* Any existing cards in the FitsChan are removed before the new cards +* are added. The FitsChan is "re-wound" on exit by clearing its Card +* attribute. This means that a subsequent invocation of +c astRead +f AST_READ +* can be made immediately without the need to re-wind the FitsChan +* first. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c cards +f CARDS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string +f A character string +* containing the FITS cards to be stored. Each individual card +* should occupy 80 characters in this string, and there should be +* no delimiters, new lines, etc, between adjacent cards. The final +* card may be less than 80 characters long. +c This is the format produced by the fits_hdr2str function in the +c CFITSIO library. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - An error will result if the supplied string contains any cards +* which cannot be interpreted. +*-- +*/ + +/* Local Variables: */ + const char *a; /* Pointer to start of next card */ + int clen; /* Length of supplied string */ + int i; /* Card index */ + int ncard; /* No. of cards supplied */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Empty the FitsChan. */ + astEmptyFits( this ); + +/* Loop round the supplied string in 80 character segments, inserting + each segment into the FitsChan as a header card. Allow the last card + to be less than 80 characters long. */ + clen = strlen( cards ); + ncard = clen/80; + if( ncard*80 < clen ) ncard++; + a = cards; + for( i = 0; i < ncard; i++, a += 80 ) astPutFits( this, a, 1 ); + +/* Rewind the FitsChan. */ + astClearCard( this ); +} + +static void PutFits( AstFitsChan *this, const char card[ AST__FITSCHAN_FITSCARDLEN + 1 ], + int overwrite, int *status ){ + +/* +*++ +* Name: +c astPutFits +f AST_PUTFITS + +* Purpose: +* Store a FITS header card in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astPutFits( AstFitsChan *this, const char card[ 80 ], +c int overwrite ) +f CALL AST_PUTFITS( THIS, CARD, OVERWRITE, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function stores a FITS header card in a FitsChan. The card +f This routine stores a FITS header card in a FitsChan. The card +* is either inserted before the current card (identified by the +* Card attribute), or over-writes the current card, as required. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c card +f CARD = CHARACTER * ( 80 ) (Given) +c Pointer to a possibly null-terminated character string +c containing the FITS card to be stored. No more than 80 +c characters will be used from this string (or fewer if a null +c occurs earlier). +f A character string string containing the FITS card to be +f stored. No more than 80 characters will be used from this +f string. +c overwrite +f OVERWRITE = LOGICAL (Given) +c If this value is zero, the new card is inserted in front of +f If this value is .FALSE., the new card is inserted in front of +* the current card in the FitsChan (as identified by the +c initial value of the Card attribute). If it is non-zero, the +f initial value of the Card attribute). If it is .TRUE., the +* new card replaces the current card. In either case, the Card +* attribute is then incremented by one so that it subsequently +* identifies the card following the one stored. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the Card attribute initially points at the "end-of-file" +* (i.e. exceeds the number of cards in the FitsChan), then the new +* card is appended as the last card in the FitsChan. +* - An error will result if the supplied string cannot be interpreted +* as a FITS header card. +*-- +*/ + +/* Local Variables: */ + char *comment; /* The keyword comment */ + char *name; /* The keyword name */ + char *value; /* The keyword value */ + const char *class; /* Object class */ + const char *method; /* Current method */ + double cfval[2]; /* Complex floating point keyword value */ + double fval; /* floating point keyword value */ + int cival[2]; /* Complex integer keyword value */ + int ival; /* Integer keyword value */ + int len; /* No. of characters to read from the value string */ + int nc; /* No. of characters read from value string */ + int type; /* Keyword data type */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astPutFits"; + class = astGetClass( this ); + +/* Split the supplied card up into name, value and commment strings, and + get pointers to local copies of them. The data type associated with the + keyword is returned. */ + type = Split( this, card, &name, &value, &comment, method, class, status ); + +/* Check that the pointers can be used. */ + if( astOK ){ + +/* Initialise the number of characters read from the value string. */ + nc = 0; + +/* Store the number of characters in the value string. */ + len = strlen( value ); + +/* Read and store floating point values from the value string. NB, this + list is roughly in the order of descreasing frequency of use (i.e. + most FITS keywords are simple floating point values, the next most + common are strings, etc). */ + if( type == AST__FLOAT ){ + if( 1 == astSscanf( value, " %lf %n", &fval, &nc ) && nc >= len ){ + astSetFitsF( this, name, fval, comment, overwrite ); + } else { + astError( AST__BDFTS, "%s(%s): Unable to read a floating point " + "FITS keyword value.", status, method, class ); + } + +/* Read and store string values from the value string. */ + } else if( type == AST__STRING ){ + astSetFitsS( this, name, value, comment, overwrite ); + +/* Read and store string values from the value string. */ + } else if( type == AST__CONTINUE ){ + astSetFitsCN( this, name, value, comment, overwrite ); + +/* Store comment card. */ + } else if( type == AST__COMMENT ){ + astSetFitsCom( this, name, comment, overwrite ); + +/* Read and store integer values from the value string. */ + } else if( type == AST__INT ){ + if( 1 == astSscanf( value, " %d %n", &ival, &nc ) && nc >= len ){ + astSetFitsI( this, name, ival, comment, overwrite ); + } else { + astError( AST__BDFTS, "%s(%s): Unable to read an integer FITS " + "keyword value.", status, method, class ); + } + +/* Read and store logical values from the value string. */ + } else if( type == AST__LOGICAL ){ + astSetFitsL( this, name, (*value == 'T'), comment, overwrite ); + +/* Read and store undefined values from the value string. */ + } else if( type == AST__UNDEF ){ + astSetFitsU( this, name, comment, overwrite ); + +/* Read and store complex floating point values from the value string. */ + } else if( type == AST__COMPLEXF ){ + if( 2 == astSscanf( value, " %lf %lf %n", cfval, cfval + 1, &nc ) && + nc >= len ){ + astSetFitsCF( this, name, cfval, comment, overwrite ); + } else { + astError( AST__BDFTS, "%s(%s): Unable to read a complex pair " + "of floating point FITS keyword values.", status, method, class ); + } + +/* Read and store complex integer values from the value string. */ + } else if( type == AST__COMPLEXI ){ + if( 2 == astSscanf( value, " %d %d %n", cival, cival + 1, &nc ) && + nc >= len ){ + astSetFitsCI( this, name, cival, comment, overwrite ); + } else { + astError( AST__BDFTS, "%s(%s): Unable to read a complex pair " + "of integer FITS keyword values.", status, method, class ); + } + +/* Report an error for any other type. */ + } else { + astError( AST__INTER, "%s: AST internal programming error - " + "FITS data-type '%d' not yet supported.", status, method, type ); + } + +/* Give a context message if an error occurred. */ + if( !astOK ){ + astError( astStatus, "%s(%s): Unable to store the following FITS " + "header card:\n%s\n", status, method, class, card ); + } + } + +/* Free the memory used to hold the keyword name, comment and value + strings. */ + (void) astFree( (void *) name ); + (void) astFree( (void *) comment ); + (void) astFree( (void *) value ); +} + +static void PutTable( AstFitsChan *this, AstFitsTable *table, + const char *extnam, int *status ) { + +/* +*++ +* Name: +c astPutTable +f AST_PUTTABLE + +* Purpose: +* Store a single FitsTable in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astPutTable( AstFitsChan *this, AstFitsTable *table, +c const char *extnam ) +f CALL AST_PUTTABLE( THIS, TABLE, EXTNAM, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* allows a representation of a single FITS binary table to be +* stored in a FitsChan. For instance, this may provide the coordinate +* look-up tables needed subequently when reading FITS-WCS headers +* for axes described using the "-TAB" algorithm. Since, in general, +* the calling application may not know which tables will be needed - +* if any - prior to calling +c astRead, the astTablesSource function +f AST_READ, the AST_TABLESOURCE routine +* provides an alternative mechanism in which a caller-supplied +* function is invoked to store a named table in the FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c table +f TABLE = INTEGER (Given) +* Pointer to a FitsTable to be added to the FitsChan. If a FitsTable +* with the associated extension name already exists in the FitsChan, +* it is replaced with the new one. A deep copy of the FitsTable is +* stored in the FitsChan, so any subsequent changes made to the +* FitsTable will have no effect on the behaviour of the FitsChan. +c extnam +f EXTNAM = CHARACTER * ( * ) (Given) +* The name of the FITS extension associated with the table. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Tables stored in the FitsChan may be retrieved using +c astGetTables. +f AST_GETTABLES. +c - The astPutTables method can add multiple FitsTables in a single call. +f - The AST_PUTTABLES method can add multiple FitsTables in a single call. +*-- +*/ + +/* Local Variables: */ + AstObject *ft; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Create a KeyMap to hold the tables within the FitsChan, if this has not + already been done. */ + if( !this->tables ) this->tables = astKeyMap( " ", status ); + +/* Store a copy of the FitsTable in the FitsChan. */ + ft = astCopy( table ); + astMapPut0A( this->tables, extnam, ft, NULL ); + ft = astAnnul( ft ); +} + +static void PutTables( AstFitsChan *this, AstKeyMap *tables, int *status ) { + +/* +*++ +* Name: +c astPutTables +f AST_PUTTABLES + +* Purpose: +* Store one or more FitsTables in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astPutTables( AstFitsChan *this, AstKeyMap *tables ) +f CALL AST_PUTTABLES( THIS, TABLES, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* allows representations of one or more FITS binary tables to be +* stored in a FitsChan. For instance, these may provide the coordinate +* look-up tables needed subequently when reading FITS-WCS headers +* for axes described using the "-TAB" algorithm. Since, in general, +* the calling application may not know which tables will be needed - +* if any - prior to calling +c astRead, the astTablesSource function +f AST_READ, the AST_TABLESOURCE routine +* provides an alternative mechanism in which a caller-supplied +* function is invoked to store a named table in the FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c tables +f TABLES = INTEGER (Given) +* Pointer to a KeyMap holding the tables that are to be added +* to the FitsChan. Each entry should hold a scalar value which is a +* pointer to a FitsTable to be added to the FitsChan. Any unusable +* entries are ignored. The key associated with each entry should be +* the name of the FITS binary extension from which the table was +* read. If a FitsTable with the associated key already exists in the +* FitsChan, it is replaced with the new one. A deep copy of each +* usable FitsTable is stored in the FitsChan, so any subsequent +* changes made to the FitsTables will have no effect on the +* behaviour of the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Tables stored in the FitsChan may be retrieved using +c astGetTables. +f AST_GETTABLES. +* - The tables in the supplied KeyMap are added to any tables already +* in the FitsChan. +c - The astPutTable +f - The AST_PUTTABLE +* method provides a simpler means of adding a single table to a FitsChan. +*-- +*/ + +/* Local Variables: */ + AstObject *obj; + const char *key; + int ientry; + int nentry; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Loop through all entries in the supplied KeyMap. */ + nentry = astMapSize( tables ); + for( ientry = 0; ientry < nentry; ientry++ ) { + key = astMapKey( tables, ientry ); + +/* Ignore entries that do not contain AST Object pointers, or are not + scalar. */ + if( astMapType( tables, key ) == AST__OBJECTTYPE && + astMapLength( tables, key ) == 1 ) { + +/* Get the pointer, amd ignore it if it is not a FitsTable. */ + astMapGet0A( tables, key, &obj ); + if( astIsAFitsTable( obj ) ) { + +/* Store it in the FitsChan. */ + astPutTable( this, (AstFitsTable *) obj, key ); + } + +/* Annul the Object pointer. */ + obj = astAnnul( obj ); + } + } +} + +static AstObject *Read( AstChannel *this_channel, int *status ) { +/* +* Name: +* Read + +* Purpose: +* Read an Object from a Channel. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstObject *Read( AstChannel *this_channel, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the astRead method +* inherited from the Channel class). + +* Description: +* This function reads an Object from a FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new Object. This will always be a FrameSet. + +* Notes: +* - The pixel Frame is given a title of "Pixel Coordinates", and +* each axis in the pixel Frame is given a label of the form "Pixel +* axis ", where is the axis index (starting at one). +* - The FITS CTYPE keyword values are used to set the labels for any +* non-celestial axes in the physical coordinate Frames, and the FITS +* CUNIT keywords are used to set the corresponding units strings. +* - On exit, the pixel Frame is the base Frame, and the physical +* Frame derived from the primary axis descriptions is the current Frame. +* - Extra Frames are added to hold any secondary axis descriptions. All +* axes within such a Frame refer to the same coordinate version ('A', +* 'B', etc). +* - For foreign encodings, the first card in the FitsChan must be +* the current card on entry (otherwise a NULL pointer is returned), +* and the FitsChan is left at end-of-file on exit. +* - For the Native encoding, reading commences from the current card +* on entry (which need not be the first in the FitsChan), and the +* current Card on exit is the first card following the last one read +* (or end-of-file). +*/ + +/* Local Variables: */ + AstObject *new; /* Pointer to returned Object */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + FitsStore *store; /* Intermediate storage for WCS information */ + const char *method; /* Pointer to string holding calling method */ + const char *class; /* Pointer to string holding object class */ + int encoding; /* The encoding scheme */ + int remove; /* Remove used cards? */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the calling method, and object class. */ + method = "astRead"; + class = astGetClass( this ); + +/* Get the encoding scheme used by the FitsChan. */ + encoding = astGetEncoding( this ); + +/* If we are reading from a FitsChan in which AST objects are encoded using + native AST-specific keywords, use the Read method inherited from the + Channel class. */ + if( encoding == NATIVE_ENCODING ){ + new = (*parent_read)( this_channel, status ); + +/* Indicate that used cards should be removed from the FitsChan. */ + remove = 1; + +/* If we are reading from a FitsChan in which AST objects are encoded using + any of the other supported encodings, the header may only contain a + single FrameSet. */ + } else { + remove = 0; + +/* Only proceed if the FitsChan is at start-of-file. */ + if( !astTestCard( this ) && astOK ){ + +/* Extract the required information from the FITS header into a standard + intermediary structure called a FitsStore. */ + store = FitsToStore( this, encoding, method, class, status ); + +/* Now create a FrameSet from this FitsStore. */ + new = FsetFromStore( this, store, method, class, status ); + +/* Release the resources used by the FitsStore. */ + store = FreeStore( store, status ); + +/* Indicate that used cards should be retained in the FitsChan. */ + remove = 0; + +/* If no object is being returned, rewind the fitschan in order to + re-instate the original current Card. */ + if( !new ) { + astClearCard( this ); + +/* Otherwise, ensure the current card is at "end-of-file". */ + } else { + astSetCard( this, INT_MAX ); + } + } + } + +/* If an error occurred, clean up by deleting the new Object and + return a NULL pointer. */ + if ( !astOK ) new = astDelete( new ); + +/* If no object is being returned, clear the "provisionally used" flags + associated with cards which were read. We do not do this if the user + wants to clean WCS cards from the FitsChan even if an error occurs. */ + if( !new && !astGetClean( this ) ) { + FixUsed( this, 0, 0, 0, method, class, status ); + +/* Otherwise, indicate that all the "provisionally used" cards have been + "definitely used". If native encoding was used, these cards are + totally removed from the FitsChan. */ + } else { + FixUsed( this, 0, 1, remove, method, class, status ); + } + +/* Return the pointer to the new Object. */ + return new; +} + +static double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s, + const char *method, const char *class, int *status ){ + +/* +* Name: +* ReadCrval + +* Purpose: +* Obtain the reference point from the supplied FitsChan in the +* supplied WCS Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The original reference point in the "s" coordinate description is read +* from the CRVAL keywords in the supplied FitsChan, and the original +* FrameSet is re-read from the FitsChan. If possible, the reference +* position is then converted from the "s" coordinate description to the +* supplied WCS Frame, and a pointer to an array holding the axis +* values for the transformed reference point is returned. + +* Parameters: +* this +* The FitsChan. +* wcsfrm +* The WCS Frame in the FitsChan being written to. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array holding the reference +* point in the supplied WCS Frame. NULL is returned if is is not +* possible to determine the reference point for any reason (for +* instance, if the FitsChan does not contain values for the CRVAL +* keywords). +*/ + +/* Local Variables: */ + AstFitsChan *fc; /* A copy of the supplied FitsChan */ + AstFrame *tfrm; /* Temporary Frame pointer */ + AstFrameSet *fs; /* The FITS FrameSet */ + AstFrameSet *tfs; /* FrameSet connecting FITS and supplied WCS Frame */ + const char *id; /* Pointer to Object "Id" string */ + char buf[ 11 ]; /* FITS keyword template buffer */ + double *crval; /* CRVAL keyword values in supplied FitsChan */ + double *ret; /* Returned array */ + int hii; /* Highest found FITS axis index */ + int iax; /* Axis index (zero based) */ + int ifr; /* Frames index */ + int loi; /* Lowest found FITS axis index */ + int nax; /* Axis count */ + int nfr; /* No. of Frames in FITS FrameSet */ + int ok; /* Were CRVAL values found? */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* We want to re-create the original FrameSet represented by the original + contents of the supplied FitsChan. Some of the contents of the + FitsChan will already have been marked as "having been read" and so + will be ignored if we attempt to read a FrameSet directly from the + supplied FitsChan. Therefore we take a deep copy of the supplied + FitsChan and clear all the "previusly read" flags in the copy. */ + fc = astCopy( this ); + astClearEncoding( fc ); + FixUsed( fc, 1, 0, 0, method, class, status ); + +/* Copy the CRVAL values for the "s" axis descriptions into a dynamically + allocated array ("crval"). */ + if( s == ' ' ) { + strcpy( buf, "CRVAL%d" ); + } else { + sprintf( buf, "CRVAL%%d%c", s ); + } + if( astKeyFields( fc, buf, 1, &hii, &loi ) > 0 ) { + crval = astMalloc( sizeof( double )*(size_t) hii ); + ok = 1; + for( iax = 0; iax < hii; iax++ ){ + ok = ok && GetValue( fc, FormatKey( "CRVAL", iax + 1, -1, s, status ), + AST__FLOAT, (void *) (crval + iax), 0, 0, method, + class, status ); + } + } else { + crval = NULL; + ok = 0; + } + +/* If the CRVAL values were obtained succesfully, attempt to read a FrameSet + from the FitsChan copy. Do it in a new error report context so that we + can annull any error when the FrameSet is read. */ + if( ok ) { + int oldreporting = astReporting( 0 ); + astClearCard( fc ); + fs = astRead( fc ); + if( fs ) { + +/* We want to find a conversion from the Frame in this FrameSet which + represents the FITS-WCS "s" coordinate descriptions and the supplied WCS + Frame. So first find the Frame which has its Ident attribute set to + "s" and make it the current Frame. */ + nfr = astGetNframe( fs ); + for( ifr = 1; ifr <= nfr; ifr++ ) { + astSetCurrent( fs, ifr ); + tfrm = astGetFrame( fs, ifr ); + id = astTestIdent( tfrm ) ? astGetIdent( tfrm ) : NULL; + tfrm = astAnnul( tfrm ); + if( id && strlen( id ) == 1 && id[ 0 ] == s ) break; + } + +/* Check a Frame was found, and that we have CRVAL values for all axes in + the Frame. */ + if( ifr <= nfr && astGetNaxes( fs ) == hii ) { + +/* Attempt to find a conversion route from the Frame found above to the + supplied WCS Frame. */ + tfs = astConvert( fs, wcsfrm, astGetDomain( wcsfrm ) ); + if( tfs ) { + +/* Allocate memory to hold the returned reference point. */ + nax = astGetNaxes( wcsfrm ); + ret = astMalloc( sizeof( double )*(size_t) nax ); + +/* Transform the original reference position from the "s" Frame to the + supplied WCS Frame using the Mapping returned by astConvert. */ + astTranN( tfs, 1, hii, 1, crval, 1, nax, 1, ret ); + +/* Free resources. */ + tfs = astAnnul( tfs ); + } + } + +/* Free resources. */ + fs = astAnnul( fs ); + +/* Annul any error that occurred reading the FitsChan. */ + } else if( !astOK ) { + astClearStatus; + } + +/* Re-instate error reporting. */ + astReporting( oldreporting ); + } + +/* Free resources. */ + if( crval ) crval = astFree( crval ); + fc = astAnnul( fc ); + +/* If an error occurred, free the returned array. */ + if( !astOK ) ret = astFree( ret ); + +/* Return the result. */ + return ret; +} + +static void ReadFits( AstFitsChan *this, int *status ){ + +/* +*++ +* Name: +c astReadFits +f AST_READFITS + +* Purpose: +* Read cards into a FitsChan from the source function. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c void astReadFits( AstFitsChan *this ) +f CALL AST_READFITS( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* reads cards from the source function that was specified when the +* FitsChan was created, and stores them in the FitsChan. This +* normally happens once-only, when the FitsChan is accessed for the +* first time. +c This function +f This routine +* provides a means of forcing a re-read of the external source, and +* may be useful if (say) new cards have been deposited into the +* external source. Any newcards read from the source are appended to +* the end of the current contents of the FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - This function returns without action if no source function was +* specified when the FitsChan was created. +* - The SourceFile attribute is ignored by this +c function. +f routine. +* New cards are read from the source file whenever a new value is +* assigned to the SourceFile attribute. + +*-- +*/ + +/* Check the inherited status */ + if( !astOK ) return; + +/* If no source function is available, re-instate any saved source + function pointer. */ + if( !this->source ) { + this->source = this->saved_source; + this->saved_source = NULL; + } + +/* Call the source function. */ + ReadFromSource( this, status ); +} + +static void ReadFromSource( AstFitsChan *this, int *status ){ + +/* +* Name: +* ReadFromSource + +* Purpose: +* Fill the FitsChan by reading cards from the source function. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void ReadFromSource( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The source function specified when the FitsChan was created is +* called repeatedly until it returns a NULL pointer. The string +* returned by each such call is assumed to be a FITS header card, +* and is stored in the FitsChan using astPutFits. +* +* If no source function was provided, the FitsChan is left as supplied. +* This is different to a standard Channel, which tries to read data +* from standard input if no source function is provided. +* +* This function should be called at the start of most public or protected +* FitsChan functions, and most private functions that are used to override +* methods inherited form the Channel class. Previously, this function +* was called only once, from the FitsChan initialiser (astInitFitschan). +* However, calling it from astInitFitsChan means that application code +* cannot use the astPutChannelData function with a FitsChan, since the +* source function would already have been called by the time the +* FitsChan constructor returned (and thus before astPutChannelData +* could have been called). In order to ensure that the source +* function is called only once, this function now nullifies the source +* function pointer after its first use. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The new cards are appended to the end of the FitsChan. +* - The first of the new cards is made the current card on exit. If no +* source function is supplied, the current card is left unchanged. +*/ + +/* Local Variables: */ + const char *(* source)( void ); /* Pointer to source function */ + const char *card; /* Pointer to externally-read header card */ + int icard; /* Current card index on entry */ + +/* Check the global status. */ + if( !astOK || !this ) return; + +/* Only proceed if source function and wrapper were supplied when the FitsChan + was created and are still available. */ + if( this->source && this->source_wrap ){ + +/* Save the source function pointer and then nullify the pointer in the + FitsChan structure. This avoids infinte loops. */ + source = this->source; + this->source = NULL; + +/* Save the source fubnction pointer in the FitsChan so that it can be + re-instated if required (e.g. by astReadFits). */ + this->saved_source = source; + +/* Ensure the FitsChan is at end-of-file. This will result in the + new cards being appended to the end of the FitsChan. */ + astSetCard( this, INT_MAX ); + +/* Store the current card index. */ + icard = astGetCard( this ); + +/* Obtain the first header card from the source function. This is an + externally supplied function which may not be thread-safe, so lock a + mutex first. Also store the channel data pointer in a global variable + so that it can be accessed in the source function using macro + astChannelData. */ + astStoreChannelData( this ); + LOCK_MUTEX2; + card = ( *this->source_wrap )( source, status ); + UNLOCK_MUTEX2; + +/* Loop until a NULL pointer is returned by the source function, or an + error occurs. */ + while( card && astOK ){ + +/* Store the card in the FitsChan. */ + astPutFits( this, card, 0 ); + +/* Free the memory holding the header card. */ + card = (char *) astFree( (void *) card ); + +/* Obtain the next header card. Also store the channel data pointer in a + global variable so that it can be accessed in the source function using + macro astChannelData. */ + astStoreChannelData( this ); + LOCK_MUTEX2; + card = ( *this->source_wrap )( source, status ); + UNLOCK_MUTEX2; + } + +/* Set the current card index so that the first of the new cards will be the + next card to be read from the FitsChan. */ + astSetCard( this, icard ); + } +} + +static void RemoveTables( AstFitsChan *this, const char *key, int *status ){ + +/* +*++ +* Name: +c astRemoveTables +f AST_REMOVETABLES + +* Purpose: +* Remove one or more tables from a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c void astRemoveTables( AstFitsChan *this, const char *key ) +f CALL AST_REMOVETABLES( THIS, KEY, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* removes the named tables from the FitsChan, it they exist (no error +* is reported if any the tables do not exist). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c key +f KEY = CHARACTER * ( * ) (Given) +* The key indicating which tables to exist. A single key or a +* comma-separated list of keys can be supplied. If a blank string +* is supplied, all tables are removed. +f STATUS = INTEGER (Given and Returned) +f The global status. +*-- +*/ + +/* Local variables: */ + char **words; + int itable; + int ntable; + +/* Return if the global error status has been set, or the FitsChan + contains no tables KeyMap. */ + if( !astOK || !this->tables ) return; + +/* If the string is blank, remove all tables. */ + if( astChrLen( key ) == 0 ) { + ntable = astMapSize( this->tables ); + for( itable = 0; itable < ntable; itable++ ) { + astMapRemove( this->tables, astMapKey( this->tables, itable ) ); + } + +/* Otherwise, split the supplied comma-separated string up into individual + items. */ + } else { + words = astChrSplitC( key, ',', &ntable ); + +/* Attempt to remove each one, and then free the string. */ + if( astOK ) { + for( itable = 0; itable < ntable; itable++ ) { + astMapRemove( this->tables, words[ itable ] ); + words[ itable ] = astFree( words[ itable ] ); + } + } + +/* Free the list. */ + words = astFree( words ); + } +} + +static void RetainFits( AstFitsChan *this, int *status ){ + +/* +*++ +* Name: +c astRetainFits +f AST_RETAINFITS + +* Purpose: +* Indicate that the current card in a FitsChan should be retained. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c void astRetainFits( AstFitsChan *this ) +f CALL AST_RETAINFITS( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* stores a flag with the current card in the FitsChan indicating that +* the card should not be removed from the FitsChan when an Object is +* read from the FitsChan using +c astRead. +f AST_READ. +* +* Cards that have not been flagged in this way are removed when a +* read operation completes succesfully, but only if the card was used +* in the process of creating the returned AST Object. Any cards that +* are irrelevant to the creation of the AST Object are retained whether +* or not they are flagged. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - This function returns without action if the FitsChan is +* initially positioned at the "end-of-file" (i.e. if the Card +* attribute exceeds the number of cards in the FitsChan). +* - The current card is not changed by this function. +*-- +*/ + +/* Local variables: */ + int flags; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Return if the global error status has been set, or the current card + is not defined. */ + if( !astOK || !this->card ) return; + +/* Set the PROTECTED flag in the current card. */ + flags = ( (FitsCard *) this->card )->flags; + ( (FitsCard *) this->card )->flags = flags | PROTECTED; +} + +static void RoundFString( char *text, int width, int *status ){ +/* +* Name: +* RoundString + +* Purpose: +* Modify a formatted floating point number to round out long +* sequences of zeros or nines. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void RoundFString( char *text, int width ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The supplied string is assumed to be a valid decimal representation of +* a floating point number. It is searched for sub-strings consisting +* of NSEQ or more adjacent zeros, or NSEQ or more adjacent nines. If found +* the string is modified to represent the result of rounding the +* number to remove the sequence of zeros or nines. + +* Parameters: +* text +* The formatted number. Modified on exit to round out long +* sequences of zeros or nines. The returned string is right justified. +* width +* The minimum field width to use. The value is right justified in +* this field width. Ignored if zero. +*/ + +/* Local Constants: */ +#define NSEQ 4 /* No. of adjacent 0's or 9's to produce rounding */ + +/* Local Variables: */ + char *a; + char *c; + char *dot; + char *exp; + char *last; + char *start; + char *end; + int i; + int neg; + int nnine; + int nonzero; + int nzero; + int replace; + int started; + int len; + int bu; + int nls; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Save the original length of the text. */ + len = strlen( text ); + +/* Locate the start of any exponent string. */ + exp = strpbrk( text, "dDeE" ); + +/* First check for long strings of adjacent zeros. + =============================================== */ + +/* Indicate that we have not yet found a decimal point in the string. */ + dot = NULL; + +/* The "started" flag controls whether *leading* zeros should be removed + if there are more than NSEQ of them. They are only removed if there is an + exponent. */ + started = ( exp != NULL ); + +/* We are not currently replacing digits with zeros. */ + replace = 0; + +/* We have not yet found any adjacent zeros. */ + nzero = 0; + +/* We have no evidence yet that the number is non-zero. */ + nonzero = 0; + +/* Loop round the supplied text string. */ + c = text; + while( *c && c != exp ){ + +/* If this is a zero, increment the number of adjacent zeros found, so + long as we have previously found a non-zero digit (or there is an + exponent). If this is the NSEQ'th adjacent zero, indicate that + subsequent digits should be replaced by zeros. */ + if( *c == '0' ){ + if( started && ++nzero >= NSEQ ) replace = 1; + +/* Note if the number contains a decimal point. */ + } else if( *c == '.' ){ + dot = c; + +/* If this character is a non-zero digit, indicate that we have found a + non-zero digit. If we have previously found a long string of adjacent + zeros, replace the digit by '0'. Otherwise, reset the count of + adjacent zeros, and indicate the final number is non-zero. */ + } else if( *c != ' ' && *c != '+' && *c != '-' ){ + started = 1; + if( replace ) { + *c = '0'; + } else { + nzero = 0; + nonzero = 1; + } + } + +/* Move on to the next character. */ + c++; + } + +/* If the final number is zero, just return the most simple decimal zero + value. */ + if( !nonzero ) { + strcpy( text, "0.0" ); + +/* Otherwise, we remove any trailing zeros which occur to the right of a + decimal point. */ + } else if( dot ) { + +/* Find the last non-zero digit. */ + while( c-- > text && *c == '0' ); + +/* If any trailing zeros were found... */ + if( c > text ) { + +/* Retain one trailing zero after a decimal point. */ + if( *c == '.' ) c++; + +/* We put a terminator following the last non-zero character. The + terminator is the exponent, if there was one, or a null character. + Remember to update the pointer to the start of the exponent. */ + c++; + if( exp ) { + a = exp; + exp = c; + while( ( *(c++) = *(a++) ) ); + } else { + *c = 0; + } + } + } + +/* Next check for long strings of adjacent nines. + ============================================= */ + +/* We have not yet found any adjacent nines. */ + nnine = 0; + +/* We have not yet found a non-nine digit. */ + a = NULL; + +/* We have not yet found a non-blank character */ + start = NULL; + last = NULL; + +/* Number is assumed positive. */ + neg = 0; + +/* Indicate that we have not yet found a decimal point in the string. */ + dot = NULL; + +/* Loop round the supplied text string. */ + c = text; + while( *c && c != exp ){ + +/* Note the address of the first non-blank character. */ + if( !start && *c != ' ' ) start = c; + +/* If this is a nine, increment the number of adjacent nines found. */ + if( *c == '9' ){ + ++nnine; + +/* Note if the number contains a decimal point. */ + } else if( *c == '.' ){ + dot = c; + +/* Note if the number is negative. */ + } else if( *c == '-' ){ + neg = 1; + +/* If this character is a non-nine digit, and we have not had a long + sequence of 9's, reset the count of adjacent nines, and update a pointer + to "the last non-nine digit prior to a long string of nines". */ + } else if( *c != ' ' && *c != '+' ){ + if( nnine < NSEQ ) { + nnine = 0; + a = c; + } + } + +/* Note the address of the last non-blank character. */ + if( *c != ' ' ) last = c; + +/* Move on to the next character. */ + c++; + } + +/* If a long string of adjacent nines was found... */ + if( nnine >= NSEQ ) { + c = NULL; + +/* If we found at least one non-nine digit. */ + if( a ) { + +/* "a" points to the last non-nine digit before the first of the group of 9's. + Increment this digit by 1. Since we know the digit is not a nine, there + is no danger of a carry. */ + *a = *a + 1; + +/* Fill with zeros up to the decimal point, or to the end if there is no + decimal point. */ + c = a + 1; + if( dot ) { + while( c < dot ) *(c++) = '0'; + } else { + while( *c ) *(c++) = '0'; + } + +/* Now make "c" point to the first character for the terminator. This is + usually the character following the last non-nine digit. However, if + the last non-nine digit appears immediately before a decimal point, then + we append ".0" to the string before appending the terminator. */ + if( *c == '.' ) { + *(++c) = '0'; + c++; + } + +/* If all digits were nines, the rounded number will occupy one more + character than the supplied number. We can only do the rounding if there + is a spare character (i.e.a space) in the supplied string. */ + } else if( last - start + 1 < len ) { + +/* Put the modified text at the left of the available space. */ + c = text; + +/* Start with a minus sing if needed, followed by the leading "1" (caused + by the overflow from the long string of 9's). */ + if( neg ) *(c++) = '-'; + *(c++) = '1'; + +/* Now find the number of zeros to place after the leading "1". This is + the number of characters in front of the terminator marking the end of + the integer part of the number. */ + if( dot ) { + nzero = dot - start; + } else if( exp ) { + nzero = exp - start; + } else { + nzero = last - start; + } + +/* If the number is negative, the above count will include the leading + minus sign, which is not a digit. So reduce the count by one. */ + if( neg ) nzero--; + +/* Now put in the correct number of zeros. */ + for( i = 0; i < nzero; i++ ) *(c++) = '0'; + +/* If the original string containsed a decimal point, make sure the + returned string also contains one. */ + if( dot ) { + *(c++) = '.'; + if( *c ) *(c++) = '0'; + } + } + +/* We put a terminator following the last non-zero character. The + terminator is the exponent, if there was one, or a null character. */ + if( c ) { + if( exp ) { + while( ( *(c++) = *(exp++) ) ); + } else { + *c = 0; + } + } + } + +/* Right justify the returned string in the original field width. */ + end = text + len; + c = text + strlen( text ); + if( c != end ) { + while( c >= text ) *(end--) = *(c--); + while( end >= text ) *(end--) = ' '; + } + +/* If a minimum field width was given, shunt the text to the left in + order to reduce the used field width to the specified value. This + requires there to be some leading spaces (because we do not want to + loose any non-blank characters from the left hand end of the string). + If there are insufficient leading spaces to allow the field width to + be reduced to the specified value, then reduce the field width as far + as possible. First find the number of spaces we would like to remove + from the front of the string (in order to reduce the used width to the + specified value). */ + bu = len - width; + +/* If we need to remove any leading spaces... */ + if( width > 0 && bu > 0 ) { + +/* Find the number of leading spaces which are available to be removed. */ + c = text - 1; + while( *(++c) == ' ' ); + nls = c - text; + +/* If there are insufficient leading spaces, just use however many there + are. */ + if( bu > nls ) bu = nls; + +/* Shift the string. */ + c = text; + a = c + bu; + while( ( *(c++) = *(a++) ) ); + } + +/* Undefine local constants. */ +#undef NSEQ +} + +static int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method, + const char *class, int *status ){ +/* +* Name: +* SAOTrans + +* Purpose: +* Translate an SAO encoded header into a TPN encoded header. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Search "this" for keywords that give a description of a distorted +* TAN projection using the SAO representation and, if found, write +* keywords to "out" that describe an equivalent projection using TPN +* representation. The definition of the SAO polynomial is taken from +* the platepos.c file included in Doug Mink's WCSTools. + +* Parameters: +* this +* Pointer to the FitsChan to read. +* out +* Pointer to a FitsCHan in which to store translated keywords. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if "this" contained an SAO encoded header. Zero otherwise. + +*/ + +#define NC 13 + +/* Local Variables: */ + char keyname[10]; + double co[ 2 ][ NC ]; + double pv; + int i; + int is_sao; + int m; + int ok; + int result; + +/* Initialise */ + result = 0; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Check there are exactly two CTYPE keywords in the header. */ + if( 2 == astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) ){ + +/* Initialise all cooefficients. */ + memset( co, 0, sizeof( co ) ); + +/* Get the required SAO keywords. */ + is_sao = 1; + ok = 1; + for( i = 0; i < 2 && ok && is_sao; i++ ) { + + ok = 0; + for( m = 0; m < NC; m++ ) { + +/* Get the value of the next "COi_j" keyword. If any of the first 3 values + are missing on either axis, we assume this is not an SAO header. */ + sprintf( keyname, "CO%d_%d", i + 1, m + 1 ); + if( !GetValue( this, keyname, AST__FLOAT, &co[ i ][ m ], 0, 1, method, + class, status ) ) { + if( m < 3 ) is_sao = 0; + break; + } + +/* Check that we have at least one non-zero coefficient (excluding the + first constant term ). */ + if( co[ i ][ m ] != 0.0 && m > 0 ) ok = 1; + } + } + +/* If this is an SAO header.. */ + if( is_sao ) { + +/* Issue a warning if all coefficients for this axis are zero. */ + if( !ok ) { + Warn( this, "badpv", "This FITS header describes an SAO encoded " + "distorted TAN projection, but all the distortion " + "coefficients for at least one axis are zero.", method, class, + status ); + +/* Otherwise, calculate and store the equivalent PV projection parameters. */ + } else { + pv = co[ 0 ][ 0 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_0", &pv, + AST__FLOAT, NULL, status ); + + pv = co[ 0 ][ 1 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_1", &pv, + AST__FLOAT, NULL, status ); + + pv = co[ 0 ][ 2 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_2", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 0 ][ 3 ] != AST__BAD ) pv += co[ 0 ][ 3 ]; + if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_4", &pv, + AST__FLOAT, NULL, status ); + + pv = co[ 0 ][ 5 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_5", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 0 ][ 4 ] != AST__BAD ) pv += co[ 0 ][ 4 ]; + if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_6", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 0 ][ 6 ] != AST__BAD ) pv += co[ 0 ][ 6 ]; + if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_7", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 0 ][ 8 ] != AST__BAD ) pv += co[ 0 ][ 8 ]; + if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_8", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 0 ][ 9 ] != AST__BAD ) pv += co[ 0 ][ 9 ]; + if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_9", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 0 ][ 7 ] != AST__BAD ) pv += co[ 0 ][ 7 ]; + if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ]; + if( pv != AST__BAD ) SetValue( out, "PV1_10", &pv, + AST__FLOAT, NULL, status ); + + pv = co[ 1 ][ 0 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_0", &pv, + AST__FLOAT, NULL, status ); + + pv = co[ 1 ][ 2 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_1", &pv, + AST__FLOAT, NULL, status ); + + pv = co[ 1 ][ 1 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_2", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 1 ][ 4 ] != AST__BAD ) pv += co[ 1 ][ 4 ]; + if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_4", &pv, + AST__FLOAT, NULL, status ); + + pv = co[ 1 ][ 5 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_5", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 1 ][ 3 ] != AST__BAD ) pv += co[ 1 ][ 3 ]; + if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_6", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 1 ][ 7 ] != AST__BAD ) pv += co[ 1 ][ 7 ]; + if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_7", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 1 ][ 9 ] != AST__BAD ) pv += co[ 1 ][ 9 ]; + if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_8", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 1 ][ 8 ] != AST__BAD ) pv += co[ 1 ][ 8 ]; + if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_9", &pv, + AST__FLOAT, NULL, status ); + + pv = 0.0; + if( co[ 1 ][ 6 ] != AST__BAD ) pv += co[ 1 ][ 6 ]; + if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ]; + if( pv != AST__BAD ) SetValue( out, "PV2_10", &pv, + AST__FLOAT, NULL, status ); + +/* From an example header provided by Bill Joye, it seems that the SAO + polynomial includes the rotation and scaling effects of the CD matrix. + Therefore we mark as read all CDi_j, CDELT and CROTA values. Without + this, the rotation and scaling would be applied twice. First, mark the + original values as having been used, no matter which FitsChan they are + in. */ + GetValue( this, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( this, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status ); + + GetValue( out, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status ); + GetValue( out, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status ); + +/* Now store new default values in the returned FitsChan. */ + pv = 1.0; + SetValue( out, "PC1_1", &pv, AST__FLOAT, NULL, + status ); + SetValue( out, "PC2_2", &pv, AST__FLOAT, NULL, + status ); + SetValue( out, "CDELT1", &pv, AST__FLOAT, NULL, + status ); + SetValue( out, "CDELT2", &pv, AST__FLOAT, NULL, + status ); + + pv = 0.0; + SetValue( out, "PC1_2", &pv, AST__FLOAT, NULL, + status ); + SetValue( out, "PC2_1", &pv, AST__FLOAT, NULL, + status ); + +/* Indicate we have converted an SAO header. */ + result = 1; + } + } + } + +/* Return a flag indicating if an SAO header was found. */ + return result; +} +#undef NC + +static int SearchCard( AstFitsChan *this, const char *name, + const char *method, const char *class, int *status ){ + +/* +* Name: +* SearchCard + +* Purpose: +* Search the whole FitsChan for a card refering to given keyword. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int SearchCard( AstFitsChan *this, const char *name, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Searches the whole FitsChan for a card refering to the supplied keyword, +* and makes it the current card. The card following the current card is +* checked first. If this is not the required card, then a search is +* performed starting with the first keyword in the FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a string holding the keyword name. +* method +* Pointer to string holding name of calling method. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if a card was found refering to the given +* keyword. Otherwise zero is returned. + +* Notes: +* - If a NULL pointer is supplied for "name" then the current card +* is left unchanged. +* - The current card is set to NULL (end-of-file) if no card can be +* found for the supplied keyword. +*/ + +/* Local Variables: */ + int ret; /* Was a card found? */ + +/* Check the global status, and supplied keyword name. */ + if( !astOK || !name ) return 0; + +/* Indicate that no card has been found yet. */ + ret = 0; + +/* The required card is very often the next card in the FitsChan, so check the + next card, and only search the entire FitsChan if the check fails. */ + MoveCard( this, 1, method, class, status ); + if( !astFitsEof( this ) && + !Ustrncmp( CardName( this, status ), name, FITSNAMLEN, status ) ){ + ret = 1; + +/* If the next card is not the required card, rewind the FitsChan back to + the first card. */ + } else { + astClearCard( this ); + +/* Attempt to find the supplied keyword, searching from the first card. */ + ret = FindKeyCard( this, name, method, class, status ); + } + +/* Return. */ + return ret; +} + +static void SetAlgCode( char *buf, const char *algcode, int *status ){ +/* +* Name: +* SetAlgCode + +* Purpose: +* Create a non-linear CTYPE string from a system code and an algorithm +* code. + +* Type: +* Private function. + +* Synopsis: +* void SetAlgCode( char *buf, const char *algcode, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* FITS-WCS paper 1 says that non-linear axes must have a CTYPE of the +* form "4-3" (e.g. "VRAD-TAB"). This function handles the truncation +* of long system codes, or the padding of short system codes. + +* Parameters: +* buf +* A buffer in which is stored the system code. Modified on exit to +* hold the combined CTYPE value. It should have a length long +* enough to hold the system code and the algorithm code. +* algcode +* Pointer to a string holding the algorithm code (with a leading +* "-", e.g. "-TAB"). +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int nc; + +/* Check inherited status */ + if( !astOK ) return; + +/* Pad the supplied string to at least 4 characters using "-" characters. */ + nc = strlen( buf ); + while( nc < 4 ) buf[ nc++ ] = '-'; + +/* Insert the null-terminated code at position 4. */ + strcpy( buf + 4, algcode ); +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* FitsChan member function (over-rides the astSetAttrib protected +* method inherited from the Channel class). + +* Description: +* This function assigns an attribute value for a FitsChan, the +* attribute and its value being specified by means of a string of + +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the FitsChan. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + const char *class; /* Object class */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + int offset; /* Offset of attribute string */ + int warn; /* Offset of Warnings string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Obtain the object class. */ + class = astGetClass( this ); + +/* Card. */ +/* ----- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "card= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetCard( this, ival ); + +/* Encoding. */ +/* --------- */ + } else if( nc = 0, + ( 0 == astSscanf( setting, "encoding=%n%*[^\n]%n", &ival, &nc ) ) + && ( nc >= len ) ) { + nc = ChrLen( setting + ival, status ); + if( !Ustrncmp( setting + ival, NATIVE_STRING, nc, status ) ){ + astSetEncoding( this, NATIVE_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSPC_STRING, nc, status ) ){ + astSetEncoding( this, FITSPC_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSPC_STRING2, nc, status ) ){ + astSetEncoding( this, FITSPC_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSWCS_STRING, nc, status ) ){ + astSetEncoding( this, FITSWCS_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSWCS_STRING2, nc, status ) ){ + astSetEncoding( this, FITSWCS_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSIRAF_STRING, nc, status ) ){ + astSetEncoding( this, FITSIRAF_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSIRAF_STRING2, nc, status ) ){ + astSetEncoding( this, FITSIRAF_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSAIPS_STRING, nc, status ) ){ + astSetEncoding( this, FITSAIPS_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSAIPS_STRING2, nc, status ) ){ + astSetEncoding( this, FITSAIPS_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING, nc, status ) ){ + astSetEncoding( this, FITSAIPSPP_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING2, nc, status ) ){ + astSetEncoding( this, FITSAIPSPP_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSCLASS_STRING, nc, status ) ){ + astSetEncoding( this, FITSCLASS_ENCODING ); + } else if( !Ustrncmp( setting + ival, FITSCLASS_STRING2, nc, status ) ){ + astSetEncoding( this, FITSCLASS_ENCODING ); + } else if( !Ustrncmp( setting + ival, DSS_STRING, nc, status ) ){ + astSetEncoding( this, DSS_ENCODING ); + } else { + astError( AST__BADAT, "astSet(%s): Unknown encoding system '%s' " + "requested for a %s.", status, class, setting + ival, class ); + } + +/* FitsDigits. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "fitsdigits= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetFitsDigits( this, ival ); + +/* FitsAxisOrder. */ +/* -------------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "fitsaxisorder=%n%*[^\n]%n", + &offset, &nc ) ) + && ( nc >= len ) ) { + astSetFitsAxisOrder( this, setting + offset ); + +/* CDMatrix */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "cdmatrix= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetCDMatrix( this, ival ); + +/* DefB1950 */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "defb1950= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetDefB1950( this, ival ); + +/* TabOK */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "tabok= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetTabOK( this, ival ); + +/* CarLin */ +/* ------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "carlin= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetCarLin( this, ival ); + +/* PolyTan */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "polytan= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetPolyTan( this, ival ); + +/* Iwc */ +/* --- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "iwc= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetIwc( this, ival ); + +/* Clean */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "clean= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetClean( this, ival ); + +/* Warnings. */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "warnings=%n%*[^\n]%n", &warn, &nc ) ) + && ( nc >= len ) ) { + astSetWarnings( this, setting + warn ); + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + } else if ( MATCH( "ncard" ) || + MATCH( "cardtype" ) || + MATCH( "cardcomm" ) || + MATCH( "cardname" ) || + MATCH( "nkey" ) || + MATCH( "allwarnings" ) ){ + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetCard( AstFitsChan *this, int icard, int *status ){ + +/* +*+ +* Name: +* astSetCard + +* Purpose: +* Set the value of the Card attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" + +* void astSetCard( AstFitsChan *this, int icard ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function sets the value of the Card attribute for the supplied +* FitsChan. This is the index of the next card to be read from the +* FitsChan. If a value of 1 or less is supplied, the first card in +* the FitsChan will be read next. If a value greater than the number +* of cards in the FitsChan is supplied, the FitsChan will be left in an +* "end-of-file" condition, in which no further read operations can be +* performed. + +* Parameters: +* this +* Pointer to the FitsChan. +* icard +* The index of the next card to read. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*- +*/ + +/* Check the supplied object. */ + if ( !this ) return; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Rewind the FitsChan. */ + astClearCard( this ); + +/* Move forward the requested number of cards. */ + MoveCard( this, icard - 1, "astSetCard", astGetClass( this ), status ); + +/* Return. */ + return; +} + +static void SetItem( double ****item, int i, int jm, char s, double val, int *status ){ +/* +* Name: +* SetItem + +* Purpose: +* Store a value for a axis keyword value in a FitStore structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void SetItem( double ****item, int i, int jm, char s, double val, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The supplied keyword value is stored in the specified array, +* at a position indicated by the axis and co-ordinate version. +* The array is created or extended as necessary to make room for +* the new value. Any old value is over-written. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->crval) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword values. These arrays of keyword values have +* one element for every pixel axis (j) or projection parameter (m). +* i +* The zero based intermediate axis index in the range 0 to 98. Set +* this to zero for keywords (e.g. CRPIX) which are not indexed by +* intermediate axis number. +* jm +* The zero based pixel axis index (in the range 0 to 98) or parameter +* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for +* keywords (e.g. CRVAL) which are not indexed by either pixel axis or +* parameter number. +* val +* The keyword value to store. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int el; /* Array index */ + int nel; /* Number of elements in array */ + int si; /* Integer co-ordinate version index */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Convert the character co-ordinate version into an integer index, and + check it is within range. The primary axis description (s=' ') is + given index zero. 'A' is 1, 'B' is 2, etc. */ + if( s == ' ' ) { + si = 0; + } else if( islower(s) ){ + si = (int) ( s - 'a' ) + 1; + } else { + si = (int) ( s - 'A' ) + 1; + } + if( si < 0 || si > 26 ) { + astError( AST__INTER, "SetItem(fitschan): AST internal error; " + "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); + +/* Check the intermediate axis index is within range. */ + } else if( i < 0 || i > 98 ) { + astError( AST__INTER, "SetItem(fitschan): AST internal error; " + "intermediate axis index %d is invalid.", status, i ); + +/* Check the pixel axis or parameter index is within range. */ + } else if( jm < 0 || jm > 99 ) { + astError( AST__INTER, "SetItem(fitschan): AST internal error; " + "pixel axis or parameter index %d is invalid.", status, jm ); + +/* Otherwise proceed... */ + } else { + +/* Store the current number of coordinate versions in the supplied array */ + nel = astSizeOf( (void *) *item )/sizeof(double **); + +/* If required, extend the array located by the supplied pointer so that + it is long enough to hold the specified co-ordinate version. */ + if( nel < si + 1 ){ + *item = (double ***) astGrow( (void *) *item, si + 1, + sizeof(double **) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise the new elements to hold NULL. Note, astGrow may add more + elements to the array than is actually needed, so use the actual current + size of the array as implied by astSize rather than the index si. */ + for( el = nel; + el < astSizeOf( (void *) *item )/sizeof(double **); + el++ ) (*item)[el] = NULL; + } + } + +/* If the above went OK... */ + if( astOK ){ + +/* Store the currrent number of intermediate axes in the supplied array */ + nel = astSizeOf( (void *) (*item)[si] )/sizeof(double *); + +/* If required, extend the array so that it is long enough to hold the + specified intermediate axis. */ + if( nel < i + 1 ){ + (*item)[si] = (double **) astGrow( (void *) (*item)[si], i + 1, + sizeof(double *) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise the new elements to hold NULL. */ + for( el = nel; + el < astSizeOf( (void *) (*item)[si] )/sizeof(double *); + el++ ) (*item)[si][el] = NULL; + } + } + +/* If the above went OK... */ + if( astOK ){ + +/* Store the current number of pixel axis or parameter values in the array. */ + nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(double); + +/* If required, extend the array so that it is long enough to hold the + specified axis. */ + if( nel < jm + 1 ){ + (*item)[si][i] = (double *) astGrow( (void *) (*item)[si][i], + jm + 1, sizeof(double) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise the new elements to hold AST__BAD. */ + for( el = nel; + el < astSizeOf( (void *) (*item)[si][i] )/sizeof(double); + el++ ) (*item)[si][i][el] = AST__BAD; + } + } + +/* If the above went OK, store the supplied keyword value. */ + if( astOK ) (*item)[si][i][jm] = val; + } + } + } +} + +static void SetItemC( char *****item, int i, int jm, char s, const char *val, + int *status ){ +/* +* Name: +* SetItemC + +* Purpose: +* Store a character string for an axis keyword value in a FitStore +* structure. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void SetItemC( char *****item, int i, int jm, char s, const char *val, +* int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The supplied keyword string value is stored in the specified array, +* at a position indicated by the axis and co-ordinate version. +* The array is created or extended as necessary to make room for +* the new value. Any old value is over-written. + +* Parameters: +* item +* The address of the pointer within the FitsStore which locates the +* arrays of values for the required keyword (eg &(store->ctype) ). +* The array located by the supplied pointer contains a vector of +* pointers. Each of these pointers is associated with a particular +* co-ordinate version (s), and locates an array of pointers for that +* co-ordinate version. Each such array of pointers has an element +* for each intermediate axis number (i), and the pointer locates an +* array of axis keyword string pointers. These arrays of keyword +* string pointers have one element for every pixel axis (j) or +* projection parameter (m). +* i +* The zero based intermediate axis index in the range 0 to 98. Set +* this to zero for keywords (e.g. RADESYS) which are not indexed by +* intermediate axis number. +* jm +* The zero based pixel axis index (in the range 0 to 98) or parameter +* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for +* keywords (e.g. CTYPE) which are not indexed by either pixel axis or +* parameter number. +* val +* The keyword string value to store. A copy of the supplied string +* is taken. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int el; /* Array index */ + int nel; /* Number of elements in array */ + int si; /* Integer co-ordinate version index */ + +/* Check the inherited status and the supplied pointer. */ + if( !astOK || !val ) return; + +/* Convert the character co-ordinate version into an integer index, and + check it is within range. The primary axis description (s=' ') is + given index zero. 'A' is 1, 'B' is 2, etc. */ + if( s == ' ' ) { + si = 0; + } else if( islower(s) ){ + si = (int) ( s - 'a' ) + 1; + } else { + si = (int) ( s - 'A' ) + 1; + } + if( si < 0 || si > 26 ) { + astError( AST__INTER, "SetItemC(fitschan): AST internal error; " + "co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s ); + +/* Check the intermediate axis index is within range. */ + } else if( i < 0 || i > 98 ) { + astError( AST__INTER, "SetItemC(fitschan): AST internal error; " + "intermediate axis index %d is invalid.", status, i ); + +/* Check the pixel axis or parameter index is within range. */ + } else if( jm < 0 || jm > 99 ) { + astError( AST__INTER, "SetItemC(fitschan): AST internal error; " + "pixel axis or parameter index %d is invalid.", status, jm ); + +/* Otherwise proceed... */ + } else { + +/* Store the current number of coordinate versions in the supplied array */ + nel = astSizeOf( (void *) *item )/sizeof(char ***); + +/* If required, extend the array located by the supplied pointer so that + it is long enough to hold the specified co-ordinate version. */ + if( nel < si + 1 ){ + *item = (char ****) astGrow( (void *) *item, si + 1, + sizeof(char ***) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise the new elements to hold NULL. Note, astGrow may add more + elements to the array than is actually needed, so use the actual current + size of the array as implied by astSize rather than the index si. */ + for( el = nel; + el < astSizeOf( (void *) *item )/sizeof(char ***); + el++ ) (*item)[el] = NULL; + } + } + +/* If the above went OK... */ + if( astOK ){ + +/* Store the currrent number of intermediate axes in the supplied array */ + nel = astSizeOf( (void *) (*item)[si] )/sizeof(char **); + +/* If required, extend the array so that it is long enough to hold the + specified intermediate axis. */ + if( nel < i + 1 ){ + (*item)[si] = (char ***) astGrow( (void *) (*item)[si], i + 1, + sizeof(char **) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise the new elements to hold NULL. */ + for( el = nel; + el < astSizeOf( (void *) (*item)[si] )/sizeof(char **); + el++ ) (*item)[si][el] = NULL; + } + } + +/* If the above went OK... */ + if( astOK ){ + +/* Store the current number of pixel axis or parameter values in the array. */ + nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *); + +/* If required, extend the array so that it is long enough to hold the + specified axis. */ + if( nel < jm + 1 ){ + (*item)[si][i] = (char **) astGrow( (void *) (*item)[si][i], + jm + 1, sizeof(char *) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise the new elements to hold NULL. */ + for( el = nel; + el < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *); + el++ ) (*item)[si][i][el] = NULL; + } + } + +/* If the above went OK... */ + if( astOK ){ + +/* Store a copy of the supplied string, using any pre-allocated memory. */ + (*item)[si][i][jm] = (char *) astStore( (void *) (*item)[si][i][jm], + (void *) val, + strlen( val ) + 1 ); + } + } + } + } +} + +static void SetSourceFile( AstChannel *this_channel, const char *source_file, + int *status ) { +/* +* Name: +* SetSourceFile + +* Purpose: +* Set a new value for the SourceFile attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void SetSourceFile( AstChannel *this, const char *source_file, +* int *status ) + +* Class Membership: +* FitsChan member function (over-rides the astSetSourceFile +* method inherited from the Channel class). + +* Description: +* This function stores the supplied string as the new value for the +* SourceFile attribute. In addition, it also attempts to open the +* file, read FITS headers from it and append them to the end of the +* FitsChan. It then closes the SourceFile. + +* Parameters: +* this +* Pointer to the FitsChan. +* source_file +* The new attribute value. Should be the path to an existing text +* file, holding FITS headers (one per line) +* status +* Inherited status pointer. + +*/ + +/* Local Constants: */ +#define ERRBUF_LEN 80 + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + FILE *fd; /* Descriptor for source file */ + char *errstat; /* Pointer for system error message */ + char card[ AST__FITSCHAN_FITSCARDLEN + 2 ]; /* Buffer for source line */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Invoke the parent astSetSourceFile method to store the supplied + string in the Channel structure. */ + (*parent_setsourcefile)( this_channel, source_file, status ); + +/* Attempt to open the file. */ + fd = NULL; + if( astOK ) { + fd = fopen( source_file, "r" ); + if( !fd ) { + if ( errno ) { +#if HAVE_STRERROR_R + strerror_r( errno, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( errno ); +#endif + astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input " + "SourceFile '%s' - %s.", status, astGetClass( this ), + source_file, errstat ); + } else { + astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input " + "SourceFile '%s'.", status, astGetClass( this ), + source_file ); + } + } + } + +/* Move the FitsChan to EOF */ + astSetCard( this, INT_MAX ); + +/* Read each line from the file, remove trailing space, and append to the + FitsChan. */ + while( astOK && fgets( card, AST__FITSCHAN_FITSCARDLEN + 2, fd ) ) { + card[ astChrLen( card ) ] = 0; + astPutFits( this, card, 0 ); + } + +/* Close the source file. */ + if( fd ) fclose( fd ); + +} + +static void SetTableSource( AstFitsChan *this, + void (*tabsource)( void ), + void (*tabsource_wrap)( void (*)( void ), + AstFitsChan *, const char *, + int, int, int * ), + int *status ){ + +/* +*+ +* Name: +* astSetTableSource + +* Purpose: +* Register source and wrapper function for accessing tables in FITS files. + +* Type: +* Protected function. + +* Synopsis: +* #include "fitschan.h" +* void astSetTableSource( AstFitsChan *this, +* void (*tabsource)( void ), +* void (*tabsource_wrap)( void (*)( void ), +* AstFitsChan *, const char *, +* int, int, int * ), +* int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function registers a table source function and its wrapper. A +* wrapper function exists to adapt the API of the table source +* function to the needs of different languages. The wrapper is called +* from the FitsChan code. The wrapper then adjusts the arguments as +* required and then calls the actualy table source function. + +* Parameters: +* this +* Pointer to the FitsChan. +* tabsource +* Pointer to the table source function. The API for this function +* will depend on the language, and so is cast to void here. It +* should be cast to the required form within the wrapper function. +* tabsource_wrap +* The wrapper function. +*- +*/ + +/* Local Variables: */ + +/* Check the global error status. */ + if ( !astOK ) return; + this->tabsource = tabsource; + this->tabsource_wrap = tabsource_wrap; +} + +static void SetValue( AstFitsChan *this, const char *keyname, void *value, + int type, const char *comment, int *status ){ + +/* +* Name: +* SetValue + +* Purpose: +* Save a FITS keyword value, over-writing any existing keyword value. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void SetValue( AstFitsChan *this, char *keyname, void *value, +* int type, const char *comment, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function saves a keyword value as a card in the supplied +* FitsChan. Comment cards are always inserted in-front of the current +* card. If the keyword is not a comment card, any existing value +* for the keyword is over-written with the new value (even if it is +* marked as having been read). Otherwise, (i.e. if it is not a comment +* card, and no previous value exists) it is inserted in front +* of the current card. + +* Parameters: +* this +* A pointer to the FitsChan. +* keyname +* A pointer to a string holding the keyword name. +* value +* A pointer to a buffer holding the keyword value. For strings, +* the buffer should hold a pointer to the character string. +* type +* The FITS data type of the supplied keyword value. +* comment +* A comment to store with the keyword. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Nothing is stored if a NULL pointer is supplied for "value". +* - If the keyword has a value of AST__BAD then nothing is stored, +* and an error is reported. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + FitsCard *card; /* Pointer to original current card */ + const char *class; /* Class name to include in error messages */ + const char *method; /* Method name to include in error messages */ + int newcard; /* Has the original current card been deleted? */ + int old_ignore_used; /* Original setting of external ignore_used variable */ + int stored; /* Has the keyword been stored? */ + +/* Check the status and supplied value pointer. */ + if( !astOK || !value ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Set up the method and class names for inclusion in error mesages. */ + method = "astWrite"; + class = astGetClass( this ); + +/* Comment card are always inserted in-front of the current card. */ + if ( type == AST__COMMENT ) { + SetFits( this, keyname, value, type, comment, 0, status ); + +/* Otherwise... */ + } else { + +/* Report an error if a bad value is stored for a keyword. */ + if( type == AST__FLOAT ){ + if( *( (double *) value ) == AST__BAD && astOK ) { + astError( AST__BDFTS, "%s(%s): The required FITS keyword " + "\"%s\" is indeterminate.", status, method, class, keyname ); + } + } + +/* Save a pointer to the current card. */ + card = (FitsCard *) this->card; + +/* Indicate that we should not skip over cards marked as having been + read. */ + old_ignore_used = ignore_used; + ignore_used = 0; + +/* Indicate that we have not yet stored the keyword value. */ + stored = 0; + +/* Attempt to find a card refering to the supplied keyword. If one is + found, it becomes the current card. */ + if( SearchCard( this, keyname, "astWrite", astGetClass( this ), status ) ){ + +/* If the card which was current on entry to this function will be + over-written, we will need to take account of this when re-instating the + original current card. Make a note of this. */ + newcard = ( card == (FitsCard *) this->card ); + +/* Replace the current card with a card holding the supplied information. */ + SetFits( this, keyname, value, type, comment, 1, status ); + stored = 1; + +/* If we have just replaced the original current card, back up a card + so that the replacement card becomes the current card. */ + if( newcard ) { + MoveCard( this, -1, "astWrite", astGetClass( this ), status ); + +/* Otherwise, re-instate the original current card. */ + } else { + this->card = (void *) card; + } + } + +/* If the keyword has not yet been stored (i.e. if it did not exist in the + FitsChan), re-instate the original current card and insert the new card + before the original current card, leaving the current card unchanged. */ + if( !stored ) { + this->card = (void *) card; + SetFits( this, keyname, value, type, comment, 0, status ); + } + +/* Re-instate the original flag indicating if cards marked as having been + read should be skipped over. */ + ignore_used = old_ignore_used; + } +} + +static void Shpc1( double xmin, double xmax, int n, double *d, double *w, + int *status ){ +/* +* Name: +* Shpc1 + +* Purpose: +* Modifies a one-dimensional polynomial to scale the polynomial argument. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void Shpc1( double xmin, double xmax, int n, double *d, double *w, +* int *status ) + +* Description: +* Given the coefficients of a one-dimensional polynomial P(u) defined on a +* unit interval (i.e. -1 <= u <= +1 ), find the coefficients of another +* one-dimensional polynomial Q(x) where: +* +* Q(x) = P(u) +* u = ( 2*x - ( xmax + xmin ) ) / ( xmax - xmin ) +* +* That is, u is a scaled version of x, such that the unit interval in u +* maps onto (xmin:xmax) in x. + +* Parameters: +* xmin +* X value corresponding to u = -1 +* xmax +* X value corresponding to u = +1 +* n +* One more than the maximum power of u within P. +* d +* An array of n elements supplied holding the coefficients of P such +* that the coefficient of (u^i) is held in element (i). +* w +* An array of n elements returned holding the coefficients of Q such +* that the coefficient of (x^i) is held in element (i). +* status +* Pointer to the inherited status variable. + +* Notes: +* - Vaguely inspired by the Numerical Recipes routine "pcshft". But the +* original had bugs, so I wrote this new version from first principles. + +*/ + +/* Local Variables: */ + double b; + double a; + int j; + int i; + +/* Check inherited status */ + if( !astOK ) return; + +/* Get the scale and shift terms so that u = a*x + b */ + a = 2.0/( xmax - xmin ); + b = ( xmin + xmax )/( xmin - xmax ); + +/* Initialise the returned coeffs */ + for( i = 0; i < n; i++ ) w[ i ] = 0.0; + +/* The supplied Polynomial is + + P(u) = d0 + d1*u + d2*u^2 + ... + + = d0 + u*( d1 + u*( d2 + ... u*( d{n-1} ) ) ) . . . . . (1) + + = d0 + (a*x+b)*( d1 + (a*x+b)*( d2 + ... (a*x+b)*( d[n-1] ) ) ) + + The inner-most parenthesised expression is a polynomial of order zero + (a constant - d[n-1]). Store the coefficients of this zeroth order + polynomial in the returned array. The "w" array is used to hold the + coefficients of Q, i.e. coefficients of powers of "x", not "u", but + since the inner-most polynomial is a constant, it makes no difference + (x^0 == u^0 == 1). */ + w[ 0 ] = d[ n - 1 ]; + +/* Now loop through each remaining level of parenthetic nesting in (1). At + each level, the parenthesised expression represents a polynomial of order + "i". At the end of each pass though this loop, the returned array "w" + holds the coefficients of this "i"th order polynomial. So on the last + loop, i = n-1, "w" holds the required coefficients of Q. */ + for( i = 1; i < n; i++ ) { + +/* If "R" is the polynomial at the "i-1"th level of nesting (the + coefficiemts of which are currently held in "w"), and "S" is the + polynomial at the "i"th level of nesting, we can see from (1) that: + + S = d[ n - 1 - i ] + u*R + + Substituting for "u", this becomes + + S = d[ n - 1 - i ] + ( a*x + b )*R + = d[ n - 1 - i ] + a*R*x + b*R + + Looking at each of these three terms in reverse order: + + 1) The "b*R" term is implemented by simply scaling the current contents + of the "w" array by "b"; in the "a*R*x" term. + + 2) In "a*R*x", the effect of multiplying by "x" is to move the existing + coefficients in "w" up one element. We then multiply the shifted + coefficients by "a" and add them onto the coefficients produced at + step 1) above. + + We know that "w" still contains the initial zeros at indices higher than + "i" so we only need to scale the bottom "i" elements. We do not do the + zeroth term in this loop since there is no lower term to shift up into + it. */ + + for( j = i; j > 0; j-- ){ + w[ j ] = b*w[ j ] + a*w[ j - 1 ]; + } + +/* Now do the zeroth term. Scale the existing zeroth term by "b" as + required by step 1) and add on the first term, the constant + "d[ n - 1 - i ]". Step 2) is a no-op, since in effect the value of + "w[-1]" is zero. */ + w[ 0 ] = d[ n - i - 1 ] + b*w[ 0 ]; + } + +} + +static void ShowFits( AstFitsChan *this, int *status ){ + +/* +*++ +* Name: +c astShowFits +f AST_SHOWFITS + +* Purpose: +* Display the contents of a FitsChan on standard output. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c void astShowFits( AstFitsChan *this ) +f CALL AST_SHOWFITS( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* formats and displays all the cards in a FitsChan on standard output. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */ + int icard; /* Current card index on entry */ + int old_ignore_used; /* Original value of external variable ignore_used */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Store the current card index. */ + icard = astGetCard( this ); + +/* Indicate that cards which have been read into an AST object should skipped + over by the functions which navigate the linked list of cards. */ + old_ignore_used = ignore_used; + ignore_used = 1; + +/* Ensure that the first card in the FitsChan will be the next one to be + read. */ + astSetCard( this, 1 ); + +/* Loop round obtaining and writing out each card, until all cards have been + processed. */ + while( !astFitsEof( this ) && astOK ){ + +/* Get the current card, and display it. The call to astFindFits increments + the current card. */ + if( astFindFits( this, "%f", card, 1 ) ) printf( "%s\n", card ); + } + +/* Re-instate the original flag indicating if cards marked as having been + read should be skipped over. */ + ignore_used = old_ignore_used; + +/* Set the current card index back to what it was on entry. */ + astSetCard( this, icard ); + +} + +static int Similar( const char *str1, const char *str2, int *status ){ +/* +* Name: +* Similar + +* Purpose: +* Are two string effectively the same to human readers? + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void Similar( const char *str1, const char *str2, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function returns a non-zero value if the two supplied strings +* are equivalent to a human reader. This is assumed to be the case if +* the strings are equal apart from leading and trailing white space, +* multiple embedded space, and case. + +* Parameters: +* str1 +* The first string +* str2 +* The second string +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the two supplied strings are equivalent, and zero +* otherwise. +*/ + +/* Local Variables: */ + const char *ea; /* Pointer to end of string a */ + const char *eb; /* Pointer to end of string b */ + const char *a; /* Pointer to next character in string a */ + const char *b; /* Pointer to next character in string b */ + int result; /* Are the two strings equivalent? */ + int ss; /* Skip subsequent spaces? */ + +/* Initialise */ + result = 0; + +/* Check the status and supplied value pointer. */ + if( !astOK ) return result; + +/* Initialise pointers into the two strings. */ + a = str1; + b = str2; + +/* Get a pointer to the character following the last non-blank character in + each string. */ + ea = a + ChrLen( a, status ) - 1; + eb = b + ChrLen( b, status ) - 1; + +/* Set a flag indicating that spaces before the next non-blank character + should be ignored. */ + ss = 1; + +/* Compare the strings. */ + while( 1 ){ + +/* Move on to the next significant character in both strings. */ + while( a < ea && *a == ' ' && ss ) a++; + while( b < eb && *b == ' ' && ss ) b++; + +/* If one string has been exhausted but the other has not, the strings + are not equivalent. */ + if( ( a < ea && b == eb ) || ( a == ea && b < eb ) ) { + break; + +/* If both strings have been exhausted simultaneously, the strings + are equivalent. */ + } else if( b == eb && a == ea ) { + result = 1; + break; + +/* If neither string has been exhausted, compare the current character + for equality, ignoring case. Break if they are different. */ + } else if( tolower( *a ) != tolower( *b ) ){ + break; + +/* If the two characters are both spaces, indicate that subsequent spaces + should be skipped. */ + } else if( *a == ' ' ) { + ss = 1; + +/* If the two characters are not spaces, indicate that subsequent spaces + should not be skipped. */ + } else { + ss = 0; + } + +/* Move on to the next characters. */ + a++; + b++; + } + +/* Return the result. */ + return result; +} + +static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) { +/* +* Name: +* SinkWrap + +* Purpose: +* Wrapper function to invoke a C FitsChan sink function. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function invokes the sink function whose pointer is +* supplied in order to write an output line to an external data +* store. + +* Parameters: +* sink +* Pointer to a sink function, whose single parameter is a +* pointer to a const, null-terminated string containing the +* text to be written, and which returns void. This is the form +* of FitsChan sink function employed by the C language interface +* to the AST library. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the sink function. */ + ( *sink )( line ); +} + +static AstMapping *SIPMapping( double *dim, FitsStore *store, char s, + int naxes, const char *method, + const char *class, int *status ){ +/* +* Name: +* SIPMapping + +* Purpose: +* Create a Mapping descriping "-SIP" (Spitzer) distortion. + +* Type: +* Private function. + +* Synopsis: +* AstMapping *SIPMapping( double *dim, FitsStore *store, char s, int naxes, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function uses the values in the supplied FitsStore to create a +* Mapping which implements the "-SIP" distortion code. This is the + +* code used by the Spitzer project and is described in: +* +* http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf +* +* SIP distortion can only be applied to axes 0 and 1. Other axes are +* passed unchanged by the returned Mapping. + +* Parameters: +* dim +* The dimensions of the array in pixels. AST__BAD is stored for +* each value if dimensions are not known. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* naxes +* The number of intermediate world coordinate axes (WCSAXES). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping. +*/ + +/* Local Variables: */ + AstMapping *ret; /* Pointer to the returned Mapping */ + AstPolyMap *pmap; /* PolyMap describing the distortion */ + AstPolyMap *pmap2; /* New PolyMap describing the distortion */ + double ****item; /* Address of FitsStore item to use */ + double *c; /* Pointer to start of coefficient description */ + double *coeff_f; /* Array of coeffs. for forward transformation */ + double *coeff_i; /* Array of coeffs. for inverse transformation */ + double cof; /* Coefficient value */ + double lbnd[ 2 ]; /* Lower bounds of fitted region */ + double ubnd[ 2 ]; /* Upper bounds of fitted region */ + int def; /* Is transformation defined? */ + int iin; /* Input (u or v) index */ + int iout; /* Output (U or V) index */ + int ncoeff_f; /* No. of coeffs. for forward transformation */ + int ncoeff_i; /* No. of coeffs. for inverse transformation */ + int p; /* Power of u or U */ + int pmax; /* Max power of u or U */ + int q; /* Power of v or V */ + int qmax; /* Max power of v or V */ + +/* Initialise the pointer to the returned Mapping. */ + ret = NULL; + +/* Check the global status. */ + if ( !astOK ) return ret; + +/* Store coefficients of the forward transformation: + ================================================ */ + +/* Indicate that we have as yet no coefficients for the forward polynomials. */ + ncoeff_f = 0; + +/* Indicate that we do not yet have any evidence that the forward + transformation is defined. */ + def = 0; + +/* Allocate workspace to hold descriptions of (initially) 20 coefficients used + within the forward polynomials. */ + coeff_f = astMalloc( sizeof( double )*20 ); + +/* Store the coefficients of the polynomial which produces each output + axis (U or V) in turn. */ + for( iout = 0; iout < 2; iout++ ){ + +/* Get a pointer to the FitsStore item holding the values defining this + output. */ + item = ( iout == 0 ) ? &(store->asip) : &(store->bsip); + +/* Get the largest powers used of u and v. */ + pmax = GetMaxI( item, s, status ); + qmax = GetMaxJM( item, s, status ); + +/* Loop round all combination of powers. */ + for( p = 0; p <= pmax; p++ ){ + for( q = 0; q <= qmax; q++ ){ + +/* Get the polynomial coefficient for this combination of powers. */ + cof = GetItem( item, p, q, s, NULL, method, class, status ); + +/* If there is no coefficient for this combination of powers, use a value + of zero. Otherwise indicate we have found at least one coefficient. */ + if( cof == AST__BAD ) { + cof = 0.0; + } else { + def = 1; + } + +/* The distortion polynomial gives a correction to be added on to the + input value. On the other hand, the returned Mapping is a direct + transformation from input to output. Therefore increment the coefficient + value by 1 for the term which corresponds to the current output axis. */ + if( p == ( 1 - iout ) && q == iout ) cof += 1.0; + +/* If the coefficient is not zero, store it in the array of coefficient + descriptions. */ + if( cof != 0.0 ) { + +/* Increment the number of coefficients for the forward polynomials. */ + ncoeff_f++; + +/* Ensure the "coeff_f" array is large enough to hold the new coefficient. */ + coeff_f = astGrow( coeff_f, sizeof( double )*4, ncoeff_f ); + if( astOK ) { + +/* Store it. Each coefficient is described by 4 values (since we have 2 + inputs to the Mapping). The first is the coefficient value, the second + is the (1-based) index of the output to which the coefficient relates. + The next is the power of input 0, and the last one is the power of input 1. */ + c = coeff_f + 4*( ncoeff_f - 1 ); + c[ 0 ] = cof; + c[ 1 ] = iout + 1; + c[ 2 ] = p; + c[ 3 ] = q; + } + } + } + } + } + +/* If no coefficients were supplied in the FitsStore, the forward + transformation is undefined. */ + if( !def ) ncoeff_f = 0; + +/* Store coefficients of the inverse transformation: + ================================================ */ + +/* Indicate that we have as yet no coefficients for the inverse polynomials. */ + ncoeff_i = 0; + +/* Indicate that we do not yet have any evidence that the forward + transformation is defined. */ + def = 0; + +/* Allocate workspace to hold descriptions of (initially) 20 coefficients used + within the inverse polynomials. */ + coeff_i = astMalloc( sizeof( double )*20 ); + +/* Store the coefficients of the polynomial which produces each input + axis (u or v) in turn. */ + for( iin = 0; iin < 2; iin++ ){ + +/* Get a pointer to the FitsStore item holding the values defining this + output. */ + item = ( iin == 0 ) ? &(store->apsip) : &(store->bpsip); + +/* Get the largest powers used of U and V. */ + pmax = GetMaxI( item, s, status ); + qmax = GetMaxJM( item, s, status ); + +/* Loop round all combination of powers. */ + for( p = 0; p <= pmax; p++ ){ + for( q = 0; q <= qmax; q++ ){ + +/* Get the polynomial coefficient for this combination of powers. */ + cof = GetItem( item, p, q, s, NULL, method, class, status ); + +/* If there is no coefficient for this combination of powers, use a value + of zero. Otherwise indicate we have found at least one coefficient. */ + if( cof == AST__BAD ) { + cof = 0.0; + } else { + def = 1; + } + +/* The distortion polynomial gives a correction to be added on to the + output value. On the other hand, the returned Mapping is a direct + transformation from output to input. Therefore increment the coefficient + value by 1 for the term which corresponds to the current input axis. */ + if( p == ( 1 - iin ) && q == iin ) cof += 1.0; + +/* If the coefficient is not zero, store it in the array of coefficient + descriptions. */ + if( cof != 0.0 ) { + +/* Increment the number of coefficients for the inverse polynomials. */ + ncoeff_i++; + +/* Ensure the "coeff_i" array is large enough to hold the new coefficient. */ + coeff_i = astGrow( coeff_i, sizeof( double )*4, ncoeff_i ); + if( astOK ) { + +/* Store it. Each coefficient is described by 4 values (since we have 2 + outputs to the Mapping). The first is the coefficient value, the second + is the (1-based) index of the input to which the coefficient relates. The + next is the power of output 0, and the last one is the power of output 1. */ + c = coeff_i + 4*( ncoeff_i - 1 ); + c[ 0 ] = cof; + c[ 1 ] = iin + 1; + c[ 2 ] = p; + c[ 3 ] = q; + } + } + } + } + } + +/* If no coefficients were supplied in the FitsStore, the forward + transformation is undefined. */ + if( !def ) ncoeff_i = 0; + +/* Create the returned Mapping: + ============================ */ + +/* If neither transformation is defined, create a UnitMap. */ + if( ncoeff_f == 0 && ncoeff_i == 0 ){ + ret = (AstMapping *) astUnitMap( naxes, "", status ); + +/* Otherwise, create a PolyMap to describe axes 0 and 1. */ + } else { + pmap = astPolyMap( 2, 2, ncoeff_f, coeff_f, ncoeff_i, coeff_i, "", status ); + +/* The inverse transformations supplied within SIP headers are often + inaccurate. So replace any existing inverse by sampling the supplied + transformation, and fitting a polynomial to the sampled positions. If + the fit fails to reach 0.01 pixel accuracy, forget it and rely on the + (slower) iterative inverse provided by the PolyMap class. Do the fit + over an area three times the size of the image to provide accurate + values outside the image.*/ + lbnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? -dim[ 0 ] : -1000.0; + lbnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? -dim[ 1 ] : -1000.0; + ubnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? 2*dim[ 0 ] : 2000.0; + ubnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? 2*dim[ 1 ] : 2000.0; + pmap2 = astPolyTran( pmap, (ncoeff_f == 0), 0.0001, 0.01, 7, lbnd, + ubnd ); + if( pmap2 ) { + (void) astAnnul( pmap ); + pmap = pmap2; + } else { + astSet( pmap, "IterInverse=1,NiterInverse=6,TolInverse=1.0E-8", + status ); + } + +/* Add the above Mapping in parallel with a UnitMap which passes any + other axes unchanged. */ + ret = AddUnitMaps( (AstMapping *) pmap, 0, naxes, status ); + pmap = astAnnul( pmap ); + } + +/* Free resources. */ + coeff_f = astFree( coeff_f ); + coeff_i = astFree( coeff_i ); + +/* Return the result. */ + return ret; +} + +static void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat, + int *wperm, char s, FitsStore *store, const char *method, + const char *class, int *status ){ +/* +* Name: +* SkyPole + +* Purpose: +* Put values for FITS keywords LONPOLE and LATPOLE into a FitsStore. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat, +* int *wperm, char s, FitsStore *store, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function calculates values for the LONPOLE and LATPOLE FITS +* keywords and stores them in the supplied FitsStore. LONPOLE and +* LATPOLE are the longitude and latitude of the celestial north pole +* in native spherical coordinates. + +* Parameters: +* map2 +* Pointer to the Mapping from Intermediate World Coordinates to Native +* Spherical Coordinates. +* map3 +* Pointer to the Mapping from Native Spherical Coordinates to celestial +* coordinates. +* ilon +* Zero-based index of longitude output from "map3". +* ilat +* Zero-based index of latitude output from "map3". +* wperm +* Pointer to an array of integers with one element for each axis of +* the current Frame. Each element holds the zero-based +* index of the FITS-WCS axis (i.e. the value of "i" in the keyword +* names "CTYPEi", "CRVALi", etc) which describes the Frame axis. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* store +* The FitsStore in which to store the FITS WCS keyword values. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding intermediate wcs coords */ + AstPointSet *pset2; /* PointSet holding final WCS coords */ + double **ptr1; /* Pointer to coordinate data */ + double **ptr2; /* Pointer to coordinate data */ + double alpha0; /* Long. of fiducial point in standard system */ + double alphap; /* Celestial longitude of native north pole */ + double deflonpole; /* Default value for lonpole */ + double delta0; /* Lat. of fiducial point in standard system */ + double latpole; /* Native latitude of celestial north pole */ + double lonpole; /* Native longitude of celestial north pole */ + double phi0; /* Native longitude at fiducial point */ + double theta0; /* Native latitude at fiducial point */ + int axlat; /* Index of latitude output from "map2" */ + int axlon; /* Index of longitude output from "map2" */ + int fits_ilat; /* FITS WCS axis index for latitude axis */ + int fits_ilon; /* FITS WCS axis index for longitude axis */ + int iax; /* Axis index */ + int nax; /* Number of IWC axes */ + int nax2; /* Number of WCS axes */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Store the indices of the native longitude and latitude outputs of the + WcsMap. */ + axlon = astGetWcsAxis( map2, 0 ); + axlat = astGetWcsAxis( map2, 1 ); + +/* Store the indices of the FITS WCS axes for longitude and latitude */ + fits_ilon = wperm[ ilon ]; + fits_ilat = wperm[ ilat ]; + +/* To find the longitude and latitude of the celestial north pole in native + spherical coordinates, we will transform the coords of the celestial north + pole into spherical cords using the inverse of "map2", and if the resulting + native spherical coords differ from the default values of LONPOLE and + LATPOLE, we store them in the FitsStore. However, for zenithal projections, + any value can be used simply by introducing an extra rotation into the + (X,Y) projection plane. If values have been set in the WcsMap (as + projection parameters PVi_3 and PVi_4 for longitude axis "i") uses + them. Otherwise, set the values bad to indicate that the default values + should be used. Note, these projection parameters are used for other + purposes in a TPN projection. */ + lonpole = AST__BAD; + latpole = AST__BAD; + if( astIsZenithal( map2 ) ) { + if( astGetWcsType( map2 ) != AST__TPN ) { + lonpole = astTestPV( map2, axlon, 3 ) ? astGetPV( map2, axlon, 3 ) + : AST__BAD; + latpole = astTestPV( map2, axlon, 4 ) ? astGetPV( map2, axlon, 4 ) + : AST__BAD; + } + +/* For non-zenithal projections, do the full calculation. */ + } else { + +/* Allocate resources. */ + nax = astGetNin( map2 ); + pset1 = astPointSet( 1, nax, "", status ); + ptr1 = astGetPoints( pset1 ); + nax2 = astGetNout( map3 ); + pset2 = astPointSet( 1, nax2, "", status ); + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + +/* Calculate the longitude and latitude of the celestial north pole + in native spherical coordinates (using the inverse of map3). These + values correspond to the LONPOLE and LATPOLE keywords. */ + for( iax = 0; iax < nax2; iax++ ) ptr2[ iax ][ 0 ] = 0.0; + ptr2[ ilat ][ 0 ] = AST__DPIBY2; + (void) astTransform( map3, pset2, 0, pset1 ); + +/* Retrieve the latitude and longitude (in the standard system) of the + fiducial point (i.e. CRVAL), in radians. */ + delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status ); + if( delta0 == AST__BAD ) delta0 = 0.0; + delta0 *= AST__DD2R; + alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status ); + if( alpha0 == AST__BAD ) alpha0 = 0.0; + alpha0 *= AST__DD2R; + +/* The default value of the LATPOLE is defined by equation 8 of FITS-WCS + paper II (taking the +ve signs). Find this value. */ + if( WcsNatPole( NULL, map2, alpha0, delta0, 999.0, ptr1[ axlon ], + &alphap, &latpole, status ) ){ + +/* If the default value is defined, compare it to the latitude of the + north pole found above. If they are equal use a bad value instead to + prevent an explicit keyword from being added to the FitsChan. */ + if( EQUALANG( ptr1[ axlat ][ 0 ], latpole ) ) { + latpole = AST__BAD; + } else { + latpole = ptr1[ axlat ][ 0 ]; + } + +/* If the default value is not defined, always store an explicit LATPOLE + value. */ + } else { + latpole = ptr1[ axlat ][ 0 ]; + } + +/* The default LONPOLE value is zero if the celestial latitude at the + fiducial point is greater than or equal to the native latitude at the + fiducial point. Otherwise, the default is (+ or -) 180 degrees. If LONPOLE + takes the default value, replace it with AST__BAD to prevent an explicit + keyword being stored in the FitsChan. */ + GetFiducialNSC( map2, &phi0, &theta0, status ); + lonpole = palDranrm( ptr1[ axlon ][ 0 ] ); + if( delta0 >= theta0 ){ + deflonpole = 0.0; + } else { + deflonpole = AST__DPI; + } + if( EQUALANG( lonpole, deflonpole ) ) lonpole = AST__BAD; + } + +/* Convert from radians to degrees. */ + if( lonpole != AST__BAD ) lonpole *= AST__DR2D; + if( latpole != AST__BAD ) latpole *= AST__DR2D; + +/* Free resources. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + } + +/* Store these values. */ + SetItem( &(store->lonpole), 0, 0, s, lonpole, status ); + SetItem( &(store->latpole), 0, 0, s, latpole, status ); + +/* FITS-WCS paper 2 recommends putting a copy of LONPOLE and LATPOLE in + projection parameters 3 and 4 associated with the longitude axis. Only do + this if the projection is not TPN (since this projection uses these + parameters for other purposes). */ + if( astGetWcsType( map2 ) != AST__TPN ) { + SetItem( &(store->pv), fits_ilon, 3, s, lonpole, status ); + SetItem( &(store->pv), fits_ilon, 4, s, latpole, status ); + } +} + +static int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype, + int wcsproj, FitsStore *store, int axlon, int axlat, char s, + int isoff, const char *method, const char *class, int *status ){ +/* +* Name: +* SkySys + +* Purpose: +* Return FITS-WCS values describing a sky coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype, +* int wcsproj, FitsStore *store, int axlon, int axlat, char s, +* int isoff, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function sets values for the following FITS-WCS keywords +* within the supplied FitsStore structure: CTYPE, CNAME, RADESYS, EQUINOX, +* MJDOBS, CUNIT, OBSGEO-X/Y/Z. The values are derived from the supplied +* SkyFrame and WcsMap. + +* Parameters: +* this +* Pointer to the FitsChan. +* skyfrm +* A pointer to the SkyFrame to be described. +* wcstype +* The type of WCS: 0 = TAB, 1 = WcsMap projection. +* wcsproj +* An identifier for the type of WCS projection to use. Should be +* one of the values defined by the WcsMap class. Only used if "wcstype" +* is 1. +* store +* A pointer to the FitsStore structure in which to store the +* results. +* axlon +* The index of the FITS WCS longitude axis (i.e. the value of "i" +* in "CTYPEi"). +* axlat +* The index of the FITS WCS latitude axis (i.e. the value of "i" +* in "CTYPEi"). +* s +* Co-ordinate version character. +* isoff +* If greater than zero, the description to add to the FitsStore +* should describe offset coordinates. If less than zero, the +* description to add to the FitsStore should describe absolute +* coordinates but should include the SkyRefIs, SkyRef and SkyRefP +* attributes. If zero, ignore all offset coordinate info. The +* absolute value indicates the nature of the reference point: +* 1 == "pole", 2 == "origin", otherwise "ignored". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Are the keywords values in the FitsStore usable? +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *label; /* Pointer to axis label string */ + char attr[20]; /* Buffer for AST attribute name */ + char com[80]; /* Buffer for keyword comment */ + char lattype[MXCTYPELEN];/* Latitude axis CTYPE value */ + char lontype[MXCTYPELEN];/* Longitude axis CTYPE value */ + const char *latsym; /* SkyFrame latitude axis symbol */ + const char *lonsym; /* SkyFrame longitude axis symbol */ + const char *prj_name; /* Pointer to projection name string */ + const char *skyref; /* Formatted SkyRef position */ + const char *skyrefis; /* SkyRefIs value */ + const char *sys; /* Celestal coordinate system */ + const char *timesys; /* Timescale specified in FitsChan */ + double ep; /* Epoch of observation in required timescale (MJD) */ + double ep_tdb; /* Epoch of observation in TDB timescale (MJD) */ + double ep_utc; /* Epoch of observation in UTC timescale (MJD) */ + double eq; /* Epoch of reference equinox (MJD) */ + double geolat; /* Geodetic latitude of observer (radians) */ + double geolon; /* Geodetic longitude of observer (radians) */ + double h; /* Geodetic altitude of observer (metres) */ + double skyref_lat; /* SkyRef latitude value (rads) */ + double skyrefp_lat; /* SkyRefP latitude value (rads) */ + double skyref_lon; /* SkyRef longitude value (rads) */ + double skyrefp_lon; /* SkyRefP longitude value (rads) */ + double xyz[3]; /* Geocentric position vector (in m) */ + int defdate; /* Can the date keywords be defaulted? */ + int i; /* Character count */ + int isys; /* Celestial coordinate system */ + int latax; /* Index of latitude axis in SkyFrame */ + int lonax; /* Index of longitude axis in SkyFrame */ + int ok; /* Do axis symbols conform to FITS-WCS CTYPE form? */ + int old_ignore_used; /* Original setting of external ignore_used variable */ + int ret; /* Returned flag */ + +/* Check the status. */ + if( !astOK ) return 0; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Check we have a SkyFrame. */ + if( !IsASkyFrame( skyfrm ) ) return 0; + +/* Initialise */ + ret = 1; + +/* Get the equinox, epoch of observation, and system of the SkyFrame. The epoch + is in TDB. It is assumed the Equinox is in UTC. */ + eq = astGetEquinox( skyfrm ); + sys = astGetC( skyfrm, "system" ); + ep_tdb = astTestEpoch( skyfrm ) ? astGetEpoch( skyfrm ) : AST__BAD; + +/* Convert the epoch to UTC. */ + ep_utc = TDBConv( ep_tdb, AST__UTC, 1, method, class, status ); + +/* See if the FitsChan contains a value for the TIMESYS keyword (include + previously used cards in the search). If so, and if it is not UTC, convert + the epoch to the specified time scale, and store a TIMESYS value in the + FitsStore. */ + old_ignore_used = ignore_used; + ignore_used = 0; + if( GetValue( this, "TIMESYS", AST__STRING, (void *) ×ys, 0, 0, method, + class, status ) && strcmp( timesys, "UTC" ) ) { + ep = TDBConv( ep_tdb, TimeSysToAst( this, timesys, method, class, + status ), + 1, method, class, status ); + SetItemC( &(store->timesys), 0, 0, s, timesys, status ); + +/* If no TIMESYS keyword was found in the FitsChan, or the timesys was + UTC, we use the UTC epoch value found above. In this case no TIMESYS value + need be stored in the FitsSTore since UTC is the default for TIMESYS. */ + } else { + ep = ep_utc; + } + +/* Reinstate the original value for the flag that indicates whether keywords + in the FitsChan that have been used previously should be ignored. */ + ignore_used = old_ignore_used; + +/* The MJD-OBS and DATE-OBS keywords default to the epoch of the + reference equinox if not supplied. Therefore MJD-OBS and DATE-OBS do + not need to be stored in the FitsChan if the epoch of observation is + the same as the epoch of the reference equinox. This can avoid + producing FITS headers which say unlikely things like + DATE-OBS = "01/01/50". Set a flag indicating if MJD-OBS and DATE-OBS + can be defaulted. */ + defdate = astEQUAL( ep_utc, eq ); + +/* Convert the equinox to a Julian or Besselian epoch. Also get the + reference frame and standard system. */ + if( !Ustrcmp( sys, "FK4", status ) ){ + eq = palEpb( eq ); + isys = RADEC; + SetItemC( &(store->radesys), 0, 0, s, "FK4", status ); + } else if( !Ustrcmp( sys, "FK4_NO_E", status ) || !Ustrcmp( sys, "FK4-NO-E", status ) ){ + eq = palEpb( eq ); + isys = RADEC; + SetItemC( &(store->radesys), 0, 0, s, "FK4-NO-E", status ); + } else if( !Ustrcmp( sys, "FK5", status ) ){ + eq = palEpj( eq ); + isys = RADEC; + SetItemC( &(store->radesys), 0, 0, s, "FK5", status ); + } else if( !Ustrcmp( sys, "ICRS", status ) ){ + eq = AST__BAD; + isys = RADEC; + SetItemC( &(store->radesys), 0, 0, s, "ICRS", status ); + } else if( !Ustrcmp( sys, "GAPPT", status ) || + !Ustrcmp( sys, "Apparent", status ) || + !Ustrcmp( sys, "Geocentric", status ) ){ + eq = AST__BAD; + isys = RADEC; + SetItemC( &(store->radesys), 0, 0, s, "GAPPT", status ); + } else if( !Ustrcmp( sys, "Helioecliptic", status ) ){ + eq = AST__BAD; + isys = HECLIP; + } else if( !Ustrcmp( sys, "Galactic", status ) ){ + eq = AST__BAD; + isys = GALAC; + } else if( !Ustrcmp( sys, "Supergalactic", status ) ){ + eq = AST__BAD; + isys = SUPER; + } else if( !Ustrcmp( sys, "AzEl", status ) ){ + eq = AST__BAD; + isys = AZEL; + } else { + eq = AST__BAD; + isys = NOCEL; + } + +/* Store these values. Only store the date if it does not take its + default value. */ + SetItem( &(store->equinox), 0, 0, s, eq, status ); + if( !defdate ) SetItem( &(store->mjdobs), 0, 0, ' ', ep, status ); + +/* Only proceed if we have usable values */ + if( astOK ) { + +/* Get the indices of the latitude and longitude axes within the + SkyFrame. */ + latax = astGetLatAxis( skyfrm ); + lonax = 1 - latax; + +/* The first 4 characters in CTYPE are determined by the celestial coordinate + system and the second 4 by the projection type. If we are describing + offset coordinates, then use "OFLN" and "OFLT. Otherwise use the + standard FITS-WCS name of the system. */ + if( isoff > 0 ){ + strcpy( lontype, "OFLN" ); + strcpy( lattype, "OFLT" ); + } else if( isys == RADEC ){ + strcpy( lontype, "RA--" ); + strcpy( lattype, "DEC-" ); + } else if( isys == ECLIP ){ + strcpy( lontype, "ELON" ); + strcpy( lattype, "ELAT" ); + } else if( isys == HECLIP ){ + strcpy( lontype, "HLON" ); + strcpy( lattype, "HLAT" ); + } else if( isys == GALAC ){ + strcpy( lontype, "GLON" ); + strcpy( lattype, "GLAT" ); + } else if( isys == SUPER ){ + strcpy( lontype, "SLON" ); + strcpy( lattype, "SLAT" ); + } else if( isys == AZEL ){ + strcpy( lontype, "AZ--" ); + strcpy( lattype, "EL--" ); + +/* For unknown systems, use the axis symbols within CTYPE if they conform + to the requirement of FITS-WCS (i.e. "xxLN/xxLT" or "xLON/xLAT") or use + "UNLN/UNLT" otherwise. */ + } else { + latsym = astGetSymbol( skyfrm, latax ); + lonsym = astGetSymbol( skyfrm, lonax ); + if( astOK ) { + + ok = 0; + if( strlen( latsym ) == 4 && strlen( lonsym ) == 4 ) { + if( !strcmp( latsym + 2, "LT" ) && + !strcmp( lonsym + 2, "LN" ) && + !strncmp( latsym, lonsym, 2 ) ) { + ok = 1; + } else if( !strcmp( latsym + 1, "LAT" ) && + !strcmp( lonsym + 1, "LON" ) && + !strncmp( latsym, lonsym, 1 ) ) { + ok = 1; + } + } + + if( !ok ) { + latsym = "UNLT"; + lonsym = "UNLN"; + } + + strncpy( lontype, lonsym, 4 ); + for( i = strlen( lonsym ); i < 4; i++ ) { + lontype[ i ] = '-'; + } + strncpy( lattype, latsym, 4 ); + for( i = strlen( latsym ); i < 4; i++ ) { + lattype[ i ] = '-'; + } + } + } + +/* Store the projection strings. */ + prj_name = ( wcstype == 0 ) ? "-TAB" : astWcsPrjName( wcsproj ); + if( astOK ) { + strcpy( lontype + 4, prj_name ); + strcpy( lattype + 4, prj_name ); + } + +/* Store the total CTYPE strings */ + SetItemC( &(store->ctype), axlon, 0, s, lontype, status ); + SetItemC( &(store->ctype), axlat, 0, s, lattype, status ); + +/* Store offset coord information. */ + if( isoff ) { + +/* If the description is for offset coords store suitable comments for + the CTYPE keywords. */ + if( isoff > 0 ) { + skyref = astGetC( skyfrm, "SkyRef" ); + + sprintf( attr, "Symbol(%d)", axlon + 1 ); + sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref ); + SetItemC( &(store->ctype_com), axlon, 0, s, com, status ); + + sprintf( attr, "Symbol(%d)", axlat + 1 ); + sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref ); + SetItemC( &(store->ctype_com), axlat, 0, s, com, status ); + +/* If the description is for absolute coords store the SkyFrame attribute + values in AST-specific keywords. */ + } else { + sprintf( attr, "SkyRef(%d)", axlon + 1 ); + skyref_lon = astGetD( skyfrm, attr ); + sprintf( attr, "SkyRef(%d)", axlat + 1 ); + skyref_lat = astGetD( skyfrm, attr ); + + sprintf( attr, "SkyRefP(%d)", axlon + 1 ); + skyrefp_lon = astGetD( skyfrm, attr ); + sprintf( attr, "SkyRefP(%d)", axlat + 1 ); + skyrefp_lat = astGetD( skyfrm, attr ); + + skyrefis = (isoff < -2) ? "IGNORED" : + ( (isoff < -1) ? "ORIGIN" : "POLE" ); + + SetItemC( &(store->skyrefis), 0, 0, s, skyrefis, status ); + if( astTest( skyfrm, "SkyRef(1)" ) ) { + SetItem( &(store->skyref), axlon, 0, s, skyref_lon, status ); + SetItem( &(store->skyref), axlat, 0, s, skyref_lat, status ); + } + if( astTest( skyfrm, "SkyRefP(1)" ) ) { + SetItem( &(store->skyrefp), axlon, 0, s, skyrefp_lon, status ); + SetItem( &(store->skyrefp), axlat, 0, s, skyrefp_lat, status ); + } + } + } + +/* If the Label attribute has been set for an axis, use it as the CTYPE + comment and CNAME value. */ + if( astTestLabel( skyfrm, latax ) ) { + label = (char *) astGetLabel( skyfrm, latax ); + SetItemC( &(store->ctype_com), axlat, 0, s, label, status ); + SetItemC( &(store->cname), axlat, 0, s, label, status ); + } + if( astTestLabel( skyfrm, lonax ) ) { + label = (char *) astGetLabel( skyfrm, lonax ); + SetItemC( &(store->ctype_com), axlon, 0, s, label, status ); + SetItemC( &(store->cname), axlon, 0, s, label, status ); + } + +/* Nullify any CUNITS strings for the longitude and latitude axes (they + always take the default value of degrees). */ + SetItemC( &(store->cunit), axlat, 0, s, NULL, status ); + SetItemC( &(store->cunit), axlon, 0, s, NULL, status ); + } + +/* Store the Domain name as the WCSNAME keyword (if set). */ + if( astTestDomain( skyfrm ) ) { + SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( skyfrm ), status ); + } + +/* Store the observer's position if set (needed for definition of AzEl + systems). */ + if( astTestObsLon( skyfrm ) && astTestObsLat( skyfrm ) && s == ' ' ) { + geolon = astGetObsLon( skyfrm ); + geolat = astGetObsLat( skyfrm ); + h = astGetObsAlt( skyfrm ); + if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) { + eraGd2gc( 1, geolon, geolat, h, xyz ); + SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status ); + SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status ); + SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status ); + } + } + if( !astOK ) ret = 0; + return ret; +} + +static char *SourceWrap( const char *(* source)( void ), int *status ) { +/* +* Name: +* SourceWrap + +* Purpose: +* Wrapper function to invoke a C FitsChan source function. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char *SourceWrap( const char *, int *status(* source)( void ) ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function invokes the source function whose pointer is +* supplied in order to read the next input line from an external +* data store. It then returns a pointer to a dynamic string +* containing a copy of the text that was read. + +* Parameters: +* source +* Pointer to a source function, with no parameters, that +* returns a pointer to a const, null-terminated string +* containing the text that it read. This is the form of FitsChan +* source function employed by the C language interface to the +* AST library. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated, null terminated string +* containing a copy of the text that was read. This string must be +* freed by the caller (using astFree) when no longer required. +* +* A NULL pointer will be returned if there is no more input text +* to read. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + char *result; /* Pointer value to return */ + const char *line; /* Pointer to input line */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the source function to read the next input line and return a + pointer to the resulting string. */ + line = ( *source )(); + +/* If a string was obtained, make a dynamic copy of it and save the + resulting pointer. */ + if ( line ) result = astString( line, (int) strlen( line ) ); + +/* Return the result. */ + return result; +} + +static AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs, + double *dim, int *wperm, + char s, FitsStore *store, double *crvals, + int *axis_done, const char *method, + const char *class, int *status ){ + +/* +* Name: +* SpectralAxes + +* Purpose: +* Add values to a FitsStore describing spectral axes in a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs, +* double *dim, int *wperm, +* char s, FitsStore *store, double *crvals, +* int *axis_done, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The current Frame of the supplied FrameSet is searched for spectral +* axes. If any are found, FITS WCS keyword values describing the axis +* are added to the supplied FitsStore, if possible (the conventions +* of FITS-WCS paper III are used). Note, this function does not store +* values for keywords which define the transformation from pixel +* coords to Intermediate World Coords (CRPIX, PC and CDELT), but a +* Mapping is returned which embodies these values. This Mapping is +* from the current Frame in the FrameSet (WCS coords) to a Frame +* representing IWC. The IWC Frame has the same number of axes as the +* WCS Frame which may be greater than the number of base Frame (i.e. +* pixel) axes. +* +* If a spectral axis is found, the RafRA and RefDec attributes of the +* SpecFrame describing the axis are ignored: it is assumed that the +* WCS Frame also contains a pair of celestial axes which will result +* in appropriate celestial reference values being stored in the +* FitsStore (this asumption should be enforced by calling function +* MakeFitsFrameSet prior to calling this function). + +* Parameters: +* this +* Pointer to the FitsChan. +* fs +* Pointer to the FrameSet. The base Frame should represent FITS pixel +* coordinates, and the current Frame should represent FITS WCS +* coordinates. The number of base Frame axes should not exceed the +* number of current Frame axes. The spectral Unit in the returned +* FrameSet will always be linearly related to the default Units for +* the spectral System in use by the axis. If this requires a +* change to the existing spectral Unit, the integrity of the +* FrameSet will be maintained by suitable adjustments to the Mappings +* within the FrameSet. +* dim +* An array holding the image dimensions in pixels. AST__BAD can be +* supplied for any unknwon dimensions. +* wperm +* Pointer to an array of integers with one element for each axis of +* the current Frame. Each element holds the zero-based +* index of the FITS-WCS axis (i.e. one les than the value of "i" in +* the keyword names "CTYPEi", "CRVALi", etc) which describes the +* Frame axis. +* s +* The co-ordinate version character. A space means the primary +* axis descriptions. Otherwise the supplied character should be +* an upper case alphabetical character ('A' to 'Z'). +* store +* The FitsStore in which to store the FITS WCS keyword values. +* crvals +* Pointer to an array holding the default CRVAL value for each +* axis in the WCS Frame. +* axis_done +* An array of flags, one for each Frame axis, which indicate if a +* description of the corresponding axis has yet been stored in the +* FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If a spectral axis was found which can be described using the +* conventions of FITS-WCS paper III, then a Mapping from the current Frame +* of the supplied FrameSet, to the IWC Frame is returned. Otherwise, +* a UnitMap is returned. Note, the Mapping only defines the IWC +* transformation for spectral axes. Any non-spectral axes are passed +* unchanged by the returned Mapping. +*/ + +/* Local Variables: */ + AstFitsTable *table; /* Pointer to structure holding -TAB table info */ + AstFrame *pframe; /* Primary Frame containing current WCS axis*/ + AstFrame *tfrm1; /* A temporary Frame */ + AstFrame *tfrm; /* A temporary Frame */ + AstFrame *wcsfrm; /* WCS Frame within FrameSet */ + AstFrameSet *tfs; /* A temporary FrameSet */ + AstGrismMap *gmap; /* GrismMap defining the spectral axis */ + AstMapping *axmap; /* Mapping from WCS to IWC */ + AstMapping *map; /* Pixel -> WCS mapping */ + AstMapping *ret; /* Returned Mapping */ + AstMapping *tmap0; /* A temporary Mapping */ + AstMapping *tmap1; /* A temporary Mapping */ + AstMapping *tmap2; /* A temporary Mapping */ + AstMapping *tmap3; /* A temporary Mapping */ + AstMapping *tmap4; /* A temporary Mapping */ + AstMapping *tmap5; /* A temporary Mapping */ + AstMapping *tmap6; /* A temporary Mapping */ + AstPermMap *pm; /* PermMap pointer */ + AstSpecFrame *specfrm; /* The SpecFrame defining current WCS axis */ + char *cname; /* Pointer to CNAME value */ + char ctype[ MXCTYPELEN ]; /* The value for the FITS CTYPE keyword */ + char lin_unit[ 20 ]; /* Linear spectral Units being used */ + char orig_system[ 40 ]; /* Value of System attribute for current WCS axis */ + char system_attr[ 10 ]; /* Name of System attribute for current WCS axis */ + char unit_attr[ 10 ]; /* Name of Unit attribute for current WCS axis */ + const char *cval; /* Pointer to temporary character string */ + const char *x_sys[ 4 ]; /* Basic spectral systems */ + double *lbnd_p; /* Pointer to array of lower pixel bounds */ + double *ubnd_p; /* Pointer to array of upper pixel bounds */ + double crval; /* The value for the FITS CRVAL keyword */ + double dgbyds; /* Rate of change of grism parameter wrt "S" at ref. point */ + double dsbydx; /* Rate of change of "S" wrt "X" at ref. point */ + double geolat; /* Geodetic latitude of observer (radians) */ + double geolon; /* Geodetic longitude of observer (radians) */ + double gval; /* Value of grism parameter at reference point */ + double h; /* Geodetic altitude of observer (metres) */ + double imagfreq; /* Image sideband equivalent to the rest frequency (Hz) */ + double lbnd_s; /* Lower bound on spectral axis */ + double pv; /* Value of projection parameter */ + double restfreq; /* Rest frequency (Hz) */ + double ubnd_s; /* Upper bound on spectral axis */ + double vsource; /* Rel.vel. of source (m/s) */ + double xval; /* Value of "X" system at reference point */ + double xyz[3]; /* Geocentric position vector (in m) */ + double zsource; /* Redshift of source */ + int *inperm; /* Pointer to permutation array for input axes */ + int *outperm; /* Pointer to permutation array for output axes */ + int extver; /* Table version number for -TAB headers */ + int fits_i; /* FITS WCS axis index for current WCS axis */ + int iax; /* Axis index */ + int icolindex; /* Index of table column holding index vector */ + int icolmain; /* Index of table column holding main coord array */ + int interp; /* INterpolation method for look-up tables */ + int ix; /* System index */ + int j; /* Loop count */ + int npix; /* Number of pixel axes */ + int nwcs; /* Number of WCS axes */ + int paxis; /* Axis index within primary Frame */ + int sourcevrf; /* Rest Frame in which SourceVel is accesed */ + +/* Initialise */ + ret = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Every supported spectral system is linearly related to one of the + following four systems. */ + x_sys[ 0 ] = "FREQ"; + x_sys[ 1 ] = "WAVE"; + x_sys[ 2 ] = "AWAV"; + x_sys[ 3 ] = "VELO"; + +/* Get a pointer to the WCS Frame. */ + wcsfrm = astGetFrame( fs, AST__CURRENT ); + +/* Store the number of pixel and WCS axes. */ + npix = astGetNin( fs ); + nwcs = astGetNout( fs ); + +/* Store the upper and lower pixel bounds. */ + lbnd_p = astMalloc( sizeof( double )*(size_t) npix ); + ubnd_p = astMalloc( sizeof( double )*(size_t) npix ); + if( astOK ) { + for( iax = 0; iax < npix; iax++ ) { + lbnd_p[ iax ] = 1.0; + ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500; + } + } + +/* Check each axis in the WCS Frame to see if it is a spectral axis. */ + axmap = NULL; + for( iax = 0; iax < nwcs; iax++ ) { + +/* Obtain a pointer to the primary Frame containing the current WCS axis. */ + astPrimaryFrame( wcsfrm, iax, &pframe, &paxis ); + +/* If the current axis belongs to a SpecFrame, we have found a spectral + axis. */ + if( astIsASpecFrame( pframe ) ) { + specfrm = (AstSpecFrame *) pframe; + +/* Note the (zero-based) FITS WCS axis index to be used for the current + Frame axis. */ + fits_i = wperm[ iax ]; + +/* Note the name and original value of the System attribute for the spectral + axis within the FrameSet current Frame. */ + sprintf( system_attr, "System(%d)", iax + 1 ); + cval = astGetC( wcsfrm, system_attr ); + if( cval ) strcpy( orig_system, cval ); + +/* Note the name of the Unit attribute for the spectral axis within the + FrameSet current Frame. */ + sprintf( unit_attr, "Unit(%d)", iax + 1 ); + +/* Get a pointer to the Mapping from FITS pixel coordinates to SpecFrame. */ + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Find the bounds of the Spectral axis over the volume of the pixel grid. */ + astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s, + NULL, NULL ); + +/* The Unit attribute of a SpecFrame can be set to arbitrary non-linear + functions of standard linear spectral units. FITS-WCS paper III requires + CRVAL etc to be given in linear units. So first we ensure that we have a + SpecFrame with linear Units. Create a copy of the SpecFrame and clear + its Unit attribute (this ensures the copy has the default linear units). + Then find a Mapping from the original spectral units to the default + linear units. If the conversion is possible, see if the Mapping + between the units is linear. If it is, then the original Unit attribute + of the SpecFrame is OK (i.e. the units are linear). If not, clear + the Unit attribute of the spectral axis in the FrameSet so that it + uses the default linear units (retaining the original value so that it + can be re-instated later). Using the clear method on the FrameSet + pointer rather than the SpecFrame pointer causes the SpecFrame to be + re-mapped within the FrameSet to maintain its correct relationship with + the other Frames in the FrameSet. Also update the pixel->spectrum + Mapping to take account of the change in units and re-calculate the new + bounds on the spectral axis. Also update any supplied CRVAL value for + the spectral axis. */ + tfrm = astCopy( specfrm ); + astClearUnit( tfrm, 0 ); + tfs = astConvert( specfrm, tfrm, "" ); + tfrm = astAnnul( tfrm ); + if( tfs ) { + crval = crvals ? crvals[ iax ] : AST__BAD; + tmap1 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + tfs = astAnnul( tfs ); + if( !IsMapLinear( tmap1, &lbnd_s, &ubnd_s, 0, status ) ) { + astClear( fs, unit_attr ); + (void) astAnnul( map ); + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s, + NULL, NULL ); + astTran1( tmap1, 1, &crval, 1, &crval ); + } + tmap1 = astAnnul( tmap1 ); + +/* Note the linear spectral Unit currently in use. */ + cval = astGetUnit( specfrm, 0 ); + if( cval ) strcpy( lin_unit, cval ); + +/* For some of the algorithms, the reference value CRVAL is arbitrary. + For these algorithms we choose to use the supplied default CRVAL value. + If no default CRVAL value was suppllied, we use the mid spectral value + if the size of the spectral axis was given, or the lower bound (i.e. + pixel 1) if the size of the spectral axis was not given. */ + if( crval == AST__BAD ) { + if( dim[ iax ] != AST__BAD ) { + crval = 0.5*( lbnd_s + ubnd_s ); + } else { + crval = lbnd_s; + } + } + +/* Modify this crval value so that it correpsonds to an integer pixel + coordinate. */ + crval = NearestPix( map, crval, iax, status ); + +/* We now check to see if the Mapping from pixel coords -> linear spectral + coords corresponds to one of the algorithms supported in FITS-WCS paper + III. First check for the "linear" algorithm in which the linear spectral + coordinate given by the SpecFrame is related linearly to the pixel + coords. */ + ctype[ 0 ] = 0; + if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) { + +/* The CTYPE value is just the spectral system. */ + strcpy( ctype, orig_system ); + +/* Create the Mapping which defines the spectral IWC axis. This is + initially the Mapping from WCS to IWCS - it subtracts the CRVAL value + from the spectral WCS value to get the spectral IWC value (other + non-spectral axes are left unchanged by this Mapping). This results + in the spectral IWC axis having the same axis index as the spectral + WCS axis. */ + crval = -crval; + tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status ); + crval = -crval; + axmap = AddUnitMaps( tmap0, iax, nwcs, status ); + tmap0 = astAnnul( tmap0 ); + } + +/* If the "linear" algorithm above is inappropriate, see if the "non-linear" + algorithm defined in FITS-WCS paper III can be used, in which pixel + coords are linearly related to some spectral system (called "X") other + than the one represented by the supplied SpecFrame (called "S"). */ + if( !ctype[ 0 ] ) { + +/* Loop round each of the 4 allowed X systems. All other spectral systems + are linearly related to one of these 4 systems and so do not need to be + tested. */ + for( ix = 0; ix < 4 && !ctype[ 0 ]; ix++ ) { + +/* Set the system of the spectral WCS axis to the new trial X system. Clear + the Unit attribute to ensure we are using the default linear units. + Using the FrameSet pointer "fs" ensures that the Mappings within the + FrameSet are modified to maintain the correct inter-Frame relationships. */ + astSetC( fs, system_attr, x_sys[ ix ] ); + astClear( fs, unit_attr ); + +/* Now we check to see if the current X system is linearly related to + pixel coordinates. */ + tmap3 = astGetMapping( fs, AST__BASE, AST__CURRENT ); + if( IsMapLinear( tmap3, lbnd_p, ubnd_p, iax, status ) ) { + +/* CTYPE: First 4 characters specify the "S" system. */ + strcpy( ctype, orig_system ); + +/* The non-linear algorithm code to be appended to the "S" system is of the + form "-X2P" ("P" is the system which is linearly related to "S"). */ + if( !strcmp( x_sys[ ix ], "FREQ" ) ) { + strcpy( ctype + 4, "-F2" ); + } else if( !strcmp( x_sys[ ix ], "WAVE" ) ) { + strcpy( ctype + 4, "-W2" ); + } else if( !strcmp( x_sys[ ix ], "AWAV" ) ) { + strcpy( ctype + 4, "-A2" ); + } else { + strcpy( ctype + 4, "-V2" ); + } + if( !strcmp( orig_system, "FREQ" ) || + !strcmp( orig_system, "ENER" ) || + !strcmp( orig_system, "WAVN" ) || + !strcmp( orig_system, "VRAD" ) ) { + strcpy( ctype + 7, "F" ); + } else if( !strcmp( orig_system, "WAVE" ) || + !strcmp( orig_system, "VOPT" ) || + !strcmp( orig_system, "ZOPT" ) ) { + strcpy( ctype + 7, "W" ); + } else if( !strcmp( orig_system, "AWAV" ) ) { + strcpy( ctype + 7, "A" ); + } else { + strcpy( ctype + 7, "V" ); + } + +/* Create a Mapping which gives S as a function of X. */ + tfrm = astCopy( specfrm ); + astSetC( tfrm, "System(1)", orig_system ); + astSetC( tfrm, "Unit(1)", lin_unit ); + tfs = astConvert( specfrm, tfrm, "" ); + tmap5 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + tfs = astAnnul( tfs ); + tfrm = astAnnul( tfrm ); + +/* Use the inverse of this Mapping to get the X value at the reference S + value. */ + astTran1( tmap5, 1, &crval, 0, &xval ); + +/* Also use it to get the rate of change of S with respect to X at the + reference point. */ + dsbydx = astRate( tmap5, &xval, 0, 0 ); + +/* Create the Mapping which defines the spectral IWC axis. This is the + Mapping from WCS to IWC - it first converts from S to X, then subtracts + the X reference value value, and then scales the axis to ensure that + the rate of change of S with respect to IWC is unity (as required by + FITS-WCS paper III). Other non-spectral axes are left unchanged by + the Mapping. The spectral IWC axis has the same axis index as the + spectral WCS axis. */ + xval = -xval; + tmap2 = (AstMapping *) astShiftMap( 1, &xval, "", status ); + astInvert( tmap5 ); + tmap0 = (AstMapping *) astCmpMap( tmap5, tmap2, 1, "", status ); + tmap5 = astAnnul( tmap5 ); + tmap2 = astAnnul( tmap2 ); + tmap2 = (AstMapping *) astZoomMap( 1, dsbydx, "", status ); + tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 1, "", status ); + tmap0 = astAnnul( tmap0 ); + tmap2 = astAnnul( tmap2 ); + axmap = AddUnitMaps( tmap1, iax, nwcs, status ); + tmap1 = astAnnul( tmap1 ); + } + tmap3 = astAnnul( tmap3 ); + +/* Re-instate the original system and unit attributes for the spectral axis. */ + astSetC( fs, system_attr, orig_system ); + astSetC( fs, unit_attr, lin_unit ); + } + } + +/* If the "non-linear" algorithm above is inappropriate, see if the + "log-linear" algorithm defined in FITS-WCS paper III can be used, in + which the spectral axis is logarithmically spaced in the spectral + system given by the SpecFrame. */ + if( !ctype[ 0 ] ) { + +/* If the "log-linear" algorithm is appropriate, the supplied SpecFrame (s) + is related to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this + is the case, then the log of s will be linearly related to pixel + coordinates. Test this. If the test is passed a Mapping is returned from + WCS to IWC. */ + axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p, crval, status ); + +/* If the axis is logarithmic... */ + if( axmap ) { + +/* CTYPE: First 4 characters specify the "S" system. */ + strcpy( ctype, orig_system ); + +/* The rest is "-LOG". */ + strcpy( ctype + 4, "-LOG" ); + } + } + +/* If the "log-linear" algorithm above is inappropriate, see if the "grism" + algorithm defined in FITS-WCS paper III can be used, in which pixel + coords are related to wavelength using a grism dispersion function, + implemented in AST by a GrismMap. GrismMaps produce either vacuum + wavelength or air wavelength as output. Temporarily set the SpecFrame + to these two systems in turn before we do the check for a GrismMap. */ + for( ix = 0; ix < 2 && !ctype[ 0 ]; ix++ ) { + astSetC( fs, system_attr, ( ix == 0 ) ? "WAVE" : "AWAV" ); + astSetC( fs, unit_attr, "m" ); + +/* Get the simplified Mapping from pixel to wavelength. If the Mapping is + a CmpMap containing a GrismMap, and if the output of the GrismMap is + scaled by a neighbouring ZoomMap (e.g. into different wavelength units), + then the GrismMap will be modified to incorporate the effect of the + ZoomMap, and the ZoomMap will be removed. */ + tmap2 = astGetMapping( fs, AST__BASE, AST__CURRENT ); + tmap1 = astSimplify( tmap2 ); + tmap2 = astAnnul( tmap2 ); + +/* Analyse this Mapping to see if the iax'th output is created diretcly by a + GrismMap (i.e. the output of theGrismMap must not subsequently be + modified by some other Mapping). If so, ExtractGrismMap returns a pointer + to the GrismMap as its function value, and also returns "tmap2" as a copy + of tmap1 in which the GrismMap has been replaced by a UnitMap. */ + gmap = ExtractGrismMap( tmap1, iax, &tmap2, status ); + if( gmap ) { + +/* The Mapping without the GrismMap must be linear on the spectral axis. */ + if( IsMapLinear( tmap2, lbnd_p, ubnd_p, iax, status ) ) { + +/* Get the reference wavelength (in "m") stored in the GrismMap. */ + crval = astGetGrismWaveR( gmap ); + +/* Save a copy of the current Wavelength (in "m") SpecFrame. */ + tfrm1 = astCopy( specfrm ); + +/* Re-instate the original System and Unit attributes for the SpecFrame. */ + astSetC( fs, system_attr, orig_system ); + astSetC( fs, unit_attr, lin_unit ); + +/* Find the Mapping from the original "S" system to wavelength (in "m"). */ + tfs = astConvert( specfrm, tfrm1, "" ); + tfrm1 = astAnnul( tfrm1 ); + if( tfs ) { + tmap3 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + tfs = astAnnul( tfs ); + +/* Use the inverse of this Mapping to convert the reference value from + wavelength to the "S" system. */ + astTran1( tmap3, 1, &crval, 0, &crval ); + +/* Concatenate the "S"->wavelength Mapping with the inverse GrismMap (from + wavelength to grism parameter), to get the "S" -> "grism parameter" + Mapping. */ + astInvert( gmap ); + tmap4 = (AstMapping *) astCmpMap( tmap3, gmap, 1, "", status ); + tmap3 = astAnnul( tmap3 ); + +/* Use this Mapping to find the grism parameter at the reference point. */ + astTran1( tmap4, 1, &crval, 1, &gval ); + +/* Also use it to find the rate of change of grism parameter with respect + to "S" at the reference point. */ + dgbyds = astRate( tmap4, &crval, 0, 0 ); + +/* FITS-WCS paper III required ds/dw to be unity at the reference point. + Therefore the rate of change of grism parameter with respect to IWC ("w") + is equal to the rate of change of grism parameter with respect to "S" + (at the reference point). The mapping from "w" to grism parameter is a + ZoomMap which scales "w" by "dgbyds" followed by a ShiftMap which adds + on "gval". */ + tmap5 = (AstMapping *) astZoomMap( 1, dgbyds, "", status ); + tmap6 = (AstMapping *) astShiftMap( 1, &gval, "", status ); + tmap3 = (AstMapping *) astCmpMap( tmap5, tmap6, 1, "", status ); + tmap5 = astAnnul( tmap5 ); + tmap6 = astAnnul( tmap6 ); + +/* Create the Mapping which defines the spectral IWC axis. This is the + Mapping from WCS "S" to IWCS "w", formed by combining the Mapping from + "S" to grism parameter (tmap4), with the Mapping from grism parameter to + "w" (inverse of tmap3). Other non-spectral axes are left unchanged by the + Mapping. The spectral IWC axis has the same axis index as the spectral + WCS axis. */ + astInvert( tmap3 ); + tmap5 = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status ); + tmap3 = astAnnul( tmap3 ); + tmap4 = astAnnul( tmap4 ); + axmap = AddUnitMaps( tmap5, iax, nwcs, status ); + tmap5 = astAnnul( tmap5 ); + +/* CTYPE: First 4 characters specify the "S" system. */ + strcpy( ctype, orig_system ); + +/* Last 4 characters are "-GRA" or "-GRI". */ + strcpy( ctype + 4, ( ix == 0 ) ? "-GRI" : "-GRA" ); + +/* Store values for the projection parameters in the FitsStore. Ignore + parameters which are set to the default values defined in FITS-WCS + paper III. */ + pv = astGetGrismG( gmap ); + if( pv != 0 ) SetItem( &(store->pv), fits_i, 0, s, pv, status ); + pv = (double) astGetGrismM( gmap ); + if( pv != 0 ) SetItem( &(store->pv), fits_i, 1, s, pv, status ); + pv = astGetGrismAlpha( gmap ); + if( pv != 0 ) SetItem( &(store->pv), fits_i, 2, s, pv*AST__DR2D, status ); + pv = astGetGrismNR( gmap ); + if( pv != 1.0 ) SetItem( &(store->pv), fits_i, 3, s, pv, status ); + pv = astGetGrismNRP( gmap ); + if( pv != 0 ) SetItem( &(store->pv), fits_i, 4, s, pv, status ); + pv = astGetGrismEps( gmap ); + if( pv != 0 ) SetItem( &(store->pv), fits_i, 5, s, pv*AST__DR2D, status ); + pv = astGetGrismTheta( gmap ); + if( pv != 0 ) SetItem( &(store->pv), fits_i, 6, s, pv*AST__DR2D, status ); + } + } + +/* Release resources. */ + tmap2 = astAnnul( tmap2 ); + gmap = astAnnul( gmap ); + } + +/* Release resources. */ + tmap1 = astAnnul( tmap1 ); + +/* Re-instate the original System and Unit attributes for the SpecFrame. */ + astSetC( fs, system_attr, orig_system ); + astSetC( fs, unit_attr, lin_unit ); + } + +/* If none of the above algorithms are appropriate, we must resort to + using the -TAB algorithm, in which the Mapping is defined by a look-up + table. Check the TabOK attribute to see -TAB is to be supported. */ + extver = astGetTabOK( this ); + if( !ctype[ 0 ] && extver > 0 ) { + +/* Get any pre-existing FitsTable from the FitsStore. This is the table + in which the tabular data will be stored (if the Mapping can be expressed + in -TAB form). */ + if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL; + +/* See if the Mapping can be expressed in -TAB form. */ + tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i, &table, + &icolmain, &icolindex, &interp, status ); + if( tmap0 ) { + +/* CTYPE: First 4 characters specify the "S" system. Last 4 characters are + "-TAB". */ + strcpy( ctype, orig_system ); + strcpy( ctype + 4, "-TAB" ); + +/* The values stored in the table index vector are GRID coords. So we + need to ensure that IWC are equivalent to GRID coords. So set CRVAL + to zero. First store the original CRVAL value (which gives the + observation centre) in AXREF. */ + SetItem( &(store->axref), fits_i, 0, s, crval, status ); + crval = 0.0; + +/* Store TAB-specific values in the FitsStore. First the name of the + FITS binary table extension holding the coordinate info. */ + SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status ); + +/* Next the table version number. This is the set (positive) value for the + TabOK attribute. */ + SetItem( &(store->pv), fits_i, 1, s, extver, status ); + +/* Also store the table version in the binary table header. */ + astSetFitsI( table->header, "EXTVER", extver, + "Table version number", 0 ); + +/* Next the name of the table column containing the main coords array. */ + SetItemC( &(store->ps), fits_i, 1, s, + astColumnName( table, icolmain ), status ); + +/* Next the name of the column containing the index array */ + if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s, + astColumnName( table, icolindex ), status ); + +/* The interpolation method (an AST extension to the published -TAB + algorithm, communicated through the QVi_4a keyword). */ + SetItem( &(store->pv), fits_i, 4, s, interp, status ); + +/* Also store the FitsTable itself in the FitsStore. */ + astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL ); + +/* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for + the -TAB algorithm). First, get a Mapping that combines the TAB axis + Mapping( tmap0) in parallel with one or two UnitMaps in order to put + the TAB axis at the required index. */ + tmap1 = AddUnitMaps( tmap0, iax, nwcs, status ); + +/* Now get a PermMap that permutes the WCS axes into the FITS axis order. */ + inperm = astMalloc( sizeof( double )*nwcs ); + outperm = astMalloc( sizeof( double )*nwcs ); + if( astOK ) { + for( j = 0; j < nwcs; j++ ) { + inperm[ j ] = wperm[ j ]; + outperm[ wperm[ j ] ] = j; + } + } + pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "", + status ); + +/* Combine these two Mappings in series, to get the Mapping from WCS to + IWC. */ + axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ", + status ); + +/* Free resources. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + pm = astAnnul( pm ); + tmap0 = astAnnul( tmap0 ); + tmap1 = astAnnul( tmap1 ); + } + if( table ) table = astAnnul( table ); + } + +/* If this axis is a usable spectral axis... */ + if( ctype[ 0 ] ) { + +/* Add the Mapping for this axis in series with any existing result Mapping. */ + if( ret ) { + tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status ); + (void) astAnnul( ret ); + ret = tmap0; + } else { + ret = astClone( axmap ); + } + axmap = astAnnul( axmap ); + +/* Store values for CTYPE, CRVAL and CUNIT in the FitsStore. */ + SetItemC( &(store->ctype), fits_i, 0, s, ctype, status ); + SetItem( &(store->crval), fits_i, 0, s, crval, status ); + SetItemC( &(store->cunit), fits_i, 0, s, lin_unit, status ); + +/* If the axis label has been set, use it as the CTYPE comment and CNAME + value. */ + if( astTestLabel( specfrm, 0 ) ) { + cname = (char *) astGetLabel( specfrm, 0 ); + SetItemC( &(store->ctype_com), fits_i, 0, s, cname, status ); + SetItemC( &(store->cname), fits_i, 0, s, cname, status ); + } + +/* Store values for the other FITS-WCS keywords which describe the + spectral system. Only store values which have been explicitly set in + the SpecFrame, which are different to the default values defined by + FITS-WCS paper III (if any), and which are not bad. */ + if( astTestObsLon( specfrm ) && astTestObsLat( specfrm ) && + s == ' ' ) { + geolon = astGetObsLon( specfrm ); + geolat = astGetObsLat( specfrm ); + h = astGetObsAlt( specfrm ); + if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) { + eraGd2gc( 1, geolon, geolat, h, xyz ); + SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status ); + SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status ); + SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status ); + } + } + if( astTestRestFreq( specfrm ) ) { + restfreq = astGetRestFreq( specfrm ); + if( restfreq != AST__BAD ) { + if( !strcmp( orig_system, "WAVE" ) || + !strcmp( orig_system, "VOPT" ) || + !strcmp( orig_system, "ZOPT" ) || + !strcmp( orig_system, "AWAV" ) ) { + SetItem( &(store->restwav), 0, 0, s, AST__C/restfreq, status ); + } else { + SetItem( &(store->restfrq), 0, 0, s, restfreq, status ); + } + } + if( astIsADSBSpecFrame( specfrm ) ) { + imagfreq = astGetImagFreq( (AstDSBSpecFrame *) specfrm ); + if( imagfreq != AST__BAD ) { + SetItem( &(store->imagfreq), 0, 0, s, imagfreq, status ); + } + } + } + cval = GetFitsSor( astGetC( specfrm, "StdOfRest" ), status ); + if( cval ) SetItemC( &(store->specsys), 0, 0, s, cval, status ); + if( astTestSourceVel( specfrm ) ) { + vsource = astGetSourceVel( specfrm ); + if( vsource != AST__BAD && fabs( vsource ) < AST__C ) { + zsource = sqrt( (AST__C + vsource)/ + (AST__C - vsource) ) - 1.0; + SetItem( &(store->zsource), 0, 0, s, zsource, status ); + cval = GetFitsSor( astGetC( specfrm, "SourceVRF" ), status ); + if( cval ) SetItemC( &(store->ssyssrc), 0, 0, s, cval, status ); + } + } else { + vsource = AST__BAD; + } + +/* Store the VELOSYS value (not strictly needed since it can be + determined from the other values, but FITS-WCS paper III says it can be + useful). We temporarily change the source velocity to be zero m/s + in the main rest frame (StdOfRest) (unless the main rest frame is + already the source rest frame). We then change the source rest + frame to topocentric and get the source velocity (i.e. the velocity of + the main rest Frame) in the topocentric system. We then re-instate the + original attribute values if they were set. */ + if( astGetStdOfRest( specfrm ) != AST__SCSOR ) { + sourcevrf = astGetSourceVRF( specfrm ); + astSetSourceVRF( specfrm, astGetStdOfRest( specfrm ) ); + astSetSourceVel( specfrm, 0.0 ); + } else { + vsource = AST__BAD; + sourcevrf = AST__NOSOR; + } + astSetSourceVRF( specfrm, AST__TPSOR ); + SetItem( &(store->velosys), 0, 0, s, + astGetSourceVel( specfrm ), status ); + if( vsource != AST__BAD ){ + astSetSourceVRF( specfrm, sourcevrf ); + astSetSourceVel( specfrm, vsource ); + } + +/* Indicate that this axis has been described. */ + axis_done[ iax ] = 1; + } + +/* Release resources. */ + map = astAnnul( map ); + } + } + pframe = astAnnul( pframe ); + } + +/* Release resources. */ + lbnd_p = astFree( lbnd_p ); + ubnd_p = astFree( ubnd_p ); + wcsfrm = astAnnul( wcsfrm ); + +/* If we have a Mapping to return, simplify it. Otherwise, create + a UnitMap to return. */ + if( ret ) { + tmap0 = ret; + ret = astSimplify( tmap0 ); + tmap0 = astAnnul( tmap0 ); + } else { + ret = (AstMapping *) astUnitMap( nwcs, "", status ); + } + +/* Return the result. */ + return ret; +} + +static AstFitsChan *SpecTrans( AstFitsChan *this, int encoding, + const char *method, const char *class, int *status ){ +/* +* Name: +* SpecTrans + +* Purpose: +* Translated non-standard WCS FITS headers into equivalent standard +* ones. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstFitsChan *SpecTrans( AstFitsChan *this, int encoding, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function checks the supplied FitsChan for selected +* non-standard WCS keywords and, if found, stores equivalent +* standard keywords in a newly created FitsChan which is returned as +* the function value. All the original keywords are marked +* as having been used, so that they are not written out when the +* FitsChan is deleted. +* + +* At the moment, the non-standard keywords checked for are: +* +* 1) RADECSYS is renamed as RADESYS +* +* 2) LONGPOLE is renamed as LONPOLE +* +* 3) CDjjjiii and CDj_i are converted to PCi_j (with unit CDELT) +* +* 4) CROTAj are converted to PCi_j +* +* 5) PROJPi are converted to PV_i +* +* 6) CmVALi are converted to CRVALis (s=A,B,,, for m=1,2...). This +* is also done for CmPIXi, CmYPEi, and CmNITi. CmELTi is converted +* to a CDj_is array. +* +* 7) EQUINOX keywords with string values equal to a date preceded +* by the letter B or J (eg "B1995.0"). These are converted to the +* corresponding Julian floating point value without any epoch +* specifier. +* +* 8) EPOCH values are converted into Julian EQUINOX values (but only +* if the FitsChan does not already contain an EQUINOX value). +* +* 9) DATE-OBS values are converted into MJD-OBS values (but only +* if the FitsChan does not already contain an MJD-OBS value). +* +* 10) EQUINOX or EPOCH keywords with value zero are converted to +* B1950. +* +* 11) The AIPS NCP and GLS projections are converted into equivalent SIN +* or SFL projections. +* +* 12) The IRAF "ZPX" projection. If the last 4 chacaters of CTYPEi + +* (i = 1, naxis) are "-ZPX", then: +* - "ZPX" is replaced by "ZPN" within the CTYPEi value +* - A distortion code of "-ZPX" is appended to the end of the CTYPEi +* value (this is used later by the DistortMaps function). +* - If the FitsChan contains no PROJP keywords, then projection +* parameter valus are read from any WATi_nnn keywords, and +* corresponding PV keywords are added to the FitsChan. +* +* 13) The IRAF "TNX" projection. If the last 4 chacaters of CTYPEi + +* (i = 1, naxis) are "-TNX", then: +* - "TNX" is replaced by "TAN" within the CTYPEi value (the distorted +* TAN projection included in a pre-final version of FITS-WCS is still +* supported by AST using the WcsMap AST__TPN projection). +* - If the FitsChan contains no PROJP keywords, then projection +* parameter valus are read from any WATi_nnn keywords, and +* corresponding PV keywords are added to the FitsChan. +* - If the TNX projection cannot be converted exactly into a TAN +* projection, ASTWARN keywords are added to the FitsChan +* containing a warning message. The calling application can (if it +* wants to) check for this keyword, and report its contents to the +* user. +* +* 14) Keywords relating to the IRAF "mini-WCS" system are removed. +* This is the IRAF equivalent of the AST native encoding. Mini-WCS +* keywords are removed in order to avoid confusion arising between +* potentially inconsistent encodings. +* +* 15) "QV" parameters for TAN projections (as produced by AUTOASTROM) +* or "-TAB" (as produced by FitsChan) are renamed to "PV". +* +* 16) RESTFREQ is converted to RESTFRQ. +* +* 17) the "-WAV", "-FRQ" and "-VEL" CTYPE algorithms included in an +* early draft of FITS-WCS paper III are translated to the +* corresponding modern "-X2P" form. +* +* 18) AIPS spectral CTYPE values are translated to FITS-WCS paper III +* equivalents. +* +* 19) AIPS spectral keywords OBSRA and OBSDEC are used to create a +* pair of celestial axes with reference point at the specified +* (OBSRA,OBSDEC) position. This is only done if the header does not +* already contain a pair of celestial axes. +* +* 20) Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s", +* "M/S" +* +* 21) Various translations specific to the FITS-CLASS encoding. +* +* 22) SAO distorted TAN projections (uses COi_j keywords to store +* polynomial coefficients) are converted to TPN projections. + +* 23) CTYPE == "LAMBDA" changed to CTYPE = "WAVE" +* +* 24) if the projection is TAN and the PolyTan attribute is non-zero, +* or if the projection is TPV (produced by SCAMP), the projection is +* changed to TPN (the AST code for the draft FITS-WCS paper II +* conventions for a distorted TAN projection). + +* Parameters: +* this +* Pointer to the FitsChan. +* encoding +* The FitsChan encoding in use. +* method +* Pointer to string holding name of calling method. +* class +* Pointer to a string holding the name of the supplied object class. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new FitsChan containing the keywords which +* constitute the standard equivalents to any non-standard keywords in +* the supplied FitsChan. A NULL pointer is returned if there are no +* non-standard keywords in the supplied FitsChan. +*/ + +/* Local Variables: */ + AstFitsChan *ret; /* The returned FitsChan */ + char *assys; /* AIPS standad of rest type */ + char *astype; /* AIPS spectral type */ + char *comm; /* Pointer to comment string */ + char *cval; /* Pointer to character string */ + char *start; /* Pointer to start of projp term */ + char *watmem; /* Pointer to total WAT string */ + char bj; /* Besselian/Julian indicator */ + char format[ 50 ]; /* scanf format string */ + char keyname[ FITSNAMLEN + 5 ];/* General keyword name + formats */ + char lattype[MXCTYPELEN]; /* CTYPE value for latitude axis */ + char lontype[MXCTYPELEN]; /* CTYPE value for longitude axis */ + char prj[6]; /* Spatial projection string */ + char s; /* Co-ordinate version character */ + char spectype[MXCTYPELEN]; /* CTYPE value for spectral axis */ + char sprj[6]; /* Spectral projection string */ + char ss; /* Co-ordinate version character */ + char template[ FITSNAMLEN + 1 ];/* General keyword name template */ + double *cvals; /* PVi_m values for TPN projection */ + double cdelti; /* CDELT for longitude axis */ + double cdeltj; /* CDELT for latitude axis */ + double cosrota; /* Cos( CROTA ) */ + double crota; /* CROTA Value */ + double dval; /* General floating value */ + double lambda; /* Ratio of CDELTs */ + double projp; /* Projection parameter value */ + double rowsum2; /* Sum of squared CDi_j row elements */ + double sinrota; /* Sin( CROTA ) */ + double sinval; /* Sin( dec ref ) */ + int *mvals; /* "m" index of each PVi_m value */ + int axlat; /* Index of latitude axis */ + int axlon; /* Index of longitude axis */ + int diag; /* Sign of diagonal CDi_j element */ + int dim; /* Length of pixel axis */ + int gotpcij; /* Does FitsChan contain any PCi_j keywords? */ + int i,j; /* Indices */ + int iaxis; /* Axis index */ + int icoeff; /* Index of next PVi_m value */ + int iproj; /* Projection parameter index */ + int jhi; /* Highest axis index */ + int jlo; /* Lowest axis index */ + int lbnd[ 2 ]; /* Lower index bounds */ + int m; /* Co-ordinate version index */ + int naxis; /* Number of axes */ + int nc; /* Length of string */ + int ncoeff; /* Number of PVi_m values */ + int ok; /* Can projection be represented in FITS-WCS?*/ + int shifted; /* Non-zero if there is an origin shift */ + int tlbnd[ 2 ]; /* Lower index bounds */ + int tubnd[ 2 ]; /* Upper index bounds */ + int ubnd[ 2 ]; /* Upper index bounds */ + int use_projp; /* Use PROJP keywors in favour of PV keywords? */ + size_t size; /* Length of string value */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise to avoid compiler warnings. */ + size = 0; + prj[ 0 ] = 0; + +/* Create the returned FitsChan. */ + ret = astFitsChan( NULL, NULL, "", status ); + +/* Loop round all axis descriptions, starting with primary (' '). */ + for( s = 'A' - 1; s <= 'Z' && astOK; s++ ){ + if( s == 'A' - 1 ) s = ' '; + +/* Find the number of axes by finding the highest axis number in any + CRPIXi keyword name. Pass on if there are no axes for this axis + description. */ + if( s != ' ' ) { + sprintf( template, "CRPIX%%d%c", s ); + } else { + strcpy( template, "CRPIX%d" ); + } + if( !astKeyFields( this, template, 1, &naxis, lbnd ) ) { + if( s == ' ' ) s = 'A' - 1; + continue; + } + +/* Find the longitude and latitude axes by examining the CTYPE values. + They are marked as read. Such markings are only provisional, and they + can be read again any number of times until the current astRead + operation is completed. Also note the projection type. */ + j = 0; + axlon = -1; + axlat = -1; + while( j < naxis && astOK ){ + if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ), + AST__STRING, (void *) &cval, 0, method, + class, status ) ){ + nc = strlen( cval ); + if( !strncmp( cval, "RA--", 4 ) || + !strncmp( cval, "AZ--", 4 ) || + ( nc > 1 && !strncmp( cval + 1, "LON", 3 ) ) || + ( nc > 2 && !strncmp( cval + 2, "LN", 2 ) ) ) { + axlon = j; + strncpy( prj, cval + 4, 4 ); + strncpy( lontype, cval, 10 ); + prj[ 4 ] = 0; + } else if( !strncmp( cval, "DEC-", 4 ) || + !strncmp( cval, "EL--", 4 ) || + ( nc > 1 && !strncmp( cval + 1, "LAT", 3 ) ) || + ( nc > 2 && !strncmp( cval + 2, "LT", 2 ) ) ) { + axlat = j; + strncpy( prj, cval + 4, 4 ); + strncpy( lattype, cval, 10 ); + prj[ 4 ] = 0; + +/* Check for spectral algorithms from early drafts of paper III */ + } else { + sprj[ 0 ] = '-'; + if( ( nc > 4 && !strncmp( cval + 4, "-WAV", 4 ) ) ) { + sprj[ 1 ] = 'W'; + } else if( ( nc > 4 && !strncmp( cval + 4, "-FRQ", 4 ) ) ) { + sprj[ 1 ] = 'F'; + } else if( ( nc > 4 && !strncmp( cval + 4, "-VEL", 4 ) ) ) { + sprj[ 1 ] = 'V'; + } else { + sprj[ 0 ] = 0; + } + if( *sprj ) { + sprj[ 2 ] = '2'; + if( !strncmp( cval, "WAVE", 4 ) ) { + sprj[ 3 ] = 'W'; + } else if( !strncmp( cval, "FREQ", 4 ) ) { + sprj[ 3 ] = 'F'; + } else if( !strncmp( cval, "VELO", 4 ) ) { + sprj[ 3 ] = 'V'; + } else if( !strncmp( cval, "VRAD", 4 ) ) { + sprj[ 3 ] = 'F'; + } else if( !strncmp( cval, "VOPT", 4 ) ) { + sprj[ 3 ] = 'W'; + } else if( !strncmp( cval, "ZOPT", 4 ) ) { + sprj[ 3 ] = 'W'; + } else if( !strncmp( cval, "ENER", 4 ) ) { + sprj[ 3 ] = 'F'; + } else if( !strncmp( cval, "WAVN", 4 ) ) { + sprj[ 3 ] = 'F'; + } else if( !strncmp( cval, "BETA", 4 ) ) { + sprj[ 3 ] = 'V'; + } else { + sprj[ 0 ] = 0; + } + } + if( *sprj ) { + strcpy( spectype, cval ); + if( sprj[ 1 ] == sprj[ 3 ] ) { + strcpy( sprj, strlen( cval ) > 8 ? "----" : " " ); + } else { + sprj[ 4 ] = 0; + } + strncpy( spectype + 4, sprj, 4 ); + cval = spectype; + SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + } + } + j++; + } else { + break; + } + } + +/* RADECSYS keywords + ----------------- */ + if( s == ' ' ) { + if( GetValue2( ret, this, "RADECSYS", AST__STRING, (void *) &cval, 0, method, + class, status ) ){ + if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){ + SetValue( ret, "RADESYS", (void *) &cval, AST__STRING, + CardComm( this, status ), status ); + } + } + +/* LONGPOLE keywords + ----------------- */ + if( GetValue2( ret, this, "LONGPOLE", AST__FLOAT, (void *) &dval, 0, method, + class, status ) ){ + if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){ + SetValue( ret, "LONPOLE", (void *) &dval, AST__FLOAT, + CardComm( this, status ), status ); + } + } + } + +/* Zero CDELT values. + ----------------- */ + +/* Check there are some CDELT keywords... */ + if( s != ' ' ) { + sprintf( template, "CDELT%%d%c", s ); + } else { + strcpy( template, "CDELT%d" ); + } + if( astKeyFields( this, template, 0, NULL, NULL ) ){ + +/* Do each row in the matrix. */ + for( j = 0; j < naxis; j++ ){ + +/* Get the CDELT value for this row. */ + GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT, + (void *) &cdeltj, 0, method, class, status ); + +/* If CDELT is zero, use 1.0E-6 of the corresponding CRVAL value + instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the + matrix to be non-invertable. The Mapping could then not be simplified + or used by a Plot. CDELT values of zero are usually used to indicate + "redundant" axes. For instance, a 2D image may be stored as a 3D cube + with a single plane with the "redundant" 3rd axis used to specify the + wavelength of the filter. The actual value used for CDELT shouldn't + matter since the axis only spans a single pixel anyway. */ + if( cdeltj == 0.0 ){ + GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT, + (void *) &dval, 1, method, class, status ); + cdeltj = 1.0E-6*dval; + if( cdeltj == 0.0 ) cdeltj = 1.0; + SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), (void *) &cdeltj, + AST__FLOAT, NULL, status ); + } + } + } + +/* Following conversions produce PCi_j keywords. Only do them if there + are currently no PCi_j keywords in the header. */ + if( s != ' ' ) { + sprintf( template, "PC%%d_%%d%c", s ); + } else { + strcpy( template, "PC%d_%d" ); + } + gotpcij = astKeyFields( this, template, 0, NULL, NULL ); + if( !gotpcij ){ + +/* CDjjjiii + -------- */ + if( s == ' ' && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){ + +/* Do each row in the matrix. */ + for( j = 0; j < naxis; j++ ){ + +/* Do each column in the matrix. */ + for( i = 0; i < naxis; i++ ){ + +/* Get the CDjjjiii matrix element */ + sprintf( keyname, "CD%.3d%.3d", j + 1, i + 1 ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) ){ + +/* If found, save it with name PCj_i, and ensure the default value of 1.0 + is used for CDELT. */ + if( encoding == FITSIRAF_ENCODING ){ + SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ), + (void *) &dval, AST__FLOAT, NULL, status ); + dval = 1.0; + SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + gotpcij = 1; + } + } + } + } + } + +/* CDj_i + ---- */ + if( s != ' ' ) { + sprintf( template, "CD%%d_%%d%c", s ); + } else { + strcpy( template, "CD%d_%d" ); + } + if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){ + +/* Do each row in the matrix. */ + for( j = 0; j < naxis; j++ ){ + +/* First find the sum of the squared elements in the row. and note the + sign of the diagonal element. */ + rowsum2 = 0.0; + diag = +1; + for( i = 0; i < naxis; i++ ){ + if( GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ), + AST__FLOAT, (void *) &dval, 0, method, class, status ) ){ + rowsum2 += dval*dval; + if( i == j ) diag = ( dval >= 0.0 ) ? +1 : -1; + } + } + +/* The CDELT value for this row will be the length of the row vector. This means that + each row will be a unit vector when converted to PCi_j form, and the CDELT will + give a real indication of the pixel size. Ensure that the diagonal + PCi+j element has a positive sign. */ + cdelti = sqrt( rowsum2 )*diag; + SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), + (void *) &cdelti, AST__FLOAT, NULL, status ); + +/* Do each column in the matrix. */ + for( i = 0; i < naxis; i++ ){ + +/* Get the CDj_i matrix element (note default value for all CD elements + is zero (even diagonal elements!). */ + if( !GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ), + AST__FLOAT, (void *) &dval, 0, method, class, status ) ){ + dval = 0.0; + } + +/* Divide by the rows cdelt value and save it with name PCj_i. */ + if( cdelti != 0.0 ) dval /= cdelti; + SetValue( ret, FormatKey( "PC", j + 1, i + 1, s, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + gotpcij = 1; + } + } + } + +/* PCjjjiii and CROTAi keywords + ---------------------------- */ + +/* Check there are some CDELT keywords... */ + if( s != ' ' ) { + sprintf( template, "CDELT%%d%c", s ); + } else { + strcpy( template, "CDELT%d" ); + } + if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){ + +/* See if there is a CROTA keyword. Try to read values for both axes + since they are sometimes both included. This ensures they will not be + included in the output when the FitsChan is deleted. Read the latitude + axis second in order to give it priority in cases where both are + present. */ + crota = AST__BAD; + GetValue2( ret, this, FormatKey( "CROTA", axlon + 1, -1, s, status ), + AST__FLOAT, (void *) &crota, 0, method, class, status ); + GetValue2( ret, this, FormatKey( "CROTA", axlat + 1, -1, s, status ), + AST__FLOAT, (void *) &crota, 0, method, class, status ); + +/* If there are any PCjjjiii keywords, rename them as PCj_i. */ + if( s == ' ' && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){ + +/* Do each row in the matrix. */ + for( j = 0; j < naxis; j++ ){ + +/* Do each column in the matrix. */ + for( i = 0; i < naxis; i++ ){ + +/* Get the PCiiijjj matrix element */ + sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) ){ + } else if( i == j ) { + dval = 1.0; + } else { + dval = 0.0; + } + +/* Store it as PCi_j */ + SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ), + (void *) &dval, AST__FLOAT, NULL, status ); + gotpcij = 1; + } + } + +/* If there is a CROTA value and no PCjjjii keywords, create a PCj_i + matrix from the CROTA values. We need to have latitude and longitude + axes for this. */ + } else if( s == ' ' && axlat != -1 && axlon != -1 && crota != AST__BAD ){ + +/* Get the sin and cos of CROTA */ + cosrota = cos( crota*AST__DD2R ); + sinrota = sin( crota*AST__DD2R ); + +/* Get the CDELT values for the longitude and latitude axes. */ + if( GetValue2( ret, this, FormatKey( "CDELT", axlat + 1, -1, ' ', status ), + AST__FLOAT, (void *) &cdeltj, 1, method, + class, status ) && + GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, ' ', status ), + AST__FLOAT, (void *) &cdelti, 1, method, + class, status ) ){ + +/* Save the ratio, needed below. */ + lambda = cdeltj/cdelti; + +/* Save a corresponding set of PCi_j keywords in the FitsChan. First do + the diagonal terms. */ + for( i = 0; i < naxis; i++ ){ + if( i == axlat ) { + dval = cosrota; + } else if( i == axlon ) { + dval = cosrota; + } else { + dval = 1.0; + } + SetValue( ret, FormatKey( "PC", i + 1, i + 1, ' ', status ), + (void *) &dval, AST__FLOAT, NULL, status ); + gotpcij = 1; + } + +/* Now do the non-zero off-diagonal terms. */ + dval = sinrota/lambda; + SetValue( ret, FormatKey( "PC", axlat + 1, axlon + 1, ' ', status ), + (void *) &dval, AST__FLOAT, NULL, status ); + dval = -sinrota*lambda; + SetValue( ret, FormatKey( "PC", axlon + 1, axlat + 1, ' ', status ), + (void *) &dval, AST__FLOAT, NULL, status ); + } + } + } + } + +/* Conversion of old PROJP, etc, is done once on the "primary" pass. */ + if( s == ' ' ) { + +/* PROJP keywords + -------------- */ + if( astKeyFields( this, "PROJP%d", 1, ubnd, lbnd ) && axlat != -1 ) { + +/* Some people produce headers with both PROJP and PV. Even worse, the + PROJP and PV values are sometimes inconsistent. In this case we trust + the PV values rather than the PROJP values, but only if the PV values + are not obviously incorrect for some reason. In particularly, we check + that, *if* either PVi_1 or PVi_2 (where i=longitude axis) is present, + then PVi_0 is also present. Conversely we check that if PVi_0 is + present then at least one of PVi_1 or PVi_2 is present. */ + use_projp = 1; + if( axlat != -1 && + astKeyFields( this, "PV%d_%d", 2, tubnd, tlbnd ) ){ + use_projp = 0; + +/* Are there any PV values for the longitude axis? */ + if( tlbnd[ 0 ] <= axlon + 1 && axlon + 1 <= tubnd[ 0 ] ) { + +/* Are either PVi_1 or PVi_2 available? */ + if( HasCard( this, FormatKey( "PV", axlon + 1, 1, ' ', status ), + method, class, status ) || + HasCard( this, FormatKey( "PV", axlon + 1, 2, ' ', status ), + method, class, status ) ) { + +/* If so use PROJP if PVi_0 is not also available. */ + if( !HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ), + method, class, status ) ) use_projp = 1; + +/* If neither PVi_1 or PVi_2 are available, use PROJP if PVi_0 is + available. */ + } else if( HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ), + method, class, status ) ) { + use_projp = 1; + } + } + } + +/* Translate PROJP to PV if required. */ + if( use_projp ) { + for( i = lbnd[ 0 ]; i <= ubnd[ 0 ]; i++ ){ + if( GetValue2( ret, this, FormatKey( "PROJP", i, -1, ' ', status ), + AST__FLOAT, (void *) &dval, 0, method, class, status ) && + ( encoding == FITSPC_ENCODING || + encoding == FITSIRAF_ENCODING ) ){ + SetValue( ret, FormatKey( "PV", axlat + 1, i, ' ', status ), + (void *) &dval, AST__FLOAT, CardComm( this, status ), status ); + } + } + } + } + +/* CmVALi keywords + --------------- */ + if( astKeyFields( this, "C%1dVAL%d", 2, ubnd, lbnd ) ){ + ss = 'A'; + for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ + for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ + sprintf( keyname, "C%dVAL%d", m, i ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) && + ( encoding == FITSPC_ENCODING || + encoding == FITSIRAF_ENCODING ) ){ + sprintf( keyname, "CRVAL%d%c", i, ss ); + SetValue( ret, keyname, (void *) &dval, AST__FLOAT, + CardComm( this, status ), status ); + } + } + ss++; + } + } + +/* CmPIXi keywords + --------------- */ + if( astKeyFields( this, "C%1dPIX%d", 2, ubnd, lbnd ) ){ + ss = 'A'; + for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ + for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ + sprintf( keyname, "C%dPIX%d", m, i ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) && + ( encoding == FITSPC_ENCODING || + encoding == FITSIRAF_ENCODING ) ){ + sprintf( keyname, "CRPIX%d%c", i, ss ); + SetValue( ret, keyname, (void *) &dval, AST__FLOAT, + CardComm( this, status ), status ); + } + } + ss++; + } + } + +/* CmYPEi keywords + --------------- */ + if( astKeyFields( this, "C%1dYPE%d", 2, ubnd, lbnd ) ){ + ss = 'A'; + for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ + for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ + sprintf( keyname, "C%dYPE%d", m, i ); + if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, + method, class, status ) && + ( encoding == FITSPC_ENCODING || + encoding == FITSIRAF_ENCODING ) ){ + sprintf( keyname, "CTYPE%d%c", i, ss ); + SetValue( ret, keyname, (void *) &cval, AST__STRING, + CardComm( this, status ), status ); + } + } + ss++; + } + } + +/* CmNITi keywords + --------------- */ + if( astKeyFields( this, "C%1dNIT%d", 2, ubnd, lbnd ) ){ + ss = 'A'; + for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ + for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){ + sprintf( keyname, "C%dNIT%d", m, i ); + if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, + method, class, status ) && + ( encoding == FITSPC_ENCODING || + encoding == FITSIRAF_ENCODING ) ){ + sprintf( keyname, "CUNIT%d%c", i, ss ); + SetValue( ret, keyname, (void *) &cval, AST__STRING, + CardComm( this, status ), status ); + } + } + ss++; + } + } + +/* CmELTi keywords + --------------- */ + if( astKeyFields( this, "C%1dELT%d", 2, ubnd, lbnd ) ){ + ss = 'A'; + for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){ + +/* Create a PCj_is matrix by copying the PCjjjiii values and rename CmELTi as + CDELTis. */ + +/* Do each row in the matrix. */ + for( j = 0; j < naxis; j++ ){ + +/* Get the CDELT value for this row. Report an error if not present. */ + sprintf( keyname, "C%dELT%d", m, j + 1 ); + GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cdeltj, 1, + method, class, status ); + +/* If CDELT is zero, use one hundredth of the corresponding CRVAL value + instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the + matrix to be non-invertable. The Mapping could then not be simplified + or used by a Plot. CDELT values of zero are usually used to indicate + "redundant" axes. For instance, a 2D image may be stored as a 3D cube + with a single plane with the "redundant" 3rd axis used to specify the + wavelength of the filter. The actual value used for CDELT shouldn't + matter since the axis only spans a single pixel anyway. */ + if( cdeltj == 0.0 ){ + GetValue2( ret, this, FormatKey( "CRVAL", j + 1, -1, ss, status ), AST__FLOAT, + (void *) &dval, 1, method, class, status ); + cdeltj = 0.01*dval; + if( cdeltj == 0.0 ) cdeltj = 1.0; + } + +/* Save it as CDELTis */ + sprintf( keyname, "CDELT%d%c", j + 1, ss ); + SetValue( ret, keyname, (void *) &cdeltj, AST__FLOAT, + CardComm( this, status ), status ); + +/* Do each column in the matrix. */ + for( i = 0; i < naxis; i++ ){ + +/* Get the PCiiijjj matrix element */ + sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) ){ + } else if( i == j ) { + dval = 1.0; + } else { + dval = 0.0; + } + +/* Store it as PCi_js. */ + SetValue( ret, FormatKey( "PC", j + 1, i + 1, ss, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + } + } + ss++; + } + } + +/* EPOCH keywords + ------------ */ + +/* Get any EPOCH card, marking it as read. */ + if( GetValue2( ret, this, "EPOCH", AST__FLOAT, (void *) &dval, 0, method, + class, status ) ){ + +/* Convert values of zero to B1950. */ + if( dval == 0.0 ) dval = 1950.0; + +/* Save a new EQUINOX card in the FitsChan, so long as there is not + already one there. */ + if( !GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0, + method, class, status ) ){ + SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT, + "Reference equinox", status ); + } + } + +/* String EQUINOX values + --------------------- + If found, EQUINOX will be used in favour of any EPOCH value found + above. */ + if( GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0, method, + class, status ) ){ + +/* Note the first character. */ + bj = cval[ 0 ]; + +/* If it is "B" or "J", read a floating value from the rest */ + if( bj == 'B' || bj == 'J' ) { + if( 1 == astSscanf( cval + 1, " %lf ", &dval ) ){ + +/* If it is a Besselian epoch, convert to Julian. */ + if( bj == 'B' ) dval = palEpj( palEpb2d( dval ) ); + +/* Replace the original EQUINOX card. */ + SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT, + CardComm( this, status ), status ); + } + } + } + +/* EQUINOX = 0.0 keywords + ---------------------- */ + if( GetValue2( ret, this, "EQUINOX", AST__FLOAT, (void *) &dval, 0, method, + class, status ) ){ + if( dval == 0.0 ){ + dval = 1950.0; + SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT, + CardComm( this, status ), status ); + } + } + } + +/* DATE-OBS keywords + ---------------- */ + +/* Read any DATE-OBS card. This prevents it being written out when the + FitsChan is deleted. */ + if( s == ' ' ) { + strcpy( keyname, "DATE-OBS" ); + if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method, + class, status ) ){ + +/* Ignore DATE-OBS values if the header contains an MJD-OBS value */ + strcpy( keyname, "MJD-OBS" ); + if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) ){ + +/* Get the corresponding mjd-obs value, checking that DATE-OBS is valid. */ + dval = DateObs( cval, status ); + if( dval != AST__BAD ){ + SetValue( ret, keyname, (void *) &dval, AST__FLOAT, + "Date of observation", status ); + } + } + } + } + +/* Things specific to the CLASS encoding + ------------------------------------- */ + if( encoding == FITSCLASS_ENCODING ) ClassTrans( this, ret, axlat, + axlon, method, class, status ); + +/* Convert SAO distorted TAN headers to TPN distorted TAN headers. + -------------------------------------------------------------- */ + if( s == ' ' && !Ustrcmp( prj, "-TAN", status ) ){ + +/* Translate the COi_m keywords into PV i+m keywords. */ + if( SAOTrans( this, ret, method, class, status ) ) { + +/* Change the CTYPE projection form TAN to TPV. */ + strcpy( prj, "-TPN" ); + strcpy( lontype + 4, "-TPN" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-TPN" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + } + } + +/* AIPS "NCP" projections + --------------------- */ + +/* Compare the projection type with "-NCP" */ + if( !Ustrcmp( prj, "-NCP", status ) ) { + +/* Get the latitude reference value, and take is cot. */ + GetValue2( ret, this, FormatKey( "CRVAL", axlat + 1, -1, s, status ), + AST__FLOAT, (void *) &dval, 1, method, class, status ); + sinval = sin( dval*AST__DD2R ); + if( sinval != 0.0 ) { + dval = cos( dval*AST__DD2R )/sinval; + +/* Replace NCP with SIN in the CTYPE values. */ + strcpy( lontype + 4, "-SIN" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-SIN" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + +/* Store the new projection parameters using names suitable to FITS_WCS + encoding. */ + SetValue( ret, FormatKey( "PV", axlat + 1, 2, s, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + dval = 0.0; + SetValue( ret, FormatKey( "PV", axlat + 1, 1, s, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + } + } + +/* CLASS "ATF" projections + ---------------------- */ + +/* Replace ATF with AIT in the CTYPE values. */ + if( !Ustrcmp( prj, "-ATF", status ) ) { + strcpy( lontype + 4, "-AIT" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-AIT" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + } + +/* AIPS "GLS" projections + --------------------- */ + +/* Compare the projection type with "-GLS" */ + if( !Ustrcmp( prj, "-GLS", status ) ) { + +/* Convert to "-SFL" */ + strcpy( lontype + 4, "-SFL" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-SFL" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + +/* FITS-WCS paper 2 (sec. 6.1.4) describes how to handle AIPS GLS + projections, but requires that the axes are not rotated. Instead, we + modify the native latitude at the fiducial point, theta_0, as is done + in wcslib function celfix in file wcsfix.c (see also FITS-WCS paper + II sec. 2.5). We only need to change theta_0 if the CRVAL position is + not the celestial origin. */ + shifted = 0; + sprintf( keyname, "CRVAL%d", axlon + 1 ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) ){ + if( dval != 0.0 ) shifted = 1; + } + sprintf( keyname, "CRVAL%d", axlat + 1 ); + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, + method, class, status ) ){ + if( dval != 0.0 ) shifted = 1; + } + + if( 0 && shifted ) { + SetValue( ret, FormatKey( "PV", axlon + 1, 2, s, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + dval = 0.0; + SetValue( ret, FormatKey( "PV", axlon + 1, 1, s, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + dval = 1.0; + SetValue( ret, FormatKey( "PV", axlon + 1, 0, s, status ), + (void *) &dval, AST__FLOAT, NULL, status ); + } + } + +/* Rename any "QV" projection parameters to "PV" (such as used by + -TAB to indicate the interpolation method, or by the internal + -TPN projection to indicate distortion coefficients). + ------------------------------------------------------------ */ + +/* Rewind the FitsChan. */ + astClearCard( this ); + +/* Search the FitsChan for QV cards. */ + if( s != ' ' ) { + sprintf( template, "QV%%d_%%d%c", s ); + } else { + strcpy( template, "QV%d_%d" ); + } + while( FindKeyCard( this, template, method, class, status ) && astOK ) { + +/* If the projection name is "TAN", replace TAN with TPN in the CTYPE values. */ + if( !Ustrcmp( prj, "-TAN", status ) ){ + strcpy( prj, "-TPN" ); + strcpy( lontype + 4, "-TPN" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-TPN" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + } + +/* Indicate that the QV card has been consumed. */ + MarkCard( this, status ); + +/* Get the keyword name and change it from QV to PV. */ + strcpy( keyname, CardName( this, status ) ); + keyname[ 0 ] ='P'; + +/* Store the new PV card so long as there it is not already present in the + FitsChan. */ + if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cval, 0, + method, class, status ) ){ + SetValue( ret, keyname, CardData( this, &size, status ), AST__FLOAT, + CardComm( this, status ), status ); + } + +/* Move on to the next card. */ + MoveCard( this, 1, method, class, status ); + } + + + +/* Change any TAN projection to TPN projection if the PolyTan attribute + is non-zero. Also change any TPV projection to TPN projection. + --------------------------------------------------- */ + if( ( !Ustrcmp( prj, "-TAN", status ) && + GetUsedPolyTan( this, ret, axlat + 1, axlon + 1, s, method, class, status ) ) || + !Ustrcmp( prj, "-TPV", status ) ){ + strcpy( prj, "-TPN" ); + strcpy( lontype + 4, "-TPN" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-TPN" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + } + + + +/* IRAF "ZPX" projections + --------------------- */ + if( s == ' ' && !Ustrcmp( prj, "-ZPX", status ) ) { + +/* Replace "ZPX" with "ZPN-ZPX" (i.e. ZPN projection with ZPX distortion + code) in the CTYPE values. */ + strcpy( lontype + 4, "-ZPN-ZPX" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-ZPN-ZPX" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ), + (void *) &cval, AST__STRING, NULL, status ); + +/* Check latitude then longitude axes */ + for( i = 0; i < 2; i++ ){ + iaxis = i ? axlat : axlon; + +/* Concatenate all the IRAF "WAT" keywords together for this axis. These + keywords are marked as having been used, so that they are not written + out when the FitsChan is deleted. */ + watmem = ConcatWAT( this, iaxis, method, class, status ); + +/* Search the total WAT string for any projp terms. */ + if( watmem ){ + for( iproj = 0; iproj < 10 && astOK; iproj++ ) { + sprintf( format, "projp%d=", iproj ); + start = strstr( watmem, format ); + if( start ) { + sprintf( format, "projp%d=%%lf", iproj ); + if( astSscanf( start, format, &projp ) ){ + SetValue( ret, FormatKey( "PV", axlat + 1, iproj, ' ', status ), + (void *) &projp, AST__FLOAT, + "ZPN projection parameter", status ); + } + } + } + +/* Release the memory used to hold the concatenated WAT keywords. */ + watmem = (char *) astFree( (void *) watmem ); + } + } + +/* IRAF "TNX" projections + --------------------- */ + } else if( s == ' ' && !Ustrcmp( prj, "-TNX", status ) ) { + +/* Replace TNX with TPN in the CTYPE values. */ + strcpy( lontype + 4, "-TPN" ); + cval = lontype; + SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ), + (void *) &cval, AST__STRING, NULL, status ); + strcpy( lattype + 4, "-TPN" ); + cval = lattype; + SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ), + (void *) &cval, AST__STRING, NULL, status ); + +/* Check latitude then longitude axes */ + for( i = 0; i < 2; i++ ){ + iaxis = i ? axlat : axlon; + +/* Concatenate all the IRAF "WAT" keywords together for this axis. These + keywords are marked as having been used, so that they are not written + out when the FitsChan is deleted. */ + watmem = ConcatWAT( this, iaxis, method, class, status ); + +/* Extract the polynomial coefficients from the concatenated WAT string. + These are returned in the form of a list of PVi_m values for a TPN + projection. */ + ncoeff = WATCoeffs( watmem, i, &cvals, &mvals, &ok, status ); + +/* If we can handle the TNX projection, store the PV values in the FitsChan. */ + if( ok ) { + for( icoeff = 0; icoeff < ncoeff; icoeff++ ) { + SetValue( ret, FormatKey( "PV", iaxis + 1, mvals[ icoeff ], + ' ', status ), + (void *) (cvals + icoeff), AST__FLOAT, + "TAN projection parameter", status ); + } + +/* If the TNX cannot be represented in FITS-WCS (within our restrictions), add + warning keywords to the FitsChan. */ + } else { + Warn( this, "tnx", "This FITS header includes, or was " + "derived from, a TNX projection which requires " + "unsupported IRAF-specific corrections. The WCS " + "information may therefore be incorrect.", method, class, status ); + } + +/* Release the memory used to hold the concatenated WAT keywords. */ + watmem = (char *) astFree( (void *) watmem ); + } + } + +/* MSX CAR projections. + ------------------- */ + if( !Ustrcmp( prj, "-CAR", status ) && !astGetCarLin( this ) ) { + +/* The CAR projection has valid projection plane points only for native + longitudes in the range [-180,+180]. The reference pixel (CRPIX) is at + native longitude zero. We need to check that the reference point is not + so far outside the image that the entire image lies outside the range + [-180,+180]. If it is, we modify the CRPIX value by the number of + pixels corresponding to 360 degres of longitude in order to bring the + array into the valid domain ([-180,+180]) of the projection. */ + if( GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, s, status ), + AST__FLOAT, (void *) &cdelti, 1, method, class, status ) && + GetValue2( ret, this, FormatKey( "CRPIX", axlon + 1, -1, s, status ), + AST__FLOAT, (void *) &dval, 0, method, class, status ) ) { + +/* We check if the mid point of the array is in the valiud longitude range. Use the + bottom left corner as a fallback if the image size is unknown. */ + if( !GetValue( this, FormatKey( "NAXIS", axlon + 1, -1, ' ', status ), + AST__INT, &dim, 0, 0, method, class, status ) ) { + dim = 0; + } + + if( cdelti != 0.0 ) { + double offset = 0.5*( dim + 1 ); + dval = offset + AST__DR2D*palDrange( AST__DD2R*( dval - offset )*cdelti )/cdelti; + SetValue( ret, FormatKey( "CRPIX", axlon + 1, -1, s, status ), + (void *) &dval, AST__FLOAT, CardComm( this, status ), status ); + } + } + } + +/* Replace RESTFREQ by RESTFRQ. + ---------------------------- */ + +/* Get any RESTFREQ card, marking it as read. */ + if( s != ' ' ) { + sprintf( keyname, "RESTFREQ%c", s ); + } else { + strcpy( keyname, "RESTFREQ" ); + } + if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, method, + class, status ) ){ + +/* Look for "MHz" and "GHz" within the comment. If found scale the value + into Hz. */ + comm = CardComm( this, status ); + if( comm ) { + if( strstr( comm, "GHz" ) ) { + dval *= 1.0E9; + comm = "[Hz] Rest Frequency"; + } else if( strstr( comm, "MHz" ) ) { + dval *= 1.0E6; + comm = "[Hz] Rest Frequency"; + } + } + +/* Save a new RESTFRQ card in the FitsChan, so long as there is not + already one there. */ + if( s != ' ' ) { + sprintf( keyname, "RESTFRQ%c", s ); + } else { + strcpy( keyname, "RESTFRQ" ); + } + if( !GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, + method, class, status ) ){ + SetValue( ret, keyname, (void *) &dval, AST__FLOAT, comm, status ); + } + } + +/* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents. + These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!) + or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!) + or "GEO". Also convert "LAMBDA" to "WAVE". */ + for( j = 0; j < naxis; j++ ) { + if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ), + AST__STRING, (void *) &cval, 0, method, + class, status ) ){ + if( IsAIPSSpectral( cval, &astype, &assys, status ) ) { + SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ), + (void *) &astype, AST__STRING, NULL, status ); + SetValue( ret, "SPECSYS", (void *) &assys, AST__STRING, NULL, status ); + break; + } else if( !strcmp( cval, "LAMBDA " ) ) { + cval = "WAVE"; + SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ), + (void *) &cval, AST__STRING, NULL, status ); + break; + } + } + } + +/* Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s", "M/S" */ + if( s != ' ' ) { + sprintf( template, "CUNIT%%d%c", s ); + } else { + strcpy( template, "CUNIT%d" ); + } + if( astKeyFields( this, template, 1, &jhi, &jlo ) ){ + +/* Convert keyword indices from 1-based to 0-base, and loop round them all. */ + jhi--; + jlo--; + for( j = jlo; j <= jhi; j++ ){ + char *keynam; + keynam = FormatKey( "CUNIT", j + 1, -1, s, status ); + if( GetValue2( ret, this, keynam, AST__STRING, (void *) &cval, 0, + method, class, status ) ){ + size_t nc = astChrLen( cval ); + if( nc == 0 ) { + cval = NULL; + } else if( !Ustrcmp( cval, "Hz", status ) ) { + cval = "Hz"; + } else if( !Ustrcmp( cval, "Angstrom", status ) ) { + cval = "Angstrom"; + } else if( !Ustrcmp( cval, "km/s", status ) ) { + cval = "km/s"; + } else if( !Ustrcmp( cval, "m/s", status ) ) { + cval = "m/s"; + } else { + cval = NULL; + } + if( cval ) SetValue( ret, keynam, (void *) &cval, AST__STRING, NULL, status ); + } + } + } + +/* After doing the primary axis descriptions, prepare to do the "A" + description. */ + if( s == ' ' ) s = 'A' - 1; + } + +/* IRAF mini-WCS keywords + ---------------------- */ + +/* Rewind the FitsChan to search from the first card. */ + astClearCard( this ); + +/* Search forward through until all cards have been checked. */ + while( !astFitsEof( this ) && astOK ){ + +/* Check to see if the keyword name from the current card matches + any of the known mini-WCS keywords. If so, mark the card as read. */ + if( Match( CardName( this, status ), "WAT%d_%d", 0, NULL, &m, method, class, status ) || + Match( CardName( this, status ), "LTM%d_%d", 0, NULL, &m, method, class, status ) || + Match( CardName( this, status ), "LTV%d", 0, NULL, &m, method, class, status ) || + Match( CardName( this, status ), "WSV%d_LEN", 0, NULL, &m, method, class, status ) || + Match( CardName( this, status ), "WSV%d_%d", 0, NULL, &m, method, class, status ) ){ + MarkCard( this, status ); + } + +/* Now move the current card on to the next card. */ + MoveCard( this, 1, method, class, status ); + } + +/* Delete the returned FitsChan if it is empty. */ + if( ret && !astGetNcard( ret ) ) ret = (AstFitsChan *) astDelete( ret ); + +/* Return. */ + return ret; +} + +int Split( AstFitsChan *this, const char *card, char **name, char **value, + char **comment, const char *method, const char *class, int *status ){ +/* +* Name: +* Split + +* Purpose: +* Extract the keyword name, value and comment from a FITS header card. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int Split( AstFitsChan *this, const char *card, char **name, char **value, +* char **comment, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* The name, value and comment (if present) are extracted from the +* supplied card text and returned. + +* Parameters: +* this +* Pointer to the FitsCHan. +* card +* Pointer to a string holding the FITS header card. +* name +* Pointer to a location at which to return the pointer to a string +* holding the keyword name. +* value +* Pointer to a location at which to return the pointer to a string +* holding the keyword value. +* comment +* Pointer to a location at which to return the pointer to a string +* holding the keyword comment. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned value: +* - An integer identifying the data type of the keyword value. This +* will be one of the values AST__UNDEF, AST__COMMENT, AST__INT, +* AST__STRING, AST__CONTINUE, AST__FLOAT, AST__COMPLEXI or AST__COMPLEXF +* defined in fitschan.h. + +* Notes: +* - If the keyword value is a string, then the returned value does not +* include the delimiting quotes, and pairs of adjacent quotes within the +* string are replaced by single quotes. +* - A maximum of 80 characters are read from the supplied card, so the +* string does not need to be null terminated unless less than 80 +* characters are to be read. +* - The memory holding the three strings "name", "value" and "comment" +* should be released when no longer needed using astFree. +* - NULL pointers and a data type of AST__COMMENT are returned if an +* error has already occurred, or if this function fails for any reason. +*/ + +/* Local Variables: */ + char *c; /* Pointer to returned comment string */ + char *dd; /* Pointer to intermediate character */ + char *slash; /* Pointer to comment character */ + char *v; /* Pointer to returned value string */ + char buf[255]; /* Buffer for warning text */ + const char *d; /* Pointer to first comment character */ + const char *v0; /* Pointer to first non-blank value character */ + double fi, fr; /* Values read from value string */ + int badval; /* Is the keyword value illegal? */ + int blank_name; /* Is keyword name blank? */ + int cont; /* Is this a continuation card? */ + int i; /* Character index */ + int ii, ir; /* Values read from value string */ + int iopt; /* Index of option within list */ + int len; /* Used length of value string */ + int lq; /* Was previous character an escaping quote? */ + int nch; /* No. of characters used */ + int ndig; /* No. of digits in the formatted integer */ + int type; /* Keyword data type */ + size_t nc; /* Number of character in the supplied card */ + size_t ncc; /* No. of characters in the comment string */ + size_t ncv; /* No. of characters in the value string */ + +/* Initialise the returned pointers. */ + *name = NULL; + *value = NULL; + *comment = NULL; + type = AST__COMMENT; + +/* Check the global status. */ + if( !astOK ) return type; + +/* Assume initially that the keyword value is legal. */ + badval = 0; + +/* Store the number of characters to be read from the supplied card. This + is not allowed to be more than the length of a FITS header card. */ + nc = 0; + while( nc < AST__FITSCHAN_FITSCARDLEN && card[ nc ] ) nc++; + +/* Reduce the number of characters to read so that any non-printing + characters such as new-lines at the end of the string are ignored. */ + while( nc > 0 && !isprint( card[ nc - 1 ] ) ) nc--; + +/* Allocate memory for a copy of the keyword name plus a terminating + null character. */ + *name = (char *) astMalloc( ( 1 + FITSNAMLEN )*sizeof(char) ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise the name string by filling it with spaces, and terminating it. */ + for( i = 0; i < FITSNAMLEN; i++ ) (*name)[ i ] = ' '; + (*name)[ FITSNAMLEN ] = 0; + +/* Copy the the keyword name, ensuring that no more than FITSNAMLEN (8) + characters are copied. */ + strncpy( *name, card, ( nc > FITSNAMLEN ) ? FITSNAMLEN : nc ); + +/* If there is no keyword name, flag that we have a blank name which will + be treated as a comment card. */ + if( strspn( *name, " " ) == strlen( *name ) ){ + blank_name = 1; + +/* If the card contains a keyword name, replace any white space with + nulls. */ + } else { + blank_name = 0; + dd = *name + strlen( *name ) - 1; + while( isspace( *dd ) ) *(dd--) = 0; + } + +/* Check the keyword name is legal. */ + CheckFitsName( this, *name, method, class, status ); + +/* Allocate memory to hold the keyword value and comment strings. */ + *value = (char *) astMalloc( sizeof(char)*( 2 + nc ) ); + *comment = (char *) astMalloc( sizeof(char)*( 1 + nc ) ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* Check for CONTINUE cards. These have keyword CONTINUE but have a space + instead of an equals sign in column 9. They must also have a single quote + in column 11. */ + cont = ( !Ustrcmp( *name, "CONTINUE", status ) && + nc > FITSNAMLEN + 3 && + card[ FITSNAMLEN ] == ' ' && + card[ FITSNAMLEN + 2 ] == '\'' ); + +/* If column 9 does not contain an equals sign (but is not a CONTINUE card), or if + the keyword is "HISTORY", "COMMENT" or blank, then columns 9 to the end are + comment characters, and the value string is null. */ + if( ( nc <= FITSNAMLEN || card[ FITSNAMLEN ] != '=' + || !Ustrcmp( *name, "HISTORY", status ) + || !Ustrcmp( *name, "COMMENT", status ) + || blank_name ) && !cont ){ + (*value)[ 0 ] = 0; + if( nc > FITSNAMLEN ){ + (void) strncpy( *comment, card + FITSNAMLEN, + nc - FITSNAMLEN ); + (*comment)[ nc - FITSNAMLEN ] = 0; + } else { + (*comment)[ 0 ] = 0; + } + +/* Otherwise there is a value field. */ + } else { + +/* Find the first non-blank character in the value string. */ + v0 = card + FITSNAMLEN + 1; + while( (size_t)(v0 - card) < nc && + isspace( (int) *v0 ) ) v0++; + +/* Store pointers to the start of the returned value and comment strings. */ + v = *value; + c = *comment; + +/* If the first character in the value string is a single quote, the value is + a string. In this case the value ends at the first non-escaped single + quote. */ + if( *v0 == '\''){ + type = cont ? AST__CONTINUE : AST__STRING; + +/* We want to copy the string value, without the delimiting quotes, to the + returned value string. Single quotes within the string are represented + by two adjacent quotes, so we also need to check for these and replace + them by one quote in the returned string. First initialise a pointer + to the first character after the opening quote, and set a flag + indicating that (for the purposes of identifying pairs of adjacent + quotes within the string) the previous character was not a quote. */ + d = v0 + 1; + lq = 0; + +/* Loop round each remaining character in the supplied card. */ + while( (size_t)(d - card) < nc ){ + +/* If the current character is a single quote... */ + if( *d == '\'' ){ + +/* If the previous character was also a single quote then the quote does + not mark the end of the string, but is a quote to be included literally + in the value. Copy the quote to the returned string and clear the flag + to indicate that the pair of adjacent quotes is now complete. */ + if( lq ){ + *(v++) = '\''; + lq = 0; + +/* If the last character was not a quote, then set the flag for the next + pass through the loop, but do not copy the quote to the returned string + since it will either be a quote escaping a following adjacent quote, or + a quote to mark the end of the string. */ + } else { + lq = 1; + } + +/* If the current character is not a quote... */ + } else { + +/* If the previous character was a quote, then we have found a single + isolated quote which therefore marks the end of the string value. + The pointer "d" is left pointing to the first character + after the terminating quote. */ + if( lq ){ + break; + +/* If the last character was not a quote, copy it to the returned string. */ + } else { + *(v++) = *d; + } + } + d++; + } + +/* Terminate the returned value string. */ + *v = 0; + +/* Now deal with logical and numerical values. */ + } else { + +/* The end of the value field is marked by the first "/". Find the number + of characters in the value field. Pointer "d" is left pointing to the + first character in the comment (if any). Only use "/" characters which + occur within the first nc characters, and do not occur wiuthin the + keyword name (not strictly legal, but a warning will have been issued + by CheckFitsName in such cases). */ + d = strchr( card + FITSNAMLEN, '/' ); + if( !d || ( d - card ) >= nc ){ + ncv = nc - FITSNAMLEN - 1; + d = NULL; + } else { + ncv = (size_t)( d - card ) - FITSNAMLEN - 1; + } + +/* Copy the value string to the returned string. */ + if( ncv == 0 ){ + *v = 0; + } else { + strncpy( v, card + FITSNAMLEN + 1, ncv ); + v[ ncv ] = ' '; + v[ ncv + 1 ] = 0; + } + +/* Find the first non-blank character in the value string. */ + v0 = v; + while( *v0 && isspace( (int) *v0 ) ) v0++; + +/* See if the value string is one of the following strings (optionally + abbreviated and case insensitive): YES, NO, TRUE, FALSE. */ + iopt = FullForm( "YES NO TRUE FALSE", v0, 1, status ); + +/* Return the single character "T" or "F" at the start of the value string + if the value matches one of the above strings. */ + if( iopt == 0 || iopt == 2 ) { + type = AST__LOGICAL; + strcpy ( v, "T" ); + } else if( iopt == 1 || iopt == 3 ) { + type = AST__LOGICAL; + strcpy ( v, "F" ); + +/* If it does not match, see if the value is numerical. */ + } else { + +/* Save the length of the value string excluding trailing blanks. */ + len = ChrLen( v, status ); + +/* If the entire string is blank, the value type is UNDEF. */ + if( len == 0 ) { + type = AST__UNDEF; + +/* If there are no dots (decimal points) or exponents (D or E) in the value... */ + } else if( !strpbrk( v, ".EeDd" ) ){ + +/* First attempt to read two integers from the string (separated by white + space). */ + if( nch = 0, + ( 2 == astSscanf( v, " %d %d%n", &ir, &ii, &nch ) ) && + ( nch >= len ) ) { + type = AST__COMPLEXI; + +/* If that failed, attempt to read a single integer from the string. */ + } else if( nch = 0, + ( 1 == astSscanf( v, " %d%n", &ir, &nch ) ) && + ( nch >= len ) ) { + type = AST__INT; + } + +/* If there are dots (decimal points) in the value... */ + } else { + +/* First attempt to read two doubles from the string (separated by white + space). */ + if( nch = 0, + ( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) && + ( nch >= len ) ) { + type = AST__COMPLEXF; + +/* If that failed, attempt to read a single double from the string. */ + } else if( nch = 0, + ( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) && + ( nch >= len ) ) { + type = AST__FLOAT; + } + +/* If both the above failed, it could be because the string contains a + "D" exponent (which is probably valid FITS) instead of an "E" exponent. + Replace any "D" in the string with "e" and try again. */ + if( type == AST__COMMENT && astOK ) { + +/* Replace "d" and "D" by "e" (if this doesn't produce a readable floating + point value then the value string will not be used, so it is safe to + do the replacement in situ). */ + for( i = 0; i < len; i++ ) { + if( v[ i ] == 'd' || v[ i ] == 'D' ) v[ i ] = 'e'; + } + +/* Attempt to read two doubles from the edited string (separated by white + space). */ + if( nch = 0, + ( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) && + ( nch >= len ) ) { + type = AST__COMPLEXF; + +/* If that failed, attempt to read a single double from the edited string. */ + } else if( nch = 0, + ( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) && + ( nch >= len ) ) { + type = AST__FLOAT; + } + } + } + } + +/* If the value type could not be determined, indicate that a warning + should be issued. */ + if( type == AST__COMMENT && astOK ) { + badval = 1; + (*value)[ 0 ] = 0; + (*comment)[ 0 ] = 0; + d = NULL; + } + } + +/* Find the number of characters in the comment. Pointer "d" should point to + the first character following the value string. */ + if( d ){ + ncc = nc - (size_t)( d - card ); + } else { + ncc = 0; + } + +/* Copy the remainder of the card to the returned comment string. */ + if( astOK && ncc > 0 ){ + strncpy( c, d, ncc ); + c[ ncc ] = 0; + +/* Find the start of the comment (indicated by the first "/" after the + value string). */ + slash = strchr( c, '/' ); + +/* Temporarily terminate the string at the slash. */ + if( slash ) *slash = 0; + +/* Shuffle the characters following the slash down to the + start of the returned string. */ + if( slash ){ + ncc -= (size_t)( slash - c ) + 1; + d = slash + 1; + for( i = 0; i < 1 + (int) ncc; i++ ) *(c++) = *(d++); + } + +/* If there is no comment string, return a null string. */ + } else { + *c = 0; + } + } + } + } + +/* Truncate the returned string to avoid wasting space. */ + if( *name ) *name = (char *) astRealloc( (void *) *name, strlen( *name ) + 1 ); + if( *comment ) *comment = (char *) astRealloc( (void *) *comment, strlen( *comment ) + 1 ); + if( *value ) *value = (char *) astRealloc( (void *) *value, strlen( *value ) + 1 ); + +/* If the value is deemed to be integer, check that the number of digits + in the formatted value does not exceed the capacity of an int. This may + be the case if there are too many digits in the integer for an "int" to + hold. In this case, change the data type to float. */ + if( *value && type == AST__INT ) { + ndig = 0; + c = *value; + while( *c ) { + if( isdigit( *(c++) ) ) ndig++; + } + if( ndig >= int_dig ) type = AST__FLOAT; + } + +/* If an error occurred, free the returned strings and issue a context message. */ + if( !astOK ){ + *name = (char *) astFree( (void *) *name ); + *value = (char *) astFree( (void *) *value ); + *comment = (char *) astFree( (void *) *comment ); + type = AST__COMMENT; + astError( astStatus, "%s(%s): Unable to store the following FITS " + "header card:\n%.*s\n", status, method, class, + AST__FITSCHAN_FITSCARDLEN, card ); + +/* If a bad keyword value was encountered, issue a warning. Remember that + "card" may not be null terminated, so ensure that only one header is + included from "card". */ + } else if( badval ){ + snprintf( buf, sizeof(buf), "The keyword value is illegal in " + "'%.*s'", AST__FITSCHAN_FITSCARDLEN, card ); + Warn( this, "badkeyvalue", buf, method, class, status ); + } + +/* Return the data type. */ + return type; +} + +static int SplitMap( AstMapping *map, int invert, int ilon, int ilat, + AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status ){ +/* +* Name: +* SplitMap + +* Purpose: +* Locate a WCS projection within a Mapping. + +* Type: +* Private function. + +* Synopsis: +* int SplitMap( AstMapping *map, int invert, int ilon, int ilat, +* AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* If possible, the supplied Mapping is decomposed into three component +* mappings to be compounded in series. To be acceptable, the second of +* these three Mappings must be an inverted WcsMap with a non-zero +* FITSProj attribute value, and there must not be such a WcsMap in +* either of the other two Mappings. If it is not possible to produce +* such a group of three Mappings, then a zero function value is returned, +* together with three NULL Mapping pointers. All the mappings before the +* WcsMap are compounded together and returned as "map1". The inverse of +* the WcsMap itself is returned as "map2", and any remaining Mappings +* are compounded together and returned as "map3". +* +* The search algorithm allows for an arbitrary combination of series and +* parallel CmpMaps. + +* Parameters: +* map +* A pointer to the Mapping from pixel to physical coordinates. +* invert +* The value of the Invert attribute to use with "map" (the value +* returned by astGetInvert is not used). +* ilon +* Index of mapping output which is connected to the longitude axis. +* ilat +* Index of mapping output which is connected to the latitude axis. +* map1 +* A location at which to return a pointer to the Mapping from pixel +* to intermediate world coordinates. +* map2 +* A location at which to return a pointer to the Mapping from +* intermediate world coordinates to native spherical coordinates. This +* will be an inverted WcsMap with non-zero FITSProj attribute value. +* map3 +* A location at which to return a pointer to the Mapping from +* native spherical coordinates to physical coordinates. +* dep +* The address of an integer holding the current depth of recursion +* into this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a suitable WcsMap was found, zero otherwise. + +* Notes: +* - The returned Mappings contain independant copies of the relevant +* components of the supplied Mapping and can be modified without +* changing the supplied Mapping. +* - NULL pointers will be returned for all Mappings if no WcsMap +* can be found in the supplied Mapping. +* - A pointer to a UnitMap will be returned for map1 if no mappings +* exist before the WcsMap. +* - A pointer to a UnitMap will be returned for map3 if no mappings +* exist after the WcsMap. +* - NULL pointers will be returned for all Mappings and a function +* value of zero will be returned if an error has occurred, or if this +* function should fail for any reason. +*/ + +/* Local Variables */ + AstFitsChan *fc; /* Pointer to temporary FitsChan */ + AstFrameSet *tfs; /* Temporary FrameSet */ + AstMapping *mapa; /* Pre-wcs Mapping */ + AstMapping *mapc; /* Post-wcs Mapping */ + AstMapping *tmap1; /* Temporary Mapping */ + AstMapping *tmap2; /* Temporary Mapping */ + AstPointSet *pset1; /* Pixel positions */ + AstPointSet *pset2; /* WCS positions */ + AstWcsMap *mapb; /* WcsMap */ + char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for header card */ + double **ptr1; /* Pointer to pixel axis values */ + double **ptr2; /* Pointer to WCS axis values */ + double *iwc_origin; /* Array holding IWC at pixel origin */ + double *pix_origin; /* Array holding pixel coords at pixel origin */ + double *w1; /* Pointer to work space */ + int i; /* Loop index */ + int npix; /* Number of pixel axes */ + int nwcs; /* Number of WCS axes */ + int ret; /* Was a non-linear Mapping found? */ + +/* Initialise */ + *map1 = NULL; + *map2 = NULL; + *map3 = NULL; + ret = 0; + +/* Check the global status. */ + if( !astOK ) return ret; + +/* Call SplitMap2 to do the work. SplitMap2 does not check that the + WcsMap is an *inverted* WcsMap, neither does it check that there + are no WcsMaps in either map1 or map3. */ + if( SplitMap2( map, invert, map1, map2, map3, status ) ) { + +/* Check that the WcsMap is inverted. */ + if( astGetInvert( *map2 ) ) { + +/* Check that map 1 does not contain a WcsMap with non-zero FITSProj + attribute. */ + if( !SplitMap2( *map1, astGetInvert( *map1 ), &mapa, &mapb, &mapc, + status ) ) { + +/* Check that map 3 does not contain a WcsMap with non-zero FITSProj + attribute. */ + if( !SplitMap2( *map3, astGetInvert( *map3 ), &mapa, &mapb, &mapc, + status ) ) { + +/* If so, the three Mappings are OK. */ + ret = 1; + } else { + mapa = astAnnul( mapa ); + mapb = astAnnul( mapb ); + mapc = astAnnul( mapc ); + } + } else { + mapa = astAnnul( mapa ); + mapb = astAnnul( mapb ); + mapc = astAnnul( mapc ); + } + } + } + +/* If the above failed to find a suitable WcsMap, we now consider cases + where the pixel->WCS mapping is linear. We can invent a CAR projection + WcsMap for such cases. We use a ShiftMap to move the origin of the + longitude IWC axis to a sensible value (it is left at zero otherwise). + We cannot do this with the latitude axis since pre-FITS-WCS fits + readers could not handle the resulting rotation from native to celestial + coords. */ + if( !ret && astGetIsLinear( map ) ) { + nwcs = astGetNout( map ); + npix = astGetNin( map ); + iwc_origin = astMalloc( sizeof( double )*nwcs ); + pix_origin = astMalloc( sizeof( double )*npix ); + if( astOK ) { + for( i = 0; i < npix; i++ ) pix_origin[ i ] = 0.0; + astTranN( map, 1, npix, 1, pix_origin, 1, nwcs, 1, iwc_origin ); + for( i = 0; i < nwcs; i++ ) { + if( i != ilon ) { + iwc_origin[ i ] = 0.0; + } else { + iwc_origin[ i ] *= -1; + } + } + mapa = (AstMapping *) astShiftMap( nwcs, iwc_origin, "", status ); + *map1 = (AstMapping *) astCmpMap( map, mapa, 1, "", status ); + *map2 = astWcsMap( nwcs, AST__CAR, ilon + 1, ilat + 1, "Invert=1", status ); + astInvert( mapa ); + *map3 = astClone( mapa ); + mapa = astAnnul( mapa ); + ret = 1; + } + iwc_origin = astFree( iwc_origin ); + pix_origin = astFree( pix_origin ); + } + +/* If the above failed to find a suitable WcsMap, we now consider cases + where the output (long,lat) values are constants supplied by a + final PermMap. We can invent a WcsMap for such cases. */ + if( !ret ) { + +/* Transform two arbitrary pixel positions into the WCS Frame. */ + npix = astGetNin( map ); + nwcs = astGetNout( map ); + pset1 = astPointSet( 2, npix, "", status ); + pset2 = astPointSet( 2, nwcs, "", status ); + ptr1 = astGetPoints( pset1 ); + ptr2 = astGetPoints( pset2 ); + w1 = astMalloc( sizeof( double )*(size_t) nwcs ); + if( astOK ) { + for( i = 0; i < npix; i++ ) { + ptr1[ i ][ 0 ] = 1.0; + ptr1[ i ][ 1 ] = 1000.0; + } + (void) astTransform( map, pset1, 1, pset2 ); + +/* If the two wcs positions have equal longitude and latitude values, + assume that the output longitude and latitude axes are assigned + constant values by the Mapping. */ + if( ptr2[ ilon ][ 0 ] == ptr2[ ilon ][ 1 ] && + ptr2[ ilon ][ 0 ] != AST__BAD && + ptr2[ ilat ][ 0 ] == ptr2[ ilat ][ 1 ] && + ptr2[ ilat ][ 0 ] != AST__BAD ) { + +/* Create a set of Mappings to return, including a WcsMap, which result in + these constant latitude and longitude values. We do this by creating a + FITS-WCS header and reading the FrameSet from it. Keywords which are not + important to the final mappings are given arbitrary values. */ + fc = astFitsChan( NULL, NULL, "", status ); + for( i = 0; i < nwcs; i++ ) { + sprintf( card, "CRPIX%d = 0", i + 1 ); + astPutFits( fc, card, 0 ); + sprintf( card, "CDELT%d = 0.0003", i + 1 ); + astPutFits( fc, card, 0 ); + if( i == ilon ) { + sprintf( card, "CTYPE%d = 'RA---TAN'", i + 1 ); + } else if( i == ilat ) { + sprintf( card, "CTYPE%d = 'DEC--TAN'", i + 1 ); + } else { + sprintf( card, "CTYPE%d = 'DUMMY'", i + 1 ); + } + astPutFits( fc, card, 0 ); + if( i == ilon ) { + sprintf( card, "CRVAL%d = %.*g", i + 1, DBL_DIG, AST__DR2D*ptr2[ ilon ][ 0 ] ); + } else if( i == ilat ) { + sprintf( card, "CRVAL%d = %.*g", i + 1, DBL_DIG, AST__DR2D*ptr2[ ilat ][ 0 ] ); + } else { + sprintf( card, "CRVAL%d = 0.0", i + 1 ); + } + astPutFits( fc, card, 0 ); + } + astClearCard( fc ); + tfs = astRead( fc ); + if( tfs ) { + +/* Use SplitMap to get the required Mapings from the FrameSet. */ + tmap2 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + SplitMap( tmap2, astGetInvert( tmap2 ), 0, 1, &tmap1, map2, + map3, status ); + tmap1 = astAnnul( tmap1 ); + tmap2 = astAnnul( tmap2 ); + +/* Create a ShiftMap which subtract the constant longitude and latitude + values off the inputs. */ + for( i = 0; i < nwcs; i++ ) w1[ i ] = 0.0; + w1[ ilon ] = -ptr2[ ilon ][ 0 ]; + w1[ ilat ] = -ptr2[ ilat ][ 0 ]; + tmap1 = (AstMapping *) astShiftMap( nwcs, w1, "", status ); + +/* Compose this with the supplied Mapping. This results in the celestial + outputs being zero. This gives the required "map1". */ + *map1 = (AstMapping *) astCmpMap( map, tmap1, 1, "", status ); + +/* Indicate success.*/ + ret = 1; + +/* Free resources. */ + tmap1 = astAnnul( tmap1 ); + tfs = astAnnul( tfs ); + } + fc = astAnnul( fc ); + } + } + +/* Free resources */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + w1 = astFree( w1 ); + } + if( !ret ) { + if( *map1 ) *map1 = astAnnul( *map1 ); + if( *map2 ) *map2 = astAnnul( *map2 ); + if( *map3 ) *map3 = astAnnul( *map3 ); + } + return ret; +} + +static int SplitMap2( AstMapping *map, int invert, AstMapping **map1, + AstWcsMap **map2, AstMapping **map3, int *status ){ +/* +* Name: +* SplitMap2 + +* Purpose: +* Locate a WCS projection within a Mapping. + +* Type: +* Private function. + +* Synopsis: +* int SplitMap2( AstMapping *map, int invert, AstMapping **map1, +* AstWcsMap **map2, AstMapping **map3, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* If possible, the supplied Mapping is decomposed into three component +* mappings to be compounded in series. To be acceptable, the second of +* these three Mappings must be a WcsMap with a non-zero FITSProj value. +* If it is not possible to produce such a group of three Mappings, then a +* zero function value is returned, together with three NULL Mapping +* pointers. All the mappings before the WcsMap are compounded together +* and returned as "map1". The WcsMap itself is returned as "map2", and +* any remaining Mappings are compounded together and returned as "map3". +* +* The search algorithm allows for an arbitrary combination of series and +* parallel CmpMaps. + +* Parameters: +* map +* A pointer to the Mapping from pixel to physical coordinates. +* invert +* The value of the Invert attribute to use with "map" (the value +* returned by astGetInvert is not used). +* map1 +* A location at which to return a pointer to the Mapping from pixel +* to intermediate world coordinates. +* map2 +* A location at which to return a pointer to the Mapping from relative +* physical coordinates to native spherical coordinates. This will +* be a WcsMap, and it will have a non-zero FITSProj value. +* map3 +* A location at which to return a pointer to the Mapping from +* native spherical coordinates to physical coordinates. +* dep +* The address of an integer holding the current depth of recursion +* into this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a suitable WcsMap was found, zero otherwise. + +* Notes: +* - The returned Mappings contain independant copies of the relevant +* components of the supplied Mapping and can be modified without +* changing the supplied Mapping. +* - NULL pointers will be returned for all Mappings if no WcsMap +* with anon-zero FITSProj value can be found in the supplied Mapping. +* - A pointer to a UnitMap will be returned for map1 if no mappings +* exist before the WcsMap. +* - A pointer to a UnitMap will be returned for map3 if no mappings +* exist after the WcsMap. +* - NULL pointers will be returned for all Mappings and a function +* value of zero will be returned if an error has occurred, or if this +* function should fail for any reason. +* - "*map1" and "*map3" may contain WcsMaps, but they will have zero +* values for their FITSProj values. +*/ + +/* Local Variables */ + AstMapping **map_list; /* Mapping array pointer */ + AstMapping *mapa; /* Pre-wcs Mapping */ + AstWcsMap *mapb; /* WcsMap */ + AstMapping *mapc; /* Post-wcs Mapping */ + AstMapping *temp; /* Intermediate Mapping */ + const char *class; /* Pointer to class of supplied Mapping */ + double pv; /* Projection parameter value */ + int *invert_list; /* Invert array pointer */ + int axis; /* No. of axes in whole Mapping */ + int axlat; /* Index of latitude axis */ + int axlon; /* Index of longitude axis */ + int haswcs; /* Was a usable inverted WcsMap found? */ + int imap; /* Index of current Mapping in list */ + int i; /* axis index */ + int m; /* Parameter index */ + int nax; /* No. of axes in Mapping */ + int nmap; /* Number of Mappings in the list */ + int ret; /* Was a non-linear Mapping found? */ + int wcsaxis; /* Index of first WcsMap axis */ + +/* Initialise */ + *map1 = NULL; + *map2 = NULL; + *map3 = NULL; + ret = 0; + +/* Check the global status. */ + if( !astOK ) return ret; + +/* Get the class of the Mapping. */ + class = astGetClass( map ); + +/* If the supplied Mapping is a CmpMap... */ + wcsaxis = -1; + if( !strcmp( class, "CmpMap" ) ){ + +/* Decompose the Mapping into a sequence of Mappings to be applied in + series and an associated list of Invert flags. */ + map_list = NULL; + invert_list = NULL; + nmap = 0; + astMapList( map, 1, invert, &nmap, &map_list, &invert_list ); + +/* If there is more than one Mapping, this must be a series CmpMap. */ + if( nmap > 1 && astOK ){ + +/* Initialise the returned pre-wcs Mapping to be a UnitMap. */ + if( invert == astGetInvert( map ) ){ + *map1 = (AstMapping *) astUnitMap( astGetNin( map ), "", status ); + } else { + *map1 = (AstMapping *) astUnitMap( astGetNout( map ), "", status ); + } + +/* Indicate we have not yet found a WcsMap. */ + ret = 0; + +/* Process each series Mapping. */ + for( imap = 0; imap < nmap; imap++ ){ + +/* If we have not yet found a WcsMap... */ + if( !ret ){ + +/* Search this Mapping for a WcsMap. */ + ret = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa, + map2, map3, status ); + +/* If no WcsMap was found, use the whole mapping as part of the + pre-wcs Mapping. */ + if( !ret ){ + mapa = astCopy( map_list[ imap ] ); + astSetInvert( mapa, invert_list[ imap ] ); + } + +/* Add the pre-wcs mapping to the cumulative pre-wcs CmpMap. */ + temp = (AstMapping *) astCmpMap( *map1, mapa, 1, "", status ); + *map1 = astAnnul( *map1 ); + mapa = astAnnul( mapa ); + *map1 = temp; + +/* If we have previously found a WcsMap, use the whole mapping + as part of the post-wcs mapping. */ + } else { + mapc = astCopy( map_list[ imap ] ); + astSetInvert( mapc, invert_list[ imap ] ); + temp = (AstMapping *) astCmpMap( *map3, mapc, 1, "", status ); + *map3 = astAnnul( *map3 ); + mapc = astAnnul( mapc ); + *map3 = temp; + } + } + +/* If there is only one Mapping, this must be a parallel CmpMap. */ + } else { + +/* Annul the Mapping pointer in the series list created above, and free the + dynamic arrays. */ + map_list[ 0 ] = astAnnul( map_list[ 0 ] ); + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + nmap = 0; + +/* Decompose the Mapping into a sequence of Mappings to be applied in + parallel and an associated list of Invert flags. */ + astMapList( map, 0, invert, &nmap, &map_list, &invert_list ); + +/* Process each parallel Mapping. */ + axis = 0; + for( imap = 0; imap < nmap && astOK; imap++ ){ + +/* See if this Mapping contains a usable WcsMap. Only do the search + if no such WcsMap has already been found, since only the first is usable. */ + if( !ret ) { + +/* Search this Mapping for a WcsMap. */ + haswcs = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa, + &mapb, &mapc, status ); + +/* Note if we have found a usable WcsMap, and its first axis index. */ + if( haswcs ){ + ret = 1; + wcsaxis = axis; + } + +/* If a WcsMap has already been found, the mapping cannot contain a + usable WcsMap. */ + } else { + haswcs = 0; + } + +/* If the Mapping did not contain a usable WcsMap, use the whole mapping as + part of the pre-wcs Mapping, and create a UnitMap as part of the post-wcs + mapping. */ + if( !haswcs ){ + mapa = astCopy( map_list[ imap ] ); + astSetInvert( mapa, invert_list[ imap ] ); + nax = astGetNout( mapa ); + mapc = (AstMapping *) astUnitMap( nax, "", status ); + } + +/* Increment the index of the first axis in the next Mapping. */ + axis += astGetNout( mapa ); + +/* Add the pre-wcs mapping in parallel with the cumulative pre-wcs CmpMap. */ + if( *map1 ){ + temp = (AstMapping *) astCmpMap( *map1, mapa, 0, "", status ); + *map1 = astAnnul( *map1 ); + mapa = astAnnul( mapa ); + *map1 = temp; + } else { + *map1 = mapa; + } + +/* Add the post-wcs mapping in parallel with the cumulative post-wcs CmpMap. */ + if( *map3 ){ + temp = (AstMapping *) astCmpMap( *map3, mapc, 0, "", status ); + *map3 = astAnnul( *map3 ); + mapc = astAnnul( mapc ); + *map3 = temp; + } else { + *map3 = mapc; + } + } + +/* If a usable WcsMap was found, create a new one which has all the same + properties, but with enough axes to join the pre and post wcs Mappings + together. Ensure the correct axes are used for longitude and latitude, + and copy the projection parameters. */ + if( ret ){ + axlat = astGetWcsAxis( mapb, 1 ); + axlon = astGetWcsAxis( mapb, 0 ); + *map2 = astWcsMap( axis, astGetWcsType( mapb ), + axlon + wcsaxis + 1, + axlat + wcsaxis + 1, "", status ); + for( i = 0; i < astGetNin( mapb ); i++ ){ + for( m = 0; m < WCSLIB_MXPAR; m++ ){ + if( astTestPV( mapb, i, m ) ) { + pv = astGetPV( mapb, i, m ); + if( pv != AST__BAD ) astSetPV( *map2, i + wcsaxis, m, pv ); + } + } + } + astInvert( *map2 ); + mapb = astAnnul( mapb ); + } + } + +/* Loop to annul all the Mapping pointers in the list. */ + for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] ); + +/* Free the dynamic arrays. */ + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + +/* If the supplied Mapping is not a CmpMap, see if it is a WcsMap with a + non-zero FITSProj value. If so, take a copy and set its invert attribute + correctly. Also create UnitMaps for the pre and post wcs mappings. */ + } else if( astOK && !strcmp( class, "WcsMap" ) && astGetFITSProj( map ) ){ + ret = 1; + nax = astGetNin( map ); + *map1 = (AstMapping *) astUnitMap( nax, "", status ); + *map2 = astCopy( map ); + astSetInvert( *map2, invert ); + *map3 = (AstMapping *) astUnitMap( nax, "", status ); + } + +/* If an error has occurred, or if no suitable WcsMap was found, annul any + Mappings. */ + if( !astOK || !ret ){ + ret = 0; + if( *map1 ) *map1 = astAnnul( *map1 ); + if( *map2 ) *map2 = astAnnul( *map2 ); + if( *map3 ) *map3 = astAnnul( *map3 ); + } + +/* Return the answer. */ + return ret; +} + +static int SplitMat( int naxis, double *matrix, double *cdelt, int *status ){ +/* +* Name: +* SplitMat + +* Purpose: +* Factorises a single "CD"-style matrix into a diagonal CDELT matrix +* and a "PC"-style matrix. + +* Type: +* Private function. + +* Synopsis: +* int SplitMat( int naxis, double *matrix, double *cdelt, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function splits up the supplied CD matrix into separate PC and +* CDELT matrices. The product of the returned matrices (CDELT.PC) +* equals the supplied CD matrix. The CDELT values are chosen so that +* the corresponding row of the PC matrix represents a unit vector. +* The signs of the CDELT values are chosen so that the diagonal terms +* of the PC matrix are all positive. +* + +* Parameters: +* naxis +* The number of axes. +* matrix +* A pointer to an array of naxis*naxis elements. On entry this holds +* the "CD" matrix. On exit, it is modified to represent the "PC" +* matrix. +* cdelt +* A pointer to an array of naxis elements. On exit this holds the CDELT +* values for each axis (i.e. the diagonal terms of the CDELT matrix). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if any bad values are found in the supplied +* matrix, or if an error has already occurred. One is returned otherwise. +*/ + +/* Local Variables: */ + int i; + int j; + int ok; + double *a; + int dineg; + double s2; + double cdlt; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Assume success. */ + ok = 1; + +/* Loop round every row in the matrix. Get a pointer to the first element + in the row. */ + for( i = 0; i < naxis; i++ ){ + a = matrix + i*naxis; + +/* Note the sign of the diagonal term (i.e. the i'th element) of this row. */ + dineg = ( a[ i ] < 0.0 ); + +/* Get the magnitude of the vector represented by this row. This is the + CDELT value for the row. BAD values cause the whole function to return. */ + s2 = 0.0; + for( j = 0; j < naxis; j++ ){ + if( *a == AST__BAD ) { + ok = 0; + break; + } + s2 += (*a)*(*a); + a++; + } + if( !ok ) break; + cdlt = sqrt( astMAX( 0.0, s2 ) ); + +/* If the diagonal term for this row of the matrix is negative, make + the CDELT value negative instead. This means that the diagonal term in + the final PC matrix will be positive. */ + if( dineg ) cdlt = -cdlt; + +/* Store the CDELT value. */ + cdelt[ i ] = cdlt; + +/* The row of the PC matrix is obtained by dividing the original row by + the CDELT value. Set to zero any PC values which are less than 1.0E-7 + (such values may be produced by rounding errors). */ + a = matrix + i*naxis; + for( j = 0; j < naxis; j++ ) { + if( cdlt != 0.0 ){ + *a /= cdlt; + if( fabs( *a ) < 1.E-7 ) *a = 0.0; + } else { + *a = 0.0; + } + a++; + } + } + return ok; +} +static void TableSource( AstFitsChan *this, + void (* tabsource)( AstFitsChan *, const char *, + int, int, int * ), + int *status ){ + +/* +*++ +* Name: +c astTableSource +f AST_TABLESOURCE + +* Purpose: +c Register a source function for accessing tables in FITS files. +f Register a source routine for accessing tables in FITS files. + +* Type: +* Public function. + +* Synopsis: +c #include "fitschan.h" +c void astTableSource( AstFitsChan *this, +c void (* tabsource)( AstFitsChan *, const char *, +c int, int, int * ) ) +f CALL AST_TABLESOURCE( THIS, TABSOURCE, STATUS ) + +* Class Membership: +* FitsChan member function. + +* Description: +c This function can be used to register a call-back function +f This routine can be used to register a call-back routine +* with a FitsChan. The registered +c function +f routine +* is called when-ever the FitsChan needs to read information from a +* binary table contained within a FITS file. This occurs if the +c astRead +f AST_READ +* function is invoked to read a FrameSet from a set of FITS headers +* that use the "-TAB" algorithm to describe one or more axes. Such +* axes use a FITS binary table to store a look-up table of axis values. +* The FitsChan will fail to read such axes unless the "TabOK" attribute +* is set to a non-zero positive integer value. The table containing the +* axis values must be made available to the FitsChan either by storing +* the table contents in the FitsChan (using +c astPutTables or astPutTable) prior to invoking astRead +f AST_PUTTABLES or AST_PUTTABLE) prior to invoking AST_READ +* or by registering a call-back +c function using astTableSource. +f routine using AST_TABLESOURCE. +* The first method is possibly simpler, but requires that the name of +* the extension containing the table be known in advance. Since the +* table name is embedded in the FITS headers, the name is often not +* known in advance. If a call-back is registered, the FitsChan will +* determine the name of the required table and invoke the call-back +c function +f routine +* to supply the table at the point where it is needed (i.e. within +c the astRead method). +f the AST_READ method). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c tabsource +f TABSOURCE = SUBROUTINE (Given) +c Pointer to the table source function to use. +f The table source routine to use. +* It takes five arguments - the first is a pointer to the +* FitsChan, the second is a string holding the name of the +* FITS extension containing the required binary table ("EXTNAME"), +* the third is the integer FITS "EXTVER" header value for the +* required extension, the fourth is the integer FITS "EXTLEVEL" +* header value for the required extension, and the fifth is +c a pointer to +* the inherited integer status value. +* +* The call-back should read the entire contents (header and data) +* of the binary table in the named extension of the external FITS +* file, storing the contents in a newly created FitsTable object. It +* should then store this FitsTable in the FitsChan using the +c astPutTables or astPutTable +f AST_PUTTABLES or AST_PUTTABLE +* method, and finally annull its local copy of the FitsTable pointer. +* If the table cannot be read for any reason, or if any other +* error occurs, it should return a non-zero integer for the final +* (third) argument. +* +c If "tabsource" is NULL, +f If TABSOURCE is AST_NULL, +* any registered call-back function will be removed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - Application code can pass arbitrary data (such as file +c descriptors, etc) to the table source function using the +c astPutChannelData function. The source function should use +c the astChannelData macro to retrieve this data. +f - The name of the routine supplied for the TABSOURCE +f argument should appear in an EXTERNAL statement in the Fortran +f routine which invokes AST_TABLESOURCE. However, this is not generally +f necessary for the null routine AST_NULL (so long as the AST_PAR +f include file has been used). +f - Note that the null routine AST_NULL (one underscore) is +f different to AST__NULL (two underscores), which is the null Object +f pointer. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Register the supplied source function, using the wrapper function + appropriate for calling C table source functions. */ + astSetTableSource( this, (void (*)( void )) tabsource, TabSourceWrap ); +} + +static AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s, + int **tabaxis, const char *method, + const char *class, int *status ) { + +/* +* Name: +* TabMapping + +* Purpose: +* Create a Mapping that performs any -TAB look-ups for all WCS axes. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s, +* int **tabaxis, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function returns a Mapping that has "nwcs" inputs and outputs, +* where "nwcs" is the number of FITS WCS axes defined in the supplied +* FitsStore. The inputs and outputs are in the same order as the +* CTYPEi keywords in the FitsStore. The forward transformation of the +* Mapping converts positions from the axes defined by the CRVALi keywords +* to the WCS axes. This transformation will be a UnitMap except for +* any axes that are described using the "-TAB" algorithm. For "-TAB" +* axes, the transformation will implement the relevant coordinate +* look-up function. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore structure holding the values to use for +* the WCS keywords. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* tabaxis +* Address of a location at which to store a pointer to an array of +* flags, one for each output of the returned Mapping. A flag will +* be non-zero if the corresponding output of the returned Mapping +* corresponds to a -TAB axis. A NULL pointer is returned if the +* returned Mapping is NULL. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a Mapping. A NULL pointer is returned if the FitsChan does +* not support the -TAB algorithm (i.e. if the value of the TabOK +* attribute is zero or negative), or if no axes use the "-TAB" algorithm. +*/ + +/* Local Variables: */ + AstFitsTable *table; + AstKeyMap *used_tables; + AstMapping *tmap1; + AstMapping *tmap2; + AstMapping *indexmap; + AstMapping *tmap0; + AstMapping *ret; + AstPermMap *pm; + char name[21]; + const char *indexcol; + const char *extname; + const char *cval; + const char *ctype; + const char *coordscol; + double dval; + int *marray; + int *permin; + int *permout; + int extlevel; + int extver; + int iaxis; + int iiaxis; + int ikey; + int interp; + int ival; + int maxis; + int mdim; + int nkey; + int nperm; + int unit; + int wcsaxes; + +/* Initialise */ + ret = NULL; + *tabaxis = NULL; + extname = NULL; + tmap0 = NULL; + tmap2 = NULL; + +/* Check the global status. */ + if( !astOK ) return ret; + +/* Obtain the number of physical axes in the header. If the WCSAXES header + was specified, use it. Otherwise assume it is the same as the number + of pixel axes. */ + dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); + if( dval != AST__BAD ) { + wcsaxes = (int) dval + 0.5; + } else { + wcsaxes = store->naxis; + } + +/* If the FitsChan does not support the -TAB algorithm, return a NULL + pointer. */ + if( astGetTabOK( this ) > 0 ) { + +/* Create a KeyMap to hold a list of the used extension names. */ + used_tables = astKeyMap( " ", status ); + +/* Allocate memory to indicate if each WCS axis is described by a -TAB + algorithm or not. Initialiss it to zero. */ + *tabaxis = astCalloc( wcsaxes, sizeof( int ) ); + +/* Allocate memory to hold the FITS-WCS axis index corresponding to each + input of the "tmap0" Mapping. Indicate that as yet, not values are + stored in this array. Also allocate memory for the inverse of this + permutation array. */ + permout = astMalloc( wcsaxes*sizeof( int ) ); + permin = astMalloc( wcsaxes*sizeof( int ) ); + nperm = 0; + if( astOK ) { + +/* Initialise the permutation arrays. */ + for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) { + permout[ iaxis ] = permin[ iaxis ] = -1; + } + +/* Otherwise, loop round all FITS WCS axis indices present in the FitsStore. */ + for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) { + +/* If the current FITS WCS axis is already included in the returned + Mapping, skip it. This will be the case if the axis uses the same + coordinate array as an earlier axis since all FITS WCS axes associated + with a coordinate array are processed together. */ + if( permin[ iaxis ] == -1 ) { + +/* See if this WCS axis uses the -TAB algorithm. */ + ctype = GetItemC( &(store->ctype), iaxis, 0, s, NULL, method, + class, status ); + if( ctype && strlen(ctype) > 4 && !strncmp( ctype + 4, "-TAB", 4 ) ) { + +/* Get the name of the FITS binary table extension holding the coordinate + info. No default, so report an error if not present. */ + sprintf( name, "PS%d_0%c", iaxis + 1, s ); + extname = GetItemC( &(store->ps), iaxis, 0, s, name, method, + class, status ); + +/* Get the extension version and level. */ + dval = GetItem( &(store->pv), iaxis, 1, s, NULL, method, + class, status ); + extver = ( dval != AST__BAD ) ? (int) dval : 1; + dval = GetItem( &(store->pv), iaxis, 2, s, NULL, method, + class, status ); + extlevel = ( dval != AST__BAD ) ? (int) dval : 1; + +/* Get the FITS binary table. This will invoke any supplied table source + function, and put a copy of the table into the FitsChan structure. + Report an error if the table can not be obtained. */ + table = GetNamedTable( this, extname, extver, extlevel, 1, + method, status ); + +/* Add this extension name to a list of used extensions. */ + astMapPut0I( used_tables, extname, 1, NULL ); + +/* Get the name of the table column containing the main coords array. No + default so report error if not present. Report an error if the column + is not present in the table. */ + sprintf( name, "PS%d_1%c", iaxis + 1, s ); + coordscol = GetItemC( &(store->ps), iaxis, 1, s, name, method, + class, status ); + if( !astHasColumn( table, coordscol ) && astOK ) { + astError( AST__BADTAB, "%s(%s): Unable to find the " + "coordinate array for FITS-WCS axis %d (type %s): " + "column '%s' cannot be found in table '%s'.", status, + method, class, iaxis + 1, ctype, coordscol, extname ); + } + +/* Get the number of dimensions spanned by the coordinate array. Report + an error if the coordinate array has only one axis (FITS-WCS paper III + requires it to have at leats two axes). */ + mdim = astGetColumnNdim( table, coordscol ); + if( mdim == 1 && astOK ) { + astError( AST__BADTAB, "%s(%s): Unable to use the " + "coordinate array for FITS-WCS axis %d (type %s): " + "column '%s' in table '%s' has one axis but at " + "least two are required.", status, method, class, + iaxis + 1, ctype, coordscol, extname ); + } + +/* Allocate memory to hold the FITS-WCS axis corresponding to each dimension + of the coordinate array. Initialise it to hold -1 (i.e. "no matching + FITS-WCS axis yet found") at every element. */ + marray = astMalloc( mdim*sizeof( int ) ); + if( astOK ) { + for( maxis = 0; maxis < mdim; maxis++ ) { + marray[ maxis ] = -1; + } + +/* Loop round each dimension of the coordinate array, storing the index + of the corresponding FITS-WCS axis in "marray". We omit the first axis + (axis 0) since FITS-WCS Paper III defines it is a "conventional" axis + used to enumerate the planes of coordinate values. */ + for( maxis = 1; maxis < mdim && astOK ; maxis++ ) { + +/* Each axis of the coordinate array (except axis 0) must have one, and only + one, corresponding FITS-WCS axis. Check each FITS-WCS axis to find one + that uses the same table and column as the "iaxis" axis, and which + corresponds to axis "maxis" of the coordinate array. */ + for( iiaxis = 0; iiaxis < wcsaxes; iiaxis++ ) { + cval = GetItemC( &(store->ps), iiaxis, 0, s, NULL, + method, class, status ); + if( cval && !strcmp( cval, extname ) ) { + cval= GetItemC( &(store->ps), iiaxis, 1, s, NULL, + method, class, status ); + if( cval && !strcmp( cval, coordscol ) ) { + dval = GetItem( &(store->pv), iiaxis, 3, s, + NULL, method, class, status ); + if( dval != AST__BAD ) { + ival = (int)( dval + 0.5 ); + } else { + ival = 1; + } + if( ival == maxis ) { + +/* Arrive here if the "iiaxis" FITS-WCS axis uses the same table and column + as "iaxis", and corresponds to the "maxis" axis in the coordinate + array. If this is the first matching FITS-WCS axis, store its index. */ + if( marray[ maxis ] == -1 ) { + marray[ maxis ] = iiaxis; + +/* If a matching FITS-WCS axis has already been found, report an error. */ + } else if( astOK ) { + astError( AST__BADTAB, "%s(%s): Unable to use " + "the coordinate array for FITS-WCS " + "axis %d (type %s): more than one " + "intermediate WCS axis is mapped onto " + " dimension %d of the coordinate " + "array in column '%s' of table '%s'.", + status, method, class, iaxis + 1, + ctype, maxis, coordscol, extname ); + } + } + } + } + } + } + +/* Check that every dimension of the coordinate array (except the first) has + a corresponding FITS-WCS axis. */ + for( maxis = 1; maxis < mdim && astOK ; maxis++ ) { + if( marray[ maxis ] == -1 ) { + astError( AST__BADTAB, "%s(%s): Unable to use the " + "coordinate array for FITS-WCS axis %d (type " + "%s): no intermediate WCS axis is mapped onto " + " dimension %d of the coordinate array in column " + " '%s' of table '%s'.", status, method, class, + iaxis + 1, ctype, maxis, coordscol, extname ); + } + } + +/* Now we know which FITS-WCS axis corresponds to each dimension of the + coordinate array. We now need to form a parallel CmpMap (compound Mapping) + by gathering together the indexing vectors for each dimension of the + coordinates array. Each indexing vector is represented by an inverted + 1D LutMap - dimensions that do not have an indexing vector are + represented using a UnitMap. */ + indexmap = NULL; + unit = 1; + for( maxis = 1; maxis < mdim && astOK ; maxis++ ) { + +/* Get the name of the column containing the index array. Defaults is to + use a unit index, so do not report an error if not present. */ + indexcol = GetItemC( &(store->ps), marray[ maxis ], 2, + s, NULL, method, class, status ); + +/* If the table contains an index vector, create a LutMap from it, then + invert it. */ + if( indexcol ) { + tmap1 = MakeColumnMap( table, indexcol, 1, 0, + method, class, status ); + astInvert( tmap1 ); + unit = 0; + +/* If the table does not contain an index vector, use a UnitMap. */ + } else { + tmap1 = (AstMapping *) astUnitMap( 1, " ", status ); + } + +/* Combine the index Mapping for this dimension in parallel with the + Mapping for all earlier dimensions. */ + if( indexmap ) { + tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1, + 0, " ", status ); + indexmap = astAnnul( indexmap ); + tmap1 = astAnnul( tmap1 ); + indexmap = tmap2; + } else { + indexmap = tmap1; + } + } + +/* Get the interpolation method to use for the main coordinate array. + This is an extension to the published -TAB algorithm in which the + QVi_4a keyword is assumed to hold zero for linear interpolation (the + default) and non-zero for nearest neighbour interpolation. The QVi_4a + keyword will be translated to PVi_4a by the SpecTrans function. */ + dval = GetItem( &(store->pv), iaxis, 4, s, + NULL, method, class, status ); + if( dval != AST__BAD ) { + interp = (int)( dval + 0.5 ); + } else { + interp = 0; + } + +/* Make a Mapping from the main coordinate array, and then if required + append it in series to the end of the index Mapping created above. */ + tmap1 = MakeColumnMap( table, coordscol, 0, interp, + method, class, status ); + if( ! unit ) { + tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1, 1, + " ", status ); + } else { + tmap2 = astClone( tmap1 ); + } + indexmap = astAnnul( indexmap ); + tmap1 = astAnnul( tmap1 ); + +/* Extend the array that holds the zero-based FITS-WCS axis index + corresponding to each input of the extended "tmap0" mapping. Also create + the inverse permutation (i.e. zero-based "tmap0" input indexed by + zero-based FITS-WCS axis index). */ + for( maxis = 1; maxis < mdim; maxis++ ) { + permout[ nperm ] = marray[ maxis ]; + permin[ marray[ maxis ] ] = nperm++; + } + +/* Free resources. */ + marray = astFree( marray ); + } + +/* Annul the table pointer. */ + table = astAnnul( table ); + +/* Clear the CTYPE algorithm code to indicate that the axis should be + considered to be linear from now on. This means that the following + functions will create a Mapping from pixel to psi (the system in which + the CRVAL values are defined when using -TAB). The psi axes will then + be mapping into the CS axes using the Mappign returned by this function. */ + strncpy( name, ctype, 4 ); + strcpy( name + 4, ctype + 8 ); + SetItemC( &(store->ctype), iaxis, 0, s, name, status ); + +/* Set the returned flag for this axis. */ + (*tabaxis)[ iaxis ] = 1; + +/* If the FITS WCS axis "iaxis" does not use a -TAB algorithm, describe + it in the returned Mapping using a 1D UnitMap. */ + } else { + tmap2 = (AstMapping *) astUnitMap( 1, " ", status ); + +/* Extend the array that holds the zero-based FITS-WCS axis index + corresponding to each input of the extended "tmap0" mapping. Also create + the inverse permutation (i.e. zero-based "tmap0" input indexed by + zero-based FITS-WCS axis index). */ + permout[ nperm ] = iaxis; + permin[ iaxis ] = nperm++; + } + +/* Append the Mapping describing the FITS WCS axis "iaxis" in parallel to any + Mappings created for earlier "iaxis" axes. */ + if( tmap0 ) { + tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 0, " ", status ); + tmap0 = astAnnul( tmap0 ); + tmap2 = astAnnul( tmap2 ); + tmap0 = tmap1; + } else { + tmap0 = tmap2; + } + } + } + +/* If no -TAB axes were found, just return a NULL pointer. */ + if( extname && astOK ) { + +/* Do a sanity check on the permutation arrays. */ + for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) { + if( permin[ iaxis ] < 0 || permin[ iaxis ] >= wcsaxes || + permout[ permin[ iaxis ] ] != iaxis ) { + astError( AST__INTER, "%s(%s): Invalid permutation " + "arrays in function TabMapping (internal AST " + "progranmming error).", status, method, class ); + break; + } + } + +/* Sandwich the "tmap0" Mapping in series between two PermMaps to create a + Mapping in which the inputs and outputs correspond to FITS WCS axis + numbering. */ + pm = astPermMap( wcsaxes, permin, wcsaxes, permout, NULL, " ", + status ); + tmap1 = (AstMapping *) astCmpMap( pm, tmap0, 1, " ", status ); + astInvert( pm ); + tmap2 = (AstMapping *) astCmpMap( tmap1, pm, 1, " ", status ); + pm = astAnnul( pm ); + tmap1 = astAnnul( tmap1 ); + +/* Simplify the returned Mapping. */ + ret = astSimplify( tmap2 ); + tmap2 = astAnnul( tmap2 ); + } + +/* Free remaining resources */ + tmap0 = astAnnul( tmap0 ); + } + permout = astFree( permout ); + permin = astFree( permin ); + +/* Remove all used tables from the FitsChan now that they have been used. */ + nkey = astMapSize( used_tables ); + for( ikey = 0; ikey < nkey; ikey++ ) { + astRemoveTables( this, astMapKey( used_tables, ikey ) ); + } + +/* Delete the KeyMap holding the used table names. */ + used_tables = astAnnul( used_tables ); + +/* If we are not returning a Mapping, ensure we do not return any axis + flags either. */ + if( !ret ) *tabaxis = astFree( *tabaxis ); + } + +/* Return the result */ + return ret; +} +static void TabSourceWrap( void (*tabsource)( void ), + AstFitsChan *this, const char *extname, + int extver, int extlevel, int *status ){ + +/* +* Name: +* TabSourceWrap + +* Purpose: +* Wrapper function to invoke the C table source function. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void TabSourceWrap( void (*tabsource)( void ), +* AstFitsChan *this, const char *extname, +* int extver, int extlevel, int *status ) + +* Class Membership: +* Channel member function. + +* Description: +* This function invokes the table source function whose pointer is +* supplied in order to read a named FITS binary table from an external +* FITS file. + +* Parameters: +* tabsource +* Pointer to the C tab source function. +* this +* Pointer to the FitsChan. The reference count for the FitsChan is +* decremented by this function (this behaviour is imposed by +* restrictions in the equivalent Fortran wrapper function). +* extname +* Pointer to the string holding the name of the FITS extension +* from which a table is to be read. +* extver +* The integer "EXTVER" value for the required extension. +* extlevel +* The integer "EXTLEVEL" value for the required extension. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFitsChan *this_id; + int lstat; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get an external identifier for the FitsChan. Could use astClone here + to avoid this function anulling the supplied pointer, but the F77 wrapper + cannot use the protected version of astClone, so for consistency we do + not use it here either. */ + this_id = astMakeId( this ); + +/* Invoke the table source function (casting it to the C API first) to + read the table, and store it in the FitsChan. */ + ( *( void (*)( struct AstFitsChan *, const char *, int, int, int * ) )tabsource )( this_id, extname, extver, extlevel, &lstat ); + +/* Free the FitsChan identifier (this annuls the supplied "this" pointer). */ + this_id = astAnnulId( this_id ); + +/* Report an error if the source function failed. */ + if( !lstat ) { + astError( AST__NOTAB, "astRead(%s): The table source function failed to read " + "a binary table from extension %s in an external FITS file.", + status, astGetClass( this ), extname ); + } +} + +static double TDBConv( double mjd, int timescale, int fromTDB, + const char *method, const char *class, int *status ){ +/* +* Name: +* TDBConv + +* Purpose: +* Convert an MJD between the TDB time scale and another timescale. + +* Type: +* Private function. + +* Synopsis: +* double TDBConv( double mjd, int timescale, int fromTDB, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function converts the supplied mjd value to or from the TDB +* timescale. + +* Parameters: +* mjd +* The input MJD value. +* timescale +* The other timescale. +* fromTDB +* Indicates the direction of the required conversion. If non-zero, +* the supplied "mjd" value should be in the TDB timescale, and the +* returned value will be in the timescale specified by "timescale". +* If zero, the supplied "mjd" value should be in the timescale +* specified by "timescale", and the returned value will be in the +* TDB timescale. +* method +* The calling method. Used only in error messages. +* class +* The object class. Used only in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The converted MJD value, or AST__BAD if an error occurs. +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* Mapping from supplied timescale to TDB */ + double ret; /* The returned value */ + +/* Initialise */ + ret = AST__BAD; + +/* Check inherited status and supplied TDB value. */ + if( !astOK || mjd == AST__BAD ) return ret; + +/* Return the supplied value if no conversion is needed. */ + if( timescale == AST__TDB ) { + ret = mjd; + +/* Otherwise, do the conversion. */ + } else { + +/* Lock the timeframes for use by the current thread, waiting if they are + currently locked by another thread. */ + astManageLock( timeframe, AST__LOCK, 1, NULL ); + astManageLock( tdbframe, AST__LOCK, 1, NULL ); + +/* Set the required timescale. */ + astSetTimeScale( timeframe, timescale ); + +/* Get the Mapping between the two timescales, and use it to convert the + suipplied value. */ + fs = astConvert( tdbframe, timeframe, "" ); + astTran1( fs, 1, &mjd, fromTDB, &ret ); + fs = astAnnul( fs ); + +/* Unlock the timeframes. */ + astManageLock( timeframe, AST__UNLOCK, 1, NULL ); + astManageLock( tdbframe, AST__UNLOCK, 1, NULL ); + } + +/* Return the result */ + return ret; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the astTestAttrib protected +* method inherited from the Channel class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a FitsChan's attributes. + +* Parameters: +* this +* Pointer to the FitsChan. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_object; + +/* Card. */ +/* ----- */ + if ( !strcmp( attrib, "card" ) ) { + result = astTestCard( this ); + +/* Encoding. */ +/* --------- */ + } else if ( !strcmp( attrib, "encoding" ) ) { + result = astTestEncoding( this ); + +/* FitsAxisOrder. */ +/* -------------- */ + } else if ( !strcmp( attrib, "fitsaxisorder" ) ) { + result = astTestFitsAxisOrder( this ); + +/* FitsDigits. */ +/* ----------- */ + } else if ( !strcmp( attrib, "fitsdigits" ) ) { + result = astTestFitsDigits( this ); + +/* DefB1950. */ +/* --------- */ + } else if ( !strcmp( attrib, "defb1950" ) ) { + result = astTestDefB1950( this ); + +/* TabOK. */ +/* ------ */ + } else if ( !strcmp( attrib, "tabok" ) ) { + result = astTestTabOK( this ); + +/* CDMatrix. */ +/* --------- */ + } else if ( !strcmp( attrib, "cdmatrix" ) ) { + result = astTestCDMatrix( this ); + +/* CarLin. */ +/* --------- */ + } else if ( !strcmp( attrib, "carlin" ) ) { + result = astTestCarLin( this ); + +/* PolyTan */ +/* ------- */ + } else if ( !strcmp( attrib, "polytan" ) ) { + result = astTestPolyTan( this ); + +/* Iwc. */ +/* ---- */ + } else if ( !strcmp( attrib, "iwc" ) ) { + result = astTestIwc( this ); + +/* Clean. */ +/* ------ */ + } else if ( !strcmp( attrib, "clean" ) ) { + result = astTestClean( this ); + +/* Warnings. */ +/* -------- */ + } else if ( !strcmp( attrib, "warnings" ) ) { + result = astTestWarnings( this ); + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + } else if ( !strcmp( attrib, "ncard" ) || + !strcmp( attrib, "nkey" ) || + !strcmp( attrib, "cardtype" ) || + !strcmp( attrib, "cardcomm" ) || + !strcmp( attrib, "cardname" ) || + !strcmp( attrib, "allwarnings" ) ){ + result = 0; + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int TestCard( AstFitsChan *this, int *status ){ + +/* +*+ +* Name: +* astTestCard + +* Purpose: +* Test the Card attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fitschan.h" +* int astTestCard( AstFitsChan *this ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function tests the Card attribute for the supplied FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. + +* Returned Value: +* If the Card attribute has its "cleared" value (i.e. if the first card +* in the FitsChan will be the next one to be read), then zero is returned, +* otherwise 1 is returned. +*- +*/ + +/* Local Variables: */ + int card; /* The original value of Card */ + int ret; /* The returned flag */ + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Get the current value of Card. */ + card = astGetCard( this ); + +/* Temporarily clear Card. */ + astClearCard( this ); + +/* See if the original Card is equal to the cleared card, and set the + returned flag appropriately. Re-instate the original value of card is + required.*/ + if( astGetCard( this ) == card ) { + ret = 0; + } else { + astSetCard( this, card ); + ret = 1; + } + +/* Return the flag. */ + return ret; +} + +static int TestFits( AstFitsChan *this, const char *name, int *there, + int *status ){ + +/* +*++ +* Name: +c astTestFits +f AST_TESTFITS + +* Purpose: +* See if a named keyword has a defined value in a FitsChan. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" + +c int astTestFits( AstFitsChan *this, const char *name, int *there ) +f RESULT = AST_TESTFITS( THIS, NAME, THERE, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +* This function serches for a named keyword in a FitsChan. If found, +* and if the keyword has a value associated with it, a +c non-zero +f .TRUE. +* value is returned. If the keyword is not found, or if it does not +* have an associated value, a +c zero +f .FALSE. +* value is returned. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +c name +f NAME = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string +f A character string +* containing the FITS keyword name. This may be a complete FITS +* header card, in which case the keyword to use is extracted from +* it. No more than 80 characters are read from this string. +c there +f THERE = LOGICAL (Returned) +c Pointer to an integer which will be returned holding a non-zero +c value if the keyword was found in the header, and zero otherwise. +f A value of .TRUE. will be returned if the keyword was found in the +f header, and .FALSE. otherwise. +* This parameter allows a distinction to be made between the case +* where a keyword is not present, and the case where a keyword is +* present but has no associated value. +c A NULL pointer may be supplied if this information is not +c required. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTestFits() +f AST_TESTFITS = LOGICAL +* A value of zero +f .FALSE. +* is returned if the keyword was not found in the FitsChan or has +* no associated value. Otherwise, a value of +c one +f .TRUE. +* is returned. + +* Notes: +* - The current card is left unchanged by this function. +* - The card following the current card is checked first. If this is +* not the required card, then the rest of the FitsChan is searched, +* starting with the first card added to the FitsChan. Therefore cards +* should be accessed in the order they are stored in the FitsChan (if +* possible) as this will minimise the time spent searching for cards. +* - An error will be reported if the keyword name does not conform +* to FITS requirements. +c - Zero +f - .FALSE. +* is returned as the function value if an error has already occurred, +* or if this function should fail for any reason. +*-- +*/ + +/* Local Variables: */ + const char *class; /* Object class */ + const char *method; /* Calling method */ + char *lcom; /* Supplied keyword comment */ + char *lname; /* Supplied keyword name */ + char *lvalue; /* Supplied keyword value */ + int icard; /* Current card index on entry */ + int ret; /* The returned value */ + +/* Initialise */ + if( there ) *there = 0; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the calling method and object class. */ + method = "astTestFits"; + class = astGetClass( this ); + +/* Initialise the returned value. */ + ret = 0; + +/* Extract the keyword name from the supplied string. */ + (void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); + +/* Store the current card index. */ + icard = astGetCard( this ); + +/* Attempt to find a card in the FitsChan refering to this keyword, + and make it the current card. Only proceed if a card was found. */ + if( SearchCard( this, lname, method, class, status ) ){ + +/* Indicate the card has been found. */ + if( there ) *there = 1; + +/* If the cards data type is no undefined, return 1. */ + if( CardType( this, status ) != AST__UNDEF ) ret = 1; + } + +/* Re-instate the original current card index. */ + astSetCard( this, icard ); + +/* Release the memory used to hold keyword name, value and comment strings. */ + lname = (char *) astFree( (void *) lname ); + lvalue = (char *) astFree( (void *) lvalue ); + lcom = (char *) astFree( (void *) lcom ); + +/* Return the answer. */ + return ret; +} + +static void TidyOffsets( AstFrameSet *fset, int *status ) { +/* +* Name: +* TidyOffsets + +* Purpose: +* Remove un-needed offset coordinate Frames. + +* Type: +* Private function. + +* Synopsis: +* void TidyOffsets( AstFrameSet *fset, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FITS header stores offset sky coordinates as two alternaive axis +* descriptions - one giving the offset axes and one giving the absolute +* axes. But AST can hold both forms in a single SkyFrame. This function +* removes the FITS Frames describing offset axes from the FrameSet. +* The remaining absolute Frame is then used to describe both absolute +* and offset. + +* Parameters: +* fset +* A FrameSet holding the Frames read from a FITS-WCS Header. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *frm; + AstFrame *pfrm; + const char *dom; + const char *skyrefis; + int hasabs; + int hasoff; + int iax; + int icurr; + int icurr_is_offset; + int ifrm; + int nax; + int nfrm; + int pax; + int remove; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Note the original current Frame index. */ + icurr = astGetCurrent( fset ); + +/* Assume the current Frame is not an offset frame until proven + otherwise. */ + icurr_is_offset = 0; + +/* Does the FrameSet contain any Frames holding sky offsets? Such Frames + should have been given a Domain of SKY_OFFSETS within function + WcsSkyFrame. Loop round all Frames, checking each one. Also note if + the FrameSet contains any (absolute) SKY frames. Also set the SkyRefIs + attribute for any absolute SkyFrames that were marked with domains + SKY_POLE or SKY_OFFSET in WcsSkyFrame. */ + hasabs = 0; + hasoff = 0; + nfrm = astGetNframe( fset ); + for( ifrm = 1; ifrm <= nfrm; ifrm++ ){ + skyrefis = NULL; + frm = astGetFrame( fset, ifrm ); + nax = astGetNaxes( frm ); + for( iax = 0; iax < nax; iax++ ) { + astPrimaryFrame( frm, iax, &pfrm, &pax ); + if( IsASkyFrame( pfrm ) ) { + dom = astGetDomain( pfrm ); + if( dom ) { + if( !strcmp( dom, "SKY_OFFSETS" ) ){ + hasoff = 1; + if( ifrm == icurr ) icurr_is_offset = 1; + iax = nax; + } else if( !strcmp( dom, "SKY" ) ){ + hasabs = 1; + iax = nax; + } else if( !strcmp( dom, "SKY_POLE" ) ){ + hasabs = 1; + skyrefis = "POLE"; + iax = nax; + } else if( !strcmp( dom, "SKY_ORIGIN" ) ){ + hasabs = 1; + skyrefis = "ORIGIN"; + iax = nax; + } + } + } + pfrm = astAnnul( pfrm ); + } + frm = astAnnul( frm ); + + if( skyrefis ) { + astSetI( fset, "Current", ifrm); + astSetC( fset, "SkyRefIs", skyrefis ); + astSetI( fset, "Current", icurr ); + } + } + +/* If one or more absolute sky frames were found, then remove any offset + sky frames. Clear the Ident attribute (that holds the FITS-WCS alternate + axis description character) for any absoute Frames. */ + if( hasabs && hasoff ) { + + for( ifrm = nfrm; ifrm > 0; ifrm-- ) { + remove = 0; + frm = astGetFrame( fset, ifrm ); + nax = astGetNaxes( frm ); + for( iax = 0; iax < nax; iax++ ) { + astPrimaryFrame( frm, iax, &pfrm, &pax ); + if( IsASkyFrame( pfrm ) ) { + dom = astGetDomain( pfrm ); + if( dom ) { + if( !strcmp( dom, "SKY_OFFSETS" ) ){ + remove = 1; + iax = nax; + + } else if( !strcmp( dom, "SKY_POLE" ) || + !strcmp( dom, "SKY_ORIGIN" ) ){ + astClearIdent( frm ); + astClearDomain( pfrm ); + +/* If we will be deleting the original current Frame (because it is an + offset Frame), then mark the first absolute Frame as the new current + Frame. */ + if( icurr_is_offset ) { + astSetCurrent( fset, ifrm ); + icurr_is_offset = 0; + } + iax = nax; + } + } + } + pfrm = astAnnul( pfrm ); + } + frm = astAnnul( frm ); + + if( remove ) astRemoveFrame( fset, ifrm ); + } + } +} + +static AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys, + const char *method, const char *class, int *status ){ +/* +* Name: +* TimeSysToAst + +* Purpose: +* Convert a FITS TIMESYS value to an AST TimeFrame timescale value. + +* Type: +* Private function. + +* Synopsis: +* AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function returns the value used by the AST TimeFrame class to +* represent the timescale specified by the "timesys" parameter, which +* should hold the value of a FITS TIMESYS keyword. The TIMESYS +* convention was introduced as part of the Y2K DATE-OBS changes, and +* is not currently part of the published FITS-WCS conventions. +* +* If the requested timescale is not supported by AST, then a warning is +* added to the FitsChan and a value of AST__UTC is returned (but no +* error is reported). + +* Parameters: +* this +* Pointer to the FitsChan. +* timesys +* Pointer to the string holding the TIMESYS value. A NULL pointer +* returns the default timescale of UTC. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The equivalent AstTimeScaleType value. +*/ + +/* Local Variables: */ + AstTimeScaleType result; /* The returned timescale */ + char buf[ 200 ]; /* Buffer for warning message */ + +/* Initialise */ + result = AST__UTC; + +/* Check the inherited status. */ + if( !astOK ) return result; + if( !timesys ) { + result = AST__UTC; + } else if( !strcmp( timesys, "UTC" ) ) { + result = AST__UTC; + } else if( !strcmp( timesys, "UT" ) ) { + result = AST__UTC; + Warn( this, "badval", "The original FITS header contained a value of UT " + "for keyword TIMESYS which is being interpreted as UTC.", method, + class, status ); + } else if( !strcmp( timesys, "TAI" ) ) { + result = AST__TAI; + } else if( !strcmp( timesys, "IAT" ) ) { + result = AST__TAI; + } else if( !strcmp( timesys, "ET" ) ) { + result = AST__TT; + Warn( this, "badval", "The original FITS header contained a value of ET " + "for keyword TIMESYS. TT will be used instead.", method, class, status ); + } else if( !strcmp( timesys, "TT" ) ) { + result = AST__TT; + } else if( !strcmp( timesys, "TDT" ) ) { + result = AST__TT; + } else if( !strcmp( timesys, "TDB" ) ) { + result = AST__TDB; + } else if( !strcmp( timesys, "TCG" ) ) { + result = AST__TCG; + } else if( !strcmp( timesys, "TCB" ) ) { + result = AST__TCB; + } else { + result = AST__UTC; + sprintf( buf, "The original FITS header contained a value of %s for " + "keyword TIMESYS. AST does not support this timescale so " + "UTC will be used instead.", timesys ); + Warn( this, "badval", buf, method, class, status ); + } + +/* Return the result */ + return result; +} + +static char *UnPreQuote( const char *string, int *status ) { +/* +* Name: +* UnPreQuote + +* Purpose: +* Reverse the pre-quoting of FITS character data. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* char *UnPreQuote( const char *string, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function reverses the effect of the PreQuote function on a +* string (apart from any loss of data due to truncation). It +* should be used to recover the original character data from the +* pre-quoted version of a string retrieved from a FITS character +* value associated with a keyword. + +* Parameters: +* string +* Pointer to a constant null-terminated string containing the +* pre-quoted character data. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a dynamically allocated null-terminated string +* containing the un-quoted character data. The memory holding this +* string should be freed by the caller (using astFree) when no +* longer required. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked wth the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + char *result; /* Pointer value to return */ + int i1; /* Offset of first useful character */ + int i2; /* Offest of last useful character */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise to use the first and last characters in the input + string. */ + i1 = 0; + i2 = strlen( string ) - 1; + +/* If the string contains at least 2 characters, check if the first + and last characters are double quotes ("). If so, adjust the + offsets to exclude them. */ + if ( ( i2 > i1 ) && + ( string[ i1 ] == '"' ) && ( string[ i2 ] == '"' ) ) { + i1++; + i2--; + } + +/* Make a dynamically allocated copy of the useful part of the + string. */ + result = astString( string + i1, i2 - i1 + 1 ); + +/* Return the answer. */ + return result; +} + +static int Use( AstFitsChan *this, int set, int helpful, int *status ) { + +/* +* Name: +* Use + +* Purpose: +* Decide whether to write a value to a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int Use( AstFitsChan *this, int set, int helpful, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* This function decides whether a value supplied by a class "Dump" +* function, via a call to one of the astWrite... protected +* methods, should actually be written to a FitsChan. +* +* This decision is based on the settings of the "set" and +* "helpful" flags supplied to the astWrite... method, plus the +* attribute settings of the FitsChan. + +* Parameters: +* this +* A pointer to the FitsChan. +* set +* The "set" flag supplied. +* helpful +* The "helpful" value supplied. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the value should be written out, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int full; /* Full attribute value */ + int result; /* Result value to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If "set" is non-zero, then so is the result ("set" values must + always be written out). */ + result = ( set != 0 ); + +/* Otherwise, obtain the value of the FitsChan's Full attribute. */ + if ( !set ) { + full = astGetFull( this ); + +/* If Full is positive, display all values, if zero, display only + "helpful" values, if negative, display no (un-"set") values. */ + if ( astOK ) result = ( ( helpful && ( full > -1 ) ) || ( full > 0 ) ); + } + +/* Return the result. */ + return result; +} + +static int Ustrcmp( const char *a, const char *b, int *status ){ +/* +* Name: +* Ustrcmp + +* Purpose: +* A case blind version of strcmp. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int Ustrcmp( const char *a, const char *b, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns 0 if there are no differences between the two strings, and 1 +* otherwise. Comparisons are case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strcmp" does. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Loop round each character. */ + while( 1 ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + } + } + +/* Return the result. */ + return ret; +} + +static int Ustrncmp( const char *a, const char *b, size_t n, int *status ){ +/* +* Name: +* Ustrncmp + +* Purpose: +* A case blind version of strncmp. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int Ustrncmp( const char *a, const char *b, size_t n, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* Returns 0 if there are no differences between the first "n" +* characters of the two strings, and 1 otherwise. Comparisons are +* case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. +* n +* The maximum number of characters to compare. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strncmp" does. +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int i; /* Character index */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Compare up to "n" characters. */ + for( i = 0; i < (int) n; i++ ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + } + } + +/* Return the result. */ + return ret; +} + +static void Warn( AstFitsChan *this, const char *condition, const char *text, + const char*method, const char *class, int *status ){ +/* +* Name: +* Warn + +* Purpose: +* Store warning cards in a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int Warn( AstFitsChan *this, const char *condition, const char *text, +* const char*method, const char *class, int *status ); + +* Class Membership: +* FitsChan member function. + +* Description: +* If the Warnings attribute indicates that occurences of the specified +* condition should be reported, the supplied text is split into lines +* and stored in the FitsChan as a series of ASTWARN cards, in front +* of the current card. If the specified condition is not being reported, +* this function returns without action. + +* Parameters: +* this +* The FitsChan. If NULL, this function returns without action. +* condition +* Pointer to a string holding a lower case condition name. +* text +* Pointer to a string holding the text of the warning. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + char buff[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for new card text */ + const char *a; /* Pointer to 1st character in next card */ + const char *b; /* Pointer to terminating null character */ + const char *c; /* Pointer to last character in next card */ + int exists; /* Has the supplied warning already been issued? */ + int icard; /* Index of original card */ + int nc; /* No. of characters in next card */ + +/* Check the inherited status, warning text, FitsChan and Clean attribute. */ + if( !astOK || !text || !text[0] || !this || astGetClean( this ) ) return; + +/* Ignore the warning if the supplied condition is not contained within + the list of conditions to be reported in this way (given by the + Warnings attribute). */ + if( FullForm( astGetWarnings( this ), condition, 0, status ) >= 0 ){ + +/* If found, store the warning in the parent Channel structure. */ + astAddWarning( this, 1, "%s", method, status, text ); + +/* For historical reasons, warnings are also stored in the FitsChan as a + set of FITS cards... First save the current card index, and rewind the + FitsChan. */ + icard = astGetCard( this ); + astClearCard( this ); + +/* Break the supplied text into lines and check the FitsChan to see if + a block of adjacent ASTWARN cards with these lines already exist + within the FitsChan. Assume they do until proven otherwise. */ + exists = 1; + a = text; + b = a + strlen( text ); + while( a < b ){ + +/* Each card contains about 60 characters of the text. Get a pointer to + the nominal last character in the next card. */ + c = a + 60; + +/* If this puts the last character beyond the end of the text, use the + last character before the null as the last character in the card. */ + if( c >= b ) { + c = b - 1; + +/* Otherwise, if the last character is not a space, move the last + character backwards to the first space. This avoids breaking words + across cards. */ + } else { + while( !isspace( *c ) && c > a ) c--; + } + +/* Copy the text into a null terminated buffer. */ + nc = c - a + 1; + strncpy( buff, a, nc ); + buff[ nc ] = 0; + +/* If this is the first line, search the entire FitsChan for an ASTWARN card + with this text. If not, indiate that the supplied text needs to be + stored in the FitsChan, and break out of the loop. */ + if( a == text ) { + exists = 0; + while( !exists && + FindKeyCard( this, "ASTWARN", method, class, status ) ) { + if( !strcmp( (const char *) CardData( this, NULL, status ), buff ) ) { + exists = 1; + } + MoveCard( this, 1, method, class, status ); + } + if( !exists ) break; + +/* If this is not the first line, see if the next card in the FitsChan is + an ASTWARN card with this text. If not, indiate that the supplied text + needs to be stored in the FitsChan, and break out of the loop. */ + } else { + if( !strcmp( CardName( this, status ), "ASTWARN" ) && + !strcmp( (const char *) CardData( this, NULL, status ), buff ) ) { + MoveCard( this, 1, method, class, status ); + } else { + exists = 0; + break; + } + } + +/* Set the start of the next bit of the text. */ + a = c + 1; + } + +/* Reinstate the original current card index. */ + astSetCard( this, icard ); + +/* We only add new cards to the FitsChan if they do not already exist. */ + if( !exists ) { + +/* Break the text into lines using the same algorithm as above, and store + each line as a new ASTWARN card. Start with a blank ASTWARN card. */ + astSetFitsS( this, "ASTWARN", " ", NULL, 0 ); + +/* Loop until the entire text has been written out. */ + a = text; + b = a + strlen( text ); + while( a < b ){ + +/* Each card contains about 60 characters of the text. Get a pointer to + the nominal last character in the next card. */ + c = a + 60; + +/* If this puts the last character beyond the end of the text, use the + last character before the null as the last character in the card. */ + if( c >= b ) { + c = b - 1; + +/* Otherwise, if the last character is not a space, move the last + character backwards to the first space. This avoids breaking words + across cards. */ + } else { + while( !isspace( *c ) && c > a ) c--; + } + +/* Copy the text into a null terminated buffer. */ + nc = c - a + 1; + strncpy( buff, a, nc ); + buff[ nc ] = 0; + +/* Store the buffer as the next card. */ + astSetFitsS( this, "ASTWARN", buff, NULL, 0 ); + +/* Set the start of the next bit of the text. */ + a = c + 1; + } + +/* Include a final blank card. */ + astSetFitsS( this, "ASTWARN", " ", NULL, 0 ); + } + } +} + +static int WATCoeffs( const char *watstr, int iaxis, double **cvals, + int **mvals, int *ok, int *status ){ +/* +* Name: +* WATCoeffs + +* Purpose: +* Get the polynomial coefficients from the lngcor or latcor component +* of an IRAF WAT string. + +* Type: +* Private function. + +* Synopsis: +* int WATCoeffs( const char *watstr, int iaxis, double **cvals, +* int **mvals, int *ok, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function extracts the polynomial coefficients from a supplied +* string containing the concatenated values of a set of IRAF "WAT" +* keywords, such as used for the IRAF-specific TNX and ZPX projections. +* The coefficients are returned in the form of a set of PVi_m values +* for a TPN projection. + +* Parameters: +* watstr +* The concatentated WAT keyword values. +* iaxis +* Zero based index of the axis to which the WAT keywords refer (0 +* or 1). +* cvals +* Location at which to return a pointer to a dynamically allocated +* list of coefficient values, or NULL if no lngcor/latcor values +* were found in the WAT string. Free using astFree. +* mvals +* Location at which to return a pointer to a dynamically allocated +* list of coefficient indices, or NULL if no lngcor/latcor values +* were found in the WAT string. Free using astFree. +* ok +* Pointer to an in which is returned set to zero if the polynomial +* in the supplied WAT string cannot be represented using TPN form. +* Non-zero otherwise. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The size of the returned cvals and mvals arrays. + +*/ + +/* Local Variables: */ + char **w1; + char **w2; + double *coeff; + double *pc; + int result; + double dval; + double etamax; + double etamin; + double ximax; + double ximin; + int cheb; + int etaorder; + int iword; + int m; + int mn; + int nword; + int order; + int porder; + int xiorder; + int ires; + +/* The number of lngcor/latcor values needed for each order. */ + static const int nab[] = {1,3,6,10,15,21,28,36}; + +/* Initialise the pointer to the returned Mapping. */ + result = 0; + *mvals = NULL; + *cvals = NULL; + *ok = 1; + +/* Other initialisation to avoid compiler warnings. */ + etamin = 0.0; + etamax = 0.0; + ximax = 0.0; + ximin = 0.0; + order = 0; + +/* Check the global status. */ + if ( !astOK || !watstr ) return result; + +/* Look for cor = "..." and extract the "..." string. */ + w1 = astChrSplitRE( watstr, "cor *= *\"(.*)\"", &nword, NULL ); + if( w1 ) { + +/* Split the "..." string into words. */ + w2 = astChrSplit( w1[ 0 ], &nword ); + if( w2 ) { + +/* Initialise flags. */ + cheb = 0; + xiorder = 0; + etaorder = 0; + coeff = NULL; + porder = -1; + +/* Loop round each word. Break early if we find that the projection + cannot be represented as a TPN projection. */ + for( iword = 0; iword < nword && *ok; iword++ ) { + +/* Convert the word to double. */ + dval = astChr2Double( w2[ iword ] ); + if( dval == AST__BAD ) { + astError( AST__BDFTS, "astRead(FitsChan): Failed to read a " + "numerical value from sub-string \"%s\" found in " + "an IRAF \"WAT...\" keyword.", status, w2[ iword ] ); + break; + } + +/* The first value gives the correction surface type. We can only handle type + 1 (chebyshev) or 3 (simple polynomial). */ + if( iword == 0 ){ + if( dval == 1.0 ) { + cheb = 1; + } else if( dval == 2.0 ) { + *ok = 0; + } + +/* The second and third numbers gives the orders of the polynomial in X + and Y. We can only handle cases in which the orders are the same on + both axes, and greater than 0 and less than 8. Store a pointer to the + first TAN projection parameter index to use. */ + } else if( iword == 1 ){ + order = dval; + porder = order - 1; + + } else if( iword == 2 ){ + if( dval - 1 != porder || dval < 0 || dval > 7 ) *ok = 0; + +/* The fourth number defines the type of cross-terms. We can only handle + type 2 (half-cross terms). */ + } else if( iword == 3 ){ + if( dval != 2.0 ) *ok = 0; + +/* We now know the maximum number of co-efficients that may be needed. + Allocate memory to hold them, and fill it with zeros. They are + stored in this array as if full cross-terms have been supplied (the + unspecified coefficients retain their initialised value of zero). */ + coeff = astCalloc( order*order, sizeof( double ) ); + if( !astOK ) break; + +/* The next 4 numbers describe the region of validity of the fits in IRAF's + xi and eta space, e.g. ximin, ximax, etamin, etamax. We only uses + these if we have a chebyshev polynomial. */ + } else if( iword == 4 ) { + ximin = dval; + + } else if( iword == 5 ) { + ximax = dval; + + } else if( iword == 6 ) { + etamin = dval; + + } else if( iword == 7 ) { + etamax = dval; + +/* The remaining terms are the coefficients of the polynomial terms. */ + } else if( iword > 7 ){ + +/* Store the coefficient in the array. They are stored so that power of + xi increases fastest. */ + coeff[ xiorder + order*etaorder ] = dval; + +/* Increment the powers of the next coefficient. We know we only have half + cross-terms, so the maximum power of xi decreases from order to zero + as we move through the list of coefficients. */ + if( ++xiorder == order - etaorder ) { + xiorder = 0; + etaorder++; + } + } + } + +/* Check that all the required co-efficients were found */ + if( porder == -1 || nword != 8 + nab[ porder ] ) *ok = 0; + +/* If we can handle the projection, proceed. */ + if( *ok && astOK ) { + +/* If the coefficients were supplied in chebyshev form, convert to simple + form. */ + if( cheb ) { + double *tcoeff = coeff; + coeff = Cheb2Poly( tcoeff, order, order, ximin, + ximax, etamin, etamax, status ); + tcoeff = astFree( tcoeff ); + } + +/* The polynomials provide a "correction* to be added to the supplied X and + Y values. Therefore increase the linear co-efficients by 1 on the axis + that is being calculated. */ + coeff[ iaxis ? order : 1 ] += 1.0; + +/* Loop round all coefficients, keeping track of the power of xi and eta + for the current coefficient. */ + pc = coeff; + for( etaorder = 0; etaorder < order; etaorder++ ) { + for( xiorder = 0; xiorder < order; xiorder++,pc++ ) { + +/* Skip coefficients that have their default values (zero, except for the + linear coefficients which default to 1.0). */ + mn = xiorder + etaorder; + if( *pc != ( mn == 1 ? 1.0 : 0.0 ) ) { + +/* Find the "m" index of the PVi_m FITS keyword for the current + coefficient. */ + m = mn*( 1 + mn )/2 + mn/2; + m += iaxis ? xiorder : etaorder; + +/* Append the PV and m values to the ends of the returned arrays. */ + ires = result++; + *cvals = astGrow( *cvals, sizeof( double ), result ); + *mvals = astGrow( *mvals, sizeof( int ), result ); + if( astOK ) { + (*cvals)[ ires ] = *pc; + (*mvals)[ ires ] = m; + } + } + } + } + +/* Free coefficients arrays */ + coeff = astFree( coeff ); + } + +/* Free resources */ + w2 = astFree( w2 ); + } + w1 = astFree( w1 ); + } + +/* Return the result. */ + return result; +} + +static AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes, + const char *method, const char *class, int *status ){ +/* +* Name: +* WcsCDeltMatrix + +* Purpose: +* Create a MatrixMap representing the CDELT scaling. + +* Type: +* Private function. + +* Synopsis: +* AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A diagonal MatrixMap representing the FITS "CDELT" keywords is +* returned. + +* Parameters: +* store +* A structure containing values for FITS keywords relating to +* the World Coordinate System. +* s +* A character s identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* naxes +* The number of intermediate world coordinate axes (WCSAXES). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the created MatrixMap or a NULL pointer if an +* error occurred. +*/ + +/* Local Variables: */ + AstMatrixMap *new; /* The created MatrixMap */ + double *el; /* Pointer to next matrix element */ + double *mat; /* Pointer to matrix array */ + int i; /* Pixel axis index */ + +/* Initialise/ */ + new = NULL; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Allocate memory for the diagonal matrix elements. */ + mat = (double *) astMalloc( sizeof(double)*naxes ); + if( astOK ){ + +/* Fill the matrix diagonal with values from the FitsStore. */ + el = mat; + for( i = 0; i < naxes; i++ ){ + +/* Get the CDELTi value for this axis. Missing terms can be defaulted so + do not report an error if the required value is not present in the + FitsStore. */ + *el = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + +/* Missing terms default to to 1.0. */ + if( *el == AST__BAD ) *el = 1.0; + +/* Move on to the next matrix element. */ + el++; + } + +/* Create the diagional matrix. */ + new = astMatrixMap( naxes, naxes, 1, mat, "", status ); + +/* Report an error if the inverse transformation is undefined. */ + if( !astGetTranInverse( new ) && astOK ) { + astError( AST__BDFTS, "%s(%s): Unusable CDELT values found " + "in the FITS-WCS header - one or more values are zero.", status, method, class ); + } + +/* Release the memory used to hold the matrix. */ + mat = (double *) astFree( (void *) mat ); + } + +/* If an error has occurred, attempt to annul the returned MatrixMap. */ + if( !astOK ) new = astAnnul( new ); + +/* Return the MatrixMap. */ + return new; +} + +static AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s, + AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat, + AstSkyFrame **reffrm, AstMapping **tabmap, + int *tabaxis, const char *method, + const char *class, int *status ){ +/* +* Name: +* WcsCelestial + +* Purpose: +* Create a Mapping from intermediate world coords to celestial coords +* as described in a FITS header. + +* Type: +* Private function. + +* Synopsis: +* AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s, +* AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat, +* AstSkyFrame **reffrm, , AstMapping **tabmap, +* int *tabaxis, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function interprets the contents of the supplied FitsStore +* structure, looking for world coordinate axes which describe positions +* on the sky. If a pair of such longitude/latitude axes is found, a +* Mapping is returned which transforms the corresponding intermediate +* world coordinates to celestial world coordinates (this mapping leaves +* any other axes unchanged). It also, modifies the supplied Frame to +* describe the axes (again, other axes are left unchanged). If no +* pair of celestial axes is found, a UnitMap is returned, and the +* supplied Frame is left unchanged. + +* Parameters: +* this +* The FitsChan. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* frm +* The address of a location at which to store a pointer to the +* Frame describing the world coordinate axes. +* iwcfrm +* A pointer to the Frame describing the intermediate world coordinate +* axes. The properties of this Frame may be changed on exit. +* reflon +* Address of a location at which to return the celestial longitude +* at the reference point. It is returned as AST__BAD if no +* celestial coordinate frame is found. +* reflat +* Address of a location at which to return the celestial latitude +* at the reference point. It is returned as AST__BAD if no +* celestial coordinate frame is found. +* reffrm +* Address of a location at which to return a pointer to a SkyFrame +* which define the reference values returned in reflon and reflat. +* It is returned as NULL if no celestial coordinate frame is found. +* tabmap +* Address of a pointer to a Mapping describing any -TAB +* transformations to be applied to the results of the Mapping returned +* by this function. If any celestial axes are found, the supplied +* Mapping is modified so that the celestial axes produce values in +* radians rather than degrees. NULL if no axes are described by -TAB. +* tabaxis +* Pointer to an array of flags, one for each WCS axis, indicating +* if the corresponding WCS axis is described by the -TAB algorithm. +* NULL if no axes are described by -TAB. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *ofrm; /* Pointer to a Frame */ + AstMapping *map1; /* Pointer to a Mapping */ + AstMapping *map2; /* Pointer to a Mapping */ + AstMapping *map3; /* Pointer to a Mapping */ + AstMapping *map4; /* Pointer to a Mapping */ + AstMapping *ret; /* Pointer to the returned Mapping */ + AstMapping *newmap; /* Modified PIXEL->IWC Mapping */ + AstMapping *shiftmap; /* ShiftMap from IWC to PPC */ + AstSkyFrame *sfrm; /* Pointer to a SkyFrame */ + char *ctype; /* Pointer to CTYPE string */ + char *keyname; /* Pointer to keyword name string */ + char buf[300]; /* Text buffer */ + char latctype[MXCTYPELEN];/* Latitude CTYPE keyword value */ + char latkey[10]; /* Latitude CTYPE keyword name */ + char lattype[4]; /* Buffer for celestial system */ + char lonctype[MXCTYPELEN];/* Longitude CTYPE keyword value */ + char lonkey[10]; /* Longitude CTYPE keyword name */ + char lontype[4]; /* Buffer for celestial system */ + double *shifts; /* Array holding axis shifts */ + double *ina; /* Pointer to memory holding input position A */ + double *inb; /* Pointer to memory holding input position B */ + double *mat; /* Pointer to data for deg->rad scaling matrix */ + double *outa; /* Pointer to memory holding output position A */ + double *outb; /* Pointer to memory holding output position B */ + double latval; /* CRVAL for latitude axis */ + double lonval; /* CRVAL for longitude axis */ + double pv; /* Projection parameter value */ + double x0; /* IWC X at the projection fiducial point */ + double y0; /* IWC Y at the projection fiducial point */ + int *axes; /* Point to a list of axis indices */ + int axlat; /* Index of latitude physical axis */ + int axlon; /* Index of longitude physical axis */ + int carlin; /* Assume native and WCS axes are the same? */ + int ctlen; /* Length of CTYPE string */ + int gotax; /* Celestial axis found? */ + int i; /* Loop count */ + int j; /* Axis index */ + int latprj; /* Latitude projection type identifier */ + int lonprj; /* Longitude projection type identifier */ + int m; /* Parameter index */ + int mxpar_lat; /* Max. projection parameter index on lat axis */ + int mxpar_lon; /* Max. projection parameter index on lon axis */ + int naxes; /* Number of axes */ + int nc; /* String length */ + int np; /* Max parameter index */ + int prj; /* Projection type identifier */ + +/* Initialise the returned values. */ + ret = NULL; + *reflon = AST__BAD; + *reflat = AST__BAD; + *reffrm = NULL; + +/* Other initialisation to avoid compiler warnings. */ + map1 = NULL; + +/* Check the global status. */ + if ( !astOK ) return ret; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Get the number of physical axes. */ + naxes = astGetNaxes( *frm ); + +/* See if CAR projections should be interpreted in the old fashioned way + (i.e native coords are always the same as WCS coords, so no need for + any rotation). */ + carlin = astGetCarLin( this ); + +/* The first major section sees if the physical axes include a pair of + longitude/latitude celestial axes. + ================================================================= */ + +/* We have not yet found any celestial axes. */ + axlon = -1; + axlat = -1; + latprj = AST__WCSBAD; + lonprj = AST__WCSBAD; + prj = AST__WCSBAD; + +/* First, we examine the CTYPE values in the FitsStore to determine + which axes are the longitude and latitude axes, and what the celestial + co-ordinate system and projection are. Loop round the physical axes, + getting each CTYPE value. */ + for( i = 0; i < naxes && astOK; i++ ){ + keyname = FormatKey( "CTYPE", i + 1, -1, s, status ); + ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + +/* Issue a warning if no CTYPE value was found. */ + if( !ctype ) { + sprintf( buf, "Axis type keywords (CTYPE, etc) were not found " + "for one or more axes in the original FITS header. These " + "axes will be assumed to be linear." ); + Warn( this, "noctype", buf, method, class, status ); + } else { + +/* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE + are "RA--" or "xLON" or "yzLN" ). If so, store the value of "x" or "yz" + (or "EQU" for equatorial coordinates) in variable "type" to indicate which + coordinate system is being used. */ + nc = strlen( ctype ); + gotax = 0; + if( !strcmp( ctype, "RA" ) || !strncmp( ctype, "RA--", 4 ) ){ + strcpy( wcscelestial_type, "EQU" ); + gotax = 1; + } else if( !strcmp( ctype, "AZ" ) || !strncmp( ctype, "AZ--", 4 ) ){ + strcpy( wcscelestial_type, "AZL" ); + gotax = 1; + } else if( nc > 1 && ( !strcmp( ctype + 1, "LON" ) || + !strncmp( ctype + 1, "LON-", 4 ) ) ){ + wcscelestial_type[ 0 ] = ctype[ 0 ]; + wcscelestial_type[ 1 ] = 0; + gotax = 1; + } else if( nc > 2 && ( !strcmp( ctype + 2, "LN" ) || + !strncmp( ctype + 2, "LN-", 3 ) ) ){ + wcscelestial_type[ 0 ] = ctype[ 0 ]; + wcscelestial_type[ 1 ] = ctype[ 1 ]; + wcscelestial_type[ 2 ] = 0; + gotax = 1; + } + +/* If this is a longitude axis... */ + if( gotax ){ + +/* Check that this is the first longitude axis to be found. */ + if( axlon == -1 ){ + +/* Find the projection type as specified by the last 4 characters + in the CTYPE keyword value. AST__WCSBAD is stored in "prj" if the + last 4 characters do not specify a known WCS projection, but no error + is reported. Assume simple linear axes if no projection code is + supplied. Note, AST__WCSBAD is used to indicate a TAB header. */ + ctlen = strlen( ctype ); + if( ctlen > 4 ) { + prj = astWcsPrjType( ctype + ctlen - 4 ); + } else if( tabmap && *tabmap ) { + prj = AST__WCSBAD; + } else { + prj = AST__CAR; + carlin = 1; + } + +/* Report an error if the projection is unknown. */ + if( prj == AST__WCSBAD && ctlen > 4 ){ + astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to " + "an unknown projection type '%s'.", status, method, class, + keyname, ctype + ctlen - 4 ); + break; + } + +/* Store the index of the longitude axis, type of longitude, etc. */ + axlon = i; + strcpy( lontype, wcscelestial_type ); + strcpy( lonkey, keyname ); + strcpy( lonctype, ctype ); + lonprj = prj; + +/* If another longitude axis has already been found, report an error. */ + } else { + astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') " + "and '%s' (='%s') both describe celestial longitude axes.", status, + method, class, keyname, ctype, lonkey, lonctype ); + break; + } + } + +/* Do the same for the latitude axis, checking for "DEC-" and "xLAT" and + "yzLT". */ + gotax = 0; + if( !strcmp( ctype, "DEC" ) || !strncmp( ctype, "DEC-", 4 ) ){ + strcpy( wcscelestial_type, "EQU" ); + gotax = 1; + } else if( !strcmp( ctype, "EL" ) || !strncmp( ctype, "EL--", 4 ) ){ + strcpy( wcscelestial_type, "AZL" ); + gotax = 1; + } else if( !strcmp( ctype + 1, "LAT" ) || !strncmp( ctype + 1, "LAT-", 4 ) ){ + wcscelestial_type[ 0 ] = ctype[ 0 ]; + wcscelestial_type[ 1 ] = 0; + gotax = 1; + } else if( !strcmp( ctype + 2, "LT" ) || !strncmp( ctype + 2, "LT-", 3 ) ){ + wcscelestial_type[ 0 ] = ctype[ 0 ]; + wcscelestial_type[ 1 ] = ctype[ 1 ]; + wcscelestial_type[ 2 ] = 0; + gotax = 1; + } + if( gotax ){ + if( axlat == -1 ){ + ctlen = strlen( ctype ); + if( ctlen > 4 ) { + prj = astWcsPrjType( ctype + ctlen - 4 ); + } else if( tabmap && *tabmap ) { + prj = AST__WCSBAD; + } else { + prj = AST__CAR; + carlin = 1; + } + + if( prj == AST__WCSBAD && ctlen > 4 ){ + astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to " + "an unknown projection type '%s'.", status, method, class, + keyname, ctype + ctlen - 4 ); + break; + } + axlat = i; + strcpy( lattype, wcscelestial_type ); + strcpy( latkey, keyname ); + strcpy( latctype, ctype ); + latprj = prj; + } else { + astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') " + "and '%s' (='%s') both describe celestial latitude axes.", status, + method, class, keyname, ctype, latkey, latctype ); + break; + } + } + } + } + +/* Check the above went OK */ + if( astOK ){ + +/* If both longitude and latitude axes were found... */ + if( axlat != -1 && axlon != -1 ){ + +/* Report an error if they refer to different celestial coordinate systems. */ + if( strcmp( lattype, lontype ) ){ + astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' " + "indicate different celestial coordinate systems " + "('%s' and '%s').", status, method, class, latkey, lonkey, + latctype, lonctype ); + +/* Otherwise report an error if longitude and latitude axes use different + projections. */ + } else if( lonprj != latprj ){ + astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' " + "indicate different projections ('%s' and '%s').", status, + method, class, latkey, lonkey, latctype, lonctype ); + } + +/* If only one axis has been provided without the other (e.g. longitude but no + latitude), report an error. */ + } else if( axlat != -1 && prj != AST__WCSBAD ){ + astError( AST__BDFTS, "%s(%s): A latitude axis ('%s') was found " + "without a corresponding longitude axis.", status, method, class, + latctype ); + } else if( axlon != -1 && prj != AST__WCSBAD ){ + astError( AST__BDFTS, "%s(%s): A longitude axis ('%s') was found " + "without a corresponding latitude axis.", status, method, class, + lonctype ); + } + } + +/* If a pair of matching celestial axes was not found, return a UnitMap + and leave the Frame unchanged. + ===================================================================== */ + if( axlat == -1 || axlon == -1 ) { + ret = (AstMapping *) astUnitMap( naxes, "", status ); + +/* The rest of this function deals with creating a Mapping from + intermediate world coords to celestial coords, and modifying the + Frame appropriately. + ===================================================================== */ + } else if( astOK ) { + +/* Create a MatrixMap which scales the intermediate world coordinate axes + corresponding to the longitude and latitude axes from degrees to radians. + Only do this if a projection was supplied. */ + if( latprj != AST__WCSBAD ) { + mat = (double *) astMalloc( sizeof(double)*naxes ); + if( mat ){ + for( i = 0; i < naxes; i++ ){ + if( i == axlat || i == axlon ){ + mat[ i ] = AST__DD2R; + } else { + mat[ i ] = 1.0; + } + } + map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status ); + mat = (double *) astFree( (void *) mat ); + } + } else { + map1 = (AstMapping *) astUnitMap( naxes, " ", status ); + } + +/* If the projection is a CAR projection, but the CarLin attribute is + set, then we consider the CAR projection to be a simple linear mapping + of pixel coords to celestial coords. Do this by using a WcsMap with no + projection. All axes will then be treated as linear and non-celestial. + If no projection was specified (i.e. if prj == AST__WCSBAD, as is the + case when using -TAB for instance) then do the same but use a UnitMap + instead of a WcsMap. */ + map3 = NULL; + if( ( latprj == AST__CAR && carlin ) || latprj == AST__WCSBAD ) { + if( latprj == AST__CAR ) { + map2 = (AstMapping *) astWcsMap( naxes, AST__WCSBAD, axlon + 1, + axlat + 1, "", status ); + } else { + map2 = (AstMapping *) astUnitMap( naxes, "", status ); + } + +/* Now create a WinMap which adds on the CRVAL values to each axis. */ + ina = astMalloc( sizeof(double)*naxes ); + inb = astMalloc( sizeof(double)*naxes ); + outa = astMalloc( sizeof(double)*naxes ); + outb = astMalloc( sizeof(double)*naxes ); + if( astOK ) { + for( i = 0; i < naxes; i++ ) { + ina[ i ] = 0.0; + inb[ i ] = 1.0; + outa[ i ] = 0.0; + outb[ i ] = 1.0; + } + lonval = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status ); + if( lonval != AST__BAD ) { + +/* For recognised projections the CRVAL value is required to be degrees, + so convert to radians. For other algorithms (e.g. -TAB) the CRVAL + values are in unknown units so retain their original scaling. */ + *reflon = ( latprj == AST__CAR ) ? lonval*AST__DD2R : lonval; + + outa[ axlon ] += *reflon; + outb[ axlon ] += *reflon; + } else { + outa[ axlon ] = AST__BAD; + outb[ axlon ] = AST__BAD; + } + + latval = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status ); + if( latval != AST__BAD ) { + *reflat = ( latprj == AST__CAR ) ? latval*AST__DD2R : latval; + outa[ axlat ] += *reflat; + outb[ axlat ] += *reflat; + } else { + outa[ axlat ] = AST__BAD; + outb[ axlat ] = AST__BAD; + } + + map3 = (AstMapping *) astWinMap( naxes, ina, inb, outa, outb, "", status ); + + } + ina = astFree( ina ); + inb = astFree( inb ); + outa = astFree( outa ); + outb = astFree( outb ); + +/* Otherwise, create a WcsMap with the specified projection. The WcsMap + is equivalent to a unit mapping for all axes other than "axlat" and + "axlon". */ + } else { + +/* Get the highest index ("m" value) of any supplied PVi_m projection + parameters (on any axes). */ + np = GetMaxJM( &(store->pv), s, status ); + +/* Create the WcsMap */ + map2 = (AstMapping *) astWcsMap( naxes, latprj, axlon + 1, + axlat + 1, "", status ); + +/* If the FITS header contains any projection parameters, store them in + the WcsMap. */ + mxpar_lat = astGetPVMax( map2, axlat ); + mxpar_lon = astGetPVMax( map2, axlon ); + for( m = 0; m <= np; m++ ){ + pv = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status ); + if( pv != AST__BAD ) { + if( m <= mxpar_lat ) { + astSetPV( map2, axlat, m, pv ); + } else { + sprintf( buf, "Projection parameter PV%d_%d found, " + "but is not used by %s projections.", axlat + 1, + m, astWcsPrjName( astGetWcsType( map2 ) ) ); + Warn( this, "badpv", buf, method, class, status ); + } + } + pv = GetItem( &(store->pv), axlon, m, s, NULL, method, class, status ); + if( pv != AST__BAD ) { + if( m <= mxpar_lon ) { + astSetPV( map2, axlon, m, pv ); + } else { + sprintf( buf, "Projection parameter PV%d_%d found, " + "but is not used by %s projections.", axlon + 1, + m, astWcsPrjName( astGetWcsType( map2 ) ) ); + Warn( this, "badpv", buf, method, class, status ); + } + } + } + +/* Invert the WcsMap to get a DEprojection. */ + astInvert( map2 ); + +/* Now produce a Mapping which converts the axes holding "Native Spherical + Coords" into "Celestial Coords", leaving all other axes unchanged. */ + map3 = WcsNative( this, store, s, (AstWcsMap *) map2, -1, -1, + method, class, status ); + +/* Retrieve and store the reference longitude and latitude. */ + *reflon = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status ); + if( *reflon != AST__BAD ) *reflon *= AST__DD2R; + *reflat = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status ); + if( *reflat != AST__BAD ) *reflat *= AST__DD2R; + } + +/* If projection parameter PVi_0a for the longitude axis "i" is non-zero, + then there is a shift of origin between Intermediate World Coords, IWC, + (the CRPIXi values correspond to the origin of IWC), and Projection Plane + Coords, PPC (these are the cartesian coordinates used by the WcsMap). + This shift of origin results in the fiducial point specified by the + CRVALi values mapping onto the pixel reference point specified by the + CRPIXj values. In this case we need to add a Mapping which implements + the shift of origin. Note, the AST-specific "TPN" projection cannot use + this convention since it uses PVi_0 to hold a polynomial correction term. */ + if( latprj != AST__WCSBAD && astGetWcsType( map2 ) != AST__TPN && + astGetPV( map2, axlon, 0 ) != 0.0 ) { + +/* Find the projection plane coords corresponding to the fiducial point + of the projection. This is done by using the inverse WcsMap to convert + the native spherical coords at the fiducial point into PPC (x,y), which + are returned in units of radians (not degrees). */ + GetFiducialPPC( (AstWcsMap *) map2, &x0, &y0, status ); + if( x0 != AST__BAD && y0 != AST__BAD ) { + +/* Allocate resources. */ + shifts = astMalloc( sizeof( double )*(size_t) naxes ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Create a Mapping (a ShiftMap) from IWC to PPC. */ + for( i = 0; i < naxes; i++ ) shifts[ i ] = 0.0; + shifts[ axlon ] = x0; + shifts[ axlat ] = y0; + shiftmap = (AstMapping *) astShiftMap( naxes, shifts, "", status ); + +/* Produce a CmpMap which combines "map1" (which converts degrees to + radians on the celestial axes) with the above ShiftMap. */ + newmap = (AstMapping *) astCmpMap( map1, shiftmap, 1, "", status ); + +/* Annul the component Mappings and use the new one in place of map1. */ + shiftmap = astAnnul( shiftmap ); + map1 = astAnnul( map1 ); + map1 = newmap; + } + +/* Free resources. */ + shifts = astFree( shifts ); + } + } + +/* Now concatenate the Mappings to produce the returned Mapping. */ + map4 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + ret = (AstMapping *) astCmpMap( map4, map3, 1, "", status ); + +/* Annul the component Mappings. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + map4 = astAnnul( map4 ); + +/* We now make changes to the supplied Frame so that the longitude and + latitude axes are described by a SkyFrame. First create an appropriate + SkyFrame. */ + sfrm = WcsSkyFrame( this, store, s, prj, wcscelestial_type, axlon, + axlat, method, class, status ); + +/* The values currently stored in *reflat and *reflon are the CRVAL + values. In some circumstances, these may not be the original values in + the supplied header but may have been translated within the SpecTrans + function as part of the process of translating an old unsupported + projection into a new supported projection. Since the returned RefLat + and RefLon values may be used to set the reference position for a + SpecFrame, we should return the original values rather than the + translated values. The original values will have been stored (within + SpecTrans) in the FitsChan as keywords RFVALi. If such keywords can + be found, use their values in preference to the currently stored CRVAL + values.*/ + if( GetValue( this, FormatKey( "RFVAL", axlon + 1, -1, s, status ), + AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) && + GetValue( this, FormatKey( "RFVAL", axlat + 1, -1, s, status ), + AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) { + *reflon = lonval*AST__DD2R; + *reflat = latval*AST__DD2R; + } + +/* Store the reflon and reflat values as the SkyRef position in the + SkyFrame, and set SkyRefIs to "ignore" so that the SkyFrame continues + to represent absolute celestial coords. Do not change the SkyFrame if + it already had a set reference posiiton. */ + if( ! astTestSkyRef( sfrm, 0 ) ) { + if( *reflon != AST__BAD && *reflat != AST__BAD ) { + astSetSkyRef( sfrm, 0, *reflon ); + astSetSkyRef( sfrm, 1, *reflat ); + astSet( sfrm, "SkyRefIs=Ignored", status ); + } + } + +/* Return a clone of this SkyFrame as the reference Frame. */ + *reffrm = astClone( sfrm ); + +/* Create a Frame by picking all the other (non-celestial) axes from the + supplied Frame. */ + axes = astMalloc( naxes*sizeof( int ) ); + if( axes ) { + j = 0; + for( i = 0; i < naxes; i++ ) { + if( i != axlat && i != axlon ) axes[ j++ ] = i; + } + +/* If there were no other axes, replace the supplied Frame with the skyframe. */ + if( j == 0 ) { + (void) astAnnul( *frm ); + *frm = (AstFrame *) astClone( sfrm ); + +/* Otherwise pick the other axes from the supplied Frame */ + } else { + ofrm = astPickAxes( *frm, j, axes, NULL ); + +/* Replace the supplied Frame with a CmpFrame made up of this Frame and + the SkyFrame. */ + (void) astAnnul( *frm ); + *frm = (AstFrame *) astCmpFrame( ofrm, sfrm, "", status ); + ofrm = astAnnul( ofrm ); + } + +/* Permute the axis order to put the longitude and latitude axes back in + their original position. The SkyFrame will have the default axis + ordering (lon=axis 0, lat = axis 1). */ + j = 0; + for( i = 0; i < naxes; i++ ) { + if( i == axlat ) { + axes[ i ] = naxes - 1; + } else if( i == axlon ) { + axes[ i ] = naxes - 2; + } else { + axes[ i ] = j++; + } + } + astPermAxes( *frm, axes ); + +/* Free the axes array. */ + axes= astFree( axes ); + } + +/* Set the units in the supplied IWC Frame for the longitude and latitude + axes. Unless using -TAB, these are degrees (the conversion from degs to + rads is part of the Mapping from IWC to WCS). If using -TAB the units + are unknown. */ + if( !tabaxis || !tabaxis[ axlon ] ) astSetUnit( iwcfrm, axlon, "deg" ); + if( !tabaxis || !tabaxis[ axlat ] ) astSetUnit( iwcfrm, axlat, "deg" ); + +/* Modify any supplied tabmap so that the celestial outputs create + radians rather than degrees (but only if the celestial axes are + generated by the -TAB algorithm). */ + if( tabaxis && tabaxis[ axlon ] && tabaxis[ axlat ] ) { + mat = (double *) astMalloc( sizeof(double)*naxes ); + if( mat ){ + for( i = 0; i < naxes; i++ ){ + if( i == axlat || i == axlon ){ + mat[ i ] = AST__DD2R; + } else { + mat[ i ] = 1.0; + } + } + map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status ); + mat = (double *) astFree( (void *) mat ); + map2 = (AstMapping *) astCmpMap( *tabmap, map1, 1, " ", status ); + map1 = astAnnul( map1 ); + (void) astAnnul( *tabmap ); + *tabmap = map2; + } + +/* Also modify the returned reflon and reflat values to transform them + using the tabmap. Also transform the reference position in the SkyFrame. */ + if( *reflon != AST__BAD && *reflat != AST__BAD ) { + ina = astMalloc( sizeof(double)*naxes ); + outa = astMalloc( sizeof(double)*naxes ); + if( astOK ) { + for( i = 0; i < naxes; i++ ) ina[ i ] = 0.0; + ina[ axlat ] = *reflat; + ina[ axlon ] = *reflon; + astTranN( *tabmap, 1, naxes, 1, ina, 1, naxes, 1, outa ); + *reflon = outa[ axlon ]; + *reflat = outa[ axlat ]; + } + ina = astFree( ina ); + outa = astFree( outa ); + +/* Store this transformed reference position in the SkyFrame. */ + astSetSkyRef( sfrm, 0, *reflon ); + astSetSkyRef( sfrm, 1, *reflat ); + astSet( sfrm, "SkyRefIs=Ignored", status ); + } + } + +/* If the header contains AXREF values for both lon and lat axes, use + them as the sky reference position in preferences to the values + derived form the CRVAL values. AXREF keywords are created by the + astWrite method for axes described by -TAB algorithm that have no inverse + transformation. */ + if( GetValue( this, FormatKey( "AXREF", axlon + 1, -1, s, status ), + AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) && + GetValue( this, FormatKey( "AXREF", axlat + 1, -1, s, status ), + AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) { + *reflon = lonval*AST__DD2R; + *reflat = latval*AST__DD2R; + astSetSkyRef( sfrm, 0, *reflon ); + astSetSkyRef( sfrm, 1, *reflat ); + astSet( sfrm, "SkyRefIs=Ignored", status ); + } + +/* Free resources. */ + sfrm = astAnnul( sfrm ); + } + +/* Return the result. */ + return ret; +} + +static void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store, + const char *method, const char *class, int *status ){ +/* +* Name: +* WcsFcRead + +* Purpose: +* Extract WCS information from a supplied FitsChan using a FITSWCS +* encoding, and store it in the supplied FitsStore. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function extracts FITSWCS keywords from the supplied FitsChan, +* and stores the corresponding WCS information in the supplied FitsStore. + +* Parameters: +* fc +* Pointer to the FitsChan containing the cards read from the +* original FITS header. This should not include any un-used +* non-standard keywords. +* fc2 +* Pointer to a second FitsChan. If a card read from "fc" fails to +* be converted to its correct data type, a warning is only issued +* if there is no card for this keyword in "fc2". "fc2" may be NULL +* in which case a warning is always issued. +* store +* Pointer to the FitsStore structure. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + char buf[200]; /* Buffer for warning message */ + char *cval; /* String keyword value */ + char *keynam; /* Pointer to current keyword name */ + char s; /* Co-ordinate version character */ + double dval; /* Floating point keyword value */ + int fld[2]; /* Integer field values from keyword name */ + int jm; /* Pixel axis or projection parameter index */ + int i; /* Intermediate axis index */ + int mark; /* Non-zero if card should be removed once used */ + int nfld; /* Number of integer fields in test string */ + int ok; /* Was value converted succesfully? */ + int type; /* Keyword data type */ + int undef; /* Is an undefined keyword value acceptable? */ + void *item; /* Pointer to item to get/put */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure the FitsChan is re-wound. */ + astClearCard( fc ); + +/* Loop round all the cards in the FitsChan obtaining the keyword name for + each card. Note, the single "=" is correct in the following "while" + statement. */ + s = 0; + jm = -1; + i = -1; + type = AST__NOTYPE; + while( (keynam = CardName( fc, status )) ){ + item = NULL; + +/* Assume the card is to be consumed by the reading process. This means + the card will be marked as used and effectively excluded from the header. + Keywords which supply observation details that do not depend on the + mapping from pixel to WCS axes, or on the nature of the WCS axes, + are not removed as they may be needed for other, non-WCS related, + purposes. */ + mark = 1; + +/* For most keywords, if the keyword is present in the header it must + have a definded value. However, some keywords are read from the header + but not actually used for anything. This is done to ensure that the + keyword is stripped from the header. It is acceptable for such + keywords to have an undefined value. Initialise a flag indicating that + the next keyword read is not allowed to have an undefined value. */ + undef = 0; + +/* Is this a primary CRVAL keyword? */ + if( Match( keynam, "CRVAL%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->crval); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + +/* Is this a secondary CRVAL keyword? */ + } else if( Match( keynam, "CRVAL%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->crval); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary CRPIX keyword? */ + } else if( Match( keynam, "CRPIX%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->crpix); + type = AST__FLOAT; + i = 0; + jm = fld[ 0 ] - 1; + s = ' '; + +/* Is this a secondary CRPIX keyword? */ + } else if( Match( keynam, "CRPIX%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->crpix); + type = AST__FLOAT; + i = 0; + jm = fld[ 0 ] - 1; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary CDELT keyword? */ + } else if( Match( keynam, "CDELT%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->cdelt); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + +/* Is this a secondary CDELT keyword? */ + } else if( Match( keynam, "CDELT%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->cdelt); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary CTYPE keyword? If so, store the associated comment. */ + } else if( Match( keynam, "CTYPE%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->ctype); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + SetItemC( &(store->ctype_com), i, 0, ' ', CardComm( fc, status ), status ); + +/* Is this a secondary CTYPE keyword? If so, store the associated comment. */ + } else if( Match( keynam, "CTYPE%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->ctype); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + SetItemC( &(store->ctype_com), i, 0, s, CardComm( fc, status ), status ); + +/* Is this a primary CNAME keyword? */ + } else if( Match( keynam, "CNAME%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->cname); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + +/* Is this a secondary CNAME keyword? */ + } else if( Match( keynam, "CNAME%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->cname); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary CUNIT keyword? */ + } else if( Match( keynam, "CUNIT%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->cunit); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + +/* Is this a secondary CUNIT keyword? */ + } else if( Match( keynam, "CUNIT%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->cunit); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary PC keyword? */ + } else if( Match( keynam, "PC%d_%d", 2, fld, &nfld, method, class, status ) ){ + item = &(store->pc); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = fld[ 1 ] - 1; + s = ' '; + +/* Is this a secondary PC keyword? */ + } else if( Match( keynam, "PC%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ + item = &(store->pc); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = fld[ 1 ] - 1; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary PV keyword? */ + } else if( Match( keynam, "PV%d_%d", 2, fld, &nfld, method, class, status ) ){ + item = &(store->pv); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = fld[ 1 ]; + s = ' '; + +/* Is this a secondary PV keyword? */ + } else if( Match( keynam, "PV%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ + item = &(store->pv); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = fld[ 1 ]; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary PS keyword? */ + } else if( Match( keynam, "PS%d_%d", 2, fld, &nfld, method, class, status ) ){ + item = &(store->ps); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = fld[ 1 ]; + s = ' '; + +/* Is this a secondary PS keyword? */ + } else if( Match( keynam, "PS%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ + item = &(store->ps); + type = AST__STRING; + i = fld[ 0 ] - 1; + jm = fld[ 1 ]; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary RADESYS keyword? */ + } else if( Match( keynam, "RADESYS", 0, fld, &nfld, method, class, status ) ){ + item = &(store->radesys); + type = AST__STRING; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary RADESYS keyword? */ + } else if( Match( keynam, "RADESYS%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->radesys); + type = AST__STRING; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary EQUINOX keyword? */ + } else if( Match( keynam, "EQUINOX", 0, fld, &nfld, method, class, status ) ){ + item = &(store->equinox); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary EQUINOX keyword? */ + } else if( Match( keynam, "EQUINOX%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->equinox); + type = AST__FLOAT; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary LATPOLE keyword? */ + } else if( Match( keynam, "LATPOLE", 0, fld, &nfld, method, class, status ) ){ + item = &(store->latpole); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary LATPOLE keyword? */ + } else if( Match( keynam, "LATPOLE%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->latpole); + type = AST__FLOAT; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary LONPOLE keyword? */ + } else if( Match( keynam, "LONPOLE", 0, fld, &nfld, method, class, status ) ){ + item = &(store->lonpole); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary LONPOLE keyword? */ + } else if( Match( keynam, "LONPOLE%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->lonpole); + type = AST__FLOAT; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary WXSAXES keyword? */ + } else if( Match( keynam, "WCSAXES", 0, fld, &nfld, method, class, status ) ){ + item = &(store->wcsaxes); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary WCSAXES keyword? */ + } else if( Match( keynam, "WCSAXES%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->wcsaxes); + type = AST__FLOAT; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary DUT1 keyword? */ + } else if( Match( keynam, "DUT1", 0, fld, &nfld, method, class, status ) ){ + mark = 0; + item = &(store->dut1); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a primary MJD-OBS keyword? */ + } else if( Match( keynam, "MJD-OBS", 0, fld, &nfld, method, class, status ) ){ + mark = 0; + item = &(store->mjdobs); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a primary WCSNAME keyword? */ + } else if( Match( keynam, "WCSNAME", 0, fld, &nfld, method, class, status ) ){ + item = &(store->wcsname); + type = AST__STRING; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary WCSNAME keyword? */ + } else if( Match( keynam, "WCSNAME%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->wcsname); + type = AST__STRING; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary SPECSYS keyword? */ + } else if( Match( keynam, "SPECSYS", 0, fld, &nfld, method, class, status ) ){ + item = &(store->specsys); + type = AST__STRING; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary SPECSYS keyword? */ + } else if( Match( keynam, "SPECSYS%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->specsys); + type = AST__STRING; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary SSYSSRC keyword? */ + } else if( Match( keynam, "SSYSSRC", 0, fld, &nfld, method, class, status ) ){ + item = &(store->ssyssrc); + type = AST__STRING; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary SSYSSRC keyword? */ + } else if( Match( keynam, "SSYSSRC%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->ssyssrc); + type = AST__STRING; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary ZSOURCE keyword? */ + } else if( Match( keynam, "ZSOURCE", 0, fld, &nfld, method, class, status ) ){ + item = &(store->zsource); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary ZSOURCE keyword? */ + } else if( Match( keynam, "ZSOURCE%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->zsource); + type = AST__FLOAT; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary VELOSYS keyword? */ + } else if( Match( keynam, "VELOSYS", 0, fld, &nfld, method, class, status ) ){ + item = &(store->velosys); + type = AST__FLOAT; + undef = 1; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary VELOSYS keyword? */ + } else if( Match( keynam, "VELOSYS%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->velosys); + type = AST__FLOAT; + undef = 1; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary RESTFRQ keyword? */ + } else if( Match( keynam, "RESTFRQ", 0, fld, &nfld, method, class, status ) ){ + item = &(store->restfrq); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary RESTFRQ keyword? */ + } else if( Match( keynam, "RESTFRQ%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->restfrq); + type = AST__FLOAT; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary RESTWAV keyword? */ + } else if( Match( keynam, "RESTWAV", 0, fld, &nfld, method, class, status ) ){ + item = &(store->restwav); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary RESTWAV keyword? */ + } else if( Match( keynam, "RESTWAV%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->restwav); + type = AST__FLOAT; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary IMAGFREQ keyword? */ + } else if( Match( keynam, "IMAGFREQ", 0, fld, &nfld, method, class, status ) ){ + item = &(store->imagfreq); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a primary SKYREF keyword? */ + } else if( Match( keynam, "SREF%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->skyref); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + +/* Is this a secondary SKYREF keyword? */ + } else if( Match( keynam, "SREF%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->skyref); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary SKYREFP keyword? */ + } else if( Match( keynam, "SREFP%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->skyrefp); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + +/* Is this a secondary SKYREFP keyword? */ + } else if( Match( keynam, "SREFP%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->skyrefp); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary SKYREFIS keyword? */ + } else if( Match( keynam, "SREFIS", 0, fld, &nfld, method, class, status ) ){ + item = &(store->skyrefis); + type = AST__STRING; + i = 0; + jm = 0; + s = ' '; + +/* Is this a secondary SKYREFIS keyword? */ + } else if( Match( keynam, "SREFIS%1c", 0, fld, &nfld, method, class, status ) ){ + item = &(store->skyrefis); + type = AST__STRING; + i = 0; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary AXREF keyword? */ + } else if( Match( keynam, "AXREF%d", 1, fld, &nfld, method, class, status ) ){ + item = &(store->axref); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = ' '; + +/* Is this a secondary AXREF keyword? */ + } else if( Match( keynam, "AXREF%d%1c", 1, fld, &nfld, method, class, status ) ){ + item = &(store->axref); + type = AST__FLOAT; + i = fld[ 0 ] - 1; + jm = 0; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a MJD-AVG keyword? */ + } else if( Match( keynam, "MJD-AVG", 0, fld, &nfld, method, class, status ) ){ + mark = 0; + item = &(store->mjdavg); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a OBSGEO-X keyword? */ + } else if( Match( keynam, "OBSGEO-X", 0, fld, &nfld, method, class, status ) ){ + mark = 0; + item = &(store->obsgeox); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a OBSGEO-Y keyword? */ + } else if( Match( keynam, "OBSGEO-Y", 0, fld, &nfld, method, class, status ) ){ + mark = 0; + item = &(store->obsgeoy); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a OBSGEO-Z keyword? */ + } else if( Match( keynam, "OBSGEO-Z", 0, fld, &nfld, method, class, status ) ){ + mark = 0; + item = &(store->obsgeoz); + type = AST__FLOAT; + i = 0; + jm = 0; + s = ' '; + +/* Is this a TIMESYS keyword? */ + } else if( Match( keynam, "TIMESYS", 0, fld, &nfld, method, class, status ) ){ + item = &(store->timesys); + type = AST__STRING; + i = 0; + jm = 0; + s = ' '; + +/* Following keywords are used to describe "-SIP" distortion as used by + the Spitzer project... */ + +/* Is this a primary A keyword? */ + } else if( Match( keynam, "A_%d_%d", 2, fld, &nfld, method, class, status ) ){ + item = &(store->asip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = ' '; + +/* Is this a secondary A keyword? */ + } else if( Match( keynam, "A_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ + item = &(store->asip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary B keyword? */ + } else if( Match( keynam, "B_%d_%d", 2, fld, &nfld, method, class, status ) ){ + item = &(store->bsip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = ' '; + +/* Is this a secondary B keyword? */ + } else if( Match( keynam, "B_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ + item = &(store->bsip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary AP keyword? */ + } else if( Match( keynam, "AP_%d_%d", 2, fld, &nfld, method, class, status ) ){ + item = &(store->apsip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = ' '; + +/* Is this a secondary AP keyword? */ + } else if( Match( keynam, "AP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ + item = &(store->apsip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = keynam[ strlen( keynam ) - 1 ]; + +/* Is this a primary BP keyword? */ + } else if( Match( keynam, "BP_%d_%d", 2, fld, &nfld, method, class, status ) ){ + item = &(store->bpsip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = ' '; + +/* Is this a secondary BP keyword? */ + } else if( Match( keynam, "BP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){ + item = &(store->bpsip); + type = AST__FLOAT; + i = fld[ 0 ]; + jm = fld[ 1 ]; + s = keynam[ strlen( keynam ) - 1 ]; + } + +/* If this keyword was recognized, store it in the FitsStore, and mark it + as having been read. */ + if( item ){ + ok = 1; + if( type == AST__FLOAT ){ + if( CnvValue( fc, AST__FLOAT, undef, &dval, method, status ) ) { + SetItem( (double ****) item, i, jm, s, dval, status ); + if( mark ) MarkCard( fc, status ); + } else { + ok = 0; + } + } else { + if( CnvValue( fc, AST__STRING, undef, &cval, method, status ) ) { + cval[ astChrLen( cval ) ] = 0; /* Exclude trailing spaces */ + SetItemC( (char *****) item, i, jm, s, cval, status ); + if( mark ) MarkCard( fc, status ); + } else { + ok = 0; + } + } + +/* Issue a warning if the value could not be converted to the expected + type. */ + if( !ok ) { + +/* First check that the keyword is not included in "fc2". */ + if( !HasCard( fc2, keynam, method, class, status ) ) { + sprintf( buf, "The original FITS header contained a value for " + "keyword %s which could not be converted to a %s.", + keynam, ( type==AST__FLOAT ? "floating point number": + "character string" ) ); + Warn( fc, "badval", buf, "astRead", "FitsChan", status ); + } + } + } + +/* Move on to the next card. */ + MoveCard( fc, 1, method, class, status ); + } +} + +static int WcsFromStore( AstFitsChan *this, FitsStore *store, + const char *method, const char *class, int *status ){ + +/* +* Name: +* WcsFromStore + +* Purpose: +* Store WCS keywords in a FitsChan using FITS-WCS encoding. + +* Type: +* Private function. + +* Synopsis: +* int WcsFromStore( AstFitsChan *this, FitsStore *store, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function copies the WCS information stored in the supplied +* FitsStore into the supplied FitsChan, using FITS-WCS encoding. + +* Parameters: +* this +* Pointer to the FitsChan. +* store +* Pointer to the FitsStore. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A value of 1 is returned if succesfull, and zero is returned +* otherwise. +*/ + +/* Local Variables: */ + char *comm; /* Pointer to comment string */ + char *cval; /* Pointer to string keyword value */ + char parprefix[3]; /* Prefix for projection parameter keywords */ + char combuf[80]; /* Buffer for FITS card comment */ + char s; /* Co-ordinate version character */ + char sign[2]; /* Fraction's sign character */ + char sup; /* Upper limit on s */ + char type[MXCTYPELEN];/* Buffer for CTYPE value */ + double cdl; /* CDELT value */ + double fd; /* Fraction of a day */ + double mjd99; /* MJD at start of 1999 */ + double val; /* General purpose value */ + int *tabaxis; /* Flags WCS axes that use -TAB algorithm */ + int i; /* Axis index */ + int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */ + int iymdf[ 4 ]; /* Year, month, date, fractional day */ + int j; /* Axis index */ + int jj; /* SlaLib status */ + int m; /* Parameter index */ + int maxm; /* Upper limit on m */ + int naxis; /* Value of NAXIS keyword */ + int nc; /* Length of STYPE string */ + int nwcs; /* No. of WCS axes */ + int ok; /* Frame created succesfully? */ + int prj; /* Projection type */ + int ret; /* Returned value */ + +/* Initialise */ + ret = 0; + +/* Other initialisation to avoid compiler warnings. */ + tabaxis = NULL; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* If the FitsChan contains a value for the NAXIS keyword, note it. + Otherwise store -1. */ + if( !astGetFitsI( this, "NAXIS", &naxis ) ) naxis = -1; + +/* Find the last WCS related card. */ + FindWcs( this, 1, 1, 0, method, class, status ); + +/* Loop round all co-ordinate versions */ + sup = GetMaxS( &(store->crval), status ); + for( s = ' '; s <= sup && astOK; s++ ){ + +/* For alternate axes, skip this axis description if there is no CRPIX1 or + CRVAL1 value. This avoids partial axis descriptions being written out. */ + if( s != ' ' ) { + if( GetItem( &(store->crpix), 0, 0, s, NULL, method, class, status ) == + AST__BAD || + GetItem( &(store->crval), 0, 0, s, NULL, method, class, status ) == + AST__BAD ) { + ok = 0; + goto next; + } + } + +/* Assume the Frame can be created succesfully. */ + ok = 1; + +/* Save the number of wcs axes. If a value for WCSAXES has been set, or + if the number of axes is not the same as specified in the NAXIS keyword, + store a WCSAXES keyword. */ + val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + nwcs = (int) ( val + 0.5 ); + } else { + nwcs = GetMaxJM( &(store->crpix), s, status ) + 1; + if( nwcs != 0 && nwcs != naxis ) val = (double) nwcs; + } + if( val != AST__BAD ) { + SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ), + &nwcs, AST__INT, "Number of WCS axes", status ); + } + +/* Get and save WCSNAME. This is NOT required, so do not return if it is + not available. If the WCS is 1-d, only store WCSNAME if its value is + different to the CTYPE1 value. */ + cval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status ); + if( cval && nwcs == 1 ) { + comm = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status ); + if( comm && Similar( comm, cval, status ) ) cval = NULL; + } + if( cval ) SetValue( this, FormatKey( "WCSNAME", -1, -1, s, status ), &cval, + AST__STRING, "Reference name for the coord. frame", status ); + +/* The prefix for numerical projection parameters is usually "PV". */ + strcpy( parprefix, "PV" ); + +/* Keywords common to all axis types... */ + +/* Get and save CRPIX for all pixel axes. These are required, so pass on + if they are not available. */ + for( i = 0; i < nwcs; i++ ) { + val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + goto next; + } + sprintf( combuf, "Reference pixel on axis %d", i + 1 ); + SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + +/* Get and save CRVAL for all WCS axes. These are required, so + pass on if they are not available. */ + for( i = 0; i < nwcs; i++ ) { + val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + goto next; + } + sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 ); + SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + +/* Allocate memory to indicate if each WCS axis is described by a -TAB + algorithm or not. Initialiss it to zero. */ + tabaxis = astCalloc( nwcs, sizeof( int ) ); + +/* Get and save CTYPE for all WCS axes. These are required, so + pass on if they are not available. */ + for( i = 0; i < nwcs; i++ ) { + cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( !cval ) { + ok = 0; + goto next; + } + comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + if( !comm ) { + sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 ); + comm = combuf; + } + +/* Extract the projection type as specified by the last 4 characters + in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial + axes. Note, CTYPE can be more than 8 characters long. */ + nc = strlen( cval ); + prj = ( nc > 4 ) ? astWcsPrjType( cval + nc - 4 ) : AST__WCSBAD; + +/* If the projection type is "TPN" (an AST-specific code) convert it to + standard FITS-WCS code "TAN" and change the prefix for projection + parameters from "PV" to "QV". AST will do the inverse conversions when + reading such a header. Non-AST software will simply ignore the QV + terms and interpret the header as a simple TAN projection. */ + if( prj == AST__TPN ) { + strcpy( parprefix, "QV" ); + strcpy( type, cval ); + (void) strcpy( type + nc - 4, "-TAN" ); + cval = type; + } + +/* Note if the axis is described by the -TAB algorithm. */ + tabaxis[ i ] = ( prj == AST__WCSBAD && strlen( cval ) >= 8 && + !strncmp( cval + 4, "-TAB", 4 ) ); + +/* Store the (potentially modified) CTYPE value. */ + SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING, + comm, status ); + } + +/* Get and save CNAME for all WCS axes. These are NOT required, so + do not pass on if they are not available. Do not include a CNAME + keyword if its value equals the commen or value of the corresponding + CTYPE keyword. */ + for( i = 0; i < nwcs; i++ ) { + cval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status ); + if( cval ) { + comm = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( !comm || strcmp( comm, cval ) ) { + comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + if( !comm || strcmp( comm, cval ) ) { + sprintf( combuf, "Description of axis %d", i + 1 ); + SetValue( this, FormatKey( "CNAME", i + 1, -1, s, status ), &cval, + AST__STRING, combuf, status ); + } + } + } + } + +/* Now choose whether to produce CDi_j or CDELT/PCi_j keywords. */ + if( astGetCDMatrix( this ) ) { + +/* CD matrix. Multiply the row of the PC matrix by the CDELT value. */ + for( i = 0; i < nwcs; i++ ) { + cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( cdl == AST__BAD ) cdl = 1.0; + for( j = 0; j < nwcs; j++ ){ + val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0; + val *= cdl; + if( val != 0.0 ) { + SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val, + AST__FLOAT, "Transformation matrix element", status ); + } + } + } + +/* If producing PC/CDELT keywords... */ + } else { + +/* CDELT keywords. */ + for( i = 0; i < nwcs; i++ ) { + val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status ); + if( val == AST__BAD ) { + ok = 0; + goto next; + } + sprintf( combuf, "Pixel size on axis %d", i + 1 ); + SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + +/* PC matrix. */ + for( i = 0; i < nwcs; i++ ) { + for( j = 0; j < nwcs; j++ ){ + val = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + if( val != AST__BAD ) { + if( i == j ) { + if( astEQUAL( val, 1.0 ) ) val = AST__BAD; + } else { + if( astEQUAL( val, 0.0 ) ) val = AST__BAD; + } + } + if( val != AST__BAD ) { + SetValue( this, FormatKey( "PC", i + 1, j + 1, s, status ), &val, + AST__FLOAT, "Transformation matrix element", status ); + } + } + } + } + +/* Get and save CUNIT for all WCS axes. These are NOT required, so + do not pass on if they are not available. */ + for( i = 0; i < nwcs; i++ ) { + cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); + if( cval ) { + sprintf( combuf, "Units for axis %d", i + 1 ); + SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING, + combuf, status ); + } + } + +/* Get and save AXREF for all WCS axes. These are NOT required, so do not + pass on if they are not available. Note, AXREF is a non-standard keyword + used by AST to communicate the reference position on any axes described + by the -TAB algorithm and which has no inverse transformation. For all + other cases, the reference position corresponds to the values of CRVAL. */ + for( i = 0; i < nwcs; i++ ) { + val = GetItem( &(store->axref), i, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + sprintf( combuf, "Reference WCS value on axis %d", i + 1 ); + SetValue( this, FormatKey( "AXREF", i + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + } + +/* Get and save SREFIS. This is NOT required, so do not return if it is + not available. Note, SREFIS is a non-standard keyword used by AST to + communicate the SkyRefIs attribute in the original SkyFrame. */ + cval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status ); + if( cval ) SetValue( this, FormatKey( "SREFIS", -1, -1, s, status ), &cval, + AST__STRING, "Is SkyRef used as pole or origin?", status ); + +/* Get and save SREF for all WCS axes. These are NOT required, so do not + pass on if they are not available. Note, SREF is a non-standard keyword + used by AST to communicate the SkyRef position on any axes described + by a offset SkyFrame. */ + for( i = 0; i < nwcs; i++ ) { + val = GetItem( &(store->skyref), i, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + sprintf( combuf, "Sky reference position on axis %d", i + 1 ); + SetValue( this, FormatKey( "SREF", i + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + } + +/* Get and save SREFP for all WCS axes. These are NOT required, so do not + pass on if they are not available. Note, SREFP is a non-standard keyword + used by AST to communicate the SkyRefP position on any axes described + by a offset SkyFrame. */ + for( i = 0; i < nwcs; i++ ) { + val = GetItem( &(store->skyrefp), i, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + sprintf( combuf, "Sky primary meridian position on axis %d", i + 1 ); + SetValue( this, FormatKey( "SREFP", i + 1, -1, s, status ), &val, AST__FLOAT, + combuf, status ); + } + } + +/* Date of observation (only allowed for primary axis descriptions). */ + if( s == ' ' ) { + val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + SetValue( this, FormatKey( "MJD-OBS", -1, -1, s, status ), + &val, AST__FLOAT, "Modified Julian Date of observation", status ); + +/* The format used for the DATE-OBS keyword depends on the value of the + keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format. + Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */ + palCaldj( 99, 1, 1, &mjd99, &jj ); + if( val < mjd99 ) { + palDjcal( 0, val, iymdf, &jj ); + sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ], + iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) ); + } else { + palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj ); + palDd2tf( 3, fd, sign, ihmsf ); + sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d", + iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1], + ihmsf[2], ihmsf[3] ); + } + +/* Now store the formatted string in the FitsChan. */ + cval = combuf; + SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING, + "Date of observation", status ); + } + val = GetItem( &(store->mjdavg), 0, 0, ' ', NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, "MJD-AVG", &val, AST__FLOAT, + "Average Modified Julian Date of observation", status ); + +/* Store the timescale in TIMESYS. */ + cval = GetItemC( &(store->timesys), 0, 0, s, NULL, method, class, status ); + if( cval ) SetValue( this, "TIMESYS", &cval, AST__STRING, + "Timescale for MJD-OBS/MJD-AVG values", status ); + } + +/* Numerical projection parameters */ + maxm = GetMaxJM( &(store->pv), s, status ); + for( i = 0; i < nwcs; i++ ){ + for( m = 0; m <= maxm; m++ ){ + val = GetItem( &(store->pv), i, m, s, NULL, method, class, status ); + if( val != AST__BAD ) { + +/* If the axis uses the "TAB" algorithm, there may be a PVi_4a parameter + in the FitsStore. This is an AST extension to the published -TAB + algorithm, and is used to hold the interpolation method. To avoid + clashing with any standard use of PV1_4a, rename it to QVi_4a. The + default is zero (linear interpolation) so do not write the QV value + if it zero. */ + if( m == 4 && tabaxis[ i ] ) { + if( val != 0.0 ) { + SetValue( this, FormatKey( "QV", i + 1, m, s, status ), + &val, AST__FLOAT, "Use nearest neighbour " + "interpolation", status ); + } + +/* Just store the parameters for other type of axes. */ + } else { + SetValue( this, FormatKey( parprefix, i + 1, m, s, status ), &val, + AST__FLOAT, "Projection parameter", status ); + } + } + } + } + +/* String projection parameters */ + maxm = GetMaxJMC( &(store->ps), s, status ); + for( i = 0; i < nwcs; i++ ){ + for( m = 0; m <= maxm; m++ ){ + cval = GetItemC( &(store->ps), i, m, s, NULL, method, class, status ); + if( cval ) { + SetValue( this, FormatKey( "PS", i + 1, m, s, status ), &cval, + AST__STRING, "Projection parameter", status ); + } + } + } + +/* Keywords specific to celestial axes... */ + +/* Get and save RADESYS. This is NOT required, so do not return if it is + not available. */ + cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); + if( cval ) SetValue( this, FormatKey( "RADESYS", -1, -1, s, status ), &cval, + AST__STRING, "Reference frame for RA/DEC values", status ); + +/* Reference equinox */ + val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "EQUINOX", -1, -1, s, status ), + &val, AST__FLOAT, + "[yr] Epoch of reference equinox", status ); + +/* Latitude of native north pole */ + val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "LATPOLE", -1, -1, s, status ), + &val, AST__FLOAT, + "[deg] Latitude of native north pole", status ); + +/* Longitude of native north pole */ + val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "LONPOLE", -1, -1, s, status ), + &val, AST__FLOAT, + "[deg] Longitude of native north pole", status ); + +/* Keywords specific to spectral axes... */ + +/* SPECSYS - the standard of rest for the spectral axis */ + cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); + if( cval ) SetValue( this, FormatKey( "SPECSYS", -1, -1, s, status ), &cval, + AST__STRING, "Standard of rest for spectral axis", status ); + +/* SSYSSRC - the standard of rest in which ZSOURCE is stored. */ + cval = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status ); + if( cval ) SetValue( this, FormatKey( "SSYSSRC", -1, -1, s, status ), &cval, + AST__STRING, "Standard of rest for source redshift", status ); + +/* ZSOURCE - topocentric optical velocity of source */ + val = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "ZSOURCE", -1, -1, s, status ), + &val, AST__FLOAT, "[] Redshift of source", status ); + +/* VELOSYS - topocentric apparent radial velocity of the standard of rest. */ + val = GetItem( &(store->velosys), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "VELOSYS", -1, -1, s, status ), + &val, AST__FLOAT, "[m/s] Topo. apparent velocity of rest frame", status ); + +/* RESTFRQ - rest frequency */ + val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFRQ", -1, -1, s, status ), + &val, AST__FLOAT, "[Hz] Rest frequency", status ); + +/* RESTWAV - rest wavelength */ + val = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, FormatKey( "RESTWAV", -1, -1, s, status ), + &val, AST__FLOAT, "[m] Rest wavelength", status ); + +/* The image frequency corresponding to the rest frequency (only used for + double sideband data). This is not part of the FITS-WCS standard but + is added for the benefit of JACH. */ + val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) { + SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status ); + } + +/* OBSGEO-X/Y/Z - observer's geocentric coords. Note, these always refer + to the primary axes. */ + if( s == ' ' ) { + val = GetItem( &(store->obsgeox), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, "OBSGEO-X", &val, AST__FLOAT, "[m] Observatory geocentric X", status ); + val = GetItem( &(store->obsgeoy), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, "OBSGEO-Y", &val, AST__FLOAT, "[m] Observatory geocentric Y", status ); + val = GetItem( &(store->obsgeoz), 0, 0, s, NULL, method, class, status ); + if( val != AST__BAD ) SetValue( this, "OBSGEO-Z", &val, AST__FLOAT, "[m] Observatory geocentric Z", status ); + } + +/* See if a Frame was sucessfully written to the FitsChan. */ +next: + ok = ok && astOK; + +/* If so, indicate we have something to return. */ + if( ok ) ret = 1; + +/* If we are producing secondary axes, clear any error status so we can + continue to produce the next Frame. Retain the error if the primary axes + could not be produced. After the primary axes, do the A axes. */ + if( s != ' ' ) { + astClearStatus; + } else { + s = 'A' - 1; + } + +/* Remove the secondary "new" flags from the FitsChan. This flag is + associated with cards which have been added to the FitsChan during + this pass through the main loop in this function. If the Frame was + written out succesfully, just clear the flags. If anything went wrong + with this Frame, remove the flagged cards from the FitsChan. */ + FixNew( this, NEW2, !ok, method, class, status ); + +/* Set the current card so that it points to the last WCS-related keyword + in the FitsChan (whether previously read or not). */ + FindWcs( this, 1, 1, 0, method, class, status ); + +/* Free resources. */ + tabaxis = astFree( tabaxis ); + } + +/* Return zero or ret depending on whether an error has occurred. */ + return astOK ? ret : 0; +} + +static AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s, + int naxes, const char *method, const char *class, int *status ){ + +/* +* Name: +* WcsIntWorld + +* Purpose: +* Create a Mapping from pixel coords to intermediate world coords. + +* Type: +* Private function. + +* Synopsis: + +* AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s, +* int naxes, const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function interprets the contents of the supplied FitsStore +* structure, and creates a Mapping which describes the transformation +* from pixel coordinates to intermediate world coordinates, using the +* FITS World Coordinate System conventions. This is a general linear +* transformation described by the CRPIXj, PCi_j and CDELTi keywords. + +* Parameters: +* this +* The FitsChan. ASTWARN cards may be added to this FitsChan if any +* anomalies are found in the keyword values in the FitsStore. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* naxes +* The number of intermediate world coordinate axes (WCSAXES). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping. +*/ + +/* Local Variables: */ + AstMapping *mapd1; /* Pointer to first distortion Mapping */ + AstMapping *mapd2; /* Pointer to second distortion Mapping */ + AstMapping *mapd3; /* Pointer to third distortion Mapping */ + AstMapping *mapd4; /* Pointer to fourth distortion Mapping */ + AstMapping *map0; /* Pointer to a Mapping */ + AstMapping *map1; /* Pointer to a Mapping */ + AstMapping *ret; /* Pointer to the returned Mapping */ + +/* Initialise the pointer to the returned Mapping. */ + ret = NULL; + +/* Check the global status. */ + if ( !astOK ) return ret; + +/* First of all, check the CTYPE keywords to see if they contain any known + distortion codes (following the syntax described in FITS-WCS paper IV). + If so, Mappings are returned which represents the distortions to be + applied at each point in the chain of Mappings produced by this function. + Any distortion codes are removed from the CTYPE values in the FitsStore. */ + DistortMaps( this, store, s, naxes, &mapd1, &mapd2, &mapd3, &mapd4, method, + class, status ); + +/* If distortion is to be applied now, initialise the returned Mapping to + be the distortion. */ + if( mapd1 ) ret = mapd1; + +/* Try to create a WinMap which translates the pixel coordinates so + that they are refered to an origin at the reference pixel. This + subtracts the value of CRPIXi from axis i. */ + map1 = (AstMapping *) WcsShift( store, s, naxes, method, class, status ); + +/* Combine this with any previous Mapping. */ + if( ret ) { + map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); + ret = astAnnul( ret ); + map1 = astAnnul( map1 ); + ret = map0; + } else { + ret = map1; + } + +/* If distortion is to be applied now, combine the two Mappings. */ + if( mapd2 ) { + map0 = (AstMapping *) astCmpMap( ret, mapd2, 1, "", status ); + ret = astAnnul( ret ); + mapd2 = astAnnul( mapd2 ); + ret = map0; + } + +/* Now try to create a MatrixMap to implement the PC matrix. Combine it + with the above Mapping. Add a Warning if this mapping cannot be inverted. */ + map1 = (AstMapping *) WcsPCMatrix( store, s, naxes, method, class, status ); + if( !astGetTranInverse( map1 ) ) { + Warn( this, "badmat", "The pixel rotation matrix in the original FITS " + "header (specified by CD or PC keywords) could not be inverted. " + "This may be because the matrix contains rows or columns which " + "are entirely zero.", method, class, status ); + } + map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); + ret = astAnnul( ret ); + map1 = astAnnul( map1 ); + ret = map0; + +/* If distortion is to be applied now, combine the two Mappings. */ + if( mapd3 ) { + map0 = (AstMapping *) astCmpMap( ret, mapd3, 1, "", status ); + ret = astAnnul( ret ); + mapd3 = astAnnul( mapd3 ); + ret = map0; + } + +/* Now try to create a diagonal MatrixMap to implement the CDELT scaling. + Combine it with the above Mapping. */ + map1 = (AstMapping *) WcsCDeltMatrix( store, s, naxes, method, class, status ); + map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status ); + ret = astAnnul( ret ); + map1 = astAnnul( map1 ); + ret = map0; + +/* If distortion is to be applied now, combine the two Mappings. */ + if( mapd4 ) { + map0 = (AstMapping *) astCmpMap( ret, mapd4, 1, "", status ); + ret = astAnnul( ret ); + mapd4 = astAnnul( mapd4 ); + ret = map0; + } + +/* Return the result. */ + return ret; +} + +static AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s, + AstFrame **frm, const char *method, + const char *class, int *status ){ + +/* +* Name: +* WcsMapFrm + +* Purpose: +* Create a Mapping and Frame for the WCS transformations described in a +* FITS header. + +* Type: +* Private function. + +* Synopsis: + +* AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s, +* AstFrame **frm, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function interprets the contents of the supplied FitsStore +* structure, and creates a Mapping which describes the transformation +* from pixel coordinates to world coordinates, using the FITS World +* Coordinate System conventions. It also creates a Frame describing +* the world coordinate axes. + +* Parameters: +* this +* The FitsChan. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* frm +* The address of a location at which to store a pointer to the +* Frame describing the world coordinate axes. If the Iwc attribute +* is non-zero, then this is actually a FrameSet in which the current +* Frame is the required WCS system. The FrameSet also contains one +* other Frame which defines the FITS IWC system. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping. +*/ + +/* Local Variables: */ + AstFrame *iwcfrm; /* Frame defining IWC system */ + AstFrameSet *fs; /* Pointer to returned FrameSet */ + AstMapping *map10; /* Pointer to a Mapping */ + AstMapping *map1; /* Pointer to a Mapping */ + AstMapping *map2; /* Pointer to a Mapping */ + AstMapping *map3; /* Pointer to a Mapping */ + AstMapping *map4; /* Pointer to a Mapping */ + AstMapping *map5; /* Pointer to a Mapping */ + AstMapping *map6; /* Pointer to a Mapping */ + AstMapping *map7; /* Pointer to a Mapping */ + AstMapping *map8; /* Pointer to a Mapping */ + AstMapping *map9; /* Pointer to a Mapping */ + AstMapping *ret; /* Pointer to the returned Mapping */ + AstMapping *tabmap; /* Mapping from psi to WCS (paper III - 6.1.2) */ + AstSkyFrame *reffrm; /* SkyFrame defining reflon and reflat */ + char id[2]; /* ID string for returned Frame */ + char iwc[5]; /* Domain name for IWC Frame */ + const char *cc; /* Pointer to Domain */ + double dut1; /* UT1-UTC correction in days */ + double dval; /* Temporary double value */ + double reflat; /* Reference celestial latitude */ + double reflon; /* Reference celestial longitude */ + int *tabaxis; /* Flags indicating -TAB axes */ + int wcsaxes; /* Number of physical axes */ + +/* Initialise the pointer to the returned Mapping. */ + ret = NULL; + +/* Check the global status. */ + if ( !astOK ) return ret; + +/* Identify any axes that use the -TAB algoritm code described in FITS-WCS + paper III, and convert their CTYPE values to describe linear axes + (i.e. just remove "-TAB" from the CTYPE value). This also returns a + Mapping (which includes one or more LutMaps) that should be applied to + the resulting linear axis values in order to generate the final WCS + axis values. A NULL pointer is returned if no axes use -TAB. */ + tabmap = TabMapping( this, store, s, &tabaxis, method, class, status ); + +/* Obtain the number of physical axes in the header. If the WCSAXES header + was specified, use it. Otherwise assume it is the same as the number + of pixel axes. */ + dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status ); + if( dval != AST__BAD ) { + wcsaxes = (int) dval + 0.5; + } else { + wcsaxes = store->naxis; + } + +/* Create a simple Frame to represent IWC coords. */ + iwcfrm = astFrame( wcsaxes, "Title=FITS Intermediate World Coordinates", status ); + strcpy( iwc, "IWC" ); + iwc[ 3 ]= s; + iwc[ 4 ]= 0; + astSetDomain( iwcfrm, iwc ); + +/* Create a simple Frame which will be used as the initial representation + for the physical axes. This Frame will be changed later (or possibly + replaced by a Frame of another class) when we know what type of + physical axes we are dealing with. Set its Domain to "AST_FITSCHAN" + This value is used to identify axes which have not been changed, + and will be replaced before returning the final FrameSet. */ + *frm = astFrame( wcsaxes, "Domain=AST_FITSCHAN", status ); + +/* Store the coordinate version character as the Ident attribute for the + returned Frame. */ + id[ 0 ] = s; + id[ 1 ] = 0; + astSetIdent( *frm, id ); + +/* Create a Mapping which goes from pixel coordinates to what FITS-WCS + paper I calls "intermediate world coordinates". This stage is the same + for all axes. It uses the CRPIXj, PCi_j and CDELTi headers (and + distortion codes from the CTYPE keywords). */ + map1 = WcsIntWorld( this, store, s, wcsaxes, method, class, status ); + +/* The conversion from intermediate world coordinates to the final world + coordinates depends on the type of axis being converted (as specified + by its CTYPE keyword). Check for each type of axis for which known + conventions exist... */ + +/* Celestial coordinate axes. The following call returns a Mapping which + transforms any celestial coordinate axes from intermediate world + coordinates to the final celestial coordinates. Other axes are left + unchanged by the Mapping. It also modifies the Frame so that a + SkyFrame is used to describe the celestial axes. */ + map2 = WcsCelestial( this, store, s, frm, iwcfrm, &reflon, &reflat, + &reffrm, &tabmap, tabaxis, method, class, status ); + +/* Spectral coordinate axes. The following call returns a Mapping which + transforms any spectral coordinate axes from intermediate world + coordinates to the final spectral coordinates. Other axes are left + unchanged by the Mapping. It also modifies the Frame so that a + SpecFrame is used to describe the spectral axes. */ + map3 = WcsSpectral( this, store, s, frm, iwcfrm, reflon, reflat, reffrm, + method, class, status ); + +/* Any axes which were not recognized by the above calls are assumed to + be linear. Create a Mapping which adds on the reference value for such + axes, and modify the Frame to desribe the axes. */ + map4 = WcsOthers( this, store, s, frm, iwcfrm, method, class, status ); + +/* If the Frame still has the Domain "AST_FITSCHAN", clear it. */ + cc = astGetDomain( *frm ); + if( cc && !strcmp( cc, "AST_FITSCHAN" ) ) astClearDomain( *frm ); + +/* Concatenate the Mappings and simplify the result. */ + map5 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + map6 = (AstMapping *) astCmpMap( map5, map3, 1, "", status ); + map7 = (AstMapping *) astCmpMap( map6, map4, 1, "", status ); + if( tabmap ) { + map8 = (AstMapping *) astCmpMap( map7, tabmap, 1, "", status ); + } else { + map8 = astClone( map7 ); + } + + ret = astSimplify( map8 ); + +/* Ensure that the coordinate version character is stored as the Ident + attribute for the returned Frame (the above calls may have changed it). */ + astSetIdent( *frm, id ); + +/* Set the DUT1 value. Note, the JACH store DUT1 in units of days in their + FITS headers, so convert from days to seconds. May need to do somthing + about this if the forthcoming FITS-WCS paper 5 (time axes) defines DUT1 + to be in seconds. */ + dut1 = GetItem( &(store->dut1), 0, 0, s, NULL, method, class, status ); + if( dut1 != AST__BAD ) astSetDut1( *frm, dut1*SPD ); + +/* The returned Frame is actually a FrameSet in which the current Frame + is the required WCS Frame. The FrameSet contains one other Frame, + which is the Frame representing IWC. Create a FrameSet containing these + two Frames. */ + if( astGetIwc( this ) ) { + fs = astFrameSet( iwcfrm, "", status ); + astInvert( map1 ); + map9 = (AstMapping *) astCmpMap( map1, ret, 1, "", status ); + astInvert( map1 ); + map10 = astSimplify( map9 ); + astAddFrame( fs, AST__BASE, map10, *frm ); + +/* Return this FrameSet instead of the Frame. */ + *frm = astAnnul( *frm ); + *frm = (AstFrame *) fs; + +/* Free resources */ + map9 = astAnnul( map9 ); + map10 = astAnnul( map10 ); + } + +/* Annull temporary resources. */ + if( reffrm ) reffrm = astAnnul( reffrm ); + if( tabmap ) tabmap = astAnnul( tabmap ); + tabaxis = astFree( tabaxis ); + iwcfrm = astAnnul( iwcfrm ); + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + map4 = astAnnul( map4 ); + map5 = astAnnul( map5 ); + map6 = astAnnul( map6 ); + map7 = astAnnul( map7 ); + map8 = astAnnul( map8 ); + +/* Annul thre returned objects if an error has occurred. */ + if( !astOK ) { + ret = astAnnul( ret ); + *frm = astAnnul( *frm ); + } + +/* Return the result. */ + return ret; +} + +static AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes, + const char *method, const char *class, int *status ){ +/* +* Name: +* WcsPCMatrix + +* Purpose: +* Create a MatrixMap representing the PC matrix. + +* Type: +* Private function. + +* Synopsis: +* AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A MatrixMap representing the FITS "PC" matrix is returned. + +* Parameters: +* store +* A structure containing values for FITS keywords relating to +* the World Coordinate System. +* s +* A character s identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* naxes +* The number of intermediate world coordinate axes (WCSAXES). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the created MatrixMap or a NULL pointer if an +* error occurred. +*/ + +/* Local Variables: */ + AstMatrixMap *new; /* The created MatrixMap */ + double *el; /* Pointer to next matrix element */ + double *mat; /* Pointer to matrix array */ + int i; /* Pixel axis index */ + int j; /* Intermediate axis index. */ + +/* Initialise/ */ + new = NULL; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Allocate memory for the matrix. */ + mat = (double *) astMalloc( sizeof(double)*naxes*naxes ); + if( astOK ){ + +/* Fill the matrix with values from the FitsStore. */ + el = mat; + for( i = 0; i < naxes; i++ ){ + for( j = 0; j < naxes; j++ ){ + +/* Get the PCj_i value for this axis. Missing terms can be defaulted so + do not report an error if the required value is not present in the + FitsStore. */ + *el = GetItem( &(store->pc), i, j, s, NULL, method, class, status ); + +/* Diagonal terms default to to 1.0, off-diagonal to zero. */ + if( *el == AST__BAD ) *el = ( i == j ) ? 1.0: 0.0; + +/* Move on to the next matrix element. */ + el++; + } + } + +/* Create the matrix. */ + new = astMatrixMap( naxes, naxes, 0, mat, "", status ); + +/* Report an error if the inverse transformation is undefined. */ + if( !astGetTranInverse( new ) && astOK ) { + astError( AST__BDFTS, "%s(%s): Unusable rotation matrix (PC or CD) found " + "in the FITS-WCS header - the matrix cannot be inverted.", status, method, class ); + } + +/* Release the memory used to hold the matrix. */ + mat = (double *) astFree( (void *) mat ); + } + +/* If an error has occurred, attempt to annul the returned MatrixMap. */ + if( !astOK ) new = astAnnul( new ); + +/* Return the MatrixMap. */ + return new; +} + +static AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s, + AstWcsMap *wcsmap, int fits_ilon, int fits_ilat, + const char *method, const char *class, int *status ){ + +/* +* Name: +* WcsNative + +* Purpose: +* Create a CmpMap which transforms Native Spherical Coords to +* Celestial Coords. + +* Type: +* Private function. + +* Synopsis: + +* AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s, +* AstWcsMap *wcsmap, int fits_ilon, int fits_ilat, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A CmpMap is created which rotates the supplied Native Spherical Coords +* into Celestial Coords in the standard system specified by the CTYPE +* keywords. Any non-celestial axes are left unchanged. +* +* At the highest level, the returned CmpMap is made up of the following + +* Mappings in series (if celestial long/lat axes are present): +* 1 - A PermMap which rearranges the axes so that the longitude axis is +* axis 0, the latitude axis is axis 1, and all other axes are +* stored at higher indices, starting at axis 2. +* 2 - A CmpMap which converts the values on axes 0 and 1 from Native +* Spherical to Celestial coordinates, leaving all other axes +* unchanged. +* 3 - A PermMap which rearranges the axes to put the longitude and +* latitude axes back in their original places. This is just the +* inverse of the PermMap used at stage 1 above. +* +* The CmpMap used at stage 2 above, is made up of two Mappings in + +* parallel: +* 4 - A CmpMap which maps axes 0 and 1 from Native Spherical to +* Celestial coordinates. +* 5 - A UnitMap which passes on the values to axes 2, 3, etc, +* without change. +* +* The CmpMap used at stage 4 above, is made up of the following Mappings + +* in series: +* 6 - A SphMap which converts the supplied spherical coordinates into +* Cartesian Coordinates. +* 7 - A MatrixMap which rotates the Cartesian coordinates from the +* Native to the Celestial system. +* 8 - A SphMap which converts the resulting Cartesian coordinates back +* to spherical coordinates. + +* Parameters: +* this +* The FitsChan in which to store any warning cards. If NULL, no +* warnings are stored. +* store +* A structure containing values for FITS keywords relating to +* the World Coordinate System. +* s +* Co-ordinate version character to use (space means primary axes). +* wcsmap +* A mapping describing the deprojection which is being used. This is +* needed in order to be able to locate the fiducial point within the +* Native Speherical Coordinate system, since it varies from projection +* to projection. +* fits_ilon +* The zero-based FITS WCS axis index corresponding to celestial +* longitude (i.e. one less than the value of "i" in the keyword +* names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of +* the longitude axis in the supplied WcsMap is used. +* fits_ilat +* The zero-based FITS WCS axis index corresponding to celestial +* latitude (i.e. one less than the value of "i" in the keyword +* names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of +* the latitude axis in the supplied WcsMap is used. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the created CmpMap or a NULL pointer if an error occurred. + +* Notes: +* - The local variable names correspond to the notation in the papers +* by Greisen & Calabretta describing the FITS WCS system. +*/ + +/* Local Variables: */ + AstCmpMap *cmpmap; /* A CmpMap */ + AstMapping *new; /* The returned CmpMap */ + AstMatrixMap *matmap2; /* Another MatrixMap */ + AstMatrixMap *matmap; /* A MatrixMap */ + AstPermMap *permmap; /* A PermMap */ + AstSphMap *sphmap; /* A SphMap */ + AstUnitMap *unitmap; /* A UnitMap */ + char buf[150]; /* Message buffer */ + double alpha0; /* Long. of fiduaicl point in standard system */ + double alphap; /* Long. of native nth pole in standard system */ + double axis[3]; /* Vector giving the axis of rotation */ + double delta0; /* Lat. of fiducial point in standard system */ + double deltap; /* Lat. of native nth pole in standard system */ + double latpole; /* Lat. of native nth pole in standard system if deltap undefined */ + double phip; /* Long. of standard nth pole in native system */ + double phi0; /* Native longitude at fiducial point */ + double theta0; /* Native latitude at fiducial point */ + int *inperm; /* Pointer to array of output axis indices */ + int *outperm; /* Pointer to array of input axis indices */ + int axlat; /* Index of latitude physical axis */ + int axlon; /* Index of longitude physical axis */ + int i; /* Loop count */ + int nax_rem; /* No. of non-astrometric axes */ + int naxis; /* No. of axes. */ + int new_axlat; /* Index of lat. physical axis after perming */ + int tpn; /* Is this a TPN projection? */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned CmpMap pointer. */ + new = NULL; + +/* Store the number of axes in a local variable. */ + naxis = astGetNin( wcsmap ); + +/* Get the indices of the celestial axes. */ + axlon = astGetWcsAxis( wcsmap, 0 ); + axlat = astGetWcsAxis( wcsmap, 1 ); + +/* If the corresponding FITS axis indices were not supplied, use the + WcsMap axes found above. */ + if( fits_ilon == -1 ) fits_ilon = axlon; + if( fits_ilat == -1 ) fits_ilat = axlat; + +/* If there is no longitude or latitude axis, or if we have a + non-celestial projection, just return a UnitMap. */ + if( axlon == axlat || astGetWcsType( wcsmap ) == AST__WCSBAD ){ + new = (AstMapping *) astUnitMap( naxis, "", status ); + +/* If there is a lon/lat axis pair, create the inperm and outperm arrays + which will be needed later to create the PermMap which reorganises + the axes so that axis zero is the longitude axis and axis 1 is the + latitude axis. */ + } else { + +/* Get storage for the two arrays. */ + inperm = (int *) astMalloc( sizeof( int )*(size_t)naxis ); + outperm = (int *) astMalloc( sizeof( int )*(size_t)naxis ); + if( astOK ){ + +/* Initialise an array holding the indices of the input axes which are copied + to each output axis. Initially assume that there is no re-arranging of + the axes. */ + for( i = 0; i < naxis; i++ ) outperm[ i ] = i; + +/* Swap the longitude axis and axis 0. */ + i = outperm[ axlon ]; + outperm[ axlon ] = outperm[ 0 ]; + outperm[ 0 ] = i; + +/* If axis 0 was originally the latitude axis, the latitude axis will now + be where the longitude axis was originally (because of the above axis + swap). */ + if( axlat == 0 ) { + new_axlat = axlon; + } else { + new_axlat = axlat; + } + +/* Swap the latitude axis and axis 1. */ + i = outperm[ new_axlat ]; + outperm[ new_axlat ] = outperm[ 1 ]; + outperm[ 1 ] = i; + +/* Create the array holding the output axis index corresponding to + each input axis. */ + for( i = 0; i < naxis; i++ ) inperm[ outperm[ i ] ] = i; + } + +/* Store the latitude and longitude (in the standard system) of the fiducial + point, in radians. */ + delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status ); + if( delta0 == AST__BAD ) delta0 = 0.0; + delta0 *= AST__DD2R; + alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status ); + if( alpha0 == AST__BAD ) alpha0 = 0.0; + alpha0 *= AST__DD2R; + +/* Limit the latitude to the range +/- PI/2, issuing a warning if the + supplied CRVAL value is outside this range. The "alphap" variable is used + as workspace here. */ + alphap = palDrange( delta0 ); + delta0 = alphap; + if ( delta0 > AST__DPIBY2 ){ + delta0 = AST__DPIBY2; + } else if ( delta0 < -AST__DPIBY2 ){ + delta0 = -AST__DPIBY2; + } + if( alphap != delta0 ) { + sprintf( buf, "The original FITS header specified a fiducial " + "point with latitude %.*g. A value of %.*g is being used " + "instead. ", DBL_DIG, alphap*AST__DR2D, DBL_DIG, + delta0*AST__DR2D ); + Warn( this, "badlat", buf, method, class, status ); + } + +/* Set a flag indicating if we have a TPN projection. The handling or + projection parameters is different for TPN projections. */ + tpn = ( astGetWcsType( wcsmap ) == AST__TPN ); + +/* Store the radian values of the FITS keywords LONPOLE and LATPOLE. Defaults + will be used if either of these items was not supplied. These keyword + values may be stored in projection parameters PVi_3a and PVi_4a for + longitude axis "i" - in which case the "PV" values take precedence over + the "LONPOLE" and "LATPOLE" values. Do not do this for TPN projections + since they use these projection parameters to specify correcton terms. */ + if( astTestPV( wcsmap, axlon, 3 ) && !tpn ) { + phip = astGetPV( wcsmap, axlon, 3 ); + } else { + phip = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status ); + if( phip != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 3, phip ); + } + if( phip != AST__BAD ) phip *= AST__DD2R; + if( astTestPV( wcsmap, axlon, 4 ) && !tpn ) { + latpole = astGetPV( wcsmap, axlon, 4 ); + } else { + latpole = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status ); + if( latpole != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 4, latpole ); + } + if( latpole != AST__BAD ) latpole *= AST__DD2R; + +/* Find the standard Celestial Coordinates of the north pole of the Native + Spherical Coordinate system. Report an error if the position was not + defined. */ + if( !WcsNatPole( this, wcsmap, alpha0, delta0, latpole, &phip, &alphap, + &deltap, status ) && astOK ){ + astError( AST__BDFTS, "%s(%s): Conversion from FITS WCS native " + "coordinates to celestial coordinates is ill-conditioned.", status, + method, class ); + } + +/* Create the SphMap which converts spherical coordinates to Cartesian + coordinates (stage 6 in the prologue). This asumes that axis 0 is the + longitude axis, and axis 1 is the latitude axis. This will be ensured + by a PermMap created later. Indicate that the SphMap will only be used + to transform points on a unit sphere. This enables a forward SphMap + to be combined with an inverse SphMap into a UnitMap, and thus aids + simplification. */ + sphmap = astSphMap( "UnitRadius=1", status ); + astInvert( sphmap ); + +/* Set the PolarLong attribute of the SphMap so that a longitude of phi0 (the + native longitude of the fiducial point) is returned by the inverse + transformation (cartesian->spherical) at either pole. */ + GetFiducialNSC( wcsmap, &phi0, &theta0, status ); + astSetPolarLong( sphmap, phi0 ); + +/* Create a unit MatrixMap to be the basis of the MatrixMap which rotates + Native Spherical Coords to Celestial Coords (stage 7 in the prologue). */ + matmap = astMatrixMap( 3, 3, 2, NULL, "", status ); + +/* Modify the above MatrixMap so that it rotates the Cartesian position vectors + by -phip (i.e. LONPOLE) about the Z axis. This puts the north pole of the + standard system at zero longitude in the rotated system. Then annul the + original MatrixMap and use the new one instead. */ + axis[ 0 ] = 0; + axis[ 1 ] = 0; + axis[ 2 ] = 1; + matmap2 = astMtrRot( matmap, -phip, axis ); + matmap = astAnnul( matmap ); + matmap = matmap2; + +/* Now modify the above MatrixMap so that it rotates the Cartesian position + vectors by -(PI/2-deltap) about the Y axis. This puts the north pole of + the standard system as 90 degrees latitude in the rotated system. Then annul + the original MatrixMap and use the new one instead. */ + axis[ 0 ] = 0; + axis[ 1 ] = 1; + axis[ 2 ] = 0; + matmap2 = astMtrRot( matmap, deltap - AST__DPIBY2, axis ); + matmap = astAnnul( matmap ); + matmap = matmap2; + +/* Finally modify the above MatrixMap so that it rotates the Cartesian position + vectors (PI+alphap) about the Z axis. This puts the primary meridian of the + standard system at zero longitude in the rotated system. This results in the + rotated system being coincident with the standard system. */ + axis[ 0 ] = 0; + axis[ 1 ] = 0; + axis[ 2 ] = 1; + matmap2 = astMtrRot( matmap, AST__DPI + alphap, axis ); + matmap = astAnnul( matmap ); + matmap = matmap2; + +/* Combine the SphMap (stage 6) and MatrixMap (stage 7) in series. */ + cmpmap = astCmpMap( sphmap, matmap, 1, "", status ); + sphmap = astAnnul( sphmap ); + matmap = astAnnul( matmap ); + +/* Create a new SphMap which converts Cartesian coordinates to spherical + coordinates (stage 8 in the prologue). Indicate that the SphMap will + only be used to transform points on a unit sphere. */ + sphmap = astSphMap( "UnitRadius=1", status ); + +/* Set the PolarLong attribute of the SphMap so that a longitude of alpha0 + (the celestial longitude of the fiducial point) is returned by the + forward transformation (cartesian->spherical) at either pole. */ + astSetPolarLong( sphmap, alpha0 ); + +/* Add it to the compound mapping. The CmpMap then corresponds to stage 4 + in the prologue. Annul the constituent mappings. */ + new = (AstMapping *) astCmpMap( cmpmap, sphmap, 1, "", status ); + cmpmap = astAnnul( cmpmap ); + sphmap = astAnnul( sphmap ); + +/* If there are any remaining axes (i.e. axes which do not describe a + Celestial coordinate system), create a UnitMap which passes on their + values unchanged (stage 5 in the prologue), and add it the CmpMap, + putting it in parallel with the earlier mappings. The resulting CmpMap + then corresponds to stage 2 in the prologue. Note, the axis numbering + used by this UnitMap needs to take account of the fact that it is only + applied to the non-celestial axes. The axes are re-ordered by the + PermMap described at stage 1 in the prologue. */ + nax_rem = naxis - 2; + if( nax_rem > 0 ){ + unitmap = astUnitMap( nax_rem, "", status ); + cmpmap = astCmpMap( new, unitmap, 0, "", status ); + new = astAnnul( new ); + unitmap = astAnnul( unitmap ); + new = (AstMapping *) cmpmap; + } + +/* Now we need to ensure that axes 0 and 1 correspond to longitude and + latitude. If this is already the case, then the CmpMap can be returned + as it is. Otherwise, a PermMap needs to be created to rearrange the + axes. */ + if( axlon != 0 || axlat != 1 ){ + +/* Create the PermMap using the inperm and outperm arrays created earlier. + This is the mapping described as stage 1 in the prologue. */ + permmap = astPermMap( naxis, inperm, naxis, outperm, NULL, "", status ); + +/* Compound this PermMap and the CmpMap corresponding to stage 2 (created + earlier) in series. */ + cmpmap = astCmpMap( permmap, new, 1, "", status ); + new = astAnnul( new ); + new = (AstMapping *) cmpmap; + +/* Now invert the PermMap, so that it re-arranges the axes back into + their original order. This is the mapping described as stage 3 in + the prologue. */ + astInvert( permmap ); + +/* And finally.... add this inverted PermMap onto the end of the CmpMap. */ + cmpmap = astCmpMap( new, permmap, 1, "", status ); + permmap = astAnnul( permmap ); + new = astAnnul( new ); + new = (AstMapping *) cmpmap; + } + +/* Free the temporary arrays. */ + inperm = (int *) astFree( (void *) inperm ); + outperm = (int *) astFree( (void *) outperm ); + } + +/* If an error has occurred, attempt to annul the new CmpMap. */ + if( !astOK ) new = astAnnul( new ); + +/* Return the CmpMap. */ + return new; +} + +static int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0, + double delta0, double latpole, double *phip, + double *alphap, double *deltap, int *status ){ + +/* +* Name: +* WcsNatPole + +* Purpose: +* Find the celestial coordinates of the north pole of the Native Spherical +* Coordinate system. + +* Type: +* Private function. + +* Synopsis: + +* int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0, +* double delta0, double latpole, double *phip, +* double *alphap, double *deltap, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* The supplied WcsMap converts projected positions given in +* "Projection Plane Coords" to positions in the "Native Spherical +* Coordinate" system. This function finds the pole of this spherical +* coordinate system in terms of the standard celestial coordinate +* system to which the CRVALi, LONPOLE and LATPOLE keywords refer (this +* system should be identified by characters 5-8 of the CTYPEi +* keywords). It also supplies a default value for LONPOLE if no value +* has been supplied explicitly in the FITS header. +* +* This function implements equations 8, 9 and 10 from the FITS-WCS paper +* II by Calabretta & Greisen (plus the associated treatment of special +* cases). The paper provides more detailed documentation for the +* mathematics implemented by this function. + +* Parameters: +* this +* The FitsChan in which to store any warning cards. If NULL, no +* warnings are stored. +* wcsmap +* A mapping describing the deprojection being used (i.e. the +* mapping from Projection Plane Coords to Native Spherical Coords). +* alpha0 +* The longitude of the fiducial point in the standard celestial +* coordinate frame (in radians). Note, this fiducial point does +* not necessarily correspond to the point given by keywords CRPIXj. +* delta0 +* The celestial latitude (radians) of the fiducial point. +* latpole +* The value of FITS keyword LATPOLE, converted to radians, or the +* symbolic constant AST__BAD if the keyword was not supplied. +* phip +* Pointer to a location at which is stored the longitude of the north +* pole of the standard Celestial coordinate system, as measured in +* the Native Spherical Coordinate system, in radians. This should be +* supplied holding the radian equivalent of the value of the FITS +* keyword LONPOLE, or the symbolic constant AST__BAD if the keyword was +* not supplied (in which case a default value will be returned at the +* given location). +* alphap +* Pointer to a location at which to store the calculated longitude +* of the Native North Pole, in radians. +* deltap +* Pointer to a location at which to store the calculated latitude +* of the Native North Pole, in radians. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A status: non-zero for success, zero if the position of the native +* north pole is undefined. + +* Notes: +* - Certain combinations of keyword values result in the latitude of +* the Native North Pole being undefined. In these cases, a value of +* 0 is returned for the function value, but no error is reported. +* - All angular values used by this function are in radians. +* - A value of 0 is returned if an error has already occurred. +*/ + +/* Local Variables: */ + char buf[150]; /* Buffer for warning message */ + double cos_theta0; /* Cosine of theta0 */ + double cos_phip; /* Cosine of (phip - phi0) */ + double cos_delta0; /* Cosine of delta0 */ + double cos_deltap; /* Cosine of deltap */ + double deltap_1; /* First possible value for deltap */ + double deltap_2; /* Second possible value for deltap */ + double sin_theta0; /* Sine of theta0 */ + double sin_phip; /* Sine of (phip - phi0) */ + double sin_delta0; /* Sine of delta0 */ + double sin_deltap; /* Sine of deltap */ + double t0, t1, t2, t3, t4; /* Intermediate values */ + double phi0, theta0; /* Native coords of fiducial point */ + +/* Check the global status. */ + if ( !astOK ) return 0; + +/* Get the longitude and latitude of the fiducial point in the native + spherical coordinate frame (in radians). */ + GetFiducialNSC( wcsmap, &phi0, &theta0, status ); + +/* If no value was supplied for the FITS keyword LONPOLE, set up a default + value such that the celestial latitude increases in the same direction + as the native latitude at the fiducial; point. */ + if( *phip == AST__BAD ){ + if( delta0 >= theta0 ){ + *phip = 0.0; + } else { + *phip = AST__DPI; + } + +/* Issue a warning that a default lonpole value has been adopted. */ + sprintf( buf, "The original FITS header did not specify the " + "longitude of the native north pole. A default value " + "of %.8g degrees was assumed.", (*phip)*AST__DR2D ); + Warn( this, "nolonpole", buf, "astRead", "FitsChan", status ); + } + +/* If the fiducial point is coincident with the Native North Pole, then the + Native North Pole must have the same coordinates as the fiducial + point. Tests for equality include some tolerance to allow for rounding + errors. */ + sin_theta0 = sin( theta0 ); + if( astEQUAL( sin_theta0, 1.0 ) ){ + *alphap = alpha0; + *deltap = delta0; + +/* If the fiducial point is concident with the Native South Pole, then the + Native North Pole must have the coordinates of the point diametrically + opposite the fiducial point. */ + } else if( astEQUAL( sin_theta0, -1.0 ) ){ + *alphap = alpha0 + AST__DPI; + *deltap = -delta0; + +/* For all other cases, go through the procedure described in the WCS paper + by Greisen & Calabretta, to find the position of the Native North Pole. + First store some useful values. */ + } else { + cos_theta0 = cos( theta0 ); + cos_delta0 = cos( delta0 ); + cos_phip = cos( *phip - phi0 ); + sin_delta0 = sin( delta0 ); + sin_phip = sin( *phip - phi0 ); + +/* Next, find the two possible values for the latitude of the Native + North Pole (deltap). If any stage of this transformation is + indeterminate, return zero (except for the single special case noted + in item 6 para. 2 of the WCS paper, for which LATPOLE specifies the + values to be used). */ + t0 = cos_theta0*cos_phip; + if( fabs( t0 ) < TOL2 && fabs( sin_theta0 ) < TOL2 ){ + if( latpole != AST__BAD ) { + *deltap = latpole; + } else { + return 0; + } + } else { + t1 = atan2( sin_theta0, t0 ); + t2 = cos_theta0*cos_phip; + t2 *= t2; + t2 += sin_theta0*sin_theta0; + if( t2 <= DBL_MIN ){ + return 0; + } else { + t3 = sin_delta0/sqrt( t2 ); + if( fabs( t3 ) > 1.0 + TOL1 ){ + return 0; + } else { + if( t3 < -1.0 ){ + t4 = AST__DPI; + } else if( t3 > 1.0 ){ + t4 = 0.0; + } else { + t4 = acos( t3 ); + } + deltap_1 = palDrange( t1 + t4 ); + deltap_2 = palDrange( t1 - t4 ); + +/* Select which of these two values of deltap to use. Values outside the + range +/- PI/2 cannot be used. If both values are within this range + use the value which is closest to the supplied value of latpole (or + use the northern most value if the LATPOLE keyword was not supplied. */ + if( fabs( deltap_1 ) > AST__DPIBY2 + TOL2 ){ + *deltap = deltap_2; + } else if( fabs( deltap_2 ) > AST__DPIBY2 + TOL2 ){ + *deltap = deltap_1; + } else { + if( latpole != AST__BAD ){ + if( fabs( deltap_1 - latpole ) < + fabs( deltap_2 - latpole ) ){ + *deltap = deltap_1; + } else { + *deltap = deltap_2; + } + } else { + if( deltap_1 > deltap_2 ){ + *deltap = deltap_1; + } else { + *deltap = deltap_2; + } + +/* Issue a warning that a default latpole value has been adopted. */ + sprintf( buf, "The original FITS header did not specify " + "the latitude of the native north pole. A " + "default value of %.8g degrees was assumed.", + (*deltap)*AST__DR2D ); + Warn( this, "nolatpole", buf, "astRead", "FitsChan", status ); + } + } + if( fabs( *deltap ) > AST__DPIBY2 + TOL2 ) { + return 0; + } else if( *deltap < -AST__DPIBY2 ){ + *deltap = -AST__DPIBY2; + } else if( *deltap > AST__DPIBY2 ){ + *deltap = AST__DPIBY2; + } + } + } + } + +/* If a valid value for the latitude (deltap) has been found, find the + longitude of the Native North Pole. */ + if( *deltap != AST__BAD ) { + if( fabs( cos_delta0) > TOL2 ){ + cos_deltap = cos( *deltap ); + sin_deltap = sin( *deltap ); + if( fabs( cos_deltap ) > TOL2 ){ + t1 = sin_phip*cos_theta0/cos_delta0; + t2 = ( sin_theta0 - sin_deltap*sin_delta0 ) + /( cos_delta0*cos_deltap ); + if( ( fabs( t1 ) > TOL2 ) || ( fabs( t2 ) > TOL2 ) ){ + *alphap = alpha0 - atan2( t1, t2 ); + } else { + *alphap = alpha0; + } + } else if( sin_deltap > 0.0 ){ + *alphap = alpha0 + (*phip - phi0) - AST__DPI; + } else { + *alphap = alpha0 - (*phip - phi0); + } + } else { + *alphap = alpha0; + } + } else { + *alphap = AST__BAD; + } + } + +/* Return a success status if valid latitude and longitude values were + found. */ + return (*deltap) != AST__BAD && (*alphap) != AST__BAD ; +} + +static AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s, + AstFrame **frm, AstFrame *iwcfrm, const char *method, + const char *class, int *status ){ + +/* +* Name: +* WcsOthers + +* Purpose: +* Create a Mapping from intermediate world coords to any axes +* which are not covered by specialised conventions. + +* Type: +* Private function. + +* Synopsis: + +* AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s, +* AstFrame **frm, AstFrame *iwcfrm, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function interprets the contents of the supplied FitsStore +* structure, looking for world coordinate axes for which no +* description has yet been added to the supplied Frame . It is +* assumed that any such axes are simple linear axes. It returns a +* Mapping which simply adds on the CRVAL values to such axes. +* It also modifies the supplied Frame to describe the axes. + +* Parameters: +* this +* The FitsChan. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* frm +* The address of a location at which to store a pointer to the +* Frame describing the world coordinate axes. +* iwcfrm +* A pointer to the Frame describing the intermediate world coordinate +* axes. The properties of this Frame may be changed on exit. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping. +*/ + +/* Local Variables: */ + AstFrame *pfrm; /* Pointer to primary Frame */ + AstFrame *pfrm2; /* Pointer to primary Frame */ + AstMapping *map1; /* Pointer to a Mapping */ + AstMapping *map2; /* Pointer to a Mapping */ + AstMapping *ret; /* The returned Mapping */ + char **comms; /* Pointer to array of CTYPE commments */ + char buf[ 100 ]; /* Buffer for textual attribute value */ + char buf2[ 100 ]; /* Buffer for textual attribute value */ + char buf3[ 20 ]; /* Buffer for default CTYPE value */ + char *newdom; /* Pointer to new Domain value */ + const char *ckeyval; /* Pointer to character keyword value */ + int i; /* Axis index */ + int j; /* Axis index */ + int len; /* Used length of string */ + int naxes; /* no. of axes in Frame */ + int nother; /* The number of "other" axes */ + int paxis; /* Primary axis index */ + int usecom; /* Use CTYPE comments as axis Labels? */ + +/* Initialise the pointer to the returned Mapping. */ + ret = NULL; + +/* Check the global status. */ + if ( !astOK ) return ret; + +/* Get the number of physical axes. */ + naxes = astGetNaxes( *frm ); + +/* Assume we will use CTYPE comments as the axis labels. */ + usecom = 1; + +/* Initialise the count of "other" axes. */ + nother = 0; + +/* Get the comments associated with the CTYPE keywords for all "other" + axes. */ + comms = astMalloc( naxes*sizeof( char * ) ); + if( comms ) { + +/* Loop round all axes in the Frame, and initialise the pointer to its + comment. */ + for( i = 0; i < naxes; i++ ){ + comms[ i ] = NULL; + +/* Get the Domain for the primary frame containing the axis. This will be + "AST_FITSCHAN" if the axis has not yet been recognised (this Domain is + set up by WcsMapFrm). Only consider the axis further if the Domain has + not been changed. */ + astPrimaryFrame( *frm, i, &pfrm, &paxis ); + if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) { + +/* Increment the count of "other" axes. */ + nother++; + +/* Get the comment associated with the CTYPE header. */ + ckeyval = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status ); + +/* If this axis has no CTYPE comment, we will use CTYPE values as axis + labels (if given, the CNAME keyword take precedence). */ + if( !ckeyval || astChrLen( ckeyval ) == 0 ) { + usecom = 0; + +/* If the CTYPE comment for this axis is the same as any other comment, we + will use CTYPE values as axis labels. */ + } else { + for( j = 0; j < nother - 1; j++ ) { + if( comms[ j ] && !strcmp( ckeyval, comms[ j ] ) ) { + usecom = 0; + break; + } + } + } + +/* If we are still using comments as axis labels, store a copy of it in the + workspace. */ + if( usecom ) comms[ i ] = astStore( NULL, ckeyval, + strlen( ckeyval ) + 1 ); + } + pfrm = astAnnul( pfrm ); + } + +/* Free the workspace holding comments. */ + for( i = 0; i < naxes; i++ ) comms[ i ] = astFree( comms[ i ] ); + comms = astFree( comms ); + } + +/* If there are no "other" axes, just return a UnitMap. */ + if( nother == 0 ) { + ret = (AstMapping *) astUnitMap( naxes, "", status ); + +/* Otherwise... */ + } else { + +/* If we have only a single other axis, use CTYPE value instead of + comment. */ + if( nother == 1 ) usecom = 0; + +/* Not yet started a new Domain value to replace "AST_FITSCHAN". */ + newdom = NULL; + pfrm2 = NULL; + +/* Check each axis of the Frame looking for axes which have not yet been + recognised. */ + for( i = 0; i < naxes; i++ ) { + +/* Get the Domain for the primary frame containing the axis. This will be + "AST_FITSCHAN" if the axis has not yet been recognised (this Domain is + set up by WcsMapFrm). Only consider the axis further if the Domain has + not been changed. */ + astPrimaryFrame( *frm, i, &pfrm, &paxis ); + if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) { + +/* Save a pointer to the primary Frame which we will use to set the + Domain of the primary Frame. */ + if( !pfrm2 ) pfrm2 = astClone( pfrm ); + +/* Get the CTYPE value. Use a default of "AXISn". */ + ckeyval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( !ckeyval ) { + sprintf( buf3, "AXIS%d", i + 1 ); + ckeyval = buf3; + } + +/* If the CTYPE value ends with "-LOG", assume it is a logarithmically spaced + axis. Get the Mapping from IWC to WCS. Reduce the used length of the + CTYPE string to exlude any trailing "-LOG" string. */ + len = strlen( ckeyval ); + if( len > 3 && !strcmp( ckeyval + len - 4, "-LOG" ) ){ + map1 = LogWcs( store, i, s, method, class, status ); + sprintf( buf2, "%.*s", len - 4, ckeyval ); + +/* Otherwise, assume the axis is linearly spaced. */ + } else { + map1 = LinearWcs( store, i, s, method, class, status ); + sprintf( buf2, "%.*s", len, ckeyval ); + } + +/* Append the CTYPE value to the final Domain value for the primary Frame. */ + if( ckeyval && astChrLen( ckeyval ) > 0 ) { + if( newdom ) { + sprintf( buf, "%s-%s", newdom, buf2 ); + } else { + sprintf( buf, "%s", buf2 ); + newdom = buf; + } + } + +/* Now modify the axis in the Frame to have appropriate values for the + Unit, Label and Symbol attributes. Also set the Unit attribute for + the corresponding axis in the IWC Frame. */ + if( ckeyval ) astSetSymbol( *frm, i, buf2 ); + ckeyval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status ); + if( !ckeyval && usecom ) ckeyval = GetItemC( &(store->ctype_com), + i, 0, s, NULL, method, class, status ); + if( !ckeyval ) ckeyval = buf2; + if( ckeyval ) astSetLabel( *frm, i, ckeyval ); + ckeyval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); + if( ckeyval ) { + astSetUnit( *frm, i, ckeyval ); + astSetUnit( iwcfrm, i, ckeyval ); + } + +/* If this axis has been described by an earlier function (because it + uses specialised conventions such as those described in FITS-WCS papers + II or III), then create a UnitMap for this axis. */ + } else { + map1 = (AstMapping *) astUnitMap( 1, "", status ); + } + +/* Annul the pointer to the primary Frame containing the current axis. */ + pfrm = astAnnul( pfrm ); + +/* Add the Mapping for this axis in parallel with the current "running sum" + Mapping (if any). */ + if( ret ) { + map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status ); + ret = astAnnul( ret ); + map1 = astAnnul( map1 ); + ret = map2; + } else { + ret = map1; + } + } + +/* Set the Domain name for the primary Frame. It is currently set to + AST_FITSCHAN. We replace it with a value formed by concatenating the + CTYPE values of its axes. */ + if( pfrm2 ) { + if( newdom && astChrLen( newdom ) > 0 ) { + astSetDomain( pfrm2, newdom ); + } else { + astClearDomain( pfrm2 ); + } + pfrm2 = astAnnul( pfrm2 ); + } + +/* If the header contained a WCSNAME keyword, use it as the Domain name for + the Frame. Also use it to create a title. */ + ckeyval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status ); + if( ckeyval ){ + astSetDomain( *frm, ckeyval ); + sprintf( buf, "%s coordinates", ckeyval ); + astSetTitle( *frm, buf ); + } + } + +/* Return the result. */ + return ret; +} + +static AstWinMap *WcsShift( FitsStore *store, char s, int naxes, + const char *method, const char *class, int *status ){ +/* +* Name: +* WcsShift + +* Purpose: +* Create a WinMap which shifts pixels coordinates so that their origin +* is at the reference pixel. + +* Type: +* Private function. + +* Synopsis: +* AstWinMap *WcsShift( FitsStore *store, char s, int naxes, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* A WinMap is created which implements a shift of origin by subtracting +* the reference pixel coordinates (CRPIXi) from the input pixel +* coordinates. + +* Parameters: +* store +* A structure containing values for FITS keywords relating to +* the World Coordinate System. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* naxes +* The number of intermediate world coordinate axes (WCSAXES). +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the created WinMap or a NULL pointer if an +* error occurred. + +* Notes: +* - If an error occurs, a NULL pointer is returned. +*/ + +/* Local Variables: */ + AstWinMap *new; /* The created WinMap */ + int j; /* Pixel axis index */ + double crpix; /* CRPIX keyword value */ + double *c1_in; /* Input corner 1 */ + double *c2_in; /* Input corner 2 */ + double *c1_out; /* Output corner 1 */ + double *c2_out; /* Output corner 2 */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned WinMap pointer. */ + new = NULL; + +/* Allocate memory to hold the two corners, in both input and output + coordinates. */ + c1_in = (double *) astMalloc( sizeof( double )*(size_t) naxes ); + c1_out = (double *) astMalloc( sizeof( double )*(size_t) naxes ); + c2_in = (double *) astMalloc( sizeof( double )*(size_t) naxes ); + c2_out = (double *) astMalloc( sizeof( double )*(size_t) naxes ); + +/* Check these pointers can be used. */ + if( astOK ){ + +/* Set up two arbitrary corners in the input coordinate system, and the + corresponding values with the CRPIX values subtracted off. */ + for( j = 0; j < naxes; j++ ){ + +/* Get the CRPIX value for this axis. */ + crpix = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status ); + if( crpix == AST__BAD ) crpix = 0.0; + +/* Store the corner co-ordinates. */ + c1_in[ j ] = 0.0; + c2_in[ j ] = 1.0; + c1_out[ j ] = -crpix; + c2_out[ j ] = 1.0 - crpix; + } + +/* Create the WinMap. */ + new = astWinMap( naxes, c1_in, c2_in, c1_out, c2_out, "", status ); + +/* If an error has occurred, attempt to annul the new WinMap. */ + if( !astOK ) new = astAnnul( new ); + } + +/* Free the memory holding the corners. */ + c1_in = (double *) astFree( (void *) c1_in ); + c1_out = (double *) astFree( (void *) c1_out ); + c2_in = (double *) astFree( (void *) c2_in ); + c2_out = (double *) astFree( (void *) c2_out ); + +/* Return the WinMap. */ + return new; +} + +static AstSkyFrame *WcsSkyFrame( AstFitsChan *this, FitsStore *store, char s, + int prj, char *sys, int axlon, int axlat, + const char *method, const char *class, int *status ){ + +/* +* Name: +* WcsSkyFrame + +* Purpose: +* Create a SkyFrame to describe a WCS celestial coordinate system. + +* Type: +* Private function. + +* Synopsis: +* AstSkyFrame *WcsSkyFrame( AstFitsChan this, FitsStore *store, char s, int prj, +* char *sys, int axlon, int axlat, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A SkyFrame is returned describing the celestial coordinate system +* described by a FITS header. The axes are *not* permuted in the +* returned Frame (that is, axis 0 is longitude and axis 1 is latitude +* in the returned SkyFrame, no matter what values are supplied for +* "axlat" and "axlon"). + +* Parameters: +* this +* The FitsChan from which the keywords were read. Warning messages +* may be added to this FitsChan. +* store +* A structure containing values for FITS keywords relating to +* the World Coordinate System. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* prj +* An integer code for the WCS projection being used. +* sys +* A pointer to a string identifying the celestial co-ordinate system +* implied by the CTYPE values in the FitsStore. This will be "EQU" (for +* equatorial), or a one or two character code extracted from the +* CTYPE values. +* axlon +* Zero based index of the longitude axis in the FITS header. +* axlat +* Zero based index of the latitude axis in the FITS header. +* method +* The calling method. Used only in error messages. +* class +* The object class. Used only in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the SkyFrame. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or +* if this function should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *ret; /* Returned Frame */ + char *ckeyval; /* Pointer to string item value */ + char *lattype; /* Pointer to latitude CTYPE value */ + char *lontype; /* Pointer to longitude CTYPE value */ + char bj; /* Besselian/Julian selector */ + char buf[300]; /* Text buffer */ + char sym[10]; /* Axis symbol */ + double dval; /* Floating point attribute value */ + double eqmjd; /* MJD equivalent of equinox */ + double equinox; /* EQUINOX value */ + double geolat; /* Observer's geodetic latitude */ + double geolon; /* Observer's geodetic longitude */ + double h; /* Observer's geodetic height */ + double mjdobs; /* MJD-OBS value */ + double obsgeo[ 3 ]; /* Observer's Cartesian position */ + int radesys; /* RADESYS value */ + int report; /* Report unknown lon/lat system? */ + +/* Initialise. */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Get the RADESYS keyword from the header, and identify the value. + Store a integer value identifying the system. Report an error if an + unrecognised system is supplied. Store NORADEC if the keyword was + not supplied. */ + ckeyval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status ); + radesys = NORADEC; + if( ckeyval ){ + if( !strncmp( ckeyval, "FK4 ", 4 ) || + !strcmp( ckeyval, "FK4" ) ){ + radesys = FK4; + } else if( !strncmp( ckeyval, "FK4-NO-E", 8 ) ){ + radesys = FK4NOE; + } else if( !strncmp( ckeyval, "FK5 ", 4 ) || + !strcmp( ckeyval, "FK5" ) ){ + radesys = FK5; + } else if( !strncmp( ckeyval, "ICRS ", 5 ) || + !strcmp( ckeyval, "ICRS" ) ){ + radesys = ICRS; + } else if( !strncmp( ckeyval, "GAPPT ", 6 ) || + !strcmp( ckeyval, "GAPPT" ) ){ + radesys = GAPPT; + } else if( astOK ){ + astError( AST__BDFTS, "%s(%s): FITS keyword '%s' has the " + "unrecognised value '%s'.", status, method, class, + FormatKey( "RADESYS", -1, -1, s, status ), ckeyval ); + } + } else { + radesys = NORADEC; + } + +/* Get the value of the EQUINOX keyword. */ + equinox = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status ); + +/* For FK4 and FK4-NO-E any supplied equinox value is Besselian. For all + other systems, the equinox value is Julian. */ + bj = 0; + if( equinox != AST__BAD ){ + if( radesys == FK4 || radesys == FK4NOE ){ + bj = 'B'; + } else if( radesys != NORADEC ) { + bj = 'J'; + +/* If no RADESYS was suppied, but an equinox was, use the IAU 1984 rule + to determine the default RADESYS and equinox type. */ + } else { + if( equinox < 1984.0 ){ + radesys = FK4; + bj = 'B'; + } else { + radesys = FK5; + bj = 'J'; + } + +/* If an equatorial system is being used, give a warning that a default RADESYS + value is being used. */ + if( !strcmp( sys, "EQU" ) ){ + sprintf( buf, "The original FITS header did not specify the " + "RA/DEC reference frame. A default value of %s was " + "assumed.", ( radesys == FK4 ) ? "FK4" : "FK5" ); + Warn( this, "noradesys", buf, method, class, status ); + } + } + +/* If no equinox was supplied, use a default equinox value depending + on the frame of reference. For FK4-based systems, use B1950. */ + } else { + if( radesys == FK4 || radesys == FK4NOE ){ + equinox = 1950.0; + bj = 'B'; + +/* For FK5-based systems, use J2000. */ + } else if( radesys == FK5 ){ + equinox = 2000.0; + bj = 'J'; + +/* If no RADESYS or EQUINOX was supplied, assume either FK4 B1950 or ICRS - + as decided by attribute DefB1950 (GAPPT and ICRS do not use EQUINOX). */ + } else if( radesys == NORADEC ) { + if( astGetDefB1950( this ) ) { + equinox = 1950.0; + bj = 'B'; + radesys = FK4; + } else { + radesys = ICRS; + } + if( !strcmp( sys, "EQU" ) ){ + sprintf( buf, "The original FITS header did not specify the " + "RA/DEC reference frame. A default value of %s was " + "assumed.", ( radesys == FK4 ) ? "FK4" : "ICRS" ); + Warn( this, "noradesys", buf, method, class, status ); + } + } + +/* If we have an equatorial or ecliptic system, issue a warning that a default + equinox has been adopted. */ + if( ( !strcmp( sys, "EQU" ) && radesys != ICRS && radesys != GAPPT ) || + !strcmp( sys, "ECL" ) ){ + sprintf( buf, "The original FITS header did not specify the " + "reference equinox. A default value of %c%.8g was " + "assumed.", bj, equinox ); + Warn( this, "noequinox", buf, method, class, status ); + } + } + +/* Convert the equinox to a Modified Julian Date. */ + if( equinox != AST__BAD ) { + if( bj == 'B' ) { + eqmjd = palEpb2d( equinox ); + } else { + eqmjd = palEpj2d( equinox ); + } + } else { + eqmjd = AST__BAD; + } + +/* Get a value for the Epoch attribute. If no value is available, use + EQUINOX and issue a warning. */ + mjdobs = ChooseEpoch( this, store, s, method, class, status ); + if( mjdobs == AST__BAD ) { + mjdobs = eqmjd; + if( mjdobs != AST__BAD ) { + sprintf( buf, "The original FITS header did not specify the " + "date of observation. A default value of %c%.8g was " + "assumed.", bj, equinox ); + Warn( this, "nomjd-obs", buf, method, class, status ); + } + } + +/* Create a SkyFrame for the specified system. */ + if( !strcmp( sys, "E" ) ){ + ret = astSkyFrame( "System=Ecliptic", status ); + } else if( !strcmp( sys, "H" ) ){ + ret = astSkyFrame( "System=Helioecliptic", status ); + } else if( !(strcmp( sys, "G" ) ) ){ + ret = astSkyFrame( "System=Galactic", status ); + } else if( !(strcmp( sys, "S" ) ) ){ + ret = astSkyFrame( "System=Supergalactic", status ); + } else if( !(strcmp( sys, "AZL" ) ) ){ + ret = astSkyFrame( "System=AzEl", status ); + } else if( !(strcmp( sys, "EQU" ) ) ){ + +/* For equatorial systems, the specific system is given by the RADESYS + value. */ + if( radesys == FK4 ){ + ret = astSkyFrame( "System=FK4", status ); + } else if( radesys == FK4NOE ){ + ret = astSkyFrame( "System=FK4-NO-E", status ); + } else if( radesys == FK5 ){ + ret = astSkyFrame( "System=FK5", status ); + } else if( radesys == ICRS ){ + ret = astSkyFrame( "System=ICRS", status ); + } else if( radesys == GAPPT ){ + ret = astSkyFrame( "System=GAPPT", status ); + } else if( astOK ){ + astError( AST__INTER, "%s(%s): Internal AST programming " + "error - FITS equatorial coordinate system type %d " + "not yet supported in WcsSkyFrame.", status, method, class, radesys ); + } + +/* If an unknown celestial co-ordinate system was specified by the CTYPE + keywords, add warning messages to the FitsChan and treat the axes as + a general spherical coordinate system. */ + } else if( astOK ){ + report = 1; + ret = astSkyFrame( "System=UNKNOWN", status ); + strcpy( sym, sys ); + if( strlen( sys ) == 1 ) { + strcpy( sym + 1, "LON" ); + astSetSymbol( ret, 0, sym ); + strcpy( sym + 1, "LAT" ); + astSetSymbol( ret, 1, sym ); + } else { + strcpy( sym + 2, "LN" ); + astSetSymbol( ret, 0, sym ); + strcpy( sym + 2, "LT" ); + astSetSymbol( ret, 1, sym ); + +/* The code "OF" is used by AST to describe offset sky coordinates. Set + the Domain to SKY_OFFSETS in these cases, so that we can identify + these Frames later. */ + if( !strcmp( sys, "OF" ) ) { + astSetDomain( ret, "SKY_OFFSETS" ); + report = 0; + } + } + + if( report ) { + lontype = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status ); + lattype = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status ); + if( lontype && lattype ){ + sprintf( buf, "This FITS header contains references to an unknown " + "spherical co-ordinate system specified in the values " + "%s and %s. It may not be possible to convert to " + "other standard co-ordinate systems.", lontype, lattype ); + Warn( this, "badcel", buf, method, class, status ); + } + } + } + +/* If a skyFrame was created... */ + if( ret ){ + +/* Store the projection description. */ + if( prj != AST__WCSBAD ) astSetProjection( ret, astWcsPrjDesc( prj ) ); + +/* Store the epoch of the observation in the SkyFrame. */ + if( mjdobs != AST__BAD ) astSetEpoch( ret, mjdobs ); + +/* For equatorial and ecliptic systems, store the epoch of the reference + equinox in the SkyFrame. */ + if( ( !strcmp( sys, "EQU" ) || !strcmp( sys, "ECL" ) ) && + equinox != AST__BAD ) astSetEquinox( ret, eqmjd ); + +/* If either of the CNAME keywords is set, use it as the axis label. */ + ckeyval = GetItemC( &(store->cname), axlon, 0, s, NULL, method, class, status ); + if( ckeyval ) astSetLabel( ret, 0, ckeyval ); + ckeyval = GetItemC( &(store->cname), axlat, 0, s, NULL, method, class, status ); + if( ckeyval ) astSetLabel( ret, 1, ckeyval ); + +/* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z + keywords, convert to geodetic longitude and latitude and store as the + SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */ + obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status ); + obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status ); + obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status ); + if( obsgeo[ 0 ] != AST__BAD && + obsgeo[ 1 ] != AST__BAD && + obsgeo[ 2 ] != AST__BAD ) { + eraGc2gd( 1, obsgeo, &geolon, &geolat, &h ); + astSetObsLat( ret, geolat ); + astSetObsLon( ret, geolon ); + astSetObsAlt( ret, h ); + } + +/* Store values for the reference point in the SkyFrame. */ + dval = GetItem( &(store->skyref), axlon, 0, s, NULL, method, class, status ); + if( dval != AST__BAD ) astSetSkyRef( ret, 0, dval ); + dval = GetItem( &(store->skyref), axlat, 0, s, NULL, method, class, status ); + if( dval != AST__BAD ) astSetSkyRef( ret, 1, dval ); + + dval = GetItem( &(store->skyrefp), axlon, 0, s, NULL, method, class, status ); + if( dval != AST__BAD ) astSetSkyRefP( ret, 0, dval ); + dval = GetItem( &(store->skyrefp), axlat, 0, s, NULL, method, class, status ); + if( dval != AST__BAD ) astSetSkyRefP( ret, 1, dval ); + +/* We cannot store the SkyRefIs value yet since this needs to be done + after the SkyFrame has been added into the FrameSet, so that the Frame + will be remapped to represent the intended offsets. SO instance, mark + the Frame by setting the domain to "SKY_POLE" or "SKY_ORIGIN". This + odd Domain value will be cleared later in TidyOffsets. */ + ckeyval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status ); + if( ckeyval ) { + if( !Ustrcmp( "POLE", ckeyval, status ) ) { + astSetDomain( ret, "SKY_POLE" ); + } else if( !Ustrcmp( "ORIGIN", ckeyval, status ) ) { + astSetDomain( ret, "SKY_ORIGIN" ); + } + } + } + +/* If an error has occurred, annul the Frame. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the Frame. */ + return ret; +} + +static AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s, + AstFrame **frm, AstFrame *iwcfrm, double reflon, double reflat, + AstSkyFrame *reffrm, const char *method, + const char *class, int *status ){ + +/* +* Name: +* WcsSpectral + +* Purpose: +* Create a Mapping from intermediate world coords to spectral coords +* as described in a FITS header. + +* Type: +* Private function. + +* Synopsis: + +* AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s, +* AstFrame **frm, AstFrame *iwcfrm, double reflon, +* double reflat, AstSkyFrame *reffrm, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function interprets the contents of the supplied FitsStore +* structure, looking for world coordinate axes which describe positions +* in a spectrum. If such an axis is found, a Mapping is returned which +* transforms the corresponding intermediate world coordinates to +* spectral world coordinates (this mapping leaves any other axes +* unchanged). It also, modifies the supplied Frame to describe the +* axis (again, other axes are left unchanged). If no spectral axis +* is found, a UnitMap is returned, and the supplied Frame is left +* unchanged. + +* Parameters: +* this +* The FitsChan. +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* frm +* The address of a location at which to store a pointer to the +* Frame describing the world coordinate axes. +* iwcfrm +* A pointer to the Frame describing the intermediate world coordinate +* axes. The properties of this Frame may be changed on exit. +* reflon +* The reference celestial longitude, in the frame given by reffrm. +* reflat +* The reference celestial latitude, in the frame given by reffrm. +* reffrm +* The SkyFrame defining reflon and reflat. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping. +*/ + +/* Local Variables: */ + AstFrame *ofrm; /* Pointer to a Frame */ + AstMapping *map1; /* Pointer to Mapping */ + AstMapping *map2; /* Pointer to Mapping */ + AstMapping *ret; /* Pointer to the returned Mapping */ + AstSpecFrame *specfrm; /* Pointer to a SpecFrame */ + char algcode[ 5 ]; /* Displayed spectral type string */ + char stype[ 5 ]; /* Displayed spectral type string */ + const char *cname; /* Pointer to CNAME value */ + const char *ctype; /* Pointer to CTYPE value */ + const char *cunit; /* Pointer to CUNIT value */ + const char *defunit; /* Default unit string */ + const char *specsys; /* Pointer to SPECSYS value */ + const char *ssyssrc; /* Pointer to SSYSSRC value */ + double geolat; /* Observer's geodetic latitude */ + double geolon; /* Observer's geodetic longitude */ + double h; /* Observer's geodetic height */ + double mjd; /* Modified Julian Date */ + double obscentre; /* Spectral value at observation centre */ + double obsgeo[ 3 ]; /* Observer's Cartesian position */ + double restfrq; /* RESTFRQ keyword value */ + double vsource; /* Source velocity */ + int *axes; /* Pointer to axis permutation array */ + int i; /* Axis index */ + int j; /* Loop count */ + int k; /* Loop count */ + int kk; /* Loop count */ + int naxes; /* No. of axes in Frame */ + +/* Initialise the pointer to the returned Mapping. */ + ret = NULL; + +/* Check the global status. */ + if ( !astOK ) return ret; + +/* Get the number of physical axes. */ + naxes = astGetNaxes( *frm ); + +/* An array to hold a list of axis selections. */ + axes = astMalloc( naxes*sizeof( int ) ); + +/* Loop round checking each axis. */ + defunit = NULL; + map1 = NULL; + for( i = 0; i < naxes && astOK; i++ ) { + +/* Get the CTYPE value. Pass on to the next axis if no CTYPE is available. */ + ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ); + if( ctype ) { + +/* See if this CTYPE describes a spectral axis, and if so, extract the + system code, the algorithm code and get the default units. */ + defunit = IsSpectral( ctype, stype, algcode, status ); + +/* Skip to the next axis if the system type was not a spectral system + type. */ + if( defunit ) { + +/* Create a SpecFrame or DSBSpecFrame with this system (the FITS type codes + are also legal SpecFrame System values). We use astSetC rather than + astSetSystem because astSetC translates string values into the + corresponding integer system identifiers. */ + if( GetItem( &(store->imagfreq), 0, 0, s, NULL, method, + class, status ) == AST__BAD ) { + specfrm = astSpecFrame( "", status ); + } else { + specfrm = (AstSpecFrame *) astDSBSpecFrame( "", status ); + } + astSetC( specfrm, "System", stype ); + +/* Set the reference position (attributes RefRA and RefDec), if known. */ + if( reffrm ) astSetRefPos( specfrm, reffrm, reflon, reflat ); + +/* Set the SpecFrame units. Use the value of the CUNIT FITS keyword for this + axis if available, otherwise use the default units for the system, noted + above. */ + cunit = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status ); + if( !cunit ) cunit = defunit; + astSetUnit( specfrm, 0, cunit ); + +/* Set the axis unit in the IWC Frame. */ + astSetUnit( iwcfrm, i, cunit ); + +/* Get a value for the Epoch attribute (the date of observation). */ + mjd = ChooseEpoch( this, store, s, method, class, status ); + if( mjd != AST__BAD ) astSetEpoch( specfrm, mjd ); + +/* Set the rest frequency. Use the RESTFRQ keyword (assumed to be in Hz), + or (if RESTFRQ is not available), RESTWAV (assumes to be in m). */ + restfrq = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status ); + if( restfrq == AST__BAD ) { + restfrq = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status ); + if( restfrq != AST__BAD ) restfrq = AST__C/restfrq; + } + astSetRestFreq( specfrm, restfrq ); + +/* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z + keywords, convert to geodetic longitude and latitude and store as the + SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */ + obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status ); + obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status ); + obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status ); + if( obsgeo[ 0 ] != AST__BAD && + obsgeo[ 1 ] != AST__BAD && + obsgeo[ 2 ] != AST__BAD ) { + eraGc2gd( 1, obsgeo, &geolon, &geolat, &h ); + astSetObsLat( specfrm, geolat ); + astSetObsLon( specfrm, geolon ); + astSetObsAlt( specfrm, h ); + } + +/* Source velocity rest frame */ + ssyssrc = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status ); + if( ssyssrc ) astSetC( specfrm, "SourceVRF", ssyssrc ); + +/* Source velocity. Use the ZSOURCE keyword and convert from redshift to + velocity. */ + vsource = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status ); + if( vsource != AST__BAD ) { + vsource += 1.0; + vsource *= vsource; + vsource = AST__C*( vsource - 1.0 )/( vsource + 1.0 ); + astSetSourceVel( specfrm, vsource ); + } + +/* Reference frame. If the SPECSYS keyword is set, use it (the FITS codes + are also legal SpecFrame StdOfRest values). We use astSetC rather than + astSetSystem because astSetC translates string values into the + corresponding integer system identifiers. */ + specsys = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status ); + if( specsys ) astSetC( specfrm, "StdOfRest", specsys ); + +/* Axis label. If the CNAME keyword is set, use it as the axis label. */ + cname = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status ); + if( cname ) astSetLabel( specfrm, 0, cname ); + +/* If the header contains an AXREF value for the spectral axis, use it as the + observation centre in preferences to the CRVAL value. AXREF keywords are + created by the astWrite method for axes described by -TAB algorithm that + have no inverse transformation. */ + obscentre = GetItem( &(store->axref), i, 0, s, NULL, method, + class, status ); + if( obscentre == AST__BAD ) { + obscentre = GetItem( &(store->crval), i, 0, s, NULL, method, + class, status ); + } + +/* Now do the extra stuff needed if we are creating a dual sideband + SpecFrame. */ + if( astIsADSBSpecFrame( specfrm ) ) { + DSBSetUp( this, store, (AstDSBSpecFrame *) specfrm, s, + obscentre, method, class, status ); + } + +/* Now branch for each type of algorithm code. Each case returns a 1D + Mapping which converts IWC value into the specified Spectral system. */ + +/* Linear */ + if( strlen( algcode ) == 0 ) { + map1 = LinearWcs( store, i, s, method, class, status ); + +/* Log-Linear */ + } else if( !strcmp( "-LOG", algcode ) ) { + map1 = LogWcs( store, i, s, method, class, status ); + +/* Non-Linear */ + } else if( algcode[ 0 ] == '-' && algcode[ 2 ] == '2' ) { + map1 = NonLinSpecWcs( this, algcode, store, i, s, specfrm, method, class, status ); + +/* Grism */ + } else if( !strcmp( "-GRI", algcode ) || + !strcmp( "-GRA", algcode ) ) { + map1 = GrismSpecWcs( algcode, store, i, s, specfrm, method, class, status ); + } else { + map1 = NULL; + } + if( map1 == NULL && astOK ) { + specfrm = astAnnul( specfrm ); + astError( AST__BDFTS, "%s(%s): Cannot implement spectral " + "algorithm code '%s' specified in FITS keyword '%s'.", status, + method, class, ctype + 4, FormatKey( "CTYPE", i + 1, -1, s, status ) ); + astError( AST__BDFTS, "%s(%s): Unknown algorithm code or " + "unusable parameter values.", status, method, class ); + break; + } + +/* Create a Frame by picking all the other (non-spectral) axes from the + supplied Frame. */ + j = 0; + for( k = 0; k < naxes; k++ ) { + if( k != i ) axes[ j++ ] = k; + } + +/* If there were no other axes, replace the supplied Frame with the + specframe. */ + if( j == 0 ) { + (void) astAnnul( *frm ); + *frm = (AstFrame *) specfrm; + +/* Otherwise pick the other axes from the supplied Frame */ + } else { + ofrm = astPickAxes( *frm, j, axes, NULL ); + +/* Replace the supplied Frame with a CmpFrame made up of this Frame and + the SpecFrame. */ + (void) astAnnul( *frm ); + *frm = (AstFrame *) astCmpFrame( ofrm, specfrm, "", status ); + ofrm = astAnnul( ofrm ); + specfrm = astAnnul( specfrm ); + } + +/* Permute the axis order to put the spectral axis back in its original + position. */ + j = 0; + for( kk = 0; kk < naxes; kk++ ) { + if( kk == i ) { + axes[ kk ] = naxes - 1; + } else { + axes[ kk ] = j++; + } + } + astPermAxes( *frm, axes ); + } + } + +/* If this axis is not a spectral axis, create a UnitMap (the Frame is left + unchanged). */ + if( !map1 && astOK ) map1 = (AstMapping *) astUnitMap( 1, "", status ); + +/* Add the Mapping for this axis in parallel with the Mappings for + previous axes. */ + if( ret ) { + map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status ); + ret = astAnnul( ret ); + map1 = astAnnul( map1 ); + ret = map2; + } else { + ret = map1; + map1 = NULL; + } + } + +/* Free the axes array. */ + axes= astFree( axes ); + +/* Return the result. */ + return ret; +} + +static void WcsToStore( AstFitsChan *this, AstFitsChan *trans, + FitsStore *store, const char *method, + const char *class, int *status ){ + +/* +* Name: +* WcsToStore + +* Purpose: +* Extract WCS information from the supplied FitsChan using a FITSWCS +* encoding, and store it in the supplied FitsStore. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* void WcsToStore( AstFitsChan *this, AstFitsChan *trans, +* FitsStore *store, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* A FitsStore is a structure containing a generalised represention of +* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and +* from a set of FITS header cards (using a specified encoding), or +* an AST FrameSet. In other words, a FitsStore is an encoding- +* independant intermediary staging post between a FITS header and +* an AST FrameSet. +* +* This function extracts FITSWCS keywords from the supplied FitsChan(s), +* and stores the corresponding WCS information in the supplied FitsStore. +* Keywords will be searched for first in "trans", and then, if they +* are not found in "trans", they will be searched for in "this". + +* Parameters: +* this +* Pointer to the FitsChan containing the cards read from the +* original FITS header. This may include non-standard keywords. +* trans +* Pointer to a FitsChan containing cards representing standard +* translations of any non-standard keywords in "this". A NULL +* pointer indicates that "this" contains no non-standard keywords. +* store +* Pointer to the FitsStore structure. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Read all usable cards out of the main FitsChan, into the FitsStore. */ + WcsFcRead( this, trans, store, method, class, status ); + +/* If a FitsChan containing standard translations was supplied, read all + cards out of it, into the FitsStore, potentially over-writing the + non-standard values stored in the previous call to WcsFcRead. */ + if( trans ) WcsFcRead( trans, NULL, store, method, class, status ); +} + +static int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm, + int *status ){ + +/* +* Name: +* WorldAxes + +* Purpose: +* Associate final world axes with pixel axes. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" + +* int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm, +* int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function finds the association between the axes of the final +* world coordinate system, and those of the pixel coordinate +* system. This may not simply be a 1-to-1 association because the +* Mapping may include a PermMap. Each output axis is associated with +* the input axis which is most nearly aligned with it. + +* Parameters: +* this +* Pointer to the FitsChan. +* cmap +* Pointer to the Mapping from pixel coordinates to final world +* coordinates. +* dim +* Pointer to an array with one element for each input of "map", +* supplied holding the no. of pixels in the data cube along the axis, or +* AST__BAD If unknown. +* perm +* Pointer to an array with one element for each output of "map". +* On exit, each element of this array holds the zero-based index of the +* "corresponding" (i.e. most nearly parallel) pixel axis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero for success - zero for failure. +*/ + +/* Local Variables: */ + AstMapping *smap; + AstMapping *map; + AstPointSet *pset1; + AstPointSet *pset2; + double **ptr2; + double **ptr1; + double *dw; + double *g0; + double *nwt; + double *ntn; + double *tn; + double *wt; + double *w0; + double dg; + double s; + double sj; + double tnmin; + double wtmax; + int *outs; + int i2; + int i; + int imin; + int j2; + int j; + int jmin; + int nin; + int nout; + int nouts; + int nused; + int ret; + int retain; + int used; + +/* Initialise returned value */ + ret = 0; + +/* Other initialisation to avoid compiler warnings. */ + retain = 0; + +/* Check the status */ + if( !astOK ) return ret; + +/* Simplfy the Mapping. */ + map = astSimplify( cmap ); + +/* Get the number of inputs and outputs for the Mapping. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* Initialise "perm". */ + for( i = 0; i < nout; i++ ) perm[ i ] = i; + +/* First deal with Mappings that are defined in both directions. */ + if( astGetTranForward( map ) && astGetTranInverse( map ) ) { + +/* Use FindBasisVectors to find an input position which coresponds to a + good output position. Store it in a dynamic array pointed to by "g0". */ + pset1 = astPointSet( nin+1, nin, "", status ); + pset2 = astPointSet( nin+1, nout, "", status ); + if( FindBasisVectors( map, nin, nout, dim, pset1, pset2, status ) ) { + g0 = astMalloc( sizeof(double)*nin ); + ptr1 = astGetPoints( pset1 ); + if( astOK ) { + for( j = 0; j < nin; j++ ) g0[ j ] = ptr1[ j ][ 0 ]; + } + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* If no basis vectors found, return. */ + } else { + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + return ret; + } + +/* Create Pointset to hold two input (pixel) points. */ + pset1 = astPointSet( 2, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + +/* Create a Pointset to hold the same number of output (world) points. */ + pset2 = astPointSet( 2, nout, "", status ); + ptr2 = astGetPoints( pset2 ); + +/* Allocate memory to use as work space */ + w0 = astMalloc( sizeof(double)*nout ); + dw = astMalloc( sizeof(double)*nout ); + tn = astMalloc( sizeof(double)*nout*nin ); + wt = astMalloc( sizeof(double)*nout*nin ); + +/* Check that the pointers can be used. */ + if( astOK ) { + +/* Transform the grid position found above, plus a position 1 pixel away + along all pixel axes, into world coords. Also set up "dw" to hold + "a small increment" along each world axis. */ + for( j = 0; j < nin; j++ ) { + ptr1[ j ] [ 0 ] = g0[ j ]; + ptr1[ j ] [ 1 ] = g0[ j ] + 1.0; + } + (void) astTransform( map, pset1, 1, pset2 ); + for( i = 0; i < nout; i++ ) { + w0[ i ] = ptr2[ i ] [ 0 ]; + if( w0[ i ] != AST__BAD && ptr2[ i ] [ 1 ] != AST__BAD ) { + dw[ i ] = fabs( 0.1*( ptr2[ i ] [ 1 ] - w0[ i ] ) ); + if( dw[ i ] <= fabs( 0.001*w0[ i ] ) ) { + if( w0[ i ] != 0.0 ) { + dw[ i ] = fabs( 0.001*w0[ i ] ); + } else { + dw[ i ] = 1.0; + } + } + } else { + dw[ i ] = AST__BAD; + } + } + +/* Any PermMap in the mapping may result in the the "inverse transformation" + not being a true inverse of the forward transformation (for instance, + constant values fed in for degenerate axis would have this effect). To + ensure that "g0" and "w0" are corresponding positions, transform the + "w0" position back into grid coords and use the resulting grid position + as "g0". */ + (void) astTransform( map, pset2, 0, pset1 ); + for( j = 0; j < nin; j++ ) { + g0[ j ] = ptr1[ j ] [ 0 ]; + } + +/* In the next loop we find the tan of the angle between each WCS axis and + each of the pixel axes. Loop round each WCS axis. */ + for( i = 0; i < nout; i++ ) { + +/* Initialise the tan values for this WCS axis to AST__BAD. */ + ntn = tn + i*nin; + nwt = wt + i*nin; + for( j = 0; j < nin; j++ ) ntn[ j ] = AST__BAD; + +/* As a side issue, initialise the pixel axis assigned to each WCS axis + to -1, to indicate that no grid axis has yet been associated with this + WCS axis. */ + perm[ i ] = -1; + +/* Skip over this axis if the increment is bad. */ + if( dw[ i ] != AST__BAD ) { + +/* Store a WCS position which is offset from the "w0" position by a small + amount along the current WCS axis. The first position in "ptr2" is + currently "w0". */ + ptr2[ i ][ 0 ] += dw[ i ]; + +/* Transform this position into grid coords. */ + (void) astTransform( map, pset2, 0, pset1 ); + +/* Re-instate the original "w0" values within "ptr2", ready for the next + WCS axis. */ + ptr2[ i ][ 0 ] = w0[ i ]; + +/* Consider each pixel axis in turn as a candidate for being assigned to + the current WCS axis. */ + for( j = 0; j < nin; j++ ) { + +/* Find the tan of the angle between the current ("i"th) WCS axis and the + current ("j"th) pixel axis. This gets stored in tn[j+nin*i]. A + corresponding weight for each angle is stored in nwt[j+nin*i]. This + is the length of the projection of the vector onto the "j"th pixel + axis. */ + s = 0.0; + sj = 0.0; + for( j2 = 0; j2 < nin; j2++ ) { + if( ptr1[ j2 ][ 0 ] != AST__BAD ) { + dg = ptr1[ j2 ][ 0 ] - g0[ j2 ]; + if( j2 != j ) { + s += dg*dg; + } else { + sj = fabs( dg ); + } + } else { + s = AST__BAD; + break; + } + } + if( s != AST__BAD && sj != 0.0 ) { + ntn[ j ] = sqrt( s )/sj; + nwt[ j ] = sj; + } + } + } + } + +/* Loop until every grid axes has been assigned to a WCS axis. */ + while( 1 ) { + +/* Pass through the array of tan values, finding the smallest. Note the + pixel and WCS axis for which the smallest tan value occurs. If the tan + values are equal, favour the one with highest weight. */ + ntn = tn; + nwt = wt; + tnmin = AST__BAD; + wtmax = AST__BAD; + imin = 0; + jmin = 0; + for( i = 0; i < nout; i++ ) { + for( j = 0; j < nin; j++ ) { + if( *ntn != AST__BAD ) { + if( tnmin == AST__BAD || *ntn < tnmin ) { + tnmin = *ntn; + wtmax = *nwt; + imin = i; + jmin = j; + } else if( astEQUAL( *ntn, tnmin ) && *nwt > wtmax ) { + wtmax = *nwt; + imin = i; + jmin = j; + } + } + ntn++; + nwt++; + } + } + +/* Check we found a usable minimum tan value */ + if( tnmin != AST__BAD ) { + +/* Assign the pixel axis to the WCS axis. */ + perm[ imin ] = jmin; + +/* Set bad all the tan values for this pixel and WCS axis pair. This ensures + that the pixel axis will not be assigned to another WCS axis, and that + the WCS will not have another pixel axis assigned to it. */ + ntn = tn; + for( i = 0; i < nout; i++ ) { + for( j = 0; j < nin; j++ ) { + if( i == imin || j == jmin ) *ntn = AST__BAD; + ntn++; + } + } + +/* Leave the loop if no more good tan values were found. */ + } else { + break; + } + } + +/* The above process may have left some WCS axes with out any assigned + pixel axis. We assign the remaining pixel arbitrarily to such axes, + starting with the first remaining pixel axis. Find the lowest unused + pixel axis. */ + for( j = 0; j < nin; j++ ) { + used = 0; + for( i = 0; i < nout; i++ ) { + if( perm[ i ] == j ) { + used = 1; + break; + } + } + if( !used ) break; + } + +/* Now check each WCS axis looking for outputs which were not assigned a + pixel axis in the above process. */ + for( i = 0; i < nout; i++ ) { + if( perm[ i ] == -1 ) { + +/* Use the next unused axis value. */ + perm[ i ] = j++; + +/* Find the next unused axis value. */ + for( ; j < nin; j++ ) { + used = 0; + for( i2 = 0; i2 < nout; i2++ ) { + if( perm[ i2 ] == j ) { + used = 1; + break; + } + } + if( !used ) break; + } + } + } + +/* Indicate success. */ + if( astOK ) ret = 1; + } + +/* Free resources. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + g0 = astFree( g0 ); + w0 = astFree( w0 ); + tn = astFree( tn ); + wt = astFree( wt ); + dw = astFree( dw ); + +/* Now, if we can use the TAB algorithm, deal with Mappings that are defined only in the forward direction. */ + } else if( astGetTranForward( map ) && astGetTabOK( this ) > 0 ) { + +/* Assume success. */ + ret = 1; + +/* Initialise to indicate no outputs have yet been assigned. */ + for( i = 0; i < nout; i++ ) perm[ i ] = -1; + +/* Find the output associated with each input. */ + for( j = 0; j < nin; j++ ) { + +/* Attempt to split off the current input. */ + outs = astMapSplit( map, 1, &j, &smap ); + +/* If successfull, store the index of the corresponding input for each + output. */ + if( outs && smap ) { + nouts = astGetNout( smap ); + for( i = 0; i < nouts; i++ ) { + if( perm[ outs[ i ] ] == -1 ) { + perm[ outs[ i ] ] = j; + } else { + ret = 0; + } + } + } + +/* Free resources. */ + outs = astFree( outs ); + if( smap ) smap = astAnnul( smap ); + } + +/* Check all outputs were assigned . */ + for( i = 0; i < nout && ret; i++ ) { + if( perm[ i ] == -1 ) ret = 0; + } + +/* If succesful, attempt to remove any duplicates from the "perm" array + (i.e. inputs that supply more than one output). First get a list of + the inputs that are currently unused (i.e. do not appear in "perm"). */ + if( ret ) { + +/* Check each input. */ + for( j = 0; j < nin; j++ ) { + +/* See how many outputs are fed by this input. */ + nused = 0; + for( i = 0; i < nout; i++ ) { + if( perm[ i ] == j ) nused++; + } + +/* If it used more than once, we need to remove all but one of the + occurrences. */ + if( nused > 1 ) { + +/* Choose the occurrence to retain. If the output with the same index as + the input is one of them, use it. Otherwise, use the first occurrence. */ + if( perm[ j ] == j ) { + retain = j; + } else { + for( i = 0; i < nout; i++ ) { + if( perm[ i ] == j ) { + retain = i; + break; + } + } + } + +/* Loop round all occurrences of this input again. */ + for( i = 0; i < nout && ret; i++ ) { + if( perm[ i ] == j ) { + +/* Replace all occurrences, except for the one being retained. */ + if( i != retain ) { + +/* Replace it with the next unused input. */ + for( j2 = 0; j2 < nin; j2++ ) { + used = 0; + for( i2 = 0; i2 < nout; i2++ ) { + if( perm[ i2 ] == j2 ) { + used = 1; + break; + } + } + if( ! used ) { + perm[ i ] = j2; + break; + } + } + +/* If there were no unused inputs, we cannot do it. */ + if( used ) ret = 0; + } + } + } + } + } + } + } + +/* Free resources. */ + map = astAnnul( map ); + +/* Return the result. */ + return ret; +} + +static int Write( AstChannel *this_channel, AstObject *object, int *status ) { +/* +* Name: +* Write + +* Purpose: +* Write an Object to a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* int Write( AstChannel *this, AstObject *object, int *status ) + +* Class Membership: +* FitsChan member function (over-rides the astWrite method +* inherited from the Channel class). + +* Description: +* This function writes an Object to a FitsChan. + +* Parameters: +* this +* Pointer to the FitsChan. +* object +* Pointer to the Object which is to be written. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of Objects written to the FitsChan by this invocation of +* astWrite. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the AST error status set, or if it should fail for any +* reason. +* - The Base Frame in the FrameSet is used as the pixel Frame, and +* the Current Frame is used to create the primary axis descriptions. +* Attempts are made to create secondary axis descriptions for any +* other Frames in the FrameSet (up to a total of 26). +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + FitsStore *store; /* Intermediate storage for WCS information */ + char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; /* Buffer for begin/end banner */ + const char *class; /* Pointer to string holding object class */ + const char *method; /* Pointer to string holding calling method */ + double *dim; /* Pointer to array of axis dimensions */ + int card0; /* Index of original current card */ + int comm; /* Value of Comm attribute */ + int encoding; /* FITS encoding scheme to use */ + int i; /* Axis index */ + int naxis; /* No. of pixel axes */ + int ret; /* Number of objects read */ + +/* Initialise. */ + ret = 0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* Store the calling method, and object class. */ + method = "astWrite"; + class = astGetClass( this ); + +/* The original current card is re-instated at the end if no object + is written. Save its index. */ + card0 = astGetCard( this ); + +/* Indicate that all cards added to the FitsCHan by this call should be + marked as "new". */ + mark_new = 1; + +/* Get the encoding scheme used by the FitsChan. */ + encoding = astGetEncoding( this ); + +/* First deal with cases where we are writing to a FitsChan in which AST + objects are encoded using native AST-specific keywords... */ + if( encoding == NATIVE_ENCODING ){ + +/* Increment the nesting level which keeps track of recursive + invocations of this function. */ + write_nest++; + +/* Initialise the current indentation level for top-level objects. */ + if ( !write_nest ) current_indent = 0; + +/* Obtain the value of the Comm attribute. */ + comm = astGetComment( this ); + +/* If this is the top-level invocation (i.e. we are about to write out + a new top-level Object), then prefix it with a blank FITS line and + an appropriate banner of FITS comments, unless comments have been + suppressed. */ + if ( !write_nest && comm ) { + astSetFitsCom( this, " ", "", 0 ); + MakeBanner( +"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", + "", "", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + if( astIsAFrameSet( object ) ) { + MakeBanner( "WCS information in AST format", "", "", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + MakeBanner( "See http://www.starlink.ac.uk/ast/", "", "", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + } + MakeBanner( HEADER_TEXT, astGetClass( object ), " object", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + MakeBanner( +"................................................................", + "", "", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + } + +/* Invoke the parent astWrite method to write out the Object data. */ + (*parent_write)( this_channel, object, status ); + +/* Append a banner of FITS comments to the object data, as above, if + necessary. */ + if ( !write_nest && comm ) { + MakeBanner( +"................................................................", + "", "", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + MakeBanner( FOOTER_TEXT, astGetClass( object ), " object", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + MakeBanner( +"----------------------------------------------------------------", + "", "", banner, status ); + astSetFitsCom( this, "COMMENT", banner, 0 ); + } + +/* Return the nesting level to its previous value. */ + write_nest--; + +/* Indicate that an object has been written. */ + ret = 1; + +/* Now deal with cases where we are writing to a FitsChan in which AST + objects are encoded using any of the supported foreign encodings... */ + } else { + +/* Only proceed if the supplied object is a FrameSet. */ + if( astIsAFrameSet( object ) ){ + +/* Note the number of pixel (i.e. Base Frame) axes, and allocate memory to + hold the image dimensions. */ + naxis = astGetNin( (AstFrameSet *) object ); + dim = (double *) astMalloc( sizeof(double)*naxis ); + if( dim ){ + +/* Note the image dimensions, if known. If not, store AST__BAD values. */ + for( i = 0; i < naxis; i++ ){ + if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ), + dim + i ) ) dim[ i ] = AST__BAD; + } + +/* Extract the required information from the FrameSet into a standard + intermediary structure called a FitsStore. The indices of any + celestial axes are returned. */ + store = FsetToStore( this, (AstFrameSet *) object, naxis, dim, + encoding, method, class, status ); + +/* If the FrameSet cannot be described in terms of any of the supported + FITS encodings, a null pointer will have been returned. */ + if( store ){ + +/* Now put header cards describing the contents of the FitsStore into the + supplied FitsChan, using the requested encoding. Zero or one is + returned depending on whether the information could be encoded. */ + ret = FitsFromStore( this, store, encoding, dim, + (AstFrameSet *) object, method, class, status ); + +/* Release the resources used by the FitsStore. */ + store = FreeStore( store, status ); + +/* If the Object was written to the FitsChan, set the current card to + end-of-file. */ + if( ret ) astSetCard( this, INT_MAX ); + } + +/* Free workspace holding image dimensions */ + dim = (double *) astFree( (void *) dim ); + } + } + } + +/* If an error has occurred, return zero and remove any new cards added + to the FitsCHan by this call. */ + if( !astOK ) ret = 0; + +/* Clear the new flag associated with cards which have been added to the + FitsChan as a result of this function. If the object was not added + succesfully to the FitsChan, remove any cards which were added before + the error was discovered. */ + FixNew( this, NEW1, !ret, method, class, status ); + FixNew( this, NEW2, !ret, method, class, status ); + +/* Indicate that all cards added to the FitsChan from now on should not be + marked as "new". */ + mark_new = 0; + +/* If no object was written, re-instate the original current card. */ + if( !ret ) astSetCard( this, card0 ); + +/* Return the answer. */ + return ret; +} + +static void WriteBegin( AstChannel *this_channel, const char *class, + const char *comment, int *status ) { +/* +* Name: +* WriteBegin + +* Purpose: +* Write a "Begin" data item to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteBegin( AstChannel *this, const char *class, +* const char *comment ) + +* Class Membership: +* FitsChan member function (over-rides the protected astWriteBegin +* method inherited from the Channel class). + +* Description: +* This function writes a "Begin" data item to the data sink +* associated with a FitsChan, so as to begin the output of a new +* Object definition. + +* Parameters: +* this +* Pointer to the FitsChan. +* class +* Pointer to a constant null-terminated string containing the +* name of the class to which the Object belongs. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the "Begin" +* item. Normally, this will describe the purpose of the Object. + +* Notes: +* - The comment supplied may not actually be used, depending on +* the nature of the FitsChan supplied. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure. */ + char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; + /* Character buffer */ + char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Increment the indentation level for comments. */ + current_indent += INDENT_INC; + +/* If we are not beginning a top-level Object definition, and helpful + information has not been suppressed, generate an indented comment + to mark the "Begin" item and write it to the FitsChan as a comment + card with a blank keyword. */ + if ( write_nest && ( astGetFull( this ) >= 0 ) ) { + MakeIndentedComment( current_indent, '+', "Beginning of ", class, buff, status ); + astSetFitsCom( this, " ", buff, 0 ); + } + +/* Create a unique FITS keyword for this "Begin" item, basing it on + "BEGAST". */ + CreateKeyword( this, "BEGAST", keyword, status ); + +/* Generate a pre-quoted version of the class name. */ + PreQuote( class, buff, status ); + +/* Write the "Begin" item to the FitsChan as a keyword and string + value. */ + astSetFitsS( this, keyword, buff, + astGetComment( this ) ? comment : NULL, 0 ); + +/* Clear the count of items written. */ + items_written = 0; +} + +static void WriteDouble( AstChannel *this_channel, const char *name, + int set, int helpful, + double value, const char *comment, int *status ) { +/* +* Name: +* WriteDouble + +* Purpose: +* Write a double value to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteDouble( AstChannel *this, const char *name, +* int set, int helpful, +* double value, const char *comment ) + +* Class Membership: +* FitsChan member function (over-rides the protected +* astWriteDouble method inherited from the Channel class). + +* Description: +* This function writes a named double value, representing the +* value of a class instance variable, to the data sink associated +* with a FitsChan. It is intended for use by class "Dump" +* functions when writing out class information which will +* subsequently be re-read. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* FitsChan's Full attribute is set - either to permit all +* values to be shown, or to suppress non-essential information +* entirely. +* value +* The value to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the FitsChan supplied and the setting of its +* Comm attribute. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure. */ + char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Use the "set" and "helpful" flags, along with the FitsChan's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Create a unique FITS keyword from the name supplied. */ + CreateKeyword( this, name, keyword, status ); + +/* Write the value to the FitsChan as a keyword and value */ + astSetFitsF( this, keyword, value, + astGetComment( this ) ? comment : NULL, 0 ); + +/* If the value is not "set", replace the card just written by a COMMENT + card containing the text of the card as the comment. */ + if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status ); + +/* Increment the count of items written. */ + items_written++; + } +} + +static void WriteEnd( AstChannel *this_channel, const char *class, int *status ) { +/* +* Name: +* WriteEnd + +* Purpose: +* Write an "End" data item to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteEnd( AstChannel *this, const char *class ) + +* Class Membership: +* FitsChan member function (over-rides the protected astWriteEnd +* method inherited from the Channel class). + +* Description: +* This function writes an "End" data item to the data sink +* associated with a FitsChan. This item delimits the end of an +* Object definition. + +* Parameters: +* this +* Pointer to the FitsChan. +* class +* Pointer to a constant null-terminated string containing the +* class name of the Object whose definition is being terminated +* by the "End" item. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure. */ + char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; + /* Character buffer */ + char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Create a unique FITS keyword for this "End" item, basing it on + "ENDAST". */ + CreateKeyword( this, "ENDAST", keyword, status ); + +/* Generate a pre-quoted version of the class name. */ + PreQuote( class, buff, status ); + +/* Write the "End" item to the FitsChan as a keyword and string + value. */ + astSetFitsS( this, keyword, buff, + astGetComment( this ) ? "End of object definition" : NULL, + 0 ); + +/* If we are not ending a top-level Object definition, and helpful + information has not been suppressed, generate an indented comment + to mark the "End" item and write it to the FitsChan as a comment + card with a blank keyword. */ + if ( write_nest && ( astGetFull( this ) >= 0 ) ) { + MakeIndentedComment( current_indent, '-', "End of ", class, buff, status ); + astSetFitsCom( this, " ", buff, 0 ); + } + +/* Decrement the indentation level for comments. */ + current_indent -= INDENT_INC; +} + +static void WriteFits( AstFitsChan *this, int *status ){ + +/* +*++ +* Name: +c astWriteFits +f AST_WRITEFITS + +* Purpose: +* Write out all cards in a FitsChan to the sink function. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "fitschan.h" +c void astWriteFits( AstFitsChan *this ) +f CALL AST_WRITEFITS( THIS, STATUS ) + +* Class Membership: +* FitsChan method. + +* Description: +c This function +f This routine +* writes out all cards currently in the FitsChan. If the SinkFile +* attribute is set, they will be written out to the specified sink file. +* Otherwise, they will be written out using the sink function specified +* when the FitsChan was created. All cards are then deleted from the +* FitsChan. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsChan. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the SinkFile is unset, and no sink function is available, this +* method simply empties the FitsChan, and is then equivalent to +c astEmptyFits. +f AST_EMPTYFITS. +* - This method attempt to execute even if an error has occurred +* previously. +*-- +*/ + +/* Ensure a FitsChan was supplied. */ + if( this ) { + +/* Ensure the source function has been called */ + ReadFromSource( this, status ); + +/* We can usefully use the local destructor function to do the work, + since it only frees resources used within teh FitsChan, rather than + freeing the FitsChan itself. */ + Delete( (AstObject *) this, status ); + } +} + +static void WriteInt( AstChannel *this_channel, const char *name, + int set, int helpful, + int value, const char *comment, int *status ) { +/* +* Name: +* WriteInt + +* Purpose: +* Write an int value to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteInt( AstChannel *this, const char *name, +* int set, int helpful, +* int value, const char *comment ) + +* Class Membership: +* FitsChan member function (over-rides the protected +* astWriteInt method inherited from the Channel class). + +* Description: +* This function writes a named int value, representing the +* value of a class instance variable, to the data sink associated +* with a FitsChan. It is intended for use by class "Dump" +* functions when writing out class information which will +* subsequently be re-read. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* FitsChan's Full attribute is set - either to permit all +* values to be shown, or to suppress non-essential information +* entirely. +* value +* The value to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the FitsChan supplied and the setting of its +* Comm attribute. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure. */ + char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Use the "set" and "helpful" flags, along with the FitsChan's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Create a unique FITS keyword from the name supplied. */ + CreateKeyword( this, name, keyword, status ); + +/* Write the value to the FitsChan as a keyword and value */ + astSetFitsI( this, keyword, value, + astGetComment( this ) ? comment : NULL, 0 ); + +/* If the value is not "set", replace the card just written by a COMMENT + card containing the text of the card as the comment. */ + if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status ); + +/* Increment the count of items written. */ + items_written++; + } +} + +static void WriteIsA( AstChannel *this_channel, const char *class, + const char *comment, int *status ) { +/* +* Name: +* WriteIsA + +* Purpose: +* Write an "IsA" data item to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteIsA( AstChannel *this, const char *class, +* const char *comment ) + +* Class Membership: +* FitsChan member function (over-rides the protected astWriteIsA +* method inherited from the Channel class). + +* Description: +* This function writes an "IsA" data item to the data sink +* associated with a FitsChan. This item delimits the end of the +* data associated with the instance variables of a class, as part +* of an overall Object definition. + +* Parameters: +* this +* Pointer to the FitsChan. +* class +* Pointer to a constant null-terminated string containing the +* name of the class whose data are terminated by the "IsA" +* item. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the "IsA" +* item. Normally, this will describe the purpose of the class +* whose data are being terminated. + +* Notes: +* - The comment supplied may not actually be used, depending on +* the nature of the FitsChan supplied. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure. */ + char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; + /* Character buffer */ + char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Output an "IsA" item only if there has been at least one item + written since the last "Begin" or "IsA" item, or if the Full + attribute for the Channel is greater than zero (requesting maximum + information). */ + if ( items_written || astGetFull( this ) > 0 ) { + +/* Create a unique FITS keyword for this "IsA" item, basing it on + "ISA". */ + CreateKeyword( this, "ISA", keyword, status ); + +/* Generate a pre-quoted version of the class name. */ + PreQuote( class, buff, status ); + +/* Write the "IsA" item to the FitsChan as a keyword and string + value. */ + astSetFitsS( this, keyword, buff, + astGetComment( this ) ? comment : NULL, 0 ); + +/* If helpful information has not been suppressed, generate an + indented comment to mark the "IsA" item and write it to the + FitsChan as a comment card with a blank keyword. */ + if ( astGetFull( this ) >= 0 ) { + MakeIndentedComment( current_indent, '.', "Class boundary", "", + buff, status ); + astSetFitsCom( this, " ", buff, 0 ); + } + } + +/* Clear the count of items written. */ + items_written = 0; +} + +static void WriteObject( AstChannel *this_channel, const char *name, + int set, int helpful, + AstObject *value, const char *comment, int *status ) { +/* +* Name: +* WriteObject + +* Purpose: +* Write an Object value to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteObject( AstChannel *this, const char *name, +* int set, int helpful, +* AstObject *value, const char *comment ) + +* Class Membership: +* FitsChan member function (over-rides the protected +* astWriteObject method inherited from the Channel class). + +* Description: +* This function writes a named Object value, representing the +* value of a class instance variable, to the data sink associated +* with a FitsChan. It is intended for use by class "Dump" +* functions when writing out class information which will +* subsequently be re-read. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* FitsChan's Full attribute is set - either to permit all +* values to be shown, or to suppress non-essential information +* entirely. +* value +* A pointer to the Object to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the FitsChan supplied and the setting of its +* Comm attribute. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure. */ + char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Use the "set" and "helpful" flags, along with the FitsChan's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Create a unique FITS keyword from the name supplied. */ + CreateKeyword( this, name, keyword, status ); + +/* Write the value to the FitsChan as a keyword and a blank string value, + not pre-quoted (this "null" value indicates that an Object description + follows). */ + astSetFitsS( this, keyword, "", + astGetComment( this ) ? comment : NULL, 0 ); + +/* If the value is "set", write out the Object description. */ + if ( set ) { + astWrite( this, value ); + +/* If the value is not set, replace the card just written to the FitsChan + by COMENT card containing the keyword and blank string value (do not + write out the Object description). */ + } else { + MakeIntoComment( this, "astWrite", astGetClass( this ), status ); + } + +/* Increment the count of items written. */ + items_written++; + } +} + +static void WriteToSink( AstFitsChan *this, int *status ){ +/* +* Name: +* WriteToSink + +* Purpose: +* Write the contents of the FitsChan out to the sink file or function. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteToSink( AstFitsChan *this, int *status ) + +* Class Membership: +* FitsChan member function. + +* Description: +* If the SinkFile attribute is set, each card in the FitsChan is +* written out to the sink file. Otherwise, the cards are passed in +* turn to the sink function specified when the FitsChan was created. +* If no sink function was provided, the cards are not written out. +* Cards marked as having been read into an AST object are not written +* out. + +* Parameters: +* this +* Pointer to the FitsChan. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The current card is left unchanged. +*/ + +/* Local Constants: */ +#define ERRBUF_LEN 80 + +/* Local Variables: */ + FILE *fd; /* File descriptor for sink file */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *errstat; /* Pointer for system error message */ + char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + const char *sink_file; /* Path to output sink file */ + int icard; /* Current card index on entry */ + int old_ignore_used; /* Original value of external variable ignore_used */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* If the SinkFile attribute is set, open the file. */ + fd = NULL; + if( astTestSinkFile( this ) ) { + sink_file = astGetSinkFile( this ); + fd = fopen( sink_file, "w" ); + if( !fd ) { + if ( errno ) { +#if HAVE_STRERROR_R + strerror_r( errno, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( errno ); +#endif + astError( AST__WRERR, "astDelete(%s): Failed to open output " + "SinkFile '%s' - %s.", status, astGetClass( this ), + sink_file, errstat ); + } else { + astError( AST__WRERR, "astDelete(%s): Failed to open output " + "SinkFile '%s'.", status, astGetClass( this ), + sink_file ); + } + } + } + +/* Only proceed if a file was opened, or sink function and wrapper were supplied. */ + if( fd || ( this->sink && this->sink_wrap ) ){ + +/* Store the current card index. */ + icard = astGetCard( this ); + +/* Indicate that cards which have been read into an AST object should skipped + over by the functions which navigate the linked list of cards. */ + old_ignore_used = ignore_used; + ignore_used = 1; + +/* Ensure that the first card in the FitsChan will be the next one to be + read. */ + astSetCard( this, 1 ); + +/* Loop round obtaining and writing out each card, until all cards have been + processed. */ + while( !astFitsEof( this ) && astOK ){ + +/* Get the current card, and write it out through the sink function. + The call to astFindFits increments the current card. */ + if( astFindFits( this, "%f", card, 1 ) ) { + +/* If s sink file was opened, write the card out to it. */ + if( fd ) { + fprintf( fd, "%s\n", card ); + +/* Otherwise, use the isnk function. The sink function is an externally + supplied function which may not be thread-safe, so lock a mutex first. + Also store the channel data pointer in a global variable so that it can + be accessed in the sink function using macro astChannelData. */ + } else { + astStoreChannelData( this ); + LOCK_MUTEX3; + ( *this->sink_wrap )( *this->sink, card, status ); + UNLOCK_MUTEX3; + } + } + } + +/* Re-instate the original flag indicating if cards marked as having been + read should be skipped over. */ + ignore_used = old_ignore_used; + +/* Set the current card index back to what it was on entry. */ + astSetCard( this, icard ); + } + +/* Close the sink file. */ + if( fd ) fclose( fd ); +} + +static void WriteString( AstChannel *this_channel, const char *name, + int set, int helpful, + const char *value, const char *comment, int *status ) { +/* +* Name: +* WriteString + +* Purpose: +* Write a string value to a data sink. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* void WriteString( AstChannel *this, const char *name, +* int set, int helpful, +* const char *value, const char *comment ) + +* Class Membership: +* FitsChan member function (over-rides the protected +* astWriteString method inherited from the Channel class). + +* Description: +* This function writes a named string value, representing the +* value of a class instance variable, to the data sink associated +* with a FitsChan. It is intended for use by class "Dump" +* functions when writing out class information which will +* subsequently be re-read. + +* Parameters: +* this +* Pointer to the FitsChan. +* name +* Pointer to a constant null-terminated string containing the +* name to be used to identify the value in the external +* representation. This will form the key for identifying it +* again when it is re-read. The name supplied should be unique +* within its class. +* +* Mixed case may be used and will be preserved in the external +* representation (where possible) for cosmetic effect. However, +* case is not significant when re-reading values. +* +* It is recommended that a maximum of 6 alphanumeric characters +* (starting with an alphabetic character) be used. This permits +* maximum flexibility in adapting to standard external data +* representations (e.g. FITS). +* set +* If this is zero, it indicates that the value being written is +* a default value (or can be re-generated from other values) so +* need not necessarily be written out. Such values will +* typically be included in the external representation with +* (e.g.) a comment character so that they are available to +* human readers but will be ignored when re-read. They may also +* be completely omitted in some circumstances. +* +* If "set" is non-zero, the value will always be explicitly +* included in the external representation so that it can be +* re-read. +* helpful +* This flag provides a hint about whether a value whose "set" +* flag is zero (above) should actually appear at all in the +* external representaton. +* +* If the external representation allows values to be "commented +* out" then, by default, values will be included in this form +* only if their "helpful" flag is non-zero. Otherwise, they +* will be omitted entirely. When possible, omitting the more +* obscure values associated with a class is recommended in +* order to improve readability. +* +* This default behaviour may be further modified if the +* FitsChan's Full attribute is set - either to permit all +* values to be shown, or to suppress non-essential information +* entirely. +* value +* Pointer to a constant null-terminated string containing the +* value to be written. +* comment +* Pointer to a constant null-terminated string containing a +* textual comment to be associated with the value. +* +* Note that this comment may not actually be used, depending on +* the nature of the FitsChan supplied and the setting of its +* Comm attribute. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFitsChan *this; /* Pointer to the FitsChan structure. */ + char *c; /* Pointer to next buffer character */ + char buff1[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for a single substring */ + char buff2[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for pre-quoted string */ + char cc; /* Next character */ + char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */ + const char *start; /* Pointer to start of substring */ + int first; /* Is this the first sub-string? */ + int nc; /* No. of available columns remaining */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_channel; + +/* Use the "set" and "helpful" flags, along with the FitsChan's + attributes to decide whether this value should actually be + written. */ + if ( Use( this, set, helpful, status ) ) { + +/* Create a unique FITS keyword from the name supplied. */ + CreateKeyword( this, name, keyword, status ); + +/* Store a pointer to the start of the next sub-string (i.e. the + beggining of the string), and then loop round until the end of the + string is reached. */ + start = value; + first = 1; + while( *start && astOK ){ + +/* Store the number of characters available in the 80 column header card + for the next substring, leaving room for the "= " string at the start, + and the delimiting quotes. Also reserve 2 characters to allow for the + possibility of double quotes being needed to protect trailing white space + (see function PreQuote). */ + nc = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 6; + +/* If this is the first sub-string reserve room for any comment. */ + if( first ){ + if( comment && comment[0] ) nc -= ChrLen( comment, status ) + 3; + +/* If the first card will be turned into a comment card, we need to leave room + for the keyword name and equals sign, etc, within the 80 columns. */ + if( !set ) nc -= FITSNAMLEN + 5; + } + +/* We need to check the sub-string for single quotes since these will + take up 2 characters each instead of 1 when encoded since single quotes + within a string are doubled. Search through from the starting + character, copying the sub-string into a buffer, and reducing the number + of available characters remaining in the card for each character. */ + c = buff1; + while( *start && nc > 0 ){ + cc = *(start++); + *(c++) = cc; + if( cc == '\'' ) { + nc -= 2; + } else { + nc -= 1; + } + } + +/* If the last character in the substring was a single quote, there may + not have been room for the extra quote which is added when the + sub-string is encoded. In this case we need to back up a character in + order to remove the single quote frin this substring and move it into + the next sub-string. */ + if( nc < 0 ){ + start--; + c--; + } + +/* If the supplied value has not been exhausted, append an ampersand to + the string. In this case we need to move the last character in the + substring into the next substring to make room for the ampersand. */ + if( *start ) { + start--; + c--; + *(c++) = '&'; + } + +/* Terminate the buffer. */ + *c = 0; + +/* The FITS standard considers trailing white space is be insignificant, + and so we need to guard against external applications throwing away + significant trailing white space. This is done by encosing the string, + including trailing white space, in double quotes. */ + PreQuote( buff1, buff2, status ); + +/* On the first pass through this loop, write the value to the FitsChan as + a keyword and value */ + if( first ){ + astSetFitsS( this, keyword, buff2, + astGetComment( this ) ? comment : NULL, 0 ); + +/* If the value is not "set", replace the card just written by a COMMENT + card containing the text of the card as the comment. */ + if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status ); + +/* On subsequent passes through the loop, store the string using a CONTINUE + keyword, with type AST__CONTINUE (this type is like AST__STRING but is + formatted without an equals sign). */ + } else { + astSetFitsCN( this, "CONTINUE", buff2, NULL, 0 ); + } + first = 0; + } + +/* Increment the count of items written. */ + items_written++; + } +} + +static AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s, + int naxes, int zpxaxes[2], const char *method, + const char *class, int *status ){ +/* +* Name: +* ZPXMapping + +* Purpose: +* Create a Mapping descriping "-ZPX" (IRAF) distortion. + +* Type: +* Private function. + +* Synopsis: +* AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s, +* int naxes, int zpxaxes[2], const char *method, +* const char *class, int *status ) + +* Class Membership: +* FitsChan + +* Description: +* This function uses the values in the supplied FitsStore to create a +* Mapping which implements the "-ZPX" distortion code, produced by +* the IRAF project. See: +* +* http://iraf.noao.edu/projects/ccdmosaic/zpx.html +* +* Note, the Mapping created by this function implements the "lngcor" +* and "latcor" corrections described in the WAT... keywords. The +* basic ZPN projection code is handled in the normal way, as any +* other projection is handled. + +* Parameters: +* store +* A structure containing information about the requested axis +* descriptions derived from a FITS header. +* s +* A character identifying the co-ordinate version to use. A space +* means use primary axis descriptions. Otherwise, it must be an +* upper-case alphabetical characters ('A' to 'Z'). +* naxes +* The number of intermediate world coordinate axes (WCSAXES). +* zpxaxes +* The zero-based indices of the two IWC axes that use the ZPX projection. +* method +* A pointer to a string holding the name of the calling method. +* This is used only in the construction of error messages. +* class +* A pointer to a string holding the class of the object being +* read. This is used only in the construction of error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Mapping. +*/ + +/* Local Variables: */ + AstMapping *ret; + char *watstr; + double *cvals[ 2 ]; + int *mvals[ 2 ]; + int ncoeff[ 2 ]; + int i; + int icoeff; + int ok; + +/* Initialise the pointer to the returned Mapping. */ + ret = NULL; + +/* Check the global status. */ + if ( !astOK ) return ret; + +/* Check both axes */ + for( i = 0; i < 2; i++ ){ + mvals[ i ] = NULL; + cvals[ i ] = NULL; + ncoeff[ i ] = 0; + +/* Concatenate all the IRAF "WAT" keywords together for this axis. These + keywords are marked as having been used, so that they are not written + out when the FitsChan is deleted. */ + watstr = ConcatWAT( this, zpxaxes[ i ], method, class, status ); + +/* Extract the polynomial coefficients from the concatenated WAT string. + These are returned in the form of a list of PVi_m values for a TPN + projection. */ + ncoeff[ i ] = WATCoeffs( watstr, i, cvals + i, mvals + i, &ok, status ); + +/* If the current axis of the ZPX projection uses features not supported + by AST, do not do any more axes. */ + if( !ok ) break; + +/* Free the WAT string. */ + watstr = astFree( watstr ); + } + +/* If we can handle the ZPX projection, store the polynomial coefficients in + a new inverted TPN WcsMap. This WcsMap is used as a correction to the ZPN + WcsMap to be created later, therefore set its FITSProj value to zero so + that it is not used as the FITS projection when written out via + astWrite. Also set TPNTan to zero to indicate that the TAN part of the + TPN projection should not be used (i.e. just use the polynomial part). */ + if( ok && astOK ) { + + if( ncoeff[ 0 ] || ncoeff[ 1 ] ) { + ret = (AstMapping *) astWcsMap( naxes, AST__TPN, zpxaxes[ 0 ] + 1, + zpxaxes[ 1 ] + 1, "Invert=1", + status ); + astSetFITSProj( ret, 0 ); + astSetTPNTan( ret, 0 ); + for( i = 0; i < 2; i++ ){ + for( icoeff = 0; icoeff < ncoeff[ i ]; icoeff++ ) { + astSetPV( ret, zpxaxes[ i ], (mvals[ i ])[ icoeff ], + (cvals[ i ])[ icoeff ] ); + } + } + + } else { + ret = (AstMapping *) astUnitMap( naxes, " ", status ); + } + +/* If the TNX cannot be represented in FITS-WCS (within our restrictions), add + warning keywords to the FitsChan. */ + } else { + Warn( this, "zpx", "This FITS header includes, or was " + "derived from, a ZPX projection which requires " + "unsupported IRAF-specific corrections. The WCS " + "information may therefore be incorrect.", method, class, + status ); + } + +/* Return the result. */ + return ret; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ + +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Card. */ +/* ===== */ + +/* +*att++ +* Name: +* Card + +* Purpose: +* Index of current FITS card in a FitsChan. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute gives the index of the "current" FITS header card +* within a FitsChan, the first card having an index of 1. The +c choice of current card affects the behaviour of functions that +f choice of current card affects the behaviour of routines that +c access the contents of the FitsChan, such as astDelFits, +c astFindFits and astPutFits. +f access the contents of the FitsChan, such as AST_DELFITS, +f AST_FINDFITS and AST_PUTFITS. +* +* A value assigned to Card will position the FitsChan at any +* desired point, so that a particular card within it can be +* accessed. Alternatively, the value of Card may be enquired in +* order to determine the current position of a FitsChan. +* +* The default value of Card is 1. This means that clearing +c this attribute (using astClear) effectively "rewinds" the +f this attribute (using AST_CLEAR) effectively "rewinds" the +* FitsChan, so that the first card is accessed next. If Card is +* set to a value which exceeds the total number of cards in the +* FitsChan (as given by its Ncard attribute), it is regarded as +* pointing at the "end-of-file". In this case, the value returned +* in response to an enquiry is always one more than the number of +* cards in the FitsChan. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* Encoding. */ +/* ========= */ + +/* +*att++ +* Name: +* Encoding + +* Purpose: +* System for encoding Objects as FITS headers. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the encoding system to use when AST +* Objects are stored as FITS header cards in a FitsChan. It +c affects the behaviour of the astWrite and astRead functions when +f affects the behaviour of the AST_WRITE and AST_READ routines when +* they are used to transfer any AST Object to or from an external +* representation consisting of FITS header cards (i.e. whenever a +* write or read operation is performed using a FitsChan as the I/O +* Channel). +* +* There are several ways (conventions) by which coordinate system +* information may be represented in the form of FITS headers and +* the Encoding attribute is used to specify which of these should +* be used. The encoding options available are outlined in the +* "Encodings Available" section below, and in more detail in the +* sections which follow. +* +* Encoding systems differ in the range of possible Objects +* (e.g. classes) they can represent, in the restrictions they +* place on these Objects (e.g. compatibility with some +* externally-defined coordinate system model) and in the number of +* Objects that can be stored together in any particular set of +* FITS header cards (e.g. multiple Objects, or only a single +* Object). The choice of encoding also affects the range of +* external applications which can potentially read and interpret +* the FITS header cards produced. +* +* The encoding options available are not necessarily mutually +* exclusive, and it may sometimes be possible to store multiple +* Objects (or the same Object several times) using different +* encodings within the same set of FITS header cards. This +* possibility increases the likelihood of other applications being +* able to read and interpret the information. +* +* By default, a FitsChan will attempt to determine which encoding +* system is already in use, and will set the default Encoding +* value accordingly (so that subsequent I/O operations adopt the +* same conventions). It does this by looking for certain critical +* FITS keywords which only occur in particular encodings. For +* details of how this works, see the "Choice of Default Encoding" +* section below. If you wish to ensure that a particular encoding +* system is used, independently of any FITS cards already present, +* you should set an explicit Encoding value yourself. + +* Encodings Available: +* The Encoding attribute can take any of the following (case +* insensitive) string values to select the corresponding encoding + +* system: +* +* - "DSS": Encodes coordinate system information in FITS header +* cards using the convention developed at the Space Telescope +* Science Institute (STScI) for the Digitised Sky Survey (DSS) +* astrometric plate calibrations. The main advantages of this +* encoding are that FITS images which use it are widely available +* and it is understood by a number of important and +* well-established astronomy applications. For further details, +* see the section "The DSS Encoding" below. +* +* - "FITS-WCS": Encodes coordinate system information in FITS +* header cards using the conventions described in the FITS +* world coordinate system (FITS-WCS) papers by E.W. Greisen, +* M. Calabretta, et al. The main advantages of this encoding are that +* it should be understood by any FITS-WCS compliant application and +* is likely to be adopted widely for FITS data in future. For further +* details, see the section "The FITS-WCS Encoding" below. +* +* - "FITS-PC": Encodes coordinate system information in FITS +* header cards using the conventions described in an earlier draft +* of the FITS world coordinate system papers by E.W. Greisen and +* M. Calabretta. This encoding uses a combination of CDELTi and +* PCiiijjj keywords to describe the scale and rotation of the pixel +* axes. This encoding is included to support existing data and +* software which uses these now superceded conventions. In general, +* the "FITS-WCS" encoding (which uses CDi_j or PCi_j keywords to +* describe the scale and rotation) should be used in preference to +* "FITS-PC". +* +* - "FITS-IRAF": Encodes coordinate system information in FITS +* header cards using the conventions described in the document +* "World Coordinate Systems Representations Within the FITS +* Format" by R.J. Hanisch and D.G. Wells, 1988. This encoding is +* currently employed by the IRAF data analysis facility, so its +* use will facilitate data exchange with IRAF. Its main advantages +* are that it is a stable convention which approximates to a +* subset of the propsed FITS-WCS encoding (above). This makes it +* suitable as an interim method for storing coordinate system +* information in FITS headers until the FITS-WCS encoding becomes +* stable. Since many datasets currently use the FITS-IRAF +* encoding, conversion of data from FITS-IRAF to the final form of +* FITS-WCS is likely to be well supported. +* +* - "FITS-AIPS": Encodes coordinate system information in FITS +* header cards using the conventions originally introduced by the +* AIPS data analysis facility. This is base on the use of CDELTi and +* CROTAi keuwords to desribe the scale and rotation of each axis. +* These conventions have been superceded but are still widely used. +* +* - "FITS-AIPS++": Encodes coordinate system information in FITS +* header cards using the conventions used by the AIPS++ project. +* This is an extension of FITS-AIPS which includes some of the +* features of FITS-IRAF and FITS-PC. +* +* - "FITS-CLASS": Encodes coordinate system information in FITS +* header cards using the conventions used by the CLASS project. +* CLASS is a software package for reducing single-dish radio and +* sub-mm spectroscopic data. See the section "CLASS FITS format" at +* http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/. +* +* - "NATIVE": Encodes AST Objects in FITS header cards using a +* convention which is private to the AST library (but adheres to +* the general FITS standard) and which uses FITS keywords that +* will not clash with other encoding systems. The main advantages +* of this are that any class of AST Object may be encoded, and any +* (reasonable) number of Objects may be stored sequentially in the +* same FITS header. This makes FITS headers an almost loss-less +* communication path for passing AST Objects between applications +* (although all such applications must, of course, make use of the +* AST library to interpret the information). For further details, +* see the section "The NATIVE Encoding" below. + +* Choice of Default Encoding: +* If the Encoding attribute of a FitsChan is not set, the default +* value it takes is determined by the presence of certain critical +* FITS keywords within the FitsChan. The sequence of decisions + +* used to arrive at the default value is as follows: +* +* - If the FitsChan contains any keywords beginning with the +* string "BEGAST", then NATIVE encoding is used, +* - Otherwise, FITS-CLASS is used if the FitsChan contains a DELTAV +* keyword and a keyword of the form VELO-xxx, where xxx indicates one +* of the rest frames used by class (e.g. "VELO-LSR"), or "VLSR". +* - Otherwise, if the FitsChan contains a CTYPE keyword which +* represents a spectral axis using the conventions of the AIPS and +* AIPS++ projects (e.g. "FELO-LSR", etc), then one of FITS-AIPS or +* FITS-AIPS++ encoding is used. FITS-AIPS++ is used if any of the +* keywords CDi_j, PROJP, LONPOLE or LATPOLE are +* found in the FitsChan. Otherwise FITS-AIPS is used. +* - Otherwise, if the FitsChan contains a keyword of the form +* "PCiiijjj", where "i" and "j" are single digits, then +* FITS-PC encoding is used, +* - Otherwise, if the FitsChan contains a keyword of the form +* "CDiiijjj", where "i" and "j" are single digits, then +* FITS-IRAF encoding is used, +* - Otherwise, if the FitsChan contains a keyword of the form +* "CDi_j", and at least one of RADECSYS, PROJPi, or CjVALi +* where "i" and "j" are single digits, then FITS-IRAF encoding is +* used. +* - Otherwise, if the FitsChan contains any keywords of the form +* PROJPi, CjVALi or RADECSYS, where "i" and "j" are single digits, +* then FITS-PC encoding is used. +* - Otherwise, if the FitsChan contains a keyword of the form +* CROTAi, where "i" is a single digit, then FITS-AIPS encoding is +* used. +* - Otherwise, if the FitsChan contains a keyword of the form +* CRVALi, where "i" is a single digit, then FITS-WCS encoding is +* used. +* - Otherwise, if the FitsChan contains the "PLTRAH" keyword, then +* DSS encoding is used, +* - Otherwise, if none of these conditions is met (as would be the +* case when using an empty FitsChan), then NATIVE encoding is +* used. +* +* Except for the NATIVE and DSS encodings, all the above checks +* also require that the header contains at least one CTYPE, CRPIX and +* CRVAL keyword (otherwise the checking process continues to the next +* case). +* +* Setting an explicit value for the Encoding attribute always +* over-rides this default behaviour. +* +* Note that when writing information to a FitsChan, the choice of +* encoding will depend greatly on the type of application you +* expect to be reading the information in future. If you do not +* know this, there may sometimes be an advantage in writing the +* information several times, using a different encoding on each +* occasion. + +* The DSS Encoding: +* The DSS encoding uses FITS header cards to store a multi-term +* polynomial which relates pixel positions on a digitised +* photographic plate to celestial coordinates (right ascension and +* declination). This encoding may only be used to store a single +* AST Object in any set of FITS header cards, and that Object must +* be a FrameSet which conforms to the STScI/DSS coordinate system +* model (this means the Mapping which relates its base and current +* Frames must include either a DssMap or a WcsMap with type +* AST__TAN or AST__TPN). +* +c When reading a DSS encoded Object (using astRead), the FitsChan +f When reading a DSS encoded Object (using AST_READ), the FitsChan +* concerned must initially be positioned at the first card (its +* Card attribute must equal 1) and the result of the read, if +* successful, will always be a pointer to a FrameSet. The base +* Frame of this FrameSet represents DSS pixel coordinates, and the +* current Frame represents DSS celestial coordinates. Such a read +* is always destructive and causes the FITS header cards required +* for the construction of the FrameSet to be removed from the +* FitsChan, which is then left positioned at the "end-of-file". A +* subsequent read using the same encoding will therefore not +* return another FrameSet, even if the FitsChan is rewound. +* +c When astWrite is used to store a FrameSet using DSS encoding, +f When AST_WRITE is used to store a FrameSet using DSS encoding, +* an attempt is first made to simplify the FrameSet to see if it +* conforms to the DSS model. Specifically, the current Frame must +* be a FK5 SkyFrame; the projection must be a tangent plane +* (gnomonic) projection with polynomial corrections conforming to +* DSS requirements, and north must be parallel to the second base +* Frame axis. +* +* If the simplification process succeeds, a description of the +* FrameSet is written to the FitsChan using appropriate DSS FITS +* header cards. The base Frame of the FrameSet is used to form the +* DSS pixel coordinate system and the current Frame gives the DSS +* celestial coordinate system. A successful write operation will +* over-write any existing DSS encoded data in the FitsChan, but +* will not affect other (non-DSS) header cards. If a destructive +* read of a DSS encoded Object has previously occurred, then an +* attempt will be made to store the FITS header cards back in +* their original locations. +* +* If an attempt to simplify a FrameSet to conform to the DSS model +* fails (or if the Object supplied is not a FrameSet), then no +c data will be written to the FitsChan and astWrite will return +f data will be written to the FitsChan and AST_WRITE will return +* zero. No error will result. + +* The FITS-WCS Encoding: +* The FITS-WCS convention uses FITS header cards to describe the +* relationship between pixels in an image (not necessarily +* 2-dimensional) and one or more related "world coordinate systems". +* The FITS-WCS encoding may only be used to store a single AST Object +* in any set of FITS header cards, and that Object must be a FrameSet +* which conforms to the FITS-WCS model (the FrameSet may, however, +* contain multiple Frames which will be result in multiple FITS +* "alternate axis descriptions"). Details of the use made by this +* Encoding of the conventions described in the FITS-WCS papers are +* given in the appendix "FITS-WCS Coverage" of this document. A few +* main points are described below. +* +* The rotation and scaling of the intermediate world coordinate system +* can be specified using either "CDi_j" keywords, or "PCi_j" together +* with "CDELTi" keywords. When writing a FrameSet to a FitsChan, the +* the value of the CDMatrix attribute of the FitsChan determines +* which system is used. +* +* In addition, this encoding supports the "TAN with polynomial correction +* terms" projection which was included in a draft of the FITS-WCS paper, +* but was not present in the final version. A "TAN with polynomial +* correction terms" projection is represented using a WcsMap with type +* AST__TPN (rather than AST__TAN which is used to represent simple +* TAN projections). When reading a FITS header, a CTYPE keyword value +* including a "-TAN" code results in an AST__TPN projection if there are +* any projection parameters (given by the PVi_m keywords) associated with +* the latitude axis, or if there are projection parameters associated +* with the longitude axis for m greater than 4. When writing a +* FrameSet to a FITS header, an AST__TPN projection gives rise to a +* CTYPE value including the normal "-TAN" code, but the projection +* parameters are stored in keywords with names "QVi_m", instead of the +* usual "PVi_m". Since these QV parameters are not part of the +* FITS-WCS standard they will be ignored by other non-AST software, +* resulting in the WCS being interpreted as a simple TAN projection +* without any corrections. This should be seen as an interim solution +* until such time as an agreed method for describing projection +* distortions within FITS-WCS has been published. +* +* AST extends the range of celestial coordinate systems which may be +* described using this encoding by allowing the inclusion of +* "AZ--" and "EL--" as the coordinate specification within CTYPE +* values. These form a longitude/latitude pair of axes which describe +* azimuth and elevation. The geographic position of the observer +* should be supplied using the OBSGEO-X/Y/Z keywords described in FITS-WCS +* paper III. Currently, a simple model is used which includes diurnal +* aberration, but ignores atmospheric refraction, polar motion, etc. +* These may be added in a later release. +* +* If an AST SkyFrame that represents offset rather than absolute +* coordinates (see attribute SkyRefIs) is written to a FitsChan using +* FITS-WCS encoding, two alternate axis descriptions will be created. +* One will describe the offset coordinates, and will use "OFLN" and +* "OFLT" as the axis codes in the CTYPE keywords. The other will +* describe absolute coordinates as specified by the System attribute +* of the SkyFrame, using the usual CTYPE codes ("RA--"/"DEC-", etc). +* In addition, the absolute coordinates description will contain +* AST-specific keywords (SREF1/2, SREFP1/2 and SREFIS) that allow the +* header to be read back into AST in order to reconstruct the original +* SkyFrame. +* +c When reading a FITS-WCS encoded Object (using astRead), the FitsChan +f When reading a FITS-WCS encoded Object (using AST_READ), the FitsChan +* concerned must initially be positioned at the first card (its +* Card attribute must equal 1) and the result of the read, if +* successful, will always be a pointer to a FrameSet. The base +* Frame of this FrameSet represents FITS-WCS pixel coordinates, +* and the current Frame represents the physical coordinate system +* described by the FITS-WCS primary axis descriptions. If +* secondary axis descriptions are also present, then the FrameSet +* may contain additional (non-current) Frames which represent +* these. Such a read is always destructive and causes the FITS +* header cards required for the construction of the FrameSet to be +* removed from the FitsChan, which is then left positioned at the +* "end-of-file". A subsequent read using the same encoding will +* therefore not return another FrameSet, even if the FitsChan is +* rewound. +* +c When astWrite is used to store a FrameSet using FITS-WCS +f When AST_WRITE is used to store a FrameSet using FITS-WCS +* encoding, an attempt is first made to simplify the FrameSet to +* see if it conforms to the FITS-WCS model. If this simplification +* process succeeds (as it often should, as the model is reasonably +* flexible), a description of the FrameSet is written to the +* FitsChan using appropriate FITS header cards. The base Frame of +* the FrameSet is used to form the FITS-WCS pixel coordinate +* system and the current Frame gives the physical coordinate +* system to be described by the FITS-WCS primary axis +* descriptions. Any additional Frames in the FrameSet may be used +* to construct secondary axis descriptions, where appropriate. +* +* A successful write operation will over-write any existing +* FITS-WCS encoded data in the FitsChan, but will not affect other +* (non-FITS-WCS) header cards. If a destructive read of a FITS-WCS +* encoded Object has previously occurred, then an attempt will be +* made to store the FITS header cards back in their original +* locations. Otherwise, the new cards will be inserted following +* any other FITS-WCS related header cards present or, failing +* that, in front of the current card (as given by the Card +* attribute). +* +* If an attempt to simplify a FrameSet to conform to the FITS-WCS +* model fails (or if the Object supplied is not a FrameSet), then +c no data will be written to the FitsChan and astWrite will +f no data will be written to the FitsChan and AST_WRITE will +* return zero. No error will result. + +* The FITS-IRAF Encoding: +* The FITS-IRAF encoding can, for most purposes, be considered as +* a subset of the FITS-WCS encoding (above), although it differs +* in the details of the FITS keywords used. It is used in exactly +* the same way and has the same restrictions, but with the + +* addition of the following: +* +* - The only celestial coordinate systems that may be represented +* are equatorial, galactic and ecliptic, +* - Sky projections can be represented only if any associated +* projection parameters are set to their default values. +* - Secondary axis descriptions are not supported, so when writing +* a FrameSet to a FitsChan, only information from the base and +* current Frames will be stored. +* +* Note that this encoding is provided mainly as an interim measure to +* provide a more stable alternative to the FITS-WCS encoding until the +* FITS standard for encoding WCS information is finalised. The name +* "FITS-IRAF" indicates the general keyword conventions used and does +* not imply that this encoding will necessarily support all features of +* the WCS scheme used by IRAF software. Nevertheless, an attempt has +* been made to support a few such features where they are known to be +* used by important sources of data. +* +* When writing a FrameSet using the FITS-IRAF encoding, axis rotations +* are specified by a matrix of FITS keywords of the form "CDi_j", where +* "i" and "j" are single digits. The alternative form "CDiiijjj", which +* is also in use, is recognised when reading an Object, but is never +* written. +* +* In addition, the experimental IRAF "ZPX" and "TNX" sky projections will +* be accepted when reading, but will never be written (the corresponding +* FITS "ZPN" or "distorted TAN" projection being used instead). However, +* there are restrictions on the use of these experimental projections. +* For "ZPX", longitude and latitude correction surfaces (appearing as +* "lngcor" or "latcor" terms in the IRAF-specific "WAT" keywords) are +* not supported. For "TNX" projections, only cubic surfaces encoded as +* simple polynomials with "half cross-terms" are supported. If an +* un-usable "TNX" or "ZPX" projection is encountered while reading +* from a FitsChan, a simpler form of TAN or ZPN projection is used +* which ignores the unsupported features and may therefore be +* inaccurate. If this happens, a warning message is added to the +* contents of the FitsChan as a set of cards using the keyword "ASTWARN". +* +* You should not normally attempt to mix the foreign FITS encodings within +* the same FitsChan, since there is a risk that keyword clashes may occur. + +* The FITS-PC Encoding: +* The FITS-PC encoding can, for most purposes, be considered as +* equivalent to the FITS-WCS encoding (above), although it differs +* in the details of the FITS keywords used. It is used in exactly +* the same way and has the same restrictions. + +* The FITS-AIPS Encoding: +* The FITS-AIPS encoding can, for most purposes, be considered as +* equivalent to the FITS-WCS encoding (above), although it differs +* in the details of the FITS keywords used. It is used in exactly +* the same way and has the same restrictions, but with the + +* addition of the following: +* +* - The only celestial coordinate systems that may be represented +* are equatorial, galactic and ecliptic, +* - Spectral axes can only be represented if they represent +* frequency, radio velocity or optical velocity, and are linearly +* sampled in frequency. In addition, the standard of rest +* must be LSRK, LSRD, barycentric or geocentric. +* - Sky projections can be represented only if any associated +* projection parameters are set to their default values. +* - The AIT, SFL and MER projections can only be written if the CRVAL +* keywords are zero for both longitude and latitude axes. +* - Secondary axis descriptions are not supported, so when writing +* a FrameSet to a FitsChan, only information from the base and +* current Frames will be stored. +* - If there are more than 2 axes in the base and current Frames, any +* rotation must be restricted to the celestial plane, and must involve +* no shear. + +* The FITS-AIPS++ Encoding: +* The FITS-AIPS++ encoding is based on the FITS-AIPS encoding, but +* includes some features of the FITS-IRAF and FITS-PC encodings. +* Specifically, any celestial projections supported by FITS-PC may be +* used, including those which require parameterisation, and the axis +* rotation and scaling may be specified using CDi_j keywords. When +* writing a FITS header, rotation will be specified using CROTA/CDELT +* keywords if possible, otherwise CDi_j keywords will be used instead. + +* The FITS-CLASS Encoding: +* The FITS-CLASS encoding uses the conventions of the CLASS project. +* These are described in the section "Developer Manual"/"CLASS FITS + +* Format" contained in the CLASS documentation at: +* +* http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/class.html. +* + +* This encoding is similar to FITS-AIPS with the following restrictions: +* +* - When a SpecFrame is created by reading a FITS-CLASS header, the +* attributes describing the observer's position (ObsLat, ObsLon and +* ObsAlt) are left unset because the CLASS encoding does not specify +* these values. Conversions to or from the topocentric standard of rest +* will therefore be inaccurate (typically by up to about 0.5 km/s) +* unless suitable values are assigned to these attributes after the +* FrameSet has been created. +* - When writing a FrameSet to a FITS-CLASS header, the current Frame +* in the FrameSet must have at least 3 WCS axes, of which one must be +* a linear spectral axis. The spectral axis in the created header will +* always describe frequency. If the spectral axis in the supplied +* FrameSet refers to some other system (e.g. radio velocity, etc), +* then it will be converted to frequency. +* - There must be a pair of celestial axes - either (RA,Dec) or +* (GLON,GLAT). RA and Dec must be either FK4/B1950 or FK5/J2000. +* - A limited range of projection codes (TAN, ARC, STG, AIT, SFL, SIN) +* can be used. For AIT and SFL, the reference point must be at the +* origin of longitude and latitude. For SIN, the associated projection +* parameters must both be zero. +* - No rotation of the celestial axes is allowed, unless the spatial +* axes are degenerate (i.e. cover only a single pixel). +* - The frequency axis in the created header will always describe +* frequency in the source rest frame. If the supplied FrameSet uses +* some other standard of rest then suitable conversion will be applied. +* - The source velocity must be defined. In other words, the SpecFrame +* attributes SourceVel and SourceVRF must have been assigned values. +* - The frequency axis in a FITS-CLASS header does not represent +* absolute frequency, but instead represents offsets from the rest +* frequency in the standard of rest of the source. +* +* When writing a FrameSet out using FITS-CLASS encoding, the current +* Frame may be temporarily modified if this will allow the header +* to be produced. If this is done, the associated pixel->WCS Mapping +* will also be modified to take account of the changes to the Frame. +* The modifications performed include re-ordering axes (WCS axes, not +* pixel axes), changing spectral coordinate system and standard of +* rest, changing the celestial coordinate system and reference equinox, +* and changing axis units. + +* The NATIVE Encoding: +* The NATIVE encoding may be used to store a description of any +* class of AST Object in the form of FITS header cards, and (for +* most practical purposes) any number of these Object descriptions +* may be stored within a single set of FITS cards. If multiple +* Object descriptions are stored, they are written and read +* sequentially. The NATIVE encoding makes use of unique FITS +* keywords which are designed not to clash with keywords that have +* already been used for other purposes (if a potential clash is +* detected, an alternative keyword is constructed to avoid the +* clash). +* +* When reading a NATIVE encoded object from a FitsChan (using +c astRead), FITS header cards are read, starting at the current +f AST_READ), FITS header cards are read, starting at the current +* card (as determined by the Card attribute), until the start of +* the next Object description is found. This description is then +* read and converted into an AST Object, for which a pointer is +* returned. Such a read is always destructive and causes all the +* FITS header cards involved in the Object description to be +* removed from the FitsChan, which is left positioned at the +* following card. +* +* The Object returned may be of any class, depending on the +* description that was read, and other AST routines may be used to +* validate it (for example, by examining its Class or ID attribute +c using astGetC). If further NATIVE encoded Object descriptions +f using AST_GETC). If further NATIVE encoded Object descriptions +c exist in the FitsChan, subsequent calls to astRead will return +f exist in the FitsChan, subsequent calls to AST_READ will return +* the Objects they describe in sequence (and destroy their +* descriptions) until no more remain between the current card and +* the "end-of-file". +* +c When astWrite is used to write an Object using NATIVE encoding, +f When AST_WRITE is used to write an Object using NATIVE encoding, +* a description of the Object is inserted immediately before the +* current card (as determined by the Card attribute). Multiple +* Object descriptions may be written in this way and are stored +* separately (and sequentially if the Card attribute is not +* modified between the writes). A write operation using the NATIVE +* encoding does not over-write previously written Object +* descriptions. Note, however, that subsequent behaviour is +* undefined if an Object description is written inside a +* previously-written description, so this should be avoided. +* +* When an Object is written to a FitsChan using NATIVE encoding, +c astWrite should (barring errors) always transfer data and +f AST_WRITE should (barring errors) always transfer data and +* return a value of 1. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,Encoding,encoding,UNKNOWN_ENCODING) +astMAKE_SET(FitsChan,Encoding,int,encoding,( + value == NATIVE_ENCODING || + value == FITSPC_ENCODING || + value == FITSWCS_ENCODING || + value == FITSIRAF_ENCODING || + value == FITSAIPS_ENCODING || + value == FITSAIPSPP_ENCODING || + value == FITSCLASS_ENCODING || + value == DSS_ENCODING ? value : + (astError( AST__BADAT, "astSetEncoding: Unknown encoding system %d " + "supplied.", status, value ), UNKNOWN_ENCODING ))) +astMAKE_TEST(FitsChan,Encoding,( this->encoding != UNKNOWN_ENCODING )) + +/* DefB1950 */ +/* ======== */ + +/* +*att++ +* Name: +* DefB1950 + +* Purpose: +* Use FK4 B1950 as defaults? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which specifies a default equinox +* and reference frame to use when reading a FrameSet from a FitsChan +* with a foreign (i.e. non-native) encoding. It is only used if the FITS +* header contains RA and DEC axes but contains no information about the +* reference frame or equinox. If this is the case, then values of FK4 and +* B1950 are assumed if the DefB1950 attribute has a non-zero value and +* ICRS is assumed if DefB1950 is zero. The default value for DefB1950 +* depends on the value of the Encoding attribute: for FITS-WCS encoding +* the default is zero, and for all other encodings it is one. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,DefB1950,defb1950,-1) +astMAKE_GET(FitsChan,DefB1950,int,1,(this->defb1950 == -1 ? (astGetEncoding(this)== FITSWCS_ENCODING?0:1): this->defb1950)) +astMAKE_SET(FitsChan,DefB1950,int,defb1950,( value ? 1 : 0 )) +astMAKE_TEST(FitsChan,DefB1950,( this->defb1950 != -1 )) + +/* TabOK */ +/* ===== */ + +/* +*att++ +* Name: +* TabOK + +* Purpose: +* Should the FITS-WCS -TAB algorithm be recognised? + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute is an integer value which indicates if the "-TAB" +* algorithm, defined in FITS-WCS paper III, should be supported by +* the FitsChan. The default value is zero. A zero or negative value +* results in no support for -TAB axes (i.e. axes that have "-TAB" +* in their CTYPE keyword value). In this case, the +c astWrite +f AST_WRITE +* method will return zero if the write operation would required the +* use of the -TAB algorithm, and the +c astRead +f AST_READ +* method will return +c a NULL pointer +f AST__NULL +* if any axis in the supplied header uses the -TAB algorithm. + +* If TabOK is set to a non-zero positive integer, these methods will +* recognise and convert axes described by the -TAB algorithm, as +* follows: +* +c The astWrite +f The AST_WRITE +* method will generate headers that use the -TAB algorithm (if +* possible) if no other known FITS-WCS algorithm can be used to +* describe the supplied FrameSet. This will result in a table of +* coordinate values and index vectors being stored in the FitsChan. +* After the write operation, the calling application should check to +* see if such a table has been stored in the FitsChan. If so, the +* table should be retrived from the FitsChan using the +c astGetTables +f AST_GETTABLES +* method, and the data (and headers) within it copied into a new +* FITS binary table extension. See +c astGetTables +f AST_GETTABLES +* for more information. The FitsChan uses a FitsTable object to store +* the table data and headers. This FitsTable will contain the required +* columns and headers as described by FITS-WCS paper III - the +* coordinates array will be in a column named "COORDS", and the index +* vector(s) will be in columns named "INDEX" (where is the index +* of the corresponding FITS WCS axis). Note, index vectors are only +* created if required. The EXTNAME value will be set to the value of the +* AST__TABEXTNAME constant (currently "WCS-TAB"). The EXTVER header +* will be set to the positive integer value assigned to the TabOK +* attribute. No value will be stored for the EXTLEVEL header, and should +* therefore be considered to default to 1. +* +c The astRead +f The AST_READ +* method will generate a FrameSet from headers that use the -TAB +* algorithm so long as the necessary FITS binary tables are made +* available. There are two ways to do this: firstly, if the application +* knows which FITS binary tables will be needed, then it can create a +* Fitstable describing each such table and store it in the FitsChan +* (using method +c astPutTables or astPutTable) before invoking the astRead method. +f AST_PUTTABLES or AST_PUTTABLE) before invoking the AST_READ method. +* Secondly, if the application does not know which FITS binary tables +* will be needed by +c astRead, +f AST_READ, +* then it can register a call-back function with the FitsChan using +* method +c astTableSource. +f AST_TABLESOURCE. +* This call-back function will be called from within +c astRead +f AST_READ +* if and when a -TAB header is encountered. When called, its arguments +* will give the name, version and level of the FITS extension containing +* a required table. The call-back function should read this table from +* an external FITS file, and create a corresponding FitsTable which +* it should then return to +c astRead. Note, currently astRead +f AST_READ. Note, currently AST_READ +* can only handle -TAB headers that describe 1-dimensional (i.e. +* separable) axes. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,TabOK,tabok,-INT_MAX) +astMAKE_GET(FitsChan,TabOK,int,0,(this->tabok == -INT_MAX ? 0 : this->tabok)) +astMAKE_SET(FitsChan,TabOK,int,tabok,value) +astMAKE_TEST(FitsChan,TabOK,( this->tabok != -INT_MAX )) + +/* CarLin */ +/* ====== */ + +/* +*att++ +* Name: +* CarLin + +* Purpose: +* Ignore spherical rotations on CAR projections? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which specifies how FITS "CAR" +* (plate carree, or "Cartesian") projections should be treated when +* reading a FrameSet from a foreign encoded FITS header. If zero (the +* default), it is assumed that the CAR projection conforms to the +* conventions described in the FITS world coordinate system (FITS-WCS) +* paper II "Representation of Celestial Coordinates in FITS" by +* M. Calabretta & E.W. Greisen. If CarLin is non-zero, then these +* conventions are ignored, and it is assumed that the mapping from pixel +* coordinates to celestial coordinates is a simple linear transformation +* (hence the attribute name "CarLin"). This is appropriate for some older +* FITS data which claims to have a "CAR" projection, but which in fact do +* not conform to the conventions of the FITS-WCS paper. +* +* The FITS-WCS paper specifies that headers which include a CAR projection +* represent a linear mapping from pixel coordinates to "native spherical +* coordinates", NOT celestial coordinates. An extra mapping is then +* required from native spherical to celestial. This mapping is a 3D +* rotation and so the overall Mapping from pixel to celestial coordinates +* is NOT linear. See the FITS-WCS papers for further details. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,CarLin,carlin,-1) +astMAKE_GET(FitsChan,CarLin,int,1,(this->carlin == -1 ? 0 : this->carlin)) +astMAKE_SET(FitsChan,CarLin,int,carlin,( value ? 1 : 0 )) +astMAKE_TEST(FitsChan,CarLin,( this->carlin != -1 )) + +/* PolyTan */ +/* ======= */ + +/* +*att++ +* Name: +* PolyTan + +* Purpose: +* Use PVi_m keywords to define distorted TAN projection? + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute is a boolean value which specifies how FITS "TAN" +* projections should be treated when reading a FrameSet from a foreign +* encoded FITS header. If zero, the projection is assumed to conform +* to the published FITS-WCS standard. If positive, the convention +* for a distorted TAN projection included in an early draft version +* of FITS-WCS paper II are assumed. In this convention the +* coefficients of a polynomial distortion to be applied to +* intermediate world coordinates are specified by the PVi_m keywords. +* This convention was removed from the paper before publication and so +* does not form part of the standard. Indeed, it is incompatible with +* the published standard because it re-defines the meaning of the +* first five PVi_m keywords on the longitude axis, which are reserved +* by the published standard for other purposes. However, headers that +* use this convention are still to be found, for instance the SCAMP +* utility (http://www.astromatic.net/software/scamp) creates them. +* +* The default value for the PolyTan attribute is -1. A negative +* values causes the used convention to depend on the contents +* of the FitsChan. If the FitsChan contains any PVi_m keywords for +* the latitude axis, or if it contains PVi_m keywords for the +* longitude axis with "m" greater than 4, then the distorted TAN +* convention is used. Otherwise, the standard convention is used. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,PolyTan,polytan,-INT_MAX) +astMAKE_SET(FitsChan,PolyTan,int,polytan,value) +astMAKE_TEST(FitsChan,PolyTan,( this->polytan != -INT_MAX )) +astMAKE_GET(FitsChan,PolyTan,int,-1,(this->polytan == -INT_MAX ? -1 : this->polytan)) + +/* Iwc */ +/* === */ + +/* +*att++ +* Name: +* Iwc + +* Purpose: +* Include a Frame representing FITS-WCS intermediate world coordinates? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which is used when a FrameSet is +* read from a FitsChan with a foreign FITS encoding (e.g. FITS-WCS) using +c astRead. +f AST_READ. +* If it has a non-zero value then the returned FrameSet will include +* Frames representing "intermediate world coordinates" (IWC). These +* Frames will have Domain name "IWC" for primary axis descriptions, and +* "IWCa" for secondary axis descriptions, where "a" is replaced by +* the single alternate axis description character, as used in the +* FITS-WCS header. The default value for "Iwc" is zero. +* +* FITS-WCS paper 1 defines IWC as a Cartesian coordinate system with one +* axis for each WCS axis, and is the coordinate system produced by the +* rotation matrix (represented by FITS keyword PCi_j, CDi_j, etc). +* For instance, for a 2-D FITS-WCS header describing projected +* celestial longitude and latitude, the intermediate world +* coordinates represent offsets in degrees from the reference point +* within the plane of projection. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,Iwc,iwc,-1) +astMAKE_GET(FitsChan,Iwc,int,1,(this->iwc == -1 ? 0 : this->iwc)) +astMAKE_SET(FitsChan,Iwc,int,iwc,( value ? 1 : 0 )) +astMAKE_TEST(FitsChan,Iwc,( this->iwc != -1 )) + +/* +*att++ +* Name: +* CDMatrix + +* Purpose: +* Use CDi_j keywords to represent pixel scaling, rotation, etc? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which specifies how the linear +* transformation from pixel coordinates to intermediate world +* coordinates should be represented within a FitsChan when using +* FITS-WCS encoding. This transformation describes the scaling, +* rotation, shear, etc., of the pixel axes. +* +* If the attribute has a non-zero value then the transformation is +* represented by a set of CDi_j keywords representing a square matrix +* (where "i" is the index of an intermediate world coordinate axis +* and "j" is the index of a pixel axis). If the attribute has a zero +* value the transformation is represented by a set of PCi_j keywords +* (which also represent a square matrix) together with a corresponding +* set of CDELTi keywords representing the axis scalings. See FITS-WCS +* paper II "Representation of Celestial Coordinates in FITS" by +* M. Calabretta & E.W. Greisen, for a complete description of these two +* schemes. +* +* The default value of the CDMatrix attribute is determined by the +* contents of the FitsChan at the time the attribute is accessed. If +* the FitsChan contains any CDi_j keywords then the default value is +* non-zero. Otherwise it is zero. Note, reading a FrameSet from a +* FitsChan will in general consume any CDi_j keywords present in the +* FitsChan. Thus the default value for CDMatrix following a read will +* usually be zero, even if the FitsChan originally contained some +* CDi_j keywords. This behaviour is similar to that of the Encoding +* attribute, the default value for which is determined by the contents +* of the FitsChan at the time the attribute is accessed. If you wish +* to retain the original value of the CDMatrix attribute (that is, +* the value before reading the FrameSet) then you should enquire the +* default value before doing the read, and then set that value +* explicitly. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,CDMatrix,cdmatrix,-1) +astMAKE_SET(FitsChan,CDMatrix,int,cdmatrix,( value ? 1 : 0 )) +astMAKE_TEST(FitsChan,CDMatrix,( this->cdmatrix != -1 )) + +/* Clean */ +/* ===== */ + +/* +*att++ +* Name: +* Clean + +* Purpose: +* Remove cards used whilst reading even if an error occurs? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute indicates whether or not cards should be removed from +* the FitsChan if an error occurs within +c astRead. +f AST_READ. +* A succesful read on a FitsChan always results in the removal of +* the cards which were involved in the description of the returned +* Object. However, in the event of an error during the read (for instance +* if the cards in the FitsChan have illegal values, or if some required +* cards are missing) no cards will be removed from the FitsChan if +* the Clean attribute is zero (the default). If Clean is non-zero then +* any cards which were used in the aborted attempt to read an object +* will be removed. +* +* This provides a means of "cleaning" a FitsChan of WCS related cards +* which works even in the event of the cards not forming a legal WCS +* description. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,Clean,clean,-1) +astMAKE_SET(FitsChan,Clean,int,clean,( value ? 1 : 0 )) +astMAKE_TEST(FitsChan,Clean,( this->clean != -1 )) + +/* +*att++ +* Name: +* FitsAxisOrder + +* Purpose: +* Frame title. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the order for the WCS axes in any new +* FITS-WCS headers created using the +c astWrite +f AST_WRITE +* method. +* +* The value of the FitsAxisOrder attribute can be either "" +* (the default value), "" or a space-separated list of axis +* symbols: +* +* "": causes the WCS axis order to be chosen automatically so that +* the i'th WCS axis in the new FITS header is the WCS axis which is +* more nearly parallel to the i'th pixel axis. +* +* "": causes the WCS axis order to be set so that the i'th WCS +* axis in the new FITS header is the i'th WCS axis in the current +* Frame of the FrameSet being written out to the header. +* +* "Sym1 Sym2...": the space-separated list is seached in turn for +* the Symbol attribute of each axis in the current Frame of the +* FrameSet. The order in which these Symbols occur within the +* space-separated list defines the order of the WCS axes in the +* new FITS header. An error is reported if Symbol for a current +* Frame axis is not present in the supplied list. However, no error +* is reported if the list contains extra words that do not correspond +* to the Symbol of any current Frame axis. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,FitsAxisOrder,fitsaxisorder,astFree( this->fitsaxisorder )) +astMAKE_GET(FitsChan,FitsAxisOrder,const char *,NULL,(this->fitsaxisorder ? this->fitsaxisorder : "" )) +astMAKE_SET(FitsChan,FitsAxisOrder,const char *,fitsaxisorder,astStore( this->fitsaxisorder, value, strlen( value ) + (size_t) 1 )) +astMAKE_TEST(FitsChan,FitsAxisOrder,( this->fitsaxisorder != NULL )) + +/* FitsDigits. */ +/* =========== */ + +/* +*att++ +* Name: +* FitsDigits + +* Purpose: +* Digits of precision for floating point FITS values. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute gives the number of significant decimal digits to +* use when formatting floating point values for inclusion in the +* FITS header cards within a FitsChan. +* +* By default, a positive value is used which results in no loss of +c information, assuming that the value's precision is double. +f information, assuming that the value is double precision. +* Usually, this causes no problems. +* +* However, to adhere strictly to the recommendations of the FITS +* standard, the width of the formatted value (including sign, +* decimal point and exponent) ought not to be more than 20 +* characters. If you are concerned about this, you should set +* FitsDigits to a negative value, such as -15. In this case, the +* absolute value (+15) indicates the maximum number of significant +* digits to use, but the actual number used may be fewer than this +* to ensure that the FITS recommendations are satisfied. When +* using this approach, the resulting number of significant digits +* may depend on the value being formatted and on the presence of +* any sign, decimal point or exponent. +* +* The value of this attribute is effective when FITS header cards +* are output, either using +c astFindFits or by the action of the FitsChan's sink function +f AST_FINDFITS or by the action of the FitsChan's sink routine +* when it is finally deleted. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(FitsChan,FitsDigits,fitsdigits,DBL_DIG) +astMAKE_GET(FitsChan,FitsDigits,int,DBL_DIG,this->fitsdigits) +astMAKE_SET(FitsChan,FitsDigits,int,fitsdigits,value) +astMAKE_TEST(FitsChan,FitsDigits,( this->fitsdigits != DBL_DIG )) + +/* CardComm */ +/* ======== */ + +/* +*att++ +* Name: +* CardComm + +* Purpose: +* The comment for the current card in a FitsChan. + +* Type: +* Public attribute. + +* Synopsis: +* String, read-only. + +* Description: +* This attribute gives the comment for the current card of the +* FitsChan. A zero-length string is returned if the card has no comment. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* CardName */ +/* ======== */ + +/* +*att++ +* Name: +* CardName + +* Purpose: +* The keyword name of the current card in a FitsChan. + +* Type: +* Public attribute. + +* Synopsis: +* String, read-only. + +* Description: +* This attribute gives the name of the keyword for the +* current card of the FitsChan. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* CardType */ +/* ======== */ + +/* +*att++ +* Name: +* CardType + +* Purpose: +* The data type of the current card in a FitsChan. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the data type of the keyword value for the +* current card of the FitsChan. It will be one of the following +* integer constants: AST__NOTYPE, AST__COMMENT, AST__INT, AST__FLOAT, +* AST__STRING, AST__COMPLEXF, AST__COMPLEXI, AST__LOGICAL, +* AST__CONTINUE, AST__UNDEF. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* Ncard */ +/* ===== */ + +/* +*att++ +* Name: +* Ncard + +* Purpose: +* Number of FITS header cards in a FitsChan. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the total number of FITS header cards +* stored in a FitsChan. It is updated as cards are added or +* deleted. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* Nkey */ +/* ==== */ + +/* +*att++ +* Name: +* Nkey + +* Purpose: +* Number of unique FITS keywords in a FitsChan. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the total number of unique FITS keywords +* stored in a FitsChan. It is updated as cards are added or +* deleted. If no keyword occurrs more than once in the FitsChan, the +* Ncard and Nkey attributes will be equal. If any keyword occurrs +* more than once, the Nkey attribute value will be smaller than +* the Ncard attribute value. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* Warnings. */ +/* ======== */ + +/* +*att++ +* Name: +* Warnings + +* Purpose: +* Controls the issuing of warnings about various conditions. + +* Type: +* Public attribute. + +* Synopsis: +* String + +* Description: +* This attribute controls the issuing of warnings about selected +* conditions when an Object or keyword is read from or written to a +* FitsChan. The value supplied for the Warnings attribute should +* consist of a space separated list of condition names (see the +* AllWarnings attribute for a list of the currently defined names). +* Each name indicates a condition which should be reported. The default +* value for Warnings is the string "BadKeyName BadKeyValue Tnx Zpx +* BadCel BadMat BadPV BadCTYPE". +* +* The text of any warning will be stored within the FitsChan in the +* form of one or more new header cards with keyword ASTWARN. If +* required, applications can check the FitsChan for ASTWARN cards +c (using astFindFits) after the call to astRead or astWrite has been +f (using AST_FINDFITS) after the call to AST_READ or AST_WRITE has been +* performed, and report the text of any such cards to the user. ASTWARN +* cards will be propagated to any output header unless they are +c deleted from the FitsChan using astDelFits. +f deleted from the FitsChan using astDelFits. + +* Notes: +* This attribute only controls the warnings that are to be stored as +* a set of header cards in the FitsChan as described above. It has no +* effect on the storage of warnings in the parent Channel structure. +* All warnings are stored in the parent Channel structure, from where +* they can be retrieved using the +c astWarnings +f AST_WARNINGS +* function. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* Clear the Warnings value by freeing the allocated memory and assigning + a NULL pointer. */ +astMAKE_CLEAR(FitsChan,Warnings,warnings,astFree( this->warnings )) + +/* If the Warnings value is not set, supply a default in the form of a + pointer to the constant string "BadKeyName BadKeyValue Tnx Zpx BadCel BadMat BadCTYPE". */ +astMAKE_GET(FitsChan,Warnings,const char *,NULL,( this->warnings ? this->warnings : + "BadKeyName BadKeyValue Tnx Zpx BadPV BadCel BadMat BadCTYPE" )) + +/* Set a Warnings value by freeing any previously allocated memory, allocating + new memory, storing the string and saving the pointer to the copy. + First check that the list does not contain any unknown conditions. If + it does, an error is reported by GoodWarns and the current attribute value + is retained. */ +astMAKE_SET(FitsChan,Warnings,const char *,warnings,( GoodWarns( value, status ) ? + astStore( this->warnings, value, strlen( value ) + (size_t) 1 ) : + this->warnings)) + +/* The Warnings value is set if the pointer to it is not NULL. */ +astMAKE_TEST(FitsChan,Warnings,( this->warnings != NULL )) + +/* AllWarnings. */ +/* ============ */ + +/* +*att++ +* Name: +* AllWarnings + +* Purpose: +* A list of all currently available condition names. + +* Type: +* Public attribute. + +* Synopsis: +* String, read-only + +* Description: +* This read-only attribute is a space separated list of all the conditions +* names recognized by the Warnings attribute. The names are listed +* below. + +* Conditions: +* The following conditions are currently recognised (all are +* case-insensitive): +* +* - "BadCel": This condition arises when reading a FrameSet from a +* non-Native encoded FitsChan if an unknown celestial co-ordinate +* system is specified by the CTYPE keywords. +* +* - "BadCTYPE": This condition arises when reading a FrameSet from a +* non-Native encoded FitsChan if an illegal algorithm code is specified +* by a CTYPE keyword, and the illegal code can be converted to an +* equivalent legal code. +* +* - "BadKeyName": This condition arises if a FITS keyword name is +* encountered that contains an illegal character (i.e. one not allowed +* by the FITS standard). +* +* - "BadKeyValue": This condition arises if the value of a FITS keyword +* cannot be determined from the content of the header card. +* +* - "BadLat": This condition arises when reading a FrameSet from a +* non-Native encoded FitsChan if the latitude of the reference point +* has an absolute value greater than 90 degrees. The actual absolute +* value used is set to exactly 90 degrees in these cases. +* +* - "BadMat": This condition arises if the matrix describing the +* transformation from pixel offsets to intermediate world coordinates +* cannot be inverted. This matrix describes the scaling, rotation, shear, +* etc., applied to the pixel axes, and is specified by keywords such as +* PCi_j, CDi_j, CROTA, etc. For example, the matrix will not be invertable +* if any rows or columns consist entirely of zeros. The FITS-WCS Paper I +* "Representation of World Coordinates in FITS" by Greisen & Calabretta +* requires that this matrix be invertable. Many operations (such as +* grid plotting) will not be possible if the matrix cannot be inverted. +* +* - "BadPV": This condition arises when reading a FrameSet from a +* non-Native encoded FitsChan. It is issued if a PVi_m header is found +* that refers to a projection parameter that is not used by the +* projection type specified by CTYPE, or the PV values are otherwise +* inappropriate for the projection type. +* +* - "BadVal": This condition arises when reading a FrameSet from a +* non-Native encoded FitsChan if it is not possible to convert the +* value of a FITS keywords to the expected type. For instance, this +* can occur if the FITS header contains a string value for a keyword +* which should have a floating point value, or if the keyword has no +* value at all (i.e. is a comment card). +* +* - "Distortion": This condition arises when reading a FrameSet from a +* non-Native encoded FitsChan if any of the CTYPE keywords specify an +* unsupported distortion code using the "4-3-3" format specified in +* FITS-WCS paper IV. Such distortion codes are ignored. +* +* - "NoCTYPE": This condition arises if a default CTYPE value is used +c within astRead, due to no value being present in the supplied FitsChan. +f within AST_READ, due to no value being present in the supplied FitsChan. +* This condition is only tested for when using non-Native encodings. +* +* - "NoEquinox": This condition arises if a default equinox value is used +c within astRead, due to no value being present in the supplied FitsChan. +f within AST_READ, due to no value being present in the supplied FitsChan. +* This condition is only tested for when using non-Native encodings. +* +* - "NoRadesys": This condition arises if a default reference frame is +c used for an equatorial co-ordinate system within astRead, due to no +f used for an equatorial co-ordinate system within AST_READ, due to no +* value being present in the supplied FitsChan. This condition is only +* tested for when using non-Native encodings. +* +* - "NoLonpole": This condition arises if a default value is used for +c the LONPOLE keyword within astRead, due to no value being present +f the LONPOLE keyword within AST_READ, due to no value being present +* in the supplied FitsChan. This condition is only tested for when +* using non-Native encodings. +* +* - "NoLatpole": This condition arises if a default value is used for +c the LATPOLE keyword within astRead, due to no value being present +f the LATPOLE keyword within AST_READ, due to no value being present +* in the supplied FitsChan. This condition is only tested for when +* using non-Native encodings. +* +* - "NoMjd-obs": This condition arises if a default value is used for +c the date of observation within astRead, due to no value being present +f the date of observation within AST_READ, due to no value being present +* in the supplied FitsChan. This condition is only tested for when using +* non-Native encodings. +* +* - "Tnx": This condition arises if a FrameSet is read from a FITS +* header containing an IRAF "TNX" projection which includes terms +* not supproted by AST. Such terms are ignored and so the resulting +* FrameSet may be inaccurate. +* +* - "Zpx": This condition arises if a FrameSet is read from a FITS +* header containing an IRAF "ZPX" projection which includes "lngcor" +* or "latcor" correction terms. These terms are not supported by AST +* and are ignored. The resulting FrameSet may therefore be inaccurate. + +* Applicability: +* FitsChan +* All FitsChans have this attribute. +*att-- +*/ + +/* Copy constructor. */ +/* ----------------- */ + +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for FitsChan objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for FitsChan objects. + +* Parameters: +* objin +* Pointer to the FitsChan to be copied. +* objout +* Pointer to the FitsChan being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The source and sink functions are not propagated (i.e. the +* pointers are set NULL in the output FitsChan). +* - This constructor makes a deep copy, including a copy of the +* keyword values. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *class; /* Pointer to object class */ + AstFitsChan *in; /* Pointer to input FitsChan */ + AstFitsChan *out; /* Pointer to output FitsChan */ + int *flags; + int icard; + int old_ignore_used; /* Original value of external variable ignore_used */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(objin); + +/* Obtain pointers to the input and output FitsChans. */ + in = (AstFitsChan *) objin; + out = (AstFitsChan *) objout; + +/* Nullify all pointers in the output FitsChan so that the input + data will not be deleted in the event of an error occurring. */ + out->card = NULL; + out->head = NULL; + out->keyseq = NULL; + out->keywords = NULL; + out->source = NULL; + out->saved_source = NULL; + out->source_wrap = NULL; + out->sink = NULL; + out->sink_wrap = NULL; + out->warnings = NULL; + out->tabsource = NULL; + out->tabsource_wrap = NULL; + +/* Store the object class. */ + class = astGetClass( in ); + +/* Ensure all cards are copied, including those already read by astRead. */ + old_ignore_used = ignore_used; + ignore_used = 0; + +/* Save the current card index in the input FitsChan. */ + icard = astGetCard( in ); + +/* Rewind the input FitsChan. */ + astClearCard( in ); + +/* Copy all the FitsCard structures from input to output. */ + while( !astFitsEof( in ) && astOK ){ + +/* Get a pointer to the flags mask for this card. */ + flags = CardFlags( in, status ); + +/* Store a new card in the output, holding the same information as the + input card. */ + NewCard( out, CardName( in, status ), CardType( in, status ), CardData( in, NULL, status ), + CardComm( in, status ), (flags?(*flags):0), status ); + +/* Move on to the next input card. */ + MoveCard( in, 1, "astCopy", class, status ); + } + +/* Set the current card in both input and output to the current input + card on entry. */ + astSetCard( in, icard ); + astSetCard( out, icard ); + +/* Copy the list of keyword sequence numbers used. */ + if( in->keyseq ) out->keyseq = astCopy( in->keyseq ); + +/* Copy the Warnings attribute value */ + if( in->warnings ) out->warnings = astStore( NULL, in->warnings, + strlen( in->warnings ) + 1 ); + +/* Copy any tables currently in the FitsChan structure. */ + if( in->tables ) out->tables = astCopy( in->tables ); + +/* Reinstate the original setting of the external ignore_used variable. */ + ignore_used = old_ignore_used; + +/* If an error occurred, delete the contents of the output Object. */ + if( !astOK ) Delete( objout, status ); +} + +/* Destructor. */ +/* ----------- */ + +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for FitsChan objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for FitsChan objects. + +* Parameters: +* obj +* Pointer to the FitsChan to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to FitsChan */ + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) obj; + +/* Write out the contents of the FitsChan using the sink function + provided when it was created. */ + WriteToSink( this, status ); + +/* Remove all cards from the FitsChan. */ + EmptyFits( this, status ); +} + +/* Dump function. */ +/* -------------- */ + +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for FitsChan objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the FitsChan class to an output Channel. + +* Parameters: +* this +* Pointer to the FitsChan whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstFitsChan *this; /* Pointer to the FitsChan structure */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *class; /* Object class */ + const char *sval; /* Pointer to string value */ + int cardtype; /* Keyword data type */ + int flags; /* Keyword flags */ + int icard; /* Index of current card */ + int ival; /* Integer value */ + int ncard; /* No. of cards dumped so far */ + int old_ignore_used; /* Original value of external variable ignore_used */ + int set; /* Attribute value set? */ + void *data; /* Pointer to keyword data value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the FitsChan structure. */ + this = (AstFitsChan *) this_object; + +/* Store the object class. */ + class = astGetClass( this ); + +/* Save the index of ht ecurrent card. */ + icard = astGetCard( this ); + +/* Write out values representing the instance variables for the + FitsChan class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* Card. */ +/* ----- */ + astWriteInt( channel, "Card", 1, 1, icard, "Index of current card" ); + +/* Encoding. */ +/* --------- */ + set = TestEncoding( this, status ); + ival = set ? GetEncoding( this, status ) : astGetEncoding( this ); + if( ival > UNKNOWN_ENCODING && ival <= MAX_ENCODING ) { + astWriteString( channel, "Encod", set, 1, xencod[ival], "Encoding system" ); + } else { + astWriteString( channel, "Encod", set, 1, UNKNOWN_STRING, "Encoding system" ); + } + +/* FitsAxisOrder. */ +/* -------------- */ + set = TestFitsAxisOrder( this, status ); + sval = set ? GetFitsAxisOrder( this, status ) : astGetFitsAxisOrder( this ); + astWriteString( channel, "FAxOrd", set, 1, sval, + "Order of WCS axes in new FITS headers" ); + +/* FitsDigits. */ +/* ----------- */ + set = TestFitsDigits( this, status ); + ival = set ? GetFitsDigits( this, status ) : astGetFitsDigits( this ); + astWriteInt( channel, "FitsDg", set, 1, ival, "No. of digits for floating point values" ); + +/* DefB1950 */ +/* -------- */ + set = TestDefB1950( this, status ); + ival = set ? GetDefB1950( this, status ) : astGetDefB1950( this ); + astWriteInt( channel, "DfB1950", set, 1, ival, (ival ? "Default to FK4 B1950": "Default to ICRS") ); + +/* TabOK */ +/* ----- */ + set = TestTabOK( this, status ); + ival = set ? GetTabOK( this, status ) : astGetTabOK( this ); + astWriteInt( channel, "TabOK", set, 1, ival, ( ival > 0 ? "EXTVER value for -TAB headers": "Do not support -TAB CTYPE codes") ); + +/* CDMatrix */ +/* -------- */ + set = TestCDMatrix( this, status ); + ival = set ? GetCDMatrix( this, status ) : astGetCDMatrix( this ); + astWriteInt( channel, "CdMat", set, 1, ival, (ival ? "Use CD Matrix":"Use PC matrix") ); + +/* CarLin */ +/* ------ */ + set = TestCarLin( this, status ); + ival = set ? GetCarLin( this, status ) : astGetCarLin( this ); + astWriteInt( channel, "CarLin", set, 1, ival, (ival ? "Use simple linear CAR projections": "Use full FITS-WCS CAR projections") ); + +/* PolyTan */ +/* ------- */ + set = TestPolyTan( this, status ); + ival = set ? GetPolyTan( this, status ) : astGetPolyTan( this ); + astWriteInt( channel, "PolyTan", set, 0, ival, (ival ? "Use distorted TAN convention": "Use standard TAN convention") ); + +/* Iwc */ +/* --- */ + set = TestIwc( this, status ); + ival = set ? GetIwc( this, status ) : astGetIwc( this ); + astWriteInt( channel, "Iwc", set, 1, ival, (ival ? "Include an IWC Frame": "Do not include an IWC Frame") ); + +/* Clean */ +/* ----- */ + set = TestClean( this, status ); + ival = set ? GetClean( this, status ) : astGetClean( this ); + astWriteInt( channel, "Clean", set, 0, ival, "Always remove used cards?" ); + +/* Warnings. */ +/* --------- */ + set = TestWarnings( this, status ); + sval = set ? GetWarnings( this, status ) : astGetWarnings( this ); + astWriteString( channel, "Warn", set, 1, sval, "Warnings to be reported" ); + +/* Now do instance variables which are not attributes. */ +/* =================================================== */ + +/* Ensure all cards are copied, including those already read by astRead. */ + old_ignore_used = ignore_used; + ignore_used = 0; + +/* Rewind the FitsChan. */ + astClearCard( this ); + +/* Dump each card. */ + ncard = 1; + while( !astFitsEof( this ) && astOK ){ + +/* Write out the keyword name. */ + if( CardName( this, status ) ){ + (void) sprintf( buff, "Nm%d", ncard ); + astWriteString( channel, buff, 1, 1, CardName( this, status ), + "FITS keyword name" ); + } + +/* Write out the keyword type. */ + cardtype = CardType( this, status ); + (void) sprintf( buff, "Ty%d", ncard ); + astWriteString( channel, buff, 1, 1, type_names[ cardtype ], + "FITS keyword data type" ); + +/* Write out the flag values if any are non-zero. */ + flags = *CardFlags( this, status ); + if( flags ){ + (void) sprintf( buff, "Fl%d", ncard ); + astWriteInt( channel, buff, 1, 1, flags, "FITS keyword flags" ); + } + +/* Write out the data value, if defined, using the appropriate data type. */ + data = CardData( this, NULL, status ); + if( data && cardtype != AST__UNDEF ){ + if( cardtype == AST__FLOAT ){ + (void) sprintf( buff, "Dt%d", ncard ); + astWriteDouble( channel, buff, 1, 1, *( (double *) data ), + "FITS keyword value" ); + } else if( cardtype == AST__STRING || cardtype == AST__CONTINUE ){ + (void) sprintf( buff, "Dt%d", ncard ); + astWriteString( channel, buff, 1, 1, (char *) data, + "FITS keyword value" ); + } else if( cardtype == AST__INT ){ + (void) sprintf( buff, "Dt%d", ncard ); + astWriteInt( channel, buff, 1, 1, *( (int *) data ), + "FITS keyword value" ); + } else if( cardtype == AST__LOGICAL ){ + (void) sprintf( buff, "Dt%d", ncard ); + astWriteInt( channel, buff, 1, 1, *( (int *) data ), + "FITS keyword value" ); + } else if( cardtype == AST__COMPLEXF ){ + (void) sprintf( buff, "Dr%d", ncard ); + astWriteDouble( channel, buff, 1, 1, *( (double *) data ), + "FITS keyword real value" ); + (void) sprintf( buff, "Di%d", ncard ); + astWriteDouble( channel, buff, 1, 1, *( ( (double *) data ) + 1 ), + "FITS keyword imaginary value" ); + } else if( cardtype == AST__COMPLEXI ){ + (void) sprintf( buff, "Dr%d", ncard ); + astWriteInt( channel, buff, 1, 1, *( (int *) data ), + "FITS keyword real value" ); + (void) sprintf( buff, "Di%d", ncard ); + astWriteInt( channel, buff, 1, 1, *( ( (int *) data ) + 1 ), + "FITS keyword imaginary value" ); + } + } + +/* Write out the keyword comment. */ + if( CardComm( this, status ) ){ + (void) sprintf( buff, "Cm%d", ncard ); + astWriteString( channel, buff, 1, 1, CardComm( this, status ), + "FITS keyword comment" ); + } + +/* Move on to the next card. */ + ncard++; + MoveCard( this, 1, "astDump", class, status ); + } + +/* Dump any FitTables. */ + if( this->tables ) { + astWriteObject( channel, "Tables", 1, 1, this->tables, + "A KeyMap holding associated binary tables" ); + } + +/* Reinstate the original setting of the external ignore_used variable. */ + ignore_used = old_ignore_used; + +/* Reinstate the original current card. */ + astSetCard( this, icard ); +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ + +/* Implement the astIsAFitsChan and astCheckFitsChan functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(FitsChan,Channel) +astMAKE_CHECK(FitsChan) +AstFitsChan *astFitsChan_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, int *status, ...) { + +/* +*++ +* Name: +c astFitsChan +f AST_FITSCHAN + +* Purpose: +* Create a FitsChan. + +* Type: +* Public function. + +* Synopsis: +c #include "fitschan.h" +c AstFitsChan *astFitsChan( const char *(* source)( void ), +c void (* sink)( const char * ), +c const char *options, ... ) +f RESULT = AST_FITSCHAN( SOURCE, SINK, OPTIONS, STATUS ) + +* Class Membership: +* FitsChan constructor. + +* Description: +* This function creates a new FitsChan and optionally initialises +* its attributes. +* +* A FitsChan is a specialised form of Channel which supports I/O +* operations involving the use of FITS (Flexible Image Transport +* System) header cards. Writing an Object to a FitsChan (using +c astWrite) will, if the Object is suitable, generate a +f AST_WRITE) will, if the Object is suitable, generate a +* description of that Object composed of FITS header cards, and +* reading from a FitsChan will create a new Object from its FITS +* header card description. +* +* While a FitsChan is active, it represents a buffer which may +* contain zero or more 80-character "header cards" conforming to +* FITS conventions. Any sequence of FITS-conforming header cards +* may be stored, apart from the "END" card whose existence is +* merely implied. The cards may be accessed in any order by using +* the FitsChan's integer Card attribute, which identifies a "current" +* card, to which subsequent operations apply. Searches +c based on keyword may be performed (using astFindFits), new +c cards may be inserted (astPutFits, astPutCards, astSetFits) and +c existing ones may be deleted (astDelFits) or changed (astSetFits). +f based on keyword may be performed (using AST_FINDFITS), new +f cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS) and +f existing ones may be deleted (AST_DELFITS) or changed (AST_SETFITS). +* +* When you create a FitsChan, you have the option of specifying +* "source" and "sink" functions which connect it to external data +* stores by reading and writing FITS header cards. If you provide +* a source function, it is used to fill the FitsChan with header cards +* when it is accessed for the first time. If you do not provide a +* source function, the FitsChan remains empty until you explicitly enter +c data into it (e.g. using astPutFits, astPutCards, astWrite +f data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE +* or by using the SourceFile attribute to specifying a text file from +* which headers should be read). When the FitsChan is deleted, any +* remaining header cards in the FitsChan can be saved in either of +* two ways: 1) by specifying a value for the SinkFile attribute (the +* name of a text file to which header cards should be written), or 2) +* by providing a sink function (used to to deliver header cards to an +* external data store). If you do not provide a sink function or a +* value for SinkFile, any header cards remaining when the FitsChan +* is deleted will be lost, so you should arrange to extract them +* first if necessary +c (e.g. using astFindFits or astRead). +f (e.g. using AST_FINDFITS or AST_READ). +* +* Coordinate system information may be described using FITS header +* cards using several different conventions, termed +* "encodings". When an AST Object is written to (or read from) a +* FitsChan, the value of the FitsChan's Encoding attribute +* determines how the Object is converted to (or from) a +* description involving FITS header cards. In general, different +* encodings will result in different sets of header cards to +* describe the same Object. Examples of encodings include the DSS +* encoding (based on conventions used by the STScI Digitised Sky +* Survey data), the FITS-WCS encoding (based on a proposed FITS +* standard) and the NATIVE encoding (a near loss-less way of +* storing AST Objects in FITS headers). +* +* The available encodings differ in the range of Objects they can +* represent, in the number of Object descriptions that can coexist +* in the same FitsChan, and in their accessibility to other +* (external) astronomy applications (see the Encoding attribute +* for details). Encodings are not necessarily mutually exclusive +* and it may sometimes be possible to describe the same Object in +* several ways within a particular set of FITS header cards by +* using several different encodings. +* +c The detailed behaviour of astRead and astWrite, when used with +f The detailed behaviour of AST_READ and AST_WRITE, when used with +* a FitsChan, depends on the encoding in use. In general, however, +c all use of astRead is destructive, so that FITS header cards +f all use of AST_READ is destructive, so that FITS header cards +* are consumed in the process of reading an Object, and are +* removed from the FitsChan (this deletion can be prevented for +* specific cards by calling the +c astRetainFits function). +f AST_RETAINFITS routine). +* +* If the encoding in use allows only a single Object description +* to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF +c encodings), then write operations using astWrite will +f encodings), then write operations using AST_WRITE will +* over-write any existing Object description using that +* encoding. Otherwise (e.g. the NATIVE encoding), multiple Object +* descriptions are written sequentially and may later be read +* back in the same sequence. + +* Parameters: +c source +f SOURCE = FUNCTION (Given) +c Pointer to a source function which takes no arguments and +c returns a pointer to a null-terminated string. This function +c will be used by the FitsChan to obtain input FITS header +c cards. On each invocation, it should read the next input card +c from some external source (such as a FITS file), and return a +c pointer to the (null-terminated) contents of the card. It +c should return a NULL pointer when there are no more cards to +c be read. +c +c If "source" is NULL, the FitsChan will remain empty until +c cards are explicitly stored in it (e.g. using astPutCards, +c astPutFits or via the SourceFile attribute). +f A source routine, which is a function taking two arguments: a +f character argument of length 80 to contain a FITS card, and an +f integer error status argument. It should return an integer value. +f This function will be used by the FitsChan to obtain input +f FITS header cards. On each invocation, it should read the +f next input card from some external source (such as a FITS +f file), and return the contents of the card via its character +f argument. It should return a function result of one unless +f there are no more cards to be read, in which case it should +f return zero. If an error occurs, it should set its error +f status argument to an error value before returning. +f +f If the null routine AST_NULL is supplied as the SOURCE value, +f the FitsChan will remain empty until cards are explicitly +f stored in it (e.g. using AST_PUTCARDS, AST_PUTFITS or via the +f SourceFile attribute). +c sink +f SINK = SUBROUTINE (Given) +c Pointer to a sink function that takes a pointer to a +c null-terminated string as an argument and returns void. If +c no value has been set for the SinkFile attribute, this +c function will be used by the FitsChan to deliver any FITS +c header cards it contains when it is finally deleted. On +c each invocation, it should deliver the contents of the character +c string passed to it as a FITS header card to some external +c data store (such as a FITS file). +f A sink routine, which is a subroutine which takes two +f arguments: a character argument of length 80 to contain a +f FITS card, and an integer error status argument. If no +f value has been set for the SinkFile attribute, this routine +f will be used by the FitsChan to deliver any FITS header cards +f it contains when it is finally deleted. On each invocation, +f it should deliver the contents of the character string passed +f to it as a FITS header card to some external data store (such +f as a FITS file). If an error occurs, it should set its error +f status argument to an error value before returning. +* +c If "sink" is NULL, +f If the null routine AST_NULL is supplied as the SINK value, +* and no value has been set for the SinkFile attribute, the +* contents of the FitsChan will be lost when it is deleted. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new FitsChan. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new FitsChan. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +* +* Note, the FITSCHAN_OPTIONS environment variable may be used +* to specify default options for all newly created FitsChans. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFitsChan() +f AST_FITSCHAN = INTEGER +* A pointer to the new FitsChan. + +* Notes: +f - The names of the routines supplied for the SOURCE and SINK +f arguments should appear in EXTERNAL statements in the Fortran +f routine which invokes AST_FITSCHAN. However, this is not generally +f necessary for the null routine AST_NULL (so long as the AST_PAR +f include file has been used). +c - No FITS "END" card will be written via the sink function. You +f - No FITS "END" card will be written via the sink routine. You +* should add this card yourself after the FitsChan has been +* deleted. +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +f - Note that the null routine AST_NULL (one underscore) is +f different to AST__NULL (two underscores), which is the null Object +f pointer. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsChan *new; /* Pointer to new FitsChan */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the FitsChan, allocating memory and initialising the + virtual function table as well if necessary. This interface is for + use by other C functions within AST, and uses the standard "wrapper" + functions included in this class. */ + new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init, + &class_vtab, "FitsChan", source, SourceWrap, + sink, SinkWrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Apply any default options specified by "_OPTIONS" environment + variable. */ + astEnvSet( new ); + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + FitsChan's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new FitsChan. */ + return new; +} + +AstFitsChan *astFitsChanId_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, ... ) { + +/* +* Name: +* astFitsChanId_ + +* Purpose: +* Create a FitsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "fitschan.h" +* AstFitsChan *astFitsChanId_( const char *(* source)( void ), +* void (* sink)( const char * ), +* const char *options, ... ) + +* Class Membership: +* FitsChan constructor. + +* Description: +* This function implements the external (public) C interface to the +* astFitsChan constructor function. Another function (astFitsChanForId) +* should be called to create a FitsChan for use within other languages. +* Both functions return an ID value (instead of a true C pointer) to +* external users, and must be provided because astFitsChan_ has a variable +* argument list which cannot be encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astFitsChan_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astFitsChan_. + +* Returned Value: +* The ID value associated with the new FitsChan. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsChan *new; /* Pointer to new FitsChan */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the FitsChan, allocating memory and initialising the + virtual function table as well if necessary. This interface is for + use by external C functions and uses the standard "wrapper" + functions included in this class. */ + new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init, + &class_vtab, "FitsChan", source, SourceWrap, + sink, SinkWrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Apply any default options specified by "_OPTIONS" environment + variable. */ + astEnvSet( new ); + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + FitsChan's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new FitsChan. */ + return astMakeId( new ); +} + +AstFitsChan *astFitsChanForId_( const char *(* source)( void ), + char *(* source_wrap)( const char *(*)( void ), int * ), + void (* sink)( const char * ), + void (* sink_wrap)( void (*)( const char * ), + const char *, int * ), + const char *options, ... ) { + +/* +*+ +* Name: +* astFitsChanFor + +* Purpose: +* Initialise a FitsChan from a foreign language interface. + +* Type: +* Public function. + +* Synopsis: +* #include "fitschan.h" +* AstFitsChan *astFitsChanFor( const char *(* source)( void ), +* char *(* source_wrap)( const char *(*) +* ( void ), int * ), +* void (* sink)( const char * ), +* void (* sink_wrap)( void (*)( const char * ), +* const char *, int * ), +* const char *options, ... ) + +* Class Membership: +* FitsChan constructor. + +* Description: +* This function creates a new FitsChan from a foreign language +* interface and optionally initialises its attributes. +* +* A FitsChan implements FITS input/output for the AST library. +* Writing an Object to a FitsChan (using astWrite) will generate a +* textual representation of that Object in terms of FITS header cards, +* and reading from a FitsChan (using astRead) will create a new Object +* from its FITS representation. +* +* Normally, when you use a FitsChan, you should provide "source" +* and "sink" functions which connect it to an external data store +* by reading and writing the resulting text. This function also +* requires you to provide "wrapper" functions which will invoke +* the source and sink functions. + +* Parameters: +* source +* Pointer to a "source" function which will be used to obtain +* FITS header cards. Generally, this will be obtained by +* casting a pointer to a source function which is compatible +* with the "source_wrap" wrapper function (below). The pointer +* should later be cast back to its original type by the +* "source_wrap" function before the function is invoked. +* +* If "source" is NULL, the FitsChan will remain empty until +* cards are added explicitly (e.g. using astPutCards or astPutFits). +* source_wrap +* Pointer to a function which can be used to invoke the +* "source" function supplied (above). This wrapper function is +* necessary in order to hide variations in the nature of the +* source function, such as may arise when it is supplied by a +* foreign (non-C) language interface. +* +* The single parameter of the "source_wrap" function is a +* pointer to the "source" function, and it should cast this +* function pointer (as necessary) and invoke the function with +* appropriate arguments to obtain the next FITS header card. +* The "source_wrap" function should then return a pointer +* to a dynamically allocated, null terminated string containing +* the text that was read. The string will be freed (using +* astFree) when no longer required and the "source_wrap" +* function need not concern itself with this. A NULL pointer +* should be returned if there is no more input to read. +* +* If "source" is NULL, the FitsChan will remain empty until +* cards are added explicitly (e.g. using astPutCards or astPutFits). +* sink +* Pointer to a "sink" function which will be used to deliver +* FITS header cards. Generally, this will be obtained by +* casting a pointer to a sink function which is compatible with +* the "sink_wrap" wrapper function (below). The pointer should +* later be cast back to its original type by the "sink_wrap" +* function before the function is invoked. +* +* If "sink" is NULL, the contents of the FitsChan will not be +* written out before being deleted. +* sink_wrap +* Pointer to a function which can be used to invoke the "sink" +* function supplied (above). This wrapper function is necessary +* in order to hide variations in the nature of the sink +* function, such as may arise when it is supplied by a foreign +* (non-C) language interface. +* +* The first parameter of the "sink_wrap" function is a pointer +* to the "sink" function, and the second parameter is a pointer +* to a const, null-terminated character string containing the +* text to be written. The "sink_wrap" function should cast the +* "sink" function pointer (as necessary) and invoke the +* function with appropriate arguments to deliver the line of +* output text. The "sink_wrap" function then returns void. +* +* If "sink_wrap" is NULL, the contents of the FitsChan will not be +* written out before being deleted. +* options +* Pointer to a null-terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new FitsChan. The syntax used is identical to +* that for the astSet function and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of additional arguments may follow it in +* order to supply values to be substituted for these +* specifiers. The rules for supplying these are identical to +* those for the astSet function (and for the C "printf" +* function). + +* Returned Value: +* astFitsChanFor() +* A pointer to the new FitsChan. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +* - This function is only available through the public interface +* to the FitsChan class (not the protected interface) and is +* intended solely for use in implementing foreign language +* interfaces to this class. +*- + +* Implememtation Notes: +* - This function behaves exactly like astFitsChanId_, in that it +* returns ID values and not true C pointers, but it has two +* additional arguments. These are pointers to the "wrapper +* functions" which are needed to accommodate foreign language +* interfaces. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsChan *new; /* Pointer to new FitsChan */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise the FitsChan, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init, + &class_vtab, "FitsChan", source, source_wrap, + sink, sink_wrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Apply any default options specified by "_OPTIONS" environment + variable. */ + astEnvSet( new ); + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + FitsChan's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new FitsChan. */ + return astMakeId( new ); +} + +AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init, + AstFitsChanVtab *vtab, const char *name, + const char *(* source)( void ), + char *(* source_wrap)( const char *(*)( void ), int * ), + void (* sink)( const char * ), + void (* sink_wrap)( void (*)( const char * ), + const char *, int * ), int *status ) { + +/* +*+ +* Name: +* astInitFitsChan + +* Purpose: +* Initialise a FitsChan. + +* Type: +* Protected function. + +* Synopsis: +* #include "fitschan.h" +* AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init, +* AstFitsChanVtab *vtab, const char *name, +* const char *(* source)( void ), +* char *(* source_wrap)( const char *(*)( void ), int * ), +* void (* sink)( const char * ), +* void (* sink_wrap)( void (*)( const char * ), +* const char *, int * ) ) + +* Class Membership: +* FitsChan initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new FitsChan object. It allocates memory (if +* necessary) to accommodate the FitsChan plus any additional data +* associated with the derived class. It then initialises a +* FitsChan structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for a FitsChan at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the FitsChan is to be +* initialised. This must be of sufficient size to accommodate +* the FitsChan data (sizeof(FitsChan)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the FitsChan (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the FitsChan structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A boolean flag indicating if the FitsChan's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new FitsChan. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* source +* Pointer to a "source" function which will be used to obtain +* FITS header cards. Generally, this will be obtained by +* casting a pointer to a source function which is compatible +* with the "source_wrap" wrapper function (below). The pointer +* should later be cast back to its original type by the +* "source_wrap" function before the function is invoked. +* +* If "source" is NULL, the FitsChan will remain empty until +* cards are added explicitly (e.g. using astPutCards or astPutFits). +* source_wrap +* Pointer to a function which can be used to invoke the +* "source" function supplied (above). This wrapper function is +* necessary in order to hide variations in the nature of the +* source function, such as may arise when it is supplied by a +* foreign (non-C) language interface. +* +* The single parameter of the "source_wrap" function is a +* pointer to the "source" function, and it should cast this +* function pointer (as necessary) and invoke the function with +* appropriate arguments to obtain the next FITS header card. +* The "source_wrap" function should then return a pointer +* to a dynamically allocated, null terminated string containing +* the text that was read. The string will be freed (using +* astFree) when no longer required and the "source_wrap" +* function need not concern itself with this. A NULL pointer +* should be returned if there is no more input to read. +* +* If "source" is NULL, the FitsChan will remain empty until +* cards are added explicitly (e.g. using astPutCards or astPutFits). +* sink +* Pointer to a "sink" function which will be used to deliver +* FITS header cards. Generally, this will be obtained by +* casting a pointer to a sink function which is compatible with +* the "sink_wrap" wrapper function (below). The pointer should +* later be cast back to its original type by the "sink_wrap" +* function before the function is invoked. +* +* If "sink" is NULL, the contents of the FitsChan will not be +* written out before being deleted. +* sink_wrap +* Pointer to a function which can be used to invoke the "sink" +* function supplied (above). This wrapper function is necessary +* in order to hide variations in the nature of the sink +* function, such as may arise when it is supplied by a foreign +* (non-C) language interface. +* +* The first parameter of the "sink_wrap" function is a pointer +* to the "sink" function, and the second parameter is a pointer +* to a const, null-terminated character string containing the +* text to be written. The "sink_wrap" function should cast the +* "sink" function pointer (as necessary) and invoke the +* function with appropriate arguments to deliver the line of +* output text. The "sink_wrap" function then returns void. +* +* If "sink_wrap" is NULL, the contents of the FitsChan will not be +* written out before being deleted. + +* Returned Value: +* A pointer to the new FitsChan. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstFitsChan *new; /* Pointer to new FitsChan */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitFitsChanVtab( vtab, name ); + +/* Initialise a Channel structure (the parent class) as the first + component within the FitsChan structure, allocating memory if + necessary. I am not sure why FitsChan has its own source_wrap and + sink_wrap items, rather than just using those inherited from Channel. + It may be possible to do away with the fitschan wrappers and just use + the channel wrapper, but I have not yet tried this. Old mail from RFWS + suggests that it may be because the F77 FitsChan source and sink + interfaces handle fixed length strings (80 characters), whereas + Channel sournce and sink handle variable length strings. This needs + investigating. */ + new = (AstFitsChan *) astInitChannel( mem, size, 0, + (AstChannelVtab *) vtab, name, + NULL, NULL, NULL, NULL ); + if ( astOK ) { + +/* Initialise the FitsChan data. */ +/* ---------------------------- */ + new->head = NULL; + new->card = NULL; + new->keyseq = NULL; + new->keywords = NULL; + new->defb1950 = -1; + new->tabok = -INT_MAX; + new->cdmatrix = -1; + new->carlin = -1; + new->polytan = -INT_MAX; + new->iwc = -1; + new->clean = -1; + new->fitsdigits = DBL_DIG; + new->fitsaxisorder = NULL; + new->encoding = UNKNOWN_ENCODING; + new->warnings = NULL; + new->tables = NULL; + +/* Save the pointers to the source and sink functions and the wrapper + functions that invoke them. */ + new->source = source; + new->saved_source = NULL; + new->source_wrap = source_wrap; + new->sink = sink; + new->sink_wrap = sink_wrap; + new->tabsource = NULL; + new->tabsource_wrap = NULL; + +/* Rewind the FitsChan so that the next read operation will return the + first card. */ + new->card = new->head; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} +AstFitsChan *astLoadFitsChan_( void *mem, size_t size, + AstFitsChanVtab *vtab, const char *name, + AstChannel *channel, int *status ) { + +/* +*+ +* Name: +* astLoadFitsChan + +* Purpose: +* Load a FitsChan. + +* Type: +* Protected function. + +* Synopsis: +* #include "fitschan.h" +* AstFitsChan *astLoadFitsChan( void *mem, size_t size, +* AstFitsChanVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* FitsChan loader. + +* Description: +* This function is provided to load a new FitsChan using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* FitsChan structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a FitsChan at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the FitsChan is to be +* loaded. This must be of sufficient size to accommodate the +* FitsChan data (sizeof(FitsChan)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the FitsChan (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the FitsChan structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstFitsChan) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new FitsChan. If this is NULL, a pointer +* to the (static) virtual function table for the FitsChan class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "FitsChan" is used instead. + +* Returned Value: +* A pointer to the new FitsChan. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsChan *new; /* Pointer to the new FitsChan */ + char *comment; /* Pointer to keyword comment */ + char *keynm; /* Keyword name */ + char *text; /* Textual version of integer value */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + double dval[2]; /* Double precision data values */ + int flags; /* Keyword flags */ + int free_data; /* Should data memory be freed? */ + int ival[2]; /* Integer data values */ + int ncard; /* No. of FitsCards read so far */ + int type; /* Keyword type */ + void *data; /* Pointer to keyword data value */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this FitsChan. In this case the + FitsChan belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstFitsChan ); + vtab = &class_vtab; + name = "FitsChan"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitFitsChanVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built FitsChan. */ + new = astLoadChannel( mem, size, (AstChannelVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ + +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "FitsChan" ); + +/* Initialise the KeyMap holding the keywords in the FitsChan. */ + new->keywords = NULL; + +/* Initialise the list of keyword sequence numbers. */ + new->keyseq = NULL; + +/* Set the pointers to the source and sink functions, and their + wrapper functions, to NULL (we cannot restore these since they + refer to process-specific addresses). */ + new->source = NULL; + new->saved_source = NULL; + new->source_wrap = NULL; + new->sink = NULL; + new->sink_wrap = NULL; + new->tabsource = NULL; + new->tabsource_wrap = NULL; + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* Encoding. */ +/* --------- */ + text = astReadString( channel, "encod", UNKNOWN_STRING ); + if( text && strcmp( text, UNKNOWN_STRING ) ) { + new->encoding = FindString( MAX_ENCODING + 1, xencod, text, + "the FitsChan component 'Encod'", + "astRead", astGetClass( channel ), status ); + } else { + new->encoding = UNKNOWN_ENCODING; + } + if ( TestEncoding( new, status ) ) SetEncoding( new, new->encoding, status ); + text = astFree( text ); + +/* FitsAxisOrder. */ +/* -------------- */ + new->fitsaxisorder = astReadString( channel, "faxord", NULL ); + +/* FitsDigits. */ +/* ----------- */ + new->fitsdigits = astReadInt( channel, "fitsdg", DBL_DIG ); + if ( TestFitsDigits( new, status ) ) SetFitsDigits( new, new->fitsdigits, status ); + +/* DefB1950 */ +/* -------- */ + new->defb1950 = astReadInt( channel, "dfb1950", -1 ); + if ( TestDefB1950( new, status ) ) SetDefB1950( new, new->defb1950, status ); + +/* TabOK */ +/* ----- */ + new->tabok = astReadInt( channel, "tabok", -INT_MAX ); + if ( TestTabOK( new, status ) ) SetTabOK( new, new->tabok, status ); + +/* CDMatrix */ +/* -------- */ + new->cdmatrix = astReadInt( channel, "cdmat", -1 ); + if ( TestCDMatrix( new, status ) ) SetCDMatrix( new, new->cdmatrix, status ); + +/* CarLin */ +/* ------ */ + new->carlin = astReadInt( channel, "carlin", -1 ); + if ( TestCarLin( new, status ) ) SetCarLin( new, new->carlin, status ); + +/* PolyTan */ +/* ------- */ + new->polytan = astReadInt( channel, "polytan", -1 ); + if ( TestPolyTan( new, status ) ) SetPolyTan( new, new->polytan, status ); + +/* Iwc */ +/* --- */ + new->iwc = astReadInt( channel, "iwc", -1 ); + if ( TestIwc( new, status ) ) SetIwc( new, new->iwc, status ); + +/* Clean */ +/* ----- */ + new->clean = astReadInt( channel, "clean", -1 ); + if ( TestClean( new, status ) ) SetClean( new, new->clean, status ); + +/* Warnings. */ +/* --------- */ + new->warnings = astReadString( channel, "warn", NULL ); + +/* Card. */ +/* ----- */ + +/* Initialise the index of the card to be read next. */ + ncard = 1; + new->card = NULL; + new->head = NULL; + +/* Load each card. */ + type = AST__NOTYPE + 1; + while( type != AST__NOTYPE && astOK ){ + +/* Get the keyword type. */ + (void) sprintf( buff, "ty%d", ncard ); + text = astReadString( channel, buff, " " ); + if( strcmp( text, " " ) ) { + type = FindString( 9, type_names, text, + "a FitsChan keyword data type", + "astRead", astGetClass( channel ), status ); + } else { + type = AST__NOTYPE; + } + text = astFree( text ); + +/* Only proceed if the keyword type was found. */ + if( type != AST__NOTYPE ){ + +/* Get the keyword name. Use a default blank name. */ + (void) sprintf( buff, "nm%d", ncard ); + keynm = astReadString( channel, buff, " " ); + +/* Get the data value, using the appropriate data type, unless the + keyword is a comment keyword or is undefined. */ + free_data = 0; + if( type == AST__FLOAT ){ + (void) sprintf( buff, "dt%d", ncard ); + dval[ 0 ] = astReadDouble( channel, buff, AST__BAD ); + data = (void *) dval; + } else if( type == AST__STRING || type == AST__CONTINUE ){ + (void) sprintf( buff, "dt%d", ncard ); + data = (void *) astReadString( channel, buff, "" ); + free_data = 1; + } else if( type == AST__INT ){ + (void) sprintf( buff, "dt%d", ncard ); + ival[ 0 ] = astReadInt( channel, buff, 0 ); + data = (void *) ival; + } else if( type == AST__LOGICAL ){ + (void) sprintf( buff, "dt%d", ncard ); + ival[ 0 ] = astReadInt( channel, buff, 0 ); + data = (void *) ival; + } else if( type == AST__COMPLEXF ){ + (void) sprintf( buff, "dr%d", ncard ); + dval[ 0 ] = astReadDouble( channel, buff, AST__BAD ); + (void) sprintf( buff, "di%d", ncard ); + dval[ 1 ] = astReadDouble( channel, buff, AST__BAD ); + data = (void *) dval; + } else if( type == AST__COMPLEXI ){ + (void) sprintf( buff, "dr%d", ncard ); + ival[ 0 ] = astReadInt( channel, buff, 0 ); + (void) sprintf( buff, "di%d", ncard ); + ival[ 1 ] = astReadInt( channel, buff, 0 ); + data = (void *) ival; + } else { + data = NULL; + } + +/* Get the keyword flags (only written by versions of AST later than + V1.4). These are packed into an int. */ + (void) sprintf( buff, "fl%d", ncard ); + flags = astReadInt( channel, buff, 0 ); + +/* If the flags were not found, use the keyword deletion flag written by + AST V1.4 and earlier. */ + if( !flags ) { + (void) sprintf( buff, "dl%d", ncard ); + flags = astReadInt( channel, buff, 0 ); + } + +/* Get the keyword comment. */ + (void) sprintf( buff, "cm%d", ncard ); + comment = astReadString( channel, buff, NULL ); + +/* Append a new card to the output FitsChan. */ + NewCard( new, keynm, type, data, comment, flags, status ); + +/* Free the character strings, and data (if required). */ + comment = (char *) astFree( (void *) comment ); + keynm = (char *) astFree( (void *) keynm ); + if( free_data ) data = astFree( data ); + } + +/* Move on to the next card. */ + ncard++; + } + +/* Set up the current card index. */ + astSetCard( new, astReadInt( channel, "card", 0 ) ); + +/* Load any FitTables. */ + new->tables = astReadObject( channel, "tables", NULL ); + } + +/* If an error occurred, clean up by deleting the new FitsChan. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the new FitsChan pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ + +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astWriteFits_( AstFitsChan *this, int *status ){ + if( !this ) return; + (**astMEMBER(this,FitsChan,WriteFits))(this, status ); +} + +void astReadFits_( AstFitsChan *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,ReadFits))(this, status ); +} + +void astEmptyFits_( AstFitsChan *this, int *status ){ + if( !this ) return; + (**astMEMBER(this,FitsChan,EmptyFits))(this, status ); +} + +void astShowFits_( AstFitsChan *this, int *status ){ + if( !this ) return; + (**astMEMBER(this,FitsChan,ShowFits))(this, status ); +} + +void astPutCards_( AstFitsChan *this, const char *cards, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,PutCards))(this,cards, status ); +} + +void astPutFits_( AstFitsChan *this, const char *card, int overwrite, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,PutFits))(this,card,overwrite, status ); +} + +void astDelFits_( AstFitsChan *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,DelFits))(this, status ); +} + +void astPurgeWCS_( AstFitsChan *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,PurgeWCS))(this, status ); +} + +AstKeyMap *astGetTables_( AstFitsChan *this, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,FitsChan,GetTables))(this, status ); +} + +void astPutTables_( AstFitsChan *this, AstKeyMap *tables, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,PutTables))(this, tables, status ); +} + +void astPutTable_( AstFitsChan *this, AstFitsTable *table, const char *extnam, + int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,PutTable))(this, table, extnam, status ); +} + +void astRemoveTables_( AstFitsChan *this, const char *key, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,RemoveTables))(this, key, status ); +} + +void astRetainFits_( AstFitsChan *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,RetainFits))(this, status ); +} + +int astFitsEof_( AstFitsChan *this, int *status ){ + if( !this ) return 1; + return (**astMEMBER(this,FitsChan,FitsEof))( this, status ); +} + +void astSetFitsCom_( AstFitsChan *this, const char *name, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsCom))( this, name, comment, overwrite, status ); +} + +void astSetFitsI_( AstFitsChan *this, const char *name, int value, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsI))( this, name, value, comment, overwrite, status ); +} + +void astSetFitsF_( AstFitsChan *this, const char *name, double value, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsF))( this, name, value, comment, overwrite, status ); +} + +void astSetFitsS_( AstFitsChan *this, const char *name, const char *value, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsS))( this, name, value, comment, overwrite, status ); +} + +void astSetFitsCN_( AstFitsChan *this, const char *name, const char *value, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsCN))( this, name, value, comment, overwrite, status ); +} + +void astSetFitsCF_( AstFitsChan *this, const char *name, double *value, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsCF))( this, name, value, comment, overwrite, status ); +} + +void astSetFitsCI_( AstFitsChan *this, const char *name, int *value, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsCI))( this, name, value, comment, overwrite, status ); +} + +void astSetFitsL_( AstFitsChan *this, const char *name, int value, + const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsL))( this, name, value, comment, overwrite, status ); +} + +void astSetFitsU_( AstFitsChan *this, const char *name, const char *comment, + int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsU))( this, name, comment, overwrite, status ); +} + +void astSetFitsCM_( AstFitsChan *this, const char *comment, int overwrite, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsChan,SetFitsCM))( this, comment, overwrite, status ); +} + +void astClearCard_( AstFitsChan *this, int *status ){ + if( !this ) return; + (**astMEMBER(this,FitsChan,ClearCard))( this, status ); +} + +void astSetCard_( AstFitsChan *this, int card, int *status ){ + if( !this ) return; + (**astMEMBER(this,FitsChan,SetCard))( this, card, status ); +} + +int astTestCard_( AstFitsChan *this, int *status ){ + if( !this ) return 0; + return (**astMEMBER(this,FitsChan,TestCard))( this, status ); +} + +int astGetCard_( AstFitsChan *this, int *status ){ + if( !this ) return 0; + return (**astMEMBER(this,FitsChan,GetCard))( this, status ); +} + +int astGetNcard_( AstFitsChan *this, int *status ){ + if( !this ) return 0; + return (**astMEMBER(this,FitsChan,GetNcard))( this, status ); +} + +int astGetCardType_( AstFitsChan *this, int *status ){ + if( !this ) return AST__NOTYPE; + return (**astMEMBER(this,FitsChan,GetCardType))( this, status ); +} + +const char *astGetCardComm_( AstFitsChan *this, int *status ){ + if( !this ) return NULL; + return (**astMEMBER(this,FitsChan,GetCardComm))( this, status ); +} + +const char *astGetCardName_( AstFitsChan *this, int *status ){ + if( !this ) return NULL; + return (**astMEMBER(this,FitsChan,GetCardName))( this, status ); +} + +int astGetNkey_( AstFitsChan *this, int *status ){ + if( !this ) return 0; + return (**astMEMBER(this,FitsChan,GetNkey))( this, status ); +} + +int astGetClean_( AstFitsChan *this, int *status ){ + if( !this ) return 0; + return (**astMEMBER(this,FitsChan,GetClean))( this, status ); +} + +const char *astGetAllWarnings_( AstFitsChan *this, int *status ){ + if( !this ) return NULL; + return (**astMEMBER(this,FitsChan,GetAllWarnings))( this, status ); +} + +int astGetFitsCF_( AstFitsChan *this, const char *name, double *value, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetFitsCF))( this, name, value, status ); +} + +int astGetFitsCI_( AstFitsChan *this, const char *name, int *value, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetFitsCI))( this, name, value, status ); +} + +int astGetFitsF_( AstFitsChan *this, const char *name, double *value, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetFitsF))( this, name, value, status ); +} + +int astGetFitsI_( AstFitsChan *this, const char *name, int *value, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetFitsI))( this, name, value, status ); +} + +int astGetFitsL_( AstFitsChan *this, const char *name, int *value, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetFitsL))( this, name, value, status ); +} + +int astTestFits_( AstFitsChan *this, const char *name, int *there, int *status ){ + if( there ) *there = 0; + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,TestFits))( this, name, there, status ); +} + +int astGetFitsS_( AstFitsChan *this, const char *name, char **value, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetFitsS))( this, name, value, status ); +} + +int astGetFitsCN_( AstFitsChan *this, const char *name, char **value, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetFitsCN))( this, name, value, status ); +} + +int astFitsGetCom_( AstFitsChan *this, const char *name, char **comment, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,FitsGetCom))( this, name, comment, status ); +} + +int astKeyFields_( AstFitsChan *this, const char *filter, int maxfld, + int *ubnd, int *lbnd, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,KeyFields))( this, filter, maxfld, + ubnd, lbnd, status ); +} + +int astFindFits_( AstFitsChan *this, const char *name, char *card, int inc, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,FindFits))( this, name, card, inc, status ); +} + +int astGetEncoding_( AstFitsChan *this, int *status ){ + if( !astOK ) return UNKNOWN_ENCODING; + return (**astMEMBER(this,FitsChan,GetEncoding))( this, status ); +} + +int astGetCDMatrix_( AstFitsChan *this, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,FitsChan,GetCDMatrix))( this, status ); +} +void astSetTableSource_( AstFitsChan *this, + void (*tabsource)( void ), + void (*tabsource_wrap)( void (*)( void ), + AstFitsChan *, const char *, + int, int, int * ), + int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,SetTableSource))( this, tabsource, + tabsource_wrap, status ); +} +void astTableSource_( AstFitsChan *this, + void (* tabsource)( AstFitsChan *, const char *, + int, int, int * ), + int *status ){ + if( !astOK ) return; + (**astMEMBER(this,FitsChan,TableSource))( this, tabsource, status ); +} + +/* + * A diagnostic function which lists the contents of a FitsChan to + * standard output. + */ + +/* +static void ListFC( AstFitsChan *, const char * ); + +static void ListFC( AstFitsChan *this, const char *ttl ) { + FitsCard *cardo; + char card[ 81 ]; + printf("%s\n----------------------------------------\n", ttl ); + cardo = (FitsCard *) this->card; + astClearCard( this ); + while( !astFitsEof( this ) && astOK ){ + FormatCard( this, card, "List" ); + if( this->card == cardo ) { + printf( "%s <<<<< currrent card <<<<< \n", card ); + } else { + printf( "%s\n", card ); + } + MoveCard( this, 1, "List", "FitsChan" ); + } + this->card = cardo; +} +*/ + + + + + + + + + + + diff --git a/fitschan.h b/fitschan.h new file mode 100644 index 0000000..ba197df --- /dev/null +++ b/fitschan.h @@ -0,0 +1,871 @@ +#if !defined( FITSCHAN_INCLUDED ) /* Include this file only once */ +#define FITSCHAN_INCLUDED +/* +*+ +* Name: +* fitschan.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the FitsChan class. + +* Invocation: +* #include "fitschan.h" + +* Description: +* This include file defines the interface to the FitsChan class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The FitsChan class provides facilities for reading and writing AST +* Objects in the form of FITS header cards. + +* Inheritance: +* The FitsChan class inherits from the Channel class. + +* Macros: + +* Protected: +* AST__NOTYPE +* Integer dentifier for an illegal FITS data type. +* AST__COMMENT +* Integer dentifier for a FITS comment keyword. +* AST__INT +* Integer dentifier for the integer FITS data type. +* AST__FLOAT +* Integer dentifier for the floating point FITS data type. +* AST__STRING +* Integer dentifier for the string FITS data type. +* AST__CONTINUE +* Integer dentifier for the continuation string FITS data type. +* AST__COMPLEXF +* Integer dentifier for the complex floating point FITS data type. +* AST__COMPLEXI +* Integer dentifier for the complex integer FITS data type. +* AST__LOGICAL +* Integer dentifier for the logical FITS data type. +* AST__UNDEF +* Integer dentifier for undefined FITS data type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 11-DEC-1996 (DSB): +* Original version. +* 30-Jun-1997 (DSB): +* Changed character pointer argument to character array in PutFits. +* 26-SEP-1997 (DSB): +* Added CDMatrix attribute. +* 21-OCT-1997 (DSB): +* o Renamed astFields as astKeyFields. +* 1-APR-2000 (DSB): +* Changes for CDi_j based FITS-WCS standard. +* 18-MAY-2000 (DSB): +* Added Warnings attribute. +* 4-APR-2001 (DSB): +* Added AllWarnings attribute. +* 20-FEB-2002 (DSB): +* Added CarLin attribute. +* 8-JAN-2003 (DSB): +* Added protected astInitFitsChanVtab method. +* 13-FEB-2003 (DSB): +* Added Clean attribute. +* 19-MAR-2004 (DSB): +* Added astPutCards function. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "channel.h" /* I/O channels (parent class) */ +#include "pointset.h" /* Defines AST__BAD */ +#include "keymap.h" /* Defines the AstKeyMap type */ + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ------- */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +#define AST__NOTYPE -1 +#define AST__COMMENT 0 +#define AST__INT 1 +#define AST__FLOAT 2 +#define AST__STRING 3 +#define AST__COMPLEXF 4 +#define AST__COMPLEXI 5 +#define AST__LOGICAL 6 +#define AST__CONTINUE 7 +#define AST__UNDEF 8 + +#if defined(astCLASS) /* Protected */ + +/* Define constants used to size global arrays in this module. */ +#define AST__FITSCHAN_FITSCARDLEN 80 +#define AST__FITSCHAN_GETATTRIB_BUFF_LEN 50 + +#endif + +/* The EXTNAME value for FITS binary tables used to store coordinate arrays for + the -TAB algorithm. */ +#define AST_TABEXTNAME "WCS-TAB" + +/* Type Definitions. */ +/* ================= */ + +/* FitsChan structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +struct AstFitsChan; +typedef struct AstFitsChan { + +/* Attributes inherited from the parent class. */ + AstChannel channel; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int encoding; /* System for encoding AST objects ito FITS headers */ + int defb1950; /* Use FK4 B1950 as defaults? */ + int tabok; /* Support -TAB algorithm? */ + int cdmatrix; /* Use a CD matrix in FITS-WCS Encoding? */ + int polytan; /* Use distorted TAN convention? */ + int carlin; /* Use linear CAR mappings? */ + int iwc; /* Include an IWC Frame? */ + int clean; /* Remove used cards even if an error occurs? */ + int fitsdigits; /* No. of decmial places in formatted floating point keyword values */ + char *fitsaxisorder; /* Pointer to a string defining WCS axis order */ + char *warnings; /* Pointer to a string containing warning conditions */ + void *card; /* Pointer to next FitsCard to be read */ + void *head; /* Pointer to first FitsCard in the circular linked list */ + AstKeyMap *keyseq; /* List of keyword sequence numbers used */ + AstKeyMap *keywords; /* A KeyMap holding the keywords in the FitsChan */ + AstKeyMap *tables; /* A KeyMap holding the binary tables in the FitsChan */ + + const char *(* source)( void ); /* Pointer to source function */ + const char *(* saved_source)( void ); /* Pointer to saved source function */ + char *(* source_wrap)( const char *(*)( void ), int * ); + /* Source wrapper function pointer */ + + void (* sink)( const char * ); /* Pointer to sink function */ + void (* sink_wrap)( void (*)( const char * ), const char *, int * ); + /* Sink wrapper function pointer */ + + void (* tabsource)( void ); /* Pointer to table source function */ + void (* tabsource_wrap)( void (*)( void ), struct AstFitsChan *, const char *, int, int, int * ); + /* Table source wrapper function pointer */ + +} AstFitsChan; + +/* Virtual function table. */ +/* ----------------------- */ +/* The virtual function table makes a forward reference to the + AstFitsTable structure which is not defined until "fitstable.h" is + included (below). Hence make a preliminary definition available + now. */ +struct AstFitsTable; + +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstFitsChanVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstChannelVtab channel_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstKeyMap *(* GetTables)( AstFitsChan *, int * ); + int (* FindFits)( AstFitsChan *, const char *, char [81], int, int * ); + int (* FitsEof)( AstFitsChan *, int * ); + int (* FitsGetCom)( AstFitsChan *, const char *, char **, int * ); + int (* GetFitsCF)( AstFitsChan *, const char *, double *, int * ); + int (* GetFitsCI)( AstFitsChan *, const char *, int *, int * ); + int (* GetFitsCN)( AstFitsChan *, const char *, char **, int * ); + int (* GetFitsF)( AstFitsChan *, const char *, double *, int * ); + int (* GetFitsI)( AstFitsChan *, const char *, int *, int * ); + int (* GetFitsL)( AstFitsChan *, const char *, int *, int * ); + int (* GetFitsS)( AstFitsChan *, const char *, char **, int * ); + int (* KeyFields)( AstFitsChan *, const char *, int, int *, int *, int * ); + int (* TestFits)( AstFitsChan *, const char *, int *, int * ); + void (* DelFits)( AstFitsChan *, int * ); + void (* Empty)( AstFitsChan *, int * ); + void (* ReadFits)( AstFitsChan *, int * ); + void (* WriteFits)( AstFitsChan *, int * ); + void (* EmptyFits)( AstFitsChan *, int * ); + void (* ShowFits)( AstFitsChan *, int * ); + void (* PurgeWCS)( AstFitsChan *, int * ); + void (* PutCards)( AstFitsChan *, const char *, int * ); + void (* PutFits)( AstFitsChan *, const char [81], int, int * ); + void (* PutTable)( AstFitsChan *, struct AstFitsTable *, const char *, int * ); + void (* PutTables)( AstFitsChan *, AstKeyMap *, int * ); + void (* RemoveTables)( AstFitsChan *, const char *, int * ); + void (* RetainFits)( AstFitsChan *, int * ); + void (* SetFitsCF)( AstFitsChan *, const char *, double *, const char *, int, int * ); + void (* SetFitsCI)( AstFitsChan *, const char *, int *, const char *, int, int * ); + void (* SetFitsCM)( AstFitsChan *, const char *, int, int * ); + void (* SetFitsCN)( AstFitsChan *, const char *, const char *, const char *, int, int * ); + void (* SetFitsCom)( AstFitsChan *, const char *, const char *, int, int * ); + void (* SetFitsF)( AstFitsChan *, const char *, double, const char *, int, int * ); + void (* SetFitsI)( AstFitsChan *, const char *, int, const char *, int, int * ); + void (* SetFitsL)( AstFitsChan *, const char *, int, const char *, int, int * ); + void (* SetFitsS)( AstFitsChan *, const char *, const char *, const char *, int, int * ); + void (* SetFitsU)( AstFitsChan *, const char *, const char *, int, int * ); + + int (* GetCard)( AstFitsChan *, int * ); + int (* TestCard)( AstFitsChan *, int * ); + void (* SetCard)( AstFitsChan *, int, int * ); + void (* ClearCard)( AstFitsChan *, int * ); + + int (* GetFitsDigits)( AstFitsChan *, int * ); + int (* TestFitsDigits)( AstFitsChan *, int * ); + void (* SetFitsDigits)( AstFitsChan *, int, int * ); + void (* ClearFitsDigits)( AstFitsChan *, int * ); + + const char *(* GetFitsAxisOrder)( AstFitsChan *, int * ); + int (* TestFitsAxisOrder)( AstFitsChan *, int * ); + void (* SetFitsAxisOrder)( AstFitsChan *, const char *, int * ); + void (* ClearFitsAxisOrder)( AstFitsChan *, int * ); + + int (* GetDefB1950)( AstFitsChan *, int * ); + int (* TestDefB1950)( AstFitsChan *, int * ); + void (* SetDefB1950)( AstFitsChan *, int, int * ); + void (* ClearDefB1950)( AstFitsChan *, int * ); + + int (* GetTabOK)( AstFitsChan *, int * ); + int (* TestTabOK)( AstFitsChan *, int * ); + void (* SetTabOK)( AstFitsChan *, int, int * ); + void (* ClearTabOK)( AstFitsChan *, int * ); + + int (* GetCarLin)( AstFitsChan *, int * ); + int (* TestCarLin)( AstFitsChan *, int * ); + void (* SetCarLin)( AstFitsChan *, int, int * ); + void (* ClearCarLin)( AstFitsChan *, int * ); + + int (* GetNcard)( AstFitsChan *, int * ); + + int (* GetCardType)( AstFitsChan *, int * ); + const char *(* GetCardName)( AstFitsChan *, int * ); + const char *(* GetCardComm)( AstFitsChan *, int * ); + + int (* GetNkey)( AstFitsChan *, int * ); + + int (* GetEncoding)( AstFitsChan *, int * ); + int (* TestEncoding)( AstFitsChan *, int * ); + void (* SetEncoding)( AstFitsChan *, int, int * ); + void (* ClearEncoding)( AstFitsChan *, int * ); + + const char *(* GetAllWarnings)( AstFitsChan *, int * ); + + const char *(* GetWarnings)( AstFitsChan *, int * ); + int (* TestWarnings)( AstFitsChan *, int * ); + void (* ClearWarnings)( AstFitsChan *, int * ); + void (* SetWarnings)( AstFitsChan *, const char *, int * ); + + int (* GetClean)( AstFitsChan *, int * ); + int (* TestClean)( AstFitsChan *, int * ); + void (* SetClean)( AstFitsChan *, int, int * ); + void (* ClearClean)( AstFitsChan *, int * ); + + int (* GetCDMatrix)( AstFitsChan *, int * ); + int (* TestCDMatrix)( AstFitsChan *, int * ); + void (* SetCDMatrix)( AstFitsChan *, int, int * ); + void (* ClearCDMatrix)( AstFitsChan *, int * ); + + int (* GetPolyTan)( AstFitsChan *, int * ); + int (* TestPolyTan)( AstFitsChan *, int * ); + void (* SetPolyTan)( AstFitsChan *, int, int * ); + void (* ClearPolyTan)( AstFitsChan *, int * ); + + int (* GetIwc)( AstFitsChan *, int * ); + int (* TestIwc)( AstFitsChan *, int * ); + void (* SetIwc)( AstFitsChan *, int, int * ); + void (* ClearIwc)( AstFitsChan *, int * ); + + void (* SetTableSource)( AstFitsChan *, + void (*)( void ), + void (*)( void (*)( void ), + AstFitsChan *, const char *, int, + int, int * ), + int * ); + + void (* TableSource)( AstFitsChan *, + void (*)( AstFitsChan *, const char *, int, int, + int * ), + int * ); + +} AstFitsChanVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstFitsChanGlobals { + AstFitsChanVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__FITSCHAN_GETATTRIB_BUFF_LEN + 1 ]; + char CnvType_Text[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + char CnvType_Text0[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + char CnvType_Text1[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + int Items_Written; + int Write_Nest; + int Current_Indent; + int Ignore_Used; + int Mark_New; + int CreateKeyword_Seq_Nchars; + char FormatKey_Buff[ 10 ]; + char FitsGetCom_Sval[ AST__FITSCHAN_FITSCARDLEN + 1 ]; + const char *IsSpectral_Ret; + char Match_Fmt[ 10 ]; + const char *Match_Template; + int *Match_PA; + int *Match_PB; + int Match_NA; + int Match_NB; + int Match_Nentry; + char WcsCelestial_Type[ 4 ]; +} AstFitsChanGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(FitsChan) /* Check class membership */ +astPROTO_ISA(FitsChan) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstFitsChan *astFitsChan_( const char *(*)( void ), void (*)( const char * ), + const char *, int *, ...); +#else +AstFitsChan *astFitsChanId_( const char *(*)( void ), void (*)( const char * ), + const char *, ... )__attribute__((format(printf,3,4))); +AstFitsChan *astFitsChanForId_( const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), + const char *, int * ), + const char *, ... ); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstFitsChan *astInitFitsChan_( void *, size_t, int, AstFitsChanVtab *, + const char *, + const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), const char *, int * ), int * ); + +/* Vtab initialiser. */ +void astInitFitsChanVtab_( AstFitsChanVtab *, const char *, int * ); + + + +/* Loader. */ +AstFitsChan *astLoadFitsChan_( void *, size_t, AstFitsChanVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitFitsChanGlobals_( AstFitsChanGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + AstKeyMap *astGetTables_( AstFitsChan *, int * ); + int astFindFits_( AstFitsChan *, const char *, char [81], int, int * ); + int astGetFitsCF_( AstFitsChan *, const char *, double *, int * ); + int astGetFitsCI_( AstFitsChan *, const char *, int *, int * ); + int astGetFitsCN_( AstFitsChan *, const char *, char **, int * ); + int astGetFitsF_( AstFitsChan *, const char *, double *, int * ); + int astGetFitsI_( AstFitsChan *, const char *, int *, int * ); + int astGetFitsL_( AstFitsChan *, const char *, int *, int * ); + int astGetFitsS_( AstFitsChan *, const char *, char **, int * ); + int astTestFits_( AstFitsChan *, const char *, int *, int * ); + void astDelFits_( AstFitsChan *, int * ); + void astReadFits_( AstFitsChan *, int * ); + void astWriteFits_( AstFitsChan *, int * ); + void astEmptyFits_( AstFitsChan *, int * ); + void astShowFits_( AstFitsChan *, int * ); + void astPurgeWCS_( AstFitsChan *, int * ); + void astPutCards_( AstFitsChan *, const char *, int * ); + void astPutFits_( AstFitsChan *, const char [81], int, int * ); + void astPutTable_( AstFitsChan *, struct AstFitsTable *, const char *, int * ); + void astPutTables_( AstFitsChan *, AstKeyMap *, int * ); + void astRemoveTables_( AstFitsChan *, const char *, int * ); + void astRetainFits_( AstFitsChan *, int * ); + void astSetFitsCF_( AstFitsChan *, const char *, double *, const char *, int, int * ); + void astSetFitsCI_( AstFitsChan *, const char *, int *, const char *, int, int * ); + void astSetFitsCM_( AstFitsChan *, const char *, int, int * ); + void astSetFitsCN_( AstFitsChan *, const char *, const char *, const char *, int, int * ); + void astSetFitsF_( AstFitsChan *, const char *, double, const char *, int, int * ); + void astSetFitsI_( AstFitsChan *, const char *, int, const char *, int, int * ); + void astSetFitsL_( AstFitsChan *, const char *, int, const char *, int, int * ); + void astSetFitsS_( AstFitsChan *, const char *, const char *, const char *, int, int * ); + void astSetFitsU_( AstFitsChan *, const char *, const char *, int, int * ); + + void astTableSource_( AstFitsChan *, + void (*)( AstFitsChan *, const char *, int, int, int * ), + int * ); + + + +# if defined(astCLASS) || defined(astFORTRAN77) /* Protected or F77 interface */ + void astSetTableSource_( AstFitsChan *, + void (*)( void ), + void (*)( void (*)( void ), + AstFitsChan *, const char *, int, + int, int * ), + int * ); + +#endif + +# if defined(astCLASS) /* Protected */ + + int astFitsEof_( AstFitsChan *, int * ); + int astFitsGetCom_( AstFitsChan *, const char *, char **, int * ); + void astSetFitsCom_( AstFitsChan *, const char *, const char *, int, int * ); + + int astKeyFields_( AstFitsChan *, const char *, int, int *, int *, int * ); + + int astGetCard_( AstFitsChan *, int * ); + int astTestCard_( AstFitsChan *, int * ); + void astSetCard_( AstFitsChan *, int, int * ); + void astClearCard_( AstFitsChan *, int * ); + + int astGetDefB1950_( AstFitsChan *, int * ); + int astTestDefB1950_( AstFitsChan *, int * ); + void astSetDefB1950_( AstFitsChan *, int, int * ); + void astClearDefB1950_( AstFitsChan *, int * ); + + int astGetTabOK_( AstFitsChan *, int * ); + int astTestTabOK_( AstFitsChan *, int * ); + void astSetTabOK_( AstFitsChan *, int, int * ); + void astClearTabOK_( AstFitsChan *, int * ); + + int astGetCDMatrix_( AstFitsChan *, int * ); + int astTestCDMatrix_( AstFitsChan *, int * ); + void astSetCDMatrix_( AstFitsChan *, int, int * ); + void astClearCDMatrix_( AstFitsChan *, int * ); + + int astGetPolyTan_( AstFitsChan *, int * ); + int astTestPolyTan_( AstFitsChan *, int * ); + void astSetPolyTan_( AstFitsChan *, int, int * ); + void astClearPolyTan_( AstFitsChan *, int * ); + + int astGetCarLin_( AstFitsChan *, int * ); + int astTestCarLin_( AstFitsChan *, int * ); + void astSetCarLin_( AstFitsChan *, int, int * ); + void astClearCarLin_( AstFitsChan *, int * ); + + int astGetIwc_( AstFitsChan *, int * ); + int astTestIwc_( AstFitsChan *, int * ); + void astSetIwc_( AstFitsChan *, int, int * ); + void astClearIwc_( AstFitsChan *, int * ); + + int astGetClean_( AstFitsChan *, int * ); + int astTestClean_( AstFitsChan *, int * ); + void astSetClean_( AstFitsChan *, int, int * ); + void astClearClean_( AstFitsChan *, int * ); + + int astGetFitsDigits_( AstFitsChan *, int * ); + int astTestFitsDigits_( AstFitsChan *, int * ); + void astSetFitsDigits_( AstFitsChan *, int, int * ); + void astClearFitsDigits_( AstFitsChan *, int * ); + + const char *astGetFitsAxisOrder_( AstFitsChan *, int * ); + int astTestFitsAxisOrder_( AstFitsChan *, int * ); + void astSetFitsAxisOrder_( AstFitsChan *, const char *, int * ); + void astClearFitsAxisOrder_( AstFitsChan *, int * ); + + const char *astGetAllWarnings_( AstFitsChan *, int * ); + + const char *astGetWarnings_( AstFitsChan *, int * ); + int astTestWarnings_( AstFitsChan *, int * ); + void astClearWarnings_( AstFitsChan *, int * ); + void astSetWarnings_( AstFitsChan *, const char *, int * ); + + int astGetNcard_( AstFitsChan *, int * ); + + int astGetCardType_( AstFitsChan *, int * ); + const char *astGetCardName_( AstFitsChan *, int * ); + const char *astGetCardComm_( AstFitsChan *, int * ); + + int astGetNkey_( AstFitsChan *, int * ); + + int astGetEncoding_( AstFitsChan *, int * ); + int astTestEncoding_( AstFitsChan *, int * ); + void astSetEncoding_( AstFitsChan *, int, int * ); + void astClearEncoding_( AstFitsChan *, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckFitsChan(this) astINVOKE_CHECK(FitsChan,this,0) +#define astVerifyFitsChan(this) astINVOKE_CHECK(FitsChan,this,1) + +/* Test class membership. */ +#define astIsAFitsChan(this) astINVOKE_ISA(FitsChan,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astFitsChan astINVOKE(F,astFitsChan_) +#else +#define astFitsChan astINVOKE(F,astFitsChanId_) +#define astFitsChanFor astINVOKE(F,astFitsChanForId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitFitsChan(mem,size,init,vtab,name,source,sourcewrap,sink,sinkwrap) \ +astINVOKE(O,astInitFitsChan_(mem,size,init,vtab,name,source,sourcewrap,sink,sinkwrap,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitFitsChanVtab(vtab,name) astINVOKE(V,astInitFitsChanVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadFitsChan(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadFitsChan_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + + +/* More include files. */ +/* =================== */ +/* The interface to the FitsTable class must be included here (after the + type definitions for the FitsChan class) because "fitstable.h" itself + includes this file ("fitschan.h"), although "fitschan.h" refers to the + AstFitsTable structure above. This seems a little strange at first, + but is simply analogous to making a forward reference to a + structure type when recursively defining a normal C structure + (except that here the definitions happen to be in separate include + files). */ +#include "fitstable.h" + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckFitsChan to validate FitsChan pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astPutFits(this,card,overwrite) \ +astINVOKE(V,astPutFits_(astCheckFitsChan(this),card,overwrite,STATUS_PTR)) + +#define astPutCards(this,cards) \ +astINVOKE(V,astPutCards_(astCheckFitsChan(this),cards,STATUS_PTR)) + +#define astDelFits(this) \ +astINVOKE(V,astDelFits_(astCheckFitsChan(this),STATUS_PTR)) + +#define astPurgeWCS(this) \ +astINVOKE(V,astPurgeWCS_(astCheckFitsChan(this),STATUS_PTR)) + +#define astGetTables(this) \ +astINVOKE(O,astGetTables_(astCheckFitsChan(this),STATUS_PTR)) + +#define astPutTable(this,table,extnam) \ +astINVOKE(V,astPutTable_(astCheckFitsChan(this),astCheckFitsTable(table),extnam,STATUS_PTR)) + +#define astPutTables(this,tables) \ +astINVOKE(V,astPutTables_(astCheckFitsChan(this),astCheckKeyMap(tables),STATUS_PTR)) + +#define astRemoveTables(this,key) \ +astINVOKE(V,astRemoveTables_(astCheckFitsChan(this),key,STATUS_PTR)) + +#define astRetainFits(this) \ +astINVOKE(V,astRetainFits_(astCheckFitsChan(this),STATUS_PTR)) + +#define astFindFits( this, name, card, inc ) \ +astINVOKE(V,astFindFits_(astCheckFitsChan(this),name,card,inc,STATUS_PTR)) + +#define astSetFitsI(this,name,value,comment,overwrite) \ +astINVOKE(V,astSetFitsI_(astCheckFitsChan(this),name,value,comment,overwrite,STATUS_PTR)) + +#define astSetFitsF(this,name,value,comment,overwrite) \ +astINVOKE(V,astSetFitsF_(astCheckFitsChan(this),name,value,comment,overwrite,STATUS_PTR)) + +#define astSetFitsS(this,name,value,comment,overwrite) \ +astINVOKE(V,astSetFitsS_(astCheckFitsChan(this),name,value,comment,overwrite,STATUS_PTR)) + +#define astSetFitsCN(this,name,value,comment,overwrite) \ +astINVOKE(V,astSetFitsCN_(astCheckFitsChan(this),name,value,comment,overwrite,STATUS_PTR)) + +#define astSetFitsCI(this,name,value,comment,overwrite) \ +astINVOKE(V,astSetFitsCI_(astCheckFitsChan(this),name,value,comment,overwrite,STATUS_PTR)) + +#define astSetFitsCF(this,name,value,comment,overwrite) \ +astINVOKE(V,astSetFitsCF_(astCheckFitsChan(this),name,value,comment,overwrite,STATUS_PTR)) + +#define astSetFitsL(this,name,value,comment,overwrite) \ +astINVOKE(V,astSetFitsL_(astCheckFitsChan(this),name,value,comment,overwrite,STATUS_PTR)) + +#define astSetFitsU(this,name,comment,overwrite) \ +astINVOKE(V,astSetFitsU_(astCheckFitsChan(this),name,comment,overwrite,STATUS_PTR)) + +#define astSetFitsCM(this,comment,overwrite) \ +astINVOKE(V,astSetFitsCM_(astCheckFitsChan(this),comment,overwrite,STATUS_PTR)) + +#define astGetFitsCF(this,name,value) \ +astINVOKE(V,astGetFitsCF_(astCheckFitsChan(this),name,value,STATUS_PTR)) + +#define astGetFitsCI(this,name,value) \ +astINVOKE(V,astGetFitsCI_(astCheckFitsChan(this),name,value,STATUS_PTR)) + +#define astGetFitsF(this,name,value) \ +astINVOKE(V,astGetFitsF_(astCheckFitsChan(this),name,value,STATUS_PTR)) + +#define astGetFitsI(this,name,value) \ +astINVOKE(V,astGetFitsI_(astCheckFitsChan(this),name,value,STATUS_PTR)) + +#define astGetFitsL(this,name,value) \ +astINVOKE(V,astGetFitsL_(astCheckFitsChan(this),name,value,STATUS_PTR)) + +#define astTestFits(this,name,there) \ +astINVOKE(V,astTestFits_(astCheckFitsChan(this),name,there,STATUS_PTR)) + +#define astGetFitsS(this,name,value) \ +astINVOKE(V,astGetFitsS_(astCheckFitsChan(this),name,value,STATUS_PTR)) + +#define astGetFitsCN(this,name,value) \ +astINVOKE(V,astGetFitsCN_(astCheckFitsChan(this),name,value,STATUS_PTR)) + +#define astReadFits(this) \ +astINVOKE(V,astReadFits_(astCheckFitsChan(this),STATUS_PTR)) + +#define astWriteFits(this) \ +astINVOKE(V,astWriteFits_(astCheckFitsChan(this),STATUS_PTR)) + +#define astEmptyFits(this) \ +astINVOKE(V,astEmptyFits_(astCheckFitsChan(this),STATUS_PTR)) + +#define astShowFits(this) \ +astINVOKE(V,astShowFits_(astCheckFitsChan(this),STATUS_PTR)) + +#define astTableSource(this,tabsource) \ +astINVOKE(V,astTableSource_(astCheckFitsChan(this),tabsource,STATUS_PTR)) + + +#if defined(astCLASS) || defined(astFORTRAN77) /* Protected or F77 interface */ + +#define astSetTableSource(this,tabsource,tabsource_wrap) \ +astINVOKE(V,astSetTableSource_(astCheckFitsChan(this),tabsource,tabsource_wrap,STATUS_PTR)) + +#endif + + +#if defined(astCLASS) /* Protected */ + +#define astFitsEof(this) \ +astINVOKE(V,astFitsEof_(astCheckFitsChan(this),STATUS_PTR)) + +#define astFitsGetCom(this,name,comment) \ +astINVOKE(V,astFitsGetCom_(astCheckFitsChan(this),name,comment,STATUS_PTR)) + +#define astSetFitsCom(this,name,comment,overwrite) \ +astINVOKE(V,astSetFitsCom_(astCheckFitsChan(this),name,comment,overwrite,STATUS_PTR)) + +#define astKeyFields(this,filter,maxfld,ubnd,lbnd) \ +astINVOKE(V,astKeyFields_(astCheckFitsChan(this),filter,maxfld,ubnd,lbnd,STATUS_PTR)) + +#define astClearCard(this) \ +astINVOKE(V,astClearCard_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetCard(this) \ +astINVOKE(V,astGetCard_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetCard(this,card) \ +astINVOKE(V,astSetCard_(astCheckFitsChan(this),card,STATUS_PTR)) +#define astTestCard(this) \ +astINVOKE(V,astTestCard_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearDefB1950(this) \ +astINVOKE(V,astClearDefB1950_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetDefB1950(this) \ +astINVOKE(V,astGetDefB1950_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetDefB1950(this,defb950) \ +astINVOKE(V,astSetDefB1950_(astCheckFitsChan(this),defb950,STATUS_PTR)) +#define astTestDefB1950(this) \ +astINVOKE(V,astTestDefB1950_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearTabOK(this) \ +astINVOKE(V,astClearTabOK_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetTabOK(this) \ +astINVOKE(V,astGetTabOK_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetTabOK(this,tabok) \ +astINVOKE(V,astSetTabOK_(astCheckFitsChan(this),tabok,STATUS_PTR)) +#define astTestTabOK(this) \ +astINVOKE(V,astTestTabOK_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearCDMatrix(this) \ +astINVOKE(V,astClearCDMatrix_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetCDMatrix(this) \ +astINVOKE(V,astGetCDMatrix_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetCDMatrix(this,cdmatrix) \ +astINVOKE(V,astSetCDMatrix_(astCheckFitsChan(this),cdmatrix,STATUS_PTR)) +#define astTestCDMatrix(this) \ +astINVOKE(V,astTestCDMatrix_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearPolyTan(this) \ +astINVOKE(V,astClearPolyTan_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetPolyTan(this) \ +astINVOKE(V,astGetPolyTan_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetPolyTan(this,value) \ +astINVOKE(V,astSetPolyTan_(astCheckFitsChan(this),value,STATUS_PTR)) +#define astTestPolyTan(this) \ +astINVOKE(V,astTestPolyTan_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearCarLin(this) \ +astINVOKE(V,astClearCarLin_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetCarLin(this) \ +astINVOKE(V,astGetCarLin_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetCarLin(this,carln) \ +astINVOKE(V,astSetCarLin_(astCheckFitsChan(this),carln,STATUS_PTR)) +#define astTestCarLin(this) \ +astINVOKE(V,astTestCarLin_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearClean(this) \ +astINVOKE(V,astClearClean_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetClean(this) \ +astINVOKE(V,astGetClean_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetClean(this,value) \ +astINVOKE(V,astSetClean_(astCheckFitsChan(this),value,STATUS_PTR)) +#define astTestClean(this) \ +astINVOKE(V,astTestClean_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearFitsDigits(this) \ +astINVOKE(V,astClearFitsDigits_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetFitsDigits(this) \ +astINVOKE(V,astGetFitsDigits_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetFitsDigits(this,fitsdigits) \ +astINVOKE(V,astSetFitsDigits_(astCheckFitsChan(this),fitsdigits,STATUS_PTR)) +#define astTestFitsDigits(this) \ +astINVOKE(V,astTestFitsDigits_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearFitsAxisOrder(this) \ +astINVOKE(V,astClearFitsAxisOrder_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetFitsAxisOrder(this) \ +astINVOKE(V,astGetFitsAxisOrder_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetFitsAxisOrder(this,fitsaxisorder) \ +astINVOKE(V,astSetFitsAxisOrder_(astCheckFitsChan(this),fitsaxisorder,STATUS_PTR)) +#define astTestFitsAxisOrder(this) \ +astINVOKE(V,astTestFitsAxisOrder_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearWarnings(this) \ +astINVOKE(V,astClearWarnings_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetWarnings(this) \ +astINVOKE(V,astGetWarnings_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetWarnings(this,warnings) \ +astINVOKE(V,astSetWarnings_(astCheckFitsChan(this),warnings,STATUS_PTR)) +#define astTestWarnings(this) \ +astINVOKE(V,astTestWarnings_(astCheckFitsChan(this),STATUS_PTR)) + +#define astGetAllWarnings(this) \ +astINVOKE(V,astGetAllWarnings_(astCheckFitsChan(this),STATUS_PTR)) + +#define astGetCardType(this) \ +astINVOKE(V,astGetCardType_(astCheckFitsChan(this),STATUS_PTR)) + +#define astGetCardName(this) \ +astINVOKE(V,astGetCardName_(astCheckFitsChan(this),STATUS_PTR)) + +#define astGetCardComm(this) \ +astINVOKE(V,astGetCardComm_(astCheckFitsChan(this),STATUS_PTR)) + +#define astGetNcard(this) \ +astINVOKE(V,astGetNcard_(astCheckFitsChan(this),STATUS_PTR)) + +#define astGetNkey(this) \ +astINVOKE(V,astGetNkey_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearEncoding(this) \ +astINVOKE(V,astClearEncoding_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetEncoding(this) \ +astINVOKE(V,astGetEncoding_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetEncoding(this,encoding) \ +astINVOKE(V,astSetEncoding_(astCheckFitsChan(this),encoding,STATUS_PTR)) +#define astTestEncoding(this) \ +astINVOKE(V,astTestEncoding_(astCheckFitsChan(this),STATUS_PTR)) + +#define astClearIwc(this) \ +astINVOKE(V,astClearIwc_(astCheckFitsChan(this),STATUS_PTR)) +#define astGetIwc(this) \ +astINVOKE(V,astGetIwc_(astCheckFitsChan(this),STATUS_PTR)) +#define astSetIwc(this,iwc) \ +astINVOKE(V,astSetIwc_(astCheckFitsChan(this),iwc,STATUS_PTR)) +#define astTestIwc(this) \ +astINVOKE(V,astTestIwc_(astCheckFitsChan(this),STATUS_PTR)) + +#endif + +#endif + + + + + diff --git a/fitstable.c b/fitstable.c new file mode 100644 index 0000000..148431c --- /dev/null +++ b/fitstable.c @@ -0,0 +1,3006 @@ +/* +*class++ +* Name: +* FitsTable + +* Purpose: +* A representation of a FITS binary table. + +* Constructor Function: +c astFitsTable +f AST_FITSTABLE + +* Description: +* The FitsTable class is a representation of a FITS binary table. It +* inherits from the Table class. The parent Table is used to hold the +* binary data of the main table, and a FitsChan (encapsulated within +* the FitsTable) is used to hold the FITS header. +* +* Note - it is not recommended to use the FitsTable class to store +* very large tables. +* +* FitsTables are primarily geared towards the needs of the "-TAB" +* algorithm defined in FITS-WCS paper 2, and so do not support all +* features of FITS binary tables. In particularly, they do not +* provide any equivalent to the following features of FITS binary +* tables: "heap" data (i.e. binary data following the main table), +* columns holding complex values, columns holding variable length +* arrays, scaled columns, column formats, columns holding bit values, +* 8-byte integer values or logical values. + +* Inheritance: +* The FitsTable class inherits from the Table class. + +* Attributes: +* The FitsTable class does not define any new attributes beyond +* those which are applicable to all Tables. + +* Functions: +c In addition to those functions applicable to all Tables, the +c following functions may also be applied to all FitsTables: +f In addition to those routines applicable to all Tables, the +f following routines may also be applied to all FitsTables: +* +c - astColumnNull: Get/set the null value for a column of a FitsTable +c - astColumnSize: Get number of bytes needed to hold a full column of data +c - astGetColumnData: Retrieve all the data values stored in a column +c - astGetTableHeader: Get the FITS headers from a FitsTable +c - astPutColumnData: Store data values in a column +c - astPutTableHeader: Store FITS headers within a FitsTable +f - AST_COLUMNNULL: Get/set the null value for a column of a FitsTable +f - AST_COLUMNSIZE: Get number of bytes needed to hold a full column of data +f - AST_GETCOLUMNDATA: Retrieve all the data values stored in a column +f - AST_GETTABLEHEADER: Get the FITS headers from a FitsTable +f - AST_PUTCOLUMNDATA: Store data values in a column +f - AST_PUTTABLEHEADER: Store FITS headers within a FitsTable + +* Copyright: +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 25-NOV-2010 (DSB): +* Original version. +* 2-OCT-2012 (DSB): +* Check for Infs as well as NaNs. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS FitsTable + +/* The KeyMap key use to store the null value for a column. */ +#define NULLKEY "Null" + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "table.h" /* Tables (parent class) */ +#include "channel.h" /* I/O channels */ +#include "pointset.h" /* For astCheckNaN(F) functions */ +#include "fitstable.h" /* Interface definition for this class */ + + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include + + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static void (* parent_addcolumn)( AstTable *, const char *, int, int, int *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(FitsTable) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(FitsTable,Class_Init) +#define class_vtab astGLOBAL(FitsTable,Class_Vtab) + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstFitsTableVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstFitsTable *astFitsTableId_( void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstFitsChan *GetTableHeader( AstFitsTable *, int * ); +static char *MakeKey( const char *, int, char *, int, int * ); +static int ColumnNull( AstFitsTable *, const char *, int, int, int *, int *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); +static size_t ColumnSize( AstFitsTable *, const char *, int * ); +static void AddColumn( AstTable *, const char *, int, int, int *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void CopyStrings( int, size_t, const char *, char *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GenerateColumns( AstFitsTable *, AstFitsChan *, int * ); +static void GetColumnData( AstFitsTable *, const char *, float, double, size_t, void *, int *, int * ); +static void PurgeHeader( AstFitsTable *, int * ); +static void PutColumnData( AstFitsTable *, const char *, int, size_t, void *, int * ); +static void PutTableHeader( AstFitsTable *, AstFitsChan *, int * ); +static void UpdateHeader( AstFitsTable *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ + +static void AddColumn( AstTable *this, const char *name, int type, + int ndim, int *dims, const char *unit, int *status ) { +/* +* Name: +* AddColumn + +* Purpose: +* Add a new column definition to a FitsTable. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void AddColumn( AstTable *this, const char *name, int type, int ndim, +* int *dims, const char *unit, int *status ) + +* Class Membership: +* FitsTable member function (over-rides the astAddColumn method +* inherited from the Table class). + +* Description: +* Adds the definition of a new column to the supplied table. Initially, +* the column contains a null value for every row. Values may be added +* subsequently using the methods of the KeyMap class. +* +* The FitsTable class extend the method inherited from the parent +* Table class in order to prevent the addition of columns with properties +* not supported by FITS binary tables. + +* Parameters: +* this +* Pointer to the Table. +* name +* The column name. Trailing spaces are ignored (all other spaces +* are significant). The supplied string is converted to upper case. +* type +* The data type associated with the column. One of AST__INTTYPE +* (for integer), AST__SINTTYPE (for short int), AST__BYTETYPE (for +* unsigned bytes - i.e. unsigned chars), AST__DOUBLETYPE (for double +* precision floating point), AST__FLOATTYPE (for single precision +* floating point), AST__STRINGTYPE (for character string). Note, +* pointers and undefined values cannot be stored in a FitsTable +* column. +* ndim +* The number of dimensions spanned by the values stored in a single +* cell of the column. Zero if the column holds scalar values. +* dims +* An array holding the the lengths of each of the axes spanned by +* the values stored in a single cell of the column. Ignored if the +* column holds scalara values. +* unit +* A string specifying the units of the column. Supply a blank +* string if the column is unitless. +* status +* Pointer to the inherited status. + +* Notes: +* - This function returns without action if a column already exists in +* the Table with the supplied name and properties. However an error is +* reported if any of the properties differ. +*/ + +/* Local Variables: */ + const char *text; /* Data type string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Report an error if the supplied data type is supported by the Table + class but not by the FitsTable class. */ + if( type == AST__OBJECTTYPE ) { + text = "Object pointer"; + + } else if( type == AST__POINTERTYPE ) { + text = "generic pointer"; + + } else if( type == AST__UNDEFTYPE ) { + text = "undefined type"; + + } else { + text = NULL; + } + + if( text ) { + astError( AST__NAXIN, "astAddColumn(%s): Bad data type (%s) supplied " + "for new column %s. The %s class does not support %s " + "columns.", status, astGetClass( this ), text, name, + astGetClass( this ), text ); + +/* Otherwise, invoke the parent method to add the column. */ + } else { + (*parent_addcolumn)( this, name, type, ndim, dims, unit, status ); + } +} + +static int ColumnNull( AstFitsTable *this, const char *column, int set, + int newval, int *wasset, int *hasnull, int *status ){ +/* +*++ +* Name: +c astColumnNull +f AST_COLUMNNULL + +* Purpose: +* Get or set the null value for an integer column of a FITS table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c int astColumnNull( AstFitsTable *this, const char *column, int set, +c int newval, int *wasset, int *hasnull ) +f RESULT = AST_COLUMNNULL( THIS, COLUMN, SET, NEWVAL, WASSET, HASNULL, +f STATUS ) + +* Class Membership: +* FitsTable method. + +* Description: +* This function allows a null value to be stored with a named +* integer-valued column in a FitsTable. The supplied null value is +* assigned to the TNULLn keyword in the FITS header associated with +* the FitsTable. A value in the named column is then considered to be +* null if 1) it equals the null value supplied to this function, or +* 2) no value has yet been stored in the cell. +* +* As well as setting a new null value, this function also returns the +* previous null value. If no null value has been set previously, a +* default value will be returned. This default will be an integer +* value that does not currently occur anywhere within the named column. +* If no such value can be found, what happens depends on whether the +* column contains any cells in which no values have yet been stored. +* If so, an error will be reported. Otherwise (i.e. if there are no +* null values in the column), an arbitrary value of zero will be +* returned as the function value, and no TNULLn keyword will be +* stored in the FITS header. +* +* A flag is returned indicating if the returned null value was set +* explicitly by a previous call to this function, or is a default +* value. +* +* A second flag is returned indicating if the named column contains +* any null values (i.e. values equal to the supplied null value, or +* cells to which no value has yet been assigned). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c column +f COLUMN = CHARACTER * ( * ) (Given) +* The character string holding the name of the column. Trailing +* spaces are ignored. +c set +f SET = LOGICAL (Given) +c If non-zero, the value supplied for parameter "newval" +f If .TRUE., the value supplied for argument NEWVAL +* will be stored as the current null value, replacing any value +* set by a previous call to this function. +c If zero, the value supplied for parameter "newval" +f If .FALSE., the value supplied for argument NEWVAL +* is ignored and the current null value is left unchanged. +c newval +f NEWVAL = INTEGER (Given) +* The new null value to use. Ignored if +c "set" is zero. +f SET is .FALSE. +* An error will be reported if the supplied value is outside the +* range of values that can be stored in the integer data type +* associated with the column. +c wasset +f WASSET = LOGICAL (Returned) +c Pointer to an int that will be returned non-zero +f .TRUE. will be returned +* if the returned null value was set previously via an +* earlier invocation of this function. +c Zero +f .FALSE. +* is returned otherwise. If the named column does not exist, or an +* error occurs, a value of +c zero is returned. +f .FALSE. is returned. +c hasnull +f HASNULL = LOGICAL (Returned) +c Pointer to an int that will be returned non-zero +f .TRUE. will be returned +* if and only if the named column currently contains any values +* equal to the null value on exit (i.e. +c "newval" if "set" is non-zero, +f NEWVAL if SET is .TRUE. +* or the returned function value otherwise), or contains any empty +* cells. If the named column does not exist, or an error occurs, a +* value of +c zero is returned. +f .FALSE. is returned. +c If a NULL pointer is supplied for "hasnull", no check on the +c presence of null values will be performed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astColumnNull() +f AST_COLUMNNULL = INTEGER +* The null value that was in use on entry to this function. If a +* null value has been set by a previous invocation of this +* function, it will be returned. Otherwise, if +c "set" is non-zero, the supplied "newval" +f SET is .TRUE., the supplied NEWVAL +* value is returned. Otherwise, a default value is chosen (if +* possible) that does not currently occur in the named column. If +* all available values are in use in the column, an error is +* reported if and only if the column contains any empty cells. +* Otherwise, a value of zero is returned. A value of zero is also +* returned if the named column does not exist, or an error occurs. + +* Notes: +* - The FITS binary table definition allows only integer-valued +* columns to have an associated null value. This routine will return +* without action if the column is not integer-valued. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *col_km; /* KeyMap holding named column definition */ + AstKeyMap *cols; /* KeyMap holding all column definitions */ + char key[ AST__MXCOLKEYLEN + 1 ]; /* Current cell key string */ + int *cell; /* Pointer to array of cell values */ + int foundhi; /* Has an occurrence of "nullhi" been found yet? */ + int foundlo; /* Has an occurrence of "nulllo" been found yet? */ + int gotresult; /* Has a usable value been put into "result"? */ + int idim; /* Index of current axis in each column's value */ + int iel; /* Index of current element within cell value */ + int imax; /* Maximum storable value */ + int imin; /* Minimum storable value */ + int irow; /* Index of current row in table */ + int ndim; /* Number of axes in each column's value */ + int nel; /* Total number of values in each cell */ + int nrow; /* Number of rows in table */ + int null; /* The null value on exit */ + int nullfound; /* Has a null value been found in the column yet? */ + int nullhi; /* Higher candidate default null value */ + int nulllo; /* Lower candidate default null value */ + int result; /* Returned value */ + int type; /* Column data type */ + +/* Initialise */ + result = 0; + *wasset = 0; + if( hasnull ) *hasnull = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Store the max and min integer values that can be store din the column + data type. */ + type = astGetColumnType( this, column ); + if( type == AST__BYTETYPE ) { + imin = 0; + imax = UCHAR_MAX; + + } else if( type == AST__SINTTYPE ) { + imin = SHRT_MIN; + imax = SHRT_MAX; + + } else if( type == AST__INTTYPE ) { + imin = INT_MIN; + imax = INT_MAX; + + } else { + imax = 0; + imin = 0; + } + +/* Check the named column contains integer values of any length. */ + if( imax > imin ) { + +/* Get the KeyMap holding information about all columns. */ + cols = astColumnProps( this ); + +/* Get the KeyMap holding information about the named column. */ + if( astMapGet0A( cols, column, &col_km ) ) { + +/* If the column definition already includes a null value, put it into + "result". Also store the "*wasset" flag that indicates if the returned + null value is a default value or not. */ + *wasset = astMapGet0I( col_km, NULLKEY, &result ); + +/* If a new null value is to be established... */ + if( set ) { + +/* If there was no previously set null value, return the new null value + as the function value. */ + if( ! *wasset ) result = newval; + +/* Indicate we now know what the returned function value is. */ + gotresult = 1; + +/* Save the null value that will be in use when this function exits. */ + null = newval; + +/* Check the supplied value is in range. If so store it in the column + keymap. Otherwise report an error. */ + if( null >= imin && null <= imax ) { + astMapPut0I( col_km, NULLKEY, null, NULL ); + + } else if( astOK ) { + astError( AST__BADNULL, "astColumnNull(%s): Supplied null " + "value (%d) is outside the range of integers " + "that can be stored in column '%s'.", status, + astGetClass( this ), newval, column ); + } + +/* If no new null value was supplied, the null value on exit will be the + previously set value, if any. */ + } else { + null = result; + gotresult = *wasset; + } + +/* The rest is only needed if we need to find a default result value, or if + we need to check if there are any null values in the table. */ + if( !gotresult || hasnull ) { + +/* Get the total number of values in each cell of the column. */ + nel = astGetColumnLength( this, column ); + +/* Allocate memory to hold the values in a single cell of the column, + stored as ints. */ + cell = astMalloc( nel*sizeof( int ) ); + +/* No null values found yet. */ + nullfound = 0; + +/* On the first pass round the following loop, we search for occurrences + of the highest and lowest integer values allowed in the column. If no + such occurrences are found we use one or the other as the default null + value. If occurrences of both of these values are found, we change the + values and start the search again. */ + nullhi = imax; + nulllo = imin; + foundlo = 0; + foundhi = 0; + +/* Loop round all rows in the Table. */ + nrow = astGetNrow( this ); + for( irow = 1; irow <= nrow && astOK; irow++ ) { + +/* Format the cell name. */ + (void) MakeKey( column, irow, key, AST__MXCOLKEYLEN + 1, + status ); + +/* Attempt to get the values in the cell */ + if( astMapGet1I( this, key, nel, &nel, cell ) ) { + +/* Get the number of dimensions. */ + ndim = astGetColumnNdim( this, column ); + +/* If we know what the null value is on exit, check the cell for such null + values (but only if the caller want s to know). Skip this check after the + first null is found. */ + if( gotresult ) { + if( ! nullfound ) { + for( idim = 0; idim < ndim; idim++ ) { + if( cell[ idim ] == null ) { + nullfound = 1; + break; + } + } + } + +/* If we do not yet know what the returned value is, we try to find an + integer value within the allowed data range that is not currently used in + the column. For the moment, this is a no-brain algorithm that will + become untenable for large tables. Need to fix it when it is apparent + that it is causing a problem. */ + } else if( nulllo <= nullhi ) { + +/* See if the current cell contains any occurrences of either of the + two currently nominated null values. Is so, increment the matched + nominated null value, and start again at row 1. */ + for( iel = 0; iel < nel; iel++ ) { + + if( cell[ iel ] == nulllo ) { + foundlo = 1; + } else if( cell[ iel ] == nullhi ) { + foundhi = 1; + } + + if( foundlo && foundhi ) { + nullhi--; + nulllo++; + irow = 0; + foundlo = 0; + foundhi = 0; + continue; + } + + } + } + +/* If the column does not contain anything in the current cell, we know + there is at least one null value in the column, so store a non-zero value + in the returned flag. */ + } else { + nullfound = 1; + } + +/* If we now have a value for the result and know that there are nulls in + the column, we can leave the loop. */ + if( gotresult && nullfound ) break; + } + +/* Return the "null found" flag if required. */ + if( hasnull ) *hasnull = nullfound; + +/* If we have not yet stored the default null value to be returned as the + function value, do so now. If no unused value could be found, and + there are missing cells in the table, report an error. */ + if( !gotresult ) { + if( !foundhi ) { + result = nullhi; + + } else if( !foundlo ) { + result = nulllo; + + } else if( nullfound && astOK ) { + astError( AST__BADNULL, "astColumnNull(%s): Cannot find " + "an unused value to use as the null value in " + "column '%s'.", status, astGetClass( this ), + column ); + } + } + +/* Free resources */ + cell = astFree( cell ); + } + col_km = astAnnul( col_km ); + } + cols = astAnnul( cols ); + } + +/* Return null values if an error occurred. */ + if( !astOK ) { + result = 0; + *wasset = 0; + if( hasnull ) *hasnull = 0; + } + +/* Return the result. */ + return result; +} + +static size_t ColumnSize( AstFitsTable *this, const char *column, int *status ){ +/* +*++ +* Name: +c astColumnSize +f AST_COLUMNSIZE + +* Purpose: +* Get the number of bytes needed to hold a full column of data. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c size_t astColumnSize( AstFitsTable *this, const char *column, +c int *hasnull ) +f RESULT = AST_COLUMNSIZE( THIS, COLUMN, STATUS ) + +* Class Membership: +* FitsTable method. + +* Description: +* This function returns the number of bytes of memory that must be +* allocated prior to retrieving the data from a column using +c astGetColumnData. +f AST_GETCOLUMNDATA. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c column +f COLUMN = CHARACTER * ( * ) (Given) +* The character string holding the name of the column. Trailing +* spaces are ignored. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astColumnNull() +f AST_COLUMNNULL = INTEGER +* The number of bytes required to store the column data. + +* Notes: +* - An error will be reported if the named column does not exist in +* the FitsTable. +* - Zero will be returned as the function value in an error occurs. + +*-- +*/ + +/* Local Variables: */ + size_t result; /* Returned value */ + int type; /* Column data type */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Find the number of bytes needed to hold a single element of the value + in a column cell. */ + type = astGetColumnType( this, column ); + if( type == AST__INTTYPE ) { + result = sizeof( int ); + + } else if( type == AST__DOUBLETYPE ){ + result = sizeof( double ); + + } else if( type == AST__STRINGTYPE ){ + result = astGetColumnLenC( this, column )*sizeof( char ); + + } else if( type == AST__FLOATTYPE ){ + result = sizeof( float ); + + } else if( type == AST__SINTTYPE ){ + result = sizeof( short int ); + + } else if( type == AST__BYTETYPE ){ + result = sizeof( char ); + + } else if( astOK ) { + astError( AST__INTER, "astColumnSize(%s): Unsupported column type " + "%d (internal AST programming error).", status, + astGetClass( this ), type ); + } + +/* Multiply it by the number of elements per value. */ + result *= astGetColumnLength( this, column ); + +/* Multiply it by the number of values per column (i.e. the number of rows). */ + result *= astGetNrow( this ); + +/* Return zero if an error occurred. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static void CopyStrings( int nval, size_t nb, const char *cbuf, char *pout, + int *status ){ +/* +* Name: +* CopyStrings + +* Purpose: +* Remove terminating nulls from an array of fixed-length strings. + +* Type: +* Private function. + +* Synopsis: +* void CopyStrings( int nval, size_t nb, const char *cbuf, char *pout, +* int *status ) + +* Description: +* This function copies null terminated strings from "cbuf" to "pout", +* removing the terminating nulls in the process. Thus each output string +* is one character shorter than the corresponding input string. + +* Parameters: +* nval +* The number of strings to copy. +* nb +* The maximum length of each string, excluding trailing null. +* cbuf +* The input array holding "nval" adjacent strings, each occupying +* ( nb + 1 ) characters (the last one is the trailing null). +* pout +* The output array to which "nval" adjacent strings are written, +* each occupying ( nb ) characters (i.e. no trailing null). +* status +* Pointer to inherited status. + +*/ + +/* Local Variables: */ + int i; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Copy the first "nb" characters of each string. */ + for( i = 0; i < nval; i++ ) { + memcpy( pout, cbuf, nb ); + +/* Increment the pointer to the start of the next output string. */ + pout += nb; + +/* Increment the pointer to the start of the next input string. */ + cbuf += nb + 1; + } + +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two FitsTables are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "fitstable.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* FitsTable member function (over-rides the astEqual protected +* method inherited from the astTable class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two FitsTables are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a FitsTable). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the FitsTables are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFitsTable *that; + AstFitsTable *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two FitsTable structures. */ + this = (AstFitsTable *) this_object; + that = (AstFitsTable *) that_object; + +/* Check the second object is a FitsTable. We know the first is a + FitsTable since we have arrived at this implementation of the virtual + function. */ + if( astIsAFitsTable( that ) ) { + +/* Check the FitsTables are equal when compared as Tables. */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Check the headers are equal. */ + result = astEqual( this->header, that->header ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void GenerateColumns( AstFitsTable *this, AstFitsChan *header, + int *status ) { +/* +* Name: +* GenerateColumns + +* Purpose: +* Add new column definitions to a FitsTable as defined by a FITS +* header. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void GenerateColumns( AstFitsTable *this, AstFitsChan *header, +* int *status ) + +* Class Membership: +* FitsTable member function + +* Description: +* For each binary table column defined in the supplied FITS header, +* this function adds an equivalent column to the FitsTable. + +* Parameters: +* this +* Pointer to the FitsTable. +* header +* Pointer to a FitsChan holding the column definitions. +* status +* Pointer to the inherited status. + +*/ + +/* Local Variables: */ + char *cval; + char *name; + char *p; + char *unit; + char buff[ 50 ]; + char code; + char keyword[ 20 ]; + double dval; + int *dims; + int icol; + int idim; + int ival; + int nc; + int ncol; + int ndim; + int nel; + int repeat; + int type; + int wasset; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise */ + type = AST__BADTYPE; + +/* Get the number of columns defined in the header. */ + if( !astGetFitsI( header, "TFIELDS", &ncol ) ) ncol = 0; + +/* Add a column definition to the FitsTable for each column in the header. */ + for( icol = 0; icol < ncol; icol++ ) { + +/* Get the TFORMi keyword that defines the column data type and shape from + the header. Report an error if it is missing. */ + sprintf( keyword, "TFORM%d", icol + 1 ); + if( !astGetFitsS( header, keyword, &cval ) && astOK ) { + astError( AST__NOFTS, "astFitsTable: Supplied FITS binary table " + "header does not contain the required keyword '%s'.", + status, keyword ); + } + +/* Extract the repeat count and data type code from the TFORM string. */ + if( sscanf( cval, "%d%n", &repeat, &nc ) == 0 ) { + repeat = 1; + nc = 0; + } else if( repeat < 0 && astOK ) { + astError( AST__BDFTS, "astFitsTable: Keyword '%s' in supplied FITS " + "binary table header has unsupported value '%s'.", status, + keyword, cval ); + } + code = cval[ nc ]; + +/* Get the corresponding KeyMap data type. Report an error if the FITS + data type is not supported by the KeyMap class. */ + if( code == 'B' ) { + type = AST__BYTETYPE; + + } else if( code == 'I' ) { + type = AST__SINTTYPE; + + } else if( code == 'J' ) { + type = AST__INTTYPE; + + } else if( code == 'D' ) { + type = AST__DOUBLETYPE; + + } else if( code == 'E' ) { + type = AST__FLOATTYPE; + + } else if( code == 'A' ) { + type = AST__STRINGTYPE; + + } else if( astOK ){ + astError( AST__BDFTS, "astFitsTable: Keyword '%s' in supplied FITS " + "binary table header has unsupported value '%s'.", status, + keyword, cval ); + } + +/* The TTYPEi keyword gives the column name. Create a column name based + on the index of the column. */ + sprintf( keyword, "TTYPE%d", icol + 1 ); + if( !astGetFitsS( header, keyword, &cval ) ) { + sprintf( buff, "FCOLUMN%d", icol + 1 ); + cval = buff; + } + name = astStore( NULL, cval, strlen( cval ) + 1 ); + +/* Column units. */ + sprintf( keyword, "TUNIT%d", icol + 1 ); + if( !astGetFitsS( header, keyword, &cval ) ) { + buff[ 0 ] = 0; + cval = buff; + } + unit = astStore( NULL, cval, strlen( cval ) + 1 ); + +/* Column shape is defined by the TDIMi keyword - in the form + "(i,j,k,...)". where i, j, k ... are the dimensions. If it is missing + then the field is assumed to be a 1D vector with the length specified by + the repeat count in the TFORMn keyword, or a scalar (if repeat cound + is one). */ + sprintf( keyword, "TDIM%d", icol + 1 ); + if( astGetFitsS( header, keyword, &cval ) ) { + +/* Count the commas in the keyword value. This equals one less than the + number of dimensions. */ + ndim = 1; + p = cval; + while( *p ) { + if( *(p++) == ',' ) ndim++; + } + +/* Allocate memory for the dimensions. */ + dims = astMalloc( ndim*sizeof( int ) ); + +/* Find each dimension and copy it into the above memory. Also find the + total number of elements (nel). */ + nel = 1; + idim = 0; + p = cval; + if( *p == '(' ) p++; + while( sscanf( p, "%d%n", dims + idim, &nc ) ) { + nel *= dims[ idim ]; + idim++; + p += nc; + if( *p == ',' ) p++; + } + +/* For strings, the first TDIM value gives the length of the string, so + reduce the number of dimensions by one. */ + if( type == AST__STRINGTYPE ) { + ndim--; + dims++; + } + + } else { + nel = repeat; + if( nel == 1 ) { + ndim = 0; + dims = NULL; + } else { + ndim = 1; + dims = astMalloc( sizeof( int ) ); + if( dims ) *dims = nel; + } + } + +/* Check the total number of elements equal the repeat count from the + TFORM keyword. */ + if( repeat != nel && astOK ) { + + sprintf( keyword, "TFORM%d", icol + 1 ); + astGetFitsS( header, keyword, &cval ); + strcpy( buff, cval ); + + sprintf( keyword, "TDIM%d", icol + 1 ); + if( !astGetFitsS( header, keyword, &cval ) ) cval = " "; + + astError( AST__BDFTS, "astFitsTable: Supplied FITS binary table " + "header contains inconsistent TFORM (%s) and TDIM (%s) " + "keywords for field %d.", status, buff, cval, icol + 1 ); + } + +/* Check any TSCALi value is 1.0 */ + sprintf( keyword, "TSCAL%d", icol + 1 ); + if( astGetFitsF( header, keyword, &dval ) && dval != 1.0 && astOK ) { + astError( AST__BDFTS, "astFitsTable: Supplied FITS binary table " + "header contains scaled columns which are not " + "supported by AST.", status ); + } + +/* Check any TZEROi value is 0.0 */ + sprintf( keyword, "TSCAL%d", icol + 1 ); + if( astGetFitsF( header, keyword, &dval ) && dval != 0.0 && astOK ) { + astError( AST__BDFTS, "astFitsTable: Supplied FITS binary table " + "header contains scaled columns which are not " + "supported by AST.", status ); + } + +/* Add the column to the table. */ + astAddColumn( this, name, type, ndim, dims, unit ); + +/* Set the null value, if present. */ + sprintf( keyword, "TNULL%d", icol + 1 ); + if( astGetFitsI( header, keyword, &ival ) ) { + (void) astColumnNull( this, name, 1, ival, &wasset, NULL ); + } + +/* Free resources. */ + dims = astFree( dims - ( ( type == AST__STRINGTYPE ) ? 1 : 0 ) ); + name = astFree( name ); + unit = astFree( unit ); + + } +} + +static void GetColumnData( AstFitsTable *this, const char *column, + float fnull, double dnull, size_t mxsize, + void *coldata, int *nelem, int *status ){ +/* +*++ +* Name: +c astGetColumnData +f AST_GETCOLUMNDATA + +* Purpose: +* Retrieve all the data values stored in a column. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astGetColumnData( AstFitsTable *this, const char *column, +c float fnull, double dnull, size_t mxsize, +c void *coldata, int *nelem ) +f CALL AST_GETCOLUMNDATA( THIS, COLUMN, RNULL, DNULL, MXSIZE, +f COLDATA, NELEM, STATUS ) + +* Class Membership: +* FitsTable method. + +* Description: +c This function +f This routine +* copies all data values from a named column into a supplied buffer + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsTable. +c column +f COLUMN = CHARACTER * ( * ) (Given) +* The character string holding the name of the column. Trailing +* spaces are ignored. +c fnull +f RNULL = REAL (Given) +* The value to return in +c "coldata" +f COLDATA +* for any cells for which no value has been stored in the +* FitsTable. Ignored if the column's data type is not +* AST__FLOATTYPE. Supplying +c AST__NANF +f AST__NANR +* will cause a single precision IEEE NaN value to be used. +c dnull +f DNULL = REAL (Given) +* The value to return in +c "coldata" +f COLDATA +* for any cells for which no value has been stored in the +* FitsTable. Ignored if the column's data type is not +* AST__DOUBLETYPE. Supplying AST__NAN will cause a double precision +* IEEE NaN value to be used. +c mxsize +f MXSIZE = INTEGER (Given) +* The size of the +c "coldata" +f COLDATA +* array, in bytes. The amount of memory needed to hold the data +* from a column may be determined using +c astColumnSize. +f AST_COLUMNSIZE. +* If the supplied array is too small to hold all the column data, +* trailing column values will be omitted from the returned array, +* but no error will be reported. +c coldata +f COLDATA( * ) = BYTE (Given) +c A pointer to an +f An +* area of memory in which to return the data +* values currently stored in the column. The values are stored in +* row order. If the column holds non-scalar values, the elements +* of each value are stored in "Fortran" order. No data type +* conversion is performed - the data type of each returned value +* is the data type associated with the column when the column was +* added to the table. If the column holds strings, the returned +* strings will be null terminated. Any excess room at the end of +* the array will be left unchanged. +c nelem +f NELEM = INTEGER (Return) +* The number of elements returned in the +c "coldata" +f COLDATA +* array. This is the product of the number of rows returned and +* the number of elements in each column value. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +f - The RNULL and DNULL arguments +c - The "fnull" and "dnull" parameters +* specify the value to be returned for any empty cells within columns +* holding floating point values. For columns holding integer values, +* the value returned for empty cells is the value returned by the +c astColumNull function. +f AST_COLUMNNULL functiom. +* For columns holding string values, the ASCII NULL character is returned +* for empty cells. +*-- +*/ + +/* Local Variables: */ + char *cbuf; /* Array of strings returned by astMapGet1C */ + char key[ AST__MXCOLKEYLEN + 1 ]; /* Current cell key string */ + int iel; /* Index of current element */ + int irow; /* Index of value being copied */ + int nel; /* No. of elements per value */ + int nrow; /* No. of values to copy */ + int nval; /* Number of values read from KeyMap entry */ + int ok; /* Was the value found in the KeyMap? */ + int type; /* Data type */ + int wasset; /* Was the integer null value set explicitly? */ + size_t nb; /* No. of bytes for a single element of a value */ + size_t nbv; /* No. of bytes per value */ + void *pnull; /* Pointer to a buffer holding a null value */ + void *pout; /* Pointer to next output element */ + +/* Initialise */ + *nelem = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise */ + nb = 0; + +/* Find the number of bytes needed to hold a single element of the value + in a column cell. */ + type = astGetColumnType( this, column ); + if( type == AST__INTTYPE ) { + nb = sizeof( int ); + + } else if( type == AST__DOUBLETYPE ){ + nb = sizeof( double ); + + } else if( type == AST__STRINGTYPE ){ + nb = astGetColumnLenC( this, column )*sizeof( char ); + + } else if( type == AST__FLOATTYPE ){ + nb = sizeof( float ); + + } else if( type == AST__SINTTYPE ){ + nb = sizeof( short int ); + + } else if( type == AST__BYTETYPE ){ + nb = sizeof( char ); + + } else if( astOK ) { + astError( AST__INTER, "astGetColumnData(%s): Unsupported column type " + "%d (internal AST programming error).", status, + astGetClass( this ), type ); + } + +/* Get the number of elements per value, and the number of bytes per value. */ + nel = astGetColumnLength( this, column ); + nbv = nb*nel; + +/* Initialise a pointer to the next element of the output array to write to. */ + pout = coldata; + +/* Get the number of rows in the table. */ + nrow = astGetNrow( this ); + +/* For string columns, the buffer returned by astMapGet1C will include a + null character at the end of each string. This is not required for the + fixed-length string format used by FITS binary tables, so for each row we + produce a copy of the string returned by astMapGet1C excluding the + trailing nulls. Allocate a buffer to receive the string returned by + astMapGet1C. */ + if( type == AST__STRINGTYPE ) { + cbuf = astMalloc( ( nb + 1 )*nel ); + } else { + cbuf = NULL; + } + +/* If required, substitute NaN values for the supplied null values. */ + fnull = astCheckNaNF( fnull ); + dnull = astCheckNaN( dnull ); + +/* Indicate we have not yet determined a null value for the column */ + pnull = NULL; + +/* Reduce the number of rows to be returned if the returned array is too + small to hold all rows. */ + if( mxsize < nbv*nrow ) nrow = mxsize/nbv; + +/* Loop round the returned rows rows. */ + for( irow = 1; irow <= nrow; irow++ ) { + +/* Format the cell name. */ + (void) MakeKey( column, irow, key, AST__MXCOLKEYLEN + 1, + status ); + +/* Get the values in the current cell of the column, using its native + data type. For floating point, convert any NaNs into the appropriate + null value (do not need to do this if the null value is itself NaN). */ + if( type == AST__INTTYPE ) { + ok = astMapGet1I( this, key, nel, &nval, pout ); + + } else if( type == AST__DOUBLETYPE ){ + ok = astMapGet1D( this, key, nel, &nval, pout ); + + if( ok && astISFINITE(dnull) ) { + for( iel = 0; iel < nel; iel++ ) { + if( !astISFINITE( ((double *)pout)[ iel ] ) ) { + ((double *)pout)[ iel ] = dnull; + } + } + } + + } else if( type == AST__FLOATTYPE ){ + ok = astMapGet1F( this, key, nel, &nval, pout ); + + if( ok && astISFINITE(fnull) ) { + for( iel = 0; iel < nel; iel++ ) { + if( !astISFINITE( ((float *)pout)[ iel ] ) ) { + ((float *)pout)[ iel ] = fnull; + } + } + } + + } else if( type == AST__SINTTYPE ){ + ok = astMapGet1S( this, key, nel, &nval, pout ); + + } else if( type == AST__BYTETYPE ){ + ok = astMapGet1B( this, key, nel, &nval, pout ); + + } else if( type == AST__STRINGTYPE ){ + ok = astMapGet1C( this, key, nb + 1, nel, &nval, cbuf ); + +/* Copy the strings returned by astMapGet1C into the returned array, + omitting the trailing null at the end of each string. */ + CopyStrings( nval, nb, cbuf, pout, status ); + + } else { + ok = 0; + } + +/* If the cell could not be found, return a suitable number of column null + values. */ + if( !ok ) { + +/* Determine the null value to use, if this has not already been done. */ + if( !pnull ) { + +/* Allocate a buffer to hold a single null value */ + pnull = astMalloc( nb ); + if( astOK ) { + +/* Copy the appropriate null value into the buffer allocated above. */ + if( type == AST__INTTYPE ) { + *( (int *) pnull ) = astColumnNull( this, column, 0, 0, + &wasset, NULL ); + } else if( type == AST__DOUBLETYPE ){ + *( (double *) pnull ) = dnull; + + } else if( type == AST__FLOATTYPE ){ + *( (float *) pnull ) = fnull; + + } else if( type == AST__STRINGTYPE ){ + memset( pnull, 0, nb ); + + } else if( type == AST__SINTTYPE ){ + *( (short int *) pnull ) = astColumnNull( this, column, 0, 0, + &wasset, NULL ); + } else if( type == AST__BYTETYPE ){ + *( (unsigned char *) pnull ) = astColumnNull( this, column, 0, 0, + &wasset, NULL ); + } + } + } + +/* Append the right number of nulls to the returned array. */ + for( iel = 0; iel < nel; iel++ ) { + memcpy( pout, pnull, nb ); + pout += nb; + } + +/* If the cell was found in the table, just increment the pointer to the next + returned value. */ + } else { + pout += nbv; + } + } + +/* Free resources. */ + cbuf = astFree( cbuf ); + pnull = astFree( pnull ); + +/* Return the number of returned elements. */ + *nelem = nel*nrow; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "fitstable.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* FitsTable member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied FitsTables, +* in bytes. + +* Parameters: +* this +* Pointer to the FitsTable. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The FitsTable size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFitsTable *this; /* Pointer to FitsTable structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the FitsTable structure. */ + this = (AstFitsTable *) this_object; + +/* Invoke the GetObjSize method inherited from the parent Table class, and + then add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astGetObjSize( this->header ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static AstFitsChan *GetTableHeader( AstFitsTable *this, int *status ) { +/* +*++ +* Name: +c astGetTableHeader +f AST_GetTableHeader + +* Purpose: +* Get the FITS headers from a FitsTable. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c AstFitsChan *astGetTableHeader( AstFitsTable *this ) +f RESULT = AST_GETTABLEHEADER( THIS, STATUS ) + +* Class Membership: +* FitsTable method. + +* Description: +* This function returns a pointer to a FitsChan holding copies of +* the FITS headers associated with a FitsTable. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsTable. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetTableHeader() +f AST_GetTableHeader = INTEGER +* A pointer to a deep copy of the FitsChan stored within the +* FitsTable. + +* Notes: +* - The returned pointer should be annulled using +c astAnnul +f AST_ANNUL +* when it is no longer needed. +* - Changing the contents of the returned FitsChan will have no effect +* on the FitsTable. To modify the FitsTable, the modified FitsChan must +* be stored in the FitsTable using +c astPutTableHeader. +f AST_PUTTABLEHEADER. + +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Ensure the fixed value headers are up-to-date in the FitsChan stored + in the FitsTable. */ + UpdateHeader( this, "astGetTableHeader", status ); + +/* Reset the current card to the first card. */ + astClearCard( this->header ); + +/* Return a deep copy of the FitsChan. */ + return astCopy( this->header ); +} + +void astInitFitsTableVtab_( AstFitsTableVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitFitsTableVtab + +* Purpose: +* Initialise a virtual function table for a FitsTable. + +* Type: +* Protected function. + +* Synopsis: +* #include "fitstable.h" +* void astInitFitsTableVtab( AstFitsTableVtab *vtab, const char *name ) + +* Class Membership: +* FitsTable vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the FitsTable class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstTableVtab *table; /* Pointer to Table component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitTableVtab( (AstTableVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAFitsTable) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstTableVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->GetTableHeader = GetTableHeader; + vtab->PutTableHeader = PutTableHeader; + vtab->ColumnNull = ColumnNull; + vtab->ColumnSize = ColumnSize; + vtab->GetColumnData = GetColumnData; + vtab->PutColumnData = PutColumnData; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + table = (AstTableVtab *) vtab; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_addcolumn = table->AddColumn; + table->AddColumn = AddColumn; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "FitsTable", "FITS binary table" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static char *MakeKey( const char *column, int irow, char *buf, int len, + int *status ){ +/* +* Name: +* MakeKey + +* Purpose: +* Construct a key for a column cell from a column name and row number. + +* Type: +* Private function. + +* Synopsis: +* #include "fitstable.h" +* char *MakeKey( const char *column, int irow, char *buf, int len, +* int *status ) + +* Class Membership: +* FitsTable member function + +* Description: +* This function constructs a key for a column cell from a column name +* and row number. An error is reported if the buffer is too short. + +* Parameters: +* column +* Pointer to the column name. Trailing white space is ignored. +* irow +* One-based index of the row. +* buf +* Pointer to a buffer in which to store the returned key. +* len +* The length of the buffer. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A copy of "buf". + +*/ + +/* Local Variables: */ + char *result; + char rbuf[ 40 ]; + int collen; + int nc; + +/* Initialise. */ + result = buf; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Format the column number. */ + nc = sprintf( rbuf, "%d", irow ); + +/* Get the used length of the column name (i.e. excluding trailing white + space). */ + collen = astChrLen( column ); + +/* For the total length of the returned string. */ + nc += collen + 3; + +/* If the buffer is large enough, store the returned string. */ + if( len >= nc ) { + sprintf( buf, "%.*s(%s)", collen, column, rbuf ); + } else { + astError( AST__INTER, "MakeKey(FitsTable): Internal buffer is too " + "short to hold Table cell name '%.*s(%s)' (internal AST " + "programming error).", status, collen, column, rbuf ); + } + +/* Return the result, */ + return result; +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* FitsTable member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstFitsTable *this; /* Pointer to FitsTable structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the FitsTable structure. */ + this = (AstFitsTable *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->header, mode, extra, fail ); + + return result; + +} +#endif + +static void PurgeHeader( AstFitsTable *this, int *status ) { +/* +* Name: +* PurgeHeader + +* Purpose: +* Remove fixed-value keywords from the table header. + +* Type: +* Private function. + +* Synopsis: +* void PurgeHeader( AstFitsTable *this, int *status ) + +* Description: +* This function ensures that the headers that are determined by the +* table contents or by the FITS standard do not exist in the header +* of the supplied FitsTable. + +* Parameters: +* this +* Pointer to the FitsTable. +* status +* Pointer to inherited status. + +*/ + +/* Local Constants: */ +#define nfixed 14 /* Number of fixed-value keywords to check for */ + +/* Local Variables: */ + int ifixed; + +/* A list of FITS keywords that have values that are fixed by the FITS + standard or by the contents of the Table. */ + const char *fixed[] = { "XTENSION", "BITPIX", "NAXIS", "NAXIS1", + "NAXIS2", "PCOUNT", "GCOUNT", "TFIELDS", + "TFORM%d", "TTYPE%d", "TNULL%d", "THEAP", + "TDIM%d", "TUNIT%d" }; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Remove headers that have fixed values. */ + for( ifixed = 0; ifixed < nfixed; ifixed++ ) { + astClearCard( this->header ); + while( astFindFits( this->header, fixed[ ifixed ], NULL, 0 ) ) { + astDelFits( this->header ); + } + } + +/* Undefine local constants */ +#undef nfixed +} + +static void PutColumnData( AstFitsTable *this, const char *column, + int clen, size_t size, void *coldata, int *status ){ +/* +*++ +* Name: +c astPutColumnData +f AST_PUTCOLUMNDATA + +* Purpose: +* Store new data values for all rows of a column. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astPutColumnData( AstFitsTable *this, const char *column, +c int clen, size_t size, void *coldata ) +f CALL AST_PUTCOLUMNDATA( THIS, COLUMN, CLEN, SIZE, COLDATA, STATUS ) + +* Class Membership: +* FitsTable method. + +* Description: +c This function +f This routine +* copies data values from a supplied buffer into a named column. The +* first element in the buffer becomes the first element in the first +* row of the column. If the buffer does not completely fill the +* column, then any trailing rows are filled with null values. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsTable. +c column +f COLUMN = CHARACTER * ( * ) (Given) +* The character string holding the name of the column. Trailing +* spaces are ignored. +c clen +f CLEN = INTEGER (Given) +* If the column holds character strings, then this must be set to +* the length of each fixed length string in the supplied array. +* This is often determined by the appropriate TFORMn keyword in +* the binary table header. The supplied value is ignored if the +* column does not hold character data. +c size +f SIZE = INTEGER (Given) +* The size of the +c "coldata" +f COLDATA +* array, in bytes. This should be an integer multiple of the +* number of bytes needed to hold the full vector value stored in a +* single cell of the column. An error is reported if this is not +* the case. +c coldata +f COLDATA( * ) = BYTE (Given) +c A pointer to an +f An +* area of memory holding the data to copy into the column. The values +* should be stored in row order. If the column holds non-scalar values, +* the elements of each value should be stored in "Fortran" order. No +* data type conversion is performed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + char key[ AST__MXCOLKEYLEN + 1 ]; /* Current cell key string */ + char **carray; /* Pointer to array of null terminated string pointers */ + int irow; /* Index of value being copied */ + int iel; /* Index of current element */ + int nel; /* No. of elements per value */ + int nrow; /* No. of values to copy */ + int type; /* Data type */ + size_t nb; /* No. of bytes for a single element of a value */ + size_t nbv; /* No. of bytes per value */ + void *pin; /* Pointer to next input array element */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise */ + nb = 0; + +/* Find the number of bytes in the supplied array holding a single element + of the value in a column cell. */ + type = astGetColumnType( this, column ); + if( type == AST__INTTYPE ) { + nb = sizeof( int ); + + } else if( type == AST__DOUBLETYPE ){ + nb = sizeof( double ); + + } else if( type == AST__STRINGTYPE ){ + nb = clen*sizeof( char ); + + } else if( type == AST__FLOATTYPE ){ + nb = sizeof( float ); + + } else if( type == AST__SINTTYPE ){ + nb = sizeof( short int ); + + } else if( type == AST__BYTETYPE ){ + nb = sizeof( char ); + + } else if( astOK ) { + astError( AST__INTER, "astPutColumnData(%s): Unsupported column type " + "%d (internal AST programming error).", status, + astGetClass( this ), type ); + } + +/* Get the number of elements per value, and the number of bytes (in the + supplied array) per value. */ + nel = astGetColumnLength( this, column ); + nbv = nb*nel; + +/* Initialise a pointer to the next element of the supplied array to read. */ + pin = coldata; + +/* Get the number of rows to copy from the supplied array. */ + nrow = nbv ? size / nbv : 0; + +/* As yet we have no array of null terminated character pointers. */ + carray = NULL; + +/* Report an error if the supplied array does not hold an exact number of + column cells. */ + if( nrow*nbv != size && astOK ) { + astError( AST__BADSIZ, "astPutColumnData(%s): The supplied array size " + "(%d bytes) is not an exact multiple of the size of one " + "column value (%d bytes).", status, astGetClass( this ), + (int) size, (int) nbv ); + } + +/* Loop round the rows to be copied. */ + for( irow = 1; irow <= nrow; irow++ ) { + +/* Format the cell name. */ + (void) MakeKey( column, irow, key, AST__MXCOLKEYLEN + 1, + status ); + +/* Put the next value into the current cell of the column, using its native + data type. Skip floating point values that are entirely NaN. */ + if( type == AST__INTTYPE ) { + astMapPut1I( this, key, nel, pin, NULL ); + + } else if( type == AST__DOUBLETYPE ){ + for( iel = 0; iel < nel; iel++ ) { + if( astISFINITE( ((double *)pin)[ iel ] ) ) { + astMapPut1D( this, key, nel, pin, NULL ); + break; + } + } + + } else if( type == AST__FLOATTYPE ){ + for( iel = 0; iel < nel; iel++ ) { + if( astISFINITE( ((double *)pin)[ iel ] ) ) { + astMapPut1F( this, key, nel, pin, NULL ); + break; + } + } + + } else if( type == AST__SINTTYPE ){ + astMapPut1S( this, key, nel, pin, NULL ); + + } else if( type == AST__BYTETYPE ){ + astMapPut1B( this, key, nel, pin, NULL ); + +/* If each cell in the column holds an array of strings, we need to + convert the fixed length strings in the supplied array into an array + of pointers to null terminated strings. */ + } else if( type == AST__STRINGTYPE ){ + carray = astStringArray( pin, nel, clen ); + astMapPut1C( this, key, nel, (const char ** ) carray, NULL ); + carray = astFree( carray ); + } + +/* Increment the pointer to the next input value. */ + pin += nbv; + } + +/* Remove any remaining cells already present in this column. */ + nrow = astGetNrow( this ); + for( ; irow <= nrow; irow++ ) { + (void) MakeKey( column, irow, key, AST__MXCOLKEYLEN + 1, + status ); + astMapRemove( this, key ); + } +} + +static void PutTableHeader( AstFitsTable *this, AstFitsChan *header, + int *status ) { +/* +*++ +* Name: +c astPutTableHeader +f AST_PUTTABLEHEADER + +* Purpose: +* Store new FITS headers in a FitsTable. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astPutTableHeader( AstFitsTable *this, AstFitsChan *header ) +f CALL AST_PUTTABLEHEADER( THIS, HEADER, STATUS ) + +* Class Membership: +* FitsTable method. + +* Description: +c This function +f This routine +* stores new FITS headers in the supplied FitsTable. Any existing +* headers are first deleted. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FitsTable. +c header +f HEADER = INTEGER (Given) +* Pointer to a FitsChan holding the headers for the FitsTable. +* A deep copy of the supplied FitsChan is stored in the FitsTable, +* replacing the current FitsChan in the Fitstable. Keywords that +* are fixed either by the properties of the Table, or by the FITS +* standard, are removed from the copy (see "Notes:" below). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The attributes of the supplied FitsChan, together with any source +* and sink functions associated with the FitsChan, are copied to the +* FitsTable. +* - Values for the following keywords are generated automatically by +* the FitsTable (any values for these keywords in the supplied +* FitsChan will be ignored): "XTENSION", "BITPIX", "NAXIS", "NAXIS1", +* "NAXIS2", "PCOUNT", "GCOUNT", "TFIELDS", "TFORM%d", "TTYPE%d", +* "TNULL%d", "THEAP", "TDIM%d". + +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Annul the existing FitsChan. */ + (void) astAnnul( this->header ); + +/* Store a deep copy of the supplied FitsChan in the FitsTable. */ + this->header = astCopy( header ); + +/* Remove headers that have fixed values. */ + PurgeHeader( this, status ); +} + +static void UpdateHeader( AstFitsTable *this, const char *method, + int *status ) { +/* +* Name: +* UpdateHeader + +* Purpose: +* Ensure FITS headers accurately describe the current table contents. + +* Type: +* Private function. + +* Synopsis: +* void UpdateHeader( AstFitsTable *this, const char *method, +* int *status ) + +* Description: +* This function ensures that the FITS headers that are determined by +* the table contents or by the FITS standard are up to date. + +* Parameters: +* this +* Pointer to the FitsTable. +* method +* Pointer to a string holding the name of the method to include in +* error messages. +* status +* Pointer to inherited status. + +*/ + +/* Local Variables: */ + char *dimbuf; + char buf[ 20 ]; + char code; + char keyword[ 14 ]; + const char *unit; + const char *name; + int *dims; + int hasNull; + int icol; + int idim; + int nc; + int ncol; + int ndim; + int nel; + int null; + int rowsize; + int set; + int slen; + int type; + +/* Check inherited status */ + if( !astOK ) return; + +/* Remove any existing headers that will have values stored for them by + this function. */ + PurgeHeader( this, status ); + +/* Store headers that have fixed values regardless of the contents of the + table. Rewind the FitsChan first so they go at the start of the header. */ + astClearCard( this->header ); + astSetFitsS( this->header, "XTENSION", "BINTABLE", NULL, 0 ); + astSetFitsI( this->header, "BITPIX", 8, NULL, 0 ); + astSetFitsI( this->header, "NAXIS", 2, NULL, 0 ); + astSetFitsI( this->header, "PCOUNT", 0, NULL, 0 ); + astSetFitsI( this->header, "GCOUNT", 1, NULL, 0 ); + +/* The number of columns. */ + ncol = astGetNcolumn( this ); + astSetFitsI( this->header, "TFIELDS", ncol, NULL, 0 ); + +/* Add column-specific keywords, one for each column in the Table. */ + dims = NULL; + dimbuf = NULL; + rowsize = 0; + for( icol = 1; icol <= ncol && astOK; icol++ ){ + +/* Get the name, type and shape of the current column. */ + name = astColumnName( this, icol ); + nel = astGetColumnLength( this, name ); + type = astGetColumnType( this, name ); + unit = astGetColumnUnit( this, name ); + ndim = astGetColumnNdim( this, name ); + dims = astGrow( dims, ndim, sizeof( int ) ); + if( astOK ) { + astColumnShape( this, name, ndim, &ndim, dims ); + +/* Get the FITS code that describes the column data type. Also increment + the number of bytes (rowsize) used to describe a whole row. */ + slen = 0; + code = ' '; + if( type == AST__BYTETYPE ) { + code = 'B'; + rowsize += nel; + + } else if( type == AST__SINTTYPE ) { + code = 'I'; + rowsize += 2*nel; + + } else if( type == AST__INTTYPE ) { + code = 'J'; + rowsize += 4*nel; + + } else if( type == AST__DOUBLETYPE ) { + code = 'D'; + rowsize += 8*nel; + + } else if( type == AST__FLOATTYPE ) { + code = 'E'; + rowsize += 4*nel; + + } else if( type == AST__STRINGTYPE ) { + code = 'A'; + +/* Use the maximum length of the strings in the current column (excluding + null) to scale the FITS repeat count. */ + slen = astGetColumnLenC( this, name ); + nel *= slen; + rowsize += nel; + +/* Report an error if the data type is not supported by FITS. */ + } else if( astOK ) { + astError( AST__INTER, "%s(%s): Illegal type %d for column '%s' " + "in a %s (internal AST programming error).", status, + method, astGetClass( this ), type, name, + astGetClass( this ) ); + } + +/* Start the TFORMn keyword value. This is the number of values in each + cell, and is not needed if the cell contains only one value. */ + nc = sprintf( buf, "%d", nel ); + +/* Add the data type code to complete the TFORMn value, and store it in + the FitsChan. */ + sprintf( buf + nc, "%c", code ); + sprintf( keyword, "TFORM%d", icol ); + astSetFitsS( this->header, keyword, buf, NULL, 0 ); + +/* Column name. */ + sprintf( keyword, "TTYPE%d", icol ); + astSetFitsS( this->header, keyword, name, NULL, 0 ); + +/* Column units. */ + if( astChrLen( unit ) ) { + sprintf( keyword, "TUNIT%d", icol ); + astSetFitsS( this->header, keyword, unit, NULL, 0 ); + } + +/* Column null value (integer columns only). Only store a TNULLn keyword + if the NULL attribute has been set for the column, or if the column + contains missing (i.e. null) values. */ + if( type == AST__BYTETYPE || type == AST__SINTTYPE || + type == AST__INTTYPE ) { + null = astColumnNull( this, name, 0, 0, &set, &hasNull ); + if( set || hasNull ) { + sprintf( keyword, "TNULL%d", icol ); + astSetFitsI( this->header, keyword, null, NULL, 0 ); + } + } + +/* Array dimensions (only needed for non-scalars). */ + if( ndim > 0 ) { + dimbuf = astGrow( dimbuf, ndim, 15 ); + if( astOK ) { + +/* For strings, the first dimension is the length of the fixed-length + strings that make up the array. */ + if( type != AST__STRINGTYPE ) { + nc = sprintf( dimbuf, "(%d", dims[ 0 ] ); + } else { + nc = sprintf( dimbuf, "(%d,%d", slen, dims[ 0 ] ); + } + +/* Append the second and subsequent dimensions to the buffer. */ + for( idim = 1; idim < ndim; idim++ ) { + nc += sprintf( dimbuf + nc, ",%d", dims[ idim ] ); + } + sprintf( dimbuf + nc, ")" ); + +/* Store the buffered string as the value for keyword TDIMn in the + FitsChan. */ + sprintf( keyword, "TDIM%d", icol ); + astSetFitsS( this->header, keyword, dimbuf, NULL, 0 ); + } + } + } + } + +/* Insert the NAXISi keywords into the header, following the NAXIS value + (i.e. starting at card 4). */ + astSetCard( this->header, 4 ); + astSetFitsI( this->header, "NAXIS1", rowsize, NULL, 0 ); + astSetFitsI( this->header, "NAXIS2", astGetNrow( this ), NULL, 0 ); + +/* Free resources. */ + dims = astFree( dims ); + dimbuf = astFree( dimbuf ); +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for FitsTable objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for FitsTable objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Mappings within the FitsTable. +*/ + +/* Local Variables: */ + AstFitsTable *in; /* Pointer to input FitsTable */ + AstFitsTable *out; /* Pointer to output FitsTable */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output FitsTables. */ + in = (AstFitsTable *) objin; + out = (AstFitsTable *) objout; + +/* Make copies of the component Tables and store pointers to them in the + output FitsTable structure. */ + out->header = astCopy( in->header ); +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for FitsTable objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for FitsTable objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstFitsTable *this; /* Pointer to FitsTable */ + +/* Obtain a pointer to the FitsTable structure. */ + this = (AstFitsTable *) obj; + +/* Annul the pointers to the component Tables. */ + this->header = astAnnul( this->header ); + +} + + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for FitsTable objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the FitsTable class to an output Channel. + +* Parameters: +* this +* Pointer to the FitsTable whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFitsTable *this; /* Pointer to the FitsTable structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FitsTable structure. */ + this = (AstFitsTable *) this_object; + +/* Write out values representing the instance variables for the FitsTable + class. Note, the primitive data in the FitsTable will be written out + by the parent Table Dump function. This function deals just with the + extra information held in the FitsTable structure. */ + +/* Write out the FITS header. */ + astWriteObject( channel, "Header", 1, 0, this->header, "FITS headers" ); +} + + + + + + + + + + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAFitsTable and astCheckFitsTable functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(FitsTable,Table) +astMAKE_CHECK(FitsTable) + +AstFitsTable *astFitsTable_( void *header_void, const char *options, int *status, ...) { +/* +*++ +* Name: +c astFitsTable +f AST_FITSTABLE + +* Purpose: +* Create a FitsTable. + +* Type: +* Public function. + +* Synopsis: +c #include "fitstable.h" +c AstFitsTable *astFitsTable( AstFitsChan *header, const char *options, ... ) +f RESULT = AST_FITSTABLE( HEADER, OPTIONS, STATUS ) + +* Class Membership: +* FitsTable constructor. + +* Description: +* This function creates a new FitsTable and optionally initialises +* its attributes. +* +* The FitsTable class is a representation of a FITS binary table. It +* inherits from the Table class. The parent Table is used to hold the +* binary data of the main table, and a FitsChan is used to hold the FITS +* header. Note, there is no provision for binary data following the main +* table (such data is referred to as a "heap" in the FITS standard). +* +* Note - it is not recommended to use the FitsTable class to store +* very large tables. + +* Parameters: +c header +f HEADER = INTEGER (Given) +* Pointer to an optional FitsChan containing headers to be stored +* in the FitsTable. +c NULL +f AST__NULL +* may be supplied if the new FitsTable is to be left empty. If +* supplied, and if the headers describe columns of a FITS binary +* table, then equivalent (empty) columns are added to the FitsTable. +* Each column has the same index in the FitsTable that it has in +* the supplied header. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new FitsTable. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new FitsTable. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFitsTable() +f AST_FITSTABLE = INTEGER +* A pointer to the new FitsTable. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list described above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsTable *new; /* Pointer to new FitsTable */ + AstFitsChan *header; /* Pointer to header FitsChan */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate pointers to the header FitsChan provided. */ + header = header_void ? astCheckFitsChan( header_void ) : NULL; + +/* Initialise the FitsTable, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitFitsTable( NULL, sizeof( AstFitsTable ), !class_init, + &class_vtab, "FitsTable", header ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new FitsTable's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new FitsTable. */ + return new; +} + +AstFitsTable *astFitsTableId_( void *header_void, const char *options, ... ) { +/* +* Name: +* astFitsTableId_ + +* Purpose: +* Create a FitsTable. + +* Type: +* Private function. + +* Synopsis: +* #include "fitstable.h" +* AstFitsTable *astFitsTableId_( AstFitsChan *header, const char *options, ... ) + +* Class Membership: +* FitsTable constructor. + +* Description: +* This function implements the external (public) interface to the +* astFitsTable constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astFitsTable_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astFitsTable_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astFitsTable_. + +* Returned Value: +* The ID value associated with the new FitsTable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsChan *header; /* Genuine C poitner to header FitsChan */ + AstFitsTable *new; /* Pointer to new FitsTable */ + int *status; /* Pointer to inherited status value */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a FitsChan pointer from any ID supplied and validate the + pointer to ensure it identifies a valid FitsChan. */ + header = header_void ? astCheckFitsChan( astMakePointer( header_void ) ) : NULL; + +/* Initialise the FitsTable, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitFitsTable( NULL, sizeof( AstFitsTable ), !class_init, + &class_vtab, "FitsTable", header ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new FitsTable's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new FitsTable. */ + return astMakeId( new ); +} + +AstFitsTable *astInitFitsTable_( void *mem, size_t size, int init, + AstFitsTableVtab *vtab, const char *name, + AstFitsChan *header, int *status ) { +/* +*+ +* Name: +* astInitFitsTable + +* Purpose: +* Initialise a FitsTable. + +* Type: +* Protected function. + +* Synopsis: +* #include "fitstable.h" +* AstFitsTable *astInitFitsTable( void *mem, size_t size, int init, +* AstFitsTableVtab *vtab, const char *name, +* AstFitsChan *header ) + +* Class Membership: +* FitsTable initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new FitsTable object. It allocates memory (if necessary) to accommodate +* the FitsTable plus any additional data associated with the derived class. +* It then initialises a FitsTable structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a FitsTable at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the FitsTable is to be initialised. +* This must be of sufficient size to accommodate the FitsTable data +* (sizeof(FitsTable)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the FitsTable (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the FitsTable +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the FitsTable's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new FitsTable. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* header +* If not NULL, a FitsChan that is used to populate the FitsTable +* with headers and columns. + +* Returned Value: +* A pointer to the new FitsTable. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFitsTable *new; /* Pointer to new FitsTable */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitFitsTableVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Table structure (the parent class) as the first component + within the FitsTable structure, allocating memory if necessary. Specify that + the Table should be defined in both the forward and inverse directions. */ + new = (AstFitsTable *) astInitTable( mem, size, 0, (AstTableVtab *) vtab, + name ); + if ( astOK ) { + +/* Initialise the FitsTable data. */ +/* ---------------------------- */ + new->header = astFitsChan( NULL, NULL, " ", status ); + +/* If a header was supplied, add equivalent columns to the FitsTable, and + store the header. */ + if( header ) { + GenerateColumns( new, header, status ); + PutTableHeader( new, header, status ); + } + +/* If an error occurred, clean up by deleting the new FitsTable. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new FitsTable. */ + return new; +} + +AstFitsTable *astLoadFitsTable_( void *mem, size_t size, AstFitsTableVtab *vtab, + const char *name, AstChannel *channel, + int *status ) { +/* +*+ +* Name: +* astLoadFitsTable + +* Purpose: +* Load a FitsTable. + +* Type: +* Protected function. + +* Synopsis: +* #include "fitstable.h" +* AstFitsTable *astLoadFitsTable( void *mem, size_t size, AstFitsTableVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* FitsTable loader. + +* Description: +* This function is provided to load a new FitsTable using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* FitsTable structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a FitsTable at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the FitsTable is to be +* loaded. This must be of sufficient size to accommodate the +* FitsTable data (sizeof(FitsTable)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the FitsTable (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the FitsTable structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstFitsTable) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new FitsTable. If this is NULL, a pointer +* to the (static) virtual function table for the FitsTable class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "FitsTable" is used instead. + +* Returned Value: +* A pointer to the new FitsTable. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFitsTable *new; /* Pointer to the new FitsTable */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this FitsTable. In this case the + FitsTable belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstFitsTable ); + vtab = &class_vtab; + name = "FitsTable"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitFitsTableVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built FitsTable. */ + new = astLoadTable( mem, size, (AstTableVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "FitsTable" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* FitsChan holding table headers. */ + new->header = astReadObject( channel, "header", NULL ); + +/* If an error occurred, clean up by deleting the new FitsTable. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new FitsTable pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +AstFitsChan *astGetTableHeader_( AstFitsTable *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,FitsTable,GetTableHeader))(this,status); +} + +void astPutTableHeader_( AstFitsTable *this, AstFitsChan *header, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FitsTable,PutTableHeader))(this,header,status); +} + +int astColumnNull_( AstFitsTable *this, const char *column, int set, + int newval, int *wasset, int *hasnull, int *status ){ + *wasset = 0; + if( hasnull ) *hasnull = 0; + if ( !astOK ) return 0; + return (**astMEMBER(this,FitsTable,ColumnNull))(this,column,set,newval,wasset,hasnull,status); +} + +size_t astColumnSize_( AstFitsTable *this, const char *column, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,FitsTable,ColumnSize))(this,column,status); +} + +void astGetColumnData_( AstFitsTable *this, const char *column, float fnull, + double dnull, size_t mxsize, void *coldata, int *nelem, + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,FitsTable,GetColumnData))(this,column,fnull,dnull,mxsize, + coldata,nelem,status); +} + +void astPutColumnData_( AstFitsTable *this, const char *column, int clen, + size_t size, void *coldata, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,FitsTable,PutColumnData))(this,column,clen,size,coldata,status); +} + + + + + + + + diff --git a/fitstable.h b/fitstable.h new file mode 100644 index 0000000..a2633b6 --- /dev/null +++ b/fitstable.h @@ -0,0 +1,235 @@ +#if !defined( FITSTABLE_INCLUDED ) /* Include this file only once */ +#define FITSTABLE_INCLUDED +/* +*+ +* Name: +* fitstable.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the FitsTable class. + +* Invocation: +* #include "fitstable.h" + +* Description: +* This include file defines the interface to the FitsTable class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The FitsTable class inherits from the Table class. + +* Copyright: +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 25-NOV-2010 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "table.h" /* Parent class */ +#include "fitschan.h" + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* FitsTable structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstFitsTable { + +/* Attributes inherited from the parent class. */ + AstTable table; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstFitsChan *header; /* FitsChan containing table headers */ +} AstFitsTable; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstFitsTableVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstTableVtab table_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstFitsChan *(* GetTableHeader)( AstFitsTable *, int * ); + void (* PutTableHeader)( AstFitsTable *, AstFitsChan *, int * ); + int (* ColumnNull)( AstFitsTable *, const char *, int, int, int *, int *, int * ); + size_t (* ColumnSize)( AstFitsTable *, const char *, int * ); + void (* GetColumnData)( AstFitsTable *, const char *, float, double, size_t, void *, int *, int * ); + void (* PutColumnData)( AstFitsTable *, const char *, int, size_t, void *, int * ); + +} AstFitsTableVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstFitsTableGlobals { + AstFitsTableVtab Class_Vtab; + int Class_Init; +} AstFitsTableGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitFitsTableGlobals_( AstFitsTableGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(FitsTable) /* Check class membership */ +astPROTO_ISA(FitsTable) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstFitsTable *astFitsTable_( void *, const char *, int *, ...); +#else +AstFitsTable *astFitsTableId_( void *, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstFitsTable *astInitFitsTable_( void *, size_t, int, AstFitsTableVtab *, + const char *, AstFitsChan *, int * ); + +/* Vtab initialiser. */ +void astInitFitsTableVtab_( AstFitsTableVtab *, const char *, int * ); + +/* Loader. */ +AstFitsTable *astLoadFitsTable_( void *, size_t, AstFitsTableVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstFitsChan *astGetTableHeader_( AstFitsTable *, int * ); +void astPutTableHeader_( AstFitsTable *, AstFitsChan *, int * ); +int astColumnNull_( AstFitsTable *, const char *, int, int, int *, int *, int * ); +size_t astColumnSize_( AstFitsTable *, const char *, int * ); +void astGetColumnData_( AstFitsTable *, const char *, float, double, size_t, void *, int *, int * ); +void astPutColumnData_( AstFitsTable *, const char *, int, size_t, void *, int * ); + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckFitsTable(this) astINVOKE_CHECK(FitsTable,this,0) +#define astVerifyFitsTable(this) astINVOKE_CHECK(FitsTable,this,1) + +/* Test class membership. */ +#define astIsAFitsTable(this) astINVOKE_ISA(FitsTable,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astFitsTable astINVOKE(F,astFitsTable_) +#else +#define astFitsTable astINVOKE(F,astFitsTableId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitFitsTable(mem,size,init,vtab,name,header) \ +astINVOKE(O,astInitFitsTable_(mem,size,init,vtab,name,header,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitFitsTableVtab(vtab,name) astINVOKE(V,astInitFitsTableVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadFitsTable(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadFitsTable_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckFitsTable to validate FitsTable pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astGetTableHeader(this) \ +astINVOKE(O,astGetTableHeader_(astCheckFitsTable(this),STATUS_PTR)) +#define astPutTableHeader(this,header) \ +astINVOKE(V,astPutTableHeader_(astCheckFitsTable(this),astCheckFitsChan(header),STATUS_PTR)) +#define astColumnNull(this,column,set,newval,wasset,hasnull) \ +astINVOKE(V,astColumnNull_(astCheckFitsTable(this),column,set,newval,wasset,hasnull,STATUS_PTR)) +#define astColumnSize(this,column) \ +astINVOKE(V,astColumnSize_(astCheckFitsTable(this),column,STATUS_PTR)) +#define astGetColumnData(this,column,fnull,dnull,mxsize,coldata,nelem) \ +astINVOKE(V,astGetColumnData_(astCheckFitsTable(this),column,fnull,dnull,mxsize,coldata,nelem,STATUS_PTR)) +#define astPutColumnData(this,column,clen,size,coldata) \ +astINVOKE(V,astPutColumnData_(astCheckFitsTable(this),column,clen,size,coldata,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#endif + +#endif diff --git a/fkeymap.c b/fkeymap.c new file mode 100644 index 0000000..d09ec1a --- /dev/null +++ b/fkeymap.c @@ -0,0 +1,1400 @@ +/* +*+ +* Name: +* fkeymap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST KeyMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the KeyMap class. + +* Routines Defined: +* AST_ISAKEYMAP +* AST_KEYMAP +* AST_MAPCOPY +* AST_MAPPUT0 +* AST_MAPPUT1 +* AST_MAPPUTU +* AST_MAPGET0 +* AST_MAPGET1 +* AST_MAPGETELEM +* AST_MAPPUTELEM +* AST_MAPREMOVE +* AST_MAPRENAME +* AST_MAPSIZE +* AST_MAPLENGTH +* AST_MAPLENC +* AST_MAPTYPE +* AST_MAPKEY + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 13-NOV-2004 (DSB): +* Original version. +* 5-JUN-2006 (DSB): +* Added support for single precision entries. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "keymap.h" /* C interface to the KeyMap class */ + +F77_LOGICAL_FUNCTION(ast_isakeymap)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAKEYMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAKeyMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_keymap)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_KEYMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astKeyMap( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_mapput0a)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT0A", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut0A( astI2P( *THIS ), key, astI2P( *VALUE ), comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput0c)( INTEGER(THIS), + CHARACTER(KEY), + CHARACTER(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(VALUE) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_CHARACTER(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *value, *key; + + astAt( "AST_MAPPUT0C", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + value = astString( VALUE, VALUE_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut0C( astI2P( *THIS ), key, value, comment ); + astFree( key ); + astFree( value ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput0i)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT0I", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut0I( astI2P( *THIS ), key, *VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput0s)( INTEGER(THIS), + CHARACTER(KEY), + WORD(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_WORD(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT0W", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut0S( astI2P( *THIS ), key, *VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput0b)( INTEGER(THIS), + CHARACTER(KEY), + UBYTE(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_UBYTE(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT0B", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut0B( astI2P( *THIS ), key, *VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput0d)( INTEGER(THIS), + CHARACTER(KEY), + DOUBLE(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_DOUBLE(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT0D", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut0D( astI2P( *THIS ), key, *VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput0r)( INTEGER(THIS), + CHARACTER(KEY), + REAL(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_REAL(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT0R", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut0F( astI2P( *THIS ), key, *VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput1a)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(SIZE), + INTEGER_ARRAY(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(SIZE) + GENPTR_INTEGER_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + AstObject **values; + int i; + + astAt( "AST_MAPPUT1A", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + + values = astMalloc( sizeof( AstObject * )*(size_t)( *SIZE )); + if( astOK ) { + for( i = 0; i < *SIZE; i++ ) { + values[ i ] = astI2P( VALUE[ i ] ); + } + } + + astMapPut1A( astI2P( *THIS ), key, *SIZE, values, comment ); + astFree( values ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput1c)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(SIZE), + CHARACTER_ARRAY(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(VALUE) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(SIZE) + GENPTR_CHARACTER_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + const char **values; + int i; + + astAt( "AST_MAPPUT1C", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + + values = astMalloc( sizeof( const char * )*(size_t)( *SIZE )); + if( astOK ) { + for( i = 0; i < *SIZE; i++ ) { + values[ i ] = astString( VALUE + i*VALUE_length, VALUE_length ); + } + } + + astMapPut1C( astI2P( *THIS ), key, *SIZE, values, comment ); + + if( astOK ) { + for( i = 0; i < *SIZE; i++ ) astFree( (void *) values[ i ] ); + } + astFree( (void *) values ); + astFree( key ); + astFree( comment ); + ) +} + + + +F77_SUBROUTINE(ast_mapput1i)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(SIZE), + INTEGER_ARRAY(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(SIZE) + GENPTR_INTEGER_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT1I", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut1I( astI2P( *THIS ), key, *SIZE, VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + + +F77_SUBROUTINE(ast_mapput1s)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(SIZE), + WORD_ARRAY(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(SIZE) + GENPTR_WORD_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT1W", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut1S( astI2P( *THIS ), key, *SIZE, VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput1b)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(SIZE), + UBYTE_ARRAY(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(SIZE) + GENPTR_UBYTE_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT1B", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut1B( astI2P( *THIS ), key, *SIZE, VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + + +F77_SUBROUTINE(ast_mapput1d)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(SIZE), + DOUBLE_ARRAY(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(SIZE) + GENPTR_DOUBLE_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT1D", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut1D( astI2P( *THIS ), key, *SIZE, VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapput1r)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(SIZE), + REAL_ARRAY(VALUE), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(SIZE) + GENPTR_REAL_ARRAY(VALUE) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUT1R", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPut1F( astI2P( *THIS ), key, *SIZE, VALUE, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_SUBROUTINE(ast_mapputu)( INTEGER(THIS), + CHARACTER(KEY), + CHARACTER(COMMENT), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(COMMENT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_CHARACTER(COMMENT) + char *comment, *key; + + astAt( "AST_MAPPUTU", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + comment = astString( COMMENT, COMMENT_length ); + astMapPutU( astI2P( *THIS ), key, comment ); + astFree( key ); + astFree( comment ); + ) +} + +F77_LOGICAL_FUNCTION(ast_mapget0i)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET0I", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet0I( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget0s)( INTEGER(THIS), + CHARACTER(KEY), + WORD(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_WORD(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET0W", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet0S( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget0b)( INTEGER(THIS), + CHARACTER(KEY), + UBYTE(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_UBYTE(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET0W", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet0B( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget0d)( INTEGER(THIS), + CHARACTER(KEY), + DOUBLE(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_DOUBLE(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET0D", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet0D( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget0r)( INTEGER(THIS), + CHARACTER(KEY), + REAL(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_REAL(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET0R", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet0F( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget0c)( INTEGER(THIS), + CHARACTER(KEY), + CHARACTER(VALUE), + INTEGER(L), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(VALUE) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_CHARACTER(VALUE) + GENPTR_INTEGER(L) + F77_LOGICAL_TYPE(RESULT); + char *key; + const char *value; + int i; + + astAt( "AST_MAPGET0C", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + value = NULL; + RESULT = astMapGet0C( astI2P( *THIS ), key, &value ) ? F77_TRUE : F77_FALSE; + astFree( key ); + i = 0; + if( value ) { + for( ; value[ i ] && ( i < VALUE_length ); i++ ) { + VALUE[ i ] = value[ i ]; + } + *L = i; + } else { + *L = 0; + } + + if( VALUE ) { + for( ; i < VALUE_length; i++ ) { + VALUE[ i ] = ' '; + } + } + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget0a)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + AstObject *value; + + astAt( "AST_MAPGET0A", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet0A( astI2P( *THIS ), key, &value ) ? F77_TRUE : F77_FALSE; + astFree( key ); + *VALUE = astP2I( value ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget1i)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(MXVAL), + INTEGER(NVAL), + INTEGER_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(MXVAL) + GENPTR_INTEGER(NVAL) + GENPTR_INTEGER_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET1I", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet1I( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + + +F77_LOGICAL_FUNCTION(ast_mapget1d)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(MXVAL), + INTEGER(NVAL), + DOUBLE_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(MXVAL) + GENPTR_INTEGER(NVAL) + GENPTR_DOUBLE_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET1D", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet1D( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget1s)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(MXVAL), + INTEGER(NVAL), + WORD_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(MXVAL) + GENPTR_INTEGER(NVAL) + GENPTR_WORD_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET1W", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet1S( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget1b)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(MXVAL), + INTEGER(NVAL), + UBYTE_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(MXVAL) + GENPTR_INTEGER(NVAL) + GENPTR_UBYTE_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET1B", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet1B( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapget1r)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(MXVAL), + INTEGER(NVAL), + REAL_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(MXVAL) + GENPTR_INTEGER(NVAL) + GENPTR_REAL_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGET1R", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGet1F( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + + +F77_LOGICAL_FUNCTION(ast_mapget1a)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(MXVAL), + INTEGER(NVAL), + INTEGER_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(MXVAL) + GENPTR_INTEGER(NVAL) + GENPTR_INTEGER_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + AstObject **values; + int i; + + astAt( "AST_MAPGET1A", NULL, 0 ); + astWatchSTATUS( + values = astMalloc( sizeof( AstObject *)*(size_t) *MXVAL ); + key = astString( KEY, KEY_length ); + RESULT = astMapGet1A( astI2P( *THIS ), key, *MXVAL, NVAL, values ) ? F77_TRUE : F77_FALSE; + astFree( key ); + if( astOK ) { + for( i = 0; i < *NVAL; i++ ) VALUE[ i ] = astP2I( values[ i ] ); + } + astFree( values ); + + ) + return RESULT; +} + + +F77_LOGICAL_FUNCTION(ast_mapget1c)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(MXVAL), + INTEGER(NVAL), + CHARACTER_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(VALUE) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(MXVAL) + GENPTR_INTEGER(NVAL) + GENPTR_CHARACTER_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + char *values, *c, *d; + int i, j, term; + + astAt( "AST_MAPGET1C", NULL, 0 ); + astWatchSTATUS( + values = astMalloc( sizeof( char )*(size_t) (*MXVAL)*( VALUE_length + 1 ) ); + key = astString( KEY, KEY_length ); + RESULT = astMapGet1C( astI2P( *THIS ), key, VALUE_length + 1, *MXVAL, + NVAL, values ) ? F77_TRUE : F77_FALSE; + astFree( key ); + +/* Loop round each string value returned in the array */ + if( astOK ) { + c = values; + d = VALUE; + for( i = 0; i < *NVAL; i++ ) { + +/* Loop round each of character in the "i"th element of the returned + array. Copy characters from the work array until a terminating null is + found. Replace this null by a space and replace all subsequent + characters by spaces up to the end of the returned array element. */ + term = 0; + for( j = 0; j < VALUE_length; j++, d++, c++ ) { + if( term ) { + *d = ' '; + } else if( (*d = *c) == 0 ) { + *d = ' '; + term = 1; + } + } + +/* Skip over the extra character at the end of each element in the work + array. */ + c++; + } + } + astFree( values ); + ) + return RESULT; +} + + +F77_SUBROUTINE(ast_mapremove)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + char *key; + + astAt( "AST_MAPREMOVE", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astMapRemove( astI2P( *THIS ), key ); + astFree( key ); + ) +} + +F77_SUBROUTINE(ast_maprename)( INTEGER(THIS), + CHARACTER(OLDKEY), + CHARACTER(NEWKEY), + INTEGER(STATUS) + TRAIL(OLDKEY) + TRAIL(NEWKEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(OLDKEY) + GENPTR_CHARACTER(NEWKEY) + char *oldkey, *newkey; + + astAt( "AST_MAPRENAME", NULL, 0 ); + astWatchSTATUS( + oldkey = astString( OLDKEY, OLDKEY_length ); + newkey = astString( NEWKEY, NEWKEY_length ); + astMapRename( astI2P( *THIS ), oldkey, newkey ); + astFree( oldkey ); + astFree( newkey ); + ) +} + +F77_SUBROUTINE(ast_mapcopy)( INTEGER(THIS), + INTEGER(THAT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(THAT) + + astAt( "AST_MAPCOPY", NULL, 0 ); + astWatchSTATUS( + astMapCopy( astI2P( *THIS ), astI2P( *THAT ) ); + ) +} + +F77_INTEGER_FUNCTION(ast_mapsize)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_MAPSIZE", NULL, 0 ); + astWatchSTATUS( + RESULT = astMapSize( astI2P( *THIS ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_maplength)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + F77_INTEGER_TYPE(RESULT); + char *key; + + astAt( "AST_MAPLENGTH", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapLength( astI2P( *THIS ), key ); + astFree( key ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_maplenc)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + F77_INTEGER_TYPE(RESULT); + char *key; + + astAt( "AST_MAPLENGTH", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapLenC( astI2P( *THIS ), key ); + astFree( key ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_maptype)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + F77_INTEGER_TYPE(RESULT); + char *key; + + astAt( "AST_MAPTYPE", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapType( astI2P( *THIS ), key ); + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_maphaskey)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPHASKEY", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapHasKey( astI2P( *THIS ), key ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapdefined)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPDEFINED", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapDefined( astI2P( *THIS ), key ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +/* NO_CHAR_FUNCTION indicates that the f77.h method of returning a + character result doesn't work, so add an extra argument instead and + wrap this function up in a normal FORTRAN 77 function (in the file + keymap.f). */ +#if NO_CHAR_FUNCTION +F77_SUBROUTINE(ast_mapkey_a)( CHARACTER(RESULT), +#else +F77_SUBROUTINE(ast_mapkey)( CHARACTER_RETURN_VALUE(RESULT), +#endif + INTEGER(THIS), + INTEGER(INDEX), +#if NO_CHAR_FUNCTION + INTEGER(STATUS) + TRAIL(RESULT) ) { +#else + INTEGER(STATUS) ) { +#endif + GENPTR_CHARACTER(RESULT) + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(INDEX) + const char *result; + int i; + + astAt( "AST_MAPKEY", NULL, 0 ); + astWatchSTATUS( + result = astMapKey( astI2P( *THIS ), *INDEX - 1 ); + i = 0; + if ( astOK ) { /* Copy result */ + for ( ; result[ i ] && i < RESULT_length; i++ ) { + RESULT[ i ] = result[ i ]; + } + } + while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */ + ) +} + + +F77_LOGICAL_FUNCTION(ast_mapgetelemi)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + INTEGER_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_INTEGER_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGETELEMI", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGetElemI( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + + +F77_LOGICAL_FUNCTION(ast_mapgetelemd)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + DOUBLE_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_DOUBLE_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGETELEMD", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGetElemD( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapgetelems)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + WORD_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_WORD_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGETELEMW", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGetElemS( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapgetelemb)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + UBYTE_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_UBYTE_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGETELEMB", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGetElemB( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_mapgetelemr)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + REAL_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_REAL_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + + astAt( "AST_MAPGETELEMR", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGetElemF( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE; + astFree( key ); + ) + return RESULT; +} + + +F77_LOGICAL_FUNCTION(ast_mapgetelema)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + INTEGER_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_INTEGER_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + AstObject *ptr; + + astAt( "AST_MAPGETELEMA", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + RESULT = astMapGetElemA( astI2P( *THIS ), key, *ELEM - 1, &ptr ) ? F77_TRUE : F77_FALSE; + astFree( key ); + if( astOK ) *VALUE = astP2I( ptr ); + ) + return RESULT; +} + + +F77_LOGICAL_FUNCTION(ast_mapgetelemc)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + CHARACTER_ARRAY(VALUE), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(VALUE) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_CHARACTER_ARRAY(VALUE) + F77_LOGICAL_TYPE(RESULT); + char *key; + char *values, *c, *d; + int j; + + astAt( "AST_MAPGETELEMC", NULL, 0 ); + astWatchSTATUS( + values = astMalloc( sizeof( char )*(size_t) ( VALUE_length + 1 ) ); + key = astString( KEY, KEY_length ); + RESULT = astMapGetElemC( astI2P( *THIS ), key, VALUE_length + 1, *ELEM - 1, + values ) ? F77_TRUE : F77_FALSE; + astFree( key ); + +/* Copy characters from the work array until a terminating null is + found. Replace this null by a space and replace all subsequent + characters by spaces up to the end of the returned array element. */ + if( astOK ) { + c = values; + d = VALUE; + + for( j = 0; j < VALUE_length; j++, d++, c++ ) { + if( (*d = *c) == 0 ) { + *d = ' '; + break; + } + } + + for( ; j < VALUE_length; j++, d++ ) *d = ' '; + + } + astFree( values ); + ) + return RESULT; +} + + +F77_SUBROUTINE(ast_mapputelemi)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + INTEGER(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_INTEGER(VALUE) + char *key; + + astAt( "AST_MAPPUTELEMI", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astMapPutElemI( astI2P( *THIS ), key, *ELEM - 1, *VALUE ); + astFree( key ); + ) +} + + +F77_SUBROUTINE(ast_mapputelems)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + WORD(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_WORD(VALUE) + char *key; + + astAt( "AST_MAPPUTELEMW", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astMapPutElemS( astI2P( *THIS ), key, *ELEM - 1, *VALUE ); + astFree( key ); + ) +} + +F77_SUBROUTINE(ast_mapputelemb)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + UBYTE(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_UBYTE(VALUE) + char *key; + + astAt( "AST_MAPPUTELEMB", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astMapPutElemB( astI2P( *THIS ), key, *ELEM - 1, *VALUE ); + astFree( key ); + ) +} + +F77_SUBROUTINE(ast_mapputelemd)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + DOUBLE(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_DOUBLE(VALUE) + char *key; + + astAt( "AST_MAPPUTELEMD", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astMapPutElemD( astI2P( *THIS ), key, *ELEM - 1, *VALUE ); + astFree( key ); + ) +} + +F77_SUBROUTINE(ast_mapputelemr)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + REAL(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_REAL(VALUE) + char *key; + + astAt( "AST_MAPPUTELEMR", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astMapPutElemF( astI2P( *THIS ), key, *ELEM - 1, *VALUE ); + astFree( key ); + ) +} + + +F77_SUBROUTINE(ast_mapputelema)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + INTEGER(VALUE), + INTEGER(STATUS) + TRAIL(KEY) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_INTEGER(VALUE) + char *key; + + astAt( "AST_MAPPUTELEMA", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + astMapPutElemA( astI2P( *THIS ), key, *ELEM - 1, astI2P( *VALUE ) ); + astFree( key ); + ) +} + + +F77_SUBROUTINE(ast_mapputelemc)( INTEGER(THIS), + CHARACTER(KEY), + INTEGER(ELEM), + CHARACTER(VALUE), + INTEGER(STATUS) + TRAIL(KEY) + TRAIL(VALUE) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(KEY) + GENPTR_INTEGER(ELEM) + GENPTR_CHARACTER(VALUE) + char *key; + char *value; + + astAt( "AST_MAPPUTELEMC", NULL, 0 ); + astWatchSTATUS( + key = astString( KEY, KEY_length ); + value = astString( VALUE, VALUE_length ); + astMapPutElemC( astI2P( *THIS ), key, *ELEM - 1, value ); + astFree( key ); + astFree( value ); + ) +} + diff --git a/flutmap.c b/flutmap.c new file mode 100644 index 0000000..4ff5f0a --- /dev/null +++ b/flutmap.c @@ -0,0 +1,107 @@ +/* +*+ +* Name: +* flutmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST LutMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the LutMap class. + +* Routines Defined: +* AST_ISALUTMAP +* AST_LUTMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 8-JUL-1997 (RFWS): +* Original version. +*- +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "lutmap.h" /* C interface to the LutMap class */ + +F77_LOGICAL_FUNCTION(ast_isalutmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISALUTMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsALutMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_lutmap)( INTEGER(NLUT), + DOUBLE_ARRAY(LUT), + DOUBLE(START), + DOUBLE(INC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NLUT) + GENPTR_DOUBLE_ARRAY(LUT) + GENPTR_DOUBLE(START) + GENPTR_DOUBLE(INC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + + astAt( "AST_LUTMAP", NULL, 0 ); + astWatchSTATUS( + char *options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astLutMap( *NLUT, LUT, *START, *INC, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fluxframe.c b/fluxframe.c new file mode 100644 index 0000000..ab8a954 --- /dev/null +++ b/fluxframe.c @@ -0,0 +1,4490 @@ +/* +*class++ +* Name: +* FluxFrame + +* Purpose: +* Measured flux description. + +* Constructor Function: +c astFluxFrame +f AST_FLUXFRAME + +* Description: +* A FluxFrame is a specialised form of one-dimensional Frame which +* represents various systems used to represent the signal level in an +* observation. The particular coordinate system to be used is specified +* by setting the FluxFrame's System attribute qualified, as necessary, by +* other attributes such as the units, etc (see the description of the +* System attribute for details). +* +* All flux values are assumed to be measured at the same frequency or +* wavelength (as given by the SpecVal attribute). Thus this class is +* more appropriate for use with images rather than spectra. + +* Inheritance: +* The FluxFrame class inherits from the Frame class. + +* Attributes: +* In addition to those attributes common to all Frames, every +* FluxFrame also has the following attributes: +* +* - SpecVal: The spectral position at which the flux values are measured. + +* Functions: +c The FluxFrame class does not define any new functions beyond those +f The FluxFrame class does not define any new routines beyond those +* which are applicable to all Frames. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 6-DEC-2004 (DSB): +* Original version. +* 14-DEC-2004 (DSB): +* Added AST__SBRIGHT and AST__SBRIGHTW systems. +* 7-DEC-2005 (DSB): +* Free memory allocated by calls to astReadString. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 31-JAN-2007 (DSB): +* Modified so that a FluxFrame can be used as a template to find a +* FluxFrame contained within a CmpFrame. This involves changes in +* Match and the removal of the local versions of SetMaxAxes and +* SetMinAxes. +* 3-SEP-2007 (DSB): +* In SubFrame, since AlignSystem is extended by the FluxFrame class +* it needs to be cleared before invoking the parent SubFrame +* method in cases where the result Frame is not a FluxFrame. +* 2-OCT-2007 (DSB): +* In Overlay, clear AlignSystem as well as System before calling +* the parent overlay method. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS FluxFrame + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__FLUXDEN +#define LAST_SYSTEM AST__SBRIGHTW + +/* Define other numerical constants for use in this module. */ +#define GETATTRIB_BUFF_LEN 50 +#define GETLABEL_BUFF_LEN 200 +#define GETSYMBOL_BUFF_LEN 20 +#define GETTITLE_BUFF_LEN 200 + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "unit.h" /* Units management facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "frame.h" /* Parent Frame class */ +#include "fluxframe.h" /* Interface definition for this class */ +#include "mapping.h" /* Coordinate Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "zoommap.h" /* Scaling Mappings */ +#include "specframe.h" /* Spectral Frames */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are used or extended by this + class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstSystemType (* parent_getalignsystem)( AstFrame *, int * ); +static AstSystemType (* parent_getsystem)( AstFrame *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getdomain)( AstFrame *, int * ); +static const char *(* parent_getlabel)( AstFrame *, int, int * ); +static const char *(* parent_getsymbol)( AstFrame *, int, int * ); +static const char *(* parent_gettitle)( AstFrame *, int * ); +static const char *(* parent_getunit)( AstFrame *, int, int * ); +static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_setunit)( AstFrame *, int, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_setsystem)( AstFrame *, AstSystemType, int * ); +static void (* parent_clearsystem)( AstFrame *, int * ); +static void (* parent_clearunit)( AstFrame *, int, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetLabel_Buff[ 0 ] = 0; \ + globals->GetSymbol_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff[ 0 ] = 0; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(FluxFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(FluxFrame,Class_Init) +#define class_vtab astGLOBAL(FluxFrame,Class_Vtab) +#define getattrib_buff astGLOBAL(FluxFrame,GetAttrib_Buff) +#define getlabel_buff astGLOBAL(FluxFrame,GetLabel_Buff) +#define getsymbol_buff astGLOBAL(FluxFrame,GetSymbol_Buff) +#define gettitle_buff astGLOBAL(FluxFrame,GetTitle_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffers for strings returned by various functions. */ +static char getattrib_buff[ AST__FLUXFRAME_GETATTRIB_BUFF_LEN + 1 ]; +static char getlabel_buff[ AST__FLUXFRAME_GETLABEL_BUFF_LEN + 1 ]; +static char getsymbol_buff[ AST__FLUXFRAME_GETSYMBOL_BUFF_LEN + 1 ]; +static char gettitle_buff[ AST__FLUXFRAME_GETTITLE_BUFF_LEN + 1 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstFluxFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static int GetObjSize( AstObject *, int * ); +static AstSpecFrame *GetSpecFrame( AstFluxFrame *, int * ); +static AstSystemType DensitySystem( AstSystemType, int * ); +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static AstSystemType GetDensitySystem( AstFluxFrame *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static const char *DefUnit( AstSystemType, const char *, const char *, int * ); +static const char *DensityUnit( AstSystemType, int * ); +static const char *FluxSystemString( AstSystemType, int * ); +static const char *GetDensityUnit( AstFluxFrame *, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const char *SystemLabel( AstSystemType, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static int GetActiveUnit( AstFrame *, int * ); +static int MakeFluxMapping( AstFluxFrame *, AstFluxFrame *, AstSystemType, AstMapping **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static int UnitsOK( AstSystemType, const char *, int, const char *, const char *, int * ); +static void ClearUnit( AstFrame *, int, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void SetUnit( AstFrame *, int, const char *, int * ); + +static AstSystemType GetSystem( AstFrame *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); +static void ClearSystem( AstFrame *, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static double GetSpecVal( AstFluxFrame *, int * ); +static int TestSpecVal( AstFluxFrame *, int * ); +static void ClearSpecVal( AstFluxFrame *, int * ); +static void SetSpecVal( AstFluxFrame *, double, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* FluxFrame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the FluxFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + int len; /* Length of attrib string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* SpecVal. */ +/* -------- */ + if ( !strcmp( attrib, "specval" ) ) { + astClearSpecVal( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearSystem + +* Purpose: +* Clear the System attribute for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* void ClearSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astClearSystem protected +* method inherited from the Frame class). + +* Description: +* This function clears the System attribute for a FluxFrame. + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + AstSystemType newsys; /* System after clearing */ + AstSystemType oldsys; /* System before clearing */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* Save the original system */ + oldsys = astGetSystem( this_frame ); + +/* Use the parent ClearSystem method to clear the System value. */ + (*parent_clearsystem)( this_frame, status ); + +/* Get the default System. */ + newsys = astGetSystem( this_frame ); + +/* If the system has actually changed. */ + if( newsys != oldsys ) { + +/* Changing the System value will in general require the Units to change + as well. If the used has previously specified the units to be used with + the new system, then re-instate them (they are stored in the "usedunits" + array in the FluxFrame structure). Otherwise, clear the units so that + the default units will eb used with the new System. */ + if( (int) newsys < this->nuunits && this->usedunits && + this->usedunits[ (int) newsys ] ) { + (*parent_setunit)( this_frame, 0, this->usedunits[ (int) newsys ], status ); + } else { + (*parent_clearunit)( this_frame, 0, status ); + } + +/* Also, clear all attributes which have system-specific defaults. */ + astClearLabel( this_frame, 0 ); + astClearSymbol( this_frame, 0 ); + astClearTitle( this_frame ); + } + +} + +static void ClearUnit( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* ClearUnit + +* Purpose: +* Clear the value of the Unit string for a FluxFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* void ClearUnit( AstFrame *this_frame, int axis ) + +* Class Membership: +* FluxFrame member function (over-rides the astClearUnit method inherited +* from the Frame class). + +* Description: +* This function clears the Unit string for a specified axis of a +* FluxFrame. It also clears the UsedUnit item in the FluxFrame +* structure corresponding to the current System. + +* Parameters: +* this +* Pointer to the FluxFrame. +* axis +* The number of the axis (zero-based). +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + int system; /* The FluxFrame's System value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astClearUnit" ); + +/* Clear the UsedUnit item for the current System, if current set. */ + system = (int) astGetSystem( this ); + if( system < this->nuunits && this->usedunits ) { + this->usedunits[ system ] = astFree( this->usedunits[ system ] ); + } + +/* Use the parent method to clear the Unit attribute of the axis. */ + (*parent_clearunit)( this_frame, axis, status ); +} + +static const char *DefUnit( AstSystemType system, const char *method, + const char *class, int *status ){ +/* +* Name: +* DefUnit + +* Purpose: +* Return the default units for a flux coordinate system type. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *DefUnit( AstSystemType system, const char *method, +* const char *class, int *status ) + +* Class Membership: +* FluxFrame member function. + +* Description: +* This function returns a textual representation of the default +* units associated with the specified flux coordinate system. + +* Parameters: +* system +* The flux coordinate system. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* As tring describing the default units. This string follows the +* units syntax described in FITS WCS paper I "Representations of world +* coordinates in FITS" (Greisen & Calabretta). + +* Notes: +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Value to return */ + +/* Initialize */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get an identifier for the default units. */ + if( system == AST__FLUXDEN ) { + result = "W/m^2/Hz"; + + } else if( system == AST__FLUXDENW ) { + result = "W/m^2/Angstrom"; + + } else if( system == AST__SBRIGHT ) { + result = "W/m^2/Hz/arcmin**2"; + + } else if( system == AST__SBRIGHTW ) { + result = "W/m^2/Angstrom/arcmin**2"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "%s(%s): Corrupt %s contains illegal System " + "identification code (%d).", status, method, class, class, + (int) system ); + } + +/* Return the result. */ + return result; +} + +static AstSystemType DensitySystem( AstSystemType sys, int *status ) { +/* +* Name: +* DensitySystem + +* Purpose: +* Obtain the System describing the spectral density for a FluxFrame +* system. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* AstSystemType DensitySystem( AstSystemType sys, int *status ) + +* Class Membership: +* FluxFrame member function. + +* Description: +* This function returns AST__FREQ if the FluxFrame system describes +* a quantity measured per unit frequency, and returns AST__WAVELEN if +* the FluxFrame system describes a quantity measured per unit wavelength. + +* Parameters: +* sys +* A System value appropriate to a FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The density System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Categorise the supplied system. */ + if( sys == AST__FLUXDEN || sys == AST__SBRIGHT ) { + result = AST__FREQ; + + } else if( sys == AST__FLUXDENW || sys == AST__SBRIGHTW ) { + result = AST__WAVELEN; + + } else if( astOK ) { + astError( AST__INTER, "DensitySystem(FluxFrame): The " + "DensitySystem method does not yet support " + "FluxFrame system %d (AST internal programming error).", status, + sys ); + } + +/* Return the result. */ + return result; +} + +static const char *DensityUnit( AstSystemType sys, int *status ) { +/* +* Name: +* DensityUnit + +* Purpose: +* Obtain the default units for the spectral density of a FluxFrame +* system. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *DensityUnit( AstSystemType sys, int *status ) + +* Class Membership: +* FluxFrame member function. + +* Description: +* This function returns "Hz" if the FluxFrame system describes +* a quantity measured per unit frequency, and returns "Angstrom" if +* the FluxFrame system describes a quantity measured per unit wavelength. + +* Parameters: +* sys +* A FluxFrame system value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the Unit value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Categorise the supplied FluxFrame system. */ + if( sys == AST__FLUXDEN || sys == AST__SBRIGHT ) { + result = "Hz"; + + } else if( sys == AST__FLUXDENW || sys == AST__SBRIGHTW ) { + result = "Angstrom"; + + } else if( astOK ) { + astError( AST__INTER, "DensityUnit(FluxFrame): The DensityUnit " + "method does not yet support FluxFrame system %d (AST " + "internal programming error).", status, sys ); + } + +/* Return the result. */ + return result; +} + +static const char *FluxSystemString( AstSystemType system, int *status ) { +/* +* Name: +* FluxSystemString + +* Purpose: +* Convert a FluxFrame coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *FluxSystemString( AstSystemType system, int *status ) + +* Class Membership: +* FluxFrame member function + +* Description: +* This function converts a FluxFrame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. */ + switch ( system ) { + + case AST__FLUXDEN: + result = "FLXDN"; + break; + + case AST__FLUXDENW: + result = "FLXDNW"; + break; + + case AST__SBRIGHT: + result = "SFCBR"; + break; + + case AST__SBRIGHTW: + result = "SFCBRW"; + break; + + } + +/* Return the result pointer. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied FluxFrame, +* in bytes. + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + int result; /* Result value to return */ + int i; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the FluxFrame structure. */ + this = (AstFluxFrame *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + if( this && this->usedunits ) { + for( i = 0; i < this->nuunits; i++ ) { + result += astTSizeOf( this->usedunits[ i ] ); + } + result += astTSizeOf( this->usedunits ); + } + + result += astGetObjSize( this->specframe ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetActiveUnit + +* Purpose: +* Obtain the value of the ActiveUnit flag for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int GetActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function returns the value of the ActiveUnit flag for a +* FluxFrame, which is always 1. + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to use for the ActiveUnit flag (1). + +*/ + return 1; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a FluxFrame, formatted as a character string. + +* Parameters: +* this +* Pointer to the FluxFrame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the FluxFrame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the FluxFrame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + const char *result; /* Pointer value to return */ + double dval; /* Attribute value */ + int len; /* Length of attrib string */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* SpecVal */ +/* ------- */ + if ( !strcmp( attrib, "specval" ) ) { + dval = astGetSpecVal( this ); + if ( astOK ) { + if( dval != AST__BAD ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } else { + result = ""; + } + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetDensitySystem( AstFluxFrame *this, int *status ) { +/* +*+ +* Name: +* astGetDensitySystem + +* Purpose: +* Obtain the System describing the spectral density of a FluxFrame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fluxframe.h" +* AstSystemType astGetDensitySystem( AstFluxFrame *this ) + +* Class Membership: +* FluxFrame method. + +* Description: +* This function returns AST__FREQ if the FluxFrame system describes +* a quantity measured per unit frequency, and returns AST__WAVELEN if +* the FluxFrame system describes a quantity measured per unit wavelength. + +* Parameters: +* this +* Pointer to the FluxFrame. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return AST__BADSYSTEM; + +/* Get the FluxFrame system and categorise it. */ + return DensitySystem( astGetSystem( this ), status ); +} + +static const char *GetDensityUnit( AstFluxFrame *this, int *status ) { +/* +*+ +* Name: +* astGetDensityUnit + +* Purpose: +* Obtain the default units for the spectral density of a FluxFrame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fluxframe.h" +* const char *astGetDensityUnit( AstFluxFrame *this ) + +* Class Membership: +* FluxFrame method. + +* Description: +* This function returns "Hz" if the FluxFrame system describes +* a quantity measured per unit frequency, and returns "Angstrom" if +* the FluxFrame system describes a quantity measured per unit wavelength. + +* Parameters: +* this +* Pointer to the FluxFrame. + +* Returned Value: +* A pointer to a null-terminated string containing the Unit value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get the FluxFrame system and categorise it. */ + return DensityUnit( astGetSystem( this ), status ); +} + +static const char *GetDomain( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDomain + +* Purpose: +* Obtain a pointer to the Domain attribute string for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *GetDomain( AstFrame *this, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetDomain protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the Domain attribute string +* for a FluxFrame. + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Domain value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the FluxFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* If a Domain attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestDomain( this ) ) { + result = (*parent_getdomain)( this_frame, status ); + +/* Otherwise, provide a pointer to a suitable default string. */ + } else { + result = "FLUX"; + } + +/* Return the result. */ + return result; +} + +static const char *GetLabel( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetLabel + +* Purpose: +* Access the Label string for a FluxFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *GetLabel( AstFrame *this, int axis, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetLabel method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Label string for a specified axis +* of a FluxFrame. + +* Parameters: +* this +* Pointer to the FluxFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstMapping *map; /* Mapping between units */ + AstSystemType system; /* Code identifying type of flux coordinates */ + char *new_lab; /* Modified label string */ + const char *result; /* Pointer to label string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetLabel" ); + +/* Check if a value has been set for the required axis label string. If so, + invoke the parent astGetLabel method to obtain a pointer to it. */ + if ( astTestLabel( this, axis ) ) { + result = (*parent_getlabel)( this, axis, status ); + +/* Otherwise, identify the flux coordinate system described by the + FluxFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default label string. */ + if ( astOK ) { + result = strcpy( getlabel_buff, SystemLabel( system, status ) ); + getlabel_buff[ 0 ] = toupper( getlabel_buff[ 0 ] ); + +/* Modify this default to take account of the current value of the Unit + attribute, if set. */ + if( astTestUnit( this, axis ) ) { + +/* Find a Mapping from the default Units for the current System, to the + units indicated by the Unit attribute. This Mapping is used to modify + the existing default label appropriately. For instance, if the default + units is "Jy" and the actual units is "log(Jy)", then the default label + of "Flux density" is changed to "log( Flux density )". */ + map = astUnitMapper( DefUnit( system, "astGetLabel", + astGetClass( this ), status ), + astGetUnit( this, axis ), result, + &new_lab ); + if( new_lab ) { + result = strcpy( getlabel_buff, new_lab ); + new_lab = astFree( new_lab ); + } + +/* Annul the unused Mapping. */ + if( map ) map = astAnnul( map ); + + } + } + } + +/* Return the result. */ + return result; +} + +static AstSpecFrame *GetSpecFrame( AstFluxFrame *this, int *status ) { +/* +* Name: +* GetSpecFrame + +* Purpose: +* Get a pointer to a SpecFrame associated with a FluxFrame + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* AstSpecFrame *GetSpecFrame( AstFluxFrame *this, int *status ) + +* Class Membership: +* FluxFrame member function + +* Description: +* This function returns a SpecFrame describing the spectral system in +* which the FluxFrame's SpecVal attribute is stored. A default +* SpecFrame is created and returned if the no SpecFrame was supplied +* when the FluxFrame was created. + +* Parameters: +* this +* The FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the SpecFrame. It should be freed using astAnnul when no +* longer needed. + +* Notes: +* - A NULL pointer value is returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSpecFrame *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the FluxFrame contains a SpecFrame, return a clone of its pointer. */ + if( this->specframe ) { + result = astClone( this->specframe ); + +/* Otherwise, create a SpecFrame appropriate to the FluxFrames System. */ + } else { + result = astSpecFrame( "", status ); + astSetSystem( result, astGetDensitySystem( this ) ); + astSetUnit( result, 0, astGetDensityUnit( this ) ); + } + +/* Annul the result if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result pointer. */ + return result; +} + +static const char *GetSymbol( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetSymbol + +* Purpose: +* Obtain a pointer to the Symbol string for a FluxFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *GetSymbol( AstFrame *this, int axis, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetSymbol method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Symbol string for a specified axis +* of a FluxFrame. + +* Parameters: +* this +* Pointer to the FluxFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstMapping *map; /* Mapping between units */ + AstSystemType system; /* Code identifying type of sky coordinates */ + char *new_sym; /* Modified symbol string */ + const char *result; /* Pointer to symbol string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetSymbol" ); + +/* Check if a value has been set for the required axis symbol string. If so, + invoke the parent astGetSymbol method to obtain a pointer to it. */ + if ( astTestSymbol( this, axis ) ) { + result = (*parent_getsymbol)( this, axis, status ); + +/* Otherwise, identify the flux coordinate system described by the FluxFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default Symbol string. */ + if ( astOK ) { + + if( system == AST__FLUXDEN ) { + result = "S_nu"; + + } else if( system == AST__FLUXDENW ) { + result = "S_lambda"; + + } else if( system == AST__SBRIGHT ) { + result = "mu_nu"; + + } else if( system == AST__SBRIGHTW ) { + result = "mu_lambda"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "astGetSymbol(%s): Corrupt %s contains " + "invalid System identification code (%d).", status, + astGetClass( this ), astGetClass( this ), (int) system ); + } + +/* Modify this default to take account of the current value of the Unit + attribute, if set. */ + if( astTestUnit( this, axis ) ) { + +/* Find a Mapping from the default Units for the current System, to the + units indicated by the Unit attribute. This Mapping is used to modify + the existing default symbol appropriately. For instance, if the default + units is "Jy" and the actual units is "log(Jy)", then the default symbol + of "S_nu" is changed to "log( S_nu )". */ + map = astUnitMapper( DefUnit( system, "astGetSymbol", + astGetClass( this ), status ), + astGetUnit( this, axis ), result, + &new_sym ); + if( new_sym ) { + result = strcpy( getsymbol_buff, new_sym ); + new_sym = astFree( new_sym ); + } + +/* Annul the unused Mapping. */ + if( map ) map = astAnnul( map ); + + } + } + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetAlignSystem + +* Purpose: +* Obtain the AlignSystem attribute for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetAlignSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the AlignSystem attribute for a FluxFrame. + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The AlignSystem value. + +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* If a AlignSystem attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestAlignSystem( this ) ) { + result = (*parent_getalignsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__FLUXDEN; + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetSystem + +* Purpose: +* Obtain the System attribute for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* AstSystemType GetSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the System attribute for a FluxFrame. + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + AstMapping *map; /* Pointer to unit Mapping */ + AstSystemType i; /* System to check */ + AstSystemType result; /* Value to return */ + const char *units; /* FluxFrame units */ + int unitSet; /* Has a value been supplied for Unit? */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* See if a value has been assigned to the Unit attribute. */ + unitSet = astTestUnit( this_frame, 0 ); + +/* If a System attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestSystem( this ) ) { + result = (*parent_getsystem)( this_frame, status ); + +/* Otherwise, if the Unit attribute has been set, provide a suitable default + system based on the units. */ + } else if( unitSet ){ + +/* Loop round each known system value. If a Mapping can be found from the + current units to the default units for the system, then use the system as + the default system. */ + units = astGetUnit( this_frame, 0 ); + for( i = FIRST_SYSTEM; i <= LAST_SYSTEM; i++ ) { + map = astUnitMapper( units, DefUnit( i, "astGetSystem", + astGetClass( this ), status ), NULL, NULL ); + if( map ) { + map = astAnnul( map ); + result = i; + break; + } + } + +/* Otherwise, report an error. */ + if( result == AST__BADSYSTEM && astOK ) { + astError( AST__BADUN, "astGetSystem(%s): The current units (%s) " + "cannot be used with any of the supported flux systems.", status, + astGetClass( this ), astGetUnit( this_frame, 0 ) ); + } + +/* Otherwise, provide a suitable default based on the units. */ + } else { + result = AST__FLUXDEN; + } + +/* Return the result. */ + return result; +} + +static const char *GetTitle( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetTitle + +* Purpose: +* Obtain a pointer to the Title string for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *GetTitle( AstFrame *this_frame, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetTitle method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Title string for a FluxFrame. +* A pointer to a suitable default string is returned if no Title value has +* previously been set. + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + AstSpecFrame *sf; /* Pointer to SpecFrame structure */ + const char *result; /* Pointer to result string */ + const char *sv; /* Formatted SpecVal string */ + const char *su; /* Units string */ + double specval; /* SpecVal value */ + int pos; /* Buffer position to enter text */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* See if a Title string has been set. If so, use the parent astGetTitle + method to obtain a pointer to it. */ + if ( astTestTitle( this ) ) { + result = (*parent_gettitle)( this_frame, status ); + +/* Otherwise, we will generate a default Title string. */ + } else { + +/* Classify the coordinate system type and create an appropriate Title + string. */ + if ( astOK ) { + result = gettitle_buff; + +/* Begin with the system's default label. */ + pos = sprintf( gettitle_buff, "%s", SystemLabel( astGetSystem( this ), status ) ); + gettitle_buff[ 0 ] = toupper( gettitle_buff[ 0 ] ); + +/* Append the spectral position, if known. */ + specval = astGetSpecVal( this ); + sf = GetSpecFrame( this, status ); + if( specval != AST__BAD && sf ) { + sv = astFormat( sf, 0, specval ); + su = astGetUnit( sf, 0 ); + pos += sprintf( gettitle_buff + pos, " at = %s %s", sv, su ); + } + sf = astAnnul( sf ); + } + } + +/* If an error occurred, clear the returned pointer value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetUnit( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetUnit + +* Purpose: +* Obtain a pointer to the Unit string for a FluxFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *GetUnit( AstFrame *this_frame, int axis ) + +* Class Membership: +* FluxFrame member function (over-rides the astGetUnit method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Unit string for a specified axis +* of a FluxFrame. If the Unit attribute has not been set for the axis, a +* pointer to a suitable default string is returned instead. + +* Parameters: +* this +* Pointer to the FluxFrame. +* axis +* The number of the axis (zero-based) for which information is required. + +* Returned Value: +* A pointer to a null-terminated string containing the Unit value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + AstSystemType system; /* The FluxFrame's System value */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetUnit" ); + +/* If a value has been set for the Unit attribute, use the parent + GetUnit method to return a pointer to the required Unit string. */ + if( astTestUnit( this, axis ) ){ + result = (*parent_getunit)( this_frame, axis, status ); + +/* Otherwise, identify the flux coordinate system described by the + FluxFrame. */ + } else { + system = astGetSystem( this ); + +/* Return a string describing the default units. */ + result = DefUnit( system, "astGetUnit", astGetClass( this ), status ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +void astInitFluxFrameVtab_( AstFluxFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitFluxFrameVtab + +* Purpose: +* Initialise a virtual function table for a FluxFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "fluxframe.h" +* void astInitFluxFrameVtab( AstFluxFrameVtab *vtab, const char *name ) + +* Class Membership: +* FluxFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the FluxFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAFluxFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->GetDensitySystem = GetDensitySystem; + vtab->GetDensityUnit = GetDensityUnit; + + vtab->ClearSpecVal = ClearSpecVal; + vtab->TestSpecVal = TestSpecVal; + vtab->GetSpecVal = GetSpecVal; + vtab->SetSpecVal = SetSpecVal; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_getdomain = frame->GetDomain; + frame->GetDomain = GetDomain; + + parent_getsystem = frame->GetSystem; + frame->GetSystem = GetSystem; + parent_setsystem = frame->SetSystem; + frame->SetSystem = SetSystem; + parent_clearsystem = frame->ClearSystem; + frame->ClearSystem = ClearSystem; + + parent_getalignsystem = frame->GetAlignSystem; + frame->GetAlignSystem = GetAlignSystem; + + parent_getlabel = frame->GetLabel; + frame->GetLabel = GetLabel; + + parent_getsymbol = frame->GetSymbol; + frame->GetSymbol = GetSymbol; + + parent_gettitle = frame->GetTitle; + frame->GetTitle = GetTitle; + + parent_clearunit = frame->ClearUnit; + frame->ClearUnit = ClearUnit; + + parent_getunit = frame->GetUnit; + frame->GetUnit = GetUnit; + + parent_setunit = frame->SetUnit; + frame->SetUnit = SetUnit; + + parent_match = frame->Match; + frame->Match = Match; + + parent_overlay = frame->Overlay; + frame->Overlay = Overlay; + + parent_subframe = frame->SubFrame; + frame->SubFrame = SubFrame; + +/* Store replacement pointers for methods which will be over-ridden by new + member functions implemented here. */ + frame->GetActiveUnit = GetActiveUnit; + frame->TestActiveUnit = TestActiveUnit; + frame->ValidateSystem = ValidateSystem; + frame->SystemString = SystemString; + frame->SystemCode = SystemCode; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "FluxFrame", "Description of flux values" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the FluxFrame structure. */ + this = (AstFluxFrame *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->specframe, mode, extra, fail ); + + return result; + +} +#endif + +static int MakeFluxMapping( AstFluxFrame *target, AstFluxFrame *result, + AstSystemType align_sys, AstMapping **map, int *status ) { +/* +* Name: +* MakeFluxMapping + +* Purpose: +* Generate a Mapping between two FluxFrames. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int MakeFluxMapping( AstFluxFrame *target, AstFluxFrame *result, +* AstSystemType align_sys, MakeFluAstMapping **map, int *status ) + +* Class Membership: +* FluxFrame member function. + +* Description: +* This function takes two FluxFrames and generates a Mapping that +* converts between them, taking account of differences in their +* coordinate systems, reference frequency, etc. +* +* In order to cut down the number of transformations to be considered, +* the scheme works by first converting from the target frame to an +* "alignment" Frame, using the attributes of the target to define the +* transformation. A transformation is then found from the alignment +* frame to the required result Frame, using the attributes of the +* result to define the transformation. The alignment Frame is +* described by the supplied parameter "align_sys". + +* Parameters: +* target +* Pointer to the first FluxFrame. +* result +* Pointer to the second FluxFrame. +* align_sys +* The flux system in which to align the two FluxFrames. +* map +* Pointer to a location which is to receive a pointer to the +* returned Mapping. The forward transformation of this Mapping +* will convert from "target" coordinates to "result" +* coordinates, and the inverse transformation will convert in +* the opposite direction (all coordinate values in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Mapping could be generated, or zero if the two +* FluxFrames are sufficiently un-related that no meaningful Mapping +* can be produced (in which case a NULL Mapping pointer will be +* returned). + +* Notes: +* A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrameSet *fs; + AstMapping *map1; + AstMapping *map2; + AstMapping *map3; + AstMapping *map4; + AstMapping *map5; + AstMapping *smap; + AstMapping *smap_in; + AstMapping *smap_out; + AstMapping *tmap; + AstSpecFrame *sfin1; + AstSpecFrame *sfin2; + AstSpecFrame *sfout1; + AstSpecFrame *sfout2; + AstSystemType rsys_in; + AstSystemType rsys_out; + AstSystemType sys_in; + AstSystemType sys_out; + double specval2; + double specval; + double specval_in; + double specval_out; + double zoom; + int match; + int sb_in; + int sb_out; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise the returned values. */ + match = 0; + *map = NULL; + +/* Initialise to avoid compiler warnings. */ + map1 = NULL; + map2 = NULL; + map3 = NULL; + +/* Note the target and result System */ + rsys_in = astGetSystem( target ); + rsys_out = astGetSystem( result ); + +/* First get a Mapping which converts from the units used in the target + to the default units associated with the target's system. + ---------------------------------------------------------------------- */ + map1 = astUnitMapper( astGetUnit( target, 0 ), + DefUnit( rsys_in, "MakeFluxMapping", "FluxFrame", status ), + NULL, NULL ); + +/* If the target system is surface brightness, change it to the + corresponding flux density system. We are effectively converting from + surface brightness to the flux density normalised to unit area. Also + set flags indicating if the systems are surface brightness systems. */ + if( rsys_in == AST__SBRIGHT ) { + sys_in = AST__FLUXDEN; + sb_in = 1; + + } else if( rsys_in == AST__SBRIGHTW ) { + sys_in = AST__FLUXDENW; + sb_in = 1; + + } else { + sys_in = rsys_in; + sb_in = 0; + } + +/* Likewise if the result system is surface brightness, change it to the + corresponding flux density system. */ + if( rsys_out == AST__SBRIGHT ) { + sys_out = AST__FLUXDEN; + sb_out = 1; + + } else if( rsys_out == AST__SBRIGHTW ) { + sys_out = AST__FLUXDENW; + sb_out = 1; + + } else { + sys_out = rsys_out; + sb_out = 0; + } + +/* Assume at this point in the chain of coversions that we have target values + in some form of flux density system (either frequency or wavelength). The + precise units do not matter at this point (so long as they are + dimensionally correct for describing the relevant form of flux density). + When other systems are added (e.g. antenna temperature), some code + will have to come before this point which produces a Mapping from (e.g.) + antenna temperature to flux density. */ + + +/* Get a Mapping from the default units for the input flux density system + to the default units for the output flux density system. + ---------------------------------------------------------------------- */ + +/* If one but not both of the systems represent surface brightness, then + we cannot form a Mapping. */ + if( sb_in != sb_out ) { + zoom = AST__BAD; + +/* If the input and output flux density systems are the same, then the + required Mapping is a UnitMap. */ + } else if( sys_in == sys_out ) { + zoom = 1.0; + +/* Otherwise, the required Mapping is a zoom map in which the scale factor is + the rate of change of the input spectral system with respect to the output + spectral system, at the position given by the SpecVal attribute (we + cannot do the conversion if the SpecVal values in the target and result + differ). Each spectral system is either wavelength (in Angstrom) or + frequency (in Hz), depending on whether the associated flux density + system is "per Angstrom" or "per Hertz". The SpecVal value may be + stored in some other system, so the first job is to create SpecFrames + with the required system and units from the SpecFrames encapsulated + within the target and result FluxFrames. Take deep copies of the two + SpecFrames, and set their systems and units. */ + } else { + sfin1 = GetSpecFrame( target, status ); + sfin2 = astCopy( sfin1 ); + astSetSystem( sfin2, DensitySystem( sys_in, status ) ); + astSetUnit( sfin2, 0, DensityUnit( sys_in, status ) ); + + sfout1 = GetSpecFrame( result, status ); + sfout2 = astCopy( sfout1 ); + astSetSystem( sfout2, DensitySystem( sys_out, status ) ); + astSetUnit( sfout2, 0, DensityUnit( sys_out, status ) ); + +/* Indicate we do not yet have a zoom factor */ + zoom = AST__BAD; + +/* Get the Mapping from output to input spectral coordinate system */ + fs = astConvert( sfout2, sfin2, "" ); + if( fs ) { + tmap = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + +/* Simplify the Mapping. */ + smap = astSimplify( tmap ); + tmap = astAnnul( tmap ); + +/* We first need to transform the two SpecVal attributes into the input + coordinate system of the "smap" Mapping (i.e. the standardised result + FluxFrame), and check they are the same. For this we need the Mappings + from the SpecFrames stored in the FluxFrames to the modified copies + created above. */ + fs = astConvert( sfin1, sfin2, "" ); + if( fs ) { + smap_in = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + } else { + smap_in = NULL; + } + + fs = astConvert( sfout1, sfout2, "" ); + if( fs ) { + smap_out = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + } else { + smap_out = NULL; + } + +/* Convert the target's SpecVal into the standardised target system */ + specval = astGetSpecVal( target ); + astTran1( smap_in, 1, &specval, 1, &specval2 ); + +/* Now convert it into the standardised result system. Note, we need to + use "smap" in the inverse direction for this. */ + astTran1( smap, 1, &specval2, 0, &specval_in ); + +/* Convert the results's SpecVal into the standardised result system */ + specval = astGetSpecVal( result ); + astTran1( smap_out, 1, &specval, 1, &specval_out ); + +/* Check they are equal and good. */ + if( astEQUALS( specval_in, specval_out, 1.0E8 ) && specval_in != AST__BAD ) { + +/* If the siSimplified Mapping is a UnitMap the required rate of change + factor is 1.0. If it resuts in a ZoomMap, the required factor is + the zoom factor in the ZoomMap. */ + if( astIsAUnitMap( smap ) ) { + zoom = 1.0; + + } else if( astIsAZoomMap( smap ) ) { + zoom = astGetZoom( smap ); + +/* For any other type of Mapping, we must determine the rate of change factor + by differentiating the Mapping at the SpecVal position. */ + } else { + specval = 0.5*( specval_in + specval_out ); + zoom = astRate( smap, &specval, 0, 0 ); + } + } + +/* Free resources */ + if( smap_in ) smap_in = astAnnul( smap_in ); + if( smap_out ) smap_out = astAnnul( smap_out ); + smap = astAnnul( smap ); + } + + sfout1 = astAnnul( sfout1 ); + sfin1 = astAnnul( sfin1 ); + sfout2 = astAnnul( sfout2 ); + sfin2 = astAnnul( sfin2 ); + } + +/* Create the required zoom map if a scaling factor was found. */ + if( zoom != AST__BAD ) { + map2 = (AstMapping *) astZoomMap( 1, fabs( zoom ), "", status ); + } else { + map2 = NULL; + } + +/* Now get a Mapping which converts from the default units associated with + the results's system, to the units used in the result. + ----------------------------------------------------------------------- */ + map3 = astUnitMapper( DefUnit( rsys_out, "MakeFluxMapping", "FluxFrame", status ), + astGetUnit( result, 0 ), NULL, NULL ); + +/* Indicate a match was found and combine all Mapings in series. */ + if( map1 && map2 && map3 ) { + match = 1; + map4 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + map5 = (AstMapping *) astCmpMap( map4, map3, 1, "", status ); + +/* Return the simplified Mapping. */ + *map = astSimplify( map5 ); + +/* Free resources. */ + map4 = astAnnul( map4 ); + map5 = astAnnul( map5 ); + } + +/* Free resources. */ + if( map1 ) map1 = astAnnul( map1 ); + if( map2 ) map2 = astAnnul( map2 ); + if( map3 ) map3 = astAnnul( map3 ); + +/* If an error occurred, annul the returned Mapping and clear the returned + values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the protected astMatch method +* inherited from the Frame class). + +* Description: +* This function matches a "template" FluxFrame to a "target" Frame and +* determines whether it is possible to convert coordinates between them. +* If it is, a mapping that performs the transformation is returned along +* with a new Frame that describes the coordinate system that results when +* this mapping is applied to the "target" coordinate system. In addition, +* information is returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" and "template" +* Frames from which they are derived. + +* Parameters: +* template +* Pointer to the template FluxFrame. This describes the coordinate +* system (or set of possible coordinate systems) into which we wish to +* convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate system in +* which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the template FluxFrame axis from +* which it is derived. If it is not derived from any template +* FluxFrame axis, a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the target Frame axis from which it +* is derived. If it is not derived from any target Frame axis, a value +* of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will be +* returned if the requested coordinate conversion is possible. If +* returned, the forward transformation of this Mapping may be used to +* convert coordinates between the "target" Frame and the "result" +* Frame (see below) and the inverse transformation will convert in the +* opposite direction. +* result +* Address of a location where a pointer to a new Frame will be returned +* if the requested coordinate conversion is possible. If returned, this +* Frame describes the coordinate system that results from applying the +* returned Mapping (above) to the "target" coordinate system. In +* general, this Frame will combine attributes from (and will therefore +* be more specific than) both the target and the template Frames. In +* particular, when the template allows the possibility of transformaing +* to any one of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate conversion is +* possible. Otherwise zero is returned (this will not in itself result in +* an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* This implementation addresses the matching of a FluxFrame class +* object to any other class of Frame. A FluxFrame will match any class +* of FluxFrame (i.e. possibly from a derived class) but will not match +* a less specialised class of Frame. +*/ + +/* Local Variables: */ + AstFrame *frame0; /* Pointer to Frame underlying axis 0 */ + AstFluxFrame *template; /* Pointer to template FluxFrame structure */ + int iaxis0; /* Axis index underlying axis 0 */ + int iaxis; /* Axis index */ + int match; /* Coordinate conversion possible? */ + int target_axis0; /* Index of FluxFrame axis in the target */ + int target_naxes; /* Number of target axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the template FluxFrame structure. */ + template = (AstFluxFrame *) template_frame; + +/* Obtain the number of axes in the target Frame. */ + target_naxes = astGetNaxes( target ); + +/* The first criterion for a match is that the template matches as a + Frame class object. This ensures that the number of axes (1) and + domain, etc. of the target Frame are suitable. Invoke the parent + "astMatch" method to verify this. */ + match = (*parent_match)( template_frame, target, matchsub, + template_axes, target_axes, map, result, status ); + +/* If a match was found, annul the returned objects, which are not + needed, but keep the memory allocated for the axis association + arrays, which we will re-use. */ + if ( astOK && match ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + } + +/* If OK so far, obtain pointers to the primary Frames which underlie + all target axes. Stop when a FluxFrame axis is found. */ + if ( match && astOK ) { + match = 0; + for( iaxis = 0; iaxis < target_naxes; iaxis++ ) { + astPrimaryFrame( target, iaxis, &frame0, &iaxis0 ); + if( astIsAFluxFrame( frame0 ) ) { + frame0 = astAnnul( frame0 ); + target_axis0 = iaxis; + match = 1; + break; + } else { + frame0 = astAnnul( frame0 ); + } + } + } + +/* Check at least one FluxFrame axis was found it the target. Store the + axis associataions. */ + if( match && astOK ) { + (*template_axes)[ 0 ] = 0; + (*target_axes)[ 0 ] = target_axis0; + +/* Use the target's "astSubFrame" method to create a new Frame (the + result Frame) with copies of the target axes in the required + order. This process also overlays the template attributes on to the + target Frame and returns a Mapping between the target and result + Frames which effects the required coordinate conversion. */ + match = astSubFrame( target, template, 1, *target_axes, *template_axes, + map, result ); + } + +/* If an error occurred, or conversion to the result Frame's + coordinate system was not possible, then free all memory, annul the + returned objects, and reset the returned value. */ + if ( !astOK || !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void Overlay( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template FluxFrame on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the protected astOverlay method +* inherited from the Frame class). + +* Description: +* This function overlays attributes of a FluxFrame (the "template") on to +* another Frame, so as to over-ride selected attributes of that second +* Frame. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which a Frame acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. +* +* Note that if the result Frame is a FluxFrame and a change of flux +* coordinate system occurs as a result of overlaying its System +* attribute, then some of its original attribute values may no +* longer be appropriate (e.g. the Title, or attributes describing +* its axes). In this case, these will be cleared before overlaying +* any new values. + +* Parameters: +* template +* Pointer to the template FluxFrame, for which values should have been +* explicitly set for any attribute which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of the +* "result" Frame (see below). For each axis in the result frame, the +* corresponding element of this array should contain the (zero-based) +* index of the template axis to which it corresponds. This array is used +* to establish from which template axis any axis-dependent attributes +* should be obtained. +* +* If any axis in the result Frame is not associated with a template +* axis, the corresponding element of this array should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - In general, if the result Frame is not from the same class as the +* template FluxFrame, or from a class derived from it, then attributes may +* exist in the template FluxFrame which do not exist in the result Frame. +* In this case, these attributes will not be transferred. +*/ + + +/* Local Variables: */ + AstFluxFrame *resff; /* Result FluxFrame */ + AstFluxFrame *tmpff; /* Template FluxFrame */ + AstSystemType new_alignsystem;/* Code identifying alignment coords */ + AstSystemType new_system; /* Code identifying new cordinates */ + AstSystemType old_system; /* Code identifying old coordinates */ + const char *method; /* Pointer to method string */ + const char *new_class; /* Pointer to template class string */ + const char *old_class; /* Pointer to result class string */ + int fluxframe; /* Result Frame is a FluxFrame? */ + int resetSystem; /* Was the template System value cleared? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise strings used in error messages. */ + new_class = astGetClass( template ); + old_class = astGetClass( result ); + method = "astOverlay"; + +/* Get the old and new systems. */ + old_system = astGetSystem( result ); + new_system = astGetSystem( template ); + +/* If the result Frame is a FluxFrame, we must test to see if overlaying its + System attribute will change the type of coordinate system it describes. + Determine the value of this attribute for the result and template + FluxFrames. */ + resetSystem = 0; + fluxframe = astIsAFluxFrame( result ); + if( fluxframe ) { + +/* If the coordinate system will change, any value already set for the result + FluxFrame's Title will no longer be appropriate, so clear it. */ + if ( new_system != old_system ) { + astClearTitle( result ); + +/* If the systems have the same default units, we can retain the current + Unit value. */ + if( strcmp( DefUnit( new_system, method, new_class, status ), + DefUnit( old_system, method, old_class, status ) ) ) { + astClearUnit( result, 0 ); + } + +/* If necessary, clear inappropriate values for all those axis attributes + whose access functions are over-ridden by this class (these access functions + will then provide suitable defaults appropriate to the new coordinate system + instead). */ + astClearLabel( result, 0 ); + astClearSymbol( result, 0 ); + } + +/* Transfer the default SpecVal value and the SpecFrame. */ + resff = (AstFluxFrame *) result; + tmpff = (AstFluxFrame *) template; + resff->defspecval = tmpff->defspecval; + if( resff->specframe ) (void) astAnnul( resff->specframe ); + resff->specframe = tmpff->specframe ? astCopy( tmpff->specframe ) : NULL; + +/* If the result Frame is not a FluxFrame, we must temporarily clear the + System and AlignSystem values since the values used by this class are only + appropriate to this class. */ + } else { + if( astTestSystem( template ) ) { + astClearSystem( template ); + + new_alignsystem = astGetAlignSystem( template ); + astClearAlignSystem( template ); + + resetSystem = 1; + } + } + +/* Invoke the parent class astOverlay method to transfer attributes inherited + from the parent class. */ + (*parent_overlay)( template, template_axes, result, status ); + +/* Reset the System and AlignSystem values if necessary */ + if( resetSystem ) { + astSetSystem( template, new_system ); + astSetAlignSystem( template, new_alignsystem ); + } + +/* Check if the result Frame is a FluxFrame or from a class derived from + FluxFrame. If not, we cannot transfer FluxFrame attributes to it as it is + insufficiently specialised. In this case simply omit these attributes. */ + if ( fluxframe && astOK ) { + +/* Define macros that test whether an attribute is set in the template and, + if so, transfers its value to the result. */ +#define OVERLAY(attribute) \ + if ( astTest##attribute( template ) ) { \ + astSet##attribute( result, astGet##attribute( template ) ); \ + } + +/* Use the macro to transfer each FluxFrame attribute in turn. */ + OVERLAY(SpecVal) + + + } + +/* Undefine macros local to this function. */ +#undef OVERLAY +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* FluxFrame member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a FluxFrame, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the FluxFrame. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This protected method is intended to be invoked by the Object astSet +* method and makes additional attributes accessible to it. +*/ + +/* Local Vaiables: */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + double dval; /* Floating point attribute value */ + int len; /* Length of setting string */ + int nc; /* No. of characters read */ + int ulen; /* Used length of setting string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Obtain the used length of the setting string. */ + ulen = astChrLen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* SpecVal. */ +/* -------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "specval= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetSpecVal( this, dval ); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetSystem( AstFrame *this_frame, AstSystemType newsys, int *status ) { +/* +* Name: +* SetSystem + +* Purpose: +* Set the System attribute for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* void SetSystem( AstFrame *this_frame, AstSystemType newsys, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astSetSystem protected +* method inherited from the Frame class). + +* Description: +* This function sets the System attribute for a FluxFrame. + +* Parameters: +* this +* Pointer to the FluxFrame. +* newsys +* The new System value to be stored. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to FluxFrame structure */ + AstSystemType oldsys; /* Original System value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* Save the original System value */ + oldsys = astGetSystem( this_frame ); + +/* Use the parent SetSystem method to store the new System value. */ + (*parent_setsystem)( this_frame, newsys, status ); + +/* If the system has changed... */ + if( oldsys != newsys ) { + +/* Changing the System value will in general require the Units to change + as well. If the user has previously specified the units to be used with + the new system, then re-instate them (they are stored in the "usedunits" + array in the FluxFrame structure). Otherwise, clear the units so that + the default units will eb used with the new System. */ + if( (int) newsys < this->nuunits && this->usedunits && + this->usedunits[ (int) newsys ] ) { + (*parent_setunit)( this_frame, 0, this->usedunits[ (int) newsys ], status ); + } else { + (*parent_clearunit)( this_frame, 0, status ); + } + +/* Also, clear all attributes which have system-specific defaults. */ + astClearLabel( this_frame, 0 ); + astClearSymbol( this_frame, 0 ); + astClearTitle( this_frame ); + } +} + +static void SetUnit( AstFrame *this_frame, int axis, const char *value, int *status ) { +/* +* Name: +* SetUnit + +* Purpose: +* Set a pointer to the Unit string for a FluxFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* void SetUnit( AstFrame *this_frame, int axis, const char *value ) + +* Class Membership: +* FluxFrame member function (over-rides the astSetUnit method inherited +* from the Frame class). + +* Description: +* This function stores a pointer to the Unit string for a specified axis +* of a FluxFrame. It also stores the string in the "usedunits" array +* in the FluxFrame structure, in the element associated with the +* current System. + +* Parameters: +* this +* Pointer to the FluxFrame. +* axis +* The number of the axis (zero-based) for which information is required. +* unit +* The new string to store. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + AstSystemType system; /* The FluxFrame's System value */ + int i; /* Loop counter */ + int isystem; /* The FluxFrame's System value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Use the parent SetUnit method to store the value in the Axis + structure */ + (*parent_setunit)( this_frame, axis, value, status ); + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astSetUnit" ); + +/* If the new units are appropriate for the current System, store the + supplied value as the UsedUnit for the current System. First ensure the + array is big enough. Free any previous value stored for the current + system. */ + system = astGetSystem( this ); + if( UnitsOK( system, value, 0, "astSetUnit", astGetClass( this ), status ) ) { + isystem = (int) astGetSystem( this ); + if( isystem >= this->nuunits ) { + this->usedunits = astGrow( this->usedunits, isystem + 1, + sizeof(char *) ); + if( astOK ) { + for( i = this->nuunits; i < isystem + 1; i++ ) this->usedunits[ i ] = NULL; + this->nuunits = isystem + 1; + } + } + +/* Now store a copy of the value, if it is different to the stored string. */ + if( astOK && ( !this->usedunits[ isystem ] || + strcmp( this->usedunits[ isystem ], value ) ) ) { + this->usedunits[ isystem ] = astStore( this->usedunits[ isystem ], + value, strlen( value ) + 1 ); + } + +/* If the new units are not appropriate for the current System, clear the + System value. Use the parent ClearSystem function since the + astClearSystem implemented by this class will clear the units. */ + } else { + (*parent_clearsystem)( this_frame, status ); + } + +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a FluxFrame and convert to the new coordinate +* system. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the protected astSubFrame +* method inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the axes +* from a "target" FluxFrame and creates a new Frame with copies of +* the selected axes assembled in the requested order. It then +* optionally overlays the attributes of a "template" Frame on to the +* result. It returns both the resulting Frame and a Mapping that +* describes how to convert between the coordinate systems described by +* the target and result Frames. If necessary, this Mapping takes +* account of any differences in the Frames' attributes due to the +* influence of the template. + +* Parameters: +* target +* Pointer to the target FluxFrame, from which axes are to be +* selected. +* template +* Pointer to the template Frame, from which new attributes for the +* result Frame are to be obtained. Optionally, this may be NULL, in +* which case no overlaying of template attributes will be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This number may +* be greater than or less than the number of axes in this Frame (or +* equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving a list +* of the (zero-based) axis indices of the axes to be selected from the +* target FluxFrame. The order in which these are given determines +* the order in which the axes appear in the result Frame. If any of the +* values in this array is set to -1, the corresponding result axis will +* not be derived from the target Frame, but will be assigned default +* attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This should +* contain a list of the template axes (given as zero-based axis indices) +* with which the axes of the result Frame are to be associated. This +* array determines which axes are used when overlaying axis-dependent +* attributes of the template on to the result. If any element of this +* array is set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not used and +* a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned Mapping. +* The forward transformation of this Mapping will describe how to +* convert coordinates from the coordinate system described by the target +* FluxFrame to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is possible +* between the target and the result Frame. Otherwise zero is returned and +* *map and *result are returned as NULL (but this will not in itself +* result in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not always +* be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* - This implementation addresses the selection of axes from a +* FluxFrame object. This results in another object of the same class +* only if the single FluxFrame axis is selected exactly once. +* Otherwise, the result is a Frame class object which inherits the +* FluxFrame's axis information (if appropriate) but none of the other +* properties of a FluxFrame. +* - In the event that a FluxFrame results, the returned Mapping will +* take proper account of the relationship between the target and result +* coordinate systems. +* - In the event that a Frame class object results, the returned Mapping +* will only represent a selection/permutation of axes. + +* Implementation Deficiencies: +* - Any axis selection is currently permitted. Probably this should be +* restricted so that each axis can only be selected once. The +* astValidateAxisSelection method will do this but currently there are bugs +* in the CmpFrame class that cause axis selections which will not pass this +* test. Install the validation when these are fixed. +*/ + +/* Local Variables: */ + AstFluxFrame *target; /* Pointer to the FluxFrame structure */ + AstFluxFrame *temp; /* Pointer to copy of target FluxFrame */ + AstSystemType align_sys; /* System in which to align the FluxFrames */ + int match; /* Coordinate conversion is possible? */ + int report; /* Report errors if FluxFrames cannot be aligned? */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the target FluxFrame structure. */ + target = (AstFluxFrame *) target_frame; + +/* Result is a FluxFrame. */ +/* -------------------------- */ +/* Check if the result Frame is to have one axis obtained by selecting + the single target FluxFrame axis. If so, the result will also be + a FluxFrame. */ + if ( ( result_naxes == 1 ) && ( target_axes[ 0 ] == 0 ) ) { + +/* Form the result from a copy of the target. */ + *result = astCopy( target ); + +/* Initialise a flag to indicate that MakeFluxMapping should not report + errors if no Mapping can be created. */ + report = 0; + +/* If required, overlay the template attributes on to the result FluxFrame. + Also get the system in which to align the two FluxFrames. These are the + values from the template (if there is a template). */ + if ( template ) { + astOverlay( template, template_axes, *result ); + if( astIsAFluxFrame( template ) ) { + align_sys = astGetAlignSystem( template ); + +/* Since we now know that both the template and target are FluxFrames, it + should usually be possible to convert betwen them. If conversion is + *not* possible then the user will probably be interested in knowing the + reason why conversion is not possible. Therefore, indicate that + MakeFluxMapping should report errors if no Mapping can be created. */ + report = 1; + + } else { + align_sys = astGetAlignSystem( target ); + } + +/* If no template was supplied, align in the System of the target. */ + } else { + align_sys = astGetSystem( target ); + } + +/* Generate a Mapping that takes account of changes in the coordinate system + between the target FluxFrame and the result FluxFrame. If this Mapping can + be generated, set "match" to indicate that coordinate conversion is + possible. If the template is a fluxframe, report errors if a match is not + possible. */ + match = ( MakeFluxMapping( target, (AstFluxFrame *) *result, + align_sys, map, status ) != 0 ); + +/* Result is not a FluxFrame. */ +/* ------------------------------ */ +/* In this case, we select axes as if the target were from the Frame + class. However, since the resulting data will then be separated + from their enclosing FluxFrame, default attribute values may differ + if the methods for obtaining them were over-ridden by the FluxFrame + class. To overcome this, we ensure that these values are explicitly + set for the result Frame (rather than relying on their defaults). */ + } else { + +/* Make a temporary copy of the target FluxFrame. We will explicitly + set the attribute values in this copy so as not to modify the original. */ + temp = astCopy( target ); + +/* Define a macro to test if an attribute is set. If not, set it + explicitly to its default value. */ +#define SET(attribute) \ + if ( !astTest##attribute( temp ) ) { \ + astSet##attribute( temp, astGet##attribute( temp ) ); \ + } + +/* Set attribute values which apply to the Frame as a whole and which + we want to retain, but whose defaults are over-ridden by the + FluxFrame class. */ + SET(Domain) + SET(Title) + +/* Define a macro to test if an attribute is set for axis zero (the only + axis of a FluxFrame). If not, set it explicitly to its default value. */ +#define SET_AXIS(attribute) \ + if ( !astTest##attribute( temp, 0 ) ) { \ + astSet##attribute( temp, 0, \ + astGet##attribute( temp, 0 ) ); \ + } + +/* Use this macro to set explicit values for all the axis attributes + for which the FluxFrame class over-rides the default value. */ + SET_AXIS(Label) + SET_AXIS(Symbol) + SET_AXIS(Unit) + +/* Clear attributes which have an extended range of values allowed by + this class. */ + astClearSystem( temp ); + astClearAlignSystem( temp ); + +/* Invoke the astSubFrame method inherited from the Frame class to + produce the result Frame by selecting the required set of axes and + overlaying the template Frame's attributes. */ + match = (*parent_subframe)( (AstFrame *) temp, template, + result_naxes, target_axes, template_axes, + map, result, status ); + +/* Delete the temporary copy of the target FluxFrame. */ + temp = astDelete( temp ); + } + +/* If an error occurred or no match was found, annul the returned + objects and reset the returned result. */ + if ( !astOK || !match ) { + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef SET +#undef SET_AXIS +} + +static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astSystemCode method +* inherited from the Frame class). + +* Description: +* This function converts a string used for the external +* description of a coordinate system into a FluxFrame +* coordinate system type code (System attribute value). It is the +* inverse of the astSystemString function. + +* Parameters: +* this +* The Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the sky coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the sky coordinate +* system description was not recognised. This does not produce an +* error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" string against each possibility and assign the + result. */ + if ( astChrMatch( "FLXDN", system ) ) { + result = AST__FLUXDEN; + + } else if ( astChrMatch( "FLXDNW", system ) ) { + result = AST__FLUXDENW; + + }else if ( astChrMatch( "SFCBR", system ) ) { + result = AST__SBRIGHT; + + } else if ( astChrMatch( "SRCBR", system ) ) { + result = AST__SBRIGHTW; + + } + +/* Return the result. */ + return result; +} + +static const char *SystemLabel( AstSystemType system, int *status ) { +/* +* Name: +* SystemLabel + +* Purpose: +* Return a label for a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *SystemLabel( AstSystemType system, int *status ) + +* Class Membership: +* FluxFrame member function. + +* Description: +* This function converts a FluxFrame coordinate system type code +* (System attribute value) into a descriptive string for human readers. + +* Parameters: +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the sky coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. */ + switch ( system ) { + + case AST__FLUXDEN: + result = "flux density"; + break; + + case AST__FLUXDENW: + result = "flux wavelength density"; + break; + + case AST__SBRIGHT: + result = "surface brightness"; + break; + + case AST__SBRIGHTW: + result = "surface brightness (per wavelength)"; + break; + + } + +/* Return the result pointer. */ + return result; +} + +static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astSystemString method +* inherited from the Frame class). + +* Description: +* This function converts a FluxFrame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* The Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + + return FluxSystemString( system, status ); +} + +static int TestActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* TestActiveUnit + +* Purpose: +* Test the ActiveUnit flag for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int TestActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astTestActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function test the value of the ActiveUnit flag for a FluxFrame, +* which is always "unset". + +* Parameters: +* this +* Pointer to the FluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The result of the test (0). + +*/ + return 0; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a FluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a FluxFrame's attributes. + +* Parameters: +* this +* Pointer to the FluxFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + int len; /* Length of attrib string */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* SpecVal. */ +/* -------- */ + if ( !strcmp( attrib, "specval" ) ) { + result = astTestSpecVal( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int UnitsOK( AstSystemType system, const char *units, int report, + const char *method, const char *class, int *status ) { +/* +* Name: +* UnitsOK + +* Purpose: +* Check if a units string is appropriate for the current System. + +* Type: +* Private function. + +* Synopsis: +* #include "fluxframe.h" +* int UnitsOK( AstSystemType system, const char *units, int report, +* const char *method, const char *class, int *status ) + +* Class Membership: +* FluxFrame member function + +* Description: +* This function returns a non-zero value if the supplied units string +* can be mapped to the defaultunits for the current System in the +* supplied FluxFrame. + +* Parameters: +* system +* The system type to check. +* unit +* The units string to check. +* report +* Should an error be reported if the units and system are +* inconsistent? +* method +* String holding a method name to be used in error messages. +* class +* String holding a class name to be used in error messages. +* status +* Pointer to the inherited status variable. + +* Returns Value: +* Non-zero if the units string can be used to describe the current +* flux System. Zero otherwise. + +*/ + +/* Local Variables: */ + AstMapping *map; + int result; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get the Mapping from the default units for the supplied system to the + supplied Units. */ + map = astUnitMapper( DefUnit( system, method, class, status ), units, NULL, NULL ); + +/* If a Mapping was found succesfully, annul it and return non-zero. + Otherwise return zero. */ + if( map ) { + result = 1; + map = astAnnul( map ); + + } else { + result = 0; + +/* Report an error if required. */ + if( report && astOK ) { + astError( AST__BADUN, "%s(%s): The units (%s) and system (%s) " + "within the supplied %s are inconsistent.", status, method, + class, units, FluxSystemString( system, status ), class ); + } + } + +/* Return the result. */ + return result; +} + +static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) { +/* +* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "fluxframe.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* FluxFrame member function (over-rides the astValidateSystem method +* inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST__BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the value is out of bounds, report an error. */ + if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) { + astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System " + "or AlignSystem attribute of a %s.", status, method, + astGetClass( this ), (int) system, astGetClass( this ) ); + +/* Otherwise, return the supplied value. */ + } else { + result = system; + } + +/* Return the result. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* +*att++ +* Name: +* SpecVal + +* Purpose: +* The spectral position at which flux values are measured. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the spectral position (frequency, wavelength, +* etc.), at which the values described by the FluxFrame are measured. +* It is used when determining the Mapping between between FluxFrames. +* +* The default value and spectral system used for this attribute are +* both specified when the FluxFrame is created. + +* Applicability: +* FluxFrame +* All FluxFrames have this attribute. + +*att-- +*/ +astMAKE_CLEAR(FluxFrame,SpecVal,specval,AST__BAD) +astMAKE_GET(FluxFrame,SpecVal,double,AST__BAD,((this->specval!=AST__BAD)?this->specval:this->defspecval)) +astMAKE_SET(FluxFrame,SpecVal,double,specval,value) +astMAKE_TEST(FluxFrame,SpecVal,( this->specval != AST__BAD )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for FluxFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for FluxFrame objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstFluxFrame *in; /* Pointer to input FluxFrame */ + AstFluxFrame *out; /* Pointer to output FluxFrame */ + char *usedunit; /* Pointer to an element of usedunits array */ + int i; /* Loop count */ + int nused; /* Size of "usedunits" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output FluxFrames. */ + in = (AstFluxFrame *) objin; + out = (AstFluxFrame *) objout; + +/* Nullify the pointers stored in the output object since these will + currently be pointing at the input data (since the output is a simple + byte-for-byte copy of the input). Otherwise, the input data could be + freed by accidient if the output object is deleted due to an error + occuring in this function. */ + out->usedunits = NULL; + out->specframe = NULL; + +/* Store the last used units in the output SpecMap. */ + if( in && in->usedunits ) { + nused = in->nuunits; + out->usedunits = astMalloc( nused*sizeof( char * ) ); + if( out->usedunits ) { + for( i = 0; i < nused; i++ ) { + usedunit = in->usedunits[ i ]; + if( usedunit ) { + out->usedunits[ i ] = astStore( NULL, usedunit, + strlen( usedunit ) + 1 ); + } else { + out->usedunits[ i ] = NULL; + } + } + } + } + +/* Copy the SpecFrame */ + if( in->specframe ) out->specframe = astCopy( in->specframe ); + +/* If an error has occurred, free the output resources. */ + if( !astOK ) Delete( (AstObject *) out, status ); + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for FluxFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for FluxFrame objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstFluxFrame *this; + int i; + +/* Release the memory referred to in the FluxFrame structure. */ + this = (AstFluxFrame *) obj; + if( this && this->usedunits ) { + for( i = 0; i < this->nuunits; i++ ) { + this->usedunits[ i ] = astFree( this->usedunits[ i ] ); + } + this->usedunits = astFree( this->usedunits ); + } + +/* Annulthe SpecFrame. */ + if( this->specframe ) this->specframe = astAnnul( this->specframe ); + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for FluxFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the FluxFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the FluxFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFluxFrame *this; /* Pointer to the FluxFrame structure */ + char buff[ 20 ]; /* Buffer for item name */ + char comm[ 50 ]; /* Buffer for comment */ + double dval; /* Double value */ + int i; /* Loop count */ + int j; /* Loop count */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FluxFrame structure. */ + this = (AstFluxFrame *) this_object; + +/* Write out values representing the instance variables for the + FluxFrame class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* SpecVal. */ +/* -------- */ + set = TestSpecVal( this, status ); + dval = set ? GetSpecVal( this, status ) : astGetSpecVal( this ); + if( dval != AST__BAD ) { + astWriteDouble( channel, "SpcVl", set, 0, dval, "Spectral position" ); + } + +/* The SpecFrame */ +/* ------------- */ + if( this->specframe ) { + astWriteObject( channel, "SpcFr", 1, 0, this->specframe, "SpcVl coord system" ); + } + +/* Default SpecVal. */ +/* ---------------- */ + if( this->defspecval != AST__BAD ) { + astWriteDouble( channel, "DfSpc", 1, 0, this->defspecval, "Default spectral position" ); + } + +/* UsedUnits */ +/* --------- */ + if( this->usedunits ) { + for( i = 0; i < this->nuunits; i++ ) { + if( this->usedunits[ i ] ) { + sprintf( buff, "U%s", astSystemString( this, (AstSystemType) i )); + for( j = 2; j < strlen( buff ); j++ ) buff[ j ] = tolower( buff[ j ] ); + sprintf( comm, "Preferred units for %s", SystemLabel( (AstSystemType) i, status ) ); + astWriteString( channel, buff, 1, 0, this->usedunits[ i ], comm ); + } + } + } +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAFluxFrame and astCheckFluxFrame functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(FluxFrame,Frame) +astMAKE_CHECK(FluxFrame) + +AstFluxFrame *astFluxFrame_( double specval, void *specfrm_void, + const char *options, int *status, ...) { +/* +*+ +* Name: +* astFluxFrame + +* Purpose: +* Create a FluxFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "fluxframe.h" +* AstFluxFrame *astFluxFrame( double specval, AstSpecFrame *specfrm, +* const char *options, ..., int *status ) + +* Class Membership: +* FluxFrame constructor. + +* Description: +* This function creates a new FluxFrame and optionally initialises its +* attributes. + +* Parameters: +* specval +* The spectral value to which the flux values refer, given in the +* spectral coordinate system specified by "specfrm". The value +* supplied for the "specval" parameter becomes the default value for +* the SpecVal attribute. +* specfrm +* A pointer to a SpecFrame describing the spectral coordinate system +* in which the "specval" parameter is given. A deep copy of this object +* is taken, so any subsequent changes to the SpecFrame using the +* supplied pointer will have no effect on the new FluxFrame. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new FluxFrame. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new FluxFrame. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic FluxFrame constructor which +* is available via the protected interface to the FluxFrame class. +* A public interface is provided by the astFluxFrameId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *um; /* Mapping from default to actual units */ + AstFluxFrame *new; /* Pointer to new FluxFrame */ + AstSpecFrame *sfrm; /* Pointer to SpecFrame */ + AstSystemType s; /* System */ + const char *u; /* Units string */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the SpecFrame structures provided. */ + sfrm = specfrm_void ? astCheckSpecFrame( specfrm_void ) : NULL; + +/* Initialise the FluxFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitFluxFrame( NULL, sizeof( AstFluxFrame ), !class_init, + &class_vtab, "FluxFrame", specval, sfrm ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new FluxFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* Check the Units are appropriate for the System. */ + u = astGetUnit( new, 0 ); + s = astGetSystem( new ); + um = astUnitMapper( DefUnit( s, "astFluxFrame", "FluxFrame", status ), + u, NULL, NULL ); + if( um ) { + um = astAnnul( um ); + } else { + astError( AST__BADUN, "astFluxFrame: Inappropriate units (%s) " + "specified for a %s axis.", status, u, SystemLabel( s, status ) ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new FluxFrame. */ + return new; +} + +AstFluxFrame *astInitFluxFrame_( void *mem, size_t size, int init, + AstFluxFrameVtab *vtab, const char *name, + double specval, AstSpecFrame *specfrm, int *status ) { +/* +*+ +* Name: +* astInitFluxFrame + +* Purpose: +* Initialise a FluxFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "fluxframe.h" +* AstFluxFrame *astInitFluxFrame( void *mem, size_t size, int init, +* AstFrameVtab *vtab, const char *name, +* double specval, AstSpecFrame *specfrm) + +* Class Membership: +* FluxFrame initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new FluxFrame object. It allocates memory (if +* necessary) to accommodate the FluxFrame plus any additional data +* associated with the derived class. It then initialises a +* FluxFrame structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual function +* table for a FluxFrame at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the FluxFrame is to be +* created. This must be of sufficient size to accommodate the +* FluxFrame data (sizeof(FluxFrame)) plus any data used by +* the derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the FluxFrame (plus derived +* class data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also stored +* in the FluxFrame structure, so a valid value must be supplied +* even if not required for allocating memory. +* init +* A logical flag indicating if the FluxFrame's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new FluxFrame. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object belongs +* (it is this pointer value that will subsequently be returned by +* the astGetClass method). +* specval +* The spectral value to which the flux values refer, given in the +* spectral coordinate system specified by "specfrm". The value +* supplied for the "specval" parameter becomes the default value for +* the SpecVal attribute. May be AST__BAD. +* specfrm +* A pointer to a SpecFrame describing the spectral coordinate system +* in which the "specval" parameter is given. A deep copy of this object +* is taken, so any subsequent changes to the SpecFrame using the +* supplied pointer will have no effect on the new FluxFrame. Should +* be NULL if "specval" is AST__BAD. + +* Returned Value: +* A pointer to the new FluxFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFluxFrame *new; /* Pointer to the new FluxFrame */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitFluxFrameVtab( vtab, name ); + +/* Initialise a 1D Frame structure (the parent class) as the first component + within the FluxFrame structure, allocating memory if necessary. */ + new = (AstFluxFrame *) astInitFrame( mem, size, 0, + (AstFrameVtab *) vtab, name, 1 ); + + if ( astOK ) { + +/* Initialise the FluxFrame data. */ +/* ----------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->specval = AST__BAD; + new->defspecval = specval; + new->specframe = specfrm ? astCopy( specfrm ) : NULL; + new->nuunits = 0; + new->usedunits = NULL; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + + } + +/* Return a pointer to the new object. */ + return new; +} + +AstFluxFrame *astLoadFluxFrame_( void *mem, size_t size, AstFluxFrameVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadFluxFrame + +* Purpose: +* Load a FluxFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "fluxframe.h" +* AstFluxFrame *astLoadFluxFrame( void *mem, size_t size, AstFluxFrameVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* FluxFrame loader. + +* Description: +* This function is provided to load a new FluxFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* FluxFrame structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the FluxFrame is to be +* loaded. This must be of sufficient size to accommodate the +* FluxFrame data (sizeof(FluxFrame)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the FluxFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the FluxFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstFluxFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new FluxFrame. If this is NULL, a pointer +* to the (static) virtual function table for the FluxFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "FluxFrame" is used instead. + +* Returned Value: +* A pointer to the new FluxFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFluxFrame *new; /* Pointer to the new FluxFrame */ + char buff[ 20 ]; /* Buffer for item name */ + char *sval; /* Pointer to string value */ + int i; /* Loop count */ + int j; /* Get a pointer to the thread specific global data structure. */ + +/* Loop count */ + int sys; /* System value */ + + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this FluxFrame. In this case the + FluxFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstFluxFrame ); + vtab = &class_vtab; + name = "FluxFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitFluxFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built FluxFrame. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "FluxFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Default SpecVal */ +/* --------------- */ + new->defspecval = astReadDouble( channel, "dfspc", AST__BAD ); + +/* SpecFrame */ +/* ---------- */ + new->specframe = astReadObject( channel, "spcfr", NULL ); + +/* SpecVal */ +/* ------- */ + new->specval = astReadDouble( channel, "spcvl", AST__BAD ); + if ( TestSpecVal( new, status ) ) SetSpecVal( new, new->specval, status ); + +/* UsedUnits */ +/* --------- */ + new->nuunits = 0; + new->usedunits = NULL; + for( sys = FIRST_SYSTEM; sys <= LAST_SYSTEM; sys++ ) { + sprintf( buff, "u%s", astSystemString( new, (AstSystemType) sys )); + for( j = 0; j < strlen( buff ); j++ ) buff[ j ] = tolower( buff[ j ] ); + sval = astReadString( channel, buff, NULL ); + if( sval ) { + if( (int) sys >= new->nuunits ) { + new->usedunits = astGrow( new->usedunits, sys + 1, + sizeof(char *) ); + if( astOK ) { + for( i = new->nuunits; i < sys + 1; i++ ) new->usedunits[ i ] = NULL; + new->nuunits = sys + 1; + } + } else { + new->usedunits[ sys ] = astFree( new->usedunits[ sys ] ); + } + if( astOK ) { + new->usedunits[ sys ] = astStore( new->usedunits[ sys ], + sval, strlen( sval ) + 1 ); + } + sval = astFree( sval ); + } + } + +/* If an error occurred, clean up by deleting the new FluxFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new FluxFrame pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +AstSystemType astGetDensitySystem_( AstFluxFrame *this, int *status ){ + if ( !astOK ) return AST__BADSYSTEM; + return (**astMEMBER(this,FluxFrame,GetDensitySystem))(this, status ); +} + +const char *astGetDensityUnit_( AstFluxFrame *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,FluxFrame,GetDensityUnit))(this, status ); +} + + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstFluxFrame *astFluxFrameId_( double, void *, const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstFluxFrame *astFluxFrameId_( double specval, void *specfrm_void, + const char *options, ... ) { +/* +*++ +* Name: +c astFluxFrame +f AST_FLUXFRAME + +* Purpose: +* Create a FluxFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "fluxframe.h" +c AstFluxFrame *astFluxFrame( double specval, AstSpecFrame *specfrm, +c const char *options, ... ) +f RESULT = AST_FLUXFRAME( SPECVAL, SPECFRM, OPTIONS, STATUS ) + +* Class Membership: +* FluxFrame constructor. + +* Description: +* This function creates a new FluxFrame and optionally initialises +* its attributes. +* +* A FluxFrame is a specialised form of one-dimensional Frame which +* represents various systems used to represent the signal level in an +* observation. The particular coordinate system to be used is specified +* by setting the FluxFrame's System attribute qualified, as necessary, by +* other attributes such as the units, etc (see the description of the +* System attribute for details). +* +* All flux values are assumed to be measured at the same frequency or +* wavelength (as given by the SpecVal attribute). Thus this class is +* more appropriate for use with images rather than spectra. + +* Parameters: +c specval +f SPECVAL = DOUBLE PRECISION (Given) +* The spectral value to which the flux values refer, given in the +* spectral coordinate system specified by +c "specfrm". The value supplied for the "specval" +f SPECFRM. The value supplied for the SPECVAL +* parameter becomes the default value for the SpecVal attribute. +* A value of AST__BAD may be supplied if the spectral position is +* unknown, but this may result in it not being possible for the +c astConvert +f AST_CONVERT +* function to determine a Mapping between the new FluxFrame and +* some other FluxFrame. +c specfrm +f SPECFRM = INTEGER (Given) +* A pointer to a SpecFrame describing the spectral coordinate system +* in which the +c "specval" +f SPECVAL +* parameter is given. A deep copy of this object is taken, so any +* subsequent changes to the SpecFrame using the supplied pointer will +* have no effect on the new FluxFrame. +c A NULL pointer can be supplied if AST__BAD is supplied for "specval". +f AST__NULL can be supplied if AST__BAD is supplied for SPECVAL. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new FluxFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new FluxFrame. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFluxFrame() +f AST_FLUXFRAME = INTEGER +* A pointer to the new FluxFrame. + +* Notes: +* - When conversion between two FluxFrames is requested (as when +c supplying FluxFrames to astConvert), +f supplying FluxFrames AST_CONVERT), +* account will be taken of the nature of the flux coordinate systems +* they represent, together with any qualifying attribute values, including +* the AlignSystem attribute. The results will therefore fully reflect the +* relationship between positions measured in the two systems. In addition, +* any difference in the Unit attributes of the two systems will also be +* taken into account. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astFluxFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astFluxFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astFluxFrame_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *um; /* Mapping from default to actual units */ + AstFluxFrame *new; /* Pointer to new FluxFrame */ + AstSpecFrame *sfrm; /* Pointer to SpecFrame */ + AstSystemType s; /* System */ + const char *u; /* Units string */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the SpecFrame structures provided. */ + sfrm = specfrm_void ? astCheckSpecFrame( astMakePointer( specfrm_void ) ) : NULL; + +/* Initialise the FluxFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitFluxFrame( NULL, sizeof( AstFluxFrame ), !class_init, + &class_vtab, "FluxFrame", specval, sfrm ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new FluxFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* Check the Units are appropriate for the System. */ + u = astGetUnit( new, 0 ); + s = astGetSystem( new ); + um = astUnitMapper( DefUnit( s, "astFluxFrame", "FluxFrame", status ), + u, NULL, NULL ); + if( um ) { + um = astAnnul( um ); + } else { + astError( AST__BADUN, "astFluxFrame: Inappropriate units (%s) " + "specified for a %s axis.", status, u, SystemLabel( s, status ) ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new FluxFrame. */ + return astMakeId( new ); +} + + + + + + + + + diff --git a/fluxframe.h b/fluxframe.h new file mode 100644 index 0000000..c030142 --- /dev/null +++ b/fluxframe.h @@ -0,0 +1,267 @@ +#if !defined( FLUXFRAME_INCLUDED ) /* Include this file only once */ +#define FLUXFRAME_INCLUDED +/* +*+ +* Name: +* fluxframe.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the FluxFrame class. + +* Invocation: +* #include "fluxframe.h" + +* Description: +* This include file defines the interface to the FluxFrame class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 1-DEC-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "frame.h" /* Parent Frame class */ +#include "specframe.h" /* Spectral coordinate systems */ + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) /* Protected */ + +/* Values used to represent different System attribute values. */ +#define AST__FLUXDEN 1 +#define AST__FLUXDENW 2 +#define AST__SBRIGHT 3 +#define AST__SBRIGHTW 4 + +/* Define constants used to size global arrays in this module. */ +#define AST__FLUXFRAME_GETATTRIB_BUFF_LEN 50 +#define AST__FLUXFRAME_GETLABEL_BUFF_LEN 200 +#define AST__FLUXFRAME_GETSYMBOL_BUFF_LEN 20 +#define AST__FLUXFRAME_GETTITLE_BUFF_LEN 200 + +#endif + +/* Type Definitions. */ +/* ================= */ + +/* FluxFrame structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstFluxFrame { + +/* Attributes inherited from the parent class. */ + AstFrame frame; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double specval; /* Spectral position */ + double defspecval; /* Default spectral position */ + AstSpecFrame *specframe; /* SpecFrame describing specval & defspecval */ + int nuunits; /* Size of usedunits array */ + char **usedunits; /* Last used units for each system */ +} AstFluxFrame; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstFluxFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + + AstSystemType (* GetDensitySystem)( AstFluxFrame *, int * ); + const char *(* GetDensityUnit)( AstFluxFrame *, int * ); + + double (* GetSpecVal)( AstFluxFrame *, int * ); + int (* TestSpecVal)( AstFluxFrame *, int * ); + void (* ClearSpecVal)( AstFluxFrame *, int * ); + void (* SetSpecVal)( AstFluxFrame *, double, int * ); + +} AstFluxFrameVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstFluxFrameGlobals { + AstFluxFrameVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__FLUXFRAME_GETATTRIB_BUFF_LEN + 1 ]; + char GetLabel_Buff[ AST__FLUXFRAME_GETLABEL_BUFF_LEN + 1 ]; + char GetSymbol_Buff[ AST__FLUXFRAME_GETSYMBOL_BUFF_LEN + 1 ]; + char GetTitle_Buff[ AST__FLUXFRAME_GETTITLE_BUFF_LEN + 1 ]; +} AstFluxFrameGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(FluxFrame) /* Check class membership */ +astPROTO_ISA(FluxFrame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstFluxFrame *astFluxFrame_( double, void *, const char *, int *, ...); +#else +AstFluxFrame *astFluxFrameId_( double, void *, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstFluxFrame *astInitFluxFrame_( void *, size_t, int, + AstFluxFrameVtab *, + const char *, double, AstSpecFrame *, int * ); + +/* Vtab initialiser. */ +void astInitFluxFrameVtab_( AstFluxFrameVtab *, const char *, int * ); + +/* Loader. */ +AstFluxFrame *astLoadFluxFrame_( void *, size_t, + AstFluxFrameVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitFluxFrameGlobals_( AstFluxFrameGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +#if defined(astCLASS) /* Protected */ + +AstSystemType astGetDensitySystem_( AstFluxFrame *, int * ); +const char *astGetDensityUnit_( AstFluxFrame *, int * ); + +double astGetSpecVal_( AstFluxFrame *, int * ); +int astTestSpecVal_( AstFluxFrame *, int * ); +void astClearSpecVal_( AstFluxFrame *, int * ); +void astSetSpecVal_( AstFluxFrame *, double, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckFluxFrame(this) astINVOKE_CHECK(FluxFrame,this,0) +#define astVerifyFluxFrame(this) astINVOKE_CHECK(FluxFrame,this,1) + +/* Test class membership. */ +#define astIsAFluxFrame(this) astINVOKE_ISA(FluxFrame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astFluxFrame astINVOKE(F,astFluxFrame_) +#else +#define astFluxFrame astINVOKE(F,astFluxFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitFluxFrame(mem,size,init,vtab,name,specval,specfrm) \ +astINVOKE(O,astInitFluxFrame_(mem,size,init,vtab,name,specval,astCheckSpecFrame(specfrm),STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitFluxFrameVtab(vtab,name) astINVOKE(V,astInitFluxFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadFluxFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadFluxFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) + +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ + +/* None. */ + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +/* Here we make use of astCheckFluxFrame to validate FluxFrame pointers + before use. This provides a contextual error report if a pointer to + the wrong sort of object is supplied. */ + +#if defined(astCLASS) /* Protected */ + +#define astGetDensitySystem(this) astINVOKE(V,astGetDensitySystem_(astCheckFluxFrame(this),STATUS_PTR)) +#define astGetDensityUnit(this) astINVOKE(V,astGetDensityUnit_(astCheckFluxFrame(this),STATUS_PTR)) + +#define astGetSpecVal(this) astINVOKE(V,astGetSpecVal_(astCheckFluxFrame(this),STATUS_PTR)) +#define astTestSpecVal(this) astINVOKE(V,astTestSpecVal_(astCheckFluxFrame(this),STATUS_PTR)) +#define astClearSpecVal(this) astINVOKE(V,astClearSpecVal_(astCheckFluxFrame(this),STATUS_PTR)) +#define astSetSpecVal(this,value) astINVOKE(V,astSetSpecVal_(astCheckFluxFrame(this),value,STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/fmapping.c b/fmapping.c new file mode 100644 index 0000000..16f2b75 --- /dev/null +++ b/fmapping.c @@ -0,0 +1,771 @@ +/* +*+ +* Name: +* fmapping.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Mapping class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Mapping class. + +* Routines Defined: +* AST_DECOMPOSE +* AST_INVERT +* AST_ISAMAPPING +* AST_LINEARMAPPING +* AST_REBIN +* AST_REBINSEQ +* AST_MAPBOX +* AST_MAPSPLIT +* AST_RATE +* AST_REMOVEREGIONS +* AST_RESAMPLE +* AST_SIMPLIFY +* AST_TRAN1 +* AST_TRAN2 +* AST_TRANGRID +* AST_TRANN +* AST_RATE + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 11-JUL-1996 (RFWS): +* Original version. +* 13-DEC-1996 (RFWS) +* Added AST_SIMPLIFY. +* 28-MAY-1998 (RFWS): +* Added AST_MAPBOX. +* 12-NOV-1998 (RFWS): +* Added AST_RESAMPLE. +* 22-NOV-2000 (DSB): +* Pass the "flags" argument by reference instead of by value in the +* MAKE_AST_RESAMPLE_UINTERP macro. +* 9-JAN-2001 (DSB): +* Changed in and out arguments for TranN from type "double (*)[]" +* to "double *". +* 26-SEP-2001 (DSB): +* Added AST_DECOMPOSE. +* 16-JUL-2003 (DSB): +* Added AST_RATE. +* 30-JUN-2005 (DSB): +* Added AST_REBIN. +* 1-SEP-2005 (DSB): +* Added AST_REBINSEQ. +* 8-MAR-2006 (DSB): +* Added AST_TRANGRID. +* 5-MAY-2009 (DSB): +* Added AST_REMOVEREGIONS. +* 4-MAY-2010 (DSB): +* Add support for AST__VARWGT flag to AST_REBINSEQ. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "mapping.h" /* C interface to the Mapping class */ + +#include + +/* Module Variables. */ +/* ================= */ +/* Pointer to user-supplied (FORTRAN 77) interpolation function for + use by AST_RESAMPLE. */ +static void (* ast_resample_FINTERP)(); + +/* Interpolation function interface. */ +/* ================================= */ +/* These functions are associated with allowing FORTRAN 77 + implementations of interpolation functions to be passed to + AST_RESAMPLE via the FORTRAN 77 interface and then to be invoked + when necessary by the C code in the main implementation of + astResample. */ + +/* Define a macro which defines an interface function called + ast_resample_uinterp for a specific data type. + + The resulting function has a suitable interface to allow it to be + passed as an interpolation function to the C interface of + astResample in the case where the "interp" parameter is set to + AST__UINTERP. In turn, it invokes the equivalent user-supplied + FORTRAN 77 interpolation function, a pointer to which should + previously have been stored in the static variable + "ast_resample_FINTERP". */ +#define MAKE_AST_RESAMPLE_UINTERP(X,Xtype,Ftype) \ +static void ast_resample_uinterp##X( int ndim, \ + const int lbnd[], const int ubnd[], \ + const Xtype in[], const Xtype in_var[], \ + int npoint, const int offset[], \ + const double *const coords[], \ + const double params[], int flags, \ + Xtype badval, \ + Xtype *out, Xtype *out_var, \ + int *nbad ) { \ + DECLARE_INTEGER(STATUS); \ + int *status; \ +\ +/* Get a pointer to the inherited staus value. */ \ + status = astGetStatusPtr; \ +\ +/* Obtain the C status and then invoke the FORTRAN 77 interpolation \ + function via the stored pointer. Note that the "coords" array we \ + pass to FORTRAN has to be a contiguous 2-d array, so we must \ + de-reference one level of pointer compared to the C case. */ \ + STATUS = astStatus; \ + ( *ast_resample_FINTERP )( INTEGER_ARG(&ndim), \ + INTEGER_ARRAY_ARG(lbnd), \ + INTEGER_ARRAY_ARG(ubnd), \ + Ftype##_ARRAY_ARG(in), \ + Ftype##_ARRAY_ARG(in_var), \ + INTEGER_ARG(&npoint), \ + INTEGER_ARRAY_ARG(offset), \ + DOUBLE_ARRAY_ARG(coords[ 0 ]), \ + DOUBLE_ARRAY_ARG(params), \ + INTEGER_ARG(&flags), \ + Ftype##_ARG(&badval), \ + Ftype##_ARRAY_ARG(out), \ + Ftype##_ARRAY_ARG(out_var), \ + INTEGER_ARG(nbad), \ + INTEGER_ARG(&STATUS) ); \ +\ +/* Set the C status to the returned FORTRAN 77 status. */ \ + astSetStatus( STATUS ); \ +} + +/* Invoke the above macro to define an interface function for each + required data type. */ +MAKE_AST_RESAMPLE_UINTERP(D,double,DOUBLE) +MAKE_AST_RESAMPLE_UINTERP(F,float,REAL) +MAKE_AST_RESAMPLE_UINTERP(I,int,INTEGER) +MAKE_AST_RESAMPLE_UINTERP(UI,unsigned int,INTEGER) +MAKE_AST_RESAMPLE_UINTERP(K,INT_BIG,INTEGER8) +MAKE_AST_RESAMPLE_UINTERP(UK,UINT_BIG,INTEGER8) +MAKE_AST_RESAMPLE_UINTERP(S,short int,WORD) +MAKE_AST_RESAMPLE_UINTERP(US,unsigned short int,UWORD) +MAKE_AST_RESAMPLE_UINTERP(B,signed char,BYTE) +MAKE_AST_RESAMPLE_UINTERP(UB,unsigned char,UBYTE) + +/* Undefine the macro. */ +#undef MAKE_AST_RESAMPLE_UINTERP + +/* Define a function called ast_resample_ukern1 which has a suitable + interface to allow it to be passed as an interpolation function to + the C interface of astResample in the case where the "interp" + parameter is set to AST__UKERN1. In turn, it invokes the equivalent + user-supplied FORTRAN 77 interpolation function, a pointer to which + should previously have been stored in the static variable + "ast_resample_FINTERP". */ +static void ast_resample_ukern1( double offset, const double params[], + int flags, double *value ) { + DECLARE_INTEGER(STATUS); + int *status; + +/* Obtain the C status and then invoke the FORTRAN 77 interpolation + function via the stored pointer. */ + status = astGetStatusPtr; + STATUS = astStatus; + ( *ast_resample_FINTERP )( DOUBLE_ARG(&offset), + DOUBLE_ARRAY_ARG(params), + INTEGER_ARG(&flags), + DOUBLE_ARG(value), + INTEGER_ARG(&STATUS) ); + +/* Set the C status to the returned FORTRAN 77 status. */ + astSetStatus( STATUS ); +} + +/* FORTRAN interface functions. */ +/* ============================ */ +/* These functions implement the remainder of the FORTRAN interface. */ + +F77_SUBROUTINE(ast_decompose)( INTEGER(THIS), + INTEGER(MAP1), + INTEGER(MAP2), + LOGICAL(SERIES), + INTEGER(INVERT1), + INTEGER(INVERT2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(MAP1) + GENPTR_INTEGER(MAP2) + GENPTR_LOGICAL(SERIES) + GENPTR_INTEGER(INVERT1) + GENPTR_INTEGER(INVERT2) + AstMapping *map1; + AstMapping *map2; + int series; + + astAt( "AST_DECOMPOSE", NULL, 0 ); + astWatchSTATUS( + astDecompose( astI2P( *THIS ), &map1, &map2, &series, INVERT1, INVERT2 ); + *MAP1 = astP2I( map1 ); + *MAP2 = astP2I( map2 ); + *SERIES = ( series )?F77_TRUE:F77_FALSE; + ) +} + +F77_SUBROUTINE(ast_invert)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_INVERT", NULL, 0 ); + astWatchSTATUS( + astInvert( astI2P( *THIS ) ); + ) +} + +F77_LOGICAL_FUNCTION(ast_isamapping)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAMAPPING", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAMapping( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_linearapprox)( INTEGER(THIS), + DOUBLE_ARRAY(LBND), + DOUBLE_ARRAY(UBND), + DOUBLE(TOL), + DOUBLE_ARRAY(FIT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(LBND) + GENPTR_DOUBLE_ARRAY(UBND) + GENPTR_DOUBLE(TOL) + GENPTR_DOUBLE_ARRAY(FIT) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_LINEARAPPROX", NULL, 0 ); + astWatchSTATUS( + RESULT = astLinearApprox( astI2P( *THIS ), LBND, UBND, *TOL, FIT ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_quadapprox)( INTEGER(THIS), + DOUBLE_ARRAY(LBND), + DOUBLE_ARRAY(UBND), + INTEGER(NX), + INTEGER(NY), + DOUBLE_ARRAY(FIT), + DOUBLE(RMS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(LBND) + GENPTR_DOUBLE_ARRAY(UBND) + GENPTR_INTEGER(NX) + GENPTR_INTEGER(NY) + GENPTR_DOUBLE_ARRAY(FIT) + GENPTR_DOUBLE(RMS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_QUADAPPROX", NULL, 0 ); + astWatchSTATUS( + RESULT = astQuadApprox( astI2P( *THIS ), LBND, UBND, *NX, *NY, FIT, RMS ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_mapbox)( INTEGER(THIS), + DOUBLE_ARRAY(LBND_IN), + DOUBLE_ARRAY(UBND_IN), + LOGICAL(FORWARD), + INTEGER(COORD_OUT), + DOUBLE(LBND_OUT), + DOUBLE(UBND_OUT), + DOUBLE_ARRAY(XL), + DOUBLE_ARRAY(XU), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(LBND_IN) + GENPTR_DOUBLE_ARRAY(UBND_IN) + GENPTR_LOGICAL(FORWARD) + GENPTR_INTEGER(COORD_OUT) + GENPTR_DOUBLE(LBND_OUT) + GENPTR_DOUBLE(UBND_OUT) + GENPTR_DOUBLE_ARRAY(XL) + GENPTR_DOUBLE_ARRAY(XU) + double lbnd_out; + double ubnd_out; + + astAt( "AST_MAPBOX", NULL, 0 ); + astWatchSTATUS( + astMapBox( astI2P( *THIS ), LBND_IN, UBND_IN, F77_ISTRUE( *FORWARD ), + *COORD_OUT, &lbnd_out, &ubnd_out, XL, XU ); + *LBND_OUT = lbnd_out; + *UBND_OUT = ubnd_out; + ) +} + +/* AST_RESAMPLE requires a function for each possible data type, so + define it via a macro. */ +#define MAKE_AST_RESAMPLE(f,F,Ftype,X,Xtype) \ +F77_INTEGER_FUNCTION(ast_resample##f)( INTEGER(THIS), \ + INTEGER(NDIM_IN), \ + INTEGER_ARRAY(LBND_IN), \ + INTEGER_ARRAY(UBND_IN), \ + Ftype##_ARRAY(IN), \ + Ftype##_ARRAY(IN_VAR), \ + INTEGER(INTERP), \ + void (* FINTERP)(), \ + DOUBLE_ARRAY(PARAMS), \ + INTEGER(FLAGS), \ + DOUBLE(TOL), \ + INTEGER(MAXPIX), \ + Ftype(BADVAL), \ + INTEGER(NDIM_OUT), \ + INTEGER_ARRAY(LBND_OUT), \ + INTEGER_ARRAY(UBND_OUT), \ + INTEGER_ARRAY(LBND), \ + INTEGER_ARRAY(UBND), \ + Ftype##_ARRAY(OUT), \ + Ftype##_ARRAY(OUT_VAR), \ + INTEGER(STATUS) ) { \ + GENPTR_INTEGER(THIS) \ + GENPTR_INTEGER(NDIM_IN) \ + GENPTR_INTEGER_ARRAY(LBND_IN) \ + GENPTR_INTEGER_ARRAY(UBND_IN) \ + GENPTR_##Ftype##_ARRAY(IN) \ + GENPTR_##Ftype##_ARRAY(IN_VAR) \ + GENPTR_INTEGER(INTERP) \ + GENPTR_DOUBLE_ARRAY(PARAMS) \ + GENPTR_INTEGER(FLAGS) \ + GENPTR_DOUBLE(TOL) \ + GENPTR_INTEGER(MAXPIX) \ + GENPTR_##Ftype(BADVAL) \ + GENPTR_INTEGER(NDIM_OUT) \ + GENPTR_INTEGER_ARRAY(LBND_OUT) \ + GENPTR_INTEGER_ARRAY(UBND_OUT) \ + GENPTR_INTEGER_ARRAY(LBND) \ + GENPTR_INTEGER_ARRAY(UBND) \ + GENPTR_##Ftype##_ARRAY(OUT) \ + GENPTR_##Ftype##_ARRAY(OUT_VAR) \ + GENPTR_INTEGER(STATUS) \ +\ + void (* finterp)(); \ + Xtype *out_var; \ + const Xtype *in_var; \ + F77_INTEGER_TYPE RESULT; \ +\ + astAt( "AST_RESAMPLE"#F, NULL, 0 ); \ + astWatchSTATUS( \ +\ +/* If *INTERP is set to a value that requires a user-supplied \ + interpolation function, then store a pointer to the supplied \ + FORTRAN 77 version of this function and use the appropriate C \ + wrapper function (defined above) to invoke it. */ \ + if ( *INTERP == AST__UINTERP ) { \ + ast_resample_FINTERP = FINTERP; \ + finterp = (void (*)()) ast_resample_uinterp##X; \ + } else if ( *INTERP == AST__UKERN1 ) { \ + ast_resample_FINTERP = FINTERP; \ + finterp = (void (*)()) ast_resample_ukern1; \ + } else { \ + ast_resample_FINTERP = NULL; \ + finterp = NULL; \ + } \ +\ +/* If the AST__USEVAR flag is set, use the input and output variance \ + arrays, otherwise pass NULL pointers. */ \ + in_var = out_var = NULL; \ + if ( AST__USEVAR & *FLAGS ) { \ + in_var = (const Xtype *) IN_VAR; \ + out_var = (Xtype *) OUT_VAR; \ + } \ + RESULT = astResample##X( astI2P( *THIS ), *NDIM_IN, \ + LBND_IN, UBND_IN, (const Xtype *) IN, in_var, \ + *INTERP, finterp, PARAMS, *FLAGS, \ + *TOL, *MAXPIX, *BADVAL, \ + *NDIM_OUT, LBND_OUT, UBND_OUT, \ + LBND, UBND, (Xtype *) OUT, out_var ); \ + ) \ + return RESULT; \ +} + +/* Invoke the above macro to define a function for each data + type. Include synonyms for some functions. */ +MAKE_AST_RESAMPLE(d,D,DOUBLE,D,double) +MAKE_AST_RESAMPLE(r,R,REAL,F,float) +MAKE_AST_RESAMPLE(i,I,INTEGER,I,int) +MAKE_AST_RESAMPLE(ui,UI,INTEGER,UI,unsigned int) +MAKE_AST_RESAMPLE(k,K,INTEGER8,K,INT_BIG) +MAKE_AST_RESAMPLE(uk,UK,INTEGER8,UK,UINT_BIG) +MAKE_AST_RESAMPLE(s,S,WORD,S,short int) +MAKE_AST_RESAMPLE(us,US,UWORD,US,unsigned short int) +MAKE_AST_RESAMPLE(w,W,WORD,S,short int) +MAKE_AST_RESAMPLE(uw,UW,UWORD,US,unsigned short int) +MAKE_AST_RESAMPLE(b,B,BYTE,B,signed char) +MAKE_AST_RESAMPLE(ub,UB,UBYTE,UB,unsigned char) +#undef MAKE_AST_RESAMPLE + +/* AST_REBIN requires a function for each possible data type, so + define it via a macro. */ +#define MAKE_AST_REBIN(f,F,Ftype,X,Xtype) \ +F77_SUBROUTINE(ast_rebin##f)( INTEGER(THIS), \ + DOUBLE(WLIM), \ + INTEGER(NDIM_IN), \ + INTEGER_ARRAY(LBND_IN), \ + INTEGER_ARRAY(UBND_IN), \ + Ftype##_ARRAY(IN), \ + Ftype##_ARRAY(IN_VAR), \ + INTEGER(INTERP), \ + DOUBLE_ARRAY(PARAMS), \ + INTEGER(FLAGS), \ + DOUBLE(TOL), \ + INTEGER(MAXPIX), \ + Ftype(BADVAL), \ + INTEGER(NDIM_OUT), \ + INTEGER_ARRAY(LBND_OUT), \ + INTEGER_ARRAY(UBND_OUT), \ + INTEGER_ARRAY(LBND), \ + INTEGER_ARRAY(UBND), \ + Ftype##_ARRAY(OUT), \ + Ftype##_ARRAY(OUT_VAR), \ + INTEGER(STATUS) ) { \ + GENPTR_INTEGER(THIS) \ + GENPTR_DOUBLE(WLIM) \ + GENPTR_INTEGER(NDIM_IN) \ + GENPTR_INTEGER_ARRAY(LBND_IN) \ + GENPTR_INTEGER_ARRAY(UBND_IN) \ + GENPTR_##Ftype##_ARRAY(IN) \ + GENPTR_##Ftype##_ARRAY(IN_VAR) \ + GENPTR_INTEGER(INTERP) \ + GENPTR_DOUBLE_ARRAY(PARAMS) \ + GENPTR_INTEGER(FLAGS) \ + GENPTR_DOUBLE(TOL) \ + GENPTR_INTEGER(MAXPIX) \ + GENPTR_##Ftype(BADVAL) \ + GENPTR_INTEGER(NDIM_OUT) \ + GENPTR_INTEGER_ARRAY(LBND_OUT) \ + GENPTR_INTEGER_ARRAY(UBND_OUT) \ + GENPTR_INTEGER_ARRAY(LBND) \ + GENPTR_INTEGER_ARRAY(UBND) \ + GENPTR_##Ftype##_ARRAY(OUT) \ + GENPTR_##Ftype##_ARRAY(OUT_VAR) \ + GENPTR_INTEGER(STATUS) \ +\ + Xtype *out_var; \ + const Xtype *in_var; \ +\ + astAt( "AST_REBIN"#F, NULL, 0 ); \ + astWatchSTATUS( \ +\ +/* If the AST__USEVAR flag is set, use the input and output variance \ + arrays, otherwise pass NULL pointers. */ \ + in_var = out_var = NULL; \ + if ( AST__USEVAR & *FLAGS ) { \ + in_var = (const Xtype *) IN_VAR; \ + out_var = (Xtype *) OUT_VAR; \ + } \ + astRebin##X( astI2P( *THIS ), *WLIM, *NDIM_IN, \ + LBND_IN, UBND_IN, (const Xtype *) IN, in_var, \ + *INTERP, PARAMS, *FLAGS, \ + *TOL, *MAXPIX, *BADVAL, \ + *NDIM_OUT, LBND_OUT, UBND_OUT, \ + LBND, UBND, (Xtype *) OUT, out_var ); \ + ) \ +} + +/* Invoke the above macro to define a function for each data + type. Include synonyms for some functions. */ +MAKE_AST_REBIN(d,D,DOUBLE,D,double) +MAKE_AST_REBIN(r,R,REAL,F,float) +MAKE_AST_REBIN(i,I,INTEGER,I,int) +MAKE_AST_REBIN(b,B,BYTE,B,signed char) +MAKE_AST_REBIN(ub,UB,UBYTE,UB,unsigned char) +#undef MAKE_AST_REBIN + +/* AST_REBINSEQ requires a function for each possible data type, so + define it via a macro. */ +#define MAKE_AST_REBINSEQ(f,F,Ftype,X,Xtype) \ +F77_SUBROUTINE(ast_rebinseq##f)( INTEGER(THIS), \ + DOUBLE(WLIM), \ + INTEGER(NDIM_IN), \ + INTEGER_ARRAY(LBND_IN), \ + INTEGER_ARRAY(UBND_IN), \ + Ftype##_ARRAY(IN), \ + Ftype##_ARRAY(IN_VAR), \ + INTEGER(INTERP), \ + DOUBLE_ARRAY(PARAMS), \ + INTEGER(FLAGS), \ + DOUBLE(TOL), \ + INTEGER(MAXPIX), \ + Ftype(BADVAL), \ + INTEGER(NDIM_OUT), \ + INTEGER_ARRAY(LBND_OUT), \ + INTEGER_ARRAY(UBND_OUT), \ + INTEGER_ARRAY(LBND), \ + INTEGER_ARRAY(UBND), \ + Ftype##_ARRAY(OUT), \ + Ftype##_ARRAY(OUT_VAR), \ + DOUBLE_ARRAY(WEIGHTS), \ + INTEGER8(NUSED), \ + INTEGER(STATUS) ) { \ + GENPTR_INTEGER(THIS) \ + GENPTR_DOUBLE(WLIM) \ + GENPTR_INTEGER(NDIM_IN) \ + GENPTR_INTEGER_ARRAY(LBND_IN) \ + GENPTR_INTEGER_ARRAY(UBND_IN) \ + GENPTR_##Ftype##_ARRAY(IN) \ + GENPTR_##Ftype##_ARRAY(IN_VAR) \ + GENPTR_INTEGER(INTERP) \ + GENPTR_DOUBLE_ARRAY(PARAMS) \ + GENPTR_INTEGER(FLAGS) \ + GENPTR_DOUBLE(TOL) \ + GENPTR_INTEGER(MAXPIX) \ + GENPTR_##Ftype(BADVAL) \ + GENPTR_INTEGER(NDIM_OUT) \ + GENPTR_INTEGER_ARRAY(LBND_OUT) \ + GENPTR_INTEGER_ARRAY(UBND_OUT) \ + GENPTR_INTEGER_ARRAY(LBND) \ + GENPTR_INTEGER_ARRAY(UBND) \ + GENPTR_##Ftype##_ARRAY(OUT) \ + GENPTR_##Ftype##_ARRAY(OUT_VAR) \ + GENPTR_DOUBLE_ARRAY(WEIGHTS) \ + GENPTR_INTEGER8(NUSED) \ + GENPTR_INTEGER(STATUS) \ +\ + Xtype *out_var; \ + const Xtype *in_var; \ + int64_t nused; \ +\ + astAt( "AST_REBINSEQ"#F, NULL, 0 ); \ + astWatchSTATUS( \ +\ +/* We need the input variances if the AST__USEVAR or AST__VARWGT flag is \ + set. Otherwise use a NULL pointer for the input variances. */ \ + if ( AST__USEVAR & *FLAGS || AST__VARWGT & *FLAGS ) { \ + in_var = (const Xtype *) IN_VAR; \ + } else { \ + in_var = NULL; \ + } \ +\ +/* We need the output variances if the AST__USEVAR or AST__GENVAR flag is \ + set. Otherwise use a NULL pointer for the output variances. */ \ + if ( AST__USEVAR & *FLAGS || AST__GENVAR & *FLAGS ) { \ + out_var = (Xtype *) OUT_VAR; \ + } else { \ + out_var = NULL; \ + } \ +\ + nused = *NUSED; \ + astRebinSeq##X( astI2P( *THIS ), *WLIM, *NDIM_IN, \ + LBND_IN, UBND_IN, (const Xtype *) IN, in_var, \ + *INTERP, PARAMS, *FLAGS, \ + *TOL, *MAXPIX, *BADVAL, \ + *NDIM_OUT, LBND_OUT, UBND_OUT, \ + LBND, UBND, (Xtype *) OUT, out_var, WEIGHTS, \ + &nused ); \ + *NUSED = nused; \ + ) \ +} \ + +/* Invoke the above macro to define a function for each data + type. Include synonyms for some functions. */ +MAKE_AST_REBINSEQ(d,D,DOUBLE,D,double) +MAKE_AST_REBINSEQ(r,R,REAL,F,float) +MAKE_AST_REBINSEQ(i,I,INTEGER,I,int) +MAKE_AST_REBINSEQ(b,B,BYTE,B,signed char) +MAKE_AST_REBINSEQ(ub,UB,UBYTE,UB,unsigned char) +#undef MAKE_AST_REBINSEQ + +F77_INTEGER_FUNCTION(ast_removeregions)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_REMOVEREGIONS", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astRemoveRegions( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_simplify)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_SIMPLIFY", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astSimplify( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_tran1)( INTEGER(THIS), + INTEGER(NPOINT), + DOUBLE(XIN), + LOGICAL(FORWARD), + DOUBLE(XOUT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NPOINT) + GENPTR_DOUBLE(XIN) + GENPTR_LOGICAL(FORWARD) + GENPTR_DOUBLE(XOUT) + + astAt( "AST_TRAN1", NULL, 0 ); + astWatchSTATUS( + astTran1( astI2P( *THIS ), *NPOINT, XIN, F77_ISTRUE( *FORWARD ), XOUT ); + ) +} + +F77_SUBROUTINE(ast_tran2)( INTEGER(THIS), + INTEGER(NPOINT), + DOUBLE(XIN), + DOUBLE(YIN), + LOGICAL(FORWARD), + DOUBLE(XOUT), + DOUBLE(YOUT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NPOINT) + GENPTR_DOUBLE(XIN) + GENPTR_DOUBLE(YIN) + GENPTR_LOGICAL(FORWARD) + GENPTR_DOUBLE(XOUT) + GENPTR_DOUBLE(YOUT) + + astAt( "AST_TRAN2", NULL, 0 ); + astWatchSTATUS( + astTran2( astI2P( *THIS ), *NPOINT, XIN, YIN, + F77_ISTRUE( *FORWARD ), XOUT, YOUT ); + ) +} + +F77_SUBROUTINE(ast_trangrid)( INTEGER(THIS), + INTEGER(NCOORD_IN), + INTEGER_ARRAY(LBND), + INTEGER_ARRAY(UBND), + DOUBLE(TOL), + INTEGER(MAXPIX), + LOGICAL(FORWARD), + INTEGER(NCOORD_OUT), + INTEGER(OUTDIM), + DOUBLE_ARRAY(OUT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NCOORD_IN) + GENPTR_INTEGER_ARRAY(LBND) + GENPTR_INTEGER_ARRAY(UBND) + GENPTR_DOUBLE(TOL) + GENPTR_INTEGER(MAXPIX) + GENPTR_LOGICAL(FORWARD) + GENPTR_INTEGER(NCOORD_OUT) + GENPTR_INTEGER(OUTDIM) + GENPTR_DOUBLE_ARRAY(OUT) + + astAt( "AST_TRANGRID", NULL, 0 ); + astWatchSTATUS( + astTranGrid( astI2P( *THIS ), *NCOORD_IN, LBND, UBND, *TOL, *MAXPIX, + F77_ISTRUE( *FORWARD ), *NCOORD_OUT, *OUTDIM, OUT ); + ) +} + +F77_SUBROUTINE(ast_trann)( INTEGER(THIS), + INTEGER(NPOINT), + INTEGER(NCOORD_IN), + INTEGER(INDIM), + DOUBLE_ARRAY(IN), + LOGICAL(FORWARD), + INTEGER(NCOORD_OUT), + INTEGER(OUTDIM), + DOUBLE_ARRAY(OUT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NPOINT) + GENPTR_INTEGER(NCOORD_IN) + GENPTR_INTEGER(INDIM) + GENPTR_DOUBLE_ARRAY(IN) + GENPTR_LOGICAL(FORWARD) + GENPTR_INTEGER(NCOORD_OUT) + GENPTR_INTEGER(OUTDIM) + GENPTR_DOUBLE_ARRAY(OUT) + + astAt( "AST_TRANN", NULL, 0 ); + astWatchSTATUS( + astTranN( astI2P( *THIS ), *NPOINT, *NCOORD_IN, *INDIM, + (const double *)IN, F77_ISTRUE( *FORWARD ), + *NCOORD_OUT, *OUTDIM, OUT ); + ) +} + +F77_DOUBLE_FUNCTION(ast_rate)( INTEGER(THIS), + DOUBLE_ARRAY(AT), + INTEGER(AX1), + INTEGER(AX2), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(AX1) + GENPTR_INTEGER(AX2) + GENPTR_DOUBLE_ARRAY(AT) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_RATE", NULL, 0 ); + astWatchSTATUS( + RESULT = astRate( astI2P( *THIS ), AT, *AX1, *AX2 ); + ) + return RESULT; +} + + +F77_SUBROUTINE(ast_mapsplit)( INTEGER(THIS), + INTEGER(NIN), + INTEGER_ARRAY(IN), + INTEGER_ARRAY(OUT), + INTEGER(MAP), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NIN) + GENPTR_INTEGER_ARRAY(IN) + GENPTR_INTEGER_ARRAY(OUT) + GENPTR_INTEGER(MAP) + AstMapping *map; + + astAt( "AST_MAPSPLIT", NULL, 0 ); + astWatchSTATUS( + astMapSplit( astI2P( *THIS ), *NIN, IN, OUT, &map ); + *MAP = astP2I( map ); + ) +} + diff --git a/fmathmap.c b/fmathmap.c new file mode 100644 index 0000000..88ac236 --- /dev/null +++ b/fmathmap.c @@ -0,0 +1,122 @@ +/* +*+ +* Name: +* fmathmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST MathMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the MathMap class. + +* Routines Defined: +* AST_ISAMATHMAP +* AST_MATHMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 3-SEP-1999 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "mathmap.h" /* C interface to the MathMap class */ + +F77_LOGICAL_FUNCTION(ast_isamathmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAMATHMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAMathMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_mathmap)( INTEGER(NIN), + INTEGER(NOUT), + INTEGER(NFWD), + CHARACTER_ARRAY(FWD), + INTEGER(NINV), + CHARACTER_ARRAY(INV), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(FWD) + TRAIL(INV) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NIN) + GENPTR_INTEGER(NOUT) + GENPTR_INTEGER(NFWD) + GENPTR_CHARACTER_ARRAY(FWD) + GENPTR_INTEGER(NINV) + GENPTR_CHARACTER_ARRAY(INV) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char **fwd; + char **inv; + char *options; + int i; + + astAt( "AST_MATHMAP", NULL, 0 ); + astWatchSTATUS( + fwd = astStringArray( FWD, *NFWD, FWD_length ); + inv = astStringArray( INV, *NINV, INV_length ); + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astMathMap( *NIN, *NOUT, + *NFWD, (const char **) fwd, + *NINV, (const char **) inv, + "%s", options ) ); + astFree( options ); + astFree( inv ); + astFree( fwd ); + ) + return RESULT; +} diff --git a/fmatrixmap.c b/fmatrixmap.c new file mode 100644 index 0000000..8fff328 --- /dev/null +++ b/fmatrixmap.c @@ -0,0 +1,110 @@ +/* +*+ +* Name: +* fmatrixmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST MatrixMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the MatrixMap class. + +* Routines Defined: +* AST_ISAMATRIXMAP +* AST_MATRIXMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 14-NOV-1996 (DSB): +* Original version. +* 3-JUN-1997 (DSB): +* AST_MTRROT and AST_MTRMULT removed. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "matrixmap.h" /* C interface to the MatrixMap class */ + +F77_LOGICAL_FUNCTION(ast_isamatrixmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAMATRIXMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAMatrixMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_matrixmap)( INTEGER(NIN), + INTEGER(NOUT), + INTEGER(FORM), + DOUBLE_ARRAY(MATRIX), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NIN) + GENPTR_INTEGER(NOUT) + GENPTR_INTEGER(FORM) + GENPTR_DOUBLE_ARRAY(MATRIX) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_MATRIXMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astMatrixMap( *NIN, *NOUT, *FORM, MATRIX, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fnormmap.c b/fnormmap.c new file mode 100644 index 0000000..f7fa127 --- /dev/null +++ b/fnormmap.c @@ -0,0 +1,101 @@ +/* +*+ +* Name: +* fnormmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST NormMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the NormMap class. + +* Routines Defined: +* AST_ISANORMMAP +* AST_NORMMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S.Berry (Starlink) + +* History: +* 11-JUL-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "normmap.h" /* C interface to the NormMap class */ + +F77_LOGICAL_FUNCTION(ast_isanormmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISANORMMAP", NULL, 0 ); + RESULT = astIsANormMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_normmap)( INTEGER(FRAME), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_NORMMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astNormMap( astI2P( *FRAME ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fnullregion.c b/fnullregion.c new file mode 100644 index 0000000..6e7d61f --- /dev/null +++ b/fnullregion.c @@ -0,0 +1,104 @@ +/* +*+ +* Name: +* fnullregion.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST NullRegion class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the NullRegion class. + +* Routines Defined: +* AST_ISANULLREGION +* AST_NULLREGION + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 12-OCT-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "nullregion.h" /* C interface to the NullRegion class */ + + +F77_LOGICAL_FUNCTION(ast_isanullregion)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISANULLREGION", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsANullRegion( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_nullregion)( INTEGER(FRAME), + INTEGER(UNC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_INTEGER(UNC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_NULLREGION", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astNullRegion( astI2P( *FRAME ), astI2P( *UNC ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fobject.c b/fobject.c new file mode 100644 index 0000000..c718986 --- /dev/null +++ b/fobject.c @@ -0,0 +1,674 @@ +/* +*+ +* Name: +* fobject.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Object class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Object class. + +* Routines Defined: +* AST_ANNUL +* AST_BEGIN +* AST_CLEAR +* AST_CLONE +* AST_COPY +* AST_DELETE +* AST_END +* AST_ESCAPES +* AST_EXEMPT +* AST_EXPORT +* AST_GET(C,D,I,L,R) +* AST_ISAOBJECT +* AST_NULL +* AST_SET +* AST_SET(C,D,I,L,R) +* AST_SHOW +* AST_VERSION +* AST_LISTISSUED (only if macro DEBUG is defined) +* AST_SETWATCHID (only if macro DEBUG is defined) +* AST_TUNE +* AST_TUNEC + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 20-JUN-1996 (RFWS): +* Original version. +* 9-SEP-1996 (RFWS): +* Added AST_SHOW. +* 11-DEC-1996 (RFWS): +* Added AST_NULL. +* 14-JUL-1997 (RFWS): +* Add AST_EXEMPT function. +* 30-APR-2003 (DSB): +* Add AST_VERSION function. +* 7-FEB-2004 (DSB): +* Add AST_ESCAPES function. +* 27-JAN-2005 (DSB): +* Added AST_LISTISSUED and AST_SETWATCHID so that DEBUG facilities +* can be used from fortran. +* 7-FEB-2006 (DSB): +* Added AST_TUNE. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 13-OCT-2011 (DSB): +* Added AST_TUNEC. +*- +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include + +/* Configuration results. */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* AST headers */ +/* ----------- */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "object.h" /* C interface to the Object class */ + +F77_SUBROUTINE(ast_annul)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_ANNUL", NULL, 0 ); + astWatchSTATUS( + *THIS = astP2I( astAnnul( astI2P( *THIS ) ) ); + ) +} + +F77_SUBROUTINE(ast_begin)( INTEGER(STATUS) ) { + + astAt( "AST_BEGIN", NULL, 0 ); + astWatchSTATUS( + int dummy = *status; /* Avoid "unused variable 'status'" messages */ + *status = dummy; + astBegin; + ) +} + +F77_SUBROUTINE(ast_clear)( INTEGER(THIS), + CHARACTER(ATTRIB), + INTEGER(STATUS) + TRAIL(ATTRIB) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(ATTRIB) + char *attrib; + + astAt( "AST_CLEAR", NULL, 0 ); + astWatchSTATUS( + attrib = astString( ATTRIB, ATTRIB_length ); + astClear( astI2P( *THIS ), attrib ); + astFree( attrib ); + ) +} + +F77_INTEGER_FUNCTION(ast_clone)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_CLONE", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astClone( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_version)( ) { + int status_value = 0; + int *STATUS = &status_value; + int *status = &status_value; + astAt( "AST_VERSION", NULL, 0 ); + return astVersion; +} + +F77_INTEGER_FUNCTION(ast_escapes)( INTEGER(NEWVAL), + INTEGER(STATUS) ) { + GENPTR_INTEGER(NEWVAL) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_ESCAPES", NULL, 0 ); + astWatchSTATUS( + RESULT = astEscapes( *NEWVAL ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_copy)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_COPY", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astCopy( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_delete)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_DELETE", NULL, 0 ); + astWatchSTATUS( + *THIS = astP2I( astDelete( astI2P( *THIS ) ) ); + ) +} + +F77_SUBROUTINE(ast_end)( INTEGER(STATUS) ) { + + astAt( "AST_END", NULL, 0 ); + astWatchSTATUS( + astEnd; + ) +} + +F77_SUBROUTINE(ast_exempt)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_EXEMPT", NULL, 0 ); + astWatchSTATUS( + astExempt( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_export)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_EXPORT", NULL, 0 ); + astWatchSTATUS( + astExport( astI2P( *THIS ) ); + ) +} + +#define MAKE_GETX(name,code,TYPE,CODE,type) \ +F77_##TYPE##_FUNCTION(ast_get##code)( INTEGER(THIS), \ + CHARACTER(ATTRIB), \ + INTEGER(STATUS) \ + TRAIL(ATTRIB) ) { \ + GENPTR_INTEGER(THIS) \ + GENPTR_CHARACTER(ATTRIB) \ + F77_##TYPE##_TYPE(RESULT); \ + char *attrib; \ +\ + astAt( name, NULL, 0 ); \ + astWatchSTATUS( \ + attrib = astString( ATTRIB, ATTRIB_length ); \ + RESULT = astGet##CODE( astI2P( *THIS ), attrib ); \ + astFree( attrib ); \ + ) \ + return RESULT; \ +} + +MAKE_GETX("AST_GETD",d,DOUBLE,D,double) +MAKE_GETX("AST_GETI",i,INTEGER,L,long) +MAKE_GETX("AST_GETR",r,REAL,D,double) + +/* NO_CHAR_FUNCTION indicates that the f77.h method of returning a + character result doesn't work, so add an extra argument instead and + wrap this function up in a normal FORTRAN 77 function (in the file + object.f). */ +#if NO_CHAR_FUNCTION +F77_SUBROUTINE(ast_getc_a)( CHARACTER(RESULT), +#else +F77_SUBROUTINE(ast_getc)( CHARACTER_RETURN_VALUE(RESULT), +#endif + INTEGER(THIS), + CHARACTER(ATTRIB), + INTEGER(STATUS) +#if NO_CHAR_FUNCTION + TRAIL(RESULT) +#endif + TRAIL(ATTRIB) ) { + GENPTR_CHARACTER(RESULT) + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(ATTRIB) + char *attrib; + const char *result; + int i; + + astAt( "AST_GETC", NULL, 0 ); + astWatchSTATUS( + attrib = astString( ATTRIB, ATTRIB_length ); + result = astGetC( astI2P( *THIS ), attrib ); + i = 0; + if ( astOK ) { /* Copy result */ + for ( ; result[ i ] && i < RESULT_length; i++ ) { + RESULT[ i ] = result[ i ]; + } + } + while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */ + astFree( attrib ); + ) +} + +F77_LOGICAL_FUNCTION(ast_getl)( INTEGER(THIS), + CHARACTER(ATTRIB), + INTEGER(STATUS) + TRAIL(ATTRIB) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(ATTRIB) + F77_LOGICAL_TYPE(RESULT); + char *attrib; + + astAt( "AST_GETL", NULL, 0 ); + astWatchSTATUS( + attrib = astString( ATTRIB, ATTRIB_length ); + RESULT = astGetL( astI2P( *THIS ), attrib ) ? F77_TRUE : F77_FALSE; + astFree( attrib ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isaobject)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAOBJECT", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAObject( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_null)( void ) {} + +/* Omit the C variable length argument list here. */ +F77_SUBROUTINE(ast_set)( INTEGER(THIS), + CHARACTER(SETTING), + INTEGER(STATUS) + TRAIL(SETTING) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(SETTING) + char *setting; + int i; + int quoted; + + astAt( "AST_SET", NULL, 0 ); + astWatchSTATUS( + setting = astString( SETTING, SETTING_length ); + +/* Truncate the string to exclude any trailing spaces. */ + astChrTrunc( setting ); + +/* Change ',' to '\n' (which is what astSet normally does to its second + argument internally to separate the fields). This then allows "setting" + to be provided as an additional string value to be formatted using "%s". + This avoids interpretation of its contents (e.g. '%') as C format + specifiers. */ + if ( astOK ) { + quoted = 0; + for ( i = 0; setting[ i ]; i++ ) { + if( !quoted ) { + if ( setting[ i ] == ',' ) { + setting[ i ] = '\n'; + } else if( setting[ i ] == '"' ) { + quoted = 1; + } + } else if( setting[ i ] == '"' ){ + quoted = 0; + } + } + } + astSet( astI2P( *THIS ), "%s", setting ); + astFree( setting ); + ) +} + +#define MAKE_SETX(name,code,TYPE,CODE,type) \ +F77_SUBROUTINE(ast_set##code)( INTEGER(THIS), \ + CHARACTER(ATTRIB), \ + TYPE(VALUE), \ + INTEGER(STATUS) \ + TRAIL(ATTRIB) ) { \ + GENPTR_INTEGER(THIS) \ + GENPTR_CHARACTER(ATTRIB) \ + GENPTR_##TYPE(VALUE) \ + char *attrib; \ +\ + astAt( name, NULL, 0 ); \ + astWatchSTATUS( \ + attrib = astString( ATTRIB, ATTRIB_length ); \ + astSet##CODE( astI2P( *THIS ), attrib, *VALUE ); \ + astFree( attrib ); \ + ) \ +} + +MAKE_SETX("AST_SETD",d,DOUBLE,D,double) +MAKE_SETX("AST_SETR",r,REAL,D,double) +MAKE_SETX("AST_SETI",i,INTEGER,L,long) + +F77_SUBROUTINE(ast_setc)( INTEGER(THIS), + CHARACTER(ATTRIB), + CHARACTER(VALUE), + INTEGER(STATUS) + TRAIL(ATTRIB) + TRAIL(VALUE) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(ATTRIB) + GENPTR_CHARACTER(VALUE) + char *attrib, *value; + + astAt( "AST_SETC", NULL, 0 ); + astWatchSTATUS( + attrib = astString( ATTRIB, ATTRIB_length ); + value = astString( VALUE, VALUE_length ); + +/* Truncate the strings to exclude any trailing spaces. */ + astChrTrunc( attrib ); + astChrTrunc( value ); + + astSetC( astI2P( *THIS ), attrib, value ); + astFree( attrib ); + astFree( value ); + ) +} + +F77_SUBROUTINE(ast_setl)( INTEGER(THIS), + CHARACTER(ATTRIB), + LOGICAL(VALUE), + INTEGER(STATUS) + TRAIL(ATTRIB) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(ATTRIB) + GENPTR_LOGICAL(VALUE) + char *attrib; + + astAt( "AST_SETL", NULL, 0 ); + astWatchSTATUS( + attrib = astString( ATTRIB, ATTRIB_length ); + astSetI( astI2P( *THIS ), attrib, F77_ISTRUE( *VALUE ) ? 1 : 0 ); + astFree( attrib ); + ) +} + +F77_SUBROUTINE(ast_show)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_SHOW", NULL, 0 ); + astWatchSTATUS( + astShow( astI2P( *THIS ) ); + ) +} + +F77_LOGICAL_FUNCTION(ast_test)( INTEGER(THIS), + CHARACTER(ATTRIB), + INTEGER(STATUS) + TRAIL(ATTRIB) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(ATTRIB) + F77_LOGICAL_TYPE(RESULT); + char *attrib; + + astAt( "AST_TEST", NULL, 0 ); + astWatchSTATUS( + attrib = astString( ATTRIB, ATTRIB_length ); + RESULT = astTest( astI2P( *THIS ), attrib ) ? F77_TRUE : F77_FALSE; + astFree( attrib ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_hasattribute)( INTEGER(THIS), + CHARACTER(ATTRIB), + INTEGER(STATUS) + TRAIL(ATTRIB) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(ATTRIB) + F77_LOGICAL_TYPE(RESULT); + char *attrib; + + astAt( "AST_HASATTRIBUTE", NULL, 0 ); + astWatchSTATUS( + attrib = astString( ATTRIB, ATTRIB_length ); + RESULT = astHasAttribute( astI2P( *THIS ), attrib ) ? F77_TRUE : F77_FALSE; + astFree( attrib ); + ) + return RESULT; +} + + +F77_LOGICAL_FUNCTION(ast_same)( INTEGER(THIS), + INTEGER(THAT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(THAT) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_SAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astSame( astI2P( *THIS ), astI2P( *THAT ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + + +#ifdef MEM_DEBUG + +F77_SUBROUTINE(ast_beginpm)( void ) { + int status = 0; + astBeginPM_( &status ); +} + +F77_SUBROUTINE(ast_endpm)( void ) { + int status = 0; + astEndPM_( &status ); +} + +F77_SUBROUTINE(ast_activememory)( CHARACTER(TEXT) + TRAIL(TEXT) ) { + GENPTR_CHARACTER(TEXT) + char *text; + int status_value; + int *status = &status_value; + *status = 0; + astBeginPM; + text = astString( TEXT, TEXT_length ); + astEndPM; + astActiveMemory( text ); + astFree( text ); +} + +F77_SUBROUTINE(ast_watchmemory)( INTEGER(ID) ) { + GENPTR_INTEGER(ID) + astWatchMemory( *ID ); +} + +F77_SUBROUTINE(ast_flushmemory)( INTEGER(LEAK) ) { + GENPTR_INTEGER(LEAK) + int status = 0; + astFlushMemory_( *LEAK, &status ); +} + +#else + +F77_SUBROUTINE(ast_activememory)( CHARACTER(TEXT) + TRAIL(TEXT) ) { + GENPTR_CHARACTER(TEXT) +} + +F77_SUBROUTINE(ast_watchmemory)( INTEGER(ID) ) { + GENPTR_INTEGER(ID) +} + +F77_SUBROUTINE(ast_flushmemory)( INTEGER(LEAK) ) { + GENPTR_INTEGER(LEAK) +} + +F77_SUBROUTINE(ast_beginpm)( void ) { +} + +F77_SUBROUTINE(ast_endpm)( void ) { +} + + + + +#endif + + +F77_INTEGER_FUNCTION(ast_tune)( CHARACTER(NAME), + INTEGER(VALUE), + INTEGER(STATUS) + TRAIL(NAME) ) { + GENPTR_INTEGER(VALUE) + GENPTR_CHARACTER(NAME) + F77_INTEGER_TYPE(RESULT); + char *name; + + astAt( "AST_TUNE", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + RESULT = astTune( name, *VALUE ); + name = astFree( name ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_tunec)( CHARACTER(NAME), + CHARACTER(VALUE), + CHARACTER(BUFF), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(VALUE) + TRAIL(BUFF) ) { + GENPTR_CHARACTER(NAME) + GENPTR_CHARACTER(VALUE) + GENPTR_CHARACTER(BUFF) + char *name; + char *value; + char *buff; + + astAt( "AST_TUNEC", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + value = astString( VALUE, VALUE_length ); + if( value && !strcmp( value, AST__TUNULLC ) ) value = astFree( value ); + buff = astMalloc( BUFF_length + 1 ); + + astTuneC( name, value, buff, BUFF_length + 1 ); + + int i = 0; + if( astOK ) { + for ( ; buff[ i ] && i < BUFF_length; i++ ) { + BUFF[ i ] = buff[ i ]; + } + } + while ( i < BUFF_length ) BUFF[ i++ ] = ' '; /* Pad with blanks */ + + buff = astFree( buff ); + name = astFree( name ); + value = astFree( value ); + ) +} + +F77_LOGICAL_FUNCTION(ast_chrsub)( CHARACTER(TEST), + CHARACTER(PATTERN), + CHARACTER(RESULT), + INTEGER(STATUS) + TRAIL(TEST) + TRAIL(PATTERN) + TRAIL(RESULT) ) { + GENPTR_CHARACTER(TEST) + GENPTR_CHARACTER(PATTERN) + GENPTR_CHARACTER(RESULT) + F77_LOGICAL_TYPE(MATCH); + + char *test, *pattern, *result; + int i; + + astAt( "AST_CHRSUB", NULL, 0 ); + astWatchSTATUS( + test = astString( TEST, TEST_length ); + pattern = astString( PATTERN, PATTERN_length ); + + if( pattern ) { + test[ astChrLen( test ) ] = 0; + pattern[ astChrLen( pattern ) ] = 0; + } + + result = astChrSub( test, pattern, NULL, 0 ); + + i = 0; + if( result ) { + MATCH = F77_TRUE; + for ( ; result[ i ] && i < RESULT_length; i++ ) { + RESULT[ i ] = result[ i ]; + } + result = astFree( result ); + } else { + MATCH = F77_FALSE; + } + while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */ + + test = astFree( test ); + pattern = astFree( pattern ); + ) + return MATCH; +} + + +F77_LOGICAL_FUNCTION(ast_equal)( INTEGER(THIS), + INTEGER(THAT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(THAT) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_EQUAL", NULL, 0 ); + astWatchSTATUS( + RESULT = astEqual( astI2P( *THIS ), astI2P( *THAT ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + + + diff --git a/fpcdmap.c b/fpcdmap.c new file mode 100644 index 0000000..bd5a335 --- /dev/null +++ b/fpcdmap.c @@ -0,0 +1,103 @@ +/* +*+ +* Name: +* fpcdmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST PcdMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the PcdMap class. + +* Routines Defined: +* AST_ISAPCDMAP +* AST_PCDMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 19-MAY-1999 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "pcdmap.h" /* C interface to the PcdMap class */ + +F77_LOGICAL_FUNCTION(ast_isapcdmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPCDMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPcdMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_pcdmap)( DOUBLE(DISCO), + DOUBLE_ARRAY(PCDCEN), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_DOUBLE(DISCO) + GENPTR_DOUBLE_ARRAY(PCDCEN) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_PCDMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astPcdMap( *DISCO, PCDCEN, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fpermmap.c b/fpermmap.c new file mode 100644 index 0000000..d280958 --- /dev/null +++ b/fpermmap.c @@ -0,0 +1,110 @@ +/* +*+ +* Name: +* fpermmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST PermMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the PermMap class. + +* Routines Defined: +* AST_ISAPERMMAP +* AST_PERMMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 26-SEP-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "permmap.h" /* C interface to the PermMap class */ + +F77_LOGICAL_FUNCTION(ast_isapermmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPERMMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPermMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_permmap)( INTEGER(NIN), + INTEGER_ARRAY(INPERM), + INTEGER(NOUT), + INTEGER_ARRAY(OUTPERM), + DOUBLE_ARRAY(CONSTANT), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NIN) + GENPTR_INTEGER_ARRAY(INPERM) + GENPTR_INTEGER(NOUT) + GENPTR_INTEGER_ARRAY(OUTPERM) + GENPTR_DOUBLE_ARRAY(CONSTANT) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_PERMMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astPermMap( *NIN, INPERM, *NOUT, OUTPERM, CONSTANT, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fplot.c b/fplot.c new file mode 100644 index 0000000..a5e0b47 --- /dev/null +++ b/fplot.c @@ -0,0 +1,683 @@ +/* +*+ +* Name: +* fplot.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Plot class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Plot class. + +* Routines Defined: +* AST_BORDER +* AST_BOUNDINGBOX +* AST_CLIP +* AST_CURVE +* AST_GENCURVE +* AST_GRID +* AST_GRIDLINE +* AST_ISAPLOT +* AST_MARK +* AST_PLOT +* AST_POLYCURVE +* AST_REGIONOUTLINE +* AST_TEXT +* AST_GRFSET +* AST_GRFPUSH +* AST_GRFPOP +* AST_STRIPESCAPES +* AST_GETGRFCONTEXT + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 23-OCT-1996 (DSB): +* Original version. +* 14-NOV-1996 (DSB): +* Method names shortened. CrvBreak removed. +* 21-NOV-1996 (DSB): +* Method names changed, CLIP argument NBND removed. +* 18-DEC-1996 (DSB): +* Argument UP changed to single precision and NCOORD removed +* in AST_TEXT. +* 11-AUG-1998 (DSB): +* Added AST_POLYCURVE. +* 9-JAN-2001 (DSB): +* Change argument "in" for astMark and astPolyCurve from type +* "const double (*)[]" to "const double *". +* 13-JUN-2001 (DSB): +* Modified to add support for astGenCurve, astGrfSet, astGrfPop, +* astGrfPush and EXTERNAL grf functions. +* 14-AUG-2002 (DSB): +* Added AST_BOUNDINGBOX. +* 8-JAN-2003 (DSB): +* Include "string.h". +* 10-JUL-2006 (DSB): +* Add AST_STRIPESCAPES +* 21-JUN-2007 (DSB): +* - Avoid use of protected astGetGrfContext function. +* - Change data type of GrfContext from integer to AST Object pointer. +* 29-JUN-2007 (DSB): +* Added astGetGrfCOntext and removed astSetGrfContext. +* 30-AUG-2007 (DSB): +* Use astGrfConID to get the identifier for the graphics context +* KeyMap. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +#define MXSTRLEN 80 /* String length at which truncation starts + within pgqtxt and pgptxt. */ +/* Header files. */ +/* ============= */ +#include "string.h" +#include "ast_err.h" /* AST error codes */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "plot.h" /* C interface to the Plot class */ +#include "grf.h" /* Low-level graphics interface */ + +/* Prototypes for external functions. */ +/* ================================== */ +/* This is the null function defined by the FORTRAN interface in +fobject.c. */ +F77_SUBROUTINE(ast_null)( void ); + +static int FGAttrWrapper( AstPlot *, int, double, double *, int ); +static int FGBBufWrapper( AstPlot * ); +static int FGEBufWrapper( AstPlot * ); +static int FGFlushWrapper( AstPlot * ); +static int FGLineWrapper( AstPlot *, int, const float *, const float * ); +static int FGMarkWrapper( AstPlot *, int, const float *, const float *, int ); +static int FGTextWrapper( AstPlot *, const char *, float, float, const char *, float, float ); +static int FGTxExtWrapper( AstPlot *, const char *, float, float, const char *, float, float, float *, float * ); +static int FGCapWrapper( AstPlot *, int, int ); +static int FGQchWrapper( AstPlot *, float *, float * ); +static int FGScalesWrapper( AstPlot *, float *, float * ); + +F77_LOGICAL_FUNCTION(ast_isaplot)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPLOT", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPlot( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_plot)( INTEGER(FRAME), + REAL_ARRAY(GRAPHBOX), + DOUBLE_ARRAY(BASEBOX), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_REAL_ARRAY(GRAPHBOX) + GENPTR_DOUBLE_ARRAY(BASEBOX) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_PLOT", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astPlot( astI2P( *FRAME ), GRAPHBOX, BASEBOX, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_border)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_BORDER", NULL, 0 ); + astWatchSTATUS( + RESULT = astBorder( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_boundingbox)( INTEGER(THIS), + REAL_ARRAY(LBND), + REAL_ARRAY(UBND), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + GENPTR_REAL_ARRAY(LBND) + GENPTR_REAL_ARRAY(UBND) + + astAt( "AST_BOUNDINGBOX", NULL, 0 ); + astWatchSTATUS( + astBoundingBox( astI2P( *THIS ), LBND, UBND ); + ) +} + +F77_SUBROUTINE(ast_clip)( INTEGER(THIS), + INTEGER(IFRAME), + DOUBLE_ARRAY(LBND), + DOUBLE_ARRAY(UBND), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(IFRAME) + GENPTR_DOUBLE_ARRAY(LBND) + GENPTR_DOUBLE_ARRAY(UBND) + + astAt( "AST_CLIP", NULL, 0 ); + astWatchSTATUS( + astClip( astI2P( *THIS ), *IFRAME, LBND, UBND ); + ) +} + +F77_SUBROUTINE(ast_gridline)( INTEGER(THIS), + INTEGER(AXIS), + DOUBLE_ARRAY(START), + DOUBLE(LENGTH), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(AXIS) + GENPTR_DOUBLE_ARRAY(START) + GENPTR_DOUBLE(LENGTH) + + astAt( "AST_GRIDLINE", NULL, 0 ); + astWatchSTATUS( + astGridLine( astI2P( *THIS ), *AXIS, START, *LENGTH ); + ) +} + +F77_SUBROUTINE(ast_curve)( INTEGER(THIS), + DOUBLE_ARRAY(START), + DOUBLE_ARRAY(FINISH), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE_ARRAY(START) + GENPTR_DOUBLE_ARRAY(FINISH) + + astAt( "AST_CURVE", NULL, 0 ); + astWatchSTATUS( + astCurve( astI2P( *THIS ), START, FINISH ); + ) +} + +F77_SUBROUTINE(ast_grid)( INTEGER(THIS), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + + astAt( "AST_GRID", NULL, 0 ); + astWatchSTATUS( + astGrid( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_mark)( INTEGER(THIS), + INTEGER(NMARK), + INTEGER(NCOORD), + INTEGER(INDIM), + DOUBLE_ARRAY(IN), + INTEGER(TYPE), + INTEGER(STATUS) ){ + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NMARK) + GENPTR_INTEGER(NCOORD) + GENPTR_INTEGER(INDIM) + GENPTR_DOUBLE_ARRAY(IN) + GENPTR_INTEGER(TYPE) + + astAt( "AST_MARK", NULL, 0 ); + astWatchSTATUS( + astMark( astI2P( *THIS ), *NMARK, *NCOORD, *INDIM, + (const double *)IN, *TYPE ); + ) +} + +F77_SUBROUTINE(ast_polycurve)( INTEGER(THIS), + INTEGER(NPOINT), + INTEGER(NCOORD), + INTEGER(INDIM), + DOUBLE_ARRAY(IN), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(NPOINT) + GENPTR_INTEGER(NCOORD) + GENPTR_INTEGER(INDIM) + GENPTR_DOUBLE_ARRAY(IN) + + astAt( "AST_POLYCURVE", NULL, 0 ); + astWatchSTATUS( + astPolyCurve( astI2P( *THIS ), *NPOINT, *NCOORD, *INDIM, + (const double *)IN ); + ) +} + +F77_SUBROUTINE(ast_regionoutline)( INTEGER(THIS), + INTEGER(REGION), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(REGION) + + astAt( "AST_REGIONOUTLINE", NULL, 0 ); + astWatchSTATUS( + astRegionOutline( astI2P( *THIS ), astI2P( *REGION ) ); + ) +} + +F77_SUBROUTINE(ast_gencurve)( INTEGER(THIS), + INTEGER(MAP), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(MAP) + + astAt( "AST_GENCURVE", NULL, 0 ); + astWatchSTATUS( + astGenCurve( astI2P( *THIS ), astI2P( *MAP ) ); + ) +} + +F77_SUBROUTINE(ast_text)( INTEGER(THIS), + CHARACTER(TEXT), + DOUBLE_ARRAY(POS), + REAL_ARRAY(UP), + CHARACTER(JUST), + INTEGER(STATUS) + TRAIL(TEXT) + TRAIL(JUST) ){ + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(TEXT) + GENPTR_DOUBLE_ARRAY(POS) + GENPTR_REAL_ARRAY(UP) + GENPTR_CHARACTER(JUST) + char *text, *just; + + astAt( "AST_TEXT", NULL, 0 ); + astWatchSTATUS( + text = astString( TEXT, TEXT_length ); + just = astString( JUST, JUST_length ); + astText( astI2P( *THIS ), text, POS, UP, just ); + (void) astFree( (void *) text ); + (void) astFree( (void *) just ); + ) +} + +F77_SUBROUTINE(ast_grfpush)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + astAt( "AST_GRFPUSH", NULL, 0 ); + astWatchSTATUS( + astGrfPush( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_grfpop)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + astAt( "AST_GRFPOP", NULL, 0 ); + astWatchSTATUS( + astGrfPop( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_bbuf)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + astAt( "AST_BBUF", NULL, 0 ); + astWatchSTATUS( + astBBuf( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_ebuf)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + astAt( "AST_EBUF", NULL, 0 ); + astWatchSTATUS( + astEBuf( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_grfset)( INTEGER(THIS), CHARACTER(NAME), + AstGrfFun FUN, INTEGER(STATUS) + TRAIL(NAME) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + char *name; + AstGrfFun fun; + const char *class; /* Object class */ + const char *method; /* Current method */ + int ifun; /* Index into grf function list */ + AstGrfWrap wrapper; /* Wrapper function for C Grf routine*/ + + method = "AST_GRFSET"; + class = "Plot"; + + astAt( method, NULL, 0 ); + astWatchSTATUS( + +/* Set the function pointer to NULL if a pointer to + the null routine AST_NULL has been supplied. */ + fun = FUN; + if ( fun == (AstGrfFun) F77_EXTERNAL_NAME(ast_null) ) { + fun = NULL; + } + + name = astString( NAME, NAME_length ); + astGrfSet( astI2P( *THIS ), name, fun ); + + ifun = astGrfFunID( name, method, class ); + + if( ifun == AST__GATTR ) { + wrapper = (AstGrfWrap) FGAttrWrapper; + + } else if( ifun == AST__GBBUF ) { + wrapper = (AstGrfWrap) FGBBufWrapper; + + } else if( ifun == AST__GEBUF ) { + wrapper = (AstGrfWrap) FGEBufWrapper; + + } else if( ifun == AST__GFLUSH ) { + wrapper = (AstGrfWrap) FGFlushWrapper; + + } else if( ifun == AST__GLINE ) { + wrapper = (AstGrfWrap) FGLineWrapper; + + } else if( ifun == AST__GMARK ) { + wrapper = (AstGrfWrap) FGMarkWrapper; + + } else if( ifun == AST__GTEXT ) { + wrapper = (AstGrfWrap) FGTextWrapper; + + } else if( ifun == AST__GCAP ) { + wrapper = (AstGrfWrap) FGCapWrapper; + + } else if( ifun == AST__GTXEXT ) { + wrapper = (AstGrfWrap) FGTxExtWrapper; + + } else if( ifun == AST__GQCH ) { + wrapper = (AstGrfWrap) FGQchWrapper; + + } else if( ifun == AST__GSCALES ) { + wrapper = (AstGrfWrap) FGScalesWrapper; + + } else { + wrapper = (AstGrfWrap) FGFlushWrapper; + if( astOK ) astError( AST__INTER, "%s(%s): AST internal programming " + "error - Grf function id %d not yet supported.", status, + method, class, ifun ); + } + astGrfWrapper( astI2P( *THIS ), name, wrapper ); + ) +} + +static int FGAttrWrapper( AstPlot *this, int attr, double value, + double *old_value, int prim ) { + DECLARE_DOUBLE(OLDVAL); + int ret; + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + + if ( !astOK ) return 0; + + GRFCON = astP2I( astGrfConID( this ) ); + ret = ( *(int (*)( INTEGER(grfcon), INTEGER(attr), DOUBLE(value), + DOUBLE(old_value), INTEGER(prim) )) + this->grffun[ AST__GATTR ])( INTEGER_ARG(&GRFCON), + INTEGER_ARG(&attr), + DOUBLE_ARG(&value), + DOUBLE_ARG(&OLDVAL), + INTEGER_ARG(&prim) ); + if( old_value ) *old_value = OLDVAL; + return ret; +} + +static int FGBBufWrapper( AstPlot *this ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)(INTEGER(grfcon))) this->grffun[ AST__GBBUF ])(INTEGER_ARG(&GRFCON)); +} + +static int FGEBufWrapper( AstPlot *this ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)(INTEGER(grfcon))) this->grffun[ AST__GEBUF ])(INTEGER_ARG(&GRFCON)); +} + +static int FGFlushWrapper( AstPlot *this ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)(INTEGER(grfcon))) this->grffun[ AST__GFLUSH ])(INTEGER_ARG(&GRFCON)); +} + +static int FGLineWrapper( AstPlot *this, int n, const float *x, + const float *y ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)( INTEGER(grfcon), INTEGER(n), REAL_ARRAY(x), REAL_ARRAY(y) )) + this->grffun[ AST__GLINE ])( INTEGER_ARG(&GRFCON), + INTEGER_ARG(&n), + REAL_ARRAY_ARG(x), + REAL_ARRAY_ARG(y) ); +} + +static int FGMarkWrapper( AstPlot *this, int n, const float *x, + const float *y, int type ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)( INTEGER(grfcon), INTEGER(n), REAL_ARRAY(x), REAL_ARRAY(y), + INTEGER(type) )) + this->grffun[ AST__GMARK ])( INTEGER_ARG(&GRFCON), + INTEGER_ARG(&n), + REAL_ARRAY_ARG(x), + REAL_ARRAY_ARG(y), + INTEGER_ARG(&type) ); +} + +static int FGTextWrapper( AstPlot *this, const char *text, float x, float y, + const char *just, float upx, float upy ) { + + DECLARE_CHARACTER(LTEXT,MXSTRLEN); + DECLARE_CHARACTER(LJUST,MXSTRLEN); + int ftext_length; + int fjust_length; + + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + + ftext_length = strlen( text ); + if( ftext_length > LTEXT_length ) ftext_length = LTEXT_length; + astStringExport( text, LTEXT, ftext_length ); + + fjust_length = strlen( just ); + if( fjust_length > LJUST_length ) fjust_length = LJUST_length; + astStringExport( just, LJUST, fjust_length ); + + return ( *(int (*)( INTEGER(grfcon), CHARACTER(LTEXT), REAL(x), REAL(y), + CHARACTER(LJUST), REAL(upx), REAL(upy) + TRAIL(ftext) TRAIL(fjust) ) ) + this->grffun[ AST__GTEXT ])( + INTEGER_ARG(&GRFCON), + CHARACTER_ARG(LTEXT), + REAL_ARG(&x), + REAL_ARG(&y), + CHARACTER_ARG(LJUST), + REAL_ARG(&upx), + REAL_ARG(&upy) + TRAIL_ARG(ftext) + TRAIL_ARG(fjust) ); +} + +static int FGCapWrapper( AstPlot *this, int cap, int value ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)( INTEGER(grfcon), INTEGER(cap), INTEGER(value) ) ) + this->grffun[ AST__GCAP ])( + INTEGER_ARG(&GRFCON), + INTEGER_ARG(&cap), + INTEGER_ARG(&value) ); +} + +static int FGTxExtWrapper( AstPlot *this, const char *text, float x, float y, + const char *just, float upx, float upy, float *xb, + float *yb ) { + DECLARE_CHARACTER(LTEXT,MXSTRLEN); + DECLARE_CHARACTER(LJUST,MXSTRLEN); + int ftext_length; + int fjust_length; + + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + + ftext_length = strlen( text ); + if( ftext_length > LTEXT_length ) ftext_length = LTEXT_length; + astStringExport( text, LTEXT, ftext_length ); + + fjust_length = strlen( just ); + if( fjust_length > LJUST_length ) fjust_length = LJUST_length; + astStringExport( just, LJUST, fjust_length ); + + return ( *(int (*)( INTEGER(grfcon), CHARACTER(LTEXT), REAL(x), REAL(y), + CHARACTER(LJUST), REAL(upx), REAL(upy), + REAL_ARRAY(xb), REAL_ARRAY(yb) TRAIL(ftext) + TRAIL(fjust) ) ) + this->grffun[ AST__GTXEXT ])( + INTEGER_ARG(&GRFCON), + CHARACTER_ARG(LTEXT), + REAL_ARG(&x), + REAL_ARG(&y), + CHARACTER_ARG(LJUST), + REAL_ARG(&upx), + REAL_ARG(&upy), + REAL_ARRAY_ARG(xb), + REAL_ARRAY_ARG(yb) + TRAIL_ARG(ftext) + TRAIL_ARG(fjust) ); +} + +static int FGQchWrapper( AstPlot *this, float *chv, float *chh ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)( INTEGER(grfcon), REAL(chv), REAL(chh) ) ) + this->grffun[ AST__GQCH ])( INTEGER_ARG(&GRFCON), REAL_ARG(chv), REAL_ARG(chh) ); +} + +static int FGScalesWrapper( AstPlot *this, float *alpha, float *beta ) { + F77_INTEGER_TYPE(GRFCON); + int *status = astGetStatusPtr; + if ( !astOK ) return 0; + GRFCON = astP2I( astGrfConID( this ) ); + return ( *(int (*)( INTEGER(grfcon), REAL(alpha), REAL(beta) ) ) + this->grffun[ AST__GSCALES ])( INTEGER_ARG(&GRFCON), REAL_ARG(alpha), REAL_ARG(beta) ); +} + + +/* NO_CHAR_FUNCTION indicates that the f77.h method of returning a + character result doesn't work, so add an extra argument instead and + wrap this function up in a normal FORTRAN 77 function (in the file + plot.f). */ +#if NO_CHAR_FUNCTION +F77_SUBROUTINE(ast_stripescapes_a)( CHARACTER(RESULT), +#else +F77_SUBROUTINE(ast_stripescapes)( CHARACTER_RETURN_VALUE(RESULT), +#endif + CHARACTER(TEXT), + INTEGER(STATUS) +#if NO_CHAR_FUNCTION + TRAIL(RESULT) +#endif + TRAIL(TEXT) ) { + GENPTR_CHARACTER(RESULT) + GENPTR_CHARACTER(TEXT) + char *text; + const char *result; + int i; + + astAt( "AST_STRIPESCAPES", NULL, 0 ); + astWatchSTATUS( + text = astString( TEXT, TEXT_length ); + result = astStripEscapes( text ); + i = 0; + if ( astOK ) { /* Copy result */ + for ( ; result[ i ] && i < RESULT_length; i++ ) { + RESULT[ i ] = result[ i ]; + } + } + while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */ + astFree( text ); + ) +} + +F77_LOGICAL_FUNCTION(ast_getgrfcontext)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETGRFCONTEXT", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetGrfContext( astI2P( *THIS ) ) ); + ) + return RESULT; +} + + + + diff --git a/fplot3d.c b/fplot3d.c new file mode 100644 index 0000000..4246f50 --- /dev/null +++ b/fplot3d.c @@ -0,0 +1,107 @@ +/* +*+ +* Name: +* fplot3d.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Plot3D class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Plot3D class. + +* Routines Defined: +* AST_ISAPLOT3D +* AST_PLOT3D + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 6-JUN-2007 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "plot3d.h" /* C interface to the Plot3D class */ + +F77_LOGICAL_FUNCTION(ast_isaplot3d)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPLOT3D", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPlot3D( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_plot3d)( INTEGER(FRAME), + REAL_ARRAY(GRAPHBOX), + DOUBLE_ARRAY(BASEBOX), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_REAL_ARRAY(GRAPHBOX) + GENPTR_DOUBLE_ARRAY(BASEBOX) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_PLOT3D", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astPlot3D( astI2P( *FRAME ), GRAPHBOX, BASEBOX, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + diff --git a/fpointlist.c b/fpointlist.c new file mode 100644 index 0000000..9644fdc --- /dev/null +++ b/fpointlist.c @@ -0,0 +1,117 @@ +/* +*+ +* Name: +* fpointlist.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST PointList class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the PointList class. + +* Routines Defined: +* AST_ISAPOINTLIST +* AST_POINTLIST + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-AUG-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "pointlist.h" /* C interface to the PointList class */ + +F77_LOGICAL_FUNCTION(ast_isapointlist)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPOINTLIST", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPointList( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_pointlist)( INTEGER(FRAME), + INTEGER(NPNT), + INTEGER(COORD), + INTEGER(INDIM), + DOUBLE_ARRAY(POINTS), + INTEGER(UNC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_INTEGER(NPNT) + GENPTR_INTEGER(COORD) + GENPTR_INTEGER(INDIM) + GENPTR_DOUBLE_ARRAY(POINTS) + GENPTR_INTEGER(UNC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_POINTLIST", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astPointList( astI2P( *FRAME ), *NPNT, *COORD, + *INDIM, POINTS, astI2P( *UNC ), "%s", + options ) ); + astFree( options ); + ) + return RESULT; +} + + + + diff --git a/fpolygon.c b/fpolygon.c new file mode 100644 index 0000000..12247d7 --- /dev/null +++ b/fpolygon.c @@ -0,0 +1,226 @@ +/* +*+ +* Name: +* fpolygon.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Polygon class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Polygon class. + +* Routines Defined: +* AST_ISAPOLYGON +* AST_POLYGON +* AST_DOWNSIZE + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 27-OCT-2004 (DSB): +* Original version. +* 28-MAY-2009 (DSB): +* Added AST_DOWNSIZE. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "polygon.h" /* C interface to the Polygon class */ + +F77_LOGICAL_FUNCTION(ast_isapolygon)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPOLYGON", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPolygon( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_polygon)( INTEGER(FRAME), + INTEGER(NPNT), + INTEGER(INDIM), + DOUBLE_ARRAY(POINTS), + INTEGER(UNC), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME) + GENPTR_INTEGER(NPNT) + GENPTR_INTEGER(INDIM) + GENPTR_DOUBLE_ARRAY(POINTS) + GENPTR_INTEGER(UNC) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_POLYGON", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astPolygon( astI2P( *FRAME ), *NPNT, *INDIM, POINTS, + astI2P( *UNC ), "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_downsize)( INTEGER(THIS), + DOUBLE(MAXERR), + INTEGER(MAXVERT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE(MAXERR) + GENPTR_INTEGER(MAXVERT) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_DOWNSIZE", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astDownsize( astI2P( *THIS ), *MAXERR, *MAXVERT ) ); + ) + return RESULT; +} + + +/* AST_OUTLINE requires a function for each possible data type, so + define it via a macro. */ +#define MAKE_AST_OUTLINE(f,F,Ftype,X,Xtype) \ +F77_INTEGER_FUNCTION(ast_outline##f)( Ftype(VALUE), \ + INTEGER(OPER), \ + Ftype##_ARRAY(ARRAY), \ + INTEGER_ARRAY(LBND), \ + INTEGER_ARRAY(UBND), \ + DOUBLE(MAXERR), \ + INTEGER(MAXVERT), \ + INTEGER_ARRAY(INSIDE), \ + LOGICAL(STARPIX), \ + INTEGER(STATUS) ) { \ + GENPTR_##Ftype(VALUE) \ + GENPTR_INTEGER(OPER) \ + GENPTR_##Ftype##_ARRAY(ARRAY) \ + GENPTR_INTEGER_ARRAY(LBND) \ + GENPTR_INTEGER_ARRAY(UBND) \ + GENPTR_DOUBLE(MAXERR) \ + GENPTR_INTEGER(MAXVERT) \ + GENPTR_INTEGER_ARRAY(INSIDE) \ + GENPTR_LOGICAL(STARPIX) \ + GENPTR_INTEGER(STATUS) \ +\ + F77_INTEGER_TYPE RESULT; \ +\ + astAt( "AST_OUTLINE"#F, NULL, 0 ); \ + astWatchSTATUS( \ + RESULT = astP2I( astOutline##X( *VALUE, *OPER, (Xtype *) ARRAY, LBND, \ + UBND, *MAXERR, *MAXVERT, INSIDE, \ + F77_ISTRUE( *STARPIX ) ? 1 : 0 ) ); \ + ) \ + return RESULT; \ +} + +/* Invoke the above macro to define a function for each data + type. Include synonyms for some functions. */ +MAKE_AST_OUTLINE(d,D,DOUBLE,D,double) +MAKE_AST_OUTLINE(r,R,REAL,F,float) +MAKE_AST_OUTLINE(i,I,INTEGER,I,int) +MAKE_AST_OUTLINE(ui,UI,INTEGER,UI,unsigned int) +MAKE_AST_OUTLINE(s,S,WORD,S,short int) +MAKE_AST_OUTLINE(us,US,UWORD,US,unsigned short int) +MAKE_AST_OUTLINE(w,W,WORD,S,short int) +MAKE_AST_OUTLINE(uw,UW,UWORD,US,unsigned short int) +MAKE_AST_OUTLINE(b,B,BYTE,B,signed char) +MAKE_AST_OUTLINE(ub,UB,UBYTE,UB,unsigned char) +#undef MAKE_AST_OUTLINE + + +/* AST_CONVEX requires a function for each possible data type, so + define it via a macro. */ +#define MAKE_AST_CONVEX(f,F,Ftype,X,Xtype) \ +F77_INTEGER_FUNCTION(ast_convex##f)( Ftype(VALUE), \ + INTEGER(OPER), \ + Ftype##_ARRAY(ARRAY), \ + INTEGER_ARRAY(LBND), \ + INTEGER_ARRAY(UBND), \ + LOGICAL(STARPIX), \ + INTEGER(STATUS) ) { \ + GENPTR_##Ftype(VALUE) \ + GENPTR_INTEGER(OPER) \ + GENPTR_##Ftype##_ARRAY(ARRAY) \ + GENPTR_INTEGER_ARRAY(LBND) \ + GENPTR_INTEGER_ARRAY(UBND) \ + GENPTR_LOGICAL(STARPIX) \ + GENPTR_INTEGER(STATUS) \ +\ + F77_INTEGER_TYPE RESULT; \ +\ + astAt( "AST_CONVEX"#F, NULL, 0 ); \ + astWatchSTATUS( \ + RESULT = astP2I( astConvex##X( *VALUE, *OPER, (Xtype *) ARRAY, LBND, \ + UBND, F77_ISTRUE( *STARPIX ) ? 1 : 0 ) ); \ + ) \ + return RESULT; \ +} + +/* Invoke the above macro to define a function for each data + type. Include synonyms for some functions. */ +MAKE_AST_CONVEX(d,D,DOUBLE,D,double) +MAKE_AST_CONVEX(r,R,REAL,F,float) +MAKE_AST_CONVEX(i,I,INTEGER,I,int) +MAKE_AST_CONVEX(ui,UI,INTEGER,UI,unsigned int) +MAKE_AST_CONVEX(s,S,WORD,S,short int) +MAKE_AST_CONVEX(us,US,UWORD,US,unsigned short int) +MAKE_AST_CONVEX(w,W,WORD,S,short int) +MAKE_AST_CONVEX(uw,UW,UWORD,US,unsigned short int) +MAKE_AST_CONVEX(b,B,BYTE,B,signed char) +MAKE_AST_CONVEX(ub,UB,UBYTE,UB,unsigned char) +#undef MAKE_AST_CONVEX + + diff --git a/fpolymap.c b/fpolymap.c new file mode 100644 index 0000000..6e3fb55 --- /dev/null +++ b/fpolymap.c @@ -0,0 +1,140 @@ +/* +*+ +* Name: +* fpolymap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST PolyMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the PolyMap class. + +* Routines Defined: +* AST_ISAPOLYMAP +* AST_POLYMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 27-SEP-2003 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "polymap.h" /* C interface to the PolyMap class */ + +F77_LOGICAL_FUNCTION(ast_isapolymap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPOLYMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPolyMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_polymap)( INTEGER(NIN), + INTEGER(NOUT), + INTEGER(NCOEFF_F), + DOUBLE_ARRAY(COEFF_F), + INTEGER(NCOEFF_I), + DOUBLE_ARRAY(COEFF_I), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NIN) + GENPTR_INTEGER(NOUT) + GENPTR_INTEGER(NCOEFF_F) + GENPTR_DOUBLE_ARRAY(COEFF_F) + GENPTR_INTEGER(NCOEFF_I) + GENPTR_DOUBLE_ARRAY(COEFF_I) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_POLYMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astPolyMap( *NIN, *NOUT, *NCOEFF_F, COEFF_F, *NCOEFF_I, + COEFF_I, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + + +F77_INTEGER_FUNCTION(ast_polytran)( INTEGER(THIS), + LOGICAL(FORWARD), + DOUBLE(ACC), + DOUBLE(MAXACC), + INTEGER(MAXORDER), + DOUBLE_ARRAY(LBND), + DOUBLE_ARRAY(UBND), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_LOGICAL(FORWARD) + GENPTR_DOUBLE(ACC) + GENPTR_DOUBLE(MAXACC) + GENPTR_INTEGER(MAXORDER) + GENPTR_DOUBLE_ARRAY(LBND) + GENPTR_DOUBLE_ARRAY(UBND) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_POLYTRAN", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astPolyTran( astI2P( *THIS ), F77_ISTRUE( *FORWARD ), + *ACC, *MAXACC, *MAXORDER, LBND, UBND ) ); + ) + return RESULT; +} + + diff --git a/fprism.c b/fprism.c new file mode 100644 index 0000000..7dc70b7 --- /dev/null +++ b/fprism.c @@ -0,0 +1,105 @@ +/* +*+ +* Name: +* fprism.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Prism class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Prism class. + +* Routines Defined: +* AST_ISAPRISM +* AST_PRISM + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 10-JAN-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "prism.h" /* C interface to the Prism class */ + + +F77_LOGICAL_FUNCTION(ast_isaprism)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAPRISM", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAPrism( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_prism)( INTEGER(REG1), + INTEGER(REG2), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(REG1) + GENPTR_INTEGER(REG2) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_PRISM", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + + RESULT = astP2I( astPrism( astI2P( *REG1 ), astI2P( *REG2 ), + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/frame.c b/frame.c new file mode 100644 index 0000000..88c13c7 --- /dev/null +++ b/frame.c @@ -0,0 +1,15859 @@ +/* +*class++ +* Name: +* Frame + +* Purpose: +* Coordinate system description. + +* Constructor Function: +c astFrame +f AST_FRAME + +* Description: +* This class is used to represent coordinate systems. It does this +* in rather the same way that a frame around a graph describes the +* coordinate space in which data are plotted. Consequently, a +* Frame has a Title (string) attribute, which describes the +* coordinate space, and contains axes which in turn hold +* information such as Label and Units strings which are used for +* labelling (e.g.) graphical output. In general, however, the +* number of axes is not restricted to two. +* +* Functions are available for converting Frame coordinate values +* into a form suitable for display, and also for calculating +* distances and offsets between positions within the Frame. +* +* Frames may also contain knowledge of how to transform to and +* from related coordinate systems. + +* Inheritance: +* The Frame class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* Frame also has the following attributes (if the Frame has only one +* axis, the axis specifier can be omited from the following attribute +* names): +* +* - AlignSystem: Coordinate system used to align Frames +* - Bottom(axis): Lowest axis value to display +* - Digits/Digits(axis): Number of digits of precision +* - Direction(axis): Display axis in conventional direction? +* - Domain: Coordinate system domain +* - Dut1: Difference between the UT1 and UTC timescale +* - Epoch: Epoch of observation +* - Format(axis): Format specification for axis values +* - InternalUnit(axis): Physical units for unformated axis values +* - Label(axis): Axis label +* - MatchEnd: Match trailing axes? +* - MaxAxes: Maximum number of Frame axes to match +* - MinAxes: Minimum number of Frame axes to match +* - Naxes: Number of Frame axes +* - NormUnit(axis): Normalised physical units for formatted axis values +* - ObsAlt: Geodetic altitude of observer +* - ObsLat: Geodetic latitude of observer +* - ObsLon: Geodetic longitude of observer +* - Permute: Permute axis order? +* - PreserveAxes: Preserve axes? +* - Symbol(axis): Axis symbol +* - System: Coordinate system used to describe the domain +* - Title: Frame title +* - Top(axis): Highest axis value to display +* - Unit(axis): Physical units for formatted axis values + +* Functions: +c In addition to those functions applicable to all Mappings, the +c following functions may also be applied to all Frames: +f In addition to those routines applicable to all Mappings, the +f following routines may also be applied to all Frames: +* +c - astAngle: Calculate the angle subtended by two points at a third point +c - astAxAngle: Find the angle from an axis, to a line through two points +c - astAxDistance: Calculate the distance between two axis values +c - astAxOffset: Calculate an offset along an axis +c - astConvert: Determine how to convert between two coordinate systems +c - astDistance: Calculate the distance between two points in a Frame +c - astFindFrame: Find a coordinate system with specified characteristics +c - astFormat: Format a coordinate value for a Frame axis +c - astGetActiveUnit: Determines how the Unit attribute will be used +c - astIntersect: Find the intersection between two geodesic curves +c - astMatchAxes: Find any corresponding axes in two Frames +c - astNorm: Normalise a set of Frame coordinates +c - astOffset: Calculate an offset along a geodesic curve +c - astOffset2: Calculate an offset along a geodesic curve in a 2D Frame +c - astPermAxes: Permute the order of a Frame's axes +c - astPickAxes: Create a new Frame by picking axes from an existing one +c - astResolve: Resolve a vector into two orthogonal components +c - astSetActiveUnit: Specify how the Unit attribute should be used +c - astUnformat: Read a formatted coordinate value for a Frame axis +f - AST_ANGLE: Find the angle subtended by two points at a third point +f - AST_AXANGLE: Find the angle from an axis, to a line through two points +f - AST_AXDISTANCE: Calculate the distance between two axis values +f - AST_AXOFFSET: Calculate an offset along an axis +f - AST_CONVERT: Determine how to convert between two coordinate systems +f - AST_DISTANCE: Calculate the distance between two points in a Frame +f - AST_FINDFRAME: Find a coordinate system with specified characteristics +f - AST_FORMAT: Format a coordinate value for a Frame axis +f - AST_GETACTIVEUNIT: Determines how the Unit attribute will be used +f - AST_INTERSECT: Find the intersection between two geodesic curves +f - AST_MATCHAXES: Find any corresponding axes in two Frames +f - AST_NORM: Normalise a set of Frame coordinates +f - AST_OFFSET: Calculate an offset along a geodesic curve +f - AST_OFFSET2: Calculate an offset along a geodesic curve in a 2D Frame +f - AST_PERMAXES: Permute the order of a Frame's axes +f - AST_PICKAXES: Create a new Frame by picking axes from an existing one +f - AST_RESOLVE: Resolve a vector into two orthogonal components +f - AST_SETACTIVEUNIT: Specify how the Unit attribute should be used +f - AST_UNFORMAT: Read a formatted coordinate value for a Frame axis + +* Notes: +* - When used as a Mapping, a Frame implements a unit (null) +* transformation in both the forward and inverse directions +* (equivalent to a UnitMap). The Nin and Nout attribute values are +* both equal to the number of Frame axes. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: B.S. Berry (Starlink) + +* History: +* 1-MAR-1996 (RFWS): +* Original version. +* 4-JUN-1996 (RFWS): +* Added the CleanDomain function to fold all Domain strings to +* upper case and remove white space. +* 12-JUL-1996 (RFWS): +* Over-ride the astReportPoints method to provide +* Frame-specific formatting. +* 11-SEP-1996 (RFWS): +* Added astGap (written by DSB). +* 10-JUN-1997 (RFWS): +* Re-implemented astConvert and astFindFrame. +* 1-SEP-1997 (RFWS): +* Added missing return statement in astAbbrev_. +* 14-NOV-1997 (RFWS): +* Fixed wrong amount of memory allocated in ValidateAxisSelection. +* 20-NOV-1997 (RFWS): +* Updated astConvert prologue. +* 22-DEC-1997 (RFWS): +* Updated astConvert prologue again. +* 15-FEB-1998 (RFWS): +* Added astUnformat. +* 2-MAR-1999 (RFWS); +* Fixed missing STATUS arguments in examples for AST_FINDFRAME +* prologue. +* 18-JUL-1999 (RFWS): +* Fixed memory leak in ConvertX. +* 21-JUN-2001 (DSB): +* Added methods astAngle and astOffset2. +* 29-AUG-2001 (DSB): +* Added methods astAxDistance and astAxOffset. +* 4-SEP-2001 (DSB): +* Added method astResolve. +* 9-SEP-2001 (DSB): +* Added method astBear. +* 21-SEP-2001 (DSB): +* Replaced astBear with astAxAngle. +* 10-OCT-2002 (DSB): +* Added Top and Bottom. +* 15-NOV-2002 (DSB): +* Moved the System and Epoch attributes from the SkyFrame class to +* this class. Added virtual method astValidateSystem, astSystemString, +* astSystemCode. Added attribute AlignSystem. +* 17-DEC-2002 (DSB): +* Added the GetActiveUnit, TestActiveUnit and SetActiveUnit functions. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitFrameVtab +* method. +* 15-SEP-2003 (DSB): +* Allow Frame attribute names to include an axis specifier within +* GetAttrib, SetAttrib, TestAttrib and ClearAttrib (eg "Domain(1)" +* is now accepted as equivalent to "Domain"). +* 24-JAN-2004 (DSB): +* o Added astFields. +* o Added argument "fmt" to Abbrev. +* 24-MAR-2004 (DSB): +* Add protected function astIsUnitFrame. +* 7-SEP-2004 (DSB): +* Modified SetUnit to exclude any trailing spaces +* 8-SEP-2004 (DSB): +* - Added astResolvePoints. +* - Override astEqual. +* 29-NOV-2004 (DSB): +* - Set/Get/Test/ClearAttrib: Allow axis specifier to be omitted from +* axis attribute names if the Frame only has one axis. +* 2-FEB-2005 (DSB): +* - Avoid using astStore to allocate more storage than is supplied +* in the "data" pointer. This can cause access violations since +* astStore will then read beyond the end of the "data" area. +* 17-FEB-2005 (DSB): +* - Change use of ActiveUnit flag so that both target and template +* Frames must have active units in order for the Mapping to take +* account of differences in units. Previously, the test was based +* on the template Frame alone. +* 23-MAR-2005 (DSB): +* - GetActiveUnit: Always return zero if the Frame contains any +* SkyAxes. +* 5-APR-2005 (DSB): +* Correct error checking in Clear/Get/Set/TestAttrib. +* 12-MAY-2005 (DSB): +* Added astNormBox method. +* 16-JUN-2005 (DSB): +* Added documentation for the TimeFrame class. +* 12-AUG-2005 (DSB): +* Added ObsLat and ObsLon attributes. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 15-MAY-2006 (DSB): +* Remove unused global variable parent_equal. +* 26-JUN-2006 (DSB): +* Document the use of the Direction attribute by the Plot class. +* 30-JUN-2006 (DSB): +* Allow astAbbrev to have a null "str1" value. +* 16-AUG-2006 (DSB): +* Correct "Class Applicability" to "Applicability". +* 5-OCT-2006 (DSB): +* Increase the number of digits used when formating a ObsLon or +* ObsLat value in GetAttrib. +* 14-OCT-2006 (DSB): +* - Add Dut1 attribute +* 26-JAN-2007 (DSB): +* Fix bug in NewUnit that causes segvio when changing axis symbols +* to accomodate changes in units. +* 17-MAY-2007 (DSB): +* Added read-only attribute NormUnit. +* 21-MAY-2007 (DSB): +* Use rather than ignore the value returned by astTestAxisDigits in +* TestAttrib. +* 25-JUN-2007 (DSB): +* Documentation typos. +* 26-NOV-2007 (DSB): +* In Clear/Get/Set/TestAttrib, include any appropriate axis index in +* the attribute name when attempting to get the attribute value from +* the primary frame +* 17-NOV-2008 (DSB): +* Correct parent class in invocation of astMAKE_ISA. +* 14-JAN-2009 (DSB): +* Added astIntersect. +* 18-MAR-2009 (DSB): +* Fixed bug in LineCrossing. +* 18-JUN-2000 (DSB): +* Added ObsAlt attribute. +* 28-SEP-2009 (DSB): +* Added astMatchAxes method. +* 22-MAR-2011 (DSB): +* Add astFrameGrid method. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +* 11-APR-2012 (DSB): +* Change astValidateAxis so that it can permute in either direction. +* 29-APR-2013 (DSB): +* Added protected methods astSetFrameVariants and astGetFrameVariants. +* 1-MAY-2013 (DSB): +* Override the astDoNotSimplify method to indicate that Frames should +* always be simplified. This is mainly because the STC class uses the +* Ident attribute with Frames, and preventing such frames from +* simplifying (which is what the parent astDoNotSimplify method does) +* causes the STC tester in the ast_tester directory to fail. +* 10-FEB-2015 (DSB): +* When checking attribute settings for attribute names that end with +* an axis index, stop looking for the axis index when the first equals +* sign is encountered. +* 17-APR-2015 (DSB): +* Added astCentre. +* 27-APR-2015 (DSB): +* Added read-only attribute InternalUnit. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Frame + +/* Define numerical constants for use in this module. */ +#define LABEL_BUFF_LEN 100 /* Max length of default axis Label string */ +#define SYMBOL_BUFF_LEN 50 /* Max length of default axis Symbol string */ +#define TITLE_BUFF_LEN 100 /* Max length of default title string */ +#define GETATTRIB_BUFF_LEN 50 /* Max length of string returned by GetAttrib */ +#define ASTFMTDECIMALYR_BUFF_LEN 50 /* Max length of string returned by GetAttrib */ +#define ASTFORMATID_MAX_STRINGS 50 /* Number of string values buffer by astFormatID*/ + + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__CART +#define LAST_SYSTEM AST__CART + +/* Define macros to implement methods for accessing axis attributes. */ +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear an attribute value for a Frame axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "frame.h" +* MAKE_CLEAR(attribute) + +* Class Membership: +* Defined by the Frame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstFrame *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear_( AstFrame *this, int axis ) +* +* which implement a method for clearing an attribute value for a specified +* axis of a Frame. + +* Parameters: +* attribute +* The name of the attribute to be cleared, as it appears in the +* function name (e.g. Label in "astClearLabel"). + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* void astClearAxis( AstAxis *this ) +* +* which clears the required attribute for an Axis object. +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attribute( AstFrame *this, int axis, int *status ) { \ + AstAxis *ax; /* Pointer to Axis object */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index and obtain a pointer to the required Axis. */ \ + (void) astValidateAxis( this, axis, 1, "astClear" #attribute ); \ + ax = astGetAxis( this, axis ); \ +\ +/* Clear the Axis attribute. */ \ + astClearAxis##attribute( ax ); \ +\ +/* Annul the Axis pointer. */ \ + ax = astAnnul( ax ); \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attribute##_( AstFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,Frame,Clear##attribute))( this, axis, status ); \ +} + +/* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get an attribute value for a Frame axis. + +* Type: +* Private macro. + +* Synopsis: +# #include "frame.h" +* MAKE_GET(attribute,type,bad_value,default,assign_default) + +* Class Membership: +* Defined by the Frame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstFrame *this, int axis ) +* +* and an external interface function of the form: +* +* Type astGet_( AstFrame *this, int axis ) +* +* which implement a method for getting an attribute value for a specified +* axis of a Frame. + +* Parameters: +* attribute +* The name of the attribute whose value is to be obtained, as +* it appears in the function name (e.g. Label in +* "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* default +* A boolean (int) constant that indicates whether a new default value +* should be returned by the method if the requested attribute has not +* been set for the axis. If this value is zero, the axis default will +* be used instead. +* assign_default +* An expression that evaluates to the new default value to be assigned. +* This value is ignored if "default" is zero, but a valid (e.g. +* constant) value should nevertheless be supplied. + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* astGetAxis( AstAxis *this ) +* +* which gets the required attribute for an Axis object. +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_GET(attribute,type,bad_value,default,assign_default) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attribute( AstFrame *this, int axis, int *status ) { \ + AstAxis *ax; /* Pointer to Axis object */ \ + int digits_set; /* Axis Digits attribute set? */ \ + int old_axis; /* Original (un-permuted) axis index */ \ + type result; /* Result to be returned */ \ +\ +/* Initialise. */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate and permute the axis index and obtain a pointer to the required \ + Axis. */ \ + old_axis = axis; \ + axis = astValidateAxis( this, axis, 1, "astGet" #attribute ); \ + ax = astGetAxis( this, old_axis ); \ +\ +/* Since the Axis is "managed" by the enclosing Frame, we next test if any \ + Axis attributes which may affect the result are undefined (i.e. have not \ + been explicitly set). If so, we over-ride them, giving them temporary \ + values dictated by the Frame. Only the Digits attribute is relevant \ + here. */ \ + digits_set = astTestAxisDigits( ax ); \ + if ( !digits_set ) astSetAxisDigits( ax, astGetDigits( this ) ); \ +\ +/* If the default value is to be over-ridden, test if the Axis attribute has \ + been set. Then, if required, obtain the attribute value from the Axis. */ \ + if ( !(default) || astTestAxis##attribute( ax ) ) { \ + result = astGetAxis##attribute( ax ); \ +\ +/* If required, assign the new default value. */ \ + } else { \ + result = (assign_default); \ + } \ +\ +/* Clear Axis attributes which were temporarily over-ridden above and annul \ + the Axis pointer. */ \ + if ( !digits_set ) astClearAxisDigits( ax ); \ + ax = astAnnul( ax ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attribute##_( AstFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,Frame,Get##attribute))( this, axis, status ); \ +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set an attribute value for a Frame axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "frame.h" +* MAKE_SET(attribute,type) + +* Class Membership: +* Defined by the Frame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstFrame *this, int axis, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstFrame *this, int axis, value ) +* +* which implement a method for setting an attribute value for a specified +* axis of a Frame. + +* Parameters: +* attribute +* The name of the attribute to be set, as it appears in the +* function name (e.g. Label in "astSetLabel"). +* type +* The C type of the attribute. + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* void astSetAxis( AstAxis *this, value ) +* +* which sets the required attribute for an Axis object. +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,type) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attribute( AstFrame *this, int axis, type value, int *status ) { \ + AstAxis *ax; /* Pointer to Axis object */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index and obtain a pointer to the required Axis. */ \ + (void) astValidateAxis( this, axis, 1, "astSet" #attribute ); \ + ax = astGetAxis( this, axis ); \ +\ +/* Set the Axis attribute value. */ \ + astSetAxis##attribute( ax, value ); \ +\ +/* Annul the Axis pointer. */ \ + ax = astAnnul( ax ); \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attribute##_( AstFrame *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,Frame,Set##attribute))( this, axis, value, status ); \ +} + +/* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a method to test if an attribute has been set for a Frame axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "frame.h" +* MAKE_TEST(attribute) + +* Class Membership: +* Defined by the Frame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( AstFrame *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest_( AstFrame *this, int axis ) +* +* which implement a method for testing if an attribute has been set for a +* specified axis of a Frame. + +* Parameters: +* attribute +* The name of the attribute to be tested, as it appears in the +* function name (e.g. Label in "astTestLabel"). + +* Notes: +* - This macro assumes the existence of a method of the form: +* +* void astTestAxis( AstAxis *this ) +* +* which tests the required attribute for an Axis object. +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_TEST(attribute) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attribute( AstFrame *this, int axis, int *status ) { \ + AstAxis *ax; /* Pointer to Axis object */ \ + int result; /* Value to be returned */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Validate the axis index and obtain a pointer to the required Axis. */ \ + (void) astValidateAxis( this, axis, 1, "astTest" #attribute ); \ + ax = astGetAxis( this, axis ); \ +\ +/* Test if the attribute has been set. */ \ + result = astTestAxis##attribute( ax ); \ +\ +/* Annul the Axis pointer. */ \ + ax = astAnnul( ax ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attribute##_( AstFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,Frame,Test##attribute))( this, axis, status ); \ +} + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "pointset.h" /* Sets of points */ +#include "unitmap.h" /* Unit Mapping */ +#include "permmap.h" /* Coordinate permutation Mapping */ +#include "cmpmap.h" /* Compound Mappings */ +#include "axis.h" /* Coordinate Axis */ +#include "skyaxis.h" /* Sky coordinate axes */ +#include "skyframe.h" /* Celestial coordinate frames */ +#include "channel.h" /* I/O channels */ +#include "frame.h" /* Interface definition for this class */ +#include "frameset.h" /* Collections of Frames */ +#include "cmpframe.h" /* Compound Frames */ +#include "pal.h" /* SLALIB library interface */ +#include "unit.h" /* Units identification and mapping */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_cleanattribs)( AstObject *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Define a variable to hold a SkyFrame which will be used for formatting + and unformatting ObsLat and ObsLon values. */ +static AstSkyFrame *skyframe; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->AstFormatID_Init = 0; \ + globals->AstFormatID_Istr = 0; \ + globals->Label_Buff[ 0 ] = 0; \ + globals->Symbol_Buff[ 0 ] = 0; \ + globals->Title_Buff[ 0 ] = 0; \ + globals->AstFmtDecimalYr_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Frame) + +#define class_init astGLOBAL(Frame,Class_Init) +#define class_vtab astGLOBAL(Frame,Class_Vtab) +#define getattrib_buff astGLOBAL(Frame,GetAttrib_Buff) +#define astformatid_strings astGLOBAL(Frame,AstFormatID_Strings) +#define astformatid_istr astGLOBAL(Frame,AstFormatID_Istr) +#define astformatid_init astGLOBAL(Frame,AstFormatID_Init) +#define label_buff astGLOBAL(Frame,Label_Buff) +#define symbol_buff astGLOBAL(Frame,Symbol_Buff) +#define title_buff astGLOBAL(Frame,Title_Buff) +#define astfmtdecimalyr_buff astGLOBAL(Frame,AstFmtDecimalYr_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ]; + +/* Strings returned by astFormatID */ +static char *astformatid_strings[ ASTFORMATID_MAX_STRINGS ]; + +/* Offset of next string in "AstFormatID_Strings" */ +static int astformatid_istr; + +/* "AstFormatID_Strings" array initialised? */ +static int astformatid_init; + +/* Default Label string buffer */ +static char label_buff[ LABEL_BUFF_LEN + 1 ]; + +/* Default Symbol buffer */ +static char symbol_buff[ SYMBOL_BUFF_LEN + 1 ]; + +/* Default Title string buffer */ +static char title_buff[ TITLE_BUFF_LEN + 1 ]; + +/* Buffer for result string */ +static char astfmtdecimalyr_buff[ ASTFMTDECIMALYR_BUFF_LEN + 1 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstAxis *GetAxis( AstFrame *, int, int * ); +static AstFrame *PickAxes( AstFrame *, int, const int[], AstMapping **, int * ); +static AstFrameSet *Convert( AstFrame *, AstFrame *, const char *, int * ); +static AstFrameSet *ConvertX( AstFrame *, AstFrame *, const char *, int * ); +static AstFrameSet *FindFrame( AstFrame *, AstFrame *, const char *, int * ); +static void MatchAxes( AstFrame *, AstFrame *, int *, int * ); +static void MatchAxesX( AstFrame *, AstFrame *, int *, int * ); +static AstLineDef *LineDef( AstFrame *, const double[2], const double[2], int * ); +static AstPointSet *FrameGrid( AstFrame *, int, const double *, const double *, int * ); +static AstPointSet *ResolvePoints( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static char *CleanDomain( char *, int * ); +static const char *Abbrev( AstFrame *, int, const char *, const char *, const char *, int * ); +static const char *Format( AstFrame *, int, double, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetDefaultLabel( int, int * ); +static const char *GetDefaultSymbol( AstFrame *, int, int * ); +static const char *GetDefaultTitle( AstFrame *, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetFormat( AstFrame *, int, int * ); +static const char *GetInternalUnit( AstFrame *, int, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetNormUnit( AstFrame *, int, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const int *GetPerm( AstFrame *, int * ); +static double Angle( AstFrame *, const double[], const double[], const double[], int * ); +static double AxAngle( AstFrame *, const double[], const double[], int, int * ); +static double AxDistance( AstFrame *, int, double, double, int * ); +static double AxOffset( AstFrame *, int, double, double, int * ); +static double Distance( AstFrame *, const double[], const double[], int * ); +static double Centre( AstFrame *, int, double, double, int * ); +static double Gap( AstFrame *, int, double, int *, int * ); +static double Offset2( AstFrame *, const double[2], double, double, double[2], int * ); +static int AxIn( AstFrame *, int, double, double, double, int, int * ); +static int ConsistentMaxAxes( AstFrame *, int, int * ); +static int ConsistentMinAxes( AstFrame *, int, int * ); +static int DefaultMaxAxes( AstFrame *, int * ); +static int DefaultMinAxes( AstFrame *, int * ); +static int DoNotSimplify( AstMapping *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int Fields( AstFrame *, int, const char *, const char *, int, char **, int *, double *, int * ); +static int GetDigits( AstFrame *, int * ); +static int GetDirection( AstFrame *, int, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int GetIsSimple( AstMapping *, int * ); +static int LineContains( AstFrame *, AstLineDef *, int, double *, int * ); +static int LineCrossing( AstFrame *, AstLineDef *, AstLineDef *, double **, int * ); +static int GetObjSize( AstObject *, int * ); +static void CleanAttribs( AstObject *, int * ); +static void LineOffset( AstFrame *, AstLineDef *, double, double, double[2], int * ); + +static double GetTop( AstFrame *, int, int * ); +static int TestTop( AstFrame *, int, int * ); +static void ClearTop( AstFrame *, int, int * ); +static void SetTop( AstFrame *, int, double, int * ); + +static double GetBottom( AstFrame *, int, int * ); +static int TestBottom( AstFrame *, int, int * ); +static void ClearBottom( AstFrame *, int, int * ); +static void SetBottom( AstFrame *, int, double, int * ); + +static AstSystemType GetSystem( AstFrame *, int * ); +static int TestSystem( AstFrame *, int * ); +static void ClearSystem( AstFrame *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); + +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static int TestAlignSystem( AstFrame *, int * ); +static void ClearAlignSystem( AstFrame *, int * ); +static void SetAlignSystem( AstFrame *, AstSystemType, int * ); + +static double GetEpoch( AstFrame *, int * ); +static int TestEpoch( AstFrame *, int * ); +static void ClearEpoch( AstFrame *, int * ); +static void SetEpoch( AstFrame *, double, int * ); + +static double GetObsLat( AstFrame *, int * ); +static int TestObsLat( AstFrame *, int * ); +static void ClearObsLat( AstFrame *, int * ); +static void SetObsLat( AstFrame *, double, int * ); + +static double GetObsLon( AstFrame *, int * ); +static int TestObsLon( AstFrame *, int * ); +static void ClearObsLon( AstFrame *, int * ); +static void SetObsLon( AstFrame *, double, int * ); + +static double GetObsAlt( AstFrame *, int * ); +static int TestObsAlt( AstFrame *, int * ); +static void ClearObsAlt( AstFrame *, int * ); +static void SetObsAlt( AstFrame *, double, int * ); + +static double GetDut1( AstFrame *, int * ); +static int TestDut1( AstFrame *, int * ); +static void ClearDut1( AstFrame *, int * ); +static void SetDut1( AstFrame *, double, int * ); + +static int GetActiveUnit( AstFrame *, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static void SetActiveUnit( AstFrame *, int, int * ); + +static AstFrameSet *GetFrameVariants( AstFrame *, int * ); +static void SetFrameVariants( AstFrame *, AstFrameSet *, int * ); + +static int GetFrameFlags( AstFrame *, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int GetMatchEnd( AstFrame *, int * ); +static int GetMaxAxes( AstFrame *, int * ); +static int GetMinAxes( AstFrame *, int * ); +static int GetNaxes( AstFrame *, int * ); +static int GetNin( AstMapping *, int * ); +static int GetNout( AstMapping *, int * ); +static int GetPermute( AstFrame *, int * ); +static int GetPreserveAxes( AstFrame *, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestDigits( AstFrame *, int * ); +static int TestDirection( AstFrame *, int, int * ); +static int TestDomain( AstFrame *, int * ); +static int TestFormat( AstFrame *, int, int * ); +static int TestLabel( AstFrame *, int, int * ); +static int TestMatchEnd( AstFrame *, int * ); +static int TestMaxAxes( AstFrame *, int * ); +static int TestMinAxes( AstFrame *, int * ); +static int TestPermute( AstFrame *, int * ); +static int TestPreserveAxes( AstFrame *, int * ); +static int TestSymbol( AstFrame *, int, int * ); +static int TestTitle( AstFrame *, int * ); +static int TestUnit( AstFrame *, int, int * ); +static int IsUnitFrame( AstFrame *, int * ); +static int Unformat( AstFrame *, int, const char *, double *, int * ); +static int ValidateAxis( AstFrame *, int, int, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static void AddUnderscores( char *, int * ); +static void CheckPerm( AstFrame *, const int *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearDigits( AstFrame *, int * ); +static void ClearDirection( AstFrame *, int, int * ); +static void ClearDomain( AstFrame *, int * ); +static void ClearFormat( AstFrame *, int, int * ); +static void ClearLabel( AstFrame *, int, int * ); +static void ClearMatchEnd( AstFrame *, int * ); +static void ClearMaxAxes( AstFrame *, int * ); +static void ClearMinAxes( AstFrame *, int * ); +static void ClearPermute( AstFrame *, int * ); +static void ClearPreserveAxes( AstFrame *, int * ); +static void ClearSymbol( AstFrame *, int, int * ); +static void ClearTitle( AstFrame *, int * ); +static void ClearUnit( AstFrame *, int, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Intersect( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * ); +static void Norm( AstFrame *, double[], int * ); +static void NormBox( AstFrame *, double[], double[], AstMapping *, int * ); +static void Offset( AstFrame *, const double[], const double[], double, double[], int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void PermAxes( AstFrame *, const int[], int * ); +static void PrimaryFrame( AstFrame *, int, AstFrame **, int *, int * ); +static void ReportPoints( AstMapping *, int, AstPointSet *, AstPointSet *, int * ); +static void Resolve( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetAxis( AstFrame *, int, AstAxis *, int * ); +static void SetDigits( AstFrame *, int, int * ); +static void SetDirection( AstFrame *, int, int, int * ); +static void SetDomain( AstFrame *, const char *, int * ); +static void SetFormat( AstFrame *, int, const char *, int * ); +static void SetFrameFlags( AstFrame *, int, int * ); +static void SetLabel( AstFrame *, int, const char *, int * ); +static void SetMatchEnd( AstFrame *, int, int * ); +static void SetMaxAxes( AstFrame *, int, int * ); +static void SetMinAxes( AstFrame *, int, int * ); +static void SetPermute( AstFrame *, int, int * ); +static void SetPreserveAxes( AstFrame *, int, int * ); +static void SetSymbol( AstFrame *, int, const char *, int * ); +static void SetTitle( AstFrame *, const char *, int * ); +static void SetUnit( AstFrame *, int, const char *, int * ); +static void NewUnit( AstAxis *, const char *, const char *, const char *, const char *, int * ); +static void ValidateAxisSelection( AstFrame *, int, const int *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static const char *Abbrev( AstFrame *this, int axis, const char *fmt, + const char *str1, const char *str2, int *status ) { +/* +*+ +* Name: +* astAbbrev + +* Purpose: +* Abbreviate a formatted Frame axis value by skipping leading fields. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* const char *astAbbrev( AstFrame *this, int axis, const char *fmt, +* const char *str1, const char *str2 ) + +* Class Membership: +* Frame method. + +* Description: +* This function compares two Frame axis values that have been +* formatted (using astFormat) and determines if they have any +* redundant leading fields (i.e. leading fields in common which +* can be suppressed when tabulating the values or plotting them on +* the axis of a graph). + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the Frame axis for which the values have been +* formatted (axis numbering starts at zero for the first axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format specification used to format the two values. +* str1 +* Pointer to a constant null-terminated string containing the +* first formatted value. If this is null, the returned pointer +* points to the start of the final field in str2. +* str2 +* Pointer to a constant null-terminated string containing the +* second formatted value. + +* Returned Value: +* A pointer into the "str2" string which locates the first +* character in the first field that differs between the two +* formatted values. +* +* If the two values have no leading fields in common, the returned +* value will point at the start of string "str2". If the two +* values are equal, it will point at the terminating null at the +* end of this string. + +* Notes: +* - This function assumes that the format specification used was +* the same when both values were formatted and that they both +* apply to the same Frame axis. +* - A pointer to the start of "str2" will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + const char *result; /* Result pointer to return */ + +/* Initialise. */ + result = str2; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index and obtain a pointer to the required + Axis. */ + (void) astValidateAxis( this, axis, 1, "astAbbrev" ); + ax = astGetAxis( this, axis ); + +/* Invoke the Axis astAxisAbbrev method to perform the processing. */ + result = astAxisAbbrev( ax, fmt, str1, str2 ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = str2; + +/* Return the result. */ + return result; +} + +static void AddUnderscores( char *string, int *status ) { +/* +* Name: +* AddUnderscores + +* Purpose: +* Add underscores to a string in place of white space. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* void AddUnderscores( char *string, int *status ) + +* Class Membership: +* Frame member function. + +* Description: +* This function changes all white space characters in a string into +* the underscore character '_'. + +* Parameters: +* this +* Pointer to the Frame. +* string +* Pointer to the null terminated string to be processed. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables. */ + int i; /* Loop counter for characters */ + +/* Inspect each character in the string. */ + for ( i = 0; string[ i ]; i++ ) { + +/* If it is a white space character, replace it with an underscore. */ + if ( isspace( string[ i ] ) ) string[ i ] = '_'; + } +} + +static double Angle( AstFrame *this, const double a[], + const double b[], const double c[], int *status ) { +/* +*++ +* Name: +c astAngle +f AST_ANGLE + +* Purpose: +* Calculate the angle subtended by two points at a third point. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c double astAngle( AstFrame *this, const double a[], const double b[], +c const double c[] ) +f RESULT = AST_ANGLE( THIS, A, B, C, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* finds the angle at point B between the line joining points A and B, +* and the line joining points C and B. These lines will in fact be +* geodesic curves appropriate to the Frame in use. For instance, in +* SkyFrame, they will be great circles. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c a +f A( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the first point. +c b +f B( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the second point. +c c +f C( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the third point. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astAngle +f AST_ANGLE = DOUBLE PRECISION +* The angle in radians, from the line AB to the line CB. If the +* Frame is 2-dimensional, it will be in the range $\pm \pi$, +* and positive rotation is in the same sense as rotation from +* the positive direction of axis 2 to the positive direction of +* axis 1. If the Frame has more than 2 axes, a positive value will +* always be returned in the range zero to $\pi$. + +* Notes: +* - A value of AST__BAD will also be returned if points A and B are +* co-incident, or if points B and C are co-incident. +* - A value of AST__BAD will also be returned if this function is +c invoked with the AST error status set, or if it should fail for +f invoked with STATUS set to an error value, or if it should fail for +* any reason. +*-- +*/ + +/* Local Variables: */ + double *ab; /* Pointer to vector AB */ + double *cb; /* Pointer to vector CB */ + double cos; /* cosine of required angle */ + double anga; /* Angle from +ve Y to the line BA */ + double angc; /* Angle from +ve Y to the line BC */ + double result; /* Result value to return */ + double sla; /* Squared length of vector AB */ + double slc; /* Squared length of vector CB */ + double sp; /* Scalar product of AB and CB */ + int axis; /* Axis index */ + int naxes; /* Number of Frame axes */ + int ok; /* Supplied points OK? */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Assume everything is ok */ + ok = 1; + +/* Obtain the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Obtain workspace. */ + ab = (double *) astMalloc( sizeof(double)*naxes ); + cb = (double *) astMalloc( sizeof(double)*naxes ); + +/* Check all positions are good, and form the vectors from b to a, and + from b to c. Also find the squared length of each vector. */ + if( astOK ) { + sla = 0.0; + slc = 0.0; + for( axis = 0; axis < naxes; axis++ ) { + if( a[ axis ] == AST__BAD || b[ axis ] == AST__BAD || + c[ axis ] == AST__BAD ) { + ok = 0; + break; + } else { + ab[ axis ] = a[ axis ] - b[ axis ]; + cb[ axis ] = c[ axis ] - b[ axis ]; + sla += ( ab[ axis ] )*( ab[ axis ] ); + slc += ( cb[ axis ] )*( cb[ axis ] ); + } + } + +/* Check that neither of the vectors have zero length. */ + if( sla == 0 || slc == 0 ) ok = 0; + +/* Only proceed if these checks were passed. */ + if ( ok ) { + +/* Deal first with 2-dimensional Frames. */ + if( naxes == 2 ) { + +/* Find the angle from +ve Y to the line BA. */ + anga = atan2( ab[ 0 ], ab[ 1 ] ); + +/* Find the angle from +ve Y to the line BC. */ + angc = atan2( cb[ 0 ], cb[ 1 ] ); + +/* Find the difference, folded into the range +/- PI. */ + result = palDrange( angc - anga ); + +/* Now deal with Frames with more than 2 axes. */ + } else { + +/* Form the scalar product of the two vectors. */ + sp = 0.0; + for( axis = 0; axis < naxes; axis++ ) { + sp += ab[ axis ]*cb[ axis ]; + } + +/* Derive the required angle from the normalized scalar product. */ + cos = sp/sqrt( sla*slc ); + if( cos > 1.0 ) { + cos = 1.0; + } else if( cos < -1.0 ) { + cos = -1.0; + } + result =acos( cos ); + } + } + } + +/* Free the work space. */ + ab = (double *) astFree( (void *) ab ); + cb = (double *) astFree( (void *) cb ); + +/* Return the result. */ + return result; +} + +static double AxAngle( AstFrame *this, const double a[], const double b[], int axis, int *status ) { +/* +*++ +* Name: +c astAxAngle +f AST_AXANGLE + +* Purpose: +* Returns the angle from an axis, to a line through two points. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c double astAxAngle( AstFrame *this, const double a[], const double b[], int axis ) +f RESULT = AST_AXANGLE( THIS, A, B, AXIS, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* finds the angle, as seen from point A, between the positive +* direction of a specified axis, and the geodesic curve joining point +* A to point B. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c a +f A( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the first point. +c b +f B( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the second point. +c axis +f AXIS = INTEGER (Given) +* The number of the Frame axis from which the angle is to be +* measured (axis numbering starts at 1 for the first axis). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astAxAngle +f AST_AXANGLE = DOUBLE PRECISION +* The angle in radians, from the positive direction of the +* specified axis, to the line AB. If the Frame is 2-dimensional, +* it will be in the range [-PI/2,+PI/2], and positive rotation is in +* the same sense as rotation from the positive direction of axis 2 +* to the positive direction of axis 1. If the Frame has more than 2 +* axes, a positive value will always be returned in the range zero +* to PI. + +* Notes: +c - The geodesic curve used by this function is the path of +f - The geodesic curve used by this routine is the path of +* shortest distance between two points, as defined by the +c astDistance function. +f AST_DISTANCE function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the require +* position angle is undefined. +*-- +*/ + +/* Local Variables: */ + double *aa; /* Pointer to third point */ + double ab; /* Absolute value of component */ + double mxab; /* Largest absolute value of component */ + double result; /* The returned value */ + int iaxis; /* Axis index */ + int naxes; /* Number of Frame axes */ + int ok; /* Are values ok to use? */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxAngle" ); + +/* Obtain the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Obtain workspace. */ + aa = (double *) astMalloc( sizeof(double)*naxes ); + +/* Create a position which is offset slightly from point A in the + positive direction of the specified axis. Also get the largest absolute + value of any component of the vector AB. */ + if( astOK ) { + ok = 1; + mxab = 0.0; + + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + if( a[ iaxis ] != AST__BAD && b[ iaxis ] != AST__BAD ) { + aa[ iaxis ] = a[ iaxis ]; + ab = fabs( a[ iaxis ] - b[ iaxis ] ); + if( ab > mxab ) mxab = ab; + } else { + ok = 0; + break; + } + } + + if( ok ) { + + if( a[ axis - 1 ] != 0.0 ) { + aa[ axis - 1 ] += 10000.0*DBL_EPSILON*fabs( a[ axis - 1 ] ); + + } else if( b[ axis - 1 ] != 0.0 ) { + aa[ axis - 1 ] = 10000.0*DBL_EPSILON*fabs( b[ iaxis - 1 ] ); + + } else if( mxab != 0.0 ) { + aa[ axis - 1 ] = 10000.0*DBL_EPSILON*mxab; + + } else { + aa[ axis - 1 ] = 1.0; + } + +/* Use astAngle to get the required angle. */ + result = astAngle( this, aa, a, b ); + } + } + +/* Free the workspace. */ + aa = (double *) astFree( (void *) aa ); + +/* Return the result. */ + return result; + +} + +static double AxDistance( AstFrame *this, int axis, double v1, double v2, int *status ) { +/* +*++ +* Name: +c astAxDistance +f AST_AXDISTANCE + +* Purpose: +* Find the distance between two axis values. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c double astAxDistance( AstFrame *this, int axis, double v1, double v2 ) +f RESULT = AST_AXDISTANCE( THIS, AXIS, V1, V2, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function returns a signed value representing the axis increment +f This routine returns a signed value representing the axis increment +* from axis value v1 to axis value v2. +* +* For a simple Frame, this is a trivial operation returning the +* difference between the two axis values. But for other derived classes +* of Frame (such as a SkyFrame) this is not the case. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c axis +f AXIS = INTEGER (Given) +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +c v1 +f V1 = DOUBLE PRECISION (Given) +* The first axis value. +c v2 +f V2 = DOUBLE PRECISION (Given) +* The second axis value. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astAxDistance +f AST_AXDISTANCE = DOUBLE PRECISION +* The distance from the first to the second axis value. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input values has this value. +* - A "bad" value will also be returned if this function is +c invoked with the AST error status set, or if it should fail for +f invoked with STATUS set to an error value, or if it should fail for +* any reason. +*-- + +* Implementation Deficiencies; +* - The protected interface for this function uses 1-based axis +* numbering (like the public interface), rather than the more usual +* zero-based system used by all other protected interfaces. There is +* no real reason for this, and it should be changed at some time. + +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + double result; /* The returned answer */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index and obtain a pointer to the required Axis. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxDistance" ); + ax = astGetAxis( this, axis - 1 ); + +/* Use the AxisDistance method associated with the Axis. */ + if( astOK ) result = astAxisDistance( ax, v1, v2 ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* Return the result. */ + return result; + +} + +static int AxIn( AstFrame *this, int axis, double lo, double hi, double val, + int closed, int *status ){ +/* +*+ +* Name: +* astAxIn + +* Purpose: +* Test if an axis value lies within a given interval. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astAxIn( AstFrame *this, int axis, double lo, double hi, double val, +* int closed ) + +* Class Membership: +* Frame member function. + +* Description: +* This function returns non-zero if a given axis values lies within a +* given axis interval. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index of the axis. The first axis has index 0. +* lo +* The lower axis limit of the interval. +* hi +* The upper axis limit of the interval. +* val +* The axis value to be tested. +* closed +* If non-zero, then the lo and hi axis values are themselves +* considered to be within the interval. Otherwise they are outside. + +* Returned Value: +* Non-zero if the test value is inside the interval. + +* Applicability: +* Frame +* Uses simple Euclidean test +* SkyFrame +* All angles which are numerically between "lo" and "hi" are within +* the interval. Angle outside this range are also within the interval +* if they can be brought into the range by addition or subtraction +* of a multiple of 2.PI. +*- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + int result; /* Returned value */ + +/* For speed, omit the astOK check and axis validation (since this is + protected code, AST should get it right). Obtain a pointer to the + required Axis. */ + ax = astGetAxis( this, axis ); + +/* Use the AxisIn method associated with the Axis. */ + result = ax ? astAxisIn( ax, lo, hi, val, closed ) : 0; + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* Return the result. */ + return result; +} + +static double AxOffset( AstFrame *this, int axis, double v1, double dist, int *status ) { +/* +*++ +* Name: +c astAxOffset +f AST_AXOFFSET + +* Purpose: +* Add an increment onto a supplied axis value. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c double astAxOffset( AstFrame *this, int axis, double v1, double dist ) +f RESULT = AST_AXOFFSET( THIS, AXIS, V1, DIST, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function returns an axis value formed by adding a signed axis +f This routine returns an axis value formed by adding a signed axis +* increment onto a supplied axis value. +* +* For a simple Frame, this is a trivial operation returning the +* sum of the two supplied values. But for other derived classes +* of Frame (such as a SkyFrame) this is not the case. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c axis +f AXIS = INTEGER (Given) +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +c v1 +f V1 = DOUBLE PRECISION (Given) +* The original axis value. +c dist +f DIST = DOUBLE PRECISION (Given) +* The axis increment to add to the original axis value. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astAxOffset +f AST_AXOFFSET = DOUBLE PRECISION +* The incremented axis value. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input values has this value. +* - A "bad" value will also be returned if this function is +c invoked with the AST error status set, or if it should fail for +f invoked with STATUS set to an error value, or if it should fail for +* any reason. +*-- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + double result; /* The returned answer */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index and obtain a pointer to the required Axis. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxOffset" ); + ax = astGetAxis( this, axis - 1 ); + +/* Use the AxisOffset method associated with the Axis. */ + if( astOK ) result = astAxisOffset( ax, v1, dist ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* Return the result. */ + return result; + +} + +static double Centre( AstFrame *this, int axis, double value, double gap, int *status ) { +/* +*+ +* Name: +* astCentre + +* Purpose: +* Find a "nice" central value for tabulating Axis values. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* double astCentre( AstFrame *this, int axis, double value, double gap ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns an axis value which produces a nice formatted +* value suitable for a major tick mark on a plot axis, close to the +* supplied axis value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* value +* An arbitrary axis value in the section that is being plotted. +* gap +* The gap size. + +* Returned Value: +* The nice central axis value. + +* Notes: +* - A value of zero is returned if the supplied gap size is zero. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + double result; /* The nice central value */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Validate the axis index and obtain a pointer to the required + Axis. */ + (void) astValidateAxis( this, axis, 1, "astCentre" ); + ax = astGetAxis( this, axis ); + +/* Find the nice central value. */ + result = astAxisCentre( ax, value, gap ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static void CheckPerm( AstFrame *this, const int *perm, const char *method, int *status ) { +/* +*+ +* Name: +* astCheckPerm + +* Purpose: +* Check that an array contains a valid permutation. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astCheckPerm( AstFrame *this, const int *perm, const char *method ) + +* Class Membership: +* Frame method. + +* Description: +* This function checks the validity of a permutation array that +* will be used to permute the order of a Frame's axes. If the +* permutation specified by the array is not valid, an error is +* reported and the global error status is set. Otherwise, the +* function returns without further action. + +* Parameters: +* this +* Pointer to the Frame. +* perm +* Pointer to an array of integers with the same number of +* elements as there are axes in the Frame. For each axis, the +* corresponding integer gives the (zero based) axis index to be +* used to identify the information for that axis (using the +* un-permuted axis numbering). To be valid, the integers in +* this array should therefore all lie in the range zero to +* (naxes-1) inclusive, where "naxes" is the number of Frame +* axes, and each value should occur exactly once. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate a permutation array. This method name is used +* solely for constructing error messages. + +* Notes: +* - Error messages issued by this function refer to the external +* (public) numbering system used for axes (which is one-based), +* whereas zero-based axis indices are used internally. +*- +*/ + +/* Local Variables: */ + int *there; /* Pointer to temporary array */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of Frame axes */ + int valid; /* Permutation array is valid? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise. */ + valid = 1; + +/* Obtain the number of Frame axes and allocate a temporary array of integers + with the same number of elements. */ + naxes = astGetNaxes( this ); + there = astMalloc( sizeof( int ) * (size_t) naxes ); + if ( astOK ) { + +/* Initialise the temporary array to zero. */ + for ( axis = 0; axis < naxes; axis++ ) there[ axis ] = 0; + +/* Scan the permutation array, checking that each permuted axis index it + contains is within the correct range. Note an error and quit checking + if an invalid value is found. */ + for ( axis = 0; axis < naxes; axis++ ) { + if ( ( perm[ axis ] < 0 ) || ( perm[ axis ] >= naxes ) ) { + valid = 0; + break; + +/* Use the temporary array to count how many times each valid axis index + occurs. */ + } else { + there[ perm[ axis ] ]++; + } + } + +/* If all the axis indices were within range, check to ensure that each value + occurred only once. */ + if ( valid ) { + for ( axis = 0; axis < naxes; axis++ ) { + +/* Note an error and quit checking if any value did not occur exactly once. */ + if ( there[ axis ] != 1 ) { + valid = 0; + break; + } + } + } + } + +/* Free the temporary array. */ + there = astFree( there ); + +/* If an invalid permutation was detected and no other error has + occurred, then report an error (note we convert to one-based axis + numbering in the error message). */ + if ( !valid && astOK ) { + astError( AST__PRMIN, "%s(%s): Invalid axis permutation array.", status, + method, astGetClass( this ) ); + astError( AST__PRMIN, "Each axis index should lie in the range 1 to %d " + "and should occur only once.", status, naxes ); + } +} + +static void CleanAttribs( AstObject *this_object, int *status ) { +/* +* Name: +* CleanAttribs + +* Purpose: +* Clear any invalid set attribute values. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* void CleanAttribs( AstObject *this_object, int *status ) + +* Class Membership: +* Frame member function (over-rides the protected astCleanAttribs +* method inherited from the Object class). + +* Description: +* This function clears any attributes that are currently set to +* invalid values in thr supplied object. + +* Parameters: +* this +* Pointer to the Object to be cleaned. + +*/ + +/* Local Variables; */ + AstAxis *ax; + AstFrame *this; + int i; + int nax; + int reporting; + +/* Check inherited status */ + if( !astOK ) return; + +/* Get a pointer to the Frame structure. */ + this = (AstFrame *) this_object; + +/* Defer error reporting, as required by the astCLEAN_ATTRIB macro. */ + reporting = astReporting( 0 ); + +/* Clean attributes in any Objects contained within "this". */ + nax = astGetNaxes( this ); + for( i = 0; i < nax; i++ ) { + ax = astGetAxis( this, i ); + astCleanAttribs( ax ); + ax = astAnnul( ax ); + } + +/* Clean attributes of this class. */ + astCLEAN_ATTRIB(System) + astCLEAN_ATTRIB(AlignSystem) + +/* Re-establish error reporting. */ + astReporting( reporting ); + +/* Invoke the method inherited form the parent to clean attributes + defined by the parent class. */ + (*parent_cleanattribs)( this_object, status ); +} + +static char *CleanDomain( char *domain, int *status ) { +/* +* Name: +* CleanDomain + +* Purpose: +* Clean a Domain string and convert to upper case. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* char *CleanDomain( char *domain, int *status ) + +* Class Membership: +* Frame member function. + +* Description: +* This function removes all white space from a string and converts +* other characters to upper case. It is intended for cleaning up +* values supplied for the Domain attribute of a Frame. + +* Parameters: +* domain +* Pointer to the null terminated Domain string to be modified. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The pointer value "domain" is always returned (even under error +* conditions). +*/ + +/* Local Variables: */ + int i; /* Loop counter for characters */ + int j; /* Good character count */ + +/* Check the global error status. */ + if ( !astOK ) return domain; + +/* Eliminate white space characters and convert the rest to upper + case. */ + for ( i = j = 0; domain[ i ]; i++ ) { + if ( !isspace( domain[ i ] ) ) domain[ j++ ] = toupper( domain[ i ] ); + } + domain[ j ] = '\0'; + +/* Return the string pointer. */ + return domain; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Frame member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* Frame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Frame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + AstFrame *this; /* Pointer to the Frame structure */ + char pfrm_attrib[ 100 ]; /* Primary Frame attribute */ + char *axis_attrib; /* Pointer to axis attribute name */ + const char *old_attrib; /* Pointer to supplied attribute name string */ + int axis; /* Frame axis number */ + int axis_nc; /* No. characters in axis attribute name */ + int free_axis_attrib; /* Should axis_attrib be freed? */ + int has_axis; /* Does attrib name include axis specifier? */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + int oldrep; /* Original error reporting state */ + int paxis; /* Axis index within primary frame */ + int used; /* Could the setting string be used? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_object; + +/* Set a flag indicating if the attribute name includes an axis + specifier. */ + has_axis = ( strchr( attrib, '(' ) != NULL ); + +/* A flag indicating that we do not need to free the axis_attrib memory. */ + free_axis_attrib = 0; + +/* Initialise things to avoid compiler warnings. */ + axis_attrib = NULL; + old_attrib = NULL; + +/* Jump back to here if we are trying the same attribute but with an explicit + axis "(1)" added to the end of the name. */ +L1: + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Digits. */ +/* ------- */ + if ( !strcmp( attrib, "digits" ) ) { + astClearDigits( this ); + +/* Digits(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "digits(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + +/* There is no function to clear the Digits attribute for an axis + directly, so obtain a pointer to the Axis and use this to clear the + attribute. */ + (void) astValidateAxis( this, axis - 1, 1, "astClearDigits(axis)" ); + ax = astGetAxis( this, axis - 1 ); + astClearAxisDigits( ax ); + ax = astAnnul( ax ); + +/* Direction(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "direction(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearDirection( this, axis - 1 ); + +/* Epoch. */ +/* ------ */ + } else if ( !strcmp( attrib, "epoch" ) ) { + astClearEpoch( this ); + +/* Top(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "top(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearTop( this, axis - 1 ); + +/* Bottom(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "bottom(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearBottom( this, axis - 1 ); + +/* Domain. */ +/* ------- */ + } else if ( !strcmp( attrib, "domain" ) ) { + astClearDomain( this ); + +/* Format(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "format(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearFormat( this, axis - 1 ); + +/* Label(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "label(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLabel( this, axis - 1 ); + +/* MatchEnd. */ +/* --------- */ + } else if ( !strcmp( attrib, "matchend" ) ) { + astClearMatchEnd( this ); + +/* MaxAxes. */ +/* -------- */ + } else if ( !strcmp( attrib, "maxaxes" ) ) { + astClearMaxAxes( this ); + +/* MinAxes. */ +/* -------- */ + } else if ( !strcmp( attrib, "minaxes" ) ) { + astClearMinAxes( this ); + +/* Permute. */ +/* -------- */ + } else if ( !strcmp( attrib, "permute" ) ) { + astClearPermute( this ); + +/* PreserveAxes. */ +/* ------------- */ + } else if ( !strcmp( attrib, "preserveaxes" ) ) { + astClearPreserveAxes( this ); + +/* Symbol(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "symbol(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearSymbol( this, axis - 1 ); + +/* System. */ +/* ------- */ + } else if ( !strcmp( attrib, "system" ) ) { + astClearSystem( this ); + +/* AlignSystem. */ +/* ------------ */ + } else if ( !strcmp( attrib, "alignsystem" ) ) { + astClearAlignSystem( this ); + +/* Title. */ +/* ------ */ + } else if ( !strcmp( attrib, "title" ) ) { + astClearTitle( this ); + +/* Unit(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "unit(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearUnit( this, axis - 1 ); + +/* ObsLat. */ +/* ------- */ + } else if ( !strcmp( attrib, "obslat" ) ) { + astClearObsLat( this ); + +/* ObsLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "obslon" ) ) { + astClearObsLon( this ); + +/* ObsAlt. */ +/* ------- */ + } else if ( !strcmp( attrib, "obsalt" ) ) { + astClearObsAlt( this ); + +/* Dut1 */ +/* --- */ + } else if ( !strcmp( attrib, "dut1" ) ) { + astClearDut1( this ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute name matches any of the read-only attributes + of this class. If it does, then report an error. */ + } else if ( !strcmp( attrib, "naxes" ) || + !strncmp( attrib, "normunit", 8 ) || + !strncmp( attrib, "internalunit", 12 ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Other axis attributes. */ +/* ---------------------- */ +/* If the attribute was not identified above, but appears to refer to + a Frame axis, then it may refer to an Axis object of a derived type + (which has additional attributes not recognised here). */ + } else if( !free_axis_attrib && ( nc = 0, + ( 1 == astSscanf( attrib, "%*[^()]%n(%d)%n", + &axis_nc, &axis, &nc ) ) + && ( nc >= len ) ) ) { + +/* Validate the axis index and extract the attribute name. */ + (void) astValidateAxis( this, axis - 1, 1, "astClear" ); + axis_attrib = astString( attrib, axis_nc ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis - 1 ); + if( astOK ) { + +/* Assume that we will be able to use the attribute name. */ + used = 1; + +/* Temporarily switch off error reporting so that if the following attempt + to access the axis attribute fails, we can try to interpret the + attribute name as an attribute of the primary Frame containing the + specified axis. Any errors reported in this context will simply be + ignored, in particularly they are not deferred for later delivery. */ + oldrep = astReporting( 0 ); + +/* Use the Axis astClearAttrib method to clear the attribute value. */ + astClearAttrib( ax, axis_attrib ); + +/* If the above call failed with a status of AST__BADAT, indicating that + the attribute name was not recognised, clear the status so that we can + try to interpret the attribute name as an attribute of the primary Frame + containing the specified axis. */ + if( astStatus == AST__BADAT ) { + astClearStatus; + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + +/* Only attempt to use the primary Frame if it is not the same as "this" + - otherwise we could end up in an infinite loop. */ + if( pfrm != this ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astClear" ); + +/* Modify the attribute name to refer to the axis numbering of the + primary frame. */ + sprintf( pfrm_attrib, "%s(%d)", axis_attrib, paxis + 1 ); + +/* Attempt to clear the attribute as an attribute of the primary Frame. */ + astClearAttrib( pfrm, pfrm_attrib ); + +/* If this failed, clear the status and indicate that we have not managed to + use the attribute name. */ + if( !astOK ) { + astClearStatus; + used = 0; + } + + } else { + used = 0; + } + +/* If not found attempt to clear the attribute value in the Axis, omitting + the axis index. */ + if( ! used ) { + astClearAttrib( pfrm, axis_attrib ); + if( !astOK ) { + astClearStatus; + } else { + used = 1; + } + } + +/* Annul the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + +/* If we could not use the attribute name, attempt to clear the axis + attribute again, this time retaining the error report. This is done + to ensure the user gets an appropriate error message. */ + if( !used ) astClearAttrib( ax, axis_attrib ); + } + +/* Annul the Axis pointer and free the memory holding the attribute + name. */ + ax = astAnnul( ax ); + axis_attrib = astFree( axis_attrib ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, and the Frame has only 1 axis, + and the attribute name does not already include an axis specifier, try + again after appending "(1)" to the end of the attribute name. */ + } else if( !has_axis && astGetNaxes( this ) == 1 ) { + +/* Take a copy of the supplied name, allowing 3 extra characters for the + axis specifier "(1)". */ + axis_attrib = astMalloc( len + 4 ); + if( axis_attrib ) memcpy( axis_attrib, attrib, len ); + +/* Indicate we should free the axis_attrib memory. */ + free_axis_attrib = 1; + +/* Add in the axis specifier. */ + strcpy( axis_attrib + len, "(1)" ); + +/* Use the new attribute name instead of the supplied name. */ + old_attrib = attrib; + attrib = axis_attrib; + +/* Indicate the attribute name now has an axis specifier. */ + has_axis = 1; + +/* Jump back to try interpreting the new attribute name. */ + goto L1; + +/* Not recognised. */ +/* --------------- */ +/* If the attribute name is still not recognised, pass it on to the parent + method for further interpretation. First re-instate the original attrib + name string if it was changed above. */ + } else { + if( free_axis_attrib ) { + attrib = old_attrib; + axis_attrib = astFree( axis_attrib ); + } + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearUnit( AstFrame *this, int axis, int *status ) { +/* +* Name: +* ClearUnit + +* Purpose: +* Clear the Unit attribute of a Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void ClearUnit( AstFrame *this, int axis, int *status ) + +* Class Membership: +* Frame method. + +* Description: +* This function clears the Unit value for an axis of a Frame. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which the Unit value is to +* be cleared. +* unit +* The new value to be set. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + const char *units; /* Pointer to units string */ + char *old_units; /* Pointer to copy of original units */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astSetUnit" ); + +/* Do nothing more if the attribute is already cleared. */ + if( astTestUnit( this, axis ) ) { + +/* Obtain a pointer to the required Axis. */ + ax = astGetAxis( this, axis ); + +/* Save a copy of the old units. */ + units = astGetAxisUnit( ax ); + old_units = astStore( NULL, units, strlen( units ) + 1 ); + +/* Clear the Axis Unit attribute value, and then get a pointer to the + new default Units string. */ + astClearAxisUnit( ax ); + units = astGetUnit( this, axis ); + +/* The new unit may require the Label and/or Symbol to be changed, but + only if the Frames ActiveUnit flag is set. */ + if( astGetActiveUnit( this ) ) NewUnit( ax, old_units, units, + "astSetUnit", astGetClass( this ), status ); + +/* Free resources. */ + old_units = astFree( old_units ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + } +} + +static int ConsistentMaxAxes( AstFrame *this, int value, int *status ) { +/* +* Name: +* ConsistentMaxAxes + +* Purpose: +* Ensure a consistent value when setting the MaxAxes attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int ConsistentMaxAxes( AstFrame *this, int value, int *status ) + +* Class Membership: +* Frame member function. + +* Description: +* This function accepts a value which is to be set for a Frame's MaxAxes +* attribute and returns an appropriately adjusted value to be assigned +* to the Frame structure's max_axes component. If necessary, the Frame's +* MinAxes attribute is adjusted to remain consistent with the new MaxAxes +* value (but note that the MaxAxes value itself is not assigned by this +* function). + +* Parameters: +* this +* Pointer to the Frame. +* value +* The new value being set for the MaxAxes attribute. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to be assigned to the max_axes component. + +* Notes: +* - A value of -INT_MAX will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return -INT_MAX; + +/* Ensure that the result value isn't negative. */ + result = ( value >= 0 ) ? value : 0; + +/* Check if the MinAxes attribute is set. If not, its default value will be + consistent with the MaxAxes value (the DefaultMinAxes function ensures + this). Otherwise, obtain its value to check for consistency. */ + if ( astTestMinAxes( this ) ) { + +/* If necessary, set a new MinAxes value to prevent it exceeding the MaxAxes + value about to be returned. */ + if ( astGetMinAxes( this ) > result ) astSetMinAxes( this, result ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -INT_MAX; + +/* Return the result. */ + return result; +} + +static int ConsistentMinAxes( AstFrame *this, int value, int *status ) { +/* +* Name: +* ConsistentMinAxes + +* Purpose: +* Ensure a consistent value when setting the MinAxes attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int ConsistentMinAxes( AstFrame *this, int value, int *status ) + +* Class Membership: +* Frame member function. + +* Description: +* This function accepts a value which is to be set for a Frame's MinAxes +* attribute and returns an appropriately adjusted value to be assigned +* to the Frame structure's min_axes component. If necessary, the Frame's +* MaxAxes attribute is adjusted to remain consistent with the new MinAxes +* value (but note that the MinAxes value itself is not assigned by this +* function). + +* Parameters: +* this +* Pointer to the Frame. +* value +* The new value being set for the MinAxes attribute. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to be assigned to the min_axes component. + +* Notes: +* - A value of -INT_MAX will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return -INT_MAX; + +/* Ensure that the result value isn't negative. */ + result = ( value >= 0 ) ? value : 0; + +/* Check if the MaxAxes attribute is set. If not, its default value will be + consistent with the MinAxes value (the DefaultMaxAxes function ensures + this). Otherwise, obtain its value to check for consistency. */ + if ( astTestMaxAxes( this ) ) { + +/* If necessary, set a new MaxAxes value to prevent it being less than the + MinAxes value about to be returned. */ + if ( astGetMaxAxes( this ) < result ) astSetMaxAxes( this, result ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -INT_MAX; + +/* Return the result. */ + return result; +} + +static AstFrameSet *Convert( AstFrame *from, AstFrame *to, + const char *domainlist, int *status ) { +/* +*++ +* Name: +c astConvert +f AST_CONVERT + +* Purpose: +* Determine how to convert between two coordinate systems. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c AstFrameSet *astConvert( AstFrame *from, AstFrame *to, +c const char *domainlist ) +f RESULT = AST_CONVERT( FROM, TO, DOMAINLIST, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +* This function compares two Frames and determines whether it is +* possible to convert between the coordinate systems which they +* represent. If conversion is possible, it returns a FrameSet +* which describes the conversion and which may be used (as a +* Mapping) to transform coordinate values in either direction. +* +* The same function may also be used to determine how to convert +* between two FrameSets (or between a Frame and a FrameSet, or +* vice versa). This mode is intended for use when (for example) +* two images have been calibrated by attaching a FrameSet to each. +c astConvert might then be used to search for a +f AST_CONVERT might then be used to search for a +* celestial coordinate system that both images have in common, and +* the result could then be used to convert between the pixel +* coordinates of both images -- having effectively used their +* celestial coordinate systems to align them. +* +* When using FrameSets, there may be more than one possible +* intermediate coordinate system in which to perform the +* conversion (for instance, two FrameSets might both have +* celestial coordinates, detector coordinates, pixel coordinates, +* etc.). A comma-separated list of coordinate system domains may +* therefore be given which defines a priority order to use when +* selecting the intermediate coordinate system. The path used for +* conversion must go via an intermediate coordinate system whose +* Domain attribute matches one of the domains given. If conversion +* cannot be achieved using the first domain, the next one is +* considered, and so on, until success is achieved. + +* Parameters: +c from +f FROM = INTEGER (Given) +* Pointer to a Frame which represents the "source" coordinate +* system. This is the coordinate system in which you already +* have coordinates available. +* +* If a FrameSet is given, its current Frame (as determined by +* its Current attribute) is taken to describe the source +* coordinate system. Note that the Base attribute of this +* FrameSet may be modified by this function to indicate which +* intermediate coordinate system was used (see under +* "FrameSets" in the "Applicability" section for details). +c to +f TO = INTEGER (Given) +* Pointer to a Frame which represents the "destination" +* coordinate system. This is the coordinate system into which +* you wish to convert your coordinates. +* +* If a FrameSet is given, its current Frame (as determined by +* its Current attribute) is taken to describe the destination +* coordinate system. Note that the Base attribute of this +* FrameSet may be modified by this function to indicate which +* intermediate coordinate system was used (see under +* "FrameSets" in the "Applicability" section for details). +c domainlist +f DOMAINLIST = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing a +f A character string containing a +* comma-separated list of Frame domains. This may be used to +* define a priority order for the different intermediate +* coordinate systems that might be used to perform the +* conversion. +* +* The function will first try to obtain a conversion by making +* use only of an intermediate coordinate system whose Domain +* attribute matches the first domain in this list. If this +* fails, the second domain in the list will be used, and so on, +* until conversion is achieved. A blank domain (e.g. two +* consecutive commas) indicates that all coordinate systems +* should be considered, regardless of their domains. +* +* This list is case-insensitive and all white space is ignored. +* If you do not wish to restrict the domain in this way, +c you should supply an empty string. This is normally +f you should supply a blank string. This is normally +* appropriate if either of the source or destination coordinate +* systems are described by Frames (rather than FrameSets), +* since there is then usually only one possible choice of +* intermediate coordinate system. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astConvert() +f AST_CONVERT = INTEGER +* If the requested coordinate conversion is possible, the +* function returns a pointer to a FrameSet which describes the +* conversion. Otherwise, a null Object pointer (AST__NULL) is +* returned without error. +* +* If a FrameSet is returned, it will contain two Frames. Frame +* number 1 (its base Frame) will describe the source coordinate +c system, corresponding to the "from" parameter. Frame number 2 +f system, corresponding to the FROM argument. Frame number 2 +* (its current Frame) will describe the destination coordinate +c system, corresponding to the "to" parameter. The Mapping +f system, corresponding to the TO argument. The Mapping +* which inter-relates these two Frames will perform the +* required conversion between their respective coordinate +* systems. +* +* Note that a FrameSet may be used both as a Mapping and as a +* Frame. If the result is used as a Mapping (e.g. with +c astTran2), then it provides a means of converting coordinates +f AST_TRAN2), then it provides a means of converting coordinates +* from the source to the destination coordinate system (or +* vice versa if its inverse transformation is selected). If it +* is used as a Frame, its attributes will describe the +* destination coordinate system. + +* Applicability: +* DSBSpecFrame +* If the AlignSideBand attribute is non-zero, alignment occurs in the +* upper sideband expressed within the spectral system and standard of +* rest given by attributes AlignSystem and AlignStdOfRest. If +* AlignSideBand is zero, the two DSBSpecFrames are aligned as if +* they were simple SpecFrames (i.e. the SideBand is ignored). +* Frame +* This function applies to all Frames. Alignment occurs within the +* coordinate system given by attribute AlignSystem. +* FrameSet +c If either of the "from" or "to" parameters is a pointer to a +f If either of the FROM or TO arguments is a pointer to a +c FrameSet, then astConvert will attempt to convert from the +f FrameSet, then AST_CONVERT will attempt to convert from the +c coordinate system described by the current Frame of the "from" +f coordinate system described by the current Frame of the FROM +c FrameSet to that described by the current Frame of the "to" +f FrameSet to that described by the current Frame of the TO +* FrameSet. +* +* To achieve this, it will consider all of the Frames within +* each FrameSet as a possible way of reaching an intermediate +* coordinate system that can be used for the conversion. There +* is then the possibility that more than one conversion path +* may exist and, unless the choice is sufficiently restricted +c by the "domainlist" string, the sequence in which the Frames +f by the DOMAINLIST string, the sequence in which the Frames +* are considered can be important. In this case, the search +* for a conversion path proceeds as follows: +c - Each field in the "domainlist" string is considered in turn. +f - Each field in the DOMAINLIST string is considered in turn. +* - The Frames within each FrameSet are considered in a +* specific order: (1) the base Frame is always considered +* first, (2) after this come all the other Frames in +* Frame-index order (but omitting the base and current Frames), +* (3) the current Frame is always considered last. However, if +* either FrameSet's Invert attribute is set to a non-zero value +* (so that the FrameSet is inverted), then its Frames are +* considered in reverse order. (Note that this still means that +* the base Frame is considered first and the current Frame +* last, because the Invert value will also cause these Frames +* to swap places.) +* - All source Frames are first considered (in the appropriate +* order) for conversion to the first destination Frame. If no +* suitable intermediate coordinate system emerges, they are +* then considered again for conversion to the second +* destination Frame (in the appropriate order), and so on. +* - Generally, the first suitable intermediate coordinate +* system found is used. However, the overall Mapping between +* the source and destination coordinate systems is also +* examined. Preference is given to cases where both the +* forward and inverse transformations are defined (as indicated +* by the TranForward and TranInverse attributes). If only one +* transformation is defined, the forward one is preferred. +* - If the domain of the intermediate coordinate system matches +c the current "domainlist" field, the conversion path is +f the current DOMAINLIST field, the conversion path is +c accepted. Otherwise, the next "domainlist" field is considered +f accepted. Otherwise, the next DOMAINLIST field is considered +* and the process repeated. +* +* If conversion is possible, the Base attributes of the two +* FrameSets will be modified on exit to identify the Frames +* used to access the intermediate coordinate system which was +* finally accepted. +* +* Note that it is possible to force a particular Frame within a +* FrameSet to be used as the basis for the intermediate +* coordinate system, if it is suitable, by (a) focussing +* attention on +c it by specifying its domain in the "domainlist" string, or (b) +f it by specifying its domain in the DOMAINLIST string, or (b) +* making it the base Frame, since this is always considered +* first. +* SpecFrame +* Alignment occurs within the spectral system and standard of rest +* given by attributes AlignSystem and AlignStdOfRest. +* TimeFrame +* Alignment occurs within the time system and time scale given by +* attributes AlignSystem and AlignTimeScale. + +* Examples: +c cvt = astConvert( a, b, "" ); +f CVT = AST_CONVERT( A, B, ' ', STATUS ) +* Attempts to convert between the coordinate systems represented +c by "a" and "b" (assumed to be Frames). If successful, a FrameSet +f by A and B (assumed to be Frames). If successful, a FrameSet +c is returned via the "cvt" pointer which may be used to apply the +f is returned via the CVT pointer which may be used to apply the +c conversion to sets of coordinates (e.g. using astTran2). +f conversion to sets of coordinates (e.g. using AST_TRAN2). +c cvt = astConvert( astSkyFrame(""), astSkyFrame("Equinox=2005"), "" ); +f CVT = AST_CONVERT( AST_SKYFRAME( ' ', STATUS ), AST_SKYFRAME( 'Equinox=2005', STATUS ), ' ', STATUS ) +* Creates a FrameSet which describes precession in the default +* FK5 celestial coordinate system between equinoxes J2000 (also +c the default) and J2005. The returned "cvt" pointer may then +f the default) and J2005. The returned CVT pointer may then +c be passed to astTran2 to apply this precession correction to +f be passed to AST_TRAN2 to apply this precession correction to +* any number of coordinate values given in radians. +* +* Note that the returned FrameSet also contains information +* about how to format coordinate values. This means that +* setting its Report attribute to 1 is a simple way to obtain +* printed output (formatted in sexagesimal notation) to show +* the coordinate values before and after conversion. +c cvt = astConvert( a, b, "sky,detector," ); +f CVT = AST_CONVERT( A, B, 'SKY,DETECTOR,', STATUS ) +* Attempts to convert between the coordinate systems +c represented by the current Frames of "a" and "b" +f represented by the current Frames of A and B +* (now assumed to be FrameSets), via the intermediate "SKY" +* coordinate system. This, by default, is the Domain +* associated with a celestial coordinate system represented by +* a SkyFrame. +* +* If this fails (for example, because either FrameSet lacks +* celestial coordinate information), then the user-defined +* "DETECTOR" coordinate system is used instead. If this also +* fails, then all other possible ways of achieving conversion +* are considered before giving up. +* +c The returned pointer "cvt" indicates whether conversion was +f The returned pointer CVT indicates whether conversion was +* possible and will have the value AST__NULL if it was not. If +c conversion was possible, "cvt" will point at a new FrameSet +f conversion was possible, CVT will point at a new FrameSet +* describing the conversion. +* +* The Base attributes of the two FrameSets +c will be set by astConvert to indicate which of their Frames was +f will be set by AST_CONVERT to indicate which of their Frames was +* used for the intermediate coordinate system. This means that +* you can subsequently determine which coordinate system was +* used by enquiring the Domain attribute of either base Frame. + +* Notes: +* - The Mapping represented by the returned FrameSet results in +* alignment taking place in the coordinate system specified by the +c AlignSystem attribute of the "to" Frame. See the description of the +f AlignSystem attribute of the TO Frame. See the description of the +* AlignSystem attribute for further details. +* - When aligning (say) two images, which have been calibrated by +* attaching FrameSets to them, it is usually necessary to convert +* between the base Frames (representing "native" pixel +* coordinates) of both FrameSets. This may be achieved by +* inverting the FrameSets (e.g. using astInvert) so as to +* interchange their base and current Frames before using +* astConvert. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* This function is simply a wrap-up for the protected astConvertX +* method which performs the required processing but swaps the order +* of the first two arguments. This is a trick to allow the +* astConvert method to be over-ridden by derived classes on the +* basis of the class of either of the first two arguments. +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Invoke the "astConvertX" method with the first two arguments + swapped. */ + return astConvertX( to, from, domainlist ); +} + +static AstFrameSet *ConvertX( AstFrame *to, AstFrame *from, + const char *domainlist, int *status ) { +/* +*+ +* Name: +* astConvertX + +* Purpose: +* Determine how to convert between two coordinate systems. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstFrameSet *astConvertX( AstFrame *to, AstFrame *from, +* const char *domainlist ) + +* Class Membership: +* Frame method. + +* Description: +* This function performs the processing for the public astConvert +* method and has exactly the same interface except that the order +* of the first two arguments is swapped. This is a trick to allow +* the astConvert method to be over-ridden by derived classes on +* the basis of the class of either of its first two arguments. +* +* See the astConvert method for details of the interface. +*- + +* Implementation Deficiencies: +* - This function's job is basically to negotiate with two Frames +* to try and find a mutually agreeable coordinate system for +* conversion. Ideally, it should be able to juggle the number of +* axes, etc. to do this. At present, however, the implementation +* is much simpler. This is adequate for the Frame classes which +* exist at the time of writing, but the implementation may need +* beefing up in future. +* - One likely problem is with attributes which default in both +* the source and destination Frames. This means they also default +* in the common coordinate system. If these default values were to +* differ when matching different target Frames, however, we would +* be in trouble, because the common coordinate system would not +* then be remaining constant. The longer-term solution to this is +* probably to provide some mechanism to "fix" all attribute values +* for a Frame, by taking any attributes that are un-set and +* explicitly setting a firm value (equal to the default) so they +* cannot then change. +*/ + +/* Local Variables: */ + AstFrameSet *result; /* Pointer to Mapping to be returned */ + AstFrame *ftmp; /* Pointer to returned Frame */ + AstMapping **map1_address; /* Location of first Mapping pointer */ + AstMapping **map2_address; /* Location of second Mapping pointer */ + AstMapping *common0; /* Initial common coordinate system */ + AstMapping *common1; /* Improved common coordinate system */ + AstMapping *common2; /* Final common coordinate system */ + AstMapping *frame1; /* Pointer to Frame for first match */ + AstMapping *frame2; /* Pointer to Frame for second match */ + AstMapping *from_map; /* Pointer to "from" Mapping */ + AstMapping *map; /* Pointer to conversion Mapping */ + AstMapping *result_map; /* Pointer to result Mapping */ + AstMapping *tmp; /* Temporary Mapping pointer */ + AstMapping *to_map; /* Pointer to "to" Mapping */ + char *domain; /* Pointer to result domain */ + char *domain_end; /* Pointer to null at end of domain */ + char *domainlist_copy; /* Pointer to copy of domainlist */ + int *axes1; /* Pointer to axis assignments */ + int *axes2; /* Pointer to axis assignments */ + int best_score; /* Score assigned to best match */ + int icom; /* Common coordinate system loop counter */ + int match1; /* First match succeeded? */ + int match2; /* Second match succeeded? */ + int match; /* Overall match found? */ + int perfect; /* Perfect match found? */ + int score; /* Score assigned to match */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + result_map = NULL; + +/* Make a temporary copy of the domains list. */ + domainlist_copy = astStore( NULL, domainlist, + strlen( domainlist ) + (size_t) 1 ); + if ( astOK ) { + +/* Loop to inspect each comma-separated field in the domains list + until an error occurs, all the domains are used up, or a match is + found. */ + domain = domainlist_copy; + match = 0; + while ( astOK && domain && !match ) { + +/* Change the comma at the end of each field to a null to terminate + the domain. Then convert the domain to upper case and eliminate + white space. */ + if ( ( domain_end = strchr( domain, ',' ) ) ) *domain_end = '\0'; + CleanDomain( domain, status ); + +/* For any given domain, we will ignore imperfect matches in favour of + better ones by assigning a score to each match. Initialise the best + score value for the current domain. */ + best_score = -1; + +/* Loop to consider both the "to" and "from" Frames in turn as the + basis of a possible common coordinate system. Quit looping early if + an error occurs or a perfect match is found. */ + perfect = 0; + for ( icom = 0; astOK && !perfect && ( icom <= 1 ); icom++ ) { + +/* Make a copy of the Frame representing the initial guess at a common + coordinate system. We will use this to probe the other + Frame. Ensure that axes are not preserved (so that we convert to + the common axis number/order). */ + common0 = astCopy( icom ? from : to ); + astSetPreserveAxes( common0, 0 ); + +/* Also, if the current domain is not blank, set the Domain attribute (so + we will only find coordinate systems which match the current + "domainlist" field). */ + if ( *domain ) astSetDomain( common0, domain ); + +/* Obtain a pointer to the other Frame. */ + frame1 = astClone( icom ? to : from ); + +/* Set the address at which to store the resulting Mapping pointer. */ + map1_address = icom ? &to_map : &from_map; + +/* See if conversion from "frame1" to the common coordinate system is + possible. If successful, this results in a new approximation + ("common1") to the possible common coordinate system. */ + match1 = astMatch( common0, frame1, 1, &axes1, &axes2, + map1_address, &ftmp ); + common1 = (AstMapping *) ftmp; + +/* If successful, free memory allocated for the axis association + arrays, which are not needed. */ + if ( astOK && match1 ) { + axes1 = astFree( axes1 ); + axes2 = astFree( axes2 ); + +/* Using the improved approximation to the common coordinate system, + now test if conversion from the alternative Frame "frame2" is + possible. */ + frame2 = astClone( icom ? from : to ); + map2_address = icom ? &from_map : &to_map; + astSetPreserveAxes( common1, 0 ); + match2 = astMatch( common1, frame2, 1, &axes1, &axes2, + map2_address, &ftmp ); + common2 = (AstMapping *) ftmp; + +/* If successful, free memory allocated for the axis association + arrays, which are not needed. */ + if ( astOK && match2 ) { + axes1 = astFree( axes1 ); + axes2 = astFree( axes2 ); + +/* Invert the "to" Mapping and concatenate the two Mappings to + describe the conversion between the "from" and "to" Frames. Then + simplify the result. */ + astInvert( to_map ); + tmp = (AstMapping *) astCmpMap( from_map, to_map, 1, "", status ); + map = astSimplify( tmp ); + tmp = astAnnul( tmp ); + +/* Assign a score that favours Mappings with both transformations + available over those with only one, and Mappings with only a + forward transformation over those with only an inverse + transformation. */ + score = ( astGetTranForward( map ) ? 2 : 0 ) + + ( astGetTranInverse( map ) ? 1 : 0 ); + +/* If the new score is better than the previous one (or is the first + one), note that we have a possible match. */ + if ( astOK && ( score > best_score ) ) { + match = 1; + +/* Update the best score and note if it indicates a perfect match (in + which case we can stop searching at this point). */ + best_score = score; + perfect = ( best_score >= 3 ); + +/* Annul any previous result Mapping pointer and replace it with this + better one. */ + if ( result_map ) result_map = astAnnul( result_map ); + result_map = astClone( map ); + } + +/* Annul pointers to all the intermediate Objects. */ + map = astAnnul( map ); + common2 = astAnnul( common2 ); + *map2_address = astAnnul( *map2_address ); + } + frame2 = astAnnul( frame2 ); + common1 = astAnnul( common1 ); + *map1_address = astAnnul( *map1_address ); + } + frame1 = astAnnul( frame1 ); + common0 = astAnnul( common0 ); + } + +/* Go on to consider the next field in the domains list. */ + domain = domain_end ? domain_end + 1 : NULL; + } + } + +/* Free the domain list copy. */ + domainlist_copy = astFree( domainlist_copy ); + +/* If returning a result, build the result FrameSet. Then annul the + result Mapping pointer. */ + if ( result_map ) { + result = astFrameSet( from, "", status ); + astAddFrame( result, AST__BASE, result_map, to ); + result_map = astAnnul( result_map ); + } + +/* If an error occurred, annul the result FrameSet pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int DefaultMaxAxes( AstFrame *this, int *status ) { +/* +* Name: +* DefaultMaxAxes + +* Purpose: +* Obtain the MaxAxes attribute from a Frame structure, with defaulting. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int DefaultMaxAxes( AstFrame *this, int *status ) + +* Class Membership: +* Frame member function. + +* Description: +* This function inspects the max_axes component of a Frame structure and +* derives a value for the MaxAxes attribute. If the component's value +* indicates that the attribute has not been set, a suitable default is +* returned which is consistent with the state of the Frames's MinAxes +* attribute. + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to be used for the MaxAxes attribute. +*/ + +/* Local Variables. */ + int result; /* Result to be returned */ + int min_axes; /* Value of MinAxes attribute */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If the Frame's max_axes component is set, return its value. */ + if ( this->max_axes != -INT_MAX ) { + result = this->max_axes; + +/* Otherwise, the preferred default value is the number of Frame axes. */ + } else { + result = astGetNaxes( this ); + +/* Before returning this value, check if the MinAxes attribute is set. If it + is, obtain its value. */ + if ( astTestMinAxes( this ) ) { + min_axes = astGetMinAxes( this ); + +/* If necessary, increase the MaxAxes default value so that it is not less than + MinAxes. */ + if ( result < min_axes ) result = min_axes; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int DefaultMinAxes( AstFrame *this, int *status ) { +/* +* Name: +* DefaultMinAxes + +* Purpose: +* Obtain the MinAxes attribute from a Frame structure, with defaulting. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int DefaultMinAxes( AstFrame *this, int *status ) + +* Class Membership: +* Frame member function. + +* Description: +* This function inspects the min_axes component of a Frame structure and +* derives a value for the MinAxes attribute. If the component's value +* indicates that the attribute has not been set, a suitable default is +* returned which is consistent with the state of the Frames's MaxAxes +* attribute. + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to be used for the MinAxes attribute. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + int max_axes; /* Value of MaxAxes attribute */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If the Frame's min_axes component is set, return its value. */ + if ( this->min_axes != -INT_MAX ) { + result = this->min_axes; + +/* Otherwise, the preferred default value is the number of Frame axes. */ + } else { + result = astGetNaxes( this ); + +/* Before returning this value, check if the MaxAxes attribute is set. If it + is, obtain its value. */ + if ( astTestMaxAxes( this ) ) { + max_axes = astGetMaxAxes( this ); + +/* If necessary, reduce the MinAxes default value so that it does not exceed + MaxAxes. */ + if ( result > max_axes ) result = max_axes; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static double Distance( AstFrame *this, + const double point1[], const double point2[], int *status ) { +/* +*++ +* Name: +c astDistance +f AST_DISTANCE + +* Purpose: +* Calculate the distance between two points in a Frame. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c double astDistance( AstFrame *this, +c const double point1[], const double point2[] ) +f RESULT = AST_DISTANCE( THIS, POINT1, POINT2, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +* This function finds the distance between two points whose Frame +* coordinates are given. The distance calculated is that along +* the geodesic curve that joins the two points. +* +* For example, in a basic Frame, the distance calculated will be +* the Cartesian distance along the straight line joining the two +* points. For a more specialised Frame describing a sky coordinate +* system, however, it would be the distance along the great circle +* passing through two sky positions. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c point1 +f POINT1( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the first point. +c point2 +f POINT2( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* containing the coordinates of the second point. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astDistance +f AST_DISTANCE = DOUBLE PRECISION +* The distance between the two points. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if this function is +c invoked with the AST error status set, or if it should fail for +f invoked with STATUS set to an error value, or if it should fail for +* any reason. +*-- +*/ + +/* Local Variables: */ + double delta; /* Separation along an axis */ + double result; /* Result value to return */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of Frame axes */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain the number of Frame axes. */ + naxes = astGetNaxes( this ); + if ( astOK ) { + +/* Loop to determine the Cartesian distance between the points. */ + result = 0.0; + for ( axis = 0; axis < naxes; axis++ ) { + +/* If any of the coordinates supplied is bad, set the distance to be + bad and quit looping. */ + if ( ( point1[ axis ] == AST__BAD ) || + ( point2[ axis ] == AST__BAD ) ) { + result = AST__BAD; + break; + +/* Otherwise, accumulate the sum of squared separations along each + axis. */ + } else { + delta = point1[ axis ] - point2[ axis ]; + result += ( delta * delta ); + } + } + +/* Take the square root to find the distance (if valid). */ + if ( result != AST__BAD ) result = sqrt( result ); + } + +/* Return the result. */ + return result; +} + +static int DoNotSimplify( AstMapping *this, int *status ) { +/* +* Name: +* DoNotSimplify + +* Purpose: +* Check if a Mapping is appropriate for simplification. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* int DoNotSImplify( AstMapping *this ); + +* Class Membership: +* CmpMap member function (over-rides the astDoNotSimplify protected +* method inherited from the parent class). + +* Description: +* This function returns a flag indivating if the supplied Mapping is +* appropriate for simplification. + +* Parameters: +* this +* Pointer to the Mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the supplied Mapping is not appropriate for +* simplification, and zero otherwise. + +* Notes: +* - A value of 0 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +*/ + +/* Unlike basic Mappings, Frames that have a set value for the Ident + can be simplified. So always return zero. */ + return 0; +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Frames are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* Frame member function (over-rides the astEqual protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two Frames are equivalent. + +* Parameters: +* this +* Pointer to the first Frame. +* that +* Pointer to the second Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the Frames are equivalent, zero otherwise. + +* Notes: +* - The two Frames are considered equivalent if the Mapping between +* them is a UnitMap. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *that; /* Pointer to the second Frame structure */ + AstFrame *this; /* Pointer to the first Frame structure */ + AstFrameSet *fs; /* FrameSet connecting the two Frames */ + AstMapping *map1; /* Mapping connecting the two Frames */ + AstMapping *map2; /* Simplified mapping connecting two Frames */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Checks that the second object is of the same class as the first . */ + if( !strcmp( astGetClass( this_object ), astGetClass( that_object ) ) ){ + +/* Obtain pointers to the two Frame structures. */ + this = (AstFrame *) this_object; + that = (AstFrame *) that_object; + +/* Get the Mapping between them, and see if it is a UnitMap. */ + fs = astConvert( that, this, "" ); + if( fs ) { + map1 = astGetMapping( fs, AST__BASE, AST__CURRENT ); + map2 = astSimplify( map1 ); + result = astIsAUnitMap( map2 ); + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + fs = astAnnul( fs ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int Fields( AstFrame *this, int axis, const char *fmt, + const char *str, int maxfld, char **fields, + int *nc, double *val, int *status ) { +/* +*+ +* Name: +* astFields + +* Purpose: +* Identify numerical fields within a formatted Axis value. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astFields( AstFrame *this, int axis, const char *fmt, +* const char *str, int maxfld, char **fields, +* int *nc, double *val ) + +* Class Membership: +* Frame method. + +* Description: +* This function identifies the numerical fields within a Frame axis +* value that has been formatted using astAxisFormat. It assumes that +* the value was formatted using the supplied format string. It also +* returns the equivalent floating point value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the Frame axis for which the values have been +* formatted (axis numbering starts at zero for the first axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format used when creating "str". +* str +* Pointer to a constant null-terminated string containing the +* formatted value. +* maxfld +* The maximum number of fields to identify within "str". +* fields +* A pointer to an array of at least "maxfld" character pointers. +* Each element is returned holding a pointer to the start of the +* corresponding field in "str" (in the order in which they occur +* within "str"), or NULL if no corresponding field can be found. +* nc +* A pointer to an array of at least "maxfld" integers. Each +* element is returned holding the number of characters in the +* corresponding field, or zero if no corresponding field can be +* found. +* val +* Pointer to a location at which to store the value +* equivalent to the returned field values. If this is NULL, +* it is ignored. + +* Returned Value: +* The number of fields succesfully identified and returned. + +* Notes: +* - Leading and trailing spaces are ignored. +* - If the formatted value is not consistent with the supplied format +* string, then a value of zero will be returned, "fields" will be +* returned holding NULLs, "nc" will be returned holding zeros, and +* "val" is returned holding VAL__BAD. +* - Fields are counted from the start of the formatted string. If the +* string contains more than "maxfld" fields, then trailing fields are +* ignored. +* - If this function is invoked with the global error status set, or +* if it should fail for any reason, then a value of zero will be returned +* as the function value, and "fields", "nc" and "val" will be returned +* holding their supplied values +*- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + int result; /* Result field count to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index and obtain a pointer to the required + Axis. */ + (void) astValidateAxis( this, axis, 1, "astFields" ); + ax = astGetAxis( this, axis ); + +/* Invoke the Axis astAxisFields method to perform the processing. */ + result = astAxisFields( ax, fmt, str, maxfld, fields, nc, val ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static AstFrameSet *FindFrame( AstFrame *target, AstFrame *template, + const char *domainlist, int *status ) { +/* +*++ +* Name: +c astFindFrame +f AST_FINDFRAME + +* Purpose: +* Find a coordinate system with specified characteristics. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c AstFrameSet *astFindFrame( AstFrame *target, AstFrame *template, +c const char *domainlist ) +f RESULT = AST_FINDFRAME( TARGET, TEMPLATE, DOMAINLIST, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +* This function uses a "template" Frame to search another Frame +* (or FrameSet) to identify a coordinate system which has a +* specified set of characteristics. If a suitable coordinate +* system can be found, the function returns a pointer to a +* FrameSet which describes the required coordinate system and how +* to convert coordinates to and from it. +* +* This function is provided to help answer general questions about +* coordinate systems, such as typically arise when coordinate +* information is imported into a program as part of an initially +* unknown dataset. For example: +* - Is there a wavelength scale? +* - Is there a 2-dimensional coordinate system? +* - Is there a celestial coordinate system? +* - Can I plot the data in ecliptic coordinates? +* +* You can also use this function as a means of reconciling a +* user's preference for a particular coordinate system (for +* example, what type of axes to draw) with what is actually +* possible given the coordinate information available. +* +* To perform a search, you supply a "target" Frame (or FrameSet) +* which represents the set of coordinate systems to be searched. +* If a basic Frame is given as the target, this set of coordinate +* systems consists of the one described by this Frame, plus all +* other "virtual" coordinate systems which can potentially be +* reached from it by applying built-in conversions (for example, +* any of the celestial coordinate conversions known to the AST +* library would constitute a "built-in" conversion). If a FrameSet +* is given as the target, the set of coordinate systems to be +* searched consists of the union of those represented by all the +* individual Frames within it. +* +* To select from this large set of possible coordinate systems, +* you supply a "template" Frame which is an instance of the type +* of Frame you are looking for. Effectively, you then ask the +* function to "find a coordinate system that looks like this". +* +* You can make your request more or less specific by setting +* attribute values for the template Frame. If a particular +* attribute is set in the template, then the function will only +* find coordinate systems which have exactly the same value for +* that attribute. If you leave a template attribute un-set, +* however, then the function has discretion about the value the +* attribute should have in any coordinate system it finds. The +* attribute will then take its value from one of the actual +* (rather than virtual) coordinate systems in the target. If the +* target is a FrameSet, its Current attribute will be modified to +* indicate which of its Frames was used for this purpose. +* +* The result of this process is a coordinate system represented by +* a hybrid Frame which acquires some attributes from the template +* (but only if they were set) and the remainder from the +* target. This represents the "best compromise" between what you +* asked for and what was available. A Mapping is then generated +* which converts from the target coordinate system to this hybrid +* one, and the returned FrameSet encapsulates all of this +* information. + +* Parameters: +c target +f TARGET = INTEGER (Given) +* Pointer to the target Frame (or FrameSet). +* +* Note that if a FrameSet is supplied (and a suitable +* coordinate system is found), then its Current attribute will +* be modified to indicate which Frame was used to obtain +* attribute values which were not specified by the template. +* This Frame will, in some sense, represent the "closest" +* non-virtual coordinate system to the one you requested. +c template +f TEMPLATE = INTEGER (Given) +* Pointer to the template Frame, which should be an instance of +* the type of Frame you wish to find. If you wanted to find a +* Frame describing a celestial coordinate system, for example, +* then you might use a SkyFrame here. See the "Examples" +* section for more ideas. +c domainlist +f DOMAINLIST = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing a +f A character string containing a +* comma-separated list of Frame domains. This may be used to +* establish a priority order for the different types of +* coordinate system that might be found. +* +* The function will first try to find a suitable coordinate +* system whose Domain attribute equals the first domain in this +* list. If this fails, the second domain in the list will be +* used, and so on, until a result is obtained. A blank domain +* (e.g. two consecutive commas) indicates that any coordinate +* system is acceptable (subject to the template) regardless of +* its domain. +* +* This list is case-insensitive and all white space is ignored. +* If you do not wish to restrict the domain in this way, +c you should supply an empty string. +f you should supply a blank string. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFindFrame() +f AST_FINDFRAME = INTEGER +* If the search is successful, the function returns a pointer +* to a FrameSet which contains the Frame found and a +* description of how to convert to (and from) the coordinate +* system it represents. Otherwise, a null Object pointer +* (AST__NULL) is returned without error. +* +* If a FrameSet is returned, it will contain two Frames. Frame +* number 1 (its base Frame) represents the target coordinate +* system and will be the same as the (base Frame of the) +* target. Frame number 2 (its current Frame) will be a Frame +* representing the coordinate system which the function +* found. The Mapping which inter-relates these two Frames will +* describe how to convert between their respective coordinate +* systems. +* +* Note that a FrameSet may be used both as a Mapping and as a +* Frame. If the result is used as a Mapping (e.g. with +* astTran2), then it provides a means of converting coordinates +* from the target coordinate system into the new coordinate +* system that was found (and vice versa if its inverse +* transformation is selected). If it is used as a Frame, its +* attributes will describe the new coordinate system. + +* Applicability: +* Frame +* This function applies to all Frames. +* FrameSet +* If the target is a FrameSet, the possibility exists that +* several of the Frames within it might be matched by the +* template. Unless the choice is sufficiently restricted by +c the "domainlist" string, the sequence in which Frames are +f the DOMAINLIST string, the sequence in which Frames are +* searched can then become important. In this case, the search +* proceeds as follows: +c - Each field in the "domainlist" string is considered in turn. +f - Each field in the DOMAINLIST string is considered in turn. +* - An attempt is made to match the template to each of the +* target's Frames in the order: (1) the current Frame, (2) the +* base Frame, (3) each remaining Frame in the order of being +* added to the target FrameSet. +* - Generally, the first match found is used. However, the +* Mapping between the target coordinate system and the +* resulting Frame is also examined. Preference is given to +* cases where both the forward and inverse transformations are +* defined (as indicated by the TranForward and TranInverse +* attributes). If only one transformation is defined, the +* forward one is preferred. +* - If a match is found and the domain of the resulting Frame also +c matches the current "domainlist" field, it is +f matches the current DOMAINLIST field, it is +c accepted. Otherwise, the next "domainlist" field is considered +f accepted. Otherwise, the next DOMAINLIST field is considered +* and the process repeated. +* +* If a suitable coordinate system is found, the Current +* attribute of the target FrameSet will be modified on exit to +* identify the Frame whose match with the target was eventually +* accepted. + +* Examples: +c result = astFindFrame( target, astFrame( 3, "" ), "" ); +f RESULT = AST_FINDFRAME( TARGET, AST_FRAME( 3, ' ', STATUS ), ' ', STATUS ) +* Searches for a 3-dimensional coordinate system in the target +* Frame (or FrameSet). No attributes have been set in the +c template Frame (created by astFrame), so no restriction has +f template Frame (created by AST_FRAME), so no restriction has +* been placed on the required coordinate system, other than +* that it should have 3 dimensions. The first suitable Frame +c found will be returned as part of the "result" FrameSet. +f found will be returned as part of the RESULT FrameSet. +c result = astFindFrame( target, astSkyFrame( "" ), "" ); +f RESULT = AST_FINDFRAME( TARGET, AST_SKYFRAME( ' ', STATUS ), ' ', STATUS ) +* Searches for a celestial coordinate system in the target +* Frame (or FrameSet). The type of celestial coordinate system +c is unspecified, so astFindFrame will return the first one +f is unspecified, so AST_FINDFRAME will return the first one +c found as part of the "result" FrameSet. If the target is +f found as part of the RESULT FrameSet. If the target is +* a FrameSet, then its Current attribute will be updated to +* identify the Frame that was used. +* +* If no celestial coordinate system can be found, a value of +* AST__NULL will be returned without error. +c result = astFindFrame( target, astSkyFrame( "MaxAxes=100" ), "" ); +f RESULT = AST_FINDFRAME( TARGET, AST_SKYFRAME( 'MaxAxes=100', STATUS ), ' ', STATUS ) +* This is like the last example, except that in the event of the +* target being a CmpFrame, the component Frames encapsulated by the +* CmpFrame will be searched for a SkyFrame. If found, the returned +* Mapping will included a PermMap which selects the required axes +* from the target CmpFrame. +* +* This is acomplished by setting the MaxAxes attribute of the +* template SkyFrame to a large number (larger than or equal to the +* number of axes in the target CmpFrame). This allows the SkyFrame +* to be used as a match for Frames containing from 2 to 100 axes. +c result = astFindFrame( target, astSkyFrame( "System=FK5" ), "" ); +f RESULT = AST_FINDFRAME( TARGET, AST_SKYFRAME( 'System=FK5', STATUS ), ' ', STATUS ) +* Searches for an equatorial (FK5) coordinate system in the +* target. The Equinox value for the coordinate system has not +* been specified, so will be obtained from the target. If the +* target is a FrameSet, its Current attribute will be updated +* to indicate which SkyFrame was used to obtain this value. +c result = astFindFrame( target, astFrame( 2, "" ), "sky,pixel," ); +f RESULT = AST_FINDFRAME( TARGET, AST_FRAME( 2, ' ', STATUS ), 'SKY,PIXEL,', STATUS ) +* Searches for a 2-dimensional coordinate system in the +* target. Initially, a search is made for a suitable coordinate +* system whose Domain attribute has the value "SKY". If this +* search fails, a search is then made for one with the domain +* "PIXEL". If this also fails, then any 2-dimensional +c coordinate system is returned as part of the "result" +f coordinate system is returned as part of the RESULT +* FrameSet. +* +* Only if no 2-dimensional coordinate systems can be reached by +* applying built-in conversions to any of the Frames in the +* target will a value of AST__NULL be returned. +c result = astFindFrame( target, astFrame( 1, "Domain=WAVELENGTH" ), "" ); +f RESULT = AST_FINDFRAME( TARGET, AST_FRAME( 1, 'Domain=WAVELENGTH', STATUS ), ' ', STATUS ) +* Searches for any 1-dimensional coordinate system in the +* target which has the domain "WAVELENGTH". +c result = astFindFrame( target, astFrame( 1, "" ), "wavelength" ); +f RESULT = AST_FINDFRAME( TARGET, AST_FRAME( 1, ' ', STATUS ), 'WAVELENGTH', STATUS ) +* This example has exactly the same effect as that above. It +* illustrates the equivalence of the template's Domain attribute +c and the fields in the "domainlist" string. +f and the fields in the DOMAINLIST string. +c result = astFindFrame( target, astFrame( 1, "MaxAxes=3" ), "" ); +f RESULT = AST_FINDFRAME( TARGET, AST_FRAME( 1, 'MaxAxes=3', STATUS ), ' ', STATUS ) +* This is a more advanced example which will search for any +* coordinate system in the target having 1, 2 or 3 +c dimensions. The Frame returned (as part of the "result" +f dimensions. The Frame returned (as part of the RESULT +* FrameSet) will always be 1-dimensional, but will be related +* to the coordinate system that was found by a suitable Mapping +* (e.g. a PermMap) which simply extracts the first axis. +* +* If we had wanted a Frame representing the actual (1, 2 or +* 3-dimensional) coordinate system found, we could set the +* PreserveAxes attribute to a non-zero value in the template. +c result = astFindFrame( target, astSkyFrame( "Permute=0" ), "" ); +f RESULT = AST_FINDFRAME( TARGET, AST_SKYFRAME( 'Permute=0', STATUS ), ' ', STATUS ) +* Searches for any celestial coordinate system in the target, +* but only finds one if its axes are in the conventional +* (longitude,latitude) order and have not been permuted +c (e.g. with astPermAxes). +f (e.g. with AST_PERMAXES). + +* Notes: +* - The Mapping represented by the returned FrameSet results in +* alignment taking place in the coordinate system specified by the +c AlignSystem attribute of the "template" Frame. See the description +f AlignSystem attribute of the TEMPLATE Frame. See the description +* of the AlignSystem attribute for further details. +* - Beware of setting the Domain attribute of the template and then +c using a "domainlist" string which does not include the template's domain +f using a DOMAINLIST string which does not include the template's domain +* (or a blank field). If you do so, no coordinate system will be +* found. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* More on Using Templates: +* A Frame (describing a coordinate system) will be found by this +* function if (a) it is "matched" by the template you supply, and +c (b) the value of its Domain attribute appears in the "domainlist" +f (b) the value of its Domain attribute appears in the DOMAINLIST +* string (except that a blank field in this string permits any +* domain). A successful match by the template depends on a number +* of criteria, as outlined below: +* - In general, a template will only match another Frame which +* belongs to the same class as the template, or to a derived (more +* specialised) class. For example, a SkyFrame template will match +* any other SkyFrame, but will not match a basic +* Frame. Conversely, a basic Frame template will match any class +* of Frame. +* - The exception to this is that a Frame of any class can be used to +* match a CmpFrame, if that CmpFrame contains a Frame of the same +* class as the template. Note however, the MaxAxes and MinAxes +* attributes of the template must be set to suitable values to allow +* it to match the CmpFrame. That is, the MinAxes attribute must be +* less than or equal to the number of axes in the target, and the MaxAxes +* attribute must be greater than or equal to the number of axes in +* the target. +* - If using a CmpFrame as a template frame, the MinAxes and MaxAxes +* for the template are determined by the MinAxes and MaxAxes values of +* the component Frames within the template. So if you want a template +* CmpFrame to be able to match Frames with different numbers of axes, +* then you must set the MaxAxes and/or MinAxes attributes in the component +* template Frames, before combining them together into the template +* CmpFrame. +* - If a template has a value set for any of its main attributes, then +* it will only match Frames which have an identical value for that +* attribute (or which can be transformed, using a built-in +* conversion, so that they have the required value for that +* attribute). If any attribute in the template is un-set, however, +* then Frames are matched regardless of the value they may have +* for that attribute. You may therefore make a template more or +* less specific by choosing the attributes for which you set +* values. This requirement does not apply to 'descriptive' attributes +* such as titles, labels, symbols, etc. +* - An important application of this principle involves the Domain +* attribute. Setting the Domain attribute of the template has the +* effect of restricting the search to a particular type of Frame +* (with the domain you specify). Conversely, if the Domain +* attribute is not set in the template, then the domain of the +* Frame found is not relevant, so all Frames are searched. Note +* that the +c "domainlist" string provides an alternative way of restricting the +f DOMAINLIST string provides an alternative way of restricting the +* search in the same manner, but is a more convenient interface if +* you wish to search automatically for another domain if the first +* search fails. +* - Normally, a template will only match a Frame which has the +* same number of axes as itself. However, for some classes of +* template, this default behaviour may be changed by means of the +* MinAxes, MaxAxes and MatchEnd attributes. In addition, the +* behaviour of a template may be influenced by its Permute and +* PreserveAxes attributes, which control whether it matches Frames +* whose axes have been permuted, and whether this permutation is +* retained in the Frame which is returned (as opposed to returning +* the axes in the order specified in the template, which is the +* default behaviour). You should consult the descriptions of these +* attributes for details of this more advanced use of templates. +*-- +*/ + +/* Local Variables: */ + AstFrame *frame; /* Pointer to result Frame */ + AstFrameSet *result; /* Pointer to result FrameSet */ + AstMapping *map; /* Pointer to result Mapping */ + AstMapping *tmp; /* Temporary Mapping pointer */ + char *domain_copy; /* Pointer to copy of result domain */ + char *domainlist_copy; /* Pointer to copy of domains list */ + const char *domain; /* Pointer to result Domain value */ + int *target_axes; /* Pointer to target axis assignments */ + int *template_axes; /* Pointer to template axis assignments */ + int i; /* Loop counter for characters */ + int j; /* Character index */ + int match; /* Template matched target? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate space to store a copy of the domains list, with added + commas. */ + domainlist_copy = astMalloc( strlen( domainlist ) + (size_t) 3 ); + if ( astOK ) { + +/* Make a copy of the domains list, with an extra comma added at each + end. Also remove all white space and convert to upper case. */ + domainlist_copy[ 0 ] = ','; + for ( i = 0, j = 1; domainlist[ i ]; i++ ) { + if ( !isspace( domainlist[ i ] ) ) { + domainlist_copy[ j++ ] = toupper( domainlist[ i ] ); + } + } + domainlist_copy[ j++ ] = ','; + domainlist_copy[ j ] = '\0'; + +/* Invoke the protected astMatch method associated with the template + Frame. This matches the template to the target and returns + information about how to convert between the target and the Frame + it found (if any). */ + match = astMatch( template, target, 0, + &template_axes, &target_axes, &map, &frame ); + +/* If successful, obtain a pointer to the Domain string of the result + Frame. Allocate space for a copy of this string, with added + commas. */ + if ( match && astOK ) { + domain = astGetDomain( frame ); + if ( astOK ) { + domain_copy = astMalloc( strlen( domain ) + (size_t) 3 ); + if ( astOK ) { + +/* Make a copy of the domain, adding an extra comma at each end. */ + domain_copy[ 0 ] = ','; + for ( i = 0, j = 1; domain[ i ]; i++ ) { + domain_copy[ j++ ] = domain[ i ]; + } + domain_copy[ j++ ] = ','; + domain_copy[ j ] = '\0'; + +/* Test if the domain appears in the domains list (with added + commas). If not, test if a blank domain (which permits the result + Frame to have any Domain) appears instead. */ + if ( strstr( domainlist_copy, domain_copy ) || + strstr( domainlist_copy, ",," ) ) { + +/* If the result Frame is acceptable, simplify the result Mapping. */ + tmp = astSimplify( map ); + map = astAnnul( map ); + map = tmp; + +/* Build the result FrameSet. */ + result = astFrameSet( target, "", status ); + astAddFrame( result, AST__BASE, map, frame ); + } + } + +/* Free the copy of the result domain. */ + domain_copy = astFree( domain_copy ); + } + +/* Free space and annul pointers allocated by astMatch. */ + template_axes = astFree( template_axes ); + target_axes = astFree( target_axes ); + map = astAnnul( map ); + frame = astAnnul( frame ); + } + } + +/* Free the copy of the domains list. */ + domainlist_copy = astFree( domainlist_copy ); + +/* If an error occurred, annul any result pointer. */ + if ( !astOK && result ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +const char *astFmtDecimalYr_( double year, int digits, int *status ) { +/* +*+ +* Name: +* astFmtDecimalYr + +* Purpose: +* Format an epoch expressed in years as a decimal string. + +* Type: +* Protected function. + +* Synopsis: +* #include "frame.h" +* const char *astFmtDecimalYr( double year, int digits ) + +* Class Membership: +* Frame member function. + +* Description: +* This function formats an epoch expressed in years as a decimal string +* and returns a pointer to the result. It is intended for formatting +* Frame Epoch values, etc, for display. + +* Parameters: +* year +* The epoch to be formatted. +* digits +* The number of digits of precision required. + +* Returned Value: +* Pointer to a null terminated string containing the formatted value. + +* Notes: +* - The result string is stored in static memory and may be +* over-written by a subsequent invocation of this function. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + int nc; /* Number of characters in buffer */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Limit the precision to what is meaningful. */ + digits = ( digits > DBL_DIG ) ? DBL_DIG : digits; + +/* Format the year value. Use "g" format to avoid buffer overflow and + to get useful diagnostic output if a silly value is given. */ + nc = sprintf( astfmtdecimalyr_buff, "%#.*g", digits, year ); + +/* Loop to remove redundant zeros from the end of the result. */ + while ( astfmtdecimalyr_buff[ --nc ] == '0' ) astfmtdecimalyr_buff[ nc ] = '\0'; + +/* If the last character is now a decimal point, put back one zero. */ + if ( astfmtdecimalyr_buff[ nc ] == '.' ) { + astfmtdecimalyr_buff[ ++nc ] = '0'; + astfmtdecimalyr_buff[ ++nc ] = '\0'; + } + +/* Return the result. */ + return astfmtdecimalyr_buff; +} + +static const char *Format( AstFrame *this, int axis, double value, int *status ) { +/* +*+ +* Name: +* astFormat + +* Purpose: +* Format a coordinate value for a Frame axis. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* const char *astFormat( AstFrame *this, int axis, double value ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a pointer to a string containing the +* formatted (character) version of a coordinate value for a Frame +* axis. The formatting applied is determined by the Frame's +* attributes and, in particular, by any Format attribute string +* that has been set for the axis. A suitable default format will +* be applied if necessary. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the Frame axis for which formatting is to be +* performed (axis numbering starts at zero for the first axis). +* value +* The coordinate value to be formatted. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Frame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Frame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic astFormat method available +* via the protected interface to the Frame class. The public +* interface to this method is provided by the astFormatId_ +* function. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + const char *result; /* Pointer value to return */ + int digits_set; /* Axis Digits attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Validate the axis index and obtain a pointer to the required Axis. */ + (void) astValidateAxis( this, axis, 1, "astFormat" ); + ax = astGetAxis( this, axis ); + +/* Test if any Axis attributes which may affect the result are undefined (i.e. + have not been explicitly set). If so, we over-ride them, giving them + temporary values dictated by the Frame. Only the Digits attribute is + relevant here. */ + digits_set = astTestAxisDigits( ax ); + if ( !digits_set ) astSetAxisDigits( ax, astGetDigits( this ) ); + +/* Format the value. */ + result = astAxisFormat( ax, value ); + +/* Clear any Axis attributes that were temporarily over-ridden. */ + if ( !digits_set ) astClearAxisDigits( ax ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static AstPointSet *FrameGrid( AstFrame *this, int size, const double *lbnd, + const double *ubnd, int *status ){ +/* +*+ +* Name: +* astFrameGrid + +* Purpose: +* Return a grid of points covering a rectangular area of a Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstPointSet *astFrameGrid( AstFrame *this_frame, int size, +* const double *lbnd, const double *ubnd ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a PointSet containing positions spread +* approximately evenly throughtout a specified rectangular area of +* the Frame. + +* Parameters: +* this +* Pointer to the Frame. +* size +* The preferred number of points in the returned PointSet. The +* actual number of points in the returned PointSet may be +* different, but an attempt is made to stick reasonably closely to +* the supplied value. +* lbnd +* Pointer to an array holding the lower bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. +* ubnd +* Pointer to an array holding the upper bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. + +* Returned Value: +* A pointer to a new PointSet holding the grid of points. + +* Notes: +* - A NULL pointer is returned if an error occurs. +*- +*/ + +/* Local Variables: */ + AstPointSet *result; + const char *unit; + double **ptr; + double *gmean; + double *step; + int *maxi; + int *nsame; + int *ntick; + int *pi; + int bad; + int iax; + int ipp; + int jax; + int naxes; + int np; + int ntick0; + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the number of axes in the Frame. */ + naxes = astGetNaxes( this ); + +/* Allocate an array to hold the number of ticks along each axis. */ + ntick = astMalloc( sizeof(int)*naxes ); + +/* Allocate an array to hold the geometric mean of the lengths of the + axes that have the same units as the current axis. */ + gmean = astMalloc( naxes*sizeof(double) ); + +/* Allocate an array to hold the number of axes that share the same unit. */ + nsame = astMalloc( naxes*sizeof(int) ); + if( astOK ) { + +/* For each axis, find the total number of axes in the Frame that have + the same unit string. Also, find the product of the lengths of these + axes. */ + bad = 0; + for( iax = 0; iax < naxes; iax++ ) { + nsame[ iax ] = 1; + + if( ubnd[ iax ] == AST__BAD && + lbnd[ iax ] == AST__BAD ) { + bad = 1; + break; + } + + gmean[ iax ] = ubnd[ iax ] - lbnd[ iax ]; + unit = astGetUnit( this, iax ); + for( jax = 0; jax < naxes; jax++ ) { + if( jax != iax ) { + if( astOK && !strcmp( unit, astGetUnit( this, jax ) ) ) { + nsame[ iax ]++; + gmean[ iax ] *= ubnd[ jax ] - lbnd[ jax ]; + } + } + } + } + +/* Do nothing if any bad bounds were supplied, or if the size is less + than 1. */ + if( !bad && size >= 1 ) { + +/* Get the nominal number of ticks per axis. */ + ntick0 = pow( size, 1.0/(double)naxes ); + if( ntick0 < 2 ) ntick0 = 2; + +/* Convert the dimension products into geometric means. */ + for( iax = 0; iax < naxes; iax++ ) { + gmean[ iax ] = pow( fabs(gmean[ iax ]), 1.0/(double)nsame[ iax ] ); + } + +/* The number of ticks to use on each axis is equal to the nominal number + multiplied by the ratio of the axis length to the geometric mean of the + axis lengths that sahare the same unit string. This gives more ticks + on the longer axes within any group of common-unit axes, whilst + retaining the overall number of ticks (roughly). Also find the total + number of points. */ + np = 1; + for( iax = 0; iax < naxes; iax++ ) { + ntick[ iax ] = ntick0*( ubnd[ iax ] - lbnd[ iax ] )/gmean[ iax ]; + if( ntick[ iax ] < 2 ) ntick[ iax ] = 2; + np *= ntick[ iax ]; + } + +/* Create a PointSet large enough to hold this many points. */ + result = astPointSet( np, naxes, " ", status ); + ptr = astGetPoints( result ); + +/* Allocate memory to hold the max indices on each axis. */ + maxi = astMalloc( sizeof( int )*(size_t) naxes ); + +/* Allocate memory to hold the indices of the current position.*/ + pi = astMalloc( sizeof( int )*(size_t) naxes ); + +/* Allocate memory to hold the step size for each axis. */ + step = astMalloc( sizeof( double )*(size_t) naxes ); + if( astOK ) { + +/* For every axis, set up the step size, initialise the current position to + the lower bound, and store a modified upper limit which includes some + safety marging to allow for rounding errors. */ + for( iax = 0; iax < naxes; iax++ ) { + step[ iax ] = ( ubnd[ iax ] - lbnd[ iax ] )/( ntick[ iax ] - 1 ); + pi[ iax ] = 0; + maxi[ iax ] = ntick[ iax ] - 1; + } + +/* Initialise the index of the next position to store. */ + ipp = 0; + +/* Loop round adding points to the array until the whole volume has been + done. */ + iax = 0; + while( iax < naxes ) { + +/* Add the current point to the supplied array, and increment the index of + the next point to add. */ + for( iax = 0; iax < naxes; iax++ ) { + ptr[ iax ][ ipp ] = lbnd[ iax ] + pi[ iax ]*step[ iax ]; + } + ipp++; + +/* We now move the current position on to the next sample */ + iax = 0; + while( iax < naxes ) { + pi[ iax ]++; + if( pi[ iax ] > maxi[ iax ] ) { + pi[ iax ] = 0; + iax++; + } else { + break; + } + } + } + } + +/* Free resources. */ + maxi = astFree( maxi ); + pi = astFree( pi ); + step = astFree( step ); + +/* Report error if supplied values were bad. */ + } else if( astOK ) { + if( bad ) { + astError( AST__ATTIN, "astFrameGrid(%s): One of more of the " + "supplied bounds is AST__BAD (programming error).", + status, astGetClass( this ) ); + } else if( size < 1 ) { + astError( AST__ATTIN, "astFrameGrid(%s): The supplied grid " + "size (%d) is invalid (programming error).", + status, astGetClass( this ), size ); + } + } + } + +/* Free resources. */ + ntick = astFree( ntick ); + nsame = astFree( nsame ); + gmean = astFree( gmean ); + +/* Annul the returned PointSet if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the PointSet holding the grid. */ + return result; +} + +static double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status ) { +/* +*+ +* Name: +* astGap + +* Purpose: +* Find a "nice" gap for tabulating Frame axis values. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* double astGap( AstFrame *this, int axis, double gap, int *ntick ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted values for a Frame axis, the returned gap +* size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + double result; /* The nice gap value */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Validate the axis index and obtain a pointer to the required + Axis. */ + (void) astValidateAxis( this, axis, 1, "astGap" ); + ax = astGetAxis( this, axis ); + +/* Find the gap. */ + result = astAxisGap( ax, gap, ntick ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int GetActiveUnit( AstFrame *this, int *status ){ +/* +*++ +* Name: +c astGetActiveUnit +f AST_GETACTIVEUNIT + +* Purpose: +* Determines how the Unit attribute will be used. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c int astGetActiveUnit( AstFrame *this ) +f RESULT = AST_GETACTIVEUNIT( THIS, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* returns the current value of the ActiveUnit flag for a Frame. See +c the description of the astSetActiveUnit function +f the description of the AST_SETACTIVEUNIT routine +* for a description of the ActiveUnit flag. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetActiveUnit +f AST_GETACTIVEUNIT = LOGICAL +* The current value of the ActiveUnit flag. + +* Notes: +c - A zero value will be returned if this function is +c invoked with the AST error status set, or if it should fail for +f - A value of .FALSE. will be returned if this function is +f invoked with STATUS set to an error value, or if it should fail for +* any reason. +*-- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to axis structure */ + int i; /* Index of axis in Frame */ + int has_skyaxis; /* Does Frame contain any SkyAxes? */ + int nax; /* Number of axes in Frame */ + int result; /* The returned value */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* See if the Frame contains a SkyAxis. */ + has_skyaxis = 0; + nax = astGetNaxes( this ); + for( i = 0; i < nax; i++ ) { + ax = astGetAxis( this, i ); + if( astIsASkyAxis( ax ) ) has_skyaxis = 1; + ax = astAnnul( ax ); + } + +/* If the Frame contains a SkyAxis the ActiveUnit flag is always zero. */ + if( !has_skyaxis ) { + +/* Otherwise, get the value from the Frame. If it has not yet been assigned a + value return the value zero. */ + result = this->active_unit; + if( result == -INT_MAX ) result = 0; + } + +/* Return the result. */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Frame member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Frame, formatted as a character string. + +* Parameters: +* this +* Pointer to the Frame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the Frame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Frame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstAxis *ax; /* Pointer to Axis */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + AstFrame *this; /* Pointer to the Frame structure */ + AstSystemType system; /* System code */ + char pfrm_attrib[ 100 ]; /* Primary Frame attribute */ + char *axis_attrib; /* Pointer to axis attribute name */ + const char *old_attrib; /* Pointer to supplied attribute name string */ + const char *result; /* Pointer value to return */ + double dval; /* Double attibute value */ + double epoch; /* Epoch attribute value (as MJD) */ + int axis; /* Frame axis number */ + int axis_nc; /* No. characters in axis attribute name */ + int digits; /* Digits attribute value */ + int direction; /* Direction attribute value */ + int free_axis_attrib; /* Should axis_attrib be freed? */ + int has_axis; /* Does attrib name include axis specifier? */ + int len; /* Length of attrib string */ + int match_end; /* MatchEnd attribute value */ + int max_axes; /* MaxAxes attribute value */ + int min_axes; /* MinAxes attribute value */ + int naxes; /* Naxes attribute value */ + int nc; /* No. characters read by astSscanf */ + int oldrep; /* Original error reporting state */ + int paxis; /* Axis index within primary frame */ + int permute; /* Permute attribute value */ + int preserve_axes; /* PreserveAxes attribute value */ + int used; /* Could the setting string be used? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_object; + +/* Set a flag indicating if the attribute name includes an axis + specifier. */ + has_axis = ( strchr( attrib, '(' ) != NULL ); + +/* A flag indicating that we do not need to free the axis_attrib memory. */ + free_axis_attrib = 0; + +/* Initialise things to avoid compiler warnings. */ + axis_attrib = NULL; + old_attrib = NULL; + +/* Jump back to here if we are trying the same attribute but with an explicit + axis "(1)" added to the end of the name. */ +L1: + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Save the number of axes in the Frame for later use. */ + naxes = astGetNaxes( this ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Digits. */ +/* ------- */ + if ( !strcmp( attrib, "digits" ) ) { + digits = astGetDigits( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", digits ); + result = getattrib_buff; + } + +/* Digits(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "digits(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + +/* There is no function to obtain the Digits attribute value for an + axis directly, so obtain a pointer to the Axis and use this to + obtain the value. Use the Frame's Digits attribute instead if the + Axis attribute value is not set. */ + (void) astValidateAxis( this, axis - 1, 1, "astGetDigits(axis)" ); + ax = astGetAxis( this, axis - 1 ); + if ( astTestAxisDigits( ax ) ) { + digits = astGetAxisDigits( ax ); + } else { + digits = astGetDigits( this ); + } + ax = astAnnul( ax ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", digits ); + result = getattrib_buff; + } + + +/* Direction(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "direction(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + direction = astGetDirection( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", direction ); + result = getattrib_buff; + } + +/* Epoch. */ +/* ------ */ + } else if ( !strcmp( attrib, "epoch" ) ) { + epoch = astGetEpoch( this ); + if ( astOK ) { + +/* Format the Epoch as decimal years. Use a Besselian epoch if it will + be less than 1984.0, otherwise use a Julian epoch. */ + result = astFmtDecimalYr( ( epoch < palEpj2d( 1984.0 ) ) ? + palEpb( epoch ) : palEpj( epoch ), DBL_DIG ); + } + +/* Top(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "top(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetTop( this, axis -1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Bottom(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "bottom(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetBottom( this, axis -1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Domain. */ +/* ------- */ + } else if ( !strcmp( attrib, "domain" ) ) { + result = astGetDomain( this ); + +/* Format(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "format(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astGetFormat( this, axis - 1 ); + +/* Label(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "label(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astGetLabel( this, axis - 1 ); + +/* MatchEnd. */ +/* --------- */ + } else if ( !strcmp( attrib, "matchend" ) ) { + match_end = astGetMatchEnd( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", match_end ); + result = getattrib_buff; + } + +/* MaxAxes. */ +/* -------- */ + } else if ( !strcmp( attrib, "maxaxes" ) ) { + max_axes = astGetMaxAxes( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", max_axes ); + result = getattrib_buff; + } + +/* MinAxes. */ +/* -------- */ + } else if ( !strcmp( attrib, "minaxes" ) ) { + min_axes = astGetMinAxes( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", min_axes ); + result = getattrib_buff; + } + +/* Naxes. */ +/* -----_ */ + } else if ( !strcmp( attrib, "naxes" ) ) { + (void) sprintf( getattrib_buff, "%d", naxes ); + result = getattrib_buff; + +/* Permute. */ +/* -------- */ + } else if ( !strcmp( attrib, "permute" ) ) { + permute = astGetPermute( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", permute ); + result = getattrib_buff; + } + +/* PreserveAxes. */ +/* ------------- */ + } else if ( !strcmp( attrib, "preserveaxes" ) ) { + preserve_axes = astGetPreserveAxes( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", preserve_axes ); + result = getattrib_buff; + } + +/* Symbol(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "symbol(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astGetSymbol( this, axis - 1 ); + +/* AlignSystem. */ +/* ------------ */ +/* Obtain the AlignSystem code and convert to a string. */ + } else if ( !strcmp( attrib, "alignsystem" ) ) { + system = astGetAlignSystem( this ); + if ( astOK ) { + result = astSystemString( this, system ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid " + "AlignSystem identification code (%d).", status, + astGetClass( this ), astGetClass( this ), (int) system ); + } + } + +/* System. */ +/* ------- */ +/* Obtain the System code and convert to a string. */ + } else if ( !strcmp( attrib, "system" ) ) { + system = astGetSystem( this ); + if ( astOK ) { + result = astSystemString( this, system ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid " + "System identification code (%d).", status, + astGetClass( this ), astGetClass( this ), (int) system ); + } + } + +/* Title. */ +/* ------ */ + } else if ( !strcmp( attrib, "title" ) ) { + result = astGetTitle( this ); + +/* Unit(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "unit(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astGetUnit( this, axis - 1 ); + +/* NormUnit(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "normunit(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astGetNormUnit( this, axis - 1 ); + +/* InternalUnit(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "internalunit(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astGetInternalUnit( this, axis - 1 ); + +/* ObsLat. */ +/* ------- */ + } else if ( !strcmp( attrib, "obslat" ) ) { + dval = astGetObsLat( this ); + if ( astOK ) { + +/* If not already created, create an FK5 J2000 SkyFrame which will be used + for formatting and unformatting ObsLon and ObsLat values. */ + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame("system=FK5,equinox=J2000,format(2)=dms.2", status ); + astEndPM; + } + +/* Display absolute value preceded by "N" or "S" as appropriate. */ + if( dval < 0 ) { + (void) sprintf( getattrib_buff, "S%s", astFormat( skyframe, 1, -dval ) ); + } else { + (void) sprintf( getattrib_buff, "N%s", astFormat( skyframe, 1, dval ) ); + } + result = getattrib_buff; + } + +/* ObsLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "obslon" ) ) { + dval = astGetObsLon( this ); + if ( astOK ) { + +/* Put into range +/- PI. */ + dval = palDrange( dval ); + +/* If not already created, create an FK5 J2000 SkyFrame which will be used + for formatting and unformatting ObsLon and ObsLat values. */ + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame( "system=FK5,equinox=J2000,format(2)=dms.2", status ); + astEndPM; + } + +/* Display absolute value preceded by "E" or "W" as appropriate. */ + if( dval < 0 ) { + (void) sprintf( getattrib_buff, "W%s", astFormat( skyframe, 1, -dval ) ); + } else { + (void) sprintf( getattrib_buff, "E%s", astFormat( skyframe, 1, dval ) ); + } + result = getattrib_buff; + + } + +/* ObsAlt. */ +/* ------- */ + } else if ( !strcmp( attrib, "obsalt" ) ) { + dval = astGetObsAlt( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Dut1. */ +/* ---- */ + } else if ( !strcmp( attrib, "dut1" ) ) { + dval = astGetDut1( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Other axis attributes. */ +/* ---------------------- */ +/* If the attribute was not identified above, but appears to refer to + a Frame axis, then it may refer to an Axis object of a derived type + (which has additional attributes not recognised here). */ + } else if ( !free_axis_attrib && ( nc = 0, + ( 1 == astSscanf( attrib, "%*[^()]%n(%d)%n", + &axis_nc, &axis, &nc ) ) + && ( nc >= len ) ) ) { + +/* Validate the axis index and extract the attribute name. */ + (void) astValidateAxis( this, axis - 1, 1, "astGet" ); + axis_attrib = astString( attrib, axis_nc ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis - 1 ); + if( astOK ) { + +/* Assume that we will be able to use the attribute name. */ + used = 1; + +/* Temporarily switch off error reporting so that if the following attempt + to access the axis attribute fails, we can try to interpret the + attribute name as an attribute of the primary Frame containing the + specified axis. Any errors reported in this context will simply be + ignored, in particularly they are not deferred for later delivery. */ + oldrep = astReporting( 0 ); + +/* Use the Axis astGetAttrib method to obtain the result. */ + result = astGetAttrib( ax, axis_attrib ); + +/* If the above call failed with a status of AST__BADAT, indicating that + the attribute name was not recognised, clear the status so that we can + try to interpret the attribute name as an attribute of the primary Frame + containing the specified axis. */ + if( astStatus == AST__BADAT ) { + astClearStatus; + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + +/* Only attempt to use the primary Frame if it is not the same as "this" + - otherwise we could end up in an infinite loop. */ + if( pfrm != this ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astGet" ); + +/* Modify the attribute name to refer to the axis numbering of the + primary frame. */ + sprintf( pfrm_attrib, "%s(%d)", axis_attrib, paxis + 1 ); + +/* Attempt to use the Axis astGetAttrib method to obtain the result. */ + result = astGetAttrib( pfrm, pfrm_attrib ); + +/* If this failed, clear the status and indicate that we have not managed to + use the attribute name. */ + if( !astOK ) { + astClearStatus; + used = 0; + } + + } else { + used = 0; + } + +/* If not found attempt to get the attribute value from the Axis, omitting + the axis index. */ + if( ! used ) { + result = astGetAttrib( pfrm, axis_attrib ); + if( !astOK ) { + astClearStatus; + } else { + used = 1; + } + } + +/* Annul the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + +/* If we could not use the attribute name, attempt to get the axis + attribute again, this time retaining the error report. This is done + to ensure the user gets an appropriate error message. */ + if( !used ) result = astGetAttrib( ax, axis_attrib ); + } + +/* Annul the Axis pointer and free the memory holding the attribute + name. */ + ax = astAnnul( ax ); + axis_attrib = astFree( axis_attrib ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, and the Frame has only 1 axis, + and the attribute name does not already include an axis specifier, try + again after appending "(1)" to the end of the attribute name. */ + } else if( !has_axis && naxes == 1 ) { + +/* Take a copy of the supplied name, allowing 3 extra characters for the + axis specifier "(1)". */ + axis_attrib = astMalloc( len + 4 ); + if( axis_attrib ) memcpy( axis_attrib, attrib, len ); + +/* Indicate we should free the axis_attrib memory. */ + free_axis_attrib = 1; + +/* Add in the axis specifier. */ + strcpy( axis_attrib + len, "(1)" ); + +/* Use the new attribute name instead of the supplied name. */ + old_attrib = attrib; + attrib = axis_attrib; + +/* Indicate the attribute name now has an axis specifier. */ + has_axis = 1; + +/* Jump back to try interpreting the new attribute name. */ + goto L1; + +/* Not recognised. */ +/* --------------- */ +/* If the attribute name is still not recognised, pass it on to the parent + method for further interpretation. First re-instate the original attrib + name string if it was changed above. */ + } else { + if( free_axis_attrib ) { + attrib = old_attrib; + axis_attrib = astFree( axis_attrib ); + } + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static AstAxis *GetAxis( AstFrame *this, int axis, int *status ) { +/* +*+ +* Name: +* astGetAxis + +* Purpose: +* Obtain a pointer to a specified Axis from a Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstAxis *astGetAxis( AstFrame *this, int axis ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a pointer to the Axis object associated +* with one of the axes of a Frame. This object describes the +* quantity which is represented along that axis. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which an Axis pointer is +* required. + +* Returned Value: +* A pointer to the requested Axis object. + +* Notes: +* - The reference count of the requested Axis object will be +* incremented by one to reflect the additional pointer returned by +* this function. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstAxis *result; /* Pointer to Axis */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise. */ + result = NULL; + +/* Validate and permute the axis index. */ + axis = astValidateAxis( this, axis, 1, "astGetAxis" ); + +/* If OK, clone the required Axis pointer. */ + if ( astOK ) result = astClone( this->axis[ axis ] ); + +/* Return the result. */ + return result; +} + +static const char *GetDefaultLabel( int axis, int *status ) { +/* +* Name: +* GetDefaultLabel + +* Purpose: +* Return a pointer to a default axis Label string. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* const char *GetDefaultLabel( int axis, int *status ) + +* Class Membership: +* Frame member function + +* Description: +* This function returns a pointer to a string holding a default axis +* Label value. + +* Parameters: +* axis +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a static null-terminated string containing the attribute +* value. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Format the axis index, putting the string in a global buffer. */ + (void) sprintf( label_buff, "Axis %d", axis + 1 ); + +/* Return a pointer to the global buffer. */ + return label_buff; +} + +static const char *GetDefaultSymbol( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetDefaultSymbol + +* Purpose: +* Return a pointer to a default axis Symbol string. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* const char *GetDefaultSymbol( AstFrame *this, int axis, int *status ) + +* Class Membership: +* Frame member function + +* Description: +* This function returns a pointer to a string holding a default axis +* Symbol value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a static null-terminated string containing the attribute +* value. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Note we use "sprintf" once to determine how many characters are + produced by the "%d" format string and then limit the number of + characters used from the Domain string in the second invocation of + "sprintf" so that the total length of the default Symbol string + does not exceed SYMBOL_BUFF_LEN characters. */ + (void) sprintf( symbol_buff, "%.*s%d", + SYMBOL_BUFF_LEN - sprintf( symbol_buff, "%d", axis + 1 ), + astTestDomain( this ) ? astGetDomain( this ) : "x", + axis + 1 ); + +/* Use the AddUnderscores function to replace any white space in the Symbol + string with underscore characters. */ + AddUnderscores( symbol_buff, status ); + +/* Return a pointer to the global buffer. */ + return symbol_buff; +} + +static const char *GetDefaultTitle( AstFrame *this, int *status ) { +/* +* Name: +* GetDefaultTitle + +* Purpose: +* Return a pointer to a default Title string. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* const char *GetDefaultTitle( AstFrame *this, int *status ) + +* Class Membership: +* Frame member function + +* Description: +* This function returns a pointer to a string holding a default Title value. + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a static null-terminated string containing the attribute +* value. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Create the Title value and put it in the global buffer. */ + (void) sprintf( title_buff, "%d-d coordinate system", astGetNaxes( this ) ); + +/* Return a pointer to the global buffer. */ + return title_buff; +} + +static int GetFrameFlags( AstFrame *this, int *status ){ +/* +*+ +* Name: +* astGetFrameFlags + +* Purpose: +* Return the bit mask of flags associated with a Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "frame.h" +* int *astGetFrameFlags( astFrame *this ) + +* Class Membership: +* Frame virtual function. + +* Description: +* This function returns a bit mask holding the current set of flags +* associated with a Frame. See astSetFrameFlags for details of these +* flags. + +* Parameters: +* this +* The Frame. + +* Returned Value: +* The bit mask. + +* Notes: +* - Zero is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the result. */ + return this->flags; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* Frame member function (over-rides the protected astGetIsLinear +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsLinear attribute for a +* Frame, which is always one because a Frame is treated like a UnitMap. + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. +*/ + return 1; +} + +static int GetIsSimple( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsSimple + +* Purpose: +* Return the value of the IsSimple attribute for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsSimple( AstMapping *this, int *status ) + +* Class Membership: +* Frame member function (over-rides the protected astGetIsSimple +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsSimple attribute for a +* Frame, which is always zero because Frames are not immutable (unlike +* non-Frame Mappings). + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. +*/ + return 0; +} + +static int GetNaxes( AstFrame *this, int *status ) { +/* +*+ +* Name: +* astGetNaxes + +* Purpose: +* Determine how many axes a Frame has. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astGetNaxes( AstFrame *this ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns the number of axes for a Frame. + +* Parameters: +* this +* Pointer to the Frame. + +* Returned Value: +* The number of Frame axes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the number of Frame axes. */ + return this->naxes; +} + +static int GetNin( AstMapping *this_mapping, int *status ) { +/* +* Name: +* GetNin + +* Purpose: +* Get the number of input coordinates for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int GetNin( AstMapping *this, int *status ) + +* Class Membership: +* Frame member function (over-rides the astGetNin method inherited +* from the Mapping class). + +* Description: +* This function returns the number of input coordinate values +* required per point by a Frame, when used as a Mapping (i.e. the +* number of dimensions of the space in which input points reside). + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Number of coordinate values required. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *this; /* Pointer to Frame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_mapping; + +/* Return the number of Frame axes. */ + result = astGetNaxes( this ); + +/* Return the result. */ + return result; +} + +static int GetNout( AstMapping *this_mapping, int *status ) { +/* +* Name: +* GetNout + +* Purpose: +* Get the number of output coordinates for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int GetNout( AstMapping *this, int *status ) + +* Class Membership: +* Frame member function (over-rides the astGetNout method +* inherited from the Mapping class). + +* Description: +* This function returns the number of output coordinate values +* generated per point by a Frame, when used as a Mapping (i.e. the +* number of dimensions of the space in which output points +* reside). + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Number of coordinate values generated. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *this; /* Pointer to Frame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_mapping; + +/* Return the number of Frame axes. */ + result = astGetNaxes( this ); + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Frame member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Frame, +* in bytes. + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *this; /* Pointer to Frame structure */ + int axis; /* Axis index */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the FrameSet structure. */ + this = (AstFrame *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astGetObjSize( this->variants ); + result += astTSizeOf( this->domain ); + result += astTSizeOf( this->title ); + result += astTSizeOf( this->axis ); + result += astTSizeOf( this->perm ); + + for ( axis = 0; axis < this->naxes; axis++ ) { + result += astGetObjSize( this->axis[ axis ] ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const int *GetPerm( AstFrame *this, int *status ) { +/* +*+ +* Name: +* astGetPerm + +* Purpose: +* Access the axis permutation array for a Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* const int *astGetPerm( AstFrame *this ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a pointer to the axis permutation array +* for a Frame. This array constitutes a lookup-table that converts +* between an axis number supplied externally and the corresponding +* index in the Frame's internal axis arrays. + +* Parameters: +* this +* Pointer to the Frame. + +* Returned Value: +* Pointer to the Frame's axis permutation array (a constant array +* of int). Each element of this contains the (zero-based) +* internal axis index to be used in place of the external index +* which is used to address the permutation array. If the Frame has +* zero axes, this pointer will be NULL. + +* Notes: +* - This protected method is provided to assist class +* implementations which need to implement axis-dependent +* extensions to Frame methods, and which therefore need to know +* how a Frames's external axis index is converted for internal +* use. +* - The pointer returned by this function gives direct access to +* data internal to the Frame object. It remains valid only so long +* as the Frame exists. The permutation array contents may be +* modified by other functions which operate on the Frame and this +* may render the returned pointer invalid. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return a pointer to the axis permutation array. */ + return this->perm; +} + +static AstFrameSet *GetFrameVariants( AstFrame *this, int *status ){ +/* +*+ +* Name: +* astGetFrameVariants + +* Purpose: +* Returns the FrameSet holding the available Frame variants. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstFrameSet *astGetFrameVariants( AstFrame *this ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a pointer to any FrameSet previously stored +* in the Frame using method astSetVariants. + +* Parameters: +* this +* Pointer to the Frame. + +* Returned Value: +* astGetFrameVariants +* A pointer to the FrameSet. It should be annulled using astAnnul +* when no longer needed. NULL will be returned if no FrameSet is +* stored in the Frame. + +* Notes: +* - A NULL value will be returned if this function is invoked with the +* AST error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFrameSet *result; + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a clone of any FrameSet pointer. */ + if( this->variants ) result = astClone( this->variants ); + +/* Return the result. */ + return result; +} + +void astInitFrameVtab_( AstFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitFrameVtab + +* Purpose: +* Initialise a virtual function table for a Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "frame.h" +* void astInitFrameVtab( AstFrameVtab *vtab, const char *name ) + +* Class Membership: +* Frame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Frame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAFrame ) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->Abbrev = Abbrev; + vtab->CheckPerm = CheckPerm; + vtab->ClearDigits = ClearDigits; + vtab->ClearDirection = ClearDirection; + vtab->ClearDomain = ClearDomain; + vtab->ClearFormat = ClearFormat; + vtab->ClearLabel = ClearLabel; + vtab->ClearMatchEnd = ClearMatchEnd; + vtab->ClearMaxAxes = ClearMaxAxes; + vtab->ClearMinAxes = ClearMinAxes; + vtab->ClearPermute = ClearPermute; + vtab->ClearPreserveAxes = ClearPreserveAxes; + vtab->ClearSymbol = ClearSymbol; + vtab->ClearTitle = ClearTitle; + vtab->ClearUnit = ClearUnit; + vtab->Convert = Convert; + vtab->ConvertX = ConvertX; + vtab->Angle = Angle; + vtab->Distance = Distance; + vtab->Fields = Fields; + vtab->FindFrame = FindFrame; + vtab->MatchAxes = MatchAxes; + vtab->MatchAxesX = MatchAxesX; + vtab->Format = Format; + vtab->Centre = Centre; + vtab->Gap = Gap; + vtab->GetAxis = GetAxis; + vtab->GetDigits = GetDigits; + vtab->GetDirection = GetDirection; + vtab->GetDomain = GetDomain; + vtab->GetFormat = GetFormat; + vtab->GetLabel = GetLabel; + vtab->GetMatchEnd = GetMatchEnd; + vtab->GetMaxAxes = GetMaxAxes; + vtab->GetMinAxes = GetMinAxes; + vtab->GetNaxes = GetNaxes; + vtab->GetPerm = GetPerm; + vtab->GetPermute = GetPermute; + vtab->GetPreserveAxes = GetPreserveAxes; + vtab->GetSymbol = GetSymbol; + vtab->GetTitle = GetTitle; + vtab->GetUnit = GetUnit; + vtab->GetInternalUnit = GetInternalUnit; + vtab->GetNormUnit = GetNormUnit; + vtab->Intersect = Intersect; + vtab->IsUnitFrame = IsUnitFrame; + vtab->Match = Match; + vtab->Norm = Norm; + vtab->NormBox = NormBox; + vtab->AxDistance = AxDistance; + vtab->AxOffset = AxOffset; + vtab->AxIn = AxIn; + vtab->AxAngle = AxAngle; + vtab->FrameGrid = FrameGrid; + vtab->Offset = Offset; + vtab->Offset2 = Offset2; + vtab->Resolve = Resolve; + vtab->ResolvePoints = ResolvePoints; + vtab->LineDef = LineDef; + vtab->LineContains = LineContains; + vtab->LineCrossing = LineCrossing; + vtab->LineOffset = LineOffset; + vtab->Overlay = Overlay; + vtab->PermAxes = PermAxes; + vtab->PickAxes = PickAxes; + vtab->PrimaryFrame = PrimaryFrame; + vtab->SetAxis = SetAxis; + vtab->SetDigits = SetDigits; + vtab->SetDirection = SetDirection; + vtab->SetDomain = SetDomain; + vtab->SetFormat = SetFormat; + vtab->SetLabel = SetLabel; + vtab->SetMatchEnd = SetMatchEnd; + vtab->SetMaxAxes = SetMaxAxes; + vtab->SetMinAxes = SetMinAxes; + vtab->SetPermute = SetPermute; + vtab->SetPreserveAxes = SetPreserveAxes; + vtab->SetSymbol = SetSymbol; + vtab->SetTitle = SetTitle; + vtab->SetUnit = SetUnit; + vtab->SubFrame = SubFrame; + vtab->TestDigits = TestDigits; + vtab->TestDirection = TestDirection; + vtab->TestDomain = TestDomain; + vtab->TestFormat = TestFormat; + vtab->TestLabel = TestLabel; + vtab->TestMatchEnd = TestMatchEnd; + vtab->TestMaxAxes = TestMaxAxes; + vtab->TestMinAxes = TestMinAxes; + vtab->TestPermute = TestPermute; + vtab->TestPreserveAxes = TestPreserveAxes; + vtab->TestSymbol = TestSymbol; + vtab->TestTitle = TestTitle; + vtab->TestUnit = TestUnit; + vtab->Unformat = Unformat; + vtab->ValidateAxis = ValidateAxis; + vtab->ValidateAxisSelection = ValidateAxisSelection; + vtab->ValidateSystem = ValidateSystem; + vtab->SystemString = SystemString; + vtab->SystemCode = SystemCode; + + vtab->GetFrameFlags = GetFrameFlags; + vtab->SetFrameFlags = SetFrameFlags; + + vtab->TestActiveUnit = TestActiveUnit; + vtab->GetActiveUnit = GetActiveUnit; + vtab->SetActiveUnit = SetActiveUnit; + + vtab->GetFrameVariants = GetFrameVariants; + vtab->SetFrameVariants = SetFrameVariants; + + vtab->ClearSystem = ClearSystem; + vtab->GetSystem = GetSystem; + vtab->SetSystem = SetSystem; + vtab->TestSystem = TestSystem; + + vtab->ClearAlignSystem = ClearAlignSystem; + vtab->GetAlignSystem = GetAlignSystem; + vtab->SetAlignSystem = SetAlignSystem; + vtab->TestAlignSystem = TestAlignSystem; + + vtab->ClearTop = ClearTop; + vtab->GetTop = GetTop; + vtab->SetTop = SetTop; + vtab->TestTop = TestTop; + + vtab->ClearBottom = ClearBottom; + vtab->GetBottom = GetBottom; + vtab->SetBottom = SetBottom; + vtab->TestBottom = TestBottom; + + vtab->ClearEpoch = ClearEpoch; + vtab->GetEpoch = GetEpoch; + vtab->SetEpoch = SetEpoch; + vtab->TestEpoch = TestEpoch; + + vtab->ClearObsLat = ClearObsLat; + vtab->TestObsLat = TestObsLat; + vtab->GetObsLat = GetObsLat; + vtab->SetObsLat = SetObsLat; + + vtab->ClearObsLon = ClearObsLon; + vtab->TestObsLon = TestObsLon; + vtab->GetObsLon = GetObsLon; + vtab->SetObsLon = SetObsLon; + + vtab->ClearObsAlt = ClearObsAlt; + vtab->TestObsAlt = TestObsAlt; + vtab->GetObsAlt = GetObsAlt; + vtab->SetObsAlt = SetObsAlt; + + vtab->ClearDut1 = ClearDut1; + vtab->GetDut1 = GetDut1; + vtab->SetDut1 = SetDut1; + vtab->TestDut1 = TestDut1; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_cleanattribs = object->CleanAttribs; + object->CleanAttribs = CleanAttribs; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping = (AstMappingVtab *) vtab; + + object->Equal = Equal; + mapping->GetIsLinear = GetIsLinear; + mapping->GetIsSimple = GetIsSimple; + mapping->GetNin = GetNin; + mapping->GetNout = GetNout; + mapping->ReportPoints = ReportPoints; + mapping->Transform = Transform; + mapping->MapSplit = MapSplit; + mapping->DoNotSimplify = DoNotSimplify; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "Frame", "Coordinate system description" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void Intersect( AstFrame *this, const double a1[2], + const double a2[2], const double b1[2], + const double b2[2], double cross[2], + int *status ) { +/* +*++ +* Name: +c astIntersect +f AST_INTERSECT + +* Purpose: +* Find the point of intersection between two geodesic curves. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astIntersect( AstFrame *this, const double a1[2], +c const double a2[2], const double b1[2], +c const double b2[2], double cross[2] ) +f CALL AST_INTERSECT( THIS, A1, A2, B1, B2, CROSS, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* finds the coordinate values at the point of intersection between +* two geodesic curves. Each curve is specified by two points on +* the curve. It can only be used with 2-dimensional Frames. +* +* For example, in a basic Frame, it will find the point of +* intersection between two straight lines. But for a SkyFrame it +* will find an intersection of two great circles. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c a1 +f A1( 2 ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of the +* first point on the first geodesic curve. +c a2 +f A2( 2 ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of a +* second point on the first geodesic curve. It should not be +* co-incident with the first point. +c b1 +f B1( 2 ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of the +* first point on the second geodesic curve. +c b2 +f B2( 2 ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of a +* second point on the second geodesic curve. It should not be +* co-incident with the first point. +c cross +f CROSS( 2 ) = DOUBLE PRECISION (Returned) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* in which the coordinates of the required intersection will +* be returned. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - For SkyFrames each curve will be a great circle, and in general +* each pair of curves will intersect at two diametrically opposite +* points on the sky. The returned position is the one which is +* closest to point +c "a1". +f A1. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the two +* points defining either geodesic are co-incident, or if the two +* curves do not intersect. +c - The geodesic curve used by this function is the path of +f - The geodesic curve used by this routine is the path of +* shortest distance between two points, as defined by the +c astDistance function. +f AST_DISTANCE function. +* - An error will be reported if the Frame is not 2-dimensional. +*-- +*/ + +/* Local Variables: */ + double ca; /* Y axis intercept of line a */ + double cb; /* Y axis intercept of line b */ + double dxa; /* X range spanned by line a */ + double dxb; /* X range spanned by line b */ + double ma; /* Gradient of line a */ + double mb; /* Gradient of line b */ + int naxes; /* Number of Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialize bad values. */ + cross[ 0 ] = AST__BAD; + cross[ 1 ] = AST__BAD; + +/* Determine the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Report an error if the Frame is not 2 dimensional. */ + if( naxes != 2 && astOK ) { + astError( AST__NAXIN, "astIntersect(%s): Invalid number of Frame axes (%d)." + " astIntersect can only be used with 2 dimensonal Frames.", status, + astGetClass( this ), naxes ); + } + +/* Check that all supplied values are OK. */ + if ( ( a1[ 0 ] != AST__BAD ) && ( a1[ 1 ] != AST__BAD ) && + ( a2[ 0 ] != AST__BAD ) && ( a2[ 1 ] != AST__BAD ) && + ( b1[ 0 ] != AST__BAD ) && ( b1[ 1 ] != AST__BAD ) && + ( b2[ 0 ] != AST__BAD ) && ( b2[ 1 ] != AST__BAD ) ) { + +/* Find the x increments spanned by the two lines. */ + +/* Check the first line is not vertical. */ + dxa = a2[ 0 ] - a1[ 0 ]; + dxb = b2[ 0 ] - b1[ 0 ]; + if( dxa != 0.0 ) { + +/* Find the gradient and Y axis intercept of the first line. */ + ma = ( a2[ 1 ] - a1[ 1 ] )/dxa; + ca = a1[ 1 ] - a1[ 0 ]*ma; + +/* Check the second line is not vertical. */ + if( dxb != 0.0 ) { + +/* Find the gradient and Y axis intercept of the second line. */ + mb = ( b2[ 1 ] - b1[ 1 ] )/dxb; + cb = b1[ 1 ] - b1[ 0 ]*mb; + +/* Check the lines are not parallel. */ + if( ma != mb ) { + +/* Find the intersection of the two lines. */ + cross[ 0 ] = ( cb -ca )/( ma - mb ); + cross[ 1 ] = ( ( ma + mb )*cross[ 0 ] + ca + cb )/2; + } + +/* If the second line is vertical but the first is not. */ + } else if( b1[ 1 ] != b2[ 1 ] ){ + cross[ 0 ] = b1[ 0 ]; + cross[ 1 ] = ma*b1[ 0 ] + ca; + } + +/* First line is vertical but second is not. */ + } else if( dxb != 0.0 && a1[ 1 ] != a2[ 1 ] ){ + +/* Find the gradient and Y axis intercept of the second line. */ + mb = ( b2[ 1 ] - b1[ 1 ] )/dxb; + cb = b1[ 1 ] - b1[ 0 ]*mb; + +/* Find the intercection. */ + cross[ 0 ] = a1[ 0 ]; + cross[ 1 ] = mb*a1[ 0 ] + cb; + } + } +} + +static int IsUnitFrame( AstFrame *this, int *status ){ +/* +*+ +* Name: +* astIsUnitFrame + +* Purpose: +* Is this Frame equivalent to a UnitMap? + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astIsUnitFrame( AstFrame *this ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a flag indicating if the supplied Frame is +* equivalent to a UnitMap when treated as a Mapping (note, the Frame +* class inherits from Mapping and therefore every Frame is also a Mapping). + +* Parameters: +* this +* Pointer to the Frame. + +* Returned Value: +* A non-zero value is returned if the supplied Frame is equivalent to +* a UnitMap when treated as a Mapping. + +*- +*/ + +/* Check the local error status. */ + if( !astOK ) return 0; + +/* The base Frame class is always equivalent to a UnitMap. */ + return 1; +} + +static int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) { +/* +*+ +* Name: +* astLineContains + +* Purpose: +* Determine if a line contains a point. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astLineContains( AstFrame *this, AstLineDef *l, int def, double *point ) + +* Class Membership: +* Frame method. + +* Description: +* This function determines if the supplied point is on the supplied +* line within the supplied Frame. The start point of the line is +* considered to be within the line, but the end point is not. The tests +* are that the point of closest approach of the line to the point should +* be between the start and end, and that the distance from the point to +* the point of closest aproach should be less than 1.0E-7 of the length +* of the line. + +* Parameters: +* this +* Pointer to the Frame. +* l +* Pointer to the structure defining the line. +* def +* Should be set non-zero if the "point" array was created by a +* call to astLineCrossing (in which case it may contain extra +* information following the axis values),and zero otherwise. +* point +* Point to an array containing the axis values of the point to be +* tested, possibly followed by extra cached information (see "def"). + +* Returned Value: +* A non-zero value is returned if the line contains the point. + +* Notes: +* - The pointer supplied for "l" should have been created using the +* astLineDef method. These structures contained cached information about +* the lines which improve the efficiency of this method when many +* repeated calls are made. An error will be reported if the structure +* does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int result; + double dx, dy, p; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the line refers to the supplied Frame. */ + if( l->frame != this ) { + astError( AST__INTER, "astLineContains(%s): The supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* If the point is good, find the offsets from the start of the line. */ + } else if( point[ 0 ] != AST__BAD && point[ 1 ] != AST__BAD ) { + dx = point[ 0 ] - l->start[ 0 ]; + dy = point[ 1 ] - l->start[ 1 ]; + +/* Check the nearest point on the line is between the start and end. + Exclude the end point. */ + p = dx*l->dir[ 0 ] + dy*l->dir[ 1 ]; + if( p >= 0.0 && p < l->length ) { + +/* Check the distance from the point to the nearest point on the line is not + further than 1.0E-7 of the length of the line. */ + if( fabs( dx*l->q[ 0 ] + dy*l->q[ 1 ] ) <= 1.0E-7*l->length ) { + result = 1; + } + } + } + +/* Return zero if an error occurred. */ + if( !astOK ) result = 0; + +/* Return a pointer to the output structure. */ + return result; +} + +static int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, + double **cross, int *status ) { +/* +*+ +* Name: +* astLineCrossing + +* Purpose: +* Determine if two lines cross. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astLineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, +* double **cross ) + +* Class Membership: +* Frame method. + +* Description: +* This function determines if the two suplied line segments cross, +* and if so returns the axis values at the point where they cross. +* A flag is also returned indicating if the crossing point occurs +* within the length of both line segments, or outside one or both of +* the line segments. + +* Parameters: +* this +* Pointer to the Frame. +* l1 +* Pointer to the structure defining the first line. +* l2 +* Pointer to the structure defining the second line. +* cross +* Pointer to a location at which to put a pointer to a dynamically +* alocated array containing the axis values at the crossing. If +* NULL is supplied no such array is returned. Otherwise, the returned +* array should be freed using astFree when no longer needed. If the +* lines are parallel (i.e. do not cross) then AST__BAD is returned for +* all axis values. Note usable axis values are returned even if the +* lines cross outside the segment defined by the start and end points +* of the lines. The order of axes in the returned array will take +* account of the current axis permutation array if appropriate. Note, +* sub-classes such as SkyFrame may append extra values to the end +* of the basic frame axis values. A NULL pointer is returned if an +* error occurs. + +* Returned Value: +* A non-zero value is returned if the lines cross at a point which is +* within the [start,end) segment of the lines that are flagged as +* finite (if a line is marked as infinite any crossing is assumed to +* be within the bounds of the line). If the crossing point is outside +* this segment on either (inifinte) line, or if the lines are parallel, +* zero is returned. Note, the start point is considered to be inside +* the length of the segment, but the end point is outside. + +* Notes: +* - The pointers supplied for "l1" and "l2" should have been created +* using the astLineDef method. These structures contained cached +* information about the lines which improve the efficiency of this method +* when many repeated calls are made. An error will be reported if +* either structure does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + double *crossing; /* Returned array */ + double den; /* Denominator */ + double dx; /* Offset in start X values */ + double dy; /* Offset in start Y values */ + double t1; /* Distance from start of line 1 to crossing */ + double t2; /* Distance from start of line 2 to crossing */ + int result; /* Returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + crossing = astMalloc( sizeof(double)*2 ); + +/* Check that both lines refer to the supplied Frame. */ + if( l1->frame != this ) { + astError( AST__INTER, "astLineCrossing(%s): First supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + + } else if( l2->frame != this ) { + astError( AST__INTER, "astLineCrossing(%s): Second supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + + } else if( crossing ){ + +/* Each of the lines can be represented as "p = start + t.v" where start is + the start position, v is the unit vector pointing from start to end, + and t is the scalar distance from the start position. So to find the + intersection put "start1 + t1.v1 = start2 + t2.v2" and solve for t1 + and t2. */ + den = (l1->dir[ 0 ])*(l2->dir[ 1 ]) - (l2->dir[ 0 ])*(l1->dir[ 1 ]); + if( den != 0.0 ) { + dx = l2->start[ 0 ] - l1->start[ 0 ]; + dy = l2->start[ 1 ] - l1->start[ 1 ]; + t1 = ( l2->dir[ 1 ]*dx - l2->dir[ 0 ]*dy )/den; + t2 = ( l1->dir[ 1 ]*dx - l1->dir[ 0 ]*dy )/den; + +/* Store the crossing point, using the smaller t value to redue error. */ + if( fabs( t1 ) < fabs( t2 ) ) { + crossing[ 0 ] = l1->start[ 0 ] + t1*l1->dir[ 0 ]; + crossing[ 1 ] = l1->start[ 1 ] + t1*l1->dir[ 1 ]; + } else { + crossing[ 0 ] = l2->start[ 0 ] + t2*l2->dir[ 0 ]; + crossing[ 1 ] = l2->start[ 1 ] + t2*l2->dir[ 1 ]; + } + +/* See if the intersection is within the length of both lines (excluding + the end points). If a line is flagged as infinite, set the "t" parameter + to zero to make it look like the crossing is within the line. */ + if( l1->infinite ) t1 = 0.0; + if( l2->infinite ) t2 = 0.0; + + if( t1 >= 0.0 && t1 < l1->length && + t2 >= 0.0 && t2 < l2->length ) result = 1; + + } else { + crossing[ 0 ] = AST__BAD; + crossing[ 1 ] = AST__BAD; + } + } + +/* Return zero if an error occurred. */ + if( !astOK ) { + crossing = astFree( crossing ); + result = 0; + } + +/* Return the crossing pointer. */ + if( cross ) { + *cross = crossing; + } else if( crossing ){ + crossing = astFree( crossing ); + } + +/* Return a pointer to the output structure. */ + return result; +} + +static AstLineDef *LineDef( AstFrame *this, const double start[2], + const double end[2], int *status ) { +/* +*+ +* Name: +* astLineDef + +* Purpose: +* Creates a structure describing a line segment in a 2D Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstLineDef *astLineDef( AstFrame *this, const double start[2], +* const double end[2] ) + +* Class Membership: +* Frame method. + +* Description: +* This function creates a structure containing information describing a +* given line segment within the supplied 2D Frame. This may include +* information which allows other methods such as astLineCrossing to +* function more efficiently. Thus the returned structure acts as a +* cache to store intermediate values used by these other methods. + +* Parameters: +* this +* Pointer to the Frame. Must have 2 axes. +* start +* An array of 2 doubles marking the start of the line segment. +* end +* An array of 2 doubles marking the end of the line segment. + +* Returned Value: +* Pointer to the memory structure containing the description of the +* line. This structure should be freed using astFree when no longer +* needed. A NULL pointer is returned (without error) if any of the +* supplied axis values are AST__BAD. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstLineDef *result; /* Pointer to output structure */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check the Frame has 2 axes. */ + if( astGetNaxes( this ) != 2 ) { + astError( AST__INTER, "astLineDef(%s): The supplied %s is not 2 " + "dimensional (internal AST proramming error).", status, + astGetClass( this ), astGetClass( this ) ); + } + +/* Check the axis values are good */ + if( start[ 0 ] != AST__BAD && start[ 1 ] != AST__BAD && + end[ 0 ] != AST__BAD && end[ 1 ] != AST__BAD ) { + +/* Allocate memory for the returned structure. */ + result = astMalloc( sizeof( AstLineDef ) ); + if( result ) { + +/* Store the supplied axis values in the returned structure. */ + result->start[ 0 ] = start[ 0 ]; + result->start[ 1 ] = start[ 1 ]; + result->end[ 0 ] = end[ 0 ]; + result->end[ 1 ] = end[ 1 ]; + +/* Store the length of the line segment. */ + result->length = astDistance( this, start, end ); + +/* Store a unit vector pointing from the start to the end. */ + if( result->length > 0.0 ) { + result->dir[ 0 ] = ( end[ 0 ] - start[ 0 ] )/result->length; + result->dir[ 1 ] = ( end[ 1 ] - start[ 1 ] )/result->length; + } else { + result->dir[ 0 ] = 1.0; + result->dir[ 1 ] = 0.0; + } + +/* Store a unit vector perpendicular to the line, such that the vector + points to the left, as vewied from the observer, when moving from the + start to the end of the line. */ + result->q[ 0 ] = -result->dir[ 1 ]; + result->q[ 1 ] = result->dir[ 0 ]; + +/* Store a pointer to the defining Frame. */ + result->frame = this; + +/* Indicate that the line is considered to be terminated at the start and + end points. */ + result->infinite = 0; + } + } + +/* Free the returned pointer if an error occurred. */ + if( !astOK ) result = astFree( result ); + +/* Return a pointer to the output structure. */ + return result; +} + +static void LineOffset( AstFrame *this, AstLineDef *line, double par, + double prp, double point[2], int *status ){ +/* +*+ +* Name: +* astLineOffset + +* Purpose: +* Find a position close to a line. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void LineOffset( AstFrame *this, AstLineDef *line, double par, +* double prp, double point[2] ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns a position formed by moving a given distance along +* the supplied line, and then a given distance away from the supplied line. + +* Parameters: +* this +* Pointer to the Frame. +* line +* Pointer to the structure defining the line. +* par +* The distance to move along the line from the start towards the end. +* prp +* The distance to move at right angles to the line. Positive +* values result in movement to the left of the line, as seen from +* the observer, when moving from start towards the end. + +* Notes: +* - The pointer supplied for "line" should have been created using the +* astLineDef method. This structure contains cached information about the +* line which improves the efficiency of this method when many repeated +* calls are made. An error will be reported if the structure does not +* refer to the Frame specified by "this". +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check that the line refers to the supplied Frame. */ + if( line->frame != this ) { + astError( AST__INTER, "astLineOffset(%s): The supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* This implementation uses simple flat geometry. */ + } else { + point[ 0 ] = line->start[ 0 ] + par*line->dir[ 0 ] + prp*line->q[ 0 ]; + point[ 1 ] = line->start[ 1 ] + par*line->dir[ 1 ] + prp*line->q[ 1 ]; + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstFrame *this; /* Pointer to Frame structure */ + int i; /* Loop count */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the Frame structure. */ + this = (AstFrame *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + for( i = 0; i < this->naxes; i++ ) { + if( !result ) result = astManageLock( this->axis[ i ], mode, extra, + fail ); + } + if( this->variants && !result ) result = astManageLock( this->variants, mode, + extra, fail ); + + return result; + +} +#endif + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* Frame method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing Frame. This is only possible if the specified inputs +* correspond to some subset of the Frame outputs. That is, there +* must exist a subset of the Frame outputs for which each output +* depends only on the selected Frame inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied Frame, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the Frame to be split (the Frame is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied Frame, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied Frame has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied Frame. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + int *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Pick the selected axes from the Frame. */ + *map = (AstMapping *) astPickAxes( (AstFrame *) this_map, nin, in, NULL ); + +/* Return a copy of the supplied axis array.*/ + result = astStore( NULL, in, sizeof( int )*(size_t) nin ); + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static int Match( AstFrame *template, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +*+ +* Name: +* astMatch + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astMatch( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result ) + +* Class Membership: +* Frame method. + +* Description: +* This function matches a "template" frame to a "target" frame and +* determines whether it is possible to convert coordinates between +* them. If it is, a mapping that performs the transformation is +* returned along with a new Frame that describes the coordinate +* system that results when this mapping is applied to the "target" +* coordinate system. In addition, information is returned to allow +* the axes in this "result" Frame to be associated with the +* corresponding axes in the "target" and "template" Frames from +* which they are derived. + +* Parameters: +* template +* Pointer to the template Frame. This describes the coordinate +* system (or set of possible coordinate systems) into which we +* wish to convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate +* system in which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case (i.e. if the +* target is of a more specialised class than the template). In +* this latter case, the target is cast down to the class of the +* template. NOTE, this argument is handled by the global method +* wrapper function "astMatch_", rather than by the class-specific +* implementations of this method. +* template_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* template Frame axis from which it is derived. If it is not +* derived from any template frame axis, a value of -1 will be +* returned instead. +* target_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* target Frame axis from which it is derived. If it is not +* derived from any target Frame axis, a value of -1 will be +* returned instead. +* map +* Address of a location where a pointer to a new Mapping will +* be returned if the requested coordinate conversion is +* possible. If returned, the forward transformation of this +* Mapping may be used to convert coordinates between the +* "target" Frame and the "result" Frame (see below) and the +* inverse transformation will convert in the opposite +* direction. +* result +* Address of a location where a pointer to a new Frame will be +* returned if the requested coordinate conversion is +* possible. If returned, this Frame describes the coordinate +* system that results from applying the returned Mapping +* (above) to the "target" coordinate system. In general, this +* Frame will combine attributes from (and will therefore be +* more specific than) both the target and the template +* Frames. In particular, when the template allows the +* possibility of transformaing to any one of a set of +* alternative coordinate systems, the "result" Frame will +* indicate which of the alternatives was used. + +* Returned Value: +* A non-zero value is returned if the requested coordinate +* conversion is possible. Otherwise zero is returned (this will +* not in itself result in an error condition). + +* Notes: +* - By default, the "result" frame will have its number of axes +* and axis order determined by the "template" Frame. However, if +* the PreserveAxes attribute of the template frame is non-zero, +* then the axis count and axis order of the "target" frame will be +* used instead. +* - The template_axes and target_axes arrays are provided so that +* if the caller needs to permute the target and/or template axes +* before invoking this function, it is possible to deduce how the +* result axes should be permuted so as to correspond with the +* original template/target axis order. +* - For result axes that do not correspond with a template and/or +* target axis (where a value of -1 is returned in the +* template_axes and/or target_axes arrays), the caller has no +* clear way of knowing where these axes should appear in any +* permuted order. In this case, the relative position of these +* axes within the result Frame (with respect to axes that do have +* template/target axis associations) will be used to convey this +* information. Such axes should be taken to be associated either +* with the next preceding or following axis (depending on the +* AST__MATCHEND flag of the template frame) which does have an +* association. +* - If the result Frame has zero axes, then NULL pointer values +* will be returned for *template_axes and *target_axes. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* This implementation addresses the matching of a Frame class +* object to other types of Frames (i.e. the target may be from a +* derived class). A Frame will match any other type of Frame with +* an acceptable number of axes but will not distinguish axis order +* (i.e. it will match the axes in whatever order they are +* given). If the template Frame has a value set for its Domain +* attribute, then it will only match another Frame with the same +* Domain. +*/ + +/* Local Variables: */ + char *template_domain; /* Pointer to copy of template domain */ + const char *ptr; /* Pointer to domain string */ + const char *target_domain; /* Pointer to target domain string */ + int match; /* Template matches target? */ + int match_end; /* Match final axes of target? */ + int max_axes; /* Maximum acceptable number of axes */ + int min_axes; /* Minimum acceptable nu,ber of axes */ + int preserve_axes; /* Preserve target axes? */ + int result_axis; /* Loop counter for result axes */ + int result_naxes; /* Number of result axes */ + int target_naxes; /* Number of target axes */ + int template_naxes; /* Number of template axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* The first requirement for a match is that the target object is a Frame. This + is already known to be true, as it forms part of the argument validation + for this function. */ + +/* The second requirement is that the number of target axes is acceptable. + Obtain the number of target axes and the minimum and maximum number of axes + that the template will match. */ + target_naxes = astGetNaxes( target ); + min_axes = astGetMinAxes( template ); + max_axes = astGetMaxAxes( template ); + +/* Test if the number of target axes is acceptable. */ + if ( astOK ) { + match = ( ( target_naxes >= min_axes ) && ( target_naxes <= max_axes ) ); + } + +/* The third requirement is that if the template has its Domain + attribute defined, then the target must also have the same Domain + (although it need not be set - the default will do). First check if + the template has a domain. */ + if ( astOK && match ) { + if ( astTestDomain( template ) ) { + +/* Obtain a pointer to the template domain. Then allocate memory and + make a copy of it (this is necessary as we will next inquire the + domain of the target and may over-write the buffer holding the + template's domain). */ + ptr = astGetDomain( template ); + if ( astOK ) { + template_domain = astStore( NULL, ptr, + strlen( ptr ) + (size_t) 1 ); + +/* Obtain a pointer to the target domain. */ + target_domain = astGetDomain( target ); + +/* Compare the domain strings for equality. Then free the memory + allocated above. */ + match = astOK && !strcmp( template_domain, target_domain ); + template_domain = astFree( template_domain ); + } + } + } + +/* If the template matches, obtain the values of the template's PreserveAxes + and MatchEnd attributes and determine the number of template axes. */ + if ( astOK && match ) { + preserve_axes = astGetPreserveAxes( template ); + match_end = astGetMatchEnd( template ); + template_naxes = astGetNaxes( template ); + +/* If the PreserveAxes attribute is non-zero, the target axes should be + preserved, so the number of result axes equals the number of target axes. + Otherwise the number of template axes is used. */ + result_naxes = preserve_axes ? target_naxes : template_naxes; + +/* Allocate memory for the arrays of axis associations to be returned. */ + *template_axes = astMalloc( sizeof( int ) * (size_t) result_naxes ); + *target_axes = astMalloc( sizeof( int ) * (size_t) result_naxes ); + if ( astOK ) { + +/* Loop through each of the result axes. */ + for ( result_axis = 0; result_axis < result_naxes; result_axis++ ) { + +/* Set up the axis associations. By default, associate the first result axis + with the first template/target axis. */ + (*template_axes)[ result_axis ] = result_axis; + (*target_axes)[ result_axis ] = result_axis; + +/* However, if the MatchEnd attribute is non-zero, associate the last result + axis with the last template/target axis (this only makes a difference if + there is a difference in the number of axes). */ + if ( match_end ) { + (*template_axes)[ result_axis ] += + template_naxes - result_naxes; + (*target_axes)[ result_axis ] += target_naxes - result_naxes; + } + +/* If any of the associations would be with a template/target axis that doesn't + exist, then use an axis index of -1 for the association instead. */ + if ( ( (*template_axes)[ result_axis ] < 0 ) || + ( (*template_axes)[ result_axis ] >= template_naxes ) ) { + (*template_axes)[ result_axis ] = -1; + } + if ( ( (*target_axes)[ result_axis ] < 0 ) || + ( (*target_axes)[ result_axis ] >= target_naxes ) ) { + (*target_axes)[ result_axis ] = -1; + } + } + +/* Use the target's astSubFrame method to select the required axes from it, + overlaying the template's attributes on to the resulting Frame. This process + also generates the required Mapping between the target and result Frames. */ + match = astSubFrame( target, template, + result_naxes, *target_axes, *template_axes, + map, result ); + } + } + +/* If an error occurred, free any allocated memory and reset the result. */ + if ( !astOK || !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void MatchAxes( AstFrame *frm1, AstFrame *frm2, int *axes, + int *status ) { +/* +*++ +* Name: +c astMatchAxes +f AST_MATCHAXES + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astMatchAxes( AstFrame *frm1, AstFrame *frm2, int *axes ) +f CALL AST_MATCHAXES( FRM1, FRM2, AXES, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +c frm1 +f FRM1 = INTEGER (Given) +* Pointer to the first Frame. +c frm2 +f FRM2 = INTEGER (Given) +* Pointer to the second Frame. +c axes +f AXES = INTEGER( * ) (Returned) +c Pointer to an +f An +* integer array in which to return the indices of the axes (within +* the first Frame) that correspond to each axis within the second +* Frame. Axis indices start at 1. A value of zero will be stored +* in the returned array for each axis in the second Frame that has +* no corresponding axis in the first Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the second Frame. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Frame +* This function applies to all Frames. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping can +* be found between them using +c astFindFrame or astConvert. +f AST_FINDFRAME or AST_CONVERT. +* Thus, "corresponding axes" are not necessarily identical. For +* instance, SkyFrame axes in two Frames will match even if they +* describe different celestial coordinate systems +*-- + +* Implementation Notes: +* This function is simply a wrap-up for the protected astMatchAxesX +* method which performs the required processing but swaps the order +* of the first two arguments. This is a trick to allow the +* astMatchAxes method to be over-ridden by derived classes on the +* basis of the class of either of the first two arguments. +* +* In practice, each class that represents an encapsulated Frame (e.g. +* FrameSet, Region, etc) should over-ride this method, extracting a +* Frame from the supplied "frm1" pointer, and then invoking +* astMatchAxesX. + +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the "astMatchAxesX" method with the first two arguments + swapped. */ + astMatchAxesX( frm2, frm1, axes ); +} + +static void MatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes, + int *status ) { +/* +*+ +* Name: +* astMatchAxesX + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astMatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes ) + +* Class Membership: +* Frame method. + +* Description: +* This function performs the processing for the public astMatchAxes +* method and has exactly the same interface except that the order +* of the first two arguments is swapped. This is a trick to allow +* the astMatchAxes method to be over-ridden by derived classes on +* the basis of the class of either of its first two arguments. +* +* See the astMatchAxes method for details of the interface. +*- +*/ + +/* Local Variables: */ + AstFrame *pfrm; + AstFrame *resfrm; + AstMapping *resmap; + int *frm1_axes; + int *pfrm_axes; + int ifirst; + int max_axes; + int min_axes; + int nax2; + int pax; + int preserve_axes; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Temporarily ensure that the PreserveAxes attribute is non-zero in + the second supplied Frame. This means thte result Frame returned by + astMatch below will have the axis count and order of the target Frame + (i.e. "pfrm"). */ + if( astTestPreserveAxes( frm1 ) ) { + preserve_axes = astGetPreserveAxes( frm1 ) ? 1 : 0; + } else { + preserve_axes = -1; + } + astSetPreserveAxes( frm1, 1 ); + +/* Temporarily ensure that the MaxAxes and MinAxes attributes in the + second supplied Frame are set so the Frame can be used as a template + in astMatch for matching any number of axes. */ + if( astTestMaxAxes( frm1 ) ) { + max_axes = astGetMaxAxes( frm1 ); + } else { + max_axes = -1; + } + astSetMaxAxes( frm1, 10000 ); + + if( astTestMinAxes( frm1 ) ) { + min_axes = astGetMinAxes( frm1 ); + } else { + min_axes = -1; + } + astSetMinAxes( frm1, 1 ); + +/* Get the number of axes in the frm2 Frame. */ + nax2 = astGetNaxes( frm2 ); + +/* Loop round the axes in the frm2 Frame. */ + for( ifirst = 0; ifirst < nax2; ifirst++ ) { + +/* Identify the primary Frame defining the current axis in the frm2 + Frame. */ + astPrimaryFrame( frm2, ifirst, &pfrm, &pax ); + +/* Attempt to find a sub-frame within the frm1 Frame that corresponds to + this primary Frame. */ + if( astMatch( frm1, pfrm, 1, &frm1_axes, &pfrm_axes, &resmap, &resfrm ) ) { + +/* Store the one-based index within "frm1" of the corresponding axis. */ + axes[ ifirst ] = frm1_axes[ pax ] + 1; + +/* Free resources */ + frm1_axes = astFree( frm1_axes ); + pfrm_axes = astFree( pfrm_axes ); + resmap = astAnnul( resmap ); + resfrm = astAnnul( resfrm ); + +/* If no corresponding axis was found store zero in the returned array. */ + } else { + axes[ ifirst ] = 0; + } + +/* Free resouces. */ + pfrm = astAnnul( pfrm ); + } + +/* Re-instate the original attribute values in the frm1 Frame. */ + if( preserve_axes == -1 ) { + astClearPreserveAxes( frm1 ); + } else { + astSetPreserveAxes( frm1, preserve_axes ); + } + + if( max_axes == -1 ) { + astClearMaxAxes( frm1 ); + } else { + astSetMaxAxes( frm1, max_axes ); + } + + if( min_axes == -1 ) { + astClearMinAxes( frm1 ); + } else { + astSetMinAxes( frm1, min_axes ); + } +} + +static void NewUnit( AstAxis *ax, const char *old_units, const char *new_units, + const char *method, const char *class, int *status ) { +/* +* Name: +* NewUnit + +* Purpose: +* Modify an Axis Label and Symbol to reflect a new Unit value. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* void NewUnit( AstAxis *ax, const char *old_units, const char *new_units, +* const char *method, const char *class ) + +* Class Membership: +* Frame method. + +* Description: +* This function modifies the Label and Symbol attributes of an Axis +* to reflect a new Unit value. This function should only be called if +* the ActiveUnit flag of the parent Frame is non-zero (this is not +* checked within this function). +* +* If the axis has a set label, then we may be able to modify it to +* correctly describe the axis in the supplied new units. For instance, +* if the original units were "Hz", the original label was "frequency", +* and the new units are "log(Hz)", then the label is modified to become +* "log( frequency )". +* +* The Axis Format attribute is cleared if the supplied units are +* different to the old units (because any set format is probably not +* going to be appropriate for a new system of units. + +* Parameters: +* ax +* Pointer to the Axis. +* old_units +* The original units value. +* new_units +* The new units value. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis selection. This method name is used +* solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string +* containing the name of the class upon which this function +* was invoked. This is used solely for constructing error messages. + +* Returned Value: +* void. +*/ + +/* Local Variables: */ + AstMapping *map; /* Pointer to units Mapping */ + char *new_lab; /* Pointer to new axis label */ + char *new_sym; /* Pointer to new axis symbol */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check that the axis label is set. We relay on sub-classes to return + appropriate default labels if the label is not set. */ + if( astTestAxisLabel( ax ) ) { + +/* See if it is possible to map the old units into the new units. + If it is, then a Mapping is returned together with an appropriately + modified label. */ + map = astUnitMapper( old_units, new_units, astGetAxisLabel( ax ), + &new_lab ); + +/* If succesfull, annul the Mapping (which we do not need), and store the + modified label in the Axis, finally freeing the memory used to hold + the modified label. */ + if( map ) { + map = astAnnul( map ); + if( new_lab ) { + astSetAxisLabel( ax, new_lab ); + new_lab = astFree( new_lab ); + } + } + } + +/* Do the same for the axis symbol. */ + if( astTestAxisSymbol( ax ) ) { + map = astUnitMapper( old_units, new_units, astGetAxisSymbol( ax ), + &new_sym ); + if( map ) { + map = astAnnul( map ); + if( new_sym ) { + astSetAxisSymbol( ax, new_sym ); + new_sym = astFree( new_sym ); + } + } + } + +/* If succesful, clear the axis format if the new and old units are + different. */ + if( astOK ) { + if( strcmp( old_units, new_units ) ) astClearAxisFormat( ax ); + } + +} + +static void Norm( AstFrame *this, double value[], int *status ) { +/* +*++ +* Name: +c astNorm +f AST_NORM + +* Purpose: +* Normalise a set of Frame coordinates. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astNorm( AstFrame *this, double value[] ) +f CALL AST_NORM( THIS, VALUE, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function normalises a set of Frame coordinate values which +f This routine normalises a set of Frame coordinate values which +* might be unsuitable for display (e.g. may lie outside the +* expected range) into a set of acceptable values suitable for +* display. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c value +f VALUE( * ) = DOUBLE PRECISION (Given and Returned) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* coordinate values representing a point in the space which the +* Frame describes. If these values lie outside the expected +* range for the Frame, they will be replaced with more +* acceptable (normalised) values. Otherwise, they will be +* returned unchanged. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - For some classes of Frame, whose coordinate values are not +* constrained, this function will never modify the values +* supplied. However, for Frames whose axes represent cyclic +* quantities (such as angles or positions on the sky), coordinates +* will typically be wrapped into an appropriate standard range, +* such as zero to 2*pi. +* - The NormMap class is a Mapping which can be used to normalise a +* set of points using the +c astNorm function +f AST_NORM routine +* of a specified Frame. +* - It is intended to be possible to put any set of coordinates +* into a form suitable for display by using this function to +* normalise them, followed by appropriate formatting +c (using astFormat). +f (using AST_FORMAT). +*-- +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Loop to process the coordinate for each axis in turn. */ + for ( axis = 0; axis < naxes; axis++ ) { + +/* Obtain a pointer to the relevant Frame Axis. */ + ax = astGetAxis( this, axis ); + +/* Normalise the coordinate for this axis. */ + astAxisNorm( ax, value + axis ); + +/* Annul the pointer to the Axis. */ + ax = astAnnul( ax ); + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } +} + +static void NormBox( AstFrame *this, double lbnd[], double ubnd[], + AstMapping *reg, int *status ) { +/* +*+ +* Name: +* astNormBox + +* Purpose: +* Extend a box to include effect of any singularities in the Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astNormBox( AstFrame *this, double lbnd[], double ubnd[], +* AstMapping *reg ) + +* Class Membership: +* Frame method. + +* Description: +* This function modifies a supplied box to include the effect of any +* singularities in the co-ordinate system represented by the Frame. +* For a normal Cartesian coordinate system, the box will be returned +* unchanged. Other classes of Frame may do other things. For instance, +* a SkyFrame will check to see if the box contains either the north +* or south pole and extend the box appropriately. + +* Parameters: +* this +* Pointer to the Frame. +* lbnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* lower axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* ubnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* upper axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* reg +* A Mapping which should be used to test if any singular points are +* inside or outside the box. The Mapping should leave an input +* position unchanged if the point is inside the box, and should +* set all bad if the point is outside the box. +*- +*/ + +/* This base class returns the box limits unchanged. */ +} + +static double Offset2( AstFrame *this, const double point1[2], double angle, + double offset, double point2[2], int *status ){ +/* +*++ +* Name: +c astOffset2 +f AST_OFFSET2 + +* Purpose: +* Calculate an offset along a geodesic curve in a 2D Frame. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c double astOffset2( AstFrame *this, const double point1[2], double angle, +c double offset, double point2[2] ); +f RESULT = AST_OFFSET2( THIS, POINT1, ANGLE, OFFSET, POINT2, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function finds the Frame coordinate values of a point which +f This routine finds the Frame coordinate values of a point which +* is offset a specified distance along the geodesic curve at a +* given angle from a specified starting point. It can only be +* used with 2-dimensional Frames. +* +* For example, in a basic Frame, this offset will be along the +* straight line joining two points. For a more specialised Frame +* describing a sky coordinate system, however, it would be along +* the great circle passing through two sky positions. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c point1 +f POINT1( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of the +* point marking the start of the geodesic curve. +c angle +f ANGLE = DOUBLE PRECISION (Given) +* The angle (in radians) from the positive direction of the second +* axis, to the direction of the required position, as seen from +* the starting position. Positive rotation is in the sense of +* rotation from the positive direction of axis 2 to the positive +* direction of axis 1. +c offset +f OFFSET = DOUBLE PRECISION +* The required offset from the first point along the geodesic +* curve. If this is positive, it will be in the direction of the +* given angle. If it is negative, it will be in the opposite +* direction. +c point2 +f POINT2( * ) = DOUBLE PRECISION (Returned) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* in which the coordinates of the required point will be returned. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astOffset2 +f AST_OFFSET2 = DOUBLE PRECISION +* The direction of the geodesic curve at the end point. That is, the +* angle (in radians) between the positive direction of the second +* axis and the continuation of the geodesic curve at the requested +* end point. Positive rotation is in the sense of rotation from +* the positive direction of axis 2 to the positive direction of axis +* 1. + +* Notes: +c - The geodesic curve used by this function is the path of +f - The geodesic curve used by this routine is the path of +* shortest distance between two points, as defined by the +c astDistance function. +f AST_DISTANCE function. +* - An error will be reported if the Frame is not 2-dimensional. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +*-- +*/ + +/* Local Variables: */ + int naxes; /* Number of Frame axes */ + double result; /* Returned value */ + +/* Check the global error status. */ + result = AST__BAD; + if ( !astOK ) return result; + +/* Initialize bad values. */ + point2[ 0 ] = AST__BAD; + point2[ 1 ] = AST__BAD; + +/* Determine the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Report an error if the Frame is not 2 dimensional. */ + if( naxes != 2 && astOK ) { + astError( AST__NAXIN, "astOffset2(%s): Invalid number of Frame axes (%d)." + " astOffset2 can only be used with 2 dimensonal Frames.", status, + astGetClass( this ), naxes ); + } + +/* Check the supplied values. */ + if ( astOK && point1[ 0 ] != AST__BAD && point1[ 1 ] != AST__BAD && + angle != AST__BAD && offset != AST__BAD ) { + +/* Store the results. */ + point2[ 0 ] = point1[ 0 ] + sin( angle )*offset; + point2[ 1 ] = point1[ 1 ] + cos( angle )*offset; + +/* The position angle of the curve does not vary in cartesian coordinates */ + result = angle; + + } + +/* Return the result. */ + return result; + +} + +static void Offset( AstFrame *this, const double point1[], + const double point2[], double offset, double point3[], int *status ) { +/* +*++ +* Name: +c astOffset +f AST_OFFSET + +* Purpose: +* Calculate an offset along a geodesic curve. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astOffset( AstFrame *this, +c const double point1[], const double point2[], +c double offset, double point3[] ) +f CALL AST_OFFSET( THIS, POINT1, POINT2, OFFSET, POINT3, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function finds the Frame coordinate values of a point which +f This routine finds the Frame coordinate values of a point which +* is offset a specified distance along the geodesic curve between +* two other points. +* +* For example, in a basic Frame, this offset will be along the +* straight line joining two points. For a more specialised Frame +* describing a sky coordinate system, however, it would be along +* the great circle passing through two sky positions. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c point1 +f POINT1( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of the +* point marking the start of the geodesic curve. +c point2 +f POINT2( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis. +* This should contain the coordinates of the point marking the +* end of the geodesic curve. +c offset +f OFFSET = DOUBLE PRECISION +* The required offset from the first point along the geodesic +* curve. If this is positive, it will be towards the second +* point. If it is negative, it will be in the opposite +* direction. This offset need not imply a position lying +* between the two points given, as the curve will be +* extrapolated if necessary. +c point3 +f POINT3( * ) = DOUBLE PRECISION (Returned) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* in which the coordinates of the required point will be returned. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - The geodesic curve used by this function is the path of +f - The geodesic curve used by this routine is the path of +* shortest distance between two points, as defined by the +c astDistance function. +f AST_DISTANCE function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - "Bad" coordinate values will also be returned if the two +* points supplied are coincident (or otherwise fail to uniquely +* specify a geodesic curve) but the requested offset is non-zero. +*-- +*/ + +/* Local Variables: */ + double delta; /* Displacement along axis */ + double dist; /* Distance between points */ + double fract; /* Fraction of distance required */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Determine the number of Frame axes. */ + naxes = astGetNaxes( this ); + if ( astOK ) { + +/* Loop to determine the Cartesian distance between points 1 and 2. */ + dist = 0.0; + for ( axis = 0; axis < naxes; axis++ ) { + +/* If any of the coordinates supplied is bad, set the distance to be + bad and quit looping. */ + if ( ( point1[ axis ] == AST__BAD ) || + ( point2[ axis ] == AST__BAD ) ) { + dist = AST__BAD; + break; + +/* Otherwise, accumulate the sum of squared displacements along each + axis. */ + } else { + delta = point1[ axis ] - point2[ axis ]; + dist += ( delta * delta ); + } + } + +/* Take the square root to find the distance (if valid). */ + if ( dist != AST__BAD ) dist = sqrt( dist ); + +/* If the distance between the points cannot be found, or the distance + is zero but the required offset is non-zero, then set the result + coordinates to be bad. */ + if ( ( dist == AST__BAD ) || + ( ( dist == 0.0 ) && ( offset != 0.0 ) ) ) { + for ( axis = 0; axis < naxes; axis++ ) { + point3[ axis ] = AST__BAD; + } + +/* Otherwise, calculate what fraction of the distance between the + points we need to move, and apply this fraction of the displacement + along each axis. */ + } else { + fract = ( dist == 0.0 ) ? 0.0 : offset / dist; + for ( axis = 0; axis < naxes; axis++ ) { + point3[ axis ] = point1[ axis ] + + fract * ( point2[ axis ] - point1[ axis ] ); + } + } + } +} + +static void Overlay( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { +/* +*+ +* Name: +* astOverlay + +* Purpose: +* Overlay the attributes of a template Frame on to another Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astOverlay( AstFrame *template, const int *template_axes, +* AstFrame *result ) + +* Class Membership: +* Frame method. + +* Description: +* This function overlays attributes of one Frame (the "template") on to +* another Frame, so as to over-ride selected attributes of that second +* Frame. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which a Frame acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. + +* Parameters: +* template +* Pointer to the template Frame, for which values should have been +* explicitly set for any attribute which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of the +* "result" Frame (see below). For each axis in the result frame, the +* corresponding element of this array should contain the (zero-based) +* index of the template axis to which it corresponds. This array is used +* to establish from which template axis any axis-dependent attributes +* should be obtained. +* +* If any axis in the result Frame is not associated with a template +* axis, the corresponding element of this array should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +*- +*/ + +/* Local Variables: */ + AstAxis *result_ax; /* Pointer to result Axis object */ + AstAxis *template_ax; /* Pointer to template Axis object */ + AstSystemType sys; /* System value */ + int result_axis; /* Loop counter for result Frame axes */ + int result_naxes; /* Number of result Frame axes */ + int template_axis; /* Index of template Frame axis */ + int template_naxes; /* Number of template Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Define a macro that tests whether an attribute is set in the template and, + if so, transfers its value to the target. */ +#define OVERLAY(attribute) \ + if ( astTest##attribute( template ) ) { \ + astSet##attribute( result, astGet##attribute( template ) ); \ + } + +/* Use the macro to transfer each Frame attribute in turn. */ + OVERLAY(Dut1); + OVERLAY(Digits); + OVERLAY(Domain); + OVERLAY(Epoch); + OVERLAY(Title); + OVERLAY(ObsLat) + OVERLAY(ObsLon) + OVERLAY(ObsAlt) + +/* Transfer the ActiveUnit flag. */ + astSetActiveUnit( result, astGetActiveUnit( template ) ); + +/* Only overlay the System and AlignSystem attribute if the values are + valid for the result class. */ + if( astTestSystem( template ) ) { + sys = astGetSystem( template ); + if( astValidateSystem( result, sys, "astOverlay" ) ) { + astSetSystem( result, sys ); + } + } + + if( astTestAlignSystem( template ) ) { + sys = astGetAlignSystem( template ); + if( astValidateSystem( result, sys, "astOverlay" ) ) { + astSetAlignSystem( result, sys ); + } + } + +/* Now transfer attributes associated with individual axes. Obtain the number + of axes in the template and result Frames. */ + template_naxes = astGetNaxes( template ); + result_naxes = astGetNaxes( result ); + if ( astOK ) { + +/* Loop through all the axes in the result Frame and determine to which + template axis each one corresponds. Check that the resulting axis index is + valid. If not, then the axis will not receive new attributes. */ + for ( result_axis = 0; result_axis < result_naxes; result_axis++ ) { + template_axis = template_axes ? template_axes[ result_axis ] : result_axis; + if ( ( template_axis >= 0 ) && ( template_axis < template_naxes ) ) { + +/* Obtain pointers to the relevant Axis objects of each Frame and use the + astAxisOverlay method of the template Axis to overlay attributes on to + the result Axis. Annul the Axis pointers afterwards. */ + template_ax = astGetAxis( template, template_axis ); + result_ax = astGetAxis( result, result_axis ); + astAxisOverlay( template_ax, result_ax ); + template_ax = astAnnul( template_ax ); + result_ax = astAnnul( result_ax ); + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + } + +/* Undefine macros local to this function. */ +#undef OVERLAY +} + +static void PermAxes( AstFrame *this, const int perm[], int *status ) { +/* +*+ +* Name: +* astPermAxes + +* Purpose: +* Permute the order of a Frame's axes. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astPermAxes( AstFrame *this, const int perm[] ) + +* Class Membership: +* Frame method. + +* Description: +* This function permutes the order in which a Frame's axes occur. + +* Parameters: +* this +* Pointer to the Frame. +* perm +* An array of int (with one element for each axis of the Frame) +* which lists the axes in their new order. Each element of this +* array should be a (zero-based) axis index identifying the +* axes according to their old (un-permuted) order. + +* Notes: +* - Only genuine permutations of the axis order are permitted, so +* each axis must be referenced exactly once in the "perm" array. +* - If more than one axis permutation is applied to a Frame, the +* effects are cumulative. +*- + +* Implementation Notes: +* - This function implements the basic astPermAxes method which is +* available via the protected interface to the Frame class. A +* public interface to this method is provided by the +* astPermAxesId_ function. +*/ + +/* Local Variables: */ + int *old; /* Pointer to copy of old permutation array */ + int axis; /* Loop counter for Frame axes */ + int naxes; /* Number of Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the permutation array, to check that it describes a genuine + permutation. */ + astCheckPerm( this, perm, "astPermAxes" ); + +/* Obtain the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Allocate memory and use it to store a copy of the old permutation array for + the Frame. */ + old = astStore( NULL, this->perm, sizeof( int ) * (size_t) naxes ); + +/* Apply the new axis permutation cumulatively to the old one and store the + result in the Frame. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + this->perm[ axis ] = old[ perm[ axis ] ]; + } + } + +/* Free the temporary copy of the old array. */ + old = astFree( old ); +} + +static AstFrame *PickAxes( AstFrame *this, int naxes, const int axes[], + AstMapping **map, int *status ) { +/* +*+ +* Name: +* astPickAxes + +* Purpose: +* Create a new Frame by picking axes from an existing one. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstFrame *PickAxes( AstFrame *this, int naxes, const int axes[], +* AstMapping **map ) + +* Class Membership: +* Frame method. + +* Description: +* This function creates a new Frame whose axes are copies of axes +* picked from an existing Frame. Other Frame attributes are also +* copied to the new Frame. Zero or more of the original axes may +* be picked in any order, but each can be used only +* once. Additional axes (with default characteristics) may be +* included in the new Frame if required. +* +* Optionally, a Mapping that converts between the original Frame's +* axes and those of the new Frame may also be returned. + +* Parameters: +* this +* Pointer to the original Frame. +* naxes +* The number of axes required in the new Frame. +* axes +* Pointer to an array of int with naxes elements. This should +* contain (zero based) axis indices specifying the axes which +* are to be included in the new Frame, in the order +* required. Each axis index may occur only once. +* +* If additional (default) axes are also to be included, the +* corresponding elements of this array should be set to -1. +* map +* Address of a location to receive a pointer to a new +* Mapping. This will be a PermMap (or a UnitMap as a special +* case) that describes the axis permutation that has taken +* place between the original and new Frames. The forward +* transformation will convert from the original Frame's axes to +* the new one's, and vice versa. +* +* If this Mapping is not required, a NULL value may be supplied +* for this parameter. + +* Returned Value: +* Pointer to the new Frame. + +* Notes: +* - The class of object returned may differ from that of the +* original Frame, depending on which axes are selected. For +* example, if a single axis is picked from a SkyFrame (which +* always has two axes), the resulting Frame cannot be a valid +* SkyFrame, so will revert to the parent class (Frame) instead. +* - The new Frame contains a deep copy of all the data selected +* from the original Frame. Modifying the new Frame will therefore +* not affect the original one. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic astPickAxes method +* available via the protected interface to the Frame class. The +* public interface to this method is provided by the +* astPickAxesId_ function. +*/ + +/* Local Variables: */ + AstFrame *frame; /* Pointer to Frame to be returned */ + AstMapping *mapping; /* Pointer to Mapping to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned pointers. */ + frame = NULL; + if ( map ) *map = NULL; + +/* Check that a valid set of axes is being selected . */ + astValidateAxisSelection( this, naxes, axes, "astPickAxes" ); + +/* Create the required new Frame by selecting the axes. This also returns a + Mapping which transforms between the original Frame and the new one. */ + astSubFrame( this, NULL, naxes, axes, NULL, &mapping, &frame ); + if ( astOK ) { + +/* Return the Mapping pointer if required. */ + if ( map ) { + *map = mapping; + +/* Otherwise annul the Mapping. If an error occurs, also annul the Frame. */ + } else { + mapping = astAnnul( mapping ); + if ( !astOK ) frame = astAnnul( frame ); + } + } + +/* Return the pointer to the new Frame. */ + return frame; +} + +static void PrimaryFrame( AstFrame *this, int axis1, + AstFrame **frame, int *axis2, int *status ) { +/* +*+ +* Name: +* astPrimaryFrame + +* Purpose: +* Uniquely identify a primary Frame and one of its axes. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astPrimaryFrame( AstFrame *this, int axis1, AstFrame **frame, +* int *axis2 ) + +* Class Membership: +* Frame method. + +* Description: +* This function returns information about the underlying (primary) Frame +* corresponding to a specified axis, when given what may be a compound +* Frame composed of more than one simpler one. + +* Parameters: +* this +* Pointer to the Frame. +* axis1 +* An axis index (zero-based) identifying the Frame axis for which +* information is required. +* frame +* Address of a location to receive a pointer to the underlying (primary) +* frame to which the requested axis belongs (i.e. this will not be a +* compound Frame). +* axis2 +* Pointer to an int which is to receive the (zero-based) axis +* index within "frame" which identifies the axis being referred +* to, using the axis order that applied when the primary Frame +* was originally constructed (i.e. this function undoes all +* subsequent axis pemutations and the effects of combining +* Frames, in order to reveal the original underlying axis +* order). + +* Notes: +* - This protected method is provided so that class implementations can +* distinguish the axes of frames from one another (e.g. can distinguish +* a longitude axis as being different from a latitide axis) even after +* their order has been permuted and they have been combined with axes from +* other Frames. +* - The reference count of the primary Frame will be incremented by one to +* reflect the new pointer returned. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise the returned values. */ + *frame = NULL; + *axis2 = 0; + +/* Validate and permute the axis index supplied. */ + axis1 = astValidateAxis( this, axis1, 1, "astPrimaryFrame" ); + +/* Since "this" is a primary Frame (i.e. is not compound), simply clone a + pointer to it. */ + if ( astOK ) *frame = astClone( this ); + +/* Return the permuted axis index, which refers to the original axis order. */ + if ( astOK ) *axis2 = axis1; +} + +double astReadDateTime_( const char *value, int *status ) { +/* +*+ +* Name: +* astReadDateTime + +* Purpose: +* Read a date/time string. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* double astReadDateTime( const char *value ) + +* Class Membership: +* Frame member function. + +* Description: +* This function reads a date/time string in a variety of formats and +* returns the resulting time as a Modified Julian Date. If the string +* cannot be interpreted as a date/time or contains invalid values, an +* error is reported. + +* Parameters: +* value +* Pointer to a null terminated string containing the value to be read. + +* Returned Value: +* The time as a Modified Julian date. + +* Date/Time Formats: +* The date/time formats supported by this function are listed below. These +* are interpreted in a case-insensitive manner and the function is +* generally flexible about the presence of additional white space and the +* use of alternative field delimiters. +* +* Besselian Epoch +* Expressed in decimal years, with or without decimal places +* ("B1950" or "B1976.13", for example). +* Julian Epoch +* Expressed in decimal years, with or without decimal places +* ("J2000" or "J2100.9", for example). +* Year +* Decimal years, with or without decimal places ("1996.8" for example). +* Such values are interpreted as a Besselian epoch (see above) if less +* than 1984.0 and as a Julian epoch otherwise. +* Julian Date +* With or without decimal places ("JD 2454321.9" for example). +* Modified Julian Date +* With or without decimal places ("MJD 54321.4" for example). +* Gregorian Calendar Date +* With the month expressed as an integer or 3-character +* abbreviation, and with optional decimal places to represent a +* fraction of a day ("1996-10-2" or "1996-Oct-2.6" for +* example). If no fractional part of a day is given, the time +* refers to the start of the day (zero hours). +* Gregorian Date and Time +* Any calendar date (as above) but with a fraction of a day expressed +* as hours, minutes and seconds ("1996-Oct-2 12:13:56.985" for example). +* The date and time can be separated by a space or by a "T" (as used +* by ISO8601). + +* Notes: +* - The date/time value is interpreted as a calendar date and time, not +* linked to any particular time system. Thus, interpretation of hours, +* minutes and seconds is done in the obvious manner assuming 86400 seconds +* in a day. No allowance for is made, for instance, for leap seconds or for +* the varying length of a second in some time systems. +* - A value of AST__BAD is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Vaiables: */ + char cmonth[ 4 ]; /* Buffer for name of month */ + char sep1[ 2 ]; /* Year/month separator string */ + char sep2[ 2 ]; /* Month/day separator string */ + char sep3[ 2 ]; /* Hour/minute separator string */ + char sep4[ 2 ]; /* Minute/second separator string */ + char *cc; /* Pointer to copy of remaining text */ + const char *v; /* Pointer into value string */ + const char *p; /* Pointer to date/time separator */ + double day; /* Day number plus fraction of whole day */ + double epoch; /* Epoch stored as decimal years */ + double hms; /* Hours, min & sec as fraction of a day */ + double jd; /* Julian Date */ + double mjd; /* Modified Julian Date */ + double result; /* Result to be returned */ + double sec; /* Seconds and fractions of a second */ + int hour; /* Number of hours */ + int iday; /* Number of whole days */ + int l; /* Length of string remaining */ + int len; /* Length of string */ + int match; /* Date/time string has correct form? */ + int minute; /* Number of minutes */ + int month; /* Number of months */ + int nc; /* Number of characters read from string */ + int stat; /* Status return from SLALIB functions */ + int year; /* Number of years */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Initialise. */ + result = AST__BAD; + +/* Obtain the length of the input string. */ + len = (int) strlen( value ); + +/* Attempt to read the string using each recognised format in turn. */ + +/* Besselian epoch in decimal years (e.g. "B1950.0"). */ +/* ================================================== */ + if ( nc = 0, + ( 1 == astSscanf( value, " %*1[Bb] %lf %n", &epoch, &nc ) ) + && ( nc >= len ) ) { + +/* Convert to Modified Julian Date. */ + result = palEpb2d( epoch ); + +/* Julian epoch in decimal years (e.g. "J2000.0"). */ +/* =============================================== */ + } else if ( nc = 0, + ( 1 == astSscanf( value, " %*1[Jj] %lf %n", &epoch, &nc ) ) + && ( nc >= len ) ) { + +/* Convert to Modified Julian Date. */ + result = palEpj2d( epoch ); + +/* Decimal years (e.g. "1976.2"). */ +/* ============================== */ + } else if ( nc = 0, + ( 1 == astSscanf( value, " %lf %n", &epoch, &nc ) ) + && ( nc >= len ) ) { + +/* Convert to Modified Julian Date, treating the epoch as Julian or Besselian + depending on whether it is 1984.0 or later. */ + result = ( epoch < 1984.0 ) ? palEpb2d( epoch ) : palEpj2d( epoch ); + +/* Modified Julian Date (e.g. "MJD 54321.0"). */ +/* ============================================ */ + } else if ( nc = 0, + ( 1 == astSscanf( value, " %*1[Mm] %*1[Jj] %*1[Dd] %lf %n", + &mjd, &nc ) ) && ( nc >= len ) ) { + +/* Use the result directly. */ + result = mjd; + +/* Julian Date (e.g. "JD 2454321.5"). */ +/* ==================================== */ + } else if ( nc = 0, + ( 1 == astSscanf( value, " %*1[Jj] %*1[Dd] %lf %n", + &jd, &nc ) ) && ( nc >= len ) ) { + +/* Convert to Modified Julian Date. */ + result = jd - 2400000.5; + +/* Gregorian calendar date (e.g. "1996-10-2" or "1996-Oct-2"). */ +/* =========================================================== */ +/* This format also allows day fractions expressed as decimal days, e.g: + + "1996-Oct-2.5001" + + or as hours, minutes and seconds, e.g: + + "1996-Oct-2 12:14:30.52" + + Various alternative field delimiters are also allowed. */ + } else { + +/* Note that the method used to parse this format relies heavily on + conditional execution controlled by "&&" and "||" operators. Initialise + the variables used. */ + v = value; + l = len; + *cmonth = '\0'; + year = month = iday = hour = minute = 0; + day = sec = 0.0; + +/* Identify the year and month. */ +/* ---------------------------- */ +/* Try to match an initial " 1996 - 10 -" or " 1996 10 " or similar. */ + match = + ( nc = 0, ( 4 == astSscanf( v, " %d %1[:/-] %2d %1[:/-]%n", + &year, sep1, &month, sep2, &nc ) ) ); + match = match || + ( nc = 0, ( 4 == astSscanf( v, " %d%1[ ] %2d%1[ ]%n", + &year, sep1, &month, sep2, &nc ) ) ); + +/* If that failed, allow " 1996 - Oct -" or " 1996 Oct " or similar. */ + match = match || + ( nc = 0, ( 4 == astSscanf( v, + " %d %1[:/-] %3[ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz] %1[:/-]%n", + &year, sep1, cmonth, sep2, &nc ) ) ); + match = match || + ( nc = 0, ( 4 == astSscanf( v, + " %d%1[ ] %3[ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz]%1[ ]%n", + &year, sep1, cmonth, sep2, &nc ) ) ); + +/* Alternative field separators are permitted above, but ensure that + they are both the same. */ + match = match && ( *sep1 == *sep2 ); + +/* Identify the day and fraction of day. */ +/*-------------------------------------- */ +/* If the above matched correctly, modify the string pointer "v" to + the next character to be interpreted and decrement the remaining + string length. */ + if ( match ) { + v += nc; + l -= nc; + +/* ISO8601 format uses the litter T as a delimiter between the date and time. + If there is a T in the remaining string, take a copy and change the T to + a space. */ + p = strchr( v, 'T' ); + if( p ) { + cc = astStore( NULL, v, l + 1 ); + cc[ p - v ] = ' '; + v = cc; + } else { + cc = NULL; + } + +/* We now try to match the following characters but without reading + any values. This is done to ensure the string has the correct form + (e.g. exclude "-" signs and exponents in numbers, which are + otherwise hard to detect). */ + +/* Try to match " 12.3456 " or similar. */ + match = + ( nc = 0, ( 0 == astSscanf( v, " %*2[0123456789].%*[0123456789] %n", + &nc ) ) + && ( nc == l ) ); + +/* If that failed, then try to match " 12. " or similar. */ + match = match || + ( nc = 0, ( 0 == astSscanf( v, " %*2[0123456789]. %n", &nc ) ) + && ( nc == l ) ); + +/* If that also failed, then try to match just " 12 " or similar. */ + match = match || + ( nc = 0, ( 0 == astSscanf( v, " %*2[0123456789] %n", &nc ) ) + && ( nc == l ) ); + +/* If any of the above patterns matched, now read the data (the day number) + as a double value. */ + if ( match ) { + match = ( nc = 0, ( 1 == astSscanf( v, " %lf %n", &day, &nc ) ) + && ( nc == l ) ); + +/* If none of the above matched, then look to see if the day fraction has been + given in hours, minutes and seconds by trying to match " 12 03 : 45 :" or + " 12 13 45 " or similar. */ + } else { + match = + ( nc = 0, ( 5 == astSscanf( v, + " %2d%*1[ ] %2d %1[:/-] %2d %1[:/-]%n", + &iday, &hour, sep3, &minute, sep4, + &nc ) ) ); + match = match || + ( nc = 0, ( 5 == astSscanf( v, " %2d%*1[ ] %2d%1[ ] %2d%1[ ]%n", + &iday, &hour, sep3, &minute, sep4, + &nc ) ) ); + +/* Alternative field separators are permitted above, but ensure that + they are both the same. */ + match = match && ( *sep3 == *sep4 ); + +/* If the day number was read as an integer, convert it to double. */ + if ( match ) day = (double) iday; + +/* If no match, see if we can get a match without a trailing seconds field. */ + if( !match ) { + match = + ( nc = 0, ( 4 == astSscanf( v, + " %2d%*1[ ] %2d %1[:/-] %2d %n", + &iday, &hour, sep3, &minute, &nc ) && + ( nc == l ) ) ); + match = match || + ( nc = 0, ( 4 == astSscanf( v, " %2d%*1[ ] %2d%1[ ] %2d %n", + &iday, &hour, sep3, &minute, &nc ) && + ( nc == l ) ) ); + +/* If the day number was read as an integer, convert it to double. */ + if ( match ) day = (double) iday; + +/* Otherwise, identify the seconds field. */ +/* -------------------------------------- */ +/* If hours and minutes fields have been matched, now look for the + final seconds (and fractions of seconds) field. This is similar to + the day/fraction field (see earlier) in that we first check that it + has the correct form before reading its value. */ + +/* Adjust the string pointer and remaining string length. */ + } else { + v += nc; + l -= nc; + +/* Try to match " 12.3456 " or similar. */ + match = + ( nc = 0, ( 0 == astSscanf( v, + " %*2[0123456789].%*[0123456789] %n", + &nc ) ) + && ( nc == l ) ); + +/* If that failed, then try to match " 12. " or similar. */ + match = match || + ( nc = 0, ( 0 == astSscanf( v, " %*2[0123456789]. %n", &nc ) ) + && ( nc == l ) ); + +/* If that also failed, then try to match just " 12 " or similar. */ + match = match || + ( nc = 0, ( 0 == astSscanf( v, " %*2[0123456789] %n", &nc ) ) + && ( nc == l ) ); + +/* If any of the above patterns matched, now read the data (the number of + seconds) as a double value. */ + if ( match ) { + match = ( nc = 0, ( 1 == astSscanf( v, " %lf %n", &sec, &nc ) ) + && ( nc == l ) ); + } + } + } + +/* Free resources */ + if( cc ) cc = astFree( cc ); + + } + +/* Interpret the values that were read. */ +/* ------------------------------------ */ +/* We execute this if all of the above text matching was successful, + transferred the required number of data values, and consumed the + entire input string. */ + if ( match ) { + +/* See if the month was given as a character string (e.g. "Oct") instead of + a number. If so, define local variables for use in converting it. */ + if ( *cmonth ) { + char lcmonth[ 4 ]; /* Lower case copy of month string */ + const char *ptr; /* Pointer result from look up */ + const char *table = /* Month look up table */ + "jan feb mar apr may jun jul aug sep oct nov dec"; + int i; /* Loop counter for characters */ + +/* Convert the month string to lower case. */ + for ( i = 0; cmonth[ i ]; i++ ) { + lcmonth[ i ] = tolower( cmonth[ i ] ); + } + lcmonth[ i ] = '\0'; + +/* Look the month up in the table of months and generate the required month + number. */ + if ( ( ptr = strstr( table, lcmonth ) ) ) { + month = 1 + ( ptr - table ) / 4; + +/* If the lookup failed, report an error. */ + } else { + astError( AST__DTERR, "Month value \"%s\" is invalid.", status, + cmonth ); + } + } + +/* If OK, extract the integral day number and convert years, months and days + to a Modified Julian Date. */ + if ( astOK ) { + iday = (int) day; + palCaldj( year, month, iday, &mjd, &stat ); + +/* Examine the return status from the conversion and report an appropriate + error if necessary. */ + switch ( stat ) { + case 1: + astError( AST__DTERR, "Year value (%d) is invalid.", status, year ); + break; + case 2: + astError( AST__DTERR, "Month value (%d) is invalid.", status, month ); + break; + case 3: + astError( AST__DTERR, "Day value (%.*g) is invalid.", status, DBL_DIG, + day ); + break; + +/* If conversion to MJD was successful, add any fractional part of a day to the + result. */ + default: + mjd += ( day - (double) iday ); + +/* Convert hours, minutes and seconds to a fraction of a day (this will give + zero if none of these quantities was supplied). */ + palDtf2d( hour, minute, sec, &hms, &stat ); + +/* Examine the return status from the conversion and report an appropriate + error if necessary. */ + switch ( stat ) { + case 1: + astError( AST__DTERR, "Hour value (%d) is invalid.", status, hour ); + break; + case 2: + astError( AST__DTERR, "Minute value (%d) is invalid.", status, + minute ); + break; + case 3: + astError( AST__DTERR, "Seconds value (%.*g) is invalid.", status, + DBL_DIG, sec ); + break; + +/* Add the fraction of a day derived from hours, minutes and seconds fields to + the result. */ + default: + mjd += hms; + break; + } + break; + } + +/* Return the result, if no error occurred. */ + if ( astOK ) result = mjd; + } + +/* If none of the supported date/time formats matched, then report an error. */ + } else { + astError( AST__DTERR, "Date/time does not have the correct form." , status); + } + } + +/* Return the result. */ + return result; +} + +static void ReportPoints( AstMapping *this_mapping, int forward, + AstPointSet *in_points, AstPointSet *out_points, int *status ) { +/* +* Name: +* ReportPoints + +* Purpose: +* Report the effect of transforming a set of points using a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void ReportPoints( AstMapping *this, int forward, +* AstPointSet *in_points, AstPointSet *out_points, int *status ) + +* Class Membership: +* Frame member function (over-rides the protected astReportPoints +* method inherited from the Mapping class). + +* Description: +* This function reports the coordinates of a set of points before +* and after being transformed by a Frame, by writing them to +* standard output. + +* Parameters: +* this +* Pointer to the Frame. +* forward +* A non-zero value indicates that the Frame's forward +* coordinate transformation has been applied, while a zero +* value indicates the inverse transformation. +* in_points +* Pointer to a PointSet which is associated with the +* coordinates of a set of points before the Frame was applied. +* out_points +* Pointer to a PointSet which is associated with the +* coordinates of the same set of points after the Frame has +* been applied. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *this; /* Pointer to the Frame structure */ + double **ptr_in; /* Pointer to array of input data pointers */ + double **ptr_out; /* Pointer to array of output data pointers */ + int coord; /* Loop counter for coordinates */ + int ncoord_in; /* Number of input coordinates per point */ + int ncoord_out; /* Number of output coordinates per point */ + int npoint; /* Number of points to report */ + int npoint_in; /* Number of input points */ + int npoint_out; /* Number of output points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_mapping; + +/* Obtain the numbers of points and coordinates associated with each + PointSet. */ + npoint_in = astGetNpoint( in_points ); + npoint_out = astGetNpoint( out_points ); + ncoord_in = astGetNcoord( in_points ); + ncoord_out = astGetNcoord( out_points ); + +/* Obtain the pointers that give access to the coordinate data + associated with each PointSet. */ + ptr_in = astGetPoints( in_points ); + ptr_out = astGetPoints( out_points ); + +/* In the event that both PointSets don't contain equal numbers of + points (this shouldn't actually happen), simply use the minimum + number. */ + npoint = ( npoint_in < npoint_out ) ? npoint_in : npoint_out; + +/* Loop to report the effect of the transformation on each point in + turn. */ + for ( point = 0; point < npoint; point++ ) { + +/* Report the input coordinates (in parentheses and separated by + commas). Format each value for display using the Frame's astFormat + method. */ + printf( "(" ); + for ( coord = 0; coord < ncoord_in; coord++ ) { + printf( "%s%s", coord ? ", " : "", + astFormat( this, coord, ptr_in[ coord ][ point ] ) ); + } + +/* Similarly report the output coordinates. */ + printf( ") --> (" ); + for ( coord = 0; coord < ncoord_out; coord++ ) { + printf( "%s%s", coord ? ", " : "", + astFormat( this, coord, ptr_out[ coord ][ point ] ) ); + } + printf( ")\n" ); + } +} + +static void Resolve( AstFrame *this, const double point1[], + const double point2[], const double point3[], + double point4[], double *d1, double *d2, int *status ){ +/* +*++ +* Name: +c astResolve +f AST_RESOLVE + +* Purpose: +* Resolve a vector into two orthogonal components + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astResolve( AstFrame *this, const double point1[], +c const double point2[], const double point3[], +c double point4[], double *d1, double *d2 ); +f CALL AST_RESOLVE( THIS, POINT1, POINT2, POINT3, POINT4, D1, D2, +f STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function resolves a vector into two perpendicular components. +f This routine resolves a vector into two perpendicular components. +* The vector from point 1 to point 2 is used as the basis vector. +* The vector from point 1 to point 3 is resolved into components +* parallel and perpendicular to this basis vector. The lengths of the +* two components are returned, together with the position of closest +* aproach of the basis vector to point 3. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c point1 +f POINT1( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vector to be resolved. +c point2 +f POINT2( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +c point3 +f POINT3( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute). This marks the end of the vector to be +* resolved. +c point4 +f POINT4( * ) = DOUBLE PRECISION (Returned) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* in which the coordinates of the point of closest approach of the +* basis vector to point 3 will be returned. +c d1 +f D1 = DOUBLE PRECISION (Returned) +c The address of a location at which to return the distance from +f The distance from +* point 1 to point 4 (that is, the length of the component parallel +* to the basis vector). Positive values are in the same sense as +* movement from point 1 to point 2. +c d2 +f D2 = DOUBLE PRECISION (Returned) +c The address of a location at which to return the distance from +f The distance from +* point 4 to point 3 (that is, the length of the component +* perpendicular to the basis vector). The value is always positive. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - Each vector used in this function is the path of +f - Each vector used in this routine is the path of +* shortest distance between two points, as defined by the +c astDistance function. +f AST_DISTANCE function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the required +* output values are undefined. +*-- +*/ + +/* Local Variables: */ + double bv; /* Length of basis vector */ + double c; /* Component length */ + double dp; /* Dot product */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of Frame axes */ + int ok; /* OK to proceed? */ + +/* Check the global error status. */ + *d1 = AST__BAD; + *d2 = AST__BAD; + if ( !astOK ) return; + +/* Determine the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Initialize bad values, and check if the supplied vectors are good. */ + ok = 1; + for( axis = 0; axis < naxes; axis++ ){ + point4[ axis ] = AST__BAD; + if( point1[ axis ] == AST__BAD || + point2[ axis ] == AST__BAD || + point3[ axis ] == AST__BAD ) ok = 0; + } + +/* Check the supplied values. */ + if ( ok ) { + +/* Find the dot product of the basis vector with the vector joining point 1 + and point 3. At the same time form the squared length of the basis + vector. */ + dp = 0.0; + bv = 0.0; + for( axis = 0; axis < naxes; axis++ ){ + c = point2[ axis ] - point1[ axis ]; + dp += c * ( point3[ axis ] - point1[ axis ] ); + bv += c * c; + } + +/* Check the basis vector does not have zero length, and convert the + squared length into a length. */ + if( bv > 0.0 ) { + bv = sqrt( bv ); + +/* The dot product is the required distance d1 multiplied by the length + of the basis vector. Form the distance d1. */ + *d1 = dp/bv; + +/* Offset away from point 1 towards point 2 by a distance of d1. */ + for( axis = 0; axis < naxes; axis++ ){ + point4[ axis ] = point1[ axis ] + + (*d1/bv)*( point2[ axis ] - point1[ axis ] ); + } + +/* Finally, form the required length d2. */ + *d2 = 0.0; + for( axis = 0; axis < naxes; axis++ ){ + c = ( point3[ axis ] - point4[ axis ] ); + *d2 += c*c; + } + *d2 = sqrt( *d2 ); + + } + } + + return; + +} + +static AstPointSet *ResolvePoints( AstFrame *this, const double point1[], + const double point2[], AstPointSet *in, + AstPointSet *out, int *status ) { +/* +*+ +* Name: +* astResolvePoints + +* Purpose: +* Resolve a set of vectors into orthogonal components + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstPointSet *astResolvePoints( AstFrame *this, const double point1[], +* const double point2[], AstPointSet *in, +* AstPointSet *out ) + +* Class Membership: +* Frame method. + +* Description: +* This function takes a Frame and a set of vectors encapsulated +* in a PointSet, and resolves each one into two orthogonal components, +* returning these two components in another PointSet. +* +* This is exactly the same as the public astResolve method, except +* that this method allows many vectors to be processed in a single call, +* thus reducing the computational cost of overheads of many +* individual calls to astResolve. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vectors to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* in +* Pointer to the PointSet holding the ends of the vectors to be +* resolved. +* out +* Pointer to a PointSet which will hold the length of the two +* resolved components. A NULL value may also be given, in which +* case a new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. The first axis will +* hold the lengths of the vector components parallel to the basis vector. +* These values will be signed (positive values are in the same sense as +* movement from point 1 to point 2. The second axis will hold the lengths +* of the vector components perpendicular to the basis vector. These +* values will be signed only if the Frame is 2-dimensional, in which +* case a positive value indicates that rotation from the basis vector +* to the tested vector is in the same sense as rotation from the first +* to the second axis of the Frame. + +* Notes: +* - The number of coordinate values per point in the input +* PointSet must match the number of axes in the supplied Frame. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and 2 coordinate values per point. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - We assume flat geometry throughout this function. Other classes, +* (e.g. SkyFrame) will override this method using more appropriate +* geometry. +*- +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_in; /* Pointers to input axis values */ + double **ptr_out; /* Pointers to returned axis values */ + double *basisv; /* Pointer to array holding basis vector */ + double *d1; /* Pointer to next parallel component value */ + double *d2; /* Pointer to next perpendicular component value */ + double *ip; /* Pointer to next input axis value */ + double bv; /* Length of basis vector */ + double c; /* Constant value */ + double d; /* Component length */ + double dp; /* Dot product */ + double x1; /* First axis of basis vector */ + double x2; /* First axis of test vector */ + double y1; /* Second axis of basis vector */ + double y2; /* Second axis of test vector */ + int axis; /* Loop counter for axes */ + int ipoint; /* Index of next point */ + int nax; /* Number of Frame axes */ + int ncoord_in; /* Number of input PointSet coordinates */ + int ncoord_out; /* Number of coordinates in output PointSet */ + int npoint; /* Number of points to transform */ + int npoint_out; /* Number of points in output PointSet */ + int ok; /* OK to proceed? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain the number of axes in the Frame. */ + nax = astGetNaxes( this ); + +/* Obtain the number of input vectors to resolve and the number of coordinate + values per vector. */ + npoint = astGetNpoint( in ); + ncoord_in = astGetNcoord( in ); + +/* If OK, check that the number of input coordinates matches the number + required by the Frame. Report an error if these numbers do not match. */ + if ( astOK && ( ncoord_in != nax ) ) { + astError( AST__NCPIN, "astResolvePoints(%s): Bad number of coordinate " + "values (%d) in input %s.", status, astGetClass( this ), ncoord_in, + astGetClass( in ) ); + astError( AST__NCPIN, "The %s given requires %d coordinate value(s) for " + "each input point.", status, astGetClass( this ), nax ); + } + +/* If still OK, and a non-NULL pointer has been given for the output PointSet, + then obtain the number of points and number of coordinates per point for + this PointSet. */ + if ( astOK && out ) { + npoint_out = astGetNpoint( out ); + ncoord_out = astGetNcoord( out ); + +/* Check that the dimensions of this PointSet are adequate to accommodate the + output coordinate values and report an error if they are not. */ + if ( astOK ) { + if ( npoint_out < npoint ) { + astError( AST__NOPTS, "astResolvePoints(%s): Too few points (%d) in " + "output %s.", status, astGetClass( this ), npoint_out, + astGetClass( out ) ); + astError( AST__NOPTS, "The %s needs space to hold %d transformed " + "point(s).", status, astGetClass( this ), npoint ); + } else if ( ncoord_out < 2 ) { + astError( AST__NOCTS, "astResolvePoints(%s): Too few coordinate " + "values per point (%d) in output %s.", status, + astGetClass( this ), ncoord_out, astGetClass( out ) ); + astError( AST__NOCTS, "The %s supplied needs space to store 2 " + "coordinate value(s) per transformed point.", status, + astGetClass( this ) ); + } + } + } + +/* If all the validation stages are passed successfully, and a NULL output + pointer was given, then create a new PointSet to encapsulate the output + coordinate data. */ + if ( astOK ) { + if ( !out ) { + result = astPointSet( npoint, 2, "", status ); + +/* Otherwise, use the PointSet supplied. */ + } else { + result = out; + } + } + +/* Get pointers to the input and output axis values */ + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Store points to the first two axis arrays in the returned PointSet. */ + d1 = ptr_out[ 0 ]; + d2 = ptr_out[ 1 ]; + +/* Allocate work space. */ + basisv = astMalloc( sizeof( double )*(size_t) nax ); + +/* If the Frame has only one axis, then the supplied basic vector is + irrelevant - the returned perpendicular distances are always zero and + the returned parallel distances are just the distances from point1 + to each input point. */ + if( nax < 2 && basisv ) { + ip = ptr_in[ 0 ]; + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++, ip++ ) { + *d1 = astAxDistance( this, 1, point1[0], *ip ); + *d2 = 0.0; + } + +/* Now deal with Frames which have 2 or more axes */ + } else if( basisv ){ + +/* Check if the supplied positions defining the basis vector are good. + Store the basis vector, and get its squared length. */ + ok = 1; + bv = 0.0; + for( axis = 0; axis < nax; axis++ ){ + if( point1[ axis ] == AST__BAD || + point2[ axis ] == AST__BAD ) { + ok = 0; + break; + } else { + basisv[ axis ] = point2[ axis ] - point1[ axis ]; + bv += basisv[ axis ]*basisv[ axis ]; + } + } + +/* Check the basis vector does not have zero length, and convert the + squared length into a length. */ + if( ok && bv > 0.0 ) { + bv = sqrt( bv ); + } else { + ok = 0; + } + +/* Store points to the first two axis arrays in the returned PointSet. */ + d1 = ptr_out[ 0 ]; + d2 = ptr_out[ 1 ]; + +/* Check supplied values can be used */ + if( ok ) { + +/* Loop round each supplied vector. */ + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++ ) { + +/* Find the dot product of the basis vector with the vector joining point 1 + and the end of the current vector. */ + ok = 1; + dp = 0.0; + for( axis = 0; axis < nax; axis++ ){ + d = ptr_in[ axis ][ ipoint ] - point1[ axis ]; + if( d != AST__BAD ) { + dp += basisv[ axis ] * d; + } else { + ok = 0; + break; + } + } + +/* If this input position is good... */ + if( ok ) { + +/* The dot product is the required parallel component length multiplied by the + length of the basis vector. Form the distance d1. */ + *d1 = dp/bv; + +/* Offset away from point 1 towards point 2 by a distance of d1, and form the + required length d2. */ + c = *d1/bv; + if( nax > 2 ) { + *d2 = 0.0; + for( axis = 0; axis < nax; axis++ ){ + d = ptr_in[ axis ][ ipoint ] - + ( point1[ axis ] + c*basisv[ axis ] ); + *d2 += d*d; + } + *d2 = sqrt( *d2 ); + +/* If the Frame is 2 dimensional, we can give a sign the the perpendicular + component. */ + } else { + x1 = c*basisv[ 0 ]; + y1 = c*basisv[ 1 ]; + x2 = ptr_in[ 0 ][ ipoint ] - ( point1[ 0 ] + x1 ); + y2 = ptr_in[ 1 ][ ipoint ] - ( point1[ 1 ] + y1 ); + *d2 = sqrt( x2*x2 + y2*y2 ); + if( x1*y2 - x2*y1 < 0.0 ) *d2 = -(*d2); + } + +/* If this input vector is bad, put bad values in the output */ + } else { + *d1 = AST__BAD; + *d2 = AST__BAD; + } + } + +/* If supplied values cannot be used, fill the returned PointSet with bad + values */ + } else { + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++ ) { + *d1 = AST__BAD; + *d2 = AST__BAD; + } + } + } + +/* Free resources */ + basisv = astFree( basisv ); + +/* Annul the returned PointSet if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void SetActiveUnit( AstFrame *this, int value, int *status ){ +/* +*++ +* Name: +c astSetActiveUnit +f AST_SETACTIVEUNIT + +* Purpose: +* Specify how the Unit attribute should be used. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astSetActiveUnit( AstFrame *this, int value ) +f CALL AST_SETACTIVEUNIT( THIS, VALUE, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* sets the current value of the ActiveUnit flag for a Frame, which +* controls how the Frame behaves when it is used (by +c astFindFrame or astConvert) +f AST_FINDFRAME or AST_CONVERT) +* to match another Frame. If the ActiveUnit flag is set in both +* template and target Frames then the returned Mapping takes into account +* any differences in axis units. The default value for simple Frames is +* zero, which preserves the behaviour of versions of AST prior to +* version 2.0. +* +* If the ActiveUnit flag of either Frame is +c zero, +f .FALSE., +* then the Mapping will ignore any difference in the Unit attributes of +* corresponding template and target axes. In this mode, the Unit +* attributes are purely descriptive commentary for the benefit of +* human readers and do not influence the Mappings between Frames. +* This is the behaviour which all Frames had in older version of AST, +* prior to the introduction of this attribute. +* +* If the ActiveUnit flag of both Frames is +c non-zero, +f .TRUE., +* then the Mapping from template to target will take account of any +* difference in the axis Unit attributes, where-ever possible. For +* instance, if corresponding target and template axes have Unit strings of +* "km" and "m", then the FrameSet class will use a ZoomMap to connect +* them which introduces a scaling of 1000. If no Mapping can be found +* between the corresponding units string, then an error is reported. +* In this mode, it is assumed that values of the Unit attribute conform +* to the syntax for units strings described in the FITS WCS Paper I +* "Representations of world coordinates in FITS" (Greisen & Calabretta). +* Particularly, any of the named unit symbols, functions, operators or +* standard multiplier prefixes listed within that paper can be used within +* a units string. A units string may contain symbols for unit which are +* not listed in the FITS paper, but transformation to any other units +* will then not be possible (except to units which depend only on the +* same unknown units - thus "flops" can be transformed to "Mflops" +* even though "flops" is not a standard FITS unit symbol). +* +* A range of common non-standard variations of unit names and multiplier +* prefixes are also allowed, such as adding an "s" to the end of Angstrom, +* using a lower case "a" at the start of "angstrom", "micron" instead of +* "um", "sec" instead of "s", etc. +* +c If the ActiveUnit flag is non-zero, setting a new Unit value for an +f If the ActiveUnit flag is .TRUE., setting a new Unit value for an +* axis may also change its Label and Symbol attributes. For instance, if +* an axis has Unit "Hz" and Label "frequency", then changing its Unit to +* "log(Hz)" will change its Label to "log( frequency )". In addition, +* the Axis Format attribute will be cleared when-ever a new value +* is assigned to the Unit attribute. +* +c Note, if a non-zero value is set for the ActiveUnit flag, then changing a +f Note, if a .TRUE. value is set for the ActiveUnit flag, then changing a +* Unit value for the current Frame within a FrameSet will result in the +* Frame being re-mapped (that is, the Mappings which define the +* relationships between Frames within the FrameSet will be modified to +* take into account the change in Units). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c value +f VALUE = LOGICAL (Given) +* The new value to use. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* SkyFrame +c The ActiveUnit flag for a SkyFrame is always 0 (any value +c supplied using this function is ignored). +f The ActiveUnit flag for a SkyFrame is always .FALSE. (any value +f supplied using this routine is ignored). +* SpecFrame +c The ActiveUnit flag for a SpecFrame is always 1 (any value +c supplied using this function is ignored). +f The ActiveUnit flag for a SpecFrame is always .TRUE. (any value +f supplied using this routine is ignored). +* FluxFrame +c The ActiveUnit flag for a FluxFrame is always 1 (any value +c supplied using this function is ignored). +f The ActiveUnit flag for a FluxFrame is always .TRUE. (any value +f supplied using this routine is ignored). +* CmpFrame +c The default ActiveUnit flag for a CmpFrame is 1 if both of the +c component Frames are using active units, and zero otherwise. When +f The default ActiveUnit flag for a CmpFrame is .TRUE. if both of the +f component Frames are using active units, and .FALSE. otherwise. When +* a new value is set for the ActiveUnit flag, the flag value +* is propagated to the component Frames. This change will be +* reflected through all references to the component Frames, not +* just those encapsulated within the CmpFrame. +* Region: +* Regions always use active units if possible. + +* Notes: +* - The ActiveUnit flag resembles a Frame attribute, except that it +* cannot be tested or cleared, and it cannot be accessed using the +c generic astGet and astSet functions. +f generic AST_GET and AST_SET routines. +c - The astGetActiveUnit function can be used to retrieve the current +f - The AST_GETACTIVEUNIT routine can be used to retrieve the current +* value of the ActiveUnit flag. + +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store a value of 1 for the Frame component if the supplied value is + non-zero. */ + this->active_unit = ( value ) ? 1 : 0; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Frame member function (over-rides the astSetAttrib method inherited +* from the Mapping class). + +* Description: +* This function assigns an attribute value for a Frame, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Frame. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Vaiables: */ + AstAxis *ax; /* Pointer to Axis */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + AstFrame *this; /* Pointer to the Frame structure */ + AstSystemType system_code; /* System code */ + char pfrm_attrib[ 100 ]; /* Primary Frame attribute */ + char *pfrm_setting; /* Primary Frame attribute */ + char *axis_setting; /* Pointer to axis attribute setting string */ + const char *equals; /* Pointer to equals sign */ + const char *old_setting; /* Pointer to supplied setting string */ + const char *op; /* Pointer to opening parenthesis */ + double dval; /* Double attibute value */ + double mjd; /* Epoch as a Modified Julian Date */ + int axis; /* Index for the Frame axis */ + int axis_nc; /* No. characters in axis attribute name */ + int axis_value; /* Offset of value to be assigned to axis */ + int digits; /* Number of digits of precision */ + int direction; /* Axis direction flag */ + int domain; /* Offset of Domain string */ + int epoch; /* Offset of Epoch string */ + int format; /* Offset of axis Format string */ + int free_axis_setting; /* Should axis_setting be freed? */ + int has_axis; /* Does setting include an axis specifier? */ + int ival; /* Integer attribute value */ + int label; /* Offset of axis Label string */ + int len; /* Length of setting string */ + int match_end; /* Match final axes of target? */ + int max_axes; /* Maximum number of axes matched */ + int min_axes; /* Minimum number of axes matched */ + int nc; /* Number of characters read by astSscanf */ + int off2; /* Modified offset of attribute value */ + int off; /* Offset of attribute value */ + int oldrep; /* Original error reporting state */ + int paxis; /* Axis index within primary frame */ + int permute; /* Permute axes in order to match? */ + int preserve_axes; /* Preserve matched target axes? */ + int sign; /* Sign of longitude value */ + int symbol; /* Offset of axis Symbol string */ + int system; /* Offset of System string */ + int title; /* Offset of Title string */ + int unit; /* Offset of axis Unit string */ + int used; /* Could the setting string be used? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_object; + +/* Find the offset to the first equal sign in the setting string. */ + equals = strchr( setting, '=' ); + +/* Set a flag indicating if the attribute name includes an axis + specifier. */ + op = strchr( setting, '(' ); + has_axis = ( !op || op > equals ) ? 0 : 1; + +/* A flag indicating that we do not need to free the axis_setting memory. */ + free_axis_setting = 0; + +/* Initialise things to avoid compiler warnings. */ + axis_setting = NULL; + old_setting = NULL; + +/* Jump back to here if we are trying the same attribute setting but with + an explicit axis "(1)" added to the attribute name. */ +L1: + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* Digits. */ +/* ------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "digits= %d %n", &digits, &nc ) ) + && ( nc >= len ) ) { + astSetDigits( this, digits ); + +/* Digits(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "digits(%d)= %d %n", + &axis, &digits, &nc ) ) + && ( nc >= len ) ) { + +/* There is no function to set the Digits attribute value for an axis + directly, so obtain a pointer to the Axis and use this to set the + attribute. */ + (void) astValidateAxis( this, axis - 1, 1, "astSetDigits(axis)" ); + ax = astGetAxis( this, axis - 1 ); + astSetAxisDigits( ax, digits ); + ax = astAnnul( ax ); + +/* Direction(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "direction(%d)= %d %n", + &axis, &direction, &nc ) ) + && ( nc >= len ) ) { + astSetDirection( this, axis - 1, direction ); + +/* Epoch. */ +/* ------ */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "epoch=%n%*[^\n]%n", &epoch, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the Epoch value to a Modified Julian Date before use. */ + mjd = astReadDateTime( setting + epoch ); + if ( astOK ) { + astSetEpoch( this, mjd ); + +/* Report contextual information if the conversion failed. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid epoch value " + "\"%s\" given for coordinate system.", status, + astGetClass( this ), setting + epoch ); + } + +/* Top(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "top(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetTop( this, axis - 1, dval ); + +/* Bottom(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "bottom(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetBottom( this, axis - 1, dval ); + +/* Domain. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "domain=%n%*[^\n]%n", &domain, &nc ) ) + && ( nc >= len ) ) { + astSetDomain( this, setting + domain ); + +/* Format(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "format(%d)=%n%*[^\n]%n", + &axis, &format, &nc ) ) + && ( nc >= len ) ) { + astSetFormat( this, axis - 1, setting + format ); + +/* Label(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "label(%d)=%n%*[^\n]%n", + &axis, &label, &nc ) ) + && ( nc >= len ) ) { + astSetLabel( this, axis - 1, setting + label ); + +/* MatchEnd. */ +/* --------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "matchend= %d %n", &match_end, &nc ) ) + && ( nc >= len ) ) { + astSetMatchEnd( this, match_end ); + +/* MaxAxes. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "maxaxes= %d %n", &max_axes, &nc ) ) + && ( nc >= len ) ) { + astSetMaxAxes( this, max_axes ); + +/* MinAxes. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "minaxes= %d %n", &min_axes, &nc ) ) + && ( nc >= len ) ) { + astSetMinAxes( this, min_axes ); + +/* Permute. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "permute= %d %n", &permute, &nc ) ) + && ( nc >= len ) ) { + astSetPermute( this, permute ); + +/* PreserveAxes. */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "preserveaxes= %d %n", + &preserve_axes, &nc ) ) + && ( nc >= len ) ) { + astSetPreserveAxes( this, preserve_axes ); + +/* Symbol(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "symbol(%d)=%n%*[^\n]%n", + &axis, &symbol, &nc ) ) + && ( nc >= len ) ) { + astSetSymbol( this, axis - 1, setting + symbol ); + +/* AlignSystem. */ +/* ------------ */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "alignsystem= %n%*s %n", &system, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a System code before use. */ + system_code = astSystemCode( this, system + setting ); + if ( system_code != AST__BADSYSTEM ) { + astSetAlignSystem( this, system_code ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, + "astSetAttrib(%s): Invalid AlignSystem description \"%s\".", status, + astGetClass( this ), system + setting ); + } + +/* System. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "system= %n%*s %n", &system, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a System code before use. */ + system_code = astSystemCode( this, system + setting ); + if ( system_code != AST__BADSYSTEM ) { + astSetSystem( this, system_code ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, + "astSetAttrib(%s): Invalid System description \"%s\".", status, + astGetClass( this ), system + setting ); + } + +/* Title. */ +/* ------ */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "title=%n%*[^\n]%n", &title, &nc ) ) + && ( nc >= len ) ) { + astSetTitle( this, setting + title ); + +/* Unit(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "unit(%d)=%n%*[^\n]%n", + &axis, &unit, &nc ) ) + & ( nc >= len ) ) { + astSetUnit( this, axis - 1, setting + unit ); + +/* ObsLat. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "obslat=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + +/* If the first character in the value string is "N" or "S", remember the + sign of the value and skip over the sign character. Default is north + (+ve). */ + off2 = off; + if( setting[ off ] == 'N' || setting[ off ] == 'n' ) { + off2++; + sign = +1; + } else if( setting[ off ] == 'S' || setting[ off ] == 's' ) { + off2++; + sign = -1; + } else { + sign = +1; + } + +/* If not already created, create an FK5 J2000 SkyFrame which will be used + for formatting and unformatting ObsLon and ObsLat values. */ + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame( "system=FK5,equinox=J2000,format(2)=dms.2", status ); + astEndPM; + } + +/* Convert the string to a radians value before use. */ + ival = astUnformat( skyframe, 1, setting + off2, &dval ); + if ( ival == astChrLen( setting ) - off2 ) { + astSetObsLat( this, dval*sign ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid value for " + "ObsLat (observers latitude) \"%s\".", status, astGetClass( this ), + setting + off ); + } + +/* ObsLon. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "obslon=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + +/* If the first character in the value string is "E" or "W", remember the + sign of the value and skip over the sign character. Default is east + (+ve). */ + off2 = off; + if( setting[ off ] == 'E' || setting[ off ] == 'e' ) { + off2++; + sign = +1; + } else if( setting[ off ] == 'W' || setting[ off ] == 'w' ) { + off2++; + sign = -1; + } else { + sign = +1; + } + +/* If not already created, create an FK5 J2000 SkyFrame which will be used + for formatting and unformatting ObsLon and ObsLat values. */ + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame( "system=FK5,equinox=J2000,format(2)=dms.2", status ); + astEndPM; + } + +/* Convert the string to a radians value before use. */ + ival = astUnformat( skyframe, 1, setting + off2, &dval ); + if ( ival == astChrLen( setting ) - off2 ) { + astSetObsLon( this, dval*sign ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid value for " + "ObsLon (observers longitude) \"%s\".", status, astGetClass( this ), + setting + off ); + } + +/* ObsAlt. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "obsalt= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetObsAlt( this, dval ); + +/* Dut1. */ +/* ---- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "dut1= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetDut1( this, dval ); + + +/* Read-only attributes. */ +/* --------------------- */ +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* Use this macro to report an error if a read-only attribute has been + specified. */ + } else if ( MATCH( "naxes" ) || + !strncmp( setting, "normunit", 8 ) || + !strncmp( setting, "internalunit", 12 ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Other axis attributes. */ +/* ---------------------- */ +/* If the attribute was not identified above, but appears to refer to + a Frame axis, then it may refer to an Axis object of a derived type + (which has additional attributes not recognised here). */ + } else if ( !free_axis_setting && ( nc = 0, + ( 1 == astSscanf( setting, "%*[^()=]%n(%d)%n=%*[^\n]%n", + &axis_nc, &axis, &axis_value, &nc ) ) + && ( nc >= len ) ) ) { + +/* Validate the axis index and copy the attribute setting string. */ + (void) astValidateAxis( this, axis - 1, 1, "astSet" ); + axis_setting = astString( setting, len ); + if ( astOK ) { + +/* Over-write the axis index in the copy with the value to be + assigned. */ + (void) strcpy( axis_setting + axis_nc, setting + axis_value ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis - 1 ); + if( astOK ) { + +/* Assume that we will be able to use the setting. */ + used = 1; + +/* Temporarily switch off error reporting so that if the following attempt + to access the axis attribute fails, we can try to interpret the + attribute name as an attribute of the primary Frame containing the + specified axis. Any errors reported in this context will simply be + ignored, in particularly they are not deferred for later delivery. */ + oldrep = astReporting( 0 ); + +/* Use the Axis astSetAttrib method + to set the value. */ + astSetAttrib( ax, axis_setting ); + +/* If the above call failed with a status of AST__BADAT, indicating that + the attribute name was not recognised, clear the status so that we can + try to interpret the attribute name as an attribute of the primary Frame + containing the specified axis. */ + if( astStatus == AST__BADAT ) { + astClearStatus; + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + +/* Only attempt to use the primary Frame if it is not the same as "this" + - otherwise we could end up in an infinite loop. */ + if( pfrm != this ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astSet" ); + +/* Modify the attribute name to refer to the axis numbering of the + primary frame. */ + sprintf( pfrm_attrib, "%.*s(%d)", axis_nc, setting, paxis + 1 ); + +/* Create a setting string in which the attribute name refers to the axis + numbering of the primary frame. */ + pfrm_setting = NULL; + nc = 0; + pfrm_setting = astAppendString( pfrm_setting, &nc, pfrm_attrib ); + pfrm_setting = astAppendString( pfrm_setting, &nc, setting + axis_value ); + +/* Attempt to set the attribute within the primary Frame. */ + astSetAttrib( pfrm, pfrm_setting ); + +/* Free the memory. */ + pfrm_setting = astFree( pfrm_setting ); + +/* If this failed, clear the status and indicate that we have not managed to + use the attribute setting. */ + if( !astOK ) { + astClearStatus; + used = 0; + } + + } else { + used = 0; + } + +/* If not found attempt to set the attribute value in the Axis, omitting + the axis index. */ + if( ! used ) { + astSetAttrib( pfrm, axis_setting ); + if( !astOK ) { + astClearStatus; + } else { + used = 1; + } + } + +/* Free the setting string, and annul the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + +/* If we could not use the setting, attempt to set the axis attribute again, + this time retaining the error report. This is done to ensure the user + gets an appropriate error message. */ + if( !used ) astSetAttrib( ax, axis_setting ); + } + +/* Annul the Axis pointer and free the memory holding the attribute + setting. */ + ax = astAnnul( ax ); + } + axis_setting = astFree( axis_setting ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, and the Frame has only 1 axis, + and the attribute name does not already include an axis specifier, try + again after appending "(1)" to the end of the attribute name. */ + } else if( !has_axis && astGetNaxes( this ) == 1 && equals ) { + +/* Take a copy of the supplied setting, allowing 3 extra characters for the + axis specifier "(1)". */ + axis_setting = astMalloc( len + 4 ); + if( axis_setting ) memcpy( axis_setting, setting, len ); + +/* Indicate we should free the axis_setting memory. */ + free_axis_setting = 1; + +/* Add in the axis specifier. */ + strcpy( axis_setting + ( equals - setting ), "(1)" ); + +/* Add in the equals sign and attribute value. */ + strcpy( axis_setting + ( equals - setting ) + 3, equals ); + +/* Use the new setting instead of the supplied setting. */ + old_setting = setting; + setting = axis_setting; + +/* Indicate the setting now has an axis specifier. */ + has_axis = 1; + +/* Jump back to try interpreting the new setting string. */ + goto L1; + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. First re-instate the original setting + string if it was changed above. */ + } else { + if( free_axis_setting ) { + setting = old_setting; + axis_setting = astFree( axis_setting ); + free_axis_setting = 0; + } + (*parent_setattrib)( this_object, setting, status ); + } + + if( free_axis_setting ) axis_setting = astFree( axis_setting ); + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void SetAxis( AstFrame *this, int axis, AstAxis *newaxis, int *status ) { +/* +*+ +* Name: +* astSetAxis + +* Purpose: +* Set a new Axis for a Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astSetAxis( AstFrame *this, int axis, AstAxis *newaxis ) + +* Class Membership: +* Frame method. + +* Description: +* This function allows a new Axis object to be associated with one +* of the axes of a Frame, replacing the previous one. Each Axis +* object contains a description of the quantity represented along +* one of the Frame's axes, so this function allows this +* description to be exchanged for another one. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index (zero-based) of the axis whose associated Axis object is to +* be replaced. +* newaxis +* Pointer to the new Axis object. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate and permute the axis index supplied. */ + axis = astValidateAxis( this, axis, 1, "astSetAxis" ); + +/* If OK, annul the Frame's pointer to the old Axis object and clone a pointer + to the new one to replace it. */ + if ( astOK ) { + this->axis[ axis ] = astAnnul( this->axis[ axis ] ); + this->axis[ axis ] = astClone( newaxis ); + } +} + +static void SetFrameFlags( AstFrame *this, int flags, int *status ){ +/* +*+ +* Name: +* astSetFrameFlags + +* Purpose: +* Store a new bit mask of flags in a Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "frame.h" +* void astSetFrameFlags( astFrame *this, int flags ) + +* Class Membership: +* Frame member function. + +* Description: +* This function stores a new set of flags in a Frame. The flags can +* be retrieved using astGetFrameFlags. + +* Parameters: +* this +* The Frame. +* flags +* A bit mask holding the flags. Currently, the following bits are +* used: +* +* 0 - Used to indicate if the Frame is currently involved in an +* attempt to restore the integrity of a FrameSet following +* changes to the attribute values of the Frame. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Assign the new bit mask. */ + this->flags = flags; +} + +static void SetFrameVariants( AstFrame *this, AstFrameSet *variants, int *status ){ +/* +*+ +* Name: +* astSetFrameVariants + +* Purpose: +* Store a FrameSet holding alternative Frame properties. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astSetVariants( AstFrame *this, AstFrameSet *variants ) + +* Class Membership: +* Frame method. + +* Description: +* This function adds sets of alternative Frame properties to a Frame. + +* Parameters: +* this +* Pointer to the Frame. +* variants +* Pointer to a FrameSet in which each Frame is of the same class +* and dimensionality as "this" and all Frames have unique Domain +* names. + +* Notes: +* - A clone of the supplied FrameSet pointer is stored in the Frame. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Annul any variants FrameSet already stored in the Frame. */ + if( this->variants ) this->variants = astAnnul( this->variants ); + +/* Store a clone of ht esupplied FrameSet pointer. */ + if( variants ) this->variants = astClone( variants ); + +} + +static void SetUnit( AstFrame *this, int axis, const char *unit, int *status ) { +/* +* Name: +* SetUnit + +* Purpose: +* Set a value for the Unit attribute of a Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void SetUnit( AstFrame *this, int axis, const char *unit, int *status ) + +* Class Membership: +* Frame method. + +* Description: +* This function sets the Unit value for a Frame. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which the Unit value is to +* be set. +* unit +* The new value to be set. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + char *c; /* Copy of supplied string */ + const char *oldunit; /* Pointer to old units string */ + int l; /* Used length of supplied string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a copy of the supplied string which excludes trailing spaces. */ + l = astChrLen( unit ); + c = astStore( NULL, unit, (size_t) (l + 1) ); + if( astOK ) { + c[ l ] = 0; + +/* Validate the axis index and obtain a pointer to the required Axis. */ + (void) astValidateAxis( this, axis, 1, "astSetUnit" ); + ax = astGetAxis( this, axis ); + +/* The new unit may require the Label and/or Symbol to be changed, but + only if the Frames ActiveUnit flag is set. */ + if( astGetActiveUnit( this ) ) { + +/* Get the existing Axis unit, using the astGetUnit method (rather than + astGetAxisUnit) in order to get any default value in the case where + the Unit attribute is not set. */ + oldunit = astGetUnit( this, axis ); + +/* Assign the new Unit value. This modifies labels and/or Symbols if + necessary. */ + NewUnit( ax, oldunit, c, "astSetUnit", astGetClass( this ), status ); + } + +/* Set the Axis Unit attribute value. */ + astSetAxisUnit( ax, c ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + } + +/* Free the string copy */ + c = astFree( c ); + +} + +static int SubFrame( AstFrame *target, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +*+ +* Name: +* astSubFrame + +* Purpose: +* Select axes from a Frame and convert to the new coordinate system. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astSubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result ) + +* Class Membership: +* Frame method. + +* Description: +* This function selects a requested sub-set (or super-set) of the axes from +* a "target" Frame and creates a new Frame with copies of the selected +* axes assembled in the requested order. It then optionally overlays the +* attributes of a "template" Frame on to the result. It returns both the +* resulting Frame and a Mapping that describes how to convert between the +* coordinate systems described by the target and result Frames. If +* necessary, this Mapping takes account of any differences in the Frames' +* attributes due to the influence of the template. + +* Parameters: +* target +* Pointer to the target Frame, from which axes are to be selected. +* template +* Pointer to the template Frame, from which new attributes for the +* result Frame are to be obtained. Optionally, this may be NULL, in +* which case no overlaying of template attributes will be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This number may +* be greater than or less than the number of axes in this Frame (or +* equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving a list +* of the (zero-based) axis indices of the axes to be selected from the +* target Frame. The order in which these are given determines the order +* in which the axes appear in the result Frame. If any of the values in +* this array is set to -1, the corresponding result axis will not be +* derived from the target Frame, but will be assigned default attributes +* instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This should +* contain a list of the template axes (given as zero-based axis indices) +* with which the axes of the result Frame are to be associated. This +* array determines which axes are used when overlaying axis-dependent +* attributes of the template on to the result. If any element of this +* array is set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not used and +* a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned Mapping. +* The forward transformation of this Mapping will describe how to +* convert coordinates from the coordinate system described by the target +* Frame to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is +* possible between the target and the result Frame. Otherwise zero +* is returned and *map and *result are returned as NULL (but this +* will not in itself result in an error condition). In general, +* coordinate conversion should always be possible if no template +* Frame is supplied but may not always be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Implementation Deficiencies: +* - Any axis selection is currently permitted. Probably this +* should be restricted so that each axis can only be selected +* once. The astValidateAxisSelection method will do this but +* currently there are bugs in the CmpFrame class that cause axis +* selections which will not pass this test. Install the validation +* when these are fixed. +*- + +* Implementation Notes: +* - This implementation addresses the selection of axes from a +* Frame class object. This simply results in another object of the +* same class and a Mapping which describes an axis permutation (or +* a unit Mapping as a special case). Changes of Frame attributes +* have no significance for coordinate values in this class, so do +* not affect the Mapping returned. +*/ + +/* Local Variables: */ + AstAxis *newaxis; /* Pointer to new Axis object */ + AstFrame *tempframe; /* Pointer to temporary Frame */ + AstMapping *aumap; /* A units Mapping for a single axis */ + AstMapping *numap; /* The new total units Mapping */ + AstMapping *umap; /* The total units Mapping */ + int *inperm; /* Pointer to permutation array */ + int *outperm; /* Pointer to permutation array */ + int match; /* Coordinate conversion possible? */ + int result_axis; /* Result Frame axis index */ + int target_axis; /* Target Frame axis index */ + int target_naxes; /* Number of target Frame axes */ + int unit; /* Unit Mapping appropriate? */ + int uunit; /* Is the "umap" Mapping a UnitMap? */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain the number of target Frame axes. */ + target_naxes = astGetNaxes( target ); + +/* Ensure we do not attempt to use a negative number of result axes. */ + if ( result_naxes < 0 ) result_naxes = 0; + +/* Create a temporary new Frame with the required number of axes. This will + have a default Axis object associated with each of its axes. We will + replace these where necessary with copies of the actual Axis objects we + require. */ + tempframe = astFrame( result_naxes, "", status ); + +/* Allocate memory to store two permutation arrays. These will be used to + construct the Mapping that relates the target and result Frames. */ + inperm = astMalloc( sizeof( int ) * (size_t) target_naxes ); + outperm = astMalloc( sizeof( int ) * (size_t) result_naxes ); + if ( astOK ) { + +/* Initialise the array that associates each target axis with the corresponding + result axis (filling it with the value -1 initially signifies no + associations). */ + for ( target_axis = 0; target_axis < target_naxes; target_axis++ ) { + inperm[ target_axis ] = -1; + } + +/* Loop through each axis in the result Frame and obtain the index of the axis + in the target Frame from which it is to be derived. */ + for ( result_axis = 0; result_axis < result_naxes; result_axis++ ) { + target_axis = target_axes[ result_axis ]; + +/* Check if the resulting axis index is valid. If not, this result axis is not + to be derived from any target axis, and it will therefore be left with its + default attributes. Make an entry in the appropriate permutation array to + indicate that this result axis is unassociated. */ + if ( ( target_axis < 0 ) || ( target_axis >= target_naxes ) ) { + outperm[ result_axis ] = -1; + +/* Otherwise, obtain a pointer to the target Axis object and modify the + temporary Frame so that its axis is associated with the same Axis object. + Annul the Axis pointer afterwards. */ + } else { + newaxis = astGetAxis( target, target_axis ); + astSetAxis( tempframe, result_axis, newaxis ); + newaxis = astAnnul( newaxis ); + +/* Update both permutation arrays to record the association between the target + and result axes. */ + outperm[ result_axis ] = target_axis; + inperm[ target_axis ] = result_axis; + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + +/* So far, we have only modified pointers in the temporary Frame to refer to + the target Frame's Axis objects. Since we will next modify these objects' + attributes, we must make a deep copy of the entire temporary Frame so that + we do not modify the target's axes. This copy now becomes our result Frame. + Annul the temporary one. */ + if ( astOK ) { + *result = astCopy( tempframe ); + tempframe = astAnnul( tempframe ); + +/* Invoke the target "astOverlay" method to overlay any remaining + attributes from the target Frame which are not associated with + individual axes (e.g. the Frame's Title and Domain). */ + astOverlay( target, target_axes, *result ); + +/* If a template Frame was supplied, also invoke its astOverlay method to + overlay its attributes on the result Frame. (Note that in this particular + case this has no effect other than transferring attributes. In general, + however, i.e. in derived classes, this process is vital to determining the + mapping below, whose main purpose is to convert between the target and + result Frames. These will have different attributes as a result of the + influence that the template has here.) */ + if ( template ) astOverlay( template, template_axes, *result ); + +/* We will next generate the Mapping that relates the target and result + Frames. If appropriate this should be a unit Mapping (UnitMap), so test if + the number of axes in both Frames is equal. */ + unit = ( target_naxes == result_naxes ); + +/* If so, check the contents of one of the permutation arrays to see if all + result axes are associated with the corresponding target axis (the converse + then also follows). If not, note this fact and quit checking. */ + if ( unit ) { + for ( result_axis = 0; result_axis < result_naxes; + result_axis++ ) { + if ( outperm[ result_axis ] != result_axis ) { + unit = 0; + break; + } + } + } + +/* If a unit Mapping is appropriate, then construct it. */ + if ( unit ) { + *map = (AstMapping *) astUnitMap( result_naxes, "", status ); + +/* Otherwise, construct a Mapping describing the axis permutation we have + produced. */ + } else { + *map = (AstMapping *) astPermMap( target_naxes, inperm, + result_naxes, outperm, NULL, + "", status ); + } + +/* Note that coordinate conversion is possible. */ + match = 1; + +/* If the ActiveUnit flag in both template and result Frame is non-zero, we + now modify the Mapping to take account of any differences in the Units + attributes of the target and results Frames. */ + if( template && astGetActiveUnit( template ) && + astGetActiveUnit( *result ) ) { + +/* Loop round the axes of the results Frame, accumulating a parallel CmpMap + ("umap") in which each Mapping is the 1-D Mapping which transforms the + Units of the corresponding target axis into the Units of the results + axis. */ + umap = NULL; + uunit = 1; + for( result_axis = 0; result_axis < result_naxes; result_axis++ ) { + +/* Find the index of the corresponding target axis. */ + if( unit ) { + target_axis = result_axis; + } else { + target_axis = outperm[ result_axis ]; + } + +/* Get the Unit string for both axes, and attempt to find a Mapping which + transforms values in the target units into the corresponding value in the + results units. If this results axis does not have a corresponding + target axis, then indicate that no units mapping can be found. */ + if( target_axis > -1 ) { + aumap = astUnitMapper( astGetUnit( target, target_axis ), + astGetUnit( *result, result_axis ), + NULL, NULL ); + } else { + aumap = NULL; + } + +/* If no Mapping could be found, annull the Mapping and leave the loop. + Otherwise, see if the Mapping is a UnitMap. If not, set a flag to indicate + that we have at least one non-unit map. */ + if( !aumap ) { + if( umap ) umap = astAnnul( umap ); + match = 0; + break; + } else { + if( !astIsAUnitMap( aumap ) ) uunit = 0; + } + +/* Add this Mapping into the parallel CmpMap. */ + if( umap ) { + numap = (AstMapping *) astCmpMap( umap, aumap, 0, "", status ); + umap = astAnnul( umap ); + aumap = astAnnul( aumap ); + umap = numap; + } else { + umap = aumap; + } + } + +/* If the resulting CmpMap is not just a UnitMap, add it in series with + the current results mapping, and then simplify it. */ + if( !uunit && umap ) { + numap = (AstMapping *) astCmpMap( *map, umap, 1, "", status ); + (void) astAnnul( *map ); + *map = numap; + } + +/* Annul the CmpMap containing the units Mappings. */ + if( umap ) umap = astAnnul( umap ); + +/* If the units could not bve matched annul the returned mapping. */ + if( !match && *map ) *map = astAnnul( *map ); + } + } + } + +/* Free the memory used for the permutation arrays. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + +/* If an error occurred, annul the returned objects and reset the returned + value. */ + if ( !astOK ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) { +/* +*+ +* Name: +* astSystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* AstSystemType SystemCode( AstFrame *this, const char *system ) + +* Class Membership: +* Frame method. + +* Description: +* This function converts a string used for the external description of +* a coordinate system into a Frame coordinate system type code (System +* attribute value). It is the inverse of the astSystemString function. + +* Parameters: +* this +* Pointer to the Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the coordinate system. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the coordinate system +* description was not recognised. This does not produce an error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*- +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" string against each possibility and assign the + result. The basic Frame class only supports a single system + "Cartesian". */ + if ( astChrMatch( "Cartesian", system ) ) { + result = AST__CART; + } + +/* Return the result. */ + return result; +} + +static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) { +/* +*+ +* Name: +* astSystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* const char *astSystemString( AstFrame *this, AstSystemType system ) + +* Class Membership: +* Frame method. + +* Description: +* This function converts a Frame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The coordinate system type code. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*- +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. (Where possible, return the same string as would be + used in the FITS WCS representation of the coordinate system). A basic + Frame only allows a single System value, "Cartesian". */ + switch ( system ) { + case AST__CART: + result = "Cartesian"; + break; + } + +/* Return the result pointer. */ + return result; + +} + +static int TestActiveUnit( AstFrame *this, int *status ){ +/* +*+ +* Name: +* astTestActiveUnit + +* Purpose: +* Determines if the ActiveUnit flag is set. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astTestActiveUnit( AstFrame *this ) + +* Class Membership: +* Frame method. + +* Description: +* This function tests the current value of the ActiveUnit flag for a +* Frame. See the description of the astSetActiveUnit function for a +* description of the ActiveUnit flag. + +* Parameters: +* this +* Pointer to the Frame. + +* Returned Value: +* Non-zero if the flag has been set. Zero otherwise. + +* Notes: +* - A zero value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*-- +*/ + +/* Local Variables: */ + int result; /* The returned value */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Return the result. */ + return ( this->active_unit != -INT_MAX ); +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Frame member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Frame's attributes. + +* Parameters: +* this +* Pointer to the Frame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis */ + AstFrame *pfrm; /* Pointer to primary Frame containing axis */ + AstFrame *this; /* Pointer to the Frame structure */ + char pfrm_attrib[ 100 ]; /* Primary Frame attribute */ + char *axis_attrib; /* Pointer to axis attribute name */ + const char *old_attrib; /* Pointer to supplied attribute name string */ + int axis; /* Frame axis number */ + int axis_nc; /* No. characters in axis attribute name */ + int free_axis_attrib; /* Should axis_attrib be freed? */ + int has_axis; /* Does attrib name include axis specifier? */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + int oldrep; /* Original error reporting state */ + int paxis; /* Axis index within primary frame */ + int result; /* Result value to return */ + int used; /* Could the setting string be used? */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_object; + +/* Set a flag indicating if the attribute name includes an axis + specifier. */ + has_axis = ( strchr( attrib, '(' ) != NULL ); + +/* A flag indicating that we do not need to free the axis_attrib memory. */ + free_axis_attrib = 0; + +/* Initialise things to avoid compiler warnings. */ + axis_attrib = NULL; + old_attrib = NULL; + +/* Jump back to here if we are trying the same attribute but with an explicit + axis "(1)" added to the end of the name. */ +L1: + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* Digits. */ +/* ------- */ + if ( !strcmp( attrib, "digits" ) ) { + result = astTestDigits( this ); + +/* Digits(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "digits(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + +/* There is no function to test the Digits attribute for an axis + directly, so obtain a pointer to the Axis and use this to test the + attribute. */ + (void) astValidateAxis( this, axis - 1, 1, "astTestDigits(axis)" ); + ax = astGetAxis( this, axis - 1 ); + result = astTestAxisDigits( ax ); + ax = astAnnul( ax ); + +/* Direction(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "direction(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestDirection( this, axis - 1 ); + +/* Epoch. */ +/* ------ */ + } else if ( !strcmp( attrib, "epoch" ) ) { + result = astTestEpoch( this ); + +/* Bottom(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "bottom(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestBottom( this, axis - 1 ); + +/* Top(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "top(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestTop( this, axis - 1 ); + +/* Domain. */ +/* ------- */ + } else if ( !strcmp( attrib, "domain" ) ) { + result = astTestDomain( this ); + +/* Format(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "format(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestFormat( this, axis - 1 ); + +/* Label(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "label(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLabel( this, axis - 1 ); + +/* MatchEnd. */ +/* --------- */ + } else if ( !strcmp( attrib, "matchend" ) ) { + result = astTestMatchEnd( this ); + +/* MaxAxes. */ +/* -------- */ + } else if ( !strcmp( attrib, "maxaxes" ) ) { + result = astTestMaxAxes( this ); + +/* MinAxes. */ +/* -------- */ + } else if ( !strcmp( attrib, "minaxes" ) ) { + result = astTestMinAxes( this ); + +/* Permute. */ +/* -------- */ + } else if ( !strcmp( attrib, "permute" ) ) { + result = astTestPermute( this ); + +/* PreserveAxes. */ +/* ------------- */ + } else if ( !strcmp( attrib, "preserveaxes" ) ) { + result = astTestPreserveAxes( this ); + +/* Symbol(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "symbol(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestSymbol( this, axis - 1 ); + +/* AlignSystem. */ +/* ------------ */ + } else if ( !strcmp( attrib, "alignsystem" ) ) { + result = astTestAlignSystem( this ); + +/* System. */ +/* ------- */ + } else if ( !strcmp( attrib, "system" ) ) { + result = astTestSystem( this ); + +/* Title. */ +/* ------ */ + } else if ( !strcmp( attrib, "title" ) ) { + result = astTestTitle( this ); + +/* Unit(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "unit(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestUnit( this, axis - 1 ); + +/* ObsLat. */ +/* ------- */ + } else if ( !strcmp( attrib, "obslat" ) ) { + result = astTestObsLat( this ); + +/* ObsLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "obslon" ) ) { + result = astTestObsLon( this ); + +/* ObsAlt. */ +/* ------- */ + } else if ( !strcmp( attrib, "obsalt" ) ) { + result = astTestObsAlt( this ); + +/* Dut1. */ +/* ---- */ + } else if ( !strcmp( attrib, "dut1" ) ) { + result = astTestDut1( this ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute name matches any of the read-only attributes + of this class. If it does, then return zero. */ + } else if ( !strcmp( attrib, "naxes" ) || + !strncmp( attrib, "normunit", 8 ) || + !strncmp( attrib, "internalunit", 12 ) ) { + result = 0; + +/* Other axis attributes. */ +/* ---------------------- */ +/* If the attribute was not identified above, but appears to refer to + a Frame axis, then it may refer to an Axis object of a derived type + (which has additional attributes not recognised here). */ + } else if ( !free_axis_attrib && ( nc = 0, + ( 1 == astSscanf( attrib, "%*[^()]%n(%d)%n", + &axis_nc, &axis, &nc ) ) + && ( nc >= len ) ) ) { + +/* Validate the axis index and extract the attribute name. */ + (void) astValidateAxis( this, axis - 1, 1, "astTest" ); + axis_attrib = astString( attrib, axis_nc ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis - 1 ); + if( astOK ) { + +/* Assume that we will be able to use the attribute name. */ + used = 1; + +/* Temporarily switch off error reporting so that if the following attempt + to access the axis attribute fails, we can try to interpret the + attribute name as an attribute of the primary Frame containing the + specified axis. Any errors reported in this context will simply be + ignored, in particularly they are not deferred for later delivery. */ + oldrep = astReporting( 0 ); + +/* Use the Axis astTestAttrib method to test the attribute value. */ + result = astTestAttrib( ax, axis_attrib ); + +/* If the above call failed with a status of AST__BADAT, indicating that + the attribute name was not recognised, clear the status so that we can + try to interpret the attribute name as an attribute of the primary Frame + containing the specified axis. */ + if( astStatus == AST__BADAT ) { + astClearStatus; + +/* Find the primary Frame containing the specified axis. */ + astPrimaryFrame( this, axis - 1, &pfrm, &paxis ); + +/* Only attempt to use the primary Frame if it is not the same as "this" + - otherwise we could end up in an infinite loop. */ + if( pfrm != this ) { + +/* astPrimaryFrame returns the original - unpermuted - axis index within + the primary Frame. So we need to take into account any axis permutation + which has been applied to the primary Frame when forming the attribute name + to use below. Find the permuted (external) axis index which corresponds to + the internal (unpermuted) axis index "paxis". */ + paxis = astValidateAxis( pfrm, paxis, 0, "astTest" ); + +/* Modify the attribute name to refer to the axis numbering of the + primary frame. */ + sprintf( pfrm_attrib, "%s(%d)", axis_attrib, paxis + 1 ); + +/* Attempt to test the attribute as an attribute of the primary Frame. */ + result = astTestAttrib( pfrm, pfrm_attrib ); + +/* If this failed, clear the status and indicate that we have not managed to + use the attribute name. */ + if( !astOK ) { + astClearStatus; + used = 0; + } + + } else { + used = 0; + } + +/* If not found attempt to test the attribute value in the Axis, omitting + the axis index. */ + if( ! used ) { + result = astTestAttrib( pfrm, axis_attrib ); + if( !astOK ) { + astClearStatus; + } else { + used = 1; + } + } + +/* Annul the primary Frame pointer. */ + pfrm = astAnnul( pfrm ); + } + +/* Re-instate the original error reporting state. */ + astReporting( oldrep ); + +/* If we could not use the attribute name, attempt to test the axis + attribute again, this time retaining the error report. This is done + to ensure the user gets an appropriate error message. */ + if( !used ) result = astTestAttrib( ax, axis_attrib ); + } + +/* Annul the Axis pointer and free the memory holding the attribute + name. */ + ax = astAnnul( ax ); + axis_attrib = astFree( axis_attrib ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, and the Frame has only 1 axis, + and the attribute name does not already include an axis specifier, try + again after appending "(1)" to the end of the attribute name. */ + } else if( !has_axis && astGetNaxes( this ) == 1 ) { + +/* Take a copy of the supplied name, allowing 3 extra characters for the + axis specifier "(1)". */ + axis_attrib = astMalloc( len + 4 ); + if( axis_attrib ) memcpy( axis_attrib, attrib, len ); + +/* Indicate we should free the axis_attrib memory. */ + free_axis_attrib = 1; + +/* Add in the axis specifier. */ + strcpy( axis_attrib + len, "(1)" ); + +/* Use the new attribute name instead of the supplied name. */ + old_attrib = attrib; + attrib = axis_attrib; + +/* Indicate the attribute name now has an axis specifier. */ + has_axis = 1; + +/* Jump back to try interpreting the new attribute name. */ + goto L1; + +/* Not recognised. */ +/* --------------- */ +/* If the attribute name is still not recognised, pass it on to the parent + method for further interpretation. First re-instate the original attrib + name string if it was changed above. */ + } else { + if( free_axis_attrib ) { + attrib = old_attrib; + axis_attrib = astFree( axis_attrib ); + } + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Use a Frame to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Frame member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a Frame and a set of points encapsulated in a +* PointSet and transforms the points so as to perform the identity +* transformation (i.e. simply copies the coordinate values). + +* Parameters: +* this +* Pointer to the Frame. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. In this case, both transformations are equivalent. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the Frame being applied. This number +* will be equal to the number of Frame axes. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstFrame *this; /* Pointer to the Frame structure */ + AstPointSet *result; /* Pointer value to be returned */ + AstUnitMap *unitmap; /* Pointer to temporary UnitMap */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_mapping; + +/* Create a unit Mapping with one coordinate for each Frame axis. */ + unitmap = astUnitMap( astGetNaxes( this ), "", status ); + +/* Use the Mapping to transform (i.e. copy) the coordinate values. */ + result = astTransform( unitmap, in, forward, out ); + +/* Annul the Mapping. */ + unitmap = astAnnul( unitmap ); + +/* If an error occurred and a new PointSet may have been created, then annul + the result. In any case, ensure that a NULL pointer is returned. */ + if ( !astOK ) { + if ( !out ) result = astAnnul( result ); + result = NULL; + } + +/* Return the result pointer. */ + return result; +} + +static int Unformat( AstFrame *this, int axis, const char *string, + double *value, int *status ) { +/* +*+ +* Name: +* astUnformat + +* Purpose: +* Read a formatted coordinate value for a Frame axis. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astUnformat( AstFrame *this, int axis, const char *string, +* double *value ) + +* Class Membership: +* Frame method. + +* Description: +* This function reads a formatted coordinate value for a Frame +* axis (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the Frame axis for which the coordinate value +* is to be read (axis numbering starts at zero for the first +* axis). +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will be +* returned. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic astUnformat method +* available via the protected interface to the Frame class. The +* public interface to this method is provided by the +* astUnformatId_ function. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + const char *label; /* Pointer to axis label string */ + double coord; /* Coordinate value read */ + int digits_set; /* Axis Digits attribute set? */ + int nc; /* Number of characters read */ + int status_value; /* AST error status */ + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Validate the axis index and obtain a pointer to the required Axis. */ + (void) astValidateAxis( this, axis, 1, "astUnformat" ); + ax = astGetAxis( this, axis ); + +/* Test if any Axis attributes which may affect the result are + undefined (i.e. have not been explicitly set). If so, we over-ride + them, giving them temporary values dictated by the Frame. Only the + Digits attribute is potentially relevant here. */ + digits_set = astTestAxisDigits( ax ); + if ( !digits_set ) astSetAxisDigits( ax, astGetDigits( this ) ); + +/* Read the coordinate value. */ + if ( astOK ) { + nc = astAxisUnformat( ax, string, &coord ); + +/* If an error occurred, save and temporarily clear the global error + status while the axis Label string is obtained. Then restore the + original error status value afterwards. */ + if ( !astOK ) { + status_value = astStatus; + astClearStatus; + label = astGetLabel( this, axis ); + astSetStatus( status_value ); + +/* Report a contextual error message containing the axis label. */ + astError( status_value, "%s(%s): Unable to read \"%s\" value.", status, + "astUnformat", astGetClass( this ), label ); + } + } + +/* Clear any Axis attributes that were temporarily over-ridden. */ + if ( !digits_set ) astClearAxisDigits( ax ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the count of characters read. */ + if ( !astOK ) { + nc = 0; + +/* Otherwise, if characters were read, return the coordinate value. */ + } else if ( nc ) { + *value = coord; + } + +/* Return the number of characters read. */ + return nc; +} + +static int ValidateAxis( AstFrame *this, int axis, int fwd, const char *method, + int *status ) { +/* +*+ +* Name: +* astValidateAxis + +* Purpose: +* Validate and permute a Frame's axis index. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astValidateAxis( AstFrame *this, int axis, int fwd, +* const char *method ) + +* Class Membership: +* Frame method. + +* Description: +* This function checks the validity of an index (zero-based) which +* is to be used to address one of the coordinate axes of a +* Frame. If the index is valid, it is permuted using the axis +* permutation array associated with the Frame and the (zero-based) +* permuted axis index is returned. This gives the location of the +* required axis information within the Frame's internal arrays. If +* the axis index supplied is not valid, an error is reported and +* the global error status is set. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The axis index (zero-based) to be checked. To be valid, it +* must lie between zero and (naxes-1) inclusive, where "naxes" +* is the number of coordinate axes associated with the Frame. +* fwd +* If non-zero, the suppplied axis index is assumed to be an +* "external" axis index, and the corresponding "internal" axis index +* is returned as the function value. Otherwise, the suppplied axis +* index is assumed to be an "internal" axis index, and the +* corresponding "external" axis index is returned as the function +* value. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. + +* Returned Value: +* The permuted axis index - either "internal" or "external" as +* specified by "fwd". + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - Error messages issued by this function refer to the public +* numbering system used for axes which is one-based (zero-based axis +* indices are used internally). +*- +*/ + +/* Local Variables: */ + const int *perm; /* Pointer to axis permutation array */ + int naxes; /* Number of Frame axes */ + int result; /* Permuted axis index */ + +/* Initialise. */ + result = 0; + +/* Determine the number of Frame axes. */ + naxes = astGetNaxes( this ); + if ( astOK ) { + +/* If the Frame has no axes, report an error (note we convert to + one-based axis numbering in the error message). */ + if ( naxes == 0 ) { + astError( AST__AXIIN, "%s(%s): Invalid attempt to use an axis index " + "(%d) for a %s which has no axes.", status, method, + astGetClass( this ), axis + 1, astGetClass( this ) ); + +/* Otherwise, check the axis index for validity and report an error if + it is not valid (again, use one-based axis numbering). */ + } else if ( ( axis < 0 ) || ( axis >= naxes ) ) { + astError( AST__AXIIN, "%s(%s): Axis index (%d) invalid - it should " + "be in the range 1 to %d.", status, method, astGetClass( this ), + axis + 1, naxes ); + +/* If the axis index was valid, obtain the axis permutation array and + use this to generate the permuted axis value. */ + } else { + perm = astGetPerm( this ); + if( perm ) { + +/* External to internal is a simple look-up. */ + if( fwd ) { + result = perm[ axis ]; + +/* Internal to external requires a search through the permutation array. */ + } else { + for( result = 0; result < naxes; result++ ) { + if( perm[ result ] == axis ) break; + } + } + } + } + } + +/* Return the result. */ + return result; +} + +static void ValidateAxisSelection( AstFrame *this, int naxes, const int *axes, + const char *method, int *status ) { +/* +*+ +* Name: +* astValidateAxisSelection + +* Purpose: +* Check that a set of axes selected from a Frame is valid. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* void astValidateAxisSelection( AstFrame *this, int naxes, +* const int *axes, const char *method ) + +* Class Membership: +* Frame method. + +* Description: +* This function checks the validity of an array of (zero-based) +* axis indices that specify a set of axes to be selected from a +* Frame. To be valid, no axis should be selected more than +* once. In assessing this, any axis indices that do not refer to +* valid Frame axes (e.g. are set to -1) are ignored. +* +* If the axis selection is valid, this function returns without further +* action. Otherwise, an error is reported and the global error status is +* set. + +* Parameters: +* this +* Pointer to the Frame. +* naxes +* The number of axes to be selected (may be zero). +* axes +* Pointer to an array of int with naxes elements that contains the +* (zero based) axis indices to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis selection. This method name is used +* solely for constructing error messages. +*- +*/ + +/* Local Variables: */ + int *count; /* Pointer to temporary array of counts */ + int axis; /* Loop counter for selected axes */ + int frame_axis; /* Loop counter for Frame axes */ + int frame_naxes; /* Number of Frame axes */ + int valid; /* Axis selection valid? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check to see if no axes have been selected. If so, there is nothing to + do. */ + if ( naxes ) { + +/* Initialise. */ + valid = 1; + +/* Obtain the number of Frame axes and allocate an array of int with + one element for each Frame axis. This will store a count of the + number of times each axis is selected. */ + frame_naxes = astGetNaxes( this ); + count = astMalloc( sizeof( int ) * (size_t) frame_naxes ); + if ( astOK ) { + +/* Initialise the array of counts to zero. */ + for ( frame_axis = 0; frame_axis < frame_naxes; frame_axis++ ) { + count[ frame_axis ] = 0; + } + +/* Loop through each selected axis. */ + for ( axis = 0; axis < naxes; axis++ ) { + frame_axis = axes[ axis ]; + +/* Check if the selected axis index is valid for the Frame. If so, increment + the selection count for that Frame axis. */ + if ( ( frame_axis >= 0 ) && ( frame_axis < frame_naxes ) ) { + count[ frame_axis ]++; + } + } + +/* Loop through the count array and check that no Frame axis was selected + more than once. If it was, clear the "valid" flag and quit checking. */ + for ( frame_axis = 0; frame_axis < frame_naxes; frame_axis++ ) { + if ( count[ frame_axis ] > 1 ) { + valid = 0; + break; + } + } + } + +/* Free the temporary count array. */ + count = astFree( count ); + +/* If no error has occurred, but the axis selection is not valid, then report + an error. */ + if ( astOK && !valid ) { + astError( AST__SELIN, "%s(%s): Invalid axis selection - each axis " + "may be selected only once.", status, method, astGetClass( this ) ); + } + } +} + +static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) { +/* +*+ +* Name: +* astValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int astValidateSystem( AstFrame *this, AstSystemType system, +* const char *method ) + +* Class Membership: +* Frame method. + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST_BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the value is out of bounds, report an error. */ + if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) { + astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System " + "or AlignSystem attribute of a %s.", status, method, + astGetClass( this ), (int) system, astGetClass( this ) ); + +/* Otherwise, return the supplied value. */ + } else { + result = system; + } + +/* Return the result. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + the axes of a Frame using the private macros defined for this + purpose at the start of this file. */ + +/* +*att++ +* Name: +* Naxes + +* Purpose: +* Number of Frame axes. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This is a read-only attribute giving the number of axes in a +* Frame (i.e. the number of dimensions of the coordinate space +* which the Frame describes). This value is determined when the +* Frame is created. + +* Applicability: +* Frame +* All Frames have this attribute. +* FrameSet +* The Naxes attribute of a FrameSet is the same as that of its +* current Frame (as specified by the Current attribute). +* CmpFrame +* The Naxes attribute of a CmpFrame is equal to the sum of the +* Naxes values of its two component Frames. +*att-- +*/ + + +/* +*att++ +* Name: +* Direction(axis) + +* Purpose: +* Display axis in conventional direction? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which suggests how the axes of +* a Frame should be displayed (e.g.) in graphical output. By +* default, it has the value one, indicating that they should be +* shown in the conventional sense (increasing left to right for an +* abscissa, and bottom to top for an ordinate). If set to zero, +* this attribute indicates that the direction should be reversed, +* as would often be done for an astronomical magnitude or a right +* ascension axis. + +* Applicability: +* Frame +* The default Direction value supplied by the Frame class is 1, +* indicating that all axes should be displayed in the +* conventional direction. +* SkyFrame +* The SkyFrame class re-defines the default Direction value to +* suggest that certain axes (e.g. right ascension) should be +* plotted in reverse when appropriate. +* FrameSet +* The Direction attribute of a FrameSet axis is the same as +* that of its current Frame (as specified by the Current +* attribute). +* Plot +* The Direction attribute of the base Frame in a Plot is set to +* indicate the sense of the two graphics axes, as implied by the +* graphics bounding box supplied when the Plot was created. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +* - The Direction attribute does not directly affect the behaviour +* of the AST library. Instead, it serves as a hint to applications +* programs about the orientation in which they may wish to display +* any data associated with the Frame. Applications are free to +* ignore this hint if they wish. +*att-- +*/ +/* This simply provides an interface to the Axis methods for accessing + the Direction flag. */ +MAKE_CLEAR(Direction) +MAKE_GET(Direction,int,0,0,0) +MAKE_SET(Direction,int) +MAKE_TEST(Direction) + +/* +*att++ +* Name: +* Dut1 + +* Purpose: +* The UT1-UTC correction. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute is used when calculating the Local Apparent Sidereal +* Time corresponding to SkyFrame's Epoch value (used when converting +* positions to or from the "AzEl" system). It should be set to the +* difference, in seconds, between the UT1 and UTC timescales at the +* moment in time represented by the SkyFrame's Epoch attribute. The +* value to use is unpredictable and depends on changes in the earth's +* rotation speed. Values for UT1-UTC can be obtained from the +* International Earth Rotation and Reference Systems Service +* (IERS) at http://www.iers.org/. +* +* Currently, the correction is always less than 1 second. This is +* ensured by the occasional introduction of leap seconds into the UTC +* timescale. Therefore no great error will usually result if no value +* is assigned to this attribute (in which case a default value of +* zero is used). However, it is possible that a decision may be taken +* at some time in the future to abandon the introduction of leap +* seconds, in which case the DUT correction could grow to significant +* sizes. + +* Applicability: +* Frame +* All Frames have this attribute. + +*att-- +*/ +/* The UT1-UTC correction, in seconds. Has a value of AST__BAD when not set + yielding a default value of 0.0. */ +astMAKE_CLEAR(Frame,Dut1,dut1,AST__BAD) +astMAKE_GET(Frame,Dut1,double,0.0,(this->dut1 == AST__BAD ? 0.0 : this->dut1)) +astMAKE_SET(Frame,Dut1,double,dut1,value) +astMAKE_TEST(Frame,Dut1,( this->dut1 != AST__BAD )) + + + +/* +*att++ +* Name: +* Epoch + +* Purpose: +* Epoch of observation. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute is used to qualify the coordinate systems described by +* a Frame, by giving the moment in time when the coordinates are known +* to be correct. Often, this will be the date of observation, and is +* important in cases where coordinates systems move with respect to each +* other over the course of time. +* +* The Epoch attribute is stored as a Modified Julian Date, but +* when setting its value it may be given in a variety of +* formats. See the "Input Formats" section (below) for details. +* Strictly, the Epoch value should be supplied in the TDB timescale, +* but for some purposes (for instance, for converting sky positions +* between different types of equatorial system) the timescale is not +* significant, and UTC may be used. + +* Input Formats: +* The formats accepted when setting an Epoch value are listed +* below. They are all case-insensitive and are generally tolerant +* of extra white space and alternative field delimiters: +* +* - Besselian Epoch: Expressed in decimal years, with or without +* decimal places ("B1950" or "B1976.13" for example). +* +* - Julian Epoch: Expressed in decimal years, with or without +* decimal places ("J2000" or "J2100.9" for example). +* +* - Year: Decimal years, with or without decimal places ("1996.8" +* for example). Such values are interpreted as a Besselian epoch +* (see above) if less than 1984.0 and as a Julian epoch otherwise. +* +* - Julian Date: With or without decimal places ("JD 2454321.9" for +* example). +* +* - Modified Julian Date: With or without decimal places +* ("MJD 54321.4" for example). +* +* - Gregorian Calendar Date: With the month expressed either as an +* integer or a 3-character abbreviation, and with optional decimal +* places to represent a fraction of a day ("1996-10-2" or +* "1996-Oct-2.6" for example). If no fractional part of a day is +* given, the time refers to the start of the day (zero hours). +* +* - Gregorian Date and Time: Any calendar date (as above) but with +* a fraction of a day expressed as hours, minutes and seconds +* ("1996-Oct-2 12:13:56.985" for example). The date and time can be +* separated by a space or by a "T" (as used by ISO8601 format). + +* Output Format: +* When enquiring Epoch values, the format used is the "Year" +* format described under "Input Formats". This is a value in +* decimal years which will be a Besselian epoch if less than +* 1984.0 and a Julian epoch otherwise. By omitting any character +* prefix, this format allows the Epoch value to be obtained as +* either a character string or a floating point value. + +* Applicability: +* Frame +* All Frames have this attribute. The basic Frame class provides +* a default of J2000.0 (Julian) but makes no use of the Epoch value. +* This is because the Frame class does not distinguish between +* different Cartesian coordinate systems (see the System attribute). +* CmpFrame +* The default Epoch value for a CmpFrame is selected as follows; +* if the Epoch attribute has been set in the first component Frame +* then the Epoch value from the first component Frame is used as +* the default for the CmpFrame. Otherwise, if the Epoch attribute has +* been set in the second component Frame then the Epoch value from the +* second component Frame is used as the default for the CmpFrame. +* Otherwise, the default Epoch value from the first component +* Frame is used as the default for the CmpFrame. When the Epoch +* attribute of a CmpFrame is set or cleared, it is also set or +* cleared in the two component Frames. +* FrameSet +* The Epoch attribute of a FrameSet is the same as that of its current +* Frame (as specified by the Current attribute). +* SkyFrame +* The coordinates of sources within a SkyFrame can changed with time +* for various reasons, including: (i) changing aberration of light +* caused by the observer's velocity (e.g. due to the Earth's motion +* around the Sun), (ii) changing gravitational deflection by the Sun +* due to changes in the observer's position with time, (iii) fictitious +* motion due to rotation of non-inertial coordinate systems (e.g. the +* old FK4 system), and (iv) proper motion of the source itself (although +* this last effect is not handled by the SkyFrame class because it +* affects individual sources rather than the coordinate system as +* a whole). +* +* The default Epoch value in a SkyFrame is B1950.0 (Besselian) for the +* old FK4-based coordinate systems (see the System attribute) and +* J2000.0 (Julian) for all others. +* +* Care must be taken to distinguish the Epoch value, which relates to +* motion (or apparent motion) of the source, from the superficially +* similar Equinox value. The latter is used to qualify a coordinate +* system which is itself in motion in a (notionally) predictable way +* as a result of being referred to a slowly moving reference plane +* (e.g. the equator). +* +* See the description of the System attribute for details of which +* qualifying attributes apply to each celestial coordinate system. +* TimeFrame +* A TimeFrame describes a general time axis and so cannot be completely +* characterised by a single Epoch value. For this reason the TimeFrame +* class makes no use of the Epoch attribute. However, user code can +* still make use of the attribute if necessary to represent a "typical" +* time spanned by the TimeFrame. The default Epoch value for a TimeFrame +* will be the TDB equivalent of the current value of the TimeFrame's +* TimeOrigin attribute. If no value has been set for TimeOrigin, +* then the default Epoch value is J2000.0. + + +The coordinates of sources within a SkyFrame can changed with time +*att-- +*/ +/* Clear the Epoch value by setting it to AST__BAD. */ +astMAKE_CLEAR(Frame,Epoch,epoch,AST__BAD) + +/* Provide a default value of J2000.0 setting. */ +astMAKE_GET(Frame,Epoch,double,AST__BAD,( + ( this->epoch != AST__BAD ) ? this->epoch : palEpj2d( 2000.0 ))) + +/* Allow any Epoch value to be set. */ +astMAKE_SET(Frame,Epoch,double,epoch,value) + +/* An Epoch value is set if it is not equal to AST__BAD. */ +astMAKE_TEST(Frame,Epoch,( this->epoch != AST__BAD )) + +/* +*att++ +* Name: +* Top(axis) + +* Purpose: +* Highest axis value to display + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute gives the highest axis value to be displayed (for +c instance, by the astGrid method). +f instance, by the AST_GRID method). + +* Applicability: +* Frame +* The default supplied by the Frame class is to display all axis +* values, without any limit. +* SkyFrame +* The SkyFrame class re-defines the default Top value to +90 degrees +* for latitude axes, and 180 degrees for co-latitude axes. The +* default for longitude axes is to display all axis values. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* This simply provides an interface to the Axis methods for accessing + the Top value. */ +MAKE_CLEAR(Top) +MAKE_GET(Top,double,DBL_MAX,0,DBL_MAX) +MAKE_SET(Top,double) +MAKE_TEST(Top) + +/* +*att++ +* Name: +* Bottom(axis) + +* Purpose: +* Lowest axis value to display + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute gives the lowest axis value to be displayed (for +c instance, by the astGrid method). +f instance, by the AST_GRID method). + +* Applicability: +* Frame +* The default supplied by the Frame class is to display all axis +* values, without any limit. +* SkyFrame +* The SkyFrame class re-defines the default Bottom value to -90 degrees +* for latitude axes, and 0 degrees for co-latitude axes. The +* default for longitude axes is to display all axis values. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* This simply provides an interface to the Axis methods for accessing + the Bottom value. */ +MAKE_CLEAR(Bottom) +MAKE_GET(Bottom,double,-DBL_MAX,0,-DBL_MAX) +MAKE_SET(Bottom,double) +MAKE_TEST(Bottom) + +/* +*att++ +* Name: +* Format(axis) + +* Purpose: +* Format specification for axis values. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the format to be used when displaying +* coordinate values associated with a particular Frame axis +* (i.e. to convert values from binary to character form). It is +c interpreted by the astFormat function and determines the +f interpreted by the AST_FORMAT function and determines the +* formatting which it applies. +* +* If no Format value is set for a Frame axis, a default value is +* supplied instead. This is based on the value of the Digits, or +* Digits(axis), attribute and is chosen so that it displays the +* requested number of digits of precision. + +* Applicability: +* Frame +* The Frame class interprets this attribute as a format +* specification string to be passed to the C "printf" function +* (e.g. "%1.7G") in order to format a single coordinate value +* (supplied as a double precision number). +c +c When supplying a value for this attribute, beware that the +c "%" character may be interpreted directly as a format +c specification by some printf-like functions (such as +c astSet). You may need to double it (i.e. use "%%") to avoid +c this. +* SkyFrame +* The SkyFrame class re-defines the syntax and default value of +* the Format string to allow the formatting of sexagesimal +* values as appropriate for the particular celestial coordinate +* system being represented. The syntax of SkyFrame Format +* strings is described (below) in the "SkyFrame Formats" +* section. +* FrameSet +* The Format attribute of a FrameSet axis is the same as that +* of its current Frame (as specified by the Current +* attribute). Note that the syntax of the Format string is also +* determined by the current Frame. +* TimeFrame +* The TimeFrame class extends the syntax of the Format string to +* allow the formatting of TimeFrame axis values as Gregorian calendar +* dates and times. The syntax of TimeFrame Format strings is described +* (below) in the "TimeFrame Formats" section. + +* SkyFrame Formats: +* The Format string supplied for a SkyFrame should contain zero or +* more of the following characters. These may occur in any order, +* but the following is recommended for clarity: +* +* - "+": Indicates that a plus sign should be prefixed to positive +* values. By default, no plus sign is used. +* +* - "z": Indicates that leading zeros should be prefixed to the +* value so that the first field is of constant width, as would be +* required in a fixed-width table (leading zeros are always +* prefixed to any fields that follow). By default, no leading +* zeros are added. +* +* - "i": Use the standard ISO field separator (a colon) between +* fields. This is the default behaviour. +* +* - "b": Use a blank to separate fields. +* +* - "l": Use a letter ("h"/"d", "m" or "s" as appropriate) to +* separate fields. +* +* - "g": Use a letter and symbols to separate fields ("h"/"d", "m" or "s", +* etc, as appropriate), but include escape sequences in the formatted +* value so that the Plot class will draw the separators as small +* super-scripts. +c The default escape sequences are optimised for the pgplot graphics +c package, but new escape sequences may be specified using function +c astSetSkyDelim. +* +* - "d": Include a degrees field. Expressing the angle purely in +* degrees is also the default if none of "h", "m", "s" or "t" are +* given. +* +* - "h": Express the angle as a time and include an hours field +* (where 24 hours correspond to 360 degrees). Expressing the angle +* purely in hours is also the default if "t" is given without +* either "m" or "s". +* +* - "m": Include a minutes field. By default this is not included. +* +* - "s": Include a seconds field. By default this is not included. +* This request is ignored if "d" or "h" is given, unless a minutes +* field is also included. +* +* - "t": Express the angle as a time (where 24 hours correspond to +* 360 degrees). This option is ignored if either "d" or "h" is +* given and is intended for use where the value is to be expressed +* purely in minutes and/or seconds of time (with no hours +* field). If "t" is given without "d", "h", "m" or "s" being +* present, then it is equivalent to "h". +* +* - ".": Indicates that decimal places are to be given for the +* final field in the formatted string (whichever field this +* is). The "." should be followed immediately by an unsigned +* integer which gives the number of decimal places required, or by an +* asterisk. If an asterisk is supplied, a default number of decimal +* places is used which is based on the value of the Digits +* attribute. +* +* All of the above format specifiers are case-insensitive. If +* several characters make conflicting requests (e.g. if both "i" +* and "b" appear), then the character occurring last takes +* precedence, except that "d" and "h" always override "t". +* +* If the format string starts with a percentage sign (%), then the +* whole format string is assumed to conform to the syntax defined by +* the Frame class, and the axis values is formated as a decimal +* radians value. + +* TimeFrame Formats: +* The Format string supplied for a TimeFrame should either use the +* syntax defined by the base Frame class (i.e. a C "printf" format +* string), or the extended "iso" syntax described below (the default +* value is inherited from the Frame class): +* +* - C "printf" syntax: If the Format string is a C "printf" format +* description such as "%1.7G", the TimeFrame axis value will be +* formatted without change as a floating point value using this format. +* The formatted string will thus represent an offset from the zero point +* specified by the TimeFrame's TimeOrigin attribute, measured in +* units given by the TimeFrame's Unit attribute. +* +* - "iso" syntax: This is used to format a TimeFrame axis value as a +* Gregorian date followed by an optional time of day. If the Format +* value commences with the string "iso" then the TimeFrame axis value +* will be converted to an absolute MJD, including the addition of the +* current TimeOrigin value, and then formatted as a Gregorian date +* using the format "yyyy-mm-dd". Optionally, the Format value may +* include an integer precision following the "iso" specification (e.g. +* "iso.2"), in which case the time of day will be appended to the +* formatted date (if no time of day is included, the date field is +* rounded to the nearest day). The integer value in the Format string +* indicates the number of decimal places to use in the seconds field. For +* instance, a Format value of "iso.0" produces a time of day of the form +* "hh:mm:ss", and a Format value of "iso.2" produces a time of day of the +* form "hh:mm:ss.ss". The date and time fields will be separated by a +* space unless 'T' is appended to the end of string, in which case +* the letter T (upper case) will be used as the separator. The value of +* the Digits attribute is ignored when using this "iso" format. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* This simply provides an interface to the Axis methods for accessing + the Format string. */ +MAKE_CLEAR(Format) +MAKE_GET(Format,const char *,NULL,0,0) +MAKE_SET(Format,const char *) +MAKE_TEST(Format) + +/* +*att++ +* Name: +* Label(axis) + +* Purpose: +* Axis label. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies a label to be attached to each axis of +* a Frame when it is represented (e.g.) in graphical output. +* +* If a Label value has not been set for a Frame axis, then a +* suitable default is supplied. + +* Applicability: +* Frame +* The default supplied by the Frame class is the string "Axis +* ", where is 1, 2, etc. for each successive axis. +* SkyFrame +* The SkyFrame class re-defines the default Label value +* (e.g. to "Right ascension" or "Galactic latitude") as +* appropriate for the particular celestial coordinate system +* being represented. +* TimeFrame +* The TimeFrame class re-defines the default Label value as +* appropriate for the particular time system being represented. +* FrameSet +* The Label attribute of a FrameSet axis is the same as that of +* its current Frame (as specified by the Current attribute). + +* Notes: +* - Axis labels are intended purely for interpretation by human +* readers and not by software. +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* This provides an interface to the Axis methods for accessing the + Label string, but provides an alternative default Label based on + the axis number. This default string is written to the static + "label_buff" buffer and a pointer to this is returned if + required. */ +MAKE_CLEAR(Label) +MAKE_GET(Label,const char *,NULL,1,GetDefaultLabel( axis, status )) +MAKE_SET(Label,const char *) +MAKE_TEST(Label) + +/* +*att++ +* Name: +* Symbol(axis) + +* Purpose: +* Axis symbol. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies a short-form symbol to be used to +* represent coordinate values for a particular axis of a +* Frame. This might be used (e.g.) in algebraic expressions where +* a full description of the axis would be inappropriate. Examples +* include "RA" and "Dec" (for Right Ascension and Declination). +* +* If a Symbol value has not been set for a Frame axis, then a +* suitable default is supplied. + +* Applicability: +* Frame +* The default Symbol value supplied by the Frame class is the +* string "", where is 1, 2, etc. for successive +* axes, and is the value of the Frame's Domain +* attribute (truncated if necessary so that the final string +* does not exceed 15 characters). If no Domain value has been +* set, "x" is used as the value in constructing this +* default string. +* SkyFrame +* The SkyFrame class re-defines the default Symbol value +* (e.g. to "RA" or "Dec") as appropriate for the particular +* celestial coordinate system being represented. +* TimeFrame +* The TimeFrame class re-defines the default Symbol value as +* appropriate for the particular time system being represented. +* FrameSet +* The Symbol attribute of a FrameSet axis is the same as that +* of its current Frame (as specified by the Current attribute). + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* This provides an interface to the Axis methods for accessing the + Symbol string, but provides an alternative default Symbol based on + the axis number and the Frame's Domain (if defined, otherwise "x" + is used). This default string is written to the static + "symbol_buff" buffer and a pointer to this is returned if + required. */ +MAKE_CLEAR(Symbol) +MAKE_GET(Symbol,const char *,NULL,1,GetDefaultSymbol( this, axis, status ) ) +MAKE_SET(Symbol,const char *) +MAKE_TEST(Symbol) + +/* +*att++ +* Name: +* Unit(axis) + +* Purpose: +* Physical units for formatted axis values + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute contains a textual representation of the physical +* units used to represent formatted coordinate values on a particular +* axis of a Frame. +c The astSetActiveUnit function controls how the Unit values +f The AST_SETACTIVEUNIT routine controls how the Unit values +* are used. + +* Applicability: +* Frame +* The default supplied by the Frame class is an empty string. +* SkyFrame +* The SkyFrame class re-defines the default Unit value (e.g. to +* "hh:mm:ss.sss") to describe the character string returned by +c the astFormat function when formatting coordinate values. +f the AST_FORMAT function when formatting coordinate values. +* SpecFrame +* The SpecFrame class re-defines the default Unit value so that it +* is appropriate for the current System value. See the System +* attribute for details. An error will be reported if an attempt +* is made to use an inappropriate Unit. +* TimeFrame +* The TimeFrame class re-defines the default Unit value so that it +* is appropriate for the current System value. See the System +* attribute for details. An error will be reported if an attempt +* is made to use an inappropriate Unit (e.g. "km"). +* FrameSet +* The Unit attribute of a FrameSet axis is the same as that of +* its current Frame (as specified by the Current attribute). + +* Notes: +* - This attribute described the units used when an axis value is +* formatted into a string using +c astFormat. +f AST_FORMAT. +* In some cases these units may be different to those used to represent +* floating point axis values within application code (for instance a +* SkyFrame always uses radians to represent floating point axis values). +* The InternalUnit attribute described the units used for floating +* point values. +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* This simply provides an interface to the Axis methods for accessing + the Unit string. */ +MAKE_GET(Unit,const char *,NULL,0,0) +MAKE_TEST(Unit) + +/* +*att++ +* Name: +* NormUnit(axis) + +* Purpose: +* Normalised physical units for formatted axis values + +* Type: +* Public attribute. + +* Synopsis: +* String, read-only. + +* Description: +* The value of this read-only attribute is derived from the current +* value of the Unit attribute. It will represent an equivalent system +* of units to the Unit attribute, but will potentially be simplified. +* For instance, if Unit is set to "s*(m/s)", the NormUnit value will +* be "m". If no simplification can be performed, the value of the +* NormUnit attribute will equal that of the Unit attribute. + +* Applicability: +* Frame +* All Frames have this attribute. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* This simply provides an interface to the Axis methods for accessing + the NormUnit string. */ +MAKE_GET(NormUnit,const char *,NULL,0,0) + +/* +*att++ +* Name: +* InternalUnit(axis) + +* Purpose: +* Physical units for unformated axis values + +* Type: +* Public attribute. + +* Synopsis: +* String, read-only. + +* Description: +* This read-only attribute contains a textual representation of the +* physical units used to represent unformatted (i.e. floating point) +* values on a particular axis of a Frame, typically handled internally +* within application code. In most cases, the value of the InternalUnit +* attribute will be the same as Unit attribute (i.e. formatted and +* unformatted axis values will normally use the same system of units). +* The main exception to this is the SkyFrame class, which represents +* unformatted axis values in radians, regardless of the current +* setting of the Unit attribute. + +* Applicability: +* Frame +* All Frames have this attribute. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the Frame axis to which it +* applies. +*att-- +*/ +/* If the Axis structure provides a value for InternalUnit, then use + that value. Otherwise, use a default equal to the value of the Unit + attribute for the axis. */ +MAKE_GET(InternalUnit,const char *,NULL,1,astGetUnit(this,axis)) + +/* Implement member functions to access the attributes associated with + the Frame as a whole using the macros defined for this purpose in + the "object.h" file. */ + +/* +*att++ +* Name: +* Digits/Digits(axis) + +* Purpose: +* Number of digits of precision. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute specifies how many digits of precision are +* required by default when a coordinate value is formatted for a +c Frame axis (e.g. using astFormat). Its value may be set either +f Frame axis (e.g. using AST_FORMAT). Its value may be set either +* for a Frame as a whole, or (by subscripting the attribute name +* with the number of an axis) for each axis individually. Any +* value set for an individual axis will over-ride the value for +* the Frame as a whole. +* +* Note that the Digits value acts only as a means of determining a +* default Format string. Its effects are over-ridden if a Format +* string is set explicitly for an axis. However, if the Format +* attribute specifies the precision using the string ".*", then +* the Digits attribute is used to determine the number of decimal +* places to produce. + +* Applicability: +* Frame +* The default Digits value supplied by the Frame class is 7. If +* a value less than 1 is supplied, then 1 is used instead. +* FrameSet +* The Digits attribute of a FrameSet (or one of its axes) is +* the same as that of its current Frame (as specified by the +* Current attribute). +* Plot +* The default Digits value used by the Plot class when drawing +* annotated axis labels is the smallest value which results in all +* adjacent labels being distinct. +* TimeFrame +* The Digits attribute is ignored when a TimeFrame formats a value +* as a date and time string (see the Format attribute). +*att-- +*/ +/* Clear the Digits value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Frame,Digits,digits,-INT_MAX) + +/* Supply a default of 7 digits if no value has been set. */ +astMAKE_GET(Frame,Digits,int,0,( ( this->digits != -INT_MAX ) ? this->digits : + 7 )) + +/* Constrain the Digits value being set to be at least 1. */ +astMAKE_SET(Frame,Digits,int,digits,( value > 1 ? value : 1 )) + +/* The Digits value is set if it is not -INT_MAX. */ +astMAKE_TEST(Frame,Digits,( this->digits != -INT_MAX )) + +/* +*att++ +* Name: +* MatchEnd + +* Purpose: +* Match trailing axes? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how a Frame +c behaves when it is used (by astFindFrame) as a template to match +f behaves when it is used (by AST_FINDFRAME) as a template to match +* another (target) Frame. It applies only in the case where a +* match occurs between template and target Frames with different +* numbers of axes. +* +* If the MatchEnd value of the template Frame is zero, then the +* axes which occur first in the target Frame will be matched and +* any trailing axes (in either the target or template) will be +* disregarded. If it is non-zero, the final axes in each Frame +* will be matched and any un-matched leading axes will be +* disregarded instead. + +* Applicability: +* Frame +* The default MatchEnd value for a Frame is zero, so that +* trailing axes are disregarded. +* FrameSet +* The MatchEnd attribute of a FrameSet is the same as that of +* its current Frame (as specified by the Current attribute). +*att-- +*/ +/* Clear the MatchEnd value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Frame,MatchEnd,match_end,-INT_MAX) + +/* Supply a default of 0 if no MatchEnd value has been set. */ +astMAKE_GET(Frame,MatchEnd,int,0,( ( this->match_end != -INT_MAX ) ? + this->match_end : 0 )) + +/* Set a MatchEnd value of 1 if any non-zero value is supplied. */ +astMAKE_SET(Frame,MatchEnd,int,match_end,( value != 0 )) + +/* The MatchEnd value is set if it is not -INT_MAX. */ +astMAKE_TEST(Frame,MatchEnd,( this->match_end != -INT_MAX )) + +/* +*att++ +* Name: +* MaxAxes + +* Purpose: +* Maximum number of Frame axes to match. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute controls how a Frame behaves when it is used (by +c astFindFrame) as a template to match another (target) Frame. It +f AST_FINDFRAME) as a template to match another (target) Frame. It +* specifies the maximum number of axes that the target Frame may +* have in order to match the template. +* +* Normally, this value will equal the number of Frame axes, so +* that a template Frame will only match another Frame with the +* same number of axes as itself. By setting a different value, +* however, the matching process may be used to identify Frames +* with specified numbers of axes. + +* Applicability: +* Frame +* The default MaxAxes value for a Frame is equal to the number +* of Frame axes (Naxes attribute). +* CmpFrame +* The MaxAxes attribute of a CmpFrame defaults to a large number +* (1000000) which is much larger than any likely number of axes in +* a Frame. Combined with the MinAxes default of zero (for a +* CmpFrame), this means that the default behaviour for a CmpFrame +* is to match any target Frame that consists of a subset of the +* axes in the template CmpFrame. To change this so that a CmpFrame +* will only match Frames that have the same number of axes, you +* should set the CmpFrame MaxAxes and MinAxes attributes to the +* number of axes in the CmpFrame. +* FrameSet +* The MaxAxes attribute of a FrameSet is the same as that of +* its current Frame (as specified by the Current attribute). + +* Notes: +* - When setting a MaxAxes value, the value of the MinAxes +* attribute may also be silently changed so that it remains +* consistent with (i.e. does not exceed) the new value. The +* default MaxAxes value may also be reduced to remain consistent +* with the MinAxes value. +* - If a template Frame is used to match a target with a different +* number of axes, the MatchEnd attribute of the template is used +* to determine how the individual axes of each Frame should match. +*att-- +*/ +/* Clear the MaxAxes value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Frame,MaxAxes,max_axes,-INT_MAX) + +/* Use the DefaultMaxAxes and ConsistentMaxAxes functions (defined earlier) for + the Get and Set operations to ensure that MinAxes and MaxAxes values remain + consistent. */ +astMAKE_GET(Frame,MaxAxes,int,0,DefaultMaxAxes( this, status )) +astMAKE_SET(Frame,MaxAxes,int,max_axes,ConsistentMaxAxes( this, value, status )) + +/* The MaxAxes value is set if it is not -INT_MAX. */ +astMAKE_TEST(Frame,MaxAxes,( this->max_axes != -INT_MAX )) + +/* +*att++ +* Name: +* MinAxes + +* Purpose: +* Minimum number of Frame axes to match. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute controls how a Frame behaves when it is used (by +c astFindFrame) as a template to match another (target) Frame. It +f AST_FINDFRAME) as a template to match another (target) Frame. It +* specifies the minimum number of axes that the target Frame may +* have in order to match the template. +* +* Normally, this value will equal the number of Frame axes, so +* that a template Frame will only match another Frame with the +* same number of axes as itself. By setting a different value, +* however, the matching process may be used to identify Frames +* with specified numbers of axes. + +* Applicability: +* Frame +* The default MinAxes value for a Frame is equal to the number +* of Frame axes (Naxes attribute). +* CmpFrame +* The MinAxes attribute of a CmpFrame defaults to zero. Combined +* with the MaxAxes default of 1000000 (for a CmpFrame), this means +* that the default behaviour for a CmpFrame is to match any target +* Frame that consists of a subset of the axes in the template +* CmpFrame. To change this so that a CmpFrame will only match Frames +* that have the same number of axes, you should set the CmpFrame +* MinAxes and MaxAxes attributes to the number of axes in the CmpFrame. +* FrameSet +* The MinAxes attribute of a FrameSet is the same as that of +* its current Frame (as specified by the Current attribute). + +* Notes: +* - When setting a MinAxes value, the value of the MaxAxes +* attribute may also be silently changed so that it remains +* consistent with (i.e. is not less than) the new value. The +* default MinAxes value may also be reduced to remain consistent +* with the MaxAxes value. +* - If a template Frame is used to match a target with a different +* number of axes, the MatchEnd attribute of the template is used +* to determine how the individual axes of each Frame should match. +*att-- +*/ +/* Clear the MinAxes value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Frame,MinAxes,min_axes,-INT_MAX) + +/* Use the DefaultMinAxes and ConsistentMinAxes functions (defined earlier) for + the Get and Set operations to ensure that MinAxes and MaxAxes values remain + consistent. */ +astMAKE_GET(Frame,MinAxes,int,0,DefaultMinAxes( this, status )) +astMAKE_SET(Frame,MinAxes,int,min_axes,ConsistentMinAxes( this, value, status )) + +/* The MinAxes value is set if it is not -INT_MAX. */ +astMAKE_TEST(Frame,MinAxes,( this->min_axes != -INT_MAX )) + +/* +*att++ +* Name: +* Domain + +* Purpose: +* Coordinate system domain. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute contains a string which identifies the physical +* domain of the coordinate system that a Frame describes. +* +* The Domain attribute also controls how a Frame behaves when it is +c used (by astFindFrame) as a template to match another (target) +f used (by AST_FINDFRAME) as a template to match another (target) +* Frame. It does this by specifying the Domain that the target +* Frame should have in order to match the template. If the Domain +* value in the template Frame is set, then only targets with the +* same Domain value will be matched. If the template's Domain +* value is not set, however, then the target's Domain will be +* ignored. + +* Applicability: +* Frame +* The default Domain value supplied by the Frame class is an +* empty string. +* SkyFrame +* The SkyFrame class re-defines the default Domain value to be +* "SKY". +* CmpFrame +* The CmpFrame class re-defines the default Domain value to be +* of the form "-", where and are the +* Domains of the two component Frames. If both these Domains are +* blank, then the string "CMP" is used as the default Domain name. +* FrameSet +* The Domain attribute of a FrameSet is the same as that of its +* current Frame (as specified by the Current attribute). +* SpecFrame +* The SpecFrame class re-defines the default Domain value to be +* "SPECTRUM". +* DSBSpecFrame +* The DSBSpecFrame class re-defines the default Domain value to be +* "DSBSPECTRUM". +* FluxFrame +* The FluxFrame class re-defines the default Domain value to be +* "FLUX". +* SpecFluxFrame +* The FluxFrame class re-defines the default Domain value to be +* "SPECTRUM-FLUX". +* TimeFrame +* The TimeFrame class re-defines the default Domain value to be +* "TIME". + +* Notes: +* - All Domain values are converted to upper case and white space +* is removed before use. +*att-- +*/ +/* Clear the Domain value by freeing the allocated memory and + assigning a NULL pointer. */ +astMAKE_CLEAR(Frame,Domain,domain,astFree( this->domain )) + +/* If the Domain value is not set, supply a default in the form of a + pointer to the constant string "". */ +astMAKE_GET(Frame,Domain,const char *,NULL,( this->domain ? this->domain : + "" )) + +/* Set a Domain value by freeing any previously allocated memory, + allocating new memory, storing the string, removing white space, + converting to upper case and saving the pointer to the cleaned + copy. */ +astMAKE_SET(Frame,Domain,const char *,domain,CleanDomain( + astStore( this->domain, + value, strlen( value ) + (size_t) 1 ), status )) + +/* The Domain value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Frame,Domain,( this->domain != NULL )) + +/* +*att++ +* Name: +* Permute + +* Purpose: +* Permute axis order? + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute is a boolean value which controls how a Frame +c behaves when it is used (by astFindFrame) as a template to match +f behaves when it is used (by AST_FINDFRAME) as a template to match +* another (target) Frame. It specifies whether the axis order of +* the target Frame may be permuted in order to obtain a match. +* +* If the template's Permute value is zero, it will match a target +* only if it can do so without changing the order of its +* axes. Otherwise, it will attempt to permute the target's axes as +* necessary. +* +* The default value is 1, so that axis permutation will be attempted. + +* Applicability: +* Frame +* All Frames have this attribute. However, the Frame class +* effectively ignores this attribute and behaves as if it has +* the value 1. This is because the axes of a basic Frame are +* not distinguishable and will always match any other Frame +* whatever their order. +* SkyFrame +* Unlike a basic Frame, the SkyFrame class makes use of this +* attribute. +* FrameSet +* The Permute attribute of a FrameSet is the same as that of +* its current Frame (as specified by the Current attribute). +*att-- +*/ +/* Clear the Permute value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Frame,Permute,permute,-INT_MAX) + +/* Supply a default of 1 if no Permute value has been set. */ +astMAKE_GET(Frame,Permute,int,0,( ( this->permute != -INT_MAX ) ? + this->permute : 1 )) + +/* Set a Permute value of 1 if any non-zero value is supplied. */ +astMAKE_SET(Frame,Permute,int,permute,( value != 0 )) + +/* The Permute value is set if it is not -INT_MAX. */ +astMAKE_TEST(Frame,Permute,( this->permute != -INT_MAX )) + +/* +*att++ +* Name: +* PreserveAxes + +* Purpose: +* Preserve axes? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls how a Frame behaves when it is used (by +c astFindFrame) as a template to match another (target) Frame. It +f AST_FINDFRAME) as a template to match another (target) Frame. It +* determines which axes appear (and in what order) in the "result" +* Frame produced. +* +* If PreserveAxes is zero in the template Frame, then the result +* Frame will have the same number (and order) of axes as the +* template. If it is non-zero, however, the axes of the target +* Frame will be preserved, so that the result Frame will have the +* same number (and order) of axes as the target. +* +* The default value is zero, so that target axes are not preserved +* and the result Frame resembles the template. + +* Applicability: +* Frame +* All Frames have this attribute. +* FrameSet +* The PreserveAxes attribute of a FrameSet is the same as that +* of its current Frame (as specified by the Current attribute). +*att-- +*/ +/* Clear the PreserveAxes value by setting it to -INT_MAX. */ +astMAKE_CLEAR(Frame,PreserveAxes,preserve_axes,-INT_MAX) + +/* Supply a default of 0 if no PreserveAxes value has been set. */ +astMAKE_GET(Frame,PreserveAxes,int,0,( ( this->preserve_axes != -INT_MAX ) ? + this->preserve_axes : 0 )) + +/* Set a PreserveAxes value of 1 if any non-zero value is supplied. */ +astMAKE_SET(Frame,PreserveAxes,int,preserve_axes,( value != 0 )) + +/* The PreserveAxes value is set if it is not -INT_MAX. */ +astMAKE_TEST(Frame,PreserveAxes,( this->preserve_axes != -INT_MAX )) + +/* +*att++ +* Name: +* AlignSystem + +* Purpose: +* Coordinate system in which to align the Frame. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls how a Frame behaves when it is used (by +c astFindFrame or astConvert) as a template to match another (target) +f AST_FINDFRAME or AST_CONVERT) as a template to match another (target) +* Frame. It identifies the coordinate system in which the two Frames +* will be aligned by the match. +* +* The values which may be assigned to this attribute, and its default +* value, depend on the class of Frame and are described in the +* "Applicability" section below. In general, the AlignSystem attribute +* will accept any of the values which may be assigned to the System +* attribute. +* +c The Mapping returned by AST_FINDFRAME or AST_CONVERT will use the +f The Mapping returned by astFindFrame or astConvert will use the +* coordinate system specified by the AlignSystem attribute as an +* intermediate coordinate system. The total returned Mapping will first +* map positions from the first Frame into this intermediate coordinate +* system, using the attributes of the first Frame. It will then map +* these positions from the intermediate coordinate system into the +* second Frame, using the attributes of the second Frame. + +* Applicability: +* Frame +* The AlignSystem attribute for a basic Frame always equals "Cartesian", +* and may not be altered. +* CmpFrame +* The AlignSystem attribute for a CmpFrame always equals "Compound", +* and may not be altered. +* FrameSet +* The AlignSystem attribute of a FrameSet is the same as that of its +* current Frame (as specified by the Current attribute). +* SkyFrame +* The default AlignSystem attribute for a SkyFrame is "ICRS". +* SpecFrame +* The default AlignSystem attribute for a SpecFrame is "Wave" +* (wavelength). +* TimeFrame +* The default AlignSystem attribute for a TimeFrame is "MJD". +*att-- +*/ +/* Clear the AlignSystem value by setting it to AST__BADSYSTEM. */ +astMAKE_CLEAR(Frame,AlignSystem,alignsystem,AST__BADSYSTEM) + +/* Provide a default AlignSystem of AST__CART. */ +astMAKE_GET(Frame,AlignSystem,AstSystemType,AST__BADSYSTEM,( + ( this->alignsystem == AST__BADSYSTEM ) ? AST__CART : this->alignsystem ) ) + +/* Validate the AlignSystem value being set and retain the original if the + supplied value is not recognized. */ +astMAKE_SET(Frame,AlignSystem,AstSystemType,alignsystem,( + (astValidateSystem( this, value, "astSetAlignSystem" ) != AST__BADSYSTEM) ? + value : this->alignsystem )) + +/* The AlignSystem value is set if it is not AST__BADSYSTEM. */ +astMAKE_TEST(Frame,AlignSystem,( this->alignsystem != AST__BADSYSTEM )) + +/* +*att++ +* Name: +* System + +* Purpose: +* Coordinate system used to describe positions within the domain + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* In general it is possible for positions within a given physical +* domain to be described using one of several different coordinate +* systems. For instance, the SkyFrame class can use galactic +* coordinates, equatorial coordinates, etc, to describe positions on +* the sky. As another example, the SpecFrame class can use frequency, +* wavelength, velocity, etc, to describe a position within an +* electromagnetic spectrum. The System attribute identifies the particular +* coordinate system represented by a Frame. Each class of Frame +* defines a set of acceptable values for this attribute, as listed +* below (all are case insensitive). Where more than one alternative +* System value is shown, the first of will be returned when an +* enquiry is made. + +* Applicability: +* Frame +* The System attribute for a basic Frame always equals "Cartesian", +* and may not be altered. +* CmpFrame +* The System attribute for a CmpFrame always equals "Compound", +* and may not be altered. In addition, the CmpFrame class allows +* the System attribute to be referenced for a component Frame by +* including the index of an axis within the required component +* Frame. For instance, "System(3)" refers to the System attribute +* of the component Frame which includes axis 3 of the CmpFrame. +* FrameSet +* The System attribute of a FrameSet is the same as that of its +* current Frame (as specified by the Current attribute). +* SkyFrame +* The SkyFrame class supports the following System values and +* associated celestial coordinate systems: +* +* - "AZEL": Horizon coordinates. The longitude axis is azimuth +* such that geographic north has an azimuth of zero and geographic +* east has an azimuth of +PI/2 radians. The zenith has elevation +* +PI/2. When converting to and from other celestial coordinate +* systems, no corrections are applied for atmospheric refraction +* or polar motion (however, a correction for diurnal aberattion is +* applied). Note, unlike most other +* celestial coordinate systems, this system is right handed. Also, +* unlike other SkyFrame systems, the AzEl system is sensitive to +* the timescale in which the Epoch value is supplied. This is +* because of the gross diurnal rotation which this system undergoes, +* causing a small change in time to translate to a large rotation. +* When converting to or from an AzEl system, the Epoch value for +* both source and destination SkyFrames should be supplied in the +* TDB timescale. The difference between TDB and TT is between 1 +* and 2 milliseconds, and so a TT value can usually be supplied in +* place of a TDB value. The TT timescale is related to TAI via +* TT = TAI + 32.184 seconds. +* +* - "ECLIPTIC": Ecliptic coordinates (IAU 1980), referred to the +* ecliptic and mean equinox specified by the qualifying Equinox +* value. +* +* - "FK4": The old FK4 (barycentric) equatorial coordinate system, +* which should be qualified by an Equinox value. The underlying +* model on which this is based is non-inertial and rotates slowly +* with time, so for accurate work FK4 coordinate systems should +* also be qualified by an Epoch value. +* +* - "FK4-NO-E" or "FK4_NO_E": The old FK4 (barycentric) equatorial +* system but without the "E-terms of aberration" (e.g. some radio +* catalogues). This coordinate system should also be qualified by +* both an Equinox and an Epoch value. +* +* - "FK5" or "EQUATORIAL": The modern FK5 (barycentric) equatorial +* coordinate system. This should be qualified by an Equinox value. +* +* - "GALACTIC": Galactic coordinates (IAU 1958). +* +* - "GAPPT", "GEOCENTRIC" or "APPARENT": The geocentric apparent +* equatorial coordinate system, which gives the apparent positions +* of sources relative to the true plane of the Earth's equator and +* the equinox (the coordinate origin) at a time specified by the +* qualifying Epoch value. (Note that no Equinox is needed to +* qualify this coordinate system because no model "mean equinox" +* is involved.) These coordinates give the apparent right +* ascension and declination of a source for a specified date of +* observation, and therefore form an approximate basis for +* pointing a telescope. Note, however, that they are applicable to +* a fictitious observer at the Earth's centre, and therefore +* ignore such effects as atmospheric refraction and the (normally +* much smaller) aberration of light due to the rotational velocity +* of the Earth's surface. Geocentric apparent coordinates are +* derived from the standard FK5 (J2000.0) barycentric coordinates +* by taking account of the gravitational deflection of light by +* the Sun (usually small), the aberration of light caused by the +* motion of the Earth's centre with respect to the barycentre +* (larger), and the precession and nutation of the Earth's spin +* axis (normally larger still). +* +* - "HELIOECLIPTIC": Ecliptic coordinates (IAU 1980), referred to the +* ecliptic and mean equinox of J2000.0, in which an offset is added to +* the longitude value which results in the centre of the sun being at +* zero longitude at the date given by the Epoch attribute. Attempts to +* set a value for the Equinox attribute will be ignored, since this +* system is always referred to J2000.0. +* +* - "ICRS": The Internation Celestial Reference System, realised +* through the Hipparcos catalogue. Whilst not an equatorial system +* by definition, the ICRS is very close to the FK5 (J2000) system +* and is usually treated as an equatorial system. The distinction +* between ICRS and FK5 (J2000) only becomes important when accuracies +* of 50 milli-arcseconds or better are required. ICRS need not be +* qualified by an Equinox value. +* +* - "J2000": An equatorial coordinate system based on the mean +* dynamical equator and equinox of the J2000 epoch. The dynamical +* equator and equinox differ slightly from those used by the FK5 +* model, and so a "J2000" SkyFrame will differ slightly from an +* "FK5(Equinox=J2000)" SkyFrame. The J2000 System need not be +* qualified by an Equinox value +* +* - "SUPERGALACTIC": De Vaucouleurs Supergalactic coordinates. +* +* - "UNKNOWN": Any other general spherical coordinate system. No +* Mapping can be created between a pair of SkyFrames if either of the +* SkyFrames has System set to "Unknown". +* +* Currently, the default System value is "ICRS". However, this +* default may change in future as new astrometric standards +* evolve. The intention is to track the most modern appropriate +* standard. For this reason, you should use the default only if +* this is what you intend (and can tolerate any associated slight +* change in future). If you intend to use the ICRS system +* indefinitely, then you should specify it explicitly. +* SpecFrame +* The SpecFrame class supports the following System values and +* associated spectral coordinate systems (the default is "WAVE" - +* wavelength). They are all defined in FITS-WCS paper III: +* +* - "FREQ": Frequency (GHz) +* - "ENER" or "ENERGY": Energy (J) +* - "WAVN" or "WAVENUM": Wave-number (1/m) +* - "WAVE" or "WAVELEN": Vacuum wave-length (Angstrom) +* - "AWAV" or "AIRWAVE": Wave-length in air (Angstrom) +* - "VRAD" or "VRADIO": Radio velocity (km/s) +* - "VOPT" or "VOPTICAL": Optical velocity (km/s) +* - "ZOPT" or "REDSHIFT": Redshift (dimensionless) +* - "BETA": Beta factor (dimensionless) +* - "VELO" or "VREL": Apparent radial ("relativistic") velocity (km/s) +* +* The default value for the Unit attribute for each system is shown +* in parentheses. Note that the default value for the ActiveUnit flag +c is non-zero +f is .TRUE. +* for a SpecFrame, meaning that changes to the Unit attribute for +* a SpecFrame will result in the SpecFrame being re-mapped within +* its enclosing FrameSet in order to reflect the change in units +c (see astSetActiveUnit function for further information). +f (see AST_SETACTIVEUNIT routine for further information). +* TimeFrame +* The TimeFrame class supports the following System values and +* associated coordinate systems (the default is "MJD"): +* +* - "MJD": Modified Julian Date (d) +* - "JD": Julian Date (d) +* - "JEPOCH": Julian epoch (yr) +* - "BEPOCH": Besselian (yr) +* +* The default value for the Unit attribute for each system is shown +* in parentheses. Strictly, these systems should not allow changes +* to be made to the units. For instance, the usual definition of +* "MJD" and "JD" include the statement that the values will be in +* units of days. However, AST does allow the use of other units +* with all the above supported systems (except BEPOCH), on the +* understanding that conversion to the "correct" units involves +* nothing more than a simple scaling (1 yr = 365.25 d, 1 d = 24 h, +* 1 h = 60 min, 1 min = 60 s). Besselian epoch values are defined +* in terms of tropical years of 365.2422 days, rather than the +* usual Julian year of 365.25 days. Therefore, to avoid any +* confusion, the Unit attribute is automatically cleared to "yr" when +* a System value of BEPOCH System is selected, and an error is +* reported if any attempt is subsequently made to change the Unit +* attribute. +* +* Note that the default value for the ActiveUnit flag +c is non-zero +f is .TRUE. +* for a TimeFrame, meaning that changes to the Unit attribute for +* a TimeFrame will result in the TimeFrame being re-mapped within +* its enclosing FrameSet in order to reflect the change in units +c (see astSetActiveUnit function for further information). +f (see AST_SETACTIVEUNIT routine for further information). +* FluxFrame +* The FluxFrame class supports the following System values and +* associated systems for measuring observed value: +* +* - "FLXDN": Flux per unit frequency (W/m^2/Hz) +* - "FLXDNW": Flux per unit wavelength (W/m^2/Angstrom) +* - "SFCBR": Surface brightness in frequency units (W/m^2/Hz/arcmin**2) +* - "SFCBRW": Surface brightness in wavelength units (W/m^2/Angstrom/arcmin**2) +* +* The above lists specified the default units for each System. If an +* explicit value is set for the Unit attribute but no value is set +* for System, then the default System value is determined by the Unit +* string (if the units are not appropriate for describing any of the +* supported Systems then an error will be reported when an attempt is +* made to access the System value). If no value has been specified for +* either Unit or System, then System=FLXDN and Unit=W/m^2/Hz are +* used. +*att-- +*/ +/* Clear the System value by setting it to AST__BADSYSTEM. */ +astMAKE_CLEAR(Frame,System,system,AST__BADSYSTEM) + +/* Provide a default coordinate system of AST__CART. */ +astMAKE_GET(Frame,System,AstSystemType,AST__BADSYSTEM,( + ( this->system == AST__BADSYSTEM ) ? AST__CART : this->system ) ) + +/* Validate the System value being set and retain the original if the + supplied value is not recognized. */ +astMAKE_SET(Frame,System,AstSystemType,system,( + (astValidateSystem( this, value, "astSetSystem" ) != AST__BADSYSTEM) ? + value : this->system )) + +/* The System value is set if it is not AST__BADSYSTEM. */ +astMAKE_TEST(Frame,System,( this->system != AST__BADSYSTEM )) + +/* +*att++ +* Name: +* Title + +* Purpose: +* Frame title. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute holds a string which is used as a title in (e.g.) +* graphical output to describe the coordinate system which a Frame +* represents. Examples might be "Detector Coordinates" or +* "Galactic Coordinates". +* +* If a Title value has not been set for a Frame, then a suitable +* default is supplied, depending on the class of the Frame. + +* Applicability: +* Frame +* The default supplied by the Frame class is "-d coordinate +* system", where is the number of Frame axes (Naxes +* attribute). +* CmpFrame +* The CmpFrame class re-defines the default Title value to be +* "-d compound coordinate system", where is the number +* of CmpFrame axes (Naxes attribute). +* FrameSet +* The Title attribute of a FrameSet is the same as that of its +* current Frame (as specified by the Current attribute). + +* Notes: +* - A Frame's Title is intended purely for interpretation by human +* readers and not by software. +*att-- +*/ +/* Clear the Title value by freeing the allocated memory and assigning + a NULL pointer. */ +astMAKE_CLEAR(Frame,Title,title,astFree( this->title )) + +/* If the Title value is not set, write a default based on the number of Frame + axes into the static "title_buff" buffer, and return a pointer to this + buffer. */ +astMAKE_GET(Frame,Title,const char *,NULL,( this->title ? + this->title : GetDefaultTitle( this, status ) )) + +/* Set a Title value by freeing any previously allocated memory, allocating + new memory, storing the string and saving the pointer to the copy. */ +astMAKE_SET(Frame,Title,const char *,title,astStore( this->title, value, + strlen( value ) + (size_t) 1 )) + +/* The Title value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Frame,Title,( this->title != NULL )) + +/* +*att++ +* Name: +* ObsLat + +* Purpose: +* The geodetic latitude of the observer + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the geodetic latitude of the observer, in +* degrees, relative to the IAU 1976 reference ellipsoid. The basic Frame +* class makes no use of this attribute, but specialised subclasses of +* Frame may use it. For instance, the SpecFrame, SkyFrame and TimeFrame +* classes use it. The default value is zero. +* +* The value is stored internally in radians, but is converted to and +* from a degrees string for access. Some example input formats are: +* "22:19:23.2", "22 19 23.2", "22:19.387", "22.32311", "N22.32311", +* "-45.6", "S45.6". As indicated, the sign of the latitude can +* optionally be indicated using characters "N" and "S" in place of the +* usual "+" and "-". When converting the stored value to a string, the +* format "[s]dd:mm:ss.ss" is used, when "[s]" is "N" or "S". + +* Applicability: +* Frame +* All Frames have this attribute. +* SpecFrame +* Together with the ObsLon, Epoch, RefRA and RefDec attributes, +* it defines the Doppler shift introduced by the observers diurnal +* motion around the earths axis, which is needed when converting to +* or from the topocentric standard of rest. The maximum velocity +* error which can be caused by an incorrect value is 0.5 km/s. The +* default value for the attribute is zero. +* TimeFrame +* Together with the ObsLon attribute, it is used when converting +* between certain time scales (TDB, TCB, LMST, LAST) + +*att-- +*/ +/* The geodetic latitude of the observer (radians). Clear the ObsLat value by + setting it to AST__BAD, returning zero as the default value. Any value is + acceptable. */ +astMAKE_CLEAR(Frame,ObsLat,obslat,AST__BAD) +astMAKE_GET(Frame,ObsLat,double,0.0,((this->obslat!=AST__BAD)?this->obslat:0.0)) +astMAKE_SET(Frame,ObsLat,double,obslat,value) +astMAKE_TEST(Frame,ObsLat,(this->obslat!=AST__BAD)) + + +/* +*att++ +* Name: +* ObsAlt + +* Purpose: +* The geodetic altitude of the observer + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the geodetic altitude of the observer, in +* metres, relative to the IAU 1976 reference ellipsoid. The basic Frame +* class makes no use of this attribute, but specialised subclasses of +* Frame may use it. For instance, the SpecFrame, SkyFrame and TimeFrame +* classes use it. The default value is zero. + +* Applicability: +* Frame +* All Frames have this attribute. +* SpecFrame +* Together with the ObsLon, Epoch, RefRA and RefDec attributes, +* it defines the Doppler shift introduced by the observers diurnal +* motion around the earths axis, which is needed when converting to +* or from the topocentric standard of rest. The maximum velocity +* error which can be caused by an incorrect value is 0.5 km/s. The +* default value for the attribute is zero. +* TimeFrame +* Together with the ObsLon attribute, it is used when converting +* between certain time scales (TDB, TCB, LMST, LAST) + +*att-- +*/ +/* The geodetic altitude of the observer (metres). Clear the ObsAlt value by + setting it to AST__BAD, returning zero as the default value. Any value is + acceptable. */ +astMAKE_CLEAR(Frame,ObsAlt,obsalt,AST__BAD) +astMAKE_GET(Frame,ObsAlt,double,0.0,((this->obsalt!=AST__BAD)?this->obsalt:0.0)) +astMAKE_SET(Frame,ObsAlt,double,obsalt,value) +astMAKE_TEST(Frame,ObsAlt,(this->obsalt!=AST__BAD)) + + +/* +*att++ +* Name: +* ObsLon + +* Purpose: +* The geodetic longitude of the observer + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the geodetic (or equivalently, geocentric) +* longitude of the observer, in degrees, measured positive eastwards. +* See also attribute ObsLat. The basic Frame class makes no use of this +* attribute, but specialised subclasses of Frame may use it. For instance, +* the SpecFrame, SkyFrame and TimeFrame classes use it. The default value +* is zero. +* +* The value is stored internally in radians, but is converted to and +* from a degrees string for access. Some example input formats are: +* "155:19:23.2", "155 19 23.2", "155:19.387", "155.32311", "E155.32311", +* "-204.67689", "W204.67689". As indicated, the sign of the longitude can +* optionally be indicated using characters "E" and "W" in place of the +* usual "+" and "-". When converting the stored value to a string, the +* format "[s]ddd:mm:ss.ss" is used, when "[s]" is "E" or "W" and the +* numerical value is chosen to be less than 180 degrees. + +* Applicability: +* Frame +* All Frames have this attribute. +* SpecFrame +* Together with the ObsLon, Epoch, RefRA and RefDec attributes, +* it defines the Doppler shift introduced by the observers diurnal +* motion around the earths axis, which is needed when converting to +* or from the topocentric standard of rest. The maximum velocity +* error which can be caused by an incorrect value is 0.5 km/s. The +* default value for the attribute is zero. +* TimeFrame +* Together with the ObsLon attribute, it is used when converting +* between certain time scales (TDB, TCB, LMST, LAST) + +*att-- +*/ +/* The geodetic longitude of the observer (radians). Clear the ObsLon value by + setting it to AST__BAD, returning zero as the default value. Any value is + acceptable. */ +astMAKE_CLEAR(Frame,ObsLon,obslon,AST__BAD) +astMAKE_GET(Frame,ObsLon,double,0.0,((this->obslon!=AST__BAD)?this->obslon:0.0)) +astMAKE_SET(Frame,ObsLon,double,obslon,value) +astMAKE_TEST(Frame,ObsLon,(this->obslon!=AST__BAD)) + + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Frame objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Frame objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstFrame *in; /* Pointer to input Frame */ + AstFrame *out; /* Pointer to output Frame */ + int axis; /* Loop counter for axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Frames. */ + in = (AstFrame *) objin; + out = (AstFrame *) objout; + +/* For safety, first clear any references to the input memory from + the output Frame. */ + out->axis = NULL; + out->domain = NULL; + out->perm = NULL; + out->title = NULL; + out->variants = NULL; + +/* If necessary, allocate memory in the output Frame and store a copy of the + input Title and Domain strings. */ + if ( in->title ) out->title = astStore( NULL, in->title, + strlen( in->title ) + (size_t) 1 ); + if ( in->domain ) out->domain = astStore( NULL, in->domain, + strlen( in->domain ) + + (size_t) 1 ); + +/* Allocate memory to hold the output Frame's Axis object pointers and its axis + permutation array. */ + out->axis = astMalloc( sizeof( AstAxis * ) * (size_t) in->naxes ); + out->perm = astMalloc( sizeof( int ) * (size_t) in->naxes ); + +/* Make a copy of each of the input Frame's Axis objects, storing the pointer + to each new Axis in the memory just allocated. Also copy the axis + permutation array. */ + if ( astOK ) { + for ( axis = 0; axis < in->naxes; axis++ ) { + out->axis[ axis ] = astCopy( in->axis[ axis ] ); + out->perm[ axis ] = in->perm[ axis ]; + } + +/* If an error occurred while copying the Axis objects, then loop through the + resulting array of pointers and make sure that all of them are properly + annulled. */ + if ( !astOK ) { + for ( axis = 0; axis < in->naxes; axis++ ) { + out->axis[ axis ] = astAnnul( out->axis[ axis ] ); + } + } + } + +/* Other remaining objects */ + if( in->variants ) out->variants = astCopy( in->variants ); + +/* If an error occurred, free any allocated memory. */ + if ( !astOK ) { + out->axis = astFree( out->axis ); + out->domain = astFree( out->domain ); + out->perm = astFree( out->perm ); + out->title = astFree( out->title ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Frame objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Frame objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstFrame *this; /* Pointer to Frame */ + int axis; /* Loop counter for Frame axes */ + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) obj; + +/* Free the memory used for the Title and Domain strings if necessary. */ + this->title = astFree( this->title ); + this->domain = astFree( this->domain ); + +/* If memory has been allocated to store pointers to the Frame's Axis objects, + annul each of these pointers and then free the memory. */ + if ( this->axis ) { + for ( axis = 0; axis < this->naxes; axis++ ) { + this->axis[ axis ] = astAnnul( this->axis[ axis ] ); + } + this->axis = astFree( this->axis ); + } + +/* Free memory used for the axis permutation array if necessary. */ + this->perm = astFree( this->perm ); + +/* Other objects. */ + if( this->variants ) this->variants = astAnnul( this->variants ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Frame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Frame class to an output Channel. + +* Parameters: +* this +* Pointer to the Frame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 150 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis */ + AstFrame *cfrm; /* Pointer to FrameSet's current Frame */ + AstFrame *this; /* Pointer to the Frame structure */ + AstSystemType system; /* System code */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment strings */ + char key[ KEY_LEN + 1 ]; /* Buffer for keywords */ + const char *sval; /* Pointer to string value */ + const char *lab; /* Pointer to unit label */ + const int *perm; /* Pointer to axis permutation array */ + double dval; /* Double attibute value */ + int *invperm; /* Pointer to inverse permutation array */ + int axis; /* Loop counter for Frame axes */ + int bessyr; /* Format as Besselian years (else Julian) */ + int digits_set; /* Digits set explicitly for any axis? */ + int full; /* Full attribute value */ + int full_set; /* Full attribute set? */ + int helpful; /* Helpful to show value even if not set? */ + int isFrame; /* Is this a simple Frame? */ + int ival; /* Integer value */ + int naxes; /* Number of Frame axes */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Frame structure. */ + this = (AstFrame *) this_object; + +/* Determine the number of Frame axes and a pointer to the Frame's + axis permutation array (using methods, to allow for any over-ride + by a derived class). */ + naxes = astGetNaxes( this ); + perm = astGetPerm( this ); + +/* Some default attribute values are not helpful for a simple Frame. Note + if this is a simple Frame, or if it is a FrameSet with a simple Frame + as its current Frame., or if it is a CmpFrame. */ + if( !strcmp( astGetClass( this ), "Frame" ) ) { + isFrame = 1; + } else if( astIsAFrameSet( this ) ) { + cfrm = astGetFrame( (AstFrameSet *) this, AST__CURRENT ); + isFrame = !strcmp( astGetClass( cfrm ), "Frame" ); + cfrm = astAnnul( cfrm ); + } else if( astIsACmpFrame( this ) ) { + isFrame = 1; + } else { + isFrame = 0; + } + +/* Allocate memory to hold an inverse axis permutation array and + generate this array from the forward permutation values. This will + be used to determine which axis should be enquired about (using + possibly over-ridden methods) to obtain data to correspond with a + particular internal value (i.e. instance variable) relating to an + axis. This step is needed so that the effect of any axis + permutation can be un-done before values are written out, as output + values are written by this function in un-permuted order. */ + invperm = astMalloc( sizeof( int ) * (size_t) naxes ); + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) invperm[ perm[ axis ] ] = axis; + +/* Write out values representing the instance variables for the Frame + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Title. */ +/* ------ */ + set = TestTitle( this, status ); + sval = set ? GetTitle( this, status ) : astGetTitle( this ); + astWriteString( channel, "Title", set, 1, sval, + "Title of coordinate system" ); + +/* Naxes. */ +/* ------ */ + set = ( this->naxes != 0 ); + ival = set ? this->naxes : naxes; + astWriteInt( channel, "Naxes", set, 1, ival, + "Number of coordinate axes" ); + +/* Domain. */ +/* ------- */ + set = TestDomain( this, status ); + sval = set ? GetDomain( this, status ) : astGetDomain( this ); + +/* Don't show an un-set Domain value if it is blank. */ + helpful = ( sval && *sval ); + astWriteString( channel, "Domain", set, helpful, sval, + "Coordinate system domain" ); + +/* Epoch. */ +/* ------ */ + set = TestEpoch( this, status ); + dval = set ? GetEpoch( this, status ) : astGetEpoch( this ); + +/* Convert MJD to Besselian or Julian years, depending on the value. */ + bessyr = ( dval < palEpj2d( 1984.0 ) ); + dval = bessyr ? palEpb( dval ) : palEpj( dval ); + astWriteDouble( channel, "Epoch", set, !isFrame, dval, + bessyr ? "Besselian epoch of observation" : + "Julian epoch of observation" ); + +/* Label. */ +/* ------ */ +/* This, and some other, attributes are stored internally by the + Frame's Axis objects, but are "re-packaged" by the Frame class to + appear as Frame attributes. We treat them here like Frame + attributes that are "un-set". There is a Label value for each Frame + axis. */ + for ( axis = 0; axis < naxes; axis++ ) { + +/* The inverse axis permutation array is used to obtain the axis index + for astGetLabel. This reverses the effect of the Frame's axis + permutation array and yields a default value appropriate to the + axis with internal index "axis". */ + sval = astGetLabel( this, invperm[ axis ] ); + +/* Create keyword and comment strings appropriate to each axis + (converting to 1-based axis numbering) and write out the Label + values. */ + (void) sprintf( key, "Lbl%d", axis + 1 ); + (void) sprintf( comment, "Label for axis %d", axis + 1 ); + astWriteString( channel, key, 0, 1, sval, comment ); + } + +/* Symbol. */ +/* ------- */ +/* There is a Symbol value for each Frame axis. These are handled in + the same way as the Label values. */ + for ( axis = 0; axis < naxes; axis++ ) { + sval = astGetSymbol( this, invperm[ axis ] ); + (void) sprintf( key, "Sym%d", axis + 1 ); + (void) sprintf( comment, "Symbol for axis %d", axis + 1 ); + astWriteString( channel, key, 0, 0, sval, comment ); + } + +/* System. */ +/* ------- */ + set = TestSystem( this, status ); + system = set ? GetSystem( this, status ) : astGetSystem( this ); + +/* If set, convert explicitly to a string for the external representation. */ + if ( set ) { + if ( astOK ) { + sval = astSystemString( this, system ); + +/* Report an error if the System value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "astWrite(%s): Corrupt %s contains invalid " + "System identification code (%d).", status, + astGetClass( channel ), astGetClass( this ), + (int) system ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "system" ); + } + +/* Write out the value. */ + astWriteString( channel, "System", set, !isFrame, sval, + "Coordinate system type" ); + +/* AlignSystem. */ +/* ------------ */ + set = TestAlignSystem( this, status ); + system = set ? GetAlignSystem( this, status ) : astGetAlignSystem( this ); + +/* If set, convert explicitly to a string for the external representation. */ + if ( set ) { + if ( astOK ) { + sval = astSystemString( this, system ); + +/* Report an error if the AlignSystem value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "astWrite(%s): Corrupt %s contains invalid " + "AlignSystem identification code (%d).", status, + astGetClass( channel ), astGetClass( this ), + (int) system ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "alignsystem" ); + } + +/* Write out the value. */ + astWriteString( channel, "AlSys", set, 0, sval, + "Alignment coordinate system" ); + +/* Unit. */ +/* ----- */ +/* There is a Unit value for each axis. */ + for ( axis = 0; axis < naxes; axis++ ) { + sval = astGetUnit( this, invperm[ axis ] ); + +/* Get any label associated with the unit string. */ + lab = astUnitLabel( sval ); + +/* Construct a comment including the above label (but only if it is not + the same as the unit string) . */ + if( lab && strcmp( lab, sval ) ) { + (void) sprintf( comment, "Units for axis %d (%s)", axis + 1, lab ); + } else { + (void) sprintf( comment, "Units for axis %d", axis + 1 ); + } + +/* Show the Unit value if it is not blank. */ + helpful = ( sval && *sval ); + (void) sprintf( key, "Uni%d", axis + 1 ); + astWriteString( channel, key, 0, helpful, sval, comment ); + } + +/* Digits. */ +/* ------- */ +/* There is a Digits value for each axis... */ + digits_set = 0; + for ( axis = 0; axis < naxes; axis++ ) { + +/* Obtain the axis Digits value, using the Frame's Digits value as a + default. */ + ax = astGetAxis( this, invperm[ axis ] ); + set = astTestAxisDigits( ax ); + ival = set ? astGetAxisDigits( ax ) : astGetDigits( this ); + ax = astAnnul( ax ); + +/* Show the value if it is set for the axis (i.e. if it differs from + the default for the whole Frame) and note if any such value is + set. */ + helpful = set; + if ( set ) digits_set = 1; + (void) sprintf( key, "Dig%d", axis + 1 ); + (void) sprintf( comment, "Individual precision for axis %d", + axis + 1 ); + astWriteInt( channel, key, 0, helpful, ival, comment ); + } + +/* There is also a Digits value for the Frame as a whole... */ + set = TestDigits( this, status ); + +/* Show the value (even if not set) if an explicit Digits value has + been set for any axis (above). */ + helpful = digits_set; + ival = set ? GetDigits( this, status ) : astGetDigits( this ); + astWriteInt( channel, "Digits", set, helpful, ival, + "Default formatting precision" ); + +/* Format. */ +/* ------- */ +/* There is a Format value for each axis. */ + for ( axis = 0; axis < naxes; axis++ ) { + sval = astGetFormat( this, invperm[ axis ] ); + +/* Show the Format value if the Digits value is set for an individual + axis. */ + ax = astGetAxis( this, invperm[ axis ] ); + helpful = astTestAxisDigits( ax ); + ax = astAnnul( ax ); + (void) sprintf( key, "Fmt%d", axis + 1 ); + (void) sprintf( comment, "Format specifier for axis %d", axis + 1 ); + astWriteString( channel, key, 0, helpful, sval, comment ); + } + +/* Direction. */ +/* ---------- */ +/* There is a Direction value for each axis. */ + for ( axis = 0; axis < naxes; axis++ ) { + ival = astGetDirection( this, invperm[ axis ] ); + +/* Show the value if it is zero. */ + helpful = ( ival == 0 ); + (void) sprintf( key, "Dir%d", axis + 1 ); + (void) sprintf( comment, + ival ? "Plot axis %d in conventional direction" : + "Plot axis %d in reverse direction", + axis + 1 ); + astWriteInt( channel, key, 0, helpful, ival, comment ); + } + +/* Bottom. */ +/* ------- */ +/* There is a Bottom value for each axis. */ + for ( axis = 0; axis < naxes; axis++ ) { + dval = astGetBottom( this, invperm[ axis ] ); + +/* Show the value if it is zero. */ + helpful = ( dval != -DBL_MAX ); + (void) sprintf( key, "Bot%d", axis + 1 ); + astWriteDouble( channel, key, 0, helpful, dval, "Lowest legal axis value"); + } + +/* Top. */ +/* ------- */ +/* There is a Top value for each axis. */ + for ( axis = 0; axis < naxes; axis++ ) { + dval = astGetTop( this, invperm[ axis ] ); + +/* Show the value if it is zero. */ + helpful = ( dval != DBL_MAX ); + (void) sprintf( key, "Top%d", axis + 1 ); + astWriteDouble( channel, key, 0, helpful, dval, "Highest legal axis value"); + } + +/* PreserveAxes. */ +/* ------------- */ + set = TestPreserveAxes( this, status ); + ival = set ? GetPreserveAxes( this, status ) : astGetPreserveAxes( this ); + astWriteInt( channel, "Presrv", set, 0, ival, + ival ? "Preserve target axes" : + "Don't preserve target axes" ); + +/* Permute. */ +/* -------- */ + set = TestPermute( this, status ); + ival = set ? GetPermute( this, status ) : astGetPermute( this ); + astWriteInt( channel, "Permut", set, 0, ival, + ival ? "Axes may be permuted to match" : + "Axes may not be permuted match" ); + +/* MinAxes. */ +/* -------- */ + set = TestMinAxes( this, status ); + ival = set ? GetMinAxes( this, status ) : astGetMinAxes( this ); + astWriteInt( channel, "MinAx", set, 0, ival, + "Minimum number of axes to match" ); + +/* MaxAxes. */ +/* -------- */ + set = TestMaxAxes( this, status ); + ival = set ? GetMaxAxes( this, status ) : astGetMaxAxes( this ); + astWriteInt( channel, "MaxAx", set, 0, ival, + "Maximum number of axes to match" ); + +/* MatchEnd. */ +/* --------- */ + set = TestMatchEnd( this, status ); + ival = set ? GetMatchEnd( this, status ) : astGetMatchEnd( this ); + astWriteInt( channel, "MchEnd", set, 0, ival, + ival ? "Match final target axes" : + "Match initial target axes" ); + +/* ObsLat. */ +/* ------- */ + set = TestObsLat( this, status ); + dval = set ? GetObsLat( this, status ) : astGetObsLat( this ); + astWriteDouble( channel, "ObsLat", set, 0, dval, "Observers geodetic latitude (rads)" ); + +/* ObsLon. */ +/* ------- */ + set = TestObsLon( this, status ); + dval = set ? GetObsLon( this, status ) : astGetObsLon( this ); + astWriteDouble( channel, "ObsLon", set, 0, dval, "Observers geodetic longitude (rads)" ); + +/* ObsAlt. */ +/* ------- */ + set = TestObsAlt( this, status ); + dval = set ? GetObsAlt( this, status ) : astGetObsAlt( this ); + astWriteDouble( channel, "ObsAlt", set, 0, dval, "Observers geodetic altitude (metres)" ); + +/* Dut1*/ +/* ---- */ + set = TestDut1( this, status ); + dval = set ? GetDut1( this, status ) : astGetDut1( this ); + astWriteDouble( channel, "Dut1", set, 0, dval, "UT1-UTC in seconds" ); + + +/* ActiveUnit. */ +/* ----------- */ + if( astTestActiveUnit( this ) ) { + ival = astGetActiveUnit( this ); + astWriteInt( channel, "ActUnt", 1, 0, ival, + ival ? "Unit strings affects alignment" : + "Unit strings do not affect alignment" ); + } + +/* Axis permutation array. */ +/* ----------------------- */ +/* Write out the axis permutation array value for each axis, + converting to 1-based axis numbering. */ + for ( axis = 0; axis < this->naxes; axis++ ) { + set = ( this->perm[ axis ] != axis ); + ival = this->perm[ axis ] + 1; + +/* Create a keyword and comment appropriate to the axis. */ + (void) sprintf( key, "Prm%d", axis + 1 ); + if ( set ) { + (void) sprintf( comment, + "Axis %d permuted to use internal axis %d", + axis + 1, ival ); + } else { + (void) sprintf( comment, "Axis %d not permuted", axis + 1 ); + } + astWriteInt( channel, key, set, 0, ival, comment ); + } + +/* Axis Objects. */ +/* ------------- */ +/* Temporarily set the Channel's Full attribute to -1 (unless it is +1 + to start with), remembering the original setting. This prevents any + unnecessary "un-set" Axis values being output that would otherwise + simply duplicate the Frame's attributes which have already been + written. "Set" Axis values are still written, however (and all + values are written if Full is set to 1). */ + full_set = astTestFull( channel ); + full = astGetFull( channel ); + if ( full <= 0 ) astSetFull( channel, -1 ); + +/* Handle each axis in turn. */ + for ( axis = 0; axis < this->naxes; axis++ ) { + +/* Create a keyword and comment appropriate to the axis (converting to + 1-based axis numbering). */ + (void) sprintf( key, "Ax%d", axis + 1 ); + (void) sprintf( comment, "Axis number %d", axis + 1 ); + +/* Write out the axis Object description. */ + astWriteObject( channel, key, 1, 0, this->axis[ axis ], comment ); + } + +/* Restore the Channel's original Full attribute setting. */ + if ( full_set ) { + astSetFull( channel, full ); + } else { + astClearFull( channel ); + } + +/* Free the inverse axis permutation array. */ + invperm = astFree( invperm ); + +/* Variants */ +/* ------- */ + if( this->variants ) astWriteObject( channel, "Vrnts", 1, 0, + this->variants, "Variant Frames" ); + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAFrame and astCheckFrame functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Frame,Mapping) +astMAKE_CHECK(Frame) + +AstFrame *astFrame_( int naxes, const char *options, int *status, ...) { +/* +*+ +* Name: +* astFrame + +* Purpose: +* Create a Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "frame.h" +* AstFrame *astFrame( int naxes, const char *options, int *status, ... ) + +* Class Membership: +* Frame constructor. + +* Description: +* This function creates a new Frame and optionally initialises its +* attributes. + +* Parameters: +* naxes +* The number of Frame axes. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new Frame. The syntax used is the same as +* for the astSet method and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of arguments may follow it in order to +* supply values to be substituted for these specifiers. The +* rules for supplying these are identical to those for the +* astSet method (and for the C "printf" function). + +* Returned Value: +* A pointer to the new Frame. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic Frame constructor which is +* available via the protected interface to the Frame class. A +* public interface is provided by the astFrameId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *new; /* Pointer to new Frame */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the Frame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitFrame( NULL, sizeof( AstFrame ), !class_init, &class_vtab, + "Frame", naxes ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Frame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Frame. */ + return new; +} + +AstFrame *astInitFrame_( void *mem, size_t size, int init, + AstFrameVtab *vtab, const char *name, + int naxes, int *status ) { +/* +*+ +* Name: +* astInitFrame + +* Purpose: +* Initialise a Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "frame.h" +* AstFrame *astInitFrame( void *mem, size_t size, int init, +* AstFrameVtab *vtab, const char *name, +* int naxes ) + +* Class Membership: +* Frame initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Frame object. It allocates memory (if necessary) to accommodate +* the Frame plus any additional data associated with the derived class. +* It then initialises a Frame structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Frame at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Frame is to be created. This +* must be of sufficient size to accommodate the Frame data +* (sizeof(Frame)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Frame (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Frame +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Frame's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Frame. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* naxes +* The number of Frame axes. + +* Returned Value: +* A pointer to the new Frame. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFrame *new; /* Pointer to new Frame */ + int axis; /* Loop counter for Frame axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitFrameVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the number of axes for validity, reporting an error if necessary. */ + if ( naxes < 0 ) { + astError( AST__NAXIN, "astInitFrame(%s): Number of axes (%d) is " + "invalid - this number should not be negative.", status, name, naxes ); + +/* Initialise a Mapping structure (the parent class) as the first + component within the Frame structure, allocating memory if + necessary. Set the number of input/output coordinates to zero (the + astGetNin and astGetNout methods are over-ridden by the Frame class + to provide values for these that are equal to the number of Frame + axes). */ + } else { + new = (AstFrame *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 0, 0, 1, 1 ); + + if ( astOK ) { + +/* Initialise the Frame data. */ +/* ----------------------------- */ +/* Set the number of Frame axes. */ + new->naxes = naxes; + +/* Initialise all attributes to their "undefined" values. */ + new->digits = -INT_MAX; + new->domain = NULL; + new->epoch = AST__BAD; + new->match_end = -INT_MAX; + new->max_axes = -INT_MAX; + new->min_axes = -INT_MAX; + new->permute = -INT_MAX; + new->preserve_axes = -INT_MAX; + new->title = NULL; + new->system = AST__BADSYSTEM; + new->alignsystem = AST__BADSYSTEM; + new->active_unit = -INT_MAX; + new->obsalt = AST__BAD; + new->obslat = AST__BAD; + new->obslon = AST__BAD; + new->dut1 = AST__BAD; + new->flags = 0; + new->variants = NULL; + +/* Allocate memory to store pointers to the Frame's Axis objects and to store + its axis permutation array. */ + new->axis = astMalloc( sizeof( AstAxis * ) * (size_t) naxes ); + new->perm = astMalloc( sizeof( int ) * (size_t) naxes ); + +/* Create a new Axis object to describe each axis of the Frame and store the + resulting pointers in the memory allocated above. Also initialise the + axis permutation array so that the axes appear in their natural order. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + new->axis[ axis ] = astAxis( "", status ); + new->perm[ axis ] = axis; + } + +/* If an error occurred while creating the Axis objects, scan through the array + of pointers to them again to ensure that they are all correctly annulled. */ + if ( !astOK ) { + for ( axis = 0; axis < naxes; axis++ ) { + new->axis[ axis ] = astAnnul( new->axis[ axis ] ); + } + } + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstFrame *astLoadFrame_( void *mem, size_t size, + AstFrameVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadFrame + +* Purpose: +* Load a Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "frame.h" +* AstFrame *astLoadFrame( void *mem, size_t size, +* AstFrameVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Frame loader. + +* Description: +* This function is provided to load a new Frame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Frame structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Frame at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the Frame is to be loaded. +* This must be of sufficient size to accommodate the Frame data +* (sizeof(Frame)) plus any data used by derived classes. If a +* value of NULL is given, this function will allocate the +* memory itself using the "size" parameter to determine its +* size. +* size +* The amount of memory used by the Frame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Frame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Frame. If this is NULL, a pointer to +* the (static) virtual function table for the Frame class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Frame" is used instead. + +* Returned Value: +* A pointer to the new Frame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstFrame *new; /* Pointer to the new Frame */ + char *sval; /* Pointer to string value */ + char key[ KEY_LEN + 1 ]; /* Buffer for keywords */ + double dval; /* DOuble attribute value */ + int axis; /* Loop counter for axes */ + int ival; /* Integer value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Frame. In this case the + Frame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstFrame ); + vtab = &class_vtab; + name = "Frame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Frame. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Assign values for transient components that are not included in the + Frame dump */ + new->flags = 0; + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Frame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Naxes. */ +/* ------ */ +/* Obtain the number of Frame axes and allocate memory for the arrays + which hold axis information. */ + new->naxes = astReadInt( channel, "naxes", 0 ); + if ( new->naxes < 0 ) new->naxes = 0; + new->perm = astMalloc( sizeof( int ) * (size_t) new->naxes ); + new->axis = astMalloc( sizeof( AstAxis * ) * (size_t) new->naxes ); + +/* If an error occurred, ensure that any allocated memory is freed. */ + if ( !astOK ) { + new->perm = astFree( new->perm ); + new->axis = astFree( new->axis ); + +/* Otherwise, initialise the array of Axis pointers. */ + } else { + for ( axis = 0; axis < new->naxes; axis++ ) new->axis[ axis ] = NULL; + +/* Now obtain those input values which are required for each axis... */ + for ( axis = 0; axis < new->naxes; axis++ ) { + +/* Axis object. */ +/* ------------ */ +/* This must be read first, so that it can hold the other axis values + obtained below. */ + +/* Create a keyword appropriate to this axis. */ + (void) sprintf( key, "ax%d", axis + 1 ); + +/* Read the Axis object. If none was read, provide a default Axis + instead. */ + new->axis[ axis ] = astReadObject( channel, key, NULL ); + if ( !new->axis[ axis ] ) new->axis[ axis ] = astAxis( "", status ); + +/* Label. */ +/* ------ */ +/* Read the Label string for each axis. If a value is obtained, use + it to set the Label attribute for the axis. Free the memory holding + the string when no longer needed. */ + (void) sprintf( key, "lbl%d", axis + 1 ); + sval = astReadString( channel, key, NULL ); + if ( sval ) { + astSetAxisLabel( new->axis[ axis ], sval ); + sval = astFree( sval ); + } + +/* Symbol. */ +/* ------- */ + (void) sprintf( key, "sym%d", axis + 1 ); + sval = astReadString( channel, key, NULL ); + if ( sval ) { + astSetAxisSymbol( new->axis[ axis ], sval ); + sval = astFree( sval ); + } + +/* Format. */ +/* ------- */ + (void) sprintf( key, "fmt%d", axis + 1 ); + sval = astReadString( channel, key, NULL ); + if ( sval ) { + astSetAxisFormat( new->axis[ axis ], sval ); + sval = astFree( sval ); + } + +/* Unit. */ +/* ----- */ + (void) sprintf( key, "uni%d", axis + 1 ); + sval = astReadString( channel, key, NULL ); + if ( sval ) { + astSetAxisUnit( new->axis[ axis ], sval ); + sval = astFree( sval ); + } + +/* Direction. */ +/* ---------- */ + (void) sprintf( key, "dir%d", axis + 1 ); + ival = astReadInt( channel, key, -INT_MAX ); + if ( ival != -INT_MAX ) { + astSetAxisDirection( new->axis[ axis ], ival ); + } + +/* Top. */ +/*----- */ + (void) sprintf( key, "top%d", axis + 1 ); + dval = astReadDouble( channel, key, AST__BAD ); + if ( dval != AST__BAD ) { + astSetAxisTop( new->axis[ axis ], dval ); + } + +/* Bottom. */ +/*----- -- */ + (void) sprintf( key, "bot%d", axis + 1 ); + dval = astReadDouble( channel, key, AST__BAD ); + if ( dval != AST__BAD ) { + astSetAxisBottom( new->axis[ axis ], dval ); + } + +/* Digits. */ +/* ------- */ + (void) sprintf( key, "dig%d", axis + 1 ); + ival = astReadInt( channel, key, -INT_MAX ); + if ( ival != -INT_MAX ) { + astSetAxisDigits( new->axis[ axis ], ival ); + } + +/* Axis permutation array. */ +/* ----------------------- */ +/* Convert from 1-based to zero-based axis numbering at this + point. The default is the "un-permuted" value. */ + sprintf( key, "prm%d", axis + 1 ); + new->perm[ axis ] = astReadInt( channel, key, axis + 1 ) - 1; + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + +/* The remaining values are not associated with particular axes... */ + +/* Title. */ +/* ------ */ + new->title = astReadString( channel, "title", NULL ); + +/* Domain. */ +/* ------- */ + new->domain = astReadString( channel, "domain", NULL ); + +/* Epoch. */ +/* ------ */ +/* Interpret this as Besselian or Julian depending on its value. */ + new->epoch = astReadDouble( channel, "epoch", AST__BAD ); + if ( TestEpoch( new, status ) ) { + SetEpoch( new, ( new->epoch < 1984.0 ) ? palEpb2d( new->epoch ) : + palEpj2d( new->epoch ), status ); + } + +/* Digits. */ +/* ------- */ +/* This is the value that applies to the Frame as a whole. */ + new->digits = astReadInt( channel, "digits", -INT_MAX ); + if ( TestDigits( new, status ) ) SetDigits( new, new->digits, status ); + +/* PreserveAxes. */ +/* ------------- */ + new->preserve_axes = astReadInt( channel, "presrv", -INT_MAX ); + if ( TestPreserveAxes( new, status ) ) { + SetPreserveAxes( new, new->preserve_axes, status ); + } + +/* Permute. */ +/* -------- */ + new->permute = astReadInt( channel, "permut", -INT_MAX ); + if ( TestPermute( new, status ) ) SetPermute( new, new->permute, status ); + +/* MinAxes. */ +/* -------- */ + new->min_axes = astReadInt( channel, "minax", -INT_MAX ); + if ( TestMinAxes( new, status ) ) SetMinAxes( new, new->min_axes, status ); + +/* MaxAxes. */ +/* -------- */ + new->max_axes = astReadInt( channel, "maxax", -INT_MAX ); + if ( TestMaxAxes( new, status ) ) SetMaxAxes( new, new->max_axes, status ); + +/* MatchEnd. */ +/* --------- */ + new->match_end = astReadInt( channel, "mchend", -INT_MAX ); + if ( TestMatchEnd( new, status ) ) SetMatchEnd( new, new->match_end, status ); + +/* ObsLat. */ +/* ------- */ + new->obslat = astReadDouble( channel, "obslat", AST__BAD ); + if ( TestObsLat( new, status ) ) SetObsLat( new, new->obslat, status ); + +/* ObsLon. */ +/* ------- */ + new->obslon = astReadDouble( channel, "obslon", AST__BAD ); + if ( TestObsLon( new, status ) ) SetObsLon( new, new->obslon, status ); + +/* ObsAlt. */ +/* ------- */ + new->obsalt = astReadDouble( channel, "obsalt", AST__BAD ); + if ( TestObsAlt( new, status ) ) SetObsAlt( new, new->obsalt, status ); + +/* Dut1. */ +/* ---- */ + new->dut1 = astReadDouble( channel, "dut1", AST__BAD ); + if ( TestDut1( new, status ) ) SetDut1( new, new->dut1, status ); + +/* ActiveUnit. */ +/* ----------- */ + new->active_unit = astReadInt( channel, "actunt", -INT_MAX ); + if ( TestActiveUnit( new, status ) ) SetActiveUnit( new, new->active_unit, status ); + +/* System. */ +/* ------- */ +/* Set the default and read the external representation as a string. */ + new->system = AST__BADSYSTEM; + sval = astReadString( channel, "system", NULL ); + +/* If a value was read, convert from a string to a System code. */ + if ( sval ) { + if ( astOK ) { + new->system = astSystemCode( new, sval ); + +/* Report an error if the value wasn't recognised. */ + if ( new->system == AST__BADSYSTEM ) { + astError( AST__ATTIN, + "astRead(%s): Invalid System description " + "\"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* AlignSystem. */ +/* ------------ */ +/* Set the default and read the external representation as a string. */ + new->alignsystem = AST__BADSYSTEM; + sval = astReadString( channel, "alsys", NULL ); + +/* If a value was read, convert from a string to a System code. */ + if ( sval ) { + if ( astOK ) { + new->alignsystem = astSystemCode( new, sval ); + +/* Report an error if the value wasn't recognised. */ + if ( new->alignsystem == AST__BADSYSTEM ) { + astError( AST__ATTIN, + "astRead(%s): Invalid AlignSystem description " + "\"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* Variants. */ +/* -------- */ + new->variants = astReadObject( channel, "vrnts", NULL ); + } + +/* If an error occurred, clean up by deleting the new Frame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Frame pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +const char *astAbbrev_( AstFrame *this, int axis, const char *fmt, + const char *str1, const char *str2, int *status ) { + if ( !astOK ) return str2; + return (**astMEMBER(this,Frame,Abbrev))( this, axis, fmt, str1, str2, status ); +} +int astFields_( AstFrame *this, int axis, const char *fmt, + const char *str, int maxfld, char **fields, + int *nc, double *val, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,Fields))( this, axis, fmt, str, maxfld, fields, nc, val, status ); +} +void astCheckPerm_( AstFrame *this, const int *perm, const char *method, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,CheckPerm))( this, perm, method, status ); +} + +AstPointSet *astResolvePoints_( AstFrame *this, const double point1[], + const double point2[], AstPointSet *in, + AstPointSet *out, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,ResolvePoints))( this, point1, point2, in, out, status ); +} +AstLineDef *astLineDef_( AstFrame *this, const double start[2], + const double end[2], int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,LineDef))( this, start, end, status ); +} +int astLineCrossing_( AstFrame *this, AstLineDef *l1, AstLineDef *l2, + double **cross, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,LineCrossing))( this, l1, l2, cross, status ); +} +void astLineOffset_( AstFrame *this, AstLineDef *line, double par, double prp, + double point[2], int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Frame,LineOffset))( this, line, par, prp, point, status ); +} +int astLineContains_( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,LineContains))( this, l, def, point, status ); +} +AstFrameSet *astConvert_( AstFrame *from, AstFrame *to, + const char *domainlist, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(from,Frame,Convert))( from, to, domainlist, status ); +} +AstFrameSet *astConvertX_( AstFrame *to, AstFrame *from, + const char *domainlist, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(to,Frame,ConvertX))( to, from, domainlist, status ); +} +double astAngle_( AstFrame *this, const double a[], const double b[], + const double c[], int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Frame,Angle))( this, a, b, c, status ); +} +int astGetActiveUnit_( AstFrame *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,GetActiveUnit))( this, status ); +} +int astTestActiveUnit_( AstFrame *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,TestActiveUnit))( this, status ); +} +void astSetActiveUnit_( AstFrame *this, int value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,SetActiveUnit))( this, value, status ); +} +double astDistance_( AstFrame *this, + const double point1[], const double point2[], int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Frame,Distance))( this, point1, point2, status ); +} +AstFrameSet *astFindFrame_( AstFrame *target, AstFrame *template, + const char *domainlist, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(target,Frame,FindFrame))( target, template, domainlist, status ); +} +void astMatchAxes_( AstFrame *frm1, AstFrame *frm2, int *axes, int *status ) { + if ( !astOK ) return; + (**astMEMBER(frm1,Frame,MatchAxes))( frm1, frm2, axes, status ); +} +void astMatchAxesX_( AstFrame *frm2, AstFrame *frm1, int *axes, int *status ) { + if ( !astOK ) return; + (**astMEMBER(frm2,Frame,MatchAxesX))( frm2, frm1, axes, status ); +} +const char *astFormat_( AstFrame *this, int axis, double value, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,Format))( this, axis, value, status ); +} +double astCentre_( AstFrame *this, int axis, double value, double gap, int *status ) { + if ( !astOK ) return 0.0; + return (**astMEMBER(this,Frame,Centre))( this, axis, value, gap, status ); +} +double astGap_( AstFrame *this, int axis, double gap, int *ntick, int *status ) { + if ( !astOK ) return 0.0; + return (**astMEMBER(this,Frame,Gap))( this, axis, gap, ntick, status ); +} +AstAxis *astGetAxis_( AstFrame *this, int axis, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,GetAxis))( this, axis, status ); +} +int astGetNaxes_( AstFrame *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,GetNaxes))( this, status ); +} +const int *astGetPerm_( AstFrame *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,GetPerm))( this, status ); +} +AstFrameSet *astGetFrameVariants_( AstFrame *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,GetFrameVariants))( this, status ); +} +void astSetFrameVariants_( AstFrame *this, AstFrameSet *variants, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,SetFrameVariants))( this, variants, status ); +} + + +int astMatch_( AstFrame *this, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { + + AstFrame *super_this; + const char *dom; + int match; + + if ( !astOK ) return 0; + + match = (**astMEMBER(this,Frame,Match))( this, target, matchsub, + template_axes, target_axes, + map, result, status ); + +/* If the template ("this") could not be used to probe the target, it may + be because the template class is a more specialised form of the target + class. E.g. a SkyFrame cannot directly be used to probe a Frame, but a + Frame *can* be used to probe a SkyFrame. This means (for instance), + that a basic Frame with Domain FRED cannot be aligned (using astConvert) + with a CmpFrame with Domain FRED. This sort of alignment is often + useful, so we try now to use the supplied template to probe a modified + form of the target that has been cast into the same class as the + template. This is only possible if the template class is a sub-class of + the target class. Attempt to do the cast. */ + if( ! match && matchsub ) { + super_this = (AstFrame *) astCast( this, target ); + +/* If the cast was possible, fix the template Domain since the parent + class may provide a different default Domain, and then invoke the Match + method appropriate to the new template class (i.e. the target class). */ + if( super_this ) { + if( astTestDomain( target ) ) { + dom = astGetDomain( this ); + if( astChrLen( dom ) > 0 ) astSetDomain( super_this, dom ); + } + match = (**astMEMBER(super_this,Frame,Match))( super_this, target, + matchsub, template_axes, + target_axes, map, + result, status ); + super_this = astAnnul( super_this ); + } + } + + return match; +} + + +int astIsUnitFrame_( AstFrame *this, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,IsUnitFrame))( this, status ); +} +void astNorm_( AstFrame *this, double value[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,Norm))( this, value, status ); +} +void astNormBox_( AstFrame *this, double lbnd[], double ubnd[], AstMapping *reg, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,NormBox))( this, lbnd, ubnd, reg, status ); +} +double astAxDistance_( AstFrame *this, int axis, double v1, double v2, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Frame,AxDistance))( this, axis, v1, v2, status ); +} +double astAxOffset_( AstFrame *this, int axis, double v1, double dist, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Frame,AxOffset))( this, axis, v1, dist, status ); +} + + +AstPointSet *astFrameGrid_( AstFrame *this, int size, const double *lbnd, + const double *ubnd, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,FrameGrid))( this, size, lbnd, ubnd, status ); +} + + +void astOffset_( AstFrame *this, const double point1[], const double point2[], + double offset, double point3[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,Offset))( this, point1, point2, offset, point3, status ); +} +double astAxAngle_( AstFrame *this, const double a[2], const double b[2], + int axis, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Frame,AxAngle))( this, a, b, axis, status ); +} +double astOffset2_( AstFrame *this, const double point1[2], double angle, + double offset, double point2[2], int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,Frame,Offset2))( this, point1, angle, offset, point2, status ); +} +void astIntersect_( AstFrame *this, const double a1[2], + const double a2[2], const double b1[2], + const double b2[2], double cross[2], + int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,Intersect))( this, a1, a2, b1, b2, cross, status ); +} +void astOverlay_( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { + if ( !astOK ) return; + (**astMEMBER(template,Frame,Overlay))( template, template_axes, result, status ); +} +void astPermAxes_( AstFrame *this, const int perm[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,PermAxes))( this, perm, status ); +} +AstFrame *astPickAxes_( AstFrame *this, int naxes, const int axes[], + AstMapping **map, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,PickAxes))( this, naxes, axes, map, status ); +} +void astPrimaryFrame_( AstFrame *this, int axis1, + AstFrame **frame, int *axis2, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,PrimaryFrame))( this, axis1, frame, axis2, status ); +} +void astResolve_( AstFrame *this, const double point1[], const double point2[], + const double point3[], double point4[], double *d1, + double *d2, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,Resolve))( this, point1, point2, point3, point4, d1, d2, status ); +} +void astSetAxis_( AstFrame *this, int axis, AstAxis *newaxis, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,SetAxis))( this, axis, newaxis, status ); +} +void astSetUnit_( AstFrame *this, int axis, const char *value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,SetUnit))( this, axis, value, status ); +} +void astClearUnit_( AstFrame *this, int axis, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,ClearUnit))( this, axis, status ); +} +int astSubFrame_( AstFrame *target, AstFrame *template, int result_naxes, + const int *target_axes, const int *template_axes, + AstMapping **map, AstFrame **result, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(target,Frame,SubFrame))( target, template, result_naxes, + target_axes, template_axes, + map, result, status ); +} +int astUnformat_( AstFrame *this, int axis, const char *string, + double *value, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,Unformat))( this, axis, string, value, status ); +} +int astValidateAxis_( AstFrame *this, int axis, int fwd, const char *method, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,ValidateAxis))( this, axis, fwd, method, status ); +} +void astValidateAxisSelection_( AstFrame *this, int naxes, const int *axes, + const char *method, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,ValidateAxisSelection))( this, naxes, axes, + method, status ); +} +AstSystemType astValidateSystem_( AstFrame *this, AstSystemType system, const char *method, int *status ) { + if ( !astOK ) return AST__BADSYSTEM; + return (**astMEMBER(this,Frame,ValidateSystem))( this, system, method, status ); +} +AstSystemType astSystemCode_( AstFrame *this, const char *system, int *status ) { + if ( !astOK ) return AST__BADSYSTEM; + return (**astMEMBER(this,Frame,SystemCode))( this, system, status ); +} +const char *astSystemString_( AstFrame *this, AstSystemType system, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Frame,SystemString))( this, system, status ); +} +int astAxIn_( AstFrame *this, int axis, double lo, double hi, double val, + int closed, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,AxIn))( this, axis, lo, hi, val, closed, status ); +} +int astGetFrameFlags_( AstFrame *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Frame,GetFrameFlags))( this, status ); +} +void astSetFrameFlags_( AstFrame *this, int value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Frame,SetFrameFlags))( this, value, status ); +} + + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstFrame *PickAxesId_( AstFrame *, int, const int[], AstMapping **, int * ); +AstFrame *astFrameId_( int, const char *, ... ); +const char *astFormatId_( AstFrame *, int, double, int * ); +int astUnformatId_( AstFrame *, int, const char *, double *, int * ); +void astPermAxesId_( AstFrame *, const int[], int * ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +const char *astFormatId_( AstFrame *this, int axis, double value, int *status ) { +/* +*++ +* Name: +c astFormat +f AST_FORMAT + +* Purpose: +* Format a coordinate value for a Frame axis. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c const char *astFormat( AstFrame *this, int axis, double value ) +f RESULT = AST_FORMAT( THIS, AXIS, VALUE, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function returns a pointer to a string containing the +f This function returns a character string containing the +* formatted (character) version of a coordinate value for a Frame +* axis. The formatting applied is determined by the Frame's +* attributes and, in particular, by any Format attribute string +* that has been set for the axis. A suitable default format (based +* on the Digits attribute value) will be applied if necessary. + +* Parameters: +c this +f THIS = INTEGER (given) +* Pointer to the Frame. +c axis +f AXIS = INTEGER (Given) +* The number of the Frame axis for which formatting is to be +* performed (axis numbering starts at 1 for the first axis). +c value +f VALUE = DOUBLE PRECISION (Given) +* The coordinate value to be formatted. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFormat() +c A pointer to a null-terminated string containing the formatted +c value. +f AST_FORMAT = CHARACTER * ( AST__SZCHR ) +f The formatted value. + +* Notes: +c - The returned pointer is guaranteed to remain valid and the +c string to which it points will not be over-written for a total +c of 50 successive invocations of this function. After this, the +c memory containing the string may be re-used, so a copy of the +c string should be made if it is needed for longer than this. +c - A formatted value may be converted back into a numerical (double) +c value using astUnformat. +f - A formatted value may be converted back into a numerical +f (double precision) value using AST_UNFORMAT. +c - A NULL pointer will be returned if this function is invoked +c with the AST error status set, or if it should fail for any +c reason. +f - A blank string will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any +f reason. +*-- + +* Implementation Notes: +* This function implements the public interface for the astFormat +* method. It is identical to astFormat_ except that: +* +* - The axis index is decremented by 1 before use. This allows the +* public interface to use 1-based axis numbers (whereas internally +* axis numbers are zero-based). +* +* - The returned string value is buffered in dynamically allocated +* memory so that it will remain valid for a guaranteed number of +* function invocations. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + const char *fvalue; /* Pointer to formatted value */ + const char *result; /* Pointer value to return */ + int i; /* Loop counter for initialisation */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(this); + +/* If the "astformatid_strings" array has not been initialised, fill it with NULL + pointers. */ + if ( !astformatid_init ) { + astformatid_init = 1; + for ( i = 0; i < ASTFORMATID_MAX_STRINGS; i++ ) astformatid_strings[ i ] = NULL; + } + +/* Invoke the normal astFormat_ function to obtain a pointer to the + required formatted value, adjusting the axis index to become + zero-based. */ + fvalue = astFormat( this, axis - 1, value ); + +/* If OK, store a copy of the resulting string in dynamically allocated memory, + putting a pointer to the copy into the next element of the "astformatid_strings" + array. (This process also de-allocates any previously allocated memory pointed + at by this "astformatid_strings" element, so the earlier string is effectively + replaced by the new one.) */ + if ( astOK ) { + astBeginPM; + astformatid_strings[ astformatid_istr ] = astStore( astformatid_strings[ astformatid_istr ], fvalue, + strlen( fvalue ) + (size_t) 1 ); + astEndPM; + +/* If OK, return a pointer to the copy and increment "astformatid_istr" to use + the next element of "astformatid_strings" on the next invocation. Recycle + "astformatid_istr" to zero when all elements have been used. */ + if ( astOK ) { + result = astformatid_strings[ astformatid_istr++ ]; + if ( astformatid_istr == ( ASTFORMATID_MAX_STRINGS - 1 ) ) astformatid_istr = 0; + } + } + +/* Return the result. */ + return result; + +} + +AstFrame *astFrameId_( int naxes, const char *options, ... ) { +/* +*++ +* Name: +c astFrame +f AST_FRAME + +* Purpose: +* Create a Frame. + +* Type: +* Public function. + +* Synopsis: +c #include "frame.h" +c AstFrame *astFrame( int naxes, const char *options, ... ) +f RESULT = AST_FRAME( NAXES, OPTIONS, STATUS ) + +* Class Membership: +* Frame constructor. + +* Description: +* This function creates a new Frame and optionally initialises its +* attributes. +* +* A Frame is used to represent a coordinate system. It does this +* in rather the same way that a frame around a graph describes the +* coordinate space in which data are plotted. Consequently, a +* Frame has a Title (string) attribute, which describes the +* coordinate space, and contains axes which in turn hold +* information such as Label and Units strings which are used for +* labelling (e.g.) graphical output. In general, however, the +* number of axes is not restricted to two. +* +* Functions are available for converting Frame coordinate values +* into a form suitable for display, and also for calculating +* distances and offsets between positions within the Frame. +* +* Frames may also contain knowledge of how to transform to and +* from related coordinate systems. + +* Parameters: +c naxes +f NAXES = INTEGER (Given) +* The number of Frame axes (i.e. the number of dimensions of +* the coordinate space which the Frame describes). +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Frame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Frame. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFrame() +f AST_FRAME = INTEGER +* A pointer to the new Frame. + +* Examples: +c frame = astFrame( 2, "Title=Energy Spectrum: Plot %d", n ); +c Creates a new 2-dimensional Frame and initialises its Title +c attribute to the string "Energy Spectrum: Plot ", where +c takes the value of the int variable "n". +c frame = astFrame( 2, "Label(1)=Energy, Label(2)=Response" ); +c Creates a new 2-dimensional Frame and initialises its axis +c Label attributes to suitable string values. +f FRAME = AST_FRAME( 2, 'Title=Energy Spectrum', STATUS ); +f Creates a new 2-dimensional Frame and initialises its Title +f attribute to the string "Energy Spectrum". +f FRAME = AST_FRAME( 2, 'Label(1)=Energy, Label(2)=Response', STATUS ); +f Creates a new 2-dimensional Frame and initialises its axis +f Label attributes to suitable string values. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astFrame_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *new; /* Pointer to new Frame */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the Frame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitFrame( NULL, sizeof( AstFrame ), !class_init, &class_vtab, + "Frame", naxes ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Frame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Frame. */ + return astMakeId( new ); +} + +void astPermAxesId_( AstFrame *this, const int perm[], int *status ) { +/* +*++ +* Name: +c astPermAxes +f AST_PERMAXES + +* Purpose: +* Permute the axis order in a Frame. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c void astPermAxes( AstFrame *this, const int perm[] ) +f CALL AST_PERMAXES( THIS, PERM, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function permutes the order in which a Frame's axes occur. +f This routine permutes the order in which a Frame's axes occur. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c perm +f PERM( * ) = INTEGER (Given) +* An array with one element for each axis of the Frame (Naxes +* attribute). This should list the axes in their new order, +* using the original axis numbering (which starts at 1 for the +* first axis). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Only genuine permutations of the axis order are permitted, so +c each axis must be referenced exactly once in the "perm" array. +f each axis must be referenced exactly once in the PERM array. +* - If successive axis permutations are applied to a Frame, then +* the effects are cumulative. +*-- + +* Implementation Notes: +* This function implements the public interface for the +* astPermAxes method. It is identical to astPermAxes_ except that +* the axis numbers in the "perm" array are decremented by 1 before +* use. This is to allow the public interface to use one-based axis +* numbering (internally, zero-based axis numbering is used). +*/ + +/* Local Variables: */ + int *perm1; /* Pointer to modified perm array */ + int axis; /* Loop counter for Frame axes */ + int naxes; /* Number of Frame axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the number of Frame axes. */ + naxes = astGetNaxes( this ); + +/* Allocate an array to hold a modified version of the "perm" + array. */ + perm1 = astMalloc( sizeof( int ) * (size_t) naxes ); + if ( astOK ) { + +/* Make a modified copy of the "perm" array by subtracting one from + each element. This allows the public interface to use one-based + axis numbering, whereas all internal code is zero-based. */ + for ( axis = 0; axis < naxes; axis++ ) perm1[ axis ] = perm[ axis ] - 1; + +/* Invoke the normal astPermAxes_ function to permute the Frame's axes. */ + astPermAxes( this, perm1 ); + } + +/* Free the temporary array. */ + perm1 = astFree( perm1 ); +} + +AstFrame *astPickAxesId_( AstFrame *this, int naxes, const int axes[], + AstMapping **map, int *status ) { +/* +*++ +* Name: +c astPickAxes +f AST_PICKAXES + +* Purpose: +* Create a new Frame by picking axes from an existing one. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c AstFrame *astPickAxes( AstFrame *this, int naxes, const int axes[], +c AstMapping **map ) +f RESULT = AST_PICKAXES( THIS, NAXES, AXES, MAP, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +* This function creates a new Frame whose axes are copied from an +* existing Frame along with other Frame attributes, such as its +* Title. Any number (zero or more) of the original Frame's axes +* may be copied, in any order, and additional axes with default +* attributes may also be included in the new Frame. +* +c Optionally, a Mapping that converts between the coordinate +f A Mapping that converts between the coordinate +* systems described by the two Frames will also be returned. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the original Frame. +c naxes +f NAXES = INTEGER (Given) +* The number of axes required in the new Frame. +c axes +f AXES( NAXES ) = INTEGER (Given) +c An array, with "naxes" elements, which lists the axes to be +f An array which lists the axes to be +* copied. These should be given in the order required in the +* new Frame, using the axis numbering in the original Frame +* (which starts at 1 for the first axis). Axes may be selected +* in any order, but each may only be used once. If additional +* (default) axes are also to be included, the corresponding +* elements of this array should be set to zero. +c map +f MAP = INTEGER (Returned) +c Address of a location in which to return a pointer to a new +f A pointer to a new +* Mapping. This will be a PermMap (or a UnitMap as a special +* case) that describes the axis permutation that has taken +* place between the original and new Frames. The Mapping's +* forward transformation will convert coordinates from the +* original Frame into the new one, and vice versa. +c +c If this Mapping is not required, a NULL value may be supplied +c for this parameter. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPickAxes() +f AST_PICKAXES = INTEGER +* A pointer to the new Frame. + +* Applicability: +* Frame +* This function applies to all Frames. The class of Frame returned +* may differ from that of the original Frame, depending on which +* axes are selected. For example, if a single axis is picked from a +* SkyFrame (which must always have two axes) then the resulting +* Frame cannot be a valid SkyFrame, so will revert to the parent +* class (Frame) instead. +* FrameSet +* Using this function on a FrameSet is identical to using it on +* the current Frame in the FrameSet. The returned Frame will not +* be a FrameSet. +* Region +* If this function is used on a Region, an attempt is made to +* retain the bounds information on the selected axes. If +* succesful, the returned Frame will be a Region of some class. +* Otherwise, the returned Frame is obtained by calling this +* function on the Frame represented by the supplied Region (the +* returned Frame will then not be a Region). In order to be +* succesful, the selected axes in the Region must be independent +* of the others. For instance, a Box can be split in this way but +* a Circle cannot. Another requirement for success is that no +* default axes are added (that is, the +c "axes" +f AXES +* array must not contain any zero values. + +* Notes: +c - The new Frame will contain a "deep" copy (c.f. astCopy) of all +f - The new Frame will contain a "deep" copy (c.f. AST_COPY) of all +* the data selected from the original Frame. Modifying any aspect +* of the new Frame will therefore not affect the original one. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* This function implements the public interface for the +* astPickAxes method. It is identical to astPickAxes_ except for +* the following: +* +* - The axis numbers in the "axes" array are decremented by 1 before +* use. This is to allow the public interface to use one-based axis +* numbering (internally, zero-based axis numbering is used). +* +* - An ID value is returned via the "map" parameter (if used) +* instead of a true C pointer. This is required because this +* conversion cannot be performed by the macro that invokes the +* function. +*/ + +/* Local Variables: */ + AstFrame *result; /* Pointer to result Frame */ + int *axes1; /* Pointer to modified axes array */ + int axis; /* Loop counter for axes */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate an array to hold a modified version of the "axes" array + (check that "naxes" is valid first - if not, this error will be + reported by astPickAxes_ below). */ + axes1 = ( naxes >= 0 ) ? astMalloc( sizeof( int ) * (size_t) naxes ) : + NULL; + if ( astOK ) { + +/* Make a modified copy of the "axes" array by subtracting one from + each element. This allows the public interface to use one-based + axis numbering, whereas all internal code is zero-based. */ + for ( axis = 0; axis < naxes; axis++ ) axes1[ axis ] = axes[ axis ] - 1; + +/* Invoke the normal astPickAxes_ function to select the required axes. */ + result = astPickAxes( this, naxes, axes1, map ); + } + +/* Free the temporary array. */ + axes1 = astFree( axes1 ); + +/* If required, return an ID value for the Mapping. */ + if ( map ) *map = astMakeId( *map ); + +/* Return the result. */ + return result; +} + +int astUnformatId_( AstFrame *this, int axis, const char *string, + double *value, int *status ) { +/* +*++ +* Name: +c astUnformat +f AST_UNFORMAT + +* Purpose: +* Read a formatted coordinate value for a Frame axis. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frame.h" +c int astUnformat( AstFrame *this, int axis, const char *string, +c double *value ) +f RESULT = AST_UNFORMAT( THIS, AXIS, STRING, VALUE, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function reads a formatted coordinate value (given as a +c character string) for a Frame axis and returns the equivalent +c numerical (double) value. It also returns the number of +c characters read from the string. +f This function reads a formatted coordinate value (given as a +f character string) for a Frame axis and returns the equivalent +f numerical (double precision) value. It also returns the number +f of characters read from the string. +* +c The principle use of this function is in decoding user-supplied +c input which contains formatted coordinate values. Free-format +c input is supported as far as possible. If input is ambiguous, it +c is interpreted with reference to the Frame's attributes (in +c particular, the Format string associated with the Frame's +c axis). This function is, in essence, the inverse of astFormat. +f The principle use of this function is in decoding user-supplied +f input which contains formatted coordinate values. Free-format +f input is supported as far as possible. If input is ambiguous, it +f is interpreted with reference to the Frame's attributes (in +f particular, the Format string associated with the Frame's +f axis). This function is, in essence, the inverse of AST_FORMAT. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Frame. +c axis +f AXIS = INTEGER (Given) +* The number of the Frame axis for which a coordinate value is to +* be read (axis numbering starts at 1 for the first axis). +c string +f STRING = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing the +c formatted coordinate value. +f A character string containing the formatted coordinate value. +* This string may contain additional information following the +* value to be read, in which case reading stops at the first +* character which cannot be interpreted as part of the value. +* Any white space before or after the value is discarded. +c value +f VALUE = DOUBLE PRECISION (Returned) +c Pointer to a double in which the coordinate value read will be +c returned. +f The coordinate value read. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astUnformat() +f AST_UNFORMAT = INTEGER +* The number of characters read from the string in order to +* obtain the coordinate value. This will include any white +* space which occurs before or after the value. + +* Applicability: +* Frame +* This function applies to all Frames. See the "Frame Input +* Format" section below for details of the input formats +* accepted by a basic Frame. +* SkyFrame +* The SkyFrame class re-defines the input format to be suitable +* for representing angles and times, with the resulting +* coordinate value returned in radians. See the "SkyFrame +* Input Format" section below for details of the formats +* accepted. +* FrameSet +* The input formats accepted by a FrameSet are determined by +* its current Frame (as specified by the Current attribute). + +* Frame Input Format +* The input format accepted for a basic Frame axis is as follows: +* - An optional sign, followed by: +* - A sequence of one or more digits possibly containing a decimal point, +* followed by: +* - An optional exponent field. +* - The exponent field, if present, consists of "E" or "e" +* followed by a possibly signed integer. +* +* Examples of acceptable Frame input formats include: +* - 99 +* - 1.25 +* - -1.6 +* - 1E8 +* - -.99e-17 +* - + +* SkyFrame Input Format +* The input format accepted for a SkyFrame axis is as follows: +* - An optional sign, followed by between one and three fields +* representing either degrees, arc-minutes, arc-seconds or hours, +* minutes, seconds (e.g. "-12 42 03"). +* - Each field should consist of a sequence of one or more digits, +* which may include leading zeros. At most one field may contain a +* decimal point, in which case it is taken to be the final field +* (e.g. decimal degrees might be given as "124.707", while degrees +* and decimal arc-minutes might be given as "-13 33.8"). +* - The first field given may take any value, allowing angles and +* times outside the conventional ranges to be +* represented. However, subsequent fields must have values of less +* than 60 (e.g. "720 45 31" is valid, whereas "11 45 61" is not). +* - Fields may be separated by white space or by ":" (colon), but +* the choice of separator must be used consistently throughout the +* value. Additional white space may be present around fields and +* separators (e.g. "- 2: 04 : 7.1"). +* - The following field identification characters may be used as +* separators to replace either of those above (or may be appended +* to the final field), in order to identify the field to which +* they are appended: "d"---degrees; "h"---hours; "m"---minutes of +* arc or time; "s"---seconds of arc or time; "'" (single +* quote)---minutes of arc; """ (double quote)---seconds of arc. +* Either lower or upper case may be used. Fields must be given in +* order of decreasing significance (e.g. "-11D 3' 14.4"" or +* "22h14m11.2s"). +* - The presence of any of the field identification characters +* "d", "'" (single quote) or """ (double quote) indicates that the +* value is to be interpreted as an angle. Conversely, the presence +* of "h" indicates that it is to be interpreted as a time (with 24 +* hours corresponding to 360 degrees). Incompatible angle/time +* identification characters may not be mixed (e.g. "10h14'3"" is +* not valid). The remaining field identification characters and +* separators do not specify a preference for an angle or a time +* and may be used with either. +c - If no preference for an angle or a time is expressed anywhere +c within the value, it is interpreted as an angle if the Format +c attribute string associated with the SkyFrame axis generates an +c angle and as a time otherwise. This ensures that values produced +c by astFormat are correctly interpreted by astUnformat. +f - If no preference for an angle or a time is expressed anywhere +f within the value, it is interpreted as an angle if the Format +f attribute string associated with the SkyFrame axis generates an +f angle and as a time otherwise. This ensures that values produced +f by AST_FORMAT are correctly interpreted by AST_UNFORMAT. +* - Fields may be omitted, in which case they default to zero. The +* remaining fields may be identified by using appropriate field +* identification characters (see above) and/or by adding extra +* colon separators (e.g. "-05m13s" is equivalent to "-:05:13"). If +* a field is not identified explicitly, it is assumed that +* adjacent fields have been given, after taking account of any +* extra separator characters (e.g. "14:25.4s" specifies minutes +* and seconds, while "14::25.4s" specifies degrees and seconds). +c - If fields are omitted in such a way that the remaining ones +c cannot be identified uniquely (e.g. "01:02"), then the first +c field (either given explicitly or implied by an extra leading +c colon separator) is taken to be the most significant field that +c astFormat would produce when formatting a value (using the +c Format attribute associated with the SkyFrame axis). By +c default, this means that the first field will normally be +c interpreted as degrees or hours. However, if this does not +c result in consistent field identification, then the last field +c (either given explicitly or implied by an extra trailing colon +c separator) is taken to to be the least significant field that +c astFormat would produce. +f - If fields are omitted in such a way that the remaining ones +f cannot be identified uniquely (e.g. "01:02"), then the first +f field (either given explicitly or implied by an extra leading +f colon separator) is taken to be the most significant field that +f AST_FORMAT would produce when formatting a value (using the +f Format attribute associated with the SkyFrame axis). By +f default, this means that the first field will normally be +f interpreted as degrees or hours. However, if this does not +f result in consistent field identification, then the last field +f (either given explicitly or implied by an extra trailing colon +f separator) is taken to to be the least significant field that +f AST_FORMAT would produce. +* +c This final convention is intended to ensure that values formatted +c by astFormat which contain less than three fields will be +c correctly interpreted if read back using astUnformat, even if +c they do not contain field identification characters. +f This final convention is intended to ensure that values formatted +f by AST_FORMAT which contain less than three fields will be +f correctly interpreted if read back using AST_UNFORMAT, even if +f they do not contain field identification characters. +* +* Examples of acceptable SkyFrame input formats (with +* interpretation in parentheses) include: +* - -14d 13m 22.2s (-14d 13' 22.2") +* - + 12:34:56.7 (12d 34' 56.7" or 12h 34m 56.7s) +* - 001 : 02 : 03.4 (1d 02' 03.4" or 1h 02m 03.4s) +* - 22h 30 (22h 30m 00s) +* - 136::10" (136d 00' 10" or 136h 00m 10s) +* - -14M 27S (-0d 14' 27" or -0h 14m 27s) +* - -:14: (-0d 14' 00" or -0h 14m 00s) +* - -::4.1 (-0d 00' 04.1" or -0h 00m 04.1s) +* - .9" (0d 00' 00.9") +* - d12m (0d 12' 00") +* - H 12:22.3s (0h 12m 22.3s) +* - (AST__BAD) +* +* Where alternative interpretations are shown, the choice of angle or +* time depends on the associated Format(axis) attribute. + +* Notes: +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +c - Beware that it is possible for a formatting error part-way +c through an input string to terminate input before it has been +c completely read, but to yield a coordinate value that appears +c valid. For example, if a user types "1.5r6" instead of "1.5e6", +c the "r" will terminate input, giving an incorrect coordinate +c value of 1.5. It is therefore most important to check the return +c value of this function to ensure that the correct number of +c characters have been read. +f - Beware that it is possible for a formatting error part-way +f through an input string to terminate input before it has been +f completely read, but to yield a coordinate value that appears +f valid. For example, if a user types "1.5R6" instead of "1.5E6", +f the "R" will terminate input, giving an incorrect coordinate +f value of 1.5. It is therefore most important to check the return +f value of this function to ensure that the correct number of +f characters have been read. +* - An error will result if a value is read which appears to have +* the correct format, but which cannot be converted into a valid +* coordinate value (for instance, because the value of one or more +* of its fields is invalid). +* - The string "" is recognised as a special case and will +* yield the coordinate value AST__BAD without error. The test for +* this string is case-insensitive and also permits embedded white +* space. +c - A function result of zero will be returned and no coordinate +c value will be returned via the "value" pointer if this function +c is invoked with the AST error status set, or if it should fail +c for any reason. +f - A function result of zero will be returned and no coordinate +f value will be returned via the VALUE argument if this function +f is invoked with the AST error status set, or if it should fail +f for any reason. +*-- + +* Implementation Notes: +* This function implements the public interface for the +* astUnformat method. It is identical to astUnformat_ except that: +* +* - The axis index is decremented by 1 before use. This allows the +* public interface to use 1-based axis numbers (whereas internally +* axis numbers are zero-based). +*/ + +/* Invoke the normal astUnformat_ function, adjusting the axis index + to become zero-based. */ + return astUnformat( this, axis - 1, string, value ); +} + + + + + + + + + + + + + diff --git a/frame.f b/frame.f new file mode 100644 index 0000000..d88402d --- /dev/null +++ b/frame.f @@ -0,0 +1,71 @@ + CHARACTER * ( * ) FUNCTION AST_FORMAT( THIS, AXIS, VALUE ) +*+ +* Name: +* AST_FORMAT + +* Purpose: +* Wrap up the C ast_format_a function. + +* Language: +* Fortran 77 + +* Invocation: +* RESULT = AST_FORMAT( THIS, AXIS, VALUE ) + +* Description: +* This function is a wrap-up of the C ast_format_a function. It +* will rarely need to be used, but is provided for use on platforms +* where the normal mechanism for returning character strings as +* function results from C to Fortran (as defined in the f77.h +* include file) doesn't work. +* +* If this problem is encountered, the C macro NO_CHAR_FUNCTION +* should be defined (in CFLAGS) during C compilation. This will +* cause the function ast_format_a to be built (instead of +* ast_format) and this returns its result via an additional initial +* argument. This Fortran function is then used simply to transfer +* the argument value to the function result. + +* Arguments: +* As for the C version of the Fortran-callable function ast_format. + +* Returned Value: +* AST_FORMAT = CHARACTER * ( * ) +* The character return value required. + +* Notes: +* - The length of the returned result is limited to 100 characters +* by a local buffer. This length can be increased if necessary. + +* Authors: +* RFWS: R.F. Warren-Smith (STARLINK, RAL) +* {enter_new_authors_here} + +* History: +* 23-JUL-1996 (RFWS): +* Original version. +* {enter_changes_here} + +* Bugs: +* {note_any_bugs_here} + +*- + +* Type Definitions: + IMPLICIT NONE ! No implicit typing + +* Arguments Given: + INTEGER THIS + INTEGER AXIS + DOUBLE PRECISION VALUE + +* Local Variables: + CHARACTER * ( 100 ) BUFF ! Local buffer for result +*. + +* Invoke the C function (with the additional argument). + CALL AST_FORMAT_A( BUFF, THIS, AXIS, VALUE ) + +* Return the argument value as the function result. + AST_FORMAT = BUFF + END diff --git a/frame.h b/frame.h new file mode 100644 index 0000000..71efc2f --- /dev/null +++ b/frame.h @@ -0,0 +1,1431 @@ +#if !defined( FRAME_INCLUDED ) /* Include this file only once */ +#define FRAME_INCLUDED +/* +*+ +* Name: +* frame.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Frame class. + +* Invocation: +* #include "frame.h" + +* Description: +* This include file defines the interface to the Frame class and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this class. +* +* A Frame object encapsulates information about a coordinate +* system, including its axes. It may also act as a "template" to +* be matched against another Frame object. This process determines +* whether it is possible to perform a coordinate transformation +* between the two coordinates systems they describe. + +* Inheritance: +* The Frame class inherits from the Mapping class. + +* Attributes Over-Ridden: +* Nin (integer, readonly) +* The Frame class sets this value to be equal to the number of +* Frame axes. +* Nout (integer, readonly) +* The Frame class sets this value to be equal to the number of +* Frame axes. + +* New Attributes Defined: +* AlignSystem (string) +* This attribute takes a value to identify the coordinate system +* in which the Frame should be aligned with other Frames. +* Digits [or Digits(axis)] (integer) +* Specifies how many digits of precision are required by +* default when a coordinate value for a Frame is formatted +* (e.g. using the astFormat method). The Digits value acts as a +* default only and is over-ridden if a Format string is +* specified for an axis explicitly. +* +* The default Digits value for a Frame is 7. This attribute +* normally applies to all the Frame's axes, but reference may +* be made to a particular axis by adding an axis subscript +* (e.g. Digits(1)). If a value is set for an individual axis, +* it will over-ride the Digits value for the Frame as a whole +* when formatting values for that axis. +* Direction(axis) (integer) +* A boolean value which specifies how coordinate values for +* each Frame axis should be displayed (e.g. in graphs). By +* default, it has the value one, indicating that they should be +* shown in the conventional sense (i.e. increasing left to +* right for an abscissa and bottom to top for an ordinate). If +* set to zero, this attribute indicates that the direction +* should be reversed (as would often be done for an +* astronomical magnitude or a right ascension axis, for +* example). +* Epoch (double) +* This value is used to qualify coordinate systems by +* giving the moment in time when the coordinates are known to +* be correct. Often, this will be the date of observation. +* Format(axis) (string) +* Specifies the format to be used to display coordinate values +* for each Frame axis (i.e. to convert them from binary to +* character form). The interpretation of this string (e.g. by +* derived classes) is left to the astFormat method which, in +* turn will invoke the astAxisFormat for the Axis object that +* describes each axis. By default, the Frame class supplies an +* Axis class object for each axis and this will interpret this +* parameter as a C "printf" format string which should be +* capable of formatting a single coordinate value stored as a +* double (e.g. "%1.7G"). If no Format string is set, the +* default format is based on the value of the Digits attribute. +* Label(axis) (string) +* Specifies the label to be attached to each Frame axis when it +* is represented in (e.g.) a graph. It is intended purely for +* interpretation by human readers and not by software. The +* default supplied by the Frame class is the string "Axis ", +* where is 1, 2, etc. for each successive axis. +* MatchEnd (integer) +* A boolean value that controls how a Frame behaves when used +* as a template to match another Frame. If it is zero and a +* template Frame matches a target frame which has a different +* number of axes, then the axes wich occur first in the target +* frame will be matched and any trailing axes in either the +* target or template will be discarded (if necessary). If it is +* non-zero, however, the last axes in each frame will be +* matched and any un-matched leading axes will be discarded +* instead. The default value supplied by the Frame class is +* zero. +* MaxAxes (integer) +* Specifies the maximum number of axes in a target Frame that +* can be matched when using the Frame as a template. Normally, +* by default, this value is equal to the number of Frame axes, +* so that a Frame will only match another Frame with the same +* number of axes as itself. By setting a different value, +* however, Frames with different numbers of axes may be matched +* (the MatchEnd attribute then determines which of the +* individual axes are matched). +* +* When setting this value, the value of the MinAxes attribute +* may be silently changed so that it remains consistent with +* (i.e. does not exceed) the new value. The default value may +* also be reduced if necessary to remain consistent with the +* MinAxes value. +* MinAxes (integer) +* Specifies the minimum number of axes in a target Frame that +* can be matched when using the Frame as a template. Normally, +* by default, this value is equal to the number of Frame axes, +* so that a Frame will only match another Frame with the same +* number of axes as itself. By setting a different value, +* however, Frames with different numbers of axes may be matched +* (the MatchEnd attribute then determines which of the +* individual axes are matched). +* +* When setting this value, the value of the MaxAxes attribute +* may be silently changed so that it remains consistent with +* (i.e. is not less than) the new value. The default value may +* also be reduced if necessary to remain consistent with the +* MaxAxes value. +* Domain (string) +* A string which may be used to identify the physical domain to +* which a Frame applies and used as an additional key when +* matching a target Frame with a template. If the Domain +* attribute in the template Frame is set, then only target +* frames with the same Domain value will be matched. If a +* Domain is not set in the template Frame, the target Frame's +* Domain value will be ignored and has no effect on +* matching. The default value supplied by the Frame class is an +* empty string. Domain values are automatically converted to +* upper case and all white space is removed before use. +* Naxes (integer) +* A read-only attribute that gives the number of axes in a +* Frame (i.e. the number of dimensions of the space which the +* Frame describes). This value is determined when the Frame is +* created. +* Permute (integer) +* A boolean value which specifies whether the axis order of a +* target Frame may be permuted in order to obtain a match with +* a template. If this value is set to zero in the template +* Frame, it will only match a target if it can do so without +* changing the order of its axes. The default value supplied by +* the Frame class is 1 (i.e. allow axis permutations). +* PreserveAxes (integer) +* A boolean value which determines how the "result" Frame is +* produced whan a target frame is matched by a template. If +* this value is zero in the template Frame, then the result +* Frame will have the same number of axes as the template. If +* it is non-zero, however, the axes of the target Frame will be +* preserved, so that the result Frame will have the same number +* of axes as the target. The default supplied by the Frame +* class is zero (i.e. target axes are not preserved). +* +* The main use for this attribute is when the MaxAxes and/or +* MinAxes attributes have been set to search for a Frame which +* may have a different number of axes from the template. For +* example, if a 2-dimensional template Frame matches a +* 3-dimensional target Frame, then by default the result is +* 2-dimensional and the last axis (normally) will be +* discarded. However, if the template's PreserveAxes value is +* non-zero, the result will instead be 3-dimensional to +* correspond with the target Frame. +* Symbol(axis) (string) +* Specifies the symbol to be used to represent coordinate +* values for each Frame axis in "short form", such as in +* algebraic expressions where a full description of the axis +* would be inappropriate. Examples include "RA" and "Dec" (for +* Right Ascension and Declination). +* +* The default supplied by the Frame class is the string +* "", where is 1, 2, etc. for successive axes, +* and is the value of the Frame's Domain attribute +* (with any white space replaced by underscores and truncated +* if necessary so that the final string does not exceed 15 +* characters). If no Domain value has been set, "x" is used as +* the value in constructing this default string. +* System (string) +* This attribute takes a value to identify the coordinate system +* used to describe positions within the domain of the Frame. +* Title (string) +* Specifies a string to be used as a title on (e.g.) graphs to +* describe the coordinate system which the Frame +* represents. Examples would be "Detector Coordinates" or +* "Galactic Coordinates". This string is intended solely for +* interpretation by human readers and not by software. The +* default supplied by the Frame class is "-D Coordinate +* System", where is the number of Frame axes. +* Unit(axis) (string) +* Describes the units used to represent coordinate values on +* each Frame axis. The default supplied by the Frame class is +* an empty string. + +* Methods Over-Ridden: +* Public: +* astGetNin +* Get the number of input coordinates for a Frame. +* astGetNout +* Get the number of output coordinates for a Frame. +* astTransform +* Use a Frame to transform a set of points. + +* Protected: +* astClearAttrib +* Clear an attribute value for a Frame. +* astGetAttrib +* Get an attribute value for a Frame. +* astReportPoints +* Report the effect of transforming a set of points using a Frame. +* astSetAttrib +* Set an attribute value for a Frame. +* astTestAttrib +* Test if an attribute value has been set for a Frame. + +* New Methods Defined: +* Public: +* astAngle +* Calculate the angle between three points. +* astAxAngle +* Find the angle from an axis to a line through two points. +* astAxDistance +* Calculate the distance between two axis values +* astAxOffset +* Calculate an offset along an axis +* astConvert +* Determine how to convert between two coordinate systems. +* astDistance +* Calculate the distance between two points. +* astFindFrame +* Find a coordinate system with specified characteristics +* astFormat +* Format a coordinate value for a Frame axis. +* astNorm +* Normalise a set of Frame coordinates. +* astOffset +* Calculate an offset along a geodesic curve. +* astOffset2 +* Calculate an offset along a geodesic curve for a 2D Frame. +* astPermAxes +* Permute the order of a Frame's axes. +* astPickAxes +* Create a new Frame by picking axes from an existing one. +* astResolve +* Resolve a vector into two orthogonal components. +* astUnformat +* Read a formatted coordinate value for a Frame axis. + +* Protected: +* astAbbrev +* Abbreviate a formatted Frame axis value by skipping +* leading fields. +* astCheckPerm +* Check that an array contains a valid permutation. +* astClearDigits +* Clear the Digits attribute for a Frame. +* astClearDirection +* Clear the Direction attribute for a Frame axis. +* astClearDomain +* Clear the Domain attribute for a Frame. +* astClearFormat +* Clear the Format attribute for a Frame axis. +* astClearLabel +* Clear the Label attribute for a Frame axis. +* astClearMatchEnd +* Clear the MatchEnd attribute for a Frame. +* astClearMaxAxes +* Clear the MaxAxes attribute for a Frame. +* astClearMinAxes +* Clear the MinAxes attribute for a Frame. +* astClearPermute +* Clear the Permute attribute for a Frame. +* astClearPreserveAxes +* Clear the PreserveAxes attribute for a Frame. +* astClearSymbol +* Clear the Symbol attribute for a Frame axis. +* astClearSystem +* Clear the value of the System attribute for a Frame. +* astClearTitle +* Clear the Title attribute for a Frame. +* astClearUnit +* Clear the Unit attribute for a Frame axis. +* astConvertX +* Determine how to convert between two coordinate systems. +* astFields +* Identify the fields within a formatted Frame axis value. +* astCentre +* Find a "nice" central value for tabulating Frame axis values. +* astGap +* Find a "nice" gap for tabulating Frame axis values. +* astGetAxis +* Obtain a pointer to a specified Axis from a Frame. +* astGetDigits +* Get the value of the Digits attribute for a Frame. +* astGetDirection +* Get the value of the Direction attribute for a Frame axis. +* astGetDomain +* Get a pointer to the Domain attribute for a Frame. +* astGetFormat +* Get a pointer to the Format attribute for a Frame axis. +* astGetLabel +* Get a pointer to the Label attribute for a Frame axis. +* astGetMatchEnd +* Get the value of the MatchEnd attribute for a Frame. +* astGetMaxAxes +* Get the value of the MaxAxes attribute for a Frame. +* astGetMinAxes +* Get the value of the MinAxes attribute for a Frame. +* astGetNaxes +* Determine how many axes a Frame has. +* astGetPerm +* Access the axis permutation array for a Frame. +* astGetPermute +* Get the value of the Permute attribute for a Frame. +* astGetPreserveAxes +* Get the value of the PreserveAxes attribute for a Frame. +* astGetSymbol +* Get a pointer to the Symbol attribute for a Frame axis. +* astGetSystem +* Get the value of the System attribute for a Frame. +* astGetTitle +* Get a pointer to the Title attribute for a Frame. +* astGetUnit +* Get a pointer to the Unit attribute for a Frame axis. +* astIsUnitFrame +* Returns a flag indicating if a Frame is equivalent to a UnitMap. +* astMatch +* Determine if conversion is possible between two coordinate systems. +* astOverlay +* Overlay the attributes of a template Frame on to another Frame. +* astPrimaryFrame +* Uniquely identify a primary Frame and one of its axes. +* astResolvePoints +* Resolve many vectors into two orthogonal components. +* astSetAxis +* Set a new Axis for a Frame. +* astSetDigits +* Set the value of the Digits attribute for a Frame. +* astSetDirection +* Set the value of the Direction attribute for a Frame axis. +* astSetDomain +* Set the value of the Domain attribute for a Frame. +* astSetFormat +* Set the value of the Format attribute for a Frame axis. +* astSetLabel +* Set the value of the Label attribute for a Frame axis. +* astSetMatchEnd +* Set the value of the MatchEnd attribute for a Frame. +* astSetMaxAxes +* Set the value of the MaxAxes attribute for a Frame. +* astSetMinAxes +* Set the value of the MinAxes attribute for a Frame. +* astSetPermute +* Set the value of the Permute attribute for a Frame. +* astSetPreserveAxes +* Set the value of the PreserveAxes attribute for a Frame. +* astSetSymbol +* Set the value of the Symbol attribute for a Frame axis. +* astSetSystem +* Set the value of the System attribute for a Frame. +* astSetTitle +* Set the value of the Title attribute for a Frame. +* astSetUnit +* Set the value of the Unit attribute for a Frame axis. +* astSubFrame +* Select axes from a Frame and convert to the new coordinate system. +* astTestDigits +* Test whether a value has been set for the Digits attribute of a +* Frame. +* astTestDirection +* Test whether a value has been set for the Direction attribute of a +* Frame axis. +* astTestDomain +* Test whether a value has been set for the Domain attribute of a +* Frame. +* astTestFormat +* Test whether a value has been set for the Format attribute of a +* Frame axis. +* astTestLabel +* Test whether a value has been set for the Label attribute of a +* Frame axis. +* astTestMatchEnd +* Test whether a value has been set for the MatchEnd attribute of a +* Frame. +* astTestMaxAxes +* Test whether a value has been set for the MaxAxes attribute of a +* Frame. +* astTestMinAxes +* Test whether a value has been set for the MinAxes attribute of a +* Frame. +* astTestPermute +* Test whether a value has been set for the Permute attribute of a +* Frame. +* astTestPreserveAxes +* Test whether a value has been set for the PreserveAxes attribute of +* a Frame. +* astTestSymbol +* Test whether a value has been set for the Symbol attribute of a +* Frame axis. +* astTestSystem +* Test whether a value has been set for the System attribute of a +* Frame. +* astTestTitle +* Test whether a value has been set for the Title attribute of a +* Frame. +* astTestUnit +* Test whether a value has been set for the Unit attribute of a Frame +* axis. +* astValidateAxis +* Validate and permute a Frame's axis index. +* astValidateAxisSelection +* Check that a set of axes selected from a Frame is valid. +* astValidateSystem +* Validate a Frame's System attribute. +* astSystemString +* Return a string representation of a System code. +* astSystemCode +* Return a code for a string representation of a System value + +* Other Class Functions: +* Public: +* astFrame +* Create a Frame. +* astIsAFrame +* Test class membership. + +* Protected: +* astCheckFrame +* Validate class membership. +* astInitFrame +* Initialise a Frame. +* astInitFrameVtab +* Initialise the virtual function table for the Frame class. +* astLoadFrame +* Load a Frame. + +* Macros: +* Public: +* None. + +* Protected: +* AST__BADSYSTEM +* A "bad" (undefined) value for the System attribute. + +* Type Definitions: +* Public: +* AstFrame +* Frame object type. + +* Protected: +* AstFrameVtab +* Frame virtual function table type. +* AstSystemType +* Enumerated type used for the System attribute. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: B.S. Berry (Starlink) + +* History: +* 1-MAR-1996 (RFWS): +* Original version. +* 25-APR-1996 (RFWS): +* Tidied up, etc. +* 11-SEP-1996 (RFWS): +* Added astGap (written by DSB). +* 10-JUN-1997 (RFWS): +* Revised astConvert and added astFindFrame. +* 15-FEB-1998 (RFWS): +* Added astUnformat. +* 21-JUN-2001 (DSB): +* Added astAngle and astOffset2. +* 29-AUG-2001 (DSB): +* Added astAxDistance and astAxOffset. +* 4-SEP-2001 (DSB): +* Added astResolve. +* 9-SEP-2001 (DSB): +* Added astBear. +* 21-SEP-2001 (DSB): +* Replace astBear with astAxAngle. +* 15-NOV-2002 (DSB): +* Moved System and Epoch attributes from SkyFrame into this class. +* Added AlignSystem attribute. +* 8-JAN-2003 (DSB): +* Added protected astInitFrameVtab method. +* 24-JAN-2004 (DSB): +* o Added astFields. +* o Added argument "fmt" to astAbbrev. +* 24-JUN-2004 (DSB): +* Remove unused entry "void (* SetMatchRange)( AstFrame *, int, int );" +* from AstFrameVtab structure. +* 9-NOV-2004 (DSB): +* Added protected astIsAUnitFrame method. +* 12-AUG-2005 (DSB): +* Added ObsLat and ObsLon attributes. +* 14-OCT-2006 (DSB): +* Added dut1 to the Frame structure. +* Added Dut1 accessor methods. +* 17-MAY-2007 (DSB): +* Added NormUnit attribute. +* 14-JAN-2009 (DSB): +* Added astIntersect method. +* 18-JUN-2009 (DSB): +* Added ObsAlt attribute. +* 17-APR-2015 (DSB): +* Added astCentre. +* 27-APR-2015 (DSB): +* Added InternalUnit attribute. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "axis.h" /* Coordinate Axis class */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#include + +/* Macros. */ +/* ------- */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) /* Protected */ + +/* A bad value for the System attribute. */ +#define AST__BADSYSTEM -1 + +/* The legal System values recognized by this class of Frame. */ +#define AST__CART 0 + +/* Flag bitmasks for use with astSetFrameFlags. */ +# define AST__INTFLAG 1 /* FrameSet integrity is currently being restored */ + +/* Define constants used to size global arrays in this module. */ +#define AST__FRAME_LABEL_BUFF_LEN 100 /* Max length of default axis Label string */ +#define AST__FRAME_SYMBOL_BUFF_LEN 50 /* Max length of default axis Symbol string */ +#define AST__FRAME_TITLE_BUFF_LEN 100 /* Max length of default title string */ +#define AST__FRAME_GETATTRIB_BUFF_LEN 50 /* Max length of string returned by GetAttrib */ +#define AST__FRAME_ASTFMTDECIMALYR_BUFF_LEN 50 /* Max length of string returned by GetAttrib */ +#define AST__FRAME_ASTFORMATID_MAX_STRINGS 50 /* Number of string values buffer by astFormatID*/ + +#endif + +/* Type Definitions. */ +/* ================= */ +/* Integer type used to store the System attribute values. */ +typedef int AstSystemType; + +/* Frame structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstFrame { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstAxis **axis; /* Pointer to array of Axis objects */ + char *domain; /* Pointer to Domain string */ + char *title; /* Pointer to Title string */ + double epoch; /* Epoch as Modified Julian Date */ + double obslat; /* Geodetic latitude of observer */ + double obslon; /* Geodetic longitude of observer */ + double obsalt; /* Height above reference spheroid (geodetic, metres) */ + double dut1; /* UT1-UTC in seconds */ + int *perm; /* Pointer to axis permutation array */ + int digits; /* Default digits of precision */ + int match_end; /* Match final axes of target? */ + int active_unit; /* Use Unit when aligning Frames? */ + int max_axes; /* Minimum no. axes matched */ + int min_axes; /* Max. no. axes matched */ + int naxes; /* Number of axes */ + int permute; /* Permute axes in order to match? */ + int preserve_axes; /* Preserve target axes? */ + AstSystemType system; /* Code identifying coordinate system */ + AstSystemType alignsystem; /* Code for Alignment coordinate system */ + int flags; /* Bit mask containing various protected flags */ + struct AstFrameSet *variants; /* FrameSet defining alternative properties for the Frame */ +} AstFrame; + +/* Cached Line structure. */ +/* ---------------------- */ +/* This structure contains information describing a line segment within a + 2D Frame. It is used by other classes to store intermediate cached values + relating to the line in order to speed up repeated operations on the + line. */ + +typedef struct AstLineDef { + AstFrame *frame; /* Pointer to Frame in which the line is defined */ + double length; /* Line length */ + int infinite; /* Disregard the start and end of the line? */ + double start[2]; /* Frame axis values at line start */ + double end[2]; /* Frame axis values at line end */ + double dir[2]; /* Unit vector defining line direction */ + double q[2]; /* Unit vector perpendicular to line */ +} AstLineDef; + +/* Virtual function table. */ +/* ----------------------- */ +/* The virtual function table makes a forward reference to the + AstFrameSet structure which is not defined until "frameset.h" is + included (below). Hence make a preliminary definition available + now. */ +struct AstFrameSet; + +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstAxis *(* GetAxis)( AstFrame *, int, int * ); + AstFrame *(* PickAxes)( AstFrame *, int, const int[], AstMapping **, int * ); + AstLineDef *(* LineDef)( AstFrame *, const double[2], const double[2], int * ); + AstPointSet *(* ResolvePoints)( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); + const char *(* Abbrev)( AstFrame *, int, const char *, const char *, const char *, int * ); + const char *(* Format)( AstFrame *, int, double, int * ); + const char *(* GetDomain)( AstFrame *, int * ); + const char *(* GetFormat)( AstFrame *, int, int * ); + const char *(* GetLabel)( AstFrame *, int, int * ); + const char *(* GetSymbol)( AstFrame *, int, int * ); + const char *(* GetTitle)( AstFrame *, int * ); + const char *(* GetInternalUnit)( AstFrame *, int, int * ); + const char *(* GetNormUnit)( AstFrame *, int, int * ); + const char *(* GetUnit)( AstFrame *, int, int * ); + const int *(* GetPerm)( AstFrame *, int * ); + double (* Angle)( AstFrame *, const double[], const double[], const double[], int * ); + double (* Distance)( AstFrame *, const double[], const double[], int * ); + double (* Centre)( AstFrame *, int, double, double, int * ); + double (* Gap)( AstFrame *, int, double, int *, int * ); + int (* Fields)( AstFrame *, int, const char *, const char *, int, char **, int *, double *, int * ); + double (* AxDistance)( AstFrame *, int, double, double, int * ); + double (* AxOffset)( AstFrame *, int, double, double, int * ); + int (* AxIn)( AstFrame *, int, double, double, double, int, int * ); + int (* GetDigits)( AstFrame *, int * ); + int (* GetDirection)( AstFrame *, int, int * ); + int (* GetMatchEnd)( AstFrame *, int * ); + int (* GetMaxAxes)( AstFrame *, int * ); + int (* GetMinAxes)( AstFrame *, int * ); + int (* GetNaxes)( AstFrame *, int * ); + int (* GetPermute)( AstFrame *, int * ); + int (* GetPreserveAxes)( AstFrame *, int * ); + int (* IsUnitFrame)( AstFrame *, int * ); + int (* LineCrossing)( AstFrame *, AstLineDef *, AstLineDef *, double **, int * ); + int (* LineContains)( AstFrame *, AstLineDef *, int, double *, int * ); + int (* Match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); + int (* SubFrame)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); + int (* TestDigits)( AstFrame *, int * ); + int (* TestDirection)( AstFrame *, int, int * ); + int (* TestDomain)( AstFrame *, int * ); + int (* TestFormat)( AstFrame *, int, int * ); + int (* TestLabel)( AstFrame *, int, int * ); + int (* TestMatchEnd)( AstFrame *, int * ); + int (* TestMaxAxes)( AstFrame *, int * ); + int (* TestMinAxes)( AstFrame *, int * ); + int (* TestPermute)( AstFrame *, int * ); + int (* TestPreserveAxes)( AstFrame *, int * ); + int (* TestSymbol)( AstFrame *, int, int * ); + int (* TestTitle)( AstFrame *, int * ); + int (* TestUnit)( AstFrame *, int, int * ); + int (* Unformat)( AstFrame *, int, const char *, double *, int * ); + int (* ValidateAxis)( AstFrame *, int, int, const char *, int * ); + AstSystemType (* ValidateSystem)( AstFrame *, AstSystemType, const char *, int * ); + AstSystemType (* SystemCode)( AstFrame *, const char *, int * ); + const char *(* SystemString)( AstFrame *, AstSystemType, int * ); + struct AstFrameSet *(* Convert)( AstFrame *, AstFrame *, const char *, int * ); + struct AstFrameSet *(* ConvertX)( AstFrame *, AstFrame *, const char *, int * ); + struct AstFrameSet *(* FindFrame)( AstFrame *, AstFrame *, const char *, int * ); + void (* MatchAxes)( AstFrame *, AstFrame *, int[], int * ); + void (* MatchAxesX)( AstFrame *, AstFrame *, int[], int * ); + void (* CheckPerm)( AstFrame *, const int *, const char *, int * ); + void (* ClearDigits)( AstFrame *, int * ); + void (* ClearDirection)( AstFrame *, int, int * ); + void (* ClearDomain)( AstFrame *, int * ); + void (* ClearFormat)( AstFrame *, int, int * ); + void (* ClearLabel)( AstFrame *, int, int * ); + void (* ClearMatchEnd)( AstFrame *, int * ); + void (* ClearMaxAxes)( AstFrame *, int * ); + void (* ClearMinAxes)( AstFrame *, int * ); + void (* ClearPermute)( AstFrame *, int * ); + void (* ClearPreserveAxes)( AstFrame *, int * ); + void (* ClearSymbol)( AstFrame *, int, int * ); + void (* ClearTitle)( AstFrame *, int * ); + void (* ClearUnit)( AstFrame *, int, int * ); + void (* Intersect)( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * ); + void (* Norm)( AstFrame *, double[], int * ); + void (* NormBox)( AstFrame *, double *, double *, AstMapping *, int * ); + void (* Offset)( AstFrame *, const double[], const double[], double, double[], int * ); + double (* AxAngle)( AstFrame *, const double[2], const double[2], int, int * ); + double (* Offset2)( AstFrame *, const double[2], double, double, double[2], int * ); + void (* Overlay)( AstFrame *, const int *, AstFrame *, int * ); + void (* PermAxes)( AstFrame *, const int[], int * ); + void (* PrimaryFrame)( AstFrame *, int, AstFrame **, int *, int * ); + void (* Resolve)( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); + void (* SetAxis)( AstFrame *, int, AstAxis *, int * ); + void (* SetDigits)( AstFrame *, int, int * ); + void (* SetDirection)( AstFrame *, int, int, int * ); + void (* SetDomain)( AstFrame *, const char *, int * ); + void (* SetFormat)( AstFrame *, int, const char *, int * ); + void (* SetLabel)( AstFrame *, int, const char *, int * ); + void (* SetMatchEnd)( AstFrame *, int, int * ); + void (* SetMaxAxes)( AstFrame *, int, int * ); + void (* SetMinAxes)( AstFrame *, int, int * ); + void (* SetPermute)( AstFrame *, int, int * ); + void (* SetPreserveAxes)( AstFrame *, int, int * ); + void (* SetSymbol)( AstFrame *, int, const char *, int * ); + void (* SetTitle)( AstFrame *, const char *, int * ); + void (* SetUnit)( AstFrame *, int, const char *, int * ); + void (* ValidateAxisSelection)( AstFrame *, int, const int *, const char *, int * ); + void (* LineOffset)( AstFrame *, AstLineDef *, double, double, double[2], int * ); + AstPointSet *(* FrameGrid)( AstFrame *, int, const double *, const double *, int * ); + struct AstFrameSet *(* GetFrameVariants)( AstFrame *, int * ); + void (* SetFrameVariants)( AstFrame *, struct AstFrameSet *, int * ); + + double (* GetTop)( AstFrame *, int, int * ); + int (* TestTop)( AstFrame *, int, int * ); + void (* ClearTop)( AstFrame *, int, int * ); + void (* SetTop)( AstFrame *, int, double, int * ); + + double (* GetBottom)( AstFrame *, int, int * ); + int (* TestBottom)( AstFrame *, int, int * ); + void (* ClearBottom)( AstFrame *, int, int * ); + void (* SetBottom)( AstFrame *, int, double, int * ); + + AstSystemType (* GetSystem)( AstFrame *, int * ); + int (* TestSystem)( AstFrame *, int * ); + void (* ClearSystem)( AstFrame *, int * ); + void (* SetSystem)( AstFrame *, AstSystemType, int * ); + + AstSystemType (* GetAlignSystem)( AstFrame *, int * ); + int (* TestAlignSystem)( AstFrame *, int * ); + void (* ClearAlignSystem)( AstFrame *, int * ); + void (* SetAlignSystem)( AstFrame *, AstSystemType, int * ); + + double (* GetEpoch)( AstFrame *, int * ); + int (* TestEpoch)( AstFrame *, int * ); + void (* ClearEpoch)( AstFrame *, int * ); + void (* SetEpoch)( AstFrame *, double, int * ); + + int (* TestActiveUnit)( AstFrame *, int * ); + int (* GetActiveUnit)( AstFrame *, int * ); + void (* SetActiveUnit)( AstFrame *, int, int * ); + + double (* GetObsLon)( AstFrame *, int * ); + int (* TestObsLon)( AstFrame *, int * ); + void (* ClearObsLon)( AstFrame *, int * ); + void (* SetObsLon)( AstFrame *, double, int * ); + + double (* GetObsLat)( AstFrame *, int * ); + int (* TestObsLat)( AstFrame *, int * ); + void (* ClearObsLat)( AstFrame *, int * ); + void (* SetObsLat)( AstFrame *, double, int * ); + + double (* GetObsAlt)( AstFrame *, int * ); + int (* TestObsAlt)( AstFrame *, int * ); + void (* ClearObsAlt)( AstFrame *, int * ); + void (* SetObsAlt)( AstFrame *, double, int * ); + + double (* GetDut1)( AstFrame *, int * ); + int (* TestDut1)( AstFrame *, int * ); + void (* ClearDut1)( AstFrame *, int * ); + void (* SetDut1)( AstFrame *, double, int * ); + + void (* SetFrameFlags)( AstFrame *, int, int * ); + int (* GetFrameFlags)( AstFrame *, int * ); + +} AstFrameVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstFrameGlobals { + AstFrameVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__FRAME_GETATTRIB_BUFF_LEN + 1 ]; + char *AstFormatID_Strings[ AST__FRAME_ASTFORMATID_MAX_STRINGS ]; + int AstFormatID_Istr; + int AstFormatID_Init; + char Label_Buff[ AST__FRAME_LABEL_BUFF_LEN + 1 ]; + char Symbol_Buff[ AST__FRAME_SYMBOL_BUFF_LEN + 1 ]; + char Title_Buff[ AST__FRAME_TITLE_BUFF_LEN + 1 ]; + char AstFmtDecimalYr_Buff[ AST__FRAME_ASTFMTDECIMALYR_BUFF_LEN + 1 ]; +} AstFrameGlobals; + +#endif +#endif + +/* More include files. */ +/* =================== */ +/* The interface to the FrameSet class must be included here (after + the type definitions for the Frame class) because "frameset.h" + itself includes this file ("frame.h"), although "frameset.h" refers + to the AstFrameSet structure above. This seems a little strange at + first, but is simply analogous to making a forward reference to a + structure type when recursively defining a normal C structure + (except that here the definitions happen to be in separate include + files). */ +#include "frameset.h" + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Frame) /* Check class membership */ +astPROTO_ISA(Frame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstFrame *astFrame_( int, const char *, int *, ...); +#else +AstFrame *astFrameId_( int, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstFrame *astInitFrame_( void *, size_t, int, AstFrameVtab *, const char *, + int, int * ); + +/* Vtab initialiser. */ +void astInitFrameVtab_( AstFrameVtab *, const char *, int * ); + +/* Loader. */ +AstFrame *astLoadFrame_( void *, size_t, AstFrameVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitFrameGlobals_( AstFrameGlobals * ); +#endif +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstFrameSet *astConvert_( AstFrame *, AstFrame *, const char *, int * ); +AstFrameSet *astFindFrame_( AstFrame *, AstFrame *, const char *, int * ); +double astAngle_( AstFrame *, const double[], const double[], const double[], int * ); +double astAxAngle_( AstFrame *, const double[2], const double[2], int, int * ); +double astAxDistance_( AstFrame *, int, double, double, int * ); +double astAxOffset_( AstFrame *, int, double, double, int * ); +double astDistance_( AstFrame *, const double[], const double[], int * ); +double astOffset2_( AstFrame *, const double[2], double, double, double[2], int * ); +int astGetActiveUnit_( AstFrame *, int * ); +void astIntersect_( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * ); +void astMatchAxes_( AstFrame *, AstFrame *, int[], int * ); +void astNorm_( AstFrame *, double[], int * ); +void astOffset_( AstFrame *, const double[], const double[], double, double[], int * ); +void astResolve_( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); +void astSetActiveUnit_( AstFrame *, int, int * ); +AstFrameSet *astGetFrameVariants_( AstFrame *, int * ); +void astSetFrameVariants_( AstFrame *, AstFrameSet *, int * ); + +#if defined(astCLASS) /* Protected */ +void astNormBox_( AstFrame *, double *, double *, AstMapping *, int * ); +AstFrame *astPickAxes_( AstFrame *, int, const int[], AstMapping **, int * ); +const char *astFormat_( AstFrame *, int, double, int * ); +int astUnformat_( AstFrame *, int, const char *, double *, int * ); +void astPermAxes_( AstFrame *, const int[], int * ); +#else +AstFrame *astPickAxesId_( AstFrame *, int, const int[], AstMapping **, int * ); +const char *astFormatId_( AstFrame *, int, double, int * ); +int astUnformatId_( AstFrame *, int, const char *, double *, int * ); +void astPermAxesId_( AstFrame *, const int[], int * ); +#endif + +#if defined(astCLASS) /* Protected */ +int astAxIn_( AstFrame *, int, double, double, double, int, int * ); +AstAxis * astGetAxis_( AstFrame *, int, int * ); +AstFrameSet *astConvertX_( AstFrame *, AstFrame *, const char *, int * ); +void astMatchAxesX_( AstFrame *, AstFrame *, int[], int * ); +AstLineDef *astLineDef_( AstFrame *, const double[2], const double[2], int * ); +AstPointSet *astResolvePoints_( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); +const char *astAbbrev_( AstFrame *, int, const char *, const char *, const char *, int * ); +const char *astGetDomain_( AstFrame *, int * ); +const char *astGetFormat_( AstFrame *, int, int * ); +const char *astGetLabel_( AstFrame *, int, int * ); +const char *astGetSymbol_( AstFrame *, int, int * ); +const char *astGetTitle_( AstFrame *, int * ); +const char *astGetUnit_( AstFrame *, int, int * ); +const char *astGetInternalUnit_( AstFrame *, int, int * ); +const char *astGetNormUnit_( AstFrame *, int, int * ); +const int *astGetPerm_( AstFrame *, int * ); +double astCentre_( AstFrame *, int, double, double, int * ); +double astGap_( AstFrame *, int, double, int *, int * ); +int astFields_( AstFrame *, int, const char *, const char *, int, char **, int *, double *, int * ); +int astGetDigits_( AstFrame *, int * ); +int astGetDirection_( AstFrame *, int, int * ); +int astGetMatchEnd_( AstFrame *, int * ); +int astGetMaxAxes_( AstFrame *, int * ); +int astGetMinAxes_( AstFrame *, int * ); +int astGetNaxes_( AstFrame *, int * ); +int astGetPermute_( AstFrame *, int * ); +int astGetPreserveAxes_( AstFrame *, int * ); +int astIsUnitFrame_( AstFrame *, int * ); +int astLineCrossing_( AstFrame *, AstLineDef *, AstLineDef *, double **, int * ); +int astLineContains_( AstFrame *, AstLineDef *, int, double *, int * ); +int astMatch_( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +int astSubFrame_( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +int astTestDigits_( AstFrame *, int * ); +int astTestDirection_( AstFrame *, int, int * ); +int astTestDomain_( AstFrame *, int * ); +int astTestFormat_( AstFrame *, int, int * ); +int astTestLabel_( AstFrame *, int, int * ); +int astTestMatchEnd_( AstFrame *, int * ); +int astTestMaxAxes_( AstFrame *, int * ); +int astTestMinAxes_( AstFrame *, int * ); +int astTestPermute_( AstFrame *, int * ); +int astTestPreserveAxes_( AstFrame *, int * ); +int astTestSymbol_( AstFrame *, int, int * ); +int astTestTitle_( AstFrame *, int * ); +int astTestUnit_( AstFrame *, int, int * ); +int astValidateAxis_( AstFrame *, int, int, const char *, int * ); +AstSystemType astValidateSystem_( AstFrame *, AstSystemType, const char *, int * ); +AstSystemType astSystemCode_( AstFrame *, const char *, int * ); +const char *astSystemString_( AstFrame *, AstSystemType, int * ); +void astCheckPerm_( AstFrame *, const int *, const char *, int * ); +void astClearDigits_( AstFrame *, int * ); +void astClearDirection_( AstFrame *, int, int * ); +void astClearDomain_( AstFrame *, int * ); +void astClearFormat_( AstFrame *, int, int * ); +void astClearLabel_( AstFrame *, int, int * ); +void astClearMatchEnd_( AstFrame *, int * ); +void astClearMaxAxes_( AstFrame *, int * ); +void astClearMinAxes_( AstFrame *, int * ); +void astClearPermute_( AstFrame *, int * ); +void astClearPreserveAxes_( AstFrame *, int * ); +void astClearSymbol_( AstFrame *, int, int * ); +void astClearTitle_( AstFrame *, int * ); +void astClearUnit_( AstFrame *, int, int * ); +void astOverlay_( AstFrame *, const int *, AstFrame *, int * ); +void astPrimaryFrame_( AstFrame *, int, AstFrame **, int *, int * ); +void astSetAxis_( AstFrame *, int, AstAxis *, int * ); +void astSetDigits_( AstFrame *, int, int * ); +void astSetDirection_( AstFrame *, int, int, int * ); +void astSetDomain_( AstFrame *, const char *, int * ); +void astSetFormat_( AstFrame *, int, const char *, int * ); +void astSetLabel_( AstFrame *, int, const char *, int * ); +void astSetMatchEnd_( AstFrame *, int, int * ); +void astSetMaxAxes_( AstFrame *, int, int * ); +void astSetMinAxes_( AstFrame *, int, int * ); +void astSetPermute_( AstFrame *, int, int * ); +void astSetPreserveAxes_( AstFrame *, int, int * ); +void astSetSymbol_( AstFrame *, int, const char *, int * ); +void astSetTitle_( AstFrame *, const char *, int * ); +void astSetUnit_( AstFrame *, int, const char *, int * ); +void astValidateAxisSelection_( AstFrame *, int, const int *, const char *, int * ); +double astReadDateTime_( const char *, int * ); +const char *astFmtDecimalYr_( double, int, int * ); +void astLineOffset_( AstFrame *, AstLineDef *, double, double, double[2], int * ); +AstPointSet *astFrameGrid_( AstFrame *, int, const double *, const double *, int * ); + +double astGetTop_( AstFrame *, int, int * ); +int astTestTop_( AstFrame *, int, int * ); +void astClearTop_( AstFrame *, int, int * ); +void astSetTop_( AstFrame *, int, double, int * ); + +double astGetBottom_( AstFrame *, int, int * ); +int astTestBottom_( AstFrame *, int, int * ); +void astClearBottom_( AstFrame *, int, int * ); +void astSetBottom_( AstFrame *, int, double, int * ); + +AstSystemType astGetSystem_( AstFrame *, int * ); +int astTestSystem_( AstFrame *, int * ); +void astClearSystem_( AstFrame *, int * ); +void astSetSystem_( AstFrame *, AstSystemType, int * ); + +AstSystemType astGetAlignSystem_( AstFrame *, int * ); +int astTestAlignSystem_( AstFrame *, int * ); +void astClearAlignSystem_( AstFrame *, int * ); +void astSetAlignSystem_( AstFrame *, AstSystemType, int * ); + +double astGetEpoch_( AstFrame *, int * ); +int astTestEpoch_( AstFrame *, int * ); +void astClearEpoch_( AstFrame *, int * ); +void astSetEpoch_( AstFrame *, double, int * ); + +double astGetObsLon_( AstFrame *, int * ); +int astTestObsLon_( AstFrame *, int * ); +void astClearObsLon_( AstFrame *, int * ); +void astSetObsLon_( AstFrame *, double, int * ); + +double astGetObsLat_( AstFrame *, int * ); +int astTestObsLat_( AstFrame *, int * ); +void astClearObsLat_( AstFrame *, int * ); +void astSetObsLat_( AstFrame *, double, int * ); + +double astGetObsAlt_( AstFrame *, int * ); +int astTestObsAlt_( AstFrame *, int * ); +void astClearObsAlt_( AstFrame *, int * ); +void astSetObsAlt_( AstFrame *, double, int * ); + +double astGetDut1_( AstFrame *, int * ); +int astTestDut1_( AstFrame *, int * ); +void astClearDut1_( AstFrame *, int * ); +void astSetDut1_( AstFrame *, double, int * ); + +int astTestActiveUnit_( AstFrame *, int * ); + +void astSetFrameFlags_( AstFrame *, int, int * ); +int astGetFrameFlags_( AstFrame *, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class to make + them easier to invoke (e.g. to avoid type mis-matches when passing pointers + to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them to + validate their own arguments. We must use a cast when passing object + pointers (so that they can accept objects from derived classes). */ + +/* Check class membership. */ +#define astCheckFrame(this) astINVOKE_CHECK(Frame,this,0) +#define astVerifyFrame(this) astINVOKE_CHECK(Frame,this,1) + +/* Test class membership. */ +#define astIsAFrame(this) astINVOKE_ISA(Frame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astFrame astINVOKE(F,astFrame_) +#else +#define astFrame astINVOKE(F,astFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitFrame(mem,size,init,vtab,name,naxes) \ +astINVOKE(O,astInitFrame_(mem,size,init,vtab,name,naxes,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitFrameVtab(vtab,name) astINVOKE(V,astInitFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckFrame to validate Frame pointers before + use. This provides a contextual error report if a pointer to the + wrong sort of Object is supplied. */ +#define astConvert(from,to,domainlist) \ +astINVOKE(O,astConvert_(astCheckFrame(from),astCheckFrame(to),domainlist,STATUS_PTR)) +#define astAngle(this,a,b,c) \ +astINVOKE(V,astAngle_(astCheckFrame(this),a,b,c,STATUS_PTR)) +#define astDistance(this,point1,point2) \ +astINVOKE(V,astDistance_(astCheckFrame(this),point1,point2,STATUS_PTR)) +#define astFindFrame(target,template,domainlist) \ +astINVOKE(O,astFindFrame_(astCheckFrame(target),astCheckFrame(template),domainlist,STATUS_PTR)) +#define astMatchAxes(frm1,frm2,axes) \ +astINVOKE(V,astMatchAxes_(astCheckFrame(frm1),astCheckFrame(frm2),axes,STATUS_PTR)) +#define astNorm(this,value) \ +astINVOKE(V,astNorm_(astCheckFrame(this),value,STATUS_PTR)) +#define astAxDistance(this,axis,v1,v2) \ +astINVOKE(V,astAxDistance_(astCheckFrame(this),axis,v1,v2,STATUS_PTR)) +#define astAxOffset(this,axis,v1,dist) \ +astINVOKE(V,astAxOffset_(astCheckFrame(this),axis,v1,dist,STATUS_PTR)) +#define astOffset(this,point1,point2,offset,point3) \ +astINVOKE(V,astOffset_(astCheckFrame(this),point1,point2,offset,point3,STATUS_PTR)) +#define astAxAngle(this,a,b,axis) \ +astINVOKE(V,astAxAngle_(astCheckFrame(this),a,b,axis,STATUS_PTR)) +#define astIntersect(this,a1,a2,b1,b2,cross) \ +astINVOKE(V,astIntersect_(astCheckFrame(this),a1,a2,b1,b2,cross,STATUS_PTR)) +#define astOffset2(this,point1,angle,offset,point2) \ +astINVOKE(V,astOffset2_(astCheckFrame(this),point1,angle,offset,point2,STATUS_PTR)) +#define astResolve(this,point1,point2,point3,point4,d1,d2) \ +astINVOKE(V,astResolve_(astCheckFrame(this),point1,point2,point3,point4,d1,d2,STATUS_PTR)) +#define astGetActiveUnit(this) \ +astINVOKE(V,astGetActiveUnit_(astCheckFrame(this),STATUS_PTR)) +#define astSetActiveUnit(this,value) \ +astINVOKE(V,astSetActiveUnit_(astCheckFrame(this),value,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astGetFrameVariants(this) \ +astINVOKE(O,astGetFrameVariants_(astCheckFrame(this),STATUS_PTR)) +#define astSetFrameVariants(this,variants) \ +astINVOKE(V,astSetFrameVariants_(astCheckFrame(this),astCheckFrameSet(variants),STATUS_PTR)) +#define astNormBox(this,lbnd,ubnd,reg) \ +astINVOKE(V,astNormBox_(astCheckFrame(this),lbnd,ubnd,astCheckMapping(reg),STATUS_PTR)) +#define astFormat(this,axis,value) \ +astINVOKE(V,astFormat_(astCheckFrame(this),axis,value,STATUS_PTR)) +#define astPermAxes(this,perm) \ +astINVOKE(V,astPermAxes_(astCheckFrame(this),perm,STATUS_PTR)) +#define astPickAxes(this,naxes,axes,map) \ +astINVOKE(O,astPickAxes_(astCheckFrame(this),naxes,axes,(AstMapping **)(map),STATUS_PTR)) +#define astUnformat(this,axis,string,value) \ +astINVOKE(V,astUnformat_(astCheckFrame(this),axis,string,value,STATUS_PTR)) +#else +#define astFormat(this,axis,value) \ +astINVOKE(V,astFormatId_(astCheckFrame(this),axis,value,STATUS_PTR)) +#define astPermAxes(this,perm) \ +astINVOKE(V,astPermAxesId_(astCheckFrame(this),perm,STATUS_PTR)) +#define astPickAxes(this,naxes,axes,map) \ +astINVOKE(O,astPickAxesId_(astCheckFrame(this),naxes,axes,(AstMapping **)(map),STATUS_PTR)) +#define astUnformat(this,axis,string,value) \ +astINVOKE(V,astUnformatId_(astCheckFrame(this),axis,string,value,STATUS_PTR)) +#endif + +#if defined(astCLASS) /* Protected */ +#define astAxIn(this,axis,lo,hi,val,closed) \ +astINVOKE(V,astAxIn_(astCheckFrame(this),axis,lo,hi,val,closed,STATUS_PTR)) +#define astAbbrev(this,axis,fmt,str1,str2) \ +astINVOKE(V,astAbbrev_(astCheckFrame(this),axis,fmt,str1,str2,STATUS_PTR)) +#define astFields(this,axis,fmt,str,maxfld,fields,nc,val) \ +astINVOKE(V,astFields_(astCheckFrame(this),axis,fmt,str,maxfld,fields,nc,val,STATUS_PTR)) +#define astCheckPerm(this,perm,method) \ +astINVOKE(V,astCheckPerm_(astCheckFrame(this),perm,method,STATUS_PTR)) +#define astResolvePoints(this,p1,p2,in,out) \ +astINVOKE(O,astResolvePoints_(astCheckFrame(this),p1,p2,astCheckPointSet(in),((out)?astCheckPointSet(out):NULL),STATUS_PTR)) +#define astLineDef(this,p1,p2) \ +astINVOKE(V,astLineDef_(astCheckFrame(this),p1,p2,STATUS_PTR)) +#define astLineOffset(this,line,par,prp,point) \ +astINVOKE(V,astLineOffset_(astCheckFrame(this),line,par,prp,point,STATUS_PTR)) +#define astFrameGrid(this,size,lbnd,ubnd) \ +astINVOKE(O,astFrameGrid_(astCheckFrame(this),size,lbnd,ubnd,STATUS_PTR)) +#define astLineCrossing(this,l1,l2,cross) \ +astINVOKE(V,astLineCrossing_(astCheckFrame(this),l1,l2,cross,STATUS_PTR)) +#define astLineContains(this,l,def,point) \ +astINVOKE(V,astLineContains_(astCheckFrame(this),l,def,point,STATUS_PTR)) +#define astClearDigits(this) \ +astINVOKE(V,astClearDigits_(astCheckFrame(this),STATUS_PTR)) +#define astClearDirection(this,axis) \ +astINVOKE(V,astClearDirection_(astCheckFrame(this),axis,STATUS_PTR)) +#define astClearDomain(this) \ +astINVOKE(V,astClearDomain_(astCheckFrame(this),STATUS_PTR)) +#define astClearFormat(this,axis) \ +astINVOKE(V,astClearFormat_(astCheckFrame(this),axis,STATUS_PTR)) +#define astClearLabel(this,axis) \ +astINVOKE(V,astClearLabel_(astCheckFrame(this),axis,STATUS_PTR)) +#define astClearMatchEnd(this) \ +astINVOKE(V,astClearMatchEnd_(astCheckFrame(this),STATUS_PTR)) +#define astClearMaxAxes(this) \ +astINVOKE(V,astClearMaxAxes_(astCheckFrame(this),STATUS_PTR)) +#define astClearMinAxes(this) \ +astINVOKE(V,astClearMinAxes_(astCheckFrame(this),STATUS_PTR)) +#define astClearPermute(this) \ +astINVOKE(V,astClearPermute_(astCheckFrame(this),STATUS_PTR)) +#define astClearPreserveAxes(this) \ +astINVOKE(V,astClearPreserveAxes_(astCheckFrame(this),STATUS_PTR)) +#define astClearSymbol(this,axis) \ +astINVOKE(V,astClearSymbol_(astCheckFrame(this),axis,STATUS_PTR)) +#define astClearTitle(this) \ +astINVOKE(V,astClearTitle_(astCheckFrame(this),STATUS_PTR)) +#define astClearUnit(this,axis) \ +astINVOKE(V,astClearUnit_(astCheckFrame(this),axis,STATUS_PTR)) +#define astConvertX(to,from,domainlist) \ +astINVOKE(O,astConvertX_(astCheckFrame(to),astCheckFrame(from),domainlist,STATUS_PTR)) +#define astCentre(this,axis,value,gap) \ +astINVOKE(V,astCentre_(astCheckFrame(this),axis,value,gap,STATUS_PTR)) +#define astGap(this,axis,gap,ntick) \ +astINVOKE(V,astGap_(astCheckFrame(this),axis,gap,ntick,STATUS_PTR)) +#define astGetAxis(this,axis) \ +astINVOKE(O,astGetAxis_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetDigits(this) \ +astINVOKE(V,astGetDigits_(astCheckFrame(this),STATUS_PTR)) +#define astGetDirection(this,axis) \ +astINVOKE(V,astGetDirection_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetDomain(this) \ +astINVOKE(V,astGetDomain_(astCheckFrame(this),STATUS_PTR)) +#define astGetFormat(this,axis) \ +astINVOKE(V,astGetFormat_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetLabel(this,axis) \ +astINVOKE(V,astGetLabel_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetMatchEnd(this) \ +astINVOKE(V,astGetMatchEnd_(astCheckFrame(this),STATUS_PTR)) +#define astGetMaxAxes(this) \ +astINVOKE(V,astGetMaxAxes_(astCheckFrame(this),STATUS_PTR)) +#define astGetMinAxes(this) \ +astINVOKE(V,astGetMinAxes_(astCheckFrame(this),STATUS_PTR)) +#define astGetNaxes(this) \ +astINVOKE(V,astGetNaxes_(astCheckFrame(this),STATUS_PTR)) +#define astGetPerm(this) \ +astINVOKE(V,astGetPerm_(astCheckFrame(this),STATUS_PTR)) +#define astGetPermute(this) \ +astINVOKE(V,astGetPermute_(astCheckFrame(this),STATUS_PTR)) +#define astGetPreserveAxes(this) \ +astINVOKE(V,astGetPreserveAxes_(astCheckFrame(this),STATUS_PTR)) +#define astGetSymbol(this,axis) \ +astINVOKE(V,astGetSymbol_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetTitle(this) \ +astINVOKE(V,astGetTitle_(astCheckFrame(this),STATUS_PTR)) +#define astGetUnit(this,axis) \ +astINVOKE(V,astGetUnit_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetNormUnit(this,axis) \ +astINVOKE(V,astGetNormUnit_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetInternalUnit(this,axis) \ +astINVOKE(V,astGetInternalUnit_(astCheckFrame(this),axis,STATUS_PTR)) +#define astMatch(template,target,matchsub,template_axes,target_axes,map,result) \ +astINVOKE(V,astMatch_(astCheckFrame(template),astCheckFrame(target),matchsub,template_axes,target_axes,(AstMapping **)(map),(AstFrame **)(result),STATUS_PTR)) +#define astIsUnitFrame(this) \ +astINVOKE(V,astIsUnitFrame_(astCheckFrame(this),STATUS_PTR)) +#define astOverlay(template,template_axes,result) \ +astINVOKE(V,astOverlay_(astCheckFrame(template),template_axes,astCheckFrame(result),STATUS_PTR)) +#define astPrimaryFrame(this,axis1,frame,axis2) \ +astINVOKE(V,astPrimaryFrame_(astCheckFrame(this),axis1,(AstFrame **)(frame),axis2,STATUS_PTR)) +#define astSetAxis(this,axis,newaxis) \ +astINVOKE(V,astSetAxis_(astCheckFrame(this),axis,astCheckAxis(newaxis),STATUS_PTR)) +#define astSetDigits(this,digits) \ +astINVOKE(V,astSetDigits_(astCheckFrame(this),digits,STATUS_PTR)) +#define astSetDirection(this,axis,direction) \ +astINVOKE(V,astSetDirection_(astCheckFrame(this),axis,direction,STATUS_PTR)) +#define astSetDomain(this,domain) \ +astINVOKE(V,astSetDomain_(astCheckFrame(this),domain,STATUS_PTR)) +#define astSetFormat(this,axis,format) \ +astINVOKE(V,astSetFormat_(astCheckFrame(this),axis,format,STATUS_PTR)) +#define astSetLabel(this,axis,label) \ +astINVOKE(V,astSetLabel_(astCheckFrame(this),axis,label,STATUS_PTR)) +#define astSetMatchEnd(this,value) \ +astINVOKE(V,astSetMatchEnd_(astCheckFrame(this),value,STATUS_PTR)) +#define astSetMaxAxes(this,value) \ +astINVOKE(V,astSetMaxAxes_(astCheckFrame(this),value,STATUS_PTR)) +#define astSetMinAxes(this,value) \ +astINVOKE(V,astSetMinAxes_(astCheckFrame(this),value,STATUS_PTR)) +#define astSetPermute(this,value) \ +astINVOKE(V,astSetPermute_(astCheckFrame(this),value,STATUS_PTR)) +#define astSetPreserveAxes(this,value) \ +astINVOKE(V,astSetPreserveAxes_(astCheckFrame(this),value,STATUS_PTR)) +#define astSetSymbol(this,axis,symbol) \ +astINVOKE(V,astSetSymbol_(astCheckFrame(this),axis,symbol,STATUS_PTR)) +#define astSetTitle(this,title) \ +astINVOKE(V,astSetTitle_(astCheckFrame(this),title,STATUS_PTR)) +#define astSetUnit(this,axis,unit) \ +astINVOKE(V,astSetUnit_(astCheckFrame(this),axis,unit,STATUS_PTR)) +#define astSubFrame(target,template,result_naxes,target_axes,template_axes,map,result) \ +astINVOKE(V,astSubFrame_(astCheckFrame(target),template?astCheckFrame(template):NULL,result_naxes,target_axes,template_axes,(AstMapping **)(map),(AstFrame **)(result),STATUS_PTR)) +#define astTestDigits(this) \ +astINVOKE(V,astTestDigits_(astCheckFrame(this),STATUS_PTR)) +#define astTestDirection(this,axis) \ +astINVOKE(V,astTestDirection_(astCheckFrame(this),axis,STATUS_PTR)) +#define astTestDomain(this) \ +astINVOKE(V,astTestDomain_(astCheckFrame(this),STATUS_PTR)) +#define astTestFormat(this,axis) \ +astINVOKE(V,astTestFormat_(astCheckFrame(this),axis,STATUS_PTR)) +#define astTestLabel(this,axis) \ +astINVOKE(V,astTestLabel_(astCheckFrame(this),axis,STATUS_PTR)) +#define astTestMatchEnd(this) \ +astINVOKE(V,astTestMatchEnd_(astCheckFrame(this),STATUS_PTR)) +#define astTestMaxAxes(this) \ +astINVOKE(V,astTestMaxAxes_(astCheckFrame(this),STATUS_PTR)) +#define astTestMinAxes(this) \ +astINVOKE(V,astTestMinAxes_(astCheckFrame(this),STATUS_PTR)) +#define astTestPermute(this) \ +astINVOKE(V,astTestPermute_(astCheckFrame(this),STATUS_PTR)) +#define astTestPreserveAxes(this) \ +astINVOKE(V,astTestPreserveAxes_(astCheckFrame(this),STATUS_PTR)) +#define astTestSymbol(this,axis) \ +astINVOKE(V,astTestSymbol_(astCheckFrame(this),axis,STATUS_PTR)) +#define astTestTitle(this) \ +astINVOKE(V,astTestTitle_(astCheckFrame(this),STATUS_PTR)) +#define astTestUnit(this,axis) \ +astINVOKE(V,astTestUnit_(astCheckFrame(this),axis,STATUS_PTR)) +#define astValidateAxis(this,axis,fwd,method) \ +astINVOKE(V,astValidateAxis_(astCheckFrame(this),axis,fwd,method,STATUS_PTR)) +#define astValidateAxisSelection(this,naxes,axes,method) \ +astINVOKE(V,astValidateAxisSelection_(astCheckFrame(this),naxes,axes,method,STATUS_PTR)) + +#define astMatchAxesX(frm2,frm1,axes) \ +astINVOKE(V,astMatchAxesX_(astCheckFrame(frm2),astCheckFrame(frm1),axes,STATUS_PTR)) + +#define astFmtDecimalYr(year,digits) astFmtDecimalYr_(year,digits,STATUS_PTR) +#define astReadDateTime(value) astReadDateTime_(value,STATUS_PTR) + +#define astValidateSystem(this,system,method) \ +astINVOKE(V,astValidateSystem_(astCheckFrame(this),system,method,STATUS_PTR)) +#define astSystemString(this,system) \ +astINVOKE(V,astSystemString_(astCheckFrame(this),system,STATUS_PTR)) +#define astSystemCode(this,system) \ +astINVOKE(V,astSystemCode_(astCheckFrame(this),system,STATUS_PTR)) + +#define astClearTop(this,axis) \ +astINVOKE(V,astClearTop_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetTop(this,axis) \ +astINVOKE(V,astGetTop_(astCheckFrame(this),axis,STATUS_PTR)) +#define astSetTop(this,axis,value) \ +astINVOKE(V,astSetTop_(astCheckFrame(this),axis,value,STATUS_PTR)) +#define astTestTop(this,axis) \ +astINVOKE(V,astTestTop_(astCheckFrame(this),axis,STATUS_PTR)) + +#define astClearBottom(this,axis) \ +astINVOKE(V,astClearBottom_(astCheckFrame(this),axis,STATUS_PTR)) +#define astGetBottom(this,axis) \ +astINVOKE(V,astGetBottom_(astCheckFrame(this),axis,STATUS_PTR)) +#define astSetBottom(this,axis,value) \ +astINVOKE(V,astSetBottom_(astCheckFrame(this),axis,value,STATUS_PTR)) +#define astTestBottom(this,axis) \ +astINVOKE(V,astTestBottom_(astCheckFrame(this),axis,STATUS_PTR)) + +#define astClearSystem(this) \ +astINVOKE(V,astClearSystem_(astCheckFrame(this),STATUS_PTR)) +#define astGetSystem(this) \ +astINVOKE(V,astGetSystem_(astCheckFrame(this),STATUS_PTR)) +#define astSetSystem(this,value) \ +astINVOKE(V,astSetSystem_(astCheckFrame(this),value,STATUS_PTR)) +#define astTestSystem(this) \ +astINVOKE(V,astTestSystem_(astCheckFrame(this),STATUS_PTR)) + +#define astClearAlignSystem(this) \ +astINVOKE(V,astClearAlignSystem_(astCheckFrame(this),STATUS_PTR)) +#define astGetAlignSystem(this) \ +astINVOKE(V,astGetAlignSystem_(astCheckFrame(this),STATUS_PTR)) +#define astSetAlignSystem(this,value) \ +astINVOKE(V,astSetAlignSystem_(astCheckFrame(this),value,STATUS_PTR)) +#define astTestAlignSystem(this) \ +astINVOKE(V,astTestAlignSystem_(astCheckFrame(this),STATUS_PTR)) + +#define astClearEpoch(this) \ +astINVOKE(V,astClearEpoch_(astCheckFrame(this),STATUS_PTR)) +#define astGetEpoch(this) \ +astINVOKE(V,astGetEpoch_(astCheckFrame(this),STATUS_PTR)) +#define astSetEpoch(this,value) \ +astINVOKE(V,astSetEpoch_(astCheckFrame(this),value,STATUS_PTR)) +#define astTestEpoch(this) \ +astINVOKE(V,astTestEpoch_(astCheckFrame(this),STATUS_PTR)) + +#define astGetObsLon(this) \ +astINVOKE(V,astGetObsLon_(astCheckFrame(this),STATUS_PTR)) +#define astTestObsLon(this) \ +astINVOKE(V,astTestObsLon_(astCheckFrame(this),STATUS_PTR)) +#define astClearObsLon(this) \ +astINVOKE(V,astClearObsLon_(astCheckFrame(this),STATUS_PTR)) +#define astSetObsLon(this,value) \ +astINVOKE(V,astSetObsLon_(astCheckFrame(this),value,STATUS_PTR)) + +#define astGetObsLat(this) \ +astINVOKE(V,astGetObsLat_(astCheckFrame(this),STATUS_PTR)) +#define astTestObsLat(this) \ +astINVOKE(V,astTestObsLat_(astCheckFrame(this),STATUS_PTR)) +#define astClearObsLat(this) \ +astINVOKE(V,astClearObsLat_(astCheckFrame(this),STATUS_PTR)) +#define astSetObsLat(this,value) \ +astINVOKE(V,astSetObsLat_(astCheckFrame(this),value,STATUS_PTR)) + +#define astGetObsAlt(this) \ +astINVOKE(V,astGetObsAlt_(astCheckFrame(this),STATUS_PTR)) +#define astTestObsAlt(this) \ +astINVOKE(V,astTestObsAlt_(astCheckFrame(this),STATUS_PTR)) +#define astClearObsAlt(this) \ +astINVOKE(V,astClearObsAlt_(astCheckFrame(this),STATUS_PTR)) +#define astSetObsAlt(this,value) \ +astINVOKE(V,astSetObsAlt_(astCheckFrame(this),value,STATUS_PTR)) + +#define astClearDut1(this) \ +astINVOKE(V,astClearDut1_(astCheckFrame(this),STATUS_PTR)) +#define astGetDut1(this) \ +astINVOKE(V,astGetDut1_(astCheckFrame(this),STATUS_PTR)) +#define astSetDut1(this,value) \ +astINVOKE(V,astSetDut1_(astCheckFrame(this),value,STATUS_PTR)) +#define astTestDut1(this) \ +astINVOKE(V,astTestDut1_(astCheckFrame(this),STATUS_PTR)) + +#define astTestActiveUnit(this) \ +astINVOKE(V,astTestActiveUnit_(astCheckFrame(this),STATUS_PTR)) + +#define astSetFrameFlags(this,flags) \ +astINVOKE(V,astSetFrameFlags_(astCheckFrame(this),flags,STATUS_PTR)) +#define astGetFrameFlags(this) \ +astINVOKE(V,astGetFrameFlags_(astCheckFrame(this),STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/frames.pdf b/frames.pdf new file mode 100644 index 0000000..6ac10e5 Binary files /dev/null and b/frames.pdf differ diff --git a/frameset.c b/frameset.c new file mode 100644 index 0000000..49855e8 --- /dev/null +++ b/frameset.c @@ -0,0 +1,13018 @@ +/* +*class++ +* Name: +* FrameSet + +* Purpose: +* Set of inter-related coordinate systems. + +* Constructor Function: +c astFrameSet +f AST_FRAMESET + +* Description: +* A FrameSet consists of a set of one or more Frames (which +* describe coordinate systems), connected together by Mappings +* (which describe how the coordinate systems are inter-related). A +* FrameSet makes it possible to obtain a Mapping between any pair +* of these Frames (i.e. to convert between any of the coordinate +* systems which it describes). The individual Frames are +* identified within the FrameSet by an integer index, with Frames +* being numbered consecutively from one as they are added to the +* FrameSet. +* +* Every FrameSet has a "base" Frame and a "current" Frame (which +* are allowed to be the same). Any of the Frames may be nominated +* to hold these positions, and the choice is determined by the +* values of the FrameSet's Base and Current attributes, which hold +* the indices of the relevant Frames. By default, the first Frame +* added to a FrameSet is its base Frame, and the last one added is +* its current Frame. +* +* The base Frame describes the "native" coordinate system of +* whatever the FrameSet is used to calibrate (e.g. the pixel +* coordinates of an image) and the current Frame describes the +* "apparent" coordinate system in which it should be viewed +* (e.g. displayed, etc.). Any further Frames represent a library +* of alternative coordinate systems, which may be selected by +* making them current. +* +* When a FrameSet is used in a context that requires a Frame, +* (e.g. obtaining its Title value, or number of axes), the current +* Frame is used. A FrameSet may therefore be used in place of its +* current Frame in most situations. +* +* When a FrameSet is used in a context that requires a Mapping, +* the Mapping used is the one between its base Frame and its +* current Frame. Thus, a FrameSet may be used to convert "native" +* coordinates into "apparent" ones, and vice versa. Like any +c Mapping, a FrameSet may also be inverted (see astInvert), which +f Mapping, a FrameSet may also be inverted (see AST_INVERT), which +* has the effect of interchanging its base and current Frames and +* hence of reversing the Mapping between them. +* +* Regions may be added into a FrameSet (since a Region is a type of +* Frame), either explicitly or as components within CmpFrames. In this +* case the Mapping between a pair of Frames within a FrameSet will +* include the effects of the clipping produced by any Regions included +* in the path between the Frames. + +* Inheritance: +* The FrameSet class inherits from the Frame class. + +* Attributes: +* In addition to those attributes common to all Frames, every +* FrameSet also has the following attributes: +* +* - AllVariants: List of all variant mappings store with current Frame +* - Base: FrameSet base Frame index +* - Current: FrameSet current Frame index +* - Nframe: Number of Frames in a FrameSet +* - Variant: Name of variant mapping in use by current Frame + +* Every FrameSet also inherits any further attributes that belong +* to its current Frame, regardless of that Frame's class. (For +* example, the Equinox attribute, defined by the SkyFrame class, is +* inherited by any FrameSet which has a SkyFrame as its current +* Frame.) The set of attributes belonging to a FrameSet may therefore +* change when a new current Frame is selected. + +* Functions: +c In addition to those functions applicable to all Frames, the +c following functions may also be applied to all FrameSets: +f In addition to those routines applicable to all Frames, the +f following routines may also be applied to all FrameSets: +* +c - astAddFrame: Add a Frame to a FrameSet to define a new coordinate +c system +c - astAddVariant: Add a variant Mapping to the current Frame +c - astGetFrame: Obtain a pointer to a specified Frame in a FrameSet +c - astGetMapping: Obtain a Mapping between two Frames in a FrameSet +c - astMirrorVariants: Make the current Frame mirror variant Mappings in another Frame +c - astRemapFrame: Modify a Frame's relationship to the other Frames in a +c FrameSet +c - astRemoveFrame: Remove a Frame from a FrameSet +f - AST_ADDFRAME: Add a Frame to a FrameSet to define a new coordinate +f system +f - AST_ADDVARIANT: Add a variant Mapping to the current Frame +f - AST_GETFRAME: Obtain a pointer to a specified Frame in a FrameSet +f - AST_GETMAPPING: Obtain a Mapping between two Frames in a FrameSet +f - AST_MIRRORVARIANTS: Make the current Frame mirror variant Mappings in another Frame +f - AST_REMAPFRAME: Modify a Frame's relationship to the other Frames in a +f FrameSet +f - AST_REMOVEFRAME: Remove a Frame from a FrameSet + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 16-FEB-1996 (RFWS): +* Original version. +* 5-JUN-1996 (RFWS): +* Tidied up, etc. +* 2-JUL-1996 (RFWS): +* Fixed bug in astRemoveFrame which caused the base/current +* Frame index to be wrong. +* 12-JUL-1996 (RFWS): +* Over-ride the astReportPoints method to provide +* Frame-specific formatting. +* 12-AUG-1996 (RFWS): +* Upgraded to provide a public interface, plus improvements to +* astAlign and the handling of nodes as Frames are +* added/removed. +* 11-SEP-1996 (RFWS): +* Added Gap. +* 25-SEP-1996 (RFWS): +* Added I/O facilities. +* 30-MAY-1997 (RFWS): +* Add special treatment for the ID attribute (which is not +* derived from the current Frame). +* 10-JUN-1997 (RFWS): +* Rationalised the astConvert implementation. +* 11-JUN-1997 (RFWS): +* Added the FindFrame implementation. +* 27-JUN-1997 (RFWS): +* Fixed bug which caused certain Mapping attributes to be +* handled by the current Frame instead of by the member +* functions defined by this class. +* 3-JUL-1997 (RFWS): +* Fixed bug: failing to extend the invert array in +* astRemapFrame. +* 10-JUL-1997 (RFWS): +* Over-ride the astSimplify method. +* 14-NOV-1997 (RFWS): +* Fixed error in loop implementing search over domains in +* FindFrame. +* 20-NOV-1997 (RFWS): +* Fixed bug in default Base and Current attribute values when a +* FrameSet has been inverted. +* 20-NOV-1997 (RFWS): +* Modified astConvert to use the current Frame of the "to" +* FrameSet as the destination coordinate system (instead of the +* base Frame) and to modify its Base attribute instead of its +* Current attribute. +* 22-DEC-1997 (RFWS): +* Further modified astConvert to convert from the Current Frame +* of the "from" FrameSet and to modify its Base +* attribute. Frame search order also reversed if the Invert +* attribute is non-zero for either FrameSet. +* 19-JAN-1998 (RFWS): +* Installed the TidyNodes function. +* 20-JAN-1998 (RFWS): +* Implemented preservation of FrameSet integrity when attribute +* values associated with the current Frame are modified. +* 24-FEB-1998 (RFWS): +* Added the ForceCopy function to allow integrity to be preserved +* when there are multiple references to the same Frame. +* 25-FEB-1998 (RFWS): +* Over-ride the astUnformat method. +* 24-MAR-1998 (RFWS): +* Fixed unterminated comment causing problems in CombineMaps. +* 6-APR-1998 (RFWS): +* Fixed another unterminated comment in CombineMaps. +* 27-MAY-1998 (RFWS): +* Fixed bug: failure to record new invert flag value after +* simplifying a CmpMap in TidyNodes. +* 17-DEC-2002 (DSB): +* Override accessors for Frame attributes Top, Bottom, Epoch, +* System, AlignSystem and ActiveUnit. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitFrameSetVtab +* method. +* 24-JAN-2004 (DSB): +* o Override the astFields method. +* o Add argument "fmt" to Abbrev. +* 23-MAR-2004 (DSB): +* Modified astGetMapping and Span to include the clipping effect of +* any Regions in the path between the two supplied Frames. +* 24-AUG-2004 (DSB): +* - Override various methods inherited from Frame (astAngle, +* astAxAngle, astAxDistance, astAxOffset, astCheckPerm, astOffset2, +* astResolve, astSystemCode, astSystemString, astValidateSystem, +* astValidateAxisSelection). These should have been overridden a +* long time ago! +* 8-SEP-2004 (DSB): +* Override astResolvePoints. +* 12-MAY-2005 (DSB): +* Override astNormBox method. +* 12-AUG-2005 (DSB): +* Override ObsLat and ObsLon accessor methods. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 15-MAY-2006 (DSB): +* Override astEqual. +* 30-JUN-2006 (DSB): +* Allow astAbbrev to have a null "str1" value. +* 22-JUN-2007 (DSB): +* Modify VSet to avoid using the args va_list twice since the +* first use (by the parent VSet function) invalidates the va_list +* causing a segvio to be generated by the second use (when +* formatting an error message). +* 11-JAN-2008 (DSB): +* Override the astRate method. +* 17-NOV-2008 (DSB): +* Correct parent class in invocation of astMAKE_ISA. +* 14-JAN-2009 (DSB): +* Override the astIntersect method. +* 18-JUN-2009 (DSB): +* Override ObsAlt accessor methods. +* 30-OCT-2009 (DSB): +* Make the Ident attribute relate to the FrameSet, not the current +* Frame. +* 22-MAR-2011 (DSB): +* Override astFrameGrid method. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +* 2-SEP-2011 (DSB): +* Fix FrameSet implememntation of astEqual (mapping comparison +* tests were logically inverted). +* 3-OCT-2012 (DSB): +* Fix bug in AppendAxes that could cause internal Mappings to +* be inverted unintentionally when astAddFrame is called with +* iframe=AST__ALLFRAMES. +* 29-APR-2013 (DSB): +* Added attributes AllVariants and Variant. Also added methods +* astAddVariant and astMirrorVariants. +* 25-SEP-2014 (DSB): +* Allow Base and Current attributes to be specified by giving a +* Domain name. +* 17-APR-2015 (DSB): +* Added Centre. +* 28-APR-2015 (DSB): +* astAdFrame now takes deep copies of the supplied mapping and +* frame, rather than just cloning their pointers. So the modified +* FrameSet is now independent of the supplied Mapping and Frame +* objects. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS FrameSet + +#define GETALLVARIANTS_BUFF_LEN 200 + +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Define a function to clear an attribute value for a FrameSet. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_CLEAR(attribute) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear( AstFrame *this ) +* +* that clears the value of a specified attribute for the current Frame +* of a FrameSet (this). This function is intended to over-ride the +* astClear method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute) \ +static void Clear##attribute( AstFrame *this_frame, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to the FrameSet structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Obtain a pointer to the current Frame and invoke its astClear \ + method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + astClear##attribute( fr ); \ + fr = astAnnul( fr ); \ +} + +/* +* Name: +* MAKE_CLEAR_AXIS + +* Purpose: +* Define a function to clear an attribute value for a FrameSet axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_CLEAR_AXIS(attribute) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear( AstFrame *this, int axis ) +* +* that clears the value of a specified attribute for an axis of +* the current Frame of a FrameSet (this). This function is +* intended to over-ride the astClear method inherited +* from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR_AXIS(attribute) \ +static void Clear##attribute( AstFrame *this_frame, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to the FrameSet structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astClear" #attribute ); \ +\ +/* Obtain a pointer to the FrameSet's current Frame and invoke its \ + astClear method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + astClear##attribute( fr, axis ); \ + fr = astAnnul( fr ); \ +} + +/* +* Name: +* MAKE_GET + +* Purpose: +* Define a function to get an attribute value for a FrameSet. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_GET(attribute,type) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static Get( AstFrame *this ) +* +* that gets the value of a specified attribute for the current Frame +* of a FrameSet (this). This function is intended to over-ride the +* astGet method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_GET(attribute,type) \ +static type Get##attribute( AstFrame *this_frame, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to the FrameSet structure */ \ + type result; /* Value to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (type) 0; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Obtain a pointer to the current Frame and invoke its \ + astGet method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + result = astGet##attribute( fr ); \ + fr = astAnnul( fr ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = (type) 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* Name: +* MAKE_GET_AXIS + +* Purpose: +* Define a function to get an attribute value for a FrameSet axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_GET_AXIS(attribute,type) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static Get( AstFrame *this, int axis ) +* +* that gets the value of a specified attribute for an axis of the +* current Frame of a FrameSet (this). This function is intended to +* over-ride the astGet method inherited from the Frame +* class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_GET_AXIS(attribute,type) \ +static type Get##attribute( AstFrame *this_frame, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to the FrameSet structure */ \ + type result; /* Value to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (type) 0; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astGet" #attribute ); \ +\ +/* Obtain a pointer to the FrameSet's current Frame and invoke its \ + astGet method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + result = astGet##attribute( fr, axis ); \ + fr = astAnnul( fr ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = (type) 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Define a function to set an attribute value for a FrameSet. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_SET(attribute,type) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstFrame *this, value ) +* +* that sets the value of a specified attribute for the current Frame +* of a FrameSet (this). This function is intended to over-ride the +* astSet method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,type) \ +static void Set##attribute( AstFrame *this_frame, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to the FrameSet structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Obtain a pointer to the FrameSet's current Frame and invoke its \ + astSet method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + astSet##attribute( fr, value ); \ + fr = astAnnul( fr ); \ +} + +/* +* Name: +* MAKE_SET_AXIS + +* Purpose: +* Define a function to set an attribute value for a FrameSet axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_SET_AXIS(attribute,type) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstFrame *this, int axis, value ) +* +* that sets the value of a specified attribute for an axis of the +* current Frame of a FrameSet (this). This function is intended to +* over-ride the astSet method inherited from the Frame +* class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_SET_AXIS(attribute,type) \ +static void Set##attribute( AstFrame *this_frame, int axis, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to the FrameSet structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astSet" #attribute ); \ +\ +/* Obtain a pointer to the FrameSet's current Frame and invoke its \ + astSet method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + astSet##attribute( fr, axis, value ); \ + fr = astAnnul( fr ); \ +} + +/* +* Name: +* MAKE_TEST + +* Purpose: +* Define a function to test if an attribute value is set for a FrameSet. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_TEST(attribute) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static int Test( AstFrame *this ) +* +* that returns a boolean result (0 or 1) to indicate if the value +* of a specified attribute for the current Frame of a FrameSet +* (this) is set. This function is intended to over-ride the +* astTest method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_TEST(attribute) \ +static int Test##attribute( AstFrame *this_frame, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to FrameSet structure */ \ + int result; /* Result to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Obtain a pointer to the FrameSet's current Frame and invoke its \ + astTest method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + result = astTest##attribute( fr ); \ + fr = astAnnul( fr ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* Name: +* MAKE_TEST_AXIS + +* Purpose: +* Define a function to test if an attribute value is set for a FrameSet +* axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "frameset.h" +* MAKE_TEST_AXIS(attribute) + +* Class Membership: +* Defined by the FrameSet class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static int Test( AstFrame *this, int axis ) +* +* that returns a boolean result (0 or 1) to indicate if the value +* of a specified attribute for an axis of the current Frame of a +* FrameSet (this) is set. This function is intended to over-ride +* the astTest method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_TEST_AXIS(attribute) \ +static int Test##attribute( AstFrame *this_frame, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *fr; /* Pointer to current Frame */ \ + AstFrameSet *this; /* Pointer to the FrameSet structure */ \ + int result; /* Value to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Obtain a pointer to the FrameSet structure. */ \ + this = (AstFrameSet *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astTest" #attribute ); \ +\ +/* Obtain a pointer to the FrameSet's current Frame and invoke its \ + astTest method. Annul the Frame pointer afterwards. */ \ + fr = astGetFrame( this, AST__CURRENT ); \ + result = astTest##attribute( fr, axis ); \ + fr = astAnnul( fr ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "mapping.h" /* Coordinate Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Coordinate permutation Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "frame.h" /* Parent Frame class */ +#include "frameset.h" /* Interface definition for this class */ +#include "cmpframe.h" /* Compound coordinate frames */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static void (* parent_clear)( AstObject *, const char *, int * ); +static int (* parent_getusedefs)( AstObject *, int * ); +static void (* parent_vset)( AstObject *, const char *, char **, va_list, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->Integrity_Frame = NULL; \ + globals->Integrity_Method = ""; \ + globals->Integrity_Lost = 0; \ + globals->GetAllVariants_Buff[ 0 ] = 0; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(FrameSet) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(FrameSet,Class_Init) +#define class_vtab astGLOBAL(FrameSet,Class_Vtab) +#define getattrib_buff astGLOBAL(FrameSet,GetAttrib_Buff) +#define integrity_frame astGLOBAL(FrameSet,Integrity_Frame) +#define integrity_method astGLOBAL(FrameSet,Integrity_Method) +#define integrity_lost astGLOBAL(FrameSet,Integrity_Lost) +#define getallvariants_buff astGLOBAL(FrameSet,GetAllVariants_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ AST__FRAMESET_GETATTRIB_BUFF_LEN + 1 ]; + +/* Variables associated with preserving FrameSet integrity. */ +static AstFrame *integrity_frame = NULL; /* Pointer to copy of current Frame */ +static const char *integrity_method = ""; /* Name of method being used */ +static int integrity_lost = 0; /* Current Frame modified? */ + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstFrameSetVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +/* String buffers. */ +static char getallvariants_buff[ AST__FRAMESET_GETALLVARIANTS_BUFF_LEN + 1 ]; + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstAxis *GetAxis( AstFrame *, int, int * ); +static AstFrame *GetFrame( AstFrameSet *, int, int * ); +static AstFrame *PickAxes( AstFrame *, int, const int[], AstMapping **, int * ); +static AstFrameSet *Convert( AstFrame *, AstFrame *, const char *, int * ); +static AstFrameSet *ConvertX( AstFrame *, AstFrame *, const char *, int * ); +static AstFrameSet *FindFrame( AstFrame *, AstFrame *, const char *, int * ); +static AstLineDef *LineDef( AstFrame *, const double[2], const double[2], int * ); +static AstMapping *CombineMaps( AstMapping *, int, AstMapping *, int, int, int * ); +static AstMapping *GetMapping( AstFrameSet *, int, int, int * ); +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstObject *Cast( AstObject *, AstObject *, int * ); +static AstPointSet *FrameGrid( AstFrame *, int, const double *, const double *, int * ); +static AstPointSet *ResolvePoints( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static const char *Abbrev( AstFrame *, int, const char *, const char *, const char *, int * ); +static const char *Format( AstFrame *, int, double, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetFormat( AstFrame *, int, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const char *GetAllVariants( AstFrameSet *, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static const int *GetPerm( AstFrame *, int * ); +static double Angle( AstFrame *, const double[], const double[], const double[], int * ); +static double AxAngle( AstFrame *, const double[], const double[], int, int * ); +static double AxDistance( AstFrame *, int, double, double, int * ); +static double AxOffset( AstFrame *, int, double, double, int * ); +static double Distance( AstFrame *, const double[], const double[], int * ); +static double Centre( AstFrame *, int, double, double, int * ); +static double Gap( AstFrame *, int, double, int *, int * ); +static double Offset2( AstFrame *, const double[2], double, double, double[2], int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int Fields( AstFrame *, int, const char *, const char *, int, char **, int *, double *, int * ); +static int ForceCopy( AstFrameSet *, int, int * ); +static int GetActiveUnit( AstFrame *, int * ); +static int GetBase( AstFrameSet *, int * ); +static int GetCurrent( AstFrameSet *, int * ); +static int GetDigits( AstFrame *, int * ); +static int GetDirection( AstFrame *, int, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int GetMatchEnd( AstFrame *, int * ); +static int GetMaxAxes( AstFrame *, int * ); +static int GetMinAxes( AstFrame *, int * ); +static int GetNaxes( AstFrame *, int * ); +static int GetNframe( AstFrameSet *, int * ); +static int GetNin( AstMapping *, int * ); +static int GetNout( AstMapping *, int * ); +static int GetObjSize( AstObject *, int * ); +static int GetPermute( AstFrame *, int * ); +static int GetPreserveAxes( AstFrame *, int * ); +static int GetTranForward( AstMapping *, int * ); +static int GetTranInverse( AstMapping *, int * ); +static int GetVarFrm( AstFrameSet *, int, int * ); +static int IsUnitFrame( AstFrame *, int * ); +static int LineContains( AstFrame *, AstLineDef *, int, double *, int * ); +static int LineCrossing( AstFrame *, AstLineDef *, AstLineDef *, double **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int Span( AstFrameSet *, AstFrame **, int, int, int, AstMapping **, int *, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestBase( AstFrameSet *, int * ); +static int TestCurrent( AstFrameSet *, int * ); +static int TestDigits( AstFrame *, int * ); +static int TestDirection( AstFrame *, int, int * ); +static int TestDomain( AstFrame *, int * ); +static int TestFormat( AstFrame *, int, int * ); +static int TestLabel( AstFrame *, int, int * ); +static int TestMatchEnd( AstFrame *, int * ); +static int TestMaxAxes( AstFrame *, int * ); +static int TestMinAxes( AstFrame *, int * ); +static int TestPermute( AstFrame *, int * ); +static int TestPreserveAxes( AstFrame *, int * ); +static int TestSymbol( AstFrame *, int, int * ); +static int TestTitle( AstFrame *, int * ); +static int TestUnit( AstFrame *, int, int * ); +static int Unformat( AstFrame *, int, const char *, double *, int * ); +static int ValidateAxis( AstFrame *, int, int, const char *, int * ); +static int ValidateFrameIndex( AstFrameSet *, int, const char *, int * ); +static void AddFrame( AstFrameSet *, int, AstMapping *, AstFrame *, int * ); +static void AppendAxes( AstFrameSet *, AstFrame *, int * ); +static void CheckPerm( AstFrame *, const int *, const char *, int * ); +static void Clear( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearBase( AstFrameSet *, int * ); +static void ClearCurrent( AstFrameSet *, int * ); +static void ClearDigits( AstFrame *, int * ); +static void ClearDirection( AstFrame *, int, int * ); +static void ClearDomain( AstFrame *, int * ); +static void ClearFormat( AstFrame *, int, int * ); +static void ClearLabel( AstFrame *, int, int * ); +static void ClearMatchEnd( AstFrame *, int * ); +static void ClearMaxAxes( AstFrame *, int * ); +static void ClearMinAxes( AstFrame *, int * ); +static void ClearPermute( AstFrame *, int * ); +static void ClearPreserveAxes( AstFrame *, int * ); +static void ClearSymbol( AstFrame *, int, int * ); +static void ClearTitle( AstFrame *, int * ); +static void ClearUnit( AstFrame *, int, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Intersect( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * ); +static void LineOffset( AstFrame *, AstLineDef *, double, double, double[2], int * ); +static void MatchAxes( AstFrame *, AstFrame *, int *, int * ); +static void MatchAxesX( AstFrame *, AstFrame *, int *, int * ); +static void Norm( AstFrame *, double[], int * ); +static void NormBox( AstFrame *, double[], double[], AstMapping *, int * ); +static void Offset( AstFrame *, const double[], const double[], double, double[], int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void PermAxes( AstFrame *, const int[], int * ); +static void PrimaryFrame( AstFrame *, int, AstFrame **, int *, int * ); +static void RecordIntegrity( AstFrameSet *, int * ); +static void AddVariant( AstFrameSet *, AstMapping *, const char *, int * ); +static void MirrorVariants( AstFrameSet *, int, int * ); +static void RemapFrame( AstFrameSet *, int, AstMapping *, int * ); +static void RemoveFrame( AstFrameSet *, int, int * ); +static void RemoveMirrors( AstFrameSet *, int, int * ); +static void ReportPoints( AstMapping *, int, AstPointSet *, AstPointSet *, int * ); +static void Resolve( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); +static void RestoreIntegrity( AstFrameSet *, int * ); +static void SetActiveUnit( AstFrame *, int, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetAxis( AstFrame *, int, AstAxis *, int * ); +static void SetBase( AstFrameSet *, int, int * ); +static void SetCurrent( AstFrameSet *, int, int * ); +static void SetDigits( AstFrame *, int, int * ); +static void SetDirection( AstFrame *, int, int, int * ); +static void SetDomain( AstFrame *, const char *, int * ); +static void SetFormat( AstFrame *, int, const char *, int * ); +static void SetLabel( AstFrame *, int, const char *, int * ); +static void SetMatchEnd( AstFrame *, int, int * ); +static void SetMaxAxes( AstFrame *, int, int * ); +static void SetMinAxes( AstFrame *, int, int * ); +static void SetPermute( AstFrame *, int, int * ); +static void SetPreserveAxes( AstFrame *, int, int * ); +static void SetSymbol( AstFrame *, int, const char *, int * ); +static void SetTitle( AstFrame *, const char *, int * ); +static void SetUnit( AstFrame *, int, const char *, int * ); +static void TidyNodes( AstFrameSet *, int * ); +static void VSet( AstObject *, const char *, char **, va_list, int * ); +static void ValidateAxisSelection( AstFrame *, int, const int *, const char *, int * ); + +static double GetBottom( AstFrame *, int, int * ); +static int TestBottom( AstFrame *, int, int * ); +static void ClearBottom( AstFrame *, int, int * ); +static void SetBottom( AstFrame *, int, double, int * ); + +static double GetTop( AstFrame *, int, int * ); +static int TestTop( AstFrame *, int, int * ); +static void ClearTop( AstFrame *, int, int * ); +static void SetTop( AstFrame *, int, double, int * ); + +static double GetEpoch( AstFrame *, int * ); +static int TestEpoch( AstFrame *, int * ); +static void ClearEpoch( AstFrame *, int * ); +static void SetEpoch( AstFrame *, double, int * ); + +static double GetObsAlt( AstFrame *, int * ); +static int TestObsAlt( AstFrame *, int * ); +static void ClearObsAlt( AstFrame *, int * ); +static void SetObsAlt( AstFrame *, double, int * ); + +static double GetObsLat( AstFrame *, int * ); +static int TestObsLat( AstFrame *, int * ); +static void ClearObsLat( AstFrame *, int * ); +static void SetObsLat( AstFrame *, double, int * ); + +static double GetObsLon( AstFrame *, int * ); +static int TestObsLon( AstFrame *, int * ); +static void ClearObsLon( AstFrame *, int * ); +static void SetObsLon( AstFrame *, double, int * ); + +static int GetUseDefs( AstObject *, int * ); + +static AstSystemType GetSystem( AstFrame *, int * ); +static int TestSystem( AstFrame *, int * ); +static void ClearSystem( AstFrame *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); + +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static int TestAlignSystem( AstFrame *, int * ); +static void ClearAlignSystem( AstFrame *, int * ); +static void SetAlignSystem( AstFrame *, AstSystemType, int * ); + +static const char *GetVariant( AstFrameSet *, int * ); +static int TestVariant( AstFrameSet *, int * ); +static void ClearVariant( AstFrameSet *, int * ); +static void SetVariant( AstFrameSet *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static const char *Abbrev( AstFrame *this_frame, int axis, const char *fmt, + const char *str1, const char *str2, int *status ) { +/* +* Name: +* Abbrev + +* Purpose: +* Abbreviate a formatted FrameSet axis value by skipping leading fields. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* const char *Abbrev( AstFrame *this, int axis, const char *fmt, +* const char *str1, const char *str2, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astAbbrev +* method inherited from the Frame class). + +* Description: +* This function compares two FrameSet axis values that have been +* formatted (using astFormat) and determines if they have any +* redundant leading fields (i.e. leading fields in common which +* can be suppressed when tabulating the values or plotting them on +* the axis of a graph). + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The number of the FrameSet axis for which the values have +* been formatted (axis numbering starts at zero for the first +* axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format specification used to format the two values. +* str1 +* Pointer to a constant null-terminated string containing the +* first formatted value. If this is null, the returned pointer +* points to the start of the final field in str2. +* str2 +* Pointer to a constant null-terminated string containing the +* second formatted value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer into the "str2" string which locates the first +* character in the first field that differs between the two +* formatted values. +* +* If the two values have no leading fields in common, the returned +* value will point at the start of string "str2". If the two +* values are equal, it will point at the terminating null at the +* end of this string. + +* Notes: +* - This function assumes that the format specification used was +* the same when both values were formatted and that they both +* apply to the same FrameSet axis. +* - A pointer to the start of "str2" will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return str2; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astAbbrev" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astAbbrev method to perform the processing. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astAbbrev( fr, axis, fmt, str1, str2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = str2; + +/* Return the result. */ + return result; +} + +static void AddFrame( AstFrameSet *this, int iframe, AstMapping *map, + AstFrame *frame, int *status ) { +/* +*++ +* Name: +c astAddFrame +f AST_ADDFRAME + +* Purpose: +* Add a Frame to a FrameSet to define a new coordinate system. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astAddFrame( AstFrameSet *this, int iframe, AstMapping *map, +c AstFrame *frame ) +f CALL AST_ADDFRAME( THIS, IFRAME, MAP, FRAME, STATUS ) + +* Class Membership: +* FrameSet method. + +* Description: +c This function adds a new Frame and an associated Mapping to a +f This routine adds a new Frame and an associated Mapping to a +* FrameSet so as to define a new coordinate system, derived from +* one which already exists within the FrameSet. The new Frame then +* becomes the FrameSet's current Frame. +* +c This function +f This routine +* may also be used to merge two FrameSets, or to append extra axes +* to every Frame in a FrameSet. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FrameSet. +c iframe +f IFRAME = INTEGER (Given) +* The index of the Frame within the FrameSet which describes +* the coordinate system upon which the new one is to be based. +* This value should lie in the range from 1 to the number of +* Frames already in the FrameSet (as given by its Nframe +* attribute). As a special case, AST__ALLFRAMES may be supplied, +* in which case the axes defined by the supplied Frame are appended +* to every Frame in the FrameSet (see the Notes section for details). +c map +f MAP = INTEGER (Given) +* Pointer to a Mapping which describes how to convert +* coordinates from the old coordinate system (described by the +c Frame with index "iframe") into coordinates in the new +f Frame with index IFRAME) into coordinates in the new +* system. The Mapping's forward transformation should perform +* this conversion, and its inverse transformation should +* convert in the opposite direction. The supplied Mapping is ignored +c if parameter "iframe"is equal to AST__ALLFRAMES. +f if parameter IFRAME is equal to AST__ALLFRAMES. +c frame +f FRAME = INTEGER (Given) +* Pointer to a Frame that describes the new coordinate system. +* Any class of Frame may be supplied (including Regions and +* FrameSets). +* +c This function may also be used to merge two FrameSets by +c supplying a pointer to a second FrameSet for this parameter +f This routine may also be used to merge two FrameSets by +f supplying a pointer to a second FrameSet for this argument +* (see the Notes section for details). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Deep copies of the supplied +c "mapping" and "frame" +f MAPPING and FRAME +* objects are stored within the modified FrameSet. So any changes made +* to the FrameSet after calling this method will have no effect on the +* supplied Mapping and Frame objects. +* - A value of AST__BASE or AST__CURRENT may be given for the +c "iframe" parameter to specify the base Frame or the current +f IFRAME argument to specify the base Frame or the current +* Frame respectively. +c - This function sets the value of the Current attribute for the +f - This routine sets the value of the Current attribute for the +* FrameSet so that the new Frame subsequently becomes the current +* Frame. +* - The number of input coordinate values accepted by the supplied +* Mapping (its Nin attribute) must match the number of axes in the +c Frame identified by the "iframe" parameter. Similarly, the +f Frame identified by the IFRAME argument. Similarly, the +* number of output coordinate values generated by this Mapping +* (its Nout attribute) must match the number of axes in the new +* Frame. +* - As a special case, if a pointer to a FrameSet is given for the +c "frame" parameter, this is treated as a request to merge a pair of +f FRAME argument, this is treated as a request to merge a pair of +* FrameSets. This is done by appending all the new Frames (in the +c "frame" FrameSet) to the original FrameSet, while preserving +f FRAME FrameSet) to the original FrameSet, while preserving +* their order and retaining all the inter-relationships +* (i.e. Mappings) between them. The two sets of Frames are +* inter-related within the merged FrameSet by using the Mapping +* supplied. This should convert between the Frame identified by +c the "iframe" parameter (in the original FrameSet) and the current +c Frame of the "frame" FrameSet. This latter Frame becomes the +f the IFRAME argument (in the original FrameSet) and the current +f Frame of the FRAME FrameSet. This latter Frame becomes the +* current Frame in the merged FrameSet. +* - As another special case, if a value of AST__ALLFRAMES is supplied +* for parameter +c "iframe", +f IFRAME, +* then the supplied Mapping is ignored, and the axes defined by the +* supplied Frame are appended to each Frame in the FrameSet. In detail, +* each Frame in the FrameSet is replaced by a CmpFrame containing the +* original Frame and the Frame specified by parameter +c "frame". +f FRAME. +* In addition, each Mapping in the FrameSet is replaced by a CmpMap +* containing the original Mapping and a UnitMap in parallel. The Nin and +* Nout attributes of the UnitMap are set equal to the number of axes +* in the supplied Frame. Each new CmpMap is simplified using +c astSimplify +f AST_SIMPLIFY +* before being stored in the FrameSet. + + + +*-- +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to Frame identified by "iframe" */ + AstFrameSet *frameset; /* Pointer to new FrameSet (if given) */ + AstMapping *inode_map; /* Temporarily saved Mapping pointer */ + AstMapping *next_map; /* Temporarily saved Mapping pointer */ + int current; /* Current Frame index in merged FrameSet */ + int current_node; /* Node number for current Frame */ + int ifr; /* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + int inode_invert; /* Temporarily saved invert flag value */ + int inode_link; /* Temporarily saved link value */ + int naxes; /* Number of Frame axes */ + int ncoord; /* Number of Mapping coordinates per point */ + int next; /* Number of next node in path */ + int next_invert; /* Temporarily saved invert flag value */ + int next_link; /* Temporarily saved link value */ + int nframe; /* Number of Frames in merged FrameSet */ + int nnode; /* Number of nodes in merged FrameSet */ + int node_zero; /* Location of "node zero" after merging */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* First handle cases where we are appending axes to the existing + Frames in a FrameSet. */ + if( iframe == AST__ALLFRAMES ) { + AppendAxes( this, frame, status ); + return; + } + +/* Now handle cases where we are adding a new Frame into the FrameSet. + Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + inode_map = NULL; + next_map = NULL; + inode_invert = 0; + next = 0; + next_invert = 0; + next_link = 0; + +/* Validate and translate the Frame index supplied. */ + iframe = astValidateFrameIndex( this, iframe, "astAddFrame" ); + +/* Obtain a pointer to the Frame from which the new coordinate system + will be derived and determine how many axes it has. Annul the Frame + pointer afterwards. */ + if ( astOK ) { + fr = astGetFrame( this, iframe ); + naxes = astGetNaxes( fr ); + fr = astAnnul( fr ); + +/* Obtain the number of input coordinate values per point for the + Mapping supplied and check that this matches the number of axes + obtained above. Report an error if it does not. */ + ncoord = astGetNin( map ); + if ( astOK && ( naxes != ncoord ) ) { + astError( AST__NCPIN, "astAddFrame(%s): Bad number of %s input " + "coordinate values (%d).", status, astGetClass( this ), + astGetClass( map ), ncoord ); + astError( AST__NCPIN, "The %s given should accept %d coordinate " + "value%s for each input point.", status, astGetClass( map ), naxes, + ( naxes == 1 ) ? "" : "s" ); + } + } + +/* Similarly, obtain the number of output coordinate values per point + for the Mapping and check that this equals the number of axes for + the Frame supplied. Report an error if necessary. */ + if ( astOK ) { + ncoord = astGetNout( map ); + naxes = astGetNaxes( frame ); + if ( astOK && ( ncoord != naxes ) ) { + astError( AST__NCPIN, "astAddFrame(%s): Bad number of %s output " + "coordinate values (%d).", status, astGetClass( this ), + astGetClass( map ), ncoord ); + astError( AST__NCPIN, "The %s given should generate %d " + "coordinate value%s for each output point.", status, + astGetClass( map ), naxes, ( naxes == 1 ) ? "" : "s" ); + } + } + +/* Normal Frame supplied. */ +/* ====================== */ +/* Check that the Frame supplied is not a FrameSet (handling a + FrameSet is a special case which is addressed later). */ + if ( !astIsAFrameSet( frame ) && astOK ) { + +/* Increase the size of the FrameSet's arrays to accommodate one new + Frame. */ + this->frame = astGrow( this->frame, this->nframe + 1, + sizeof( AstFrame * ) ); + this->varfrm = astGrow( this->varfrm, this->nframe + 1, + sizeof( int ) ); + this->node = astGrow( this->node, this->nframe + 1, sizeof( int ) ); + this->map = astGrow( this->map, this->nnode, sizeof( AstMapping * ) ); + this->link = astGrow( this->link, this->nnode, sizeof( int ) ); + this->invert = astGrow( this->invert, this->nnode, sizeof( int ) ); + if ( astOK ) { + +/* Copy pointers to the Frame and Mapping supplied and store these pointers + in the FrameSet arrays. */ + this->frame[ this->nframe ] = astCopy( frame ); + this->map[ this->nnode - 1 ] = astCopy( map ); + +/* Indicate the Frame does not reflect the variant Mappings of any other + Frame. */ + this->varfrm[ this->nframe ] = 0; + +/* Associate the Frame with the Mapping via the "node" array. */ + this->node[ this->nframe ] = this->nnode; + +/* Add a "link" value which identifies the node from which the new + node is derived and store the current value of the Invert attribute + for the Mapping. */ + this->link[ this->nnode - 1 ] = this->node[ iframe - 1 ]; + this->invert[ this->nnode - 1 ] = astGetInvert( map ); + +/* If successful, increment the FrameSet's Frame and node counts and + set the Current attribute so that the new Frame becomes the current + Frame. */ + if ( astOK ) { + this->nframe++; + this->nnode++; + astSetCurrent( this, this->nframe ); + +/* If an error occurred while filling the FrameSet's arrays, clear any values + that may have been added, annulling any copied pointers. */ + } else { + this->frame[ this->nframe ] = + astAnnul( this->frame[ this->nframe ] ); + this->node[ this->nframe ] = -1; + this->map[ this->nnode - 1 ] = + astAnnul( this->map[ this->nnode - 1 ] ); + this->link[ this->nnode - 1 ] = -1; + } + } + +/* FrameSet supplied. */ +/* ================== */ +/* If the Frame supplied is a FrameSet, we handle this as a special + case by merging the two FrameSets (so that the final result + contains references to all the Frames from both FrameSets). */ + } else if ( astOK ) { + +/* Obtain a pointer to the FrameSet structure containing the new Frame + references and calculate how many Frames and nodes the combined + FrameSet will contain. */ + frameset = (AstFrameSet *) frame; + nframe = this->nframe + frameset->nframe; + nnode = this->nnode + frameset->nnode; + +/* Extend the original FrameSet's arrays to accommodate the new Frames + and nodes. */ + this->frame = astGrow( this->frame, nframe, sizeof( AstFrame * ) ); + this->varfrm = astGrow( this->varfrm, nframe, sizeof( int ) ); + this->node = astGrow( this->node, nframe, sizeof( int ) ); + this->map = astGrow( this->map, nnode - 1, sizeof( AstMapping * ) ); + this->link = astGrow( this->link, nnode - 1, sizeof( int ) ); + this->invert = astGrow( this->invert, nnode - 1, sizeof( int ) ); + +/* If OK, loop to transfer the new Frame data into the new array + elements, cloning each Frame pointer. Increment each "node" value + to allow for the new node numbering which will apply when the new + node data is appended to the new arrays. */ + if ( astOK ) { + for ( ifr = 1; ifr <= frameset->nframe; ifr++ ) { + this->frame[ this->nframe + ifr - 1 ] = + astCopy( frameset->frame[ ifr - 1 ] ); + this->node[ this->nframe + ifr - 1 ] = + frameset->node[ ifr - 1 ] + this->nnode; + if( frameset->varfrm[ ifr - 1 ] > 0 ) { + this->varfrm[ this->nframe + ifr - 1 ] = + frameset->varfrm[ ifr - 1 ] + this->nframe; + } else { + this->varfrm[ this->nframe + ifr - 1 ] = 0; + } + } + +/* Similarly, transfer the new node data, cloning each Mapping + pointer. Increment each "link" value to allow for the new node + numbering. */ + for ( inode = 1; inode < frameset->nnode; inode++ ) { + this->map[ this->nnode + inode - 1 ] = + astCopy( frameset->map[ inode - 1 ] ); + this->link[ this->nnode + inode - 1 ] = + frameset->link[ inode - 1 ] + this->nnode; + this->invert[ this->nnode + inode - 1 ] = + frameset->invert[ inode - 1 ]; + } + +/* In transferring the node data (above), we left an empty array + element which will later be filled with data corresponding to node + zero in the new FrameSet (there are no data to be copied for this + node). Initialise the data for this element to null values. */ + this->map[ this->nnode - 1 ] = NULL; + this->link[ this->nnode - 1 ] = -1; + this->invert[ this->nnode - 1 ] = -1; + +/* Determine which is the current Frame in the new FrameSet and + convert this into the corresponding Frame number in the combined + one. */ + current = astGetCurrent( frameset ) + this->nframe; + +/* We must now form a new link between this Frame and Frame "iframe" + (using the Mapping supplied) in order to inter-relate the Frames + from the two FrameSets. However, this cannot be done immediately + because in general the node corresponding to Frame "current" will + already have a link pointing to another node. Moreover, the node + which was originally node zero (in the new FrameSet) still has + no data in our merged FrameSet. + + To overcome this, we must re-structure the links within the + transferred data. We do this by starting at the node corresponding + to Frame "current" and working back through each link until the + original node zero is reached. At each step along this path, we + reverse the direction of the link. This involves shifting the + associated data by one step along the path, so that it becomes + associated with the next node. This results in the final + (initialised-to-null) node acquiring some data, and the starting + node being left free to receive our new link. + + We compensate for reversing the links by reversing the sense of the + "invert" flag associated with each Mapping along the path, so that + the overall structure of the FrameSet is unchanged. */ + +/* Identify the starting node (the one corresponding to Frame + "current"). */ + if ( astOK ) { + current_node = this->node[ current - 1 ]; + +/* Obtain the value which a "link" element will now have if it + originally identified node zero in the new FrameSet. We will use + this value to detect the end of the path. */ + node_zero = this->nnode; + +/* If we are not already at "node zero", save the data for the current + node. */ + if ( current_node != node_zero ) { + inode_map = this->map[ current_node - 1 ]; + inode_link = this->link[ current_node - 1 ]; + inode_invert = this->invert[ current_node - 1 ]; + +/* Reset the node's data to null values (pending setting up the new + link using the Mapping supplied). */ + this->map[ current_node - 1 ] = NULL; + this->link[ current_node - 1 ] = -1; + this->invert[ current_node - 1 ] = -1; + +/* Identify the next node in the path. */ + next = inode_link; + } + +/* Follow the path until "node zero" is reached. */ + inode = current_node; + while( inode != node_zero ) { + +/* If the next node on the path is not "node zero", save its data + (because we are about to write over it). */ + if ( next != node_zero ) { + next_map = this->map[ next - 1 ]; + next_link = this->link[ next - 1 ]; + next_invert = this->invert[ next - 1 ]; + } + +/* Reverse the link from the current node to the "next" node. This + involves transferring the "map" and "invert" values to the "next" + node and inverting the sense of the latter to compensate. Make the + "next" node point back to the current one. */ + this->map[ next - 1 ] = inode_map; + this->link[ next - 1 ] = inode; + this->invert[ next - 1 ] = !inode_invert; + +/* Move on to consider the next node. */ + inode = next; + +/* If we have not reached "node zero" yet, transfer the node data we + saved above into the variables from which it will be transferred to + the following node on the next pass through this loop. */ + if ( inode != node_zero ) { + inode_map = next_map; + inode_link = next_link; + inode_invert = next_invert; + +/* Identify the node that follows the next one. */ + next = inode_link; + } + } + +/* Once the necessary links have been re-structured, establish the new + link that inter-relates the Frames from the two FrameSets. */ + this->map[ current_node - 1 ] = astCopy( map ); + this->link[ current_node - 1 ] = this->node[ iframe - 1 ]; + this->invert[ current_node - 1 ] = astGetInvert( map ); + } + +/* If successful, update the Frame and node counts and make the + appropriate Frame current. */ + if ( astOK ) { + this->nframe = nframe; + this->nnode = nnode; + astSetCurrent( this, current ); + +/* If an error occurred, loop through all the new Frame and node array + elements and clear them, ensuring that any remaining Object + pointers are annulled. */ + } else { + for ( ifr = 1; ifr <= frameset->nframe; ifr++ ) { + this->frame[ this->nframe + ifr - 1 ] = + astAnnul( this->frame[ this->nframe + ifr - 1 ] ); + this->node[ this->nframe + ifr - 1 ] = -1; + this->varfrm[ this->nframe + ifr - 1 ] = 0; + } + for ( inode = 0; inode < frameset->nnode; inode++ ) { + this->map[ this->nnode + inode - 1 ] = + astAnnul( this->map[ this->nnode + inode - 1 ] ); + this->link[ this->nnode + inode - 1 ] = -1; + this->invert[ this->nnode + inode - 1 ] = -1; + } + } + } + } +} + +static void AddVariant( AstFrameSet *this, AstMapping *map, + const char *name, int *status ) { +/* +*++ +* Name: +c astAddVariant +f AST_ADDVARIANT + +* Purpose: +* Store a new variant Mapping for the current Frame in a FrameSet. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astAddVariant( AstFrameSet *this, AstMapping *map, +c const char *name, int *status ) +f CALL AST_ADDVARIANT( THIS, MAP, NAME, STATUS ) + +* Class Membership: +* FrameSet method. + +* Description: +c This function +f This routine +* allows a new variant Mapping to be stored with the current Frame +* in a FrameSet. See the "Variant" attribute for more details. It can +* also be used to rename the currently selected variant Mapping. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FrameSet. +c map +f MAP = INTEGER (Given) +* Pointer to a Mapping which describes how to convert +* coordinates from the current Frame to the new variant of the +* current Frame. If +c NULL +f AST__NULL +* is supplied, then the name associated with the currently selected +* variant of the current Frame is set to the value supplied for +c "name", but no new variant is added. +f NAME, but no new variant is added. +c name +f NAME = CHARACTER * ( * ) (Given) +* The name to associate with the new variant Mapping (or the currently +* selected variant Mapping if +c "map" is NULL). +f MAP is AST__NULL). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The newly added Variant becomes the current variant on exit (this is +* equivalent to setting the Variant attribute to the value supplied for +c "name). +f NAME). +* - An error is reported if a variant with the supplied name already +* exists in the current Frame. +* - An error is reported if the current Frame is a mirror for the +* variant Mappings in another Frame. This is only the case if the +c astMirrorVariants function +f AST_MIRRORVARIANTS routine +* has been called to make the current Frame act as a mirror. + +*-- +*/ + +/* Local Variables: */ + AstCmpMap *map2; + AstFrame *frm; + AstFrame *tfrm; + AstFrame *vfrm; + AstFrameSet *tfs; + AstFrameSet *vfs; + AstMapping *map1; + AstMapping *map3; + char *myname; + const char *dom; + int icur; + int ifrm; + int new; + int nfrm; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the one-based index of the current Frame. */ + icur = astGetCurrent( this ); + +/* Report an error if the current Frame is just a mirror. */ + if( this->varfrm[ icur - 1 ] > 0 && astOK ) { + astError( AST__MIRRO, "astAddVariant(%s): Illegal attempt to " + "add a variant Mapping to a mirror Frame (programming " + "error).", status, astGetClass( this ) ); + } + +/* Get a copy of the supplied string and clean it. */ + myname = astStore( NULL, name, strlen( name ) + 1 ); + astRemoveLeadingBlanks( myname ); + astChrCase( NULL, myname, 1, 0 ); + if( astOK ) { + myname[ astChrLen( myname ) ] = 0; + +/* Get the Variants FrameSet for the current Frame in "this". */ + frm = astGetFrame( this, icur ); + vfs = astGetFrameVariants( frm ); + +/* If current Frame of this has no Variant FrameSet, create a Variants + FrameSet containing a copy of the current Frame (retain its Domain + as the default variant name). */ + if( !vfs ) { + tfrm = astCopy( frm ); + vfs = astFrameSet( tfrm, " ", status ); + tfrm = astAnnul( tfrm ); + new = 1; + } else { + new = 0; + } + +/* Check the Variants FrameSet does not already contain a Frame with + a Domain equal to the supplied name. */ + nfrm = astGetNframe( vfs ); + for( ifrm = 0; ifrm < nfrm && astOK; ifrm++ ) { + vfrm = astGetFrame( vfs, ifrm + 1 ); + dom = astGetDomain( vfrm ); + if( astOK && !strcmp( dom, myname ) ) { + astError( AST__BDVNM, "astAddVariant(%s): Cannot add a " + "variant %s Frame with name '%s' because one " + "already exists in the %s (programming " + "error).", status, astGetClass( this ), + astGetDomain( frm ), myname, astGetClass( this ) ); + } + vfrm = astAnnul( vfrm ); + } + +/* If no Mapping was supplied, just set the name of the currently + selected variant. The names are stored in the Domain attribute of + the Frames in the variants FrameSet, so set teh DOmain for the current + Frame. */ + if( !map ){ + vfrm = astGetFrame( vfs, AST__CURRENT ); + astSetDomain( vfrm, name ); + vfrm = astAnnul( vfrm ); + +/* If a Mapping was supplied.... */ + } else { + +/* Get the Mapping from the current Frame in the variants FrameSet to the + current Frame in "this". Temporarily match the Domains so that + astConvert can work. */ + vfrm = astGetFrame( vfs, AST__CURRENT ); + dom = astGetDomain( frm ); + if( dom ) dom = astStore( NULL, dom, strlen( dom ) + 1 ); + astSetDomain( frm, astGetDomain( vfrm ) ); + tfs = astConvert( vfrm, frm, "" ); + astSetDomain( frm, dom ); + if( tfs ) { + map1 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + tfs = astAnnul( tfs ); + +/* Concatenate it with the supplied Mapping to get the mapping from the + current Frame in the Variants FrameSet to the new variant Frame. */ + map2 = astCmpMap( map1, map, 1, " ", status ); + map3 = astSimplify( map2 ); + +/* Add a copy of parent Frame into Variants FrameSet, using the above + mapping to connect it to the original current Variants Frame. Set + its Domain to the supplied name. Re-instate the original current Frame + afterwards. Remove the variant frame info before adding it. */ + (void) astAnnul( vfrm ); + vfrm = astCopy( frm ); + astSetFrameVariants( vfrm, NULL ); + astSetDomain( vfrm, name ); + icur = astGetCurrent( vfs ); + astAddFrame( vfs, AST__CURRENT, map3, vfrm ); + astSetCurrent( vfs, icur ); + +/* Free resources. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + +/* Report an error if a Mapping cannot be found from the new variant Frame + to the current Frame in "this". */ + } else if( astOK ) { + astError( AST__INTER, "astAddVariant(%s): Cannot convert " + "from a %s with Domain '%s' to a %s with Domain " + "'%s' (internal programming error).", status, + astGetClass( this ), astGetClass( vfrm ), + astGetDomain( vfrm ), astGetClass( frm ), + astGetDomain( frm ) ); + } + +/* Free resources. */ + dom = astFree( (void *) dom ); + vfrm = astAnnul( vfrm ); + } + +/* If all is well, and the Variants FrameSet is new, store a pointer to + it in the current Frame of "this". */ + if( new ) astSetFrameVariants( frm, vfs ); + +/* Make the new Variant the current variant. */ + if( map ) astSetVariant( this, name ); + +/* Free remaining resources. */ + frm = astAnnul( frm ); + vfs = astAnnul( vfs ); + } + myname = astFree( myname ); +} + +static double Angle( AstFrame *this_frame, const double a[], + const double b[], const double c[], int *status ) { +/* +* Name: +* Angle + +* Purpose: +* Calculate the angle subtended by two points at a third point. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double Angle( AstFrame *this, const double a[], const double b[], +* const double c[], int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astAngle +* method inherited from the Frame class). + +* Description: +* This function finds the angle at point B between the line joining points +* A and B, and the line joining points C and B. These lines will in fact be +* geodesic curves appropriate to the Frame in use. For instance, in +* SkyFrame, they will be great circles. + +* Parameters: +* this +* Pointer to the Frame. +* a +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the first point. +* b +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the second point. +* c +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the third point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* astAngle +* The angle in radians, from the line AB to the line CB. If the +* Frame is 2-dimensional, it will be in the range $\pm \pi$, +* and positive rotation is in the same sense as rotation from +* the positive direction of axis 2 to the positive direction of +* axis 1. If the Frame has more than 2 axes, a positive value will +* always be returned in the range zero to $\pi$. + +* Notes: +* - A value of AST__BAD will also be returned if points A and B are +* co-incident, or if points B and C are co-incident. +* - A value of AST__BAD will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astAngle method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astAngle( fr, a, b, c ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static void AppendAxes( AstFrameSet *this, AstFrame *frame, int *status ) { +/* +* Name: +* AppendAxes + +* Purpose: +* Append axes to every Frame in a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void AppendAxes( AstFrameSet *this, AstFrame *frame, int *status ) + +* Class Membership: +* FrameSet member function + +* Description: +* This function replaces every Frame in the FrameSet with a CmpFrame +* holding the original Frame and the supplied Frame. It also replaces +* every Mapping in the FrameSet with a parallel CmpMap holding the +* original Mapping and a UnitMap. The Nin and Nout attributes of every +* UnitMap are equal to the number of axes in the supplied Frame. Each +* CmpMap is simplified before being stored in the FrameSet. + + +* Parameters: +* this +* Pointer to the Frame. +* frame +* Pointer to a Frame holding the new axes to add to every Frame in +* the FrameSet. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpFrame *frm; /* Pointer to new Frame */ + AstCmpMap *map; /* UnitMap to new Mapping */ + AstUnitMap *umap; /* UnitMap to feed the new axes */ + int iframe; /* Frame index */ + int imap; /* Mapping index */ + int inv_orig; /* Original value of Invert attribute */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Loop round every Frame in the FrameSet. */ + for ( iframe = 0; iframe < this->nframe; iframe++ ) { + +/* Create a CmpFrame holding the original Frame and the new Frame. */ + frm = astCmpFrame( this->frame[ iframe ], frame, " ", status ); + +/* Annul the original Frame pointer and store the new CmpFrame pointer. */ + (void) astAnnul( this->frame[ iframe ] ); + this->frame[ iframe ] = (AstFrame *) frm; + } + +/* Create a UnitMap with the number of inputs and outputs equal to the + number of axes in the supplied Frame. */ + umap = astUnitMap( astGetNaxes( frame ), " ", status ); + +/* Loop round every Mapping in the FrameSet. */ + for ( imap = 0; imap < this->nnode - 1; imap++ ) { + +/* The Invert attribute of the Mapping may have been changed via a + different pointer since it was first added into the FrameSet. To + ensure that the FrameSet continues to behave as was originally + intended, we set the Invert attribute back to the value it had when + the Mapping was first added into the FrameSet. First, note the + current value of the Invert flag so that it can be re-instated later. */ + inv_orig = astGetInvert( this->map[ imap ] ); + astSetInvert( this->map[ imap ], this->invert[ imap ] ); + +/* Create a parallel CmpMap holding the original Mapping and the UnitMap. */ + map = astCmpMap( this->map[ imap ], umap, 0, " ", status ); + +/* Re-instate the original value for the Invert flag, and then annul the + original Mapping pointer. */ + astSetInvert( this->map[ imap ], inv_orig ); + (void) astAnnul( this->map[ imap ] ); + +/* Simplify the new Mapping, and store it in the FrameSet. */ + this->map[ imap ] = astSimplify( map ); + +/* Store a copy of the Invert attribute that should be used with this + Mapping within the FrameSet (just in case it is modified via some + excternal reference). */ + this->invert[ imap ] = astGetInvert( this->map[ imap ] ); + +/* Annul the un-simplified Mapping pointer. */ + map = astAnnul( map ); + } + +/* Annul the UnitMap pointer. */ + umap = astAnnul( umap ); +} + +static double AxAngle( AstFrame *this_frame, const double a[], const double b[], int axis, int *status ) { +/* +* Name: +* AxAngle + +* Purpose: +* Returns the angle from an axis, to a line through two points. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double AxAngle( AstFrame *this, const double a[], const double b[], int axis, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astAxAngle +* method inherited from the Frame class). + +* Description: +* This function finds the angle, as seen from point A, between the positive +* direction of a specified axis, and the geodesic curve joining point +* A to point B. + +* Parameters: +* this +* Pointer to the Frame. +* a +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the first point. +* b +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the second point. +* axis +* The number of the Frame axis from which the angle is to be +* measured (one-based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The angle in radians, from the positive direction of the +* specified axis, to the line AB. If the Frame is 2-dimensional, +* it will be in the range $\pm \pi$, and positive rotation is in +* the same sense as rotation from the positive direction of axis 2 +* to the positive direction of axis 1. If the Frame has more than 2 +* axes, a positive value will always be returned in the range zero +* to $\pi$. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the require +* position angle is undefined. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxAngle" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astAxAngle method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astAxAngle( fr, a, b, axis ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static double AxDistance( AstFrame *this_frame, int axis, double v1, double v2, int *status ) { +/* +* Name: +* AxDistance + +* Purpose: +* Find the distance between two axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double AxDistance( AstFrame *this, int axis, double v1, double v2, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astAxDistance +* method inherited from the Frame class). + +* Description: +* This function returns a signed value representing the axis increment +* from axis value v1 to axis value v2. +* +* For a simple Frame, this is a trivial operation returning the +* difference between the two axis values. But for other derived classes +* of Frame (such as a SkyFrame) this is not the case. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +* v1 +* The first axis value. +* v2 +* The second axis value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The distance between the two axis values. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input vaues has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxDistance" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astAxDistance method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astAxDistance( fr, axis, v1, v2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static double AxOffset( AstFrame *this_frame, int axis, double v1, double dist, int *status ) { +/* +* Name: +* AxOffset + +* Purpose: +* Add an increment onto a supplied axis value. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double AxOffset( AstFrame *this, int axis, double v1, double dist, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astAxOffset +* method inherited from the Frame class). + +* Description: +* This function returns an axis value formed by adding a signed axis +* increment onto a supplied axis value. +* +* For a simple Frame, this is a trivial operation returning the +* sum of the two supplied values. But for other derived classes +* of Frame (such as a SkyFrame) this is not the case. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +* v1 +* The original axis value. +* dist +* The axis increment to add to the original axis value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The incremented axis value. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input vaues has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxOffset" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astAxOffset method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astAxOffset( fr, axis, v1, dist ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static AstObject *Cast( AstObject *this_object, AstObject *obj, int *status ) { +/* +* Name: +* Cast + +* Purpose: +* Cast an Object into an instance of a sub-class. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstObject *Cast( AstObject *this, AstObject *obj, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astCast +* method inherited from the Frame class). + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. Specifically, if "this" and "new" are +* of the same class, a copy of "this" is returned. If "this" is an +* instance of a subclass of "obj", then a copy of the component +* of "this" that matches the class of "obj" is returned. Otherwise, +* a NULL pointer is returned without error. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. NULL if "this" is not a sub-class of +* "obj", or if an error occurs. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables; */ + AstFrame *cfrm; + AstObject *new; + astDECLARE_GLOBALS + int generation_gap; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* See how many steps up the class inheritance ladder it is from "obj" + to this class (FrameSet). A positive value is returned if FrameSet + is a sub-class of "obj". A negative value is returned if "obj" is + a sub-class of FrameSet. Zero is returned if "obj" is a FrameSet. + AST__COUSIN is returned if "obj" is not on the same line of descent + as FrameSet. */ + generation_gap = astClassCompare( (AstObjectVtab *) &class_vtab, + astVTAB( obj ) ); + +/* If "obj" is a FrameSet or a sub-class of FrameSet, we can cast by + truncating the vtab for "this" so that it matches the vtab of "obJ", + and then taking a deep copy of "this". */ + if( generation_gap <= 0 && generation_gap != AST__COUSIN ) { + new = astCastCopy( this_object, obj ); + +/* If "obj" is not a FrameSet or a sub-class of FrameSet (e.g. a Frame or + some sub-class of Frame), we attempt to cast the current Frame into + the class indicated by "obj". */ + } else { + cfrm = astGetFrame( (AstFrameSet *) this_object, AST__CURRENT ); + new = astCast( cfrm, obj ); + cfrm = astAnnul( cfrm ); + } + +/* Return the new pointer. */ + return new; +} + +static double Centre( AstFrame *this_frame, int axis, double value, double gap, int *status ) { +/* +* Name: +* Centre + +* Purpose: +* Find a "nice" central value for tabulating Frame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double Centre( AstFrame *this_frame, int axis, double value, +* double gap, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astCentre method +* inherited from the Frame class). + +* Description: +* This function returns an axis value which produces a nice formatted +* value suitable for a major tick mark on a plot axis, close to the +* supplied axis value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which a central value +* is to be found. +* value +* An arbitrary axis value in the section that is being plotted. +* gap +* The gap size. + +* Returned Value: +* The nice central axis value. + +* Notes: +* - A value of zero is returned if the supplied gap size is zero. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astCentre" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astCentre method to obtain the required value. Annul the + Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astCentre( fr, axis, value, gap ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static void CheckPerm( AstFrame *this_frame, const int *perm, const char *method, int *status ) { +/* +* Name: +* CheckPerm + +* Purpose: +* Check that an array contains a valid permutation. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void CheckPerm( AstFrame *this, const int *perm, const char *method, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astCheckPerm +* method inherited from the Frame class). + +* Description: +* This function checks the validity of a permutation array that +* will be used to permute the order of a Frame's axes. If the +* permutation specified by the array is not valid, an error is +* reported and the global error status is set. Otherwise, the +* function returns without further action. + +* Parameters: +* this +* Pointer to the Frame. +* perm +* Pointer to an array of integers with the same number of +* elements as there are axes in the Frame. For each axis, the +* corresponding integer gives the (zero based) axis index to be +* used to identify the information for that axis (using the +* un-permuted axis numbering). To be valid, the integers in +* this array should therefore all lie in the range zero to +* (naxes-1) inclusive, where "naxes" is the number of Frame +* axes, and each value should occur exactly once. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate a permutation array. This method name is used +* solely for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Error messages issued by this function refer to the external +* (public) numbering system used for axes (which is one-based), +* whereas zero-based axis indices are used internally. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astCheckPerm method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astCheckPerm( fr, perm, method ); + fr = astAnnul( fr ); + +} + +static void Clear( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* Clear + +* Purpose: +* Clear attribute values for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void Clear( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the public astClear method +* inherited from the Object class). + +* Description: +* This function clears the values of a specified set of attributes +* for a FrameSet. Clearing an attribute cancels any value that has +* previously been explicitly set for it, so that the standard +* default attribute value will subsequently be used instead. This +* also causes the astTest function to return the value zero for +* the attribute, indicating that no value has been set. + +* Parameters: +* this +* Pointer to the FrameSet. +* attrib +* Pointer to a null-terminated character string containing a +* comma-separated list of the names of the attributes to be +* cleared. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function preserves the integrity of the FrameSet (if +* possible) by appropriately remapping its current Frame to take +* account of its changed attribute values. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *save_frame; /* Saved pointer to integrity Frame */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + const char *save_method; /* Saved pointer to method name */ + int ok; /* Status OK? */ + int save_lost; /* Saved integrity modified flag */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* To allow this function to be invoked recursively, we first save any + existing FrameSet integrity information in local variables. */ + save_frame = integrity_frame; + save_lost= integrity_lost; + save_method = integrity_method; + +/* Set the name of the method being used (for use in error + messages). */ + integrity_method = "astClear"; + +/* Record the initial integrity state of the FrameSet. */ + RecordIntegrity( this, status ); + +/* Invoke the parent astClear method to clear the FrameSet's attribute + values and note if this succeeds. */ + (*parent_clear)( this_object, attrib, status ); + ok = astOK; + +/* Restore the FrameSet's integrity. */ + RestoreIntegrity( this, status ); + +/* If integrity could not be restored, then add contextual error + information. */ + if ( !astOK && ok ) { + astError( astStatus, "Unable to accommodate clearing the \"%s\" " + "attribute(s).", status, attrib ); + } + +/* Restore any saved FrameSet integrity information. */ + integrity_frame = save_frame; + integrity_lost = save_lost; + integrity_method = save_method; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void ClearAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* FrameSet member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* FrameSet, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the FrameSet. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* We first handle attributes that apply to the FrameSet as a whole + (rather than to the current Frame). */ + +/* Base. */ +/* ----- */ + if ( !strcmp( attrib, "base" ) ) { + astClearBase( this ); + +/* Current. */ +/* -------- */ +/* Since this determines the choice of current Frame, we must restore + the integrity state of the FrameSet before changing this attribute + and record the new integrity state afterwards. */ + } else if ( !strcmp( attrib, "current" ) ) { + RestoreIntegrity( this, status ); + astClearCurrent( this ); + RecordIntegrity( this, status ); + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + astClearID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + astClearIdent( this ); + +/* Invert. */ +/* ------- */ +/* Since this affects the choice of current Frame, we must restore the + integrity state of the FrameSet before changing this attribute and + record the new integrity state afterwards. */ + } else if ( !strcmp( attrib, "invert" ) ) { + RestoreIntegrity( this, status ); + astClearInvert( this ); + RecordIntegrity( this, status ); + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + astClearReport( this ); + +/* Variant. */ +/* -------- */ + } else if ( !strcmp( attrib, "variant" ) ) { + astClearVariant( this ); + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class. If it does, then report an + error. */ + } else if ( !strcmp( attrib, "allvariants" ) || + !strcmp( attrib, "class" ) || + !strcmp( attrib, "nframe" ) || + !strcmp( attrib, "nin" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "nout" ) || + !strcmp( attrib, "refcount" ) || + !strcmp( attrib, "tranforward" ) || + !strcmp( attrib, "traninverse" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass unrecognised attributes on to the FrameSet's current Frame for + further interpretation. */ + } else { + +/* Force a copy to be made of the current Frame, if needed, to make it + independent of other Frames within the FrameSet. */ + (void) ForceCopy( this, AST__CURRENT, status ); + +/* Obtain a pointer to the current Frame and invoke its astClearAttrib + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astClearAttrib( fr, attrib ); + fr = astAnnul( fr ); + +/* Note that the current Frame has been modified. */ + integrity_lost = 1; + } +} + +static void ClearBase( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astClearBase + +* Purpose: +* Clear the value of the Base attribute of a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* void astClearBase( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function clears the value of the Base attribute of a +* FrameSet. This value is an index that identifies the base Frame +* for the FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, clear the base Frame index, otherwise + clear the current Frame index instead. */ + if ( astOK ) *( invert ? &this->current : &this->base ) = -INT_MAX; +} + +static void ClearCurrent( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astClearCurrent + +* Purpose: +* Clear the value of the Current attribute for a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* int astClearCurrent( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function clears the value of the Current attribute for a +* FrameSet. This attribute is an index that identifies the current +* Frame for the FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, clear the current frame index, + otherwise clear the base Frame index instead. */ + if ( astOK ) *( invert ? &this->base : &this->current ) = -INT_MAX; +} + +static void ClearVariant( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astClearVariant + +* Purpose: +* Clear the value of the Variant attribute of a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* void astClearVariant( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function clears the value of the Variant attribute of a +* FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. +*- +*/ + +/* Local Variables: */ + AstFrame *frm; + int icur; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the one-based index of the Frame to use. */ + icur = GetVarFrm( this, astGetCurrent( this ), status ); + +/* Get a pointer to the current Frame in the FrameSet. */ + frm = astGetFrame( this, icur ); + +/* Replace any Variants FrameSet in the Frame with a NULL pointer. */ + astSetFrameVariants( frm, NULL ); + +/* Annul the current Frame pointer. */ + frm = astAnnul( frm ); + +} + +static AstMapping *CombineMaps( AstMapping *mapping1, int invert1, + AstMapping *mapping2, int invert2, + int series, int *status ) { +/* +* Name: +* CombineMaps + +* Purpose: +* Combine two Mappings with specified Invert flags into a CmpMap. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstMapping *CombineMaps( AstMapping *mapping1, int invert1, +* AstMapping *mapping2, int invert2, +* int series ) + +* Class Membership: +* FrameSet member function. + +* Description: +* This function combines two Mappings into a CmpMap (compound +* Mapping) as if their Invert flags were set to specified values +* when the CmpMap is created. However, the individual Mappings are +* returned with their Invert flag values unchanged from their +* original state. + +* Parameters: +* mapping1 +* Pointer to the first Mapping. +* invert1 +* The (boolean) Invert flag value required for the first Mapping. +* mapping2 +* Pointer to the second Mapping. +* invert2 +* The (boolean) Invert flag value required for the second Mapping. +* series +* Whether the Mappings are to be combined in series (as opposed to +* in parallel). + +* Returned Value: +* A pointer to the resulting compound Mapping (a CmpMap). + +* Notes: +* - This function is a wrap-up for the astCmpMap constructor and +* temporarily assigns the required Invert flag values while +* creating the required CmpMap. However, it also takes account of +* the possibility that the two Mapping pointers supplied may point +* at the same Mapping. +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *map1; /* First temporary Mapping pointer */ + AstMapping *map2; /* Second temporary Mapping pointer */ + AstMapping *result; /* Pointer to result Mapping */ + int copy; /* Copy needed? */ + int inv1; /* First original Invert flag value */ + int inv2; /* Second original Invert flag value */ + int set1; /* First Invert flag originally set? */ + int set2; /* Second Invert flag originally set? */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Limit incoming values to 0 or 1. */ + invert1 = ( invert1 != 0 ); + invert2 = ( invert2 != 0 ); + +/* Obtain the Invert flag values for each Mapping. */ + inv1 = astGetInvert( mapping1 ); + inv2 = astGetInvert( mapping2 ); + +/* Also determine if these values are explicitly set. */ + set1 = astTestInvert( mapping1 ); + set2 = astTestInvert( mapping2 ); + +/* If both Mappings are actually the same but we need different Invert + flag values to be set, then this can only be achieved by making a + copy. Note if this is necessary. */ + copy = ( ( mapping1 == mapping2 ) && ( invert1 != invert2 ) ); + +/* Clone the first Mapping pointer. Do likewise for the second but + make a copy instead if necessary. */ + map1 = astClone( mapping1 ); + map2 = copy ? astCopy( mapping2 ) : astClone( mapping2 ); + +/* If the Invert value for the first Mapping needs changing, make the + change. */ + if ( invert1 != inv1 ) { + if ( invert1 ) { + astSetInvert( map1, 1 ); + } else { + astClearInvert( map1 ); + } + } + +/* Similarly, change the Invert flag for the second Mapping if + necessary. */ + if ( invert2 != inv2 ) { + if ( invert2 ) { + astSetInvert( map2, 1 ); + } else { + astClearInvert( map2 ); + } + } + +/* Combine the two Mappings into a CmpMap. */ + result = (AstMapping *) astCmpMap( map1, map2, series, "", status ); + +/* If the first Mapping's Invert value was changed, restore it to its + original state. */ + if ( invert1 != inv1 ) { + if ( set1 ) { + astSetInvert( map1, inv1 ); + } else { + astClearInvert( map1 ); + } + } + +/* Similarly, restore the second Mapping's Invert value if + necessary. This step is not needed, however, if a copy was made. */ + if ( ( invert2 != inv2 ) && !copy ) { + if ( set2 ) { + astSetInvert( map2, inv2 ); + } else { + astClearInvert( map2 ); + } + } + +/* Annul the temporary Mapping pointers. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + +/* If an error occurred, then annul the result pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstFrameSet *Convert( AstFrame *from, AstFrame *to, + const char *domainlist, int *status ) { +/* +* Name: +* Convert + +* Purpose: +* Determine how to convert between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstFrameSet *Convert( AstFrame *from, AstFrame *to, +* const char *domainlist, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the public astConvert +* method inherited fromm the Frame class). + +* Description: +* This function compares two FrameSets and determines whether it +* is possible to convert between the coordinate systems which +* their current Frames represent. If conversion is possible, it +* returns a FrameSet which describes the conversion and which may +* be used (as a Mapping) to transform coordinate values in either +* direction. +* +* If conversion is possible, the Base attributes of both FrameSets +* will be modified on exit to identify the Frames which were used +* as the intermediate coordinate system. + +* Parameters: +* from +* Pointer to a FrameSet whose current Frame represents the +* "source" coordinate system. Note that the Base attribute of +* the FrameSet may be modified by this function. +* to +* Pointer to a FrameSet whose current Frame represents the +* "destination" coordinate system. Note that the Base +* attribute of the FrameSet may be modified by this function. +* domainlist +* Pointer to a null-terminated character string containing a +* comma-separated list of Frame domains. This may be used to +* define a priority order for the different intermediate +* coordinate systems that might be used to perform the +* conversion. +* +* The function will first try to obtain a conversion by making +* use only of intermediate Frames whose Domain attribute +* matches the first domain in this list. If this fails, the +* second domain in the list will be used, and so on, until +* conversion is achieved. A blank domain (e.g. two consecutive +* commas) indicates that all Frames should be considered, +* regardless of their Domain attributes. The list is +* case-insensitive and all white space is ignored. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the requested coordinate conversion is possible, the +* function returns a pointer to a FrameSet which describes the +* conversion. Otherwise, a null Object pointer (AST__NULL) is +* returned without error. +* +* If a FrameSet is returned, it will contain two Frames. Frame +* number 1 (its base Frame) will describe the source coordinate +* system, corresponding to the "from" parameter. Frame number 2 +* (its current Frame) will describe the destination coordinate +* system, corresponding to the "to" parameter. The Mapping +* which inter-relates these Frames will perform the required +* conversion between the two coordinate systems. + +* Notes: +* - Either of the "from" and "to" pointers may identify a basic +* Frame instead of a FrameSet, in which case the function behaves +* as if it were a FrameSet containing only a single Frame. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Implementation Notes: +* - This function is simply a wrap-up for the ConvertX function +* which performs the required processing but swaps the order of the +* first two arguments. This is a trick to allow the astConvert +* method to be over-ridden by derived classes on the basis of the +* class of either of the first two arguments. +*/ + +/* Check the inherited status. */ + if ( !astOK ) return NULL; + +/* Invoke the private "ConvertX" member function with the first two + arguments swapped. */ + return ConvertX( to, from, domainlist, status ); +} + +static AstFrameSet *ConvertX( AstFrame *to, AstFrame *from, + const char *domainlist, int *status ) { +/* +* Name: +* ConvertX + +* Purpose: +* Determine how to convert between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstFrameSet *ConvertX( AstFrame *to, AstFrame *from, +* const char *domainlist ) + +* Class Membership: +* FrameSet member function (over-rides the protected "astConvertX" +* method inherited from the Frame class). + +* Description: +* This function performs the processing for the public astConvert +* method (as inherited from the Frame class and over-ridden by the +* FrameSet class) and has exactly the same interface except that +* the order of the first two arguments is swapped. This is a trick +* to allow the astConvert method to be over-ridden by derived +* classes on the basis of the class of either of its first two +* arguments. +* +* See the astConvert method for details of the interface. +*/ + +/* Local Variables: */ + AstFrame *from_frame; /* Pointer to "from" Frame */ + AstFrame *to_frame; /* Pointer to "to" Frame */ + AstFrameSet *cvt; /* Pointer to conversion FrameSet */ + AstFrameSet *result; /* Pointer to FrameSet to be returned */ + AstMapping *from_map; /* Pointer to "from" Mapping */ + AstMapping *map; /* Pointer to conversion Mapping */ + AstMapping *result_map; /* Pointer to result Mapping */ + AstMapping *tmp; /* Temporary Mapping pointer */ + AstMapping *to_map; /* Pointer to "to" Mapping */ + char *domain; /* Pointer to individual domain string */ + char *domain_end; /* Pointer to final null of domain string */ + char *domainlist_copy; /* Pointer to copy of domains list */ + int *from_order; /* List of Frame indices in search order */ + int *to_order; /* List of Frame indices in search order */ + int best_score; /* Score from best match */ + int from_base; /* Index of "from" base Frame */ + int from_current; /* Index of "from" current Frame */ + int from_index; /* Index of "from" Frame */ + int from_isframe; /* "from" is a Frame (not a FrameSet)? */ + int from_nframe; /* Number of "from" Frames */ + int from_number; /* Loop counter for "from" Frames */ + int iframe_from; /* Index of best "from" Frame */ + int iframe_to; /* Index of best "to" Frame */ + int match; /* Possible match found? */ + int n; /* Count of Frames */ + int perfect; /* Perfect match found? */ + int score; /* Score from latest match */ + int to_base; /* Index of "to" base Frame */ + int to_current; /* Index of "to" current Frame */ + int to_index; /* Index of "to" Frame */ + int to_isframe; /* "to" is a Frame (not a FrameSet)? */ + int to_nframe; /* Number of "to" Frames */ + int to_number; /* Loop counter for "to" Frames */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + result_map = NULL; + iframe_from = 0; + iframe_to = 0; + +/* Determine the number of Frames in "from" and the indices of its + base and current Frames. Use values of 1 if "from" is a Frame and + not a FrameSet. */ + from_isframe = !astIsAFrameSet( from ); + from_nframe = from_isframe ? 1 : astGetNframe( from ); + from_base = from_isframe ? 1 : astGetBase( from ); + from_current = from_isframe ? 1 : astGetCurrent( from ); + +/* Obtain similar values for "to". */ + to_isframe = !astIsAFrameSet( to ); + to_nframe = to_isframe ? 1 : astGetNframe( to ); + to_base = to_isframe ? 1 : astGetBase( to ); + to_current = to_isframe ? 1 : astGetCurrent( to ); + +/* Allocate memory for arrays which will hold the indices of "from" + and "to" Frames. */ + from_order = astMalloc( sizeof( int ) * (size_t) from_nframe ); + to_order = astMalloc( sizeof( int ) * (size_t) to_nframe ); + +/* Make a temporary copy of the domains list. */ + domainlist_copy = astStore( NULL, domainlist, + strlen( domainlist ) + (size_t) 1 ); + if ( astOK ) { + +/* Fill the "from_order" array with the indices of all the Frames in + "from", in the order in which they will be used for searching. Use + the base Frame first. */ + n = 0; + from_order[ n++ ] = from_base; + +/* Then add all the "from" Frames in the appropriate order, omitting + the base and current Frames. */ + if ( !astGetInvert( from ) ) { + for ( from_index = 1; from_index <= from_nframe; from_index++ ) { + if ( ( from_index != from_base ) && + ( from_index != from_current ) ) { + from_order[ n++ ] = from_index; + } + } + } else { + for ( from_index = from_nframe; from_index >= 1; from_index-- ) { + if ( ( from_index != from_base ) && + ( from_index != from_current ) ) { + from_order[ n++ ] = from_index; + } + } + } + +/* Finish with the current Frame, if different from the base Frame. */ + if ( from_current != from_base ) from_order[ n++ ] = from_current; + +/* Repeat this process for the "to" Frame. */ + n = 0; + to_order[ n++ ] = to_base; + if ( !astGetInvert( to ) ) { + for ( to_index = 1; to_index <= to_nframe; to_index++ ) { + if ( ( to_index != to_base ) && ( to_index != to_current ) ) { + to_order[ n++ ] = to_index; + } + } + } else { + for ( to_index = to_nframe; to_index >= 1; to_index-- ) { + if ( ( to_index != to_base ) && ( to_index != to_current ) ) { + to_order[ n++ ] = to_index; + } + } + } + if ( to_current != to_base ) to_order[ n++ ] = to_current; + +/* Loop to inspect each comma-separated field in the domains list + until an error occurs, all the domains are used up, or a match is + found. */ + domain = domainlist_copy; + match = 0; + while ( astOK && domain && !match ) { + +/* Change the comma at the end of each field to a null to terminate + the domain. */ + if ( ( domain_end = strchr( domain, ',' ) ) ) *domain_end = '\0'; + +/* For any given domain, we will ignore imperfect matches in favour of + better ones by assigning a score to each match. Initialise the best + score value for the current domain. */ + best_score = -1; + +/* Loop through each Frame in "to". Quit looping early if an error + occurs or a perfect match is found. */ + perfect = 0; + for ( to_number = 0; + astOK && !perfect && ( to_number < to_nframe ); + to_number++ ) { + +/* Permute the "to" Frame number into a Frame index to implement the + required search order, and obtain a pointer to the required "to" + Frame. */ + to_index = to_order[ to_number ]; + to_frame = to_isframe ? astClone( to ) : + astGetFrame( to, to_index ); + +/* Loop through each Frame in "from". Quit looping early if an error + occurs or a perfect match is found. */ + for ( from_number = 0; + astOK && !perfect && ( from_number < from_nframe ); + from_number++ ) { + +/* Permute the "from" Frame number into a Frame index to implement the + required search order, and obtain a pointer to the required "from" + Frame. */ + from_index = from_order[ from_number ]; + from_frame = from_isframe ? astClone( from ) : + astGetFrame( from, from_index ); + +/* Attempt to obtain a FrameSet which describes the conversion between + the selected "from" and "to" Frames and test if successful. If so, + we have a potential route to construct the overall Mapping we + want. */ + cvt = astConvert( from_frame, to_frame, domain ); + if ( astOK && cvt ) { + +/* Extract the required Mapping from the returned FrameSet. */ + map = astGetMapping( cvt, AST__BASE, AST__CURRENT ); + +/* If necessary, prefix the Mapping between the "from" current Frame + and the individual "from" Frame we have selected. */ + if ( from_index != from_current ) { + from_map = astGetMapping( from, AST__CURRENT, + from_index ); + tmp = (AstMapping *) astCmpMap( from_map, map, 1, "", status ); + from_map = astAnnul( from_map ); + map = astAnnul( map ); + map = tmp; + } + +/* Similarly, if necessary, append the Mapping between the selected + "to" Frame and the "to" current Frame. */ + if ( to_index != to_current ) { + to_map = astGetMapping( to, to_index, AST__CURRENT ); + tmp = (AstMapping *) astCmpMap( map, to_map, 1, "", status ); + to_map = astAnnul( to_map ); + map = astAnnul( map ); + map = tmp; + } + +/* Simplify the resulting overall Mapping (this is done here because + it may sometimes affect the attribute values used to assign a score + below). */ + tmp = astSimplify( map ); + map = astAnnul( map ); + map = tmp; + +/* Assign a score that favours Mappings with both transformations + available over those with only one, and Mappings with only a + forward transformation over those with only an inverse + transformation. */ + score = ( astGetTranForward( map ) ? 2 : 0 ) + + ( astGetTranInverse( map ) ? 1 : 0 ); + +/* If the new score is better than the previous one (or is the first + one), note that we have a possible match. */ + if ( astOK && ( score > best_score ) ) { + match = 1; + +/* Update the best score and note if it indicates a perfect match (in + which case we can stop searching at this point). */ + best_score = score; + perfect = ( best_score >= 3 ); + +/* Annul any previous result Mapping pointer and replace it with this + better one. */ + if ( result_map ) result_map = astAnnul( result_map ); + result_map = astClone( map ); + +/* Note which "from" and "to" Frames were used. */ + iframe_from = from_index; + iframe_to = to_index; + } + +/* Annul pointers to the intermediate Objects. */ + map = astAnnul( map ); + cvt = astAnnul( cvt ); + } + from_frame = astAnnul( from_frame ); + } + to_frame = astAnnul( to_frame ); + } + +/* Go on to consider the next field in the domains list. */ + domain = domain_end ? domain_end + 1 : NULL; + } + } + +/* Free the memory allocated for temporary arrays. */ + domainlist_copy = astFree( domainlist_copy ); + from_order = astFree( from_order ); + to_order = astFree( to_order ); + +/* If a result is being returned, then obtain a pointer to the current + "from" Frame and use it to start constructing the result + FrameSet. */ + if ( result_map ) { + from_frame = from_isframe ? astClone( from ) : + astGetFrame( from, AST__CURRENT ); + result = astFrameSet( from_frame, "", status ); + from_frame = astAnnul( from_frame ); + +/* Similarly. obtain a pointer to the current "to" frame and add it to + the result FrameSet (related to the base Frame by the result + Mapping). */ + to_frame = to_isframe ? astClone( to ) : + astGetFrame( to, AST__CURRENT ); + astAddFrame( result, AST__BASE, result_map, to_frame ); + to_frame = astAnnul( to_frame ); + +/* Annul the result Mapping pointer. */ + result_map = astAnnul( result_map ); + } + +/* If successful, and a FrameSet is being returned, then set the base + Frames of "from" and "to" (if they are FrameSets) to indicate the + route used to generate the result Mapping. */ + if ( astOK && result ) { + if ( !from_isframe ) astSetBase( from, iframe_from ); + if ( !to_isframe ) astSetBase( to, iframe_to ); + } + +/* If an error occurred, annul the returned FrameSet pointer. */ + if ( !astOK && result ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static double Distance( AstFrame *this_frame, + const double point1[], const double point2[], int *status ) { +/* +* Name: +* Distance + +* Purpose: +* Calculate the distance between two points. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double Distance( AstFrame *this, +* const double point1[], const double point2[], int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astDistance +* method inherited from the Frame class). + +* Description: +* This function finds the distance between two points whose +* FrameSet coordinates are given. The distance calculated is that +* along the geodesic curve that joins the two points. + +* Parameters: +* this +* Pointer to the FrameSet. +* point1 +* An array of double, with one element for each FrameSet axis +* containing the coordinates of the first point. +* point2 +* An array of double, with one element for each FrameSet axis +* containing the coordinates of the second point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The distance between the two points. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astDistance method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astDistance( fr, point1, point2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two FrameSets are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astEqual protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two FrameSets are equivalent. + +* Parameters: +* this +* Pointer to the first FrameSet. +* that +* Pointer to the second FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the FrameSets are equivalent, zero otherwise. + +* Notes: +* - The two FrameSets are considered equivalent if all the encapsulated +* Frames are equal and all the encapsulated Mappings are equal. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrameSet *that; /* Pointer to the second FrameSet structure */ + AstFrameSet *this; /* Pointer to the first FrameSet structure */ + int i; /* Loop index */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Checks that the second object is of the same class as the first . */ + if( !strcmp( astGetClass( this_object ), astGetClass( that_object ) ) ){ + +/* Obtain pointers to the two FrameSet structures. */ + this = (AstFrameSet *) this_object; + that = (AstFrameSet *) that_object; + +/* Check the number of nodes and frames are equal. Also check the indices + of the base and current Frames are equal */ + if( this->nframe == that->nframe && + this->nnode == that->nnode && + this->base == that->base && + this->current == that->current ) { + +/* Check the Frames and nodes are equal. */ + result = 1; + for ( i = 0; i < this->nframe; i++ ) { + if( !astEqual( this->frame[ i ], that->frame[ i ] ) || + this->node[ i ] != that->node[ i ] ){ + result = 0; + break; + } + } + +/* Check the Mappings, links and invert flags are equal. */ + if( result ) { + for ( i = 0; i < this->nnode - 1; i++ ) { + if( !astEqual( this->map[ i ], that->map[ i ] ) || + this->link[ i ] != that->link[ i ] || + this->invert[ i ] != that->invert[ i ] ) { + result = 0; + break; + } + } + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int Fields( AstFrame *this_frame, int axis, const char *fmt, + const char *str, int maxfld, char **fields, + int *nc, double *val, int *status ) { +/* +*+ +* Name: +* astFields + +* Purpose: +* Identify numerical fields within a formatted FrameSet axis value. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astFields( AstFrame *this, int axis, const char *fmt, +* const char *str, int maxfld, char **fields, +* int *nc, double *val ) + +* Class Membership: +* FrameSet member function (over-rides the protected astFields +* method inherited from the Frame class). + +* Description: +* This function identifies the numerical fields within a FrameSet axis +* value that has been formatted using astAxisFormat. It assumes that +* the value was formatted using the supplied format string. It also +* returns the equivalent floating point value. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The number of the FrameSet axis for which the values have been +* formatted (axis numbering starts at zero for the first axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format used when creating "str". +* str +* Pointer to a constant null-terminated string containing the +* formatted value. +* maxfld +* The maximum number of fields to identify within "str". +* fields +* A pointer to an array of at least "maxfld" character pointers. +* Each element is returned holding a pointer to the start of the +* corresponding field in "str" (in the order in which they occur +* within "str"), or NULL if no corresponding field can be found. +* nc +* A pointer to an array of at least "maxfld" integers. Each +* element is returned holding the number of characters in the +* corresponding field, or zero if no corresponding field can be +* found. +* val +* Pointer to a location at which to store the value +* equivalent to the returned field values. If this is NULL, +* it is ignored. + +* Returned Value: +* The number of fields succesfully identified and returned. + +* Notes: +* - Leading and trailing spaces are ignored. +* - If the formatted value is not consistent with the supplied format +* string, then a value of zero will be returned, "fields" will be +* returned holding NULLs, "nc" will be returned holding zeros, and +* "val" is returned holding VAL__BAD. +* - Fields are counted from the start of the formatted string. If the +* string contains more than "maxfld" fields, then trailing fields are +* ignored. +* - If this function is invoked with the global error status set, or +* if it should fail for any reason, then a value of zero will be returned +* as the function value, and "fields", "nc" and "val" will be returned +* holding their supplied values +*- +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int result; /* Result field count to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astFields" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astFields method to perform the processing. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astFields( fr, axis, fmt, str, maxfld, fields, nc, val ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static AstFrameSet *FindFrame( AstFrame *target_frame, AstFrame *template, + const char *domainlist, int *status ) { +/* +* Name: +* FindFrame + +* Purpose: +* Find a coordinate system with specified characteristics. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstFrameSet *FindFrame( AstFrame *target, AstFrame *template, +* const char *domainlist, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astFindFrame method +* inherited from the Frame class). + +* Description: +* This function uses a "template" Frame to search a FrameSet to +* identify a coordinate system which has a specified set of +* characteristics. If a suitable coordinate system can be found, +* the function returns a pointer to a FrameSet which describes the +* required coordinate system and how to convert coordinates to and +* from it. + +* Parameters: +* target +* Pointer to the target FrameSet. Note that if a suitable +* coordinate system is found, then the FrameSet's Current +* attribute will be modified to indicate which Frame was used +* to obtain attribute values which were not specified by the +* template. +* template +* Pointer to the template Frame, which should be an instance of +* the type of Frame you wish to find. +* domainlist +* Pointer to a null-terminated character string containing a +* comma-separated list of Frame domains. This may be used to +* establish a priority order for the different types of +* coordinate system that might be found. +* +* The function will first try to find a suitable coordinate +* system whose Domain attribute equals the first domain in this +* list. If this fails, the second domain in the list will be +* used, and so on, until a result is obtained. A blank domain +* (e.g. two consecutive commas) indicates that any coordinate +* system is acceptable (subject to the template) regardless of +* its domain. +* +* This list is case-insensitive and all white space is ignored. +* If you do not wish to restrict the domain in this way, you +* should supply an empty string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the search is successful, the function returns a pointer to a +* FrameSet which contains the Frame found and a description of how +* to convert to (and from) the coordinate system it +* represents. Otherwise, a null Object pointer (AST__NULL) is +* returned without error. +* +* If a FrameSet is returned, it will contain two Frames. Frame +* number 1 (its base Frame) represents the target coordinate +* system and will be the same as the (base Frame of the) +* target. Frame number 2 (its current Frame) will be a Frame +* representing the coordinate system which the function found. The +* Mapping which inter-relates these two Frames will describe how +* to convert between their respective coordinate systems. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *base_frame; /* Pointer to target base Frame */ + AstFrame *frame; /* Pointer to result Frame */ + AstFrame *selected_frame; /* Pointer to selected target Frame */ + AstFrameSet *found; /* FrameSet pointer (result of search) */ + AstFrameSet *result; /* Pointer to result FrameSet */ + AstFrameSet *target; /* Pointer to target FrameSet structure */ + AstMapping *map; /* Pointer to result Mapping */ + AstMapping *prefix; /* Pointer to prefix Mapping */ + AstMapping *tmp; /* Temporary Mapping pointer */ + char *domain; /* Pointer to individual domain field */ + char *domain_end; /* Pointer to null at end of domain */ + char *domainlist_copy; /* Pointer to copy of domains list */ + int *target_order; /* Array of indices defining search order */ + int match; /* Match obtained? */ + int n; /* Count of target_order elements */ + int target_base; /* Index of target base Frame */ + int target_current; /* Index of target current Frame */ + int target_index; /* Index of selected target Frame */ + int target_nframe; /* Number of Frames in target FrameSet */ + int target_number; /* Loop index for search */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + target_index = 0; + +/* Obtain a pointer to the target FrameSet structure. */ + target = (AstFrameSet *) target_frame; + +/* Determine the number of Frames in the target FrameSet and the + indices of the current and base Frames. */ + target_nframe = astGetNframe( target ); + target_current = astGetCurrent( target ); + target_base = astGetBase( target ); + +/* Allocate an array to hold a list of all the target Frame indices. */ + target_order = astMalloc( sizeof( int ) * (size_t) target_nframe ); + +/* Make a temporary copy of the domains list. */ + domainlist_copy = astStore( NULL, domainlist, + strlen( domainlist ) + (size_t) 1 ); + if ( astOK ) { + +/* Form a list of the indices of all the Frames in the target in the + order they will be searched for a match. Add the current Frame + index first. */ + n = 0; + target_order[ n++ ] = target_current; + +/* Follow this by the base Frame index, if different. */ + if ( target_base != target_current ) target_order[ n++ ] = target_base; + +/* Then add all the remaining target Frame indices. */ + for ( target_index = 1; target_index <= target_nframe; target_index++ ) { + if ( ( target_index != target_current ) && + ( target_index != target_base ) ) { + target_order[ n++ ] = target_index; + } + } + +/* Loop to inspect each comma-separated field in the domains list + until an error occurs, all the domains are used up, or a match is + found. */ + domain = domainlist_copy; + match = 0; + while ( astOK && domain && !match ) { + +/* Change the comma at the end of each field to a null to terminate + the domain. */ + if ( ( domain_end = strchr( domain, ',' ) ) ) *domain_end = '\0'; + +/* Loop to try and match each target Frame in turn, in the order + identified above. Quit the loop early if an error occurs or a match + is found. */ + for ( target_number = 0; + astOK && !match && ( target_number < target_nframe ); + target_number++ ) { + +/* Permute the target Frame number into a Frame index to implement the + required search order. Then obtain a pointer to the selected target + Frame. */ + target_index = target_order[ target_number ]; + selected_frame = astGetFrame( target, target_index ); + +/* Search the target Frame using the template supplied, together with + the current domain. */ + found = astFindFrame( selected_frame, template, domain ); + +/* Note if a match is found, and extract pointers to the conversion + Mapping and the result Frame from the FrameSet produced. */ + if ( astOK && found ) { + match = 1; + map = astGetMapping( found, AST__BASE, AST__CURRENT ); + frame = astGetFrame( found, AST__CURRENT ); + +/* Obtain a pointer to the Mapping between the target base Frame and + the selected target Frame, and prefix this Mapping to the one + obtained above. */ + prefix = astGetMapping( target, AST__BASE, target_index ); + tmp = (AstMapping *) astCmpMap( prefix, map, 1, "", status ); + prefix = astAnnul( prefix ); + map = astAnnul( map ); + map = tmp; + +/* Simplify the resulting Mapping. */ + tmp = astSimplify( map ); + map = astAnnul( map ); + map = tmp; + +/* Obtain a pointer to the target base Frame, and use this to start + building the result FrameSet. */ + base_frame = astGetFrame( target, AST__BASE ); + result = astFrameSet( base_frame, "", status ); + base_frame = astAnnul( base_frame ); + +/* Add the result Frame, which is related to the base Frame by the + result Mapping. */ + astAddFrame( result, AST__BASE, map, frame ); + +/* Annul pointers to all intermediate Objects. */ + map = astAnnul( map ); + frame = astAnnul( frame ); + found = astAnnul( found ); + } + selected_frame = astAnnul( selected_frame ); + } + +/* Go on to consider the next field in the domains list. */ + domain = domain_end ? domain_end + 1 : NULL; + } + } + +/* Free the temporary arrays. */ + target_order = astFree( target_order ); + domainlist_copy = astFree( domainlist_copy ); + +/* If a result is being returned, set the current Frame of the target + to indicate where the result Frame was found. */ + if ( astOK && result ) astSetCurrent( target, target_index ); + +/* If an error occurred, annul any result FrameSet pointer. */ + if ( !astOK && result ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *FrameGrid( AstFrame *this_frame, int size, const double *lbnd, + const double *ubnd, int *status ){ +/* +* Name: +* FrameGrid + +* Purpose: +* Return a grid of points covering a rectangular area of a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstPointSet *FrameGrid( AstFrame *this_frame, int size, +* const double *lbnd, const double *ubnd, +* int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astFrameGrid +* method inherited from the Frame class). + +* Description: +* This function returns a PointSet containing positions spread +* approximately evenly throughtout a specified rectangular area of +* the Frame. + +* Parameters: +* this +* Pointer to the Frame. +* size +* The preferred number of points in the returned PointSet. The +* actual number of points in the returned PointSet may be +* different, but an attempt is made to stick reasonably closely to +* the supplied value. +* lbnd +* Pointer to an array holding the lower bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. +* ubnd +* Pointer to an array holding the upper bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. + +* Returned Value: +* A pointer to a new PointSet holding the grid of points. + +* Notes: +* - A NULL pointer is returned if an error occurs. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + AstPointSet *result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astFrameGrid method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astFrameGrid( fr, size, lbnd, ubnd ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int ForceCopy( AstFrameSet *this, int iframe, int *status ) { +/* +* Name: +* ForceCopy + +* Purpose: +* Force a copy to be made of a Frame, if necessary. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int ForceCopy( AstFrameSet *this, int iframe, int *status ) + +* Class Membership: +* FrameSet member function. + +* Description: +* This function examines a Frame within a FrameSet, identified by its +* Frame index. If the same Frame is found to be referenced a second time +* within the FrameSet, then the original reference is replaced with an +* independent copy of the Frame. +* +* This process supports the preservation of FrameSet integrity in cases +* where the same Frame is referenced more than once. After using this +* function, the nominated Frame's attributes may be modified without +* danger of affecting other parts of the FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. +* iframe +* The index of the Frame to be examined. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a copy of the nominated Frame was made, otherwise zero. + +* Notes: +* - Using this function a second time on the same Frame will have no +* effect, since the first usage will make the Frame independent of any +* other Frames within the FrameSet. +* - A value of zero will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *frame; /* Pointer to Frame */ + AstFrame *tmp; /* Temporary Frame pointer */ + int ifr; /* Loop counter for Frames */ + int result; /* Value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Validate and translate the Frame index supplied. */ + iframe = astValidateFrameIndex( this, iframe, integrity_method ); + +/* If OK, obtain the corresponding Frame pointer (don't clone it). */ + if ( astOK ) { + frame = this->frame[ iframe - 1 ]; + +/* Loop to inspect each Frame in the FrameSet. */ + for ( ifr = 1; ifr <= this->nframe; ifr++ ) { + +/* If the same Frame is referenced anywhere else, then make a copy of it. */ + if ( ( ifr != iframe ) && ( this->frame[ ifr - 1 ] == frame ) ) { + tmp = astCopy( frame ); + +/* If successful, replace the original reference to the Frame with a pointer + to this copy and annul the original pointer. */ + if ( astOK ) { + this->frame[ iframe - 1 ] = tmp; + frame = astAnnul( frame ); + +/* Set the returned result. */ + if ( astOK ) result = 1; + } + +/* Quit looping once a copy has been made. */ + break; + } + } + } + +/* Return the result. */ + return result; +} + +static const char *Format( AstFrame *this_frame, int axis, double value, int *status ) { +/* +* Name: +* Format + +* Purpose: +* Format a coordinate value for a FrameSet axis. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* const char *Format( AstFrame *this, int axis, double value, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astFormat method +* inherited from the Frame class). + +* Description: +* This function returns a pointer to a string containing the +* formatted (character) version of a coordinate value for a +* FrameSet axis. The formatting applied is that specified by a +* previous invocation of the astSetFormat method. A suitable +* default format is applied if necessary. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The number of the axis (zero-based) for which formatting is +* to be performed. +* value +* The coordinate value to be formatted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the FrameSet object, or at static memory. The contents of +* the string may be over-written or the pointer may become invalid +* following a further invocation of the same function or deletion +* of the FrameSet. A copy of the string should therefore be made +* if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astFormat" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astFormat method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astFormat( fr, axis, value ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static double Gap( AstFrame *this_frame, int axis, double gap, int *ntick, int *status ) { +/* +* Name: +* Gap + +* Purpose: +* Find a "nice" gap for tabulating FrameSet axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astGap method +* inherited from the Frame class). + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted values for a FrameSet axis, the returned gap +* size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Gap value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGap" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astGap method to obtain the required gap value. Annul the + Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astGap( fr, axis, gap, ntick ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied FrameSet, +* in bytes. + +* Parameters: +* this +* Pointer to the FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + int result; /* Result value to return */ + int iframe; /* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + for ( iframe = 0; iframe < this->nframe; iframe++ ) { + result += astGetObjSize( this->frame[ iframe ] ); + } + + for ( inode = 0; inode < this->nnode - 1; inode++ ) { + result += astGetObjSize( this->map[ inode ] ); + } + + result += astTSizeOf( this->frame ); + result += astTSizeOf( this->varfrm ); + result += astTSizeOf( this->node ); + result += astTSizeOf( this->map ); + result += astTSizeOf( this->link ); + result += astTSizeOf( this->invert ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a FrameSet, formatted as a character string. + +* Parameters: +* this +* Pointer to the FrameSet. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the FrameSet, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the FrameSet. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + const char *result; /* Pointer value to return */ + int base; /* Base attribute value */ + int current; /* Current attribute value */ + int invert; /* Invert attribute value */ + int nframe; /* Nframe attribute value */ + int nin; /* Nin attribute value */ + int nobject; /* Nobject attribute value */ + int nout; /* Nout attribute value */ + int ref_count; /* RefCount attribute value */ + int report; /* Report attribute value */ + int tranforward; /* TranForward attribute value */ + int traninverse; /* TranInverse attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* We first handle attributes that apply to the FrameSet as a whole + (rather than to the current Frame). */ + +/* AllVariants. */ +/* ------------ */ + if ( !strcmp( attrib, "allvariants" ) ) { + result = astGetAllVariants( this ); + +/* Base. */ +/* ----- */ + } else if ( !strcmp( attrib, "base" ) ) { + base = astGetBase( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", base ); + result = getattrib_buff; + } + +/* Class. */ +/* ------ */ + } else if ( !strcmp( attrib, "class" ) ) { + result = astGetClass( this ); + +/* Current. */ +/* -------- */ + } else if ( !strcmp( attrib, "current" ) ) { + current = astGetCurrent( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", current ); + result = getattrib_buff; + } + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + result = astGetID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astGetIdent( this ); + +/* Invert. */ +/* ------- */ + } else if ( !strcmp( attrib, "invert" ) ) { + invert = astGetInvert( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", invert ); + result = getattrib_buff; + } + +/* Nframe. */ +/* ------- */ + } else if ( !strcmp( attrib, "nframe" ) ) { + nframe = astGetNframe( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nframe ); + result = getattrib_buff; + } + +/* Nin. */ +/* ---- */ + } else if ( !strcmp( attrib, "nin" ) ) { + nin = astGetNin( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nin ); + result = getattrib_buff; + } + +/* Nobject. */ +/* -------- */ + } else if ( !strcmp( attrib, "nobject" ) ) { + nobject = astGetNobject( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nobject ); + result = getattrib_buff; + } + +/* Nout. */ +/* ----- */ + } else if ( !strcmp( attrib, "nout" ) ) { + nout = astGetNout( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nout ); + result = getattrib_buff; + } + +/* RefCount. */ +/* --------- */ + } else if ( !strcmp( attrib, "refcount" ) ) { + ref_count = astGetRefCount( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ref_count ); + result = getattrib_buff; + } + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + report = astGetReport( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", report ); + result = getattrib_buff; + } + +/* TranForward. */ +/* ------------ */ + } else if ( !strcmp( attrib, "tranforward" ) ) { + tranforward = astGetTranForward( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", tranforward ); + result = getattrib_buff; + } + +/* TranInverse. */ +/* ------------ */ + } else if ( !strcmp( attrib, "traninverse" ) ) { + traninverse = astGetTranInverse( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", traninverse ); + result = getattrib_buff; + } + +/* Variant. */ +/* -------- */ + } else if ( !strcmp( attrib, "variant" ) ) { + result = astGetVariant( this ); + +/* Pass unrecognised attributes on to the FrameSet's current Frame for + further interpretation. */ + } else { + +/* Obtain a pointer to the current Frame and invoke its astGetAttrib + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astGetAttrib( fr, attrib ); + fr = astAnnul( fr ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static AstAxis *GetAxis( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetAxis + +* Purpose: +* Obtain a pointer to a specified Axis from a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstAxis *GetAxis( AstFrame *this, int axis, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astGetAxis method +* inherited from the Frame class). + +* Description: +* This function returns a pointer to the Axis object associated +* with one of the axes of the current Frame of a FrameSet. This +* object describes the quantity which is represented along that +* axis. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The number of the axis (zero-based) for which an Axis pointer +* is required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the requested Axis object. + +* Notes: +* - The reference count of the requested Axis object will be +* incremented by one to reflect the additional pointer returned by +* this function. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstAxis *result; /* Pointer to Axis */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGetAxis" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astGetAxis method to obtain the required Axis + pointer. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astGetAxis( fr, axis ); + fr = astAnnul( fr ); + +/* If an error occurred, annul the result. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int GetBase( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astGetBase + +* Purpose: +* Obtain the value of the Base attribute for a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astGetBase( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns the value of the Base attribute for a +* FrameSet. This value is an index that identifies the base Frame +* in the FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* The Base attribute value. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + int result; /* Value to return */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, return the base Frame index, otherwise + return the index of the current Frame instead. Provide defaults if + necessary. */ + if ( astOK ) { + if ( !invert ) { + result = ( this->base != -INT_MAX ) ? this->base : 1; + } else { + result = ( this->current != -INT_MAX ) ? this->current : + astGetNframe( this ); + } + } + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetCurrent( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astGetCurrent + +* Purpose: +* Obtain the value of the Current attribute for a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* int astGetCurrent( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns the value of the Current attribute for a +* FrameSet. This attribute is an index that identifies the +* current Frame in a FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* Value of the Current attribute. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + int result; /* Value to return */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, return the current Frame index, + otherwise return the index of the base Frame instead. Provide + defaults if necessary. */ + if ( astOK ) { + if ( !invert ) { + result = ( this->current != -INT_MAX ) ? this->current : + astGetNframe( this ); + } else { + result = ( this->base != -INT_MAX ) ? this->base : 1; + } + } + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static AstFrame *GetFrame( AstFrameSet *this, int iframe, int *status ) { +/* +*++ +* Name: +c astGetFrame +f AST_GETFRAME + +* Purpose: +* Obtain a pointer to a specified Frame in a FrameSet. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c AstFrame *astGetFrame( AstFrameSet *this, int iframe ) +f RESULT = AST_GETFRAME( THIS, IFRAME, STATUS ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns a pointer to a specified Frame in a +* FrameSet. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FrameSet. +c iframe +f IFRAME = INTEGER (Given) +* The index of the required Frame within the FrameSet. This +* value should lie in the range from 1 to the number of Frames +* in the FrameSet (as given by its Nframe attribute). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetFrame() +f AST_GETFRAME = INTEGER +* A pointer to the requested Frame. + +* Notes: +* - A value of AST__BASE or AST__CURRENT may be given for the +c "iframe" parameter to specify the base Frame or the current +f IFRAME argument to specify the base Frame or the current +* Frame respectively. +* - This function increments the RefCount attribute of the +* selected Frame by one. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstFrame *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate and translate the Frame index supplied. */ + iframe = astValidateFrameIndex( this, iframe, "astGetFrame" ); + +/* If OK, clone a pointer to the requested Frame. */ + if ( astOK ) result = astClone( this->frame[ iframe - 1 ] ); + +/* Return the result. */ + return result; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astGetIsLinear +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsLinear attribute for a +* FrameSet, which is the IsLinear value of he base->current Mapping. + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. +*/ +/* Local Variables: */ + AstMapping *map; + int result; + +/* Check global status */ + if( !astOK ) return 0; + +/* Get the Mapping. */ + map = astGetMapping( (AstFrameSet *) this_mapping, AST__BASE, + AST__CURRENT ); + +/* Get its IsLinear attribute value. */ + result = astGetIsLinear( map ); + +/* Free the Mapping. */ + map = astAnnul( map ); + +/* Return the result. */ + return result; +} + +static AstMapping *GetMapping( AstFrameSet *this, int iframe1, int iframe2, int *status ) { +/* +*++ +* Name: +c astGetMapping +f AST_GETMAPPING + +* Purpose: +* Obtain a Mapping that converts between two Frames in a FrameSet. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c AstMapping *astGetMapping( AstFrameSet *this, int iframe1, int iframe2 ) +f RESULT = AST_GETMAPPING( THIS, IFRAME1, IFRAME2, STATUS ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns a pointer to a Mapping that will convert +* coordinates between the coordinate systems represented by two +* Frames in a FrameSet. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FrameSet. +c iframe1 +f IFRAME1 = INTEGER (Given) +* The index of the first Frame in the FrameSet. This Frame describes +* the coordinate system for the "input" end of the Mapping. +c iframe2 +f IFRAME2 = INTEGER (Given) +* The index of the second Frame in the FrameSet. This Frame +* describes the coordinate system for the "output" end of the +* Mapping. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetMapping() +f AST_GETMAPPING = INTEGER +* Pointer to a Mapping whose forward transformation converts +* coordinates from the first coordinate system to the second +* one, and whose inverse transformation converts coordinates in +* the opposite direction. + +* Notes: +* - The returned Mapping will include the clipping effect of any +* Regions which occur on the path between the two supplied Frames +* (this includes the two supplied Frames themselves). +c - The values given for the "iframe1" and "iframe2" parameters +f - The values given for the IFRAME1 and IFRAME2 arguments +* should lie in the range from 1 to the number of Frames in the +* FrameSet (as given by its Nframe attribute). A value of +* AST__BASE or AST__CURRENT may also be given to identify the +* FrameSet's base Frame or current Frame respectively. It is +c permissible for both these parameters to have the same value, in +f permissible for both these arguments to have the same value, in +* which case a unit Mapping (UnitMap) is returned. +* - It should always be possible to generate the Mapping +* requested, but this does necessarily guarantee that it will be +* able to perform the required coordinate conversion. If +* necessary, the TranForward and TranInverse attributes of the +* returned Mapping should be inspected to determine if the +* required transformation is available. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstFrame *fr; /* Temporary pointer to Frame */ + AstFrame **frames; /* Pointer to array of Frames */ + AstMapping **path; /* Pointer to array of conversion Mappings */ + AstMapping *copy; /* Pointer to copy of Mapping */ + AstMapping *result; /* Result pointer to be returned */ + AstMapping *tmp; /* Temporary pointer for joining Mappings */ + int *forward; /* Pointer to array of Mapping directions */ + int ipath; /* Loop counter for conversion path steps */ + int iframe; /* Frame index */ + int inode; /* Node index */ + int npath; /* Number of steps in conversion path */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate and translate the Frame indices supplied. */ + iframe1 = astValidateFrameIndex( this, iframe1, "astGetMapping" ); + iframe2 = astValidateFrameIndex( this, iframe2, "astGetMapping" ); + +/* Allocate memory to hold an array of Mapping pointers and associated + direction flags - a maximum of one element for each Mapping and one + for each Frame in the FrameSet. */ + path = astMalloc( sizeof( AstMapping * ) * (size_t) ( this->nnode - 1 + + this->nframe ) ); + forward = astMalloc( sizeof( int ) * (size_t) ( this->nnode - 1 + + this->nframe ) ); + +/* Allocate memory to hold a list of the Frame pointers (if any) associated + with each node. */ + frames = astMalloc( sizeof( AstFrame * ) * (size_t) ( this->nnode ) ); + +/* If OK, set up an array of Frame pointers indexed by node index. If a + node has no associated Frame store a NULL pointer. This is needed so + that we can find Frame pointers quickly within the Span function. Note, + we simply copy the pointers rather than cloning them, so they do not + need to be annulled when finished with. */ + if ( astOK ) { + for( inode = 0; inode < this->nnode; inode++ ) frames[ inode ] = NULL; + for( iframe = 0; iframe < this->nframe; iframe++ ) { + frames[ this->node[ iframe ] ] = this->frame[ iframe ]; + } + +/* Obtain the Mapping pointers and direction flags needed to convert + coordinates between the nodes associated with the two specified + Frames. */ + npath = Span( this, frames, this->node[ iframe1 - 1 ], + this->node[ iframe2 - 1 ], -1, path, forward, status ) - 1; + +/* If this failed, it indicates a corrupt FrameSet object, so report + an error. */ + if ( npath < 0 ) { + astError( AST__FRSIN, "astGetMapping(%s): Invalid or corrupt " + "%s - could not find conversion path between Frames " + "%d and %d.", status, astGetClass( this ), astGetClass( this ), + iframe1, iframe2 ); + +/* If the conversion path is of zero length (i.e. the two Frames are + the same) then we will return a Mapping which is equivalent to the + Frame. Most classes of Frame are equivalent to a UnitMap. However, we do + not hard-wire this equivalence since some classes of Frame (e.g. Regions + or CmpFrames containing Regions) do not correspond to a UnitMap. Instead + we use the astIsUnitFrame method on the Frame to determine if the + Frame is equivalent to a UnitMap.Is os, create a suitable UnitMap. If + not, return the Frame itself (a form of Mapping). */ + } else if ( npath == 0 ) { + fr = astGetFrame( this, iframe1 ); + if( astIsUnitFrame( fr ) ){ + result = (AstMapping *) astUnitMap( astGetNaxes( fr ), "", status ); + } else { + result = (AstMapping *) astClone( fr ); + } + fr = astAnnul( fr ); + +/* If the conversion path involves at least one non-trivial Mapping, + make a copy of the first Mapping, inverting the copy if + necessary. */ + } else { + result = astCopy( path[ 0 ] ); + if ( !forward[ 0 ] ) astInvert( result ); + +/* Now loop to concatenate any further Mappings. First make a copy of + each additional Mapping and invert the copy if necessary. */ + for ( ipath = 1; ipath < npath; ipath++ ) { + copy = astCopy( path[ ipath ] ); + if ( !forward[ ipath ] ) astInvert( copy ); + +/* Concatenate the copy with the result so far, then annul the pointer + to the copy and save the pointer to the new result. */ + tmp = (AstMapping *) astCmpMap( result, copy, 1, "", status ); + result = astAnnul( result ); + copy = astAnnul( copy ); + result = tmp; + } + } + } + +/* Free the memory allocated for the conversion path information. */ + path = astFree( path ); + forward = astFree( forward ); + frames = astFree( frames ); + +/* If an error occurred, annul the returned Mapping. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int GetNaxes( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetNaxes + +* Purpose: +* Determine how many axes a FrameSet has. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetNaxes( AstFrame *this, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astGetNaxes method +* inherited from the Frame class). + +* Description: +* This function returns the number of axes for a FrameSet. This is equal +* to the number of axes in its current Frame. + +* Parameters: +* this +* Pointer to the FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of FrameSet axes (zero or more). + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame. */ + fr = astGetFrame( this, AST__CURRENT ); + +/* Obtain the number of axes in this Frame. */ + result = astGetNaxes( fr ); + +/* Annul the current Frame pointer. */ + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetNframe( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astGetNframe + +* Purpose: +* Determine the number of Frames in a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astGetNframe( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns the number of Frames in a FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* The number of Frames in the FrameSet (always 1 or more). + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the Frame count. */ + return this->nframe; +} + +static int GetNin( AstMapping *this_mapping, int *status ) { +/* +* Name: +* GetNin + +* Purpose: +* Get the number of input coordinates for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetNin( AstMapping *this, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astGetNin method +* inherited from the Frame class). + +* Description: +* This function returns the number of input coordinate values +* required per point by a FrameSet when used to transform a set of +* points (i.e. the number of dimensions of the space in which the +* input points reside). +* +* The value returned is equal to the number of axes in the +* FrameSet's base Frame. + +* Parameters: +* this +* Pointer to the FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Number of coordinate values required. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to base Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_mapping; + +/* Obtain a pointer to the FrameSet's base Frame. */ + fr = astGetFrame( this, AST__BASE ); + +/* Obtain the number of axes in this Frame. */ + result = astGetNaxes( fr ); + +/* Annul the base Frame pointer. */ + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetNout( AstMapping *this_mapping, int *status ) { +/* +* Name: +* GetNout + +* Purpose: +* Get the number of output coordinates for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetNout( AstMapping *this, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astGetNout method +* inherited from the Frame class). + +* Description: +* This function returns the number of output coordinate values +* generated per point by a FrameSet when used to transform a set +* of points (i.e. the number of dimensions of the space in which +* the output points reside). +* +* The value returned is equal to the number of axes in the +* FrameSet's current Frame. + +* Parameters: +* this +* Pointer to the FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Number of coordinate values generated. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the number of axes in the FrameSet's current Frame. */ + return GetNaxes( (AstFrame *) this_mapping, status ); +} + +static const int *GetPerm( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetPerm + +* Purpose: +* Access the axis permutation array for the current Frame of a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* const int *GetPerm( AstFrame *this, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astGetPerm protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the axis permutation array +* for the current Frame of a FrameSet. This array constitutes a +* lookup-table that converts between an axis number supplied +* externally and the corresponding index in the Frame's internal +* axis arrays. + +* Parameters: +* this +* Pointer to the FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the current Frame's axis permutation array (a +* constant array of int). Each element of this contains the +* (zero-based) internal axis index to be used in place of the +* external index which is used to address the permutation +* array. If the current Frame has zero axes, this pointer will be +* NULL. + +* Notes: +* - The pointer returned by this function gives direct access to +* data internal to the Frame object. It remains valid only so long +* as the Frame exists. The permutation array contents may be +* modified by other functions which operate on the Frame and this +* may render the returned pointer invalid. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + const int *result; /* Result pointer value */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and then obtain a + pointer to its axis permutation array. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astGetPerm( fr ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static int GetTranForward( AstMapping *this_mapping, int *status ) { +/* +* Name: +* GetTranForward + +* Purpose: +* Determine if a FrameSet defines a forward coordinate transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetTranForward( AstMapping *this ) + +* Class Membership: +* Frameset member function (over-rides the astGetTranForward +* protected method inherited from the Frame class). + +* Description: +* This function returns a value indicating whether a FrameSet is +* able to perform a coordinate transformation in the "forward" +* direction. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* Zero if the forward coordinate transformation is not defined, or +* 1 if it is. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + AstMapping *map; /* Pointer to base->current Mapping */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_mapping; + +/* Obtain the Mapping between the base and current Frames in the + FrameSet (note this takes account of whether the FrameSet has been + inverted). */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Determine whether the required transformation is defined. */ + result = astGetTranForward( map ); + +/* Annul the Mapping pointer. */ + map = astAnnul( map ); + +/* If an error occurred, clear the returned result. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetTranInverse( AstMapping *this_mapping, int *status ) { +/* +* Name: +* GetTranInverse + +* Purpose: +* Determine if a FrameSet defines an inverse coordinate transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetTranInverse( AstMapping *this ) + +* Class Membership: +* Frameset member function (over-rides the astGetTranInverse +* protected method inherited from the Frame class). + +* Description: +* This function returns a value indicating whether a FrameSet is +* able to perform a coordinate transformation in the "inverse" +* direction. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* Zero if the inverse coordinate transformation is not defined, or +* 1 if it is. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + AstMapping *map; /* Pointer to base->current Mapping */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_mapping; + +/* Obtain the Mapping between the base and current Frames in the + FrameSet (note this takes account of whether the FrameSet has been + inverted). */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Determine whether the required transformation is defined. */ + result = astGetTranInverse( map ); + +/* Annul the Mapping pointer. */ + map = astAnnul( map ); + +/* If an error occurred, clear the returned result. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetUseDefs( AstObject *this_object, int *status ) { +/* +* Name: +* GetUseDefs + +* Purpose: +* Get the value of the UseDefs attribute for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetUseDefs( AstObject *this_object, int *status ) { + +* Class Membership: +* FrameSet member function (over-rides the protected astGetUseDefs +* method inherited from the Frame class). + +* Description: +* This function returns the value of the UseDefs attribute for a FrameSet, +* supplying a suitable default. + +* Parameters: +* this +* Pointer to the FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - The USeDefs value. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int result; /* Value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* If the UseDefs value for the FrameSet has been set explicitly, use the + Get method inherited from the parent Frame class to get its value> */ + if( astTestUseDefs( this ) ) { + result = (*parent_getusedefs)( this_object, status ); + +/* Otherwise, supply a default value equal to the UseDefs value of the + current Frame. */ + } else { + fr = astGetFrame( this, AST__CURRENT ); + result = astGetUseDefs( fr ); + fr = astAnnul( fr ); + } + +/* Return the result. */ + return result; +} + +static int GetVarFrm( AstFrameSet *this, int iframe, int *status ) { +/* +* Name: +* GetVarFrm + +* Purpose: +* Get the index of the variants Frame for a nominated Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int GetVarFrm( AstFrameSet *this, int iframe, int *status ) { + +* Class Membership: +* Private function. + +* Description: +* This function returns the index of the variants Frame associated +* with a nominated mirror Frame. See astMirrorVariants. + +* Parameters: +* this +* Pointer to the FrameSet. +* iframe +* The one-based index of the nominated Frame that may potentially be +* a mirror for the variant Mappings in another Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The one-based Frame index of the Frame that defines the variant +* Mappings associated with Frame "iframe". This will be the same as +* "iframe" unless the nominated Frame is a mirror for another Frame. +*/ + +/* Local Variables: */ + int result; /* Value to return */ + +/* Initialise. */ + result = AST__NOFRAME; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise the returned value. */ + result = iframe; + +/* If the nominated Frame is mirroring another Frame, return the index of + the mirrored Frame. Walk up the chain until we reach a Frame which is + not a mirror for another Frame. */ + while( this->varfrm[ result - 1 ] > 0 ) { + if( this->varfrm[ result - 1 ] == result ) { + astError( AST__INTER, "GetVarFrm(FrameSet): FrameSet is corrupt " + "(internal programming error).", status ); + break; + } else { + result = this->varfrm[ result - 1 ]; + } + } + +/* Return the result. */ + return result; +} + +static const char *GetVariant( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astGetVariant + +* Purpose: +* Obtain the value of the Variant attribute for a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* const char *astGetVariant( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns the value of the Variant attribute for a +* FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* The Variant attribute value. + +* Notes: +* - A NULL value will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFrame *frm; + AstFrame *vfs; + const char *result; + int icur; + int iuse; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the one-based index of the current Frame. */ + icur = astGetCurrent( this ); + +/* The current Frame may mirror the variant Mappings in another Frame, + rather than defining any variant Mappings itself. Get the one-based + index of the Frame that defines the variant Mappings to use. */ + iuse = GetVarFrm( this, icur, status ); + +/* Get a pointer to the Variants FrameSet in the used Frame. */ + frm = astGetFrame( this, iuse ); + vfs = astGetFrameVariants( frm ); + +/* If the current Frame has no Variants FrameSet, return the Domain name + of the current Frame. */ + if( !vfs ) { + result = astGetDomain( this ); + +/* Otherwise, return the Domain name of the current Frame in the Variants + FrameSet. Then annul the Variants FrameSet pointer. */ + } else { + result = astGetDomain( vfs ); + vfs = astAnnul( vfs ); + } + +/* Annul the current Frame pointer. */ + frm = astAnnul( frm ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +void astInitFrameSetVtab_( AstFrameSetVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitFrameSetVtab + +* Purpose: +* Initialise a virtual function table for a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* void astInitFrameSetVtab( AstFrameSetVtab *vtab, const char *name ) + +* Class Membership: +* FrameSet vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the FrameSet class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAFrameSet) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->AddFrame = AddFrame; + vtab->AddVariant = AddVariant; + vtab->ClearBase = ClearBase; + vtab->ClearCurrent = ClearCurrent; + vtab->GetBase = GetBase; + vtab->GetCurrent = GetCurrent; + vtab->GetFrame = GetFrame; + vtab->GetMapping = GetMapping; + vtab->GetNframe = GetNframe; + vtab->GetAllVariants = GetAllVariants; + vtab->MirrorVariants = MirrorVariants; + vtab->RemapFrame = RemapFrame; + vtab->RemoveFrame = RemoveFrame; + vtab->SetBase = SetBase; + vtab->SetCurrent = SetCurrent; + vtab->TestBase = TestBase; + vtab->TestCurrent = TestCurrent; + vtab->ValidateFrameIndex = ValidateFrameIndex; + + vtab->ClearVariant = ClearVariant; + vtab->GetVariant = GetVariant; + vtab->SetVariant = SetVariant; + vtab->TestVariant = TestVariant; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + + parent_clear = object->Clear; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + object->Clear = Clear; + + parent_vset = object->VSet; + object->VSet = VSet; + + parent_getusedefs = object->GetUseDefs; + object->GetUseDefs = GetUseDefs; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping = (AstMappingVtab *) vtab; + frame = (AstFrameVtab *) vtab; + + object->ClearAttrib = ClearAttrib; + object->GetAttrib = GetAttrib; + object->SetAttrib = SetAttrib; + object->TestAttrib = TestAttrib; + + object->GetUseDefs = GetUseDefs; + object->Equal = Equal; + object->Cast = Cast; + + mapping->GetIsLinear = GetIsLinear; + mapping->GetNin = GetNin; + mapping->GetNout = GetNout; + mapping->GetTranForward = GetTranForward; + mapping->GetTranInverse = GetTranInverse; + mapping->Rate = Rate; + mapping->ReportPoints = ReportPoints; + mapping->RemoveRegions = RemoveRegions; + mapping->Simplify = Simplify; + mapping->Transform = Transform; + mapping->MapSplit = MapSplit; + + frame->Abbrev = Abbrev; + frame->Angle = Angle; + frame->AxAngle = AxAngle; + frame->AxDistance = AxDistance; + frame->AxOffset = AxOffset; + frame->CheckPerm = CheckPerm; + frame->ClearDigits = ClearDigits; + frame->ClearDirection = ClearDirection; + frame->ClearDomain = ClearDomain; + frame->ClearFormat = ClearFormat; + frame->ClearLabel = ClearLabel; + frame->ClearMatchEnd = ClearMatchEnd; + frame->ClearMaxAxes = ClearMaxAxes; + frame->ClearMinAxes = ClearMinAxes; + frame->ClearPermute = ClearPermute; + frame->ClearPreserveAxes = ClearPreserveAxes; + frame->ClearSymbol = ClearSymbol; + frame->ClearTitle = ClearTitle; + frame->ClearUnit = ClearUnit; + frame->Convert = Convert; + frame->ConvertX = ConvertX; + frame->Distance = Distance; + frame->Fields = Fields; + frame->FindFrame = FindFrame; + frame->Format = Format; + frame->FrameGrid = FrameGrid; + frame->Centre = Centre; + frame->Gap = Gap; + frame->GetAxis = GetAxis; + frame->GetDigits = GetDigits; + frame->GetDirection = GetDirection; + frame->GetDomain = GetDomain; + frame->GetFormat = GetFormat; + frame->GetLabel = GetLabel; + frame->GetMatchEnd = GetMatchEnd; + frame->GetMaxAxes = GetMaxAxes; + frame->GetMinAxes = GetMinAxes; + frame->GetNaxes = GetNaxes; + frame->GetPerm = GetPerm; + frame->GetPermute = GetPermute; + frame->GetPreserveAxes = GetPreserveAxes; + frame->GetSymbol = GetSymbol; + frame->GetTitle = GetTitle; + frame->GetUnit = GetUnit; + frame->Intersect = Intersect; + frame->IsUnitFrame = IsUnitFrame; + frame->LineContains = LineContains; + frame->LineCrossing = LineCrossing; + frame->LineDef = LineDef; + frame->LineOffset = LineOffset; + frame->Match = Match; + frame->MatchAxes = MatchAxes; + frame->MatchAxesX = MatchAxesX; + frame->Norm = Norm; + frame->NormBox = NormBox; + frame->Offset = Offset; + frame->Offset2 = Offset2; + frame->Overlay = Overlay; + frame->PermAxes = PermAxes; + frame->PickAxes = PickAxes; + frame->PrimaryFrame = PrimaryFrame; + frame->Resolve = Resolve; + frame->ResolvePoints = ResolvePoints; + frame->SetAxis = SetAxis; + frame->SetDigits = SetDigits; + frame->SetDirection = SetDirection; + frame->SetDomain = SetDomain; + frame->SetFormat = SetFormat; + frame->SetLabel = SetLabel; + frame->SetMatchEnd = SetMatchEnd; + frame->SetMaxAxes = SetMaxAxes; + frame->SetMinAxes = SetMinAxes; + frame->SetPermute = SetPermute; + frame->SetPreserveAxes = SetPreserveAxes; + frame->SetSymbol = SetSymbol; + frame->SetTitle = SetTitle; + frame->SetUnit = SetUnit; + frame->SubFrame = SubFrame; + frame->SystemCode = SystemCode; + frame->SystemString = SystemString; + frame->TestDigits = TestDigits; + frame->TestDirection = TestDirection; + frame->TestDomain = TestDomain; + frame->TestFormat = TestFormat; + frame->TestLabel = TestLabel; + frame->TestMatchEnd = TestMatchEnd; + frame->TestMaxAxes = TestMaxAxes; + frame->TestMinAxes = TestMinAxes; + frame->TestPermute = TestPermute; + frame->TestPreserveAxes = TestPreserveAxes; + frame->TestSymbol = TestSymbol; + frame->TestTitle = TestTitle; + frame->TestUnit = TestUnit; + frame->Unformat = Unformat; + frame->ValidateAxis = ValidateAxis; + frame->ValidateAxisSelection = ValidateAxisSelection; + frame->ValidateSystem = ValidateSystem; + + frame->GetActiveUnit = GetActiveUnit; + frame->SetActiveUnit = SetActiveUnit; + frame->TestActiveUnit = TestActiveUnit; + + frame->GetTop = GetTop; + frame->SetTop = SetTop; + frame->TestTop = TestTop; + frame->ClearTop = ClearTop; + + frame->GetBottom = GetBottom; + frame->SetBottom = SetBottom; + frame->TestBottom = TestBottom; + frame->ClearBottom = ClearBottom; + + frame->GetEpoch = GetEpoch; + frame->SetEpoch = SetEpoch; + frame->TestEpoch = TestEpoch; + frame->ClearEpoch = ClearEpoch; + + frame->GetSystem = GetSystem; + frame->SetSystem = SetSystem; + frame->TestSystem = TestSystem; + frame->ClearSystem = ClearSystem; + + frame->GetAlignSystem = GetAlignSystem; + frame->SetAlignSystem = SetAlignSystem; + frame->TestAlignSystem = TestAlignSystem; + frame->ClearAlignSystem = ClearAlignSystem; + + frame->ClearObsLat = ClearObsLat; + frame->TestObsLat = TestObsLat; + frame->GetObsLat = GetObsLat; + frame->SetObsLat = SetObsLat; + + frame->ClearObsAlt = ClearObsAlt; + frame->TestObsAlt = TestObsAlt; + frame->GetObsAlt = GetObsAlt; + frame->SetObsAlt = SetObsAlt; + + frame->ClearObsLon = ClearObsLon; + frame->TestObsLon = TestObsLon; + frame->GetObsLon = GetObsLon; + frame->SetObsLon = SetObsLon; + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "FrameSet", + "Set of inter-related coordinate systems" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void Intersect( AstFrame *this_frame, const double a1[2], + const double a2[2], const double b1[2], + const double b2[2], double cross[2], + int *status ) { +/* +* Name: +* Intersect + +* Purpose: +* Find the point of intersection between two geodesic curves. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void Intersect( AstFrame *this_frame, const double a1[2], +* const double a2[2], const double b1[2], +* const double b2[2], double cross[2], +* int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astIntersect method +* inherited from the Frame class). + +* Description: +* This function finds the coordinate values at the point of +* intersection between two geodesic curves. Each curve is specified +* by two points on the curve. + +* Parameters: +* this +* Pointer to the SkyFrame. +* a1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the first +* geodesic curve. +* a2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on the +* first geodesic curve. +* b1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the second +* geodesic curve. +* b2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on +* the second geodesic curve. +* cross +* An array of double, with one element for each Frame axis +* in which the coordinates of the required intersection +* point will be returned. These will be AST__BAD if the curves do +* not intersect. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - For SkyFrames each curve will be a great circle, and in general +* each pair of curves will intersect at two diametrically opposite +* points on the sky. The returned position is the one which is +* closest to point "a1". +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astIntersect method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astIntersect( fr, a1, a2, b1, b2, cross ); + fr = astAnnul( fr ); + +} + +static int IsUnitFrame( AstFrame *this_frame, int *status ){ +/* +* Name: +* IsUnitFrame + +* Purpose: +* Is this Frame equivalent to a UnitMap? + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int IsUnitFrame( AstFrame *this, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astIsUnitFrame +* method inherited from the Frame class). + +* Description: +* This function returns a flag indicating if the supplied Frame is +* equivalent to a UnitMap when treated as a Mapping (note, the Frame +* class inherits from Mapping and therefore every Frame is also a Mapping). + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the supplied Frame is equivalent to +* a UnitMap when treated as a Mapping. + +*- +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int result; /* Result to be returned */ + +/* Initialise the returned value. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame. */ + fr = astGetFrame( this, AST__CURRENT ); + +/* Invoke the astIsUnitFrame method for this Frame. */ + result = astIsUnitFrame( fr ); + +/* Annul the Frame pointer. */ + fr = astAnnul( fr ); + +/* If an error occurred, clean up by clearing the returned result. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int LineContains( AstFrame *this_frame, AstLineDef *l, int def, double *point, int *status ) { +/* +* Name: +* LineContains + +* Purpose: +* Determine if a line contains a point. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astLineContains +* method inherited from the Frame class). + +* Description: +* This function determines if the supplied point is on the supplied +* line within the supplied Frame. + +* Parameters: +* this +* Pointer to the Frame. +* l +* Pointer to the structure defining the line. +* def +* Should be set non-zero if the "point" array was created by a +* call to astLineCrossing (in which case it may contain extra +* information following the axis values),and zero otherwise. +* point +* Point to an array containing the axis values of the point to be +* tested, possibly followed by extra cached information (see "def"). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the line contains the point. + +* Notes: +* - The pointer supplied for "l" should have been created using the +* astLineDef method. These structures contained cached information about +* the lines which improve the efficiency of this method when many +* repeated calls are made. An error will be reported if the structure +* does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + int result; /* Returned value */ + +/* Initialise */ + result =0; + +/* Obtain a pointer to the FrameSet's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( (AstFrameSet *) this_frame, AST__CURRENT ); + result = astLineContains( fr, l, def, point ); + fr = astAnnul( fr ); + +/* Return the result. */ + return result; +} + +static int LineCrossing( AstFrame *this_frame, AstLineDef *l1, AstLineDef *l2, + double **cross, int *status ) { +/* +* Name: +* LineCrossing + +* Purpose: +* Determine if two lines cross. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, +* double **cross, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astLineCrossing +* method inherited from the Frame class). + +* Description: +* This function determines if the two suplied line segments cross, +* and if so returns the axis values at the point where they cross. +* A flag is also returned indicating if the crossing point occurs +* within the length of both line segments, or outside one or both of +* the line segments. + +* Parameters: +* this +* Pointer to the Frame. +* l1 +* Pointer to the structure defining the first line. +* l2 +* Pointer to the structure defining the second line. +* cross +* Pointer to a location at which to put a pointer to a dynamically +* alocated array containing the axis values at the crossing. If +* NULL is supplied no such array is returned. Otherwise, the returned +* array should be freed using astFree when no longer needed. If the +* lines are parallel (i.e. do not cross) then AST__BAD is returned for +* all axis values. Note usable axis values are returned even if the +* lines cross outside the segment defined by the start and end points +* of the lines. The order of axes in the returned array will take +* account of the current axis permutation array if appropriate. Note, +* sub-classes such as SkyFrame may append extra values to the end +* of the basic frame axis values. A NULL pointer is returned if an +* error occurs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the lines cross at a point which is +* within the [start,end) segment of both lines. If the crossing point +* is outside this segment on either line, or if the lines are parallel, +* zero is returned. Note, the start point is considered to be inside +* the length of the segment, but the end point is outside. + +* Notes: +* - The pointers supplied for "l1" and "l2" should have been created +* using the astLineDef method. These structures contained cached +* information about the lines which improve the efficiency of this method +* when many repeated calls are made. An error will be reported if +* either structure does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + int result; /* Returned value */ + +/* Initialise */ + result =0; + +/* Obtain a pointer to the FrameSet's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( (AstFrameSet *) this_frame, AST__CURRENT ); + result = astLineCrossing( fr, l1, l2, cross ); + fr = astAnnul( fr ); + +/* Return the result. */ + return result; +} + +static AstLineDef *LineDef( AstFrame *this_frame, const double start[2], + const double end[2], int *status ) { +/* +* Name: +* LineDef + +* Purpose: +* Creates a structure describing a line segment in a 2D Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstLineDef *LineDef( AstFrame *this, const double start[2], +* const double end[2], int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astLineDef +* method inherited from the Frame class). + +* Description: +* This function creates a structure containing information describing a +* given line segment within the supplied 2D Frame. This may include +* information which allows other methods such as astLineCrossing to +* function more efficiently. Thus the returned structure acts as a +* cache to store intermediate values used by these other methods. + +* Parameters: +* this +* Pointer to the Frame. Must have 2 axes. +* start +* An array of 2 doubles marking the start of the line segment. +* end +* An array of 2 doubles marking the end of the line segment. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the memory structure containing the description of the +* line. This structure should be freed using astFree when no longer +* needed. A NULL pointer is returned (without error) if any of the +* supplied axis values are AST__BAD. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstLineDef *result; /* Returned value */ + +/* Initialise */ + result = NULL; + +/* Obtain a pointer to the FrameSet's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( (AstFrameSet *) this_frame, AST__CURRENT ); + result = astLineDef( fr, start, end ); + fr = astAnnul( fr ); + +/* Return the result. */ + return result; +} + +static void LineOffset( AstFrame *this_frame, AstLineDef *line, double par, + double prp, double point[2], int *status ){ +/* +* Name: +* LineOffset + +* Purpose: +* Find a position close to a line. + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* void LineOffset( AstFrame *this, AstLineDef *line, double par, +* double prp, double point[2], int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astLineOffset +* method inherited from the Frame class). + +* Description: +* This function returns a position formed by moving a given distance along +* the supplied line, and then a given distance away from the supplied line. + +* Parameters: +* this +* Pointer to the Frame. +* line +* Pointer to the structure defining the line. +* par +* The distance to move along the line from the start towards the end. +* prp +* The distance to move at right angles to the line. Positive +* values result in movement to the left of the line, as seen from +* the observer, when moving from start towards the end. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The pointer supplied for "line" should have been created using the +* astLineDef method. This structure contains cached information about the +* line which improves the efficiency of this method when many repeated +* calls are made. An error will be reported if the structure does not +* refer to the Frame specified by "this". +*/ + + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + +/* Obtain a pointer to the FrameSet's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( (AstFrameSet *) this_frame, AST__CURRENT ); + astLineOffset( fr, line, par, prp, point ); + fr = astAnnul( fr ); +} + +static const char *GetAllVariants( AstFrameSet *this, int *status ) { +/* +* Name: +* GetAllVariants + +* Purpose: +* Get a pointer to a list of the variant Mappings for the current Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* const char *getAllVariants( AstFrameSet *this ) + +* Class Membership: +* FrameSet member function. + +* Description: +* This function returns a space separated list of names for all the +* variant Mappings associated with the current Frame. See attribute +* "Variant". If the current Frame has no variant Mappings, the return +* value contains just the Domain name of the current Frame in the +* supplied FrameSet. + +* Parameters: +* this +* Pointer to the Frame. + +* Returned Value: +* A pointer to a null-terminated string containing the list. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the FrameSet, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Frame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstFrame *frm; + AstFrame *vfrm; + AstFrameSet *vfs; + const char *dom; + const char *result; + int ifrm; + int nc; + int icur; + int nfrm; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS( this ); + +/* Get the one-based index of the Frame that defines the available + variant Mappings. */ + icur = GetVarFrm( this, astGetCurrent( this ), status ); + +/* Get the variants FrameSet from the Frame selected above. */ + frm = astGetFrame( this, icur ); + vfs = astGetFrameVariants( frm ); + +/* If the Frame does not have a variants FrameSet, just return the DOmain + name from the current Frame. */ + if( !vfs ) { + result = astGetDomain( this ); + +/* If a variants FrameSet was found, form a space sperated list of the + Domain names in the FrameSet, stored in the static "getallvariants_buff" + string. */ + } else if( astOK ){ + nc = 0; + + nfrm = astGetNframe( vfs ); + for( ifrm = 0; ifrm < nfrm; ifrm++ ) { + vfrm = astGetFrame( vfs, ifrm + 1 ); + dom = astGetDomain( vfrm ); + if( astOK ){ + if( ( nc + strlen(dom) + 1 ) < GETALLVARIANTS_BUFF_LEN ) { + nc += sprintf( getallvariants_buff + nc, "%s ", dom ); + } else { + astError( AST__INTER, "astGetAllVariants(%s): Buffer " + "overflow - too many variants.", status, + astGetClass(this) ); + } + } + vfrm = astAnnul( vfrm ); + } + +/* Remove the final space. */ + getallvariants_buff[ nc - 1 ] = 0; + +/* Return a pointer to the buffer. */ + result = getallvariants_buff; + +/* Annul the pointer to the variants FrameSet. */ + vfs = astAnnul( vfs ); + } + +/* Free the pointer to the current Frame. */ + frm = astAnnul( frm ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + int i; /* Loop count */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + for( i = 0; i < this->nframe; i++ ) { + if( !result ) result = astManageLock( this->frame[ i ], mode, + extra, fail ); + } + + for ( i = 0; i < this->nnode - 1; i++ ) { + if( !result ) result = astManageLock( this->map[ i ], mode, extra, + fail ); + } + + return result; + +} +#endif + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* FrameSet method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing FrameSet. This is only possible if the specified inputs +* correspond to some subset of the FrameSet outputs. That is, there +* must exist a subset of the FrameSet outputs for which each output +* depends only on the selected FrameSet inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied FrameSet, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the FrameSet to be split (the FrameSet is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied FrameSet, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied FrameSet has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied FrameSet. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstMapping *bcmap; /* Base->current Mapping */ + int *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the Mapping from base to current Frame and try to split it. */ + bcmap = astGetMapping( (AstFrameSet *) this_map, AST__BASE, AST__CURRENT ); + result = astMapSplit( bcmap, nin, in, map ); + bcmap = astAnnul( bcmap ); + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static int Match( AstFrame *this_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astMatch +* method inherited from the Frame class). + +* Description: +* This function matches the current Frame of a "template" FrameSet +* to a "target" frame and determines whether it is possible to +* convert coordinates between them. If it is, a Mapping that +* performs the transformation is returned along with a new Frame +* that describes the coordinate system that results when this +* Mapping is applied to the current Frame of the target +* FrameSet. In addition, information is returned to allow the axes +* in this "result" Frame to be associated with the corresponding +* axes in the target and template Frames from which they are +* derived. + +* Parameters: +* template +* Pointer to the template FrameSet, whose current Frame +* describes the coordinate system (or set of possible +* coordinate systems) into which we wish to convert our +* coordinates. +* target +* Pointer to the target Frame. This describes the coordinate +* system in which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case (i.e. if the +* target is of a more specialised class than the template). In +* this latter case, the target is cast down to the class of the +* template. NOTE, this argument is handled by the global method +* wrapper function "astMatch_", rather than by the class-specific +* implementations of this method. +* template_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the index of the axis in the +* template FrameSet's current Frame from which it is +* derived. If it is not derived from any template FrameSet +* axis, a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the index of the target Frame axis +* from which it is derived. If it is not derived from any +* target Frame axis, a value of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will +* be returned if the requested coordinate conversion is +* possible. If returned, the forward transformation of this +* Mapping may be used to convert coordinates between the target +* Frame and the result Frame (see below) and the inverse +* transformation will convert in the opposite direction. +* result +* Address of a location where a pointer to a new Frame will be +* returned if the requested coordinate conversion is +* possible. If returned, this Frame describes the coordinate +* system that results from applying the returned Mapping +* (above) to the "target" coordinate system. In general, this +* Frame will combine attributes from (and will therefore be +* more specific than) both the target Frame and the current +* Frame of the template FrameSet. In particular, when the +* template allows the possibility of transformaing to any one +* of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate +* conversion is possible. Otherwise zero is returned (this will +* not in itself result in an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int match; /* Result to be returned */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame. */ + fr = astGetFrame( this, AST__CURRENT ); + +/* Invoke the astMatch method for this Frame. */ + match =astMatch( fr, target, matchsub, template_axes, target_axes, + map, result ); + +/* Annul the Frame pointer. */ + fr = astAnnul( fr ); + +/* If an error occurred, clean up by freeing any allocated memory, + annulling returned objects and clearing the returned result. */ + if ( !astOK ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + *map = astAnnul( *map ); + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void MatchAxes( AstFrame *frm1_frame, AstFrame *frm2, int *axes, + int *status ) { +/* +* Name: +* MatchAxes + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void MatchAxes( AstFrame *frm1, AstFrame *frm2, int *axes ) +* int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astMatchAxes +* method inherited from the Frame class). + +* Description: +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +* frm1 +* Pointer to the first Frame. +* frm2 +* Pointer to the second Frame. +* axes +* Pointer to an +* integer array in which to return the indices of the axes (within +* the second Frame) that correspond to each axis within the first +* Frame. Axis indices start at 1. A value of zero will be stored +* in the returned array for each axis in the first Frame that has +* no corresponding axis in the second Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the first Frame. +* status +* Pointer to inherited status value. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping +* can be found between them using astFindFrame or astConvert. Thus, +* "corresponding axes" are not necessarily identical. For instance, +* SkyFrame axes in two Frames will match even if they describe +* different celestial coordinate systems +*/ + +/* Local Variables: */ + AstFrame *frm1; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the current Frame in the FrameSet. */ + frm1 = astGetFrame( (AstFrameSet *) frm1_frame, AST__CURRENT ); + +/* Invoke the astMatchAxesX on the second Frame. */ + astMatchAxesX( frm2, frm1, axes ); + +/* Free resources */ + frm1 = astAnnul( frm1 ); +} + +static void MatchAxesX( AstFrame *frm2_frame, AstFrame *frm1, int *axes, + int *status ) { +/* +* Name: +* MatchAxesX + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void MatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes ) +* int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astMatchAxesX +* method inherited from the Frame class). + +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +* frm2 +* Pointer to the second Frame. +* frm1 +* Pointer to the first Frame. +* axes +* Pointer to an integer array in which to return the indices of +* the axes (within the first Frame) that correspond to each axis +* within the second Frame. Axis indices start at 1. A value of zero +* will be stored in the returned array for each axis in the second +* Frame that has no corresponding axis in the first Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the second Frame. +* status +* Pointer to inherited status value. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping +* can be found between them using astFindFrame or astConvert. Thus, +* "corresponding axes" are not necessarily identical. For instance, +* SkyFrame axes in two Frames will match even if they describe +* different celestial coordinate systems +*/ + +/* Local Variables: */ + AstFrame *frm2; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the current Frame in the FrameSet. */ + frm2 = astGetFrame( (AstFrameSet *) frm2_frame, AST__CURRENT ); + +/* Invoke the astMatchAxesX on the current Frame. */ + astMatchAxesX( frm2, frm1, axes ); + +/* Free resources */ + frm2 = astAnnul( frm2 ); +} + +static void MirrorVariants( AstFrameSet *this, int iframe, int *status ) { +/* +*++ +* Name: +c astMirrorVariants +f AST_MIRRORVARIANTS + +* Purpose: +* Make the current Frame mirror the variant Mappings in another Frame. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astMirrorVariants( AstFrameSet *this, int iframe, int *status ) +f CALL AST_MIRRORVARIANTS( THIS, IFRAME, STATUS ) + +* Class Membership: +* FrameSet method. + +* Description: +c This function +f This routine +* indicates that all access to the Variant attribute of the current +* Frame should should be forwarded to some other nominated Frame in +* the FrameSet. For instance, if a value is set subsequently for the +* Variant attribute of the current Frame, the current Frame will be left +* unchanged and the setting is instead applied to the nominated Frame. +* Likewise, if the value of the Variant attribute is requested, the +* value returned is the value stored for the nominated Frame rather +* than the current Frame itself. +* +* This provides a mechanism for propagating the effects of variant +* Mappings around a FrameSet. If a new Frame is added to a FrameSet +* by connecting it to an pre-existing Frame that has two or more variant +* Mappings, then it may be appropriate to set the new Frame so that it +* mirrors the variants Mappings of the pre-existing Frame. If this is +* done, then it will be possible to select a specific variant Mapping +* using either the pre-existing Frame or the new Frame. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FrameSet. +c iframe +f IFRAME = INTEGER (Given) +* The index of the Frame within the FrameSet which is to be +* mirrored by the current Frame. This value should lie in the range +* from 1 to the number of Frames in the FrameSet (as given by its +* Nframe attribute). If AST__NOFRAME is supplied (or the current +* Frame is specified), then any mirroring established by a previous +* call to this +c function +f routine +* is disabled. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Mirrors can be chained. That is, if Frame B is set to be a mirror +* of Frame A, and Frame C is set to be a mirror of Frame B, then +* Frame C will act as a mirror of Frame A. +* - Variant Mappings cannot be added to the current Frame if it is +* mirroring another Frame. So calls to the +c astAddVariant function +f AST_ADDVARIANT routine +* will cause an error to be reported if the current Frame is +* mirroring another Frame. +* - A value of AST__BASE may be given for the +c "iframe" parameter +f IFRAME argument +* to specify the base Frame. +* - Any variant Mappings explicitly added to the current Frame using +c astAddVariant +f AST_ADDVARIANT +* will be ignored if the current Frame is mirroring another Frame. + +*-- +*/ + +/* Local Variables: */ + int icur; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the current Frame index. */ + icur = astGetCurrent( this ); + +/* If AST__NOFRAME, disable any mirroring. */ + if( iframe == AST__NOFRAME ) { + this->varfrm[ icur - 1 ] = 0; + +/* Otherwise, validate and translate the Frame index supplied. */ + } else { + iframe = astValidateFrameIndex( this, iframe, "astMirrorVariants" ); + +/* If the current Frame has been specified, disable any mirroring. */ + if( iframe == icur ) { + this->varfrm[ icur - 1 ] = 0; + +/* Otherwise, store the one-based variants frame index. */ + } else { + this->varfrm[ icur - 1 ] = iframe; + } + } +} + +static void Norm( AstFrame *this_frame, double value[], int *status ) { +/* +* Name: +* Norm + +* Purpose: +* Normalise a set of FrameSet coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void Norm( AstAxis *this, double value[], int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astNorm method +* inherited from the Frame class). + +* Description: +* This function converts a set of coordinate values for the +* current Frame of a FrameSet, which might potentially be +* unsuitable for display to a user (for instance, may lie outside +* the expected range of values) into a set of acceptable +* alternative values suitable for display. +* +* Typically, for Frames whose axes represent cyclic values (such +* as angles or positions on the sky), this function wraps an +* arbitrary set of coordinates, so that they lie within the first +* cycle (say zero to 2*pi or -pi/2 to +pi/2). For Frames with +* ordinary linear axes, without constraints, this function will +* typically return the original coordinate values unchanged. + +* Parameters: +* this +* Pointer to the FrameSet. +* value +* An array of double, with one element for each FrameSet axis. +* This should contain the initial set of coordinate values, +* which will be modified in place. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to the current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astNorm method to obtain the new values. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astNorm( fr, value ); + fr = astAnnul( fr ); +} + +static void NormBox( AstFrame *this_frame, double lbnd[], double ubnd[], + AstMapping *reg, int *status ) { +/* +* Name: +* NormBox + +* Purpose: +* Extend a box to include effect of any singularities in the Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void astNormBox( AstFrame *this, double lbnd[], double ubnd[], +* AstMapping *reg, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astNormBox method inherited +* from the Frame class). + +* Description: +* This function modifies a supplied box to include the effect of any +* singularities in the co-ordinate system represented by the Frame. +* For a normal Cartesian coordinate system, the box will be returned +* unchanged. Other classes of Frame may do other things. For instance, +* a SkyFrame will check to see if the box contains either the north +* or south pole and extend the box appropriately. + +* Parameters: +* this +* Pointer to the Frame. +* lbnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* lower axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* ubnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* upper axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* reg +* A Mapping which should be used to test if any singular points are +* inside or outside the box. The Mapping should leave an input +* position unchanged if the point is inside the box, and should +* set all bad if the point is outside the box. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to the current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astNormBox method to obtain the new values. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astNormBox( fr, lbnd, ubnd, reg ); + fr = astAnnul( fr ); +} + +static void Offset( AstFrame *this_frame, const double point1[], + const double point2[], double offset, double point3[], int *status ) { +/* +* Name: +* Offset + +* Purpose: +* Calculate an offset along a geodesic curve. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "frameset.h" +* void Offset( AstFrame *this, +* const double point1[], const double point2[], +* double offset, double point3[], int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astOffset +* method inherited from the Frame class). + +* Description: +* This function finds the FrameSet coordinate values of a point +* which is offset a specified distance along the geodesic curve +* between two other points. + +* Parameters: +* this +* Pointer to the FrameSet. +* point1 +* An array of double, with one element for each FrameSet axis. +* This should contain the coordinates of the point marking the +* start of the geodesic curve. +* point2 +* An array of double, with one element for each FrameSet axis +* This should contain the coordinates of the point marking the +* end of the geodesic curve. +* offset +* The required offset from the first point along the geodesic +* curve. If this is positive, it will be towards the second +* point. If it is negative, it will be in the opposite +* direction. This offset need not imply a position lying +* between the two points given, as the curve will be +* extrapolated if necessary. +* point3 +* An array of double, with one element for each FrameSet axis +* in which the coordinates of the required point will be +* returned. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - "Bad" coordinate values will also be returned if the two +* points supplied are coincident (or otherwise fail to uniquely +* specify a geodesic curve) but the requested offset is non-zero. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astOffset method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astOffset( fr, point1, point2, offset, point3 ); + fr = astAnnul( fr ); +} + +static double Offset2( AstFrame *this_frame, const double point1[2], + double angle, double offset, double point2[2], int *status ){ +/* +* Name: +* Offset2 + +* Purpose: +* Calculate an offset along a geodesic curve in a 2D Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* double Offset2( AstFrame *this, const double point1[2], double angle, +* double offset, double point2[2], int *status ); + +* Class Membership: +* FrameSet member function (over-rides the protected astOffset2 +* method inherited from the Frame class). + +* Description: +* This function finds the Frame coordinate values of a point which +* is offset a specified distance along the geodesic curve at a +* given angle from a specified starting point. It can only be +* used with 2-dimensional Frames. +* +* For example, in a basic Frame, this offset will be along the +* straight line joining two points. For a more specialised Frame +* describing a sky coordinate system, however, it would be along +* the great circle passing through two sky positions. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of the +* point marking the start of the geodesic curve. +* angle +* The angle (in radians) from the positive direction of the second +* axis, to the direction of the required position, as seen from +* the starting position. Positive rotation is in the sense of +* rotation from the positive direction of axis 2 to the positive +* direction of axis 1. +* offset +* The required offset from the first point along the geodesic +* curve. If this is positive, it will be in the direction of the +* given angle. If it is negative, it will be in the opposite +* direction. +* point2 +* An array of double, with one element for each Frame axis +* in which the coordinates of the required point will be returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The direction of the geodesic curve at the end point. That is, the +* angle (in radians) between the positive direction of the second +* axis and the continuation of the geodesic curve at the requested +* end point. Positive rotation is in the sense of rotation from +* the positive direction of axis 2 to the positive direction of axis 1. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - An error will be reported if the Frame is not 2-dimensional. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astOffset2 method for this Frame. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astOffset2( fr, point1, angle, offset, point2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static void Overlay( AstFrame *template_frame, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template FrameSet on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astOverlay +* method inherited from the Frame class). + +* Description: +* This function overlays attributes from the current Frame of a +* FrameSet on to another Frame, so as to over-ride selected +* attributes of that second Frame. Normally only those attributes +* which have been specifically set in the template will be +* transferred. This implements a form of defaulting, in which a +* Frame acquires attributes from the template, but retains its +* original attributes (as the default) if new values have not +* previously been explicitly set in the template. + +* Parameters: +* template +* Pointer to the template FrameSet, for whose current Frame +* values should have been explicitly set for any attribute +* which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of +* the "result" Frame (see below). For each axis in the result +* frame, the corresponding element of this array should contain +* the (zero-based) index of the axis in the current Frame of +* the template FrameSet to which it corresponds. This array is +* used to establish from which template Frame axis any +* axis-dependent attributes should be obtained. +* +* If any axis in the result Frame is not associated with a +* template Frame axis, the corresponding element of this array +* should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *template; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + template = (AstFrameSet *) template_frame; + +/* Obtain a pointer to the current Frame and invoke its astOverlay + method to overlay its attributes. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( template, AST__CURRENT ); + astOverlay( fr, template_axes, result ); + fr = astAnnul( fr ); +} + +static void PermAxes( AstFrame *this_frame, const int perm[], int *status ) { +/* +* Name: +* PermAxes + +* Purpose: +* Permute the order of a FrameSet's axes. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void PermAxes( AstFrame *this, const int perm[], int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astPermAxes method +* inherited from the Frame class). + +* Description: +* This function permutes the order in which the axes in the +* current Frame of a FrameSet occur. + +* Parameters: +* this +* Pointer to the FrameSet. +* perm +* An array of int (with one element for each axis of the +* FrameSet's current Frame) which lists the axes in their new +* order. Each element of this array should be a (zero-based) +* axis index identifying the axes according to their old +* (un-permuted) order. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Only genuine permutations of the axis order are permitted, so +* each axis must be referenced exactly once in the "perm" array. +* - If more than one axis permutation is applied to the same Frame +* in a FrameSet, the effects are cumulative. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + AstPermMap *map; /* Pointer to axis permutation Mapping */ + int *invperm; /* Pointer to inverse permutation array */ + int axis; /* Loop counter for axes */ + int naxes; /* Number of FrameSet axes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the permutation array, to check that it describes a + genuine permutation. */ + astCheckPerm( this, perm, "astPermAxes" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astPermAxes method to permute its axes. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astPermAxes( fr, perm ); + fr = astAnnul( fr ); + +/* Obtain the number of axes in the FrameSet's current Frame and allocate + memory to hold an inverse permutation array. */ + naxes = astGetNaxes( this ); + invperm = astMalloc( sizeof( int ) * (size_t) naxes ); + +/* Fill the inverse permutation array with values that will invert the + axis permutation supplied. */ + if ( astOK ) { + for ( axis = 0; axis < naxes; axis++ ) invperm[ perm[ axis ] ] = axis; + +/* Create a PermMap that will permute coordinate values in the same way as + the current Frame's axes have been permuted. */ + map = astPermMap( naxes, invperm, naxes, perm, NULL, "", status ); + +/* Modify the Frame's relationship to the rest of the Frames in the + FrameSet so that the correct coordinate values remain associated + with the permuted axes. */ + astRemapFrame( this, AST__CURRENT, map ); + +/* Annul the PermMap and free the inverse permutation array. */ + map = astAnnul( map ); + } + invperm = astFree( invperm ); +} + +static AstFrame *PickAxes( AstFrame *this_frame, int naxes, const int axes[], + AstMapping **map, int *status ) { +/* +* Name: +* PickAxes + +* Purpose: +* Create a new Frame by picking axes from a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstFrame *PickAxes( AstFrame *this, int naxes, const int axes[], +* AstMapping **map, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astPickAxes protected +* method inherited from the Frame class). + +* Description: +* This function creates a new Frame whose axes are copies of axes +* picked from the current Frame of an existing FrameSet. Other +* Frame attributes are also copied from this current Frame to the +* new Frame. Zero or more of the original axes may be picked in +* any order, but each can be used only once. Additional axes (with +* default characteristics) may be included in the new Frame if +* required. +* +* Optionally, a Mapping that converts between the original Frame's +* axes and those of the new Frame may also be returned. + +* Parameters: +* this +* Pointer to the FrameSet. +* naxes +* The number of axes required in the new Frame. +* axes +* Pointer to an array of int with naxes elements. This should +* contain (zero based) axis indices specifying the axes which +* are to be included in the new Frame, in the order +* required. Each axis index may occur only once. +* +* If additional (default) axes are also to be included, the +* corresponding elements of this array should be set to -1. +* map +* Address of a location to receive a pointer to a new +* Mapping. This will be a PermMap (or a UnitMap as a special +* case) that describes the axis permutation that has taken +* place between the current Frame of the FrameSet and the new +* Frame. The forward transformation will convert from the +* original FrameSet's axes to the new one's, and vice versa. +* +* If this Mapping is not required, a NULL value may be supplied +* for this parameter. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new Frame. + +* Notes: +* - The class of object returned may differ from that of the +* original current Frame, depending on which axes are +* selected. For example, if a single axis is picked from a +* SkyFrame (which always has two axes), the resulting Frame cannot +* be a valid SkyFrame, so will revert to the parent class (Frame) +* instead. +* - The new Frame contains a deep copy of all the data selected +* from the original current Frame. Modifying the new Frame will +* therefore not affect the FrameSet or the Frames it contains. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrame *frame; /* Pointer to Frame to be returned */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + +/* Initialise the returned pointers. */ + if ( map ) *map = NULL; + frame = NULL; + +/* Check the global error status. */ + if ( !astOK ) return frame; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Check that a valid set of axes is being selected . */ + astValidateAxisSelection( this, naxes, axes, "astPickAxes" ); + +/* Obtain a pointer to the FrameSet's current Frame and use its + astPickAxes method to obtain the required new Frame and + Mapping. Annul the current Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + frame = astPickAxes( fr, naxes, axes, map ); + fr = astAnnul( fr ); + +/* If an error occurred, annul the Mapping pointer (if requested) and + the new Frame pointer. */ + if ( !astOK ) { + if ( map ) *map = astAnnul( *map ); + frame = astAnnul( frame ); + } + +/* Return the pointer to the new Frame. */ + return frame; +} + +static void PrimaryFrame( AstFrame *this_frame, int axis1, + AstFrame **frame, int *axis2, int *status ) { +/* +* Name: +* PrimaryFrame + +* Purpose: +* Uniquely identify a primary Frame and one of its axes. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void PrimaryFrame( AstFrame *this, int axis1, AstFrame **frame, +* int *axis2, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected +* astPrimaryFrame method inherited from the Frame class). + +* Description: +* This function returns information about the underlying (primary) +* Frame corresponding to a specified axis of the current Frame of +* a FrameSet, when this current Frame may be a compound Frame +* composed of more than one simpler one. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis1 +* An axis index (zero-based) identifying the axis of the +* FrameSet's current Frame for which information is required. +* frame +* Address of a location to receive a pointer to the underlying +* (primary) frame to which the requested axis belongs +* (i.e. this will not be a compound Frame). +* axis2 +* Pointer to an int which is to receive the axis index within +* "frame" which identifies the axis being referred to, using +* the axis order that applied when the primary Frame was +* originally constructed (i.e. this function undoes all +* subsequent axis pemutations and the effects of combining +* Frames, in order to reveal the original underlying axis +* order). +* status +* Pointer to the inherited status variable. + +* Notes: +* - This protected method is provided so that class +* implementations can distinguish the axes of Frames from one +* another (e.g. can distinguish a longitude axis as being +* different from a latitide axis) even after their order has been +* permuted and they have been combined with axes from other +* Frames. +* - The reference count of the primary Frame will be incremented +* by one to reflect the new pointer returned. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Initialise the returned values. */ + *frame = NULL; + *axis2 = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index supplied. */ + (void) astValidateAxis( this, axis1, 1, "astPrimaryFrame" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke its + astPrimaryFrame method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astPrimaryFrame( fr, axis1, frame, axis2 ); + fr = astAnnul( fr ); + +/* If an error occurred, annul the returned object and clear the + returned axis value. */ + if ( !astOK ) { + *frame = astAnnul( *frame ); + *axis2 = 0; + } +} + +static double Rate( AstMapping *this_mapping, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astRate method +* inherited from the Frame class). + +* This function evaluates the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. +* +* The result is estimated by interpolating the function using a +* fourth order polynomial in the neighbourhood of the specified +* position. The size of the neighbourhood used is chosen to minimise +* the RMS residual per unit length between the interpolating +* polynomial and the supplied Mapping function. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* astRate() +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + AstMapping *map; /* Pointer to the base->current Mapping */ + double result; /* Returned rate of change */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_mapping; + +/* Obtain the Mapping between the base and current Frames in the + FrameSet (note this takes account of whether the FrameSet has been + inverted). */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Invoke the astRate method on the Mapping. */ + result = astRate( map, at, ax1, ax2 ); + +/* Annul the Mapping pointer. */ + map = astAnnul( map ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void RecordIntegrity( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* RecordIntegrity + +* Purpose: +* Record the current integrity state of a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void RecordIntegrity( AstFrameSet *this, int *status ) + +* Class Membership: +* FrameSet member function. + +* Description: +* This function makes a record of the current integrity state of a +* FrameSet by taking a copy of its current Frame (it stores a +* pointer to this copy in a static variable). If the current Frame +* is subsequently modified, the RestoreIntegrity function can then +* attempt to restore the FrameSet's integrity to this recorded +* state by appropriately remapping its current Frame. + +* Parameters: +* this +* Pointer to the FrameSet. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *current; /* Pointer to current Frame */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise the record of the FrameSet's integrity. */ + integrity_frame = NULL; + integrity_lost = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet's current Frame. */ + current = astGetFrame( this, AST__CURRENT ); + +/* Make a copy of this Frame, storing its pointer. */ + integrity_frame = astCopy( current ); + +/* Annul the current Frame pointer. */ + current = astAnnul( current ); +} + +static void RemapFrame( AstFrameSet *this, int iframe, AstMapping *map, int *status ) { +/* +*++ +* Name: +c astRemapFrame +f AST_REMAPFRAME + +* Purpose: +* Modify a Frame's relationship to other Frames in a FrameSet. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astRemapFrame( AstFrameSet *this, int iframe, AstMapping *map ) +f CALL AST_REMAPFRAME( THIS, IFRAME, MAP, STATUS ) + +* Class Membership: +* FrameSet method. + +* Description: +c This function modifies the relationship (i.e. Mapping) between a +f This routine modifies the relationship (i.e. Mapping) between a +* specified Frame in a FrameSet and the other Frames in that +* FrameSet. +* +* Typically, this might be required if the FrameSet has been used +* to calibrate (say) an image, and that image is re-binned. The +* Frame describing the image will then have undergone a coordinate +* transformation, and this should be communicated to the associated +c FrameSet using this function. +f FrameSet using this routine. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FrameSet. +c iframe +f IFRAME = INTEGER (Given) +* The index within the FrameSet of the Frame to be modified. +* This value should lie in the range from 1 to the number of +* Frames in the FrameSet (as given by its Nframe attribute). +c map +f MAP = INTEGER (Given) +* Pointer to a Mapping whose forward transformation converts +* coordinate values from the original coordinate system +* described by the Frame to the new one, and whose inverse +* transformation converts in the opposite direction. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - A value of AST__BASE or AST__CURRENT may be given for the +c "iframe" parameter to specify the base Frame or the current +f IFRAME argument to specify the base Frame or the current +* Frame respectively. +* - The relationship between the selected Frame and any other +c Frame within the FrameSet will be modified by this function, +f Frame within the FrameSet will be modified by this routine, +* but the relationship between all other Frames in the FrameSet +* remains unchanged. +* - The number of input coordinate values accepted by the Mapping +* (its Nin attribute) and the number of output coordinate values +* generated (its Nout attribute) must be equal and must match the +* number of axes in the Frame being modified. +* - If a simple change of axis order is required, then the +c astPermAxes function may provide a more straightforward method +f AST_PERMAXES routine may provide a more straightforward method +* of making the required changes to the FrameSet. +c - This function cannot be used to change the number of Frame +f - This routine cannot be used to change the number of Frame +* axes. To achieve this, a new Frame must be added to the FrameSet +c (astAddFrame) and the original one removed if necessary +c (astRemoveFrame). +f (AST_ADDFRAME) and the original one removed if necessary +f (AST_REMOVEFRAME). +* - Any variant Mappings associated with the remapped Frame (except +* for the current variant) will be lost as a consequence of calling this +* method (see attribute "Variant"). +*-- +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to Frame */ + int icur; /* Index of original current Frame */ + int naxes; /* Number of Frame axes */ + int nin; /* Number of Mapping input coordinates */ + int nout; /* Number of Mapping output coordinates */ + int varfrm; /* The index of the variants frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate and translate the Frame index supplied. */ + iframe = astValidateFrameIndex( this, iframe, "astRemapFrame" ); + +/* Variant Mappings from a source node to a destination node are stored + within the Frame object associated with the destination node. But + remapping a Frame causes the Frame to be dissociated from its original + node, and associated with a new node, leaving the original node + without any Frame in which to store its variant mappings. So we are + forced to remove the variant Mappings if the Frame is remapped. We do + this by clearing the Variant attribute before the Frame is remapped. + This will leave the current variant as the sole Mapping between the + original source and destination nodes. However, if the Frame being + remapped is just a mirror for another Frame, then we do not need to + do this since the Frame being mirrored is not itself being remapped + and so can retain its variant mappings. So we temporarily prevent the + remapped Frame from acting as a mirror before we clear the Variant + attribute. */ + icur = astGetCurrent( this ); + astSetCurrent( this, iframe ); + + varfrm = this->varfrm[ iframe - 1 ]; + this->varfrm[ iframe - 1 ] = 0; + + astClearVariant( this ); + + this->varfrm[ iframe - 1 ] = varfrm; + astSetCurrent( this, icur ); + +/* Obtain the number of input and output coordinates per point for the + Mapping supplied. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* Obtain a pointer to the specified Frame and determine how many axes + it has. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, iframe ); + naxes = astGetNaxes( fr ); + fr = astAnnul( fr ); + +/* Check that the number of input coordinates matches the number of + Frame axes and report an error if necessary. */ + if ( astOK ) { + if ( nin != naxes ) { + astError( AST__NCPIN, "astRemapFrame(%s): Bad number of %s input " + "coordinate values (%d).", status, astGetClass( this ), + astGetClass( map ), nin ); + astError( AST__NCPIN, "The %s given should accept %d coordinate " + "value%s for each input point.", status, astGetClass( map ), naxes, + ( naxes == 1 ) ? "" : "s" ); + +/* Similarly, check that the number of output coordinates matches the + number of Frame axes. */ + } else if ( nout != naxes ) { + astError( AST__NCPIN, "astRemapFrame(%s): Bad number of %s output " + "coordinate values (%d).", status, astGetClass( this ), + astGetClass( map ), nout ); + astError( AST__NCPIN, "The %s given should generate %d " + "coordinate value%s for each output point.", status, + astGetClass( map ), naxes, ( naxes == 1 ) ? "" : "s" ); + } + } + +/* If there is more than one Frame present in the FrameSet, extend the + FrameSet arrays to hold a new node. */ + if ( astOK && ( this->nframe > 1 ) ) { + this->map = astGrow( this->map, this->nnode, sizeof( AstMapping * ) ); + this->link = astGrow( this->link, this->nnode, sizeof( int ) ); + this->invert = astGrow( this->invert, this->nnode, sizeof( int ) ); + +/* Clone and store a pointer to the Mapping. */ + if ( astOK ) { + this->map[ this->nnode - 1 ] = astClone( map ); + +/* Add a new "link" element showing that the new node is derived from + that of the old Frame and store the current value of the Invert + attribute for the Mapping. */ + this->link[ this->nnode - 1 ] = this->node[ iframe - 1 ]; + this->invert[ this->nnode - 1 ] = astGetInvert( map ); + +/* Increment the node count and associate the modified Frame with the + new node. */ + if ( astOK ) { + this->nnode++; + this->node[ iframe - 1 ] = this->nnode - 1; + +/* Tidy the resulting set of nodes, because the node originally + referenced by the Frame may no longer be needed. This also + simplifies any compound Mapping which may result if this node is + removed. */ + TidyNodes( this, status ); + } + } + } +} + +static void RemoveFrame( AstFrameSet *this, int iframe, int *status ) { +/* +*++ +* Name: +c astRemoveFrame +f AST_REMOVEFRAME + +* Purpose: +* Remove a Frame from a FrameSet. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "frameset.h" +c void astRemoveFrame( AstFrameSet *this, int iframe ) +f CALL AST_REMOVEFRAME( THIS, IFRAME, STATUS ) + +* Class Membership: +* FrameSet method. + +* Description: +c This function removes a Frame from a FrameSet. All other Frames +f This routine removes a Frame from a FrameSet. All other Frames +* in the FrameSet have their indices re-numbered from one (if +* necessary), but are otherwise unchanged. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the FrameSet. +c iframe +f IFRAME = INTEGER (Given) +* The index within the FrameSet of the Frame to be removed. +* This value should lie in the range from 1 to the number of +* Frames in the FrameSet (as given by its Nframe attribute). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Removing a Frame from a FrameSet does not affect the +* relationship between other Frames in the FrameSet, even if they +* originally depended on the Frame being removed. +* - The number of Frames in a FrameSet cannot be reduced to zero. +* An error will result if an attempt is made to remove the only +* remaining Frame. +* - A value of AST__BASE or AST__CURRENT may be given for the +c "iframe" parameter to specify the base Frame or the current +f IFRAME argument to specify the base Frame or the current +* Frame respectively. +* - If a FrameSet's base or current Frame is removed, the Base or +* Current attribute (respectively) of the FrameSet will have its +* value cleared, so that another Frame will then assume its role +* by default. +* - If any other Frame is removed, the base and current Frames +* will remain the same. To ensure this, the Base and/or Current +* attributes of the FrameSet will be changed, if necessary, to +* reflect any change in the indices of these Frames. +*-- +*/ + +/* Local Variables: */ + int ifr; /* Loop counter for Frames */ + int ii; /* Base/current Frame index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate and translate the Frame index supplied. */ + iframe = astValidateFrameIndex( this, iframe, "astRemoveFrame" ); + if ( astOK ) { + +/* Reject any attempt to remove the final Frame from the FrameSet. */ + if ( this->nframe == 1 ) { + astError( AST__REMIN, "astRemoveFrame(%s): Invalid attempt to " + "remove the only Frame in a %s.", status, astGetClass( this ), + astGetClass( this ) ); + +/* If OK, annul the pointer to the selected Frame. */ + } else { + this->frame[ iframe - 1 ] = astAnnul( this->frame[ iframe - 1 ] ); + +/* Ensure that the variant Mappings in the Frame being removed are not + mirrored by any other Frames in the FrameSet. */ + RemoveMirrors( this, iframe, status ); + +/* Any Frames that are mirroring variants in Frames higher than the + removed Frame need to have their mirror frame indices decremented. */ + for ( ifr = 1; ifr <= this->nframe; ifr++ ) { + if( this->varfrm[ ifr - 1 ] > iframe ) this->varfrm[ ifr - 1 ]--; + } + +/* Loop to move all subsequent Frame pointers down in the FrameSet's + "frame" array to close the resulting gap. Also move the associated + "node" and "varfrm" array contents in the same way. */ + for ( ifr = iframe; ifr < this->nframe; ifr++ ) { + this->frame[ ifr - 1 ] = this->frame[ ifr ]; + this->node[ ifr - 1 ] = this->node[ ifr ]; + this->varfrm[ ifr - 1 ] = this->varfrm[ ifr ]; + } + this->frame[ this->nframe - 1 ] = NULL; + this->node[ this->nframe - 1 ] = -1; + this->varfrm[ this->nframe - 1 ] = 0; + +/* Decrement the Frame count. */ + this->nframe--; + +/* Tidy the nodes in the FrameSet. */ + TidyNodes( this, status ); + +/* If the Base attribute is set and the removed Frame was the base + Frame, then clear the attribute value so that a new base Frame will + be selected by default. */ + if ( astTestBase( this ) ) { + ii = astGetBase( this ); + if ( iframe == ii ) { + astClearBase( this ); + +/* If the index of the removed Frame is smaller than the base Frame + index, then decrement the Base attribute so that the same base + Frame will be used in future. */ + } else if ( iframe < ii ) { + astSetBase( this, ii - 1 ); + } + } + +/* Repeat the above procedure for the current Frame. */ + if ( astTestCurrent( this ) ) { + ii = astGetCurrent( this ); + if ( iframe == ii ) { + astClearCurrent( this ); + } else if ( iframe < ii ) { + astSetCurrent( this, ii - 1 ); + } + } + } + } +} + +static void RemoveMirrors( AstFrameSet *this, int iframe, int *status ) { +/* +* Name: +* RemoveMirrors + +* Purpose: +* Ensure no other Frames act as mirrors for a specified Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void RemoveMirrors( AstFrameSet *this, int iframe, int *status ) + +* Class Membership: +* Private function. + +* Description: +* This function searchs the FrameSet for Frames that are currently +* acting as mirrors for the variant Mappings in the Frame with index +* "iframe", and disables mirroring in any found Frames. It should be +* used when "iframe" has its variant Mappings removed. + +* Parameters: +* this +* Pointer to the FrameSet. +* iframe +* One-based index of a Frame that has had its variant Mappings +* removed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int *frmlist; + int ifr; + int nfrm; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Iniitalise a list to hold the indices of the FRames that mirror + "iframe". */ + nfrm = 0; + frmlist = NULL; + +/* Check each Frame in the FrameSet. */ + for( ifr = 1; ifr <= this->nframe; ifr++ ) { + +/* Get the index of the Frame that defines the variant Mappings to use + with Frame "ifr". If this is "iframe", then add "ifr" to the list of + Frames that need to be "de-mirrored". We cannot "de-mirror" the Frame + immediately as doing so may break a chain of mirrors, resulting in the + Frames higher up the chain no longer being associated with "iframe". */ + if( GetVarFrm( this, ifr, status ) == iframe ) { + frmlist = astGrow( frmlist, nfrm + 1, sizeof( *frmlist ) ); + if( astOK ) frmlist[ nfrm++ ] = ifr; + } + } + +/* Loop round all the Frames found above that mirror "iframe". */ + for( ifr = 0; ifr < nfrm; ifr++ ) { + +/* Indicate that the Frame no longer mirrors any other Frame. */ + this->varfrm[ frmlist[ ifr ] - 1 ] = 0; + } + +/* Free the list. */ + frmlist = astFree( frmlist ); +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* FrameSet method (over-rides the astRemoveRegions method inherited +* from the Mapping class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a FrameSet) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel FrameSet), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the FrameSet class invokes the +* astRemoveRegions method on all the component Frames and Mappings, +* and joins the results together into a new FrameSet. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame **newfrms; /* Array of new Frames */ + AstFrameSet *new; /* Pointer to new FrameSet */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + AstMapping **newmaps; /* Array of new Mappings */ + AstMapping *result; /* Result pointer to return */ + int changed; /* Has any mapping been changed? */ + int i; /* Loop count */ + int nax; /* Number of Frame axes */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the FrameSet. */ + this = (AstFrameSet *) this_mapping; + +/* Allocate arrays to hold the modified Mapping and Frame pointers. */ + newmaps = astMalloc( sizeof( AstMapping *)*( this->nnode - 1 ) ); + newfrms = astMalloc( sizeof( AstFrame *)*( this->nframe ) ); + if( astOK ) { + +/* Invoke the astRemoveRegions method on all the component Mappings. */ + changed = 0; + for( i = 0; i < this->nnode - 1; i++ ) { + newmaps[ i ] = astRemoveRegions( this->map[ i ] ); + +/* Note if any Mapping was changed. */ + if( newmaps[ i ] != this->map[ i ] ) { + changed = 1; + +/* The implementation of the astRemoveRegions method provided by the + Region class returns a Frame rather than a UnitMap. But we need + Mappings here, not Frames. So if the new Mapping is a Frame, replace + it with an equivalent UnitMap. */ + if( astIsAFrame( newmaps[ i ] ) ) { + nax = astGetNin( newmaps[ i ] ); + (void) astAnnul( newmaps[ i ] ); + newmaps[ i ] = (AstMapping *) astUnitMap( nax, " ", status ); + } + } + } + +/* Invoke the astRemoveRegions method on all the component Frames. */ + for( i = 0; i < this->nframe; i++ ) { + newfrms[ i ] = astRemoveRegions( this->frame[ i ] ); + +/* Note if any Frame was changed. */ + if( newfrms[ i ] != this->frame[ i ] ) changed = 1; + } + +/* If no component was modified, just return a clone of the supplied + pointer. */ + if( ! changed ) { + result = astClone( this ); + +/* Otherwise, we need to create a new FrameSet to return. We take a deep + copy of the supplied FrameSet and then modify the Mappings and Frames + so that we retain any extra information in the supplied FrameSet. */ + } else { + new = astCopy( this ); + + for( i = 0; i < this->nnode - 1; i++ ) { + (void) astAnnul( new->map[ i ] ); + new->map[ i ] = astClone( newmaps[ i ] ); + } + + for( i = 0; i < this->nframe; i++ ) { + (void) astAnnul( new->frame[ i ] ); + new->frame[ i ] = astClone( newfrms[ i ] ); + } + + result = (AstMapping *) new; + } + +/* Free resources. */ + for( i = 0; i < this->nnode - 1; i++ ) { + newmaps[ i ] = astAnnul( newmaps[ i ] ); + } + + for( i = 0; i < this->nframe; i++ ) { + newfrms[ i ] = astAnnul( newfrms[ i ] ); + } + + } + + newfrms = astFree( newfrms ); + newmaps = astFree( newmaps ); + +/* Annul the returned Mapping if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void ReportPoints( AstMapping *this_mapping, int forward, + AstPointSet *in_points, AstPointSet *out_points, int *status ) { +/* +* Name: +* ReportPoints + +* Purpose: +* Report the effect of transforming a set of points using a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void ReportPoints( AstMapping *this, int forward, +* AstPointSet *in_points, AstPointSet *out_points, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astReportPoints +* method inherited from the Frame class). + +* Description: +* This function reports the coordinates of a set of points before +* and after being transformed by a FrameSet, by writing them to +* standard output. + +* Parameters: +* this +* Pointer to the FrameSet. +* forward +* A non-zero value indicates that the FrameSet's forward +* coordinate transformation has been applied, while a zero +* value indicates the inverse transformation. +* in_points +* Pointer to a PointSet which is associated with the +* coordinates of a set of points before the FrameSet was +* applied. +* out_points +* Pointer to a PointSet which is associated with the +* coordinates of the same set of points after the FrameSet has +* been applied. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *base_frame; /* Pointer to current Frame */ + AstFrame *current_frame; /* Pointer to base Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double **ptr_in; /* Pointer to array of input data pointers */ + double **ptr_out; /* Pointer to array of output data pointers */ + int coord; /* Loop counter for coordinates */ + int ncoord_in; /* Number of input coordinates per point */ + int ncoord_out; /* Number of output coordinates per point */ + int npoint; /* Number of points to report */ + int npoint_in; /* Number of input points */ + int npoint_out; /* Number of output points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_mapping; + +/* Obtain the numbers of points and coordinates associated with each + PointSet. */ + npoint_in = astGetNpoint( in_points ); + npoint_out = astGetNpoint( out_points ); + ncoord_in = astGetNcoord( in_points ); + ncoord_out = astGetNcoord( out_points ); + +/* Obtain the pointers that give access to the coordinate data + associated with each PointSet. */ + ptr_in = astGetPoints( in_points ); + ptr_out = astGetPoints( out_points ); + +/* In the event that both PointSets don't contain equal numbers of + points (this shouldn't actually happen), simply use the minimum + number. */ + npoint = ( npoint_in < npoint_out ) ? npoint_in : npoint_out; + +/* Obtain pointers to the FrameSet's base and current Frames. */ + base_frame = astGetFrame( this, AST__BASE ); + current_frame = astGetFrame( this, AST__CURRENT ); + +/* Loop to report the effect of the transformation on each point in + turn. */ + if ( astOK ) { + for ( point = 0; point < npoint; point++ ) { + +/* Report the input coordinates (in parentheses and separated by + commas). Format each value for display using the appropriate + Frame's astFormat method. */ + printf( "(" ); + for ( coord = 0; coord < ncoord_in; coord++ ) { + printf( "%s%s", coord ? ", " : "", + astFormat( forward ? base_frame : current_frame, + coord, ptr_in[ coord ][ point ] ) ); + } + +/* Similarly report the output coordinates, this time formatting + values using the other Frame's astFormat method. */ + printf( ") --> (" ); + for ( coord = 0; coord < ncoord_out; coord++ ) { + printf( "%s%s", coord ? ", " : "", + astFormat( forward ? current_frame : base_frame, + coord, ptr_out[ coord ][ point ] ) ); + } + printf( ")\n" ); + } + } + +/* Annul the Frame pointers. */ + base_frame = astAnnul( base_frame ); + current_frame = astAnnul( current_frame ); +} + +static void Resolve( AstFrame *this_frame, const double point1[], + const double point2[], const double point3[], + double point4[], double *d1, double *d2, int *status ){ +/* +* Name: +* Resolve + +* Purpose: +* Resolve a vector into two orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void Resolve( AstFrame *this, const double point1[], +* const double point2[], const double point3[], +* double point4[], double *d1, double *d2, int *status ); + +* Class Membership: +* FrameSet member function (over-rides the protected astResolve +* method inherited from the Frame class). + +* Description: +* This function resolves a vector into two perpendicular components. +* The vector from point 1 to point 2 is used as the basis vector. +* The vector from point 1 to point 3 is resolved into components +* parallel and perpendicular to this basis vector. The lengths of the +* two components are returned, together with the position of closest +* aproach of the basis vector to point 3. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vector to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* point3 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the vector to be +* resolved. +* point4 +* An array of double, with one element for each Frame axis +* in which the coordinates of the point of closest approach of the +* basis vector to point 3 will be returned. +* d1 +* The address of a location at which to return the distance from +* point 1 to point 4 (that is, the length of the component parallel +* to the basis vector). Positive values are in the same sense as +* movement from point 1 to point 2. +* d2 +* The address of a location at which to return the distance from +* point 4 to point 3 (that is, the length of the component +* perpendicular to the basis vector). The value is always positive. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Each vector used in this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the required +* output values are undefined. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astResolve method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astResolve( fr, point1, point2, point3, point4, d1, d2 ); + fr = astAnnul( fr ); + +} + +static AstPointSet *ResolvePoints( AstFrame *this_frame, const double point1[], + const double point2[], AstPointSet *in, + AstPointSet *out, int *status ) { +/* +* Name: +* ResolvePoints + +* Purpose: +* Resolve a set of vectors into orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstPointSet *astResolvePoints( AstFrame *this, const double point1[], +* const double point2[], AstPointSet *in, +* AstPointSet *out ) + +* Class Membership: +* FrameSet member function (over-rides the astResolvePoints method +* inherited from the Frame class). + +* Description: +* This function takes a Frame and a set of vectors encapsulated +* in a PointSet, and resolves each one into two orthogonal components, +* returning these two components in another PointSet. +* +* This is exactly the same as the public astResolve method, except +* that this method allows many vectors to be processed in a single call, +* thus reducing the computational cost of overheads of many +* individual calls to astResolve. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vectors to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* in +* Pointer to the PointSet holding the ends of the vectors to be +* resolved. +* out +* Pointer to a PointSet which will hold the length of the two +* resolved components. A NULL value may also be given, in which +* case a new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. The first axis will +* hold the lengths of the vector components parallel to the basis vector. +* These values will be signed (positive values are in the same sense as +* movement from point 1 to point 2. The second axis will hold the lengths +* of the vector components perpendicular to the basis vector. These +* values will always be positive. + +* Notes: +* - The number of coordinate values per point in the input +* PointSet must match the number of axes in the supplied Frame. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and 2 coordinate values per point. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + AstFrame *fr; /* Pointer to current Frame */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astResolvePoints method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astResolvePoints( this, point1, point2, in, out ); + fr = astAnnul( fr ); + +/* Return a pointer to the output PointSet. */ + return result; + +} + +static void RestoreIntegrity( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* RestoreIntegrity + +* Purpose: +* Restore a previous integrity state for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void RestoreIntegrity( AstFrameSet *this ) + +* Class Membership: +* FrameSet member function. + +* Description: +* This function restores a FrameSet to a previous integrity state, +* as recorded (in static variables) by a previous invocation of +* the RecordIntegrity function. It does this by appropriately +* remapping the FrameSet's current Frame, if this appears +* necessary. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Notes: +* - The previous record of the FrameSet's integrity state (as +* recorded by RecordIntegrity) is deleted by this function, even +* if it is invoked with the global error status set. +* - An error will result if the previous integrity state cannot be +* restored. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *current; /* Pointer to current Frame */ + AstFrameSet *cvt; /* Pointer to conversion FrameSet */ + AstMapping *map; /* Pointer to conversion Mapping */ + int flags; /* Flags associated with current frame */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Check that a previous record of the FrameSet's integrity state has + been made. Do not modify the FrameSet if it appears that the + previous integrity state has not been lost (i.e. that the current + Frame has not been modified), nor if there is only one Frame + present. Check the global error status. */ + if ( integrity_frame && integrity_lost && ( astGetNframe( this ) > 1 ) && + astOK ) { + +/* Obtain a pointer to the current Frame. */ + current = astGetFrame( this, AST__CURRENT ); + +/* Since we need to obtain a conversion between the recorded copy of + this Frame and the current one, we must match their Domain + attributes (otherwise conversion cannot be performed). Do this by + changing the recorded copy as necessary. */ + if ( astTestDomain( current ) ) { + astSetDomain( integrity_frame, astGetDomain( current ) ); + } else { + astClearDomain( integrity_frame ); + } + +/* Temporarily set both Frames AST__INTFLAG flag to indicate that the + following call to astConvert is part of the process of restoring a + FrameSet's integrity. Some classes of Frame (e.g. DSBSpecFrames) may + choose to return a different Mapping in this case. */ + astSetFrameFlags( integrity_frame, astGetFrameFlags( integrity_frame ) + | AST__INTFLAG ); + flags = astGetFrameFlags( current ); + astSetFrameFlags( current, flags | AST__INTFLAG ); + +/* Obtain the required conversion FrameSet, restore the original frame + flags and annul the current Frame pointer. */ + cvt = astConvert( integrity_frame, current, "" ); + astSetFrameFlags( current, flags ); + current = astAnnul( current ); + +/* If no conversion could be found, then the FrameSet's integrity + state cannot be restored, so report an error. */ + if ( !cvt ) { + if( astOK ) { + astError( AST__ILOST, "%s(%s): Cannot maintain %s integrity.", status, + integrity_method, astGetClass( this ), + astGetClass( this ) ); + } + +/* Otherwise, obtain a pointer to the conversion Mapping. */ + } else { + map = astGetMapping( cvt, AST__BASE, AST__CURRENT ); + +/* If the Mapping is not a UnitMap (i.e. a null Mapping), then use it + to remap the FrameSet's current Frame. */ + if ( strcmp( astGetClass( map ), "UnitMap" ) ) { + astRemapFrame( this, AST__CURRENT, map ); + } + +/* Annul the conversion Mapping and Frameset pointers. */ + map = astAnnul( map ); + cvt = astAnnul( cvt ); + } + } + +/* Delete the recorded integrity information by annulling the original + copy of the current Frame (thus deleting it) and resetting the + associated modification flag. */ + if ( integrity_frame ) integrity_frame = astAnnul( integrity_frame ); + integrity_lost = 0; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* FrameSet member function (extends the astSetAttrib method +* inherited from the Frame class). + +* Description: +* This function assigns an attribute value for a FrameSet, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the FrameSet. +* setting +* Pointer to a null terminated string specifying the new +* attribute value. +* status +* Pointer to the inherited status variable. + +* Attributes: +* The set of attribute values is not fixed and is determined by +* the current Frame. In addition, the FrameSet class defines the +* following attributes: +* +* Base (integer) +* Current (integer) + +* Notes: +* - This protected method is intended to be invoked by the Object +* astSet method and makes additional attributes accessible to it. +* - All attribute settings passed to this function are simply +* passed on to the corresponding method for the FrameSet's current +* Frame. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to a Frame within the FrameSet */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *dom; /* Pointer to Domain string */ + int base; /* Base attribute value */ + int base_off; /* Offset of Base value string */ + int current; /* Current attribute value */ + int current_off; /* Offset of Current value string */ + int id; /* Offset of ID string */ + int invert; /* Invert attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + int nfrm; /* Number of Frames in FrameSet */ + int report; /* Report attribute value */ + int variant; /* Offset of Variant string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* We first handle attributes that apply to the FrameSet as a whole + (rather than to the current Frame). */ + +/* Base. */ +/* ----- */ +/* Read as an integer. */ + if ( nc = 0, + ( 1 == astSscanf( setting, "base= %d %n", &base, &nc ) ) + && ( nc >= len ) ) { + astSetBase( this, base ); + +/* Also allow a string. */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "base= %n%*s %n", &base_off, &nc ) ) + && ( nc >= len ) ) { + +/* Check for "AST__CURRENT" or "Current". */ + if ( astChrMatch( "AST__CURRENT", setting + base_off ) || + astChrMatch( "Current", setting + base_off ) ) { + astSetBase( this, AST__CURRENT ); + +/* Check for "AST__BASE" or "Base" (possible, although not very + useful). */ + } else if ( astChrMatch( "AST__BASE", setting + base_off ) || + astChrMatch( "Base", setting + base_off ) ) { + +/* If the FrameSet contains a Frame with the given Domain name, make it + the base Frame. */ + } else { + nfrm = astGetNframe( this ); + for( base = 1; base <= nfrm; base++ ) { + fr = astGetFrame( this, base ); + dom = astGetDomain( fr ); + fr = astAnnul( fr ); + if( astChrMatch( dom, setting + base_off ) ) break; + } + + if( base <= nfrm ) { + astSetBase( this, base ); + +/* Report an error if the value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid index value for " + "Base Frame \"%s\".", status, + astGetClass( this ), setting + base_off ); + } + } + +/* Current. */ +/* -------- */ +/* Since this determines the choice of current Frame, we must restore + the integrity state of the FrameSet before changing this attribute + and record the new integrity state afterwards. */ + +/* Read as an integer. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "current= %d %n", ¤t, &nc ) ) + && ( nc >= len ) ) { + RestoreIntegrity( this, status ); + astSetCurrent( this, current ); + RecordIntegrity( this, status ); + +/* Also allow a string. */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "current= %n%*s %n", + ¤t_off, &nc ) ) + && ( nc >= len ) ) { + +/* Check for "AST__BASE" or "Base". */ + if ( astChrMatch( "AST__BASE", setting + current_off ) || + astChrMatch( "Base", setting + current_off ) ) { + RestoreIntegrity( this, status ); + astSetCurrent( this, AST__BASE ); + RecordIntegrity( this, status ); + +/* Check for "AST__CURRENT" or "Current" (possible, although not very + useful). */ + } else if ( astChrMatch( "AST__CURRENT", setting + current_off ) || + astChrMatch( "Current", setting + current_off ) ) { + +/* If the FrameSet contains a Frame with the given Domain name, make it + the current Frame. */ + } else { + nfrm = astGetNframe( this ); + for( current = 1; current <= nfrm; current++ ) { + fr = astGetFrame( this, current ); + dom = astGetDomain( fr ); + fr = astAnnul( fr ); + if( astChrMatch( dom, setting + current_off ) ) break; + } + + if( current <= nfrm ) { + RestoreIntegrity( this, status ); + astSetCurrent( this, current ); + RecordIntegrity( this, status ); + +/* Report an error if the value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid index value for " + "Current Frame \"%s\".", status, + astGetClass( this ), setting + current_off ); + } + } + +/* ID. */ +/* --- */ + } else if ( nc = 0, ( 0 == astSscanf( setting, "id=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetID( this, setting + id ); + +/* Ident. */ +/* ------ */ + } else if ( nc = 0, ( 0 == astSscanf( setting, "ident=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetIdent( this, setting + id ); + +/* Invert. */ +/* ------- */ +/* Since this affects the choice of current Frame, we must restore the + integrity state of the FrameSet before changing this attribute and + record the new integrity state afterwards. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "invert= %d %n", &invert, &nc ) ) + && ( nc >= len ) ) { + RestoreIntegrity( this, status ); + astSetInvert( this, invert ); + RecordIntegrity( this, status ); + +/* Report. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "report= %d %n", &report, &nc ) ) + && ( nc >= len ) ) { + astSetReport( this, report ); + +/* Variant. */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "variant=%n%*[^\n]%n", &variant, &nc ) ) + && ( nc >= len ) ) { + astSetVariant( this, setting + variant ); + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + } else if ( MATCH( "allvariants" ) || + MATCH( "class" ) || + MATCH( "nframe" ) || + MATCH( "nin" ) || + MATCH( "nobject" ) || + MATCH( "nout" ) || + MATCH( "refcount" ) || + MATCH( "tranforward" ) || + MATCH( "traninverse" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass unrecognised settings on to the FrameSet's current Frame for + further interpretation. */ + } else { + +/* Force a copy to be made of the current Frame, if needed, to make it + independent of other Frames within the FrameSet. */ + (void) ForceCopy( this, AST__CURRENT, status ); + +/* Obtain a pointer to the current Frame and invoke its astSetAttrib + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astSetAttrib( fr, setting ); + fr = astAnnul( fr ); + +/* Note that the current Frame has been modified. */ + integrity_lost = 1; + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void SetAxis( AstFrame *this_frame, int axis, AstAxis *newaxis, int *status ) { +/* +* Name: +* SetAxis + +* Purpose: +* Set a new Axis for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void SetAxis( AstFrame *this, int axis, AstAxis *newaxis, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astSetAxis method +* inherited from the Frame class). + +* Description: +* This function allows a new Axis object to be associated with one +* of the axes of the current Frame in a FrameSet, replacing the +* previous one. Each Axis object contains a description of the +* quantity represented along one of the Frame's axes, so this +* function allows this description to be exchanged for another +* one. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The index (zero-based) of the axis whose associated Axis +* object is to be replaced. +* newaxis +* Pointer to the new Axis object. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index supplied. */ + (void) astValidateAxis( this, axis, 1, "astSetAxis" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astSetAxis method to assign the new Axis object. Annul the + Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astSetAxis( fr, axis, newaxis ); + fr = astAnnul( fr ); +} + +static void SetBase( AstFrameSet *this, int iframe, int *status ) { +/* +*+ +* Name: +* astSetBase + +* Purpose: +* Set a value for the Base attribute of a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* void astSetBase( AstFrameSet *this, int iframe ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function sets a value for the Base attribute of a FrameSet. This +* value is an index that identifies the base Frame for the FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. +* iframe +* Value to be set for the Base attribute. + +* Notes: +* - A value of AST__BASE or AST__CURRENT may be given for the +* "iframe" parameter to identify the base Frame or the current +* Frame respectively. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate and translate the Frame index supplied. */ + iframe = astValidateFrameIndex( this, iframe, "astSetBase" ); + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, set the base Frame index, otherwise + set the current Frame index instead. */ + if ( astOK ) *( invert ? &this->current : &this->base ) = iframe; +} + +static void SetCurrent( AstFrameSet *this, int iframe, int *status ) { +/* +*+ +* Name: +* astSetCurrent + +* Purpose: +* Set a value for the Current attribute of a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* int astSetCurrent( AstFrameSet *this, int iframe ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function sets a value for the Current attribute of a +* FrameSet. This attribute is an index that identifies the current +* Frame for the FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. +* iframe +* Value to be set for the Current attribute. + +* Notes: +* - A value of AST__BASE or AST__CURRENT may be given for the +* "iframe" parameter to identify the base Frame or the current +* Frame respectively. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate and translate the Frame index supplied. */ + iframe = astValidateFrameIndex( this, iframe, "astSetCurrent" ); + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, set the current frame index, otherwise + set the base Frame index instead. */ + if ( astOK ) *( invert ? &this->base : &this->current ) = iframe; +} + +static void SetVariant( AstFrameSet *this, const char *variant, int *status ) { +/* +*+ +* Name: +* astSetVariant + +* Purpose: +* Set a value for the Variant attribute of a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* void astSetVariant( AstFrameSet *this, const char *variant ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function sets a value for the Variant attribute of a FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. +* variant +* Value to be set for the Variant attribute. + +* Notes: +* - An error will be reported if the supplied variant name cannot be +* found in the Variants FrameSet associated with the current Frame. + +*- +*/ + +/* Local Variables: */ + AstCmpMap *map6; + AstCmpMap *map5; + AstCmpMap *map4; + AstFrame *frm; + AstFrame *vfrm; + AstFrameSet *tfs; + AstFrameSet *vfs; + AstMapping *map0; + AstMapping *map2; + AstMapping *map3; + AstMapping *map1; + char *myvar; + const char *dom; + int icur; + int ifrm; + int inode; + int inv0; + int inv; + int nfrm; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a copy of the supplied string and clean it. */ + myvar = astStore( NULL, variant, strlen( variant ) + 1 ); + astRemoveLeadingBlanks( myvar ); + astChrCase( NULL, myvar, 1, 0 ); + if( astOK ) { + myvar[ astChrLen( myvar ) ] = 0; + +/* Get the one-based index of the Frame that defines the available + variant Mappings. */ + icur = GetVarFrm( this, astGetCurrent( this ), status ); + +/* Get the variants FrameSet from the Frame selected above. */ + frm = astGetFrame( this, icur ); + vfs = astGetFrameVariants( frm ); + +/* If there is no variants FrameSet in the Frame, the only allowed value + for "Variant" is the Domain name of the current Frame. */ + if( ! vfs ) { + dom = astGetDomain( this ); + if( astOK && strcmp( myvar, dom ) ) { + astError( AST__ATTIN, "astSetVariant(%s): Unknown Frame " + "variant '%s' requested.", status, astGetClass( this ), + myvar ); + } + +/* If there is a variants FrameSet in the Frame... */ + } else { + +/* Find the index of the Frame in the Variants FrameSet that has a Domain + equal to myvar. */ + nfrm = astGetNframe( vfs ); + for( ifrm = 0; ifrm < nfrm; ifrm++ ) { + vfrm = astGetFrame( vfs, ifrm + 1 ); + dom = astGetDomain( vfrm ); + vfrm = astAnnul( vfrm ); + if( !astOK || !strcmp( myvar, dom ) ) break; + } + +/* Report an error if no such Frame found. */ + if( ifrm == nfrm && astOK ) { + astError( AST__ATTIN, "astSetVariant(%s): Unknown Frame " + "variant '%s' requested - available variants are " + "'%s'.", status, astGetClass(this), myvar, + astGetAllVariants(this) ); + +/* Otherwise, get a Mapping from the current Frame in "this" to the + currently selected Variant Frame. We cannot assume that they are the + same as attributes of the current Frame (e.g. System) may have been + changed since the variant was added. If the required Frame is already + the current Frame, there is nothing more to do since the required + variant is already selected. */ + } else if( ifrm + 1 != astGetCurrent( vfs ) ){ + vfrm = astGetFrame( vfs, AST__CURRENT ); + dom = astGetDomain( frm ); + if( dom ) dom = astStore( NULL, dom, strlen( dom ) + 1 ); + astSetDomain( frm, astGetDomain( vfrm ) ); + tfs = astConvert( frm, vfrm, "" ); + astSetDomain( frm, dom ); + if( tfs ) { + map1 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + tfs = astAnnul( tfs ); + vfrm = astAnnul( vfrm ); + +/* Get the Mapping from the original Variant Frame to the requested variant + Frame. */ + map2 = astGetMapping( vfs, AST__CURRENT, ifrm + 1 ); + +/* Get a Mapping from the new variant Frame to the current Frame in "this". */ + vfrm = astGetFrame( vfs, ifrm + 1 ); + astSetDomain( frm, astGetDomain( vfrm ) ); + tfs = astConvert( vfrm, frm, "" ); + astSetDomain( frm, dom ); + if( tfs ) { + map3 = astGetMapping( tfs, AST__BASE, AST__CURRENT ); + tfs = astAnnul( tfs ); + +/* Concatentate the three Mappings, to get the Mapping from the old + variant Frame to the new variant Frame. */ + map4 = astCmpMap( map1, map2, 1, " ", status ); + map5 = astCmpMap( map4, map3, 1, " ", status ); + +/* Now we modify the Mapping in the FrameSet. First get the index of the node + with which the Frame is associated. */ + inode = this->node[ icur - 1 ]; + +/* Get the Mapping that generates the node values, and its Invert flag. */ + map0 = this->map[ inode - 1 ]; + inv0 = this->invert[ inode - 1 ]; + +/* Temporarily reset the invert flag in the Mapping to account for any + changes made to the Mapping via other pointers. */ + inv = astGetInvert( map0 ); + astSetInvert( map0, inv0 ); + +/* Concatentate with "map5" to get the Mapping form the the parent node + to the new variant of the current node. */ + map6 = astCmpMap( map0, map5, 1, " ", status ); + +/* Simplify it and use it to replace the Mapping in the FrameSet structure. */ + this->map[ inode - 1 ] = astSimplify( map6 ); + this->invert[ inode - 1 ] = astGetInvert( this->map[ inode - 1 ] ); + +/* Re-instate the original Invert flag and free the old Mapping pointer. */ + astSetInvert( map0, inv ); + map0 = astAnnul( map0 ); + +/* Make the variant Frame the current Frame within the Variants FrameSet. */ + astSetCurrent( vfs, ifrm + 1 ); + +/* Free resources. */ + map6 = astAnnul( map6 ); + map5 = astAnnul( map5 ); + map4 = astAnnul( map4 ); + map3 = astAnnul( map3 ); + +/* Report an error if a Mapping cannot be found from the new variant Frame + to the current Frame in "this". */ + } else if( astOK ) { + astError( AST__INTER, "astSetVariant(%s): Cannot convert " + "from a %s with Domain '%s' to a %s with Domain " + "'%s' (internal programming error).", status, + astGetClass( this ), astGetClass( vfrm ), + astGetDomain( vfrm ), astGetClass( frm ), + astGetDomain( frm ) ); + } + +/* Free resources. */ + map2 = astAnnul( map2 ); + map1 = astAnnul( map1 ); + +/* Report an error if a Mapping cannot be found from the current Frame in + "this" to the current Variant Frame. */ + } else if( astOK ) { + astError( AST__INTER, "astSetVariant(%s): Cannot convert " + "from a %s with Domain '%s' to a %s with Domain " + "'%s' (internal programming error).", status, + astGetClass( this ), astGetClass( frm ), + astGetDomain( frm ), astGetClass( vfrm ), + astGetDomain( vfrm ) ); + } + +/* Free resources. */ + vfrm = astAnnul( vfrm ); + dom = astFree( (void *) dom ); + } + +/* Annul the pointer to the Variants FrameSet. */ + vfs = astAnnul( vfs ); + } + +/* Annul the pointer to the current Frame in "this". */ + frm = astAnnul( frm ); + } + +/* Free the memory holding the cleaned variant name. */ + myvar = astFree( myvar ); +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mappings in a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* FrameSet method (over-rides the astSimplify method inherited +* from the Frame class). + +* Description: +* This function simplifies the Mappings in a FrameSet to eliminate +* redundant computational steps, or to merge separate steps which +* can be performed more efficiently in a single operation. + +* Parameters: +* this +* Pointer to the original FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new pointer to the (possibly simplified) FrameSet. If +* simplification was not possible, this will be a cloned pointer +* to the original FrameSet. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrameSet *new; /* Pointer to new (simpler?) FrameSet */ + AstFrameSet *this; /* Pointer to original FrameSet structure */ + AstMapping *map; /* Pointer to Mapping */ + AstMapping *result; /* Result pointer to return */ + AstMapping *tmp; /* Temporary Mapping pointer */ + int inode; /* Loop counter for FrameSet nodes */ + int inv; /* Mapping Invert attribute value */ + int invert; /* Invert flag value */ + int set; /* Invert attribute set? */ + int simpler; /* Simplification achieved? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_mapping; + +/* Make a copy of the FrameSet, since we may alter it (this is a deep + copy, which is a minor limitation of the current implementation). */ + new = astCopy( this ); + +/* Loop to examine each of the Mappings between the Frames in the + copy. */ + simpler = 0; + for ( inode = 1; astOK && ( inode < new->nnode ); inode++ ) { + +/* Obtain the Mapping pointer and associated invert flag. */ + map = new->map[ inode - 1 ]; + invert = new->invert[ inode - 1 ]; + +/* Determine if the Mapping's Invert attribute is set, and obtain its + value. */ + set = astTestInvert( map ); + inv = astGetInvert( map ); + +/* If necessary, set the required value for the Invert attribute. */ + if ( inv != invert ) astSetInvert( map, invert ); + +/* Simplify the Mapping. */ + tmp = astSimplify( map ); + +/* If necessary, restore the original state of the Mapping's Invert + attribute. */ + if ( inv != invert ) { + if ( set ) { + astSetInvert( map, inv ); + } else { + astClearInvert( map ); + } + } + +/* Test if simplification was performed. */ + if ( astOK ) { + if ( tmp != map ) { + +/* If so, annul the original Mapping pointer and substitute the new + one. Also set a new invert flag to accompany it. */ + (void) astAnnul( new->map[ inode - 1 ] ); + new->map[ inode - 1 ] = astClone( tmp ); + new->invert[ inode - 1 ] = astGetInvert( tmp ); + +/* Note if any Mapping within the FrameSet is simplified. */ + simpler = 1; + } + } + +/* Annul the pointer to the simplified Mapping. */ + tmp = astAnnul( tmp ); + } + +/* If simplification was possible, clone a pointer to the new + FrameSet. Otherwise clone a pointer to the original one. */ + if ( astOK ) result = astClone( simpler ? new : this ); + +/* Annul the new FrameSet pointer. */ + new = astAnnul( new ); + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int Span( AstFrameSet *this, AstFrame **frames, int inode1, int inode2, + int avoid, AstMapping **map, int *forward, int *status ) { +/* +* Name: +* Span + +* Purpose: +* Find a path between two nodes in a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int Span( AstFrameSet *this, AstFrame **frames, int inode1, int inode2, +* int avoid, AstMapping **map, int *forward, int *status ) + +* Class Membership: +* FrameSet member function. + +* Description: +* This function searches a FrameSet to identify a path between two +* specified nodes. It returns an array of pointers to each Mapping +* in the path, along with direction information, so that an +* overall Mapping between the two nodes can be constructed. + +* Parameters: +* this +* Pointer to the FrameSet. +* frames +* Pointer to an array of Frame pointers, indexed by node index. +* Nodes which have no associated Frame will have a NULL pointer +* stored in this array. +* inode1 +* Zero based index of the starting node. +* inode2 +* Zero based index of the ending node. +* avoid +* Zero based index which identifies a node which is to be +* avoided (i.e. the initial step in the path should not be via +* this node). This value is required because the function +* invokes itself recursively; it provides a mechanism to +* prevent searches proceeding back down paths that have already +* been searched. External callers should provide a value of -1, +* which indicates that all possible paths should initially be +* explored. +* map +* Pointer to the start of an array that will be filled with a +* series of pointers to Mappings which must be applied in turn +* in order to transform between the two Frames. External +* callers should ensure that this array contains at least as many +* elements as there are Mappings and Frames in the FrameSet (one less +* than the number of nodes plus the number of Frames). +* +* Note that the pointers are simply copies of addresses from +* the FrameSet's "map" array. They are not cloned, so should +* not be annulled by the caller. +* forward +* Pointer to the start of an array of int that will be filled +* with boolean flags (0 or 1) to indicate whether the forward +* (as opposed to the inverse) transformation should be used for +* each Mapping returned in order to effect the transformation +* between the starting and ending nodes. This array should be the +* same size as the "map" array. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The function returns one more than the number of Mappings +* required to perform the transformation, or zero if it was not +* possible to find a path between the two nodes. + +* Notes: +* - If a node has an associated Frame, the Frame usually represents a +* UnitMap and so can be ignored. The exception is if the Frame is +* actually a Region (or a CmpFrame containing a Region), in which case +* it represents a Mapping which returns bad values if the input position +* is outside the region. This form of Mapping should not be ignored, and +* so the returned list of Mappings includes the effect of any Frames +* along the path which are not equivalent to a UnitMap. This +* equivalence is determined by invoking the astSimplify method on the +* Frame. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +* - On the assumption that the FrameSet has been consistently +* constructed, there should be exactly one path between any pair +* of its nodes. It should not, therefore, ever fail to find a +* path except when invoked recursively to explore a subset of the +* FrameSet's nodes (this should not be visible to an external +* caller). Failure to find a path does not in itself result in an +* error condition. +*/ + +/* Local Variables: */ + AstFrame *frame; /* Pointer to Frame associated with inode1 */ + int fwd; /* Forward Mapping identified? */ + int inode; /* Loop counter for nodes */ + int inv; /* Inverse Mapping identified? */ + int invert; /* Original Mapping Invert value */ + int nextra; /* No. of extra Mappings to add to path */ + int result; /* Count of mappings (to be returned) */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* See if the two nodes are the same. */ + result = ( inode1 == inode2 ); + +/* If so, we need to consider the Mapping represented by any Frame + associated with the node. Most classes of Frames are equivalent to a + UnitMap and so can be ignored. But some (e.g. the Region class) are not + equivalent to a UnitMap and so needs to be included in the returned + Mapping list. */ + if( result ) { + result = 1; + +/* If inode1 is associated with a Frame, which is not equivalent to a + UnitMap, add the Frame as the first Mapping into the returned list. The + "forward" value is irrelevant since the forward and inverse transformations + of Frames are the same. */ + frame = frames[ inode1 ]; + if( frame ) { + if( !astIsUnitFrame( frame ) ) { + result++; + *map = (AstMapping *) frame; + *forward = 1; + } + } + +/* If the nodes are different, we now attempt to find the next step in + the path between them. Loop through all available nodes looking for + the next one to transform to (i.e. one that is directly related by + a Mapping to our starting node). */ + } else { + for ( inode = 0; inode < this->nnode; inode++ ) { + +/* Do not consider node "avoid". This prevents us re-tracing our steps + backwards when this function is invoked recursively. */ + if ( inode != avoid ) { + +/* Test if inode is derived from inode1 (if so, the Mapping associated + with inode will convert from inode1 to inode when applied in the + forward direction). */ + fwd = ( inode > 0 ) && ( this->link[ inode - 1 ] == inode1 ); + +/* Test if inode1 is derived from inode (if so, the Mapping associated + with inode1 will convert from inode1 to inode when applied in the + inverse direction). */ + inv = ( inode1 > 0 ) && ( this->link[ inode1 - 1 ] == inode ); + +/* If the nodes are directly related, we try to find a path from inode to + inode2 without going back through inode1. */ + if ( fwd || inv ) { + +/* If node1 is associated with a Frame, we need to include the Frame + as a Mapping in the returned list unless the Frame is equivalent to a + UnitMap. Note the number of slots to be reserved for node1 when we call + Span recursively below. */ + nextra = 1; + frame = frames[ inode1 ]; + if( frame && !astIsUnitFrame( frame ) ) nextra = 2; + +/* Invoke this function recursively to try and find a path from inode + to inode2 without going back through inode1. If this is possible, a + non-zero result will be returned. Store the returned Mappings and + direction information in the arrays supplied, but leave extra space to + insert information about the Mapping between nodes inode1 and inode. */ + result = Span( this, frames, inode, inode2, inode1, + map + nextra, forward + nextra, status ); + +/* If a path was found, increment the Mapping count to account for the + one that transforms between nodes inode1 and inode and insert + information for this Mapping into the output arrays. */ + if ( result ) { + result++; + nextra--; + map[ nextra ] = this->map[ ( fwd ? inode : inode1 ) - 1 ]; + forward[ nextra ] = fwd; + +/* Obtain the original value of the Invert attribute for the Mapping + between nodes inode1 and inode (recorded when the Mapping was first + added to the FrameSet). Test if this value has now changed. If so, + some external code has inverted the Mapping via another pointer, so + invert the returned direction information to compensate for + this. */ + invert = this->invert[ ( fwd ? inode : inode1 ) - 1 ]; + if ( invert != astGetInvert( map[ nextra ] ) ) { + forward[ nextra ] = !forward[ nextra ]; + } + +/* If inode1 is associated with a non-unit Frame Mapping, add the Frame + Mapping in as the first Mapping in the returned list. The "forward" value + is irrelevant since the forward and inverse transformations of Frames + are the same. */ + if( nextra ) { + result++; + *map = (AstMapping *) frame; + *forward = 1; + } + +/* Quit searching once a path has been found. */ + break; + } + } + } + } + } + +/* Return the result, which is one more than the number of mappings + found (i.e. steps in the path), or zero if no path was found (this + should only occur when invoked recursively to explore an + unsuccessful sub-path). */ + return result; +} + +static int SubFrame( AstFrame *this_frame, AstFrame *template, + int result_naxes, + const int *target_axes, const int *template_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a FrameSet and convert to the new coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int SubFrame( AstFrame *target, AstFrame *template, int result_naxes, +* const int *target_axes, const int *template_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astSubFrame +* method inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the +* axes from the current Frame of a "target" FrameSet and creates a +* new Frame with copies of the selected axes assembled in the +* requested order. It then optionally overlays the attributes of a +* "template" Frame on to the result. It returns both the resulting +* Frame and a Mapping that describes how to convert between the +* coordinate systems described by the current Frame of the target +* FrameSet and the result Frame. If necessary, this Mapping takes +* account of any differences in the Frames' attributes due to the +* influence of the template. + +* Parameters: +* target +* Pointer to the target FrameSet, from whose current Frame the +* axes are to be selected. +* template +* Pointer to the template Frame, from which new attributes for +* the result Frame are to be obtained. Optionally, this may be +* NULL, in which case no overlaying of template attributes will +* be performed. +* result_naxes +* Number of axes to be selected from the target FrameSet. This +* number may be greater than or less than the number of axes in +* the FrameSet's current Frame (or equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving +* a list of the (zero-based) axis indices of the axes to be +* selected from the current Frame of the target FrameSet. The +* order in which these are given determines the order in which +* the axes appear in the result Frame. If any of the values in +* this array is set to -1, the corresponding result axis will +* not be derived from the target FrameSet, but will be assigned +* default attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This +* should contain a list of the template axes (given as +* zero-based axis indices) with which the axes of the result +* Frame are to be associated. This array determines which axes +* are used when overlaying axis-dependent attributes of the +* template on to the result. If any element of this array is +* set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not +* used and a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned +* Mapping. The forward transformation of this Mapping will +* describe how to convert coordinates from the coordinate +* system described by the current Frame of the target FrameSet +* to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is +* possible between the current Frame of the target FrameSet and +* the result Frame. Otherwise zero is returned and *map and +* *result are returned as NULL (but this will not in itself result +* in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not +* always be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int match; /* Result to be returned */ + +/* Initialise. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame. */ + fr = astGetFrame( this, AST__CURRENT ); + +/* Invoke the astSubFrame method for this Frame. */ + match = astSubFrame( fr, template, result_naxes, target_axes, template_axes, + map, result ); + +/* Annul the Frame pointer. */ + fr = astAnnul( fr ); + +/* If an error occurred, clean up by annulling any returned objects and clear + the returned result. */ + if ( !astOK ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static AstSystemType SystemCode( AstFrame *this_frame, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astSystemCode +* method inherited from the Frame class). + +* Description: +* This function converts a string used for the external description of +* a coordinate system into a Frame coordinate system type code (System +* attribute value). It is the inverse of the astSystemString function. + +* Parameters: +* this +* Pointer to the Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the coordinate system +* description was not recognised. This does not produce an error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astSystemCode method for this Frame. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astSystemCode( fr, system ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BADSYSTEM; + +/* Return the result. */ + return result; +} + +static const char *SystemString( AstFrame *this_frame, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astSystemString +* method inherited from the Frame class). + +* Description: +* This function converts a Frame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astSystemString method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astSystemString( fr, system ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result pointer. */ + return result; + +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a FrameSet's attributes. + +* Parameters: +* this +* Pointer to the FrameSet. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* We first handle attributes that apply to the FrameSet as a whole + (rather than to the current Frame). */ + +/* Base. */ +/* ----- */ + if ( !strcmp( attrib, "base" ) ) { + result = astTestBase( this ); + +/* Current. */ +/* -------- */ + } else if ( !strcmp( attrib, "current" ) ) { + result = astTestCurrent( this ); + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + result = astTestID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astTestIdent( this ); + +/* Invert. */ +/* ------- */ + } else if ( !strcmp( attrib, "invert" ) ) { + result = astTestInvert( this ); + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + result = astTestReport( this ); + +/* Variant. */ +/* -------- */ + } else if ( !strcmp( attrib, "variant" ) ) { + result = astTestVariant( this ); + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + } else if ( !strcmp( attrib, "allvariants" ) || + !strcmp( attrib, "class" ) || + !strcmp( attrib, "nframe" ) || + !strcmp( attrib, "nin" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "nout" ) || + !strcmp( attrib, "refcount" ) || + !strcmp( attrib, "tranforward" ) || + !strcmp( attrib, "traninverse" ) ) { + result = 0; + +/* Pass unrecognised attributes on to the FrameSet's current Frame for + further interpretation. */ + } else { + +/* Obtain a pointer to the current Frame and invoke its astTestAttrib + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astTestAttrib( fr, attrib ); + fr = astAnnul( fr ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int TestBase( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astTestBase + +* Purpose: +* Determine if a value has been set for the Base attribute of a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astTestBase( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns a boolean result to indicate if a value +* has been set for the Base attribute of a FrameSet. This +* attribute is an index that identifies the base Frame in the +* FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* Zero or 1, depending on whether a value has been set. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + int result; /* Value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, test the base Frame index, otherwise + test the index of the current Frame instead. */ + if ( astOK ) { + if ( !invert ) { + result = ( this->base != -INT_MAX ); + } else { + result = ( this->current != -INT_MAX ); + } + } + +/* Return the result. */ + return result; +} + +static int TestCurrent( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astTestCurrent + +* Purpose: +* Test if a value has been set for the Current attribute of a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* int astTestCurrent( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns a boolean result to indicate whether a +* value has been set for the Current attribute of a FrameSet. +* This attribute is an index that identifies the current Frame in +* a FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* Zero or 1, depending on whether a value has been set. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int invert; /* FrameSet is inverted? */ + int result; /* Value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the FrameSet has been inverted. */ + invert = astGetInvert( this ); + +/* If it has not been inverted, test the current Frame index, + otherwise test the index of the base Frame instead. */ + if ( astOK ) { + if ( !invert ) { + result = ( this->current != -INT_MAX ); + } else { + result = ( this->base != -INT_MAX ); + } + } + +/* Return the result. */ + return result; +} + +static int TestVariant( AstFrameSet *this, int *status ) { +/* +*+ +* Name: +* astTestVariant + +* Purpose: +* Determine if a value has been set for the Variant attribute of a FrameSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astTestVariant( AstFrameSet *this ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function returns a boolean result to indicate if a value +* has been set for the Variant attribute of a FrameSet. + +* Parameters: +* this +* Pointer to the FrameSet. + +* Returned Value: +* Zero or 1, depending on whether a value has been set. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFrame *frm; + AstFrameSet *vfs; + int result; + int icur; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the one-based index of the Frame to use. */ + icur = GetVarFrm( this, astGetCurrent( this ), status ); + +/* Get a pointer to the Variants FrameSet in the current Frame. */ + frm = astGetFrame( this, icur ); + vfs = astGetFrameVariants( frm ); + +/* If it is null, return zero, otherwise 1. */ + result = vfs ? 1 : 0; + +/* Annul pointers. */ + if( vfs ) vfs = astAnnul( vfs ); + frm = astAnnul( frm ); + +/* Return the result. */ + return result; +} + +static void TidyNodes( AstFrameSet *this, int *status ) { +/* +* Name: +* TidyNodes + +* Purpose: +* Tidy the nodes in a FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void TidyNodes( AstFrameSet *this, int *status ) + +* Class Membership: +* FrameSet member function. + +* Description: +* This function tidies the nodes in a FrameSet, removing any that +* are unnecessary or represent dead-ends. It should be used after +* any changes have been made to a FrameSet that may have reduced +* the number of references to any of its nodes (either by Frames +* or by other nodes). + +* Parameters: +* this +* Pointer to the FrameSet. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstMapping *newmap; /* Pointer to simplified Mapping */ + AstMapping *tmpmap; /* Pointer to new compound Mapping */ + int ifr; /* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + int last_link[ 2 ]; /* Last nodes to reference via "link" array */ + int link_ref; /* Number of "link" array references */ + int needed; /* Node still required? */ + int next; /* Node which references the removed one */ + int remove; /* Node to be removed */ + int suspect; /* Loop counter for testing nodes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Loop to search for unnecessary nodes until no more are found. */ + needed = 0; + while ( !needed ) { + +/* Inspect each node (including node zero, which does not actually + have a Mapping associated with it) to see how many times it is + referenced. */ + for ( suspect = 0; suspect < this->nnode; suspect++ ) { + link_ref = 0; + +/* Test for at least one reference from within the "node" array which + associates Frames with particular nodes. */ + for ( ifr = 1; ifr <= this->nframe; ifr++ ) { + if ( ( needed = ( this->node[ ifr - 1 ] == suspect ) ) ) break; + } + +/* If no references were found above, look for references in the + "link" array that inter-connects all the nodes. */ + if ( !needed ) { + for ( inode = 1; inode < this->nnode; inode ++ ) { + if ( this->link[ inode - 1 ] == suspect ) { + +/* Node zero must be retained if it has more than two links + referencing it, while other nodes only require more than one. */ + if ( ( needed = ( link_ref >= ( suspect ? 1 : 2 ) ) ) ) break; + +/* Remember (up to) the first two nodes which reference the current one. */ + last_link[ link_ref++ ] = inode; + } + } + } + +/* If there were insufficient references to retain this node, we must + now decide why it should be removed. */ + if ( !needed ) { + +/* If there is no Frame associated with a node and there are less than + two links to it (for node zero), or less then one link (for other + nodes), then the there is no route to anything else via this node. + It is a dead-end. */ + if ( link_ref < ( suspect ? 1 : 2 ) ) { + +/* To tidy up, we remove the affected node or, for node zero, the + remaining one that references it. Annul the Mapping associated with + the node being removed. */ + remove = suspect ? suspect : last_link[ 0 ]; + this->map[ remove - 1 ] = astAnnul( this->map[ remove - 1 ] ); + +/* If an unnecessary node is not a dead-end, then it is a redundant + node which simply joins two Mappings. */ + } else { + +/* To tidy up, we remove the affected node or, for node zero, the + first one that references it. */ + remove = suspect ? suspect : last_link[ 0 ]; + +/* We then produce a compound Mapping which spans the gap by + concatenating the Mappings associated with the node being removed + and the remaining one which references it. For node zero, the first + of these Mappings must be inverted because there are no out-going + Mappings from node zero. */ + next = suspect ? last_link[ 0 ] : last_link[ 1 ]; + tmpmap = CombineMaps( this->map[ remove - 1 ], + this->invert[ remove - 1 ] != !suspect, + this->map[ next - 1 ], + this->invert[ next - 1 ], 1, status ); + +/* Simplify this compound Mapping. */ + newmap = astSimplify( tmpmap ); + tmpmap = astAnnul( tmpmap ); + +/* Annul the individual Mapping pointers. */ + this->map[ remove - 1 ] = astAnnul( this->map[ remove - 1 ] ); + this->map[ next - 1 ] = astAnnul( this->map[ next - 1 ] ); + +/* Install the new compound Mapping and its Invert flag. */ + this->map[ next - 1 ] = newmap; + this->invert[ next - 1 ] = astGetInvert( newmap ); + +/* Transfer the "link" value from the removed node to the one which + takes its place. */ + this->link[ next - 1 ] = this->link[ remove - 1 ]; + } + +/* Loop to move all subsequent node data down in the "map", "invert" + and "link" arrays to close the gap where a node has been + removed. */ + for ( inode = remove; inode < this->nnode - 1; inode ++ ) { + this->map [ inode - 1 ] = this->map[ inode ]; + this->link [ inode - 1 ] = this->link[ inode ]; + this->invert[ inode - 1 ] = this->invert[ inode ]; + } + this->map[ this->nnode - 2 ] = NULL; + this->link[ this->nnode - 2 ] = -1; + this->invert[ this->nnode - 2 ] = -1; + +/* Decrement the node count. */ + this->nnode--; + +/* Loop to adjust each entry in the "node" array for the change in + node numbering, re-directing references to the removed node towards + the new node zero. */ + for ( ifr = 1; ifr <= this->nframe; ifr++ ) { + if ( this->node[ ifr - 1 ] > remove ) { + this->node[ ifr - 1 ]--; + } else if ( this->node[ ifr - 1 ] == remove ) { + this->node[ ifr - 1 ] = 0; + } + } + +/* Similarly adjust each entry in the "link" array. */ + for ( inode = 1; inode < this->nnode; inode++ ) { + if ( this->link[ inode - 1 ] > remove ) { + this->link[ inode - 1 ]--; + } else if ( this->link[ inode - 1 ] == remove ) { + this->link[ inode - 1 ] = 0; + } + } + +/* Once a node has been removed, other nodes (perhaps already tested) + may no longer be needed, so quit the testing loop and start testing + again with node zero. The process terminates when no more + unnecessary nodes can be found. */ + break; + } + } + } +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the astTransform method +* inherited from the Frame class). + +* Description: +* This function takes a FrameSet and a set of points encapsulated +* in a PointSet, and applies either the forward or inverse +* coordinate transformation (if defined by the FrameSet) to the +* points. The forward transformation converts between the +* FrameSet's base Frame and its current Frame, while the inverse +* transformation converts in the opposite direction. + +* Parameters: +* this +* Pointer to the FrameSet. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - An error will result if the FrameSet supplied does not define +* the requested coordinate transformation (either forward or +* inverse). +* - The number of coordinate values per point in the input +* PointSet must match the number of input coordinates for the +* FrameSet being applied (or number of output coordinates if the +* inverse transformation is requested). This will be equal to the +* number of axes in the FrameSet's base Frame (or the current +* Frame for the inverse transformation). +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and coordinate values per point to +* accommodate the result (e.g. the number of FrameSet output +* coordinates, or number of input coordinates if the inverse +* transformation is requested). Any excess space will be ignored. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + AstMapping *map; /* Pointer to the base->current Mapping */ + AstPointSet *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_mapping; + +/* Obtain the Mapping between the base and current Frames in the + FrameSet (note this takes account of whether the FrameSet has been + inverted). */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Apply the Mapping to the input PointSet. */ + result = astTransform( map, in, forward, out ); + +/* Annul the Mapping pointer. */ + map = astAnnul( map ); + +/* If an error has occurred and a new PointSet may have been created, then + clean up by annulling it. In any case, ensure that a NULL result is + returned.*/ + if ( !astOK ) { + if ( !out ) result = astAnnul( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static int Unformat( AstFrame *this_frame, int axis, const char *string, + double *value, int *status ) { +/* +* Name: +* Unformat + +* Purpose: +* Read a formatted coordinate value for a FrameSet axis. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int Unformat( AstFrame *this, int axis, const char *string, +* double *value, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the public astUnformat +* method inherited from the Frame class). + +* Description: +* This function reads a formatted coordinate value for a FrameSet +* axis (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The number of the FrameSet axis for which the coordinate +* value is to be read (axis numbering starts at zero for the +* first axis). +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will be +* returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + double coord; /* Coordinate value read */ + int nc; /* Number of characters read */ + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astUnformat" ); + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astUnformat method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + nc = astUnformat( fr, axis, string, &coord ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the number of characters read. */ + if ( !astOK ) { + nc = 0; + +/* Otherwise, if characters were read, return the coordinate value. */ + } else if ( nc ) { + *value = coord; + } + +/* Return the number of characters read. */ + return nc; +} + +static int ValidateAxis( AstFrame *this_frame, int axis, int fwd, + const char *method, int *status ) { +/* +* Name: +* ValidateAxis + +* Purpose: +* Validate and permute a FrameSet's axis index. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int ValidateAxis( AstFrame *this, int axis, int fwd, const char *method, +* int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected +* astValidateAxis method inherited from the Frame class). + +* Description: +* This function checks the validity of an index (zero-based) which +* is to be used to address one of the coordinate axes of the +* current Frame in a FrameSet. If the index is valid, it is +* permuted using the axis permutation array associated with the +* FrameSet's current Frame and the (zero-based) permuted axis +* index is returned. This gives the index the axis had when the +* Frame was first created. If the axis index supplied is not +* valid, an error is reported and the global error status is set. + +* Parameters: +* this +* Pointer to the FrameSet. +* axis +* The axis index (zero-based) to be checked. To be valid, it +* must lie between zero and (naxes-1) inclusive, where "naxes" +* is the number of coordinate axes associated with the +* FrameSet's current Frame. +* fwd +* If non-zero, the suppplied axis index is assumed to be an +* "external" axis index, and the corresponding "internal" axis index +* is returned as the function value. Otherwise, the suppplied axis +* index is assumed to be an "internal" axis index, and the +* corresponding "external" axis index is returned as the function +* value. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The permuted axis index - either "internal" or "external" as +* specified by "fwd". + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + int naxes; /* Number of FrameSet axes */ + int result; /* Permuted axis index */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Determine the number of FrameSet axes. */ + naxes = astGetNaxes( this ); + if ( astOK ) { + +/* If the FrameSet has no axes, report an error (convert to 1-based + axis numbering for the benefit of the public interface). */ + if ( naxes == 0 ) { + astError( AST__AXIIN, "%s(%s): Invalid attempt to use an axis index " + "(%d) for a %s which has no axes.", status, method, + astGetClass( this ), axis + 1, astGetClass( this ) ); + +/* Otherwise, check the axis index for validity and report an error if + it is not valid (again, convert to 1-based axis numbering). */ + } else if ( ( axis < 0 ) || ( axis >= naxes ) ) { + astError( AST__AXIIN, "%s(%s): Axis index (%d) invalid - it should " + "be in the range 1 to %d.", status, method, astGetClass( this ), + axis + 1, naxes ); + +/* If the axis index was valid, obtain a pointer to the FrameSet's + current Frame and invoke this Frame's astValidateAxis method to + obtain the permuted axis index. Annul the Frame pointer + afterwards. */ + } else { + fr = astGetFrame( this, AST__CURRENT ); + result = astValidateAxis( fr, axis, fwd, "astValidateAxis" ); + fr = astAnnul( fr ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static void ValidateAxisSelection( AstFrame *this_frame, int naxes, + const int *axes, const char *method, int *status ) { +/* +* Name: +* ValidateAxisSelection + +* Purpose: +* Check that a set of axes selected from a Frame is valid. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void ValidateAxisSelection( AstFrame *this, int naxes, +* const int *axes, const char *method, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astValidateAxisSelection +* method inherited from the Frame class). + +* Description: +* This function checks the validity of an array of (zero-based) +* axis indices that specify a set of axes to be selected from a +* Frame. To be valid, no axis should be selected more than +* once. In assessing this, any axis indices that do not refer to +* valid Frame axes (e.g. are set to -1) are ignored. +* +* If the axis selection is valid, this function returns without further +* action. Otherwise, an error is reported and the global error status is +* set. + +* Parameters: +* this +* Pointer to the Frame. +* naxes +* The number of axes to be selected (may be zero). +* axes +* Pointer to an array of int with naxes elements that contains the +* (zero based) axis indices to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis selection. This method name is used +* solely for constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke this + Frame's astValidateAxisSelection method. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + astValidateAxisSelection( fr, naxes, axes, method ); + fr = astAnnul( fr ); + +} + +static int ValidateFrameIndex( AstFrameSet *this, int iframe, + const char *method, int *status ) { +/* +*+ +* Name: +* astValidateFrameIndex + +* Purpose: +* Validate a FrameSet Frame index number. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frameset.h" +* int astValidateFrameIndex( AstFrameSet *this, int iframe, +* const char *method ) + +* Class Membership: +* FrameSet method. + +* Description: +* This function checks a (one-based) FrameSet Frame index for +* validity. If it is not valid, an error is reported. Otherwise, +* the function returns the Frame index value, having translated +* the special values AST__CURRENT and AST__BASE into valid Frame +* indices if necessary. + +* Parameters: +* this +* Pointer to the FrameSet. +* iframe +* The Frame index. To be valid this should lie in the range 1 +* to the number of Frames in the FrameSet. In addition, the +* values AST__CURRENT and AST__BASE may be given to indicate +* the "current" and "base" Frames. These values will be +* translated into the acceptable range. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate a Frame index. This method name is used solely +* for constructing error messages. + +* Returned Value: +* The validated (one-based) Frame index. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason +* (e.g. if the Frame index is invalid). +*- +*/ + +/* Local Variables: */ + int nframe; /* Number of Frames */ + int result; /* Returned index value */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check if the base or current Frame was specified and retrieve the + required Frame index from the FrameSet. */ + if ( iframe == AST__BASE ) { + result = astGetBase( this ); + } else if ( iframe == AST__CURRENT ) { + result = astGetCurrent( this ); + +/* Otherwise, determine how many Frames there are in the FrameSet. */ + } else { + nframe = astGetNframe( this ); + if ( astOK ) { + +/* Check that the supplied index is within range and report an error + if it is not. */ + if ( ( iframe < 1 ) || ( iframe > nframe ) ) { + astError( AST__FRMIN, "%s(%s): Invalid Frame index (%d) given.", status, + method, astGetClass( this ), iframe ); + astError( AST__FRMIN, "This value should be in the range 1 to " + "%d (or AST__CURRENT or AST__BASE).", status, nframe ); + +/* If OK, return the validated index value. */ + } else { + result = iframe; + } + } + } + +/* Return the result. */ + return result; +} + +static int ValidateSystem( AstFrame *this_frame, AstSystemType system, const char *method, int *status ) { +/* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astValidateSystem +* method inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST_BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_frame; + +/* Obtain a pointer to the FrameSet's current Frame and invoke the + astValidateSystem method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this, AST__CURRENT ); + result = astValidateSystem( this, system, method ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BADSYSTEM; + +/* Return the result. */ + return result; +} + +static void VSet( AstObject *this_object, const char *settings, + char **text, va_list args, int *status ) { +/* +* Name: +* VSet + +* Purpose: +* Set values for a FrameSet's attributes. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void VSet( AstObject *this, const char *settings, char **text, +* va_list args, int *status ) + +* Class Membership: +* FrameSet member function (over-rides the protected astVSet +* method inherited from the Object class). + +* Description: +* This function assigns a set of attribute values for a FrameSet, +* the attributes and their values being specified by means of a +* string containing a comma-separated list of the form: +* +* "attribute1 = value1, attribute2 = value2, ... " +* +* Here, "attribute" specifies an attribute name and the value to +* the right of each "=" sign should be a suitable textual +* representation of the value to be assigned to that +* attribute. This will be interpreted according to the attribute's +* data type. +* +* The string supplied may also contain "printf"-style format +* specifiers identified by a "%" sign in the usual way. If +* present, these will be substituted by values supplied as +* optional arguments (as a va_list variable argument list), using +* the normal "printf" rules, before the string is used. + +* Parameters: +* this +* Pointer to the FrameSet. +* settings +* Pointer to a null-terminated string containing a +* comma-separated list of attribute settings. +* text +* Pointer to a location at which to return a pointer to dynamic +* memory holding a copy of the expanded setting string. This memory +* should be freed using astFree when no longer needed. If a NULL +* pointer is supplied, no string is created. +* args +* The variable argument list which contains values to be +* substituted for any "printf"-style format specifiers that +* appear in the "settings" string. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function preserves the integrity of the FrameSet (if +* possible) by appropriately remapping its current Frame to take +* account of its changed attribute values. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrame *save_frame; /* Saved pointer to integrity Frame */ + AstFrameSet *this; /* Pointer to FrameSet structure */ + char *fulltext; /* Pointer to expanded text string */ + const char *save_method; /* Saved pointer to method name */ + int len; /* Length of settings string */ + int ok; /* Status OK? */ + int save_lost; /* Saved integrity modified flag */ + +/* Initialise */ + if( text ) *text = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain the length of the "settings" string and test it is not + zero. If it is, there is nothing more to do. */ + len = (int) strlen( settings ); + if ( len != 0 ) { + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* This function may be invoked recursively (because astConvert, + below, constructs a FrameSet and may require that its attributes be + set). To allow this, we first save any existing FrameSet integrity + information in local variables. */ + save_frame = integrity_frame; + save_lost = integrity_lost; + save_method = integrity_method; + +/* Set the name of the method being used (for use in error + messages). */ + integrity_method = "astSet"; + +/* Record the initial integrity state of the FrameSet. */ + RecordIntegrity( this, status ); + +/* Invoke the parent astVSet method to set the FrameSet's attribute + values and note if this succeeds. */ + (*parent_vset)( this_object, settings, &fulltext, args, status ); + ok = astOK; + +/* Restore the FrameSet's integrity. */ + RestoreIntegrity( this, status ); + +/* If integrity could not be restored, then add contextual error + information. */ + if ( !astOK && ok ) { + +/* Display the message. */ + astError( astStatus, "Unable to accommodate the attribute setting " + "\"%s\".", status, fulltext ); + } + +/* Restore any saved FrameSet integrity information. */ + integrity_frame = save_frame; + integrity_lost = save_lost; + integrity_method = save_method; + +/* If the full text of the setting string is not needed, free it. + Otherwise return it. */ + if( text ) { + *text = fulltext; + } else { + fulltext = astFree( fulltext ); + } + } +} + +/* FrameSet Attributes. */ +/* -------------------- */ +/* +*att++ +* Name: +* AllVariants + +* Purpose: +* A list of the variant Mappings associated with the current Frame. + +* Type: +* Public attribute. + +* Synopsis: +* String, read-only. + +* Description: +* This attrbute gives a space separated list of the names of all the +* variant Mappings associated with the current Frame (see attribute +* "Variant"). If the current Frame has no variant Mappings, then the +* list will hold a single entry equal to the Domain name of the +* current Frame. + +* Applicability: +* FrameSet +* All FrameSets have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* Base + +* Purpose: +* FrameSet base Frame index. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute gives the index of the Frame which is to be +* regarded as the "base" Frame within a FrameSet. The default is +* the first Frame added to the FrameSet when it is created (this +* Frame always has an index of 1). +* +* When setting a new value for this attribute, a string may be +* supplied instead of an integer index. In this case a search +* is made within the FrameSet for a Frame that has its Domain +* attribute value equal to the supplied string (the comparison is +* case-insensitive). If found, the Frame is made the base Frame. +* Otherwise an error is reported. + +* Applicability: +* FrameSet +* All FrameSets have this attribute. + +* Notes: +* - Inverting a FrameSet (inverting the boolean sense of its +c Invert attribute, with the astInvert function for example) will +f Invert attribute, with the AST_INVERT routine for example) will +* interchange the values of its Base and Current attributes. +*att-- +*/ + +/* +*att++ +* Name: +* Current + +* Purpose: +* FrameSet current Frame index. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute gives the index of the Frame which is to be +* regarded as the "current" Frame within a FrameSet. The default +* is the most recent Frame added to the FrameSet (this Frame +* always has an index equal to the FrameSet's Nframe attribute). +* +* When setting a new value for this attribute, a string may be +* supplied instead of an integer index. In this case a search +* is made within the FrameSet for a Frame that has its Domain +* attribute value equal to the supplied string (the comparison is +* case-insensitive). If found, the Frame is made the current Frame. +* Otherwise an error is reported. + +* Applicability: +* FrameSet +* All FrameSets have this attribute. + +* Notes: +* - Inverting a FrameSet (inverting the boolean sense of its +c Invert attribute, with the astInvert function for example) will +f Invert attribute, with the AST_INVERT routine for example) will +* interchange the values of its Base and Current attributes. +*att-- +*/ + +/* +*att++ +* Name: +* Nframe + +* Purpose: +* Number of Frames in a FrameSet. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attrbute gives the number of Frames in a FrameSet. This +* value will change as Frames are added or removed, but will +* always be at least one. + +* Applicability: +* FrameSet +* All FrameSets have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* Variant + +* Purpose: +* Indicates which variant of the current Frame is to be used. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute can be used to change the Mapping that connects the +* current Frame to the other Frames in the FrameSet. By default, each +* Frame in a FrameSet is connected to the other Frames by a single +* Mapping that can only be changed by using the +c astRemapFrame +f AST_REMAPFRAME +* method. However, it is also possible to associate multiple Mappings +* with a Frame, each Mapping having an identifying name. If this is +* done, the "Variant" attribute can be set to indicate the name of +* the Mapping that is to be used with the current Frame. +* +* A possible (if unlikely) use-case is to create a FrameSet that can +* be used to describe the WCS of an image formed by co-adding images +* of two different parts of the sky. In such an image, each pixel contains +* flux from two points on the sky.and so the WCS for the image should +* ideally contain one pixel Frame and two SkyFrames - one describing +* each of the two co-added images. There is nothing to prevent a +* FrameSet containing two explicit SkyFrames, but the problem then arises +* of how to distinguish between them. The two primary characteristics of +* a Frame that distinguishes it from other Frames ar eits class and its +* Domain attribute value. The class of a Frame cannot be changed, but we +* could in principle use two different Domain values to distinguish the +* two SkyFrames. However, in practice it is not uncommon for application +* software to assume that SkyFrames will have the default Domain value +* of "SKY". That is, instead of searching for Frames that have a class +* of "SkyFrame", such software searches for Frames that have a Domain +* of "SKY". To alleviate this problem, it is possible to add a single +* SkyFrame to the FrameSet, but specifying two alternate Mappings to +* use with the SkyFrame. Setting the "Variant" attribute to the name +* of one or the other of these alternate Mappings will cause the +* SkyFrame to be remapped within the FrameSet so that it uses the +* specified Mapping. The same facility can be used with any class of +* Frame, not just SkyFrames. +* +* To use this facility, the Frame should first be added to the +* FrameSet in the usual manner using the +c astAddFrame method. By default, the Mapping supplied to astAddFrame +f AST_ADDFRAME method. By default, the Mapping supplied to AST_ADDVARIANT +* is assigned a name equal to the Domain name of the Frame. To assign a +* different name to it, the +c astAddVariant +f AST_ADDVARIANT +* method should then be called specifying the required name and a NULL +* Mapping. The +c astAddFrame +f AST_ADDFRAME +* method should then be called repeatedly to add each required extra +* Mapping to the current Frame, supplying a unique name for each one. +* +* Each Frame in a FrameSet can have its own set of variant Mappings. +* To control the Mappings in use with a specific Frame, you need first +* to make it the current Frame in the FrameSet. +* +* The +c astMirrorVariants function +f AST_MIRRORVARIANTS routine +* allows the effects of variant Mappings associated with a nominated +* Frame to be propagated to other Frames in the FrameSet. +* +* Once this has been done, setting a new value for the "Variant" +* attribute of a FrameSet will cause the current Frame in the +* FrameSet to be remapped to use the specified variant Mapping. An +* error will be reported if the current Frame has no variant Mapping +* with the supplied name. +* +* Getting the value of the "Variant" attribute will return the name +* of the variant Mapping currently in use with the current Frame. If +* the Frame has no variant Mappings, the value will default to the +* Domain name of the current Frame. +* +* Clearing the "Variant" attribute will have the effect of removing +* all variant Mappings (except for the currently selected Mapping) from +* the current Frame. +* +* Testing the "Variant" attribute will return +c a non-zero value +f .TRUE. +* if the current Frame contains any variant Mappings, and +c zero +f .FALSE. +* otherwise. +* +* A complete list of the names associated with all the available +* variant Mappings in the current Frame can be obtained from the +* AllVariants attribute. +* +* If a Frame with variant Mappings is remapped using the +c astRemapFrame +f AST_REMAPFRAME +* method, the currently selected variant Mapping is used by +c astRemapFrame +f AST_REMAPFRAME +* and the other variant Mappings are removed from the Frame. + +* Applicability: +* FrameSet +* All FrameSets have this attribute. +*att-- +*/ + +/* Access to attributes of the current Frame. */ +/* ------------------------------------------ */ +/* Use the macros defined at the start of this file to implement + private member functions that give access to the attributes of the + current Frame of a FrameSet and its axes. These functions over-ride + the attribute access methods inherited from the Frame class. */ + +/* Clear, Get, Set and Test axis-independent Frame attributes. */ +MAKE_CLEAR(Digits) +MAKE_CLEAR(Domain) +MAKE_CLEAR(MatchEnd) +MAKE_CLEAR(MaxAxes) +MAKE_CLEAR(MinAxes) +MAKE_CLEAR(Permute) +MAKE_CLEAR(PreserveAxes) +MAKE_CLEAR(Title) + +MAKE_GET(Digits,int) +MAKE_GET(Domain,const char *) +MAKE_GET(MatchEnd,int) +MAKE_GET(MaxAxes,int) +MAKE_GET(MinAxes,int) +MAKE_GET(Permute,int) +MAKE_GET(PreserveAxes,int) +MAKE_GET(Title,const char *) +MAKE_SET(Digits,int) +MAKE_SET(Domain,const char *) +MAKE_SET(MatchEnd,int) +MAKE_SET(MaxAxes,int) +MAKE_SET(MinAxes,int) +MAKE_SET(Permute,int) +MAKE_SET(PreserveAxes,int) +MAKE_SET(Title,const char *) +MAKE_TEST(Digits) +MAKE_TEST(Domain) +MAKE_TEST(MatchEnd) +MAKE_TEST(MaxAxes) +MAKE_TEST(MinAxes) +MAKE_TEST(Permute) +MAKE_TEST(PreserveAxes) +MAKE_TEST(Title) + +MAKE_GET(ActiveUnit,int) +MAKE_SET(ActiveUnit,int) +MAKE_TEST(ActiveUnit) + +MAKE_GET(System,AstSystemType) +MAKE_SET(System,AstSystemType) +MAKE_TEST(System) +MAKE_CLEAR(System) + +MAKE_GET(AlignSystem,AstSystemType) +MAKE_SET(AlignSystem,AstSystemType) +MAKE_TEST(AlignSystem) +MAKE_CLEAR(AlignSystem) + +MAKE_GET(Epoch,double) +MAKE_SET(Epoch,double) +MAKE_TEST(Epoch) +MAKE_CLEAR(Epoch) + +MAKE_GET(ObsLon,double) +MAKE_SET(ObsLon,double) +MAKE_TEST(ObsLon) +MAKE_CLEAR(ObsLon) + +MAKE_GET(ObsLat,double) +MAKE_SET(ObsLat,double) +MAKE_TEST(ObsLat) +MAKE_CLEAR(ObsLat) + +MAKE_GET(ObsAlt,double) +MAKE_SET(ObsAlt,double) +MAKE_TEST(ObsAlt) +MAKE_CLEAR(ObsAlt) + +/* Clear, Get, Set and Test axis-dependent Frame attributes. */ +MAKE_CLEAR_AXIS(Direction) +MAKE_CLEAR_AXIS(Format) +MAKE_CLEAR_AXIS(Label) +MAKE_CLEAR_AXIS(Symbol) +MAKE_CLEAR_AXIS(Unit) +MAKE_GET_AXIS(Direction,int) +MAKE_GET_AXIS(Format,const char *) +MAKE_GET_AXIS(Label,const char *) +MAKE_GET_AXIS(Symbol,const char *) +MAKE_GET_AXIS(Unit,const char *) +MAKE_SET_AXIS(Direction,int) +MAKE_SET_AXIS(Format,const char *) +MAKE_SET_AXIS(Label,const char *) +MAKE_SET_AXIS(Symbol,const char *) +MAKE_SET_AXIS(Unit,const char *) +MAKE_TEST_AXIS(Direction) +MAKE_TEST_AXIS(Format) +MAKE_TEST_AXIS(Label) +MAKE_TEST_AXIS(Symbol) +MAKE_TEST_AXIS(Unit) + +MAKE_GET_AXIS(Bottom,double) +MAKE_SET_AXIS(Bottom,double) +MAKE_TEST_AXIS(Bottom) +MAKE_CLEAR_AXIS(Bottom) + +MAKE_GET_AXIS(Top,double) +MAKE_SET_AXIS(Top,double) +MAKE_TEST_AXIS(Top) +MAKE_CLEAR_AXIS(Top) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for FrameSet objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for FrameSet objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstFrameSet *in; /* Pointer to input FrameSet */ + AstFrameSet *out; /* Pointer to output FrameSet */ + int iframe; /* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output FrameSets. */ + in = (AstFrameSet *) objin; + out = (AstFrameSet *) objout; + +/* For safety, first clear any references to the input memory from + the output FrameSet. */ + out->frame = NULL; + out->varfrm = NULL; + out->node = NULL; + out->map = NULL; + out->link = NULL; + out->invert = NULL; + +/* Allocate memory in the output FrameSet to store the Frame and node + information and copy scalar information across. */ + out->frame = astMalloc( sizeof( AstFrame * ) * (size_t) in->nframe ); + out->varfrm = astStore( NULL, in->varfrm, sizeof( int ) * + (size_t) in->nframe ); + out->node = astStore( NULL, in->node, sizeof( int ) * + (size_t) in->nframe ); + out->map = astMalloc( sizeof( AstMapping * ) * (size_t) ( in->nnode - 1 ) ); + out->link = astStore( NULL, in->link, sizeof( int ) * + (size_t) ( in->nnode - 1 ) ); + out->invert = astStore( NULL, in->invert, sizeof( int ) * + (size_t) ( in->nnode - 1 ) ); + +/* If OK, make copies of each input Frame and Mapping and store the + resulting pointers in the output FrameSet. */ + if ( astOK ) { + for ( iframe = 0; iframe < in->nframe; iframe++ ) { + out->frame[ iframe ] = astCopy( in->frame[ iframe ] ); + } + for ( inode = 0; inode < in->nnode - 1; inode++ ) { + out->map[ inode ] = astCopy( in->map[ inode ] ); + } + +/* If an error occurred while copying any of these objects, clean up + by looping through the arrays of pointers again and annulling them + all. */ + if ( !astOK ) { + for ( iframe = 0; iframe < in->nframe; iframe++ ) { + out->frame[ iframe ] = astAnnul( out->frame[ iframe ] ); + } + for ( inode = 0; inode < in->nnode - 1; inode++ ) { + out->map[ inode ] = astAnnul( out->map[ inode ] ); + } + } + } + +/* If an error occurred, clean up by freeing all memory allocated above. */ + if ( !astOK ) { + out->frame = astFree( out->frame ); + out->varfrm = astFree( out->varfrm ); + out->node = astFree( out->node ); + out->map = astFree( out->map ); + out->link = astFree( out->link ); + out->invert = astFree( out->invert ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for FrameSet objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for FrameSet objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to FrameSet */ + int iframe; /* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) obj; + +/* Annul all Frame pointers and clear the node numbers associated with + them. */ + for ( iframe = 0; iframe < this->nframe; iframe++ ) { + this->frame[ iframe ] = astAnnul( this->frame[ iframe ] ); + this->node[ iframe ] = 0; + } + +/* Annul all Mapping pointers and clear the links between pairs of + nodes and the associated Mapping Invert information. */ + for ( inode = 0; inode < this->nnode - 1; inode++ ) { + this->map[ inode ] = astAnnul( this->map[ inode ] ); + this->link[ inode ] = 0; + this->invert[ inode ] = 0; + } + +/* Free all allocated memory. */ + this->frame = astFree( this->frame ); + this->varfrm = astFree( this->varfrm ); + this->node = astFree( this->node ); + this->map = astFree( this->map ); + this->link = astFree( this->link ); + this->invert = astFree( this->invert ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for FrameSet objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the FrameSet class to an output Channel. + +* Parameters: +* this +* Pointer to the FrameSet whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 150 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstFrameSet *this; /* Pointer to the FrameSet structure */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment string */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int ifr; /* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + int invert; /* Invert attribute value */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstFrameSet *) this_object; + +/* Determine if the FrameSet is inverted. This is required so that the + effects of inversion can be un-done to obtain information about the + "true" Base and Current Frames. (The values written are "internal" + values that are not affected by the Invert setting.) */ + invert = astGetInvert( this ); + +/* Write out values representing the instance variables for the + FrameSet class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Nframe. */ +/* ------- */ + set = ( this->nframe != 0 ); + ival = set ? this->nframe : astGetNframe( this ); + astWriteInt( channel, "Nframe", set, 1, ival, + "Number of Frames in FrameSet" ); + +/* Base. */ +/* ----- */ + set = ( this->base != -INT_MAX ); + ival = set ? this->base : ( !invert ? astGetBase( this ) : + astGetCurrent( this ) ); + astWriteInt( channel, "Base", set, 1, ival, "Index of base Frame" ); + +/* Current. */ +/* -------- */ + set = ( this->current != -INT_MAX ); + ival = set ? this->current : ( !invert ? astGetCurrent( this ) : + astGetBase( this ) ); + astWriteInt( channel, "Currnt", set, 1, ival, "Index of current Frame" ); + +/* Number of nodes. */ +/* ---------------- */ + ival = this->nnode; + set = ( ival != this->nframe ); + astWriteInt( channel, "Nnode", set, 0, ival, + "Number of nodes in FrameSet" ); + +/* Node index for each Frame. */ +/* -------------------------- */ +/* There is a node value for each Frame in the FrameSet. */ + for ( ifr = 1; ifr <= this->nframe; ifr++ ) { + +/* Convert node numbering to start at 1 for the external + representation. Regard a node number as "set" if it differs from + the Frame number. */ + ival = this->node[ ifr - 1 ] + 1; + set = ( ival != ifr ); + +/* Create a suitable keyword and comment. */ + (void) sprintf( key, "Nod%d", ifr ); + (void) sprintf( comment, + "Frame %d is associated with node %d", ifr, ival ); + +/* Write out the value. */ + astWriteInt( channel, key, set, 0, ival, comment ); + } + +/* Index of variants Frame for each Frame. */ +/* --------------------------------------- */ + for ( ifr = 1; ifr <= this->nframe; ifr++ ) { + +/* Retain the one-based Frame index values in varfrm. Regard a number as + "set" if it is greater than zero. */ + ival = this->varfrm[ ifr - 1 ]; + set = ( ival > 0 ); + +/* Create a suitable keyword and comment. */ + (void) sprintf( key, "VFr%d", ifr ); + (void) sprintf( comment, + "Frame %d inherits variants from Frame %d", ifr, ival ); + +/* Write out the value. */ + astWriteInt( channel, key, set, 0, ival, comment ); + } + +/* Links between nodes. */ +/* -------------------- */ +/* Each node in the FrameSet (except the first) has a link to another + node from which it is derived. */ + for ( inode = 1; inode < this->nnode; inode++ ) { + +/* Convert node numbering to start at 1 (as above). */ + ival = this->link[ inode - 1 ] + 1; + (void) sprintf( key, "Lnk%d", inode + 1 ); + (void) sprintf( comment, + "Node %d is derived from node %d", inode + 1, ival ); + astWriteInt( channel, key, 1, 0, ival, comment ); + +/* Inversion flags. */ +/* ---------------- */ +/* Each node with a link has a value which the Invert attribute of the + associated Mapping should have when the transformation from the + parent node to the node in question is required. */ + ival = this->invert[ inode - 1 ]; + +/* Regard the value as set only if the Mapping's inverse + transformation is required. */ + set = ( ival != 0 ); + (void) sprintf( key, "Inv%d", inode + 1 ); + astWriteInt( channel, key, set, 0, ival, + ival ? "The inverse mapping is used" : + "The forward mapping is used" ); + } + +/* Frame objects. */ +/* -------------- */ +/* Output an Object description for each Frame in the FrameSet. */ + for ( ifr = 1; ifr <= this->nframe; ifr++ ) { + (void) sprintf( key, "Frm%d", ifr ); + (void) sprintf( comment, "Frame number %d", ifr ); + astWriteObject( channel, key, 1, 1, this->frame[ ifr - 1 ], + comment ); + } + +/* Mapping objects. */ +/* ---------------- */ +/* Output an Object description for each Mapping in the FrameSet. */ + for ( inode = 1; inode < this->nnode; inode++ ) { + (void) sprintf( key, "Map%d", inode + 1 ); + (void) sprintf( comment, "Mapping between nodes %d and %d", + this->link[ inode - 1 ] + 1, inode + 1 ); + astWriteObject( channel, key, 1, 1, this->map[ inode - 1 ], comment ); + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAFrameSet and astCheckFrameSet functions using + the macros defined for this purpose in the "object.h" header + file. */ +astMAKE_ISA(FrameSet,Frame) +astMAKE_CHECK(FrameSet) + +AstFrameSet *astFrameSet_( void *frame_void, const char *options, int *status, ...) { +/* +*+ +* Name: +* astFrameSet + +* Purpose: +* Create a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* AstFrameSet *astFrameSet( AstFrame *frame, const char *options, int *status, ... ) + +* Class Membership: +* FrameSet constructor. + +* Description: +* This function creates a new FrameSet and optionally initialises +* its attributes. + +* Parameters: +* frame +* Pointer to the initial Frame. If this is not a FrameSet, the +* new FrameSet will be initialised to contain this Frame alone. +* +* If it is a FrameSet, the new FrameSet will be initialised to +* contain the same Frames (and Mappings) and to have the same +* attribute values as the one supplied. This is similar to +* making a copy, except that the Frames (and Mappings) +* contained in the original FrameSet are not themselves copied, +* but are shared by both FrameSets. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new FrameSet. The syntax used is the same as +* for the astSet method and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of arguments may follow it in order to +* supply values to be substituted for these specifiers. The +* rules for supplying these are identical to those for the +* astSet method (and for the C "printf" function). + +* Returned Value: +* A pointer to the new FrameSet. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic FrameSet constructor which +* is available via the protected interface to the FrameSet class. +* A public interface is provided by the astFrameSetId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "frame" parameter is of type (void *) and is converted and +* validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstFrameSet *new; /* Pointer to new FrameSet */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain and validate a pointer to the Frame structure provided. */ + frame = astCheckFrame( frame_void ); + if ( astOK ) { + +/* Initialise the FrameSet, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitFrameSet( NULL, sizeof( AstFrameSet ), !class_init, + &class_vtab, "FrameSet", frame ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + FrameSet's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new FrameSet. */ + return new; +} + +AstFrameSet *astInitFrameSet_( void *mem, size_t size, int init, + AstFrameSetVtab *vtab, const char *name, + AstFrame *frame, int *status ) { +/* +*+ +* Name: +* astInitFrameSet + +* Purpose: +* Initialise a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* AstFrameSet *astInitFrameSet( void *mem, size_t size, int init, +* AstFrameSetVtab *vtab, const char *name, +* AstFrame *frame ) + +* Class Membership: +* FrameSet initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new FrameSet object. It allocates memory (if +* necessary) to accommodate the FrameSet plus any additional data +* associated with the derived class. It then initialises a +* FrameSet structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for a FrameSet at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the FrameSet is to be +* created. This must be of sufficient size to accommodate the +* FrameSet data (sizeof(FrameSet)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the FrameSet (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the FrameSet structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A logical flag indicating if the FrameSet's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new FrameSet. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* frame +* Pointer to the initial Frame (or FrameSet). + +* Returned Value: +* A pointer to the new FrameSet. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstFrameSet *new; /* Pointer to new FrameSet */ + AstFrameSet *old; /* Pointer to original FrameSet */ + int iframe; /* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitFrameSetVtab( vtab, name ); + +/* Initialise a Frame structure (the parent class) as the first + component within the FrameSet structure, allocating memory if + necessary. Give this Frame zero axes, as all axis information for + the FrameSet will be derived from the Frames it contains. */ + new = (AstFrameSet *) astInitFrame( mem, size, 0, + (AstFrameVtab *) vtab, name, 0 ); + + if ( astOK ) { + +/* Initialise the FrameSet data. */ +/* ----------------------------- */ + +/* Normal Frame supplied. */ +/* ---------------------- */ +/* Check that the Frame supplied is not a FrameSet (initialising using + a FrameSet is a special case which is handled below). If not, we + initialise the new FrameSet to refer to a single Frame. */ + if ( !astIsAFrameSet( frame ) && astOK ) { + +/* Allocate memory for the arrays of Frame information. */ + new->frame = astMalloc( sizeof( AstFrame * ) ); + new->node = astMalloc( sizeof( int ) ); + new->varfrm = astMalloc( sizeof( int ) ); + +/* The node arrays are not required until at least two Frames are + present. */ + new->map = NULL; + new->link = NULL; + new->invert = NULL; + +/* If OK, initialise these arrays, thus adding the Frame to the + FrameSet. */ + if ( astOK ) { + new->frame[ 0 ] = astClone( frame ); + new->node[ 0 ] = 0; + new->varfrm[ 0 ] = 0; + new->nframe = 1; + new->nnode = 1; + +/* Initialise the FrameSet attributes to their undefined states. */ + new->base = -INT_MAX; + new->current = -INT_MAX; + } + +/* FrameSet supplied. */ +/* ------------------ */ +/* If a FrameSet was supplied, we will initialise the new FrameSet to + refer to the same Frame and Mapping information (this is similar to + making a copy, except that we clone all the pointers, instead of + copying the Objects they refer to). */ + } else if ( astOK ) { + +/* Obtain a pointer to the original FrameSet structure. */ + old = (AstFrameSet *) frame; + +/* Allocate memory in the new FrameSet to store the Frame and node + information and copy any scalar information across. */ + new->frame = astMalloc( sizeof( AstFrame * ) * (size_t) old->nframe ); + new->node = astStore( NULL, old->node, + sizeof( int ) * (size_t) old->nframe ); + new->varfrm = astStore( NULL, old->varfrm, + sizeof( int ) * (size_t) old->nframe ); + new->map = astMalloc( sizeof( AstMapping * ) * + (size_t) ( old->nnode - 1 ) ); + new->link = astStore( NULL, old->link, + sizeof( int ) * (size_t) ( old->nnode - 1 ) ); + new->invert = astStore( NULL, old->invert, + sizeof( int ) * (size_t) ( old->nnode - 1 ) ); + +/* If OK, clone the pointer to each Frame and Mapping referenced by + the original FrameSet and store the resulting pointers in the new + FrameSet. */ + if ( astOK ) { + for ( iframe = 0; iframe < old->nframe; iframe++ ) { + new->frame[ iframe ] = astClone( old->frame[ iframe ] ); + } + for ( inode = 0; inode < old->nnode - 1; inode++ ) { + new->map[ inode ] = astClone( old->map[ inode ] ); + } + +/* If an error occurred while cloning any of these pointers, clean up + by looping through the arrays of cloned pointers again and + annulling them all. */ + if ( !astOK ) { + for ( iframe = 0; iframe < old->nframe; iframe++ ) { + new->frame[ iframe ] = astAnnul( new->frame[ iframe ] ); + } + for ( inode = 0; inode < old->nnode - 1; inode++ ) { + new->map[ inode ] = astAnnul( new->map[ inode ] ); + } + } + } + +/* If an error occurred, clean up by freeing all memory allocated + above. */ + if ( !astOK ) { + new->frame = astFree( new->frame ); + new->node = astFree( new->node ); + new->varfrm = astFree( new->varfrm ); + new->map = astFree( new->map ); + new->link = astFree( new->link ); + new->invert = astFree( new->invert ); + } + +/* Copy the Frame and node counts across. */ + new->nframe = old->nframe; + new->nnode = old->nnode; + +/* Copy the other FrameSet attributes across. */ + new->base = old->base; + new->current = old->current; + +/* Transfer any other inherited attribute values that relate to the + FrameSet itself (rather than the enclosed Frames). */ + if ( astTestInvert( old ) ) astSetInvert( new, astGetInvert( old ) ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; + +/* Undefine macros local to this function. */ +#undef TRANSFER +} + +AstFrameSet *astLoadFrameSet_( void *mem, size_t size, + AstFrameSetVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadFrameSet + +* Purpose: +* Load a FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "frameset.h" +* AstFrameSet *astLoadFrameSet( void *mem, size_t size, +* AstFrameSetVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* FrameSet loader. + +* Description: +* This function is provided to load a new FrameSet using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* FrameSet structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the FrameSet is to be +* loaded. This must be of sufficient size to accommodate the +* FrameSet data (sizeof(FrameSet)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the FrameSet (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the FrameSet structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstFrameSet) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new FrameSet. If this is NULL, a pointer +* to the (static) virtual function table for the FrameSet class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "FrameSet" is used instead. + +* Returned Value: +* A pointer to the new FrameSet. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstFrameSet *new; /* Pointer to the new FrameSet */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int ifr; /* Get a pointer to the thread specific global data structure. */ + +/* Loop counter for Frames */ + int inode; /* Loop counter for nodes */ + + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this FrameSet. In this case the + FrameSet belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstFrameSet ); + vtab = &class_vtab; + name = "FrameSet"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitFrameSetVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built FrameSet. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "FrameSet" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Nframe. */ +/* ------- */ + new->nframe = astReadInt( channel, "nframe", 1 ); + if ( new->nframe < 0 ) new->nframe = 1; + +/* Number of nodes. */ +/* ---------------- */ + new->nnode = astReadInt( channel, "nnode", new->nframe ); + if ( new->nnode < 1 ) new->nnode = 1; + +/* Allocate memory to hold Frame and node information. */ + new->frame = astMalloc( sizeof( AstFrame *) * (size_t) new->nframe ); + new->node = astMalloc( sizeof( int ) * (size_t) new->nframe ); + new->varfrm = astMalloc( sizeof( int ) * (size_t) new->nframe ); + new->link = astMalloc( sizeof( int ) * (size_t) ( new->nnode - 1 ) ); + new->invert = astMalloc( sizeof( int ) * (size_t) ( new->nnode - 1 ) ); + new->map = astMalloc( sizeof( AstMapping * ) * + (size_t) ( new->nnode - 1 ) ); + +/* If an error occurs, ensure that all allocated memory is freed. */ + if ( !astOK ) { + new->frame = astFree( new->frame ); + new->node = astFree( new->node ); + new->varfrm = astFree( new->varfrm ); + new->link = astFree( new->link ); + new->invert = astFree( new->invert ); + new->map = astFree( new->map ); + +/* Otherwise, initialise the arrays which will hold Object pointers. */ + } else { + for ( ifr = 1; ifr <= new->nframe; ifr++ ) { + new->frame[ ifr - 1 ] = NULL; + } + for ( inode = 1; inode < new->nnode; inode++ ) { + new->map[ inode - 1 ] = NULL; + } + +/* Read Frame data... */ + for ( ifr = 1; ifr <= new->nframe; ifr++ ) { + +/* Frame objects. */ +/* -------------- */ +/* Create the required keyword and then read the Frame. */ + (void) sprintf( key, "frm%d", ifr ); + new->frame[ ifr - 1 ] = astReadObject( channel, key, NULL ); + +/* Node index for each Frame. */ +/* -------------------------- */ + (void) sprintf( key, "nod%d", ifr ); + new->node[ ifr - 1 ] = astReadInt( channel, key, ifr ) - 1; + +/* Index of variants Frame. */ +/* ------------------------ */ + (void) sprintf( key, "vfr%d", ifr ); + new->varfrm[ ifr - 1 ] = astReadInt( channel, key, 0 ); + } + +/* Read node data... */ + for ( inode = 1; inode < new->nnode; inode++ ) { + +/* Links between nodes. */ +/* -------------------- */ + (void) sprintf( key, "lnk%d", inode + 1 ); + new->link[ inode - 1 ] = astReadInt( channel, key, 0 ) - 1; + +/* Inversion flags. */ +/* ---------------- */ + (void) sprintf( key, "inv%d", inode + 1 ); + new->invert[ inode - 1 ] = astReadInt( channel, key, 0 ); + +/* Mapping objects. */ +/* ---------------- */ + (void) sprintf( key, "map%d", inode + 1 ); + new->map[ inode - 1 ] = astReadObject( channel, key, NULL ); + } + +/* Read remaining data... */ + +/* Base. */ +/* ----- */ + new->base = astReadInt( channel, "base", -INT_MAX ); + if ( new->base < 1 ) new->base = -INT_MAX; + +/* Current. */ +/* -------- */ + new->current = astReadInt( channel, "currnt", -INT_MAX ); + if ( new->base < 1 ) new->base = -INT_MAX; + } + +/* If an error occurred, clean up by deleting the new FrameSet. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new FrameSet pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astAddFrame_( AstFrameSet *this, int iframe, AstMapping *map, + AstFrame *frame, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,AddFrame))( this, iframe, map, frame, status ); +} +void astClearBase_( AstFrameSet *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,ClearBase))( this, status ); +} +void astClearCurrent_( AstFrameSet *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,ClearCurrent))( this, status ); +} +void astClearVariant_( AstFrameSet *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,ClearVariant))( this, status ); +} +int astGetBase_( AstFrameSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,GetBase))( this, status ); +} +int astGetCurrent_( AstFrameSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,GetCurrent))( this, status ); +} +const char *astGetVariant_( AstFrameSet *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,FrameSet,GetVariant))( this, status ); +} +AstFrame *astGetFrame_( AstFrameSet *this, int iframe, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,FrameSet,GetFrame))( this, iframe, status ); +} +AstMapping *astGetMapping_( AstFrameSet *this, int iframe1, int iframe2, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,FrameSet,GetMapping))( this, iframe1, iframe2, status ); +} +int astGetNframe_( AstFrameSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,GetNframe))( this, status ); +} +void astRemapFrame_( AstFrameSet *this, int iframe, AstMapping *map, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,RemapFrame))( this, iframe, map, status ); +} +void astAddVariant_( AstFrameSet *this, AstMapping *map, const char *name, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,AddVariant))( this, map, name, status ); +} +void astMirrorVariants_( AstFrameSet *this, int iframe, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,MirrorVariants))( this, iframe, status ); +} +void astRemoveFrame_( AstFrameSet *this, int iframe, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,RemoveFrame))( this, iframe, status ); +} +void astSetBase_( AstFrameSet *this, int ibase, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,SetBase))( this, ibase, status ); +} +void astSetCurrent_( AstFrameSet *this, int icurrent, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,SetCurrent))( this, icurrent, status ); +} +void astSetVariant_( AstFrameSet *this, const char *variant, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,FrameSet,SetVariant))( this, variant, status ); +} +int astTestBase_( AstFrameSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,TestBase))( this, status ); +} +int astTestCurrent_( AstFrameSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,TestCurrent))( this, status ); +} +int astTestVariant_( AstFrameSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,TestVariant))( this, status ); +} +int astValidateFrameIndex_( AstFrameSet *this, int iframe, + const char *method, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,FrameSet,ValidateFrameIndex))( this, iframe, + method, status ); +} +const char *astGetAllVariants_( AstFrameSet *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,FrameSet,GetAllVariants))( this, status ); +} + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstFrameSet *astFrameSetId_( void *, const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstFrameSet *astFrameSetId_( void *frame_void, const char *options, ... ) { +/* +*++ +* Name: +c astFrameSet +f AST_FRAMESET + +* Purpose: +* Create a FrameSet. + +* Type: +* Public function. + +* Synopsis: +c #include "frameset.h" +c AstFrameSet *astFrameSet( AstFrame *frame, const char *options, ... ) +f RESULT = AST_FRAMESET( FRAME, OPTIONS, STATUS ) + +* Class Membership: +* FrameSet constructor. + +* Description: +* This function creates a new FrameSet and optionally initialises +* its attributes. +* +* A FrameSet consists of a set of one or more Frames (which +* describe coordinate systems), connected together by Mappings +* (which describe how the coordinate systems are inter-related). A +* FrameSet makes it possible to obtain a Mapping between any pair +* of these Frames (i.e. to convert between any of the coordinate +* systems which it describes). The individual Frames are +* identified within the FrameSet by an integer index, with Frames +* being numbered consecutively from one as they are added to the +* FrameSet. +* +* Every FrameSet has a "base" Frame and a "current" Frame (which +* are allowed to be the same). Any of the Frames may be nominated +* to hold these positions, and the choice is determined by the +* values of the FrameSet's Base and Current attributes, which hold +* the indices of the relevant Frames. By default, the first Frame +* added to a FrameSet is its base Frame, and the last one added is +* its current Frame. +* +* The base Frame describes the "native" coordinate system of +* whatever the FrameSet is used to calibrate (e.g. the pixel +* coordinates of an image) and the current Frame describes the +* "apparent" coordinate system in which it should be viewed +* (e.g. displayed, etc.). Any further Frames represent a library +* of alternative coordinate systems, which may be selected by +* making them current. +* +* When a FrameSet is used in a context that requires a Frame, +* (e.g. obtaining its Title value, or number of axes), the current +* Frame is used. A FrameSet may therefore be used in place of its +* current Frame in most situations. +* +* When a FrameSet is used in a context that requires a Mapping, +* the Mapping used is the one between its base Frame and its +* current Frame. Thus, a FrameSet may be used to convert "native" +* coordinates into "apparent" ones, and vice versa. Like any +c Mapping, a FrameSet may also be inverted (see astInvert), which +f Mapping, a FrameSet may also be inverted (see AST_INVERT), which +* has the effect of interchanging its base and current Frames and +* hence of reversing the Mapping between them. +* +* Regions may be added into a FrameSet (since a Region is a type of +* Frame), either explicitly or as components within CmpFrames. In this +* case the Mapping between a pair of Frames within a FrameSet will +* include the effects of the clipping produced by any Regions included +* in the path between the Frames. + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* Pointer to the first Frame to be inserted into the +* FrameSet. This initially becomes both the base and the +* current Frame. (Further Frames may be added using the +c astAddFrame function.) +f AST_ADDFRAME routine.) +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new FrameSet. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new FrameSet. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astFrameSet() +f AST_FRAMESET +* A pointer to the new FrameSet. + +* Notes: +c - If a pointer to an existing FrameSet is given for the "frame" +c parameter, then the new FrameSet will (as a special case) be +f - If a pointer to an existing FrameSet is given for the FRAME +f argument, then the new FrameSet will (as a special case) be +* initialised to contain the same Frames and Mappings, and to have +* the same attribute values, as the one supplied. This process is +c similar to making a copy of a FrameSet (see astCopy), except +f similar to making a copy of a FrameSet (see AST_COPY), except +* that the Frames and Mappings contained in the original are not +* themselves copied, but are shared by both FrameSets. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astFrameSet constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astFrameSet_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "frame" parameter is of type +* (void *) and is converted from an ID value to a pointer and +* validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astFrameSet_ directly, so it must be a +* re-implementation of it in all respects, except for the +* conversions between IDs and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstFrameSet *new; /* Pointer to new FrameSet */ + va_list args; /* Variable argument list */ + + int *status; + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain the Frame pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + if ( astOK ) { + +/* Initialise the FrameSet, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitFrameSet( NULL, sizeof( AstFrameSet ), !class_init, + &class_vtab, "FrameSet", frame ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + FrameSet's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new FrameSet. */ + return astMakeId( new ); +} + + + diff --git a/frameset.h b/frameset.h new file mode 100644 index 0000000..1f33054 --- /dev/null +++ b/frameset.h @@ -0,0 +1,709 @@ +/* +*+ +* Name: +* frameset.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the FrameSet class. + +* Invocation: +* #include "frameset.h" + +* Description: +* This include file defines the interface to the FrameSet class and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this class. +* +* A FrameSet consists of a set of one or more Frames, which are +* inter-related by Mappings in such a way that it is possible to +* obtain a Mapping between any pair of the Frames. The Frames are +* identified by an integer index, with Frames being numbered +* consecutively from one as they are added to the FrameSet. +* +* At any time, there is a "base" Frame and a "current" Frame +* (which are allowed to be the same). Any of the Frames may be +* nominated to hold these positions, and the choice is determined +* by the values of the FrameSet's Base and Current attributes +* which hold the indices of the relevant Frames. By default, the +* first Frame added to a FrameSet is its base Frame, and the last +* one added is its current Frame. +* +* The base Frame describes the "native" coordinate system of +* whatever the FrameSet is used to calibrate (e.g. the pixel +* coordinates of an image) and the current Frame describes the +* "apparent" coordinate system in which it should be viewed +* (e.g. displayed, etc.). The other Frames represent alternative +* coordinate systems which may be selected by making them current. +* +* When Frame methods are invoked on a FrameSet (e.g. to obtain a +* Title value or to determine the number of axes), they are +* applied to the current Frame. Thus, a FrameSet may be used in +* place of its current Frame in most situations. +* +* When Mapping methods are invoked on a FrameSet, the Mapping used +* is the one between its base Frame and its current Frame. Thus, a +* FrameSet may be used to convert "native" coordinates into +* "apparent" ones, and vice versa. A FrameSet may also be +* inverted, which has the effect of interchanging its base and +* current Frames (and hence of reversing the Mapping between +* them). +* +* The FrameSet class also defines methods of its own, which are +* used to manage the Frames and Mappings that it contains and to +* convert between coordinate systems described by different +* FrameSets. + +* Inheritance: +* The FrameSet class inherits from the Frame class. + +* Attributes Over-Ridden: +* Digits (integer) +* Direction(axis) (integer) +* Domain (string) +* Format(axis) (string) +* Label(axis) (string) +* MatchEnd (integer) +* MaxAxes (integer) +* MinAxes (integer) +* Naxes (integer) +* Permute (integer) +* PreserveAxes (integer) +* Symbol(axis) (string) +* Title (string) +* Unit(axis) (string) +* The FrameSet acquires all of these attributes from its +* current Frame, so their meanings, values and defaults are +* determined by this Frame and may change if a different +* current Frame is selected. +* Nin (integer) +* Nout (integer) +* TranForward (integer) +* TranInverse (integer) +* The FrameSet interprets all of these as applying to the +* Mapping that converts coordinates between its base Frame and +* its current Frame, so their values may change if a different +* base or current Frame is selected. +* Invert (integer) +* Report (integer) +* The FrameSet interprets these as applying to the Mapping that +* converts coordinates between its base Frame and its current +* Frame, but their values are not affected by selecing a +* different base or current Frame. + +* New Attributes Defined: +* Base (integer) +* The (one-based) index of the Frame which is to be regarded as +* the base Frame in the FrameSet. By default, this is the first +* Frame added to the FrameSet (i.e. when it was created), +* unless the Frameset has been inverted, in which case it is +* the last Frame added. Inverting a FrameSet interchanges the +* values of its Base and Current attributes. +* Current (integer) +* The (one-based) index of the Frame which is to be regarded as +* the current Frame in the FrameSet. By default, this is the +* last Frame added to the FrameSet, unless the Frameset has +* been inverted, in which case it is the first Frame added +* (i.e. when the FrameSet was created). Inverting a FrameSet +* interchanges the values of its Base and Current attributes. +* Nframe (integer) +* A read-only value giving the number of Frames in a +* FrameSet. This value will change as Frames are added or +* removed. + +* Methods Over-Ridden: +* Public: +* astClear +* Clear attribute values for a FrameSet. +* astConvert +* Determine how to convert between two coordinate systems. +* astDistance +* Calculate the distance between two points. +* astFindFrame +* Find a coordinate system with specified characteristics +* astFormat +* Format a coordinate value for a FrameSet axis. +* astGetAxis +* Obtain a pointer to a specified Axis from a FrameSet. +* astGetNaxes +* Determine how many axes a FrameSet has. +* astGetNin +* Get the number of input coordinates for a FrameSet. +* astGetNout +* Get the number of output coordinates for a FrameSet. +* astNorm +* Normalise a set of FrameSet coordinates. +* astOffset +* Calculate an offset along a geodesic curve. +* astPermAxes +* Permute the order of a FrameSet's axes. +* astPickAxes +* Create a new Frame by picking axes from a FrameSet. +* astSetAxis +* Set a new Axis for a FrameSet. +* astSimplify +* Simplify the Mappings in a FrameSet. +* astTransform +* Transform a set of points. +* astUnformat +* Read a formatted coordinate value for a FrameSet axis. +* +* Protected: +* astAbbrev +* Abbreviate a formatted FrameSet axis value by skipping leading +* fields. +* astClearDigits +* Clear the value of the Digits attribute for a FrameSet. +* astClearDirection +* Clear the value of the Direction attribute for a FrameSet axis. +* astClearDomain +* Clear the value of the Domain attribute for a FrameSet. +* astClearFormat +* Clear the value of the Format attribute for a FrameSet axis. +* astClearLabel +* Clear the value of the Label attribute for a FrameSet axis. +* astClearMatchEnd +* Clear the value of the MatchEnd attribute for a FrameSet. +* astClearMaxAxes +* Clear the value of the MaxAxes attribute for a FrameSet. +* astClearMinAxes +* Clear the value of the MinAxes attribute for a FrameSet. +* astClearPermute +* Clear the value of the Permute attribute for a FrameSet. +* astClearPreserveAxes +* Clear the value of the PreserveAxes attribute for a FrameSet. +* astClearSymbol +* Clear the value of the Symbol attribute for a FrameSet axis. +* astClearTitle +* Clear the value of the Title attribute for a FrameSet. +* astClearUnit +* Clear the value of the Unit attribute for a FrameSet axis. +* astConvertX +* Determine how to convert between two coordinate systems. +* astGap +* Find a "nice" gap for tabulating FrameSet axis values. +* astGetDigits +* Get the value of the Digits attribute for a FrameSet. +* astGetDirection +* Get the value of the Direction attribute for a FrameSet axis. +* astGetDomain +* Get the value of the Domain attribute for a FrameSet. +* astGetFormat +* Get the value of the Format attribute for a FrameSet axis. +* astGetLabel +* Get the value of the Label attribute for a FrameSet axis. +* astGetMatchEnd +* Get the value of the MatchEnd attribute for a FrameSet. +* astGetMaxAxes +* Get the value of the MaxAxes attribute for a FrameSet. +* astGetMinAxes +* Get the value of the MinAxes attribute for a FrameSet. +* astGetPerm +* Access the axis permutation array for the current Frame of +* a FrameSet. +* astGetPermute +* Get the value of the Permute attribute for a FrameSet. +* astGetPreserveAxes +* Get the value of the PreserveAxes attribute for a FrameSet. +* astGetSymbol +* Get the value of the Symbol attribute for a FrameSet axis. +* astGetTitle +* Get the value of the Title attribute for a FrameSet. +* astGetTranForward +* Determine if a Mapping can perform a "forward" coordinate +* transformation. +* astGetTranInverse +* Determine if a Mapping can perform an "inverse" coordinate +* transformation. +* astGetUnit +* Get the value of the Unit attribute for a FrameSet axis. +* astMatch +* Determine if conversion is possible between two coordinate systems. +* astOverlay +* Overlay the attributes of a template FrameSet on to another Frame. +* astPrimaryFrame +* Uniquely identify a primary Frame and one of its axes. +* astReportPoints +* Report the effect of transforming a set of points using a FrameSet. +* astSetAttrib +* Set an attribute value for a FrameSet. +* astSetDigits +* Set the value of the Digits attribute for a FrameSet. +* astSetDirection +* Set the value of the Direction attribute for a FrameSet axis. +* astSetDomain +* Set the value of the Domain attribute for a FrameSet. +* astSetFormat +* Set the value of the Format attribute for a FrameSet axis. +* astSetLabel +* Set the value of the Label attribute for a FrameSet axis. +* astSetMatchEnd +* Set the value of the MatchEnd attribute for a FrameSet. +* astSetMaxAxes +* Set the value of the MaxAxes attribute for a FrameSet. +* astSetMinAxes +* Set the value of the MinAxes attribute for a FrameSet. +* astSetPermute +* Set the value of the Permute attribute for a FrameSet. +* astSetPreserveAxes +* Set the value of the PreserveAxes attribute for a FrameSet. +* astSetSymbol +* Set the value of the Symbol attribute for a FrameSet axis. +* astSetTitle +* Set the value of the Title attribute for a FrameSet. +* astSetUnit +* Set the value of the Unit attribute for a FrameSet axis. +* astSubFrame +* Select axes from a FrameSet and convert to the new coordinate +* system. +* astTestDigits +* Test if a value has been set for the Digits attribute of a +* FrameSet. +* astTestDirection +* Test if a value has been set for the Direction attribute of a +* FrameSet axis. +* astTestDomain +* Test if a value has been set for the Domain attribute of a +* FrameSet. +* astTestFormat +* Test if a value has been set for the Format attribute of a +* FrameSet axis. +* astTestLabel +* Test if a value has been set for the Label attribute of a +* FrameSet axis. +* astTestMatchEnd +* Test if a value has been set for the MatchEnd attribute of a +* FrameSet. +* astTestMaxAxes +* Test if a value has been set for the MaxAxes attribute of a +* FrameSet. +* astTestMinAxes +* Test if a value has been set for the MinAxes attribute of a +* FrameSet. +* astTestPermute +* Test if a value has been set for the Permute attribute of a +* FrameSet. +* astTestPreserveAxes +* Test if a value has been set for the PreserveAxes attribute of a +* FrameSet. +* astTestSymbol +* Test if a value has been set for the Symbol attribute of a +* FrameSet axis. +* astTestTitle +* Test if a value has been set for the Title attribute of a FrameSet. +* astTestUnit +* Test if a value has been set for the Unit attribute of a FrameSet +* axis. +* astValidateAxis +* Validate and permute a FrameSet's axis index. +* astVSet +* Set values for a FrameSet's attributes. + +* New Methods Defined: +* Public: +* astAddFrame +* Add a Frame to a FrameSet to define a new coordinate system. +* astGetFrame +* Obtain a pointer to a specified Frame in a FrameSet. +* astGetMapping +* Obtain a Mapping between two Frames in a FrameSet. +* astRemapFrame +* Modify a Frame's relationshp to the other Frames in a FrameSet. +* astRemoveFrame +* Remove a Frame from a FrameSet. +* +* Protected: +* astClearBase +* Clear the value of the Base attribute for a FrameSet. +* astClearCurrent +* Clear the value of the Current attribute for a FrameSet. +* astGetBase +* Obtain the value of the Base attribute for a FrameSet. +* astGetCurrent +* Obtain the value of the Current attribute for a FrameSet. +* astGetNframe +* Determine the number of Frames in a FrameSet. +* astSetBase +* Set the value of the Base attribute for a FrameSet. +* astSetCurrent +* Set the value of the Current attribute for a FrameSet. +* astTestBase +* Test if a value has been set for the Base attribute of a FrameSet. +* astTestCurrent +* Test if a value has been set for the Current attribute of a +* FrameSet. +* astValidateFrameIndex +* Validate a FrameSet Frame index number. + +* Other Class Functions: +* Public: +* astFrameSet +* Create a FrameSet. +* astIsAFrameSet +* Test class membership. +* +* Protected: +* astCheckFrameSet +* Validate class membership. +* astInitFrameSet +* Initialise a FrameSet. +* astInitFrameSetVtab +* Initialise the virtual function table for the FrameSet class. +* astLoadFrameSet +* Load a FrameSet. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstFrameSet +* FrameSet object type. + +* Protected: +* AstFrameSetVtab +* FrameSet virtual function table type. + +* Macros: +* Public: +* AST__BASE +* Expands to a constant int that may be used as a Frame index to +* refer to a FrameSet's base Frame. +* AST__CURRENT +* Expands to a constant int that may be used as a Frame index to +* refer to a FrameSet's current Frame. +* AST__NOFRAME +* Expands to a constant int that is guaranteed not to be valid when +* used as a Frame index for a FrameSet. +* +* Protected: +* None. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 16-FEB-1996 (RFWS): +* Original version. +* 5-JUN-1996 (RFWS): +* Tidied up, etc. +* 12-AUG-1996 (RFWS): +* Added support for the public interface. +* 25-SEP-1996 (RFWS): +* Added I/O facilities. +* 20-JAN-1998 (RFWS): +* Implemented preservation of FrameSet integrity when attribute +* values associated with the current Frame are modified. +* 25-FEB-1998 (RFWS): +* Over-ride the astUnformat method. +* 8-JAN-2003 (DSB): +* Added protected astInitFrameSetVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "frame.h" /* Parent Frame class */ + +/* Note that the usual setting of the FRAMESET_INCLUDED flag, which + prevents this file being included more than once, must be deferred + until after including the "frame.h" file. This is because "frame.h" + needs to include the present interface definition (as a form of + "forward reference") in order to have access to FrameSets + itself. */ +#if !defined( FRAMESET_INCLUDED ) +#define FRAMESET_INCLUDED + +/* Macros. */ +/* ======= */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST__BASE (0) /* Identify base Frame */ +#define AST__CURRENT (-1) /* Identify current Frame */ +#define AST__NOFRAME (-99) /* An invalid Frame index */ +#define AST__ALLFRAMES (-199) /* A value representing all Frames */ +#define AST__FRAMESET_GETALLVARIANTS_BUFF_LEN 200 /* Length for AllVariants buffer */ +#define AST__FRAMESET_GETATTRIB_BUFF_LEN 200 /* Length for GetAtribb buffer */ + +/* Type Definitions. */ +/* ================= */ +/* FrameSet structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstFrameSet { + +/* Attributes inherited from the parent class. */ + AstFrame parent; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstFrame **frame; /* Array of Frame pointers */ + AstMapping **map; /* Array of Mapping pointers */ + int *varfrm; /* Array of variants Frames indices */ + int *invert; /* Array of Mapping Invert values */ + int *link; /* Parent node index for each node */ + int *node; /* Index of node associated with Frame */ + int base; /* Index of base Frame */ + int current; /* Index of current Frame */ + int nframe; /* Number of Frames */ + int nnode; /* Number of nodes */ +} AstFrameSet; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstFrameSetVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstFrame *(* GetFrame)( AstFrameSet *, int, int * ); + AstMapping *(* GetMapping)( AstFrameSet *, int, int, int * ); + int (* GetBase)( AstFrameSet *, int * ); + int (* GetCurrent)( AstFrameSet *, int * ); + int (* GetNframe)( AstFrameSet *, int * ); + int (* TestBase)( AstFrameSet *, int * ); + int (* TestCurrent)( AstFrameSet *, int * ); + int (* ValidateFrameIndex)( AstFrameSet *, int, const char *, int * ); + void (* AddFrame)( AstFrameSet *, int, AstMapping *, AstFrame *, int * ); + void (* AddVariant)( AstFrameSet *, AstMapping *, const char *, int * ); + void (* MirrorVariants)( AstFrameSet *, int, int * ); + void (* ClearBase)( AstFrameSet *, int * ); + void (* ClearCurrent)( AstFrameSet *, int * ); + void (* RemapFrame)( AstFrameSet *, int, AstMapping *, int * ); + void (* RemoveFrame)( AstFrameSet *, int, int * ); + void (* SetBase)( AstFrameSet *, int, int * ); + void (* SetCurrent)( AstFrameSet *, int, int * ); + void (* ClearVariant)( AstFrameSet *, int * ); + const char *(* GetVariant)( AstFrameSet *, int * ); + void (* SetVariant)( AstFrameSet *, const char *, int * ); + int (* TestVariant)( AstFrameSet *, int * ); + const char *(* GetAllVariants)( AstFrameSet *, int * ); +} AstFrameSetVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstFrameSetGlobals { + AstFrameSetVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__FRAMESET_GETATTRIB_BUFF_LEN + 1 ]; + char GetAllVariants_Buff[ AST__FRAMESET_GETALLVARIANTS_BUFF_LEN + 1 ]; + AstFrame *Integrity_Frame; + const char *Integrity_Method; + int Integrity_Lost; +} AstFrameSetGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(FrameSet) /* Check class membership */ +astPROTO_ISA(FrameSet) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstFrameSet *astFrameSet_( void *, const char *, int *, ...); +#else +AstFrameSet *astFrameSetId_( void *, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstFrameSet *astInitFrameSet_( void *, size_t, int, AstFrameSetVtab *, + const char *, AstFrame *, int * ); + +/* Vtab initialiser. */ +void astInitFrameSetVtab_( AstFrameSetVtab *, const char *, int * ); + +/* Loader. */ +AstFrameSet *astLoadFrameSet_( void *, size_t, AstFrameSetVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitFrameSetGlobals_( AstFrameSetGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstFrame *astGetFrame_( AstFrameSet *, int, int * ); +AstMapping *astGetMapping_( AstFrameSet *, int, int, int * ); +void astAddFrame_( AstFrameSet *, int , AstMapping *, AstFrame *, int * ); +void astAddVariant_( AstFrameSet *, AstMapping *, const char *, int * ); +void astMirrorVariants_( AstFrameSet *, int, int * ); +void astRemapFrame_( AstFrameSet *, int, AstMapping *, int * ); +void astRemoveFrame_( AstFrameSet *, int, int * ); + +#if defined(astCLASS) /* Protected */ +const char *astGetAllVariants_( AstFrameSet *, int * ); +int astGetBase_( AstFrameSet *, int * ); +int astGetCurrent_( AstFrameSet *, int * ); +int astGetNframe_( AstFrameSet *, int * ); +int astTestBase_( AstFrameSet *, int * ); +int astTestCurrent_( AstFrameSet *, int * ); +int astValidateFrameIndex_( AstFrameSet *, int, const char *, int * ); +void astClearBase_( AstFrameSet *, int * ); +void astClearCurrent_( AstFrameSet *, int * ); +void astSetBase_( AstFrameSet *, int, int * ); +void astSetCurrent_( AstFrameSet *, int, int * ); +void astClearVariant_( AstFrameSet *, int * ); +const char *astGetVariant_( AstFrameSet *, int * ); +void astSetVariant_( AstFrameSet *, const char *, int * ); +int astTestVariant_( AstFrameSet *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class to make + them easier to invoke (e.g. to avoid type mis-matches when passing pointers + to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them to + validate their own arguments. We must use a cast when passing object + pointers (so that they can accept objects from derived classes). */ + +/* Check class membership. */ +#define astCheckFrameSet(this) astINVOKE_CHECK(FrameSet,this,0) +#define astVerifyFrameSet(this) astINVOKE_CHECK(FrameSet,this,1) + +/* Test class membership. */ +#define astIsAFrameSet(this) astINVOKE_ISA(FrameSet,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astFrameSet astINVOKE(F,astFrameSet_) +#else +#define astFrameSet astINVOKE(F,astFrameSetId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitFrameSet(mem,size,init,vtab,name,frame) \ +astINVOKE(O,astInitFrameSet_(mem,size,init,vtab,name,astCheckFrame(frame),STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitFrameSetVtab(vtab,name) astINVOKE(V,astInitFrameSetVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadFrameSet(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadFrameSet_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckFrameSet to validate FrameSet pointers before + use. This provides a contextual error report if a pointer to the wrong sort + of object is supplied. */ +#define astAddFrame(this,iframe,map,frame) \ +astINVOKE(V,astAddFrame_(astCheckFrameSet(this),iframe,(((iframe)!=AST__ALLFRAMES)?astCheckMapping(map):NULL),astCheckFrame(frame),STATUS_PTR)) +#define astAddVariant(this,map,name) \ +astINVOKE(V,astAddVariant_(astCheckFrameSet(this),map?astCheckMapping(map):NULL,name,STATUS_PTR)) +#define astMirrorVariants(this,iframe) \ +astINVOKE(V,astMirrorVariants_(astCheckFrameSet(this),iframe,STATUS_PTR)) +#define astGetFrame(this,iframe) \ +astINVOKE(O,astGetFrame_(astCheckFrameSet(this),iframe,STATUS_PTR)) +#define astGetMapping(this,iframe1,iframe2) \ +astINVOKE(O,astGetMapping_(astCheckFrameSet(this),iframe1,iframe2,STATUS_PTR)) +#define astRemapFrame(this,iframe,map) \ +astINVOKE(V,astRemapFrame_(astCheckFrameSet(this),iframe,astCheckMapping(map),STATUS_PTR)) +#define astRemoveFrame(this,iframe) \ +astINVOKE(V,astRemoveFrame_(astCheckFrameSet(this),iframe,STATUS_PTR)) + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +#if defined(astCLASS) /* Protected */ +#define astClearBase(this) \ +astINVOKE(V,astClearBase_(astCheckFrameSet(this),STATUS_PTR)) +#define astClearCurrent(this) \ +astINVOKE(V,astClearCurrent_(astCheckFrameSet(this),STATUS_PTR)) +#define astGetBase(this) \ +astINVOKE(V,astGetBase_(astCheckFrameSet(this),STATUS_PTR)) +#define astGetCurrent(this) \ +astINVOKE(V,astGetCurrent_(astCheckFrameSet(this),STATUS_PTR)) +#define astGetNframe(this) \ +astINVOKE(V,astGetNframe_(astCheckFrameSet(this),STATUS_PTR)) +#define astSetBase(this,ibase) \ +astINVOKE(V,astSetBase_(astCheckFrameSet(this),ibase,STATUS_PTR)) +#define astSetCurrent(this,icurrent) \ +astINVOKE(V,astSetCurrent_(astCheckFrameSet(this),icurrent,STATUS_PTR)) +#define astTestBase(this) \ +astINVOKE(V,astTestBase_(astCheckFrameSet(this),STATUS_PTR)) +#define astTestCurrent(this) \ +astINVOKE(V,astTestCurrent_(astCheckFrameSet(this),STATUS_PTR)) +#define astValidateFrameIndex(this,iframe,method) \ +astINVOKE(V,astValidateFrameIndex_(astCheckFrameSet(this),iframe,method,STATUS_PTR)) +#define astClearVariant(this) \ +astINVOKE(V,astClearVariant_(astCheckFrameSet(this),STATUS_PTR)) +#define astGetVariant(this) \ +astINVOKE(V,astGetVariant_(astCheckFrameSet(this),STATUS_PTR)) +#define astSetVariant(this,variant) \ +astINVOKE(V,astSetVariant_(astCheckFrameSet(this),variant,STATUS_PTR)) +#define astTestVariant(this) \ +astINVOKE(V,astTestVariant_(astCheckFrameSet(this),STATUS_PTR)) +#define astGetAllVariants(this) \ +astINVOKE(V,astGetAllVariants_(astCheckFrameSet(this),STATUS_PTR)) +#endif +#endif + + + + + diff --git a/frameset.pdf b/frameset.pdf new file mode 100644 index 0000000..7730800 Binary files /dev/null and b/frameset.pdf differ diff --git a/fratemap.c b/fratemap.c new file mode 100644 index 0000000..62588ab --- /dev/null +++ b/fratemap.c @@ -0,0 +1,106 @@ +/* +*+ +* Name: +* fratemap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST RateMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the RateMap class. + +* Routines Defined: +* AST_ISARATEMAP +* AST_RATEMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S.Berry (Starlink) + +* History: +* 10-FEB-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "ratemap.h" /* C interface to the RateMap class */ + +F77_LOGICAL_FUNCTION(ast_isaratemap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISARATEMAP", NULL, 0 ); + RESULT = astIsARateMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_ratemap)( INTEGER(MAP), + INTEGER(AX1), + INTEGER(AX2), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(MAP) + GENPTR_INTEGER(AX1) + GENPTR_INTEGER(AX2) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_RATEMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astRateMap( astI2P( *MAP ), *AX1, *AX2, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fregion.c b/fregion.c new file mode 100644 index 0000000..2c3b4ef --- /dev/null +++ b/fregion.c @@ -0,0 +1,297 @@ +/* +*+ +* Name: +* fregion.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Region class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Region class. + +* Routines Defined: +* AST_NEGATE +* AST_ISAREGION +* AST_MAPREGION +* AST_GETREGIONBOUNDS +* AST_GETREGIONFRAME +* AST_GETREGIONFRAMESET +* AST_OVERLAP +* AST_SETUNC +* AST_GETUNC +* AST_SHOWMESH + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-MAR-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "region.h" /* C interface to the Region class */ + +/* FORTRAN interface functions. */ +/* ============================ */ +/* These functions implement the remainder of the FORTRAN interface. */ + +F77_SUBROUTINE(ast_negate)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_NEGATE", NULL, 0 ); + astWatchSTATUS( + astNegate( astI2P( *THIS ) ); + ) +} + +F77_SUBROUTINE(ast_setunc)( INTEGER(THIS), + INTEGER(UNC), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(UNC) + + astAt( "AST_SETUNC", NULL, 0 ); + astWatchSTATUS( + astSetUnc( astI2P( *THIS ), astI2P( *UNC ) ); + ) +} + +F77_LOGICAL_FUNCTION(ast_isaregion)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAREGION", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsARegion( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_getregionframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETREGIONFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetRegionFrame( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_getregionframeset)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETREGIONFRAMESET", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetRegionFrameSet( astI2P( *THIS ) ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_getunc)( INTEGER(THIS), + LOGICAL(DEF), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_LOGICAL(DEF) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETUNC", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetUnc( astI2P( *THIS ), F77_ISTRUE( *DEF ) ? 1 : 0 ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_mapregion)( INTEGER(REG), + INTEGER(MAP), + INTEGER(FRM), + INTEGER(STATUS) ) { + GENPTR_INTEGER(REG) + GENPTR_INTEGER(MAP) + GENPTR_INTEGER(FRM) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_MAPREGION", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astMapRegion( astI2P( *REG ), astI2P( *MAP ), + astI2P( *FRM ) ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_overlap)( INTEGER(THIS), + INTEGER(THAT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(THAT) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_OVERLAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astOverlap( astI2P( *THIS ), astI2P( *THAT ) ); + ) + return RESULT; +} + +/* AST_MASK requires a function for each possible data type, so + define it via a macro. */ +#define MAKE_AST_MASK(f,F,Ftype,X,Xtype) \ +F77_INTEGER_FUNCTION(ast_mask##f)( INTEGER(THIS), \ + INTEGER(MAP), \ + LOGICAL(INSIDE), \ + INTEGER(NDIM), \ + INTEGER_ARRAY(LBND), \ + INTEGER_ARRAY(UBND), \ + Ftype##_ARRAY(IN), \ + Ftype(VAL), \ + INTEGER(STATUS) ) { \ + GENPTR_INTEGER(THIS) \ + GENPTR_INTEGER(MAP) \ + GENPTR_LOGICAL(INSIDE) \ + GENPTR_INTEGER(NDIM) \ + GENPTR_INTEGER_ARRAY(LBND) \ + GENPTR_INTEGER_ARRAY(UBND) \ + GENPTR_##Ftype##_ARRAY(IN) \ + GENPTR_##Ftype(VAL) \ + GENPTR_INTEGER(STATUS) \ +\ + F77_INTEGER_TYPE RESULT; \ +\ + astAt( "AST_MASK"#F, NULL, 0 ); \ + astWatchSTATUS( \ +\ + RESULT = astMask##X( astI2P( *THIS ), astI2P( *MAP ), \ + F77_ISTRUE( *INSIDE ) ? 1 : 0, *NDIM, \ + LBND, UBND, (Xtype *) IN, *VAL ); \ + ) \ + return RESULT; \ +} + +/* Invoke the above macro to define a function for each data + type. Include synonyms for some functions. */ +MAKE_AST_MASK(d,D,DOUBLE,D,double) +MAKE_AST_MASK(r,R,REAL,F,float) +MAKE_AST_MASK(i,I,INTEGER,I,int) +MAKE_AST_MASK(ui,UI,INTEGER,UI,unsigned int) +MAKE_AST_MASK(s,S,WORD,S,short int) +MAKE_AST_MASK(us,US,UWORD,US,unsigned short int) +MAKE_AST_MASK(w,W,WORD,S,short int) +MAKE_AST_MASK(uw,UW,UWORD,US,unsigned short int) +MAKE_AST_MASK(b,B,BYTE,B,signed char) +MAKE_AST_MASK(ub,UB,UBYTE,UB,unsigned char) +#undef MAKE_AST_MASK + +F77_SUBROUTINE(ast_getregionbounds)( INTEGER(THIS), + DOUBLE(LBND), + DOUBLE(UBND), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_DOUBLE(XOUT) + GENPTR_DOUBLE(YOUT) + + astAt( "AST_GETREGIONBOUNDS", NULL, 0 ); + astWatchSTATUS( + astGetRegionBounds( astI2P( *THIS ), LBND, UBND ); + ) +} + +F77_SUBROUTINE(ast_showmesh)( INTEGER(THIS), + LOGICAL(FORMAT), + CHARACTER(TTL), + INTEGER(STATUS) + TRAIL(TTL) ) { + GENPTR_INTEGER(THIS) + GENPTR_LOGICAL(FORMAT) + GENPTR_CHARACTER(TTL) + char *ttl; + + astAt( "AST_SHOWMESH", NULL, 0 ); + astWatchSTATUS( + ttl = astString( TTL, TTL_length ); + astShowMesh( astI2P( *THIS ), F77_ISTRUE( *FORMAT ) ? 1 : 0, ttl ); + ttl = astFree( ttl ); + ) +} + +F77_SUBROUTINE(ast_getregionpoints)( INTEGER(THIS), + INTEGER(MAXPOINT), + INTEGER(MAXCOORD), + INTEGER(NPOINT), + DOUBLE_ARRAY(POINTS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(MAXPOINT) + GENPTR_INTEGER(MAXCOORD) + GENPTR_INTEGER(NPOINT) + GENPTR_DOUBLE_ARRAY(POINTS) + + astAt( "AST_GETREGIONPOINT", NULL, 0 ); + astWatchSTATUS( + astGetRegionPoints( astI2P( *THIS ), *MAXPOINT, *MAXCOORD, NPOINT, + POINTS ); + ) +} + +F77_SUBROUTINE(ast_getregionmesh)( INTEGER(THIS), + LOGICAL(SURFACE), + INTEGER(MAXPOINT), + INTEGER(MAXCOORD), + INTEGER(NPOINT), + DOUBLE_ARRAY(POINTS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_LOGICAL(SURFACE) + GENPTR_INTEGER(MAXPOINT) + GENPTR_INTEGER(MAXCOORD) + GENPTR_INTEGER(NPOINT) + GENPTR_DOUBLE_ARRAY(POINTS) + + astAt( "AST_GETREGIONMESH", NULL, 0 ); + astWatchSTATUS( + astGetRegionMesh( astI2P( *THIS ), F77_ISTRUE( *SURFACE ), *MAXPOINT, + *MAXCOORD, NPOINT, POINTS ); + ) +} + + diff --git a/fronta.pdf b/fronta.pdf new file mode 100644 index 0000000..fc0cd6e Binary files /dev/null and b/fronta.pdf differ diff --git a/fronta_bw.pdf b/fronta_bw.pdf new file mode 100644 index 0000000..099405e Binary files /dev/null and b/fronta_bw.pdf differ diff --git a/frontb.pdf b/frontb.pdf new file mode 100644 index 0000000..e01d2cf Binary files /dev/null and b/frontb.pdf differ diff --git a/frontb_bw.pdf b/frontb_bw.pdf new file mode 100644 index 0000000..d5a0e67 Binary files /dev/null and b/frontb_bw.pdf differ diff --git a/frontc.pdf b/frontc.pdf new file mode 100644 index 0000000..ec9228c Binary files /dev/null and b/frontc.pdf differ diff --git a/frontc_bw.pdf b/frontc_bw.pdf new file mode 100644 index 0000000..d6dd1c8 Binary files /dev/null and b/frontc_bw.pdf differ diff --git a/fsalign.pdf b/fsalign.pdf new file mode 100644 index 0000000..e1c0c97 Binary files /dev/null and b/fsalign.pdf differ diff --git a/fsconvert.pdf b/fsconvert.pdf new file mode 100644 index 0000000..1665762 Binary files /dev/null and b/fsconvert.pdf differ diff --git a/fselectormap.c b/fselectormap.c new file mode 100644 index 0000000..4ecc4a7 --- /dev/null +++ b/fselectormap.c @@ -0,0 +1,115 @@ +/* +*+ +* Name: +* fselectormap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SelectorMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SelectorMap class. + +* Routines Defined: +* AST_ISASELECTORMAP +* AST_SELECTORMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S.Berry (Starlink) + +* History: +* 14-MAR-2006 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "selectormap.h" /* C interface to the SelectorMap class */ + +F77_LOGICAL_FUNCTION(ast_isaselectormap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISASELECTORMAP", NULL, 0 ); + RESULT = astIsASelectorMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_selectormap)( INTEGER(NREG), + INTEGER_ARRAY(REGS), + DOUBLE(BADVAL), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NREG) + GENPTR_INTEGER_ARRAY(REGS) + GENPTR_DOUBLE(BADVAL) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + AstObject **regs; + + astAt( "AST_SELECTORMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + regs = astMalloc( sizeof(AstObject *) * (*NREG) ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + + for ( i = 0; i < *NREG; i++ ) { + regs[ i ] = astI2P( REGS[ i ] ); + } + } + + + RESULT = astP2I( astSelectorMap( *NREG, (void **) regs, *BADVAL, "%s", + options ) ); + astFree( regs ); + astFree( options ); + ) + return RESULT; +} diff --git a/fsexample.pdf b/fsexample.pdf new file mode 100644 index 0000000..4b7d1df Binary files /dev/null and b/fsexample.pdf differ diff --git a/fshiftmap.c b/fshiftmap.c new file mode 100644 index 0000000..d876071 --- /dev/null +++ b/fshiftmap.c @@ -0,0 +1,103 @@ +/* +*+ +* Name: +* fshiftmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST ShiftMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the ShiftMap class. + +* Routines Defined: +* AST_ISASHIFTMAP +* AST_SHIFTMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 18-AUG-2003 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "shiftmap.h" /* C interface to the ShiftMap class */ + +F77_LOGICAL_FUNCTION(ast_isashiftmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASHIFTMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAShiftMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_shiftmap)( INTEGER(NAXES), + DOUBLE(SHIFT), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NAXES) + GENPTR_DOUBLE(SHIFT) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_SHIFTMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astShiftMap( *NAXES, SHIFT, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fskyframe.c b/fskyframe.c new file mode 100644 index 0000000..ebdbaaa --- /dev/null +++ b/fskyframe.c @@ -0,0 +1,112 @@ +/* +*+ +* Name: +* fskyframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SkyFrame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SkyFrame class. + +* Routines Defined: +* AST_ISASKYFRAME +* AST_SKYFRAME + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 23-JUL-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "skyframe.h" /* C interface to the SkyFrame class */ + +F77_LOGICAL_FUNCTION(ast_isaskyframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASKYFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsASkyFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_skyframe)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_SKYFRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astSkyFrame( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_skyoffsetmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_SKYOFFSETMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astSkyOffsetMap( astI2P( *THIS ) ) ); + ) + return RESULT; +} + diff --git a/fslamap.c b/fslamap.c new file mode 100644 index 0000000..f90955e --- /dev/null +++ b/fslamap.c @@ -0,0 +1,120 @@ +/* +*+ +* Name: +* fslamap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SlaMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SlaMap class. + +* Routines Defined: +* AST_ISASLAMAP +* AST_SLAADD +* AST_SLAMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 28-MAY-1997 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "slamap.h" /* C interface to the SlaMap class */ + +F77_LOGICAL_FUNCTION(ast_isaslamap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASLAMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsASlaMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_slaadd)( INTEGER(THIS), + CHARACTER(CVT), + DOUBLE_ARRAY(ARGS), + INTEGER(STATUS) + TRAIL(CVT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CVT) + GENPTR_DOUBLE_ARRAY(ARGS) + char *cvt; + + astAt( "AST_SLAADD", NULL, 0 ); + astWatchSTATUS( + cvt = astString( CVT, CVT_length ); + astSlaAdd( astI2P( *THIS ), cvt, ARGS ); + astFree( cvt ); + ) +} + +F77_INTEGER_FUNCTION(ast_slamap)( INTEGER(FLAGS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FLAGS) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_SLAMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astSlaMap( *FLAGS, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fsmerge.pdf b/fsmerge.pdf new file mode 100644 index 0000000..33fe7dc Binary files /dev/null and b/fsmerge.pdf differ diff --git a/fspecfluxframe.c b/fspecfluxframe.c new file mode 100644 index 0000000..fb1c218 --- /dev/null +++ b/fspecfluxframe.c @@ -0,0 +1,104 @@ +/* +*+ +* Name: +* fspecfluxframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SpecFluxFrame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SpecFluxFrame class. + +* Routines Defined: +* AST_SPECFLUXFRAME +* AST_ISASPECFLUXFRAME + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 8-DEC-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "specfluxframe.h" /* C interface to the SpecFluxFrame class */ + +F77_LOGICAL_FUNCTION(ast_isaspecfluxframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASPECFLUXFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsASpecFluxFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_specfluxframe)( INTEGER(FRAME1), + INTEGER(FRAME2), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FRAME1) + GENPTR_INTEGER(FRAME2) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_SPECFLUXFRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astSpecFluxFrame( astI2P( *FRAME1 ), astI2P( *FRAME2 ), + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fspecframe.c b/fspecframe.c new file mode 100644 index 0000000..c45c7e3 --- /dev/null +++ b/fspecframe.c @@ -0,0 +1,134 @@ +/* +*+ +* Name: +* fspecframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SpecFrame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SpecFrame class. + +* Routines Defined: +* AST_ISASPECFRAME +* AST_SPECFRAME +* AST_SETREFPOS +* AST_GETREFPOS + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 20-NOV-2002 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "specframe.h" /* C interface to the SpecFrame class */ + +F77_LOGICAL_FUNCTION(ast_isaspecframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASPECFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsASpecFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_specframe)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_SPECFRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astSpecFrame( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_getrefpos)( INTEGER(THIS), + INTEGER(FRM), + DOUBLE(LON), + DOUBLE(LAT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(FRM) + GENPTR_DOUBLE(LON) + GENPTR_DOUBLE(LAT) + + astAt( "AST_GETREFPOS", NULL, 0 ); + astWatchSTATUS( + astGetRefPos( astI2P( *THIS ), astI2P( *FRM ), LON, LAT ); + ) +} + +F77_SUBROUTINE(ast_setrefpos)( INTEGER(THIS), + INTEGER(FRM), + DOUBLE(LON), + DOUBLE(LAT), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(FRM) + GENPTR_DOUBLE(LON) + GENPTR_DOUBLE(LAT) + + astAt( "AST_SETREFPOS", NULL, 0 ); + astWatchSTATUS( + astSetRefPos( astI2P( *THIS ), astI2P( *FRM ), *LON, *LAT ); + ) +} + diff --git a/fspecmap.c b/fspecmap.c new file mode 100644 index 0000000..e72a2f7 --- /dev/null +++ b/fspecmap.c @@ -0,0 +1,122 @@ +/* +*+ +* Name: +* fspecmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SpecMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SpecMap class. + +* Routines Defined: +* AST_ISASPECMAP +* AST_SPECADD +* AST_SPECMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 11-NOV-2002 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "specmap.h" /* C interface to the SpecMap class */ + +F77_LOGICAL_FUNCTION(ast_isaspecmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASPECMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsASpecMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_specadd)( INTEGER(THIS), + CHARACTER(CVT), + DOUBLE_ARRAY(ARGS), + INTEGER(STATUS) + TRAIL(CVT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CVT) + GENPTR_DOUBLE_ARRAY(ARGS) + char *cvt; + + astAt( "AST_SPECADD", NULL, 0 ); + astWatchSTATUS( + cvt = astString( CVT, CVT_length ); + astSpecAdd( astI2P( *THIS ), cvt, ARGS ); + astFree( cvt ); + ) +} + +F77_INTEGER_FUNCTION(ast_specmap)( INTEGER(NIN), + INTEGER(FLAGS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NIN) + GENPTR_INTEGER(FLAGS) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_SPECMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astSpecMap( *NIN, *FLAGS, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fsphmap.c b/fsphmap.c new file mode 100644 index 0000000..439fec9 --- /dev/null +++ b/fsphmap.c @@ -0,0 +1,99 @@ +/* +*+ +* Name: +* fsphmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SphMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SphMap class. + +* Routines Defined: +* AST_ISASPHMAP +* AST_SPHMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 25-OCT-1996 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "sphmap.h" /* C interface to the SphMap class */ + +F77_LOGICAL_FUNCTION(ast_isasphmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASPHMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsASphMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_sphmap)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_SPHMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astSphMap( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fsremap.pdf b/fsremap.pdf new file mode 100644 index 0000000..3554420 Binary files /dev/null and b/fsremap.pdf differ diff --git a/fstc.c b/fstc.c new file mode 100644 index 0000000..50c53bb --- /dev/null +++ b/fstc.c @@ -0,0 +1,114 @@ +/* +*+ +* Name: +* fstc.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Stc class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Stc class. + +* Routines Defined: +* AST_ISASTC +* AST_GETSTCREGION +* AST_GETSTCCOORD +* AST_GETSTCNCOORD + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-NOV-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "stc.h" /* C interface to the Stc class */ + + +F77_INTEGER_FUNCTION(ast_getstcncoord)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETSTCNCOORD", NULL, 0 ); + astWatchSTATUS( + RESULT = astGetStcNCoord( astI2P( *THIS ) ); + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_getstccoord)( INTEGER(THIS), + INTEGER(ICOORD), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(ICOORD) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETSTCCOORD", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetStcCoord( astI2P( *THIS ), *ICOORD ) ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isastc)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASTC", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAStc( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_getstcregion)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_INTEGER_TYPE(RESULT); + + astAt( "AST_GETSTCREGION", NULL, 0 ); + astWatchSTATUS( + RESULT = astP2I( astGetStcRegion( astI2P( *THIS ) ) ); + ) + return RESULT; +} + + diff --git a/fstccatalogentrylocation.c b/fstccatalogentrylocation.c new file mode 100644 index 0000000..5c88aa0 --- /dev/null +++ b/fstccatalogentrylocation.c @@ -0,0 +1,117 @@ +/* +*+ +* Name: +* fstccatalogentrylocation.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST StcCatalogEntryLocation class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the StcCatalogEntryLocation class. + +* Routines Defined: +* AST_ISASTCCATALOGENTRYLOCATION +* AST_STCCATALOGENTRYLOCATION + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-NOV-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "stccatalogentrylocation.h" /* C interface to the StcCatalogEntryLocation class */ + + +F77_LOGICAL_FUNCTION(ast_isastccatalogentrylocation)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASTCCATALOGENTRYLOCATION", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAStcCatalogEntryLocation( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_stccatalogentrylocation)( INTEGER(REG), + INTEGER(NCOORDS), + INTEGER_ARRAY(COORDS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(REG) + GENPTR_INTEGER(NCOORDS) + GENPTR_CHARACTER(OPTIONS) + GENPTR_INTEGER_ARRAY(COORDS) + AstKeyMap **coords; + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_STCCATALOGENTRYLOCATION", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + +/* Convert supplied integers to pointers. */ + coords = astMalloc( sizeof( AstKeyMap * )*(size_t)( *NCOORDS )); + if( astOK ) { + for( i = 0; i < *NCOORDS; i++ ) { + coords[ i ] = (AstKeyMap *) astMakePointer( astI2P( COORDS[ i ] )); + } + } + + RESULT = astP2I( astStcCatalogEntryLocation( astI2P( *REG ), *NCOORDS, + coords, "%s", options ) ); + astFree( coords ); + astFree( options ); + ) + return RESULT; +} diff --git a/fstcobsdatalocation.c b/fstcobsdatalocation.c new file mode 100644 index 0000000..767ebb4 --- /dev/null +++ b/fstcobsdatalocation.c @@ -0,0 +1,117 @@ +/* +*+ +* Name: +* fstcobsdatalocation.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST StcObsDataLocation class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the StcObsDataLocation class. + +* Routines Defined: +* AST_ISASTCOBSDATALOCATION +* AST_STCOBSDATALOCATION + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-NOV-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "stcobsdatalocation.h" /* C interface to the StcObsDataLocation class */ + + +F77_LOGICAL_FUNCTION(ast_isastcobsdatalocation)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASTCOBSDATALOCATION", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAStcObsDataLocation( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_stcobsdatalocation)( INTEGER(REG), + INTEGER(NCOORDS), + INTEGER_ARRAY(COORDS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(REG) + GENPTR_INTEGER(NCOORDS) + GENPTR_CHARACTER(OPTIONS) + GENPTR_INTEGER_ARRAY(COORDS) + AstKeyMap **coords; + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_STCOBSDATALOCATION", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + +/* Convert supplied integers to pointers. */ + coords = astMalloc( sizeof( AstKeyMap * )*(size_t)( *NCOORDS )); + if( astOK ) { + for( i = 0; i < *NCOORDS; i++ ) { + coords[ i ] = (AstKeyMap *) astMakePointer( astI2P( COORDS[ i ] )); + } + } + + RESULT = astP2I( astStcObsDataLocation( astI2P( *REG ), *NCOORDS, + coords, "%s", options ) ); + astFree( coords ); + astFree( options ); + ) + return RESULT; +} diff --git a/fstcresourceprofile.c b/fstcresourceprofile.c new file mode 100644 index 0000000..04484f3 --- /dev/null +++ b/fstcresourceprofile.c @@ -0,0 +1,118 @@ +/* +*+ +* Name: +* fstcresourceprofile.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST StcResourceProfile class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the StcResourceProfile class. + +* Routines Defined: +* AST_ISASTCRESOURCEPROFILE +* AST_STCRESOURCEPROFILE + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-NOV-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "object.h" /* Basic AST Object management functions */ +#include "stcresourceprofile.h" /* C interface to the StcResourceProfile class */ + + +F77_LOGICAL_FUNCTION(ast_isastcresourceprofile)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASTCRESOURCEPROFILE", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAStcResourceProfile( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_stcresourceprofile)( INTEGER(REG), + INTEGER(NCOORDS), + INTEGER_ARRAY(COORDS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(REG) + GENPTR_INTEGER(NCOORDS) + GENPTR_CHARACTER(OPTIONS) + GENPTR_INTEGER_ARRAY(COORDS) + AstKeyMap **coords; + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_STCRESOURCEPROFILE", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + +/* Convert supplied integers to pointers. */ + coords = astMalloc( sizeof( AstKeyMap * )*(size_t)( *NCOORDS )); + if( astOK ) { + for( i = 0; i < *NCOORDS; i++ ) { + coords[ i ] = (AstKeyMap *) astMakePointer( astI2P( COORDS[ i ] )); + } + } + + RESULT = astP2I( astStcResourceProfile( astI2P( *REG ), *NCOORDS, + coords, "%s", options ) ); + astFree( coords ); + astFree( options ); + ) + return RESULT; +} diff --git a/fstcschan.c b/fstcschan.c new file mode 100644 index 0000000..9f47469 --- /dev/null +++ b/fstcschan.c @@ -0,0 +1,131 @@ +/* +*+ +* Name: +* fstcschan.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST StcsChan class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the StcsChan class. + +* Routines Defined: +* AST_STCSCHAN +* AST_ISASTCSCHAN + +* Copyright: +* Copyright (C) 2008 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (JAC,UCLan) + +* History: +* 18-DEC-2008 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "channel.h" /* Provides wrapper functions */ +#include "stcschan.h" /* C interface to the StcsChan class */ + +#include + +/* Prototypes for external functions. */ +/* ================================== */ +/* This is the null function defined by the FORTRAN interface in fobject.c. */ +F77_SUBROUTINE(ast_null)( void ); + +/* FORTRAN interface functions. */ +/* ============================ */ +/* These functions implement the remainder of the FORTRAN interface. */ +F77_INTEGER_FUNCTION(ast_stcschan)( void (* SOURCE)(), + void (* SINK)(), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + const char *(* source)( void ); + int i; + void (* sink)( const char * ); + + astAt( "AST_STCSCHAN", NULL, 0 ); + astWatchSTATUS( + +/* Set the source and sink function pointers to NULL if a pointer to + the null routine AST_NULL has been supplied. */ + source = (const char *(*)( void )) SOURCE; + if ( source == (const char *(*)( void )) F77_EXTERNAL_NAME(ast_null) ) { + source = NULL; + } + sink = (void (*)( const char * )) SINK; + if ( sink == (void (*)( const char * )) F77_EXTERNAL_NAME(ast_null) ) { + sink = NULL; + } + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astStcsChanFor( source, astSourceWrap, sink, astSinkWrap, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isastcschan)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASTCSCHAN", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAStcsChan( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + + + + + diff --git a/fstcsearchlocation.c b/fstcsearchlocation.c new file mode 100644 index 0000000..aa74baf --- /dev/null +++ b/fstcsearchlocation.c @@ -0,0 +1,117 @@ +/* +*+ +* Name: +* fstcsearchlocation.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST StcSearchLocation class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the StcSearchLocation class. + +* Routines Defined: +* AST_ISASTCSEARCHLOCATION +* AST_STCSEARCHLOCATION + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-NOV-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "stcsearchlocation.h" /* C interface to the StcSearchLocation class */ + + +F77_LOGICAL_FUNCTION(ast_isastcsearchlocation)( INTEGER(THIS), INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISASTCSEARCHLOCATION", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAStcSearchLocation( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_stcsearchlocation)( INTEGER(REG), + INTEGER(NCOORDS), + INTEGER_ARRAY(COORDS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(REG) + GENPTR_INTEGER(NCOORDS) + GENPTR_CHARACTER(OPTIONS) + GENPTR_INTEGER_ARRAY(COORDS) + AstKeyMap **coords; + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_STCSEARCHLOCATION", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + +/* Convert supplied integers to pointers. */ + coords = astMalloc( sizeof( AstKeyMap * )*(size_t)( *NCOORDS )); + if( astOK ) { + for( i = 0; i < *NCOORDS; i++ ) { + coords[ i ] = (AstKeyMap *) astMakePointer( astI2P( COORDS[ i ] )); + } + } + + RESULT = astP2I( astStcSearchLocation( astI2P( *REG ), *NCOORDS, + coords, "%s", options ) ); + astFree( coords ); + astFree( options ); + ) + return RESULT; +} diff --git a/fswitchmap.c b/fswitchmap.c new file mode 100644 index 0000000..0159420 --- /dev/null +++ b/fswitchmap.c @@ -0,0 +1,118 @@ +/* +*+ +* Name: +* fswitchmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST SwitchMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the SwitchMap class. + +* Routines Defined: +* AST_ISASWITCHMAP +* AST_SWITCHMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S.Berry (Starlink) + +* History: +* 13-MAR-2006 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "switchmap.h" /* C interface to the SwitchMap class */ + +F77_LOGICAL_FUNCTION(ast_isaswitchmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISASWITCHMAP", NULL, 0 ); + RESULT = astIsASwitchMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_switchmap)( INTEGER(FSMAP), + INTEGER(ISMAP), + INTEGER(NROUTE), + INTEGER_ARRAY(ROUTEMAPS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FSMAP) + GENPTR_INTEGER(ISMAP) + GENPTR_INTEGER(NROUTE) + GENPTR_INTEGER_ARRAY(ROUTEMAPS) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + AstObject **routemaps; + + astAt( "AST_SWITCHMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + routemaps = astMalloc( sizeof(AstObject *) * (*NROUTE) ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + + for ( i = 0; i < *NROUTE; i++ ) { + routemaps[ i ] = astI2P( ROUTEMAPS[ i ] ); + } + } + + + RESULT = astP2I( astSwitchMap( astI2P( *FSMAP ), astI2P( *ISMAP ), + *NROUTE, (void **) routemaps, "%s", + options ) ); + astFree( routemaps ); + astFree( options ); + ) + return RESULT; +} diff --git a/ftable.c b/ftable.c new file mode 100644 index 0000000..5656531 --- /dev/null +++ b/ftable.c @@ -0,0 +1,330 @@ +/* +*+ +* Name: +* ftable.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST Table class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the Table class. + +* Routines Defined: +* AST_ISATABLE +* AST_TABLE + +* Copyright: +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S.Berry (Starlink) + +* History: +* 22-NOV-2010 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "table.h" /* C interface to the Table class */ + +F77_LOGICAL_FUNCTION(ast_isatable)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISATABLE", NULL, 0 ); + RESULT = astIsATable( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_table)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_TABLE", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astTable( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_SUBROUTINE(ast_addcolumn)( INTEGER(THIS), + CHARACTER(NAME), + INTEGER(TYPE), + INTEGER(NDIM), + INTEGER_ARRAY(DIMS), + CHARACTER(UNIT), + INTEGER(STATUS) + TRAIL(NAME) + TRAIL(UNIT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + GENPTR_INTEGER(TYPE) + GENPTR_INTEGER(NDIM) + GENPTR_INTEGER_ARRAY(DIMS) + GENPTR_CHARACTER(UNIT) + char *name, *unit; + + astAt( "AST_ADDCOLUMN", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + unit = astString( UNIT, UNIT_length ); + astAddColumn( astI2P( *THIS ), name, *TYPE, *NDIM, DIMS, unit ); + astFree( name ); + astFree( unit ); + ) +} + +F77_SUBROUTINE(ast_addparameter)( INTEGER(THIS), + CHARACTER(NAME), + INTEGER(STATUS) + TRAIL(NAME) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + char *name; + + astAt( "AST_ADDPARAMETER", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + astAddParameter( astI2P( *THIS ), name ); + astFree( name ); + ) +} + +F77_SUBROUTINE(ast_removecolumn)( INTEGER(THIS), + CHARACTER(NAME), + INTEGER(STATUS) + TRAIL(NAME) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + char *name; + + astAt( "AST_REMOVECOLUMN", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + astRemoveColumn( astI2P( *THIS ), name ); + astFree( name ); + ) +} + +F77_SUBROUTINE(ast_removeparameter)( INTEGER(THIS), + CHARACTER(NAME), + INTEGER(STATUS) + TRAIL(NAME) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(NAME) + char *name; + + astAt( "AST_REMOVEPARAMETER", NULL, 0 ); + astWatchSTATUS( + name = astString( NAME, NAME_length ); + astRemoveParameter( astI2P( *THIS ), name ); + astFree( name ); + ) +} + +F77_SUBROUTINE(ast_removerow)( INTEGER(THIS), + INTEGER(INDEX), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(INDEX) + + astAt( "AST_REMOVEROW", NULL, 0 ); + astWatchSTATUS( + astRemoveRow( astI2P( *THIS ), *INDEX ); + ) +} + +F77_SUBROUTINE(ast_purgerows)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + + astAt( "AST_PURGEROWS", NULL, 0 ); + astWatchSTATUS( + astPurgeRows( astI2P( *THIS ) ); + ) +} + + +/* NO_CHAR_FUNCTION indicates that the f77.h method of returning a + character result doesn't work, so add an extra argument instead and + wrap this function up in a normal FORTRAN 77 function (in the file + keymap.f). */ +#if NO_CHAR_FUNCTION +F77_SUBROUTINE(ast_columnname_a)( CHARACTER(RESULT), +#else +F77_SUBROUTINE(ast_columnname)( CHARACTER_RETURN_VALUE(RESULT), +#endif + INTEGER(THIS), + INTEGER(INDEX), +#if NO_CHAR_FUNCTION + INTEGER(STATUS) + TRAIL(RESULT) ) { +#else + INTEGER(STATUS) ) { +#endif + GENPTR_CHARACTER(RESULT) + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(INDEX) + const char *result; + int i; + + astAt( "AST_COLUMNNAME", NULL, 0 ); + astWatchSTATUS( + result = astColumnName( astI2P( *THIS ), *INDEX ); + i = 0; + if ( astOK ) { /* Copy result */ + for ( ; result[ i ] && i < RESULT_length; i++ ) { + RESULT[ i ] = result[ i ]; + } + } + while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */ + ) +} + +/* NO_CHAR_FUNCTION indicates that the f77.h method of returning a + character result doesn't work, so add an extra argument instead and + wrap this function up in a normal FORTRAN 77 function (in the file + keymap.f). */ +#if NO_CHAR_FUNCTION +F77_SUBROUTINE(ast_parametername_a)( CHARACTER(RESULT), +#else +F77_SUBROUTINE(ast_parametername)( CHARACTER_RETURN_VALUE(RESULT), +#endif + INTEGER(THIS), + INTEGER(INDEX), +#if NO_CHAR_FUNCTION + INTEGER(STATUS) + TRAIL(RESULT) ) { +#else + INTEGER(STATUS) ) { +#endif + GENPTR_CHARACTER(RESULT) + GENPTR_INTEGER(THIS) + GENPTR_INTEGER(INDEX) + const char *result; + int i; + + astAt( "AST_PARAMETERNAME", NULL, 0 ); + astWatchSTATUS( + result = astParameterName( astI2P( *THIS ), *INDEX ); + i = 0; + if ( astOK ) { /* Copy result */ + for ( ; result[ i ] && i < RESULT_length; i++ ) { + RESULT[ i ] = result[ i ]; + } + } + while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */ + ) +} + + +F77_SUBROUTINE(ast_columnshape)( INTEGER(THIS), + CHARACTER(COLUMN), + INTEGER(MXDIM), + INTEGER(NDIM), + INTEGER_ARRAY(DIMS), + INTEGER(STATUS) + TRAIL(COLUMN) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(COLUMN) + GENPTR_INTEGER(MXDIM) + GENPTR_INTEGER(NDIM) + GENPTR_INTEGER_ARRAY(DIMS) + char *column; + + astAt( "AST_COLUMNSHAPE", NULL, 0 ); + astWatchSTATUS( + column = astString( COLUMN, COLUMN_length ); + astColumnShape( astI2P( *THIS ), column, *MXDIM, NDIM, DIMS ); + astFree( column ); + ) +} + + +F77_LOGICAL_FUNCTION(ast_hascolumn)( INTEGER(THIS), + CHARACTER(COLUMN), + INTEGER(STATUS) + TRAIL(COLUMN) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(COLUMN) + F77_LOGICAL_TYPE(RESULT); + char *column; + + astWatchSTATUS( + astAt( "AST_HASCOLUMN", NULL, 0 ); + column = astString( COLUMN, COLUMN_length ); + RESULT = astHasColumn( astI2P( *THIS ), column ) ? F77_TRUE : F77_FALSE; + astFree( column ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_hasparameter)( INTEGER(THIS), + CHARACTER(PARAM), + INTEGER(STATUS) + TRAIL(PARAM) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(PARAM) + F77_LOGICAL_TYPE(RESULT); + char *param; + + astWatchSTATUS( + astAt( "AST_HASPARAMETER", NULL, 0 ); + param = astString( PARAM, PARAM_length ); + RESULT = astHasParameter( astI2P( *THIS ), param ) ? F77_TRUE : F77_FALSE; + astFree( param ); + ) + return RESULT; +} + diff --git a/ftemplateclass.c b/ftemplateclass.c new file mode 100644 index 0000000..28dca89 --- /dev/null +++ b/ftemplateclass.c @@ -0,0 +1,109 @@ +1 - Replace TemplateClass with capitalised class name +2 - Replace templateclass with lower case class name +3 - Replace TEMPLATECLASS with upper case class name +4 - Replace TemplateParent with capitalised parent class name +5 - Replace templateparent with lower case parent class name +6 - Replace all occurrences of >>> with suitable text + +/* +*+ +* Name: +* ftemplateclass.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST TemplateClass class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the TemplateClass class. + +* Routines Defined: +* AST_ISATEMPLATECLASS +* AST_TEMPLATECLASS + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* >>> 20-NOV-2002 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "templateclass.h" /* C interface to the TemplateClass class */ + +F77_LOGICAL_FUNCTION(ast_isatemplateclass)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISATEMPLATECLASS", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsATemplateClass( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_templateclass)( >>> CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { +>>> + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_TEMPLATECLASS", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astTemplateClass( >>> "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +>>> diff --git a/ftimeframe.c b/ftimeframe.c new file mode 100644 index 0000000..f164c26 --- /dev/null +++ b/ftimeframe.c @@ -0,0 +1,114 @@ +/* +*+ +* Name: +* ftimeframe.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST TimeFrame class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the TimeFrame class. + +* Routines Defined: +* AST_ISATIMEFRAME +* AST_TIMEFRAME +* AST_CURRENTTIME + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* NG: Norman Gray (Starlink) + +* History: +* 02-AUG-2003 (NG): +* Original version, heavily based on fspecframe.c. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "timeframe.h" /* C interface to the TimeFrame class */ + +F77_LOGICAL_FUNCTION(ast_isatimeframe)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISATIMEFRAME", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsATimeFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_timeframe)( CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_TIMEFRAME", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astTimeFrame( "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_DOUBLE_FUNCTION(ast_currenttime)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_DOUBLE_TYPE(RESULT); + + astAt( "AST_CURRENTTIME", NULL, 0 ); + astWatchSTATUS( + RESULT = astCurrentTime( astI2P( *THIS ) ); + ) + return RESULT; +} + + diff --git a/ftimemap.c b/ftimemap.c new file mode 100644 index 0000000..bdc5ba4 --- /dev/null +++ b/ftimemap.c @@ -0,0 +1,120 @@ +/* +*+ +* Name: +* ftimemap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST TimeMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the TimeMap class. + +* Routines Defined: +* AST_ISATIMEMAP +* AST_TIMEADD +* AST_TIMEMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* NG: Norman Gray (Starlink) + +* History: +* 08-Sep-2003 (NG): +* Original version (heavily based on fspecmap.c) +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "timemap.h" /* C interface to the TimeMap class */ + +F77_LOGICAL_FUNCTION(ast_isatimemap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISATIMEMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsATimeMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_SUBROUTINE(ast_timeadd)( INTEGER(THIS), + CHARACTER(CVT), + DOUBLE_ARRAY(ARGS), + INTEGER(STATUS) + TRAIL(CVT) ) { + GENPTR_INTEGER(THIS) + GENPTR_CHARACTER(CVT) + GENPTR_DOUBLE_ARRAY(ARGS) + char *cvt; + + astAt( "AST_TIMEADD", NULL, 0 ); + astWatchSTATUS( + cvt = astString( CVT, CVT_length ); + astTimeAdd( astI2P( *THIS ), cvt, ARGS ); + astFree( cvt ); + ) +} + +F77_INTEGER_FUNCTION(ast_timemap)( INTEGER(FLAGS), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(FLAGS) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_TIMEMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astTimeMap( *FLAGS, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/ftranmap.c b/ftranmap.c new file mode 100644 index 0000000..155439b --- /dev/null +++ b/ftranmap.c @@ -0,0 +1,104 @@ +/* +*+ +* Name: +* ftranmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST TranMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the TranMap class. + +* Routines Defined: +* AST_ISATRANMAP +* AST_TRANMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S.Berry (Starlink) + +* History: +* 10-FEB-2004 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "tranmap.h" /* C interface to the TranMap class */ + +F77_LOGICAL_FUNCTION(ast_isatranmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astWatchSTATUS( + astAt( "AST_ISATRANMAP", NULL, 0 ); + RESULT = astIsATranMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_tranmap)( INTEGER(MAP1), + INTEGER(MAP2), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(MAP1) + GENPTR_INTEGER(MAP2) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + int i; + char *options; + + astAt( "AST_TRANMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astTranMap( astI2P( *MAP1 ), astI2P( *MAP2 ), + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/funitmap.c b/funitmap.c new file mode 100644 index 0000000..986a82d --- /dev/null +++ b/funitmap.c @@ -0,0 +1,101 @@ +/* +*+ +* Name: +* funitmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST UnitMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the UnitMap class. + +* Routines Defined: +* AST_ISAUNITMAP +* AST_UNITMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 25-SEP-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "unitmap.h" /* C interface to the UnitMap class */ + +F77_LOGICAL_FUNCTION(ast_isaunitmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAUNITMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAUnitMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_unitmap)( INTEGER(NCOORD), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NCOORD) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_UNITMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astUnitMap( *NCOORD, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fwcsmap.c b/fwcsmap.c new file mode 100644 index 0000000..0b1e3a3 --- /dev/null +++ b/fwcsmap.c @@ -0,0 +1,108 @@ +/* +*+ +* Name: +* fwcsmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST WcsMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the WcsMap class. + +* Routines Defined: +* AST_ISAWCSMAP +* AST_WCSMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 18-NOV-1996 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "wcsmap.h" /* C interface to the WcsMap class */ + +F77_LOGICAL_FUNCTION(ast_isawcsmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAWCSMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAWcsMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_wcsmap)( INTEGER(NAXES), + INTEGER(TYPE), + INTEGER(LONAX), + INTEGER(LATAX), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NAXES) + GENPTR_INTEGER(TYPE) + GENPTR_INTEGER(LONAX) + GENPTR_INTEGER(LATAX) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_WCSMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astWcsMap( *NAXES, *TYPE, *LONAX, *LATAX, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fwinmap.c b/fwinmap.c new file mode 100644 index 0000000..41338a0 --- /dev/null +++ b/fwinmap.c @@ -0,0 +1,110 @@ +/* +*+ +* Name: +* fwinmap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST WinMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the WinMap class. + +* Routines Defined: +* AST_ISAWINMAP +* AST_WINMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 23-OCT-1996 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "winmap.h" /* C interface to the WinMap class */ + +F77_LOGICAL_FUNCTION(ast_isawinmap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAWINMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAWinMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_winmap)( INTEGER(NAXES), + DOUBLE(C1_IN), + DOUBLE(C2_IN), + DOUBLE(C1_OUT), + DOUBLE(C2_OUT), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NAXES) + GENPTR_DOUBLE(C1_IN) + GENPTR_DOUBLE(C2_IN) + GENPTR_DOUBLE(C1_OUT) + GENPTR_DOUBLE(C2_OUT) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_WINMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astWinMap( *NAXES, C1_IN, C2_IN, C1_OUT, C2_OUT, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/fxmlchan.c b/fxmlchan.c new file mode 100644 index 0000000..17efc50 --- /dev/null +++ b/fxmlchan.c @@ -0,0 +1,130 @@ +/* +*+ +* Name: +* fxmlchan.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST XmlChan class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the XmlChan class. + +* Routines Defined: +* AST_XMLCHAN +* AST_ISAXMLCHAN + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 21-OCT-2003 (DSB): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "channel.h" /* Provides wrapper functions */ +#include "xmlchan.h" /* C interface to the XmlChan class */ + +#include + +/* Prototypes for external functions. */ +/* ================================== */ +/* This is the null function defined by the FORTRAN interface in fobject.c. */ +F77_SUBROUTINE(ast_null)( void ); + +/* FORTRAN interface functions. */ +/* ============================ */ +/* These functions implement the remainder of the FORTRAN interface. */ +F77_INTEGER_FUNCTION(ast_xmlchan)( void (* SOURCE)(), + void (* SINK)(), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + const char *(* source)( void ); + int i; + void (* sink)( const char * ); + + astAt( "AST_XMLCHAN", NULL, 0 ); + astWatchSTATUS( + +/* Set the source and sink function pointers to NULL if a pointer to + the null routine AST_NULL has been supplied. */ + source = (const char *(*)( void )) SOURCE; + if ( source == (const char *(*)( void )) F77_EXTERNAL_NAME(ast_null) ) { + source = NULL; + } + sink = (void (*)( const char * )) SINK; + if ( sink == (void (*)( const char * )) F77_EXTERNAL_NAME(ast_null) ) { + sink = NULL; + } + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astXmlChanFor( source, astSourceWrap, sink, astSinkWrap, + "%s", options ) ); + astFree( options ); + ) + return RESULT; +} + +F77_LOGICAL_FUNCTION(ast_isaxmlchan)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAXMLCHAN", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAXmlChan( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + + + + diff --git a/fzoommap.c b/fzoommap.c new file mode 100644 index 0000000..d2c8e48 --- /dev/null +++ b/fzoommap.c @@ -0,0 +1,103 @@ +/* +*+ +* Name: +* fzoommap.c + +* Purpose: +* Define a FORTRAN 77 interface to the AST ZoomMap class. + +* Type of Module: +* C source file. + +* Description: +* This file defines FORTRAN 77-callable C functions which provide +* a public FORTRAN 77 interface to the ZoomMap class. + +* Routines Defined: +* AST_ISAZOOMMAP +* AST_ZOOMMAP + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 18-JUL-1996 (RFWS): +* Original version. +*/ + +/* Define the astFORTRAN77 macro which prevents error messages from + AST C functions from reporting the file and line number where the + error occurred (since these would refer to this file, they would + not be useful). */ +#define astFORTRAN77 + +/* Header files. */ +/* ============= */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* F77 <-> C support functions/macros */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory handling facilities */ +#include "zoommap.h" /* C interface to the ZoomMap class */ + +F77_LOGICAL_FUNCTION(ast_isazoommap)( INTEGER(THIS), + INTEGER(STATUS) ) { + GENPTR_INTEGER(THIS) + F77_LOGICAL_TYPE(RESULT); + + astAt( "AST_ISAZOOMMAP", NULL, 0 ); + astWatchSTATUS( + RESULT = astIsAZoomMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE; + ) + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_zoommap)( INTEGER(NAXES), + DOUBLE(ZOOM), + CHARACTER(OPTIONS), + INTEGER(STATUS) + TRAIL(OPTIONS) ) { + GENPTR_INTEGER(NAXES) + GENPTR_DOUBLE(ZOOM) + GENPTR_CHARACTER(OPTIONS) + F77_INTEGER_TYPE(RESULT); + char *options; + int i; + + astAt( "AST_ZOOMMAP", NULL, 0 ); + astWatchSTATUS( + options = astString( OPTIONS, OPTIONS_length ); + +/* Truncate the options string to exlucde any trailing spaces. */ + astChrTrunc( options ); + +/* Change ',' to '\n' (see AST_SET in fobject.c for why). */ + if ( astOK ) { + for ( i = 0; options[ i ]; i++ ) { + if ( options[ i ] == ',' ) options[ i ] = '\n'; + } + } + RESULT = astP2I( astZoomMap( *NAXES, *ZOOM, "%s", options ) ); + astFree( options ); + ) + return RESULT; +} diff --git a/getatt b/getatt new file mode 100755 index 0000000..fb8bc78 --- /dev/null +++ b/getatt @@ -0,0 +1,163 @@ +#! /usr/bin/env perl +# This script requires that Perl be in the path, and that the +# environment variable SST_DIR be set to the location of the prolat +# binary. + + $C = 1; + $fortran = 0; + $prologue = 0; + $name = ""; + $name_section = 0; + $key = ""; + $unix_script = 0; + $cmt = quotemeta( "*" ); + +# Read switches. + while ( $_ = $ARGV[0], /^-/ ) { + shift; + last if /^--$/; + if ( /^-f/ ) { $fortran = 1; $C = 0; }; + if ( /^-u/ ) { $unix_script = 1; $cmt = quotemeta( "#" ); }; + if ( /^-att/ ) { $key = "att" }; + if ( /^-class/ ) { $key = "class" }; + } + +# Set up pattern for matching language-dependent lines. + $language_symbol = "c"; + if ( $fortran ) { $language_symbol = "f"; } + if ( $unix_script ) { + $lang = $cmt . "[fc]"; + $thislang = quotemeta( "#" . $language_symbol ); + } else { + $lang = "[fc]"; + $thislang = quotemeta( $language_symbol ); + } + +# Read input lines. + line: while (<>) { + +# Skip language-specific lines that aren't wanted. + if ( /^$lang/ ) { + next line if ( !/^$thislang/ ); + +# Put "*" in first column. + s/^$thislang/\*/; + } + +# Detect end of prologue. + if ( /^$cmt$key--/o ) { + $prologue = 0; + +# Append "*-" to prologue text and save the whole prologue as an element +# in the "pro" associative array. + $pro{ $name } = $text . "*-\n"; + +# Clear the name ready for next prologue. + $name = ""; + } + +# Process the prologue contents. + if ( $prologue ) { + +# Remove trailing blanks. + s/ *$//; + +# Convert/remove sections that don't appear in external documentation. + if ( $unix_script ) { + ; + } elsif ( !$key && $C ) { + s/^($cmt *)Synopsis:$/$1Invocation:/; + s/^($cmt *)Parameters:$/$1Arguments:/; + } elsif ( !$key && $fortran ) { + s/^($cmt *)Synopsis:$/$1Invocation:/; + s/^($cmt *)Parameters:$/$1Arguments:/; + } elsif ( $key =~ /att/ ) { + s/^($cmt *)Synopsis:$/$1Invocation:/; + } elsif ( $key =~ /class/ ) { + s/^($cmt *)Constructor Function:$/$1Invocation:/; + } + +# Skip the contents of these sections... + if ( /^$cmt *Type:$/ || + /^$cmt *Class Membership:$/ || + /^$cmt *Copyright:$/ ) { + while ( <> ) { if ( /^ *$/ ) { next line } }; + } + +# Remove any #include directives from the C Synopsis (Invocation) section. + if ( !$key && $C && !$unix_script ) { + if ( /^$cmt *Invocation:$/ ) { + while ( <> && /^$cmt *#include/ ) {}; + } + } + +# If in the "Name:" section, search for the routine name, noting when +# found. + if ( $name_section && ( ( $name ) = /^. *([^ ]*) *$/ ) ) { + $name_section = 0; + }; + +# Detect the "Name:" line itself. + if ( /^$cmt *Name:$/ ) { $name_section = 1 }; + +# Change to use the standard comment character "*". + s/^$cmt/\*/; + +# Append each prologue line to the end of the prologue text. + $text = $text . $_; + } + +# Detect start of prologue and initialise prologue text. + if ( /^$cmt$key\+\+/o ) { + $prologue = 1; + $name_section = 0; + $text = "*+\n"; + }; + } + +# Output prologues in alphabetical order to a scratch file. + open( TEMP, '>/tmp/getatt' ); + foreach $name ( sort( keys( %pro ) ) ) { print( TEMP $pro{ $name } ); } + close( TEMP ); + +# Write the names to a log file, escaping special (to Latex) characters. + open( NAMES, '>getatt.labels' ); + foreach $name ( sort( keys( %pro ) ) ) { + $name =~ s/_/\\_/g; + $name =~ s/\>/\$\>\$/g; + $name =~ s/\ ) { + if ( $unix_script ) { + ; + } elsif ( !$key && $C ) { + s/\\sstinvocation{/\\sstsynopsis{/; + s/\\sstarguments{/\\sstparameters{/; + } elsif ( $key =~ /att/ ) { + s/\\sstinvocation{/\\sstattributetype{/; + } elsif ( $key =~ /class/ ) { + s/\\sstinvocation{/\\sstconstructor{/; + } + +# Fix up constructs that don't otherwise convert to HTML properly. + s/{\\tt \'}/'/g; + s/{\\tt \"}/{\\tt{\"}}/g; + print; + } + close( LATEX ); + +# Delete the output file from PROLAT. + print( STDERR `rm -f /tmp/prolat.tex` ); diff --git a/getnewversion b/getnewversion new file mode 100644 index 0000000..b05f6d0 --- /dev/null +++ b/getnewversion @@ -0,0 +1,15 @@ + +# Prompt for new AST version number and store in "version.number" +# file in repository. Don't prompt if $AST_VERSION already specifies +# a version to use (just store the new value in the file). +version="${AST_VERSION}" +if test ! -n "${version}"; then + old_version="`cat ./version.number`" + echo; + echo "Always include a release number !!! (e.g. \"2.0-1\")" + echo -n "New version number /${old_version}/ > " + read version; + echo; + if test ! -n "${version}"; then version="${old_version}"; fi +fi +echo "${version}" >version.number diff --git a/globals.c b/globals.c new file mode 100644 index 0000000..ccb5a43 --- /dev/null +++ b/globals.c @@ -0,0 +1,251 @@ +#if defined( THREAD_SAFE ) + +#define astCLASS + +#include "globals.h" +#include "error.h" +#include +#include +#include + +/* Configuration results. */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* Select the appropriate memory management functions. These will be the + system's malloc, free and realloc unless AST was configured with the + "--with-starmem" option, in which case they will be the starmem + malloc, free and realloc. */ +#ifdef HAVE_STAR_MEM_H +# include +# define MALLOC starMalloc +# define FREE starFree +# define REALLOC starRealloc +#else +# define MALLOC malloc +# define FREE free +# define REALLOC realloc +#endif + +/* Module variables */ +/* ================ */ + +/* A count of the number of thread-specific data structures created so + far. Create a mutex to serialise access to this static variable. */ +static int nthread = 0; +static pthread_mutex_t nthread_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* External variables visible throughout AST */ +/* ========================================= */ + +/* Set a flag indicating that the thread-specific data key has not yet + been created. */ +pthread_once_t starlink_ast_globals_initialised = PTHREAD_ONCE_INIT; + +/* Declare the pthreads key that will be associated with the thread-specific + data for each thread. */ +pthread_key_t starlink_ast_globals_key; + +/* Declare the pthreads key that will be associated with the thread-specific + status value for each thread. */ +pthread_key_t starlink_ast_status_key; + + +/* Function definitions: */ +/* ===================== */ + + +void astGlobalsCreateKey_( void ) { +/* +*+ +* Name: +* astGlobalsCreateKey_ + +* Purpose: +* Create the thread specific data key used for accessing global data. + +* Type: +* Protected function. + +* Synopsis: +* #include "globals.h" +* astGlobalsCreateKey_() + +* Description: +* This function creates the thread-specific data key. It is called +* once only by the pthread_once function, which is invoked via the +* astGET_GLOBALS(this) macro by each AST function that requires access to +* global data. + +* Returned Value: +* Zero for success. + +*- +*/ + +/* Create the key used to access thread-specific global data values. + Report an error if it fails. */ + if( pthread_key_create( &starlink_ast_globals_key, NULL ) ) { + fprintf( stderr, "ast: Failed to create Thread-Specific Data key" ); + +/* If succesful, create the key used to access the thread-specific status + value. Report an error if it fails. */ + } else if( pthread_key_create( &starlink_ast_status_key, NULL ) ) { + fprintf( stderr, "ast: Failed to create Thread-Specific Status key" ); + + } + +} + +AstGlobals *astGlobalsInit_( void ) { +/* +*+ +* Name: +* astGlobalsInit + +* Purpose: +* Create and initialise a structure holding thread-specific global +* data values. + +* Type: +* Protected function. + +* Synopsis: +* #include "globals.h" +* AstGlobals *astGlobalsInit; + +* Description: +* This function allocates memory to hold thread-specific global data +* for use throughout AST, and initialises it. + +* Returned Value: +* Pointer to the structure holding global data values for the +* currently executing thread. + +*- +*/ + +/* Local Variables: */ + AstGlobals *globals; + AstStatusBlock *status; + +/* Allocate memory to hold the global data values for the currently + executing thread. Use malloc rather than astMalloc (the AST memory + module uses global data managed by this module and so using astMalloc + could put us into an infinite loop). */ + globals = MALLOC( sizeof( AstGlobals ) ); + + if ( !globals ){ + fprintf( stderr, "ast: Failed to allocate memory to hold AST " + "global data values" ); + +/* Initialise the global data values. */ + } else { + +/* Each thread has a unique integer identifier. */ + pthread_mutex_lock( &nthread_mutex ); + globals->thread_identifier = nthread++; + pthread_mutex_unlock( &nthread_mutex ); + +#define INIT(class) astInit##class##Globals_( &(globals->class) ); + INIT( Error ); + INIT( Memory ); + INIT( Object ); + INIT( Axis ); + INIT( Mapping ); + INIT( Frame ); + INIT( Channel ); + INIT( CmpMap ); + INIT( KeyMap ); + INIT( FitsChan ); + INIT( FitsTable ); + INIT( CmpFrame ); + INIT( DSBSpecFrame ); + INIT( FrameSet ); + INIT( LutMap ); + INIT( MathMap ); + INIT( PcdMap ); + INIT( PointSet ); + INIT( SkyAxis ); + INIT( SkyFrame ); + INIT( SlaMap ); + INIT( SpecFrame ); + INIT( SphMap ); + INIT( TimeFrame ); + INIT( WcsMap ); + INIT( ZoomMap ); + INIT( FluxFrame ); + INIT( SpecFluxFrame ); + INIT( GrismMap ); + INIT( IntraMap ); + INIT( Plot ); + INIT( Plot3D ); + INIT( Region ); + INIT( Xml ); + INIT( XmlChan ); + INIT( Box ); + INIT( Circle ); + INIT( CmpRegion ); + INIT( DssMap ); + INIT( Ellipse ); + INIT( Interval ); + INIT( MatrixMap ); + INIT( NormMap ); + INIT( NullRegion ); + INIT( PermMap ); + INIT( PointList ); + INIT( PolyMap ); + INIT( Polygon ); + INIT( Prism ); + INIT( RateMap ); + INIT( SelectorMap ); + INIT( ShiftMap ); + INIT( SpecMap ); + INIT( Stc ); + INIT( StcCatalogEntryLocation ); + INIT( StcObsDataLocation ); + INIT( SwitchMap ); + INIT( Table ); + INIT( TimeMap ); + INIT( TranMap ); + INIT( UnitMap ); + INIT( WinMap ); + INIT( StcResourceProfile ); + INIT( StcSearchLocation ); + INIT( StcsChan ); +#undef INIT + +/* Save the pointer as the value of the starlink_ast_globals_key + thread-specific data key. */ + if( pthread_setspecific( starlink_ast_globals_key, globals ) ) { + fprintf( stderr, "ast: Failed to store Thread-Specific Data pointer." ); + +/* We also take this opportunity to allocate and initialise the + thread-specific status value. */ + } else { + status = MALLOC( sizeof( AstStatusBlock ) ); + if( status ) { + status->internal_status = 0; + status->status_ptr = &( status->internal_status ); + +/* If succesful, store the pointer to this memory as the value of the + status key for the currently executing thread. Report an error if + this fails. */ + if( pthread_setspecific( starlink_ast_status_key, status ) ) { + fprintf( stderr, "ast: Failed to store Thread-Specific Status pointer." ); + } + + } else { + fprintf( stderr, "ast: Failed to allocate memory for Thread-Specific Status pointer." ); + } + } + } + +/* Return a pointer to the data structure holding the global data values. */ + return globals; +} + +#endif + diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..7f631ce --- /dev/null +++ b/globals.h @@ -0,0 +1,243 @@ +#if !defined( GLOBALS_INCLUDED ) /* Include this file only once */ +#define GLOBALS_INCLUDED 1 + +/* If thread-safety is required... */ +#if defined( THREAD_SAFE ) && ( defined( astCLASS ) || defined( astFORTRAN77) ) + +/* Include files: */ +/* ============== */ + +/* AST includes */ +#include "axis.h" +#include "box.h" +#include "channel.h" +#include "circle.h" +#include "cmpframe.h" +#include "cmpmap.h" +#include "cmpregion.h" +#include "dsbspecframe.h" +#include "dssmap.h" +#include "ellipse.h" +#include "error.h" +#include "fitschan.h" +#include "fitstable.h" +#include "fluxframe.h" +#include "frame.h" +#include "frameset.h" +#include "grismmap.h" +#include "interval.h" +#include "intramap.h" +#include "keymap.h" +#include "lutmap.h" +#include "mapping.h" +#include "mathmap.h" +#include "matrixmap.h" +#include "memory.h" +#include "normmap.h" +#include "nullregion.h" +#include "object.h" +#include "pcdmap.h" +#include "permmap.h" +#include "plot.h" +#include "plot3d.h" +#include "pointlist.h" +#include "pointset.h" +#include "polygon.h" +#include "polymap.h" +#include "prism.h" +#include "ratemap.h" +#include "region.h" +#include "selectormap.h" +#include "shiftmap.h" +#include "skyaxis.h" +#include "skyframe.h" +#include "slamap.h" +#include "specfluxframe.h" +#include "specframe.h" +#include "specmap.h" +#include "sphmap.h" +#include "stc.h" +#include "stccatalogentrylocation.h" +#include "stcobsdatalocation.h" +#include "stcresourceprofile.h" +#include "stcsearchlocation.h" +#include "stcschan.h" +#include "switchmap.h" +#include "table.h" +#include "timeframe.h" +#include "timemap.h" +#include "tranmap.h" +#include "unitmap.h" +#include "wcsmap.h" +#include "winmap.h" +#include "xml.h" +#include "xmlchan.h" +#include "zoommap.h" + + + +/* System includes */ +#include + +/* Macros */ +/* ====== */ + +/* The name of the variable used to access thread-specific global data */ +#define AST__GLOBALS ast_globals + +/* Defines a macro that gives access to a specific global data item. */ +#define astGLOBAL(class,name) (AST__GLOBALS->class.name) + + +/* Declares the pointer for the structure holding thread-specific values + for AST global data. */ +#define astDECLARE_GLOBALS AstGlobals *AST__GLOBALS; + + +/* A macro that should be invoked in each function that refers to a + global data item. The "This" parameter should be a pointer to an + Object, or NULL. It ensures the thread-specific data key has been + created. It also allocates and initialises memory to hold the global + data. */ +#define astGET_GLOBALS(This) \ +\ +/* If the supplied Object pointer contains a pointer to the thread-specific \ + data structure, return it. */ \ + if( This && ((AstObject *)This)->globals ) { \ + AST__GLOBALS = ((AstObject *)This)->globals; \ +\ +/* Otherwise, ensure that the thread specific data key has been created. */ \ + } else if( pthread_once( &starlink_ast_globals_initialised, \ + astGlobalsCreateKey_ ) ) { \ + AST__GLOBALS = NULL; \ + fprintf( stderr, "Starlink AST package initialisation failed." ); \ +\ +/* If the current thread does not yet have a structure to hold \ + thread-specific global data, create one now (initialising its \ + contents) and associate it with the thread speciifc data key. */ \ + } else if( ( AST__GLOBALS = \ + pthread_getspecific( starlink_ast_globals_key ) ) == NULL ) { \ + AST__GLOBALS = astGlobalsInit_(); \ + if( pthread_setspecific( starlink_ast_globals_key, AST__GLOBALS ) ) { \ + fprintf( stderr, "Starlink AST failed to store Thread-Specific " \ + "Data pointer." ); \ + } \ + } + + +/* A macro that expands to the value of a unique integer identifier for + the calling thread. */ +#define AST__THREAD_ID (AST__GLOBALS->thread_identifier) \ + + +#define astMAKE_INITGLOBALS(class) \ +\ +void astInit##class##Globals_( Ast##class##Globals *globals ){ \ + GLOBAL_inits \ +} + +/* Type definitions */ +/* ================ */ + +typedef struct AstGlobals { + int thread_identifier; + AstMemoryGlobals Memory; + AstErrorGlobals Error; + AstObjectGlobals Object; + AstAxisGlobals Axis; + AstMappingGlobals Mapping; + AstFrameGlobals Frame; + AstChannelGlobals Channel; + AstCmpMapGlobals CmpMap; + AstKeyMapGlobals KeyMap; + AstFitsChanGlobals FitsChan; + AstFitsTableGlobals FitsTable; + AstCmpFrameGlobals CmpFrame; + AstDSBSpecFrameGlobals DSBSpecFrame; + AstFrameSetGlobals FrameSet; + AstLutMapGlobals LutMap; + AstMathMapGlobals MathMap; + AstPcdMapGlobals PcdMap; + AstPointSetGlobals PointSet; + AstSkyAxisGlobals SkyAxis; + AstSkyFrameGlobals SkyFrame; + AstSlaMapGlobals SlaMap; + AstSpecFrameGlobals SpecFrame; + AstSphMapGlobals SphMap; + AstTimeFrameGlobals TimeFrame; + AstWcsMapGlobals WcsMap; + AstZoomMapGlobals ZoomMap; + AstFluxFrameGlobals FluxFrame; + AstSpecFluxFrameGlobals SpecFluxFrame; + AstGrismMapGlobals GrismMap; + AstIntraMapGlobals IntraMap; + AstPlotGlobals Plot; + AstPlot3DGlobals Plot3D; + AstRegionGlobals Region; + AstBoxGlobals Box; + AstXmlGlobals Xml; + AstXmlChanGlobals XmlChan; + AstCircleGlobals Circle; + AstCmpRegionGlobals CmpRegion; + AstDssMapGlobals DssMap; + AstEllipseGlobals Ellipse; + AstIntervalGlobals Interval; + AstMatrixMapGlobals MatrixMap; + AstNormMapGlobals NormMap; + AstNullRegionGlobals NullRegion; + AstPermMapGlobals PermMap; + AstPointListGlobals PointList; + AstPolyMapGlobals PolyMap; + AstPolygonGlobals Polygon; + AstPrismGlobals Prism; + AstRateMapGlobals RateMap; + AstSelectorMapGlobals SelectorMap; + AstShiftMapGlobals ShiftMap; + AstSpecMapGlobals SpecMap; + AstStcGlobals Stc; + AstStcCatalogEntryLocationGlobals StcCatalogEntryLocation; + AstStcObsDataLocationGlobals StcObsDataLocation; + AstSwitchMapGlobals SwitchMap; + AstTableGlobals Table; + AstTimeMapGlobals TimeMap; + AstTranMapGlobals TranMap; + AstUnitMapGlobals UnitMap; + AstWinMapGlobals WinMap; + AstStcResourceProfileGlobals StcResourceProfile; + AstStcSearchLocationGlobals StcSearchLocation; + AstStcsChanGlobals StcsChan; +} AstGlobals; + + +/* Externally declared variables */ +/* ============================= */ + + +/* The pthreads key that is associated with the thread-specific data for + each thread. Declared in global.c. */ +extern pthread_key_t starlink_ast_globals_key; + +/* The pthreads key that is associated with the thread-specific status + value for each thread. Declared in global.c. */ +extern pthread_key_t starlink_ast_status_key; + +/* This is a flag indicating that the thread-specific data key has not yet + been created. Declared in globals.c. */ +extern pthread_once_t starlink_ast_globals_initialised; + +/* Function Prototypes: */ +/* ==================== */ + +void astGlobalsCreateKey_( void ); +AstGlobals *astGlobalsInit_( void ); + + +/* If thread-safety is not required, define some null macros. */ +#else + +#define astDECLARE_GLOBALS +#define astGET_GLOBALS(This) +#define astINIT_GLOBALS + +#endif +#endif diff --git a/grf.h b/grf.h new file mode 100644 index 0000000..04294f3 --- /dev/null +++ b/grf.h @@ -0,0 +1,110 @@ +#if !defined( GRF_INCLUDED ) /* Include this file only once */ +#define GRF_INCLUDED +/* +*+ +* Name: +* grf.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the grf module + +* Invocation: +* #include "grf.h" + +* Description: +* This include file defines the interface to the grf module and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this module. + +* Inheritance: +* The grf module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 27-JUN-1996 (DSB): +* Original version. +* 25-OCT-1996 (DSB): +* Primatives macros defined, extra parameter added to astGAttr. +* 17-FEB-2006 (DSB): +* Added GRF__ESH and GRF__ESG. +*- +*/ + +/* Constant Values. */ +/* ================ */ +/* Values identifying different graphics attributes. */ +#define GRF__STYLE 0 +#define GRF__WIDTH 1 +#define GRF__SIZE 2 +#define GRF__FONT 3 +#define GRF__COLOUR 4 + +/* Values identifying different graphics primatives. */ +#define GRF__TEXT 0 +#define GRF__LINE 1 +#define GRF__MARK 2 + +/* The number of different graphics attributes */ +#define GRF__NATTR 5 + +/* Values identifying capabilities */ +#define GRF__ESC 0 +#define GRF__MJUST 1 +#define GRF__SCALES 2 + +/* Values identifying types of graphics escape sequence */ +#define GRF__ESPER 1 +#define GRF__ESSUP 2 +#define GRF__ESSUB 3 +#define GRF__ESGAP 4 +#define GRF__ESBAC 5 +#define GRF__ESSIZ 6 +#define GRF__ESWID 7 +#define GRF__ESFON 8 +#define GRF__ESCOL 9 +#define GRF__ESSTY 10 +#define GRF__ESPOP 11 +#define GRF__ESPSH 12 +#define GRF__ESH 13 +#define GRF__ESG 14 + +/* Function prototypes. */ +/* ==================== */ +int astGAttr( int, double, double *, int ); +int astGScales( float *, float * ); +int astGBBuf( void ); +int astGEBuf( void ); +int astGFlush( void ); +int astGLine( int, const float *, const float * ); +int astGMark( int, const float *, const float *, int ); +int astGQch( float *, float * ); +int astGText( const char *, float, float, const char *, float, float ); +int astGTxExt( const char *, float, float, const char *, float, float, float *, float * ); +int astGCap( int, int ); + +#endif diff --git a/grf3d.c b/grf3d.c new file mode 100644 index 0000000..c0c1a33 --- /dev/null +++ b/grf3d.c @@ -0,0 +1,102 @@ +/* +* Name: +* grf3d.c + +* Purpose: +* Implement the grf3D interface if no graphics system is available. + +* Description: +* This file implements the low level 3D graphics functions required +* by the rest of AST. These implementations simply report an error +* when called. + +* Inheritance: +* This module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (JACH - UCLan) + +* History: +* 20-JUN-2007 (DSB): +* Original version. +*/ + +/* Header files */ +/* ============ */ +#include "grf3d.h" /* Declare the functions in this module */ +#include "error.h" /* AST error reporting facilities */ +#include "ast_err.h" /* AST error codes */ + +/* Function Prototypes */ +/* =================== */ +static void Report( const char * ); + +/* Function definitions */ +/* ==================== */ +int astG3DCap( int cap, int value ){ + return 0; +} + +int astG3DFlush( void ){ + Report( "astG3DFlush"); + return 0; +} + +int astG3DLine( int n, float *x, float *y, float *z ){ + Report( "astG3DLine" ); + return 0; +} + +int astG3DQch( float *ch ){ + Report( "astG3DQch" ); + return 0; +} + +int astG3DMark( int n, float *x, float *y, float *z, int type, float norm[3] ){ + Report( "astG3DMark" ); + return 0; +} + +int astG3DText( const char *text, float ref[3], const char *just, + float up[3], float norm[3] ){ + Report( "astG3DText" ); + return 0; +} + +int astG3DTxExt( const char *text, float ref[3], const char *just, + float up[3], float norm[3], float *xb, float *yb, + float *zb, float bl[3] ){ + Report( "astG3DTxExt" ); + return 0; +} + +int astG3DAttr( int attr, double value, double *old_value, int prim ){ + Report( "astG3DAttr" ); + return 0; +} + +static void Report( const char *name ){ + astError( AST__GRFER, "%s: No graphics facilities are available.", name ); + astError( AST__GRFER, "Re-link using an option such as '-pgplot' with " + "the ast_link script." ); +} diff --git a/grf3d.h b/grf3d.h new file mode 100644 index 0000000..6ab7195 --- /dev/null +++ b/grf3d.h @@ -0,0 +1,69 @@ +#if !defined( GRF3D_INCLUDED ) /* Include this file only once */ +#define GRF3D_INCLUDED +/* +*+ +* Name: +* grf3d.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the grf3d module + +* Invocation: +* #include "grf3d.h" + +* Description: +* This include file defines the interface to the grf3d module and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this module. + +* Inheritance: +* The grf3d module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (JACH - UCLan) + +* History: +* 20-JUN-2007 (DSB): +* Original version. +*- +*/ + +/* Include the 2D grf header file in order to inherit the GRF__ macros. */ +#include "grf.h" + +/* Function prototypes. */ +/* ==================== */ +int astG3DAttr( int, double, double *, int ); +int astG3DCap( int, int ); +int astG3DFlush( void ); +int astG3DLine( int, float *, float *, float * ); +int astG3DMark( int, float *, float *, float *, int, float[3] ); +int astG3DQch( float * ); +int astG3DText( const char *, float[3], const char *, float[3], float[3] ); +int astG3DTxExt( const char *, float[3], const char *, float[3], float[3], float *, float *, float *, float[3] ); + + +#endif diff --git a/grf3d_pgplot.c b/grf3d_pgplot.c new file mode 100644 index 0000000..ab25085 --- /dev/null +++ b/grf3d_pgplot.c @@ -0,0 +1,3196 @@ +/* +* Name: +* grf3d_pgplot.c + +* Purpose: +* Implement the grf3d interface using the PGPLOT graphics system. + +* Description: +* This file implements the low level 3D graphics functions required +* by the rest of AST, by calling suitable PGPLOT functions (the +* FORTRAN PGPLOT interface is used). +* +* This file can be used as a template for the development of +* similar implementations based on other graphics systems. +* +* Unlike world coordinates used by the 2D grf interface, the 3D world +* coordinates used by the grf3D interface are assume to be equally scaled +* (that is, they are assumed to have the same units). Therefore this +* module has no equivalent to the astGScales function defined by the +* 2D grf interface. + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 20-JUN-2007 (DSB): +* Original version. +*/ + + +/* Macros */ +/* ====== */ +#define MXDEV 16 /* Max no of concurrent PGPLOT devices */ +#define MXSTRLEN 80 /* Max PGPLOT string length */ +#define CAMERA_OK 123456789 /* Flags that a Camera has been initialised */ +#define TWOPI 6.28318530718 /* 2*PI */ +#define MXSIDE 32 /* Max no of sides in a marker polygon */ + + +/* Header files. */ +/* ============= */ +/* AST header files */ +#include "grf3d.h" /* The grf3D interface definition */ +#include "pg3d.h" /* Other public functions in this module */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* C to FORTRAN interface functions */ +#include "memory.h" /* Memory allocation facilities */ +#include "error.h" /* Error reporting facilities */ +#include "pointset.h" /* Defines AST__BAD */ +#include "ast_err.h" /* AST error codes */ + +/* System header files */ +#include +#include +#include +#include +#include + + +/* Type definitions. */ +/* ================= */ +/* Structure defining the position and orientation of the camera in 3D + world coords. This is specific to the PGPLOT implementation. Other + implementations need not include any equivalent to this structure. */ +typedef struct camera { + float eye_vector[3]; + float target_vector[3]; + float up_vector[3]; + float w2c_matrix[9]; + float screen_distance; + int ok_flag; +} Camera; + + +/* Module variables. */ +/* ================= */ +/* One camera structure for each PGPLOT device identifier. PGPLOT allows + a maximum of 8 concurrent devices at the moment. Make the array twice + this size to allow for some future expansion in PGPLOT. Again, this is + specific to the PGPLOT implementation of the grf3D interface. */ +static Camera cameras[ MXDEV ]; + +/* Function templates. */ +/* =================== */ +/* Templates for functions that are private to this module. */ +static Camera *getCamera( int ); +static float getCharHeight( void ); +static int Polygon( int, float *, float *, float *, float[3], float[3], float[3], float[3] ); +static int Text( int *, int, float[3], const char *, float[3], float[3], float[3] ); +static int TextCam( Camera *, float[3], float[3], float[3], float[3] ); +static int TxExt( int *, int, float[3], const char *, float[3], float[3], float[3], float *, float *, float *, float[3] ); +static int getTextAxes( float[3], float[3], float[3], const char *, float[3], float[3], float[3], char[3] ); +static int transform( Camera *, int, float *, float *, float *, float *, float * ); +static int vectorNorm( float * ); +static float vectorModulus( float * ); +static void getSymbolList( const char *, int, int *, int * ); +static void vectorProduct( float *, float *, float * ); +static float dotProduct( float *, float * ); +static void vectorSub( float *, float *, float * ); + +/* Templates for private functions that wrap PGPLOT Fortran routines. */ +static void ccgrsyds( int *, int *, const char *, int, int ); +static void ccgrsymk( int, int, int * ); +static void ccgrsyxd( int, int *, int * ); +static void ccpgline( int, float[], float[] ); +static void ccpgpoly( int, float[], float[] ); +static void ccpgqcf( int * ); +static void ccpgqcf(int *); +static void ccpgqch( float * ); +static void ccpgqci( int * ); +static void ccpgqclp( int * ); +static void ccpgqid( int * ); +static void ccpgqls( int * ); +static void ccpgqlw( int * ); +static void ccpgqvsz( int, float *, float *, float *, float * ); +static void ccpgqwin( float *, float *, float *, float * ); +static void ccpgscf( int ); +static void ccpgsch( float ); +static void ccpgsci( int ); +static void ccpgsclp( int ); +static void ccpgsls( int ); +static void ccpgslw( int ); +static void ccpgswin( float, float, float, float ); +static void ccpgupdt( void ); + + +/* Templates for Fortran PGPLOT routines needed by this module. */ +F77_SUBROUTINE(grsyds)( INTEGER_ARRAY(list), INTEGER(nlist), CHARACTER(text), INTEGER(font) TRAIL(text) ); +F77_SUBROUTINE(grsymk)( INTEGER(type), INTEGER(font), INTEGER(symbol) ); +F77_SUBROUTINE(grsyxd)( INTEGER(symbol), INTEGER_ARRAY(xygrid), INTEGER(unused) ); +F77_SUBROUTINE(pgline)( INTEGER(N), REAL_ARRAY(X), REAL_ARRAY(Y) ); +F77_SUBROUTINE(pgpoly)( INTEGER(N), REAL_ARRAY(X), REAL_ARRAY(Y) ); +F77_SUBROUTINE(pgqcf)( INTEGER(ival) ); +F77_SUBROUTINE(pgqch)( REAL(rval) ); +F77_SUBROUTINE(pgqci)( INTEGER(ival) ); +F77_SUBROUTINE(pgqclp)( INTEGER(clip) ); +F77_SUBROUTINE(pgqid)( INTEGER(id) ); +F77_SUBROUTINE(pgqls)( INTEGER(ival) ); +F77_SUBROUTINE(pgqlw)( INTEGER(ival) ); +F77_SUBROUTINE(pgqvsz)( INTEGER(units), REAL(x1), REAL(x2), REAL(y1), REAL(y2) ); +F77_SUBROUTINE(pgqwin)( REAL(wx1), REAL(wx2), REAL(wy1), REAL(wy2) ); +F77_SUBROUTINE(pgscf)( INTEGER(ival) ); +F77_SUBROUTINE(pgsch)( REAL(rval) ); +F77_SUBROUTINE(pgsci)( INTEGER(ival) ); +F77_SUBROUTINE(pgsclp)( INTEGER(clip) ); +F77_SUBROUTINE(pgsls)( INTEGER(ival) ); +F77_SUBROUTINE(pgslw)( INTEGER(ival) ); +F77_SUBROUTINE(pgswin)( REAL(X1), REAL(X2), REAL(Y1), REAL(Y2) ); +F77_SUBROUTINE(pgupdt)( void ); + + +/* Public functions defined by the grf3D interface. */ +/* ================================================ */ +/* All implementations of the grf3d interface must provide implementations + of all the functions in this block. The corresponding templates are in + grf3d.h */ + + +int astG3DAttr( int attr, double value, double *old_value, int prim ){ +/* +*+ +* Name: +* astG3DAttr + +* Purpose: +* Enquire or set a 3D graphics attribute value. + +* Synopsis: +* #include "grf3d.h" +* int int astG3DAttr( int attr, double value, double *old_value, int prim ) + +* Description: +* This function returns the current value of a specified 3D graphics +* attribute, and optionally establishes a new value. The supplied +* value is converted to an integer value if necessary before use. + +* Parameters: +* attr +* An integer value identifying the required attribute. The +* following symbolic values are defined in grf3d.h: +* +* GRF__STYLE - Line style. +* GRF__WIDTH - Line width. +* GRF__SIZE - Character and marker size scale factor. +* GRF__FONT - Character font. +* GRF__COLOUR - Colour index. +* value +* A new value to store for the attribute. If this is AST__BAD +* no value is stored. +* old_value +* A pointer to a double in which to return the attribute value. +* If this is NULL, no value is returned. +* prim +* The sort of graphics primitive to be drawn with the new attribute. +* Identified by the following values defined in grf.h: +* GRF__LINE +* GRF__MARK +* GRF__TEXT + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: + +*- +*/ + + int ival; + float rval, dx, dy, deflw, x1, x2, y1, y2; + +/* If required retrieve the current line style, and set a new line style. */ + if( attr == GRF__STYLE ){ + ccpgqls( &ival ); + if( old_value ) *old_value = (double) ival; + + if( value != AST__BAD ){ + ival = (int) ( value + 0.5 ); + if( value < 0.0 ) ival -= 1; + + ival = ( ival - 1 ) % 5; + ival += ( ival < 0 ) ? 6 : 1; + + ccpgsls( ival ); + } + +/* If required retrieve the current line width, and set a new line width. + Line width is stored in Plot as a scale factor (1.0 for the default line + width which is a fixed fraction of the diagonal of the view surface), but + pgplot stores it in units of 0.005 of an inch. */ + } else if( attr == GRF__WIDTH ){ + +/* Get the bounds of the view surface in inches. */ + ccpgqvsz( 1, &x1, &x2, &y1, &y2 ); + +/* Find the default line width in inches (i.e. 0.0005 of the length + of the view surface diagonal). */ + dx = ( x1 - x2 ); + dy = ( y1 - y2 ); + deflw = 0.0005*sqrt( (double )( dx*dx + dy*dy ) ); + +/* Get the current pgplot line width in units of 0.005 of an inch. */ + ccpgqlw( &ival ); + +/* If required, return the factor by which this exceeds the default line + width found above. */ + if( old_value ) *old_value = (double)( ival )/( 200.0 * deflw ); + +/* If a new line width has been provided, the pgplot line width needs to + be set to the corresponding absolute value. */ + if( value != AST__BAD ){ + ival = (int) ( 200.0*value*deflw ); + if( ival < 1 ) { + ival = 1; + } else if( ival > 201 ){ + ival = 201; + } + ccpgslw( ival ); + } + +/* If required retrieve the current character size, and set a new size. + The attribute value should be a factor by which to multiply the + default character size. */ + } else if( attr == GRF__SIZE ){ + ccpgqch( &rval ); + if( old_value ) *old_value = (double) rval; + + if( value != AST__BAD ){ + ccpgsch( (float) value ); + } + +/* If required retrieve the current character font, and set a new font. */ + } else if( attr == GRF__FONT ){ + ccpgqcf( &ival ); + if( old_value ) *old_value = (double) ival; + + if( value != AST__BAD ){ + ival = (int) ( value + 0.5 ); + if( value < 0.0 ) ival -= 1; + + ival = ( ival - 1 ) % 4; + ival += ( ival < 0 ) ? 5 : 1; + ccpgscf( ival ); + } + +/* If required retrieve the current colour index, and set a new colour + index. */ + } else if( attr == GRF__COLOUR ){ + ccpgqci( &ival ); + if( old_value ) *old_value = (double) ival; + + if( value != AST__BAD ){ + ival = (int) ( value + 0.5 ); + if( ival < 0 ) ival = 1; + ccpgsci( ival ); + } + +/* Give an error message for any other attribute value. */ + } else { + astError( AST__GRFER, "astG3DAttr: Unknown graphics attribute '%d' " + "requested.", attr ); + return 0; + } + +/* Return. */ + return 1; +} + +int astG3DCap( int cap, int value ){ +/* +*+ +* Name: +* astG3DCap + +* Purpose: +* Indicate if this grf3d module has a given capability. + +* Synopsis: +* #include "grf3d.h" +* int astG3DCap( int cap, int value ) + +* Description: +* This function is called by the AST Plot class to determine if the +* grf3d module has a given capability, as indicated by the "cap" +* argument. + +* Parameters: +* cap +* The capability being inquired about. This will be one of the +* following constants defined in grf3d.h: +* +* GRF3D__ESC: This function should return a non-zero value if the +* astG3DText and astG3DTxExt functions can recognise and interpret +* graphics escape sequences within the supplied string. These +* escape sequences are described below. Zero should be returned +* if escape sequences cannot be interpreted (in which case the +* Plot class will interpret them itself if needed). The supplied +* "value" argument should be ignored only if escape sequences cannot +* be interpreted by astG3DText and astG3DTxExt. Otherwise, "value" +* indicates whether astG3DText and astG3DTxExt should interpret escape +* sequences in subsequent calls. If "value" is non-zero then +* escape sequences should be interpreted by astG3DText and +* astG3DTxExt. Otherwise, they should be drawn as literal text. + +* Returned Value: +* The return value, as described above. Zero should be returned if +* the supplied capability is not recognised. + +* Escape Sequences: +* Escape sequences are introduced into the text string by a percent +* "%" character. The following escape sequences are currently recognised +* ("..." represents a string of one or more decimal digits): +* +* %% - Print a literal "%" character (type GRF__ESPER ). +* +* %^...+ - Draw subsequent characters as super-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the super-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF__ESSUP ). +* %^+ - Draw subsequent characters with the normal base-line. +* +* %v...+ - Draw subsequent characters as sub-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the sub-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF__ESSUB ). +* +* %v+ - Draw subsequent characters with the normal base-line +* (equivalent to %^+). +* +* %>...+ - Leave a gap before drawing subsequent characters. +* The digits "..." give the size of the gap, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF__ESGAP ). +* +* %<...+ - Move backwards before drawing subsequent characters. +* The digits "..." give the size of the movement, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF_ESBAC). +* +* %s...+ - Change the Size attribute for subsequent characters. The +* digits "..." give the new Size as a fraction of the +* "normal" Size, scaled so that a value of "100" corresponds +* to 1.0 (type GRF__ESSIZ ). +* +* %s+ - Reset the Size attribute to its "normal" value. +* +* %w...+ - Change the Width attribute for subsequent characters. The +* digits "..." give the new width as a fraction of the +* "normal" Width, scaled so that a value of "100" corresponds +* to 1.0 (type GRF__ESWID ). +* +* %w+ - Reset the Size attribute to its "normal" value. +* +* %f...+ - Change the Font attribute for subsequent characters. The +* digits "..." give the new Font value (type GRF__ESFON ). +* +* %f+ - Reset the Font attribute to its "normal" value. +* +* %c...+ - Change the Colour attribute for subsequent characters. The +* digits "..." give the new Colour value (type GRF__ESCOL ). +* +* %c+ - Reset the Colour attribute to its "normal" value. +* +* %t...+ - Change the Style attribute for subsequent characters. The +* digits "..." give the new Style value (type GRF__ESSTY ). +* +* %t+ - Reset the Style attribute to its "normal" value. +* +* %- - Push the current graphics attribute values onto the top of +* the stack - see "%+" (type GRF__ESPSH). +* +* %+ - Pop attributes values of the top the stack - see "%-". If +* the stack is empty, "normal" attribute values are restored +* (type GRF__ESPOP). +* +* The astFindEscape function (in libast.a) can be used to locate escape +* sequences within a text string. It has the following signature: +* +* #include "plot.h" +* int astFindEscape( const char *text, int *type, int *value, int *nc ) +* +* Parameters: +* text +* Pointer to the string to be checked. +* type +* Pointer to a location at which to return the type of escape +* sequence. Each type is identified by a symbolic constant defined +* in grf.h and is indicated in the above section. The returned value +* is undefined if the supplied text does not begin with an escape +* sequence. +* value +* Pointer to a lcation at which to return the integer value +* associated with the escape sequence. All usable values will be +* positive. Zero is returned if the escape sequence has no associated +* integer. A value of -1 indicates that the attribute identified by +* "type" should be reset to its "normal" value (as established using +* the astG3DAttr function, etc). The returned value is undefined if +* the supplied text does not begin with an escape sequence. +* nc +* Pointer to a location at which to return the number of +* characters read by this call. If the text starts with an escape +* sequence, the returned value will be the number of characters in +* the escape sequence. Otherwise, the returned value will be the +* number of characters prior to the first escape sequence, or the +* length of the supplied text if no escape sequence is found. + +* Returned Value: +* A non-zero value is returned if the supplied text starts with a +* graphics escape sequence, and zero is returned otherwise. + +*- +*/ + + return 0; +} + +int astG3DFlush( void ){ +/* +*+ +* Name: +* astG3DFlush + +* Purpose: +* Flush all pending graphics to the output device. + +* Synopsis: +* #include "grf3d.h" +* int astG3DFlush( void ) + +* Description: +* This function ensures that the display device is up-to-date, +* by flushing any pending graphics to the output device. + +* Parameters: +* None. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*- +*/ + + ccpgupdt(); + return 1; +} + +int astG3DLine( int n, float *x, float *y, float *z ){ +/* +*+ +* Name: +* astG3DLine + +* Purpose: +* Draw a polyline (i.e. a set of connected lines). + +* Synopsis: +* #include "grf3d.h" +* int astG3DLine( int n, float *x, float *y, float *z ) + +* Description: +* This function displays lines joining the given positions. + +* Parameters: +* n +* The number of positions to be joined together. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* z +* A pointer to an array holding the "n" z values. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - A camera must have been established prior to calling this +* function using either astG3DSetCamera or astG3DAutoCamera. +* - Nothing is done if "n" is less than 2, or if a NULL pointer is +* given for either "x", "y" or "z". + +*- +*/ + +/* Local Variables: */ + int clip; + int result = 0; + float *h, *r; + +/* Do nothing if we have less than 2 points, but do not indicate an error. */ + if( n < 2 ){ + result = 1; + +/* Check the pointers. */ + } else if( x && y && z ) { + +/* Save the current clipping flag, and ensure clipping is off. */ + ccpgqclp( &clip ); + ccpgsclp( 0 ); + +/* Allocate work space for the 2D world coordinate positions. */ + h = astMalloc( sizeof( float )*(size_t) n ); + r = astMalloc( sizeof( float )*(size_t) n ); + if( astOK ) { + +/* Convert the supplied points from 3D world coordinates to 2D world + (i.e. screen) coordinates. If succesful, plot the lines. */ + if( transform( NULL, n, x, y, z, h, r ) ) { + ccpgline( n, (float *) h, (float *) r ); + result = 1; + } + } + +/* Free work space. */ + h = astFree( h ); + r = astFree( r ); + +/* Re-instate original clipping flag. */ + ccpgsclp( clip ); + + } + return result; +} + +int astG3DMark( int n, float *x, float *y, float *z, int type, + float norm[3] ){ +/* +*+ +* Name: +* astG3DMark + +* Purpose: +* Draw a set of markers. + +* Synopsis: +* #include "grf.h" +* int astG3DMark( int n, float *x, float *y, float *z, int type, +* float norm[3] ) + +* Description: +* This function draws markers centred at the given positions, on a +* plane with a specified normal vector. + +* Parameters: +* n +* The number of markers to draw. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* z +* A pointer to an array holding the "n" z values. +* type +* An integer which can be used to indicate the type of marker symbol +* required. See the description of routine PGPT in the PGPLOT manual. +* norm +* The (x,y,z) components of a vector that is normal to the plane +* containing the marker. The given vector passes through the marker +* from the back to the front. If all components of this vector are +* zero, then a normal vector pointing from the position of the +* first marker towards the camera eye is used. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Nothing is done if "n" is less than 1, or if a NULL pointer is +* given for "x", "y" or "z". + +*- +*/ + +/* local Variables: */ + char just[3]; + float ref[3]; + float up[3]; + float tx[3], ty[3], tz[3]; + float vx[ MXSIDE ], vy[ MXSIDE ], vz[ MXSIDE ]; + float ch, ang, dang; + int clip; + int font; + int i; + int nlist; + int symnum; + int ns; + +/* Return if any of the coordinate pointers is NULL. */ + if( !x || !y || !z ) return 1; + +/* Initialise */ + ns = 0; + +/* Unless the "norm" vector is parallel to the z axis, we use an up vector + that is parallel to the z axis. Otherwise we use an up vector paralle + to the x axis. */ + if( norm[ 0 ] != 0.0 || norm[ 1 ] != 0.0 ) { + up[ 0 ] = 0.0; + up[ 1 ] = 0.0; + up[ 2 ] = 1.0; + } else { + up[ 0 ] = 1.0; + up[ 1 ] = 0.0; + up[ 2 ] = 0.0; + } + +/* Create unit vectors along the three axes of the text plane + coordinate system. */ + ref[ 0 ] = x[ 0 ]; + ref[ 1 ] = y[ 0 ]; + ref[ 2 ] = z[ 0 ]; + if( !getTextAxes( ref, up, norm, "CC", tx, ty, tz, just ) ) return 0; + +/* Calculate the pgplot symbol number for the given marker type. */ + if( type > 0 ) { + if( type > 127 ) { + symnum = type; + } else { + ccpgqcf( &font ); + ccgrsymk( type, font, &symnum ); + } + + } else if( type > -3 ) { + getSymbolList( ".", 1, &symnum, &nlist ); + +/* Regular polygons - create an array of text plane coordinates for the + vertices of the polygon. */ + } else { + symnum = type; + +/* Get the character height in world coordinate units. A PGPLOT + character height of 1.0 corresponds to 1/40 of the 2D window height. */ + ch = getCharHeight(); + +/* Limit the number of sides that can be produced. */ + ns = -type; + if( ns > MXSIDE ) ns = MXSIDE; + +/* Calculate the angle subtended by each edge of the polygon. */ + dang = TWOPI/ns; + ang = 0.0; + +/* Loop round each vertex. */ + for( i = 0; i < ns; i++ ) { + vx[ i ] = ch*sin( ang ); + vy[ i ] = ch*cos( ang ); + vz[ i ] = 0.0; + ang += dang; + } + } + +/* Save the current clipping flag, and ensure clipping is off. */ + ccpgqclp( &clip ); + ccpgsclp( 0 ); + +/* Draw each marker in turn. */ + for( i = 0; i < n; i++ ) { + +/* Store the centre world coords */ + ref[ 0 ] = x[ i ]; + ref[ 1 ] = y[ i ]; + ref[ 2 ] = z[ i ]; + +/* Draw the symbol, and return if anything goes wrong. */ + if( symnum >= 0 ) { + if( !Text( &symnum, 1, ref, "CC", tx, ty, tz ) ) return 0; + + } else { + if( !Polygon( ns, vx, vy, vz, ref, tx, ty, tz ) ) return 0; + + } + + } + +/* Re-instate original clipping flag. */ + ccpgsclp( clip ); + +/* If we arrive here we have been succesful, so return a non-zero value. */ + return 1; +} + +int astG3DQch( float *ch ){ +/* +*+ +* Name: +* astG3DQch + +* Purpose: +* Return the character height in world coordinates. + +* Synopsis: +* #include "grf3d.h" +* int astG3DQch( float *ch ) + +* Description: +* This function returns the height of characters drawn using astG3DText. + +* Parameters: +* ch +* A pointer to the double which is to receive the height of +* characters drawn with astG3DText. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Since the 3D world coordinate axes are assumed to be equally +* scaled, the height of text in world coordinate units is independent +* of the orientation of the text. Therefore, this function returns +* only one height value, unlike the equivalent 2D astGQch function +* that returns two heights. +*- +*/ + *ch = getCharHeight(); + return 1; +} + +int astG3DText( const char *text, float ref[3], const char *just, float up[3], + float norm[3] ){ +/* +*+ +* Name: +* astG3DText + +* Purpose: +* Draw a character string. + +* Synopsis: +* #include "grf3d.h" +* int astG3DText( const char *text, float ref[3], const char *just, +* float up[3], float norm[3] ) + +* Description: +* This function displays a character string at a given position +* on a given plane in 3D world coords, using a specified +* justification and up-vector. + +* Parameters: +* text +* Pointer to a null-terminated character string to be displayed. +* ref +* The reference (x,y,z) coordinates. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* up +* The (x,y,z) up-vector for the text. The actual up vector used is +* the projection of the supplied vector onto the plane specified by +* "norm". +* norm +* The (x,y,z) components of a vector that is normal to the plane +* containing the text. The given vector passes through the text +* from the back to the front. If all components of this vector are +* zero, then a normal vector pointing towards the camera eye is used. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - This routine does not recognise PGPLOT escape sequences. +* - A NULL value for "just" causes a value of "CC" to be used. +*- +*/ + +/* Local Constants: */ +#define MXLEN 256 + +/* Local Variables: */ + char newjust[3]; + float tx[3], ty[3], tz[3]; + int list[ MXLEN ]; + int nlist; + +/* Convert the supplied string into a list of PGPLOT symbol numbers */ + getSymbolList( text, MXLEN, &nlist, list ); + +/* Create unit vectors along the three axes of the text plane + coordinate system. */ + if( !getTextAxes( ref, up, norm, just, tx, ty, tz, newjust ) ) return 0; + +/* Draw the text. */ + return Text( list, nlist, ref, newjust, tx, ty, tz ); + +/* Clear local constants. */ +#undef MXLEN +} + +int astG3DTxExt( const char *text, float ref[3], const char *just, + float up[3], float norm[3], float *xb, float *yb, + float *zb, float bl[3] ){ +/* +*+ +* Name: +* astG3DTxExt + +* Purpose: +* Get the extent of a character string. + +* Synopsis: +* #include "grf3d.h" +* int astG3DTxExt( const char *text, float ref[3], const char *just, +* float up[3], float norm[3], float *xb, float *yb, +* float *zb, float bl[3] ) + +* Description: +* This function returns the corners of a box which would enclose the +* supplied character string if it were displayed using astG3DText. +* +* The returned box INCLUDES any leading or trailing spaces. + +* Parameters: +* text +* Pointer to a null-terminated character string to be displayed. +* ref +* The reference (x,y,z) coordinates. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", 'B' for "baseline", or "M" for "bottom", and +* specifies the vertical location of the reference position. Note, +* "baseline" corresponds to the base-line of normal text. Some +* characters (eg "y", "g", "p", etc) descend below the base-line, +* and so "M" and "B" will produce different effects for such +* characters. The second character may be 'L' for "left", 'C' for +* "centre", or 'R' for "right", and specifies the horizontal +* location of the reference position. If the string has less than +* 2 characters then 'C' is used for the missing characters. +* up +* The (x,y,z) up-vector for the text. The actual up vector used is +* the projection of the supplied vector onto the plane specified by +* "norm". +* norm +* The (x,y,z) components of a vector that is normal to the plane +* containing the text. The given vector passes through the text +* from the back to the front. If all components of this vector are +* zero, then a normal vector pointing towards the camera eye is used. +* xb +* An array of 4 elements in which to return the x coordinate of +* each corner of the bounding box. +* yb +* An array of 4 elements in which to return the y coordinate of +* each corner of the bounding box. +* zb +* An array of 4 elements in which to return the z coordinate of +* each corner of the bounding box. +* bl +* The 3D world coordinates at the left hand end of the text +* baseline. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - The order of the corners is anti-clockwise starting at the +* bottom left when viewing the text normally (i.e. face on). +* - This routine does not recognise PGPLOT escape sequences. +* - A NULL value for "just" causes a value of "CC" to be used. +*- +*/ + +/* Local Constants: */ +#define MXLEN 256 + +/* Local Variables: */ + char newjust[3]; + int i; + int list[ MXLEN ]; + int nlist; + float tx[3], ty[3], tz[3]; + +/* Initialise the returned values to indicate no box available. */ + for( i = 0; i < 4; i++ ){ + xb[ i ] = 0.0; + yb[ i ] = 0.0; + zb[ i ] = 0.0; + } + +/* Convert the supplied string into a list of symbol numbers */ + getSymbolList( text, MXLEN, &nlist, list ); + +/* Create unit vectors along the three axes of the text plane + coordinate system. */ + if( !getTextAxes( ref, up, norm, just, tx, ty, tz, newjust ) ) return 0; + +/* Find the bounding box of this list of symbols. */ + return TxExt( list, nlist, ref, newjust, tx, ty, tz, xb, yb, zb, bl ); + +/* Clear local constants. */ +#undef MXLEN +} + + +/* Public functions specific to this PGPLOT implementation. */ +/* ======================================================== */ +/* Other implementations of the grf3d interface can ignore the following + functions. They provide control of the 3D view. */ + +int PG3DSetCamera( float eye[3], float target[3], float up[3], float screen ){ +/* +*+ +* Name: +* PG3DSetCamera + +* Purpose: +* Store new camera settings for the current PGPLOT device. + +* Synopsis: +* #include "grf3d.h" +* int PG3DSetCamera( float eye[3], float target[3], float up[3], +* float screen ) + +* Description: +* This function stores new camera settings for the current PGPLOT +* device. +* +* A "camera" describes the projection of the 3D world coordinate +* space onto a 2D "screen". This screen corresponds to the 2D viewing +* surface used by PGPLOT. The 2D window used by PGPLOT (as set by +* PGSWIN, etc) defines the bounds of the screen area that is visible +* in the PGPLOT viewport. +* +* The 3D world coordinate axes (x,y,z) are such that if "z" is +* vertically upwards and "x" points to the right, then "y" goes +* out of the paper away from you. All 3 axes are assume to have equal +* scale. +* +* A camera defines a second set of 3D axes (called "(u,v,w)") with +* origin at the 3D world coordinates given by "eye": +* +* - the "w" axis points towards the position given by "target" +* - the "v" axis is perpendicular to the "w" axis and is in the plane +* spanned by the "w" axis and the supplied "up" vector +* - the "u" axis is perpendicular to both "w" and "v" and points to +* the left when looking from the eye along the w axis with the v +* axis upwards +* +* Thus the "v" axis is parallel to "vertically up" on the 2D screen, +* "u" is parallel to "horizontally to the left", and "w" is +* perpendicular to the screen, pointing towards the target. +* +* The screen is a plane perpendicular to the "w" axis, at the "w" axis +* value given by "screen". A 2D cartesian coordinate system (h,r) is +* defined on the screen, with origin at the point where the "w" axis +* intersects the screen. The "h" (horizontal) axis is parallel to the +* "u" axis but points in the opposite direction (to the left), and the +* "r" (vertical) axis is parallel to the "v" axis. The (h,r) system is +* taken to be the same as the PGPLOT 2D world coordinate system, and +* PGSWIN can therefore be used to specify the rectangular area on the +* screen that is mapped onto the PGPLOT viewport. +* +* It is assumed that all axes (x,y,z), (u,v,w) and (h,r) are measured +* in the same units. + +* Parameters: +* eye +* The position vector of the camera's "eye", in 3D world coordinates. +* target +* The position vector of a point in 3D world coordinates that is +* at the centre of the camera's view. In other words, the camera is +* looking towards this point. Zero will be returned if the target +* is the same position as the eye. +* up +* A vector in 3D world coordinates that will appear vertically +* upwards when projected onto the screen. Zero will be returned if +* the up vector has zero length or is parallel to the line joining +* the eye and the target. +* screen +* The distance from the camera's eye to the projection screen. If +* this is zero, then an orthographic projection is used. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +*- +*/ + +/* Local Variables: */ + Camera *cam; + float *u, *v, *w; + int result = 0; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 0 ); + if( cam ) { + result = 1; + +/* Store the supplied values in the camera. */ + memcpy( cam->target_vector, target, 3*sizeof( float ) ); + memcpy( cam->eye_vector, eye, 3*sizeof( float ) ); + cam->screen_distance = screen; + +/* Get pointers to the three rows of the w2c_matrix. This is a 3x3 matrix that + rotates vectors in the (x,y,z) system into vectors in the (u,v,w) + system. Each row in the matrix is a unit vector along the u, v or w + axes. */ + u = cam->w2c_matrix; + v = u + 3; + w = v + 3; + +/* The "w" axis points form the eye to the target, so get the vector from + the eye to the target and normalise it. */ + vectorSub( target, eye, w ); + if( ! vectorNorm( w ) ) result = 0; + +/* The "v" vector is in the plane spanned by the "w" axis and the "up" + vector. Get the normal to this plane, storing the result temporarily + in the "u" vector. . */ + vectorProduct( w, up, u ); + +/* The "v" vector is normal to the vector found above and is also normal + to the "w" axis. Get this vector and normalise it. */ + vectorProduct( u, w, v ); + if( ! vectorNorm( v ) ) result = 0; + +/* The "u" vector is perpendicualr to both the "w" and "v" vectors. */ + vectorProduct( v, w, u ); + if( ! vectorNorm( u ) ) result = 0; + +/* Use "v" as the stored up vector (the supplied "up" vector is not + necesarily the same as "v"). */ + memcpy( cam->up_vector, v, 3*sizeof( float ) ); + +/* Se a flag that indicates that the Camera is usable. */ + cam->ok_flag = result ? CAMERA_OK : CAMERA_OK/2; + } + + return result; +} + +int PG3DSetEye( float eye[3] ){ +/* +*+ +* Name: +* PG3DSetEye + +* Purpose: +* Store a new camera eye position for the current PGPLOT device. + +* Synopsis: +* #include "grf3d.h" +* int PG3DSetEye( float eye[3] ) + +* Description: +* This function modifies the camera eye position for the current +* PGPLOT device. Other camera settings are left unchanged. See +* PG3DSetCamera for more details. + +* Parameters: +* eye +* The position vector of the camera's "eye", in 3D world coordinates. +* Zero is returned if the new eye position is the same as the +* existing camera target position. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +* - This function can only be called to modify an existing Camera. +* Consequently it returns zero if a camera has not already been set +* for the current PGPLOT device by calling PG3DSetCamera. +*- +*/ + +/* Local Variables: */ + Camera *cam; + int result = 0; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 1 ); + if( cam ) { + +/* If so, modify the camera values, using the supplied eye position but + retaining the other camera settings. */ + result = PG3DSetCamera( eye, cam->target_vector, cam->up_vector, + cam->screen_distance ); + } + + return result; +} + +int PG3DRotateEye( int dir, float angle ){ +/* +*+ +* Name: +* PG3DRotateEye + +* Purpose: +* Move the eye on a great circle around the current target position. + +* Synopsis: +* #include "grf3d.h" +* int PG3DRotateEye( int dir, float angle ) + +* Description: +* This function modifies the camera eye position for the current +* PGPLOT device. Other camera settings are left unchanged. See +* PG3DSetCamera for more details. +* +* The eye is moved by a gven distance along an arc of a great circle +* centred on the current target position. The target position itself +* is left unchanged. + +* Parameters: +* dir +* The direction in which to move the eye position: +* 1 - Move eye upwards +* 2 - Move eye downwards +* 3 - Move eye left +* 4 - Move eye right +* angle +* The arc-distance, in degrees, by which to move the eye. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +* - This function can only be called to modify an existing Camera. +* Consequently it returns zero if a camera has not already been set +* for the current PGPLOT device by calling PG3DSetCamera. +*- +*/ + +/* Local Variables: */ + Camera *cam; + int result = 0; + int i; + float e[3], f[3], emod, neweye[3], sina, cosa; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 1 ); + if( cam ) { + +/* Get the cos and sine of the supplied angle. */ + cosa = cos( angle*TWOPI/360 ); + sina = sin( angle*TWOPI/360 ); + +/* Get the vector from the target to the eye, get its modulus. */ + vectorSub( cam->eye_vector, cam->target_vector, e ); + emod = vectorModulus( e ); + +/* If we are moving the eye upwards, find the new eye position. */ + if( dir == 1 ) { + for( i = 0; i < 3; i++ ) { + neweye[ i ] = e[ i ]*cosa + emod*cam->up_vector[ i ]*sina + + cam->target_vector[ i ]; + } + +/* If we are moving the eye downwards, find the new eye position. */ + } else if( dir == 2 ) { + for( i = 0; i < 3; i++ ) { + neweye[ i ] = e[ i ]*cosa - emod*cam->up_vector[ i ]*sina + + cam->target_vector[ i ]; + } + +/* If we are moving the eye left or right we need a vector in the plane + of rotation that is at right angles to "e", and points to the right + of the eye. */ + } else { + vectorProduct( cam->up_vector, e, f ); + vectorNorm( f ); + +/* Get the new eye position. */ + if( dir == 3 ) { + for( i = 0; i < 3; i++ ) { + neweye[ i ] = e[ i ]*cosa - emod*f[ i ]*sina + cam->target_vector[ i ]; + } + + } else { + for( i = 0; i < 3; i++ ) { + neweye[ i ] = e[ i ]*cosa + emod*f[ i ]*sina + cam->target_vector[ i ]; + } + } + } + +/* Modify the camera eye vector, retaining the other camera settings. */ + result = PG3DSetCamera( neweye, cam->target_vector, cam->up_vector, + cam->screen_distance ); + } + + return result; +} + +int PG3DForward( float distance ){ +/* +*+ +* Name: +* PG3DForward + +* Purpose: +* Move the eye forward towards the target. + +* Synopsis: +* #include "grf3d.h" +* int PG3DForward( float distance ) + +* Description: +* This function modifies the camera eye position for the current +* PGPLOT device. Other camera settings are left unchanged. See +* PG3DSetCamera for more details. +* +* The eye is moved forward by a given distance towards the target +* point, and the target point is also moved forward so that the +* distance between eye and target remains unchanged. + +* Parameters: +* distance +* The distance to move the eye and target, given as a fraction of +* the distance between the eye and the target. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +* - This function can only be called to modify an existing Camera. +* Consequently it returns zero if a camera has not already been set +* for the current PGPLOT device by calling PG3DSetCamera. +*- +*/ + +/* Local Variables: */ + Camera *cam; + int result = 0; + int i; + float e[3], newtarg[3], neweye[3]; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 1 ); + if( cam ) { + +/* Get the vector from the eye to the target. */ + vectorSub( cam->target_vector, cam->eye_vector, e ); + +/* Find the new eye and target positions. */ + for( i = 0; i < 3; i++ ){ + neweye[ i ] = cam->eye_vector[ i ] + e[ i ]*distance; + newtarg[ i ] = cam->target_vector[ i ] + e[ i ]*distance; + } + +/* Modify the camera eye and target vectors, retaining the other camera + settings. */ + result = PG3DSetCamera( neweye, newtarg, cam->up_vector, + cam->screen_distance ); + } + + return result; +} + + +int PG3DFindNearest( int n, float *x, float *y, float *z, int *iclose ){ +/* +*+ +* Name: +* PG3DForward + +* Purpose: +* Find the closest point to the eye. + +* Synopsis: +* #include "grf3d.h" +* int PG3DFindNearest( int n, float *x, float *y, float *z, int *iclose ) + +* Description: +* This function checks every supplied point and returns the index of +* the point that is closest to the eye. + +* Parameters: +* n +* The number of points to check. +* x +* Pointer to an array of "n" X values. +* y +* Pointer to an array of "n" Y values. +* z +* Pointer to an array of "n" Z values. +* iclose +* Pointer to an int in which to return the index of hte nearest +* point. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +*- +*/ + +/* Local Variables: */ + Camera *cam; + int result = 0; + int i; + float c[3], v[3]; + float d; + float dmin; + + *iclose = 0; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 1 ); + if( cam ) { + result = 1; + +/* Loop through all the supplied positions. */ + dmin = FLT_MAX; + for( i = 0; i < n; i++ ) { + +/* Get the required distance. */ + v[ 0 ] = x[ i ]; + v[ 1 ] = y[ i ]; + v[ 2 ] = z[ i ]; + vectorSub( v, cam->eye_vector, c ); + d = vectorModulus( c ); + +/* If this is the smallest distance so far, remember it. */ + if( d < dmin ) { + dmin = d; + *iclose = i; + } + } + } + + return result; +} + + +int PG3DSetTarget( float target[3] ){ +/* +*+ +* Name: +* PG3DSetTarget + +* Purpose: +* Store a new camera target position for the current PGPLOT device. + +* Synopsis: +* #include "grf3d.h" +* int PG3DSetTarget( float target[3] ) + +* Description: +* This function modifies the camera target position for the current +* PGPLOT device. Other camera settings are left unchanged. See +* PG3DSetCamera for more details. + +* Parameters: +* target +* The position vector of the camera's "target", in 3D world coordinates. +* Zero is returned if the new target position is the same as the +* existing camera eye position. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +* - This function can only be called to modify an existing Camera. +* Consequently it returns zero if a camera has not already been set +* for the current PGPLOT device by calling PG3DSetCamera. +*- +*/ + +/* Local Variables: */ + Camera *cam; + int result = 0; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 1 ); + if( cam ) { + +/* If so, modify the camera values, using the supplied target position but + retaining the other camera settings. */ + result = PG3DSetCamera( cam->eye_vector, target, cam->up_vector, + cam->screen_distance ); + } + + return result; +} + + +int PG3DSetUp( float up[3] ){ +/* +*+ +* Name: +* PG3DSetUp + +* Purpose: +* Store a new camera up vector for the current PGPLOT device. + +* Synopsis: +* #include "grf3d.h" +* int PG3DSetUp( float up[3] ) + +* Description: +* This function modifies the camera up vector for the current +* PGPLOT device. Other camera settings are left unchanged. See +* PG3DSetCamera for more details. + +* Parameters: +* up +* The new up vector, in 3D world coordinates. Zero is returned if +* the new up vector is parallel to the line joining the eye and +* the target positions. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +* - This function can only be called to modify an existing Camera. +* Consequently it returns zero if a camera has not already been set +* for the current PGPLOT device by calling PG3DSetCamera. +*- +*/ + +/* Local Variables: */ + Camera *cam; + int result = 0; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 1 ); + if( cam ) { + +/* If so, modify the camera values, using the supplied up vector but + retaining the other camera settings. */ + result = PG3DSetCamera( cam->eye_vector, cam->target_vector, up, + cam->screen_distance ); + } + + return result; +} + + +int PG3DSetScreen( float screen ){ +/* +*+ +* Name: +* PG3DSetScreen + +* Purpose: +* Store a new camera screen distance for the current PGPLOT device. + +* Synopsis: +* #include "grf3d.h" +* int PG3DSetScreen( float screen ) + +* Description: +* This function modifies the camera screen distance for the current +* PGPLOT device. Other camera settings are left unchanged. See +* PG3DSetCamera for more details. + +* Parameters: +* screen +* The distance from the camera's eye to the projection screen in +* 3D world coordinate units. If this is zero, then an orthographic +* projection is used. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +* - This function can only be called to modify an existing Camera. +* Consequently it returns zero if a camera has not already been set +* for the current PGPLOT device by calling PG3DSetCamera. +*- +*/ + +/* Local Variables: */ + Camera *cam; + int result = 0; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 1 ); + if( cam ) { + +/* If so, modify the camera values, using the supplied screen distance but + retaining the other camera settings. */ + result = PG3DSetCamera( cam->eye_vector, cam->target_vector, + cam->up_vector, screen ); + } + + return result; +} + +int PG3DAutoCamera( float lbnd[3], float ubnd[3] ){ +/* +*+ +* Name: +* PG3DAutoCamera + +* Purpose: +* Set up a default camera to view a given box of 3D world coords. + +* Synopsis: +* #include "grf3d.h" +* int PG3DAutoCamera( float lbnd[3], float ubnd[3] ) + +* Description: +* This function sets up the camera and the 2D PGPLOT window for the +* current device so that it produces a default view of a specified +* volume of 3D world coordinate space. + +* Parameters: +* lbnd +* The lower bounds of the volume of 3D world coordinates that +* is to be visible using the camera and 2D PGPLOT window. +* ubnd +* The upper bounds of the volume of 3D world coordinates that +* is to be visible using the camera and 2D PGPLOT window. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Zero is returned if no PGPLOT device has been opened prior to +* calling this function. +*- +*/ + +/* Local Variables: */ + float target[3], eye[3], up[3], screen, dx, dy, dz, hlo, hhi, rlo, rhi; + float x[8], y[8], z[8], h[8], r[8]; + Camera *cam; + int result = 0; + int i; + +/* Get a pointer to the Camera structure for the current PGPLOT device. + Return without action if no PGPLOT device is open. */ + cam = getCamera( 0 ); + if( cam ) { + +/* The target position (i.e. the position towards which the camera is + looking) is the middle of the volume. */ + target[ 0 ] = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] ); + target[ 1 ] = 0.5*( lbnd[ 1 ] + ubnd[ 1 ] ); + target[ 2 ] = 0.5*( lbnd[ 2 ] + ubnd[ 2 ] ); + +/* The eye is slightly offset from a corner view. */ + eye[ 0 ] = 0.85*ubnd[ 0 ] + 0.15*lbnd[ 0 ]; + eye[ 1 ] = 0.75*ubnd[ 1 ] + 0.25*lbnd[ 1 ]; + eye[ 2 ] = 0.75*ubnd[ 2 ] + 0.25*lbnd[ 2 ]; + +/* The eye is seven times the size of the box away from the box centre. */ + eye[ 0 ] = 7*(eye[ 0 ] - target[ 0 ] ) + target[ 0 ]; + eye[ 1 ] = 7*(eye[ 1 ] - target[ 1 ] ) + target[ 1 ]; + eye[ 2 ] = 7*(eye[ 2 ] - target[ 2 ] ) + target[ 2 ]; + +/* The up vector is paralle to the Z axis. */ + up[ 0 ] = 0.0; + up[ 1 ] = 0.0; + up[ 2 ] = 1.0; + +/* The screen is at the centre of the box. */ + dx = eye[ 0 ] - target[ 0 ]; + dy = eye[ 1 ] - target[ 1 ]; + dz = eye[ 2 ] - target[ 2 ]; + screen = sqrtf( dx*dx + dy*dy + dz*dz ); + +/* Set the camera. */ + if( PG3DSetCamera( eye, target, up, screen ) ) { + +/* Get the 3D World coords at the corners of the volume. */ + x[ 0 ] = ubnd[ 0 ]; + x[ 1 ] = ubnd[ 0 ]; + x[ 2 ] = lbnd[ 0 ]; + x[ 3 ] = lbnd[ 0 ]; + x[ 4 ] = ubnd[ 0 ]; + x[ 5 ] = ubnd[ 0 ]; + x[ 6 ] = lbnd[ 0 ]; + x[ 7 ] = lbnd[ 0 ]; + + y[ 0 ] = lbnd[ 1 ]; + y[ 1 ] = ubnd[ 1 ]; + y[ 2 ] = ubnd[ 1 ]; + y[ 3 ] = lbnd[ 1 ]; + y[ 4 ] = lbnd[ 1 ]; + y[ 5 ] = ubnd[ 1 ]; + y[ 6 ] = ubnd[ 1 ]; + y[ 7 ] = lbnd[ 1 ]; + + z[ 0 ] = lbnd[ 2 ]; + z[ 1 ] = lbnd[ 2 ]; + z[ 2 ] = lbnd[ 2 ]; + z[ 3 ] = lbnd[ 2 ]; + z[ 4 ] = ubnd[ 2 ]; + z[ 5 ] = ubnd[ 2 ]; + z[ 6 ] = ubnd[ 2 ]; + z[ 7 ] = ubnd[ 2 ]; + +/* Transform these into screen coordinates. */ + if( transform( cam, 8, x, y, z, h, r ) ) { + +/* Find the bounds in h and r of the projection of the volume. */ + hlo = FLT_MAX; + hhi = -FLT_MAX; + rlo = FLT_MAX; + rhi = -FLT_MAX; + + for( i = 0; i < 8; i++ ) { + if( h[ i ] < hlo ) hlo = h[ i ]; + if( h[ i ] > hhi ) hhi = h[ i ]; + if( r[ i ] < rlo ) rlo = r[ i ]; + if( r[ i ] > rhi ) rhi = r[ i ]; + } + +/* Extend these bounds by 5% at each end */ + dx = 0.05*( hhi - hlo ); + hhi += dx; + hlo -= dx; + + dy = 0.05*( rhi - rlo ); + rhi += dy; + rlo -= dy; + +/* If the box has non-zero area, set it as the 2D PGPLOT window, and + indicate success. */ + if( rlo < rhi && hlo < hhi ) { + ccpgswin( hlo, hhi, rlo, rhi ); + result = 1; + } + } + } + } + return result; +} + + + + + +/* Private functions for this module */ +/* ================================= */ + +static int TextCam( Camera *textcam, float ref[3], float tx[3], float ty[3], + float tz[3] ){ +/* +* Name: +* TextCam + +* Purpose: +* Create a Camera that converts 3D text plane coordinates into 2D world +* coordinates. + +* Synopsis: +* #include "grf3d.h" +* int TextCam( Camera *textcam, float ref[3], float tx[3], float ty[3], +* float tz[3] ) + +* Description: +* This function initialises the contents of a supplied Camera +* structure so that the Camera describes the transformation from 3D +* "text plane" coordinates to 2D PGPLOT world coordinates. The text +* plane coordinate system is defined by three vectors along its x, y +* and z axes, and an origin position. +* +* Text plane coordinates describe a plane upon which 2D graphics such +* as text is drawn. The X axis is parallel to the text base line, the +* Y axis is the text up vector, and the Z axis is perpendicular to +* the text, passing from the back of the text to the front of the text. + +* Parameters: +* textcam +* The Camera structure which is to be modified. +* ref +* The (x,y,z) coordinates at the text plane origin. +* tx +* A unit vector (expressed in 3D world coords) along the text plane +* X axis. This is parallel to the text base line. +* ty +* A unit vector (expressed in 3D world coords) along the text plane +* Y axis. This is parallel to the projectionof ht eup vector on to +* the text plane. +* tz +* A unit vector (expressed in 3D world coords) along the text plane +* Z axis. This is perpendicular to the text plane, passing from +* the back of the text to the front of the text. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*/ + + +/* Local Variables: */ + Camera *cam; + float dx, dy, dz; + int i; + float a, b, c; + +/* Get the Camera for the current device identifier. Abort if no camera + is available. This camera describes the transformation from 3D world + coordinates (x,y,z) to 2D world coordinates (screen coordinates) (h,r). */ + cam = getCamera( 1 ); + if( !cam ) return 0; + +/* Create a Camera structure that describes the transformation from + text plane coordinates to 2D world coords, putting the origin of text + plane coordinates at the given reference position. */ + dx = cam->eye_vector[ 0 ] - ref[ 0 ]; + dy = cam->eye_vector[ 1 ] - ref[ 1 ]; + dz = cam->eye_vector[ 2 ] - ref[ 2 ]; + + textcam->eye_vector[ 0 ] = tx[ 0 ]*dx + tx[ 1 ]*dy + tx[ 2 ]*dz; + textcam->eye_vector[ 1 ] = ty[ 0 ]*dx + ty[ 1 ]*dy + ty[ 2 ]*dz; + textcam->eye_vector[ 2 ] = tz[ 0 ]*dx + tz[ 1 ]*dy + tz[ 2 ]*dz; + + for( i = 0; i < 8; i += 3 ) { + a = cam->w2c_matrix[ i ]; + b = cam->w2c_matrix[ i + 1 ]; + c = cam->w2c_matrix[ i + 2 ]; + textcam->w2c_matrix[ i ] = a*tx[ 0 ] + b*tx[ 1 ] + c*tx[ 2 ]; + textcam->w2c_matrix[ i + 1 ] = a*ty[ 0 ] + b*ty[ 1 ] + c*ty[ 2 ]; + textcam->w2c_matrix[ i + 2 ] = a*tz[ 0 ] + b*tz[ 1 ] + c*tz[ 2 ]; + } + + textcam->screen_distance = cam->screen_distance; + textcam->ok_flag = CAMERA_OK; + + return 1; +} + +static int Polygon( int nside, float *vx, float *vy, float *vz, float ref[3], + float tx[3], float ty[3], float tz[3] ){ +/* +* Name: +* Polygon + +* Purpose: +* Draw a regular polygon. + +* Synopsis: +* #include "grf3d.h" +* int Polygon( int nside, float *vx, float *vy, float *vz, float ref[3], +* float tx[3], float ty[3], float tz[3] ) + +* Description: +* This function draws a polygon centred at a given position on a +* given plane in 3D world coords, using a specified up-vector. The +* polygon vertices are specified in text plane coordinates via vx, +* vy and vz. + +* Parameters: +* nside +* Number of sides for the polygon. Numbers higher than 32 are +* treated as 32. +* vx +* Pointer to an array of "nside" text plane X axis values. +* vy +* Pointer to an array of "nside" text plane Y axis values. +* vz +* Pointer to an array of "nside" text plane Z axis values. +* ref +* The (x,y,z) coordinates at the polygon centre. +* tx +* A unit vector (expressed in 3D world coords) along the text plane +* X axis. This is parallel to the text base line. +* ty +* A unit vector (expressed in 3D world coords) along the text plane +* Y axis. This is parallel to the projectionof ht eup vector on to +* the text plane. +* tz +* A unit vector (expressed in 3D world coords) along the text plane +* Z axis. This is perpendicular to the text plane, passing from +* the back of the text to the front of the text. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*/ + + +/* Local Variables: */ + Camera *cam; + Camera newcam; + float h[ MXSIDE ], r[ MXSIDE ]; + +/* Get the Camera for the current device identifier. Abort if no camera + is available. */ + cam = getCamera( 1 ); + if( !cam ) return 0; + +/* Check the number of points. */ + if( nside > MXSIDE) return 0; + +/* Create a Camera structure that describes the transformation from + text plane coordinates to 2D world coords, putting the origin of text + plane coordinates at the given reference position. */ + if( !TextCam( &newcam, ref, tx, ty, tz ) ) return 0; + +/* Transform the given text plane coordinates into 2D world coordinates. */ + if( !transform( &newcam, nside, vx, vy, vz, h, r ) ) return 0; + +/* Draw the polygon. */ + ccpgpoly( nside, h, r ); + +/* If we get here we have succeeded so return a non-zero value. */ + return 1; +} + +static int Text( int *list, int nlist, float ref[3], const char *just, + float tx[3], float ty[3], float tz[3] ){ +/* +* Name: +* Text + +* Purpose: +* Draw a character string. + +* Synopsis: +* #include "grf3d.h" +* int Text( int *list, int nlist, float ref[3], const char *just, +* float tx[3], float ty[3], float tz[3] ) + +* Description: +* This function displays a symbol list at a given position on a given +* plane in 3D world coords, using a specified justification and up-vector. + +* Parameters: +* list +* Pointer to an array of pgplot symbol values. +* nlist +* Length of the "list" array. +* ref +* The reference (x,y,z) coordinates. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* tx +* A unit vector (expressed in 3D world coords) along the text plane +* X axis. This is parallel to the text base line. +* ty +* A unit vector (expressed in 3D world coords) along the text plane +* Y axis. This is parallel to the projection of the up vector on to +* the text plane. +* tz +* A unit vector (expressed in 3D world coords) along the text plane +* Z axis. This is perpendicular to the text plane, passing from +* the back of the text to the front of the text. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - This routine does not recognise PGPLOT escape sequences. +* - A NULL value for "just" causes a value of "CC" to be used. +*/ + +/* Local Constants: */ +#define MXLEN 256 + +/* Local Variables: */ + Camera *cam; + Camera newcam; + float ch; + float tm, txc, tyc; + float txleft, tybase; + float xt[ 150 ], yt[ 150 ], zt[ 150 ], h[ 150 ], r[ 150 ]; + float xb[3], yb[3], zb[3], bl[3]; + int clip; + int i; + int j; + int k; + int unused; + int xygrid[ 300 ]; + +/* If there is nothing to plot return without error. */ + if( nlist == 0 ) return 1; + +/* Find the 3D world coordinates at the left hand end of the text + baseline. */ + if( !TxExt( list, nlist, ref, just, tx, ty, tz, xb, yb, zb, bl ) ) return 0; + +/* Get the Camera for the current device identifier. Abort if no camera + is available. */ + if( ! (cam = getCamera( 1 ) ) ) return 0; + +/* Create a Camera structure that describes the transformation from + text plane coordinates to 2D world coords. */ + if( !TextCam( &newcam, ref, tx, ty, tz ) ) return 0; + +/* Save the current clipping flag, and ensure clipping is off. */ + ccpgqclp( &clip ); + ccpgsclp( 0 ); + +/* Calculate the text plane X coord of the left hand edge of the first + character. */ + txleft = tx[ 0 ]*( bl[ 0 ] - ref[ 0 ] ) + + tx[ 1 ]*( bl[ 1 ] - ref[ 1 ] ) + + tx[ 2 ]*( bl[ 2 ] - ref[ 2 ] ); + +/* Calculate the text plane Y coord at the text baseline. */ + tybase = ty[ 0 ]*( bl[ 0 ] - ref[ 0 ] ) + + ty[ 1 ]*( bl[ 1 ] - ref[ 1 ] ) + + ty[ 2 ]*( bl[ 2 ] - ref[ 2 ] ); + +/* Get the character height in world coordinate units. A PGPLOT + character height of 1.0 corresponds to 1/40 of the 2D window height. */ + ch = getCharHeight(); + +/* Get the polylines that correspond to the first symbol. */ + ccgrsyxd( list[ 0 ], xygrid, &unused ); + +/* Create a linear transformation that maps the font grid coordinate + system used by grsyxd onto text plane coordinates. This transformation + will be different for each character in the string. The initial + transformation set up now is appropriate for the first character. The + mapping is: + + Text_x = txc + tm*Font_x + Text_y = tyc + tm*Font_y + +*/ + tm = ch/( xygrid[ 2 ] - xygrid[ 1 ] ); + tyc = tybase - tm*xygrid[ 1 ]; + txc = txleft - tm*xygrid[ 3 ]; + +/* Loop round each symbol. */ + for( i = 0; i < nlist; i++ ) { + +/* Loop round each polyline that forms a segment of the character */ + k = 5; + while( 1 ) { + +/* Map the polyline vertices into text plane coordinates. */ + j = 0; + while( j < 150 ){ + if( xygrid[ k ] != -64 ) { + xt[ j ] = txc + tm*xygrid[ k++ ]; + yt[ j ] = tyc + tm*xygrid[ k++ ]; + zt[ j++ ] = 0.0; + } else { + break; + } + } + +/* Map the text plane coordinates into 2D world coordinates. */ + if( j > 0 ) { + (void) transform( &newcam, j, xt, yt, zt, h, r ); + +/* Draw the polyline. */ + ccpgline( j, h, r ); + } + +/* If this is the last segment in the character, pass on to the next + character. */ + if( xygrid[ k + 1 ] == -64 ) break; + +/* Otherwise, skip over the end markers in the xygrid array, and go on to + plot the next polyline segment. */ + k += 2; + } + +/* If this is not the last symbol... */ + if( i != nlist - 1 ) { + +/* Set the text x value at which to place the left edge of the next + character. This is the right hand edge of the character just drawn. */ + txleft += tm*( xygrid[ 4 ] - xygrid[ 3 ] ); + +/* Get the polylines that correspond to the next symbol. */ + ccgrsyxd( list[ i + 1 ], xygrid, &unused ); + +/* Modify the transformation from font grid coords to text plane coords + so that it is appropriate for the next character in the string. */ + txc = txleft - tm*xygrid[ 3 ]; + } + +/* Next symbol. */ + } + +/* Re-instate original clipping flag. */ + ccpgsclp( clip ); + +/* If we arrive here, we have been successful, so return a non-zero + value. */ + return 1; + +/* Clear local constants. */ +#undef MXLEN +} + +static int TxExt( int *list, int nlist, float ref[3], const char *just, + float tx[3], float ty[3], float tz[3], float *xb, float *yb, + float *zb, float bl[3] ){ +/* +* Name: +* TxExt + +* Purpose: +* Get the extent of a character string. + +* Synopsis: +* #include "grf3d.h" +* int TxExt( int *list, int nlist, float ref[3], const char *just, +* float tx[3], float ty[3], float tz[3], float *xb, float *yb, +* float *zb, float bl[3] ) + +* Description: +* This function returns the corners of a box which would enclose the +* supplied symbol list if it were displayed using Text. +* +* The returned box includes any leading or trailing spaces. + +* Parameters: +* list +* Pointer to an array of pgplot symbol numbers. +* nlist +* The length of the "list" array. +* ref +* The reference (x,y,z) coordinates. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", 'B' for "baseline", or "M" for "bottom", and +* specifies the vertical location of the reference position. Note, +* "baseline" corresponds to the base-line of normal text. Some +* characters (eg "y", "g", "p", etc) descend below the base-line, +* and so "M" and "B" will produce different effects for such +* characters. The second character may be 'L' for "left", 'C' for +* "centre", or 'R' for "right", and specifies the horizontal +* location of the reference position. If the string has less than +* 2 characters then 'C' is used for the missing characters. +* tx +* A unit vector (expressed in 3D world coords) along the text plane +* X axis. This is parallel to the text base line. +* ty +* A unit vector (expressed in 3D world coords) along the text plane +* Y axis. This is parallel to the projectionof ht eup vector on to +* the text plane. +* tz +* A unit vector (expressed in 3D world coords) along the text plane +* Z axis. This is perpendicular to the text plane, passing from +* the back of the text to the front of the text. +* xb +* An array of 4 elements in which to return the x coordinate of +* each corner of the bounding box. +* yb +* An array of 4 elements in which to return the y coordinate of +* each corner of the bounding box. +* zb +* An array of 4 elements in which to return the z coordinate of +* each corner of the bounding box. +* bl +* The 3D world coordinates at the left hand end of the text +* baseline. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - The order of the corners is anti-clockwise starting at the +* bottom left when viewing the text normally (i.e. face on). +* - This routine does not recognise PGPLOT escape sequences. +* - A NULL value for "just" causes a value of "CC" to be used. +*/ + +/* Local Constants: */ +#define MXLEN 256 + +/* Local Variables: */ + float ch; + float txlo, txhi, tylo, tyhi, tyzero; + float tm; + float w; + int i; + int unused; + int xygrid[ 300 ]; + int gylo, gyhi, width; + +/* Initialise the returned values to indicate no box available. */ + for( i = 0; i < 4; i++ ){ + xb[ i ] = 0.0; + yb[ i ] = 0.0; + zb[ i ] = 0.0; + } + +/* If there is nothing to plot return without error. */ + if( nlist == 0 ) return 1; + +/* We now find the bounding box of the text in "text plane coordinates". + These are (tx,ty,tz) axes that span the plane upon which the text is + writtens. The origin of (tx,ty,tz) is at the supplied 3D reference + position, the X axis increases along the text baseline, and Y axis + increases along the text up vector, and Z increases from the back + of the text to the front of the text (all are measured in 3D world + coord units). We first find the bounds of the text in these text plane + coordinates, assuming that the bottom left of the text baseline is + placed at the given reference position (i.e. at the origiin of text + plane coordinates). */ + +/* Get the character height in world coordinate units. A PGPLOT + character height of 1.0 corresponds to 1/40 of the 2D window height. */ + ch = getCharHeight(); + +/* Initialise the Y bounds of the text bounding box in grid coords. */ + gylo = INT_MAX; + gyhi = -INT_MAX; + +/* Initialise things. */ + width = 0; + tm = 1.0; + +/* Loop round each symbol. */ + for( i = 0; i < nlist; i++ ) { + +/* Get the polylines that correspond to this symbol. */ + ccgrsyxd( list[ i ], xygrid, &unused ); + +/* If this is the first symbol, set the scaling factor that converts + grid units to text plane units. */ + if( i == 0 ) tm = ch/( xygrid[ 2 ] - xygrid[ 1 ] ); + +/* Note the highest and lowest y grid value. */ + w = xygrid[ 2 ] - xygrid[ 1 ]; + if( w > gyhi ) gyhi = w; + + w = xygrid[ 0 ] - xygrid[ 1 ]; + if( w < gylo ) gylo = w; + +/* Increment the total width of the string in grid units. */ + width += xygrid[ 4 ] - xygrid[ 3 ]; + } + +/* Set up the bounding box in text plane coordinates. */ + txlo = 0.0; + txhi = width*tm; + tylo = gylo*tm; + tyhi = gyhi*tm; + tyzero = 0.0; + +/* Adjust the text plane bounding box to take account of the specified + text justification. The above process implicitly assumed a + justifiation of "BL". */ + if( !just || just[ 0 ] == 'C' || just[ 0 ] == 0 ){ + w = 0.5*( tyhi + tylo ); + tylo -= w; + tyhi -= w; + tyzero -= w; + + } else if( just[ 0 ] == 'T' ){ + w = tyhi; + tylo -= w; + tyhi -= w; + tyzero -= w; + + } else if( just[ 0 ] == 'M' ){ + w = -tylo; + tylo += w; + tyhi += w; + tyzero += w; + + } else if( just[ 0 ] != 'B' ) { + astError( AST__GRFER, "astG3DTxExt: Justification string '%s' " + "is invalid.", just ); + return 0; + } + + if( !just || just[ 1 ] == 'C' || just[ 1 ] == 0 ){ + w = 0.5*( txhi + txlo ); + txlo -= w; + txhi -= w; + + } else if( just[ 1 ] == 'R' ){ + w = txhi; + txlo -= w; + txhi -= w; + + } else if( just[ 1 ] == 'L' ){ + w = txlo; + txlo -= w; + txhi -= w; + + } else { + astError( AST__GRFER, "astG3DTxExt: Justification string '%s' " + "is invalid.", just ); + return 0; + } + +/* Use the supplied text plane axis vectors to transform the corners of + the text plane bounding box into 3D world coordinates. */ + xb[ 0 ] = tx[ 0 ]*txlo + ty[ 0 ]*tylo + ref[ 0 ]; + yb[ 0 ] = tx[ 1 ]*txlo + ty[ 1 ]*tylo + ref[ 1 ]; + zb[ 0 ] = tx[ 2 ]*txlo + ty[ 2 ]*tylo + ref[ 2 ]; + + xb[ 1 ] = tx[ 0 ]*txhi + ty[ 0 ]*tylo + ref[ 0 ]; + yb[ 1 ] = tx[ 1 ]*txhi + ty[ 1 ]*tylo + ref[ 1 ]; + zb[ 1 ] = tx[ 2 ]*txhi + ty[ 2 ]*tylo + ref[ 2 ]; + + xb[ 2 ] = tx[ 0 ]*txhi + ty[ 0 ]*tyhi + ref[ 0 ]; + yb[ 2 ] = tx[ 1 ]*txhi + ty[ 1 ]*tyhi + ref[ 1 ]; + zb[ 2 ] = tx[ 2 ]*txhi + ty[ 2 ]*tyhi + ref[ 2 ]; + + xb[ 3 ] = tx[ 0 ]*txlo + ty[ 0 ]*tyhi + ref[ 0 ]; + yb[ 3 ] = tx[ 1 ]*txlo + ty[ 1 ]*tyhi + ref[ 1 ]; + zb[ 3 ] = tx[ 2 ]*txlo + ty[ 2 ]*tyhi + ref[ 2 ]; + +/* Also transform the text plane coordinates at the bottom left of the + text baseline into 3D world coordinates. */ + bl[ 0 ] = tx[ 0 ]*txlo + ty[ 0 ]*tyzero + ref[ 0 ]; + bl[ 1 ] = tx[ 1 ]*txlo + ty[ 1 ]*tyzero + ref[ 1 ]; + bl[ 2 ] = tx[ 2 ]*txlo + ty[ 2 ]*tyzero + ref[ 2 ]; + +/* If we get here, we have been succesful, so return a non-zero value. */ + return 1; + +/* Clear local constants. */ +#undef MXLEN +} + +static float getCharHeight( void ){ +/* +* Name: +* getCharHeight + +* Purpose: +* Get the current text height setting. + +* Synopsis: +* #include "grf3d.h" +* float getCharHeight( void ) + +* Description: +* This function returns the PGPLOT character height, scaled into +* world coordinate units. + +* Returned Value: +* The character height, in world coordinate units. + +*/ + +/* Local Variables: */ + float wx1, wx2, wy1, wy2; + float ch; + +/* Get the bounds of the PGPLTO 2D window. */ + ccpgqwin( &wx1, &wx2, &wy1, &wy2 ); + +/* Get the normalised PGPLOT character height. */ + ccpgqch( &ch ); + +/* A PGPLOT character height of 1.0 corresponds to 1/40 of the 2D window + height. Scale the normalised character height into world coordinate + units, and return it. */ + return ch*fabs( wy1 - wy2 )/40.0; + +} + +static int getTextAxes( float ref[3], float up[3], float norm[3], + const char *just, float tx[3], float ty[3], + float tz[3], char newjust[3] ){ +/* +* Name: +* getTextAxes + +* Purpose: +* Get unit vectors along the text plane coordinate axes. + +* Synopsis: +* #include "grf3d.h" +* int getTextAxes( float ref[3], float up[3], float norm[3], +* const char *just, float tx[3], float ty[3], +* float tz[3], char newjust[3] ) + +* Description: +* This function returns three unit vectors that define the axes of a +* 3D Cartesian coordinate system known as "text plane coordinates". +* These axes span the plane upon which text (or other graphics) is to +* be written. The origin is at the supplied 3D reference position, the +* X axis increases along the text baseline, and Y axis increases along +* the text up vector, and Z increases from the back of the text to the +* front of the text (all are measured in 3D world coord units). +* +* The returned vectors are reversed if this will result in text +* appearing more "normal" (i.e. viewed from the front rather than +* the back, and viewed upright rather thna upside down). If the +* vectors are reversed, the justification string is also changed so +* that the text occupies the requested area on the screen. + +* Parameters: +* ref +* The reference (x,y,z) coordinates. +* up +* The (x,y,z) up-vector for the text. The actual up vector used is +* the projection of the supplied vector onto the plane specified by +* "norm". +* norm +* The (x,y,z) components of a vector that is normal to the plane +* containing the text. The given vector passes through the text +* from the back to the front. If all components of this vector are +* zero, then a normal vector pointing towards the camera eye is used. +* just +* The requested text justification, as supplied to astG3DText. +* tx +* A unit vector along the text plane X axis. +* ty +* A unit vector along the text plane X axis. +* tz +* A unit vector along the text plane X axis. +* newjust +* The text justification to use. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. +*/ + +/* Local Variables: */ + Camera *cam; + float eye[3]; + +/* Initialise the returned justification to equal the supplied + justification, supplying defaults if required. . */ + if( just ) { + strncpy( newjust, just, 2 ); + if( !newjust[ 0 ] ) newjust[ 0 ] = 'C'; + if( !newjust[ 1 ] ) newjust[ 1 ] = 'C'; + newjust[ 2 ] = 0; + } else { + strcpy( newjust, "CC" ); + } + +/* Get the Camera for the current device identifier. Abort if no camera + is available. */ + if( !( cam = getCamera( 1 ) ) ) return 0; + +/* Calculate the vector from the reference position to the eye, and store + it in "eye". */ + vectorSub( cam->eye_vector, ref, eye ); + +/* Create unit vectors along the three axes of the text plane coordinate + system. These unit vectors are represented in terms of the 3D world + coordinate axes. The text Z axis is parallel to the supplied "norm" + vector. */ + tz[ 0 ] = norm[ 0 ]; + tz[ 1 ] = norm[ 1 ]; + tz[ 2 ] = norm[ 2 ]; + +/* Attempt to normalise the "tz" vector. If it has zero length, use the + offset from the reference point to the eye. */ + if( ! vectorNorm( tz ) ){ + +/* Use the "eye" vector calculated above as the text plane Z axis. */ + tz[ 0 ] = eye[ 0 ]; + tz[ 1 ] = eye[ 1 ]; + tz[ 2 ] = eye[ 2 ]; + } + +/* Find vectors along the text plane x and y axes. */ + vectorProduct( up, tz, tx ); + vectorProduct( tz, tx, ty ); + +/* Normalise the three text plane axis vectors. If any vector has zero + length, abort. */ + if( !vectorNorm( tx ) || !vectorNorm( ty ) || !vectorNorm( tz ) ) return 0; + +/* We now reverse text plane vectors if this will help ther text to be + viewed "normally" on the screen. If the existing vectors cause the + text to be viewed from the back rather than the front, reverse the tx + and tz vectors so that he text is viewed from the front. */ + if( dotProduct( tz, eye ) < 0.0 ) { + tz[ 0 ] = -tz[ 0 ]; + tz[ 1 ] = -tz[ 1 ]; + tz[ 2 ] = -tz[ 2 ]; + tx[ 0 ] = -tx[ 0 ]; + tx[ 1 ] = -tx[ 1 ]; + tx[ 2 ] = -tx[ 2 ]; + +/* The text will have spun around the up vector (i.e. the ty axis), so + modify the horizontal justification so that thex text occupies the same + area on the screen. */ + if( newjust[ 1 ] == 'L' ) { + newjust[ 1 ] = 'R'; + } else if( newjust[ 1 ] == 'R' ) { + newjust[ 1 ] = 'L'; + } + } + +/* If the existing vectors cause the text to be viewed upside down, reverse + the tx and ty vectors so that he text is viewed right way up. */ + if( dotProduct( ty, cam->up_vector ) < 0.0 ) { + ty[ 0 ] = -ty[ 0 ]; + ty[ 1 ] = -ty[ 1 ]; + ty[ 2 ] = -ty[ 2 ]; + tx[ 0 ] = -tx[ 0 ]; + tx[ 1 ] = -tx[ 1 ]; + tx[ 2 ] = -tx[ 2 ]; + +/* The text will have spun around the tz vector (i.e. the viewing vector), + so modify both vertical and horizontal justification so that the text + occupies the same area on the screen. */ + if( newjust[ 0 ] == 'B' || newjust[ 0 ] == 'M' ) { + newjust[ 0 ] = 'T'; + } else if( newjust[ 0 ] == 'T' ) { + newjust[ 0 ] = 'M'; + } + + if( newjust[ 1 ] == 'L' ) { + newjust[ 1 ] = 'R'; + } else if( newjust[ 1 ] == 'R' ) { + newjust[ 1 ] = 'L'; + } + } + +/* If we arraive here we have been succesful, so return a non-zero value. */ + return 1; +} + +static void getSymbolList( const char *text, int mxlen, int *nlist, int *list ){ +/* +* Name: +* getSymbolList + +* Purpose: +* Get the extent of a character string. + +* Synopsis: +* #include "grf3d.h" +* void getSymbolList( const char *text, int mxlen, int *nlist, int *list ) + +* Description: +* This function converts a supplied text string into a list of PGPLOT +* symbol numbers for the current PGPLOT font. + +* Parameters: +* text +* Pointer to a null-terminated character string. +* mxlen +* The length of the "list" array. +* nlist +* Pointer to an integer in which to place the number of symbol +* values stored in the "list" array. This will be returned equal +* to zero if there are no non-blank characters in the supplied +* string. If there is one or more non-blank characters in "text", +* then the returned list will include any trailing spaces. +* list +* Pointer to an array in which to return the symbol numbers. The +* array should be at least "mxlen" elements long. +*/ + + +/* Local Variables: */ + int font; + int tlen; + +/* Assume we have no symbols. */ + *nlist = 0; + +/* Check there is something to plot. */ + if( astChrLen( text ) > 0 ) { + +/* Find the length of text that can be displayed. */ + tlen = strlen( text ); + if( tlen > mxlen ) tlen = mxlen; + +/* Get the current PGPLOT font. */ + ccpgqcf( &font ); + +/* Convert the supplied string into a list of symbol numbers */ + ccgrsyds( list, nlist, text, tlen, font ); + } +} + +static Camera *getCamera( int check ){ +/* +*+ +* Name: +* getCamera + +* Purpose: +* Return a pointer to the Camera structure for the current PGPLOT +* device. + +* Synopsis: +* #include "grf3d.h" +* Camera getCamera( int check ) + +* Description: +* This function returns a pointer to a static structure that defines the +* position and orientation of the camera in 3D world coords. It can +* be used to transform positions from 3D world coordinates (x,y,z) to +* 2D screen coordinates (h,r). + +* Parameters: +* check +* If non-zero, a check will be made that the Camera has been +* initialised, and NULL will be returned if the Camera has not +* been initialsied. If "check" is zero, a pointer to the Camera +* is returned even if it has not been initialised. + +* Returned Value: +* Pointer to the Camera, or NULL if an error occurs. +*- +*/ + +/* Local Variables: */ + int id; + Camera *cam = NULL; + +/* Get the pgplot current device identifier. Return NULL if no device is + currently open. */ + ccpgqid( &id ); + if( id > 0 && id <= MXDEV ) { + +/* Get a pointer to the required Camera structure. */ + cam = cameras + id - 1; + +/* If required, check that the structure has been initialised. */ + if( check && cam->ok_flag != CAMERA_OK ) cam = NULL; + } + + return cam; +} + +static int transform( Camera *cam, int n, float *x, float *y, float *z, + float *h, float *r ){ +/* +*+ +* Name: +* transform + +* Purpose: +* Transform positions from 3D world coords to 2D screen cooords. + +* Synopsis: +* #include "grf3d.h" +* int transform( Camera *cam, int n, float *x, float *y, float *z, +* float *h, float *r ) + +* Description: +* This function transforms a set of positions from 3D world +* coordinates (x,y,z) to 2D screen coordinates (h,r), using the +* supplied camera. + +* Parameters: +* cam +* Pointer to a structure descibing the projection from 3D world +* coords to 2D screen coords. If NULL, the camera for the current +* PGPLOT device is used. +* n +* The number of positions to transform. +* x +* An array of "n" values for the "x" axis of the 3D world +* coordinate system. +* y +* An array of "n" values for the "y" axis of the 3D world +* coordinate system. +* z +* An array of "n" values for the "z" axis of the 3D world +* coordinate system. +* h +* An array to receive the "n" values for the "h" axis of the 2D +* screen coordinate system. +* r +* An array to receive the "n" values for the "r" axis of the 2D +* screen coordinate system. + +* Returned Value: +* Zero if an error occurs. One otherwise. + +*- +*/ + +/* Local Variables: */ + float dx, dy, dz, u, v, w, f; + int i; + int result = 0; + +/* If no camera was supplied use the camera for the current PGPLOT + device. */ + if( ! cam ) cam = getCamera( 0 ); + +/* Check we now have a usable camera */ + if( cam && cam->ok_flag == CAMERA_OK ) { + result = 1; + +/* Loop round each position. */ + for( i = 0; i < n; i++ ) { + +/* Offset from supplied position to the camera eye. */ + dx = x[ i ] - (cam->eye_vector)[ 0 ]; + dy = y[ i ] - (cam->eye_vector)[ 1 ]; + dz = z[ i ] - (cam->eye_vector)[ 2 ]; + +/* Get the representation of this vector in the (u,v,w) system. */ + u = (cam->w2c_matrix)[ 0 ]*dx + + (cam->w2c_matrix)[ 1 ]*dy + + (cam->w2c_matrix)[ 2 ]*dz; + + v = (cam->w2c_matrix)[ 3 ]*dx + + (cam->w2c_matrix)[ 4 ]*dy + + (cam->w2c_matrix)[ 5 ]*dz; + + w = (cam->w2c_matrix)[ 6 ]*dx + + (cam->w2c_matrix)[ 7 ]*dy + + (cam->w2c_matrix)[ 8 ]*dz; + +/* Find the screen coords, using either a tangent plane or an + orothograhic projection. */ + if( cam->screen_distance != 0.0 ) { + if( w != 0.0 ) { + f = cam->screen_distance/w; + h[ i ] = -f*u; + r[ i ] = f*v; + } else { + h[ i ] = FLT_MAX; + r[ i ] = FLT_MAX; + } + } else { + h[ i ] = -u; + r[ i ] = v; + } + + } + } + return result; +} + + +/* Dot product of a pair of 3-vectors "a" and "b". */ +static float dotProduct( float *a, float *b ){ + return a[ 0 ]*b[ 0 ] + a[ 1 ]*b[ 1 ] + a[ 2 ]*b[ 2 ]; +} + +/* Vector product of a pair of 3-vectors "a" and "b". */ +static void vectorProduct( float *a, float *b, float *c ){ + c[ 0 ] = a[ 1 ]*b[ 2 ] - a[ 2 ]*b[ 1 ]; + c[ 1 ] = a[ 2 ]*b[ 0 ] - a[ 0 ]*b[ 2 ]; + c[ 2 ] = a[ 0 ]*b[ 1 ] - a[ 1 ]*b[ 0 ]; +} + +/* Vector from "b" to "a" (i.e. a minus b) . */ +static void vectorSub( float *a, float *b, float *c ){ + c[ 0 ] = a[ 0 ] - b[ 0 ]; + c[ 1 ] = a[ 1 ] - b[ 1 ]; + c[ 2 ] = a[ 2 ] - b[ 2 ]; +} + +/* Normalises a vector to a unit length. Returns zero if the vector has + zero length, and 1 otherwise. */ +static int vectorNorm( float *a ){ + float d; + d = vectorModulus( a ); + if( d > 0.0 ) { + a[ 0 ] /= d; + a[ 1 ] /= d; + a[ 2 ] /= d; + return 1; + } else { + return 0; + } +} + +/* Return the length of a vector. */ +static float vectorModulus( float *a ){ + return sqrtf( a[ 0 ]*a[ 0 ] + a[ 1 ]*a[ 1 ] + a[ 2 ]*a[ 2 ] ); +} + + + + + + + +/* PGPLOT interface functions */ +/* ========================== */ +static void ccpgqclp(int *clip){ + F77_INTEGER_TYPE CLIP; + F77_CALL(pgqclp)( INTEGER_ARG(&CLIP) ); + *clip = (int) CLIP; +} + +static void ccpgsclp(int clip){ + F77_INTEGER_TYPE CLIP; + CLIP = (F77_INTEGER_TYPE) clip; + F77_CALL(pgsclp)( INTEGER_ARG(&CLIP) ); +} + +static void ccpgqid(int *id){ + F77_INTEGER_TYPE ID; + F77_CALL(pgqid)( INTEGER_ARG(&ID) ); + *id = (int) ID; +} + + +static void ccpgswin(float x1, float x2, float y1, float y2){ + F77_REAL_TYPE X1; + F77_REAL_TYPE X2; + F77_REAL_TYPE Y1; + F77_REAL_TYPE Y2; + + X1 = x1; + X2 = x2; + Y1 = y1; + Y2 = y2; + + F77_CALL(pgswin)( REAL_ARG(&X1), REAL_ARG(&X2), REAL_ARG(&Y1), + REAL_ARG(&Y2) ); +} + +static void ccpgline(int n, float xpts[], float ypts[] ){ + F77_INTEGER_TYPE N; + F77_REAL_TYPE *XX; + F77_REAL_TYPE *YY; + int i; + + XX = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + YY = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + + if( astOK ){ + + for( i = 0; i < n; i++ ){ + XX[ i ] = (F77_REAL_TYPE) xpts[ i ]; + YY[ i ] = (F77_REAL_TYPE) ypts[ i ]; + } + + N = (F77_INTEGER_TYPE) n; + + F77_CALL(pgline)( INTEGER_ARG(&N), REAL_ARRAY_ARG(XX), + REAL_ARRAY_ARG(YY) ); + + XX = (F77_REAL_TYPE *) astFree( (void *) XX ); + YY = (F77_REAL_TYPE *) astFree( (void *) YY ); + } +} + +static void ccpgpoly(int n, float xpts[], float ypts[] ){ + F77_INTEGER_TYPE N; + F77_REAL_TYPE *XX; + F77_REAL_TYPE *YY; + int i; + + XX = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + YY = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + + if( astOK ){ + + for( i = 0; i < n; i++ ){ + XX[ i ] = (F77_REAL_TYPE) xpts[ i ]; + YY[ i ] = (F77_REAL_TYPE) ypts[ i ]; + } + + N = (F77_INTEGER_TYPE) n; + + F77_CALL(pgpoly)( INTEGER_ARG(&N), REAL_ARRAY_ARG(XX), + REAL_ARRAY_ARG(YY) ); + + XX = (F77_REAL_TYPE *) astFree( (void *) XX ); + YY = (F77_REAL_TYPE *) astFree( (void *) YY ); + } +} + +static void ccpgqwin(float *x1, float *x2, float *y1, float *y2){ + F77_REAL_TYPE X1; + F77_REAL_TYPE X2; + F77_REAL_TYPE Y1; + F77_REAL_TYPE Y2; + + F77_CALL(pgqwin)( REAL_ARG(&X1), REAL_ARG(&X2), REAL_ARG(&Y1), + REAL_ARG(&Y2) ); + *x1 = (float) X1; + *x2 = (float) X2; + *y1 = (float) Y1; + *y2 = (float) Y2; +} + +static void ccpgqch(float *ch){ + F77_REAL_TYPE CH; + F77_CALL(pgqch)( REAL_ARG(&CH) ); + *ch = (float) CH; +} + +static void ccpgqcf(int *cf){ + F77_INTEGER_TYPE CF; + F77_CALL(pgqcf)( INTEGER_ARG(&CF) ); + *cf = (int) CF; +} + +static void ccgrsyds( int *list, int *nlist, const char *text, int tlen, + int font ){ + F77_INTEGER_TYPE *LIST; + F77_INTEGER_TYPE NLIST; + DECLARE_CHARACTER(LTEXT,MXSTRLEN); + F77_INTEGER_TYPE FONT; + int ftext_length; + int i; + + ftext_length = tlen; + if( ftext_length > LTEXT_length ) ftext_length = LTEXT_length; + astStringExport( text, LTEXT, ftext_length ); + + LIST = (F77_INTEGER_TYPE *) astMalloc( sizeof( F77_INTEGER_TYPE )*(size_t) ftext_length ); + + if( astOK ){ + + FONT = (F77_INTEGER_TYPE) font; + + F77_CALL(grsyds)( INTEGER_ARRAY_ARG(LIST), INTEGER_ARG(&NLIST), + CHARACTER_ARG(LTEXT), INTEGER_ARG(&FONT) + TRAIL_ARG(ftext) ); + + *nlist = (int) NLIST; + for( i = 0; i < ftext_length; i++ ){ + list[ i ] = (int) LIST[ i ]; + } + + LIST = (F77_INTEGER_TYPE *) astFree( (void *) LIST ); + } +} + +static void ccgrsymk( int type, int font, int *symbol ){ + F77_INTEGER_TYPE TYPE; + F77_INTEGER_TYPE FONT; + F77_INTEGER_TYPE SYMBOL; + + TYPE = (F77_INTEGER_TYPE) type; + FONT = (F77_INTEGER_TYPE) font; + F77_CALL(grsymk)( INTEGER_ARG(&TYPE), INTEGER_ARG(&FONT), + INTEGER_ARG(&SYMBOL) ); + *symbol = (int) SYMBOL; +} + + +static void ccgrsyxd( int symbol, int *xygrid, int *unused ){ + F77_INTEGER_TYPE SYMBOL; + DECLARE_INTEGER_ARRAY(XYGRID,300); + F77_LOGICAL_TYPE UNUSED; + int i; + + SYMBOL = (F77_INTEGER_TYPE) symbol; + F77_CALL(grsyxd)( INTEGER_ARG(&SYMBOL), INTEGER_ARRAY_ARG(XYGRID), + LOGICAL_ARG(&UNUSED) ); + + *unused = ( UNUSED == F77_TRUE ); + for( i = 0; i < 5; i++ ) xygrid[ i ] = (int) XYGRID[ i ]; + for( ; i < 300; i++ ){ + xygrid[ i ] = (int) XYGRID[ i ]; + i++; + if( ( xygrid[ i ] = (int) XYGRID[ i ] ) == -64 ) break; + } +} + +static void ccpgupdt( void ){ + F77_CALL(pgupdt)(); +} + +static void ccpgqci(int *ci){ + F77_INTEGER_TYPE CI; + F77_CALL(pgqci)( INTEGER_ARG(&CI) ); + *ci = (int) CI; +} + +static void ccpgqls(int *ls){ + F77_INTEGER_TYPE LS; + F77_CALL(pgqls)( INTEGER_ARG(&LS) ); + *ls = (int) LS; +} + +static void ccpgqlw(int *lw){ + F77_INTEGER_TYPE LW; + F77_CALL(pgqlw)( INTEGER_ARG(&LW) ); + *lw = (int) LW; +} + +static void ccpgscf(int cf){ + F77_INTEGER_TYPE CF; + CF = (F77_INTEGER_TYPE) cf; + F77_CALL(pgscf)( INTEGER_ARG(&CF) ); +} + +static void ccpgsch(float ch){ + F77_REAL_TYPE CH; + CH = (F77_REAL_TYPE) ch; + F77_CALL(pgsch)( REAL_ARG(&CH) ); +} + +static void ccpgsci(int ci){ + F77_INTEGER_TYPE CI; + CI = (F77_INTEGER_TYPE) ci; + F77_CALL(pgsci)( INTEGER_ARG(&ci) ); +} + +static void ccpgsls(int ls){ + F77_INTEGER_TYPE LS; + LS = (F77_INTEGER_TYPE) ls; + F77_CALL(pgsls)( INTEGER_ARG(&LS) ); +} + +static void ccpgslw(int lw){ + F77_INTEGER_TYPE LW; + LW = (F77_INTEGER_TYPE) lw; + F77_CALL(pgslw)( INTEGER_ARG(&LW) ); +} + +static void ccpgqvsz(int units, float *x1, float *x2, float *y1, float *y2){ + F77_INTEGER_TYPE UNITS; + F77_REAL_TYPE X1; + F77_REAL_TYPE X2; + F77_REAL_TYPE Y1; + F77_REAL_TYPE Y2; + + UNITS = (F77_INTEGER_TYPE) units; + F77_CALL(pgqvsz)( INTEGER_ARG(&UNITS), REAL_ARG(&X1), REAL_ARG(&X2), + REAL_ARG(&Y1), REAL_ARG(&Y2) ); + *x1 = (float) X1; + *x2 = (float) X2; + *y1 = (float) Y1; + *y2 = (float) Y2; +} + + + +/* Fortran interfaces for public functions in this module. */ +/* ======================================================= */ + + +F77_LOGICAL_FUNCTION(pg3d_findnearest)( INTEGER(N), + REAL_ARRAY(X), + REAL_ARRAY(Y), + REAL_ARRAY(Z), + INTEGER(ICLOSE) ){ + GENPTR_INTEGER(N) + GENPTR_REAL_ARRAY(X) + GENPTR_REAL_ARRAY(Y) + GENPTR_REAL_ARRAY(Z) + GENPTR_INTEGER(ICLOSE) + return PG3DFindNearest( *N, X, Y, Z, ICLOSE ) ? F77_TRUE : F77_FALSE; +} + + + +F77_LOGICAL_FUNCTION(pg3d_setcamera)( REAL_ARRAY(EYE), + REAL_ARRAY(TARGET), + REAL_ARRAY(UP), + REAL(SCREEN) ){ + GENPTR_REAL_ARRAY(EYE) + GENPTR_REAL_ARRAY(TARGET) + GENPTR_REAL_ARRAY(UP) + GENPTR_REAL(SCREEN) + return PG3DSetCamera( EYE, TARGET, UP, *SCREEN ) ? F77_TRUE : F77_FALSE; +} + + +F77_LOGICAL_FUNCTION(pg3d_autocamera)( REAL_ARRAY(LBND), + REAL_ARRAY(UBND) ){ + GENPTR_REAL_ARRAY(LBND) + GENPTR_REAL_ARRAY(UBND) + return PG3DAutoCamera( LBND, UBND ) ? F77_TRUE : F77_FALSE; +} + +F77_LOGICAL_FUNCTION(pg3d_seteye)( REAL_ARRAY(EYE) ){ + GENPTR_REAL_ARRAY(EYE) + return PG3DSetEye( EYE ) ? F77_TRUE : F77_FALSE; +} + +F77_LOGICAL_FUNCTION(pg3d_setup)( REAL_ARRAY(UP) ){ + GENPTR_REAL_ARRAY(UP) + return PG3DSetUp( UP ) ? F77_TRUE : F77_FALSE; +} + +F77_LOGICAL_FUNCTION(pg3d_rotateeye)( INTEGER(DIR), REAL(ANGLE) ){ + GENPTR_INTEGER(DIR) + GENPTR_REAL(ANGLE) + return PG3DRotateEye( *DIR, *ANGLE ) ? F77_TRUE : F77_FALSE; +} + +F77_LOGICAL_FUNCTION(pg3d_forward)( REAL(DISTANCE) ){ + GENPTR_REAL(DISTANCE) + return PG3DForward( *DISTANCE ) ? F77_TRUE : F77_FALSE; +} + +F77_LOGICAL_FUNCTION(ast_g3dmark)( INTEGER(N), + REAL_ARRAY(X), + REAL_ARRAY(Y), + REAL_ARRAY(Z), + INTEGER(TYPE), + REAL_ARRAY(NORM)){ + GENPTR_INTEGER(N) + GENPTR_REAL_ARRAY(X) + GENPTR_REAL_ARRAY(Y) + GENPTR_REAL_ARRAY(Z) + GENPTR_INTEGER(TYPE) + GENPTR_REAL_ARRAY(NORM) + return astG3DMark( *N, X, Y, Z, *TYPE, NORM ) ? F77_TRUE : F77_FALSE; + +} + +F77_LOGICAL_FUNCTION(ast_g3dline)( INTEGER(N), + REAL_ARRAY(X), + REAL_ARRAY(Y), + REAL_ARRAY(Z) ){ + GENPTR_INTEGER(N) + GENPTR_REAL_ARRAY(X) + GENPTR_REAL_ARRAY(Y) + GENPTR_REAL_ARRAY(Z) + return astG3DLine( *N, X, Y, Z ) ? F77_TRUE : F77_FALSE; + +} + + +F77_INTEGER_FUNCTION(ast_g3dtext)( CHARACTER(TEXT), + REAL_ARRAY(REF), + CHARACTER(JUST), + REAL_ARRAY(UP), + REAL_ARRAY(NORM) + TRAIL(TEXT) + TRAIL(JUST) ){ + GENPTR_CHARACTER(TEXT) + GENPTR_REAL_ARRAY(REF) + GENPTR_CHARACTER(JUST) + GENPTR_REAL_ARRAY(UP) + GENPTR_REAL_ARRAY(NORM) + F77_INTEGER_TYPE(RESULT); + char *text, *just, *p; + + text = astString( TEXT, TEXT_length ); + just = astString( JUST, JUST_length ); + +/* Ignore trailing spaces in the text */ + p = text + TEXT_length; + while( !*p || *p == ' ' ) *(p--) = 0; + + if( astOK ) { + RESULT = (F77_INTEGER_TYPE) astG3DText( text, REF, just, UP, NORM ); + } else { + RESULT = 0; + } + + (void) astFree( text ); + (void) astFree( just ); + + return RESULT; +} + +F77_INTEGER_FUNCTION(ast_g3dtxext)( CHARACTER(TEXT), + REAL_ARRAY(REF), + CHARACTER(JUST), + REAL_ARRAY(UP), + REAL_ARRAY(NORM), + REAL_ARRAY(XB), + REAL_ARRAY(YB), + REAL_ARRAY(ZB), + REAL_ARRAY(BL) + TRAIL(TEXT) + TRAIL(JUST) ){ + GENPTR_CHARACTER(TEXT) + GENPTR_REAL_ARRAY(REF) + GENPTR_CHARACTER(JUST) + GENPTR_REAL_ARRAY(UP) + GENPTR_REAL_ARRAY(NORM) + GENPTR_REAL_ARRAY(XB) + GENPTR_REAL_ARRAY(YB) + GENPTR_REAL_ARRAY(ZB) + GENPTR_REAL_ARRAY(BL) + F77_INTEGER_TYPE(RESULT); + char *text, *just, *p; + + text = astString( TEXT, TEXT_length ); + just = astString( JUST, JUST_length ); + +/* Ignore trailing spaces in the text */ + p = text + TEXT_length; + while( !*p || *p == ' ' ) *(p--) = 0; + + if( astOK ) { + RESULT = (F77_INTEGER_TYPE) astG3DTxExt( text, REF, just, UP, NORM, + XB, YB, ZB, BL ); + } else { + RESULT = 0; + } + + (void) astFree( text ); + (void) astFree( just ); + + return RESULT; +} + + diff --git a/grf_2.0.c b/grf_2.0.c new file mode 100644 index 0000000..170c191 --- /dev/null +++ b/grf_2.0.c @@ -0,0 +1,101 @@ +/* +* Name: +* grf_2.0.c + +* Purpose: +* Implement the grf module required by AST V2.0 if no graphics system +* is available. + +* Description: +* This file implements the low level graphics functions required +* by the rest of AST V2.0, by reporting errors when called. + +* Inheritance: +* This module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-OCT-1996 (DSB): +* Original version. +* 13-NOV-1996 (DSB): +* Modified to issue error messages using astError instead of printf. +* 23-NOV-2004 (DSB): +* Renamed from grf_null.c +*/ + +/* Header files */ +/* ============ */ +#include "grf.h" /* Declare the functions in this module */ +#include "error.h" /* AST error reporting facilities */ +#include "ast_err.h" /* AST error codes */ + +/* Function Prototypes */ +/* =================== */ +static void Report( const char * ); + +/* Function definitions */ +/* ==================== */ +int astGFlush( void ){ + Report( "astGFlush"); + return 0; +} + +int astGLine( int n, const float *x, const float *y ){ + Report( "astGLine" ); + return 0; +} + +int astGQch( float *chv, float *chh ){ + Report( "astGQch" ); + return 0; +} + +int astGMark( int n, const float *x, const float *y, int type ){ + Report( "astGMark" ); + return 0; +} + +int astGText( const char *text, float x, float y, const char *just, + float upx, float upy ){ + Report( "astGText" ); + return 0; +} + +int astGTxExt( const char *text, float x, float y, const char *just, + float upx, float upy, float *xb, float *yb ){ + Report( "astGTxExt" ); + return 0; +} + +int astGAttr( int attr, double value, double *old_value, int prim ){ + Report( "astGAttr" ); + return 0; +} + +static void Report( const char *name ){ + astError( AST__GRFER, "%s: No graphics facilities are available.", name ); + astError( AST__GRFER, "Re-link using an option such as '-pgplot' with " + "the ast_link script." ); +} diff --git a/grf_3.2.c b/grf_3.2.c new file mode 100644 index 0000000..d6fc1a1 --- /dev/null +++ b/grf_3.2.c @@ -0,0 +1,74 @@ +/* +* Name: +* grf_3.2.c + +* Purpose: +* Implement the grf module required by AST V3.2 if no graphics system +* is available. + +* Description: +* This file implements the low level graphics functions required +* by the rest of AST V3.2, except for those already defined in +* grf_2.0.c (i.e. those needed by AST V2.0). These implementations +* simply report an error when called. + +* Inheritance: +* This module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-NOV-2004 (DSB): +* Original version. +*/ + +/* Header files */ +/* ============ */ +#include "grf.h" /* Declare the functions in this module */ +#include "error.h" /* AST error reporting facilities */ +#include "ast_err.h" /* AST error codes */ + +/* Function Prototypes */ +/* =================== */ +static void Report( const char * ); + +/* Function definitions */ +/* ==================== */ +int astGScales( float *alpha, float *beta ){ + Report( "astGScales" ); + return 0; +} + +int astGCap( int cap, int value ){ + return 0; +} + +static void Report( const char *name ){ + astError( AST__GRFER, "%s: The graphics facilities implement by %s " + "(introduced at AST V3.2) are needed but are unavailable.", + name, name ); + astError( AST__GRFER, "Re-link using a suitable option such as '-pgplot' " + "with the ast_link script, or add an implementation of this " + "function to your 'grf' module." ); +} diff --git a/grf_5.6.c b/grf_5.6.c new file mode 100644 index 0000000..3a77f58 --- /dev/null +++ b/grf_5.6.c @@ -0,0 +1,77 @@ +/* +* Name: +* grf_5.6.c + +* Purpose: +* Implement the grf module required by AST V5.6 if no graphics system +* is available. + +* Description: +* This file implements the low level graphics functions required +* by the rest of AST V5.6, except for those already defined in +* earlier grf_xxx.c files (i.e. those needed by ealier versions +* of AST). These implementations simply report an error when called. + +* Inheritance: +* This module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 2011 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 4-MAR-2011 (DSB): +* Original version. +*/ + +/* Header files */ +/* ============ */ +#include "grf.h" /* Declare the functions in this module */ +#include "error.h" /* AST error reporting facilities */ +#include "ast_err.h" /* AST error codes */ + +/* Function Prototypes */ +/* =================== */ +static void Report( const char * ); + +/* Function definitions */ +/* ==================== */ +int astGBBuf( void ){ + Report( "astGBBuf" ); + return 0; +} + +int astGEBuf( void ){ + Report( "astGEBuf" ); + return 0; +} + +static void Report( const char *name ){ + astError( AST__GRFER, "%s: The graphics facilities implement by %s " + "(introduced at AST V5.6) are needed but are unavailable.", + name, name ); + astError( AST__GRFER, "Re-link using a suitable option such as '-pgplot' " + "with the ast_link script, or add an implementation of this " + "function to your 'grf' module." ); +} + + diff --git a/grf_null.c b/grf_null.c new file mode 100644 index 0000000..ef7a97e --- /dev/null +++ b/grf_null.c @@ -0,0 +1,98 @@ +/* +* Name: +* grf_null.c + +* Purpose: +* Implement the grf module if no graphics system is available. + +* Description: +* This file implements the low level graphics functions required +* by the rest of AST, by reporting errors when called. + +* Inheritance: +* This module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-OCT-1996 (DSB): +* Original version. +* 13-NOV-1996 (DSB): +* Modified to issue error messages using astError instead of printf. +*/ + +/* Header files */ +/* ============ */ +#include "grf.h" /* Declare the functions in this module */ +#include "error.h" /* AST error reporting facilities */ +#include "ast_err.h" /* AST error codes */ + +/* Function Prototypes */ +/* =================== */ +static void Report( const char * ); + +/* Function definitions */ +/* ==================== */ +int astGFlush( void ){ + Report( "astGFlush"); + return 0; +} + +int astGLine( int n, const float *x, const float *y ){ + Report( "astGLine" ); + return 0; +} + +int astGQch( float *chv, float *chh ){ + Report( "astGQch" ); + return 0; +} + +int astGMark( int n, const float *x, const float *y, int type ){ + Report( "astGMark" ); + return 0; +} + +int astGText( const char *text, float x, float y, const char *just, + float upx, float upy ){ + Report( "astGText" ); + return 0; +} + +int astGTxExt( const char *text, float x, float y, const char *just, + float upx, float upy, float *xb, float *yb ){ + Report( "astGTxExt" ); + return 0; +} + +int astGAttr( int attr, double value, double *old_value, int prim ){ + Report( "astGAttr" ); + return 0; +} + +static void Report( const char *name ){ + astError( AST__GRFER, "%s: No graphics facilities are available.", name ); + astError( AST__GRFER, "Re-link using an option such as '-pgplot' with " + "the ast_link script." ); +} diff --git a/grf_pgplot.c b/grf_pgplot.c new file mode 100644 index 0000000..d0b1193 --- /dev/null +++ b/grf_pgplot.c @@ -0,0 +1,1494 @@ +/* +* Name: +* grf_pgplot.c + +* Purpose: +* Implement the grf module using the PGPLOT graphics system. + +* Description: +* This file implements the low level graphics functions required +* by the rest of AST, by calling suitable PGPLOT functions (the +* FORTRAN PGPLOT interface is used). +* +* This file can be used as a template for the development of +* similar modules to support alternative graphics systems. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 27-JUN-1996 (DSB): +* Original version. +* 13-NOV-1996 (DSB): +* Use C wrappers for PGPLOT functions. +* 15-NOV-1996 (RFWS): +* Merged the C interface to PGPLOT into this file so that the +* interface functions can be static. +* 7-OCT-1997 (DSB): +* Corrected astGText and astGTxExt, by including a check for +* reversed axes. Previously, the up-vector was used as supplied +* even if the axes had been reversed. +* 15-OCT-1997 (DSB): +* o Corrected astGText and astGTxExt to take account of non-equal +* scales on the two axes. +* o Modified astGTxExt so that it includes any leading or trailing +* spaces in the returned box. +* o Added astGAxScale. +* 28-OCT-1998 (DSB): +* o Changed interpretation of the Width attribute from inches, to +* a multiple of a small line width. +* o Wrapper for pgplot F77 subroutine PGQVSZ added. +* 30-JAN-2004 (DSB): +* o Added GCap +* o Renamed GAxScale as GScales +* 4-MAR-2011 (DSB): +* Added astGBBuf and astGEBuf. +*/ + +/* Macros */ +/* ====== */ +#define MXSTRLEN 80 /* String length at which truncation starts + within pgqtxt and pgptxt. */ + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ +#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */ +#include "c2f77.h" /* C to FORTRAN interface functions */ +#include "pointset.h" /* Defines AST__BAD */ +#include "memory.h" /* Memory allocation facilities */ +#include "error.h" /* Error reporting facilities */ +#include "grf.h" /* Interface to this module */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include + +/* Constants. */ +/* ========== */ +#define R2D 57.29578 /* Radians to degrees factor */ + +/* Function Prototypes. */ +/* ==================== */ +/* These define a local C interface to the PGPLOT library. */ +static void ccpgline(int n, float xpts[], float ypts[] ); +static void ccpgpt(int n, float xpts[], float ypts[], int symbol); +static void ccpgptxt(float x, float y, float angle, float fjust, char *text ); +static void ccpgqcf(int *cf); +static void ccpgqch(float *ch); +static void ccpgqci(int *ci); +static void ccpglen(int units, char *text, float *xl, float *yl); +static void ccpgqcs(int units, float *xch, float *ych); +static void ccpgqls(int *ls); +static void ccpgqlw(int *lw); +static void ccpgqtbg(int *tbci); +static void ccpgqtxt(float x, float y, float angle, float fjust, char *text, float xbox[], float ybox[]); +static void ccpgqvp(int units, float *x1, float *x2, float *y1, float *y2); +static void ccpgqvsz(int units, float *x1, float *x2, float *y1, float *y2); +static void ccpgqwin(float *x1, float *x2, float *y1, float *y2); +static void ccpgscf(int cf); +static void ccpgsch(float ch); +static void ccpgsci(int ci); +static void ccpgsls(int ls); +static void ccpgslw(int lw); +static void ccpgstbg(int tbci); +static void ccpgupdt( void ); +static void ccpgbbuf( void ); +static void ccpgebuf( void ); + +/* These describe the native Fortran interface to the PGPLOT library. The + macros used come from the "f77.h" include file. */ +F77_SUBROUTINE(pgline)( INTEGER(n), REAL_ARRAY(x), REAL_ARRAY(y) ); +F77_SUBROUTINE(pgpt)( INTEGER(n), REAL_ARRAY(x), REAL_ARRAY(y), INTEGER(TYPE) ); +F77_SUBROUTINE(pgptxt)( REAL(x), REAL(y), REAL(angle), REAL(fjust), CHARACTER(text) TRAIL(text) ); +F77_SUBROUTINE(pgqcf)( INTEGER(ival) ); +F77_SUBROUTINE(pgqch)( REAL(rval) ); +F77_SUBROUTINE(pgqci)( INTEGER(ival) ); +F77_SUBROUTINE(pgqcs)( INTEGER(units), REAL(chv), REAL(chh) ); +F77_SUBROUTINE(pglen)( INTEGER(units), CHARACTER(text), REAL(xl), REAL(yl) TRAIL(text) ); +F77_SUBROUTINE(pgqls)( INTEGER(ival) ); +F77_SUBROUTINE(pgqlw)( INTEGER(ival) ); +F77_SUBROUTINE(pgqtbg)( INTEGER(tbg) ); +F77_SUBROUTINE(pgqtxt)( REAL(x), REAL(y), REAL(angle), REAL(fjust), CHARACTER(text), REAL_ARRAY(xbox), REAL_ARRAY(ybox) TRAIL(text) ); +F77_SUBROUTINE(pgqvp)( INTEGER(units), REAL(vx1), REAL(vx2), REAL(vy1), REAL(vy2) ); +F77_SUBROUTINE(pgqvsz)( INTEGER(units), REAL(x1), REAL(x2), REAL(y1), REAL(y2) ); +F77_SUBROUTINE(pgqwin)( REAL(wx1), REAL(wx2), REAL(wy1), REAL(wy2) ); +F77_SUBROUTINE(pgscf)( INTEGER(ival) ); +F77_SUBROUTINE(pgsch)( REAL(rval) ); +F77_SUBROUTINE(pgsci)( INTEGER(ival) ); +F77_SUBROUTINE(pgsls)( INTEGER(ival) ); +F77_SUBROUTINE(pgslw)( INTEGER(ival) ); +F77_SUBROUTINE(pgstbg)( INTEGER(tbg) ); +F77_SUBROUTINE(pgupdt)( ); +F77_SUBROUTINE(pgbbuf)( ); +F77_SUBROUTINE(pgebuf)( ); + +/* Externally visible functions. */ +/* ============================= */ +/* These implement the "grf" interface in terms of the local C interface + to PGPLOT. */ +int astGBBuf( void ){ +/* +*+ +* Name: +* astGBBuf + +* Purpose: +* Start a new graphics buffering context. + +* Synopsis: +* #include "grf.h" +* int astGBBuf( void ) + +* Description: +* This function begins saving graphical output commands in an +* internal buffer; the commands are held until a matching astGEBuf +* call (or until the buffer is emptied by astGFlush). This can +* greatly improve the efficiency of some graphics systems. astGBBuf +* increments an internal counter, while astGEBuf decrements this +* counter and flushes the buffer to the output device when the +* counter drops to zero. astGBBuf and astGEBuf calls should always +* be paired. + +* Parameters: +* None. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*- +*/ + ccpgbbuf(); + return 1; +} + +int astGEBuf( void ){ +/* +*+ +* Name: +* astGEBuf + +* Purpose: +* End a graphics buffering context. + +* Synopsis: +* #include "grf.h" +* int astGEBuf( void ) + +* Description: +* This function marks the end of a batch of graphical output begun +* with the last call of astGBBuf. astGBBuf and astGEBUF calls should +* always be paired. Each call to astGBBuf increments a counter, while +* each call to astGEBuf decrements the counter. When the counter +* reaches 0, the batch of output is written on the output device. + +* Parameters: +* None. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*- +*/ + ccpgebuf(); + return 1; +} + +int astGFlush( void ){ +/* +*+ +* Name: +* astGFlush + +* Purpose: +* Flush all pending graphics to the output device. + +* Synopsis: +* #include "grf.h" +* int astGFlush( void ) + +* Description: +* This function ensures that the display device is up-to-date, +* by flushing any pending graphics to the output device. + +* Parameters: +* None. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*- +*/ + + ccpgupdt(); + return 1; +} + +int astGCap( int cap, int value ){ +/* +*+ +* Name: +* astGCap + +* Purpose: +* Indicate if this grf module has a given capability. + +* Synopsis: +* #include "grf.h" +* int astGCap( int cap, int value ) + +* Description: +* This function is called by the AST Plot class to determine if the +* grf module has a given capability, as indicated by the "cap" +* argument. + +* Parameters: +* cap +* The capability being inquired about. This will be one of the +* following constants defined in grf.h: +* +* GRF__SCALES: This function should return a non-zero value if +* it implements the astGScales function, and zero otherwise. The +* supplied "value" argument should be ignored. +* +* GRF__MJUST: This function should return a non-zero value if +* the astGText and astGTxExt functions recognise "M" as a +* character in the justification string. If the first character of +* a justification string is "M", then the text should be justified +* with the given reference point at the bottom of the bounding box. +* This is different to "B" justification, which requests that the +* reference point be put on the baseline of the text, since some +* characters hang down below the baseline. If the astGText or +* astGTxExt function cannot differentiate between "M" and "B", +* then this function should return zero, in which case "M" +* justification will never be requested by Plot. The supplied +* "value" argument should be ignored. +* +* GRF__ESC: This function should return a non-zero value if the +* astGText and astGTxExt functions can recognise and interpret +* graphics escape sequences within the supplied string. These +* escape sequences are described below. Zero should be returned +* if escape sequences cannot be interpreted (in which case the +* Plot class will interpret them itself if needed). The supplied +* "value" argument should be ignored only if escape sequences cannot +* be interpreted by astGText and astGTxExt. Otherwise, "value" +* indicates whether astGText and astGTxExt should interpret escape +* sequences in subsequent calls. If "value" is non-zero then +* escape sequences should be interpreted by astGText and +* astGTxExt. Otherwise, they should be drawn as literal text. + +* Returned Value: +* The return value, as described above. Zero should be returned if +* the supplied capability is not recognised. + +* Escape Sequences: +* Escape sequences are introduced into the text string by a percent +* "%" character. The following escape sequences are currently recognised +* ("..." represents a string of one or more decimal digits): +* +* %% - Print a literal "%" character (type GRF__ESPER ). +* +* %^...+ - Draw subsequent characters as super-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the super-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF__ESSUP ). +* %^+ - Draw subsequent characters with the normal base-line. +* +* %v...+ - Draw subsequent characters as sub-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the sub-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF__ESSUB ). +* +* %v+ - Draw subsequent characters with the normal base-line +* (equivalent to %^+). +* +* %>...+ - Leave a gap before drawing subsequent characters. +* The digits "..." give the size of the gap, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF__ESGAP ). +* +* %<...+ - Move backwards before drawing subsequent characters. +* The digits "..." give the size of the movement, scaled +* so that a value of "100" corresponds to the height of +* "normal" text (type GRF_ESBAC). +* +* %s...+ - Change the Size attribute for subsequent characters. The +* digits "..." give the new Size as a fraction of the +* "normal" Size, scaled so that a value of "100" corresponds +* to 1.0 (type GRF__ESSIZ ). +* +* %s+ - Reset the Size attribute to its "normal" value. +* +* %w...+ - Change the Width attribute for subsequent characters. The +* digits "..." give the new width as a fraction of the +* "normal" Width, scaled so that a value of "100" corresponds +* to 1.0 (type GRF__ESWID ). +* +* %w+ - Reset the Size attribute to its "normal" value. +* +* %f...+ - Change the Font attribute for subsequent characters. The +* digits "..." give the new Font value (type GRF__ESFON ). +* +* %f+ - Reset the Font attribute to its "normal" value. +* +* %c...+ - Change the Colour attribute for subsequent characters. The +* digits "..." give the new Colour value (type GRF__ESCOL ). +* +* %c+ - Reset the Colour attribute to its "normal" value. +* +* %t...+ - Change the Style attribute for subsequent characters. The +* digits "..." give the new Style value (type GRF__ESSTY ). +* +* %t+ - Reset the Style attribute to its "normal" value. +* +* %- - Push the current graphics attribute values onto the top of +* the stack - see "%+" (type GRF__ESPSH). +* +* %+ - Pop attributes values of the top the stack - see "%-". If +* the stack is empty, "normal" attribute values are restored +* (type GRF__ESPOP). +* +* The astFindEscape function (in libast.a) can be used to locate escape +* sequences within a text string. It has the following signature: +* +* #include "plot.h" +* int astFindEscape( const char *text, int *type, int *value, int *nc ) +* +* Parameters: +* text +* Pointer to the string to be checked. +* type +* Pointer to a location at which to return the type of escape +* sequence. Each type is identified by a symbolic constant defined +* in grf.h and is indicated in the above section. The returned value +* is undefined if the supplied text does not begin with an escape +* sequence. +* value +* Pointer to a lcation at which to return the integer value +* associated with the escape sequence. All usable values will be +* positive. Zero is returned if the escape sequence has no associated +* integer. A value of -1 indicates that the attribute identified by +* "type" should be reset to its "normal" value (as established using +* the astGAttr function, etc). The returned value is undefined if +* the supplied text does not begin with an escape sequence. +* nc +* Pointer to a location at which to return the number of +* characters read by this call. If the text starts with an escape +* sequence, the returned value will be the number of characters in +* the escape sequence. Otherwise, the returned value will be the +* number of characters prior to the first escape sequence, or the +* length of the supplied text if no escape sequence is found. + +* Returned Value: +* A non-zero value is returned if the supplied text starts with a +* graphics escape sequence, and zero is returned otherwise. + +*- +*/ + + int result = 0; + if( cap == GRF__SCALES ) result = 1; + return result; +} + +int astGLine( int n, const float *x, const float *y ){ +/* +*+ +* Name: +* astGLine + +* Purpose: +* Draw a polyline (i.e. a set of connected lines). + +* Synopsis: +* #include "grf.h" +* int astGLine( int n, const float *x, const float *y ) + +* Description: +* This function displays lines joining the given positions. + +* Parameters: +* n +* The number of positions to be joined together. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Nothing is done if "n" is less than 2, or if a NULL pointer is +* given for either "x" or "y". + +*- +*/ + + if( n > 1 && x && y ) ccpgline( n, (float *) x, (float *) y ); + return 1; +} + +int astGMark( int n, const float *x, const float *y, int type ){ +/* +*+ +* Name: +* astGMark + +* Purpose: +* Draw a set of markers. + +* Synopsis: +* #include "grf.h" +* int astGMark( int n, const float *x, const float *y, int type ) + +* Description: +* This function displays markers at the given positions. + +* Parameters: +* n +* The number of markers to draw. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* type +* An integer which can be used to indicate the type of marker symbol +* required. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Nothing is done if "n" is less than 1, or if a NULL pointer is +* given for either "x" or "y". + +*- +*/ + + if( n > 0 && x && y ) ccpgpt( n, (float *) x, (float *) y, type ); + return 1; +} + +int astGText( const char *text, float x, float y, const char *just, + float upx, float upy ){ +/* +*+ +* Name: +* astGText + +* Purpose: +* Draw a character string. + +* Synopsis: +* #include "grf.h" +* int astGText( const char *text, float x, float y, const char *just, +* float upx, float upy ) + +* Description: +* This function displays a character string at a given position +* using a specified justification and up-vector. + +* Parameters: +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* upx +* The x component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* left to right on the screen. +* upy +* The y component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* bottom to top on the screen. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - Any graphics within the rotated box enclosing the text are erased. +* - A NULL value for "just" causes a value of "CC" to be used. +* - Both "upx" and "upy" being zero causes an error. +* - Any unrecognised character in "just" causes an error. +*- +*/ + +/* Local Variables: */ + char lj[ 2 ]; + float uplen, xbox[ 4 ], ybox[ 4 ]; + float angle, fjust, hu, test, alpha, beta; + int i, tbg; + +/* Check that there is something to draw. */ + if( text && text[ 0 ] != 0 ){ + +/* Fill in any missing parts of the justification string. */ + if( just ){ + if( just[ 0 ] == 'T' || just[ 0 ] == 'C' || just[ 0 ] == 'B' ){ + lj[ 0 ] = just[ 0 ]; + } else { + astError( AST__GRFER, "astGText: Justification string '%s' is " + "invalid.", just ); + return 0; + } + + if( just[ 1 ] == 'L' || just[ 1 ] == 'C' || just[ 1 ] == 'R' ){ + lj[ 1 ] = just[ 1 ]; + } else { + astError( AST__GRFER, "astGText: Justification string '%s' " + "is invalid.", just ); + return 0; + } + + } else { + lj[ 0 ] = 'C'; + lj[ 1 ] = 'C'; + } + +/* Find the conversion factors between increment in world coordinate axes, + and the corresponding increments in millimetres ( Xmm = alpha*Xworld, + Ymm = beta*Yworld ). */ + if( !astGScales( &alpha, &beta ) ) return 0; + +/* If either axis is reversed, reverse the supplied up-vector components + so that they refer to the world-coordinates axes. */ + if( alpha < 0.0 ) upx = -upx; + if( beta < 0.0 ) upy = -upy; + +/* Get the angle between the text base-line and horizontal. */ + angle = atan2( -(double) upx*alpha, (double) upy*beta )*R2D; + +/* Get the fractional horizontal justification as needed by PGPLOT. */ + if( lj[ 1 ] == 'L' ) { + fjust = 0.0; + } else if( lj[ 1 ] == 'R' ) { + fjust = 1.0; + } else { + fjust = 0.5; + } + +/* Unless the requested justification is "Bottom", we need to adjust + the supplied reference position before we use it with PGPLOT because + PGPLOT assumes "Bottom" justification. */ + if( lj[0] != 'B' ) { + +/* Get the bounding box of the string. Note, only the size of the box is + significant here, not its position. Also note, leading and trailing + spaces are not included in the bounding box. */ + ccpgqtxt( x, y, angle, fjust, (char *) text, xbox, ybox ); + +/* Normalise the up-vector in world coordinates. */ + uplen = sqrt( (double) (upx*upx + upy*upy) ); + if( uplen > 0.0 ){ + upx /= uplen; + upy /= uplen; + } else { + astError( AST__GRFER, "astGText: Zero length up-vector supplied."); + return 0; + } + +/* Find the height of the text above the base-line. Note, the PGPLOT + manual is not clear about the order of the corners returned by + pgqtxt, so we have to find the largest distance between + the corners in the direction of the supplied up-vector. */ + hu = 0.0; + for( i = 0; i < 4; i++ ){ + test = upx*( xbox[ i ] - x ) + upy*( ybox[ i ] - y ); + if( test > hu ) hu = test; + } + +/* Adjust the vertical position of the reference point, since PGPLOT + requires it to be at the bottom of the text. */ + if( lj[ 0 ] == 'T' ){ + x -= upx*hu; + y -= upy*hu; + } else if( lj[ 0 ] == 'C' ){ + x -= 0.5*upx*hu; + y -= 0.5*upy*hu; + } + } + +/* Display the text, erasing any graphics. */ + ccpgqtbg( &tbg ); + ccpgstbg( 0 ); + ccpgptxt( x, y, angle, fjust, (char *) text ); + ccpgstbg( tbg ); + } + +/* Return. */ + return 1; +} + +int astGScales( float *alpha, float *beta ){ +/* +*+ +* Name: +* astGScales + +* Purpose: +* Get the axis scales. + +* Synopsis: +* #include "grf.h" +* int astGScales( float *alpha, float *beta ) + +* Description: +* This function returns two values (one for each axis) which scale +* increments on the corresponding axis into a "normal" coordinate +* system in which: +* 1 - The axes have equal scale in terms of (for instance) +* millimetres per unit distance. +* 2 - X values increase from left to right. +* 3 - Y values increase from bottom to top. + +* Parameters: +* alpha +* A pointer to the location at which to return the scale for the +* X axis (i.e. Xnorm = alpha*Xworld). +* beta +* A pointer to the location at which to return the scale for the +* Y axis (i.e. Ynorm = beta*Yworld). + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*- +*/ + +/* Local Variables: */ + float nx1, nx2, ny1, ny2, wx1, wx2, wy1, wy2; + int ret; + +/* Find the conversion factors between increment in world coordinate axes, + and the corresponding increments in millimetres ( Xmm = alpha*Xworld, + Ymm = beta*Yworld ). */ + ccpgqvp( 2, &nx1, &nx2, &ny1, &ny2 ); + ccpgqwin( &wx1, &wx2, &wy1, &wy2 ); + + if( wx2 != wx1 && wy2 != wy1 && + nx2 != nx1 && ny2 != ny1 ) { + *alpha= ( nx2 - nx1 ) / ( wx2 - wx1 ); + *beta = ( ny2 - ny1 ) / ( wy2 - wy1 ); + ret = 1; + } else { + astError( AST__GRFER, "astGScales: The graphics window or viewport has zero size." ); + ret = 0; + } + + return ret; +} + +int astGTxExt( const char *text, float x, float y, const char *just, + float upx, float upy, float *xb, float *yb ){ +/* +*+ +* Name: +* astGTxExt + +* Purpose: +* Get the extent of a character string. + +* Synopsis: +* #include "grf.h" +* int astGTxExt( const char *text, float x, float y, const char *just, +* float upx, float upy, float *xb, float *yb ) + +* Description: +* This function returns the corners of a box which would enclose the +* supplied character string if it were displayed using astGText. +* +* The returned box INCLUDES any leading or trailing spaces. + +* Parameters: +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* upx +* The x component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* left to right on the screen. +* upy +* The y component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* bottom to top on the screen. +* xb +* An array of 4 elements in which to return the x coordinate of +* each corner of the bounding box. +* yb +* An array of 4 elements in which to return the y coordinate of +* each corner of the bounding box. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: +* - The order of the corners is anti-clockwise (in world coordinates) +* starting at the bottom left. +* - A NULL value for "just" causes a value of "CC" to be used. +* - Both "upx" and "upy" being zero causes an error. +* - Any unrecognised character in "just" causes an error. +* - Zero is returned for all bounds of the box if an error occurs. + +*- +*/ + +/* Local Variables: */ + char lj[ 2 ]; + float udx, udy, vdx, vdy, vx, vy, uplen, xbox[ 4 ], + ybox[ 4 ], uxu, uyu, uxd, uyd, ux, uy; + float angle, width, test, xl, yl; + float alpha, beta, xc, yc, hu, hd, a, b; + int i; + +/* Initialise the returned values to indicate no box available. */ + for( i = 0; i < 4; i++ ){ + xb[ i ] = 0.0; + yb[ i ] = 0.0; + } + +/* Check that there is something to draw. */ + if( text && text[ 0 ] != 0 ){ + +/* Fill in any missing parts of the justification string. */ + if( just ){ + if( just[ 0 ] == 'T' || just[ 0 ] == 'C' || just[ 0 ] == 'B' ){ + lj[ 0 ] = just[ 0 ]; + } else { + astError( AST__GRFER, "astGTxExt: Justification string '%s' is " + "invalid.", just ); + return 0; + } + + if( just[ 1 ] == 'L' || just[ 1 ] == 'C' || just[ 1 ] == 'R' ){ + lj[ 1 ] = just[ 1 ]; + } else { + astError( AST__GRFER, "astGTxExt: Justification string '%s' is " + "invalid.", just ); + return 0; + } + + } else { + lj[ 0 ] = 'C'; + lj[ 1 ] = 'C'; + } + +/* Find the conversion factors between increment in world coordinate axes, + and the corresponding increments in millimetres ( Xmm = alpha*Xworld, + Ymm = beta*Yworld ). */ + if( !astGScales( &alpha, &beta ) ) return 0; + +/* If either axis is reversed, reverse the supplied up-vector components + so that they refer to the world-coordinates axes. */ + if( alpha < 0.0 ) upx = -upx; + if( beta < 0.0 ) upy = -upy; + +/* Convert the up-vector into millimetres. */ + ux = alpha*upx; + uy = beta*upy; + +/* Normalise the up-vector to a length of 1 millimetre. */ + uplen = sqrt( (double) (ux*ux + uy*uy) ); + if( uplen > 0.0 ){ + ux /= uplen; + uy /= uplen; + } else { + astError( AST__GRFER, "astGText: Zero length up-vector supplied."); + return 0; + } + +/* Form the base-line vector by rotating the up-vector by 90 degrees + clockwise. */ + vx = uy; + vy = -ux; + +/* Get the angle between the text base-line and horizontal. */ + angle = atan2( (double) vy, (double) vx )*R2D; + +/* Get the bounding box of the string drawn with its bottom left corner + at the origin. */ + ccpgqtxt( 0.0, 0.0, angle, 0.0, (char *) text, xbox, ybox ); + +/* Convert the returned bounding box world coordinates into millimetres. */ + for( i = 0; i < 4; i++ ){ + xbox[ i ] *= alpha; + ybox[ i ] *= beta; + } + +/* Find the height of the bounding box, in millimetres. Note, + the PGPLOT manual is not clear about the order of the corners + returned by pgqtxt, so we have to find the largest distance between + the corners in the direction of the supplied up-vector. The reference + point is on the text base-line which is not usually at the bottom of + the bounding box (some letters - like "y" - extend below the base-line). + Find the distance from the base-line to the top (hu) and bottom (hd) + of the bounding box. */ + hu = -FLT_MAX; + hd = FLT_MAX; + for( i = 0; i < 4; i++ ){ + test = ux*xbox[ i ] + uy*ybox[ i ]; + if( test > hu ) hu = test; + if( test < hd ) hd = test; + } + +/* Get an up and a down vector scaled to the height/depth of the + bounding box above/below the text base-line . */ + uxu = ux*hu; + uyu = uy*hu; + uxd = ux*hd; + uyd = uy*hd; + +/* The bounding box returned by pgqtxt does not include any leading or + trailing spaces. We need to include such spaces in the returned box. + To do this we get the length of the text string in millimetres + using pglen instead of using the bounding box returned by pgqtxt. */ + ccpglen( 2, (char *) text, &xl, &yl ); + +/* The abolute width of the string in millimetres may depend on the + up-vector. The values returned by pglen are for horizontal and + vertical text. Find the width using the supplied up-vector. */ + a = uy*xl; + b = ux*yl; + width = sqrt( a*a + b*b ); + +/* The pglen function returns a value which is slightly smaller than + the area cleared to hold the text when written using pgptxt. Increase + the text width so that it is about equal to the area cleared. */ + width += 0.2*hu; + +/* Scale the base-line vector so that its length is equal to the width + of the bounding box (including spaces). */ + vx *= width; + vy *= width; + +/* Convert the base-line vector back into world coordinates. */ + vx /= alpha; + vy /= beta; + +/* Convert the up and down vectors into world coordinates. */ + uxu /= alpha; + uyu /= beta; + uxd /= alpha; + uyd /= beta; + +/* Find the coordinates at the centre of the bounding box in world + coordinates. */ + xc = x; + yc = y; + + if( lj[0] == 'B' ) { + xc += 0.5*uxu; + yc += 0.5*uyu; + } else if( lj[0] == 'T' ) { + xc -= 0.5*uxu; + yc -= 0.5*uyu; + } + + if( lj[1] == 'L' ) { + xc += 0.5*vx; + yc += 0.5*vy; + } else if( lj[1] == 'R' ) { + xc -= 0.5*vx; + yc -= 0.5*vy; + } + +/* Get the corners of the bounding box. */ + vdx = 0.5*vx; + vdy = 0.5*vy; + udx = 0.5*uxu; + udy = 0.5*uyu; + +/* Bottom left corner... */ + xb[ 0 ] = xc - vdx - udx + uxd; + yb[ 0 ] = yc - vdy - udy + uyd; + +/* Bottom right corner... */ + xb[ 1 ] = xc + vdx - udx + uxd; + yb[ 1 ] = yc + vdy - udy + uyd; + +/* Top right corner... */ + xb[ 2 ] = xc + vdx + udx; + yb[ 2 ] = yc + vdy + udy; + +/* Top left corner... */ + xb[ 3 ] = xc - vdx + udx; + yb[ 3 ] = yc - vdy + udy; + + } + +/* Return. */ + return 1; +} + +int astGQch( float *chv, float *chh ){ +/* +*+ +* Name: +* astGQch + +* Purpose: +* Return the character height in world coordinates. + +* Synopsis: +* #include "grf.h" +* int astGQch( float *chv, float *chh ) + +* Description: +* This function returns the heights of characters drawn vertically and +* horizontally in world coordinates. + +* Parameters: +* chv +* A pointer to the double which is to receive the height of +* characters drawn with a vertical baseline . This will be an +* increment in the X axis. +* chh +* A pointer to the double which is to receive the height of +* characters drawn with a horizontal baseline. This will be an +* increment in the Y axis. + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +*- +*/ + +/* Local Variables: */ + float vx1,vx2,vy1,vy2,wx1,wx2,wy1,wy2; + +/* Get the character height in normalised device coordinates */ + ccpgqcs( 0, chv, chh ); + +/* Get the bounds of the PGPLOT viewport in normalised device + coordinates. */ + ccpgqvp( 0, &vx1, &vx2, &vy1, &vy2 ); + +/* Get the bounds of the PGPLOT window in world coordinates. */ + ccpgqwin( &wx1, &wx2, &wy1, &wy2 ); + +/* Convert the text height from normalised device coordinates into world + coordinates for vertical text. Print an error message if the viewport + has zero size. */ + if( vx1 != vx2 ){ + *chv *= ( wx2 - wx1 )/( vx2 - vx1 ); + + } else { + astError( AST__GRFER, "astGQch: The graphics viewport has zero size " + "in the X direction."); + return 0; + } + +/* Convert the text height from normalised device coordinates into world + coordinates for horizontal text. Print an error message if the viewport + has zero size. */ + if( vy1 != vy2 ){ + *chh *= ( wy2 - wy1 )/( vy2 - vy1 ); + } else { + astError( AST__GRFER, "astGQch: The graphics viewport has zero size " + "in the Y direction."); + return 0; + } + +/* Return. */ + return 1; +} + +int astGAttr( int attr, double value, double *old_value, int prim ){ +/* +*+ +* Name: +* astGAttr + +* Purpose: +* Enquire or set a graphics attribute value. + +* Synopsis: +* #include "grf.h" +* int int astGAttr( int attr, double value, double *old_value, int prim ) + +* Description: +* This function returns the current value of a specified graphics +* attribute, and optionally establishes a new value. The supplied +* value is converted to an integer value if necessary before use. + +* Parameters: +* attr +* An integer value identifying the required attribute. The +* following symbolic values are defined in grf.h: +* +* GRF__STYLE - Line style. +* GRF__WIDTH - Line width. +* GRF__SIZE - Character and marker size scale factor. +* GRF__FONT - Character font. +* GRF__COLOUR - Colour index. +* value +* A new value to store for the attribute. If this is AST__BAD +* no value is stored. +* old_value +* A pointer to a double in which to return the attribute value. +* If this is NULL, no value is returned. +* prim +* The sort of graphics primitive to be drawn with the new attribute. +* Identified by the following values defined in grf.h: +* GRF__LINE +* GRF__MARK +* GRF__TEXT + +* Returned Value: +* A value of 0 is returned if an error occurs, and 1 is returned +* otherwise. + +* Notes: + +*- +*/ + + int ival; + float rval, dx, dy, deflw, x1, x2, y1, y2; + +/* If required retrieve the current line style, and set a new line style. */ + if( attr == GRF__STYLE ){ + ccpgqls( &ival ); + if( old_value ) *old_value = (double) ival; + + if( value != AST__BAD ){ + ival = (int) ( value + 0.5 ); + if( value < 0.0 ) ival -= 1; + + ival = ( ival - 1 ) % 5; + ival += ( ival < 0 ) ? 6 : 1; + + ccpgsls( ival ); + } + +/* If required retrieve the current line width, and set a new line width. + Line width is stored in Plot as a scale factor (1.0 for the default line + width which is a fixed fraction of the diagonal of the view surface), but + pgplot stores it in units of 0.005 of an inch. */ + } else if( attr == GRF__WIDTH ){ + +/* Get the bounds of the view surface in inches. */ + ccpgqvsz( 1, &x1, &x2, &y1, &y2 ); + +/* Find the default line width in inches (i.e. 0.0005 of the length + of the view surface diagonal). */ + dx = ( x1 - x2 ); + dy = ( y1 - y2 ); + deflw = 0.0005*sqrt( (double )( dx*dx + dy*dy ) ); + +/* Get the current pgplot line width in units of 0.005 of an inch. */ + ccpgqlw( &ival ); + +/* If required, return the factor by which this exceeds the default line + width found above. */ + if( old_value ) *old_value = (double)( ival )/( 200.0 * deflw ); + +/* If a new line width has been provided, the pgplot line width needs to + be set to the corresponding absolute value. */ + if( value != AST__BAD ){ + ival = (int) ( 200.0*value*deflw ); + if( ival < 1 ) { + ival = 1; + } else if( ival > 201 ){ + ival = 201; + } + ccpgslw( ival ); + } + +/* If required retrieve the current character size, and set a new size. + The attribute value should be a factor by which to multiply the + default character size. */ + } else if( attr == GRF__SIZE ){ + ccpgqch( &rval ); + if( old_value ) *old_value = (double) rval; + + if( value != AST__BAD ){ + ccpgsch( (float) value ); + } + +/* If required retrieve the current character font, and set a new font. */ + } else if( attr == GRF__FONT ){ + ccpgqcf( &ival ); + if( old_value ) *old_value = (double) ival; + + if( value != AST__BAD ){ + ival = (int) ( value + 0.5 ); + if( value < 0.0 ) ival -= 1; + + ival = ( ival - 1 ) % 4; + ival += ( ival < 0 ) ? 5 : 1; + ccpgscf( ival ); + } + +/* If required retrieve the current colour index, and set a new colour + index. */ + } else if( attr == GRF__COLOUR ){ + ccpgqci( &ival ); + if( old_value ) *old_value = (double) ival; + + if( value != AST__BAD ){ + ival = (int) ( value + 0.5 ); + if( ival < 0 ) ival = 1; + ccpgsci( ival ); + } + +/* Give an error message for any other attribute value. */ + } else { + astError( AST__GRFER, "astGAttr: Unknown graphics attribute '%d' " + "requested.", attr ); + return 0; + } + +/* Return. */ + return 1; +} + +/* Local Functions. */ +/* ================ */ +/* These implement the local C interface to PGPLOT in terms of its + native Fortran interface. Only those PGPLOT functions used within + this module are included. */ +static void ccpgline(int n, float xpts[], float ypts[] ){ + F77_INTEGER_TYPE N; + F77_REAL_TYPE *XX; + F77_REAL_TYPE *YY; + int i; + + XX = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + YY = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + + if( astOK ){ + + for( i = 0; i < n; i++ ){ + XX[ i ] = (F77_REAL_TYPE) xpts[ i ]; + YY[ i ] = (F77_REAL_TYPE) ypts[ i ]; + } + + N = (F77_INTEGER_TYPE) n; + + F77_CALL(pgline)( INTEGER_ARG(&N), REAL_ARRAY_ARG(XX), + REAL_ARRAY_ARG(YY) ); + + XX = (F77_REAL_TYPE *) astFree( (void *) XX ); + YY = (F77_REAL_TYPE *) astFree( (void *) YY ); + } +} + +static void ccpgpt(int n, float xpts[], float ypts[], int symbol){ + F77_INTEGER_TYPE N; + F77_REAL_TYPE *XX; + F77_REAL_TYPE *YY; + F77_INTEGER_TYPE SYMBOL; + int i; + + XX = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + YY = (F77_REAL_TYPE *) astMalloc( sizeof( F77_REAL_TYPE )*(size_t) n ); + + if( astOK ){ + + for( i = 0; i < n; i++ ){ + XX[ i ] = (F77_REAL_TYPE) xpts[ i ]; + YY[ i ] = (F77_REAL_TYPE) ypts[ i ]; + } + + N = (F77_INTEGER_TYPE) n; + SYMBOL = (F77_INTEGER_TYPE) symbol; + + + F77_CALL(pgpt)( INTEGER_ARG(&N), REAL_ARRAY_ARG(XX), + REAL_ARRAY_ARG(YY), INTEGER_ARG(&SYMBOL) ); + + XX = (F77_REAL_TYPE *) astFree( (void *) XX ); + YY = (F77_REAL_TYPE *) astFree( (void *) YY ); + } +} + +static void ccpgptxt(float x, float y, float angle, float fjust, char *text ){ + F77_REAL_TYPE X; + F77_REAL_TYPE Y; + F77_REAL_TYPE ANGLE; + F77_REAL_TYPE FJUST; + DECLARE_CHARACTER(LTEXT,MXSTRLEN); + int ftext_length; + + X = (F77_REAL_TYPE) x; + Y = (F77_REAL_TYPE) y; + ANGLE = (F77_REAL_TYPE) angle; + FJUST = (F77_REAL_TYPE) fjust; + + ftext_length = strlen( text ); + if( ftext_length > LTEXT_length ) ftext_length = LTEXT_length; + astStringExport( text, LTEXT, ftext_length ); + + F77_CALL(pgptxt)( REAL_ARG(&X), REAL_ARG(&Y), REAL_ARG(&ANGLE), + REAL_ARG(&FJUST), CHARACTER_ARG(LTEXT) + TRAIL_ARG(ftext) ); +} + +static void ccpgqtxt(float x, float y, float angle, float fjust, char *text, + float xbox[], float ybox[]){ + F77_REAL_TYPE X; + F77_REAL_TYPE Y; + F77_REAL_TYPE ANGLE; + F77_REAL_TYPE FJUST; + DECLARE_CHARACTER(LTEXT,MXSTRLEN); + F77_REAL_TYPE XBOX[ 4 ]; + F77_REAL_TYPE YBOX[ 4 ]; + int i; + int ftext_length; + + X = (F77_REAL_TYPE) x; + Y = (F77_REAL_TYPE) y; + ANGLE = (F77_REAL_TYPE) angle; + FJUST = (F77_REAL_TYPE) fjust; + + ftext_length = strlen( text ); + if( ftext_length > LTEXT_length ) ftext_length = LTEXT_length; + astStringExport( text, LTEXT, ftext_length ); + + F77_CALL(pgqtxt)( REAL_ARG(&X), REAL_ARG(&Y), REAL_ARG(&ANGLE), + REAL_ARG(&FJUST), CHARACTER_ARG(LTEXT), + REAL_ARRAY_ARG(XBOX), REAL_ARRAY_ARG(YBOX) + TRAIL_ARG(ftext) ); + + for( i = 0; i < 4; i++ ){ + xbox[ i ] = (float) XBOX[ i ]; + ybox[ i ] = (float) YBOX[ i ]; + } + +} + +static void ccpgqtbg(int *tbci){ + F77_INTEGER_TYPE TBCI; + F77_CALL(pgqtbg)( INTEGER_ARG(&TBCI) ); + *tbci = (int) TBCI; +} + +static void ccpgstbg(int tbci){ + F77_INTEGER_TYPE TBCI; + TBCI = (F77_INTEGER_TYPE) tbci; + F77_CALL(pgstbg)( INTEGER_ARG(&TBCI) ); +} + +static void ccpgqcs(int units, float *xch, float *ych){ + F77_INTEGER_TYPE UNITS; + F77_REAL_TYPE XCH; + F77_REAL_TYPE YCH; + UNITS = (F77_INTEGER_TYPE) units; + + F77_CALL(pgqcs)( INTEGER_ARG(&UNITS), REAL_ARG(&XCH), REAL_ARG(&YCH) ); + + *xch = (float) XCH; + *ych = (float) YCH; +} + +static void ccpglen(int units, char *text, float *xl, float *yl ){ + F77_INTEGER_TYPE UNITS; + F77_REAL_TYPE XL; + F77_REAL_TYPE YL; + DECLARE_CHARACTER(LTEXT,MXSTRLEN); + int ftext_length; + + UNITS = (F77_INTEGER_TYPE) units; + + + ftext_length = strlen( text ); + if( ftext_length > LTEXT_length ) ftext_length = LTEXT_length; + astStringExport( text, LTEXT, ftext_length ); + + F77_CALL(pglen)( INTEGER_ARG(&UNITS), CHARACTER_ARG(LTEXT), + REAL_ARG(&XL), REAL_ARG(&YL) TRAIL_ARG(ftext) ); + + *xl = (float) XL; + *yl = (float) YL; +} + +static void ccpgqvp(int units, float *x1, float *x2, float *y1, float *y2){ + F77_INTEGER_TYPE UNITS; + F77_REAL_TYPE X1; + F77_REAL_TYPE X2; + F77_REAL_TYPE Y1; + F77_REAL_TYPE Y2; + + UNITS = (F77_INTEGER_TYPE) units; + F77_CALL(pgqvp)( INTEGER_ARG(&UNITS), REAL_ARG(&X1), REAL_ARG(&X2), + REAL_ARG(&Y1), REAL_ARG(&Y2) ); + *x1 = (float) X1; + *x2 = (float) X2; + *y1 = (float) Y1; + *y2 = (float) Y2; +} + +static void ccpgqvsz(int units, float *x1, float *x2, float *y1, float *y2){ + F77_INTEGER_TYPE UNITS; + F77_REAL_TYPE X1; + F77_REAL_TYPE X2; + F77_REAL_TYPE Y1; + F77_REAL_TYPE Y2; + + UNITS = (F77_INTEGER_TYPE) units; + F77_CALL(pgqvsz)( INTEGER_ARG(&UNITS), REAL_ARG(&X1), REAL_ARG(&X2), + REAL_ARG(&Y1), REAL_ARG(&Y2) ); + *x1 = (float) X1; + *x2 = (float) X2; + *y1 = (float) Y1; + *y2 = (float) Y2; +} + +static void ccpgqwin(float *x1, float *x2, float *y1, float *y2){ + F77_REAL_TYPE X1; + F77_REAL_TYPE X2; + F77_REAL_TYPE Y1; + F77_REAL_TYPE Y2; + + F77_CALL(pgqwin)( REAL_ARG(&X1), REAL_ARG(&X2), REAL_ARG(&Y1), + REAL_ARG(&Y2) ); + *x1 = (float) X1; + *x2 = (float) X2; + *y1 = (float) Y1; + *y2 = (float) Y2; +} + +static void ccpgqls(int *ls){ + F77_INTEGER_TYPE LS; + F77_CALL(pgqls)( INTEGER_ARG(&LS) ); + *ls = (int) LS; +} + +static void ccpgsls(int ls){ + F77_INTEGER_TYPE LS; + LS = (F77_INTEGER_TYPE) ls; + F77_CALL(pgsls)( INTEGER_ARG(&LS) ); +} + +static void ccpgqlw(int *lw){ + F77_INTEGER_TYPE LW; + F77_CALL(pgqlw)( INTEGER_ARG(&LW) ); + *lw = (int) LW; +} + +static void ccpgslw(int lw){ + F77_INTEGER_TYPE LW; + LW = (F77_INTEGER_TYPE) lw; + F77_CALL(pgslw)( INTEGER_ARG(&LW) ); +} + +static void ccpgqch(float *ch){ + F77_REAL_TYPE CH; + F77_CALL(pgqch)( REAL_ARG(&CH) ); + *ch = (float) CH; +} + +static void ccpgsch(float ch){ + F77_REAL_TYPE CH; + CH = (F77_REAL_TYPE) ch; + F77_CALL(pgsch)( REAL_ARG(&CH) ); +} + +static void ccpgqcf(int *cf){ + F77_INTEGER_TYPE CF; + F77_CALL(pgqcf)( INTEGER_ARG(&CF) ); + *cf = (int) CF; +} + +static void ccpgscf(int cf){ + F77_INTEGER_TYPE CF; + CF = (F77_INTEGER_TYPE) cf; + F77_CALL(pgscf)( INTEGER_ARG(&CF) ); +} + +static void ccpgqci(int *ci){ + F77_INTEGER_TYPE CI; + F77_CALL(pgqci)( INTEGER_ARG(&CI) ); + *ci = (int) CI; +} + +static void ccpgsci(int ci){ + F77_INTEGER_TYPE CI; + CI = (F77_INTEGER_TYPE) ci; + F77_CALL(pgsci)( INTEGER_ARG(&ci) ); +} + +static void ccpgupdt( void ){ + F77_CALL(pgupdt)(); +} + +static void ccpgbbuf( void ){ + F77_CALL(pgbbuf)(); +} + +static void ccpgebuf( void ){ + F77_CALL(pgebuf)(); +} diff --git a/gridplot.pdf b/gridplot.pdf new file mode 100644 index 0000000..1ba8733 Binary files /dev/null and b/gridplot.pdf differ diff --git a/gridplot_bw.pdf b/gridplot_bw.pdf new file mode 100644 index 0000000..c480928 Binary files /dev/null and b/gridplot_bw.pdf differ diff --git a/grismmap.c b/grismmap.c new file mode 100644 index 0000000..dfdd10b --- /dev/null +++ b/grismmap.c @@ -0,0 +1,2596 @@ +/* +*class++ +* Name: +* GrismMap + +* Purpose: +* Transform 1-dimensional coordinates using a grism dispersion equation. + +* Constructor Function: +c astGrismMap +f AST_GRISMMAP + +* Description: +* A GrismMap is a specialised form of Mapping which transforms +* 1-dimensional coordinates using the spectral dispersion equation +* described in FITS-WCS paper III "Representation of spectral +* coordinates in FITS". This describes the dispersion produced by +* gratings, prisms and grisms. +* +* When initially created, the forward transformation of a GrismMap +* transforms input "grism parameter" values into output wavelength +* values. The "grism parameter" is a dimensionless value which is +* linearly related to position on the detector. It is defined in FITS-WCS +* paper III as "the offset on the detector from the point of intersection +* of the camera axis, measured in units of the effective local length". +* The units in which wavelength values are expected or returned is +* determined by the values supplied for the GrismWaveR, GrismNRP and +* GrismG attribute: whatever units are used for these attributes will +* also be used for the wavelength values. + +* Inheritance: +* The GrismMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* GrismMap also has the following attributes: +* +* - GrismNR: The refractive index at the reference wavelength +* - GrismNRP: Rate of change of refractive index with wavelength +* - GrismWaveR: The reference wavelength +* - GrismAlpha: The angle of incidence of the incoming light +* - GrismG: The grating ruling density +* - GrismM: The interference order +* - GrismEps: The angle between the normal and the dispersion plane +* - GrismTheta: Angle between normal to detector plane and reference ray + +* Functions: +c The GrismMap class does not define any new functions beyond those +f The GrismMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 18-JUN-2003 (DSB): +* Original version. +* 10-MAY-2006 (DSB): +* Override astEqual. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS GrismMap + + +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear an attribute value for a GrismMap. + +* Type: +* Private macro. + +* Synopsis: +* #include "grismmap.h" +* MAKE_CLEAR(class,attribute,component,assign) + +* Class Membership: +* Defined by the GrismMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstGrismMap *this ) +* +* and an external interface function of the form: +* +* void astClear_( AstGrismMap *this ) +* +* which implement a method for clearing a specified attribute value for +* a class. The derived constants stored in the GrismMap structure are +* updated after the attribute has been cleared. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. Label in "astClearLabel"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(class,attribute,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attribute( Ast##class *this, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Report an error if the object has been cloned (i.e. has a reference \ + count that is greater than one). */ \ + if( astGetRefCount( this ) > 1 ) { \ + astError( AST__IMMUT, "astClear(%s): The " #attribute "attribute of " \ + "the supplied %s cannot be cleared because the %s has " \ + "been cloned (programming error).", status, \ + astGetClass(this), astGetClass(this), astGetClass(this) ); \ +\ +/* Otherwise, assign the "clear" value in the structure component. */ \ + } else { \ + this->component = (assign); \ + } \ +\ +/* Update the derived constants. */ \ + UpdateConstants( this, status ); \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attribute##_( Ast##class *this, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,class,Clear##attribute))( this, status ); \ +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set an attribute value for a GrismMap. + +* Type: +* Private macro. + +* Synopsis: +* #include "grismmap.h" +* astMAKE_SET(class,attribute,type,component,assign) + +* Class Membership: +* Defined by the GrismMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstGrismMap *this, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstGrismMap *this, value ) +* +* which implement a method for setting a specified attribute value for a +* GrismMap. The derived constants stored in the GrismMap structure are +* updated after the attribute has been cleared. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute to be set, as it appears in the function +* name (e.g. Label in "astSetLabel"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_SET(class,attribute,type,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attribute( Ast##class *this, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Report an error if the object has been cloned (i.e. has a reference \ + count that is greater than one). */ \ + if( astGetRefCount( this ) > 1 ) { \ + astError( AST__IMMUT, "astSet(%s): The " #attribute "attribute of " \ + "the supplied %s cannot be changed because the %s has " \ + "been cloned (programming error).", status, \ + astGetClass(this), astGetClass(this), astGetClass(this) ); \ +\ +/* Otherwise, store the new value in the structure component. */ \ + } else { \ + this->component = (assign); \ + } \ +\ +/* Update the derived constants. */ \ + UpdateConstants( this, status ); \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attribute##_( Ast##class *this, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,class,Set##attribute))( this, value, status ); \ +} + + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "unitmap.h" /* Unit Mappings */ +#include "channel.h" /* I/O channels */ +#include "zoommap.h" /* ZoomMap interface */ +#include "winmap.h" /* WinMap interface */ +#include "grismmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Macros which return the maximum and minimum of two values. */ +#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb)) +#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb)) + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(GrismMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(GrismMap,Class_Init) +#define class_vtab astGLOBAL(GrismMap,Class_Vtab) +#define getattrib_buff astGLOBAL(GrismMap,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstGrismMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstGrismMap *astGrismMapId_( const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static AstMapping *CanMerge( AstMapping *, int, AstMapping *, int, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void UpdateConstants( AstGrismMap *, int * ); + +static double GetGrismNR( AstGrismMap *, int * ); +static int TestGrismNR( AstGrismMap *, int * ); +static void ClearGrismNR( AstGrismMap *, int * ); +static void SetGrismNR( AstGrismMap *, double, int * ); + +static double GetGrismNRP( AstGrismMap *, int * ); +static int TestGrismNRP( AstGrismMap *, int * ); +static void ClearGrismNRP( AstGrismMap *, int * ); +static void SetGrismNRP( AstGrismMap *, double, int * ); + +static double GetGrismWaveR( AstGrismMap *, int * ); +static int TestGrismWaveR( AstGrismMap *, int * ); +static void ClearGrismWaveR( AstGrismMap *, int * ); +static void SetGrismWaveR( AstGrismMap *, double, int * ); + +static double GetGrismAlpha( AstGrismMap *, int * ); +static int TestGrismAlpha( AstGrismMap *, int * ); +static void ClearGrismAlpha( AstGrismMap *, int * ); +static void SetGrismAlpha( AstGrismMap *, double, int * ); + +static double GetGrismG( AstGrismMap *, int * ); +static int TestGrismG( AstGrismMap *, int * ); +static void ClearGrismG( AstGrismMap *, int * ); +static void SetGrismG( AstGrismMap *, double, int * ); + +static int GetGrismM( AstGrismMap *, int * ); +static int TestGrismM( AstGrismMap *, int * ); +static void ClearGrismM( AstGrismMap *, int * ); +static void SetGrismM( AstGrismMap *, int, int * ); + +static double GetGrismEps( AstGrismMap *, int * ); +static int TestGrismEps( AstGrismMap *, int * ); +static void ClearGrismEps( AstGrismMap *, int * ); +static void SetGrismEps( AstGrismMap *, double, int * ); + +static double GetGrismTheta( AstGrismMap *, int * ); +static int TestGrismTheta( AstGrismMap *, int * ); +static void ClearGrismTheta( AstGrismMap *, int * ); +static void SetGrismTheta( AstGrismMap *, double, int * ); + +/* Member functions. */ +/* ================= */ +static AstMapping *CanMerge( AstMapping *map1, int inv1, AstMapping *map2, + int inv2, int *status ){ +/* +* +* Name: +* CanMerge + +* Purpose: +* Checks if two GrismMaps can be merged. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* AstMapping *CanMerge( AstMapping *map1, int inv1, AstMapping *map2, +* int inv2, int *status ) + +* Class Membership: +* GrismMap internal utility function. + +* Description: +* This function checks the two supplied Mappings to see if they can +* be merged into a single Mapping. One of the two Mappings should be +* a GrismMap. If they can be merged, the Merged Mapping is returned +* as the function value. Otherwise NULL is returned. + +* Parameters: +* map1 +* A pointer to the first mapping. +* map2 +* A pointer to the second mapping. +* inv1 +* The invert flag to use with the first mapping. +* inv2 +* The invert flag to use with the second mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the merged Mapping if the supplied Mappings can be merged, +* NULL otherwise. + +*/ + +/* Local Variables: */ + AstGrismMap *gmap2; /* Pointer to second GrismMap */ + AstGrismMap *gmap; /* Pointer to first GrismMap */ + AstMapping *ret; /* Returned merged Mapping */ + double g; /* The value of the GrismG attribute */ + double nrp; /* The value of the GrismNRP attribute */ + double waver; /* The value of the GrismWaveR attribute */ + double z; /* Wavelength scaling */ + int invert_result; /* Is "ret" the inverse of the required Mapping? */ + +/* Initialise the returned value. */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + gmap = NULL; + invert_result = 0; + +/* Initialise the zoom factor of the adjacent ZoomMap to indicate + that we have not yet found an adjacent ZoomMap. */ + z = AST__BAD; + +/* If the first Mapping is a GrismMap... */ + if( !strcmp( "GrismMap", astGetClass( map1 ) ) ) { + gmap = (AstGrismMap *) map1; + +/* If the second Mapping is also a GrismMap, they can be merged into a + UnitMap if one GrismMap is the inverse of the other. */ + if( !strcmp( "GrismMap", astGetClass( map2 ) ) ) { + gmap2 = (AstGrismMap *) map2; + +/* Check that the two GrismMaps have the same attribute values. */ + if( astEQUAL( astGetGrismNR( gmap ), astGetGrismNR( gmap2 )) && + astEQUAL( astGetGrismNRP( gmap ), astGetGrismNRP( gmap2 )) && + astEQUAL( astGetGrismWaveR( gmap ), astGetGrismWaveR( gmap2 )) && + astEQUAL( astGetGrismAlpha( gmap ), astGetGrismAlpha( gmap2 )) && + astEQUAL( astGetGrismG( gmap ), astGetGrismG( gmap2 )) && + astGetGrismM( gmap ) != astGetGrismM( gmap2 ) && + astEQUAL( astGetGrismEps( gmap ), astGetGrismEps( gmap2 )) && + astEQUAL( astGetGrismTheta( gmap ), astGetGrismTheta( gmap2 )) ){ + +/* If so, check that the GrismMaps are applied in opposite senses. If so + we can cancel the two GrismMaps, so return a UnitMap. */ + if( inv1 != inv2 ) ret = (AstMapping *) astUnitMap( 1, "", status ); + } + +/* If the first Mapping is a GrismMap but the second one is not... */ + } else { + +/* We can merge the GrismMap with the second Mapping if the GrismMap has + not been inverted (i.e. if the wavelength output produced by the + GrismMap is fed as input to the second Mapping), and if the second + Mapping is a ZoomMap. */ + if( !inv1 ) { + +/* Indicate that any merged Mapping to be created later will not need to + be inverted. */ + invert_result = 0; + +/* See if the second Mapping is a ZoomMap, and if so, get the zoom + factor. If the Invert attribute in the ZoomMap is not set to the + required value, invert the zoom factor. This gives us the required + *forward* transformation. */ + if( !strcmp( "ZoomMap", astGetClass( map2 ) ) ) { + z = astGetZoom( (AstZoomMap *) map2 ); + if( astGetInvert( map2 ) != inv2 && z != 0.0 ) z = 1.0/z; + } + } + } + +/* If the first Mapping is not a GrismMap, but the second one is... */ + } else if( !strcmp( "GrismMap", astGetClass( map2 ) ) ) { + gmap = (AstGrismMap *) map2; + +/* We can merge the GrismMap with the first Mapping if the GrismMap has + been inverted (i.e. if the wavelength output produced by the first + Mapping is fed as input to the inverted GrismMap), and if the first + Mapping is a ZoomMap. */ + if( inv2 ) { + +/* It is easier to consider pairs of Mappings in which an un-inverted + GrismMap is followed by a ZoomMap (as in the above case). For this + reason, we invert the Mappings here, so that the merged Mapping created + later will be in the inverse of the required Mapping. Indicate that the + merged Mapping will therefore need to be inverted before being returned. */ + invert_result = 1; + +/* See if the first Mapping is a ZoomMap. If so, get the zoom factor. If the + Invert attribute in the ZoomMap is not set to the opposite of the required + value, invert the zoom factor. This gives us the required *inverse* + transformation. */ + if( !strcmp( "ZoomMap", astGetClass( map1 ) ) ) { + z = astGetZoom( (AstZoomMap *) map1 ); + if( astGetInvert( map1 ) == inv1 && z != 0.0 ) z = 1.0/z; + } + } + } + +/* If required, produce the merged Mapping by merging the forward + GrismMap with the following ZoomMap (and then invert the + resulting Mapping if it is in the wrong direction). */ + if( !ret && z != AST__BAD && z != 0.0 ) { + +/* Ensure we have a forward GrismMap. */ + ret = astCopy( gmap ); + astSetInvert( ret, 0 ); + +/* Get the required GrismMap attribute values. */ + g = astGetGrismG( ret ); + nrp = astGetGrismNRP( ret ); + waver = astGetGrismWaveR( ret ); + +/* The above code ensures that z is the zoom factor from the wavelength + produced by the forward GrismMap to the final (modified) wavelength units. + Set the new GrismMap attribute values. GrismG, GrismNRP and GrismWaveR have + units of length and are scaled to represent new length units using the + zoom factor found above. */ + g /= z; + nrp /= z; + waver *= z; + + astSetGrismG( ret, g ); + astSetGrismNRP( ret, nrp ); + astSetGrismWaveR( ret, waver ); + +/* If required invert this GrismMap. */ + if( invert_result ) astInvert( ret ); + + } + +/* Return the answer. */ + return ret; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a GrismMap. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* GrismMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* GrismMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the GrismMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstGrismMap *this; /* Pointer to the GrismMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the GrismMap structure. */ + this = (AstGrismMap *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + if ( !strcmp( attrib, "grismnr" ) ) { + astClearGrismNR( this ); + + } else if ( !strcmp( attrib, "grismnrp" ) ) { + astClearGrismNRP( this ); + + } else if ( !strcmp( attrib, "grismwaver" ) ) { + astClearGrismWaveR( this ); + + } else if ( !strcmp( attrib, "grismalpha" ) ) { + astClearGrismAlpha( this ); + + } else if ( !strcmp( attrib, "grismg" ) ) { + astClearGrismG( this ); + + } else if ( !strcmp( attrib, "grismm" ) ) { + astClearGrismM( this ); + + } else if ( !strcmp( attrib, "grismeps" ) ) { + astClearGrismEps( this ); + + } else if ( !strcmp( attrib, "grismtheta" ) ) { + astClearGrismTheta( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two GrismMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* GrismMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two GrismMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a GrismMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the GrismMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstGrismMap *that; + AstGrismMap *this; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two GrismMap structures. */ + this = (AstGrismMap *) this_object; + that = (AstGrismMap *) that_object; + +/* Check the second object is a GrismMap. We know the first is a + GrismMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAGrismMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two GrismMaps differ, it may still be possible + for them to be equivalent. First compare the GrismMaps if their Invert + flags are the same. In this case all the attributes of the two GrismMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + + if( astEQUAL( this->nr, that->nr ) && + astEQUAL( this->nrp, that->nrp ) && + astEQUAL( this->waver, that->waver ) && + astEQUAL( this->alpha, that->alpha ) && + astEQUAL( this->g, that->g ) && + this->m == that->m && + astEQUAL( this->eps, that->eps ) && + astEQUAL( this->theta, that->theta ) && + astEQUAL( this->k1, that->k1 ) && + astEQUAL( this->k2, that->k2 ) && + astEQUAL( this->k3, that->k3 ) ) { + result = 1; + } + +/* If the Invert flags for the two GrismMaps differ, the attributes of the two + GrismMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a GrismMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a GrismMap. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* GrismMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a GrismMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the GrismMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the GrismMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the GrismMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstGrismMap *this; /* Pointer to the GrismMap structure */ + const char *result; /* Pointer value to return */ + double dval; /* Attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the GrismMap structure. */ + this = (AstGrismMap *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + + if ( !strcmp( attrib, "grismnr" ) ) { + dval = astGetGrismNR( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismnrp" ) ) { + dval = astGetGrismNRP( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismwaver" ) ) { + dval = astGetGrismWaveR( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismalpha" ) ) { + dval = astGetGrismAlpha( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismg" ) ) { + dval = astGetGrismG( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismm" ) ) { + dval = astGetGrismM( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismeps" ) ) { + dval = astGetGrismEps( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + } else if ( !strcmp( attrib, "grismtheta" ) ) { + dval = astGetGrismTheta( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +void astInitGrismMapVtab_( AstGrismMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitGrismMapVtab + +* Purpose: +* Initialise a virtual function table for a GrismMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "grismmap.h" +* void astInitGrismMapVtab( AstGrismMapVtab *vtab, const char *name ) + +* Class Membership: +* GrismMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the GrismMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAGrismMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->ClearGrismNR = ClearGrismNR; + vtab->GetGrismNR = GetGrismNR; + vtab->SetGrismNR = SetGrismNR; + vtab->TestGrismNR = TestGrismNR; + + vtab->ClearGrismNRP = ClearGrismNRP; + vtab->GetGrismNRP = GetGrismNRP; + vtab->SetGrismNRP = SetGrismNRP; + vtab->TestGrismNRP = TestGrismNRP; + + vtab->ClearGrismWaveR = ClearGrismWaveR; + vtab->GetGrismWaveR = GetGrismWaveR; + vtab->SetGrismWaveR = SetGrismWaveR; + vtab->TestGrismWaveR = TestGrismWaveR; + + vtab->ClearGrismAlpha = ClearGrismAlpha; + vtab->GetGrismAlpha = GetGrismAlpha; + vtab->SetGrismAlpha = SetGrismAlpha; + vtab->TestGrismAlpha = TestGrismAlpha; + + vtab->ClearGrismG = ClearGrismG; + vtab->GetGrismG = GetGrismG; + vtab->SetGrismG = SetGrismG; + vtab->TestGrismG = TestGrismG; + + vtab->ClearGrismM = ClearGrismM; + vtab->GetGrismM = GetGrismM; + vtab->SetGrismM = SetGrismM; + vtab->TestGrismM = TestGrismM; + + vtab->ClearGrismEps = ClearGrismEps; + vtab->GetGrismEps = GetGrismEps; + vtab->SetGrismEps = SetGrismEps; + vtab->TestGrismEps = TestGrismEps; + + vtab->ClearGrismTheta = ClearGrismTheta; + vtab->GetGrismTheta = GetGrismTheta; + vtab->SetGrismTheta = SetGrismTheta; + vtab->TestGrismTheta = TestGrismTheta; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the class dump, copy and delete functions.*/ + astSetDump( vtab, Dump, "GrismMap", + "Map 1-d coordinates using a spectral disperser" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a GrismMap. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* GrismMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated GrismMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated GrismMap with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated GrismMap which is to be merged with +* its neighbours. This should be a cloned copy of the GrismMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* GrismMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated GrismMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *merged_map; /* Merger of two Mappings */ + int i1; /* Lower index of the two GrismMaps being merged */ + int i2; /* Upper index of the two GrismMaps being merged */ + int i; /* Mapping index */ + int result; /* Result value to return */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + i1 = -1; + i2 = -1; + +/* See if the GrismMap can be merged with the Mappings on either side of it + in the list. This can only be done in series for a GrismMap. */ +/* ===================================================================== */ + if( series ) { + +/* Set a flag indicating that we have not yet found a neighbour with which + the GrismMap can be merged. */ + merged_map = NULL; + +/* First check the lower neighbour (if any). */ + if( where > 0 ) { + i1 = where - 1; + i2 = where; + merged_map = CanMerge( ( *map_list )[ i1 ], (* invert_list)[ i1 ], + ( *map_list )[ i2 ], (* invert_list)[ i2 ], status ); + } + +/* If the GrismMap can not be merged with its lower neighbour, check its + upper neighbour (if any) in the same way. */ + if( !merged_map && where < *nmap - 1 ) { + i1 = where; + i2 = where + 1; + merged_map = CanMerge( ( *map_list )[ i1 ], (* invert_list)[ i1 ], + ( *map_list )[ i2 ], (* invert_list)[ i2 ], status ); + } + +/* If either neighbour has passed these checks, replace the pair of + Mappings which have been merged with the single merged Mapping returned + above. */ + if( merged_map ) { + +/* Annul the two Mappings. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + (void) astAnnul( ( *map_list )[ i2 ] ); + +/* Store a pointer for the merged Mapping in place of the first of the + two replaced Mappings. */ + ( *map_list )[ i1 ] = merged_map; + ( *invert_list )[ i1 ] = astGetInvert( merged_map ); + +/* Shuffle down the remaining Mappings to fill the hole left by the + second of the replaced Mappings. */ + for ( i = i2 + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + (*nmap)--; + result = i1; + + } + } + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a GrismMap. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* GrismMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a GrismMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the GrismMap. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstGrismMap *this; /* Pointer to the GrismMap structure */ + double dval; /* Attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the GrismMap structure. */ + this = (AstGrismMap *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + + if ( nc = 0, ( 1 == astSscanf( setting, "grismnr= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismNR( this, dval ); + + } else if ( nc = 0, ( 1 == astSscanf( setting, "grismnrp= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismNRP( this, dval ); + + } else if ( nc = 0, ( 1 == astSscanf( setting, "grismwaver= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismWaveR( this, dval ); + + } else if ( nc = 0, ( 1 == astSscanf( setting, "grismalpha= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismAlpha( this, dval ); + + } else if ( nc = 0, ( 1 == astSscanf( setting, "grismg= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismG( this, dval ); + + } else if ( nc = 0, ( 1 == astSscanf( setting, "grismm= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismM( this, dval ); + + } else if ( nc = 0, ( 1 == astSscanf( setting, "grismeps= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismEps( this, dval ); + + } else if ( nc = 0, ( 1 == astSscanf( setting, "grismtheta= %lf %n", &dval, &nc ) ) && ( nc >= len ) ) { + astSetGrismTheta( this, dval ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a GrismMap. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* GrismMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a GrismMap's attributes. + +* Parameters: +* this +* Pointer to the GrismMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstGrismMap *this; /* Pointer to the GrismMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the GrismMap structure. */ + this = (AstGrismMap *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + if ( !strcmp( attrib, "grismnr" ) ) { + result = astTestGrismNR( this ); + + } else if ( !strcmp( attrib, "grismnrp" ) ) { + result = astTestGrismNRP( this ); + + } else if ( !strcmp( attrib, "grismwaver" ) ) { + result = astTestGrismWaveR( this ); + + } else if ( !strcmp( attrib, "grismalpha" ) ) { + result = astTestGrismAlpha( this ); + + } else if ( !strcmp( attrib, "grismg" ) ) { + result = astTestGrismG( this ); + + } else if ( !strcmp( attrib, "grismm" ) ) { + result = astTestGrismM( this ); + + } else if ( !strcmp( attrib, "grismeps" ) ) { + result = astTestGrismEps( this ); + + } else if ( !strcmp( attrib, "grismtheta" ) ) { + result = astTestGrismTheta( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a GrismMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* GrismMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a GrismMap and a set of points encapsulated +* in a PointSet and transforms the points so as to apply the +* forward or inverse dispersal equation. + +* Parameters: +* this +* Pointer to the GrismMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate +* transformation should be applied, while a zero value requests +* the inverse transformation. +* out +* Pointer to a PointSet which will hold the transformed +* (output) coordinate values. A NULL value may also be given, +* in which case a new PointSet will be created by this +* function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - The number of coordinate values per point in the input +* PointSet must equal 1. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points (with 1 coordinate value per point) +* to accommodate the result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstGrismMap *map; /* Pointer to GrismMap to be applied */ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double sinbeta; /* Sin( beta ) (see FITS-WCS paper III) */ + double value_in; /* Input coordinate value */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the GrismMap. */ + map = (AstGrismMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform + member function inherited from the parent Mapping class. This + function validates all arguments and generates an output PointSet + if necessary, but does not actually transform any coordinate + values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points from the input PointSet and obtain + pointers for accessing the input and output coordinate values. */ + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, + according to the direction specified and whether the mapping has + been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* If any of the parameters are undefined fill the output with bad + values (if possible). */ + if( map->k1 == AST__BAD || map->k2 == AST__BAD || map->k3 == AST__BAD ) { + if( astOK ) { + for ( point = 0; point < npoint; point++ ) { + ptr_out[ 0 ][ point ] = AST__BAD; + } + } + +/* Otherwise... */ + } else { + +/* Forward transformation. */ +/* ----------------------- */ + if ( forward ) { + +/* Loop to transform each input point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Extract the input grism parameter value. */ + value_in = ptr_in[ 0 ][ point ]; + +/* Check for bad input coordinates and generate a bad result if necessary. */ + if( value_in == AST__BAD || map->k2 == 0.0 ) { + ptr_out[ 0 ][ point ] = AST__BAD; + +/* Otherwise, apply the algorithm described in FITS-WCS paper III. */ + } else { + ptr_out[ 0 ][ point ] = ( map->k1 + sin( atan( value_in ) + map->k3 ) )/map->k2; + } + } + +/* Inverse transformation. */ +/* ----------------------- */ + } else { + +/* Loop to transform each input point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Extract the input wavelength value. */ + value_in = ptr_in[ 0 ][ point ]; + +/* Check for bad input coordinates and generate a bad result if necessary. */ + if ( value_in == AST__BAD ) { + ptr_out[ 0 ][ point ] = AST__BAD; + +/* Otherwise, apply the algorithm described in FITS-WCS paper III. */ + } else { + sinbeta = map->k2*value_in - map->k1; + if( sinbeta < -1.0 || sinbeta > 1.0 ) { + ptr_out[ 0 ][ point ] = AST__BAD; + } else { + ptr_out[ 0 ][ point ] = tan( asin( sinbeta ) - map->k3 ); + } + } + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; +} + + +static void UpdateConstants( AstGrismMap *this, int *status ){ +/* +* Name: +* UpdateConstants + +* Purpose: +* Re-calculate the constants used within the transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* void UpdateConstants( AstGrismMap *this, int *status ) + +* Class Membership: +* GrismMap member function + +* Description: +* This function re-calculates the constants used within the +* transformation on the basis of the current values of the +* GrismMap attributes. It should be called whenever a new value is +* set for an attribute, or an attribute is cleared. + +* Parameters: +* this +* Pointer to the GrismMap. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double alpha; /* The current vaue of the GrismAlpha attribute */ + double coseps; /* cos( eps ) */ + double sinalpha; /* sin( alpha ) */ + double eps; /* The current vaue of the GrismEps attribute */ + double g; /* The current vaue of the GrismG attribute */ + double nr; /* The current vaue of the GrismNR attribute */ + double nrp; /* The current vaue of the GrismNRP attribute */ + double sinbeta_r; /* sin( beta_r ) */ + double theta; /* The current vaue of the GrismTheta attribute */ + double wave_r; /* The current vaue of the GrismWaveR attribute */ + int m; /* The current vaue of the GrismM attribute */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the current attribute values. */ + nr = astGetGrismNR( this ); + nrp = astGetGrismNRP( this ); + wave_r = astGetGrismWaveR( this ); + alpha = astGetGrismAlpha( this ); + g = astGetGrismG( this ); + m = astGetGrismM( this ); + eps = astGetGrismEps( this ); + theta = astGetGrismTheta( this ); + +/* Re-calculate the constants. */ + coseps = cos( eps ); + sinalpha = sin( alpha ); + + this->k1 = sinalpha*( nr - nrp*wave_r ); + + if( coseps != 0.0 ) { + this->k2 = ( g*m/coseps ) - nrp*sinalpha; + + sinbeta_r = g*m*wave_r/coseps - nr*sinalpha; + if( sinbeta_r < -1.0 || sinbeta_r > 1.0 ) { + this->k3 = AST__BAD; + } else { + this->k3 = asin( sinbeta_r ) + theta; + } + + } else { + this->k2 = AST__BAD; + this->k3 = AST__BAD; + } + +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* GrismNR + +* Purpose: +* The refractive index at the reference wavelength. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds refractive index of the grism material at the +* reference wavelength (given by attribute GrismWaveR). The default +* value is 1.0. +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismNR,nr,(AST__BAD)) +astMAKE_GET(GrismMap,GrismNR,double,1.0,( ( this->nr == AST__BAD ) ? + 1.0 : this->nr )) +MAKE_SET(GrismMap,GrismNR,double,nr,(value) ) +astMAKE_TEST(GrismMap,GrismNR,( this->nr != AST__BAD )) + +/* +*att++ +* Name: +* GrismNRP + +* Purpose: +* The rate of change of refractive index with wavelength. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds the rate of change of the refractive index of the +* grism material with respect to wavelength at the reference wavelength +* (given by attribute GrismWaveR). The default value is 0.0 (the +* appropriate value for a pure grating disperser with no prism). The +* units of this attribute should be consistent with those of attributes +* GrismWaveR and GrismG. +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismNRP,nrp,(AST__BAD)) +astMAKE_GET(GrismMap,GrismNRP,double,0.0,( ( this->nrp == AST__BAD ) ? + 0.0 : this->nrp )) +MAKE_SET(GrismMap,GrismNRP,double,nrp,(value) ) +astMAKE_TEST(GrismMap,GrismNRP,( this->nrp != AST__BAD )) + +/* +*att++ +* Name: +* GrismWaveR + +* Purpose: +* The reference wavelength. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds reference wavelength. The default value is +* 5000 (Angstrom). The units of this attribute should be consistent with +* those of attributes GrismNRP and GrismG. +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismWaveR,waver,(AST__BAD)) +astMAKE_GET(GrismMap,GrismWaveR,double,5000.0,( ( this->waver == AST__BAD ) ? + 5000.0 : this->waver )) +MAKE_SET(GrismMap,GrismWaveR,double,waver,(value) ) +astMAKE_TEST(GrismMap,GrismWaveR,( this->waver != AST__BAD )) + +/* +*att++ +* Name: +* GrismAlpha + +* Purpose: +* The angle of incidence of the incoming light on the grating surface. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds the angle between the incoming light and the +* normal to the grating surface, in radians. The default value is 0. +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismAlpha,alpha,(AST__BAD)) +astMAKE_GET(GrismMap,GrismAlpha,double,0.0,( ( this->alpha == AST__BAD ) ? + 0.0 : this->alpha )) +MAKE_SET(GrismMap,GrismAlpha,double,alpha,(value) ) +astMAKE_TEST(GrismMap,GrismAlpha,( this->alpha != AST__BAD )) + +/* +*att++ +* Name: +* GrismG + +* Purpose: +* The grating ruling density. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds the number of grating rulings per unit length. +* The unit of length used should be consistent with the units used +* for attributes GrismWaveR and GrismNRP. The default value is 0.0. +* (the appropriate value for a pure prism disperser with no grating). +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismG,g,(AST__BAD)) +astMAKE_GET(GrismMap,GrismG,double,0.0,( ( this->g == AST__BAD ) ? + 0.0 : this->g )) +MAKE_SET(GrismMap,GrismG,double,g,(value) ) +astMAKE_TEST(GrismMap,GrismG,( this->g != AST__BAD )) + +/* +*att++ +* Name: +* GrismM + +* Purpose: +* The interference order + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute holds the interference order being considered. +* The default value is 0. +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismM,m,(INT_MAX)) +astMAKE_GET(GrismMap,GrismM,int,0,( ( this->m == INT_MAX ) ? + 0 : this->m )) +MAKE_SET(GrismMap,GrismM,int,m,(value) ) +astMAKE_TEST(GrismMap,GrismM,( this->m != INT_MAX )) + +/* +*att++ +* Name: +* GrismEps + +* Purpose: +* The angle between the normal and the dispersion plane. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds the angle (in radians) between the normal to +* the grating or exit prism face, and the dispersion plane. The +* dispersion plane is the plane spanned by the incoming and outgoing +* ray. The default value is 0.0. +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismEps,eps,(AST__BAD)) +astMAKE_GET(GrismMap,GrismEps,double,0.0,( ( this->eps == AST__BAD ) ? + 0.0 : this->eps )) +MAKE_SET(GrismMap,GrismEps,double,eps,(value) ) +astMAKE_TEST(GrismMap,GrismEps,( this->eps != AST__BAD )) + +/* +*att++ +* Name: +* GrismTheta + +* Purpose: +* Angle between normal to detector plane and reference ray. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute gives the angle of incidence of light of the +* reference wavelength (given by attribute GrismWaveR) onto the +* detector. Specifically, it holds the angle (in radians) between +* the normal to the detector plane and an incident ray at the reference +* wavelength. The default value is 0.0. +* +* Note, the value of this attribute may changed only if the GrismMap +* has no more than one reference. That is, an error is reported if the +* GrismMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* GrismMap +* All GrismMaps have this attribute. + +*att-- +*/ +MAKE_CLEAR(GrismMap,GrismTheta,theta,(AST__BAD)) +astMAKE_GET(GrismMap,GrismTheta,double,0.0,( ( this->theta == AST__BAD ) ? + 0.0 : this->theta )) +MAKE_SET(GrismMap,GrismTheta,double,theta,(value) ) +astMAKE_TEST(GrismMap,GrismTheta,( this->theta != AST__BAD )) + +/* Copy constructor. */ +/* ----------------- */ +/* No copy constructor is needed, as a byte-by-byte copy suffices. */ + +/* Destructor. */ +/* ----------- */ +/* No destructor is needed as no memory, etc. needs freeing. */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for GrismMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the GrismMap class to an output Channel. + +* Parameters: +* this +* Pointer to the GrismMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstGrismMap *this; /* Pointer to the GrismMap structure */ + double dval; /* Double value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the GrismMap structure. */ + this = (AstGrismMap *) this_object; + +/* Write out values representing the instance variables for the + GrismMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + + set = TestGrismNR( this, status ); + dval = set ? GetGrismNR( this, status ) : astGetGrismNR( this ); + astWriteDouble( channel, "GrmNR", set, 1, dval, "Refractive index at the ref. wavelength" ); + + set = TestGrismNRP( this, status ); + dval = set ? GetGrismNRP( this, status ) : astGetGrismNRP( this ); + astWriteDouble( channel, "GrmNRP", set, 1, dval, "Rate of change of refractive index" ); + + set = TestGrismWaveR( this, status ); + dval = set ? GetGrismWaveR( this, status ) : astGetGrismWaveR( this ); + astWriteDouble( channel, "GrmWR", set, 1, dval, "Ref. wavelength" ); + + set = TestGrismAlpha( this, status ); + dval = set ? GetGrismAlpha( this, status ) : astGetGrismAlpha( this ); + astWriteDouble( channel, "GrmAlp", set, 1, dval, "Angle of incidence of incoming light" ); + + set = TestGrismG( this, status ); + dval = set ? GetGrismG( this, status ) : astGetGrismG( this ); + astWriteDouble( channel, "GrmG", set, 1, dval, "Grating ruling density" ); + + set = TestGrismM( this, status ); + dval = set ? GetGrismM( this, status ) : astGetGrismM( this ); + astWriteDouble( channel, "GrmM", set, 1, dval, "The interference order" ); + + set = TestGrismEps( this, status ); + dval = set ? GetGrismEps( this, status ) : astGetGrismEps( this ); + astWriteDouble( channel, "GrmEps", set, 1, dval, "Angle between grating normal and dispersion plane" ); + + set = TestGrismTheta( this, status ); + dval = set ? GetGrismTheta( this, status ) : astGetGrismTheta( this ); + astWriteDouble( channel, "GrmTh", set, 1, dval, "Angle between detector normal and reference ray" ); + +} + + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAGrismMap and astCheckGrismMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(GrismMap,Mapping) +astMAKE_CHECK(GrismMap) + +AstGrismMap *astGrismMap_( const char *options, int *status, ...) { +/* +*++ +* Name: +c astGrismMap +f AST_GRISMMAP + +* Purpose: +* Create a GrismMap. + +* Type: +* Public function. + +* Synopsis: +c #include "grismmap.h" +c AstGrismMap *astGrismMap( const char *options, ... ) +f RESULT = AST_GRISMMAP( OPTIONS, STATUS ) + +* Class Membership: +* GrismMap constructor. + +* Description: +* This function creates a new GrismMap and optionally initialises +* its attributes. +* +* A GrismMap is a specialised form of Mapping which transforms +* 1-dimensional coordinates using the spectral dispersion equation +* described in FITS-WCS paper III "Representation of spectral +* coordinates in FITS". This describes the dispersion produced by +* gratings, prisms and grisms. +* +* When initially created, the forward transformation of a GrismMap +* transforms input "grism parameter" values into output wavelength +* values. The "grism parameter" is a dimensionless value which is +* linearly related to position on the detector. It is defined in FITS-WCS +* paper III as "the offset on the detector from the point of intersection +* of the camera axis, measured in units of the effective local length". +* The units in which wavelength values are expected or returned is +* determined by the values supplied for the GrismWaveR, GrismNRP and +* GrismG attribute: whatever units are used for these attributes will +* also be used for the wavelength values. + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new GrismMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new GrismMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGrismMap() +f AST_GRISMMAP = INTEGER +* A pointer to the new GrismMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstGrismMap *new; /* Pointer to new GrismMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the GrismMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitGrismMap( NULL, sizeof( AstGrismMap ), !class_init, + &class_vtab, "GrismMap" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + GrismMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new GrismMap. */ + return new; +} + +AstGrismMap *astGrismMapId_( const char *options, ... ) { +/* +* Name: +* astGrismMapId_ + +* Purpose: +* Create a GrismMap. + +* Type: +* Private function. + +* Synopsis: +* #include "grismmap.h" +* AstGrismMap *astGrismMapId( const char *options, int *status, ... ) + +* Class Membership: +* GrismMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astGrismMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astGrismMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astGrismMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astGrismMap_. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The ID value associated with the new GrismMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstGrismMap *new; /* Pointer to new GrismMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the GrismMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitGrismMap( NULL, sizeof( AstGrismMap ), !class_init, + &class_vtab, "GrismMap" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new GrismMap's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new GrismMap. */ + return astMakeId( new ); +} + +AstGrismMap *astInitGrismMap_( void *mem, size_t size, int init, + AstGrismMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitGrismMap + +* Purpose: +* Initialise a GrismMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "grismmap.h" +* AstGrismMap *astInitGrismMap( void *mem, size_t size, int init, +* AstGrismMapVtab *vtab, const char *name ) + +* Class Membership: +* GrismMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new GrismMap object. It allocates memory (if necessary) to accommodate +* the GrismMap plus any additional data associated with the derived class. +* It then initialises a GrismMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a GrismMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the GrismMap is to be initialised. +* This must be of sufficient size to accommodate the GrismMap data +* (sizeof(GrismMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the GrismMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the GrismMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the GrismMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new GrismMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). + +* Returned Value: +* A pointer to the new GrismMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstGrismMap *new; /* Pointer to new GrismMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitGrismMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Mapping structure (the parent class) as the first component + within the GrismMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstGrismMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 1, 1, 1, 1 ); + if ( astOK ) { + +/* Initialise the GrismMap data. */ +/* ---------------------------- */ + new->nr = AST__BAD; + new->nrp = AST__BAD; + new->waver = AST__BAD; + new->alpha = AST__BAD; + new->g = AST__BAD; + new->m = INT_MAX; + new->eps = AST__BAD; + new->theta = AST__BAD; + +/* Set up the other required derived constants. */ + UpdateConstants( new, status ); + +/* If an error occurred, clean up by deleting the new GrismMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new GrismMap. */ + return new; +} + +AstGrismMap *astLoadGrismMap_( void *mem, size_t size, + AstGrismMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadGrismMap + +* Purpose: +* Load a GrismMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "grismmap.h" +* AstGrismMap *astLoadGrismMap( void *mem, size_t size, +* AstGrismMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* GrismMap loader. + +* Description: +* This function is provided to load a new GrismMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* GrismMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a GrismMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the GrismMap is to be +* loaded. This must be of sufficient size to accommodate the +* GrismMap data (sizeof(GrismMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the GrismMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the GrismMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstGrismMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new GrismMap. If this is NULL, a pointer +* to the (static) virtual function table for the GrismMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "GrismMap" is used instead. + +* Returned Value: +* A pointer to the new GrismMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstGrismMap *new; /* Pointer to the new GrismMap */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this GrismMap. In this case the + GrismMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstGrismMap ); + vtab = &class_vtab; + name = "GrismMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitGrismMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built GrismMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "GrismMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + + new->nr = astReadDouble( channel, "grmnr", AST__BAD ); + if ( TestGrismNR( new, status ) ) SetGrismNR( new, new->nr, status ); + + new->nrp = astReadDouble( channel, "grmnrp", AST__BAD ); + if ( TestGrismNRP( new, status ) ) SetGrismNRP( new, new->nrp, status ); + + new->waver = astReadDouble( channel, "grmwr", AST__BAD ); + if ( TestGrismWaveR( new, status ) ) SetGrismWaveR( new, new->waver, status ); + + new->alpha = astReadDouble( channel, "grmalp", AST__BAD ); + if ( TestGrismAlpha( new, status ) ) SetGrismAlpha( new, new->alpha, status ); + + new->g = astReadDouble( channel, "grmg", AST__BAD ); + if ( TestGrismG( new, status ) ) SetGrismG( new, new->g, status ); + + new->m = astReadInt( channel, "grmm", INT_MAX ); + if ( TestGrismM( new, status ) ) SetGrismM( new, new->m, status ); + + new->eps = astReadDouble( channel, "grmeps", AST__BAD ); + if ( TestGrismEps( new, status ) ) SetGrismEps( new, new->eps, status ); + + new->theta = astReadDouble( channel, "grmth", AST__BAD ); + if ( TestGrismTheta( new, status ) ) SetGrismTheta( new, new->theta, status ); + +/* Set up the other required derived constants. */ + UpdateConstants( new, status ); + } + +/* If an error occurred, clean up by deleting the new GrismMap. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the new GrismMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions + defined by this class. Each simply checks the global error status + and then locates and executes the appropriate member function, + using the function pointer stored in the object's virtual function + table (this pointer is located using the astMEMBER macro defined in + "object.h"). + + Note that the member function may not be the one defined here, as + it may have been over-ridden by a derived class. However, it should + still have the same interface. */ + + + + + diff --git a/grismmap.h b/grismmap.h new file mode 100644 index 0000000..5655be0 --- /dev/null +++ b/grismmap.h @@ -0,0 +1,353 @@ +#if !defined( GRISMMAP_INCLUDED ) /* Include this file only once */ +#define GRISMMAP_INCLUDED +/* +*+ +* Name: +* grismmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the GrismMap class. + +* Invocation: +* #include "grismmap.h" + +* Description: +* This include file defines the interface to the GrismMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The GrismMap class implements Mappings which perform a "zoom" +* transformation by multiplying all coordinate values by the same +* scale factor (the inverse transformation is performed by +* dividing by this scale factor). + +* Inheritance: +* The GrismMap class inherits from the Mapping class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 8-JUL-2003 (DSB): +* Initial version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* GrismMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstGrismMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double nr; + double nrp; + double waver; + double alpha; + double g; + int m; + double eps; + double theta; + double k1; + double k2; + double k3; + +} AstGrismMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstGrismMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + double (* GetGrismNR)( AstGrismMap *, int * ); + int (* TestGrismNR)( AstGrismMap *, int * ); + void (* ClearGrismNR)( AstGrismMap *, int * ); + void (* SetGrismNR)( AstGrismMap *, double, int * ); + + double (* GetGrismNRP)( AstGrismMap *, int * ); + int (* TestGrismNRP)( AstGrismMap *, int * ); + void (* ClearGrismNRP)( AstGrismMap *, int * ); + void (* SetGrismNRP)( AstGrismMap *, double, int * ); + + double (* GetGrismWaveR)( AstGrismMap *, int * ); + int (* TestGrismWaveR)( AstGrismMap *, int * ); + void (* ClearGrismWaveR)( AstGrismMap *, int * ); + void (* SetGrismWaveR)( AstGrismMap *, double, int * ); + + double (* GetGrismAlpha)( AstGrismMap *, int * ); + int (* TestGrismAlpha)( AstGrismMap *, int * ); + void (* ClearGrismAlpha)( AstGrismMap *, int * ); + void (* SetGrismAlpha)( AstGrismMap *, double, int * ); + + double (* GetGrismG)( AstGrismMap *, int * ); + int (* TestGrismG)( AstGrismMap *, int * ); + void (* ClearGrismG)( AstGrismMap *, int * ); + void (* SetGrismG)( AstGrismMap *, double, int * ); + + int (* GetGrismM)( AstGrismMap *, int * ); + int (* TestGrismM)( AstGrismMap *, int * ); + void (* ClearGrismM)( AstGrismMap *, int * ); + void (* SetGrismM)( AstGrismMap *, int, int * ); + + double (* GetGrismEps)( AstGrismMap *, int * ); + int (* TestGrismEps)( AstGrismMap *, int * ); + void (* ClearGrismEps)( AstGrismMap *, int * ); + void (* SetGrismEps)( AstGrismMap *, double, int * ); + + double (* GetGrismTheta)( AstGrismMap *, int * ); + int (* TestGrismTheta)( AstGrismMap *, int * ); + void (* ClearGrismTheta)( AstGrismMap *, int * ); + void (* SetGrismTheta)( AstGrismMap *, double, int * ); + +} AstGrismMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstGrismMapGlobals { + AstGrismMapVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstGrismMapGlobals; + +#endif +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(GrismMap) /* Check class membership */ +astPROTO_ISA(GrismMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstGrismMap *astGrismMap_( const char *, int *, ...); +#else +AstGrismMap *astGrismMapId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstGrismMap *astInitGrismMap_( void *, size_t, int, AstGrismMapVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitGrismMapVtab_( AstGrismMapVtab *, const char *, int * ); + +/* Loader. */ +AstGrismMap *astLoadGrismMap_( void *, size_t, AstGrismMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitGrismMapGlobals_( AstGrismMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ + + double astGetGrismNR_( AstGrismMap *, int * ); + int astTestGrismNR_( AstGrismMap *, int * ); + void astClearGrismNR_( AstGrismMap *, int * ); + void astSetGrismNR_( AstGrismMap *, double, int * ); + + double astGetGrismNRP_( AstGrismMap *, int * ); + int astTestGrismNRP_( AstGrismMap *, int * ); + void astClearGrismNRP_( AstGrismMap *, int * ); + void astSetGrismNRP_( AstGrismMap *, double, int * ); + + double astGetGrismWaveR_( AstGrismMap *, int * ); + int astTestGrismWaveR_( AstGrismMap *, int * ); + void astClearGrismWaveR_( AstGrismMap *, int * ); + void astSetGrismWaveR_( AstGrismMap *, double, int * ); + + double astGetGrismAlpha_( AstGrismMap *, int * ); + int astTestGrismAlpha_( AstGrismMap *, int * ); + void astClearGrismAlpha_( AstGrismMap *, int * ); + void astSetGrismAlpha_( AstGrismMap *, double, int * ); + + double astGetGrismG_( AstGrismMap *, int * ); + int astTestGrismG_( AstGrismMap *, int * ); + void astClearGrismG_( AstGrismMap *, int * ); + void astSetGrismG_( AstGrismMap *, double, int * ); + + int astGetGrismM_( AstGrismMap *, int * ); + int astTestGrismM_( AstGrismMap *, int * ); + void astClearGrismM_( AstGrismMap *, int * ); + void astSetGrismM_( AstGrismMap *, int, int * ); + + double astGetGrismEps_( AstGrismMap *, int * ); + int astTestGrismEps_( AstGrismMap *, int * ); + void astClearGrismEps_( AstGrismMap *, int * ); + void astSetGrismEps_( AstGrismMap *, double, int * ); + + double astGetGrismTheta_( AstGrismMap *, int * ); + int astTestGrismTheta_( AstGrismMap *, int * ); + void astClearGrismTheta_( AstGrismMap *, int * ); + void astSetGrismTheta_( AstGrismMap *, double, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckGrismMap(this) astINVOKE_CHECK(GrismMap,this,0) +#define astVerifyGrismMap(this) astINVOKE_CHECK(GrismMap,this,1) + +/* Test class membership. */ +#define astIsAGrismMap(this) astINVOKE_ISA(GrismMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astGrismMap astINVOKE(F,astGrismMap_) +#else +#define astGrismMap astINVOKE(F,astGrismMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitGrismMap(mem,size,init,vtab,name) \ +astINVOKE(O,astInitGrismMap_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitGrismMapVtab(vtab,name) astINVOKE(V,astInitGrismMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadGrismMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadGrismMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckGrismMap to validate GrismMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ + +#define astGetGrismNR(this) astINVOKE(V,astGetGrismNR_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismNR(this) astINVOKE(V,astTestGrismNR_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismNR(this) astINVOKE(V,astClearGrismNR_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismNR(this,value) astINVOKE(V,astSetGrismNR_(astCheckGrismMap(this),value,STATUS_PTR)) + +#define astGetGrismNRP(this) astINVOKE(V,astGetGrismNRP_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismNRP(this) astINVOKE(V,astTestGrismNRP_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismNRP(this) astINVOKE(V,astClearGrismNRP_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismNRP(this,value) astINVOKE(V,astSetGrismNRP_(astCheckGrismMap(this),value,STATUS_PTR)) + +#define astGetGrismWaveR(this) astINVOKE(V,astGetGrismWaveR_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismWaveR(this) astINVOKE(V,astTestGrismWaveR_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismWaveR(this) astINVOKE(V,astClearGrismWaveR_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismWaveR(this,value) astINVOKE(V,astSetGrismWaveR_(astCheckGrismMap(this),value,STATUS_PTR)) + +#define astGetGrismAlpha(this) astINVOKE(V,astGetGrismAlpha_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismAlpha(this) astINVOKE(V,astTestGrismAlpha_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismAlpha(this) astINVOKE(V,astClearGrismAlpha_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismAlpha(this,value) astINVOKE(V,astSetGrismAlpha_(astCheckGrismMap(this),value,STATUS_PTR)) + +#define astGetGrismG(this) astINVOKE(V,astGetGrismG_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismG(this) astINVOKE(V,astTestGrismG_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismG(this) astINVOKE(V,astClearGrismG_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismG(this,value) astINVOKE(V,astSetGrismG_(astCheckGrismMap(this),value,STATUS_PTR)) + +#define astGetGrismM(this) astINVOKE(V,astGetGrismM_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismM(this) astINVOKE(V,astTestGrismM_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismM(this) astINVOKE(V,astClearGrismM_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismM(this,value) astINVOKE(V,astSetGrismM_(astCheckGrismMap(this),value,STATUS_PTR)) + +#define astGetGrismEps(this) astINVOKE(V,astGetGrismEps_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismEps(this) astINVOKE(V,astTestGrismEps_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismEps(this) astINVOKE(V,astClearGrismEps_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismEps(this,value) astINVOKE(V,astSetGrismEps_(astCheckGrismMap(this),value,STATUS_PTR)) + +#define astGetGrismTheta(this) astINVOKE(V,astGetGrismTheta_(astCheckGrismMap(this),STATUS_PTR)) +#define astTestGrismTheta(this) astINVOKE(V,astTestGrismTheta_(astCheckGrismMap(this),STATUS_PTR)) +#define astClearGrismTheta(this) astINVOKE(V,astClearGrismTheta_(astCheckGrismMap(this),STATUS_PTR)) +#define astSetGrismTheta(this,value) astINVOKE(V,astSetGrismTheta_(astCheckGrismMap(this),value,STATUS_PTR)) + + +#endif +#endif + + + + + diff --git a/interval.c b/interval.c new file mode 100644 index 0000000..73e23bc --- /dev/null +++ b/interval.c @@ -0,0 +1,4686 @@ +/* +*class++ +* Name: +* Interval + +* Purpose: +* A region representing an interval on one or more axes of a Frame. + +* Constructor Function: +c astInterval +f AST_INTERVAL + +* Description: +* The Interval class implements a Region which represents upper +* and/or lower limits on one or more axes of a Frame. For a point to +* be within the region represented by the Interval, the point must +* satisfy all the restrictions placed on all the axes. The point is +* outside the region if it fails to satisfy any one of the restrictions. +* Each axis may have either an upper limit, a lower limit, both or +* neither. If both limits are supplied but are in reverse order (so +* that the lower limit is greater than the upper limit), then the +* interval is an excluded interval, rather than an included interval. +* +* Note, The Interval class makes no allowances for cyclic nature of +* some coordinate systems (such as SkyFrame coordinates). A Box +* should usually be used in these cases since this requires the user +* to think about suitable upper and lower limits, + +* Inheritance: +* The Interval class inherits from the Region class. + +* Attributes: +* The Interval class does not define any new attributes beyond +* those which are applicable to all Regions. + +* Functions: +c The Interval class does not define any new functions beyond those +f The Interval class does not define any new routines beyond those +* which are applicable to all Regions. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 29-OCT-2004 (DSB): +* Original version. +* 19-APR-2006 (DSB): +* Negate the cached equivalent Box if the Interval has been negated. +* 28-MAY-2007 (DSB): +* Re-implemented BndBaseMesh. +* 20-JAN-2009 (DSB): +* Over-ride astRegBasePick. +* 26-JAN-2009 (DSB): +* Over-ride astMapMerge. +* 4-NOV42-2013 (DSB): +* - Change RegCentre so that it does not report an error for an unbounded +* Interval if the centre is merely being inquired rather than set. This is +* the documented behaviour of the astRegCentre method. +* - Modify RegPins so that it can handle uncertainty regions that straddle +* a discontinuity. Previously, such uncertainty Regions could have a huge +* bounding box resulting in matching region being far too big. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Interval + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Abstract coordinate regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "box.h" /* Box Regions */ +#include "nullregion.h" /* Null Regions */ +#include "wcsmap.h" /* Definitons of AST__DPI etc */ +#include "interval.h" /* Interface definition for this class */ +#include "ellipse.h" /* Interface definition for ellipse class */ +#include "mapping.h" /* Position mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "cmpframe.h" /* Compound Frames */ +#include "prism.h" /* Prism regions */ +#include "pointlist.h" /* Lists of points in a Frame */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static int (* parent_overlap)( AstRegion *, AstRegion *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static void (* parent_setunc)( AstRegion *, AstRegion *, int * ); +static void (* parent_resetcache)( AstRegion *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Interval) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Interval,Class_Init) +#define class_vtab astGLOBAL(Interval,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstIntervalVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstInterval *astIntervalId_( void *, const double[], const double[], void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstBox *Cache( AstInterval *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *BndBaseMesh( AstRegion *, double *, double *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * ); +static AstRegion *GetDefUnc( AstRegion *, int * ); +static AstRegion *MergeInterval( AstInterval *, AstRegion *, int, int * ); +static double *RegCentre( AstRegion *this, double *, double **, int, int, int * ); +static int *OneToOne( AstMapping *, int * ); +static int GetBounded( AstRegion *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int Overlap( AstRegion *, AstRegion *, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void IntervalPoints( AstInterval *, double *, double *, int *); +static void RegBaseBox( AstRegion *this, double *, double *, int * ); +static void ResetCache( AstRegion *this, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); +static void SetUnc( AstRegion *, AstRegion *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ + +static AstPointSet *BndBaseMesh( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* BndBaseMesh + +* Purpose: +* Return a PointSet containing points spread around part of the boundary +* of a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstPointSet *BndBaseMesh( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* Interval method (over-rides the astBndBaseMesh method inherited from +* the Region class). + +* Description: +* This function returns a PointSet containing a set of points on the +* boundary of the intersection between the supplied Region and the +* supplied box. The points refer to the base Frame of the +* encapsulated FrameSet. If the boundary of the supplied Region does +* not intersect the supplied box, then a PointSet containing a single +* bad point is returned. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array holding the lower limits of the axis values +* within the required box. +* ubnd +* Pointer to an array holding the upper limits of the axis values +* within the required box. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the uncertainties which were +* supplied when the Region was created. +* +* If the Region does not intersect the supplied box, the returned +* PointSet will contain a single point with a value of AST__BAD on +* every axis. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstBox *box; + AstFrame *bfrm; + AstInterval *this_interval; + AstMapping *map; + AstPointSet *result; + double *lbndb; + double *ubndb; + double **ptr; + int closed; + int i; + int nbase; + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Store a pointer to the interval. */ + this_interval = (AstInterval *) this; + +/* If the Interval is effectively a Box, invoke the astBndBaseMesh + function on the equivalent Box. A pointer to the equivalent Box will + be stored in the Interval structure. */ + box = Cache( (AstInterval *) this, status ); + if( box ) { + result = astBndBaseMesh( box, lbnd, ubnd ); + +/* If the Interval is not equivalent to a Box (i.e. if one or more bounds + are missing)... */ + } else { + +/* Find the base frame box that just encloses the supplied current Frame + box. */ + map = astGetMapping( this->frameset, AST__CURRENT, AST__BASE ); + nbase = astGetNout( map ); + lbndb = astMalloc( sizeof(double)*nbase ); + ubndb = astMalloc( sizeof(double)*nbase ); + if( astOK ) { + for( i = 0; i < nbase; i++ ) { + astMapBox( map, lbnd, ubnd, 1, i, lbndb + i, ubndb + i, + NULL, NULL ); + } + +/* Create a Box that is like this Interval except that missing bounds are + inherited from the supplied limits. Check that the resulting box is + closed. */ + closed = 1; + for( i = 0; i < nbase; i++ ) { + if( this_interval->ubnd[ i ] != DBL_MAX ) ubndb[ i ] = this_interval->ubnd[ i ]; + if( this_interval->lbnd[ i ] != -DBL_MAX ) lbndb[ i ] = this_interval->lbnd[ i ]; + if( lbndb[ i ] > ubndb[ i ] ) closed = 0; + } + +/* Cannot create the required mesh if the box is not closed. */ + if( closed ) { + +/* Create the Box. */ + bfrm = astGetFrame( this->frameset, AST__BASE ); + box = astBox( bfrm, 1, lbndb, ubndb, NULL, "", status ); + +/* Create the required mesh. */ + result = astRegBaseMesh( box ); + +/* Free resources */ + bfrm = astAnnul( bfrm ); + box = astAnnul( box ); + +/* If the boundary of the supplied Region does not intersect the box, + return a PointSet containing a single bad position. */ + } else { + result = astPointSet( 1, nbase, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + for( i = 0; i < nbase; i++ ) ptr[ i ][ 0 ] = AST__BAD; + } + } + } + +/* Free resources. */ + map = astAnnul( map ); + lbndb = astFree( lbndb ); + ubndb = astFree( ubndb ); + } + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +static AstBox *Cache( AstInterval *this, int *status ){ +/* +* Name: +* Cache + +* Purpose: +* Calculate intermediate values and cache them in the Interval structure. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstBox *Cache( AstInterval *this, int *status ) + +* Class Membership: +* Interval member function + +* Description: +* This function uses the PointSet stored in the parent Region to calculate +* some intermediate values which are useful in other methods. These +* values are stored within the Interval structure. + +* Parameters: +* this +* Pointer to the Interval. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the Interval is equivalent to a Box, then a pointer to the +* equivalent Box is returned. This is a copy of the pointer stored in +* the Interval structure and should not be annulled. + +*/ + +/* Local Variables: */ + AstBox *bbox; /* Equivalent base Box */ + AstFrame *bfrm; /* Interval base Frame */ + AstFrame *cfrm; /* Interval current Frame */ + AstRegion *map; /* Interval base->current Mapping */ + AstRegion *reg; /* Pointer to this Region structure */ + AstRegion *unc; /* Pointer to uncertainty Region */ + double **ptr; /* Pointer to data holding all axis limits */ + double *lbnd; /* Pointer to array of lower axis limits */ + double *ubnd; /* Pointer to array of upper axis limits */ + int i; /* Axis index */ + int isBox; /* Is this Interval equivalent to a Box? */ + int nc; /* Number of base Frame axes */ + int neg; /* Is the equivalent Box negated? */ + +/* Check the global error status. Also return if the cached information + is up to date (i.e. not stale). */ + if( !this->stale || !astOK ) return this->box; + +/* Get a pointer to the Region structure */ + reg = (AstRegion *) this; + +/* The Interval structure contains a pointer to an equivalent Box + structure. This Box structure is created below if the Interval is + equivalent to a Box. Annul any previous box. */ + if( this->box ) this->box = astAnnul( this->box ); + +/* Get the number of axes in the base Frame of the FrameSet encapsulated + by the parent Region structure. */ + nc = astGetNin( reg->frameset ); + +/* Get a pointer to the array holding the axis limits held in the PointSet + encapsulated in the parent Region structure. */ + ptr = astGetPoints( reg->points ); + +/* Allocate memory to hold the limits organised per point rather than per + axis. */ + lbnd = astMalloc( sizeof( double )*(size_t)nc ); + ubnd = astMalloc( sizeof( double )*(size_t)nc ); + +/* Check these pointers can be used safely. */ + if( ubnd ) { + +/* See if the Interval is effectively a (possibly negated) Box. Assume it + is to begin with. */ + isBox = 1; + +/* Initialisation to prevent compiler warnings. */ + neg = 0; + +/* Check the limits on every axis. */ + for( i = 0; i < nc; i++ ) { + +/* Copy the axis limits into the allocated arrays (these are needed by the + Box constructor later on). */ + lbnd[ i ] = ptr[ i ][ 0 ]; + ubnd[ i ] = ptr[ i ][ 1 ]; + +/* The Interval is not a Box if any axis limit is missing. In this case + use -DBL_MAX or +DBL_MAX as the limit to be stored in the Interval + structure. */ + if( lbnd[ i ] == AST__BAD ) lbnd[ i ] = -DBL_MAX; + if( fabs( lbnd[ i ] ) == DBL_MAX ) isBox = 0; + + if( ubnd[ i ] == AST__BAD ) ubnd[ i ] = DBL_MAX; + if( fabs( ubnd[ i ] ) == DBL_MAX ) isBox = 0; + +/* If this is the first axis, note if the axis interval is included or + excluded. This is determined by whether the "lower limit" is greater + than or less than the "upper limit". If the axis interval is excluded + (lower limit greater than upper limit), then any equivalent Box will be + a negated Box (i.e. will represent the outside of a box rather than + the inside). */ + if( i == 0 ){ + neg = ( lbnd[ i ] > ubnd[ i ] ); + +/* The Interval is not a Box if the limits for this axis are not the same + way round as those of the first axis. */ + } else { + + if( neg ) { + if( lbnd[ i ] < ubnd[ i ] ) isBox = 0; + } else { + if( lbnd[ i ] > ubnd[ i ] ) isBox = 0; + } + + } + } + +/* If the Interval is effectively an unnegated Box, create the equivalent Box, + and store a pointer to it in the Interval structure. */ + if( isBox && !neg ) { + bfrm = astGetFrame( reg->frameset, AST__BASE ); + cfrm = astGetFrame( reg->frameset, AST__CURRENT ); + map = astGetMapping( reg->frameset, AST__BASE, AST__CURRENT ); + unc = astTestUnc( reg ) ? astGetUncFrm( reg, AST__BASE ) : NULL; + + bbox = astBox( bfrm, 1, lbnd, ubnd, unc, "", status ); + if( astIsAUnitMap( map ) ){ + this->box = astClone( bbox ); + } else { + this->box = astMapRegion( bbox, map, cfrm ); + } + + if( unc ) unc = astAnnul( unc ); + cfrm = astAnnul( cfrm ); + bfrm = astAnnul( bfrm ); + map = astAnnul( map ); + bbox = astAnnul( bbox ); + +/* If the supplied Interval has been negated, negate the equivalent Box. */ + if( astGetNegated( this ) ) astNegate( this->box ); + +/* If the supplied Interval is closed, ensure the equivalent Box is closed. */ + astSetClosed( this->box, astGetClosed( this ) ); + } + +/* Store the axis limits in the Interval structure. */ + if( this->lbnd ) astFree( this->lbnd ); + if( this->ubnd ) astFree( this->ubnd ); + this->lbnd = lbnd; + this->ubnd = ubnd; + } + +/* Indicate the cached information is no longer stale, and return a + pointer to any equivalent Box. */ + this->stale = 0; + return this->box; +} + +static int GetBounded( AstRegion *this, int *status ) { +/* +* Name: +* GetBounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* int GetBounded( AstRegion *this, int *status ) + +* Class Membership: +* Interval method (over-rides the astGetBounded method inherited from +* the Region class). + +* Description: +* This function returns a flag indicating if the Region is bounded. +* The implementation provided by the base Region class is suitable +* for Region sub-classes representing the inside of a single closed +* curve (e.g. Circle, Interval, Box, etc). Other sub-classes (such as +* CmpRegion, PointList, etc ) may need to provide their own +* implementations. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Region is bounded. Zero otherwise. + +*/ + +/* Local Variables: */ + int result; /* Returned result */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The unnegated Interval is bounded only if there is an equivalent Box + structure stored in the Interval structure. */ + if( Cache( (AstInterval *) this, status ) ) result = 1; + +/* Return the required pointer. */ + return result; +} + +static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) { +/* +* Name: +* GetDefUnc + +* Purpose: +* Obtain a pointer to the default uncertainty Region for a given Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstRegion *GetDefUnc( AstRegion *this, int *status ) + +* Class Membership: +* Interval member function (over-rides the astGetDefUnc protected +* method inherited from the Region class). + +* Description: +* This function returns a pointer to a Region which represents the +* default uncertainty associated with a position on the boundary of the +* given Region. The returned Region refers to the base Frame within the +* FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstBox *box; /* Pointer to equivalent Box */ + AstFrame *bfrm; /* Base Frame of supplied Region */ + AstInterval *this; /* Pointer to Interval structure */ + AstRegion *result; /* Returned pointer */ + double *lbnd; /* Ptr. to array holding axis lower bounds */ + double *ubnd; /* Ptr. to array holding axis upper bounds */ + double c; /* Central axis value */ + double hw; /* Half width of uncertainty interval */ + int i; /* Axis index */ + int nax; /* Number of base Frame axes */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Interval structure. */ + this = (AstInterval *) this_region; + +/* If this Interval is equivalent to a Box, get the default uncertainty + for the equivalent Box and return it. */ + box = Cache( this, status ); + if( box ) { + result = astGetDefUnc( box ); + +/* Otherwise, we use a box covering 1.0E-6 of each axis interval, centred on + the origin. */ + } else { + +/* Get a pointer to the base Frame. */ + bfrm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Get the number of base Frame axes. */ + nax = astGetNaxes( bfrm ); + +/* Allocate arrays to hold the bounds of the uncertainty Box. */ + lbnd = astMalloc( sizeof( double)*(size_t) nax ); + ubnd = astMalloc( sizeof( double)*(size_t) nax ); + if( astOK ) { + +/* Ensure cached information (e.g.bounds) is up to date. */ + Cache( this, status ); + +/* Do each axis in turn */ + for( i = 0; i < nax; i++ ) { + +/* If this axis has both limits, use 1.0E-6 of the difference between the + limits. */ + if( this->lbnd[ i ] != -DBL_MAX && + this->ubnd[ i ] != DBL_MAX ) { + hw = fabs( 0.5E-6*( this->ubnd[ i ] - this->lbnd[ i ] ) ); + c = 0.5*( this->ubnd[ i ] + this->lbnd[ i ] ); + if( hw == 0.0 ) hw = c*0.5E-6; + ubnd[ i ] = c + hw; + lbnd[ i ] = c - hw; + +/* Otherwise use zero. */ + } else { + ubnd[ i ] = 0.0; + lbnd[ i ] = 0.0; + } + } + +/* Create the Box. */ + result = (AstRegion *) astBox( bfrm, 1, lbnd, ubnd, NULL, "", status ); + } + +/* Free resources. */ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + bfrm = astAnnul( bfrm ); + } + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +void astInitIntervalVtab_( AstIntervalVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitIntervalVtab + +* Purpose: +* Initialise a virtual function table for a Interval. + +* Type: +* Protected function. + +* Synopsis: +* #include "interval.h" +* void astInitIntervalVtab( AstIntervalVtab *vtab, const char *name ) + +* Class Membership: +* Interval vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Interval class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAInterval) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->IntervalPoints = IntervalPoints; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_overlap = region->Overlap; + region->Overlap = Overlap; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_resetcache = region->ResetCache; + region->ResetCache = ResetCache; + + parent_setunc = region->SetUnc; + region->SetUnc = SetUnc; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping->MapMerge = MapMerge; + + region->RegCentre = RegCentre; + region->GetBounded = GetBounded; + region->GetDefUnc = GetDefUnc; + region->RegPins = RegPins; + region->RegTrace = RegTrace; + region->RegBaseMesh = RegBaseMesh; + region->BndBaseMesh = BndBaseMesh; + region->RegBaseBox = RegBaseBox; + region->RegBasePick = RegBasePick; + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "Interval", "Axis intervals" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +void IntervalPoints( AstInterval *this, double *lbnd, double *ubnd, + int *status) { +/* +*+ +* Name: +* astIntervalPoints + +* Purpose: +* Return the defining points of a Interval. + +* Type: +* Protected function. + +* Synopsis: +* #include "box.h" +* astIntervalPoints( AstInterval *this, double *lbnd, double *ubnd ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns the axis values at the points defining the +* supplied Interval. + +* Parameters: +* this +* Pointer to the Interval. +* lbnd +* A pointer to an array in which to return the "lbnd" values +* supplied when the Interval was constructed. These are in the +* base Frame of the encapsilated FrameSet. +* ubnd +* A pointer to an array in which to return the "ubnd" values +* supplied when the Interval was constructed. These are in the +* base Frame of the encapsilated FrameSet. + +* Notes: +* - It is assumed that the length of the supplied arrays is at least +* equal to the number of axes in the base frame of the encapsulated +* FrameSet. +*- +*/ + +/* Local Variables: */ + AstPointSet *pset; + double **ptr; + int nc; + int i; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Get a pointer to the PointSet holding the points defining the Interval. */ + pset = ((AstRegion *) this)->points; + +/* Get a pointer to the PointSet's data arrays. */ + ptr = astGetPoints( pset ); + +/* See how many axes each point in the PointSet has. */ + nc = astGetNcoord( pset ); + +/* Copy the axis values from the PointSet into the supplied arrays. */ + for( i = 0; i < nc; i++ ) { + lbnd[ i ] = ptr[ i ] [ 0 ]; + ubnd[ i ] = ptr[ i ] [ 1 ]; + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* Interval member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstInterval *this; /* Pointer to Interval structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the Interval structure. */ + this = (AstInterval *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->box, mode, extra, fail ); + + return result; + +} +#endif + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a Interval. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* Interval method (over-rides the protected astMapMerge method +* inherited from the Region class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated Interval in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated Interval with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated Interval which is to be merged with +* its neighbours. This should be a cloned copy of the Interval +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* Interval it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated Interval resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstInterval *oldint; /* Pointer to supplied Interval */ + AstMapping *map; /* Pointer to adjacent Mapping */ + AstMapping *new; /* Simplified or merged Region */ + int i1; /* Index of first Mapping merged */ + int i; /* Loop counter */ + int result; /* Result value to return */ + +/* Initialise. */ + result = -1; + i1 = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Interval. */ + oldint = (AstInterval *) this; + +/* First of all, see if the Interval can be replaced by a simpler Region, + without reference to the neighbouring Regions in the list. */ +/* =====================================================================*/ + +/* Try to simplify the Interval. If the pointer value has changed, we assume + some simplification took place. */ + new = astSimplify( oldint ); + if( new != (AstMapping *) oldint ) { + +/* Annul the Interval pointer in the list and replace it with the new Region + pointer, and indicate that the forward transformation of the returned + Region should be used (not really needed but keeps things clean). */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = new; + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the Interval itself could not be simplified, see if it can be merged + with the Regions on either side of it in the list. We can only merge + in parallel. */ +/* =====================================================================*/ + } else if( ! series ){ + new = astAnnul( new ); + +/* Attempt to merge the Interval with its lower neighbour (if any). */ + if( where > 0 ) { + i1 = where - 1; + map = ( *map_list )[ where - 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergeInterval( oldint, (AstRegion *) map, + 0, status ); + } + } + +/* If this did not produced a merged Region, attempt to merge the Interval + with its upper neighbour (if any). */ + if( !new && where < *nmap - 1 ) { + i1 = where; + map = ( *map_list )[ where + 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergeInterval( oldint, (AstRegion *) map, + 1, status ); + } + } + +/* If succesfull... */ + if( new ){ + +/* Annul the first of the two Mappings, and replace it with the merged + Region. Also clear the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = new; + ( *invert_list )[ i1 ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i1 + 1 ] ); + for ( i = i1 + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + } + + } else { + new = astAnnul( new ); + } + +/* Return the result. */ + return result; +} + +static AstRegion *MergeInterval( AstInterval *this, AstRegion *reg, + int intfirst, int *status ) { +/* +* Name: +* MergeInterval + +* Purpose: +* Attempt to merge a Interval with another Region to form a Region of +* higher dimensionality. + +* Type: +* Private function. + +* Synopsis: +* #include "box.h" +* AstRegion *MergeInterval( AstInterval *this, AstRegion *reg, +* int intfirst, int *status ) + +* Class Membership: +* Interval member function. + +* Description: +* This function attempts to combine the supplied Regions together +* into a Region of higher dimensionality. + +* Parameters: +* this +* Pointer to a Interval. +* reg +* Pointer to another Region. +* intfirst +* If non-zero, then the Interval axes are put first in the new Region. +* Otherwise, the other Region's axes are put first. +* status +* Pointer to the inherited status value. + +* Returned Value: +* A pointer to a new region, or NULL if the supplied Regions could +* not be merged. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* Pointer to base Frame for "result" */ + AstFrame *cfrm; /* Pointer to current Frame for "result" */ + AstFrame *frm_reg; /* Pointer to Frame from "reg" */ + AstFrame *frm_this; /* Pointer to Frame from "this" */ + AstMapping *bcmap; /* Base->current Mapping for "result" */ + AstMapping *map_reg; /* Base->current Mapping from "reg" */ + AstMapping *map_this; /* Base->current Mapping from "this" */ + AstMapping *sbunc; /* Simplified uncertainty */ + AstPointSet *pset_new; /* PointSet holding PointList axis values for new */ + AstPointSet *pset_reg; /* PointSet holding PointList axis values for reg */ + AstRegion *bunc; /* Base Frame uncertainty Region */ + AstRegion *new; /* Pointer to new Interval in base Frame */ + AstRegion *result; /* Pointer to returned Interval in current Frame */ + AstRegion *unc_reg; /* Current Frame uncertainty Region from "reg" */ + AstRegion *unc_this; /* Current Frame uncertainty Region from "this" */ + double **ptr_new; /* Pointers to arrays holding new axis values */ + double **ptr_reg; /* Pointers to arrays holding reg axis values */ + double *centre; /* Array to hold Interval centre axis values */ + double *corner; /* Array to hold Interval corner axis values */ + double *lbnd; /* Array to hold lower axis bounds */ + double *lbnd_unc; /* Array to hold uncertainty lower bounds */ + double *p; /* Pointer to next input value */ + double *q; /* Pointer to next output value */ + double *ubnd; /* Array to hold upper axis bounds */ + double *ubnd_unc; /* Array to hold uncertainty upper bounds */ + double fac_reg; /* Ratio of used to default MeshSize for "reg" */ + double fac_this; /* Ratio of used to default MeshSize for "this" */ + double temp; /* Temporary storage */ + int i; /* Loop count */ + int j; /* Loop count */ + int msz_reg; /* Original MeshSize for "reg" */ + int msz_reg_set; /* Was MeshSize originally set for "reg"? */ + int msz_this; /* Original MeshSize for "this" */ + int msz_this_set; /* Was MeshSize originally set for "this"? */ + int nax; /* Number of axes in "result" */ + int nax_reg; /* Number of axes in "reg" */ + int nax_this; /* Number of axes in "this" */ + int neg_reg; /* Negated attribute value for other supplied Region */ + int neg_this; /* Negated attribute value for supplied Interval */ + int npnt; /* Number of points in PointList */ + int ok; /* Can supplied Regions be merged? */ + +/* Initialise */ + result = NULL; + lbnd = NULL; + ubnd = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get the Closed attributes of the two Regions. They must be the same in + each Region if we are to merge the Regions. In addition, in order to + merge, either both Regions must have a defined uncertainty, or neither + Region must have a defined Uncertainty. */ + if( astGetClosed( this ) == astGetClosed( reg ) && + astTestUnc( this ) == astTestUnc( reg ) ) { + +/* Get the Nagated attributes of the two Regions. */ + neg_this = astGetNegated( this ); + neg_reg = astGetNegated( reg ); + +/* Get the number of axes in the two supplied Regions. */ + nax_reg = astGetNaxes( reg ); + nax_this = astGetNaxes( this ); + +/* If the Regions can be combined, get the number of axes the + combination will have. */ + nax = nax_reg + nax_this; + +/* Get the base Frames from the two Region FrameSets, and combine them + into a single CmpFrame that will be used to create any new Region. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + frm_reg = astGetFrame( reg->frameset, AST__BASE ); + + if( intfirst ) { + bfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + bfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + +/* Indicate we do not yet have a merged Region. */ + new = NULL; + +/* First attempt to merge with another Interval. The result will be an + Interval. Both Intervals must be un-negated. */ + if( astIsAInterval( reg ) && !neg_this && !neg_reg ) { + +/* Allocate memory to store the bounds of the returned Interval. */ + lbnd = astMalloc( sizeof( double )*(size_t) nax ); + ubnd = astMalloc( sizeof( double )*(size_t) nax ); + +/* Copy the limits from the supplied Intervals into the above arrays, + in the requested order. */ + if( intfirst ) { + astIntervalPoints( this, lbnd, ubnd ); + astIntervalPoints( reg, lbnd + nax_this, ubnd + nax_this ); + } else { + astIntervalPoints( reg, lbnd, ubnd ); + astIntervalPoints( this, lbnd + nax_reg, ubnd + nax_reg ); + } + +/* Create the new Interval, initially with no uncertainty. */ + new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "", + status ); + +/* Free resources .*/ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* Now attempt to merge with a Box. The result will be an Interval. Both + Regions must be un-negated. */ + } else if( astIsABox( reg ) && !neg_this && !neg_reg ) { + +/* Allocate memory to store the bounds of the returned Interval. */ + lbnd = astMalloc( sizeof( double )*(size_t) nax ); + ubnd = astMalloc( sizeof( double )*(size_t) nax ); + +/* Get the bounds from the Interval and add them into the above arrays. */ + if( intfirst ) { + astIntervalPoints( this, lbnd, ubnd ); + } else { + astIntervalPoints( this, lbnd + nax_reg, ubnd + nax_reg ); + } + +/* Copy the centre and corner from the supplied Box into the required part + of the above arrays. */ + if( intfirst ) { + centre = lbnd + nax_this; + corner = ubnd + nax_this; + } else { + centre = lbnd; + corner = ubnd; + } + astBoxPoints( reg, centre, corner ); + +/* Convert these centre and corner positions into upper and lower bounds. */ + if( astOK ) { + for( i = 0; i < nax_reg; i++ ) { + centre[ i ] = 2*centre[ i ] - corner[ i ]; + if( centre[ i ] > corner[ i ] ) { + temp = centre[ i ]; + centre[ i ] = corner[ i ]; + corner[ i ] = temp; + } + } + } + +/* Create the new Interval, initially with no uncertainty. */ + new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "", + status ); + +/* Free resources .*/ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* Now attempt to merge with a NullRegion. The result will be an Interval. + The NullRegion must be negated and the Interval must not. */ + } else if( astIsANullRegion( reg ) && !neg_this && neg_reg ) { + +/* Allocate memory to store the bounds of the returned Interval. */ + lbnd = astMalloc( sizeof( double )*(size_t) nax ); + ubnd = astMalloc( sizeof( double )*(size_t) nax ); + +/* Copy the limits from the supplied Interval into the above arrays. + Store bad values for the other axes indicating they are unbounded. */ + if( intfirst ) { + astIntervalPoints( this, lbnd, ubnd ); + for( i = nax_this; i < nax; i++ ) { + lbnd[ i ] = AST__BAD; + ubnd[ i ] = AST__BAD; + } + } else { + for( i = 0; i < nax_reg; i++ ) { + lbnd[ i ] = AST__BAD; + ubnd[ i ] = AST__BAD; + } + astIntervalPoints( this, lbnd + nax_reg, ubnd + nax_reg ); + } + +/* Create the new Interval, initially with no uncertainty. */ + new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "", + status ); + +/* Free resources .*/ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* Now attempt to merge with a PointList. The result will be a PointList. + Both Regions must be un-negated. */ + } else if( astIsAPointList( reg ) && !neg_this && !neg_reg ) { + +/* We can only do this if the Interval has zero width on each axis (i.e. + represents a point). Get the Interval bounds. */ + lbnd = astMalloc( sizeof( double )*(size_t) nax_this ); + ubnd = astMalloc( sizeof( double )*(size_t) nax_this ); + astRegBaseBox( this, lbnd, ubnd ); + +/* Get the size of the Interval's uncertainty region. */ + lbnd_unc = astMalloc( sizeof( double )*(size_t) nax_this ); + ubnd_unc = astMalloc( sizeof( double )*(size_t) nax_this ); + bunc = astGetUncFrm( this, AST__BASE ); + astGetRegionBounds( bunc, lbnd, ubnd ); + +/* Set "ok" to zero if the Interval does not have zero width on any axis. Here + "zero width" means a width less than half the uncertainty on the axis. + We also replace the lower bound values in the "lbnd" array by the central + values in the Interval. */ + if( astOK ) { + ok = 1; + for( i = 0; i < nax_this; i++ ) { + if( fabs( lbnd[ i ] - lbnd[ i ] ) > + 0.25*fabs( ubnd_unc[ i ] - lbnd_unc[ i ] ) ) { + ok = 0; + break; + } else { + lbnd[ i ] = 0.5*( lbnd[ i ] + ubnd[ i ] ); + } + } + +/* If the Interval is a point, we go on to create a new PointList. */ + if( ok ) { + +/* Get a PointSet holding the axis values in the supplied PointList data. + Also get the number of points in the PointSet and pointers to the arrays + holding the axis values. */ + astPointListPoints( reg, &pset_reg ); + npnt = astGetNpoint( pset_reg ); + ptr_reg = astGetPoints( pset_reg ); + +/* Create a new PointSet with room for the same number of points, but + with the extra required axes. Get pointers to its axis arrays. */ + pset_new = astPointSet( npnt, nax, "", status ); + ptr_new = astGetPoints( pset_new ); + +/* Copy the PointList axis values into the new PointSet, and then include + the extra axis values defined by the Interval to each point. */ + if( astOK ) { + + for( j = 0; j < nax_reg; j++ ) { + p = ptr_reg[ j ]; + q = ptr_new[ intfirst ? nax_this + j : j ]; + for( i = 0; i < npnt; i++ ) *(q++) = *(p++); + } + + for( j = 0; j < nax_this; j++ ) { + p = lbnd + j; + q = ptr_new[ intfirst ? j : nax_reg + j ]; + for( i = 0; i < npnt; i++ ) *(q++) = *p; + } + +/* Create the new PointList, initially with no uncertainty. */ + new = (AstRegion *) astPointList( bfrm, pset_new, NULL, + "", status ); + } + +/* Free resources .*/ + pset_new = astAnnul( pset_new ); + pset_reg = astAnnul( pset_reg ); + } + } + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + lbnd_unc = astFree( lbnd_unc ); + ubnd_unc = astFree( ubnd_unc ); + bunc = astAnnul( bunc ); + + } + +/* If a new Region was created above, propagate remaining attributes of + the supplied Region to it. */ + if( new ) { + astRegOverlay( new, this, 1 ); + +/* The above Prism constructors create the Prism with the correct value + for the Nagated attribute (i.e. zero). Ensure the above call to + astRegOverlay has not changed this. */ + astClearNegated( new ); + +/* If both the supplied Regions have uncertainty, assign the new Region an + uncertainty. */ + if( astTestUnc( this ) && astTestUnc( reg ) ) { + +/* Get the uncertainties from the two supplied Regions. */ + unc_this = astGetUncFrm( this, AST__BASE ); + unc_reg = astGetUncFrm( reg, AST__BASE ); + +/* Combine them into a single Region (a Prism), in the correct order. */ + if( intfirst ) { + bunc = (AstRegion *) astPrism( unc_this, unc_reg, "", status ); + } else { + bunc = (AstRegion *) astPrism( unc_reg, unc_this, "", status ); + } + +/* Attempt to simplify the Prism. */ + sbunc = astSimplify( bunc ); + +/* Use the simplified Prism as the uncertainty for the returned Region. */ + astSetUnc( new, sbunc ); + +/* Free resources. */ + sbunc = astAnnul( sbunc ); + bunc = astAnnul( bunc ); + unc_reg = astAnnul( unc_reg ); + unc_this = astAnnul( unc_this ); + } + +/* Get the current Frames from the two Region FrameSets, and combine them + into a single CmpFrame. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__CURRENT ); + frm_reg = astGetFrame( reg->frameset, AST__CURRENT ); + + if( intfirst ) { + cfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + cfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + +/* Get the base -> current Mappings from the two Region FrameSets, and + combine them into a single parallel CmpMap that connects bfrm and cfrm. */ + map_this = astGetMapping( ((AstRegion *) this)->frameset, AST__BASE, + AST__CURRENT ); + map_reg = astGetMapping( reg->frameset, AST__BASE, AST__CURRENT ); + + if( intfirst ) { + bcmap = (AstMapping *) astCmpMap( map_this, map_reg, 0, "", + status ); + } else { + bcmap = (AstMapping *) astCmpMap( map_reg, map_this, 0, "", + status ); + } + +/* Map the new Region into the new current Frame. */ + result = astMapRegion( new, bcmap, cfrm ); + +/* The filling factor in the returned is the product of the filling + factors for the two supplied Regions. */ + if( astTestFillFactor( reg ) || astTestFillFactor( this ) ) { + astSetFillFactor( result, astGetFillFactor( reg )* + astGetFillFactor( this ) ); + } + +/* If the MeshSize value is set in either supplied Region, set a value + for the returned Region which scales the default value by the + product of the scaling factors for the two supplied Regions. First see + if either MeshSize value is set. */ + msz_this_set = astTestMeshSize( this ); + msz_reg_set = astTestMeshSize( reg ); + if( msz_this_set || msz_reg_set ) { + +/* If so, get the two MeshSize values (one of which may be a default + value), and then clear them so that the default value will be returned + in future. */ + msz_this = astGetMeshSize( this ); + msz_reg = astGetMeshSize( reg ); + astClearMeshSize( this ); + astClearMeshSize( reg ); + +/* Get the ratio of the used MeshSize to the default MeshSize for both + Regions. */ + fac_this = (double)msz_this/(double)astGetMeshSize( this ); + fac_reg = (double)msz_reg/(double)astGetMeshSize( reg ); + +/* The MeshSize of the returned Returned is the default value scaled by + the product of the two ratios found above. */ + astSetMeshSize( result, fac_this*fac_reg*astGetMeshSize( result ) ); + +/* Re-instate the original MeshSize values for the supplied Regions (if + set) */ + if( msz_this_set ) astSetMeshSize( this, msz_this ); + if( msz_reg_set ) astSetMeshSize( reg, msz_reg ); + } + +/* Free remaining resources */ + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + map_this = astAnnul( map_this ); + map_reg = astAnnul( map_reg ); + bcmap = astAnnul( bcmap ); + new = astAnnul( new ); + cfrm = astAnnul( cfrm ); + } + bfrm = astAnnul( bfrm ); + + } + +/* If an error has occurred, annul the returned pointer. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int *OneToOne( AstMapping *map, int *status ){ +/* +* Name: +* OneToOne + +* Purpose: +* Does each output of the supplied Mapping depend on only one input? + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* int OneToOne( AstMapping *map, int *status ) + +* Class Membership: +* Interval method + +* Description: +* This function returns a flag indicating if the Mapping is 1-to-1. +* That is, if each output depends only on one input. + +* Parameters: +* map +* Pointer to the Mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the Mapping is 1-to-1, a pointer to an array of ints is returned +* (NULL is returned otherwise). There is one int for each output of +* the supplied Mapping. The value of each int is the index of the +* corresponding input which feeds the output. The array should be +* freed using astFree when no longer needed. + +*/ + +/* Local Variables: */ + int *result; + const char *class; + int nout; + int i; + int *tt; + AstMapping *tmap; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the number of outputs for the Mapping. */ + nout = astGetNout( map ); + +/* The Mapping cannot be 1-to-1 if the number of inputs is different.*/ + if( astGetNin( map ) == nout ) { + +/* Allocate an output array on the assumption that the Mapping is 1-to-1. */ + result = astMalloc( sizeof( int )*(size_t) nout ); + if( result ) { + +/* Check known specal cases for speed. */ + class = astGetClass( map ); + if( !strcmp( class, "WinMap" ) || + !strcmp( class, "ZoomMap" ) || + !strcmp( class, "UnitMap" ) || + !strcmp( class, "ShiftMap" ) ){ + +/* Each output is fed by the corresponding input for these classes of + Mapping. */ + for( i = 0; i < nout; i++ ) result[ i ] = i; + +/* Now do the general case. */ + } else { + +/* Loop round each input axis. */ + for( i = 0; i < nout; i++ ) { + +/* Use astMapSplit to see if this input corresponds to a single output. */ + tt = astMapSplit( map, 1, &i, &tmap ); + +/* If not, annul the returned array and break. */ + if( !tmap ) { + result = astFree( result ); + break; + +/* If so, store the index of the corresponding input in the returned + array and free resources. */ + } else { + result[ tt[ 0 ] ] = i; + tt = astFree( tt ); + if( astGetNout( tmap ) != 1 ) result = astFree( result ); + tmap = astAnnul( tmap ); + if( !result ) break; + } + } + } + } + } + +/* Return the result */ + return result; +} + +static int Overlap( AstRegion *this, AstRegion *that, int *status ){ +/* +* Name: +* Overlap + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* int Overlap( AstRegion *this, AstRegion *that, int *status ) + +* Class Membership: +* Interval member function (over-rides the astOverlap method inherited +* from the Region class). + +* Description: +* This function returns an integer value indicating if the two +* supplied Regions overlap. The two Regions are converted to a commnon +* coordinate system before performing the check. If this conversion is +* not possible (for instance because the two Regions represent areas in +* different domains), then the check cannot be performed and a zero value +* is returned to indicate this. + +* Parameters: +* this +* Pointer to the first Region. +* that +* Pointer to the second Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* astOverlap() +* A value indicating if there is any overlap between the two Regions. +* Possible values are: +* +* 0 - The check could not be performed because the second Region +* could not be mapped into the coordinate system of the first +* Region. +* +* 1 - There is no overlap between the two Regions. +* +* 2 - The first Region is completely inside the second Region. +* +* 3 - The second Region is completely inside the first Region. +* +* 4 - There is partial overlap between the two Regions. +* +* 5 - The Regions are identical. +* +* 6 - The second Region is the negation of the first Region. + +* Notes: +* - The returned values 5 and 6 do not check the value of the Closed +* attribute in the two Regions. +* - A value of zero will be returned if this function is invoked with the +* AST error status set, or if it should fail for any reason. + +*/ + +/* Local Variables: */ + AstFrame *frm; + AstFrameSet *fs; + AstMapping *map; + AstMapping *map1; + AstMapping *map2; + AstMapping *map3; + AstMapping *smap; + AstMapping *tmap; + AstPointSet *pset_that; + AstRegion *unc_temp; + AstRegion *unc_that; + AstRegion *unc_this; + double **ptr_that; + double **ptr_thato; + double **ptr_this; + double *lbndu_that; + double *lbndu_this; + double *ubndu_that; + double *ubndu_this; + double err; + double err_that; + double err_this; + double lb_that; + double lb_this; + double tmp; + double ub_that; + double ub_this; + int *outperm; + int ic; + int inc_that; + int inc_this; + int lb_equal; + int nc; + int neg_that; + int neg_this; + int ov; + int result; + int ub_equal; + + static int newResult[ 5 ][ 5 ] = { { 1, 1, 1, 1, 1}, + { 1, 2, 4, 4, 2}, + { 1, 4, 3, 4, 3}, + { 1, 4, 4, 4, 4}, + { 1, 2, 3, 4, 5} }; + +/* Initialise */ + result = 0; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* If both Regions are Intervals, we provide a specialised implementation. + The implementation in the parent Region class assumes that at least one of + the two Regions can be represented using a finite mesh of points on the + boundary which is not the case with Intervals. The implementation in this + class sees if the Mapping between the base Frames of the Intervals allows + the axis limits to be transferred from one Frame ot the other. */ + if( astIsAInterval( this ) && astIsAInterval( that ) ) { + +/* Get a FrameSet which connects the Frame represented by the second Interval + to the Frame represented by the first Interval. Check that the conection is + defined. */ + fs = astConvert( that, this, "" ); + if( fs ) { + +/* Get a pointer to the Mapping from base to current Frame in the second + Interval */ + map1 = astGetMapping( that->frameset, AST__BASE, AST__CURRENT ); + +/* Get the Mapping from the current Frame of the second Interval to the + current Frame of the first Interval. */ + map2 = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Get a pointer to the Mapping from current to base Frame in the first + Interval. */ + map3 = astGetMapping( this->frameset, AST__CURRENT, AST__BASE ); + +/* Combine these Mappings to get the Mapping from the base Frame of the + second Interval to the base Frame of the first Interval. */ + tmap = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + map = (AstMapping *) astCmpMap( tmap, map3, 1, "", status ); + +/* Simplify this Mapping. */ + smap = astSimplify( map ); + +/* We can only proceed if each output of the simplified Mapping depends + on only one input. Test this. */ + outperm = OneToOne( smap, status ); + if( outperm ){ + +/* Get the uncertainty Regions for both Intervals, expressed in the base + Frames of the Intervals. */ + unc_this = astGetUncFrm( this, AST__BASE ); + unc_temp = astGetUncFrm( that, AST__BASE ); + +/* Map the uncertainty Region for the second Interval from the base Frame + of the second Interval into the base Frame of the first Interval. */ + frm = astGetFrame( this->frameset, AST__BASE ); + unc_that = astMapRegion( unc_temp, smap, frm ); + +/* Get the bounding boxes of the two uncertainty Regions in the base + Frame of the first Interval. */ + nc = astGetNaxes( frm ); + lbndu_this = astMalloc( sizeof( double )*(size_t)nc ); + ubndu_this = astMalloc( sizeof( double )*(size_t)nc ); + astGetRegionBounds( unc_this, lbndu_this, ubndu_this ); + + lbndu_that = astMalloc( sizeof( double )*(size_t)nc ); + ubndu_that = astMalloc( sizeof( double )*(size_t)nc ); + astGetRegionBounds( unc_that, lbndu_that, ubndu_that ); + +/* Transform the PointSet holding the limits for the second Interval into + the Frame of the first Interval. */ + pset_that = astTransform( smap, that->points, 1, NULL ); + +/* Get pointers for accesing the limits of the two Intervals, expressed + in a common Frame (the base Frame of the first Interval). */ + ptr_that = astGetPoints( pset_that ); + ptr_thato = astGetPoints( that->points ); + ptr_this = astGetPoints( this->points ); + if( astOK ) { + +/* Check the limits on each base Frame axis in turn. */ + for( ic = 0; ic < nc; ic++ ) { + +/* Get the widths of the two uncertainty boxes on this axis. */ + err_this = ubndu_this[ ic ] - lbndu_this[ ic ]; + err_that = ubndu_that[ ic ] - lbndu_that[ ic ]; + +/* Add this together in quadrature to get the tolerance for two values on + the current axis to be considered equal. */ + err = sqrt( err_that*err_that + err_this*err_this ); + +/* Get the limits on this axis from both Intervals. */ + lb_this = ptr_this[ ic ][ 0 ]; + ub_this = ptr_this[ ic ][ 1 ]; + lb_that = ptr_that[ ic ][ 0 ]; + ub_that = ptr_that[ ic ][ 1 ]; + +/* The limits for "that" have been mapped, which may have resulted in + them being swapped. We need to unswap them in this case to prevent the + swapping being used as an indication of a desire to use an excluded + interval rather than an included interval. */ + if( lb_that != AST__BAD && ub_that != AST__BAD ) { + if( ptr_thato[ ic ][ 0 ] < ptr_thato[ ic ][ 1 ] ) { + if( lb_that > ub_that ) { + tmp = lb_that; + lb_that = ub_that; + ub_that = tmp; + } + } else { + if( lb_that < ub_that ) { + tmp = lb_that; + lb_that = ub_that; + ub_that = tmp; + } + } + } + +/* If the regions are not closed, reduce the limits by the smallest + amount possible. */ + if( !astGetClosed( that ) ) { + if( lb_that != AST__BAD && lb_that < DBL_MAX ) + lb_that += DBL_EPSILON*fabs(lb_that); + if( ub_that != AST__BAD && ub_that > -DBL_MAX ) + ub_that -= DBL_EPSILON*fabs(ub_that); + } + if( !astGetClosed( this ) ) { + if( lb_this != AST__BAD && lb_this < DBL_MAX ) + lb_this += DBL_EPSILON*fabs(lb_this); + if( ub_this != AST__BAD && ub_this > -DBL_MAX ) + ub_this -= DBL_EPSILON*fabs(ub_this); + } + +/* Replace any missing limits with suitable extreme values */ + if( lb_this == AST__BAD ) lb_this = -DBL_MAX; + if( ub_this == AST__BAD ) ub_this = DBL_MAX; + if( lb_that == AST__BAD ) lb_that = -DBL_MAX; + if( ub_that == AST__BAD ) ub_that = DBL_MAX; + +/* If the bounds are the wrong way round (indicating an excluded rather + than an included axis range), swap them. Also set a flag indicating if + the limits define an included or excluded range. */ + inc_this = ( lb_this <= ub_this ); + if( !inc_this ) { + tmp = lb_this; + lb_this = ub_this; + ub_this = tmp; + } + + inc_that = ( lb_that <= ub_that ); + if( !inc_that ) { + tmp = lb_that; + lb_that = ub_that; + ub_that = tmp; + } + + +/* Are the lower limits from the two Intervals effectively equal? Take care + about DBL_MAX values causing overflow. */ + lb_equal = astEQUALS( lb_this, lb_that, 1.0E9 ); + + if( !lb_equal && fabs(lb_this) != DBL_MAX && + fabs(lb_that) != DBL_MAX ) { + lb_equal = ( fabs( lb_this - lb_that) <= err ); + } + +/* Are the upper limits from the two Intervals effectively equal? */ + ub_equal = astEQUALS( ub_this, ub_that, 1.0E9 ); + if( !ub_equal && fabs(ub_this) != DBL_MAX && + fabs(ub_that) != DBL_MAX ) { + ub_equal = ( fabs( ub_this - ub_that) <= err ); + } + + + +/* If both the limits on this axis are effectively equal for the two Intervals, + set "ov" to 5 if both Interval ranges are inclusive or both are exclusive, + and set "ov" to 6 if one Interval range is exclusive and the other is + inclusive. */ + if( lb_equal && ub_equal ) { + ov = ( inc_this == inc_that ) ? 5 : 6; + +/* See if the limits on this axis indicate overlap for the two Intervals. "ov" + is set to 1 if there is no overlap, 2 if the first Interval range is + completely inside the second Interval range, 3 if the second Interval + range is completely inside the first Interval range, and 4 if there is + partial overlap between the Interval ranges. */ + } else if( inc_this ) { + if( inc_that ) { + if( lb_that <= lb_this && ub_that >= ub_this ) { + ov = 2; + } else if( lb_that >= lb_this && ub_that <= ub_this ) { + ov = 3; + } else if( ub_that >= lb_this && lb_that <= ub_this ) { + ov = 4; + } else { + ov = 1; + } + + } else { + + if( lb_that <= lb_this && ub_that >= ub_this ) { + ov = 1; + } else if( lb_that >= ub_this || ub_that <= lb_this ) { + ov = 2; + } else if( lb_this == -DBL_MAX && ub_this == DBL_MAX ) { + ov = 3; + } else { + ov = 4; + } + } + + } else { + + if( inc_that ) { + if( lb_this <= lb_that && ub_this >= ub_that ) { + ov = 1; + } else if( lb_this >= ub_that || ub_this <= lb_that ) { + ov = 3; + } else if( lb_that == -DBL_MAX && ub_that == DBL_MAX ) { + ov = 2; + } else { + ov = 4; + } + + } else { + ov = 4; + } + } + +/* The returned value is initialised on the basis of the first axis + overlap. */ + if( ic == 0 ) { + result = ov; + +/* For subsequent axes, combine the old result value with the new ov value + to get the new result value. */ + } else { + result = newResult[ result - 1 ][ ov - 1 ]; + } + +/* If we now know there is no overlap, there is no point in checking any + remaining axes. */ + if( result == 1 ) break; + + } + +/* The above logic assumed that neither of the Intervals has been negated. + Decide on the value to return, taking into account whether either of + the Intervals has been negated. */ + neg_this = astGetNegated( this ); + neg_that = astGetNegated( that ); + + if( result == 1 ) { + if( neg_this ) { + result = neg_that ? 4 : 3; + } else if( neg_that ){ + result = 2; + } + + } else if( result == 2) { + if( neg_this ) { + result = neg_that ? 3 : 4; + } else if( neg_that ){ + result = 1; + } + + } else if( result == 3) { + if( neg_this ) { + result = neg_that ? 2 : 1; + } else if( neg_that ){ + result = 4; + } + + } else if( result == 4) { + result = 4; + + } else if( result == 5) { + if( neg_this ) { + result = neg_that ? 5 : 6; + } else if( neg_that ){ + result = 6; + } + } + } + +/* Free resources. */ + pset_that = astAnnul( pset_that ); + unc_this = astAnnul( unc_this ); + unc_that = astAnnul( unc_that ); + unc_temp = astAnnul( unc_temp ); + frm = astAnnul( frm ); + lbndu_this = astFree( lbndu_this ); + ubndu_this = astFree( ubndu_this ); + lbndu_that = astFree( lbndu_that ); + ubndu_that = astFree( ubndu_that ); + outperm = astFree( outperm ); + } + + smap = astAnnul( smap ); + map = astAnnul( map ); + tmap = astAnnul( tmap ); + map3 = astAnnul( map3 ); + map2 = astAnnul( map2 ); + map1 = astAnnul( map1 ); + fs = astAnnul( fs ); + } + } + +/* If overlap could not be determined using the above implementation, try + using the implementation inherited from the parent Region class. */ + if( !result ) result = (*parent_overlap)( this, that, status ); + +/* If not OK, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* Interval member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstInterval *this; + int nax; + int i; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Interval structure */ + this = (AstInterval *) this_region; + +/* Ensure the cached bounds are up to date. */ + Cache( this, status ); + +/* Copy the cached bounds into the supplied arrays. */ + nax = astGetNin( this_region->frameset ); + for( i = 0; i < nax; i++ ) { + lbnd[ i ] = this->lbnd[ i ]; + ubnd[ i ] = this->ubnd[ i ]; + } +} + +static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* Interval member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the accuracies which were +* supplied when the Region was created. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Local Variables: */ + AstBox *box; /* The equivalent Box */ + AstPointSet *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the Interval is effectively a Box, invoke the astRegBaseMesh + function on the equivalent Box. A pointer to the equivalent Box will + be stored in the Interval structure. */ + box = Cache( (AstInterval *) this_region, status ); + if( box ) { + result = astRegBaseMesh( box ); + +/* If the Interval is not equivalent to a Box, report an error. */ + } else { + astError( AST__INTER, "astRegBaseMesh(%s): The %s given is " + "unbounded and therefore no boundary mesh can be " + "produced (internal AST programming error).", status, + astGetClass( this_region ), astGetClass( this_region ) ); + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, + const int *axes, int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* Interval member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* The base Frame in the supplied Region */ + AstFrame *frm; /* The base Frame in the returned Region */ + AstPointSet *pset; /* Holds axis values defining the supplied Region */ + AstRegion *bunc; /* The uncertainty in the supplied Region */ + AstRegion *result; /* Returned Region */ + AstRegion *unc; /* The uncertainty in the returned Region */ + double **ptr; /* Holds axis values defining the supplied Region */ + double *lbnd; /* Base Frm lower bound axis values */ + double *ubnd; /* Base Frm upper bound axis values */ + int i; /* Index of axis within returned Region */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the base Frame of the encapsulated FrameSet. */ + bfrm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Create a Frame by picking the selected axes from the base Frame of the + encapsulated FrameSet. */ + frm = astPickAxes( bfrm, naxes, axes, NULL ); + +/* Get the uncertainty Region (if any) within the base Frame of the supplied + Region, and select the required axes from it. If the resulting Object + is not a Region, annul it so that the returned Region will have no + uncertainty. */ + if( astTestUnc( this_region ) ) { + bunc = astGetUncFrm( this_region, AST__BASE ); + unc = astPickAxes( bunc, naxes, axes, NULL ); + bunc = astAnnul( bunc ); + + if( ! astIsARegion( unc ) ) unc = astAnnul( unc ); + + } else { + unc = NULL; + } + +/* Get pointers to the coordinate data in the parent Region structure. */ + pset = this_region->points; + ptr = astGetPoints( pset ); + +/* Get space to hold the limits of the Interval in the new Frame. */ + lbnd = astMalloc( sizeof( *lbnd )*naxes ); + ubnd = astMalloc( sizeof( *ubnd )*naxes ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the limits for the selected axes into the arrays allocated above. */ + for( i = 0; i < naxes; i++ ) { + lbnd[ i ] = ptr[ axes[ i ] ][ 0 ]; + ubnd[ i ] = ptr[ axes[ i ] ][ 1 ]; + } + +/* Create the new Interval. */ + result = (AstRegion *) astInterval( frm, lbnd, ubnd, unc, "", status ); + + } + +/* Free resources */ + frm = astAnnul( frm ); + bfrm = astAnnul( bfrm ); + if( unc ) unc = astAnnul( unc ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static double *RegCentre( AstRegion *this_region, double *cen, double **ptr, + int index, int ifrm, int *status ){ +/* +* Name: +* RegCentre + +* Purpose: +* Re-centre a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* double *RegCentre( AstRegion *this, double *cen, double **ptr, +* int index, int ifrm, int *status ) + +* Class Membership: +* Interval member function (over-rides the astRegCentre protected +* method inherited from the Region class). + +* Description: +* This function shifts the centre of the supplied Region to a +* specified position, or returns the current centre of the Region. + +* Parameters: +* this +* Pointer to the Region. +* cen +* Pointer to an array of axis values, giving the new centre. +* Supply a NULL value for this in order to use "ptr" and "index" to +* specify the new centre. +* ptr +* Pointer to an array of points, one for each axis in the Region. +* Each pointer locates an array of axis values. This is the format +* returned by the PointSet method astGetPoints. Only used if "cen" +* is NULL. +* index +* The index of the point within the arrays identified by "ptr" at +* which is stored the coords for the new centre position. Only used +* if "cen" is NULL. +* ifrm +* Should be AST__BASE or AST__CURRENT. Indicates whether the centre +* position is supplied and returned in the base or current Frame of +* the FrameSet encapsulated within "this". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If both "cen" and "ptr" are NULL then a pointer to a newly +* allocated dynamic array is returned which contains the centre +* coords of the Region. This array should be freed using astFree when +* no longer needed. If either of "ptr" or "cen" is not NULL, then a +* NULL pointer is returned. + +* Notes: +* - Some Region sub-classes do not have a centre. Such classes will report +* an AST__INTER error code if this method is called with either "ptr" or +* "cen" not NULL. If "ptr" and "cen" are both NULL, then no error is +* reported if this method is invoked on a Region of an unsuitable class, +* but NULL is always returned. + +*/ + +/* Local Variables: */ + AstInterval *this; /* Pointer to Interval structure */ + AstBox *box; /* Pointer to equivalent Box structure */ + double **bptr; /* Data pointers for Region PointSet */ + double *lbnd; /* Pointer to new lower bound values */ + double *ubnd; /* Pointer to new upper bound values */ + double *result; /* Returned pointer */ + int i; /* Coordinate index */ + int nax; /* Number of axes */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Interval structure. */ + this = (AstInterval *) this_region; + +/* The Interval can only be re-centred if it is effectively a Box. */ + box = Cache( (AstInterval *) this_region, status ); + if( box ) { + +/* If the centre is being changed... */ + if( cen || ptr ) { + +/* Set the new centre in the equivalent box. */ + astRegCentre( box, cen, ptr, index, ifrm ); + +/* Get the new base Frame bounds from the Box. */ + nax = astGetNin( this_region->frameset ); + lbnd = astMalloc( sizeof( double )*nax ); + ubnd = astMalloc( sizeof( double )*nax ); + astRegBaseBox( box, lbnd, ubnd ); + +/* Store these bounds in the Interval structure. */ + bptr = astGetPoints( this_region->points ); + if( astOK ) { + for( i = 0; i < nax; i++ ) { + bptr[ i ][ 0 ] = lbnd[ i ]; + bptr[ i ][ 1 ] = ubnd[ i ]; + } + } + +/* Free resources. */ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* If the centre is not being changed, just invoke the method on the + equivalent box. */ + } else { + result = astRegCentre( box, NULL, NULL, 0, AST__BASE ); + } + +/* If the Interval is not equivalent to a Box, report an error */ + } else if( cen || ptr ) { + astError( AST__REGCN, "astRegCentre(%s): The supplied %s is not a " + "closed Interval and so cannot be re-centred.", status, + astGetClass( this ), astGetClass( this ) ); + } + +/* Return the result. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Interval. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* Interval member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Interval. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Interval "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Interval. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstBox *box; /* The equivalent Box */ + AstInterval *large_int; /* Interval slightly larger than "this" */ + AstInterval *small_int; /* Interval slightly smaller than "this" */ + AstInterval *this; /* Pointer to the Interval structure. */ + AstFrame *frm; /* Base Frame in supplied Interval */ + AstPointSet *ps1; /* Points masked by larger Interval */ + AstPointSet *ps2; /* Points masked by larger and smaller Intervals */ + AstRegion *tunc; /* Uncertainity Region from "this" */ + double **ptr; /* Pointer to axis values in "ps2" */ + double *large_lbnd; /* Lower bounds of larger interval */ + double *large_ubnd; /* Upper bounds of larger interval */ + double *lbnd_tunc; /* Lower bounds of "this" uncertainty Region */ + double *lbnd_unc; /* Lower bounds of supplied uncertainty Region */ + double *p; /* Pointer to next axis value */ + double *safe; /* An interior point in "this" */ + double *small_lbnd; /* Lower bounds of smaller interval */ + double *small_ubnd; /* Upper bounds of smaller interval */ + double *ubnd_tunc; /* Upper bounds of "this" uncertainty Region */ + double *ubnd_unc; /* Upper bounds of supplied uncertainty Region */ + double *wid; /* Widths of "this" border */ + double lb; /* Lower bound */ + double ub; /* Upper bound */ + double t; /* Swap space */ + double w; /* Width */ + int i; /* Axis index */ + int j; /* Point index */ + int nc; /* No. of axes in Interval base frame */ + int np; /* No. of supplied points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the Interval structure. */ + this = (AstInterval *) this_region; + +/* If the Interval is effectively a Box, invoke the astRegPins function on + the equivalent Box. A pointer to the equivalent Box will be stored in the + Interval structure. */ + box = Cache( this, status ); + if( box ) return astRegPins( box, pset, unc, mask ); + +/* Arrive here only if the Interval is not equivalent to a box (i.e. has + at least one infinite boundary). Get the number of base Frame axes in the + Interval, and check the supplied PointSet has the same number of axis + values per point. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + nc = astGetNaxes( frm ); + if( astGetNcoord( pset ) != nc && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axis " + "values per point (%d) in the supplied PointSet - should be " + "%d (internal AST programming error).", status, astGetClass( this ), + astGetNcoord( pset ), nc ); + } + +/* Get the number of axes in the uncertainty Region and check it is the + same as above. */ + if( unc && astGetNaxes( unc ) != nc && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axes (%d) " + "in the supplied uncertainty Region - should be " + "%d (internal AST programming error).", status, astGetClass( this ), + astGetNaxes( unc ), nc ); + } + +/* Get the centre of the region in the base Frame. We use this as a "safe" + interior point within the region. */ + safe = astRegCentre( this, NULL, NULL, 0, AST__BASE ); + +/* We now find the maximum distance on each axis that a point can be from + the boundary of the Interval for it still to be considered to be on the + boundary. First get the Region which defines the uncertainty within the + Interval being checked (in its base Frame), re-centre it on the interior + point found above (to avoid problems if the uncertainty region straddles + a discontinuity), and get its bounding box. */ + tunc = astGetUncFrm( this, AST__BASE ); + if( safe ) astRegCentre( tunc, safe, NULL, 0, AST__CURRENT ); + lbnd_tunc = astMalloc( sizeof( double )*(size_t) nc ); + ubnd_tunc = astMalloc( sizeof( double )*(size_t) nc ); + astGetRegionBounds( tunc, lbnd_tunc, ubnd_tunc ); + +/* Also get the Region which defines the uncertainty of the supplied + points and get its bounding box. First re-centre the uncertainty at the + interior position to avoid problems from uncertainties that straddle a + discontinuity. */ + if( unc ) { + if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT ); + lbnd_unc = astMalloc( sizeof( double )*(size_t) nc ); + ubnd_unc = astMalloc( sizeof( double )*(size_t) nc ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + } else { + lbnd_unc = NULL; + ubnd_unc = NULL; + } + +/* The required border width for each axis is half of the total width of + the two bounding boxes. Use a zero sized box "unc" if no box was supplied. */ + wid = astMalloc( sizeof( double )*(size_t) nc ); + large_lbnd = astMalloc( sizeof( double )*(size_t) nc ); + large_ubnd = astMalloc( sizeof( double )*(size_t) nc ); + small_lbnd = astMalloc( sizeof( double )*(size_t) nc ); + small_ubnd = astMalloc( sizeof( double )*(size_t) nc ); + if( small_ubnd ) { + if( unc ) { + for( i = 0; i < nc; i++ ) { + wid[ i ] = 0.5*( fabs( astAxDistance( frm, i + 1, lbnd_tunc[ i ], + ubnd_tunc[ i ] ) ) + + fabs( astAxDistance( frm, i + 1, lbnd_unc[ i ], + ubnd_unc[ i ] ) ) ); + } + } else { + for( i = 0; i < nc; i++ ) { + wid[ i ] = 0.5*fabs( astAxDistance( frm, i + 1, lbnd_tunc[ i ], + ubnd_tunc[ i ] ) ); + } + } + +/* Create two new Intervals, one of which is larger than "this" by the widths + found above, and the other of which is smaller than "this" by the widths + found above. */ + for( i = 0; i < nc; i++ ) { + lb = this->lbnd[ i ]; + ub = this->ubnd[ i ]; + if( lb > ub ) { + t = ub; + ub = lb; + lb = t; + } + + w = fabs( wid[ i ] ); + if( lb != -DBL_MAX ){ + large_lbnd[ i ] = lb - w; + small_lbnd[ i ] = lb + w; + } else { + large_lbnd[ i ] = AST__BAD; + small_lbnd[ i ] = AST__BAD; + } + + if( ub != DBL_MAX ){ + large_ubnd[ i ] = ub + w; + small_ubnd[ i ] = ub - w; + } else { + large_ubnd[ i ] = AST__BAD; + small_ubnd[ i ] = AST__BAD; + } + + if( small_lbnd[ i ] > small_ubnd[ i ] ) { + small_lbnd[ i ] = small_ubnd[ i ]; + } + } + + large_int = astInterval( frm, large_lbnd, large_ubnd, NULL, "", status ); + small_int = astInterval( frm, small_lbnd, small_ubnd, NULL, "", status ); + +/* Negate the smaller interval.*/ + astNegate( small_int ); + +/* Points are on the boundary of "this" if they are inside both the large + interval and the negated small interval. First transform the supplied + PointSet using the large interval, then transform them using the negated + smaller Interval. */ + ps1 = astTransform( large_int, pset, 1, NULL ); + ps2 = astTransform( small_int, ps1, 1, NULL ); + +/* Get a point to the resulting axis values, and the number of axis + values per axis. */ + ptr = astGetPoints( ps2 ); + np = astGetNpoint( ps2 ); + +/* If a mask array is to be returned, create one. */ + if( mask ) { + *mask = astMalloc( sizeof(int)*(size_t) np ); + +/* Check all the resulting points, setting mask values for all of them. */ + if( astOK ) { + +/* Initialise the mask elements on the basis of the first axis values */ + result = 1; + p = ptr[ 0 ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } else { + (*mask)[ j ] = 1; + } + } + +/* Now check for bad values on other axes. */ + for( i = 1; i < nc; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ j ] = 0; + } + } + } + } + +/* If no output mask is to be made, we can break out of the check as soon + as the first bad value is found. */ + } else if( astOK ) { + result = 1; + for( i = 0; i < nc && result; i++ ) { + p = ptr[ i ]; + for( j = 0; j < np; j++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + break; + } + } + } + } + +/* Free resources. */ + large_int = astAnnul( large_int ); + small_int = astAnnul( small_int ); + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + } + + tunc = astAnnul( tunc ); + frm = astAnnul( frm ); + lbnd_tunc = astFree( lbnd_tunc ); + ubnd_tunc = astFree( ubnd_tunc ); + if( unc ) lbnd_unc = astFree( lbnd_unc ); + if( unc ) ubnd_unc = astFree( ubnd_unc ); + wid = astFree( wid ); + large_lbnd = astFree( large_lbnd ); + large_ubnd = astFree( large_ubnd ); + small_lbnd = astFree( small_lbnd ); + small_ubnd = astFree( small_ubnd ); + safe = astFree( safe ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr, + int *status ){ +/* +*+ +* Name: +* RegTrace + +* Purpose: +* Return requested positions on the boundary of a 2D Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* int astTraceRegion( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* Interval member function (overrides the astTraceRegion method +* inherited from the parent Region class). + +* Description: +* This function returns positions on the boundary of the supplied +* Region, if possible. The required positions are indicated by a +* supplied list of scalar parameter values in the range zero to one. +* Zero corresponds to some arbitrary starting point on the boundary, +* and one corresponds to the end (which for a closed region will be +* the same place as the start). + +* Parameters: +* this +* Pointer to the Region. +* n +* The number of positions to return. If this is zero, the function +* returns without action (but the returned function value still +* indicates if the method is supported or not). +* dist +* Pointer to an array of "n" scalar parameter values in the range +* 0 to 1.0. +* ptr +* A pointer to an array of pointers. The number of elements in +* this array should equal tthe number of axes in the Frame spanned +* by the Region. Each element of the array should be a pointer to +* an array of "n" doubles, in which to return the "n" values for +* the corresponding axis. The contents of the arrays are unchanged +* if the supplied Region belongs to a class that does not +* implement this method. + +* Returned Value: +* Non-zero if the astTraceRegion method is implemented by the class +* of Region supplied, and zero if not. + +*- +*/ + +/* Local Variables; */ + AstBox *box; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( ! astOK ) return result; + +/* If the Interval is effectively a Box, invoke the astRegTrace function on + the equivalent Box. A pointer to the equivalent Box will be stored in the + Interval structure. */ + box = Cache( (AstInterval *) this_region, status ); + if( box ) result = astRegTrace( box, n, dist, ptr ); + +/* Return the result. */ + return result; +} + + + +static void ResetCache( AstRegion *this, int *status ){ +/* +* Name: +* ResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* void ResetCache( AstRegion *this, int *status ) + +* Class Membership: +* Region member function (overrides the astResetCache method +* inherited from the parent Region class). + +* Description: +* This function clears cached information from the supplied Region +* structure. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + if( this ) { + ( (AstInterval *) this )->stale = 1; + (*parent_resetcache)( this, status ); + } +} + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* Interval method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* Indicate that the cached intermediate information is now stale and + should be recreated when next needed. */ + astResetCache( this_region ); +} + +static void SetUnc( AstRegion *this, AstRegion *unc, int *status ){ +/* +* Name: +* SetUnc + +* Purpose: +* Store uncertainty information in a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* void SetUnc( AstRegion *this, AstRegion *unc, int *status ) + +* Class Membership: +* Interval method (over-rides the astSetUnc method inherited from the +* Region class). + +* Description: +* Each Region (of any class) can have an "uncertainty" which specifies +* the uncertainties associated with the boundary of the Region. This +* information is supplied in the form of a second Region. The uncertainty +* in any point on the boundary of a Region is found by shifting the +* associated "uncertainty" Region so that it is centred at the boundary +* point being considered. The area covered by the shifted uncertainty +* Region then represents the uncertainty in the boundary position. +* The uncertainty is assumed to be the same for all points. +* +* The uncertainty is usually specified when the Region is created, but +* this function allows it to be changed at any time. + +* Parameters: +* this +* Pointer to the Region which is to be assigned a new uncertainty. +* unc +* Pointer to the new uncertainty Region. This must be either a Box, +* a Circle or an Ellipse. A deep copy of the supplied Region will be +* taken, so subsequent changes to the uncertainty Region using the +* supplied pointer will have no effect on the Region "this". +* status +* Pointer to the inherited status variable. +*/ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Invoke the astSetUnc method inherited from the parent Region class. */ + (*parent_setunc)( this, unc, status ); + +/* Indicate that the cached intermediate information is now stale and + should be recreated when next needed. */ + astResetCache( this ); +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Interval method (over-rides the astSimplify method inherited +* from the Region class). + +* Description: +* This function invokes the parent Region Simplify method, and then +* performs any further region-specific simplification. +* +* If the Mapping from base to current Frame is not a UnitMap, this +* will include attempting to fit a new Region to the boundary defined +* in the current Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstBox *box2; /* Box used to determine 1-to-1 axis correspondance */ + AstBox *box; /* Box used to determine 1-to-1 axis correspondance */ + AstInterval *this_interval;/* Pointer to Interval structure */ + AstMapping *bfrm; /* Pointer to base Frame in supplied Interval */ + AstMapping *cfrm; /* Pointer to current Frame in supplied Interval */ + AstMapping *map; /* Base -> current Mapping after parent simplification */ + AstMapping *result; /* Result pointer to return */ + AstPointSet *pset2; /* PointSet containing current Frame test points */ + AstPointSet *pset3; /* PointSet containing base Frame test points */ + AstPointSet *psetb; /* PointSet holding base positions */ + AstPointSet *psetc; /* PointSet holding current positions */ + AstRegion *new; /* Pointer to Region simplfied by parent class */ + AstRegion *sreg; /* Pointer to simplified Box */ + AstRegion *this; /* Pointer to supplied Region structure */ + AstRegion *unc; /* Pointer to uncertainty Region */ + double **ptr2; /* Pointer axis values in "pset2" */ + double **ptr3; /* Pointer axis values in "pset3" */ + double **ptr; /* Pointer to base Frame values defining Interval */ + double **ptrb; /* Pointer to "psetb" axis values */ + double **sptr; /* Pointer to simplified Interval bounds */ + double *lbnd; /* Pointer to array of base Frame lower bounds */ + double *slbnd; /* Pointer to array of current Frame lower bounds */ + double *subnd; /* Pointer to array of current Frame upper bounds */ + double *ubnd; /* Pointer to array of base Frame upper bounds */ + double d; /* Distance between axis values */ + double lb; /* Lower bound on axis values */ + double lwid; /* Axis width below the Interval lower limit */ + double maxd; /* Maximum currenrt Frame axis offset between test points */ + double tmp; /* Temporary storage for swapping variable values */ + double ub; /* Upperbound on axis values */ + double uwid; /* Axis width above the Interval upper limit */ + int bax; /* Base Frame axis index corresponding to "ic" */ + int ic; /* Axis index */ + int jc; /* Axis index */ + int nc; /* No. of base Frame axis values per point */ + int simpler; /* Has some simplication taken place? */ + int snc; /* No. of current Frame axis values per point */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the supplied Region structure. */ + this = (AstRegion *) this_mapping; + +/* Get a pointer to the supplied Interval structure. */ + this_interval = (AstInterval *) this; + +/* If this Interval is equivalent to a Box, use the astTransform method of + the equivalent Box. */ + box = Cache( this_interval, status ); + if( box ) { + result = astSimplify( box ); + +/* Otherwise, we use a new implementation appropriate for unbounded + intervals. */ + } else { + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. */ + new = (AstRegion *) (*parent_simplify)( this_mapping, status ); + if( new ) { + +/* Note if any simplification took place. This is assumed to be the case + if the pointer returned by the above call is different to the supplied + pointer. */ + simpler = ( new != this ); + +/* If the Mapping from base to current Frame is not a UnitMap, we attempt + to simplify the Interval by re-defining it within its current Frame. */ + map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT ); + if( !astIsAUnitMap( map ) ){ + +/* Take a copy of the Interval bounds (defined in the base Frame of the + Intervals FrameSet) and replace any missing limits with arbitrary + non-BAD values. This will give us a complete set of bounds defining a + box within the base Frame of the Interval. */ + ptr = astGetPoints( new->points ); + nc = astGetNcoord( new->points ); + + lbnd = astMalloc( sizeof( double )*(size_t) nc ); + ubnd = astMalloc( sizeof( double )*(size_t) nc ); + + if( astOK ) { + for( ic = 0; ic < nc; ic++ ) { + lbnd[ ic ] = ptr[ ic ][ 0 ]; + ubnd[ ic ] = ptr[ ic ][ 1 ]; + +/* Ensure we have a good upper bound for this axis. */ + if( ubnd[ ic ] == AST__BAD ) { + if( lbnd[ ic ] == AST__BAD ) { + ubnd[ ic ] = 1.0; + + } else if( lbnd[ ic ] > 0.0 ) { + ubnd[ ic ] = lbnd[ ic ]*1.01; + + } else if( lbnd[ ic ] < 0.0 ) { + ubnd[ ic ] = lbnd[ ic ]*0.99; + + } else { + ubnd[ ic ] = 1.0; + } + } + +/* Ensure we have a good lower bound for this axis. */ + if( lbnd[ ic ] == AST__BAD ) { + if( ubnd[ ic ] > 0.0 ) { + lbnd[ ic ] = ubnd[ ic ]*0.99; + + } else if( ubnd[ ic ] < 0.0 ) { + lbnd[ ic ] = ubnd[ ic ]*1.01; + + } else { + lbnd[ ic ] = 1.0; + } + } + } + } + +/* Transform the box corners found above into the current frame and then back + into the base Frame, and ensure that the box encloses both the original + and the new bounds. PermMaps with fewer outputs than inputs can cause the + resulting base Frame positions to differ significantly from the original. */ + psetb =astPointSet( 2, nc,"", status ); + ptrb =astGetPoints( psetb ); + if( astOK ) { + for( ic = 0; ic < nc; ic++ ) { + ptrb[ ic ][ 0 ] = lbnd[ ic ]; + ptrb[ ic ][ 1 ] = ubnd[ ic ]; + } + } + psetc = astTransform( map, psetb, 1, NULL ); + (void) astTransform( map, psetc, 0, psetb ); + if( astOK ) { + for( ic = 0; ic < nc; ic++ ) { + lb = ptrb[ ic ][ 0 ]; + if( lb != AST__BAD ) { + if( lb < lbnd[ ic ] ) lbnd[ ic ] = lb; + if( lb > ubnd[ ic ] ) ubnd[ ic ] = lb; + } + ub = ptrb[ ic ][ 1 ]; + if( ub != AST__BAD ) { + if( ub < lbnd[ ic ] ) lbnd[ ic ] = ub; + if( ub > ubnd[ ic ] ) ubnd[ ic ] = ub; + } + } + } + psetb = astAnnul( psetb ); + psetc = astAnnul( psetc ); + +/* Limit this box to not exceed the limits imposed by the Interval.*/ + Cache( this_interval, status ); + for( ic = 0; ic < nc; ic++ ) { + lb = this_interval->lbnd[ ic ] ; + ub = this_interval->ubnd[ ic ] ; + if( lb <= ub ) { + if( lbnd[ ic ] < lb ) { + lbnd[ ic ] = lb; + } else if( lbnd[ ic ] > ub ) { + lbnd[ ic ] = ub; + } + if( ubnd[ ic ] < lb ) { + ubnd[ ic ] = lb; + } else if( ubnd[ ic ] > ub ) { + ubnd[ ic ] = ub; + } + } else { + lwid = lb - lbnd[ ic ]; + uwid = ubnd[ ic ] - ub; + if( lwid > uwid ) { + if( lbnd[ ic ] > lb ) lbnd[ ic ] = lb; + if( ubnd[ ic ] > lb ) ubnd[ ic ] = lb; + } else { + if( lbnd[ ic ] < ub ) lbnd[ ic ] = ub; + if( ubnd[ ic ] < ub ) ubnd[ ic ] = ub; + } + } + +/* Ensure the bounds are not equal */ + if( lbnd[ ic ] == 0.0 && ubnd[ ic ] == 0.0 ) { + ubnd[ ic ] = 1.0; + + } else if( astEQUALS( lbnd[ ic ], ubnd[ ic ], 1.0E9 ) ) { + ubnd[ ic ] = astMAX( ubnd[ ic ], lbnd[ ic ] )*( 1.0E6*DBL_EPSILON ); + } + } + +/* Create a new Box representing the box found above. */ + bfrm = astGetFrame( new->frameset, AST__BASE ); + unc = astTestUnc( new ) ? astGetUncFrm( new, AST__BASE ) : NULL; + box = astBox( bfrm, 1, lbnd, ubnd, unc, "", status ); + if( unc ) unc = astAnnul( unc ); + +/* Modify this Box so that it has the same current Frame as this Interval. */ + cfrm = astGetFrame( new->frameset, AST__CURRENT ); + box2 = astMapRegion( box, map, cfrm ); + +/* Try simplifying the Box. */ + sreg = (AstRegion *) astSimplify( box2 ); + +/* Only proceed if the Box was simplified */ + if( sreg != (AstRegion *) box2 ) { + +/* If the simplified Box is a NullRegion return it. */ + if( astIsANullRegion( sreg ) ) { + (void) astAnnul( new ); + new = astClone( sreg ); + simpler = 1; + +/* If the simplified Box is a Box or an Interval... */ + } else if( astIsABox( sreg ) || astIsAInterval( sreg ) ) { + +/* Get the bounds of the simplified Box. We assume that the base and + current Frames in the simplified Box are the same. */ + snc = astGetNin( sreg->frameset ); + slbnd = astMalloc( sizeof( double )*(size_t)snc ); + subnd = astMalloc( sizeof( double )*(size_t)snc ); + if( astIsAInterval( sreg ) ) { + sptr = astGetPoints( sreg->points ); + if( astOK ) { + for( ic = 0; ic < snc; ic++ ) { + slbnd[ ic ] = sptr[ ic ][ 0 ]; + subnd[ ic ] = sptr[ ic ][ 1 ]; + } + } + } else { + astRegBaseBox( sreg, slbnd, subnd ); + } + +/* Now create a PointSet containing one point for each axis in the + current (or equivalently, base ) Frame of the simplified Box, plus an + extra point. */ + pset2 = astPointSet( snc + 1, snc, "", status ); + ptr2 = astGetPoints( pset2 ); + +/* Put the lower bounds of the simplified Box into the first point in + this PointSet. The remaining points are displaced from this first point + along each axis in turn. The length of each displacement is determined + by the length of the box on the axis. */ + if( astOK ) { + for( ic = 0; ic < snc; ic++ ) { + for( jc = 0; jc < snc + 1; jc++ ) { + ptr2[ ic ][ jc ] = slbnd[ ic ]; + } + ptr2[ ic ][ ic + 1 ] = subnd[ ic ]; + } + } + +/* Transform this PointSet into the base Frame of this Interval using the + inverse of the base->current Mapping. */ + pset3 = astTransform( map, pset2, 0, NULL ); + ptr3 = astGetPoints( pset3 ); + if( astOK ) { + +/* Now consider each axis of the Interval's current Frame (i.e. each base + Frame axis in the simplified Box). */ + for( ic = 0; ic < snc; ic++ ) { + +/* Given that the Box simplified succesfully, we know that there is a one + to one connection between the axes of the base and current Frame in this + Interval, but we do not yet know which base Frame axis corresponds to + which current Frame axis (and the number of base and current Frame axes + need not be equal). We have two points on a line parallel to current + Frame axis number "ic" (points zero and "ic+1" in "pset2"). Look at the + corresponding base Frame positions (in "pset3), and see which base Frame + axis they are parallel to. We look for the largest base Frame axis + increment (this allows small non-zero displacements to occur on the + other axes due to rounding errors). */ + maxd = -DBL_MAX; + bax = -1; + for( jc = 0; jc < nc; jc++ ) { + d = fabs( astAxDistance( bfrm, jc + 1, ptr3[ jc ][ 0 ], + ptr3[ jc ][ ic + 1 ] ) ); + if( d != AST__BAD && d > maxd ) { + maxd = d; + bax = jc; + } + } + +/* If the largest base Frame axis increment is zero, it must mean that + the current Frame axis is not present in the base Frame. The only + plausable cause of this is if the base->current Mapping contains a + PermMap which introduces an extra axis, in which case the axis will + have a fixed value (any other Mapping arrangement would have prevented + the Box from simplifying). Therefore, set upper and lower limits for + this axis to the same value. */ + if( maxd <= 0.0 ) { + if( slbnd[ ic ] == AST__BAD || + subnd[ ic ] == AST__BAD ) { + slbnd[ ic ] = AST__BAD; + } else { + slbnd[ ic ] = 0.5*( slbnd[ ic ] + subnd[ ic ] ); + } + subnd[ ic ] = slbnd[ ic ]; + +/* If we have found a base Frame axis which corresponds to the current + Frame axis "ic", then look to see which limits are specified for the + base Frame axis, and transfer missing limits to the current Frame. */ + } else { + if( ptr[ bax ][ 0 ] == AST__BAD ) slbnd[ ic ] = AST__BAD; + if( ptr[ bax ][ 1 ] == AST__BAD ) subnd[ ic ] = AST__BAD; + +/* If the original limits were equal, ensure the new limits are equal + (the code above modified the upper limit to ensure it was different to + the lower limit). */ + if( ptr[ bax ][ 1 ] == ptr[ bax ][ 0 ] ) { + subnd[ ic ] = slbnd[ ic ]; + +/* If the original interval was an inclusion (ubnd > lbnd), ensure the new + interval is also an inclusion by swapping the limits if required. */ + } else if( ptr[ bax ][ 1 ] > ptr[ bax ][ 0 ] ) { + if( subnd[ ic ] < slbnd[ ic ] ) { + tmp = subnd[ ic ]; + subnd[ ic ] = slbnd[ ic ]; + slbnd[ ic ] = tmp; + } + +/* If the original interval was an exclusion (ubnd < lbnd), ensure the new + interval is also an exlusion by swapping the limits if required. */ + } else if( ptr[ bax ][ 1 ] < ptr[ bax ][ 0 ] ) { + if( subnd[ ic ] > slbnd[ ic ] ) { + tmp = subnd[ ic ]; + subnd[ ic ] = slbnd[ ic ]; + slbnd[ ic ] = tmp; + } + } + } + } + +/* Create the simplified Interval from the current Frame limits found + above, and use it in place of the original. */ + unc = astTestUnc( new ) ? astGetUncFrm( new, AST__CURRENT ) : NULL; + (void) astAnnul( new ); + new = (AstRegion *) astInterval( cfrm, slbnd, subnd, unc, "", status ); + if( unc ) unc = astAnnul( unc ); + simpler = 1; + } + +/* Free resources */ + pset2 = astAnnul( pset2 ); + pset3 = astAnnul( pset3 ); + slbnd = astFree( slbnd ); + subnd = astFree( subnd ); + } + } + +/* Free resources */ + bfrm = astAnnul( bfrm ); + cfrm = astAnnul( cfrm ); + box = astAnnul( box ); + box2 = astAnnul( box2 ); + sreg = astAnnul( sreg ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + } + +/* Free resources */ + map = astAnnul( map ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. + If the supplied Region had no uncertainty, ensure the returned Region + has no uncertainty. Otherwise, return a clone of the supplied pointer. */ + if( simpler ){ + astRegOverlay( new, this, 1 ); + result = (AstMapping *) new; + } else { + new = astAnnul( new ); + result = astClone( this ); + } + } + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a Interval to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Interval member function (over-rides the astTransform protected +* method inherited from the Region class). + +* Description: +* This function takes a Interval and a set of points encapsulated in a +* PointSet and transforms the points by setting axis values to +* AST__BAD for all points which are outside the region. Points inside +* the region are copied unchanged from input to output. + +* Parameters: +* this +* Pointer to the Interval. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The forward and inverse transformations are identical for a +* Region. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of axes in the Frame represented by the Interval. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstBox *box; /* Pointer to equivalent Box */ + AstInterval *this; /* Pointer to Interval structure */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + AstRegion *reg; /* Pointer to Region structure */ + AstRegion *unc; /* Uncertainty Region */ + double **ptr_lims; /* Pointer to limits array */ + double **ptr_out; /* Pointer to output coordinate data */ + double **ptr_tmp; /* Pointer to base Frame coordinate data */ + double *lbnd_unc; /* Lower bounds of uncertainty Region */ + double *ubnd_unc; /* Upper bounds of uncertainty Region */ + double lb; /* Base Frame axis lower bound */ + double p; /* Input base Frame axis value */ + double ub; /* Base Frame axis upper bound */ + double wid; /* Half width of uncertainy Region */ + int coord; /* Zero-based index for coordinates */ + int ncoord_out; /* No. of coordinates per output point */ + int ncoord_tmp; /* No. of coordinates per base Frame point */ + int neg; /* Has the Region been negated? */ + int npoint; /* No. of points */ + int pass; /* Does this point pass the axis test? */ + int point; /* Loop counter for points */ + int setbad; /* Set the output point bad? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain pointers to the Region and to the Interval. */ + reg = (AstRegion *) this_mapping; + this = (AstInterval *) this_mapping; + +/* If this Interval is equivalent to a Box, use the astTransform method of + the equivalent Box. */ + box = Cache( this, status ); + if( box ) { + result = astTransform( box, in, forward, out ); + +/* Otherwise, we use a new implementation appropriate for unbounded + intervals. */ + } else { + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, + containing a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet to transform the supplied positions + from the current Frame in the encapsulated FrameSet (the Frame + represented by the Region), to the base Frame (the Frame in which the + Region is defined). This call also returns a pointer to the base Frame + of the encapsulated FrameSet. Note, the returned pointer may be a + clone of the "in" pointer, and so we must be carefull not to modify the + contents of the returned PointSet. */ + pset_tmp = astRegTransform( reg, in, 0, NULL, NULL ); + +/* Determine the numbers of points and coordinates per point from the base + Frame PointSet and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( pset_tmp ); + ncoord_tmp = astGetNcoord( pset_tmp ); + ptr_tmp = astGetPoints( pset_tmp ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* Get a pointer to the array of axis limits */ + ptr_lims = astGetPoints( reg->points ); + +/* See if the Region is negated. */ + neg = astGetNegated( reg ); + +/* Indicate we have not yet got the bounding box of the uncertainty + Region. */ + lbnd_unc = NULL; + ubnd_unc = NULL; + unc = NULL; + +/* Perform coordinate arithmetic. */ + if ( astOK ) { + +/* First deal with closed unnegated Intervals. */ +/* ------------------------------------------- */ + if( astGetClosed( reg ) ) { + if( !neg ) { + +/* Loop round each point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Assume this point is inside the Region. We change this flag when we find + the first axis for which the point does not pass the axis test. */ + setbad = 0; + +/* Loop round each base Frame axis */ + Cache( this, status ); + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + p = ptr_tmp[ coord ][ point ]; + lb = (this->lbnd)[ coord ]; + ub = (this->ubnd)[ coord ]; + +/* If the limits are equal separate them slightly to give some tolerance. */ + if( lb == ub ) { + +/* If not yet done so, get the bounding box of the uncertainty Region in the + base Frame of the Interval */ + if( !unc ) { + unc = astGetUncFrm( reg, AST__BASE ); + lbnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp ); + ubnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + } + +/* Set the gap between the limits to be equal to the uincertainty on this + axis. */ + if( astOK ) { + wid = 0.5*( ubnd_unc[ coord ] - lbnd_unc[ coord ] ); + lb -= wid; + ub += wid; + } + } + +/* Bad input points should always be bad in the output. */ + if( p == AST__BAD ) { + setbad = 1; + break; + +/* Does the current axis value pass the limits test for this axis? */ + } else if( lb <= ub ) { + pass = ( lb <= p && p <= ub ); + } else { + pass = ( p <= ub || lb <= p ); + } + +/* If this point does not pass the test for this axis, then indicate that + we should set the resulting output point bad and break since we now have + a definite value for the inside/outside flag. */ + if( !pass ) { + setbad = 1; + break; + } + } + +/* Set the axis values bad for this output point if required. */ + if( setbad ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + +/* Now deal with closed negated Intervals. */ +/* --------------------------------------- */ + } else { + +/* Loop round each point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Assume this point is outside the negated Region (i.e. inside the + unnegated Region). We change this flag when we find the first axis for + which the point passes the axis test. */ + setbad = 1; + +/* Loop round each base Frame axis */ + Cache( this, status ); + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + p = ptr_tmp[ coord ][ point ]; + lb = (this->lbnd)[ coord ]; + ub = (this->ubnd)[ coord ]; + +/* Bad input points should always be bad in the output. */ + if( p == AST__BAD ) { + setbad = 1; + break; + +/* Does the current axis value pass the limits test for this axis? */ + } else if( lb <= ub ) { + pass = ( p <= lb || ub <= p ); + } else { + pass = ( ub <= p && p <= lb ); + } + +/* If this point passes the test for this axis, then indicate that we should + not set the resulting output point bad and break since we now have a + definite value for the inside/outside flag. */ + if( pass ) { + setbad = 0; + break; + } + } + +/* Set the axis values bad for this output point if required. */ + if( setbad ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + +/* Now deal with open unnegated Intervals. */ +/* --------------------------------------- */ + } else { + if( !neg ) { + +/* Loop round each point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Assume this point is inside the Region. We change this flag when we find + the first axis for which the point does not pass the axis test. */ + setbad = 0; + +/* Loop round each base Frame axis */ + Cache( this, status ); + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + p = ptr_tmp[ coord ][ point ]; + lb = (this->lbnd)[ coord ]; + ub = (this->ubnd)[ coord ]; + +/* Bad input points should always be bad in the output. */ + if( p == AST__BAD ) { + setbad = 1; + break; + +/* Does the current axis value pass the limits test for this axis? */ + } else if( lb <= ub ) { + pass = ( lb < p && p < ub ); + } else { + pass = ( p < ub || lb < p ); + } + +/* If this point does not pass the test for this axis, then indicate that + we should set the resulting output point bad and break since we now have + a definite value for the inside/outside flag. */ + if( !pass ) { + setbad = 1; + break; + } + } + +/* Set the axis values bad for this output point if required. */ + if( setbad ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + +/* Now deal with open negated Intervals. */ +/* ------------------------------------- */ + } else { + +/* Loop round each point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Assume this point is outside the negated Region (i.e. inside the + unnegated Region). We change this flag when we find the first axis for + which the point passes the axis test. */ + setbad = 1; + +/* Loop round each base Frame axis */ + Cache( this, status ); + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + p = ptr_tmp[ coord ][ point ]; + lb = (this->lbnd)[ coord ]; + ub = (this->ubnd)[ coord ]; + +/* If the limits are equal separate them slightly to give some tolerance. */ + if( lb == ub ) { + +/* If not yet done so, get the bounding box of the uncertainty Region in the + base Frame of the Interval */ + if( !unc ) { + unc = astGetUncFrm( reg, AST__BASE ); + lbnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp ); + ubnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + } + +/* Set the gap between the limits to be equal to the uincertainty on this + axis. */ + if( astOK ) { + wid = 0.5*( ubnd_unc[ coord ] - lbnd_unc[ coord ] ); + lb -= wid; + ub += wid; + } + } + +/* Bad input points should always be bad in the output. */ + if( p == AST__BAD ) { + setbad = 1; + break; + +/* Does the current axis value pass the limits test for this axis? */ + } else if( lb <= ub ) { + pass = ( p < lb || ub < p ); + } else { + pass = ( ub < p && p < lb ); + } + +/* If this point passes the test for this axis, then indicate that we should + not set the resulting output point bad and break since we now have a + definite value for the inside/outside flag. */ + if( pass ) { + setbad = 0; + break; + } + } + +/* Set the axis values bad for this output point if required. */ + if( setbad ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + } + } + +/* Free resources */ + pset_tmp = astAnnul( pset_tmp ); + if( lbnd_unc ) lbnd_unc = astFree( lbnd_unc ); + if( ubnd_unc ) ubnd_unc = astFree( ubnd_unc ); + if( unc ) unc = astAnnul( unc ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Interval objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Region objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstInterval *in; /* Pointer to input Interval */ + AstInterval *out; /* Pointer to output Interval */ + size_t nb; /* Number of bytes in limits array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Intervals. */ + in = (AstInterval *) objin; + out = (AstInterval *) objout; + +/* For safety, first clear any references to the input memory from + the output Interval. */ + out->box = NULL; + out->lbnd = NULL; + out->ubnd = NULL; + +/* Note the number of bytes in each limits array */ + nb = sizeof( double )*(size_t) astGetNin( ((AstRegion *) in)->frameset ); + +/* Copy dynamic memory contents */ + if( in->box ) out->box = astCopy( in->box ); + out->lbnd = astStore( NULL, in->lbnd, nb ); + out->ubnd = astStore( NULL, in->ubnd, nb ); +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Interval objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Interval objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstInterval *this; /* Pointer to Interval */ + +/* Obtain a pointer to the Interval structure. */ + this = (AstInterval *) obj; + +/* Annul all resources. */ + if( this->box ) this->box = astAnnul( this->box ); + this->lbnd = astFree( this->lbnd ); + this->ubnd = astFree( this->ubnd ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Interval objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Interval class to an output Channel. + +* Parameters: +* this +* Pointer to the Interval whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstInterval *this; /* Pointer to the Interval structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Interval structure. */ + this = (AstInterval *) this_object; + +/* Write out values representing the instance variables for the + Interval class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAInterval and astCheckInterval functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Interval,Region) +astMAKE_CHECK(Interval) + +AstInterval *astInterval_( void *frame_void, const double lbnd[], + const double ubnd[], AstRegion *unc, + const char *options, int *status, ...) { +/* +*++ +* Name: +c astInterval +f AST_INTERVAL + +* Purpose: +* Create a Interval. + +* Type: +* Public function. + +* Synopsis: +c #include "interval.h" +c AstInterval *astInterval( AstFrame *frame, const double lbnd[], +c const double ubnd[], AstRegion *unc, +c const char *options, ... ) +f RESULT = AST_INTERVAL( FRAME, LBND, UBND, UNC, OPTIONS, STATUS ) + +* Class Membership: +* Interval constructor. + +* Description: +* This function creates a new Interval and optionally initialises its +* attributes. +* +* A Interval is a Region which represents upper and/or lower limits on +* one or more axes of a Frame. For a point to be within the region +* represented by the Interval, the point must satisfy all the +* restrictions placed on all the axes. The point is outside the region +* if it fails to satisfy any one of the restrictions. Each axis may have +* either an upper limit, a lower limit, both or neither. If both limits +* are supplied but are in reverse order (so that the lower limit is +* greater than the upper limit), then the interval is an excluded +* interval, rather than an included interval. +* +* At least one axis limit must be supplied. +* +* Note, The Interval class makes no allowances for cyclic nature of +* some coordinate systems (such as SkyFrame coordinates). A Box +* should usually be used in these cases since this requires the user +* to think about suitable upper and lower limits, + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame in which the region is defined. A deep +* copy is taken of the supplied Frame. This means that any +* subsequent changes made to the Frame using the supplied pointer +* will have no effect the Region. +c lbnd +f LBND( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the lower limits on each axis. +* Set a value to AST__BAD to indicate that the axis has no lower +* limit. +c ubnd +f UBND( * ) = DOUBLE PRECISION (Given) +c An array of double, with one element for each Frame axis +f An array with one element for each Frame axis +* (Naxes attribute) containing the upper limits on each axis. +* Set a value to AST__BAD to indicate that the axis has no upper +* limit. +c unc +f UNC = INTEGER (Given) +* An optional pointer to an existing Region which specifies the +* uncertainties associated with the boundary of the Interval being created. +* The uncertainty in any point on the boundary of the Interval is found by +* shifting the supplied "uncertainty" Region so that it is centred at +* the boundary point being considered. The area covered by the +* shifted uncertainty Region then represents the uncertainty in the +* boundary position. The uncertainty is assumed to be the same for +* all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created Interval. Alternatively, +f a null Object pointer (AST__NULL) +c a NULL Object pointer +* may be supplied, in which case a default uncertainty is used +* equivalent to a box 1.0E-6 of the size of the Interval being created. +* +* The uncertainty Region has two uses: 1) when the +c astOverlap +f AST_OVERLAP +* function compares two Regions for equality the uncertainty +* Region is used to determine the tolerance on the comparison, and 2) +* when a Region is mapped into a different coordinate system and +* subsequently simplified (using +c astSimplify), +f AST_SIMPLIFY), +* the uncertainties are used to determine if the transformed boundary +* can be accurately represented by a specific shape of Region. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Interval. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Interval. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astInterval() +f AST_INTERVAL = INTEGER +* A pointer to the new Interval. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstInterval *new; /* Pointer to new Interval */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the supplied Frame structure. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the Interval, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitInterval( NULL, sizeof( AstInterval ), !class_init, + &class_vtab, "Interval", frame, lbnd, ubnd, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Interval's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Interval. */ + return new; +} + +AstInterval *astIntervalId_( void *frame_void, const double lbnd[], + const double ubnd[], void *unc_void, + const char *options, ... ) { +/* +* Name: +* astIntervalId_ + +* Purpose: +* Create a Interval. + +* Type: +* Private function. + +* Synopsis: +* #include "interval.h" +* AstInterval *astIntervalId_( AstFrame *frame, const double lbnd[], +* const double ubnd[], AstRegion *unc, +* const char *options, ... ) + +* Class Membership: +* Interval constructor. + +* Description: +* This function implements the external (public) interface to the +* astInterval constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astInterval_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astInterval_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astInterval_. + +* Returned Value: +* The ID value associated with the new Interval. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstInterval *new; /* Pointer to new Interval */ + AstRegion *unc; /* Pointer to Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Frame pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Obtain a Region pointer from the supplied "unc" ID and validate the + pointer to ensure it identifies a valid Region . */ + unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL; + +/* Initialise the Interval, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitInterval( NULL, sizeof( AstInterval ), !class_init, &class_vtab, + "Interval", frame, lbnd, ubnd, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Interval's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Interval. */ + return astMakeId( new ); +} + +AstInterval *astInitInterval_( void *mem, size_t size, int init, AstIntervalVtab *vtab, + const char *name, AstFrame *frame, + const double lbnd[], const double ubnd[], + AstRegion *unc, int *status ) { +/* +*+ +* Name: +* astInitInterval + +* Purpose: +* Initialise a Interval. + +* Type: +* Protected function. + +* Synopsis: +* #include "interval.h" +* AstInterval *astInitInterval( void *mem, size_t size, int init, AstIntervalVtab *vtab, +* const char *name, AstFrame *frame, +* const double lbnd[], const double ubnd[], +* AstRegion *unc ) + +* Class Membership: +* Interval initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Interval object. It allocates memory (if necessary) to accommodate +* the Interval plus any additional data associated with the derived class. +* It then initialises a Interval structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Interval at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Interval is to be initialised. +* This must be of sufficient size to accommodate the Interval data +* (sizeof(Interval)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Interval (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Interval +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Interval's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Interval. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame in which the region is defined. +* lbnd +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the lower limits on each axis. +* Set a value to AST__BAD to indicate that the axis has no lower +* limit. Upper and ower limits can be reversed to create an +* excluded interval rather than an included interval. +* ubnd +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the upper limits on each axis. +* Set a value to AST__BAD to indicate that the axis has no upper +* limit. +* unc +* A pointer to a Region which specifies the uncertainty in the +* supplied positions (all points on the boundary of the new Interval +* being initialised are assumed to have the same uncertainty). A NULL +* pointer can be supplied, in which case default uncertainties equal to +* 1.0E-6 of the dimensions of the new Interval's bounding box are used. +* If an uncertainty Region is supplied, it must be either a Box, a +* Circle or an Ellipse, and its encapsulated Frame must be related +* to the Frame supplied for parameter "frame" (i.e. astConvert +* should be able to find a Mapping between them). Two positions +* the "frame" Frame are considered to be co-incident if their +* uncertainty Regions overlap. The centre of the supplied +* uncertainty Region is immaterial since it will be re-centred on the +* point being tested before use. A deep copy is taken of the supplied +* Region. + +* Returned Value: +* A pointer to the new Interval. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstInterval *new; /* Pointer to new Interval */ + AstPointSet *pset; /* PointSet to pass to Region initialiser */ + double **ptr; /* Pointer to coords data in pset */ + int i; /* Axis index */ + int nc; /* No. of axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitIntervalVtab( &class_vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Get the number of axis values required for each position. */ + nc = astGetNaxes( frame ); + +/* Create a PointSet to hold the upper and lower bounds, and get pointers to + the data arrays. */ + pset = astPointSet( 2, nc, "", status ); + ptr = astGetPoints( pset ); + if( astOK ) { + +/* Copy the limits into the PointSet. */ + for( i = 0; i < nc; i++ ) { + ptr[ i ][ 0 ] = lbnd[ i ]; + ptr[ i ][ 1 ] = ubnd[ i ]; + } + +/* Initialise a Region structure (the parent class) as the first component + within the Interval structure, allocating memory if necessary. */ + new = (AstInterval *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frame, pset, unc ); + + if ( astOK ) { + +/* Initialise the Interval data. */ +/* ----------------------------- */ + new->lbnd = NULL; + new->ubnd = NULL; + new->box = NULL; + new->stale = 1; + +/* If an error occurred, clean up by deleting the new Interval. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free resources. */ + pset = astAnnul( pset ); + +/* Return a pointer to the new Interval. */ + return new; +} + +AstInterval *astLoadInterval_( void *mem, size_t size, AstIntervalVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadInterval + +* Purpose: +* Load a Interval. + +* Type: +* Protected function. + +* Synopsis: +* #include "interval.h" +* AstInterval *astLoadInterval( void *mem, size_t size, AstIntervalVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Interval loader. + +* Description: +* This function is provided to load a new Interval using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Interval structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Interval at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Interval is to be +* loaded. This must be of sufficient size to accommodate the +* Interval data (sizeof(Interval)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Interval (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Interval structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstInterval) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Interval. If this is NULL, a pointer +* to the (static) virtual function table for the Interval class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Interval" is used instead. + +* Returned Value: +* A pointer to the new Interval. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstInterval *new; /* Pointer to the new Interval */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Interval. In this case the + Interval belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstInterval ); + vtab = &class_vtab; + name = "Interval"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitIntervalVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Interval. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Interval" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + new->lbnd = NULL; + new->ubnd = NULL; + new->box = NULL; + new->stale = 1; + +/* If an error occurred, clean up by deleting the new Interval. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Interval pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astIntervalPoints_( AstInterval *this, double *lbnd, double *ubnd, + int *status) { + if ( !astOK ) return; + (**astMEMBER(this,Interval,IntervalPoints))( this, lbnd, ubnd, status ); + return; +} + + + + + + diff --git a/interval.h b/interval.h new file mode 100644 index 0000000..3397e24 --- /dev/null +++ b/interval.h @@ -0,0 +1,236 @@ +#if !defined( INTERVAL_INCLUDED ) /* Include this file only once */ +#define INTERVAL_INCLUDED +/* +*+ +* Name: +* interval.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Interval class. + +* Invocation: +* #include "interval.h" + +* Description: +* This include file defines the interface to the Interval class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Interval class implement a Region which represents a simple interval +* on each axis of the encapsulated Frame + +* Inheritance: +* The Interval class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 1-NOV-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "box.h" /* Closed box regions */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* Interval structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstInterval { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *lbnd; /* Lower limits */ + double *ubnd; /* Lower limits */ + AstBox *box; /* Equivalent Box */ + int stale; /* Is cached information stale? */ + +} AstInterval; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstIntervalVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* IntervalPoints)( AstInterval *, double *, double *, int *); + +} AstIntervalVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstIntervalGlobals { + AstIntervalVtab Class_Vtab; + int Class_Init; +} AstIntervalGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitIntervalGlobals_( AstIntervalGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Interval) /* Check class membership */ +astPROTO_ISA(Interval) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstInterval *astInterval_( void *, const double[], const double[], AstRegion *, const char *, int *, ...); +#else +AstInterval *astIntervalId_( void *, const double[], const double[], AstRegion *, const char *, ... )__attribute__((format(printf,5,6))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstInterval *astInitInterval_( void *, size_t, int, AstIntervalVtab *, + const char *, AstFrame *, const double[], + const double[], AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitIntervalVtab_( AstIntervalVtab *, const char *, int * ); + +/* Loader. */ +AstInterval *astLoadInterval_( void *, size_t, AstIntervalVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +void astIntervalPoints_( AstInterval *, double *, double *, int *); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckInterval(this) astINVOKE_CHECK(Interval,this,0) +#define astVerifyInterval(this) astINVOKE_CHECK(Interval,this,1) + +/* Test class membership. */ +#define astIsAInterval(this) astINVOKE_ISA(Interval,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astInterval astINVOKE(F,astInterval_) +#else +#define astInterval astINVOKE(F,astIntervalId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitInterval(mem,size,init,vtab,name,frame,lbnd,ubnd,unc) \ +astINVOKE(O,astInitInterval_(mem,size,init,vtab,name,frame,lbnd,ubnd,unc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitIntervalVtab(vtab,name) astINVOKE(V,astInitIntervalVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadInterval(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadInterval_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckInterval to validate Interval pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astIntervalPoints(this,lbnd,ubnd) astINVOKE(V,astIntervalPoints_(astCheckInterval(this),lbnd,ubnd,STATUS_PTR)) +#endif +#endif + + + + + diff --git a/intramap.c b/intramap.c new file mode 100644 index 0000000..67eebb2 --- /dev/null +++ b/intramap.c @@ -0,0 +1,2942 @@ +/* +*class++ +* Name: +* IntraMap + +* Purpose: +c Map points using a private transformation function. +f Map points using a private transformation routine. + +* Constructor Function: +c astIntraMap (also see astIntraReg) +f AST_INTRAMAP (also see AST_INTRAREG) + +* Description: +c The IntraMap class provides a specialised form of Mapping which +c encapsulates a privately-defined coordinate transformation +c other AST Mapping. This allows you to create Mappings that +c perform any conceivable coordinate transformation. +f The IntraMap class provides a specialised form of Mapping which +f encapsulates a privately-defined coordinate transformation +f routine (e.g. written in Fortran) so that it may be used like +f any other AST Mapping. This allows you to create Mappings that +f perform any conceivable coordinate transformation. +* +* However, an IntraMap is intended for use within a single program +* or a private suite of software, where all programs have access +* to the same coordinate transformation functions (i.e. can be +* linked against them). IntraMaps should not normally be stored in +* datasets which may be exported for processing by other software, +* since that software will not have the necessary transformation +* functions available, resulting in an error. +* +c You must register any coordinate transformation functions to be +c used using astIntraReg before creating an IntraMap. +f You must register any coordinate transformation functions to be +f used using AST_INTRAREG before creating an IntraMap. + +* Inheritance: +* The IntraMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* IntraMap also has the following attributes: +* +* - IntraFlag: IntraMap identification string + +* Functions: +c The IntraMap class does not define any new functions beyond those +f The IntraMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 16-MAR-1998 (RFWS): +* Original version. +* 15-SEP-1999 (RFWS): +* Added a "this" pointer to the external transformation function +* used by an IntraMap. +* 20-JUN-2001 (DSB): +* Add an "astClone" call to prevent the pointer for "this" being +* annulled at the end of the Transform method. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitIntraMapVtab +* method. +* 7-DEC-2005 (DSB): +* Free memory allocated by calls to astReadString. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 10-MAY-2006 (DSB): +* Override astEqual. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS IntraMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "unitmap.h" /* Unit (identity) Mappings */ +#include "intramap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Pointer to array of transformation function data. */ +static AstIntraMapTranData *tran_data = NULL; + +/* Number of transformation functions registered. */ +static int tran_nfun = 0; + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are used or extended by this + class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_getnin)( AstMapping *, int * ); +static int (* parent_getnout)( AstMapping *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(IntraMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(IntraMap,Class_Init) +#define class_vtab astGLOBAL(IntraMap,Class_Vtab) + + +/* A mutex used to serialise invocations of the IntraReg function (the + only function allowed to modify the contents of the static tran_data + array). */ +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ); +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ); + +/* A mutex used to serialise invocations of extrnal transformation + functions (which may not be thread-safe). */ +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstIntraMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstIntraMap *astIntraMapId_( const char *, int, int, const char *, ... ); +void astIntraRegFor_( const char *, int, int, void (* tran)( AstMapping *, int, int, const double *[], int, int, double *[] ), void (* tran_wrap)( void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), AstMapping *, int, int, const double *[], int, int, double *[], int * ), unsigned int, const char *, const char *, const char *, int * ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static char *CleanName( const char *, const char *, int * ); +static int GetObjSize( AstObject *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetIntraFlag( AstIntraMap *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestIntraFlag( AstIntraMap *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearIntraFlag( AstIntraMap *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static void IntraReg( const char *, int, int, void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), void (*)( void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), AstMapping *, int, int, const double *[], int, int, double *[], int * ), unsigned int, const char *, const char *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetIntraFlag( AstIntraMap *, const char *, int * ); +static void TranWrap( void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), AstMapping *, int, int, const double *[], int, int, double *[], int * ); + +/* Member functions. */ +/* ================= */ +static char *CleanName( const char *name, const char *caller, int *status ) { +/* +* Name: +* CleanName + +* Purpose: +* Clean (and validate) a transformation function name. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* char *CleanName( const char *name, const char *caller, int *status ) + +* Class Membership: +* IntraMap member function. + +* Description: +* This function cleans a transformation function name by removing +* all white space. It returns a copy of the cleaned name held in +* dynamically allocated memory. If the name is entirely blank, an +* error is reported. + +* Parameters: +* name +* Pointer to a null-terminated string containing the name to be +* cleaned. +* caller +* Pointer to a null-terminated string containing the name of +* the calling function. This is only used to construct error +* messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a dynamically-allocated null-terminated string +* containing the cleaned name. A NULL pointer is returned if the +* name was entirely blank. + +* Notes: +* - The memory holding the returned string should be deallocated +* (using astFree) when no longer required. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + char *result; /* Pointer to result string */ + int i; /* Loop counter for input characters */ + int len; /* Length of name */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine the number of non-blank characters in the name supplied. */ + len = 0; + for ( i = 0; name[ i ]; i++ ) if ( !isspace( (int) name[ i ] ) ) len++; + +/* If the name is entirely blank, then report an error. */ + if ( !len ) { + astError( AST__ITFNI, "%s: Invalid blank transformation function name " + "given.", status, caller ); + +/* Otherwise, allocate memory to hold the cleaned name. */ + } else { + result = astMalloc( (size_t) ( len + 1 ) ); + +/* If OK, make a copy of the name with white space removed. */ + if ( astOK ) { + len = 0; + for ( i = 0; name[ i ]; i++ ) { + if ( !isspace( (int) name[ i ] ) ) result[ len++ ] = name[ i ]; + } + +/* Terminate the result string. */ + result[ len ] = '\0'; + } + } + +/* Return the result pointer. */ + return result; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for an IntraMap. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* IntraMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for an +* IntraMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the IntraMap. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstIntraMap *this; /* Pointer to the IntraMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the IntraMap structure. */ + this = (AstIntraMap *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* IntraFlag. */ +/* ---------- */ + if ( !strcmp( attrib, "intraflag" ) ) { + astClearIntraFlag( this ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two IntraMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* IntraMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two IntraMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a IntraMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the IntraMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstIntraMap *that; + AstIntraMap *this; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two IntraMap structures. */ + this = (AstIntraMap *) this_object; + that = (AstIntraMap *) that_object; + +/* Check the second object is a IntraMap. We know the first is a + IntraMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAIntraMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two IntraMaps differ, it may still be possible + for them to be equivalent. First compare the IntraMaps if their Invert + flags are the same. In this case all the attributes of the two IntraMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + + if( this->ifun == that->ifun && + this->intraflag && that->intraflag && + !strcmp( this->intraflag, that->intraflag ) ) { + result = 1; + } + +/* If the Invert flags for the two IntraMaps differ, the attributes of the two + IntraMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a IntraMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* IntraMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied IntraMap, +* in bytes. + +* Parameters: +* this +* Pointer to the IntraMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstIntraMap *this; /* Pointer to IntraMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the IntraMap structure. */ + this = (AstIntraMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->intraflag ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for an IntraMap. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* IntraMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a IntraMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the IntraMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the IntraMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the IntraMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstIntraMap *this; /* Pointer to the IntraMap structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the IntraMap structure. */ + this = (AstIntraMap *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* IntraFlag. */ +/* ---------- */ + if ( !strcmp( attrib, "intraflag" ) ) { + result = astGetIntraFlag( this ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +void astInitIntraMapVtab_( AstIntraMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitIntraMapVtab + +* Purpose: +* Initialise a virtual function table for an IntraMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "intramap.h" +* void astInitIntraMapVtab( AstIntraMapVtab *vtab, const char *name ) + +* Class Membership: +* IntraMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the IntraMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAIntraMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->ClearIntraFlag = ClearIntraFlag; + vtab->GetIntraFlag = GetIntraFlag; + vtab->SetIntraFlag = SetIntraFlag; + vtab->TestIntraFlag = TestIntraFlag; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Store pointers to inherited methods that will be invoked explicitly + by this class. */ + parent_getnin = mapping->GetNin; + parent_getnout = mapping->GetNout; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "IntraMap", + "Map points using a private transformation function" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void IntraReg( const char *name, int nin, int nout, + void (* tran)( AstMapping *, int, int, const double *[], + int, int, double *[] ), + void (* tran_wrap)( void (*)( AstMapping *, int, int, + const double *[], int, int, + double *[] ), + AstMapping *, int, int, + const double *[], int, int, + double *[], int * ), + unsigned int flags, + const char *purpose, const char *author, + const char *contact, int *status ) { +/* +* Name: +* IntraReg + +* Purpose: +* Register a transformation function for use by an IntraMap. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* void IntraReg( const char *name, int nin, int nout, +* void (* tran)( AstMapping *, int, int, const double *[], +* int, int, double *[] ), +* void (* tran_wrap)( void (*)( AstMapping *, int, int, +* const double *[], int, int, +* double *[] ), +* AstMapping *, int, int, +* const double *[], int, int, +* double *[], int * ), +* unsigned int flags, +* const char *purpose, const char *author, +* const char *contact, int *status ) + +* Class Membership: +* IntraMap member function. + +* Description: +* This function registers a transformation function which will +* later be used by an IntraMap and associates it with a name. It +* also stores related information which will be required by the +* IntraMap. + +* Parameters: +* name +* Pointer to a null-terminated string containing the name to be +* used to identify the transformation function. This string is +* case sensitive. All white space is removed before use. +* nin +* The number of input coordinates per point (or AST__ANY if any +* number are allowed). +* nout +* The number of output coordinates per point (or AST__ANY if +* any number are allowed). +* tran +* Pointer to the transformation function to be registered. +* This may have any form of interface, which need not be known +* to the implementation of the IntraMap class. Instead, the +* method of invoking the transformation function should be +* encapsulated in the "tran_wrap" function (below). +* tran_wrap +* Pointer to a wrapper function appropriate to the transformation +* function (above). This wrapper function should have the same +* interface as astTranP (from the Mapping class), except that it takes +* a pointer to a function like "tran" as an additional first argument. +* The purpose of this wrapper is to invoke the transformation function +* via the pointer supplied, to pass it the necessary information +* derived from the remainder of its arguments, and then to return the +* results. +* flags +* This argument may be used to supply a set of flags which +* control the behaviour of any IntraMap which uses the +* registered transformation function. See the public interface +* for astIntraReg for details. +* purpose +* Pointer to a null-terminated string containing a short (one +* line) textual comment to describe the purpose of the +* transformation function. +* author +* Pointer to a null-terminated string containing the name of +* the author of the transformation function. +* contact +* Pointer to a null-terminated string containing contact +* details for the author of the function (e.g. an e-mail or WWW +* address). +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *clname; /* Pointer to cleaned name string */ + int found; /* Transformation function found? */ + int ifun; /* Loop counter for function information */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* This function modifies the global static tran_data array, so we use a + mutex to ensure that only one thread can run this function at any one + time. */ + LOCK_MUTEX1; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Clean (and validate) the name supplied. */ + clname = CleanName( name, "astIntraReg", status ); + +/* If OK, also validate the numbers of input and output + coordinates. Report an error if necessary. */ + if ( astOK ) { + if ( ( nin < 0 ) && ( nin != AST__ANY ) ) { + astError( AST__BADNI, "astIntraReg(%s): Bad number of input " + "coordinates (%d).", status, clname, nin ); + astError( AST__BADNI, "This number should be zero or more (or " + "AST__ANY)." , status); + + } else if ( ( nout < 0 ) && ( nout != AST__ANY ) ) { + astError( AST__BADNO, "astIntraReg(%s): Bad number of output " + "coordinates (%d).", status, clname, nout ); + astError( AST__BADNO, "This number should be zero or more (or " + "AST__ANY)." , status); + } + } + +/* Search the array of transformation function data to see if a + function with the same name has already been registered. */ + if ( astOK ) { + found = 0; + for ( ifun = 0; ifun < tran_nfun; ifun++ ) { + if ( ( found = !strcmp( clname, tran_data[ ifun ].name ) ) ) break; + } + +/* If so, then check that the information supplied this time is + identical to that supplied before. If any discrepancy is found, + report an error. */ + if ( found ) { + if ( ( nin != tran_data[ ifun ].nin ) || + ( nout != tran_data[ ifun ].nout ) || + ( tran != tran_data[ ifun ].tran ) || + ( tran_wrap != tran_data[ ifun ].tran_wrap ) || + ( flags != tran_data[ ifun ].flags ) || + strcmp( purpose, tran_data[ ifun ].purpose ) || + strcmp( author, tran_data[ ifun ].author ) || + strcmp( contact, tran_data[ ifun ].contact ) ) { + astError( AST__MRITF, "astIntraReg: Invalid attempt to register " + "the transformation function name \"%s\" " + "multiple times.", status, clname ); + } + +/* If this is a new function, extend the array of transformation + function data to accommodate it. */ + } else { + + tran_data = astGrow( tran_data, tran_nfun + 1, sizeof( AstIntraMapTranData ) ); + +/* Store the information supplied. */ + if ( astOK ) { + tran_data[ tran_nfun ].name = clname; + tran_data[ tran_nfun ].nin = nin; + tran_data[ tran_nfun ].nout = nout; + tran_data[ tran_nfun ].tran = tran; + tran_data[ tran_nfun ].tran_wrap = tran_wrap; + tran_data[ tran_nfun ].flags = flags; + tran_data[ tran_nfun ].purpose = + astStore( NULL, purpose, strlen( purpose ) + (size_t) 1 ); + tran_data[ tran_nfun ].author = + astStore( NULL, author, strlen( author ) + (size_t) 1 ); + tran_data[ tran_nfun ].contact = + astStore( NULL, contact, strlen( contact ) + (size_t) 1 ); + +/* If successful, increment the count of transformation functions + registered. */ + if ( astOK ) { + tran_nfun++; + +/* If an error occurred, free any memory that was allocated. */ + } else { + tran_data[ tran_nfun ].name = NULL; + tran_data[ tran_nfun ].purpose = + astFree( tran_data[ tran_nfun ].purpose ); + tran_data[ tran_nfun ].author = + astFree( tran_data[ tran_nfun ].author ); + tran_data[ tran_nfun ].contact = + astFree( tran_data[ tran_nfun ].contact ); + } + } + } + } + +/* If an error occurred, free the memory holding the cleaned function + name. */ + if ( !astOK ) clname = astFree( clname ); + +/* Mark the end of the section in which memory allocations may never be + freed (other than by any AST exit handler). */ + astEndPM; + +/* Unlock the mutex that ensures that only one thread can run this function + at any one time. */ + UNLOCK_MUTEX1; + +} + +void astIntraReg_( const char *name, int nin, int nout, + void (* tran)( AstMapping *, int, int, const double *[], + int, int, double *[] ), + unsigned int flags, const char *purpose, const char *author, + const char *contact, int *status ) { +/* +*++ +* Name: +c astIntraReg +f AST_INTRAREG + +* Purpose: +c Register a transformation function for use by an IntraMap. +f Register a transformation routine for use by an IntraMap. + +* Type: +* Public function. + +* Synopsis: +c #include "intramap.h" +c astIntraReg( const char *name, int nin, int nout, +c void (* tran)( AstMapping *, int, int, const double *[], +c int, int, double *[] ), +c unsigned int flags, const char *purpose, const char *author, +c const char *contact ) +f CALL AST_INTRAREG( NAME, NIN, NOUT, TRAN, FLAGS, PURPOSE, AUTHOR, +f CONTACT, STATUS ) + +* Class Membership: +* IntraMap member function. + +* Description: +c This function registers a privately-defined coordinate +c transformation function written in C so that it may be used to +c create an IntraMap. An IntraMap is a specialised form of Mapping +c which encapsulates the C function so that it may be used like +c any other AST Mapping. This allows you to create Mappings that +c perform any conceivable coordinate transformation. +f This function registers a privately-defined coordinate +f transformation routine written in Fortran so that it may be used +f to create an IntraMap. An IntraMap is a specialised form of +f Mapping which encapsulates the Fortran routine so that it may be +f used like any other AST Mapping. This allows you to create +f Mappings that perform any conceivable coordinate transformation. +* +c Registration of relevant transformation functions is required +c before using the astIntraMap constructor function to create an +c IntraMap or reading an external representation of an IntraMap +c from a Channel. +f Registration of relevant transformation routines is required +f before using the AST_INTRAMAP constructor function to create an +f IntraMap or reading an external representation of an IntraMap +f from a Channel. + +* Parameters: +c name +f NAME = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing a unique name +c to be associated with the transformation function in order to +c identify it. This name is case sensitive. All white space +c will be removed before use. +f A character string containing a unique name to be associated +f with the transformation routine in order to identify it. This +f name is case sensitive. All white space will be removed +f before use. +c nin +f NIN = INTEGER (Given) +c The number of input coordinates accepted by the +c transformation function (i.e. the number of dimensions of the +c space in which the input points reside). A value of AST__ANY +c may be given if the function is able to accommodate a +c variable number of input coordinates. +f The number of input coordinates accepted by the +f transformation routine (i.e. the number of dimensions of the +f space in which the input points reside). A value of AST__ANY +f may be given if the routine is able to accommodate a variable +f number of input coordinates. +c nout +f NOUT = INTEGER (Given) +c The number of output coordinates produced by the +c transformation function (i.e. the number of dimensions of the +c space in which the output points reside). A value of AST__ANY +c may be given if the function is able to produce a variable +c number of output coordinates. +f The number of output coordinates produced by the +f transformation routine (i.e. the number of dimensions of the +f space in which the output points reside). A value of AST__ANY +f may be given if the routine is able to produce a variable +f number of output coordinates. +c tran +f TRAN = SUBROUTINE (Given) +c Pointer to the transformation function to be registered. +c This function should perform whatever coordinate +c transformations are required and should have an interface +c like astTranP (q.v.). +f The transformation routine to be registered. This routine +f should perform whatever coordinate transformations are +f required and should have an interface like AST_TRANN (q.v.). +f +f This transformation routine must also appear in an EXTERNAL +f statement in the routine which calls AST_INTRAREG. +c flags +f FLAGS = INTEGER (Given) +c This value may be used to supply a set of flags which +c describe the transformation function and which may affect the +c behaviour of any IntraMap which uses it. Often, a value of +c zero will be given here, but you may also supply the bitwise +c OR of a set of flags as described in the "Transformation +c Flags" section (below). +f This value may be used to supply a set of flags which +f describe the transformation routine and which may affect the +f behaviour of any IntraMap which uses it. Often, a value of +f zero will be given here, but you may also supply the sum of a +f set of flags as described in the "Transformation Flags" +f section (below). +c purpose +f PURPOSE = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing a short (one +c line) textual comment to describe the purpose of the +c transformation function. +f A character string containing a short (one line) textual +f comment to describe the purpose of the transformation +f routine. +c author +f AUTHOR = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing the name of +c the author of the transformation function. +f A character string containing the name of the author of the +f transformation routine. +c contact +f CONTACT = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing contact +c details for the author of the transformation function +c (e.g. an e-mail or WWW address). If any IntraMap which uses +c this transformation function is exported as part of a dataset +c to an external user who does not have access to the function, +c then these contact details should allow them to obtain the +c necessary code. +f A character string containing contact details for the author +f of the transformation routine (e.g. an e-mail or WWW +f address). If any IntraMap which uses this transformation +f routine is exported as part of a dataset to an external user +f who does not have access to the routine, then these contact +f details should allow them to obtain the necessary code. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - Beware that an external representation of an IntraMap (created +c by writing it to a Channel) will not include the coordinate +c transformation function which it uses, so will only refer to the +c function by its name (as assigned using astIntraReg). +c Consequently, the external representation cannot be utilised by +c another program unless that program has also registered the same +c transformation function with the same name using an identical +c invocation of astIntraReg. If no such registration has been +c performed, then attempting to read the external representation +c will result in an error. +f - Beware that an external representation of an IntraMap (created +f by writing it to a Channel) will not include the coordinate +f transformation routine which it uses, so will only refer to the +f routine by its name (as assigned using AST_INTRAREG). +f Consequently, the external representation cannot be utilised by +f another program unless that program has also registered the same +f transformation routine with the same name using an identical +f invocation of AST_INTRAREG. If no such registration has been +f performed, then attempting to read the external representation +f will result in an error. +c - You may use astIntraReg to register a transformation function +c with the same name more than once, but only if the arguments +c supplied are identical on each occasion (i.e there is no way of +c changing things once a function has been successfully registered +c under a given name, and attempting to do so will result in an +c error). This feature simply allows registration to be performed +c independently, but consistently, at several places within your +c program, without having to check whether it has already been +c done. +f - You may use AST_INTRAREG to register a transformation routine +f with the same name more than once, but only if the arguments +f supplied are identical on each occasion (i.e there is no way of +f changing things once a routine has been successfully registered +f under a given name, and attempting to do so will result in an +f error). This feature simply allows registration to be performed +f independently, but consistently, at several places within your +f program, without having to check whether it has already been +f done. +c - If an error occurs in the transformation function, this may be +c indicated by setting the AST error status to an error value +c (using astSetStatus) before it returns. This will immediately +c terminate the current AST operation. The error value AST__ITFER +c is available for this purpose, but other values may also be used +c (e.g. if you wish to distinguish different types of error). +f - If an error occurs in the transformation routine, this may be +f indicated by setting its STATUS argument to an error value +f before it returns. This will immediately terminate the current +f AST operation. The error value AST__ITFER is available for this +f purpose, but other values may also be used (e.g. if you wish to +f distinguish different types of error). The AST__ITFER error +f value is defined in the AST_ERR include file. + +* Transformation Flags: +c The following flags are defined in the ``ast.h'' header file and +c allow you to provide further information about the nature of the +c transformation function. Having selected the set of flags which +c apply, you should supply the bitwise OR of their values as the +c ``flags'' argument to astIntraReg. +f The following flags are defined in the AST_PAR include file and +f allow you to provide further information about the nature of the +f transformation routine. Having selected the set of flags which +f apply, you should supply the sum of their values as the FLAGS +f argument to AST_INTRAREG. + +c - AST__NOFWD: If this flag is set, it indicates that the +c transformation function does not implement a forward coordinate +c transformation. In this case, any IntraMap which uses it will +c have a TranForward attribute value of zero and the +c transformation function itself will not be invoked with its +c ``forward'' argument set to a non-zero value. By default, it is +c assumed that a forward transformation is provided. +f - AST__NOFWD: If this flag is set, it indicates that the +f transformation routine does not implement a forward coordinate +f transformation. In this case, any IntraMap which uses it will +f have a TranForward attribute value of zero and the +f transformation routine itself will not be called with its +f FORWARD argument set to .TRUE.. By default, it is assumed that a +f forward transformation is provided. +c - AST__NOINV: If this flag is set, it indicates that the +c transformation function does not implement an inverse coordinate +c transformation. In this case, any IntraMap which uses it will +c have a TranInverse attribute value of zero and the +c transformation function itself will not be invoked with its +c ``forward'' argument set to zero. By default, it is assumed +c that an inverse transformation is provided. +f - AST__NOINV: If this flag is set, it indicates that the +f transformation routine does not implement an inverse coordinate +f transformation. In this case, any IntraMap which uses it will +f have a TranInverse attribute value of zero and the +f transformation routine itself will not be called with its +f FORWARD argument set to .FALSE.. By default, it is assumed that +f an inverse transformation is provided. +c - AST__SIMPFI: You may set this flag if applying the +c transformation function's forward coordinate transformation, +c followed immediately by the matching inverse transformation, +c should always restore the original set of coordinates. It +c indicates that AST may replace such a sequence of operations by +c an identity Mapping (a UnitMap) if it is encountered while +c simplifying a compound Mapping (e.g. using astSimplify). It is +c not necessary that both transformations have actually been +c implemented. +f - AST__SIMPFI: You may set this flag if applying the +f transformation routine's forward coordinate transformation, +f followed immediately by the matching inverse transformation, +f should always restore the original set of coordinates. It +f indicates that AST may replace such a sequence of operations by +f an identity Mapping (a UnitMap) if it is encountered while +f simplifying a compound Mapping (e.g. using AST_SIMPLIFY). It is +f not necessary that both transformations have actually been +f implemented. +c - AST__SIMPIF: You may set this flag if applying the +c transformation function's inverse coordinate transformation, +c followed immediately by the matching forward transformation, +c should always restore the original set of coordinates. It +c indicates that AST may replace such a sequence of operations by +c an identity Mapping (a UnitMap) if it is encountered while +c simplifying a compound Mapping (e.g. using astSimplify). It is +c not necessary that both transformations have actually been +c implemented. +f - AST__SIMPIF: You may set this flag if applying the +f transformation routine's inverse coordinate transformation, +f followed immediately by the matching forward transformation, +f should always restore the original set of coordinates. It +f indicates that AST may replace such a sequence of operations by +f an identity Mapping (a UnitMap) if it is encountered while +f simplifying a compound Mapping (e.g. using AST_SIMPLIFY). It is +f not necessary that both transformations have actually been +f implemented. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Register the transformation function together with the appropriate + wrapper function for the C language. */ + IntraReg( name, nin, nout, tran, TranWrap, flags, purpose, author, + contact, status ); +} + +void astIntraRegFor_( const char *name, int nin, int nout, + void (* tran)( AstMapping *, int, int, const double *[], + int, int, double *[] ), + void (* tran_wrap)( void (*)( AstMapping *, int, int, + const double *[], int, int, + double *[] ), + AstMapping *, int, int, + const double *[], int, int, + double *[], int * ), + unsigned int flags, const char *purpose, + const char *author, const char *contact, int *status ) { +/* +*+ +* Name: +* astIntraRegFor + +* Purpose: +* Register a foreign language transformation function for an IntraMap. + +* Type: +* Public function. + +* Synopsis: +* #include "intramap.h" +* void astIntraRegFor( const char *name, int nin, int nout, +* void (* tran)( AstMapping *, int, int, +* const double *[], int, int, +* double *[] ), +* void (* tran_wrap)( void (*)( AstMapping *, int, +* int, const double *[], +* int, int, +* double *[] ), +* AstMapping *, int, int, +* const double *[], int, int, +* double *[], int * ), +* unsigned int flags, const char *purpose, +* const char *author, const char *contact ) + +* Class Membership: +* IntraMap member function. + +* Description: +* This function registers a transformation function provided by a +* foreign language interface which will later be used by an +* IntraMap, and associates it with a name. It also stores related +* information which will be required by the IntraMap. + +* Parameters: +* name +* Pointer to a null-terminated string containing the name to be +* used to identify the transformation function. This string is +* case sensitive. All white space is removed before use. +* nin +* The number of input coordinates per point (or AST__ANY if any +* number are allowed). +* nout +* The number of output coordinates per point (or AST__ANY if +* any number are allowed). +* tran +* Pointer to the foreign language transformation function to be +* registered. This may have any form of interface, which need +* not be known to the implementation of the IntraMap +* class. Instead, the method of invoking the transformation +* function should be encapsulated in the "tran_wrap" function +* (below). +* tran_wrap +* Pointer to a wrapper function appropriate to the foreign +* language interface. This wrapper function should have the +* same interface as astTranP (from the Mapping class), except +* that it takes a pointer to a function like "tran" as an additional +* first argument. The purpose of this wrapper is to invoke the +* transformation function via the pointer supplied, to pass it the +* necessary information derived from the remainder of its arguments, +* and then to return the results. +* flags +* This argument may be used to supply a set of flags which +* control the behaviour of any IntraMap which uses the +* registered transformation function. See the description of +* astIntraReg for details. +* purpose +* Pointer to a null-terminated string containing a short (one +* line) textual comment to describe the purpose of the +* transformation function. +* author +* Pointer to a null-terminated string containing the name of +* the author of the transformation function. +* contact +* Pointer to a null-terminated string containing contact +* details for the author of the transformation function +* (e.g. an e-mail address or URL). If any IntraMap using this +* transformation function is exported as part of a dataset to +* an external user who does not have access to the function, +* then these contact details should allow them to obtain the +* necessary code. + +* Notes: +* - This function is only available through the public interface +* to the IntraMap class (not the protected interface) and is +* intended solely for use in implementing foreign language +* interfaces to this class. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Register the transformation function together with the appropriate + wrapper function for the foreign language interface. */ + IntraReg( name, nin, nout, tran, tran_wrap, flags, purpose, author, + contact, status ); +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing an IntraMap. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* IntraMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated IntraMap in the sequence with its +* neighbours, so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated IntraMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated IntraMap which is to be merged with +* its neighbours. This should be a cloned copy of the IntraMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* IntraMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated IntraMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstIntraMap *intramap1; /* Pointer to first IntraMap */ + AstIntraMap *intramap2; /* Pointer to second IntraMap */ + AstMapping *new; /* Pointer to replacement Mapping */ + const char *class; /* Pointer to Mapping class string */ + int imap1; /* Index of first IntraMap */ + int imap2; /* Index of second IntraMap */ + int imap; /* Loop counter for Mappings */ + int invert1; /* Invert flag value (1st IntraMap) */ + int invert2; /* Invert flag value (2nd IntraMap) */ + int nin1; /* No. input coordinates (1st IntraMap) */ + int nout2; /* No. output coordinates (2nd IntraMap) */ + int result; /* Result value to return */ + int simpler; /* Mappings simplified? */ + +/* Initialise the returned result. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Further initialisation. */ + new = NULL; + simpler = 0; + nin1 = -1; + +/* We will only handle the case of IntraMaps in series and will + consider merging the nominated IntraMap with the Mapping which + follows it. Check that there is such a Mapping. */ + if ( series && ( ( where + 1 ) < *nmap ) ) { + +/* Obtain the indices of the two potential IntraMaps to be merged and + a pointer to the first one. */ + imap1 = where; + imap2 = where + 1; + intramap1 = (AstIntraMap *) ( *map_list )[ imap1 ]; + +/* Obtain the Class string of the second Mapping and determine if it + is an IntraMap. */ + class = astGetClass( ( *map_list )[ imap2 ] ); + if ( astOK && !strcmp( class, "IntraMap" ) ) { + +/* Obtain a pointer to the second IntraMap. */ + intramap2 = (AstIntraMap *) ( *map_list )[ imap2 ]; + +/* Check that the two IntraMaps use the same transformation function + and have the same IntraFlag string (if set). */ + if ( ( intramap1->ifun == intramap2->ifun ) && + !strcmp( intramap1->intraflag ? intramap1->intraflag : "", + intramap2->intraflag ? intramap2->intraflag : "" ) ) { + +/* Determine the number of input coordinates that the first IntraMap + would have if its Invert attribute were set to the value of the + associated invert flag. Take account of the current Invert + attribute in obtaining this value. */ + invert1 = ( *invert_list )[ imap1 ]; + if ( astGetInvert( intramap1 ) ) { + nin1 = invert1 ? astGetNin( intramap1 ) : + astGetNout( intramap1 ); + } else { + nin1 = invert1 ? astGetNout( intramap1 ) : + astGetNin( intramap1 ); + } + +/* Similarly, determine the number of output coordinates that the + second IntraMap would have. */ + invert2 = ( *invert_list )[ imap2 ]; + if ( astGetInvert( intramap2 ) ) { + nout2 = invert2 ? astGetNout( intramap2 ) : + astGetNin( intramap2 ); + } else { + nout2 = invert2 ? astGetNin( intramap2 ) : + astGetNout( intramap2 ); + } + +/* Check that the effect of applying the two IntraMaps will be to + preserve the number of coordinates. */ + if ( astOK && ( nin1 == nout2 ) ) { + +/* If so, check if the first transformation function is applied in the + forward direction and the second in the inverse direction. If so, + note if this configuration can be simplified. */ + if ( !invert1 && invert2 ) { + simpler = tran_data[ intramap1->ifun ].flags & AST__SIMPFI; + +/* Similarly, if the first transformation function is applied in the + inverse direction and the second in the forward direction, then + note if this configuration can be simplified. */ + } else if ( invert1 && !invert2 ) { + simpler = tran_data[ intramap1->ifun ].flags & AST__SIMPIF; + } + } + } + } + +/* If the two IntraMaps can be simplified, create a UnitMap to replace + them. */ + if ( simpler ) { + new = (AstMapping *) astUnitMap( nin1, "", status ); + +/* Annul the pointers to the IntraMaps. */ + if ( astOK ) { + ( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] ); + ( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] ); + +/* Insert the pointer to the replacement Mapping and initialise its + invert flag. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Loop to close the resulting gap by moving subsequent elements down + in the arrays. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - 1 ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - 1 ] = ( *invert_list )[ imap ]; + } + +/* Clear the vacated elements at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = imap1; + } + } + } + +/* If an error occurred, clear the returned result. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for an IntraMap. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* IntraMap member function (over-rides the astSetAttrib method inherited +* from the Mapping class). + +* Description: +* This function assigns an attribute value for a IntraMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the IntraMap. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Vaiables: */ + AstIntraMap *this; /* Pointer to the IntraMap structure */ + int intraflag; /* Offset of IntraFlag value in string */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the IntraMap structure. */ + this = (AstIntraMap *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* IntraFlag. */ +/* ---------- */ + if ( nc = 0, + ( 0 == astSscanf( setting, "intraflag=%n%*[^\n]%n", &intraflag, &nc ) ) + && ( nc >= len ) ) { + astSetIntraFlag( this, setting + intraflag ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for an IntraMap. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* IntraMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a IntraMap's attributes. + +* Parameters: +* this +* Pointer to the IntraMap. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstIntraMap *this; /* Pointer to the IntraMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the IntraMap structure. */ + this = (AstIntraMap *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* IntraFlag. */ +/* ---------- */ + if ( !strcmp( attrib, "intraflag" ) ) { + result = astTestIntraFlag( this ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply an IntraMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* IntraMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a IntraMap and a set of points encapsulated +* in a PointSet and transforms the points using the transformation +* function associated with the IntraMap. + +* Parameters: +* this +* Pointer to the IntraMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate +* transformation should be applied, while a zero value requests +* the inverse transformation. +* out +* Pointer to a PointSet which will hold the transformed +* (output) coordinate values. A NULL value may also be given, +* in which case a new PointSet will be created by this +* function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - The number of coordinate values per point in the input +* PointSet must match the number of coordinates for the IntraMap +* being applied. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and coordinate values per point to +* accommodate the result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstIntraMap *this; /* Pointer to IntraMap structure */ + AstMapping *id; /* Public ID for the IntraMap supplied */ + AstPointSet *result; /* Pointer to output PointSet */ + const double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + int ncoord_in; /* Number of coordinates per input point */ + int ncoord_out; /* Number of coordinates per output point */ + int npoint; /* Number of points */ + int ok; /* AST status OK? */ + int status_value; /* AST status value */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_mapping); + +/* Obtain a pointer to the IntraMap structure. */ + this = (AstIntraMap *) this_mapping; + +/* Apply the parent mapping using the stored pointer to the Transform + member function inherited from the parent Mapping class. This + function validates all arguments and generates an output PointSet + if necessary, but does not actually transform any coordinate + values. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the + input and output PointSets and obtain pointers for accessing the + input and output coordinate values. */ + npoint = astGetNpoint( in ); + ncoord_in = astGetNcoord( in ); + ncoord_out = astGetNcoord( result ); + ptr_in = (const double **) astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse transformation, + according to the direction specified and whether the Mapping has + been inverted. */ + if ( astGetInvert( this ) ) forward = !forward; + +/* Obtain a public (external) ID for the IntraMap. This will be + required (instead of a true C pointer) by the transformation function, + since it is user-written. Clone the IntraMap pointer so that the call + to astAnnulID later on does not annul the IntraMap pointer. */ + id = (AstMapping *) astMakeId( astClone( this ) ); + +/* Locate the transformation function data associated with the + IntraMap and use the wrapper function to invoke the transformation + function itself. */ + if ( ( ok = astOK ) ) { + LOCK_MUTEX2; + ( *tran_data[ this->ifun ].tran_wrap )( tran_data[ this->ifun ].tran, + id, npoint, ncoord_in, ptr_in, + forward, ncoord_out, ptr_out, + status ); + UNLOCK_MUTEX2; + +/* If an error occurred, report a contextual error message. To ensure + that the location of the error appears in the message, we first clear + the global status (which makes the error system think this is the + first report). */ + if ( !( ok = astOK ) ) { + status_value = astStatus; + astClearStatus; + astError( status_value, + "astTransform(%s): Error signalled by \"%s\" " + "transformation function.", status, + astGetClass( this ), tran_data[ this->ifun ].name ); + } + } + +/* Annul the external identifier. */ + id = astMakeId( astAnnulId( id ) ); + +/* If an error occurred here, but earlier steps were successful, then + something has happened to the external ID, so report a contextual + error message. */ + if ( !astOK && ok ) { + astError( astStatus, + "astTransform(%s): %s pointer corrupted by \"%s\" " + "transformation function.", status, + astGetClass( this ), astGetClass( this ), + tran_data[ this->ifun ].name ); + } + +/* If an error occurred, clear the returned pointer. If a new output + PointSet has been created, then delete it. */ + if ( !astOK ) { + result = ( result == out ) ? NULL : astDelete( result ); + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void TranWrap( void (* tran)( AstMapping *, int, int, const double *[], + int, int, double *[] ), + AstMapping *this, int npoint, int ncoord_in, + const double *ptr_in[], int forward, int ncoord_out, + double *ptr_out[], int *status ) { +/* +* Name: +* TranWrap + +* Purpose: +* Wrapper function to invoke a C transformation function. + +* Type: +* Private function. + +* Synopsis: +* void TranWrap( void (* tran)( AstMapping *, int, int, const double *[], +* int, int, double *[] ), +* AstMapping *this, int npoint, int ncoord_in, +* const double *ptr_in[], int forward, int ncoord_out, +* double *ptr_out[], int *status ) + +* Class Membership: +* IntraMap member function. + +* Description: +* This function invokes a C implementation of a transformation +* function (which resembles the astTranP function from the Mapping +* class). +* +* This wrapper is essentially a dummy function for the C language. +* It may be replaced by alternative versions for foreign language +* interfaces, thus allowing transformation functions supplied by +* those languages to be invoked without knowledge of their +* interfaces. + +* Parameters: +* tran +* Pointer to the transformation function to be invoked. This +* should resemble astTranP (but with the first argument +* omitted). +* this +* An external Mapping ID associated with the internal (true C) pointer +* for the IntraMap whose transformation is being evaluated. +* npoint +* The number of points to be transformed. +* ncoord_in +* The number of coordinates being supplied for each input point +* (i.e. the number of dimensions of the space in which the +* input points reside). +* ptr_in +* An array of pointers to double, with "ncoord_in" +* elements. Element "ptr_in[coord]" should point at the first +* element of an array of double (with "npoint" elements) which +* contain the values of coordinate number "coord" for each +* input (untransformed) point. The value of coordinate number +* "coord" for input point number "point" is therefore given by +* "ptr_in[coord][point]". +* forward +* A non-zero value indicates that the forward coordinate +* transformation is to be applied, while a zero value indicates +* that the inverse transformation should be used. +* ncoord_out +* The number of coordinates being generated for each output +* point (i.e. the number of dimensions of the space in which +* the output points reside). This need not be the same as +* "ncoord_in". +* ptr_out +* An array of pointers to double, with "ncoord_out" +* elements. Element "ptr_out[coord]" should point at the first +* element of an array of double (with "npoint" elements) into +* which the values of coordinate number "coord" for each output +* (transformed) point will be written. The value of coordinate +* number "coord" for output point number "point" will therefore +* be found in "ptr_out[coord][point]". +* status +* Pointer to the inherited status value. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the transformation function. */ + ( *tran )( this, npoint, ncoord_in, ptr_in, forward, ncoord_out, ptr_out ); +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* IntraFlag + +* Purpose: +* IntraMap identification string. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +c This attribute allows an IntraMap to be flagged so that it is +c distinguishable from other IntraMaps. The transformation function +c associated with the IntraMap may then enquire the value of this +c attribute and adapt the transformation it provides according to the +c particular IntraMap involved. +f This attribute allows an IntraMap to be flagged so that it is +f distinguishable from other IntraMaps. The transformation routine +f associated with the IntraMap may then enquire the value of this +f attribute and adapt the transformation it provides according to the +f particular IntraMap involved. +* +c Although this is a string attribute, it may often be useful to store +c numerical values here, encoded as a character string, and to use these +c as data within the transformation function. Note, however, that this +c mechanism is not suitable for transferring large amounts of data (more +c than about 1000 characters) to an IntraMap. For that purpose, global +c variables are recommended, although the IntraFlag value can be used to +c supplement this approach. The default IntraFlag value is an empty +c string. +f Although this is a string attribute, it may often be useful to store +f numerical values here, encoded as a character string, and to use these +f as data within the transformation routine. Note, however, that this +f mechanism is not suitable for transferring large amounts of data (more +f than about 1000 characters) to an IntraMap. For that purpose, global +f variables are recommended, although the IntraFlag value can be used to +f supplement this approach. The default IntraFlag value is an empty +f string. + +* Applicability: +* IntraMap +* All IntraMaps have this attribute. + +* Notes: +c - A pair of IntraMaps whose transformations may potentially cancel +c cannot be simplified to produce a UnitMap (e.g. using astSimplify) +c unless they have the same IntraFlag values. The test for equality is +c case-sensitive. +f - A pair of IntraMaps whose transformations may potentially cancel +f cannot be simplified to produce a UnitMap (e.g. using AST_SIMPLIFY) +f unless they have the same IntraFlag values. The test for equality is +f case-sensitive. +*att-- +*/ + +/* Clear the IntraFlag value by freeing the allocated memory and + assigning a NULL pointer. */ +astMAKE_CLEAR(IntraMap,IntraFlag,intraflag,astFree( this->intraflag )) + +/* Return a pointer to the IntraFlag value. */ +astMAKE_GET(IntraMap,IntraFlag,const char *,NULL,this->intraflag) + +/* Set a IntraFlag value by freeing any previously allocated memory, + allocating new memory, storing the string and saving the pointer to + the copy. */ +astMAKE_SET(IntraMap,IntraFlag,const char *,intraflag,astStore( + this->intraflag, value, + strlen( value ) + + (size_t) 1 )) + +/* The IntraFlag value is set if the pointer to it is not NULL. */ +astMAKE_TEST(IntraMap,IntraFlag,( this->intraflag != NULL )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for IntraMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for IntraMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstIntraMap *in; /* Pointer to input IntraMap */ + AstIntraMap *out; /* Pointer to output IntraMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output IntraMaps. */ + in = (AstIntraMap *) objin; + out = (AstIntraMap *) objout; + +/* For safety, first clear any references to the input memory from + the output IntraMap. */ + out->intraflag = NULL; + +/* If necessary, allocate memory in the output IntraMap and store a + copy of the input IntraFlag string. */ + if ( in->intraflag ) out->intraflag = astStore( NULL, in->intraflag, + strlen( in->intraflag ) + + (size_t) 1 ); + +/* If an error occurred, free any allocated memory. */ + if ( !astOK ) { + out->intraflag = astFree( out->intraflag ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for IntraMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for IntraMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstIntraMap *this; /* Pointer to IntraMap */ + +/* Obtain a pointer to the IntraMap structure. */ + this = (AstIntraMap *) obj; + +/* Free the memory used for the IntraFlag string if necessary. */ + this->intraflag = astFree( this->intraflag ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for IntraMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the IntraMap class to an output Channel. + +* Parameters: +* this +* Pointer to the IntraMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstIntraMap *this; /* Pointer to the IntraMap structure */ + const char *sval; /* Pointer to string value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the IntraMap structure. */ + this = (AstIntraMap *) this_object; + +/* Write out values representing the instance variables for the + IntraMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* Transformation function name. */ +/* ----------------------------- */ + astWriteString( channel, "Fname", 1, 1, tran_data[ this->ifun ].name, + "Name of transformation function" ); + +/* IntraFlag string. */ +/* ----------------- */ + set = TestIntraFlag( this, status ); + sval = set ? GetIntraFlag( this, status ) : astGetIntraFlag( this ); + astWriteString( channel, "Iflag", set, 0, sval, + "IntraMap identification string" ); + +/* Purpose string. */ +/* --------------- */ + astWriteString( channel, "Purp", 1, 1, tran_data[ this->ifun ].purpose, + "Purpose of function" ); + +/* Author's name. */ +/* -------------- */ + astWriteString( channel, "Auth", 1, 1, tran_data[ this->ifun ].author, + "Author's name" ); + +/* Contact details. */ +/* ---------------- */ + astWriteString( channel, "Cntact", 1, 1, tran_data[ this->ifun ].contact, + "Contact address" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAIntraMap and astCheckIntraMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(IntraMap,Mapping) +astMAKE_CHECK(IntraMap) + +AstIntraMap *astIntraMap_( const char *name, int nin, int nout, + const char *options, int *status, ...) { +/* +*++ +* Name: +c astIntraMap +f AST_INTRAMAP + +* Purpose: +* Create an IntraMap. + +* Type: +* Public function. + +* Synopsis: +c #include "intramap.h" +c AstIntraMap *astIntraMap( const char *name, int nin, int nout, +c const char *options, ... ) +f RESULT = AST_INTRAMAP( NAME, NIN, NOUT, OPTIONS, STATUS ) + +* Class Membership: +* IntraMap constructor. + +* Description: +* This function creates a new IntraMap and optionally initialises +* its attributes. +* +c An IntraMap is a specialised form of Mapping which encapsulates +c a privately-defined coordinate transformation function +c (e.g. written in C) so that it may be used like any other AST +c Mapping. This allows you to create Mappings that perform any +c conceivable coordinate transformation. +f An IntraMap is a specialised form of Mapping which encapsulates +f a privately-defined coordinate transformation routine +f (e.g. written in Fortran) so that it may be used like any other +f AST Mapping. This allows you to create Mappings that perform any +f conceivable coordinate transformation. +* +* However, an IntraMap is intended for use within a single program +* or a private suite of software, where all programs have access +* to the same coordinate transformation functions (i.e. can be +* linked against them). IntraMaps should not normally be stored in +* datasets which may be exported for processing by other software, +* since that software will not have the necessary transformation +* functions available, resulting in an error. +* +c You must register any coordinate transformation functions to be +c used using astIntraReg before creating an IntraMap. +f You must register any coordinate transformation functions to be +f used using AST_INTRAREG before creating an IntraMap. + +* Parameters: +c name +f NAME = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing the name of +c the transformation function to use (which should previously +c have been registered using astIntraReg). This name is case +c sensitive. All white space will be removed before use. +f A character string containing the name of the transformation +f routine to use (which should previously have been registered +f using AST_INTRAREG). This name is case sensitive. All white +f space will be removed before use. +c nin +f NIN = INTEGER (Given) +c The number of input coordinates. This must be compatible with +c the number of input coordinates accepted by the +c transformation function (as specified when this function was +c registered using astIntraReg). +f The number of input coordinates. This must be compatible with +f the number of input coordinates accepted by the +f transformation routine (as specified when this routine was +f registered using AST_INTRAREG). +c nout +f NOUT = INTEGER (Given) +c The number of output coordinates. This must be compatible +c with the number of output coordinates produced by the +c transformation function (as specified when this function was +c registered using astIntraReg). +f The number of output coordinates. This must be compatible +f with the number of output coordinates produced by the +f transformation routine (as specified when this routine was +f registered using AST_INTRAREG). +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new IntraMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new IntraMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astIntraMap() +f AST_INTRAMAP = INTEGER +* A pointer to the new IntraMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstIntraMap *new; /* Pointer to new IntraMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the IntraMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitIntraMap( NULL, sizeof( AstIntraMap ), !class_init, + &class_vtab, "IntraMap", name, nin, nout ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + IntraMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new IntraMap. */ + return new; +} + +AstIntraMap *astIntraMapId_( const char *name, int nin, int nout, + const char *options, ... ) { +/* +* Name: +* astIntraMapId_ + +* Purpose: +* Create an IntraMap. + +* Type: +* Private function. + +* Synopsis: +* #include "intramap.h" +* AstIntraMap *astIntraMapId_( const char *name, int nin, int nout, +* const char *options, ... ) + +* Class Membership: +* IntraMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astIntraMap constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astIntraMap_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* +* The variable argument list also prevents this function from +* invoking astIntraMap_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. + +* Parameters: +* As for astIntraMap_. + +* Returned Value: +* The ID value associated with the new IntraMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstIntraMap *new; /* Pointer to new IntraMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the IntraMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitIntraMap( NULL, sizeof( AstIntraMap ), !class_init, + &class_vtab, "IntraMap", name, nin, nout ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + IntraMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new IntraMap. */ + return astMakeId( new ); +} + + +AstIntraMap *astInitIntraMap_( void *mem, size_t size, int init, + AstIntraMapVtab *vtab, const char *name, + const char *fname, int nin, int nout, int *status ) { +/* +*+ +* Name: +* astInitIntraMap + +* Purpose: +* Initialise an IntraMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "intramap.h" +* AstIntraMap *astInitIntraMap( void *mem, size_t size, int init, +* AstIntraMapVtab *vtab, const char *name, +* const char *fname, int nin, int nout ) + +* Class Membership: +* IntraMap initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new IntraMap object. It allocates memory (if +* necessary) to accommodate the IntraMap plus any additional data +* associated with the derived class. It then initialises a +* IntraMap structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for a IntraMap at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the IntraMap is to be +* initialised. This must be of sufficient size to accommodate +* the IntraMap data (sizeof(IntraMap)) plus any data used by +* the derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the IntraMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the IntraMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A logical flag indicating if the IntraMap's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new IntraMap. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* fname +* Pointer to a null-terminated string containing the name of +* the transformation function to be used, as previously +* registered using astIntraReg. +* nin +* The number of input coordinates. +* nout +* The number of output coordinates. + +* Returned Value: +* A pointer to the new IntraMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstIntraMap *new; /* Pointer to new IntraMap */ + char *clname; /* Cleaned transformation function name */ + int found; /* Transformation function name found? */ + int ifun; /* Loop counter for registered functions */ + +/* Initialise. */ + new = NULL; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + found = 0; + ifun = 0; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitIntraMapVtab( vtab, name ); + +/* Clean (and validate) the transformation function name supplied. */ + clname = CleanName( fname, "astIntraMap", status ); + +/* Search for a registered transformation function name which matches. */ + if ( astOK ) { + found = 0; + for ( ifun = 0; ifun < tran_nfun; ifun++ ) { + if ( ( found = !strcmp( clname, tran_data[ ifun ].name ) ) ) break; + } + } + +/* Free the memory containing the cleaned name string. */ + clname = astFree( clname ); + +/* If no match was found, then report an error. */ + if ( astOK ) { + if ( !found ) { + astError( AST__URITF, "astInitIntraMap(%s): The transformation " + "function \"%s\" has not been registered using " + "astIntraReg.", status, name, clname ); + +/* Check that the number of input coordinates is compatible with the + number used by the transformation function (as specified when it + was registered). Report an error if necessary. */ + } else { + if ( ( nin != tran_data[ ifun ].nin ) && + ( tran_data[ ifun ].nin != AST__ANY ) ) { + astError( AST__BADNI, "astInitIntraMap(%s): Number of input " + "coordinates (%d) does not match the number " + "used by the \"%s\" transformation function " + "(%d).", status, name, nin, tran_data[ ifun ].name, + tran_data[ ifun ].nin ); + +/* Similarly check the number of output coordinates. */ + } else if ( ( nout != tran_data[ ifun ].nout ) && + ( tran_data[ ifun ].nout != AST__ANY ) ) { + astError( AST__BADNO, "astInitIntraMap(%s): Number of output " + "coordinates (%d) does not match the number " + "used by the \"%s\" transformation function " + "(%d).", status, name, nout, tran_data[ ifun ].name, + tran_data[ ifun ].nout ); + +/* If OK, initialise a Mapping structure (the parent class) as the + first component within the IntraMap structure, allocating memory if + necessary (note that this also provides further checks on the + validity of "nin" and "nout"). Specify whether the forward and + inverse transformations are defined. */ + } else { + new = (AstIntraMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, nin, nout, + ( ( tran_data[ ifun ].flags & AST__NOFWD ) == 0 ), + ( ( tran_data[ ifun ].flags & AST__NOINV ) == 0 ) ); + + if ( astOK ) { + +/* Initialise the IntraMap data. */ +/* ---------------------------- */ +/* Initialise the IntraFlag string pointer. */ + new->intraflag = NULL; + +/* Store the index used to access the transformation function data. */ + new->ifun = ifun; + +/* If an error occurred, clean up by deleting the new IntraMap. */ + if ( !astOK ) new = astDelete( new ); + } + } + } + } + +/* Return a pointer to the new IntraMap. */ + return new; +} + +AstIntraMap *astLoadIntraMap_( void *mem, size_t size, + AstIntraMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadIntraMap + +* Purpose: +* Load an IntraMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "intramap.h" +* AstIntraMap *astLoadIntraMap( void *mem, size_t size, +* AstIntraMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* IntraMap loader. + +* Description: +* This function is provided to load a new IntraMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* IntraMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for an IntraMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the IntraMap is to be +* loaded. This must be of sufficient size to accommodate the +* IntraMap data (sizeof(IntraMap)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the IntraMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the IntraMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstIntraMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new IntraMap. If this is NULL, a pointer +* to the (static) virtual function table for the IntraMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "IntraMap" is used instead. + +* Returned Value: +* A pointer to the new IntraMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstIntraMap *new; /* Pointer to the new IntraMap */ + char *author; /* Pointer to author's name string */ + char *contact; /* Pointer to contact details string */ + char *fname; /* Pointer to transformation function name */ + char *purpose; /* Pointer to purpose comment string */ + int found; /* Function name found? */ + int ifun; /* Loop counter for registered functions */ + int nin; /* Number of IntraMap input coordinates */ + int nout; /* Number of IntraMap output coordinates */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this IntraMap. In this case the + IntraMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstIntraMap ); + vtab = &class_vtab; + name = "IntraMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitIntraMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built IntraMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "IntraMap" ); + +/* Now read each individual data item from this list. */ + +/* Transformation function name. */ +/* ----------------------------- */ + fname = astReadString( channel, "fname", "" ); + +/* IntraFlag string. */ +/* ----------------- */ + new->intraflag = astReadString( channel, "iflag", NULL ); + +/* Purpose string. */ +/* --------------- */ + purpose = astReadString( channel, "purp", "" ); + +/* Author's name. */ +/* -------------- */ + author = astReadString( channel, "auth", "" ); + +/* Contact details. */ +/* ---------------- */ + contact = astReadString( channel, "cntact", "" ); + +/* If OK, search the array of transformation function data to see if + the required transformation function has been registered. */ + if ( astOK ) { + found = 0; + for ( ifun = 0; ifun < tran_nfun; ifun++ ) { + if ( ( found = !strcmp( fname, tran_data[ ifun ].name ) ) ) break; + } + +/* If the transformation function has not been registered, report an + error explaining how to obtain it from the author and register + it. */ + if ( !found ) { + astError( AST__URITF, "astLoadIntraMap(%s): An IntraMap was read " + "which uses an unknown transformation " + "function.", status, astGetClass( channel ) ); + astError( AST__URITF, "This is a private extension to the AST " + "library: to handle it, you must obtain the " + "source code from its author." , status); + astError( AST__URITF, "You can then register it with AST in your " + "software by calling astIntraReg (see " + "SUN/211)." , status); + astError( AST__URITF, " " , status); + astError( AST__URITF, " Function name: \"%s\".", status, fname ); + astError( AST__URITF, " Purpose: \"%s\".", status, purpose ); + astError( AST__URITF, " Author: \"%s\".", status, author ); + astError( AST__URITF, " Contact address: \"%s\".", status, contact ); + astError( AST__URITF, " " , status); + +/* Obtain the numbers of input and output coordinates for the + IntraMap. Use parent methods for this, since if any derived class + has overridden these methods it may depend on data that have not + yet been loaded. */ + } else { + nin = ( *parent_getnin )( (AstMapping *) new, status ); + nout = ( *parent_getnout )( (AstMapping *) new, status ); + if ( astOK ) { + +/* Check that the numbers of coordinates are compatible with the + numbers used by the transformation function, as specified when it + was registered. */ + if ( ( nin != tran_data[ ifun ].nin ) && + ( tran_data[ ifun ].nin != AST__ANY ) ) { + astError( AST__BADNI, "astLoadIntraMap(%s): The number of " + "input coordinates for the IntraMap " + "read (%d) does not match the number " + "used by the registered \"%s\" " + "transformation function (%d).", status, + astGetClass( channel ), nin, + tran_data[ ifun ].name, + tran_data[ ifun ].nin ); + } else if ( ( nout != tran_data[ ifun ].nout ) && + ( tran_data[ ifun ].nout != AST__ANY ) ) { + astError( AST__BADNO, "astLoadIntraMap(%s): The number of " + "output coordinates for the IntraMap " + "read (%d) does not match the number " + "used by the registered \"%s\" " + "transformation function (%d).", status, + astGetClass( channel ), nout, + tran_data[ ifun ].name, + tran_data[ ifun ].nout ); + +/* If OK, store the index used to access the transformation function + data. */ + } else { + new->ifun = ifun; + } + } + } + } + +/* Free strings allocated by astReadString. */ + fname = astFree( fname ); + purpose = astFree( purpose ); + author = astFree( author ); + contact = astFree( contact ); + +/* If an error occurred, clean up by deleting the new IntraMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new IntraMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + diff --git a/intramap.h b/intramap.h new file mode 100644 index 0000000..ac2d8cd --- /dev/null +++ b/intramap.h @@ -0,0 +1,344 @@ +#if !defined( INTRAMAP_INCLUDED ) /* Include this file only once */ +#define INTRAMAP_INCLUDED +/* +*+ +* Name: +* intramap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the IntraMap class. + +* Invocation: +* #include "intramap.h" + +* Description: +* This include file defines the interface to the IntraMap class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. +* +* The IntraMap class implements Mappings which transform +* coordinates using a privately-defined transformation function +* (e.g. written in C). + +* Inheritance: +* The IntraMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* IntraFlag +* IntraMap identification string. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astMapMerge +* Simplify a sequence of Mappings containing an IntraMap. +* astTransform +* Transform a set of points using an IntraMap. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astClearIntraFlag +* Clear the IntraFlag attribute for an IntraMap. +* astGetIntraFlag +* Get the value of the IntraFlag attribute for an IntraMap. +* astSetIntraFlag +* Set the value of the IntraFlag attribute for an IntraMap. +* astTestIntraFlag +* Test whether a value has been set for the IntraFlag attribute of +* an IntraMap. + +* Other Class Functions: +* Public: +* astIntraMap +* Create an IntraMap. +* astIntraReg +* Register a transformation function for use by an IntraMap. +* astIsAIntraMap +* Test class membership. +* +* Protected: +* astCheckIntraMap +* Validate class membership. +* astInitIntraMap +* Initialise an IntraMap. +* astLoadIntraMap +* Load an IntraMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstIntraMap +* IntraMap object type. +* +* Protected: +* AstIntraMapVtab +* IntraMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 24-MAR-1998 (RFWS): +* Original version. +* 16-SEP-1999 (RFWS): +* Added the IntraFlag attribute and added a Mapping pointer as a new +* first argument to transformation functions. +* 8-JAN-2003 (DSB): +* Added protected astInitIntraMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macro Definitions. */ +/* ================== */ +#define AST__NOFWD (1U) /* No forward transformation defined */ +#define AST__NOINV (2U) /* No inverse transformation defined */ +#define AST__SIMPFI (4U) /* Forward-inverse may be simplified */ +#define AST__SIMPIF (8U) /* Inverse-forward may be simplified */ + +#define AST__ANY (-66) /* Allow any number of input/output coords */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* IntraMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstIntraMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + char *intraflag; /* Pointer to identification string */ + int ifun; /* Transformation function index */ +} AstIntraMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstIntraMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + const char *(* GetIntraFlag)( AstIntraMap *, int * ); + int (* TestIntraFlag)( AstIntraMap *, int * ); + void (* ClearIntraFlag)( AstIntraMap *, int * ); + void (* SetIntraFlag)( AstIntraMap *, const char *, int * ); +} AstIntraMapVtab; + + +/* Structure to hold data for transformation functions. */ +typedef struct AstIntraMapTranData { + void (* tran)( AstMapping *, int, int, const double *[], int, int, double *[] ); + /* Pointer to transformation function */ + void (* tran_wrap)( void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), AstMapping *, int, int, const double *[], int, int, double *[], int * ); + /* Pointer to wrapper function */ + char *author; /* Author's name */ + char *contact; /* Contact details (e.g. e-mail address) */ + char *name; /* Function name (assigned by caller) */ + char *purpose; /* Comment string describing purpose */ + int nin; /* Number of input coordinates per point */ + int nout; /* Number of output coordinates per point */ + unsigned int flags; /* Flags to describe function behaviour */ +} AstIntraMapTranData; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstIntraMapGlobals { + AstIntraMapVtab Class_Vtab; + int Class_Init; +} AstIntraMapGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(IntraMap) /* Check class membership */ +astPROTO_ISA(IntraMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstIntraMap *astIntraMap_( const char *, int, int, const char *, int *, ...); +#else +AstIntraMap *astIntraMapId_( const char *, int, int, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstIntraMap *astInitIntraMap_( void *, size_t, int, AstIntraMapVtab *, + const char *, const char *, int, int, int * ); + +/* Vtab initialiser. */ +void astInitIntraMapVtab_( AstIntraMapVtab *, const char *, int * ); + +/* Loader. */ +AstIntraMap *astLoadIntraMap_( void *, size_t, AstIntraMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitIntraMapGlobals_( AstIntraMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astIntraReg_( const char *, int, int, void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), unsigned int, const char *, const char *, const char *, int * ); + +#if defined(astCLASS) /* Protected */ +const char *astGetIntraFlag_( AstIntraMap *, int * ); +int astTestIntraFlag_( AstIntraMap *, int * ); +void astClearIntraFlag_( AstIntraMap *, int * ); +void astSetIntraFlag_( AstIntraMap *, const char *, int * ); + +#else /* Public only */ +void astIntraRegFor_( const char *, int, int, void (*)( AstMapping *, int, int, const double *[], int, int, double *[] ), void (*)( void (*)( AstMapping *, int, int, const double *[], int, int, double *[]), AstMapping *, int, int, const double *[], int, int, double *[], int * ), unsigned int, const char *, const char *, const char *, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckIntraMap(this) astINVOKE_CHECK(IntraMap,this,0) +#define astVerifyIntraMap(this) astINVOKE_CHECK(IntraMap,this,1) + +/* Test class membership. */ +#define astIsAIntraMap(this) astINVOKE_ISA(IntraMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astIntraMap astINVOKE(F,astIntraMap_) +#else +#define astIntraMap astINVOKE(F,astIntraMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitIntraMap(mem,size,init,vtab,name,fname,nin,nout) \ +astINVOKE(O,astInitIntraMap_(mem,size,init,vtab,name,fname,nin,nout,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitIntraMapVtab(vtab,name) astINVOKE(V,astInitIntraMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadIntraMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadIntraMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckIntraMap to validate IntraMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astIntraReg(name,nin,nout,tran,flags,purpose,author,contact) \ +astIntraReg_(name,nin,nout,tran,flags,purpose,author,contact,STATUS_PTR) + +#if defined(astCLASS) /* Protected */ +#define astClearIntraFlag(this) \ +astINVOKE(V,astClearIntraFlag_(astCheckIntraMap(this),STATUS_PTR)) +#define astGetIntraFlag(this) \ +astINVOKE(V,astGetIntraFlag_(astCheckIntraMap(this),STATUS_PTR)) +#define astSetIntraFlag(this,value) \ +astINVOKE(V,astSetIntraFlag_(astCheckIntraMap(this),value,STATUS_PTR)) +#define astTestIntraFlag(this) \ +astINVOKE(V,astTestIntraFlag_(astCheckIntraMap(this),STATUS_PTR)) + +#else /* Public only */ +#define astIntraRegFor(name,nin,nout,tran,tran_wrap,flags,purpose,author,contact) \ +astIntraRegFor_(name,nin,nout,tran,tran_wrap,flags,purpose,author,contact,STATUS_PTR) +#endif +#endif + + + + + diff --git a/keymap.c b/keymap.c new file mode 100644 index 0000000..dff514b --- /dev/null +++ b/keymap.c @@ -0,0 +1,10768 @@ +/* +*class++ +* Name: +* KeyMap + +* Purpose: +* Store a set of key/value pairs. + +* Constructor Function: +c astKeyMap +f AST_KEYMAP + +* Description: +* The KeyMap class is used to store a set of values with associated keys +* which identify the values. The keys are strings. These may be case +* sensitive or insensitive as selected by the KeyCase attribute, and +* trailing spaces are ignored. The value associated with a key can be +* integer (signed 4 and 2 byte, or unsigned 1 byte), floating point +* (single or double precision), +c void pointer, +* character string or AST Object pointer. Each +* value can be a scalar or a one-dimensional vector. A KeyMap is +* conceptually similar to a Mapping in that a KeyMap transforms an +* input into an output - the input is the key, and the output is the +* value associated with the key. However, this is only a conceptual +* similarity, and it should be noted that the KeyMap class inherits from +* the Object class rather than the Mapping class. The methods of the +* Mapping class cannot be used with a KeyMap. + +* Inheritance: +* The KeyMap class inherits from the Object class. + +* Attributes: +* In addition to those attributes common to all Objects, every +* KeyMap also has the following attributes: +* +* - KeyCase: Sets the case in which keys are stored +* - KeyError: Report an error if the requested key does not exist? +* - SizeGuess: The expected size of the KeyMap. +* - SortBy: Determines how keys are sorted in a KeyMap. +* - MapLocked: Prevent new entries being added to the KeyMap? + +* Functions: +c In addition to those functions applicable to all Objects, the +c following functions may also be applied to all KeyMaps: +f In addition to those routines applicable to all Objects, the +f following routines may also be applied to all KeyMaps: +* +c - astMapDefined: Does a KeyMap contain a defined value for a key? +c - astMapGet0: Get a named scalar entry from a KeyMap +c - astMapGet1: Get a named vector entry from a KeyMap +c - astMapGetElem: Get an element of a named vector entry from a KeyMap +c - astMapHasKey: Does the KeyMap contain a named entry? +c - astMapKey: Return the key name at a given index in the KeyMap +c - astMapLenC: Get the length of a named character entry in a KeyMap +c - astMapLength: Get the length of a named entry in a KeyMap +c - astMapCopy: Copy entries from one KeyMap into another +c - astMapPut0: Add a new scalar entry to a KeyMap +c - astMapPut1: Add a new vector entry to a KeyMap +c - astMapPutElem: Puts a value into a vector entry in a KeyMap +c - astMapPutU: Add a new entry to a KeyMap with an undefined value +c - astMapRemove: Removed a named entry from a KeyMap +c - astMapRename: Rename an existing entry in a KeyMap +c - astMapSize: Get the number of entries in a KeyMap +c - astMapType: Return the data type of a named entry in a map +f - AST_MAPDEFINED: Does a KeyMap contain a defined value for a key? +f - AST_MAPGET0: Get a named scalar entry from a KeyMap +f - AST_MAPGET1: Get a named vector entry from a KeyMap +f - AST_MAPGETELEM: Get an element of a named vector entry from a KeyMap +f - AST_MAPHASKEY: Does the KeyMap contain a named entry? +f - AST_MAPKEY: Return the key name at a given index in the KeyMap +f - AST_MAPLENC: Get the length of a named character entry in a KeyMap +f - AST_MAPLENGTH: Get the length of a named entry in a KeyMap +f - AST_MAPCOPY: Copy entries from one KeyMap into another +f - AST_MAPPUT0: Add a new scalar entry to a KeyMap +f - AST_MAPPUT1: Add a new vector entry to a KeyMap +f - AST_MAPPUTELEM: Puts a value into a vector entry in a KeyMap +f - AST_MAPPUTU: Add a new entry to a KeyMap with an undefined value +f - AST_MAPREMOVE: Removed a named entry from a KeyMap +f - AST_MAPRENAME: Rename an existing entry in a KeyMap +f - AST_MAPSIZE: Get the number of entries in a KeyMap +f - AST_MAPTYPE: Return the data type of a named entry in a map + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2008-2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: B.S. Berry (Starlink) + +* History: +* 12-NOV-2004 (DSB): +* Original version. +* 5-JAN-2005 (DSB): +* Added astMapLenC method. +* 17-JAN-2005 (DSB): +* Remove "void *" arithmetic. +* 25-JAN-2005 (DSB): +* Added more DEBUG blocks +* 30-SEP-2005 (DSB): +* Allow an integer to be read from a formatted floating point value. +* 6-DEC-2005 (DSB): +* Remove astMapGet0C stuff from description of astMapGet1C. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 5-JUN-2006 (DSB): +* Added support for single precision entries. +* 30-NOV-2007 (DSB): +* Added SizeGuess attribute. +* 4-DEC-2007 (DSB): +* Allow size of hash table to grow dynamically as more entries are +* added to the KeyMap. +* 5-DEC-2007 (DSB): +* Ensure mapsize is always a power of 2. +* 6-DEC-2007 (DSB): +* - Define the minium table size rather than the default SizeGuess +* value, and derive the default SizeGuess value from the minimum +* table size. +* - Use "&" rather than "%" to get the hash table index from the +* full width hash value (& may be faster than %). +* 7-MAR-2008 (DSB): +* Added support for pointer ("P") entries. +* 31-MAR-2009 (DSB): +* Remove rounding errors from formatted double values. +* 27-APR-2009 (DSB): +* Added astMapGetElem. +* 1-SEP-2009 (DSB): +* Added KeyError attribute. +* 12-FEB-2010 (DSB): +* When converting an entry value between double and string, treat +* "" as the formatted version of AST__BAD. +* 3-MAR-2010 (DSB): +* Added astMapPutElem. +* 27-APR-2010 (DSB): +* Added MapLocked attribute. +* 4-MAY-2010 (DSB): +* - Propagate MapLocked and KeyError attributes to any encapsulated +* KeyMaps. +* - Added astMapCopy method. +* - Added astMapPutU method and AST__UNDEFTYPE data type. +* 11-AUG-2010 (DSB): +* Added SortBy attribute. +* 12-AUG-2010 (DSB): +* Speed up access to large KeyMaps. +* 13-AUG-2010 (DSB): +* - No need to sort all entries when doubling the table size since +* changing the table size does not change the linked list of sorted +* entries. +* - Initialise the sortby attribute to the cleared value, rather +* than the default value. +* 2-OCT-2010 (DSB): +* Added support for short int valued entries. +* 24-NOV-2010 (DSB): +* Fix memory leak in astMapPutElemC and astMapPutElemA. +* 26-NOV-2010 (DSB): +* Added support for unsigned byte valued entries. +* 3-DEC-2010 (DSB): +* Added KeyCase attribute. +* 14-JAN-2011 (DSB): +* Fix bug that prevented zero length strings being stored in a +* keymap. +* 17-SEP-2012 (DSB): +* Fix bug that prevented UNDEF entries from being read back in +* from a dump of a KeyMap. +* 18-MAR-2013 (DSB): +* Added astMapDefined. +* 18-JUL-2013 (DSB): +* Added SortBy options "KeyAgeUp" and "KeyAgeDown". +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS KeyMap + +/* Minimum size for the hash table. */ +#define MIN_TABLE_SIZE 16 + +/* The maximum number of entries per element of the hash table. If this + value is exceeded the hash table will be doubled in size. */ +#define MAX_ENTRIES_PER_TABLE_ENTRY 10 + +/* String used to represent the formatetd version of AST__BAD. */ +#define BAD_STRING "" + +/* Integer values to represent the different values of the SortBy attribute. */ +#define SORTBY_NONE 0 +#define SORTBY_AGEUP 1 +#define SORTBY_AGEDOWN 2 +#define SORTBY_KEYUP 3 +#define SORTBY_KEYDOWN 4 +#define SORTBY_KEYAGEUP 5 +#define SORTBY_KEYAGEDOWN 6 + + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* For AST__BAD */ +#include "channel.h" /* I/O channels */ +#include "keymap.h" /* Interface definition for this class */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Type Definitions */ +/* ================ */ + +/* This structure is a AstMapEntry holding a scalar int */ +typedef struct Entry0I { + struct AstMapEntry entry; /* The parent Entry information */ + int value; /* The integer value */ +} Entry0I; + +/* This structure is a AstMapEntry holding a scalar double */ +typedef struct Entry0D { + struct AstMapEntry entry; /* The parent Entry information */ + double value; /* The floating point value */ +} Entry0D; + +/* This structure is a AstMapEntry holding a scalar short int */ +typedef struct Entry0S { + struct AstMapEntry entry; /* The parent Entry information */ + short int value; /* The short int value */ +} Entry0S; + +/* This structure is a AstMapEntry holding a scalar unsigned byte + (unsigned char) */ +typedef struct Entry0B { + struct AstMapEntry entry; /* The parent Entry information */ + unsigned char value; /* The byte value */ +} Entry0B; + +/* This structure is a AstMapEntry holding a scalar float */ +typedef struct Entry0F { + struct AstMapEntry entry; /* The parent Entry information */ + float value; /* The floating point value */ +} Entry0F; + +/* This structure is a AstMapEntry holding a scalar string */ +typedef struct Entry0C { + struct AstMapEntry entry; /* The parent Entry information */ + const char *value; /* The string pointer */ +} Entry0C; + +/* This structure is a AstMapEntry holding a scalar AST Object */ +typedef struct Entry0A { + struct AstMapEntry entry; /* The parent Entry information */ + AstObject *value; /* The Object pointer */ + struct AstMapEntry *next; /* Pointer to next AST Object entry */ + struct AstMapEntry *prev; /* Pointer to previous AST Object entry */ +} Entry0A; + +/* This structure is a AstMapEntry holding a scalar void pointer */ +typedef struct Entry0P { + struct AstMapEntry entry; /* The parent Entry information */ + void *value; /* The pointer */ +} Entry0P; + +/* This structure is a AstMapEntry holding a 1D array of ints */ +typedef struct Entry1I { + struct AstMapEntry entry; /* The parent Entry information */ + int *value; /* The integer values */ +} Entry1I; + +/* This structure is a AstMapEntry holding a 1D array of doubles */ +typedef struct Entry1D { + struct AstMapEntry entry; /* The parent Entry information */ + double *value; /* The floating point values */ +} Entry1D; + +/* This structure is a AstMapEntry holding a 1D array of short ints */ +typedef struct Entry1S { + struct AstMapEntry entry; /* The parent Entry information */ + short int *value; /* The short int values */ +} Entry1S; + +/* This structure is a AstMapEntry holding a 1D array of unsigned bytes */ +typedef struct Entry1B { + struct AstMapEntry entry; /* The parent Entry information */ + unsigned char *value; /* The byte values */ +} Entry1B; + +/* This structure is a AstMapEntry holding a 1D array of floats */ +typedef struct Entry1F { + struct AstMapEntry entry; /* The parent Entry information */ + float *value; /* The floating point values */ +} Entry1F; + +/* This structure is a AstMapEntry holding a 1D array of strings */ +typedef struct Entry1C { + struct AstMapEntry entry; /* The parent Entry information */ + const char **value; /* The string pointers */ +} Entry1C; + +/* This structure is a AstMapEntry holding a 1D array of AST Objects */ +typedef struct Entry1A { + struct AstMapEntry entry; /* The parent Entry information */ + AstObject **value; /* The Object pointers */ + struct AstMapEntry *next; /* Pointer to next AST Object entry */ + struct AstMapEntry *prev; /* Pointer to previous AST Object entry */ +} Entry1A; + +/* This structure is a AstMapEntry holding a 1D array of void pointers. */ +typedef struct Entry1P { + struct AstMapEntry entry; /* The parent Entry information */ + void **value; /* The pointers */ +} Entry1P; + + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->ConvertValue_Init = 0; \ + globals->ConvertValue_Istr = 0; \ + globals->ConvertValue_Buff[ 0 ] = 0; \ + globals->MapKey_Init = 0; \ + globals->MapKey_Istr = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(KeyMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(KeyMap,Class_Init) +#define class_vtab astGLOBAL(KeyMap,Class_Vtab) +#define getattrib_buff astGLOBAL(KeyMap,GetAttrib_Buff) +#define convertvalue_strings astGLOBAL(KeyMap,ConvertValue_Strings) +#define convertvalue_istr astGLOBAL(KeyMap,ConvertValue_Istr) +#define convertvalue_init astGLOBAL(KeyMap,ConvertValue_Init) +#define convertvalue_buff astGLOBAL(KeyMap,ConvertValue_Buff) +#define mapkey_strings astGLOBAL(KeyMap,MapKey_Strings) +#define mapkey_istr astGLOBAL(KeyMap,MapKey_Istr) +#define mapkey_init astGLOBAL(KeyMap,MapKey_Init) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ \ +static char getattrib_buff[ AST__KEYMAP_GETATTRIB_BUFF_LEN + 1 ]; + +/* Strings returned by ConvertValue */ \ +static char *convertvalue_strings[ AST__KEYMAP_CONVERTVALUE_MAX_STRINGS ]; + +/* Offset of next string in "ConvertValue_Strings" */ \ +static int convertvalue_istr; + +/* "ConvertValue_Strings" array initialised? */ \ +static int convertvalue_init; + +/* ConvertValue string buffer */ \ +static char convertvalue_buff[ AST__KEYMAP_CONVERTVALUE_BUFF_LEN + 1 ]; + +/* Strings returned by MapKey */ \ +static char *mapkey_strings[ AST__KEYMAP_MAPKEY_MAX_STRINGS ]; + +/* Offset of next string in "MapKey_Strings" */ \ +static int mapkey_istr; + +/* "MapKey_Strings" array initialised? */ \ +static int mapkey_init; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstKeyMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstKeyMap *astKeyMapId_( const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapEntry *AddTableEntry( AstKeyMap *, int, AstMapEntry *, int, int * ); +static AstMapEntry *CopyMapEntry( AstMapEntry *, int * ); +static AstMapEntry *FreeMapEntry( AstMapEntry *, int * ); +static AstMapEntry *RemoveTableEntry( AstKeyMap *, int, const char *, int * ); +static AstMapEntry *SearchTableEntry( AstKeyMap *, int, const char *, int * ); +static const char *ConvertKey( AstKeyMap *, const char *, char *, int, const char *, int * ); +static const char *GetKey( AstKeyMap *, int index, int * ); +static const char *MapIterate( AstKeyMap *, int, int * ); +static const char *MapKey( AstKeyMap *, int index, int * ); +static const char *SortByString( int, const char *, int * ); +static int CompareEntries( const void *, const void * ); +static int ConvertValue( void *, int, void *, int, int * ); +static int GetObjSize( AstObject *, int * ); +static int HashFun( const char *, int, unsigned long *, int * ); +static int KeyCmp( const char *, const char * ); +static int MapDefined( AstKeyMap *, const char *, int * ); +static int MapGet0A( AstKeyMap *, const char *, AstObject **, int * ); +static int MapGet0C( AstKeyMap *, const char *, const char **, int * ); +static int MapGet0D( AstKeyMap *, const char *, double *, int * ); +static int MapGet0S( AstKeyMap *, const char *, short int *, int * ); +static int MapGet0B( AstKeyMap *, const char *, unsigned char *, int * ); +static int MapGet0F( AstKeyMap *, const char *, float *, int * ); +static int MapGet0I( AstKeyMap *, const char *, int *, int * ); +static int MapGet0P( AstKeyMap *, const char *, void **, int * ); +static int MapGet1A( AstKeyMap *, const char *, int, int *, AstObject **, int * ); +static int MapGet1C( AstKeyMap *, const char *, int, int, int *, char *, int * ); +static int MapGet1D( AstKeyMap *, const char *, int, int *, double *, int * ); +static int MapGet1B( AstKeyMap *, const char *, int, int *, unsigned char *, int * ); +static int MapGet1S( AstKeyMap *, const char *, int, int *, short int *, int * ); +static int MapGet1F( AstKeyMap *, const char *, int, int *, float *, int * ); +static int MapGet1I( AstKeyMap *, const char *, int, int *, int *, int * ); +static int MapGet1P( AstKeyMap *, const char *, int, int *, void **, int * ); +static int MapGetElemA( AstKeyMap *, const char *, int, AstObject **, int * ); +static int MapGetElemC( AstKeyMap *, const char *, int, int, char *, int * ); +static int MapGetElemD( AstKeyMap *, const char *, int, double *, int * ); +static int MapGetElemB( AstKeyMap *, const char *, int, unsigned char *, int * ); +static int MapGetElemS( AstKeyMap *, const char *, int, short int *, int * ); +static int MapGetElemF( AstKeyMap *, const char *, int, float *, int * ); +static int MapGetElemI( AstKeyMap *, const char *, int, int *, int * ); +static int MapGetElemP( AstKeyMap *, const char *, int, void **, int * ); +static int MapHasKey( AstKeyMap *, const char *, int * ); +static int MapLenC( AstKeyMap *, const char *, int * ); +static int MapLength( AstKeyMap *, const char *, int * ); +static int MapSize( AstKeyMap *, int * ); +static int MapType( AstKeyMap *, const char *, int * ); +static int SortByInt( const char *, const char *, int * ); +static size_t SizeOfEntry( AstMapEntry *, int * ); +static void AddToObjectList( AstKeyMap *, AstMapEntry *, int * ); +static void AddToSortedList( AstKeyMap *, AstMapEntry *, int * ); +static void CheckCircle( AstKeyMap *, AstObject *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void CopyTableEntry( AstKeyMap *, AstKeyMap *, int, int * ); +static void Delete( AstObject *, int * ); +static void DoubleTableSize( AstKeyMap *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void DumpEntry( AstMapEntry *, AstChannel *, int, int * ); +static void FreeTableEntry( AstKeyMap *, int itab, int * ); +static void InitMapEntry( AstMapEntry *, int, int, int * ); +static void MapCopy( AstKeyMap *, AstKeyMap *, int * ); +static void MapPut0A( AstKeyMap *, const char *, AstObject *, const char *, int * ); +static void MapPut0C( AstKeyMap *, const char *, const char *, const char *, int * ); +static void MapPut0D( AstKeyMap *, const char *, double, const char *, int * ); +static void MapPut0B( AstKeyMap *, const char *, unsigned char, const char *, int * ); +static void MapPut0S( AstKeyMap *, const char *, short int, const char *, int * ); +static void MapPut0F( AstKeyMap *, const char *, float, const char *, int * ); +static void MapPut0I( AstKeyMap *, const char *, int, const char *, int * ); +static void MapPut0P( AstKeyMap *, const char *, void *, const char *, int * ); +static void MapPut1A( AstKeyMap *, const char *, int, AstObject *const [], const char *, int * ); +static void MapPut1C( AstKeyMap *, const char *, int, const char *const [], const char *, int * ); +static void MapPut1D( AstKeyMap *, const char *, int, const double *, const char *, int * ); +static void MapPut1B( AstKeyMap *, const char *, int, const unsigned char *, const char *, int * ); +static void MapPut1S( AstKeyMap *, const char *, int, const short int *, const char *, int * ); +static void MapPut1F( AstKeyMap *, const char *, int, const float *, const char *, int * ); +static void MapPut1I( AstKeyMap *, const char *, int, const int *, const char *, int * ); +static void MapPut1P( AstKeyMap *, const char *, int, void *const [], const char *, int * ); +static void MapPutElemA( AstKeyMap *, const char *, int, AstObject *, int * ); +static void MapPutElemC( AstKeyMap *, const char *, int, const char *, int * ); +static void MapPutElemD( AstKeyMap *, const char *, int, double, int * ); +static void MapPutElemB( AstKeyMap *, const char *, int, unsigned char, int * ); +static void MapPutElemS( AstKeyMap *, const char *, int, short int, int * ); +static void MapPutElemF( AstKeyMap *, const char *, int, float, int * ); +static void MapPutElemI( AstKeyMap *, const char *, int, int, int * ); +static void MapPutElemP( AstKeyMap *, const char *, int, void *, int * ); +static void MapPutU( AstKeyMap *, const char *, const char *, int * ); +static void MapRemove( AstKeyMap *, const char *, int * ); +static void MapRename( AstKeyMap *, const char *, const char *, int * ); +static void NewTable( AstKeyMap *, int, int * ); +static void RemoveFromSortedList( AstKeyMap *, AstMapEntry *, int * ); +static void RemoveFromObjectList( AstKeyMap *, AstMapEntry *, int * ); +static void SortEntries( AstKeyMap *, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static int GetSizeGuess( AstKeyMap *, int * ); +static int TestSizeGuess( AstKeyMap *, int * ); +static void ClearSizeGuess( AstKeyMap *, int * ); +static void SetSizeGuess( AstKeyMap *, int, int * ); + +static int GetSortBy( AstKeyMap *, int * ); +static int TestSortBy( AstKeyMap *, int * ); +static void ClearSortBy( AstKeyMap *, int * ); +static void SetSortBy( AstKeyMap *, int, int * ); + +static int GetKeyError( AstKeyMap *, int * ); +static int TestKeyError( AstKeyMap *, int * ); +static void ClearKeyError( AstKeyMap *, int * ); +static void SetKeyError( AstKeyMap *, int, int * ); + +static int GetKeyCase( AstKeyMap *, int * ); +static int TestKeyCase( AstKeyMap *, int * ); +static void ClearKeyCase( AstKeyMap *, int * ); +static void SetKeyCase( AstKeyMap *, int, int * ); + +static int GetMapLocked( AstKeyMap *, int * ); +static int TestMapLocked( AstKeyMap *, int * ); +static void ClearMapLocked( AstKeyMap *, int * ); +static void SetMapLocked( AstKeyMap *, int, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static AstMapEntry *AddTableEntry( AstKeyMap *this, int itab, + AstMapEntry *entry, int keymember, + int *status ){ +/* +* Name: +* AddTableEntry + +* Purpose: +* Add an new entry to a linked-list of KeyMap entries. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* AstMapEntry *AddTableEntry( AstKeyMap *this, int itab, +* AstMapEntry *entry, int keymember, +* int *status ){ + +* Class Membership: +* KeyMap member function. + +* Description: +* This function adds the supplied MapEntry to the head of the linked +* list of MapEntries stored at the specified entry of the hash table. +* If this results in the linked list having too many entries, then a +* new larger hash table is allocated and the entries in the existing +* table are moved into the new table. + +* Parameters: +* this +* Pointer to the KeyMap. +* itab +* Index of the hash table element to be searched. +* entry +* Pointer to the MapEntry to be added. +* keymember +* A unique integer identifier for the key that increases +* monotonically with age of the key. If this is negative, +* the next available identifier will be used automatically. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A NULL pointer. + +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Put a pointer to the MapEntry which is currently at the head of the + linked list in the "next" component of the supplied MapEntry. */ + entry->next = this->table[ itab ]; + +/* Store the supplied MapEntry pointer in the requested element of the + hash table. */ + this->table[ itab ] = entry; + +/* Increment the length of linked list. */ + this->nentry[ itab ]++; + +/* Each new entry added to the KeyMap has a unique member index that is + never re-used. */ + entry->member = (this->member_count)++; + +/* Each key added to the KeyMap also has a separate unique member index, + but this index is re-used each time the same key is added into the + KeyMap. So changing the value associated with a key does not cause the + keymember value to change. */ + if( keymember >= 0 ) { + entry->keymember = keymember; + } else { + entry->keymember = (this->member_count)++; + } + +/* Insert the supplied MapEntry into a list sorted by key. */ + AddToSortedList( this, entry, status ); + +/* If the entry is of type AST__OBJECTTYPE, add it to the head of the + list of AST__OBJECTTYPE entries in the KeyMap. */ + AddToObjectList( this, entry, status ); + +/* If the population of this table entry is now too large, double the size + of the table, moving the table entries to appropriate places in the + new larger table. */ + if( this->nentry[ itab ] > MAX_ENTRIES_PER_TABLE_ENTRY ) { + DoubleTableSize( this, status ); + } + +/* Return a NULL pointer. */ + return NULL; +} + +static void AddToObjectList( AstKeyMap *this, AstMapEntry *entry, int *status ){ +/* +* Name: +* AddToObjectList + +* Purpose: +* Add AST__OBJECTTYPE entries into a linked-list of such entries. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void AddToObjectList( AstKeyMap *this, AstMapEntry *entry, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* If the supplied MapEntry holds one or more pointers to AST Objects, +* then the entry is added to a linked list of such entries. + +* Parameters: +* this +* Pointer to the KeyMap. +* entry +* Pointer to the MapEntry to be added. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + Entry0A *scalar; /* Pointer to a scalar AST__OBJECTTYPE entry */ + Entry1A *vector; /* Pointer to a vector AST__OBJECTTYPE entry */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do nothing if the entry does not hold AST Object pointers. */ + if( entry->type == AST__OBJECTTYPE ) { + +/* If the list is not currently empty, add the new entry into the list. */ + if( this->firstA ) { + +/* Store a pointer to the new entry as the previous link in the current + first entry in the list. */ + if( this->firstA->nel == 0 ) { + scalar = (Entry0A *) this->firstA; + scalar->prev = entry; + } else { + vector = (Entry1A *) this->firstA; + vector->prev = entry; + } + +/* Store a pointer to the current first entry as the next link in the new + entry, and nullify the previus link. */ + if( entry->nel == 0 ) { + scalar = (Entry0A *) entry; + scalar->next = this->firstA; + scalar->prev = NULL; + } else { + vector = (Entry1A *) entry; + vector->next = this->firstA; + vector->prev = NULL; + } + +/* If the list is currently empty, nullify both links in the entry. */ + } else { + if( entry->nel == 0 ) { + scalar = (Entry0A *) entry; + scalar->next = NULL; + scalar->prev = NULL; + } else { + vector = (Entry1A *) entry; + vector->next = NULL; + vector->prev = NULL; + } + + } + +/* Store the new entry as the first entry. */ + this->firstA = entry; + } +} + +static void AddToSortedList( AstKeyMap *this, AstMapEntry *entry, int *status ){ +/* +* Name: +* AddToSortedList + +* Purpose: +* Add an entry into the linked-list of sorted KeyMap entries. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void AddToSortedList( AstKeyMap *this, AstMapEntry *entry, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function adds the supplied MapEntry into the linked list of +* sorted MapEntries at a position that maintains the sorted order. + +* Parameters: +* this +* Pointer to the KeyMap. +* entry +* Pointer to the MapEntry to be added. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapEntry *hi; /* MapEntry at high end of current range */ + AstMapEntry *lo; /* MapEntry at low end of current range */ + AstMapEntry *mid; /* MapEntry at middle of current range */ + int cmp; /* Result of comparing two entries */ + int istep; /* Step counter */ + int nstep; /* Number of entries in current range */ + int sortby; /* How to sort the keys */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the SortBy value. */ + sortby = astGetSortBy( this ); + +/* Do nothing if no sorting is required. */ + if( sortby != SORTBY_NONE ) { + +/* Get pointers to the entries at the start and end of the sorted list. */ + lo = this->first; + hi = lo ? lo->sprev : NULL; + +/* Store sortby value in the mapentry structures. */ + if( lo ) lo->sortby = sortby; + if( hi ) hi->sortby = sortby; + entry->sortby = sortby; + +/* If the sorted list is empty, just store the supplied entry at the + head, and set the links to point back to itself. */ + if( !lo ) { + this->first = entry; + entry->sprev = entry; + entry->snext = entry; + +/* If the new entry comes before the first entry or is equal to the first + entry, record it as the new first entry, and insert it into the linked + list before the original first entry. */ + } else if( CompareEntries( &entry, &lo ) <= 0 ) { + this->first = entry; + entry->snext = lo; + entry->sprev = hi; + lo->sprev = entry; + hi->snext = entry; + +/* If the new entry comes after the last entry or is equal to the last + entry, insert it into the linked list after the last entry. */ + } else if( CompareEntries( &entry, &hi ) >= 0 ) { + entry->snext = lo; + entry->sprev = hi; + lo->sprev = entry; + hi->snext = entry; + +/* If the list only contains two values, insert the new entry into the linked + list between the existing two entries. */ + } else if( lo->snext == hi ) { + entry->snext = hi; + entry->sprev = lo; + lo->snext = entry; + hi->sprev = entry; + +/* Otherwise we do a binary chop within the existing sorted list to find the + correct position for the new entry. */ + } else { + +/* Get a pointer to the entry mid way between the hi and lo entries. The + mid entry will be on the upper side of half way if there are an even + number of entries. */ + nstep = this->nsorted/2; + mid = lo; + for( istep = 0; istep < nstep; istep++ ) mid = mid->snext; + +/* Loop until we have a pointer to the first entry which is equal to or + higher than the new entry. */ + while( lo->snext != hi ) { + +/* The next step will be half the length of the previous step. Do not + allow the step size to fall to zero. */ + nstep = ( nstep > 1 ) ? nstep/2 : 1; + +/* Compare the new entry with the current mid-way entry. */ + mid->sortby = sortby; + cmp = CompareEntries( &entry, &mid ); + +/* If the new entry comes before the mid entry, use the mid entry as the + next hi entry, and go down the list by the new step size to find the + new mid-way entry. */ + if( cmp < 0 ) { + hi = mid; + for( istep = 0; istep < nstep; istep++ ) mid = mid->sprev; + +/* If the new entry comes after the mid entry, use the mid entry as the + next lo entry, and go up the list by the new step size to find the + new mid-way entry. */ + } else if( cmp > 0 ) { + lo = mid; + for( istep = 0; istep < nstep; istep++ ) mid = mid->snext; + +/* If the new entry is equal to the mid entry, use the mid entry as hi + and set lo to the previous entry. This causes the loop to quit. */ + } else { + hi = mid; + lo = mid->sprev; + } + } + +/* Insert the new entry into the list between lo and hi. */ + entry->sprev = lo; + entry->snext = hi; + lo->snext = entry; + hi->sprev = entry; + } + +/* Increment the number of entries in the sorted list. */ + (this->nsorted)++; + } +} + +static void CheckCircle( AstKeyMap *this, AstObject *obj, const char *method, int *status ) { +/* +* Name: +* CheckCircle + +* Purpose: +* Check for circular dependencies between KeyMaps. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void CheckCircle( AstKeyMap *this, AstObject *obj, const char *method, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function checks that the given AstObject is not a KeyMap which +* contains "this". If it is, an error is reported. + +* Parameters: +* this +* The KeyMap pointer. +* obj +* Pointer to the AstObject to be inserted into the KeyMap, or NULL. +* method +* Name of method to include in error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstKeyMap *keymap; /* The KeyMap being added to "this" */ + AstObject **vec; /* Pointer to list of AstObject pointers */ + const char *key; /* The i'th key within second KeyMap */ + int i; /* Index of entry within second KeyMap */ + int j; /* Index within the vector of values */ + int len; /* No. of AST pointers stored in the entry */ + int nkey; /* No. of entries in the second KeyMap */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Return if the AstObject is not a KeyMap. */ + if( obj && astIsAKeyMap( obj ) ) { + keymap = (AstKeyMap *) obj; + +/* First check if the supplied Objects are the same. You cannot store a + KeyMap as an entry within itself. */ + if( keymap == this ) { + astError( AST__KYCIR, "%s(%s): Cannot add a %s into another " + "%s because they are same %s.", status, method, + astGetClass( this ), astGetClass( this ), + astGetClass( this ), astGetClass( this ) ); + +/* Otherwise, loop through all the entries in the KeyMap looking for AstObject + entries. */ + } else { + nkey = astMapSize( keymap ); + for( i = 0; i < nkey && astOK; i++ ) { + key = astMapKey( keymap, i ); + if( astMapType( keymap, key ) == AST__OBJECTTYPE ) { + +/* Find the number of AstObject pointers stored in this entry, and + allocate memory to store a copy of the every pointer. */ + len = astMapLength( keymap, key ); + vec = astMalloc( sizeof( AstObject *) * len ); + if( vec ) { + +/* Extract pointers to the AstObjects at this entry, and loop round them. */ + astMapGet1A( keymap, key, len, &len, vec ); + for( j = 0; j < len; j++ ) { + +/* If this entry is a KeyMap, we need to check if is the same as "this" + or contains "this". */ + if( astIsAKeyMap( vec[ j ] ) ) { + +/* If it is the same as "this", report an error. */ + if( vec[ j ] == (AstObject *) this ) { + astError( AST__KYCIR, "%s(%s): Cannot add a KeyMap " + "into another KeyMap because the first " + "KeyMap contains the second KeyMap.", status, + method, astGetClass( this ) ); + break; + +/* Otherwise, see if it contains "this". */ + } else { + CheckCircle( this, vec[ j ], method, status ); + } + } + +/* Free resources. */ + vec[ j ] = astAnnul( vec[ j ] ); + } + vec = astFree( vec ); + } + } + } + } + } +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* KeyMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* KeyMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the KeyMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstKeyMap *this; /* Pointer to the KeyMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the KeyMap structure. */ + this = (AstKeyMap *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* SizeGuess. */ +/* ---------- */ + if ( !strcmp( attrib, "sizeguess" ) ) { + astClearSizeGuess( this ); + +/* KeyError. */ +/* --------- */ + } else if ( !strcmp( attrib, "keyerror" ) ) { + astClearKeyError( this ); + +/* KeyCase. */ +/* --------- */ + } else if ( !strcmp( attrib, "keycase" ) ) { + astClearKeyCase( this ); + +/* MapLocked. */ +/* --------- */ + } else if ( !strcmp( attrib, "maplocked" ) ) { + astClearMapLocked( this ); + +/* SortBy. */ +/* ------- */ + } else if ( !strcmp( attrib, "sortby" ) ) { + astClearSortBy( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearKeyCase( AstKeyMap *this, int *status ) { +/* +*+ +* Name: +* astClearKeyCase + +* Purpose: +* Clear the value of the KeyCase attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astSetKeyCase( AstKeyMap *this ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function Clears the KeyCase attribute of a KeyMap. It reports +* an error if the KeyMap contains any entries. + +* Parameters: +* this +* Pointer to the KeyMap. + +*- +*/ + +/* Local Variables: */ + int defval; /* Default KeyCase value */ + int itab; /* Index into hash table */ + int oldval; /* Old KeyCase value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Save the old value. */ + oldval = astGetKeyCase( this ); + +/* Clear it. */ + this->keycase = -1; + +/* Get the default value. */ + defval = astGetKeyCase( this ); + +/* If the old value and the default value are not the same, we must check + that the KeyMap is empty. If not, restore the old value and report an + error. */ + if( defval != oldval ) { + for( itab = 0; itab < this->mapsize; itab++ ) { + if( this->nentry[ itab ] > 0 ) { + this->keycase = oldval; + astError( AST__NOWRT, "astClearAttrib(KeyMap): Illegal attempt to " + "clear the KeyCase attribute of a non-empty KeyMap.", + status); + break; + } + } + } +} + +static void ClearKeyError( AstKeyMap *this, int *status ) { +/* +*+ +* Name: +* astClearKeyError + +* Purpose: +* Clear the value of the KeyError attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astClearKeyError( AstKeyMap *this ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function clears the value of the KeyError attribute for a +* KeyMap. It clears the attribute recursively in any KeyMaps +* contained within the supplied KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. + +*- +*/ + +/* Local Variables: */ + AstMapEntry *next; /* Pointer to next Entry to copy */ + AstObject **obj_list; /* List of pointers to AST Object entries */ + int i; /* Index into hash table */ + int iel; /* Index of current vector element */ + int nel; /* Number of elements in vector */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Clear the KeyError value in the supplied KeyMap. */ + this->keyerror = -INT_MAX; + +/* Loop round each entry in the hash table. */ + for( i = 0; i < this->mapsize; i++ ) { + +/* Get a pointer to the next KeyMap entry. */ + next = this->table[ i ]; + +/* Loop round all entries in this element of the hash table. */ + while( next && astOK ) { + +/* If this entry has an Object data type, see if holds any KeyMaps. */ + if( next->type == AST__OBJECTTYPE ) { + +/* Get the number of objects to check, and a pointer to the first. */ + nel = next->nel; + if( nel == 0 ) { + obj_list = &( ((Entry0A *)next)->value ); + nel = 1; + } else { + obj_list = ((Entry1A *)next)->value; + } + +/* Loop round checking all Objects. */ + for( iel = 0; iel < nel; iel++ ) { + +/* If this Object is a KeyMap, clear its KeyError attribute. */ + if( astIsAKeyMap( obj_list[ iel ] ) ) { + astClearKeyError( (AstKeyMap *) obj_list[ iel ] ); + } + } + } + +/* Get a pointer to the next entry. */ + next = next->next; + } + } +} + +static void ClearMapLocked( AstKeyMap *this, int *status ) { +/* +*+ +* Name: +* astClearMapLocked + +* Purpose: +* Clear the value of the MapLocked attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astClearMapLocked( AstKeyMap *this ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function clears the value of the MapLocked attribute for a +* KeyMap. It clears the attribute recursively in any KeyMaps +* contained within the supplied KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. + +*- +*/ + +/* Local Variables: */ + AstMapEntry *next; /* Pointer to next Entry to copy */ + AstObject **obj_list; /* List of pointers to AST Object entries */ + int i; /* Index into hash table */ + int iel; /* Index of current vector element */ + int nel; /* Number of elements in vector */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Clear the MapLocked value in the supplied KeyMap. */ + this->maplocked = -INT_MAX; + +/* Loop round each entry in the hash table. */ + for( i = 0; i < this->mapsize; i++ ) { + +/* Get a pointer to the next KeyMap entry. */ + next = this->table[ i ]; + +/* Loop round all entries in this element of the hash table. */ + while( next && astOK ) { + +/* If this entry has an Object data type, see if holds any KeyMaps. */ + if( next->type == AST__OBJECTTYPE ) { + +/* Get the number of objects to check, and a pointer to the first. */ + nel = next->nel; + if( nel == 0 ) { + obj_list = &( ((Entry0A *)next)->value ); + nel = 1; + } else { + obj_list = ((Entry1A *)next)->value; + } + +/* Loop round checking all Objects. */ + for( iel = 0; iel < nel; iel++ ) { + +/* If this Object is a KeyMap, clear its MapLocked attribute. */ + if( astIsAKeyMap( obj_list[ iel ] ) ) { + astClearMapLocked( (AstKeyMap *) obj_list[ iel ] ); + } + } + } + +/* Get a pointer to the next entry. */ + next = next->next; + } + } +} + +static void ClearSizeGuess( AstKeyMap *this, int *status ) { +/* +*+ +* Name: +* astClearSizeGuess + +* Purpose: +* Clear the value of the SizeGuess attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astClearSizeGuess( AstKeyMap *this ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function clears the value of the SizeGuess attribute for a +* KeyMap. It reports an error if the KeyMap contains any entries. + +* Parameters: +* this +* Pointer to the KeyMap. + +*- +*/ + +/* Local Variables: */ + int empty; /* Is the KeyMap empty? */ + int itab; /* Index into hash table */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* See if the KeyMap is empty. */ + empty = 1; + for( itab = 0; itab < this->mapsize; itab++ ) { + if( this->nentry[ itab ] > 0 ) { + empty = 0; + break; + } + } + +/* If not report an error. */ + if( !empty ) { + astError( AST__NOWRT, "astClearAttrib(KeyMap): Illegal attempt to " + "clear the SizeGuess attribute of a non-empty KeyMap." , status); + +/* Otherwise, store the "cleared" value and change the size of the hash + table. */ + } else { + this->sizeguess = INT_MAX; + NewTable( this, MIN_TABLE_SIZE, status ); + } +} + +static void ClearSortBy( AstKeyMap *this, int *status ) { +/* +*+ +* Name: +* astClearSortBy + +* Purpose: +* Clear the value of the SortBy attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astClearSortBy( AstKeyMap *this ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function clears the value of the SortBy attribute for a +* KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. + +*- +*/ + +/* Local Variables: */ + int oldval; /* The old sortby value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the original SortBy value. */ + oldval = astGetSortBy( this ); + +/* Clear the SortBy value in the supplied KeyMap. */ + this->sortby = -INT_MAX; + +/* If the value has changed, re-sort the keys. */ + if( oldval != astGetSortBy( this ) ) SortEntries( this, status ); +} + +static int CompareEntries( const void *first_void, const void *second_void ) { +/* +* Name: +* CompareEntries + +* Purpose: +* Determine the sorting order of two mapEntries. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* int CompareEntries( const void *first, const void *second ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function returns a value indicating if the first MapEntry +* is less than, equal to, or greater than the second MapEntry using +* the indicated sorting method. It is designed for use with the +* "qsort" function, and therefore must used "void *" pointers. + +* Parameters: +* first +* Pointer to the address of the first MapEntry. +* second +* Pointer to the address of the second MapEntry. + +* Returned Value: +* -1 if "first" is less than "second". This implies that "first" +* should come before "second" in the sorted list. +* +* 0 if "first" is equal to "second". +* +* +1 if "first" is greater than "second". This implies that "first" +* should come after "second" in the sorted list. + +*/ + +/* Local Variables: */ + AstMapEntry *first; /* Pointer to first MapEntry structure */ + AstMapEntry *second; /* Pointer to second MapEntry structure */ + int result; /* Returned value */ + int sortby; /* Sorting method */ + +/* Initialise returned value */ + result = 0; + +/* Get pointers to the MapEntry structures, and get the sorting method. */ + first = *( (AstMapEntry **) first_void ); + second = *( (AstMapEntry **) second_void ); + sortby = first->sortby; + +/* First handle sorting by increasing age of the value */ + if( sortby == SORTBY_AGEUP ) { + if( first->member < second->member ) { + result = 1; + } else if( first->member > second->member ) { + result = -1; + } else { + result = 0; + } + +/* Next handle sorting by decreasing age of the value */ + } else if( sortby == SORTBY_AGEDOWN ) { + if( first->member < second->member ) { + result = -1; + } else if( first->member > second->member ) { + result = 1; + } else { + result = 0; + } + +/* Next handle sorting by increasing age of the key */ + } else if( sortby == SORTBY_KEYAGEUP ) { + if( first->keymember < second->keymember ) { + result = 1; + } else if( first->keymember > second->keymember ) { + result = -1; + } else { + result = 0; + } + +/* Next handle sorting by decreasing age of the key */ + } else if( sortby == SORTBY_KEYAGEDOWN ) { + if( first->keymember < second->keymember ) { + result = -1; + } else if( first->keymember > second->keymember ) { + result = 1; + } else { + result = 0; + } + +/* Next handle sorting by increasing alphabetical position. */ + } else if( sortby == SORTBY_KEYUP ) { + result = KeyCmp( first->key, second->key ); + +/* Next handle sorting by decreasing alphabetical position. */ + } else if( sortby == SORTBY_KEYDOWN ) { + result = KeyCmp( second->key, first->key ); + + } + +/* Return the result. */ + return result; + +} + +static const char *ConvertKey( AstKeyMap *this, const char *skey, char *keybuf, + int blen, const char *method, int *status ){ +/* +* Name: +* ConvertValue + +* Purpose: +* Convert the supplied key to upper case if required. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* const char *ConvertKey( AstKeyMap *this, const char *skey, char *keybuf, +* int blen, const char *method, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function converts the supplied key string to uppercase if the +* KeyCase attribute it currently set to zero in the supplied KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. +* skey +* Pointer to the supplied key string. +* keybuf +* Pointer to a buffer in which to place the converted string. This +* will only be used if the supplied key string needs to be +* converted. +* blen +* The length of the "keybuf" buffer. This should include room for +* a terminating null character. +* method +* Pointer to a string holding the name of the method to include in +* any error message. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the KeyMap's KeyCase attribute is currently set to a non-zero +* value, the returned value will be a copy of "skey". Otherwise it +* will be copy of "keybuf" (the buffer holding the upper case version +* of the supplied string). + +* Notes: +* - The valeu of "skey" will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + const char *result; + int len; + +/* Initialise. */ + result = skey; + +/* Check the global error status and the supplied pointers. */ + if( !astOK ) return result; + +/* If the KeyCase attribute is non-zero, return "skey". Otherwise, convert + the "skey" string to upper case and return "keybuf". Report an error if + the key is too long. */ + if( !astGetKeyCase( this ) && astOK ) { + len = astChrLen( skey ); + if( len >= blen ) { + astError( AST__BIGKEY, "%s(%s): Supplied key '%s' is too long " + "(keys must be no more than %d characters long).", + status, method, astGetClass( this ), skey, blen - 1 ); + } else { + astChrCase( skey, keybuf, 1, blen ); + result = keybuf; + } + } + +/* Return the result. */ + return result; +} + +static int ConvertValue( void *raw, int raw_type, void *out, int out_type, int *status ) { +/* +* Name: +* ConvertValue + +* Purpose: +* Convert a value from one KeyMap data type to another. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* int ConvertValue( void *raw, int raw_type, void *out, int out_type, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function converts a supplied value from one KeyMap data type to +* another, if possible. + +* Parameters: +* raw +* Pointer to input value. +* raw_type +* The data type of the input value. +* out +* Pointer to the location at which to store the output value. This +* may be NULL, in which case the conversion is still performed if +* possible, but the result of the conversion is thrown away. +* out_type +* The data type of the output value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the conversion was performed succesfully, otherwise zero. +* In the case of the output type being AST__STRINGTYPE, the returned +* non-zero value will be the length of the formatted string (including +* the terminating null character). This value will be returned correctly +* even if "out" is NULL. + +* Notes: +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstObject *aval; /* AstObject pointer value */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *cval; /* Pointer to string value */ + const char *cvalue; /* Pointer to output string value */ + double dval; /* Double precision value */ + float fval; /* Single precision value */ + int i; /* Loop count */ + int ival; /* Integer value */ + int n1; /* Number of characters at reduced precision */ + int n2; /* Number of characters at full precision */ + int nc; /* Number of characters read from string */ + int nval; /* Number of values read from string */ + int result; /* Returned flag */ + short int sval; /* Short int value */ + unsigned char bval; /* Byte value */ + +/* Initialise. */ + result = 0; + +/* Check the global error status and the supplied pointers. */ + if( !astOK || !raw ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* If the "convertvalue_strings" array has not been initialised, fill it with + NULL pointers. */ + if( !convertvalue_init ) { + convertvalue_init = 1; + for( i = 0; i < AST__KEYMAP_CONVERTVALUE_MAX_STRINGS; i++ ) convertvalue_strings[ i ] = NULL; + } + +/* Assume conversion is possible */ + result = 1; + cvalue = NULL; + +/* Do nothing if both data types are AST__UNDEFTYPE. */ + if( raw_type == AST__UNDEFTYPE && out_type == AST__UNDEFTYPE ) { + +/* Indicate failure if one of the two types is AST__UNDEFTYPE and the + other is not. */ + } else if( raw_type == AST__UNDEFTYPE || out_type == AST__UNDEFTYPE ) { + result = 0; + +/* Otherwise, consider conversion from "int". */ + } else if( raw_type == AST__INTTYPE ) { + ival = *( (int *) raw ); + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + if( out ) *( (int *) out ) = ival; + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + if( out ) *( (short int *) out ) = (short int) ival; + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + if( out ) *( (unsigned char *) out ) = (unsigned char) ival; + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + if( out ) *( (float *) out ) = (float) ival; + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + if( out ) *( (double *) out ) = (double) ival; + +/* Consider conversion to "const char *". */ + } else if( out_type == AST__STRINGTYPE ) { + (void) sprintf( convertvalue_buff, "%d", ival ); + cvalue = convertvalue_buff; + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + result = 0; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + result = 0; + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Otherwise, consider conversion from "short int". */ + } else if( raw_type == AST__SINTTYPE ) { + sval = *( (short int *) raw ); + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + if( out ) *( (int *) out ) = sval; + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + if( out ) *( (short int *) out ) = sval; + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + if( out ) *( (unsigned char *) out ) = sval; + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + if( out ) *( (float *) out ) = (float) sval; + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + if( out ) *( (double *) out ) = (double) sval; + +/* Consider conversion to "const char *". */ + } else if( out_type == AST__STRINGTYPE ) { + (void) sprintf( convertvalue_buff, "%d", (int) sval ); + cvalue = convertvalue_buff; + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + result = 0; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + result = 0; + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Otherwise, consider conversion from "byte". */ + } else if( raw_type == AST__BYTETYPE ) { + bval = *( (unsigned char *) raw ); + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + if( out ) *( (int *) out ) = bval; + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + if( out ) *( (short int *) out ) = bval; + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + if( out ) *( (unsigned char *) out ) = bval; + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + if( out ) *( (float *) out ) = (float) bval; + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + if( out ) *( (double *) out ) = (double) bval; + +/* Consider conversion to "const char *". */ + } else if( out_type == AST__STRINGTYPE ) { + (void) sprintf( convertvalue_buff, "%d", (int) bval ); + cvalue = convertvalue_buff; + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + result = 0; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + result = 0; + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Consider conversion from "double". */ + } else if( raw_type == AST__DOUBLETYPE ) { + dval = *( (double *) raw ); + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + if( out ) *( (int *) out ) = (int)( dval + 0.5 ); + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + if( out ) *( (short int *) out ) = (int)( dval + 0.5 ); + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + if( out ) *( (unsigned char *) out ) = (int)( dval + 0.5 ); + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + if( out ) *( (double *) out ) = dval; + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + if( out ) *( (float *) out ) = (float) dval; + +/* Consider conversion to "const char *". If reducing the number of + decimal places by two produces a saving of 10 or more characters, + assume the least significant two characters are rounding error. */ + } else if( out_type == AST__STRINGTYPE ) { + if( dval != AST__BAD ) { + n1 = sprintf( convertvalue_buff, "%.*g", DBL_DIG - 2, dval ); + n2 = sprintf( convertvalue_buff, "%.*g", DBL_DIG, dval ); + if( n2 - n1 > 9 ) { + (void) sprintf( convertvalue_buff, "%.*g", DBL_DIG - 2, dval ); + } + cvalue = convertvalue_buff; + } else { + cvalue = BAD_STRING; + } + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + result = 0; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + result = 0; + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Consider conversion from "float". */ + } else if( raw_type == AST__FLOATTYPE ) { + fval = *( (float *) raw ); + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + if( out ) *( (int *) out ) = (int)( fval + 0.5 ); + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + if( out ) *( (short int *) out ) = (int)( fval + 0.5 ); + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + if( out ) *( (unsigned char *) out ) = (int)( fval + 0.5 ); + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + if( out ) *( (double *) out ) = (double) fval; + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + if( out ) *( (float *) out ) = fval; + +/* Consider conversion to "const char *". */ + } else if( out_type == AST__STRINGTYPE ) { + (void) sprintf( convertvalue_buff, "%.*g", FLT_DIG, fval ); + cvalue = convertvalue_buff; + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + result = 0; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + result = 0; + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Consider conversion from "const char *". */ + } else if( raw_type == AST__STRINGTYPE ) { + cval = *( (const char **) raw ); + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + nc = 0; + nval = astSscanf( cval, " %d %n", &ival, &nc ); + if( ( nval == 1 ) && ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (int *) out ) = ival; + } else { + nc = 0; + nval = astSscanf( cval, " %lf %n", &dval, &nc ); + if( ( nval == 1 ) && ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (int *) out ) = (int) ( dval + 0.5 ); + } else { + result = 0; + } + } + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + nc = 0; + nval = astSscanf( cval, " %d %n", &ival, &nc ); + if( ( nval == 1 ) && ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (short int *) out ) = ival; + } else { + nc = 0; + nval = astSscanf( cval, " %lf %n", &dval, &nc ); + if( ( nval == 1 ) && ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (short int *) out ) = (int) ( dval + 0.5 ); + } else { + result = 0; + } + } + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + nc = 0; + nval = astSscanf( cval, " %d %n", &ival, &nc ); + if( ( nval == 1 ) && ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (unsigned char *) out ) = ival; + } else { + nc = 0; + nval = astSscanf( cval, " %lf %n", &dval, &nc ); + if( ( nval == 1 ) && ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (unsigned char *) out ) = (int) ( dval + 0.5 ); + } else { + result = 0; + } + } + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + nc = 0; + nval = astSscanf( cval, " " BAD_STRING " %n", &nc ); + if( ( astSscanf( cval, " " BAD_STRING " %n", &nc ) == 0 ) && + ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (double *) out ) = AST__BAD; + + } else if( ( astSscanf( cval, " %lf %n", &dval, &nc ) == 1 ) && + ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (double *) out ) = dval; + + } else { + result = 0; + } + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + nc = 0; + nval = astSscanf( cval, " %f %n", &fval, &nc ); + if( ( nval == 1 ) && ( nc >= (int) strlen( cval ) ) ) { + if( out ) *( (float *) out ) = fval; + } else { + result = 0; + } + +/* Consider conversion to "const char *". */ + } else if( out_type == AST__STRINGTYPE ) { + cvalue = cval; + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + result = 0; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + result = 0; + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Consider conversion from "AstObject *". */ + } else if( raw_type == AST__OBJECTTYPE ) { + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + result = 0; + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + result = 0; + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + result = 0; + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + result = 0; + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + result = 0; + +/* Consider conversion to "const char *". */ + } else if( out_type == AST__STRINGTYPE ) { + result = 0; + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + aval = *( (AstObject **) raw ); + if( out ) *( (AstObject **) out ) = aval ? astClone( aval ) : NULL; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + result = 0; + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Consider conversion from "void *". */ + } else if( raw_type == AST__POINTERTYPE ) { + +/* Consider conversion to "int". */ + if( out_type == AST__INTTYPE ) { + result = 0; + +/* Consider conversion to "short int". */ + } else if( out_type == AST__SINTTYPE ) { + result = 0; + +/* Consider conversion to "byte". */ + } else if( out_type == AST__BYTETYPE ) { + result = 0; + +/* Consider conversion to "double". */ + } else if( out_type == AST__DOUBLETYPE ) { + result = 0; + +/* Consider conversion to "float". */ + } else if( out_type == AST__FLOATTYPE ) { + result = 0; + +/* Consider conversion to "const char *". */ + } else if( out_type == AST__STRINGTYPE ) { + result = 0; + +/* Consider conversion to "AstObject *". */ + } else if( out_type == AST__OBJECTTYPE ) { + result = 0; + +/* Consider conversion to "void *". */ + } else if( out_type == AST__POINTERTYPE ) { + if( out ) *( (void **) out ) = *( (void **) raw ); + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + out_type ); + } + +/* Report an error if the data type is unknown. */ + } else { + result = 0; + astError( AST__INTER, "ConvertValue(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + raw_type ); + } + +/* If the output is a string, store a copy of the resulting string in + dynamically allocated memory, putting a pointer to the copy into the next + element of the "convertvalue_strings" array. (This process also de-allocates + any previously allocated memory pointed at by this "convertvalue_strings" + element, so the earlier string is effectively replaced by the new + one.) */ + if( out_type == AST__STRINGTYPE && astOK && result && cvalue ) { + result = strlen( cvalue ) + 1; + + astBeginPM; + convertvalue_strings[ convertvalue_istr ] = astStore( convertvalue_strings[ convertvalue_istr ], cvalue, + (size_t) result ); + astEndPM; + +/* If OK, return a pointer to the copy and increment "convertvalue_istr" to use the + next element of "convertvalue_strings" on the next invocation. Recycle "convertvalue_istr" to + zero when all elements have been used. */ + if ( astOK ) { + if( out ) *( (const char **) out ) = convertvalue_strings[ convertvalue_istr++ ]; + if( convertvalue_istr == ( AST__KEYMAP_CONVERTVALUE_MAX_STRINGS - 1 ) ) convertvalue_istr = 0; + } + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + + +static AstMapEntry *CopyMapEntry( AstMapEntry *in, int *status ){ +/* +* Name: +* CopyMapEntry + +* Purpose: +* Produces a copy of the supplied KeyMap entry. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* AstMapEntry *CopyMapEntry( AstMapEntry *in, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function creates a deep copy of the supplied KeyMap entry. + +* Parameters: +* in +* Pointer to the MapEntry to be copied. NULL may be supplied in +* which case NULL will be returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new copy. The link to the next MapEntry in the +* linked list is set NULL in the returned copy. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapEntry *result; /* Returned pointer */ + AstObject **alist; /* Pointer to list of AST object pointers */ + AstObject *obj; /* Pointer to AstObject value */ + const char **slist; /* Pointer to list of text pointers */ + const char *text; /* Pointer to text string */ + int i; /* Loop count */ + int nel; /* No. of values in entry vector (0 => scalar) */ + int type; /* Entry data type */ + size_t size; /* Size of Entry structure */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status and the supplied pointer. */ + if ( !astOK || !in ) return result; + +/* Get the size, data type and length of the MapEntry. */ + size = SizeOfEntry( in, status ); + nel = in->nel; + type = in->type; + +/* Do a byte-for-byte copy of the supplied MapEntry. */ + result = astStore( NULL, in, size ); + +/* Copy or nullify pointers in the AstMapEntry structure. */ + result->next = NULL; + result->snext = NULL; + result->sprev = NULL; + text = in->key; + result->key = text ? astStore( NULL, text, strlen( text ) + 1 ) : NULL; + text = in->comment; + result->comment = text ? astStore( NULL, text, strlen( text ) + 1 ) : NULL; + +/* Nothing further to do for undefined values. */ + if( type == AST__UNDEFTYPE ) { + +/* Next deal with string entries. */ + } else if( type == AST__STRINGTYPE ) { + +/* Scalar valued entries... */ + if( nel == 0 ) { + +/* Take a copy of the single string in the input entry. */ + text = ( (Entry0C *) in )->value; + ( (Entry0C *) result )->value = text ? astStore( NULL, text, + strlen( text ) + 1 ) : NULL; +/* Vector valued entries... */ + } else { + +/* Allocate an array to store the string pointers. */ + slist = astMalloc( sizeof(char *)*(size_t)nel ); + ( (Entry1C *) result )->value = slist; + +/* Copy the strings. */ + if( slist ) { + for( i = 0; i < nel; i++ ) { + text = ( (Entry1C *) in )->value[ i ]; + slist[ i ] = text ? astStore( NULL, text, strlen( text ) + 1 ) : NULL; + } + } + } + +/* Similarly deal with AST Object entries. */ + } else if( type == AST__OBJECTTYPE ) { + if( nel == 0 ) { + obj = ( (Entry0A *) in )->value; + ( (Entry0A *) result )->value = obj ? astCopy( obj ) : NULL; + ( (Entry0A *) result )->next = NULL; + ( (Entry0A *) result )->prev = NULL; + } else { + alist = astMalloc( sizeof(AstObject *)*(size_t)nel ); + ( (Entry1A *) result )->value = alist; + if( alist ) { + for( i = 0; i < nel; i++ ) { + obj = ( (Entry1A *) in )->value[ i ]; + alist[ i ] = obj ? astCopy( obj ) : NULL; + } + ( (Entry1A *) result )->next = NULL; + ( (Entry1A *) result )->prev = NULL; + } + } + +/* Now deal with integer entries. Scalar entries do not need any further + action. If this is a vector entry copy the values array. */ + } else if( type == AST__INTTYPE ) { + if( nel > 0 ) { + ( (Entry1I *) result )->value = astStore( NULL, + ( (Entry1I *) in )->value, + sizeof( int )*(size_t)nel ); + } + +/* Now deal with short int entries. Scalar entries do not need any further + action. If this is a vector entry copy the values array. */ + } else if( type == AST__SINTTYPE ) { + if( nel > 0 ) { + ( (Entry1S *) result )->value = astStore( NULL, + ( (Entry1S *) in )->value, + sizeof( short int )*(size_t)nel ); + } + +/* Now deal with byte entries. Scalar entries do not need any further + action. If this is a vector entry copy the values array. */ + } else if( type == AST__BYTETYPE ) { + if( nel > 0 ) { + ( (Entry1B *) result )->value = astStore( NULL, + ( (Entry1B *) in )->value, + sizeof( unsigned char )*(size_t)nel ); + } + +/* Similarly deal with floating point entries. */ + } else if( type == AST__DOUBLETYPE ) { + if( nel > 0 ) { + ( (Entry1D *) result )->value = astStore( NULL, + ( (Entry1D *) in )->value, + sizeof( double )*(size_t)nel ); + } + + } else if( type == AST__FLOATTYPE ) { + if( nel > 0 ) { + ( (Entry1F *) result )->value = astStore( NULL, + ( (Entry1F *) in )->value, + sizeof( float )*(size_t)nel ); + } + +/* Similarly deal with void pointer entries. */ + } else if( type == AST__POINTERTYPE ) { + if( nel > 0 ) { + ( (Entry1P *) result )->value = astStore( NULL, + ( (Entry1P *) in )->value, + sizeof( void * )*(size_t)nel ); + } + +/* Report an error if the data type is unknown. */ + } else { + astError( AST__INTER, "CopyMapEntry(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + type ); + } + +/* If an error has occurred, attempt to delete the returned MapEntry. */ + if( !astOK ) result = FreeMapEntry( result, status ); + +/* Return the result. */ + return result; +} + +static void CopyTableEntry( AstKeyMap *in, AstKeyMap *out, int itab, int *status ){ +/* +* Name: +* CopyTableEntry + +* Purpose: +* Produces a deep copy of a hash table element. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void CopyTableEntry( AstKeyMap *in, AstKeyMap *out, int itab, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function creates a deep copy of the linked-list of KeyMap entries +* stored in the specified element of the input KeyMaps hash table. + +* Parameters: +* in +* Pointer to the input KeyMap. +* out +* Pointer to the output KeyMap. +* itab +* Index of the hash table element to be copied. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapEntry **link; /* Address to store foward link */ + AstMapEntry *next; /* Pointer to next Entry to copy */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* The "link" variable holds the address of the location at which the + pointer to the next copied MapEntry should be stored. Initialise this to + be the address of the required element of the output hash table. */ + link = &( out->table[ itab ] ); + +/* The "next" variable holds the address of the next MapEntry to be + copied. Initialise this to the MapEntry at the head of the linked list + associated with the specified index of the input KeyMaps hash table. */ + next = in->table[ itab ]; + +/* If the hash table element is empty, store a null pointer and pass on. */ + if( !next ) { + out->table[ itab ] = NULL; + +/* Otherwise copy the liked list. */ + } else { + +/* Loop round until we have copied all entries. */ + while( next && astOK ) { + +/* Copy the next entry, storing the resulting pointer at the position + indicated by "link". */ + *link = CopyMapEntry( next, status ); + +/* If the entry is of type AST__OBJECTTYPE, add it to the head of the + list of AST__OBJECTTYPE entries in the output KeyMap. */ + AddToObjectList( out, *link, status ); + +/* Update "link" and "next" */ + next = next->next; + link = &( (*link)->next ); + } + } + +/* Set the number of entries in the output to be the same as the input. */ + out->nentry[ itab ] = in->nentry[ itab ]; + +/* If an error has occurred, attempt to delete the returned MapEntry. */ + if( !astOK ) FreeTableEntry( out, itab, status ); +} + +static void DoubleTableSize( AstKeyMap *this, int *status ) { +/* +* Name: +* DoubleTableSize + +* Purpose: +* Double the size of the hash table in a KeyMap + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void DoubleTableSize( AstKeyMap *this, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function creates a new hash table which has twice as many +* elements as the current hash table, and moves all the entries out +* of the old table into the new table (at their new positions). + +* Parameters: +* this +* The KeyMap pointer. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapEntry **newtable; + AstMapEntry *next; + AstMapEntry *new_next; + int *newnentry; + int bitmask; + int i; + int newi; + int newmapsize; + +/* Check the global error status. */ + if( !astOK ) return; + +/* Determine the new hash table size. Since mapsize starts out as a power + of 2 (ensured by the NewTable function), the new mapsize will also be + a power of 2. Also, create a bit mask that can be used to zero the + upper bits in a full width hash value. */ + newmapsize = 2*this->mapsize; + bitmask = newmapsize - 1; + +/* Create the new arrays, leaving the old arrays intact for the moment. */ + newtable = astMalloc( newmapsize*sizeof( AstMapEntry * ) ); + newnentry = astMalloc( newmapsize*sizeof( int ) ); + if( astOK ) { + +/* Initialise the new table. */ + for( i = 0; i < newmapsize; i++ ) { + newtable[ i ] = NULL; + newnentry[ i ] = 0; + } + +/* Loop round each of the existing table entries. */ + for( i = 0; i < this->mapsize; i++ ) { + +/* The "next" variable holds the address of the next MapEntry to be + moved. Initialise this to the MapEntry at the head of the linked list + associated with the specified index of the input KeyMaps hash table. */ + next = this->table[ i ]; + +/* Loop round until we have moved all entries. */ + while( next && astOK ) { + +/* Find the index within the new table at which to store this entry. */ + newi = ( next->hash & bitmask ); + +/* Save the pointer to the next entry following the current one in the + linked list. */ + new_next = next->next; + +/* Put a pointer to the MapEntry which is currently at the head of the + linked list in the "next" component of the current MapEntry. */ + next->next = newtable[ newi ]; + +/* Store the supplied MapEntry pointer in the requested element of the + hash table. */ + newtable[ newi ] = next; + +/* Increment the length of linked list. */ + newnentry[ newi ]++; + +/* Use the pointer to the next map entry to be moved. */ + next = new_next; + } + } + } + +/* If OK, delete the existing table and use the new table */ + if( astOK ) { + this->mapsize = newmapsize; + + (void) astFree( this->table ); + this->table = newtable; + + (void) astFree( this->nentry ); + this->nentry = newnentry; + +/* If not OK, delete the new table. */ + } else { + newtable = astFree( newtable ); + newnentry = astFree( newnentry ); + } +} + +static void DumpEntry( AstMapEntry *entry, AstChannel *channel, int nentry, int *status ) { +/* +* Name: +* DumpEntry + +* Purpose: +* Dump a single AstMapEntry to a Channel. + +* Type: +* Private function. + +* Synopsis: +* void DumpEntry( AstMapEntry *entry, AstChannel *channel, int nentry ) + +* Description: +* This function dumps the supplied MapEntry to the supplied Channel. + +* Parameters: +* entry +* Pointer to the MapEntry whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* nentry +* The integer index value to use when describing the MapEntry in +* the dump. +*/ + +/* Local Variables: */ + char buff[20]; /* Buffer for item names */ + const char *com; /* Pointer to comment string */ + int index; /* Index into vector valued entry */ + int nel; /* Number of elements in value */ + int type; /* Entry data type */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Dump the entry key. */ + (void) sprintf( buff, "Key%d", nentry ); + astWriteString( channel, buff, 1, 1, entry->key, "Item name" ); + +/* Dump the comment if not blank. */ + if( entry->comment && *(entry->comment) ) { + (void) sprintf( buff, "Com%d", nentry ); + astWriteString( channel, buff, 1, 1, entry->comment, "Item comment" ); + } + +/* Get the data type and the length of the Entry. */ + type = entry->type; + nel = entry->nel; + +/* Dump the entry data type. */ + if( type == AST__STRINGTYPE ) { + com = "Item data type (string)"; + + } else if( type == AST__OBJECTTYPE ) { + com = "Item data type (AST Object)"; + + } else if( type == AST__INTTYPE ) { + com = "Item data type (int)"; + + } else if( type == AST__SINTTYPE ) { + com = "Item data type (short int)"; + + } else if( type == AST__BYTETYPE ) { + com = "Item data type (unsigned byte)"; + + } else if( type == AST__DOUBLETYPE ) { + com = "Item data type (double)"; + + } else if( type == AST__FLOATTYPE ) { + com = "Item data type (float)"; + + } else if( type == AST__POINTERTYPE ) { + com = "Item data type (pointer)"; + + } else if( type == AST__UNDEFTYPE ) { + com = "Item data type (undefined)"; + + } else { + com = ""; + astError( AST__INTER, "DumpEntry(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + type ); + } + (void) sprintf( buff, "Typ%d", nentry ); + astWriteInt( channel, buff, 1, 1, entry->type, com ); + +/* Dump the vector length */ + if( entry->nel > 0 ) { + (void) sprintf( buff, "Nel%d", nentry ); + astWriteInt( channel, buff, 1, 1, entry->nel, "Vector length" ); + } + +/* First deal with integer entries. */ + if( type == AST__INTTYPE ) { + if( entry->nel == 0 ) { + (void) sprintf( buff, "Val%d", nentry ); + astWriteInt( channel, buff, 1, 1, ((Entry0I *)entry)->value, "Item value" ); + } else { + com = "Item values"; + for( index = 0; index < nel; index++ ){ + (void) sprintf( buff, "V%d_%d", nentry, index + 1 ); + astWriteInt( channel, buff, 1, 1, ((Entry1I *)entry)->value[ index ], com ); + com = ""; + } + } + +/* Similarly, deal with short int entries. */ + } else if( type == AST__SINTTYPE ) { + if( entry->nel == 0 ) { + (void) sprintf( buff, "Val%d", nentry ); + astWriteInt( channel, buff, 1, 1, (int) ((Entry0S *)entry)->value, "Item value" ); + } else { + com = "Item values"; + for( index = 0; index < nel; index++ ){ + (void) sprintf( buff, "V%d_%d", nentry, index + 1 ); + astWriteInt( channel, buff, 1, 1, (int) ((Entry1S *)entry)->value[ index ], com ); + com = ""; + } + } + +/* Similarly, deal with byte entries. */ + } else if( type == AST__BYTETYPE ) { + if( entry->nel == 0 ) { + (void) sprintf( buff, "Val%d", nentry ); + astWriteInt( channel, buff, 1, 1, (int) ((Entry0B *)entry)->value, "Item value" ); + } else { + com = "Item values"; + for( index = 0; index < nel; index++ ){ + (void) sprintf( buff, "V%d_%d", nentry, index + 1 ); + astWriteInt( channel, buff, 1, 1, (int) ((Entry1B *)entry)->value[ index ], com ); + com = ""; + } + } + +/* Similarly deal with floating point entries. */ + } else if( type == AST__DOUBLETYPE ) { + if( entry->nel == 0 ) { + if( ((Entry0D *)entry)->value != AST__BAD ) { + (void) sprintf( buff, "Val%d", nentry ); + astWriteDouble( channel, buff, 1, 1, ((Entry0D *)entry)->value, "Item value" ); + } + } else { + com = "Item values"; + for( index = 0; index < nel; index++ ){ + if( ((Entry1D *)entry)->value[ index ] != AST__BAD ) { + (void) sprintf( buff, "V%d_%d", nentry, index + 1 ); + astWriteDouble( channel, buff, 1, 1, ((Entry1D *)entry)->value[ index ], com ); + com = ""; + } + } + } + +/* Similarly deal with single precision floating point entries. */ + } else if( type == AST__FLOATTYPE ) { + if( entry->nel == 0 ) { + (void) sprintf( buff, "Val%d", nentry ); + astWriteDouble( channel, buff, 1, 1, (double) ((Entry0F *)entry)->value, "Item value" ); + } else { + com = "Item values"; + for( index = 0; index < nel; index++ ){ + (void) sprintf( buff, "V%d_%d", nentry, index + 1 ); + astWriteDouble( channel, buff, 1, 1, (double) ((Entry1F *)entry)->value[ index ], com ); + com = ""; + } + } + +/* Do the same for string values. */ + } else if( type == AST__STRINGTYPE ) { + if( entry->nel == 0 ) { + (void) sprintf( buff, "Val%d", nentry ); + astWriteString( channel, buff, 1, 1, ((Entry0C *)entry)->value, "Item value" ); + } else { + com = "Item values"; + for( index = 0; index < nel; index++ ){ + (void) sprintf( buff, "V%d_%d", nentry, index + 1 ); + astWriteString( channel, buff, 1, 1, ((Entry1C *)entry)->value[ index ], com ); + com = ""; + } + } + +/* Do the same for Object values. */ + } else if( type == AST__OBJECTTYPE ) { + if( entry->nel == 0 ) { + if( ((Entry0A *)entry)->value ) { + (void) sprintf( buff, "Val%d", nentry ); + astWriteObject( channel, buff, 1, 1, ((Entry0A *)entry)->value, "Item value" ); + } + } else { + com = "Item values"; + for( index = 0; index < nel; index++ ){ + if( ((Entry1A *)entry)->value[ index ] ) { + (void) sprintf( buff, "V%d_%d", nentry, index + 1 ); + astWriteObject( channel, buff, 1, 1, ((Entry1A *)entry)->value[ index ], com ); + com = ""; + } + } + } + +/* Void pointer values cannot be dumped. */ + } else if( type == AST__POINTERTYPE ) { + astError( AST__INTER, "DumpEntry(KeyMap): Cannot dump KeyMaps in " + "which the values are arbitrary C pointers (possible " + "programming error)." , status); + +/* Nothing further to do for undefined values. */ + } else if( type == AST__UNDEFTYPE ) { + +/* Report an error if the data type is unknown. */ + } else if( astOK ) { + astError( AST__INTER, "DumpEntry(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + type ); + } +} + +static AstMapEntry *FreeMapEntry( AstMapEntry *in, int *status ){ +/* +* Name: +* FreeMapEntry + +* Purpose: +* Frees the supplied KeyMap entry. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* AstMapEntry *FreeMapEntry( AstMapEntry *in, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function frees resources used by the supplied MapEntry, then +* frees the MapEntry structure itself and returns a NULL pointer. + +* Parameters: +* in +* Pointer to the MapEntry to be freed. NULL may be supplied in +* which the function returns without action. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A NULL pointer. + +* Notes: +* - It is the callers responsibility to ensure that any other MapEntry +* which refers to the supplied MapEntry (e.g. through the "next" link +* in the MapEntry structure) is modified to take account of the +* freeing of the supplied MapEntry. +* - This function attempts to execute even if it is invoked with the +* global error status set. +*/ + +/* Local Variables: */ + AstObject **alist; /* Pointer to list of AST object pointers */ + AstObject *obj; /* Pointer to AST object */ + const char **slist; /* Pointer to list of text pointers */ + int i; /* Loop count */ + int nel; /* No. of values in entry vector (0 => scalar) */ + int type; /* Entry data type */ + +/* Check the supplied pointer. */ + if( !in ) return NULL; + +/* Get the data type and length of the MapEntry. */ + nel = in->nel; + type = in->type; + +/* First deal with string entries. */ + if( type == AST__STRINGTYPE ) { + +/* For scalar valued entries, free the single string in the input entry. */ + if( nel == 0 ) { + ( (Entry0C *) in )->value = (const char *) astFree( ( void *) ( (Entry0C *) in )->value ); + +/* For vector valued entries, free the strings, then free the array storing + the string pointers. */ + } else { + slist = ( (Entry1C *) in )->value; + if( slist ) { + for( i = 0; i < nel; i++ ) slist[ i ] = astFree( (void *) slist[ i ] ); + ( (Entry1C *) in )->value = astFree( (void *) slist ); + } + } + +/* Similarly deal with AST Object entries. */ + } else if( type == AST__OBJECTTYPE ) { + if( nel == 0 ) { + obj = ( (Entry0A *) in )->value; + if( obj ) ( (Entry0A *) in )->value = astAnnul( obj ); + ( (Entry0A *) in )->next = NULL; + ( (Entry0A *) in )->prev = NULL; + } else { + alist = ( (Entry1A *) in )->value; + if( alist ) { + for( i = 0; i < nel; i++ ) { + if( alist[ i ] ) alist[ i ] = astAnnul( alist[ i ] ); + } + ( (Entry1A *) in )->value = astFree( alist ); + ( (Entry1A *) in )->next = NULL; + ( (Entry1A *) in )->prev = NULL; + } + } + +/* Now deal with integer entries. Scalar entries do not need any further + action. If this is a vector entry free the values array. */ + } else if( type == AST__INTTYPE ) { + if( nel > 0 ) ( (Entry1I *) in )->value = astFree( ( (Entry1I *) in )->value ); + +/* Now deal with short int entries. Scalar entries do not need any further + action. If this is a vector entry free the values array. */ + } else if( type == AST__SINTTYPE ) { + if( nel > 0 ) ( (Entry1S *) in )->value = astFree( ( (Entry1S *) in )->value ); + +/* Now deal with byte entries. Scalar entries do not need any further + action. If this is a vector entry free the values array. */ + } else if( type == AST__BYTETYPE ) { + if( nel > 0 ) ( (Entry1B *) in )->value = astFree( ( (Entry1B *) in )->value ); + +/* Similarly deal with void * pointer entries. */ + } else if( type == AST__POINTERTYPE ) { + if( nel > 0 ) ( (Entry1P *) in )->value = astFree( ( (Entry1P *) in )->value ); + +/* Similarly deal with floating point entries. */ + } else if( type == AST__DOUBLETYPE ) { + if( nel > 0 ) ( (Entry1D *) in )->value = astFree( ( (Entry1D *) in )->value ); + +/* Similarly deal with single precision floating point entries. */ + } else if( type == AST__FLOATTYPE ) { + if( nel > 0 ) ( (Entry1F *) in )->value = astFree( ( (Entry1F *) in )->value ); + +/* Nothing further to do for undefined values. */ + } else if( type == AST__UNDEFTYPE ) { + +/* Report an error if the data type is unknown. */ + } else { + astError( AST__INTER, "FreeMapEntry(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + type ); + } + +/* Free or nullify pointers in the AstMapEntry structure. */ + in->next = NULL; + in->snext = NULL; + in->sprev = NULL; + in->key = astFree( (void *) in->key ); + in->comment = astFree( (void *) in->comment ); + +/* Free the complete AstMapEntry structure. */ + astFree( in ); + +/* Return a NULL pointer. */ + return NULL; +} + +static void FreeTableEntry( AstKeyMap *this, int itab, int *status ){ +/* +* Name: +* FreeTableEntry + +* Purpose: +* Frees the linked list of KeyMap entries stored in a given element of +* the hash table. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void FreeTableEntry( AstKeyMap *this, int itab, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function frees resources used by all the MapEntries in the +* linked list associated with the specified element of the hash table +* of the supplied KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap +* itab +* Index of the hash table element to free. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if it is invoked with the +* global error status set. +*/ + +/* Local Variables: */ + AstMapEntry *link; /* Pointer the next but one MapEntry to be freed */ + AstMapEntry *next; /* Pointer the next MapEntry to be freed */ + +/* Check it is safe to proceed. */ + if( this && itab >= 0 && itab < this->mapsize ) { + +/* Store a pointer to the MapEntry which is to be freed next. */ + next = this->table[ itab ]; + +/* Loop round freeing all MapEntries in the linked list. */ + while( next ) { + +/* Store a pointer to the MapEntry which will be freed after this one. */ + link = next->next; + +/* Free this MapEntry */ + (void) FreeMapEntry( next, status ); + +/* Set up the next MapEntry to be freed. */ + next = link; + } + +/* Store a NULL pointer in the table element. */ + this->table[ itab ] = NULL; + +/* Sets the number of entries in this hash table element to zero. */ + this->nentry[ itab ] = 0; + } +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* KeyMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a KeyMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the KeyMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the KeyMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the KeyMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstKeyMap *this; /* Pointer to the KeyMap structure */ + const char *result; /* Pointer value to return */ + int ival; /* Attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the KeyMap structure. */ + this = (AstKeyMap *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* SizeGuess. */ +/* ---------- */ + if ( !strcmp( attrib, "sizeguess" ) ) { + ival = astGetSizeGuess( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* KeyCase. */ +/* --------- */ + } else if ( !strcmp( attrib, "keycase" ) ) { + ival = astGetKeyCase( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* KeyError. */ +/* --------- */ + } else if ( !strcmp( attrib, "keyerror" ) ) { + ival = astGetKeyError( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* MapLocked. */ +/* --------- */ + } else if ( !strcmp( attrib, "maplocked" ) ) { + ival = astGetMapLocked( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* SortBy. */ +/* --------- */ + } else if ( !strcmp( attrib, "sortby" ) ) { + ival = astGetSortBy( this ); + if ( astOK ) { + result = SortByString( ival, "astGetAttrib", status ); + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* KeyMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied KeyMap, +* in bytes. + +* Parameters: +* this +* Pointer to the KeyMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstKeyMap *this; /* Pointer to KeyMap structure */ + AstMapEntry *next; /* Pointer the next MapEntry */ + AstObject **alist; /* Pointer to list of AST object pointers */ + AstObject *obj; /* Pointer to AST object */ + const char **slist; /* Pointer to list of text pointers */ + int i; /* Loop count */ + int itab; /* Table entry index */ + int nel; /* No. of values in entry vector (0 => scalar) */ + int result; /* Result value to return */ + int type; /* Entry data type */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the KeyMap structure. */ + this = (AstKeyMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + for( itab = 0; itab < this->mapsize; itab++ ) { + next = this->table[ itab ]; + while( next ) { + nel = next->nel; + type = next->type; + + if( type == AST__STRINGTYPE ) { + + if( nel == 0 ) { + result += astTSizeOf( ( void *) ( (Entry0C *) next )->value ); + + } else { + slist = ( (Entry1C *) next )->value; + if( slist ) { + for( i = 0; i < nel; i++ ) result += astTSizeOf( (void *) slist[ i ] ); + result += astTSizeOf( (void *) slist ); + } + } + + } else if( type == AST__OBJECTTYPE ) { + if( nel == 0 ) { + obj = ( (Entry0A *) next )->value; + result += astGetObjSize( obj ); + } else { + alist = ( (Entry1A *) next )->value; + if( alist ) { + for( i = 0; i < nel; i++ ) { + result += astGetObjSize( alist[ i ] ); + } + result += astTSizeOf( alist ); + } + } + + } else if( type == AST__POINTERTYPE ) { + if( nel > 0 ) result += astTSizeOf( ( (Entry1P *) next )->value ); + + } else if( type == AST__INTTYPE ) { + if( nel > 0 ) result += astTSizeOf( ( (Entry1I *) next )->value ); + + } else if( type == AST__SINTTYPE ) { + if( nel > 0 ) result += astTSizeOf( ( (Entry1S *) next )->value ); + + } else if( type == AST__BYTETYPE ) { + if( nel > 0 ) result += astTSizeOf( ( (Entry1B *) next )->value ); + + } else if( type == AST__DOUBLETYPE ) { + if( nel > 0 ) result += astTSizeOf( ( (Entry1D *) next )->value ); + + } else if( type == AST__FLOATTYPE ) { + if( nel > 0 ) result += astTSizeOf( ( (Entry1F *) next )->value ); + + } else if( type == AST__UNDEFTYPE ) { + + } else { + astError( AST__INTER, "astGetObjSize(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + type ); + } + + result += astTSizeOf( (void *) next->key ); + result += astTSizeOf( (void *) next->comment ); + result += astTSizeOf( next ); + + next = next->next; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetKey( AstKeyMap *this, int index, int *status ) { +/* +* Name: +* GetKey + +* Purpose: +* Get the key at a given index within the KeyMap. + +* Type: +* Private member function. + +* Synopsis: +* #include "keymap.h" +* const char *GetKey( AstKeyMap *this, int index, int *status ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns a string holding the key for the entry with +* the given index within the KeyMap. The index associated with a +* given key is determined by the current setting of the SortBy attribute. + +* Parameters: +* this +* Pointer to the KeyMap. +* index +* The index into the KeyMap. The first entry has index zero, and the last +* has index "size-1", where "size" is the value returned by the +* astMapSize function. An error is reported if the supplied index is +* out of bounds. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the key. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the AST error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapEntry *entry; /* Pointer to the entry */ + const char *result; /* Pointer value to return */ + int ifirst; /* Index of first entry in this table element */ + int ilast; /* Index of last entry in this table element */ + int istep; /* Entry count */ + int itab; /* Index into hash table */ + int nstep; /* No. of entries to skip */ + int sortby; /* The value of the SortBy attribute */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the SortBy value. */ + sortby = astGetSortBy( this ); + +/* First deal with unsorted keys. */ + if( sortby == SORTBY_NONE ) { + +/* Loop round each entry in the hash table. */ + ilast = -1; + for( itab = 0; itab < this->mapsize; itab++ ) { + +/* Find the index of the first and the last Entry in the linked list associated + with this element of the hash table. */ + ifirst = ilast + 1; + ilast += this->nentry[ itab ]; + +/* Pass on if we have not yet reached the element containing the required + key. */ + if( ilast >= index ) { + +/* Find how many steps we need to proceed down the linked list to reach + the required index. */ + nstep = index - ifirst; + +/* Make this many steps down the linked list.*/ + entry = this->table[ itab ]; + for( istep = 0; entry && istep < nstep; istep++ ) entry = entry->next; + +/* Return a pointer to the key string, and break out of the loop. */ + if( entry ) result = entry->key; + break; + + } + } + +/* Now deal with sorted keys. */ + } else { + +/* Get a pointer to the first entry in the sorted list. */ + entry = this->first; + +/* Move up the sorted list by the required number of entries. */ + for( istep = 0; entry && istep < index; istep++ ) entry = entry->snext; + +/* Return a pointer to the key string. */ + if( entry ) result = entry->key; + } + +/* Report an error if the element was not found. */ + if( !result && astOK ) { + astError( AST__MPIND, "astMapKey(%s): Cannot find element " + "%d (zero-based) of the %s.", status, astGetClass( this ), + index, astGetClass( this ) ); + } + +/* Return the result.*/ + return result; +} + +static int GetSizeGuess( AstKeyMap *this, int *status ) { +/* +*+ +* Name: +* astGetSizeGuess + +* Purpose: +* Get the value of the SizeGuess attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* int astGetSizeGuess( AstKeyMap *this ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns the value of the SizeGuess attribute for a +* KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. + +* Returned Value: +* The value of the SizeGuess attribute. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set. + +*- +*/ + +/* Local Variables: */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Return the attribute value using a default if not set. */ + return ( this->sizeguess == INT_MAX ) ? + MIN_TABLE_SIZE*MAX_ENTRIES_PER_TABLE_ENTRY : this->sizeguess; +} + +static int HashFun( const char *key, int bitmask, unsigned long *hash, int *status ){ +/* +* Name: +* HashFun + +* Purpose: +* Returns an integer hash code for a string + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* int HashFun( const char *key, int bitmask, int *hash, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function returns an integer hash code for the supplied string, +* This is the value that isused as the index into the hash table for +* the specified key. + +* Parameters: +* key +* Pointer to the string. Trailing spaces are ignored. +* bitmask +* A bit mask that is used to zero the upper bits of a full width +* hash value in order to produce the required array index. This +* should be one less than the length of the hash table. +* hash +* Pointer to a location at which to put the full width hash value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* An integer in the range zero to ( mapsize - 1 ). + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set. +*/ + +/* Local Variables: */ + int c; + +/* Check the local error status. */ + if ( !astOK ) return 0; + +/* djb2: This hash function was first reported by Dan Bernstein many years + ago in comp.lang.c Each through the "hile" loop corresponds to + "hash = hash*33 + c ". Ignore spaces so that trailing spaces used to + pad F77 character variables will be ignored. */ + *hash = 5381; + while( (c = *key++) ) { + if( c != ' ' ) { + *hash = ((*hash << 5) + *hash) + c; + } + } + return ( *hash & bitmask ); +} + +void astInitKeyMapVtab_( AstKeyMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitKeyMapVtab + +* Purpose: +* Initialise a virtual function table for a KeyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "keymap.h" +* void astInitKeyMapVtab( AstKeyMapVtab *vtab, const char *name ) + +* Class Membership: +* KeyMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the KeyMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitObjectVtab( (AstObjectVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAKeyMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstObjectVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + + vtab->MapGet0P = MapGet0P; + vtab->MapGet0A = MapGet0A; + vtab->MapGet0C = MapGet0C; + vtab->MapGet0D = MapGet0D; + vtab->MapGet0F = MapGet0F; + vtab->MapGet0I = MapGet0I; + vtab->MapGet0B = MapGet0B; + vtab->MapGet0S = MapGet0S; + vtab->MapGet1P = MapGet1P; + vtab->MapGet1A = MapGet1A; + vtab->MapGet1C = MapGet1C; + vtab->MapGet1D = MapGet1D; + vtab->MapGet1F = MapGet1F; + vtab->MapGet1I = MapGet1I; + vtab->MapGet1B = MapGet1B; + vtab->MapGet1S = MapGet1S; + vtab->MapGetElemP = MapGetElemP; + vtab->MapGetElemA = MapGetElemA; + vtab->MapGetElemC = MapGetElemC; + vtab->MapGetElemD = MapGetElemD; + vtab->MapGetElemF = MapGetElemF; + vtab->MapGetElemI = MapGetElemI; + vtab->MapGetElemS = MapGetElemS; + vtab->MapGetElemB = MapGetElemB; + vtab->MapPutElemP = MapPutElemP; + vtab->MapPutElemA = MapPutElemA; + vtab->MapPutElemC = MapPutElemC; + vtab->MapPutElemD = MapPutElemD; + vtab->MapPutElemF = MapPutElemF; + vtab->MapPutElemI = MapPutElemI; + vtab->MapPutElemS = MapPutElemS; + vtab->MapPutElemB = MapPutElemB; + vtab->MapPut0A = MapPut0A; + vtab->MapPut0P = MapPut0P; + vtab->MapPut0C = MapPut0C; + vtab->MapPut0D = MapPut0D; + vtab->MapPut0F = MapPut0F; + vtab->MapPut0I = MapPut0I; + vtab->MapPut0S = MapPut0S; + vtab->MapPut0B = MapPut0B; + vtab->MapPut1P = MapPut1P; + vtab->MapPut1A = MapPut1A; + vtab->MapPut1C = MapPut1C; + vtab->MapPut1D = MapPut1D; + vtab->MapPut1F = MapPut1F; + vtab->MapPut1I = MapPut1I; + vtab->MapPut1S = MapPut1S; + vtab->MapPut1B = MapPut1B; + vtab->MapPutU = MapPutU; + vtab->MapRemove = MapRemove; + vtab->MapRename = MapRename; + vtab->MapCopy = MapCopy; + vtab->MapDefined = MapDefined; + vtab->MapSize = MapSize; + vtab->MapLenC = MapLenC; + vtab->MapLength = MapLength; + vtab->MapType = MapType; + vtab->MapHasKey = MapHasKey; + vtab->MapKey = MapKey; + vtab->MapIterate = MapIterate; + + vtab->ClearSizeGuess = ClearSizeGuess; + vtab->SetSizeGuess = SetSizeGuess; + vtab->GetSizeGuess = GetSizeGuess; + vtab->TestSizeGuess = TestSizeGuess; + + vtab->ClearSortBy = ClearSortBy; + vtab->SetSortBy = SetSortBy; + vtab->GetSortBy = GetSortBy; + vtab->TestSortBy = TestSortBy; + + vtab->ClearKeyError = ClearKeyError; + vtab->SetKeyError = SetKeyError; + vtab->GetKeyError = GetKeyError; + vtab->TestKeyError = TestKeyError; + + vtab->ClearKeyCase = ClearKeyCase; + vtab->SetKeyCase = SetKeyCase; + vtab->GetKeyCase = GetKeyCase; + vtab->TestKeyCase = TestKeyCase; + + vtab->ClearMapLocked = ClearMapLocked; + vtab->SetMapLocked = SetMapLocked; + vtab->GetMapLocked = GetMapLocked; + vtab->TestMapLocked = TestMapLocked; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + +/* Declare the destructor, copy constructor and dump function. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "KeyMap", "Map of key/value pairs" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void InitMapEntry( AstMapEntry *entry, int type, int nel, int *status ){ +/* +* Name: +* InitMapEntry + +* Purpose: +* initialise a MapEntry structure to null values. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void InitMapEntry( AstMapEntry *entry, int type, int nel, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function initialises the contents of a MapEntry to null values. + +* Parameters: +* this +* Pointer to the MapEntry. +* type +* The type of the MapEntry. +* nel +* The number of elements in the entry: 0 = scalar, >0 = vector. +* status +* Pointer to the inherited status variable. + +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Initialise all elements with in the MapEntry structure. */ + entry->next = NULL; + entry->key = NULL; + entry->hash = 0; + entry->type = type; + entry->nel = nel; + entry->comment = NULL; + entry->defined = 0; + entry->snext = NULL; + entry->sprev = NULL; + entry->member = 0; + entry->keymember = 0; + entry->sortby = SORTBY_NONE; + + if( type == AST__OBJECTTYPE ) { + if( nel == 0 ) { + ( (Entry0A *) entry )->next = NULL; + ( (Entry0A *) entry )->prev = NULL; + } else { + ( (Entry1A *) entry )->next = NULL; + ( (Entry1A *) entry )->prev = NULL; + } + } + +} + +static int KeyCmp( const char *key1, const char *key2 ) { +/* +* Name: +* KeyCmp + +* Purpose: +* Compares keys for equality. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* int KeyCmp( const char *key1, const char *key2 ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function compares two strings. It is like strcmp except that it +* ignores trailing spaces. + +* Parameters: +* key1 +* Pointer to first string. +* key2 +* Pointer to second string. + +* Returned Value: +* One if the keys differ. Zero if they are identical (except for +* trailing spaces). + +*/ + +/* Local Variables: */ + const char *k1; /* Pointer to next "key1" character */ + const char *k2; /* Pointer to next "key2" character */ + int result; /* Returned flag */ + +/* Check the strings are deifned. */ + if ( !key1 || !key2 ) return 0; + +/* Get pointers to the first characters to differ, or to the first null + character, which ever comes first. */ + k1 = key1; + k2 = key2; + while( *k1 && ( *k1 == *k2 ) ) { + k1++; + k2++; + } + +/* If both characters are null, the strings are identical. If neither is null, + the string definitely differ. If one is null, we need to check if the + other one only has spaces to the end of the string. */ + if( *k1 ) { + if( *k2 ) { + result = ( *k1 > *k2 ) ? 1 : -1; + } else { + while( *k1 == ' ' ) k1++; + result = ( *k1 == 0 ) ? 0 : 1; + } + } else { + if( *k2 ) { + while( *k2 == ' ' ) k2++; + result = ( *k2 == 0 ) ? 0 : -1; + } else { + result = 0; + } + } + +/* Return the result. */ + return result; +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* KeyMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstKeyMap *this; /* Pointer to KeyMap structure */ + AstMapEntry *next; /* Pointer the next MapEntry */ + AstObject **alist; /* Pointer to list of AST object pointers */ + AstObject *obj; /* Pointer to AST object */ + int i; /* Loop count */ + int nel; /* No. of values in entry vector (0 => scalar) */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the KeyMap structure. */ + this = (AstKeyMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + + next = this->firstA; + while( next ) { + nel = next->nel; + if( nel == 0 ) { + obj = ( (Entry0A *) next )->value; + if( !result ) result = astManageLock( obj, mode, extra, fail ); + next = ( (Entry0A *) next)->next; + } else { + alist = ( (Entry1A *) next )->value; + if( alist ) { + for( i = 0; i < nel; i++ ) { + if( !result ) result = astManageLock( alist[ i ], mode, + extra, fail ); + } + } + next = ( (Entry1A *) next)->next; + } + } + + return result; + +} +#endif + +static void MapCopy( AstKeyMap *this, AstKeyMap *that, int *status ) { +/* +*++ +* Name: +c astMapCopy +f AST_MAPCOPY + +* Purpose: +* Copy entries from one KeyMap into another. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c void astMapCopy( AstKeyMap *this, AstKeyMap *that ) +f CALL AST_MAPCOPY( THIS, THAT, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +c This function +f This routine +* copies all entries from one KeyMap into another. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the destination KeyMap. +c that +f THAT = INTEGER (Given) +* Pointer to the source KeyMap. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Entries from the source KeyMap will replace any existing entries in +* the destination KeyMap that have the same key. +* - The one exception to the above rule is that if a source entry +* contains a scalar KeyMap entry, and the destination contains a +* scalar KeyMap entry with the same key, then the source KeyMap entry +* will be copied into the destination KeyMap entry using this function, +* rather than simply replacing the destination KeyMap entry. +* - If the destination entry has a non-zero value for its MapLocked +* attribute, then an error will be reported if the source KeyMap +* contains any keys that do not already exist within the destination +* KeyMap. + +*-- +*/ + +/* Local Variables: */ + AstMapEntry *in_entry; /* Pointer to next source entry to copy */ + AstMapEntry *out_entry;/* Pointer to existing destination entry */ + AstObject *in_obj; /* Pointer for source Object entry */ + AstObject *out_obj; /* Pointer for destination Object entry */ + const char *key; /* Key for current entry */ + int i; /* Index into source hash table */ + int itab; /* Index of destination hash table element */ + int keymember; /* Identifier for key */ + int merged; /* Were source and destination KeyMaps merged? */ + unsigned long hash; /* Full width hash value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Loop round all entries in the source hash table. */ + for( i = 0; i < that->mapsize; i++ ) { + +/* Get a pointer to the next source KeyMap entry. */ + in_entry = that->table[ i ]; + +/* Loop round all entries in this element of the source hash table. */ + while( in_entry && astOK ) { + +/* Get its key. */ + key = in_entry->key; + +/* Search for a destination entry with the same key. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + out_entry = SearchTableEntry( this, itab, key, status ); + +/* If the destination KeyMap does not contain an entry with the current + key, store a copy of the entry in the destination, or report an error + if the destination's MapLocked attribute is set. */ + if( !out_entry ) { + if( astGetMapLocked( this ) ) { + astError( AST__BADKEY, "astMapCopy(%s): Failed to copy " + "item \"%s\": \"%s\" is not a known item.", status, + astGetClass( this ), key, key ); + } else { + out_entry = CopyMapEntry( in_entry, status ); + out_entry = AddTableEntry( this, itab, out_entry, -1, status ); + } + +/* If the destination KeyMap contains an entry with the current key... */ + } else { + +/* The usual thing is to just replace the existing entry in the + destination with a copy of the source entry. The one case where this is + not done is if both entries are scalar KeyMaps. In this case the source + KeyMap is merged into the destination KeyMap using this function. First + see if we have this situation, and if so, copy the entries from the + source KeyMap to the destination KeyMap. */ + merged = 0; + if( in_entry->nel == 0 || in_entry->nel == 1 ) { + if( out_entry->nel == 0 || out_entry->nel == 1 ) { + if( in_entry->type == AST__OBJECTTYPE && + out_entry->type == AST__OBJECTTYPE ) { + + if( in_entry->nel == 0 ) { + in_obj = ((Entry0A *)in_entry)->value; + } else { + in_obj = (((Entry1A *)in_entry)->value)[ 0 ]; + } + + if( out_entry->nel == 0 ) { + out_obj = ((Entry0A *)out_entry)->value; + } else { + out_obj = (((Entry1A *)out_entry)->value)[ 0 ]; + } + + if( astIsAKeyMap( in_obj ) && + astIsAKeyMap( out_obj ) ) { + astMapCopy( (AstKeyMap *) out_obj, + (AstKeyMap *) in_obj ); + merged = 1; + } + } + } + } + +/* If the source and desination entries are not KeyMaps, then just remove + the entry in the desination KeyMap and add a copy of the source entry. + But retain the original keymember value since we are just changing the + value of an existing key. */ + if( ! merged ) { + out_entry = RemoveTableEntry( this, itab, key, status ); + keymember = out_entry->keymember; + (void) FreeMapEntry( out_entry, status ); + out_entry = CopyMapEntry( in_entry, status ); + out_entry = AddTableEntry( this, itab, out_entry, keymember, status ); + } + } + +/* Update the address of the next MapEntry in the source. */ + in_entry = in_entry->next; + } + } +} + +static const char *MapKey( AstKeyMap *this, int index, int *status ) { +/* +*++ +* Name: +c astMapKey +f AST_MAPKEY + +* Purpose: +* Get the key at a given index within the KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c const char *astMapKey( AstKeyMap *this, int index ) +f RESULT = AST_MAPKEY( THIS, INDEX, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns a string holding the key for the entry with +* the given index within the KeyMap. +* +* This function is intended primarily as a means of iterating round all +* the elements in a KeyMap. For this purpose, the number of entries in +* the KeyMap should first be found using +c astMapSize +f AST_MAPSIZE +* and this function should then be called in a loop, with the index +* value going from +c zero to one less than the size of the KeyMap. +f one to the size of the KeyMap. +* The index associated with a given entry is determined by the SortBy +* attribute. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c index +f INDEX = INTEGER (Given) +* The index into the KeyMap. The first entry has index +c zero, and the last has index "size-1", where "size" is the value +c returned by the astMapSize function. +f one, and the last has index SIZE, the value returned by the +f AST_MAPSIZE function. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapKey() +c A pointer to a null-terminated string containing the key. +f AST_MAPKEY = CHARACTER * ( AST__SZCHR ) +f The key value. + +* Notes: +c - The returned pointer is guaranteed to remain valid and the +c string to which it points will not be over-written for a total +c of 50 successive invocations of this function. After this, the +c memory containing the string may be re-used, so a copy of the +c string should be made if it is needed for longer than this. +c - A NULL pointer will be returned if this function is invoked +c with the AST error status set, or if it should fail for any +c reason. +f - A blank string will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any +f reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *result; /* Pointer value to return */ + const char *value; /* Pointer to key value */ + int i; /* Loop counter for initialisation */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* If the "mapkey_strings" array has not been initialised, fill it with + NULL pointers. */ + if ( !mapkey_init ) { + mapkey_init = 1; + for ( i = 0; i < AST__KEYMAP_MAPKEY_MAX_STRINGS; i++ ) mapkey_strings[ i ] = NULL; + } + +/* Obtain a pointer to the required key value. */ + value = GetKey( this, index, status ); + +/* If OK, store a copy of the resulting string in dynamically + allocated memory, putting a pointer to the copy into the next + element of the "mapkey_strings" array. (This process also de-allocates + any previously allocated memory pointed at by this "mapkey_strings" + element, so the earlier string is effectively replaced by the new + one.) */ + if ( astOK ) { + astBeginPM; + mapkey_strings[ mapkey_istr ] = astStore( mapkey_strings[ mapkey_istr ], value, + strlen( value ) + (size_t) 1 ); + astEndPM; + +/* If OK, return a pointer to the copy and increment "mapkey_istr" to use the + next element of "mapkey_strings" on the next invocation. Recycle "mapkey_istr" to + zero when all elements have been used. */ + if ( astOK ) { + result = mapkey_strings[ mapkey_istr++ ]; + if ( mapkey_istr == ( AST__KEYMAP_MAPKEY_MAX_STRINGS - 1 ) ) mapkey_istr = 0; + } + } + +/* Return the result. */ + return result; + +} + +/* +*++ +* Name: +c astMapPut0 +f AST_MAPPUT0 + +* Purpose: +* Add a scalar value to a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c void astMapPut0( AstKeyMap *this, const char *key, type value, +c const char *comment ); +f CALL AST_MAPPUT0( THIS, KEY, VALUE, COMMENT, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +c This is a set of functions +f This is a set of routine +* for adding scalar values to a KeyMap. You should use a +c function +f routine +* which matches the data type of the data you wish to add to the KeyMap +* by replacing in the generic +c function name astMapPut0 +f routine name AST_MAPPUT0 +* by an appropriate 1-character type code (see the "Data Type Codes" +* section below for the code appropriate to each supported data type). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap in which to store the supplied value. +c key +f KEY = CHARACTER * ( * ) (Given) +* A character string to be stored with the value, which can later +* be used to identify the value. Trailing spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +c value +f VALUE = type (Given) +* The value to be stored. The data type of this value should match the +* 1-character type code appended to the +c function name (e.g. if you are using astMapPut0A, the type of this +c value should be "pointer to AstObject"). +f routine name (e.g. if you are using AST_MAPPUT0A, the type of this +f value should be "integer pointer for an AstObject"). +c comment +f COMMENT = CHARACTER * ( * ) (Given) +f A comment string to be stored with the value. +c A pointer to a null-terminated comment string to be stored with the +c value. A NULL pointer may be supplied, in which case no comment is +c stored. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the supplied key is already in use in the KeyMap, the new value +* will replace the old value. +* - If the stored value is an AST Object pointer, the Object's reference +* count is incremented by this call. Any subsequent changes made to +* the Object using the returned pointer will be reflected in any +* any other active pointers for the Object, including any obtained +* later using +c astMapget0A. +f AST_MAPGET0A. +* The reference count for the Object will be decremented when the +* KeyMap is destroyed, or the entry is removed or over-written with a +* different pointer. + +* Data Type Codes: +* To select the appropriate +c function, you should replace in the generic function name astMapPut0 +f routine, you should replace in the generic routine name AST_MAPPUT0 +* with a 1-character data type code, so as to match the data type type +* of the data you are processing, as follows: +c - D: double +c - F: float +c - I: int +c - C: "const" pointer to null terminated character string +c - A: Pointer to AstObject +c - P: Generic "void *" pointer +c - S: short int +c - B: unsigned byte (i.e. unsigned char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - C: CHARACTER +f - A: INTEGER used to identify an AstObject +f - S: INTEGER*2 (short integer) +f - B: Unsigned byte +* +c For example, astMapPut0D would be used to store a "double" value, +c while astMapPut0I would be used to store an "int", etc. +f For example, AST_MAPPUT0D would be used to store a DOUBLE PRECISION value, +f while AST_MAPPUT0I would be used to store an INTEGER, etc. +c +c Note that KeyMaps containing generic "void *" pointers cannot be +c written out using astShow or astWrite. An error will be reported if +c this is attempted. +*-- +*/ +/* Define a macro to implement the function for a specific data type. */ +#define MAKE_MAPPUT0(X,Xtype,Itype,ValExp) \ +static void MapPut0##X( AstKeyMap *this, const char *skey, Xtype value, \ + const char *comment, int *status ) { \ +\ +/* Local Variables: */ \ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ \ + AstMapEntry *oldent; /* Pointer to existing MapEntry */ \ + Entry0##X *entry; /* Structure holding the data for the new Entry */ \ + const char *key; /* Pointer to key string to use */ \ + char *p; /* Pointer to next key character */ \ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + int itab; /* Index of hash table element to use */ \ + int keylen; /* Length of supplied key string */ \ + int keymember; /* Identifier for existing key */ \ + int there; /* Did the entry already exist in the KeyMap? */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Perform any necessary checks on the supplied value to be stored. */ \ + CHECK_##X \ +\ +/* Convert the supplied key to upper case if required. */ \ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapPut0" #X, \ + status ); \ +\ +/* Allocate memory for the new MapEntry. */ \ + entry = astMalloc( sizeof( Entry0##X ) ); \ + if( astOK ) { \ +\ +/* Initialise the new structure.*/ \ + mapentry = (AstMapEntry *) entry; \ + InitMapEntry( mapentry, Itype, 0, status ); \ +\ +/* Now store the new values. */ \ + keylen = strlen( key ); \ + mapentry->key = astStore( NULL, key, keylen + 1 ); \ + if( comment ) mapentry->comment = astStore( NULL, comment, strlen( comment ) + 1 ); \ + mapentry->defined = 1; \ + entry->value = ValExp; \ +\ +/* Terminate the key string to exclude any trailing spaces. */ \ + if( astOK ) { \ + p = (char *) mapentry->key + keylen; \ + while( --p >= mapentry->key ) { \ + if( *p == ' ' ) { \ + *p = 0; \ + } else { \ + break; \ + } \ + } \ + } \ +\ +/* Use the hash function to determine the element of the hash table in \ + which to store the new entry. */ \ + itab = HashFun( mapentry->key, this->mapsize - 1, &(mapentry->hash), status ); \ +\ +/* Remove any existing entry with the given key from the table element. \ + First save the key identifier. */ \ + oldent = RemoveTableEntry( this, itab, mapentry->key, status ); \ + if( oldent ) { \ + keymember = oldent->keymember; \ + oldent = FreeMapEntry( oldent, status ); \ + there = 1; \ + } else { \ + keymember = -1; \ + there = 0; \ + } \ +\ +/* If the KeyMap is locked we report an error if an attempt is made to add a value for \ + a new key. */ \ + if( !there && astGetMapLocked( this ) ) { \ + astError( AST__BADKEY, "astMapPut0" #X "(%s): Failed to add item \"%s\" to a KeyMap: " \ + "\"%s\" is not a known item.", status, astGetClass( this ), key, key ); \ + } \ +\ +/* If all has gone OK, store the new entry at the head of the linked list \ + associated with the selected table entry. */ \ + if( astOK ) { \ + mapentry = AddTableEntry( this, itab, mapentry, keymember, status ); \ +\ +/* If anything went wrong, try to delete the new entry. */ \ + } else { \ + mapentry = FreeMapEntry( mapentry, status ); \ + } \ + } \ +} + +/* Define macros which perform any necessary checks on the supplied value + to be stored. For Object entries, check that we are not adding a KeyMap + which already contains "this". This avoids circular dependencies. + Other types do not need any checks. */ +#define CHECK_A CheckCircle( this, value, "astMapPut0A", status ); +#define CHECK_I +#define CHECK_D +#define CHECK_F +#define CHECK_C +#define CHECK_P +#define CHECK_S +#define CHECK_B + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPPUT0(I,int,AST__INTTYPE,value) +MAKE_MAPPUT0(D,double,AST__DOUBLETYPE,value) +MAKE_MAPPUT0(F,float,AST__FLOATTYPE,value) +MAKE_MAPPUT0(C,const char *,AST__STRINGTYPE,astStore(NULL,value,strlen(value)+1)) +MAKE_MAPPUT0(A,AstObject *,AST__OBJECTTYPE,(value?astClone(value):NULL)) +MAKE_MAPPUT0(P,void *,AST__POINTERTYPE,value) +MAKE_MAPPUT0(S,short int,AST__SINTTYPE,value) +MAKE_MAPPUT0(B,unsigned char,AST__BYTETYPE,value) + +/* Undefine the macro. */ +#undef MAKE_MAPPUT0 +#undef CHECK_A +#undef CHECK_I +#undef CHECK_S +#undef CHECK_D +#undef CHECK_F +#undef CHECK_C +#undef CHECK_P +#undef CHECK_B + +/* +*++ +* Name: +c astMapPut1 +f AST_MAPPUT1 + +* Purpose: +* Add a vector value to a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c void astMapPut1( AstKeyMap *this, const char *key, int size, +c const type value[], const char *comment ); +f CALL AST_MAPPUT1( THIS, KEY, SIZE, VALUE, COMMENT, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +c This is a set of functions +f This is a set of routine +* for adding vector values to a KeyMap. You should use a +c function +f routine +* which matches the data type of the data you wish to add to the KeyMap +* by replacing in the generic +c function name astMapPut1 +f routine name AST_MAPPUT1 +* by an appropriate 1-character type code (see the "Data Type Codes" +* section below for the code appropriate to each supported data type). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap in which to store the supplied values. +c key +f KEY = CHARACTER * ( * ) (Given) +* A character string to be stored with the values, which can later +* be used to identify the values. Trailing spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +c size +f SIZE = INTEGER (Given) +* The number of elements in the supplied array of values. +c value +f VALUE( * ) = type (Given) +* The array of values to be stored. The data type of this value should +* match the 1-character type code appended to the +c function name (e.g. if you are using astMapPut1A, the type of this +c value should be "array of pointers to AstObject"). +f routine name (e.g. if you are using AST_MAPPUT1A, the type of this +f value should be "integer pointer for an AstObject)". +c comment +f COMMENT = CHARACTER * ( * ) (Given) +f A comment string to be stored with the values. +c A pointer to a null-terminated comment string to be stored with the +c values. A NULL pointer may be supplied, in which case no comment is +c stored. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the supplied key is already in use in the KeyMap, the new values +* will replace the old values. + +* Data Type Codes: +* To select the appropriate +c function, you should replace in the generic function name astMapPut1 +f routine, you should replace in the generic routine name AST_MAPPUT1 +* with a 1-character data type code, so as to match the data type type +* of the data you are processing, as follows: +c - D: double +c - F: float +c - I: int +c - C: "const" pointer to null terminated character string +c - A: Pointer to AstObject +c - P: Generic "void *" pointer +c - S: short int +c - B: Unsigned byte (i.e. char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - C: CHARACTER +f - A: INTEGER used to identify an AstObject +f - S: INTEGER*2 (short integer) +f - B: Unsigned byte +* +c For example, astMapPut1D would be used to store "double" values, +c while astMapPut1I would be used to store "int", etc. +f For example, AST_MAPPUT1D would be used to store DOUBLE PRECISION values, +f while AST_MAPPUT1I would be used to store INTEGER, etc. +c +c Note that KeyMaps containing generic "void *" pointers cannot be +c written out using astShow or astWrite. An error will be reported if +c this is attempted. +*-- +*/ +/* Define a macro to implement the function for a specific data type. */ +#define MAKE_MAPPUT1(X,Xtype,Itype,ValExp) \ +static void MapPut1##X( AstKeyMap *this, const char *skey, int size, \ + Xtype value[], const char *comment, \ + int *status ) { \ +\ +/* Local Variables: */ \ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ \ + AstMapEntry *oldent; /* Pointer to existing MapEntry */ \ + Entry1##X *entry; /* Structure holding the data for the new Entry */ \ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + const char *key; /* Pointer to key string to use */ \ + char *p; /* Pointer to next key character */ \ + int itab; /* Index of hash table element to use */ \ + int i; /* Loop count */ \ + int keylen; /* Length of supplied key string */ \ + int keymember; /* Identifier for existing key */ \ + int there; /* Did the entry already exist in the KeyMap? */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Perform any necessary checks on the supplied value to be stored. */ \ + CHECK_##X \ +\ +/* Convert the supplied key to upper case if required. */ \ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapPut1" #X, \ + status ); \ +\ +/* Allocate memory for the new MapEntry. */ \ + entry = astMalloc( sizeof( Entry1##X ) ); \ + if( astOK ) { \ +\ +/* Initialise the new structure.*/ \ + mapentry = (AstMapEntry *) entry; \ + InitMapEntry( mapentry, Itype, size, status ); \ +\ +/* Now store the new values. */ \ + keylen = strlen( key ); \ + mapentry->key = astStore( NULL, key, keylen + 1 ); \ + if( comment ) mapentry->comment = astStore( NULL, comment, strlen( comment ) + 1 ); \ + mapentry->defined = 1; \ + entry->value = astMalloc( sizeof( Xtype )*(size_t)size ); \ +\ + if( astOK ) { \ + for( i = 0; i < size; i++ ) { \ + entry->value[ i ] = ValExp; \ + } \ +\ +/* Terminate the key string to exclude any trailing spaces. */ \ + p = (char *) mapentry->key + keylen; \ + while( --p >= mapentry->key ) { \ + if( *p == ' ' ) { \ + *p = 0; \ + } else { \ + break; \ + } \ + } \ + } \ +\ +/* Use the hash function to determine the element of the hash table in \ + which to store the new entry. */ \ + itab = HashFun( mapentry->key, this->mapsize - 1, &(mapentry->hash), status ); \ +\ +/* Remove any existing entry with the given key from the table element. \ + First save the key identifier. */ \ + oldent = RemoveTableEntry( this, itab, mapentry->key, status ); \ + if( oldent ) { \ + keymember = oldent->keymember; \ + oldent = FreeMapEntry( oldent, status ); \ + there = 1; \ + } else { \ + keymember = -1; \ + there = 0; \ + } \ +\ +/* If the KeyMap is locked we report an error if an attempt is made to add a value for \ + a new key. */ \ + if( !there && astGetMapLocked( this ) ) { \ + astError( AST__BADKEY, "astMapPut1" #X "(%s): Failed to add item \"%s\" to a KeyMap: " \ + "\"%s\" is not a known item.", status, astGetClass( this ), key, key ); \ + } \ +\ +/* If all has gone OK, store the new entry at the head of the linked list \ + associated with the selected table entry. */ \ + if( astOK ) { \ + mapentry = AddTableEntry( this, itab, mapentry, keymember, status ); \ +\ +/* If anything went wrong, try to delete the new entry. */ \ + } else { \ + mapentry = FreeMapEntry( mapentry, status ); \ + } \ + } \ +} + +/* Define macros which perform any necessary checks on the supplied values + to be stored. For Object entries, check that we are not adding a KeyMap + which already contains "this". This avoids circular dependencies. + Other types do not need any checks. */ +#define CHECK_A \ +for( i = 0; i < size; i++ ) { \ + CheckCircle( this, value[ i ], "astMapPut1A", status ); \ +} +#define CHECK_I +#define CHECK_S +#define CHECK_B +#define CHECK_D +#define CHECK_F +#define CHECK_C +#define CHECK_P + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPPUT1(D,const double,AST__DOUBLETYPE,value[i]) +MAKE_MAPPUT1(F,const float,AST__FLOATTYPE,value[i]) +MAKE_MAPPUT1(I,const int,AST__INTTYPE,value[i]) +MAKE_MAPPUT1(C,const char *const,AST__STRINGTYPE,astStore(NULL,value[i],strlen(value[i])+1)) +MAKE_MAPPUT1(A,AstObject *const,AST__OBJECTTYPE,(value[i]?astClone(value[i]):NULL)) +MAKE_MAPPUT1(P,void *const,AST__POINTERTYPE,value[i]) +MAKE_MAPPUT1(S,const short int,AST__SINTTYPE,value[i]) +MAKE_MAPPUT1(B,const unsigned char,AST__BYTETYPE,value[i]) + +/* Undefine the macro. */ +#undef MAKE_MAPPUT1 +#undef CHECK_A +#undef CHECK_I +#undef CHECK_B +#undef CHECK_S +#undef CHECK_D +#undef CHECK_F +#undef CHECK_C +#undef CHECK_P + +void astMapPut1AId_( AstKeyMap *this, const char *skey, int size, + AstObject *const value[], const char *comment, + int *status ) { +/* +* Name: +* astMapPut1AId_ + +* Purpose: +* Add a vector of AstObject pointers to a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "ast.h" +* void astMapPut1A( AstKeyMap *this, const char *key, int size, +* AstObject *const value[], const char *comment ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is the public implementation of the astMapPut1A function +* It is identical to astMapPut1A_ except that ID values are supplied +* via the "value" parameter instead of a true C pointers. + +* Parameters: +* (see astMapPut1) + +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + AstMapEntry *oldent; /* Pointer to existing MapEntry */ + AstObject *op; /* Object pointer */ + Entry1A *entry; /* Structure holding the data for the new Entry */ + char *p; /* Pointer to next key character */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + const char *key; /* Pointer to key string to use */ \ + int i; /* Loop count */ + int itab; /* Index of hash table element to use */ + int keylen; /* Length of supplied key string */ + int keymember; /* Identifier for existing key */ + int there; /* Did the entry already exist in the KeyMap? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapPut1A", + status ); + +/* Allocate memory for the new MapEntry. */ + entry = astMalloc( sizeof( Entry1A ) ); + if( astOK ) { + +/* Initialise the new structure.*/ + mapentry = (AstMapEntry *) entry; + InitMapEntry( mapentry, AST__OBJECTTYPE, size, status ); + +/* Now store the new values. */ + keylen = strlen( key ); + mapentry->key = astStore( NULL, key, keylen + 1 ); + if( comment ) mapentry->comment = astStore( NULL, comment, strlen( comment ) + 1 ); + mapentry->defined = 1; + entry->value = astMalloc( sizeof( AstObject * )*(size_t)size ); + + if( astOK ) { + for( i = 0; i < size; i++ ) { + op = value[ i ] ? astMakePointer( value[ i ] ) : NULL; + entry->value[ i ] = op ? astClone( op ) : NULL; + } + +/* Terminate the key string to exclude any trailing spaces. */ \ + p = (char *) mapentry->key + keylen; + while( --p >= mapentry->key ) { + if( *p == ' ' ) { + *p = 0; + } else { + break; + } + } + } + +/* Use the hash function to determine the element of the hash table in + which to store the new entry. */ + itab = HashFun( mapentry->key, this->mapsize - 1, &(mapentry->hash), status ); + +/* Remove any existing entry with the given key from the table element. */ + oldent = RemoveTableEntry( this, itab, mapentry->key, status ); + if( oldent ) { + keymember = oldent->keymember; + oldent = FreeMapEntry( oldent, status ); + there = 1; + } else { + there = 0; + keymember = -1; + } + +/* If the KeyMap is locked we report an error if an attempt is made to add a value for + a new key. */ + if( !there && astGetMapLocked( this ) ) { + astError( AST__BADKEY, "astMapPut1A(%s): Failed to add item \"%s\" to a KeyMap: " + "\"%s\" is not a known item.", status, astGetClass( this ), key, key ); + } + +/* If all has gone OK, store the new entry at the head of the linked list + associated with the selected table entry. */ + if( astOK ) { + mapentry = AddTableEntry( this, itab, mapentry, keymember, status ); + +/* If anything went wrong, try to delete the new entry. */ + } else { + mapentry = FreeMapEntry( mapentry, status ); + } + } +} + +static void MapPutU( AstKeyMap *this, const char *skey, const char *comment, + int *status ) { +/* +*++ +* Name: +c astMapPutU +f AST_MAPPUTU + +* Purpose: +* Add an entry to a KeyMap with an undefined value. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c void astMapPutU( AstKeyMap *this, const char *key, const char *comment ); +f CALL AST_MAPPUTU( THIS, KEY, COMMENT, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +c This function +f This routine +* adds a new entry to a KeyMap, but no value is stored with the +* entry. The entry therefore has a special data type represented by +* symbolic constant AST__UNDEFTYPE. +* +* An example use is to add entries with undefined values to a KeyMap +* prior to locking them with the MapLocked attribute. Such entries +* can act as placeholders for values that can be added to the KeyMap +* later. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap in which to store the supplied value. +c key +f KEY = CHARACTER * ( * ) (Given) +* A character string to be stored with the value, which can later +* be used to identify the value. Trailing spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +c comment +f COMMENT = CHARACTER * ( * ) (Given) +f A comment string to be stored with the value. +c A pointer to a null-terminated comment string to be stored with the +c value. A NULL pointer may be supplied, in which case no comment is +c stored. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the supplied key is already in use in the KeyMap, the value +* associated with the key will be removed. + +*-- +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + AstMapEntry *oldent; /* Pointer to existing MapEntry */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + const char *key; /* Pointer to key string to use */ + char *p; /* Pointer to next key character */ + int itab; /* Index of hash table element to use */ + int keylen; /* Length of supplied key string */ + int keymember; /* Identifier for existing key */ + int there; /* Did the entry already exist in the KeyMap? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapPutU", + status ); + +/* Allocate memory for the new MapEntry. */ + mapentry = astMalloc( sizeof( AstMapEntry ) ); + if( astOK ) { + +/* Initialise the new structure.*/ + InitMapEntry( mapentry, AST__UNDEFTYPE, 0, status ); + +/* Now store the new values. */ + keylen = strlen( key ); + mapentry->key = astStore( NULL, key, keylen + 1 ); + if( comment ) mapentry->comment = astStore( NULL, comment, strlen( comment ) + 1 ); + mapentry->defined = 0; + +/* Terminate the key string to exclude any trailing spaces. */ + if( astOK ) { + p = (char *) mapentry->key + keylen; + while( --p >= mapentry->key ) { + if( *p == ' ' ) { + *p = 0; + } else { + break; + } + } + } + +/* Use the hash function to determine the element of the hash table in + which to store the new entry. */ + itab = HashFun( mapentry->key, this->mapsize - 1, &(mapentry->hash), status ); + +/* Remove any existing entry with the given key from the table element. */ + oldent = RemoveTableEntry( this, itab, mapentry->key, status ); + if( oldent ) { + keymember = oldent->keymember; + oldent = FreeMapEntry( oldent, status ); + there = 1; + } else { + keymember = -1; + there = 0; + } + +/* If the KeyMap is locked we report an error if an attempt is made to add a value for + a new key. */ + if( !there && astGetMapLocked( this ) ) { + astError( AST__BADKEY, "astMapPutU(%s): Failed to add item \"%s\" to a KeyMap: " + "\"%s\" is not a known item.", status, astGetClass( this ), key, key ); + } + +/* If all has gone OK, store the new entry at the head of the linked list + associated with the selected table entry. */ + if( astOK ) { + mapentry = AddTableEntry( this, itab, mapentry, keymember, status ); + +/* If anything went wrong, try to delete the new entry. */ + } else { + mapentry = FreeMapEntry( mapentry, status ); + } + } +} + +/* +*++ +* Name: +c astMapGet0 +f AST_MAPGET0 + +* Purpose: +* Get a scalar value from a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c int astMapGet0( AstKeyMap *this, const char *key, type *value ); +f RESULT = AST_MAPGET0( THIS, KEY, VALUE, STATUS ) + +f RESULT = AST_MAPGET0C( THIS, KEY, VALUE, L, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is a set of functions for retrieving a scalar value from a KeyMap. +* You should replace in the generic function name +c astMapGet0 +f AST_MAPGET0 +* by an appropriate 1-character type code (see the "Data Type Codes" +* section below for the code appropriate to each supported data type). +* The stored value is converted to the data type indiced by +* before being returned (an error is reported if it is not possible to +* convert the stored value to the requested data type). +f Note, the version of this function which returns character strings, +f AST_MAPGET0C, has an extra parameter in which is returned the number +f of characters written into the supplied CHARACTER variable. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the value to be retrieved. Trailing +* spaces are ignored. The supplied string is converted to upper +* case before use if the KeyCase attribute is currently set to zero. +c value +f VALUE = type (Returned) +c A pointer to a buffer in which to return the requested value. +f The requested value. +* If the requested key is not found, or if it is found but has an +* undefined value (see +c astMapPutU), +f AST_MAPPUTU), +* then the contents of the +* buffer on entry to this function will be unchanged on exit. +c For pointer types ("A" and "C"), the buffer should be a suitable +c pointer, and the address of this pointer should be supplied as the +c "value" parameter. +f L = INTEGER (Returned) +f This parameter is only present in the interface for the AST_MAPGET0C +f function. It is returned holding the number of characters +f written into the CHARACTER variable supplied for parameter VALUE. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapGet0() +f AST_MAPGET0 = LOGICAL +c A non-zero value +f .TRUE. +* is returned if the requested key name was found, and does not have +* an undefined value (see +c astMapPutU). Zero +f AST_MAPPUTU). .FALSE. +* is returned otherwise. + +* Notes: +* - No error is reported if the requested key cannot be found in the +* given KeyMap, but a +c zero +f .FALSE. +* value will be returned as the function value. The supplied buffer +* will be returned unchanged. +* - If the stored value is a vector value, then the first value in +* the vector will be returned. +c - A string pointer returned by astMapGet0C is guaranteed to remain valid +c and the string to which it points will not be over-written for a +c total of 50 successive invocations of this function. After this, +c the memory containing the string may be re-used, so a copy of +c the string should be made if it is needed for longer than this. +* - If the returned value is an AST Object pointer, the Object's reference +* count is incremented by this call. Any subsequent changes made to +* the Object using the returned pointer will be reflected in any +* any other active pointers for the Object. The returned pointer +* should be annulled using +c astAnnul +f AST_ANNUL +* when it is no longer needed. + +* Data Type Codes: +* To select the appropriate +c function, you should replace in the generic function name astMapGet0 +f routine, you should replace in the generic routine name AST_MAPGET0 +* with a 1-character data type code, so as to match the data type type +* of the data you are processing, as follows: +c - F: float +c - D: double +c - I: int +c - C: "const" pointer to null terminated character string +c - A: Pointer to AstObject +c - P: Generic "void *" pointer +c - S: short int +c - B: Unsigned byte (i.e. word) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - C: CHARACTER +f - A: INTEGER used to identify an AstObject +f - S: INTEGER*2 (short integer) +f - B: Unsigned byte +* +c For example, astMapGet0D would be used to get a "double" value, +c while astMapGet0I would be used to get an "int", etc. +f For example, AST_MAPGET0D would be used to get a DOUBLE PRECISION value, +f while AST_MAPGET0I would be used to get an INTEGER, etc. +*-- +*/ +/* Define a macro to implement the function for a specific data type. */ +#define MAKE_MAPGET0(X,Xtype,Itype) \ +static int MapGet0##X( AstKeyMap *this, const char *skey, Xtype *value, int *status ) { \ +\ +/* Local Variables: */ \ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ \ + const char *key; /* Pointer to key string to use */ \ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + int itab; /* Index of hash table element to use */ \ + int raw_type; /* Data type of stored value */ \ + int result; /* Returned flag */ \ + unsigned long hash; /* Full width hash value */ \ + void *raw; /* Pointer to stored value */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Convert the supplied key to upper case if required. */ \ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGet0" #X, \ + status ); \ +\ +/* Use the hash function to determine the element of the hash table in \ + which the key will be stored. */ \ + itab = HashFun( key, this->mapsize - 1, &hash, status ); \ +\ +/* Search the relevent table entry for the required MapEntry. */ \ + mapentry = SearchTableEntry( this, itab, key, status ); \ +\ +/* Skip rest if the key was not found. */ \ + if( mapentry ) { \ +\ +/* Get the address of the raw value, and its data type. */ \ + raw_type = mapentry->type; \ + if( raw_type == AST__INTTYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0I *)mapentry)->value ); \ + } else { \ + raw = ((Entry1I *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__SINTTYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0S *)mapentry)->value ); \ + } else { \ + raw = ((Entry1S *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__BYTETYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0B *)mapentry)->value ); \ + } else { \ + raw = ((Entry1B *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__DOUBLETYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0D *)mapentry)->value ); \ + } else { \ + raw = ((Entry1D *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__FLOATTYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0F *)mapentry)->value ); \ + } else { \ + raw = ((Entry1F *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__POINTERTYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0P *)mapentry)->value ); \ + } else { \ + raw = ((Entry1P *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__STRINGTYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0C *)mapentry)->value ); \ + } else { \ + raw = ((Entry1C *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__OBJECTTYPE ){ \ + if( mapentry->nel == 0 ) { \ + raw = &( ((Entry0A *)mapentry)->value ); \ + } else { \ + raw = ((Entry1A *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__UNDEFTYPE ){ \ + raw = NULL; \ +\ + } else { \ + raw = NULL; \ + astError( AST__INTER, "astMapGet0(KeyMap): Illegal map entry data " \ + "type %d encountered (internal AST programming error).", status, \ + raw_type ); \ + } \ +\ +/* Convert the value, storing the result the supplied buffer. Report an \ + error if conversion is not possible. */ \ + if( !raw ) { \ + result = 0; \ +\ + } else if( !ConvertValue( raw, raw_type, value, Itype, status ) && astOK ){ \ + astError( AST__MPGER, "astMapGet0" #X "(%s): The value of KeyMap key " \ + "\"%s\" cannot be read using the requested data " \ + "type.", status,astGetClass( this ), key ); \ +\ + } else { \ + result = 1; \ + } \ +\ +/* If the KeyError attribute is non-zero, report an error if the key is not \ + found */ \ + } else if( astGetKeyError( this ) && astOK ) { \ + astError( AST__MPKER, "astMapGet0" #X "(%s): No value was found for " \ + "%s in the supplied KeyMap.", status, astGetClass( this ), \ + key ); \ + } \ +\ +/* If an error occurred, return zero. */ \ + if( !astOK ) result = 0; \ +\ +/* Return the result.*/ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPGET0(I,int,AST__INTTYPE) +MAKE_MAPGET0(D,double,AST__DOUBLETYPE) +MAKE_MAPGET0(F,float,AST__FLOATTYPE) +MAKE_MAPGET0(C,const char *,AST__STRINGTYPE) +MAKE_MAPGET0(A,AstObject *,AST__OBJECTTYPE) +MAKE_MAPGET0(P,void *,AST__POINTERTYPE) +MAKE_MAPGET0(S,short int,AST__SINTTYPE) +MAKE_MAPGET0(B,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPGET0 + +int astMapGet0AId_( AstKeyMap *this, const char *skey, AstObject **value, int *status ) { +/* +* Name: +* astMapGet0AId_ + +* Purpose: +* Get a scalar AstObject pointer from a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "ast.h" +* int astMapGet0A( AstKeyMap *this, const char *key, AstObject **value ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is the public implementation of the astMapGet0A function +* It is identical to astMapGet0A_ except that an ID value is returned +* via the "value" parameter instead of a true C pointer. This is required +* because this conversion cannot be performed by the macro that invokes +* the function. + +* Parameters: +* (see astMapGet0) + +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + const char *key; /* Pointer to key string to use */ \ + int itab; /* Index of hash table element to use */ + int raw_type; /* Data type of stored value */ + int result; /* Returned flag */ + unsigned long hash; /* Full width hash value */ + void *raw; /* Pointer to stored value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGet0A", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + +/* Get the address of the raw value, and its data type. */ + raw_type = mapentry->type; + if( raw_type == AST__INTTYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0I *)mapentry)->value ); + } else { + raw = ((Entry1I *)mapentry)->value; + } + + } else if( raw_type == AST__POINTERTYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0P *)mapentry)->value ); + } else { + raw = ((Entry1P *)mapentry)->value; + } + + } else if( raw_type == AST__SINTTYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0S *)mapentry)->value ); + } else { + raw = ((Entry1S *)mapentry)->value; + } + + } else if( raw_type == AST__BYTETYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0B *)mapentry)->value ); + } else { + raw = ((Entry1B *)mapentry)->value; + } + + } else if( raw_type == AST__DOUBLETYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0D *)mapentry)->value ); + } else { + raw = ((Entry1D *)mapentry)->value; + } + + } else if( raw_type == AST__FLOATTYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0F *)mapentry)->value ); + } else { + raw = ((Entry1F *)mapentry)->value; + } + + } else if( raw_type == AST__STRINGTYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0C *)mapentry)->value ); + } else { + raw = ((Entry1C *)mapentry)->value; + } + + } else if( raw_type == AST__OBJECTTYPE ){ + if( mapentry->nel == 0 ) { + raw = &( ((Entry0A *)mapentry)->value ); + } else { + raw = ((Entry1A *)mapentry)->value; + } + + } else if( raw_type == AST__UNDEFTYPE ){ + raw = NULL; + + } else { + raw = NULL; + astError( AST__INTER, "astMapGet0(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + raw_type ); + } + +/* Convert the value, storing the result the supplied buffer. Report an + error if conversion is not possible. */ + if( !raw ) { + result = 0; + + } else if( !ConvertValue( raw, raw_type, value, AST__OBJECTTYPE, status ) && astOK ){ + astError( AST__MPGER, "astMapGet0A(%s): The value of KeyMap key " + "\"%s\" cannot be read using the requested data " + "type.", status, astGetClass( this ), key ); + + } else { + result = 1; + } + +/* If the KeyError attribute is non-zero, report an error if the key is not + found */ + } else if( astGetKeyError( this ) && astOK ) { + astError( AST__MPKER, "astMapGet0A(%s): No value was found for " + "%s in the supplied KeyMap.", status, astGetClass( this ), + key ); + } + +/* If required, return an ID value for the Object. */ + if( result && *value ) *value = astMakeId( *value ); + +/* Return the result.*/ + return result; +} + +/* +*++ +* Name: +c astMapGet1 +f AST_MAPGET1 + +* Purpose: +* Get a vector value from a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c int astMapGet1( AstKeyMap *this, const char *key, int mxval, +c int *nval, type *value ) +c int astMapGet1C( AstKeyMap *this, const char *key, int l, int mxval, +c int *nval, const char *value ) +f RESULT = AST_MAPGET1( THIS, KEY, MXVAL, NVAL, VALUE, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is a set of functions for retrieving a vector value from a KeyMap. +* You should replace in the generic function name +c astMapGet1 +f AST_MAPGET1 +* by an appropriate 1-character type code (see the "Data Type Codes" +* section below for the code appropriate to each supported data type). +* The stored value is converted to the data type indiced by +* before being returned (an error is reported if it is not possible to +* convert the stored value to the requested data type). +c Note, the astMapGet1C function has an extra parameter "l" which +c specifies the maximum length of each string to be stored in the +c "value" buffer (see the "astMapGet1C" section below). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the value to be retrieved. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +c mxval +f MXVAL = INTEGER (Given) +* The number of elements in the +c "value" array. +f VALUE array. +c nval +f NVAL = INTEGER (Returned) +c The address of an integer in which to put the +f The +* number of elements stored in the +c "value" array. +* Any unused elements of the array are left unchanged. +c value +f VALUE( MXVAL ) = type (Returned) +c A pointer to an array in which to return the requested values. +f The requested values. +* If the requested key is not found, or if it is found but has an +* undefined value (see +c astMapPutU), +f AST_MAPPUTU), +* then the contents of the +* buffer on entry to this function will be unchanged on exit. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapGet1() +f AST_MAPGET1 = LOGICAL +c A non-zero value +f .TRUE. +* is returned if the requested key name was found, and does not have +* an undefined value (see +c astMapPutU). Zero +f AST_MAPPUTU). .FALSE. +* is returned otherwise. + +* Notes: +* - No error is reported if the requested key cannot be found in the +* given KeyMap, but a +c zero +f .FALSE. +* value will be returned as the function value. The supplied array +* will be returned unchanged. +* - If the stored value is a scalar value, then the value will be +* returned in the first element of the supplied array, and +c "nval" +f NVAL +* will be returned set to 1. + +c astMapGet1C: +c The "value" buffer supplied to the astMapGet1C function should be a +c pointer to a character array with "mxval*l" elements, where "l" is +c the maximum length of a string to be returned. The value of "l" +c should be supplied as an extra parameter following "key" when +c invoking astMapGet1C, and should include space for a terminating +c null character. + +* Data Type Codes: +* To select the appropriate +c function, you should replace in the generic function name astMapGet1 +f routine, you should replace in the generic routine name AST_MAPGET1 +* with a 1-character data type code, so as to match the data type type +* of the data you are processing, as follows: +c - D: double +c - F: float +c - I: int +c - C: "const" pointer to null terminated character string +c - A: Pointer to AstObject +c - P: Generic "void *" pointer +c - S: short int +c - B: Unsigned byte (i.e. char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - C: CHARACTER +f - A: INTEGER used to identify an AstObject +f - S: INTEGER*2 (short integer) +f - B: Unsigned byte +* +c For example, astMapGet1D would be used to get "double" values, while +c astMapGet1I would be used to get "int" values, etc. For D or I, the +c supplied "value" parameter should be a pointer to an array of doubles +c or ints, with "mxval" elements. For C, the supplied "value" parameter +c should be a pointer to a character string with "mxval*l" elements. +c For A, the supplied "value" parameter should be a pointer to an +c array of AstObject pointers. +f For example, AST_MAPGET1D would be used to get DOUBLE PRECISION values, +f while AST_MAPGET1I would be used to get INTEGER values, etc. + +*-- +*/ +/* Define a macro to implement the function for a specific data type +(excluding "C" since that needs an extra parameter). */ +#define MAKE_MAPGET1(X,Xtype,Itype) \ +static int MapGet1##X( AstKeyMap *this, const char *skey, int mxval, int *nval, Xtype *value, int *status ) { \ +\ +/* Local Variables: */ \ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ \ + const char *key; /* Pointer to key string to use */ \ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + int i; /* Element index */ \ + int itab; /* Index of hash table element to use */ \ + int nel; /* Number of elements in raw vector */ \ + int raw_type; /* Data type of stored value */ \ + int result; /* Returned flag */ \ + size_t raw_size; /* Size of a single raw value */ \ + unsigned long hash; /* Full width hash value */ \ + void *raw; /* Pointer to stored value */ \ +\ +/* Initialise */ \ + result = 0; \ + *nval = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Convert the supplied key to upper case if required. */ \ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGet1" #X, \ + status ); \ +\ +/* Use the hash function to determine the element of the hash table in \ + which the key will be stored. */ \ + itab = HashFun( key, this->mapsize - 1, &hash, status ); \ +\ +/* Search the relevent table entry for the required MapEntry. */ \ + mapentry = SearchTableEntry( this, itab, key, status ); \ +\ +/* Skip rest if the key was not found. */ \ + if( mapentry ) { \ + result = 1; \ +\ +/* Get the address of the first raw value, and its data type. Also get \ + the size of each element of the vector. */ \ + nel = mapentry->nel; \ + raw_type = mapentry->type; \ + if( raw_type == AST__INTTYPE ){ \ + raw_size = sizeof( int ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0I *)mapentry)->value ); \ + } else { \ + raw = ((Entry1I *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__DOUBLETYPE ){ \ + raw_size = sizeof( double ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0D *)mapentry)->value ); \ + } else { \ + raw = ((Entry1D *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__SINTTYPE ){ \ + raw_size = sizeof( short int ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0S *)mapentry)->value ); \ + } else { \ + raw = ((Entry1S *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__BYTETYPE ){ \ + raw_size = sizeof( unsigned char ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0B *)mapentry)->value ); \ + } else { \ + raw = ((Entry1B *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__POINTERTYPE ){ \ + raw_size = sizeof( void * ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0P *)mapentry)->value ); \ + } else { \ + raw = ((Entry1P *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__FLOATTYPE ){ \ + raw_size = sizeof( float ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0F *)mapentry)->value ); \ + } else { \ + raw = ((Entry1F *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__STRINGTYPE ){ \ + raw_size = sizeof( const char * ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0C *)mapentry)->value ); \ + } else { \ + raw = ((Entry1C *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__OBJECTTYPE ){ \ + raw_size = sizeof( AstObject * ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0A *)mapentry)->value ); \ + } else { \ + raw = ((Entry1A *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__UNDEFTYPE ){ \ + raw_size = 0; \ + raw = NULL; \ +\ + } else { \ + raw_size = 0; \ + raw = NULL; \ + astError( AST__INTER, "astMapGet1(KeyMap): Illegal map entry data " \ + "type %d encountered (internal AST programming error).", status, \ + raw_type ); \ + } \ +\ +/* Treat scalars as single-value vectors. */ \ + if( nel == 0 ) nel = 1; \ +\ +/* Ensure no more than "mxval" values are returned. */ \ + if( nel > mxval ) nel = mxval; \ +\ +/* Return the number of values stored in the buffer. */ \ + *nval = nel; \ +\ +/* Loop round all values in the vector. */ \ + for( i = 0; i < nel && astOK; i++ ) { \ +\ +/* Convert the value, storing the result in the supplied buffer. Report an \ + error if conversion is not possible. */ \ + if( !raw ) { \ + result = 0; \ +\ + } else if( !ConvertValue( raw, raw_type, value + i, Itype, status ) && astOK ){ \ + astError( AST__MPGER, "astMapGet1" #X "(%s): The value of " \ + "element %d of KeyMap key \"%s\" cannot be read using " \ + "the requested data type.", status,astGetClass( this ), i + 1, key ); \ + } \ +\ +/* Increment the pointers to the next raw value. */ \ + raw = (char *) raw + raw_size; \ + } \ +\ +/* If the KeyError attribute is non-zero, report an error if the key is not \ + found */ \ + } else if( astGetKeyError( this ) && astOK ) { \ + astError( AST__MPKER, "astMapGet1" #X "(%s): No value was found for " \ + "%s in the supplied KeyMap.", status, astGetClass( this ), \ + key ); \ + } \ +\ +/* If an error occurred,return zero. */ \ + if( !astOK ) result = 0; \ +\ +/* Return the result.*/ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type (except C which is done differently). */ +MAKE_MAPGET1(I,int,AST__INTTYPE) +MAKE_MAPGET1(D,double,AST__DOUBLETYPE) +MAKE_MAPGET1(F,float,AST__FLOATTYPE) +MAKE_MAPGET1(A,AstObject *,AST__OBJECTTYPE) +MAKE_MAPGET1(P,void *,AST__POINTERTYPE) +MAKE_MAPGET1(S,short int,AST__SINTTYPE) +MAKE_MAPGET1(B,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPGET1 + + +static int MapGet1C( AstKeyMap *this, const char *skey, int l, int mxval, + int *nval, char *value, int *status ) { +/* +* Name: +* MapGet1C + +* Purpose: +* Get a vector value from a KeyMap. + +* Type: +* Private member function. + +* Synopsis: +* #include "ast.h" +* int MapGet1C( AstKeyMap *this, const char *key, int l, int mxval, +* int *nval, char *value, int *status ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is the implementation of astMapGet1 for = "C". We +* cannot use the MAKE_MAPGET1 macro for this because the string +* version of this function has an extra parameter giving the maximum +* length of each string which can be stored in the supplied buffer. + +* Parameters: +* (see astMapGet1) +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + char *val; /* Pointer to next buffer element */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + const char *cvalue; /* Pointer to converted string */ + const char *key; /* Pointer to key string to use */ \ + int i; /* Element index */ + int itab; /* Index of hash table element to use */ + int nel; /* Number of elements in raw vector */ + int raw_type; /* Data type of stored value */ + int result; /* Returned flag */ + size_t raw_size; /* Size of a single raw value */ + unsigned long hash; /* Full width hash value */ + void *raw; /* Pointer to stored value */ + +/* Initialise */ + result = 0; + *nval = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGet1C", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + result = 1; + +/* Get the address of the first raw value, and its data type. Also get + the size of each element of the vector. */ + nel = mapentry->nel; + raw_type = mapentry->type; + if( raw_type == AST__INTTYPE ){ + raw_size = sizeof( int ); + if( nel == 0 ) { + raw = &( ((Entry0I *)mapentry)->value ); + } else { + raw = ((Entry1I *)mapentry)->value; + } + + } else if( raw_type == AST__POINTERTYPE ){ + raw_size = sizeof( void * ); + if( nel == 0 ) { + raw = &( ((Entry0P *)mapentry)->value ); + } else { + raw = ((Entry1P *)mapentry)->value; + } + + } else if( raw_type == AST__DOUBLETYPE ){ + raw_size = sizeof( double ); + if( nel == 0 ) { + raw = &( ((Entry0D *)mapentry)->value ); + } else { + raw = ((Entry1D *)mapentry)->value; + } + + } else if( raw_type == AST__SINTTYPE ){ + raw_size = sizeof( short int ); + if( nel == 0 ) { + raw = &( ((Entry0S *)mapentry)->value ); + } else { + raw = ((Entry1S *)mapentry)->value; + } + + } else if( raw_type == AST__BYTETYPE ){ + raw_size = sizeof( unsigned char ); + if( nel == 0 ) { + raw = &( ((Entry0B *)mapentry)->value ); + } else { + raw = ((Entry1B *)mapentry)->value; + } + + } else if( raw_type == AST__FLOATTYPE ){ + raw_size = sizeof( float ); + if( nel == 0 ) { + raw = &( ((Entry0F *)mapentry)->value ); + } else { + raw = ((Entry1F *)mapentry)->value; + } + + } else if( raw_type == AST__STRINGTYPE ){ + raw_size = sizeof( const char * ); + if( nel == 0 ) { + raw = &( ((Entry0C *)mapentry)->value ); + } else { + raw = ((Entry1C *)mapentry)->value; + } + + } else if( raw_type == AST__OBJECTTYPE ){ + raw_size = sizeof( AstObject * ); + if( nel == 0 ) { + raw = &( ((Entry0A *)mapentry)->value ); + } else { + raw = ((Entry1A *)mapentry)->value; + } + + } else if( raw_type == AST__UNDEFTYPE ){ + raw_size = 0; + raw = NULL; + + } else { + raw_size = 0; + raw = NULL; + astError( AST__INTER, "astMapGet1C(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + raw_type ); + } + +/* Treat scalars as single-value vectors. */ + if( nel == 0 ) nel = 1; + +/* Ensure no more than "mxval" values are returned. */ + if( nel > mxval ) nel = mxval; + +/* Return the number of values stored in the buffer. */ + *nval = nel; + +/* Loop round all values in the vector. */ + val = value; + for( i = 0; i < nel && astOK; i++ ) { + +/* Convert the value, storing the result in the supplied buffer. Report an + error if conversion is not possible. */ + if( !raw ) { + result = 0; + + } else if( !ConvertValue( raw, raw_type, &cvalue, AST__STRINGTYPE, status ) && astOK ){ + astError( AST__MPGER, "astMapGet1C(%s): The value of " + "element %d of KeyMap key \"%s\" cannot be read using " + "the requested data type.", status,astGetClass( this ), i + 1, key ); + +/* If succesful, copy the string into the supplied buffer, or as much of + it as will fit. Leave room for a trailing null character. */ + } else { + strncpy( val, cvalue, l - 1 ); + val[ l - 1 ] = 0; + } + +/* Increment the pointers to the next raw value and the next buffer + location. */ + raw = (char *) raw + raw_size; + val += l; + } + +/* If the KeyError attribute is non-zero, report an error if the key is not + found */ + } else if( astGetKeyError( this ) && astOK ) { + astError( AST__MPKER, "astMapGet1C(%s): No value was found for " + "%s in the supplied KeyMap.", status, astGetClass( this ), + key ); + } + +/* If an error occurred,return zero. */ + if( !astOK ) result = 0; + +/* Return the result.*/ + return result; +} + +int astMapGet1AId_( AstKeyMap *this, const char *skey, int mxval, int *nval, + AstObject **value, int *status ) { +/* +* Name: +* astMapGet1AId_ + +* Purpose: +* Get a vector of AstObject pointers from a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "ast.h" +* int astMapGet1A( AstKeyMap *this, const char *key, int mxval, int *nval, +* AstObject **value ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is the public implementation of the astMapGet1A function +* It is identical to astMapGet1A_ except that ID values are returned +* via the "value" parameter instead of a true C pointers. This is required +* because this conversion cannot be performed by the macro that invokes +* the function. + +* Parameters: +* (see astMapGet1) + +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + AstObject *avalue; /* Pointer to AstObject */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + const char *key; /* Pointer to key string to use */ \ + int i; /* Element index */ + int itab; /* Index of hash table element to use */ + int nel; /* Number of elements in raw vector */ + int raw_type; /* Data type of stored value */ + int result; /* Returned flag */ + size_t raw_size; /* Size of a single raw value */ + unsigned long hash; /* Full width hash value */ + void *raw; /* Pointer to stored value */ + +/* Initialise */ + result = 0; + *nval = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGet1A", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + result = 1; + +/* Get the address of the first raw value, and its data type. Also get + the size of each element of the vector. */ + nel = mapentry->nel; + raw_type = mapentry->type; + if( raw_type == AST__INTTYPE ){ + raw_size = sizeof( int ); + if( nel == 0 ) { + raw = &( ((Entry0I *)mapentry)->value ); + } else { + raw = ((Entry1I *)mapentry)->value; + } + + } else if( raw_type == AST__DOUBLETYPE ){ + raw_size = sizeof( double ); + if( nel == 0 ) { + raw = &( ((Entry0D *)mapentry)->value ); + } else { + raw = ((Entry1D *)mapentry)->value; + } + + } else if( raw_type == AST__SINTTYPE ){ + raw_size = sizeof( short int ); + if( nel == 0 ) { + raw = &( ((Entry0S *)mapentry)->value ); + } else { + raw = ((Entry1S *)mapentry)->value; + } + + } else if( raw_type == AST__BYTETYPE ){ + raw_size = sizeof( unsigned char ); + if( nel == 0 ) { + raw = &( ((Entry0B *)mapentry)->value ); + } else { + raw = ((Entry1B *)mapentry)->value; + } + + } else if( raw_type == AST__POINTERTYPE ){ + raw_size = sizeof( void * ); + if( nel == 0 ) { + raw = &( ((Entry0P *)mapentry)->value ); + } else { + raw = ((Entry1P *)mapentry)->value; + } + + } else if( raw_type == AST__FLOATTYPE ){ + raw_size = sizeof( float ); + if( nel == 0 ) { + raw = &( ((Entry0F *)mapentry)->value ); + } else { + raw = ((Entry1F *)mapentry)->value; + } + + } else if( raw_type == AST__STRINGTYPE ){ + raw_size = sizeof( const char * ); + if( nel == 0 ) { + raw = &( ((Entry0C *)mapentry)->value ); + } else { + raw = ((Entry1C *)mapentry)->value; + } + + } else if( raw_type == AST__OBJECTTYPE ){ + raw_size = sizeof( AstObject * ); + if( nel == 0 ) { + raw = &( ((Entry0A *)mapentry)->value ); + } else { + raw = ((Entry1A *)mapentry)->value; + } + + } else if( raw_type == AST__UNDEFTYPE ){ + raw_size = 0; + raw = NULL; + + } else { + raw_size = 0; + raw = NULL; + astError( AST__INTER, "astMapGet1A(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", + status, raw_type ); + } + +/* Treat scalars as single-value vectors. */ + if( nel == 0 ) nel = 1; + +/* Ensure no more than "mxval" values are returned. */ + if( nel > mxval ) nel = mxval; + +/* Return the number of values stored in the buffer. */ + *nval = nel; + +/* Loop round all values in the vector. */ + for( i = 0; i < nel && astOK; i++ ) { + +/* Convert the value, storing the result in the supplied buffer. Report an + error if conversion is not possible. */ + if( !raw ) { + result = 0; + + } else if( !ConvertValue( raw, raw_type, &avalue, AST__OBJECTTYPE, status ) && astOK ){ + astError( AST__MPGER, "astMapGet1A(%s): The value of " + "element %d of KeyMap key \"%s\" cannot be read using " + "the requested data type.", status, astGetClass( this ), + i + 1, key ); + +/* If succesful, return an ID value for the Object. */ + } else { + value[ i ] = avalue ? astMakeId( avalue ) : NULL; + } + +/* Increment the pointers to the next raw value. */ + raw = (char *) raw + raw_size; + } + +/* If the KeyError attribute is non-zero, report an error if the key is not + found */ + } else if( astGetKeyError( this ) && astOK ) { + astError( AST__MPKER, "astMapGet1A(%s): No value was found for " + "%s in the supplied KeyMap.", status, astGetClass( this ), + key ); + } + +/* If an error occurred,return zero. */ + if( !astOK ) result = 0; + +/* Return the result.*/ + return result; +} + +/* +*++ +* Name: +c astMapGetElem +f AST_MAPGETELEM + +* Purpose: +* Get a single element of a vector value from a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c int astMapGetElem( AstKeyMap *this, const char *key, int elem, +c type *value ) +c int astMapGetElemC( AstKeyMap *this, const char *key, int l, int elem, +c char *value ) +f RESULT = AST_MAPGETELEM( THIS, KEY, ELEM, VALUE, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is a set of functions for retrieving a single element of a vector +* value from a KeyMap. You should replace in the generic function name +c astMapGetElem +f AST_MAPGETELEM +* by an appropriate 1-character type code (see the "Data Type Codes" +* section below for the code appropriate to each supported data type). +* The stored value is converted to the data type indiced by +* before being returned (an error is reported if it is not possible to +* convert the stored value to the requested data type). +c Note, the astMapGetElemC function has an extra parameter "l" which +c specifies the maximum length of the string to be stored in the +c "value" buffer (see the "astMapGetElemC" section below). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the value to be retrieved. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +c elem +f ELEM = INTEGER (Given) +* The index of the required vector element, starting at +c zero. +f one. +* An error will be reported if the value is outside the range of +* the vector. +c value +f VALUE = type (Returned) +c A pointer to a buffer in which to return the requested value. +f The requested value. +* If the requested key is not found, or if it is found but has an +* undefined value (see +c astMapPutU), +f AST_MAPPUTU), +* then the contents of the +* buffer on entry to this function will be unchanged on exit. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapGetElem() +f AST_MAPGETELEM = LOGICAL +c A non-zero value +f .TRUE. +* is returned if the requested key name was found, and does not have +* an undefined value (see +c astMapPutU). Zero +f AST_MAPPUTU). .FALSE. +* is returned otherwise. + +* Notes: +* - No error is reported if the requested key cannot be found in the +* given KeyMap, or if it has an undefined value, but a +c zero +f .FALSE. +* value will be returned as the function value. + +c astMapGetElemC: +c The "value" buffer supplied to the astMapGetElemC function should be a +c pointer to a character array with "l" elements, where "l" is the +c maximum length of the string to be returned. The value of "l" +c should be supplied as an extra parameter following "key" when +c invoking astMapGetElemC, and should include space for a terminating +c null character. + +* Data Type Codes: +* To select the appropriate +c function, you should replace in the generic function name +c astMapGetElem +f routine, you should replace in the generic routine name +f AST_MAPGETELEM +* with a 1-character data type code, so as to match the data type type +* of the data you are processing, as follows: +c - D: double +c - F: float +c - I: int +c - C: "const" pointer to null terminated character string +c - A: Pointer to AstObject +c - P: Generic "void *" pointer +c - S: short int +c - B: Unsigned byte (i.e. char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - C: CHARACTER +f - A: INTEGER used to identify an AstObject +f - S: INTEGER*2 (short integer) +f - B: Unsigned byte +* +c For example, astMapGetElemD would be used to get a "double" value, while +c astMapGetElemI would be used to get an "int" value, etc. For D or I, the +c supplied "value" parameter should be a pointer to a double or int. For +c C, the supplied "value" parameter should be a pointer to a character +c string with "l" elements. For A, the supplied "value" parameter should +c be a pointer to an AstObject pointer. +f For example, AST_MAPGETELEMD would be used to get a DOUBLE PRECISION +f value, while AST_MAPGETELEMI would be used to get an INTEGER value, etc. + +*-- +*/ +/* Define a macro to implement the function for a specific data type +(excluding "C" since that needs an extra parameter). */ +#define MAKE_MAPGETELEM(X,Xtype,Itype) \ +static int MapGetElem##X( AstKeyMap *this, const char *skey, int elem, \ + Xtype *value, int *status ) { \ +\ +/* Local Variables: */ \ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ \ + const char *key; /* Pointer to key string to use */ \ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + int itab; /* Index of hash table element to use */ \ + int nel; /* Number of elements in raw vector */ \ + int raw_type; /* Data type of stored value */ \ + int result; /* Returned flag */ \ + size_t raw_size; /* Size of a single raw value */ \ + unsigned long hash; /* Full width hash value */ \ + void *raw; /* Pointer to stored value */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Convert the supplied key to upper case if required. */ \ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGetElem" #X, \ + status ); \ +\ +/* Use the hash function to determine the element of the hash table in \ + which the key will be stored. */ \ + itab = HashFun( key, this->mapsize - 1, &hash, status ); \ +\ +/* Search the relevent table entry for the required MapEntry. */ \ + mapentry = SearchTableEntry( this, itab, key, status ); \ +\ +/* Skip rest if the key was not found. */ \ + if( mapentry ) { \ + result = 1; \ +\ +/* Get the address of the first raw value, and its data type. Also get \ + the size of each element of the vector. */ \ + nel = mapentry->nel; \ + raw_type = mapentry->type; \ + if( raw_type == AST__INTTYPE ){ \ + raw_size = sizeof( int ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0I *)mapentry)->value ); \ + } else { \ + raw = ((Entry1I *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__DOUBLETYPE ){ \ + raw_size = sizeof( double ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0D *)mapentry)->value ); \ + } else { \ + raw = ((Entry1D *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__SINTTYPE ){ \ + raw_size = sizeof( short int ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0S *)mapentry)->value ); \ + } else { \ + raw = ((Entry1S *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__BYTETYPE ){ \ + raw_size = sizeof( unsigned char ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0B *)mapentry)->value ); \ + } else { \ + raw = ((Entry1B *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__POINTERTYPE ){ \ + raw_size = sizeof( void * ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0P *)mapentry)->value ); \ + } else { \ + raw = ((Entry1P *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__FLOATTYPE ){ \ + raw_size = sizeof( float ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0F *)mapentry)->value ); \ + } else { \ + raw = ((Entry1F *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__STRINGTYPE ){ \ + raw_size = sizeof( const char * ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0C *)mapentry)->value ); \ + } else { \ + raw = ((Entry1C *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__OBJECTTYPE ){ \ + raw_size = sizeof( AstObject * ); \ + if( nel == 0 ) { \ + raw = &( ((Entry0A *)mapentry)->value ); \ + } else { \ + raw = ((Entry1A *)mapentry)->value; \ + } \ +\ + } else if( raw_type == AST__UNDEFTYPE ){ \ + raw = NULL; \ +\ + } else { \ + raw_size = 0; \ + raw = NULL; \ + astError( AST__INTER, "astMapGetElem(KeyMap): Illegal map entry " \ + "data type %d encountered (internal AST programming " \ + "error).", status, raw_type ); \ + } \ +\ +/* Treat scalars as single-value vectors. */ \ + if( nel == 0 ) nel = 1; \ +\ +/* Ensure the requested element is within the bounds of the vector */ \ + if( elem >= nel || elem < 0 ) { \ + if( astOK ) { \ + astError( AST__MPVIN, "astMapGetElem(KeyMap): Illegal " \ + "zero-based vector index %d supplied for KeyMap " \ + "entry '%s' - the vector has %d elements.", status, \ + elem, key, nel ); \ + } \ +\ +/* Get a pointer to the requested raw value. */ \ + } else if( raw ) { \ + raw = (char *) raw + elem*raw_size; \ +\ +/* Convert the requested value, storing the result in the supplied buffer. \ + Report an error if conversion is not possible. */ \ + if( !ConvertValue( raw, raw_type, value, Itype, status ) && astOK ){ \ + astError( AST__MPGER, "astMapGetElem" #X "(%s): The value of " \ + "element %d of KeyMap key \"%s\" cannot be read using " \ + "the requested data type.", status, astGetClass( this ), \ + elem + 1, key ); \ + } \ + } \ +\ +/* If the KeyError attribute is non-zero, report an error if the key is not \ + found */ \ + } else if( astGetKeyError( this ) && astOK ) { \ + astError( AST__MPKER, "astMapGetElem" #X "(%s): No value was found for " \ + "%s in the supplied KeyMap.", status, astGetClass( this ), \ + key ); \ + } \ +\ +/* If an error occurred,return zero. */ \ + if( !astOK ) result = 0; \ +\ +/* Return the result.*/ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type (except C which is done differently). */ +MAKE_MAPGETELEM(I,int,AST__INTTYPE) +MAKE_MAPGETELEM(D,double,AST__DOUBLETYPE) +MAKE_MAPGETELEM(F,float,AST__FLOATTYPE) +MAKE_MAPGETELEM(A,AstObject *,AST__OBJECTTYPE) +MAKE_MAPGETELEM(P,void *,AST__POINTERTYPE) +MAKE_MAPGETELEM(S,short int,AST__SINTTYPE) +MAKE_MAPGETELEM(B,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPGETELEM + + +static int MapGetElemC( AstKeyMap *this, const char *skey, int l, int elem, + char *value, int *status ) { +/* +* Name: +* MapGetElemC + +* Purpose: +* Get a single element of a vector value from a KeyMap. + +* Type: +* Private member function. + +* Synopsis: +* #include "ast.h" +* int MapGetElemC( AstKeyMap *this, const char *key, int l, int elem, +* char *value, int *status ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is the implementation of astMapGetElem for = "C". We +* cannot use the MAKE_MAPGETELEM macro for this because the string +* version of this function has an extra parameter giving the maximum +* length of each string which can be stored in the supplied buffer. + +* Parameters: +* (see astMapGetElem) +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + const char *cvalue; /* Pointer to converted string */ + int itab; /* Index of hash table element to use */ + int nel; /* Number of elements in raw vector */ + int raw_type; /* Data type of stored value */ + int result; /* Returned flag */ + size_t raw_size; /* Size of a single raw value */ + unsigned long hash; /* Full width hash value */ + void *raw; /* Pointer to stored value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGetElemC", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + result = 1; + +/* Get the address of the first raw value, and its data type. Also get + the size of each element of the vector. */ + nel = mapentry->nel; + raw_type = mapentry->type; + if( raw_type == AST__INTTYPE ){ + raw_size = sizeof( int ); + if( nel == 0 ) { + raw = &( ((Entry0I *)mapentry)->value ); + } else { + raw = ((Entry1I *)mapentry)->value; + } + + } else if( raw_type == AST__POINTERTYPE ){ + raw_size = sizeof( void * ); + if( nel == 0 ) { + raw = &( ((Entry0P *)mapentry)->value ); + } else { + raw = ((Entry1P *)mapentry)->value; + } + + } else if( raw_type == AST__DOUBLETYPE ){ + raw_size = sizeof( double ); + if( nel == 0 ) { + raw = &( ((Entry0D *)mapentry)->value ); + } else { + raw = ((Entry1D *)mapentry)->value; + } + + } else if( raw_type == AST__SINTTYPE ){ + raw_size = sizeof( short int ); + if( nel == 0 ) { + raw = &( ((Entry0S *)mapentry)->value ); + } else { + raw = ((Entry1S *)mapentry)->value; + } + + } else if( raw_type == AST__BYTETYPE ){ + raw_size = sizeof( unsigned char ); + if( nel == 0 ) { + raw = &( ((Entry0B *)mapentry)->value ); + } else { + raw = ((Entry1B *)mapentry)->value; + } + + } else if( raw_type == AST__FLOATTYPE ){ + raw_size = sizeof( float ); + if( nel == 0 ) { + raw = &( ((Entry0F *)mapentry)->value ); + } else { + raw = ((Entry1F *)mapentry)->value; + } + + } else if( raw_type == AST__STRINGTYPE ){ + raw_size = sizeof( const char * ); + if( nel == 0 ) { + raw = &( ((Entry0C *)mapentry)->value ); + } else { + raw = ((Entry1C *)mapentry)->value; + } + + } else if( raw_type == AST__OBJECTTYPE ){ + raw_size = sizeof( AstObject * ); + if( nel == 0 ) { + raw = &( ((Entry0A *)mapentry)->value ); + } else { + raw = ((Entry1A *)mapentry)->value; + } + + } else if( raw_type == AST__UNDEFTYPE ){ + raw = NULL; + + } else { + raw_size = 0; + raw = NULL; + astError( AST__INTER, "astMapGetElemC(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + raw_type ); + } + +/* Treat scalars as single-value vectors. */ + if( nel == 0 ) nel = 1; + +/* Ensure the requested element is within the bounds of the vector */ + if( elem >= nel || elem < 0 ) { + if( astOK ) { + astError( AST__MPVIN, "astMapGetElemC(KeyMap): Illegal vector " + "index %d supplied for KeyMap entry '%s' - should be " + "in the range 1 to %d.", status, elem + 1, key, nel + 1 ); + } + +/* Get a pointer to the requested raw value. */ + } else if( raw ){ + raw = (char *) raw + elem*raw_size; + +/* Convert the value, storing the result in the supplied buffer. Report an + error if conversion is not possible. */ + if( !ConvertValue( raw, raw_type, &cvalue, AST__STRINGTYPE, status ) && astOK ){ + astError( AST__MPGER, "astMapGetElemC(%s): The value of " + "element %d of KeyMap key \"%s\" cannot be read using " + "the requested data type.", status,astGetClass( this ), + elem + 1, key ); + +/* If succesful, copy the string into the supplied buffer, or as much of + it as will fit. Leave room for a trailing null character. */ + } else { + strncpy( value, cvalue, l - 1 ); + value[ l - 1 ] = 0; + } + } + +/* If the KeyError attribute is non-zero, report an error if the key is not + found */ + } else if( astGetKeyError( this ) && astOK ) { + astError( AST__MPKER, "astMapGetElemC(%s): No value was found for " + "%s in the supplied KeyMap.", status, astGetClass( this ), + key ); + } + +/* If an error occurred,return zero. */ + if( !astOK ) result = 0; + +/* Return the result.*/ + return result; +} + +int astMapGetElemAId_( AstKeyMap *this, const char *skey, int elem, + AstObject **value, int *status ) { +/* +* Name: +* astMapGetElemAId_ + +* Purpose: +* Get a single element of a vector of AstObject pointers from a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "ast.h" +* int astMapGetElemA( AstKeyMap *this, const char *key, int elem, +* AstObject **value ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is the public implementation of the astMapGetElemA function +* It is identical to astMapGetElemA_ except that an ID value is returned +* via the "value" parameter instead of a true C pointer. This is required +* because this conversion cannot be performed by the macro that invokes +* the function. + +* Parameters: +* (see astMapGet1) + +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + AstObject *avalue; /* Pointer to AstObject */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + int itab; /* Index of hash table element to use */ + int nel; /* Number of elements in raw vector */ + int raw_type; /* Data type of stored value */ + int result; /* Returned flag */ + size_t raw_size; /* Size of a single raw value */ + unsigned long hash; /* Full width hash value */ + void *raw; /* Pointer to stored value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapGetElemA", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + result = 1; + +/* Get the address of the first raw value, and its data type. Also get + the size of each element of the vector. */ + nel = mapentry->nel; + raw_type = mapentry->type; + if( raw_type == AST__INTTYPE ){ + raw_size = sizeof( int ); + if( nel == 0 ) { + raw = &( ((Entry0I *)mapentry)->value ); + } else { + raw = ((Entry1I *)mapentry)->value; + } + + } else if( raw_type == AST__SINTTYPE ){ + raw_size = sizeof( short int ); + if( nel == 0 ) { + raw = &( ((Entry0S *)mapentry)->value ); + } else { + raw = ((Entry1S *)mapentry)->value; + } + + } else if( raw_type == AST__BYTETYPE ){ + raw_size = sizeof( unsigned char ); + if( nel == 0 ) { + raw = &( ((Entry0B *)mapentry)->value ); + } else { + raw = ((Entry1B *)mapentry)->value; + } + + } else if( raw_type == AST__DOUBLETYPE ){ + raw_size = sizeof( double ); + if( nel == 0 ) { + raw = &( ((Entry0D *)mapentry)->value ); + } else { + raw = ((Entry1D *)mapentry)->value; + } + + } else if( raw_type == AST__POINTERTYPE ){ + raw_size = sizeof( void * ); + if( nel == 0 ) { + raw = &( ((Entry0P *)mapentry)->value ); + } else { + raw = ((Entry1P *)mapentry)->value; + } + + } else if( raw_type == AST__FLOATTYPE ){ + raw_size = sizeof( float ); + if( nel == 0 ) { + raw = &( ((Entry0F *)mapentry)->value ); + } else { + raw = ((Entry1F *)mapentry)->value; + } + + } else if( raw_type == AST__STRINGTYPE ){ + raw_size = sizeof( const char * ); + if( nel == 0 ) { + raw = &( ((Entry0C *)mapentry)->value ); + } else { + raw = ((Entry1C *)mapentry)->value; + } + + } else if( raw_type == AST__OBJECTTYPE ){ + raw_size = sizeof( AstObject * ); + if( nel == 0 ) { + raw = &( ((Entry0A *)mapentry)->value ); + } else { + raw = ((Entry1A *)mapentry)->value; + } + + } else if( raw_type == AST__UNDEFTYPE ){ + raw = NULL; + + } else { + raw_size = 0; + raw = NULL; + astError( AST__INTER, "astMapGetElemA(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + raw_type ); + } + +/* Treat scalars as single-value vectors. */ + if( nel == 0 ) nel = 1; + +/* Ensure the requested element is within the bounds of the vector */ + if( elem >= nel || elem < 0 ) { + if( astOK ) { + astError( AST__MPVIN, "astMapGetElemA(KeyMap): Illegal vector " + "index %d supplied for KeyMap entry '%s' - should be " + "in the range 1 to %d.", status, elem + 1, key, nel + 1 ); + } + +/* Get a pointer to the requested raw value. */ + } else if( raw ){ + raw = (char *) raw + elem*raw_size; + +/* Convert the value, storing the result in the supplied buffer. Report an + error if conversion is not possible. */ + if( !ConvertValue( raw, raw_type, &avalue, AST__OBJECTTYPE, status ) && astOK ){ + astError( AST__MPGER, "astMapGetElemA(%s): The value of " + "element %d of KeyMap key \"%s\" cannot be read using " + "the requested data type.", status,astGetClass( this ), + elem + 1, key ); + +/* If succesful, return an ID value for the Object. */ + } else { + *value = avalue ? astMakeId( avalue ) : NULL; + } + } + +/* If the KeyError attribute is non-zero, report an error if the key is not + found */ + } else if( astGetKeyError( this ) && astOK ) { + astError( AST__MPKER, "astMapGetElemA(%s): No value was found for " + "%s in the supplied KeyMap.", status, astGetClass( this ), + key ); + } + +/* If an error occurred,return zero. */ + if( !astOK ) result = 0; + +/* Return the result.*/ + return result; +} + +static int MapDefined( AstKeyMap *this, const char *skey, int *status ) { +/* +*++ +* Name: +c astMapDefined +f AST_MAPDEFINED + +* Purpose: +* Check if a KeyMap contains a defined value for a key. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c int astMapDefined( AstKeyMap *this, const char *key ); +f RESULT = AST_MAPDEFINED( THIS, KEY, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function checks to see if a KeyMap contains a defined value for +* a given key. If the key is present in the KeyMap but has an +* undefined value it returns +c zero (unlike astMapHasKey which would return non-zero). +f .FALSE. (unlike AST_MAPHASKEY which would return .TRUE.). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the value to be retrieved. Trailing +* spaces are ignored. The supplied string is converted to upper +* case before use if the KeyCase attribute is currently set to zero. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapDefined() +f AST_MAPDEFINED = LOGICAL +c A non-zero value +f .TRUE. +* is returned if the requested key name is present in the KeyMap +* and has a defined value. + +*-- +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + int itab; /* Index of hash table element to use */ + int result; /* Returned flag */ + unsigned long hash; /* Full width hash value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapDefined", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + +/* Set the result depending on the entry data type. */ + if( mapentry->type == AST__UNDEFTYPE ){ + result = 0; + } else { + result = 1; + } + +/* If the KeyError attribute is non-zero, report an error if the key is not + found */ + } else if( astGetKeyError( this ) && astOK ) { + astError( AST__MPKER, "astMapDefined(%s): No value was found for " + "%s in the supplied KeyMap.", status, astGetClass( this ), + key ); + } + +/* If an error occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result.*/ + return result; +} + +static int MapHasKey( AstKeyMap *this, const char *skey, int *status ) { +/* +*++ +* Name: +c astMapHasKey +f AST_MAPHASKEY + +* Purpose: +* Check if an entry with a given key exists in a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c int astMapHasKey( AstKeyMap *this, const char *key ) +f RESULT = AST_MAPHASKEY( THIS, KEY, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns a flag indicating if the KeyMap contains an +* entry with the given key. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the KeyMap entry. Trailing spaces are +* ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapHasKey() +f AST_MAPHASKEY = LOGICAL +c Non-zero if the key was found, and zero otherwise. +f .TRUE. if the key was found, and .FALSE. otherwise. + +* Notes: +c - A non-zero function value +f - .TRUE. +* is returned if the key exists but has an undefined value (that is, +* the returned value does not depend on whether the entry has a +* defined value or not). See also +c astMapDefined, which returns zero in such a case. +f AST_MAPDEFINED, which returns zero in such a case. +* - A function value of +c zero +f .FALSE. +* will be returned if an error has already occurred, or if this +* function should fail for any reason. + +*-- +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to entry in linked list */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + int itab; /* Index of hash table element to use */ + int result; /* Returned value */ + unsigned long hash; /* Full width hash value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapHasKey", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Set a non-zero return value if the key was found. */ + if( mapentry ) result = 1; + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; + +} + +static void MapRemove( AstKeyMap *this, const char *skey, int *status ) { +/* +*++ +* Name: +c astMapRemove +f AST_MAPREMOVE + +* Purpose: +* Removed a named entry from a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c void astMapRemove( AstKeyMap *this, const char *key ) +f CALL AST_MAPREMOVE( THIS, KEY, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +c This function +f This routine +* removes a named entry from a KeyMap. It returns without action if the +* KeyMap does not contain the specified key. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the value to be retrieved. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +f STATUS = INTEGER (Given and Returned) +f The global status. +*-- +*/ + +/* Local Variables: */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + int itab; /* Index of hash table element to use */ + unsigned long hash; /* Full width hash value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapRemove", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry and remove it. */ + (void) FreeMapEntry( RemoveTableEntry( this, itab, key, status ), status ); +} + +static void MapRename( AstKeyMap *this, const char *soldkey, const char *snewkey, + int *status ) { +/* +*++ +* Name: +c astMapRename +f AST_MAPRENAME + +* Purpose: +* Rename an existing KeyMap entry. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c void astMapRename( AstKeyMap *this, const char *oldkey, const char *newkey ) +f CALL AST_MAPRENAME( THIS, OLDKEY, NEWKEY, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +c This function +f This routine +* associated a new key with an existing entry in a KeyMap. It returns +* without action if the oldkey does not exist in the KeyMap. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c oldkey +f OLDKEY = CHARACTER * ( * ) (Given) +* The character string identifying the entry to be renamed. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +c newkey +f NEKEY = CHARACTER * ( * ) (Given) +* The new character string to associated with the renamed entry. +* Trailing spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +f STATUS = INTEGER (Given and Returned) +f The global status. +*-- +*/ + +/* Local Variables: */ + AstMapEntry *entry; /* Pointer to the entry being renamed */ + AstMapEntry *oldent; /* Pointer to old entry with new name */ + const char *oldkey; /* Pointer to key string to use */ + char oldkeybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + const char *newkey; /* Pointer to key string to use */ + char newkeybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + char *p; /* Pointer to next key character */ + int itab; /* Index of hash table element to use */ + int keylen; /* Length of supplied key string */ + int keymember; /* Identifier for new key */ + int there; /* Did the entry already exist in the KeyMap? */ + unsigned long hash; /* Full width hash value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Convert the supplied keys to upper case if required. */ + oldkey = ConvertKey( this, soldkey, oldkeybuf, AST__MXKEYLEN + 1, + "astMapRename", status ); + newkey = ConvertKey( this, snewkey, newkeybuf, AST__MXKEYLEN + 1, + "astMapRename", status ); + +/* Do nothing if the keys are the same. */ + if( strcmp( oldkey, newkey ) ){ + +/* Use the hash function to determine the element of the hash table in + which the old key will be stored. */ + itab = HashFun( oldkey, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. Remove it + from the list, but do not free it. */ + entry = RemoveTableEntry( this, itab, oldkey, status ); + +/* Skip rest if the key was not found. */ + if( entry ) { + +/* Store the new key string, and terminate it to exclude any trailing + spaces. */ + keylen = strlen( newkey ); + entry->key = astStore( (void *) entry->key, newkey, keylen + 1 ); + if( astOK ) { + p = (char *) entry->key + keylen; + while( --p >= entry->key ) { + if( *p == ' ' ) { + *p = 0; + } else { + break; + } + } + } + +/* Use the hash function to determine the element of the hash table in + which to store the entry with its new key. */ + itab = HashFun( entry->key, this->mapsize - 1, &(entry->hash), status ); + +/* Remove and free any existing entry with the given key from the table + element. */ + oldent = RemoveTableEntry( this, itab, entry->key, status ); + if( oldent ) { + keymember = oldent->keymember; + oldent = FreeMapEntry( oldent, status ); + there = 1; + } else { + keymember = -1; + there = 0; + } + +/* If the KeyMap is locked we report an error if an attempt is made to + introduce a new key. */ + if( !there && astGetMapLocked( this ) ) { + astError( AST__BADKEY, "astMapRename(%s): Failed to rename item " + "\"%s\" in a KeyMap to \"%s\": \"%s\" is not a known " + "item.", status, astGetClass( this ), oldkey, newkey, + newkey ); + } + +/* If all has gone OK, store the renamed entry at the head of the linked list + associated with the selected table entry. */ + if( astOK ) { + entry = AddTableEntry( this, itab, entry, keymember, status ); + +/* If anything went wrong, try to delete the renamed entry. */ + } else { + entry = FreeMapEntry( entry, status ); + } + } + } +} + +static int MapSize( AstKeyMap *this, int *status ) { +/* +*++ +* Name: +c astMapSize +f AST_MAPSIZE + +* Purpose: +* Get the number of entries in a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c int astMapSize( AstKeyMap *this ) +f RESULT = AST_MAPSIZE( THIS, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns the number of entries in a KeyMap. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapSize() +f AST_MAPSIZE = INTEGER +* The number of entries in the KeyMap. + +* Notes: +* - A function value of zero will be returned if an error has already +* occurred, or if this function should fail for any reason. + +*-- +*/ + +/* Local Variables: */ + int itab; /* Index of hash table element to use */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Add up the number of entries in all elements of the hash table. */ + for( itab = 0; itab < this->mapsize; itab++ ) result += this->nentry[ itab ]; + +/* Return the result. */ + return result; + +} + +static int MapLenC( AstKeyMap *this, const char *skey, int *status ) { +/* +*++ +* Name: +c astMapLenC +f AST_MAPLENC + +* Purpose: +* Get the number of characters in a character entry in a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c int astMapLenC( AstKeyMap *this, const char *key ) +f RESULT = AST_MAPLENC( THIS, KEY, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns the minimum length which a character variable +* which must have in order to be able to store a specified entry in +* the supplied KeyMap. If the named entry is a vector entry, then the +* returned value is the length of the longest element of the vector +* value. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the KeyMap entry. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapLenC() +f AST_MAPLENC = INTEGER +* The length (i.e. number of characters) of the longest formatted +* value associated with the named entry. +c This does not include the trailing null character. + +* Notes: +* - A function value of zero will be returned without error if the +* named entry cannot be formatted as a character string. +* - A function value of zero will be returned if an error has already +* occurred, or if this function should fail for any reason. + +*-- +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + int i; /* Element index */ + int itab; /* Index of hash table element to use */ + int l; /* Length of formatted vector element */ + int nel; /* Number of elements in raw vector */ + int raw_type; /* Data type of stored value */ + int result; /* Returned value */ + size_t raw_size; /* Size of a single raw value */ + unsigned long hash; /* Full width hash value */ + void *raw; /* Pointer to stored value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapLenC", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + +/* Get the address of the first raw value, and its data type. Also get + the size of each element of the vector. */ + nel = mapentry->nel; + raw_type = mapentry->type; + if( raw_type == AST__INTTYPE ){ + raw_size = sizeof( int ); + if( nel == 0 ) { + raw = &( ((Entry0I *)mapentry)->value ); + } else { + raw = ((Entry1I *)mapentry)->value; + } + + } else if( raw_type == AST__POINTERTYPE ){ + raw_size = sizeof( void * ); + if( nel == 0 ) { + raw = &( ((Entry0P *)mapentry)->value ); + } else { + raw = ((Entry1P *)mapentry)->value; + } + + } else if( raw_type == AST__DOUBLETYPE ){ + raw_size = sizeof( double ); + if( nel == 0 ) { + raw = &( ((Entry0D *)mapentry)->value ); + } else { + raw = ((Entry1D *)mapentry)->value; + } + + } else if( raw_type == AST__SINTTYPE ){ + raw_size = sizeof( short int ); + if( nel == 0 ) { + raw = &( ((Entry0S *)mapentry)->value ); + } else { + raw = ((Entry1S *)mapentry)->value; + } + + } else if( raw_type == AST__BYTETYPE ){ + raw_size = sizeof( unsigned char ); + if( nel == 0 ) { + raw = &( ((Entry0B *)mapentry)->value ); + } else { + raw = ((Entry1B *)mapentry)->value; + } + + } else if( raw_type == AST__FLOATTYPE ){ + raw_size = sizeof( float ); + if( nel == 0 ) { + raw = &( ((Entry0F *)mapentry)->value ); + } else { + raw = ((Entry1F *)mapentry)->value; + } + + } else if( raw_type == AST__STRINGTYPE ){ + raw_size = sizeof( const char * ); + if( nel == 0 ) { + raw = &( ((Entry0C *)mapentry)->value ); + } else { + raw = ((Entry1C *)mapentry)->value; + } + + } else if( raw_type == AST__OBJECTTYPE ){ + raw_size = sizeof( AstObject * ); + if( nel == 0 ) { + raw = &( ((Entry0A *)mapentry)->value ); + } else { + raw = ((Entry1A *)mapentry)->value; + } + + } else if( raw_type == AST__UNDEFTYPE ){ + raw_size = 0; + raw = NULL; + + } else { + raw_size = 0; + raw = NULL; + astError( AST__INTER, "astMapLenC(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + raw_type ); + } + +/* Treat scalars as single-value vectors. */ + if( nel == 0 ) nel = 1; + +/* Skip undefined values. */ + if( raw ) { + +/* Initialise the maximum length of any formatted value in the entry. */ + result= 0; + +/* Loop round all values in the vector. */ + for( i = 0; i < nel && astOK; i++ ) { + +/* Go through the motions of formatting the value. We do not actually + need the formatted string (just its length) so we provide a NULL pointer + for the output buffer. The entry is ignored if it cannot be formatted. + Note, the length returned by ConvertValue includes the terminating null, + so decrement it first. */ + l = ConvertValue( raw, raw_type, NULL, AST__STRINGTYPE, status ); + if( --l > result ) result = l; + +/* Increment the pointer to the next raw value. */ + raw = (char *) raw + raw_size; + } + } + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; + +} + +static int MapLength( AstKeyMap *this, const char *skey, int *status ) { +/* +*++ +* Name: +c astMapLength +f AST_MAPLENGTH + +* Purpose: +* Get the vector length of an entry in a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c int astMapLength( AstKeyMap *this, const char *key ) +f RESULT = AST_MAPLENGTH( THIS, KEY, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns the vector length of a named entry in a KeyMap, +* (that is, how many values are associated with the entry). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the KeyMap entry. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapLength() +f AST_MAPLENGTH = INTEGER +* The length of the entry. One for a scalar, greater than one for +* a vector. A value of zero is returned if the KeyMap does not +* contain the named entry. + +* Notes: +* - A function value of zero will be returned if an error has already +* occurred, or if this function should fail for any reason. + +*-- +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to entry in linked list */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + int itab; /* Index of hash table element to use */ + int result; /* Returned value */ + unsigned long hash; /* Full width hash value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapLength", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Skip rest if the key was not found. */ + if( mapentry ) { + +/* Store the netry length */ + result = mapentry->nel; + +/* Return 1 for a scalar. */ + if( result == 0 ) result = 1; + + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; + +} + +/* +*++ +* Name: +c astMapPutElem +f AST_MAPPUTELEM + +* Purpose: +* Put a value into an element of a vector value in a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "ast.h" +c void astMapPutElem( AstKeyMap *this, const char *key, int elem, +c type *value ) +f CALL AST_MAPPUTELEM( THIS, KEY, ELEM, VALUE, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This is a set of functions for storing a value in a single element of +* a vector value in a KeyMap. You should replace in the generic +* function name +c astMapPutElem +f AST_MAPPUTELEM +* by an appropriate 1-character type code (see the "Data Type Codes" +* section below for the code appropriate to each supported data type). +* The supplied value is converted from the data type indicated by +* to the data type of the KeyMap entry before being stored (an error +* is reported if it is not possible to convert the value to the +* required data type). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the value to be retrieved. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +c elem +f ELEM = INTEGER (Given) +* The index of the vector element to modify, starting at +c zero. +f one. +c value +f VALUE = type (Given) +* The value to store. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* KeyMap +* If the +c "elem" +f ELEM +* index is outside the range of the vector, the length of +* the vector will be increased by one element and the supplied +* value will be stored at the end of the vector in the new element. +* Table +* If the +c "elem" +f ELEM +* index is outside the range of the vector, an error will be +* reported. The number of elements in each cell of a column is +* specified when the column is created using +c astAddColumn. +f AST_ADDCOLUMN. + +* Notes: +* - If the entry originally holds a scalar value, it will be treated +* like a vector entry of length 1. +* - If the specified key cannot be found in the given KeyMap, or is +* found but has an undefined value, a new +* vector entry with the given name, and data type implied by , is +* created and the supplied value is stored in its first entry. + +* Data Type Codes: +* To select the appropriate +c function, you should replace in the generic function name +c astMapPutElem +f routine, you should replace in the generic routine name +f AST_MAPPUTELEM +* with a 1-character data type code, so as to match the data type type +* of the data you are processing, as follows: +c - D: double +c - F: float +c - I: int +c - C: "const" pointer to null terminated character string +c - A: Pointer to AstObject +c - P: Generic "void *" pointer +c - S: short int +c - B: Unsigned byte (i.e. char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - C: CHARACTER +f - A: INTEGER used to identify an AstObject +f - S: INTEGER*2 (short integer) +f - B: BYTE (unsigned) +* +c For example, astMapPutElemD would be used to put a "double" value, while +c astMapPutElemI would be used to put an "int" value, etc. For D or I, the +c supplied "value" parameter should be a double or int. For +c C, the supplied "value" parameter should be a pointer to a character +c string. For A, the supplied "value" parameter should be an AstObject +c pointer. +f For example, AST_MAPPUTELEMD would be used to put a DOUBLE PRECISION +f value, while AST_MAPPUTELEMI would be used to put an INTEGER value, etc. + +*-- +*/ +/* Define a macro to implement the function for a specific data type +(excluding "C" since that needs an extra parameter). */ +#define MAKE_MAPPUTELEM(X,Xtype,Itype) \ +static void MapPutElem##X( AstKeyMap *this, const char *skey, int elem, \ + Xtype value, int *status ) { \ +\ +/* Local Variables: */ \ + AstMapEntry *mapentry; /* Pointer to parent MapEntry structure */ \ + const char *key; /* Pointer to key string to use */ \ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ \ + int itab; /* Index of hash table element to use */ \ + int nel; /* Number of elements in raw vector */ \ + int new; /* Was a new uninitialised element created? */ \ + int raw_type; /* Data type of stored value */ \ + size_t raw_size; /* Size of a single raw value */ \ + unsigned long hash; /* Full width hash value */ \ + void *raw; /* Pointer to stored value */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Perform any necessary checks on the supplied value to be stored. */ \ + CHECK_##X \ +\ +/* Convert the supplied key to upper case if required. */ \ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapPutElem" #X, \ + status ); \ +\ +/* Use the hash function to determine the element of the hash table in \ + which the key will be stored. */ \ + itab = HashFun( key, this->mapsize - 1, &hash, status ); \ +\ +/* Search the relevent table entry for the required MapEntry. */ \ + mapentry = SearchTableEntry( this, itab, key, status ); \ +\ +/* If the key was not found, or was found but has an undefined value, create \ + a new one with a single element, \ + and store the supplied value in it. */ \ + if( !mapentry || mapentry->type == AST__UNDEFTYPE ) { \ + astMapPut1##X( this, key, 1, &value, NULL ); \ +\ +/* If the key was found.... */ \ + } else { \ +\ +/* Get the current length of the vector (0=>scalar), and the data type. */ \ + nel = mapentry->nel; \ + raw_type = mapentry->type; \ +\ +/* Do each data type in turn. */ \ + if( raw_type == AST__INTTYPE ){ \ +\ +/* If the existing entry is scalar, create a new vector entry with the \ + same name, value, data type and comment. Then get a pointer to the new \ + entry, and indicate that we now have a vector entry of length 1. */ \ + if( nel == 0 ) { \ + astMapPut1I( this, key, 1, &( ((Entry0I *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ +\ +/* Get the address of the first raw value in the vector. Also get \ + the size of each element of the vector. */ \ + raw = ((Entry1I *)mapentry)->value; \ + raw_size = sizeof( int ); \ +\ +/* Handle other data type in the same way. */ \ + } else if( raw_type == AST__SINTTYPE ){ \ + if( nel == 0 ) { \ + astMapPut1S( this, key, 1, &( ((Entry0S *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ + raw = ((Entry1S *)mapentry)->value; \ + raw_size = sizeof( short int ); \ +\ + } else if( raw_type == AST__BYTETYPE ){ \ + if( nel == 0 ) { \ + astMapPut1B( this, key, 1, &( ((Entry0B *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ + raw = ((Entry1B *)mapentry)->value; \ + raw_size = sizeof( unsigned char ); \ +\ + } else if( raw_type == AST__DOUBLETYPE ){ \ + if( nel == 0 ) { \ + astMapPut1D( this, key, 1, &( ((Entry0D *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ + raw = ((Entry1D *)mapentry)->value; \ + raw_size = sizeof( double ); \ +\ + } else if( raw_type == AST__POINTERTYPE ){ \ + if( nel == 0 ) { \ + astMapPut1P( this, key, 1, &( ((Entry0P *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ + raw = ((Entry1P *)mapentry)->value; \ + raw_size = sizeof( void * ); \ +\ + } else if( raw_type == AST__FLOATTYPE ){ \ + if( nel == 0 ) { \ + astMapPut1F( this, key, 1, &( ((Entry0F *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ + raw = ((Entry1F *)mapentry)->value; \ + raw_size = sizeof( float ); \ +\ + } else if( raw_type == AST__STRINGTYPE ){ \ + if( nel == 0 ) { \ + astMapPut1C( this, key, 1, &( ((Entry0C *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ + raw = ((Entry1C *)mapentry)->value; \ + raw_size = sizeof( const char * ); \ +\ + } else if( raw_type == AST__OBJECTTYPE ){ \ + if( nel == 0 ) { \ + astMapPut1A( this, key, 1, &( ((Entry0A *)mapentry)->value ), \ + mapentry->comment ); \ + mapentry = SearchTableEntry( this, itab, key, status ); \ + nel = 1; \ + } \ + raw = ((Entry1A *)mapentry)->value; \ + raw_size = sizeof( AstObject * ); \ +\ + } else { \ + raw_size = 0; \ + raw = NULL; \ + astError( AST__INTER, "astMapPutElem(KeyMap): Illegal map entry " \ + "data type %d encountered (internal AST programming " \ + "error).", status, raw_type ); \ + } \ +\ +/* If the requested element is outside the bounds of the vector, extend \ + the vector by one element. */ \ + new = ( elem >= nel || elem < 0 ); \ + if( new ) { \ + elem = nel++; \ + raw = astGrow( raw, nel, raw_size ); \ + if( astOK ) { \ + mapentry->nel = nel; \ + if( raw_type == AST__INTTYPE ){ \ + ((Entry1I *)mapentry)->value = (int *) raw; \ + } else if( raw_type == AST__SINTTYPE ){ \ + ((Entry1S *)mapentry)->value = (short int *) raw; \ + } else if( raw_type == AST__BYTETYPE ){ \ + ((Entry1B *)mapentry)->value = (unsigned char *) raw; \ + } else if( raw_type == AST__DOUBLETYPE ){ \ + ((Entry1D *)mapentry)->value = (double *) raw; \ + } else if( raw_type == AST__POINTERTYPE ){ \ + ((Entry1P *)mapentry)->value = (void *) raw; \ + } else if( raw_type == AST__FLOATTYPE ){ \ + ((Entry1F *)mapentry)->value = (float *) raw; \ + } else if( raw_type == AST__STRINGTYPE ){ \ + ((Entry1C *)mapentry)->value = (const char **) raw; \ + } else if( raw_type == AST__OBJECTTYPE ){ \ + ((Entry1A *)mapentry)->value = (AstObject **) raw; \ + } \ + } \ + } \ +\ +/* Get a pointer to the requested element. */ \ + if( astOK ) { \ + raw = (char *) raw + elem*raw_size; \ +\ +/* Free any memory used by the value already in the requested element. */ \ + if( ! new ) { \ + if( raw_type == AST__STRINGTYPE ){ \ + char **cp = (char **) raw; \ + *cp = astFree( *cp ); \ + } else if( raw_type == AST__OBJECTTYPE ){ \ + AstObject **op = (AstObject **) raw; \ + if( *op ) *op = astAnnul( *op ); \ + } \ + } \ +\ +/* Convert the supplied value, storing the result in the requested element. \ + Report an error if conversion is not possible. */ \ + if( !ConvertValue( &value, Itype, raw, raw_type, status ) && astOK ){ \ + astError( AST__MPPER, "astMapPutElem" #X "(%s): The supplied " \ + "value cannot be converted to the data type of " \ + "KeyMap key \"%s\".", status, astGetClass( this ), \ + key ); \ + } \ + } \ + } \ +} + +/* Define macros which perform any necessary checks on the supplied value + to be stored. For Object entries, check that we are not adding a KeyMap + which already contains "this". This avoids circular dependencies. + Other types do not need any checks. */ +#define CHECK_A CheckCircle( this, value, "astMapPutElemA", status ); +#define CHECK_I +#define CHECK_B +#define CHECK_S +#define CHECK_D +#define CHECK_F +#define CHECK_C +#define CHECK_P + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPPUTELEM(I,int,AST__INTTYPE) +MAKE_MAPPUTELEM(D,double,AST__DOUBLETYPE) +MAKE_MAPPUTELEM(F,float,AST__FLOATTYPE) +MAKE_MAPPUTELEM(A,AstObject *,AST__OBJECTTYPE) +MAKE_MAPPUTELEM(P,void *,AST__POINTERTYPE) +MAKE_MAPPUTELEM(C,const char *,AST__STRINGTYPE) +MAKE_MAPPUTELEM(S,short int,AST__SINTTYPE) +MAKE_MAPPUTELEM(B,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPPUTELEM +#undef CHECK_A +#undef CHECK_I +#undef CHECK_B +#undef CHECK_S +#undef CHECK_D +#undef CHECK_F +#undef CHECK_C +#undef CHECK_P + + +static int MapType( AstKeyMap *this, const char *skey, int *status ) { +/* +*++ +* Name: +c astMapType +f AST_MAPTYPE + +* Purpose: +* Get the data type of an entry in a KeyMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "keymap.h" +c int astMapType( AstKeyMap *this, const char *key ) +f RESULT = AST_MAPTYPE( THIS, KEY, STATUS ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns a value indicating the data type of a +* named entry in a KeyMap. This is the data type which was used when the +* entry was added to the KeyMap. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the KeyMap. +c key +f KEY = CHARACTER * ( * ) (Given) +* The character string identifying the KeyMap entry. Trailing +* spaces are ignored. +* The supplied string is converted to upper case before use if the +* KeyCase attribute is currently set to zero. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapType() +f AST_MAPTYPE = INTEGER +* One of AST__INTTYPE (for integer), AST__SINTTYPE (for +c short int), +f INTEGER*2), +* AST__BYTETYPE (for unsigned bytes +c - i.e. unsigned chars +* ) AST__DOUBLETYPE (for double +* precision floating point), AST__FLOATTYPE (for single +* precision floating point), AST__STRINGTYPE (for character string), +* AST__OBJECTTYPE (for AST Object pointer), AST__POINTERTYPE (for +* arbitrary C pointer) or AST__UNDEFTYPE (for undefined values +* created by +c astMapPutU). +f AST_MAPPUTU). +* AST__BADTYPE is returned if the supplied key is not found in the KeyMap. + +* Notes: +* - A function value of AST__BADTYPE will be returned if an error has +* already occurred, or if this function should fail for any reason. + +*-- +*/ + +/* Local Variables: */ + AstMapEntry *mapentry; /* Pointer to entry in linked list */ + const char *key; /* Pointer to key string to use */ + char keybuf[ AST__MXKEYLEN + 1 ]; /* Buffer for upper cas key */ + int itab; /* Index of hash table element to use */ + int result; /* Returned value */ + unsigned long hash; /* Full width hash value */ + +/* Initialise */ + result = AST__BADTYPE; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Convert the supplied key to upper case if required. */ + key = ConvertKey( this, skey, keybuf, AST__MXKEYLEN + 1, "astMapType", + status ); + +/* Use the hash function to determine the element of the hash table in + which the key will be stored. */ + itab = HashFun( key, this->mapsize - 1, &hash, status ); + +/* Search the relevent table entry for the required MapEntry. */ + mapentry = SearchTableEntry( this, itab, key, status ); + +/* Store the type if found. */ + if( mapentry ) result = mapentry->type; + +/* If an error has occurred, return zero. */ + if( !astOK ) result = AST__BADTYPE; + +/* Return the result. */ + return result; + +} + +static const char *MapIterate( AstKeyMap *this, int reset, int *status ) { +/* +*+ +* Name: +* astMapIterate + +* Purpose: +* Iterate through the keys in a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* const char *astMapIterate( AstKeyMap *this, int reset, int *status ) + +* Class Membership: +* KeyMap method. + +* Description: +* If "reset" is non-zero, this function returns a pointer to a string +* holding the first key in the KeyMap. On subsequent invocation (if +* reset is zero) it returns a pointer to the next key in the KeyMap. The +* context is stored within the KeyMap structure, so calls on different +* KeyMaps can be mixed. +* +* The order in which keys are returned is determined by the KeyMap +* SortBy attribute. + +* Parameters: +* this +* Pointer to the KeyMap. +* reset +* If non-zero, return the first key in the KeyMap. Otherwise, +* returns the key following the one returned by the previous +* invocation of this function. + +* Returned Value: +* A pointer to the null-terminated string holding the next key, +* or NULL if there are no more keys in the KeyMap. The returned +* string should NOT be freed or modified. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the AST error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapEntry *entry; /* Pointer to the entry */ + const char *key; /* Pointer value to return */ + int itab; /* Index into hash table */ + int sortby; /* The value of the SortBy attribute */ + +/* Initialise. */ + key = NULL; + +/* Check the global error status. */ + if ( !astOK ) return key; + +/* Get the SortBy value. */ + sortby = astGetSortBy( this ); + +/* First deal with unsorted keys. */ + if( sortby == SORTBY_NONE ) { + +/* Get the index of the hash table to check first. Also get a pointer to + the entry within the hash table to check next. */ + if( reset ){ + itab = 0; + entry = this->table[ 0 ]; + } else { + itab = this->iter_itab; + entry = this->iter_entry; + } + +/* Move through elements of the hash table until we have a non-null entry. */ + while( !entry && ++itab < this->mapsize ) { + entry = this->table[ itab ]; + } + +/* Return a pointer to the key. */ + if( entry ) { + key = entry->key; + +/* Move on to the next entry in the unsorted linked list, saving the context + in the KeyMap structure. */ + this->iter_itab = itab; + this->iter_entry = entry->next; + } + +/* Now deal with sorted keys. */ + } else { + +/* If starting from the beginning, use the "first" entry. Otherwise, use + the nxt entry. */ + if( reset ) { + entry = this->first; + } else { + entry = this->iter_entry; + } + +/* If we have an entry, return a pointer to its key, and then update the + context to point to the next entry in the *sorted* list. */ + if( entry ) { + key = entry->key; + this->iter_entry = entry->snext; + } + } + +/* If no more entries were found, reset the context in the KeyMap + structure. */ + if( ! key ) { + this->iter_itab = 0; + this->iter_entry = NULL; + } + +/* Return the result.*/ + return key; +} + +static void NewTable( AstKeyMap *this, int size, int *status ){ +/* +* Name: +* NewTable + +* Purpose: +* Create a new hash table. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void NewTable( AstKeyMap *this, int size, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function removes any existing hash table and allocates memory +* for a new one of the specified size (except that the supplied size +* is modified to be the next higher power of 2). The table is +* initialised to indicate that it is empty. + +* Parameters: +* this +* Pointer to the KeyMap. +* size +* The reuqired size of the hash table. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure the table size is at least MIN_TABLE_SIZE and is a power of 2. */ + if( size <= MIN_TABLE_SIZE ) { + size = MIN_TABLE_SIZE; + } else { + size = (int) ( 0.5 + pow( 2.0, ceil( log( size )/log( 2.0 ) ) ) ); + } + +/* Remove any existing entries. */ + for( i = 0; i < this->mapsize; i++ ) FreeTableEntry( this, i, status ); + +/* Do nothing more if the table size is not changing. */ + if( size != this->mapsize ) { + +/* Modify the size of the existing table. */ + this->mapsize = size; + this->table = astGrow( this->table, size, sizeof( AstMapEntry * ) ); + this->nentry = astGrow( this->nentry, size, sizeof( int ) ); + +/* Initialise the new table. */ + if( astOK ) { + for( i = 0; i < size; i++ ) { + this->table[ i ] = NULL; + this->nentry[ i ] = 0; + } + } + } +} + +static void RemoveFromObjectList( AstKeyMap *this, AstMapEntry *entry, + int *status ){ +/* +* Name: +* RemoveFromObjectList + +* Purpose: +* Remove an entry from the linked-list of AST__OBJECTTYPE entries. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void RemoveFromObjectList( AstKeyMap *this, AstMapEntry *entry, +* int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function removes the supplied MapEntry from the linked list of +* AST__OBJECTTYPE entries. + +* Parameters: +* this +* Pointer to the KeyMap. +* entry +* Pointer to the MapEntry to be removed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapEntry *a; /* Previous entry */ + AstMapEntry *b; /* Next entry */ + Entry0A *scalar; /* Pointer to a scalar AST__OBJECTTYPE entry */ + Entry1A *vector; /* Pointer to a vector AST__OBJECTTYPE entry */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do nothing if the entry does not hold AST Object pointers. */ + if( entry->type == AST__OBJECTTYPE ) { + +/* Get pointers to the MapEntries before and after the entry being + removed. At the same time, nullify both pointers in the entry itself. */ + if( entry->nel == 0 ) { + scalar = (Entry0A *) entry; + a = scalar->prev; + b = scalar->next; + scalar->prev = NULL; + scalar->next = NULL; + } else { + vector = (Entry1A *) entry; + a = vector->prev; + b = vector->next; + vector->prev = NULL; + vector->next = NULL; + } + +/* Set the forward link in the previous entry. */ + if( a ) { + if( a->nel == 0 ) { + scalar = (Entry0A *) a; + scalar->next = b; + } else { + vector = (Entry1A *) a; + vector->next = b; + } + +/* If we are removing the list head, store the following entry as the new head. */ + } else { + this->firstA = b; + } + +/* Set the backward link in the next entry. */ + if( b ) { + if( b->nel == 0 ) { + scalar = (Entry0A *) b; + scalar->prev = a; + } else { + vector = (Entry1A *) b; + vector->prev = a; + } + } + } +} + +static void RemoveFromSortedList( AstKeyMap *this, AstMapEntry *entry, + int *status ){ +/* +* Name: +* RemoveFromSortedList + +* Purpose: +* Remove an entry from the linked-list of sorted KeyMap entries. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void RemoveFromSortedList( AstKeyMap *this, AstMapEntry *entry, +* int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function removes the supplied MapEntry from the linked list of +* sorted MapEntries. + +* Parameters: +* this +* Pointer to the KeyMap. +* entry +* Pointer to the MapEntry to be removed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapEntry *next; /* Next higher MapEntry */ + AstMapEntry *prev; /* Next lower MapEntry */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get pointers to the entries on either side of the entry to be removed. */ + next = entry->snext; + prev = entry->sprev; + +/* If the entry is not in the sorted list, abort. */ + if( next && prev ) { + +/* Connect the previous to the next, bypassing the entry being removed. */ + next->sprev = prev; + prev->snext = next; + +/* NULLify the next and previous entries stored in the entry being + removed. */ + entry->snext = NULL; + entry->sprev = NULL; + +/* Decrement the number of entries in the sorted list. */ + (this->nsorted)--; + +/* If the entry being removed is the first entry, store a pointer to the new + first entry. */ + if( this->nsorted == 0 ) { + this->first = NULL; + } else if( entry == this->first ) { + this->first = next; + } + } +} + +static AstMapEntry *RemoveTableEntry( AstKeyMap *this, int itab, + const char *key, int *status ){ +/* +* Name: +* RemoveTableEntry + +* Purpose: +* Remove an entry from a linked-list of KeyMap entries. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* AstMapEntry *RemoveTableEntry( AstKeyMap *this, int itab, +* const char *key, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function removes any entries with the specified key from the +* linked-list of entries stored at the specified entry of the hash +* table. If the supplied key is found in the list, a pointer to the +* first removed entry is returned. Otherwise, a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the KeyMap. +* itab +* Index of the hash table element to be searched. +* key +* The key string to be searched for. Trailing spaces are ignored. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the removed Entry, or NULL if no matching entry found. + +*/ + +/* Local Variables: */ + AstMapEntry **link; /* Address to store foward link */ + AstMapEntry *next; /* Pointer to next Entry to copy */ + AstMapEntry *result; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The "next" variable holds the address of the next MapEntry to be + checked. Initialise this to the MapEntry at the head of the linked + list associated with the supplied element of the hash table. */ + next = this->table[ itab ]; + +/* The "link" variable holds the address of the location at which the + pointer to the MapEntry following the removed MapEntry should be stored. + Initialise this to be the address of the hash table element. */ + link = &( this->table[ itab ] ); + +/* Loop round until we have checked all entries. */ + while( next && astOK ) { + +/* If the key for the current entry macthes the supplied key... */ + if( !KeyCmp( next->key, key ) ) { + +/* Remove the MapEntry from the list sorted by key. */ + RemoveFromSortedList( this, next, status ); + +/* If the entry is of type AST__OBJECTTYPE, remove it from the + list of AST__OBJECTTYPE entries. */ + RemoveFromObjectList( this, next, status ); + +/* Store a pointer to the next MapEntry in the list, replacing the + original pointer to the MapEntry which is being deleted. */ + *link = next->next; + +/* Return a pointer to the first matching MapEntry. Free any subsequent + matching MapEntries. */ + if( result ) { + FreeMapEntry( next, status ); + } else { + result = next; + } + +/* Decrement the number of entries in the linked list. */ + this->nentry[ itab ]--; + +/* Set up the next MapEntry to be freed. */ + next = *link; + +/* If the key for the current entry does not match the supplied key... */ + } else { + +/* Update the address at which to store the pointer to the next MapEntry + in the list. */ + link = &(next->next); + +/* Update the address of the next MapEntry in the list. */ + next = next->next; + } + } + +/* Return the result */ + return result; +} + +static AstMapEntry *SearchTableEntry( AstKeyMap *this, int itab, const char *key, int *status ){ +/* +* Name: +* SearchTableEntry + +* Purpose: +* Search an element of a has table for a given key. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* AstMapEntry *SearchTableEntry( AstKeyMap *this, int itab, const char *key, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function searches the specified element of the KeyMaps hash table +* until an element is found which has a key matching the supplied key. +* The address of this entry is returned. If no suitable entry is found, +* then NULL is returned. + +* Parameters: +* this +* Pointer to the KeyMap. +* itab +* The index of the hash table to be searched. +* key +* The key string to be searched for. Trailing spaces are ignored. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The address of the first MapEntry in the linked list which refers +* to the given key, or NULL if the key is not found. + +*/ + +/* Local Variables: */ + AstMapEntry *next; /* Pointer to next Entry to check */ + AstMapEntry *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The "next" variable holds the address of the next MapEntry to be + checked. Initialise this to the supplied MapEntry. */ + next = this->table[ itab ]; + +/* Loop round until we have checked all entries. */ + while( next ) { + +/* If the key for the current entry matches the supplied key, store the + MapEntry pointer and break. */ + if( !KeyCmp( next->key, key ) ) { + result = next; + break; + } + +/* Update the address of the next MapEntry in the list. */ + next = next->next; + + } + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* KeyMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a KeyMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the KeyMap. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstKeyMap *this; /* Pointer to the KeyMap structure */ + int ival; /* Attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the KeyMap structure. */ + this = (AstKeyMap *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* SizeGuess. */ +/* ---------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "sizeguess= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSizeGuess( this, ival ); + +/* KeyCase. */ +/* --------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "keycase= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetKeyCase( this, ival ); + +/* KeyError. */ +/* --------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "keyerror= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetKeyError( this, ival ); + +/* MapLocked. */ +/* --------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "maplocked= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetMapLocked( this, ival ); + +/* SortBy. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "sortby= %n%*s %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSortBy( this, SortByInt( setting + ival, "astSetAttrib", status ) ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetKeyCase( AstKeyMap *this, int keycase, int *status ) { +/* +*+ +* Name: +* astSetKeyCase + +* Purpose: +* Set the value of the KeyCase attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astSetKeyCase( AstKeyMap *this, int keycase ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function sets a new value for the KeyCase attribute of a +* KeyMap. It reports an error if the KeyMap contains any entries. + +* Parameters: +* this +* Pointer to the KeyMap. +* keycase +* The new attribute value. + +*- +*/ + +/* Local Variables: */ + int ok; /* Can the KeyCase value be changed? */ + int itab; /* Index into hash table */ + int newval; /* New KeyCase value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Normalise the new value */ + newval = keycase ? 1 : 0; + +/* If the KeyCase value is to be changed, see if the KeyMap is empty. */ + ok = 1; + if( astGetKeyCase( this ) != newval ) { + for( itab = 0; itab < this->mapsize; itab++ ) { + if( this->nentry[ itab ] > 0 ) { + ok = 0; + break; + } + } + } + +/* If not report an error. */ + if( !ok ) { + astError( AST__NOWRT, "astSetAttrib(KeyMap): Illegal attempt to " + "change the KeyCase attribute of a non-empty KeyMap." , status); + +/* Otherwise, store the new value. */ + } else { + this->keycase = newval; + } +} + +static void SetKeyError( AstKeyMap *this, int keyerror, int *status ) { +/* +*+ +* Name: +* astSetKeyError + +* Purpose: +* Set the value of the KeyError attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astSetKeyError( AstKeyMap *this, int keyerror ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function sets the value of the KeyError attribute for a +* KeyMap. It also sets the attribute recursively in any KeyMaps +* contained within the supplied KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. +* keyerror +* The new value for the attribute. +*- +*/ + +/* Local Variables: */ + AstMapEntry *next; /* Pointer to next Entry to copy */ + AstObject **obj_list; /* List of pointers to AST Object entries */ + int i; /* Index into hash table */ + int iel; /* Index of current vector element */ + int nel; /* Number of elements in vector */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Set the KeyError value in the supplied KeyMap. */ + this->keyerror = keyerror ? 1 : 0; + +/* Loop round each entry in the hash table. */ + for( i = 0; i < this->mapsize; i++ ) { + +/* Get a pointer to the next KeyMap entry. */ + next = this->table[ i ]; + +/* Loop round all entries in this element of the hash table. */ + while( next && astOK ) { + +/* If this entry has an Object data type, see if holds any KeyMaps. */ + if( next->type == AST__OBJECTTYPE ) { + +/* Get the number of objects to check, and a pointer to the first. */ + nel = next->nel; + if( nel == 0 ) { + obj_list = &( ((Entry0A *)next)->value ); + nel = 1; + } else { + obj_list = ((Entry1A *)next)->value; + } + +/* Loop round checking all Objects. */ + for( iel = 0; iel < nel; iel++ ) { + +/* If this Object is a KeyMap, set its KeyError attribute. */ + if( astIsAKeyMap( obj_list[ iel ] ) ) { + astSetKeyError( (AstKeyMap *) obj_list[ iel ], keyerror ); + } + } + } + +/* Get a pointer to the next entry. */ + next = next->next; + } + } +} + +static void SetMapLocked( AstKeyMap *this, int maplocked, int *status ) { +/* +*+ +* Name: +* astSetMapLocked + +* Purpose: +* Set the value of the MapLocked attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astSetMapLocked( AstKeyMap *this, int maplocked ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function sets the value of the MapLocked attribute for a +* KeyMap. It also sets the attribute recursively in any KeyMaps +* contained within the supplied KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. +* maplocked +* The new value for the attribute. +*- +*/ + +/* Local Variables: */ + AstMapEntry *next; /* Pointer to next Entry to copy */ + AstObject **obj_list; /* List of pointers to AST Object entries */ + int i; /* Index into hash table */ + int iel; /* Index of current vector element */ + int nel; /* Number of elements in vector */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Set the MapLocked value in the supplied KeyMap. */ + this->maplocked = maplocked ? 1 : 0; + +/* Loop round each entry in the hash table. */ + for( i = 0; i < this->mapsize; i++ ) { + +/* Get a pointer to the next KeyMap entry. */ + next = this->table[ i ]; + +/* Loop round all entries in this element of the hash table. */ + while( next && astOK ) { + +/* If this entry has an Object data type, see if holds any KeyMaps. */ + if( next->type == AST__OBJECTTYPE ) { + +/* Get the number of objects to check, and a pointer to the first. */ + nel = next->nel; + if( nel == 0 ) { + obj_list = &( ((Entry0A *)next)->value ); + nel = 1; + } else { + obj_list = ((Entry1A *)next)->value; + } + +/* Loop round checking all Objects. */ + for( iel = 0; iel < nel; iel++ ) { + +/* If this Object is a KeyMap, set its MapLocked attribute. */ + if( astIsAKeyMap( obj_list[ iel ] ) ) { + astSetMapLocked( (AstKeyMap *) obj_list[ iel ], maplocked ); + } + } + } + +/* Get a pointer to the next entry. */ + next = next->next; + } + } +} + +static void SetSizeGuess( AstKeyMap *this, int sizeguess, int *status ) { +/* +*+ +* Name: +* astSetSizeGuess + +* Purpose: +* Set the value of the SizeGuess attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astSetSizeGuess( AstKeyMap *this, int sizeguess ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function sets a new value for the SizeGuess attribute of a +* KeyMap. It reports an error if the KeyMap contains any entries. + +* Parameters: +* this +* Pointer to the KeyMap. +* sizeguess +* The new attribute value. + +*- +*/ + +/* Local Variables: */ + int empty; /* Is the KeyMap empty? */ + int itab; /* Index into hash table */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* See if the KeyMap is empty. */ + empty = 1; + for( itab = 0; itab < this->mapsize; itab++ ) { + if( this->nentry[ itab ] > 0 ) { + empty = 0; + break; + } + } + +/* If not report an error. */ + if( !empty ) { + astError( AST__NOWRT, "astSetAttrib(KeyMap): Illegal attempt to " + "change the SizeGuess attribute of a non-empty KeyMap." , status); + +/* Otherwise, store the new value and change the size of the hash + table. */ + } else { + this->sizeguess = sizeguess; + NewTable( this, sizeguess/MAX_ENTRIES_PER_TABLE_ENTRY, status ); + } +} + +static void SetSortBy( AstKeyMap *this, int sortby, int *status ) { +/* +*+ +* Name: +* astSetSortBy + +* Purpose: +* Set the value of the SortBy attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* void astSetSortBy( AstKeyMap *this, int sortby ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function sets the value of the SortBy attribute for a +* KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. +* sortby +* The new value for the attribute. +*- +*/ + +/* Local Variables: */ + int oldval; /* The old sortby value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the old SortBy value. */ + oldval = astGetSortBy( this ); + +/* Set the new SortBy value. */ + this->sortby = sortby; + +/* If the value has changed, re-sort the keys. */ + if( oldval != sortby ) SortEntries( this, status ); + +} + +static size_t SizeOfEntry( AstMapEntry *entry, int *status ){ +/* +* Name: +* SizeOfEntry + +* Purpose: +* Return the size of the supplied MapEntry structure. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* size_t SizeOfEntry( AstMapEntry *entry, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function returns the size of the supplied MapEntry structure. + +* Parameters: +* entry +* Pointer to the MapEntry. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The size of the MapEntry structure. This does not include the size +* of any data for which pointers are stored in the MapEntry structure. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + size_t result; /* Returned value */ + int nel; /* Entry length */ + int type; /* Data type */ + +/* Initialise. */ + result = 0; + +/* Check the global error status and the supplied pointer. */ + if ( !astOK || !entry ) return result; + +/* Get the data type and length of the MapEntry. */ + type = entry->type; + nel = entry->nel; + +/* Deal with each type. */ + if( type == AST__STRINGTYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0C ) : sizeof( Entry1C ); + + } else if( type == AST__OBJECTTYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0A ) : sizeof( Entry1A ); + + } else if( type == AST__INTTYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0I ) : sizeof( Entry1I ); + + } else if( type == AST__POINTERTYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0P ) : sizeof( Entry1P ); + + } else if( type == AST__SINTTYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0S ) : sizeof( Entry1S ); + + } else if( type == AST__BYTETYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0B ) : sizeof( Entry1B ); + + } else if( type == AST__DOUBLETYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0D ) : sizeof( Entry1D ); + + } else if( type == AST__FLOATTYPE ) { + result = ( nel == 0 ) ? sizeof( Entry0F ) : sizeof( Entry1F ); + + } else if( type == AST__UNDEFTYPE ) { + result = sizeof( AstMapEntry ); + +/* Report an error if the data type is unknown. */ + } else { + astError( AST__INTER, "SizeOfEntry(KeyMap): Illegal map entry data " + "type %d encountered (internal AST programming error).", status, + type ); + } + +/* Return the result. */ + return result; +} + +static int SortByInt( const char *sortby, const char *method, int *status ){ +/* +* Name: +* SortByInt + +* Purpose: +* Get the integer associated with a string SortBy value. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* int SortByInt( const char *sortby, const char *method, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function returns the integer associated with the supplied +* string SortBy value. + +* Parameters: +* sortby +* Pointer to the string SortBy value (case insensitive). +* method +* Pointer to a string holding the name of the calling method for +* inclusion in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The associated SortBy integer. + +*/ + +/* Local Variables: */ + int result; /* The returned integer */ + +/* Initialise. */ + result = SORTBY_NONE; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check each known value. */ + if( astChrMatch( sortby, "None" ) ) { + result = SORTBY_NONE; + + } else if( astChrMatch( sortby, "AgeUp" ) ) { + result = SORTBY_AGEUP; + + } else if( astChrMatch( sortby, "AgeDown" ) ) { + result = SORTBY_AGEDOWN; + + } else if( astChrMatch( sortby, "KeyAgeUp" ) ) { + result = SORTBY_KEYAGEUP; + + } else if( astChrMatch( sortby, "KeyAgeDown" ) ) { + result = SORTBY_KEYAGEDOWN; + + } else if( astChrMatch( sortby, "KeyUp" ) ) { + result = SORTBY_KEYUP; + + } else if( astChrMatch( sortby, "KeyDown" ) ) { + result = SORTBY_KEYDOWN; + + } else { + astError( AST__INTER, "%s(KeyMap): Illegal SortBy value %s " + "encountered.", status, method, sortby ); + } + +/* Return the result. */ + return result; +} + +static const char *SortByString( int sortby, const char *method, int *status ){ +/* +* Name: +* SortByString + +* Purpose: +* Get the string associated with an integer SortBy value. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* const char *SortByString( int sortby, const char *method, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function returns the string associated with the supplied +* integer SortBy value. + +* Parameters: +* sortby +* The integer SortBy value. +* method +* Pointer to a string holding the name of the calling method for +* inclusion in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the associated SortBy string. + +*/ + +/* Local Variables: */ + const char *result; /* The returned string */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check each value. */ + if( sortby == SORTBY_NONE ) { + result = "None"; + + } else if( sortby == SORTBY_AGEUP ) { + result = "AgeUp"; + + } else if( sortby == SORTBY_AGEDOWN ) { + result = "AgeDown"; + + } else if( sortby == SORTBY_KEYAGEUP ) { + result = "KeyAgeUp"; + + } else if( sortby == SORTBY_KEYAGEDOWN ) { + result = "KeyAgeDown"; + + } else if( sortby == SORTBY_KEYUP ) { + result = "KeyUp"; + + } else if( sortby == SORTBY_KEYDOWN ) { + result = "KeyDown"; + + } else { + astError( AST__INTER, "%s(KeyMap): Illegal integer SortBy value %d " + "encountered (internal AST programming error).", status, + method, sortby ); + } + +/* Return the result. */ + return result; +} + +static void SortEntries( AstKeyMap *this, int *status ){ +/* +* Name: +* SortEntries + +* Purpose: +* Ensure the entries in a KeyMap are sorted correctly. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* void SortEntries( AstKeyMap *this, int *status ) + +* Class Membership: +* KeyMap member function. + +* Description: +* This function sorts all the entries in the supplied KeyMap in +* the manner indicated by the SortBy attribute value in the KeyMap. +* A double linked list is maintained indicating the ordering, with +* the first entry in the sorted list being pointed to by "this->first". +* Each entry contains "snext" and "sprev" pointers that point to the +* next and previous entries in the sorted list. The number of entries +* in the sorted list (which should usually equal the total number of +* entries currently in the KeyMap), is stored in "this->nsorted". + +* Parameters: +* this +* Pointer to the KeyMap. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapEntry **ents; + AstMapEntry **pent; + AstMapEntry **a; + AstMapEntry **b; + AstMapEntry *entry; + int i; + int nent; + int sortby; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Empty the sorted list. */ + this->nsorted = 0; + this->first = NULL; + +/* Get the SortBy value. */ + sortby = astGetSortBy( this ); + +/* Do nothing more if no sorting is required. */ + if( sortby != SORTBY_NONE ) { + +/* Get the number of entries in the keyMap. */ + nent = astMapSize( this ); + +/* Only sort if the KeyMap is not empty. */ + if( nent > 0 ) { + +/* Allocate an array with one element for each entry. Each element is a + pointer to a MapEntry structure. */ + ents = astMalloc( sizeof( *ents )*nent ); + if( astOK ) { + +/* Loop round all entries in the hash table. */ + pent = ents; + for( i = 0; i < this->mapsize; i++ ) { + +/* Get a pointer to the next KeyMap entry. */ + entry = this->table[ i ]; + +/* Loop round all entries in this element of the hash table. */ + while( entry ) { + +/* Store the sorting method in the MapEntry. */ + entry->sortby = sortby; + +/* Put a pointer to the MapEntry into the array. */ + *(pent++) = entry; + +/* Update the address of the next MapEntry in the source. */ + entry = entry->next; + } + } + +/* No need for sorting if there is only one entry. */ + if( nent == 1 ) { + ents[ 0 ]->snext = ents[ 0 ]; + ents[ 0 ]->sprev = ents[ 0 ]; + +/* Sort the array of pointers if there is more than one entry... */ + } else { + qsort( ents, nent, sizeof( *ents ), CompareEntries ); + +/* Establish the double linked list. */ + a = ents; + b = ents + 1; + for( i = 1; i < nent; i++ ) { + (*b)->sprev = *a; + (*a)->snext = *b; + a = b++; + } + + b = ents; + (*b)->sprev = *a; + (*a)->snext = *b; + + } + +/* Store a pointer to the first entry in the sorted list. */ + this->first = ents[ 0 ]; + +/* Store the number of entrie sin the sorted list. */ + this->nsorted = nent; + } + +/* Free resources. */ + ents = astFree( ents ); + } + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* KeyMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a KeyMap's attributes. + +* Parameters: +* this +* Pointer to the KeyMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstKeyMap *this; /* Pointer to the KeyMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the KeyMap structure. */ + this = (AstKeyMap *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* SizeGuess. */ +/* ---------- */ + if ( !strcmp( attrib, "sizeguess" ) ) { + result = astTestSizeGuess( this ); + +/* KeyCase. */ +/* --------- */ + } else if ( !strcmp( attrib, "keycase" ) ) { + result = astTestKeyCase( this ); + +/* KeyError. */ +/* --------- */ + } else if ( !strcmp( attrib, "keyerror" ) ) { + result = astTestKeyError( this ); + +/* MapLocked. */ +/* --------- */ + } else if ( !strcmp( attrib, "maplocked" ) ) { + result = astTestMapLocked( this ); + +/* SortBy. */ +/* ------- */ + } else if ( !strcmp( attrib, "sortby" ) ) { + result = astTestSortBy( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int TestSizeGuess( AstKeyMap *this, int *status ) { +/* +*+ +* Name: +* astTestSizeGuess + +* Purpose: +* Test the value of the SizeGuess attribute for a KeyMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "keymap.h" +* int astTestSizeGuess( AstKeyMap *this ) + +* Class Membership: +* KeyMap method. + +* Description: +* This function returns a non-zero value if the SizeGuess attribute +* has been set in a KeyMap. + +* Parameters: +* this +* Pointer to the KeyMap. + +* Returned Value: +* Non-zero if the SizeGuess attribute is set. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set. + +*- +*/ + +/* Local Variables: */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Return non-zero if the attribute is still set to its "not set" value. */ + return ( this->sizeguess != INT_MAX ); +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ + +/* +*att++ +* Name: +* SizeGuess + +* Purpose: +* The expected size of the KeyMap. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This is attribute gives an estimate of the number of entries that +* will be stored in the KeyMap. It is used to tune the internal +* properties of the KeyMap for speed and efficiency. A larger value +* will result in faster access at the expense of increased memory +* requirements. However if the SizeGuess value is much larger than +* the actual size of the KeyMap, then there will be little, if any, +* speed gained by making the SizeGuess even larger. The default value +* is 300. +* +* The value of this attribute can only be changed if the KeyMap is +* empty. Its value can be set conveniently when creating the KeyMap. +* An error will be reported if an attempt is made to set or clear the +* attribute when the KeyMap contains any entries. + +* Applicability: +* KeyMap +* All KeyMaps have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* KeyCase + +* Purpose: +* Are keys case sensitive? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how keys are +* used. If KeyCase is zero, then key strings supplied to any method +* are automatically converted to upper case before being used. If +* KeyCase is non-zero (the default), then supplied key strings are +* used without modification. +* +* The value of this attribute can only be changed if the KeyMap is +* empty. Its value can be set conveniently when creating the KeyMap. +* An error will be reported if an attempt is made to change the +* attribute value when the KeyMap contains any entries. + +* Applicability: +* KeyMap +* All KeyMaps have this attribute. +* Table +* The Table class over-rides this attribute by forcing it to zero. +* That is, keys within a Table are always case insensitive. +*att-- +*/ +astMAKE_GET(KeyMap,KeyCase,int,1,(this->keycase == -1 ? 1 : this->keycase)) +astMAKE_TEST(KeyMap,KeyCase,( this->keycase != -1 )) + +/* +*att++ +* Name: +* KeyError + +* Purpose: +* Report an error when getting the value of a non-existant KeyMap entry? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how the +c astMapGet... +f AST_MAPGET... +* functions behave if the requested key is not found in the KeyMap. +* If KeyError is zero (the default), then these functions will return +c zero +f .FALSE. +* but no error will be reported. If KeyError is non-zero, then the +* same values are returned but an error is also reported. + +* Notes: +* - When setting a new value for KeyError, the supplied value is +* propagated to any KeyMaps contained within the supplied KeyMap. +* - When clearing the KeyError attribute, the attribute is also +* cleared in any KeyMaps contained within the supplied KeyMap. + +* Applicability: +* KeyMap +* All KeyMaps have this attribute. +*att-- +*/ +astMAKE_GET(KeyMap,KeyError,int,0,( ( this->keyerror != -INT_MAX ) ? + this->keyerror : 0 )) +astMAKE_TEST(KeyMap,KeyError,( this->keyerror != -INT_MAX )) + +/* +*att++ +* Name: +* MapLocked + +* Purpose: +* Prevent new entries being added to a KeyMap? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* If this boolean attribute is set to +c a non-zero value, +f .TRUE., +* an error will be reported if an attempt is made to add a new entry +* to the KeyMap. Note, the value associated with any existing entries +* can still be changed, but no new entries can be stored in the KeyMap. +* The default value +c (zero) +f (.FALSE.) +* allows new entries to be added to the KeyMap. + +* Notes: +* - When setting a new value for MapLocked, the supplied value is +* propagated to any KeyMaps contained within the supplied KeyMap. +* - When clearing the MapLocked attribute, the attribute is also +* cleared in any KeyMaps contained within the supplied KeyMap. + +* Applicability: +* KeyMap +* All KeyMaps have this attribute. +*att-- +*/ +astMAKE_GET(KeyMap,MapLocked,int,0,( ( this->maplocked != -INT_MAX ) ? + this->maplocked : 0 )) +astMAKE_TEST(KeyMap,MapLocked,( this->maplocked != -INT_MAX )) + +/* +*att++ +* Name: +* SortBy + +* Purpose: +* Determines how keys are sorted in a KeyMap. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute determines the order in which keys are returned by the +c astMapKey +f AST_MAPKEY +* function. It may take the following values (the default is "None"): +* +* - "None": The keys are returned in an arbitrary order. This is the +* fastest method as it avoids the need for a sorted list of keys to +* be maintained and used. +* +* - "AgeDown": The keys are returned in the order in which values were +* stored in the KeyMap, with the key for the most recent value being +* returned last. If the value of an existing entry is changed, it goes +* to the end of the list. +* +* - "AgeUp": The keys are returned in the order in which values were +* stored in the KeyMap, with the key for the most recent value being +* returned first. If the value of an existing entry is changed, it goes +* to the top of the list. +* +* - "KeyAgeDown": The keys are returned in the order in which they +* were originally stored in the KeyMap, with the most recent key being +* returned last. If the value of an existing entry is changed, its +* position in the list does not change. +* +* - "KeyAgeUp": The keys are returned in the order in which they +* were originally stored in the KeyMap, with the most recent key being +* returned first. If the value of an existing entry is changed, its +* position in the list does not change. +* +* - "KeyDown": The keys are returned in alphabetical order, with "A..." +* being returned last. +* +* - "KeyUp": The keys are returned in alphabetical order, with "A..." +* being returned first. + +* Notes: +* - If a new value is assigned to SortBy (or if SortBy is cleared), +* all entries currently in the KeyMap are re-sorted according to the +* new SortBy value. + +* Applicability: +* KeyMap +* All KeyMaps have this attribute. +*att-- +*/ +astMAKE_GET(KeyMap,SortBy,int,SORTBY_NONE,( ( this->sortby != -INT_MAX ) ? + this->sortby : SORTBY_NONE )) +astMAKE_TEST(KeyMap,SortBy,( this->sortby != -INT_MAX )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for KeyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for KeyMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstKeyMap *in; /* Pointer to input KeyMap */ + AstKeyMap *out; /* Pointer to output KeyMap */ + int i; /* Index into hash table */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output KeyMap structures. */ + in = (AstKeyMap *) objin; + out = (AstKeyMap *) objout; + +/* For safety, first clear any references to the input memory from + the output KeyMap. */ + out->table = NULL; + out->nentry = NULL; + out->first = NULL; + out->firstA = NULL; + +/* Make copies of the table entries. */ + out->table = astMalloc( sizeof( AstMapEntry * )*( out->mapsize ) ); + out->nentry = astMalloc( sizeof( int )*( out->mapsize ) ); + + for( i = 0; i < out->mapsize; i++ ) CopyTableEntry( in, out, i, status ); + +/* Create the required sorted key list in the new KeyMap. */ + SortEntries( out, status ); + +/* If an error occurred, clean up by freeing all memory allocated above. */ + if ( !astOK ) { + for( i = 0; i < out->mapsize; i++ ) FreeTableEntry( out, i, status ); + out->table = astFree( out->table ); + out->nentry = astFree( out->nentry ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for KeyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for KeyMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstKeyMap *this; /* Pointer to the KeyMap structure */ + int i; /* Loop count */ + +/* Obtain a pointer to the KeyMap structure. */ + this = (AstKeyMap *) obj; + +/* Free all allocated memory. */ + for( i = 0; i < this->mapsize; i++ ) FreeTableEntry( this, i, status ); + +/* Free memory used to hold tables. */ + this->table = astFree( this->table ); + this->nentry = astFree( this->nentry ); + +/* Nullify other pointers. */ + this->first = NULL; + this->firstA = NULL; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for KeyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the KeyMap class to an output Channel. + +* Parameters: +* this +* Pointer to the KeyMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstKeyMap *this; /* Pointer to the KeyMap structure */ + AstMapEntry *next; /* Pointer to the next AstMapEntry to dump */ + int i; /* Index into hash table */ + int nentry; /* Number of entries dumped so far */ + int set; /* Is attribute set? */ + int ival; /* Attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the KeyMap structure. */ + this = (AstKeyMap *) this_object; + +/* Initialise the number of KeyMap entries dumped so far. */ + nentry = 0; + +/* SizeGuess. */ +/* ---------- */ + set = TestSizeGuess( this, status ); + ival = set ? GetSizeGuess( this, status ) : astGetSizeGuess( this ); + astWriteInt( channel, "SzGss", set, 0, ival, "Guess at KeyMap size" ); + +/* SortBy. */ +/* ------- */ + set = TestSortBy( this, status ); + ival = set ? GetSortBy( this, status ) : astGetSortBy( this ); + astWriteString( channel, "SortBy", set, 0, SortByString( ival, "astDump", + status ), + "Sorting scheme for keys" ); + +/* KeyCase. */ +/* --------- */ + set = TestKeyCase( this, status ); + ival = set ? GetKeyCase( this, status ) : astGetKeyCase( this ); + astWriteInt( channel, "KyCas", set, 0, ival, "Are keys case sensitive?" ); + +/* KeyError. */ +/* --------- */ + set = TestKeyError( this, status ); + ival = set ? GetKeyError( this, status ) : astGetKeyError( this ); + astWriteInt( channel, "KyErr", set, 0, ival, "Report non-existant keys?" ); + +/* MapLocked. */ +/* --------- */ + set = TestMapLocked( this, status ); + ival = set ? GetMapLocked( this, status ) : astGetMapLocked( this ); + astWriteInt( channel, "MpLck", set, 0, ival, "Prevent addition of new entries?" ); + +/* MapSize. */ +/* -------- */ + astWriteInt( channel, "MapSz", 1, 1, this->mapsize, "Size of hash table" ); + +/* member count. */ + astWriteInt( channel, "MemCnt", 1, 1, this->member_count, "Total member count" ); + +/* Loop round each entry in the hash table. */ + for( i = 0; i < this->mapsize; i++ ) { + +/* Get a pointer to the next KeyMap entry to dump. */ + next = this->table[ i ]; + +/* Loop round dumping all KeyMap entries in this element of the hash table. */ + while( next && astOK ) { + DumpEntry( next, channel, ++nentry, status ); + +/* Get a pointer to the next entry to dump. */ + next = next->next; + + } + } +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAKeyMap and astCheckKeyMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(KeyMap,Object) +astMAKE_CHECK(KeyMap) + +AstKeyMap *astKeyMap_( const char *options, int *status, ...) { +/* +*++ +* Name: +c astKeyMap +f AST_KEYMAP + +* Purpose: +* Create a KeyMap. + +* Type: +* Public function. + +* Synopsis: +c #include "keymap.h" +c AstKeyMap *astKeyMap( const char *options, ... ) +f RESULT = AST_KEYMAP( OPTIONS, STATUS ) + +* Class Membership: +* KeyMap constructor. + +* Description: +* This function creates a new empty KeyMap and optionally initialises its +* attributes. Entries can then be added to the KeyMap using the +c astMapPut0 and astMapPut1 functions. +f AST_MAPPUT0 and AST_MAPPUT1 functions. +* +* The KeyMap class is used to store a set of values with associated keys +* which identify the values. The keys are strings. These may be case +* sensitive or insensitive as selected by the KeyCase attribute, and +* trailing spaces are ignored. The value associated with a key can be +* integer (signed 4 and 2 byte, or unsigned 1 byte), floating point +* (single or double precision), +c void pointer, +* character string or AST Object pointer. Each +* value can be a scalar or a one-dimensional vector. A KeyMap is +* conceptually similar to a Mapping in that a KeyMap transforms an +* input into an output - the input is the key, and the output is the +* value associated with the key. However, this is only a conceptual +* similarity, and it should be noted that the KeyMap class inherits from +* the Object class rather than the Mapping class. The methods of the +* Mapping class cannot be used with a KeyMap. + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new KeyMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new KeyMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astKeyMap() +f AST_MAP = INTEGER +* A pointer to the new KeyMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstKeyMap *new; /* Pointer to new KeyMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the KeyMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitKeyMap( NULL, sizeof( AstKeyMap ), !class_init, &class_vtab, "KeyMap" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new KeyMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new KeyMap. */ + return new; +} + +AstKeyMap *astKeyMapId_( const char *options, ... ) { +/* +* Name: +* astKeyMapId_ + +* Purpose: +* Create a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "keymap.h" +* AstKeyMap *astKeyMapId_( const char *options, ... ) + +* Class Membership: +* KeyMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astKeyMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astKeyMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astKeyMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astKeyMap_. + +* Returned Value: +* The ID value associated with the new KeyMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstKeyMap *new; /* Pointer to new KeyMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the KeyMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitKeyMap( NULL, sizeof( AstKeyMap ), !class_init, &class_vtab, "KeyMap" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new KeyMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new KeyMap. */ + return astMakeId( new ); +} + +AstKeyMap *astInitKeyMap_( void *mem, size_t size, int init, AstKeyMapVtab *vtab, + const char *name, int *status ) { +/* +*+ +* Name: +* astInitKeyMap + +* Purpose: +* Initialise a KeyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "keymap.h" +* AstKeyMap *astInitKeyMap( void *mem, size_t size, int init, AstKeyMapVtab *vtab, +* const char *name ) + +* Class Membership: +* KeyMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new KeyMap object. It allocates memory (if necessary) to accommodate +* the KeyMap plus any additional data associated with the derived class. +* It then initialises a KeyMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a KeyMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the KeyMap is to be created. This +* must be of sufficient size to accommodate the KeyMap data +* (sizeof(KeyMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the KeyMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the KeyMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the KeyMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new KeyMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astClass +* method). + +* Returned Value: +* A pointer to the new KeyMap. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstKeyMap *new; /* Pointer to the new KeyMap */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitKeyMapVtab( vtab, name ); + +/* Initialise an Object structure (the parent class) as the first component + within the KeyMap structure, allocating memory if necessary. */ + new = (AstKeyMap *) astInitObject( mem, size, 0, (AstObjectVtab *) vtab, + name ); + + if ( astOK ) { + +/* Initialise the KeyMap data. */ +/* ---------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->sizeguess = INT_MAX; + new->mapsize = 0; + new->table = NULL; + new->nentry = NULL; + new->keycase = -1; + new->keyerror = -INT_MAX; + new->maplocked = -INT_MAX; + new->sortby = -INT_MAX; + new->first = NULL; + new->nsorted = 0; + new->member_count = 0; + new->firstA = NULL; + new->iter_itab = 0; + new->iter_entry = NULL; + + NewTable( new, MIN_TABLE_SIZE, status ); + +/* If an error occurred, clean up by deleting the new KeyMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new KeyMap. */ + return new; +} + +AstKeyMap *astLoadKeyMap_( void *mem, size_t size, AstKeyMapVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadKeyMap + +* Purpose: +* Load a KeyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "keymap.h" +* AstKeyMap *astLoadKeyMap( void *mem, size_t size, AstKeyMapVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* KeyMap loader. + +* Description: +* This function is provided to load a new KeyMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* KeyMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a KeyMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the KeyMap is to be +* loaded. This must be of sufficient size to accommodate the +* KeyMap data (sizeof(KeyMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the KeyMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the KeyMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstKeyMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new KeyMap. If this is NULL, a pointer +* to the (static) virtual function table for the KeyMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "KeyMap" is used instead. + +* Returned Value: +* A pointer to the new KeyMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstKeyMap *new; /* Pointer to the new KeyMap */ + AstObject **alist; /* Pointer to vector of entry values */ + AstObject *aval; /* AST Object value for an entry */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *com; /* Pointer to comment string for an entry */ + char *key; /* Pointer to key string for an entry */ + char *sval; /* String value for an entry */ + char buff[ 30 ]; /* Buffer for key names */ + const char **slist; /* Pointer to vector of entry values */ + double *dlist; /* Pointer to vector of entry values */ + double dval; /* Floating point value for an entry */ + float *flist; /* Pointer to vector of entry values */ + int *ilist; /* Pointer to vector of entry values */ + int index; /* Index of next array element in a vector entry */ + int ival; /* Integer value for an entry */ + int mapsize; /* Size for new hash table */ + int nel; /* Vector length */ + int nentry; /* Number of KeyMap entries read so far */ + int type; /* Data type for an entry */ + short int *wlist; /* Pointer to vector of entry values */ + short int wval; /* Short int value for an entry */ + unsigned char *blist; /* Pointer to vector of entry values */ + unsigned char bval; /* Byte value for an entry */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this KeyMap. In this case the + KeyMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstKeyMap ); + vtab = &class_vtab; + name = "KeyMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitKeyMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built KeyMap. */ + new = astLoadObject( mem, size, (AstObjectVtab *) vtab, name, channel ); + + if ( astOK ) { + +/* Inidicate the KeyMap is empty. */ + new->mapsize = 0; + new->table = NULL; + new->nentry = NULL; + new->firstA = NULL; + new->iter_itab = 0; + new->iter_entry = NULL; + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "KeyMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* SizeGuess. */ +/* ---------- */ + new->sizeguess = astReadInt( channel, "szgss", INT_MAX ); + if ( TestSizeGuess( new, status ) ) SetSizeGuess( new, new->sizeguess, status ); + +/* KeyCase. */ +/* --------- */ + new->keycase = astReadInt( channel, "kycas", -INT_MAX ); + if ( TestKeyCase( new, status ) ) SetKeyCase( new, new->keycase, status ); + +/* KeyError. */ +/* --------- */ + new->keyerror = astReadInt( channel, "kyerr", -INT_MAX ); + if ( TestKeyError( new, status ) ) SetKeyError( new, new->keyerror, status ); + +/* MapLocked. */ +/* --------- */ + new->maplocked = astReadInt( channel, "mplck", -INT_MAX ); + if ( TestMapLocked( new, status ) ) SetMapLocked( new, new->maplocked, status ); + +/* SortBy. */ +/* ------- */ + sval = astReadString( channel, "sortby", " " ); + new->sortby = -INT_MAX; + if( astOK && strcmp( sval, " " ) ) { + new->sortby = SortByInt( sval, "astRead", status ); + } + if( TestSortBy( new, status ) ) SetSortBy( new, new->sortby, status ); + sval = astFree( sval ); + +/* MapSize. */ +/* -------- */ + mapsize = astReadInt( channel, "mapsz", MIN_TABLE_SIZE ); + NewTable( new, mapsize, status ); + +/* Entries... */ +/* ---------- */ + +/* Initialise the index of the next AstMapEntry to be read. */ + nentry = 0; + +/* Read Entries until no more are found */ + while( astOK ) { + nentry++; + +/* Get the entry key. */ + (void) sprintf( buff, "key%d", nentry ); + key = astReadString( channel, buff, NULL ); + +/* We have finished reading entries if no key was found. */ + if( !key ) break; + +/* Get the entry comment. */ + (void) sprintf( buff, "com%d", nentry ); + com = astReadString( channel, buff, NULL ); + +/* Get the entry data type. */ + (void) sprintf( buff, "typ%d", nentry ); + type = astReadInt( channel, buff, AST__BADTYPE ); + + if( type == AST__BADTYPE && astOK ) { + astError( AST__BDFTS, "astLoadKeyMap(%s): No data type code found " + "whilst reading a %s.", status, name, name ); + + } + +/* Get the vector length. */ + (void) sprintf( buff, "nel%d", nentry ); + nel = astReadInt( channel, buff, 0 ); + +/* Get the entry member number. Set the KeyMap member count to this value + so that the next entry added to the KeyMap will get this value as its + member index. */ + (void) sprintf( buff, "mem%d", nentry ); + new->member_count = astReadInt( channel, buff, 0 ); + +/* First deal with integer entries. */ + if( type == AST__INTTYPE ) { + +/* For scalar entries, use "val" to get the value then create a new + entry and add it to the KeyMap. */ + if( nel == 0 ) { + (void) sprintf( buff, "val%d", nentry ); + ival = astReadInt( channel, buff, 0 ); + MapPut0I( new, key, ival, com, status ); + +/* If we must have an array of values... */ + } else { + +/* Create an array to hold the values. */ + ilist = astMalloc( sizeof(int)*nel ); + +/* Loop round reading values. */ + for( index = 0; astOK && index < nel; index++ ) { + (void) sprintf( buff, "v%d_%d", nentry, index + 1 ); + ilist[ index ] = astReadInt( channel, buff, 0 ); + } + +/* Create the KeyMap entry. */ + MapPut1I( new, key, nel, ilist, com, status ); + +/* Free resources. */ + ilist = astFree( ilist ); + } + +/* Do the same for short int values. */ + } else if( type == AST__SINTTYPE ) { + if( nel == 0 ) { + (void) sprintf( buff, "val%d", nentry ); + wval = (short int) astReadInt( channel, buff, 0 ); + MapPut0S( new, key, wval, com, status ); + } else { + wlist = astMalloc( sizeof(short int)*nel ); + for( index = 0; astOK && index < nel; index++ ) { + (void) sprintf( buff, "v%d_%d", nentry, index + 1 ); + wlist[ index ] = (short int) astReadInt( channel, buff, 0 ); + } + MapPut1S( new, key, nel, wlist, com, status ); + wlist = astFree( wlist ); + } + +/* Do the same for byte values. */ + } else if( type == AST__BYTETYPE ) { + if( nel == 0 ) { + (void) sprintf( buff, "val%d", nentry ); + bval = (unsigned char) astReadInt( channel, buff, 0 ); + MapPut0B( new, key, bval, com, status ); + } else { + blist = astMalloc( sizeof(unsigned char)*nel ); + for( index = 0; astOK && index < nel; index++ ) { + (void) sprintf( buff, "v%d_%d", nentry, index + 1 ); + blist[ index ] = (unsigned char) astReadInt( channel, buff, 0 ); + } + MapPut1B( new, key, nel, blist, com, status ); + blist = astFree( blist ); + } + +/* Do the same for double values. */ + } else if( type == AST__DOUBLETYPE ) { + if( nel == 0 ) { + (void) sprintf( buff, "val%d", nentry ); + dval = astReadDouble( channel, buff, AST__BAD ); + MapPut0D( new, key, dval, com, status ); + } else { + dlist = astMalloc( sizeof(double)*nel ); + for( index = 0; astOK && index < nel; index++ ) { + (void) sprintf( buff, "v%d_%d", nentry, index + 1 ); + dlist[ index ] = astReadDouble( channel, buff, AST__BAD ); + } + MapPut1D( new, key, nel, dlist, com, status ); + dlist = astFree( dlist ); + } + +/* Do the same for float values. */ + } else if( type == AST__FLOATTYPE ) { + if( nel == 0 ) { + (void) sprintf( buff, "val%d", nentry ); + dval = astReadDouble( channel, buff, 0.0 ); + MapPut0F( new, key, (float) dval, com, status ); + } else { + flist = astMalloc( sizeof(float)*nel ); + for( index = 0; astOK && index < nel; index++ ) { + (void) sprintf( buff, "v%d_%d", nentry, index + 1 ); + flist[ index ] = (float) astReadDouble( channel, buff, 0.0 ); + } + MapPut1F( new, key, nel, flist, com, status ); + flist = astFree( flist ); + } + +/* Do the same for string values. */ + } else if( type == AST__STRINGTYPE ) { + if( nel == 0 ) { + (void) sprintf( buff, "val%d", nentry ); + sval = astReadString( channel, buff, "" ); + MapPut0C( new, key, sval, com, status ); + sval = astFree( sval ); + } else { + slist = astMalloc( sizeof(const char *)*nel ); + for( index = 0; astOK && index < nel; index++ ) { + (void) sprintf( buff, "v%d_%d", nentry, index + 1 ); + slist[ index ] = astReadString( channel, buff, "" ); + } + MapPut1C( new, key, nel, slist, com, status ); + for( index = 0; astOK && index < nel; index++ ) { + slist[ index ] = astFree( (void *) slist[ index ] ); + } + slist = astFree( slist ); + } + +/* Do the same for object values. */ + } else if( type == AST__OBJECTTYPE ) { + if( nel == 0 ) { + (void) sprintf( buff, "val%d", nentry ); + aval = astReadObject( channel, buff, NULL ); + MapPut0A( new, key, aval, com, status ); + if( aval ) aval = astAnnul( aval ); + } else { + alist = astMalloc( sizeof(AstObject *)*nel ); + for( index = 0; astOK && index < nel; index++ ) { + (void) sprintf( buff, "v%d_%d", nentry, index + 1 ); + alist[ index ] = astReadObject( channel, buff, NULL ); + } + MapPut1A( new, key, nel, alist, com, status ); + for( index = 0; astOK && index < nel; index++ ) { + if( alist[ index ] ) alist[ index ] = astAnnul( alist[ index ] ); + } + alist = astFree( alist ); + } + +/* Undef values have no value. */ + } else if( type == AST__UNDEFTYPE ) { + MapPutU( new, key, com, status ); + +/* Report an error if the data type is unknown. */ + } else if( astOK ) { + astError( AST__BDFTS, "astLoadKeyMap(%s): Unknown data type code " + "(%d) encountered whilst reading a %s.", status, name, type, + name ); + } +/* Free resources. */ + key = astFree( key ); + if( com ) com = astFree( com ); + + } + +/* Set the final member count for the KeyMap. */ + new->member_count = astReadInt( channel, "memcnt", 0 ); + +/* If an error occurred, clean up by deleting the new KeyMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new KeyMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +#define MAKE_MAPPUT0_(X,Xtype) \ +void astMapPut0##X##_( AstKeyMap *this, const char *key, Xtype value, \ + const char *comment, int *status ){ \ + if ( !astOK ) return; \ + (**astMEMBER(this,KeyMap,MapPut0##X))(this,key,value,comment, status ); \ +} +MAKE_MAPPUT0_(D,double) +MAKE_MAPPUT0_(F,float) +MAKE_MAPPUT0_(I,int) +MAKE_MAPPUT0_(C,const char *) +MAKE_MAPPUT0_(A,AstObject *) +MAKE_MAPPUT0_(P,void *) +MAKE_MAPPUT0_(S,short int) +MAKE_MAPPUT0_(B,unsigned char) +#undef MAKE_MAPPUT0_ + + +#define MAKE_MAPPUT1_(X,Xtype) \ +void astMapPut1##X##_( AstKeyMap *this, const char *key, int size, \ + Xtype value[], const char *comment, \ + int *status ){ \ + if ( !astOK ) return; \ + (**astMEMBER(this,KeyMap,MapPut1##X))(this,key,size,value,comment, status ); \ +} +MAKE_MAPPUT1_(S,const short int) +MAKE_MAPPUT1_(B,const unsigned char) +MAKE_MAPPUT1_(D,const double) +MAKE_MAPPUT1_(F,const float) +MAKE_MAPPUT1_(I,const int) +MAKE_MAPPUT1_(C,const char *const) +MAKE_MAPPUT1_(A,AstObject *const) +MAKE_MAPPUT1_(P,void *const) +#undef MAKE_MAPPUT1_ + +#define MAKE_MAPGET0_(X,Xtype) \ +int astMapGet0##X##_( AstKeyMap *this, const char *key, Xtype *value, int *status ){ \ + if ( !astOK ) return 0; \ + return (**astMEMBER(this,KeyMap,MapGet0##X))(this,key,value, status ); \ +} +MAKE_MAPGET0_(D,double) +MAKE_MAPGET0_(S,short int) +MAKE_MAPGET0_(B,unsigned char) +MAKE_MAPGET0_(F,float) +MAKE_MAPGET0_(I,int) +MAKE_MAPGET0_(C,const char *) +MAKE_MAPGET0_(A,AstObject *) +MAKE_MAPGET0_(P,void *) +#undef MAKE_MAPGET0_ + + +#define MAKE_MAPGET1_(X,Xtype) \ +int astMapGet1##X##_( AstKeyMap *this, const char *key, int mxval, int *nval, \ + Xtype *value, int *status ){ \ + if ( !astOK ) return 0; \ + return (**astMEMBER(this,KeyMap,MapGet1##X))(this,key,mxval,nval,value,status); \ +} +MAKE_MAPGET1_(B,unsigned char) +MAKE_MAPGET1_(S,short int) +MAKE_MAPGET1_(D,double) +MAKE_MAPGET1_(F,float) +MAKE_MAPGET1_(I,int) +MAKE_MAPGET1_(A,AstObject *) +MAKE_MAPGET1_(P,void *) +#undef MAKE_MAPGET1_ + +#define MAKE_MAPGETELEM_(X,Xtype) \ +int astMapGetElem##X##_( AstKeyMap *this, const char *key, int elem, \ + Xtype *value, int *status ){ \ + if ( !astOK ) return 0; \ + return (**astMEMBER(this,KeyMap,MapGetElem##X))(this,key,elem,value,status); \ +} +MAKE_MAPGETELEM_(B,unsigned char) +MAKE_MAPGETELEM_(S,short int) +MAKE_MAPGETELEM_(D,double) +MAKE_MAPGETELEM_(F,float) +MAKE_MAPGETELEM_(I,int) +MAKE_MAPGETELEM_(A,AstObject *) +MAKE_MAPGETELEM_(P,void *) +#undef MAKE_MAPGETELEM_ + +int astMapGet1C_( AstKeyMap *this, const char *key, int l, int mxval, int *nval, + char *value, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapGet1C))(this,key,l,mxval,nval,value,status); +} + +int astMapGetElemC_( AstKeyMap *this, const char *key, int l, int elem, + char *value, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapGetElemC))(this,key,l,elem,value,status); +} + +#define MAKE_MAPPUTELEM_(X,Xtype) \ +void astMapPutElem##X##_( AstKeyMap *this, const char *key, int elem, \ + Xtype value, int *status ){ \ + if ( !astOK ) return; \ + (**astMEMBER(this,KeyMap,MapPutElem##X))(this,key,elem,value,status); \ +} +MAKE_MAPPUTELEM_(B,unsigned char) +MAKE_MAPPUTELEM_(S,short int) +MAKE_MAPPUTELEM_(D,double) +MAKE_MAPPUTELEM_(F,float) +MAKE_MAPPUTELEM_(I,int) +MAKE_MAPPUTELEM_(A,AstObject *) +MAKE_MAPPUTELEM_(C,const char *) +MAKE_MAPPUTELEM_(P,void *) +#undef MAKE_MAPPUTELEM_ + +void astMapPutU_( AstKeyMap *this, const char *key, const char *comment, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,KeyMap,MapPutU))(this,key,comment,status); +} + +void astMapRemove_( AstKeyMap *this, const char *key, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,KeyMap,MapRemove))(this,key,status); +} +void astMapRename_( AstKeyMap *this, const char *oldkey, const char *newkey, + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,KeyMap,MapRename))(this,oldkey,newkey,status); +} +void astMapCopy_( AstKeyMap *this, AstKeyMap *that, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,KeyMap,MapCopy))(this,that,status); +} +int astMapDefined_( AstKeyMap *this, const char *key, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapDefined))(this,key,status); +} +int astMapSize_( AstKeyMap *this, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapSize))(this,status); +} +int astMapLenC_( AstKeyMap *this, const char *key, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapLenC))(this,key,status); +} +int astMapLength_( AstKeyMap *this, const char *key, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapLength))(this,key,status); +} +int astMapType_( AstKeyMap *this, const char *key, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapType))(this,key,status); +} +int astMapHasKey_( AstKeyMap *this, const char *key, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,MapHasKey))(this,key,status); +} +const char *astMapKey_( AstKeyMap *this, int index, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,KeyMap,MapKey))(this,index,status); +} +const char *astMapIterate_( AstKeyMap *this, int reset, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,KeyMap,MapIterate))(this,reset,status); +} +int astGetSizeGuess_( AstKeyMap *this, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,GetSizeGuess))(this,status); +} +int astTestSizeGuess_( AstKeyMap *this, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,KeyMap,TestSizeGuess))(this,status); +} +void astClearSizeGuess_( AstKeyMap *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,ClearSizeGuess))(this,status); +} +void astSetSizeGuess_( AstKeyMap *this, int sizeguess, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,SetSizeGuess))(this,sizeguess,status); +} + +void astClearMapLocked_( AstKeyMap *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,ClearMapLocked))(this,status); +} +void astSetMapLocked_( AstKeyMap *this, int maplocked, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,SetMapLocked))(this,maplocked,status); +} + +void astClearKeyError_( AstKeyMap *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,ClearKeyError))(this,status); +} +void astSetKeyError_( AstKeyMap *this, int keyerror, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,SetKeyError))(this,keyerror,status); +} + +void astClearSortBy_( AstKeyMap *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,ClearSortBy))(this,status); +} +void astSetSortBy_( AstKeyMap *this, int sortby, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,SetSortBy))(this,sortby,status); +} + +void astClearKeyCase_( AstKeyMap *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,ClearKeyCase))(this,status); +} +void astSetKeyCase_( AstKeyMap *this, int keycase, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,KeyMap,SetKeyCase))(this,keycase,status); +} + diff --git a/keymap.h b/keymap.h new file mode 100644 index 0000000..78c10f1 --- /dev/null +++ b/keymap.h @@ -0,0 +1,566 @@ +#if !defined( KEYMAP_INCLUDED ) /* Include this file only once */ +#define KEYMAP_INCLUDED +/* +*+ +* Name: +* keymap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the KeyMap class. + +* Invocation: +* #include "keymap.h" + +* Description: +* This include file defines the interface to the KeyMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The KeyMap class extends the Object class to represent a set of +* key/value pairs. Keys are strings, and values can be integer, +* floating point, string or Object - scalar of vector. + +* Inheritance: +* The KeyMap class inherits from the Object class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 13-NOV-2004 (DSB): +* Original version. +* 5-JUN-2006 (DSB): +* Added support for single precision entries. +* 7-MAR-2008 (DSB): +* Added support for pointer ("P") entries. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Coordinate objects (parent class) */ + +/* C header files. */ +/* --------------- */ + +/* Macros */ +/* ====== */ +/* Data type constants: */ +#define AST__BADTYPE 0 +#define AST__INTTYPE 1 +#define AST__DOUBLETYPE 2 +#define AST__STRINGTYPE 3 +#define AST__OBJECTTYPE 4 +#define AST__FLOATTYPE 5 +#define AST__POINTERTYPE 6 +#define AST__SINTTYPE 7 +#define AST__UNDEFTYPE 8 +#define AST__BYTETYPE 9 + +/* Define constants used to size global arrays in this module. */ +#define AST__KEYMAP_GETATTRIB_BUFF_LEN 50 /* Max length of string returned by GetAttrib */ +#define AST__KEYMAP_CONVERTVALUE_MAX_STRINGS 50 /* Number of string values to buffer in ConvertValue */ +#define AST__KEYMAP_CONVERTVALUE_BUFF_LEN 50 /* Max. characters in result buffer for ConvertValue */ +#define AST__KEYMAP_MAPKEY_MAX_STRINGS 50 /* Number of string values to buffer in MapKey */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Maximum key length when using case insensitive keymaps */ +#define AST__MXKEYLEN 200 + +/* Type Definitions. */ +/* ================= */ + +/* This structure contains information describing a single generic entry in + a KeyMap. This structure is extended by other structures to hold data of + specific data types. */ + +typedef struct AstMapEntry { + struct AstMapEntry *next; /* Pointer to next structure in unsorted list. */ + const char *key; /* The name used to identify the entry */ + unsigned long hash; /* The full width hash value */ + int type; /* Data type. */ + int nel; /* 0 => scalar, >0 => array with "nel" elements */ + const char *comment; /* Pointer to a comment for the entry */ + int defined; /* Non-zero if the entry value is defined */ + struct AstMapEntry *snext;/* Pointer to next structure in sorted list. */ + struct AstMapEntry *sprev;/* Pointer to previous structure in sorted list. */ + int member; /* No. of values added to KeyMap prior to this one */ + int keymember; /* No. of keys added to KeyMap prior to this one */ + int sortby; /* Used for comunnication with qsort function */ +} AstMapEntry; + +/* KeyMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstKeyMap { + +/* Attributes inherited from the parent class. */ + AstObject object; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int sizeguess; /* Guess at KeyMap size */ + AstMapEntry **table; /* Hash table containing pointers to + the KeyMap entries */ + int *nentry; /* No. of Entries in each table element */ + int mapsize; /* Length of table */ + int keycase; /* Are keys case sensitive? */ + int keyerror; /* Report error if no key? */ + int maplocked; /* Prevent addition of new entries? */ + int sortby; /* How the keys should be sorted */ + AstMapEntry *first; /* Pointer to first structure in sorted list. */ + int nsorted; /* Length of sorted list */ + int member_count; /* Total no. of values ever added to keyMap */ + AstMapEntry *firstA; /* Pointer to first "AST object"-type entry */ + int iter_itab; /* Next hash table entry to return */ + AstMapEntry *iter_entry; /* Next entry to return */ +} AstKeyMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstKeyMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstObjectVtab object_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* MapPut0I)( AstKeyMap *, const char *, int, const char *, int * ); + void (* MapPut0D)( AstKeyMap *, const char *, double, const char *, int * ); + void (* MapPut0B)( AstKeyMap *, const char *, unsigned char, const char *, int * ); + void (* MapPut0S)( AstKeyMap *, const char *, short int, const char *, int * ); + void (* MapPut0F)( AstKeyMap *, const char *, float, const char *, int * ); + void (* MapPut0C)( AstKeyMap *, const char *, const char *, const char *, int * ); + void (* MapPut0A)( AstKeyMap *, const char *, AstObject *, const char *, int * ); + void (* MapPut0P)( AstKeyMap *, const char *, void *, const char *, int * ); + void (* MapPut1I)( AstKeyMap *, const char *, int, const int[], const char *, int * ); + void (* MapPut1D)( AstKeyMap *, const char *, int, const double[], const char *, int * ); + void (* MapPut1B)( AstKeyMap *, const char *, int, const unsigned char[], const char *, int * ); + void (* MapPut1S)( AstKeyMap *, const char *, int, const short int[], const char *, int * ); + void (* MapPut1F)( AstKeyMap *, const char *, int, const float[], const char *, int * ); + void (* MapPut1C)( AstKeyMap *, const char *, int, const char *const [], const char *, int * ); + void (* MapPut1A)( AstKeyMap *, const char *, int, AstObject *const [], const char *, int * ); + void (* MapPut1P)( AstKeyMap *, const char *, int, void *const [], const char *, int * ); + void (* MapPutU)( AstKeyMap *, const char *, const char *, int * ); + int (* MapGet0I)( AstKeyMap *, const char *, int *, int * ); + int (* MapGet0D)( AstKeyMap *, const char *, double *, int * ); + int (* MapGet0B)( AstKeyMap *, const char *, unsigned char *, int * ); + int (* MapGet0S)( AstKeyMap *, const char *, short int *, int * ); + int (* MapGet0F)( AstKeyMap *, const char *, float *, int * ); + int (* MapGet0C)( AstKeyMap *, const char *, const char **, int * ); + int (* MapGet0A)( AstKeyMap *, const char *, AstObject **, int * ); + int (* MapGet0P)( AstKeyMap *, const char *, void **, int * ); + int (* MapGet1A)( AstKeyMap *, const char *, int, int *, AstObject **, int * ); + int (* MapGet1P)( AstKeyMap *, const char *, int, int *, void **, int * ); + int (* MapGet1C)( AstKeyMap *, const char *, int, int, int *, char *, int * ); + int (* MapGet1D)( AstKeyMap *, const char *, int, int *, double *, int * ); + int (* MapGet1B)( AstKeyMap *, const char *, int, int *, unsigned char *, int * ); + int (* MapGet1S)( AstKeyMap *, const char *, int, int *, short int *, int * ); + int (* MapGet1F)( AstKeyMap *, const char *, int, int *, float *, int * ); + int (* MapGet1I)( AstKeyMap *, const char *, int, int *, int *, int * ); + int (* MapGetElemA)( AstKeyMap *, const char *, int, AstObject **, int * ); + int (* MapGetElemP)( AstKeyMap *, const char *, int, void **, int * ); + int (* MapGetElemC)( AstKeyMap *, const char *, int, int, char *, int * ); + int (* MapGetElemD)( AstKeyMap *, const char *, int, double *, int * ); + int (* MapGetElemB)( AstKeyMap *, const char *, int, unsigned char *, int * ); + int (* MapGetElemS)( AstKeyMap *, const char *, int, short int *, int * ); + int (* MapGetElemF)( AstKeyMap *, const char *, int, float *, int * ); + int (* MapGetElemI)( AstKeyMap *, const char *, int, int *, int * ); + void (* MapPutElemA)( AstKeyMap *, const char *, int, AstObject *, int * ); + void (* MapPutElemP)( AstKeyMap *, const char *, int, void *, int * ); + void (* MapPutElemC)( AstKeyMap *, const char *, int, const char *, int * ); + void (* MapPutElemD)( AstKeyMap *, const char *, int, double, int * ); + void (* MapPutElemB)( AstKeyMap *, const char *, int, unsigned char, int * ); + void (* MapPutElemS)( AstKeyMap *, const char *, int, short int, int * ); + void (* MapPutElemF)( AstKeyMap *, const char *, int, float, int * ); + void (* MapPutElemI)( AstKeyMap *, const char *, int, int, int * ); + void (* MapRemove)( AstKeyMap *, const char *, int * ); + void (* MapRename)( AstKeyMap *, const char *, const char *, int * ); + void (* MapCopy)( AstKeyMap *, AstKeyMap *, int * ); + int (* MapSize)( AstKeyMap *, int * ); + int (* MapLength)( AstKeyMap *, const char *, int * ); + int (* MapLenC)( AstKeyMap *, const char *, int * ); + int (* MapType)( AstKeyMap *, const char *, int * ); + int (* MapHasKey)( AstKeyMap *, const char *, int * ); + int (* MapDefined)( AstKeyMap *, const char *, int * ); + const char *(* MapIterate)( AstKeyMap *, int, int * ); + const char *(* MapKey)( AstKeyMap *, int, int * ); + + int (* GetSizeGuess)( AstKeyMap *, int * ); + int (* TestSizeGuess)( AstKeyMap *, int * ); + void (* SetSizeGuess)( AstKeyMap *, int, int * ); + void (* ClearSizeGuess)( AstKeyMap *, int * ); + + int (* GetMapLocked)( AstKeyMap *, int * ); + int (* TestMapLocked)( AstKeyMap *, int * ); + void (* ClearMapLocked)( AstKeyMap *, int * ); + void (* SetMapLocked)( AstKeyMap *, int, int * ); + + int (* GetKeyError)( AstKeyMap *, int * ); + int (* TestKeyError)( AstKeyMap *, int * ); + void (* ClearKeyError)( AstKeyMap *, int * ); + void (* SetKeyError)( AstKeyMap *, int, int * ); + + int (* GetKeyCase)( AstKeyMap *, int * ); + int (* TestKeyCase)( AstKeyMap *, int * ); + void (* ClearKeyCase)( AstKeyMap *, int * ); + void (* SetKeyCase)( AstKeyMap *, int, int * ); + + int (* GetSortBy)( AstKeyMap *, int * ); + int (* TestSortBy)( AstKeyMap *, int * ); + void (* ClearSortBy)( AstKeyMap *, int * ); + void (* SetSortBy)( AstKeyMap *, int, int * ); + +} AstKeyMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstKeyMapGlobals { + AstKeyMapVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__KEYMAP_GETATTRIB_BUFF_LEN + 1 ]; + char *ConvertValue_Strings[ AST__KEYMAP_CONVERTVALUE_MAX_STRINGS ]; + int ConvertValue_Istr; + int ConvertValue_Init; + char ConvertValue_Buff[ AST__KEYMAP_CONVERTVALUE_BUFF_LEN + 1 ]; + char *MapKey_Strings[ AST__KEYMAP_MAPKEY_MAX_STRINGS ]; + int MapKey_Istr; + int MapKey_Init; +} AstKeyMapGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(KeyMap) /* Check class membership */ +astPROTO_ISA(KeyMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstKeyMap *astKeyMap_( const char *, int *, ...); +#else +AstKeyMap *astKeyMapId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstKeyMap *astInitKeyMap_( void *, size_t, int, AstKeyMapVtab *, const char *, int * ); + +/* Vtab initialiser. */ +void astInitKeyMapVtab_( AstKeyMapVtab *, const char *, int * ); + +/* Loader. */ +AstKeyMap *astLoadKeyMap_( void *, size_t, AstKeyMapVtab *, const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitKeyMapGlobals_( AstKeyMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +#if defined(astCLASS) /* Protected */ +int astMapGet0A_( AstKeyMap *, const char *, AstObject **, int * ); +int astMapGet1A_( AstKeyMap *, const char *, int, int *, AstObject **, int * ); +void astMapPut1A_( AstKeyMap *, const char *, int, AstObject *const [], const char *, int * ); +int astMapGetElemA_( AstKeyMap *, const char *, int, AstObject **, int * ); +#else +int astMapGet0AId_( AstKeyMap *, const char *, AstObject **, int * ); +int astMapGet1AId_( AstKeyMap *, const char *, int, int *, AstObject **, int * ); +void astMapPut1AId_( AstKeyMap *, const char *, int, AstObject *const [], const char *, int * ); +int astMapGetElemAId_( AstKeyMap *, const char *, int, AstObject **, int * ); +#endif + +const char *astMapKey_( AstKeyMap *, int, int * ); + + +int astMapGet0B_( AstKeyMap *, const char *, unsigned char *, int * ); +int astMapGet0C_( AstKeyMap *, const char *, const char **, int * ); +int astMapGet0D_( AstKeyMap *, const char *, double *, int * ); +int astMapGet0F_( AstKeyMap *, const char *, float *, int * ); +int astMapGet0I_( AstKeyMap *, const char *, int *, int * ); +int astMapGet0P_( AstKeyMap *, const char *, void **, int * ); +int astMapGet0S_( AstKeyMap *, const char *, short int *, int * ); +int astMapGet1B_( AstKeyMap *, const char *, int, int *, unsigned char *, int * ); +int astMapGet1C_( AstKeyMap *, const char *, int, int, int *, char *, int * ); +int astMapGet1D_( AstKeyMap *, const char *, int, int *, double *, int * ); +int astMapGet1F_( AstKeyMap *, const char *, int, int *, float *, int * ); +int astMapGet1I_( AstKeyMap *, const char *, int, int *, int *, int * ); +int astMapGet1P_( AstKeyMap *, const char *, int, int *, void **, int * ); +int astMapGet1S_( AstKeyMap *, const char *, int, int *, short int *, int * ); +int astMapGetElemB_( AstKeyMap *, const char *, int, unsigned char *, int * ); +int astMapGetElemC_( AstKeyMap *, const char *, int, int, char *, int * ); +int astMapGetElemD_( AstKeyMap *, const char *, int, double *, int * ); +int astMapGetElemF_( AstKeyMap *, const char *, int, float *, int * ); +int astMapGetElemI_( AstKeyMap *, const char *, int, int *, int * ); +int astMapGetElemP_( AstKeyMap *, const char *, int, void **, int * ); +int astMapGetElemS_( AstKeyMap *, const char *, int, short int *, int * ); +int astMapHasKey_( AstKeyMap *, const char *, int * ); +int astMapDefined_( AstKeyMap *, const char *, int * ); +int astMapLenC_( AstKeyMap *, const char *, int * ); +int astMapLength_( AstKeyMap *, const char *, int * ); +int astMapSize_( AstKeyMap *, int * ); +int astMapType_( AstKeyMap *, const char *, int * ); +void astMapCopy_( AstKeyMap *, AstKeyMap *, int * ); +void astMapPut0A_( AstKeyMap *, const char *, AstObject *, const char *, int * ); +void astMapPut0B_( AstKeyMap *, const char *, unsigned char, const char *, int * ); +void astMapPut0C_( AstKeyMap *, const char *, const char *, const char *, int * ); +void astMapPut0D_( AstKeyMap *, const char *, double, const char *, int * ); +void astMapPut0F_( AstKeyMap *, const char *, float, const char *, int * ); +void astMapPut0I_( AstKeyMap *, const char *, int, const char *, int * ); +void astMapPut0P_( AstKeyMap *, const char *, void *, const char *, int * ); +void astMapPut0S_( AstKeyMap *, const char *, short int, const char *, int * ); +void astMapPut1B_( AstKeyMap *, const char *, int, const unsigned char[], const char *, int * ); +void astMapPut1C_( AstKeyMap *, const char *, int, const char *const [], const char *, int * ); +void astMapPut1D_( AstKeyMap *, const char *, int, const double *, const char *, int * ); +void astMapPut1F_( AstKeyMap *, const char *, int, const float *, const char *, int * ); +void astMapPut1I_( AstKeyMap *, const char *, int, const int *, const char *, int * ); +void astMapPut1P_( AstKeyMap *, const char *, int, void *const [], const char *, int * ); +void astMapPut1S_( AstKeyMap *, const char *, int, const short int *, const char *, int * ); +void astMapPutElemA_( AstKeyMap *, const char *, int, AstObject *, int * ); +void astMapPutElemB_( AstKeyMap *, const char *, int, unsigned char, int * ); +void astMapPutElemC_( AstKeyMap *, const char *, int, const char *, int * ); +void astMapPutElemD_( AstKeyMap *, const char *, int, double, int * ); +void astMapPutElemF_( AstKeyMap *, const char *, int, float, int * ); +void astMapPutElemI_( AstKeyMap *, const char *, int, int, int * ); +void astMapPutElemP_( AstKeyMap *, const char *, int, void *, int * ); +void astMapPutElemS_( AstKeyMap *, const char *, int, short int, int * ); +void astMapPutU_( AstKeyMap *, const char *, const char *, int * ); +void astMapRemove_( AstKeyMap *, const char *, int * ); +void astMapRename_( AstKeyMap *, const char *, const char *, int * ); + +#if defined(astCLASS) /* Protected */ +const char *astMapIterate_( AstKeyMap *, int, int * ); + +int astGetSizeGuess_( AstKeyMap *, int * ); +int astTestSizeGuess_( AstKeyMap *, int * ); +void astSetSizeGuess_( AstKeyMap *, int, int * ); +void astClearSizeGuess_( AstKeyMap *, int * ); + +int astGetKeyError_( AstKeyMap *, int * ); +int astTestKeyError_( AstKeyMap *, int * ); +void astSetKeyError_( AstKeyMap *, int, int * ); +void astClearKeyError_( AstKeyMap *, int * ); + +int astGetKeyCase_( AstKeyMap *, int * ); +int astTestKeyCase_( AstKeyMap *, int * ); +void astSetKeyCase_( AstKeyMap *, int, int * ); +void astClearKeyCase_( AstKeyMap *, int * ); + +int astGetSortBy_( AstKeyMap *, int * ); +int astTestSortBy_( AstKeyMap *, int * ); +void astSetSortBy_( AstKeyMap *, int, int * ); +void astClearSortBy_( AstKeyMap *, int * ); + +int astGetMapLocked_( AstKeyMap *, int * ); +int astTestMapLocked_( AstKeyMap *, int * ); +void astSetMapLocked_( AstKeyMap *, int, int * ); +void astClearMapLocked_( AstKeyMap *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckKeyMap(this) astINVOKE_CHECK(KeyMap,this,0) +#define astVerifyKeyMap(this) astINVOKE_CHECK(KeyMap,this,1) + +/* Test class membership. */ +#define astIsAKeyMap(this) astINVOKE_ISA(KeyMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astKeyMap astINVOKE(F,astKeyMap_) +#else +#define astKeyMap astINVOKE(F,astKeyMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitKeyMap(mem,size,init,vtab,name) astINVOKE(O,astInitKeyMap_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitKeyMapVtab(vtab,name) astINVOKE(V,astInitKeyMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadKeyMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadKeyMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckKeyMap to validate KeyMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +#define astMapPutU(this,key,comment) astINVOKE(V,astMapPutU_(astCheckKeyMap(this),key,comment,STATUS_PTR)) +#define astMapPut0I(this,key,value,comment) astINVOKE(V,astMapPut0I_(astCheckKeyMap(this),key,value,comment,STATUS_PTR)) +#define astMapPut0B(this,key,value,comment) astINVOKE(V,astMapPut0B_(astCheckKeyMap(this),key,value,comment,STATUS_PTR)) +#define astMapPut0S(this,key,value,comment) astINVOKE(V,astMapPut0S_(astCheckKeyMap(this),key,value,comment,STATUS_PTR)) +#define astMapPut0D(this,key,value,comment) astINVOKE(V,astMapPut0D_(astCheckKeyMap(this),key,value,comment,STATUS_PTR)) +#define astMapPut0F(this,key,value,comment) astINVOKE(V,astMapPut0F_(astCheckKeyMap(this),key,value,comment,STATUS_PTR)) +#define astMapPut0C(this,key,value,comment) astINVOKE(V,astMapPut0C_(astCheckKeyMap(this),key,value,comment,STATUS_PTR)) +#define astMapPut0A(this,key,value,comment) astINVOKE(V,astMapPut0A_(astCheckKeyMap(this),key,astCheckObject(value),comment,STATUS_PTR)) +#define astMapPut0P(this,key,value,comment) astINVOKE(V,astMapPut0P_(astCheckKeyMap(this),key,value,comment,STATUS_PTR)) +#define astMapPut1I(this,key,size,value,comment) astINVOKE(V,astMapPut1I_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapPut1B(this,key,size,value,comment) astINVOKE(V,astMapPut1B_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapPut1S(this,key,size,value,comment) astINVOKE(V,astMapPut1S_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapPut1D(this,key,size,value,comment) astINVOKE(V,astMapPut1D_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapPut1F(this,key,size,value,comment) astINVOKE(V,astMapPut1F_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapPut1C(this,key,size,value,comment) astINVOKE(V,astMapPut1C_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapGet0I(this,key,value) astINVOKE(V,astMapGet0I_(astCheckKeyMap(this),key,value,STATUS_PTR)) +#define astMapGet0B(this,key,value) astINVOKE(V,astMapGet0B_(astCheckKeyMap(this),key,value,STATUS_PTR)) +#define astMapGet0S(this,key,value) astINVOKE(V,astMapGet0S_(astCheckKeyMap(this),key,value,STATUS_PTR)) +#define astMapGet0D(this,key,value) astINVOKE(V,astMapGet0D_(astCheckKeyMap(this),key,value,STATUS_PTR)) +#define astMapGet0F(this,key,value) astINVOKE(V,astMapGet0F_(astCheckKeyMap(this),key,value,STATUS_PTR)) +#define astMapGet0C(this,key,value) astINVOKE(V,astMapGet0C_(astCheckKeyMap(this),key,value,STATUS_PTR)) +#define astMapGet1I(this,key,mxval,nval,value) astINVOKE(V,astMapGet1I_(astCheckKeyMap(this),key,mxval,nval,value,STATUS_PTR)) +#define astMapGet1B(this,key,mxval,nval,value) astINVOKE(V,astMapGet1B_(astCheckKeyMap(this),key,mxval,nval,value,STATUS_PTR)) +#define astMapGet1S(this,key,mxval,nval,value) astINVOKE(V,astMapGet1S_(astCheckKeyMap(this),key,mxval,nval,value,STATUS_PTR)) +#define astMapGet1D(this,key,mxval,nval,value) astINVOKE(V,astMapGet1D_(astCheckKeyMap(this),key,mxval,nval,value,STATUS_PTR)) +#define astMapGet1F(this,key,mxval,nval,value) astINVOKE(V,astMapGet1F_(astCheckKeyMap(this),key,mxval,nval,value,STATUS_PTR)) +#define astMapGet1C(this,key,l,mxval,nval,value) astINVOKE(V,astMapGet1C_(astCheckKeyMap(this),key,l,mxval,nval,value,STATUS_PTR)) +#define astMapGetElemI(this,key,elem,value) astINVOKE(V,astMapGetElemI_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapGetElemB(this,key,elem,value) astINVOKE(V,astMapGetElemB_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapGetElemS(this,key,elem,value) astINVOKE(V,astMapGetElemS_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapGetElemD(this,key,elem,value) astINVOKE(V,astMapGetElemD_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapGetElemF(this,key,elem,value) astINVOKE(V,astMapGetElemF_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapGetElemC(this,key,l,elem,value) astINVOKE(V,astMapGetElemC_(astCheckKeyMap(this),key,l,elem,value,STATUS_PTR)) +#define astMapGetElemP(this,key,elem,value) astINVOKE(V,astMapGetElemP_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapPutElemA(this,key,elem,value) astINVOKE(V,astMapPutElemA_(astCheckKeyMap(this),key,elem,astCheckObject(value),STATUS_PTR)) +#define astMapPutElemI(this,key,elem,value) astINVOKE(V,astMapPutElemI_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapPutElemB(this,key,elem,value) astINVOKE(V,astMapPutElemB_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapPutElemS(this,key,elem,value) astINVOKE(V,astMapPutElemS_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapPutElemD(this,key,elem,value) astINVOKE(V,astMapPutElemD_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapPutElemF(this,key,elem,value) astINVOKE(V,astMapPutElemF_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapPutElemC(this,key,elem,value) astINVOKE(V,astMapPutElemC_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapPutElemP(this,key,elem,value) astINVOKE(V,astMapPutElemP_(astCheckKeyMap(this),key,elem,value,STATUS_PTR)) +#define astMapRemove(this,key) astINVOKE(V,astMapRemove_(astCheckKeyMap(this),key,STATUS_PTR)) +#define astMapRename(this,oldkey,newkey) astINVOKE(V,astMapRename_(astCheckKeyMap(this),oldkey,newkey,STATUS_PTR)) +#define astMapCopy(this,that) astINVOKE(V,astMapCopy_(astCheckKeyMap(this),astCheckKeyMap(that),STATUS_PTR)) +#define astMapSize(this) astINVOKE(V,astMapSize_(astCheckKeyMap(this),STATUS_PTR)) +#define astMapLength(this,key) astINVOKE(V,astMapLength_(astCheckKeyMap(this),key,STATUS_PTR)) +#define astMapLenC(this,key) astINVOKE(V,astMapLenC_(astCheckKeyMap(this),key,STATUS_PTR)) +#define astMapHasKey(this,key) astINVOKE(V,astMapHasKey_(astCheckKeyMap(this),key,STATUS_PTR)) +#define astMapDefined(this,key) astINVOKE(V,astMapDefined_(astCheckKeyMap(this),key,STATUS_PTR)) +#define astMapKey(this,index) astINVOKE(V,astMapKey_(astCheckKeyMap(this),index,STATUS_PTR)) +#define astMapType(this,key) astINVOKE(V,astMapType_(astCheckKeyMap(this),key,STATUS_PTR)) +#define astMapGet0P(this,key,value) astINVOKE(V,astMapGet0P_(astCheckKeyMap(this),key,value,STATUS_PTR)) +#define astMapGet1P(this,key,mxval,nval,value) astINVOKE(V,astMapGet1P_(astCheckKeyMap(this),key,mxval,nval,value,STATUS_PTR)) +#define astMapPut1P(this,key,size,value,comment) astINVOKE(V,astMapPut1P_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astMapGet0A(this,key,value) astINVOKE(V,astMapGet0A_(astCheckKeyMap(this),key,(AstObject **)(value),STATUS_PTR)) +#define astMapGet1A(this,key,mxval,nval,value) astINVOKE(V,astMapGet1A_(astCheckKeyMap(this),key,mxval,nval,(AstObject **)(value),STATUS_PTR)) +#define astMapPut1A(this,key,size,value,comment) astINVOKE(V,astMapPut1A_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapGetElemA(this,key,elem,value) astINVOKE(V,astMapGetElemA_(astCheckKeyMap(this),key,elem,(AstObject **)(value),STATUS_PTR)) +#define astMapIterate(this,reset) astINVOKE(V,astMapIterate_(astCheckKeyMap(this),reset,STATUS_PTR)) + +#define astClearSizeGuess(this) \ +astINVOKE(V,astClearSizeGuess_(astCheckKeyMap(this),STATUS_PTR)) +#define astGetSizeGuess(this) \ +astINVOKE(V,astGetSizeGuess_(astCheckKeyMap(this),STATUS_PTR)) +#define astSetSizeGuess(this,sizeguess) \ +astINVOKE(V,astSetSizeGuess_(astCheckKeyMap(this),sizeguess,STATUS_PTR)) +#define astTestSizeGuess(this) \ +astINVOKE(V,astTestSizeGuess_(astCheckKeyMap(this),STATUS_PTR)) + +#define astClearKeyError(this) \ +astINVOKE(V,astClearKeyError_(astCheckKeyMap(this),STATUS_PTR)) +#define astGetKeyError(this) \ +astINVOKE(V,astGetKeyError_(astCheckKeyMap(this),STATUS_PTR)) +#define astSetKeyError(this,keyerror) \ +astINVOKE(V,astSetKeyError_(astCheckKeyMap(this),keyerror,STATUS_PTR)) +#define astTestKeyError(this) \ +astINVOKE(V,astTestKeyError_(astCheckKeyMap(this),STATUS_PTR)) + +#define astClearKeyCase(this) \ +astINVOKE(V,astClearKeyCase_(astCheckKeyMap(this),STATUS_PTR)) +#define astGetKeyCase(this) \ +astINVOKE(V,astGetKeyCase_(astCheckKeyMap(this),STATUS_PTR)) +#define astSetKeyCase(this,keycase) \ +astINVOKE(V,astSetKeyCase_(astCheckKeyMap(this),keycase,STATUS_PTR)) +#define astTestKeyCase(this) \ +astINVOKE(V,astTestKeyCase_(astCheckKeyMap(this),STATUS_PTR)) + +#define astClearSortBy(this) \ +astINVOKE(V,astClearSortBy_(astCheckKeyMap(this),STATUS_PTR)) +#define astGetSortBy(this) \ +astINVOKE(V,astGetSortBy_(astCheckKeyMap(this),STATUS_PTR)) +#define astSetSortBy(this,sortby) \ +astINVOKE(V,astSetSortBy_(astCheckKeyMap(this),sortby,STATUS_PTR)) +#define astTestSortBy(this) \ +astINVOKE(V,astTestSortBy_(astCheckKeyMap(this),STATUS_PTR)) + +#define astClearMapLocked(this) \ +astINVOKE(V,astClearMapLocked_(astCheckKeyMap(this),STATUS_PTR)) +#define astGetMapLocked(this) \ +astINVOKE(V,astGetMapLocked_(astCheckKeyMap(this),STATUS_PTR)) +#define astSetMapLocked(this,maplocked) \ +astINVOKE(V,astSetMapLocked_(astCheckKeyMap(this),maplocked,STATUS_PTR)) +#define astTestMapLocked(this) \ +astINVOKE(V,astTestMapLocked_(astCheckKeyMap(this),STATUS_PTR)) + + +#else +#define astMapGet0A(this,key,value) astINVOKE(V,astMapGet0AId_(astCheckKeyMap(this),key,(AstObject **)(value),STATUS_PTR)) +#define astMapGet1A(this,key,mxval,nval,value) astINVOKE(V,astMapGet1AId_(astCheckKeyMap(this),key,mxval,nval,(AstObject **)(value),STATUS_PTR)) +#define astMapPut1A(this,key,size,value,comment) astINVOKE(V,astMapPut1AId_(astCheckKeyMap(this),key,size,value,comment,STATUS_PTR)) +#define astMapGetElemA(this,key,elem,value) astINVOKE(V,astMapGetElemAId_(astCheckKeyMap(this),key,elem,(AstObject **)(value),STATUS_PTR)) +#endif + +#endif + diff --git a/leap-seconds.png b/leap-seconds.png new file mode 100644 index 0000000..bee2559 Binary files /dev/null and b/leap-seconds.png differ diff --git a/loader.c b/loader.c new file mode 100644 index 0000000..58bd04e --- /dev/null +++ b/loader.c @@ -0,0 +1,192 @@ +#define astCLASS +#include "axis.h" +#include "box.h" +#include "channel.h" +#include "circle.h" +#include "cmpframe.h" +#include "cmpmap.h" +#include "cmpregion.h" +#include "dsbspecframe.h" +#include "dssmap.h" +#include "ellipse.h" +#include "fitschan.h" +#include "fluxframe.h" +#include "timeframe.h" +#include "timemap.h" +#include "frame.h" +#include "frameset.h" +#include "grismmap.h" +#include "interval.h" +#include "intramap.h" +#include "keymap.h" +#include "loader.h" +#include "lutmap.h" +#include "mapping.h" +#include "mathmap.h" +#include "matrixmap.h" +#include "nullregion.h" +#include "object.h" +#include "pcdmap.h" +#include "permmap.h" +#include "plot.h" +#include "plot3d.h" +#include "pointlist.h" +#include "pointset.h" +#include "polygon.h" +#include "polymap.h" +#include "prism.h" +#include "normmap.h" +#include "ratemap.h" +#include "region.h" +#include "shiftmap.h" +#include "skyaxis.h" +#include "skyframe.h" +#include "slamap.h" +#include "specfluxframe.h" +#include "specframe.h" +#include "specmap.h" +#include "sphmap.h" +#include "tranmap.h" +#include "selectormap.h" +#include "switchmap.h" +#include "unitmap.h" +#include "wcsmap.h" +#include "winmap.h" +#include "xmlchan.h" +#include "zoommap.h" +#include "stc.h" +#include "stcresourceprofile.h" +#include "stcsearchlocation.h" +#include "stccatalogentrylocation.h" +#include "stcobsdatalocation.h" +#include "stcschan.h" +#include "table.h" +#include "fitstable.h" + +#include "error.h" +#include "ast_err.h" +#include +#include + +/* +*+ +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 18-NOV-1997 (RFWS): +* Original version. +* 18-MAR-1998 (RFWS): +* Added the IntraMap class. +* 3-JUN-1999 (RFWS): +* Added the PcdMap class. +* 17-AUG-1999 (RFWS): +* Added the MathMap class. +* 8-JAN-2003 (DSB): +* Added the SpecMap and SpecFrame classes. +* 15-JUL-2003 (DSB): +* Added the GrsimMap class. +* 6-FEB-2009 (DSB): +* Added the StcsChan class. +*- +*/ + +AstLoaderType *astGetLoader( const char *class, int *status ) { + if ( !astOK ) return NULL; + +#define LOAD(name) \ +if ( !strcmp( class, #name ) ) return (AstLoaderType *) astLoad##name##_ + + LOAD(Axis); + LOAD(Box); + LOAD(Channel); + LOAD(Circle); + LOAD(CmpFrame); + LOAD(CmpMap); + LOAD(CmpRegion); + LOAD(DSBSpecFrame); + LOAD(DssMap); + LOAD(Ellipse); + LOAD(FitsChan); + LOAD(FitsTable); + LOAD(FluxFrame); + LOAD(Frame); + LOAD(FrameSet); + LOAD(GrismMap); + LOAD(Interval); + LOAD(IntraMap); + LOAD(KeyMap); + LOAD(LutMap); + LOAD(Mapping); + LOAD(MathMap); + LOAD(MatrixMap); + LOAD(NullRegion); + LOAD(Object); + LOAD(PcdMap); + LOAD(PermMap); + LOAD(Plot); + LOAD(Plot3D); + LOAD(PointList); + LOAD(PointSet); + LOAD(PolyMap); + LOAD(Polygon); + LOAD(Prism); + LOAD(NormMap); + LOAD(RateMap); + LOAD(Region); + LOAD(ShiftMap); + LOAD(SkyAxis); + LOAD(SkyFrame); + LOAD(SlaMap); + LOAD(SpecFluxFrame); + LOAD(SpecFrame); + LOAD(SpecMap); + LOAD(SphMap); + LOAD(SelectorMap); + LOAD(SwitchMap); + LOAD(Table); + LOAD(TimeFrame); + LOAD(TimeMap); + LOAD(TranMap); + LOAD(UnitMap); + LOAD(WcsMap); + LOAD(WinMap); + LOAD(XmlChan); + LOAD(ZoomMap); + + LOAD(StcsChan); + LOAD(Stc); + LOAD(StcResourceProfile); + LOAD(StcSearchLocation); + LOAD(StcCatalogEntryLocation); + LOAD(StcObsDataLocation); + + astError( AST__OCLUK, "astGetLoader: Object of unknown class \"%s\" cannot " + "be loaded.", status, class ); + return NULL; +#undef LOAD +} + + + diff --git a/loader.h b/loader.h new file mode 100644 index 0000000..6227a81 --- /dev/null +++ b/loader.h @@ -0,0 +1,49 @@ +#if !defined( LOADER_INCLUDED ) /* Include this file only once */ +#define LOADER_INCLUDED +/* +*+ + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 18-NOV-1997 (RFWS): +* Original version. +*- +*/ + +#include "object.h" +#include "channel.h" + +#if defined(astCLASS) /* Protected */ + +typedef AstObject *(AstLoaderType)( void *, size_t, AstObjectVtab *, + const char *, AstChannel *, int * ); + +AstLoaderType *astGetLoader( const char *, int * ); + +#endif +#endif + + + diff --git a/lutmap.c b/lutmap.c new file mode 100644 index 0000000..76faced --- /dev/null +++ b/lutmap.c @@ -0,0 +1,2629 @@ +/* +*class++ +* Name: +* LutMap + +* Purpose: +* Transform 1-dimensional coordinates using a lookup table. + +* Constructor Function: +c astLutMap +f AST_LUTMAP + +* Description: +* A LutMap is a specialised form of Mapping which transforms +* 1-dimensional coordinates by using linear interpolation in a +* lookup table. +* +* Each input coordinate value is first scaled to give the index of +* an entry in the table by subtracting a starting value (the input +* coordinate corresponding to the first table entry) and dividing +* by an increment (the difference in input coordinate value +* between adjacent table entries). +* +* The resulting index will usually contain a fractional part, so +* the output coordinate value is then generated by interpolating +* linearly between the appropriate entries in the table. If the +* index lies outside the range of the table, linear extrapolation +* is used based on the two nearest entries (i.e. the two entries +* at the start or end of the table, as appropriate). If either of the +* entries used for the interplation has a value of AST__BAD, then the +* interpolated value is returned as AST__BAD. +* +* If the lookup table entries increase or decrease monotonically +* (ignoring any flat sections), then the inverse transformation may +* also be performed. + +* Inheritance: +* The LutMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* LutMap also has the following attributes: +* +* - LutEpsilon: The relative error of the values in the table. +* - LutInterp: The interpolation method to use between table entries. + +* Functions: +c The LutMap class does not define any new functions beyond those +f The LutMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2007-2011 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (JAC, UCLan) + +* History: +* 8-JUL-1997 (RFWS): +* Original version. +* 10-JUL-1997 (RFWS): +* Added the MapMerge function. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitLutMapVtab +* method. +* 12-JAN-2004 (DSB): +* Check for AST__BAD values in the supplied lut array. +* 17-MAR-2006 (DSB): +* - MapMerge changed so that a LutMap will cancel with its own +* inverse. +* - Added attribute LutInterp +* 10-MAY-2006 (DSB): +* Override astEqual. +* 4-OCT-2006 (DSB): +* - Correct "mintick" to "lutinterp" in SetAttrib. +* - Do not include bad values in the dumped LUT array. +* 8-NOV-2007 (DSB): +* - Take account of the requested invert flag when comparing two +* neighbouring LutMaps for equality in MapMerge. +* 19-NOV-2010 (DSB): +* Added (protected) astGetLutMapInfo function. +* 24-JAN-2011 (DSB): +* Implement an inverse transformation even if the coordinate +* array contains sections of equal or bad values. The inverse +* transformation will generate bad values if used within such +* regions of the coordinate array. +* 6-JUL-2011 (DSB): +* Avoid indexing the lut array beyond the end when doing an +* inverse transform. +* 2-OCT-2012 (DSB): +* Check for Infs as well as NaNs. +* 21-MAY-2015 (DSB): +* Aded LutEpsilon +* 23-SEP-2015 (DSB): +* The GetMonotonic function had a bug that caused all LutMaps +* to be considered monotonic, and thus have an inverse +* transformation. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS LutMap + +#define LINEAR 0 +#define NEAR 1 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "winmap.h" /* Linear mappings between windows */ +#include "channel.h" /* I/O channels */ +#include "unitmap.h" /* Unit mappings */ +#include "lutmap.h" /* Interface definition for this class */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(LutMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(LutMap,Class_Init) +#define class_vtab astGLOBAL(LutMap,Class_Vtab) +#define getattrib_buff astGLOBAL(LutMap,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstLutMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstLutMap *astLutMapId_( int, const double [], double, double, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int GetLinear( AstMapping *, int * ); +static int GetMonotonic( int, const double *, int *, double **, int **, int **, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static double *GetLutMapInfo( AstLutMap *, double *, double *, int *, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static int GetLutInterp( AstLutMap *, int * ); +static int TestLutInterp( AstLutMap *, int * ); +static void ClearLutInterp( AstLutMap *, int * ); +static void SetLutInterp( AstLutMap *, int, int * ); + +static double GetLutEpsilon( AstLutMap *, int * ); +static int TestLutEpsilon( AstLutMap *, int * ); +static void ClearLutEpsilon( AstLutMap *, int * ); +static void SetLutEpsilon( AstLutMap *, double, int * ); + +/* Member functions. */ +/* ================= */ +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a LutMap. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* LutMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* LutMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the LutMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstLutMap *this; /* Pointer to the LutMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the LutMap structure. */ + this = (AstLutMap *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* LutInterp. */ +/* ---------- */ + if ( !strcmp( attrib, "lutinterp" ) ) { + astClearLutInterp( this ); + +/* LutEpsilon. */ +/* ------------- */ + } else if ( !strcmp( attrib, "lutepsilon" ) ) { + astClearLutEpsilon( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two LutMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* LutMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two LutMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a LutMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the LutMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstLutMap *that; + AstLutMap *this; + int i; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two LutMap structures. */ + this = (AstLutMap *) this_object; + that = (AstLutMap *) that_object; + +/* Check the second object is a LutMap. We know the first is a + LutMap since we have arrived at this implementation of the virtual + function. */ + if( astIsALutMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two LutMaps differ, it may still be possible + for them to be equivalent. First compare the LutMaps if their Invert + flags are the same. In this case all the attributes of the two LutMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + + if( astEQUAL( this->start, that->start ) && + astEQUAL( this->inc, that->inc ) && + this->nlut == that->nlut && + this->lutinterp == that->lutinterp ){ + + result = 1; + for( i = 0; i < this->nlut; i++ ) { + if( !astEQUAL( (this->lut)[ i ], (that->lut)[ i ] ) ) { + result = 0; + break; + } + } + } + +/* If the Invert flags for the two LutMaps differ, the attributes of the two + LutMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a LutMap, Invert flags must be equal. */ + result = 0; + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a LutMap. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* LutMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a LutMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the LutMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the LutMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the LutMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstLutMap *this; /* Pointer to the LutMap structure */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const char *result; /* Pointer value to return */ + double luteps; /* LutEpsilon attribute value */ + int lutinterp; /* LutInterp attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the LutMap structure. */ + this = (AstLutMap *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* LutInterp. */ +/* ---------- */ + if ( !strcmp( attrib, "lutinterp" ) ) { + lutinterp = astGetLutInterp( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", lutinterp ); + result = getattrib_buff; + } + +/* LutEpsilon. */ +/* ------------- */ + } else if ( !strcmp( attrib, "lutepsilon" ) ) { + luteps = astGetLutEpsilon( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, luteps ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static int GetLinear( AstMapping *this_mapping, int *status ) { +/* +* Name: +* GetLinear + +* Purpose: +* Determine if a LutMap implements a linear coordinate transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* int GetLinear( AstMapping *this, int *status ) + +* Class Membership: +* LutMap member function. + +* Description: +* This function returns a boolean value to indicate if the LutMap +* supplied is equivalent to a linear coordinate +* transformation. This will be the case if the lookup table +* elements increase or decrease linearly. + +* Parameters: +* this +* Pointer to the LutMap. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstLutMap *this; /* Pointer to the LutMap structure */ + double *lut; /* Pointer to the lookup table */ + double eps; /* Relative error on the table values */ + double fract; /* Fractional position within table */ + double hi; /* Largest value */ + double interp; /* Interpolated value */ + double lo; /* Smallest value */ + double tol1; /* First tolerance estimate */ + double tol2; /* Second tolerance estimate */ + double tol; /* Tolerance value used */ + int ilut; /* Loop counter for table elements */ + int linear; /* Result to be returned */ + int nlut; /* Number of lookup table elements */ + +/* Initialise. */ + linear = 0; + +/* Check the global error status. */ + if ( !astOK ) return linear; + +/* Obtain a pointer to the LutMap structure. */ + this = (AstLutMap *) this_mapping; + +/* Nearest neighbour LutMaps are not considered to be linear because of + the discontinuities at the start and end of the table. */ + if( astGetLutInterp( this ) != NEAR ) { + +/* Obtain the lookup table details. */ + lut = this->lut; + nlut = this->nlut; + +/* Loop to identify the largest and smallest values in the lookup + table. */ + lo = DBL_MAX; + hi = -DBL_MAX; + for ( ilut = 0; ilut < nlut; ilut++ ) { + if ( lut[ ilut ] > hi ) hi = lut[ ilut ]; + if ( lut[ ilut ] < lo ) lo = lut[ ilut ]; + } + +/* Check if the values are all the same (this makes the LutMap + linear, although it will have no inverse). */ + linear = ( hi == lo ); + if ( !linear ) { + +/* Get the relative error associated with the table values. */ + eps = astGetLutEpsilon( this ); + +/* Form a tolerance estimate based on the overall range of values in + the lookup table. */ + tol1 = fabs( hi - lo ) * eps; + +/* Now loop to inspect all the lookup table elements except the first + and last. */ + linear = 1; + for ( ilut = 1; ilut < ( nlut - 1 ); ilut++ ) { + +/* Calculate the fractional position of the current element within the + table. */ + fract = ( (double) ilut ) / ( (double) ( nlut - 1 ) ); + +/* Calculate the value it should have if the table is linear by + interpolating between the first and last values. */ + interp = lut[ 0 ] * ( 1.0 - fract ) + lut[ nlut - 1 ] * fract; + +/* Form a second tolerance estimate from this interpolated + value. Select whichever tolerance estimate is larger (this avoids + problems when values are near zero). */ + tol2 = fabs( interp ) * eps; + tol = ( tol1 > tol2 ) ? tol1 : tol2; + +/* Test for linearity within a small multiple of the tolerance. */ + linear = ( fabs( lut[ ilut ] - interp ) <= ( 2.0 * tol ) ); + if ( !linear ) break; + } + } + } + +/* Return the result. */ + return linear; +} + +static double *GetLutMapInfo( AstLutMap *this, double *start, double *inc, + int *nlut, int *status ){ +/* +* Name: +* GetLutMapInfo + +* Purpose: +* Return information about a LutMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "permmap.h" +* double *astGetLutMapInfo( AstLutMap *this, double *start, double *inc, +* int *nlut, int *status ) + +* Class Membership: +* LutMap method + +* Description: +* This function returns information about the supplied LutMap. + +* Parameters: +* this +* Pointer to the LutMap. +* start +* Pointer to a double in which to return the "start" value +* supplied when the LutMap was created. +* inc +* Pointer to a double in which to return the "inc" value +* supplied when the LutMap was created. +* nlut +* Pointer to a double in which to return the number of values in +* the LUT. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array holding a copy of the +* look-up table. This is an array of "nlut" elements, giving the +* output values for input values "start", "start+inc", "start+2*inc", +* etc. The pointer should be freed using astFree when no longer +* needed. + +* Notes: +* - A value of NULL will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Store the scalar values. */ + *start = this->start; + *inc = this->inc; + *nlut = this->nlut; + +/* Return a copy of the look up table. */ + return astStore( NULL, this->lut, sizeof( double )*this->nlut ); +} + +static int GetMonotonic( int nlut, const double *lut, int *nluti, double **luti, + int **flagsi, int **indexi, int *status ) { +/* +* Name: +* GetMonotonic + +* Purpose: +* Determine if a array is monotonic increasing or decreasing. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* int GetMonotonic( int nlut, const double *lut, int *nluti, double **luti, +* int **flagsi, int **indexi, int *status ) + +* Class Membership: +* LutMap member function. + +* Description: +* This function returns a flag indiciating the supplied array is +* monotonic increasing, monotonic decreasing, or non-monotonic. +* Sections of equal or bad values do not invalidate an otherwise +* monotonic array. +* +* It also returns information needed to implement the inverse +* transformation of a LutMap. + +* Parameters: +* nlut +* The length of the array. +* lut +* The array to check. +* nluti +* Address of an int in which to store the length of the returned +* "luti" and "flags" arrays. +* luti +* Address at which to return a pointer to a newly allocated array +* containing "*nluti" elements. This is a copy of the supplied +* "lut" array but with any bad or NaN values omitted. Subsequent +* elements are shuffled down to fill the holes left by removing +* these bad values. A NULL pointer is returned if there are no bad +* values in "lut". +* flagsi +* Address at which to return a pointer to a newly allocated array +* containing "*nluti" elements. Each element is non-zero if the +* corresponding value stored in "luti" was adjacent to a bad value +* in the supplied "lut" array. A NULL pointer is returned if there +* are no bad values in "lut". +* indexi +* Address at which to return a pointer to a newly allocated array +* containing "*nluti" elements. Each element is the index within +* "lut" of the corresponding value stored in "luti". A NULL pointer +* is returned if there are no bad values in "lut". +* status +* Pointer to the inherited status variable. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + const double *p3; + double *p1; + double lval; + int *p2; + int *p4; + int ilut; + int nbad; + int result; + +/* Initialise. */ + result = 0; + *nluti = 0; + *luti = NULL; + *flagsi = NULL; + *indexi = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* As yet we have not found a good value. */ + lval = AST__BAD; + +/* As yet we have not found a bad value. */ + nbad = 0; + +/* Loop round the supplied array, ignoring bad (AST__BAD or NaN) values. */ + for ( ilut = 0; ilut < nlut; ilut++ ) { + if( !astISBAD( lut[ ilut ] ) ) { + +/* If this is the first good value, record it. */ + if( lval == AST__BAD ) { + lval = lut[ ilut ]; + +/* If this is not the first good value, ignore it if it is equal to the + previous god value. */ + } else if( lut[ ilut ] != lval ) { + +/* Set the returned flag on the basis of the first pair of good values. */ + if( result == 0 ) { + result = ( lut[ ilut ] > lval ) ? 1 : -1; + +/* For subsequent pairs of good values, check the pair increases or + decreases in the same way as the first pair. Reset the returned value + to zero and break if not. */ + } else if( result == 1 && lut[ ilut ] < lval ) { + result = 0; + break; + + } else if( result == -1 && lut[ ilut ] >lval ) { + result = 0; + break; + } + +/* Record the current good value. */ + lval = lut[ ilut ]; + } + } else { + nbad++; + } + } + +/* If any bad values were found, we now allocate the required returned + arrays. */ + if( nbad ) { + *nluti = nlut - nbad; + *luti = astMalloc( sizeof( double )*( *nluti ) ); + *flagsi = astMalloc( sizeof( double )*( *nluti ) ); + *indexi = astMalloc( sizeof( double )*( *nluti ) ); + + if( astOK ) { + +/* Into "luti" copy all good values from "lut", shuffling down values to + fill holes left by bad values. Into "flagsi", store a flag indicating + if the corresponding "luti" value was adjacent to a bad value in the + full "lut" array. */ + p1 = *luti; + p2 = *flagsi; + p3 = lut; + p4 = *indexi; + +/* Do the first input point (it has no lower neighbour). */ + if( !astISBAD( *p3 ) ) { + *(p1++) = *p3; + *(p2++) = astISBAD( p3[ +1 ] ); + *(p4++) = 0; + } + +/* Do all remaining input points except for the last one. */ + for ( ilut = 1,p3++; ilut < nlut-1; ilut++,p3++ ) { + if( !astISBAD( *p3 ) ) { + *(p1++) = *p3; + *(p2++) = astISBAD( p3[ -1 ] ) || astISBAD( p3[ +1 ] ); + *(p4++) = ilut; + } + } + +/* Do the last input point (it has no upper neighbour). */ + if( !astISBAD( *p3 ) ) { + *p1 = *p3; + *p2 = astISBAD( p3[ -1 ] ); + *p4 = ilut; + } + } + } + + +/* Return the result. */ + return result; +} + +void astInitLutMapVtab_( AstLutMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitLutMapVtab + +* Purpose: +* Initialise a virtual function table for a LutMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "lutmap.h" +* void astInitLutMapVtab( AstLutMapVtab *vtab, const char *name ) + +* Class Membership: +* LutMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the LutMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsALutMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->ClearLutInterp = ClearLutInterp; + vtab->GetLutInterp = GetLutInterp; + vtab->SetLutInterp = SetLutInterp; + vtab->TestLutInterp = TestLutInterp; + vtab->ClearLutEpsilon = ClearLutEpsilon; + vtab->GetLutEpsilon = GetLutEpsilon; + vtab->SetLutEpsilon = SetLutEpsilon; + vtab->TestLutEpsilon = TestLutEpsilon; + vtab->GetLutMapInfo = GetLutMapInfo; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the class dump, copy and delete functions.*/ + astSetDump( vtab, Dump, "LutMap", + "Map 1-d coordinates using a lookup table" ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + astSetDelete( (AstObjectVtab *) vtab, Delete ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a LutMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* LutMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated LutMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated LutMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated LutMap which is to be merged with +* its neighbours. This should be a cloned copy of the LutMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* LutMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated LutMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstLutMap *map; /* Pointer to LutMap */ + AstLutMap *neb; /* Pointer to neighbouring LutMap */ + AstMapping *new; /* Pointer to replacement Mapping */ + double a1; /* First input coordinate value */ + double a2; /* Second input coordinate value */ + double b1; /* First output coordinate value */ + double b2; /* Second output coordinate value */ + int equal; /* Are LutMaps equal? */ + int i; /* Mapping index */ + int ilo; /* Index of lower LutMap */ + int invneb; /* Should the neigbour be used inverted? */ + int old_inv; /* Original Invert value for neigbour */ + int result; /* Result value to return */ + int simpler; /* Mapping simplified? */ + +/* Initialise the returned result. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the nominated LutMap. */ + map = (AstLutMap *) ( *map_list )[ where ]; + +/* See if the LutMap is linear. If so, it can probably be + simplified. */ + simpler = GetLinear( (AstMapping *) map, status ); + if ( simpler ) { + +/* Obtain the range of input values corresponding to the first and + last lookup table elements. */ + a1 = map->start; + a2 = map->start + map->inc * ( map->nlut - 1 ); + +/* Obtain the corresponding range of output values and check these + values are not the same. */ + b1 = map->lut[ 0 ]; + b2 = map->lut[ map->nlut - 1 ]; + if ( b1 != b2 ) { + +/* Create a new WinMap that implements an equivalent linear Mapping, + allowing for the invert flag associated with the LutMap. */ + if ( !( *invert_list )[ where ] ) { + new = (AstMapping *) astWinMap( 1, &a1, &a2, &b1, &b2, "", status ); + } else { + new = (AstMapping *) astWinMap( 1, &b1, &b2, &a1, &a2, "", status ); + } + +/* If OK, annul the original LutMap pointer and substitute the new + one. Also clear the associated invert flag. */ + if ( astOK ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = new; + ( *invert_list )[ where ] = 0; + +/* Assign the result value. */ + result = where; + } + } + +/* Otherwise, see if the LutMap is in series with its own inverse. If so + the pair of LutMaps can be replaced by a UnitMap. */ + } else if( series ) { + +/* Is the higher neighbour a LutMap? If so get a pointer to it, and + note the index of the lower of the two adjacent LutMaps. */ + if( where < ( *nmap - 1 ) && + astIsALutMap( ( *map_list )[ where + 1 ] ) ){ + neb = (AstLutMap *) ( *map_list )[ where + 1 ]; + invneb = ( *invert_list )[ where + 1 ]; + ilo = where; + +/* If not, is the lower neighbour a LutMap? If so get a pointer to it, + and note the index of the lower of the two adjacent LutMaps. */ + } else if( where > 0 && + astIsALutMap( ( *map_list )[ where - 1 ] ) ){ + neb = (AstLutMap *) ( *map_list )[ where - 1 ]; + invneb = ( *invert_list )[ where - 1 ]; + ilo = where - 1; + + } else { + neb = NULL; + } + +/* If a neighbouring LutMap was found, we can replace the pair by a + UnitMap if the two LutMaps are equal but have opposite values for + their Invert flags. Temporarily invert the neighbour, then compare + the two LutMaps for equality, then re-invert the neighbour. */ + if( neb ) { + old_inv = astGetInvert( neb ); + astSetInvert( neb, invneb ); + astInvert( neb ); + equal = astEqual( map, neb ); + astSetInvert( neb, old_inv ); + +/* If the two LutMaps are equal but opposite, annul the first of the two + Mappings, and replace it with a UnitMap. Also set the invert flag. */ + if( equal ) { + new = (AstMapping *) astUnitMap( 1, "", status ); + (void) astAnnul( ( *map_list )[ ilo ] ); + ( *map_list )[ ilo ] = new; + ( *invert_list )[ ilo ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ ilo + 1 ] ); + for ( i = ilo + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = where; + } + } + } + +/* If an error occurred, clear the returned result. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a LutMap. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* LutMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a LutMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the LutMap. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstLutMap *this; /* Pointer to the LutMap structure */ + double luteps; /* LutEpsilon attribute value */ + int lutinterp; /* LutInterp attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the LutMap structure. */ + this = (AstLutMap *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* LutInterp. */ +/* ---------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "lutinterp= %d %n", &lutinterp, &nc ) ) + && ( nc >= len ) ) { + astSetLutInterp( this, lutinterp ); + +/* LutEpsilon. */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "lutepsilon= %lf %n", &luteps, &nc ) ) + && ( nc >= len ) ) { + astSetLutEpsilon( this, luteps ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a LutMap. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* LutMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a LutMap's attributes. + +* Parameters: +* this +* Pointer to the LutMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstLutMap *this; /* Pointer to the LutMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the LutMap structure. */ + this = (AstLutMap *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* LutInterp. */ +/* ---------- */ + if ( !strcmp( attrib, "lutinterp" ) ) { + result = astTestLutInterp( this ); + +/* LutEpsilon. */ +/* ------------- */ + } else if ( !strcmp( attrib, "lutepsilon" ) ) { + result = astTestLutEpsilon( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a LutMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* LutMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a LutMap and a set of points encapsulated +* in a PointSet and transforms the points so as to apply the +* lookup table transformation. + +* Parameters: +* this +* Pointer to the LutMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate +* transformation should be applied, while a zero value requests +* the inverse transformation. +* out +* Pointer to a PointSet which will hold the transformed +* (output) coordinate values. A NULL value may also be given, +* in which case a new PointSet will be created by this +* function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - The number of coordinate values per point in the input +* PointSet must equal 1. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points (with 1 coordinate value per point) +* to accommodate the result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstLutMap *map; /* Pointer to LutMap to be applied */ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *lut; /* Pointer to LUT */ + double d1; /* Offset to I1 value */ + double d2; /* Offset to I2 value */ + double fract; /* Fractional interpolation distance */ + double scale; /* Normalising scale factor */ + double value_in; /* Input coordinate value */ + double value_out; /* Output coordinate value */ + double x; /* Value normalised to LUT increment */ + double xi; /* Integer value of "x" */ + int *flags; /* Flags indicating an adjacent bad value */ + int *index; /* Translates reduced to original indices */ + int i1; /* Lower adjacent LUT index */ + int i2; /* Upper adjacent LUT index */ + int i; /* New LUT index */ + int istart; /* Original LUT index at start of interval */ + int ix; /* "x" converted to an int */ + int near; /* Perform nearest neighbour interpolation? */ + int nlut; /* Number of LUT entries */ + int nlutm1; /* Number of LUT entries minus one */ + int npoint; /* Number of points */ + int ok; /* Lookup table is not flat */ + int point; /* Loop counter for points */ + int up; /* LUT values are increasing? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the LutMap. */ + map = (AstLutMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform + member function inherited from the parent Mapping class. This + function validates all arguments and generates an output PointSet + if necessary, but does not actually transform any coordinate + values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points from the input PointSet and obtain + pointers for accessing the input and output coordinate values. */ + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, + according to the direction specified and whether the mapping has + been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Forward transformation. */ +/* ----------------------- */ + if( astOK ){ + if ( forward ) { + +/* Obtain lookup table details. */ + lut = map->lut; + nlut = map->nlut; + near = ( astGetLutInterp( map ) == NEAR ); + nlutm1 = nlut - 1; + +/* Calculate the scale factor required. */ + scale = 1.0 / map->inc; + +/* Loop to transform each input point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Extract the input coordinate value. */ + value_in = ptr_in[ 0 ][ point ]; + +/* First check if this is the same value as we transformed last. If + so, re-use the last result. */ + if ( value_in == map->last_fwd_in ) { + value_out = map->last_fwd_out; + +/* Check for bad input coordinates and generate a bad result if + necessary. */ + } else if ( value_in == AST__BAD ) { + value_out = AST__BAD; + +/* For nearest-neighbour interpolation, return the value of the lookup table + entry corresponding to the input coordinate. */ + } else if( near ){ + x = ( value_in - map->start ) * scale; + xi = floor( x + 0.5 ); + ix = (int) xi; + if ( ix < 0 || ix >= nlut ) { + value_out = AST__BAD; + } else { + value_out = lut[ ix ]; + } + +/* Otherwise, (for linear interpolation) identify the lookup table entry + corresponding to the input coordinate. */ + } else { + x = ( value_in - map->start ) * scale; + xi = floor( x ); + ix = (int) xi; + +/* If the input value lies below the first lookup table entry, + extrapolate using the first two table values. */ + if ( ix < 0 ) { + if( lut[ 0 ] != AST__BAD && lut[ 1 ] != AST__BAD ) { + value_out = lut[ 0 ] + x * ( lut[ 1 ] - lut[ 0 ] ); + } else { + value_out = AST__BAD; + } + +/* If the input value lies above the last lookup table entry (or equals + it), extrapolate using the last two table values. */ + } else if ( ix >= nlutm1 ) { + if( lut[ nlutm1 ] != AST__BAD && + lut[ nlut - 2 ] != AST__BAD ) { + value_out = lut[ nlutm1 ] + + ( x - (double) ( nlutm1 ) ) * + ( lut[ nlutm1 ] - lut[ nlut - 2 ] ); + } else { + value_out = AST__BAD; + } + +/* Otherwise, interpolate between the adjacent entries. */ + } else { + if( lut[ ix ] != AST__BAD && + lut[ ix + 1 ] != AST__BAD ) { + fract = x - xi; + value_out = lut[ ix ] * ( 1.0 - fract ) + + lut[ ix + 1 ] * fract; + } else { + value_out = AST__BAD; + } + } + } + +/* Assign the output coordinate value. */ + ptr_out[ 0 ][ point ] = value_out; + +/* Retain the input and output coordinate values for possible re-use + in future. */ + map->last_fwd_in = value_in; + map->last_fwd_out = value_out; + } + +/* Inverse transformation. */ +/* ----------------------- */ + } else { + +/* Obtain details of the lookup table to be used by the inverse + transformation. This is the same as the forward transformation lookup + table, except that any bad values are omitted. Also, get a pointer to a + array of flags that indicate if the corresponding lookup table entries + were adjacent to a bad value or not in the full lookup table. */ + if( map->luti ) { + lut = map->luti; + flags = map->flagsi; + nlut = map->nluti; + index = map->indexi; + } else { + lut = map->lut; + flags = NULL; + nlut = map->nlut; + index = NULL; + } + near = ( astGetLutInterp( map ) == NEAR ); + nlutm1 = nlut - 1; + +/* Loop to transform each input point. */ + for ( point = 0; point < npoint; point++ ) { + +/* Extract the input coordinate value. */ + value_in = ptr_in[ 0 ][ point ]; + +/* First check if this is the same value as we transformed last. If + so, re-use the last result. */ + if ( value_in == map->last_inv_in ) { + value_out = map->last_inv_out; + +/* Check for bad input coordinates and generate a bad result if + necessary. */ + } else if ( value_in == AST__BAD ) { + value_out = AST__BAD; + +/* Otherwise, we can determine an inverse. Note the inverse transformation + will not be defined, so will not be attempted, unless all the table + entries are monotonically increasing or decreasing, possibly with sections + of equal or bad values. */ + } else { + up = ( lut[ nlutm1 ] > lut[ 0 ] ); + +/* Perform a binary search to identify two adjacent lookup table + elements whose values bracket the input coordinate value. */ + i1 = -1; + i2 = nlutm1; + while ( i2 > ( i1 + 1 ) ) { + i = ( i1 + i2 ) / 2; + *( ( ( value_in >= lut[ i ] ) == up ) ? &i1 : &i2 ) = i; + } + +/* If the lower table value is equal to the required value, and either of + its neighbours is also equal to the required value, then we have been + asked to find the inverse in a flat region of the table, so return + a bad value. Likewise, if the upper table value is equal to the required + value, and either of its neighbours is also equal to the required value, + then we have been asked to find the inverse in a flat region of the table, + so return a bad value. */ + ok = 1; + if( lut[ i1 ] == value_in ) { + if( i1 > 0 && lut[ i1 - 1 ] == value_in ) ok = 0; + if( lut[ i2 ] == value_in ) ok = 0; + } else if( lut[ i2 ] == value_in ) { + if( i2 < nlutm1 && lut[ i2 + 1 ] == value_in ) ok = 0; + if( lut[ i1 ] == value_in ) ok = 0; + } + + if( !ok ) { + value_out = AST__BAD; + +/* If both of the two table elements were adjacent to a bad value in the + full lookup table, return a bad output value. */ + } else if( flags && ( flags[ i1 ] && flags[ i2 ] ) ) { + value_out = AST__BAD; + +/* Nearest neighbour interpolation: return the closest of i1 or i2. Return + AST__BAD if the supplied value is less than either or greater than + either. */ + } else if( near ) { + d1 = lut[ i1 ] - value_in; + d2 = lut[ i2 ] - value_in; + if( ( d1 > 0.0 && d2 > 0.0 ) || + ( d1 < 0.0 && d2 < 0.0 ) ) { + value_out = AST__BAD; + + } else { + + if( fabs( d1 ) < fabs( d2 ) ){ + istart = index ? index[ i1 ] : i1; + } else { + istart = index ? index[ i2 ] : i2; + } + value_out = map->start + map->inc * istart; + + } + +/* Linear interpolation... */ + } else { + +/* We are interested in the lower bracketing table element. If + necessary, restrict this element's index to lie within the + table. This causes extrapolation to occur (instead of + interpolation) if the input value actually lies outside the range + of the lookup table. */ + if ( i1 < 0 ) i1 = 0; + if ( i1 > ( nlut - 2 ) ) i1 = nlut - 2; + +/* Interpolate (or extrapolate) to derive the output coordinate + value. */ + istart = index ? index[ i1 ] : i1; + value_out = map->start + map->inc * ( (double) istart + + ( ( value_in - lut[ i1 ] ) / + ( lut[ i1 + 1 ] - lut[ i1 ] ) ) ); + } + } + +/* Assign the output coordinate value. */ + ptr_out[ 0 ][ point ] = value_out; + +/* Retain the input and output coordinate values for possible re-use + in future. */ + map->last_inv_in = value_in; + map->last_inv_out = value_out; + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* LutInterp + +* Purpose: +* Look-up table interpolation method. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute indicates the method to be used when finding the +* output value of a LutMap for an input value part way between two +* table entries. If it is set to 0 (the default) then linear +* interpolation is used. Otherwise, nearest neighbour interpolation +* is used. +* +* Using nearest neighbour interpolation causes AST__BAD to be returned +* for any point which falls outside the bounds of the table. Linear +* interpolation results in an extrapolated value being returned based +* on the two end entries in the table. +* +* Note, the value of this attribute may changed only if the LutMap +* has no more than one reference. That is, an error is reported if the +* LutMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* LutMap +* All LutMaps have this attribute. + +*att-- +*/ +astMAKE_CLEAR1(LutMap,LutInterp,lutinterp,-INT_MAX) +astMAKE_GET(LutMap,LutInterp,int,LINEAR,( ( this->lutinterp == -INT_MAX ) ? + LINEAR : this->lutinterp )) +astMAKE_SET1(LutMap,LutInterp,int,lutinterp,(( value == LINEAR ) ? LINEAR : NEAR )) +astMAKE_TEST(LutMap,LutInterp,( this->lutinterp != -INT_MAX )) + +/* +*att++ +* Name: +* LutEpsilon + +* Purpose: +* The relative error of the values held in the took-up table. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds the relative error of the values held in the +* took-up table. It is used when simplifying a LutMap, to determine +* if the LutMap should be considered linear. Setting a larger value +* makes it more likely that a LutMap will be replaced by a WinMap +* (i.e. a linear Mapping) when simplified. +* +* The default value is the value of the system constant DBL_EPSILON +* (typically around 1e-16 or 2E-16). If the values in the look-up +* table were derived from single precision data, it may be appropriate +* to set this attribute to a value around 1E-7. +* +* Note, the value of this attribute may changed only if the LutMap +* has no more than one reference. That is, an error is reported if the +* LutMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* LutMap +* All LutMaps have this attribute. + +*att-- +*/ +astMAKE_CLEAR1(LutMap,LutEpsilon,lutepsilon,AST__BAD) +astMAKE_GET(LutMap,LutEpsilon,double,DBL_EPSILON,( ( this->lutepsilon == AST__BAD ) ? + DBL_EPSILON : this->lutepsilon )) +astMAKE_SET1(LutMap,LutEpsilon,double,lutepsilon,(value)) +astMAKE_TEST(LutMap,LutEpsilon,( this->lutepsilon != AST__BAD )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for LutMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for LutMap objects. + +* Parameters: +* objin +* Pointer to the LutMap to be copied. +* objout +* Pointer to the LutMap being constructed. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstLutMap *out; /* Pointer to output LutMap */ + AstLutMap *in; /* Pointer to input LutMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the input and output LutMaps. */ + in= (AstLutMap *) objin; + out = (AstLutMap *) objout; + +/* Nullify all output pointers. */ + out->lut = NULL; + out->luti = NULL; + out->flagsi = NULL; + out->indexi = NULL; + +/* Allocate memory and store a copy of the lookup table data. */ + out->lut = astStore( NULL, in->lut, + sizeof( double ) * (size_t) in->nlut ); + +/* Do the arrays used for the inverse transformation, if they exist. */ + if( in->luti ) out->luti = astStore( NULL, in->luti, + sizeof( double ) * (size_t) in->nluti ); + if( in->flagsi ) out->flagsi = astStore( NULL, in->flagsi, + sizeof( double ) * (size_t) in->nluti ); + if( in->indexi ) out->indexi = astStore( NULL, in->indexi, + sizeof( double ) * (size_t) in->nluti ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for LutMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for LutMap objects. + +* Parameters: +* obj +* Pointer to the LutMap to be deleted. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstLutMap *this; /* Pointer to LutMap */ + +/* Obtain a pointer to the LutMap structure. */ + this = (AstLutMap *) obj; + +/* Free the memory holding the lookup tables, etc. */ + this->lut = astFree( this->lut ); + this->luti = astFree( this->luti ); + this->flagsi = astFree( this->flagsi ); + this->indexi = astFree( this->indexi ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for LutMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the LutMap class to an output Channel. + +* Parameters: +* this +* Pointer to the LutMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstLutMap *this; /* Pointer to the LutMap structure */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + double dval; /* Double value */ + int ilut; /* Loop counter for table elements */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the LutMap structure. */ + this = (AstLutMap *) this_object; + +/* Write out values representing the instance variables for the LutMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written. */ + +/* Number of lookup table elements. */ + astWriteInt( channel, "Nlut", 1, 1, this->nlut, + "Number of lookup table elements" ); + +/* Input coordinate at first element centre. */ + astWriteDouble( channel, "Start", ( this->start != 0.0 ), 1, this->start, + "Input value at first element" ); + +/* Element spacing. */ + astWriteDouble( channel, "Incr", ( this->inc != 1.0 ), 1, this->inc, + "Input value increment between elements" ); + +/* Interpolation method */ + set = TestLutInterp( this, status ); + ival = set ? GetLutInterp( this, status ) : astGetLutInterp( this ); + astWriteInt( channel, "LutInt", set, 1, ival, "Interpolation method" ); + +/* Precision */ + if( TestLutEpsilon( this, status ) ) { + dval = GetLutEpsilon( this, status ); + astWriteDouble( channel, "LutEps", 1, 1, dval, "Table relative error" ); + } + +/* Lookup table contents. */ + for ( ilut = 0; ilut < this->nlut; ilut++ ) { + if( this->lut[ ilut ] != AST__BAD ) { + (void) sprintf( buff, "L%d", ilut + 1 ); + astWriteDouble( channel, buff, 1, 1, this->lut[ ilut ], + ilut ? "" : "Lookup table elements..." ); + } + } + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsALutMap and astCheckLutMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(LutMap,Mapping) +astMAKE_CHECK(LutMap) + +AstLutMap *astLutMap_( int nlut, const double lut[], + double start, double inc, + const char *options, int *status, ...) { +/* +*++ +* Name: +c astLutMap +f AST_LUTMAP + +* Purpose: +* Create a LutMap. + +* Type: +* Public function. + +* Synopsis: +c #include "lutmap.h" +c AstLutMap *astLutMap( int nlut, const double lut[], +c double start, double inc, +c const char *options, ... ) +f RESULT = AST_LUTMAP( NLUT, LUT, START, INC, OPTIONS, STATUS ) + +* Class Membership: +* LutMap constructor. + +* Description: +* This function creates a new LutMap and optionally initialises +* its attributes. +* +* A LutMap is a specialised form of Mapping which transforms +* 1-dimensional coordinates by using linear interpolation in a +* lookup table. Each input coordinate value is first scaled to +* give the index of an entry in the table by subtracting a +* starting value (the input coordinate corresponding to the first +* table entry) and dividing by an increment (the difference in +* input coordinate value between adjacent table entries). +* +* The resulting index will usually contain a fractional part, so +* the output coordinate value is then generated by interpolating +* linearly between the appropriate entries in the table. If the +* index lies outside the range of the table, linear extrapolation +* is used based on the two nearest entries (i.e. the two entries +* at the start or end of the table, as appropriate). +* +* If the lookup table entries increase or decrease monotonically, +* then the inverse transformation may also be performed. + +* Parameters: +c nlut +f NLUT = INTEGER (Given) +* The number of entries in the lookup table. This value must be +* at least 2. +c lut +f LUT( NLUT ) = DOUBLE PRECISION (Given) +c An array containing the "nlut" +f An array containing the +* lookup table entries. +c start +f START = DOUBLE PRECISION (Given) +* The input coordinate value which corresponds to the first lookup +* table entry. +c inc +f INC = DOUBLE PRECISION (Given) +* The lookup table spacing (the increment in input coordinate +* value between successive lookup table entries). This value +* may be positive or negative, but must not be zero. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new LutMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new LutMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astLutMap() +f AST_LUTMAP = INTEGER +* A pointer to the new LutMap. + +* Notes: +* - If the entries in the lookup table either increase or decrease +* monotonically, then the new LutMap's TranInverse attribute will +* have a value of one, indicating that the inverse transformation +* can be performed. Otherwise, it will have a value of zero, so +* that any attempt to use the inverse transformation will result +* in an error. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstLutMap *new; /* Pointer to new LutMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the LutMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitLutMap( NULL, sizeof( AstLutMap ), !class_init, &class_vtab, + "LutMap", nlut, lut, start, inc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + LutMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new LutMap. */ + return new; +} + +AstLutMap *astLutMapId_( int nlut, const double lut[], + double start, double inc, + const char *options, ... ) { +/* +* Name: +* astLutMapId_ + +* Purpose: +* Create a LutMap. + +* Type: +* Private function. + +* Synopsis: +* #include "lutmap.h" +* AstLutMap *astLutMapId( int nlut, const double lut[], +* double start, double inc, +* const char *options, ... ) + +* Class Membership: +* LutMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astLutMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astLutMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astLutMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astLutMap_. + +* Returned Value: +* The ID value associated with the new LutMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstLutMap *new; /* Pointer to new LutMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the LutMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitLutMap( NULL, sizeof( AstLutMap ), !class_init, &class_vtab, + "LutMap", nlut, lut, start, inc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new LutMap's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new LutMap. */ + return astMakeId( new ); +} + +AstLutMap *astInitLutMap_( void *mem, size_t size, int init, + AstLutMapVtab *vtab, const char *name, + int nlut, const double lut[], + double start, double inc, int *status ) { +/* +*+ +* Name: +* astInitLutMap + +* Purpose: +* Initialise a LutMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "lutmap.h" +* AstLutMap *astInitLutMap( void *mem, size_t size, int init, +* AstLutMapVtab *vtab, const char *name, +* int nlut, const double lut[], +* double start, double inc ) + +* Class Membership: +* LutMap initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new LutMap object. It allocates memory (if +* necessary) to accommodate the LutMap plus any additional data +* associated with the derived class. It then initialises a LutMap +* structure at the start of this memory. If the "init" flag is +* set, it also initialises the contents of a virtual function +* table for a LutMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the LutMap is to be +* initialised. This must be of sufficient size to accommodate +* the LutMap data (sizeof(LutMap)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the LutMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the LutMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A logical flag indicating if the LutMap's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new LutMap. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* nlut +* The number of elements in the lookup table. This value must +* be at least 2. +* lut +* An array containing the "nlut" lookup table elements. +* start +* The input coordinate value which corresponds with the first +* lookup table element. +* inc +* The lookup table element spacing (i.e. the increment in input +* coordinate value between successive lookup table elements). + +* Returned Value: +* A pointer to the new LutMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstLutMap *new; /* Pointer to new LutMap */ + double *luti; /* Pointer to table for inverse transformation */ + double *p; /* Pointer to next lut element */ + int *flagsi; /* Pointer to flags for inverse transformation */ + int *indexi; /* Pointer to translation from original to reduced */ + int dirn; /* +1 => values increasing, -1 => values decreasing */ + int ilut; /* Loop counter for LUT elements */ + int nluti; /* Length of "luti" array */ + +/* Initialise. */ + new = NULL; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitLutMapVtab( vtab, name ); + +/* Check that the number of lookup table elements is valid. */ + if ( nlut < 2 ) { + astError( AST__LUTIN, "astInitLutMap(%s): Invalid number of lookup " + "table elements (%d).", status, name, nlut ); + astError( AST__LUTIN, "This value should be at least 2." , status); + +/* Also check that the input value increment is not zero. */ + } else if ( inc == 0.0 ) { + astError( AST__LUTII, "astInitLutMap(%s): An input value increment of " + "zero between lookup table elements is not allowed.", status, name ); + +/* Determine if the element values increase or decrease monotonically (except + that adjacent entries can be equal). We can only implement the inverse + transformation if this is so. The inverse transformation will generate + AST__BAD output values for sections of the table that contain equal + adjacent values, or hold AST__BAD values. */ + } else { + dirn = GetMonotonic( nlut, lut, &nluti, &luti, &flagsi, &indexi, + status ); + +/* Initialise a Mapping structure (the parent class) as the first + component within the LutMap structure, allocating memory if + necessary. Specify that the Mapping should be defined in the + forward direction, and conditionally in the inverse direction. */ + new = (AstLutMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 1, 1, 1, ( dirn != 0 ) ); + + if ( astOK ) { + +/* Initialise the LutMap data. */ +/* ---------------------------- */ + new->nlut = nlut; + new->start = start; + new->inc = inc; + new->lutinterp = LINEAR; + new->lutepsilon = AST__BAD; + new->nluti = nluti; + new->luti = luti; + new->flagsi = flagsi; + new->indexi = indexi; + +/* Allocate memory and store the lookup table. */ + new->lut = astStore( NULL, lut, sizeof( double ) * (size_t) nlut ); + +/* Replace an NaN values by AST__BAD */ + p = new->lut; + for ( ilut = 0; ilut < nlut; ilut++, p++ ) { + if( !astISFINITE(*p) ) *p = AST__BAD; + } + +/* Initialise the retained input and output coordinate values. */ + new->last_fwd_in = AST__BAD; + new->last_fwd_out = AST__BAD; + new->last_inv_in = AST__BAD; + new->last_inv_out = AST__BAD; + } + +/* If an error occurred, clean up by deleting the new LutMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new LutMap. */ + return new; +} + +AstLutMap *astLoadLutMap_( void *mem, size_t size, + AstLutMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadLutMap + +* Purpose: +* Load a LutMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "lutmap.h" +* AstLutMap *astLoadLutMap( void *mem, size_t size, +* AstLutMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* LutMap loader. + +* Description: +* This function is provided to load a new LutMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* LutMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a LutMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the LutMap is to be +* loaded. This must be of sufficient size to accommodate the +* LutMap data (sizeof(LutMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the LutMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the LutMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstLutMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new LutMap. If this is NULL, a pointer +* to the (static) virtual function table for the LutMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "LutMap" is used instead. + +* Returned Value: +* A pointer to the new LutMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstLutMap *new; /* Pointer to the new LutMap */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int ilut; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Loop counter for table elements */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this LutMap. In this case the + LutMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstLutMap ); + vtab = &class_vtab; + name = "LutMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitLutMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built LutMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "LutMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* Number of lookup table elements. */ + new->nlut = astReadInt( channel, "nlut", 2 ); + +/* Starting input coordinate value. */ + new->start = astReadDouble( channel, "start", 0.0 ); + +/* Input coordinate value increment. */ + new->inc = astReadDouble( channel, "incr", 1.0 ); + +/* Interpolation method */ + new->lutinterp = astReadInt( channel, "lutint", LINEAR ); + if ( TestLutInterp( new, status ) ) SetLutInterp( new, new->lutinterp, status ); + +/* Precision */ + new->lutepsilon = astReadDouble( channel, "luteps", AST__BAD ); + if ( TestLutEpsilon( new, status ) ) SetLutEpsilon( new, new->lutepsilon, status ); + +/* Allocate memory to hold the lookup table elements. */ + new->lut = astMalloc( sizeof( double ) * (size_t) new->nlut ); + +/* If OK, loop to read each element. */ + if ( astOK ) { + for ( ilut = 0; ilut < new->nlut; ilut++ ) { + (void) sprintf( buff, "l%d", ilut + 1 ); + new->lut[ ilut ] = astReadDouble( channel, buff, AST__BAD ); + } + +/* Initialise the retained input and output coordinate values. */ + new->last_fwd_in = AST__BAD; + new->last_fwd_out = AST__BAD; + new->last_inv_in = AST__BAD; + new->last_inv_out = AST__BAD; + +/* See if the array is monotonic increasing or decreasing. */ + (void) GetMonotonic( new->nlut, new->lut, &(new->nluti), + &(new->luti), &(new->flagsi), &(new->indexi), + status ); + } + } + +/* If an error occurred, clean up by deleting the new LutMap. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the new LutMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions + defined by this class. Each simply checks the global error status + and then locates and executes the appropriate member function, + using the function pointer stored in the object's virtual function + table (this pointer is located using the astMEMBER macro defined in + "object.h"). + + Note that the member function may not be the one defined here, as + it may have been over-ridden by a derived class. However, it should + still have the same interface. */ + +double *astGetLutMapInfo_( AstLutMap *this, double *start, double *inc, + int *nlut, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,LutMap,GetLutMapInfo))( this, start, inc, nlut, + status ); +} + + + diff --git a/lutmap.h b/lutmap.h new file mode 100644 index 0000000..68fa194 --- /dev/null +++ b/lutmap.h @@ -0,0 +1,335 @@ +#if !defined( LUTMAP_INCLUDED ) /* Include this file only once */ +#define LUTMAP_INCLUDED +/* +*+ +* Name: +* lutmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the LutMap class. + +* Invocation: +* #include "lutmap.h" + +* Description: +* This include file defines the interface to the LutMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The LutMap class implements Mappings which transform +* 1-dimensional coordinates using linear interpolation in a lookup +* table. + +* Inheritance: +* The LutMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astMapMerge +* Simplify a sequence of Mappings. +* astTransform +* Apply a LutMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsALutMap +* Test class membership. +* astLutMap +* Create a LutMap. +* +* Protected: +* astCheckLutMap +* Validate class membership. +* astInitLutMap +* Initialise a LutMap. +* astInitLutMapVtab +* Initialise the virtual function table for the LutMap class. +* astLoadLutMap +* Load a LutMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstLutMap +* LutMap object type. +* +* Protected: +* AstLutMapVtab +* LutMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 8-JUL-1997 (RFWS): +* Original version. +* 8-JAN-2003 (DSB): +* Added protected astInitLutMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* LutMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstLutMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *lut; /* Pointer to lookup table */ + double *luti; /* Reduced lookup table for inverse trans. */ + double inc; /* Input increment between table entries */ + double last_fwd_in; /* Last input value (forward transfm.) */ + double last_fwd_out; /* Last output value (forward transfm.) */ + double last_inv_in; /* Last input value (inverse transfm.) */ + double last_inv_out; /* Last output value (inverse transfm.) */ + double start; /* Input value for first table entry */ + int *flagsi; /* Flags indicating adjacent bad values */ + int *indexi; /* Translates reduced to original indices */ + double lutepsilon; /* Relative error of table values */ + int lutinterp; /* Interpolation method */ + int nlut; /* Number of table entries */ + int nluti; /* Reduced number of table entries */ +} AstLutMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstLutMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (*GetLutInterp)( AstLutMap *, int * ); + int (* TestLutInterp)( AstLutMap *, int * ); + void (* ClearLutInterp)( AstLutMap *, int * ); + void (* SetLutInterp)( AstLutMap *, int, int * ); + double (*GetLutEpsilon)( AstLutMap *, int * ); + int (* TestLutEpsilon)( AstLutMap *, int * ); + void (* ClearLutEpsilon)( AstLutMap *, int * ); + void (* SetLutEpsilon)( AstLutMap *, double, int * ); + double *(* GetLutMapInfo)( AstLutMap *, double *, double *, int *, int * ); + +} AstLutMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstLutMapGlobals { + AstLutMapVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstLutMapGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(LutMap) /* Check class membership */ +astPROTO_ISA(LutMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstLutMap *astLutMap_( int, const double [], double, double, const char *, int *, ...); +#else +AstLutMap *astLutMapId_( int, const double [], double, double, const char *, ... )__attribute__((format(printf,5,6))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstLutMap *astInitLutMap_( void *, size_t, int, AstLutMapVtab *, const char *, int, const double *, double, double, int * ); + +/* Vtab initialiser. */ +void astInitLutMapVtab_( AstLutMapVtab *, const char *, int * ); + +/* Loader. */ +AstLutMap *astLoadLutMap_( void *, size_t, AstLutMapVtab *, const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitLutMapGlobals_( AstLutMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ + int astGetLutInterp_( AstLutMap *, int * ); + int astTestLutInterp_( AstLutMap *, int * ); + void astClearLutInterp_( AstLutMap *, int * ); + void astSetLutInterp_( AstLutMap *, int, int * ); + + double astGetLutEpsilon_( AstLutMap *, int * ); + int astTestLutEpsilon_( AstLutMap *, int * ); + void astClearLutEpsilon_( AstLutMap *, int * ); + void astSetLutEpsilon_( AstLutMap *, double, int * ); + + double *astGetLutMapInfo_( AstLutMap *, double *, double *, int *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckLutMap(this) astINVOKE_CHECK(LutMap,this,0) +#define astVerifyLutMap(this) astINVOKE_CHECK(LutMap,this,1) + +/* Test class membership. */ +#define astIsALutMap(this) astINVOKE_ISA(LutMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astLutMap astINVOKE(F,astLutMap_) +#else +#define astLutMap astINVOKE(F,astLutMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define \ +astInitLutMap(mem,size,init,vtab,name,nlut,lut,start,inc) \ +astINVOKE(O,astInitLutMap_(mem,size,init,vtab,name,nlut,lut,start,inc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitLutMapVtab(vtab,name) astINVOKE(V,astInitLutMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadLutMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadLutMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckLutMap to validate LutMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +#if defined(astCLASS) /* Protected */ + +#define astClearLutInterp(this) \ + astINVOKE(V,astClearLutInterp_(astCheckLutMap(this),STATUS_PTR)) +#define astGetLutInterp(this) \ + astINVOKE(V,astGetLutInterp_(astCheckLutMap(this),STATUS_PTR)) +#define astSetLutInterp(this,value) \ + astINVOKE(V,astSetLutInterp_(astCheckLutMap(this),value,STATUS_PTR)) +#define astTestLutInterp(this) \ + astINVOKE(V,astTestLutInterp_(astCheckLutMap(this),STATUS_PTR)) +#define astGetLutMapInfo(this,start,inc,nlut) \ + astINVOKE(V,astGetLutMapInfo_(astCheckLutMap(this),start,inc,nlut,STATUS_PTR)) +#define astClearLutEpsilon(this) \ + astINVOKE(V,astClearLutEpsilon_(astCheckLutMap(this),STATUS_PTR)) +#define astGetLutEpsilon(this) \ + astINVOKE(V,astGetLutEpsilon_(astCheckLutMap(this),STATUS_PTR)) +#define astSetLutEpsilon(this,value) \ + astINVOKE(V,astSetLutEpsilon_(astCheckLutMap(this),value,STATUS_PTR)) +#define astTestLutEpsilon(this) \ + astINVOKE(V,astTestLutEpsilon_(astCheckLutMap(this),STATUS_PTR)) + +#endif + +#endif + + + + + diff --git a/makeh b/makeh new file mode 100644 index 0000000..c07d343 --- /dev/null +++ b/makeh @@ -0,0 +1,313 @@ +#!/usr/bin/perl +#+ +# Name: +# makeh + +# Purpose: +# Generate the contents of the "ast.h" header file. + +# Type: +# Perl script. + +# Invocation: +# makeh file_list + +# Description: +# This script processes private header files used within the AST library +# in order to extract the public information they contain. This information +# is produced in a form suitable for use in the public "ast.h" header file, +# which defines the public interface to the library. + +# Parameters: +# file_list +# A space-separated list of the private AST header files whose public +# information is to be extracted. + +# Result: +# The contents of the "ast.h" header file are written to standard output. + +# Notes: +# - This script is specific to the AST library and contains some knowledge +# of the input file contents. + +# History: +# 10-JAN-2005 (DSB): +# Added second argument (mode) to the mkdir invocation which creates +# the temporary directory. +# 2-MAR-2006 (DSB): +# Check for "config.h" as well as +# 6-JAN-2010 (DSB): +# Add work-around for GCC 4.4.2 problem - the pre-processor seesm to simply +# throw away backslshes that escape newlines in the input header file. Reported +# by Bryan Irby at GSFC. +# 27-JUL-2011 (DSB): +# Include the process ID in the name of the temporary directory and +# file, so that simultaneous invocations of this script do not trample +# all over each other. +#- + +( $#ARGV >= 0 ) || Usage(); + +# Test whether we need to include config.h in the result. +$need_config_h = 0; + +# Location of source files (makefile variable $(srcdir)). +# This is most typically '.', but can be different. +$srcdir = '.'; + +while ( $ARGV[0] =~ /^-/ ) { + if ( $ARGV[0] eq '-s' ) { + shift @ARGV; + ( $#ARGV >= 0 ) || Usage(); + $srcdir = $ARGV[0]; + shift @ARGV; + } else { + Usage(); + } +} + +# Create a scratch directory. +$tmpdir="/tmp/makeh-$$.tmp"; +unless ( -d $tmpdir && -w $tmpdir ) { + if ( -e $tmpdir ) { + die "Temp $tmpdir exists, but is unwritable or is not a directory\n"; + } + mkdir $tmpdir, 0777 || + die "Failed to create temporary directory $tmpdir\n"; +} + +# Open each input file. +foreach $file ( @ARGV ) { + protect_copy_file( $file ); +} + + +# Open a pipe to a script (in the current directory) which runs the C +# preprocessor and direct its output to a scratch file. +open( CC, "| ./ast_cpp >/tmp/ast-$$.h" ) || + die "Can't open pipe to C preprocessor (cpp)"; + +if ( $need_config_h ) { +# Include this directory's config.h, unescaped, in the output. We +# need to pay attention to the values in this file, but don't want +# them unexpanded in the final ast.h. + chomp($cwd = `pwd`); + print(CC "#include \"$cwd/config.h\"\n"); +} + +# Before including each file, write an underlined heading in the form of +# C comments (with comment characters suitably protected so that they will +# be passed unchanged by cpp). +foreach $file ( @ARGV ) { + $comment = $file; + $comment =~ s|^.*/||; + $comment =~ s|.h$||; + print( CC "/_* " . $comment . ". *_/\n" ); + $comment =~ s/./=/g; + print( CC "/_* " . $comment . "= *_/\n" ); + +# Write #include "xxx.h" lines to cpp so that it includes (and +# preprocesses) each of the temporary files created above in turn. + $tmp_file = $file; + $tmp_file =~ s|^.*/||; + printf(CC "#include \"%s/%s\"\n", $tmpdir, $tmp_file); +}; + +# Close the pipe to cpp. +close( CC ) || die "C preprocessor (cpp) error"; + +# Remove the temporary directory and the files it contains. +print( stderr `rm -r $tmpdir` ); + +# Write the output preamble. +print( +'#if !defined(AST_INCLUDED) +#define AST_INCLUDED +/* +*+ +* Name: +* ast.h + +* Purpose: +* Define the public C interface to the AST library. + +* Language: +* ANSI C + +* Type of Module: +* C header file. + +* Description: +* This file defines the public C interface to the AST library. It contains +* all the type definitions, function prototypes, macro definitions, etc. +* needed to use the library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (STARLINK) +* RFWS: R.F. Warren-Smith (STARLINK) +* {enter_new_authors_here} + +* History: +* ' ); + +# Add the current date at this point. +( $sec, $min, $hour, $mday, $mon, $year ) = localtime; +print( $mday, '-', + ( 'JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', + 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC' )[ $mon ], '-', + ( $year > 95 ? 1900 : 2000 ) + $year ); + +print( ' (makeh): +* Original version, generated automatically from the internal header +* files by the "makeh" script. +* {enter_changes_here} +*- +*/ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +' ); + +# Open the scratch file created above and read it. +$space = 0; +open( TEMP, " ) { + +# Remove the underscores from the protected lines and macros. + s/^_#include(\s)/#include$1/; + s/^_#define(\s)/#define$1/; + s/___LINE__/__LINE__/g; + s/___FILE__/__FILE__/g; + +# Also un-protect protected comments. + s|/_\*|/*|g; + s|\*_/|*/|g; + +# Convert multiple blank lines (cpp creates lots of these) into single blank +# lines. + if ( /^\s*$/ ) { + $space = 1; + } else { + +# Remove additional unwanted lines that cpp creates. + if ( ! /^# \d+/ ) { + if ( $space ) { print( "\n" ) }; + $space = 0; + +# Output the lines we want to keep. + print; + } + } +} + +# Write closing output lines. +print( +' +#endif +' ); + +# Close and remove the scratch file. +close( TEMP ); +unlink "/tmp/ast-$$.h"; + + +sub protect_copy_file { + my $file = shift; + + open( INCLUDE, "$srcdir/$file" ) + || die "Can't open input file " . $srcdir/$file; + +# Inicate we have no deferred text to write out. + $total = ""; + +# Open an output file with the same name in the temporary directory. + $tmp_file = $file; + $tmp_file =~ s|^.*/||; + open( TEMP, ">$tmpdir/$tmp_file" ); + +# Read the input file and detect "#include <...>" lines, extracting the name +# of the header file being included. +line: while ( ) { +# Omit any config.h includes, and note that we saw this + if (/^\s*\#\s*include\s+/ || + /^\s*\#\s*include\s+"config.h"/) { + $need_config_h = 1; + next line; + } + + if ( ( $header ) = /^\#include\s+<(.+)>/ ) { + +# If this system header file has already been included, ignore it and go on to +# the next input line. + next line if $done{ $header }++; + +# Otherwise, protect the #include with an underscore to prevent the file +# actually being included. + s/^/_/; + } + +# Also protect "#define ..." lines, to prevent macro substitution being +# performed by the C preprocessor. Do not do this to lines of the form +# "#define XXX_INCLUDED" because these are still needed to determine which +# AST header files get included. + if ( /^\#define\s/ ) { + if ( ! /^\#define\s+\w*_INCLUDED/ ) { s/^/_/ }; + } + +# Similarly add underscores to protect standard C macros. + s/__LINE__/___LINE__/g; + s/__FILE__/___FILE__/g; + +# Some pre-processors (e.g. GCC 4.4.2) seem to simply throw away trailing +# backslashes used to concatenate consecutive lines, producing two +# independent lines in the output rather than one. This completely fouls +# up the resulting header file. To avoid this, we concatenate such lines +# explicitly, before writing them out to the temporary output file. +# If the current line ends with an escaped newline, remove the escape +# character and newline, and concatenate it with any previously deferred +# lines. + if( /^(.*)\\\s*$/ ) { + $total .= $1; + +# If the current line does not end with an escaped newline, concatenate it +# with any previously deferred lines, and write the total string out. Then +# reset the total string to indicate we have no deferred text. + } else { + $total .= $_; + print TEMP $total; + $total = ""; + } + } + +# Close each file when done. + close( INCLUDE ); + close( TEMP ); +} + +sub Usage { + print STDERR "$0 [-s srcdir] file...\n"; + exit (1); +} diff --git a/mapping.c b/mapping.c new file mode 100644 index 0000000..a1b68fa --- /dev/null +++ b/mapping.c @@ -0,0 +1,24616 @@ +/* +*class++ +* Name: +* Mapping + +* Purpose: +* Inter-relate two coordinate systems. + +* Constructor Function: +* None. + +* Description: +* This class provides the basic facilities for transforming a set +* of coordinates (representing "input" points) to give a new set +* of coordinates (representing "output" points). It is used to +* describe the relationship which exists between two different +* coordinate systems and to implement operations which make use of +* this (such as transforming coordinates and resampling grids of +* data). However, the Mapping class does not have a constructor +* function of its own, as it is simply a container class for a +* family of specialised Mappings which implement particular types +* of coordinate transformation. + +* Inheritance: +* The Mapping class inherits from the Object class. + +* Attributes: +* In addition to those attributes common to all Objects, every +* Mapping also has the following attributes: +* +* - Invert: Mapping inversion flag +* - IsLinear: Is the Mapping linear? +* - IsSimple: Has the Mapping been simplified? +* - Nin: Number of input coordinates for a Mapping +* - Nout: Number of output coordinates for a Mapping +* - Report: Report transformed coordinates? +* - TranForward: Forward transformation defined? +* - TranInverse: Inverse transformation defined? + +* Functions: +c In addition to those functions applicable to all Objects, the +c following functions may also be applied to all Mappings: +f In addition to those routines applicable to all Objects, the +f following routines may also be applied to all Mappings: +* +c - astDecompose: Decompose a Mapping into two component Mappings +c - astTranGrid: Transform a grid of positions +c - astInvert: Invert a Mapping +c - astLinearApprox: Calculate a linear approximation to a Mapping +c - astMapBox: Find a bounding box for a Mapping +c - astMapSplit: Split a Mapping up into parallel component Mappings +c - astQuadApprox: Calculate a quadratic approximation to a 2D Mapping +c - astRate: Calculate the rate of change of a Mapping output +c - astRebin: Rebin a region of a data grid +c - astRebinSeq: Rebin a region of a sequence of data grids +c - astResample: Resample a region of a data grid +c - astRemoveRegions: Remove any Regions from a Mapping +c - astSimplify: Simplify a Mapping +c - astTran1: Transform 1-dimensional coordinates +c - astTran2: Transform 2-dimensional coordinates +c - astTranN: Transform N-dimensional coordinates +c - astTranP: Transform N-dimensional coordinates held in separate arrays +f - AST_DECOMPOSE: Decompose a Mapping into two component Mappings +f - AST_TRANGRID: Transform a grid of positions +f - AST_INVERT: Invert a Mapping +f - AST_LINEARAPPROX: Calculate a linear approximation to a Mapping +f - AST_QUADAPPROX: Calculate a quadratic approximation to a 2D Mapping +f - AST_MAPBOX: Find a bounding box for a Mapping +f - AST_MAPSPLIT: Split a Mapping up into parallel component Mappings +f - AST_RATE: Calculate the rate of change of a Mapping output +f - AST_REBIN: Rebin a region of a data grid +f - AST_REBINSEQ: Rebin a region of a sequence of data grids +f - AST_REMOVEREGIONS: Remove any Regions from a Mapping +f - AST_RESAMPLE: Resample a region of a data grid +f - AST_SIMPLIFY: Simplify a Mapping +f - AST_TRAN1: Transform 1-dimensional coordinates +f - AST_TRAN2: Transform 2-dimensional coordinates +f - AST_TRANN: Transform N-dimensional coordinates + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* MBT: Mark Taylor (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 1-FEB-1996 (RFWS): +* Original version. +* 29-FEB-1996 (RFWS): +* Minor improvements to error messages. +* 15-JUL-1996 (RFWS): +* Support external interface. +* 13-DEC-1996 (RFWS): +* Added the astMapMerge method. +* 13-DEC-1996 (RFWS): +* Added the astSimplify method. +* 27-MAY-1997 (RFWS): +* Improved the astSimplify method to use astMapMerge to +* simplify a single Mapping where possible. +* 29-MAY-1998 (RFWS): +* Added the MapBox method. +* 13-NOV-1998 (RFWS): +* Made default MapBox convergence accuracy larger (i.e. less +* accurate). +* 10-DEC-1998 (RFWS): +* First useful implementation of astResample. +* 16-AUG-1999 (RFWS): +* Fixed bug in SpecialBounds - wrong number of coordinates being used +* when checking for bad output coordinate values. +* 17-AUG-1999 (RFWS): +* Improved the convergence security of MapBox (return to older but +* less efficient setting). +* 24-NOV-2000 (MBT): +* Fixed bug (function being invoked as wrong type) in AST__UINTERP +* scheme, and added new AST__BLOCKAVE scheme, in astResample. +* 9-JAN-2001 (DSB): +* Changed in and out arguments for TranN from type "double (*)[]" +* to "double *". +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitMappingVtab +* method. +* 10-JUL-2003 (DSB): +* Added method astRate. +* 2-SEP-2004 (DSB): +* Free resources before leaving astRate. +* 31-AUG-2004 (DSB): +* Make the LinearApprox function protected rather than private, +* rename it to astLinearApprox, and make the bounds parameters of +* type double rather than int. Also, correct the size of the fit +* coefficient array (was "(nin+1)*nout", now is "(nout+1)*nin"). +* Also correct the index of the first gradient coefficient from +* "fit+nout" to "fit+nin". These errors have probably never been +* noticed because they make no difference if nin=nout, which is +* usually the case. +* 6-SEP-2004 (DSB): +* Make astRate more robust by adding checks for unusal conditions. +* 20-SEP-2004 (DSB): +* Make the LinearApprox function public and change its interface +* to be more appropriate for public use. This involved swapping the +* direction of the fit (the original astLinearApprox fitted the +* inverse transformation, but the public version now fits the forwrd +* transformation). +* 4-OCT-2004 (DSB): +* Modify astMapList to return flag indicating presence of inverted +* CmpMaps in supplied Mapping. +* 9-NOV-2004 (DSB): +* Override astEqual method. +* 6-DEC-2004 (DSB): +* Remove the second derivative estimate from the astRate function +* since CmpMap has trouble calculating it. +* 17-DEC-2004 (DSB): +* Added astMapSplit +* 22-APR-2005 (DSB): +* Modified SpecialBounds to handle cases where some irrelevant +* output always produces bad values (e.g. a PermMap may do this). +* 30-JUN-2005 (DSB): +* Added astRebin. +* 7-JUL-2005 (DSB): +* Make MapSplit public rather than protected. +* 11-AUG-2005 (DSB): +* Added the AST__CONSERVEFLUX flag (used by astResampleX). +* 17-AUG-2005 (DSB): +* Added the AST__SOMBCOS kernel. +* 31-AUG-2005 (DSB): +* Added astRebinSeq. +* 9-SEP-2005 (DSB): +* Corrected axis indices returned by public interface for astMapSplit. +* 31-JAN-2006 (DSB): +* Added IsSimple attribute. +* 2-FEB-2006 (DSB): +* Corrections to prologue of astLinearApprox. +* 16-FEB-2006 (DSB): +* Some speed optimisations to rebinning code. +* 2-MAR-2006 (DSB): +* Use HAVE_LONG_DOUBLE in place of AST_LONG_DOUBLE +* 7-MAR-2006 (DSB): +* Added astTranGrid. +* 14-MAR-2006 (DSB): +* - The constructor no longer reports an error if the resulting +* Mapping cannot transform points in either direction. This is +* because it may be possible to simplify such a Mapping and the +* simplified Mapping may have defined transformations. E.g. if a +* Mapping which has only a forward transformation is combined in +* series with its own inverse, the combination CmpMap will simplify +* to a UnitMap (usually). +* - Reset the "issimple" flag when the Invert flag is changed. +* 9-MAY-2006 (DSB): +* Correct upper bounds for idim in RebinWithblocking. Also, remove +* the single precision "F" instantiation of the MAKE_REBINSEQ macro. +* Also correct the "nout = astGetNin" line in the MAKE_REBINSEQ +* macro to "nout = astGetNout". +* 12-MAY-2006 (DSB): +* Modify SpecialBounds to include points slightly inside the +* corners. This is because some Mappings may have singularies at +* the the edges. +* 17-MAY-2006 (DSB): +* Correct the "nout = astGetNin" line in the MAKE_RESAMPLE +* and MAKE_REBIN macros to "nout = astGetNout". +* 7-JUL-2006 (DSB): +* Change -CHAR_MAX value (used as a "not set" value for boolean +* attributes) to +CHAR_MAX, since some compilers do not allow +* chars to have negative values. +* 23-AUG-2006 (DSB): +* Change the Equal function so that it reports an error when +* called, rather than using astSimplify to determine if two Mappings +* are equal. All concrete Mapping classes should now provide +* their own implementation of astEqual, avoiding the use of +* astSimplify. This is so that astSimplify can use astEqual safely +* (i.e. without danger of entering an infinite loop). +* 24-NOV-2006 (DSB): +* Allow astRebinSeq to be called with a NULL pointer for the input +* data array. +* 14-MAR-2007 (DSB): +* Modify astRebinSeq to allow input variances to be used as weights. +* 19-MAR-2007 (DSB): +* Fix bug in LINEAR_2D macro that caused bad input pixel values to be +* treated as good. +* 16-APR-2007 (DSB): +* Account for reduction in number of degrees of freedom when +* calculating output variances on the basis of spread of input values in +* astReinSeq. +* 28-APR-2007 (DSB): +* Correct code within Rebin... and Resample... functions that provides +* optimal handling for 1- and 2- dimensional mappings. Previously, the +* check for whether or not to use these optimisations was based only on +* the dimensionality of either input (Rebin) or output (Resample). This +* could cause the optimised code to be used at inappropriate times, +* leading to an incorrect effective Mapping between input and output. The +* checks now check both input and output dimensionality in all cases. +* 3-MAY-2007 (DSB): +* An extra parameter ("nused") has been added to astRebinSeq, and +* all the rebinning stuff has been modified to keep "nused" up to date. +* This is needed to correct a fault in the generation of GENVAR +* variances. +* 12-DEC-2007 (DSB): +* Some rebinning kernels (e.g. SINCSINC) have negative values and +* can result in overall negative output weights. Therefore do not +* set output pixels with negative weights bad. +* 6-MAR-2008 (DSB): +* Add an option for astResample to leave unchanged any output pixels +* for which an interpolated value cannot be obtained. This is +* controlled by the new AST__NOBAD flag. +* 7-MAY-2008 (DSB): +* Clarified meaning of AST__GENVAR, AST__USEVAR and AST__VARWGT flags +* in astRebinSeq. +* 9-MAY-2008 (DSB): +* Prevent memory over-run in RebinSeq. +* 5-MAY-2009 (DSB): +* Added astRemoveRegions. +* 11-NOV-2009 (DSB): +* In astRebinSeq initialise "*nused" to zero (as documented) if the +* AST__REBININIT flag is supplied. +* 17-NOV-2009 (DSB): +* Added AST_DISVAR flag for use with astRebinSeq. +* 15-DEC-2009 (DSB): +* Ensure that all axes span at least one pixel when calling +* astLinearApprox. +* 18-DEC-2009 (DSB): +* When using a 1D spreading kernel (in astRebin(Seq)), if the kernel +* is not contained completely within the output array, reflect the +* section of the kernel that falls outside the output array back into +* the output array so that no flux is lost. Also discovered that the +* n-D code (i.e. the KERNEL_ND macro) incorrectly uses the first +* user-supplied parameter as the full kernel width rather than the +* half-width. This has been fixed. +* 26-FEB-2010 (DSB): +* Add astQuadApprox. +* 27-FEB-2010 (DSB): +* - Make astQuadApprox faster, and fix a bug in the calculation of +* the matrix. +* 7-JUN-2010 (DSB): +* In the KERNEL_D rebinning macros, correct the test for the +* central point being outside the bounds of the output image. +* 13-AUG-2010 (DSB): +* In astRebinSeq, scale WLIM to take account of weighting by +* input variances. +* 13-DEC-2010 (DSB): +* Ensure that astMapSplit returns a Mapping that is independent of +* the supplied Mapping (i.e. return a deep copy). This means that +* subsequent changes to the supplied Mapping cannot affect the returned +* Mapping. +* 10-FEB-2011 (DSB): +* When rebinning (in macros NEAR_1/2/ND, KERNEL_1/2/ND, LINEAR_1/2/ND), +* do not treat a zero variance as bad unless the reciprocals of the +* variances are being used as weights. +* 16-JUN-2011 (DSB): +* Allow a check for NaNs to be performed as a debugging tool after +* every invocation of astTransform. This is controlled by the +* AST_REPLACE_NAN environment variable: if unset, no check is +* performed, if set to "1" NaNs are changed to AST__BAD but no +* error is reported, if set to anything else NaNs are changed to +* AST__BAD and an error is reported. +* 6-JUL-2012 (DSB): +* The astRebinSeq family was normalising the returned data and +* variances values incorrectly, when the AST__REBINEND flag was +* supplied. The exact size of the error depended on the nature of +* the Mapping and the spreading method, and so is hard to predict. +* 20-JUL-2012 (DSB): +* Major re-structuring of astRebinSeq to add further +* corrections to the normalisation. The model is now that each +* input array is first rebinned and then scaled to preserve the +* total data sum, and then each final output pixel is the weighed +* mean of all the aligned rebinned pixels. +* 13-AUG-2012 (DSB): +* Added AST__NONORM flag for asstRebuinSeq. +* 30-AUG_2012 (DSB): +* Added AST__CONSERVEFLUX flag for astRebinSeq. +* 10-SEP-2012 (DSB): +* Cater for Mappings that have different numbers of inputs and +* outputs when finding the flux conservation factor within +* astRebinSeq and astResample. +* 1-OCT-2012 (DSB): +* Ensure astRebinSeq does not create any negative output +* variances. +* 2-OCT-2012 (DSB): +* - Check for Infs as well as NaNs. +* - In Rate, break out of the loop if the RMS is very small, not +* just if it is exactly zero. +* 5-OCT-2012 (DSB): +* Complete re-write of Rate. It's now much simpler, faster and +* more reliable. +* 16-OCT-2012 (DSB): +* In MatrixDet, ignore rows/columns filled with AST_BAD as well as +* rows/columns filled with zeros. +* 26-APR-2013 (DSB): +* Change the "nused" parameter of astRebinSeq from "int *" to +* "size_t *" to allow greater amounts of data to be pasted into +* the output array. +* 29-APR-2013 (DSB): +* No sot simplify Mappings that have a set value for their Ident +* attribute. If an Ident value has been set then it means that we +* should be trying to preserve the identify of the Mapping. This +* is implemented via a new protected method (astDoNotSimplify) which +* is overridden by the Frame class so that this restriction applies +* only to genuine Mappings, not Frames. +* 9-MAY-2013 (DSB): +* Change the "nused" parameter of astRebinSeq from "size_t *" to +* "int64_t *" to cater for systems where "size_t" is only 32 bits long. +* 20-MAY-2013 (DSB): +* Always perform a linear fit in RebinAdaptively if flux +* conservation is requested. +* 18-JUL-2013 (DSB): +* Correct logic for determining whether to divide or not in +* RebinAdaptively. The old logic could lead to infinite recursion. +* 1-SEP-2014 (DSB): +* Modify astLinearAPprox to avoid using regularly placed +* test points, as such regular placement may result in +* non-representative behaviour. +* 25-SEP-2014 (DSB): +* Add support for B and UB data types to astRebin and astRebinSeq. +* 23-OCT-2014 (DSB): +* Report an error if arrays have too many pixels to count in a 32 +* bit int (astTranGrid, astResample, astRebin and astRebinSeq). +* 23-APR-2015 (DSB): +* Use one bit of this->flags to store the "IsSimple" attribute +* rather using a whole char (this->issimple). +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to the header + files that define class interfaces that they should make "protected" + symbols available. */ +#define astCLASS Mapping + +/* Define numerical constants for use in thie module. */ +#define GETATTRIB_BUFF_LEN 50 +#define RATEFUN_MAX_CACHE 5 +#define RATE_ORDER 8 + +/* Include files. */ +/* ============== */ + +/* Configuration results */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#include "mapping.h" /* Interface definition for this class */ +#include "cmpmap.h" /* Compund Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Axis permutations */ +#include "winmap.h" /* Window scalings */ +#include "pal.h" /* SLALIB interface */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module type definitions. */ +/* ======================== */ +/* Enum to represent the data type when resampling a grid of data. */ +typedef enum DataType { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + TYPE_LD, +#endif + TYPE_D, + TYPE_F, + TYPE_L, + TYPE_UL, + TYPE_K, + TYPE_UK, + TYPE_I, + TYPE_UI, + TYPE_S, + TYPE_US, + TYPE_B, + TYPE_UB +} DataType; + +/* Data structure to hold information about a Mapping for use by + optimisation algorithms. */ +typedef struct MapData { + AstMapping *mapping; /* Pointer to the Mapping */ + AstPointSet *pset_in; /* Pointer to input PointSet */ + AstPointSet *pset_out; /* Pointer to output PointSet */ + double *lbnd; /* Pointer to lower constraints on input */ + double *ubnd; /* Pointer to upper constraints on input */ + double **ptr_in; /* Pointer to input PointSet coordinates */ + double **ptr_out; /* Pointer to output PointSet coordinates */ + int coord; /* Index of output coordinate to optimise */ + int forward; /* Use forward transformation? */ + int negate; /* Negate the output value? */ + int nin; /* Number of input coordinates per point */ + int nout; /* Number of output coordinates per point */ +} MapData; + +/* Convert from floating point to floating point or integer */ +#define CONV(IntType,val) ( ( IntType ) ? (int) ( (val) + (((val)>0)?0.5:-0.5) ) : (val) ) + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->Unsimplified_Mapping = NULL; \ + globals->Rate_Disabled = 0; + + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Mapping) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Mapping,Class_Init) +#define class_vtab astGLOBAL(Mapping,Class_Vtab) +#define getattrib_buff astGLOBAL(Mapping,GetAttrib_Buff) +#define unsimplified_mapping astGLOBAL(Mapping,Unsimplified_Mapping) +#define rate_disabled astGLOBAL(Mapping,Rate_Disabled) +#define ratefun_pset1_cache astGLOBAL(Mapping,RateFun_Pset1_Cache) +#define ratefun_pset2_cache astGLOBAL(Mapping,RateFun_Pset2_Cache) +#define ratefun_next_slot astGLOBAL(Mapping,RateFun_Next_Slot) +#define ratefun_pset_size astGLOBAL(Mapping,RateFun_Pset_Size) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ]; + +/* Pointer to origin (unsimplified) Mapping, only used for reporting + error messages. */ +static AstMapping *unsimplified_mapping = NULL; + +/* A flag which indicates if the astRate method should be disabled in + order to improve algorithm speed in cases where the rate value is not + significant. If astRate is disabled then it always returns a constant + value of 1.0. */ +static int rate_disabled = 0; + +/* static values used in function "RateFun". */ +static AstPointSet *ratefun_pset1_cache[ RATEFUN_MAX_CACHE ]; +static AstPointSet *ratefun_pset2_cache[ RATEFUN_MAX_CACHE ]; +static int ratefun_next_slot; +static int ratefun_pset_size[ RATEFUN_MAX_CACHE ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstMappingVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* Prototypes for private member functions. */ +/* ======================================== */ + +#define DECLARE_GENERIC(X,Xtype) \ +static int InterpolateKernel1##X( AstMapping *, int, const int *, const int *, \ + const Xtype *, const Xtype *, int, \ + const int *, const double *const *, \ + void (*)( double, const double *, int, \ + double *, int * ), \ + void (*)( double, const double *, int, \ + double * ), \ + int, const double *, int, Xtype, \ + Xtype *, Xtype *, int * );\ +\ +static int InterpolateLinear##X( int, const int *, const int *, const Xtype *, \ + const Xtype *, int, const int *, \ + const double *const *, int, Xtype, Xtype *, \ + Xtype *, int * ); \ +\ +static int InterpolateNearest##X( int, const int *, const int *, const Xtype *, \ + const Xtype *, int, const int *, \ + const double *const *, int, Xtype, Xtype *, \ + Xtype *, int * ); \ +\ +static int Resample##X( AstMapping *, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, \ + void (*)( void ), const double [], int, double, int, \ + Xtype, int, const int [], const int [], \ + const int [], const int [], Xtype [], Xtype [], int * ); \ +\ +static void ConserveFlux##X( double, int, const int *, Xtype, Xtype *, Xtype *, \ + int * ); \ +\ +static void InterpolateBlockAverage##X( int, const int[], const int[], \ + const Xtype [], const Xtype [], int, const int[], \ + const double *const[], const double[], int, \ + Xtype, Xtype *, Xtype *, int * ); + +DECLARE_GENERIC(B,signed char) +DECLARE_GENERIC(D,double) +DECLARE_GENERIC(F,float) +DECLARE_GENERIC(I,int) +DECLARE_GENERIC(K,INT_BIG) +DECLARE_GENERIC(L,long int) +DECLARE_GENERIC(S,short int) +DECLARE_GENERIC(UB,unsigned char) +DECLARE_GENERIC(UI,unsigned int) +DECLARE_GENERIC(UK,UINT_BIG) +DECLARE_GENERIC(UL,unsigned long int) +DECLARE_GENERIC(US,unsigned short int) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +DECLARE_GENERIC(LD,long double) +#endif + +#undef DECLARE_GENERIC + +#define DECLARE_GENERIC(X,Xtype) \ +static void Rebin##X( AstMapping *, double, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, const double [], int, \ + double, int, Xtype, int, const int [], const int [], \ + const int [], const int [], Xtype [], Xtype [], int * ); \ +\ +static void RebinSeq##X( AstMapping *, double, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, const double [], \ + int, double, int, Xtype, int, const int [], \ + const int [], const int [], const int [], Xtype [], \ + Xtype [], double [], int64_t *, int * ); \ +\ +static void SpreadKernel1##X( AstMapping *, int, const int *, const int *, \ + const Xtype *, const Xtype *, double, int, const int *, \ + const double *const *, \ + void (*)( double, const double *, int, double *, int * ), \ + int, const double *, int, Xtype, int, Xtype *, \ + Xtype *, double *, int64_t *, int * ); \ +\ +static void SpreadLinear##X( int, const int *, const int *, const Xtype *, \ + const Xtype *, double, int, const int *, const double *const *, \ + int, Xtype, int, Xtype *, Xtype *, double *, int64_t *, \ + int * ); \ +\ +static void SpreadNearest##X( int, const int *, const int *, const Xtype *, \ + const Xtype *, double, int, const int *, const double *const *, \ + int, Xtype, int, Xtype *, Xtype *, double *, \ + int64_t *, int * ); + +DECLARE_GENERIC(D,double) +DECLARE_GENERIC(F,float) +DECLARE_GENERIC(I,int) +DECLARE_GENERIC(UB,unsigned char) +DECLARE_GENERIC(B,signed char) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +DECLARE_GENERIC(LD,long double) +#endif + +#undef DECLARE_GENERIC + + + + + + +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static double FindGradient( AstMapping *, double *, int, int, double, double, double *, int * ); +static double J1Bessel( double, int * ); +static double LocalMaximum( const MapData *, double, double, double [], int * ); +static double MapFunction( const MapData *, const double [], int *, int * ); +static double MatrixDet( int, int, const double *, int * ); +static double MaxD( double, double, int * ); +static double NewVertex( const MapData *, int, double, double [], double [], int *, double [], int * ); +static double Random( long int *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static double UphillSimplex( const MapData *, double, int, const double [], double [], double *, int *, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetInvert( AstMapping *, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int GetIsSimple( AstMapping *, int * ); +static int GetNin( AstMapping *, int * ); +static int GetNout( AstMapping *, int * ); +static int GetReport( AstMapping *, int * ); +static int GetTranForward( AstMapping *, int * ); +static int GetTranInverse( AstMapping *, int * ); +static int LinearApprox( AstMapping *, const double *, const double *, double, double *, int * ); +static int MapList( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int MaxI( int, int, int * ); +static int MinI( int, int, int * ); +static int DoNotSimplify( AstMapping *, int * ); +static int QuadApprox( AstMapping *, const double[2], const double[2], int, int, double *, double *, int * ); +static int RebinAdaptively( AstMapping *, int, const int *, const int *, const void *, const void *, DataType, int, const double *, int, double, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * ); +static int RebinWithBlocking( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, const double *, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * ); +static int ResampleAdaptively( AstMapping *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, int, double, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * ); +static int ResampleSection( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, double, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * ); +static int ResampleWithBlocking( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * ); +static int SpecialBounds( const MapData *, double *, double *, double [], double [], int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestInvert( AstMapping *, int * ); +static int TestReport( AstMapping *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearInvert( AstMapping *, int * ); +static void ClearReport( AstMapping *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Gauss( double, const double [], int, double *, int * ); +static void GlobalBounds( MapData *, double *, double *, double [], double [], int * ); +static void Invert( AstMapping *, int * ); +static void MapBox( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * ); +static void RateFun( AstMapping *, double *, int, int, int, double *, double *, int * ); +static void RebinSection( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, double, DataType, int, const double *, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * ); +static void ReportPoints( AstMapping *, int, AstPointSet *, AstPointSet *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetInvert( AstMapping *, int, int * ); +static void SetReport( AstMapping *, int, int * ); +static void Sinc( double, const double [], int, double *, int * ); +static void SincCos( double, const double [], int, double *, int * ); +static void SincGauss( double, const double [], int, double *, int * ); +static void SincSinc( double, const double [], int, double *, int * ); +static void Somb( double, const double [], int, double *, int * ); +static void SombCos( double, const double [], int, double *, int * ); +static void Tran1( AstMapping *, int, const double [], int, double [], int * ); +static void Tran2( AstMapping *, int, const double [], const double [], int, double [], double [], int * ); +static void TranGrid( AstMapping *, int, const int[], const int[], double, int, int, int, int, double *, int * ); +static void TranGridAdaptively( AstMapping *, int, const int[], const int[], const int[], const int[], double, int, int, double *[], int * ); +static void TranGridSection( AstMapping *, const double *, int, const int *, const int *, const int *, const int *, int, double *[], int * ); +static void TranGridWithBlocking( AstMapping *, const double *, int, const int *, const int *, const int *, const int *, int, double *[], int * ); +static void TranN( AstMapping *, int, int, int, const double *, int, int, int, double *, int * ); +static void TranP( AstMapping *, int, int, const double *[], int, int, double *[], int * ); +static void ValidateMapping( AstMapping *, int, int, int, int, const char *, int * ); + + + +/* Member functions. */ +/* ================= */ +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Mapping member function (over-rides the astClearAttrib protected +* method inherited from the Object class). + +* Description: +* This function clears the value of a specified attribute for a +* Mapping, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Mapping. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstMapping *this; /* Pointer to the Mapping structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Mapping structure. */ + this = (AstMapping *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Invert. */ +/* ------- */ + if ( !strcmp( attrib, "invert" ) ) { + astClearInvert( this ); + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + astClearReport( this ); + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class. If it does, then report an + error. */ + } else if ( !strcmp( attrib, "nin" ) || + !strcmp( attrib, "nout" ) || + !strcmp( attrib, "issimple" ) || + !strcmp( attrib, "islinear" ) || + !strcmp( attrib, "tranforward" ) || + !strcmp( attrib, "traninverse" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +/* +* Name: +* ConserveFlux + +* Purpose: +* Scale the output data and variance values produced by ResampleSection +* by the given flux conservation factor. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void ConserveFlux( double factor, int npoint, const int *offset, +* badval, *out, +* *out_var ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which scale the supplied resampled data +* values by the given flux conservation factor. It also scales any +* variances by the square of the factor. + +* Parameters: +* factor +* The flux conservation factor. This should be the ratio of the +* output pixel size to the input pixel size, in the locality of +* the supplied data values. +* npoint +* The number of points at which the input grid was resampled. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each output point, this array should contain the zero-based +* offset in the output array(s) (i.e. the "out" and, +* optionally, the "out_var" arrays) at which the resampled +* output value(s) is stored. +* badval +* This parameter specifies the value which is used to identify +* bad data and/or variance values in the output array(s). +* out +* Pointer to an array in which the resampled data is supplied. Note +* that details of how the output grid maps on to this array +* (e.g. the storage order, number of dimensions, etc.) is +* arbitrary and is specified entirely by means of the "offset" +* array. The "out" array should therefore contain sufficient +* elements to accommodate the "offset" values supplied. There +* is no requirement that all elements of the "out" array should +* be assigned values, and any which are not addressed by the +* contents of the "offset" array will be left unchanged. +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, in which variance estimates for +* the resampled values are supplied. If no output variance estimates +* are available, a NULL pointer should be given. + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_CONSERVEFLUX(X,Xtype) \ +static void ConserveFlux##X( double factor, int npoint, const int *offset, \ + Xtype badval, Xtype *out, Xtype *out_var, int *status ) { \ +\ +/* Local Variables: */ \ + int off_out; /* Pixel offset into output array */ \ + int point; /* Loop counter for output points */ \ +\ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ + for ( point = 0; point < npoint; point++ ) { \ + off_out = offset[ point ]; \ + if( out[ off_out ] != badval ) out[ off_out ] *= factor; \ + } \ +\ + if( out_var ) { \ + factor *= factor; \ + for ( point = 0; point < npoint; point++ ) { \ + off_out = offset[ point ]; \ + if( out_var[ off_out ] != badval ) out_var[ off_out ] *= factor; \ + } \ + } \ +} + + +/* Expand the macro above to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_CONSERVEFLUX(LD,long double) +#endif +MAKE_CONSERVEFLUX(D,double) +MAKE_CONSERVEFLUX(F,float) +MAKE_CONSERVEFLUX(K,INT_BIG) +MAKE_CONSERVEFLUX(L,long int) +MAKE_CONSERVEFLUX(I,int) +MAKE_CONSERVEFLUX(S,short int) +MAKE_CONSERVEFLUX(B,signed char) +MAKE_CONSERVEFLUX(UL,unsigned long int) +MAKE_CONSERVEFLUX(UI,unsigned int) +MAKE_CONSERVEFLUX(UK,UINT_BIG) +MAKE_CONSERVEFLUX(US,unsigned short int) +MAKE_CONSERVEFLUX(UB,unsigned char) + +/* Undefine the macros used above. */ +#undef MAKE_CONSERVEFLUX + +static void Decompose( AstMapping *this, AstMapping **map1, AstMapping **map2, + int *series, int *invert1, int *invert2, int *status ) { +/* +*+ +* Name: +* astDecompose + +* Purpose: +* Decompose a Mapping into two component Mappings. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* void astDecompose( AstMapping *this, AstMapping **map1, +* AstMapping **map2, int *series, int *invert1, +* int *invert2 ) + +* Class Membership: +* Mapping method. + +* Description: +* This function returns pointers to two Mappings which, when applied +* either in series or parallel, are equivalent to the supplied Mapping. +* +* Since the Frame class inherits from the Mapping class, Frames can +* be considered as special types of Mappings and so this method can +* be used to decompose CmpMaps, CmpFrames, CmpRegions or Prisms. + +* Parameters: +* this +* Pointer to the Mapping. +* map1 +* Address of a location to receive a pointer to first component +* Mapping. +* map2 +* Address of a location to receive a pointer to second component +* Mapping. +* series +* Address of a location to receive a value indicating if the +* component Mappings are applied in series or parallel. A non-zero +* value means that the supplied Mapping is equivalent to applying map1 +* followed by map2 in series. A zero value means that the supplied +* Mapping is equivalent to applying map1 to the lower numbered axes +* and map2 to the higher numbered axes, in parallel. +* invert1 +* The value of the Invert attribute to be used with map1. +* invert2 +* The value of the Invert attribute to be used with map2. + +* Applicability: +* CmpMap +* If the supplied Mapping is a CmpMap, then map1 and map2 will be +* returned holding pointers to the component Mappings used to +* create the CmpMap, either in series or parallel. +* Mapping +* For any class of Mapping other than a CmpMap, map1 will be +* returned holding a clone of the supplied Mapping pointer, and map2 +* will be returned holding a NULL pointer. +* CmpFrame +* If the supplied Mapping is a CmpFrame, then map1 and map2 will be +* returned holding pointers to the component Frames used to +* create the CmpFrame. The component Frames are considered to be in +* applied in parallel. +* Frame +* For any class of Frame other than a CmpFrame, map1 will be +* returned holding a clone of the supplied Frame pointer, and map2 +* will be returned holding a NULL pointer. + +* Notes: +* - Any changes made to the component Mappings using the returned +* pointers will be reflected in the supplied Mapping. +* - The returned Invert values should be used in preference to the +* current values of the Invert attribute in map1 and map2. This is +* because the attributes may have changed value since the Mappings +* were combined. + +* Implementation Notes: +* - This function implements the basic astDecompose method +* available via the protected interface to the Frame class. The +* public interface to this method is provided by the +* astDecomposeId_ function. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* The basic Mapping class returns a clone of the supplied Mapping as + map1 and a NULL pointer as map2. */ + if( map1 ) *map1 = astClone( this ); + if( map2 ) *map2 = NULL; + if( series ) *series = 1; + if( invert1 ) *invert1 = astGetInvert( this ); + if( invert2 ) *invert2 = 0; +} + +static int DoNotSimplify( AstMapping *this, int *status ) { +/* +*+ +* Name: +* astMapMerge + +* Purpose: +* Check if a Mapping is appropriate for simplification. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astDoNotSImplify( AstMapping *this ); + +* Class Membership: +* Mapping method. + +* Description: +* This function returns a flag indivating if the supplied Mapping is +* appropriate for simplification. + +* Parameters: +* this +* Pointer to the Mapping. + +* Returned Value: +* Non-zero if the supplied Mapping is not appropriate for +* simplification, and zero otherwise. + +* Notes: +* - A value of 0 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check inherited status. */ + if( !astOK ) return 0; + +/* Mappings that have a set value for the Ident attribute should not be + simplified since we want to preserve their individual identify (otherwise + why would the user have given them an Ident value?). */ + return astTestIdent( this ); +} + +int astRateState_( int disabled, int *status ) { +/* +*+ +* Name: +* astRateState + +* Purpose: +* Control whether the astRate method is disabled or not. + +* Type: +* Protected function. + +* Synopsis: +* #include "mapping.h" +* int astRateState( int disabled ) + +* Class Membership: +* Mapping member function + +* Description: +* Some algorithms which use use the astRate method do not actually need +* to know what the Rate value is. For instance, when the Plot class draws +* a border it evaluates the GRAPHICS->Current Mapping hundreds of time. +* If the Mapping includes a RateMap then this can be very very slow +* (depending on how the astRate method is implemented). In fact the +* border drawing algorithm onlyneeds to know if the result is bad or +* not - the actual value produced by the Mappign does not matter. +* +* Such algorithms can be speeded up by forcing the astRate method to +* return a constant value rather than actually doing the numerical +* differentiation. This can be accomplised by calling this method prior +* to implementing the algorithm. It should be called at the end in +* order to re-instate the original disabled flag. + +* Parameters: +* disabled +* The new value for the astRate disabled flag. + +* Returned Value: +* The original value of the astRate disabled flag. + +*- +*/ + astDECLARE_GLOBALS + int result; + astGET_GLOBALS(NULL); + + result = rate_disabled; + rate_disabled = disabled; + return result; +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Mappings are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* Mapping member function (over-rides the astEqual protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two Mappings are equivalent. +* +* The implementation provided by this class (the base Mapping class) +* simply reports an error when called, since all concrete Mapping +* subclasses should provide their own implementation. +* +* Note, sub-class implementations should not use astSimplify (e.g. +* combining the two Mapping and then simplifying it), since the +* astSimplify method for certain classes (e.g. CmpMap) may use +* astEqual. Consequently, if astEqual called astSimplify, there would +* be possibilities for infinite loops. + +* Parameters: +* this +* Pointer to the first Object (a Mapping). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the Frames are equivalent, zero otherwise. + +* Notes: +* - The two Mappings are considered equivalent if the combination of +* the first in series with the inverse of the second simplifies to a +* UnitMap. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent Object class. This checks + that the Objects are both of the same class (amongst other things). */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Report an error since the concrete sub-class should have over-riden + this method. */ + astError( AST__INTER, "astEqual(Mapping): The %s class does " + "not override the abstract astEqual method inherited " + "from the base Mapping class (internal AST programming " + "error).", status, astGetClass( this_object ) ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static double FindGradient( AstMapping *map, double *at, int ax1, int ax2, + double x0, double h, double *range, int *status ){ +/* +* Name: +* FindGradient + +* Purpose: +* Find the mean gradient in an interval, and the range of gradients +* within the interval. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double FindGradient( AstMapping *map, double *at, int ax1, int ax2, +* double x0, double h, double *range, int *status ) + +* Class Membership: +* Mapping method. + +* Description: +* This function finds the mean gradient in an interval, and the range +* of gradients within the interval. + +* Parameters: +* map +* Pointer to a Mapping which yields the value of the function at x. +* The Mapping may have any number of inputs and outputs; the specific +* output representing the function value, f, is specified by ax1 and +* the specific input representing the argument, x, is specified by ax2. +* at +* A pointer to an array holding axis values at the position at which +* the function is to be evaluated. The number of values supplied +* must equal the number of inputs to the Mapping. The value supplied +* for axis "ax2" is ignored (the value of "x" is used for axis "ax2"). +* ax1 +* The zero-based index of the Mapping output which is to be +* differentiated. Set this to -1 to allocate, or -2 to release, +* the static resources used by this function. +* ax2 +* The zero-based index of the Mapping input which is to be varied. +* x0 +* The central axis value at which the function is to be evaluated. +* h +* The interval over which the fitting is to be performed. +* range +* A pointer to a location at which to return the range of +* gradients found within the interval. +* status +* Pointer to the inherited status variable. + +* Returns: +* The mean gradient, or AST__BAD if the mean gradient cannot be +* calculated. +*/ + +/* Local Variables: */ + double dh; + double g; + double gmax; + double gmin; + double ret; + double x1; + double x2; + double x[ RATE_ORDER + 2 ]; + double y1; + double y2; + double y[ RATE_ORDER + 2 ]; + int i0; + int i; + int ngood; + +/* Initialise */ + ret = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Store the x values at (RATE_ORDER+1) evenly spaced points over the interval + "h" centred on "x0". */ + i0 = RATE_ORDER/2; + dh = h/RATE_ORDER; + + for( i = 0; i <= RATE_ORDER; i++ ) { + x[ i ] = x0 + ( i - i0 )*dh; + } + +/* Get the function values at these positions. */ + RateFun( map, at, ax1, ax2, RATE_ORDER + 1, x, y, status ); + +/* Find the maximum and minimum mean gradient within any sub-interval, and + note the (x,y) values at the first and last good point within the + interval. */ + y1 = AST__BAD; + y2 = AST__BAD; + gmax = AST__BAD; + gmin = AST__BAD; + ngood = 0; + + for( i = 0; i < RATE_ORDER; i++ ) { + if( y[ i + 1 ] !=AST__BAD && y[ i ] != AST__BAD && + x[ i + 1 ] != x[ i ] ) { + ngood++; + + g = ( y[ i + 1 ] - y[ i ] )/( x[ i + 1 ] - x[ i ] ); + + if( ngood == 1 ) { + gmax = gmin = g; + } else if( g < gmin ) { + gmin = g; + } else if( g > gmax) { + gmax = g; + } + if( y1 == AST__BAD ) { + y1 = y[ i ]; + x1 = x[ i ]; + } + y2 = y[ i + 1 ]; + x2 = x[ i + 1 ]; + } + } + +/* If two or more sub-intervals were usable, return the range of + gradients found, and the mean gradient. */ + if( ngood > 1 ) { + ret = ( y2 - y1 )/( x2 - x1 ); + if( range ) *range = ( gmax - gmin ); + } + + return ret; +} + +static void Gauss( double offset, const double params[], int flags, + double *value, int *status ) { +/* +* Name: +* Gauss + +* Purpose: +* 1-dimensional Gaussian spreading kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void Gauss( double offset, const double params[], int flags, +* double *value, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of a 1-dimensional sub-pixel +* spreading kernel. The function used is exp(-k*x*x). + +* Parameters: +* offset +* The offset of a pixel from the central output point, measured +* in pixels. +* params +* The first element of this array should give a value for "k" +* in the exp(-k*x*x) term. +* flags +* Not used. +* value +* Pointer to a double to receive the calculated kernel value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Calculate the result. */ + *value = exp( -params[ 0 ] * offset * offset ); +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Mapping member function (over-rides the protected astGetAttrib +* method inherited from the Object class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Mapping, formatted as a character string. + +* Parameters: +* this +* Pointer to the Mapping. +* attrib +* Pointer to a null terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Mapping, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Mapping. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *this; /* Pointer to the Mapping structure */ + const char *result; /* Pointer value to return */ + int invert; /* Invert attribute value */ + int islinear; /* IsLinear attribute value */ + int issimple; /* IsSimple attribute value */ + int nin; /* Nin attribute value */ + int nout; /* Nout attribute value */ + int report; /* Report attribute value */ + int tran_forward; /* TranForward attribute value */ + int tran_inverse; /* TranInverse attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Mapping structure. */ + this = (AstMapping *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Invert. */ +/* ------- */ + if ( !strcmp( attrib, "invert" ) ) { + invert = astGetInvert( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", invert ); + result = getattrib_buff; + } + +/* IsLinear. */ +/* --------- */ + } else if ( !strcmp( attrib, "islinear" ) ) { + islinear = astGetIsLinear( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", islinear ); + result = getattrib_buff; + } + +/* IsSimple. */ +/* --------- */ + } else if ( !strcmp( attrib, "issimple" ) ) { + issimple = astGetIsSimple( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", issimple ); + result = getattrib_buff; + } + +/* Nin. */ +/* ---- */ + } else if ( !strcmp( attrib, "nin" ) ) { + nin = astGetNin( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nin ); + result = getattrib_buff; + } + +/* Nout. */ +/* ----- */ + } else if ( !strcmp( attrib, "nout" ) ) { + nout = astGetNout( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nout ); + result = getattrib_buff; + } + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + report = astGetReport( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", report ); + result = getattrib_buff; + } + +/* TranForward. */ +/* ------------ */ + } else if ( !strcmp( attrib, "tranforward" ) ) { + tran_forward = astGetTranForward( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", tran_forward ); + result = getattrib_buff; + } + +/* TranInverse. */ +/* ------------ */ + } else if ( !strcmp( attrib, "traninverse" ) ) { + tran_inverse = astGetTranInverse( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", tran_inverse ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetIsLinear( AstMapping *this, int *status ) { +/* +*+ +* Name: +* astGetIsLinear + +* Purpose: +* Determine if a Mapping is an instance of a linear Mapping class. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astGetIsLinear( AstMapping *this ) + +* Class Membership: +* Mapping method. + +* Description: +* This function returns a value indicating whether a Mapping is +* a member of a class of linear Mappings. The base Mapping class +* returns a value of zero. Linear Mapping classes should over-ride +* this function to return a non-zero value. + +* Parameters: +* this +* Pointer to the Mapping. + +* Returned Value: +* One if the Mapping is a member of a linear Mapping class. Zero +* otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + return 0; +} + +static int GetNin( AstMapping *this, int *status ) { +/* +*+ +* Name: +* astGetNin + +* Purpose: +* Get the number of input coordinates for a Mapping. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astGetNin( AstMapping *this ) + +* Class Membership: +* Mapping method. + +* Description: +* This function returns the number of input coordinate values +* required per point by a Mapping (i.e. the number of dimensions +* of the space in which input points reside). + +* Parameters: +* this +* Pointer to the Mapping. + +* Returned Value: +* Number of coordinate values required. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + int invert; /* Invert attribute value */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the Mapping has been inverted. */ + invert = astGetInvert( this ); + +/* Obtain the Nin value. */ + if ( astOK ) result = invert ? this->nout : this->nin; + +/* Return the result. */ + return result; +} + +static int GetNout( AstMapping *this, int *status ) { +/* +*+ +* Name: +* astGetNout + +* Purpose: +* Get the number of output coordinates for a Mapping. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astGetNout( AstMapping *this ) + +* Class Membership: +* Mapping method. + +* Description: +* This function returns the number of output coordinate values +* generated per point by a Mapping (i.e. the number of dimensions +* of the space in which output points reside). + +* Parameters: +* this +* Pointer to the Mapping. + +* Returned Value: +* Number of coordinate values generated. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + int invert; /* Invert attribute value */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the Mapping has been inverted. */ + invert = astGetInvert( this ); + +/* Obtain the Nout value. */ + if ( astOK ) result = invert ? this->nin : this->nout; + +/* Return the result. */ + return result; +} + +static int GetTranForward( AstMapping *this, int *status ) { +/* +*+ +* Name: +* astGetTranForward + +* Purpose: +* Determine if a Mapping defines a forward coordinate transformation. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astGetTranForward( AstMapping *this ) + +* Class Membership: +* Mapping method. + +* Description: +* This function returns a value indicating whether a Mapping is +* able to perform a coordinate transformation in the "forward" +* direction. + +* Parameters: +* this +* Pointer to the Mapping. + +* Returned Value: +* Zero if the forward coordinate transformation is not defined, or +* 1 if it is. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + int invert; /* Mapping inverted? */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the Mapping has been inverted. */ + invert = astGetInvert( this ); + +/* If OK, obtain the result. */ + if ( astOK ) result = invert ? this->tran_inverse : this->tran_forward; + +/* Return the result. */ + return result; +} + +static int GetTranInverse( AstMapping *this, int *status ) { +/* +*+ +* Name: +* astGetTranInverse + +* Purpose: +* Determine if a Mapping defines an inverse coordinate transformation. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astGetTranInverse( AstMapping *this ) + +* Class Membership: +* Mapping method. + +* Description: +* This function returns a value indicating whether a Mapping is +* able to perform a coordinate transformation in the "inverse" +* direction. + +* Parameters: +* this +* Pointer to the Mapping. + +* Returned Value: +* Zero if the inverse coordinate transformation is not defined, or +* 1 if it is. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + int invert; /* Mapping inverted? */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Determine if the Mapping has been inverted. */ + invert = astGetInvert( this ); + +/* If OK, obtain the result. */ + if ( astOK ) result = invert ? this->tran_forward : this->tran_inverse; + +/* Return the result. */ + return result; +} + +static void GlobalBounds( MapData *mapdata, double *lbnd, double *ubnd, + double xl[], double xu[], int *status ) { +/* +* Name: +* GlobalBounds + +* Purpose: +* Estimate global coordinate bounds for a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GlobalBounds( MapData *mapdata, double *lbnd, double *ubnd, +* double xl[], double xu[], int *status ); + +* Class Membership: +* Mapping member function. + +* Description: +* This function estimates the global lower and upper bounds of a +* Mapping function within a constrained region of its input +* coordinate space. It uses a robust global optimisation algorithm +* based on the selection of pseudo-random starting positions, +* followed by the location of local minima and maxima using the +* downhill (or uphill) simplex method. The algorithm will cope +* with the case where there are several competing minima (or +* maxima) with nearly equal values. It attempts to locate the +* global bounds to full machine precision when possible. + +* Parameters: +* mapdata +* Pointer to a MapData structure describing the Mapping +* function, its coordinate constraints, etc. +* lbnd +* Pointer to a double. On entry, this should contain a +* previously-obtained upper limit on the global lower bound, or +* AST__BAD if no such limit is available. On exit, it will be +* updated with a new estimate of the global lower bound, if a +* better one has been found. +* ubnd +* Pointer to a double. On entry, this should contain a +* previously-obtained lower limit on the global upper bound, or +* AST__BAD if no such limit is available. On exit, it will be +* updated with a new estimate of the global upper bound, if a +* better one has been found. +* xl +* Pointer to an array of double, with one element for each +* input coordinate. On entry, if *lbnd is not equal to AST__OK, +* this should contain the input coordinates of a point at which +* the Mapping function takes the value *lbnd. On exit, this +* function returns the position of a (not necessarily unique) +* input point at which the Mapping function takes the value of +* the new global lower bound. This array is not altered if an +* improved estimate of the global lower bound cannot be found. +* xu +* Pointer to an array of double, with one element for each +* input coordinate. On entry, if *ubnd is not equal to AST__OK, +* this should contain the input coordinates of a point at which +* the Mapping function takes the value *ubnd. On exit, this +* function returns the position of a (not necessarily unique) +* input point at which the Mapping function takes the value of +* the new global upper bound. This array is not altered if an +* improved estimate of the global upper bound cannot be found. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The efficiency of this function will usually be improved if +* previously-obtained estimates of the extrema and their locations +* are provided. +* - The values returned via "lbnd", "ubnd", "xl" and "xu" will be +* set to the value AST__BAD if this function should fail for any +* reason. Their initial values on entry will not be altered if the +* function is invoked with the global error status set. +*/ + +/* Local Constants: */ + const double default_acc = 3.0e-5; /* Default convergence accuracy */ + const int maxiter = 10000; /* Maximum number of iterations */ + const int minsame = 5; /* Minimum no. consistent extrema required */ + const int nbatch = 32; /* No. function samples obtained per batch */ + +/* Local Variables: */ + AstPointSet *pset_in; /* Input PointSet for batch transformation */ + AstPointSet *pset_out; /* Output PointSet for batch transformation */ + double **ptr_in; /* Pointer to batch input coordinates */ + double **ptr_out; /* Pointer to batch output coordinates */ + double *active_hi; /* Estimated upper limits of active region */ + double *active_lo; /* Estimated lower limits of active region */ + double *sample_hi; /* Upper limits of sampled region */ + double *sample_lo; /* Lower limits of sampled region */ + double *sample_width; /* Nominal widths of sampled region */ + double *x; /* Pointer to array of coordinates */ + double acc; /* Convergence accuracy for finding maximum */ + double active_width; /* Estimated width of active region */ + double new_max; /* Value of new local maximum */ + double new_min; /* Value of new local minimum */ + double oversize; /* Over-size factor for sampled region */ + double random; /* Pseudo-random number */ + int bad; /* Transformed position is bad? */ + int batch; /* Next element to use in position batch */ + int coord; /* Loop counter for coordinates */ + int done_max; /* Satisfactory global maximum found? */ + int done_min; /* Satisfactory global minimum found? */ + int iter; /* Loop counter for iterations */ + int ncoord; /* Number of coordinates in search space */ + int nmax; /* Number of local maxima found */ + int nmin; /* Number of local minima found */ + int nsame_max; /* Number of equivalent local maxima found */ + int nsame_min; /* Number of equivalent local minima found */ + long int seed = 1776655449; /* Arbitrary pseudo-random number seed */ + +/* Check the global error status */ + if ( !astOK ) return; + +/* Initialise. */ + done_max = 0; + done_min = 0; + nmax = 0; + nmin = 0; + nsame_max = 0; + nsame_min = 0; + pset_in = NULL; + pset_out = NULL; + ptr_in = NULL; + ptr_out = NULL; + oversize = 0; + bad = 0; + +/* Extract the number of input coordinates for the Mapping function + and allocate workspace. */ + ncoord = mapdata->nin; + active_hi = astMalloc( sizeof( double ) * (size_t) ncoord ); + active_lo = astMalloc( sizeof( double ) * (size_t) ncoord ); + sample_hi = astMalloc( sizeof( double ) * (size_t) ncoord ); + sample_lo = astMalloc( sizeof( double ) * (size_t) ncoord ); + sample_width = astMalloc( sizeof( double ) * (size_t) ncoord ); + x = astMalloc( sizeof( double ) * (size_t) ncoord ); + if ( astOK ) { + +/* Calculate the factor by which the size of the region we sample will + exceed the size of the Mapping function's active region (the region + where the transformed coordinates are non-bad) in each + dimension. This is chosen so that the volume ratio will be 2. */ + oversize = pow( 2.0, 1.0 / (double) ncoord ); + +/* Initialise the limits of the active region to unknown. */ + for ( coord = 0; coord < ncoord; coord++ ) { + active_lo[ coord ] = DBL_MAX;; + active_hi[ coord ] = -DBL_MAX; + +/* Initialise the nominal widths of the sampled region to be the + actual widths of the search region times the over-size factor. */ + sample_width[ coord ] = ( mapdata->ubnd[ coord ] - + mapdata->lbnd[ coord ] ) * oversize; + +/* Initialise the sampled region to match the search region. */ + sample_lo[ coord ] = mapdata->lbnd[ coord ]; + sample_hi[ coord ] = mapdata->ubnd[ coord ]; + } + +/* Set up position buffer. */ +/* ======================= */ +/* Create two PointSets to act as buffers to hold a complete batch of + input and output coordinates. Obtain pointers to their coordinate + arrays. */ + pset_in = astPointSet( nbatch, ncoord, "", status ); + pset_out = astPointSet( nbatch, mapdata->nout, "", status ); + ptr_in = astGetPoints( pset_in ); + ptr_out = astGetPoints( pset_out ); + +/* Initialise the next element to be used in the position buffer to + indicate that the buffer is initially empty. */ + batch = nbatch; + } + +/* Define a macro to fill the position buffer with a set of + pseudo-random positions and to transform them. */ +#define FILL_POSITION_BUFFER {\ +\ +/* We first generate a suitable volume over which to distribute the\ + batch of pseudo-random positions. Initially, this will be the\ + entire search volume, but if we find that the only non-bad\ + transformed coordinates we obtain are restricted to a small\ + sub-region of this input volume, then we reduce the sampled volume\ + so as to concentrate more on the active region. */\ +\ +/* Loop through each input coordinate, checking that at least one\ + non-bad transformed point has been obtained. If not, we do not\ + adjust the sampled volume, as we do not yet know where the active\ + region lies. */\ + for ( coord = 0; coord < ncoord; coord++ ) {\ + if ( active_hi[ coord ] >= active_lo[ coord ] ) {\ +\ +/* Estimate the width of the active region from the range of input\ + coordinates that have so far produced non-bad transformed\ + coordinates. */\ + active_width = active_hi[ coord ] - active_lo[ coord ];\ +\ +/* If the current width of the sampled volume exceeds this estimate by\ + more than the required factor, then reduce the width of the sampled\ + volume. The rate of reduction is set so that the volume of the\ + sampled region can halve with every fourth batch of positions. */\ + if ( ( active_width * oversize ) < sample_width[ coord ] ) {\ + sample_width[ coord ] /= pow( oversize, 0.25 );\ +\ +/* If the width of the sampled volume does not exceed that of the\ + known active region by the required factor, then adjust it so that\ + it does. Note that we must continue to sample some points outside\ + the known active region in case we have missed any (in which case\ + the sampled region will expand again to include them). */\ + } else if ( ( active_width * oversize ) > sample_width[ coord ] ) {\ + sample_width[ coord ] = active_width * oversize;\ + }\ +\ +/* Calculate the lower and upper bounds on the sampled volume, using\ + the new width calculated above and centring it on the active\ + region, as currently known. */\ + sample_lo[ coord ] = ( active_lo[ coord ] + active_hi[ coord ] -\ + sample_width[ coord ] ) * 0.5;\ + sample_hi[ coord ] = ( active_lo[ coord ] + active_hi[ coord ] +\ + sample_width[ coord ] ) * 0.5;\ +\ +/* Ensure that the sampled region does not extend beyond the original\ + search region. */\ + if ( sample_lo[ coord ] < mapdata->lbnd[ coord ] ) {\ + sample_lo[ coord ] = mapdata->lbnd[ coord ];\ + }\ + if ( sample_hi[ coord ] > mapdata->ubnd[ coord ] ) {\ + sample_hi[ coord ] = mapdata->ubnd[ coord ];\ + }\ + }\ + }\ +\ +/* Having determined the size of the sampled volume, create a batch of\ + pseudo-random positions uniformly distributed within it. */\ + for ( batch = 0; batch < nbatch; batch++ ) {\ + for ( coord = 0; coord < ncoord; coord++ ) {\ + random = Random( &seed, status );\ + ptr_in[ coord ][ batch ] = sample_lo[ coord ] * random +\ + sample_hi[ coord ] * ( 1.0 - random );\ + }\ + }\ +\ +/* Transform these positions. We process them in a single batch in\ + order to minimise the overheads in doing this. */\ + (void) astTransform( mapdata->mapping, pset_in, mapdata->forward,\ + pset_out );\ +\ +/* Indicate that the position buffer is now full. */\ + batch = 0;\ +} + +/* Fill the position buffer using the above macro. (Note that because + we do not yet have an estimate of the size of the active region, + this does not change the sampled region size from our earlier + initialised values. */ + FILL_POSITION_BUFFER; + +/* Iterate. */ +/* ======== */ +/* Loop to perform up to "maxiter" iterations to estimate the global + minimum and maximum. */ + for ( iter = 0; astOK && ( iter < maxiter ); iter++ ) { + +/* Determine the search accuracy. */ +/* ============================== */ +/* Decide the accuracy to which local extrema should be found. The + intention here is to optimise performance, especially where one + extremum lies near zero and so could potentially be found to + unnecessarily high precision. If we make a mis-assumption (the code + below is not fool-proof), we will slow things down for this + iteration, but the error will be corrected in future iterations + once better estimates are available. */ + +/* If we have no current estimate of either global extremum, we assume + the values we eventually obtain will be of order unity and required + to the default accuracy. */ + acc = default_acc; + +/* If we already have an estimate of both global extrema, we set the + accuracy level so that the difference between them will be known to + the default accuracy. */ + if ( ( *lbnd != AST__BAD ) && ( *ubnd != AST__BAD ) ) { + acc = fabs( *ubnd - *lbnd ) * default_acc; + +/* If we have an estimate of only one global extremum, we assume that + the difference between the two global extrema will eventually be of + the same order as the estimate we currently have, so long as this + is not less than unity. */ + } else if ( *lbnd != AST__BAD ) { + if ( fabs( *lbnd ) > 1.0 ) acc = fabs( *lbnd) * default_acc; + } else if ( *ubnd != AST__BAD ) { + if ( fabs( *ubnd ) > 1.0 ) acc = fabs( *ubnd) * default_acc; + } + +/* Search for a new local minimum. */ +/* =============================== */ +/* If we are still searching for the global minimum, then obtain a set + of starting coordinates from which to find a new local minimum. */ + if ( !done_min ) { + +/* On the first iteration, start searching at the position where the + best estimate of the global minimum (if any) has previously been + found. We know that this produces non-bad transformed + coordinates. */ + bad = 0; + if ( !iter && ( *lbnd != AST__BAD ) ) { + for ( coord = 0; coord < ncoord; coord++ ) { + x[ coord ] = xl[ coord ]; + } + +/* Otherwise, if no estimate of the global minimum is available, then + start searching at the position where the best estimate of the + global maximum (if any) has been found. This may be a long way from + a local minimum, but at least it will yield a non-bad value for the + Mapping function, so some sort of estimate of the global minimum + will be obtained. This is important in cases where finding the + active region of the function is the main problem. Note that this + condition can only occur once, since the global minimum will have + an estimate on the next iteration. */ + } else if ( ( *lbnd == AST__BAD ) && ( *ubnd != AST__BAD ) ) { + for ( coord = 0; coord < ncoord; coord++ ) { + x[ coord ] = xu[ coord ]; + } + +/* Having exhausted the above possibilities, we use pseudo-random + starting positions which are uniformly distributed throughout the + search volume. First check to see if the buffer containing such + positions is empty and refill it if necessary. */ + } else { + if ( batch >= nbatch ) FILL_POSITION_BUFFER; + +/* Test the next available set of output (transformed) coordinates in + the position buffer to see if they are bad. */ + if ( astOK ) { + for ( coord = 0; coord < mapdata->nout; coord++ ) { + bad = ( ptr_out[ coord ][ batch ] == AST__BAD ); + if ( bad ) break; + } + +/* If not, we have a good starting position for finding a local + minimum, so extract the corresponding input coordinates. */ + if ( !bad ) { + for ( coord = 0; coord < ncoord; coord++ ) { + x[ coord ] = ptr_in[ coord ][ batch ]; + } + } + +/* Increment the position buffer location. */ + batch++; + } + } + +/* If we do not have a good starting position, we can't do anything + more on this iteration. A new position will be obtained and tested + on the next iteration and this (we hope) will eventually identify a + suitable starting point. */ + if ( astOK && !bad ) { + +/* Form estimates of the lower and upper limits of the active region + from the starting positions used. */ + for ( coord = 0; coord < ncoord; coord++ ) { + if ( x[ coord ] < active_lo[ coord ] ) { + active_lo[ coord ] = x[ coord ]; + } + if ( x[ coord ] > active_hi[ coord ] ) { + active_hi[ coord ] = x[ coord ]; + } + } + +/* Indicate that the Mapping function should be negated (because we + want a local minimum) and then search for a local maximum in this + negated function. If the result is non-bad (as it should always be, + barring an error), then negate it to obtain the value of the local + minimum found. */ + mapdata->negate = 1; + new_min = LocalMaximum( mapdata, acc, 0.01, x, status ); + if ( new_min != AST__BAD ) { + new_min = -new_min; + +/* Update the estimates of the lower and upper bounds of the active + region to take account of where the minimum was found. */ + for ( coord = 0; coord < ncoord; coord++ ) { + if ( x[ coord ] < active_lo[ coord ] ) { + active_lo[ coord ] = x[ coord ]; + } + if ( x[ coord ] > active_hi[ coord ] ) { + active_hi[ coord ] = x[ coord ]; + } + } + +/* Count the number of times we successfully locate a local minimum + (ignoring the fact they might all be the same one). */ + nmin++; + +/* Update the global minimum. */ +/* ========================== */ +/* If this is the first estimate of the global minimum, then set to + one the count of the number of consecutive iterations where this + estimate remains unchanged. Store the minimum value and its + position. */ + if ( *lbnd == AST__BAD ) { + nsame_min = 1; + *lbnd = new_min; + for ( coord = 0; coord < ncoord; coord++ ) { + xl[ coord ] = x[ coord ]; + } + +/* Otherwise, test if this local minimum is lower than the previous + estimate of the global minimum. If so, then reset the count of + unchanged estimates of the global mimimum to one if the difference + exceeds the accuracy with which the minimum was found (i.e. if we + have found a significantly different minimum). Otherwise, just + increment this count (because we have found the same minimum but by + chance with slightly improved accuracy). Store the new minimum and + its position. */ + } else if ( new_min < *lbnd ) { + nsame_min = ( ( *lbnd - new_min ) > acc ) ? 1 : + nsame_min + 1; + *lbnd = new_min; + for ( coord = 0; coord < ncoord; coord++ ) { + xl[ coord ] = x[ coord ]; + } + +/* If the latest local minimum is no improvement on previous estimates + of the global minimum, then increment the count of unchanged + estimates of the global mimimum, but do not save the new one. */ + } else { + nsame_min++; + } + +/* Determine if a satisfactory estimate of the global minimum has been + obtained. It has if the number of consecutive local minima which + have not significantly improved the estimate is at least equal to + "minsame", and at least 30% of the total number of local minima + found. */ + if ( ( nsame_min >= minsame ) && + ( nsame_min >= (int) ( 0.3f * (float) nmin + 0.5f ) ) ) { + done_min = 1; + } + } + } + } + +/* Search for a new local maximum. */ +/* =============================== */ +/* Now repeat all of the above to find a new local maximum which + estimates the global maximum. */ + if ( !done_max ) { + +/* Choose a suitable starting position, based on one already available + if appropriate. */ + if ( !iter && ( *ubnd != AST__BAD ) ) { + for ( coord = 0; coord < ncoord; coord++ ) { + x[ coord ] = xu[ coord ]; + } + + } else if ( ( *ubnd == AST__BAD ) && ( *lbnd != AST__BAD ) ) { + for ( coord = 0; coord < ncoord; coord++ ) { + x[ coord ] = xl[ coord ]; + } + +/* Otherwise use a pseudo-random position, refilling the position + buffer if necessary. Check if the transformed coordinates are + bad. */ + } else { + if ( batch >= nbatch ) FILL_POSITION_BUFFER; + if ( astOK ) { + for ( coord = 0; coord < mapdata->nout; coord++ ) { + bad = ( ptr_out[ coord ][ batch ] == AST__BAD ); + if ( bad ) break; + } + if ( !bad ) { + for ( coord = 0; coord < ncoord; coord++ ) { + x[ coord ] = ptr_in[ coord ][ batch ]; + } + } + batch++; + } + } + +/* If the coordinates are OK, update the active region limits. */ + if ( astOK && !bad ) { + for ( coord = 0; coord < ncoord; coord++ ) { + if ( x[ coord ] < active_lo[ coord ] ) { + active_lo[ coord ] = x[ coord ]; + } + if ( x[ coord ] > active_hi[ coord ] ) { + active_hi[ coord ] = x[ coord ]; + } + } + +/* Find a local maximum in the Mapping function. */ + mapdata->negate = 0; + new_max = LocalMaximum( mapdata, acc, 0.01, x, status ); + if ( new_max != AST__BAD ) { + +/* Use the result to further update the active region limits. */ + for ( coord = 0; coord < ncoord; coord++ ) { + if ( x[ coord ] < active_lo[ coord ] ) { + active_lo[ coord ] = x[ coord ]; + } + if ( x[ coord ] > active_hi[ coord ] ) { + active_hi[ coord ] = x[ coord ]; + } + } + +/* Count the number of local maxima found. */ + nmax++; + +/* Update the estimate of the global maximum. */ + if ( *ubnd == AST__BAD ) { + nsame_max = 1; + *ubnd = new_max; + for ( coord = 0; coord < ncoord; coord++ ) { + xu[ coord ] = x[ coord ]; + } + + } else if ( new_max > *ubnd ) { + nsame_max = ( ( new_max - *ubnd ) > acc ) ? 1 : + nsame_max + 1; + *ubnd = new_max; + for ( coord = 0; coord < ncoord; coord++ ) { + xu[ coord ] = x[ coord ]; + } + + } else { + nsame_max++; + } + +/* Test for a satisfactory global maximum estimate. */ + if ( ( nsame_max >= minsame ) && + ( nsame_max >= (int) ( 0.3f * (float) nmax + 0.5 ) ) ) { + done_max = 1; + } + } + } + } + +/* Quit iterating once both the global minimum and the global maximum + have been found. */ + if ( done_min && done_max ) break; + } + +/* Free workspace. */ + active_hi = astFree( active_hi ); + active_lo = astFree( active_lo ); + sample_hi = astFree( sample_hi ); + sample_lo = astFree( sample_lo ); + sample_width = astFree( sample_width ); + x = astFree( x ); + +/* Annul temporary PointSets. */ + pset_in = astAnnul( pset_in ); + pset_out = astAnnul( pset_out ); + +/* If the global minimum has been found, attempt to polish the result + to machine precision by requesting that it be found with an + accuracy tolerance of zero (subject to the maximum number of + iterations that LocalMaximum will perform,). */ + if ( astOK ) { + if ( *lbnd != AST__BAD ) { + mapdata->negate = 1; + *lbnd = LocalMaximum( mapdata, 0.0, sqrt( DBL_EPSILON ), xl, status ); + if ( *lbnd != AST__BAD ) *lbnd = - *lbnd; + } + +/* Similarly polish the estimate of the global maximum. */ + if ( *ubnd != AST__BAD ) { + mapdata->negate = 0; + *ubnd = LocalMaximum( mapdata, 0.0, sqrt( DBL_EPSILON ), xu, status ); + } + +/* If either extremum could not be found, then report an error. */ + if ( ( *lbnd == AST__BAD ) || ( *ubnd == AST__BAD ) ) { + astError( AST__MBBNF, "astMapBox(%s): No valid output coordinates " + "(after %d test points).", status, astGetClass( mapdata->mapping ), + 2 * maxiter ); + } + +/* If an error occurred, then return bad extremum values and + coordinates. */ + if ( !astOK ) { + *lbnd = AST__BAD; + *ubnd = AST__BAD; + for ( coord = 0; coord < ncoord; coord++ ) { + xl[ coord ] = AST__BAD; + xu[ coord ] = AST__BAD; + } + } + } + +/* Undefine macros local to this function. */ +#undef FILL_POSITION_BUFFER +} + +void astInitMappingVtab_( AstMappingVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitMappingVtab + +* Purpose: +* Initialise a virtual function table for a Mapping. + +* Type: +* Protected function. + +* Synopsis: +* #include "mapping.h" +* void astInitMappingVtab( AstMappingVtab *vtab, const char *name ) + +* Class Membership: +* Mapping vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Mapping class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitObjectVtab( (AstObjectVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAMapping) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstObjectVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ +#define VTAB_GENERIC(X) \ + vtab->Resample##X = Resample##X; + +VTAB_GENERIC(B) +VTAB_GENERIC(D) +VTAB_GENERIC(F) +VTAB_GENERIC(I) +VTAB_GENERIC(K) +VTAB_GENERIC(L) +VTAB_GENERIC(S) +VTAB_GENERIC(UB) +VTAB_GENERIC(UI) +VTAB_GENERIC(UK) +VTAB_GENERIC(UL) +VTAB_GENERIC(US) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +VTAB_GENERIC(LD) +#endif + +#undef VTAB_GENERIC + +#define VTAB_GENERIC(X) \ + vtab->Rebin##X = Rebin##X; \ + vtab->RebinSeq##X = RebinSeq##X; + +VTAB_GENERIC(D) +VTAB_GENERIC(F) +VTAB_GENERIC(I) +VTAB_GENERIC(B) +VTAB_GENERIC(UB) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +VTAB_GENERIC(LD) +#endif + +#undef VTAB_GENERIC + + + vtab->ClearInvert = ClearInvert; + vtab->ClearReport = ClearReport; + vtab->Decompose = Decompose; + vtab->DoNotSimplify = DoNotSimplify; + vtab->GetInvert = GetInvert; + vtab->GetIsLinear = GetIsLinear; + vtab->GetIsSimple = GetIsSimple; + vtab->GetNin = GetNin; + vtab->GetNout = GetNout; + vtab->GetReport = GetReport; + vtab->GetTranForward = GetTranForward; + vtab->GetTranInverse = GetTranInverse; + vtab->Invert = Invert; + vtab->LinearApprox = LinearApprox; + vtab->MapBox = MapBox; + vtab->MapList = MapList; + vtab->MapMerge = MapMerge; + vtab->MapSplit = MapSplit; + vtab->QuadApprox = QuadApprox; + vtab->Rate = Rate; + vtab->ReportPoints = ReportPoints; + vtab->RemoveRegions = RemoveRegions; + vtab->SetInvert = SetInvert; + vtab->SetReport = SetReport; + vtab->Simplify = Simplify; + vtab->TestInvert = TestInvert; + vtab->TestReport = TestReport; + vtab->Tran1 = Tran1; + vtab->Tran2 = Tran2; + vtab->TranGrid = TranGrid; + vtab->TranN = TranN; + vtab->TranP = TranP; + vtab->Transform = Transform; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + parent_equal = object->Equal; + object->Equal = Equal; + +/* Declare the destructor, copy constructor and dump function. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "Mapping", "Mapping between coordinate systems" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +/* +* Name: +* InterpolateKernel1 + +* Purpose: +* Resample a data grid, using a 1-d interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int InterpolateKernel1( AstMapping *this, int ndim_in, +* const int *lbnd_in, const int *ubnd_in, +* const *in, const *in_var, +* int npoint, const int *offset, +* const double *const *coords, +* void (* kernel)( double, const double [], int, +* double *, int * ), +* void (* fkernel)( double, const double [], int, +* double * ), +* int neighb, const double *params, int flags, +* badval, +* *out, *out_var ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which resample a rectangular input +* grid of data (and, optionally, associated statistical variance +* values) so as to place them into a new output grid. Each output +* grid point may be mapped on to a position in the input grid in +* an arbitrary way. The input and output grids may have any number +* of dimensions, not necessarily equal. +* +* Where the positions given do not correspond with a pixel centre +* in the input grid, interpolation is performed using a weighted +* sum of the surrounding pixel values. The weights are determined +* by a separable kernel which is the product of a 1-dimensional +* kernel function evaluated along each input dimension. A pointer +* should be supplied to the 1-dimensional kernel function to be +* used. + +* Parameters: +* this +* Pointer to the Mapping being used in the resampling operation +* (this is only used for constructing error messages). +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input grid, its extent along a particular +* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" +* is zero-based). They also define the input grid's coordinate +* system, with each pixel being of unit extent along each +* dimension with integral coordinate values at its centre. +* in +* Pointer to the array of data to be resampled (with an element +* for each pixel in the input grid). The numerical type of +* these data should match the function used, as given by the +* suffix on the function name. The storage order should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +* (i.e. Fortran array storage order). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and type as the "in" array), which +* represent estimates of the statistical variance associated +* with each element of the "in" array. If this second array is +* given (along with the corresponding "out_var" array), then +* estimates of the variance of the resampled data will also be +* returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* npoint +* The number of points at which the input grid is to be +* resampled. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each output point, this array should contain the zero-based +* offset in the output array(s) (i.e. the "out" and, +* optionally, the "out_var" arrays) at which the resampled +* output value(s) should be stored. +* coords +* An array of pointers to double, with "ndim_in" +* elements. Element "coords[coord]" should point at the first +* element of an array of double (with "npoint" elements) which +* contains the values of coordinate number "coord" for each +* interpolation point. The value of coordinate number "coord" +* for interpolation point number "point" is therefore given by +* "coords[coord][point]" (assuming both indices to be +* zero-based). If any point has a coordinate value of AST__BAD +* associated with it, then the corresponding output data (and +* variance) will be set to the value given by "badval" (unles the +* AST__NOBAD flag is specified). +* kernel +* Pointer to the 1-dimensional kernel function to be used. +* fkernel +* Pointer to the 1-dimensional kernel function to be used with no +* trailing status argument. This is only used if "kernel" is NULL. +* neighb +* The number of neighbouring pixels in each dimension (on each +* side of the interpolation position) which are to contribute +* to the interpolated value. This value should be at least 1. +* params +* Pointer to an optional array of parameter values to be passed +* to the interpolation kernel function. If no parameters are +* required by this function, then a NULL pointer may be +* supplied. +* flags +* The bitwise OR of a set of flag values which provide +* additional control over the resampling operation. +* badval +* If the AST__USEBAD flag is set in the "flags" value (above), +* this parameter specifies the value which is used to identify +* bad data and/or variance values in the input array(s). Its +* numerical type must match that of the "in" (and "in_var") +* arrays. Unles the AST__NOBAD flag is specified in "flags", the +* same value will also be used to flag any output array elements +* for which resampled values could not be obtained. The output +* arrays(s) may be flagged with this value whether or not the +* AST__USEBAD flag is set (the function return value indicates +* whether any such values have been produced). +* out +* Pointer to an array with the same data type as the "in" +* array, into which the resampled data will be returned. Note +* that details of how the output grid maps on to this array +* (e.g. the storage order, number of dimensions, etc.) is +* arbitrary and is specified entirely by means of the "offset" +* array. The "out" array should therefore contain sufficient +* elements to accommodate the "offset" values supplied. There +* is no requirement that all elements of the "out" array should +* be assigned values, and any which are not addressed by the +* contents of the "offset" array will be left unchanged. +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the resampled values may be returned. This array will only be +* used if the "in_var" array has been given. It is addressed in +* exactly the same way (via the "offset" array) as the "out" +* array. The values returned are estimates of the statistical +* variance of the corresponding values in the "out" array, on +* the assumption that all errors in input grid values (in the +* "in" array) are statistically independent and that their +* variance estimates (in the "in_var" array) may simply be +* summed (with appropriate weighting factors). +* +* If no output variance estimates are required, a NULL pointer +* should be given. + +* Returned Value: +* The number of output grid points for which no valid output value +* could be obtained. + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +* - A value of zero will be returned if any of these functions is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ +/* Define macros to implement the function for a specific data + type. */ +#define MAKE_INTERPOLATE_KERNEL1(X,Xtype,Xfloating,Xfloattype,Xsigned) \ +static int InterpolateKernel1##X( AstMapping *this, int ndim_in, \ + const int *lbnd_in, const int *ubnd_in, \ + const Xtype *in, const Xtype *in_var, \ + int npoint, const int *offset, \ + const double *const *coords, \ + void (* kernel)( double, const double [], \ + int, double *, int * ), \ + void (* fkernel)( double, const double [], \ + int, double * ), \ + int neighb, const double *params, \ + int flags, Xtype badval, \ + Xtype *out, Xtype *out_var, int *status ) { \ +\ +/* Local Variables: */ \ + astDECLARE_GLOBALS /* Thread-specific data */ \ + Xfloattype hi_lim; /* Upper limit on output values */ \ + Xfloattype lo_lim; /* Lower limit on output values */ \ + Xfloattype sum; /* Weighted sum of pixel data values */ \ + Xfloattype sum_var; /* Weighted sum of pixel variance values */ \ + Xfloattype val; /* Data value to be assigned to output */ \ + Xfloattype val_var; /* Variance to be assigned to output */ \ + Xfloattype wtsum; /* Sum of weight values */ \ + Xfloattype wtsum_sq; /* Square of sum of weights */ \ + Xtype var; /* Variance value */ \ + double **wtptr; /* Pointer to array of weight pointers */ \ + double **wtptr_last; /* Array of highest weight pointer values */ \ + double *kval; /* Pointer to array of kernel values */ \ + double *wtprod; /* Accumulated weight value array pointer */ \ + double *xn_max; /* Pointer to upper limits array (n-d) */ \ + double *xn_min; /* Pointer to lower limits array (n-d) */ \ + double pixwt; /* Weight to apply to individual pixel */ \ + double wt_y; /* Value of y-dependent pixel weight */ \ + double x; /* x coordinate value */ \ + double xmax; /* x upper limit */ \ + double xmin; /* x lower limit */ \ + double xn; /* Coordinate value (n-d) */ \ + double y; /* y coordinate value */ \ + double ymax; /* y upper limit */ \ + double ymin; /* y lower limit */ \ + int *hi; /* Pointer to array of upper indices */ \ + int *lo; /* Pointer to array of lower indices */ \ + int *stride; /* Pointer to array of dimension strides */ \ + int bad; /* Output pixel bad? */ \ + int bad_var; /* Output variance bad? */ \ + int done; /* All pixel indices done? */ \ + int hi_x; /* Upper pixel index (x dimension) */ \ + int hi_y; /* Upper pixel index (y dimension) */ \ + int idim; /* Loop counter for dimensions */ \ + int ii; /* Loop counter for dimensions */ \ + int ix; /* Pixel index in input grid x dimension */ \ + int ixn; /* Pixel index in input grid (n-d) */ \ + int iy; /* Pixel index in input grid y dimension */ \ + int kerror; /* Error signalled by kernel function? */ \ + int lo_x; /* Lower pixel index (x dimension) */ \ + int lo_y; /* Lower pixel index (y dimension) */ \ + int nobad; /* Was the AST__NOBAD flag set? */ \ + int off1; /* Input pixel offset due to y index */ \ + int off_in; /* Offset to input pixel */ \ + int off_out; /* Offset to output pixel */ \ + int pixel; /* Offset to input pixel containing point */ \ + int point; /* Loop counter for output points */ \ + int result; /* Result value to return */ \ + int s; /* Temporary variable for strides */ \ + int usebad; /* Use "bad" input pixel values? */ \ + int usevar; /* Process variance array? */ \ + int ystride; /* Stride along input grid y dimension */ \ +\ +/* Initialise. */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Get a pointer to a structure holding thread-specific global data values */ \ + astGET_GLOBALS(this); \ +\ +/* Further initialisation. */ \ + kerror = 0; \ + sum_var = 0; \ + val = 0; \ + val_var = 0; \ + wtsum = 0; \ + bad = 0; \ + bad_var = 0; \ + sum = 0.0; \ +\ +/* Determine if we are processing bad pixels or variances. */ \ + nobad = flags & AST__NOBAD; \ + usebad = flags & AST__USEBAD; \ + usevar = in_var && out_var; \ +\ +/* Set up limits for checking output values to ensure that they do not \ + overflow the range of the data type being used. */ \ + lo_lim = LO_##X; \ + hi_lim = HI_##X; \ +\ +/* Handle the 1-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + if ( ndim_in == 1 ) { \ +\ +/* Calculate the coordinate limits of the input grid. */ \ + xmin = (double) lbnd_in[ 0 ] - 0.5; \ + xmax = (double) ubnd_in[ 0 ] + 0.5; \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ + } \ + } \ + } \ +\ +/* Four more cases as above, but this time with the AST__NOBAD flag \ + un-set. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Exit point on error in kernel function */ \ + Kernel_Error_1d: ; \ +\ +/* Handle the 2-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + } else if ( ndim_in == 2 ) { \ +\ +/* Allocate workspace. */ \ + kval = astMalloc( sizeof( double ) * (size_t) ( 2 * neighb ) ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along the y dimension of the input grid. */ \ + ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ +\ +/* Calculate the coordinate limits of the input grid in each \ + dimension. */ \ + xmin = (double) lbnd_in[ 0 ] - 0.5; \ + xmax = (double) ubnd_in[ 0 ] + 0.5; \ + ymin = (double) lbnd_in[ 1 ] - 0.5; \ + ymax = (double) ubnd_in[ 1 ] + 0.5; \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ + } \ + } \ + } \ +\ +/* Another four cases, as above, but this time without the AST__NOBAD \ + flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Exit point on error in kernel function */ \ + Kernel_Error_2d: ; \ + } \ +\ +/* Free the workspace. */ \ + kval = astFree( kval ); \ +\ +/* Handle other numbers of dimensions. */ \ +/* ----------------------------------- */ \ + } else { \ +\ +/* Allocate workspace. */ \ + hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + kval = astMalloc( sizeof( double ) * (size_t) \ + ( 2 * neighb * ndim_in ) ); \ + wtprod = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + wtptr = astMalloc( sizeof( double * ) * (size_t) ndim_in ); \ + wtptr_last = astMalloc( sizeof( double * ) * (size_t) ndim_in ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along each dimension of the input grid. */ \ + for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ + stride[ idim ] = s; \ + s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ +\ +/* Calculate the coordinate limits of the input grid in each \ + dimension. */ \ + xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ + xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ + } \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ + } \ + } \ + } \ +\ +/* Another 4 cases as above, but this time with the AST__NOBAD flag \ + un-set. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Exit point on error in kernel function */ \ + Kernel_Error_Nd: ;\ + } \ +\ +/* Free the workspace. */ \ + hi = astFree( hi ); \ + lo = astFree( lo ); \ + stride = astFree( stride ); \ + xn_max = astFree( xn_max ); \ + xn_min = astFree( xn_min ); \ + kval = astFree( kval ); \ + wtprod = astFree( wtprod ); \ + wtptr = astFree( wtptr ); \ + wtptr_last = astFree( wtptr_last ); \ + } \ +\ +/* If an error occurred in the kernel function, then report a \ + contextual error message. */ \ + if ( kerror ) { \ + astError( astStatus, "astResample"#X"(%s): Error signalled by " \ + "user-supplied 1-d interpolation kernel.", status, \ + astGetClass( unsimplified_mapping ) ); \ + } \ +\ +/* If an error has occurred, clear the returned result. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 1-dimensional + case. */ +#define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the input grid, or is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If input bad pixels must be detected, then obtain the offset along \ + the input grid x dimension of the input pixel which contains the \ + current coordinate, and calculate this pixel's offset from the \ + start of the input array. */ \ + if ( Usebad ) { \ + pixel = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ +\ +/* Test if the pixel is bad. */ \ + bad = ( in[ pixel ] == badval ); \ + } \ +\ +/* If OK, calculate the lowest and highest indices (in the x \ + dimension) of the region of neighbouring pixels that will \ + contribute to the interpolated result. Constrain these values to \ + lie within the input grid. */ \ + if ( !bad ) { \ + ix = (int) floor( x ); \ + lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ + hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ +\ +/* Initialise sums for forming the interpolated result. */ \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + bad_var = 0; \ + } \ +\ +/* Loop to inspect all the contributing pixels, calculating the offset \ + of each pixel from the start of the input array. */ \ + off_in = lo_x - lbnd_in[ 0 ]; \ + for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ +\ +/* If necessary, test if the input pixel is bad. If not, calculate its \ + weight by evaluating the kernel function. */ \ + if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ + if( kernel ) { \ + ( *kernel )( (double) ix - x, params, flags, &pixwt, status ); \ + } else { \ + ( *fkernel )( (double) ix - x, params, flags, &pixwt ); \ + } \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_Error_1d; \ + } \ +\ +/* Form the weighted sums required for finding the interpolated \ + value. */ \ + sum += ( (Xfloattype) pixwt ) * ( (Xfloattype) in[ off_in ] ); \ + wtsum += (Xfloattype) pixwt; \ +\ +/* If a variance estimate is required and it still seems possible to \ + obtain one, then obtain the variance value associated with the \ + current input pixel. */ \ + if ( Usevar ) { \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + var = in_var[ off_in ]; \ +\ +/* If necessary, test if this value is bad (if the data type is \ + signed, also check that it is not negative). */ \ + if ( Usebad ) bad_var = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If any bad input variance value is obtained, we cannot generate a \ + valid output variance estimate. Otherwise, form the sum needed to \ + calculate this estimate. */ \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ + ( (Xfloattype) var ); \ + } \ + } \ + } \ + } \ + } \ + } \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 2-dimensional + case. */ +#define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the input grid, or is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If not, then similarly obtain and test the y coordinate. */ \ + y = coords[ 1 ][ point ]; \ + bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If input bad pixels must be detected, then obtain the offsets along \ + each input grid dimension of the input pixel which contains the \ + current coordinates, and calculate this pixel's offset from the \ + start of the input array. */ \ + if ( Usebad ) { \ + ix = (int) floor( x + 0.5 ); \ + iy = (int) floor( y + 0.5 ); \ + pixel = ix - lbnd_in[ 0 ] + ystride * ( iy - lbnd_in[ 1 ] ); \ +\ +/* Test if the pixel is bad. */ \ + bad = ( in[ pixel ] == badval ); \ + } \ +\ +/* If OK, calculate the lowest and highest indices (in each dimension) \ + of the region of neighbouring pixels that will contribute to the \ + interpolated result. Constrain these values to lie within the input \ + grid. */ \ + if ( !bad ) { \ + ix = (int) floor( x ); \ + lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ + hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ + iy = (int) floor( y ); \ + lo_y = MaxI( iy - neighb + 1, lbnd_in[ 1 ], status ); \ + hi_y = MinI( iy + neighb, ubnd_in[ 1 ], status ); \ +\ +/* Loop to evaluate the kernel function along the x dimension, storing \ + the resulting values. The function's argument is the offset of the \ + contributing pixel (along this dimension) from the input \ + position. */ \ + for ( ix = lo_x; ix <= hi_x; ix++ ) { \ + if( kernel ) { \ + ( *kernel )( (double) ix - x, params, flags, \ + kval + ix - lo_x, status ); \ + } else { \ + ( *fkernel )( (double) ix - x, params, flags, \ + kval + ix - lo_x ); \ + } \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_Error_2d; \ + } \ + } \ +\ +/* Initialise sums for forming the interpolated result. */ \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + bad_var = 0; \ + } \ +\ +/* Loop over the y index to inspect all the contributing pixels, while \ + keeping track of their offset within the input array. Evaluate the \ + kernel function for each y index value. */ \ + off1 = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \ + for ( iy = lo_y; iy <= hi_y; iy++, off1 += ystride ) { \ + if( kernel ) { \ + ( *kernel )( (double) iy - y, params, flags, &wt_y, status ); \ + } else { \ + ( *fkernel )( (double) iy - y, params, flags, &wt_y ); \ + } \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_Error_2d; \ + } \ +\ +/* Loop over the x index, calculating the pixel offset in the input \ + array. */ \ + off_in = off1; \ + for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ +\ +/* If necessary, test if the input pixel is bad. If not, calculate its \ + weight as the product of the kernel function's value for the x and \ + y dimensions. */ \ + if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ + pixwt = kval[ ix - lo_x ] * wt_y; \ +\ +/* Form the weighted sums required for finding the interpolated \ + value. */ \ + sum += ( (Xfloattype) pixwt ) * \ + ( (Xfloattype) in[ off_in ] ); \ + wtsum += (Xfloattype) pixwt; \ +\ +/* If a variance estimate is required and it still seems possible to \ + obtain one, then obtain the variance value associated with the \ + current input pixel. */ \ + if ( Usevar ) { \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + var = in_var[ off_in ]; \ +\ +/* If necessary, test if this value is bad (if the data type is \ + signed, also check that it is not negative). */ \ + if ( Usebad ) bad_var = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If any bad input variance value is obtained, we cannot generate a \ + valid output variance estimate. Otherwise, form the sum needed to \ + calculate this estimate. */ \ + if ( !( ( Xsigned ) || ( Usebad ) ) || \ + !bad_var ) { \ + sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ + ( (Xfloattype) var ); \ + } \ + } \ + } \ + } \ + } \ + } \ + } \ + } \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the n-dimensional + case. */ +#define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Initialise offsets into the input array. Then loop to obtain each \ + coordinate associated with the current output point. */ \ + pixel = 0; \ + off_in = 0; \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + xn = coords[ idim ][ point ]; \ +\ +/* Test if the coordinate lies outside the input grid, or is bad. If \ + either is true, the corresponding output pixel value will be bad, \ + so give up on this point. */ \ + bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ + ( xn == AST__BAD ); \ + if ( bad ) break; \ +\ +/* If input bad pixels must be detected, then obtain the index along \ + the current input grid dimension of the pixel which contains this \ + coordinate and accumulate the pixel's offset from the start of the \ + input array. */ \ + if ( Usebad ) { \ + pixel += stride[ idim ] * \ + ( (int) floor( xn + 0.5 ) - lbnd_in[ idim ] ); \ + } \ +\ +/* Calculate the lowest and highest indices (in the current dimension) \ + of the region of neighbouring pixels that will contribute to the \ + interpolated result. Constrain these values to lie within the input \ + grid. */ \ + ixn = (int) floor( xn ); \ + lo[ idim ] = MaxI( ixn - neighb + 1, lbnd_in[ idim ], status ); \ + hi[ idim ] = MinI( ixn + neighb, ubnd_in[ idim ], status ); \ +\ +/* Accumulate the offset (from the start of the input array) of the \ + contributing pixel which has the lowest index in each dimension. */ \ + off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \ + } \ +\ +/* Once the input pixel which contains the required coordinates has \ + been identified, test if it is bad, if necessary. */ \ + if ( Usebad ) bad = bad || ( in[ pixel ] == badval ); \ +\ +/* If OK, loop to evaluate the kernel function which will be used to \ + weight the contributions from surrounding pixels. */ \ + if ( !bad ) { \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ +\ +/* Set up an array of pointers to locate kernel values (stored in the \ + "kval" array) for each dimension. Initially, each of these pointers \ + locates the first weight value which applies to the contributing \ + pixel with the lowest index in each dimension. */ \ + wtptr[ idim ] = kval + ( 2 * neighb * idim ); \ +\ +/* Also set up pointers to the last weight value in each dimension. */ \ + wtptr_last[ idim ] = wtptr[ idim ] + ( hi[ idim ] - lo[ idim ] ); \ +\ +/* Loop to evaluate the kernel function along each dimension, storing \ + the resulting values. The function's argument is the offset of the \ + contributing pixel (along the relevant dimension) from the input \ + point. */ \ + xn = coords[ idim ][ point ]; \ + for ( ixn = lo[ idim ]; ixn <= hi[ idim ]; ixn++ ) { \ + if( kernel ) { \ + ( *kernel )( (double) ixn - xn, params, flags, \ + wtptr[ idim ] + ixn - lo[ idim ], status ); \ + } else { \ + ( *fkernel )( (double) ixn - xn, params, flags, \ + wtptr[ idim ] + ixn - lo[ idim ] ); \ + } \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_Error_Nd; \ + } \ + } \ + } \ +\ +/* Initialise, and loop over the neighbouring input pixels to obtain \ + an interpolated value. */ \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + bad_var = 0; \ + } \ + idim = ndim_in - 1; \ + wtprod[ idim ] = 1.0; \ + done = 0; \ + do { \ +\ +/* Each contributing pixel is weighted by the product of the kernel \ + weight factors evaluated along each input dimension. However, since \ + we typically only change the index of one dimension at a time, we \ + can avoid forming this product repeatedly by retaining an array of \ + accumulated products for all higher dimensions. We need then only \ + update the lower elements in this array, corresponding to those \ + dimensions whose index has changed. We do this here, "idim" being \ + the index of the most significant dimension to have changed. Note \ + that on the first pass, all dimensions are considered changed, \ + causing this array to be initialised. */ \ + for ( ii = idim; ii >= 1; ii-- ) { \ + wtprod[ ii - 1 ] = wtprod[ ii ] * *( wtptr[ ii ] ); \ + } \ +\ +/* If necessary, test each pixel which may contribute to the result to \ + see if it is bad. If not, obtain its weight from the accumulated \ + product of weights. Also multiply by the weight for dimension zero, \ + which is not included in the "wtprod" array). */ \ + if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ + pixwt = wtprod[ 0 ] * *( wtptr[ 0 ] ); \ +\ +/* Form the weighted sums required for finding the interpolated \ + value. */ \ + sum += ( (Xfloattype) pixwt ) * ( (Xfloattype) in[ off_in ] ); \ + wtsum += (Xfloattype) pixwt; \ +\ +/* If a variance estimate is required and it still seems possible to \ + obtain one, then obtain the variance value associated with the \ + current input pixel. */ \ + if ( Usevar ) { \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + var = in_var[ off_in ]; \ +\ +/* If necessary, test if this value is bad (if the data type is \ + signed, also check that it is not negative). */ \ + if ( Usebad ) bad_var = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If any bad input variance value is obtained, we cannot generate a \ + valid output variance estimate. Otherwise, form the sum needed to \ + calculate this estimate. */ \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ + ( (Xfloattype) var ); \ + } \ + } \ + } \ + } \ +\ +/* Now update the weight value pointers and pixel offset to refer to \ + the next input pixel to be considered. */ \ + idim = 0; \ + do { \ +\ +/* The first input dimension whose weight value pointer has not yet \ + reached its final value has this pointer incremented, and the pixel \ + offset into the input array is updated accordingly. */ \ + if ( wtptr[ idim ] != wtptr_last[ idim ] ) { \ + wtptr[ idim ]++; \ + off_in += stride[ idim ]; \ + break; \ +\ +/* Any earlier dimensions (which have reached the final pointer value) \ + have this pointer returned to its lowest value. Again, the pixel \ + offset into the input image is updated accordingly. */ \ + } else { \ + wtptr[ idim ] -= ( hi[ idim ] - lo[ idim ] ); \ + off_in -= stride[ idim ] * \ + ( hi[ idim ] - lo[ idim ] ); \ + done = ( ++idim == ndim_in ); \ + } \ + } while ( !done ); \ + } while ( !done ); \ + } + +/* This subsidiary macro calculates the interpolated output value (and + variance) from the sums over contributing pixels, checks the + results for validity, and assigns them to the output array(s). */ +#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Usebad,Usevar,Nobad) \ +\ +/* If the output data value has not yet been flagged as bad, then \ + check that an interpolated value can actually be produced. First \ + check that the sum of weights is not zero. */ \ + if ( !bad ) { \ + bad = ( wtsum == (Xfloattype) 0.0 ); \ +\ +/* If OK, calculate the interpolated value. Then, if the output data \ + type is not floating point, check that this value will not overflow \ + the available output range. */ \ + if ( !bad ) { \ + val = sum / wtsum; \ + if ( !( Xfloating ) ) { \ + bad = ( val <= lo_lim ) || ( val >= hi_lim ); \ + } \ + } \ +\ +/* If no interpolated data value can be produced, then no associated \ + variance will be required either. */ \ + if ( ( Usevar ) && bad ) bad_var = 1; \ + } \ +\ +/* Now perform similar checks on the output variance value (if \ + required). This time we check that the square of the sum of \ + weights is not zero (since this might underflow before the sum of \ + weights). Again we also check to prevent the result overflowing the \ + output data type. */ \ + if ( ( Usevar ) && !bad_var ) { \ + wtsum_sq = wtsum * wtsum; \ + bad_var = ( wtsum_sq == (Xfloattype) 0.0 ); \ + if ( !bad_var ) { \ + val_var = sum_var / wtsum_sq; \ + if ( !( Xfloating ) ) { \ + bad_var = ( val_var <= lo_lim ) || ( val_var >= hi_lim ); \ + } \ + } \ + } \ +\ +/* Obtain the pixel offset into the output array. */ \ + off_out = offset[ point ]; \ +\ +/* Assign a bad output value (and variance) if required and count it. */ \ + if ( bad ) { \ + if( !Nobad ) { \ + out[ off_out ] = badval; \ + if ( Usevar ) out_var[ off_out ] = badval; \ + } \ + result++; \ +\ +/* Otherwise, assign the interpolated value. If the output data type \ + is floating point, the result can be stored directly, otherwise we \ + must round to the nearest integer. */ \ + } else { \ + if ( Xfloating ) { \ + out[ off_out ] = (Xtype) val; \ + } else { \ + out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \ + ( (Xfloattype) 0.5 ) : \ + ( (Xfloattype) -0.5 ) ) ); \ + } \ +\ +/* If a variance estimate is required but none can be obtained, then \ + store a bad output variance value and count it. */ \ + if ( Usevar ) { \ + if ( bad_var ) { \ + if( !Nobad ) { \ + out_var[ off_out ] = badval; \ + } \ + result++; \ +\ +/* Otherwise, store the variance estimate, rounding to the nearest \ + integer if necessary. */ \ + } else { \ + if ( Xfloating ) { \ + out_var[ off_out ] = (Xtype) val_var; \ + } else { \ + out_var[ off_out ] = (Xtype) ( val_var + \ + ( ( val_var >= (Xfloattype) 0.0 ) ? \ + ( (Xfloattype) 0.5 ) : \ + ( (Xfloattype) -0.5 ) ) ); \ + } \ + } \ + } \ + } + +/* These subsidiary macros define limits for range checking of results + before conversion to the final data type. For each data type code + , HI_ gives the least positive floating point value which + just overflows that data type towards plus infinity, while LO_ + gives the least negative floating point value which just overflows + that data type towards minus infinity. Thus, a floating point value + must satisfy LO is a floating point type, the limits are not actually used, + but must be present to permit error-free compilation. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define HI_LD ( 0.0L ) +#define LO_LD ( 0.0L ) +#endif +#define HI_D ( 0.0 ) +#define LO_D ( 0.0 ) +#define HI_F ( 0.0f ) +#define LO_F ( 0.0f ) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define HI_K ( 0.5L + (long double) LONG_MAX ) +#define LO_K ( -0.5L + (long double) LONG_MIN ) +#define HI_UK ( 0.5L + (long double) ULONG_MAX ) +#define LO_UK ( -0.5L ) +#define HI_L ( 0.5L + (long double) LONG_MAX ) +#define LO_L ( -0.5L + (long double) LONG_MIN ) +#define HI_UL ( 0.5L + (long double) ULONG_MAX ) +#define LO_UL ( -0.5L ) +#else +#define HI_K ( 0.5 + (double) LONG_MAX ) +#define LO_K ( -0.5 + (double) LONG_MIN ) +#define HI_UK ( 0.5 + (double) ULONG_MAX ) +#define LO_UK ( -0.5 ) +#define HI_L ( 0.5 + (double) LONG_MAX ) +#define LO_L ( -0.5 + (double) LONG_MIN ) +#define HI_UL ( 0.5 + (double) ULONG_MAX ) +#define LO_UL ( -0.5 ) +#endif + +#define HI_I ( 0.5 + (double) INT_MAX ) +#define LO_I ( -0.5 + (double) INT_MIN ) +#define HI_UI ( 0.5 + (double) UINT_MAX ) +#define LO_UI ( -0.5 ) +#define HI_S ( 0.5f + (float) SHRT_MAX ) +#define LO_S ( -0.5f + (float) SHRT_MIN ) +#define HI_US ( 0.5f + (float) USHRT_MAX ) +#define LO_US ( -0.5f ) +#define HI_B ( 0.5f + (float) SCHAR_MAX ) +#define LO_B ( -0.5f + (float) SCHAR_MIN ) +#define HI_UB ( 0.5f + (float) UCHAR_MAX ) +#define LO_UB ( -0.5f ) + +/* This subsidiary macro tests for negative variance values. This + check is required only for signed data types. */ +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ + bad_var = bad_var || ( var < ( (Xtype) 0 ) ); + +/* Expand the main macro above to generate a function for each + required signed data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_INTERPOLATE_KERNEL1(LD,long double,1,long double,1) +MAKE_INTERPOLATE_KERNEL1(L,long int,0,long double,1) +MAKE_INTERPOLATE_KERNEL1(K,INT_BIG,0,long double,1) +#else +MAKE_INTERPOLATE_KERNEL1(L,long int,0,double,1) +MAKE_INTERPOLATE_KERNEL1(K,INT_BIG,0,double,1) +#endif +MAKE_INTERPOLATE_KERNEL1(D,double,1,double,1) +MAKE_INTERPOLATE_KERNEL1(F,float,1,float,1) +MAKE_INTERPOLATE_KERNEL1(I,int,0,double,1) +MAKE_INTERPOLATE_KERNEL1(S,short int,0,float,1) +MAKE_INTERPOLATE_KERNEL1(B,signed char,0,float,1) + +/* Re-define the macro for testing for negative variances to do + nothing. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) + +/* Expand the main macro above to generate a function for each + required unsigned data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_INTERPOLATE_KERNEL1(UL,unsigned long int,0,long double,0) +MAKE_INTERPOLATE_KERNEL1(UK,UINT_BIG,0,long double,0) +#else +MAKE_INTERPOLATE_KERNEL1(UL,unsigned long int,0,double,0) +MAKE_INTERPOLATE_KERNEL1(UK,UINT_BIG,0,double,0) +#endif +MAKE_INTERPOLATE_KERNEL1(UI,unsigned int,0,double,0) +MAKE_INTERPOLATE_KERNEL1(US,unsigned short int,0,float,0) +MAKE_INTERPOLATE_KERNEL1(UB,unsigned char,0,float,0) + +/* Undefine the macros used above. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#undef HI_LD +#undef LO_LD +#endif +#undef HI_D +#undef LO_D +#undef HI_F +#undef LO_F +#undef HI_L +#undef LO_L +#undef HI_UL +#undef LO_UL +#undef HI_K +#undef LO_K +#undef HI_UK +#undef LO_UK +#undef HI_I +#undef LO_I +#undef HI_UI +#undef LO_UI +#undef HI_S +#undef LO_S +#undef HI_US +#undef LO_US +#undef HI_B +#undef LO_B +#undef HI_UB +#undef LO_UB +#undef CALC_AND_ASSIGN_OUTPUT +#undef ASSEMBLE_INPUT_ND +#undef ASSEMBLE_INPUT_2D +#undef ASSEMBLE_INPUT_1D +#undef MAKE_INTERPOLATE_KERNEL1 + +/* +* Name: +* InterpolateLinear + +* Purpose: +* Resample a data grid, using the linear interpolation scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int InterpolateLinear( int ndim_in, +* const int *lbnd_in, const int *ubnd_in, +* const *in, const *in_var, +* int npoint, const int *offset, +* const double *const *coords, +* int flags, badval, +* *out, *out_var ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which resample a rectangular input +* grid of data (and, optionally, associated statistical variance +* values) so as to place them into a new output grid. Each output +* grid point may be mapped on to a position in the input grid in +* an arbitrary way. Where the positions given do not correspond +* with a pixel centre in the input grid, the interpolation scheme +* used is linear interpolation between the centres of the nearest +* neighbouring pixels in each dimension (there are 2 nearest +* neighbours in 1 dimension, 4 in 2 dimensions, 8 in 3 dimensions, +* etc.). + +* Parameters: +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input grid, its extent along a particular +* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" +* is zero-based). They also define the input grid's coordinate +* system, with each pixel being of unit extent along each +* dimension with integral coordinate values at its centre. +* in +* Pointer to the array of data to be resampled (with an element +* for each pixel in the input grid). The numerical type of +* these data should match the function used, as given by the +* suffix on the function name. The storage order should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +* (i.e. Fortran array storage order). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and type as the "in" array), which +* represent estimates of the statistical variance associated +* with each element of the "in" array. If this second array is +* given (along with the corresponding "out_var" array), then +* estimates of the variance of the resampled data will also be +* returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* npoint +* The number of points at which the input grid is to be +* resampled. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each output point, this array should contain the zero-based +* offset in the output array(s) (i.e. the "out" and, +* optionally, the "out_var" arrays) at which the resampled +* output value(s) should be stored. +* coords +* An array of pointers to double, with "ndim_in" +* elements. Element "coords[coord]" should point at the first +* element of an array of double (with "npoint" elements) which +* contains the values of coordinate number "coord" for each +* interpolation point. The value of coordinate number "coord" +* for interpolation point number "point" is therefore given by +* "coords[coord][point]" (assuming both indices to be +* zero-based). If any point has a coordinate value of AST__BAD +* associated with it, then the corresponding output data (and +* variance) will be set to the value given by "badval" (unles the +* AST__NOBAD flag is specified). +* flags +* The bitwise OR of a set of flag values which control the +* operation of the function. Currently, only the flag +* AST__USEBAD is significant and indicates whether there are +* "bad" (i.e. missing) data in the input array(s) which must be +* recognised and propagated to the output array(s). If this +* flag is not set, all input values are treated literally. +* badval +* If the AST__USEBAD flag is set in the "flags" value (above), +* this parameter specifies the value which is used to identify +* bad data and/or variance values in the input array(s). Its +* numerical type must match that of the "in" (and "in_var") +* arrays. Unles the AST__NOBAD flag is specified in "flags", the +* same value will also be used to flag any output array elements +* for which resampled values could not be obtained. The output +* arrays(s) may be flagged with this value whether or not the +* AST__USEBAD flag is set (the function return value indicates +* whether any such values have been produced). +* out +* Pointer to an array with the same data type as the "in" +* array, into which the resampled data will be returned. Note +* that details of how the output grid maps on to this array +* (e.g. the storage order, number of dimensions, etc.) is +* arbitrary and is specified entirely by means of the "offset" +* array. The "out" array should therefore contain sufficient +* elements to accommodate the "offset" values supplied. There +* is no requirement that all elements of the "out" array should +* be assigned values, and any which are not addressed by the +* contents of the "offset" array will be left unchanged. +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the resampled values may be returned. This array will only be +* used if the "in_var" array has been given. It is addressed in +* exactly the same way (via the "offset" array) as the "out" +* array. The values returned are estimates of the statistical +* variance of the corresponding values in the "out" array, on +* the assumption that all errors in input grid values (in the +* "in" array) are statistically independent and that their +* variance estimates (in the "in_var" array) may simply be +* summed (with appropriate weighting factors). +* +* If no output variance estimates are required, a NULL pointer +* should be given. + +* Returned Value: +* The number of output grid points to which a data value (or a +* variance value if relevant) equal to "badval" has been assigned +* because no valid output value could be obtained. + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +* - A value of zero will be returned if any of these functions is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ +/* Define macros to implement the function for a specific data + type. */ +#define MAKE_INTERPOLATE_LINEAR(X,Xtype,Xfloating,Xfloattype,Xsigned) \ +static int InterpolateLinear##X( int ndim_in, \ + const int *lbnd_in, const int *ubnd_in, \ + const Xtype *in, const Xtype *in_var, \ + int npoint, const int *offset, \ + const double *const *coords, \ + int flags, Xtype badval, \ + Xtype *out, Xtype *out_var, int *status ) { \ +\ +/* Local Variables: */ \ + Xfloattype sum; /* Weighted sum of pixel data values */ \ + Xfloattype sum_var; /* Weighted sum of pixel variance values */ \ + Xfloattype val; /* Value to be asigned to output pixel */ \ + Xfloattype wtsum; /* Sum of weight values */ \ + Xtype var; /* Variance value */ \ + double *frac_hi; /* Pointer to array of weights */ \ + double *frac_lo; /* Pointer to array of weights */ \ + double *wt; /* Pointer to array of weights */ \ + double *wtprod; /* Array of accumulated weights pointer */ \ + double *xn_max; /* Pointer to upper limits array (n-d) */ \ + double *xn_min; /* Pointer to lower limits array (n-d) */ \ + double frac_hi_x; /* Pixel weight (x dimension) */ \ + double frac_hi_y; /* Pixel weight (y dimension) */ \ + double frac_lo_x; /* Pixel weight (x dimension) */ \ + double frac_lo_y; /* Pixel weight (y dimension) */ \ + double pixwt; /* Weight to apply to individual pixel */ \ + double x; /* x coordinate value */ \ + double xmax; /* x upper limit */ \ + double xmin; /* x lower limit */ \ + double xn; /* Coordinate value (n-d) */ \ + double y; /* y coordinate value */ \ + double ymax; /* y upper limit */ \ + double ymin; /* y lower limit */ \ + int *dim; /* Pointer to array of pixel indices */ \ + int *hi; /* Pointer to array of upper indices */ \ + int *lo; /* Pointer to array of lower indices */ \ + int *stride; /* Pointer to array of dimension strides */ \ + int bad; /* Output pixel bad? */ \ + int bad_var; /* Output variance bad? */ \ + int done; /* All pixel indices done? */ \ + int hi_x; /* Upper pixel index (x dimension) */ \ + int hi_y; /* Upper pixel index (y dimension) */ \ + int idim; /* Loop counter for dimensions */ \ + int ii; /* Loop counter for weights */ \ + int ix; /* Pixel index in input grid x dimension */ \ + int ixn; /* Pixel index (n-d) */ \ + int iy; /* Pixel index in input grid y dimension */ \ + int lo_x; /* Lower pixel index (x dimension) */ \ + int lo_y; /* Lower pixel index (y dimension) */ \ + int nobad; /* Was the AST__NOBAD flag set? */ \ + int off_in; /* Offset to input pixel */ \ + int off_lo; /* Offset to "first" input pixel */ \ + int off_out; /* Offset to output pixel */ \ + int pixel; /* Offset to input pixel containing point */ \ + int point; /* Loop counter for output points */ \ + int result; /* Result value to return */ \ + int s; /* Temporary variable for strides */ \ + int usebad; /* Use "bad" input pixel values? */ \ + int usevar; /* Process variance array? */ \ + int ystride; /* Stride along input grid y dimension */ \ +\ +/* Initialise. */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Initialise variables to avoid "used of uninitialised variable" \ + messages from dumb compilers. */ \ + sum = 0; \ + sum_var = 0; \ + wtsum = 0; \ + bad = 0; \ + bad_var = 0; \ +\ +/* Determine if we are processing bad pixels or variances. */ \ + nobad = flags & AST__NOBAD; \ + usebad = flags & AST__USEBAD; \ + usevar = in_var && out_var; \ +\ +/* Handle the 1-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + if ( ndim_in == 1 ) { \ +\ +/* Calculate the coordinate limits of the input grid. */ \ + xmin = (double) lbnd_in[ 0 ] - 0.5; \ + xmax = (double) ubnd_in[ 0 ] + 0.5; \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,0,1) \ + } \ + } \ + } \ +\ +/* Four more cases as above, but this time with the AST__NOBAD flag \ + un-set. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Handle the 2-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + } else if ( ndim_in == 2 ) { \ +\ +/* Calculate the stride along the y dimension of the input grid. */ \ + ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ +\ +/* Calculate the coordinate limits of the input grid in each \ + dimension. */ \ + xmin = (double) lbnd_in[ 0 ] - 0.5; \ + xmax = (double) ubnd_in[ 0 ] + 0.5; \ + ymin = (double) lbnd_in[ 1 ] - 0.5; \ + ymax = (double) ubnd_in[ 1 ] + 0.5; \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,0,1) \ + } \ + } \ + } \ +\ +/* Four more case as above, but without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + 0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Handle other numbers of dimensions. */ \ +/* ----------------------------------- */ \ + } else { \ +\ +/* Allocate workspace. */ \ + dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + frac_hi = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + frac_lo = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + wt = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + wtprod = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along each dimension of the input grid. */ \ + for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ + stride[ idim ] = s; \ + s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ +\ +/* Calculate the coordinate limits of the input grid in each \ + dimension. */ \ + xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ + xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ + } \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,0,0,1) \ + } \ + } \ + } \ +\ +/* Four more case as above, but without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ + Xsigned,0,0,0) \ + } \ + } \ + } \ + } \ + } \ +\ +/* Free the workspace. */ \ + dim = astFree( dim ); \ + frac_hi = astFree( frac_hi ); \ + frac_lo = astFree( frac_lo ); \ + hi = astFree( hi ); \ + lo = astFree( lo ); \ + stride = astFree( stride ); \ + wt = astFree( wt ); \ + wtprod = astFree( wtprod ); \ + xn_max = astFree( xn_max ); \ + xn_min = astFree( xn_min ); \ + } \ +\ +/* If an error has occurred, clear the returned result. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 1-dimensional + case. */ +#define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the input grid. Also test if it is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If input bad pixels must be detected, then obtain the offset along \ + the input grid x dimension of the input pixel which contains the \ + current coordinate and calculate this pixel's offset from the start \ + of the input array. */ \ + if ( Usebad ) { \ + pixel = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ +\ +/* Test if the pixel is bad. */ \ + bad = ( in[ pixel ] == badval ); \ + } \ +\ +/* If OK, obtain the indices along the input grid x dimension of the \ + two adjacent pixels which will contribute to the interpolated \ + result. Also obtain the fractional weight to be applied to each of \ + these pixels. */ \ + if ( !bad ) { \ + lo_x = (int) floor( x ); \ + hi_x = lo_x + 1; \ + frac_lo_x = (double) hi_x - x; \ + frac_hi_x = 1.0 - frac_lo_x; \ +\ +/* Obtain the offset within the input array of the first pixel to be \ + used for interpolation (the one with the smaller index). */ \ + off_lo = lo_x - lbnd_in[ 0 ]; \ +\ +/* Initialise sums for forming the interpolated result. */ \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \ + } \ +\ +/* For each of the two pixels which may contribute to the result, \ + test if the pixel index lies within the input grid. Where it does, \ + accumulate the sums required for forming the interpolated \ + result. In each case, we supply the pixel's offset within the input \ + array and the weight to be applied to it. */ \ + if ( lo_x >= lbnd_in[ 0 ] ) { \ + FORM_LINEAR_INTERPOLATION_SUM(off_lo,frac_lo_x,Xtype, \ + Xfloattype,Xsigned,Usebad,Usevar) \ + } \ + if ( hi_x <= ubnd_in[ 0 ] ) { \ + FORM_LINEAR_INTERPOLATION_SUM(off_lo + 1,frac_hi_x,Xtype, \ + Xfloattype,Xsigned,Usebad,Usevar) \ + } \ + } \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 2-dimensional + case. */ +#define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the input grid. Also test if it is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If OK, then similarly obtain and test the y coordinate. */ \ + y = coords[ 1 ][ point ]; \ + bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If input bad pixels must be detected, then obtain the offsets along \ + each input grid dimension of the input pixel which contains the \ + current coordinates. */ \ + if ( Usebad ) { \ + ix = (int) floor( x + 0.5 ); \ + iy = (int) floor( y + 0.5 ); \ +\ +/* Calculate this pixel's offset from the start of the input array. */ \ + pixel = ix - lbnd_in[ 0 ] + ystride * ( iy - lbnd_in[ 1 ] ); \ +\ +/* Test if the pixel is bad. */ \ + bad = ( in[ pixel ] == badval ); \ + } \ +\ +/* If OK, obtain the indices along the input grid x dimension of the \ + two adjacent pixels which will contribute to the interpolated \ + result. Also obtain the fractional weight to be applied to each of \ + these pixels. */ \ + if ( !bad ) { \ + lo_x = (int) floor( x ); \ + hi_x = lo_x + 1; \ + frac_lo_x = (double) hi_x - x; \ + frac_hi_x = 1.0 - frac_lo_x; \ +\ +/* Repeat this process for the y dimension. */ \ + lo_y = (int) floor( y ); \ + hi_y = lo_y + 1; \ + frac_lo_y = (double) hi_y - y; \ + frac_hi_y = 1.0 - frac_lo_y; \ +\ +/* Obtain the offset within the input array of the first pixel to be \ + used for interpolation (the one with the smaller index along both \ + dimensions). */ \ + off_lo = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \ +\ +/* Initialise sums for forming the interpolated result. */ \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \ + } \ +\ +/* For each of the four pixels which may contribute to the result, \ + test if the pixel indices lie within the input grid. Where they do, \ + accumulate the sums required for forming the interpolated \ + result. In each case, we supply the pixel's offset within the input \ + array and the weight to be applied to it. */ \ + if ( lo_y >= lbnd_in[ 1 ] ) { \ + if ( lo_x >= lbnd_in[ 0 ] ) { \ + FORM_LINEAR_INTERPOLATION_SUM(off_lo, \ + frac_lo_x * frac_lo_y,Xtype, \ + Xfloattype, Xsigned, \ + Usebad,Usevar) \ + } \ + if ( hi_x <= ubnd_in[ 0 ] ) { \ + FORM_LINEAR_INTERPOLATION_SUM(off_lo + 1, \ + frac_hi_x * frac_lo_y,Xtype, \ + Xfloattype,Xsigned, \ + Usebad,Usevar) \ + } \ + } \ + if ( hi_y <= ubnd_in[ 1 ] ) { \ + if ( lo_x >= lbnd_in[ 0 ] ) { \ + FORM_LINEAR_INTERPOLATION_SUM(off_lo + ystride, \ + frac_lo_x * frac_hi_y,Xtype, \ + Xfloattype,Xsigned, \ + Usebad,Usevar) \ + } \ + if ( hi_x <= ubnd_in[ 0 ] ) { \ + FORM_LINEAR_INTERPOLATION_SUM(off_lo + ystride + 1, \ + frac_hi_x * frac_hi_y,Xtype, \ + Xfloattype,Xsigned, \ + Usebad,Usevar) \ + } \ + } \ + } \ + } \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the n-dimensional + case. */ +#define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Initialise offsets into the input array. Then loop to obtain each + coordinate associated with the current output point. */ \ + off_in = 0; \ + if ( Usebad ) pixel = 0; \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + xn = coords[ idim ][ point ]; \ +\ +/* Test if the coordinate lies outside the input grid. Also test if \ + it is bad. If either is true, the corresponding output pixel value \ + will be bad, so give up on this point. */ \ + bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ + ( xn == AST__BAD ); \ + if ( bad ) break; \ +\ +/* If input bad pixels must be detected, obtain the index along the \ + current input grid dimension of the pixel which contains this \ + coordinate and accumulate the pixel's offset from the start of the \ + input array. */ \ + if ( Usebad ) { \ + pixel += stride[ idim ] * \ + ( (int) floor( xn + 0.5 ) - lbnd_in[ idim ] ); \ + } \ +\ +/* Obtain the indices along the current dimension of the input grid of \ + the two (usually adjacent) pixels which will contribute to the \ + output value. If necessary, however, restrict each index to ensure \ + it does not lie outside the input grid. Also calculate the \ + fractional weight to be given to each pixel in order to interpolate \ + linearly between them. */ \ + ixn = (int) floor( xn ); \ + lo[ idim ] = MaxI( ixn, lbnd_in[ idim ], status ); \ + hi[ idim ] = MinI( ixn + 1, ubnd_in[ idim ], status ); \ + frac_lo[ idim ] = 1.0 - fabs( xn - (double) lo[ idim ] ); \ + frac_hi[ idim ] = 1.0 - fabs( xn - (double) hi[ idim ] ); \ +\ +/* Store the lower index involved in interpolation along each \ + dimension and accumulate the offset from the start of the input \ + array of the pixel which has these indices. */ \ + dim[ idim ] = lo[ idim ]; \ + off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \ +\ +/* Also store the fractional weight associated with the lower pixel \ + along each dimension. */ \ + wt[ idim ] = frac_lo[ idim ]; \ + } \ +\ +/* If the input pixel which contains the required coordinates has \ + been identified, test if it is bad. */ \ + if ( Usebad ) bad = bad || ( in[ pixel ] == badval ); \ +\ +/* If OK, initialise and loop over adjacent input pixels to obtain an \ + interpolated value. */ \ + if ( !bad ) { \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \ + } \ + idim = ndim_in - 1; \ + wtprod[ idim ] = 1.0; \ + done = 0; \ + do { \ +\ +/* Each contributing pixel is weighted by the product of the weights \ + which account for the displacement of its centre from the required \ + position along each dimension. However, since we typically only \ + change the index of one dimension at a time, we can avoid forming \ + this product repeatedly by retaining an array of accumulated weight \ + products for all higher dimensions. We need then only update the \ + lower elements in this array, corresponding to those dimensions \ + whose index has changed. We do this here, "idim" being the index of \ + the most significant dimension to have changed. Note that on the \ + first pass, all dimensions are considered changed, causing this \ + array to be initialised. */ \ + for ( ii = idim; ii >= 1; ii-- ) { \ + wtprod[ ii - 1 ] = wtprod[ ii ] * wt[ ii ]; \ + } \ +\ +/* Accumulate the sums required for forming the interpolated \ + result. We supply the pixel's offset within the input array and the \ + weight to be applied to it. The pixel weight is formed by including \ + the weight factor for dimension zero, since this is not included in \ + the "wtprod" array. */ \ + FORM_LINEAR_INTERPOLATION_SUM(off_in,wtprod[ 0 ] * wt[ 0 ], \ + Xtype,Xfloattype,Xsigned, \ + Usebad,Usevar) \ +\ +/* Now update the indices, offset and weight factors to refer to the \ + next input pixel to be considered. */ \ + idim = 0; \ + do { \ +\ +/* The first input dimension which still refers to the pixel with the \ + lower of the two possible indices is switched to refer to the other \ + pixel (with the higher index). The offset into the input array and \ + the fractional weight factor for this dimension are also updated \ + accordingly. */ \ + if ( dim[ idim ] != hi[ idim ] ) { \ + dim[ idim ] = hi[ idim ]; \ + off_in += stride[ idim ]; \ + wt[ idim ] = frac_hi[ idim ]; \ + break; \ +\ +/* Any earlier dimensions (referring to the higher index) are switched \ + back to the lower index, if not already there, before going on to \ + consider the next dimension. (This process is the same as \ + incrementing a binary number and propagating overflows up through \ + successive digits, except that dimensions where the "lo" and "hi" \ + values are the same can only take one value.) The process stops at \ + the first attempt to return the final dimension to the lower \ + index. */ \ + } else { \ + if ( dim[ idim ] != lo[ idim ] ) { \ + dim[ idim ] = lo[ idim ]; \ + off_in -= stride[ idim ]; \ + wt[ idim ] = frac_lo[ idim ]; \ + } \ + done = ( ++idim == ndim_in ); \ + } \ + } while ( !done ); \ + } while ( !done ); \ + } + +/* This subsidiary macro adds the contribution from a specified input + pixel to the accumulated sums for forming the linearly interpolated + value. */ +#define FORM_LINEAR_INTERPOLATION_SUM(off,wt,Xtype,Xfloattype,Xsigned, \ + Usebad,Usevar) \ +\ +/* Obtain the offset of the input pixel to use. */ \ + off_in = ( off ); \ +\ +/* If necessary, test if this pixel is bad. If not, then obtain the \ + weight to apply to it. */ \ + if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ + pixwt = ( wt ); \ +\ +/* Increment the weighted sum of pixel values and the sum of weights. */ \ + sum += ( (Xfloattype) in[ off_in ] ) * ( (Xfloattype) pixwt ); \ + wtsum += (Xfloattype) pixwt; \ +\ +/* If an output variance estimate is to be generated, and it still \ + seems possible to produce one, then obtain the input variance \ + value. */ \ + if ( Usevar ) { \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + var = in_var[ off_in ]; \ +\ +/* Test if the variance value is bad (if the data type is signed, also \ + check that it is not negative). */ \ + if ( Usebad ) bad_var = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If OK, increment the weighted sum of variance values. */ \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ + ( (Xfloattype) var ); \ + } \ + } \ + } \ + } + +/* This subsidiary macro calculates the interpolated output value (and + variance) from the sums over contributing pixels and assigns them + to the output array(s). */ +#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ + Usebad,Usevar,Nobad) \ +\ +/* Obtain the pixel offset into the output array. */ \ + off_out = offset[ point ]; \ +\ +/* Assign a bad output value (and variance) if required and count it. */ \ + if ( bad ) { \ + if( !Nobad ) { \ + out[ off_out ] = badval; \ + if ( Usevar ) out_var[ off_out ] = badval; \ + } \ + result++; \ +\ +/* Otherwise, calculate the interpolated value. If the output data \ + type is floating point, this result can be stored directly, \ + otherwise we must round to the nearest integer. */ \ + } else { \ + val = sum / wtsum; \ + if ( Xfloating ) { \ + out[ off_out ] = (Xtype) val; \ + } else { \ + out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \ + ( (Xfloattype) 0.5 ) : \ + ( (Xfloattype) -0.5 ) ) ); \ + } \ +\ +/* If a variance estimate is required but none can be obtained, then \ + store a bad output variance value and count it. */ \ + if ( Usevar ) { \ + if ( ( ( Xsigned ) || ( Usebad ) ) && bad_var ) { \ + if( !Nobad ) out_var[ off_out ] = badval; \ + result++; \ +\ +/* Otherwise, calculate the variance estimate and store it, rounding \ + to the nearest integer if necessary. */ \ + } else { \ + val = sum_var / ( wtsum * wtsum ); \ + if ( Xfloating ) { \ + out_var[ off_out ] = (Xtype) val; \ + } else { \ + out_var[ off_out ] = (Xtype) ( val + \ + ( ( val >= (Xfloattype) 0.0 ) ? \ + ( (Xfloattype) 0.5 ) : \ + ( (Xfloattype) -0.5 ) ) ); \ + } \ + } \ + } \ + } + +/* This subsidiary macro tests for negative variance values in the + macros above. This check is required only for signed data types. */ +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ + bad_var = bad_var || ( var < ( (Xtype) 0 ) ); + +/* Expand the main macro above to generate a function for each + required signed data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_INTERPOLATE_LINEAR(LD,long double,1,long double,1) +MAKE_INTERPOLATE_LINEAR(L,long int,0,long double,1) +MAKE_INTERPOLATE_LINEAR(K,INT_BIG,0,long double,1) +#else +MAKE_INTERPOLATE_LINEAR(L,long int,0,double,1) +MAKE_INTERPOLATE_LINEAR(K,INT_BIG,0,double,1) +#endif +MAKE_INTERPOLATE_LINEAR(D,double,1,double,1) +MAKE_INTERPOLATE_LINEAR(F,float,1,float,1) +MAKE_INTERPOLATE_LINEAR(I,int,0,double,1) +MAKE_INTERPOLATE_LINEAR(S,short int,0,float,1) +MAKE_INTERPOLATE_LINEAR(B,signed char,0,float,1) + +/* Re-define the macro for testing for negative variances to do + nothing. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) + +/* Expand the main macro above to generate a function for each + required unsigned data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_INTERPOLATE_LINEAR(UL,unsigned long int,0,long double,0) +MAKE_INTERPOLATE_LINEAR(UK,UINT_BIG,0,long double,0) +#else +MAKE_INTERPOLATE_LINEAR(UL,unsigned long int,0,double,0) +MAKE_INTERPOLATE_LINEAR(UK,UINT_BIG,0,double,0) +#endif +MAKE_INTERPOLATE_LINEAR(UI,unsigned int,0,double,0) +MAKE_INTERPOLATE_LINEAR(US,unsigned short int,0,float,0) +MAKE_INTERPOLATE_LINEAR(UB,unsigned char,0,float,0) + +/* Undefine the macros uxsed above. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#undef CALC_AND_ASSIGN_OUTPUT +#undef FORM_LINEAR_INTERPOLATION_SUM +#undef ASSEMBLE_INPUT_ND +#undef ASSEMBLE_INPUT_2D +#undef ASSEMBLE_INPUT_1D +#undef MAKE_INTERPOLATE_LINEAR + +/* +* Name: +* InterpolateNearest + +* Purpose: +* Resample a data grid, using the nearest-pixel interpolation scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int InterpolateNearest( int ndim_in, +* const int *lbnd_in, const int *ubnd_in, +* const *in, const *in_var, +* int npoint, const int *offset, +* const double *const *coords, +* int flags, badval, +* *out, *out_var ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which resample a rectangular input +* grid of data (and, optionally, associated statistical variance +* values) so as to place them into a new output grid. Each output +* grid point may be mapped on to a position in the input grid in +* an arbitrary way. Where the positions given do not correspond +* with a pixel centre in the input grid, the interpolation scheme +* used is simply to select the nearest pixel (i.e. the one whose +* bounds contain the supplied position). + +* Parameters: +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input grid, its extent along a particular +* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" +* is zero-based). They also define the input grid's coordinate +* system, with each pixel being of unit extent along each +* dimension with integral coordinate values at its centre. +* in +* Pointer to the array of data to be resampled (with an element +* for each pixel in the input grid). The numerical type of +* these data should match the function used, as given by the +* suffix on the function name. The storage order should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +* (i.e. Fortran array storage order). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and type as the "in" array), which +* represent estimates of the statistical variance associated +* with each element of the "in" array. If this second array is +* given (along with the corresponding "out_var" array), then +* estimates of the variance of the resampled data will also be +* returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* npoint +* The number of points at which the input grid is to be +* resampled. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each output point, this array should contain the zero-based +* offset in the output array(s) (i.e. the "out" and, +* optionally, the "out_var" arrays) at which the resampled +* output value(s) should be stored. +* coords +* An array of pointers to double, with "ndim_in" +* elements. Element "coords[coord]" should point at the first +* element of an array of double (with "npoint" elements) which +* contains the values of coordinate number "coord" for each +* interpolation point. The value of coordinate number "coord" +* for interpolation point number "point" is therefore given by +* "coords[coord][point]" (assuming both indices to be +* zero-based). If any point has a coordinate value of AST__BAD +* associated with it, then the corresponding output data (and +* variance) will be set to the value given by "badval" (unles the +* AST__NOBAD flag is specified). +* flags +* The bitwise OR of a set of flag values which control the +* operation of the function. Currently, only the flag +* AST__USEBAD is significant and indicates whether there are +* "bad" (i.e. missing) data in the input array(s) which must be +* recognised and propagated to the output array(s). If this +* flag is not set, all input values are treated literally. +* badval +* If the AST__USEBAD flag is set in the "flags" value (above), +* this parameter specifies the value which is used to identify +* bad data and/or variance values in the input array(s). Its +* numerical type must match that of the "in" (and "in_var") +* arrays. Unles the AST__NOBAD flag is specified in "flags", the +* same value will also be used to flag any output array elements +* for which resampled values could not be obtained. The output +* arrays(s) may be flagged with this value whether or not the +* AST__USEBAD flag is set (the function return value indicates +* whether any such values have been produced). +* out +* Pointer to an array with the same data type as the "in" +* array, into which the resampled data will be returned. Note +* that details of how the output grid maps on to this array +* (e.g. the storage order, number of dimensions, etc.) is +* arbitrary and is specified entirely by means of the "offset" +* array. The "out" array should therefore contain sufficient +* elements to accommodate the "offset" values supplied. There +* is no requirement that all elements of the "out" array should +* be assigned values, and any which are not addressed by the +* contents of the "offset" array will be left unchanged. +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the resampled values may be returned. This array will only be +* used if the "in_var" array has been given. It is addressed in +* exactly the same way (via the "offset" array) as the "out" +* array. The values returned are estimates of the statistical +* variance of the corresponding values in the "out" array, on +* the assumption that all errors in input grid values (in the +* "in" array) are statistically independent and that their +* variance estimates (in the "in_var" array) may simply be +* summed (with appropriate weighting factors). +* +* If no output variance estimates are required, a NULL pointer +* should be given. + +* Returned Value: +* The number of output grid points to which a data value (or a +* variance value if relevant) equal to "badval" has been assigned +* because no valid output value could be obtained. + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +* - A value of zero will be returned if any of these functions is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_INTERPOLATE_NEAREST(X,Xtype,Xsigned) \ +static int InterpolateNearest##X( int ndim_in, \ + const int *lbnd_in, const int *ubnd_in, \ + const Xtype *in, const Xtype *in_var, \ + int npoint, const int *offset, \ + const double *const *coords, \ + int flags, Xtype badval, \ + Xtype *out, Xtype *out_var, int *status ) { \ +\ +/* Local Variables: */ \ + Xtype var; /* Variance value */ \ + double *xn_max; /* Pointer to upper limits array (n-d) */ \ + double *xn_min; /* Pointer to lower limits array (n-d) */ \ + double x; /* x coordinate value */ \ + double xmax; /* x upper limit */ \ + double xmin; /* x lower limit */ \ + double xn; /* Coordinate value (n-d) */ \ + double y; /* y coordinate value */ \ + double ymax; /* y upper limit */ \ + double ymin; /* y lower limit */ \ + int *stride; /* Pointer to array of dimension strides */ \ + int bad; /* Output pixel bad? */ \ + int idim; /* Loop counter for dimensions */ \ + int ix; /* Number of pixels offset in x direction */ \ + int ixn; /* Number of pixels offset (n-d) */ \ + int iy; /* Number of pixels offset in y direction */ \ + int nobad; /* Was the AST__NOBAD flag set? */ \ + int off_in; /* Pixel offset into input array */ \ + int off_out; /* Pixel offset into output array */ \ + int point; /* Loop counter for output points */ \ + int result; /* Returned result value */ \ + int s; /* Temporary variable for strides */ \ + int usebad; /* Use "bad" input pixel values? */ \ + int usevar; /* Process variance array? */ \ + int ystride; /* Stride along input grid y direction */ \ +\ +/* Initialise. */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Initialise variables to avoid "used of uninitialised variable" \ + messages from dumb compilers. */ \ + bad = 0; \ + off_in = 0; \ +\ +/* Determine if we are processing bad pixels or variances. */ \ + nobad = flags & AST__NOBAD; \ + usebad = flags & AST__USEBAD; \ + usevar = in_var && out_var; \ +\ +/* Handle the 1-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + if ( ndim_in == 1 ) { \ +\ +/* Calculate the coordinate limits of the input array. */ \ + xmin = (double) lbnd_in[ 0 ] - 0.5; \ + xmax = (double) ubnd_in[ 0 ] + 0.5; \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \ + } \ + } \ + } \ +\ +/* Four more cases as above, but without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Handle the 2-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + } else if ( ndim_in == 2 ) { \ +\ +/* Calculate the stride along the y dimension of the input grid. */ \ + ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ +\ +/* Calculate the coordinate limits of the input array in each \ + dimension. */ \ + xmin = (double) lbnd_in[ 0 ] - 0.5; \ + xmax = (double) ubnd_in[ 0 ] + 0.5; \ + ymin = (double) lbnd_in[ 1 ] - 0.5; \ + ymax = (double) ubnd_in[ 1 ] + 0.5; \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \ + } \ + } \ + } \ +\ +/* Four more cases as above, but without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Handle other numbers of dimensions. */ \ +/* ----------------------------------- */ \ + } else { \ +\ +/* Allocate workspace. */ \ + stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along each dimension of the input grid. */ \ + for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ + stride[ idim ] = s; \ + s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ +\ +/* Calculate the coordinate limits of the input grid in each \ + dimension. */ \ + xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ + xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ + } \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \ + } \ + } \ + } \ +\ +/* Another 4 cases as above, but without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \ + } \ + } \ + } \ + } \ + } \ +\ +/* Free the workspace. */ \ + stride = astFree( stride ); \ + xn_max = astFree( xn_max ); \ + xn_min = astFree( xn_min ); \ + } \ +\ +/* If an error has occurred, clear the returned result. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 1-dimensional + case. */ +#define ASSEMBLE_INPUT_1D(X,Xtype,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the input grid, or is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If not, then obtain the offset within the input grid of the pixel \ + which contains the current point. */ \ + off_in = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ +\ +/* If necessary, test if the input pixel is bad. */ \ + if ( Usebad ) bad = ( in[ off_in ] == badval ); \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 2-dimensional + case. */ +#define ASSEMBLE_INPUT_2D(X,Xtype,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the input grid, or is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If not, then similarly obtain and test the y coordinate. */ \ + y = coords[ 1 ][ point ]; \ + bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ + if ( !bad ) { \ +\ +/* Obtain the offsets along each input grid dimension of the input \ + pixel which contains the current point. */ \ + ix = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ + iy = (int) floor( y + 0.5 ) - lbnd_in[ 1 ]; \ +\ +/* Calculate this pixel's offset from the start of the input array. */ \ + off_in = ix + ystride * iy; \ +\ +/* If necessary, test if the input pixel is bad. */ \ + if ( Usebad ) bad = ( in[ off_in ] == badval ); \ + } \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the n-dimensional + case. */ +#define ASSEMBLE_INPUT_ND(X,Xtype,Usebad,Usevar) \ +\ +/* Initialise the offset into the input array. Then loop to obtain \ + each coordinate associated with the current output point. */ \ + off_in = 0; \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + xn = coords[ idim ][ point ]; \ +\ +/* Test if the coordinate lies outside the input grid, or is bad. If \ + either is true, the corresponding output pixel value will be bad, \ + so give up on this point. */ \ + bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ + ( xn == AST__BAD ); \ + if ( bad ) break; \ +\ +/* Obtain the offset along the current input grid dimension of the \ + input pixel which contains the current point. */ \ + ixn = (int) floor( xn + 0.5 ) - lbnd_in[ idim ]; \ +\ +/* Accumulate this pixel's offset from the start of the input \ + array. */ \ + off_in += ixn * stride[ idim ]; \ + } \ +\ +/* Once the required input pixel has been located, test if it is \ + bad, if necessary. */ \ + if ( Usebad ) bad = bad || ( in[ off_in ] == badval ); + +/* This subsidiary macro assigns the output value (and variance) to + the output array(s). */ +#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,Usebad,Usevar,Nobad) \ +\ +/* Obtain the pixel offset into the output array. */ \ + off_out = offset[ point ]; \ +\ +/* If the input data value is bad, assign a bad output value (and \ + variance, if required) and count it. */ \ + if ( bad ) { \ + if( !Nobad ) { \ + out[ off_out ] = badval; \ + if ( Usevar ) out_var[ off_out ] = badval; \ + } \ + result++; \ +\ +/* Otherwise, assign the value obtained from the input grid. */ \ + } else { \ + out[ off_out ] = in[ off_in ]; \ +\ +/* If required, obtain the associated variance value. If necessary, \ + test if it is bad (if the data type is signed, also check that it \ + is not negative). */ \ + if ( Usevar ) { \ + var = in_var[ off_in ]; \ + if ( Usebad ) bad = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If the variance value can be bad, and is, then store a bad value in \ + the output array and count it. Otherwise, store the variance \ + value. */ \ + if ( ( ( Xsigned ) || ( Usebad ) ) && bad ) { \ + if( !Nobad ) out_var[ off_out ] = badval; \ + result++; \ + } else { \ + out_var[ off_out ] = var; \ + } \ + } \ + } + +/* This subsidiary macro tests for negative variance values in the + macros above. This check is required only for signed data + types. */ +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ + bad = bad || ( var < ( (Xtype) 0 ) ); + +/* Expand the main macro above to generate a function for each + required signed data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_INTERPOLATE_NEAREST(LD,long double,1) +#endif +MAKE_INTERPOLATE_NEAREST(D,double,1) +MAKE_INTERPOLATE_NEAREST(F,float,1) +MAKE_INTERPOLATE_NEAREST(L,long int,1) +MAKE_INTERPOLATE_NEAREST(K,INT_BIG,1) +MAKE_INTERPOLATE_NEAREST(I,int,1) +MAKE_INTERPOLATE_NEAREST(S,short int,1) +MAKE_INTERPOLATE_NEAREST(B,signed char,1) + +/* Re-define the macro for testing for negative variances to do + nothing. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) + +/* Expand the main macro above to generate a function for each + required unsigned data type. */ +MAKE_INTERPOLATE_NEAREST(UK,UINT_BIG,0) +MAKE_INTERPOLATE_NEAREST(UL,unsigned long int,0) +MAKE_INTERPOLATE_NEAREST(UI,unsigned int,0) +MAKE_INTERPOLATE_NEAREST(US,unsigned short int,0) +MAKE_INTERPOLATE_NEAREST(UB,unsigned char,0) + +/* Undefine the macros used above. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#undef CALC_AND_ASSIGN_OUTPUT +#undef ASSEMBLE_INPUT_ND +#undef ASSEMBLE_INPUT_2D +#undef ASSEMBLE_INPUT_1D +#undef MAKE_INTERPOLATE_NEAREST + +/* +* Name: +* InterpolateBlockAverage + +* Purpose: +* Resample a data grid, using multidimensional block averaging. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void InterpolateBlockAverage( int ndim_in, +* const int lbnd_in[], +* const int ubnd_in[], +* const in[], +* const in_var[], +* int npoint, const int offset[], +* const double *const coords[], +* const double params[], int flags, +* badval, *out, +* *out_var, int *nbad ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which resample a rectangular input +* grid of data (and, optionally, associated statistical variance +* values) so as to place them into a new output grid. To generate +* an output grid pixel, a block average is taken over an ndim- +* dimensional hypercube of pixels in the input grid. If variances +* are being used then the input pixels will be weighted according +* to the reciprocals of the corresponding variance values, and +* input pixels without a valid variance will be ignored; +* otherwise an unweighted average will be taken over +* all non-bad pixels in the cube. The size of the cube over which +* the average is taken is determined by the first element of the +* params array. +* +* This "interpolation" scheme is appropriate where an input grid +* is to be resampled onto a much coarser output grid. + +* Parameters: +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input grid, its extent along a particular +* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" +* is zero-based). They also define the input grid's coordinate +* system, with each pixel being of unit extent along each +* dimension with integral coordinate values at its centre. +* in +* Pointer to the array of data to be resampled (with an element +* for each pixel in the input grid). The numerical type of +* these data should match the function used, as given by the +* suffix on the function name. The storage order should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +* (i.e. Fortran array storage order). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and type as the "in" array), which +* represent estimates of the statistical variance associated +* with each element of the "in" array. If this second array is +* given (along with the corresponding "out_var" array), then +* estimates of the variance of the resampled data will also be +* returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* npoint +* The number of points at which the input grid is to be +* resampled. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each output point, this array should contain the zero-based +* offset in the output array(s) (i.e. the "out" and, +* optionally, the "out_var" arrays) at which the resampled +* output value(s) should be stored. +* coords +* An array of pointers to double, with "ndim_in" +* elements. Element "coords[coord]" should point at the first +* element of an array of double (with "npoint" elements) which +* contains the values of coordinate number "coord" for each +* interpolation point. The value of coordinate number "coord" +* for interpolation point number "point" is therefore given by +* "coords[coord][point]" (assuming both indices to be +* zero-based). If any point has a coordinate value of AST__BAD +* associated with it, then the corresponding output data (and +* variance) will be set to the value given by "badval" (unles the +* AST__NOBAD flag is specified). +* params +* A pointer to an array of doubles giving further information +* about how the resampling is to proceed. Only the first +* element is significant; the nearest integer to this gives +* the number of pixels on either side of the central input +* grid pixel to use in each dimension. Therefore +* (1 + 2*params[0])**ndim_in pixels will be averaged over to +* generate each output pixel. +* flags +* The bitwise OR of a set of flag values which control the +* operation of the function. Currently, only the flag +* AST__USEBAD is significant and indicates whether there are +* "bad" (i.e. missing) data in the input array(s) which must be +* recognised and propagated to the output array(s). If this +* flag is not set, all input values are treated literally. +* badval +* If the AST__USEBAD flag is set in the "flags" value (above), +* this parameter specifies the value which is used to identify +* bad data and/or variance values in the input array(s). Its +* numerical type must match that of the "in" (and "in_var") +* arrays. Unles the AST__NOBAD flag is specified in "flags", the +* same value will also be used to flag any output array elements +* for which resampled values could not be obtained. The output +* arrays(s) may be flagged with this value whether or not the +* AST__USEBAD flag is set (the function return value indicates +* whether any such values have been produced). +* out +* Pointer to an array with the same data type as the "in" +* array, into which the resampled data will be returned. Note +* that details of how the output grid maps on to this array +* (e.g. the storage order, number of dimensions, etc.) is +* arbitrary and is specified entirely by means of the "offset" +* array. The "out" array should therefore contain sufficient +* elements to accommodate the "offset" values supplied. There +* is no requirement that all elements of the "out" array should +* be assigned values, and any which are not addressed by the +* contents of the "offset" array will be left unchanged. +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the resampled values may be returned. This array will only be +* used if the "in_var" array has been given. It is addressed in +* exactly the same way (via the "offset" array) as the "out" +* array. The values returned are estimates of the statistical +* variance of the corresponding values in the "out" array, on +* the assumption that all errors in input grid values (in the +* "in" array) are statistically independent and that their +* variance estimates (in the "in_var" array) may simply be +* summed (with appropriate weighting factors). +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* nbad +* Pointer to an int in which to return the number of +* interpolation points at which an output data value (and/or a +* variance value if relevant) equal to "badval" has been +* assigned because no valid interpolated value could be +* obtained. The maximum value that will be returned is +* "npoint" and the minimum is zero (indicating that all output +* values were successfully obtained). + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_INTERPOLATE_BLOCKAVE(X,Xtype,Xfloating,Xfloattype,Xsigned) \ +static void InterpolateBlockAverage##X( int ndim_in, \ + const int lbnd_in[], \ + const int ubnd_in[], \ + const Xtype in[], \ + const Xtype in_var[], \ + int npoint, const int offset[], \ + const double *const coords[], \ + const double params[], int flags, \ + Xtype badval, Xtype *out, \ + Xtype *out_var, int *nbad ) { \ +\ +/* Local Variables: */ \ + Xfloattype hi_lim; /* Upper limit on output values */ \ + Xfloattype lo_lim; /* Lower limit on output values */ \ + Xfloattype pixwt; /* Weight to apply to individual pixel */ \ + Xfloattype sum; /* Weighted sum of pixel data values */ \ + Xfloattype sum_var; /* Weighted sum of pixel variance values */ \ + Xfloattype val; /* Data value to be assigned to output */ \ + Xfloattype val_var; /* Variance to be assigned to output */ \ + Xfloattype wtsum; /* Sum of weight values */ \ + Xfloattype wtsum_sq; /* Square of sum of weights */ \ + Xtype var; /* Variance value */ \ + double *xn_max; /* Pointer to upper limits array (n-d) */ \ + double *xn_min; /* Pointer to lower limits array (n-d) */ \ + double x; /* x coordinate value */ \ + double xn; /* Coordinate value (n-d) */ \ + double y; /* y coordinate value */ \ + int *hi; /* Pointer to array of upper indices */ \ + int *ixm; /* Pointer to array of current indices */ \ + int *lo; /* Pointer to array of lower indices */ \ + int *status; /* Pointer to inherited status value */ \ + int *stride; /* Pointer to array of dimension strides */ \ + int bad; /* Output pixel bad? */ \ + int bad_var; /* Output variance bad? */ \ + int done; /* All pixel indices done? */ \ + int hi_x; /* Upper pixel index (x dimension) */ \ + int hi_y; /* Upper pixel index (y dimension) */ \ + int idim; /* Loop counter for dimensions */ \ + int ix; /* Pixel index in input grid x dimension */ \ + int ixn; /* Pixel index in input grid (n-d) */ \ + int iy; /* Pixel index in input grid y dimension */ \ + int lo_x; /* Lower pixel index (x dimension) */ \ + int lo_y; /* Lower pixel index (y dimension) */ \ + int neighb; /* Number of adjacent pixels on each side */ \ + int nobad; /* Was the AST__NOBAD flag set? */ \ + int off1; /* Input pixel offset due to y index */ \ + int off_in; /* Offset to input pixel */ \ + int off_out; /* Offset to output pixel */ \ + int point; /* Loop counter for output points */ \ + int s; /* Temporary variable for strides */ \ + int usebad; /* Use "bad" input pixel values? */ \ + int usevar; /* Process variance array? */ \ + int ystride; /* Stride along input grid y dimension */ \ +\ +/* Initialise. */ \ + *nbad = 0; \ +\ +/* Get a pointer to the inherited status argument. */ \ + status = astGetStatusPtr; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Initialise variables to avoid "used of uninitialised variable" \ + messages from dumb compilers. */ \ + val = 0; \ + val_var = 0; \ + sum_var = 0; \ + wtsum = 0; \ + bad = 0; \ + bad_var = 0; \ +\ +/* Determine if we are processing bad pixels or variances. */ \ + nobad = flags & AST__NOBAD; \ + usebad = flags & AST__USEBAD; \ + usevar = in_var && out_var; \ +\ +/* Set the number of pixels each side of central pixel to use. */ \ + neighb = (int) floor( params[ 0 ] + 0.5 ); \ +\ +/* Set up limits for checking output values to ensure that they do not \ + overflow the range of the data type being used. */ \ + lo_lim = LO_##X; \ + hi_lim = HI_##X; \ +\ +/* Handle the 1-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + if ( ndim_in == 1 ) { \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ + } \ + } \ + } \ +\ +/* Another 4 cases as above, but without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Handle the 2-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + } else if ( ndim_in == 2 ) { \ +\ +/* Calculate the stride along the y dimension of the input grid. */ \ + ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ + } \ + } \ + } \ +\ +/* Another 4 cases as above, but without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ + } \ + } \ + } \ + } \ +\ +/* Handle other numbers of dimensions. */ \ +/* ----------------------------------- */ \ + } else { \ +\ +/* Allocate workspace. */ \ + hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + ixm = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ + xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along each dimension of the input grid. */ \ + for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ + stride[ idim ] = s; \ + s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ +\ +/* Calculate the coordinate limits of the input grid in each \ + dimension. */ \ + xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ + xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ + } \ +\ +/* Identify four cases, according to whether bad pixels and/or \ + variances are being processed. In each case, loop through all the \ + output points to (a) assemble the input data needed to form the \ + interpolated value, and (b) calculate the result and assign it to \ + the output arrays(s). In each case we assign constant values (0 or \ + 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ + pixels and variances can be eliminated when not required. */ \ + if ( nobad ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ + } \ + } \ + } \ +\ +/* Another 4 cases as above, but this time without the AST__NOBAD flag. */ \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ + } \ + } \ + } else { \ + if ( usevar ) { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ + } \ + } else { \ + for ( point = 0; point < npoint; point++ ) { \ + ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ + CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ + } \ + } \ + } \ + } \ + } \ +\ +/* Free the workspace. */ \ + hi = astFree( hi ); \ + lo = astFree( lo ); \ + stride = astFree( stride ); \ + ixm = astFree( ixm ); \ + xn_max = astFree( xn_max ); \ + xn_min = astFree( xn_min ); \ + } \ +\ +/* If an error has occurred, clear the returned result. */ \ + if ( !astOK ) *nbad = 0; \ +\ +/* Return. */ \ +} + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 1-dimensional + case. */ +#define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x == AST__BAD ); \ +\ +/* Note we do not need to check here whether the pixel in this position is \ + bad; if any pixels in the cube are good we can form an average. */ \ +\ +/* If OK, calculate the lowest and highest indices (in the x \ + dimension) of the region of neighbouring pixels that will \ + contribute to the interpolated result. Constrain these values to \ + lie within the input grid. */ \ + if ( !bad ) { \ + ix = (int) floor( x ); \ + lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ + hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ +\ +/* Initialise sums for forming the interpolated result. */ \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + bad_var = 0; \ + } \ +\ +/* Loop to inspect all the contributing pixels, calculating the offset \ + of each pixel from the start of the input array. */ \ + off_in = lo_x - lbnd_in[ 0 ]; \ + for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ +\ +/* If necessary, test if the input pixel is bad. */ \ + if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ +\ +/* If we are using variances, then check that the variance is valid; \ + if it is invalid then ignore this pixel altogether. */ \ + if ( Usevar ) { \ + var = in_var[ off_in ]; \ + if ( Usebad ) bad_var = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If variance is valid then accumulate suitably weighted values into \ + the totals. */ \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + pixwt = (Xfloattype) 1.0 / var; \ + sum += pixwt * ( (Xfloattype) in[ off_in ] ); \ + wtsum += pixwt; \ + sum_var += pixwt; \ + } \ +\ +/* If we are not using variances, then accumulate values into the \ + totals with a weighting of unity. */ \ + } else { \ + sum += (Xfloattype) in[ off_in ]; \ + wtsum++; \ + } \ + } \ + } \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the 2-dimensional + case. */ +#define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Obtain the x coordinate of the current point and test if it is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If not, then similarly obtain and test the y coordinate. */ \ + y = coords[ 1 ][ point ]; \ + bad = ( y == AST__BAD ); \ +\ +/* Note we do not need to check here whether the pixel in this position is \ + bad; if any pixels in the cube are good we can form an average. */ \ +\ +/* If OK, calculate the lowest and highest indices (in each dimension) \ + of the region of neighbouring pixels that will contribute to the \ + interpolated result. Constrain these values to lie within the input \ + grid. */ \ + if ( !bad ) { \ + ix = (int) floor( x ); \ + lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ + hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ + iy = (int) floor( y ); \ + lo_y = MaxI( iy - neighb + 1, lbnd_in[ 1 ], status ); \ + hi_y = MinI( iy + neighb, ubnd_in[ 1 ], status ); \ +\ +/* Initialise sums for forming the interpolated result. */ \ + sum = (Xfloattype) 0.0; \ + wtsum = (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + bad_var = 0; \ + } \ +\ +/* Loop to inspect all the contributing pixels, calculating the offset \ + of each pixel from the start of the input array. */ \ + off1 = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \ + for ( iy = lo_y; iy <= hi_y; iy++, off1 += ystride ) { \ + off_in = off1; \ + for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ +\ +/* If necessary, test if the input pixel is bad. */ \ + if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ +\ +/* If we are using variances, then check that the variance is valid; \ + if it is invalid then ignore this pixel altogether. */ \ + if ( Usevar ) { \ + var = in_var[ off_in ]; \ + if ( Usebad ) bad_var = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If variance is valid then accumulate suitably weighted values into \ + the totals. */ \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + pixwt = (Xfloattype) 1.0 / var; \ + sum += pixwt * ( (Xfloattype) in[ off_in ] ); \ + wtsum += pixwt; \ + sum_var += pixwt; \ + } \ +\ +/* If we are not using variances, then accumulate values into the \ + totals with a weighting of unity. */ \ + } else { \ + sum += (Xfloattype) in[ off_in ]; \ + wtsum++; \ + } \ + } \ + } \ + } \ + } \ + } + +/* This subsidiary macro assembles the input data needed in + preparation for forming the interpolated value in the n-dimensional + case. */ +#define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ +\ +/* Initialise offsets into the input array. then loop to obtain each \ + coordinate associated with the current output poitn. */ \ + off_in = 0; \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + xn = coords[ idim ][ point ]; \ +\ +/* Test if the coordinate is bad. If so give up on this point. */ \ + bad = ( xn == AST__BAD ); \ + if ( bad ) break; \ +\ +/* Calculate the lowest and highest indices (in the current dimension) \ + of the region of neighbouring pixels that will contribute to the \ + interpolated result. Constrain these values to lie within the input \ + grid. */ \ + ixn = (int) floor( xn ); \ + lo[ idim ] = MaxI( ixn - neighb + 1, lbnd_in[ idim ], status ); \ + hi[ idim ] = MinI( ixn + neighb, ubnd_in[ idim ], status ); \ +\ +/* If the cube has a zero dimension then no data can come from it. */ \ + bad = ( lo[ idim ] > hi[ idim ] ); \ + if ( bad ) break; \ +\ +/* Accumulate the offset (from the start of the input array) of the \ + contributing pixel which has the lowest index in each dimension. */ \ + off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \ +\ +/* Initialise an array to keep track of the current position in the \ + input cube. */ \ + ixm[ idim ] = lo[ idim ]; \ + } \ +\ +/* Note we do not need to check here whether the pixel in this position is \ + bad; if any pixels in the cube are good we can form an average. */ \ +\ +/* If OK, initialise sums for forming the interpolated result. */ \ + if ( !bad ) { \ + sum = (Xfloattype) 0.0; \ + wtsum= (Xfloattype) 0.0; \ + if ( Usevar ) { \ + sum_var = (Xfloattype) 0.0; \ + bad_var = 0; \ + } \ +\ +/* Loop to inspect all the contributing pixels, calculating the offset \ + of each pixel from the start of the input array. */ \ + do { \ +\ +/* If necessary, test if the input pixel is bad. */ \ + if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ +\ +/* If we are using variances, then check that the variance is valid; \ + if it is invalid then ignore this pixel altogether. */ \ + if ( Usevar ) { \ + var = in_var[ off_in ]; \ + if ( Usebad ) bad_var = ( var == badval ); \ + CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ +\ +/* If variance is valid then accumulate suitably weighted values into \ + the totals. */ \ + if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ + pixwt = (Xfloattype) 1.0 / var; \ + sum += pixwt * ( (Xfloattype) in[ off_in ] ); \ + wtsum += pixwt; \ + sum_var += pixwt; \ + } \ +\ +/* If we are not using variances, then accumulate values into the \ + totals with a weighting of unity. */ \ + } else { \ + sum += (Xfloattype) in[ off_in ]; \ + wtsum++; \ + } \ + } \ +\ +/* Locate the next pixel in the input cube; try incrementing the lowest \ + dimension index first, if that rolls over increment the next \ + dimension index, and so on. */ \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + if ( ixm[ idim ] < hi[ idim ] ) { \ + off_in += stride[ idim ]; \ + ixm[ idim ]++; \ + break; \ + } else { \ + off_in -= stride[ idim ] * ( hi[ idim ] - lo[ idim ] ); \ + ixm[ idim ] = lo[ idim ]; \ + } \ + } \ +\ +/* If the highest dimension index has rolled over, we have done all \ + the pixels in the cube. */ \ + done = ( idim == ndim_in ); \ + } while ( !done ); \ + } + +/* This subsidiary macro calculates the interpolated output value (and + variance) from the sums over contributing pixels, checks the + results for validity, and assigns them to the output array(s). */ +#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Usebad,Usevar,Nobad) \ +\ +/* If the output data value has not yet been flagged as bad, then \ + check that an interpolated value can actually be produced. First \ + check that the sum of weights is not zero. */ \ + if ( !bad ) { \ + bad = ( wtsum == (Xfloattype) 0.0 ); \ +\ +/* If OK, calculate the interpolated value. Then, if the output data \ + type is not floating point, check that this value will not overflow \ + the available output range. */ \ + if ( !bad ) { \ + val = sum / wtsum; \ + if ( !( Xfloating ) ) { \ + bad = ( val <= lo_lim ) || ( val >= hi_lim ); \ + } \ + } \ +\ +/* If no interpolated data value can be produced, then no associated \ + variance will be required either. */ \ + if ( ( Usevar ) && bad ) bad_var = 1; \ + } \ +\ +/* Now perform similar checks on the output variance value (if \ + required). This time we check that the square of the sum of \ + weights is not zero (since this might underflow before the sum of \ + weights). Again we also check to prevent the result overflowing the \ + output data type. */ \ + if ( ( Usevar ) && !bad_var ) { \ + wtsum_sq = wtsum * wtsum; \ + bad_var = ( wtsum_sq == (Xfloattype) 0.0 ); \ + if ( !bad_var ) { \ + val_var = sum_var / wtsum_sq; \ + if ( !( Xfloating ) ) { \ + bad_var = ( val_var <= lo_lim ) || ( val_var >= hi_lim ); \ + } \ + } \ + } \ +\ +/* Obtain the pixel offset into the output array. */ \ + off_out = offset[ point ]; \ +\ +/* Assign a bad output value (and variance) if required and count it. */ \ + if ( bad ) { \ + if( !Nobad ) { \ + out[ off_out ] = badval; \ + if ( Usevar ) out_var[ off_out ] = badval; \ + } \ + (*nbad)++; \ +\ +/* Otherwise, assign the interpolated value. If the output data type \ + is floating point, the result can be stored directly, otherwise we \ + must round to the nearest integer. */ \ + } else { \ + if ( Xfloating ) { \ + out[ off_out ] = (Xtype) val; \ + } else { \ + out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \ + ( (Xfloattype) 0.5 ) : \ + ( (Xfloattype) -0.5 ) ) ); \ + } \ +\ +/* If a variance estimate is required but none can be obtained, then \ + store a bad output variance value and count it. */ \ + if ( Usevar ) { \ + if ( bad_var ) { \ + if( !Nobad ) out_var[ off_out ] = badval; \ + (*nbad)++; \ +\ +/* Otherwise, store the variance estimate, rounding to the nearest \ + integer if necessary. */ \ + } else { \ + if ( Xfloating ) { \ + out_var[ off_out ] = (Xtype) val_var; \ + } else { \ + out_var[ off_out ] = (Xtype) ( val_var + \ + ( ( val_var >= (Xfloattype) 0.0 ) ? \ + ( (Xfloattype) 0.5 ) : \ + ( (Xfloattype) -0.5 ) ) ); \ + } \ + } \ + } \ + } + +/* These subsidiary macros define limits for range checking of results + before conversion to the final data type. For each data type code + , HI_ gives the least positive floating point value which + just overflows that data type towards plus infinity, while LO_ + gives the least negative floating point value which just overflows + that data type towards minus infinity. Thus, a floating point value + must satisfy LO is a floating point type, the limits are not actually used, + but must be present to permit error-free compilation. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define HI_LD ( 0.0L ) +#define LO_LD ( 0.0L ) +#endif +#define HI_D ( 0.0 ) +#define LO_D ( 0.0 ) +#define HI_F ( 0.0f ) +#define LO_F ( 0.0f ) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define HI_L ( 0.5L + (long double) LONG_MAX ) +#define LO_L ( -0.5L + (long double) LONG_MIN ) +#define HI_UL ( 0.5L + (long double) ULONG_MAX ) +#define LO_UL ( -0.5L ) +#define HI_K ( 0.5L + (long double) LONG_MAX ) +#define LO_K ( -0.5L + (long double) LONG_MIN ) +#define HI_UK ( 0.5L + (long double) ULONG_MAX ) +#define LO_UK ( -0.5L ) +#else +#define HI_L ( 0.5 + (double) LONG_MAX ) +#define LO_L ( -0.5 + (double) LONG_MIN ) +#define HI_UL ( 0.5 + (double) ULONG_MAX ) +#define LO_UL ( -0.5 ) +#define HI_K ( 0.5 + (double) LONG_MAX ) +#define LO_K ( -0.5 + (double) LONG_MIN ) +#define HI_UK ( 0.5 + (double) ULONG_MAX ) +#define LO_UK ( -0.5 ) +#endif +#define HI_I ( 0.5 + (double) INT_MAX ) +#define LO_I ( -0.5 + (double) INT_MIN ) +#define HI_UI ( 0.5 + (double) UINT_MAX ) +#define LO_UI ( -0.5 ) +#define HI_S ( 0.5f + (float) SHRT_MAX ) +#define LO_S ( -0.5f + (float) SHRT_MIN ) +#define HI_US ( 0.5f + (float) USHRT_MAX ) +#define LO_US ( -0.5f ) +#define HI_B ( 0.5f + (float) SCHAR_MAX ) +#define LO_B ( -0.5f + (float) SCHAR_MIN ) +#define HI_UB ( 0.5f + (float) UCHAR_MAX ) +#define LO_UB ( -0.5f ) + +/* This subsidiary macro tests for negative variance values. This + check is required only for signed data types. */ +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ + bad_var = bad_var || ( var < ( (Xtype) 0 ) ); + +/* Expand the main macro above to generate a function for each + required signed data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_INTERPOLATE_BLOCKAVE(LD,long double,1,long double,1) +MAKE_INTERPOLATE_BLOCKAVE(L,long int,0,long double,1) +MAKE_INTERPOLATE_BLOCKAVE(K,INT_BIG,0,long double,1) +#else +MAKE_INTERPOLATE_BLOCKAVE(L,long int,0,double,1) +MAKE_INTERPOLATE_BLOCKAVE(K,INT_BIG,0,double,1) +#endif +MAKE_INTERPOLATE_BLOCKAVE(D,double,1,double,1) +MAKE_INTERPOLATE_BLOCKAVE(F,float,1,float,1) +MAKE_INTERPOLATE_BLOCKAVE(I,int,0,double,1) +MAKE_INTERPOLATE_BLOCKAVE(S,short int,0,float,1) +MAKE_INTERPOLATE_BLOCKAVE(B,signed char,0,float,1) + +/* Re-define the macro for testing for negative variances to do + nothing. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) + +/* Expand the main macro above to generate a function for each + required unsigned data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_INTERPOLATE_BLOCKAVE(UL,unsigned long int,0,long double,0) +MAKE_INTERPOLATE_BLOCKAVE(UK,UINT_BIG,0,long double,0) +#else +MAKE_INTERPOLATE_BLOCKAVE(UL,unsigned long int,0,double,0) +MAKE_INTERPOLATE_BLOCKAVE(UK,UINT_BIG,0,double,0) +#endif +MAKE_INTERPOLATE_BLOCKAVE(UI,unsigned int,0,double,0) +MAKE_INTERPOLATE_BLOCKAVE(US,unsigned short int,0,float,0) +MAKE_INTERPOLATE_BLOCKAVE(UB,unsigned char,0,float,0) + +/* Undefine the macros used above. */ +#undef CHECK_FOR_NEGATIVE_VARIANCE +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#undef HI_LD +#undef LO_LD +#endif +#undef HI_D +#undef LO_D +#undef HI_F +#undef LO_F +#undef HI_L +#undef LO_L +#undef HI_UL +#undef LO_UL +#undef HI_K +#undef LO_K +#undef HI_UK +#undef LO_UK +#undef HI_I +#undef LO_I +#undef HI_UI +#undef LO_UI +#undef HI_S +#undef LO_S +#undef HI_US +#undef LO_US +#undef HI_B +#undef LO_B +#undef HI_UB +#undef LO_UB +#undef CALC_AND_ASSIGN_OUTPUT +#undef ASSEMBLE_INPUT_ND +#undef ASSEMBLE_INPUT_2D +#undef ASSEMBLE_INPUT_1D +#undef MAKE_INTERPOLATE_BLOCKAVE + + +static void Invert( AstMapping *this, int *status ) { +/* +*++ +* Name: +c astInvert +f AST_INVERT + +* Purpose: +* Invert a Mapping. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astInvert( AstMapping *this ) +f CALL AST_INVERT( THIS, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function inverts a Mapping by reversing the boolean sense +f This routine inverts a Mapping by reversing the boolean sense +* of its Invert attribute. If this attribute is zero (the +* default), the Mapping will transform coordinates in the way +* specified when it was created. If it is non-zero, the input and +* output coordinates will be inter-changed so that the direction +* of the Mapping is reversed. This will cause it to display the +* inverse of its original behaviour. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping. +f STATUS = INTEGER (Given and Returned) +f The global status. +*-- +*/ + +/* Local Variables: */ + int invert; /* New Invert attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Determine the new Invert attribute value. */ + invert = !astGetInvert( this ); + +/* Clear the old value. */ + astClearInvert( this ); + +/* If the resulting default value is not the one required, then set a + new value explicitly. */ + if ( astGetInvert( this ) != invert ) astSetInvert( this, invert ); +} + +static double J1Bessel( double x, int *status ) { +/* +* Name: +* J1Bessel + +* Purpose: +* Calculates the first-order Bessel function of the first kind. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double J1Bessel( double x, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of the first-order Bessel function +* of the first kind. + +* Parameters: +* x +* The argument for J1. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The calculated J1(x) value. + +* Notes: +* - The algorithm is taken from the SCUBA routine SCULIB_BESSJ1, by +* J.Lightfoot. +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Local Variables: */ + static double p1 = 1.0; + static double p2 = 0.183105E-2; + static double p3 = -0.3516396496E-4; + static double p4 = 0.2457520174E-5; + static double p5 = -0.240337019E-6; + + static double q1 = 0.04687499995; + static double q2 = -0.2002690873E-3; + static double q3 = 0.8449199096E-5; + static double q4 = -0.88228987E-6; + static double q5 = 0.105787412E-6; + + static double r1 = 72362614232.0; + static double r2 = -7895059235.0; + static double r3 = 242396853.1; + static double r4 = -2972611.439; + static double r5 = 15704.48260; + static double r6 = -30.16036606; + + static double s1 = 144725228442.0; + static double s2 = 2300535178.0; + static double s3 = 18583304.74; + static double s4 = 99447.43394; + static double s5 = 376.9991397; + static double s6 = 1.0; + + double ax; + double xx; + double z; + double y; + double value; + int s; + +/* Calculate the value */ + ax = fabs( x ); + if( ax < 8.0 ) { + y = x*x; + value = x*( r1 + y*( r2 + y*( r3 + y*( r4 + y*( r5 + y*r6 ) ) ) ) ) / + ( s1 + y*( s2 + y*( s3 + y*( s4 + y*( s5 + y*s6 ) ) ) ) ); + } else { + s = ( x >= 0.0 ) ? 1 : -1; + z = 8.0 / ax; + y = z*z; + xx = ax - 2.356194491; + value = sqrt ( 0.636619772/ax )*( cos( xx )*( p1 + y*( p2 + y* + ( p3 + y*( p4 + y*p5 ) ) ) )-z*sin( xx )*( q1 + y*( q2 + y*( q3 + y* + ( q4 + y*q5 ) ) ) ) )*s; + } + + return value; + +} + +static int LinearApprox( AstMapping *this, const double *lbnd, + const double *ubnd, double tol, double *fit, int *status ) { +/* +*++ +* Name: +c astLinearApprox +f AST_LINEARAPPROX + +* Purpose: +* Obtain a linear approximation to a Mapping, if appropriate. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c int astLinearApprox( AstMapping *this, const double *lbnd, +c const double *ubnd, double tol, double *fit ) +f RESULT = AST_LINEARAPPROX( THIS, LBND, UBND, TOL, FIT, STATUS ) + +* Class Membership: +* Mapping function. + +* Description: +* This function tests the forward coordinate transformation +* implemented by a Mapping over a given range of input coordinates. If +* the transformation is found to be linear to a specified level of +* accuracy, then an array of fit coefficients is returned. These +* may be used to implement a linear approximation to the Mapping's +* forward transformation within the specified range of output coordinates. +* If the transformation is not sufficiently linear, no coefficients +* are returned. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping. +c lbnd +f LBND( * ) = DOUBLE PRECISION (Given) +c Pointer to an array of doubles +f An array +* containing the lower bounds of a box defined within the input +* coordinate system of the Mapping. The number of elements in this +* array should equal the value of the Mapping's Nin attribute. This +* box should specify the region over which linearity is required. +c ubnd +f UBND( * ) = DOUBLE PRECISION (Given) +c Pointer to an array of doubles +f An array +* containing the upper bounds of the box specifying the region over +* which linearity is required. +c tol +f TOL = DOUBLE PRECISION (Given) +* The maximum permitted deviation from linearity, expressed as +* a positive Cartesian displacement in the output coordinate +* space of the Mapping. If a linear fit to the forward +* transformation of the Mapping deviates from the true transformation +* by more than this amount at any point which is tested, then no fit +* coefficients will be returned. +c fit +f FIT( * ) = DOUBLE PRECISION (Returned) +c Pointer to an array of doubles +f An array +* in which to return the co-efficients of the linear +* approximation to the specified transformation. This array should +* have at least "( Nin + 1 ) * Nout", elements. The first Nout elements +* hold the constant offsets for the transformation outputs. The +* remaining elements hold the gradients. So if the Mapping has 2 inputs +* and 3 outputs the linear approximation to the forward transformation +* is: +* +c X_out = fit[0] + fit[3]*X_in + fit[4]*Y_in +f X_out = fit(1) + fit(4)*X_in + fit(5)*Y_in +* +c Y_out = fit[1] + fit[5]*X_in + fit[6]*Y_in +f Y_out = fit(2) + fit(6)*X_in + fit(7)*Y_in +* +c Z_out = fit[2] + fit[7]*X_in + fit[8]*Y_in +f Z_out = fit(3) + fit(8)*X_in + fit(9)*Y_in +* +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astLinearApprox() +f AST_LINEARAPPROX = LOGICAL +* If the forward transformation is sufficiently linear, +c a non-zero value is returned. Otherwise zero is returned +f .TRUE is returned. Otherwise .FALSE. is returned +* and the fit co-efficients are set to AST__BAD. + +* Notes: +* - This function fits the Mapping's forward transformation. To fit +* the inverse transformation, the Mapping should be inverted using +c astInvert +f AST_INVERT +* before invoking this function. +c - A value of zero +f - A value of .FALSE. +* will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*-- + +* Implementation Deficiencies: +* Sub-classes which implement linear mappings should probably +* over-ride this function to get better accuracy and faster execution, +* but currently they do not. + +*/ + +/* Local Variables: */ + AstPointSet *pset_in_f; /* PointSet for input fitting points */ + AstPointSet *pset_in_t; /* PointSet for input test points */ + AstPointSet *pset_out_f; /* PointSet for output fitting points */ + AstPointSet *pset_out_t; /* PointSet for output test points */ + double **ptr_in_f; /* Input coordinate array pointers */ + double **ptr_in_t; /* Input coordinate array pointers */ + double **ptr_out_f; /* Output coordinate array pointers */ + double **ptr_out_t; /* Output coordinate array pointers */ + double *grad; /* Pointer to matrix of gradients */ + double *zero; /* Pointer to array of zero point values */ + double diff; /* Difference in coordinate values */ + double err; /* Sum of squared error */ + double frac; /* Fraction of input coordinate range */ + double in1; /* Input coordinate value */ + double in2; /* Input coordinate value */ + double indiff; /* Difference in input coordinate values */ + double out1; /* Output coordinate value */ + double out2; /* Output coordinate value */ + double x0; /* Coordinate of grid centre */ + double y; /* Output coordinate (transformed) */ + double yfit; /* Coordinate resulting from fit */ + double z; /* Sum for calculating zero points */ + int *vertex; /* Pointer to flag array for vertices */ + int coord_in; /* Loop counter for input coordinates */ + int coord_out; /* Loop counter for output coordinates. */ + int done; /* All vertices visited? */ + int face1; /* Index of first face coordinates */ + int face2; /* Index of second face coordinates */ + int face; /* Loop counter for faces */ + int ii; /* Index into gradient matrix */ + int linear; /* Mapping is linear? */ + int nc; /* Number of coeffs in fit */ + int ndim_in; /* Number of Mapping inputs */ + int ndim_out; /* Number of Mapping outputs */ + int npoint; /* Number of test points required */ + int point; /* Counter for points */ + int result; /* Returned flag */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + linear = 1; + grad = NULL; + zero = NULL; + +/* Get the number of Mapping output and inputs. */ + ndim_in = astGetNin( this ); + ndim_out = astGetNout( this ); + +/* Store the number of coefficients in the fit.*/ + nc = ( ndim_in + 1 ) * ndim_out; + +/* Create a PointSet to hold input coordinates and obtain a pointer + to its coordinate arrays. */ + pset_in_f = astPointSet( 2 * ndim_in, ndim_in, "", status ); + ptr_in_f = astGetPoints( pset_in_f ); + if ( astOK ) { + +/* Set up and transform an initial set of points. */ +/* ---------------------------------------------- */ +/* Loop to set up input coordinates at the centre of each face of the + input grid, storing them in the PointSet created above. */ + point = 0; + for ( face = 0; face < ( 2 * ndim_in ); face++ ) { + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + ptr_in_f[ coord_in ][ point ] = + 0.5 * ( lbnd[ coord_in ] + ubnd[ coord_in ] ); + } + ptr_in_f[ face / 2 ][ point ] = ( face % 2 ) ? + ubnd[ face / 2 ] : lbnd[ face / 2 ]; + point++; + } + } + +/* Transform these coordinates into the output grid's coordinate system + and obtain an array of pointers to the resulting coordinate + data. */ + pset_out_f = astTransform( this, pset_in_f, 1, NULL ); + ptr_out_f = astGetPoints( pset_out_f ); + if ( astOK ) { + +/* Fit a linear approximation to the points. */ +/* ----------------------------------------- */ +/* Obtain pointers to the locations in the fit coefficients array + where the gradients and zero points should be stored. */ + grad = fit + ndim_out; + zero = fit; + +/* On the assumption that the transformation applied above is + approximately linear, loop to determine the matrix of gradients and + the zero points which describe it. */ + ii = 0; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + z = 0.0; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + +/* Find the indices of opposite faces in each input dimension. */ + face1 = 2 * coord_in; + face2 = face1 + 1; + +/* Obtain the input and output coordinates at these face centres. */ + in1 = ptr_in_f[ coord_in ][ face1 ]; + in2 = ptr_in_f[ coord_in ][ face2 ]; + out1 = ptr_out_f[ coord_out ][ face1 ]; + out2 = ptr_out_f[ coord_out ][ face2 ]; + +/* Check whether any transformed coordinates are bad. If so, the + transformation cannot be linear, so give up trying to fit it. */ + if ( ( out1 == AST__BAD ) || ( out2 == AST__BAD ) ) { + linear = 0; + break; + } + +/* If possible, determine the gradient along this dimension, storing + it in the appropriate element of the gradient matrix. */ + indiff = in2 - in1; + if ( indiff != 0.0 ) { + grad[ ii++ ] = ( out2 - out1 ) / indiff; + } else { + grad[ ii++ ] = 0.0; + } + +/* Accumulate the sum used to determine the zero point. */ + z += ( out1 + out2 ); + } + +/* Also quit the outer loop if a linear fit cannot be obtained. */ + if ( !linear ) break; + +/* Determine the average zero point from all dimensions. */ + zero[ coord_out ] = z / (double) ( 2 * ndim_in ); + } + +/* If a linear fit was obtained, its zero points will be appropriate + to an input coordinate system with an origin at the centre of the + input grid (we assume this to simplify the calculations above). To + correct for this, we transform the actual input coordinates of the + grid's centre through the matrix of gradients and subtract the + resulting coordinates from the zero point values. The zero points + are then correct for the actual output and input coordinate systems + we are using. */ + if ( linear ) { + ii = 0; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + x0 = 0.5 * ( lbnd[ coord_in ] + ubnd[ coord_in ] ); + zero[ coord_out ] -= grad[ ii++ ] * x0; + } + } + } + } + +/* Annul the pointers to the PointSets used above. */ + pset_out_f = astAnnul( pset_out_f ); + pset_in_f = astAnnul( pset_in_f ); + +/* Calculate the number of test points required. */ +/* --------------------------------------------- */ +/* If we have obtained a linear fit above, it will (by construction) + be exact at the centre of each face of the input grid. However, it + may not fit anywhere else. We therefore set up some test points to + determine if it is an adequate approximation elsewhere. */ + if ( astOK && linear ) { + +/* Calculate the number of test points required to place one at each + vertex of the grid. */ + npoint = 1; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + npoint *= 2; + } + +/* Now calculate the total number of test points required, also + allowing one at the centre, one at half the distance to each face, + and one at half the distance to each vertex. */ + npoint = 1 + 2 * ( ndim_in + npoint ); + +/* Set up test points in the input coordinate system. */ +/* --------------------------------------------------- */ +/* Create a PointSet to hold the test coordinates and obtain an array + of pointers to its coordinate data. */ + pset_in_t = astPointSet( npoint, ndim_in, "", status ); + ptr_in_t = astGetPoints( pset_in_t ); + if ( astOK ) { + +/* If the input array is 1-dimensional, the face and vertex positions + calculated below will co-incide. Therefore, we simply distribute + the required number of test points uniformly throughout the input + coordinate range (avoiding the end-points, where the fit has been + obtained). The coordinates are stored in the PointSet created + above. */ + if ( ndim_in == 1 ) { + for ( point = 0; point < npoint; point++ ) { + frac = ( (double) ( point + 1 ) ) / (double) ( npoint + 1 ); + ptr_in_t[ 0 ][ point ] = ( 1.0 - frac ) * lbnd[ 0 ] + + frac * ubnd[ 0 ]; + } + +/* Otherwise, generate one point at the grid centre (offset slightly + since the exact centre may not be very representative). */ + } else { + point = 0; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + ptr_in_t[ coord_in ][ point ] = + 0.49 * lbnd[ coord_in ] + 0.51 * ubnd[ coord_in ]; + } + point++; + +/* Similarly generate a point half way between the grid centre and the + centre of each face. Again introduce some small random offsets to break + any regularity in the grid. */ + for ( face = 0; face < ( 2 * ndim_in ); face++ ) { + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + ptr_in_t[ coord_in ][ point ] = + 0.48 * lbnd[ coord_in ] + 0.52 * ubnd[ coord_in ]; + } + ptr_in_t[ face / 2 ][ point ] = + ( 0.51 * ( ( ( face % 2 ) ? ubnd[ face / 2 ] : + lbnd[ face / 2 ] ) ) + + 0.49 * ptr_in_t[ face / 2 ][ 0 ] ); + point++; + } + +/* Allocate workspace and initialise flags for identifying the + vertices. */ + vertex = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + vertex[ coord_in ] = 0; + } + +/* Now loop to visit each input grid vertex. */ + done = 0; + do { + +/* Generate a test point at each vertex. */ + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + ptr_in_t[ coord_in ][ point ] = vertex[ coord_in ] ? + ubnd[ coord_in ] : + lbnd[ coord_in ]; + +/* Also place one half way between the grid centre and each vertex. */ + ptr_in_t[ coord_in ][ point + 1 ] = + ( 0.52 * ptr_in_t[ coord_in ][ point ] + + 0.48 * ptr_in_t[ coord_in ][ 0 ] ); + } + point += 2; + +/* Now update the array of vertex flags to identify the next vertex. */ + coord_in = 0; + do { + +/* The least significant dimension which does not have its upper bound + as one of the vertex coordinates is changed to use its upper bound + in the next vertex. */ + if ( !vertex[ coord_in ] ) { + vertex[ coord_in ] = 1; + break; + +/* Any less significant dimensions whose upper bounds are already + being used are changed to use their lower bounds in the next + vertex. */ + } else { + vertex[ coord_in ] = 0; + +/* All vertices have been visited when the most significant dimension + is changed back to using its lower bound. */ + done = ( ++coord_in == ndim_in ); + } + } while ( !done ); + } while ( !done ); + } + +/* Free the workspace used for vertex flags. */ + vertex = astFree( vertex ); + } + +/* Transform the test points. */ +/* -------------------------- */ +/* Use the Mapping to transform the test points into the output grid's + coordinate system, obtaining a pointer to the resulting arrays of + output coordinates. */ + pset_out_t = astTransform( this, pset_in_t, 1, NULL ); + ptr_out_t = astGetPoints( pset_out_t ); + +/* Test the linear fit for accuracy. */ +/* --------------------------------- */ +/* If OK so far, then loop to use this fit to transform each test + point and compare the result with the result of applying the + Mapping. */ + if ( astOK ) { + for ( point = 0; point < npoint; point++ ) { + +/* Initialise the fitting error for the current point. */ + err = 0.0; + +/* Obtain each output coordinate (produced by using the Mapping) in + turn and check that it is not bad. If it is, then the + transformation is not linear, so give up testing the fit. */ + ii = 0; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + y = ptr_out_t[ coord_out ][ point ]; + if ( y == AST__BAD ) { + linear = 0; + break; + } + +/* Apply the fitted transformation to the input coordinates to obtain + the approximate output coordinate value. */ + yfit = zero[ coord_out ]; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + yfit += grad[ ii++ ] * ptr_in_t[ coord_in ][ point ]; + } + +/* Form the sum of squared differences between the Mapping's + transformation and the fit. */ + diff = ( y - yfit ); + err += diff * diff; + } + +/* Quit the outer loop if the Mapping is found to be non-linear. */ + if ( !linear ) break; + +/* Test if the Cartesian distance between the true output coordinate + and the approximate one exceeds the accuracy tolerance. If this + happens for any test point, we declare the Mapping non-linear and + give up. */ + if ( sqrt( err ) > tol ) { + linear = 0; + break; + } + } + } + +/* Annul the pointers to the PointSets used above. */ + pset_out_t = astAnnul( pset_out_t ); + } + pset_in_t = astAnnul( pset_in_t ); + } + +/* If an error occurred, or the Mapping was found to be non-linear, + then set the coefficients to AST_BAD. Otherwise, set the returned flag + to indicate that the fit was succesful. */ + if ( !astOK || !linear ) { + for( ii = 0; ii < nc; ii++ ) fit[ ii ] = AST__BAD; + } else { + result = 1; + } + +/* Return the result. */ + return result; +} + +static double LocalMaximum( const MapData *mapdata, double acc, double fract, + double x[], int *status ) { +/* +* Name: +* LocalMaximum + +* Purpose: +* Find a local maximum in a Mapping function. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double LocalMaximum( const MapData *mapdata, double acc, double fract, +* double x[], int *status ); + +* Class Membership: +* Mapping member function. + +* Description: +* This function finds a local maximum in the Mapping function +* supplied. It employs the modified simplex method (as +* implemented by UphillSimplex), but repeatedly re-starts the +* simplex algorithm and tests for convergence of successive +* maxima, so as to further improve robustness on difficult +* problems. + +* Parameters: +* mapdata +* Pointer to a MapData structure describing the Mapping +* function, its coordinate constraints, etc. +* acc +* The required accuracy with which the maximum is to be found. +* fract +* A value between 0.0 and 1.0 which determines the initial step +* length along each coordinate axis. It should be given as a +* fraction of the difference between the upper and lower +* constraint values for each axis (as specified in the +* "mapdata" structure). +* x +* Pointer to an array of double containing the coordinates of +* an initial estimate of the position of the maximum. On exit, +* this will be updated to contain the best estimate of the +* maximum's position, as found by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The best estimate of the Mapping function's maximum value. + +* Notes: +* - A value of AST__BAD will be returned, and no useful +* information about a solution will be produced, if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Constants: */ + const int maxcall = 1500; /* Maximum number of function evaluations */ + const int maxiter = 5; /* Maximum number of iterations */ + +/* Local Variables: */ + double *dx; /* Pointer to array of step lengths */ + double err; /* Simplex error estimate */ + double maximum; /* Simplex maximum value */ + double middle; /* Middle coordinate between bounds */ + double result; /* Result value to return */ + int coord; /* Loop counter for coordinates */ + int done; /* Iterations complete? */ + int iter; /* Loop counter for iterations */ + int ncall; /* Number of function calls (junk) */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialise to avoid compiler warnings. */ + err = 0.0; + +/* Allocate workspace. */ + dx = astMalloc( sizeof( double ) * (size_t) mapdata->nin ); + +/* Perform iterations to repeatedly identify a local maximum. */ + for ( iter = 0; astOK && ( iter < maxiter ); iter++ ) { + +/* Set up initial step lengths along each coordinate axis, adjusting + their signs to avoid placing points outside the coordinate + constraints (i.e. step away from the closer boundary on each + axis). */ + for ( coord = 0; coord < mapdata->nin; coord++ ) { + middle = 0.5 * ( mapdata->lbnd[ coord ] + mapdata->ubnd[ coord ] ); + dx[ coord ] = fract * ( mapdata->ubnd[ coord ] - + mapdata->lbnd[ coord ] ); + if ( x[ coord ] > middle ) dx[ coord ] = -dx[ coord ]; + } + +/* Find an approximation to a local maximum using the simplex method + and check for errors. */ + maximum = UphillSimplex( mapdata, acc, maxcall, dx, x, &err, &ncall, status ); + if ( astOK ) { + +/* Use this maximum value if no previous maximum has been found. */ + if ( result == AST__BAD ) { + result = maximum; + +/* Otherwise use it only if it improves on the previous maximum. */ + } else if ( maximum >= result ) { + +/* We iterate, re-starting the simplex algorithm from its previous + best position so as to guard against premature false + convergence. Iterations continue until the improvement in the + maximum is no greater than the required accuracy (and the simplex + algorithm itself has converged to the required accuracy). Note when + iterations should cease. */ + done = ( ( ( maximum - result ) <= acc ) && ( err <= acc ) ); + +/* Store the best maximum and quit iterating if appropriate. */ + result = maximum; + if ( done ) break; + } + +/* Otherwise, decrement the initial step size for the next iteration. */ + fract /= 1000.0; + } + } + +/* Free the workspace. */ + dx = astFree( dx ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* return the result. */ + return result; +} + +static void MapBox( AstMapping *this, + const double lbnd_in[], const double ubnd_in[], + int forward, int coord_out, + double *lbnd_out, double *ubnd_out, + double xl[], double xu[], int *status ) { +/* +*+ +* Name: +* astMapBox + +* Purpose: +* Find a bounding box for a Mapping. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* void astMapBox( AstMapping *this, +* const double lbnd_in[], const double ubnd_in[], +* int forward, int coord_out, +* double *lbnd_out, double *ubnd_out, +* double xl[], double xu[] ); + +* Class Membership: +* Mapping method. + +* Description: +* This function allows you to find the "bounding box" which just +* encloses another box after it has been transformed by a Mapping +* (using either its forward or inverse transformation). A typical +* use might be to calculate the size which an image would have +* after being transformed by the Mapping. +* +* The function works on one dimension at a time. When supplied +* with the lower and upper bounds of a rectangular region (box) of +* input coordinate space, it finds the lowest and highest values +* taken by a nominated output coordinate within that +* region. Optionally, it also returns the input coordinates where +* these bounding values are attained. It should be used repeatedly +* if the extent of the bounding box is required in more than one +* dimension. + +* Parameters: +* this +* Pointer to the Mapping. +* lbnd_in +* Pointer to an array of double, with one element for each +* Mapping input coordinate. This should contain the lower bound +* of the input box in each dimension. +* ubnd_in +* Pointer to an array of double, with one element for each +* Mapping input coordinate. This should contain the upper bound +* of the input box in each dimension. +* +* Note that it is permissible for the lower bound to exceed the +* corresponding upper bound, as the values will simply be +* swapped before use. +* forward +* If this value is non-zero, then the Mapping's forward +* transformation will be used to transform the input +* box. Otherwise, its inverse transformation will be used. +* +* (If the inverse transformation is selected, then references +* to "input" and "output" coordinates in this description +* should be transposed. For example, the size of the "lbnd_in" +* and "ubnd_in" arrays should match the number of output +* coordinates, as given by the Mapping's Nout attribute.) +* coord_out +* The (zero-based) index of the output coordinate for which the +* lower and upper bounds are required. +* lbnd_out +* Pointer to a double in which to return the lowest value taken +* by the nominated output coordinate within the specified +* region of input coordinate space. +* ubnd_out +* Pointer to a double in which to return the highest value +* taken by the nominated output coordinate within the specified +* region of input coordinate space. +* xl +* An optional pointer to an array of double, with one element +* for each Mapping input coordinate. If given, this array will +* be filled with the coordinates of an input point (although +* not necessarily a unique one) for which the nominated output +* coordinate takes the lower bound value returned in +* "*lbnd_out". +* +* If these coordinates are not required, a NULL pointer may be +* supplied. +* xu +* An optional pointer to an array of double, with one element +* for each Mapping input coordinate. If given, this array will +* be filled with the coordinates of an input point (although +* not necessarily a unique one) for which the nominated output +* coordinate takes the upper bound value returned in +* "*ubnd_out". +* +* If these coordinates are not required, a NULL pointer may be +* supplied. + +* Notes: +* - Any input points which are transformed by the Mapping to give +* output coordinates containing the value AST__BAD are regarded as +* invalid and are ignored, They will make no contribution to +* determining the output bounds, even although the nominated +* output coordinate might still have a valid value at such points. +* - An error will occur if the required output bounds cannot be +* found. Typically, this might occur if all the input points which +* the function considers turn out to be invalid (see above). The +* number of points considered before generating such an error is +* quite large, however, so this is unlikely to occur by accident +* unless valid points are restricted to a very small subset of the +* input coordinate space. +* - The values returned via "lbnd_out", "ubnd_out", "xl" and "xu" +* will be set to the value AST__BAD if this function should fail +* for any reason. Their initial values on entry will not be +* altered if the function is invoked with the global error status +* set. +*- + +* Implementation Notes: +* - This function implements the basic astMapBox method available +* via the protected interface to the Mapping class. The public +* interface to this method is provided by the astMapBoxId_ +* function. +*/ + +/* Local Variables: */ + MapData mapdata; /* Structure to describe Mapping function */ + double *x_l; /* Pointer to coordinate workspace */ + double *x_u; /* Pointer to coordinate workspace */ + double lbnd; /* Required lower bound */ + double ubnd; /* Required upper bound */ + int coord; /* Loop counter for coordinates. */ + int nin; /* Effective number of input coordinates */ + int nout; /* Effective number of output coordinates */ + int refine; /* Can bounds be refined? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialisation to avoid compiler warnings. */ + lbnd = AST__BAD; + ubnd = AST__BAD; + +/* Obtain the effective numbers of input and output coordinates for + the Mapping, taking account of which transformation is to be + used. */ + nin = forward ? astGetNin( this ) : astGetNout( this ); + nout = forward ? astGetNout( this ) : astGetNin( this ); + +/* Check that the output coordinate index supplied is valid and report + an error if it is not. Use public (one-based) coordinate numbering + in the error message. */ + if ( astOK ) { + if ( ( coord_out < 0 ) || ( coord_out >= nout ) ) { + astError( AST__BADCI, "astMapBox(%s): Output coordinate index (%d) " + "invalid - it should be in the range 1 to %d.", status, + astGetClass( this ), coord_out + 1, nout ); + } + } + +/* Initialise a MapData structure to describe the Mapping function + whose limits are to be found. Since it may be evaluated many + times, we attempt to simplify the Mapping supplied. */ + if ( astOK ) { + mapdata.mapping = astSimplify( this ); + +/* Store the number of input/output coordinates and the index of the + output coordinate in which we are interested. */ + mapdata.nin = nin; + mapdata.nout = nout; + mapdata.coord = coord_out; + +/* Note which Mapping transformation is being used. */ + mapdata.forward = forward; + +/* Store pointers to arrays which will contain the input coordinate + bounds. */ + mapdata.lbnd = astMalloc( sizeof( double ) * (size_t) nin ); + mapdata.ubnd = astMalloc( sizeof( double ) * (size_t) nin ); + +/* Create PointSets for passing coordinate data to and from the + Mapping. */ + mapdata.pset_in = astPointSet( 1, nin, "", status ); + mapdata.pset_out = astPointSet( 1, nout, "", status ); + +/* Obtain pointers to these PointSets' coordinate arrays. */ + mapdata.ptr_in = astGetPoints( mapdata.pset_in ); + mapdata.ptr_out = astGetPoints( mapdata.pset_out ); + +/* Allocate workspace for the returned input coordinates. */ + x_l = astMalloc( sizeof( double ) * (size_t) nin ); + x_u = astMalloc( sizeof( double ) * (size_t) nin ); + if ( astOK ) { + +/* Initialise the output bounds and corresponding input coordinates to + "unknown". */ + for ( coord = 0; coord < nin; coord++ ) { + x_l[ coord ] = AST__BAD; + x_u[ coord ] = AST__BAD; + +/* Initialise the input bounds, ensuring they are the correct way + around (if not already supplied this way). */ + mapdata.lbnd[ coord ] = ( lbnd_in[ coord ] < ubnd_in[ coord ] ) ? + lbnd_in[ coord ] : ubnd_in[ coord ]; + mapdata.ubnd[ coord ] = ( ubnd_in[ coord ] > lbnd_in[ coord ] ) ? + ubnd_in[ coord ] : lbnd_in[ coord ]; + } + +/* First examine a set of special input points to obtain an initial + estimate of the required output bounds. Do this only so long as the + number of points involved is not excessive. */ + if ( nin <= 12 ) { + refine = SpecialBounds( &mapdata, &lbnd, &ubnd, x_l, x_u, status ); + } else { + refine = 1; + } + +/* Then attempt to refine this estimate using a global search + algorithm. */ + if( refine ) GlobalBounds( &mapdata, &lbnd, &ubnd, x_l, x_u, status ); + +/* If an error occurred, generate a contextual error message. */ + if ( !astOK ) { + astError( astStatus, "Unable to find a bounding box for a %s.", status, + astGetClass( this ) ); + } + } + +/* Return the output bounds and, if required, the input coordinate + values which correspond with them. */ + if ( astOK ) { + *lbnd_out = lbnd; + *ubnd_out = ubnd; + for ( coord = 0; coord < nin; coord++ ) { + if ( xl ) xl[ coord ] = x_l[ coord ]; + if ( xu ) xu[ coord ] = x_u[ coord ]; + } + } + +/* Annul the simplified Mapping pointer and the temporary + PointSets. Also free the workspace. */ + mapdata.mapping = astAnnul( mapdata.mapping ); + mapdata.lbnd = astFree( mapdata.lbnd ); + mapdata.ubnd = astFree( mapdata.ubnd ); + mapdata.pset_in = astAnnul( mapdata.pset_in ); + mapdata.pset_out = astAnnul( mapdata.pset_out ); + x_l = astFree( x_l ); + x_u = astFree( x_u ); + } + +/* If an error occurred, then return bad bounds values and + coordinates. */ + if ( !astOK ) { + *lbnd_out = AST__BAD; + *ubnd_out = AST__BAD; + for ( coord = 0; coord < nin; coord++ ) { + if ( xl ) xl[ coord ] = AST__BAD; + if ( xu ) xu[ coord ] = AST__BAD; + } + } +} + +static double MapFunction( const MapData *mapdata, const double in[], + int *ncall, int *status ) { +/* +* Name: +* MapFunction + +* Purpose: +* Return the value of a selected transformed coordinate. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double MapFunction( const MapData *mapdata, const double in[], +* int *ncall, int *status ); + +* Class Membership: +* Mapping member function. + +* Description: +* This function takes a set of input coordinates and applies a +* Mapping's coordinate transformation to them. It then returns the +* value of one of the transformed coordinates. +* +* It is provided for use by optimisation functions (e.g. those +* used for finding bounding boxes). The Mapping to be used and +* associated parameters (such as constraints on the range of input +* coordinates and the index of the output coordinate to be +* returned) are supplied in a MapData structure. The value +* returned will be negated if the "negate" component of this +* structure is non-zero. +* +* The value AST__BAD will be returned by this function if the +* input coordinates lie outside the constrained range given in +* the MapData structure, or if any of the transformed output +* coordinates is bad. + +* Parameters: +* mapdata +* Pointer to a MapData structure which describes the Mapping to +* be used. +* in +* A double array containing the input coordinates of a single point. +* ncall +* Pointer to an int containing a count of the number of times +* the Mapping's coordinate transformation has been used. This +* value will be updated to reflect any use made by this +* function. Normally, this means incrementing the value by 1, +* but this will be omitted if the input coordinates supplied +* are outside the constrained range so that no transformation +* is performed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The selected output coordinate value, or AST__BAD, as appropriate. + +* Notes: +* - A value of AST__BAD will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + double result; /* Result to be returned */ + int bad; /* Output coordinates invalid? */ + int coord_in; /* Loop counter for input coordinates */ + int coord_out; /* Loop counter for output coordinates */ + int outside; /* Input point outside bounds? */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* See if the input point lies outside the required bounds. */ + outside = 0; + for ( coord_in = 0; coord_in < mapdata->nin; coord_in++ ) { + if ( ( in[ coord_in ] < mapdata->lbnd[ coord_in ] ) || + ( in[ coord_in ] > mapdata->ubnd[ coord_in ] ) ) { + outside = 1; + break; + } + +/* Also store the input coordinates in the memory associated with the + Mapping's input PointSet. */ + mapdata->ptr_in[ coord_in ][ 0 ] = in[ coord_in ]; + } + +/* If the input coordinates are within bounds, transform them, using the + PointSets identified in the "mapdata" structure. */ + if ( !outside ) { + (void) astTransform( mapdata->mapping, mapdata->pset_in, + mapdata->forward, mapdata->pset_out ); + +/* Increment the number of calls to astTransform and check the error + status. */ + ( *ncall )++; + if ( astOK ) { + +/* If OK, test if any of the output coordinates is bad. */ + bad = 0; + for ( coord_out = 0; coord_out < mapdata->nout; coord_out++ ) { + if ( mapdata->ptr_out[ coord_out ][ 0 ] == AST__BAD ) { + bad = 1; + break; + } + } + +/* If not, then extract the required output coordinate, negating it if + necessary. */ + if ( !bad ) { + result = mapdata->ptr_out[ mapdata->coord ][ 0 ]; + if ( mapdata->negate ) result = -result; + } + } + } + +/* Return the result. */ + return result; +} + +static int MapList( AstMapping *this, int series, int invert, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +*+ +* Name: +* astMapList + +* Purpose: +* Decompose a Mapping into a sequence of simpler Mappings. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astMapList( AstMapping *this, int series, int invert, int *nmap, +* AstMapping ***map_list, int **invert_list ) + +* Class Membership: +* Mapping method. + +* Description: +* This function decomposes a Mapping (which, in derived classes, +* may be a compound Mapping) into a sequence of simpler Mappings +* which may be applied in sequence to achieve the same effect. The +* Mapping is decomposed as far as possible, but it is not +* guaranteed that this will necessarily yield any more than one +* Mapping, which may actually be the original one supplied. +* +* This function is provided to support both the simplification of +* compound Mappings, and the analysis of Mapping structure so that +* particular forms can be recognised. + +* Parameters: +* this +* Pointer to the Mapping to be decomposed (the Mapping is not +* actually modified by this function). +* series +* If this value is non-zero, an attempt will be made to +* decompose the Mapping into a sequence of equivalent Mappings +* which can be applied in series (i.e. one after the other). If +* it is zero, the decomposition will instead yield Mappings +* which can be applied in parallel (i.e. on successive sub-sets +* of the input/output coordinates). +* invert +* The value to which the Mapping's Invert attribute is to be +* (notionally) set before performing the +* decomposition. Normally, the value supplied here will be the +* actual Invert value obtained from the Mapping (e.g. using +* astGetInvert). Sometimes, however, when a Mapping is +* encapsulated within another structure, that structure may +* retain an Invert value (in order to prevent external +* interference) which should be used instead. +* +* Note that the actual Invert value of the Mapping supplied is +* not used (or modified) by this function. +* nmap +* The address of an int which holds a count of the number of +* individual Mappings in the decomposition. On entry, this +* should count the number of Mappings already in the +* "*map_list" array (below). On exit, it is updated to include +* any new Mappings appended by this function. +* map_list +* Address of a pointer to an array of Mapping pointers. On +* entry, this array pointer should either be NULL (if no +* Mappings have yet been obtained) or should point at a +* dynamically allocated array containing Mapping pointers +* ("*nmap" in number) which have been obtained from a previous +* invocation of this function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Mapping pointers that result from the decomposition +* requested. These pointers will be appended to any previously +* present, and the array pointer will be updated as necessary +* to refer to the enlarged array (any space released by the +* original array will be freed automatically). +* +* The new Mapping pointers returned will identify a sequence of +* Mappings which, when applied in order, will perform a forward +* transformation equivalent to that of the original Mapping +* (after its Invert flag has first been set to the value +* requested above). The Mappings should be applied in series or +* in parallel according to the type of decomposition requested. +* +* All the Mapping pointers returned by this function should be +* annulled by the caller, using astAnnul, when no longer +* required. The dynamic array holding these pointers should +* also be freed, using astFree. +* invert_list +* Address of a pointer to an array of int. On entry, this array +* pointer should either be NULL (if no Mappings have yet been +* obtained) or should point at a dynamically allocated array +* containing Invert attribute values ("*nmap" in number) which +* have been obtained from a previous invocation of this +* function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Invert attribute values that result from the +* decomposition requested. These values will be appended to any +* previously present, and the array pointer will be updated as +* necessary to refer to the enlarged array (any space released +* by the original array will be freed automatically). +* +* The new Invert values returned identify the values which must +* be assigned to the Invert attributes of the corresponding +* Mappings (whose pointers are in the "*map_list" array) before +* they are applied. Note that these values may differ from the +* actual Invert attribute values of these Mappings, which are +* not relevant. +* +* The dynamic array holding these values should be freed by the +* caller, using astFree, when no longer required. + +* Returned Value: +* A non-zero value is returned if the supplied Mapping contained any +* inverted CmpMaps. + +* Notes: +* - It is unspecified to what extent the original Mapping and the +* individual (decomposed) Mappings are +* inter-dependent. Consequently, the individual Mappings cannot be +* modified without risking modification of the original. +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then the *nmap value, the +* list of Mapping pointers and the list of Invert values will all +* be returned unchanged. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Since we are dealing with a basic Mapping, only one new Mapping + pointer will be returned. Extend the dynamic arrays to accommodate + this Mapping. */ + *map_list = astGrow( *map_list, *nmap + 1, sizeof( AstMapping * ) ); + *invert_list = astGrow( *invert_list, *nmap + 1, sizeof( int ) ); + if ( astOK ) { + +/* Return the invert flag value for the Mapping and a clone of the + Mapping pointer. */ + ( *invert_list )[ *nmap ] = ( invert != 0 ); + ( *map_list )[ *nmap ] = astClone( this ); + +/* If OK, return the new Mapping count. */ + if ( astOK ) ( *nmap )++; + } + + return 0; +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +*+ +* Name: +* astMapMerge + +* Purpose: +* Simplify a sequence of Mappings. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int astMapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list ) + +* Class Membership: +* Mapping method. + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated Mapping in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated Mapping with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated Mapping which is to be merged with +* its neighbours. This should be a cloned copy of the Mapping +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* Mapping it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated Mapping resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* This is the default method which is inherited by all Mappings which + do not explicitly provide their own simplification method. Return + -1 to indicate that no simplification is provided. */ + return -1; +} + +static int *MapSplit( AstMapping *this, int nin, const int *in, + AstMapping **map, int *status ){ +/* +*+ +* Name: +* astMapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* Mapping. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* int *astMapSplit( AstMapping *this, int nin, const int *in, +* AstMapping **map ) + +* Class Membership: +* Mapping method. + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing Mapping. This is only possible if the specified inputs +* correspond to some subset of the Mapping outputs. That is, there +* must exist a subset of the Mapping outputs for which each output +* depends only on the selected Mapping inputs, and not on any of the +* inputs which have not been selected. Also, any output which is not in +* this subset must not depend on any of the selected inputs. If these +* conditions are not met by the supplied Mapping, then a NULL Mapping +* is returned. + +* Parameters: +* this +* Pointer to the Mapping to be split (the Mapping is not +* actually modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied Mapping, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be differetn to "nin"). A NULL pointer will be +* returned if the supplied Mapping has no subset of outputs which +* depend only on the selected inputs. The returned Mapping is a +* deep copy of the required parts of the supplied Mapping. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied Mapping. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*- + +* Implementation Notes: +* - This function implements the basic astMapSplit method available +* via the protected interface to the Mapping class. The public +* interface to this method is provided by the astMapSplitId_ +* function. +*/ + +/* Local Variables: */ + AstCmpMap *rmap; /* Unsimplified result mapping */ + AstPermMap *pm; /* PermMap which rearranges the inputs */ + int *outperm; /* PermMap output axis permutation array */ + int *result; /* Pointer to returned array */ + int iin; /* Input index */ + int iout; /* Output index */ + int mapnin; /* Number of Mapping inputs */ + int nout; /* No of outputs */ + int ok; /* Can the supplied "in" array be used? */ + int perm; /* Are the inputs permuted? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Verify the input axis indices.*/ + mapnin = astGetNin( this ); + for( iin = 0; iin < nin; iin++ ){ + if( in[ iin ] < 0 || in[ iin ] >= mapnin ) { + astError( AST__AXIIN, "astMapSplit(%s): One of the supplied Mapping " + "input indices has value %d which is invalid; it should " + "be in the range 1 to %d.", status, astGetClass( this ), + in[ iin ] + 1, mapnin ); + break; + } + } + +/* Since we are dealing with a basic Mapping, we can only create the + required output Mapping if all inputs are being selected. */ + if( nin == mapnin ) { + +/* The inputs may have been selected in a different order to that in + which they occur in the supplied Mapping. We therefore create a + PermMap which rearranges the inputs into the order they have in the + supplied Mapping. The supplied "in" array can act as the PermMap's + "inperm" array. Allocate memory for the "outperm" array. */ + outperm = astMalloc( sizeof(int)*(size_t) nin ); + if( astOK ) { + +/* Store the input index for each output in the outperm array and check that + each input has been selected once and only once. Also set a flag + indicating if a PermMap is needed. */ + perm = 0; + ok = 1; + for( iout = 0; iout < nin; iout++ ) outperm[ iout ] = -1; + for( iin = 0; iin < nin; iin++ ) { + iout = in[ iin ]; + if( outperm[ iout ] != -1 ) { + ok = 0; + break; + } else { + outperm[ iout ] = iin; + } + } + for( iout = 0; iout < nin; iout++ ) { + if( outperm[ iout ] == -1 ) { + ok = 0; + break; + } else if( outperm[ iout ] != iout ) { + perm = 1; + } + } + if( ok ) { + +/* Allocate the array to hold the returned output indices. */ + nout = astGetNout( this ); + result = astMalloc( sizeof(int)*(size_t) nout ); + if( astOK ) { + +/* The outputs are copied from the supplied Mapping. */ + for( iout = 0; iout < nout; iout++ ) result[ iout ] = iout; + +/* If the inputs are to be permuted, create the PermMap. */ + if( perm ) { + pm = astPermMap( nin, in, nin, outperm, NULL, "", status ); + +/* The returned Mapping is a series CmpMap containing this PermMap + followed by the supplied Mapping. */ + rmap = astCmpMap( pm, this, 1, "", status ); + *map = astSimplify( rmap ); + rmap = astAnnul( rmap ); + +/* Annul the PermMap pointer. */ + pm = astAnnul( pm ); + +/* If no input permutation is needed, the resturned Mapping is just the + supplied Mapping. */ + } else { + *map = astClone( this ); + } + } + } + +/* Free resources. */ + outperm = astFree( outperm ); + } + } + +/* Free resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static double MatrixDet( int nrow, int ncol, const double *matrix, int *status ){ +/* +* Name: +* MatrixDet + +* Purpose: +* Return the determinant of a matrix. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double MatrixDet( int nrow, int ncol, const double *matrix, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function returns the determinant of the supplied matrix. Any +* rows or columns that hold only zeros or AST_BAD values are first +* removed from the matrix. If the resulting matrix is not square, a +* value of AST__BAD is returned for the determinant. + +* Parameters: +* nrow +* The number of rows in the matrix. +* ncol +* The number of columns in the matrix. +* matrix +* The matrix element values. The first row of "ncol" elements +* should be supplied first, followed by the second row, etc. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The determinant, or AST__BAD if the determinant could not be +* caclculated. +*/ + +/* Local Variables: */ + const double *sqmat; + const double *m; + double *a; + double *y; + double result; + int *iw; + int *usecol; + int *userow; + int i; + int icol; + int irow; + int jf; + int ncoluse; + int ndim; + int nrowuse; + +/* Initialise */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise... */ + sqmat = NULL; + nrowuse = 0; + ncoluse = 0; + +/* Flag any rows and columns that should be ignored because they contain + only bad values or zeros. */ + userow = astCalloc( nrow, sizeof( *userow ) ); + usecol = astCalloc( ncol, sizeof( *userow ) ); + if( astOK ) { + m = matrix; + for( irow = 0; irow < nrow; irow++ ) { + for( icol = 0; icol < ncol; icol++,m++ ) { + if( *m != AST__BAD && *m != 0.0 ) { + usecol[ icol ] = 1; + userow[ irow ] = 1; + } + } + } + +/* Find the number of usable rows and columns. */ + for( irow = 0; irow < nrow; irow++ ) { + if( userow[ irow ] ) nrowuse++; + } + + for( icol = 0; icol < ncol; icol++ ) { + if( usecol[ icol ] ) ncoluse++; + } + } + +/* Return AST__BAD if the resulting matrix is not square. */ + if( ncoluse == nrowuse ) { + ndim = ncoluse; + +/* If any rows or columns contained just bad or zero values, create a new + matrix that excludes them. */ + if( ncol > ndim || nrow > ndim ) { + sqmat = astMalloc( ndim*ndim*sizeof(*sqmat) ); + if( astOK ) { + m = matrix; + a = (double *) sqmat; + for( irow = 0; irow < nrow; irow++ ) { + if( userow[ irow ] ) { + for( icol = 0; icol < ncol; icol++,m++ ) { + if( usecol[ icol ] ) *(a++) = *m; + } + } else { + m += ncol; + } + } + } + +/* If no rows or columns contained just bad values, use the supplied + matrix. */ + } else { + sqmat = matrix; + } + +/* Calculate the determinant of the modified matrix */ + if( ndim == 1 ) { + result = sqmat[ 0 ]; + + } else if( ndim == 2 ) { + result = sqmat[ 0 ]*sqmat[ 3 ] - sqmat[ 1 ]*sqmat[ 2 ]; + + } else { + a = astStore( NULL, sqmat, sizeof( double )*(size_t) (ndim*ndim) ); + iw = astMalloc( sizeof( int )*(size_t) ndim ); + y = astMalloc( sizeof( double )*(size_t) ndim ); + if( y ) { + for( i = 0; i < ndim; i++ ) y[ i ] = 1.0; + palDmat( ndim, a, y, &result, &jf, iw ); + } + y = astFree( y ); + iw = astFree( iw ); + a = astFree( a ); + } + + } + +/* Free the square matrix if it was allocated here. */ + if( sqmat != matrix ) sqmat = astFree( (void *) sqmat ); + +/* Free the usable row/column flags. */ + userow = astFree( userow ); + usecol = astFree( usecol ); + + return result; +} + +static double MaxD( double a, double b, int *status ) { +/* +* Name: +* MaxD + +* Purpose: +* Return the maximum of two double values. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double MaxD( double a, double b, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function returns the maximum of two double values. + +* Parameters: +* a +* The first value. +* b +* The second value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The maximum. +*/ + +/* Return the larger value. */ + return ( a > b ) ? a : b; +} + +static int MaxI( int a, int b, int *status ) { +/* +* Name: +* MaxI + +* Purpose: +* Return the maximum of two integer values. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MaxI( int a, int b, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function returns the maximum of two integer values. + +* Parameters: +* a +* The first value. +* b +* The second value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The maximum. +*/ + +/* Return the larger value. */ + return ( a > b ) ? a : b; +} + +static int MinI( int a, int b, int *status ) { +/* +* Name: +* MinI + +* Purpose: +* Return the minimum of two integer values. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MinI( int a, int b, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function returns the minimum of two integer values. + +* Parameters: +* a +* The first value. +* b +* The second value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The minimum. +*/ + +/* Return the smaller value. */ + return ( a < b ) ? a : b; +} + +static double NewVertex( const MapData *mapdata, int lo, double scale, + double x[], double f[], int *ncall, double xnew[], int *status ) { +/* +* Name: +* NewVertex + +* Purpose: +* Locate a new vertex for a simplex. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double NewVertex( const MapData *mapdata, int lo, double scale, +* double x[], double f[], int *ncall, double xnew[], int *status ); + +* Class Membership: +* Mapping member function. + +* Description: +* This function is provided for use during optimisation of a +* Mapping function using the simplex method. It generates the +* coordinates of a new simplex vertex and evaluates the Mapping +* function at that point. If the function's value is better then +* (i.e. larger than) the value at the previously worst vertex, +* then it is used to replace that vertex. + +* Parameters: +* mapdata +* Pointer to a MapData structure which describes the Mapping +* function to be used. +* lo +* The (zero-based) index of the simplex vertex which initially +* has the worst (lowest) value. +* scale +* The scale factor to be used to generate the new vertex. The +* distance of the worst vertex from the centre of the face +* opposite it is scaled by this factor to give the new vertex +* position. Negative factors result in reflection through this +* opposite face. +* x +* An array of double containing the coordinates of the vertices +* of the simplex. The coordinates of the first vertex are +* stored first, then those of the second vertex, etc. This +* array will be updated by this function if the new vertex is +* used to replace an existing one. +* f +* An array of double containing the Mapping function values at +* each vertex of the simplex. This array will be updated by +* this function if the new vertex is used to replace an +* existing one. +* ncall +* Pointer to an int containing a count of the number of times +* the Mapping function has been invoked. This value will be +* updated to reflect the actions of this function. +* xnew +* An array of double with one element for each input coordinate +* of the Mapping function. This is used as workspace. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Mapping function value at the new vertex. This value is +* returned whether or not the new vertex replaces an existing one. + +* Notes: +* - A value of AST__BAD will be returned by this function if it is +* invoked with the global error status set, or if it should fail +* for any reason. +* - A value of AST__BAD will also be returned if the new vertex +* lies outside the constrained range of input coordinates +* associated with the Mapping function (as specified in the +* MapData structure supplied) or if any of the transformed output +* coordinates produced by the underlying Mapping is bad. In either +* case the new vertex will not be used to replace an existing one. +*/ + +/* Local Variables: */ + double fnew; /* Function value at new vertex */ + double xface; /* Coordinate of centre of magnification */ + int coord; /* Loop counter for coordinates */ + int ncoord; /* Number of coordinates */ + int nvertex; /* Number of simplex vertices */ + int vertex; /* Loop counter for vertices */ + +/* Initialise. */ + fnew = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return fnew; + +/* Obtain the number of Mapping input coordinates from the MapData + structure and calculate the number of simplex vertices. */ + ncoord = mapdata->nin; + nvertex = ncoord + 1; + +/* Loop to obtain each coordinate of the new vertex. */ + for ( coord = 0; coord < ncoord; coord++ ) { + +/* Loop over all vertices except the lowest one and average their + coordinates. This gives the coordinate of the centre of the face + opposite the lowest vertex, which will act as the centre of + magnification. */ + xface = 0.0; + for ( vertex = 0; vertex < nvertex; vertex++ ) { + if ( vertex != lo ) { + +/* Divide each coordinate by the number of vertices as the sum is + accumulated in order to minimise the risk of overflow. */ + xface += x[ vertex * ncoord + coord ] / + ( (double ) ( nvertex - 1 ) ); + } + } + +/* Magnify the lowest vertex's distance from this point by the + required factor to give the coordinates of the new vertex. */ + xnew[ coord ] = xface + ( x[ lo * ncoord + coord ] - xface ) * scale; + } + +/* Evaluate the Mapping function at the new vertex. */ + fnew = MapFunction( mapdata, xnew, ncall, status ); + +/* If the result is not bad and exceeds the previous value at the + lowest vertex, then replace the lowest vertex with this new one. */ + if ( astOK && ( fnew != AST__BAD ) && ( fnew > f[ lo ] ) ) { + for ( coord = 0; coord < ncoord; coord++ ) { + x[ lo * ncoord + coord ] = xnew[ coord ]; + } + f[ lo ] = fnew; + } + +/* Return the value at the new vertex. */ + return fnew; +} + +static int QuadApprox( AstMapping *this, const double lbnd[2], + const double ubnd[2], int nx, int ny, double *fit, + double *rms, int *status ){ +/* +*++ +* Name: +c astQuadApprox +f AST_QUADAPPROX + +* Purpose: +* Obtain a quadratic approximation to a 2D Mapping. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c int QuadApprox( AstMapping *this, const double lbnd[2], +c const double ubnd[2], int nx, int ny, double *fit, +c double *rms ) +f RESULT = AST_QUADAPPROX( THIS, LBND, UBND, NX, NY, FIT, RMS, STATUS ) + +* Class Membership: +* Mapping function. + +* Description: +* This function returns the co-efficients of a quadratic fit to the +* supplied Mapping over the input area specified by +c "lbnd" and "ubnd". +f LBND and UBND. +* The Mapping must have 2 inputs, but may have any number of outputs. +* The i'th Mapping output is modelled as a quadratic function of the +* 2 inputs (x,y): +* +* output_i = a_i_0 + a_i_1*x + a_i_2*y + a_i_3*x*y + a_i_4*x*x + +* a_i_5*y*y +* +c The "fit" +f The FIT +* array is returned holding the values of the co-efficients a_0_0, +* a_0_1, etc. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping. +c lbnd +f LBND( * ) = DOUBLE PRECISION (Given) +c Pointer to an array of doubles +f An array +* containing the lower bounds of a box defined within the input +* coordinate system of the Mapping. The number of elements in this +* array should equal the value of the Mapping's Nin attribute. This +* box should specify the region over which the fit is to be +* performed. +c ubnd +f UBND( * ) = DOUBLE PRECISION (Given) +c Pointer to an array of doubles +f An array +* containing the upper bounds of the box specifying the region over +* which the fit is to be performed. +c nx +f NX = INTEGER (Given) +* The number of points to place along the first Mapping input. The +* first point is at +c "lbnd[0]" and the last is at "ubnd[0]". +f LBND( 1 ) and the last is at UBND( 1 ). +* If a value less than three is supplied a value of three will be used. +c ny +f NY = INTEGER (Given) +* The number of points to place along the second Mapping input. The +* first point is at +c "lbnd[1]" and the last is at "ubnd[1]". +f LBND( 2 ) and the last is at UBND( 2 ). +* If a value less than three is supplied a value of three will be used. +c fit +f FIT( * ) = DOUBLE PRECISION (Returned) +c Pointer to an array of doubles +f An array +* in which to return the co-efficients of the quadratic +* approximation to the specified transformation. This array should +* have at least "6*Nout", elements. The first 6 elements hold the +* fit to the first Mapping output. The next 6 elements hold the +* fit to the second Mapping output, etc. So if the Mapping has 2 +* inputs and 2 outputs the quadratic approximation to the forward +* transformation is: +* +c X_out = fit[0] + fit[1]*X_in + fit[2]*Y_in + fit[3]*X_in*Y_in + +c fit[4]*X_in*X_in + fit[5]*Y_in*Y_in +c Y_out = fit[6] + fit[7]*X_in + fit[8]*Y_in + fit[9]*X_in*Y_in + +c fit[10]*X_in*X_in + fit[11]*Y_in*Y_in +f X_out = fit(1) + fit(2)*X_in + fit(3)*Y_in + fit(4)*X_in*Y_in + +f fit(5)*X_in*X_in + fit(6)*Y_in*Y_in +f Y_out = fit(7) + fit(8)*X_in + fit(9)*Y_in + fit(10)*X_in*Y_in + +f fit(11)*X_in*X_in + fit(12)*Y_in*Y_in +* +c rms +f RMS = DOUBLE PRECISION (Returned) +c Pointer to a double in which to return the +f The +* RMS residual between the fit and the Mapping, summed over all +* Mapping outputs. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astQuadApprox() +f AST_QUADAPPROX = LOGICAL +* If a quadratic approximation was created, +c a non-zero value is returned. Otherwise zero is returned +f .TRUE is returned. Otherwise .FALSE. is returned +* and the fit co-efficients are set to AST__BAD. + +* Notes: +* - This function fits the Mapping's forward transformation. To fit +* the inverse transformation, the Mapping should be inverted using +c astInvert +f AST_INVERT +* before invoking this function. +c - A value of zero +f - A value of .FALSE. +* will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*-- + +*/ + +/* Local Variables: */ + AstPointSet *pset1; + AstPointSet *pset2; + double **pdat1; + double **pdat2; + double *ofit; + double *px; + double *py; + double *pz; + double det; + double dx; + double dy; + double mat[ 6*6 ]; + double sx2; + double sx2y2; + double sx2y; + double sx3; + double sx3y; + double sx4; + double sx; + double sxy2; + double sxy3; + double sxy; + double sy2; + double sy3; + double sy4; + double sy; + double sz; + double sz2; + double szx2; + double szx; + double szxy; + double szy2; + double szy; + double x; + double xx; + double xy; + double y; + double yy; + double z; + int i; + int iout; + int iw[ 6 ]; + int ix; + int iy; + int n; + int nin; + int nout; + int np; + int ntot; + int result; + int sing; + +/* Initialise the returned values. */ + result = 0; + fit[ 0 ] = AST__BAD; + *rms = AST__BAD; + ntot = 0; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Get the number of Mapping inputs and outputs. Report an error if not + correct. */ + nin = astGetI( this, "Nin" ); + nout = astGetI( this, "Nout" ); + if( nin != 2 && astOK ) { + astError( AST__BADNI, "astQuadApprox(%s): Input Mapping has %d %s - " + "it must have 2 inputs.", status, astGetClass( this ), nin, + (nin==1)?"input":"inputs" ); + } + +/* Ensure we are using at least 3 points on each of the two input axes. */ + if( nx < 3 ) nx = 3; + if( ny < 3 ) ny = 3; + +/* Get the total number of grid points. */ + np = nx*ny; + +/* Create a PointSet to hold the 2D grid of input positions. */ + pset1 = astPointSet( np, 2, " ", status ); + pdat1 = astGetPoints( pset1 ); + +/* Create a PointSet to hold the N-D grid of output positions. */ + pset2 = astPointSet( np, nout, " ", status ); + pdat2 = astGetPoints( pset2 ); + +/* Check the memory allocation (and everything else) was succesful. */ + if( astOK ) { + +/* Find the cell dimensions on X and Y input axes. */ + dx = ( ubnd[ 0 ] - lbnd[ 0 ] )/( nx - 1 ); + dy = ( ubnd[ 1 ] - lbnd[ 1 ] )/( ny - 1 ); + +/* Create a regular grid of input positions. */ + px = pdat1[ 0 ]; + py = pdat1[ 1 ]; + for( iy = 0; iy < ny; iy++ ) { + x = lbnd[ 0 ]; + y = lbnd[ 1 ] + iy*dy; + for( ix = 0; ix < nx; ix++ ) { + *(px++) = x; + *(py++) = y; + x += dx; + } + } + +/* Use the supplied Mapping to transform this grid into the output space. */ + (void) astTransform( this, pset1, 1, pset2 ); + +/* Assume the approximation can be created. */ + result = 1; + *rms = 0.0; + +/* Loop round each Mapping output. */ + for( iout = 0; iout < nout && astOK; iout++ ) { + +/* Get a pointer to the first element of the fit array for this output. */ + ofit = fit + 6*iout; + +/* Form the required sums. */ + n = 0; + sx = 0.0; + sy = 0.0; + sxy = 0.0; + sx2 = 0.0; + sy2 = 0.0; + sx2y = 0.0; + sx3 = 0.0; + sxy2 = 0.0; + sy3 = 0.0; + sx2y2 = 0.0; + sx3y = 0.0; + sxy3 = 0.0; + sx4 = 0.0; + sy4 = 0.0; + sz = 0.0; + sz2 = 0.0; + szx = 0.0; + szy = 0.0; + szxy = 0.0; + szx2 = 0.0; + szy2 = 0.0; + + px = pdat1[ 0 ]; + py = pdat1[ 1 ]; + pz = pdat2[ iout ]; + + for( i = 0; i < np; i++ ) { + x = *(px++); + y = *(py++); + z = *(pz++); + + if( z != AST__BAD ) { + xx = x*x; + yy = y*y; + xy = x*y; + + n++; + sx += x; + sy += y; + sxy += xy; + sx2 += xx; + sy2 += yy; + sx2y += xx*y; + sx3 += xx*x; + sxy2 += x*yy; + sy3 += yy*y; + sx2y2 += xx*yy; + sx3y += xx*xy; + sxy3 += xy*yy; + sx4 += xx*xx; + sy4 += yy*yy; + sz += z; + sz2 += z*z; + szx += z*x; + szy += z*y; + szxy += z*xy; + szx2 += z*xx; + szy2 += z*yy; + } + } + +/* Form a matrix (M) and vector (V) such that M.X = V, where X is the + solution vector holding the required best fit parameter values (V is + stored in ofit). */ + mat[ 0 ] = n; + mat[ 1 ] = sx; + mat[ 2 ] = sy; + mat[ 3 ] = sxy; + mat[ 4 ] = sx2; + mat[ 5 ] = sy2; + + mat[ 6 ] = sx; + mat[ 7 ] = sx2; + mat[ 8 ] = sxy; + mat[ 9 ] = sx2y; + mat[ 10 ] = sx3; + mat[ 11 ] = sxy2; + + mat[ 12 ] = sy; + mat[ 13 ] = sxy; + mat[ 14 ] = sy2; + mat[ 15 ] = sxy2; + mat[ 16 ] = sx2y; + mat[ 17 ] = sy3; + + mat[ 18 ] = sxy; + mat[ 19 ] = sx2y; + mat[ 20 ] = sxy2; + mat[ 21 ] = sx2y2; + mat[ 22 ] = sx3y; + mat[ 23 ] = sxy3; + + mat[ 24 ] = sx2; + mat[ 25 ] = sx3; + mat[ 26 ] = sx2y; + mat[ 27 ] = sx3y; + mat[ 28 ] = sx4; + mat[ 29 ] = sx2y2; + + mat[ 30 ] = sy2; + mat[ 31 ] = sxy2; + mat[ 32 ] = sy3; + mat[ 33 ] = sxy3; + mat[ 34 ] = sx2y2; + mat[ 35 ] = sy4; + + ofit[ 0 ] = sz; + ofit[ 1 ] = szx; + ofit[ 2 ] = szy; + ofit[ 3 ] = szxy; + ofit[ 4 ] = szx2; + ofit[ 5 ] = szy2; + +/* Now find the solution vector (the solution over-writes teh current + contents of "ofit"). */ + palDmat( 6, mat, ofit, &det, &sing, iw ); + +/* If the fit failed, fill the coefficient array with bad values. */ + if( sing != 0 ) { + for( i = 0; i < 6; i++ ) ofit[ i ] = AST__BAD; + result = 0; + break; + +/* If the fit succeeded, update the summ of the squared residuals. */ + } else { + ntot += n; + *rms += ofit[ 0 ]*ofit[ 0 ]*n + + 2*ofit[ 0 ]*ofit[ 1 ]*sx + + 2*ofit[ 0 ]*ofit[ 2 ]*sy + + 2*( ofit[ 0 ]*ofit[ 3 ] + ofit[ 1 ]*ofit[ 2 ] )*sxy + + ( 2*ofit[ 0 ]*ofit[ 4 ] + ofit[ 1 ]*ofit[ 1 ] )*sx2 + + ( 2*ofit[ 0 ]*ofit[ 5 ] + ofit[ 2 ]*ofit[ 2 ] )*sy2 + + 2*ofit[ 1 ]*ofit[ 4 ]*sx3 + + 2*( ofit[ 1 ]*ofit[ 3 ] + ofit[ 2 ]*ofit[ 4 ] )*sx2y + + 2*( ofit[ 1 ]*ofit[ 5 ] + ofit[ 2 ]*ofit[ 3 ] )*sxy2 + + 2*ofit[ 2 ]*ofit[ 5 ]*sy3 + + ofit[ 4 ]*ofit[ 4 ]*sx4 + + 2*ofit[ 3 ]*ofit[ 4 ]*sx3y + + ( 2*ofit[ 4 ]*ofit[ 5 ] + ofit[ 3 ]*ofit[ 3 ] )*sx2y2 + + 2*ofit[ 3 ]*ofit[ 5 ]*sxy3 + + ofit[ 5 ]*ofit[ 5 ]*sy4 + + sz2 - 2*( + ofit[ 0 ]*sz + + ofit[ 1 ]*szx + + ofit[ 2 ]*szy + + ofit[ 3 ]*szxy + + ofit[ 4 ]*szx2 + + ofit[ 5 ]*szy2 + ); + } + } + } + +/* Free resources. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Return AST__BAD if anything went wrong. */ + if( !astOK || ntot == 0 ) { + result = 0; + fit[ 0 ] = AST__BAD; + *rms = AST__BAD; + +/* Otherwise normalise the returned RMS. */ + } else { + if( *rms > 0.0 ) { + *rms = sqrt( *rms/ntot ); + } else { + *rms = 0.0; + } + } + +/* Return result */ + return result; +} + +static double Random( long int *seed, int *status ) { +/* +* Name: +* Random + +* Purpose: +* Return a pseudo-random value in the range 0 to 1. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double Random( long int *seed, int *status ); + +* Class Membership: +* Mapping member function. + +* Description: +* This function returns a pseudo-random double value from a PDF +* uniformly distributed in the range 0 to 1. It also updates a +* seed value so that a sequence of pseudo-random values may be +* obtained with successive invocations. + +* Parameters: +* seed +* Pointer to a long int which should initially contain a +* non-zero seed value. This will be updated with a new seed +* which may be supplied on the next invocation in order to +* obtain a different pseudo-random value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The pseudo-random value. +*/ + +/* Local Variables: */ + long int i; /* Temporary storage */ + +/* This a basic random number generator using constants given in + Numerical Recipes (Press et al.). */ + i = *seed / 127773; + *seed = ( *seed - i * 127773 ) * 16807 - i * 2836; + if ( *seed < 0 ) *seed += 2147483647; + +/* Return the result as a double value in the range 0 to 1. */ + return ( (double) ( *seed - 1 ) ) / (double) 2147483646; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, + int *status ){ +/* +*+ +* Name: +* astRate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* result = astRate( AstMapping *this, double *at, int ax1, int ax2 ) + +* Class Membership: +* Mapping method. + +* Description: +* This function evaluates the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. +* +* The result is the mean gradient within a small interval centred on +* the supplied position. The interval size is selected automatically +* to minimise the error on the returned value. For large intervals, +* the error is dominated by changes in the gradient of the +* transformation. For small intervals, the error is dominated by +* rounding errors. The best interval is the one that gives the most +* consistent measure of the gradient within the interval. To find this +* consistency, each candidate interval is subdivided into eight +* sub-intervals, the mean gradient within each sub-interval is found, +* and the associated consistency measure is then the difference between +* the maximum and minimum sub-interval gradient found within the interval. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). + +* Returned Value: +* astRate() +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic astRate method available +* via the protected interface to the Mapping class. The public +* interface to this method is provided by the astRateId_ +* function. +*/ + +#define NN 50 + +/* Local Variables: */ + double h0; + double h; + double mean; + double minrange; + double range0; + double range; + double ret; + double x0; + double y[2*NN+1]; + double z[2*NN+1]; + int ibot; + int iin; + int iret; + int itop; + int nin; + int nout; + +/* Initialise */ + ret = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Allocate resources */ + RateFun( NULL, NULL, -1, 0, 0, NULL, NULL, status ); + +/* Obtain the numbers of input and output coordinates for the Mapping. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + +/* Validate the output index. */ + if ( astOK && ( ax1 < 0 || ax1 >= nout ) ) { + astError( AST__AXIIN, "astRate(%s): The supplied Mapping output " + "index (%d) is invalid; it should be in the range 1 to %d.", status, + astGetClass( this ), ax1 + 1, nout ); + } + +/* Validate the input index. */ + if ( astOK && ( ax2 < 0 || ax2 >= nin ) ) { + astError( AST__AXIIN, "astRate(%s): The supplied Mapping input " + "index (%d) is invalid; it should be in the range 1 to %d.", status, + astGetClass( this ), ax2 + 1, nin ); + } + +/* Check the Mapping has a forward transformation. */ + if ( astOK && !astGetTranForward( this ) ) { + astError( AST__NODEF, "astRate(%s): The supplied Mapping does not " + "have a defined forward transformation.", status, + astGetClass( this ) ); + } + +/* Save the central value on the Mapping input which is to be varied. */ + x0 = at[ ax2 ]; + +/* If it is bad, return bad values. */ + if( astOK && x0 != AST__BAD ) { + +/* The required derivative is formed by evaluating the transformation at + two positions close to "x0", and dividing the change in y by the + change in x. The complexity comes in deciding how close to "x0" the + two points should be. If the points are too far apart, the gradient of + the function may vary significantly between the two points and so we + have little confidence that he mean gradient in the interval is a good + estimate of the gradient at "x0". On the other hand if the points are + too close together, rounding errors will make the gradient value + unreliable. The optimal interval is found by testing a number of + different intervals as follows. Each interval is split into NDIV equal + sub-intervals, and the gradient in each sub-interval is found. The max + and min gradient for any of these sub-intervals is found, and the + difference between them is used as an estimate of the reliability of the + mean gradient within the whole interval. The interval with the + greatest reliability is used to define the returned gradient. + + The initial estimate of the interval size is a fixed small fraction of + the supplied "x0" value, or 1.0 if "x0" is zero. */ + h0 = ( x0 != 0.0 ) ? DBL_EPSILON*1.0E9*fabs( x0 ) : 1.0; + +/* Attempt to find the mean gradient, and the range of gradients, within + an interval of size "h0" centred on "x0". If this cannot be done, + increase "h0" by a factor fo ten repeatedly until it can be done, or a + silly large interval size is reached. */ + mean = AST__BAD; + while( mean == AST__BAD && h0 < 1.0E-10*DBL_MAX ) { + h0 *= 10; + mean = FindGradient( this, at, ax1, ax2, x0, h0, &range0, status ); + } + +/* If this was not successful, return AST__BAD as the function value. */ + if( mean != AST__BAD ) { + +/* We now search through a range of larger interval sizes, to see if any + produce a more reliable mean gradient estimate (i.e. have a smaller range + of gradients within the interval ). After that we search through a range + of smaller interval sizes. The gradient range and mean gradient for + each interval size are stored in arrays "y" and "z" respectively. "iret" + is the index of the most reliable interval found so far (i.e. the one + with the smallest range of sub-interval gradients). The original interval + "h0" is stored in the middle element of these arrays (index "NN"). + Intervals are stored in monotonic order of interval size in the arrays. */ + iret = NN; + y[ NN ] = range0; + z[ NN ] = mean; + minrange = range0; + +/* itop is the index of the last array elements to store calculated values. */ + itop = NN; + +/* Loop round increasing the interval size by a factor of four each time + round. */ + h = h0; + for( iin = NN + 1; iin <= 2*NN && astOK; iin++ ){ + h *= 4.0; + +/* Calculate the mean gradient, and the range of gradients, using the + current interval size. */ + mean = FindGradient( this, at, ax1, ax2, x0, h, &range, status ); + +/* If it could be done, store the values in the arrays. */ + if( mean != AST__BAD ) { + itop++; + z[ itop ] = mean; + y[ itop ] = range; + +/* Look for the smallest range, and note its index in the arrays. */ + if( range < minrange ) { + minrange = range; + iret = itop; + +/* If a range of zero is encountered, we only believe it if the previous + interval also had zero range. Otherwise, it's probably just a numerical + fluke. If the previous interval also had a range of zero, we can forget + the rest of the algorithm since the supplied transformation is linear + and we now have its gradient. So leave the loop. */ + } else if( range == 0.0 && y[ iin - 1 ] == 0 ) { + iret = itop; + break; + } + +/* Stop looping when the interval range is 100 times the original + interval range. */ + if( range > 100*range0 ) break; + } + } + +/* Record the minimum range found so far. */ + range0 = minrange; + +/* ibot is the index of the first array elements to store calculated values. */ + ibot = NN; + +/* Loop round decreasing the interval size by a factor of four each time + round. This is just like the last loop, but goes the other way, to + lower indices. */ + h = h0; + for( iin = NN - 1; iin >= 0 && astOK; iin-- ){ + h /= 4.0; + + mean = FindGradient( this, at, ax1, ax2, x0, h, &range, status ); + if( mean != AST__BAD ) { + ibot--; + z[ ibot ] = mean; + y[ ibot ] = range; + + if( range < minrange ) { + minrange = range; + iret = ibot; + } else if( range == 0.0 && y[ iin + 1 ] == 0 ) { + iret = ibot; + break; + } + + if( range > 100*range0 ) break; + } + } + +/* If the smallest gradient range in any interval was zero, we only + believe it if the adjacent interval size also had zero range. */ + if( minrange == 0.0 ) { + if( ( iret > ibot && y[ iret - 1 ] == 0 ) || + ( iret < itop && y[ iret + 1 ] == 0 ) ) { + ret = z[ iret ]; + +/* Otherwise, search for the smallest gradient range, ignoring values + exactly equal to zero, and return the corresponding mean interval + gradient. */ + } else { + for( iin = ibot; iin <= itop; iin++ ){ + if( y[ iin ] > 0.0 ){ + if( minrange == 0 || y[ iin ] < minrange ) { + minrange = y[ iin ]; + ret = z[ iin ]; + } + } + } + } + +/* If the minimum range was non-zero, we can just return the + corresponding mean gradient. */ + } else { + ret = z[ iret ]; + } + } + } + +/* Free resources */ + RateFun( NULL, NULL, -2, 0, 0, NULL, NULL, status ); + +/* Return the result. */ + return ret; + +#undef NN +} + +static void RateFun( AstMapping *map, double *at, int ax1, int ax2, + int n, double *x, double *y, int *status ) { +/* +* Name: +* RateFun + +* Purpose: +* Find the value of the function currently being differentiated by the +* astRate method. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void RateFun( AstMapping *map, double *at, int ax1, int ax2, +* int n, double *x, double *y, int *status ) + +* Class Membership: +* Mapping method. + +* Description: +* This is a service function for the astRate method. It evaluates the +* function being differentiated at specified axis values. +* +* This function uses static resources in order to avoid the overhead +* of creating new PointSets each time this function is called. These +* static resources which must be initialised before the first invocation +* with a given Mapping, and must be released after the final invocation. +* See "ax1". + +* Parameters: +* map +* Pointer to a Mapping which yields the value of the function at x. +* The Mapping may have any number of inputs and outputs; the specific +* output representing the function value, f, is specified by ax1 and +* the specific input representing the argument, x, is specified by ax2. +* at +* A pointer to an array holding axis values at the position at which +* the function is to be evaluated. The number of values supplied +* must equal the number of inputs to the Mapping. The value supplied +* for axis "ax2" is ignored (the value of "x" is used for axis "ax2"). +* ax1 +* The zero-based index of the Mapping output which is to be +* differentiated. Set this to -1 to allocate, or -2 to release, +* the static resources used by this function. +* ax2 +* The zero-based index of the Mapping input which is to be varied. +* n +* The number of elements in the "x" and "y" arrays. This should not +* be greater than 2*RATE_ORDER. +* x +* The value of the Mapping input specified by ax2 at which the +* function is to be evaluated. If "ax2" is set to -1, then the +* supplied value is used as flag indicating if the static resources +* used by this function should be initialised (if x >= 0 ) or +* freed (if x < 0). +* y +* An array in which to return the function values at the positions +* given in "x". +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstPointSet *pset1; + AstPointSet *pset2; + double **ptr1; + double **ptr2; + double *oldx; + double *oldy; + double *p; + double xx; + int i; + int k; + int nin; + int nout; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(map); + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + pset2 = NULL; + +/* If required, initialise things. */ + if( ax1 == -1 ) { + for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) { + ratefun_pset_size[ i ] = 0; + ratefun_pset1_cache[ i ] = NULL; + ratefun_pset2_cache[ i ] = NULL; + } + ratefun_next_slot = 0; + +/* If required, clean up. */ + } else if( ax1 == -2 ) { + for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) { + ratefun_pset_size[ i ] = 0; + if( ratefun_pset1_cache[ i ] ) ratefun_pset1_cache[ i ] = astAnnul( ratefun_pset1_cache[ i ] ); + if( ratefun_pset2_cache[ i ] ) ratefun_pset2_cache[ i ] = astAnnul( ratefun_pset2_cache[ i ] ); + } + ratefun_next_slot = 0; + +/* Otherwise do the transformations. */ + } else { + +/* See if we have already created PointSets of the correct size. */ + pset1 = NULL; + for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) { + if( ratefun_pset_size[ i ] == n ) { + pset1 = ratefun_pset1_cache[ i ]; + pset2 = ratefun_pset2_cache[ i ]; + break; + } + } + +/* If we have not, create new PointSets now. */ + if( pset1 == NULL ) { + nin = astGetNin( map ); + pset1 = astPointSet( n, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + + nout = astGetNout( map ); + pset2 = astPointSet( n, nout, "", status ); + ptr2 = astGetPoints( pset2 ); + +/* Store the input position in the input PointSet. */ + for( i = 0; i < nin; i++ ) { + xx = at[ i ]; + p = ptr1[ i ]; + for( k = 0; k < n; k++, p++ ) *p = xx; + } + +/* Add these new PointSets to the cache, removing any existing + PointSets. */ + if( ratefun_pset_size[ ratefun_next_slot ] > 0 ) { + (void) astAnnul( ratefun_pset1_cache[ ratefun_next_slot ] ); + (void) astAnnul( ratefun_pset2_cache[ ratefun_next_slot ] ); + } + ratefun_pset1_cache[ ratefun_next_slot ] = pset1; + ratefun_pset2_cache[ ratefun_next_slot ] = pset2; + ratefun_pset_size[ ratefun_next_slot ] = n; + if( ++ratefun_next_slot == RATEFUN_MAX_CACHE ) ratefun_next_slot = 0; + +/* If existing PointSets were found, get there data arrays. */ + } else { + ptr1 = astGetPoints( pset1 ); + ptr2 = astGetPoints( pset2 ); + } + +/* Store the input X values in the input PointSet data array. */ + oldx = ptr1[ ax2 ]; + ptr1[ ax2 ] = x; + +/* Store the output Y values in the output PointSet data array. */ + oldy = ptr2[ ax1 ]; + ptr2[ ax1 ] = y; + +/* Transform the positions. */ + (void) astTransform( map, pset1, 1, pset2 ); + +/* Re-instate the original arrays in the PointSets. */ + ptr1[ ax2 ] = oldx; + ptr2[ ax1 ] = oldy; + + } +} + +/* +*++ +* Name: +c astRebin +f AST_REBIN + +* Purpose: +* Rebin a region of a data grid. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astRebin( AstMapping *this, double wlim, int ndim_in, +c const int lbnd_in[], const int ubnd_in[], +c const in[], const in_var[], +c int spread, const double params[], int flags, +c double tol, int maxpix, +c badval, int ndim_out, +c const int lbnd_out[], const int ubnd_out[], +c const int lbnd[], const int ubnd[], +c out[], out_var[] ); +f CALL AST_REBIN( THIS, WLIM, NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, +f SPREAD, PARAMS, FLAGS, +f TOL, MAXPIX, BADVAL, +f NDIM_OUT, LBND_OUT, UBND_OUT, +f LBND, UBND, OUT, OUT_VAR, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +* This is a set of functions for rebinning gridded data (e.g. an +* image) under the control of a geometrical transformation, which +* is specified by a Mapping. The functions operate on a pair of +* data grids (input and output), each of which may have any number +* of dimensions. Rebinning may be restricted to a specified +* region of the input grid. An associated grid of error estimates +* associated with the input data may also be supplied (in the form +* of variance values), so as to produce error estimates for the +* rebined output data. Propagation of missing data (bad pixels) +* is supported. +* +* Note, if you will be rebining a sequence of input arrays and then +* co-adding them into a single array, the alternative +c astRebinSeq functions +f AST_REBINSEQ routines +* will in general be more efficient. +* +* You should use a rebinning function which matches the numerical +* type of the data you are processing by replacing in +c the generic function name astRebin by an appropriate 1- or +f the generic function name AST_REBIN by an appropriate 1- or +* 2-character type code. For example, if you are rebinning data +c with type "float", you should use the function astRebinF (see +f with type REAL, you should use the function AST_REBINR (see +* the "Data Type Codes" section below for the codes appropriate to +* other numerical types). +* +* Rebinning of the grid of input data is performed by transforming +* the coordinates of the centre of each input grid element (or pixel) +* into the coordinate system of the output grid. The input pixel +* value is then divided up and assigned to the output pixels in the +* neighbourhood of the central output coordinates. A choice of +* schemes are provided for determining how each input pixel value is +* divided up between the output pixels. In general, each output pixel +* may be assigned values from more than one input pixel. All +* contributions to a given output pixel are summed to produce the +* final output pixel value. Output pixels can be set to the supplied +* bad value if they receive contributions from an insufficient number +* of input pixels. This is controlled by the +c "wlim" parameter. +f WLIM argument. +* +* Input pixel coordinates are transformed into the coordinate +* system of the output grid using the forward transformation of the +* Mapping which is supplied. This means that geometrical features +* in the input data are subjected to the Mapping's forward +* transformation as they are transferred from the input to the +* output grid. +* +* In practice, transforming the coordinates of every pixel of a +* large data grid can be time-consuming, especially if the Mapping +* involves complicated functions, such as sky projections. To +* improve performance, it is therefore possible to approximate +* non-linear Mappings by a set of linear transformations which are +* applied piece-wise to separate sub-regions of the data. This +* approximation process is applied automatically by an adaptive +* algorithm, under control of an accuracy criterion which +* expresses the maximum tolerable geometrical distortion which may +* be introduced, as a fraction of a pixel. +* +* This algorithm first attempts to approximate the Mapping with a +* linear transformation applied over the whole region of the +* input grid which is being used. If this proves to be +* insufficiently accurate, the input region is sub-divided into +* two along its largest dimension and the process is repeated +* within each of the resulting sub-regions. This process of +* sub-division continues until a sufficiently good linear +* approximation is found, or the region to which it is being +* applied becomes too small (in which case the original Mapping is +* used directly). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to a Mapping, whose forward transformation will be +* used to transform the coordinates of pixels in the input +* grid into the coordinate system of the output grid. +* +* The number of input coordinates used by this Mapping (as +* given by its Nin attribute) should match the number of input +c grid dimensions given by the value of "ndim_in" +f grid dimensions given by the value of NDIM_IN +* below. Similarly, the number of output coordinates (Nout +* attribute) should match the number of output grid dimensions +c given by "ndim_out". +f given by NDIM_OUT. +c wlim +f WLIM = DOUBLE PRECISION (Given) +* Gives the required number of input pixel values which must contribute +* to an output pixel in order for the output pixel value to be +* considered valid. If the sum of the input pixel weights contributing +* to an output pixel is less than the supplied +c "wlim" +f WLIM +* value, then the output pixel value is returned set to the +* supplied bad value. +c ndim_in +f NDIM_IN = INTEGER (Given) +* The number of dimensions in the input grid. This should be at +* least one. +c lbnd_in +f LBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +c ubnd_in +f UBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +c Note that "lbnd_in" and "ubnd_in" together define the shape +f Note that LBND_IN and UBND_IN together define the shape +* and size of the input grid, its extent along a particular +c (j'th) dimension being ubnd_in[j]-lbnd_in[j]+1 (assuming the +c index "j" to be zero-based). They also define +f (J'th) dimension being UBND_IN(J)-LBND_IN(J)+1. They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre. +c in +f IN( * ) = (Given) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* input grid, containing the input data to be rebined. The +* numerical type of this array should match the 1- or +* 2-character type code appended to the function name (e.g. if +c you are using astRebinF, the type of each array element +c should be "float"). +f you are using AST_REBINR, the type of each array element +f should be REAL). +* +* The storage order of data within this array should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c in_var +f IN_VAR( * ) = (Given) +c An optional pointer to a second array with the same size and +c type as the "in" array. If given, this should contain a set +c of non-negative values which represent estimates of the +c statistical variance associated with each element of the "in" +c array. If this array is supplied (together with the +c corresponding "out_var" array), then estimates of the +c variance of the rebined output data will be calculated. +c +c If no input variance estimates are being provided, a NULL +c pointer should be given. +f An optional second array with the same size and type as the +f IN array. If the AST__USEVAR flag is set via the FLAGS +f argument (below), this array should contain a set of +f non-negative values which represent estimates of the +f statistical variance associated with each element of the IN +f array. Estimates of the variance of the rebined output data +f will then be calculated. +f +f If the AST__USEVAR flag is not set, no input variance +f estimates are required and this array will not be used. A +f dummy (e.g. one-element) array may then be supplied. +c spread +f SPREAD = INTEGER (Given) +c This parameter specifies the scheme to be used for dividing +f This argument specifies the scheme to be used for dividing +* each input data value up amongst the corresponding output pixels. +* It may be used to select +* from a set of pre-defined schemes by supplying one of the +* values described in the "Pixel Spreading Schemes" +* section below. If a value of zero is supplied, then the +* default linear spreading scheme is used (equivalent to +* supplying the value AST__LINEAR). +c params +f PARAMS( * ) = DOUBLE PRECISION (Given) +c An optional pointer to an array of double which should contain +f An optional array which should contain +* any additional parameter values required by the pixel +* spreading scheme. If such parameters are required, this +* will be noted in the "Pixel Spreading Schemes" +* section below. +* +c If no additional parameters are required, this array is not +c used and a NULL pointer may be given. +f If no additional parameters are required, this array is not +f used. A dummy (e.g. one-element) array may then be supplied. +c flags +f FLAGS = INTEGER (Given) +c The bitwise OR of a set of flag values which may be used to +f The sum of a set of flag values which may be used to +* provide additional control over the rebinning operation. See +* the "Control Flags" section below for a description of the +* options available. If no flag values are to be set, a value +* of zero should be given. +c tol +f TOL = DOUBLE PRECISION (Given) +* The maximum tolerable geometrical distortion which may be +* introduced as a result of approximating non-linear Mappings +* by a set of piece-wise linear transformations. This should be +* expressed as a displacement in pixels in the output grid's +* coordinate system. +* +* If piece-wise linear approximation is not required, a value +* of zero may be given. This will ensure that the Mapping is +* used without any approximation, but may increase execution +* time. +* +* If the value is too high, discontinuities between the linear +* approximations used in adjacent panel will be higher, and may +* cause the edges of the panel to be visible when viewing the output +* image at high contrast. If this is a problem, reduce the +* tolerance value used. +c maxpix +f MAXPIX = INTEGER (Given) +* A value which specifies an initial scale size (in pixels) for +* the adaptive algorithm which approximates non-linear Mappings +* with piece-wise linear transformations. Normally, this should +* be a large value (larger than any dimension of the region of +* the input grid being used). In this case, a first attempt to +* approximate the Mapping by a linear transformation will be +* made over the entire input region. +* +* If a smaller value is used, the input region will first be +c divided into sub-regions whose size does not exceed "maxpix" +f divided into sub-regions whose size does not exceed MAXPIX +* pixels in any dimension. Only at this point will attempts at +* approximation commence. +* +* This value may occasionally be useful in preventing false +* convergence of the adaptive algorithm in cases where the +* Mapping appears approximately linear on large scales, but has +* irregularities (e.g. holes) on smaller scales. A value of, +* say, 50 to 100 pixels can also be employed as a safeguard in +* general-purpose software, since the effect on performance is +* minimal. +* +* If too small a value is given, it will have the effect of +* inhibiting linear approximation altogether (equivalent to +c setting "tol" to zero). Although this may degrade +f setting TOL to zero). Although this may degrade +* performance, accurate results will still be obtained. +c badval +f BADVAL = (Given) +* This argument should have the same type as the elements of +c the "in" array. It specifies the value used to flag missing +f the IN array. It specifies the value used to flag missing +* data (bad pixels) in the input and output arrays. +* +c If the AST__USEBAD flag is set via the "flags" parameter, +f If the AST__USEBAD flag is set via the FLAGS argument, +c then this value is used to test for bad pixels in the "in" +c (and "in_var") array(s). +f then this value is used to test for bad pixels in the IN +f (and IN_VAR) array(s). +* +* In all cases, this value is also used to flag any output +c elements in the "out" (and "out_var") array(s) for which +f elements in the OUT (and OUT_VAR) array(s) for which +* rebined values could not be obtained (see the "Propagation +* of Missing Data" section below for details of the +* circumstances under which this may occur). +c ndim_out +f NDIM_OUT = INTEGER (Given) +* The number of dimensions in the output grid. This should be +* at least one. It need not necessarily be equal to the number +* of dimensions in the input grid. +c lbnd_out +f LBND_OUT( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the output grid along each dimension. +c ubnd_out +f UBND_OUT( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the output grid along each dimension. +* +c Note that "lbnd_out" and "ubnd_out" together define the +f Note that LBND_OUT and UBND_OUT together define the +* shape, size and coordinate system of the output grid in the +c same way as "lbnd_in" and "ubnd_in" define the shape, size +f same way as LBND_IN and UBND_IN define the shape, size +* and coordinate system of the input grid. +c lbnd +f LBND( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the first pixel in the region +* of the input grid which is to be included in the rebined output +* array. +c ubnd +f UBND( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the last pixel in the region of +* the input grid which is to be included in the rebined output +* array. +* +c Note that "lbnd" and "ubnd" together define the shape and +f Note that LBND and UBND together define the shape and +* position of a (hyper-)rectangular region of the input grid +* which is to be included in the rebined output array. This region +* should lie wholly within the extent of the input grid (as +c defined by the "lbnd_in" and "ubnd_in" arrays). Regions of +f defined by the LBND_IN and UBND_IN arrays). Regions of +* the input grid lying outside this region will not be used. +c out +f OUT( * ) = (Returned) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* output grid, in which the rebined data values will be +* returned. The numerical type of this array should match that +c of the "in" array, and the data storage order should be such +f of the IN array, and the data storage order should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c out_var +f OUT_VAR( * ) = (Returned) +c An optional pointer to an array with the same type and size +c as the "out" array. If given, this array will be used to +c return variance estimates for the rebined data values. This +c array will only be used if the "in_var" array has also been +c supplied. +f An optional array with the same type and size as the OUT +f array. If the AST__USEVAR flag is set via the FLAGS argument, +f this array will be used to return variance estimates for the +f rebined data values. +* +* The output variance values will be calculated on the +* assumption that errors on the input data values are +* statistically independent and that their variance estimates +* may simply be summed (with appropriate weighting factors) +* when several input pixels contribute to an output data +* value. If this assumption is not valid, then the output error +* estimates may be biased. In addition, note that the +* statistical errors on neighbouring output data values (as +* well as the estimates of those errors) may often be +* correlated, even if the above assumption about the input data +* is correct, because of the pixel spreading schemes +* employed. +* +c If no output variance estimates are required, a NULL pointer +c should be given. +f If the AST__USEVAR flag is not set, no output variance +f estimates will be calculated and this array will not be +f used. A dummy (e.g. one-element) array may then be supplied. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Data Type Codes: +* To select the appropriate rebinning function, you should +c replace in the generic function name astRebin with a +f replace in the generic function name AST_REBIN with a +* 1- or 2-character data type code, so as to match the numerical +* type of the data you are processing, as follows: +c - D: double +c - F: float +c - I: int +c - B: byte (signed char) +c - UB: unsigned byte (unsigned char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - B: BYTE (treated as signed) +f - UB: BYTE (treated as unsigned) +* +c For example, astRebinD would be used to process "double" +c data, while astRebinI would be used to process "int" +c data, etc. +f For example, AST_REBIND would be used to process DOUBLE +f PRECISION data, while AST_REBINI would be used to process +f integer data (stored in an INTEGER array), etc. +* +* Note that, unlike +c astResample, the astRebin +f AST_RESAMPLE, the AST_REBIN +* set of functions does not yet support unsigned integer data types +* or integers of different sizes. + +* Pixel Spreading Schemes: +* The pixel spreading scheme specifies the Point Spread Function (PSF) +* applied to each input pixel value as it is copied into the output +* array. It can be thought of as the inverse of the sub-pixel +* interpolation schemes used by the +c astResample +f AST_RESAMPLE +* group of functions. That is, in a sub-pixel interpolation scheme the +* kernel specifies the weight to assign to each input pixel when +* forming the weighted mean of the input pixels, whereas the kernel in a +* pixel spreading scheme specifies the fraction of the input data value +* which is to be assigned to each output pixel. As for interpolation, the +* choice of suitable pixel spreading scheme involves stricking a balance +* between schemes which tend to degrade sharp features in the data by +* smoothing them, and those which attempt to preserve sharp features but +* which often tend to introduce unwanted artifacts. See the +c astResample +f AST_RESAMPLE +* documentation for further discussion. +* +* The binning algorithm used has the ability to introduce artifacts +* not seen when using a resampling algorithm. Particularly, when +* viewing the output image at high contrast, systems of curves lines +* covering the entire image may be visible. These are caused by a +* beating effect between the input pixel positions and the output pixels +* position, and their nature and strength depend critically upon the +* nature of the Mapping and the spreading function being used. In +* general, the nearest neighbour spreading function demonstrates this +* effect more clearly than the other functions, and for this reason +* should be used with caution. +* +* The following values (defined in the +c "ast.h" header file) +f AST_PAR include file) +* may be assigned to the +c "spread" +f SPREAD +* parameter. See the +c astResample +f AST_RESAMPLE +* documentation for details of these schemes including the use of the +c "fspread" and "params" parameters: +f FSPREAD and PARAMS arguments: +* +* - AST__NEAREST +* - AST__LINEAR +* - AST__SINC +* - AST__SINCSINC +* - AST__SINCCOS +* - AST__SINCGAUSS +* - AST__SOMBCOS +* +* In addition, the following schemes can be used with +f AST_REBIN but not with AST_RESAMPLE: +c astRebin but not with astResample: +* +* - AST__GAUSS: This scheme uses a kernel of the form exp(-k*x*x), with k +* a positive constant determined by the full-width at half-maximum (FWHM). +* The FWHM should be supplied in units of output pixels by means of the +c "params[1]" +f PARAMS(2) +* value and should be at least 0.1. The +c "params[0]" +f PARAMS(1) +* value should be used to specify at what point the Gaussian is truncated +* to zero. This should be given as a number of output pixels on either +* side of the central output point in each dimension (the nearest integer +* value is used). + +* Control Flags: +c The following flags are defined in the "ast.h" header file and +f The following flags are defined in the AST_PAR include file and +* may be used to provide additional control over the rebinning +* process. Having selected a set of flags, you should supply the +c bitwise OR of their values via the "flags" parameter: +f sum of their values via the FLAGS argument: +* +* - AST__USEBAD: Indicates that there may be bad pixels in the +* input array(s) which must be recognised by comparing with the +c value given for "badval" and propagated to the output array(s). +f value given for BADVAL and propagated to the output array(s). +* If this flag is not set, all input values are treated literally +c and the "badval" value is only used for flagging output array +f and the BADVAL value is only used for flagging output array +* values. +f - AST__USEVAR: Indicates that variance information should be +f processed in order to provide estimates of the statistical error +f associated with the rebined values. If this flag is not set, +f no variance processing will occur and the IN_VAR and OUT_VAR +f arrays will not be used. (Note that this flag is only available +f in the Fortran interface to AST.) + +* Propagation of Missing Data: +* Instances of missing data (bad pixels) in the output grid are +c identified by occurrences of the "badval" value in the "out" +f identified by occurrences of the BADVAL value in the OUT +* array. These are produced if the sum of the weights of the +* contributing input pixels is less than +c "wlim". +f WLIM. +* +* An input pixel is considered bad (and is consequently ignored) if +* its +c data value is equal to "badval" and the AST__USEBAD flag is +c set via the "flags" parameter. +f data value is equal to BADVAL and the AST__USEBAD flag is +f set via the FLAGS argument. +* +* In addition, associated output variance estimates (if +c calculated) may be declared bad and flagged with the "badval" +c value in the "out_var" array for similar reasons. +f calculated) may be declared bad and flagged with the BADVAL +f value in the OUT_VAR array for similar reasons. +*-- +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_REBIN(X,Xtype,IntType) \ +static void Rebin##X( AstMapping *this, double wlim, int ndim_in, \ + const int lbnd_in[], const int ubnd_in[], \ + const Xtype in[], const Xtype in_var[], \ + int spread, const double params[], int flags, \ + double tol, int maxpix, Xtype badval, \ + int ndim_out, const int lbnd_out[], \ + const int ubnd_out[], const int lbnd[], \ + const int ubnd[], Xtype out[], Xtype out_var[], int *status ) { \ +\ +/* Local Variables: */ \ + astDECLARE_GLOBALS /* Thread-specific data */ \ + const char *badflag; /* Name of illegal flag */ \ + AstMapping *simple; /* Pointer to simplified Mapping */ \ + Xtype *d; /* Pointer to next output data value */ \ + Xtype *v; /* Pointer to next output variance value */ \ + double *w; /* Pointer to next weight value */ \ + double *work; /* Pointer to weight array */ \ + int idim; /* Loop counter for coordinate dimensions */ \ + int ipix_out; /* Index into output array */ \ + int nin; /* Number of Mapping input coordinates */ \ + int nout; /* Number of Mapping output coordinates */ \ + int npix; /* Number of pixels in input region */ \ + int npix_out; /* Number of pixels in output array */ \ + int64_t mpix; /* Number of pixels for testing */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Get a pointer to a structure holding thread-specific global data values */ \ + astGET_GLOBALS(this); \ +\ +/* Obtain values for the Nin and Nout attributes of the Mapping. */ \ + nin = astGetNin( this ); \ + nout = astGetNout( this ); \ +\ +/* If OK, check that the number of input grid dimensions matches the \ + number required by the Mapping and is at least 1. Report an error \ + if necessary. */ \ + if ( astOK && ( ( ndim_in != nin ) || ( ndim_in < 1 ) ) ) { \ + astError( AST__NGDIN, "astRebin"#X"(%s): Bad number of input grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim_in ); \ + if ( ndim_in != nin ) { \ + astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ + "to specify an input position.", status, \ + astGetClass( this ), nin, ( nin == 1 ) ? "" : "s" ); \ + } \ + } \ +\ +/* If OK, also check that the number of output grid dimensions matches \ + the number required by the Mapping and is at least 1. Report an \ + error if necessary. */ \ + if ( astOK && ( ( ndim_out != nout ) || ( ndim_out < 1 ) ) ) { \ + astError( AST__NGDIN, "astRebin"#X"(%s): Bad number of output grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim_out ); \ + if ( ndim_out != nout ) { \ + astError( AST__NGDIN, "The %s given generates %s%d coordinate " \ + "value%s for each output position.", status, astGetClass( this ), \ + ( nout < ndim_out ) ? "only " : "", nout, \ + ( nout == 1 ) ? "" : "s" ); \ + } \ + } \ +\ +/* Check that the lower and upper bounds of the input grid are \ + consistent. Report an error if any pair is not. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + if ( lbnd_in[ idim ] > ubnd_in[ idim ] ) { \ + astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ + "input grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd_in[ idim ], ubnd_in[ idim ] ); \ + astError( AST__GBDIN, "Error in input dimension %d.", status, \ + idim + 1 ); \ + break; \ + } else { \ + mpix *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the input. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astRebin"#X"(%s): Supplied input array " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* Check that the positional accuracy tolerance supplied is valid and \ + report an error if necessary. */ \ + if ( astOK && ( tol < 0.0 ) ) { \ + astError( AST__PATIN, "astRebin"#X"(%s): Invalid positional " \ + "accuracy tolerance (%.*g pixel).", status, \ + astGetClass( this ), DBL_DIG, tol ); \ + astError( AST__PATIN, "This value should not be less than zero." , status); \ + } \ +\ +/* Check that the initial scale size in pixels supplied is valid and \ + report an error if necessary. */ \ + if ( astOK && ( maxpix < 0 ) ) { \ + astError( AST__SSPIN, "astRebin"#X"(%s): Invalid initial scale " \ + "size in pixels (%d).", status, astGetClass( this ), maxpix ); \ + astError( AST__SSPIN, "This value should not be less than zero." , status); \ + } \ +\ +/* Check that the lower and upper bounds of the output grid are \ + consistent. Report an error if any pair is not. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + if ( lbnd_out[ idim ] > ubnd_out[ idim ] ) { \ + astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ + "output grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd_out[ idim ], ubnd_out[ idim ] ); \ + astError( AST__GBDIN, "Error in output dimension %d.", status, \ + idim + 1 ); \ + break; \ + } else { \ + mpix *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the output. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astRebin"#X"(%s): Supplied output array " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* Similarly check the bounds of the input region. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + if ( lbnd[ idim ] > ubnd[ idim ] ) { \ + astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ + "input region (%d) exceeds corresponding upper " \ + "bound (%d).", status, astGetClass( this ), \ + lbnd[ idim ], ubnd[ idim ] ); \ +\ +/* Also check that the input region lies wholly within the input \ + grid. */ \ + } else if ( lbnd[ idim ] < lbnd_in[ idim ] ) { \ + astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ + "input region (%d) is less than corresponding " \ + "bound of input grid (%d).", status, astGetClass( this ), \ + lbnd[ idim ], lbnd_in[ idim ] ); \ + } else if ( ubnd[ idim ] > ubnd_in[ idim ] ) { \ + astError( AST__GBDIN, "astRebin"#X"(%s): Upper bound of " \ + "input region (%d) exceeds corresponding " \ + "bound of input grid (%d).", status, astGetClass( this ), \ + ubnd[ idim ], ubnd_in[ idim ] ); \ + } else { \ + mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ + } \ +\ +/* Say which dimension produced the error. */ \ + if ( !astOK ) { \ + astError( AST__GBDIN, "Error in output dimension %d.", status, \ + idim + 1 ); \ + break; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the input region. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astRebin"#X"(%s): Supplied input region " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* If OK, loop to determine how many input pixels are to be binned. */ \ + simple = NULL; \ + npix = 1; \ + npix_out = 1; \ + unsimplified_mapping = this; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + npix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ + } \ +\ +/* Loop to determine how many pixels the output array contains. */ \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + npix_out *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ + } \ +\ +/* If there are sufficient pixels to make it worthwhile, simplify the \ + Mapping supplied to improve performance. Otherwise, just clone the \ + Mapping pointer. Note we have already saved a pointer to the original \ + Mapping so that lower-level functions can use it if they need to report \ + an error. */ \ + if ( npix > 1024 ) { \ + simple = astSimplify( this ); \ + } else { \ + simple = astClone( this ); \ + } \ + } \ +\ +/* Report an error if the forward transformation of this simplified \ + Mapping is not defined. */ \ + if ( !astGetTranForward( simple ) && astOK ) { \ + astError( AST__TRNND, "astRebin"#X"(%s): An forward coordinate " \ + "transformation is not defined by the %s supplied.", status, \ + astGetClass( unsimplified_mapping ), \ + astGetClass( unsimplified_mapping ) ); \ + } \ +\ +/* Report an error if any illegal flags were supplied. */ \ + if( flags & AST__REBININIT ) { \ + badflag = "AST__REBININIT"; \ + } else if( flags & AST__REBINEND ) { \ + badflag = "AST__REBINEND"; \ + } else if( flags & AST__GENVAR ) { \ + badflag = "AST__GENVAR"; \ + } else if( flags & AST__DISVAR ) { \ + badflag = "AST__DISVAR"; \ + } else if( flags & AST__VARWGT ) { \ + badflag = "AST__VARWGT"; \ + } else if( flags & AST__NONORM ) { \ + badflag = "AST__NONORM"; \ + } else if( flags & AST__CONSERVEFLUX ) { \ + badflag = "AST__CONSERVEFLUX"; \ + } else if( flags & ~( AST__USEBAD + AST__USEVAR ) ) { \ + badflag = "unknown"; \ + } else { \ + badflag = NULL; \ + } \ + if ( badflag && astOK ) { \ + astError( AST__BADFLG, "astRebin"#X"(%s): An illegal flag (%s) " \ + "was included in the 'flags' argument.", status, \ + astGetClass( unsimplified_mapping ), badflag ); \ + } \ +\ +/* If required, allocate work array to hold the sum of the weights \ + contributing to each output pixel, and initialise it to zero. */ \ + if( wlim > 0.0 ) { \ + work = astMalloc( sizeof( double )*(size_t) npix_out ); \ + if( work ) { \ + w = work; \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++ ) *(w++) = 0.0; \ + } \ + } else { \ + work = NULL; \ + } \ +\ +/* Initialise the output arrays to hold zeros. */ \ + d = out; \ + if( out_var ) { \ + v = out_var; \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, v++ ) { \ + *d = 0; \ + *v = 0; \ + } \ + } else { \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++ ) { \ + *d = 0; \ + } \ + } \ +\ +/* Perform the rebinning. Note that we pass all gridded data, the \ + spread function and the bad pixel value by means of pointer \ + types that obscure the underlying data type. This is to avoid \ + having to replicate functions unnecessarily for each data \ + type. However, we also pass an argument that identifies the data \ + type we have obscured. */ \ + if( RebinAdaptively( simple, ndim_in, lbnd_in, ubnd_in, \ + (const void *) in, (const void *) in_var, \ + TYPE_##X, spread, \ + params, flags, tol, maxpix, \ + (const void *) &badval, \ + ndim_out, lbnd_out, ubnd_out, \ + lbnd, ubnd, npix_out, \ + (void *) out, (void *) out_var, work, \ + NULL, status ) && astOK ) { \ + astError( AST__CNFLX, "astRebin"#X"(%s): Flux conservation was " \ + "requested but could not be performed because the " \ + "forward transformation of the supplied Mapping " \ + "is too non-linear.", status, astGetClass( this ) ); \ + } \ +\ +/* If required set output pixels bad if they have a total weight less \ + than "wlim". */ \ + if( work ) { \ + w = work; \ + d = out; \ + if( out_var ) { \ + v = out_var; \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, w++, v++ ) { \ + if( fabs( *w ) < wlim ) { \ + *d = badval; \ + *v = badval; \ + } \ + } \ + } else { \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, w++ ) { \ + if( fabs( *w ) < wlim ) *d = badval; \ + } \ + } \ +\ +/* Free the work array. */ \ + work = astFree( work ); \ + } \ +\ +/* Annul the pointer to the simplified/cloned Mapping. */ \ + simple = astAnnul( simple ); \ +\ +} + +/* Expand the above macro to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_REBIN(LD,long double,0) +#endif +MAKE_REBIN(D,double,0) +MAKE_REBIN(F,float,0) +MAKE_REBIN(I,int,1) +MAKE_REBIN(B,signed char,1) +MAKE_REBIN(UB,unsigned char,1) + +/* Undefine the macro. */ +#undef MAKE_REBIN + +static int RebinAdaptively( AstMapping *this, int ndim_in, + const int *lbnd_in, const int *ubnd_in, + const void *in, const void *in_var, + DataType type, int spread, + const double *params, int flags, double tol, + int maxpix, const void *badval_ptr, + int ndim_out, const int *lbnd_out, + const int *ubnd_out, const int *lbnd, + const int *ubnd, int npix_out, + void *out, void *out_var, double *work, + int64_t *nused, int *status ){ +/* +* Name: +* RebinAdaptively + +* Purpose: +* Rebin a section of a data grid adaptively. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int RebinAdaptively( AstMapping *this, int ndim_in, +* const int *lbnd_in, const int *ubnd_in, +* const void *in, const void *in_var, +* DataType type, int spread, +* const double *params, int flags, double tol, +* int maxpix, const void *badval_ptr, +* int ndim_out, const int *lbnd_out, +* const int *ubnd_out, const int *lbnd, +* const int *ubnd, int npix_out, void *out, +* void *out_var, double *work, int64_t *nused, +* int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function rebins a specified section of a rectangular grid of +* data (with any number of dimensions) into another rectangular grid +* (with a possibly different number of dimensions). The coordinate +* transformation used to convert input pixel coordinates into positions +* in the output grid is given by the forward transformation of the +* Mapping which is supplied. Any pixel spreading scheme may be specified +* for distributing the flux of an input pixel amongst the output +* pixels. +* +* This function is very similar to RebinWithBlocking and RebinSection +* which lie below it in the calling hierarchy. However, this function +* also attempts to adapt to the Mapping supplied and to sub-divide the +* section being rebinned into smaller sections within which a linear +* approximation to the Mapping may be used. This reduces the number of +* Mapping evaluations, thereby improving efficiency particularly when +* complicated Mappings are involved. + +* Parameters: +* this +* Pointer to a Mapping, whose forward transformation may be +* used to transform the coordinates of pixels in the input +* grid into associated positions in the output grid. +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input data grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input data grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input data grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* in +* Pointer to the input array of data to be rebinned (with one +* element for each pixel in the input grid). The numerical type +* of these data should match the "type" value (below). The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and data type as the "in" array), +* which represent estimates of the statistical variance +* associated with each element of the "in" array. If this +* second array is given (along with the corresponding "out_var" +* array), then estimates of the variance of the rebinned data +* will also be returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* type +* A value taken from the "DataType" enum, which specifies the +* data type of the input and output arrays containing the +* gridded data (and variance) values. +* spread +* A value selected from a set of pre-defined macros to identify +* which pixel spread function should be used. +* params +* Pointer to an optional array of parameters that may be passed +* to the pixel spread algorithm, if required. If no parameters +* are required, a NULL pointer should be supplied. +* flags +* The bitwise OR of a set of flag values which provide additional +* control over the resampling operation. +* tol +* The maximum permitted positional error in transforming input +* pixel positions into the output grid in order to rebin +* it. This should be expressed as a displacement in pixels in +* the output grid's coordinate system. If the Mapping's forward +* transformation can be approximated by piecewise linear functions +* to this accuracy, then such functions may be used instead of the +* Mapping in order to improve performance. Otherwise, every input +* pixel position will be transformed individually using the Mapping. +* +* If linear approximation is not required, a "tol" value of +* zero may be given. This will ensure that the Mapping is used +* without any approximation. +* maxpix +* A value which specifies the largest scale size on which to +* search for non-linearities in the Mapping supplied. This +* value should be expressed as a number of pixels in the input +* grid. The function will break the input section specified +* into smaller sub-sections (if necessary), each no larger than +* "maxpix" pixels in any dimension, before it attempts to +* approximate the Mapping by a linear function over each +* sub-section. +* +* If the value given is larger than the largest dimension of +* the input section (the normal recommendation), the function +* will initially search for non-linearity on a scale determined +* by the size of the input section. This is almost always +* satisfactory. Very occasionally, however, a Mapping may +* appear linear on this scale but nevertheless have smaller +* irregularities (e.g. "holes") in it. In such cases, "maxpix" +* may be set to a suitably smaller value so as to ensure this +* non-linearity is not overlooked. Typically, a value of 50 to +* 100 pixels might be suitable and should have little effect on +* performance. +* +* If too small a value is given, however, it will have the +* effect of preventing linear approximation occurring at all +* (equivalent to setting "tol" to zero). Although this may +* degrade performance, accurate results will still be obtained. +* badval_ptr +* If the AST__USEBAD flag is set (above), this parameter is a +* pointer to a value which is used to identify bad data and/or +* variance values in the input array(s). The referenced value's +* data type must match that of the "in" (and "in_var") +* arrays. The same value will also be used to flag any output +* array elements for which rebinned values could not be +* obtained. The output arrays(s) may be flagged with this +* value whether or not the AST__USEBAD flag is set (the +* function return value indicates whether any such values have +* been produced). +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output data grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output data grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output data grid in the same way as "lbnd_in" +* and "ubnd_in" define the shape and size of the input grid +* (see above). +* lbnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the first pixel in the +* section of the input data grid which is to be rebinned. +* ubnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the last pixel in the +* section of the input data grid which is to be rebinned. +* +* Note that "lbnd" and "ubnd" define the shape and position of +* the section of the input grid which is to be rebinned. This section +* should lie wholly within the extent of the input grid (as defined +* by the "lbnd_out" and "ubnd_out" arrays). Regions of the input +* grid lying outside this section will be ignored. +* npix_out +* The number of pixels in the output array. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the rebinned data will be returned. The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the rebinned values may be returned. This array will only be +* used if the "in_var" array has been given. +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* work +* An optional pointer to a double array with the same size as +* the "out" array. The contents of this array (if supplied) are +* incremented by the accumulated weights assigned to each output pixel. +* If no accumulated weights are required, a NULL pointer should be +* given. +* nused +* An optional pointer to a int64_t which will be incremented by the +* number of input values pasted into the output array. Ignored if NULL. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if "flags" included AST__CONSERVEFLUX (i.e. +* flux conservation was requested), but the forward transformation of the +* supplied Mapping had zero determinant everywhere within the region +* being binned (no error is reported if this happens). Zero is returned +* otherwise. + +*/ + +/* Local Variables: */ + double *flbnd; /* Array holding floating point lower bounds */ + double *fubnd; /* Array holding floating point upper bounds */ + double *linear_fit; /* Pointer to array of fit coefficients */ + int *hi; /* Pointer to array of section upper bounds */ + int *lo; /* Pointer to array of section lower bounds */ + int coord_in; /* Loop counter for input coordinates */ + int dim; /* Output section dimension size */ + int dimx; /* Dimension with maximum section extent */ + int divide; /* Sub-divide the output section? */ + int i; /* Loop count */ + int isLinear; /* Is the transformation linear? */ + int mxdim; /* Largest output section dimension size */ + int need_fit; /* Do we need to perform a linear fit? */ + int npix; /* Number of pixels in output section */ + int npoint; /* Number of points for obtaining a fit */ + int nvertex; /* Number of vertices of output section */ + int result; /* Returned value */ + int res1; /* Flux conservation error in 1st section? */ + int res2; /* Flux conservation error in 2nd section? */ + int toobig; /* Section too big (must sub-divide)? */ + int toosmall; /* Section too small to sub-divide? */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + npix = 1; + mxdim = 0; + dimx = 1; + nvertex = 1; + +/* Loop through the input grid dimensions. */ + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + +/* Obtain the extent in each dimension of the input section which is + to be rebinned, and calculate the total number of pixels it contains. */ + dim = ubnd[ coord_in ] - lbnd[ coord_in ] + 1; + npix *= dim; + +/* Find the maximum dimension size of this input section and note which + dimension has this size. */ + if ( dim > mxdim ) { + mxdim = dim; + dimx = coord_in; + } + +/* Calculate how many vertices the output section has. */ + nvertex *= 2; + } + +/* Calculate how many sample points will be needed (by the astLinearApprox + function) to obtain a linear fit to the Mapping's forward transformation. */ + npoint = 1 + 4 * ndim_in + 2 * nvertex; + +/* If the number of pixels in the input section is not at least 4 + times this number, we will probably not save significant time by + attempting to obtain a linear fit, so note that the input section + is too small. */ + toosmall = ( npix < ( 4 * npoint ) ); + +/* Note if the maximum dimension of the input section exceeds the + user-supplied scale factor. */ + toobig = ( maxpix < mxdim ); + +/* Indicate we do not yet have a linear fit. */ + linear_fit = NULL; + +/* Initialise a flag indicating if we need to perform a linear fit. This + is always the case if flux conservation was requested. */ + need_fit = ( flags & AST__CONSERVEFLUX ); + +/* If the output section is too small to be worth obtaining a linear + fit, or if the accuracy tolerance is zero, we will not + sub-divide. This means that the Mapping will be used to transform + each pixel's coordinates and no linear approximation will be + used. */ + if ( toosmall || ( tol == 0.0 ) ) { + divide = 0; + +/* Otherwise, if the largest input section dimension exceeds the + scale length given, we will sub-divide. This offers the possibility + of obtaining a linear approximation to the Mapping over a reduced + range of input coordinates (which will be handled by a recursive + invocation of this function). */ + } else if ( toobig ) { + divide = 1; + +/* If neither of the above apply, we need to do a fit regardless of + whether flux conservation was requested or not. Whether we divide or + not will depend on whether the Mapping is linear or not. Assume for + the moment that the Mapping is not linear and so we will divide. */ + } else { + need_fit = 1; + divide = 1; + } + +/* If required, attempt to fit a linear approximation to the Mapping's + forward transformation over the range of coordinates covered by the + input section. We need to temporarily copy the integer bounds into + floating point arrays to use astLinearApprox. */ + if( need_fit ) { + +/* Allocate memory for floating point bounds and for the coefficient array */ + flbnd = astMalloc( sizeof( double )*(size_t) ndim_in ); + fubnd = astMalloc( sizeof( double )*(size_t) ndim_in ); + linear_fit = astMalloc( sizeof( double )* + (size_t) ( ndim_out*( ndim_in + 1 ) ) ); + if( astOK ) { + +/* Copy the bounds into these arrays, and change them so that they refer + to the lower and upper edges of the cell rather than the centre. This + is essential if one of the axes is spanned by a single cell, since + otherwise the upper and lower bounds would be identical. */ + for( i = 0; i < ndim_in; i++ ) { + flbnd[ i ] = (double) lbnd[ i ] - 0.5; + fubnd[ i ] = (double) ubnd[ i ] + 0.5; + } + +/* Get the linear approximation to the forward transformation. */ + isLinear = astLinearApprox( this, flbnd, fubnd, tol, linear_fit ); + +/* Free the coeff array if the inverse transformation is not linear. */ + if( !isLinear ) linear_fit = astFree( linear_fit ); + + } else { + linear_fit = astFree( linear_fit ); + } + +/* Free resources */ + flbnd = astFree( flbnd ); + fubnd = astFree( fubnd ); + +/* If a linear fit was obtained, we will use it and therefore do not + wish to sub-divide further. Otherwise, we sub-divide (unless the + section is too small or too big as determined earlier) in the hope + that this may result in a linear fit next time. */ + if( linear_fit ) divide = 0; + } + +/* If no sub-division is required, perform rebinning (in a + memory-efficient manner, since the section we are rebinning might + still be very large). This will use the linear fit, if obtained + above. */ + if ( astOK ) { + if ( !divide ) { + result = RebinWithBlocking( this, linear_fit, ndim_in, lbnd_in, + ubnd_in, in, in_var, type, spread, + params, flags, badval_ptr, ndim_out, + lbnd_out, ubnd_out, lbnd, ubnd, npix_out, + out, out_var, work, nused, status ); + +/* Otherwise, allocate workspace to perform the sub-division. */ + } else { + lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); + hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Initialise the bounds of a new input section to match the original + input section. */ + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + lo[ coord_in ] = lbnd[ coord_in ]; + hi[ coord_in ] = ubnd[ coord_in ]; + } + +/* Replace the upper bound of the section's largest dimension with the + mid-point of the section along this dimension, rounded downwards. */ + hi[ dimx ] = + (int) floor( 0.5 * (double) ( lbnd[ dimx ] + ubnd[ dimx ] ) ); + +/* Rebin the resulting smaller section using a recursive invocation + of this function. */ + res1 = RebinAdaptively( this, ndim_in, lbnd_in, ubnd_in, in, + in_var, type, spread, params, + flags, tol, maxpix, badval_ptr, ndim_out, + lbnd_out, ubnd_out, lo, hi, npix_out, out, + out_var, work, nused, status ); + +/* Now set up a second section which covers the remaining half of the + original input section. */ + lo[ dimx ] = hi[ dimx ] + 1; + hi[ dimx ] = ubnd[ dimx ]; + +/* If this section contains pixels, resample it in the same way, + summing the returned values. */ + if ( lo[ dimx ] <= hi[ dimx ] ) { + res2 = RebinAdaptively( this, ndim_in, lbnd_in, ubnd_in, in, + in_var, type, spread, params, + flags, tol, maxpix, badval_ptr, + ndim_out, lbnd_out, ubnd_out, + lo, hi, npix_out, out, out_var, work, + nused, status ); + } else { + res2 = 0; + } + +/* If neither section could be rebinned because of an indeterminant + mapping, return a result indicating this. */ + result = ( res1 && res2 ); + } + +/* Free the workspace. */ + lo = astFree( lo ); + hi = astFree( hi ); + } + } + +/* If coefficients for a linear fit were obtained, then free the space + they occupy. */ + if ( linear_fit ) linear_fit = astFree( linear_fit ); + +/* Retyurn a flag indicating if no part of the array could be binned + because of an indeterminate Mapping. */ + return result; +} + +static void RebinSection( AstMapping *this, const double *linear_fit, + int ndim_in, const int *lbnd_in, const int *ubnd_in, + const void *in, const void *in_var, double infac, + DataType type, int spread, const double *params, + int flags, const void *badval_ptr, int ndim_out, + const int *lbnd_out, const int *ubnd_out, + const int *lbnd, const int *ubnd, int npix_out, + void *out, void *out_var, double *work, + int64_t *nused, int *status ) { +/* +* Name: +* RebinSection + +* Purpose: +* Rebin a section of a data grid. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void RebinSection( AstMapping *this, const double *linear_fit, +* int ndim_in, const int *lbnd_in, const int *ubnd_in, +* const void *in, const void *in_var, double infac, +* DataType type, int spread, const double *params, +* int flags, const void *badval_ptr, int ndim_out, +* const int *lbnd_out, const int *ubnd_out, +* const int *lbnd, const int *ubnd, int npix_out, +* void *out, void *out_var, double *work, +* int64_t *nused, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function rebins a specified section of a rectangular grid of +* data (with any number of dimensions) into another rectangular grid +* (with a possibly different number of dimensions). The coordinate +* transformation used to convert input pixel coordinates into positions +* in the output grid is given by the forward transformation of the +* Mapping which is supplied or, alternatively, by a linear approximation +* fitted to a Mapping's forward transformation. Any pixel spreading scheme +* may be specified for distributing the flux of an input pixel amongst +* the output pixels. + +* Parameters: +* this +* Pointer to a Mapping, whose forward transformation may be +* used to transform the coordinates of pixels in the input +* grid into associated positions in the output grid. +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* linear_fit +* Pointer to an optional array of double which contains the +* coefficients of a linear fit which approximates the above +* Mapping's forward coordinate transformation. If this is +* supplied, it will be used in preference to the above Mapping +* when transforming coordinates. This may be used to enhance +* performance in cases where evaluation of the Mapping's +* forward transformation is expensive. If no linear fit is +* available, a NULL pointer should be supplied. +* +* The way in which the fit coefficients are stored in this +* array and the number of array elements are as defined by the +* astLinearApprox function. +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input data grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input data grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input data grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* in +* Pointer to the input array of data to be rebinned (with one +* element for each pixel in the input grid). The numerical type +* of these data should match the "type" value (below). The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and data type as the "in" array), +* which represent estimates of the statistical variance +* associated with each element of the "in" array. If this +* second array is given (along with the corresponding "out_var" +* array), then estimates of the variance of the rebinned data +* will also be returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* infac +* A factor by which to multiply the input data values before use. +* type +* A value taken from the "DataType" enum, which specifies the +* data type of the input and output arrays containing the +* gridded data (and variance) values. +* spread +* A value selected from a set of pre-defined macros to identify +* which pixel spread function should be used. +* params +* Pointer to an optional array of parameters that may be passed +* to the pixel spread algorithm, if required. If no parameters +* are required, a NULL pointer should be supplied. +* flags +* The bitwise OR of a set of flag values which provide additional +* control over the resampling operation. +* badval_ptr +* If the AST__USEBAD flag is set (above), this parameter is a +* pointer to a value which is used to identify bad data and/or +* variance values in the input array(s). The referenced value's +* data type must match that of the "in" (and "in_var") +* arrays. The same value will also be used to flag any output +* array elements for which rebinned values could not be +* obtained. The output arrays(s) may be flagged with this +* value whether or not the AST__USEBAD flag is set (the +* function return value indicates whether any such values have +* been produced). +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output data grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output data grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output data grid in the same way as "lbnd_in" +* and "ubnd_in" define the shape and size of the input grid +* (see above). +* lbnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the first pixel in the +* section of the input data grid which is to be rebinned. +* ubnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the last pixel in the +* section of the input data grid which is to be rebinned. +* +* Note that "lbnd" and "ubnd" define the shape and position of +* the section of the input grid which is to be rebinned. This section +* should lie wholly within the extent of the input grid (as defined +* by the "lbnd_out" and "ubnd_out" arrays). Regions of the input +* grid lying outside this section will be ignored. +* npix_out +* The number of pixels in the output array. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the rebinned data will be returned. The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the rebinned values may be returned. This array will only be +* used if the "in_var" array has been given. +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* work +* An optional pointer to a double array with the same size as +* the "out" array. The contents of this array (if supplied) are +* incremented by the accumulated weights assigned to each output pixel. +* If no accumulated weights are required, a NULL pointer should be +* given. +* nused +* An optional pointer to a int64_t which will be incremented by the +* number of input values pasted into the output array. Ignored if NULL. + +* Notes: +* - This function does not take steps to limit memory usage if the +* grids supplied are large. To resample large grids in a more +* memory-efficient way, the ResampleWithBlocking function should +* be used. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific data */ + AstPointSet *pset_in; /* Input PointSet for transformation */ + AstPointSet *pset_out; /* Output PointSet for transformation */ + const double *grad; /* Pointer to gradient matrix of linear fit */ + const double *zero; /* Pointer to zero point array of fit */ + double **ptr_in; /* Pointer to input PointSet coordinates */ + double **ptr_out; /* Pointer to output PointSet coordinates */ + double *accum; /* Pointer to array of accumulated sums */ + double x1; /* Interim x coordinate value */ + double xx1; /* Initial x coordinate value */ + double y1; /* Interim y coordinate value */ + double yy1; /* Initial y coordinate value */ + int *dim; /* Pointer to array of output pixel indices */ + int *offset; /* Pointer to array of output pixel offsets */ + int *stride; /* Pointer to array of output grid strides */ + int coord_in; /* Loop counter for input dimensions */ + int coord_out; /* Loop counter for output dimensions */ + int done; /* All pixel indices done? */ + int i1; /* Interim offset into "accum" array */ + int i2; /* Final offset into "accum" array */ + int idim; /* Loop counter for dimensions */ + int ix; /* Loop counter for output x coordinate */ + int iy; /* Loop counter for output y coordinate */ + int neighb; /* Number of neighbouring pixels */ + int npoint; /* Number of output points (pixels) */ + int off1; /* Interim pixel offset into output array */ + int off2; /* Interim pixel offset into output array */ + int off; /* Final pixel offset into output array */ + int point; /* Counter for output points (pixels ) */ + int s; /* Temporary variable for strides */ + const double *par; /* Pointer to parameter array */ + double fwhm; /* Full width half max. of gaussian */ + double lpar[ 1 ]; /* Local parameter array */ + void (* kernel)( double, const double [], int, double *, int * ); /* Kernel fn. */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to a structure holding thread-specific global data values */ + astGET_GLOBALS(this); + +/* Further initialisation. */ + pset_in = NULL; + ptr_in = NULL; + ptr_out = NULL; + pset_out = NULL; + neighb = 0; + kernel = NULL; + +/* Calculate the number of input points, as given by the product of + the input grid dimensions. */ + for ( npoint = 1, coord_in = 0; coord_in < ndim_in; coord_in++ ) { + npoint *= ubnd[ coord_in ] - lbnd[ coord_in ] + 1; + } + +/* Allocate workspace. */ + offset = astMalloc( sizeof( int ) * (size_t) npoint ); + stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Calculate the stride for each input grid dimension. */ + off = 0; + s = 1; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + stride[ coord_in ] = s; + s *= ubnd_in[ coord_in ] - lbnd_in[ coord_in ] + 1; + } + +/* A linear fit to the Mapping is available. */ +/* ========================================= */ + if ( linear_fit ) { + +/* If a linear fit to the Mapping has been provided, then obtain + pointers to the array of gradients and zero-points comprising the + fit. */ + grad = linear_fit + ndim_out; + zero = linear_fit; + +/* Create a PointSet to hold the output grid coordinates and obtain an + array of pointers to its coordinate data. */ + pset_out = astPointSet( npoint, ndim_out, "", status ); + ptr_out = astGetPoints( pset_out ); + if ( astOK ) { + +/* Initialise the count of input points. */ + point = 0; + +/* Handle the 1-dimensional case optimally. */ +/* ---------------------------------------- */ + if ( ( ndim_in == 1 ) && ( ndim_out == 1 ) ) { + +/* Loop through the pixels of the input grid and transform their x + coordinates into the output grid's coordinate system using the + linear fit supplied. Store the results in the PointSet created + above. */ + off = lbnd[ 0 ] - lbnd_in[ 0 ]; + xx1 = zero[ 0 ] + grad[ 0 ] * (double) lbnd[ 0 ]; + + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_out[ 0 ][ point ] = xx1; + xx1 += grad[ 0 ]; + offset[ point++ ] = off++; + } + +/* Handle the 2-dimensional case optimally. */ +/* ---------------------------------------- */ + } else if ( ( ndim_in == 2 ) && ( ndim_out == 2 ) ) { + +/* Loop through the range of y coordinates in the input grid and + calculate interim values of the output coordinates using the linear + fit supplied. */ + x1 = zero[ 0 ] + grad[ 1 ] * (double) ( lbnd[ 1 ] - 1 ); + y1 = zero[ 1 ] + grad[ 3 ] * (double) ( lbnd[ 1 ] - 1 ); + off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) - lbnd_in[ 0 ]; + for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { + x1 += grad[ 1 ]; + y1 += grad[ 3 ]; + +/* Also calculate an interim pixel offset into the input array. */ + off1 += stride[ 1 ]; + +/* Now loop through the range of input x coordinates and calculate + the final values of the input coordinates, storing the results in + the PointSet created above. */ + xx1 = x1 + grad[ 0 ] * (double) lbnd[ 0 ]; + yy1 = y1 + grad[ 2 ] * (double) lbnd[ 0 ]; + off = off1 + lbnd[ 0 ]; + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_out[ 0 ][ point ] = xx1; + xx1 += grad[ 0 ]; + ptr_out[ 1 ][ point ] = yy1; + yy1 += grad[ 2 ]; + +/* Also calculate final pixel offsets into the input array. */ + offset[ point++ ] = off++; + } + } + +/* Handle other numbers of dimensions. */ +/* ----------------------------------- */ + } else { + +/* Allocate workspace. */ + accum = astMalloc( sizeof( double ) * + (size_t) ( ndim_in * ndim_out ) ); + dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Initialise an array of pixel indices for the input grid which refer to the + first pixel which we will rebin. Also calculate the offset of this pixel + within the input array. */ + off = 0; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + dim[ coord_in ] = lbnd[ coord_in ]; + off += stride[ coord_in ] * + ( dim[ coord_in ] - lbnd_in[ coord_in ] ); + } + +/* To calculate each output grid coordinate we must perform a matrix + multiply on the input grid coordinates (using the gradient matrix) + and then add the zero points. However, since we will usually only + be altering one input coordinate at a time (the least + significant), we can avoid the full matrix multiply by accumulating + partial sums for the most significant input coordinates and only + altering those sums which need to change each time. The zero points + never change, so we first fill the "most significant" end of the + "accum" array with these. */ + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + accum[ ( coord_out + 1 ) * ndim_in - 1 ] = + zero[ coord_out ]; + } + coord_in = ndim_in - 1; + +/* Now loop to process each input pixel. */ + for ( done = 0; !done; point++ ) { + +/* To generate the output coordinate that corresponds to the current + input pixel, we work down from the most significant dimension + whose index has changed since the previous pixel we considered + (given by "coord_in"). For each affected dimension, we accumulate + in "accum" the matrix sum (including the zero point) for that + dimension and all higher input dimensions. We must accumulate a + separate set of sums for each output coordinate we wish to + produce. (Note that for the first pixel we process, all dimensions + are considered "changed", so we start by initialising the whole + "accum" array.) */ + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { +/* + ptr_out[ coord_out ][ point ] = zero[ coord_out ]; + for ( idim = 0; idim < ndim_in; idim++ ) { + ptr_out[ coord_out ][ point ] += + grad[ idim + coord_out*ndim_in ] * + dim[ idim ]; + } +*/ + + i1 = coord_out * ndim_in; + for ( idim = coord_in; idim >= 1; idim-- ) { + i2 = i1 + idim; + accum[ i2 - 1 ] = accum[ i2 ] + + dim[ idim ] * grad[ i2 ]; + } + +/* The output coordinate for each dimension is given by the accumulated + sum for input dimension zero (giving the sum over all input + dimensions). We do not store this in the "accum" array, but assign + the result directly to the coordinate array of the PointSet created + earlier. */ + ptr_out[ coord_out ][ point ] = accum[ i1 ] + + dim[ 0 ] * grad[ i1 ]; + } + +/* Store the offset of the current pixel in the input array. */ + offset[ point ] = off; + +/* Now update the array of pixel indices to refer to the next input pixel. */ + coord_in = 0; + do { + +/* The least significant index which currently has less than its maximum + value is incremented by one. The offset into the input array is updated + accordingly. */ + if ( dim[ coord_in ] < ubnd[ coord_in ] ) { + dim[ coord_in ]++; + off += stride[ coord_in ]; + break; + +/* Any less significant indices which have reached their maximum value + are returned to their minimum value and the input pixel offset is + decremented appropriately. */ + } else { + dim[ coord_in ] = lbnd[ coord_in ]; + off -= stride[ coord_in ] * + ( ubnd[ coord_in ] - lbnd[ coord_in ] ); + +/* All the output pixels have been processed once the most significant + pixel index has been returned to its minimum value. */ + done = ( ++coord_in == ndim_in ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + accum = astFree( accum ); + dim = astFree( dim ); + } + } + +/* No linear fit to the Mapping is available. */ +/* ========================================== */ + } else { + +/* Create a PointSet to hold the coordinates of the input pixels and + obtain a pointer to its coordinate data. */ + pset_in = astPointSet( npoint, ndim_in, "", status ); + ptr_in = astGetPoints( pset_in ); + if ( astOK ) { + +/* Initialise the count of input points. */ + point = 0; + +/* Handle the 1-dimensional case optimally. */ +/* ---------------------------------------- */ + if ( ndim_in == 1 && ndim_out == 1 ) { + +/* Loop through the required range of input x coordinates, assigning + the coordinate values to the PointSet created above. Also store a + pixel offset into the input array. */ + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_in[ 0 ][ point ] = (double) ix; + offset[ point++ ] = ix - lbnd_in[ 0 ]; + } + +/* Handle the 2-dimensional case optimally. */ +/* ---------------------------------------- */ + } else if ( ndim_in == 2 && ndim_out == 2) { + +/* Loop through the required range of input y coordinates, + calculating an interim pixel offset into the input array. */ + off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) + - lbnd_in[ 0 ]; + for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { + off1 += stride[ 1 ]; + +/* Loop through the required range of input x coordinates, assigning + the coordinate values to the PointSet created above. Also store a + final pixel offset into the input array. */ + off2 = off1 + lbnd[ 0 ]; + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_in[ 0 ][ point ] = (double) ix; + ptr_in[ 1 ][ point ] = (double) iy; + offset[ point++ ] = off2++; + } + } + +/* Handle other numbers of dimensions. */ +/* ----------------------------------- */ + } else { + +/* Allocate workspace. */ + dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Initialise an array of pixel indices for the input grid which + refer to the first pixel to be rebinned. Also calculate the offset + of this pixel within the input array. */ + off = 0; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + dim[ coord_in ] = lbnd[ coord_in ]; + off += stride[ coord_in ] * + ( dim[ coord_in ] - lbnd_in[ coord_in ] ); + } + +/* Loop to generate the coordinates of each input pixel. */ + for ( done = 0; !done; point++ ) { + +/* Copy each pixel's coordinates into the PointSet created above. */ + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + ptr_in[ coord_in ][ point ] = + (double) dim[ coord_in ]; + } + +/* Store the offset of the pixel in the input array. */ + offset[ point ] = off; + +/* Now update the array of pixel indices to refer to the next input + pixel. */ + coord_in = 0; + do { + +/* The least significant index which currently has less than its + maximum value is incremented by one. The offset into the input + array is updated accordingly. */ + if ( dim[ coord_in ] < ubnd[ coord_in ] ) { + dim[ coord_in ]++; + off += stride[ coord_in ]; + break; + +/* Any less significant indices which have reached their maximum value + are returned to their minimum value and the input pixel offset is + decremented appropriately. */ + } else { + dim[ coord_in ] = lbnd[ coord_in ]; + off -= stride[ coord_in ] * + ( ubnd[ coord_in ] - lbnd[ coord_in ] ); + +/* All the input pixels have been processed once the most significant + pixel index has been returned to its minimum value. */ + done = ( ++coord_in == ndim_in ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + dim = astFree( dim ); + } + +/* When all the input pixel coordinates have been generated, use the + Mapping's forward transformation to generate the output coordinates + from them. Obtain an array of pointers to the resulting coordinate + data. */ + pset_out = astTransform( this, pset_in, 1, NULL ); + ptr_out = astGetPoints( pset_out ); + } + +/* Annul the PointSet containing the input coordinates. */ + pset_in = astAnnul( pset_in ); + } + } + + +/* Rebin the input grid. */ +/* ------------------------ */ + if( astOK ) { + +/* Identify the pixel spreading scheme to be used. */ +/* Nearest pixel. */ +/* -------------- */ + switch ( spread ) { + case AST__NEAREST: + +/* Define a macro to use a "case" statement to invoke the + nearest-pixel spreading function appropriate to a given data + type. */ +#define CASE_NEAREST(X,Xtype) \ + case ( TYPE_##X ): \ + SpreadNearest##X( ndim_out, lbnd_out, ubnd_out, \ + (Xtype *) in, (Xtype *) in_var, \ + infac, npoint, offset, \ + (const double *const *) ptr_out, \ + flags, *( (Xtype *) badval_ptr ), \ + npix_out, (Xtype *) out, \ + (Xtype *) out_var, work, nused, status ); \ + break; + +/* Use the above macro to invoke the appropriate function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_NEAREST(LD,long double) +#endif + CASE_NEAREST(D,double) + CASE_NEAREST(F,float) + CASE_NEAREST(I,int) + CASE_NEAREST(B,signed char) + CASE_NEAREST(UB,unsigned char) + + case ( TYPE_L ): break; + case ( TYPE_K ): break; + case ( TYPE_S ): break; + case ( TYPE_UL ): break; + case ( TYPE_UI ): break; + case ( TYPE_UK ): break; + case ( TYPE_US ): break; + } + break; + +/* Undefine the macro. */ +#undef CASE_NEAREST + +/* Linear spreading. */ +/* ----------------- */ +/* Note this is also the default if zero is given. */ + case AST__LINEAR: + case 0: + +/* Define a macro to use a "case" statement to invoke the linear + spreading function appropriate to a given data type. */ +#define CASE_LINEAR(X,Xtype) \ + case ( TYPE_##X ): \ + SpreadLinear##X( ndim_out, lbnd_out, ubnd_out,\ + (Xtype *) in, (Xtype *) in_var, \ + infac, npoint, offset, \ + (const double *const *) ptr_out, \ + flags, *( (Xtype *) badval_ptr ), \ + npix_out, (Xtype *) out, \ + (Xtype *) out_var, work, nused, status ); \ + break; + +/* Use the above macro to invoke the appropriate function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_LINEAR(LD,long double) +#endif + CASE_LINEAR(D,double) + CASE_LINEAR(F,float) + CASE_LINEAR(I,int) + CASE_LINEAR(B,signed char) + CASE_LINEAR(UB,unsigned char) + + case ( TYPE_L ): break; + case ( TYPE_K ): break; + case ( TYPE_S ): break; + case ( TYPE_UL ): break; + case ( TYPE_UI ): break; + case ( TYPE_UK ): break; + case ( TYPE_US ): break; + } + break; + +/* Undefine the macro. */ +#undef CASE_LINEAR + +/* Spreading using a 1-d kernel. */ +/* ----------------------------- */ + case AST__SINC: + case AST__SINCCOS: + case AST__SINCGAUSS: + case AST__GAUSS: + case AST__SINCSINC: + case AST__SOMB: + case AST__SOMBCOS: + +/* Obtain a pointer to the appropriate 1-d kernel function (either + internal or user-defined) and set up any parameters it may + require. */ + par = NULL; + switch ( spread ) { + +/* sinc(pi*x) */ +/* ---------- */ +/* Assign the kernel function. */ + case AST__SINC: + kernel = Sinc; + +/* Calculate the number of neighbouring pixels to use. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) { + neighb = 2; + } else { + neighb = MaxI( 1, neighb, status ); + } + break; + +/* somb(pi*x) */ +/* ---------- */ +/* Assign the kernel function. */ + case AST__SOMB: + kernel = Somb; + +/* Calculate the number of neighbouring pixels to use. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) { + neighb = 2; + } else { + neighb = MaxI( 1, neighb, status ); + } + break; + +/* sinc(pi*x)*cos(k*pi*x) */ +/* ---------------------- */ +/* Assign the kernel function. */ + case AST__SINCCOS: + kernel = SincCos; + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, the number will be calculated automatically below. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = INT_MAX; + +/* Calculate the maximum number of neighbouring pixels required by the + width of the kernel, and use this value if preferable. */ + neighb = MinI( neighb, + (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); + break; + +/* sinc(pi*x)*exp(-k*x*x) */ +/* ---------------------- */ +/* Assign the kernel function. */ + case AST__SINCGAUSS: + kernel = SincGauss; + +/* Constrain the full width half maximum of the gaussian factor. */ + fwhm = MaxD( 0.1, params[ 1 ], status ); + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, use the number of neighbouring pixels required by the width + of the kernel (out to where the gaussian term falls to 1% of its + peak value). */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / + lpar[ 0 ] ) ); + break; + +/* exp(-k*x*x) */ +/* ----------- */ +/* Assign the kernel function. */ + case AST__GAUSS: + kernel = Gauss; + +/* Constrain the full width half maximum of the gaussian. */ + fwhm = MaxD( 0.1, params[ 1 ], status ); + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, use the number of neighbouring pixels required by the width + of the kernel (out to where the gaussian term falls to 1% of its + peak value). */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / + lpar[ 0 ] ) ); + break; + +/* somb(pi*x)*cos(k*pi*x) */ +/* ---------------------- */ +/* Assign the kernel function. */ + case AST__SOMBCOS: + kernel = SombCos; + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, the number will be calculated automatically below. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = INT_MAX; + +/* Calculate the maximum number of neighbouring pixels required by the + width of the kernel, and use this value if preferable. */ + neighb = MinI( neighb, + (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); + break; + +/* sinc(pi*x)*sinc(k*pi*x) */ +/* ----------------------- */ +/* Assign the kernel function. */ + case AST__SINCSINC: + kernel = SincSinc; + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, the number will be calculated automatically below. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = INT_MAX; + +/* Calculate the maximum number of neighbouring pixels required by the + width of the kernel, and use this value if preferable. */ + neighb = MinI( neighb, + (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); + break; + } + +/* Define a macro to use a "case" statement to invoke the 1-d kernel + interpolation function appropriate to a given data type, passing it + the pointer to the kernel function obtained above. */ +#define CASE_KERNEL1(X,Xtype) \ + case ( TYPE_##X ): \ + SpreadKernel1##X( this, ndim_out, lbnd_out, ubnd_out, \ + (Xtype *) in, (Xtype *) in_var, \ + infac, npoint, offset, \ + (const double *const *) ptr_out, \ + kernel, neighb, par, flags, \ + *( (Xtype *) badval_ptr ), \ + npix_out, (Xtype *) out, \ + (Xtype *) out_var, work, nused, \ + status ); \ + break; + +/* Use the above macro to invoke the appropriate function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_KERNEL1(LD,long double) +#endif + CASE_KERNEL1(D,double) + CASE_KERNEL1(F,float) + CASE_KERNEL1(I,int) + CASE_KERNEL1(B,signed char) + CASE_KERNEL1(UB,unsigned char) + + case ( TYPE_L ): break; + case ( TYPE_K ): break; + case ( TYPE_S ): break; + case ( TYPE_UL ): break; + case ( TYPE_UI ): break; + case ( TYPE_UK ): break; + case ( TYPE_US ): break; + } + break; + +/* Undefine the macro. */ +#undef CASE_KERNEL1 + +/* Error: invalid pixel spreading scheme specified. */ +/* ------------------------------------------------ */ + default: + +/* Define a macro to report an error message appropriate to a given + data type. */ +#define CASE_ERROR(X) \ + case TYPE_##X: \ + astError( AST__SISIN, "astRebin"#X"(%s): Invalid " \ + "pixel spreading scheme (%d) specified.", status, \ + astGetClass( unsimplified_mapping ), spread ); \ + break; + +/* Use the above macro to report an appropriate error message. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_ERROR(LD) +#endif + CASE_ERROR(D) + CASE_ERROR(F) + CASE_ERROR(I) + CASE_ERROR(B) + CASE_ERROR(UB) + + case ( TYPE_L ): break; + case ( TYPE_K ): break; + case ( TYPE_S ): break; + case ( TYPE_UL ): break; + case ( TYPE_UI ): break; + case ( TYPE_UK ): break; + case ( TYPE_US ): break; + } + break; + +/* Undefine the macro. */ +#undef CASE_ERROR + } + } + +/* Annul the PointSet used to hold output coordinates. */ + pset_out = astAnnul( pset_out ); + +/* Free the workspace. */ + offset = astFree( offset ); + stride = astFree( stride ); +} + +/* +*++ +* Name: +c astRebinSeq +f AST_REBINSEQ + +* Purpose: +* Rebin a region of a sequence of data grids. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astRebinSeq( AstMapping *this, double wlim, int ndim_in, +c const int lbnd_in[], const int ubnd_in[], +c const in[], const in_var[], +c int spread, const double params[], int flags, +c double tol, int maxpix, badval, +c int ndim_out, const int lbnd_out[], +c const int ubnd_out[], const int lbnd[], +c const int ubnd[], out[], out_var[], +c double weights[], int64_t *nused ); +f CALL AST_REBINSEQ( THIS, WLIM, NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, +f SPREAD, PARAMS, FLAGS, TOL, MAXPIX, BADVAL, +f NDIM_OUT, LBND_OUT, UBND_OUT, LBND, UBND, OUT, +f OUT_VAR, WEIGHTS, NUSED, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +* This set of +c functions is identical to astRebin +f routines is identical to AST_REBIN +* except that the rebinned input data is added into the supplied +* output arrays, rather than simply over-writing the contents of the +* output arrays. Thus, by calling this +c function +f routine +* repeatedly, a sequence of input arrays can be rebinned and accumulated +* into a single output array, effectively forming a mosaic of the +* input data arrays. +* +* In addition, the weights associated with each output pixel are +* returned. The weight of an output pixel indicates the number of input +* pixels which have been accumulated in that output pixel. If the entire +* value of an input pixel is assigned to a single output pixel, then the +* weight of that output pixel is incremented by one. If some fraction of +* the value of an input pixel is assigned to an output pixel, then the +* weight of that output pixel is incremented by the fraction used. +* +* The start of a new sequence is indicated by specifying the +* AST__REBININIT flag via the +c "flags" parameter. +f FLAGS argument. +* This causes the supplied arrays to be filled with zeros before the +* rebinned input data is added into them. Subsequenct invocations +* within the same sequence should omit the AST__REBININIT flag. +* +* The last call in a sequence is indicated by specifying the +* AST__REBINEND flag. Depending on which flags are supplied, this may +* cause the output data and variance arrays to be normalised before +* being returned. This normalisation consists of dividing the data +* array by the weights array, and can eliminate artifacts which may be +* introduced into the rebinned data as a consequence of aliasing +* between the input and output grids. This results in each output +* pixel value being the weighted mean of the input pixel values that +* fall in the neighbourhood of the output pixel (rather like +c astResample). +f AST_RESAMPLE). +* Optionally, these normalised +* values can then be multiplied by a scaling factor to ensure that the +* total data sum in any small area is unchanged. This scaling factor +* is equivalent to the number of input pixel values that fall into each +* output pixel. In addition to +* normalisation of the output data values, any output variances are +* also appropriately normalised, and any output data values with +* weight less than +c "wlim" are set to "badval". +f WLIM are set to BADVAL. +* +* Output variances can be generated in two ways; by rebinning the supplied +* input variances with appropriate weights, or by finding the spread of +* input data values contributing to each output pixel (see the AST__GENVAR +* and AST__USEVAR flags). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to a Mapping, whose forward transformation will be +* used to transform the coordinates of pixels in the input +* grid into the coordinate system of the output grid. +* +* The number of input coordinates used by this Mapping (as +* given by its Nin attribute) should match the number of input +c grid dimensions given by the value of "ndim_in" +f grid dimensions given by the value of NDIM_IN +* below. Similarly, the number of output coordinates (Nout +* attribute) should match the number of output grid dimensions +c given by "ndim_out". +f given by NDIM_OUT. +c If "in" is NULL, the Mapping will not be used, but a valid +c Mapping must still be supplied. +c wlim +f WLIM = DOUBLE PRECISION (Given) +* This value is only used if the AST__REBINEND flag is specified +* via the +c "flags" parameter. +f FLAGS argument. +* It gives the required number of input pixel values which must +* contribute to an output pixel (i.e. the output pixel weight) in +* order for the output pixel value to be considered valid. If the sum +* of the input pixel weights contributing to an output pixel is less +* than the supplied +c "wlim" +f WLIM +* value, then the output pixel value is returned set to the +* supplied bad value. If the supplied value is less than 1.0E-10 +* then 1.0E-10 is used instead. +c ndim_in +f NDIM_IN = INTEGER (Given) +* The number of dimensions in the input grid. This should be at +* least one. +c Not used if "in" is NULL. +c lbnd_in +f LBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +c Not used if "in" is NULL. +c ubnd_in +f UBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +c Note that "lbnd_in" and "ubnd_in" together define the shape +f Note that LBND_IN and UBND_IN together define the shape +* and size of the input grid, its extent along a particular +c (j'th) dimension being ubnd_in[j]-lbnd_in[j]+1 (assuming the +c index "j" to be zero-based). They also define +f (J'th) dimension being UBND_IN(J)-LBND_IN(J)+1. They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre. +c Not used if "in" is NULL. +c in +f IN( * ) = (Given) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* input grid, containing the input data to be rebined. The +* numerical type of this array should match the 1- or +* 2-character type code appended to the function name (e.g. if +c you are using astRebinSeqF, the type of each array element +c should be "float"). +f you are using AST_REBINSEQR, the type of each array element +f should be REAL). +* +* The storage order of data within this array should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c If a NULL pointer is supplied for "in", then no data is added to +c the output arrays, but any initialisation or normalisation +c requested by "flags" is still performed. +c in_var +f IN_VAR( * ) = (Given) +* An optional +c pointer to a +* second array with the same size and type as the +c "in" +f IN +* array. If given, this should contain a set of non-negative values +* which represent estimates of the statistical variance associated +* with each element of the +c "in" +f IN +* array. +* If neither the AST__USEVAR nor the AST__VARWGT flag is set, no +* input variance estimates are required and this +f array +c pointer +* will not be used. +f A dummy (e.g. one-element) array +c A NULL pointer +* may then be supplied. +c spread +f SPREAD = INTEGER (Given) +c This parameter specifies the scheme to be used for dividing +f This argument specifies the scheme to be used for dividing +* each input data value up amongst the corresponding output pixels. +* It may be used to select +* from a set of pre-defined schemes by supplying one of the +* values described in the "Pixel Spreading Schemes" +* section in the description of the +c astRebin functions. +f AST_REBIN routines. +* If a value of zero is supplied, then the default linear spreading +* scheme is used (equivalent to supplying the value AST__LINEAR). +c Not used if "in" is NULL. +c params +f PARAMS( * ) = DOUBLE PRECISION (Given) +c An optional pointer to an array of double which should contain +f An optional array which should contain +* any additional parameter values required by the pixel +* spreading scheme. If such parameters are required, this +* will be noted in the "Pixel Spreading Schemes" section in the +* description of the +c astRebin functions. +f AST_REBIN routines. +* +c If no additional parameters are required, this array is not +c used and a NULL pointer may be given. +f If no additional parameters are required, this array is not +f used. A dummy (e.g. one-element) array may then be supplied. +c Not used if "in" is NULL. +c flags +f FLAGS = INTEGER (Given) +c The bitwise OR of a set of flag values which may be used to +f The sum of a set of flag values which may be used to +* provide additional control over the rebinning operation. See +* the "Control Flags" section below for a description of the +* options available. If no flag values are to be set, a value +* of zero should be given. +c tol +f TOL = DOUBLE PRECISION (Given) +* The maximum tolerable geometrical distortion which may be +* introduced as a result of approximating non-linear Mappings +* by a set of piece-wise linear transformations. This should be +* expressed as a displacement in pixels in the output grid's +* coordinate system. +* +* If piece-wise linear approximation is not required, a value +* of zero may be given. This will ensure that the Mapping is +* used without any approximation, but may increase execution +* time. +* +* If the value is too high, discontinuities between the linear +* approximations used in adjacent panel will be higher, and may +* cause the edges of the panel to be visible when viewing the output +* image at high contrast. If this is a problem, reduce the +* tolerance value used. +c Not used if "in" is NULL. +c maxpix +f MAXPIX = INTEGER (Given) +* A value which specifies an initial scale size (in pixels) for +* the adaptive algorithm which approximates non-linear Mappings +* with piece-wise linear transformations. Normally, this should +* be a large value (larger than any dimension of the region of +* the input grid being used). In this case, a first attempt to +* approximate the Mapping by a linear transformation will be +* made over the entire input region. +* +* If a smaller value is used, the input region will first be +c divided into sub-regions whose size does not exceed "maxpix" +f divided into sub-regions whose size does not exceed MAXPIX +* pixels in any dimension. Only at this point will attempts at +* approximation commence. +* +* This value may occasionally be useful in preventing false +* convergence of the adaptive algorithm in cases where the +* Mapping appears approximately linear on large scales, but has +* irregularities (e.g. holes) on smaller scales. A value of, +* say, 50 to 100 pixels can also be employed as a safeguard in +* general-purpose software, since the effect on performance is +* minimal. +* +* If too small a value is given, it will have the effect of +* inhibiting linear approximation altogether (equivalent to +c setting "tol" to zero). Although this may degrade +f setting TOL to zero). Although this may degrade +* performance, accurate results will still be obtained. +c Not used if "in" is NULL. +c badval +f BADVAL = (Given) +* This argument should have the same type as the elements of +c the "in" array. It specifies the value used to flag missing +f the IN array. It specifies the value used to flag missing +* data (bad pixels) in the input and output arrays. +* +c If the AST__USEBAD flag is set via the "flags" parameter, +f If the AST__USEBAD flag is set via the FLAGS argument, +c then this value is used to test for bad pixels in the "in" +c (and "in_var") array(s). +f then this value is used to test for bad pixels in the IN +f (and IN_VAR) array(s). +* +* In all cases, this value is also used to flag any output +c elements in the "out" (and "out_var") array(s) for which +f elements in the OUT (and OUT_VAR) array(s) for which +* rebined values could not be obtained (see the "Propagation +* of Missing Data" section below for details of the +* circumstances under which this may occur). +c ndim_out +f NDIM_OUT = INTEGER (Given) +* The number of dimensions in the output grid. This should be +* at least one. It need not necessarily be equal to the number +* of dimensions in the input grid. +c lbnd_out +f LBND_OUT( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the output grid along each dimension. +c ubnd_out +f UBND_OUT( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the output grid along each dimension. +* +c Note that "lbnd_out" and "ubnd_out" together define the +f Note that LBND_OUT and UBND_OUT together define the +* shape, size and coordinate system of the output grid in the +c same way as "lbnd_in" and "ubnd_in" define the shape, size +f same way as LBND_IN and UBND_IN define the shape, size +* and coordinate system of the input grid. +c lbnd +f LBND( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the first pixel in the region +* of the input grid which is to be included in the rebined output +* array. +c Not used if "in" is NULL. +c ubnd +f UBND( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the last pixel in the region of +* the input grid which is to be included in the rebined output +* array. +* +c Note that "lbnd" and "ubnd" together define the shape and +f Note that LBND and UBND together define the shape and +* position of a (hyper-)rectangular region of the input grid +* which is to be included in the rebined output array. This region +* should lie wholly within the extent of the input grid (as +c defined by the "lbnd_in" and "ubnd_in" arrays). Regions of +f defined by the LBND_IN and UBND_IN arrays). Regions of +* the input grid lying outside this region will not be used. +c Not used if "in" is NULL. +c out +f OUT( * ) = (Given and Returned) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* output grid. The rebined data values will be added into the +* original contents of this array. The numerical type of this array +* should match that of the +c "in" array, and the data storage order should be such +f IN array, and the data storage order should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c out_var +f OUT_VAR( * ) = (Given and Returned) +* A +c pointer to an +* array with the same type and size as the +c "out" +f OUT +* array. This +c pointer +f array +* will only be used if the AST__USEVAR or AST__GENVAR flag is set +f via the FLAGS argument, +f via the "flags" parameter, +* in which case variance estimates for the rebined data values will +* be added into the array. If neither the AST__USEVAR flag nor the +* AST__GENVAR flag is set, no output variance estimates will be +* calculated and this +c pointer +f array +* will not be used. A +c NULL pointer +f dummy (e.g. one-element) array +* may then be supplied. +c weights +f WEIGHTS( * ) = DOUBLE PRECISION (Given and Returned) +c Pointer to an array of double, +f An array +* with one or two elements for each pixel in the output grid, +* depending on whether or not the AST__GENVAR flag has been supplied +* via the +c "flags" parameter. +f FLAGS parameter. +* If AST__GENVAR has not been specified then the array should have +* one element for each output pixel, and it will be used to +* accumulate the weight associated with each output pixel. +* If AST__GENVAR has been specified then the array should have +* two elements for each output pixel. The first half of the array +* is again used to accumulate the weight associated with each output +* pixel, and the second half is used to accumulate the square of +* the weights. In each half, the data storage order should be such that +* the index of the first grid dimension varies most rapidly and that of +* the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c nused +f NUSED = INTEGER*8 (Given and Returned) +c A pointer to an int64_t containing the +f The +* number of input data values that have been added into the output +* array so far. The supplied value is incremented on exit by the +* number of input values used. The value is initially set to zero +* if the AST__REBININIT flag is set in +c "flags". +f FLAGS. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Data Type Codes: +* To select the appropriate rebinning function, you should +c replace in the generic function name astRebinSeq with a +f replace in the generic function name AST_REBINSEQ with a +* 1- or 2-character data type code, so as to match the numerical +* type of the data you are processing, as follows: +c - D: double +c - F: float +c - I: int +c - B: byte (signed char) +c - UB: unsigned byte (unsigned char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - B: BYTE (treated as signed) +f - UB: BYTE (treated as unsigned) +* +c For example, astRebinSeqD would be used to process "double" +c data, while astRebinSeqI would be used to process "int" +c data, etc. +f For example, AST_REBIND would be used to process DOUBLE +f PRECISION data, while AST_REBINI would be used to process +f integer data (stored in an INTEGER array), etc. +* +* Note that, unlike +c astResample, the astRebinSeq +f AST_RESAMPLE, the AST_REBINSEQ +* set of functions does not yet support unsigned integer data types +* or integers of different sizes. + +* Control Flags: +c The following flags are defined in the "ast.h" header file and +f The following flags are defined in the AST_PAR include file and +* may be used to provide additional control over the rebinning +* process. Having selected a set of flags, you should supply the +c bitwise OR of their values via the "flags" parameter: +f sum of their values via the FLAGS argument: +* +* - AST__REBININIT: Used to mark the first call in a sequence. It indicates +* that the supplied +c "out", "out_var" and "weights" +f OUT, OUT_VAR and WEIGHTS +* arrays should be filled with zeros (thus over-writing any supplied +* values) before adding the rebinned input data into them. This flag +* should be used when rebinning the first input array in a sequence. +* - AST__REBINEND: Used to mark the last call in a sequence. It causes +* each value in the +c "out" and "out_var" +f OUT and OUT_VAR +* arrays to be divided by a normalisation factor before being +* returned. The normalisation factor for each output data value is just +* the corresponding value from the weights array. The normalisation +* factor for each output variance value is the square of the data value +* normalisation factor (see also AST__CONSERVEFLUX). It also causes +* output data values to be set bad if the corresponding weight is less +* than the value supplied for +c parameter "wlim". +f argument WLIM. +* It also causes any temporary values stored in the output variance array +* (see flag AST__GENVAR below) to be converted into usable variance values. +* Note, this flag is ignored if the AST__NONORM flag is set. +* - AST__USEBAD: Indicates that there may be bad pixels in the +* input array(s) which must be recognised by comparing with the +c value given for "badval" and propagated to the output array(s). +f value given for BADVAL and propagated to the output array(s). +* If this flag is not set, all input values are treated literally +c and the "badval" value is only used for flagging output array +f and the BADVAL value is only used for flagging output array +* values. +* - AST__USEVAR: Indicates that output variance estimates should be +* created by rebinning the supplied input variance estimates. An +* error will be reported if both this flag and the AST__GENVAR flag +* are supplied. +* - AST__GENVAR: Indicates that output variance estimates should be +* created based on the spread of input data values contributing to each +* output pixel. An error will be reported if both this flag and the +* AST__USEVAR flag are supplied. If the AST__GENVAR flag is specified, +* the supplied output variance array is first used as a work array to +* accumulate the temporary values needed to generate the output +* variances. When the sequence ends (as indicated by the +* AST__REBINEND flag), the contents of the output variance array are +* converted into the required variance estimates. If the generation of +* such output variances is required, this flag should be used on every +* invocation of this +c function +f routine +* within a sequence, and any supplied input variances will have no effect +* on the output variances (although input variances will still be used +* to weight the input data if the AST__VARWGT flag is also supplied). +* The statistical meaning of these output varianes is determined by +* the presence or absence of the AST__DISVAR flag (see below). +* - AST__DISVAR: This flag is ignored unless the AST__GENVAR flag +* has also been specified. It determines the statistical meaning of +* the generated output variances. If AST__DISVAR is not specified, +* generated variances represent variances on the output mean values. If +* AST__DISVAR is specified, the generated variances represent the variance +* of the distribution from which the input values were taken. Each output +* variance created with AST__DISVAR will be larger than that created +* without AST__DISVAR by a factor equal to the number of input samples +* that contribute to the output sample. +* - AST__VARWGT: Indicates that the input data should be weighted by +* the reciprocal of the input variances. Otherwise, all input data are +* given equal weight. If this flag is specified, the calculation of the +* output variances (if any) is modified to take account of the +* varying weights assigned to the input data values. +* - AST__NONORM: If the simple unnormalised sum of all input data falling +* in each output pixel is required, then this flag should be set on +* each call in the sequence and the AST__REBINEND should not be used +* on the last call. In this case +c NULL pointers can be supplied for "weights" and "nused". +f WEIGHTS and NUSED are ignored. +* This flag cannot be used with the AST__CONSERVEFLUX, AST__GENVAR +* or AST__VARWGT flag. +* - AST__CONSERVEFLUX: Indicates that the normalized output pixel values +* generated by the AST__REBINEND flag should be scaled in such a way as +* to preserve the total data value in a feature on the sky. Without this +* flag, each normalised output pixel value represents a weighted mean +* of the input data values around the corresponding input position. +f (i.e. AST_REBINSEQ behaves similarly to AST_RESAMPLE). This +f (i.e. AST_REBINSEQ behaves similarly to AST_RESAMPLE). This +* is appropriate if the input data represents the spatial density of +* some quantity (e.g. surface brightness in Janskys per square +* arc-second) because the output pixel values will have the same +* normalisation and units as the input pixel values. However, if the +* input data values represent flux (or some other physical quantity) +* per pixel, then the AST__CONSERVEFLUX flag could be of use. It causes +* each output pixel value to be scaled by the ratio of the output pixel +* size to the input pixel size. +* +* This flag can only be used if the Mapping is successfully approximated +* by one or more linear transformations. Thus an error will be reported +* if it used when the +c "tol" parameter +f TOL argument +* is set to zero (which stops the use of linear approximations), or +* if the Mapping is too non-linear to be approximated by a piece-wise +* linear transformation. The ratio of output to input pixel size is +* evaluated once for each panel of the piece-wise linear approximation to +* the Mapping, and is assumed to be constant for all output pixels in the +* panel. The scaling factors for adjacent panels will in general +* differ slightly, and so the joints between panels may be visible when +* viewing the output image at high contrast. If this is a problem, +* reduce the value of the +c "tol" parameter +f TOL argument +* until the difference between adjacent panels is sufficiently small +* to be insignificant. +* +* This flag should normally be supplied on each invocation of +c astRebinSeq +f AST_REBINSEQ +* within a given sequence. +* +* Note, this flag cannot be used in conjunction with the AST__NOSCALE +* flag (an error will be reported if both flags are specified). + +* Propagation of Missing Data: +* Instances of missing data (bad pixels) in the output grid are +c identified by occurrences of the "badval" value in the "out" +f identified by occurrences of the BADVAL value in the OUT +* array. These are only produced if the AST__REBINEND flag is +* specified and a pixel has zero weight. +* +* An input pixel is considered bad (and is consequently ignored) if +* its +c data value is equal to "badval" and the AST__USEBAD flag is +c set via the "flags" parameter. +f data value is equal to BADVAL and the AST__USEBAD flag is +f set via the FLAGS argument. +* +* In addition, associated output variance estimates (if +c calculated) may be declared bad and flagged with the "badval" +c value in the "out_var" array for similar reasons. +f calculated) may be declared bad and flagged with the BADVAL +f value in the OUT_VAR array for similar reasons. + +*-- +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_REBINSEQ(X,Xtype,IntType) \ +static void RebinSeq##X( AstMapping *this, double wlim, int ndim_in, \ + const int lbnd_in[], const int ubnd_in[], \ + const Xtype in[], const Xtype in_var[], \ + int spread, const double params[], int flags, \ + double tol, int maxpix, Xtype badval, \ + int ndim_out, const int lbnd_out[], \ + const int ubnd_out[], const int lbnd[], \ + const int ubnd[], Xtype out[], Xtype out_var[], \ + double weights[], int64_t *nused, int *status ) { \ +\ +/* Local Variables: */ \ + AstMapping *simple; /* Pointer to simplified Mapping */ \ + Xtype *d; /* Pointer to next output data value */ \ + Xtype *v; /* Pointer to next output variance value */ \ + astDECLARE_GLOBALS /* Thread-specific data */ \ + double *w; /* Pointer to next weight value */ \ + double mwpip; /* Mean weight per input pixel */ \ + double neff; /* Effective number of contributing input pixels */ \ + double sw; /* Sum of weights at output pixel */ \ + double wgt; /* Output pixel weight */ \ + int i; /* Loop counter for output pixels */ \ + int idim; /* Loop counter for coordinate dimensions */ \ + int ipix_out; /* Index into output array */ \ + int nin; /* Number of Mapping input coordinates */ \ + int nout; /* Number of Mapping output coordinates */ \ + int npix; /* Number of pixels in input region */ \ + int npix_out; /* Number of pixels in output array */ \ + int64_t mpix; /* Number of pixels for testing */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Get a pointer to a structure holding thread-specific global data values */ \ + astGET_GLOBALS(this); \ +\ +/* Loop to determine how many pixels the output array contains. */ \ + npix_out = 1; \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + npix_out *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ + } \ +\ +/* Obtain values for the Nin and Nout attributes of the Mapping. */ \ + nin = astGetNin( this ); \ + nout = astGetNout( this ); \ +\ +/* If OK, also check that the number of output grid dimensions matches \ + the number required by the Mapping and is at least 1. Report an \ + error if necessary. */ \ + if ( astOK && ( ( ndim_out != nout ) || ( ndim_out < 1 ) ) ) { \ + astError( AST__NGDIN, "astRebinSeq"#X"(%s): Bad number of output grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim_out ); \ + if ( ndim_out != nout ) { \ + astError( AST__NGDIN, "The %s given generates %s%d coordinate " \ + "value%s for each output position.", status, astGetClass( this ), \ + ( nout < ndim_out ) ? "only " : "", nout, \ + ( nout == 1 ) ? "" : "s" ); \ + } \ + } \ +\ +/* If no input data was supplied, jump to the normalisation section. */ \ + simple = NULL; \ + if( in ) { \ +\ +/* If OK, check that the number of input grid dimensions matches the \ + number required by the Mapping and is at least 1. Report an error \ + if necessary. */ \ + if ( astOK && ( ( ndim_in != nin ) || ( ndim_in < 1 ) ) ) { \ + astError( AST__NGDIN, "astRebinSeq"#X"(%s): Bad number of input grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim_in ); \ + if ( ndim_in != nin ) { \ + astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ + "to specify an input position.", status, \ + astGetClass( this ), nin, ( nin == 1 ) ? "" : "s" ); \ + } \ + } \ +\ +/* Check that the lower and upper bounds of the input grid are \ + consistent. Report an error if any pair is not. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + if ( lbnd_in[ idim ] > ubnd_in[ idim ] ) { \ + astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ + "input grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd_in[ idim ], ubnd_in[ idim ] ); \ + astError( AST__GBDIN, "Error in input dimension %d.", status, \ + idim + 1 ); \ + break; \ + } else { \ + mpix *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the input. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astRebinSeq"#X"(%s): Supplied input array " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* Ensure any supplied "in_var" pointer is ignored if no input variances are \ + needed. */ \ + if( !( flags & AST__USEVAR ) && !( flags & AST__VARWGT ) ) { \ + in_var = NULL; \ + } \ +\ +/* Ensure any supplied "out_var" pointer is ignored if no output variances \ + being created. */ \ + if( !( flags & AST__USEVAR ) && !( flags & AST__GENVAR ) ) { \ + out_var = NULL; \ + } \ +\ +/* Check that the positional accuracy tolerance supplied is valid and \ + report an error if necessary. */ \ + if ( astOK && ( tol < 0.0 ) ) { \ + astError( AST__PATIN, "astRebinSeq"#X"(%s): Invalid positional " \ + "accuracy tolerance (%.*g pixel).", status, \ + astGetClass( this ), DBL_DIG, tol ); \ + astError( AST__PATIN, "This value should not be less than zero." , status); \ + } \ +\ +/* Check that the initial scale size in pixels supplied is valid and \ + report an error if necessary. */ \ + if ( astOK && ( maxpix < 0 ) ) { \ + astError( AST__SSPIN, "astRebinSeq"#X"(%s): Invalid initial scale " \ + "size in pixels (%d).", status, astGetClass( this ), maxpix ); \ + astError( AST__SSPIN, "This value should not be less than zero." , status); \ + } \ +\ +/* Check that the lower and upper bounds of the output grid are \ + consistent. Report an error if any pair is not. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + if ( lbnd_out[ idim ] > ubnd_out[ idim ] ) { \ + astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ + "output grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd_out[ idim ], ubnd_out[ idim ] ); \ + astError( AST__GBDIN, "Error in output dimension %d.", status, \ + idim + 1 ); \ + break; \ + } else { \ + mpix *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the output. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astRebinSeq"#X"(%s): Supplied output array " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* Similarly check the bounds of the input region. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + if ( lbnd[ idim ] > ubnd[ idim ] ) { \ + astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ + "input region (%d) exceeds corresponding upper " \ + "bound (%d).", status, astGetClass( this ), \ + lbnd[ idim ], ubnd[ idim ] ); \ +\ +/* Also check that the input region lies wholly within the input \ + grid. */ \ + } else if ( lbnd[ idim ] < lbnd_in[ idim ] ) { \ + astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ + "input region (%d) is less than corresponding " \ + "bound of input grid (%d).", status, astGetClass( this ), \ + lbnd[ idim ], lbnd_in[ idim ] ); \ + } else if ( ubnd[ idim ] > ubnd_in[ idim ] ) { \ + astError( AST__GBDIN, "astRebinSeq"#X"(%s): Upper bound of " \ + "input region (%d) exceeds corresponding " \ + "bound of input grid (%d).", status, astGetClass( this ), \ + ubnd[ idim ], ubnd_in[ idim ] ); \ + } else { \ + mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ + } \ +\ +/* Say which dimension produced the error. */ \ + if ( !astOK ) { \ + astError( AST__GBDIN, "Error in output dimension %d.", status, \ + idim + 1 ); \ + break; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the input region. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astRebinSeq"#X"(%s): Supplied input region " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* Check that only one of AST__USEVAR and ASR__GENVAR has been supplied. */ \ + if( ( flags & AST__USEVAR ) && ( flags & AST__GENVAR ) ) { \ + if( astOK ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ + "AST__GENVAR and AST__USEVAR have been specified " \ + "together (programming error).", status, astGetClass( this ) ); \ + } \ + } \ +\ +/* If AST__USEVAR or AST_VARWGT has been specified, check we have an \ + input variance array. */ \ + if( !in_var && astOK ) { \ + if( ( flags & AST__USEVAR ) ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__USEVAR flag " \ + "was specified but no input variance array was supplied " \ + "(programming error).", status, astGetClass( this ) ); \ + } else if( ( flags & AST__VARWGT ) ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__VARWGT flag " \ + "was specified but no input variance array was supplied " \ + "(programming error).", status, astGetClass( this ) ); \ + } \ + } \ +\ +/* If AST__USEVAR or AST_GENVAR has been specified, check we have an \ + output variance array. */ \ + if( !out_var && astOK ) { \ + if( ( flags & AST__USEVAR ) ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__USEVAR flag " \ + "was specified but no output variance array was supplied " \ + "(programming error).", status, astGetClass( this ) ); \ + } else if( ( flags & AST__GENVAR ) ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__GENVAR flag " \ + "was specified but no output variance array was supplied " \ + "(programming error).", status, astGetClass( this ) ); \ + } \ + } \ +\ +/* If the AST__NONORM flag has been supplied, check no incompatible flags have \ + been specified. */ \ + if( flags & AST__NONORM ) { \ + if( ( flags & AST__GENVAR ) && astOK ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ + "AST__GENVAR and AST__NONORM have been specified " \ + "together (programming error).", status, astGetClass( this ) ); \ + } else if( ( flags & AST__VARWGT ) && astOK ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ + "AST__VARWGT and AST__NONORM have been specified " \ + "together (programming error).", status, astGetClass( this ) ); \ + } else if( ( flags & AST__CONSERVEFLUX ) && astOK ) { \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ + "AST__CONSERVEFLUX and AST__NONORM have been specified " \ + "together (programming error).", status, astGetClass( this ) ); \ + } \ +\ +/* If the AST__NONORM flag has not been supplied, check that a weights array \ + and nused pointer have been supplied. */ \ + } else if( !weights ){ \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): No weights array " \ + "supplied (programming error).", status, \ + astGetClass( this ) ); \ + } else if( !nused ){ \ + astError( AST__BDPAR, "astRebinSeq"#X"(%s): No 'nused' pointer " \ + "supplied (programming error).", status, \ + astGetClass( this ) ); \ + } \ +\ +/* If OK, loop to determine how many input pixels are to be binned. */ \ + npix = 1; \ + unsimplified_mapping = this; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + npix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ + } \ +\ +/* If there are sufficient pixels to make it worthwhile, simplify the \ + Mapping supplied to improve performance. Otherwise, just clone the \ + Mapping pointer. Note we have already saved a pointer to the original \ + Mapping so that lower-level functions can use it if they need to report \ + an error. */ \ + if ( npix > 1024 ) { \ + simple = astSimplify( this ); \ + } else { \ + simple = astClone( this ); \ + } \ + } \ +\ +/* Report an error if the forward transformation of this simplified \ + Mapping is not defined. */ \ + if ( !astGetTranForward( simple ) && astOK ) { \ + astError( AST__TRNND, "astRebinSeq"#X"(%s): An forward coordinate " \ + "transformation is not defined by the %s supplied.", status, \ + astGetClass( unsimplified_mapping ), \ + astGetClass( unsimplified_mapping ) ); \ + } \ +\ +/* If required, initialise the output arrays to hold zeros. */ \ + if( flags & AST__REBININIT ) { \ + d = out; \ + if( out_var ) { \ + v = out_var; \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, v++ ) { \ + *d = 0; \ + *v = 0; \ + } \ + } else { \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++ ) { \ + *d = 0; \ + } \ + } \ + if( weights ) { \ + w = weights; \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, w++ ) { \ + *w = 0; \ + } \ + if( flags & AST__GENVAR ) { \ + for( ipix_out = 0; ipix_out < npix_out; ipix_out++, w++ ) *w = 0; \ + } \ + } \ + if( nused ) *nused = 0; \ + } \ +\ +/* Paste the input values into the supplied output arrays. */ \ + if( RebinAdaptively( simple, ndim_in, lbnd_in, ubnd_in, \ + (const void *) in, (const void *) in_var, \ + TYPE_##X, spread, params, flags, \ + tol, maxpix, (const void *) &badval, \ + ndim_out, lbnd_out, ubnd_out, lbnd, \ + ubnd, npix_out, (void *) out, \ + (void *) out_var, weights, nused, status ) ) { \ + astError( AST__CNFLX, "astRebinSeq"#X"(%s): Flux conservation was " \ + "requested but could not be performed because the " \ + "forward transformation of the supplied Mapping " \ + "is too non-linear.", status, astGetClass( this ) ); \ + } \ +\ +/* Annul the pointer to the simplified/cloned Mapping. */ \ + simple = astAnnul( simple ); \ +\ + } \ +\ +/* If required, finalise the sequence. */ \ + if( ( flags & AST__REBINEND ) && !( flags & AST__NONORM ) && \ + weights && nused ) { \ +\ +/* Ensure "wlim" is not zero. */ \ + if( wlim < 1.0E-10 ) wlim = 1.0E-10; \ +\ +/* If it will be needed, find the average weight per input pixel. */ \ + if( !( flags & AST__GENVAR ) && *nused > 0 ) { \ + sw = 0.0; \ + for( i = 0; i < npix_out; i++ ) { \ + sw += weights[ i ]; \ + } \ + mwpip = sw/( *nused ); \ + } else { \ + mwpip = AST__BAD; \ + } \ +\ +/* Normalise each output pixel. */ \ + for( i = 0; i < npix_out; i++ ) { \ +\ +/* Find the effective number of input samples that contribute to the \ + output sample. To do this properly requires the sum of the squared \ + weights in each output pixel, but this is only available if AST__GENVAR \ + flag is in use. In order to avoid changing the API for astRebinSeq, we \ + honour this long-standing restriction, and use an approximation if \ + AST__GENVAR is not in use. */ \ + wgt = weights[ i ]; \ + if( flags & AST__GENVAR ) { \ + if( wgt > 0.0 && weights[ i + npix_out ] > 0 ) { \ + neff = (wgt*wgt)/weights[ i + npix_out ]; \ + } else { \ + neff = 0.0; \ + } \ +\ +/* If the sum of the squared weights is not available, compare the weight \ + for this output pixel with the mean weight per input pixel. */ \ + } else if( mwpip != AST__BAD ){ \ + neff = wgt/mwpip; \ +\ + } else if( astOK ) { \ + astError( AST__BADIN, "astRebinSeq"#X"(%s): The overlap " \ + "between the %d-d input array and the %d-d output " \ + "array contains no pixels with good data %svalues.", \ + status, astGetClass( this ), nin, nout, \ + in_var ? "and variance " : "" ); \ + } \ +\ +/* Assign bad values to unused output pixels. */ \ + if( neff < wlim || neff == 0.0 ) { \ + out[ i ] = badval; \ + if( out_var ) out_var[ i ] = badval; \ +\ +/* Otherwise, normalise the returned data value. No need to check "wgt" \ + since it must be larger than zero since neff is larger than wlim. */ \ + } else { \ + out[ i ] /= wgt; \ +\ +/* Normalise the returned variance: propagated from input variances... */ \ + if( out_var ) { \ + if( flags & AST__USEVAR ) { \ + out_var[ i ] /= wgt*wgt; \ +\ +/* Normalise the returned variance: from spread of input values... */ \ + } else if( flags & AST__GENVAR && neff > 1.0 ) { \ + out_var[ i ] /= wgt; \ + out_var[ i ] -= out[ i ]*out[ i ]; \ + if( out_var[ i ] < 0.0 ) out_var[ i ] = 0.0; \ +\ +/* If output variances are estimates of the variance of the distribution \ + from which the input values were sampled... */ \ + if( flags & AST__DISVAR ) { \ + out_var[ i ] *= neff/( neff - 1.0 ); \ +\ +/* If output variances are estimates of the error on the mean data value... */ \ + } else { \ + out_var[ i ] *= 1.0/( neff - 1.0 ); \ + } \ +\ + } else { \ + out_var[ i ] = badval; \ + } \ + } \ + } \ + } \ + } \ +\ +} + +/* Expand the above macro to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_REBINSEQ(LD,long double,0) +#endif +MAKE_REBINSEQ(D,double,0) +MAKE_REBINSEQ(F,float,0) +MAKE_REBINSEQ(I,int,1) +MAKE_REBINSEQ(B,signed char,1) +MAKE_REBINSEQ(UB,unsigned char,1) + +/* Undefine the macro. */ +#undef MAKE_REBINSEQ + +static int RebinWithBlocking( AstMapping *this, const double *linear_fit, + int ndim_in, const int *lbnd_in, + const int *ubnd_in, const void *in, + const void *in_var, DataType type, + int spread, const double *params, int flags, + const void *badval_ptr, int ndim_out, + const int *lbnd_out, const int *ubnd_out, + const int *lbnd, const int *ubnd, int npix_out, + void *out, void *out_var, double *work, + int64_t *nused, int *status ) { +/* +* Name: +* RebinWithBlocking + +* Purpose: +* Rebin a section of a data grid in a memory-efficient way. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int RebinWithBlocking( AstMapping *this, const double *linear_fit, +* int ndim_in, const int *lbnd_in, +* const int *ubnd_in, const void *in, +* const void *in_var, DataType type, +* int spread, const double *params, int flags, +* const void *badval_ptr, int ndim_out, +* const int *lbnd_out, const int *ubnd_out, +* const int *lbnd, const int *ubnd, int npix_out, +* void *out, void *out_var, double *work, +* int64_t *nused, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function rebins a specified section of a rectangular grid of +* data (with any number of dimensions) into another rectangular grid +* (with a possibly different number of dimensions). The coordinate +* transformation used to convert input pixel coordinates into positions +* in the output grid is given by the forward transformation of the +* Mapping which is supplied. Any pixel spreading scheme may be specified +* for distributing the flux of an input pixel amongst the output +* pixels. +* +* This function is very similar to RebinSection, except that in +* order to limit memory usage and to ensure locality of reference, +* it divides the input grid up into "blocks" which have a limited +* extent along each input dimension. Each block, which will not +* contain more than a pre-determined maximum number of pixels, is +* then passed to RebinSection for resampling. + +* Parameters: +* this +* Pointer to a Mapping, whose forward transformation may be +* used to transform the coordinates of pixels in the input +* grid into associated positions in the output grid. +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* linear_fit +* Pointer to an optional array of double which contains the +* coefficients of a linear fit which approximates the above +* Mapping's forward coordinate transformation. If this is +* supplied, it will be used in preference to the above Mapping +* when transforming coordinates. This may be used to enhance +* performance in cases where evaluation of the Mapping's +* forward transformation is expensive. If no linear fit is +* available, a NULL pointer should be supplied. +* +* The way in which the fit coefficients are stored in this +* array and the number of array elements are as defined by the +* astLinearApprox function. +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input data grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input data grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input data grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* in +* Pointer to the input array of data to be rebinned (with one +* element for each pixel in the input grid). The numerical type +* of these data should match the "type" value (below). The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and data type as the "in" array), +* which represent estimates of the statistical variance +* associated with each element of the "in" array. If this +* second array is given (along with the corresponding "out_var" +* array), then estimates of the variance of the rebinned data +* will also be returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* type +* A value taken from the "DataType" enum, which specifies the +* data type of the input and output arrays containing the +* gridded data (and variance) values. +* spread +* A value selected from a set of pre-defined macros to identify +* which pixel spread function should be used. +* params +* Pointer to an optional array of parameters that may be passed +* to the pixel spread algorithm, if required. If no parameters +* are required, a NULL pointer should be supplied. +* flags +* The bitwise OR of a set of flag values which provide additional +* control over the resampling operation. +* badval_ptr +* If the AST__USEBAD flag is set (above), this parameter is a +* pointer to a value which is used to identify bad data and/or +* variance values in the input array(s). The referenced value's +* data type must match that of the "in" (and "in_var") +* arrays. The same value will also be used to flag any output +* array elements for which rebinned values could not be +* obtained. The output arrays(s) may be flagged with this +* value whether or not the AST__USEBAD flag is set (the +* function return value indicates whether any such values have +* been produced). +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output data grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output data grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output data grid in the same way as "lbnd_in" +* and "ubnd_in" define the shape and size of the input grid +* (see above). +* lbnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the first pixel in the +* section of the input data grid which is to be rebinned. +* ubnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the last pixel in the +* section of the input data grid which is to be rebinned. +* +* Note that "lbnd" and "ubnd" define the shape and position of +* the section of the input grid which is to be rebinned. This section +* should lie wholly within the extent of the input grid (as defined +* by the "lbnd_out" and "ubnd_out" arrays). Regions of the input +* grid lying outside this section will be ignored. +* npix_out +* The number of pixels in the output array. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the rebinned data will be returned. The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the rebinned values may be returned. This array will only be +* used if the "in_var" array has been given. +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* work +* An optional pointer to a double array with the same size as +* the "out" array. The contents of this array (if supplied) are +* incremented by the accumulated weights assigned to each output pixel. +* If no accumulated weights are required, a NULL pointer should be +* given. +* nused +* An optional pointer to a int64_t which will be incremented by the +* number of input values pasted into the output array. Ignored if NULL. + +* Returned Value: +* A non-zero value is returned if "flags" included AST__CONSERVEFLUX (i.e. +* flux conservation was requested), but the supplied linear fit to the +* forward transformation of the Mapping had zero determinant (no error +* is reported if this happens). Zero is returned otherwise. + +*/ + +/* Local Constants: */ + const int mxpix = 2 * 1024; /* Maximum number of pixels in a block (this + relatively small number seems to give best + performance) */ + +/* Local Variables: */ + double factor; /* Flux conservation factor */ + int *dim_block; /* Pointer to array of block dimensions */ + int *lbnd_block; /* Pointer to block lower bound array */ + int *ubnd_block; /* Pointer to block upper bound array */ + int dim; /* Dimension size */ + int done; /* All blocks rebinned? */ + int hilim; /* Upper limit on maximum block dimension */ + int idim; /* Loop counter for dimensions */ + int lolim; /* Lower limit on maximum block dimension */ + int mxdim_block; /* Maximum block dimension */ + int npix; /* Number of pixels in block */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate workspace. */ + lbnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); + ubnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); + dim_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Find the optimum block size. */ +/* ---------------------------- */ +/* We first need to find the maximum extent which a block of input + pixels may have in each dimension. We determine this by taking the + input grid extent in each dimension and then limiting the maximum + dimension size until the resulting number of pixels is sufficiently + small. This approach allows the block shape to approximate (or + match) the input grid shape when appropriate. */ + +/* First loop to calculate the total number of input pixels and the + maximum input dimension size. */ + npix = 1; + mxdim_block = 0; + for ( idim = 0; idim < ndim_in; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + npix *= dim; + if ( mxdim_block < dim ) mxdim_block = dim; + } + +/* If the number of input pixels is too large for a single block, we + perform iterations to determine the optimum upper limit on a + block's dimension size. Initialise the limits on this result. */ + if ( npix > mxpix ) { + lolim = 1; + hilim = mxdim_block; + +/* Loop to perform a binary chop, searching for the best result until + the lower and upper limits on the result converge to adjacent + values. */ + while ( ( hilim - lolim ) > 1 ) { + +/* Form a new estimate from the mid-point of the previous limits. */ + mxdim_block = ( hilim + lolim ) / 2; + +/* See how many pixels a block contains if its maximum dimension is + limited to this new value. */ + for ( npix = 1, idim = 0; idim < ndim_in; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + npix *= ( dim < mxdim_block ) ? dim : mxdim_block; + } + +/* Update the appropriate limit, according to whether the number of + pixels is too large or too small. */ + *( ( npix <= mxpix ) ? &lolim : &hilim ) = mxdim_block; + } + +/* When iterations have converged, obtain the maximum limit on the + dimension size of a block which results in no more than the maximum + allowed number of pixels per block. However, ensure that all block + dimensions are at least 2. */ + mxdim_block = lolim; + } + if ( mxdim_block < 2 ) mxdim_block = 2; + +/* Calculate the block dimensions by applying this limit to the output + grid dimensions. */ + for ( idim = 0; idim < ndim_in; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + dim_block[ idim ] = ( dim < mxdim_block ) ? dim : mxdim_block; + +/* Also initialise the lower and upper bounds of the first block of + output grid pixels to be rebinned, ensuring that this does not + extend outside the grid itself. */ + lbnd_block[ idim ] = lbnd[ idim ]; + ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + } + +/* Determine the flux conservation constant if needed. */ +/* --------------------------------------------------- */ + factor = 1.0; + if( flags & AST__CONSERVEFLUX ) { + if( linear_fit ) { + factor = MatrixDet( ndim_out, ndim_in, linear_fit + ndim_out, + status ); + if( factor != 0.0 ) { + factor = 1.0/factor; + } else { + result = 1; + } + } else { + result = 1; + } + } + +/* Rebin each block of input pixels. */ +/* --------------------------------- */ +/* Loop to generate the extent of each block of input pixels and to + rebin them. */ + done = result; + while ( !done && astOK ) { + +/* Rebin the current block, accumulating the sum of bad pixels produced. */ + RebinSection( this, linear_fit, ndim_in, lbnd_in, ubnd_in, in, + in_var, factor, type, spread, params, flags, badval_ptr, + ndim_out, lbnd_out, ubnd_out, lbnd_block, ubnd_block, + npix_out, out, out_var, work, nused, status ); + +/* Update the block extent to identify the next block of input pixels. */ + idim = 0; + do { + +/* We find the least significant dimension where the upper bound of + the block has not yet reached the upper bound of the region of the + input grid which we are rebinning. The block's position is then + incremented by one block extent along this dimension, checking that + the resulting extent does not go outside the region being rebinned. */ + if ( ubnd_block[ idim ] < ubnd[ idim ] ) { + lbnd_block[ idim ] = MinI( lbnd_block[ idim ] + + dim_block[ idim ], ubnd[ idim ], status ); + ubnd_block[ idim ] = MinI( lbnd_block[ idim ] + + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + break; + +/* If any less significant dimensions are found where the upper bound + of the block has reached its maximum value, we reset the block to + its lowest position. */ + } else { + lbnd_block[ idim ] = lbnd[ idim ]; + ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + +/* All the blocks have been processed once the position along the most + significant dimension has been reset. */ + done = ( ++idim == ndim_in ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + lbnd_block = astFree( lbnd_block ); + ubnd_block = astFree( ubnd_block ); + dim_block = astFree( dim_block ); + +/* Return a flag indicating if there was an error conserving flux. */ + return result; +} + +static AstMapping *RemoveRegions( AstMapping *this, int *status ) { +/* +*++ +* Name: +c astRemoveRegions +f AST_REMOVEREGIONS + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Public function. + +* Synopsis: +c #include "mapping.h" +c AstMapping *astRemoveRegions( AstMapping *this ) +f RESULT = AST_REMOVEREGIONS( THIS, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +* This function searches the suppliedMapping (which may be a +* compound Mapping such as a CmpMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel CmpMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the original Mapping. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astRemoveRegions() +f AST_REMOVEREGIONS = INTEGER +* A new pointer to the (possibly modified) Mapping. + +* Applicability: +* CmpFrame +* If the supplied Mapping is a CmpFrame, any component Frames that +* are instances of the Region class are replaced by the equivalent +* Frame. +* FrameSet +* If the supplied Mapping is a FrameSet, the returned Mapping +* will be a copy of the supplied FrameSet in which Regions have +* been removed from all the inter-Frame Mappings, and any Frames +* which are instances of the Region class are repalced by the +* equivalent Frame. +* Mapping +* This function applies to all Mappings. +* Region +* If the supplied Mapping is a Region, the returned Mapping will +* be the equivalent Frame. + +* Notes: +* - This function can safely be applied even to Mappings which +* contain no Regions. If no Regions are found, it +c behaves exactly like astClone and returns a pointer to the +f behaves exactly like AST_CLONE and returns a pointer to the +* original Mapping. +* - The Mapping returned by this function may not be independent +* of the original (even if some Regions were removed), and +* modifying it may therefore result in indirect modification of +* the original. If a completely independent result is required, a +c copy should be made using astCopy. +f copy should be made using AST_COPY. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* This base iplementation just returns a clone of the supplied Mapping + pointer. Sub-classes should override it as necessary. */ + return astClone( this ); +} + +static void ReportPoints( AstMapping *this, int forward, + AstPointSet *in_points, AstPointSet *out_points, int *status ) { +/* +*+ +* Name: +* astReportPoints + +* Purpose: +* Report the effect of transforming a set of points using a Mapping. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* void astReportPoints( AstMapping *this, int forward, +* AstPointSet *in_points, AstPointSet *out_points ) + +* Class Membership: +* Mapping method. + +* Description: +* This function reports the coordinates of a set of points before +* and after being transformed by a Mapping, by writing them to +* standard output. + +* Parameters: +* this +* Pointer to the Mapping. +* forward +* A non-zero value indicates that the Mapping's forward +* coordinate transformation has been applied, while a zero +* value indicates the inverse transformation. +* in_points +* Pointer to a PointSet which is associated with the +* coordinates of a set of points before the Mapping was +* applied. +* out_points +* Pointer to a PointSet which is associated with the +* coordinates of the same set of points after the Mapping has +* been applied. + +* Notes: +* - This method is provided as a development and debugging aid to +* be invoked when coordinates are transformed by public Mapping +* methods and under control of the "Report" Mapping attribute. +* - Derived clases may over-ride this method in order to change +* the way in which coordinates are formatted, etc. +*- +*/ + +/* Local Variables: */ + double **ptr_in; /* Pointer to array of input data pointers */ + double **ptr_out; /* Pointer to array of output data pointers */ + int coord; /* Loop counter for coordinates */ + int ncoord_in; /* Number of input coordinates per point */ + int ncoord_out; /* Number of output coordinates per point */ + int npoint; /* Number of points to report */ + int npoint_in; /* Number of input points */ + int npoint_out; /* Number of output points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the numbers of points and coordinates associated with each + PointSet. */ + npoint_in = astGetNpoint( in_points ); + npoint_out = astGetNpoint( out_points ); + ncoord_in = astGetNcoord( in_points ); + ncoord_out = astGetNcoord( out_points ); + +/* Obtain the pointers that give access to the coordinate data + associated with each PointSet. */ + ptr_in = astGetPoints( in_points ); + ptr_out = astGetPoints( out_points ); + +/* In the event that both PointSets don't contain equal numbers of + points (this shouldn't actually happen), simply use the minimum + number. */ + npoint = ( npoint_in < npoint_out ) ? npoint_in : npoint_out; + +/* Loop to report the effect of the Mapping on each point in turn. */ + for ( point = 0; point < npoint; point++ ) { + +/* Report the input coordinates (in parentheses and separated by + commas). Replace coordinate values of AST__BAD with the string + "" to indicate missing values. */ + printf( "(" ); + for ( coord = 0; coord < ncoord_in; coord++ ) { + if ( ptr_in[ coord ][ point ] == AST__BAD ) { + printf( "%s", coord ? ", " : "" ); + } else { + printf( "%s%.*g", coord ? ", " : "", + DBL_DIG, ptr_in[ coord ][ point ] ); + } + } + +/* Similarly report the output coordinates. */ + printf( ") --> (" ); + for ( coord = 0; coord < ncoord_out; coord++ ) { + if ( ptr_out[ coord ][ point ] == AST__BAD ) { + printf( "%s", coord ? ", " : "" ); + } else { + printf( "%s%.*g", coord ? ", " : "", + DBL_DIG, ptr_out[ coord ][ point ] ); + } + } + printf( ")\n" ); + } +} + +/* +*++ +* Name: +c astResample +f AST_RESAMPLE + +* Purpose: +* Resample a region of a data grid. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c int astResample( AstMapping *this, int ndim_in, +c const int lbnd_in[], const int ubnd_in[], +c const in[], const in_var[], +c int interp, void (* finterp)( void ), +c const double params[], int flags, +c double tol, int maxpix, +c badval, int ndim_out, +c const int lbnd_out[], const int ubnd_out[], +c const int lbnd[], const int ubnd[], +c out[], out_var[] ); +f RESULT = AST_RESAMPLE( THIS, NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, +f INTERP, FINTERP, PARAMS, FLAGS, +f TOL, MAXPIX, BADVAL, +f NDIM_OUT, LBND_OUT, UBND_OUT, +f LBND, UBND, OUT, OUT_VAR, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +* This is a set of functions for resampling gridded data (e.g. an +* image) under the control of a geometrical transformation, which +* is specified by a Mapping. The functions operate on a pair of +* data grids (input and output), each of which may have any number +* of dimensions. Resampling may be restricted to a specified +* region of the output grid. An associated grid of error estimates +* associated with the input data may also be supplied (in the form +* of variance values), so as to produce error estimates for the +* resampled output data. Propagation of missing data (bad pixels) +* is supported. +* +* You should use a resampling function which matches the numerical +* type of the data you are processing by replacing in +c the generic function name astResample by an appropriate 1- or +f the generic function name AST_RESAMPLE by an appropriate 1- or +* 2-character type code. For example, if you are resampling data +c with type "float", you should use the function astResampleF (see +f with type REAL, you should use the function AST_RESAMPLER (see +* the "Data Type Codes" section below for the codes appropriate to +* other numerical types). +* +* Resampling of the grid of input data is performed by +* transforming the coordinates of the centre of each output grid +* element (or pixel) into the coordinate system of the input grid. +* Since the resulting coordinates will not, in general, coincide +* with the centre of an input pixel, sub-pixel interpolation is +* performed between the neighbouring input pixels. This produces a +* resampled value which is then assigned to the output pixel. A +* choice of sub-pixel interpolation schemes is provided, but you +* may also implement your own. +* +* This algorithm samples the input data value, it does not integrate +* it. Thus total data value in the input image will not, in general, +* be conserved. However, an option is provided (see the "Control Flags" +* section below) which can produce approximate flux conservation by +* scaling the output values using the ratio of the output pixel size +* to the input pixel size. However, if accurate flux conservation is +* important to you, consder using the +c astRebin or astRebinSeq family of functions +f AST_REBIN or AST_REBINSEQ family of routines +* instead. +* +* Output pixel coordinates are transformed into the coordinate +* system of the input grid using the inverse transformation of the +* Mapping which is supplied. This means that geometrical features +* in the input data are subjected to the Mapping's forward +* transformation as they are transferred from the input to the +* output grid (although the Mapping's forward transformation is +* not explicitly used). +* +* In practice, transforming the coordinates of every pixel of a +* large data grid can be time-consuming, especially if the Mapping +* involves complicated functions, such as sky projections. To +* improve performance, it is therefore possible to approximate +* non-linear Mappings by a set of linear transformations which are +* applied piece-wise to separate sub-regions of the data. This +* approximation process is applied automatically by an adaptive +* algorithm, under control of an accuracy criterion which +* expresses the maximum tolerable geometrical distortion which may +* be introduced, as a fraction of a pixel. +* +* This algorithm first attempts to approximate the Mapping with a +* linear transformation applied over the whole region of the +* output grid which is being used. If this proves to be +* insufficiently accurate, the output region is sub-divided into +* two along its largest dimension and the process is repeated +* within each of the resulting sub-regions. This process of +* sub-division continues until a sufficiently good linear +* approximation is found, or the region to which it is being +* applied becomes too small (in which case the original Mapping is +* used directly). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to a Mapping, whose inverse transformation will be +* used to transform the coordinates of pixels in the output +* grid into the coordinate system of the input grid. This +* yields the positions which are used to obtain resampled +* values by sub-pixel interpolation within the input grid. +* +* The number of input coordinates used by this Mapping (as +* given by its Nin attribute) should match the number of input +c grid dimensions given by the value of "ndim_in" +f grid dimensions given by the value of NDIM_IN +* below. Similarly, the number of output coordinates (Nout +* attribute) should match the number of output grid dimensions +c given by "ndim_out". +f given by NDIM_OUT. +c ndim_in +f NDIM_IN = INTEGER (Given) +* The number of dimensions in the input grid. This should be at +* least one. +c lbnd_in +f LBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +c ubnd_in +f UBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +c Note that "lbnd_in" and "ubnd_in" together define the shape +f Note that LBND_IN and UBND_IN together define the shape +* and size of the input grid, its extent along a particular +c (j'th) dimension being ubnd_in[j]-lbnd_in[j]+1 (assuming the +c index "j" to be zero-based). They also define +f (J'th) dimension being UBND_IN(J)-LBND_IN(J)+1. They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre. +c in +f IN( * ) = (Given) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* input grid, containing the input data to be resampled. The +* numerical type of this array should match the 1- or +* 2-character type code appended to the function name (e.g. if +c you are using astResampleF, the type of each array element +c should be "float"). +f you are using AST_RESAMPLER, the type of each array element +f should be REAL). +* +* The storage order of data within this array should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c in_var +f IN_VAR( * ) = (Given) +c An optional pointer to a second array with the same size and +c type as the "in" array. If given, this should contain a set +c of non-negative values which represent estimates of the +c statistical variance associated with each element of the "in" +c array. If this array is supplied (together with the +c corresponding "out_var" array), then estimates of the +c variance of the resampled output data will be calculated. +c +c If no input variance estimates are being provided, a NULL +c pointer should be given. +f An optional second array with the same size and type as the +f IN array. If the AST__USEVAR flag is set via the FLAGS +f argument (below), this array should contain a set of +f non-negative values which represent estimates of the +f statistical variance associated with each element of the IN +f array. Estimates of the variance of the resampled output data +f will then be calculated. +f +f If the AST__USEVAR flag is not set, no input variance +f estimates are required and this array will not be used. A +f dummy (e.g. one-element) array may then be supplied. +c interp +f INTERP = INTEGER (Given) +c This parameter specifies the scheme to be used for sub-pixel +f This argument specifies the scheme to be used for sub-pixel +* interpolation within the input grid. It may be used to select +* from a set of pre-defined schemes by supplying one of the +* values described in the "Sub-Pixel Interpolation Schemes" +* section below. If a value of zero is supplied, then the +* default linear interpolation scheme is used (equivalent to +* supplying the value AST__LINEAR). +* +* Alternatively, you may supply a value which indicates that +c you will provide your own function to perform sub-pixel +c interpolation by means of the "finterp " parameter. Again, see +f you will provide your own routine to perform sub-pixel +f interpolation by means of the FINTERP argument. Again, see +* the "Sub-Pixel Interpolation Schemes" section below for +* details. +c finterp +f FINTERP = SUBROUTINE (Given) +c If the value given for the "interp" parameter indicates that +c you will provide your own function for sub-pixel +c interpolation, then a pointer to that function should be +c given here. For details of the interface which the function +c should have (several are possible, depending on the value of +c "interp"), see the "Sub-Pixel Interpolation Schemes" section +c below. +f If the value given for the INTERP argument indicates that you +f will provide your own routine for sub-pixel interpolation, +f then the name of that routine should be given here (the name +f should also appear in a Fortran EXTERNAL statement in the +f routine which invokes AST_RESAMPLE). For details of the +f interface which the routine should have (several are +f possible, depending on the value of INTERP), see the +f "Sub-Pixel Interpolation Schemes" section below. +* +c If the "interp" parameter has any other value, corresponding +c to one of the pre-defined interpolation schemes, then this +c function will not be used and you may supply a NULL pointer. +f If the INTERP argument has any other value, corresponding to +f one of the pre-defined interpolation schemes, then this +f routine will not be used and you may supply the null routine +f AST_NULL here (note only one underscore). No EXTERNAL +f statement is required for this routine, so long as the AST_PAR +f include file has been used. +c params +f PARAMS( * ) = DOUBLE PRECISION (Given) +c An optional pointer to an array of double which should contain +f An optional array which should contain +* any additional parameter values required by the sub-pixel +* interpolation scheme. If such parameters are required, this +* will be noted in the "Sub-Pixel Interpolation Schemes" +c section below (you may also use this array to pass values +c to your own interpolation function). +f section below (you may also use this array to pass values +f to your own interpolation routine). +* +c If no additional parameters are required, this array is not +c used and a NULL pointer may be given. +f If no additional parameters are required, this array is not +f used. A dummy (e.g. one-element) array may then be supplied. +c flags +f FLAGS = INTEGER (Given) +c The bitwise OR of a set of flag values which may be used to +f The sum of a set of flag values which may be used to +* provide additional control over the resampling operation. See +* the "Control Flags" section below for a description of the +* options available. If no flag values are to be set, a value +* of zero should be given. +c tol +f TOL = DOUBLE PRECISION (Given) +* The maximum tolerable geometrical distortion which may be +* introduced as a result of approximating non-linear Mappings +* by a set of piece-wise linear transformations. This should be +* expressed as a displacement in pixels in the input grid's +* coordinate system. +* +* If piece-wise linear approximation is not required, a value +* of zero may be given. This will ensure that the Mapping is +* used without any approximation, but may increase execution +* time. +c maxpix +f MAXPIX = INTEGER (Given) +* A value which specifies an initial scale size (in pixels) for +* the adaptive algorithm which approximates non-linear Mappings +* with piece-wise linear transformations. Normally, this should +* be a large value (larger than any dimension of the region of +* the output grid being used). In this case, a first attempt to +* approximate the Mapping by a linear transformation will be +* made over the entire output region. +* +* If a smaller value is used, the output region will first be +c divided into sub-regions whose size does not exceed "maxpix" +f divided into sub-regions whose size does not exceed MAXPIX +* pixels in any dimension. Only at this point will attempts at +* approximation commence. +* +* This value may occasionally be useful in preventing false +* convergence of the adaptive algorithm in cases where the +* Mapping appears approximately linear on large scales, but has +* irregularities (e.g. holes) on smaller scales. A value of, +* say, 50 to 100 pixels can also be employed as a safeguard in +* general-purpose software, since the effect on performance is +* minimal. +* +* If too small a value is given, it will have the effect of +* inhibiting linear approximation altogether (equivalent to +c setting "tol" to zero). Although this may degrade +f setting TOL to zero). Although this may degrade +* performance, accurate results will still be obtained. +c badval +f BADVAL = (Given) +* This argument should have the same type as the elements of +c the "in" array. It specifies the value used to flag missing +f the IN array. It specifies the value used to flag missing +* data (bad pixels) in the input and output arrays. +* +c If the AST__USEBAD flag is set via the "flags" parameter, +f If the AST__USEBAD flag is set via the FLAGS argument, +c then this value is used to test for bad pixels in the "in" +c (and "in_var") array(s). +f then this value is used to test for bad pixels in the IN +f (and IN_VAR) array(s). +* +c Unless the AST__NOBAD flag is set via the "flags" parameter, +f Unless the AST__NOBAD flag is set via the FLAGS argument, +* this value is also used to flag any output +c elements in the "out" (and "out_var") array(s) for which +f elements in the OUT (and OUT_VAR) array(s) for which +* resampled values could not be obtained (see the "Propagation +* of Missing Data" section below for details of the +c circumstances under which this may occur). The astResample +f circumstances under which this may occur). The AST_RESAMPLE +* function return value indicates whether any such values have +* been produced. If the AST__NOBAD flag is set. then output array +* elements for which no resampled value could be obtained are +* left set to the value they had on entry to this function. +c ndim_out +f NDIM_OUT = INTEGER (Given) +* The number of dimensions in the output grid. This should be +* at least one. It need not necessarily be equal to the number +* of dimensions in the input grid. +c lbnd_out +f LBND_OUT( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the output grid along each dimension. +c ubnd_out +f UBND_OUT( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the output grid along each dimension. +* +c Note that "lbnd_out" and "ubnd_out" together define the +f Note that LBND_OUT and UBND_OUT together define the +* shape, size and coordinate system of the output grid in the +c same way as "lbnd_in" and "ubnd_in" define the shape, size +f same way as LBND_IN and UBND_IN define the shape, size +* and coordinate system of the input grid. +c lbnd +f LBND( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the first pixel in the region +* of the output grid for which a resampled value is to be +* calculated. +c ubnd +f UBND( NDIM_OUT ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_out" elements, +f An array +* containing the coordinates of the last pixel in the region of +* the output grid for which a resampled value is to be +* calculated. +* +c Note that "lbnd" and "ubnd" together define the shape and +f Note that LBND and UBND together define the shape and +* position of a (hyper-)rectangular region of the output grid +* for which resampled values should be produced. This region +* should lie wholly within the extent of the output grid (as +c defined by the "lbnd_out" and "ubnd_out" arrays). Regions of +f defined by the LBND_OUT and UBND_OUT arrays). Regions of +* the output grid lying outside this region will not be +* modified. +c out +f OUT( * ) = (Returned) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* output grid, into which the resampled data values will be +* returned. The numerical type of this array should match that +c of the "in" array, and the data storage order should be such +f of the IN array, and the data storage order should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c out_var +f OUT_VAR( * ) = (Returned) +c An optional pointer to an array with the same type and size +c as the "out" array. If given, this array will be used to +c return variance estimates for the resampled data values. This +c array will only be used if the "in_var" array has also been +c supplied. +f An optional array with the same type and size as the OUT +f array. If the AST__USEVAR flag is set via the FLAGS argument, +f this array will be used to return variance estimates for the +f resampled data values. +* +* The output variance values will be calculated on the +* assumption that errors on the input data values are +* statistically independent and that their variance estimates +* may simply be summed (with appropriate weighting factors) +* when several input pixels contribute to an output data +* value. If this assumption is not valid, then the output error +* estimates may be biased. In addition, note that the +* statistical errors on neighbouring output data values (as +* well as the estimates of those errors) may often be +* correlated, even if the above assumption about the input data +* is correct, because of the sub-pixel interpolation schemes +* employed. +* +c If no output variance estimates are required, a NULL pointer +c should be given. +f If the AST__USEVAR flag is not set, no output variance +f estimates will be calculated and this array will not be +f used. A dummy (e.g. one-element) array may then be supplied. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astResample() +f AST_RESAMPLE = INTEGER +* The number of output pixels for which no valid resampled value +* could be obtained. Thus, in the absence of any error, a returned +* value of zero indicates that all the required output pixels +* received valid resampled data values (and variances). See the +c "badval" and "flags" parameters. +f BADVAL and FLAGS arguments. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Data Type Codes: +* To select the appropriate resampling function, you should +c replace in the generic function name astResample with a +f replace in the generic function name AST_RESAMPLE with a +* 1- or 2-character data type code, so as to match the numerical +* type of the data you are processing, as follows: +c - D: double +c - F: float +c - L: long int (may be 32 or 64 bit) +c - K: 64 bit int +c - UL: unsigned long int (may be 32 or 64 bit) +c - UK: unsigned 64 bit int +c - I: int +c - UI: unsigned int +c - S: short int +c - US: unsigned short int +c - B: byte (signed char) +c - UB: unsigned byte (unsigned char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - UI: INTEGER (treated as unsigned) +f - S: INTEGER*2 (short integer) +f - US: INTEGER*2 (short integer, treated as unsigned) +f - B: BYTE (treated as signed) +f - UB: BYTE (treated as unsigned) +* +c For example, astResampleD would be used to process "double" +c data, while astResampleS would be used to process "short int" +c data, etc. +f For example, AST_RESAMPLED would be used to process DOUBLE +f PRECISION data, while AST_RESAMPLES would be used to process +f short integer data (stored in an INTEGER*2 array), etc. +f +f For compatibility with other Starlink facilities, the codes W +f and UW are provided as synonyms for S and US respectively (but +f only in the Fortran interface to AST). + +* Sub-Pixel Interpolation Schemes: +* There is no such thing as a perfect sub-pixel interpolation +* scheme and, in practice, all resampling will result in some +* degradation of gridded data. A range of schemes is therefore +* provided, from which you can choose the one which best suits +* your needs. +* +* In general, a balance must be struck between schemes which tend +* to degrade sharp features in the data by smoothing them, and +* those which attempt to preserve sharp features. The latter will +* often tend to introduce unwanted oscillations, typically visible +* as "ringing" around sharp features and edges, especially if the +* data are under-sampled (i.e. if the sharpest features are less +* than about two pixels across). In practice, a good interpolation +* scheme is likely to be a compromise and may exhibit some aspects +* of both these features. +* +* For under-sampled data, some interpolation schemes may appear to +* preserve data resolution because they transform single input +* pixels into single output pixels, rather than spreading their +* data between several output pixels. While this may look +* better cosmetically, it can result in a geometrical shift of +* sharp features in the data. You should beware of this if you +* plan to use such features (e.g.) for image alignment. +* +* The following are two easy-to-use sub-pixel interpolation +* schemes which are generally applicable. They are selected by +c supplying the appropriate value (defined in the "ast.h" header +c file) via the "interp" parameter. In these cases, the "finterp" +c and "params" parameters are not used: +f supplying the appropriate value (defined in the AST_PAR include +f file) via the INTERP argument. In these cases, the FINTERP +f and PARAMS arguments are not used: +* +* - AST__NEAREST: This is the simplest possible scheme, in which +* the value of the input pixel with the nearest centre to the +* interpolation point is used. This is very quick to execute and +* will preserve single-pixel features in the data, but may +* displace them by up to half their width along each dimension. It +* often gives a good cosmetic result, so is useful for quick-look +* processing, but is unsuitable if accurate geometrical +* transformation is required. +* - AST__LINEAR: This is the default scheme, which uses linear +* interpolation between the nearest neighbouring pixels in the +* input grid (there are two neighbours in one dimension, four +* neighbours in two dimensions, eight in three dimensions, +* etc.). It is superior to the nearest-pixel scheme (above) in not +* displacing features in the data, yet it still executes fairly +* rapidly. It is generally a safe choice if you do not have any +* particular reason to favour another scheme, since it cannot +* introduce oscillations. However, it does introduce some spatial +* smoothing which varies according to the distance of the +* interpolation point from the neighbouring pixels. This can +* degrade the shape of sharp features in the data in a +* position-dependent way. It may also show in the output variance +* grid (if used) as a pattern of stripes or fringes. +* +* An alternative set of interpolation schemes is based on forming +* the interpolated value from the weighted sum of a set of +* surrounding pixel values (not necessarily just the nearest +* neighbours). This approach has its origins in the theory of +* digital filtering, in which interpolated values are obtained by +* conceptually passing the sampled data (represented by a grid of +* delta functions) through a linear filter which implements a +* convolution. Because the convolution kernel is continuous, the +* convolution yields a continuous function which may then be +* evaluated at fractional pixel positions. The (possibly +* multi-dimensional) kernel is usually regarded as "separable" and +* formed from the product of a set of identical 1-dimensional +* kernel functions, evaluated along each dimension. Different +* interpolation schemes are then distinguished by the choice of +* this 1-dimensional interpolation kernel. The number of +* surrounding pixels which contribute to the result may also be +* varied. +* +* From a practical standpoint, it is useful to divide the weighted +* sum of pixel values by the sum of the weights when determining +* the interpolated value. Strictly, this means that a true +* convolution is no longer being performed. However, the +* distinction is rarely important in practice because (for +* slightly subtle reasons) the sum of weights is always +* approximately constant for good interpolation kernels. The +* advantage of this technique, which is used here, is that it can +* easily accommodate missing data and tends to minimise unwanted +* oscillations at the edges of the data grid. +* +* In the following schemes, which are based on a 1-dimensional +c interpolation kernel, the first element of the "params" array +f interpolation kernel, the first element of the PARAMS array +* should be used to specify how many pixels are to contribute to the +* interpolated result on either side of the interpolation point in +* each dimension (the nearest integer value is used). Execution time +* increases rapidly with this number. Typically, a value of 2 is +* appropriate and the minimum value used will be 1 (i.e. two pixels +* altogether, one on either side of the interpolation point). +c A value of zero or less may be given for "params[0]" +f A value of zero or less may be given for PARAMS(1) +* to indicate that a suitable number of pixels should be calculated +* automatically. +* +c In each of these cases, the "finterp" parameter is not used: +f In each of these cases, the FINTERP argument is not used: +* +* - AST__GAUSS: This scheme uses a kernel of the form exp(-k*x*x), with +* k a positive constant. The full-width at half-maximum (FWHM) is +* given by +c "params[1]" +f PARAMS(2) +f value, which should be at least 0.1 (in addition, setting PARAMS(1) +* to zero will select the number of contributing pixels so as to utilise +* the width of the kernel out to where the envelope declines to 1% of its +* maximum value). This kernel suppresses noise at the expense of +* smoothing the output array. +* - AST__SINC: This scheme uses a sinc(pi*x) kernel, where x is the +* pixel offset from the interpolation point and sinc(z)=sin(z)/z. This +* sometimes features as an "optimal" interpolation kernel in books on +* image processing. Its supposed optimality depends on the assumption +* that the data are band-limited (i.e. have no spatial frequencies above +* a certain value) and are adequately sampled. In practice, astronomical +* data rarely meet these requirements. In addition, high spatial +* frequencies are often present due (e.g.) to image defects and cosmic +* ray events. Consequently, substantial ringing can be experienced with +* this kernel. The kernel also decays slowly with distance, so that +* many surrounding pixels are required, leading to poor performance. +* Abruptly truncating it, by using only a few neighbouring pixels, +c improves performance and may reduce ringing (if "params[0]" is set to +f improves performance and may reduce ringing (if PARAMS(1) is set to +* zero, then only two pixels will be used on either side). However, a +* more gradual truncation, as implemented by other kernels, is generally +* to be preferred. This kernel is provided mainly so that you can +* convince yourself not to use it! +* - AST__SINCSINC: This scheme uses an improved kernel, of the form +* sinc(pi*x).sinc(k*pi*x), with k a constant, out to the point where +* sinc(k*pi*x) goes to zero, and zero beyond. The second sinc() factor +* provides an "envelope" which gradually rolls off the normal sinc(pi*x) +* kernel at large offsets. The width of this envelope is specified by +* giving the number of pixels offset at which it goes to zero by means +c of the "params[1]" value, which should be at least 1.0 (in addition, +c setting "params[0]" to zero will select the number of contributing +f of the PARAMS(2) value, which should be at least 1.0 (in addition, +f setting PARAMS(1) to zero will select the number of contributing +* pixels so as to utilise the full width of the kernel, out to where it +c reaches zero). The case given by "params[0]=2, params[1]=2" is typically +f reaches zero). The case given by PARAMS(1)=2, PARAMS(2)=2 is typically +* a good choice and is sometimes known as the Lanczos kernel. This is a +* valuable general-purpose interpolation scheme, intermediate in its +* visual effect on images between the AST__NEAREST and AST__LINEAR +* schemes. Although the kernel is slightly oscillatory, ringing is +* adequately suppressed if the data are well sampled. +* - AST__SINCCOS: This scheme uses a kernel of the form +* sinc(pi*x).cos(k*pi*x), with k a constant, out to the point where +* cos(k*pi*x) goes to zero, and zero beyond. As above, the cos() factor +* provides an envelope which gradually rolls off the sinc() kernel +* at large offsets. The width of this envelope is specified by giving +* the number of pixels offset at which it goes to zero by means +c of the "params[1]" value, which should be at least 1.0 (in addition, +c setting "params[0]" to zero will select the number of contributing +f of the PARAMS(2) value, which should be at least 1.0 (in addition, +f setting PARAMS(1) to zero will select the number of contributing +* pixels so as to utilise the full width of the kernel, out to where it +* reaches zero). This scheme gives similar results to the +* AST__SINCSINC scheme, which it resembles. +* - AST__SINCGAUSS: This scheme uses a kernel of the form +* sinc(pi*x).exp(-k*x*x), with k a positive constant. Here, the sinc() +* kernel is rolled off using a Gaussian envelope which is specified by +c giving its full-width at half-maximum (FWHM) by means of the "params[1]" +c value, which should be at least 0.1 (in addition, setting "params[0]" +f giving its full-width at half-maximum (FWHM) by means of the PARAMS(2) +f value, which should be at least 0.1 (in addition, setting PARAMS(1) +* to zero will select the number of contributing pixels so as to utilise +* the width of the kernel out to where the envelope declines to 1% of its +* maximum value). On astronomical images and spectra, good results are +* often obtained by approximately matching the FWHM of the +c envelope function, given by "params[1]", to the point spread function +f envelope function, given by PARAMS(2), to the point spread function +* of the input data. However, there does not seem to be any theoretical +* reason for this. +* - AST__SOMB: This scheme uses a somb(pi*x) kernel (a "sombrero" +* function), where x is the pixel offset from the interpolation point +* and somb(z)=2*J1(z)/z (J1 is a Bessel function of the first kind of +* order 1). It is similar to the AST__SINC kernel, and has the same +* parameter usage. +* - AST__SOMBCOS: This scheme uses a kernel of the form +* somb(pi*x).cos(k*pi*x), with k a constant, out to the point where +* cos(k*pi*x) goes to zero, and zero beyond. It is similar to the +* AST__SINCCOS kernel, and has the same parameter usage. +* +* In addition, the following schemes are provided which are not based +* on a 1-dimensional kernel: +* +* - AST__BLOCKAVE: This scheme simply takes an average of all the +* pixels on the input grid in a cube centred on the interpolation +* point. The number of pixels in the cube is determined by the +c value of the first element of the "params" array, which gives +f value of the first element of the PARAMS array, which gives +* the number of pixels in each dimension on either side of the +c central point. Hence a block of (2 * params[0])^ndim_in +f central point. Hence a block of (2 * PARAMS(1))**NDIM_IN +* pixels in the input grid will be examined to determine the +* value of the output pixel. If the variance is not being used +c (var_in or var_out = NULL) then all valid pixels in this cube +f (USEVAR = .FALSE.) then all valid pixels in this cube +* will be averaged in to the result with equal weight. +* If variances are being used, then each input pixel will be +* weighted proportionally to the reciprocal of its variance; any +* pixel without a valid variance will be discarded. This scheme +* is suitable where the output grid is much coarser than the +* input grid; if the ratio of pixel sizes is R then a suitable +c value of params[0] may be R/2. +f value of PARAMS(1) may be R/2. +* +c Finally, supplying the following values for "interp" allows you +c to implement your own sub-pixel interpolation scheme by means of +c your own function. You should supply a pointer to this function +c via the "finterp" parameter: +f Finally, supplying the following values for INTERP allows you to +f implement your own sub-pixel interpolation scheme by means of +f your own routine. You should supply the name of this routine via +f the FINTERP argument: +* +c - AST__UKERN1: In this scheme, you supply a function to evaluate +c your own 1-dimensional interpolation kernel, which is then used +c to perform sub-pixel interpolation (as described above). The +c function you supply should have the same interface as the +c fictitious astUkern1 function (q.v.). In addition, a value +c should be given via "params[0]" to specify the number of +c neighbouring pixels which are to contribute to each interpolated +c value (in the same way as for the pre-defined interpolation +c schemes described above). Other elements of the "params" array +c are available to pass values to your interpolation function. +f - AST__UKERN1: In this scheme, you supply a routine to evaluate +f your own 1-dimensional interpolation kernel, which is then used +f to perform sub-pixel interpolation (as described above). The +f routine you supply should have the same interface as the +f fictitious AST_UKERN1 routine (q.v.). In addition, a value +f should be given via PARAMS(1) to specify the number of +f neighbouring pixels which are to contribute to each interpolated +f value (in the same way as for the pre-defined interpolation +f schemes described above). Other elements of the PARAMS array +f are available to pass values to your interpolation routine. +* +c - AST__UINTERP: This is a completely general scheme, in which +c your interpolation function has access to all of the input +c data. This allows you to implement any interpolation algorithm +c you choose, which could (for example) be non-linear, or +c adaptive. In this case, the astResample functions play no +c role in the sub-pixel interpolation process and simply handle +c the geometrical transformation of coordinates and other +c housekeeping. The function you supply should have the same +c interface as the fictitious astUinterp function (q.v.). In this +c case, the "params" parameter is not used by astResample, but +c is available to pass values to your interpolation function. +f - AST__UINTERP: This is a completely general scheme, in which +f your interpolation routine has access to all of the input +f data. This allows you to implement any interpolation algorithm +f you choose, which could (for example) be non-linear, or +f adaptive. In this case, the AST_RESAMPLE functions play no +f role in the sub-pixel interpolation process and simply handle +f the geometrical transformation of coordinates and other +f housekeeping. The routine you supply should have the same +f interface as the fictitious AST_UINTERP routine (q.v.). In this +f case, the PARAMS argument is not used by AST_RESAMPLE, but +f is available to pass values to your interpolation routine. + +* Control Flags: +c The following flags are defined in the "ast.h" header file and +f The following flags are defined in the AST_PAR include file and +* may be used to provide additional control over the resampling +* process. Having selected a set of flags, you should supply the +c bitwise OR of their values via the "flags" parameter: +f sum of their values via the FLAGS argument: +* +* - AST__NOBAD: Indicates that any output array elements for which no +* resampled value could be obtained should be left set to the value +* they had on entry to this function. If this flag is not supplied, +* such output array elements are set to the value supplied for +c parameter "badval". Note, this flag cannot be used in conjunction +f argument BADVAL. Note, this flag cannot be used in conjunction +* with the AST__CONSERVEFLUX flag (an error will be reported if both +* flags are specified). +* - AST__URESAMP1, 2, 3 & 4: A set of four flags which are +* reserved for your own use. They may be used to pass private +c information to any sub-pixel interpolation function which you +f information to any sub-pixel interpolation routine which you +* implement yourself. They are ignored by all the pre-defined +* interpolation schemes. +* - AST__USEBAD: Indicates that there may be bad pixels in the +* input array(s) which must be recognised by comparing with the +c value given for "badval" and propagated to the output array(s). +f value given for BADVAL and propagated to the output array(s). +* If this flag is not set, all input values are treated literally +c and the "badval" value is only used for flagging output array +f and the BADVAL value is only used for flagging output array +* values. +f - AST__USEVAR: Indicates that variance information should be +f processed in order to provide estimates of the statistical error +f associated with the resampled values. If this flag is not set, +f no variance processing will occur and the IN_VAR and OUT_VAR +f arrays will not be used. (Note that this flag is only available +f in the Fortran interface to AST.) +* - AST__CONSERVEFLUX: Indicates that the output pixel values should +* be scaled in such a way as to preserve (approximately) the total data +* value in a feature on the sky. Without this flag, each output pixel +* value represents an instantaneous sample of the input data values at +* the corresponding input position. This is appropriate if the input +* data represents the spatial density of some quantity (e.g. surface +* brightness in Janskys per square arc-second) because the output +* pixel values will have the same normalisation and units as the +* input pixel values. However, if the input data values represent +* flux (or some other physical quantity) per pixel, then the +* AST__CONSERVEFLUX flag could be used. This causes each output +* pixel value to be scaled by the ratio of the output pixel size to +* the input pixel size. +* +* This flag can only be used if the Mapping is successfully approximated +* by one or more linear transformations. Thus an error will be reported +* if it used when the +c "tol" parameter +f TOL argument +* is set to zero (which stops the use of linear approximations), or +* if the Mapping is too non-linear to be approximated by a piece-wise +* linear transformation. The ratio of output to input pixel size is +* evaluated once for each panel of the piece-wise linear approximation to +* the Mapping, and is assumed to be constant for all output pixels in the +* panel. The scaling factors for adjacent panels will in general +* differ slightly, and so the joints between panels may be visible when +* viewing the output image at high contrast. If this is a problem, +* reduce the value of the +c "tol" parameter +f TOL argument +* until the difference between adjacent panels is sufficiently small +* to be insignificant. +* +* Note, this flag cannot be used in conjunction with the AST__NOBAD +* flag (an error will be reported if both flags are specified). + +* Propagation of Missing Data: +* Unless the AST__NOBAD flag is specified, instances of missing data +* (bad pixels) in the output grid are +c identified by occurrences of the "badval" value in the "out" +f identified by occurrences of the BADVAL value in the OUT +* array. These may be produced if any of the following happen: +* +* - The input position (the transformed position of the output +* pixel's centre) lies outside the boundary of the grid of input +* pixels. +* - The input position lies inside the boundary of a bad input +* pixel. In this context, an input pixel is considered bad if its +c data value is equal to "badval" and the AST__USEBAD flag is +c set via the "flags" parameter. +f data value is equal to BADVAL and the AST__USEBAD flag is +f set via the FLAGS argument. +* (Positions which have half-integral coordinate values, and +* therefore lie on a pixel boundary, are regarded as lying within +* the pixel with the larger, i.e. more positive, index.) +* - The set of neighbouring input pixels (excluding those which +* are bad) is unsuitable for calculating an interpolated +* value. Whether this is true may depend on the sub-pixel +* interpolation scheme in use. +* - The interpolated value lies outside the range which can be +c represented using the data type of the "out" array. +f represented using the data type of the OUT array. +* +* In addition, associated output variance estimates (if +c calculated) may be declared bad and flagged with the "badval" +c value in the "out_var" array under any of the following +f calculated) may be declared bad and flagged with the BADVAL +f value in the OUT_VAR array under any of the following +* circumstances: +* +c - The associated resampled data value (in the "out" array) is bad. +f - The associated resampled data value (in the OUT array) is bad. +* - The set of neighbouring input pixels which contributed to the +* output data value do not all have valid variance estimates +* associated with them. In this context, an input variance +* estimate may be regarded as bad either because it has the value +c "badval" (and the AST__USEBAD flag is set), or because it is +f BADVAL (and the AST__USEBAD flag is set), or because it is +* negative. +* - The set of neighbouring input pixels for which valid variance +* values are available is unsuitable for calculating an overall +* variance value. Whether this is true may depend on the sub-pixel +* interpolation scheme in use. +* - The variance value lies outside the range which can be +c represented using the data type of the "out_var" array. +f represented using the data type of the OUT_VAR array. +* +* If the AST__NOBAD flag is specified via +c parameter "flags", +f argument FLAGS, +* then output array elements that would otherwise be set to +c "badval" +f BADVAL +* are instead left holding the value they had on entry to this +* function. The number of such array elements is returned as +* the function value. +*-- +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_RESAMPLE(X,Xtype) \ +static int Resample##X( AstMapping *this, int ndim_in, \ + const int lbnd_in[], const int ubnd_in[], \ + const Xtype in[], const Xtype in_var[], \ + int interp, void (* finterp)( void ), \ + const double params[], int flags, double tol, \ + int maxpix, Xtype badval, \ + int ndim_out, const int lbnd_out[], \ + const int ubnd_out[], const int lbnd[], \ + const int ubnd[], Xtype out[], Xtype out_var[], int *status ) { \ +\ +/* Local Variables: */ \ + astDECLARE_GLOBALS /* Thread-specific data */ \ + AstMapping *simple; /* Pointer to simplified Mapping */ \ + int idim; /* Loop counter for coordinate dimensions */ \ + int nin; /* Number of Mapping input coordinates */ \ + int nout; /* Number of Mapping output coordinates */ \ + int npix; /* Number of pixels in output region */ \ + int result; /* Result value to return */ \ + int64_t mpix; /* Number of pixels for testing */ \ +\ +/* Initialise. */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Get a pointer to a structure holding thread-specific global data values */ \ + astGET_GLOBALS(this); \ +\ +/* Obtain values for the Nin and Nout attributes of the Mapping. */ \ + nin = astGetNin( this ); \ + nout = astGetNout( this ); \ +\ +/* If OK, check that the number of input grid dimensions matches the \ + number required by the Mapping and is at least 1. Report an error \ + if necessary. */ \ + if ( astOK && ( ( ndim_in != nin ) || ( ndim_in < 1 ) ) ) { \ + astError( AST__NGDIN, "astResample"#X"(%s): Bad number of input grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim_in ); \ + if ( ndim_in != nin ) { \ + astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ + "to specify an input position.", status, \ + astGetClass( this ), nin, ( nin == 1 ) ? "" : "s" ); \ + } \ + } \ +\ +/* If OK, also check that the number of output grid dimensions matches \ + the number required by the Mapping and is at least 1. Report an \ + error if necessary. */ \ + if ( astOK && ( ( ndim_out != nout ) || ( ndim_out < 1 ) ) ) { \ + astError( AST__NGDIN, "astResample"#X"(%s): Bad number of output grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim_out ); \ + if ( ndim_out != nout ) { \ + astError( AST__NGDIN, "The %s given generates %s%d coordinate " \ + "value%s for each output position.", status, astGetClass( this ), \ + ( nout < ndim_out ) ? "only " : "", nout, \ + ( nout == 1 ) ? "" : "s" ); \ + } \ + } \ +\ +/* Check that the lower and upper bounds of the input grid are \ + consistent. Report an error if any pair is not. Also get the number \ + of pixels in the input grid. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_in; idim++ ) { \ + if ( lbnd_in[ idim ] > ubnd_in[ idim ] ) { \ + astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ + "input grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd_in[ idim ], ubnd_in[ idim ] ); \ + astError( AST__GBDIN, "Error in input dimension %d.", status, \ + idim + 1 ); \ + break; \ + } else { \ + mpix *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the input. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astResample"#X"(%s): Supplied input array " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* Check that the positional accuracy tolerance supplied is valid and \ + report an error if necessary. */ \ + if ( astOK && ( tol < 0.0 ) ) { \ + astError( AST__PATIN, "astResample"#X"(%s): Invalid positional " \ + "accuracy tolerance (%.*g pixel).", status, \ + astGetClass( this ), DBL_DIG, tol ); \ + astError( AST__PATIN, "This value should not be less than zero." , status); \ + } \ +\ +/* Check that the initial scale size in pixels supplied is valid and \ + report an error if necessary. */ \ + if ( astOK && ( maxpix < 0 ) ) { \ + astError( AST__SSPIN, "astResample"#X"(%s): Invalid initial scale " \ + "size in pixels (%d).", status, astGetClass( this ), maxpix ); \ + astError( AST__SSPIN, "This value should not be less than zero." , status); \ + } \ +\ +/* Check that the lower and upper bounds of the output grid are \ + consistent. Report an error if any pair is not. Also get the \ + number of pixels in the output array. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + if ( lbnd_out[ idim ] > ubnd_out[ idim ] ) { \ + astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ + "output grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd_out[ idim ], ubnd_out[ idim ] ); \ + astError( AST__GBDIN, "Error in output dimension %d.", status, \ + idim + 1 ); \ + break; \ + } else { \ + mpix *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the output. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astResample"#X"(%s): Supplied output array " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* Similarly check the bounds of the output region. */ \ + mpix = 1; \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + if ( lbnd[ idim ] > ubnd[ idim ] ) { \ + astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ + "output region (%d) exceeds corresponding upper " \ + "bound (%d).", status, astGetClass( this ), \ + lbnd[ idim ], ubnd[ idim ] ); \ +\ +/* Also check that the output region lies wholly within the output \ + grid. */ \ + } else if ( lbnd[ idim ] < lbnd_out[ idim ] ) { \ + astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ + "output region (%d) is less than corresponding " \ + "bound of output grid (%d).", status, astGetClass( this ), \ + lbnd[ idim ], lbnd_out[ idim ] ); \ + } else if ( ubnd[ idim ] > ubnd_out[ idim ] ) { \ + astError( AST__GBDIN, "astResample"#X"(%s): Upper bound of " \ + "output region (%d) exceeds corresponding " \ + "bound of output grid (%d).", status, astGetClass( this ), \ + ubnd[ idim ], ubnd_out[ idim ] ); \ + } else { \ + mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ + } \ +\ +/* Say which dimension produced the error. */ \ + if ( !astOK ) { \ + astError( AST__GBDIN, "Error in output dimension %d.", status, \ + idim + 1 ); \ + break; \ + } \ + } \ + } \ +\ +/* Report an error if there are too many pixels in the output region. */ \ + if ( astOK && (int) mpix != mpix ) { \ + astError( AST__EXSPIX, "astResample"#X"(%s): Supplied output region " \ + "contains too many pixels (%g): must be fewer than %d.", \ + status, astGetClass( this ), (double) mpix, INT_MAX ); \ + } \ +\ +/* If we are conserving flux, check "tol" is not zero. */ \ + if( ( flags & AST__CONSERVEFLUX ) && astOK ) { \ + if( tol == 0.0 ) { \ + astError( AST__CNFLX, "astResample"#X"(%s): Flux conservation was " \ + "requested but cannot be performed because zero tolerance " \ + "was also specified.", status, astGetClass( this ) ); \ +\ +/* Also check "nin" and "nout" are equal. */ \ + } else if( nin != nout ) { \ + astError( AST__CNFLX, "astResample"#X"(%s): Flux conservation was " \ + "requested but cannot be performed because the Mapping " \ + "has different numbers of inputs and outputs.", status, \ + astGetClass( this ) ); \ + } \ + } \ +\ +/* If OK, loop to determine how many pixels require resampled values. */ \ + simple = NULL; \ + if ( astOK ) { \ + npix = 1; \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + npix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ + } \ +\ +/* If there are sufficient pixels to make it worthwhile, simplify the \ + Mapping supplied to improve performance. Otherwise, just clone the \ + Mapping pointer. Note we save a pointer to the original Mapping so \ + that lower-level functions can use it if they need to report an \ + error. */ \ + unsimplified_mapping = this; \ + if ( npix > 1024 ) { \ + simple = astSimplify( this ); \ + } else { \ + simple = astClone( this ); \ + } \ + } \ +\ +/* Report an error if the inverse transformation of this simplified \ + Mapping is not defined. */ \ + if ( !astGetTranInverse( simple ) && astOK ) { \ + astError( AST__TRNND, "astResample"#X"(%s): An inverse coordinate " \ + "transformation is not defined by the %s supplied.", status, \ + astGetClass( unsimplified_mapping ), \ + astGetClass( unsimplified_mapping ) ); \ + } \ +\ +/* Perform the resampling. Note that we pass all gridded data, the \ + interpolation function and the bad pixel value by means of pointer \ + types that obscure the underlying data type. This is to avoid \ + having to replicate functions unnecessarily for each data \ + type. However, we also pass an argument that identifies the data \ + type we have obscured. */ \ + result = ResampleAdaptively( simple, ndim_in, lbnd_in, ubnd_in, \ + (const void *) in, (const void *) in_var, \ + TYPE_##X, interp, finterp, \ + params, flags, tol, maxpix, \ + (const void *) &badval, \ + ndim_out, lbnd_out, ubnd_out, \ + lbnd, ubnd, \ + (void *) out, (void *) out_var, status ); \ +\ +/* Annul the pointer to the simplified/cloned Mapping. */ \ + simple = astAnnul( simple ); \ +\ +/* If an error occurred, clear the returned result. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_RESAMPLE(LD,long double) +#endif +MAKE_RESAMPLE(D,double) +MAKE_RESAMPLE(F,float) +MAKE_RESAMPLE(L,long int) +MAKE_RESAMPLE(UL,unsigned long int) +MAKE_RESAMPLE(K,INT_BIG) +MAKE_RESAMPLE(UK,UINT_BIG) +MAKE_RESAMPLE(I,int) +MAKE_RESAMPLE(UI,unsigned int) +MAKE_RESAMPLE(S,short int) +MAKE_RESAMPLE(US,unsigned short int) +MAKE_RESAMPLE(B,signed char) +MAKE_RESAMPLE(UB,unsigned char) + +/* Undefine the macro. */ +#undef MAKE_RESAMPLE + +static int ResampleAdaptively( AstMapping *this, int ndim_in, + const int *lbnd_in, const int *ubnd_in, + const void *in, const void *in_var, + DataType type, int interp, void (* finterp)( void ), + const double *params, int flags, double tol, + int maxpix, const void *badval_ptr, + int ndim_out, const int *lbnd_out, + const int *ubnd_out, const int *lbnd, + const int *ubnd, void *out, void *out_var, int *status ) { +/* +* Name: +* ResampleAdaptively + +* Purpose: +* Resample a section of a data grid adaptively. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int ResampleAdaptively( AstMapping *this, int ndim_in, +* const int *lbnd_in, const int *ubnd_in, +* const void *in, const void *in_var, +* DataType type, int interp, void (* finterp)( void ), +* const double *params, int flags, double tol, +* int maxpix, const void *badval_ptr, +* int ndim_out, const int *lbnd_out, +* const int *ubnd_out, const int *lbnd, +* const int *ubnd, void *out, void *out_var ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function resamples a rectangular grid of data (with any +* number of dimensions) into a specified section of another +* rectangular grid (with a possibly different number of +* dimensions). The coordinate transformation used to convert +* output pixel coordinates into positions in the input grid is +* given by the inverse transformation of the Mapping which is +* supplied. Any pixel interpolation scheme may be specified for +* interpolating between the pixels of the input grid. +* +* This function is very similar to ResampleWithBlocking and +* ResampleSection which lie below it in the calling +* hierarchy. However, this function also attempts to adapt to the +* Mapping supplied and to sub-divide the section being resampled +* into smaller sections within which a linear approximation to the +* Mapping may be used. This reduces the number of Mapping +* evaluations, thereby improving efficiency particularly when +* complicated Mappings are involved. + +* Parameters: +* this +* Pointer to a Mapping, whose inverse transformation may be +* used to transform the coordinates of pixels in the output +* grid into associated positions in the input grid, from which +* the output pixel values should be derived (by interpolation +* if necessary). +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input data grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input data grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input data grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* in +* Pointer to the input array of data to be resampled (with one +* element for each pixel in the input grid). The numerical type +* of these data should match the "type" value (below). The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and data type as the "in" array), +* which represent estimates of the statistical variance +* associated with each element of the "in" array. If this +* second array is given (along with the corresponding "out_var" +* array), then estimates of the variance of the resampled data +* will also be returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* type +* A value taken from the "DataType" enum, which specifies the +* data type of the input and output arrays containing the +* gridded data (and variance) values. +* interp +* A value selected from a set of pre-defined macros to identify +* which sub-pixel interpolation algorithm should be used. +* finterp +* If "interp" is set to a value which requires a user-supplied +* function, then a pointer to that function shoild be given +* here. Otherwise, this value is not used and may be a NULL +* pointer. +* params +* Pointer to an optional array of parameters that may be passed +* to the interpolation algorithm, if required. If no parameters +* are required, a NULL pointer should be supplied. +* flags +* The bitwise OR of a set of flag values which provide +* additional control over the resampling operation. +* tol +* The maximum permitted positional error in transforming output +* pixel positions into the input grid in order to resample +* it. This should be expressed as a displacement in pixels in +* the input grid's coordinate system. If the Mapping's inverse +* transformation can be approximated by piecewise linear +* functions to this accuracy, then such functions may be used +* instead of the Mapping in order to improve +* performance. Otherwise, every output pixel position will be +* transformed individually using the Mapping. +* +* If linear approximation is not required, a "tol" value of +* zero may be given. This will ensure that the Mapping is used +* without any approximation. +* maxpix +* A value which specifies the largest scale size on which to +* search for non-linearities in the Mapping supplied. This +* value should be expressed as a number of pixels in the output +* grid. The function will break the output section specified +* into smaller sub-sections (if necessary), each no larger than +* "maxpix" pixels in any dimension, before it attempts to +* approximate the Mapping by a linear function over each +* sub-section. +* +* If the value given is larger than the largest dimension of +* the output section (the normal recommendation), the function +* will initially search for non-linearity on a scale determined +* by the size of the output section. This is almost always +* satisfactory. Very occasionally, however, a Mapping may +* appear linear on this scale but nevertheless have smaller +* irregularities (e.g. "holes") in it. In such cases, "maxpix" +* may be set to a suitably smaller value so as to ensure this +* non-linearity is not overlooked. Typically, a value of 50 to +* 100 pixels might be suitable and should have little effect on +* performance. +* +* If too small a value is given, however, it will have the +* effect of preventing linear approximation occurring at all +* (equivalent to setting "tol" to zero). Although this may +* degrade performance, accurate results will still be obtained. +* badval_ptr +* If the AST__USEBAD flag is set (above), this parameter is a +* pointer to a value which is used to identify bad data and/or +* variance values in the input array(s). The referenced value's +* data type must match that of the "in" (and "in_var") +* arrays. Unless the AST__NOBAD flag is set, the same value will +* also be used to flag any output array elements for which +* resampled values could not be obtained. The output arrays(s) +* may be flagged with this value whether or not the AST__USEBAD +* flag is set (the function return value indicates whether any +* such values have been produced). +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output data grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output data grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output data grid in the same way as "lbnd_in" +* and "ubnd_in" define the shape and size of the input grid +* (see above). +* lbnd +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the first pixel in the +* section of the output data grid for which a value is +* required. +* ubnd +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the last pixel in the +* section of the output data grid for which a value is +* required. +* +* Note that "lbnd" and "ubnd" define the shape and position of +* the section of the output grid for which resampled values are +* required. This section should lie wholly within the extent of +* the output grid (as defined by the "lbnd_out" and "ubnd_out" +* arrays). Regions of the output grid lying outside this section +* will not be modified. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the resampled data will be returned. The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the resampled values may be returned. This array will only be +* used if the "in_var" array has been given. +* +* If no output variance estimates are required, a NULL pointer +* should be given. + +* Returned Value: +* The number of output grid points for which no valid output value +* could be obtained. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + double *flbnd; /* Array holding floating point lower bounds */ + double *fubnd; /* Array holding floating point upper bounds */ + double *linear_fit; /* Pointer to array of fit coefficients */ + int *hi; /* Pointer to array of section upper bounds */ + int *lo; /* Pointer to array of section lower bounds */ + int coord_out; /* Loop counter for output coordinates */ + int dim; /* Output section dimension size */ + int dimx; /* Dimension with maximum section extent */ + int divide; /* Sub-divide the output section? */ + int i; /* Loop count */ + int isLinear; /* Is the transformation linear? */ + int mxdim; /* Largest output section dimension size */ + int npix; /* Number of pixels in output section */ + int npoint; /* Number of points for obtaining a fit */ + int nvertex; /* Number of vertices of output section */ + int result; /* Result value to return */ + int toobig; /* Section too big (must sub-divide)? */ + int toosmall; /* Section too small to sub-divide? */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + npix = 1; + mxdim = 0; + dimx = 1; + nvertex = 1; + +/* Loop through the output grid dimensions. */ + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + +/* Obtain the extent in each dimension of the output section which is + to receive resampled values, and calculate the total number of + pixels it contains. */ + dim = ubnd[ coord_out ] - lbnd[ coord_out ] + 1; + npix *= dim; + +/* Find the maximum dimension size of this output section and note + which dimension has this size. */ + if ( dim > mxdim ) { + mxdim = dim; + dimx = coord_out; + } + +/* Calculate how many vertices the output section has. */ + nvertex *= 2; + } + +/* Calculate how many sample points will be needed (by the + astLinearApprox function) to obtain a linear fit to the Mapping's + inverse transformation. */ + npoint = 1 + 4 * ndim_out + 2 * nvertex; + +/* If the number of pixels in the output section is not at least 4 + times this number, we will probably not save significant time by + attempting to obtain a linear fit, so note that the output section + is too small. */ + toosmall = ( npix < ( 4 * npoint ) ); + +/* Note if the maximum dimension of the output section exceeds the + user-supplied scale factor. */ + toobig = ( maxpix < mxdim ); + +/* Assume the Mapping is significantly non-linear before deciding + whether to sub-divide the output section. */ + linear_fit = NULL; + +/* If the output section is too small to be worth obtaining a linear + fit, or if the accuracy tolerance is zero, we will not + sub-divide. This means that the Mapping will be used to transform + each pixel's coordinates and no linear approximation will be + used. */ + if ( toosmall || ( tol == 0.0 ) ) { + divide = 0; + +/* Otherwise, if the largest output section dimension exceeds the + scale length given, we will sub-divide. This offers the possibility + of obtaining a linear approximation to the Mapping over a reduced + range of output coordinates (which will be handled by a recursive + invocation of this function). */ + } else if ( toobig ) { + divide = 1; + +/* If neither of the above apply, then attempt to fit a linear + approximation to the Mapping's inverse transformation over the + range of coordinates covered by the output section. We need to + temporarily copy the integer bounds into floating point arrays to + use astLinearApprox. */ + } else { + +/* Allocate memory for floating point bounds and for the coefficient array */ + flbnd = astMalloc( sizeof( double )*(size_t) ndim_out ); + fubnd = astMalloc( sizeof( double )*(size_t) ndim_out ); + linear_fit = astMalloc( sizeof( double )* + (size_t) ( ndim_in*( ndim_out + 1 ) ) ); + if( astOK ) { + +/* Copy the bounds into these arrays, and change them so that they refer + to the lower and upper edges of the cell rather than the centre. This + is essential if one of the axes is spanned by a single cell, since + otherwise the upper and lower bounds would be identical. */ + for( i = 0; i < ndim_out; i++ ) { + flbnd[ i ] = (double) lbnd[ i ] - 0.5; + fubnd[ i ] = (double) ubnd[ i ] + 0.5; + } + +/* Get the linear approximation to the inverse transformation. The + astLinearApprox function fits the forward transformation so temporarily + invert the Mapping in order to get a fit to the inverse transformation. */ + astInvert( this ); + isLinear = astLinearApprox( this, flbnd, fubnd, tol, linear_fit ); + astInvert( this ); + +/* Free the coeff array if the inverse transformation is not linear. */ + if( !isLinear ) linear_fit = astFree( linear_fit ); + + } else { + linear_fit = astFree( linear_fit ); + } + +/* Free resources */ + flbnd = astFree( flbnd ); + fubnd = astFree( fubnd ); + +/* If a linear fit was obtained, we will use it and therefore do not + wish to sub-divide further. Otherwise, we sub-divide in the hope + that this may result in a linear fit next time. */ + divide = !linear_fit; + } + +/* If no sub-division is required, perform resampling (in a + memory-efficient manner, since the section we are resampling might + still be very large). This will use the linear fit, if obtained + above. */ + if ( astOK ) { + if ( !divide ) { + result = ResampleWithBlocking( this, linear_fit, + ndim_in, lbnd_in, ubnd_in, + in, in_var, type, interp, finterp, + params, flags, badval_ptr, + ndim_out, lbnd_out, ubnd_out, + lbnd, ubnd, out, out_var, status ); + +/* Otherwise, allocate workspace to perform the sub-division. */ + } else { + lo = astMalloc( sizeof( int ) * (size_t) ndim_out ); + hi = astMalloc( sizeof( int ) * (size_t) ndim_out ); + if ( astOK ) { + +/* Initialise the bounds of a new output section to match the original + output section. */ + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + lo[ coord_out ] = lbnd[ coord_out ]; + hi[ coord_out ] = ubnd[ coord_out ]; + } + +/* Replace the upper bound of the section's largest dimension with the + mid-point of the section along this dimension, rounded + downwards. */ + hi[ dimx ] = + (int) floor( 0.5 * (double) ( lbnd[ dimx ] + ubnd[ dimx ] ) ); + +/* Resample the resulting smaller section using a recursive invocation + of this function. */ + result = ResampleAdaptively( this, ndim_in, lbnd_in, ubnd_in, + in, in_var, type, interp, finterp, + params, flags, tol, maxpix, + badval_ptr, ndim_out, + lbnd_out, ubnd_out, + lo, hi, out, out_var, status ); + +/* Now set up a second section which covers the remaining half of the + original output section. */ + lo[ dimx ] = hi[ dimx ] + 1; + hi[ dimx ] = ubnd[ dimx ]; + +/* If this section contains pixels, resample it in the same way, + summing the returned values. */ + if ( lo[ dimx ] <= hi[ dimx ] ) { + result += ResampleAdaptively( this, ndim_in, lbnd_in, ubnd_in, + in, in_var, type, interp, finterp, + params, flags, tol, maxpix, + badval_ptr, ndim_out, + lbnd_out, ubnd_out, + lo, hi, out, out_var, status ); + } + } + +/* Free the workspace. */ + lo = astFree( lo ); + hi = astFree( hi ); + } + } + +/* If coefficients for a linear fit were obtained, then free the space + they occupy. */ + if ( linear_fit ) linear_fit = astFree( linear_fit ); + +/* If an error occurred, clear the returned result. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int ResampleSection( AstMapping *this, const double *linear_fit, + int ndim_in, + const int *lbnd_in, const int *ubnd_in, + const void *in, const void *in_var, + DataType type, int interp, void (* finterp)( void ), + const double *params, double factor, int flags, + const void *badval_ptr, int ndim_out, + const int *lbnd_out, const int *ubnd_out, + const int *lbnd, const int *ubnd, + void *out, void *out_var, int *status ) { +/* +* Name: +* ResampleSection + +* Purpose: +* Resample a section of a data grid. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int ResampleSection( AstMapping *this, const double *linear_fit, +* int ndim_in, const int *lbnd_in, const int *ubnd_in, +* const void *in, const void *in_var, +* DataType type, int interp, void (* finterp)( void ), +* const double *params, double factor, int flags, +* const void *badval_ptr, int ndim_out, +* const int *lbnd_out, const int *ubnd_out, +* const int *lbnd, const int *ubnd, +* void *out, void *out_var ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function resamples a rectangular grid of data (with any +* number of dimensions) into a specified section of another +* rectangular grid (with a possibly different number of +* dimensions). The coordinate transformation used is given by the +* inverse transformation of the Mapping which is supplied or, +* alternatively, by a linear approximation fitted to a Mapping's +* inverse transformation. Any pixel interpolation scheme may be +* specified for interpolating between the pixels of the input +* grid. + +* Parameters: +* this +* Pointer to a Mapping, whose inverse transformation may be +* used to transform the coordinates of pixels in the output +* grid into associated positions in the input grid, from which +* the output pixel values should be derived (by interpolation +* if necessary). +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* linear_fit +* Pointer to an optional array of double which contains the +* coefficients of a linear fit which approximates the above +* Mapping's inverse coordinate transformation. If this is +* supplied, it will be used in preference to the above Mapping +* when transforming coordinates. This may be used to enhance +* performance in cases where evaluation of the Mapping's +* inverse transformation is expensive. If no linear fit is +* available, a NULL pointer should be supplied. +* +* The way in which the fit coefficients are stored in this +* array and the number of array elements are as defined by the +* astLinearApprox function. +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input data grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input data grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input data grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* in +* Pointer to the input array of data to be resampled (with one +* element for each pixel in the input grid). The numerical type +* of these data should match the "type" value (below). The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and data type as the "in" array), +* which represent estimates of the statistical variance +* associated with each element of the "in" array. If this +* second array is given (along with the corresponding "out_var" +* array), then estimates of the variance of the resampled data +* will also be returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* type +* A value taken from the "DataType" enum, which specifies the +* data type of the input and output arrays containing the +* gridded data (and variance) values. +* interp +* A value selected from a set of pre-defined macros to identify +* which sub-pixel interpolation algorithm should be used. +* finterp +* If "interp" is set to a value which requires a user-supplied +* function, then a pointer to that function shoild be given +* here. Otherwise, this value is not used and may be a NULL +* pointer. +* params +* Pointer to an optional array of parameters that may be passed +* to the interpolation algorithm, if required. If no parameters +* are required, a NULL pointer should be supplied. +* factor +* A factor by which to scale the resampled output data values before +* returning them. If flux is being conserved this should be set to +* the ratio of the output pixel size to the input pixel size in the +* section. Otherwise it should be set to 1.0. +* flags +* The bitwise OR of a set of flag values which provide +* additional control over the resampling operation. +* badval_ptr +* If the AST__USEBAD flag is set (above), this parameter is a +* pointer to a value which is used to identify bad data and/or +* variance values in the input array(s). The referenced value's +* data type must match that of the "in" (and "in_var") +* arrays. Unless the AST__NOBAD flag is set, the same value will +* also be used to flag any output array elements for which +* resampled values could not be obtained. The output arrays(s) +* may be flagged with this value whether or not the AST__USEBAD +* flag is set (the function return value indicates whether any +* such values have been produced). +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output data grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output data grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output data grid in the same way as "lbnd_in" +* and "ubnd_in" define the shape and size of the input grid +* (see above). +* lbnd +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the first pixel in the +* section of the output data grid for which a value is +* required. +* ubnd +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the last pixel in the +* section of the output data grid for which a value is +* required. +* +* Note that "lbnd" and "ubnd" define the shape and position of +* the section of the output grid for which resampled values are +* required. This section should lie wholly within the extent of +* the output grid (as defined by the "lbnd_out" and "ubnd_out" +* arrays). Regions of the output grid lying outside this section +* will not be modified. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the resampled data will be returned. The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the resampled values may be returned. This array will only be +* used if the "in_var" array has been given. +* +* If no output variance estimates are required, a NULL pointer +* should be given. + +* Returned Value: +* The number of output grid points for which no valid output value +* could be obtained. + +* Notes: +* - This function does not take steps to limit memory usage if the +* grids supplied are large. To resample large grids in a more +* memory-efficient way, the ResampleWithBlocking function should +* be used. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific data */ + AstPointSet *pset_in; /* Input PointSet for transformation */ + AstPointSet *pset_out; /* Output PointSet for transformation */ + const double *grad; /* Pointer to gradient matrix of linear fit */ + const double *par; /* Pointer to parameter array */ + const double *zero; /* Pointer to zero point array of fit */ + double **ptr_in; /* Pointer to input PointSet coordinates */ + double **ptr_out; /* Pointer to output PointSet coordinates */ + double *accum; /* Pointer to array of accumulated sums */ + double fwhm; /* Full width half max. of gaussian */ + double lpar[ 1 ]; /* Local parameter array */ + double x1; /* Interim x coordinate value */ + double y1; /* Interim y coordinate value */ + int *dim; /* Pointer to array of output pixel indices */ + int *offset; /* Pointer to array of output pixel offsets */ + int *stride; /* Pointer to array of output grid strides */ + int conserve; /* Conserve flux? */ + int coord_in; /* Loop counter for input dimensions */ + int coord_out; /* Loop counter for output dimensions */ + int done; /* All pixel indices done? */ + int i1; /* Interim offset into "accum" array */ + int i2; /* Final offset into "accum" array */ + int idim; /* Loop counter for dimensions */ + int ix; /* Loop counter for output x coordinate */ + int iy; /* Loop counter for output y coordinate */ + int nbad; /* Number of pixels assigned a bad value */ + int neighb; /* Number of neighbouring pixels */ + int npoint; /* Number of output points (pixels) */ + int off1; /* Interim pixel offset into output array */ + int off; /* Final pixel offset into output array */ + int point; /* Counter for output points (pixels ) */ + int result; /* Result value to be returned */ + int s; /* Temporary variable for strides */ + int usevar; /* Process variance array? */ + void (* gifunc)( void ); /* General interpolation function */ + void (* kernel)( double, const double [], int, double *, int * ); /* Kernel fn. */ + void (* fkernel)( double, const double [], int, double * ); /* User kernel fn. */ + +/* Initialise. */ + result = 0; + +/* Get a pointer to a structure holding thread-specific global data values */ + astGET_GLOBALS(this); + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + pset_in = NULL; + ptr_in = NULL; + neighb = 0; + gifunc = NULL; + kernel = NULL; + fkernel = NULL; + +/* See if we are conserving flux */ + conserve = flags & AST__CONSERVEFLUX; + +/* If we are conserving flux, then we need some way to tell which output + array elements have been assigned a value and which have not. If the + AST__NOBAD flag has been specified then this is not possible to report + an error. */ + if( ( flags & AST__NOBAD ) && conserve ) { + astError( AST__BADFLG, "astResample: Cannot use the AST__NOBAD and " + "AST__CONSERVEFLUX flags together (programming error)." , status); + } + +/* Calculate the number of output points, as given by the product of + the output grid dimensions. */ + for ( npoint = 1, coord_out = 0; coord_out < ndim_out; coord_out++ ) { + npoint *= ubnd[ coord_out ] - lbnd[ coord_out ] + 1; + } + +/* Allocate workspace. */ + offset = astMalloc( sizeof( int ) * (size_t) npoint ); + stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); + if ( astOK ) { + +/* Calculate the stride for each output grid dimension. */ + off = 0; + s = 1; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + stride[ coord_out ] = s; + s *= ubnd_out[ coord_out ] - lbnd_out[ coord_out ] + 1; + } + +/* A linear fit to the Mapping is available. */ +/* ========================================= */ + if ( linear_fit ) { + +/* If a linear fit to the Mapping has been provided, then obtain + pointers to the array of gradients and zero-points comprising the + fit. */ + grad = linear_fit + ndim_in; + zero = linear_fit; + +/* Create a PointSet to hold the input grid coordinates and obtain an + array of pointers to its coordinate data. */ + pset_in = astPointSet( npoint, ndim_in, "", status ); + ptr_in = astGetPoints( pset_in ); + if ( astOK ) { + +/* Initialise the count of output points. */ + point = 0; + +/* Handle the 1-dimensional case optimally. */ +/* ---------------------------------------- */ + if ( ( ndim_in == 1 ) && ( ndim_out == 1 ) ) { + +/* Loop through the pixels of the output grid and transform their x + coordinates into the input grid's coordinate system using the + linear fit supplied. Store the results in the PointSet created + above. */ + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_in[ 0 ][ point ] = zero[ 0 ] + grad[ 0 ] * (double) ix; + +/* Calculate the offset of each pixel within the output array. */ + offset[ point ] = ix - lbnd_out[ 0 ]; + point++; + } + +/* Handle the 2-dimensional case optimally. */ +/* ---------------------------------------- */ + } else if ( ( ndim_in == 2 ) && ( ndim_out == 2 ) ) { + +/* Loop through the range of y coordinates in the output grid and + calculate interim values of the input coordinates using the linear + fit supplied. */ + for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { + x1 = zero[ 0 ] + grad[ 1 ] * (double) iy; + y1 = zero[ 1 ] + grad[ 3 ] * (double) iy; + +/* Also calculate an interim pixel offset into the output array. */ + off1 = stride[ 1 ] * ( iy - lbnd_out[ 1 ] ) - lbnd_out[ 0 ]; + +/* Now loop through the range of output x coordinates and calculate + the final values of the input coordinates, storing the results in + the PointSet created above. */ + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_in[ 0 ][ point ] = x1 + grad[ 0 ] * (double) ix; + ptr_in[ 1 ][ point ] = y1 + grad[ 2 ] * (double) ix; + +/* Also calculate final pixel offsets into the output array. */ + offset[ point ] = off1 + ix; + point++; + } + } + +/* Handle other numbers of dimensions. */ +/* ----------------------------------- */ + } else { + +/* Allocate workspace. */ + accum = astMalloc( sizeof( double ) * + (size_t) ( ndim_in * ndim_out ) ); + dim = astMalloc( sizeof( int ) * (size_t) ndim_out ); + if ( astOK ) { + +/* Initialise an array of pixel indices for the output grid which + refer to the first pixel for which we require a value. Also + calculate the offset of this pixel within the output array. */ + off = 0; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + dim[ coord_out ] = lbnd[ coord_out ]; + off += stride[ coord_out ] * + ( dim[ coord_out ] - lbnd_out[ coord_out ] ); + } + +/* To calculate each input grid coordinate we must perform a matrix + multiply on the output grid coordinates (using the gradient matrix) + and then add the zero points. However, since we will usually only + be altering one output coordinate at a time (the least + significant), we can avoid the full matrix multiply by accumulating + partial sums for the most significant output coordinates and only + altering those sums which need to change each time. The zero points + never change, so we first fill the "most significant" end of the + "accum" array with these. */ + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + accum[ ( coord_in + 1 ) * ndim_out - 1 ] = + zero[ coord_in ]; + } + coord_out = ndim_out - 1; + +/* Now loop to process each output pixel. */ + for ( done = 0; !done; point++ ) { + +/* To generate the input coordinate that corresponds to the current + output pixel, we work down from the most significant dimension + whose index has changed since the previous pixel we considered + (given by "coord_out"). For each affected dimension, we accumulate + in "accum" the matrix sum (including the zero point) for that + dimension and all higher output dimensions. We must accumulate a + separate set of sums for each input coordinate we wish to + produce. (Note that for the first pixel we process, all dimensions + are considered "changed", so we start by initialising the whole + "accum" array.) */ + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + i1 = coord_in * ndim_out; + for ( idim = coord_out; idim >= 1; idim-- ) { + i2 = i1 + idim; + accum[ i2 - 1 ] = accum[ i2 ] + + dim[ idim ] * grad[ i2 ]; + } + +/* The input coordinate for each dimension is given by the accumulated + sum for output dimension zero (giving the sum over all output + dimensions). We do not store this in the "accum" array, but assign + the result directly to the coordinate array of the PointSet created + earlier. */ + ptr_in[ coord_in ][ point ] = accum[ i1 ] + + dim[ 0 ] * grad[ i1 ]; + } + +/* Store the offset of the current pixel in the output array. */ + offset[ point ] = off; + +/* Now update the array of pixel indices to refer to the next output + pixel. */ + coord_out = 0; + do { + +/* The least significant index which currently has less than its + maximum value is incremented by one. The offset into the output + array is updated accordingly. */ + if ( dim[ coord_out ] < ubnd[ coord_out ] ) { + dim[ coord_out ]++; + off += stride[ coord_out ]; + break; + +/* Any less significant indices which have reached their maximum value + are returned to their minimum value and the output pixel offset is + decremented appropriately. */ + } else { + dim[ coord_out ] = lbnd[ coord_out ]; + off -= stride[ coord_out ] * + ( ubnd[ coord_out ] - lbnd[ coord_out ] ); + +/* All the output pixels have been processed once the most significant + pixel index has been returned to its minimum value. */ + done = ( ++coord_out == ndim_out ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + accum = astFree( accum ); + dim = astFree( dim ); + } + } + +/* No linear fit to the Mapping is available. */ +/* ========================================== */ + } else { + +/* If flux conseravtion was requested, report an error, since we can only + conserve flux if a linear approximation is available. */ + if( conserve && astOK ) { + astError( AST__CNFLX, "astResampleSection(%s): Flux conservation " + "was requested but cannot be performed because either the Mapping " + "is too non-linear, or the requested tolerance is too small.", status, + astGetClass( this ) ); + } + +/* Create a PointSet to hold the coordinates of the output pixels and + obtain a pointer to its coordinate data. */ + pset_out = astPointSet( npoint, ndim_out, "", status ); + ptr_out = astGetPoints( pset_out ); + if ( astOK ) { + +/* Initialise the count of output points. */ + point = 0; + +/* Handle the 1-dimensional case optimally. */ +/* ---------------------------------------- */ + if ( ndim_out == 1 && ndim_in == 1 ) { + +/* Loop through the required range of output x coordinates, assigning + the coordinate values to the PointSet created above. Also store a + pixel offset into the output array. */ + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_out[ 0 ][ point ] = (double) ix; + offset[ point ] = ix - lbnd_out[ 0 ]; + +/* Increment the count of output pixels. */ + point++; + } + +/* Handle the 2-dimensional case optimally. */ +/* ---------------------------------------- */ + } else if ( ndim_out == 2 && ndim_in == 2 ) { + +/* Loop through the required range of output y coordinates, + calculating an interim pixel offset into the output array. */ + for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { + off1 = stride[ 1 ] * ( iy - lbnd_out[ 1 ] ) - lbnd_out[ 0 ]; + +/* Loop through the required range of output x coordinates, assigning + the coordinate values to the PointSet created above. Also store a + final pixel offset into the output array. */ + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_out[ 0 ][ point ] = (double) ix; + ptr_out[ 1 ][ point ] = (double) iy; + offset[ point ] = off1 + ix; + +/* Increment the count of output pixels. */ + point++; + } + } + +/* Handle other numbers of dimensions. */ +/* ----------------------------------- */ + } else { + +/* Allocate workspace. */ + dim = astMalloc( sizeof( int ) * (size_t) ndim_out ); + if ( astOK ) { + +/* Initialise an array of pixel indices for the output grid which + refer to the first pixel for which we require a value. Also + calculate the offset of this pixel within the output array. */ + off = 0; + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + dim[ coord_out ] = lbnd[ coord_out ]; + off += stride[ coord_out ] * + ( dim[ coord_out ] - lbnd_out[ coord_out ] ); + } + +/* Loop to generate the coordinates of each output pixel. */ + for ( done = 0; !done; point++ ) { + +/* Copy each pixel's coordinates into the PointSet created above. */ + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + ptr_out[ coord_out ][ point ] = + (double) dim[ coord_out ]; + } + +/* Store the offset of the pixel in the output array. */ + offset[ point ] = off; + +/* Now update the array of pixel indices to refer to the next output + pixel. */ + coord_out = 0; + do { + +/* The least significant index which currently has less than its + maximum value is incremented by one. The offset into the output + array is updated accordingly. */ + if ( dim[ coord_out ] < ubnd[ coord_out ] ) { + dim[ coord_out ]++; + off += stride[ coord_out ]; + break; + +/* Any less significant indices which have reached their maximum value + are returned to their minimum value and the output pixel offset is + decremented appropriately. */ + } else { + dim[ coord_out ] = lbnd[ coord_out ]; + off -= stride[ coord_out ] * + ( ubnd[ coord_out ] - lbnd[ coord_out ] ); + +/* All the output pixels have been processed once the most significant + pixel index has been returned to its minimum value. */ + done = ( ++coord_out == ndim_out ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + dim = astFree( dim ); + } + +/* When all the output pixel coordinates have been generated, use the + Mapping's inverse transformation to generate the input coordinates + from them. Obtain an array of pointers to the resulting coordinate + data. */ + pset_in = astTransform( this, pset_out, 0, NULL ); + ptr_in = astGetPoints( pset_in ); + } + +/* Annul the PointSet containing the output coordinates. */ + pset_out = astAnnul( pset_out ); + } + } + +/* Resample the input grid. */ +/* ------------------------ */ +/* Determine if a variance array is to be processed. */ + usevar = ( in_var && out_var ); + +/* If the input coordinates have been produced successfully, identify + the input grid resampling method to be used. */ + if ( astOK ) { + +/* Nearest pixel. */ +/* -------------- */ + switch ( interp ) { + case AST__NEAREST: + +/* Define a macro to use a "case" statement to invoke the + nearest-pixel interpolation function appropriate to a given data + type. */ +#define CASE_NEAREST(X,Xtype) \ + case ( TYPE_##X ): \ + result = \ + InterpolateNearest##X( ndim_in, lbnd_in, ubnd_in, \ + (Xtype *) in, (Xtype *) in_var, \ + npoint, offset, \ + (const double *const *) ptr_in, \ + flags, *( (Xtype *) badval_ptr ), \ + (Xtype *) out, (Xtype *) out_var, status ); \ + break; + +/* Use the above macro to invoke the appropriate function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_NEAREST(LD,long double) +#endif + CASE_NEAREST(D,double) + CASE_NEAREST(F,float) + CASE_NEAREST(L,long int) + CASE_NEAREST(UL,unsigned long int) + CASE_NEAREST(K,INT_BIG) + CASE_NEAREST(UK,UINT_BIG) + CASE_NEAREST(I,int) + CASE_NEAREST(UI,unsigned int) + CASE_NEAREST(S,short int) + CASE_NEAREST(US,unsigned short int) + CASE_NEAREST(B,signed char) + CASE_NEAREST(UB,unsigned char) + } + break; + +/* Undefine the macro. */ +#undef CASE_NEAREST + +/* Linear interpolation. */ +/* --------------------- */ +/* Note this is also the default if zero is given. */ + case AST__LINEAR: + case 0: + +/* Define a macro to use a "case" statement to invoke the linear + interpolation function appropriate to a given data type. */ +#define CASE_LINEAR(X,Xtype) \ + case ( TYPE_##X ): \ + result = \ + InterpolateLinear##X( ndim_in, lbnd_in, ubnd_in,\ + (Xtype *) in, (Xtype *) in_var, \ + npoint, offset, \ + (const double *const *) ptr_in, \ + flags, *( (Xtype *) badval_ptr ), \ + (Xtype *) out, (Xtype *) out_var, status ); \ + break; + +/* Use the above macro to invoke the appropriate function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_LINEAR(LD,long double) +#endif + CASE_LINEAR(D,double) + CASE_LINEAR(F,float) + CASE_LINEAR(L,long int) + CASE_LINEAR(UL,unsigned long int) + CASE_LINEAR(K,INT_BIG) + CASE_LINEAR(UK,UINT_BIG) + CASE_LINEAR(I,int) + CASE_LINEAR(UI,unsigned int) + CASE_LINEAR(S,short int) + CASE_LINEAR(US,unsigned short int) + CASE_LINEAR(B,signed char) + CASE_LINEAR(UB,unsigned char) + } + break; + +/* Undefine the macro. */ +#undef CASE_LINEAR + +/* Interpolation using a 1-d kernel. */ +/* --------------------------------- */ + case AST__GAUSS: + case AST__SINC: + case AST__SINCCOS: + case AST__SINCGAUSS: + case AST__SINCSINC: + case AST__SOMB: + case AST__SOMBCOS: + case AST__UKERN1: /* User-supplied 1-d kernel function */ + +/* Obtain a pointer to the appropriate 1-d kernel function (either + internal or user-defined) and set up any parameters it may + require. */ + par = NULL; + switch ( interp ) { + +/* sinc(pi*x) interpolation. */ +/* ------------------------- */ +/* Assign the kernel function. */ + case AST__SINC: + kernel = Sinc; + +/* Calculate the number of neighbouring pixels to use. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) { + neighb = 2; + } else { + neighb = MaxI( 1, neighb, status ); + } + break; + +/* sinc(pi*x)*cos(k*pi*x) interpolation. */ +/* ------------------------------------- */ +/* Assign the kernel function. */ + case AST__SINCCOS: + kernel = SincCos; + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, the number will be calculated automatically below. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = INT_MAX; + +/* Calculate the maximum number of neighbouring pixels required by the + width of the kernel, and use this value if preferable. */ + neighb = MinI( neighb, + (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); + break; + +/* somb(pi*x) interpolation. */ +/* ------------------------- */ +/* Assign the kernel function. */ + case AST__SOMB: + kernel = Somb; + +/* Calculate the number of neighbouring pixels to use. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) { + neighb = 2; + } else { + neighb = MaxI( 1, neighb, status ); + } + break; + +/* somb(pi*x)*cos(k*pi*x) interpolation. */ +/* ------------------------------------- */ +/* Assign the kernel function. */ + case AST__SOMBCOS: + kernel = SombCos; + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, the number will be calculated automatically below. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = INT_MAX; + +/* Calculate the maximum number of neighbouring pixels required by the + width of the kernel, and use this value if preferable. */ + neighb = MinI( neighb, + (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); + break; + +/* sinc(pi*x)*exp(-k*x*x) interpolation. */ +/* ------------------------------------- */ +/* Assign the kernel function. */ + case AST__SINCGAUSS: + kernel = SincGauss; + +/* Constrain the full width half maximum of the gaussian factor. */ + fwhm = MaxD( 0.1, params[ 1 ], status ); + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, use the number of neighbouring pixels required by the width + of the kernel (out to where the gaussian term falls to 1% of its + peak value). */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / + lpar[ 0 ] ) ); + break; + +/* exp(-k*x*x) interpolation. */ +/* -------------------------- */ +/* Assign the kernel function. */ + case AST__GAUSS: + kernel = Gauss; + +/* Constrain the full width half maximum of the gaussian. */ + fwhm = MaxD( 0.1, params[ 1 ], status ); + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, use the number of neighbouring pixels required by the width + of the kernel (out to where the gaussian term falls to 1% of its + peak value). */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / + lpar[ 0 ] ) ); + break; + +/* sinc(pi*x)*sinc(k*pi*x) interpolation. */ +/* -------------------------------------- */ +/* Assign the kernel function. */ + case AST__SINCSINC: + kernel = SincSinc; + +/* Store the required value of "k" in a local parameter array and pass + this array to the kernel function. */ + lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); + par = lpar; + +/* Obtain the number of neighbouring pixels to use. If this is zero or + less, the number will be calculated automatically below. */ + neighb = (int) floor( params[ 0 ] + 0.5 ); + if ( neighb <= 0 ) neighb = INT_MAX; + +/* Calculate the maximum number of neighbouring pixels required by the + width of the kernel, and use this value if preferable. */ + neighb = MinI( neighb, + (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); + break; + +/* User-supplied kernel. */ +/* --------------------- */ +/* Assign the kernel function. */ + case AST__UKERN1: + fkernel = (void (*)( double, const double [], + int, double * )) finterp; + +/* Calculate the number of neighbouring pixels to use. */ + neighb = MaxI( 1, (int) floor( params[ 0 ] + 0.5 ), status ); + +/* Pass a pointer to the "params" array. */ + par = params; + break; + } + +/* Define a macro to use a "case" statement to invoke the 1-d kernel + interpolation function appropriate to a given data type, passing it + the pointer to the kernel function obtained above. */ +#define CASE_KERNEL1(X,Xtype) \ + case ( TYPE_##X ): \ + result = \ + InterpolateKernel1##X( this, ndim_in, lbnd_in, ubnd_in, \ + (Xtype *) in, (Xtype *) in_var, \ + npoint, offset, \ + (const double *const *) ptr_in, \ + kernel, fkernel, neighb, par, flags, \ + *( (Xtype *) badval_ptr ), \ + (Xtype *) out, (Xtype *) out_var, status ); \ + break; + +/* Use the above macro to invoke the appropriate function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_KERNEL1(LD,long double) +#endif + CASE_KERNEL1(D,double) + CASE_KERNEL1(F,float) + CASE_KERNEL1(L,long int) + CASE_KERNEL1(UL,unsigned long int) + CASE_KERNEL1(K,INT_BIG) + CASE_KERNEL1(UK,UINT_BIG) + CASE_KERNEL1(I,int) + CASE_KERNEL1(UI,unsigned int) + CASE_KERNEL1(S,short int) + CASE_KERNEL1(US,unsigned short int) + CASE_KERNEL1(B,signed char) + CASE_KERNEL1(UB,unsigned char) + } + break; + +/* Undefine the macro. */ +#undef CASE_KERNEL1 + +/* General sub-pixel interpolation function. */ +/* ----------------------------------------- */ + case AST__BLOCKAVE: + case AST__UINTERP: + +/* Define a macro to use a "case" statement to invoke the general + sub-pixel interpolation function appropriate to a given type and + the selected value of the interp variable. */ +#define CASE_GINTERP(X,Xtype) \ + case ( TYPE_##X ): \ +\ +/* Obtain a pointer to the appropriate general interpolation function \ + (either internal or user-defined) and set up any parameters it may \ + require. */ \ + switch ( interp ) { \ +\ +/* Block averaging interpolation. */ \ +/* ------------------------------ */ \ + case AST__BLOCKAVE: \ + gifunc = (void (*)( void )) InterpolateBlockAverage##X; \ + break; \ +\ +/* User-supplied sub-pixel interpolation function. */ \ +/* ----------------------------------------------- */ \ + case AST__UINTERP: \ + gifunc = (void (*)( void )) finterp; \ + break; \ + } \ +\ +/* Invoke the general interpolation function. It has to be cast to the \ + right type (i.e. a function with the correctly typed arguments) \ + to prevent default promotion (to int or double) of its arguments. \ + The cast here corresponds to the declaration of + ast_resample_uinterp##Xtype. */ \ + ( *( (void (*)( int, const int[], const int[], \ + const Xtype[], \ + const Xtype[], \ + int, const int[], \ + const double *const[], \ + const double[], int, \ + Xtype, \ + Xtype *, \ + Xtype *, \ + int * )) \ + gifunc ) )( ndim_in, lbnd_in, ubnd_in, \ + (Xtype *) in, \ + (Xtype *) ( usevar ? in_var : NULL ), \ + npoint, offset, \ + (const double *const *) ptr_in, \ + params, flags, \ + *( (Xtype *) badval_ptr ), \ + (Xtype *) out, \ + (Xtype *) ( usevar ? out_var : NULL ), \ + &nbad ); \ + if ( astOK ) { \ + result += nbad; \ + } else { \ + astError( astStatus, "astResample"#X"(%s): Error " \ + "signalled by user-supplied sub-pixel " \ + "interpolation function.", status, \ + astGetClass( unsimplified_mapping ) ); \ + } \ + break; + +/* Use the above macro to invoke the function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_GINTERP(LD,long double) +#endif + CASE_GINTERP(D,double) + CASE_GINTERP(F,float) + CASE_GINTERP(L,long int) + CASE_GINTERP(UL,unsigned long int) + CASE_GINTERP(K,INT_BIG) + CASE_GINTERP(UK,UINT_BIG) + CASE_GINTERP(I,int) + CASE_GINTERP(UI,unsigned int) + CASE_GINTERP(S,short int) + CASE_GINTERP(US,unsigned short int) + CASE_GINTERP(B,signed char) + CASE_GINTERP(UB,unsigned char) + } + break; + +/* Undefine the macro. */ +#undef CASE_GINTERP + +/* Error: invalid interpolation scheme specified. */ +/* ---------------------------------------------- */ + default: + +/* Define a macro to report an error message appropriate to a given + data type. */ +#define CASE_ERROR(X) \ + case TYPE_##X: \ + astError( AST__SISIN, "astResample"#X"(%s): Invalid " \ + "sub-pixel interpolation scheme (%d) specified.", status, \ + astGetClass( unsimplified_mapping ), interp ); \ + break; + +/* Use the above macro to report an appropriate error message. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_ERROR(LD) +#endif + CASE_ERROR(D) + CASE_ERROR(F) + CASE_ERROR(L) + CASE_ERROR(UL) + CASE_ERROR(K) + CASE_ERROR(UK) + CASE_ERROR(I) + CASE_ERROR(UI) + CASE_ERROR(S) + CASE_ERROR(US) + CASE_ERROR(B) + CASE_ERROR(UB) + } + break; + +/* Undefine the macro. */ +#undef CASE_ERROR + } + } + +/* Now scale the output values to conserve flux if required. */ + if( conserve ) { + +/* Define a macro to use a "case" statement to invoke the function + appropriate to a given data type. These simply multiple the output data + value by the factor, and the output variance by the square of the + factor. */ +#define CASE_CONSERVE(X,Xtype) \ + case ( TYPE_##X ): \ + ConserveFlux##X( factor, npoint, offset, *( (Xtype *) badval_ptr ), \ + (Xtype *) out, \ + (Xtype *) ( usevar ? out_var : NULL ), status ); \ + break; + +/* Use the above macro to invoke the appropriate function. */ + switch ( type ) { +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + CASE_CONSERVE(LD,long double) +#endif + CASE_CONSERVE(D,double) + CASE_CONSERVE(F,float) + CASE_CONSERVE(L,long int) + CASE_CONSERVE(UL,unsigned long int) + CASE_CONSERVE(K,INT_BIG) + CASE_CONSERVE(UK,UINT_BIG) + CASE_CONSERVE(I,int) + CASE_CONSERVE(UI,unsigned int) + CASE_CONSERVE(S,short int) + CASE_CONSERVE(US,unsigned short int) + CASE_CONSERVE(B,signed char) + CASE_CONSERVE(UB,unsigned char) + } + +/* Undefine the macro. */ +#undef CASE_CONSERVE + } + +/* Annul the PointSet used to hold input coordinates. */ + pset_in = astAnnul( pset_in ); + +/* Free the workspace. */ + offset = astFree( offset ); + stride = astFree( stride ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int ResampleWithBlocking( AstMapping *this, const double *linear_fit, + int ndim_in, + const int *lbnd_in, const int *ubnd_in, + const void *in, const void *in_var, + DataType type, int interp, void (* finterp)( void ), + const double *params, int flags, + const void *badval_ptr, int ndim_out, + const int *lbnd_out, const int *ubnd_out, + const int *lbnd, const int *ubnd, + void *out, void *out_var, int *status ) { +/* +* Name: +* ResampleWithBlocking + +* Purpose: +* Resample a section of a data grid in a memory-efficient way. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int ResampleWithBlocking( AstMapping *this, const double *linear_fit, +* int ndim_in, +* const int *lbnd_in, const int *ubnd_in, +* const void *in, const void *in_var, +* DataType type, int interp, void (* finterp)( void ), +* const double *params, int flags, +* const void *badval_ptr, int ndim_out, +* const int *lbnd_out, const int *ubnd_out, +* const int *lbnd, const int *ubnd, +* void *out, void *out_var, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function resamples a rectangular grid of data (with any +* number of dimensions) into a specified section of another +* rectangular grid (with a possibly different number of +* dimensions). The coordinate transformation used is given by the +* inverse transformation of the Mapping which is supplied or, +* alternatively, by a linear approximation fitted to a Mapping's +* inverse transformation. Any pixel interpolation scheme may be +* specified for interpolating between the pixels of the input +* grid. +* +* This function is very similar to ResampleSection, except that in +* order to limit memory usage and to ensure locality of reference, +* it divides the output grid up into "blocks" which have a limited +* extent along each output dimension. Each block, which will not +* contain more than a pre-determined maximum number of pixels, is +* then passed to ResampleSection for resampling. + +* Parameters: +* this +* Pointer to a Mapping, whose inverse transformation may be +* used to transform the coordinates of pixels in the output +* grid into associated positions in the input grid, from which +* the output pixel values should be derived (by interpolation +* if necessary). +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* linear_fit +* Pointer to an optional array of double which contains the +* coefficients of a linear fit which approximates the above +* Mapping's inverse coordinate transformation. If this is +* supplied, it will be used in preference to the above Mapping +* when transforming coordinates. This may be used to enhance +* performance in cases where evaluation of the Mapping's +* inverse transformation is expensive. If no linear fit is +* available, a NULL pointer should be supplied. +* +* The way in which the fit coefficients are stored in this +* array and the number of array elements are as defined by the +* astLinearApprox function. +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input data grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input data grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input data grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* in +* Pointer to the input array of data to be resampled (with one +* element for each pixel in the input grid). The numerical type +* of these data should match the "type" value (below). The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and data type as the "in" array), +* which represent estimates of the statistical variance +* associated with each element of the "in" array. If this +* second array is given (along with the corresponding "out_var" +* array), then estimates of the variance of the resampled data +* will also be returned. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* type +* A value taken from the "DataType" enum, which specifies the +* data type of the input and output arrays containing the +* gridded data (and variance) values. +* interp +* A value selected from a set of pre-defined macros to identify +* which sub-pixel interpolation algorithm should be used. +* finterp +* If "interp" is set to a value which requires a user-supplied +* function, then a pointer to that function shoild be given +* here. Otherwise, this value is not used and may be a NULL +* pointer. +* params +* Pointer to an optional array of parameters that may be passed +* to the interpolation algorithm, if required. If no parameters +* are required, a NULL pointer should be supplied. +* flags +* The bitwise OR of a set of flag values which provide +* additional control over the resampling operation. +* badval_ptr +* If the AST__USEBAD flag is set (above), this parameter is a +* pointer to a value which is used to identify bad data and/or +* variance values in the input array(s). The referenced value's +* data type must match that of the "in" (and "in_var") +* arrays. Unless the AST__NOBAD flag is set, the same value will +* also be used to flag any output array elements for which +* resampled values could not be obtained. The output arrays(s) +* may be flagged with this value whether or not the AST__USEBAD +* flag is set (the function return value indicates whether any +* such values have been produced). +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output data grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output data grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output data grid in the same way as "lbnd_in" +* and "ubnd_in" define the shape and size of the input grid +* (see above). +* lbnd +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the first pixel in the +* section of the output data grid for which a value is +* required. +* ubnd +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the last pixel in the +* section of the output data grid for which a value is +* required. +* +* Note that "lbnd" and "ubnd" define the shape and position of +* the section of the output grid for which resampled values are +* required. This section should lie wholly within the extent of +* the output grid (as defined by the "lbnd_out" and "ubnd_out" +* arrays). Regions of the output grid lying outside this section +* will not be modified. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the resampled data will be returned. The +* storage order should be such that the coordinate of the first +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order is used). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the resampled values may be returned. This array will only be +* used if the "in_var" array has been given. +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of output grid points for which no valid output value +* could be obtained. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Constants: */ + const int mxpix = 2 * 1024; /* Maximum number of pixels in a block (this + relatively small number seems to give best + performance) */ + +/* Local Variables: */ + double factor; /* Flux conservation factor */ + int *dim_block; /* Pointer to array of block dimensions */ + int *lbnd_block; /* Pointer to block lower bound array */ + int *ubnd_block; /* Pointer to block upper bound array */ + int dim; /* Dimension size */ + int done; /* All blocks resampled? */ + int hilim; /* Upper limit on maximum block dimension */ + int idim; /* Loop counter for dimensions */ + int lolim; /* Lower limit on maximum block dimension */ + int mxdim_block; /* Maximum block dimension */ + int npix; /* Number of pixels in block */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate workspace. */ + lbnd_block = astMalloc( sizeof( int ) * (size_t) ndim_out ); + ubnd_block = astMalloc( sizeof( int ) * (size_t) ndim_out ); + dim_block = astMalloc( sizeof( int ) * (size_t) ndim_out ); + if ( astOK ) { + +/* Find the optimum block size. */ +/* ---------------------------- */ +/* We first need to find the maximum extent which a block of output + pixels may have in each dimension. We determine this by taking the + output grid extent in each dimension and then limiting the maximum + dimension size until the resulting number of pixels is sufficiently + small. This approach allows the block shape to approximate (or + match) the output grid shape when appropriate. */ + +/* First loop to calculate the total number of output pixels and the + maximum output dimension size. */ + npix = 1; + mxdim_block = 0; + for ( idim = 0; idim < ndim_out; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + npix *= dim; + if ( mxdim_block < dim ) mxdim_block = dim; + } + +/* If the number of output pixels is too large for a single block, we + perform iterations to determine the optimum upper limit on a + block's dimension size. Initialise the limits on this result. */ + if ( npix > mxpix ) { + lolim = 1; + hilim = mxdim_block; + +/* Loop to perform a binary chop, searching for the best result until + the lower and upper limits on the result converge to adjacent + values. */ + while ( ( hilim - lolim ) > 1 ) { + +/* Form a new estimate from the mid-point of the previous limits. */ + mxdim_block = ( hilim + lolim ) / 2; + +/* See how many pixels a block contains if its maximum dimension is + limited to this new value. */ + for ( npix = 1, idim = 0; idim < ndim_out ; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + npix *= ( dim < mxdim_block ) ? dim : mxdim_block; + } + +/* Update the appropriate limit, according to whether the number of + pixels is too large or too small. */ + *( ( npix <= mxpix ) ? &lolim : &hilim ) = mxdim_block; + } + +/* When iterations have converged, obtain the maximum limit on the + dimension size of a block which results in no more than the maximum + allowed number of pixels per block. However, ensure that all block + dimensions are at least 2. */ + mxdim_block = lolim; + } + if ( mxdim_block < 2 ) mxdim_block = 2; + +/* Calculate the block dimensions by applying this limit to the output + grid dimensions. */ + for ( idim = 0; idim < ndim_out ; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + dim_block[ idim ] = ( dim < mxdim_block ) ? dim : mxdim_block; + +/* Also initialise the lower and upper bounds of the first block of + output grid pixels to be resampled, ensuring that this does not + extend outside the grid itself. */ + lbnd_block[ idim ] = lbnd[ idim ]; + ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + } + +/* Determine the flux conservation constant if needed. */ +/* --------------------------------------------------- */ + if( ( flags & AST__CONSERVEFLUX ) && linear_fit ) { + factor = MatrixDet( ndim_in, ndim_out, linear_fit + ndim_in, status ); + } else { + factor = 1.0; + } + +/* Resample each block of output pixels. */ +/* ------------------------------------- */ +/* Loop to generate the extent of each block of output pixels and to + resample them. */ + done = 0; + while ( !done && astOK ) { + +/* Resample the current block, accumulating the sum of bad pixels + produced. */ + result += ResampleSection( this, linear_fit, + ndim_in, lbnd_in, ubnd_in, + in, in_var, type, interp, finterp, params, + factor, flags, badval_ptr, + ndim_out, lbnd_out, ubnd_out, + lbnd_block, ubnd_block, out, out_var, status ); + +/* Update the block extent to identify the next block of output + pixels. */ + idim = 0; + do { + +/* We find the least significant dimension where the upper bound of + the block has not yet reached the upper bound of the region of the + output grid which we are resampling. The block's position is then + incremented by one block extent along this dimension, checking that + the resulting extent does not go outside the region being + resampled. */ + if ( ubnd_block[ idim ] < ubnd[ idim ] ) { + lbnd_block[ idim ] = MinI( lbnd_block[ idim ] + + dim_block[ idim ], ubnd[ idim ], status ); + ubnd_block[ idim ] = MinI( lbnd_block[ idim ] + + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + break; + +/* If any less significant dimensions are found where the upper bound + of the block has reached its maximum value, we reset the block to + its lowest position. */ + } else { + lbnd_block[ idim ] = lbnd[ idim ]; + ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + +/* All the blocks have been processed once the position along the most + significant dimension has been reset. */ + done = ( ++idim == ndim_out ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + lbnd_block = astFree( lbnd_block ); + ubnd_block = astFree( ubnd_block ); + dim_block = astFree( dim_block ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* Mapping member function (over-rides the astSetAttrib protected +* method inherited from the Object class). + +* Description: +* This function assigns an attribute value for a Mapping, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Mapping. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstMapping *this; /* Pointer to the Mapping structure */ + int invert; /* Invert attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + int report; /* Report attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Mapping structure. */ + this = (AstMapping *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Invert. */ +/* ------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "invert= %d %n", &invert, &nc ) ) + && ( nc >= len ) ) { + astSetInvert( this, invert ); + +/* Report. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "report= %d %n", &report, &nc ) ) + && ( nc >= len ) ) { + astSetReport( this, report ); + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + } else if ( MATCH( "nin" ) || + MATCH( "nout" ) || + MATCH( "islinear" ) || + MATCH( "issimple" ) || + MATCH( "tranforward" ) || + MATCH( "traninverse" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void Sinc( double offset, const double params[], int flags, + double *value, int *status ) { +/* +* Name: +* Sinc + +* Purpose: +* 1-dimensional sinc(pi*x) interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void Sinc( double offset, const double params[], int flags, +* double *value, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of a 1-dimensional sub-pixel +* interpolation kernel. The function used is sinc(pi*x), where +* sinc(z)=sin(z)/z. + +* Parameters: +* offset +* The offset of a pixel from the interpolation point, measured +* in pixels. +* params +* Not used. +* flags +* Not used. +* value +* Pointer to a double to receive the calculated kernel value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Local Variables: */ + static double pi; /* Value of pi */ + static int init = 0; /* Initialisation flag */ + +/* On the first invocation, initialise a local value for pi. Do this + only once. */ + if ( !init ) { + pi = acos( -1.0 ); + init = 1; + } + +/* Scale the offset. */ + offset *= pi; + +/* Evaluate the function. */ + *value = ( offset != 0.0 ) ? ( sin( offset ) / offset ) : 1.0; +} + +static void SincCos( double offset, const double params[], int flags, + double *value, int *status ) { +/* +* Name: +* SincCos + +* Purpose: +* 1-dimensional sinc(pi*x)*cos(k*pi*x) interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SincCos( double offset, const double params[], int flags, +* double *value, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of a 1-dimensional sub-pixel +* interpolation kernel. The function used is sinc(pi*x)*cos(k*pi*x) +* out to the point where cos(k*pi*x) = 0, and zero beyond. Here, +* sinc(z)=sin(z)/z. + +* Parameters: +* offset +* The offset of a pixel from the interpolation point, measured +* in pixels. +* params +* The first element of this array should give a value for "k" +* in the cos(k*pi*x) term. +* flags +* Not used. +* value +* Pointer to a double to receive the calculated kernel value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Local Variables: */ + double offset_k; /* Scaled offset */ + static double halfpi; /* Value of pi/2 */ + static double pi; /* Value of pi */ + static int init = 0; /* Initialisation flag */ + +/* On the first invocation, initialise local values for pi and + pi/2. Do this only once. */ + if ( !init ) { + pi = acos( -1.0 ); + halfpi = 0.5 * pi; + init = 1; + } + +/* Multiply the offset by pi and remove its sign. */ + offset = pi * fabs( offset ); + +/* Find the offset scaled by the "k" factor. */ + offset_k = offset * params[ 0 ]; + +/* If the cos(k*pi*x) term has not reached zero, calculate the + result. */ + if ( offset_k < halfpi ) { + *value = ( ( offset != 0.0 ) ? ( sin( offset ) / offset ) : 1.0 ) * + cos( offset_k ); + +/* Otherwise, the result is zero. */ + } else { + *value = 0.0; + } +} + +static void SincGauss( double offset, const double params[], int flags, + double *value, int *status ) { +/* +* Name: +* SincGauss + +* Purpose: +* 1-dimensional sinc(pi*x)*exp(-k*x*x) interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SincGauss( double offset, const double params[], int flags, +* double *value, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of a 1-dimensional sub-pixel +* interpolation kernel. The function used is sinc(pi*x)*exp(-k*x*x), +* where sinc(z)=sin(z)/z. + +* Parameters: +* offset +* The offset of a pixel from the interpolation point, measured +* in pixels. +* params +* The first element of this array should give a value for "k" +* in the exp(-k*x*x) term. +* flags +* Not used. +* value +* Pointer to a double to receive the calculated kernel value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Local Variables: */ + double offset_pi; /* Offset multiplied by pi */ + static double pi; /* Value of pi */ + static int init = 0; /* Initialisation flag */ + +/* On the first invocation, initialise a local value for pi. Do this + only once. */ + if ( !init ) { + pi = acos( -1.0 ); + init = 1; + } + +/* Find the offset scaled by pi. */ + offset_pi = pi * offset; + +/* Calculate the result. */ + *value = ( ( offset_pi != 0.0 ) ? ( sin( offset_pi ) / offset_pi ) : 1.0 ) * + exp( -params[ 0 ] * offset * offset ); +} + +static void SincSinc( double offset, const double params[], int flags, + double *value, int *status ) { +/* +* Name: +* SincSinc + +* Purpose: +* 1-dimensional sinc(pi*x)*sinc(k*pi*x) interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SincSinc( double offset, const double params[], int flags, +* double *value, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of a 1-dimensional sub-pixel +* interpolation kernel. The function used is sinc(pi*x)*sinc(k*pi*x), +* out to the point where sinc(k*pi*x)=0, and zero beyond. Here, +* sinc(z)=sin(z)/z. + +* Parameters: +* offset +* The offset of a pixel from the interpolation point, measured +* in pixels. +* params +* The first element of this array should give a value for "k" +* in the sinc(k*pi*x) term. +* flags +* Not used. +* value +* Pointer to a double to receive the calculated kernel value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Local Variables: */ + double offset_k; /* Scaled offset */ + static double halfpi; /* Value of pi/2 */ + static double pi; /* Value of pi */ + static int init = 0; /* Initialisation flag */ + +/* On the first invocation, initialise local values for pi and + pi/2. Do this only once. */ + if ( !init ) { + pi = acos( -1.0 ); + halfpi = 0.5 * pi; + init = 1; + } + +/* Multiply the offset by pi and remove its sign. */ + offset = pi * fabs( offset ); + +/* Find the offset scaled by the "k" factor. */ + offset_k = offset * params[ 0 ]; + +/* If the sinc(k*pi*x) term has not reached zero, calculate the + result. */ + if ( offset_k < halfpi ) { + *value = ( ( offset != 0.0 ) ? ( sin( offset ) / offset ) : 1.0 ) * + ( ( offset_k != 0.0 ) ? ( sin( offset_k ) / offset_k ) : 1.0 ); + +/* Otherwise, the result is zero. */ + } else { + *value = 0.0; + } +} + +static AstMapping *Simplify( AstMapping *this, int *status ) { +/* +*++ +* Name: +c astSimplify +f AST_SIMPLIFY + +* Purpose: +* Simplify a Mapping. + +* Type: +* Public function. + +* Synopsis: +c #include "mapping.h" +c AstMapping *astSimplify( AstMapping *this ) +f RESULT = AST_SIMPLIFY( THIS, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +* This function simplifies a Mapping (which may be a compound +* Mapping such as a CmpMap) to eliminate redundant computational +* steps, or to merge separate steps which can be performed more +* efficiently in a single operation. +* +* As a simple example, a Mapping which multiplied coordinates by +* 5, and then multiplied the result by 10, could be simplified to +* a single step which multiplied by 50. Similarly, a Mapping which +* multiplied by 5, and then divided by 5, could be reduced to a +* simple copying operation. +* +* This function should typically be applied to Mappings which have +* undergone substantial processing or have been formed by merging +* other Mappings. It is of potential benefit, for example, in +* reducing execution time if applied before using a Mapping to +* transform a large number of coordinates. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the original Mapping. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSimplify() +f AST_SIMPLIFY = INTEGER +* A new pointer to the (possibly simplified) Mapping. + +* Applicability: +* Mapping +* This function applies to all Mappings. +* FrameSet +* If the supplied Mapping is a FrameSet, the returned Mapping +* will be a copy of the supplied FrameSet in which all the +* inter-Frame Mappings have been simplified. + +* Notes: +* - Mappings that have a set value for their Ident attribute are +* left unchanged after simplification. This is so that their +* individual identity is preserved. This restriction does not +* apply to the simplification of Frames. +* - This function can safely be applied even to Mappings which +* cannot be simplified. If no simplification is possible, it +c behaves exactly like astClone and returns a pointer to the +f behaves exactly like AST_CLONE and returns a pointer to the +* original Mapping. +* - The Mapping returned by this function may not be independent +* of the original (even if simplification was possible), and +* modifying it may therefore result in indirect modification of +* the original. If a completely independent result is required, a +c copy should be made using astCopy. +f copy should be made using AST_COPY. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstMapping **map_list; /* Pointer to array of Mapping pointers */ + AstMapping *map; /* Cloned pointer to nominated Mapping */ + AstMapping *result; /* Pointer to result Mapping */ + int *invert_list; /* Pointer to array of invert flags */ + int imap; /* Loop counter for Mappings */ + int modified; /* Index of first modified element */ + int nmap; /* Number of Mappings */ + int simpler; /* Simplification achieved? */ + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Initialise dynamic arrays of Mapping pointers and associated invert + flags. */ + nmap = 0; + map_list = NULL; + invert_list = NULL; + +/* Build a Mapping list to contain this Mapping (the list should only + have 1 element). */ + astMapList( this, 1, astGetInvert( this ), &nmap, &map_list, &invert_list ); + +/* Pass the list repeatedly to the "astMapMerge" method for + simplification. */ + simpler = 0; + while ( astOK ) { + map = astClone( map_list[ 0 ] ); + modified = astMapMerge( map, 0, 1, &nmap, &map_list, &invert_list ); + map = astAnnul( map ); + +/* Quit looping if the number of Mappings increases above 1, or if no + further change occurs. Note if any simplification was achieved. */ + if ( ( nmap > 1 ) || ( modified < 0 ) ) break; + simpler = 1; + } + +/* Check whether simplification has occurred. If not, simply clone the + original Mapping pointer. This is what will normally happen for + Mapping classes which inherit the default (null) "astMapMerge" + method from this class and do not define one of their own. */ + if ( astOK ) { + if ( !simpler || ( nmap > 1 ) ) { + result = astClone( this ); + +/* If simplification occurred, test if the resulting Mapping has the + Invert attribute value we want. If so, we can simply clone a + pointer to it. */ + } else { + if ( invert_list[ 0 ] == astGetInvert( map_list[ 0 ] ) ) { + result = astClone( map_list[ 0 ] ); + +/* If not, we must make a copy. */ + } else { + result = astCopy( map_list[ 0 ] ); + +/* Either clear the copy's Invert attribute, or set it to 1, as + required. */ + if ( invert_list[ 0 ] ) { + astSetInvert( result, 1 ); + } else { + astClearInvert( result ); + } + } + } + } + +/* Loop to annul all the pointers in the Mapping list. */ + for ( imap = 0; imap < nmap; imap++ ) { + map_list[ imap ] = astAnnul( map_list[ imap ] ); + } + +/* Free the dynamic arrays. */ + map_list = astFree( map_list ); + invert_list = astFree( invert_list ); + +/* If an error occurred, annul the returned Mapping. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void Somb( double offset, const double params[], int flags, + double *value, int *status ) { +/* +* Name: +* Somb + +* Purpose: +* 1-dimensional somb(pi*x) interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void Somb( double offset, const double params[], int flags, +* double *value, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of a 1-dimensional sub-pixel +* interpolation kernel. The function used is somb(pi*x), where +* somb(z)=2*J1(z)/z (J1 is a Bessel function of the first kind of +* order 1). + +* Parameters: +* offset +* The offset of a pixel from the interpolation point, measured +* in pixels. +* params +* Not used. +* flags +* Not used. +* value +* Pointer to a double to receive the calculated kernel value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Local Variables: */ + static double pi; /* Value of pi */ + static int init = 0; /* Initialisation flag */ + +/* On the first invocation, initialise a local value for pi. Do this + only once. */ + if ( !init ) { + pi = acos( -1.0 ); + init = 1; + } + +/* Scale the offset. */ + offset *= pi; + +/* Evaluate the function. */ + *value = ( offset != 0.0 ) ? ( 2.0*J1Bessel( offset, status ) / offset ) : 1.0; +} + +static void SombCos( double offset, const double params[], int flags, + double *value, int *status ) { +/* +* Name: +* SombCos + +* Purpose: +* 1-dimensional somb(pi*x)*cos(k*pi*x) interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SombCos( double offset, const double params[], int flags, +* double *value, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function calculates the value of a 1-dimensional sub-pixel +* interpolation kernel. The function used is somb(pi*x)*cos(k*pi*x) +* out to the point where cos(k*pi*x) = 0, and zero beyond. Here, +* somb(z)=2*J1(z)/z (J1 is a Bessel function of the first kind of +* order 1). + +* Parameters: +* offset +* The offset of a pixel from the interpolation point, measured +* in pixels. +* params +* The first element of this array should give a value for "k" +* in the cos(k*pi*x) term. +* flags +* Not used. +* value +* Pointer to a double to receive the calculated kernel value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +*/ + +/* Local Variables: */ + double offset_k; /* Scaled offset */ + static double halfpi; /* Value of pi/2 */ + static double pi; /* Value of pi */ + static int init = 0; /* Initialisation flag */ + +/* On the first invocation, initialise local values for pi and + pi/2. Do this only once. */ + if ( !init ) { + pi = acos( -1.0 ); + halfpi = 0.5 * pi; + init = 1; + } + +/* Multiply the offset by pi and remove its sign. */ + offset = pi * fabs( offset ); + +/* Find the offset scaled by the "k" factor. */ + offset_k = offset * params[ 0 ]; + +/* If the cos(k*pi*x) term has not reached zero, calculate the + result. */ + if ( offset_k < halfpi ) { + *value = ( ( offset != 0.0 ) ? ( J1Bessel( offset, status ) / offset ) : 1.0 ) * + cos( offset_k ); + +/* Otherwise, the result is zero. */ + } else { + *value = 0.0; + } +} + +static int SpecialBounds( const MapData *mapdata, double *lbnd, double *ubnd, + double xl[], double xu[], int *status ) { +/* +* Name: +* SpecialBounds + +* Purpose: +* Estimate coordinate bounds using special points. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int SpecialBounds( const MapData *mapdata, double *lbnd, double *ubnd, +* double xl[], double xu[], int *status ); + +* Class Membership: +* Mapping member function. + +* Description: +* This function makes a rough estimate of the lower and upper +* bounds of a Mapping function over a constrained region of its +* input coordinate space by transforming a set of special test +* points. The points used lie at the corners of the constrained +* region, at the centre of each of its faces, at its centroid, and +* (if within the coordinate constraints) the origin. +* +* In many practical cases, the true extrema may actually lie at +* one or other of these points, in which case the true bounds will +* be found. In other cases, this function only provides an +* approximate limit on each bound (there is no way of telling if +* this is the case, however). In either case, having these initial +* estimates can speed subsequent searches to find the global +* extrema as well as making that search more secure + +* Parameters: +* mapdata +* Pointer to a MapData structure describing the Mapping +* function, its coordinate constraints, etc. +* lbnd +* Pointer to a double. On entry, this should contain a +* previously-obtained upper limit on the lower bound, or +* AST__BAD if no such limit is available. On exit, it will be +* updated with a new estimate of the lower bound, if a better +* one has been found. +* ubnd +* Pointer to a double. On entry, this should contain a +* previously-obtained lower limit on the upper bound, or +* AST__BAD if no such limit is available. On exit, it will be +* updated with a new estimate of the upper bound, if a better +* one has been found. +* xl +* Pointer to an array of double, with one element for each +* input coordinate, in which to return the position of a (not +* necessarily unique) input point at which the lower output +* bound is reached. This array is not altered if an improved +* estimate of the lower bound cannot be found. +* xu +* Pointer to an array of double, with one element for each +* input coordinate, in which to return the position of a (not +* necessarily unique) input point at which the upper output +* bound is reached. This array is not altered if an improved +* estimate of the upper bound cannot be found. +* status +* Pointer to the inherited status variable. + +* Returned: +* A flag indicating if the returned values can be refined. + +*/ + +/* Local Variables: */ + AstPointSet *pset_in; /* PointSet for input coordinates */ + AstPointSet *pset_out; /* PointSet for output coordinates */ + double **ptr_in; /* Pointer to input coordinates */ + double **ptr_out; /* Pointer to output coordinates */ + double *sxl; /* Secondary xl values */ + double *sxu; /* Secondary xu values */ + double f; /* Output coordinate value */ + double slbnd; /* Secondary lbnd value */ + double subnd; /* Secondary lbnd value */ + int *limit; /* Workspace for lower/upper limit flags */ + int bad; /* Output coordinate bad? */ + int coord; /* Loop counter for coordinates */ + int done; /* All corners done? */ + int face; /* Loop counter for faces */ + int ic; /* Index of corner */ + int icen; /* Index of centroid point */ + int ncorner; /* Number of corners */ + int ncoord; /* Number of input coordinates */ + int npoint; /* Number of points */ + int origin; /* Origin lies within bounds? */ + int point; /* Loop counter for points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 1; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + pset_out = NULL; + +/* Obtain the number of coordinate axes and calculate the number of + points required in order to place one at every corner of the + constrained region of the coordinate space. */ + ncoord = mapdata->nin; + for ( npoint = 1, coord = 0; coord < ncoord; coord++ ) npoint *= 2; + +/* Also include a second point at each corner,offset slightly from the + corner towards the centroid */ + ncorner = npoint; + npoint *= 2; + +/* Also include placing one at the centre of every face and one at the + centroid of the constrained coordinate space. */ + npoint += 2 * ncoord + 1; + +/* Determine if the origin lies within the bounds. If so, include it + as a further point. */ + origin = 1; + for ( coord = 0; coord < ncoord; coord++ ) { + if ( ( mapdata->lbnd[ coord ] > 0.0 ) || + ( mapdata->ubnd[ coord ] < 0.0 ) ) { + origin = 0; + break; + } + } + if ( origin ) npoint++; + +/* Initialise secondary bounds to be the supplied primary bounds */ + slbnd = *lbnd; + subnd = *ubnd; + +/* Create workspace for ssecondary xl xu values */ + sxl = astMalloc( sizeof(double)*(size_t) ncoord ); + sxu = astMalloc( sizeof(double)*(size_t) ncoord ); + +/* Create a PointSet to hold the coordinates and obtain a pointer to + its coordinate values. Also allocate workspace for calculating the + corner coordinates. */ + pset_in = astPointSet( npoint, ncoord, "", status ); + ptr_in = astGetPoints( pset_in ); + limit = astMalloc( sizeof( int ) * (size_t) ncoord ); + if ( astOK ) { + +/* Initialise the workspace. */ + for ( coord = 0; coord < ncoord; coord++ ) limit[ coord ] = 0; + +/* Loop to visit every corner. */ + point = 0; + done = 0; + do { + +/* At each corner, translate the contents of the "limit" array + (containing zeros and ones) into the lower or upper bound on the + corresponding axis. This gives the coordinates of the corner, which + we store in the input PointSet. */ + for ( coord = 0; coord < ncoord; coord++ ) { + ptr_in[ coord ][ point ] = limit[ coord ] ? + mapdata->ubnd[ coord ] : + mapdata->lbnd[ coord ]; + } + +/* Increment the count of points (i.e. corners). */ + point++; + +/* Now update the limit array to identify the next corner. */ + coord = 0; + do { + +/* Flip the first zero found to become a one. This gives a new + corner. */ + if ( !limit[ coord ] ) { + limit[ coord ] = 1; + break; + +/* However, first flip any previous ones to become zeros and then + examine the next element. We have processed all corners once the + array is entirely filled with ones. */ + } else { + limit[ coord ] = 0; + done = ( ++coord == ncoord ); + } + } while ( !done ); + } while ( !done ); + +/* Once the corners have been processed, loop to consider the centre + of each face. */ + for ( face = 0; face < ( 2 * ncoord ); face++ ) { + +/* First calculate the centroid value for each coordinate. Then set + one of these coordinates to the bound where the face lies. */ + for ( coord = 0; coord < ncoord; coord++ ) { + ptr_in[ coord ][ point ] = 0.5 * ( mapdata->lbnd[ coord ] + + mapdata->ubnd[ coord ] ); + } + ptr_in[ face / 2 ][ point ] = ( face % 2 ) ? + mapdata->lbnd[ face / 2 ] : + mapdata->ubnd[ face / 2 ]; + +/* Increment the count of points. */ + point++; + } + +/* Place a point at the centroid of the constrained coordinate + space. */ + for ( coord = 0; coord < ncoord; coord++ ) { + ptr_in[ coord ][ point ] = 0.5 * ( mapdata->lbnd[ coord ] + + mapdata->ubnd[ coord ] ); + } + icen = point++; + +/* Add a set of positions which are offset slightly from each corner + towards the centroid. */ + for ( ic = 0; ic < ncorner; ic++ ) { + for ( coord = 0; coord < ncoord; coord++ ) { + ptr_in[ coord ][ point ] = 0.999*ptr_in[ coord ][ ic ] + + 0.001*ptr_in[ coord ][ icen ]; + } + point++; + } + +/* Finally, add the origin, if it lies within the constraints. */ + if ( origin ) { + for ( coord = 0; coord < ncoord; coord++ ) { + ptr_in[ coord ][ point ] = 0.0; + } + } + +/* Once all the input coordinates have been calculated, transform them + and obtain a pointer to the resulting coordinate values. */ + pset_out = astTransform( mapdata->mapping, pset_in, mapdata->forward, + NULL ); + ptr_out = astGetPoints( pset_out ); + if ( astOK ) { + +/* Loop through each point and test if any of its transformed + coordinates is bad. */ + for ( point = 0; point < npoint; point++ ) { + bad = 0; + for ( coord = 0; coord < mapdata->nout; coord++ ) { + if ( ptr_out[ coord ][ point ] == AST__BAD ) { + bad = 1; + break; + } + } + +/* If so, we ignore the point. Otherwise, extract the required + coordinate. */ + f = ptr_out[ mapdata->coord ][ point ]; + if ( !bad ) { + +/* Use this to update the lower and upper bounds we are seeking. If + either bound is updated, also store the coordinates of the + corresponding input point. */ + if ( ( *lbnd == AST__BAD ) || ( f < *lbnd ) ) { + *lbnd = f; + for ( coord = 0; coord < ncoord; coord++ ) { + xl[ coord ] = ptr_in[ coord ][ point ]; + } + } + if ( ( *ubnd == AST__BAD ) || ( f > *ubnd ) ) { + *ubnd = f; + for ( coord = 0; coord < ncoord; coord++ ) { + xu[ coord ] = ptr_in[ coord ][ point ]; + } + } + +/* If this point has a bad coord value, it may still be useful if the + required coord value is not bad. In this case, extract the required + coordinate. */ + } else if ( f != AST__BAD ) { + +/* Use this to update secondary lower and upper bounds we are seeking. + These will be returned if no primary values are found via the previous + code block. */ + if ( ( slbnd == AST__BAD ) || ( f < slbnd ) ) { + slbnd = f; + for ( coord = 0; coord < ncoord; coord++ ) { + sxl[ coord ] = ptr_in[ coord ][ point ]; + } + } + if ( ( subnd == AST__BAD ) || ( f > subnd ) ) { + subnd = f; + for ( coord = 0; coord < ncoord; coord++ ) { + sxu[ coord ] = ptr_in[ coord ][ point ]; + } + } + } + } + +/* If no primary values could be found, use secondary values. */ + if( *lbnd == AST__BAD && *ubnd == AST__BAD ) { + *lbnd = slbnd; + *ubnd = subnd; + for ( coord = 0; coord < ncoord; coord++ ) { + xu[ coord ] = sxu[ coord ]; + xl[ coord ] = sxl[ coord ]; + } + result = ( slbnd == AST__BAD || subnd == AST__BAD ); + } + } + } + +/* Free workspace */ + sxl = astFree( sxl ); + sxu = astFree( sxu ); + +/* Annul the temporary PointSets and free the workspace. */ + pset_in = astAnnul( pset_in ); + pset_out = astAnnul( pset_out ); + limit = astFree( limit ); + + return result; +} + +/* +* Name: +* SpreadKernel1 + +* Purpose: +* Rebin a data grid, using a 1-d interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SpreadKernel1( AstMapping *this, int ndim_out, +* const int *lbnd_out, const int *ubnd_out, +* const *in, const *in_var, +* double infac, int npoint, const int *offset, +* const double *const *coords, +* void (* kernel)( double, const double [], int, +* double *, int * ), +* int neighb, const double *params, int flags, +* badval, int npix_out, *out, +* *out_var, double *work, int64_t *nused, +* int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which rebins a rectangular region of an +* input grid of data (and, optionally, associated statistical variance +* values) so as to place them into a new output grid. Each input +* grid point may be mapped on to a position in the output grid in +* an arbitrary way. The input and output grids may have any number +* of dimensions, not necessarily equal. +* +* Where the input positions given do not correspond with a pixel centre +* in the output grid, the each input pixel value is spread out between the +* surrounding output pixels using weights determined by a separable kernel +* which is the product of a 1-dimensional kernel function evaluated along +* each output dimension. A pointer should be supplied to the 1-dimensional +* kernel function to be used. + +* Parameters: +* this +* Pointer to the Mapping being used in the rebinning operation +* (this is only used for constructing error messages). +* ndim_out +* The number of dimensions in the output grid. This should be at +* least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output grid, its extent along a particular +* (i'th) dimension being ubnd_out[i]-lbnd_out[i]+1 (assuming "i" +* is zero-based). They also define the output grid's coordinate +* system, with each pixel being of unit extent along each +* dimension with integral coordinate values at its centre. +* in +* Pointer to the array of data to be rebinned. The numerical type +* of these data should match the function used, as given by the +* suffix on the function name. Note that details of how the input +* grid maps on to this array (e.g. the storage order, number of +* dimensions, etc.) is arbitrary and is specified entirely by means +* of the "offset" array. The "in" array should therefore contain +* sufficient elements to accommodate the "offset" values supplied. +* There is no requirement that all elements of the "in" array +* should be rebinned, and any which are not addressed by the +* contents of the "offset" array will be ignored. +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and type as the "in" array), which +* represent estimates of the statistical variance associated +* with each element of the "in" array. If this second array is +* given (along with the corresponding "out_var" array), then +* estimates of the variance of the resampled data will also be +* returned. It is addressed in exactly the same way (via the +* "offset" array) as the "in" array. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* infac +* A factor by which to multiply the input data values before use. +* npoint +* The number of input points which are to be rebinned. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each input point, this array should contain the zero-based +* offset in the input array(s) (i.e. the "in" and, optionally, +* the "in_var" arrays) from which the value to be rebinned should +* be obtained. +* coords +* An array of pointers to double, with "ndim_out" elements. +* Element "coords[coord]" should point at the first element of +* an array of double (with "npoint" elements) which contains the +* values of coordinate number "coord" for each point being +* rebinned. The value of coordinate number "coord" for +* rebinning point number "point" is therefore given by +* "coords[coord][point]" (assuming both indices are +* zero-based). If any point has a coordinate value of AST__BAD +* associated with it, then the corresponding input data (and +* variance) value will be ignored. +* kernel +* Pointer to the 1-dimensional kernel function to be used. +* neighb +* The number of neighbouring pixels in each dimension (on each +* side of the interpolation position) which are to receive +* contributions from the input pixel value. This value should be at +* least 1. +* params +* Pointer to an optional array of parameter values to be passed +* to the kernel function. If no parameters are required by this +* function, then a NULL pointer may be supplied. +* flags +* The bitwise OR of a set of flag values which control the +* operation of the function. These are chosend from: +* +* - AST__USEBAD: indicates whether there are "bad" (i.e. missing) data +* in the input array(s) which must be recognised. If this flag is not +* set, all input values are treated literally. +* - AST__GENVAR: Indicates that output variances should be generated +* from the spread of values contributing to each output pixel. +* - AST__USEVAR: Indicates that output variances should be generated +* by rebinning the input variances. +* - AST__VARWGT: Indicates that input variances should be used to +* create weights for the input data values. +* +* Only one of AST__GENVAR and AST__USEVAR should be supplied. +* badval +* If the AST__USEBAD flag is set in the "flags" value (above), +* this parameter specifies the value which is used to identify +* bad data and/or variance values in the input array(s). Its +* numerical type must match that of the "in" (and "in_var") +* arrays. The same value will also be used to flag any output +* array elements for which resampled values could not be +* obtained. The output arrays(s) may be flagged with this +* value whether or not the AST__USEBAD flag is set (the +* function return value indicates whether any such values have +* been produced). +* npix_out +* Number of pixels in output array. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the rebinned data will be returned. The +* storage order should be such that the index of the first grid +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the rebinned values may be returned. This array will only be +* used if the "in_var" array has been given. The values returned +* are estimates of the statistical variance of the corresponding +* values in the "out" array, on the assumption that all errors in +* input grid values (in the "in" array) are statistically independent +* and that their variance estimates (in the "in_var" array) may +* simply be summed (with appropriate weighting factors). +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* work +* A pointer to an array with the same data type and size as the "out" +* array which is used as work space. The values in the supplied +* array are incremented on exit by the sum of the weights used +* with each output pixel. +* nused +* An optional pointer to a int64_t which will be incremented by the +* number of input values pasted into the output array. Ignored if NULL. + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +*/ +/* Define macros to implement the function for a specific data + type. */ +#define MAKE_SPREAD_KERNEL1(X,Xtype,IntType) \ +static void SpreadKernel1##X( AstMapping *this, int ndim_out, \ + const int *lbnd_out, const int *ubnd_out, \ + const Xtype *in, const Xtype *in_var, \ + double infac, int npoint, const int *offset, \ + const double *const *coords, \ + void (* kernel)( double, const double [], \ + int, double *, int * ), \ + int neighb, const double *params, \ + int flags, Xtype badval, int npix_out, \ + Xtype *out, Xtype *out_var, double *work, \ + int64_t *nused, int *status ) { \ +\ +/* Local Variables: */ \ + astDECLARE_GLOBALS /* Thread-specific data */ \ + Xtype c; \ + Xtype in_val; /* Input pixel value */ \ + double **wtptr; /* Pointer to array of weight pointers */ \ + double **wtptr_last; /* Array of highest weight pointer values */ \ + double *filter; /* Pointer to Nd array of filter values */ \ + double *kp; /* Pointer to next weight values */ \ + double *kstart; /* Pointer to next kernel value */ \ + double *kval; /* Pointer to 1d array of kernel values */ \ + double *wtprod; /* Accumulated weight value array pointer */ \ + double *xfilter; /* Pointer to 1d array of x axis filter values */ \ + double *xnl; /* Pointer to previous ofset array (n-d) */ \ + double pfac; /* Input weight with extra supplied factor */ \ + double pixwt; /* Weight to apply to individual pixel */ \ + double sum; /* Sum of all filter values */ \ + double wgt; /* Weight for input value */ \ + double x; /* x coordinate value */ \ + double xn; /* Coordinate value (n-d) */ \ + double xx; /* X offset */ \ + double xxl; /* Previous X offset */ \ + double xxn; \ + double y; /* y coordinate value */ \ + double yy; /* Y offset */ \ + double yyl; /* Previous Y offset */ \ + int *hi; /* Pointer to array of upper indices */ \ + int *jhi; /* Pointer to array of filter upper indices */ \ + int *jlo; /* Pointer to array of filter lower indices */ \ + int *lo; /* Pointer to array of lower indices */ \ + int *stride; /* Pointer to array of dimension strides */ \ + int bad; /* Output pixel bad? */ \ + int done; /* All pixel indices done? */ \ + int genvar; /* Generate output variances? */ \ + int hi_ix; /* Upper output pixel index (x dimension) */ \ + int hi_iy; /* Upper output pixel index (y dimension) */ \ + int hi_jx; /* Upper filter pixel index (x dimension) */ \ + int hi_jy; /* Upper filter pixel index (y dimension) */ \ + int idim; /* Loop counter for dimensions */ \ + int ii; /* Loop counter for dimensions */ \ + int ix; /* Pixel index in output grid x dimension */ \ + int iy; /* Pixel index in output grid y dimension */ \ + int jjx; /* Reflected pixel index in filter grid x dimension */ \ + int jjy; /* Reflected pixel index in filter grid y dimension */ \ + int jx; /* Pixel index in filter grid x dimension */ \ + int jxn; \ + int jy; /* Pixel index in filter grid y dimension */ \ + int kerror; /* Error signalled by kernel function? */ \ + int lo_ix; /* Lower output pixel index (x dimension) */ \ + int lo_iy; /* Lower output pixel index (y dimension) */ \ + int lo_jx; /* Lower filter pixel index (x dimension) */ \ + int lo_jy; /* Lower filter pixel index (y dimension) */ \ + int nb2; /* The total number of neighbouring pixels */ \ + int nf; /* Number of pixels in filter array */ \ + int nwx; /* Used X width of kernel function (*2) */ \ + int nwy; /* Used Y width of kernel function (*2) */ \ + int off1; /* Input pixel offset due to y index */ \ + int off_in; /* Offset to input pixel */ \ + int off_out; /* Offset to output pixel */ \ + int off_xedge; /* Does filter box overlap array edge on the X axis? */ \ + int off_yedge; /* Does filter box overlap array edge on the Y axis? */ \ + int point; /* Loop counter for output points */ \ + int s; /* Temporary variable for strides */ \ + int usebad; /* Use "bad" input pixel values? */ \ + int usevar; /* Process variance array? */ \ + int varwgt; /* Use input variances as weights? */ \ + int ystride; /* Stride along input grid y dimension */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Get a pointer to a structure holding thread-specific global data values */ \ + astGET_GLOBALS(this); \ +\ +/* Further initialisation. */ \ + kerror = 0; \ + sum = 0.0; \ + bad = 0; \ +\ +/* Find the total number of pixels in the filter used to spread a single \ + input pixel into the output image. */ \ + nb2 = 2*neighb; \ + nf = 1; \ + for ( idim = 0; idim < ndim_out; idim++ ) nf *= nb2; \ +\ +/* Allocate workspace to hold the filter values. */ \ + filter = astMalloc( sizeof( double ) * (size_t) nf ); \ + if ( astOK ) { \ +\ +/* Determine if we are processing bad pixels or variances. */ \ + usebad = flags & AST__USEBAD; \ + usevar = 0; \ + genvar = 0; \ + if( flags & AST__GENVAR ) { \ + genvar = out_var && work; \ + } else if( flags & AST__USEVAR ) { \ + usevar = in_var && out_var; \ + } \ + varwgt = ( flags & AST__VARWGT ) && in_var && work; \ +\ +/* Handle the 1-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + if ( ndim_out == 1 ) { \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + KERNEL_1D(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + KERNEL_1D(X,Xtype,1,0,1,IntType,1) \ + } else { \ + KERNEL_1D(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + KERNEL_1D(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + KERNEL_1D(X,Xtype,0,0,1,IntType,1) \ + } else { \ + KERNEL_1D(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + KERNEL_1D(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + KERNEL_1D(X,Xtype,1,0,1,IntType,0) \ + } else { \ + KERNEL_1D(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + KERNEL_1D(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + KERNEL_1D(X,Xtype,0,0,1,IntType,0) \ + } else { \ + KERNEL_1D(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ +\ +/* Exit point on error in kernel function */ \ + Kernel_SError_1d: ; \ +\ +/* Handle the 2-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + } else if ( ndim_out == 2 ) { \ +\ +/* Allocate workspace to hold the X axis filter values. */ \ + xfilter = astMalloc( sizeof( double ) * (size_t) nb2 ); \ +\ +/* Calculate the stride along the y dimension of the output grid. */ \ + ystride = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + KERNEL_2D(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + KERNEL_2D(X,Xtype,1,0,1,IntType,1) \ + } else { \ + KERNEL_2D(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + KERNEL_2D(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + KERNEL_2D(X,Xtype,0,0,1,IntType,1) \ + } else { \ + KERNEL_2D(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + KERNEL_2D(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + KERNEL_2D(X,Xtype,1,0,1,IntType,0) \ + } else { \ + KERNEL_2D(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + KERNEL_2D(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + KERNEL_2D(X,Xtype,0,0,1,IntType,0) \ + } else { \ + KERNEL_2D(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ +\ +/* Free work space */ \ + xfilter = astFree( xfilter ); \ +\ +/* Exit point on error in kernel function */ \ + Kernel_SError_2d: ; \ +\ +/* Handle other numbers of dimensions. */ \ +/* ----------------------------------- */ \ + } else { \ +\ +/* Allocate workspace. */ \ + hi = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + lo = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + jhi = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + jlo = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + xnl = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + kval = astMalloc( sizeof( double ) * (size_t) \ + ( nb2 * ndim_out ) ); \ + wtprod = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + wtptr = astMalloc( sizeof( double * ) * (size_t) ndim_out ); \ + wtptr_last = astMalloc( sizeof( double * ) * (size_t) ndim_out ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along each dimension of the output grid. */ \ + for ( s = 1, idim = 0; idim < ndim_out; idim++ ) { \ + stride[ idim ] = s; \ + s *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ + xnl[ idim ] = AST__BAD; \ + } \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + KERNEL_ND(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + KERNEL_ND(X,Xtype,1,0,1,IntType,1) \ + } else { \ + KERNEL_ND(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + KERNEL_ND(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + KERNEL_ND(X,Xtype,0,0,1,IntType,1) \ + } else { \ + KERNEL_ND(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + KERNEL_ND(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + KERNEL_ND(X,Xtype,1,0,1,IntType,0) \ + } else { \ + KERNEL_ND(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + KERNEL_ND(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + KERNEL_ND(X,Xtype,0,0,1,IntType,0) \ + } else { \ + KERNEL_ND(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ +\ +/* Exit point on error in kernel function */ \ + Kernel_SError_Nd: ;\ + } \ +\ +/* Free the workspace. */ \ + hi = astFree( hi ); \ + lo = astFree( lo ); \ + jhi = astFree( jhi ); \ + jlo = astFree( jlo ); \ + stride = astFree( stride ); \ + xnl = astFree( xnl ); \ + kval = astFree( kval ); \ + wtprod = astFree( wtprod ); \ + wtptr = astFree( wtptr ); \ + wtptr_last = astFree( wtptr_last ); \ + } \ + filter = astFree( filter ); \ + }\ +\ +/* If an error occurred in the kernel function, then report a \ + contextual error message. */ \ + if ( kerror ) { \ + astError( astStatus, "astRebin"#X"(%s): Error signalled by " \ + "user-supplied 1-d interpolation kernel.", status, \ + astGetClass( unsimplified_mapping ) ); \ + } \ +\ +} + + + + +#define KERNEL_1D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* We do not yet have a previous filter position. */ \ + xxl = AST__BAD; \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Obtain the x coordinate of the current point and test if it is bad. \ + Also test that the central point falls within the output array. */ \ + x = coords[ 0 ][ point ]; \ + ix = (int) floor( x + 0.5 ); \ + if( ix < lbnd_out[ 0 ] || ix > ubnd_out[ 0 ] ) bad = 1; \ + bad = bad || ( x == AST__BAD ); \ +\ +/* If OK, calculate the lowest and highest indices (in the x \ + dimension) of the region of neighbouring output pixels that will \ + receive contributions from the current input pixel. Constrain these \ + values to lie within the output grid. */ \ + if ( !bad ) { \ + ix = (int) floor( x ) - neighb + 1; \ + lo_ix = MaxI( ix, lbnd_out[ 0 ], status ); \ + hi_ix = MinI( ix + nb2 - 1, ubnd_out[ 0 ], status ); \ +\ +/* Skip to the next input point if the current input point makes no \ + contribution to any output pixel. */ \ + if( lo_ix <= hi_ix ) { \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* Convert these output indices to the corresponding indices \ + within a box [ 0, 2*neighb ] holding the kernel values. */ \ + lo_jx = lo_ix - ix; \ + hi_jx = hi_ix - ix; \ +\ +/* See if the kernel extends off the edge of the output array. */ \ + nwx = hi_jx - lo_jx + 1; \ + off_xedge = ( nwx < nb2 ); \ +\ +/* Use the kernel function to fill the work array with weights for all output \ + pixels whether or not they fall within the output array. At the same \ + time find the sum of all the factors. */ \ + xx = (double) ix - x; \ + if( xx != xxl || off_xedge ) { \ + sum = 0.0; \ +\ +/* First handle cases where the kernel box overlaps an edge of the output \ + array. In these cases, in order to conserve flux, the bit of the \ + kernel function that is off the edge is reflected back onto the array. \ + Care must be taken since the reflected part of the kernel may itself \ + overlap the opposite edge of the array, in which case the overlapping \ + part must again be reflected back onto the array. This iterative \ + reflection is implemented using a fractional division (%) operator. */ \ + if( off_xedge ) { \ + nwx *= 2; \ + xxl = AST__BAD; \ + for( jx = 0; jx < nb2; jx++ ) filter[ jx ] = 0.0; \ +\ + for ( jx = 0; jx < nb2; jx++ ) { \ + ( *kernel )( xx, params, flags, &pixwt, status ); \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_1d; \ + } \ +\ + jjx = ( jx - lo_jx ) % nwx + lo_jx; \ + if( jjx < lo_jx ) jjx += nwx; \ + if( jjx > hi_jx ) jjx = 2*hi_jx - jjx + 1; \ +\ + filter[ jjx ] += pixwt; \ + sum += pixwt; \ + xx += 1.0; \ + } \ +\ +/* Now handle cases where the kernel box is completely within the output \ + array. */ \ + } else { \ + xxl = xx; \ +\ + for ( jx = 0; jx < nb2; jx++ ) { \ + ( *kernel )( xx, params, flags, &pixwt, status ); \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_1d; \ + } \ +\ +/* Store the kernel factor and increment the sum of all factors. */ \ + filter[ jx ] = pixwt; \ + sum += pixwt; \ + xx += 1.0; \ + } \ +\ + } \ +\ +/* Ensure we do not divide by zero. */ \ + if( sum == 0.0 ) sum = 1.0; \ + } \ +\ +/* If we are using the input data variances as weights, calculate the \ + total weight, incorporating the normalisation factor for the kernel. */ \ + if( Varwgt ) { \ + wgt = 1.0/(sum*in_var[ off_in ]); \ +\ +/* If we are not using input variances as weights, the weight is just the \ + kernel normalisation factor. */ \ + } else { \ + wgt = 1.0/sum; \ + } \ +\ +/* Loop round all the output pixels which receive contributions from this \ + input pixel, calculating the offset of each pixel from the start of the \ + input array. */ \ + off_out = lo_ix - lbnd_out[ 0 ]; \ + for ( jx = lo_jx; jx <= hi_jx; jx++, off_out++ ) { \ +\ +/* Retrieve the weight for the current output pixel and normalise it. */ \ + pixwt = wgt*filter[ jx ]; \ + pfac = pixwt*infac; \ +\ +/* Update the output pixel with the required fraction of the input pixel \ + value. */ \ + c = CONV(IntType,pfac*in_val); \ +\ + if( work ) { \ + out[ off_out ] += c; \ + work[ off_out ] += pixwt; \ + } else {\ + out[ off_out ] += c; \ + } \ +\ + if ( Usevar ) { \ + out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off_out ] += c*c/pixwt; \ + work[ off_out + npix_out ] += pixwt*pixwt; \ + } \ +\ + } \ + } \ + } \ + } + + + + +#define KERNEL_2D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* We do not yet have a previous filter position. */ \ + xxl = AST__BAD; \ + yyl = AST__BAD; \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Obtain the x coordinate of the current point and test if it is bad. \ + Also test that the central point falls within the output array. */ \ + x = coords[ 0 ][ point ]; \ + ix = (int) floor( x + 0.5 ); \ + if( ix < lbnd_out[ 0 ] || ix > ubnd_out[ 0 ] ) bad = 1; \ + bad = bad || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* Similarly obtain and test the y coordinate. */ \ + y = coords[ 1 ][ point ]; \ + iy = (int) floor( y + 0.5 ); \ + if( iy < lbnd_out[ 1 ] || iy > ubnd_out[ 1 ] ) bad = 1; \ + bad = bad || ( y == AST__BAD ); \ + if ( !bad ) { \ +\ +/* If OK, calculate the lowest and highest indices (in each dimension) \ + of the region of neighbouring output pixels which will receive \ + contributions from the current input pixel. Constrain these values \ + to lie within the input grid. */ \ + ix = (int) floor( x ) - neighb + 1; \ + lo_ix = MaxI( ix, lbnd_out[ 0 ], status ); \ + hi_ix = MinI( ix + nb2 - 1, ubnd_out[ 0 ], status ); \ + iy = (int) floor( y ) - neighb + 1; \ + lo_iy = MaxI( iy, lbnd_out[ 1 ], status ); \ + hi_iy = MinI( iy + nb2 - 1, ubnd_out[ 1 ], status ); \ +\ +/* Skip to the next input point if the current input point makes no \ + contribution to any output pixel. */ \ + if( lo_ix <= hi_ix && lo_iy <= hi_iy ) { \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* Convert these output indices to the corresponding indices \ + within a box [ 0:2*neighb, 0:2*neighb ] holding the kernel values. */ \ + lo_jx = lo_ix - ix; \ + hi_jx = hi_ix - ix; \ + lo_jy = lo_iy - iy; \ + hi_jy = hi_iy - iy; \ +\ +/* See if the kernel extends off the edge of the output array on either \ + axis. */ \ + nwx = hi_jx - lo_jx + 1; \ + nwy = hi_jy - lo_jy + 1; \ + off_xedge = ( nwx < nb2 ); \ + off_yedge = ( nwy < nb2 ); \ +\ +/* Loop to evaluate the kernel function along the y dimension, storing \ + the resulting weight values in all elements of each associated row \ + in the kvar array. The function's argument is the offset of the \ + output pixel (along this dimension) from the central output \ + position. */ \ + yy = (double) iy - y; \ + xx = (double) ix - x; \ + if( xx != xxl || yy != yyl || off_xedge || off_yedge ) { \ +\ +/* First handle cases where the kernel box extends beyond the top or \ + bottom edge of the output array. In these cases, in order to conserve \ + flux, the bit of the kernel function that is off the edge is reflected \ + back onto the array. Care must be taken since the reflected part of the \ + kernel may itself overlap the opposite edge of the array, in which \ + case the overlapping part must again be reflected back onto the \ + array. This iterative reflection is implemented using a fractional \ + division (%) operator. */ \ + if( off_yedge ) { \ + nwy *= 2; \ + xxl = AST__BAD; \ + yyl = AST__BAD; \ + for( jy = 0; jy < nb2*nb2; jy++ ) filter[ jy ] = 0.0; \ +\ + for ( jy = 0; jy < nb2; jy++ ) { \ + ( *kernel )( yy, params, flags, &pixwt, status ); \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_2d; \ + } \ +\ + jjy = ( jy - lo_jy ) % nwy + lo_jy; \ + if( jjy < lo_jy ) jjy += nwy; \ + if( jjy > hi_jy ) jjy = 2*hi_jy - jjy + 1; \ +\ + kp = filter + jjy*nb2; \ + for( jx = 0; jx < nb2; jx++ ) *(kp++) += pixwt; \ + yy += 1.0; \ + } \ +\ +/* Now handles cases where the kernel does not overlap the top or bottom edge \ + of the output array. */ \ + } else { \ + xxl = xx; \ + yyl = yy; \ + kp = filter; \ + for ( jy = 0; jy < nb2; jy++ ) { \ + ( *kernel )( yy, params, flags, &pixwt, status ); \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_2d; \ + } \ +\ +/* Store the kernel factor in all elements of the current row. */ \ + for( jx = 0; jx < nb2; jx++ ) *(kp++) = pixwt; \ +\ +/* Move on to the next row. */ \ + yy += 1.0; \ + } \ + } \ +\ +/* Loop to evaluate the kernel function along the x dimension, multiplying \ + the resulting weight values by the values already stored in the the \ + associated column in the kvar array. The function's argument is the \ + offset of the output pixel (along this dimension) from the central output \ + position. Also form the total data sum in the filter array. First \ + handle cases where the kernel overlaps the left or right edge of the \ + output array. */ \ + sum = 0.0; \ +\ +/* First deal with cases where the kernel extends beyond the left or \ + right edge of the output array. */ \ + if( off_xedge ) { \ + nwx *= 2; \ + xxl = AST__BAD; \ + for( jx = 0; jx < nb2; jx++ ) xfilter[ jx ] = 0.0; \ +\ + for ( jx = 0; jx < nb2; jx++ ) { \ + ( *kernel )( xx, params, flags, &pixwt, status ); \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_2d; \ + } \ +\ + jjx = ( jx - lo_jx ) % nwx + lo_jx; \ + if( jjx < lo_jx ) jjx += nwx; \ + if( jjx > hi_jx ) jjx = 2*hi_jx - jjx + 1; \ +\ + xfilter[ jjx ] += pixwt; \ + xx += 1.0; \ + } \ +\ + for ( jx = 0; jx < nb2; jx++ ) { \ + kp = filter + jx; \ + for( jy = 0; jy < nb2; jy++, kp += nb2 ) { \ + *kp *= xfilter[ jx ]; \ + sum += *kp; \ + } \ + } \ +\ +/* Now deal with cases where the kernel does not extends beyond the left or \ + right edge of the output array. */ \ + } else { \ +\ + for ( jx = 0; jx < nb2; jx++ ) { \ + ( *kernel )( xx, params, flags, &pixwt, status ); \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_2d; \ + } \ +\ +/* Multiply the kernel factor by all elements of the current column. */ \ + kp = filter + jx; \ + for( jy = 0; jy < nb2; jy++, kp += nb2 ) { \ + *kp *= pixwt; \ + sum += *kp; \ + } \ +\ +/* Move on to the next column. */ \ + xx += 1.0; \ + } \ + } \ +\ +/* Ensure we do not divide by zero. */ \ + if( sum == 0.0 ) sum = 1.0; \ + } \ +\ +/* If we are using the input data variances as weights, calculate the \ + total weight, incorporating the normalisation factor for the kernel. */ \ + if( Varwgt ) { \ + wgt = 1.0/(sum*in_var[ off_in ]); \ +\ +/* If we are not using input variances as weights, the weight is just the \ + kernel normalisation factor. */ \ + } else { \ + wgt = 1.0/sum; \ + } \ +\ +/* Find the offset into the output array at the first modified output pixel \ + in the first modified row. */ \ + off1 = lo_ix - lbnd_out[ 0 ] + ystride * ( lo_iy - lbnd_out[ 1 ] ); \ +\ +/* Loop over the affected output rows again. */ \ + for ( jy = lo_jy; jy <= hi_jy; jy++, off1 += ystride ) { \ +\ +/* Save the offset of the first output pixel to be modified in the \ + current row. */ \ + off_out = off1; \ +\ +/* Get a pointer to the first weight value which will be used. */ \ + kp = filter + lo_jx + jy*nb2; \ +\ +/* Loop over the affected output columns again. */ \ + for ( jx = lo_jx; jx <= hi_jx; jx++, off_out++, kp++ ) { \ +\ +/* Calculate the weight for this output pixel and normalise it. */ \ + pixwt = wgt*( *kp ); \ +\ +/* Update the output pixel with the required fraction of the input pixel \ + value. */ \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ +\ + out[ off_out ] += c; \ + if( work ) work[ off_out ] += pixwt; \ +\ + if ( Usevar ) { \ + out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off_out ] += c*c/pixwt; \ + work[ off_out + npix_out ] += pixwt*pixwt; \ + } \ + } \ + } \ + } \ + } \ + } \ + } + + + + +#define KERNEL_ND(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* We do not yet have a normalising factor */ \ + sum = AST__BAD; \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Initialise offsets into the output array. Then loop to obtain each \ + coordinate associated with the current output point. Set a flag \ + indicating if any output pixel will be modified. */ \ + if( !bad ) { \ + off_out = 0; \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + xn = coords[ idim ][ point ]; \ +\ +/* Test if the coordinate is bad. If true, the corresponding output pixel \ + value will be bad, so give up on this point. */ \ + ix = (int) floor( xn + 0.5 ); \ + if( ix < lbnd_out[ idim ] || ix > ubnd_out[ idim ] ) bad = 1; \ + bad = bad || ( xn == AST__BAD ); \ + if ( bad ) break; \ +\ +/* Calculate the lowest and highest indices (in the current dimension) \ + of the region of neighbouring output pixels that will be modified. \ + Constrain these values to lie within the output grid. */ \ + ix = (int) floor( xn ) - neighb + 1; \ + lo[ idim ] = MaxI( ix, lbnd_out[ idim ], status ); \ + hi[ idim ] = MinI( ix + nb2 - 1, ubnd_out[ idim ], status ); \ + jlo[ idim ] = lo[ idim ] - ix; \ + jhi[ idim ] = hi[ idim ] - ix; \ +\ +/* Check there is some overlap with the output array on this axis. */ \ + if( lo[ idim ] > hi[ idim ] ) { \ + bad = 1; \ + break; \ + } \ +\ +/* Accumulate the offset (from the start of the output array) of the \ + modified output pixel which has the lowest index in each dimension. */ \ + off_out += stride[ idim ] * ( lo[ idim ] - lbnd_out[ idim ] ); \ +\ +/* Set up an array of pointers to locate the first filter pixel (stored in the \ + "kval" array) for each dimension. */ \ + wtptr[ idim ] = kval + nb2*idim; \ + wtptr_last[ idim ] = wtptr[ idim ] + nb2 - 1; \ +\ +/* See if the kernel extends off the edge of the output array on the current \ + axis. */ \ + lo_jx = jlo[ idim ]; \ + hi_jx = jhi[ idim ]; \ + nwx = hi_jx - lo_jx + 1; \ + off_xedge = ( nwx < nb2 ); \ +\ +/* Loop to evaluate the kernel function along each dimension, storing \ + the resulting values. The function's argument is the offset of the \ + output pixel (along the relevant dimension) from the central output \ + point. */ \ + xxn = (double) ix - xn; \ + if( xxn != xnl[ idim ] || off_xedge ) { \ + sum = AST__BAD; \ +\ +/* First handle cases where the kernel box overlaps an edge of the output \ + array. In these cases, in order to conserve flux, the bit of the \ + kernel function that is off the edge is reflected back onto the array. \ + Care must be taken since the reflected part of the kernel may itself \ + overlap the opposite edge of the array, in which case the overlapping \ + part must again be reflected back onto the array. This iterative \ + reflection is implemented using a fractional division (%) operator. */ \ + if( off_xedge ) { \ + nwx *= 2; \ + xnl[ idim ] = AST__BAD; \ + kp = wtptr[ idim ]; \ + for( jx = 0; jx < nb2; jx++ ) *(kp++) = 0.0; \ +\ + kp = wtptr[ idim ]; \ + for ( jx = 0; jx < nb2; jx++ ) { \ + ( *kernel )( xxn, params, flags, &pixwt, status ); \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_1d; \ + } \ +\ + jjx = ( jx - lo_jx ) % nwx + lo_jx; \ + if( jjx < lo_jx ) jjx += nwx; \ + if( jjx > hi_jx ) jjx = 2*hi_jx - jjx + 1; \ +\ + kp[ jjx ] += pixwt; \ + xxn += 1.0; \ + } \ +\ +/* Now handle cases where the kernel box is completely within the output \ + array. */ \ + } else { \ + xnl[ idim ] = xxn; \ + for ( jxn = 0; jxn < nb2; jxn++ ) { \ + ( *kernel )( xxn, params, flags, wtptr[ idim ] + jxn, status ); \ +\ +/* Check for errors arising in the kernel function. */ \ + if ( !astOK ) { \ + kerror = 1; \ + goto Kernel_SError_Nd; \ + } \ +\ +/* Increment the kernel position. */ \ + xxn += 1.0; \ + } \ + } \ + } \ + } \ +\ +/* If OK... */ \ + if ( !bad ) { \ +\ +/* We only need to modify the normalising factor if the weight values \ + have changed. */ \ + if( sum == AST__BAD ) { \ +\ +/* The kernel value to use for each output pixel is the product of the \ + kernel values for each individual axis at that point. To conserve \ + flux we need to make sure that the sum of these kernel products is unity. \ + So loop over the values now to find the total sum of all kernel values. */ \ + idim = ndim_out - 1; \ + wtprod[ idim ] = 1.0; \ + done = 0; \ + sum = 0; \ + do { \ +\ +/* Each modified output pixel has a weight equal to the product of the kernel \ + weight factors evaluated along each input dimension. However, since \ + we typically only change the index of one dimension at a time, we \ + can avoid forming this product repeatedly by retaining an array of \ + accumulated products for all higher dimensions. We need then only \ + update the lower elements in this array, corresponding to those \ + dimensions whose index has changed. We do this here, "idim" being \ + the index of the most significant dimension to have changed. Note \ + that on the first pass, all dimensions are considered changed, \ + causing this array to be initialised. */ \ + for ( ii = idim; ii >= 1; ii-- ) { \ + wtprod[ ii - 1 ] = wtprod[ ii ] * *( wtptr[ ii ] ); \ + } \ +\ +/* Obtain the weight of each pixel from the accumulated product of \ + weights. Also multiply by the weight for dimension zero, which is not \ + included in the "wtprod" array). Increment the sum of all weights. */ \ + sum += wtprod[ 0 ] * *( wtptr[ 0 ] ); \ +\ +/* Now update the weight value pointers and pixel offset to refer to \ + the next output pixel to be considered. */ \ + idim = 0; \ + do { \ +\ +/* The first input dimension whose weight value pointer has not yet \ + reached its final value has this pointer incremented. */ \ + if ( wtptr[ idim ] != wtptr_last[ idim ] ) { \ + wtptr[ idim ]++; \ + break; \ +\ +/* Any earlier dimensions (which have reached the final pointer value) \ + have this pointer returned to its lowest value. */ \ + } else { \ + wtptr[ idim ] -= nb2 - 1; \ + done = ( ++idim == ndim_out ); \ + } \ + } while ( !done ); \ + } while ( !done ); \ +\ +/* Ensure we do not divide by zero. */ \ + if( sum == 0.0 ) sum = 1.0; \ + } \ +\ +/* Re-initialise the weights pointers to refer to the first and last \ + filter pixels which overlaps the output array. */ \ + kstart = kval; \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + wtptr[ idim ] = kstart + jlo[ idim ]; \ + wtptr_last[ idim ] = kstart + jhi[ idim ]; \ + kstart += nb2; \ + } \ +\ +/* If we are using the input data variances as weights, calculate the \ + total weight, incorporating the normalisation factor for the kernel. */ \ + if( Varwgt ) { \ + wgt = 1.0/(sum*in_var[ off_in ]); \ +\ +/* If we are not using input variances as weights, the weight is just the \ + kernel normalisation factor. */ \ + } else { \ + wgt = 1.0/sum; \ + } \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* Initialise, and loop over the neighbouring output pixels to divide up \ + the input pixel value between them. */ \ + idim = ndim_out - 1; \ + wtprod[ idim ] = 1.0; \ + done = 0; \ + do { \ +\ +/* Each modified output pixel has a weight equal to the product of the kernel \ + weight factors evaluated along each input dimension. However, since \ + we typically only change the index of one dimension at a time, we \ + can avoid forming this product repeatedly by retaining an array of \ + accumulated products for all higher dimensions. We need then only \ + update the lower elements in this array, corresponding to those \ + dimensions whose index has changed. We do this here, "idim" being \ + the index of the most significant dimension to have changed. Note \ + that on the first pass, all dimensions are considered changed, \ + causing this array to be initialised. */ \ + for ( ii = idim; ii >= 1; ii-- ) { \ + wtprod[ ii - 1 ] = wtprod[ ii ] * *( wtptr[ ii ] ); \ + } \ +\ +/* Obtain the weight of each pixel from the accumulated \ + product of weights. Also multiply by the weight for dimension zero, \ + which is not included in the "wtprod" array). */ \ + pixwt = ( wtprod[ 0 ] * *( wtptr[ 0 ] ) )*wgt; \ +\ +/* Update the output pixel with the required fraction of the input pixel \ + value. */ \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ +\ + if( work ) { \ + out[ off_out ] += c; \ + work[ off_out ] += pixwt; \ + } else {\ + out[ off_out ] += c; \ + } \ +\ + if ( Usevar ) { \ + out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off_out ] += c*c/pixwt; \ + work[ off_out + npix_out ] += pixwt*pixwt; \ + } \ +\ +/* Now update the weight value pointers and pixel offset to refer to \ + the next output pixel to be considered. */ \ + idim = 0; \ + do { \ +\ +/* The first input dimension whose weight value pointer has not yet \ + reached its final value has this pointer incremented, and the pixel \ + offset into the input array is updated accordingly. */ \ + if ( wtptr[ idim ] != wtptr_last[ idim ] ) { \ + wtptr[ idim ]++; \ + off_out += stride[ idim ]; \ + break; \ +\ +/* Any earlier dimensions (which have reached the final pointer value) \ + have this pointer returned to its lowest value. Again, the pixel \ + offset into the input image is updated accordingly. */ \ + } else { \ + wtptr[ idim ] -= ( hi[ idim ] - lo[ idim ] ); \ + off_out -= stride[ idim ] * \ + ( hi[ idim ] - lo[ idim ] ); \ + done = ( ++idim == ndim_out ); \ + } \ + } while ( !done ); \ + } while ( !done ); \ + } \ + } \ + } + + +/* Expand the main macro above to generate a function for each + required signed data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_SPREAD_KERNEL1(LD,long double,0) +#endif +MAKE_SPREAD_KERNEL1(D,double,0) +MAKE_SPREAD_KERNEL1(F,float,0) +MAKE_SPREAD_KERNEL1(I,int,1) +MAKE_SPREAD_KERNEL1(B,signed char,1) +MAKE_SPREAD_KERNEL1(UB,unsigned char,1) + +/* Undefine the macros used above. */ +#undef KERNEL_ND +#undef KERNEL_2D +#undef KERNEL_1D +#undef MAKE_SPREAD_KERNEL1 + +/* +* Name: +* SpreadLinear + +* Purpose: +* Rebin a data grid, using the linear spreading scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SpreadLinear( int ndim_out, +* const int *lbnd_out, const int *ubnd_out, +* const *in, const *in_var, +* double infac, int npoint, const int *offset, +* const double *const *coords, int flags, +* badval, int npix_out, *out, +* *out_var, double *work, int64_t *nused ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which rebins a rectangular region of an +* input grid of data (and, optionally, associated statistical variance +* values) so as to place them into a new output grid. Each input +* grid point may be mapped on to a position in the output grid in +* an arbitrary way. Where the positions given do not correspond +* with a pixel centre in the input grid, the spreading scheme +* used divides the input pixel value up linearly between the +* nearest neighbouring output pixels in each dimension (there are 2 +* nearest neighbours in 1 dimension, 4 in 2 dimensions, 8 in 3 +* dimensions, etc.). + +* Parameters: +* ndim_out +* The number of dimensions in the output grid. This should be at +* least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output grid, its extent along a particular +* (i'th) dimension being ubnd_out[i]-lbnd_out[i]+1 (assuming "i" +* is zero-based). They also define the output grid's coordinate +* system, with each pixel being of unit extent along each +* dimension with integral coordinate values at its centre. +* in +* Pointer to the array of data to be rebinned. The numerical type +* of these data should match the function used, as given by the +* suffix on the function name. Note that details of how the input +* grid maps on to this array (e.g. the storage order, number of +* dimensions, etc.) is arbitrary and is specified entirely by means +* of the "offset" array. The "in" array should therefore contain +* sufficient elements to accommodate the "offset" values supplied. +* There is no requirement that all elements of the "in" array +* should be rebinned, and any which are not addressed by the +* contents of the "offset" array will be ignored. +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and type as the "in" array), which +* represent estimates of the statistical variance associated +* with each element of the "in" array. If this second array is +* given (along with the corresponding "out_var" array), then +* estimates of the variance of the resampled data will also be +* returned. It is addressed in exactly the same way (via the +* "offset" array) as the "in" array. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* infac +* A factor by which to multiply the input data values before use. +* npoint +* The number of input points which are to be rebinned. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each input point, this array should contain the zero-based +* offset in the input array(s) (i.e. the "in" and, optionally, +* the "in_var" arrays) from which the value to be rebinned should +* be obtained. +* coords +* An array of pointers to double, with "ndim_out" elements. +* Element "coords[coord]" should point at the first element of +* an array of double (with "npoint" elements) which contains the +* values of coordinate number "coord" for each point being +* rebinned. The value of coordinate number "coord" for +* rebinning point number "point" is therefore given by +* "coords[coord][point]" (assuming both indices are +* zero-based). If any point has a coordinate value of AST__BAD +* associated with it, then the corresponding input data (and +* variance) value will be ignored. +* The bitwise OR of a set of flag values which control the +* operation of the function. These are chosend from: +* +* - AST__USEBAD: indicates whether there are "bad" (i.e. missing) data +* in the input array(s) which must be recognised. If this flag is not +* set, all input values are treated literally. +* - AST__GENVAR: Indicates that any input variances are to be +* ignored, and that the output variances should be generated from +* the spread of values contributing to each output pixel. +* badval +* If the AST__USEBAD flag is set in the "flags" value (above), +* this parameter specifies the value which is used to identify +* bad data and/or variance values in the input array(s). Its +* numerical type must match that of the "in" (and "in_var") +* arrays. The same value will also be used to flag any output +* array elements for which resampled values could not be +* obtained. The output arrays(s) may be flagged with this +* value whether or not the AST__USEBAD flag is set (the +* function return value indicates whether any such values have +* been produced). +* npix_out +* Number of pixels in output array. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the rebinned data will be returned. The +* storage order should be such that the index of the first grid +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the rebinned values may be returned. This array will only be +* used if the "in_var" array has been given. The values returned +* are estimates of the statistical variance of the corresponding +* values in the "out" array, on the assumption that all errors in +* input grid values (in the "in" array) are statistically independent +* and that their variance estimates (in the "in_var" array) may +* simply be summed (with appropriate weighting factors). +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* work +* An optional pointer to a double array with the same size as +* the "out" array. The contents of this array (if supplied) are +* incremented by the accumulated weights assigned to each output pixel. +* If no accumulated weights are required, a NULL pointer should be +* given. +* nused +* An optional pointer to a int64_t which will be incremented by the +* number of input values pasted into the output array. Ignored if NULL. + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +*/ +/* Define macros to implement the function for a specific data + type. */ +#define MAKE_SPREAD_LINEAR(X,Xtype,IntType) \ +static void SpreadLinear##X( int ndim_out, \ + const int *lbnd_out, const int *ubnd_out, \ + const Xtype *in, const Xtype *in_var, \ + double infac, int npoint, const int *offset, \ + const double *const *coords, int flags, \ + Xtype badval, int npix_out, Xtype *out, \ + Xtype *out_var, double *work, int64_t *nused, \ + int *status ) { \ +\ +/* Local Variables: */ \ + Xtype c; /* Contribution to output value */ \ + Xtype in_val; /* Input value */ \ + double *frac_hi; /* Pointer to array of weights */ \ + double *frac_lo; /* Pointer to array of weights */ \ + double *wt; /* Pointer to array of weights */ \ + double *wtprod; /* Array of accumulated weights pointer */ \ + double *xn_max; /* Pointer to upper limits array (n-d) */ \ + double *xn_min; /* Pointer to lower limits array (n-d) */ \ + double frac_hi_x; /* Pixel weight (x dimension) */ \ + double frac_hi_y; /* Pixel weight (y dimension) */ \ + double frac_lo_x; /* Pixel weight (x dimension) */ \ + double frac_lo_y; /* Pixel weight (y dimension) */ \ + double pfac; /* Scaled pixel weight */ \ + double pixwt; /* Total pixel weight */ \ + double wgt; /* Weight for input value */ \ + double x; /* x coordinate value */ \ + double xmax; /* x upper limit */ \ + double xmin; /* x lower limit */ \ + double xn; /* Coordinate value (n-d) */ \ + double y; /* y coordinate value */ \ + double ymax; /* y upper limit */ \ + double ymin; /* y lower limit */ \ + int *dim; /* Pointer to array of pixel indices */ \ + int *hi; /* Pointer to array of upper indices */ \ + int *lo; /* Pointer to array of lower indices */ \ + int *stride; /* Pointer to array of dimension strides */ \ + int bad; /* Output pixel bad? */ \ + int done; /* All pixel indices done? */ \ + int genvar; /* Generate output variances? */ \ + int hi_x; /* Upper pixel index (x dimension) */ \ + int hi_y; /* Upper pixel index (y dimension) */ \ + int idim; /* Loop counter for dimensions */ \ + int ii; /* Loop counter for weights */ \ + int ixn; /* Pixel index (n-d) */ \ + int lo_x; /* Lower pixel index (x dimension) */ \ + int lo_y; /* Lower pixel index (y dimension) */ \ + int off; /* Total offset to input pixel */ \ + int off_in; /* Offset to input pixel */ \ + int off_lo; /* Offset to "first" input pixel */ \ + int off_out; /* Offset to output pixel */ \ + int point; /* Loop counter for output points */ \ + int s; /* Temporary variable for strides */ \ + int usebad; /* Use "bad" input pixel values? */ \ + int usevar; /* Process variance array? */ \ + int varwgt; /* Use input variances as weights? */ \ + int ystride; /* Stride along input grid y dimension */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Initialise variables to avoid "used of uninitialised variable" \ + messages from dumb compilers. */ \ + bad = 0; \ +\ +/* Determine if we are processing bad pixels or variances. */ \ + usebad = flags & AST__USEBAD; \ + usevar = 0; \ + genvar = 0; \ + if( flags & AST__GENVAR ) { \ + genvar = out_var && work; \ + } else if( flags & AST__USEVAR ) { \ + usevar = in_var && out_var; \ + } \ + varwgt = ( flags & AST__VARWGT ) && in_var && work; \ +\ +/* Handle the 1-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + if ( ndim_out == 1 ) { \ +\ +/* Calculate the coordinate limits of the input grid. */ \ + xmin = (double) lbnd_out[ 0 ] - 0.5; \ + xmax = (double) ubnd_out[ 0 ] + 0.5; \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + LINEAR_1D(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + LINEAR_1D(X,Xtype,1,0,1,IntType,1) \ + } else { \ + LINEAR_1D(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + LINEAR_1D(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + LINEAR_1D(X,Xtype,0,0,1,IntType,1) \ + } else { \ + LINEAR_1D(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + LINEAR_1D(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + LINEAR_1D(X,Xtype,1,0,1,IntType,0) \ + } else { \ + LINEAR_1D(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + LINEAR_1D(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + LINEAR_1D(X,Xtype,0,0,1,IntType,0) \ + } else { \ + LINEAR_1D(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ +\ +/* Handle the 2-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + } else if ( ndim_out == 2 ) { \ +\ +/* Calculate the stride along the y dimension of the output grid. */ \ + ystride = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; \ +\ +/* Calculate the coordinate limits of the output grid in each \ + dimension. */ \ + xmin = (double) lbnd_out[ 0 ] - 0.5; \ + xmax = (double) ubnd_out[ 0 ] + 0.5; \ + ymin = (double) lbnd_out[ 1 ] - 0.5; \ + ymax = (double) ubnd_out[ 1 ] + 0.5; \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + LINEAR_2D(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + LINEAR_2D(X,Xtype,1,0,1,IntType,1) \ + } else { \ + LINEAR_2D(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + LINEAR_2D(X,Xtype,0,1,0,IntType,1) \ + }else if ( genvar ) { \ + LINEAR_2D(X,Xtype,0,0,1,IntType,1) \ + } else { \ + LINEAR_2D(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + LINEAR_2D(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + LINEAR_2D(X,Xtype,1,0,1,IntType,0) \ + } else { \ + LINEAR_2D(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + LINEAR_2D(X,Xtype,0,1,0,IntType,0) \ + }else if ( genvar ) { \ + LINEAR_2D(X,Xtype,0,0,1,IntType,0) \ + } else { \ + LINEAR_2D(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ +\ +/* Handle other numbers of dimensions. */ \ +/* ----------------------------------- */ \ + } else { \ +\ +/* Allocate workspace. */ \ + dim = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + frac_hi = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + frac_lo = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + hi = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + lo = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + wt = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + wtprod = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + xn_max = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + xn_min = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along each dimension of the output grid. */ \ + for ( s = 1, idim = 0; idim < ndim_out; idim++ ) { \ + stride[ idim ] = s; \ + s *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ +\ +/* Calculate the coordinate limits of the output grid in each \ + dimension. */ \ + xn_min[ idim ] = (double) lbnd_out[ idim ] - 0.5; \ + xn_max[ idim ] = (double) ubnd_out[ idim ] + 0.5; \ + } \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + LINEAR_ND(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + LINEAR_ND(X,Xtype,1,0,1,IntType,1) \ + } else { \ + LINEAR_ND(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + LINEAR_ND(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + LINEAR_ND(X,Xtype,0,0,1,IntType,1) \ + } else { \ + LINEAR_ND(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + LINEAR_ND(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + LINEAR_ND(X,Xtype,1,0,1,IntType,0) \ + } else { \ + LINEAR_ND(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + LINEAR_ND(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + LINEAR_ND(X,Xtype,0,0,1,IntType,0) \ + } else { \ + LINEAR_ND(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ + } \ +\ +/* Free the workspace. */ \ + dim = astFree( dim ); \ + frac_hi = astFree( frac_hi ); \ + frac_lo = astFree( frac_lo ); \ + hi = astFree( hi ); \ + lo = astFree( lo ); \ + stride = astFree( stride ); \ + wt = astFree( wt ); \ + wtprod = astFree( wtprod ); \ + xn_max = astFree( xn_max ); \ + xn_min = astFree( xn_min ); \ + } \ +\ +} + + + + + + +#define LINEAR_1D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the output grid. Also test if it is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ +\ +/* If OK, obtain the indices along the output grid x dimension of the \ + two adjacent output pixels which will receive contributions from the \ + input pixel. Also obtain the fractional weight to be applied to each of \ + these pixels. */ \ + if ( !bad ) { \ + lo_x = (int) floor( x ); \ + hi_x = lo_x + 1; \ + frac_lo_x = (double) hi_x - x; \ + frac_hi_x = 1.0 - frac_lo_x; \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* Obtain the offset within the output array of the first pixel to be \ + updated (the one with the smaller index). */ \ + off_lo = lo_x - lbnd_out[ 0 ]; \ +\ +/* If we are using the input data variances as weights, calculate the \ + weight, and scale the fractions of each input pixel by the weight. */ \ + if( Varwgt ) { \ + wgt = 1.0/in_var[ off_in ]; \ + frac_lo_x *= wgt; \ + frac_hi_x *= wgt; \ + } \ +\ +/* For each of the two pixels which may be updated, test if the pixel index \ + lies within the output grid. Where it does, update the output pixel \ + with the required fraction of the input pixel value. */ \ + if ( lo_x >= lbnd_out[ 0 ] ) { \ + pfac = frac_lo_x*infac; \ + c = CONV(IntType,pfac*in_val); \ + out[ off_lo ] += CONV(IntType, c ); \ + if( work ) work[ off_lo ] += frac_lo_x; \ + if ( Usevar ) { \ + out_var[ off_lo ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && frac_lo_x != 0.0 ) { \ + out_var[ off_lo ] += c*c/frac_lo_x; \ + work[ off_lo + npix_out ] += frac_lo_x*frac_lo_x; \ + } \ + } \ + if ( hi_x <= ubnd_out[ 0 ] ) { \ + pfac = frac_hi_x*infac; \ + c = CONV(IntType,pfac*in_val); \ + out[ off_lo + 1 ] += CONV(IntType, c ); \ + if( work ) work[ off_lo + 1 ] += frac_hi_x; \ + if ( Usevar ) { \ + out_var[ off_lo + 1 ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && frac_hi_x != 0.0 ) { \ + out_var[ off_lo + 1 ] += c*c/frac_hi_x; \ + work[ off_lo + 1 + npix_out ] += frac_hi_x*frac_hi_x; \ + } \ + } \ + } \ + } + + + + +#define LINEAR_2D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Obtain the x coordinate of the current point and test if it lies \ + outside the output grid. Also test if it is bad. */ \ + y = coords[ 1 ][ point ]; \ + bad = bad || ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ + if ( !bad ) { \ +\ +/* Similarly obtain and test the y coordinate. */ \ + x = coords[ 0 ][ point ]; \ + bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* If OK, obtain the indices along the output grid x dimension of the \ + two adjacent pixels which recieve contributions from the input pixel. \ + Also obtain the fractional weight to be applied to each of \ + these pixels. */ \ + lo_x = (int) floor( x ); \ + hi_x = lo_x + 1; \ + frac_lo_x = (double) hi_x - x; \ + frac_hi_x = 1.0 - frac_lo_x; \ +\ +/* Repeat this process for the y dimension. */ \ + lo_y = (int) floor( y ); \ + hi_y = lo_y + 1; \ + frac_lo_y = (double) hi_y - y; \ + frac_hi_y = 1.0 - frac_lo_y; \ +\ +/* If we are using the input data variances as weights, calculate the \ + weight, and scale the fractions of each input pixel by the weight. \ + Since the product of two fractions is always used ot scale the input \ + data values, we use the square root of the reciprocal of the variance \ + as the weight (so that when the product of two fractions is taken, \ + the square roots multiply together to give the required 1/variance \ + weight). */ \ + if( Varwgt ) { \ + wgt = 1.0/sqrt( in_var[ off_in ] ); \ + frac_lo_x *= wgt; \ + frac_hi_x *= wgt; \ + frac_lo_y *= wgt; \ + frac_hi_y *= wgt; \ + } \ +\ +/* Obtain the offset within the output array of the first pixel to be \ + updated (the one with the smaller index along both dimensions). */ \ + off_lo = lo_x - lbnd_out[ 0 ] + ystride * ( lo_y - lbnd_out[ 1 ] ); \ +\ +/* For each of the four pixels which may be updated, test if the pixel indices \ + lie within the output grid. Where they do, update the output pixel \ + with the required fraction of the input pixel value. */ \ + if ( lo_y >= lbnd_out[ 1 ] ) { \ + if ( lo_x >= lbnd_out[ 0 ] ) { \ + pixwt = frac_lo_x * frac_lo_y; \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ + out[ off_lo ] += CONV(IntType, c ); \ + if( work ) work[ off_lo ] += pixwt; \ + if ( Usevar ) { \ + out_var[ off_lo ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off_lo ] += c*c/pixwt; \ + work[ off_lo + npix_out ] += pixwt*pixwt; \ + } \ + } \ + if ( hi_x <= ubnd_out[ 0 ] ) { \ + off = off_lo + 1; \ + pixwt = frac_hi_x * frac_lo_y; \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ + out[ off ] += CONV(IntType, c ); \ + if( work ) work[ off ] += pixwt; \ + if ( Usevar ) { \ + out_var[ off ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off ] += c*c/pixwt; \ + work[ off + npix_out ] += pixwt*pixwt; \ + } \ + } \ + } \ + if ( hi_y <= ubnd_out[ 1 ] ) { \ + if ( lo_x >= lbnd_out[ 0 ] ) { \ + off = off_lo + ystride; \ + pixwt = frac_lo_x * frac_hi_y; \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ + out[ off ] += CONV(IntType, c ); \ + if( work ) work[ off ] += pixwt; \ + if ( Usevar ) { \ + out_var[ off ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off ] += c*c/pixwt; \ + work[ off + npix_out ] += pixwt*pixwt; \ + } \ + } \ + if ( hi_x <= ubnd_out[ 0 ] ) { \ + off = off_lo + ystride + 1; \ + pixwt = frac_hi_x * frac_hi_y; \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ + out[ off ] += CONV(IntType, c ); \ + if( work ) work[ off ] += pixwt; \ + if ( Usevar ) { \ + out_var[ off ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off ] += c*c/pixwt; \ + work[ off + npix_out ] += pixwt*pixwt; \ + } \ + } \ + } \ + } \ + } \ + } + + +#define LINEAR_ND(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Initialise offsets into the output array. Then loop to obtain each \ + coordinate associated with the current output point. */ \ + if( !bad ) { \ + off_out = 0; \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + xn = coords[ idim ][ point ]; \ +\ +/* Test if the coordinate lies outside the output grid. Also test if \ + it is bad. If either is true, the corresponding output pixel value \ + will be bad, so give up on this point. */ \ + bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ + ( xn == AST__BAD ); \ + if ( bad ) break; \ +\ +/* Obtain the indices along the current dimension of the output grid of \ + the two (usually adjacent) pixels which will be updated. If necessary, \ + however, restrict each index to ensure it does not lie outside the \ + input grid. Also calculate the fractional weight to be given to each \ + pixel in order to divide the input value linearly between them. */ \ + ixn = (int) floor( xn ); \ + lo[ idim ] = MaxI( ixn, lbnd_out[ idim ], status ); \ + hi[ idim ] = MinI( ixn + 1, ubnd_out[ idim ], status ); \ + frac_lo[ idim ] = 1.0 - fabs( xn - (double) lo[ idim ] ); \ + frac_hi[ idim ] = 1.0 - fabs( xn - (double) hi[ idim ] ); \ +\ +/* Store the lower index involved in spreading along each \ + dimension and accumulate the offset from the start of the output \ + array of the pixel which has these indices. */ \ + dim[ idim ] = lo[ idim ]; \ + off_out += stride[ idim ] * ( lo[ idim ] - lbnd_out[ idim ] ); \ +\ +/* Also store the fractional weight associated with the lower pixel \ + along each dimension. */ \ + wt[ idim ] = frac_lo[ idim ]; \ + } \ +\ +/* If we are using the input data variances as weights, calculate the \ + weight, and scale the fractions of each input pixel by the weight. */ \ + if( Varwgt ) { \ + wgt = pow( in_var[ off_in ], -1.0/(double)ndim_out ); \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + frac_lo[ idim ] *= wgt; \ + frac_hi[ idim ] *= wgt; \ + wt[ idim ] = frac_lo[ idim ]; \ + } \ + } \ +\ +/* If OK, increment the number of input pixels pasted into the output array. */ \ + if ( !bad ) { \ + if( nused ) (*nused)++; \ +\ +/* Loop over adjacent output pixels to divide up the input value. */ \ + idim = ndim_out - 1; \ + wtprod[ idim ] = 1.0; \ + done = 0; \ + do { \ +\ +/* Each pixel pixel to be updated has a total weight equal to the product \ + of the weights which account for the displacement of its centre from \ + the required position along each dimension. However, since we typically \ + only change the index of one dimension at a time, we can avoid forming \ + this product repeatedly by retaining an array of accumulated weight \ + products for all higher dimensions. We need then only update the \ + lower elements in this array, corresponding to those dimensions \ + whose index has changed. We do this here, "idim" being the index of \ + the most significant dimension to have changed. Note that on the \ + first pass, all dimensions are considered changed, causing this \ + array to be initialised. */ \ + for ( ii = idim; ii >= 1; ii-- ) { \ + wtprod[ ii - 1 ] = wtprod[ ii ] * wt[ ii ]; \ + } \ +\ +/* Update the relevent output pixel. The pixel weight is formed by including \ + the weight factor for dimension zero, since this is not included in \ + the "wtprod" array. */ \ + pixwt = wtprod[ 0 ] * wt[ 0 ]; \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ + out[ off_out ] += CONV(IntType, c ); \ + if( work ) work[ off_out ] += pixwt; \ + if ( Usevar ) { \ + out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ + } else if ( Genvar && pixwt != 0.0 ) { \ + out_var[ off_out ] += c*c/pixwt; \ + work[ off_out + npix_out ] += pixwt*pixwt; \ + } \ +\ +/* Now update the indices, offset and weight factors to refer to the \ + next output pixel to be updated. */ \ + idim = 0; \ + do { \ +\ +/* The first input dimension which still refers to the pixel with the \ + lower of the two possible indices is switched to refer to the other \ + pixel (with the higher index). The offset into the output array and \ + the fractional weight factor for this dimension are also updated \ + accordingly. */ \ + if ( dim[ idim ] != hi[ idim ] ) { \ + dim[ idim ] = hi[ idim ]; \ + off_out += stride[ idim ]; \ + wt[ idim ] = frac_hi[ idim ]; \ + break; \ +\ +/* Any earlier dimensions (referring to the higher index) are switched \ + back to the lower index, if not already there, before going on to \ + consider the next dimension. (This process is the same as \ + incrementing a binary number and propagating overflows up through \ + successive digits, except that dimensions where the "lo" and "hi" \ + values are the same can only take one value.) The process stops at \ + the first attempt to return the final dimension to the lower \ + index. */ \ + } else { \ + if ( dim[ idim ] != lo[ idim ] ) { \ + dim[ idim ] = lo[ idim ]; \ + off_out -= stride[ idim ]; \ + wt[ idim ] = frac_lo[ idim ]; \ + } \ + done = ( ++idim == ndim_out ); \ + } \ + } while ( !done ); \ + } while ( !done ); \ + } \ + } \ + } + +/* Expand the main macro above to generate a function for each + required signed data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_SPREAD_LINEAR(LD,long double,0) +#endif +MAKE_SPREAD_LINEAR(D,double,0) +MAKE_SPREAD_LINEAR(F,float,0) +MAKE_SPREAD_LINEAR(I,int,1) +MAKE_SPREAD_LINEAR(B,signed char,1) +MAKE_SPREAD_LINEAR(UB,unsigned char,1) + +/* Undefine the macros used above. */ +#undef LINEAR_1D +#undef LINEAR_2D +#undef LINEAR_ND +#undef MAKE_SPREAD_LINEAR + +/* +* Name: +* SpreadNearest + +* Purpose: +* Rebin a data grid, using the nearest-pixel spreading scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void SpreadNearest( int ndim_out, const int *lbnd_out, +* const int *ubnd_out, const *in, +* const *in_var, double infac, int npoint, +* const int *offset, const double *const *coords, +* int flags, badval, int npix_out, *out, +* *out_var, double *work, int64_t *nused, +* int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This is a set of functions which rebins a rectangular region of an +* input grid of data (and, optionally, associated statistical variance +* values) so as to place them into a new output grid. Each input +* grid point may be mapped on to a position in the output grid in +* an arbitrary way. Where the positions given do not correspond +* with a pixel centre in the output grid, the spreading scheme +* used is simply to select the nearest pixel (i.e. the one whose +* bounds contain the supplied position). + +* Parameters: +* ndim_out +* The number of dimensions in the output grid. This should be at +* least one. +* lbnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the first +* pixel in the output grid along each dimension. +* ubnd_out +* Pointer to an array of integers, with "ndim_out" elements. +* This should give the coordinates of the centre of the last +* pixel in the output grid along each dimension. +* +* Note that "lbnd_out" and "ubnd_out" together define the shape +* and size of the output grid, its extent along a particular +* (i'th) dimension being ubnd_out[i]-lbnd_out[i]+1 (assuming "i" +* is zero-based). They also define the output grid's coordinate +* system, with each pixel being of unit extent along each +* dimension with integral coordinate values at its centre. +* in +* Pointer to the array of data to be rebinned. The numerical type +* of these data should match the function used, as given by the +* suffix on the function name. Note that details of how the input +* grid maps on to this array (e.g. the storage order, number of +* dimensions, etc.) is arbitrary and is specified entirely by means +* of the "offset" array. The "in" array should therefore contain +* sufficient elements to accommodate the "offset" values supplied. +* There is no requirement that all elements of the "in" array +* should be rebinned, and any which are not addressed by the +* contents of the "offset" array will be ignored. +* in_var +* An optional pointer to a second array of positive numerical +* values (with the same size and type as the "in" array), which +* represent estimates of the statistical variance associated +* with each element of the "in" array. If this second array is +* given (along with the corresponding "out_var" array), then +* estimates of the variance of the resampled data will also be +* returned. It is addressed in exactly the same way (via the +* "offset" array) as the "in" array. +* +* If no variance estimates are required, a NULL pointer should +* be given. +* infac +* A factor by which to multiply the input data values before use. +* npoint +* The number of input points which are to be rebinned. +* offset +* Pointer to an array of integers with "npoint" elements. For +* each input point, this array should contain the zero-based +* offset in the input array(s) (i.e. the "in" and, optionally, +* the "in_var" arrays) from which the value to be rebinned should +* be obtained. +* coords +* An array of pointers to double, with "ndim_out" elements. +* Element "coords[coord]" should point at the first element of +* an array of double (with "npoint" elements) which contains the +* values of coordinate number "coord" for each point being +* rebinned. The value of coordinate number "coord" for +* rebinning point number "point" is therefore given by +* "coords[coord][point]" (assuming both indices are +* zero-based). If any point has a coordinate value of AST__BAD +* associated with it, then the corresponding input data (and +* variance) value will be ignored. +* flags +* The bitwise OR of a set of flag values which control the +* operation of the function. These are chosend from: +* +* - AST__USEBAD: indicates whether there are "bad" (i.e. missing) data +* in the input array(s) which must be recognised. If this flag is not +* set, all input values are treated literally. +* - AST__GENVAR: Indicates that output variances should be generated +* from the spread of values contributing to each output pixel. +* - AST__USEVAR: Indicates that output variances should be generated +* by rebinning the input variances. +* - AST__VARWGT: Indicates that input variances should be used to +* create weights for the input data values. +* +* Only one of AST__GENVAR and AST__USEVAR should be supplied. +* badval +* If the AST__USEBAD flag is set in the "flags" value (above), +* this parameter specifies the value which is used to identify +* bad data and/or variance values in the input array(s). Its +* numerical type must match that of the "in" (and "in_var") +* arrays. The same value will also be used to flag any output +* array elements for which resampled values could not be +* obtained. The output arrays(s) may be flagged with this +* value whether or not the AST__USEBAD flag is set (the +* function return value indicates whether any such values have +* been produced). +* npix_out +* Number of pixels in output array. +* out +* Pointer to an array with the same data type as the "in" +* array, into which the rebinned data will be returned. The +* storage order should be such that the index of the first grid +* dimension varies most rapidly and that of the final dimension +* least rapidly (i.e. Fortran array storage order). +* out_var +* An optional pointer to an array with the same data type and +* size as the "out" array, into which variance estimates for +* the rebinned values may be returned. This array will only be +* used if the "in_var" array has been given. The values returned +* are estimates of the statistical variance of the corresponding +* values in the "out" array, on the assumption that all errors in +* input grid values (in the "in" array) are statistically independent +* and that their variance estimates (in the "in_var" array) may +* simply be summed (with appropriate weighting factors). +* +* If no output variance estimates are required, a NULL pointer +* should be given. +* work +* A pointer to an array with the same data type and size as the "out" +* array which is used as work space. The values in the supplied +* array are incremented on exit by the sum of the weights used +* with each output pixel. +* nused +* An optional pointer to a size_t which will be incremented by the +* number of input values pasted into the output array. Ignored if NULL. + +* Notes: +* - There is a separate function for each numerical type of +* gridded data, distinguished by replacing the in the function +* name by the appropriate 1- or 2-character suffix. +*/ +/* Define a macro to implement the function for a specific data type. */ +#define MAKE_SPREAD_NEAREST(X,Xtype,IntType) \ +static void SpreadNearest##X( int ndim_out, \ + const int *lbnd_out, const int *ubnd_out, \ + const Xtype *in, const Xtype *in_var, \ + double infac, int npoint, const int *offset, \ + const double *const *coords, int flags, \ + Xtype badval, int npix_out, Xtype *out, \ + Xtype *out_var, double *work, int64_t *nused, \ + int *status ) { \ +\ +/* Local Variables: */ \ + Xtype c; /* Contribution to output value */ \ + Xtype in_val; /* Input data value */ \ + double *xn_max; /* Pointer to upper limits array (n-d) */ \ + double *xn_min; /* Pointer to lower limits array (n-d) */ \ + double pfac; /* Input weight with extra supplied factor */ \ + double pixwt; /* Weight for input value */ \ + double x; /* x coordinate value */ \ + double xmax; /* x upper limit */ \ + double xmin; /* x lower limit */ \ + double xn; /* Coordinate value (n-d) */ \ + double y; /* y coordinate value */ \ + double ymax; /* y upper limit */ \ + double ymin; /* y lower limit */ \ + int *stride; /* Pointer to array of dimension strides */ \ + int bad; /* Output pixel bad? */ \ + int genvar; /* Generate output variances? */ \ + int idim; /* Loop counter for dimensions */ \ + int ix; /* Number of pixels offset in x direction */ \ + int ixn; /* Number of pixels offset (n-d) */ \ + int iy; /* Number of pixels offset in y direction */ \ + int off_in; /* Pixel offset into input array */ \ + int off_out; /* Pixel offset into output array */ \ + int point; /* Loop counter for output points */ \ + int s; /* Temporary variable for strides */ \ + int usebad; /* Use "bad" input pixel values? */ \ + int usevar; /* Process variance array? */ \ + int varwgt; /* Use input variances as weights? */ \ + int ystride; /* Stride along input grid y direction */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Determine if we are processing bad pixels or variances. */ \ + usebad = flags & AST__USEBAD; \ + usevar = 0; \ + genvar = 0; \ + if( flags & AST__GENVAR ) { \ + genvar = out_var && work; \ + } else if( flags & AST__USEVAR ) { \ + usevar = in_var && out_var; \ + } \ + varwgt = ( flags & AST__VARWGT ) && in_var && work; \ +\ +/* Handle the 1-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + if ( ndim_out == 1 ) { \ +\ +/* Calculate the coordinate limits of the output array. */ \ + xmin = (double) lbnd_out[ 0 ] - 0.5; \ + xmax = (double) ubnd_out[ 0 ] + 0.5; \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + NEAR_1D(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + NEAR_1D(X,Xtype,1,0,1,IntType,1) \ + } else { \ + NEAR_1D(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + NEAR_1D(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + NEAR_1D(X,Xtype,0,0,1,IntType,1) \ + } else { \ + NEAR_1D(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + NEAR_1D(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + NEAR_1D(X,Xtype,1,0,1,IntType,0) \ + } else { \ + NEAR_1D(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + NEAR_1D(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + NEAR_1D(X,Xtype,0,0,1,IntType,0) \ + } else { \ + NEAR_1D(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ +\ +/* Handle the 2-dimensional case optimally. */ \ +/* ---------------------------------------- */ \ + } else if ( ndim_out == 2 ) { \ +\ +/* Calculate the stride along the y dimension of the output grid. */ \ + ystride = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; \ +\ +/* Calculate the coordinate limits of the output array in each \ + dimension. */ \ + xmin = (double) lbnd_out[ 0 ] - 0.5; \ + xmax = (double) ubnd_out[ 0 ] + 0.5; \ + ymin = (double) lbnd_out[ 1 ] - 0.5; \ + ymax = (double) ubnd_out[ 1 ] + 0.5; \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + NEAR_2D(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + NEAR_2D(X,Xtype,1,0,1,IntType,1) \ + } else { \ + NEAR_2D(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + NEAR_2D(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + NEAR_2D(X,Xtype,0,0,1,IntType,1) \ + } else { \ + NEAR_2D(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + NEAR_2D(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + NEAR_2D(X,Xtype,1,0,1,IntType,0) \ + } else { \ + NEAR_2D(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + NEAR_2D(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + NEAR_2D(X,Xtype,0,0,1,IntType,0) \ + } else { \ + NEAR_2D(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ +\ +/* Handle other numbers of dimensions. */ \ +/* ----------------------------------- */ \ + } else { \ +\ +/* Allocate workspace. */ \ + stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ + xn_max = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + xn_min = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ + if ( astOK ) { \ +\ +/* Calculate the stride along each dimension of the output grid. */ \ + for ( s = 1, idim = 0; idim < ndim_out; idim++ ) { \ + stride[ idim ] = s; \ + s *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ +\ +/* Calculate the coordinate limits of the output grid in each \ + dimension. */ \ + xn_min[ idim ] = (double) lbnd_out[ idim ] - 0.5; \ + xn_max[ idim ] = (double) ubnd_out[ idim ] + 0.5; \ + } \ +\ +/* Identify eight cases, according to whether bad pixels and/or variances \ + are being processed and/or used. In each case we assign constant values \ + (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ + handling bad pixels and variances can be eliminated by the compiler's \ + optimisation system when not required. */ \ + if( varwgt ) { \ + if ( usebad ) { \ + if ( usevar ) { \ + NEAR_ND(X,Xtype,1,1,0,IntType,1) \ + } else if ( genvar ) { \ + NEAR_ND(X,Xtype,1,0,1,IntType,1) \ + } else { \ + NEAR_ND(X,Xtype,1,0,0,IntType,1) \ + } \ + } else { \ + if ( usevar ) { \ + NEAR_ND(X,Xtype,0,1,0,IntType,1) \ + } else if ( genvar ) { \ + NEAR_ND(X,Xtype,0,0,1,IntType,1) \ + } else { \ + NEAR_ND(X,Xtype,0,0,0,IntType,1) \ + } \ + } \ + } else { \ + if ( usebad ) { \ + if ( usevar ) { \ + NEAR_ND(X,Xtype,1,1,0,IntType,0) \ + } else if ( genvar ) { \ + NEAR_ND(X,Xtype,1,0,1,IntType,0) \ + } else { \ + NEAR_ND(X,Xtype,1,0,0,IntType,0) \ + } \ + } else { \ + if ( usevar ) { \ + NEAR_ND(X,Xtype,0,1,0,IntType,0) \ + } else if ( genvar ) { \ + NEAR_ND(X,Xtype,0,0,1,IntType,0) \ + } else { \ + NEAR_ND(X,Xtype,0,0,0,IntType,0) \ + } \ + } \ + } \ + } \ +\ +/* Free the workspace. */ \ + stride = astFree( stride ); \ + xn_max = astFree( xn_max ); \ + xn_min = astFree( xn_min ); \ + } \ +\ +} + + + + + +#define NEAR_1D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Obtain the output x coordinate corresponding to the centre of the \ + current input pixel and test if it lies outside the output grid, or \ + is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* If not, then obtain the offset within the output grid of the pixel \ + which contains the current input point. */ \ + off_out = (int) floor( x + 0.5 ) - lbnd_out[ 0 ]; \ +\ +/* If we are using the input data variances as weights, calculate the \ + weight. */ \ + if( Varwgt ) { \ + pixwt = 1.0/in_var[ off_in ]; \ + } else { \ + pixwt = 1.0; \ + } \ +\ +/* Get the weighted input data value, including any extra scaling. */ \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ +\ +/* Increment the value of this output pixel by the weighted input pixel \ + value, and increment the sum of the weights. */ \ + out[ off_out ] += CONV(IntType, c ); \ + if( work ) work[ off_out ] += pixwt; \ +\ +/* If output variances are being calculated on the basis of the input \ + variances, then we also store the required sum in "out_var". */ \ + if( Usevar ) { \ + out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ +\ +/* If output variances are being calculated on the basis of the spread of \ + input values, we need the sum of the squared weighted data values, the \ + sum of the weights (already in the first half of the "work" array), and \ + the sum of the squared weights. */ \ + } else if( Genvar && pixwt != 0.0 ) { \ + out_var[ off_out ] += c*c/pixwt; \ + work[ off_out + npix_out ] += pixwt*pixwt; \ + } \ + } \ + } + + + + + + +#define NEAR_2D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ +/* Obtain the output y coordinate corresponding to the centre of the \ + current input pixel and test if it lies outside the output grid, or \ + is bad. */ \ + y = coords[ 1 ][ point ]; \ + bad = bad || ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ + if ( !bad ) { \ +\ +/* Obtain the output x coordinate corresponding to the centre of the \ + current input pixel and test if it lies outside the output grid, or \ + is bad. */ \ + x = coords[ 0 ][ point ]; \ + bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ + if ( !bad ) { \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* Obtain the offsets along each output grid dimension of the output \ + pixel which is to receive the input pixel value. */ \ + ix = (int) floor( x + 0.5 ) - lbnd_out[ 0 ]; \ + iy = (int) floor( y + 0.5 ) - lbnd_out[ 1 ]; \ +\ +/* Calculate this pixel's offset from the start of the output array. */ \ + off_out = ix + ystride * iy; \ +\ +/* If we are using the input data variances as weights, calculate the \ + weight. */ \ + if( Varwgt ) { \ + pixwt = 1.0/in_var[ off_in ]; \ + } else { \ + pixwt = 1.0; \ + } \ +\ +/* Get the weighted input data value, including any extra scaling. */ \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ +\ +/* Increment the value of this output pixel by the weighted input pixel \ + value, and increment the sum of the weights. */ \ + out[ off_out ] += CONV(IntType, c ); \ + if( work ) work[ off_out ] += pixwt; \ +\ +/* If output variances are being calculated on the basis of the input \ + variances, then we also store the required sum in "out_var". */ \ + if( Usevar ) { \ + out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ +\ +/* If output variances are being calculated on the basis of the spread of \ + input values, we need the sum of the squared weighted data values, the \ + sum of the weights (already in the first half of the "work" array), and \ + the sum of the squared weights. */ \ + } else if( Genvar && pixwt != 0.0 ) { \ + out_var[ off_out ] += c*c/pixwt; \ + work[ off_out + npix_out ] += pixwt*pixwt; \ + } \ + } \ + } \ + } + + + +#define NEAR_ND(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ +\ +/* Loop round all input points which are to be rebinned. */ \ + for( point = 0; point < npoint; point++ ) { \ +\ +/* Obtain the input data value which is to be added into the output array. */ \ + off_in = offset[ point ]; \ + in_val = in[ off_in ]; \ +\ +/* If necessary, test if the input data value or variance is bad. If we \ + are using the reciprocal of the input variances as weights, then \ + variance values of zero are also effectively bad (but we can use input \ + variances of zero otherwise). */ \ + if ( Usebad ) { \ + bad = ( in_val == badval ); \ + if ( Varwgt ) { \ + bad = bad || ( in_var[ off_in ] == badval ) \ + || ( in_var[ off_in ] <= 0.0 ); \ + } else if ( Usevar ) { \ + bad = bad || ( in_var[ off_in ] == badval ); \ + } \ + } else { \ + if ( Varwgt ) { \ + bad = ( in_var[ off_in ] <= 0.0 ); \ + } else { \ + bad = 0; \ + } \ + } \ +\ + if( !bad ) { \ +\ +/* Initialise the offset into the output array. Then loop to obtain \ + each coordinate associated with the current output point. */ \ + off_out = 0; \ + for ( idim = 0; idim < ndim_out; idim++ ) { \ + xn = coords[ idim ][ point ]; \ +\ +/* Test if the coordinate lies outside the output grid, or is bad. If \ + either is true, the corresponding input pixel value will be ignored, \ + so give up on this point. */ \ + bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ + ( xn == AST__BAD ); \ + if ( bad ) { \ + break; \ + } \ +\ +/* Obtain the offset along the current output grid dimension of the \ + output pixel which is to receive the input pixel value. */ \ + ixn = (int) floor( xn + 0.5 ) - lbnd_out[ idim ]; \ +\ +/* Accumulate this pixel's offset from the start of the output array. */ \ + off_out += ixn * stride[ idim ]; \ + } \ +\ + if( !bad ) { \ +\ +/* Increment the number of input pixels pasted into the output array. */ \ + if( nused ) (*nused)++; \ +\ +/* If we are using the input data variances as weights, calculate the \ + weight. */ \ + if( Varwgt ) { \ + pixwt = 1.0/in_var[ off_in ]; \ + } else { \ + pixwt = 1.0; \ + } \ +\ +/* Get the weighted input data value, including any extra scaling. */ \ + pfac = pixwt*infac; \ + c = CONV(IntType,pfac*in_val); \ +\ +/* Increment the value of this output pixel by the weighted input pixel \ + value, and increment the sum of the weights. */ \ + out[ off_out ] += CONV(IntType, c ); \ + if( work ) work[ off_out ] += pixwt; \ +\ +/* If output variances are being calculated on the basis of the input \ + variances, then we also store the required sum in "out_var". */ \ + if( Usevar ) { \ + out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ +\ +/* If output variances are being calculated on the basis of the spread of \ + input values, we need the sum of the squared weighted data values, the \ + sum of the weights (already in the first half of the "work" array), and \ + the sum of the squared weights. */ \ + } else if( Genvar && pixwt != 0.0 ) { \ + out_var[ off_out ] += c*c/pixwt; \ + work[ off_out + npix_out ] += pixwt*pixwt; \ + } \ + } \ + } \ + } + + + + + + +/* Expand the main macro above to generate a function for each + required signed data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_SPREAD_NEAREST(LD,long double,0) +#endif + +MAKE_SPREAD_NEAREST(D,double,0) +MAKE_SPREAD_NEAREST(F,float,0) +MAKE_SPREAD_NEAREST(I,int,1) +MAKE_SPREAD_NEAREST(B,signed char,1) +MAKE_SPREAD_NEAREST(UB,unsigned char,1) + +/* Undefine the macros used above. */ +#undef NEAR_ND +#undef NEAR_2D +#undef NEAR_1D +#undef MAKE_SPREAD_NEAREST + + + + + + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Mapping member function (over-rides the astTestAttrib protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Mapping's attributes. + +* Parameters: +* this +* Pointer to the Mapping. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *this; /* Pointer to the Mapping structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Mapping structure. */ + this = (AstMapping *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* Invert. */ +/* ------- */ + if ( !strcmp( attrib, "invert" ) ) { + result = astTestInvert( this ); + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + result = astTestReport( this ); + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + } else if ( !strcmp( attrib, "nin" ) || + !strcmp( attrib, "islinear" ) || + !strcmp( attrib, "issimple" ) || + !strcmp( attrib, "nout" ) || + !strcmp( attrib, "tranforward" ) || + !strcmp( attrib, "traninverse" ) ) { + result = 0; + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static void Tran1( AstMapping *this, int npoint, const double xin[], + int forward, double xout[], int *status ) { +/* +*++ +* Name: +c astTran1 +f AST_TRAN1 + +* Purpose: +* Transform 1-dimensional coordinates. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astTran1( AstMapping *this, int npoint, const double xin[], +c int forward, double xout[] ) +f CALL AST_TRAN1( THIS, NPOINT, XIN, FORWARD, XOUT, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function applies a Mapping to transform the coordinates of +f This routine applies a Mapping to transform the coordinates of +* a set of points in one dimension. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping to be applied. +c npoint +f NPOINT = INTEGER (Given) +* The number of points to be transformed. +c xin +f XIN( NPOINT ) = DOUBLE PRECISION (Given) +c An array of "npoint" coordinate values for the input +f An array of coordinate values for the input +* (untransformed) points. +c forward +f FORWARD = LOGICAL (Given) +c A non-zero value indicates that the Mapping's forward +c coordinate transformation is to be applied, while a zero +c value indicates that the inverse transformation should be +c used. +f A .TRUE. value indicates that the Mapping's forward +f coordinate transformation is to be applied, while a .FALSE. +f value indicates that the inverse transformation should be +f used. +c xout +f XOUT( NPOINT ) = DOUBLE PRECISION (Returned) +c An array (with "npoint" elements) into which the +f An array into which the +* coordinates of the output (transformed) points will be written. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The Mapping supplied must have the value 1 for both its Nin +* and Nout attributes. +*-- +*/ + +/* Local Variables: */ + AstPointSet *in_points; /* Pointer to input PointSet */ + AstPointSet *out_points; /* Pointer to output PointSet */ + const double *in_ptr[ 1 ]; /* Array of input data pointers */ + double *out_ptr[ 1 ]; /* Array of output data pointers */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the Mapping and numbers of points/coordinates. */ + ValidateMapping( this, forward, npoint, 1, 1, "astTran1", status ); + +/* Set up pointers to the input and output coordinate arrays. */ + if ( astOK ) { + in_ptr[ 0 ] = xin; + out_ptr[ 0 ] = xout; + +/* Create PointSets to describe the input and output points. */ + in_points = astPointSet( npoint, 1, "", status ); + out_points = astPointSet( npoint, 1, "", status ); + +/* Associate the data pointers with the PointSets (note we must + explicitly remove the "const" qualifier from the input data here, + although they will not be modified). */ + astSetPoints( in_points, (double **) in_ptr ); + astSetPoints( out_points, out_ptr ); + +/* Apply the required transformation to the coordinates. */ + (void) astTransform( this, in_points, forward, out_points ); + +/* If the Mapping's Report attribute is set, report the effect the + Mapping has had on the coordinates. */ + if ( astGetReport( this ) ) astReportPoints( this, forward, + in_points, out_points ); + +/* Delete the two PointSets. */ + in_points = astDelete( in_points ); + out_points = astDelete( out_points ); + } +} + +static void Tran2( AstMapping *this, + int npoint, const double xin[], const double yin[], + int forward, double xout[], double yout[], int *status ) { +/* +*++ +* Name: +c astTran2 +f AST_TRAN2 + +* Purpose: +* Transform 2-dimensional coordinates. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astTran2( AstMapping *this, +c int npoint, const double xin[], const double yin[], +c int forward, double xout[], double yout[] ) +f CALL AST_TRAN2( THIS, NPOINT, XIN, YIN, FORWARD, XOUT, YOUT, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function applies a Mapping to transform the coordinates of +f This routine applies a Mapping to transform the coordinates of +* a set of points in two dimensions. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping to be applied. +c npoint +f NPOINT = INTEGER (Given) +* The number of points to be transformed. +c xin +f XIN( NPOINT ) = DOUBLE PRECISION (Given) +c An array of "npoint" X-coordinate values for the input +f An array of X-coordinate values for the input +* (untransformed) points. +c yin +f YIN( NPOINT ) = DOUBLE PRECISION (Given) +c An array of "npoint" Y-coordinate values for the input +f An array of Y-coordinate values for the input +* (untransformed) points. +c forward +f FORWARD = LOGICAL (Given) +c A non-zero value indicates that the Mapping's forward +c coordinate transformation is to be applied, while a zero +c value indicates that the inverse transformation should be +c used. +f A .TRUE. value indicates that the Mapping's forward +f coordinate transformation is to be applied, while a .FALSE. +f value indicates that the inverse transformation should be +f used. +c xout +f XOUT( NPOINT ) = DOUBLE PRECISION (Returned) +c An array (with "npoint" elements) into which the +f An array into which the +* X-coordinates of the output (transformed) points will be written. +c yout +f YOUT( NPOINT ) = DOUBLE PRECISION (Returned) +c An array (with "npoint" elements) into which the +f An array into which the +* Y-coordinates of the output (transformed) points will be written. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The Mapping supplied must have the value 2 for both its Nin +* and Nout attributes. +*-- +*/ + +/* Local Variables: */ + AstPointSet *in_points; /* Pointer to input PointSet */ + AstPointSet *out_points; /* Pointer to output PointSet */ + const double *in_ptr[ 2 ]; /* Array of input data pointers */ + double *out_ptr[ 2 ]; /* Array of output data pointers */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the Mapping and the numbers of points/coordinates. */ + ValidateMapping( this, forward, npoint, 2, 2, "astTran2", status ); + +/* Set up pointers to the input and output coordinate arrays. */ + if ( astOK ) { + in_ptr[ 0 ] = xin; + in_ptr[ 1 ] = yin; + out_ptr[ 0 ] = xout; + out_ptr[ 1 ] = yout; + +/* Create PointSets to describe the input and output points. */ + in_points = astPointSet( npoint, 2, "", status ); + out_points = astPointSet( npoint, 2, "", status ); + +/* Associate the data pointers with the PointSets (note we must + explicitly remove the "const" qualifier from the input data here, + although they will not be modified). */ + astSetPoints( in_points, (double **) in_ptr ); + astSetPoints( out_points, out_ptr ); + +/* Apply the required transformation to the coordinates. */ + (void) astTransform( this, in_points, forward, out_points ); + +/* If the Mapping's Report attribute is set, report the effect the + Mapping has had on the coordinates. */ + if ( astGetReport( this ) ) astReportPoints( this, forward, + in_points, out_points ); + +/* Delete the two PointSets. */ + in_points = astDelete( in_points ); + out_points = astDelete( out_points ); + } +} + +static void TranGrid( AstMapping *this, int ncoord_in, const int lbnd[], + const int ubnd[], double tol, int maxpix, int forward, + int ncoord_out, int outdim, double *out, int *status ) { +/* +*++ +* Name: +c astTranGrid +f AST_TRANGRID + +* Purpose: +* Transform a grid of positions + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astTranGrid( AstMapping *this, int ncoord_in, +c const int lbnd[], const int ubnd[], +c double tol, int maxpix, int forward, +c int ncoord_out, int outdim, double *out ); +f CALL AST_TRANGRID( THIS, NCOORD_IN, LBND, UBND, TOL, MAXPIX, +f FORWARD, NCOORD_OUT, OUTDIM, OUT, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +* This function uses the supplied Mapping to transforms a regular square +* grid of points covering a specified box. It attempts to do this +* quickly by first approximating the Mapping with a linear transformation +* applied over the whole region of the input grid which is being used. +* If this proves to be insufficiently accurate, the input region is +* sub-divided into two along its largest dimension and the process is +* repeated within each of the resulting sub-regions. This process of +* sub-division continues until a sufficiently good linear approximation +* is found, or the region to which it is being applied becomes too small +* (in which case the original Mapping is used directly). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping to be applied. +c ncoord_in +f NCOORD_IN = INTEGER (Given) +* The number of coordinates being supplied for each box corner +* (i.e. the number of dimensions of the space in which the +* input points reside). +c lbnd +f LBND( NCOORD_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ncoord_in" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +c ubnd +f UBND( NCOORD_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ncoord_in" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +c Note that "lbnd" and "ubnd" together define the shape +f Note that LBND and UBND together define the shape +* and size of the input grid, its extent along a particular +c (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the +c index "j" to be zero-based). They also define +f (J'th) dimension being UBND(J)-LBND(J)+1. They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre. +c tol +f TOL = DOUBLE PRECISION (Given) +* The maximum tolerable geometrical distortion which may be +* introduced as a result of approximating non-linear Mappings +* by a set of piece-wise linear transformations. This should be +* expressed as a displacement within the output coordinate system +* of the Mapping. +* +* If piece-wise linear approximation is not required, a value +* of zero may be given. This will ensure that the Mapping is +* used without any approximation, but may increase execution +* time. +* +* If the value is too high, discontinuities between the linear +* approximations used in adjacent panel will be higher. If this +* is a problem, reduce the tolerance value used. +c maxpix +f MAXPIX = INTEGER (Given) +* A value which specifies an initial scale size (in input grid points) +* for the adaptive algorithm which approximates non-linear Mappings +* with piece-wise linear transformations. Normally, this should +* be a large value (larger than any dimension of the region of +* the input grid being used). In this case, a first attempt to +* approximate the Mapping by a linear transformation will be +* made over the entire input region. +* +* If a smaller value is used, the input region will first be +c divided into sub-regions whose size does not exceed "maxpix" +f divided into sub-regions whose size does not exceed MAXPIX +* grid points in any dimension. Only at this point will attempts +* at approximation commence. +* +* This value may occasionally be useful in preventing false +* convergence of the adaptive algorithm in cases where the +* Mapping appears approximately linear on large scales, but has +* irregularities (e.g. holes) on smaller scales. A value of, +* say, 50 to 100 grid points can also be employed as a safeguard +* in general-purpose software, since the effect on performance is +* minimal. +* +* If too small a value is given, it will have the effect of +* inhibiting linear approximation altogether (equivalent to +c setting "tol" to zero). Although this may degrade +f setting TOL to zero). Although this may degrade +* performance, accurate results will still be obtained. +c forward +f FORWARD = LOGICAL (Given) +c A non-zero value indicates that the Mapping's forward +c coordinate transformation is to be applied, while a zero +c value indicates that the inverse transformation should be +c used. +f A .TRUE. value indicates that the Mapping's forward +f coordinate transformation is to be applied, while a .FALSE. +f value indicates that the inverse transformation should be +f used. +c ncoord_out +f NCOORD_OUT = INTEGER (Given) +* The number of coordinates being generated by the Mapping for +* each output point (i.e. the number of dimensions of the +* space in which the output points reside). This need not be +c the same as "ncoord_in". +f the same as NCOORD_IN. +c outdim +f OUTDIM = INTEGER (Given) +c The number of elements along the second dimension of the "out" +f The number of elements along the first dimension of the OUT +* array (which will contain the output coordinates). The value +* given should not be less than the number of points in the grid. +c out +f OUT( OUTDIM, NCOORD_OUT ) = DOUBLE PRECISION (Returned) +c The address of the first element in a 2-dimensional array of +c shape "[ncoord_out][outdim]", into +c which the coordinates of the output (transformed) points will +c be written. These will be stored such that the value of +c coordinate number "coord" for output point number "point" +c will be found in element "out[coord][point]". +f An array into which the coordinates of the output +f (transformed) points will be written. These will be stored +f such that the value of coordinate number COORD for output +f point number POINT will be found in element OUT(POINT,COORD). +* The points are ordered such that the first axis of the input +* grid changes most rapidly. For example, if the input grid is +* 2-dimensional and extends from (2,-1) to (3,1), the output +* points will be stored in the order (2,-1), (3, -1), (2,0), (3,0), +* (2,1), (3,1). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - If the forward coordinate transformation is being applied, the +c Mapping supplied must have the value of "ncoord_in" for its Nin +c attribute and the value of "ncoord_out" for its Nout attribute. If +c the inverse transformation is being applied, these values should +c be reversed. +f - If the forward coordinate transformation is being applied, the +f Mapping supplied must have the value of NCOORD_IN for its Nin +f attribute and the value of NCOORD_OUT for its Nout attribute. If +f the inverse transformation is being applied, these values should +f be reversed. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific data */ + AstMapping *simple; /* Pointer to simplified Mapping */ + double **out_ptr; /* Pointer to array of output data pointers */ + int coord; /* Loop counter for coordinates */ + int idim; /* Loop counter for coordinate dimensions */ + int npoint; /* Number of points in the grid */ + int64_t mpix; /* Number of points for testing */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to a structure holding thread-specific global data values */ + astGET_GLOBALS(this); + +/* Calculate the number of points in the grid, and check that the lower and + upper bounds of the input grid are consistent. Report an error if any + pair is not. */ + mpix = 1; + for ( idim = 0; idim < ncoord_in; idim++ ) { + if ( lbnd[ idim ] > ubnd[ idim ] ) { + astError( AST__GBDIN, "astTranGrid(%s): Lower bound of " + "input grid (%d) exceeds corresponding upper bound " + "(%d).", status, astGetClass( this ), + lbnd[ idim ], ubnd[ idim ] ); + astError( AST__GBDIN, "Error in input dimension %d.", status, + idim + 1 ); + break; + } else { + mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; + } + } + +/* Report an error if there are too many pixels in the input. */ + npoint = mpix; + if ( astOK && npoint != mpix ) { + astError( AST__EXSPIX, "astTranGrid(%s): Supplied grid " + "contains too many points (%g): must be fewer than %d.", + status, astGetClass( this ), (double) mpix, INT_MAX/ncoord_out ); + } + + mpix = outdim*ncoord_out; + if ( astOK && (int) mpix != mpix ) { + astError( AST__EXSPIX, "astTranGrid(%s): Supplied output array " + "contains too many pixels (%g): must be fewer than %d.", + status, astGetClass( this ), (double) mpix, INT_MAX ); + } + + +/* Validate the mapping and numbers of points/coordinates. */ + ValidateMapping( this, forward, npoint, ncoord_in, ncoord_out, + "astTranGrid", status ); + +/* Check that the positional accuracy tolerance supplied is valid and + report an error if necessary. */ + if ( astOK && ( tol < 0.0 ) ) { + astError( AST__PATIN, "astTranGrid(%s): Invalid positional " + "accuracy tolerance (%.*g pixel).", status, + astGetClass( this ), DBL_DIG, tol ); + astError( AST__PATIN, "This value should not be less than zero." , status); + } + +/* Check that the initial scale size in grid points supplied is valid and + report an error if necessary. */ + if ( astOK && ( maxpix < 0 ) ) { + astError( AST__SSPIN, "astTranGrid(%s): Invalid initial scale " + "size in grid points (%d).", status, astGetClass( this ), maxpix ); + astError( AST__SSPIN, "This value should not be less than zero." , status); + } + +/* Validate the output array dimension argument. */ + if ( astOK && ( outdim < npoint ) ) { + astError( AST__DIMIN, "astTranGrid(%s): The output array dimension value " + "(%d) is invalid.", status, astGetClass( this ), outdim ); + astError( AST__DIMIN, "This should not be less than the number of " + "grid points being transformed (%d).", status, npoint ); + } + +/* If there are sufficient pixels to make it worthwhile, simplify the + Mapping supplied to improve performance. Otherwise, just clone the + Mapping pointer. Note we save a pointer to the original Mapping so + that lower-level functions can use it if they need to report an error. */ + simple = NULL; + unsimplified_mapping = this; + if ( astOK ) { + if ( npoint > 1024 ) { + simple = astSimplify( this ); + +/* Report an error if the required transformation of this simplified + Mapping is not defined. */ + if( astOK ) { + if ( forward && !astGetTranForward( simple ) ) { + astError( AST__TRNND, "astTranGrid(%s): A forward coordinate " + "transformation is not defined by the %s supplied.", status, + astGetClass( unsimplified_mapping ), + astGetClass( unsimplified_mapping ) ); + } else if ( !forward && !astGetTranInverse( simple ) ) { + astError( AST__TRNND, "astTranGrid(%s): An inverse coordinate " + "transformation is not defined by the %s supplied.", status, + astGetClass( unsimplified_mapping ), + astGetClass( unsimplified_mapping ) ); + } + } + + } else { + simple = astClone( this ); + } + +/* Allocate memory to hold the array of output data pointers. */ + out_ptr = astMalloc( sizeof( double * ) * (size_t) ncoord_out ); + +/* Initialise the output data pointers to point into the "out" array. */ + if ( astOK ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + out_ptr[ coord ] = out + coord * outdim; + } + +/* If required, temporarily invert the Mapping. */ + if( !forward ) astInvert( simple ); + +/* Perform the transformation. */ + TranGridAdaptively( simple, ncoord_in, lbnd, ubnd, lbnd, ubnd, tol, + maxpix, ncoord_out, out_ptr, status ); + +/* If required, uninvert the Mapping. */ + if( !forward ) astInvert( simple ); + + } + +/* Free the memory used for the data pointers. */ + out_ptr = astFree( out_ptr ); + +/* Annul the pointer to the simplified/cloned Mapping. */ + simple = astAnnul( simple ); + } +} + +static void TranGridAdaptively( AstMapping *this, int ncoord_in, + const int *lbnd_in, const int *ubnd_in, + const int lbnd[], const int ubnd[], + double tol, int maxpix, int ncoord_out, + double *out[], int *status ){ +/* +* Name: +* TranGridAdaptively + +* Purpose: +* Transform grid positions adaptively. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void TranGridAdaptively( AstMapping *this, int ncoord_in, +* const int *lbnd_in, const int *ubnd_in, +* const int lbnd[], const int ubnd[], +* double tol, int maxpix, int ncoord_out, +* double *out[] ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function transforms grid points within a specified section of a +* rectangular grid (with any number of dimensions) using the forward +* transformation of the specified Mapping. +* +* This function is very similar to TranGridWithBlocking and TranGridSection +* which lie below it in the calling hierarchy. However, this function +* also attempts to adapt to the Mapping supplied and to sub-divide the +* section being transformed into smaller sections within which a linear +* approximation to the Mapping may be used. This reduces the number of +* Mapping evaluations, thereby improving efficiency particularly when +* complicated Mappings are involved. + +* Parameters: +* this +* Pointer to the Mapping to be applied. The forward transformation +* is used. +* ncoord_in +* The number of coordinates being supplied for each box corner +* (i.e. the number of dimensions of the space in which the +* input points reside). +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the whole input grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* lbnd +* Pointer to an array of integers, with "ncoord_in" elements, +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +* ubnd +* Pointer to an array of integers, with "ncoord_in" elements, +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +* Note that "lbnd" and "ubnd" together define the shape +* and size of the input grid, its extent along a particular +* (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the +* index "j" to be zero-based). They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre. +* tol +* The maximum tolerable geometrical distortion which may be +* introduced as a result of approximating non-linear Mappings +* by a set of piece-wise linear transformations. This should be +* expressed as a displacement in pixels in the output grid's +* coordinate system. +* +* If piece-wise linear approximation is not required, a value +* of zero may be given. This will ensure that the Mapping is +* used without any approximation, but may increase execution +* time. +* +* If the value is too high, discontinuities between the linear +* approximations used in adjacent panel will be higher. If this +* is a problem, reduce the tolerance value used. +* maxpix +* A value which specifies an initial scale size (in grid points) +* for the adaptive algorithm which approximates non-linear Mappings +* with piece-wise linear transformations. Normally, this should +* be a large value (larger than any dimension of the region of +* the input grid being used). In this case, a first attempt to +* approximate the Mapping by a linear transformation will be +* made over the entire input region. +* +* If a smaller value is used, the input region will first be +* divided into sub-regions whose size does not exceed "maxpix" +* grid points in any dimension. Only at this point will attempts +* at approximation commence. +* +* This value may occasionally be useful in preventing false +* convergence of the adaptive algorithm in cases where the +* Mapping appears approximately linear on large scales, but has +* irregularities (e.g. holes) on smaller scales. A value of, +* say, 50 to 100 grid points can also be employed as a safeguard +* in general-purpose software, since the effect on performance is +* minimal. +* +* If too small a value is given, it will have the effect of +* inhibiting linear approximation altogether (equivalent to +* setting "tol" to zero). Although this may degrade +* performance, accurate results will still be obtained. +* ncoord_out +* The number of dimensions of the space in which the output points +* reside. +* out +* Pointer to an array with "ndim_out" elements. Element [i] of +* this array is a pointer to an array in which to store the +* transformed values for output axis "i". The points are ordered +* such that the first axis of the input grid changes most rapidly. +* For example, if the input grid is 2-dimensional and extends from +* (2,-1) to (3,1), the output points will be stored in the order +* (2,-1), (3, -1), (2,0), (3,0), (2,1), (3,1). + +*/ + +/* Local Variables: */ + double *flbnd; /* Array holding floating point lower bounds */ + double *fubnd; /* Array holding floating point upper bounds */ + double *linear_fit; /* Pointer to array of fit coefficients */ + int *hi; /* Pointer to array of section upper bounds */ + int *lo; /* Pointer to array of section lower bounds */ + int coord_in; /* Loop counter for input coordinates */ + int dim; /* Output section dimension size */ + int dimx; /* Dimension with maximum section extent */ + int divide; /* Sub-divide the output section? */ + int i; /* Loop count */ + int isLinear; /* Is the transformation linear? */ + int mxdim; /* Largest output section dimension size */ + int npix; /* Number of pixels in output section */ + int npoint; /* Number of points for obtaining a fit */ + int nvertex; /* Number of vertices of output section */ + int toobig; /* Section too big (must sub-divide)? */ + int toosmall; /* Section too small to sub-divide? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Further initialisation. */ + npix = 1; + mxdim = 0; + dimx = 1; + nvertex = 1; + +/* Loop through the input grid dimensions. */ + for ( coord_in = 0; coord_in < ncoord_in; coord_in++ ) { + +/* Obtain the extent in each dimension of the input section which is + to be rebinned, and calculate the total number of pixels it contains. */ + dim = ubnd[ coord_in ] - lbnd[ coord_in ] + 1; + npix *= dim; + +/* Find the maximum dimension size of this input section and note which + dimension has this size. */ + if ( dim > mxdim ) { + mxdim = dim; + dimx = coord_in; + } + +/* Calculate how many vertices the output section has. */ + nvertex *= 2; + } + +/* Calculate how many sample points will be needed (by the astLinearApprox + function) to obtain a linear fit to the Mapping's forward transformation. */ + npoint = 1 + 4 * ncoord_in + 2 * nvertex; + +/* If the number of pixels in the input section is not at least 4 + times this number, we will probably not save significant time by + attempting to obtain a linear fit, so note that the input section + is too small. */ + toosmall = ( npix < ( 4 * npoint ) ); + +/* Note if the maximum dimension of the input section exceeds the + user-supplied scale factor. */ + toobig = ( maxpix < mxdim ); + +/* Assume the Mapping is significantly non-linear before deciding + whether to sub-divide the output section. */ + linear_fit = NULL; + +/* If the output section is too small to be worth obtaining a linear + fit, or if the accuracy tolerance is zero, we will not + sub-divide. This means that the Mapping will be used to transform + each pixel's coordinates and no linear approximation will be + used. */ + if ( toosmall || ( tol == 0.0 ) ) { + divide = 0; + +/* Otherwise, if the largest input section dimension exceeds the + scale length given, we will sub-divide. This offers the possibility + of obtaining a linear approximation to the Mapping over a reduced + range of input coordinates (which will be handled by a recursive + invocation of this function). */ + } else if ( toobig ) { + divide = 1; + +/* If neither of the above apply, then attempt to fit a linear + approximation to the forward transformation of the Mapping over + the range of coordinates covered by the input section. We need to + temporarily copy the integer bounds into floating point arrays to + use astLinearApprox. */ + } else { + +/* Allocate memory for floating point bounds and for the coefficient array */ + flbnd = astMalloc( sizeof( double )*(size_t) ncoord_in ); + fubnd = astMalloc( sizeof( double )*(size_t) ncoord_in ); + linear_fit = astMalloc( sizeof( double )* + (size_t) ( ncoord_out*( ncoord_in + 1 ) ) ); + if( astOK ) { + +/* Copy the bounds into these arrays, and change them so that they refer + to the lower and upper edges of the cell rather than the centre. This + is essential if one of the axes is spanned by a single cell, since + otherwise the upper and lower bounds would be identical. */ + for( i = 0; i < ncoord_in; i++ ) { + flbnd[ i ] = (double) lbnd[ i ] - 0.5; + fubnd[ i ] = (double) ubnd[ i ] + 0.5; + } + +/* Get the linear approximation to the forward transformation. */ + isLinear = astLinearApprox( this, flbnd, fubnd, tol, linear_fit ); + +/* Free the coeff array if the inverse transformation is not linear. */ + if( !isLinear ) linear_fit = astFree( linear_fit ); + + } else { + linear_fit = astFree( linear_fit ); + } + +/* Free resources */ + flbnd = astFree( flbnd ); + fubnd = astFree( fubnd ); + +/* If a linear fit was obtained, we will use it and therefore do not + wish to sub-divide further. Otherwise, we sub-divide in the hope + that this may result in a linear fit next time. */ + divide = !linear_fit; + } + +/* If no sub-division is required, perform the transformation (in a + memory-efficient manner, since the section we are rebinning might + still be very large). This will use the linear fit, if obtained + above. */ + if ( astOK ) { + if ( !divide ) { + TranGridWithBlocking( this, linear_fit, ncoord_in, lbnd_in, + ubnd_in, lbnd, ubnd, ncoord_out, out, status ); + +/* Otherwise, allocate workspace to perform the sub-division. */ + } else { + lo = astMalloc( sizeof( int ) * (size_t) ncoord_in ); + hi = astMalloc( sizeof( int ) * (size_t) ncoord_in ); + if ( astOK ) { + +/* Initialise the bounds of a new input section to match the original + input section. */ + for ( coord_in = 0; coord_in < ncoord_in; coord_in++ ) { + lo[ coord_in ] = lbnd[ coord_in ]; + hi[ coord_in ] = ubnd[ coord_in ]; + } + +/* Replace the upper bound of the section's largest dimension with the + mid-point of the section along this dimension, rounded downwards. */ + hi[ dimx ] = + (int) floor( 0.5 * (double) ( lbnd[ dimx ] + ubnd[ dimx ] ) ); + +/* Rebin the resulting smaller section using a recursive invocation + of this function. */ + TranGridAdaptively( this, ncoord_in, lbnd_in, ubnd_in, lo, hi, + tol, maxpix, ncoord_out, out, status ); + +/* Now set up a second section which covers the remaining half of the + original input section. */ + lo[ dimx ] = hi[ dimx ] + 1; + hi[ dimx ] = ubnd[ dimx ]; + +/* If this section contains pixels, transform it in the same way. */ + if ( lo[ dimx ] <= hi[ dimx ] ) { + TranGridAdaptively( this, ncoord_in, lbnd_in, ubnd_in, lo, hi, + tol, maxpix, ncoord_out, out, status ); + } + } + +/* Free the workspace. */ + lo = astFree( lo ); + hi = astFree( hi ); + } + } + +/* If coefficients for a linear fit were obtained, then free the space + they occupy. */ + if ( linear_fit ) linear_fit = astFree( linear_fit ); +} + +static void TranGridSection( AstMapping *this, const double *linear_fit, + int ndim_in, const int *lbnd_in, + const int *ubnd_in, const int *lbnd, + const int *ubnd, int ndim_out, double *out[], int *status ){ +/* +* Name: +* TranGridSection + +* Purpose: +* Transform grid points within a section of a rectangular grid. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void TranGridSection( AstMapping *this, const double *linear_fit, +* int ndim_in, const int *lbnd_in, +* const int *ubnd_in, const int *lbnd, +* const int *ubnd, int ndim_out, double *out[] ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function transforms grid points within a specified section of a +* rectangular grid (with any number of dimensions) using a specified +* Mapping or, alternatively, a linear approximation fitted to the +* Mapping's forward transformation. + +* Parameters: +* this +* Pointer to a Mapping, whose forward transformation may be +* used to transform the coordinates of points in the input +* grid. +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* linear_fit +* Pointer to an optional array of double which contains the +* coefficients of a linear fit which approximates the above +* Mapping's forward coordinate transformation. If this is +* supplied, it will be used in preference to the above Mapping +* when transforming coordinates. This may be used to enhance +* performance in cases where evaluation of the Mapping's +* forward transformation is expensive. If no linear fit is +* available, a NULL pointer should be supplied. +* +* The way in which the fit coefficients are stored in this +* array and the number of array elements are as defined by the +* astLinearApprox function. +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input data grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input data grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the input data grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* lbnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the first pixel in the +* section of the input data grid which is to be rebinned. +* ubnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the last pixel in the +* section of the input data grid which is to be rebinned. +* +* Note that "lbnd" and "ubnd" define the shape and position of +* the section of the input grid which is to be rebinned. This section +* should lie wholly within the extent of the input grid (as defined +* by the "lbnd_out" and "ubnd_out" arrays). Regions of the input +* grid lying outside this section will be ignored. +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* out +* Pointer to an array with "ndim_out" elements. Element [i] of +* this array is a pointer to an array in which to store the +* transformed values for output axis "i". The points are ordered +* such that the first axis of the input grid changes most rapidly. +* For example, if the input grid is 2-dimensional and extends from +* (2,-1) to (3,1), the output points will be stored in the order +* (2,-1), (3, -1), (2,0), (3,0), (2,1), (3,1). + +* Notes: +* - This function does not take steps to limit memory usage if the +* grids supplied are large. To resample large grids in a more +* memory-efficient way, the ResampleWithBlocking function should +* be used. +*/ + +/* Local Variables: */ + AstPointSet *pset_in; /* Input PointSet for transformation */ + AstPointSet *pset_out; /* Output PointSet for transformation */ + const double *grad; /* Pointer to gradient matrix of linear fit */ + const double *zero; /* Pointer to zero point array of fit */ + double **ptr_in; /* Pointer to input PointSet coordinates */ + double **ptr_out; /* Pointer to output PointSet coordinates */ + double *accum; /* Pointer to array of accumulated sums */ + double x1; /* Interim x coordinate value */ + double xx1; /* Initial x coordinate value */ + double y1; /* Interim y coordinate value */ + double yy1; /* Initial y coordinate value */ + int *dim; /* Pointer to array of output pixel indices */ + int *offset; /* Pointer to array of output pixel offsets */ + int *stride; /* Pointer to array of output grid strides */ + int coord_in; /* Loop counter for input dimensions */ + int coord_out; /* Loop counter for output dimensions */ + int done; /* All pixel indices done? */ + int i1; /* Interim offset into "accum" array */ + int i2; /* Final offset into "accum" array */ + int idim; /* Loop counter for dimensions */ + int ix; /* Loop counter for output x coordinate */ + int iy; /* Loop counter for output y coordinate */ + int npoint; /* Number of output points (pixels) */ + int off1; /* Interim pixel offset into output array */ + int off2; /* Interim pixel offset into output array */ + int off; /* Final pixel offset into output array */ + int point; /* Counter for output points (pixels ) */ + int s; /* Temporary variable for strides */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Further initialisation. */ + pset_in = NULL; + ptr_in = NULL; + ptr_out = NULL; + pset_out = NULL; + +/* Calculate the number of input points, as given by the product of + the input grid dimensions. */ + for ( npoint = 1, coord_in = 0; coord_in < ndim_in; coord_in++ ) { + npoint *= ubnd[ coord_in ] - lbnd[ coord_in ] + 1; + } + +/* Allocate workspace. */ + offset = astMalloc( sizeof( int ) * (size_t) npoint ); + stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Calculate the stride for each input grid dimension. */ + off = 0; + s = 1; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + stride[ coord_in ] = s; + s *= ubnd_in[ coord_in ] - lbnd_in[ coord_in ] + 1; + } + +/* A linear fit to the Mapping is available. */ +/* ========================================= */ + if ( linear_fit ) { + +/* If a linear fit to the Mapping has been provided, then obtain + pointers to the array of gradients and zero-points comprising the + fit. */ + grad = linear_fit + ndim_out; + zero = linear_fit; + +/* Create a PointSet to hold the output grid coordinates and obtain an + array of pointers to its coordinate data. */ + pset_out = astPointSet( npoint, ndim_out, "", status ); + ptr_out = astGetPoints( pset_out ); + if ( astOK ) { + +/* Initialise the count of input points. */ + point = 0; + +/* Handle the 1-dimensional case optimally. */ +/* ---------------------------------------- */ + if ( ( ndim_in == 1 ) && ( ndim_out == 1 ) ) { + +/* Loop through the pixels of the input grid and transform their x + coordinates into the output grid's coordinate system using the + linear fit supplied. Store the results in the PointSet created + above. */ + off = lbnd[ 0 ] - lbnd_in[ 0 ]; + xx1 = zero[ 0 ] + grad[ 0 ] * (double) lbnd[ 0 ]; + + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_out[ 0 ][ point ] = xx1; + xx1 += grad[ 0 ]; + offset[ point++ ] = off++; + } + +/* Handle the 2-dimensional case optimally. */ +/* ---------------------------------------- */ + } else if ( ( ndim_in == 2 ) && ( ndim_out == 2 ) ) { + +/* Loop through the range of y coordinates in the input grid and + calculate interim values of the output coordinates using the linear + fit supplied. */ + x1 = zero[ 0 ] + grad[ 1 ] * (double) ( lbnd[ 1 ] - 1 ); + y1 = zero[ 1 ] + grad[ 3 ] * (double) ( lbnd[ 1 ] - 1 ); + off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) - lbnd_in[ 0 ]; + for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { + x1 += grad[ 1 ]; + y1 += grad[ 3 ]; + +/* Also calculate an interim pixel offset into the input array. */ + off1 += stride[ 1 ]; + +/* Now loop through the range of input x coordinates and calculate + the final values of the input coordinates, storing the results in + the PointSet created above. */ + xx1 = x1 + grad[ 0 ] * (double) lbnd[ 0 ]; + yy1 = y1 + grad[ 2 ] * (double) lbnd[ 0 ]; + off = off1 + lbnd[ 0 ]; + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_out[ 0 ][ point ] = xx1; + xx1 += grad[ 0 ]; + ptr_out[ 1 ][ point ] = yy1; + yy1 += grad[ 2 ]; + +/* Also calculate final pixel offsets into the input array. */ + offset[ point++ ] = off++; + } + } + +/* Handle other numbers of dimensions. */ +/* ----------------------------------- */ + } else { + +/* Allocate workspace. */ + accum = astMalloc( sizeof( double ) * + (size_t) ( ndim_in * ndim_out ) ); + dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Initialise an array of pixel indices for the input grid which refer to the + first pixel which we will rebin. Also calculate the offset of this pixel + within the input array. */ + off = 0; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + dim[ coord_in ] = lbnd[ coord_in ]; + off += stride[ coord_in ] * + ( dim[ coord_in ] - lbnd_in[ coord_in ] ); + } + +/* To calculate each output grid coordinate we must perform a matrix + multiply on the input grid coordinates (using the gradient matrix) + and then add the zero points. However, since we will usually only + be altering one input coordinate at a time (the least + significant), we can avoid the full matrix multiply by accumulating + partial sums for the most significant input coordinates and only + altering those sums which need to change each time. The zero points + never change, so we first fill the "most significant" end of the + "accum" array with these. */ + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + accum[ ( coord_out + 1 ) * ndim_in - 1 ] = + zero[ coord_out ]; + } + coord_in = ndim_in - 1; + +/* Now loop to process each input pixel. */ + for ( done = 0; !done; point++ ) { + +/* To generate the output coordinate that corresponds to the current + input pixel, we work down from the most significant dimension + whose index has changed since the previous pixel we considered + (given by "coord_in"). For each affected dimension, we accumulate + in "accum" the matrix sum (including the zero point) for that + dimension and all higher input dimensions. We must accumulate a + separate set of sums for each output coordinate we wish to + produce. (Note that for the first pixel we process, all dimensions + are considered "changed", so we start by initialising the whole + "accum" array.) */ + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + i1 = coord_out * ndim_in; + for ( idim = coord_in; idim >= 1; idim-- ) { + i2 = i1 + idim; + accum[ i2 - 1 ] = accum[ i2 ] + + dim[ idim ] * grad[ i2 ]; + } + +/* The output coordinate for each dimension is given by the accumulated + sum for input dimension zero (giving the sum over all input + dimensions). We do not store this in the "accum" array, but assign + the result directly to the coordinate array of the PointSet created + earlier. */ + ptr_out[ coord_out ][ point ] = accum[ i1 ] + + dim[ 0 ] * grad[ i1 ]; + } + +/* Store the offset of the current pixel in the input array. */ + offset[ point ] = off; + +/* Now update the array of pixel indices to refer to the next input pixel. */ + coord_in = 0; + do { + +/* The least significant index which currently has less than its maximum + value is incremented by one. The offset into the input array is updated + accordingly. */ + if ( dim[ coord_in ] < ubnd[ coord_in ] ) { + dim[ coord_in ]++; + off += stride[ coord_in ]; + break; + +/* Any less significant indices which have reached their maximum value + are returned to their minimum value and the input pixel offset is + decremented appropriately. */ + } else { + dim[ coord_in ] = lbnd[ coord_in ]; + off -= stride[ coord_in ] * + ( ubnd[ coord_in ] - lbnd[ coord_in ] ); + +/* All the output pixels have been processed once the most significant + pixel index has been returned to its minimum value. */ + done = ( ++coord_in == ndim_in ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + accum = astFree( accum ); + dim = astFree( dim ); + } + } + +/* No linear fit to the Mapping is available. */ +/* ========================================== */ + } else { + +/* Create a PointSet to hold the coordinates of the input pixels and + obtain a pointer to its coordinate data. */ + pset_in = astPointSet( npoint, ndim_in, "", status ); + ptr_in = astGetPoints( pset_in ); + if ( astOK ) { + +/* Initialise the count of input points. */ + point = 0; + +/* Handle the 1-dimensional case optimally. */ +/* ---------------------------------------- */ + if ( ndim_in == 1 && ndim_out == 1 ) { + +/* Loop through the required range of input x coordinates, assigning + the coordinate values to the PointSet created above. Also store a + pixel offset into the input array. */ + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_in[ 0 ][ point ] = (double) ix; + offset[ point++ ] = ix - lbnd_in[ 0 ]; + } + +/* Handle the 2-dimensional case optimally. */ +/* ---------------------------------------- */ + } else if ( ndim_in == 2 && ndim_out == 2 ) { + +/* Loop through the required range of input y coordinates, + calculating an interim pixel offset into the input array. */ + off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) + - lbnd_in[ 0 ]; + for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { + off1 += stride[ 1 ]; + +/* Loop through the required range of input x coordinates, assigning + the coordinate values to the PointSet created above. Also store a + final pixel offset into the input array. */ + off2 = off1 + lbnd[ 0 ]; + for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { + ptr_in[ 0 ][ point ] = (double) ix; + ptr_in[ 1 ][ point ] = (double) iy; + offset[ point++ ] = off2++; + } + } + +/* Handle other numbers of dimensions. */ +/* ----------------------------------- */ + } else { + +/* Allocate workspace. */ + dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Initialise an array of pixel indices for the input grid which + refer to the first pixel to be rebinned. Also calculate the offset + of this pixel within the input array. */ + off = 0; + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + dim[ coord_in ] = lbnd[ coord_in ]; + off += stride[ coord_in ] * + ( dim[ coord_in ] - lbnd_in[ coord_in ] ); + } + +/* Loop to generate the coordinates of each input pixel. */ + for ( done = 0; !done; point++ ) { + +/* Copy each pixel's coordinates into the PointSet created above. */ + for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { + ptr_in[ coord_in ][ point ] = + (double) dim[ coord_in ]; + } + +/* Store the offset of the pixel in the input array. */ + offset[ point ] = off; + +/* Now update the array of pixel indices to refer to the next input + pixel. */ + coord_in = 0; + do { + +/* The least significant index which currently has less than its + maximum value is incremented by one. The offset into the input + array is updated accordingly. */ + if ( dim[ coord_in ] < ubnd[ coord_in ] ) { + dim[ coord_in ]++; + off += stride[ coord_in ]; + break; + +/* Any less significant indices which have reached their maximum value + are returned to their minimum value and the input pixel offset is + decremented appropriately. */ + } else { + dim[ coord_in ] = lbnd[ coord_in ]; + off -= stride[ coord_in ] * + ( ubnd[ coord_in ] - lbnd[ coord_in ] ); + +/* All the input pixels have been processed once the most significant + pixel index has been returned to its minimum value. */ + done = ( ++coord_in == ndim_in ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + dim = astFree( dim ); + } + +/* When all the input pixel coordinates have been generated, use the + Mapping's forward transformation to generate the output coordinates + from them. Obtain an array of pointers to the resulting coordinate + data. */ + pset_out = astTransform( this, pset_in, 1, NULL ); + ptr_out = astGetPoints( pset_out ); + } + +/* Annul the PointSet containing the input coordinates. */ + pset_in = astAnnul( pset_in ); + } + } + +/* Copy the output coordinates into the correct positions within the + supplied "out" array. */ +/* ================================================================= */ + if( astOK ) { + for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { + for ( point = 0; point < npoint; point++ ) { + out[ coord_out ][ offset[ point ] ] = ptr_out[ coord_out ][ point ]; + } + } + } + +/* Annul the PointSet used to hold output coordinates. */ + pset_out = astAnnul( pset_out ); + +/* Free the workspace. */ + offset = astFree( offset ); + stride = astFree( stride ); +} + +static void TranGridWithBlocking( AstMapping *this, const double *linear_fit, + int ndim_in, const int *lbnd_in, + const int *ubnd_in, const int *lbnd, + const int *ubnd, int ndim_out, + double *out[], int *status ){ +/* +* Name: +* TranGridWithBlocking + +* Purpose: +* Transforms positions in a section of a grid in a memory-efficient way. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void TranGridWithBlocking( AstMapping *this, const double *linear_fit, +* int ndim_in, const int *lbnd_in, +* const int *ubnd_in, const int *lbnd, +* const int *ubnd, int ndim_out, +* double *out[], int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function transforms positions within a specified section of a +* rectangular grid (with any number of dimensions) using the forward +* transformation of the supplied Mapping. +* +* This function is very similar to TranGridSection, except that in +* order to limit memory usage and to ensure locality of reference, +* it divides the input grid up into "blocks" which have a limited +* extent along each input dimension. Each block, which will not +* contain more than a pre-determined maximum number of pixels, is +* then passed to TranGridSection for transformation. + +* Parameters: +* this +* Pointer to a Mapping, whose forward transformation may be +* used to transform the coordinates of pixels in the input +* grid into associated positions in the output grid. +* +* The number of input coordintes for the Mapping (Nin +* attribute) should match the value of "ndim_in" (below), and +* the number of output coordinates (Nout attribute) should +* match the value of "ndim_out". +* linear_fit +* Pointer to an optional array of double which contains the +* coefficients of a linear fit which approximates the above +* Mapping's forward coordinate transformation. If this is +* supplied, it will be used in preference to the above Mapping +* when transforming coordinates. This may be used to enhance +* performance in cases where evaluation of the Mapping's +* forward transformation is expensive. If no linear fit is +* available, a NULL pointer should be supplied. +* +* The way in which the fit coefficients are stored in this +* array and the number of array elements are as defined by the +* astLinearApprox function. +* ndim_in +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the first +* pixel in the input grid along each dimension. +* ubnd_in +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the centre of the last +* pixel in the input grid along each dimension. +* +* Note that "lbnd_in" and "ubnd_in" together define the shape +* and size of the whole input grid, its extent along a +* particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + +* 1). They also define the input grid's coordinate system, with +* each pixel being of unit extent along each dimension with +* integral coordinate values at its centre. +* lbnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the first pixel in the +* section of the input data grid which is to be transformed. +* ubnd +* Pointer to an array of integers, with "ndim_in" elements. +* This should give the coordinates of the last pixel in the +* section of the input data grid which is to be transformed. +* +* Note that "lbnd" and "ubnd" define the shape and position of the +* section of the input grid which is to be transformed. +* ndim_out +* The number of dimensions in the output grid. This should be +* at least one. +* out +* Pointer to an array with "ndim_out" elements. Element [i] of +* this array is a pointer to an array in which to store the +* transformed values for output axis "i". The points are ordered +* such that the first axis of the input grid changes most rapidly. +* For example, if the input grid is 2-dimensional and extends from +* (2,-1) to (3,1), the output points will be stored in the order +* (2,-1), (3, -1), (2,0), (3,0), (2,1), (3,1). +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Constants: */ + const int mxpix = 2 * 1024; /* Maximum number of pixels in a block (this + relatively small number seems to give best + performance) */ + +/* Local Variables: */ + int *dim_block; /* Pointer to array of block dimensions */ + int *lbnd_block; /* Pointer to block lower bound array */ + int *ubnd_block; /* Pointer to block upper bound array */ + int dim; /* Dimension size */ + int done; /* All blocks rebinned? */ + int hilim; /* Upper limit on maximum block dimension */ + int idim; /* Loop counter for dimensions */ + int lolim; /* Lower limit on maximum block dimension */ + int mxdim_block; /* Maximum block dimension */ + int npix; /* Number of pixels in block */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Allocate workspace. */ + lbnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); + ubnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); + dim_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); + if ( astOK ) { + +/* Find the optimum block size. */ +/* ---------------------------- */ +/* We first need to find the maximum extent which a block of input + pixels may have in each dimension. We determine this by taking the + input grid extent in each dimension and then limiting the maximum + dimension size until the resulting number of pixels is sufficiently + small. This approach allows the block shape to approximate (or + match) the input grid shape when appropriate. */ + +/* First loop to calculate the total number of input pixels and the + maximum input dimension size. */ + npix = 1; + mxdim_block = 0; + for ( idim = 0; idim < ndim_in; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + npix *= dim; + if ( mxdim_block < dim ) mxdim_block = dim; + } + +/* If the number of input pixels is too large for a single block, we + perform iterations to determine the optimum upper limit on a + block's dimension size. Initialise the limits on this result. */ + if ( npix > mxpix ) { + lolim = 1; + hilim = mxdim_block; + +/* Loop to perform a binary chop, searching for the best result until + the lower and upper limits on the result converge to adjacent + values. */ + while ( ( hilim - lolim ) > 1 ) { + +/* Form a new estimate from the mid-point of the previous limits. */ + mxdim_block = ( hilim + lolim ) / 2; + +/* See how many pixels a block contains if its maximum dimension is + limited to this new value. */ + for ( npix = 1, idim = 0; idim < ndim_in; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + npix *= ( dim < mxdim_block ) ? dim : mxdim_block; + } + +/* Update the appropriate limit, according to whether the number of + pixels is too large or too small. */ + *( ( npix <= mxpix ) ? &lolim : &hilim ) = mxdim_block; + } + +/* When iterations have converged, obtain the maximum limit on the + dimension size of a block which results in no more than the maximum + allowed number of pixels per block. However, ensure that all block + dimensions are at least 2. */ + mxdim_block = lolim; + } + if ( mxdim_block < 2 ) mxdim_block = 2; + +/* Calculate the block dimensions by applying this limit to the output + grid dimensions. */ + for ( idim = 0; idim < ndim_in; idim++ ) { + dim = ubnd[ idim ] - lbnd[ idim ] + 1; + dim_block[ idim ] = ( dim < mxdim_block ) ? dim : mxdim_block; + +/* Also initialise the lower and upper bounds of the first block of + output grid pixels to be rebinned, ensuring that this does not + extend outside the grid itself. */ + lbnd_block[ idim ] = lbnd[ idim ]; + ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + } + +/* Transform each block of input grid positions. */ +/* --------------------------------------------- */ +/* Loop to generate the extent of each block of input grid positions and to + transform them. */ + done = 0; + while ( !done && astOK ) { + +/* Rebin the current block, accumulating the sum of bad pixels produced. */ + TranGridSection( this, linear_fit, ndim_in, lbnd_in, ubnd_in, + lbnd_block, ubnd_block, ndim_out, out, status ); + +/* Update the block extent to identify the next block of input pixels. */ + idim = 0; + do { + +/* We find the least significant dimension where the upper bound of + the block has not yet reached the upper bound of the region of the + input grid which we are rebinning. The block's position is then + incremented by one block extent along this dimension, checking that + the resulting extent does not go outside the region being rebinned. */ + if ( ubnd_block[ idim ] < ubnd[ idim ] ) { + lbnd_block[ idim ] = MinI( lbnd_block[ idim ] + + dim_block[ idim ], ubnd[ idim ], status ); + ubnd_block[ idim ] = MinI( lbnd_block[ idim ] + + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + break; + +/* If any less significant dimensions are found where the upper bound + of the block has reached its maximum value, we reset the block to + its lowest position. */ + } else { + lbnd_block[ idim ] = lbnd[ idim ]; + ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, + ubnd[ idim ], status ); + +/* All the blocks have been processed once the position along the most + significant dimension has been reset. */ + done = ( ++idim == ndim_in ); + } + } while ( !done ); + } + } + +/* Free the workspace. */ + lbnd_block = astFree( lbnd_block ); + ubnd_block = astFree( ubnd_block ); + dim_block = astFree( dim_block ); +} + +static void TranN( AstMapping *this, int npoint, + int ncoord_in, int indim, const double *in, + int forward, + int ncoord_out, int outdim, double *out, int *status ) { +/* +*++ +* Name: +c astTranN +f AST_TRANN + +* Purpose: +* Transform N-dimensional coordinates. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astTranN( AstMapping *this, int npoint, +c int ncoord_in, int indim, const double *in, +c int forward, +c int ncoord_out, int outdim, double *out ) +f CALL AST_TRANN( THIS, NPOINT, +f NCOORD_IN, INDIM, IN, +f FORWARD, NCOORD_OUT, OUTDIM, OUT, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function applies a Mapping to transform the coordinates of +f This routine applies a Mapping to transform the coordinates of +* a set of points in an arbitrary number of dimensions. It is the +* appropriate routine to use if the coordinates are not purely 1- +* or 2-dimensional and are stored in a single array (which they +* need not fill completely). +c +c If the coordinates are not stored in a single array, then the +c astTranP function might be more suitable. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping to be applied. +c npoint +f NPOINT = INTEGER (Given) +* The number of points to be transformed. +c ncoord_in +f NCOORD_IN = INTEGER (Given) +* The number of coordinates being supplied for each input point +* (i.e. the number of dimensions of the space in which the +* input points reside). +c indim +f INDIM = INTEGER (Given) +c The number of elements along the second dimension of the "in" +f The number of elements along the first dimension of the IN +* array (which contains the input coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +c given should not be less than "npoint". +f given should not be less than NPOINT. +c in +f IN( INDIM, NCOORD_IN ) = DOUBLE PRECISION (Given) +c The address of the first element in a 2-dimensional array of +c shape "[ncoord_in][indim]", +c containing the coordinates of the input (untransformed) +c points. These should be stored such that the value of +c coordinate number "coord" for input point number "point" is +c found in element "in[coord][point]". +f An array containing the coordinates of the input +f (untransformed) points. These should be stored such that the +f value of coordinate number COORD for input point number POINT +f is found in element IN(POINT,COORD). +c forward +f FORWARD = LOGICAL (Given) +c A non-zero value indicates that the Mapping's forward +c coordinate transformation is to be applied, while a zero +c value indicates that the inverse transformation should be +c used. +f A .TRUE. value indicates that the Mapping's forward +f coordinate transformation is to be applied, while a .FALSE. +f value indicates that the inverse transformation should be +f used. +c ncoord_out +f NCOORD_OUT = INTEGER (Given) +* The number of coordinates being generated by the Mapping for +* each output point (i.e. the number of dimensions of the +* space in which the output points reside). This need not be +c the same as "ncoord_in". +f the same as NCOORD_IN. +c outdim +f OUTDIM = INTEGER (Given) +c The number of elements along the second dimension of the "out" +f The number of elements along the first dimension of the OUT +* array (which will contain the output coordinates). This value +* is required so that the coordinate values can be correctly +* located if they will not entirely fill this array. The value +c given should not be less than "npoint". +f given should not be less than NPOINT. +c out +f OUT( OUTDIM, NCOORD_OUT ) = DOUBLE PRECISION (Returned) +c The address of the first element in a 2-dimensional array of +c shape "[ncoord_out][outdim]", into +c which the coordinates of the output (transformed) points will +c be written. These will be stored such that the value of +c coordinate number "coord" for output point number "point" +c will be found in element "out[coord][point]". +f An array into which the coordinates of the output +f (transformed) points will be written. These will be stored +f such that the value of coordinate number COORD for output +f point number POINT will be found in element OUT(POINT,COORD). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - If the forward coordinate transformation is being applied, the +c Mapping supplied must have the value of "ncoord_in" for its Nin +c attribute and the value of "ncoord_out" for its Nout attribute. If +c the inverse transformation is being applied, these values should +c be reversed. +f - If the forward coordinate transformation is being applied, the +f Mapping supplied must have the value of NCOORD_IN for its Nin +f attribute and the value of NCOORD_OUT for its Nout attribute. If +f the inverse transformation is being applied, these values should +f be reversed. +*-- +*/ + +/* Local Variables: */ + AstPointSet *in_points; /* Pointer to input PointSet */ + AstPointSet *out_points; /* Pointer to output PointSet */ + const double **in_ptr; /* Pointer to array of input data pointers */ + double **out_ptr; /* Pointer to array of output data pointers */ + int coord; /* Loop counter for coordinates */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the mapping and numbers of points/coordinates. */ + ValidateMapping( this, forward, npoint, ncoord_in, ncoord_out, "astTranN", status ); + +/* Also validate the input array dimension argument. */ + if ( astOK && ( indim < npoint ) ) { + astError( AST__DIMIN, "astTranN(%s): The input array dimension value " + "(%d) is invalid.", status, astGetClass( this ), indim ); + astError( AST__DIMIN, "This should not be less than the number of " + "points being transformed (%d).", status, npoint ); + } + +/* Similarly, validate the output array dimension argument. */ + if ( astOK && ( outdim < npoint ) ) { + astError( AST__DIMIN, "astTranN(%s): The output array dimension value " + "(%d) is invalid.", status, astGetClass( this ), outdim ); + astError( AST__DIMIN, "This should not be less than the number of " + "points being transformed (%d).", status, npoint ); + } + +/* Allocate memory to hold the arrays of input and output data + pointers. */ + if ( astOK ) { + in_ptr = (const double **) astMalloc( sizeof( const double * ) * + (size_t) ncoord_in ); + out_ptr = astMalloc( sizeof( double * ) * (size_t) ncoord_out ); + + +#ifdef DEBUG + { int i, ns; + ns = ncoord_out*outdim; + for( i = 0; i < ns; i++ ) out[ i ] = 0.0; + } +#endif + + +/* Initialise the input data pointers to locate the coordinate data in + the "in" array. */ + if ( astOK ) { + for ( coord = 0; coord < ncoord_in; coord++ ) { + in_ptr[ coord ] = in + coord * indim; + } + +/* Similarly initialise the output data pointers to point into the + "out" array. */ + for ( coord = 0; coord < ncoord_out; coord++ ) { + out_ptr[ coord ] = out + coord * outdim; + } + +/* Create PointSets to describe the input and output points. */ + in_points = astPointSet( npoint, ncoord_in, "", status ); + out_points = astPointSet( npoint, ncoord_out, "", status ); + +/* Associate the data pointers with the PointSets (note we must + explicitly remove the "const" qualifier from the input data here, + although they will not be modified). */ + astSetPoints( in_points, (double **) in_ptr ); + astSetPoints( out_points, out_ptr ); + +/* Apply the required transformation to the coordinates. */ + (void) astTransform( this, in_points, forward, out_points ); + +/* If the Mapping's Report attribute is set, report the effect the + Mapping has had on the coordinates. */ + if ( astGetReport( this ) ) astReportPoints( this, forward, + in_points, out_points ); + +/* Delete the two PointSets. */ + in_points = astDelete( in_points ); + out_points = astDelete( out_points ); + } + +/* Free the memory used for the data pointers. */ + in_ptr = (const double **) astFree( (void *) in_ptr ); + out_ptr = astFree( out_ptr ); + } +} + +static void TranP( AstMapping *this, int npoint, + int ncoord_in, const double *ptr_in[], + int forward, int ncoord_out, double *ptr_out[], int *status ) { +/* +c++ +* Name: +* astTranP + +* Purpose: +* Transform N-dimensional coordinates held in separate arrays. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "mapping.h" +* void astTranP( AstMapping *this, int npoint, +* int ncoord_in, const double *ptr_in[], +* int forward, int ncoord_out, double *ptr_out[] ) + +* Class Membership: +* Mapping method. + +* Description: +* This function applies a Mapping to transform the coordinates of +* a set of points in an arbitrary number of dimensions. It is the +* appropriate routine to use if the coordinates are not purely 1- +* or 2-dimensional and are stored in separate arrays, since each +* coordinate array is located by supplying a separate pointer to +* it. +* +* If the coordinates are stored in a single (2-dimensional) array, +* then the astTranN function might be more suitable. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* npoint +* The number of points to be transformed. +* ncoord_in +* The number of coordinates being supplied for each input point +* (i.e. the number of dimensions of the space in which the +* input points reside). +* ptr_in +* An array of pointers to double, with "ncoord_in" +* elements. Element "ptr_in[coord]" should point at the first +* element of an array of double (with "npoint" elements) which +* contain the values of coordinate number "coord" for each +* input (untransformed) point. The value of coordinate number +* "coord" for input point number "point" is therefore given by +* "ptr_in[coord][point]" (assuming both indices are +* zero-based). +* forward +* A non-zero value indicates that the Mapping's forward +* coordinate transformation is to be applied, while a zero +* value indicates that the inverse transformation should be +* used. +* ncoord_out +* The number of coordinates being generated by the Mapping for +* each output point (i.e. the number of dimensions of the space +* in which the output points reside). This need not be the same +* as "ncoord_in". +* ptr_out +* An array of pointers to double, with "ncoord_out" +* elements. Element "ptr_out[coord]" should point at the first +* element of an array of double (with "npoint" elements) into +* which the values of coordinate number "coord" for each output +* (transformed) point will be written. The value of coordinate +* number "coord" for output point number "point" will therefore +* be found in "ptr_out[coord][point]". + +* Notes: +* - If the forward coordinate transformation is being applied, the +* Mapping supplied must have the value of "ncoord_in" for its Nin +* attribute and the value of "ncoord_out" for its Nout +* attribute. If the inverse transformation is being applied, these +* values should be reversed. +* - This routine is not available in the Fortran 77 interface to +* the AST library. +c-- +*/ + +/* Local Variables: */ + AstPointSet *in_points; /* Pointer to input PointSet */ + AstPointSet *out_points; /* Pointer to output PointSet */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the Mapping and number of points/coordinates. */ + ValidateMapping( this, forward, npoint, ncoord_in, ncoord_out, "astTranP", status ); + +/* Create PointSets to describe the input and output points. */ + if ( astOK ) { + in_points = astPointSet( npoint, ncoord_in, "", status ); + out_points = astPointSet( npoint, ncoord_out, "", status ); + +/* Associate the data pointers with the PointSets (note we must + explicitly remove the "const" qualifier from the input data here, + although they will not be modified). */ + astSetPoints( in_points, (double **) ptr_in ); + astSetPoints( out_points, ptr_out ); + +/* Apply the required transformation to the coordinates. */ + (void) astTransform( this, in_points, forward, out_points ); + +/* If the Mapping's Report attribute is set, report the effect the + Mapping has had on the coordinates. */ + if ( astGetReport( this ) ) astReportPoints( this, forward, + in_points, out_points ); + +/* Delete the two PointSets. */ + in_points = astDelete( in_points ); + out_points = astDelete( out_points ); + } +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +*+ +* Name: +* astTransform + +* Purpose: +* Transform a set of points. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "mapping.h" +* AstPointSet *astTransform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out ) + +* Class Membership: +* Mapping method. + +* Description: +* This function takes a Mapping and a set of points encapsulated +* in a PointSet, and applies either the forward or inverse +* coordinate transformation (if defined by the Mapping) to the +* points. + +* Parameters: +* this +* Pointer to the Mapping. The nature of the coordinate +* transformation will depend on the class of Mapping +* supplied. Note that there is no constructor for the Mapping +* class itself, so this object should be from a derived class. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate +* transformation should be applied, while a zero value requests +* the inverse transformation. +* out +* Pointer to a PointSet which will hold the transformed +* (output) coordinate values. A NULL value may also be given, +* in which case a new PointSet will be created by this +* function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - An error will result if the Mapping supplied does not define +* the requested coordinate transformation (either forward or +* inverse). +* - The number of coordinate values per point in the input +* PointSet must match the number of input coordinates for the +* Mapping being applied (or number of output coordinates if the +* inverse transformation is requested). +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and coordinate values per point to +* accommodate the result (e.g. the number of Mapping output +* coordinates, or number of input coordinates if the inverse +* transformation is requested). Any excess space will be ignored. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + int def; /* Coordinate transformation defined? */ + int ncoord_in; /* Number of input PointSet coordinates */ + int ncoord_out; /* Number of coordinates in output PointSet */ + int nin; /* Number of input Mapping coordinates */ + int nout; /* Number of output Mapping coordinates */ + int npoint; /* Number of points to transform */ + int npoint_out; /* Number of points in output PointSet */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise. */ + result = NULL; + +/* Determine if a coordinate transformation is defined for the requested + direction. */ + def = forward ? astGetTranForward( this ) : astGetTranInverse( this ); + +/* Report an error if the transformation is not defined. */ + if ( astOK && !def ) { + astError( AST__TRNND, "astTransform(%s): %s coordinate transformation " + "is not defined by the %s supplied.", status, astGetClass( this ), + forward ? "A forward" : "An inverse", astGetClass( this ) ); + } + +/* Obtain the effective number of input and output coordinate values for the + transformation to be performed, taking account of the transformation + direction required. Note we use Mapping methods to obtain these values, as + this will take account of whether the Mapping has been inverted. */ + nin = forward ? astGetNin( this ) : astGetNout( this ); + nout = forward ? astGetNout( this ) : astGetNin( this ); + +/* Obtain the number of input points to transform and the number of coordinate + values per input point. */ + npoint = astGetNpoint( in ); + ncoord_in = astGetNcoord( in ); + +/* If OK, check that the number of input coordinates matches the number + required by the mapping. Report an error if these numbers do not match. */ + if ( astOK && ( ncoord_in != nin ) ) { + astError( AST__NCPIN, "astTransform(%s): Bad number of coordinate " + "values (%d) in input %s.", status, astGetClass( this ), ncoord_in, + astGetClass( in ) ); + astError( AST__NCPIN, "The %s given requires %d coordinate value(s) for " + "each input point.", status, astGetClass( this ), nin ); + } + +/* If still OK, and a non-NULL pointer has been given for the output PointSet, + then obtain the number of points and number of coordinates per point for + this PointSet. */ + if ( astOK && out ) { + npoint_out = astGetNpoint( out ); + ncoord_out = astGetNcoord( out ); + +/* Check that the dimensions of this PointSet are adequate to accommodate the + output coordinate values and report an error if they are not. */ + if ( astOK ) { + if ( npoint_out < npoint ) { + astError( AST__NOPTS, "astTransform(%s): Too few points (%d) in " + "output %s.", status, astGetClass( this ), npoint_out, + astGetClass( out ) ); + astError( AST__NOPTS, "The %s needs space to hold %d transformed " + "point(s).", status, astGetClass( this ), npoint ); + } else if ( ncoord_out < nout ) { + astError( AST__NOCTS, "astTransform(%s): Too few coordinate " + "values per point (%d) in output %s.", status, + astGetClass( this ), ncoord_out, astGetClass( out ) ); + astError( AST__NOCTS, "The %s supplied needs space to store %d " + "coordinate value(s) per transformed point.", status, + astGetClass( this ), nout ); + } + } + } + +/* If all the validation stages are passed successfully, and a NULL output + pointer was given, then create a new PointSet to encapsulate the output + coordinate data. */ + if ( astOK ) { + if ( !out ) { + result = astPointSet( npoint, nout, "", status ); + +/* Otherwise, use the PointSet supplied. */ + } else { + result = out; + } + } + +/* Return a pointer to the output PointSet. Note that we do not actually + transform (or even copy) the coordinates. This is left for derived classes + to implement. */ + return result; +} + +/* +*++ +* Name: +c astUinterp +f AST_UINTERP + +* Purpose: +* Perform sub-pixel interpolation on a grid of data. + +* Type: +* Fictitious function. + +* Synopsis: +c #include "mapping.h" +c void astUinterp( int ndim_in, const int lbnd_in[], const int ubnd_in[], +c const in[], const in_var[], +c int npoint, const int offset[], +c const double *const coords[], const double params[], +c int flags, badval, +c out[], out_var[], int *nbad ) +f CALL AST_UINTERP( NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, +f NPOINT, OFFSET, COORDS, PARAMS, FLAGS, BADVAL, +f OUT, OUT_VAR, NBAD, STATUS ) + +* Class Membership: +* Mapping member function. + +* Description: +c This is a fictitious function which does not actually +c exist. Instead, this description constitutes a template so that +c you may implement a function with this interface for yourself +c (and give it any name you wish). A pointer to such a function +c may be passed via the "finterp" parameter of the astResample +c functions (q.v.) in order to perform sub-pixel interpolation +c during resampling of gridded data (you must also set the +c "interp" parameter of astResample to the value +c AST__UINTERP). This allows you to use your own interpolation +c algorithm in addition to those which are pre-defined. +f This is a fictitious routine which does not actually +f exist. Instead, this description constitutes a template so that +f you may implement a routine with this interface for yourself +f (and give it any name you wish). Such a routine +f may be passed via the FINTERP argument of the AST_RESAMPLE +f functions (q.v.) in order to perform sub-pixel interpolation +f during resampling of gridded data (you must also set the +f INTERP argument of AST_RESAMPLE to the value +f AST__UINTERP). This allows you to use your own interpolation +f algorithm in addition to those which are pre-defined. +* +c The function interpolates an input grid of data (and, +f The routine interpolates an input grid of data (and, +* optionally, processes associated statistical variance estimates) +* at a specified set of points. + +* Parameters: +c ndim_in +f NDIM_IN = INTEGER (Given) +* The number of dimensions in the input grid. This will be at +* least one. +c lbnd_in +f LBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +c ubnd_in +f UBND_IN( NDIM_IN ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim_in" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +c Note that "lbnd_in" and "ubnd_in" together define the shape, +f Note that LBND_IN and UBND_IN together define the shape, +* size and coordinate system of the input grid in the same +c way as they do in astResample. +f way as they do in AST_RESAMPLE. +c in +f IN( * ) = (Given) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* input grid, containing the input data. This will be the same +c array as was passed to astResample via the "in" parameter. +f array as was passed to AST_RESAMPLE via the IN argument. +* The numerical type of this array should match that of the +* data being processed. +c in_var +f IN_VAR( * ) = (Given) +c Pointer to an optional second array with the same size and +c type as the "in" array. If given, this will contain the set +c of variance values associated with the input data and will be +c the same array as was passed to astResample via the +c "in_var" parameter. +f An optional second array with the same size and type as the +f IN array. This will only be given if the AST__USEVAR flag is +f set via the FLAGS argument (below). If given, it will contain +f the set of variance values associated with the input data and +f will be the same array as was passed to AST_RESAMPLE via +f the IN_VAR argument. +* +c If no variance values are being processed, this will be a +c NULL pointer. +f If the AST__USEVAR flag is not set, then no variance values +f are being processed. In this case, this array of variance +f values may be a dummy (e.g. one-element) array and should not +f be used. +c npoint +f NPOINT = INTEGER (Given) +* The number of points at which the input grid is to be +* interpolated. This will be at least one. +c offset +f OFFSET( NPOINT ) = INTEGER (Given) +c Pointer to an array of integers with "npoint" elements. For +c each interpolation point, this will contain the zero-based +c index in the "out" (and "out_var") array(s) at which the +c interpolated value (and its variance, if required) should be +c stored. For example, the interpolated value for point number +c "point" should be stored in "out[offset[point]]" (assuming +c the index "point" is zero-based). +f For each interpolation point, this array will contain the +f offset from the start of the OUT (and OUT_VAR) array(s) at +f which the interpolated value (and its variance, if required) +f should be stored. For example, the interpolated value for +f point number POINT should be stored in OUT(1+OFFSET(POINT)). +c coords +f COORDS( NPOINT, NDIM_IN ) = DOUBLE PRECISION (Given) +c An array of pointers to double, with "ndim_in" +c elements. Element "coords[coord]" will point at the first +c element of an array of double (with "npoint" elements) which +c contains the values of coordinate number "coord" for each +c interpolation point. The value of coordinate number "coord" +c for interpolation point number "point" is therefore given by +c "coords[coord][point]" (assuming both indices are +c zero-based). +f A 2-dimensional array containing the coordinates of the +f points at which interpolation should be performed. These will +f be stored so that coordinate number COORD for interpolation +f point number POINT is found in element COORDS(POINT,COORD). +* +* If any interpolation point has any of its coordinates equal +c to the value AST__BAD (as defined in the "ast.h" header +f to the value AST__BAD (as defined in the AST_PAR include +* file), then the corresponding output data (and variance) +c should either be set to the value given by "badval", +f should either be set to the value given by BADVAL, +* or left unchanged, depending on whether the AST__NOBAD flag is +c specified by "flags". +f specified by FLAGS. +c params +f PARAMS( * ) = DOUBLE PRECISION (Given) +c This will be a pointer to the same array as was given via the +c "params" parameter of astResample. You may use this to +f This will be the same array as was given via the +f PARAMS argument of AST_RESAMPLE. You may use this to +* pass any additional parameter values required by your +* interpolation algorithm. +c flags +f FLAGS = INTEGER (Given) +c This will be the same value as was given via the "flags" +c parameter of astResample. You may test this value to +f This will be the same value as was given via the FLAGS +f argument of AST_RESAMPLE. You may test this value to +* provide additional control over the operation of your +* resampling algorithm. Note that the special flag values +* AST__URESAMP1, 2, 3 & 4 are reserved for you to use for your +* own purposes and will not clash with other pre-defined flag +c values (see astResample). +f values (see AST_RESAMPLE). +c badval +f BADVAL = (Given) +c This will be the same value as was given via the "badval" +c parameter of astResample, and will have the same numerical +c type as the data being processed (i.e. as elements of the +c "in" array). It should be used to test for bad pixels in the +c input grid (but only if the AST__USEBAD flag is set via the +c "flags" parameter) and (unless the AST__NOBAD flag is set in +c "flags") for identifying bad output values in +c the "out" (and "out_var") array(s). +f This will be the same value as was given for the BADVAL +f argument of AST_RESAMPLE, and will have the same numerical +f type as the data being processed (i.e. as elements of the IN +f array). It should be used to test for bad pixels in the +f input grid (but only if the AST__USEBAD flag is set via the +f FLAGS argument) and (unless the AST__NOBAD flag is set in +f FLAGS) for identifying bad output values in the OUT (and +f OUT_VAR) array(s). +c out +f OUT( * ) = (Returned) +c Pointer to an array with the same numerical type as the "in" +f An array with the same numerical type as the IN +* array, into which the interpolated data values should be +* returned. Note that details of the storage order and number +* of dimensions of this array are not required, since the +c "offset" array contains all necessary information about where +f OFFSET array contains all necessary information about where +* each returned value should be stored. +* +c In general, not all elements of this array (or the "out_var" +f In general, not all elements of this array (or the OUT_VAR +* array below) may be used in any particular invocation of the +c function. Those which are not used should be returned +f routine. Those which are not used should be returned +* unchanged. +c out_var +f OUT_VAR( * ) = (Returned) +c Pointer to an optional array with the same type and size as +c the "out" array, into which variance estimates for the +c resampled values should be returned. This array will only be +c given if the "in_var" array has also been given. +f An optional array with the same type and size as the OUT +f array, into which variance estimates for the resampled values +f should be returned. This array will only be given if the +f AST__USEVAR flag is set via the FLAGS argument. +* +c If given, it is addressed in exactly the same way (via the +c "offset" array) as the "out" array. The values returned +c should be estimates of the statistical variance of the +c corresponding values in the "out" array, on the assumption +c that all errors in input data values are statistically +c independent and that their variance estimates may simply be +c summed (with appropriate weighting factors). +f If given, it is addressed in exactly the same way (via the +f OFFSET array) as the OUT array. The values returned should be +f estimates of the statistical variance of the corresponding +f values in the OUT array, on the assumption that all errors in +f input data values are statistically independent and that +f their variance estimates may simply be summed (with +f appropriate weighting factors). +* +c If no output variance estimates are required, a NULL pointer +c will be given. +f If the AST__USEVAR flag is not set, then variance values are +f not being processed. In this case, this array may be a dummy +f (e.g. one-element) array and should not be used. +c nbad +f NBAD = INTEGER (Returned) +c Pointer to an int in which to return the number of interpolation +c points at +f This should return the number of interpolation points at +* which no valid interpolated value could be obtained. The maximum +c value that should be returned is "npoint", and the minimum is +f value that should be returned is NPOINT, and the minimum is +* zero (indicating that all output values were successfully +* obtained). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The data type indicates the numerical type of the data +c being processed, as for astResample. +f being processed, as for AST_RESAMPLE. +c - This function will typically be invoked more than once for each +c invocation of astResample. +f - This routine will typically be invoked more than once for each +f invocation of AST_RESAMPLE. +c - If an error occurs within this function, it should use +c astSetStatus to set the AST error status to an error value. +c This will cause an immediate return from astResample. The error +c value AST__UINER is available for this purpose, but other values may +c also be used (e.g. if you wish to distinguish different types of +c error). +f - If an error occurs within this routine, it should set the +f STATUS argument to an error value before returning. This will +f cause an immediate return from AST_RESAMPLE. The error value +f AST__UINER is available for this purpose, but other values may also +f be used (e.g. if you wish to distinguish different types of error). +f The AST__UINER error value is defined in the AST_ERR include file. +*-- +*/ +/* Note the above is just a description to act as a template. The + function does not actually exist. */ + +/* +*++ +* Name: +c astUkern1 +f AST_UKERN1 + +* Purpose: +* 1-dimensional sub-pixel interpolation kernel. + +* Type: +* Fictitious function. + +* Synopsis: +c #include "mapping.h" +c void astUkern1( double offset, const double params[], int flags, +c double *value ) +f CALL AST_UKERN1( OFFSET, PARAMS, FLAGS, VALUE, STATUS ) + +* Class Membership: +* Mapping member function. + +* Description: +c This is a fictitious function which does not actually +c exist. Instead, this description constitutes a template so that +c you may implement a function with this interface for yourself +c (and give it any name you wish). A pointer to such a function +c may be passed via the "finterp" parameter of the astResample +c functions (q.v.) in order to supply a 1-dimensional +c interpolation kernel to the algorithm which performs sub-pixel +c interpolation during resampling of gridded data (you must also +c set the "interp" parameter of astResample to the value +c AST__UKERN1). This allows you to use your own interpolation +c kernel in addition to those which are pre-defined. +f This is a fictitious routine which does not actually +f exist. Instead, this description constitutes a template so that +f you may implement a routine with this interface for yourself +f (and give it any name you wish). Such a routine +f may be passed via the FINTERP argument of the AST_RESAMPLE +f functions (q.v.) in order to supply a 1-dimensional +f interpolation kernel to the algorithm which performs sub-pixel +f interpolation during resampling of gridded data (you must also +f set the INTERP argument of AST_RESAMPLE to the value +f AST__UKERN1). This allows you to use your own interpolation +f kernel in addition to those which are pre-defined. +* +c The function calculates the value of a 1-dimensional sub-pixel +f The routine calculates the value of a 1-dimensional sub-pixel +* interpolation kernel. This determines how the weight given to +* neighbouring pixels in calculating an interpolated value depends +* on the pixel's offset from the interpolation point. In more than +* one dimension, the weight assigned to a pixel is formed by +* evaluating this 1-dimensional kernel using the offset along each +* dimension in turn. The product of the returned values is then +* used as the pixel weight. + +* Parameters: +c offset +f OFFSET = DOUBLE PRECISION (Given) +* This will be the offset of the pixel from the interpolation +* point, measured in pixels. This value may be positive or +* negative, but for most practical interpolation schemes its +* sign should be ignored. +c params +f PARAMS( * ) = DOUBLE PRECISION (Given) +c This will be a pointer to the same array as was given via the +c "params" parameter of astResample. You may use this to +f This will be the same array as was given via the +f PARAMS argument of AST_RESAMPLE. You may use this to +* pass any additional parameter values required by your kernel, +c but note that "params[0]" will already have been used to specify +f but note that PARAMS(1) will already have been used to specify +* the number of neighbouring pixels which contribute to the +* interpolated value. +c flags +f FLAGS = INTEGER (Given) +c This will be the same value as was given via the "flags" +c parameter of astResample. You may test this value to +f This will be the same value as was given via the FLAGS +f argument of AST_RESAMPLE. You may test this value to +* provide additional control over the operation of your +c function. Note that the special flag values AST__URESAMP1, 2, +f routine. Note that the special flag values AST__URESAMP1, 2, +* 3 & 4 are reserved for you to use for your own purposes and +* will not clash with other pre-defined flag +c values (see astResample). +f values (see AST_RESAMPLE). +c value +f VALUE = DOUBLE PRECISION (Returned) +c Pointer to a double to receive the calculated kernel value, +f The calculated kernel value, +* which may be positive or negative. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Not all functions make good interpolation kernels. In general, +* acceptable kernels tend to be symmetrical about zero, to have a +* positive peak (usually unity) at zero, and to evaluate to zero +* whenever the pixel offset has any other integral value (this +* ensures that the interpolated values pass through the original +* data). An interpolation kernel may or may not have regions with +* negative values. You should consult a good book on image +* processing for more details. +c - If an error occurs within this function, it should use +c astSetStatus to set the AST error status to an error value. +c This will cause an immediate return from astResample. The error +c value AST__UK1ER is available for this purpose, but other values may +c also be used (e.g. if you wish to distinguish different types of +c error). +f - If an error occurs within this routine, it should set the +f STATUS argument to an error value before returning. This will +f cause an immediate return from AST_RESAMPLE. The error value +f AST__UK1ER is available for this purpose, but other values may also +f be used (e.g. if you wish to distinguish different types of error). +f The AST__UK1ER error value is defined in the AST_ERR include file. +*-- +*/ +/* Note the above is just a description to act as a template. The + function does not actually exist. */ + +static double UphillSimplex( const MapData *mapdata, double acc, int maxcall, + const double dx[], double xmax[], double *err, + int *ncall, int *status ) { +/* +* Name: +* UphillSimplex + +* Purpose: +* Find a function maximum using a modification of the simplex method. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* double UphillSimplex( const MapData *mapdata, double acc, int maxcall, +* const double dx[], double xmax[], double *err, +* int *ncall, int *status ); + +* Class Membership: +* Mapping member function. + +* Description: +* This function applies a modification of the simplex method to +* find a local maximum in the value returned by a Mapping +* function. The modification used allows the method to cope with +* coordinate constraints and (equivalently) regions where the +* function returns "bad" values. The method is robust and not +* susceptible to overflow, so is suitable for applying to Mapping +* functions of unknown form. + +* Parameters: +* mapdata +* Pointer to a MapData structure which describes the Mapping +* function, its coordinate constraints, etc. +* acc +* The accuracy required in the value of the maximum. +* maxcall +* The maximum number of Mapping function evaluations to use. +* dx +* Pointer to an array of double containing an offset along each +* input coordinate for the Mapping function supplied. These +* offsets will be used to construct the initial simplex +* (i.e. they are the initial "step lengths" for each +* coordinate) and may be positive or negative. +* xmax +* Pointer to an array of double which contains the coordinates +* of an initial estimate of the location of the maximum. On +* exit, this will be updated to contain the best estimate of +* the location of the maximum as generated by this function. +* err +* Pointer to a double in which to return an estimate of the +* error in the value of the maximum found. For normal +* convergence, this should be no larger than "acc". However, if +* the maximum number of Mapping function evaluations is +* reached, the returned value may be larger than this, although +* it should still be valid. In such cases, re-starting the +* algorithm at the new location returned in "xmax" may be +* advisable. +* ncall +* Pointer to an int in which the number of Mapping function +* evaluations will be returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* An estimate of the Mapping function value at the local maximum. + +* Notes: +* - The function may return before the requested accuracy has been +* met and before all Mapping function evaluations have been +* made. This signifies that an excessive number of function values +* have been needed outside the coordinate constraints. This is +* only likely if the function is unable to make progress near such +* a constraint, in which case the algorithm should probably be +* re-started. +* - A value of AST__BAD will be returned if no maximum could be +* found. This means that all the Mapping function evaluations +* performed returned a value of AST__BAD. +* - A value of AST__BAD will also be returned and no useful +* information about a solution will be produced if this routine is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Constants: */ + const double factor = 3.0; /* Simplex contraction/expansion factor */ + +/* Local Variables: */ + double *f; /* Pointer to array of function values */ + double *x; /* Pointer to array of vertex coordinates */ + double *xnew; /* Pointer to workspace array */ + double fnew; /* New function value */ + double fsave; /* Saved function value */ + double offset; /* Coordinate difference between vertices */ + double range; /* Range of simplex values */ + double result; /* Value to return */ + double tmp; /* Temporary store for coordinate */ + int coord; /* Loop counter for coordinates */ + int hi; /* Index of best vertex */ + int lo; /* Index of worst vertex */ + int ncalla; /* Number of function calls attempted */ + int ncoord; /* Number of function dimensions */ + int nextlo; /* Index of second worst vertex */ + int nvertex; /* Number of simplex vertices */ + int vertex; /* Loop counter for vertices */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + *err = DBL_MAX; + *ncall = 0; + +/* Obtain the number of input coordinates for the Mapping function and + calculate the number of simplex vertices. */ + ncoord = mapdata->nin; + nvertex = ncoord + 1; + +/* Allocate workspace. */ + f = astMalloc( sizeof( double ) * (size_t) nvertex ); + x = astMalloc( sizeof( double ) * (size_t) ( ncoord * nvertex ) ); + xnew = astMalloc( sizeof( double ) * (size_t) ncoord ); + if ( astOK ) { + +/* Loop to set up an initial simplex. */ + for ( vertex = 0; vertex < nvertex; vertex++ ) { + for ( coord = 0; coord < ncoord; coord++ ) { + tmp = xmax[ coord ]; + +/* Displace each point (except the first) the required amount along + one of the axes to generate the coordinates of the simplex + vertices. */ + if ( coord == ( vertex - 1 ) ) tmp += dx[ coord ]; + x[ vertex * ncoord + coord ] = tmp; + } + +/* Evaluate the Mapping function at each vertex. */ + f[ vertex ] = MapFunction( mapdata, &x[ vertex * ncoord ], ncall, status ); + if ( f[ vertex ] == AST__BAD ) f[ vertex ] = -DBL_MAX; + } + +/* Initialise the number of times we attempt to call the Mapping + function (not necessarily the same as the number of times it was + actually called, which is stored in *ncall). */ + ncalla = nvertex; + +/* Loop until convergence is reached or an error occurs. */ + while( astOK ) { + +/* Initialise the index of the lowest vertex of the simplex, the next + lowest vertex and the highest vertex. */ + lo = ( f[ 0 ] < f[ 1 ] ) ? 0 : 1; + nextlo = 1 - lo; + hi = 0; + +/* Loop to inspect each vertex and update these values. Ensure that in + the case of equal vertices, the first one is taken to be the + highest. This makes the maximisation stable (so that if no better + maximum can be found, the original position is returned rather than + a nearby position that yields the same function value). */ + for ( vertex = 0; vertex < nvertex; vertex++ ) { + if ( f[ vertex ] <= f[ lo ] ) { + nextlo = lo; + lo = vertex; + } else if ( ( f[ vertex ] <= f[ nextlo ] ) && ( vertex != lo ) ) { + nextlo = vertex; + } + if ( f[ vertex ] > f[ hi ] ) hi = vertex; + } + +/* Estimate the error on the result as the difference between the + highest and lowest simplex vertices. */ + if ( ( f[ hi ] == -DBL_MAX ) || ( f[ lo ] == -DBL_MAX ) ) { + range = DBL_MAX; + } else { + range = f[ hi ] - f[ lo ]; + } + +/* Test for convergence. Ideally, the accuracy criterion should have + been met. However, also quit if the maximum number of Mapping + function evaluations has been reached, or the number of points at + which function values have been requested reaches three times this + limit (this latter number will typically be larger because points + lying outside the coordinate constraints do not result in the + Mapping function being evaluated). */ + if ( range <= fabs( acc ) || + ( *ncall >= maxcall ) || ( ncalla >= ( 3 * maxcall ) ) ) { + +/* If quitting, return the coordinates and function value at the best + simplex vertex, and the error estimate. */ + for ( coord = 0; coord < ncoord; coord++ ) { + xmax[ coord ] = x[ hi * ncoord + coord ]; + } + result = ( f[ hi ] == -DBL_MAX ) ? AST__BAD : f[ hi ]; + *err = range; + break; + } + +/* If performing another iteration, first try reflecting the worst + vertex through the opposite face of the simplex. Check for + errors. */ + fnew = NewVertex( mapdata, lo, -1.0, x, f, ncall, xnew, status ); + ncalla++; + if ( astOK ) { + +/* If this results in a point lying in a forbiddden region (either + outside the coordinate constraints or where the Mapping function + yields bad coordinate values), then we must make a departure from + the standard simplex algorithm. This is because the inability to + make forward progress in this case can cause the simplex to + repeatedly contract about each face (except one) in turn. This + mechanism normally results in lateral contraction as the simplex + attempts to squeeze through a narrow gap which is impeding + progress. However, in this case there is no gap to get through, so + the lateral contraction can eventually make the simplex become + degenerate (due to rounding). This prevents it from expanding + laterally again and exploring the region adjacent to the constraint + boundary once it has become small enough. */ + if ( fnew == AST__BAD ) { + +/* To overcome this, we instead contract the worst simplex vertex + towards the best vertex (this has the cumulative effect of + contracting the simplex without changing its shape). First find the + offset in each coordinate between these two vertices. */ + for ( coord = 0; coord < ncoord; coord++ ) { + offset = x[ lo * ncoord + coord ] - x[ hi * ncoord + coord ]; + +/* Scale the offset to obtain the new coordinate. */ + x[ lo * ncoord + coord ] = x[ hi * ncoord + coord ] + + offset / factor; + +/* If the distance between the two vertices has not decreased, we are + in a region where rounding errors prevent them approaching each + other any more closely, so simply set them equal. */ + if ( fabs( x[ lo * ncoord + coord ] - + x[ hi * ncoord + coord ] ) >= fabs( offset ) ) { + x[ lo * ncoord + coord ] = x[ hi * ncoord + coord ]; + } + } + +/* Evaluate the Mapping function at the new vertex. */ + f[ lo ] = MapFunction( mapdata, &x[ lo * ncoord ], ncall, status ); + if ( f[ lo ] == AST__BAD ) f[ lo ] = -DBL_MAX; + ncalla++; + +/* We now return to the standard simplex algorithm. If the new vertex + is a new maximum, then see if more of the same is even better by + trying to expand the best vertex away from the opposite face. */ + } else if ( fnew >= f[ hi ] ) { + fnew = NewVertex( mapdata, lo, factor, x, f, ncall, xnew, status ); + ncalla++; + +/* Otherwise, if the new vertex was no improvement on the second + worst, then try contracting the worst vertex towards the opposite + face. */ + } else if ( fnew <= f[ nextlo ] ) { + fsave = f[ lo ]; + fnew = NewVertex( mapdata, lo, 1.0 / factor, x, f, ncall, xnew, status ); + ncalla++; + +/* If this didn't result in any improvement, then contract the entire + simplex towards the best vertex. Use the same approach as earlier + to protect against rounding so that all the simplex vertices will + eventually coalesce if this process is repeated enough times. */ + if ( astOK && ( fnew <= fsave ) ) { + for ( vertex = 0; vertex < nvertex; vertex++ ) { + if ( vertex != hi ) { + for ( coord = 0; coord < ncoord; coord++ ) { + offset = x[ vertex * ncoord + coord ] - + x[ hi * ncoord + coord ]; + x[ vertex * ncoord + coord ] = + x[ hi * ncoord + coord ] + offset / factor; + if ( fabs( x[ vertex * ncoord + coord ] - + x[ hi * ncoord + coord ] ) >= + fabs( offset ) ) { + x[ vertex * ncoord + coord ] = + x[ hi * ncoord + coord ]; + } + } + +/* Evaluate the Mapping function at each new vertex. */ + f[ vertex ] = MapFunction( mapdata, + &x[ vertex * ncoord ], + ncall, status ); + if ( f[ vertex ] == AST__BAD ) f[ vertex ] = -DBL_MAX; + ncalla++; + } + } + } + } + } + } + } + +/* Free workspace. */ + f = astFree( f ); + x = astFree( x ); + xnew = astFree( xnew ); + +/* If an error occurred, clear the returned result. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static void ValidateMapping( AstMapping *this, int forward, + int npoint, int ncoord_in, int ncoord_out, + const char *method, int *status ) { +/* +* Name: +* ValidateMapping + +* Purpose: +* Validate a Mapping for use to transform coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void ValidateMapping( AstMapping *this, int forward, +* int npoint, int ncoord_in, int ncoord_out, +* const char *method, int *status ) + +* Class Membership: +* Mapping member function. + +* Description: +* This function checks that a Mapping is suitable for transforming +* a set of points. It also checks that the number of points and +* the number of coordinate values per point is valid. If an error +* is detected, the global error status is set and an error report +* made. Otherwise, the function returns without further action. + +* Parameters: +* this +* Pointer to the Mapping. +* forward +* A non-zero value indicates that the forward coordinate +* transformation is to be checked, while a zero value requests +* the inverse transformation. +* npoint +* The number of points being transformed. +* ncoord_in +* The number of coordinates associated with each input point. +* ncoord_out +* The number of coordinates associated with each output point. +* method +* Pointer to a null terminated character string containing the +* name of the method which invoked this function to validate a +* Mapping. This is used solely for constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int nin; /* Mapping Nin attribute value */ + int nout; /* Mapping Nout attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Report an error if the requested transformation is not defined. */ + if ( !( forward ? astGetTranForward( this ) : astGetTranInverse( this ) ) + && astOK ) { + astError( AST__TRNND, "%s(%s): %s coordinate transformation " + "is not defined by the %s supplied.", status, method, + astGetClass( this ), + ( forward ? "A forward" : "An inverse" ), + astGetClass( this ) ); + } + +/* Obtain the effective values of the Nin and Nout attributes for the + Mapping. */ + nin = forward ? astGetNin( this ) : astGetNout( this ); + nout = forward ? astGetNout( this ) : astGetNin( this ); + +/* If OK, check that the number of input coordinates matches the + number required by the Mapping. Report an error if these numbers do + not match. */ + if ( astOK && ( ncoord_in != nin ) ) { + astError( AST__NCPIN, "%s(%s): Bad number of input coordinate values " + "(%d).", status, method, astGetClass( this ), ncoord_in ); + astError( AST__NCPIN, "The %s given requires %d coordinate value%s for " + "each input point.", status, astGetClass( this ), nin, + ( nin == 1 ) ? "" : "s" ); + } + +/* If OK, also check that the number of output coordinates matches the + number required by the Mapping. Report an error if these numbers do + not match. */ + if ( astOK && ( ncoord_out != nout ) ) { + astError( AST__NCPIN, "%s(%s): Bad number of output coordinate values " + "(%d).", status, method, astGetClass( this ), ncoord_out ); + astError( AST__NCPIN, "The %s given generates %s%d coordinate value%s " + "for each output point.", status, astGetClass( this ), + ( nout < ncoord_out ) ? "only " : "", nout, + ( nout == 1 ) ? "" : "s" ); + } + +/* Check that the number of points being transformed is not negative + and report an error if necessary. */ + if ( astOK && ( npoint < 0 ) ) { + astError( AST__NPTIN, "%s(%s): Number of points to be transformed (%d) " + "is invalid.", status, method, astGetClass( this ), npoint ); + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. */ +/* +*att++ +* Name: +* Invert + +* Purpose: +* Mapping inversion flag. + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls which one of a Mapping's two possible +* coordinate transformations is considered the "forward" +* transformation (the other being the "inverse" +* transformation). If the attribute value is zero (the default), +* the Mapping's behaviour will be the same as when it was first +* created. However, if it is non-zero, its two transformations +* will be inter-changed, so that the Mapping displays the inverse +* of its original behaviour. +* +* Inverting the boolean sense of the Invert attribute will cause +* the values of a Mapping's Nin and Nout attributes to be +* interchanged. The values of its TranForward and TranInverse +* attributes will also be interchanged. This operation may be +c performed with the astInvert function. +f performed with the AST_INVERT routine. + +* Applicability: +* Mapping +* All Mappings have this attribute. +* UnitMap +* The value of the Invert attribute has no effect on the +* behaviour of a UnitMap. +* FrameSet +* Inverting the boolean sense of the Invert attribute for a +* FrameSet will cause its base and current Frames (and its Base +* and Current attributes) to be interchanged. This, in turn, +* may affect other properties and attributes of the FrameSet +* (such as Nin, Nout, Naxes, TranForward, TranInverse, +* etc.). The Invert attribute of a FrameSet is not itself +* affected by selecting a new base or current Frame. +*att-- +*/ +/* This ia a boolean value (0 or 1) with a value of CHAR_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(Mapping,Invert,invert,CHAR_MAX) +astMAKE_GET(Mapping,Invert,int,0,( ( this->invert == CHAR_MAX ) ? + 0 : this->invert )) +astMAKE_SET(Mapping,Invert,int,invert,( (this->flags&=~AST__ISSIMPLE_FLAG),(value!=0) )) +astMAKE_TEST(Mapping,Invert,( this->invert != CHAR_MAX )) + +/* +*att++ +* Name: +* IsLinear + +* Purpose: +* Is the Mapping linear? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This attribute indicates whether a Mapping is an instance of a +* class that always represents a linear transformation. Note, some +* Mapping classes can represent linear or non-linear transformations +* (the MathMap class for instance). Such classes have a zero value for +* the IsLinear attribute. Specific instances of such classes can be +* tested for linearity using the +* astLinearApprox function. +* AST_LINEARAPPROX routine. + +* Applicability: +* Mapping +* All Mappings have this attribute. +* CmpMap +* The IsLinear value for a CmpMap is determined by the classes +* of the encapsulated Mappings. For instance, a CmpMap that combines +* a ZoomMap and a ShiftMap will have a non-zero value for its IsLinear +* attribute, but a CmpMap that contains a MathMap will have a +* value of zero for its IsLinear attribute. +* Frame +* The IsLinear value for a Frame is 1 (since a Frame is equivalent +* to a UnitMap). +* FrameSet +* The IsLinear value for a FrameSet is obtained from the Mapping +* from the base Frame to the current Frame. + +*att-- +*/ + +/* +*att++ +* Name: +* IsSimple + +* Purpose: +* Has the Mapping been simplified? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This attribute indicates whether a Mapping has been simplified +* by the +c astSimplify +f AST_SIMPLIFY +* method. If the IsSimple value is non-zero, then the Mapping has +* been simplified and so there is nothing to be gained by simplifying +* it again. Indeed, the +c astSimplify +f AST_SIMPLIFY +* method will immediately return the Mapping unchanged if the IsSimple +* attribute indicates that the Mapping has already been simplified. + +* Applicability: +* Mapping +* All Mappings have this attribute. +* Frame +* All classes of Frame return zero for the IsSimple attribute. +* This is because changes can be made to a Frame which affect the +* Mapping represented by the Frame, and so there can be no +* guarantee that the Mapping may not need re-simplifying. Most +* non-Frame Mappings, on the other hand, are immutable and so when +* they are simplified it is certain that they weill remain in a +* simple state. + +*att-- +*/ +astMAKE_GET(Mapping,IsSimple,int,0,((this->flags)&AST__ISSIMPLE_FLAG)) + +/* +*att++ +* Name: +* Nin + +* Purpose: +* Number of input coordinates for a Mapping. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the number of coordinate values required to +* specify an input point for a Mapping (i.e. the number of +* dimensions of the space in which the Mapping's input points +* reside). + +* Applicability: +* Mapping +* All Mappings have this attribute. +* CmpMap +* If a CmpMap's component Mappings are joined in series, then +* its Nin attribute is equal to the Nin attribute of the first +* component (or to the Nout attribute of the second component +* if the the CmpMap's Invert attribute is non-zero). +* +* If a CmpMap's component Mappings are joined in parallel, then +* its Nin attribute is given by the sum of the Nin attributes +* of each component (or to the sum of their Nout attributes if +* the CmpMap's Invert attribute is non-zero). +* Frame +* The Nin attribute for a Frame is always equal to the number +* of Frame axes (Naxes attribute). +* FrameSet +* The Nin attribute of a FrameSet is equal to the number of +* axes (Naxes attribute) of its base Frame (as specified by the +* FrameSet's Base attribute). The Nin attribute value may +* therefore change if a new base Frame is selected. +*att-- +*/ + +/* +*att++ +* Name: +* Nout + +* Purpose: +* Number of output coordinates for a Mapping. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the number of coordinate values generated +* by a Mapping to specify each output point (i.e. the number of +* dimensions of the space in which the Mapping's output points +* reside). + +* Applicability: +* Mapping +* All Mappings have this attribute. +* CmpMap +* If a CmpMap's component Mappings are joined in series, then +* its Nout attribute is equal to the Nout attribute of the +* second component (or to the Nin attribute of the first +* component if the the CmpMap's Invert attribute is non-zero). +* +* If a CmpMap's component Mappings are joined in parallel, then +* its Nout attribute is given by the sum of the Nout attributes +* of each component (or to the sum of their Nin attributes if +* the CmpMap's Invert attribute is non-zero). +* Frame +* The Nout attribute for a Frame is always equal to the number +* of Frame axes (Naxes attribute). +* FrameSet +* The Nout attribute of a FrameSet is equal to the number of +* FrameSet axes (Naxes attribute) which, in turn, is equal to +* the Naxes attribute of the FrameSet's current Frame (as +* specified by the Current attribute). The Nout attribute value +* may therefore change if a new current Frame is selected. +*att-- +*/ + +/* +*att++ +* Name: +* Report + +* Purpose: +* Report transformed coordinates? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls whether coordinate values are reported +* whenever a Mapping is used to transform a set of points. If its +* value is zero (the default), no report is made. However, if it +* is non-zero, the coordinates of each point are reported (both +* before and after transformation) by writing them to standard +* output. +* +* This attribute is provided as an aid to debugging, and to avoid +* having to report values explicitly in simple programs. + +* Applicability: +* Mapping +* All Mappings have this attribute. +* CmpMap +* When applied to a compound Mapping (CmpMap), only the Report +* attribute of the CmpMap, and not those of its component +* Mappings, is used. Coordinate information is never reported +* for the component Mappings individually, only for the +* complete CmpMap. +* Frame +* When applied to any Frame, the formatting capabilities of the +c Frame (as provided by the astFormat function) will be used to +f Frame (as provided by the AST_FORMAT function) will be used to +* format the reported coordinates. +* FrameSet +* When applied to any FrameSet, the formatting capabilities of +* the base and current Frames will be used (as above) to +* individually format the input and output coordinates, as +* appropriate. The Report attribute of a FrameSet is not itself +* affected by selecting a new base or current Frame, but the +* resulting formatting capabilities may be. + +* Notes: +* - Unlike most other attributes, the value of the Report +* attribute is not transferred when a Mapping is copied. Instead, +* its value is undefined (and therefore defaults to zero) in any +* copy. Similarly, it becomes undefined in any external +c representation of a Mapping produced by the astWrite function. +f representation of a Mapping produced by the AST_WRITE routine. +*att-- +*/ +/* This ia a boolean value (0 or 1) with a value of CHAR_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(Mapping,Report,report,CHAR_MAX) +astMAKE_GET(Mapping,Report,int,0,( ( this->report == CHAR_MAX ) ? + 0 : this->report )) +astMAKE_SET(Mapping,Report,int,report,( value != 0 )) +astMAKE_TEST(Mapping,Report,( this->report != CHAR_MAX )) + +/* +*att++ +* Name: +* TranForward + +* Purpose: +* Forward transformation defined? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This attribute indicates whether a Mapping is able to transform +* coordinates in the "forward" direction (i.e. converting input +* coordinates into output coordinates). If this attribute is +* non-zero, the forward transformation is available. Otherwise, it +* is not. + +* Applicability: +* Mapping +* All Mappings have this attribute. +* CmpMap +* The TranForward attribute value for a CmpMap is given by the +* boolean AND of the value for each component Mapping. +* FrameSet +* The TranForward attribute of a FrameSet applies to the +* transformation which converts between the FrameSet's base +* Frame and its current Frame (as specified by the Base and +* Current attributes). This value is given by the boolean AND +* of the TranForward values which apply to each of the +* individual sub-Mappings required to perform this conversion. +* The TranForward attribute value for a FrameSet may therefore +* change if a new Base or Current Frame is selected. + +* Notes: +* - An error will result if a Mapping with a TranForward value of +* zero is used to transform coordinates in the forward direction. +*att-- +*/ + +/* +*att++ +* Name: +* TranInverse + +* Purpose: +* Inverse transformation defined? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), readonly. + +* Description: +* This attribute indicates whether a Mapping is able to transform +* coordinates in the "inverse" direction (i.e. converting output +* coordinates back into input coordinates). If this attribute is +* non-zero, the inverse transformation is available. Otherwise, it +* is not. + +* Applicability: +* Mapping +* All Mappings have this attribute. +* CmpMap +* The TranInverse attribute value for a CmpMap is given by the +* boolean AND of the value for each component Mapping. +* FrameSet +* The TranInverse attribute of a FrameSet applies to the +* transformation which converts between the FrameSet's current +* Frame and its base Frame (as specified by the Current and +* Base attributes). This value is given by the boolean AND of +* the TranInverse values which apply to each of the individual +* sub-Mappings required to perform this conversion. +* The TranInverse attribute value for a FrameSet may therefore +* change if a new Base or Current Frame is selected. + +* Notes: +* - An error will result if a Mapping with a TranInverse value of +* zero is used to transform coordinates in the inverse direction. +*att-- +*/ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Mapping objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Mapping objects. + +* Parameters: +* objin +* Pointer to the Mapping to be copied. +* objout +* Pointer to the Mapping being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor exists simply to ensure that the "Report" +* attribute is cleared in any copy made of a Mapping. +*/ + +/* Local Variables: */ + AstMapping *out; /* Pointer to output Mapping */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the output Mapping. */ + out = (AstMapping *) objout; + +/* Clear the output Report attribute. */ + out->report = CHAR_MAX; +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Mapping objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Mapping objects. + +* Parameters: +* obj +* Pointer to the Mapping to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This destructor does nothing and exists only to maintain a +* one-to-one correspondence between destructors and copy +* constructors. +*/ + +/* Return without action. */ +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Mapping objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Mapping class to an output Channel. + +* Parameters: +* this +* Pointer to the Mapping whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstMapping *this; /* Pointer to the Mapping structure */ + int invert; /* Mapping inverted? */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Mapping structure. */ + this = (AstMapping *) this_object; + +/* Write out values representing the instance variables for the + Mapping class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Determine if the Mapping is inverted. The output values + (e.g. number of input and output coordinates) will refer to the + Mapping ***before*** this inversion flag is applied, but we need it + when using (e.g.) the astGetNin/astGetNout methods to determine + which one will return the required value. */ + invert = astGetInvert( this ); + +/* (NB. there is a subtle point here that dictates the extent to which + this inversion flag can be used... All use of methods (such as + astGetInvert, which might be over-ridden by derived classes) must + be restricted to determining the values of "unset" output + quantities only (below). This is because when re-loading the + Mapping, the derived classes will not have been loaded at the point + when these values are re-read - hence any value whose + interpretation depends on these methods cannot be reliably + recovered.) */ + +/* Nin. */ +/* ---- */ +/* Use the instance variable directly to avoid the effect of the + Invert attribute on the private member function. Treat zero as the + default. */ + set = ( this->nin != 0 ); + ival = set ? this->nin : ( !invert ? astGetNin( this ) : + astGetNout( this ) ); + astWriteInt( channel, "Nin", set, 0, ival, + "Number of input coordinates" ); + +/* Nout. */ +/* ----- */ +/* Use the instance variable directly. Treat zero as the default. */ + set = ( this->nout != this->nin ); + ival = set ? this->nout : ( !invert ? astGetNout( this ) : + astGetNin( this ) ); + astWriteInt( channel, "Nout", set, 0, ival, + "Number of output coordinates" ); + +/* IsSimple. */ +/* --------- */ + ival = astGetIsSimple( this ); + astWriteInt( channel, "IsSimp", ival, 0, ival, + ival ? "Mapping has been simplified" : + "Mapping has not been simplified" ); + +/* Invert. */ +/* ------- */ + set = TestInvert( this, status ); + ival = set ? GetInvert( this, status ) : astGetInvert( this ); + astWriteInt( channel, "Invert", set, 0, ival, + ival ? "Mapping inverted" : + "Mapping not inverted" ); + +/* TranForward. */ +/* ------------ */ +/* Use the instance variable directly. Treat 1 as the default. */ + set = ( this->tran_forward == 0 ); + ival = set ? this->tran_forward : ( !invert ? astGetTranForward( this ) : + astGetTranInverse( this ) ); + astWriteInt( channel, "Fwd", set, 0, ival, + ival ? "Forward transformation defined" : + "Forward transformation not defined" ); + +/* TranInverse. */ +/* ------------ */ +/* Use the instance variable directly. Treat 1 as the default. */ + set = ( this->tran_inverse == 0 ); + ival = set ? this->tran_inverse : ( !invert ? astGetTranInverse( this ) : + astGetTranForward( this ) ); + astWriteInt( channel, "Inv", set, 0, ival, + ival ? "Inverse transformation defined" : + "Inverse transformation not defined" ); + +/* Report. */ +/* ------- */ + set = TestReport( this, status ); + ival = set ? GetReport( this, status ) : astGetReport( this ); + astWriteInt( channel, "Report", set, 0, ival, + ival ? "Report coordinate transformations" : + "Don't report coordinate transformations" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAMapping and astCheckMapping functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Mapping,Object) +astMAKE_CHECK(Mapping) + +AstMapping *astInitMapping_( void *mem, size_t size, int init, + AstMappingVtab *vtab, const char *name, + int nin, int nout, + int tran_forward, int tran_inverse, int *status ) { +/* +*+ +* Name: +* astInitMapping + +* Purpose: +* Initialise a Mapping. + +* Type: +* Protected function. + +* Synopsis: +* #include "mapping.h" +* AstMapping *astInitMapping( void *mem, size_t size, int init, +* AstMappingVtab *vtab, const char *name, +* int nin, int nout, +* int tran_forward, int tran_inverse ) + +* Class Membership: +* Mapping initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Mapping object. It allocates memory (if necessary) to accommodate +* the Mapping plus any additional data associated with the derived class. +* It then initialises a Mapping structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Mapping at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Mapping is to be initialised. +* This must be of sufficient size to accommodate the Mapping data +* (sizeof(Mapping)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Mapping (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Mapping +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Mapping's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Mapping. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* nin +* The number of coordinate values per input point. +* nout +* The number of coordinate vales per output point. +* tran_forward +* A non-zero value indicates that the Mapping will be able to +* transform coordinates in the forward direction. A zero value +* indicates that it will not. +* tran_inverse +* A non-zero value indicates that the Mapping will be able to +* transform coordinates in the inverse direction. A zero value +* indicates that it will not. + +* Returned Value: +* A pointer to the new Mapping. + +* Notes: +* - The Mappings produced by this function implement all the basic methods +* defined by the Mapping class. However, their astTransform method does not +* actually perform any coordinate transformation (although it performs all +* necessary argument validation and creates an output PointSet if +* necessary, leaving its coordinate values undefined). +* - This means that Mappings produced by this function are of limited use +* on their own, but may easily be extended by a derived class simply by +* over-riding the astTransform method to add the necessary coordinate +* arithmetic. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstMapping *new; /* Pointer to new Mapping */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitMappingVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the initialisation values for validity, reporting an error if + necessary. */ + if ( nin < 0 ) { + astError( AST__BADNI, "astInitMapping(%s): Bad number of input " + "coordinates (%d).", status, name, nin ); + astError( AST__BADNI, "This number should be zero or more." , status); + } else if ( nout < 0 ) { + astError( AST__BADNO, "astInitMapping(%s): Bad number of output " + "coordinates (%d).", status, name, nout ); + astError( AST__BADNI, "This number should be zero or more." , status); + } + +/* Initialise an Object structure (the parent class) as the first component + within the Mapping structure, allocating memory if necessary. */ + new = (AstMapping *) astInitObject( mem, size, 0, + (AstObjectVtab *) vtab, name ); + + if ( astOK ) { + +/* Initialise the Mapping data. */ +/* ---------------------------- */ +/* Store the numbers of input and output coordinates. */ + new->nin = nin; + new->nout = nout; + +/* Store the flags indicating which coordinate transformations are + defined (constrain these values to 0 or 1). */ + new->tran_forward = ( tran_forward != 0 ); + new->tran_inverse = ( tran_inverse != 0 ); + +/* Initialise other attributes to their undefined values. */ + new->invert = CHAR_MAX; + new->report = CHAR_MAX; + new->flags = 0; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstMapping *astLoadMapping_( void *mem, size_t size, + AstMappingVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadMapping + +* Purpose: +* Load a Mapping. + +* Type: +* Protected function. + +* Synopsis: +* #include "mapping.h" +* AstMapping *astLoadMapping( void *mem, size_t size, +* AstMappingVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Mapping loader. + +* Description: +* This function is provided to load a new Mapping using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Mapping structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Mapping at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the Mapping is to be +* loaded. This must be of sufficient size to accommodate the +* Mapping data (sizeof(Mapping)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Mapping (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Mapping structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstMapping) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Mapping. If this is NULL, a pointer +* to the (static) virtual function table for the Mapping class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Mapping" is used instead. + +* Returned Value: +* A pointer to the new Mapping. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *new; /* Pointer to the new Mapping */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Mapping. In this case the + Mapping belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstMapping ); + vtab = &class_vtab; + name = "Mapping"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitMappingVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Mapping. */ + new = astLoadObject( mem, size, (AstObjectVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Mapping" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Initialise bitwise flags to zero. */ + new->flags = 0; + +/* Nin. */ +/* ---- */ + new->nin = astReadInt( channel, "nin", 0 ); + if ( new->nin < 0 ) new->nin = 0; + +/* Nout. */ +/* ----- */ + new->nout = astReadInt( channel, "nout", new->nin ); + if ( new->nout < 0 ) new->nout = 0; + +/* Invert. */ +/* ------- */ + new->invert = astReadInt( channel, "invert", CHAR_MAX ); + if ( TestInvert( new, status ) ) SetInvert( new, new->invert, status ); + +/* IsSimple. */ +/* --------- */ + if( astReadInt( channel, "issimp", 0 ) ) new->flags |= AST__ISSIMPLE_FLAG; + +/* TranForward. */ +/* ------------ */ + new->tran_forward = ( astReadInt( channel, "fwd", 1 ) != 0 ); + +/* TranInverse. */ +/* ------------ */ + new->tran_inverse = ( astReadInt( channel, "inv", 1 ) != 0 ); + +/* Report. */ +/* ------- */ + new->report = astReadInt( channel, "report", CHAR_MAX ); + if ( TestReport( new, status ) ) SetReport( new, new->report, status ); + +/* If an error occurred, clean up by deleting the new Mapping. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Mapping pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions + defined by this class. Each simply checks the global error status + and then locates and executes the appropriate member function, + using the function pointer stored in the object's virtual function + table (this pointer is located using the astMEMBER macro defined in + "object.h"). + + Note that the member function may not be the one defined here, as + it may have been over-ridden by a derived class. However, it should + still have the same interface. */ + +void astDecompose_( AstMapping *this, AstMapping **map1, AstMapping **map2, + int *series, int *invert1, int *invert2, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,Decompose))( this, map1, map2, series, invert1, invert2, status ); +} +int astGetNin_( AstMapping *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,GetNin))( this, status ); +} +int astGetNout_( AstMapping *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,GetNout))( this, status ); +} +int astGetIsLinear_( AstMapping *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,GetIsLinear))( this, status ); +} +int astGetTranForward_( AstMapping *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,GetTranForward))( this, status ); +} +int astGetTranInverse_( AstMapping *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,GetTranInverse))( this, status ); +} +void astInvert_( AstMapping *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,Invert))( this, status ); +} +void astMapBox_( AstMapping *this, + const double lbnd_in[], const double ubnd_in[], int forward, + int coord_out, double *lbnd_out, double *ubnd_out, + double xl[], double xu[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,MapBox))( this, lbnd_in, ubnd_in, forward, + coord_out, lbnd_out, ubnd_out, xl, xu, status ); +} +int astMapList_( AstMapping *this, int series, int invert, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,MapList))( this, series, invert, + nmap, map_list, invert_list, status ); +} +int *astMapSplit_( AstMapping *this, int nin, const int *in, AstMapping **map, + int *status ){ + int *result = NULL; + AstMapping *tmap; + + if( map ) *map = NULL; + if ( !astOK ) return NULL; + + result = (**astMEMBER(this,Mapping,MapSplit))( this, nin, in, &tmap, status ); + if( tmap ) { + *map = astCopy( tmap ); + tmap = astAnnul( tmap ); + } + + return result; +} +int astMapMerge_( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { + + if ( !astOK || astDoNotSimplify( this ) ) return -1; + return (**astMEMBER(this,Mapping,MapMerge))( this, where, series, nmap, + map_list, invert_list, status ); +} +int astDoNotSimplify_( AstMapping *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,DoNotSimplify))( this, status ); +} +void astReportPoints_( AstMapping *this, int forward, + AstPointSet *in_points, AstPointSet *out_points, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,ReportPoints))( this, forward, + in_points, out_points, status ); +} +#define MAKE_RESAMPLE_(X,Xtype) \ +int astResample##X##_( AstMapping *this, int ndim_in, const int *lbnd_in, \ + const int *ubnd_in, const Xtype *in, \ + const Xtype *in_var, int interp, \ + void (* finterp)( void ), const double *params, \ + int flags, double tol, int maxpix, Xtype badval, \ + int ndim_out, \ + const int *lbnd_out, const int *ubnd_out, \ + const int *lbnd, const int *ubnd, Xtype *out, \ + Xtype *out_var, int *status ) { \ + if ( !astOK ) return 0; \ + return (**astMEMBER(this,Mapping,Resample##X))( this, ndim_in, lbnd_in, \ + ubnd_in, in, in_var, \ + interp, finterp, params, \ + flags, tol, maxpix, \ + badval, ndim_out, \ + lbnd_out, ubnd_out, \ + lbnd, ubnd, \ + out, out_var, status ); \ +} +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_RESAMPLE_(LD,long double) +#endif +MAKE_RESAMPLE_(D,double) +MAKE_RESAMPLE_(F,float) +MAKE_RESAMPLE_(L,long int) +MAKE_RESAMPLE_(UL,unsigned long int) +MAKE_RESAMPLE_(I,int) +MAKE_RESAMPLE_(UI,unsigned int) +MAKE_RESAMPLE_(K,INT_BIG) +MAKE_RESAMPLE_(UK,UINT_BIG) +MAKE_RESAMPLE_(S,short int) +MAKE_RESAMPLE_(US,unsigned short int) +MAKE_RESAMPLE_(B,signed char) +MAKE_RESAMPLE_(UB,unsigned char) +#undef MAKE_RESAMPLE_ + +#define MAKE_REBIN_(X,Xtype) \ +void astRebin##X##_( AstMapping *this, double wlim, int ndim_in, const int *lbnd_in, \ + const int *ubnd_in, const Xtype *in, \ + const Xtype *in_var, int interp, \ + const double *params, \ + int flags, double tol, int maxpix, Xtype badval, \ + int ndim_out, \ + const int *lbnd_out, const int *ubnd_out, \ + const int *lbnd, const int *ubnd, Xtype *out, \ + Xtype *out_var, int *status ) { \ + if ( !astOK ) return; \ + (**astMEMBER(this,Mapping,Rebin##X))( this, wlim, ndim_in, lbnd_in, \ + ubnd_in, in, in_var, \ + interp, params, \ + flags, tol, maxpix, \ + badval, ndim_out, \ + lbnd_out, ubnd_out, \ + lbnd, ubnd, \ + out, out_var, status ); \ +} +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_REBIN_(LD,long double) +#endif +MAKE_REBIN_(D,double) +MAKE_REBIN_(F,float) +MAKE_REBIN_(I,int) +MAKE_REBIN_(B,signed char) +MAKE_REBIN_(UB,unsigned char) +#undef MAKE_REBIN_ + + +#define MAKE_REBINSEQ_(X,Xtype) \ +void astRebinSeq##X##_( AstMapping *this, double wlim, int ndim_in, const int *lbnd_in, \ + const int *ubnd_in, const Xtype *in, \ + const Xtype *in_var, int interp, \ + const double *params, \ + int flags, double tol, int maxpix, Xtype badval, \ + int ndim_out, \ + const int *lbnd_out, const int *ubnd_out, \ + const int *lbnd, const int *ubnd, Xtype *out, \ + Xtype *out_var, double *weights, int64_t *nused, \ + int *status ) { \ + if ( !astOK ) return; \ + (**astMEMBER(this,Mapping,RebinSeq##X))( this, wlim, ndim_in, lbnd_in, \ + ubnd_in, in, in_var, \ + interp, params, \ + flags, tol, maxpix, \ + badval, ndim_out, \ + lbnd_out, ubnd_out, \ + lbnd, ubnd, out, out_var, \ + weights, nused, status ); \ +} + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_REBINSEQ_(LD,long double) +#endif +MAKE_REBINSEQ_(D,double) +MAKE_REBINSEQ_(F,float) +MAKE_REBINSEQ_(I,int) +MAKE_REBINSEQ_(B,signed char) +MAKE_REBINSEQ_(UB,unsigned char) + +#undef MAKE_REBINSEQ_ + +double astRate_( AstMapping *this, double *at, int ax1, int ax2, int *status ){ + astDECLARE_GLOBALS + + if ( !astOK ) return AST__BAD; + + astGET_GLOBALS(this); + + if( ax1 < 0 || ax1 >= astGetNout( this ) ) { + astError( AST__AXIIN, "astRate(%s): Invalid output index (%d) " + "specified - should be in the range 1 to %d.", status, + astGetClass( this ), ax1 + 1, astGetNout( this ) ); + + } else if( ax2 < 0 || ax2 >= astGetNin( this ) ) { + astError( AST__AXIIN, "astRate(%s): Invalid input index (%d) " + "specified - should be in the range 1 to %d.", status, + astGetClass( this ), ax2 + 1, astGetNin( this ) ); + } + + if( rate_disabled ) { + return ( at[ ax2 ] != AST__BAD ) ? 1.0 : AST__BAD; + } else { + return (**astMEMBER(this,Mapping,Rate))( this, at, ax1, ax2, status ); + } +} +AstMapping *astRemoveRegions_( AstMapping *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Mapping,RemoveRegions))( this, status ); +} +AstMapping *astSimplify_( AstMapping *this, int *status ) { + AstMapping *result; + if ( !astOK ) return NULL; + if( !astGetIsSimple( this ) && !astDoNotSimplify( this ) ) { + result = (**astMEMBER(this,Mapping,Simplify))( this, status ); + if( result ) result->flags |= AST__ISSIMPLE_FLAG; /* Indicate simplification has been done */ + } else { + result = astClone( this ); + } + return result; +} +AstPointSet *astTransform_( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { + AstPointSet *result; + if ( !astOK ) return NULL; + result = (**astMEMBER(this,Mapping,Transform))( this, in, forward, out, status ); + (void) astReplaceNaN( result ); + return result; +} +void astTran1_( AstMapping *this, int npoint, const double xin[], + int forward, double xout[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,Tran1))( this, npoint, xin, forward, xout, status ); +} +void astTran2_( AstMapping *this, + int npoint, const double xin[], const double yin[], + int forward, double xout[], double yout[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,Tran2))( this, npoint, xin, yin, + forward, xout, yout, status ); +} +void astTranGrid_( AstMapping *this, int ncoord_in, const int lbnd[], + const int ubnd[], double tol, int maxpix, int forward, + int ncoord_out, int outdim, double *out, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,TranGrid))( this, ncoord_in, lbnd, ubnd, tol, + maxpix, forward, ncoord_out, outdim, + out, status ); +} +void astTranN_( AstMapping *this, int npoint, + int ncoord_in, int indim, const double *in, + int forward, int ncoord_out, int outdim, double *out, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,TranN))( this, npoint, + ncoord_in, indim, in, + forward, ncoord_out, outdim, out, status ); +} +void astTranP_( AstMapping *this, int npoint, + int ncoord_in, const double *ptr_in[], + int forward, int ncoord_out, double *ptr_out[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Mapping,TranP))( this, npoint, + ncoord_in, ptr_in, + forward, ncoord_out, ptr_out, status ); +} +int astLinearApprox_( AstMapping *this, const double *lbnd, + const double *ubnd, double tol, double *fit, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,LinearApprox))( this, lbnd, ubnd, tol, fit, status ); +} + + +int astQuadApprox_( AstMapping *this, const double lbnd[2], + const double ubnd[2], int nx, int ny, double *fit, + double *rms, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Mapping,QuadApprox))( this, lbnd, ubnd, nx, + ny, fit, rms, status ); +} + + + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +void DecomposeId_( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +void MapBoxId_( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * ); +double astRateId_( AstMapping *, double *, int, int, int * ); +void astMapSplitId_( AstMapping *, int, const int *, int *, AstMapping **, + int * ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +void astDecomposeId_( AstMapping *this, AstMapping **map1, + AstMapping **map2, int *series, int *invert1, + int *invert2, int *status ) { +/* +*++ +* Name: +c astDecompose +f AST_DECOMPOSE + +* Purpose: +* Decompose a Mapping into two component Mappings. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astDecompose( AstMapping *this, AstMapping **map1, +c AstMapping **map2, int *series, int *invert1, +c int *invert2 ) +f CALL AST_DECOMPOSE( THIS, MAP1, MAP2, SERIES, INVERT1, INVERT2, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function returns pointers to two Mappings which, when applied +f This routine returns pointers to two Mappings which, when applied +* either in series or parallel, are equivalent to the supplied Mapping. +* +* Since the Frame class inherits from the Mapping class, Frames can +* be considered as special types of Mappings and so this method can +* be used to decompose either CmpMaps or CmpFrames. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping. +c map1 +f MAP1 = INTEGER (Returned) +c Address of a location to receive a pointer to first component +f A pointer to first component +* Mapping. +c map2 +f MAP2 = INTEGER (Returned) +c Address of a location to receive a pointer to second component +f A pointer to second component +* Mapping. +c series +f SERIES = LOGICAL (Returned) +c Address of a location to receive a value indicating if the +c component Mappings are applied in series or parallel. A non-zero +c value means that the supplied Mapping is equivalent to applying map1 +c followed by map2 in series. A zero value means that the supplied +c Mapping is equivalent to applying map1 to the lower numbered axes +c and map2 to the higher numbered axes, in parallel. +f Indicates if the +f component Mappings are applied in series or parallel. A .TRUE. +f value means that the supplied Mapping is equivalent to applying MAP1 +f followed by MAP2 in series. A zero value means that the supplied +f Mapping is equivalent to applying MAP1 to the lower numbered axes +f and MAP2 to the higher numbered axes, in parallel. +c invert1 +f INVERT1 = INTEGER (Returned) +c The value of the Invert attribute to be used with map1. +f The value of the Invert attribute to be used with MAP1. +c invert2 +f INVERT2 = INTEGER (Returned) +c The value of the Invert attribute to be used with map2. +f The value of the Invert attribute to be used with MAP2. + +* Applicability: +* CmpMap +c If the supplied Mapping is a CmpMap, then map1 and map2 will be +f If the supplied Mapping is a CmpMap, then MAP1 and MAP2 will be +* returned holding pointers to the component Mappings used to +* create the CmpMap, either in series or parallel. Note, changing +* the Invert attribute of either of the component Mappings using +* the returned pointers will have no effect on the supplied CmpMap. +* This is because the CmpMap remembers and uses the original settings +* of the Invert attributes (that is, the values of the Invert +* attributes when the CmpMap was first created). These are the +c Invert values which are returned in invert1 and invert2. +f Invert values which are returned in INVERT1 and INVERT2. +* TranMap +c If the supplied Mapping is a TranMap, then map1 and map2 will be +f If the supplied Mapping is a TranMap, then MAP1 and MAP2 will be +* returned holding pointers to the forward and inverse Mappings +* represented by the TranMap (zero will be returned for +c series). +f SERIES). +* Note, changing the Invert attribute of +* either of the component Mappings using the returned pointers will +* have no effect on the supplied TranMap. This is because the TranMap +* remembers and uses the original settings of the Invert attributes +* (that is, the values of the Invert attributes when the TranMap was +* first created). These are the +c Invert values which are returned in invert1 and invert2. +f Invert values which are returned in INVERT1 and INVERT2. +* Mapping +c For any class of Mapping other than a CmpMap, map1 will be +c returned holding a clone of the supplied Mapping pointer, and map2 +c will be returned holding a NULL pointer. Invert1 will be returned +c holding the current value of the Invert attribute for the supplied +c Mapping, and invert2 will be returned holding zero. +f For any class of Mapping other than a CmpMap, MAP1 will be +f returned holding a clone of the supplied Mapping pointer, and MAP2 +f will be returned holding AST__NULL. INVERT1 will be returned +f holding the current value of the Invert attribute for the supplied +f Mapping, and INVERT2 will be returned holding zero. +* CmpFrame +c If the supplied Mapping is a CmpFrame, then map1 and map2 will be +f If the supplied Mapping is a CmpFrame, then MAP1 and MAP2 will be +* returned holding pointers to the component Frames used to +* create the CmpFrame. The component Frames are considered to be in +* applied in parallel. +* Frame +c For any class of Frame other than a CmpFrame, map1 will be +c returned holding a clone of the supplied Frame pointer, and map2 +c will be returned holding a NULL pointer. +f For any class of Frame other than a CmpFrame, MAP1 will be +f returned holding a clone of the supplied Frame pointer, and MAP2 +f will be returned holding AST__NULL. + +* Notes: +* - The returned Invert values should be used in preference to the +* current values of the Invert attribute in map1 and map2. This is +* because the attributes may have changed value since the Mappings +* were combined. +* - Any changes made to the component Mappings using the returned +* pointers will be reflected in the supplied Mapping. + +*-- + +* Implementation Notes: +* This function implements the public interface for the +* astDecompose method. It is identical to astDecompose_ except for +* the following: +* +* - ID values are returned via the "map1" and "map2" parameters +* instead of true C pointers. This is required because this +* conversion cannot be performed by the macro that invokes the +* function. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the normal astDecompose_ function to decompose the Mapping. */ + astDecompose( this, map1, map2, series, invert1, invert2 ); + +/* If required, return ID values for the component Mappings. */ + if ( map1 ) *map1 = astMakeId( *map1 ); + if ( map2 ) *map2 = astMakeId( *map2 ); + +} + +void astMapBoxId_( AstMapping *this, + const double lbnd_in[], const double ubnd_in[], + int forward, int coord_out, + double *lbnd_out, double *ubnd_out, + double xl[], double xu[], int *status ) { +/* +*++ +* Name: +c astMapBox +f AST_MAPBOX + +* Purpose: +* Find a bounding box for a Mapping. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astMapBox( AstMapping *this, +c const double lbnd_in[], const double ubnd_in[], +c int forward, int coord_out, +c double *lbnd_out, double *ubnd_out, +c double xl[], double xu[] ); +f CALL AST_MAPBOX( THIS, LBND_IN, UBND_IN, FORWARD, COORD_OUT, +f LBND_OUT, UBND_OUT, XL, XU, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function allows you to find the "bounding box" which just +c encloses another box after it has been transformed by a Mapping +c (using either its forward or inverse transformation). A typical +c use might be to calculate the size of an image after being +c transformed by a Mapping. +f This routine allows you to find the "bounding box" which just +f encloses another box after it has been transformed by a Mapping +f (using either its forward or inverse transformation). A typical +f use might be to calculate the size of an image after being +f transformed by a Mapping. +* +c The function works on one dimension at a time. When supplied +c with the lower and upper bounds of a rectangular region (box) of +c input coordinate space, it finds the lowest and highest values +c taken by a nominated output coordinate within that +c region. Optionally, it also returns the input coordinates where +c these bounding values are attained. It should be used repeatedly +c to obtain the extent of the bounding box in more than one +c dimension. +f The routine works on one dimension at a time. When supplied with +f the lower and upper bounds of a rectangular region (box) of +f input coordinate space, it finds the lowest and highest values +f taken by a nominated output coordinate within that region. It +f also returns the input coordinates where these bounding values +f are attained. It should be used repeatedly to obtain the extent +f of the bounding box in more than one dimension. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping. +c lbnd_in +f LBND_IN( * ) = DOUBLE PRECISION (Given) +c Pointer to an array of double, with one element for each +c Mapping input coordinate. This should contain the lower bound +c of the input box in each input dimension. +f An array with one element for each Mapping input +f coordinate. This should contain the lower bound of the input +f box in each input dimension. +c ubnd_in +f UBND_IN( * ) = DOUBLE PRECISION (Given) +c Pointer to an array of double, with one element for each +c Mapping input coordinate. This should contain the upper bound +c of the input box in each input dimension. +f An array with one element for each Mapping input +f coordinate. This should contain the upper bound of the input +f box in each input dimension. +* +* Note that it is permissible for the upper bound to be less +* than the corresponding lower bound, as the values will simply +* be swapped before use. +c forward +f FORWARD = LOGICAL (Given) +c If this value is non-zero, then the Mapping's forward +c transformation will be used to transform the input +c box. Otherwise, its inverse transformation will be used. +f If this value is .TRUE., then the Mapping's forward +f transformation will be used to transform the input +f box. Otherwise, its inverse transformation will be used. +* +c (If the inverse transformation is selected, then references +c to "input" and "output" coordinates in this description +c should be transposed. For example, the size of the "lbnd_in" +c and "ubnd_in" arrays should match the number of output +c coordinates, as given by the Mapping's Nout +c attribute. Similarly, the "coord_out" parameter, below, +c should nominate one of the Mapping's input coordinates.) +f (If the inverse transformation is selected, then references +f to "input" and "output" coordinates in this description +f should be transposed. For example, the size of the LBND_IN +f and UBND_IN arrays should match the number of output +f coordinates, as given by the Mapping's Nout attribute. +f Similarly, the COORD_OUT argument, below, should nominate one +f of the Mapping's input coordinates.) +c coord_out +f COORD_OUT = INTEGER (Given) +* The index of the output coordinate for which the lower and +* upper bounds are required. This value should be at least one, +* and no larger than the number of Mapping output coordinates. +c lbnd_out +f LBND_OUT = DOUBLE PRECISION (Returned) +c Pointer to a double in which to return the lowest value taken +c by the nominated output coordinate within the specified +c region of input coordinate space. +f The lowest value taken by the nominated output coordinate +f within the specified region of input coordinate space. +c ubnd_out +f UBND_OUT = DOUBLE PRECISION (Returned) +c Pointer to a double in which to return the highest value +c taken by the nominated output coordinate within the specified +c region of input coordinate space. +f The highest value taken by the nominated output coordinate +f within the specified region of input coordinate space. +c xl +f XL( * ) = DOUBLE PRECISION (Returned) +c An optional pointer to an array of double, with one element +c for each Mapping input coordinate. If given, this array will +c be filled with the coordinates of an input point (although +c not necessarily a unique one) for which the nominated output +c coordinate attains the lower bound value returned in +c "*lbnd_out". +c +c If these coordinates are not required, a NULL pointer may be +c supplied. +f An array with one element for each Mapping input +f coordinate. This will return the coordinates of an input +f point (although not necessarily a unique one) for which the +f nominated output coordinate attains the lower bound value +f returned in LBND_OUT. +c xu +f XU( * ) = DOUBLE PRECISION (Returned) +c An optional pointer to an array of double, with one element +c for each Mapping input coordinate. If given, this array will +c be filled with the coordinates of an input point (although +c not necessarily a unique one) for which the nominated output +c coordinate attains the upper bound value returned in +c "*ubnd_out". +c +c If these coordinates are not required, a NULL pointer may be +c supplied. +f An array with one element for each Mapping input +f coordinate. This will return the coordinates of an input +f point (although not necessarily a unique one) for which the +f nominated output coordinate attains the upper bound value +f returned in UBND_OUT. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Any input points which are transformed by the Mapping to give +* output coordinates containing the value AST__BAD are regarded as +* invalid and are ignored. They will make no contribution to +* determining the output bounds, even although the nominated +* output coordinate might still have a valid value at such points. +c - An error will occur if the required output bounds cannot be +c found. Typically, this might happen if all the input points +c which the function considers turn out to be invalid (see +c above). The number of points considered before generating such +c an error is quite large, so this is unlikely to occur by +c accident unless valid points are restricted to a very small +c subset of the input coordinate space. +f - An error will occur if the required output bounds cannot be +f found. Typically, this might happen if all the input points +f which the routine considers turn out to be invalid (see +f above). The number of points considered before generating such +f an error is quite large, so this is unlikely to occur by +f accident unless valid points are restricted to a very small +f subset of the input coordinate space. +c - The values returned via "lbnd_out", "ubnd_out", "xl" and "xu" +c will be set to the value AST__BAD if this function should fail +c for any reason. Their initial values on entry will not be +c altered if the function is invoked with the AST error status +c set. +f - The values returned via LBND_OUT, UBND_OUT, XL and XU will be +f set to the value AST__BAD if this routine should fail for any +f reason. Their initial values on entry will not be altered if the +f routine is invoked with STATUS set to an error value. +*-- + +* Implementation Notes: +* This function implements the public interface for the astMapBox +* method. It is identical to astMapBox_ except that the nominated +* output coordinate given in "coord_out" is decremented by one +* before use. This is to allow the public interface to use +* one-based coordinate numbering (internally, zero-based +* coordinate numbering is used). +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the protected version of this function with the "coord_out" + value decremented. */ + astMapBox( this, lbnd_in, ubnd_in, forward, coord_out - 1, + lbnd_out, ubnd_out, xl, xu ); +} + +double astRateId_( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +*++ +* Name: +c astRate +f AST_RATE + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c double astRate( AstMapping *this, double *at, int ax1, int ax2 ) +f RESULT = AST_RATE( THIS, AT, AX1, AX2, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function +f This routine +* evaluates the rate of change of a specified output of the supplied +* Mapping with respect to a specified input, at a specified input +* position. +* +* The result is estimated by interpolating the function using a +* fourth order polynomial in the neighbourhood of the specified +* position. The size of the neighbourhood used is chosen to minimise +* the RMS residual per unit length between the interpolating +* polynomial and the supplied Mapping function. This method produces +* good accuracy but can involve evaluating the Mapping 100 or more +* times. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping to be applied. +c at +f AT( * ) = DOUBLE PRECISION (Given) +c The address of an +f An +* array holding the axis values at the position at which the rate +* of change is to be evaluated. The number of elements in this +* array should equal the number of inputs to the Mapping. +c ax1 +f AX1 = INTEGER (Given) +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 1 for the first output). +c ax2 +f AX2 = INTEGER (Given) +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 1 for the first +* input). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astRate() +f AST_RATE = DOUBLE PRECISION +c The rate of change of Mapping output "ax1" with respect to input +c "ax2", evaluated at "at", or AST__BAD if the value cannot be +c calculated. +f The rate of change of Mapping output AX1 with respect to input +f AX2, evaluated at AT, or AST__BAD if the value cannot be +f calculated. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*-- + +* Implementation Notes: +* This function implements the public interface for the astRate +* method. It is identical to astRate_ except that the nominated +* coordinates given in "ax1" and "ax2" are decremented by one +* before use. This is to allow the public interface to use +* one-based coordinate numbering (internally, zero-based +* coordinate numbering is used). +*/ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Invoke the protected version of this function with the axis indices + decremented. */ + return astRate( this, at, ax1 - 1, ax2 - 1 ); +} + +void astMapSplitId_( AstMapping *this, int nin, const int *in, int *out, + AstMapping **map, int *status ){ +/* +*++ +* Name: +c astMapSplit +f AST_MAPSPLIT + +* Purpose: +* Split a Mapping up into parallel component Mappings. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astMapSplit( AstMapping *this, int nin, const int *in, int *out, +c AstMapping **map ) +f CALL AST_MAPSPLIT( THIS, NIN, IN, OUT, MAP, STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +c This function +f This routine +* creates a new Mapping which connects specified inputs within a +* supplied Mapping to the corresponding outputs of the supplied Mapping. +* This is only possible if the specified inputs correspond to some +* subset of the Mapping outputs. That is, there must exist a subset of +* the Mapping outputs for which each output depends only on the selected +* Mapping inputs, and not on any of the inputs which have not been +* selected. Also, any output which is not in this subset must not depend +* on any of the selected inputs. If these conditions are not met by the +* supplied Mapping, then +c a NULL +f an AST__NULL +* Mapping pointer is returned. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Mapping to be split. +c nin +f NIN = INTEGER (Given) +c The number of inputs to pick from "this". +f The number of inputs to pick from THIS. +c in +f IN( NIN ) = INTEGER (Given) +c Pointer to an +f An +* array holding the indices within the supplied Mapping of the inputs +* which are to be picked from the Mapping. +c This array should have "nin" elements. +* If "Nin" is the number of inputs of the supplied Mapping, then each +* element should have a value in the range 1 to Nin. +c out +f OUT( * ) = INTEGER (Returned) +c Pointer to an +f An +* array in which to return the indices of the outputs of the supplied +* Mapping which are fed by the picked inputs. A value of one is +* used to refer to the first Mapping output. The supplied array should +* have a length at least equal to the number of outputs in the +* supplied Mapping. The number of values stored in the array on +* exit will equal the number of outputs in the returned Mapping. +* The i'th element in the returned array holds the index within +* the supplied Mapping which corresponds to the i'th output of +* the returned Mapping. +c map +f MAP = INTEGER (Returned) +c Address of a location at which to return a pointer to the +f The +* returned Mapping. This Mapping will have +c "nin" inputs (the number of outputs may be different to "nin"). NULL +f NIN inputs (the number of outputs may be different to NIN). AST__NULL +* is returned if the supplied Mapping has no subset of outputs which +* depend only on the selected inputs. The returned Mapping is a +* deep copy of the required parts of the supplied Mapping. + +* Notes: +* - If this +c function +f routine +* is invoked with the global error status set, or if it should fail for +* any reason, then +c a NULL value +f AST__NULL +* will be returned for +c the "map" pointer. +f MAP. +*-- + +* Implementation Notes: +* - This function implements the astMapSplit method available via the +* public interface to the Mapping class and uses 1-based axis indices. +* The protected interface method is provided by the astMapSplit function +* and uses zero-based axis indices. Also, an ID value is returned for +* "map" rather than a pointer. +*/ + +/* Local Variables: */ + int *in_zero; /* Pointer to array of zero-based input indices */ + int *result; /* Pointer to array of zero-based output indices*/ + int i; /* Axis index */ + int nout; /* No of outputs */ + +/* Initialise */ + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Decrement the axis indices by 1. */ + in_zero = astMalloc( sizeof( int )*(size_t) nin ); + if( in_zero ) { + for( i = 0; i < nin; i++ ) in_zero[ i ] = in[ i ] - 1; + +/* Invoked the protected astMapSplit functon. */ + result = astMapSplit( this, nin, in_zero, map ); + +/* If succesful, copy the output axes to the supplied array. */ + if( result ) { + nout = astGetNout( *map ); + for( i = 0; i < nout; i++ ) out[ i ] = result[ i ] + 1; + +/* Free resurces. */ + result = astFree( result ); + } + in_zero = astFree( in_zero ); + } + +/* Free the returned Mapping if an error has occurred. */ + if( !astOK ) *map = astAnnul( *map ); + +/* Return an ID value for the Mapping. */ + *map = astMakeId( *map ); +} + + + + + + + + diff --git a/mapping.h b/mapping.h new file mode 100644 index 0000000..7b63fa2 --- /dev/null +++ b/mapping.h @@ -0,0 +1,856 @@ +#if !defined( MAPPING_INCLUDED ) /* Include this file only once */ +#define MAPPING_INCLUDED +/* +*++ +* Name: +* mapping.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Mapping class. + +* Invocation: +* #include "mapping.h" + +* Description: +* This include file defines the interface to the Mapping class and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this class. +* +* The Mapping class provides basic facilities for transforming a +* set of points to give a new set of points and for resampling +* grids of data. However, it does not have a constructor +* function. This is because the class only forms a template for +* deriving new classes which themselves implement specific forms +* of coordinate transformation. They do this by extending the +* protected astTransform method provided by this class. + +* Inheritance: +* The Mapping class inherits from the Object class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* Nin (integer) +* A read-only attribute giving the number of input coordinate +* values required per point by a Mapping (i.e. the number of +* dimensions of the space in which input points reside). +* Nout (integer) +* A read-only attribute giving the number of output coordinate +* values generated per point by a Mapping (i.e. the number of +* dimensions of the space in which output points reside). +* Invert (integer) +* A boolean value (0 or 1) which controls which of a Mapping's +* two possible coordinate transformations is considered the +* "forward" transformation and which is the "inverse" +* transformation. If this value is zero (the default), the +* behaviour will be as defined when the Mapping was first +* created. If it is non-zero, the transformations will be +* inter-changed, so that the Mapping displays the inverse of +* its original behaviour. +* +* Note that inverting the boolean sense of the Invert attribute +* will cause the values of the Nin/Nout and +* TranForward/TranInverse attributes to be interchanged. +* Report (integer) +* A boolean value (0 or 1) which controls whether to report +* coordinate values when a Mapping is used to transform a set +* of points. If this value is zero (the default), no report is +* made. If it is non-zero, the coordinates of each point +* (before and after transformation) are reported by writing +* them to standard output. +* +* This attribute is intended as an aid to debugging and to save +* having to report values explicitly in simple programs. +* Unlike other attributes, the value of the Report attribute is +* not inherited when a Mapping is copied (its value is +* initially undefined, and therefore defaults to zero, in any +* copy). +* IsSimple (boolean) +* A read-only attribute indicating if the Mapping has been +* simpified. +* TranForward (integer) +* A read-only boolean value (0 or 1) which indicates whether a +* Mapping is able to transform coordinates in the "forward" +* direction (i.e. converting input coordinates into output +* coordinates). +* TranInverse (integer) +* A read-only boolean value (0 or 1) which indicates whether a +* Mapping is able to transform coordinates in the "inverse" +* direction (i.e. converting output coordinates back into input +* coordinates). + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astClearAttrib +* Clear an attribute value for a Mapping. +* astGetAttrib +* Get an attribute value for a Mapping. +* astSetAttrib +* Set an attribute value for a Mapping. +* astTestAttrib +* Test if an attribute value has been set for a Mapping. + +* New Methods Defined: +* Public: +* astDecompose +* Decompose a Mapping into two component Mappings. +* astInvert +* Invert a Mapping. +* astLinearApprox +* Form a linear approximation to a Mapping +* astMapBox +* Find a bounding box for a Mapping. +* astQuadApprox +* Form a quadratic approximation to a Mapping +* astRate +* Find rate of change of a Mapping output +* astRebin +* Rebin a region of a data grid. +* astRebinSeq +* Rebin a region of a sequence of data grids. +* astResample +* Resample a region of a data grid. +* astSimplify +* Simplify a Mapping. +* astTran1 +* Transform 1-dimensional coordinates. +* astTran2 +* Transform 2-dimensional coordinates. +* astTranGrid +* Transform an N-dimensional regular grid of positions. +* astTranN +* Transform N-dimensional coordinates. +* astTranP (C only) +* Transform N-dimensional coordinates held in separate arrays. +* +* Protected: +* astClearInvert +* Clear the Invert attribute value for a Mapping. +* astClearReport +* Clear the Report attribute value for a Mapping. +* astGetInvert +* Get the Invert attribute value for a Mapping. +* astGetIsSimple +* Get the IsSimple attribute. +* astGetNin +* Get the number of input coordinates for a Mapping. +* astGetNout +* Get the number of output coordinates for a Mapping. +* astGetReport +* Get the Report attribute value for a Mapping. +* astGetTranForward +* Determine if a Mapping can perform a "forward" coordinate +* transformation. +* astGetTranInverse +* Determine if a Mapping can perform an "inverse" coordinate +* transformation. +* astMapList +* Decompose a Mapping into a sequence of simpler Mappings. +* astMapSplit +* Select a subset of Mapping inputs. +* astMapMerge +* Simplify a sequence of Mappings. +* astReportPoints +* Report the effect of transforming a set of points using a Mapping. +* astSetInvert +* Set the Invert attribute value for a Mapping. +* astSetReport +* Set the Report attribute value for a Mapping. +* astTestInvert +* Test if an Invert attribute value has been set for a Mapping. +* astTestReport +* Test if an Report attribute value has been set for a Mapping. +* astTransform +* Transform a set of points. + +* Other Class Functions: +* Public: +* astIsAMapping +* Test class membership. +* +* Protected: +* astCheckMapping +* Validate class membership. +* astInitMapping +* Initialise a Mapping. +* astInitMappingVtab +* Initialise the virtual function table for the Mapping class. +* astLoadMapping +* Load a Mapping. + +* Macros: +* Public: +* AST__BLOCKAVE +* Block averaging interpolation. +* AST__GAUSS +* Use exp(-k*x*x) spreading. +* AST__LINEAR +* Simple linear interpolation. +* AST__NEAREST +* Use nearest pixel centre. +* AST__SINC +* Use sinc(pi*x) interpolation. +* AST__SINCCOS +* Use sinc(pi*x)*cos(k*pi*x) interpolation. +* AST__SINCGAUSS +* Use sinc(pi*x)*exp(-k*x*x) interpolation. +* AST__SINCSINC +* Use sinc(pi*x)*sinc(k*pi*x) interpolation. +* AST__SOMB +* Use somb(pi*x) interpolation. +* AST__SOMBCOS +* Use somb(pi*x)*cos(k*pi*x) interpolation. +* AST__UINTERP +* Use general user-defined sub-pixel interpolation algorithm. +* AST__UKERN1 +* Use user-defined 1-d interpolation kernel. +* AST__URESAMP1, 2, 3 & 4 +* Flags reserved for user-defined purposes. +* AST__USEBAD +* Recognise bad pixels? +* AST__CONSERVEFLUX +* Conserve flux in astResample? +* AST__REBININIT +* Initialise a new sequnece of calls to astRebinSeq +* AST__REBINEND +* End a sequnece of calls to astRebinSeq +* AST__NOBAD +* Leave bad output pixels unchanged in calls to astResample +* AST__USEVAR +* Use variance arrays? + +* Type Definitions: +* Public: +* AstMapping +* Mapping object type. +* +* Protected: +* AstMappingVtab +* Mapping virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* MBT: Mark Taylor (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 30-JAN-1996 (RFWS): +* Original version. +* 12-JUL-1996 (RFWS): +* Updated to support the external interface plus various other +* additions. +* 12-DEC-1996 (RFWS): +* Added the astMapList method. +* 13-DEC-1996 (RFWS): +* Added the astMapMerge method. +* 13-DEC-1996 (RFWS): +* Added the astSimplify method. +* 28-MAY-1998 (RFWS): +* Added the astMapBox method. +* 12-NOV-1998 (RFWS): +* Added astResample and associated code. +* 24-NOV-2000 (MBT): +* Added AST__BLOCKAVE interpolation scheme. +* 9-JAN-2001 (DSB): +* Changed in and out arguments for TranN from type "double (*)[]" +* to "double *". +* 8-JAN-2003 (DSB): +* Added protected astInitMappingVtab method. +* 10-JUL-2003 (DSB): +* Added method astRate. +* 20-SEP-2004 (DSB): +* Added method astLinearApprox. +* 30-JUN-2005 (DSB): +* Added method astRebin +* 1-SEP-2005 (DSB): +* Added method astRebinSeq +* 31-JAN-2006 (DSB): +* Added IsSimple attribute. +* 2-MAR-2006 (DSB): +* Use HAVE_LONG_DOUBLE in place of AST_LONG_DOUBLE +* 8-MAR-2006 (DSB): +* Add astTranGrid. +* 5-MAY-2009 (DSB): +* Add astRemoveRegions. +* 26-FEB-2010 (DSB): +* Added method astQuadApprox. +*-- +*/ + +/* Include files. */ +/* ============== */ + +/* Configuration results */ +/* --------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ + +/* C header files. */ +/* --------------- */ +#include +#include + +/* Macros. */ +/* ======= */ + +/* Sizes of global arrays */ + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST__MAPPING_GETATTRIB_BUFF_LEN 50 +#define AST__MAPPING_RATEFUN_MAX_CACHE 5 + +/* Resampling flags. */ +/* ----------------- */ +/* These macros define flag values which may be passed to + astResample (via the "flags" argument) to provide control over + resampling operations. */ +#define AST__URESAMP1 (1) /* Flags reserved for user-defined purposes */ +#define AST__URESAMP2 (2) +#define AST__URESAMP3 (4) +#define AST__URESAMP4 (8) +#define AST__USEVAR (16) /* Use variance arrays? */ +#define AST__USEBAD (32) /* Recognise bad pixels? */ +#define AST__CONSERVEFLUX (64) /* Conserve flux? */ +#define AST__REBININIT (128) /* Initialise a new sequence of calls to astRebinSeq? */ +#define AST__REBINEND (256) /* End a sequence of calls to astRebinSeq? */ +#define AST__GENVAR (512) /* Generate output variances when rebinning? */ +#define AST__VARWGT (1024) /* Use input variances as weights? */ +#define AST__NOBAD (2048) /* Leave bad output values unchanged? */ +#define AST__DISVAR (4096) /* Generate distribution (not mean) variance? */ +#define AST__NONORM (8192) /* No normalisation required at end? */ + +/* These macros identify standard sub-pixel interpolation algorithms + for use by astResample. They are used by giving the macro's + value for the "interp" argument. */ +#define AST__UKERN1 (1) /* User-supplied 1-d interpolation kernel */ +#if 0 /* Not yet implemented */ +#define AST__UKERNN (2) /* User-supplied n-d interpolation kernel */ +#endif +#define AST__UINTERP (3) /* User-supplied interpolation function */ +#define AST__NEAREST (4) /* Use pixel with nearest centre */ +#define AST__LINEAR (5) /* Simple linear interpolation */ +#define AST__SINC (6) /* sinc(pi*x) interpolation */ +#define AST__SINCSINC (7) /* sinc(pi*x)*sinc(k*pi*x) interpolation */ +#define AST__SINCCOS (8) /* sinc(pi*x)*cos(k*pi*x) interpolation */ +#define AST__SINCGAUSS (9) /* sinc(pi*x)*exp(-k*x*x) interpolation */ +#define AST__BLOCKAVE (10) /* Block averaging interpolation */ +#define AST__GAUSS (11) /* exp(-k*x*x) spreading */ +#define AST__SOMB (12) /* somp(pi*x) interpolation */ +#define AST__SOMBCOS (13) /* somp(pi*x)*cos(k*pi*x) interpolation */ + +/* 64 bit types */ +#if HAVE_INT64_T && HAVE_UINT64_T +#include +typedef int64_t INT_BIG; +typedef uint64_t UINT_BIG; + +#elif SIZEOF_LONG == 8 +typedef long int INT_BIG; +typedef unsigned long int UINT_BIG; + +#elif SIZEOF_LONG_LONG == 8 +typedef long long int INT_BIG; +typedef unsigned long long int UINT_BIG; + +#else +#define INT_BIG "no int64_t type available" +#define UINT_BIG "no uint64_t type available" +#endif + +/* Flags defining the meaning of each bit in the "flags" field of the + Mapping structure. */ +#if defined(astCLASS) /* Protected */ +#define AST__ISSIMPLE_FLAG 1 /* Mapping has been simplified */ +#define AST__FROZEN_FLAG 2 /* Mapping cannot be nominated for simplification */ +#endif + + +/* Type Definitions. */ +/* ================= */ +/* Mapping structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstMapping { + +/* Attributes inherited from the parent class. */ + AstObject object; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + char invert; /* Mapping inverted? */ + char flags; /* Bit-wise flags describing the Mapping */ + int nin; /* Number of input coordinates */ + int nout; /* Number of output coordinates */ + char report; /* Report when converting coordinates? */ + char tran_forward; /* Forward transformation defined? */ + char tran_inverse; /* Inverse transformation defined? */ +} AstMapping; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstMappingVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstObjectVtab object_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstMapping *(* RemoveRegions)( AstMapping *, int * ); + AstMapping *(* Simplify)( AstMapping *, int * ); + AstPointSet *(* Transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + double (* Rate)( AstMapping *, double *, int, int, int * ); + int (* DoNotSimplify)( AstMapping *, int * ); + int (* GetInvert)( AstMapping *, int * ); + int (* GetIsSimple)( AstMapping *, int * ); + int (* GetNin)( AstMapping *, int * ); + int (* GetNout)( AstMapping *, int * ); + int (* GetReport)( AstMapping *, int * ); + int (* GetTranForward)( AstMapping *, int * ); + int (* GetTranInverse)( AstMapping *, int * ); + int (* GetIsLinear)( AstMapping *, int * ); + int (* LinearApprox)( AstMapping *, const double *, const double *, double, double *, int * ); + int (* MapMerge)( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); + int (* QuadApprox)( AstMapping *, const double[2], const double[2], int, int, double *, double *, int * ); + int (* TestInvert)( AstMapping *, int * ); + int (* TestReport)( AstMapping *, int * ); + void (* ClearInvert)( AstMapping *, int * ); + void (* ClearReport)( AstMapping *, int * ); + void (* Decompose)( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); + void (* Invert)( struct AstMapping *, int * ); + void (* MapBox)( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * ); + int (* MapList)( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); + int *(* MapSplit)( AstMapping *, int, const int *, AstMapping **, int * ); + void (* ReportPoints)( AstMapping *, int, AstPointSet *, AstPointSet *, int * ); + void (* SetInvert)( AstMapping *, int, int * ); + void (* SetReport)( AstMapping *, int, int * ); + void (* Tran1)( AstMapping *, int, const double [], int, double [], int * ); + void (* Tran2)( AstMapping *, int, const double [], const double [], int, double [], double [], int * ); + void (* TranGrid)( AstMapping *, int, const int[], const int[], double, int, int, int, int, double *, int * ); + void (* TranN)( AstMapping *, int, int, int, const double *, int, int, int, double *, int * ); + void (* TranP)( AstMapping *, int, int, const double *[], int, int, double *[], int * ); + +#define DECLARE_GENERIC_ALL(X,Xtype) \ + int (* Resample##X)( AstMapping *, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, \ + void (*)( void ), const double [], int, double, int, \ + Xtype, int, const int [], const int [], \ + const int [], const int [], Xtype [], Xtype [], int * ); \ + +DECLARE_GENERIC_ALL(B,signed char) +DECLARE_GENERIC_ALL(D,double) +DECLARE_GENERIC_ALL(F,float) +DECLARE_GENERIC_ALL(I,int) +DECLARE_GENERIC_ALL(K,INT_BIG) +DECLARE_GENERIC_ALL(L,long int) +DECLARE_GENERIC_ALL(S,short int) +DECLARE_GENERIC_ALL(UB,unsigned char) +DECLARE_GENERIC_ALL(UI,unsigned int) +DECLARE_GENERIC_ALL(UK,UINT_BIG) +DECLARE_GENERIC_ALL(UL,unsigned long int) +DECLARE_GENERIC_ALL(US,unsigned short int) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +DECLARE_GENERIC_ALL(LD,long double) +#endif + +#undef DECLARE_GENERIC_ALL + +#define DECLARE_GENERIC_DFI(X,Xtype) \ + void (* Rebin##X)( AstMapping *, double, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, const double [], int, \ + double, int, Xtype, int, const int [], const int [], \ + const int [], const int [], Xtype [], Xtype [], int * ); \ + void (* RebinSeq##X)( AstMapping *, double, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, const double [], \ + int, double, int, Xtype, int, const int [], \ + const int [], const int [], const int [], Xtype [], \ + Xtype [], double [], int64_t *, int * ); + +DECLARE_GENERIC_DFI(D,double) +DECLARE_GENERIC_DFI(F,float) +DECLARE_GENERIC_DFI(I,int) +DECLARE_GENERIC_DFI(B,signed char) +DECLARE_GENERIC_DFI(UB,unsigned char) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +DECLARE_GENERIC_DFI(LD,long double) +#endif + +#undef DECLARE_GENERIC_DFI + +} AstMappingVtab; + + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstMappingGlobals { + AstMappingVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__MAPPING_GETATTRIB_BUFF_LEN + 1 ]; + AstMapping *Unsimplified_Mapping; + int Rate_Disabled; + AstPointSet *RateFun_Pset1_Cache[ AST__MAPPING_RATEFUN_MAX_CACHE ]; + AstPointSet *RateFun_Pset2_Cache[ AST__MAPPING_RATEFUN_MAX_CACHE ]; + int RateFun_Next_Slot; + int RateFun_Pset_Size[ AST__MAPPING_RATEFUN_MAX_CACHE ]; +} AstMappingGlobals; + +#endif +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Mapping) /* Check class membership */ +astPROTO_ISA(Mapping) /* Test class membership */ + +/* NB. There is no constructor function for this class. */ + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstMapping *astInitMapping_( void *, size_t, int, AstMappingVtab *, + const char *, int, int, int, int, int * ); + +/* Vtab initialiser. */ +void astInitMappingVtab_( AstMappingVtab *, const char *, int * ); + +/* Loader. */ +AstMapping *astLoadMapping_( void *, size_t, AstMappingVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitMappingGlobals_( AstMappingGlobals * ); +#endif +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +#define PROTO_GENERIC_ALL(X,Xtype) \ + int astResample##X##_( AstMapping *, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, \ + void (*)( void ), const double [], int, double, int, \ + Xtype, int, const int [], const int [], \ + const int [], const int [], Xtype [], Xtype [], int * ); \ + +PROTO_GENERIC_ALL(B,signed char) +PROTO_GENERIC_ALL(D,double) +PROTO_GENERIC_ALL(F,float) +PROTO_GENERIC_ALL(I,int) +PROTO_GENERIC_ALL(K,INT_BIG) +PROTO_GENERIC_ALL(L,long int) +PROTO_GENERIC_ALL(S,short int) +PROTO_GENERIC_ALL(UB,unsigned char) +PROTO_GENERIC_ALL(UI,unsigned int) +PROTO_GENERIC_ALL(UK,UINT_BIG) +PROTO_GENERIC_ALL(UL,unsigned long int) +PROTO_GENERIC_ALL(US,unsigned short int) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +PROTO_GENERIC_ALL(LD,long double) +#endif + +#undef PROTO_GENERIC_ALL + +#define PROTO_GENERIC_DFI(X,Xtype) \ + void astRebin##X##_( AstMapping *, double, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, const double [], int, \ + double, int, Xtype, int, const int [], const int [], \ + const int [], const int [], Xtype [], Xtype [], int * ); \ + void astRebinSeq##X##_( AstMapping *, double, int, const int [], const int [], \ + const Xtype [], const Xtype [], int, const double [], \ + int, double, int, Xtype, int, const int [], \ + const int [], const int [], const int [], Xtype [], \ + Xtype [], double [], int64_t *, int * ); + +PROTO_GENERIC_DFI(D,double) +PROTO_GENERIC_DFI(F,float) +PROTO_GENERIC_DFI(I,int) +PROTO_GENERIC_DFI(B,signed char) +PROTO_GENERIC_DFI(UB,unsigned char) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +PROTO_GENERIC_DFI(LD,long double) +#endif + +#undef PROTO_GENERIC_DFI + +AstMapping *astRemoveRegions_( AstMapping *, int * ); +AstMapping *astSimplify_( AstMapping *, int * ); +void astInvert_( AstMapping *, int * ); +int astLinearApprox_( AstMapping *, const double *, const double *, double, double *, int * ); +int astQuadApprox_( AstMapping *, const double[2], const double[2], int, int, double *, double *, int * ); +void astTran1_( AstMapping *, int, const double [], int, double [], int * ); +void astTran2_( AstMapping *, int, const double [], const double [], int, double [], double [], int * ); +void astTranGrid_( AstMapping *, int, const int[], const int[], double, int, int, int, int, double *, int * ); +void astTranN_( AstMapping *, int, int, int, const double *, int, int, int, double *, int * ); +void astTranP_( AstMapping *, int, int, const double *[], int, int, double *[], int * ); + +#if defined(astCLASS) /* Protected */ +void astDecompose_( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +void astMapBox_( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * ); +double astRate_( AstMapping *, double *, int, int, int * ); +int *astMapSplit_( AstMapping *, int, const int *, AstMapping **, int * ); +#else +void astDecomposeId_( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +void astMapBoxId_( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * ); +double astRateId_( AstMapping *, double *, int, int, int * ); +void astMapSplitId_( AstMapping *, int, const int *, int *, AstMapping **, int * ); +#endif + +#if defined(astCLASS) /* Protected */ +int astRateState_( int, int * ); +AstPointSet *astTransform_( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +int astGetInvert_( AstMapping *, int * ); +int astGetIsSimple_( AstMapping *, int * ); +int astGetNin_( AstMapping *, int * ); +int astGetNout_( AstMapping *, int * ); +int astGetReport_( AstMapping *, int * ); +int astGetTranForward_( AstMapping *, int * ); +int astGetTranInverse_( AstMapping *, int * ); +int astGetIsLinear_( AstMapping *, int * ); +int astDoNotSimplify_( AstMapping *, int * ); +int astMapMerge_( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +int astTestInvert_( AstMapping *, int * ); +int astTestReport_( AstMapping *, int * ); +void astClearInvert_( AstMapping *, int * ); +void astClearReport_( AstMapping *, int * ); +int astMapList_( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +void astReportPoints_( AstMapping *, int, AstPointSet *, AstPointSet *, int * ); +void astSetInvert_( AstMapping *, int, int * ); +void astSetReport_( AstMapping *, int, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class to make + them easier to invoke (e.g. to avoid type mis-matches when passing pointers + to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them to + validate their own arguments. We must use a cast when passing object + pointers (so that they can accept objects from derived classes). */ + +/* Check class membership. */ +#define astCheckMapping(this) astINVOKE_CHECK(Mapping,this,0) +#define astVerifyMapping(this) astINVOKE_CHECK(Mapping,this,1) + +/* Test class membership. */ +#define astIsAMapping(this) astINVOKE_ISA(Mapping,this) + +/* NB. There is no constructor function for this class. */ + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitMapping(mem,size,init,vtab,name,nin,nout,tran_forward,tran_inverse) \ +astINVOKE(O,astInitMapping_(mem,size,init,vtab,name,nin,nout,tran_forward,tran_inverse,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitMappingVtab(vtab,name) astINVOKE(V,astInitMappingVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadMapping(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadMapping_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to member functions. */ +/* ------------------------------- */ +/* Here we make use of astCheckMapping (et al.) to validate Mapping + pointers before use. This provides a contextual error report if a + pointer to the wrong sort of object is supplied. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define astResampleLD(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleLD_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#endif + +#define astInvert(this) \ +astINVOKE(V,astInvert_(astCheckMapping(this),STATUS_PTR)) +#define astLinearApprox(this,lbnd,ubnd,tol,fit) \ +astINVOKE(V,astLinearApprox_(astCheckMapping(this),lbnd,ubnd,tol,fit,STATUS_PTR)) +#define astQuadApprox(this,lbnd,ubnd,nx,ny,fit,rms) \ +astINVOKE(V,astQuadApprox_(astCheckMapping(this),lbnd,ubnd,nx,ny,fit,rms,STATUS_PTR)) +#define astRebinD(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astRebinD_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astRebinF(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astRebinF_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astRebinI(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astRebinI_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astRebinB(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astRebinB_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astRebinUB(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astRebinUB_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astRebinSeqD(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused) \ +astINVOKE(V,astRebinSeqD_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused,STATUS_PTR)) +#define astRebinSeqF(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused) \ +astINVOKE(V,astRebinSeqF_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused,STATUS_PTR)) +#define astRebinSeqI(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused) \ +astINVOKE(V,astRebinSeqI_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused,STATUS_PTR)) +#define astRebinSeqB(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused) \ +astINVOKE(V,astRebinSeqB_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused,STATUS_PTR)) +#define astRebinSeqUB(this,wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused) \ +astINVOKE(V,astRebinSeqUB_(astCheckMapping(this),wlim,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,weights,nused,STATUS_PTR)) +#define astResampleD(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleD_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleF(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleF_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleL(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleL_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleUL(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleUL_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleI(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleI_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleUI(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleUI_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleK(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleK_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleUK(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleUK_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleS(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleS_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleUS(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleUS_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleB(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleB_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astResampleUB(this,ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var) \ +astINVOKE(V,astResampleUB_(astCheckMapping(this),ndim_in,lbnd_in,ubnd_in,in,in_var,interp,finterp,params,flags,tol,maxpix,badval,ndim_out,lbnd_out,ubnd_out,lbnd,ubnd,out,out_var,STATUS_PTR)) +#define astRemoveRegions(this) astINVOKE(O,astRemoveRegions_(astCheckMapping(this),STATUS_PTR)) +#define astSimplify(this) astINVOKE(O,astSimplify_(astCheckMapping(this),STATUS_PTR)) +#define astTran1(this,npoint,xin,forward,xout) \ +astINVOKE(V,astTran1_(astCheckMapping(this),npoint,xin,forward,xout,STATUS_PTR)) +#define astTran2(this,npoint,xin,yin,forward,xout,yout) \ +astINVOKE(V,astTran2_(astCheckMapping(this),npoint,xin,yin,forward,xout,yout,STATUS_PTR)) +#define astTranGrid(this,ncoord_in,lbnd,ubnd,tol,maxpix,forward,ncoord_out,outdim,out) \ +astINVOKE(V,astTranGrid_(astCheckMapping(this),ncoord_in,lbnd,ubnd,tol,maxpix,forward,ncoord_out,outdim,out,STATUS_PTR)) +#define astTranN(this,npoint,ncoord_in,indim,in,forward,ncoord_out,outdim,out) \ +astINVOKE(V,astTranN_(astCheckMapping(this),npoint,ncoord_in,indim,in,forward,ncoord_out,outdim,out,STATUS_PTR)) +#define astTranP(this,npoint,ncoord_in,ptr_in,forward,ncoord_out,ptr_out) \ +astINVOKE(V,astTranP_(astCheckMapping(this),npoint,ncoord_in,ptr_in,forward,ncoord_out,ptr_out,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astDecompose(this,map1,map2,series,inv1,inv2) \ +astINVOKE(V,astDecompose_(astCheckMapping(this),(AstMapping **)(map1),(AstMapping **)(map2),series,inv1,inv2,STATUS_PTR)) +#define astMapBox(this,lbnd_in,ubnd_in,forward,coord_out,lbnd_out,ubnd_out,xl,xu) \ +astINVOKE(V,astMapBox_(astCheckMapping(this),lbnd_in,ubnd_in,forward,coord_out,lbnd_out,ubnd_out,xl,xu,STATUS_PTR)) +#define astRate(this,at,ax1,ax2) \ +astINVOKE(V,astRate_(astCheckMapping(this),at,ax1,ax2,STATUS_PTR)) +#define astMapSplit(this,nin,in,map) \ +astINVOKE(V,astMapSplit_(this,nin,in,map,STATUS_PTR)) +#else +#define astDecompose(this,map1,map2,series,inv1,inv2) \ +astINVOKE(V,astDecomposeId_(astCheckMapping(this),(AstMapping **)(map1),(AstMapping **)(map2),series,inv1,inv2,STATUS_PTR)) +#define astMapBox(this,lbnd_in,ubnd_in,forward,coord_out,lbnd_out,ubnd_out,xl,xu) \ +astINVOKE(V,astMapBoxId_(astCheckMapping(this),lbnd_in,ubnd_in,forward,coord_out,lbnd_out,ubnd_out,xl,xu,STATUS_PTR)) +#define astRate(this,at,ax1,ax2) \ +astINVOKE(V,astRateId_(astCheckMapping(this),at,ax1,ax2,STATUS_PTR)) +#define astMapSplit(this,nin,in,out,map) \ +astINVOKE(V,astMapSplitId_(astCheckMapping(this),nin,in,out,map,STATUS_PTR)) +#endif + +#if defined(astCLASS) /* Protected */ +#define astRateState(disabled) astRateState_(disabled,STATUS_PTR) +#define astClearInvert(this) \ +astINVOKE(V,astClearInvert_(astCheckMapping(this),STATUS_PTR)) +#define astClearReport(this) \ +astINVOKE(V,astClearReport_(astCheckMapping(this),STATUS_PTR)) +#define astGetInvert(this) \ +astINVOKE(V,astGetInvert_(astCheckMapping(this),STATUS_PTR)) +#define astGetIsSimple(this) \ +astINVOKE(V,astGetIsSimple_(astCheckMapping(this),STATUS_PTR)) +#define astGetNin(this) \ +astINVOKE(V,astGetNin_(astCheckMapping(this),STATUS_PTR)) +#define astGetNout(this) \ +astINVOKE(V,astGetNout_(astCheckMapping(this),STATUS_PTR)) +#define astGetReport(this) \ +astINVOKE(V,astGetReport_(astCheckMapping(this),STATUS_PTR)) +#define astGetTranForward(this) \ +astINVOKE(V,astGetTranForward_(astCheckMapping(this),STATUS_PTR)) +#define astGetTranInverse(this) \ +astINVOKE(V,astGetTranInverse_(astCheckMapping(this),STATUS_PTR)) +#define astGetIsLinear(this) \ +astINVOKE(V,astGetIsLinear_(astCheckMapping(this),STATUS_PTR)) +#define astMapList(this,series,invert,nmap,map_list,invert_list) \ +astINVOKE(V,astMapList_(this,series,invert,nmap,map_list,invert_list,STATUS_PTR)) +#define astMapMerge(this,where,series,nmap,map_list,invert_list) \ +astINVOKE(V,astMapMerge_(astCheckMapping(this),where,series,nmap,map_list,invert_list,STATUS_PTR)) +#define astReportPoints(this,forward,in_points,out_points) \ +astINVOKE(V,astReportPoints_(astCheckMapping(this),forward,astCheckPointSet(in_points),astCheckPointSet(out_points),STATUS_PTR)) +#define astSetInvert(this,value) \ +astINVOKE(V,astSetInvert_(astCheckMapping(this),value,STATUS_PTR)) +#define astSetReport(this,value) \ +astINVOKE(V,astSetReport_(astCheckMapping(this),value,STATUS_PTR)) +#define astTestInvert(this) \ +astINVOKE(V,astTestInvert_(astCheckMapping(this),STATUS_PTR)) +#define astTestReport(this) \ +astINVOKE(V,astTestReport_(astCheckMapping(this),STATUS_PTR)) +#define astDoNotSimplify(this) \ +astINVOKE(V,astDoNotSimplify_(astCheckMapping(this),STATUS_PTR)) + +/* Since a NULL PointSet pointer is acceptable here, we must omit the argument + checking in that case. (But unfortunately, "out" then gets evaluated + twice - this is unlikely to matter, but is there a better way?) */ +#define astTransform(this,in,forward,out) \ +astINVOKE(O,astTransform_(astCheckMapping(this),astCheckPointSet(in),forward,(out)?astCheckPointSet(out):NULL,STATUS_PTR)) +#endif +#endif + + + + + diff --git a/mapping.pdf b/mapping.pdf new file mode 100644 index 0000000..95b5866 Binary files /dev/null and b/mapping.pdf differ diff --git a/mathmap.c b/mathmap.c new file mode 100644 index 0000000..cfcd033 --- /dev/null +++ b/mathmap.c @@ -0,0 +1,7421 @@ +/* +*class++ +* Name: +* MathMap + +* Purpose: +* Transform coordinates using mathematical expressions. + +* Constructor Function: +c astMathMap +f AST_MATHMAP + +* Description: +c A MathMap is a Mapping which allows you to specify a set of forward +c and/or inverse transformation functions using arithmetic operations +c and mathematical functions similar to those available in C. The +c MathMap interprets these functions at run-time, whenever its forward +c or inverse transformation is required. Because the functions are not +c compiled in the normal sense (unlike an IntraMap), they may be used to +c describe coordinate transformations in a transportable manner. A +c MathMap therefore provides a flexible way of defining new types of +c Mapping whose descriptions may be stored as part of a dataset and +c interpreted by other programs. +f A MathMap is a Mapping which allows you to specify a set of forward +f and/or inverse transformation functions using arithmetic operations +f and mathematical functions similar to those available in Fortran. The +f MathMap interprets these functions at run-time, whenever its forward +f or inverse transformation is required. Because the functions are not +f compiled in the normal sense (unlike an IntraMap), they may be used to +f describe coordinate transformations in a transportable manner. A +f MathMap therefore provides a flexible way of defining new types of +f Mapping whose descriptions may be stored as part of a dataset and +f interpreted by other programs. + +* Inheritance: +* The MathMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* MathMap also has the following attributes: +* - Seed: Random number seed +* - SimpFI: Forward-inverse MathMap pairs simplify? +* - SimpIF: Inverse-forward MathMap pairs simplify? + +* Functions: +c The MathMap class does not define any new functions beyond those +f The MathMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 3-SEP-1999 (RFWS): +* Original version. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitMathMapVtab +* method. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 14-MAR-2006 (DSB): +* - Add QIF function. +* - Override astEqual method. +* 20-NOV-2006 (DSB): +* Re-implement the Equal method to avoid use of astSimplify. +* 30-AUG-2012 (DSB): +* Fix bug in undocumented Gaussian noise function. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS MathMap + +/* Allocate pointer array. */ +/* ----------------------- */ +/* This macro allocates an array of pointers. If successful, each element + of the array is initialised to NULL. */ +#define MALLOC_POINTER_ARRAY(array_name,array_type,array_size) \ +\ +/* Allocate the array. */ \ + (array_name) = astMalloc( sizeof(array_type) * (size_t) (array_size) ); \ + if ( astOK ) { \ +\ +/* If successful, loop to initialise each element. */ \ + int array_index_; \ + for ( array_index_ = 0; array_index_ < (array_size); array_index_++ ) { \ + (array_name)[ array_index_ ] = NULL; \ + } \ + } + +/* Free pointer array. */ +/* ------------------- */ +/* This macro frees a dynamically allocated array of pointers, each of + whose elements may point at a further dynamically allocated array + (which is also to be freed). It also allows for the possibility of any + of the pointers being NULL. */ +#define FREE_POINTER_ARRAY(array_name,array_size) \ +\ +/* Check that the main array pointer is not NULL. */ \ + if ( (array_name) ) { \ +\ +/* If OK, loop to free each of the sub-arrays. */ \ + int array_index_; \ + for ( array_index_ = 0; array_index_ < (array_size); array_index_++ ) { \ +\ +/* Check that each sub-array pointer is not NULL before freeing it. */ \ + if ( (array_name)[ array_index_ ] ) { \ + (array_name)[ array_index_ ] = \ + astFree( (array_name)[ array_index_ ] ); \ + } \ + } \ +\ +/* Free the main pointer array. */ \ + (array_name) = astFree( (array_name) ); \ + } + +/* SizeOf pointer array. */ +/* --------------------- */ +/* This macro increments "result" by the number of bytes allocated for an + array of pointers, each of whose elements may point at a further + dynamically allocated array (which is also to be included). It also + allows for the possibility of any of the pointers being NULL. */ +#define SIZEOF_POINTER_ARRAY(array_name,array_size) \ +\ +/* Check that the main array pointer is not NULL. */ \ + if ( (array_name) ) { \ +\ +/* If OK, loop to measure each of the sub-arrays. */ \ + int array_index_; \ + for ( array_index_ = 0; array_index_ < (array_size); array_index_++ ) { \ +\ +/* Check that each sub-array pointer is not NULL before measuring it. */ \ + if ( (array_name)[ array_index_ ] ) { \ + result += astTSizeOf( (array_name)[ array_index_ ] ); \ + } \ + } \ +\ +/* Include the main pointer array. */ \ + result += astTSizeOf( (array_name) ); \ + } + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ +#include "channel.h" /* I/O channels */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "cmpmap.h" /* Compound Mappings */ +#include "mathmap.h" /* Interface definition for this class */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points */ +#include "unitmap.h" /* Unit Mapping */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ +/* This type is made obscure since it is publicly accessible (but not + useful). Provide shorthand for use within this module. */ +typedef AstMathMapRandContext_ Rcontext; + + + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* This declaration enumerates the operation codes recognised by the + EvaluateFunction function which evaluates arithmetic expressions. */ +typedef enum { + +/* User-supplied constants and variables. */ + OP_LDCON, /* Load constant */ + OP_LDVAR, /* Load variable */ + +/* System constants. */ + OP_LDBAD, /* Load bad value (AST__BAD) */ + OP_LDDIG, /* Load # decimal digits (DBL_DIG) */ + OP_LDEPS, /* Load relative precision (DBL_EPSILON) */ + OP_LDMAX, /* Load largest value (DBL_MAX) */ + OP_LDMAX10E, /* Max. decimal exponent (DBL_MAX_10_EXP) */ + OP_LDMAXE, /* Load maximum exponent (DBL_MAX_EXP) */ + OP_LDMDIG, /* Load # mantissa digits (DBL_MANT_DIG) */ + OP_LDMIN, /* Load smallest value (DBL_MIN) */ + OP_LDMIN10E, /* Min. decimal exponent (DBL_MIN_10_EXP) */ + OP_LDMINE, /* Load minimum exponent (DBL_MIN_EXP) */ + OP_LDRAD, /* Load floating radix (FLT_RADIX) */ + OP_LDRND, /* Load rounding mode (FLT_ROUNDS) */ + +/* Mathematical constants. */ + OP_LDE, /* Load e (base of natural logarithms) */ + OP_LDPI, /* Load pi */ + +/* Functions with one argument. */ + OP_ABS, /* Absolute value (sign removal) */ + OP_ACOS, /* Inverse cosine (radians) */ + OP_ACOSD, /* Inverse cosine (degrees) */ + OP_ACOSH, /* Inverse hyperbolic cosine */ + OP_ACOTH, /* Inverse hyperbolic cotangent */ + OP_ACSCH, /* Inverse hyperbolic cosecant */ + OP_ASECH, /* Inverse hyperbolic secant */ + OP_ASIN, /* Inverse sine (radians) */ + OP_ASIND, /* Inverse sine (degrees) */ + OP_ASINH, /* Inverse hyperbolic sine */ + OP_ATAN, /* Inverse tangent (radians) */ + OP_ATAND, /* Inverse tangent (degrees) */ + OP_ATANH, /* Inverse hyperbolic tangent */ + OP_CEIL, /* C ceil function (round up) */ + OP_COS, /* Cosine (radians) */ + OP_COSD, /* Cosine (degrees) */ + OP_COSH, /* Hyperbolic cosine */ + OP_COTH, /* Hyperbolic cotangent */ + OP_CSCH, /* Hyperbolic cosecant */ + OP_EXP, /* Exponential function */ + OP_FLOOR, /* C floor function (round down) */ + OP_INT, /* Integer value (round towards zero) */ + OP_ISBAD, /* Test for bad value */ + OP_LOG, /* Natural logarithm */ + OP_LOG10, /* Base 10 logarithm */ + OP_NINT, /* Fortran NINT function (round to nearest) */ + OP_POISS, /* Poisson random number */ + OP_SECH, /* Hyperbolic secant */ + OP_SIN, /* Sine (radians) */ + OP_SINC, /* Sinc function [= sin(x)/x] */ + OP_SIND, /* Sine (degrees) */ + OP_SINH, /* Hyperbolic sine */ + OP_SQR, /* Square */ + OP_SQRT, /* Square root */ + OP_TAN, /* Tangent (radians) */ + OP_TAND, /* Tangent (degrees) */ + OP_TANH, /* Hyperbolic tangent */ + +/* Functions with two arguments. */ + OP_ATAN2, /* Inverse tangent (2 arguments, radians) */ + OP_ATAN2D, /* Inverse tangent (2 arguments, degrees) */ + OP_DIM, /* Fortran DIM (positive difference) fn. */ + OP_GAUSS, /* Gaussian random number */ + OP_MOD, /* Modulus function */ + OP_POW, /* Raise to power */ + OP_RAND, /* Uniformly distributed random number */ + OP_SIGN, /* Transfer of sign function */ + +/* Functions with three arguments. */ + OP_QIF, /* C "question mark" operator "a?b:c" */ + +/* Functions with variable numbers of arguments. */ + OP_MAX, /* Maximum of 2 or more values */ + OP_MIN, /* Minimum of 2 or more values */ + +/* Unary arithmetic operators. */ + OP_NEG, /* Negate (change sign) */ + +/* Unary boolean operators. */ + OP_NOT, /* Boolean NOT */ + +/* Binary arithmetic operators. */ + OP_ADD, /* Add */ + OP_DIV, /* Divide */ + OP_MUL, /* Multiply */ + OP_SUB, /* Subtract */ + +/* Bit-shift operators. */ + OP_SHFTL, /* Shift bits left */ + OP_SHFTR, /* Shift bits right */ + +/* Relational operators. */ + OP_EQ, /* Relational equal */ + OP_GE, /* Greater than or equal */ + OP_GT, /* Greater than */ + OP_LE, /* Less than or equal */ + OP_LT, /* Less than */ + OP_NE, /* Not equal */ + +/* Bit-wise operators. */ + OP_BITAND, /* Bit-wise AND */ + OP_BITOR, /* Bit-wise OR */ + OP_BITXOR, /* Bit-wise exclusive OR */ + +/* Binary boolean operators. */ + OP_AND, /* Boolean AND */ + OP_EQV, /* Fortran logical .EQV. operation */ + OP_OR, /* Boolean OR */ + OP_XOR, /* Boolean exclusive OR */ + +/* Null operation. */ + OP_NULL /* Null operation */ +} Oper; + +/* This structure holds a description of each symbol which may appear + in an expression. */ +typedef struct { + const char *text; /* Symbol text as it appears in expressions */ + const int size; /* Size of symbol text */ + const int operleft; /* An operator when seen from the left? */ + const int operright; /* An operator when seen from the right? */ + const int unarynext; /* May be followed by a unary +/- ? */ + const int unaryoper; /* Is a unary +/- ? */ + const int leftpriority; /* Priority when seen from the left */ + const int rightpriority; /* Priority when seen from the right */ + const int parincrement; /* Change in parenthesis level */ + const int stackincrement; /* Change in evaluation stack size */ + const int nargs; /* Number of function arguments */ + const Oper opcode; /* Resulting operation code */ +} Symbol; + +/* This initialises an array of Symbol structures to hold data on all + the supported symbols. The order is not important, but symbols are + arranged here in approximate order of descending evaluation + priority. The end of the array is indicated by an element with a NULL + "text" component. */ +static const Symbol symbol[] = { + +/* User-supplied constants and variables. */ + { "" , 0, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDCON }, + { "" , 0, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDVAR }, + +/* System constants. */ + { "" , 5, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDBAD }, + { "" , 5, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDDIG }, + { "" , 9, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDEPS }, + { "" , 10, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDMDIG }, + { "" , 5, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDMAX }, + { "", 12, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDMAX10E }, + { "" , 9, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDMAXE }, + { "" , 5, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDMIN }, + { "", 12, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDMIN10E }, + { "" , 9, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDMINE }, + { "" , 7, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDRAD }, + { "" , 8, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDRND }, + +/* Mathematical constants. */ + { "" , 3, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDE }, + { "" , 4, 0, 0, 0, 0, 19, 19, 0, 1, 0, OP_LDPI }, + +/* Functions with one argument. */ + { "abs(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ABS }, + { "acos(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ACOS }, + { "acosd(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ACOSD }, + { "acosh(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ACOSH }, + { "acoth(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ACOTH }, + { "acsch(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ACSCH }, + { "aint(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_INT }, + { "asech(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ASECH }, + { "asin(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ASIN }, + { "asind(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ASIND }, + { "asinh(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ASINH }, + { "atan(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ATAN }, + { "atand(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ATAND }, + { "atanh(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ATANH }, + { "ceil(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_CEIL }, + { "cos(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_COS }, + { "cosd(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_COSD }, + { "cosh(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_COSH }, + { "coth(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_COTH }, + { "csch(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_CSCH }, + { "exp(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_EXP }, + { "fabs(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ABS }, + { "floor(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_FLOOR }, + { "int(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_INT }, + { "isbad(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_ISBAD }, + { "log(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_LOG }, + { "log10(" , 6, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_LOG10 }, + { "nint(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_NINT }, + { "poisson(" , 8, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_POISS }, + { "sech(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_SECH }, + { "sin(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_SIN }, + { "sinc(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_SINC }, + { "sind(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_SIND }, + { "sinh(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_SINH }, + { "sqr(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_SQR }, + { "sqrt(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_SQRT }, + { "tan(" , 4, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_TAN }, + { "tand(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_TAND }, + { "tanh(" , 5, 0, 1, 1, 0, 19, 1, 1, 0, 1, OP_TANH }, + +/* Functions with two arguments. */ + { "atan2(" , 6, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_ATAN2 }, + { "atan2d(" , 7, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_ATAN2D }, + { "dim(" , 4, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_DIM }, + { "fmod(" , 5, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_MOD }, + { "gauss(" , 6, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_GAUSS }, + { "mod(" , 4, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_MOD }, + { "pow(" , 4, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_POW }, + { "rand(" , 5, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_RAND }, + { "sign(" , 5, 0, 1, 1, 0, 19, 1, 1, -1, 2, OP_SIGN }, + +/* Functions with two arguments. */ + { "qif(" , 4, 0, 1, 1, 0, 19, 1, 1, -2, 3, OP_QIF }, + +/* Functions with variable numbers of arguments. */ + { "max(" , 4, 0, 1, 1, 0, 19, 1, 1, -1, -2, OP_MAX }, + { "min(" , 4, 0, 1, 1, 0, 19, 1, 1, -1, -2, OP_MIN }, + +/* Parenthesised expressions. */ + { ")" , 1, 1, 0, 0, 0, 2, 19, -1, 0, 0, OP_NULL }, + { "(" , 1, 0, 1, 1, 0, 19, 1, 1, 0, 0, OP_NULL }, + +/* Unary arithmetic operators. */ + { "+" , 1, 0, 1, 1, 1, 17, 16, 0, 0, 0, OP_NULL }, + { "-" , 1, 0, 1, 1, 1, 17, 16, 0, 0, 0, OP_NEG }, + +/* Unary boolean operators. */ + { "!" , 1, 0, 1, 1, 0, 17, 16, 0, 0, 0, OP_NOT }, + { ".not." , 5, 0, 1, 1, 0, 17, 16, 0, 0, 0, OP_NOT }, + +/* Binary arithmetic operators. */ + { "**" , 2, 1, 1, 1, 0, 18, 15, 0, -1, 0, OP_POW }, + { "*" , 1, 1, 1, 1, 0, 14, 14, 0, -1, 0, OP_MUL }, + { "/" , 1, 1, 1, 1, 0, 14, 14, 0, -1, 0, OP_DIV }, + { "+" , 1, 1, 1, 1, 0, 13, 13, 0, -1, 0, OP_ADD }, + { "-" , 1, 1, 1, 1, 0, 13, 13, 0, -1, 0, OP_SUB }, + +/* Bit-shift operators. */ + { "<<" , 2, 1, 1, 1, 0, 12, 12, 0, -1, 0, OP_SHFTL }, + { ">>" , 2, 1, 1, 1, 0, 12, 12, 0, -1, 0, OP_SHFTR }, + +/* Relational operators. */ + { "<" , 1, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_LT }, + { ".lt." , 4, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_LT }, + { "<=" , 2, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_LE }, + { ".le." , 4, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_LE }, + { ">" , 1, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_GT }, + { ".gt." , 4, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_GT }, + { ">=" , 2, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_GE }, + { ".ge." , 4, 1, 1, 1, 0, 11, 11, 0, -1, 0, OP_GE }, + { "==" , 2, 1, 1, 1, 0, 10, 10, 0, -1, 0, OP_EQ }, + { ".eq." , 4, 1, 1, 1, 0, 10, 10, 0, -1, 0, OP_EQ }, + { "!=" , 2, 1, 1, 1, 0, 10, 10, 0, -1, 0, OP_NE }, + { ".ne." , 4, 1, 1, 1, 0, 10, 10, 0, -1, 0, OP_NE }, + +/* Bit-wise operators. */ + { "&" , 1, 1, 1, 1, 0, 9, 9, 0, -1, 0, OP_BITAND }, + { "^" , 1, 1, 1, 1, 0, 8, 8, 0, -1, 0, OP_BITXOR }, + { "|" , 1, 1, 1, 1, 0, 7, 7, 0, -1, 0, OP_BITOR }, + +/* Binary boolean operators. */ + { "&&" , 2, 1, 1, 1, 0, 6, 6, 0, -1, 0, OP_AND }, + { ".and." , 5, 1, 1, 1, 0, 6, 6, 0, -1, 0, OP_AND }, + { "^^" , 2, 1, 1, 1, 0, 5, 5, 0, -1, 0, OP_XOR }, + { "||" , 2, 1, 1, 1, 0, 4, 4, 0, -1, 0, OP_OR }, + { ".or." , 4, 1, 1, 1, 0, 4, 4, 0, -1, 0, OP_OR }, + { ".eqv." , 5, 1, 1, 1, 0, 3, 3, 0, -1, 0, OP_EQV }, + { ".neqv." , 6, 1, 1, 1, 0, 3, 3, 0, -1, 0, OP_XOR }, + { ".xor." , 5, 1, 1, 1, 0, 3, 3, 0, -1, 0, OP_XOR }, + +/* Separators. */ + { "," , 1, 1, 1, 1, 0, 2, 2, 0, 0, 0, OP_NULL }, + +/* End of symbol data. */ + { NULL , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, OP_NULL } +}; + +/* These variables identify indices in the above array which hold + special symbols used explicitly in the code. */ +static const int symbol_ldcon = 0; /* Load a constant */ +static const int symbol_ldvar = 1; /* Load a variable */ + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(MathMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(MathMap,Class_Init) +#define class_vtab astGLOBAL(MathMap,Class_Vtab) +#define getattrib_buff astGLOBAL(MathMap,GetAttrib_Buff) + + + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 ); +#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 ); + +static pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX4 pthread_mutex_lock( &mutex4 ); +#define UNLOCK_MUTEX4 pthread_mutex_unlock( &mutex4 ); + +static pthread_mutex_t mutex5 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX5 pthread_mutex_lock( &mutex5 ); +#define UNLOCK_MUTEX5 pthread_mutex_unlock( &mutex5 ); + +static pthread_mutex_t mutex6 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX6 pthread_mutex_lock( &mutex6 ); +#define UNLOCK_MUTEX6 pthread_mutex_unlock( &mutex6 ); + +static pthread_mutex_t mutex7 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX7 pthread_mutex_lock( &mutex7 ); +#define UNLOCK_MUTEX7 pthread_mutex_unlock( &mutex7 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 51 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstMathMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#define LOCK_MUTEX3 +#define UNLOCK_MUTEX3 + +#define LOCK_MUTEX4 +#define UNLOCK_MUTEX4 + +#define LOCK_MUTEX5 +#define UNLOCK_MUTEX5 + +#define LOCK_MUTEX6 +#define UNLOCK_MUTEX6 + +#define LOCK_MUTEX7 +#define UNLOCK_MUTEX7 + +#endif + + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstMathMap *astMathMapId_( int, int, int, const char *[], int, const char *[], const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int GetObjSize( AstObject *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static double Gauss( Rcontext *, int * ); +static double LogGamma( double, int * ); +static double Poisson( Rcontext *, double, int * ); +static double Rand( Rcontext *, int * ); +static int DefaultSeed( const Rcontext *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetSeed( AstMathMap *, int * ); +static int GetSimpFI( AstMathMap *, int * ); +static int GetSimpIF( AstMathMap *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestSeed( AstMathMap *, int * ); +static int TestSimpFI( AstMathMap *, int * ); +static int TestSimpIF( AstMathMap *, int * ); +static void CleanFunctions( int, const char *[], char ***, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearSeed( AstMathMap *, int * ); +static void ClearSimpFI( AstMathMap *, int * ); +static void ClearSimpIF( AstMathMap *, int * ); +static void CompileExpression( const char *, const char *, const char *, int, const char *[], int **, double **, int *, int * ); +static void CompileMapping( const char *, const char *, int, int, int, const char *[], int, const char *[], int ***, int ***, double ***, double ***, int *, int *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void EvaluateFunction( Rcontext *, int, const double **, const int *, const double *, int, double *, int * ); +static void EvaluationSort( const double [], int, int [], int **, int *, int * ); +static void ExtractExpressions( const char *, const char *, int, const char *[], int, char ***, int * ); +static void ExtractVariables( const char *, const char *, int, const char *[], int, int, int, int, int, char ***, int * ); +static void ParseConstant( const char *, const char *, const char *, int, int *, double *, int * ); +static void ParseName( const char *, int, int *, int * ); +static void ParseVariable( const char *, const char *, const char *, int, int, const char *[], int *, int *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetSeed( AstMathMap *, int, int * ); +static void SetSimpFI( AstMathMap *, int, int * ); +static void SetSimpIF( AstMathMap *, int, int * ); +static void ValidateSymbol( const char *, const char *, const char *, int, int, int *, int **, int **, int *, double **, int * ); + +/* Member functions. */ +/* ================= */ +static void CleanFunctions( int nfun, const char *fun[], char ***clean, int *status ) { +/* +* Name: +* CleanFunctions + +* Purpose: +* Make a clean copy of a set of functions. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void CleanFunctions( int nfun, const char *fun[], char ***clean, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function copies an array of strings, eliminating any white space +* characters and converting to lower case. It is intended for cleaning +* up arrays of function definitions prior to compilation. The returned +* copy is stored in dynamically allocated memory. + +* Parameters: +* nfun +* The number of functions to be cleaned. +* fun +* Pointer to an array, with "nfun" elements, of pointers to null +* terminated strings which contain each of the functions. +* clean +* Address in which to return a pointer to an array (with "nfun" +* elements) of pointers to null terminated strings containing the +* cleaned functions (i.e. this returns an array of strings). +* +* Both the returned array of pointers, and the strings to which they +* point, will be dynamically allocated and should be freed by the +* caller (using astFree) when no longer required. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A NULL value will be returned for "*clean" if this function is +* invoked with the global error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + char c; /* Character from function string */ + int i; /* Loop counter for characters */ + int ifun; /* Loop counter for functions */ + int nc; /* Count of non-blank characters */ + +/* Initialise. */ + *clean = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Allocate and initialise an array to hold the returned pointers. */ + MALLOC_POINTER_ARRAY( *clean, char *, nfun ) + +/* Loop through all the input functions. */ + if ( astOK ) { + for ( ifun = 0; ifun < nfun; ifun++ ) { + +/* Count the number of non-blank characters in each function string. */ + nc = 0; + for ( i = 0; ( c = fun[ ifun ][ i ] ); i++ ) nc += !isspace( c ); + +/* Allocate a string long enough to hold the function with all the + white space removed, storing its pointer in the array allocated + earlier. Check for errors. */ + ( *clean )[ ifun ] = astMalloc( sizeof( char ) * + (size_t) ( nc + 1 ) ); + if ( !astOK ) break; + +/* Loop to copy the non-blank function characters into the new + string. */ + nc = 0; + for ( i = 0; ( c = fun[ ifun ][ i ] ); i++ ) { + if ( !isspace( c ) ) ( *clean )[ ifun ][ nc++ ] = tolower( c ); + } + +/* Null-terminate the result. */ + ( *clean )[ ifun ][ nc ] = '\0'; + } + +/* If an error occurred, then free the main pointer array together + with any strings that have been allocated, resetting the output + value. */ + if ( !astOK ) { + FREE_POINTER_ARRAY( *clean, nfun ) + } + } +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a MathMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* MathMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* MathMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the MathMap. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstMathMap *this; /* Pointer to the MathMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the MathMap structure. */ + this = (AstMathMap *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Seed. */ +/* ----- */ + if ( !strcmp( attrib, "seed" ) ) { + astClearSeed( this ); + +/* SimpFI. */ +/* ------- */ + } else if ( !strcmp( attrib, "simpfi" ) ) { + astClearSimpFI( this ); + +/* SimpIF. */ +/* ------- */ + } else if ( !strcmp( attrib, "simpif" ) ) { + astClearSimpIF( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void CompileExpression( const char *method, const char *class, + const char *exprs, int nvar, const char *var[], + int **code, double **con, int *stacksize, int *status ) { +/* +* Name: +* CompileExpression + +* Purpose: +* Compile a mathematical expression. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void CompileExpression( const char *method, const char *class, +* const char *exprs, int nvar, const char *var[], +* int **code, double **con, int *stacksize ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function checks and compiles a mathematical expression. It +* produces a sequence of operation codes (opcodes) and a set of +* numerical constants which may subsequently be used to evaluate the +* expression on a push-down stack. + +* Parameters: +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function. +* This method name is used solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string containing the +* class name of the Object being processed. This name is used solely +* for constructing error messages. +* exprs +* Pointer to a null-terminated string containing the expression +* to be compiled. This is case sensitive and should contain no white +* space. +* nvar +* The number of variable names defined for use in the expression. +* var +* An array of pointers (with "nvar" elements) to null-terminated +* strings. Each of these should contain a variable name which may +* appear in the expression. These strings are case sensitive and +* should contain no white space. +* code +* Address of a pointer which will be set to point at a dynamically +* allocated array of int containing the set of opcodes (cast to int) +* produced by this function. The first element of this array will +* contain a count of the number of opcodes which follow. +* +* The allocated space must be freed by the caller (using astFree) when +* no longer required. +* con +* Address of a pointer which will be set to point at a dynamically +* allocated array of double containing the set of constants +* produced by this function (this may be NULL if no constants are +* produced). +* +* The allocated space must be freed by the caller (using astFree) when +* no longer required. +* stacksize +* Pointer to an int in which to return the size of the push-down stack +* required to evaluate the expression using the returned opcodes and +* constants. + +* Algorithm: +* The function passes through the input expression searching for +* symbols. It looks for standard symbols (arithmetic operators, +* parentheses, function calls and delimiters) in the next part of the +* expression to be parsed, using identification information stored in +* the static "symbol" array. It ignores certain symbols, according to +* whether they appear to be operators or operands. The choice depends on +* what the previous symbol was; for instance, two operators may not +* occur in succession. Unary +/- operators are also ignored in +* situations where they are not permitted. +* +* If a standard symbol is found, it is passed to the ValidateSymbol +* function, which keeps track of the current level of parenthesis in the +* expression and of the number of arguments supplied to any (possibly +* nested) function calls. This function then accepts or rejects the +* symbol according to whether it is valid within the current context. An +* error is reported if it is rejected. +* +* If the part of the expression currently being parsed did not contain a +* standard symbol, an attempt is made to parse it first as a constant, +* then as a variable name. If either of these succeeds, an appropriate +* symbol number is added to the list of symbols identified so far, and a +* value is added to the list of constants - this is either the value of +* the constant itself, or the identification number of the variable. If +* the expression cannot be parsed, an error is reported. +* +* When the entire expression has been analysed as a sequence of symbols +* (and associated constants), the EvaluationSort function is +* invoked. This sorts the symbols into evaluation order, which is the +* order in which the associated operations must be performed on a +* push-down arithmetic stack to evaluate the expression. This routine +* also substitutes operation codes (defined in the "Oper" enum) for the +* symbol numbers and calculates the size of evaluation stack which will +* be required. + +* Notes: +* - A value of NULL will be returned for the "*code" and "*con" pointers +* and a value of zero will be returned for the "*stacksize" value if this +* function is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + double c; /* Value of parsed constant */ + int *argcount; /* Array of argument count information */ + int *opensym; /* Array of opening parenthesis information */ + int *symlist; /* Array of symbol indices */ + int found; /* Standard symbol identified? */ + int iend; /* Ending index in the expression string */ + int istart; /* Staring index in the expression string */ + int isym; /* Loop counter for symbols */ + int ivar; /* Index of variable name */ + int lpar; /* Parenthesis level */ + int ncon; /* Number of constants generated */ + int nsym; /* Number of symbols identified */ + int opernext; /* Next symbol an operator (from left)? */ + int size; /* Size of symbol matched */ + int sym; /* Index of symbol in static "symbol" array */ + int unarynext; /* Next symbol may be unary +/- ? */ + +/* Initialise. */ + *code = NULL; + *con = NULL; + *stacksize = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Further initialisation. */ + argcount = NULL; + lpar = 0; + ncon = 0; + nsym = 0; + opensym = NULL; + symlist = NULL; + sym = 0; + ivar = 0; + +/* The first symbol to be encountered must not look like an operator + from the left. It may be a unary + or - operator. */ + opernext = 0; + unarynext = 1; + +/* Search through the expression to classify each symbol which appears + in it. Stop when there are no more input characters or an error is + detected. */ + istart = 0; + for ( istart = 0; astOK && exprs[ istart ]; istart = iend + 1 ) { + +/* Compare each of the symbols in the symbol data with the next + section of the expression, looking for the longest symbol text which + will match. Stop if a NULL "text" value is found, which acts as the + end flag. */ + found = 0; + size = 0; + for ( isym = 0; symbol[ isym ].text; isym++ ) { + +/* Only consider symbols which have text associated with them and + which look like operators or operands from the left, according to the + setting of the "opernext" flag. Thus, if an operator or operand is + missing from the input expression, the next symbol will not be + identified, because it will be of the wrong type. Also exclude unary + +/- operators if they are out of context. */ + if ( symbol[ isym ].size && + ( symbol[ isym ].operleft == opernext ) && + ( !symbol[ isym ].unaryoper || unarynext ) ) { + +/* Test if the text of the symbol matches the expression at the + current position. If so, note that a match has been found. */ + if ( !strncmp( exprs + istart, symbol[ isym ].text, + (size_t) symbol[ isym ].size ) ) { + found = 1; + +/* If this symbol matches more characters than any previous symbol, + then store the symbol's index and note its size. */ + if ( symbol[ isym ].size > size ) { + sym = isym; + size = symbol[ isym ].size; + +/* Calculate the index of the last symbol character in the expression + string. */ + iend = istart + size - 1; + } + } + } + } + +/* If the symbol was identified as one of the standard symbols, then + validate it, updating the parenthesis level and argument count + information at the same time. */ + if ( found ) { + ValidateSymbol( method, class, exprs, iend, sym, &lpar, &argcount, + &opensym, &ncon, con, status ); + +/* If it was not one of the standard symbols, then check if the next + symbol was expected to be an operator. If so, then there is a missing + operator, so report an error. */ + } else { + if ( opernext ) { + astError( AST__MIOPR, + "%s(%s): Missing or invalid operator in the expression " + "\"%.*s\".", status, + method, class, istart + 1, exprs ); + +/* If the next symbol was expected to be an operand, then it may be a + constant, so try to parse it as one. */ + } else { + ParseConstant( method, class, exprs, istart, &iend, &c, status ); + if ( astOK ) { + +/* If successful, set the symbol number to "symbol_ldcon" (load + constant) and extend the "*con" array to accommodate a new + constant. Check for errors. */ + if ( iend >= istart ) { + sym = symbol_ldcon; + *con = astGrow( *con, ncon + 1, sizeof( double ) ); + if ( astOK ) { + +/* Append the constant to the "*con" array. */ + ( *con )[ ncon++ ] = c; + } + +/* If the symbol did not parse as a constant, then it may be a + variable name, so try to parse it as one. */ + } else { + ParseVariable( method, class, exprs, istart, nvar, var, + &ivar, &iend, status ); + if ( astOK ) { + +/* If successful, set the symbol to "symbol_ldvar" (load variable) and + extend the "*con" array to accommodate a new constant. Check for + errors. */ + if ( ivar != -1 ) { + sym = symbol_ldvar; + *con = astGrow( *con, ncon + 1, sizeof( double ) ); + if ( astOK ) { + +/* Append the variable identification number as a constant to the + "*con" array. */ + ( *con )[ ncon++ ] = (double) ivar; + } + +/* If the expression did not parse as a variable name, then there is a + missing operand in the expression, so report an error. */ + } else { + astError( AST__MIOPA, + "%s(%s): Missing or invalid operand in the " + "expression \"%.*s\".", status, + method, class, istart + 1, exprs ); + } + } + } + } + } + } + +/* If there has been no error, then the next symbol in the input + expression has been identified and is valid. */ + if ( astOK ) { + +/* Decide whether the next symbol should look like an operator or an + operand from the left. This is determined by the nature of the symbol + just identified (seen from the right) - two operands or two operators + cannot be adjacent. */ + opernext = !symbol[ sym ].operright; + +/* Also decide whether the next symbol may be a unary +/- operator, + according to the "unarynext" symbol data entry for the symbol just + identified. */ + unarynext = symbol[ sym ].unarynext; + +/* Extend the "symlist" array to accommodate the symbol just + identified. Check for errors. */ + symlist = astGrow( symlist, nsym + 1, sizeof( int ) ); + if ( astOK ) { + +/* Append the symbol's index to the end of this list. */ + symlist[ nsym++ ] = sym; + } + } + } + +/* If there has been no error, check the final context after + identifying all the symbols... */ + if ( astOK ) { + +/* If an operand is still expected, then there is an unsatisfied + operator on the end of the expression, so report an error. */ + if ( !opernext ) { + astError( AST__MIOPA, + "%s(%s): Missing or invalid operand in the expression " + "\"%s\".", status, + method, class, exprs ); + +/* If the final parenthesis level is positive, then there is a missing + right parenthesis, so report an error. */ + } else if ( lpar > 0 ) { + astError( AST__MRPAR, + "%s(%s): Missing right parenthesis in the expression " + "\"%s\".", status, + method, class, exprs ); + } + } + +/* Sort the symbols into evaluation order to produce output opcodes. */ + EvaluationSort( *con, nsym, symlist, code, stacksize, status ); + +/* Free any memory used as workspace. */ + if ( argcount ) argcount = astFree( argcount ); + if ( opensym ) opensym = astFree( opensym ); + if ( symlist ) symlist = astFree( symlist ); + +/* If OK, re-allocate the "*con" array to have the correct size (since + astGrow may have over-allocated space). */ + if ( astOK && *con ) { + *con = astRealloc( *con, sizeof( double ) * (size_t) ncon ); + } + +/* If an error occurred, free any allocated memory and reset the + output values. */ + if ( !astOK ) { + *code = astFree( *code ); + *con = astFree( *con ); + *stacksize = 0; + } +} + +static void CompileMapping( const char *method, const char *class, + int nin, int nout, + int nfwd, const char *fwdfun[], + int ninv, const char *invfun[], + int ***fwdcode, int ***invcode, + double ***fwdcon, double ***invcon, + int *fwdstack, int *invstack, int *status ) { +/* +* Name: +* CompileMapping + +* Purpose: +* Compile the transformation functions for a MathMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void CompileMapping( const char *method, const char *class, +* int nin, int nout, +* int nfwd, const char *fwdfun[], +* int ninv, const char *invfun[], +* int ***fwdcode, int ***invcode, +* double ***fwdcon, double ***invcon, +* int *fwdstack, int *invstack, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function checks and compiles the transformation functions required +* to create a MathMap. It produces sequences of operation codes (opcodes) +* and numerical constants which may subsequently be used to evaluate the +* functions on a push-down stack. + +* Parameters: +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function. +* This method name is used solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string containing the +* class name of the Object being processed. This name is used solely +* for constructing error messages. +* nin +* Number of input variables for the MathMap. +* nout +* Number of output variables for the MathMap. +* nfwd +* The number of forward transformation functions being supplied. +* This must be at least equal to "nout". +* fwdfun +* Pointer to an array, with "nfwd" elements, of pointers to null +* terminated strings which contain each of the forward transformation +* functions. These must be in lower case and should contain no white +* space. +* ninv +* The number of inverse transformation functions being supplied. +* This must be at least equal to "nin". +* invfun +* Pointer to an array, with "ninv" elements, of pointers to null +* terminated strings which contain each of the inverse transformation +* functions. These must be in lower case and should contain no white +* space. +* fwdcode +* Address in which to return a pointer to an array (with "nfwd" +* elements) of pointers to arrays of int containing the set of opcodes +* (cast to int) for each forward transformation function. The number +* of opcodes produced for each function is given by the first element +* of the opcode array. +* +* Both the returned array of pointers, and the arrays of int to which +* they point, will be stored in dynamically allocated memory and should +* be freed by the caller (using astFree) when no longer required. +* +* If the right hand sides (including the "=" sign) of all the supplied +* functions are absent, then this indicates an undefined transformation +* and the returned pointer value will be NULL. An error results if +* an "=" sign is present but no expression follows it. +* invcode +* Address in which to return a pointer to an array (with "ninv" +* elements) of pointers to arrays of int containing the set of opcodes +* (cast to int) for each inverse transformation function. The number +* of opcodes produced for each function is given by the first element +* of the opcode array. +* +* Both the returned array of pointers, and the arrays of int to which +* they point, will be stored in dynamically allocated memory and should +* be freed by the caller (using astFree) when no longer required. +* +* If the right hand sides (including the "=" sign) of all the supplied +* functions are absent, then this indicates an undefined transformation +* and the returned pointer value will be NULL. An error results if +* an "=" sign is present but no expression follows it. +* fwdcon +* Address in which to return a pointer to an array (with "nfwd" +* elements) of pointers to arrays of double containing the set of +* constants for each forward transformation function. +* +* Both the returned array of pointers, and the arrays of double to which +* they point, will be stored in dynamically allocated memory and should +* be freed by the caller (using astFree) when no longer required. Note +* that any of the pointers to the arrays of double may be NULL if no +* constants are associated with a particular function. +* +* If the forward transformation is undefined, then the returned pointer +* value will be NULL. +* invcon +* Address in which to return a pointer to an array (with "ninv" +* elements) of pointers to arrays of double containing the set of +* constants for each inverse transformation function. +* +* Both the returned array of pointers, and the arrays of double to which +* they point, will be stored in dynamically allocated memory and should +* be freed by the caller (using astFree) when no longer required. Note +* that any of the pointers to the arrays of double may be NULL if no +* constants are associated with a particular function. +* +* If the inverse transformation is undefined, then the returned pointer +* value will be NULL. +* fwdstack +* Pointer to an int in which to return the size of the push-down stack +* required to evaluate the forward transformation functions. +* invstack +* Pointer to an int in which to return the size of the push-down stack +* required to evaluate the inverse transformation functions. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A value of NULL will be returned for the "*fwdcode", "*invcode", +* "*fwdcon" and "*invcon" pointers and a value of zero will be returned +* for the "*fwdstack" and "*invstack" values if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + char **exprs; /* Pointer to array of expressions */ + char **var; /* Pointer to array of variable names */ + const char **strings; /* Pointer to temporary array of strings */ + int ifun; /* Loop counter for functions */ + int nvar; /* Number of variables to extract */ + int stacksize; /* Required stack size */ + +/* Initialise. */ + *fwdcode = NULL; + *invcode = NULL; + *fwdcon = NULL; + *invcon = NULL; + *fwdstack = 0; + *invstack = 0; + nvar = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Further initialisation. */ + exprs = NULL; + var = NULL; + +/* Compile the forward transformation. */ +/* ----------------------------------- */ +/* Allocate space for an array of pointers to the functions from which + we will extract variable names. */ + strings = astMalloc( sizeof( char * ) * (size_t) ( nin + nfwd ) ); + +/* Fill the first elements of this array with pointers to the inverse + transformation functions ("nin" in number) which yield the final input + values. These will have the names of the input variables on their left + hand sides. */ + if ( astOK ) { + nvar = 0; + for ( ifun = ninv - nin; ifun < ninv; ifun++ ) { + strings[ nvar++ ] = invfun[ ifun ]; + } + +/* Fill the remaining elements of the array with pointers to the + forward transformation functions. These will have the names of any + intermediate variables plus the final output variables on their left + hand sides. */ + for ( ifun = 0; ifun < nfwd; ifun++ ) strings[ nvar++ ] = fwdfun[ ifun ]; + +/* Extract the variable names from the left hand sides of these + functions and check them for validity and absence of duplication. */ + ExtractVariables( method, class, nvar, strings, nin, nout, nfwd, ninv, 1, + &var, status ); + } + +/* Free the temporary array of string pointers. */ + strings = astFree( strings ); + +/* Extract the expressions from the right hand sides of the forward + transformation functions. */ + ExtractExpressions( method, class, nfwd, fwdfun, 1, &exprs, status ); + +/* If OK, and the forward transformation is defined, then allocate and + initialise space for an array of pointers to the opcodes for each + expression and, similarly, for the constants for each expression. */ + if ( astOK && exprs ) { + MALLOC_POINTER_ARRAY( *fwdcode, int *, nfwd ) + MALLOC_POINTER_ARRAY( *fwdcon, double *, nfwd ) + +/* If OK, loop to compile each of the expressions, storing pointers to + the resulting opcodes and constants in the arrays allocated above. On + each loop, we make progressively more of the variable names in "var" + visible to the compilation function. This ensures that each expression + can only use variables which have been defined earlier. */ + if ( astOK ) { + for ( ifun = 0; ifun < nfwd; ifun++ ) { + CompileExpression( method, class, exprs[ ifun ], + nin + ifun, (const char **) var, + &( *fwdcode )[ ifun ], &( *fwdcon )[ ifun ], + &stacksize, status ); + +/* If an error occurs, then report contextual information and quit. */ + if ( !astOK ) { + astError( astStatus, + "Error in forward transformation function %d.", status, + ifun + 1 ); + break; + } + +/* If OK, calculate the maximum evaluation stack size required by any + of the expressions. */ + *fwdstack = ( *fwdstack > stacksize ) ? *fwdstack : stacksize; + } + } + } + +/* Free the memory containing the extracted expressions and variables. */ + FREE_POINTER_ARRAY( exprs, nfwd ) + FREE_POINTER_ARRAY( var, nvar ) + +/* Compile the inverse transformation. */ +/* ----------------------------------- */ +/* Allocate space for an array of pointers to the functions from which + we will extract variable names. */ + strings = astMalloc( sizeof( char * ) * (size_t) ( nout + ninv ) ); + +/* Fill the first elements of this array with pointers to the forward + transformation functions ("nout" in number) which yield the final + output values. These will have the names of the output variables on + their left hand sides. */ + if ( astOK ) { + nvar = 0; + for ( ifun = nfwd - nout; ifun < nfwd; ifun++ ) { + strings[ nvar++ ] = fwdfun[ ifun ]; + } + +/* Fill the remaining elements of the array with pointers to the + inverse transformation functions. These will have the names of any + intermediate variables plus the final input variables on their left + hand sides. */ + for ( ifun = 0; ifun < ninv; ifun++ ) strings[ nvar++ ] = invfun[ ifun ]; + +/* Extract the variable names from the left hand sides of these + functions and check them for validity and absence of duplication. */ + ExtractVariables( method, class, nvar, strings, nin, nout, nfwd, ninv, 0, + &var, status ); + } + +/* Free the temporary array of string pointers. */ + strings = astFree( strings ); + +/* Extract the expressions from the right hand sides of the inverse + transformation functions. */ + ExtractExpressions( method, class, ninv, invfun, 0, &exprs, status ); + +/* If OK, and the forward transformation is defined, then allocate and + initialise space for an array of pointers to the opcodes for each + expression and, similarly, for the constants for each expression. */ + if ( astOK && exprs ) { + MALLOC_POINTER_ARRAY( *invcode, int *, ninv ) + MALLOC_POINTER_ARRAY( *invcon, double *, ninv ) + +/* If OK, loop to compile each of the expressions, storing pointers to + the resulting opcodes and constants in the arrays allocated above. On + each loop, we make progressively more of the variable names in "var" + visible to the compilation function. This ensures that each expression + can only use variables which have been defined earlier. */ + if ( astOK ) { + for ( ifun = 0; ifun < ninv; ifun++ ) { + CompileExpression( method, class, exprs[ ifun ], + nout + ifun, (const char **) var, + &( *invcode )[ ifun ], &( *invcon )[ ifun ], + &stacksize, status ); + +/* If an error occurs, then report contextual information and quit. */ + if ( !astOK ) { + astError( astStatus, + "Error in inverse transformation function %d.", status, + ifun + 1 ); + break; + } + +/* If OK, calculate the maximum evaluation stack size required by any + of the expressions. */ + *invstack = ( *invstack > stacksize ) ? *invstack : stacksize; + } + } + } + +/* Free the memory containing the extracted expressions and variables. */ + FREE_POINTER_ARRAY( exprs, ninv ) + FREE_POINTER_ARRAY( var, nvar ) + +/* If an error occurred, then free all remaining allocated memory and + reset the output values. */ + if ( !astOK ) { + FREE_POINTER_ARRAY( *fwdcode, nfwd ) + FREE_POINTER_ARRAY( *invcode, ninv ) + FREE_POINTER_ARRAY( *fwdcon, nfwd ) + FREE_POINTER_ARRAY( *invcon, ninv ) + *fwdstack = 0; + *invstack = 0; + } +} + +static int DefaultSeed( const Rcontext *context, int *status ) { +/* +* Name: +* DefaultSeed + +* Purpose: +* Generate an unpredictable seed for a random number generator. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* int DefaultSeed( Rcontext *context, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* On each invocation this function returns an integer value which is +* highly unpredictable. This value may be used as a default seed for the +* random number generator associated with a MathMap, so that it +* generates a different sequence on each occasion. + +* Parameters: +* context +* Pointer to the random number generator context associated with +* the MathMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The unpredictable integer. + +* Notes: +* - This function does not perform error checking and will execute even +* if the global error status is set. +*/ + +/* Local Constants: */ + const int nwarm = 5; /* Number of warm-up iterations */ + const long int a = 8121L; /* Constants for random number generator... */ + const long int c = 28411L; + const long int m = 134456L; + +/* Local Variables; */ + int iwarm; /* Loop counter for warm-up iterations */ + static long init = 0; /* Local initialisation performed? */ + static long int rand; /* Local random integer */ + unsigned long int bits; /* Bit pattern for producing result */ + +/* On the first invocation, initialise a local random number generator + to a value derived by combining bit patterns obtained from the system + clock and the processor time used. The result needs to be positive and + lie in the range 0 to "m-1". */ + LOCK_MUTEX5 + if ( !init ) { + rand = (long int) ( ( (unsigned long int) time( NULL ) ^ + (unsigned long int) clock() ) % + (unsigned long int) m ); + +/* These values will typically only change in their least significant + bits between programs run successively, but by using the bit pattern + as a seed, we ensure that these differences are rapidly propagated to + other bits. To hasten this process, we "warm up" the local generator + with a few iterations. This is a quick and dirty generator using + constants from Press et al. (Numerical recipes). */ + for ( iwarm = 0; iwarm < nwarm; iwarm++ ) { + rand = ( rand * a + c ) % m; + } + +/* Note that this initialisation has been performed. */ + init = 1; + } + UNLOCK_MUTEX5 + +/* Generate a new bit pattern from the system time. Apart from the + first invocation, this will be a different time to that used above. */ + bits = (unsigned long int) time( NULL ); + +/* Mask in a pattern derived from the CPU time used. */ + bits ^= (unsigned long int) clock(); + +/* The system time may change quite slowly (e.g. every second), so + also mask in the address of the random number generator context + supplied. This makes the seed depend on which MathMap is in use. */ + bits ^= (unsigned long int) context; + +/* Now mask in the last random integer produced by the random number + generator whose context has been supplied. This makes the seed depend + on the MathMap's past use of random numbers. */ + bits ^= (unsigned long int) context->random_int; + +/* Finally, in order to produce different seeds when this function is + invoked twice in rapid succession on the same object (with no + intermediate processing), we also mask in a pseudo-random value + generated here. Generate the next local random integer. */ + rand = ( rand * a + c ) % m; + +/* We then scale this value to give an integer in the range 0 to + ULONG_MAX and mask the corresponding bit pattern into our seed. */ + bits ^= (unsigned long int) ( ( (double) rand / (double) ( m - 1UL ) ) * + ( ( (double) ULONG_MAX + 1.0 ) * + ( 1.0 - DBL_EPSILON ) ) ); + +/* Return the integer value of the seed (which may involve discarding + some unwanted bits). */ + return (int) bits; +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two MathMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* MathMap member function (over-rides the astEqual protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two MathMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a MathMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the MathMaps are equivalent, zero otherwise. + +* Notes: +* - The two MathMaps are considered equivalent if the combination of +* the first in series with the inverse of the second simplifies to a +* UnitMap. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMathMap *that; /* Pointer to the second MathMap structure */ + AstMathMap *this; /* Pointer to the first MathMap structure */ + double **that_con; /* Lists of constants from "that" */ + double **this_con; /* Lists of constants from "this" */ + int **that_code; /* Lists of opcodes from "that" */ + int **this_code; /* Lists of opcodes from "this" */ + int code; /* Opcode value */ + int icode; /* Opcode index */ + int icon; /* Constant index */ + int ifun; /* Function index */ + int ncode; /* No. of opcodes for current "this" function */ + int ncode_that; /* No. of opcodes for current "that" function */ + int nin; /* Number of inputs */ + int nout; /* Number of outputs */ + int pass; /* Check fwd or inv */ + int result; /* Result value to return */ + int that_nfun; /* Number of functions from "that" */ + int this_nfun; /* Number of functions from "this" */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two MathMap structures. */ + this = (AstMathMap *) this_object; + that = (AstMathMap *) that_object; + +/* Check the second object is a MathMap. We know the first is a + MathMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAMathMap( that ) ) { + +/* Check they have the same number of inputs and outputs */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNout( that ) == nout && astGetNin( that ) == nin ) { + +/* Assume equality. */ + result = 1; + +/* The first pass through this next loop compares forward functions, and + the second pass compares inverse functions. */ + for( pass = 0; pass < 2 && result; pass++ ) { + +/* On the first pass, get pointers to the lists of opcodes and constants for + the effective forward transformations (taking into account the value + of the Invert attribute), together with the number of such functions. */ + if( pass == 0 ) { + if( !astGetInvert( this ) ) { + this_code = this->fwdcode; + this_con = this->fwdcon; + this_nfun = this->nfwd; + } else { + this_code = this->invcode; + this_con = this->invcon; + this_nfun = this->ninv; + } + + if( !astGetInvert( that ) ) { + that_code = that->fwdcode; + that_con = that->fwdcon; + that_nfun = that->nfwd; + } else { + that_code = that->invcode; + that_con = that->invcon; + that_nfun = that->ninv; + } + +/* On the second pass, get pointers to the lists of opcodes and constants for + the effective inverse transformations, together with the number of such + functions. */ + } else { + + if( astGetInvert( this ) ) { + this_code = this->fwdcode; + this_con = this->fwdcon; + this_nfun = this->nfwd; + } else { + this_code = this->invcode; + this_con = this->invcon; + this_nfun = this->ninv; + } + + if( astGetInvert( that ) ) { + that_code = that->fwdcode; + that_con = that->fwdcon; + that_nfun = that->nfwd; + } else { + that_code = that->invcode; + that_con = that->invcon; + that_nfun = that->ninv; + } + } + +/* Check that "this" and "that" have the same number of functions */ + if( that_nfun != this_nfun ) result = 0; + +/* Loop round each function. */ + for( ifun = 0; ifun < this_nfun && result; ifun++ ) { + +/* The first element in the opcode array is the number of subsequent + opcodes. Obtain and compare these counts. */ + ncode = this_code ? this_code[ ifun ][ 0 ] : 0; + ncode_that = that_code ? that_code[ ifun ][ 0 ] : 0; + if( ncode != ncode_that ) result = 0; + +/* Compare the following opcodes. Some opcodes consume constants from the + list of constants associated with the MathMap. Compare the constants + for such opcodes. */ + icon = 0; + for( icode = 0; icode < ncode && result; icode++ ){ + code = this_code[ ifun ][ icode ]; + if( that_code[ ifun ][ icode ] != code ) { + result = 0; + + } else if( code == OP_LDCON || + code == OP_LDVAR || + code == OP_MAX || + code == OP_MIN ) { + + if( this_con[ ifun ][ icon ] != + that_con[ ifun ][ icon ] ) { + result = 0; + } else { + icon++; + } + } + } + } + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void EvaluateFunction( Rcontext *rcontext, int npoint, + const double **ptr_in, const int *code, + const double *con, int stacksize, double *out, int *status ) { +/* +* Name: +* EvaluateFunction + +* Purpose: +* Evaluate a compiled function. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void EvaluateFunction( Rcontext *rcontext, int npoint, +* const double **ptr_in, const int *code, +* const double *con, int stacksize, double *out, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function implements a "virtual machine" which executes operations +* on an arithmetic stack in order to evaluate transformation functions. +* Each operation is specified by an input operation code (opcode) and +* results in the execution of a vector operation on a stack. The final +* result, after executing all the supplied opcodes, is returned as a +* vector. +* +* This function detects arithmetic errors (such as overflow and division +* by zero) and propagates any "bad" coordinate values, including those +* present in the input, to the output. + +* Parameters: +* npoint +* The number of points to be transformd (i.e. the size of the vector +* of values on which operations are to be performed). +* ptr_in +* Pointer to an array of pointers to arrays of double (with "npoint" +* elements). These arrays should contain the input coordinate values, +* such that coordinate number "coord" for point number "point" can be +* found in "ptr_in[coord][point]". +* code +* Pointer to an array of int containing the set of opcodes (cast to int) +* for the operations to be performed. The first element of this array +* should contain a count of the number of opcodes which follow. +* con +* Pointer to an array of double containing the set of constants required +* to evaluate the function (this may be NULL if no constants are +* required). +* stacksize +* The size of the stack required to evaluate the expression using the +* opcodes and constants supplied. This value should be calculated during +* expression compilation. +* out +* Pointer to an array of double (with "npoint" elements) in which to +* return the vector of result values. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ + const int bits = /* Number of bits in an unsigned long */ + sizeof( unsigned long ) * CHAR_BIT; + const double eps = /* Smallest number subtractable from 2.0 */ + 2.0 * DBL_EPSILON; + const double scale = /* 2.0 raised to the power "bits" */ + ldexp( 1.0, bits ); + const double scale1 = /* 2.0 raised to the power "bits-1" */ + scale * 0.5; + const double rscale = /* Reciprocal scale factor */ + 1.0 / scale; + const double rscale1 = /* Reciprocal initial scale factor */ + 1.0 / scale1; + const int nblock = /* Number of blocks of bits to process */ + ( sizeof( double ) + sizeof( unsigned long ) - 1 ) / + sizeof( unsigned long ); + const unsigned long signbit = /* Mask for extracting sign bit */ + 1UL << ( bits - 1 ); + +/* Local Variables: */ + double **stack; /* Array of pointers to stack elements */ + double *work; /* Pointer to stack workspace */ + double *xv1; /* Pointer to first argument vector */ + double *xv2; /* Pointer to second argument vector */ + double *xv3; /* Pointer to third argument vector */ + double *xv; /* Pointer to sole argument vector */ + double *y; /* Pointer to result */ + double *yv; /* Pointer to result vector */ + double abs1; /* Absolute value (temporary variable) */ + double abs2; /* Absolute value (temporary variable) */ + double frac1; /* First (maybe normalised) fraction */ + double frac2; /* Second (maybe normalised) fraction */ + double frac; /* Sole normalised fraction */ + double newexp; /* New power of 2 exponent value */ + double ran; /* Random number */ + double result; /* Function result value */ + double unscale; /* Factor for removing scaling */ + double value; /* Value to be assigned to stack vector */ + double x1; /* First argument value */ + double x2; /* Second argument value */ + double x3; /* Third argument value */ + double x; /* Sole argument value */ + int expon1; /* First power of 2 exponent */ + int expon2; /* Second power of 2 exponent */ + int expon; /* Sole power of 2 exponent */ + int iarg; /* Loop counter for arguments */ + int iblock; /* Loop counter for blocks of bits */ + int icode; /* Opcode value */ + int icon; /* Counter for number of constants used */ + int istk; /* Loop counter for stack elements */ + int ivar; /* Input variable number */ + int narg; /* Number of function arguments */ + int ncode; /* Number of opcodes to process */ + int point; /* Loop counter for stack vector elements */ + int sign; /* Argument is non-negative? */ + int tos; /* Top of stack index */ + static double d2r; /* Degrees to radians conversion factor */ + static double log2; /* Natural logarithm of 2.0 */ + static double pi; /* Value of PI */ + static double r2d; /* Radians to degrees conversion factor */ + static double rsafe_sq; /* Reciprocal of "safe_sq" */ + static double safe_sq; /* Huge value that can safely be squared */ + static int init = 0; /* Initialisation performed? */ + unsigned long b1; /* Block of bits from first argument */ + unsigned long b2; /* Block of bits from second argument */ + unsigned long b; /* Block of bits for result */ + unsigned long neg; /* Result is negative? (sign bit) */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If this is the first invocation of this function, then initialise + constant values. */ + LOCK_MUTEX2 + if ( !init ) { + +/* Trigonometrical conversion factors. */ + pi = acos( -1.0 ); + r2d = 180.0 / pi; + d2r = pi / 180.0; + +/* Natural logarithm of 2.0. */ + log2 = log( 2.0 ); + +/* This value must be safe to square without producing overflow, yet + large enough that adding or subtracting 1.0 from the square makes no + difference. We also need its reciprocal. */ + safe_sq = 0.9 * sqrt( DBL_MAX ); + rsafe_sq = 1.0 / safe_sq; + +/* Note that initialisation has been performed. */ + init = 1; + } + UNLOCK_MUTEX2 + +/* Allocate space for an array of pointers to elements of the + workspace stack (each stack element being an array of double). */ + stack = astMalloc( sizeof( double * ) * (size_t) stacksize ); + +/* Allocate space for the stack itself. */ + work = astMalloc( sizeof( double ) * + (size_t) ( npoint * ( stacksize - 1 ) ) ); + +/* If OK, then initialise the stack pointer array to identify the + start of each vector on the stack. The first element points at the + output array (in which the result will be accumulated), while other + elements point at successive vectors within the workspace allocated + above. */ + if ( astOK ) { + stack[ 0 ] = out; + for ( istk = 1; istk < stacksize; istk++ ) { + stack[ istk ] = work + ( istk - 1 ) * npoint; + } + +/* Define stack operations. */ +/* ======================== */ +/* We now define a set of macros for performing vector operations on + elements of the stack. Each is in the form of a "case" block for + execution in response to the appropriate operation code (opcode). */ + +/* Zero-argument operation. */ +/* ------------------------ */ +/* This macro performs a zero-argument operation, which results in the + insertion of a new vector on to the stack. */ +#define ARG_0(oper,setup,function) \ +\ +/* Test for the required opcode value. */ \ + case oper: \ +\ +/* Perform any required initialisation. */ \ + {setup;} \ +\ +/* Increment the top of stack index and obtain a pointer to the new stack \ + element (vector). */ \ + yv = stack[ ++tos ]; \ +\ +/* Loop to access each vector element, obtaining a pointer to it. */ \ + for ( point = 0; point < npoint; point++ ) { \ + y = yv + point; \ +\ +/* Perform the processing, which results in assignment to this element. */ \ + {function;} \ + } \ +\ +/* Break out of the "case" block. */ \ + break; + +/* One-argument operation. */ +/* ----------------------- */ +/* This macro performs a one-argument operation, which processes the + top stack element without changing the stack size. */ +#define ARG_1(oper,function) \ +\ +/* Test for the required opcode value. */ \ + case oper: \ +\ +/* Obtain a pointer to the top stack element (vector). */ \ + xv = stack[ tos ]; \ +\ +/* Loop to access each vector element, obtaining its value and \ + checking that it is not bad. */ \ + for ( point = 0; point < npoint; point++ ) { \ + if ( ( x = xv[ point ] ) != AST__BAD ) { \ +\ +/* Also obtain a pointer to the element. */ \ + y = xv + point; \ +\ +/* Perform the processing, which uses the element's value and then \ + assigns the result to this element. */ \ + {function;} \ + } \ + } \ +\ +/* Break out of the "case" block. */ \ + break; + +/* One-argument boolean operation. */ +/* ------------------------------- */ +/* This macro is similar in function to ARG_1 above, except that no + checks are made for bad argument values. It is intended for use with + boolean functions where bad values are handled explicitly. */ +#define ARG_1B(oper,function) \ +\ +/* Test for the required opcode value. */ \ + case oper: \ +\ +/* Obtain a pointer to the top stack element (vector). */ \ + xv = stack[ tos ]; \ +\ +/* Loop to access each vector element, obtaining the argument value \ + and a pointer to the element. */ \ + for ( point = 0; point < npoint; point++ ) { \ + x = xv[ point ]; \ + y = xv + point; \ +\ +/* Perform the processing, which uses the element's value and then \ + assigns the result to this element. */ \ + {function;} \ + } \ +\ +/* Break out of the "case" block. */ \ + break; + +/* Two-argument operation. */ +/* ----------------------- */ +/* This macro performs a two-argument operation, which processes the + top two stack elements and produces a single result, resulting in the + stack size decreasing by one. In this case, we first define a macro + without the "case" block statements present. */ +#define DO_ARG_2(function) \ +\ +/* Obtain pointers to the top two stack elements (vectors), decreasing \ + the top of stack index by one. */ \ + xv2 = stack[ tos-- ]; \ + xv1 = stack[ tos ]; \ +\ +/* Loop to access each vector element, obtaining the value of the \ + first argument and checking that it is not bad. */ \ + for ( point = 0; point < npoint; point++ ) { \ + if ( ( x1 = xv1[ point ] ) != AST__BAD ) { \ +\ +/* Also obtain a pointer to the element which is to receive the \ + result. */ \ + y = xv1 + point; \ +\ +/* Obtain the value of the second argument, again checking that it is \ + not bad. */ \ + if ( ( x2 = xv2[ point ] ) != AST__BAD ) { \ +\ +/* Perform the processing, which uses the two argument values and then \ + assigns the result to the appropriate top of stack element. */ \ + {function;} \ +\ +/* If the second argument was bad, so is the result. */ \ + } else { \ + *y = AST__BAD; \ + } \ + } \ + } + +/* This macro simply wraps the one above up in a "case" block. */ +#define ARG_2(oper,function) \ + case oper: \ + DO_ARG_2(function) \ + break; + +/* Two-argument boolean operation. */ +/* ------------------------------- */ +/* This macro is similar in function to ARG_2 above, except that no + checks are made for bad argument values. It is intended for use with + boolean functions where bad values are handled explicitly. */ +#define ARG_2B(oper,function) \ +\ +/* Test for the required opcode value. */ \ + case oper: \ +\ +/* Obtain pointers to the top two stack elements (vectors), decreasing \ + the top of stack index by one. */ \ + xv2 = stack[ tos-- ]; \ + xv1 = stack[ tos ]; \ +\ +/* Loop to access each vector element, obtaining the value of both \ + arguments and a pointer to the element which is to receive the \ + result. */ \ + for ( point = 0; point < npoint; point++ ) { \ + x1 = xv1[ point ]; \ + x2 = xv2[ point ]; \ + y = xv1 + point; \ +\ +/* Perform the processing, which uses the two argument values and then \ + assigns the result to the appropriate top of stack element. */ \ + {function;} \ + } \ +\ +/* Break out of the "case" block. */ \ + break; + +/* Three-argument boolean operation. */ +/* --------------------------------- */ +/* This macro is similar in function to ARG_2B above, except that it + takes three values of the stack and puts one back. It performs no + checks for bad values. */ +#define ARG_3B(oper,function) \ +\ +/* Test for the required opcode value. */ \ + case oper: \ +\ +/* Obtain pointers to the top three stack elements (vectors), decreasing \ + the top of stack index by two. */ \ + xv3 = stack[ tos-- ]; \ + xv2 = stack[ tos-- ]; \ + xv1 = stack[ tos ]; \ +\ +/* Loop to access each vector element, obtaining the value of all 3 \ + arguments and a pointer to the element which is to receive the \ + result. */ \ + for ( point = 0; point < npoint; point++ ) { \ + x1 = xv1[ point ]; \ + x2 = xv2[ point ]; \ + x3 = xv3[ point ]; \ + y = xv1 + point; \ +\ +/* Perform the processing, which uses the three argument values and then \ + assigns the result to the appropriate top of stack element. */ \ + {function;} \ + } \ +\ +/* Break out of the "case" block. */ \ + break; + +/* Define arithmetic operations. */ +/* ============================= */ +/* We now define macros for performing some of the arithmetic + operations we will require in a "safe" way - i.e. trapping numerical + problems such as overflow and invalid arguments and translating them + into the AST__BAD value. */ + +/* Absolute value. */ +/* --------------- */ +/* This is just shorthand. */ +#define ABS(x) ( ( (x) >= 0.0 ) ? (x) : -(x) ) + +/* Integer part. */ +/* ------------- */ +/* This implements rounding towards zero without involving conversion + to an integer (which could overflow). */ +#define INT(x) ( ( (x) >= 0.0 ) ? floor( (x) ) : ceil( (x) ) ) + +/* Trap maths overflow. */ +/* -------------------- */ +/* This macro calls a C maths library function and checks for overflow + in the result. */ +#define CATCH_MATHS_OVERFLOW(function) \ + ( \ +\ +/* Clear the "errno" value. */ \ + errno = 0, \ +\ +/* Evaluate the function. */ \ + result = (function), \ +\ +/* Check if "errno" and the returned result indicate overflow and \ + return the appropriate result. */ \ + ( ( errno == ERANGE ) && ( ABS( result ) == HUGE_VAL ) ) ? AST__BAD : \ + result \ + ) + +/* Trap maths errors. */ +/* ------------------ */ +/* This macro is similar to the one above, except that it also checks + for domain errors (i.e. invalid argument values). */ +#define CATCH_MATHS_ERROR(function) \ + ( \ +\ +/* Clear the "errno" value. */ \ + errno = 0, \ +\ +/* Evaluate the function. */ \ + result = (function), \ +\ +/* Check if "errno" and the returned result indicate a domain error or \ + overflow and return the appropriate result. */ \ + ( ( errno == EDOM ) || \ + ( ( errno == ERANGE ) && ( ABS( result ) == HUGE_VAL ) ) ) ? \ + AST__BAD : result \ + ) + +/* Tri-state boolean OR. */ +/* --------------------- */ +/* This evaluates a boolean OR using tri-state logic. For example, + "a||b" may evaluate to 1 if "a" is bad but "b" is non-zero, so that + the normal rules of bad value propagation do not apply. */ +#define TRISTATE_OR(x1,x2) \ +\ +/* Test if the first argument is bad. */ \ + ( (x1) == AST__BAD ) ? ( \ +\ +/* If so, test the second argument. */ \ + ( ( (x2) == 0.0 ) || ( (x2) == AST__BAD ) ) ? AST__BAD : 1.0 \ + ) : ( \ +\ +/* Test if the second argument is bad. */ \ + ( (x2) == AST__BAD ) ? ( \ +\ +/* If so, test the first argument. */ \ + ( (x1) == 0.0 ) ? AST__BAD : 1.0 \ +\ +/* If neither argument is bad, use the normal OR operator. */ \ + ) : ( \ + ( (x1) != 0.0 ) || ( (x2) != 0.0 ) \ + ) \ + ) + +/* Tri-state boolean AND. */ +/* ---------------------- */ +/* This evaluates a boolean AND using tri-state logic. */ +#define TRISTATE_AND(x1,x2) \ +\ +/* Test if the first argument is bad. */ \ + ( (x1) == AST__BAD ) ? ( \ +\ +/* If so, test the second argument. */ \ + ( (x2) != 0.0 ) ? AST__BAD : 0.0 \ + ) : ( \ +\ +/* Test if the second argument is bad. */ \ + ( (x2) == AST__BAD ) ? ( \ +\ +/* If so, test the first argument. */ \ + ( (x1) != 0.0 ) ? AST__BAD : 0.0 \ +\ +/* If neither argument is bad, use the normal AND operator. */ \ + ) : ( \ + ( (x1) != 0.0 ) && ( (x2) != 0.0 ) \ + ) \ + ) + +/* Safe addition. */ +/* -------------- */ +/* This macro performs addition while avoiding possible overflow. */ +#define SAFE_ADD(x1,x2) ( \ +\ +/* Test if the first argument is non-negative. */ \ + ( (x1) >= 0.0 ) ? ( \ +\ +/* If so, then we can perform addition if the second argument is \ + non-positive. Otherwise, we must calculate the most positive safe \ + second argument value that can be added and test for this (the test \ + itself is safe against overflow). */ \ + ( ( (x2) <= 0.0 ) || ( ( (DBL_MAX) - (x1) ) >= (x2) ) ) ? ( \ +\ +/* Perform addition if it is safe, otherwise return AST__BAD. */ \ + (x1) + (x2) \ + ) : ( \ + AST__BAD \ + ) \ +\ +/* If the first argument is negative, then we can perform addition if \ + the second argument is non-negative. Otherwise, we must calculate the \ + most negative second argument value that can be added and test for \ + this (the test itself is safe against overflow). */ \ + ) : ( \ + ( ( (x2) >= 0.0 ) || ( ( (DBL_MAX) + (x1) ) >= -(x2) ) ) ? ( \ +\ +/* Perform addition if it is safe, otherwise return AST__BAD. */ \ + (x1) + (x2) \ + ) : ( \ + AST__BAD \ + ) \ + ) \ +) + +/* Safe subtraction. */ +/* ----------------- */ +/* This macro performs subtraction while avoiding possible overflow. */ +#define SAFE_SUB(x1,x2) ( \ +\ +/* Test if the first argument is non-negative. */ \ + ( (x1) >= 0.0 ) ? ( \ +\ +/* If so, then we can perform subtraction if the second argument is \ + also non-negative. Otherwise, we must calculate the most negative safe \ + second argument value that can be subtracted and test for this (the \ + test itself is safe against overflow). */ \ + ( ( (x2) >= 0.0 ) || ( ( (DBL_MAX) - (x1) ) >= -(x2) ) ) ? ( \ +\ +/* Perform subtraction if it is safe, otherwise return AST__BAD. */ \ + (x1) - (x2) \ + ) : ( \ + AST__BAD \ + ) \ +\ +/* If the first argument is negative, then we can perform subtraction \ + if the second argument is non-positive. Otherwise, we must calculate \ + the most positive second argument value that can be subtracted and \ + test for this (the test itself is safe against overflow). */ \ + ) : ( \ + ( ( (x2) <= 0.0 ) || ( ( (DBL_MAX) + (x1) ) >= (x2) ) ) ? ( \ +\ +/* Perform subtraction if it is safe, otherwise return AST__BAD. */ \ + (x1) - (x2) \ + ) : ( \ + AST__BAD \ + ) \ + ) \ +) + +/* Safe multiplication. */ +/* -------------------- */ +/* This macro performs multiplication while avoiding possible overflow. */ +#define SAFE_MUL(x1,x2) ( \ +\ +/* Multiplication is safe if the absolute value of either argument is \ + unity or less. Otherwise, we must use the first argument to calculate \ + the maximum absolute value that the second argument may have and test \ + for this (the test itself is safe against overflow). */ \ + ( ( ( abs1 = ABS( (x1) ) ) <= 1.0 ) || \ + ( ( abs2 = ABS( (x2) ) ) <= 1.0 ) || \ + ( ( (DBL_MAX) / abs1 ) >= abs2 ) ) ? ( \ +\ +/* Perform multiplication if it is safe, otherwise return AST__BAD. */ \ + (x1) * (x2) \ + ) : ( \ + AST__BAD \ + ) \ +) + +/* Safe division. */ +/* -------------- */ +/* This macro performs division while avoiding possible overflow. */ +#define SAFE_DIV(x1,x2) ( \ +\ +/* Division is unsafe if the second argument is zero. Otherwise, it is \ + safe if the abolute value of the second argument is unity or \ + more. Otherwise, we must use the second argument to calculate the \ + maximum absolute value that the first argument may have and test for \ + this (the test itself is safe against overflow). */ \ + ( ( (x2) != 0.0 ) && \ + ( ( ( abs2 = ABS( (x2) ) ) >= 1.0 ) || \ + ( ( (DBL_MAX) * abs2 ) >= ABS( (x1) ) ) ) ) ? ( \ +\ +/* Perform division if it is safe, otherwise return AST__BAD. */ \ + (x1) / (x2) \ + ) : ( \ + AST__BAD \ + ) \ +) + +/* Bit-shift operation. */ +/* -------------------- */ +/* This macro shifts the bits in a double value a specified number of + places to the left, which simply corresponds to multiplying by the + appropriate power of two. */ +#define SHIFT_BITS(x1,x2) ( \ +\ +/* Decompose the value into a normalised fraction and a power of 2. */ \ + frac = frexp( (x1), &expon ), \ +\ +/* Calculate the new power of 2 which should apply after the shift, \ + rounding towards zero to give an integer value. */ \ + newexp = INT( (x2) ) + (double) expon, \ +\ +/* If the new exponent is too negative to convert to an integer, then \ + the result must underflow to zero. */ \ + ( newexp < (double) -INT_MAX ) ? ( \ + 0.0 \ +\ +/* Otherwise, if it is too positive to convert to an integer, then the \ + result must overflow, unless the normalised fraction is zero. */ \ + ) : ( ( newexp > (double) INT_MAX ) ? ( \ + ( frac == 0.0 ) ? 0.0 : AST__BAD \ +\ +/* Otherwise, convert the new exponent to an integer and apply \ + it. Trap any overflow which may still occur. */ \ + ) : ( \ + CATCH_MATHS_OVERFLOW( ldexp( frac, (int) newexp ) ) \ + ) ) \ +) + +/* Two-argument bit-wise boolean operation. */ +/* ---------------------------------------- */ +/* This macro expands to code which performs a bit-wise boolean + operation on a pair of arguments and assigns the result to the + variable "result". It operates on floating point (double) values, + which are regarded as if they are fixed-point binary numbers with + negative values expressed in twos-complement notation. This means that + it delivers the same results for integer values as the normal + (integer) C bit-wise operations. However, it will also operate on the + fraction bits of floating point numbers. It also offers greater + precision (the first 53 or so significant bits of the result being + preserved for typical IEEE floating point implementations). */ +#define BIT_OPER(oper,x1,x2) \ +\ +/* Convert each argument to a normalised fraction in the range \ + [0.5,1.0) and a power of two exponent, removing any sign \ + information. */ \ + frac1 = frexp( ABS( (x1) ), &expon1 ); \ + frac2 = frexp( ABS( (x2) ), &expon2 ); \ +\ +/* Set "expon" to be the larger of the two exponents. If the two \ + exponents are not equal, divide the fraction with the smaller exponent \ + by 2 to the power of the exponent difference. This gives both \ + fractions the same effective exponent (although one of them may no \ + longer be normalised). Note that overflow is avoided because all \ + numbers remain less than 1.0, but underflow may occur. */ \ + expon = expon1; \ + if ( expon2 > expon1 ) { \ + expon = expon2; \ + frac1 = ldexp( frac1, expon1 - expon ); \ + } else if ( expon1 > expon2 ) { \ + frac2 = ldexp( frac2, expon2 - expon ); \ + } \ +\ +/* If either of the original arguments is negative, we now subtract \ + the corresponding fraction from 2.0. If we think of the fraction as \ + represented in fixed-point binary notation, this corresponds to \ + converting negative numbers into the twos-complement form normally used \ + for integers (the sign bit being the bit with value 1) instead \ + of having a separate sign bit as for floating point numbers. \ +\ + Note that one of the fractions may have underflowed during the \ + scaling above. In that case (if the original argument was negative), \ + we must subtract the value "eps" (= 2.0 * DBL_EPSILON) from 2.0 \ + instead, so that we produce the largest number less than 2.0. In \ + twos-complement notation this represents the smallest possible \ + negative number and corresponds to extending the sign bit of the \ + original number up into more significant bits. This causes all bits to \ + be set as we require (rather than all being clear if the underflow \ + is simply ignored). */ \ + if ( (x1) < 0.0 ) frac1 = 2.0 - ( ( frac1 > eps ) ? frac1 : eps ); \ + if ( (x2) < 0.0 ) frac2 = 2.0 - ( ( frac2 > eps ) ? frac2 : eps ); \ +\ +/* We now extract the bits from the fraction values into integer \ + variables so that we may perform bit-wise operations on them. However, \ + since a double may be longer than any available integer, we may \ + have to handle several successive blocks of bits individually. */ \ +\ +/* Extract the first block of bits by scaling by the required power of \ + 2 to shift the required bits to the left of the binary point. Then \ + extract the integer part. Note that this initial shift is one bit less \ + than the number of bits in an unsigned long, because we have \ + introduced an extra sign bit. */ \ + frac1 *= scale1; \ + frac2 *= scale1; \ + b1 = (unsigned long) frac1; \ + b2 = (unsigned long) frac2; \ +\ +/* Perform the required bit-wise operation on the extracted blocks of \ + bits. */ \ + b = b1 oper b2; \ +\ +/* Extract the sign bit from this initial result. This determines \ + whether the final result bit pattern should represent a negative \ + floating point number. */ \ + neg = b & signbit; \ +\ +/* Initialise the floating point result by setting it to the integer \ + result multipled by the reciprocal of the scale factor used to shift \ + the bits above. This returns the result bits to their correct \ + significance. */ \ + unscale = rscale1; \ + result = (double) b * unscale; \ +\ +/* We now loop to extract and process further blocks of bits (if \ + present). The number of blocks is determined by the relative lengths \ + of a double and an unsigned long. In practice, some bits of the double \ + will be used by its exponent, so the last block may be incomplete and \ + will simply be padded with zeros. */ \ + for ( iblock = 1; iblock < nblock; iblock++ ) { \ +\ +/* Subtract the integer part (which has already been processed) from \ + each fraction, to leave the bits which remain to be processed. Then \ + multiply by a scale factor to shift the next set of bits to the left \ + of the binary point. This time, we use as many bits as will fit into \ + an unsigned long. */ \ + frac1 = ( frac1 - (double) b1 ) * scale; \ + frac2 = ( frac2 - (double) b2 ) * scale; \ +\ +/* Extract the integer part, which contains the required bits. */ \ + b1 = (unsigned long) frac1; \ + b2 = (unsigned long) frac2; \ +\ +/* Perform the required bit-wise operation on the extracted blocks of \ + bits. */ \ + b = b1 oper b2; \ +\ +/* Update the result floating point value by adding the new integer \ + result multiplied by a scale factor to return the bits to their \ + original significance. */ \ + unscale *= rscale; \ + result += (double) b * unscale; \ + } \ +\ +/* If the (normalised fraction) result represents a negative number, \ + then subtract 2.0 from it (equivalent to subtracting it from 2 and \ + negating the result). This converts back to using a separate sign bit \ + instead of twos-complement notation. */ \ + if ( neg ) result -= 2.0; \ +\ +/* Scale by the required power of 2 to remove the initial \ + normalisation applied and assign the result to the "result" \ + variable. */ \ + result = ldexp( result, expon ) + +/* Gaussian random number. */ +/* ----------------------- */ +/* This macro expands to code which assigns a pseudo-random value to + the "result" variable. The value is drawn from a Gaussian distribution + with mean "x1" and standard deviation "ABS(x2)". */ +#define GAUSS(x1,x2) \ +\ +/* Loop until a satisfactory result is obtained. */ \ + do { \ +\ +/* Obtain a value drawn from a standard Gaussian distribution. */ \ + ran = Gauss( rcontext, status ); \ +\ +/* Multiply by "ABS(x2)", trapping possible overflow. */ \ + result = ABS( (x2) ); \ + result = SAFE_MUL( ran, result ); \ +\ +/* If OK, add "x1", again trapping possible overflow. */ \ + if ( result != AST__BAD ) result = SAFE_ADD( result, (x1) ); \ +\ +/* Continue generating values until one is found which does not cause \ + overflow. */ \ + } while ( result == AST__BAD ); + +/* Implement the stack-based arithmetic. */ +/* ===================================== */ +/* Initialise the top of stack index and constant counter. */ + tos = -1; + icon = 0; + +/* Determine the number of opcodes to be processed and loop to process + them, executing the appropriate "case" block for each one. */ + ncode = code[ 0 ]; + for ( icode = 1; icode <= ncode; icode++ ) { + switch ( (Oper) code[ icode ] ) { + +/* Ignore any null opcodes (which shouldn't occur). */ + case OP_NULL: break; + +/* Otherwise, perform the required vector operation on the stack... */ + +/* User-supplied constants and variables. */ +/* -------------------------------------- */ +/* Loading a constant involves incrementing the constant count and + assigning the next constant's value to the top of stack element. */ + ARG_0( OP_LDCON, value = con[ icon++ ], *y = value ) + +/* Loading a variable involves obtaining the variable's index by + consuming a constant (as above), and then copying the variable's + values into the top of stack element. */ + ARG_0( OP_LDVAR, ivar = (int) ( con[ icon++ ] + 0.5 ), + *y = ptr_in[ ivar ][ point ] ) + +/* System constants. */ +/* ----------------- */ +/* Loading a "bad" value simply means assigning AST__BAD to the top of + stack element. */ + ARG_0( OP_LDBAD, ;, *y = AST__BAD ) + +/* The following load constants associated with the (double) floating + point representation into the top of stack element. */ + ARG_0( OP_LDDIG, ;, *y = (double) DBL_DIG ) + ARG_0( OP_LDEPS, ;, *y = DBL_EPSILON ) + ARG_0( OP_LDMAX, ;, *y = DBL_MAX ) + ARG_0( OP_LDMAX10E, ;, *y = (double) DBL_MAX_10_EXP ) + ARG_0( OP_LDMAXE, ;, *y = (double) DBL_MAX_EXP ) + ARG_0( OP_LDMDIG, ;, *y = (double) DBL_MANT_DIG ) + ARG_0( OP_LDMIN, ;, *y = DBL_MIN ) + ARG_0( OP_LDMIN10E, ;, *y = (double) DBL_MIN_10_EXP ) + ARG_0( OP_LDMINE, ;, *y = (double) DBL_MIN_EXP ) + ARG_0( OP_LDRAD, ;, *y = (double) FLT_RADIX ) + ARG_0( OP_LDRND, ;, *y = (double) FLT_ROUNDS ) + +/* Mathematical constants. */ +/* ----------------------- */ +/* The following load mathematical constants into the top of stack + element. */ + ARG_0( OP_LDE, value = exp( 1.0 ), *y = value ) + ARG_0( OP_LDPI, ;, *y = pi ) + +/* Functions with one argument. */ +/* ---------------------------- */ +/* The following simply evaluate a function of the top of stack + element and assign the result to the same element. */ + ARG_1( OP_ABS, *y = ABS( x ) ) + ARG_1( OP_ACOS, *y = ( ABS( x ) <= 1.0 ) ? + acos( x ) : AST__BAD ) + ARG_1( OP_ACOSD, *y = ( ABS( x ) <= 1.0 ) ? + acos( x ) * r2d : AST__BAD ) + ARG_1( OP_ACOSH, *y = ( x < 1.0 ) ? AST__BAD : + ( ( x > safe_sq ) ? log( x ) + log2 : + log( x + sqrt( x * x - 1.0 ) ) ) ) + ARG_1( OP_ACOTH, *y = ( ABS( x ) <= 1.0 ) ? AST__BAD : + 0.5 * ( log( ( x + 1.0 ) / + ( x - 1.0 ) ) ) ) + ARG_1( OP_ACSCH, *y = ( ( x == 0.0 ) ? AST__BAD : + ( sign = ( x >= 0.0 ), x = ABS( x ), + ( sign ? 1.0 : -1.0 ) * + ( ( x < rsafe_sq ) ? log2 - log( x ) : + ( x = 1.0 / x, + log( x + sqrt( x * x + 1.0 ) ) ) ) ) ) ) + ARG_1( OP_ASECH, *y = ( ( x <= 0 ) || ( x > 1.0 ) ) ? AST__BAD : + ( ( x < rsafe_sq ) ? log2 - log( x ) : + ( x = 1.0 / x, + log( x + sqrt( x * x - 1.0 ) ) ) ) ) + ARG_1( OP_ASIN, *y = ( ABS( x ) <= 1.0 ) ? + asin( x ) : AST__BAD ) + ARG_1( OP_ASIND, *y = ( ABS( x ) <= 1.0 ) ? + asin( x ) * r2d : AST__BAD ) + ARG_1( OP_ASINH, *y = ( sign = ( x >= 0.0 ), x = ABS( x ), + ( sign ? 1.0 : -1.0 ) * + ( ( x > safe_sq ) ? log( x ) + log2 : + log( x + sqrt( x * x + 1.0 ) ) ) ) ) + ARG_1( OP_ATAN, *y = atan( x ) ) + ARG_1( OP_ATAND, *y = atan( x ) * r2d ) + ARG_1( OP_ATANH, *y = ( ABS( x ) >= 1.0 ) ? AST__BAD : + 0.5 * ( log( ( 1.0 + x ) / + ( 1.0 - x ) ) ) ) + ARG_1( OP_CEIL, *y = ceil( x ) ) + ARG_1( OP_COS, *y = cos( x ) ) + ARG_1( OP_COSD, *y = cos( x * d2r ) ) + ARG_1( OP_COSH, *y = CATCH_MATHS_OVERFLOW( cosh( x ) ) ) + ARG_1( OP_COTH, *y = ( x = tanh( x ), SAFE_DIV( 1.0, x ) ) ) + ARG_1( OP_CSCH, *y = ( x = CATCH_MATHS_OVERFLOW( sinh( x ) ), + ( x == AST__BAD ) ? + 0.0 : SAFE_DIV( 1.0, x ) ) ) + ARG_1( OP_EXP, *y = CATCH_MATHS_OVERFLOW( exp( x ) ) ) + ARG_1( OP_FLOOR, *y = floor( x ) ) + ARG_1( OP_INT, *y = INT( x ) ) + ARG_1B( OP_ISBAD, *y = ( x == AST__BAD ) ) + ARG_1( OP_LOG, *y = ( x > 0.0 ) ? log( x ) : AST__BAD ) + ARG_1( OP_LOG10, *y = ( x > 0.0 ) ? log10( x ) : AST__BAD ) + ARG_1( OP_NINT, *y = ( x >= 0 ) ? + floor( x + 0.5 ) : ceil( x - 0.5 ) ) + ARG_1( OP_POISS, *y = Poisson( rcontext, x, status ) ) + ARG_1( OP_SECH, *y = ( x = CATCH_MATHS_OVERFLOW( cosh( x ) ), + ( x == AST__BAD ) ? 0.0 : 1.0 / x ) ) + ARG_1( OP_SIN, *y = sin( x ) ) + ARG_1( OP_SINC, *y = ( x == 0.0 ) ? 1.0 : sin( x ) / x ) + ARG_1( OP_SIND, *y = sin( x * d2r ) ) + ARG_1( OP_SINH, *y = CATCH_MATHS_OVERFLOW( sinh( x ) ) ) + ARG_1( OP_SQR, *y = SAFE_MUL( x, x ) ) + ARG_1( OP_SQRT, *y = ( x >= 0.0 ) ? sqrt( x ) : AST__BAD ) + ARG_1( OP_TAN, *y = CATCH_MATHS_OVERFLOW( tan( x ) ) ) + ARG_1( OP_TAND, *y = tan( x * d2r ) ) + ARG_1( OP_TANH, *y = tanh( x ) ) + +/* Functions with two arguments. */ +/* ----------------------------- */ +/* These evaluate a function of the top two entries on the stack. */ + ARG_2( OP_ATAN2, *y = atan2( x1, x2 ) ) + ARG_2( OP_ATAN2D, *y = atan2( x1, x2 ) * r2d ) + ARG_2( OP_DIM, *y = ( x1 > x2 ) ? x1 - x2 : 0.0 ) + ARG_2( OP_GAUSS, GAUSS( x1, x2 ); *y = result ) + ARG_2( OP_MOD, *y = ( x2 != 0.0 ) ? + fmod( x1, x2 ) : AST__BAD ) + ARG_2( OP_POW, *y = CATCH_MATHS_ERROR( pow( x1, x2 ) ) ) + ARG_2( OP_RAND, ran = Rand( rcontext, status ); + *y = x1 * ran + x2 * ( 1.0 - ran ); ) + ARG_2( OP_SIGN, *y = ( ( x1 >= 0.0 ) == ( x2 >= 0.0 ) ) ? + x1 : -x1 ) + +/* Functions with three arguments. */ +/* ------------------------------- */ +/* These evaluate a function of the top three entries on the stack. */ + ARG_3B( OP_QIF, *y = ( ( x1 ) ? ( x2 ) : ( x3 ) ) ) + + +/* Functions with variable numbers of arguments. */ +/* --------------------------------------------- */ +/* These operations take a variable number of arguments, the actual + number being determined by consuming a constant. We then loop to + perform a 2-argument operation on the stack (as above) the required + number of times. */ + case OP_MAX: + narg = (int) ( con[ icon++ ] + 0.5 ); + for ( iarg = 0; iarg < ( narg - 1 ); iarg++ ) { + DO_ARG_2( *y = ( x1 >= x2 ) ? x1 : x2 ) + } + break; + case OP_MIN: + narg = (int) ( con[ icon++ ] + 0.5 ); + for ( iarg = 0; iarg < ( narg - 1 ); iarg++ ) { + DO_ARG_2( *y = ( x1 <= x2 ) ? x1 : x2 ) + } + break; + +/* Unary arithmetic operators. */ +/* --------------------------- */ + ARG_1( OP_NEG, *y = -x ) + +/* Unary boolean operators. */ +/* ------------------------ */ + ARG_1( OP_NOT, *y = ( x == 0.0 ) ) + +/* Binary arithmetic operators. */ +/* ---------------------------- */ + ARG_2( OP_ADD, *y = SAFE_ADD( x1, x2 ) ) + ARG_2( OP_SUB, *y = SAFE_SUB( x1, x2 ) ) + ARG_2( OP_MUL, *y = SAFE_MUL( x1, x2 ) ) + ARG_2( OP_DIV , *y = SAFE_DIV( x1, x2 ) ) + +/* Bit-shift operators. */ +/* -------------------- */ + ARG_2( OP_SHFTL, *y = SHIFT_BITS( x1, x2 ) ) + ARG_2( OP_SHFTR, *y = SHIFT_BITS( x1, -x2 ) ) + +/* Relational operators. */ +/* --------------------- */ + ARG_2( OP_EQ, *y = ( x1 == x2 ) ) + ARG_2( OP_GE, *y = ( x1 >= x2 ) ) + ARG_2( OP_GT, *y = ( x1 > x2 ) ) + ARG_2( OP_LE, *y = ( x1 <= x2 ) ) + ARG_2( OP_LT, *y = ( x1 < x2 ) ) + ARG_2( OP_NE, *y = ( x1 != x2 ) ) + +/* Bit-wise operators. */ +/* ------------------- */ + ARG_2( OP_BITOR, BIT_OPER( |, x1, x2 ); *y = result ) + ARG_2( OP_BITXOR, BIT_OPER( ^, x1, x2 ); *y = result ) + ARG_2( OP_BITAND, BIT_OPER( &, x1, x2 ); *y = result ) + +/* Binary boolean operators. */ +/* ------------------------- */ + ARG_2B( OP_AND, *y = TRISTATE_AND( x1, x2 ) ) + ARG_2( OP_EQV, *y = ( ( x1 != 0.0 ) == ( x2 != 0.0 ) ) ) + ARG_2B( OP_OR, *y = TRISTATE_OR( x1, x2 ) ) + ARG_2( OP_XOR, *y = ( ( x1 != 0.0 ) != ( x2 != 0.0 ) ) ) + } + } + } + +/* When all opcodes have been processed, the result of the function + evaluation will reside in the lowest stack entry - i.e. the output + array. */ + +/* Free the workspace arrays. */ + work = astFree( work ); + stack = astFree( stack ); + +/* Undefine macros local to this function. */ +#undef ARG_0 +#undef ARG_1 +#undef ARG_1B +#undef DO_ARG_2 +#undef ARG_2 +#undef ARG_2B +#undef ABS +#undef INT +#undef CATCH_MATHS_OVERFLOW +#undef CATCH_MATHS_ERROR +#undef TRISTATE_OR +#undef TRISTATE_AND +#undef SAFE_ADD +#undef SAFE_SUB +#undef SAFE_MUL +#undef SAFE_DIV +#undef SHIFT_BITS +#undef BIT_OPER +#undef GAUSS +} + +static void EvaluationSort( const double con[], int nsym, int symlist[], + int **code, int *stacksize, int *status ) { +/* +* Name: +* EvaluationSort + +* Purpose: +* Perform an evaluation-order sort on parsed expression symbols. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void EvaluationSort( const double con[], int nsym, int symlist[], +* int **code, int *stacksize, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function sorts a sequence of numbers representing symbols +* identified in an expression. The symbols (i.e. the expression syntax) +* must have been fully validated beforehand, as no validation is +* performed here. +* +* The symbols are sorted into the order in which corresponding +* operations must be performed on a push-down arithmetic stack in order +* to evaluate the expression. Operation codes (opcodes), as defined in +* the "Oper" enum, are then substituted for the symbol numbers. + +* Parameters: +* con +* Pointer to an array of double containing the set of constants +* generated while parsing the expression (these are required in order +* to determine the number of arguments associated with functions which +* take a variable number of arguments). +* nsym +* The number of symbols identified while parsing the expression. +* symlist +* Pointer to an array of int, with "nsym" elements. On entry, this +* should contain the indices in the static "symbol" array of the +* symbols identified while parsing the expression. On exit, the +* contents are undefined. +* code +* Address of a pointer which will be set to point at a dynamically +* allocated array of int containing the set of opcodes (cast to int) +* produced by this function. The first element of this array will +* contain a count of the number of opcodes which follow. +* +* The allocated space must be freed by the caller (using astFree) when +* no longer required. +* stacksize +* Pointer to an int in which to return the size of the push-down stack +* required to evaluate the expression using the returned opcodes. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A value of NULL will be returned for the "*code" pointer and a value +* of zero will be returned for the "*stacksize" value if this function is +* invoked with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int flush; /* Flush parenthesised symbol sequence? */ + int icon; /* Input constant counter */ + int isym; /* Input symbol counter */ + int ncode; /* Number of opcodes generated */ + int nstack; /* Evaluation stack size */ + int push; /* Push a new symbol on to stack? */ + int sym; /* Variable for symbol number */ + int tos; /* Top of sort stack index */ + +/* Initialise */ + *code = NULL; + *stacksize = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Further initialisation. */ + flush = 0; + icon = 0; + isym = 0; + ncode = 0; + nstack = 0; + tos = -1; + +/* Loop to generate output opcodes until the sort stack is empty and + there are no further symbols to process, or an error is detected. */ + while ( astOK && ( ( tos > -1 ) || ( isym < nsym ) ) ) { + +/* Decide whether to push a symbol on to the sort stack (which + "diverts" it so that higher-priority symbols can be output), or to pop + the top symbol off the sort stack and send it to the output + stream... */ + +/* We must push a symbol on to the sort stack if the stack is + currently empty. */ + if ( tos == -1 ) { + push = 1; + +/* We must pop the top symbol off the sort stack if there are no more + input symbols to process. */ + } else if ( isym >= nsym ) { + push = 0; + +/* If the sort stack is being flushed to complete the evaluation of a + parenthesised expression, then the top symbol (which will be the + opening parenthesis or function call) must be popped. This is only + done once, so reset the "flush" flag before the next loop. */ + } else if ( flush ) { + push = 0; + flush = 0; + +/* In all other circumstances, we must push a symbol on to the sort + stack if its evaluation priority (seen from the left) is higher than + that of the current top of stack symbol (seen from the right). This + means it will eventually be sent to the output stream ahead of the + current top of stack symbol. */ + } else { + push = ( symbol[ symlist[ isym ] ].leftpriority > + symbol[ symlist[ tos ] ].rightpriority ); + } + +/* If a symbol is being pushed on to the sort stack, then get the next + input symbol which is to be used. */ + if ( push ) { + sym = symlist[ isym++ ]; + +/* If the symbol decreases the parenthesis level (a closing + parenthesis), then all the sort stack entries down to the symbol which + opened the current level of parenthesis (the matching opening + parenthesis or function call) will already have been sent to the + output stream as a consequence of the evaluation priority defined for + a closing parenthesis in the symbol data. The opening parenthesis (or + function call) must next be flushed from the sort stack, so set the + "flush" flag which is interpreted on the next loop. Ignore the current + symbol, which cancels with the opening parenthesis on the stack. */ + if ( symbol[ sym ].parincrement < 0 ) { + flush = 1; + +/* All other symbols are pushed on to the sort stack. The stack + occupies that region of the "symlist" array from which the input + symbol numbers have already been extracted. */ + } else { + symlist[ ++tos ] = sym; + } + +/* If a symbol is being popped from the top of the sort stack, then + the top of stack entry is transferred to the output stream. Obtain the + symbol number from the stack. Increment the local constant counter if + the associated operation will use a constant. */ + } else { + sym = symlist[ tos-- ]; + icon += ( ( sym == symbol_ldvar ) || ( sym == symbol_ldcon ) ); + +/* If the output symbol does not represent a "null" operation, + increase the size of the output opcode array to accommodate it, + checking for errors. Note that we allocate one extra array element + (the first) which will eventually hold a count of all the opcodes + generated. */ + if ( symbol[ sym ].opcode != OP_NULL ) { + *code = astGrow( *code, ncode + 2, sizeof( int ) ); + if ( astOK ) { + +/* Append the new opcode to the end of this array. */ + ( *code )[ ++ncode ] = (int) symbol[ sym ].opcode; + +/* Increment/decrement the counter representing the stack size + required for evaluation of the expression. If the symbol is a + function with a variable number of arguments (indicated by a negative + "nargs" entry in the symbol data table), then the change in stack size + must be determined from the argument number stored in the constant + table. */ + if ( symbol[ sym ].nargs >= 0 ) { + nstack += symbol[ sym ].stackincrement; + } else { + nstack -= (int) ( con[ icon++ ] + 0.5 ) - 1; + } + +/* Note the maximum size of the stack. */ + *stacksize = ( nstack > *stacksize ) ? nstack : *stacksize; + } + } + } + } + +/* If no "*code" array has been allocated, then allocate one simply to + store the number of opcodes generated, i.e. zero (this shouldn't + normally happen as this represents an invalid expression). */ + if ( !*code ) *code = astMalloc( sizeof( int ) ); + +/* If no error has occurred, store the count of opcodes generated in + the first element of the "*code" array and re-allocate the array to + its final size (since astGrow may have over-allocated space). */ + if ( astOK ) { + ( *code )[ 0 ] = ncode; + *code = astRealloc( *code, sizeof( int ) * (size_t) ( ncode + 1 ) ); + } + +/* If an error occurred, free any memory that was allocated and reset + the output values. */ + if ( !astOK ) { + *code = astFree( *code ); + *stacksize = 0; + } +} + +static void ExtractExpressions( const char *method, const char *class, + int nfun, const char *fun[], int forward, + char ***exprs, int *status ) { +/* +* Name: +* ExtractExpressions + +* Purpose: +* Extract and validate expressions. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void ExtractExpressions( const char *method, const char *class, +* int nfun, const char *fun[], int forward, +* char ***exprs, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function extracts expressions from the right hand sides of a set +* of functions. These expressions are then validated to check that they +* are either all present, or all absent (absence indicating an undefined +* transformation). An error is reported if anything is found to be +* wrong. +* +* Note that the syntax of the expressions is not checked by this function +* (i.e. they are not compiled). + +* Parameters: +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function. +* This method name is used solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string containing the +* class name of the Object being processed. This name is used solely +* for constructing error messages. +* nfun +* The number of functions to be analysed. +* fun +* Pointer to an array, with "nfun" elements, of pointers to null +* terminated strings which contain each of the functions. These +* strings should contain no white space. +* forward +* A non-zero value indicates the the MathMap's forward transformation +* functions are being processed, while a zero value indicates processing +* of the inverse transformation functions. This value is used solely for +* constructing error messages. +* exprs +* Address in which to return a pointer to an array (with "nfun" +* elements) of pointers to null terminated strings containing the +* extracted expressions (i.e. this returns an array of strings). +* +* Both the returned array of pointers, and the strings to which they +* point, will be stored in dynamically allocated memory and should +* be freed by the caller (using astFree) when no longer required. +* +* If the right hand sides (including the "=" sign) of all the supplied +* functions are absent, then this indicates an undefined transformation +* and the returned pointer value will be NULL. An error results if +* an "=" sign is present but no expression follows it. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A NULL value will be returned for "*exprs" if this function is +* invoked with the global error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + char *ex; /* Pointer to start of expression string */ + int ifun; /* Loop counter for functions */ + int iud; /* Index of first undefined function */ + int nud; /* Number of undefined expressions */ + +/* Initialise. */ + *exprs = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Further initialisation. */ + nud = 0; + iud = 0; + +/* Allocate and initialise memory for the returned array of pointers. */ + MALLOC_POINTER_ARRAY( *exprs, char *, nfun ) + +/* Loop to inspect each function in turn. */ + if ( astOK ) { + for ( ifun = 0; ifun < nfun; ifun++ ) { + +/* Search for the first "=" sign. */ + if ( ( ex = strchr( fun[ ifun ], '=' ) ) ) { + +/* If found, and there are more characters after the "=" sign, then + find the length of the expression which follows. Allocate a string to + hold this expression, storing its pointer in the array allocated + above. Check for errors. */ + if ( *++ex ) { + ( *exprs )[ ifun ] = astMalloc( strlen( ex ) + (size_t) 1 ); + if ( !astOK ) break; + +/* If OK, extract the expression string. */ + (void) strcpy( ( *exprs )[ ifun ], ex ); + +/* If an "=" sign was found but there are no characters following it, + then there is a missing right hand side to a function, so report an + error and quit. */ + } else { + astError( AST__NORHS, + "%s(%s): Missing right hand side in expression: " + "\"%s\".", status, + method, class, fun[ ifun ] ); + astError( astStatus, + "Error in %s transformation function %d.", status, + forward ? "forward" : "inverse", ifun + 1 ); + break; + } + +/* If no "=" sign was found, then the transformation may be undefined, + in which case each function should only contain a variable name. Count + the number of times this happens and record the index of the first + instance. */ + } else { + nud++; + if ( nud == 1 ) iud = ifun; + } + } + } + +/* Either all functions should have an "=" sign (in which case the + transformation is defined), or none of them should have (in which case + it is undefined). If some do and some don't, then report an error, + citing the first instance of a missing "=" sign. */ + if ( astOK && ( nud != 0 ) && ( nud != nfun ) ) { + astError( AST__NORHS, + "%s(%s): Missing right hand side in function: \"%s\".", status, + method, class, fun[ iud ] ); + astError( astStatus, + "Error in %s transformation function %d.", status, + forward ? "forward" : "inverse", iud + 1 ); + } + +/* If an error occurred, or all the expressions were absent, then free any + allocated memory and reset the output value. */ + if ( !astOK || nud ) { + FREE_POINTER_ARRAY( *exprs, nfun ) + } +} + +static void ExtractVariables( const char *method, const char *class, + int nfun, const char *fun[], + int nin, int nout, int nfwd, int ninv, + int forward, char ***var, int *status ) { +/* +* Name: +* ExtractVariables + +* Purpose: +* Extract and validate variable names. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void ExtractVariables( const char *method, const char *class, +* int nfun, const char *fun[], +* int nin, int nout, int nfwd, int ninv, +* int forward, char ***var, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function extracts variable names from the left hand sides of a +* set of transformation functions belonging to a MathMap. These variable +* names are then validated to check for correct syntax and no +* duplication. An error is reported if anything is wrong with the +* variable names obtained. + +* Parameters: +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function. +* This method name is used solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string containing the +* class name of the Object being processed. This name is used solely +* for constructing error messages. +* nfun +* The number of functions to be analysed. +* fun +* Pointer to an array, with "nfun" elements, of pointers to null +* terminated strings which contain each of the functions. These strings +* are case sensitive and should contain no white space. +* +* The first elements of this array should point to the functions that +* define the primary input/output variables (depending on direction). +* These should be followed by any functions which define intermediate +* variables (taken from the set of functions which transform in the +* opposite direction to the first ones). +* nin +* Number of input variables for the MathMap. +* nout +* Number of output variables for the MathMap. +* nfwd +* Number of forward transformation functions for the MathMap. +* ninv +* Number of inverse transformation functions for the MathMap. +* forward +* A non-zero value indicates the the MathMap's forward transformation +* functions are being processed, while a zero value indicates processing +* of the inverse transformation functions. This value, together with +* "nin", "nout", "nfwd" and "ninv" are used solely for constructing +* error messages. +* var +* Address in which to return a pointer to an array (with "nfun" +* elements) of pointers to null terminated strings containing the +* extracted variable names (i.e. this returns an array of strings). +* +* Both the returned array of pointers, and the strings to which they +* point, will be stored in dynamically allocated memory and should +* be freed by the caller (using astFree) when no longer required. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A NULL value will be returned for "*var" if this function is +* invoked with the global error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + char *duser1; /* Transformation direction for function */ + char *duser2; /* Transformation direction for function */ + char c; /* Extracted character */ + int i1; /* Loop counter for detecting duplicates */ + int i2; /* Loop counter for detecting duplicates */ + int i; /* Loop counter for characters */ + int iend; /* Last character index in parsed name */ + int ifun; /* Loop counter for functions */ + int iuser1; /* Function number as known to the user */ + int iuser2; /* Function number as known to the user */ + int nc; /* Character count */ + int nextra; /* Number of intermediate functions */ + int nprimary; /* Number of primary input/output variables */ + +/* Initialise. */ + *var = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the number of primary input/output variables, depending on + the direction of the coordinate transformation. */ + nprimary = ( forward ? nin : nout ); + +/* Deterine the number of extra (intermediate) functions that come + before these primary ones. These affect the numbering of + transformation functions as known to the user, and must be accounted + for when reporting error messages. */ + nextra = ( forward ? ninv - nin : nfwd - nout ); + +/* Allocate and initialise memory for the returned array of pointers. */ + MALLOC_POINTER_ARRAY( *var, char *, nfun ) + +/* Loop to process each function in turn. */ + if ( astOK ) { + for ( ifun = 0; ifun < nfun; ifun++ ) { + +/* Count the number of characters appearing before the "=" sign (or in + the entire string if the "=" is absent). */ + for ( nc = 0; ( c = fun[ ifun ][ nc ] ); nc++ ) if ( c == '=' ) break; + +/* If no characters were counted, then report an appropriate error + message, depending on whether the function string was entirely + blank. */ + if ( !nc ) { + if ( c ) { + astError( AST__MISVN, + "%s(%s): No left hand side in expression: \"%s\".", status, + method, class, fun[ ifun ] ); + } else { + astError( AST__MISVN, + "%s: Transformation function contains no variable " + "name.", status, + method ); + } + break; + } + +/* If OK, allocate memory to hold the output string and check for + errors. */ + ( *var )[ ifun ] = astMalloc( sizeof( char ) * (size_t) ( nc + 1 ) ) ; + if ( !astOK ) break; + +/* If OK, copy the characters before the "=" sign to the new + string. */ + nc = 0; + for ( i = 0; ( c = fun[ ifun ][ i ] ); i++ ) { + if ( c == '=' ) break; + ( *var )[ ifun ][ nc++] = c; + } + +/* Null terminate the result. */ + ( *var )[ ifun ][ nc ] = '\0'; + +/* Try to parse the contents of the extracted string as a name. */ + ParseName( ( *var )[ ifun ], 0, &iend, status ); + +/* If unsuccessful, or if all the characters were not parsed, then we + have an invalid variable name, so report an error and quit. */ + if ( ( iend < 0 ) || ( *var )[ ifun ][ iend + 1 ] ) { + astError( AST__VARIN, + "%s(%s): Variable name is invalid: \"%s\".", status, + method, class, ( *var )[ ifun ] ); + break; + } + } + +/* If an error occurred above, then determine the function number, and + the direction of the transformation of which it forms part, as known + to the user. */ + if ( !astOK ) { + if ( ifun < nprimary ) { + iuser1 = ifun + 1 + nextra; + duser1 = ( forward ? "inverse" : "forward" ); + } else { + iuser1 = ifun + 1 - nprimary; + duser1 = ( forward ? "forward" : "inverse" ); + } + +/* Report a contextual error message. */ + astError( astStatus, + "Error in %s transformation function %d.", status, + duser1, iuser1 ); + } + } + +/* If there has been no error, loop to compare all the variable names + with each other to detect duplication. */ + if ( astOK ) { + for ( i1 = 1; i1 < nfun; i1++ ) { + for ( i2 = 0; i2 < i1; i2++ ) { + +/* If a duplicate variable name is found, report an error. */ + if ( !strcmp( ( *var )[ i1 ], ( *var )[ i2 ] ) ) { + astError( AST__DUVAR, + "%s(%s): Duplicate definition of variable name: " + "\"%s\".", status, + method, class, ( *var )[ i1 ] ); + +/* For each transformation function involved, determine the function + number and the direction of the transformation of which it forms part, + as known to the user. */ + if ( i1 < nprimary ) { + iuser1 = i1 + 1 + nextra; + duser1 = ( forward ? "inverse" : "forward" ); + } else { + iuser1 = i1 + 1 - nprimary; + duser1 = ( forward ? "forward" : "inverse" ); + } + if ( i2 < nprimary ) { + iuser2 = i2 + 1 + nextra; + duser2 = ( forward ? "inverse" : "forward" ); + } else { + iuser2 = i2 + 1 - nprimary; + duser2 = ( forward ? "forward" : "inverse" ); + } + +/* Report a contextual error message. */ + astError( astStatus, + "Conflict between %s function %d and %s function %d.", status, + duser1, iuser1, duser2, iuser2 ); + break; + } + } + if ( !astOK ) break; + } + } + +/* If an error occurred, free any allocated memory and reset the + output value. */ + if ( !astOK ) { + FREE_POINTER_ARRAY( *var, nfun ) + } +} + +static double Gauss( Rcontext *context, int *status ) { +/* +* Name: +* Gauss + +* Purpose: +* Produce a pseudo-random sample from a standard Gaussian distribution. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* double Gauss( Rcontext *context, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* On each invocation, this function returns a pseudo-random sample drawn +* from a standard Gaussian distribution with mean zero and standard +* deviation unity. The Box-Muller transformation method is used. + +* Parameters: +* context +* Pointer to an Rcontext structure which holds the random number +* generator's context between invocations. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A sample from a standard Gaussian distribution. + +* Notes: +* - The sequence of numbers returned is determined by the "seed" +* value in the Rcontext structure supplied. +* - If the seed value is changed, the "active" flag must also be cleared +* so that this function can re-initiallise the Rcontext structure before +* generating the next pseudo-random number. The "active" flag should +* also be clear to force initialisation the first time an Rcontext +* structure is used. +* - This function does not perform error checking and does not generate +* errors. It will execute even if the global error status is set. +*/ + +/* Local Variables: */ + double rsq; /* Squared radius */ + double s; /* Scale factor */ + double x; /* First result value */ + static double y; /* Second result value */ + static int ysaved = 0; /* Previously-saved value available? */ + + LOCK_MUTEX7 + +/* If the random number generator context is not active, then it will + be (re)initialised on the first invocation of Rand (below). Ensure + that any previously-saved value within this function is first + discarded. */ + if ( !context->active ) ysaved = 0; + +/* If there is a previously-saved value available, then use it and + mark it as no longer available. */ + if ( ysaved ) { + x = y; + ysaved = 0; + +/* Otherwise, loop until a suitable new pair of values has been + obtained. */ + } else { + while ( 1 ) { + +/* Loop to obtain two random values uniformly distributed inside the + unit circle, while avoiding the origin (which maps to an infinite + result). */ + do { + x = 2.0 * Rand( context, status ) - 1.0; + y = 2.0 * Rand( context, status ) - 1.0; + rsq = x * x + y * y; + } while ( ( rsq >= 1.0 ) || ( rsq == 0.0 ) ); + +/* Perform the Box-Muller transformation, checking that this will not + produce overflow (which is extremely unlikely). If overflow would + occur, we simply repeat the above steps with a new pair of random + numbers. */ + s = -2.0 * log( rsq ); + if ( ( DBL_MAX * rsq ) >= s ) { + s = sqrt( s / rsq ); + +/* Scale the original random values to give a pair of results. One will be + returned and the second kept until next time. */ + x *= s; + y *= s; + break; + } + } + +/* Note that a saved value is available. */ + ysaved = 1; + } + + UNLOCK_MUTEX7 + +/* Return the current result. */ + return x; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* MathMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied MathMap, +* in bytes. + +* Parameters: +* this +* Pointer to the MathMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMathMap *this; /* Pointer to MathMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the MathMap structure. */ + this = (AstMathMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + SIZEOF_POINTER_ARRAY( this->fwdfun, this->nfwd ) + SIZEOF_POINTER_ARRAY( this->invfun, this->ninv ) + SIZEOF_POINTER_ARRAY( this->fwdcode, this->nfwd ) + SIZEOF_POINTER_ARRAY( this->invcode, this->ninv ) + SIZEOF_POINTER_ARRAY( this->fwdcon, this->nfwd ) + SIZEOF_POINTER_ARRAY( this->invcon, this->ninv ) + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a MathMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* MathMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a MathMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the MathMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the MathMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the MathMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMathMap *this; /* Pointer to the MathMap structure */ + const char *result; /* Pointer value to return */ + int ival; /* Integer attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the MathMap structure. */ + this = (AstMathMap *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Seed. */ +/* ----- */ + if ( !strcmp( attrib, "seed" ) ) { + ival = astGetSeed( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* SimpFI. */ +/* ------- */ + } else if ( !strcmp( attrib, "simpfi" ) ) { + ival = astGetSimpFI( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* SimpIF. */ +/* ------- */ + } else if ( !strcmp( attrib, "simpif" ) ) { + ival = astGetSimpIF( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +void astInitMathMapVtab_( AstMathMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitMathMapVtab + +* Purpose: +* Initialise a virtual function table for a MathMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "mathmap.h" +* void astInitMathMapVtab( AstMathMapVtab *vtab, const char *name ) + +* Class Membership: +* MathMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the MathMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAMathMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->ClearSeed = ClearSeed; + vtab->ClearSimpFI = ClearSimpFI; + vtab->ClearSimpIF = ClearSimpIF; + vtab->GetSeed = GetSeed; + vtab->GetSimpFI = GetSimpFI; + vtab->GetSimpIF = GetSimpIF; + vtab->SetSeed = SetSeed; + vtab->SetSimpFI = SetSimpFI; + vtab->SetSimpIF = SetSimpIF; + vtab->TestSeed = TestSeed; + vtab->TestSimpFI = TestSimpFI; + vtab->TestSimpIF = TestSimpIF; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "MathMap", + "Transformation using mathematical functions" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static double LogGamma( double x, int *status ) { +/* +* Name: +* LogGamma + +* Purpose: +* Calculate the logarithm of the gamma function. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* double LogGamma( double x, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function returns the natural logarithm of the gamma function +* for real arguments x>0. It uses the approximation of Lanczos, with +* constants from Press et al. (Numerical Recipes), giving a maximum +* fractional error (on the gamma function) of less than 2e-10. + +* Parameters: +* x +* The function argument, which must be greater than zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The natural logarithm of the gamma function with "x" as argument, +* or AST__BAD if "x" is not greater than zero. + +* Notes: +* - This function does not generate errors and does not perform error +* reporting. It will execute even if the global error status is set. +*/ + +/* Local Constants: */ + const double c0 = 1.000000000190015; /* Coefficients for series sum... */ + const double c1 = 76.18009172947146; + const double c2 = -86.50532032941677; + const double c3 = 24.01409824083091; + const double c4 = -1.231739572450155; + const double c5 = 0.1208650973866179e-2; + const double c6 = -0.5395239384953e-5; + const double g = 5.0; + +/* Local Variables: */ + double result; /* Result value to return */ + double sum; /* Series sum */ + double xx; /* Denominator for summing series */ + static double root_twopi; /* sqrt( 2.0 * pi ) */ + static int init = 0; /* Initialisation performed? */ + +/* If initialisation has not yet been performed, calculate the + constant required below. */ + LOCK_MUTEX3 + if ( !init ) { + root_twopi = sqrt( 2.0 * acos( -1.0 ) ); + +/* Note that initialisation has been performed. */ + init = 1; + } + UNLOCK_MUTEX3 + +/* Return a bad value if "x" is not greater than zero. */ + if ( x <= 0.0 ) { + result = AST__BAD; + +/* Otherwise, form the series sum. Since we only use 6 terms, the loop + that would normally be used has been completely unrolled here. */ + } else { + xx = x; + sum = c0; + sum += c1 / ++xx; + sum += c2 / ++xx; + sum += c3 / ++xx; + sum += c4 / ++xx; + sum += c5 / ++xx; + sum += c6 / ++xx; + +/* Calculate the result. */ + result = x + g + 0.5; + result -= ( x + 0.5 ) * log( result ); + result = log( root_twopi * sum / x ) - result; + } + +/* Return the result. */ + return result; +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a MathMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* MathMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated MathMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated MathMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated MathMap which is to be merged with +* its neighbours. This should be a cloned copy of the MathMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* MathMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated MathMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *new; /* Pointer to replacement Mapping */ + AstMathMap *mathmap1; /* Pointer to first MathMap */ + AstMathMap *mathmap2; /* Pointer to second MathMap */ + char **fwd1; /* Pointer to first forward function array */ + char **fwd2; /* Pointer to second forward function array */ + char **inv1; /* Pointer to first inverse function array */ + char **inv2; /* Pointer to second inverse function array */ + int ifun; /* Loop counter for functions */ + int imap1; /* Index of first Mapping */ + int imap2; /* Index of second Mapping */ + int imap; /* Loop counter for Mappings */ + int invert1; /* Invert flag for first MathMap */ + int invert2; /* Invert flag for second MathMap */ + int nfwd1; /* No. forward functions for first MathMap */ + int nfwd2; /* No. forward functions for second MathMap */ + int nin1; /* Number input coords for first MathMap */ + int ninv1; /* No. inverse functions for first MathMap */ + int ninv2; /* No. inverse functions for second MathMap */ + int nout2; /* Number output coords for second MathMap */ + int result; /* Result value to return */ + int simplify; /* Mappings may simplify? */ + +/* Initialise the returned result. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + mathmap1 = NULL; + mathmap2 = NULL; + imap1 = 0; + imap2 = 0; + invert1 = 0; + invert2 = 0; + nfwd1 = 0; + nin1 = 0; + ninv1 = 0; + +/* MathMaps are only worth simplifying if they occur in series. */ + simplify = series; + +/* If simplification appears possible, then obtain the indices of the + nominated mapping and of the one which follows it. Check that a + mapping exists for the second index. */ + if ( simplify ) { + imap1 = where; + imap2 = imap1 + 1; + simplify = ( imap2 < *nmap ); + } + +/* If OK, check whether the class of both Mappings is "MathMap" (a + MathMap can only combine with another MathMap). */ + if ( simplify ) { + simplify = !strcmp( astGetClass( ( *map_list )[ imap1 ] ), "MathMap" ); + } + if ( astOK && simplify ) { + simplify = !strcmp( astGetClass( ( *map_list )[ imap2 ] ), "MathMap" ); + } + +/* If still OK, obtain pointers to the two MathMaps and the associated + invert flag values. */ + if ( astOK && simplify ) { + mathmap1 = (AstMathMap *) ( *map_list )[ imap1 ]; + mathmap2 = (AstMathMap *) ( *map_list )[ imap2 ]; + invert1 = ( *invert_list )[ imap1 ]; + invert2 = ( *invert_list )[ imap2 ]; + +/* Depending on the invert flag values, obtain the SimpFI or SimpIF + attribute value from each MathMap and check whether they are set so as + to permit simplification. */ + simplify = ( ( invert1 ? astGetSimpIF( mathmap1 ) : + astGetSimpFI( mathmap1 ) ) && + ( invert2 ? astGetSimpFI( mathmap2 ) : + astGetSimpIF( mathmap2 ) ) ); + } + +/* If still OK, obtain the effective numbers of input coordinates for + the first MathMap and output coordinates for the second. Take account + of the associated invert flags and the way the Invert attribute of + each MathMap is currently set. */ + if ( astOK && simplify ) { + nin1 = ( invert1 == astGetInvert( mathmap1 ) ) ? + astGetNin( mathmap1 ) : astGetNout( mathmap1 ); + nout2 = ( invert2 == astGetInvert( mathmap2 ) ) ? + astGetNout( mathmap2 ) : astGetNin( mathmap2 ); + +/* Simplification is only possible if these two numbers are equal + (otherwise the the two MathMaps cannot be identical). */ + simplify = ( nin1 == nout2 ); + } + +/* If still OK, obtain the effective number of forward transformation + functions for the first MathMap (allowing for the associated invert + flag). Similarly, obtain the effective number of inverse + transformation functions for the second MathMap. */ + if ( astOK && simplify ) { + nfwd1 = !invert1 ? mathmap1->nfwd : mathmap1->ninv; + ninv2 = !invert2 ? mathmap2->ninv : mathmap2->nfwd; + +/* Check whether these values are equal. The MathMaps cannot be + identical if they are not. */ + simplify = ( nfwd1 == ninv2 ); + } + +/* As above, obtain pointers to the array of effective forward + transformation functions for the first MathMap, and the effective + inverse transformation functions for the second MathMap. */ + if ( astOK && simplify ) { + fwd1 = !invert1 ? mathmap1->fwdfun : mathmap1->invfun; + inv2 = !invert2 ? mathmap2->invfun : mathmap2->fwdfun; + +/* Loop to check whether these two sets of functions are + identical. The MathMaps cannot be merged unless they are. */ + for ( ifun = 0; ifun < nfwd1; ifun++ ) { + simplify = !strcmp( fwd1[ ifun ], inv2[ ifun ] ); + if ( !simplify ) break; + } + } + +/* If OK, repeat the above process to compare the effective inverse + transformation functions of the first MathMap with the forward + functions of the second one. */ + if ( astOK && simplify ) { + ninv1 = !invert1 ? mathmap1->ninv : mathmap1->nfwd; + nfwd2 = !invert2 ? mathmap2->nfwd : mathmap2->ninv; + simplify = ( ninv1 == nfwd2 ); + } + if ( astOK && simplify ) { + inv1 = !invert1 ? mathmap1->invfun : mathmap1->fwdfun; + fwd2 = !invert2 ? mathmap2->fwdfun : mathmap2->invfun; + for ( ifun = 0; ifun < ninv1; ifun++ ) { + simplify = !strcmp( inv1[ ifun ], fwd2[ ifun ] ); + if ( !simplify ) break; + } + } + +/* If the two MathMaps can be merged, create a UnitMap as a + replacement. */ + if ( astOK && simplify ) { + new = (AstMapping *) astUnitMap( nin1, "", status ); + +/* If OK, annul the pointers to the original MathMaps. */ + if ( astOK ) { + ( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] ); + ( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] ); + +/* Insert the pointer to the replacement UnitMap and store the + associated invert flag. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Loop to move the following Mapping pointers and invert flags down + in their arrays to close the gap. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - 1 ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - 1 ] = ( *invert_list )[ imap ]; + } + +/* Clear the final entry in each array. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = imap1; + } + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static void ParseConstant( const char *method, const char *class, + const char *exprs, int istart, int *iend, + double *con, int *status ) { +/* +* Name: +* ParseConstant + +* Purpose: +* Parse a constant. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void ParseConstant( const char *method, const char *class, +* const char *exprs, int istart, int *iend, +* double *con, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This routine parses an expression, looking for a constant starting at +* the character with index "istart" in the string "exprs". If it +* identifies the constant successfully, "*con" it will return its value +* and "*iend" will be set to the index of the final constant character +* in "exprs". +* +* If the characters encountered are clearly not part of a constant (it +* does not begin with a numeral or decimal point) the function returns +* with "*con" set to zero and "*iend" set to -1, but without reporting +* an error. However, if the first character appears to be a constant but +* its syntax proves to be invalid, then an error is reported. +* +* The expression must be in lower case with no embedded white space. +* The constant must not have a sign (+ or -) in front of it. + +* Parameters: +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function. +* This method name is used solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string containing the +* class name of the Object being processed. This name is used solely +* for constructing error messages. +* exprs +* Pointer to a null-terminated string containing the expression +* to be parsed. +* istart +* Index of the first character in "exprs" to be considered by this +* function. +* iend +* Pointer to an int in which to return the index in "exprs" of the +* final character which forms part of the constant. If no constant is +* found, a value of -1 is returned. +* con +* Pointer to a double, in which the value of the constant, if found, +* will be returned. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + char *str; /* Pointer to temporary string */ + char c; /* Single character from the expression */ + int dpoint; /* Decimal point encountered? */ + int expon; /* Exponent character encountered? */ + int i; /* Loop counter for characters */ + int iscon; /* Character is part of the constant? */ + int n; /* Number of values read by astSscanf */ + int nc; /* Number of characters read by astSscanf */ + int numer; /* Numeral encountered in current field? */ + int sign; /* Sign encountered? */ + int valid; /* Constant syntax valid? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise. */ + *con = 0.0; + *iend = -1; + +/* Check if the expression starts with a numeral or a decimal point. */ + c = exprs[ istart ]; + numer = isdigit( c ); + dpoint = ( c == '.' ); + +/* If it begins with any of these, the expression is clearly intended + to be a constant, so any failure beyond this point will result in an + error. Otherwise, failure to find a constant is not an error. */ + if ( numer || dpoint ) { + +/* Initialise remaining variables specifying the parser context. */ + expon = 0; + sign = 0; + valid = 1; + +/* Loop to increment the last constant character position until the + following character in the expression does not look like part of the + constant. */ + *iend = istart; + iscon = 1; + while ( ( c = exprs[ *iend + 1 ] ) && iscon ) { + iscon = 0; + +/* It may be part of a numerical constant if it is a numeral, wherever + it occurs. */ + if ( isdigit( c ) ) { + numer = 1; + iscon = 1; + +/* Or a decimal point, so long as it is the first one and is not in + the exponent field. Otherwise it is invalid. */ + } else if ( c == '.' ) { + if ( !( dpoint || expon ) ) { + dpoint = 1; + iscon = 1; + } else { + valid = 0; + } + +/* Or if it is a 'd' or 'e' exponent character, so long as it is the + first one and at least one numeral has been encountered first. + Otherwise it is invalid. */ + } else if ( ( c == 'd' ) || ( c == 'e' ) ) { + if ( !expon && numer ) { + expon = 1; + numer = 0; + iscon = 1; + } else { + valid = 0; + } + +/* Or if it is a sign, so long as it is in the exponent field and is + the first sign with no previous numerals in the same field. Otherwise + it is invalid (unless numerals have been encountered, in which case it + marks the end of the constant). */ + } else if ( ( c == '+' ) || ( c == '-' ) ) { + if ( expon && !sign && !numer ) { + sign = 1; + iscon = 1; + } else if ( !numer ) { + valid = 0; + } + } + +/* Increment the character count if the next character may be part of + the constant, or if it was invalid (it will then form part of the + error message). */ + if ( iscon || !valid ) ( *iend )++; + } + +/* Finally, check that the last field contained a numeral. */ + valid = ( valid && numer ); + +/* If the constant appears valid, allocate a temporary string to hold + it. */ + if ( valid ) { + str = astMalloc( (size_t) ( *iend - istart + 2 ) ); + if ( astOK ) { + +/* Copy the constant's characters, changing 'd' to 'e' so that + "astSscanf" will recognise it as an exponent character. */ + for ( i = istart; i <= *iend; i++ ) { + str[ i - istart ] = ( exprs[ i ] == 'd' ) ? 'e' : exprs[ i ]; + } + str[ *iend - istart + 1 ] = '\0'; + +/* Attempt to read the constant as a double, noting how many values + are read and how many characters consumed. */ + n = astSscanf( str, "%lf%n", con, &nc ); + +/* Check that one value was read and all the characters consumed. If + not, then the constant's syntax is invalid. */ + if ( ( n != 1 ) || ( nc < ( *iend - istart + 1 ) ) ) valid = 0; + } + +/* Free the temporary string. */ + str = astFree( str ); + } + +/* If the constant syntax is invalid, and no other error has occurred, + then report an error. */ + if ( astOK && !valid ) { + astError( AST__CONIN, + "%s(%s): Invalid constant syntax in the expression " + "\"%.*s\".", status, + method, class, *iend + 1, exprs ); + } + +/* If an error occurred, reset the output values. */ + if ( !astOK ) { + *iend = -1; + *con = 0.0; + } + } +} + +static void ParseName( const char *exprs, int istart, int *iend, int *status ) { +/* +* Name: +* ParseName + +* Purpose: +* Parse a name. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void ParseName( const char *exprs, int istart, int *iend, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This routine parses an expression, looking for a name starting at the +* character with index "istart" in the string "exprs". If it identifies +* a name successfully, "*iend" will return the index of the final name +* character in "exprs". A name must begin with an alphabetic character +* and subsequently contain only alphanumeric characters or underscores. +* +* If the expression does not contain a name at the specified location, +* "*iend" is set to -1. No error results. +* +* The expression should not contain embedded white space. + +* Parameters: +* exprs +* Pointer to a null-terminated string containing the expression +* to be parsed. +* istart +* Index of the first character in "exprs" to be considered by this +* function. +* iend +* Pointer to an int in which to return the index in "exprs" of the +* final character which forms part of the name. If no name is +* found, a value of -1 is returned. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + char c; /* Single character from expression */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise. */ + *iend = -1; + +/* Check the first character is valid for a name (alphabetic). */ + if ( isalpha( exprs[ istart ] ) ) { + +/* If so, loop to inspect each subsequent character until one is found + which is not part of a name (not alphanumeric or underscore). */ + for ( *iend = istart; ( c = exprs[ *iend + 1 ] ); ( *iend )++ ) { + if ( !( isalnum( c ) || ( c == '_' ) ) ) break; + } + } +} + +static void ParseVariable( const char *method, const char *class, + const char *exprs, int istart, int nvar, + const char *var[], int *ivar, int *iend, int *status ) { +/* +* Name: +* ParseVariable + +* Purpose: +* Parse a variable name. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void ParseVariable( const char *method, const char *class, +* const char *exprs, int istart, int nvar, +* const char *var[], int *ivar, int *iend, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This routine parses an expression, looking for a recognised variable +* name starting at the character with index "istart" in the string +* "exprs". If it identifies a variable name successfully, "*ivar" will +* return a value identifying it and "*iend" will return the index of the +* final variable name character in "exprs". To be recognised, a name +* must begin with an alphabetic character and subsequently contain only +* alphanumeric characters or underscores. It must also appear in the +* list of defined variable names supplied to this function. +* +* If the expression does not contain a name at the specified location, +* "*ivar" and "*iend" are set to -1 and no error results. However, if +* the expression contains a name but it is not in the list of defined +* variable names supplied, then an error is reported. +* +* This function is case sensitive. The expression should not contain +* embedded white space. + +* Parameters: +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function. +* This method name is used solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string containing the +* class name of the Object being processed. This name is used solely +* for constructing error messages. +* exprs +* Pointer to a null-terminated string containing the expression +* to be parsed. +* istart +* Index of the first character in "exprs" to be considered by this +* function. +* nvar +* The number of defined variable names. +* var +* An array of pointers (with "nvar" elements) to null-terminated +* strings. Each of these should contain a variable name to be +* recognised. These strings are case sensitive and should contain +* no white space. +* ivar +* Pointer to an int in which to return the index in "vars" of the +* variable name found. If no variable name is found, a value of -1 +* is returned. +* iend +* Pointer to an int in which to return the index in "exprs" of the +* final character which forms part of the variable name. If no variable +* name is found, a value of -1 is returned. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int found; /* Variable name recognised? */ + int nc; /* Number of characters in variable name */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise. */ + *ivar = -1; + *iend = -1; + +/* Determine if the characters in the expression starting at index + "istart" constitute a valid name. */ + ParseName( exprs, istart, iend, status ); + +/* If so, calculate the length of the name. */ + if ( *iend >= istart ) { + nc = *iend - istart + 1; + +/* Loop to compare the name with the list of variable names + supplied. */ + found = 0; + for ( *ivar = 0; *ivar < nvar; ( *ivar )++ ) { + found = ( nc == (int) strlen( var[ *ivar ] ) ) && + !strncmp( exprs + istart, var[ *ivar ], (size_t) nc ); + +/* Break if the name is recognised. */ + if ( found ) break; + } + +/* If it was not recognised, then report an error and reset the output + values. */ + if ( !found ) { + astError( AST__UDVOF, + "%s(%s): Undefined variable or function in the expression " + "\"%.*s\".", status, + method, class, *iend + 1, exprs ); + *ivar = -1; + *iend = -1; + } + } +} + +static double Poisson( Rcontext *context, double mean, int *status ) { +/* +* Name: +* Poisson + +* Purpose: +* Produce a pseudo-random sample from a Poisson distribution. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* double Poisson( Rcontext *context, double mean, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* On each invocation, this function returns a pseudo-random sample drawn +* from a Poisson distribution with a specified mean. A combination of +* methods is used, depending on the value of the mean. The algorithm is +* based on that given by Press et al. (Numerical Recipes), but +* re-implemented and extended. + +* Parameters: +* context +* Pointer to an Rcontext structure which holds the random number +* generator's context between invocations. +* mean +* The mean of the Poisson distribution, which should not be +* negative. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A sample (which will only take integer values) from the Poisson +* distribution, or AST__BAD if the mean supplied is negative. + +* Notes: +* - The sequence of numbers returned is determined by the "seed" +* value in the Rcontext structure supplied. +* - If the seed value is changed, the "active" flag must also be cleared +* so that this function can re-initiallise the Rcontext structure before +* generating the next pseudo-random number. The "active" flag should +* also be clear to force initialisation the first time an Rcontext +* structure is used. +* - This function does not perform error checking and does not generate +* errors. It will execute even if the global error status is set. +*/ + +/* Local Constants: */ + const double small = 9.3; /* "Small" distribution mean value */ + +/* Local Variables: */ + double pfract; /* Probability of accepting sample */ + double product; /* Product of random samples */ + double ran; /* Sample from Lorentzian distribution */ + double result; /* Result value to return */ + static double beta; /* Constant for forming acceptance ratio */ + static double huge; /* Large mean where std. dev. is negligible */ + static double last_mean; /* Value of "mean" on last invocation */ + static double log_mean; /* Logarithm of "mean" */ + static double pi; /* Value of pi */ + static double ranmax; /* Maximum safe value of "ran" */ + static double root_2mean; /* sqrt( 2.0 * mean ) */ + static double sqrt_point9; /* Square root of 0.9 */ + static double thresh; /* Threshold for product of samples */ + static int init = 0; /* Local initialisation performed? */ + + LOCK_MUTEX6 + +/* If initialisation has not yet been performed, then perform it + now. */ + if ( !init ) { + +/* Initialise the mean value from the previous invocation. */ + last_mean = -1.0; + +/* Calculate simple constants. */ + pi = acos( -1.0 ); + sqrt_point9 = sqrt( 0.9 ); + +/* Calculate the value of the distribution mean for which the smallest + representable deviation from the mean permitted by the machine + precision is one thousand standard deviations. */ + huge = pow( 1.0e3 / DBL_EPSILON, 2.0 ); + +/* Calculate the largest value such that + (0.9+(sqrt_point9*ranmax)*(sqrt_point9*ranmax)) doesn't overflow, + allowing a small margin for rounding error. */ + ranmax = ( sqrt( DBL_MAX - 0.9 ) / sqrt( 0.9 ) ) * + ( 1.0 - 4.0 * DBL_EPSILON ); + +/* Note that initialisation has been performed. */ + init = 1; + } + +/* If the distribution mean is less than zero, then return a bad + result. */ + if ( mean < 0.0 ) { + result = AST__BAD; + +/* If the mean is zero, then the result can only be zero. */ + } else if ( mean == 0.0 ) { + result = 0.0; + +/* Otherwise, if the mean is sufficiently small, we can use the direct + method of summing a series of exponentially distributed random samples + and counting the number which occur before the mean is exceeded. This + is equivalent to multiplying a series of uniformly distributed + samples and counting the number which occur before the product + becomes less then an equivalent threshold. */ + } else if ( mean <= small ) { + +/* If the mean has changed since the last invocation, store the new + mean and calculate a new threshold. */ + if ( mean != last_mean ) { + last_mean = mean; + thresh = exp( -mean ); + } + +/* Initialise the product and the result. */ + product = 1.0; + result = -1.0; + +/* Multiply the random samples, counting the number needed to reach + the threshold. */ + do { + product *= Rand( context, status ); + result += 1.0; + } while ( product > thresh ); + +/* Otherwise, if the distribution mean is large (but not huge), we + must use an indirect rejection method. */ + } else if ( mean <= huge ) { + +/* If the mean has changed since the last invocation, then + re-calculate the constants required below. Note that because of the + restrictions we have placed on "mean", these calculations are safe + against overflow. */ + if ( mean != last_mean ) { + last_mean = mean; + log_mean = log( mean ); + root_2mean = sqrt( 2.0 * mean ); + beta = mean * log_mean - LogGamma( mean + 1.0, status ); + } + +/* Loop until a suitable random sample has been generated. */ + do { + do { + +/* First transform a sample from a uniform distribution to obtain a + sample from a Lorentzian distribution. Check that the result is not so + large as to cause overflow later. Also check for overflow in the maths + library. If necessary, obtain a new sample. */ + do { + errno = 0; + ran = tan( pi * Rand( context, status ) ); + } while ( ( ran > ranmax ) || + ( ( errno == ERANGE ) && + ( ( ( ran >= 0.0 ) ? ran : -ran ) == HUGE_VAL ) ) ); + +/* If OK, scale the sample and add a constant so that the sample's + distribution approximates the Poisson distribution we + require. Overflow is prevented by the check on "ran" above, together + with the restricted value of "mean". */ + result = ran * root_2mean + mean; + +/* If the result is less than zero (where the Poisson distribution has + value zero), then obtain a new sample. */ + } while ( result < 0.0 ); + +/* Round down to an integer, so that the sample is valid for a Poisson + distribution. */ + result = floor( result ); + +/* Calculate the ratio between the required Poisson distribution and + the Lorentzian from which we have sampled (the factor of 0.9 prevents + this exceeding 1.0, and overflow is again prevented by the checks + performed above). */ + ran *= sqrt_point9; + pfract = ( 0.9 + ran * ran ) * + exp( result * log_mean - LogGamma( result + 1.0, status ) - beta ); + +/* Accept the sample with this fractional probability, otherwise + obtain a new sample. */ + } while ( Rand( context, status ) > pfract ); + +/* If the mean is huge, the relative standard deviation will be + negligible compared to the machine precision. In such cases, the + probability of getting a result that differs from the mean is + effectively zero, so we can simply return the mean. */ + } else { + result = mean; + } + + UNLOCK_MUTEX6 + +/* Return the result. */ + return result; +} + +static double Rand( Rcontext *context, int *status ) { +/* +* Name: +* Rand + +* Purpose: +* Produce a uniformly distributed pseudo-random number. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* double Rand( Rcontext *context, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* On each invocation, this function returns a pseudo-random number +* uniformly distributed in the range 0.0 to 1.0 (inclusive). The +* underlying algorithm is that used by the "ran2" function of Press et +* al. (Numerical Recipes), which has a long period and good statistical +* properties. This independent implementation returns double precision +* values. + +* Parameters: +* context +* Pointer to an Rcontext structure which holds the random number +* generator's context between invocations. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The sequence of numbers returned is determined by the "seed" +* value in the Rcontext structure supplied. +* - If the seed value is changed, the "active" flag must also be cleared +* so that this function can re-initiallise the Rcontext structure before +* generating the next pseudo-random number. The "active" flag should +* also be clear to force initialisation the first time an Rcontext +* structure is used. +* - This function does not perform error checking and does not generate +* errors. It will execute even if the global error status is set. +*/ + +/* Local Constants: */ + const long int a1 = 40014L; /* Random number generator constants... */ + const long int a2 = 40692L; + const long int m1 = 2147483563L; + const long int m2 = 2147483399L; + const long int q1 = 53668L; + const long int q2 = 52774L; + const long int r1 = 12211L; + const long int r2 = 3791L; + const int ntab = /* Size of shuffle table */ + AST_MATHMAP_RAND_CONTEXT_NTAB_; + const int nwarm = 8; /* Number of warm-up iterations */ + +/* Local Variables: */ + double result; /* Result value to return */ + double scale; /* Scale factor for random integers */ + double sum; /* Sum for forming normalisation constant */ + int dbits; /* Approximate bits in double mantissa */ + int irand; /* Loop counter for random integers */ + int itab; /* Loop counter for shuffle table */ + int lbits; /* Approximate bits used by generators */ + long int seed; /* Random number seed */ + long int tmp; /* Temporary variable */ + static double norm; /* Normalisation constant */ + static double scale0; /* Scale decrement for successive integers */ + static int init = 0; /* Local initialisation performed? */ + static int nrand; /* Number of random integers to use */ + +/* If the random number generator context is not active, then + initialise it. */ + if ( !context->active ) { + +/* First, perform local initialisation for this function, if not + already done. */ + LOCK_MUTEX4 + if ( !init ) { + +/* Obtain the approximate number of bits used by the random integer + generator from the value "m1". */ + (void) frexp( (double) m1, &lbits ); + +/* Obtain the approximate number of bits used by the mantissa of the + double value we want to produce, allowing for the (unlikely) + possibility that the mantissa's radix isn't 2. */ + dbits = (int) ceil( (double) DBL_MANT_DIG * + log( (double) FLT_RADIX ) / log( 2.0 ) ); + +/* Hence determine how many random integers we need to combine to + produce each double value, so that all the mantissa's bits will be + used. */ + nrand = ( dbits + lbits - 1 ) / lbits; + +/* Calculate the scale factor by which each successive random + integer's contribution to the result is reduced so as to generate + progressively less significant bits. */ + scale0 = 1.0 / (double) ( m1 - 1L ); + +/* Loop to sum the maximum contributions from each random integer + (assuming that each takes the largest possible value, of "m1-1", + from which we will later subtract 1). This produces the normalisation + factor by which the result must be scaled so as to lie between 0.0 and + 1.0 (inclusive). */ + sum = 0.0; + scale = 1.0; + for ( irand = 0; irand < nrand; irand++ ) { + scale *= scale0; + sum += scale; + } + norm = 1.0 / ( sum * (double) ( m1 - 2L ) ); + +/* Note that local initialisation has been done. */ + init = 1; + } + UNLOCK_MUTEX4 + +/* Obtain the seed value, enforcing positivity. */ + seed = (long int) context->seed; + if ( seed < 1 ) seed = seed + LONG_MAX; + if ( seed < 1 ) seed = LONG_MAX; + +/* Initialise the random number generators with this seed. */ + context->rand1 = context->rand2 = seed; + +/* Now loop to initialise the shuffle table with an initial set of + random values. We generate more values than required in order to "warm + up" the generator before recording values in the table. */ + for ( itab = ntab + nwarm - 1; itab >= 0; itab-- ) { + +/* Repeatedly update "rand1" from the expression "(rand1*a1)%m1" while + avoiding overflow. */ + tmp = context->rand1 / q1; + context->rand1 = a1 * ( context->rand1 - tmp * q1 ) - tmp * r1; + if ( context->rand1 < 0L ) context->rand1 += m1; + +/* After warming up, start recording values in the table. */ + if ( itab < ntab ) context->table[ itab ] = context->rand1; + } + +/* Record the last entry in the table as the "previous" random + integer. */ + context->random_int = context->table[ 0 ]; + +/* Note the random number generator context is active. */ + context->active = 1; + } + +/* Generate a random value. */ +/* ------------------------ */ +/* Initialise. */ + result = 0.0; + +/* Loop to generate sufficient random integers to combine into a + double value. */ + scale = norm; + for ( irand = 0; irand < nrand; irand++ ) { + +/* Update the first generator "rand1" from the expression + "(a1*rand1)%m1" while avoiding overflow. */ + tmp = context->rand1 / q1; + context->rand1 = a1 * ( context->rand1 - tmp * q1 ) - tmp * r1; + if ( context->rand1 < 0L ) context->rand1 += m1; + +/* Similarly, update the second generator "rand2" from the expression + "(a2*rand2)%m2". */ + tmp = context->rand2 / q2; + context->rand2 = a2 * ( context->rand2 - tmp * q2 ) - tmp * r2; + if ( context->rand2 < 0L ) context->rand2 += m2; + +/* Use the previous random integer to generate an index into the + shuffle table. */ + itab = (int) ( context->random_int / + ( 1L + ( m1 - 1L ) / (long int) ntab ) ); + +/* The algorithm left by RFWS seems to have a bug that "itab" can + sometimes be outside the range of [0.,ntab-1] causing the context->table + array to be addressed out of bounds. To avoid this, use the + following sticking plaster, since I'm not sure what the correct fix is. */ + if( itab < 0 ) itab = -itab; + itab = itab % ntab; + +/* Extract the table entry and replace it with a new random value from + the first generator "rand1". This is the Bays-Durham shuffle. */ + context->random_int = context->table[ itab ]; + context->table[ itab ] = context->rand1; + +/* Combine the extracted value with the latest value from the second + generator "rand2". */ + context->random_int -= context->rand2; + if ( context->random_int < 1L ) context->random_int += m1 - 1L; + +/* Update the scale factor to apply to the resulting random integer + and accumulate its contribution to the result. */ + scale *= scale0; + result += scale * (double) ( context->random_int - 1L ); + } + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a MathMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* MathMap member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a MathMap, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the MathMap. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void +*/ + +/* Local Vaiables: */ + AstMathMap *this; /* Pointer to the MathMap structure */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the MathMap structure. */ + this = (AstMathMap *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* Seed. */ +/* ----- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "seed= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSeed( this, ival ); + +/* SimpFI. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "simpfi= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSimpFI( this, ival ); + +/* SimpIF. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "simpif= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSimpIF( this, ival ); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a MathMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* MathMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a MathMap's attributes. + +* Parameters: +* this +* Pointer to the MathMap. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMathMap *this; /* Pointer to the MathMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the MathMap structure. */ + this = (AstMathMap *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* Seed. */ +/* ----- */ + if ( !strcmp( attrib, "seed" ) ) { + result = astTestSeed( this ); + +/* SimpFI. */ +/* ------- */ + } else if ( !strcmp( attrib, "simpfi" ) ) { + result = astTestSimpFI( this ); + +/* SimpIF. */ +/* ------- */ + } else if ( !strcmp( attrib, "simpif" ) ) { + result = astTestSimpIF( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *map, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a MathMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* AstPointSet *Transform( AstMapping *map, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* MathMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a MathMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required coordinate +* transformation. + +* Parameters: +* map +* Pointer to the MathMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the MathMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstMathMap *this; /* Pointer to MathMap to be applied */ + AstPointSet *result; /* Pointer to output PointSet */ + double **data_ptr; /* Array of pointers to coordinate data */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *work; /* Workspace for intermediate results */ + int idata; /* Loop counter for data pointer elements */ + int ifun; /* Loop counter for functions */ + int ncoord_in; /* Number of coordinates per input point */ + int ncoord_out; /* Number of coordinates per output point */ + int ndata; /* Number of data pointer elements filled */ + int nfun; /* Number of functions to evaluate */ + int npoint; /* Number of points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + work = NULL; + +/* Obtain a pointer to the MathMap. */ + this = (AstMathMap *) map; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( map, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + transformation needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + and output PointSets and obtain pointers for accessing the input and output + coordinate values. */ + ncoord_in = astGetNcoord( in ); + ncoord_out = astGetNcoord( result ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse transformation, according + to the direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( this ) ) forward = !forward; + +/* Obtain the number of transformation functions that must be + evaluated to perform the transformation. This will include any that + produce intermediate results from which the final results are + calculated. */ + nfun = forward ? this->nfwd : this->ninv; + +/* If intermediate results are to be calculated, then allocate + workspace to hold them (each intermediate result being a vector of + "npoint" double values). */ + if ( nfun > ncoord_out ) { + work = astMalloc( sizeof( double) * + (size_t) ( npoint * ( nfun - ncoord_out ) ) ); + } + +/* Also allocate space for an array to hold pointers to the input + data, intermediate results and output data. */ + data_ptr = astMalloc( sizeof( double * ) * (size_t) ( ncoord_in + nfun ) ); + +/* We now set up the "data_ptr" array to locate the data to be + processed. */ + if ( astOK ) { + +/* The first elements of this array point at the input data + vectors. */ + ndata = 0; + for ( idata = 0; idata < ncoord_in; idata++ ) { + data_ptr[ ndata++ ] = ptr_in[ idata ]; + } + +/* The following elements point at successive vectors within the + workspace array (if allocated). These vectors will act first as output + arrays for intermediate results, and then as input arrays for + subsequent calculations which use these results. */ + for ( idata = 0; idata < ( nfun - ncoord_out ); idata++ ) { + data_ptr[ ndata++ ] = work + ( idata * npoint ); + } + +/* The final elements point at the output coordinate data arrays into + which the final results will be written. */ + for ( idata = 0; idata < ncoord_out; idata++ ) { + data_ptr[ ndata++ ] = ptr_out[ idata ]; + } + +/* Perform coordinate transformation. */ +/* ---------------------------------- */ +/* Loop to evaluate each transformation function in turn. */ + for ( ifun = 0; ifun < nfun; ifun++ ) { + +/* Invoke the function that evaluates compiled expressions. Pass the + appropriate code and constants arrays, depending on the direction of + coordinate transformation, together with the required stack size. The + output array is the vector located by successive elements of the + "data_ptr" array (skipping the input data elements), while the + function has access to all previous elements of the "data_ptr" array + to locate the required input data. */ + EvaluateFunction( &this->rcontext, npoint, (const double **) data_ptr, + forward ? this->fwdcode[ ifun ] : + this->invcode[ ifun ], + forward ? this->fwdcon[ ifun ] : + this->invcon[ ifun ], + forward ? this->fwdstack : this->invstack, + data_ptr[ ifun + ncoord_in ], status ); + } + } + +/* Free the array of data pointers and any workspace allocated for + intermediate results. */ + data_ptr = astFree( data_ptr ); + if ( nfun > ncoord_out ) work = astFree( work ); + +/* If an error occurred, then return a NULL pointer. If no output + PointSet was supplied, also delete any new one that may have been + created. */ + if ( !astOK ) { + result = ( result == out ) ? NULL : astDelete( result ); + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void ValidateSymbol( const char *method, const char *class, + const char *exprs, int iend, int sym, + int *lpar, int **argcount, int **opensym, + int *ncon, double **con, int *status ) { +/* +* Name: +* ValidateSymbol + +* Purpose: +* Validate a symbol in an expression. + +* Type: +* Private function. + +* Synopsis: +* #include "mathmap.h" +* void ValidateSymbol( const char *method, const char *class, +* const char *exprs, int iend, int sym, int *lpar, +* int **argcount, int **opensym, int *ncon, +* double **con, int *status ) + +* Class Membership: +* MathMap member function. + +* Description: +* This function validates an identified standard symbol during +* compilation of an expression. Its main task is to keep track of the +* level of parenthesis in the expression and to count the number of +* arguments supplied to functions at each level of parenthesis (for +* nested function calls). On this basis it is able to interpret and +* accept or reject symbols which represent function calls, parentheses +* and delimiters. Other symbols are accepted automatically. + +* Parameters: +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function. +* This method name is used solely for constructing error messages. +* class +* Pointer to a constant null-terminated character string containing the +* class name of the Object being processed. This name is used solely +* for constructing error messages. +* exprs +* Pointer to a null-terminated string containing the expression +* being parsed. This is only used for constructing error messages. +* iend +* Index in "exprs" of the last character belonging to the most +* recently identified symbol. This is only used for constructing error +* messages. +* sym +* Index in the static "symbol" array of the most recently identified +* symbol in the expression. This is the symbol to be verified. +* lpar +* Pointer to an int which holds the current level of parenthesis. On +* the first invocation, this should be zero. The returned value should +* be passed to subsequent invocations. +* argcount +* Address of a pointer to a dynamically allocated array of int in +* which argument count information is maintained for each level of +* parenthesis (e.g. for nested function calls). On the first invocation, +* "*argcount" should be NULL. This function will allocate the required +* space as needed and update this pointer. The returned pointer value +* should be passed to subsequent invocations. +* +* The allocated space must be freed by the caller (using astFree) when +* no longer required. +* opensym +* Address of a pointer to a dynamically allocated array of int, in which +* information is maintained about the functions associated with each +* level of parenthesis (e.g. for nested function calls). On the first +* invocation, "*opensym" should be NULL. This function will allocate the +* required space as needed and update this pointer. The returned pointer +* value should be passed to subsequent invocations. +* +* The allocated space must be freed by the caller (using astFree) when +* no longer required. +* ncon +* Pointer to an int which holds a count of the constants associated +* with the expression (and determines the size of the "*con" array). +* This function will update the count to reflect any new constants +* appended to the "*con" array and the returned value should be passed +* to subsequent invocations. +* con +* Address of a pointer to a dynamically allocated array of double, in +* which the constants associated with the expression being parsed are +* accumulated. On entry, "*con" should point at a dynamic array with +* at least "*ncon" elements containing existing constants (or may be +* NULL if no constants have yet been stored). This function will +* allocate the required space as needed and update this pointer (and +* "*ncon") appropriately. The returned pointer value should be passed +* to subsequent invocations. +* +* The allocated space must be freed by the caller (using astFree) when +* no longer required. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The dynamically allocated arrays normally returned by this function +* will be freed and NULL pointers will be returned if this function is +* invoked with the global error status set, or if it should fail for any +* reason. +*/ + +/* Check the global error status, but do not return at this point + because dynamic arrays may require freeing. */ + if ( astOK ) { + +/* Check if the symbol is a comma. */ + if ( ( symbol[ sym ].text[ 0 ] == ',' ) && + ( symbol[ sym ].text[ 1 ] == '\0' ) ) { + +/* A comma is only used to delimit function arguments. If the current + level of parenthesis is zero, or the symbol which opened the current + level of parenthesis was not a function call (indicated by an argument + count of zero at the current level of parenthesis), then report an + error. */ + if ( ( *lpar <= 0 ) || ( ( *argcount )[ *lpar - 1 ] == 0 ) ) { + astError( AST__COMIN, + "%s(%s): Spurious comma encountered in the expression " + "\"%.*s\".", status, + method, class, iend + 1, exprs ); + +/* If a comma is valid, then increment the argument count at the + current level of parenthesis. */ + } else { + ( *argcount )[ *lpar - 1 ]++; + } + +/* If the symbol is not a comma, check if it increases the current + level of parenthesis. */ + } else if ( symbol[ sym ].parincrement > 0 ) { + +/* Increase the size of the arrays which hold parenthesis level + information and check for errors. */ + *argcount = astGrow( *argcount, *lpar + 1, sizeof( int ) ); + *opensym = astGrow( *opensym, *lpar + 1, sizeof( int ) ); + if ( astOK ) { + +/* Increment the level of parenthesis and initialise the argument + count at the new level. This count is set to zero if the symbol which + opens the parenthesis level is not a function call (indicated by a + zero "nargs" entry in the symbol data), and it subsequently remains at + zero. If the symbol is a function call, the argument count is + initially set to 1 and increments whenever a comma is encountered at + this parenthesis level. */ + ( *argcount )[ ++( *lpar ) - 1 ] = ( symbol[ sym ].nargs != 0 ); + +/* Remember the symbol which opened this parenthesis level. */ + ( *opensym )[ *lpar - 1 ] = sym; + } + +/* Check if the symbol decreases the current parenthesis level. */ + } else if ( symbol[ sym ].parincrement < 0 ) { + +/* Ensure that the parenthesis level is not already at zero. If it is, + then there is a missing left parenthesis in the expression being + compiled, so report an error. */ + if ( *lpar == 0 ) { + astError( AST__MLPAR, + "%s(%s): Missing left parenthesis in the expression " + "\"%.*s\".", status, + method, class, iend + 1, exprs ); + +/* If the parenthesis level is valid and the symbol which opened this + level of parenthesis was a function call with a fixed number of + arguments (indicated by a positive "nargs" entry in the symbol data), + then we must check the number of function arguments which have been + encountered. */ + } else if ( symbol[ ( *opensym )[ *lpar - 1 ] ].nargs > 0 ) { + +/* Report an error if the number of arguments is wrong. */ + if ( ( *argcount )[ *lpar - 1 ] != + symbol[ ( *opensym )[ *lpar - 1 ] ].nargs ) { + astError( AST__WRNFA, + "%s(%s): Wrong number of function arguments in the " + "expression \"%.*s\".", status, + method, class, iend + 1, exprs ); + +/* If the number of arguments is valid, decrement the parenthesis + level. */ + } else { + ( *lpar )--; + } + +/* If the symbol which opened this level of parenthesis was a function + call with a variable number of arguments (indicated by a negative + "nargs" entry in the symbol data), then we must check and process the + number of function arguments. */ + } else if ( symbol[ ( *opensym )[ *lpar - 1 ] ].nargs < 0 ) { + +/* Check that the minimum required number of arguments have been + supplied. Report an error if they have not. */ + if ( ( *argcount )[ *lpar - 1 ] < + ( -symbol[ ( *opensym )[ *lpar - 1 ] ].nargs ) ) { + astError( AST__WRNFA, + "%s(%s): Insufficient function arguments in the " + "expression \"%.*s\".", status, + method, class, iend + 1, exprs ); + +/* If the number of arguments is valid, increase the size of the + constants array and check for errors. */ + } else { + *con = astGrow( *con, *ncon + 1, sizeof( double ) ); + if ( astOK ) { + +/* Append the argument count to the end of the array of constants and + decrement the parenthesis level. */ + ( *con )[ ( *ncon )++ ] = + (double) ( *argcount )[ --( *lpar ) ]; + } + } + +/* Finally, if the symbol which opened this level of parenthesis was + not a function call ("nargs" entry in the symbol data is zero), then + decrement the parenthesis level. In this case there is no need to + check the argument count, because it will not have been + incremented. */ + } else { + ( *lpar )--; + } + } + } + +/* If an error occurred (or the global error status was set on entry), + then reset the parenthesis level and free any memory which may have + been allocated. */ + if ( !astOK ) { + *lpar = 0; + if ( *argcount ) *argcount = astFree( *argcount ); + if ( *opensym ) *opensym = astFree( *opensym ); + if ( *con ) *con = astFree( *con ); + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* Seed + +* Purpose: +* Random number seed for a MathMap. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute, which may take any integer value, determines the +* sequence of random numbers produced by the random number functions in +* MathMap expressions. It is set to an unpredictable default value when +* a MathMap is created, so that by default each MathMap uses a different +* set of random numbers. +* +* If required, you may set this Seed attribute to a value of your +* choosing in order to produce repeatable behaviour from the random +* number functions. You may also enquire the Seed value (e.g. if an +* initially unpredictable value has been used) and then use it to +* reproduce the resulting sequence of random numbers, either from the +* same MathMap or from another one. +* +* Clearing the Seed attribute gives it a new unpredictable default +* value. + +* Applicability: +* MathMap +* All MathMaps have this attribute. +*att-- +*/ +/* Clear the Seed value by setting it to a new unpredictable value + produced by DefaultSeed and clearing the "seed_set" flag in the + MathMap's random number generator context. Also clear the "active" + flag, so that the generator will be re-initialised to use this seed + when it is next invoked. */ +astMAKE_CLEAR(MathMap,Seed,rcontext.seed,( this->rcontext.seed_set = 0, + this->rcontext.active = 0, + DefaultSeed( &this->rcontext, status ) )) + +/* Return the "seed" value from the random number generator + context. */ +astMAKE_GET(MathMap,Seed,int,0,this->rcontext.seed) + +/* Store the new seed value in the MathMap's random number generator + context and set the context's "seed_set" flag. Also clear the "active" + flag, so that the generator will be re-initialised to use this seed + when it is next invoked. */ +astMAKE_SET(MathMap,Seed,int,rcontext.seed,( this->rcontext.seed_set = 1, + this->rcontext.active = 0, + value )) + +/* Test the "seed_set" flag in the random number generator context. */ +astMAKE_TEST(MathMap,Seed,( this->rcontext.seed_set )) + +/* +*att++ +* Name: +* SimpFI + +* Purpose: +* Forward-inverse MathMap pairs simplify? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +c This attribute should be set to a non-zero value if applying a +c MathMap's forward transformation, followed immediately by the matching +c inverse transformation will always restore the original set of +c coordinates. It indicates that AST may replace such a sequence of +c operations by an identity Mapping (a UnitMap) if it is encountered +c while simplifying a compound Mapping (e.g. using astSimplify). +f This attribute should be set to a non-zero value if applying a +f MathMap's forward transformation, followed immediately by the matching +f inverse transformation will always restore the original set of +f coordinates. It indicates that AST may replace such a sequence of +f operations by an identity Mapping (a UnitMap) if it is encountered +f while simplifying a compound Mapping (e.g. using AST_SIMPLIFY). +* +* By default, the SimpFI attribute is zero, so that AST will not perform +* this simplification unless you have set SimpFI to indicate that it is +* safe to do so. + +* Applicability: +* MathMap +* All MathMaps have this attribute. + +* Notes: +* - For simplification to occur, the two MathMaps must be in series and +* be identical (with textually identical transformation +* functions). Functional equivalence is not sufficient. +* - The consent of both MathMaps is required before simplification can +* take place. If either has a SimpFI value of zero, then simplification +* will not occur. +* - The SimpFI attribute controls simplification only in the case where +* a MathMap's forward transformation is followed by the matching inverse +* transformation. It does not apply if an inverse transformation is +* followed by a forward transformation. This latter case is controlled +* by the SimpIF attribute. +c - The "forward" and "inverse" transformations referred to are those +c defined when the MathMap is created (corresponding to the "fwd" and +c "inv" parameters of its constructor function). If the MathMap is +c inverted (i.e. its Invert attribute is non-zero), then the role of the +c SimpFI and SimpIF attributes will be interchanged. +f - The "forward" and "inverse" transformations referred to are those +f defined when the MathMap is created (corresponding to the FWD and +f INV arguments of its constructor function). If the MathMap is +f inverted (i.e. its Invert attribute is non-zero), then the role of the +f SimpFI and SimpIF attributes will be interchanged. +*att-- +*/ +/* Clear the SimpFI value by setting it to -INT_MAX. */ +astMAKE_CLEAR(MathMap,SimpFI,simp_fi,-INT_MAX) + +/* Supply a default of 0 if no SimpFI value has been set. */ +astMAKE_GET(MathMap,SimpFI,int,0,( ( this->simp_fi != -INT_MAX ) ? + this->simp_fi : 0 )) + +/* Set a SimpFI value of 1 if any non-zero value is supplied. */ +astMAKE_SET(MathMap,SimpFI,int,simp_fi,( value != 0 )) + +/* The SimpFI value is set if it is not -INT_MAX. */ +astMAKE_TEST(MathMap,SimpFI,( this->simp_fi != -INT_MAX )) + +/* +*att++ +* Name: +* SimpIF + +* Purpose: +* Inverse-forward MathMap pairs simplify? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +c This attribute should be set to a non-zero value if applying a +c MathMap's inverse transformation, followed immediately by the matching +c forward transformation will always restore the original set of +c coordinates. It indicates that AST may replace such a sequence of +c operations by an identity Mapping (a UnitMap) if it is encountered +c while simplifying a compound Mapping (e.g. using astSimplify). +f This attribute should be set to a non-zero value if applying a +f MathMap's inverse transformation, followed immediately by the matching +f forward transformation will always restore the original set of +f coordinates. It indicates that AST may replace such a sequence of +f operations by an identity Mapping (a UnitMap) if it is encountered +f while simplifying a compound Mapping (e.g. using AST_SIMPLIFY). +* +* By default, the SimpIF attribute is zero, so that AST will not perform +* this simplification unless you have set SimpIF to indicate that it is +* safe to do so. + +* Applicability: +* MathMap +* All MathMaps have this attribute. + +* Notes: +* - For simplification to occur, the two MathMaps must be in series and +* be identical (with textually identical transformation +* functions). Functional equivalence is not sufficient. +* - The consent of both MathMaps is required before simplification can +* take place. If either has a SimpIF value of zero, then simplification +* will not occur. +* - The SimpIF attribute controls simplification only in the case where +* a MathMap's inverse transformation is followed by the matching forward +* transformation. It does not apply if a forward transformation is +* followed by an inverse transformation. This latter case is controlled +* by the SimpFI attribute. +c - The "forward" and "inverse" transformations referred to are those +c defined when the MathMap is created (corresponding to the "fwd" and +c "inv" parameters of its constructor function). If the MathMap is +c inverted (i.e. its Invert attribute is non-zero), then the role of the +c SimpFI and SimpIF attributes will be interchanged. +f - The "forward" and "inverse" transformations referred to are those +f defined when the MathMap is created (corresponding to the FWD and +f INV arguments of its constructor function). If the MathMap is +f inverted (i.e. its Invert attribute is non-zero), then the role of the +f SimpFI and SimpIF attributes will be interchanged. +*att-- +*/ +/* Clear the SimpIF value by setting it to -INT_MAX. */ +astMAKE_CLEAR(MathMap,SimpIF,simp_if,-INT_MAX) + +/* Supply a default of 0 if no SimpIF value has been set. */ +astMAKE_GET(MathMap,SimpIF,int,0,( ( this->simp_if != -INT_MAX ) ? + this->simp_if : 0 )) + +/* Set a SimpIF value of 1 if any non-zero value is supplied. */ +astMAKE_SET(MathMap,SimpIF,int,simp_if,( value != 0 )) + +/* The SimpIF value is set if it is not -INT_MAX. */ +astMAKE_TEST(MathMap,SimpIF,( this->simp_if != -INT_MAX )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for MathMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for MathMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstMathMap *in; /* Pointer to input MathMap */ + AstMathMap *out; /* Pointer to output MathMap */ + int ifun; /* Loop counter for functions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output MathMaps. */ + in = (AstMathMap *) objin; + out = (AstMathMap *) objout; + +/* For safety, first clear any references to the input memory from + the output MathMap. */ + out->fwdfun = NULL; + out->invfun = NULL; + out->fwdcode = NULL; + out->invcode = NULL; + out->fwdcon = NULL; + out->invcon = NULL; + +/* Now allocate and initialise each of the output pointer arrays + required. */ + if ( in->fwdfun ) { + MALLOC_POINTER_ARRAY( out->fwdfun, char *, out->nfwd ) + } + if ( in->invfun ) { + MALLOC_POINTER_ARRAY( out->invfun, char *, out->ninv ) + } + if ( in->fwdcode ) { + MALLOC_POINTER_ARRAY( out->fwdcode, int *, out->nfwd ) + } + if ( in->invcode ) { + MALLOC_POINTER_ARRAY( out->invcode, int *, out->ninv ) + } + if ( in->fwdcon ) { + MALLOC_POINTER_ARRAY( out->fwdcon, double *, out->nfwd ) + } + if ( in->invcon ) { + MALLOC_POINTER_ARRAY( out->invcon, double *, out->ninv ) + } + +/* If OK, loop to make copies of the data (where available) associated + with each forward transformation function, storing pointers to the + copy in the output pointer arrays allocated above. */ + if ( astOK ) { + for ( ifun = 0; ifun < out->nfwd; ifun++ ) { + if ( in->fwdfun && in->fwdfun[ ifun ] ) { + out->fwdfun[ ifun ] = astStore( NULL, in->fwdfun[ ifun ], + astSizeOf( in->fwdfun[ ifun ] ) ); + } + if ( in->fwdcode && in->fwdcode[ ifun ] ) { + out->fwdcode[ ifun ] = astStore( NULL, in->fwdcode[ ifun ], + astSizeOf( in->fwdcode[ ifun ] ) ); + } + if ( in->fwdcon && in->fwdcon[ ifun ] ) { + out->fwdcon[ ifun ] = astStore( NULL, in->fwdcon[ ifun ], + astSizeOf( in->fwdcon[ ifun ] ) ); + } + if ( !astOK ) break; + } + } + +/* Repeat this process for the inverse transformation functions. */ + if ( astOK ) { + for ( ifun = 0; ifun < out->ninv; ifun++ ) { + if ( in->invfun && in->invfun[ ifun ] ) { + out->invfun[ ifun ] = astStore( NULL, in->invfun[ ifun ], + astSizeOf( in->invfun[ ifun ] ) ); + } + if ( in->invcode && in->invcode[ ifun ] ) { + out->invcode[ ifun ] = astStore( NULL, in->invcode[ ifun ], + astSizeOf( in->invcode[ ifun ] ) ); + } + if ( in->invcon && in->invcon[ ifun ] ) { + out->invcon[ ifun ] = astStore( NULL, in->invcon[ ifun ], + astSizeOf( in->invcon[ ifun ] ) ); + } + if ( !astOK ) break; + } + } + +/* If an error occurred, clean up by freeing all output memory + allocated above. */ + if ( !astOK ) { + FREE_POINTER_ARRAY( out->fwdfun, out->nfwd ) + FREE_POINTER_ARRAY( out->invfun, out->ninv ) + FREE_POINTER_ARRAY( out->fwdcode, out->nfwd ) + FREE_POINTER_ARRAY( out->invcode, out->ninv ) + FREE_POINTER_ARRAY( out->fwdcon, out->nfwd ) + FREE_POINTER_ARRAY( out->invcon, out->ninv ) + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for MathMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for MathMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstMathMap *this; /* Pointer to MathMap */ + +/* Obtain a pointer to the MathMap structure. */ + this = (AstMathMap *) obj; + +/* Free all memory allocated by the MathMap. */ + FREE_POINTER_ARRAY( this->fwdfun, this->nfwd ) + FREE_POINTER_ARRAY( this->invfun, this->ninv ) + FREE_POINTER_ARRAY( this->fwdcode, this->nfwd ) + FREE_POINTER_ARRAY( this->invcode, this->ninv ) + FREE_POINTER_ARRAY( this->fwdcon, this->nfwd ) + FREE_POINTER_ARRAY( this->invcon, this->ninv ) +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for MathMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the MathMap class to an output Channel. + +* Parameters: +* this +* Pointer to the MathMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 150 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstMathMap *this; /* Pointer to the MathMap structure */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment strings */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword strings */ + int ifun; /* Loop counter for functions */ + int invert; /* MathMap inverted? */ + int ival; /* Integer attribute value */ + int nin; /* True number of input coordinates */ + int nout; /* True number of output coordinates */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the MathMap structure. */ + this = (AstMathMap *) this_object; + +/* Determine if the MathMap is inverted and obtain the "true" number + of input and output coordinates by un-doing the effects of any + inversion. */ + invert = astGetInvert( this ); + nin = !invert ? astGetNin( this ) : astGetNout( this ); + nout = !invert ? astGetNout( this ) : astGetNin( this ); + +/* Write out values representing the instance variables for the + MathMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Number of forward transformation functions. */ +/* ------------------------------------------- */ +/* We regard this value as set if it differs from the number of output + coordinates for the MathMap. */ + set = ( this->nfwd != nout ); + astWriteInt( channel, "Nfwd", set, 0, this->nfwd, + "Number of forward transformation functions" ); + +/* Forward transformation functions. */ +/* --------------------------------- */ +/* Loop to write out each forward transformation function, generating + a suitable keyword and comment for each one. */ + for ( ifun = 0; ifun < this->nfwd; ifun++ ) { + (void) sprintf( key, "Fwd%d", ifun + 1 ); + (void) sprintf( comment, "Forward function %d", ifun + 1 ); + astWriteString( channel, key, 1, 1, this->fwdfun[ ifun ], comment ); + } + +/* Number of inverse transformation functions. */ +/* ------------------------------------------- */ +/* We regard this value as set if it differs from the number of input + coordinates for the MathMap. */ + set = ( this->ninv != nin ); + astWriteInt( channel, "Ninv", set, 0, this->ninv, + "Number of inverse transformation functions" ); + +/* Inverse transformation functions. */ +/* --------------------------------- */ +/* Similarly, loop to write out each inverse transformation + function. */ + for ( ifun = 0; ifun < this->ninv; ifun++ ) { + (void) sprintf( key, "Inv%d", ifun + 1 ); + (void) sprintf( comment, "Inverse function %d", ifun + 1 ); + astWriteString( channel, key, 1, 1, this->invfun[ ifun ], comment ); + } + +/* SimpFI. */ +/* ------- */ +/* Write out the forward-inverse simplification flag. */ + set = TestSimpFI( this, status ); + ival = set ? GetSimpFI( this, status ) : astGetSimpFI( this ); + astWriteInt( channel, "SimpFI", set, 0, ival, + ival ? "Forward-inverse pairs may simplify" : + "Forward-inverse pairs do not simplify" ); + +/* SimpIF. */ +/* ------- */ +/* Write out the inverse-forward simplification flag. */ + set = TestSimpIF( this, status ); + ival = set ? GetSimpIF( this, status ) : astGetSimpIF( this ); + astWriteInt( channel, "SimpIF", set, 0, ival, + ival ? "Inverse-forward pairs may simplify" : + "Inverse-forward pairs do not simplify" ); + +/* Seed. */ +/* ----- */ +/* Write out any random number seed value which is set. Prefix this with + a separate flag which indicates if the seed has been set. */ + set = TestSeed( this, status ); + ival = set ? GetSeed( this, status ) : astGetSeed( this ); + astWriteInt( channel, "Seeded", set, 0, set, + set? "Explicit random number seed set" : + "No random number seed set" ); + astWriteInt( channel, "Seed", set, 0, ival, + set ? "Random number seed value" : + "Default random number seed used" ); + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAMathMap and astCheckMathMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(MathMap,Mapping) +astMAKE_CHECK(MathMap) + +AstMathMap *astMathMap_( int nin, int nout, + int nfwd, const char *fwd[], + int ninv, const char *inv[], + const char *options, int *status, ...) { +/* +*+ +* Name: +* astMathMap + +* Purpose: +* Create a MathMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "mathmap.h" +* AstMathMap *astMathMap( int nin, int nout, +* int nfwd, const char *fwd[], +* int ninv, const char *inv[], +* const char *options, ..., int *status ) + +* Class Membership: +* MathMap constructor. + +* Description: +* This function creates a new MathMap and optionally initialises its +* attributes. + +* Parameters: +* nin +* Number of input variables for the MathMap. +* nout +* Number of output variables for the MathMap. +* nfwd +* The number of forward transformation functions being supplied. +* This must be at least equal to "nout". +* fwd +* Pointer to an array, with "nfwd" elements, of pointers to null +* terminated strings which contain each of the forward transformation +* functions. +* ninv +* The number of inverse transformation functions being supplied. +* This must be at least equal to "nin". +* inv +* Pointer to an array, with "ninv" elements, of pointers to null +* terminated strings which contain each of the inverse transformation +* functions. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new MathMap. The syntax used is the same as +* for the astSet method and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of arguments may follow it in order to +* supply values to be substituted for these specifiers. The +* rules for supplying these are identical to those for the +* astSet method (and for the C "printf" function). + +* Returned Value: +* A pointer to the new MathMap. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic MathMap constructor which is +* available via the protected interface to the MathMap class. A +* public interface is provided by the astMathMapId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMathMap *new; /* Pointer to new MathMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the MathMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitMathMap( NULL, sizeof( AstMathMap ), !class_init, &class_vtab, + "MathMap", nin, nout, nfwd, fwd, ninv, inv ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new MathMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new MathMap. */ + return new; +} + +AstMathMap *astMathMapId_( int nin, int nout, + int nfwd, const char *fwd[], + int ninv, const char *inv[], + const char *options, ... ) { +/* +*++ +* Name: +c astMathMap +f AST_MATHMAP + +* Purpose: +* Create a MathMap. + +* Type: +* Public function. + +* Synopsis: +c #include "mathmap.h" +c AstMathMap *astMathMap( int nin, int nout, +c int nfwd, const char *fwd[], +c int ninv, const char *inv[], +c const char *options, ... ) +f RESULT = AST_MATHMAP( NIN, NOUT, NFWD, FWD, NINV, INV, OPTIONS, STATUS ) + +* Class Membership: +* MathMap constructor. + +* Description: +* This function creates a new MathMap and optionally initialises its +* attributes. +* +c A MathMap is a Mapping which allows you to specify a set of forward +c and/or inverse transformation functions using arithmetic operations +c and mathematical functions similar to those available in C. The +c MathMap interprets these functions at run-time, whenever its forward +c or inverse transformation is required. Because the functions are not +c compiled in the normal sense (unlike an IntraMap), they may be used to +c describe coordinate transformations in a transportable manner. A +c MathMap therefore provides a flexible way of defining new types of +c Mapping whose descriptions may be stored as part of a dataset and +c interpreted by other programs. +f A MathMap is a Mapping which allows you to specify a set of forward +f and/or inverse transformation functions using arithmetic operations +f and mathematical functions similar to those available in Fortran. The +f MathMap interprets these functions at run-time, whenever its forward +f or inverse transformation is required. Because the functions are not +f compiled in the normal sense (unlike an IntraMap), they may be used to +f describe coordinate transformations in a transportable manner. A +f MathMap therefore provides a flexible way of defining new types of +f Mapping whose descriptions may be stored as part of a dataset and +f interpreted by other programs. + +* Parameters: +c nin +f NIN = INTEGER +* Number of input variables for the MathMap. This determines the +* value of its Nin attribute. +c nout +f NOUT = INTEGER +* Number of output variables for the MathMap. This determines the +* value of its Nout attribute. +c nfwd +f NFWD = INTEGER +* The number of forward transformation functions being supplied. +c This must be at least equal to "nout", but may be increased to +f This must be at least equal to NOUT, but may be increased to +* accommodate any additional expressions which define intermediate +* variables for the forward transformation (see the "Calculating +* Intermediate Values" section below). +c fwd +f FWD = CHARACTER * ( * )( NFWD ) +c An array (with "nfwd" elements) of pointers to null terminated strings +c which contain the expressions defining the forward transformation. +f An array which contains the expressions defining the forward +f transformation. +* The syntax of these expressions is described below. +c ninv +f NINV = INTEGER +* The number of inverse transformation functions being supplied. +c This must be at least equal to "nin", but may be increased to +f This must be at least equal to NIN, but may be increased to +* accommodate any additional expressions which define intermediate +* variables for the inverse transformation (see the "Calculating +* Intermediate Values" section below). +c inv +f INV = CHARACTER * ( * )( NINV ) +c An array (with "ninv" elements) of pointers to null terminated strings +c which contain the expressions defining the inverse transformation. +f An array which contains the expressions defining the inverse +f transformation. +* The syntax of these expressions is described below. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new MathMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new MathMap. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMathMap() +f AST_MATHMAP = INTEGER +* A pointer to the new MathMap. + +* Defining Transformation Functions: +c A MathMap's transformation functions are supplied as a set of +c expressions in an array of character strings. Normally you would +c supply the same number of expressions for the forward transformation, +c via the "fwd" parameter, as there are output variables (given by the +c MathMap's Nout attribute). For instance, if Nout is 2 you might use: +c - "r = sqrt( x * x + y * y )" +c - "theta = atan2( y, x )" +c +c which defines a transformation from Cartesian to polar +c coordinates. Here, the variables that appear on the left of each +c expression ("r" and "theta") provide names for the output variables +c and those that appear on the right ("x" and "y") are references to +c input variables. +f A MathMap's transformation functions are supplied as a set of +f expressions in an array of character strings. Normally you would +f supply the same number of expressions for the forward transformation, +f via the FWD argument, as there are output variables (given by the +f MathMap's Nout attribute). For instance, if Nout is 2 you might use: +f - 'R = SQRT( X * X + Y * Y )' +f - 'THETA = ATAN2( Y, X )' +f +f which defines a transformation from Cartesian to polar +f coordinates. Here, the variables that appear on the left of each +f expression (R and THETA) provide names for the output variables and +f those that appear on the right (X and Y) are references to input +f variables. +* +c To complement this, you must also supply expressions for the inverse +c transformation via the "inv" parameter. In this case, the number of +c expressions given would normally match the number of MathMap input +c coordinates (given by the Nin attribute). If Nin is 2, you might use: +c - "x = r * cos( theta )" +c - "y = r * sin( theta )" +c +c which expresses the transformation from polar to Cartesian +c coordinates. Note that here the input variables ("x" and "y") are +c named on the left of each expression, and the output variables ("r" +c and "theta") are referenced on the right. +f To complement this, you must also supply expressions for the inverse +f transformation via the INV argument. In this case, the number of +f expressions given would normally match the number of MathMap input +f coordinates (given by the Nin attribute). If Nin is 2, you might use: +f - 'X = R * COS( THETA )' +f - 'Y = R * SIN( THETA )' +f +f which expresses the transformation from polar to Cartesian +f coordinates. Note that here the input variables (X and Y) are named on +f the left of each expression, and the output variables (R and THETA) +f are referenced on the right. +* +* Normally, you cannot refer to a variable on the right of an expression +* unless it is named on the left of an expression in the complementary +* set of functions. Therefore both sets of functions (forward and +* inverse) must be formulated using the same consistent set of variable +* names. This means that if you wish to leave one of the transformations +* undefined, you must supply dummy expressions which simply name each of +* the output (or input) variables. For example, you might use: +c - "x" +c - "y" +f - 'X' +f - 'Y' +* +* for the inverse transformation above, which serves to name the input +* variables but without defining an inverse transformation. + +* Calculating Intermediate Values: +c It is sometimes useful to calculate intermediate values and then to +c use these in the final expressions for the output (or input) +c variables. This may be done by supplying additional expressions for +c the forward (or inverse) transformation functions. For instance, the +c following array of five expressions describes 2-dimensional pin-cushion +c distortion: +c - "r = sqrt( xin * xin + yin * yin )" +c - "rout = r * ( 1 + 0.1 * r * r )" +c - "theta = atan2( yin, xin )" +c - "xout = rout * cos( theta )" +c - "yout = rout * sin( theta )" +f It is sometimes useful to calculate intermediate values and then to +f use these in the final expressions for the output (or input) +f variables. This may be done by supplying additional expressions for +f the forward (or inverse) transformation functions. For instance, the +f following array of five expressions describes 2-dimensional pin-cushion +f distortion: +f - 'R = SQRT( XIN * XIN + YIN * YIN )' +f - 'ROUT = R * ( 1 + 0.1 * R * R )' +f - 'THETA = ATAN2( YIN, XIN )', +f - 'XOUT = ROUT * COS( THETA )' +f - 'YOUT = ROUT * SIN( THETA )' +* +c Here, we first calculate three intermediate results ("r", "rout" +c and "theta") and then use these to calculate the final results ("xout" +c and "yout"). The MathMap knows that only the final two results +c constitute values for the output variables because its Nout attribute +c is set to 2. You may define as many intermediate variables in this +c way as you choose. Having defined a variable, you may then refer to it +c on the right of any subsequent expressions. +f Here, we first calculate three intermediate results (R, ROUT +f and THETA) and then use these to calculate the final results (XOUT +f and YOUT). The MathMap knows that only the final two results +f constitute values for the output variables because its Nout attribute +f is set to 2. You may define as many intermediate variables in this +f way as you choose. Having defined a variable, you may then refer to it +f on the right of any subsequent expressions. +* +c Note that when defining the inverse transformation you may only refer +c to the output variables "xout" and "yout". The intermediate variables +c "r", "rout" and "theta" (above) are private to the forward +c transformation and may not be referenced by the inverse +c transformation. The inverse transformation may, however, define its +c own private intermediate variables. +f Note that when defining the inverse transformation you may only refer +f to the output variables XOUT and YOUT. The intermediate variables R, +f ROUT and THETA (above) are private to the forward transformation and +f may not be referenced by the inverse transformation. The inverse +f transformation may, however, define its own private intermediate +f variables. + +* Expression Syntax: +c The expressions given for the forward and inverse transformations +c closely follow the syntax of the C programming language (with some +c extensions for compatibility with Fortran). They may contain +c references to variables and literal constants, together with +c arithmetic, boolean, relational and bitwise operators, and function +c invocations. A set of symbolic constants is also available. Each of +c these is described in detail below. Parentheses may be used to +c over-ride the normal order of evaluation. There is no built-in limit +c to the length of expressions and they are insensitive to case or the +c presence of additional white space. +f The expressions given for the forward and inverse transformations +f closely follow the syntax of Fortran (with some extensions for +f compatibility with the C language). They may contain references to +f variables and literal constants, together with arithmetic, logical, +f relational and bitwise operators, and function invocations. A set of +f symbolic constants is also available. Each of these is described in +f detail below. Parentheses may be used to over-ride the normal order of +f evaluation. There is no built-in limit to the length of expressions +f and they are insensitive to case or the presence of additional white +f space. + +* Variables: +* Variable names must begin with an alphabetic character and may contain +* only alphabetic characters, digits, and the underscore character +* "_". There is no built-in limit to the length of variable names. + +* Literal Constants: +c Literal constants, such as "0", "1", "0.007" or "2.505e-16" may appear +c in expressions, with the decimal point and exponent being optional (a +c "D" may also be used as an exponent character for compatibility with +c Fortran). A unary minus "-" may be used as a prefix. +f Literal constants, such as "0", "1", "0.007" or "2.505E-16" may appear +f in expressions, with the decimal point and exponent being optional (a +f "D" may also be used as an exponent character). A unary minus "-" may +f be used as a prefix. + +* Arithmetic Precision: +* All arithmetic is floating point, performed in double precision. + +* Propagation of Missing Data: +* Unless indicated otherwise, if any argument of a function or operator +* has the value AST__BAD (indicating missing data), then the result of +* that function or operation is also AST__BAD, so that such values are +* propagated automatically through all operations performed by MathMap +* transformations. The special value AST__BAD can be represented in +* expressions by the symbolic constant "". +* +* A result (i.e. equal to AST__BAD) is also produced in response +* to any numerical error (such as division by zero or numerical +* overflow), or if an invalid argument value is provided to a function +* or operator. + +* Arithmetic Operators: +* The following arithmetic operators are available: +c - x1 + x2: Sum of "x1" and "x2". +f - X1 + X2: Sum of X1 and X2. +c - x1 - x2: Difference of "x1" and "x2". +f - X1 - X2: Difference of X1 and X2. +c - x1 * x2: Product of "x1" and "x1". +f - X1 * X2: Product of X1 and X2. +c - x1 / x2: Ratio of "x1" and "x2". +f - X1 / X2: Ratio of X1 and X2. +c - x1 ** x2: "x1" raised to the power of "x2". +f - X1 ** X2: X1 raised to the power of X2. +c - + x: Unary plus, has no effect on its argument. +f - + X: Unary plus, has no effect on its argument. +c - - x: Unary minus, negates its argument. +f - - X: Unary minus, negates its argument. + +c Boolean Operators: +f Logical Operators: +c Boolean values are represented using zero to indicate false and +c non-zero to indicate true. In addition, the value AST__BAD is taken to +c mean "unknown". The values returned by boolean operators may therefore +c be 0, 1 or AST__BAD. Where appropriate, "tri-state" logic is +c implemented. For example, "a||b" may evaluate to 1 if "a" is non-zero, +c even if "b" has the value AST__BAD. This is because the result of the +c operation would not be affected by the value of "b", so long as "a" is +c non-zero. +f Logical values are represented using zero to indicate .FALSE. and +f non-zero to indicate .TRUE.. In addition, the value AST__BAD is taken to +f mean "unknown". The values returned by logical operators may therefore +f be 0, 1 or AST__BAD. Where appropriate, "tri-state" logic is +f implemented. For example, A.OR.B may evaluate to 1 if A is non-zero, +f even if B has the value AST__BAD. This is because the result of the +f operation would not be affected by the value of B, so long as A is +f non-zero. +* +c The following boolean operators are available: +f The following logical operators are available: +c - x1 && x2: Boolean AND between "x1" and "x2", returning 1 if both "x1" +c and "x2" are non-zero, and 0 otherwise. This operator implements +c tri-state logic. (The synonym ".and." is also provided for compatibility +c with Fortran.) +f - X1 .AND. X2: Logical AND between X1 and X2, returning 1 if both X1 +f and X2 are non-zero, and 0 otherwise. This operator implements +f tri-state logic. (The synonym "&&" is also provided for compatibility +f with C.) +c - x1 || x2: Boolean OR between "x1" and "x2", returning 1 if either "x1" +c or "x2" are non-zero, and 0 otherwise. This operator implements +c tri-state logic. (The synonym ".or." is also provided for compatibility +c with Fortran.) +f - X1 .OR. X2: Logical OR between X1 and X2, returning 1 if either X1 +f or X2 are non-zero, and 0 otherwise. This operator implements +f tri-state logic. (The synonym "||" is also provided for compatibility +f with C.) +c - x1 ^^ x2: Boolean exclusive OR (XOR) between "x1" and "x2", returning +c 1 if exactly one of "x1" and "x2" is non-zero, and 0 otherwise. Tri-state +c logic is not used with this operator. (The synonyms ".neqv." and ".xor." +c are also provided for compatibility with Fortran, although the second +c of these is not standard.) +f - X1 .NEQV. X2: Logical exclusive OR (XOR) between X1 and X2, +f returning 1 if exactly one of X1 and X2 is non-zero, and 0 +f otherwise. Tri-state logic is not used with this operator. (The +f synonym ".XOR." is also provided, although this is not standard +f Fortran. In addition, the C-like synonym "^^" may be used, although +f this is also not standard.) +c - x1 .eqv. x2: This is provided only for compatibility with Fortran +c and tests whether the boolean states of "x1" and "x2" (i.e. true/false) +c are equal. It is the negative of the exclusive OR (XOR) function. +c Tri-state logic is not used with this operator. +f - X1 .EQV. X2: Tests whether the logical states of X1 and X2 +f (i.e. .TRUE./.FALSE.) are equal. It is the negative of the exclusive OR +f (XOR) function. Tri-state logic is not used with this operator. +c - ! x: Boolean unary NOT operation, returning 1 if "x" is zero, and +c 0 otherwise. (The synonym ".not." is also provided for compatibility +c with Fortran.) +f - .NOT. X: Logical unary NOT operation, returning 1 if X is zero, and +f 0 otherwise. (The synonym "!" is also provided for compatibility with +f C.) + +* Relational Operators: +c Relational operators return the boolean result (0 or 1) of comparing +c the values of two floating point values for equality or inequality. The +c value AST__BAD may also be returned if either argument is . +f Relational operators return the logical result (0 or 1) of comparing +f the values of two floating point values for equality or inequality. The +f value AST__BAD may also be returned if either argument is . +* +* The following relational operators are available: +c - x1 == x2: Tests whether "x1" equals "x1". (The synonym ".eq." is +c also provided for compatibility with Fortran.) +f - X1 .EQ. X2: Tests whether X1 equals X2. (The synonym "==" is also +f provided for compatibility with C.) +c - x1 != x2: Tests whether "x1" is unequal to "x2". (The synonym ".ne." +c is also provided for compatibility with Fortran.) +f - X1 .NE. X2: Tests whether X1 is unequal to X2. (The synonym "!=" is +f also provided for compatibility with C.) +c - x1 > x2: Tests whether "x1" is greater than "x2". (The synonym +c ".gt." is also provided for compatibility with Fortran.) +f - X1 .GT. X2: Tests whether X1 is greater than X2. (The synonym ">" is +f also provided for compatibility with C.) +c - x1 >= x2: Tests whether "x1" is greater than or equal to "x2". (The +c synonym ".ge." is also provided for compatibility with Fortran.) +f - X1 .GE. X2: Tests whether X1 is greater than or equal to X2. (The +f synonym ">=" is also provided for compatibility with C.) +c - x1 < x2: Tests whether "x1" is less than "x2". (The synonym ".lt." +c is also provided for compatibility with Fortran.) +f - X1 .LT. X2: Tests whether X1 is less than X2. (The synonym "<" is also +f provided for compatibility with C.) +c - x1 <= x2: Tests whether "x1" is less than or equal to "x2". (The +c synonym ".le." is also provided for compatibility with Fortran.) +f - X1 .LE. X2: Tests whether X1 is less than or equal to X2. (The synonym +f "<=" is also provided for compatibility with C.) +* +c Note that relational operators cannot usefully be used to compare +c values with the value (representing missing data), because the +c result is always . The isbad() function should be used instead. +f Note that relational operators cannot usefully be used to compare +f values with the value (representing missing data), because the +f result is always . The ISBAD() function should be used instead. +f +f Note, also, that because logical operators can operate on floating +f point values, care must be taken to use parentheses in some cases +f where they would not normally be required in Fortran. For example, +f the expresssion: +f - .NOT. A .EQ. B +f +f must be written: +f - .NOT. ( A .EQ. B ) +f +f to prevent the .NOT. operator from associating with the variable A. + +* Bitwise Operators: +c The bitwise operators provided by C are often useful when operating on +c raw data (e.g. from instruments), so they are also provided for use in +c MathMap expressions. In this case, however, the values on which they +c operate are floating point values rather than pure integers. In order +c to produce results which match the pure integer case, the operands are +c regarded as fixed point binary numbers (i.e. with the binary +c equivalent of a decimal point) with negative numbers represented using +c twos-complement notation. For integer values, the resulting bit +c pattern corresponds to that of the equivalent signed integer (digits +c to the right of the point being zero). Operations on the bits +c representing the fractional part are also possible, however. +f Bitwise operators are often useful when operating on raw data +f (e.g. from instruments), so they are provided for use in MathMap +f expressions. In this case, however, the values on which they operate +f are floating point values rather than the more usual pure integers. In +f order to produce results which match the pure integer case, the +f operands are regarded as fixed point binary numbers (i.e. with the +f binary equivalent of a decimal point) with negative numbers +f represented using twos-complement notation. For integer values, the +f resulting bit pattern corresponds to that of the equivalent signed +f integer (digits to the right of the point being zero). Operations on +f the bits representing the fractional part are also possible, however. +* +* The following bitwise operators are available: +c - x1 >> x2: Rightward bit shift. The integer value of "x2" is taken +c (rounding towards zero) and the bits representing "x1" are then +c shifted this number of places to the right (or to the left if the +c number of places is negative). This is equivalent to dividing "x1" by +c the corresponding power of 2. +f - X1 >> X2: Rightward bit shift. The integer value of X2 is taken +f (rounding towards zero) and the bits representing X1 are then +f shifted this number of places to the right (or to the left if the +f number of places is negative). This is equivalent to dividing X1 by +f the corresponding power of 2. +c - x1 << x2: Leftward bit shift. The integer value of "x2" is taken +c (rounding towards zero), and the bits representing "x1" are then +c shifted this number of places to the left (or to the right if the +c number of places is negative). This is equivalent to multiplying "x1" +c by the corresponding power of 2. +f - X1 << X2: Leftward bit shift. The integer value of X2 is taken +f (rounding towards zero), and the bits representing X1 are then +f shifted this number of places to the left (or to the right if the +f number of places is negative). This is equivalent to multiplying X1 +f by the corresponding power of 2. +c - x1 & x2: Bitwise AND between the bits of "x1" and those of "x2" +c (equivalent to a boolean AND applied at each bit position in turn). +f - X1 & X2: Bitwise AND between the bits of X1 and those of X2 +f (equivalent to a logical AND applied at each bit position in turn). +c - x1 | x2: Bitwise OR between the bits of "x1" and those of "x2" +c (equivalent to a boolean OR applied at each bit position in turn). +f - X1 | X2: Bitwise OR between the bits of X1 and those of X2 +f (equivalent to a logical OR applied at each bit position in turn). +c - x1 ^ x2: Bitwise exclusive OR (XOR) between the bits of "x1" and +c those of "x2" (equivalent to a boolean XOR applied at each bit +c position in turn). +f - X1 ^ X2: Bitwise exclusive OR (XOR) between the bits of X1 and +f those of X2 (equivalent to a logical XOR applied at each bit +f position in turn). +* +c Note that no bit inversion operator ("~" in C) is provided. This is +c because inverting the bits of a twos-complement fixed point binary +c number is equivalent to simply negating it. This differs from the +c pure integer case because bits to the right of the binary point are +c also inverted. To invert only those bits to the left of the binary +c point, use a bitwise exclusive OR with the value -1 (i.e. "x^-1"). +f Note that no bit inversion operator is provided. This is +f because inverting the bits of a twos-complement fixed point binary +f number is equivalent to simply negating it. This differs from the +f pure integer case because bits to the right of the binary point are +f also inverted. To invert only those bits to the left of the binary +f point, use a bitwise exclusive OR with the value -1 (i.e. X^-1). + +* Functions: +* The following functions are available: +c - abs(x): Absolute value of "x" (sign removal), same as fabs(x). +f - ABS(X): Absolute value of X (sign removal), same as FABS(X). +c - acos(x): Inverse cosine of "x", in radians. +f - ACOS(X): Inverse cosine of X, in radians. +c - acosd(x): Inverse cosine of "x", in degrees. +f - ACOSD(X): Inverse cosine of X, in degrees. +c - acosh(x): Inverse hyperbolic cosine of "x". +f - ACOSH(X): Inverse hyperbolic cosine of X. +c - acoth(x): Inverse hyperbolic cotangent of "x". +f - ACOTH(X): Inverse hyperbolic cotangent of X. +c - acsch(x): Inverse hyperbolic cosecant of "x". +f - ACSCH(X): Inverse hyperbolic cosecant of X. +c - aint(x): Integer part of "x" (round towards zero), same as int(x). +f - AINT(X): Integer part of X (round towards zero), same as INT(X). +c - asech(x): Inverse hyperbolic secant of "x". +f - ASECH(X): Inverse hyperbolic secant of X. +c - asin(x): Inverse sine of "x", in radians. +f - ASIN(X): Inverse sine of X, in radians. +c - asind(x): Inverse sine of "x", in degrees. +f - ASIND(X): Inverse sine of X, in degrees. +c - asinh(x): Inverse hyperbolic sine of "x". +f - ASINH(X): Inverse hyperbolic sine of X. +c - atan(x): Inverse tangent of "x", in radians. +f - ATAN(X): Inverse tangent of X, in radians. +c - atand(x): Inverse tangent of "x", in degrees. +f - ATAND(X): Inverse tangent of X, in degrees. +c - atanh(x): Inverse hyperbolic tangent of "x". +f - ATANH(X): Inverse hyperbolic tangent of X. +c - atan2(x1, x2): Inverse tangent of "x1/x2", in radians. +f - ATAN2(X1, X2): Inverse tangent of X1/X2, in radians. +c - atan2d(x1, x2): Inverse tangent of "x1/x2", in degrees. +f - ATAN2D(X1, X2): Inverse tangent of X1/X2, in degrees. +c - ceil(x): Smallest integer value not less then "x" (round towards +c plus infinity). +f - CEIL(X): Smallest integer value not less then X (round towards +f plus infinity). +c - cos(x): Cosine of "x" in radians. +f - COS(X): Cosine of X in radians. +c - cosd(x): Cosine of "x" in degrees. +f - COSD(X): Cosine of X in degrees. +c - cosh(x): Hyperbolic cosine of "x". +f - COSH(X): Hyperbolic cosine of X. +c - coth(x): Hyperbolic cotangent of "x". +f - COTH(X): Hyperbolic cotangent of X. +c - csch(x): Hyperbolic cosecant of "x". +f - CSCH(X): Hyperbolic cosecant of X. +c - dim(x1, x2): Returns "x1-x2" if "x1" is greater than "x2", otherwise 0. +f - DIM(X1, X2): Returns X1-X2 if X1 is greater than X2, otherwise 0. +c - exp(x): Exponential function of "x". +f - EXP(X): Exponential function of X. +c - fabs(x): Absolute value of "x" (sign removal), same as abs(x). +f - FABS(X): Absolute value of X (sign removal), same as ABS(X). +c - floor(x): Largest integer not greater than "x" (round towards +c minus infinity). +f - FLOOR(X): Largest integer not greater than X (round towards +f minus infinity). +c - fmod(x1, x2): Remainder when "x1" is divided by "x2", same as +c mod(x1, x2). +f - FMOD(X1, X2): Remainder when X1 is divided by X2, same as +f MOD(X1, X2). +c - gauss(x1, x2): Random sample from a Gaussian distribution with mean +c "x1" and standard deviation "x2". +f - GAUSS(X1, X2): Random sample from a Gaussian distribution with mean +f X1 and standard deviation X2. +c - int(x): Integer part of "x" (round towards zero), same as aint(x). +f - INT(X): Integer part of X (round towards zero), same as AINT(X). +c - isbad(x): Returns 1 if "x" has the value (AST__BAD), otherwise 0. +f - ISBAD(X): Returns 1 if X has the value (AST__BAD), otherwise 0. +c - log(x): Natural logarithm of "x". +f - LOG(X): Natural logarithm of X. +c - log10(x): Logarithm of "x" to base 10. +f - LOG10(X): Logarithm of X to base 10. +c - max(x1, x2, ...): Maximum of two or more values. +f - MAX(X1, X2, ...): Maximum of two or more values. +c - min(x1, x2, ...): Minimum of two or more values. +f - MIN(X1, X2, ...): Minimum of two or more values. +c - mod(x1, x2): Remainder when "x1" is divided by "x2", same as +c fmod(x1, x2). +f - MOD(X1, X2): Remainder when X1 is divided by X2, same as +f FMOD(X1, X2). +c - nint(x): Nearest integer to "x" (round to nearest). +f - NINT(X): Nearest integer to X (round to nearest). +c - poisson(x): Random integer-valued sample from a Poisson +c distribution with mean "x". +f - POISSON(X): Random integer-valued sample from a Poisson +f distribution with mean X. +c - pow(x1, x2): "x1" raised to the power of "x2". +f - POW(X1, X2): X1 raised to the power of X2. +c - qif(x1, x2, x3): Returns "x2" if "x1" is true, and "x3" otherwise. +f - QIF(x1, x2, x3): Returns X2 if X1 is true, and X3 otherwise. +c - rand(x1, x2): Random sample from a uniform distribution in the +c range "x1" to "x2" inclusive. +f - RAND(X1, X2): Random sample from a uniform distribution in the +f range X1 to X2 inclusive. +c - sech(x): Hyperbolic secant of "x". +f - SECH(X): Hyperbolic secant of X. +c - sign(x1, x2): Absolute value of "x1" with the sign of "x2" +c (transfer of sign). +f - SIGN(X1, X2): Absolute value of X1 with the sign of X2 +f (transfer of sign). +c - sin(x): Sine of "x" in radians. +f - SIN(X): Sine of X in radians. +c - sinc(x): Sinc function of "x" [= "sin(x)/x"]. +f - SINC(X): Sinc function of X [= SIN(X)/X]. +c - sind(x): Sine of "x" in degrees. +f - SIND(X): Sine of X in degrees. +c - sinh(x): Hyperbolic sine of "x". +f - SINH(X): Hyperbolic sine of X. +c - sqr(x): Square of "x" (= "x*x"). +f - SQR(X): Square of X (= X*X). +c - sqrt(x): Square root of "x". +f - SQRT(X): Square root of X. +c - tan(x): Tangent of "x" in radians. +f - TAN(X): Tangent of X in radians. +c - tand(x): Tangent of "x" in degrees. +f - TAND(X): Tangent of X in degrees. +c - tanh(x): Hyperbolic tangent of "x". +f - TANH(X): Hyperbolic tangent of X. + +* Symbolic Constants: +* The following symbolic constants are available (the enclosing "<>" +* brackets must be included): +c - : The "bad" value (AST__BAD) used to flag missing data. Note +c that you cannot usefully compare values with this constant because the +c result is always . The isbad() function should be used instead. +f - : The "bad" value (AST__BAD) used to flag missing data. Note +f that you cannot usefully compare values with this constant because the +f result is always . The ISBAD() function should be used instead. +c - : Number of decimal digits of precision available in a +c floating point (double) value. +f - : Number of decimal digits of precision available in a +f floating point (double precision) value. +* - : Base of natural logarithms. +* - : Smallest positive number such that 1.0+ is +* distinguishable from unity. +c - : The number of base digits stored in the +c mantissa of a floating point (double) value. +f - : The number of base digits stored in the +f mantissa of a floating point (double precision) value. +c - : Maximum representable floating point (double) value. +f - : Maximum representable floating point (double precision) value. +c - : Maximum integer such that 10 raised to that power +c can be represented as a floating point (double) value. +f - : Maximum integer such that 10 raised to that power +f can be represented as a floating point (double precision) value. +c - : Maximum integer such that raised to that +c power minus 1 can be represented as a floating point (double) value. +f - : Maximum integer such that raised to that +f power minus 1 can be represented as a floating point (double precision) +f value. +c - : Smallest positive number which can be represented as a +c normalised floating point (double) value. +f - : Smallest positive number which can be represented as a +f normalised floating point (double precision) value. +c - : Minimum negative integer such that 10 raised to that +c power can be represented as a normalised floating point (double) value. +f - : Minimum negative integer such that 10 raised to that +f power can be represented as a normalised floating point (double +f precision) value. +c - : Minimum negative integer such that raised to +c that power minus 1 can be represented as a normalised floating point +c (double) value. +f - : Minimum negative integer such that raised to +f that power minus 1 can be represented as a normalised floating point +f (double precision) value. +* - : Ratio of the circumference of a circle to its diameter. +c - : The radix (number base) used to represent the mantissa of +c floating point (double) values. +f - : The radix (number base) used to represent the mantissa of +f floating point (double precision) values. +* - : The mode used for rounding floating point results after +* addition. Possible values include: -1 (indeterminate), 0 (toward +* zero), 1 (to nearest), 2 (toward plus infinity) and 3 (toward minus +* infinity). Other values indicate machine-dependent behaviour. + +* Evaluation Precedence and Associativity: +* Items appearing in expressions are evaluated in the following order +* (highest precedence first): +* - Constants and variables +* - Function arguments and parenthesised expressions +* - Function invocations +* - Unary + - ! .not. +* - ** +* - * / +* - + - +* - << >> +* - < .lt. <= .le. > .gt. >= .ge. +* - == .eq. != .ne. +* - & +* - ^ +* - | +* - && .and. +* - ^^ +* - || .or +* - .eqv. .neqv. .xor. +* +* All operators associate from left-to-right, except for unary +, +* unary -, !, .not. and ** which associate from right-to-left. + +* Notes: +* - The sequence of numbers produced by the random number functions +* available within a MathMap is normally unpredictable and different for +* each MathMap. However, this behaviour may be controlled by means of +* the MathMap's Seed attribute. +c - Normally, compound Mappings (CmpMaps) which involve MathMaps will +c not be subject to simplification (e.g. using astSimplify) because AST +c cannot know how different MathMaps will interact. However, in the +c special case where a MathMap occurs in series with its own inverse, +c then simplification may be possible. Whether simplification does, in +c fact, occur under these circumstances is controlled by the MathMap's +c SimpFI and SimpIF attributes. +f - Normally, compound Mappings (CmpMaps) which involve MathMaps will +f not be subject to simplification (e.g. using AST_SIMPLIFY) because AST +f cannot know how different MathMaps will interact. However, in the +f special case where a MathMap occurs in series with its own inverse, +f then simplification may be possible. Whether simplification does, in +f fact, occur under these circumstances is controlled by the MathMap's +f SimpFI and SimpIF attributes. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astMathMap constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astMathMap_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astMathMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMathMap *new; /* Pointer to new MathMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the MathMap, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitMathMap( NULL, sizeof( AstMathMap ), !class_init, &class_vtab, + "MathMap", nin, nout, nfwd, fwd, ninv, inv ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new MathMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new MathMap. */ + return astMakeId( new ); +} + +AstMathMap *astInitMathMap_( void *mem, size_t size, int init, + AstMathMapVtab *vtab, const char *name, + int nin, int nout, + int nfwd, const char *fwd[], + int ninv, const char *inv[], int *status ) { +/* +*+ +* Name: +* astInitMathMap + +* Purpose: +* Initialise a MathMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "mathmap.h" +* AstMathMap *astInitMathMap_( void *mem, size_t size, int init, +* AstMathMapVtab *vtab, const char *name, +* int nin, int nout, +* int nfwd, const char *fwd[], +* int ninv, const char *inv[] ) + +* Class Membership: +* MathMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new MathMap object. It allocates memory (if necessary) to accommodate +* the MathMap plus any additional data associated with the derived class. +* It then initialises a MathMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a MathMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the MathMap is to be initialised. +* This must be of sufficient size to accommodate the MathMap data +* (sizeof(MathMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the MathMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the MathMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the MathMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new MathMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* nin +* Number of input variables for the MathMap. +* nout +* Number of output variables for the MathMap. +* nfwd +* The number of forward transformation functions being supplied. +* This must be at least equal to "nout". +* fwd +* Pointer to an array, with "nfwd" elements, of pointers to null +* terminated strings which contain each of the forward transformation +* functions. +* ninv +* The number of inverse transformation functions being supplied. +* This must be at least equal to "nin". +* inv +* Pointer to an array, with "ninv" elements, of pointers to null +* terminated strings which contain each of the inverse transformation +* functions. + +* Returned Value: +* A pointer to the new MathMap. + +* Notes: +* - This function does not attempt to ensure that the forward and inverse +* transformations performed by the resulting MathMap are consistent in any +* way. +* - This function makes a copy of the contents of the strings supplied. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstMathMap *new; /* Pointer to new MathMap */ + char **fwdfun; /* Array of cleaned forward functions */ + char **invfun; /* Array of cleaned inverse functions */ + double **fwdcon; /* Constants for forward functions */ + double **invcon; /* Constants for inverse functions */ + int **fwdcode; /* Code for forward functions */ + int **invcode; /* Code for inverse functions */ + int fwdstack; /* Stack size for forward functions */ + int invstack; /* Stack size for inverse functions */ + +/* Initialise. */ + new = NULL; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitMathMapVtab( vtab, name ); + +/* Check the numbers of input and output variables for validity, + reporting an error if necessary. */ + if ( nin < 1 ) { + astError( AST__BADNI, + "astInitMathMap(%s): Bad number of input coordinates (%d).", status, + name, nin ); + astError( AST__BADNI, + "This number should be one or more." , status); + } else if ( nout < 1 ) { + astError( AST__BADNO, + "astInitMathMap(%s): Bad number of output coordinates (%d).", status, + name, nout ); + astError( AST__BADNI, + "This number should be one or more." , status); + +/* Check that sufficient number of forward and inverse transformation + functions have been supplied and report an error if necessary. */ + } else if ( nfwd < nout ) { + astError( AST__INNTF, + "astInitMathMap(%s): Too few forward transformation functions " + "given (%d).", status, + name, nfwd ); + astError( astStatus, + "At least %d forward transformation functions must be " + "supplied. ", status, + nout ); + } else if ( ninv < nin ) { + astError( AST__INNTF, + "astInitMathMap(%s): Too few inverse transformation functions " + "given (%d).", status, + name, ninv ); + astError( astStatus, + "At least %d inverse transformation functions must be " + "supplied. ", status, + nin ); + +/* Of OK, clean the forward and inverse functions provided. This makes + a lower-case copy with white space removed. */ + } else { + CleanFunctions( nfwd, fwd, &fwdfun, status ); + CleanFunctions( ninv, inv, &invfun, status ); + +/* Compile the cleaned functions. From the returned pointers (if + successful), we can now tell which transformations (forward and/or + inverse) are defined. */ + CompileMapping( "astInitMathMap", name, nin, nout, + nfwd, (const char **) fwdfun, + ninv, (const char **) invfun, + &fwdcode, &invcode, &fwdcon, &invcon, + &fwdstack, &invstack, status ); + +/* Initialise a Mapping structure (the parent class) as the first + component within the MathMap structure, allocating memory if + necessary. Specify that the Mapping should be defined in the required + directions. */ + new = (AstMathMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nout, + ( fwdcode != NULL ), + ( invcode != NULL ) ); + + +/* If an error has occurred, free all the memory which may have been + allocated by the cleaning and compilation steps above. */ + if ( !astOK ) { + FREE_POINTER_ARRAY( fwdfun, nfwd ) + FREE_POINTER_ARRAY( invfun, ninv ) + FREE_POINTER_ARRAY( fwdcode, nfwd ) + FREE_POINTER_ARRAY( invcode, ninv ) + FREE_POINTER_ARRAY( fwdcon, nfwd ) + FREE_POINTER_ARRAY( invcon, ninv ) + } + +/* Initialise the MathMap data. */ +/* ---------------------------- */ +/* Store pointers to the compiled function information, together with + other MathMap data. */ + if ( new ) { + new->fwdfun = fwdfun; + new->invfun = invfun; + new->fwdcode = fwdcode; + new->invcode = invcode; + new->fwdcon = fwdcon; + new->invcon = invcon; + new->fwdstack = fwdstack; + new->invstack = invstack; + new->nfwd = nfwd; + new->ninv = ninv; + new->simp_fi = -INT_MAX; + new->simp_if = -INT_MAX; + +/* Initialise the random number generator context associated with the + MathMap, using an unpredictable default seed value. */ + new->rcontext.active = 0; + new->rcontext.random_int = 0; + new->rcontext.seed_set = 0; + new->rcontext.seed = DefaultSeed( &new->rcontext, status ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstMathMap *astLoadMathMap_( void *mem, size_t size, + AstMathMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadMathMap + +* Purpose: +* Load a MathMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "mathmap.h" +* AstMathMap *astLoadMathMap( void *mem, size_t size, +* AstMathMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* MathMap loader. + +* Description: +* This function is provided to load a new MathMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* MathMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a MathMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the MathMap is to be +* loaded. This must be of sufficient size to accommodate the +* MathMap data (sizeof(MathMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the MathMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the MathMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstMathMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new MathMap. If this is NULL, a pointer +* to the (static) virtual function table for the MathMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "MathMap" is used instead. + +* Returned Value: +* A pointer to the new MathMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstMathMap *new; /* Pointer to the new MathMap */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword strings */ + int ifun; /* Loop counter for functions */ + int invert; /* Invert attribute value */ + int nin; /* True number of input coordinates */ + int nout; /* True number of output coordinates */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this MathMap. In this case the + MathMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstMathMap ); + vtab = &class_vtab; + name = "MathMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitMathMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built MathMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "MathMap" ); + +/* Determine if the MathMap is inverted and obtain the "true" number + of input and output coordinates by un-doing the effects of any + inversion. */ + invert = astGetInvert( new ); + nin = invert ? astGetNout( new ) : astGetNin( new ); + nout = invert ? astGetNin( new ) : astGetNout( new ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Numbers of transformation functions. */ +/* ------------------------------------ */ +/* Read the numbers of forward and inverse transformation functions, + supplying appropriate defaults. */ + new->nfwd = astReadInt( channel, "nfwd", nout ); + new->ninv = astReadInt( channel, "ninv", nin ); + if ( astOK ) { + +/* Allocate memory for the MathMap's transformation function arrays. */ + MALLOC_POINTER_ARRAY( new->fwdfun, char *, new->nfwd ) + MALLOC_POINTER_ARRAY( new->invfun, char *, new->ninv ) + if ( astOK ) { + +/* Forward transformation functions. */ +/* --------------------------------- */ +/* Create a keyword for each forward transformation function and read + the function's value as a string. */ + for ( ifun = 0; ifun < new->nfwd; ifun++ ) { + (void) sprintf( key, "fwd%d", ifun + 1 ); + new->fwdfun[ ifun ] = astReadString( channel, key, "" ); + } + +/* Inverse transformation functions. */ +/* --------------------------------- */ +/* Repeat this process for the inverse transformation functions. */ + for ( ifun = 0; ifun < new->ninv; ifun++ ) { + (void) sprintf( key, "inv%d", ifun + 1 ); + new->invfun[ ifun ] = astReadString( channel, key, "" ); + } + +/* Forward-inverse simplification flag. */ +/* ------------------------------------ */ + new->simp_fi = astReadInt( channel, "simpfi", -INT_MAX ); + if ( TestSimpFI( new, status ) ) SetSimpFI( new, new->simp_fi, status ); + +/* Inverse-forward simplification flag. */ +/* ------------------------------------ */ + new->simp_if = astReadInt( channel, "simpif", -INT_MAX ); + if ( TestSimpIF( new, status ) ) SetSimpIF( new, new->simp_if, status ); + +/* Random number context. */ +/* ---------------------- */ +/* Initialise the random number generator context. */ + new->rcontext.active = 0; + new->rcontext.random_int = 0; + +/* Read the flag that determines if the Seed value is set, and the + Seed value itself. */ + new->rcontext.seed_set = astReadInt( channel, "seeded", 0 ); + if ( TestSeed( new, status ) ) { + new->rcontext.seed = astReadInt( channel, "seed", 0 ); + SetSeed( new, new->rcontext.seed, status ); + +/* Supply an unpredictable default Seed value if necessary. */ + } else { + new->rcontext.seed = DefaultSeed( &new->rcontext, status ); + } + +/* Compile the MathMap's transformation functions. */ + CompileMapping( "astLoadMathMap", name, nin, nout, + new->nfwd, (const char **) new->fwdfun, + new->ninv, (const char **) new->invfun, + &new->fwdcode, &new->invcode, + &new->fwdcon, &new->invcon, + &new->fwdstack, &new->invstack, status ); + } + +/* If an error occurred, clean up by deleting the new MathMap. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return the new MathMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + + + + + diff --git a/mathmap.h b/mathmap.h new file mode 100644 index 0000000..e3b4004 --- /dev/null +++ b/mathmap.h @@ -0,0 +1,410 @@ +#if !defined( MATHMAP_INCLUDED ) /* Include this file only once */ +#define MATHMAP_INCLUDED +/* +*+ +* Name: +* mathmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the MathMap class. + +* Invocation: +* #include "mathmap.h" + +* Description: +* This include file defines the interface to the MathMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The MathMap class implements Mappings that are specified by a series +* of arithmetic expressions that relate output variables to input +* variables (and vice versa). + +* Inheritance: +* The MathMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* Seed +* Random number seed. +* SimpFI +* Forward-inverse MathMap pairs simplify? +* SimpIF +* Inverse-forward MathMap pairs simplify? + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astClearAttrib +* Clear an attribute value for a Frame. +* astGetAttrib +* Get an attribute value for a Frame. +* astMapMerge +* Simplify a sequence of Mappings containing a MathMap. +* astSetAttrib +* Set an attribute value for a Frame. +* astTestAttrib +* Test if an attribute value has been set for a Frame. +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astClearSeed +* Clear the Seed attribute for a MathMap. +* astClearSimpFI +* Clear the SimpFI attribute for a MathMap. +* astClearSimpIF +* Clear the SimpIF attribute for a MathMap. +* astGetSeed +* Get the value of the Seed attribute for a MathMap. +* astGetSimpFI +* Get the value of the SimpFI attribute for a MathMap. +* astGetSimpIF +* Get the value of the SimpIF attribute for a MathMap. +* astSetSeed +* Set the value of the Seed attribute for a MathMap. +* astSetSimpFI +* Set the value of the SimpFI attribute for a MathMap. +* astSetSimpIF +* Set the value of the SimpIF attribute for a MathMap. +* astTestSeed +* Test whether a value has been set for the Seed attribute of a +* MathMap. +* astTestSimpFI +* Test whether a value has been set for the SimpFI attribute of a +* MathMap. +* astTestSimpIF +* Test whether a value has been set for the SimpIF attribute of a +* MathMap. + +* Other Class Functions: +* Public: +* astIsAMathMap +* Test class membership. +* astMathMap +* Create a MathMap. +* +* Protected: +* astCheckMathMap +* Validate class membership. +* astInitMathMap +* Initialise a MathMap. +* astInitMathMapVtab +* Initialise the virtual function table for the MathMap class. +* astLoadMathMap +* Load a MathMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstMathMap +* MathMap object type. +* +* Protected: +* AstMathMapVtab +* MathMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 3-SEP-1999 (RFWS): +* Original version. +* 8-JAN-2003 (DSB): +* Added protected astInitMathMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#include "pointset.h" /* Sets of points/coordinates */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ======= */ +/* This value defines the size of an internal table in the AstMathMap + data type. Since it will be publicly accessible (but of no external + use), we give it an obscure name. */ + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST_MATHMAP_RAND_CONTEXT_NTAB_ (32) + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* Random number generator context. */ +/* -------------------------------- */ +/* This structure holds the context for the random number generator + used by each MathMap. This ensures that the random number sequences + used by different MathMaps are independent, and can be independently + controlled by setting/clearing their Seed attributes. Random numbers + are produced by combining the output of two internal generators. */ +typedef struct AstMathMapRandContext_ { + long int rand1; /* State of first internal generator */ + long int rand2; /* State of second internal generator */ + long int random_int; /* Last random integer produced */ + long int table[ AST_MATHMAP_RAND_CONTEXT_NTAB_ ]; /* Shuffle table */ + int active; /* Generator has been initialised? */ + int seed; /* Seed to be used during initialisation */ + int seed_set; /* Seed value set via "Seed" attribute? */ +} AstMathMapRandContext_; + +/* MathMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstMathMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstMathMapRandContext_ rcontext; /* Random number generator context */ + char **fwdfun; /* Array of forward functions */ + char **invfun; /* Array of inverse functions */ + double **fwdcon; /* Array of constants for forward functions */ + double **invcon; /* Array of constants for inverse functions */ + int **fwdcode; /* Array of opcodes for forward functions */ + int **invcode; /* Array of opcodes for inverse functions */ + int fwdstack; /* Stack size required by forward functions */ + int invstack; /* Stack size required by inverse functions */ + int nfwd; /* Number of forward functions */ + int ninv; /* Number of inverse functions */ + int simp_fi; /* Forward-inverse MathMap pairs simplify? */ + int simp_if; /* Inverse-forward MathMap pairs simplify? */ +} AstMathMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstMathMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* GetSeed)( AstMathMap *, int * ); + int (* GetSimpFI)( AstMathMap *, int * ); + int (* GetSimpIF)( AstMathMap *, int * ); + int (* TestSeed)( AstMathMap *, int * ); + int (* TestSimpFI)( AstMathMap *, int * ); + int (* TestSimpIF)( AstMathMap *, int * ); + void (* ClearSeed)( AstMathMap *, int * ); + void (* ClearSimpFI)( AstMathMap *, int * ); + void (* ClearSimpIF)( AstMathMap *, int * ); + void (* SetSeed)( AstMathMap *, int, int * ); + void (* SetSimpFI)( AstMathMap *, int, int * ); + void (* SetSimpIF)( AstMathMap *, int, int * ); +} AstMathMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstMathMapGlobals { + AstMathMapVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 51 ]; +} AstMathMapGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(MathMap) /* Check class membership */ +astPROTO_ISA(MathMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstMathMap *astMathMap_( int, int, int, const char *[], int, const char *[], + const char *, int *, ...); +#else +AstMathMap *astMathMapId_( int, int, int, const char *[], int, const char *[], + const char *, ... )__attribute__((format(printf,7,8))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstMathMap *astInitMathMap_( void *, size_t, int, AstMathMapVtab *, + const char *, int, int, + int, const char *[], int, const char *[], int * ); + +/* Vtab initialiser. */ +void astInitMathMapVtab_( AstMathMapVtab *, const char *, int * ); + +/* Loader. */ +AstMathMap *astLoadMathMap_( void *, size_t, AstMathMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitMathMapGlobals_( AstMathMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +#if defined(astCLASS) /* Protected */ +int astGetSeed_( AstMathMap *, int * ); +int astGetSimpFI_( AstMathMap *, int * ); +int astGetSimpIF_( AstMathMap *, int * ); +int astTestSeed_( AstMathMap *, int * ); +int astTestSimpFI_( AstMathMap *, int * ); +int astTestSimpIF_( AstMathMap *, int * ); +void astClearSeed_( AstMathMap *, int * ); +void astClearSimpFI_( AstMathMap *, int * ); +void astClearSimpIF_( AstMathMap *, int * ); +void astSetSeed_( AstMathMap *, int, int * ); +void astSetSimpFI_( AstMathMap *, int, int * ); +void astSetSimpIF_( AstMathMap *, int, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckMathMap(this) astINVOKE_CHECK(MathMap,this,0) +#define astVerifyMathMap(this) astINVOKE_CHECK(MathMap,this,1) + +/* Test class membership. */ +#define astIsAMathMap(this) astINVOKE_ISA(MathMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astMathMap astINVOKE(F,astMathMap_) +#else +#define astMathMap astINVOKE(F,astMathMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitMathMap(mem,size,init,vtab,name,nin,nout,nfwd,fwd,ninv,inv) \ +astINVOKE(O,astInitMathMap_(mem,size,init,vtab,name,nin,nout,nfwd,fwd,ninv,inv,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitMathMapVtab(vtab,name) astINVOKE(V,astInitMathMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadMathMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadMathMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckMathMap to validate MathMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +#if defined(astCLASS) /* Protected */ +#define astClearSeed(this) \ +astINVOKE(V,astClearSeed_(astCheckMathMap(this),STATUS_PTR)) +#define astClearSimpFI(this) \ +astINVOKE(V,astClearSimpFI_(astCheckMathMap(this),STATUS_PTR)) +#define astClearSimpIF(this) \ +astINVOKE(V,astClearSimpIF_(astCheckMathMap(this),STATUS_PTR)) +#define astGetSeed(this) \ +astINVOKE(V,astGetSeed_(astCheckMathMap(this),STATUS_PTR)) +#define astGetSimpFI(this) \ +astINVOKE(V,astGetSimpFI_(astCheckMathMap(this),STATUS_PTR)) +#define astGetSimpIF(this) \ +astINVOKE(V,astGetSimpIF_(astCheckMathMap(this),STATUS_PTR)) +#define astSetSeed(this,value) \ +astINVOKE(V,astSetSeed_(astCheckMathMap(this),value,STATUS_PTR)) +#define astSetSimpFI(this,value) \ +astINVOKE(V,astSetSimpFI_(astCheckMathMap(this),value,STATUS_PTR)) +#define astSetSimpIF(this,value) \ +astINVOKE(V,astSetSimpIF_(astCheckMathMap(this),value,STATUS_PTR)) +#define astTestSeed(this) \ +astINVOKE(V,astTestSeed_(astCheckMathMap(this),STATUS_PTR)) +#define astTestSimpFI(this) \ +astINVOKE(V,astTestSimpFI_(astCheckMathMap(this),STATUS_PTR)) +#define astTestSimpIF(this) \ +astINVOKE(V,astTestSimpIF_(astCheckMathMap(this),STATUS_PTR)) +#endif +#endif + + + + + diff --git a/matrixmap.c b/matrixmap.c new file mode 100644 index 0000000..4ffc3f7 --- /dev/null +++ b/matrixmap.c @@ -0,0 +1,5501 @@ +/* +*class++ +* Name: +* MatrixMap + +* Purpose: +* Map coordinates by multiplying by a matrix. + +* Constructor Function: +c astMatrixMap +f AST_MATRIXMAP + +* Description: +* A MatrixMap is form of Mapping which performs a general linear +* transformation. Each set of input coordinates, regarded as a +* column-vector, are pre-multiplied by a matrix (whose elements +* are specified when the MatrixMap is created) to give a new +* column-vector containing the output coordinates. If appropriate, +* the inverse transformation may also be performed. + +* Inheritance: +* The MatrixMap class inherits from the Mapping class. + +* Attributes: +* The MatrixMap class does not define any new attributes beyond +* those which are applicable to all Mappings. + +* Functions: +c The MatrixMap class does not define any new functions beyond those +f The MatrixMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 9-FEB-1996 (DSB): +* Original version. +* 13-NOV-1996 (DSB): +* Updated to support attributes, I/O and an external interface. +* 3-JUN-1997 (DSB): +* astMtrMult and astMtrRot made protected instead of public. +* 16-JUN-1997 (RFWS): +* Tidied public prologues. +* 24-JUN-1997 (DSB): +* Zero returned for coordinates which are indeterminate as a +* result of using an inverted, non-square, diagonal matrix. +* 10-OCT-1997 (DSB): +* o The inverse matrix is no longer dumped by the Dump function. +* Instead, it is re-calculated by the Load function. +* o The description of argument "form" in astMatrixMap corrected +* to indicate that a value of 2 produces a unit matrix. +* o String values used to represent choices externally, instead +* of integers. +* 24-NOV-1997 (DSB): +* Use of error code AST__OPT replaced by AST__RDERR. +* 28-JAN-1998 (DSB): +* Bug fix in astMtrMult: the matrix (forward or inverse) used for +* the "a" MatrixMap was determined by the Invert flag of the other +* ("this") MatrixMap. +* 14-APR-1998 (DSB): +* Bug fix in Dump. Previously, matrix elements with value AST__BAD +* were explicitly written out. Now they are not written out, since +* AST__BAD can have different values on different machines. Missing +* elements default to AST__BAD when read back in using astLoadMatrixMap. +* 20-APR-1998 (DSB): +* Bug fix in astLoadMatrixMap: initialise the pointer to the inverse +* matrix array to NULL if no inverse matrix is needed. +* 25-AUG-1998 (DSB): +* - Transform changed so that bad input axis values are not +* propagated to output axes which are independant of the input axis. +* - CompressMatrix changed to allow a tolerance of DBL_EPSILON when +* determining if a matrix is a unit matrix, or a diagonal matrix. +* - MapMerge changed to allow MatrixMaps to swap with PermMaps +* in order to move the MatrixMap closer to a Mapping with which it +* could merge. +* 22-FEB-1999 (DSB): +* Changed logic of MapMerge to avoid infinite looping. +* 5-MAY-1999 (DSB): +* More corrections to MapMerge: Cleared up errors in the use of the +* supplied invert flags, and corrected logic for deciding which +* neighbouring Mapping to swap with. +* 16-JUL-1999 (DSB): +* Fixed memory leaks in MatWin and MapMerge. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitatrixMapVtab +* method. +* 11-SEP-2003 (DSB): +* Increased tolerance on checks for unit matrices within +* CompressMatrix. Now uses sqrt(DBL_EPSILON)*diag (previously was +* DBL_EPSILON*DIAG ). +* 10-NOV-2003 (DSB): +* Modified functions which swap a MatrixMap with another Mapping +* (e.g. MatSwapPerm, etc), to simplify the returned Mappings. +* 13-JAN-2003 (DSB): +* Modified the tolerance used by CompressMatrix when checking for +* zero matrix elements. Old system compared each element to thre +* size of the diagonal, but different scalings on different axes could +* cause this to trat as zero values which should nto be treated as +* zero. +* 23-APR-2004 (DSB): +* Changes to simplification algorithm. +* 8-JUL-2004 (DSB): +* astMtrMult - Report an error if either MatrixMap does not have a +* defined forward transformation. +* 1-SEP-2004 (DSB): +* Ensure do1 and do2 are initialised before use in MapMerge. +* 7-SEP-2005 (DSB): +* Take account of the Invert flag when using the zoom factor from +* a ZoomMap. +* 14-FEB-2006 (DSB): +* Correct row/col confusion in CompressMatrix. +* 15-MAR-2006 (DSB): +* Override astEqual. +* 15-MAR-2009 (DSB): +* MapSplit: Only create the returned Mapping if it would have some +* outputs. Also, do not create the returned Mapping if any output +* depends on a mixture of selected and unselected inputs. +* 16-JUL-2009 (DSB): +* MatPerm: Fix memory leak (mm2 was not being annulled). +* 2-OCT-2012 (DSB): +* - Check for Infs as well as NaNs. +* - In MapSplit do not split the MatrixMap if the resulting +* matrix would contain only bad elements. +* - Report an error if an attempt is made to create a MatrixMap +* containing only bad elements. +* 4-NOV-2013 (DSB): +* Allow a full form MatrixMap to be simplified to a diagonal form +* MatrixMap if all the off-diagonal values are zero. +* 23-APR-2015 (DSB): +* Improve MapMerge. If a MatrixMap can merge with its next-but-one +* neighbour, then swap the MatrixMap with its neighbour, so that +* it is then next its next-but-one neighbour, and then merge the +* two Mappings into a single Mapping. Previously, only the swap +* was performed - not the merger. And the swap was only performed +* if the intervening neighbour could not itself merge. This could +* result in an infinite simplification loop, which was detected by +* CmpMap and and aborted, resulting in no useful simplification. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS MatrixMap + +/* Define identifiers for the different forms of matrix storage. */ +#define FULL 0 +#define DIAGONAL 1 +#define UNIT 2 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "matrixmap.h" /* Interface definition for this class */ +#include "pal.h" /* SLALIB function definitions */ +#include "permmap.h" +#include "zoommap.h" +#include "unitmap.h" +#include "winmap.h" + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; +static const char *Form[3] = { "Full", "Diagonal", "Unit" }; /* Text values + used to represent storage form externally */ + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(MatrixMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(MatrixMap,Class_Init) +#define class_vtab astGLOBAL(MatrixMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstMatrixMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstMatrixMap *astMatrixMapId_( int, int, int, const double [], const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMatrixMap *MatMat( AstMapping *, AstMapping *, int, int, int * ); +static AstMatrixMap *MatPerm( AstMatrixMap *, AstPermMap *, int, int, int, int * ); +static AstMatrixMap *MatZoom( AstMatrixMap *, AstZoomMap *, int, int, int * ); +static AstMatrixMap *MtrMult( AstMatrixMap *, AstMatrixMap *, int * ); +static AstMatrixMap *MtrRot( AstMatrixMap *, double, const double[], int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double *InvertMatrix( int, int, int, double *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * ); +static int Ustrcmp( const char *, const char *, int * ); +static int GetTranForward( AstMapping *, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int GetTranInverse( AstMapping *, int * ); +static int CanSwap( AstMapping *, AstMapping *, int, int, int *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int PermOK( AstMapping *, int * ); +static int ScalingRowCol( AstMatrixMap *, int, int * ); +static void CompressMatrix( AstMatrixMap *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *obj, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void ExpandMatrix( AstMatrixMap *, int * ); +static void MatWin( AstMapping **, int *, int, int * ); +static void MatPermSwap( AstMapping **, int *, int, int * ); +static void PermGet( AstPermMap *, int **, int **, double **, int * ); +static void SMtrMult( int, int, int, const double *, double *, double*, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); + +/* Member functions. */ +/* ================= */ +static int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, + int *simpler, int *status ){ +/* +* Name: +* CanSwap + +* Purpose: +* Determine if two Mappings could be swapped. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, +* int *simpler, int *status ) + +* Class Membership: +* MatrixMap member function + +* Description: +* This function returns a flag indicating if the pair of supplied +* Mappings could be replaced by an equivalent pair of Mappings from the +* same classes as the supplied pair, but in reversed order. Each pair +* of Mappings is considered to be compunded in series. The supplied +* Mapings are not changed in any way. + +* Parameters: +* map1 +* The Mapping to be applied first. +* map2 +* The Mapping to be applied second. +* inv1 +* The invert flag to use with map1. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* inv2 +* The invert flag to use with map2. +* simpler +* Addresss of a location at which to return a flag indicating if +* the swapped Mappings would be intrinsically simpler than the +* original Mappings. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if the Mappings could be swapped, 0 otherwise. + +* Notes: +* - One of the supplied pair of Mappings must be a MatrixMap. +* - A value of 0 is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstMatrixMap *mat; /* Pointer to MatrixMap Mapping */ + AstMapping *nomat; /* Pointer to non-MatrixMap Mapping */ + const char *class1; /* Pointer to map1 class string */ + const char *class2; /* Pointer to map2 class string */ + const char *nomat_class; /* Pointer to non-MatrixMap class string */ + double *consts; /* Pointer to constants array */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int i; /* Loop count */ + int invert[ 2 ]; /* Original invert flags */ + int nax; /* No. of in/out coordinates for the MatrixMap */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + int ret; /* Returned flag */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise */ + ret = 0; + *simpler = 0; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + invert[ 0 ] = astGetInvert( map1 ); + astSetInvert( map1, inv1 ); + + invert[ 1 ] = astGetInvert( map2 ); + astSetInvert( map2, inv2 ); + +/* Get the classes of the two mappings. */ + class1 = astGetClass( map1 ); + class2 = astGetClass( map2 ); + if( astOK ){ + +/* Get a pointer to the MatrixMap and non-MatrixMap Mappings. */ + if( !strcmp( class1, "MatrixMap" ) ){ + mat = (AstMatrixMap *) map1; + nomat = map2; + nomat_class = class2; + } else { + nomat = map1; + mat = (AstMatrixMap *) map2; + nomat_class = class1; + } + +/* Get the number of input axes for the MatrixMap. */ + nax = astGetNin( mat ); + +/* If it is a WinMap, the Mappings can be swapped. */ + if( !strcmp( nomat_class, "WinMap" ) ){ + ret = 1; + +/* If it is a PermMap, the Mappings can be swapped so long as: + 1) all links between input and output axes in the PermMap are + bi-directional. This does not preclude the existence of unconnected + axes, which do not have links (bi-directional or otherwise). + 2) The MatrixMap is square, and invertable. + 3) If the permMap is applied first, then each output of the PermMap + which is assigned a constant value must correspond to a "scaling" row + and column in the MatrixMap. I.e. if PermMap output axis "i" is + assigned a constant value, then row i and column i of the following + MatrixMap must contain only zeros, EXCEPT for the diagonal term (row + i, column i) which must be non-zero. If the Mappings are in the other + order, then the same applies to PermMap input axes assigned a constant + value. */ + +/* Check the other Mapping is a PermMap, and that the MatrixMap is square + and has an inverse. */ + } else if( !strcmp( nomat_class, "PermMap" ) && + nax == astGetNout( mat ) && ( mat->form == UNIT || + ( mat->i_matrix != NULL && + mat->f_matrix != NULL ) ) ) { + +/* Get the number of input and output coordinates for the PermMap. */ + nin = astGetNin( nomat ); + nout = astGetNout( nomat ); + +/* We need to know the axis permutation arrays and constants array for + the PermMap. */ + PermGet( (AstPermMap *) nomat, &outperm, &inperm, &consts, status ); + if( astOK ) { + +/* Indicate we can swap with the PermMap. */ + ret = 1; + +/* Check each output axis. If any links between axes are found which are + not bi-directional, indicate that we cannot swap with the PermMap. */ + for( i = 0; i < nout; i++ ){ + if( outperm[ i ] >= 0 && outperm[ i ] < nin ) { + if( inperm[ outperm[ i ] ] != i ) { + ret = 0; + break; + } + } + } + +/* Check each input axis. If any links between axes are found which are + not bi-directional, indicate that we cannot swap with the PermMap. */ + for( i = 0; i < nin; i++ ){ + if( inperm[ i ] >= 0 && inperm[ i ] < nout ) { + if( outperm[ inperm[ i ] ] != i ) { + ret = 0; + break; + } + } + } + +/* If the PermMap is suitable, check that any constant values fed from the + PermMap into the MatrixMap (in either forward or inverse direction) + are not changed by the MatrixMap. This requires the row and column for + each constant axis to be zeros, ecept for a value of 1.0 on the + diagonal. First deal with the cases where the PermMap is applied + first, so the outputs of the PermMap are fed into the MatrixMap in the + forward direction. */ + if( ret && ( nomat == map1 ) ) { + + if( nout != nax ){ + astError( AST__RDERR, "PermMap produces %d outputs, but the following" + "MatrixMap has %d inputs\n", status, nout, nax ); + ret = 0; + } + +/* Consider each output axis of the PermMap. */ + for( i = 0; i < nout && astOK ; i++ ) { + +/* If this PermMap output is assigned a constant... */ + if( outperm[ i ] < 0 || outperm[ i ] >= nin ) { + +/* Check the i'th row of the MatrixMap is all zero except for the i'th + column which must be non-zero. If not indicate that the MatrixMap cannot + swap with the PermMap and leave the loop. */ + if( !ScalingRowCol( mat, i, status ) ) { + ret = 0; + break; + } + } + } + } + +/* Now deal with the cases where the PermMap is applied second, so the inputs + of the PermMap are fed into the MatrixMap in the inverse direction. */ + if( ret && ( nomat == map2 ) ) { + + if( nin != nax ){ + astError( AST__RDERR, "Inverse PermMap produces %d inputs, but the " + "preceding MatrixMap has %d outputs\n", status, nin, nax ); + ret = 0; + } + +/* Consider each input axis of the PermMap. */ + for( i = 0; i < nin && astOK; i++ ){ + +/* If this PermMap input is assigned a constant (by the inverse Mapping)... */ + if( inperm[ i ] < 0 || inperm[ i ] >= nout ) { + +/* Check the i'th row of the MatrixMap is all zero except for the i'th + column which must be non-zero. If not indicate that the MatrixMap cannot + swap with the PermMap and leave the loop. */ + if( !ScalingRowCol( mat, i, status ) ) { + ret = 0; + break; + } + } + } + } + +/* If we can swap with the PermMap, the swapped Mappings may be + intrinsically simpler than the original mappings. */ + if( ret ) { + +/* If the PermMap precedes the WinMap, this will be the case if the PermMap + has more outputs than inputs. If the WinMap precedes the PermMap, this + will be the case if the PermMap has more inputs than outputs. */ + *simpler = ( nomat == map1 ) ? nout > nin : nin > nout; + } + +/* Free the axis permutation and constants arrays. */ + outperm = (int *) astFree( (void *) outperm ); + inperm = (int *) astFree( (void *) inperm ); + consts = (double *) astFree( (void *) consts ); + } + } + } + +/* Re-instate the original settings of the Invert attributes for the + supplied MatrixMaps. */ + astSetInvert( map1, invert[ 0 ] ); + astSetInvert( map2, invert[ 1 ] ); + +/* Return the answer. */ + return astOK ? ret : 0; +} + +static void CompressMatrix( AstMatrixMap *this, int *status ){ +/* +* Name: +* CompressMatrix + +* Purpose: +* If possible, reduce the amount of storage needed to store a MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* void CompressMatrix( AstMatrixMap *this, int *status ) + +* Class Membership: +* MatrixMap member function. + +* Description: +* The supplid MatrixMap is converted to its most compressed form +* (i.e no element values if it is a unit matrix, diagonal elements only +* if it is a diagonal matrix, or all elements otherwise). + +* Parameters: +* this +* A pointer to the MatrixMap to be compressed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double *a; /* Pointer to next element */ + double *colmax; /* Pointer to array holding column max values */ + double *fmat; /* Pointer to compressed forward matrix */ + double *rowmax; /* Pointer to array holding row max values */ + double mval; /* Matrix element value */ + int i; /* Loop count */ + int j; /* Loop count */ + int k; /* Loop count */ + int ncol; /* No. of columns in forward matrix */ + int ndiag; /* No. of diagonal elements in matrix */ + int new_form; /* Compressed storage form */ + int new_inv; /* New inverse requied? */ + int next_diag; /* Index of next diagonal element */ + int nrow; /* No. of rows in forward matrix */ + +/* Check the global error status. */ + if ( !astOK || !this ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + new_inv = 0; + +/* Get the dimensions of the forward matrix. */ + if( astGetInvert( this ) ){ + nrow = astGetNin( this ); + ncol = astGetNout( this ); + } else { + ncol = astGetNin( this ); + nrow = astGetNout( this ); + } + +/* Store the number of diagonal elements in the matrix. This is the + minimum of the number of rows and columns. */ + if( ncol < nrow ){ + ndiag = ncol; + } else { + ndiag = nrow; + } + +/* If the MatrixMap is already stored in UNIT form, it cannot be compressed + any further. */ + if( this->form == UNIT){ + return; + +/* Otherwise, if the MatrixMap is stored in DIAGONAL form, it could be + compressed into a UNIT MatrixMap if all the supplied element values are + one. */ + } else if( this->form == DIAGONAL ){ + new_form = UNIT; + for( i = 0; i < ndiag; i++ ){ + if( !astEQUAL( (this->f_matrix)[ i ], 1.0 ) ){ + new_form = DIAGONAL; + break; + } + } + +/* If it can be compressed, change the storage form and free the arrays + holding the diagonal element values. */ + if( new_form == UNIT ) { + this->f_matrix = (double *) astFree( (void *)( this->f_matrix ) ); + this->i_matrix = (double *) astFree( (void *)( this->i_matrix ) ); + this->form = UNIT; + } + +/* Otherwise, a full MatrixMap has been supplied, but this could be stored + in a unit or diagonal MatrixMap if the element values are appropriate. */ + } else { + new_form = FULL; + +/* Find the maximum absolute value in each column. Scale by + sqrt(DBL_EPSILON) to be come a lower limit for non-zero values. */ + colmax = astMalloc( ncol*sizeof( double ) ); + if( colmax ) { + for( j = 0; j < ncol; j++ ) { + colmax[ j ] = 0.0; + i = j; + for( k = 0; k < nrow; k++ ) { + mval = (this->f_matrix)[ i ]; + if( mval != AST__BAD ) { + mval = fabs( mval ); + if( mval > colmax[ j ] ) colmax[ j ] = mval; + } + i += ncol; + } + colmax[ j ] *= sqrt( DBL_EPSILON ); + } + } + +/* Find the maximum absolute value in each row. Scale by + sqrt(DBL_EPSILON) to be come a lower limit for non-zero values. */ + rowmax = astMalloc( nrow*sizeof( double ) ); + if( rowmax ) { + for( k = 0; k < nrow; k++ ) { + rowmax[ k ] = 0.0; + i = k*ncol; + for( j = 0; j < ncol; j++ ) { + mval = (this->f_matrix)[ i ]; + if( mval != AST__BAD ) { + mval = fabs( mval ); + if( mval > rowmax[ k ] ) rowmax[ k ] = mval; + } + i++; + } + rowmax[ k ] *= sqrt( DBL_EPSILON ); + } + } + +/* Check memory can be used */ + if( astOK ) { + +/* Initialise a flag indicating that the inverse matrix does not need to + be re-calculated. */ + new_inv = 0; + +/* Initially assume that the forward matrix is a unit matrix. */ + new_form = UNIT; + +/* Store a pointer to the next matrix element. */ + a = this->f_matrix; + +/* Loop through all the rows in the forward matrix array. */ + for( k = 0; k < nrow; k++ ) { + +/* Loop through all the elements in this column. */ + for( j = 0; j < ncol; j++, a++ ) { + +/* If this element is bad, use full form. */ + if( *a == AST__BAD ) { + new_form = FULL; + +/* Otherwise, if this is a diagonal term, check its value. If it is not one, + then the matrix cannot be a unit matrix, but it could still be a diagonal + matrix. */ + } else { + if( j == k ) { + if( *a != 1.0 && new_form == UNIT ) new_form = DIAGONAL; + +/* If this is not a diagonal element, and the element value is not zero, + then the matrix is not a diagonal matrix. Allow a tolerance of + SQRT(DBL_EPSILON) times the largest value in the same row or column as + the current matrix element. That is, an element must be insignificant + to both its row and its column to be considered as effectively zero. + Replace values less than this limit with zero. */ + } else { + mval = fabs( *a ); + if( mval <= rowmax[ k ] && + mval <= colmax[ j ] ) { + +/* If the element will change value, set a flag indicating that the inverse + matrix needs to be re-calculated. */ + if( *a != 0.0 ) new_inv = 1; + +/* Ensure this element value is zero. */ + *a = 0.0; + + } else { + new_form = FULL; + } + } + } + } + } + } + +/* Free memory. */ + colmax = astFree( colmax ); + rowmax = astFree( rowmax ); + +/* If it can be compressed into a UNIT MatrixMap, change the storage form and + free the arrays holding the element values. */ + if( new_form == UNIT ) { + this->f_matrix = (double *) astFree( (void *)( this->f_matrix ) ); + this->i_matrix = (double *) astFree( (void *)( this->i_matrix ) ); + this->form = UNIT; + +/* Otherwise, if it can be compressed into a DIAGONAL MatrixMap, copy the + diagonal elements from the full forward matrix into a newly allocated + array, use this array to replace the forward matrix array in the MatrixMap, + create a new inverse matrix, and change the storage form. */ + } else if( new_form == DIAGONAL ) { + fmat = astMalloc( sizeof(double)*(size_t)ndiag ); + if( fmat ){ + + next_diag = 0; + for( i = 0; i < ndiag; i++ ){ + fmat[ i ] = (this->f_matrix)[ next_diag ]; + next_diag += ncol + 1; + } + + (void) astFree( (void *) this->f_matrix ); + (void) astFree( (void *) this->i_matrix ); + + this->f_matrix = fmat; + this->i_matrix = InvertMatrix( DIAGONAL, nrow, ncol, fmat, status ); + this->form = DIAGONAL; + + } + +/* Calculate a new inverse matrix if necessary. */ + } else if( new_inv ) { + (void) astFree( (void *) this->i_matrix ); + this->i_matrix = InvertMatrix( FULL, nrow, ncol, this->f_matrix, status ); + } + } + + return; + +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two MatrixMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* MatrixMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two MatrixMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a MatrixMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the MatrixMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMatrixMap *that; + AstMatrixMap *this; + double *that_matrix; + double *this_matrix; + int i; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two MatrixMap structures. */ + this = (AstMatrixMap *) this_object; + that = (AstMatrixMap *) that_object; + +/* Check the second object is a MatrixMap. We know the first is a + MatrixMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAMatrixMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNout( that ) == nout && astGetNin( that ) == nin ) { + +/* Assume the MatrixMaps are equivalent. */ + result = 1; + +/* Ensure both MatrixMaps are stored in full form. */ + ExpandMatrix( this, status ); + ExpandMatrix( that, status ); + +/* Get pointers to the arrays holding the elements of the forward matrix + for both MatrixMaps. */ + if( astGetInvert( this ) ) { + this_matrix = this->i_matrix; + } else { + this_matrix = this->f_matrix; + } + + if( astGetInvert( that ) ) { + that_matrix = that->i_matrix; + } else { + that_matrix = that->f_matrix; + } + +/* If either of the above arrays is not available, try to get the inverse + matrix arrays. */ + if( !this_matrix || !that_matrix ) { + if( astGetInvert( this ) ) { + this_matrix = this->f_matrix; + } else { + this_matrix = this->i_matrix; + } + + if( astGetInvert( that ) ) { + that_matrix = that->f_matrix; + } else { + that_matrix = that->i_matrix; + } + } + +/* If both arrays are now available compare their elements. */ + if( this_matrix && that_matrix ) { + result = 1; + for( i = 0; i < nin*nout; i++ ) { + if( !astEQUAL( this_matrix[ i ], that_matrix[ i ] ) ){ + result = 0; + break; + } + } + } + +/* Ensure the supplied MatrixMaps are stored back in compressed form. */ + CompressMatrix( this, status ); + CompressMatrix( that, status ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void ExpandMatrix( AstMatrixMap *this, int *status ){ +/* +* Name: +* ExpandMatrix + +* Purpose: +* Ensure the MatrixMap is stored in full (non-compressed) form. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* void ExpandMatrix( AstMatrixMap *this, int *status ) + +* Class Membership: +* MatrixMap member function. + +* Description: +* If the supplid MatrixMap is stored in a compressed form (i.e no +* element values if it is a unit matrix, diagonal elements only +* if it is a diagonal matrix), it is expanded into a full MatrixMap +* in which all elements are stored. + +* Parameters: +* this +* A pointer to the MatrixMap to be expanded. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double *fmat; /* Pointer to full forward matrix */ + double *imat; /* Pointer to full inverse matrix */ + int i; /* Loop count */ + int ncol; /* No. of columns in forward matrix */ + int ndiag; /* No. of diagonal elements in matrix */ + int nrow; /* No. of rows in forward matrix */ + +/* Check the global error status. Also return if the MatrixMap + pointer is null. */ + if ( !astOK || !this ) return; + +/* Return without action if the MatrixMap is already in full form. */ + if( this->form == FULL ) return; + +/* Get the dimensions of the forward matrix. */ + if( astGetInvert( this ) ){ + nrow = astGetNin( this ); + ncol = astGetNout( this ); + } else { + ncol = astGetNin( this ); + nrow = astGetNout( this ); + } + +/* Store the number of diagonal elements. */ + if( nrow > ncol ){ + ndiag = ncol; + } else { + ndiag = nrow; + } + +/* Allocate arrays to hold the full forward and inverse matrices. */ + fmat = (double *) astMalloc( sizeof( double )*(size_t)( nrow*ncol ) ); + imat = (double *) astMalloc( sizeof( double )*(size_t)( nrow*ncol ) ); + if( imat && fmat ){ + +/* Fill them both with zeros. */ + for( i = 0; i < nrow*ncol; i++ ) { + fmat[ i ] = 0.0; + imat[ i ] = 0.0; + } + +/* If a unit MatrixMap was supplied, put ones on the diagonals. */ + if( this->form == UNIT ){ + for( i = 0; i < ndiag; i++ ) { + fmat[ i*( ncol + 1 ) ] = 1.0; + imat[ i*( nrow + 1 ) ] = 1.0; + } + +/* If a diagonal MatrixMap was supplied, copy the diagonal terms from + the supplied MatrixMap. */ + } else if( this->form == DIAGONAL ){ + for( i = 0; i < ndiag; i++ ) { + fmat[ i*( ncol + 1 ) ] = (this->f_matrix)[ i ]; + imat[ i*( nrow + 1 ) ] = (this->i_matrix)[ i ]; + } + } + +/* Free any existing arrays in the MatrixMap and store the new ones. */ + (void) astFree( (void *) this->f_matrix ); + (void) astFree( (void *) this->i_matrix ); + + this->f_matrix = fmat; + this->i_matrix = imat; + +/* Update the storage form. */ + this->form = FULL; + +/* If either of the new matrices could not be allocated, ensure that + both have been freed. */ + } else { + fmat = (double *) astFree( (void *) fmat ); + imat = (double *) astFree( (void *) imat ); + } + + return; + +} + +static int FindString( int n, const char *list[], const char *test, + const char *text, const char *method, + const char *class, int *status ){ +/* +* Name: +* FindString + +* Purpose: +* Find a given string within an array of character strings. + +* Type: +* Private function. + +* Synopsis: +* #include "matrix.h" +* int FindString( int n, const char *list[], const char *test, +* const char *text, const char *method, const char *class, int *status ) + +* Class Membership: +* MatrixMap method. + +* Description: +* This function identifies a supplied string within a supplied +* array of valid strings, and returns the index of the string within +* the array. The test option may not be abbreviated, but case is +* insignificant. + +* Parameters: +* n +* The number of strings in the array pointed to be "list". +* list +* A pointer to an array of legal character strings. +* test +* A candidate string. +* text +* A string giving a description of the object, parameter, +* attribute, etc, to which the test value refers. +* This is only for use in constructing error messages. It should +* start with a lower case letter. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The index of the identified string within the supplied array, starting +* at zero. + +* Notes: +* - A value of -1 is returned if an error has already occurred, or +* if this function should fail for any reason (for instance if the +* supplied option is not specified in the supplied list). + +*/ + +/* Local Variables: */ + int ret; /* The returned index */ + +/* Check global status. */ + if( !astOK ) return -1; + +/* Compare the test string with each element of the supplied list. Leave + the loop when a match is found. */ + for( ret = 0; ret < n; ret++ ) { + if( !Ustrcmp( test, list[ ret ], status ) ) break; + } + +/* Report an error if the supplied test string does not match any element + in the supplied list. */ + if( ret >= n ) { + astError( AST__RDERR, "%s(%s): Illegal value '%s' supplied for %s.", status, + method, class, test, text ); + ret = -1; + } + +/* Return the answer. */ + return ret; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* MatrixMap member function (over-rides the protected astGetIsLinear +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsLinear attribute for a +* Frame, which is always one. + +* Parameters: +* this +* Pointer to the MatrixMap. +* status +* Pointer to the inherited status variable. +*/ + return 1; +} + +static int Ustrcmp( const char *a, const char *b, int *status ){ +/* +* Name: +* Ustrncmp + +* Purpose: +* A case blind version of strcmp. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int Ustrcmp( const char *a, const char *b ) + +* Class Membership: +* MatrixMap member function. + +* Description: +* Returns 0 if there are no differences between the two strings, and 1 +* otherwise. Comparisons are case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strcmp" does. +* - This function attempts to execute even if an error has occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Loop round each character. */ + while( 1 ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + +void astInitMatrixMapVtab_( AstMatrixMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitMatrixMapVtab + +* Purpose: +* Initialise a virtual function table for a MatrixMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "matrixmap.h" +* void astInitMatrixMapVtab( AstMatrixMapVtab *vtab, const char *name ) + +* Class Membership: +* MatrixMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the MatrixMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAMatrixMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->MtrRot = MtrRot; + vtab->MtrMult = MtrMult; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_mapsplit = mapping->MapSplit; + mapping->MapSplit = MapSplit; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->GetIsLinear = GetIsLinear; + mapping->GetTranForward = GetTranForward; + mapping->GetTranInverse = GetTranInverse; + mapping->MapMerge = MapMerge; + mapping->Rate = Rate; + +/* Declare the destructor and copy constructor. */ + astSetDelete( (AstObjectVtab *) vtab, Delete ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + +/* Declare the class dump function. */ + astSetDump( vtab, Dump, "MatrixMap", "Matrix transformation" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + + +static double *InvertMatrix( int form, int nrow, int ncol, double *matrix, int *status ){ +/* +* Name: +* InvertMatrix + +* Purpose: +* Invert a suplied matrix. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* double *InvertMatrix( int form, int nrow, int ncol, double *matrix, int *status ) + +* Class Membership: +* MatrixMap member function. + +* Description: +* This function returns a pointer to a matrix holding the inverse of +* the supplied matrix, or a NULL pointer if the inverse is not defined. +* The memory to store the inverse matrix is allocated internally, and +* should be freed using astFree when no longer required. +* +* The correspondence between a full matrix and its inverse is only +* unique if the matrix is square, and so a NULL pointer is returned if +* the supplied matrix is not square. + +* Parameters: +* form +* The form of the MatrixMap; UNIT, DIAGONAL or FULL. +* nrow +* Number of rows in the supplied matrix. +* ncol +* Number of columns in the supplied matrix. +* matrix +* A pointer to the input matrix. Elements should be stored in row +* order (i.e. (row 1,column 1 ), (row 1,column 2 )... (row 2,column 1), +* etc). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output matrix. + +* Notes: +* - A NULL pointer is returned if a unit matrix is supplied. +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - No error is reported if the inverse is not defined. +*/ + +/* Local Variables: */ + double det; /* Determinant of supplied matrix */ + double mval; /* Matrix element value */ + double *out; /* Pointer to returned inverse matrix */ + double *vector; /* Pointer to vector used by palDmat */ + int i; /* Matrix element number */ + int *iw; /* Pointer to workspace used by palDmat */ + int nel; /* No. of elements in square matrix */ + int ndiag; /* No. of diagonal elements */ + int ok; /* Zero if any bad matrix values found */ + int sing; /* Zero if matrix is not singular */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return a NULL pointer if the input matrix is NULL. */ + if( !matrix ) return NULL; + +/* If a unit matrix map has been supplied, return NULL. */ + if( form == UNIT ){ + return NULL; + +/* If a diagonal matrix has been supplied, allocate an array to hold + the diagonal terms of the inverse matrix. Store the reciprocal + of the input matrix diagonal terms in it. If any of the input diagonal + terms are zero or BAD, set the associated elements of the inverse matrix + BAD. */ + } else if( form == DIAGONAL ){ + if( nrow > ncol ) { + ndiag = ncol; + } else { + ndiag = nrow; + } + + out = (double *) astMalloc( sizeof( double )*(size_t)ndiag ); + + if( out ) { + for( i = 0; i < ndiag; i++ ) { + mval = matrix[ i ]; + if( mval != 0.0 && mval != AST__BAD ){ + out[ i ] = 1.0/mval; + } else { + out[ i ] = AST__BAD; + } + } + } + +/* If a full matrix has been supplied, initialise the returned pointer. */ + } else { + out = NULL; + +/* Check that the matrix is square. */ + if( nrow == ncol ){ + +/* Find the number of elements in the matrix. */ + nel = nrow*ncol; + +/* See if there are any bad values in the matrix. */ + ok = 1; + for ( i=0; iform == UNIT && nin == nout ){ + map2 = (AstMapping *) astUnitMap( nin, "", status ); + +/* If the MatrixMap is a square diagonal matrix with equal diagonal + terms, then it can be replaced by a ZoomMap. */ + } else if( mm->form == DIAGONAL && nin == nout && + mm->f_matrix && mm->i_matrix && + (mm->f_matrix)[ 0 ] != AST__BAD ){ + zoom = 1; + b = mm->f_matrix + 1; + for( i = 1; i < nin; i++ ){ + if( !astEQUAL( *b, *( b - 1 ) ) ){ + zoom = 0; + break; + } + b++; + } + + if( zoom ){ + if( ( *invert_list )[ where ] ){ + map2 = (AstMapping *) astZoomMap( nin, (mm->i_matrix)[ 0 ], "", status ); + } else { + map2 = (AstMapping *) astZoomMap( nin, (mm->f_matrix)[ 0 ], "", status ); + } + } + +/* If the MatrixMap is a full matrix but all off-diagnal elements are + zero, it can be replaced by a diagonal MatrixMap. */ + } else if( mm->form == FULL && nin == nout && mm->f_matrix ){ + new_mat = astMalloc( sizeof( double )*nin ); + b = mm->f_matrix; + for( i = 0; i < nin && new_mat; i++ ){ + for( j = 0; j < nout; j++,b++ ){ + if( i == j ) { + new_mat[ i ] = *b; + } else if( *b != 0.0 ) { + new_mat = astFree( new_mat ); + break; + } + } + } + + if( new_mat ) { + map2 = (AstMapping *) astMatrixMap( nin, nout, 1, new_mat, "", + status ); + new_mat = astFree( new_mat ); + } + } + +/* If the MatrixMap can be replaced, annul the MatrixMap pointer in the + list and replace it with the new Mapping pointer, and indicate that the + forward transformation of the returned Mapping should be used. */ + if( map2 ){ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = map2; + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the MatrixMap itself could not be simplified, see if it can be merged + with the Mappings on either side of it in the list. */ +/*==========================================================================*/ + } else { + +/* Store the classes of the neighbouring Mappings in the list. */ + class1 = ( where > 0 ) ? astGetClass( ( *map_list )[ where - 1 ] ) : NULL; + class2 = ( where < *nmap - 1 ) ? astGetClass( ( *map_list )[ where + 1 ] ) : NULL; + +/* In series. */ +/* ========== */ + if ( series ) { + +/* We first look to see if the MatrixMap can be merged with one of its + neighbours, resulting in a reduction of one in the number of Mappings + in the list. MatrixMaps can merge directly with another MatrixMap, a + ZoomMap, an invertable PermMap, or a UnitMap. */ + if( class1 && ( !strcmp( class1, "MatrixMap" ) || + !strcmp( class1, "ZoomMap" ) || + !strcmp( class1, "PermMap" ) || + !strcmp( class1, "UnitMap" ) ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( class2 && ( !strcmp( class2, "MatrixMap" ) || + !strcmp( class2, "ZoomMap" ) || + !strcmp( class2, "PermMap" ) || + !strcmp( class2, "UnitMap" ) ) ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } else { + nclass = NULL; + } + +/* Only some PermMaps can be merged with (those which have consistent + forward and inverse mappings). If this is not one of them, set nclass + NULL to indicate this. */ + if( nclass && !strcmp( nclass, "PermMap" ) && + !PermOK( ( *map_list )[ (i1==where)?i2:i1 ], status ) ) nclass = NULL; + +/* If the MatrixMap can merge with one of its neighbours, create the merged + Mapping. */ + if( nclass ){ + + if( !strcmp( nclass, "MatrixMap" ) ){ + newmm = MatMat( ( *map_list )[ i1 ], ( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], status ); + invert = 0; + + } else if( !strcmp( nclass, "ZoomMap" ) ){ + if( i1 == where ){ + newmm = MatZoom( (AstMatrixMap *)( *map_list )[ i1 ], + (AstZoomMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], status ); + } else { + newmm = MatZoom( (AstMatrixMap *)( *map_list )[ i2 ], + (AstZoomMap *)( *map_list )[ i1 ], + ( *invert_list )[ i2 ], ( *invert_list )[ i1 ], status ); + } + invert = 0; + + } else if( !strcmp( nclass, "PermMap" ) ){ + if( i1 == where ){ + newmm = MatPerm( (AstMatrixMap *)( *map_list )[ i1 ], + (AstPermMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], 1, status ); + } else { + newmm = MatPerm( (AstMatrixMap *)( *map_list )[ i2 ], + (AstPermMap *)( *map_list )[ i1 ], + ( *invert_list )[ i2 ], ( *invert_list )[ i1 ], 0, status ); + } + invert = 0; + + } else { + newmm = astClone( ( *map_list )[ where ] ); + invert = ( *invert_list )[ where ]; + } + +/* If succesfull... */ + if( astOK ){ + +/* Annul the first of the two Mappings, and replace it with the merged + MatrixMap. Also set the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = (AstMapping *) newmm; + ( *invert_list )[ i1 ] = invert; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i2 ] ); + for ( i = i2 + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + + } + +/* If the MatrixMap could not merge directly with either of its neighbours, + we consider whether it would be worthwhile to swap the MatrixMap with + either of its neighbours. This can only be done for certain classes + of Mapping (WinMaps and some PermMaps), and will usually require both + Mappings to be modified (unless they are commutative). The advantage of + swapping the order of the Mappings is that it may result in the MatrixMap + being adjacent to a Mapping with which it can merge directly on the next + invocation of this function, thus reducing the number of Mappings + in the list. */ + } else { + +/* Set a flag if we could swap the MatrixMap with its higher neighbour. "do2" + is returned if swapping the Mappings would simplify either of the Mappings. */ + if( where + 1 < *nmap ){ + swaphi = CanSwap( ( *map_list )[ where ], + ( *map_list )[ where + 1 ], + ( *invert_list )[ where ], + ( *invert_list )[ where + 1 ], &do2, status ); + } else { + swaphi = 0; + do2 = 0; + } + +/* If so, step through each of the Mappings which follow the MatrixMap, + looking for a Mapping with which the MatrixMap could merge directly. Stop + when such a Mapping is found, or if a Mapping is found with which the + MatrixMap could definitely not swap. Note the number of Mappings which + separate the MatrixMap from the Mapping with which it could merge (if + any). */ + nstep2 = -1; + if( swaphi ){ + for( i2 = where + 1; i2 < *nmap; i2++ ){ + +/* See if we can merge with this Mapping. If so, note the number of steps + between the two Mappings and leave the loop. */ + nclass = astGetClass( ( *map_list )[ i2 ] ); + if( !strcmp( nclass, "MatrixMap" ) || + !strcmp( nclass, "ZoomMap" ) || + ( !strcmp( nclass, "PermMap" ) && PermOK( ( *map_list )[ i2 ], status ) ) || + !strcmp( nclass, "UnitMap" ) ) { + nstep2 = i2 - where - 1; + break; + } + +/* If there is no chance that we can swap with this Mapping, leave the loop + with -1 for the number of steps to indicate that no merging is possible. + MatrixMaps can swap with WinMaps and some permmaps. */ + if( strcmp( nclass, "WinMap" ) && + strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Do the same working forward from the MatrixMap towards the start of the map + list. */ + if( where > 0 ){ + swaplo = CanSwap( ( *map_list )[ where - 1 ], + ( *map_list )[ where ], + ( *invert_list )[ where - 1 ], + ( *invert_list )[ where ], &do1, status ); + } else { + swaplo = 0; + do1 = 0; + } + + nstep1 = -1; + if( swaplo ){ + for( i1 = where - 1; i1 >= 0; i1-- ){ + + nclass = astGetClass( ( *map_list )[ i1 ] ); + if( !strcmp( nclass, "MatrixMap" ) || + ( !strcmp( nclass, "PermMap" ) && PermOK( ( *map_list )[ i1 ], status ) ) || + !strcmp( nclass, "ZoomMap" ) || + !strcmp( nclass, "UnitMap" ) ) { + nstep1 = where - 1 - i1; + break; + } + + if( strcmp( nclass, "WinMap" ) && + strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Choose which neighbour to swap with so that the MatrixMap moves towards the + nearest Mapping with which it can merge. */ + if( do1 || ( + nstep1 != -1 && ( nstep2 == -1 || nstep2 > nstep1 ) ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + } else if( do2 || nstep2 != -1 ){ + nclass = class2; + i1 = where; + i2 = where + 1; + } else { + nclass = NULL; + } + +/* If there is a target Mapping in the list with which the MatrixMap could + merge, consider replacing the supplied Mappings with swapped Mappings to + bring the MatrixMap closer to the target Mapping. */ + if( nclass ){ + +/* Swap the Mappings. */ + if (!strcmp( nclass, "WinMap" ) ){ + MatWin( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + + } else if( !strcmp( nclass, "PermMap" ) ){ + MatPermSwap( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + } + +/* And then merge them. */ + if( where == i1 && where + 1 < *nmap ) { /* Merging upwards */ + map2 = astClone( (*map_list)[ where + 1 ] ); + nmapt = *nmap - where - 1; + maplt = *map_list + where + 1; + invlt = *invert_list + where + 1; + + (void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + *nmap = where + 1 + nmapt; + + } else if( where - 2 >= 0 ) { /* Merging downwards */ + map2 = astClone( (*map_list)[ where - 2 ] ); + nmapt = *nmap - where + 2; + maplt = *map_list + where - 2 ; + invlt = *invert_list + where - 2; + + (void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + *nmap = where - 2 + nmapt; + } + + result = i1; + +/* If there is no Mapping available for merging, it may still be + advantageous to swap with a neighbour because the swapped Mapping may + be simpler than the original Mappings. For instance, a PermMap may + strip rows of the MatrixMap leaving only a UnitMap. */ + } else if( swaphi || swaplo ) { + +/* Try swapping with each possible neighbour in turn. */ + for( i = 0; i < 2; i++ ) { + +/* Set up the class and pointers for the mappings to be swapped, first + the lower neighbour, then the upper neighbour. */ + if( i == 0 && swaplo ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( i == 1 && swaphi ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } else { + nclass = NULL; + } + +/* If we have a Mapping to swap with... */ + if( nclass ) { + +/* Take copies of the Mapping and Invert flag arrays so we do not change + the supplied values. */ + mc[ 0 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[0] ); + mc[ 1 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[1] ); + ic[ 0 ] = ( (*invert_list) + i1 )[0]; + ic[ 1 ] = ( (*invert_list) + i1 )[1]; + +/* Swap these Mappings. */ + if( !strcmp( nclass, "WinMap" ) ){ + MatWin( mc, ic, where - i1, status ); + } else if( !strcmp( nclass, "PermMap" ) ){ + MatPermSwap( mc, ic, where - i1, status ); + } + +/* If neither of the swapped Mappings can be simplified further, then there + is no point in swapping the Mappings, so just annul the map copies. */ + smc0 = astSimplify( mc[0] ); + smc1 = astSimplify( mc[1] ); + + if( astGetClass( smc0 ) == astGetClass( mc[0] ) && + astGetClass( smc1 ) == astGetClass( mc[1] ) ) { + + mc[ 0 ] = (AstMapping *) astAnnul( mc[ 0 ] ); + mc[ 1 ] = (AstMapping *) astAnnul( mc[ 1 ] ); + +/* If one or both of the swapped Mappings could be simplified, then annul + the supplied Mappings and return the swapped mappings, storing the index + of the first modified Mapping. */ + } else { + (void ) astAnnul( ( (*map_list) + i1 )[0] ); + (void ) astAnnul( ( (*map_list) + i1 )[1] ); + + ( (*map_list) + i1 )[0] = mc[ 0 ]; + ( (*map_list) + i1 )[1] = mc[ 1 ]; + + ( (*invert_list) + i1 )[0] = ic[ 0 ]; + ( (*invert_list) + i1 )[1] = ic[ 1 ]; + + result = i1; + break; + } + +/* Annul the simplied Mappings */ + smc0 = astAnnul( smc0 ); + smc1 = astAnnul( smc1 ); + + } + } + } + } + } + } + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* MatrixMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing MatrixMap. This is only possible if the specified inputs +* correspond to some subset of the MatrixMap outputs. That is, there +* must exist a subset of the MatrixMap outputs for which each output +* depends only on the selected MatrixMap inputs, and not on any of the +* inputs which have not been selected. In addition, outputs that are +* not in this subset must not depend on any selected inputs. If these +* conditions are not met by the supplied MatrixMap, then a NULL Mapping +* is returned. + +* Parameters: +* this +* Pointer to the MatrixMap to be split (the MatrixMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied MatrixMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied MatrixMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied MatrixMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstMatrixMap *this; /* Pointer to MatrixMap structure */ + double *mat; /* Pointer to matrix for supplied MatrixMap */ + double *pmat; /* Pointer to row start in returned matrix */ + double *prow; /* Pointer to row start in supplied matrix */ + double *rmat; /* Pointer to matrix for returned MatrixMap */ + double el; /* Next element value in supplied matrix */ + int *result; /* Pointer to returned array */ + int good; /* Would new matrix contain any good values/ */ + int i; /* Loop count */ + int icol; /* Column index within supplied MatrixMap */ + int iel; /* Index of next element from the input matrix */ + int irow; /* Row index within supplied MatrixMap */ + int isel; /* Does output depend on any selected inputs? */ + int ncol; /* Number of columns (inputs) in supplied MatrixMap */ + int nout; /* Number of outputs in returned MatrixMap */ + int nrow; /* Number of rows (outputs) in supplied MatrixMap */ + int ok; /* Are input indices OK? */ + int sel; /* Does any output depend on selected inputs? */ + int unsel; /* Does any output depend on unselected inputs? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the parent astMapSplit method to see if it can do the job. */ + result = (*parent_mapsplit)( this_map, nin, in, map, status ); + +/* If not, we provide a special implementation here. */ + if( !result ) { + +/* Get a pointer to the MatrixMap structure. */ + this = (AstMatrixMap *) this_map; + +/* Get the number of inputs and outputs. */ + ncol = astGetNin( this ); + nrow = astGetNout( this ); + +/* Check the supplied input indices are usable. */ + ok = 1; + for( i = 0; i < nin; i++ ) { + if( in[ i ] < 0 || in[ i ] >= ncol ) { + ok = 0; + break; + } + } + + if( ok ) { + +/* Ensure the MatrixMap is stored in full form. */ + ExpandMatrix( this, status ); + +/* Allocate the largest array that could be necessary to hold the + returned array of Mapping outputs. */ + result = astMalloc( sizeof(int)*(size_t) nrow ); + +/* Allocate the largest array that could be necessary to hold the + matrix representing the returned MatrixMap. */ + rmat = astMalloc( sizeof(double)*(size_t) (nrow*ncol) ); + +/* Get the matrix which defines the current forward transformation. This + takes into account whether the MatrixMap has been inverted or not. */ + if( astGetInvert( this ) ) { + mat = this->i_matrix; + } else { + mat = this->f_matrix; + } + +/* We cannot create the require Mapping if the matrix is undefined. */ + if( !mat || !astOK ) { + ok = 0; + nout = 0; + good = 0; + +/* Otherwise, loop round all the rows in the matrix. */ + } else { + nout = 0; + good = 0; + pmat = rmat; + iel = 0; + for( irow = 0; irow < nrow; irow++ ) { + +/* Indicate that this output (i.e. row of the matrix) depends on neither + selected nor unselected inputs as yet. */ + sel = 0; + unsel = 0; + +/* Save a pointer to the first element of this row in the MatrixMap + matrix. */ + prow = mat + iel; + +/* Loop round all the elements in the current row of the matrix. */ + for( icol = 0; icol < ncol; icol++ ) { + +/* If this element is non-zero and non-bad, then output "irow" depends on + input "icol". */ + el = mat[ iel++ ]; + if( el != 0.0 && el != AST__BAD ) { + +/* Is input "icol" one of the selected inputs? */ + isel = 0; + for( i = 0; i < nin; i++ ) { + if( in[ i ] == icol ) { + isel = 1; + break; + } + } + +/* If so, note that this output depends on selected inputs. Otherwise note + it depends on unselected inputs. */ + if( isel ) { + sel = 1; + } else { + unsel = 1; + } + } + } + +/* If this output depends only on selected inputs, we can include it in + the returned Mapping.*/ + if( sel && !unsel ) { + +/* Store the index of the output within the original MatrixMap. */ + result[ nout ] = irow; + +/* Increment the number of outputs in the returned Mapping. */ + nout++; + +/* Copy the elements of the current matrix row which correspond to the + selected inputs into the new matrix. */ + for( i = 0; i < nin; i++ ) { + if( astISGOOD( prow[ in[ i ] ] ) ) { + *(pmat++) = prow[ in[ i ] ]; + good = 1; + } + } + } + +/* If this output depends on a selected input, but also depends on an + unselected input, we cannot split the MatrixMap. */ + if( sel && unsel ) { + ok = 0; + break; + } + } + } + + +/* If the returned Mapping can be created, create it. */ + if( ok && nout > 0 && good ) { + *map = (AstMapping *) astMatrixMap( nin, nout, 0, rmat, "", status ); + +/* Otherwise, free the returned array. */ + } else { + result = astFree( result ); + } + +/* Free resources. */ + rmat = astFree( rmat ); + +/* Re-compress the supplied MatrixMap. */ + CompressMatrix( this, status ); + } + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static AstMatrixMap *MatMat( AstMapping *map1, AstMapping *map2, int inv1, + int inv2, int *status ){ +/* +* Name: +* MatMat + +* Purpose: +* Create a merged MatrixMap from two supplied MatrixMaps. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *MatMat( AstMapping *map1, AstMapping *map2, int inv1, +* int inv2, int *status ) + +* Class Membership: +* MatrixMap member function + +* Description: +* This function creates a new MatrixMap which performs a mapping +* equivalent to applying the two supplied MatrixMaps in series, in the +* directions specified by the "invert" flags (the Invert attributes of +* the supplied MatrixMaps are ignored). + +* Parameters: +* map1 +* A pointer to the MatrixMap to apply first. +* map2 +* A pointer to the MatrixMap to apply second. +* inv1 +* The invert flag to use with map1. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* inv2 +* The invert flag to use with map2. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new MatrixMap. + +* Notes: +* - The forward direction of the returned MatrixMap is equivalent to the +* combined effect of the two supplied MatrixMap, operating in the +* directions specified by "inv1" and "inv2". +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMatrixMap *result; /* Pointer to output MatrixMap */ + int invert[ 2 ]; /* Original invert flags */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned pointer. */ + result = NULL; + +/* Temporarily set their Invert attributes to the supplied values. */ + invert[ 0 ] = astGetInvert( map1 ); + astSetInvert( map1, inv1 ); + + invert[ 1 ] = astGetInvert( map2 ); + astSetInvert( map2, inv2 ); + +/* Create a new MatrixMap by multiplying them together. */ + result = astMtrMult( (AstMatrixMap *) map1, (AstMatrixMap *) map2 ); + +/* Re-instate the original settings of the Invert attributes for the + supplied MatrixMaps. */ + astSetInvert( map1, invert[ 0 ] ); + astSetInvert( map2, invert[ 1 ] ); + +/* If an error has occurred, annull the returned MatrixMap. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output MatrixMap. */ + return result; +} + +static AstMatrixMap *MatPerm( AstMatrixMap *mm, AstPermMap *pm, int minv, + int pinv, int mat1, int *status ){ +/* +* Name: +* MatPerm + +* Purpose: +* Create a MatrixMap by merging a MatrixMap and a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *MatPerm( AstMatrixMap *mm, AstPermMap *pm, int minv, +* int pinv, int mat1, int *status ) + +* Class Membership: +* MatrixMap member function + +* Description: +* This function creates a new MatrixMap which performs a mapping +* equivalent to applying the two supplied Mappings in series in the +* directions specified by the "invert" flags (the Invert attributes of +* the supplied MatrixMaps are ignored). + +* Parameters: +* mm +* A pointer to the MatrixMap. +* pm +* A pointer to the PermMap. +* minv +* The invert flag to use with mm. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* pinv +* The invert flag to use with pm. +* mat1 +* If non-zero, then "mm" is applied first followed by "pm". Otherwise, +* "pm" is applied first followed by "mm". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new MatrixMap. + +* Notes: +* - The forward direction of the returned MatrixMap is equivalent to the +* combined effect of the two supplied Mappings, operating in the +* directions specified by "pinv" and "minv". +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMatrixMap *mm2; /* Pointer to an intermediate MatrixMap */ + AstMatrixMap *result; /* Pointer to output MatrixMap */ + AstPointSet *pset1; /* Pointer to a PointSet holding unpermuted unit vectors */ + AstPointSet *pset2; /* Pointer to a PointSet holding permuted unit vectors */ + double *matrix; /* Pointer to a matrix representing the PermMap */ + double *p; /* Pointer to next matrix element */ + double **ptr1; /* Pointer to the data in pset1 */ + double **ptr2; /* Pointer to the data in pset2 */ + int i; /* Axis index */ + int j; /* Point index */ + int nax; /* No. of axes in the PermMap */ + int old_minv; /* Original setting of MatrixMap Invert attribute */ + int old_pinv; /* Original setting of PermMap Invert attribute */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned pointer. */ + result = NULL; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + old_minv = astGetInvert( mm ); + astSetInvert( mm, minv ); + + old_pinv = astGetInvert( pm ); + astSetInvert( pm, pinv ); + +/* Get the number of axes in the PermMap. The PermMap will have the same + number of input and output axes because a check has already been made on + it to ensure that this is so (in function PermOK). */ + nax = astGetNin( pm ); + +/* We first represent the PermMap as a MatrixMap containing elements with + values zero or one. Each row of this matrix is obtained by transforming a + unit vector along each axis using the inverse PermMap. Allocate memory + to hold the matrix array, and create a PointSet holding the unit + vectors. */ + matrix = (double *) astMalloc( sizeof( double )*(size_t)( nax*nax ) ); + + pset1 = astPointSet( nax, nax, "", status ); + ptr1 = astGetPoints( pset1 ); + + pset2 = astPointSet( nax, nax, "", status ); + ptr2 = astGetPoints( pset2 ); + + if( astOK ){ + for( i = 0; i < nax; i++ ){ + for( j = 0; j < nax; j++ ) ptr1[ i ][ j ] = 0.0; + ptr1[ i ][ i ] = 1.0; + } + +/* Transform these unit vectors using the inverse PermMap. */ + (void) astTransform( pm, pset1, 0, pset2 ); + +/* Copy the transformed vectors into the matrix array. */ + p = matrix; + for( j = 0; j < nax; j++ ){ + for( i = 0; i < nax; i++ ) *(p++) = ptr2[ i ][ j ]; + } + +/* Create a MatrixMap holding this array. */ + mm2 = astMatrixMap( nax, nax, 0, matrix, "", status ); + +/* Create a new MatrixMap equal to the product of the supplied MatrixMap + and the MatrixMap just created from the PermMap. */ + if( mat1 ){ + result = astMtrMult( mm, mm2 ); + } else { + result = astMtrMult( mm2, mm ); + } + +/* Free everything. */ + mm2 = astAnnul( mm2 ) ; + } + + pset2 = astAnnul( pset2 ); + pset1 = astAnnul( pset1 ); + matrix = (double *) astFree( (void *) matrix ); + +/* Re-instate the original settings of the Invert attribute for the + supplied Mappings. */ + astSetInvert( mm, old_minv ); + astSetInvert( pm, old_pinv ); + +/* If an error has occurred, annull the returned MatrixMap. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output MatrixMap. */ + return result; +} + +static void MatPermSwap( AstMapping **maps, int *inverts, int imm, int *status ){ +/* +* Name: +* MatPermSwap + +* Purpose: +* Swap a PermMap and a MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* void MatPermSwap( AstMapping **maps, int *inverts, int imm ) + +* Class Membership: +* MatrixMap member function + +* Description: +* A list of two Mappings is supplied containing a PermMap and a +* MatrixMap. These Mappings are annulled, and replaced with +* another pair of Mappings consisting of a PermMap and a MatrixMap +* in the opposite order. These Mappings are chosen so that their +* combined effect is the same as the original pair of Mappings. + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* imm +* The index within "maps" of the MatrixMap. + +* Notes: +* - There are restictions on the sorts of PermMaps which can be +* swapped with a MatrixMap -- see function CanSwap. It is assumed +* that the supplied MatrixMap and PermMap satisfy these requirements. + +*/ + +/* Local Variables: */ + AstMatrixMap *mm; /* Pointer to the supplied MatrixMap */ + AstMatrixMap *mmnew; /* Pointer to new MatrixMap */ + AstMatrixMap *smmnew; /* Pointer to new simplified MatrixMap */ + AstPermMap *pm; /* Pointer to the supplied PermMap */ + AstPermMap *pmnew; /* Pointer to new PermMap */ + AstPermMap *spmnew; /* Pointer to new simplified PermMap */ + double *consts; /* Pointer to constants array */ + double *matrix; /* Supplied array of matrix elements */ + double *out_el; /* Pointer to next element of new MatrixMap */ + double *out_mat; /* Matrix elements for new MatrixMap */ + double c; /* Constant */ + double matel; /* Matrix element */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int col; /* Index of matrix column */ + int i; /* Axis count */ + int k; /* Axis count */ + int nin; /* No. of axes in supplied PermMap */ + int nout; /* No. of axes in returned PermMap */ + int old_pinv; /* Invert value for the supplied PermMap */ + int row; /* Index of matrix row */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + mmnew = NULL; + pmnew = NULL; + +/* Store pointers to the supplied PermMap and the MatrixMap. */ + pm = (AstPermMap *) maps[ 1 - imm ]; + mm = (AstMatrixMap *) maps[ imm ]; + +/* Temporarily set the Invert attribute of the supplied PermMap to the + supplied value. */ + old_pinv = astGetInvert( pm ); + astSetInvert( pm, inverts[ 1 - imm ] ); + +/* Ensure the MatrixMap is stored in full form. */ + ExpandMatrix( mm, status ); + +/* Store a pointer to the required array of matrix elements. */ + if( inverts[ imm ] ) { + matrix = mm->i_matrix; + } else { + matrix = mm->f_matrix; + } + +/* Get the number of input and output axes of the PermMap. */ + nin = astGetNin( pm ); + nout = astGetNout( pm ); + +/* Allocate memory to hold the matrix elements for the swapped MatrixMap. + The number of rows and olumns in the new matrix must equal the number of + input or output axes for the PermMap, depending on whether the PermMap + or MatrixMap is applied first. */ + if( imm == 0 ) { + out_mat = (double *) astMalloc( sizeof( double )*(size_t)( nout*nout ) ); + } else { + out_mat = (double *) astMalloc( sizeof( double )*(size_t)( nin*nin ) ); + } + +/* We need to know the axis permutation arrays and constants array for + the PermMap. */ + PermGet( pm, &outperm, &inperm, &consts, status ); + if( astOK ) { + +/* First deal with cases where the MatrixMap is applied first. */ + if( imm == 0 ) { + +/* Consider each output axis of the PermMap. */ + for( i = 0; i < nout; i++ ) { + +/* If this output is connected to one of the input axes... */ + row = outperm[ i ]; + if( row >= 0 && row < nin ) { + +/* Permute the row of the supplied matrix which feeds the corresponding + PermMap input axis (i.e. axis outperm[k] ) using the forward PermMap. + Store zeros for any output axes which are assigned constants. This forms + row i of the new MatrixMap. */ + out_el = out_mat + nout*i; + for( k = 0; k < nout; k++ ){ + col = outperm[ k ]; + if( col >= 0 && col < nin ) { + *(out_el++) = *( matrix + nin*row + col ); + } else { + *(out_el++) = 0.0; + } + } + +/* If this output is asigned a constant value, use a "diagonal" vector for + row i of the new MatrixMap (i.e. all zeros except for a 1.0 in column + i ). */ + } else { + out_el = out_mat + nout*i; + for( k = 0; k < nout; k++ ) { + if( k != i ) { + *(out_el++) = 0.0; + } else { + *(out_el++) = 1.0; + } + } + } + } + +/* Create the new MatrixMap. */ + mmnew = astMatrixMap( nout, nout, 0, out_mat, "", status ); + +/* Any PermMap inputs which are assigned a constant value need to be + changed now, since they will no longer be scaled by the inverse + MatrixMap. CanSwap ensures that the inverse MatrixMap produces a + simple scaling for constant axes, so we change the PermMap constant + to be the constant AFTER scaling by the inverse MatrixMap. + + Consider each input axis of the PermMap. */ + for( i = 0; i < nin; i++ ) { + +/* If this input is assigned a constant value... */ + if( inperm[ i ] < 0 ) { + +/* Divide the supplied constant value by the corresponding diagonal term + in the supplied MatrixMap. */ + c = consts[ -inperm[ i ] - 1 ]; + if( c != AST__BAD ) { + matel = matrix[ i*( nin + 1 ) ]; + if( matel != 0.0 && matel != AST__BAD ) { + consts[ -inperm[ i ] - 1 ] /= matel; + } else { + consts[ -inperm[ i ] - 1 ] = AST__BAD; + } + } + } + } + +/* Now deal with cases where the PermMap is applied first. */ + } else { + +/* Consider each input axis of the PermMap. */ + for( i = 0; i < nin; i++ ) { + +/* If this input is connected to one of the output axes... */ + row = inperm[ i ]; + if( row >= 0 && row < nout ) { + +/* Permute the row of the supplied matrix which feeds the corresponding + PermMap output axis (i.e. axis inperm[k] ) using the inverse PermMap. + Store zeros for any input axes which are assigned constants. This forms + row i of the new MatrixMap. */ + out_el = out_mat + nin*i; + for( k = 0; k < nin; k++ ){ + col = inperm[ k ]; + if( col >= 0 && col < nout ) { + *(out_el++) = *( matrix + nout*row + col ); + } else { + *(out_el++) = 0.0; + } + } + +/* If this input is asigned a constant value, use a "diagonal" vector for + row i of the new MatrixMap (i.e. all zeros except for a 1.0 in column + i ). */ + } else { + out_el = out_mat + nin*i; + for( k = 0; k < nin; k++ ) { + if( k != i ) { + *(out_el++) = 0.0; + } else { + *(out_el++) = 1.0; + } + } + } + } + +/* Create the new MatrixMap. */ + mmnew = astMatrixMap( nin, nin, 0, out_mat, "", status ); + +/* Any PermMap outputs which are assigned a constant value need to be + changed now, since they will no longer be scaled by the forward + MatrixMap. CanSwap ensures that the forward MatrixMap produces a + simple scaling for constant axes, so we change the PermMap constant + to be the constant AFTER scaling by the forward MatrixMap. + + Consider each output axis of the PermMap. */ + for( i = 0; i < nout; i++ ) { + +/* If this output is assigned a constant value... */ + if( outperm[ i ] < 0 ) { + +/* Multiple the supplied constant value by the corresponding diagonal term in + the supplied MatrixMap. */ + c = consts[ -outperm[ i ] - 1 ]; + if( c != AST__BAD ) { + matel = matrix[ i*( nout + 1 ) ]; + if( matel != AST__BAD ) { + consts[ -outperm[ i ] - 1 ] *= matel; + } else { + consts[ -outperm[ i ] - 1 ] = AST__BAD; + } + } + } + } + } + +/* Create a new PermMap (since the constants may have changed). */ + pmnew = astPermMap( nin, inperm, nout, outperm, consts, "", status ); + +/* Free the axis permutation and constants arrays. */ + outperm = (int *) astFree( (void *) outperm ); + inperm = (int *) astFree( (void *) inperm ); + consts = (double *) astFree( (void *) consts ); + } + +/* Free the memory used to hold the new matrix elements. */ + out_mat = (double *) astFree( (void *) out_mat ); + +/* Ensure the supplied MatrixMap is stored back in compressed form. */ + CompressMatrix( mm, status ); + +/* Re-instate the original value of the Invert attribute of the supplied + PermMap. */ + astSetInvert( pm, old_pinv ); + + if( astOK ) { + +/* Annul the supplied PermMap. */ + (void) astAnnul( pm ); + +/* Simplify the returned Mappings. */ + spmnew = astSimplify( pmnew ); + pmnew = astAnnul( pmnew ); + + smmnew = astSimplify( mmnew ); + mmnew = astAnnul( mmnew ); + +/* Store a pointer to the new PermMap in place of the supplied MatrixMap. This + PermMap should be used in its forward direction. */ + maps[ imm ] = (AstMapping *) spmnew; + inverts[ imm ] = astGetInvert( spmnew ); + +/* Annul the supplied matrixMap. */ + (void) astAnnul( mm ); + +/* Store a pointer to the new MatrixMap. This MatrixMap should be used in + its forward direction. */ + maps[ 1 - imm ] = (AstMapping *) smmnew; + inverts[ 1 - imm ] = astGetInvert( smmnew ); + } + +/* Return. */ + return; +} + +static void MatWin( AstMapping **maps, int *inverts, int imm, int *status ){ +/* +* Name: +* MatWin + +* Purpose: +* Swap a WinMap and a MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* void MatWin( AstMapping **maps, int *inverts, int imm, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* A list of two Mappings is supplied containing a WinMap and a +* MatrixMap. These Mappings are annulled, and replaced with +* another pair of Mappings consisting of a WinMap and a MatrixMap +* in the opposite order. These Mappings are chosen so that their +* combined effect is the same as the original pair of Mappings. +* The scale factors in the returned WinMap are always unity (i.e. +* the differences in scaling get absorbed into the returned +* MatrixMap). + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* imm +* The index within "maps" of the MatrixMap. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMatrixMap *m1; /* Pointer to Diagonal scale factor MatrixMap */ + AstMatrixMap *m2; /* Pointer to returned MatrixMap */ + AstMatrixMap *sm2; /* Pointer to simplified returned MatrixMap */ + AstMatrixMap *mm; /* Pointer to the supplied MatrixMap */ + AstPointSet *pset1; /* Shift terms from supplied WinMap */ + AstPointSet *pset2; /* Shift terms for returned WinMap */ + AstWinMap *w1; /* Pointer to the returned WinMap */ + AstWinMap *sw1; /* Pointer to the simplified returned WinMap */ + AstWinMap *wm; /* Pointer to the supplied WinMap */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + double *a; /* Array of shift terms from supplied WinMap */ + double *aa; /* Pointer to next shift term */ + double *b; /* Array of scale terms from supplied WinMap */ + double *bb; /* Pointer to next scale term */ + int i; /* Axis count */ + int nin; /* No. of axes in supplied WinMap */ + int nout; /* No. of axes in returned WinMap */ + int old_minv; /* Invert value for the supplied MatrixMap */ + int old_winv; /* Invert value for the supplied WinMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store pointers to the supplied WinMap and the MatrixMap. */ + wm = (AstWinMap *) maps[ 1 - imm ]; + mm = (AstMatrixMap *) maps[ imm ]; + +/* Temporarily set the Invert attribute of the supplied Mappings to the + supplied values. */ + old_winv = astGetInvert( wm ); + astSetInvert( wm, inverts[ 1 - imm ] ); + + old_minv = astGetInvert( mm ); + astSetInvert( mm, inverts[ imm ] ); + +/* Get copies of the shift and scale terms used by the WinMap. This + also returns the number of axes in the WinMap. */ + nin = astWinTerms( wm, &a, &b ); + +/* Create a diagonal MatrixMap holding the scale factors from the + supplied WinMap. */ + m1 = astMatrixMap( nin, nin, 1, b, "", status ); + +/* Create a PointSet holding a single position given by the shift terms + in the supplied WinMap. */ + pset1 = astPointSet( 1, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + if( astOK ){ + aa = a; + for( i = 0; i < nin; i++ ) ptr1[ i ][ 0 ] = *(aa++); + } + +/* First deal with cases when the WinMap is applied first, followed by + the MatrixMap. */ + if( imm == 1 ){ + +/* Multiply the diagonal matrix holding the WinMap scale factors by the + supplied matrix. The resulting MatrixMap is the one to return in the + map list. */ + m2 = astMtrMult( m1, mm ); + +/* Transform the position given by the shift terms from the supplied + WinMap using the supplied MatrixMap to get the shift terms for + the returned WinMap. */ + pset2 = astTransform( mm, pset1, 1, NULL ); + +/* Now deal with cases when the MatrixMap is applied first, followed by + the WinMap. */ + } else { + +/* Multiply the supplied MatrixMap by the diagonal matrix holding scale + factors from the supplied WinMap. The resulting MatrixMap is the one to + return in the map list. */ + m2 = astMtrMult( mm, m1 ); + +/* Transform the position given by the shift terms from the supplied + WinMap using the inverse of the returned MatrixMap to get the shift + terms for the returned WinMap. */ + pset2 = astTransform( m2, pset1, 0, NULL ); + + } + +/* Re-instate the original value of the Invert attributes of the supplied + Mappings. */ + astSetInvert( wm, old_winv ); + astSetInvert( mm, old_minv ); + +/* Get pointers to the shift terms for the returned WinMap. */ + ptr2 = astGetPoints( pset2 ); + +/* Create the returned WinMap, initially with undefined corners. The number of + axes in the WinMap must equal the number of shift terms. */ + nout = astGetNcoord( pset2 ); + w1 = astWinMap( nout, NULL, NULL, NULL, NULL, "", status ); + +/* If succesful, store the scale and shift terms in the WinMap. The scale + terms are always unity. */ + if( astOK ){ + bb = w1->b; + aa = w1->a; + for( i = 0; i < nout; i++ ) { + *(bb++) = 1.0; + *(aa++) = ptr2[ i ][ 0 ]; + } + +/* Replace the supplied Mappings and invert flags with the ones found + above. Remember that the order of the Mappings is now swapped */ + (void) astAnnul( maps[ 0 ] ); + (void) astAnnul( maps[ 1 ] ); + + sw1 = astSimplify( w1 ); + w1 = astAnnul( w1 ); + + maps[ imm ] = (AstMapping *) sw1; + inverts[ imm ] = astGetInvert( sw1 ); + + sm2 = astSimplify( m2 ); + m2 = astAnnul( m2 ); + + maps[ 1 - imm ] = (AstMapping *) sm2; + inverts[ 1 - imm ] = astGetInvert( sm2 ); + + } + +/* Annul the MatrixMap and PointSet holding the scale and shift terms from the + supplied WinMap. */ + m1 = astAnnul( m1 ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Free the copies of the scale and shift terms from the supplied WinMap. */ + b = (double *) astFree( (void *) b ); + a = (double *) astFree( (void *) a ); + +/* Return. */ + return; +} + +static AstMatrixMap *MatZoom( AstMatrixMap *mm, AstZoomMap *zm, int minv, + int zinv, int *status ){ +/* +* Name: +* MatZoom + +* Purpose: +* Create a MatrixMap by merging a MatrixMap and a ZoomMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *MatZoom( AstMatrixMap *mm, AstZoomMap *zm, int minv, +* int zinv, int *status ) + +* Class Membership: +* MatrixMap member function + +* Description: +* This function creates a new MatrixMap which performs a mapping +* equivalent to applying the two supplied Mappings in series in the +* directions specified by the "invert" flags (the Invert attributes of +* the supplied MatrixMaps are ignored). + +* Parameters: +* mm +* A pointer to the MatrixMap. +* zm +* A pointer to the ZoomMap. +* minv +* The invert flag to use with mm. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* zinv +* The invert flag to use with zm. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new MatrixMap. + +* Notes: +* - The forward direction of the returned MatrixMap is equivalent to the +* combined effect of the two supplied Mappings, operating in the +* directions specified by "zinv" and "minv". +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMatrixMap *mm2; /* Pointer to intermediate MatrixMap */ + AstMatrixMap *result; /* Pointer to output MatrixMap */ + double *matrix; /* Pointer to diagonal matrix elements array */ + double zfac; /* Zoom factor */ + int i; /* Axis index */ + int nrow; /* No. of rows in the MatrixMap */ + int old_minv; /* Original setting of MatrixMap Invert attribute */ + int old_zinv; /* Original setting of ZoomMap Invert attribute */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned pointer. */ + result = NULL; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + old_minv = astGetInvert( mm ); + astSetInvert( mm, minv ); + + old_zinv = astGetInvert( zm ); + astSetInvert( zm, zinv ); + +/* Get the number of rows in the MatrixMap (i.e. the number of output + axes). */ + nrow = astGetNout( mm ); + +/* Get the zoom factor implemented by the ZoomMap. Invert it if necessary + since astGetZoom does not take account of the Invert setting. */ + zfac = astGetZoom( zm ); + if( zinv ) zfac = 1.0 / zfac; + +/* Create a diagonal matrix map in which each diagonal element is equal + to the zoom factor. */ + matrix = (double *) astMalloc( sizeof( double )*(size_t) nrow ); + if( astOK ) { + for( i = 0; i < nrow; i++ ) matrix[ i ] = zfac; + } + mm2 = astMatrixMap( nrow, nrow, 1, matrix, "", status ); + matrix = (double *) astFree( (void *) matrix ); + +/* Create a new MatrixMap holding the product of the supplied MatrixMap + and the diagonal MatrixMap just created. */ + result = astMtrMult( mm, mm2 ); + mm2 = astAnnul( mm2 ); + +/* Re-instate the original settings of the Invert attribute for the + supplied Mappings. */ + astSetInvert( mm, old_minv ); + astSetInvert( zm, old_zinv ); + +/* If an error has occurred, annull the returned MatrixMap. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output MatrixMap. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +static AstMatrixMap *MtrMult( AstMatrixMap *this, AstMatrixMap *a, int *status ){ +/* +*+ +* Name: +* astMtrMult + +* Purpose: +* Multiply a MatrixMap by another MatrixMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *MtrMult( astMatrixMap *this, astMatrixMap *a ) + +* Class Membership: +* MatrixMap method + +* Description: +* This function multiples the matrices given by "this" and "a", returning +* a pointer to a new MatrixMap holding the product "a x this". +* +* The number of columns in the "a" matrix must match the number of +* rows in the "this" matrix. The number of rows in the returned +* MatrixMap is equal to the number of rows in "a", and the number of +* columns is the same as the number of rows in "this". + +* Parameters: +* this +* Pointer to the first MatrixMap. +* a +* Pointer to a second MatrixMap. + +* Returned Value: +* A pointer to the product MatrixMap. + +* Notes: +* - An error is reported if the two MatrixMaps have incompatible +* shapes, or if either MatrixMap does not have a defined forward +* transformation. +* - A null Object pointer will also be returned if this function +* is invoked with the AST error status set, or if it should fail +* for any reason. +*- +*/ + +/* Local variables. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMatrixMap *new; /* New MatrixMap holding the product matrix */ + double *a_matrix; /* Pointer to the forward "a" matrix */ + double *a_row; /* Pointer to start of current row in "a" */ + double a_val; /* Current element value from "a" */ + double factor; /* Diagonal matrix term */ + double *new_matrix; /* Pointer to the new forward "this" matrix */ + double *new_val; /* Pointer to current output element value */ + double sum; /* Dot product value */ + double *this_col; /* Pointer to start of current column in "this" */ + double *this_matrix; /* Pointer to the forward "this" matrix */ + double this_val; /* Current element value from "this" */ + int col; /* Current output column number */ + int i; /* Loop count */ + int minrow; /* Min. number of rows in "a" or "this" */ + int ncol_a; /* No. of columns in the "a" MatrixMap */ + int ncol_this; /* No. of columns in the "this" MatrixMap */ + int nrow_a; /* No. of rows in the "a" MatrixMap */ + int nrow_this; /* No. of rows in the "this" MatrixMap */ + int row; /* Current output row number */ + +/* Return a NULL pointer if an error has already occurred. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise */ + new = NULL; + +/* Report an error if eitherof the MatrixMaps doe snot have a defined + forward transformation.*/ + if( !astGetTranForward( this ) ){ + astError( AST__MTRML, "astMtrMult(%s): Cannot find the product of 2 " + "MatrixMaps- the first MatrixMap has no forward transformation.", status, + astClass(this) ); + return NULL; + } + + if( !astGetTranInverse( this ) ){ + astError( AST__MTRML, "astMtrMult(%s): Cannot find the product of 2 " + "MatrixMaps- the second MatrixMap has no forward transformation.", status, + astClass(this) ); + return NULL; + } + +/* Report an error if the shapes of the two matrices are incompatible. */ + nrow_a = astGetNout( a ); + ncol_a = astGetNin( a ); + nrow_this = astGetNout( this ); + ncol_this = astGetNin( this ); + + if( ncol_a != nrow_this && astOK ){ + astError( AST__MTRML, "astMtrMult(%s): Number of rows in the first " + "MatrixMap (%d) does not equal number of columns in the " + "second MatrixMap (%d).", status, astClass(this), nrow_this, ncol_a ); + return NULL; + } + +/* Store the minimum number of rows in either matrix for later use. */ + if( nrow_a < nrow_this ){ + minrow = nrow_a; + } else { + minrow = nrow_this; + } + +/* Ensure that "this" is stored in FULL form (i.e. with all elements + stored explicitly, even if the matrix is a unit or diagonal matrix). */ + ExpandMatrix( this, status ); + +/* Store pointers to the current forward matrices (taking into + account the current states of the Mapping inversion flags ). */ + this_matrix = astGetInvert( this ) ? this->i_matrix : this->f_matrix; + a_matrix = astGetInvert( a ) ? a->i_matrix : a->f_matrix; + +/* Get memory to hold the product matrix in full form. */ + new_matrix = (double *) astMalloc( sizeof( double )* + (size_t)( nrow_a*ncol_this ) ); + if( astOK ){ + +/* First deal with cases where the "a" MatrixMap represents a unit + matrix. */ + if( a->form == UNIT ){ + +/* Copy the required number of rows from "this" to "new". */ + (void) memcpy( (void *) new_matrix, (const void *) this_matrix, + sizeof(double)*(size_t)( minrow*ncol_this ) ); + +/* If there are insufficient rows in "this", append some zero-filled rows. */ + if( minrow < nrow_a ){ + for( i = minrow*ncol_this; i < nrow_a*ncol_this; i++ ){ + new_matrix[ i ] = 0.0; + } + } + +/* Now deal with cases where the "a" MatrixMap represents a diagonal + matrix. */ + } else if( a->form == DIAGONAL ){ + +/* Scale the required number of rows from "this" storing them in "new", + and checking for bad values. */ + i = 0; + + for( row = 0; row < minrow; row++ ){ + factor = a_matrix[ row ]; + + if( factor != AST__BAD ){ + + for( col = 0; col < ncol_this; col++ ){ + this_val = this_matrix[ i ]; + if( this_val != AST__BAD ){ + new_matrix[ i ] = this_val*factor; + } else { + new_matrix[ i ] = AST__BAD; + } + i++; + } + + } else { + + for( col = 0; col < ncol_this; col++ ){ + new_matrix[ i++ ] = AST__BAD; + } + + } + } + +/* If there are insufficient rows in "this", append some zero-filled rows. */ + if( minrow < nrow_a ){ + for( i = minrow*ncol_this; i < nrow_a*ncol_this; i++ ){ + new_matrix[ i ] = 0.0; + } + } + + +/* Now deal with cases where the "a" MatrixMap represents a full, non-diagonal + matrix. */ + } else { + +/* Initialise a pointer to the next element in the product matrix. */ + new_val = new_matrix; + +/* Get a pointer to the start of each row of the "a" matrix. */ + for( row = 0; row < nrow_a; row++ ){ + a_row = a_matrix + ncol_a*row; + +/* Get a pointer to the start of each column of the "this" matrix. */ + for( col = 0; col < ncol_this; col++ ){ + this_col = this_matrix + col; + +/* Form the dot product of the current row from "a", and the current + column from "this", checking for bad values. */ + sum = 0.0; + for( i = 0; i < ncol_a; i++ ){ + a_val = a_row[ i ]; + this_val = this_col[ i*ncol_this ]; + if( a_val != AST__BAD && this_val != AST__BAD ){ + sum += a_val*this_val; + } else { + sum = AST__BAD; + break; + } + } + +/* Store the output matrix element value. */ + *(new_val++) = sum; + + } + } + } + +/* Create the new MatrixMap. */ + new = astInitMatrixMap( NULL, sizeof( AstMatrixMap ), !class_init, + &class_vtab, "MatrixMap", ncol_this, nrow_a, + FULL, new_matrix ); + +/* If possible, compress the new MatrixMap by removing off-diagonal zero + elements. */ + CompressMatrix( new, status ); + +/* Re-compress the original "this" MatrixMap. */ + CompressMatrix( this, status ); + + } + +/* Free the memory used to hold the product matrix in full form. */ + new_matrix = (double *) astFree( (void *) new_matrix ); + + return new; + +} + +static AstMatrixMap *MtrRot( AstMatrixMap *this, double theta, + const double axis[], int *status ){ +/* +*+ +* Name: +* astMtrRot + +* Purpose: +* Multiply a MatrixMap by a rotation matrix. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *astMtrRot( astMatrixMap *this, double theta, +* const double axis[] ) + +* Class Membership: +* MatrixMap method. + +* Description: +* This function creates a new MatrixMap which is a copy of "this", +* rotated by a specified angle. It can only be used on MatrixMaps which +* have either 2 or 3 output coordinates. In the 3-D case, the rotation +* is about an arbitrary axis passing through the origin. + +* Parameters: +* this +* Pointer to the MatrixMap. +* theta +* The angle by which to rotate the matrix, in radians. If the matrix +* is applied to a 2-D vector position, the resulting vector is +* rotated clockwise about the origin (i.e. from the positive direction +* of the second axis to the positive direction of the first axis). If +* the vector positions are three dimensional, the rotation is clockwise +* when looking along the vector given by "axis". Note, "theta" measures +f when looking along the vector given by AXIS. Note, THETA measures +* the movemement of the vectors relative to a fixed reference frame. +* Alternatively, the reference frame can be thought of as rotating by +* (-theta) relative to the fixed vectors. +* axis +* A 3-D vector specifying the axis of rotation. This parameter is +* ignored if the output from MatrixMap is 2-dimensional. + +* Returned Value: +* A pointer to the rotated MatrixMap. + +* Notes: +* - A null Object pointer will also be returned if this function +* is invoked with the AST error status set, or if it should fail +* for any reason. +*- +*/ + +/* Local variables. */ + AstMatrixMap *new; /* New MatrixMap holding the rotated matrix */ + double as,a,b,c,d,e,f,g; /* Intermediate quantities */ + double axlen; /* Length of axis vector */ + double axlen2; /* Squared length of axis vector */ + double costh; /* Cos(rotation angle) */ + double sinth; /* Sin(rotation angle) */ + double rotmat[9]; /* Rotation matrix */ + double work[3]; /* Work space for matrix multiplication */ + int ncol; /* No. of columns in the MatrixMap */ + int nrow; /* No. of rows in the MatrixMap */ + +/* Return a NULL pointer if an error has already occurred. */ + if ( !astOK ) return NULL; + +/* Initialise the returned MarixMap to be a copy of the supplied MatrixMap. */ + new = astCopy( this ); + +/* Save the cos and sin of the rotation angle for future use. */ + costh = cos( theta ); + sinth = sin( theta ); + +/* Return without changing the MatrixMap if the rotation angle is a + multiple of 360 degrees. */ + if ( costh == 1.0 ) return new; + +/* Get the dimensions of the MatrixMap. */ + nrow = astGetNout( new ); + ncol = astGetNin( new ); + +/* First do rotation of a plane about the origin. */ + if( nrow == 2 ){ + +/* Ensure that the MatrixMap is stored in full form rather than + compressed form. */ + ExpandMatrix( new, status ); + +/* Form the 2x2 forward rotation matrix. Theta is the clockwise angle + of rotation. */ + rotmat[0] = costh; + rotmat[1] = sinth; + rotmat[2] = -sinth; + rotmat[3] = costh; + +/* Post-multiply the current forward matrix (depending on whether or not + the MatrixMap has been inverted) by the forward rotation matrix. */ + if( !astGetInvert( new ) ){ + SMtrMult( 1, 2, ncol, rotmat, new->f_matrix, work, status ); + } else { + SMtrMult( 1, 2, ncol, rotmat, new->i_matrix, work, status ); + } + +/* Now form the 2x2 inverse rotation matrix (the diagonal elements + don't change). */ + rotmat[1] = -sinth; + rotmat[2] = sinth; + +/* Pre-multiply the current inverse matrix (depending on whether or + not the MatrixMap has been inverted) by the inverse rotation matrix. */ + if( !astGetInvert( new ) ){ + SMtrMult( 0, ncol, 2, rotmat, new->i_matrix, work, status ); + } else { + SMtrMult( 0, ncol, 2, rotmat, new->f_matrix, work, status ); + } + +/* See if the matrix can be stored as a UNIT or DIAGONAL matrix. */ + CompressMatrix( new, status ); + +/* Now do rotation of a volume about an axis passing through the origin. */ + } else if( nrow == 3 ){ + +/* Find the length of the axis vector. Report an error if it has zero + length or has not been supplied. */ + if( axis ) { + axlen2 = axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]; + } else { + axlen2 = 0.0; + } + if( axlen2 <= 0.0 ) { + astError( AST__MTRAX, "astMtrRot(%s): NULL or zero length " + "axis vector supplied.", status, astClass(new) ); + } + axlen = sqrt( axlen2 ); + +/* Ensure that the MatrixMap is stored in full form rather than + compressed form. */ + ExpandMatrix( new, status ); + +/* Form commonly used terms in the rotation matrix. */ + as = sinth/axlen; + a = (1.0 - costh)/axlen2; + b = a*axis[0]*axis[1]; + c = as*axis[2]; + d = a*axis[0]*axis[2]; + e = as*axis[1]; + f = a*axis[1]*axis[2]; + g = as*axis[0]; + +/* Form the 3x3 forward rotation matrix. Theta is the clockwise angle + of rotation looking in the direction of the axis vector. */ + rotmat[0] = a*axis[0]*axis[0] + costh; + rotmat[1] = b - c; + rotmat[2] = d + e; + rotmat[3] = b + c; + rotmat[4] = a*axis[1]*axis[1] + costh; + rotmat[5] = f - g; + rotmat[6] = d - e; + rotmat[7] = f + g; + rotmat[8] = a*axis[2]*axis[2] + costh; + +/* Post-multiply the current forward matrix (depending on whether or not + the MatrixMap has been inverted) by the forward rotation matrix. */ + if( !astGetInvert( new ) ){ + SMtrMult( 1, 3, ncol, rotmat, new->f_matrix, work, status ); + } else { + SMtrMult( 1, 3, ncol, rotmat, new->i_matrix, work, status ); + } + +/* Now form the 3x3 inverse rotation matrix (the diagonal elements + don't change). */ + rotmat[1] = b + c; + rotmat[2] = d - e; + rotmat[3] = b - c; + rotmat[5] = f + g; + rotmat[6] = d + e; + rotmat[7] = f - g; + +/* Pre-multiply the current inverse matrix (depending on whether or + not the MatrixMap has been inverted) by the inverse rotation matrix. */ + if( !astGetInvert( new ) ){ + SMtrMult( 0, ncol, 3, rotmat, new->i_matrix, work, status ); + } else { + SMtrMult( 0, ncol, 3, rotmat, new->f_matrix, work, status ); + } + +/* See if the matrix can be stored as a UNIT or DIAGONAL matrix. */ + CompressMatrix( new, status ); + +/* Report an error if the matrix is not suitable for rotation. */ + } else { + astError( AST__MTR23, "astMtrRot(%s): Cannot rotate a %dx%d" + " MatrixMap.", status, astClass(new), nrow, ncol ); + } + +/* Delete the new MatrixMap if an error has occurred. */ + if( !astOK ) new = astDelete( new ); + + return new; + +} + +static void PermGet( AstPermMap *map, int **outperm, int **inperm, + double **consts, int *status ){ +/* +* Name: +* PermGet + +* Purpose: +* Get the axis permutation and constants array for a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* void PermGet( AstPermMap *map, int **outperm, int **inperm, +* double **const, int *status ) + +* Class Membership: +* MatrixMap member function + +* Description: +* This function returns axis permutation and constants arrays which can +* be used to create a PermMap which is equivalent to the supplied PermMap. + +* Parameters: +* map +* The PermMap. +* outperm +* An address at which to return a popinter to an array of ints +* holding the output axis permutation array. The array should be +* released using astFree when no longer needed. +* inperm +* An address at which to return a popinter to an array of ints +* holding the input axis permutation array. The array should be +* released using astFree when no longer needed. +* consts +* An address at which to return a popinter to an array of doubles +* holding the constants array. The array should be released using +* astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - NULL pointers are returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding input positions for PermMap */ + AstPointSet *pset2; /* PointSet holding output positions for PermMap */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + double *cnst; /* Pointer to constants array */ + double cn; /* Potential new constant value */ + double ip; /* Potential output axis index */ + double op; /* Potential input axis index */ + int *inprm; /* Pointer to input axis permutation array */ + int *outprm; /* Pointer to output axis permutation array */ + int i; /* Axis count */ + int nc; /* Number of constants stored so far */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + +/* Initialise. */ + if( outperm ) *outperm = NULL; + if( inperm ) *inperm = NULL; + if( consts ) *consts = NULL; + +/* Check the global error status and the supplied pointers. */ + if ( !astOK || !outperm || !inperm || !consts ) return; + +/* Get the number of input and output axes for the supplied PermMap. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* Allocate the memory for the returned arrays. */ + outprm = (int *) astMalloc( sizeof( int )* (size_t) nout ); + inprm = (int *) astMalloc( sizeof( int )* (size_t) nin ); + cnst = (double *) astMalloc( sizeof( double )* (size_t) ( nout + nin ) ); + +/* Returned the pointers to these arrays.*/ + *outperm = outprm; + *inperm = inprm; + *consts = cnst; + +/* Create two PointSets, each holding two points, which can be used for + input and output positions with the PermMap. */ + pset1 = astPointSet( 2, nin, "", status ); + pset2 = astPointSet( 2, nout, "", status ); + +/* Set up the two input positions to be [0,1,2...] and [-1,-1,-1,...]. The + first position is used to enumerate the axes, and the second is used to + check for constant axis values. */ + ptr1 = astGetPoints( pset1 ); + if( astOK ){ + for( i = 0; i < nin; i++ ){ + ptr1[ i ][ 0 ] = ( double ) i; + ptr1[ i ][ 1 ] = -1.0; + } + } + +/* Use the PermMap to transform these positions in the forward direction. */ + (void) astTransform( map, pset1, 1, pset2 ); + +/* No constant axis valeus found yet. */ + nc = 0; + +/* Look at the mapped positions to determine the output axis permutation + array. */ + ptr2 = astGetPoints( pset2 ); + if( astOK ){ + +/* Do each output axis. */ + for( i = 0; i < nout; i++ ){ + +/* If the output axis value is copied from an input axis value, the index + of the appropriate input axis will be in the mapped first position. */ + op = ptr2[ i ][ 0 ]; + +/* If the output axis value is assigned a constant value, the result of + mapping the two different input axis values will be the same. */ + cn = ptr2[ i ][ 1 ]; + if( op == cn ) { + +/* We have found another constant. Store it in the constants array, and + store the index of the constant in the output axis permutation array. */ + cnst[ nc ] = cn; + outprm[ i ] = -( nc + 1 ); + nc++; + +/* If the output axis values are different, then the output axis value + must be copied from the input axis value. */ + } else { + outprm[ i ] = (int) ( op + 0.5 ); + } + } + } + +/* Now do the same thing to determine the input permutation array. */ + if( astOK ){ + for( i = 0; i < nout; i++ ){ + ptr2[ i ][ 0 ] = ( double ) i; + ptr2[ i ][ 1 ] = -1.0; + } + } + + (void) astTransform( map, pset2, 0, pset1 ); + + if( astOK ){ + + for( i = 0; i < nin; i++ ){ + + ip = ptr1[ i ][ 0 ]; + cn = ptr1[ i ][ 1 ]; + if( ip == cn ) { + + cnst[ nc ] = cn; + inprm[ i ] = -( nc + 1 ); + nc++; + + } else { + inprm[ i ] = (int) ( ip + 0.5 ); + } + } + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* If an error has occurred, attempt to free the returned arrays. */ + if( !astOK ) { + *outperm = (int *) astFree( (void *) *outperm ); + *inperm = (int *) astFree( (void *) *inperm ); + *consts = (double *) astFree( (void *) *consts ); + } + +/* Return. */ + return; +} + +static int PermOK( AstMapping *pm, int *status ){ +/* +* Name: +* PermOK + +* Purpose: +* Determine if a PermMap can be merged with a MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int PermOK( AstMapping *pm, int *status ) + +* Class Membership: +* PermMap member function + +* Description: +* This function returns a flag indicating if the supplied PermMap +* could be merged with a MatrixMap. For thios to be possible, the +* PermMap must have the same number of input and output axes, and the +* inverse and forward mappings must be consistent. + +* Parameters: +* pm +* The PermMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if the PermMap can be merged, 0 otherwise. + +* Notes: +* - A value of 0 is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding input positions for PermMap */ + AstPointSet *pset2; /* PointSet holding output positions for PermMap */ + double **ptr1; /* Pointer to pset1 data */ + int i; /* Loop count */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + int ret; /* Returned flag */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise */ + ret = 0; + +/* The PermMap must have the same number of input and output coordinates. */ + nin = astGetNin( pm ); + nout = astGetNout( pm ); + if( nin == nout ){ + +/* Create two PointSets, each holding two points, which can be used for + the input and output positions with the PermMap. */ + pset1 = astPointSet( 2, nin, "", status ); + pset2 = astPointSet( 2, nout, "", status ); + +/* Set up the two input positions to be [1,2,3...] and [0,-1,-2,...] */ + ptr1 = astGetPoints( pset1 ); + if( astOK ){ + for( i = 0; i < nin; i++ ){ + ptr1[ i ][ 0 ] = ( double )( i + 1 ); + ptr1[ i ][ 1 ] = ( double )( -i ); + } + + } + +/* Use the PermMap to transform these positions in the forward direction. */ + (void) astTransform( pm, pset1, 1, pset2 ); + +/* Now transform the results back again using the inverse PermMap. */ + (void) astTransform( pm, pset2, 0, pset1 ); + +/* See if the input positions have changed. If they have, then the PermMap + does not have a consistent pair of transformations. If they have not, + then the transformations must be consistent because we used two + different input positions and only one could come out unchanged by + chance. */ + if( astOK ){ + ret = 1; + for( i = 0; i < nin; i++ ){ + if( ptr1[ i ][ 0 ] != ( double )( i + 1 ) || + ptr1[ i ][ 1 ] != ( double )( -i ) ){ + ret = 0; + break; + } + } + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + } + +/* Return the answer. */ + return astOK ? ret : 0; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* MatrixMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + +/* Local Variables: */ + AstMatrixMap *map; + double *matrix; + double result; + +/* Check inherited status */ + if( !astOK ) return AST__BAD; + +/* Get a pointer to the MatrixMap structure. */ + map = (AstMatrixMap *) this; + +/* Get a pointer to the array holding the required matrix elements, according + to whether the MatrixMap has been inverted. */ + if( !astGetInvert( this ) ) { + matrix = map->f_matrix; + } else { + matrix = map->i_matrix; + } + +/* First deal with full MatrixMaps in which all matrix elements are stored. */ + if( map->form == FULL ){ + result = matrix[ ax1*astGetNin( this ) + ax2 ]; + +/* For unit matrices, the rate is unity if the input and output axes are + equal, and zero otherwise. */ + } else if( map->form == UNIT ){ + result = (ax1 == ax2 ) ? 1.0 : 0.0; + +/* For diagonal matrices, the rate is zero for off diagonal elements and + the matrix array stored the on-diagonal rates. */ + } else if( ax1 == ax2 ) { + result = matrix[ ax1 ]; + + } else { + result = 0.0; + } + +/* Return the result. */ + return result; +} + +static void SMtrMult( int post, int m, int n, const double *mat1, + double *mat2, double *work, int *status ){ +/* +* Name: +* SMtrMult + +* Purpose: +* Multiply a square matrix and a non-square matrix. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* void SMtrMult( int post, int m, int n, const double *mat1, +* double *mat2, double *work, int *status ) + +* Class Membership: +* MatrixMap member function. + +* Description: +* The matrix pointed to by "mat2" is modified by multiplying it by +* the square matrix pointed to by "mat1". If "post" is 1, then: +* +* mat2 -> mat1*mat2 (mat1 is mxm and mat2 is mxn) +* +* If "post" is zero, then: +* +* mat2 -> mat2*mat1 (mat1 is nxn and mat2 is mxn) +* +* The restriction that "mat1" must be square is imposed so that the +* returned matrix will have the same shape as the supplied matrix (mat1). + +* Parameters: +* post +* Specifies whether to post- or pre- multiply mat2 by mat1. +* m +* The number of rows in mat2. +* n +* The number of columns in mat2. +* mat1 +* The multiplier matrix. It must be square of size m or n, depending +* on "post". +* mat2 +* The multiplicand matrix. +* work +* Pointer to work space containing room for m doubles (if "post" +* is 1), or n doubles (if "post" is 0). +* status +* Pointer to the inherited status variable. + +* Notes: +* - No error is reported if "mat2" is supplied NULL. In this case +* it will also be returned NULL. +*/ + +/* Local Variables */ + double dot; /* Output matrix element value */ + const double *mat1_col; /* Pointer to start of current column of mat1 */ + const double *mat1_row; /* Pointer to start of current row of mat1 */ + double *mat2_col; /* Pointer to start of current column of mat2 */ + double *mat2_row; /* Pointer to start of current row of mat2 */ + double cel; /* Column element value */ + double rel; /* Row element value */ + int i; /* Index of current output row */ + int j; /* Index of current output column */ + int k; /* Dot product index */ + +/* Do nothing if mat2 is NULL */ + if ( mat2 ){ + +/* First deal with cases where the supplied matrix is post-multiplied + (i.e. the returned matrix is mat1*mat2). */ + if( post ){ + +/* Loop round each column of the output matrix, storing a pointer to + the start of the corresponding column of mat2. */ + for( j=0; jform == UNIT ) { + result = 1; + +/* Otherwise, check that the appropriate array is defined in the + MatrixMap. */ + } else { + +/* Determine if the Mapping has been inverted. */ + invert = astGetInvert( this ); + +/* If OK, obtain the result. */ + if ( astOK ) { + + if( invert ){ + result = ( map->i_matrix != NULL ); + } else { + result = ( map->f_matrix != NULL ); + } + + } + + } + +/* Return the result. */ + return result; + +} + +static int GetTranInverse( AstMapping *this, int *status ) { +/* +* +* Name: +* GetTranInverse + +* Purpose: +* Determine if a MatrixMap defines an inverse coordinate transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int GetTranInverse( AstMapping *this, int *status ) + +* Class Membership: +* MatrixMap member function (over-rides the astGetTranInverse method +* inherited from the Mapping class). + +* Description: +* This function returns a value indicating if the MatrixMap is able +* to perform an inverse coordinate transformation. + +* Parameters: +* this +* Pointer to the MatrixMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the inverse coordinate transformation is not defined, or 1 if it +* is. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMatrixMap *map; /* Pointer to MatrixMap to be queried */ + int invert; /* Has the mapping been inverted? */ + int result; /* The returned value */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the MatrixMap. */ + map = (AstMatrixMap *) this; + +/* All unit MatrixMaps are defined in both directions. */ + if( map->form == UNIT ) { + result = 1; + +/* Otherwise, check that the appropriate array is defined in the + MatrixMap. */ + } else { + +/* Determine if the Mapping has been inverted. */ + invert = astGetInvert( this ); + +/* If OK, obtain the result. */ + if ( astOK ) { + + if( invert ){ + result = ( map->f_matrix != NULL ); + } else { + result = ( map->i_matrix != NULL ); + } + + } + + } + +/* Return the result. */ + return result; + +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a MatrixMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* MatrixMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a MatrixMap and a set of points encapsulated in a +* PointSet and transforms the points by multiplying them by the matrix. + +* Parameters: +* this +* Pointer to the MatrixMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of columns in the MatrixMap being applied. +* - The number of coordinate values per point in the output PointSet will +* equal the number of rows in the MatrixMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstMatrixMap *map; /* Pointer to MatrixMap to be applied */ + double diag_term; /* Current diagonal element value */ + double *indata; /* Pointer to next input data value */ + double *matrix; /* Pointer to start of matrix element array */ + double *matrix_element; /* Pointer to current matrix element value */ + double *outdata; /* Pointer to next output data value */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double sum; /* Partial output value */ + double val; /* Data value */ + int in_coord; /* Index of output coordinate */ + int nax; /* Output axes for which input axes exist */ + int ncoord_in; /* Number of coordinates per input point */ + int ncoord_out; /* Number of coordinates per output point */ + int npoint; /* Number of points */ + int out_coord; /* Index of output coordinate */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the MatrixMap. */ + map = (AstMatrixMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + and output PointSets and obtain pointers for accessing the input and + output coordinate values. */ + ncoord_in = astGetNcoord( in ); + ncoord_out = astGetNcoord( result ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Get a pointer to the array holding the required matrix elements, according + to the direction of mapping required. */ + if ( forward ) { + matrix = map->f_matrix; + } else { + matrix = map->i_matrix; + } + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* First deal with full MatrixMaps in which all matrix elements are stored. */ + if( map->form == FULL ){ + +/* Loop to apply the matrix to each point in turn, checking for + (and propagating) bad values in the process. The matrix elements are + accessed sequentially in row order. The next matrix element to be + used is identified by a pointer which is initialised to point to the + first element of the matrix prior to processing each point. */ + for ( point = 0; point < npoint; point++ ) { + matrix_element = matrix; + +/* Each output co-ordinate value is created by summing the product of the + corresponding input co-ordinates and the elements of one row of the + matrix. */ + for ( out_coord = 0; out_coord < ncoord_out; out_coord++ ) { + sum = 0.0; + + for ( in_coord = 0; in_coord < ncoord_in; in_coord++ ) { + +/* Check the current input coordinate value and the current matrix element. + If the coordinate value is bad, then the output value will also be + bad unless the matrix element is zero. That is, a zero matrix element + results in the input coordinate value being ignored, even if it is bad. + This prevents bad input values being propagated to output axes which + are independant of the bad input axis. A bad matrix element always results + in the output value being bad. In either of these cases, break out of the + loop, remembering to advance the pointer to the next matrix element so + that it points to the start of the next row ready for doing the next + output coordinate. */ + if ( ( ptr_in[ in_coord ][ point ] == AST__BAD && + (*matrix_element) != 0.0 ) || + (*matrix_element) == AST__BAD ) { + sum = AST__BAD; + matrix_element += ncoord_in - in_coord; + break; + +/* If the input coordinate and the current matrix element are both + valid, increment the sum by their product, and step to the next matrix + element pointer If we arrive here with a bad input value, then the + matrix element must be zero, in which case the running sum is left + unchanged. */ + } else { + if ( ptr_in[ in_coord ][ point ] != AST__BAD ) { + sum += ptr_in[ in_coord ][ point ] * (*matrix_element); + } + matrix_element++; + } + } + +/* Store the output coordinate value. */ + ptr_out[ out_coord ][ point ] = sum; + + } + + } + +/* Now deal with unit and diagonal MatrixMaps. */ + } else { + +/* Find the number of output axes for which input data is available. */ + if( ncoord_in < ncoord_out ){ + nax = ncoord_in; + } else { + nax = ncoord_out; + } + +/* For unit matrices, copy the input axes to the corresponding output axes. */ + if( map->form == UNIT ){ + for( out_coord = 0; out_coord < nax; out_coord++ ) { + (void) memcpy( ptr_out[ out_coord ], + (const void *) ptr_in[ out_coord ], + sizeof( double )*(size_t)npoint ); + } + +/* For diagonal matrices, scale each input axis using the appropriate + diagonal element from the matrix, and store in the output. */ + } else { + for( out_coord = 0; out_coord < nax; out_coord++ ){ + diag_term = matrix[ out_coord ]; + outdata = ptr_out[ out_coord ]; + indata = ptr_in[ out_coord ]; + + if( diag_term != AST__BAD ){ + for( point = 0; point < npoint; point++ ){ + val = *(indata++); + if( val != AST__BAD ){ + *(outdata++) = diag_term*val; + } else { + *(outdata++) = AST__BAD; + } + } + + } else { + for( point = 0; point < npoint; point++ ){ + *(outdata++) = AST__BAD; + } + } + } + } + +/* If there are any remaining output axes, fill the first one with zeros. */ + if( nax < ncoord_out ){ + outdata = ptr_out[ nax ]; + for( point = 0; point < npoint; point++ ) *(outdata++) = 0.0; + +/* Copy this axis to any remaining output axes. */ + outdata = ptr_out[ nax ]; + for( out_coord = nax + 1; out_coord < ncoord_out; out_coord++ ) { + (void) memcpy( ptr_out[ out_coord ], (const void *) outdata, + sizeof( double )*(size_t)npoint ); + } + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static int ScalingRowCol( AstMatrixMap *map, int axis, int *status ){ +/* +* Name: +* ScalingRowCol + +* Purpose: +* Determine if a given row and column of a MatrixMap are zeros +* with a non-zero diagonal term. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int ScalingRowCol( AstMatrixMap *map, int axis, int *status ) + +* Class Membership: +* MatrixMap member function + +* Description: +* This function returns a flag indicating if a MatrixMap presents a +* simple scaling for a given axis in both directions. The MatrixMap +* must be square. A value of one is returned if every element of the +* row and column corresponding to the given axis is zero, except for +* the diagonal term which must be non-zero. + +* Parameters: +* map +* The MatrixMap. +* axis +* The zero-based index of the axis to check. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if the row/column produces a simple scaling, 0 otherwise. + +*/ + +/* Local Variables: */ + double *el; /* Pointer to matrix element */ + int i; /* Element count */ + int ncol; /* No. of input coordinates */ + int ret; /* Returned flag */ + +/* Initialise */ + ret = 0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* If a unit or diagonal MatrixMap has been supplied, return 1. */ + if( map->form != FULL ){ + ret = 1; + +/* If a full matrix has been supplied... */ + } else { + +/* Assume the row/column gives a unit mapping. */ + ret = 1; + +/* Get the number of input axes for the MatrixMap. */ + ncol = astGetNin( map ); + +/* Check that all elements of the "axis"th row are effectively zero, except + for the "axis"th element which must be non-zero. */ + el = map->f_matrix + axis*ncol; + for( i = 0; i < ncol; i++ ) { + if( i == axis ) { + if( fabs( *el ) <= DBL_EPSILON ) { + ret = 0; + break; + } + } else if( fabs( *el ) > DBL_EPSILON ) { + ret = 0; + break; + } + el++; + } + +/* Check that all elements of the "axis"th column are effectively zero, except + for the "axis"th element which must be non-zero. */ + if( ret ) { + el = map->f_matrix + axis; + for( i = 0; i < ncol; i++ ) { + if( i == axis ) { + if( fabs( *el ) <= DBL_EPSILON ) { + ret = 0; + break; + } + } else if( fabs( *el ) > DBL_EPSILON ) { + ret = 0; + break; + } + el += ncol; + } + } + } + +/* Return the answer. */ + return astOK ? ret : 0; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for MatrixMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for MatrixMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the matrix +* element values associated with the input MatrixMap. +*/ + + +/* Local Variables: */ + AstMatrixMap *in; /* Pointer to input MatrixMap */ + AstMatrixMap *out; /* Pointer to output MatrixMap */ + int nel; /* No. of elements in the matrix */ + int nin; /* No. of input coordinates */ + int nout; /* No. of output coordinates */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output MatrixMaps. */ + in = (AstMatrixMap *) objin; + out = (AstMatrixMap *) objout; + +/* Nullify the pointers stored in the output object since these will + currently be pointing at the input data (since the output is a simple + byte-for-byte copy of the input). Otherwise, the input data could be + freed by accidient if the output object is deleted due to an error + occuring in this function. */ + out->f_matrix = NULL; + out->i_matrix = NULL; + +/* If the input MatrixMap is a unit mapping, then no matrix elements are + stored with it, so do nothing in this case. */ + if( out->form != UNIT ){ + +/* Obtain the number of stored values in the MatrixMap. This is independant of + whether the Mapping has been inverted or not. If the MatrixMap is diagonal, + only the diagonal terms are stored. */ + nin = astGetNin( in ); + nout = astGetNout( in ); + + if( out->form == DIAGONAL ){ + if( nin < nout ){ + nel = nin; + } else { + nel = nout; + } + + } else { + nel = nin*nout; + } + +/* Store the forward matrix elements in the output MatrixMap. */ + out->f_matrix = (double *) astStore( NULL, (void *) in->f_matrix, + sizeof( double )*(size_t) nel ); + +/* Store the inverse matrix elements (if defined) in the output + MatrixMap. */ + if( in->i_matrix ){ + out->i_matrix = (double *) astStore( NULL, (void *) in->i_matrix, + sizeof( double )*(size_t) nel ); + } + +/* If an error has occurred, free the output MatrixMap arrays. */ + if( !astOK ) { + out->f_matrix = (double *) astFree( (void *) out->f_matrix ); + out->i_matrix = (double *) astFree( (void *) out->i_matrix ); + } + } + + return; + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for MatrixMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for MatrixMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstMatrixMap *this; /* Pointer to MatrixMap */ + +/* Obtain a pointer to the MatrixMap structure. */ + this = (AstMatrixMap *) obj; + +/* Free the arrays used to store element values for forward and inverse + matrices. */ + this->f_matrix = (double *) astFree( (void *) this->f_matrix ); + this->i_matrix = (double *) astFree( (void *) this->i_matrix ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for MatrixMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the MatrixMap class to an output Channel. + +* Parameters: +* this +* Pointer to the MatrixMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstMatrixMap *this; /* Pointer to the MatrixMap structure */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int el; /* Element index */ + int nel; /* No. of elements in the matrix */ + int nin; /* No. of input coords */ + int nout; /* No. of output coords */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the MatrixMap structure. */ + this = (AstMatrixMap *) this_object; + +/* Find the number of elements stored for each matrix. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + + if( this->form == FULL ){ + nel = nin*nout; + + } else if( this->form == DIAGONAL ){ + nel = astMIN( nin, nout ); + + } else { + nel = 0; + } + +/* Write out values representing the instance variables for the + MatrixMap class. */ + +/* The forward matrix. The inverse matrix not written out since it can be + re-calculated when the MatrixMap is read back in. Note BAD values are + not written out as the AST__BAD value may differ on different machines. + If a matrix element is not found when reading the matrix back in + again (in astLoadMatrixMap), then it is assigned a default value of + AST__BAD. */ + if( this->f_matrix ){ + for( el = 0; el < nel; el++ ){ + if( (this->f_matrix)[ el ] != AST__BAD ) { + (void) sprintf( buff, "M%d", el ); + astWriteDouble( channel, buff, 1, 1, (this->f_matrix)[ el ], + "Forward matrix value" ); + } + } + } + +/* The matrix storage form. */ + astWriteString( channel, "Form", 1, 1, Form[ this->form ], + "Matrix storage form" ); + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAMatrixMap and astCheckMatrixMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(MatrixMap,Mapping) +astMAKE_CHECK(MatrixMap) + +AstMatrixMap *astMatrixMap_( int nin, int nout, int form, + const double matrix[], const char *options, int *status, ...){ +/* +*++ +* Name: +c astMatrixMap +f AST_MATRIXMAP + +* Purpose: +* Create a MatrixMap. + +* Type: +* Public function. + +* Synopsis: +c #include "matrixmap.h" +c AstMatrixMap *astMatrixMap( int nin, int nout, int form, +c const double matrix[], +c const char *options, ... ) +f RESULT = AST_MATRIXMAP( NIN, NOUT, FORM, MATRIX, OPTIONS, STATUS ) + +* Class Membership: +* MatrixMap constructor. + +* Description: +* This function creates a new MatrixMap and optionally initialises +* its attributes. +* +* A MatrixMap is a form of Mapping which performs a general linear +* transformation. Each set of input coordinates, regarded as a +* column-vector, are pre-multiplied by a matrix (whose elements +* are specified when the MatrixMap is created) to give a new +* column-vector containing the output coordinates. If appropriate, +* the inverse transformation may also be performed. + +* Parameters: +c nin +f NIN = INTEGER (Given) +* The number of input coordinates, which determines the number +* of columns in the matrix. +c nout +f NOUT = INTEGER (Given) +* The number of output coordinates, which determines the number +* of rows in the matrix. +c form +f FORM = INTEGER (Given) +* An integer which indicates the form in which the matrix +* elements will be supplied. +* +c A value of zero indicates that a full "nout" x "nin" matrix +f A value of zero indicates that a full NOUT x NIN matrix +c of values will be supplied via the "matrix" parameter +f of values will be supplied via the MATRIX argument +* (below). In this case, the elements should be given in row +* order (the elements of the first row, followed by the +* elements of the second row, etc.). +* +* A value of 1 indicates that only the diagonal elements of the +* matrix will be supplied, and that all others should be +c zero. In this case, the elements of "matrix" should contain +f zero. In this case, the elements of MATRIX should contain +* only the diagonal elements, stored consecutively. +* +* A value of 2 indicates that a "unit" matrix is required, +* whose diagonal elements are set to unity (with all other +c elements zero). In this case, the "matrix" parameter is +c ignored and a NULL pointer may be supplied. +f elements zero). In this case, the MATRIX argument is not used. +c matrix +f MATRIX( * ) = DOUBLE PRECISION (Given) +* The array of matrix elements to be used, stored according to +c the value of "form". +f the value of FORM. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new MatrixMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new MatrixMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMatrixMap() +f AST_MATRIXMAP = INTEGER +* A pointer to the new MatrixMap. + +* Notes: +* - In general, a MatrixMap's forward transformation will always +* be available (as indicated by its TranForward attribute), but +* its inverse transformation (TranInverse attribute) will only be +* available if the associated matrix is square and non-singular. +* - As an exception to this, the inverse transformation is always +* available if a unit or diagonal matrix is specified. In this +* case, if the matrix is not square, one or more of the input +* coordinate values may not be recoverable from a set of output +* coordinates. Any coordinates affected in this way will simply be +* set to the value zero. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMatrixMap *new; /* Pointer to new MatrixMap */ + va_list args; /* Variable argument list */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise the MatrixMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitMatrixMap( NULL, sizeof( AstMatrixMap ), !class_init, + &class_vtab, "MatrixMap", nin, nout, form, matrix); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new MatrixMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new MatrixMap. */ + return new; +} + +AstMatrixMap *astMatrixMapId_( int nin, int nout, int form, const double matrix[], + const char *options, ... ) { +/* +* Name: +* astMatrixMapId_ + +* Purpose: +* Create a MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *astMatrixMapId_( int nin, int nout, int form, +* const double matrix[], const char *options, +* ... ) + +* Class Membership: +* MatrixMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astMatrixMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astMatrixMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astMatrixMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astMatrixMap_. + +* Returned Value: +* The ID value associated with the new MatrixMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMatrixMap *new; /* Pointer to new MatrixMap */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the MatrixMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitMatrixMap( NULL, sizeof( AstMatrixMap ), !class_init, &class_vtab, + "MatrixMap", nin, nout, form, matrix ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new MatrixMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new MatrixMap. */ + return astMakeId( new ); +} + +AstMatrixMap *astInitMatrixMap_( void *mem, size_t size, int init, + AstMatrixMapVtab *vtab, const char *name, + int nin, int nout, int form, + const double *matrix, int *status ) { +/* +*+ +* Name: +* astInitMatrixMap + +* Purpose: +* Initialise a MatrixMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *astInitMatrixMap( void *mem, size_t size, int init, +* AstMatrixMapVtab *vtab, const char *name, +* int nin, int nout, int form, +* const double *matrix ) + +* Class Membership: +* MatrixMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new MatrixMap object. It allocates memory (if necessary) to accommodate +* the MatrixMap plus any additional data associated with the derived class. +* It then initialises a MatrixMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a MatrixMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the MatrixMap is to be initialised. +* This must be of sufficient size to accommodate the MatrixMap data +* (sizeof(MatrixMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the MatrixMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the MatrixMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the MatrixMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new MatrixMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* nin +* The number of input coordinate values per point. This is the +* same as the number of columns in the matrix. +* nout +* The number of output coordinate values per point. This is the +* same as the number of rows in the matrix. +* form +* If "form" is 2 or larger, then a unit MatrixMap is created. In this +* case "matrix" is ignored and can be supplied as NULL. If "form" is +* 1, then a diagonal MatrixMap is created. In this case, the number of +* values in "matrix" should be equal to the minimum of nin and nout, +* and "matrix" should contain the corresponding diagonal terms, in row +* order. If "form" is 0 or less, then a full MatrixMap is created, and +* "matrix" should contain all nin*nout element values. +* matrix +* A pointer to an array of matrix element values. The values should be +* supplied in row order. The content of this array is determined by +* "form". If a full MatrixMap is to be created then the array starts +* with (row 1, column 1), then comes (row 1, column 2), up to (row 1, +* column nin), then (row 2, column 1), (row 2, column 2), and so on, +* finishing with (row nout, column nin) ). An error is reported if a +* NULL value is supplied unless "form" is 2 or more. + +* Returned Value: +* A pointer to the new MatrixMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstMatrixMap *new; /* Pointer to new MatrixMap */ + double *fmat; /* Pointer to the forward matrix */ + double *imat; /* Pointer to the inverse matrix */ + int i; /* Loop count */ + int nel; /* No. of elements in matrix array */ + int nuse; /* Number of usable matrix elements */ + int used_form; /* Form limited to 0, 1 or 2 */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitMatrixMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Report an error if a NULL matrix was supplied, unless a unit MatrixMap + has been requested. */ + if( form < 2 && !matrix ){ + astError( AST__MTRMT, "astInitMatrixMap(%s): NULL matrix supplied.", status, + name ); + + } else { + +/* Initialise a Mapping structure (the parent class) as the first component + within the MatrixMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstMatrixMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nout, 1, 1 ); + + if ( astOK ) { + +/* Initialise the MatrixMap data. */ +/* ---------------------------- */ +/* If a unit MatrixMap is being created, then no additional storage is + required. */ + if( form > 1 ){ + nel = 0; + used_form = UNIT; + +/* If a diagonal MatrixMap is being created, then memory is needed to hold + the diagonal terms. */ + } else if( form == 1 ){ + if( nin < nout ){ + nel = nin; + } else { + nel = nout; + } + used_form = DIAGONAL; + +/* If a full MatrixMap is being created, then memory is needed to hold + all the terms. */ + } else { + nel = nin*nout ; + used_form = FULL; + } + +/* Allocate memory for the forward matrix, storing the supplied matrix + values in it. */ + fmat = (double *) astStore( NULL, (void *) matrix, + sizeof(double)*(size_t)nel ); + +/* Replace any NaNs by AST__BAD and count the number of usable values. */ + if( nel > 0 ) { + nuse = 0; + for( i = 0; i < nel; i++ ) { + if( !astISFINITE(fmat[ i ]) ) { + fmat[ i ] = AST__BAD; + } else if( fmat[ i ] != AST__BAD ) { + nuse++; + } + } + +/* Report an error if there are no usable values. */ + if( nuse == 0 && astOK ) { + astError( AST__MTRMT, "astInitMatrixMap(%s): Supplied matrix " + "contains only bad values.", status, name ); + } + } + +/* Create an inverse matrix if possible. */ + imat = InvertMatrix( used_form, nout, nin, fmat, status ); + +/* Store the matrix arrays. */ + new->form = used_form; + new->f_matrix = fmat; + new->i_matrix = imat; + +/* Attempt to compress the MatrixMap into DIAGONAL or UNIT form. */ + CompressMatrix( new, status ); + +/* If an error occurred, clean up by deleting the new MatrixMap. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new MatrixMap. */ + return new; +} + +AstMatrixMap *astLoadMatrixMap_( void *mem, size_t size, + AstMatrixMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadMatrixMap + +* Purpose: +* Load a MatrixMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "matrixmap.h" +* AstMatrixMap *astLoadMatrixMap( void *mem, size_t size, +* AstMatrixMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* MatrixMap loader. + +* Description: +* This function is provided to load a new MatrixMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* MatrixMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a MatrixMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the MatrixMap is to be +* loaded. This must be of sufficient size to accommodate the +* MatrixMap data (sizeof(MatrixMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the MatrixMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the MatrixMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstMatrixMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new MatrixMap. If this is NULL, a pointer +* to the (static) virtual function table for the MatrixMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "MatrixMap" is used instead. + +* Returned Value: +* A pointer to the new MatrixMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +/* Local Variables: */ + AstMatrixMap *new; /* Pointer to the new MatrixMap */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *form; /* String form */ + int def; /* Is the matrix defined? */ + int el; /* Element index */ + int nel; /* No. of elements in the matrix */ + int nin; /* No. of input coords */ + int nout; /* No. of output coords */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this MatrixMap. In this case the + MatrixMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstMatrixMap ); + vtab = &class_vtab; + name = "MatrixMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitMatrixMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built MatrixMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "MatrixMap" ); + +/* Now obtain the Matrix storage form from this list. */ + form = astReadString( channel, "form", Form[FULL] ); + new->form = FindString( 3, Form, form, "the MatrixMap component 'Form'", + "astRead", astGetClass( channel ), status ); + form = astFree( (void *) form ); + +/* Find the number of elements stored for each matrix. */ + nin = astGetNin( (AstMapping *) new ); + nout = astGetNout( (AstMapping *) new ); + + if( new->form == FULL ){ + nel = nin*nout; + + } else if( new->form == DIAGONAL ){ + nel = astMIN( nin, nout ); + + } else { + nel = 0; + } + +/* Allocate memory to hold the forward matrix. */ + new->f_matrix = (double *) astMalloc( sizeof(double)*(size_t)nel ); + +/* Now read the other data items from the list and use them to + initialise the appropriate instance variable(s) for this class. */ + +/* The forward matrix. */ + if( new->f_matrix ){ + def = 0; + + for( el = 0; el < nel; el++ ){ + (void) sprintf( buff, "m%d", el ); + (new->f_matrix)[ el ] = astReadDouble( channel, buff, AST__BAD ); + if( (new->f_matrix)[ el ] != AST__BAD ) def = 1; + } + +/* Store a NULL pointer if no elements of the matrix were found. */ + if( !def ) new->f_matrix = (double *) astFree( (void *) new->f_matrix ); + + } + +/* Create an inverse matrix if possible, otherwise store a NULL pointer. */ + if( new->f_matrix ){ + new->i_matrix = InvertMatrix( new->form, nout, nin, new->f_matrix, status ); + } else { + new->i_matrix = NULL; + } + +/* If an error occurred, clean up by deleting the new MatrixMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new MatrixMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +AstMatrixMap *astMtrRot_( AstMatrixMap *this, double theta, + const double axis[], int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,MatrixMap,MtrRot))( this, theta, axis, status ); +} + +AstMatrixMap *astMtrMult_( AstMatrixMap *this, AstMatrixMap *a, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,MatrixMap,MtrMult))( this, a, status ); +} + + + + diff --git a/matrixmap.h b/matrixmap.h new file mode 100644 index 0000000..b24565a --- /dev/null +++ b/matrixmap.h @@ -0,0 +1,318 @@ +#if !defined( MATRIXMAP_INCLUDED ) /* Include this file only once */ +#define MATRIXMAP_INCLUDED +/* +*+ +* Name: +* matrixmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the MatrixMap class. + +* Invocation: +* #include "matrixmap.h" + +* Description: +* This include file defines the interface to the MatrixMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The MatrixMap class implements Mappings that transform a set of +* coordinates by multiplying them by a matrix. The inverse transformation +* can only be applied if the associated matrix is square and non-singular. + +* Inheritance: +* The MatrixMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* TranForward (integer) +* A read-only boolean value (0 or 1) which indicates whether a +* MatrixMap is able to transform coordinates in the "forward" +* direction (i.e. converting input coordinates into output +* coordinates). +* TranInverse (integer) +* A read-only boolean value (0 or 1) which indicates whether a +* MatrixMap is able to transform coordinates in the "inverse" +* direction (i.e. converting output coordinates back into input +* coordinates). + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astTransform +* Apply a MatrixMap to transform a set of points. +* astGetTranForward +* Determine if a MatrixMap can perform a "forward" coordinate +* transformation. +* astGetTranInverse +* Determine if a MatrixMap can perform an "inverse" coordinate +* transformation. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astMtrMult +* Multiply a MatrixMap by another MatrixMap. +* astMtrRot +* Rotate a MatrixMap. + +* Other Class Functions: +* Public: +* astIsAMatrixMap +* Test class membership. +* astMatrixMap +* Create a MatrixMap. +* +* Protected: +* astCheckMatrixMap +* Validate class membership. +* astInitMatrixMap +* Initialise a MatrixMap. +* astInitMatrixMapVtab +* Initialise the virtual function table for the MatrixMap class. +* astLoadMatrixMap +* Load a MatrixMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstMatrixMap +* MatrixMap object type. +* +* Protected: +* AstMatrixMapVtab +* MatrixMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 12-Feb-1996 (DSB): +* Original version. +* 22-Feb-1996 (DSB): +* Method "astMatrixRotate" added. +* 5-Mar-1996 (DSB): +* Method "astMatrixMult" added. +* 14-NOV-1996 (DSB): +* External interface and I/O added. Public method names shortened. +* 3-JUN-1997 (DSB): +* astMtrMult and astMtrRot made protected instead of public. +* 8-JAN-2003 (DSB): +* Added protected astInitMatrixMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* MatrixMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstMatrixMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *f_matrix; /* Pointer to forward matrix */ + double *i_matrix; /* Pointer to inverse matrix */ + int form; /* Matrix storage form */ + +} AstMatrixMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstMatrixMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstMatrixMap *(* MtrRot)( AstMatrixMap *, double, const double[], int * ); + AstMatrixMap *(* MtrMult)( AstMatrixMap *, AstMatrixMap *, int * ); + +} AstMatrixMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstMatrixMapGlobals { + AstMatrixMapVtab Class_Vtab; + int Class_Init; +} AstMatrixMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitMatrixMapGlobals_( AstMatrixMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(MatrixMap) /* Check class membership */ +astPROTO_ISA(MatrixMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstMatrixMap *astMatrixMap_( int, int, int, const double[], const char *, int *, ...); +#else +AstMatrixMap *astMatrixMapId_( int, int, int, const double[], const char *, ... )__attribute__((format(printf,5,6))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstMatrixMap *astInitMatrixMap_( void *, size_t, int, AstMatrixMapVtab *, + const char *, int, int, int, const double[], int * ); + +/* Vtab initialiser. */ +void astInitMatrixMapVtab_( AstMatrixMapVtab *, const char *, int * ); + +/* Loader. */ +AstMatrixMap *astLoadMatrixMap_( void *, size_t, AstMatrixMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +AstMatrixMap *astMtrRot_( AstMatrixMap *, double, const double[], int * ); +AstMatrixMap *astMtrMult_( AstMatrixMap *, AstMatrixMap *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckMatrixMap(this) astINVOKE_CHECK(MatrixMap,this,0) +#define astVerifyMatrixMap(this) astINVOKE_CHECK(MatrixMap,this,1) + +/* Test class membership. */ +#define astIsAMatrixMap(this) astINVOKE_ISA(MatrixMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astMatrixMap astINVOKE(F,astMatrixMap_) +#else +#define astMatrixMap astINVOKE(F,astMatrixMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitMatrixMap(mem,size,init,vtab,name,nin,nout,form,matrix) \ +astINVOKE(O,astInitMatrixMap_(mem,size,init,vtab,name,nin,nout,form,matrix,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitMatrixMapVtab(vtab,name) astINVOKE(V,astInitMatrixMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadMatrixMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadMatrixMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckMatrixMap to validate MatrixMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astMtrRot(this,theta,axis) \ +astINVOKE(O,astMtrRot_(astCheckMatrixMap(this),theta,axis,STATUS_PTR)) + +#define astMtrMult(this,a) \ +astINVOKE(O,astMtrMult_(astCheckMatrixMap(this),astCheckMatrixMap(a),STATUS_PTR)) +#endif +#endif + + + + + diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..0bc3d93 --- /dev/null +++ b/memory.c @@ -0,0 +1,5483 @@ +/* +* Name: +* memory.c + +* Purpose: +* Implement memory allocation/deallocation functions. + +* Description: +* This file implements the Memory module which is used for +* allocating and freeing memory in the AST library. For a +* description of the module and its interface, see the .h file of +* the same name. + +* Note, it is assumed that malloc, free and realloc are thread-safe. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009-2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: D.S. Berry (Starlink) + +* History: +* 2-JAN-1996 (RFWS): +* Original version. +* 26-JAN-1996 (RFWS): +* Removed trailing underscores from static functions and +* changed to use new error function interfaces. +* 20-JUN-1996 (RFWS): +* Added astString. +* 15-JUL-1996 (RFWS): +* Make IsDynamic execute under error conditions to avoid memory +* leaks in such situations. +* 11-SEP-1996 (RFWS): +* Added astStringArray (original written by DSB). +* 18-MAR-1998 (RFWS): +* Added notes about these functions being available for writing +* foreign language and graphics interfaces, etc. +* 29-JAN-2002 (DSB): +* Added astChrLen and astSscanf. +* 15-FEB-2002 (DSB): +* Removed use of non-ANSI vsscanf from astSscanf. +* 15-NOV-2002 (DSB): +* Moved ChrMatch from SkyFrame (etc) to here. Included stdio.h and +* ctype.h. +* 10-FEB-2003 (DSB): +* Added facilities for detecting and tracing memory leaks. These +* are only included if AST is compiled with the -DDEBUG flag. +* 3-MAR-2004 (DSB): +* Modified astSscanf to avoid use of uninitialised values +* corresponding to "%n" fields in the format string. +* 26-JAN-2004 (DSB): +* Modified astRealloc to clarify the nature of the returned pointer +* (which is not a "Memory *"). Also correct issuing and deissuing +* of pointers in DEBUG code within astRealloc. +* 16-FEB-2006 (DSB): +* Convert Magic from a function to a macro for extra speed. +* 21-FEB-2006 (DSB): +* Convert IsDynamic from a function to a macro for extra speed. +* 23-FEB-2006 (DSB): +* Added the caching system for allocated but unused memory blocks, +* controlled by AST tuning parameter MemoryCaching. +* 2-MAR-2006 (DSB): +* Added astFlushMemory, and renamed the memory debugging functions. +* These are now conditionally compiled if the MEM_DEBUG macros is +* defined (set by configuring AST with the --with-memdebug option). +* Also modified them to take into account MemoryCaching. +* 24-MAY-2006 (DSB): +* Ensure that pointers to memory returned by this module are all +* aligned on 8 byte boundaries. This fixes problems with ualigned +* memory access that could cause bus errors on Solaris. +* 26-MAY-2006 (DSB): +* Cast (void *) pointers to (char *) before doing arithmetic on +* them (changes supplied by Micah Johnson). +* 4-DEC-2006 (DSB): +* Fix bug in astMalloc that caused a non-null pointer to be +* returned on error. +* 4-JAN-2007 (DSB): +* Move definition of astCLASS macro so that it comes before the +* inclusion of the AST include files (which test for astCLASS). +* 27-JUN-2007 (DSB): +* Added astIsDynamic. +* 24-OCT-2007 (DSB): +* Zero the size of memory blocks stored in the Cache so that an +* error will be reported if an attempt is made to free a memory +* block that has already been freed. +* 25-OCT-2007 (DSB): +* Added astRemoveLeadingBlanks. +* 28-FEB-2008 (DSB): +* Added astChrSub. +* 17-MAR-2008 (DSB): +* Added "{nnn}" quantifier to astChrSub. +* 27-MAR-2008 (DSB): +* Added astChrSplitRE, and re-structured regexp functions. +* 18-NOV-2008 (DSB): +* In astFlushMemory, do not release permanent memory blocks as +* they may still be needed. +* 9-FEB-2009 (DSB): +* Added astChr2Double. +* 25-JUN-2009 (DSB): +* Fix handling of escape characters in astSplitC. +* 19-MAY-2010 (DSB): +* - Added astStringCase. +* - Changed access from protected to public for commonly used +* functions. +* 26-MAY-2010 (DSB): +* Added astCalloc. +* 18-AUG-2010 (DSB): +* Added astMemoryStats +* 19-AUG-2010 (DSB): +* Added astMemoryWarning +* 8-OCT-2010 (DSB): +* Modify memory allocation to use "calloc" directly, rather than +* using "malloc+memset". +* 12-APR-2011 (DSB): +* Fix regular expression problem where a ".*" template field failed to +* match a null string if it occurred before a closing parenthesis at +* the end of the template. +* 26-MAY-2011 (DSB): +* - Changed API for astCalloc to match RTL (i.e. remove "init"). +* - Changed astChr2Double to check for strigs like "2.", which +* some sscanfs fail to read as a floating point value. +* 27-MAY-2011 (DSB): +* Added astFreeDouble to free a dynamically allocated array of +* pointers to other dynamically allocated arrays. +* 21-JUN-2011 (DSB): +* Added astCheckMemory - an alternative to astFlushMemory that does +* not free any memory. +* 21-NOV-2011 (DSB): +* Correct matchend value returned by astChrSplitRE. +* 6-JAN-2014 (DSB): +* Optimise access to cache to avoid valgrind warnings. +* 16-JAN-2014 (DSB): +* Dump details of all active memory blocks if the total memory allocation +* specified by astMemoryWarning is exceeded. +* 23-SEP-2014 (DSB): +* Modify astAppendString to allow printf conversion specifications to +* be included in the appended string. +* 20-OCT-2014 (DSB): +* Revert the change to astAppendString made on 23-SEP-2014 as it is +* insecure. Instead add new function astAppendStringf. +* 26-MAR-2015 (DSB): +* Added astChrTrunc. +*/ + +/* Configuration results. */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* Module Macros. */ +/* ============== */ +/* Define the astCLASS macro (even although this is not a class + implementation) to obtain access to the protected error handling + functions. */ +#define astCLASS memory + +/* The maximum number of fields within a format string allowed by astSscanf. */ +#define VMAXFLD 20 + +/* The maximum number of nested astBeginPM/astEndPM contexts. */ +#define PM_STACK_MAXSIZE 20 + +/* Select the appropriate memory management functions. These will be the + system's malloc, calloc, free and realloc unless AST was configured with + the "--with-starmem" option, in which case they will be the starmem + malloc, calloc, free and realloc. */ +#ifdef HAVE_STAR_MEM_H +# include +# define MALLOC starMalloc +# define CALLOC starCalloc +# define FREE starFree +# define REALLOC starRealloc +#else +# define MALLOC malloc +# define CALLOC calloc +# define FREE free +# define REALLOC realloc +#endif + + +#ifdef MEM_DEBUG +#define ISSUED "issued" +#define FREED "freed" +#endif + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" /* Error reporting facilities */ +#include "globals.h" /* Thread-specific global data */ +#include "memory.h" /* Interface to this module */ +#include "pointset.h" /* For AST__BAD */ + +#ifdef MEM_DEBUG +#include "object.h" /* For astMakePointer */ +#endif + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef THREAD_SAFE +#include +#endif + +#ifdef MEM_PROFILE +#include +#endif + +/* Function Macros. */ +/* =============== */ +/* These are defined as macros rather than functions to avoid the + overhead of a function call since they are called extremely frequently. */ + +/* +* Name: +* IS_DYNAMIC + +* Purpose: +* Test whether a memory region has been dynamically allocated. + +* Type: +* Private macro + +* Synopsis: +* #include "memory.h" +* IS_DYNAMIC( ptr, dynamic ) + +* Description: +* This macro takes a pointer to a region of memory and tests if +* the memory has previously been dynamically allocated using other +* functions from this module. It does this by checking for the +* presence of a "magic" number in the header which precedes the +* allocated memory. If the magic number is not present (or the +* pointer is invalid for any other reason), an error is reported +* and the global error status is set. +* +* The result of the test is written to the variable specified by "res". + +* Parameters: +* ptr +* Pointer to the start (as known to the external user) of the +* dynamically allocated memory. +* dynamic +* Name of an "int" variable to recieve the result of the test. +* If the memory was allocated dynamically, a value of 1 is +* stored in this variable. Otherwise, zero is stored and an error +* results. + +* Notes: +* - A NULL pointer value produces an error report from this +* function, although other functions may wish to regard a NULL +* pointer as valid. +* - This function attempts to execute even if the global error +* status is set, although no further error report will be made if +* the memory is not dynamically allocated under these +* circumstances. +* - The test performed by this function is not 100% secure as the +* "magic" value could occur by accident (although this is +* unlikely). It is mainly intended to provide security against +* programming errors, including accidental corruption of the +* memory header and attempts to allocate the same region of memory +* more than once. +*/ + +#define IS_DYNAMIC(ptr,dynamic) \ +\ +/* Initialise. */ \ + dynamic = 0; \ +\ +/* Check that a NULL pointer has not been supplied and report an error \ + if it has (but not if the global status is already set). */ \ + if ( !ptr ) { \ + if ( astOK ) { \ + astError( AST__PTRIN, "Invalid NULL pointer (address %p).", status, ptr ); \ + } \ +\ +/* If OK, derive a pointer to the memory header that precedes the \ + allocated region of memory. */ \ + } else { \ + Memory *isdynmem; /* Pointer to memory header */ \ + isdynmem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY ); \ +\ +/* Check if the "magic number" in the header is valid and report an \ + error if it is not (but not if the global status is already \ + set). */ \ + if ( isdynmem->magic != MAGIC( isdynmem, isdynmem->size ) ) { \ + if ( astOK ) { \ + astError( AST__PTRIN, \ + "Invalid pointer or corrupted memory at address %p.", status, \ + ptr ); \ + } \ +\ +/* Note if the magic number is OK. */ \ + } else { \ + dynamic = 1; \ + } \ + } + + + +/* +* Name: +* MAGIC + +* Purpose: +* Generate a "magic number". + +* Type: +* Private macro. + +* Synopsis: +* #include "memory.h" +* unsigned long MAGIC( void *ptr, size_t size ) + +* Description: +* This macro generates a "magic number" which is a function of +* a memory address and an object size. This number may be stored +* in a region of dynamically allocated memory to allow it to be +* recognised as dynamically allocated by other routines, and also +* to provide security against memory leaks, etc. + +* Parameters: +* ptr +* The memory pointer. +* size +* The object size. + +* Returned Value: +* The function returns the magic number. + +* Notes: +* This function does not perform error checking. +*/ + +/* Form the bit-wise exclusive OR between the memory address and the + object size, then add 1 and invert the bits. Return the result as + an unsigned long integer. */ +#define MAGIC(ptr,size) \ + ( ~( ( ( (unsigned long) ptr ) ^ ( (unsigned long) size ) ) + \ + ( (unsigned long) 1 ) ) ) + +/* A macro that returns the size of the a Memory structure padded to a + multiple of 8 bytes. */ +#define SIZEOF_MEMORY \ + ( ( sizeof_memory != 0 ) ? sizeof_memory : SizeOfMemory( status ) ) + + +/* Type Definitions. */ +/* ================= */ + +#ifdef MEM_PROFILE + +/* Structure used to record the time spent between matching calls to + astStartTimer and astStopTimer. */ +typedef struct AstTimer { + int id; /* Unique integer identifier for timer */ + clock_t e0; /* Absolute elapsed time at timer start */ + clock_t u0; /* Absolute user time at timer start */ + clock_t s0; /* Absolute system time at timer start */ + clock_t et; /* Cumulative elapsed time within timer */ + clock_t ut; /* Cumulative user time within timer */ + clock_t st; /* Cumulative system time within timer */ + int nentry; /* Number of entries into the timer */ + const char *name; /* An identifying label for the timer */ + const char *file; /* Name of source file where timer was started */ + int line; /* Source file line no. where timer was started */ + struct AstTimer *parent; /* The parent enclosing timer */ + int nchild; /* Number of child timers */ + struct AstTimer **children;/* Timers that count time within this timer */ +} AstTimer; + +#endif + +/* Module Variables. */ +/* ================= */ + +/* Extra stuff for profiling (can only be used in single threaded + environments). */ +#ifdef MEM_PROFILE +static AstTimer *Current_Timer = NULL; +static int Enable_Timers = 0; +static int Timer_Count = 0; +#endif + +/* Extra stuff for debugging of memory management (tracking of leaks + etc). */ +#ifdef MEM_DEBUG + +/* The identifier for the memory block which is to be tracked. */ +static int Watched_ID = -1; + +/* The next integer to use to identify an active memory block pointer. */ +static int Next_ID = -1; + +/* Indicates if future memory allocations are permanent (i.e. will not + usually be freed explicitly by AST). */ +static int Perm_Mem = 0; + +/* A "first in, last out" stack of Perm_Mem values used by nested + astBeginPM/astEndPM contexts. */ +static int PM_Stack[ PM_STACK_MAXSIZE ]; + +/* The number of values currently in the PM_Stack array. */ +static int PM_Stack_Size = 0; + +/* A pointer to a double linked list holding pointers to currently active + memory blocks (i.e. memory blocks for which a pointer has been issued + but not yet freed). This does not include the memory blocks in the + Cache array (these are not considered to be active). */ +static Memory *Active_List = NULL; + +/* Should a new ID be issued each time a cached memory block is returned + by astMalloc? Otherwise, the same ID value is used throughout the + life of a memory block. */ +static int Keep_ID = 0; + +/* Suppress all memory use reports except for issuing and freeing? */ +static int Quiet_Use = 0; + +/* Report the ID of every cached block when the cache is emptied? */ +static int List_Cache = 0; + +/* Memory allocation at which to issue a warning. */ +static size_t Warn_Usage = 0; + +/* Current memory allocated by AST. */ +static size_t Current_Usage = 0; + +/* Peak memory allocated by AST. */ +static size_t Peak_Usage = 0; + +#ifdef THREAD_SAFE +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_DEBUG_MUTEX pthread_mutex_lock( &mutex2 ); +#define UNLOCK_DEBUG_MUTEX pthread_mutex_unlock( &mutex2 ); +#else +#define LOCK_DEBUG_MUTEX +#define UNLOCK_DEBUG_MUTEX +#endif + +#endif + +/* Define macros for accessing all items of thread-safe global data + used by this module. */ +#ifdef THREAD_SAFE + +#define sizeof_memory astGLOBAL(Memory,Sizeof_Memory) +#define cache astGLOBAL(Memory,Cache) +#define cache_init astGLOBAL(Memory,Cache_Init) +#define use_cache astGLOBAL(Memory,Use_Cache) + +/* Define the initial values for the global data for this module. */ +#define GLOBAL_inits \ + globals->Sizeof_Memory = 0; \ + globals->Cache_Init = 0; \ + globals->Use_Cache = 0; \ + +/* Create the global initialisation function. */ +astMAKE_INITGLOBALS(Memory) + +/* If thread safety is not needed, declare globals at static variables. */ +/* -------------------------------------------------------------------- */ +#else + +/* The size of a Memory header structure, padded to a multiple of 8 + bytes. This value is initialised by the SizeOfMemory function, and + should be accessed using the SIZEOF_MEMORY macro. */ +static size_t sizeof_memory = 0; + +/* A cache of allocated but currently unused memory block. This cache is + maintained in order to avoid the overhead of continual calls to malloc to + allocate small blocks of memory. The vast majority of memory blocks + allocated by AST are under 200 bytes in size. Each element in this array + stores a pointer to the header for a free (i.e. allocated but currently + unused) memory block. The size of the memory block (not including the + Memory header) will equal the index at which the pointer is stored within + "cache". Each free memory block contains (in its Memory header) a pointer + to the header for another free memory block of the same size (or a NULL + pointer if there are no other free memory blocks of the same size). */ +static Memory *cache[ MXCSIZE + 1 ]; + +/* Has the "cache" array been initialised? */ +static int cache_init = 0; + +/* Should the cache be used? */ +static int use_cache = 0; + +#endif + +/* Prototypes for Private Functions. */ +/* ================================= */ +static size_t SizeOfMemory( int * ); +static char *CheckTempStart( const char *, const char *, const char *, char *, int *, int *, int *, int *, int *, int *, int *, int * ); +static char *ChrMatcher( const char *, const char *, const char *, const char *, const char *[], int, int, int, char ***, int *, const char **, int * ); +static char *ChrSuber( const char *, const char *, const char *[], int, int, char ***, int *, const char **, int * ); + +#ifdef MEM_DEBUG +static void Issue( Memory *, int * ); +static void DeIssue( Memory *, int * ); +#endif + +#ifdef MEM_PROFILE +static AstTimer *ReportTimer( AstTimer *, int, AstTimer **, int *, int * ); +static int CompareTimers( const void *, const void * ); +static int CompareTimers2( const void *, const void * ); +#endif + +/* Function implementations. */ +/* ========================= */ +char *astAppendString_( char *str1, int *nc, const char *str2, int *status ) { +/* +*++ +* Name: +* astAppendString + +* Purpose: +* Append a string to another string which grows dynamically. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char *astAppendString( char *str1, int *nc, const char *str2 ) + +* Description: +* This function appends one string to another dynamically +* allocated string, extending the dynamic string as necessary to +* accommodate the new characters (plus the final null). + +* Parameters: +* str1 +* Pointer to the null-terminated dynamic string, whose memory +* has been allocated using an AST memory allocation function. +* If no space has yet been allocated for this string, a NULL +* pointer may be given and fresh space will be allocated by this +* function. +* nc +* Pointer to an integer containing the number of characters in +* the dynamic string (excluding the final null). This is used +* to save repeated searching of this string to determine its +* length and it defines the point where the new string will be +* appended. Its value is updated by this function to include +* the extra characters appended. +* +* If "str1" is NULL, the initial value supplied for "*nc" will +* be ignored and zero will be used. +* str2 +* Pointer to a constant null-terminated string, a copy of which +* is to be appended to "str1". + +* Returned Value: +* astAppendString() +* A possibly new pointer to the dynamic string with the new string +* appended (its location in memory may have to change if it has to +* be extended, in which case the original memory is automatically +* freed by this function). When the string is no longer required, +* its memory should be freed using astFree. + +* Notes: +* - If this function is invoked with the global error status set +* or if it should fail for any reason, then the returned pointer +* will be equal to "str1" and the dynamic string contents will be +* unchanged. +*-- +*/ + + +/* Local Variables: */ + char *result; /* Pointer value to return */ + int len; /* Length of new string */ + +/* Initialise. */ + result = str1; + +/* If the first string pointer is NULL, also initialise the character + count to zero. */ + if ( !str1 ) *nc = 0; + +/* Check the global error status. */ + if ( !astOK || !str2 ) return result; + +/* Calculate the total string length once the two strings have been + concatenated. */ + len = *nc + (int) strlen( str2 ); + +/* Extend the first (dynamic) string to the required length, including + a final null. Save the resulting pointer, which will be + returned. */ + result = astGrow( str1, len + 1, sizeof( char ) ); + +/* If OK, append the second string and update the total character + count. */ + if ( astOK ) { + (void) strcpy( result + *nc, str2 ); + *nc = len; + } + +/* Return the result pointer. */ + return result; +} + +char *astAppendStringf_( char *str1, int *nc, const char *str2, ... ) { +/* +*++ +* Name: +* astAppendStringf + +* Purpose: +* Append a string to another string, allowing printf format specifiers. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char *astAppendStringf( char *str1, int *nc, const char *str2, ... ) + +* Description: +* This function appends one string to another dynamically +* allocated string, extending the dynamic string as necessary to +* accommodate the new characters (plus the final null). It is the +* same as astAppendString, except that the "str2" string ay include +* printf format specifiers. + +* Parameters: +* str1 +* Pointer to the null-terminated dynamic string, whose memory +* has been allocated using an AST memory allocation function. +* If no space has yet been allocated for this string, a NULL +* pointer may be given and fresh space will be allocated by this +* function. +* nc +* Pointer to an integer containing the number of characters in +* the dynamic string (excluding the final null). This is used +* to save repeated searching of this string to determine its +* length and it defines the point where the new string will be +* appended. Its value is updated by this function to include +* the extra characters appended. +* +* If "str1" is NULL, the initial value supplied for "*nc" will +* be ignored and zero will be used. +* str2 +* Pointer to a constant null-terminated string, a copy of which +* is to be appended to "str1". It may contain format +* specifications such as used with the C "printf" family of +* functions. +* ... +* Additional optional arguments (as used by e.g. "printf") +* which specify values which are to be substituted into the "str2" +* string in place of any format specifications. + +* Returned Value: +* astAppendString() +* A possibly new pointer to the dynamic string with the new string +* appended (its location in memory may have to change if it has to +* be extended, in which case the original memory is automatically +* freed by this function). When the string is no longer required, +* its memory should be freed using astFree. + +* Notes: +* - If this function is invoked with the global error status set +* or if it should fail for any reason, then the returned pointer +* will be equal to "str1" and the dynamic string contents will be +* unchanged. +*-- +*/ + +/* Local Variables: */ + char *pbuf; /* Pointer to buffer for expanded "str2" */ + char *result; /* Pointer value to return */ + char buf[1000]; /* A large buffer for the expanded "str2" */ + int *status; /* Pointer to inherited status variable */ + int buf_size; /* Size of buffer for expanded "str2" */ + int len; /* Length of new string */ + int nexp; /* Number of characters written to "pbuf". */ + va_list args; /* Variable argument list pointer */ + +/* Initialise. */ + result = str1; + +/* If the first string pointer is NULL, also initialise the character + count to zero. */ + if( !str1 ) *nc = 0; + +/* Get a pointer to the integer holding the inherited status value. This + function cannot have a "status" argument like most other functions + because of the variable argument list. */ + status = astGetStatusPtr; + +/* Check the global error status. */ + if ( !astOK || !str2 ) return result; + +/* If available use vsnprintf to determine the amount of memory needed to + hold the expanded version of "str2". Then allocate the required memory. */ +#if HAVE_VSNPRINTF + va_start( args, str2 ); + buf_size = vsnprintf( buf, sizeof( buf ), str2, args ) + 1; + va_end( args ); + pbuf = astMalloc( buf_size ); + +/* Otherwise, all we can do is use a big buffer and hope for the best. */ +#else + buf_size = sizeof( buf ); + pbuf = buf; +#endif + +/* Expand any conversion specifications in "str2". */ + va_start( args, str2 ); + nexp = vsprintf( pbuf, str2, args ); + va_end( args ); + +/* Check that the result buffer did not overflow (should only be + possible if vsnprintf is not available). If it did, memory may + have been corrupted. Report the error and abort. */ + if( nexp >= buf_size ) { + if( astOK ) astError( AST__ATSER, "astAppendString: Internal buffer " + "overflow while appending a string - the " + "result exceeds %d characters.", status, + buf_size - 1 ); + } + +/* Calculate the total string length once the two strings have been + concatenated. */ + len = *nc + nexp; + +/* Extend the first (dynamic) string to the required length, including + a final null. Save the resulting pointer, which will be + returned. */ + result = astGrow( str1, len + 1, sizeof( char ) ); + +/* If OK, append the second string and update the total character + count. */ + if ( astOK ) { + (void) strcpy( result + *nc, pbuf ); + *nc = len; + } + +/* If required, free the buffer holding the expanded version of "str2". */ +#if HAVE_VSNPRINTF + pbuf = astFree( pbuf ); +#endif + +/* Return the result pointer. */ + return result; +} + +void *astCalloc_( size_t nmemb, size_t size, int *status ) { +/* +*++ +* Name: +* astCalloc + +* Purpose: +* Allocate and initialise memory. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void *astCalloc( size_t nmemb, size_t size ) + +* Description: +* This function allocates memory in a similar manner to the +* standard C "calloc" function, but with improved security +* (against memory leaks, etc.) and with error reporting. It also +* fills the allocated memory with zeros. +* +* Like astMalloc, it allows zero-sized memory allocation +* (without error), resulting in a NULL returned pointer value. + +* Parameters: +* nmemb +* The number of array elements for which memory is to be allocated. +* size +* The size of each array element, in bytes. + +* Returned Value: +* astCalloc() +* If successful, the function returns a pointer to the start of +* the allocated memory region. If the size allocated is zero, this +* will be a NULL pointer. + +* Notes: +* - A pointer value of NULL is returned if this function is +* invoked with the global error status set or if it fails for any +* reason. +*-- +*/ +/* Local Variables: */ + void *result; /* Returned pointer */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Attempt to allocate and initialise the required amount of memory. */ + result = astMallocInit( nmemb*size ); + +/* If the above call failed due to failure of the system malloc function, + issue an extra error giving the number of elements and element size. */ + if( astStatus == AST__NOMEM ) { + astError( AST__NOMEM, "(%lu elements, each of %lu bytes).", status, + (unsigned long) nmemb, (unsigned long) size ); + } + +/* Return the result. */ + return result; +} + +static char *CheckTempStart( const char *template, const char *temp, + const char *pattern, + char *allowed, int *ntemp, int *allow, + int *min_nc, int *max_nc, int *start_sub, + int *end_sub, int *greedy, int *status ){ +/* +* Name: +* CheckTempStart + +* Purpose: +* Examine the leading field in an astChrSub template. + +* Type: +* Private function. + +* Synopsis: +* char *CheckTempStart( const char *template, const char *temp, +* const char *pattern, +* char *allowed, int *ntemp, int *allow, +* int *min_nc, int *max_nc, int *start_sub, +* int *end_sub, int *greedy, int *status ) + +* Description: +* This function returns inforation about the leading field in a +* template string supplied to astChrSub. + +* Parameters: +* template +* The full template string (used for error messages). +* temp +* Pointer to the next character to read from the template string. +* pattern +* Pointer to the user supplied pattern string (only used in error +* messages). +* allowed +* Pointer to a buffer in which to store a string of characters +* that the leading temeplate field will match. A NULL pointer may +* be supplied in which case new memory will be allocated. The +* supplied memory is expanded as necessary, and a pointer to it is +* returned as the function value. +* ntemp +* Address of an int in which to return the number of characters +* consumed from the start of "temp". +* allow +* Address of an int in which to return a flag which is non-zero if +* the returned string contains characters that are allowed in the +* test field, or zero if the returned string contains characters that +* are disallowed in the test field. +* min_nc +* Address of an int in which to return the minimum number of +* test characters that must belong to the returned set of +* allowed characters. +* max_nc +* Address of an int in which to return the maximum number of +* test characters that must belong to the returned set of +* allowed characters. +* start_sub +* Address of an int in which to return a flag which is non-zero if +* the leading template field indicates the start of a field to be +* substituted. In this case the supplied "allowed" pointer is +* returned without change as the function value, "Min_nc" is +* returned as zero, and max_nc is returned as zero. +* end_sub +* Address of an int in which to return a flag which is non-zero if +* the leading template field indicates the end of a field to be +* substituted. In this case the supplied "allowed" pointer is +* returned without change as the function value, "Min_nc" is +* returned as zero, and limit is returned as zero. +* greedy +* Address of an int in which to return a flag which is non-zero if +* the template starts with a greedy quantifier. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a (possibly newly allocated) memory area holding a +* string of characters that the leading temeplate field will match. +* This string should be released using astFree when no longer needed. +* If a NULL pointyer is returned, then all characters are allowed +* (or disallowed if "*allow" is zero). + +* Notes: +* - The returned value is also stored in the module variable +* sizeof_memory. +*/ + +/* Local Variables: */ + char *result; + const char *start; + const char *end; + +/* Initialise. */ + result = allowed; + *ntemp = 0; + *allow = 1; + *min_nc = 0; + *max_nc = 0; + *start_sub = 0; + *end_sub = 0; + *greedy = 1; + +/* Check global status */ + if( !astOK ) return result; + +/* If the next character is an opening parenthesis, this marks the start + of a substitution field. */ + if( *temp == '(' ) { + *start_sub = 1; + *ntemp = 1; + +/* If the next character is an closing parenthesis, this marks the end + of a substitution field. */ + } else if( *temp == ')' ) { + *end_sub = 1; + *ntemp = 1; + +/* If the next character is an opening bracket, this marks the start of a + field of allowed or disallowed characters. */ + } else { + if( *temp == '[' ) { + +/* If the first character in the brackets is "^" this is a field of + disallowed characters, otherwise they are allowed. */ + if( temp[ 1 ] == '^' ) { + *allow = 0; + start = temp + 2; + } else { + start = temp + 1; + } + +/* Get a pointer to the closing bracket. */ + end = strchr( temp, ']' ); + +/* Copy the intervening string into the returned string. */ + if( end ) { + result = astStore( allowed, start, end - start + 1 ); + if( result ) result[ end - start ] = 0; + +/* Report an error if no closing bracket was found. */ + } else { + astError( AST__BADSUB, "Invalid pattern matching template \"%s\": " + "missing ']'.", status, pattern ); + } + +/* Indicate how many template characters have been used. */ + *ntemp = end - temp + 1; + +/* A single dot matches any character. */ + } else if( *temp == '.' ) { + result = astFree( result ); + *ntemp = 1; + +/* Now deal with escape sequences. */ + } else if( *temp == '\\' ) { + +/* Digits... */ + if( temp[ 1 ] == 'd' || temp[ 1 ] == 'D' ) { + result = astStore( allowed, "0123456789", 11 ); + result[ 10 ] = 0; + if( temp[ 1 ] == 'D' ) *allow = 0; + +/* White space... */ + } else if( temp[ 1 ] == 's' || temp[ 1 ] == 'S' ) { + result = astStore( allowed, " \n\r", 5 ); + result[ 4 ] = 0; + if( temp[ 1 ] == 'S' ) *allow = 0; + +/* Word characters... */ + } else if( temp[ 1 ] == 'w' || temp[ 1 ] == 'W' ) { + result = astStore( allowed, "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", + 64 ); + result[ 63 ] = 0; + if( temp[ 1 ] == 'W' ) *allow = 0; + +/* Any other character is treated literally. */ + } else { + result = astStore( allowed, temp + 1, 2 ); + result[ 1 ] = 0; + } + +/* Set number of template characters consumed. */ + *ntemp = 2; + +/* Everything else must be matched literally. */ + } else { + + if( *temp == '*' || *temp == '?' || *temp == '+' ){ + astError( AST__BADSUB, "Invalid pattern matching template \"%s\": " + "field starts with '%c'.", status, pattern, temp[ *ntemp ] ); + } else { + result = astStore( allowed, temp, 2 ); + result[ 1 ] = 0; + *ntemp = 1; + } + + } + +/* Now see if there is any quantifier. */ + if( temp[ *ntemp ] == '*' ) { + *min_nc = 0; + *max_nc = INT_MAX; + (*ntemp)++; + if( temp[ *ntemp ] == '?' ){ + *greedy = 0; + (*ntemp)++; + } + + } else if( temp[ *ntemp ] == '+' ) { + *min_nc = 1; + *max_nc = INT_MAX; + (*ntemp)++; + if( temp[ *ntemp ] == '?' ){ + *greedy = 0; + (*ntemp)++; + } + + } else if( temp[ *ntemp ] == '?' ) { + *min_nc = 0; + *max_nc = 1; + (*ntemp)++; + + } else { + +/* See if the remaining string starts with "{nnn}". If so, extract the + "nnn" and use it as the minimum and maximum field length. */ + if( temp[ *ntemp ] == '{' ) { + + start = temp + *ntemp + 1; + while( isdigit( (int) *start ) ) { + *min_nc = 10*( *min_nc ) + (int )( ( *start ) - '0' ); + start++; + } + + if( *start == '}' ) { + *max_nc = *min_nc; + *ntemp = start - temp + 1; + } else { + start = NULL; + } + + } else { + start = NULL; + } + +/* If the remaining string does not start with "{nnn}", use a minimum and + maximum field length of 1. */ + if( !start ) { + *min_nc = 1; + *max_nc = 1; + } + } + } + +/* Return the string of allowed characters. */ + return result; +} + +double astChr2Double_( const char *str, int *status ) { +/* +*++ +* Name: +* astChr2Double + +* Purpose: +* read a double value from a string. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* double astChr2Double( const char *str ) + +* Description: +* This function reads a double from the supplied null-terminated string, +* ignoring leading and trailing white space. AST__BAD is ereturned +* without error if the string is not a numerical value. + +* Parameters: +* str +* Pointer to the string. + +* Returned Value: +* astChr2Double() +* The double value, or AST__BAD. + +* Notes: +* - A value of AST__BAD is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*-- +*/ + +/* Local Variables: */ + double result; /* The returned value */ + int ival; /* Integer value read from string */ + int len; /* Length of supplied string */ + int nc; /* Number of characters read from the string */ + +/* Check the global error status and supplied pointer. */ + if ( !astOK || !str ) return AST__BAD; + +/* Save the length of the supplied string. */ + len = strlen( str ); + +/* Use scanf to read the floating point value. This fails if either 1) the + string does not begin with a numerical value (in which case astSscanf + returns zero), or 2) there are non-white characters following the + numerical value (in which case "nc" - the number of characters read from + the string - is less than the length of the string). */ + if ( nc = 0, + ( 1 != astSscanf( str, " %lg %n", &result, &nc ) ) || ( nc < len ) ) { + result = AST__BAD; + } + +/* If the above failed, try again allowing the string to be an integer + followed by a dot (e.g. "1."). Some implementations of sscanf do not + consider this to be a floating point value. */ + if( 1 || result == AST__BAD ) { + if ( nc = 0, + ( 1 == astSscanf( str, " %d. %n", &ival, &nc ) ) && ( nc >= len ) ) { + result = ival; + } + } + +/* Return the result. */ + return result; +} + +void astChrCase_( const char *in, char *out, int upper, int blen, int *status ) { +/* +*++ +* Name: +* astChrCase + +* Purpose: +* Convert a string to upper or lower case. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void astChrCase( const char *in, char *out, int upper, int blen, int *status ) + +* Description: +* This function converts a supplied string to upper or lower case, +* storing the result in a supplied buffer. The astStringCase function +* is similar, but stores the result in a dynamically allocated buffer. + +* Parameters: +* in +* Pointer to the null terminated string to be converted. If this +* is NULL, the supplied contents of the "out" string are used as +* the input string. +* out +* Pointer to the buffer to receive the converted string. The +* length of this buffer is given by "blen". If NULL is supplied +* for "in", then the supplied contents of "out" are converted and +* written back into "out" over-writing the supplied contents. +* upper +* If non-zero, the string is converted to upper case. Otherwise it +* is converted to lower case. +* blen +* The length of the output buffer. Ignored if "in" is NULL. No +* more than "blen - 1" characters will be copied from "in" to +* "out", and a terminating null character will then be added. + +*-- +*/ + +/* Local Variables: */ + const char *pin; + char *pout; + int i; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* The simple case of over-writing the supplied string. */ + if( ! in ) { + pout = out - 1; + while( *(++pout) ) *pout = toupper( (int) *pout ); + +/* If a separate output buffer has been supplied... */ + } else { + +/* Initialise pointers to the input and output buffers. */ + pin = in; + pout = out; + +/* Copy the string character by character, converting the case in the + process. Start counting from 1, not 0, in order to ensure that we are + left with room for a terminating null. */ + for( i = 1; i < blen && *pin; i++ ) { + *(pout++) = toupper( (int) *(pin++) ); + } + +/* Terminate the returned string. */ + *pout = 0; + } +} + +int astChrMatch_( const char *str1, const char *str2, int *status ) { +/* +*++ +* Name: +* astChrMatch + +* Purpose: +* Case insensitive string comparison. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* int astChrMatch( const char *str1, const char *str2 ) + +* Description: +* This function compares two null terminated strings for equality, +* discounting differences in case and any trailing white space in either +* string. + +* Parameters: +* str1 +* Pointer to the first string. +* str2 +* Pointer to the second string. + +* Returned Value: +* astChrMatch() +* Non-zero if the two strings match, otherwise zero. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*-- +*/ + +/* Local Variables: */ + int match; /* Strings match? */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + match = 1; + +/* Loop to compare characters in the two strings until a mis-match occurs or + we reach the end of the longer string. */ + while ( match && ( *str1 || *str2 ) ) { + +/* Two characters match if (a) we are at the end of one string and the other + string contains white space or (b) both strings contain the same character + when converted to lower case. */ + match = ( !*str1 && isspace( *str2 ) ) || + ( !*str2 && isspace( *str1 ) ) || + ( tolower( *str1 ) == tolower( *str2 ) ); + +/* Step through each string a character at a time until its end is reached. */ + if ( *str1 ) str1++; + if ( *str2 ) str2++; + } + +/* Return the result. */ + return match; +} + +int astChrMatchN_( const char *str1, const char *str2, size_t n, int *status ) { +/* +*++ +* Name: +* astChrMatchN + +* Purpose: +* Case insensitive string comparison of at most N characters + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* int astChrMatchN( const char *str1, const char *str2, size_t n ) + +* Description: +* This function compares two null terminated strings for equality, +* discounting differences in case and any trailing white space in either +* string. No more than "n" characters are compared. + +* Parameters: +* str1 +* Pointer to the first string. +* str2 +* Pointer to the second string. +* n +* Maximum number of characters to compare. + +* Returned Value: +* astChrMatchN() +* Non-zero if the two strings match, otherwise zero. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*-- +*/ + +/* Local Variables: */ + int match; /* Strings match? */ + int nc; /* Number of characters compared so far */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + match = 1; + +/* So far we have compared zero characters */ + nc = 0; + +/* Loop to compare characters in the two strings until a mis-match occurs or + we reach the end of the longer string, or we reach the specified + maximum number of characters. */ + while ( match && ( *str1 || *str2 ) && nc++ < n ) { + +/* Two characters match if (a) we are at the end of one string and the other + string contains white space or (b) both strings contain the same character + when converted to lower case. */ + match = ( !*str1 && isspace( *str2 ) ) || + ( !*str2 && isspace( *str1 ) ) || + ( tolower( *str1 ) == tolower( *str2 ) ); + +/* Step through each string a character at a time until its end is reached. */ + if ( *str1 ) str1++; + if ( *str2 ) str2++; + } + +/* Return the result. */ + return match; +} + +char **astChrSplit_( const char *str, int *n, int *status ) { +/* +*++ +* Name: +* astChrSplit + +* Purpose: +* Extract words from a supplied string. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char **astChrSplit_( const char *str, int *n ) + +* Description: +* This function extracts all space-separated words form the supplied +* string and returns them in an array of dynamically allocated strings. + +* Parameters: +* str +* Pointer to the string to be split. +* n +* Address of an int in which to return the number of words returned. + +* Returned Value: +* astChrSplit() +* A pointer to a dynamically allocated array containing "*n" elements. +* Each element is a pointer to a dynamically allocated character +* string containing a word extracted from the supplied string. Each +* of these words will have no leading or trailing white space. + +* Notes: +* - A NULL pointer is returned if this function is invoked with the +* global error status set or if it should fail for any reason, or if +* the supplied string contains no words. +*-- +*/ + +/* Local Variables: */ + char **result; + char *w; + const char *p; + const char *ws; + int first; + int state; + int wl; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise. */ + result = NULL; + ws = NULL; + *n = 0; + +/* State 0 is "looking for the next non-white character which marks the + start of the next word". State 1 is "looking for the next white character + which marks the end of the current word". */ + state = 0; + +/* Loop through all characters in the supplied string, including the + terminating null. */ + p = str - 1; + first = 1; + while( *(p++) || first ) { + first = 0; + +/* If this is the terminating null or a space, and we are currently looking + for the end of a word, allocate memory for the new word, copy the text + in, terminate it, extend the returned array by one element, and store + the new word in it. */ + if( !*p || isspace( *p ) ) { + if( state == 1 ) { + wl = p - ws; + w = astMalloc( wl + 1 ); + if( w ) { + strncpy( w, ws, wl ); + w[ wl ] = 0; + result = astGrow( result, *n + 1, sizeof( char * ) ); + if( result ) result[ (*n)++ ] = w; + } + state = 0; + } + +/* If this is non-blank character, and we are currently looking for the + start of a word, note the address of the start of the word, and + indicate that we are now looking for the end of a word. */ + } else { + if( state == 0 ) { + state = 1; + ws = p; + } + } + } + +/* Return the result. */ + return result; +} + +char **astChrSplitC_( const char *str, char c, int *n, int *status ) { +/* +*++ +* Name: +* astChrSplitC + +* Purpose: +* Split a string using a specified character delimiter. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char **astChrSplitC( const char *str, char c, int *n ) + +* Description: +* This function extracts all sub-strings separated by a given +* character from the supplied string and returns them in an array +* of dynamically allocated strings. The delimiter character itself +* is not included in the returned strings. +* +* Delimiter characters that are preceded by "\" are not used as +* delimiters but are included in the returned word instead (without +* the "\"). + +* Parameters: +* str +* Pointer to the string to be split. +* c +* The delimiter character. +* n +* Address of an int in which to return the number of words returned. + +* Returned Value: +* astChrSplitC() +* A pointer to a dynamically allocated array containing "*n" elements. +* Each element is a pointer to a dynamically allocated character +* string containing a word extracted from the supplied string. + +* Notes: +* - A NULL pointer is returned if this function is invoked with the +* global error status set or if it should fail for any reason, or if +* the supplied string contains no words. +*-- +*/ + +/* Local Variables: */ + char **result; + char *word; + const char *p; + int escaped; + int wordlen; + +/* Initialise returned values. */ + *n = 0; + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* More initialisations. */ + word = NULL; + wordlen = 0; + escaped = 0; + +/* Loop through all characters in the supplied string, including the + terminating null. */ + p = str; + while( *p ) { + +/* Is this a delimiter character? */ + if( *p == c ) { + +/* If it is escaped, it does not mark the end of a word. Put it into the + current output buffer instead, overwriting the escape character that + preceded it. */ + if( escaped ) { + word[ wordlen - 1 ] = c; + +/* The next character is not escaped. */ + escaped = 0; + +/* If the delimiter is not escaped, terminate the current word and store + a pointer to it in the returned array. */ + } else { + result = astGrow( result, *n + 1, sizeof( char * ) ); + word = astGrow( word, wordlen + 1, 1 ); + if( result && word ) { + word[ wordlen ] = 0; + result[ (*n)++ ] = word; + wordlen = 0; + word = NULL; + } + } + +/* If this is not a delimitier character, store it in the returned word. */ + } else { + word = astGrow( word, wordlen + 1, 1 ); + if( word ) word[ wordlen++ ] = *p; + +/* If the current character was escaped, indicate that the next character + is not escaped. */ + if( escaped ) { + escaped = 0; + +/* If this character is a unescaped backslash, set a flag indicating that the + next character is escaped. */ + } else if( *p == '\\' ){ + escaped = 1; + } + } + +/* Move on to the next character. */ + p++; + } + +/* Store the text following the final delimitier. */ + result = astGrow( result, *n + 1, sizeof( char * ) ); + word = astGrow( word, wordlen + 1, 1 ); + if( result && word ) { + word[ wordlen ] = 0; + result[ (*n)++ ] = word; + } + +/* Return the result. */ + return result; +} + +char **astChrSplitRE_( const char *str, const char *regexp, int *n, + const char **matchend, int *status ) { +/* +*++ +* Name: +* astChrSplitRE + +* Purpose: +* Extract sub-strings matching a specified regular expression. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char **astChrSplitRE( const char *str, const char *regexp, int *n, +* const char **matchend ) + +* Description: +* This function compares the supplied string with the supplied +* regular expression. If they match, each section of the test string +* that corresponds to a parenthesised sub-string in the regular +* expression is copied and stored in the returned array. + +* Parameters: +* str +* Pointer to the string to be split. +* regexp +* The regular expression. See "Template Syntax:" in the astChrSub +* prologue. Note, this function differs from astChrSub in that any +* equals signs (=) in the regular expression are treated literally. +* n +* Address of an int in which to return the number of sub-strings +* returned. +* matchend +* A pointer to a location at which to return a pointer to the +* character that follows the last character within the supplied test +* string that matched any parenthesises sub-section of "regexp". A +* NULL pointer is returned if no matches were found. A NULL pointer +* may be supplied if the location of the last matching character is +* not needed. + +* Returned Value: +* astChrSplitRE() +* A pointer to a dynamically allocated array containing "*n" elements. +* Each element is a pointer to a dynamically allocated character +* string containing a sub-string extracted from the supplied string. +* The array itself, and the strings within it, should all be freed +* using astFree when no longer needed. + +* Notes: +* - If a parenthesised sub-string in the regular expression is matched +* by more than one sub-string within the test string, then only the +* first is returned. To return multiple matches, the regular +* expression should include multiple copies of the parenthesised +* sub-string (for instance, separated by ".+?" if the intervening +* string is immaterial). +* - A NULL pointer is returned if this function is invoked with the +* global error status set or if it should fail for any reason, or if +* the supplied string contains no words. +*-- +*/ + +/* Local Variables: */ + char **result; + char *temp; + int i; + +/* Initialise returned values. */ + *n = 0; + result = NULL; + +/* Check global status */ + if( !astOK ) return result; + +/* Call ChrSuber to do the work, saving the matching parts of the test + string. */ + temp = ChrSuber( str, regexp, NULL, 0, 1, &result, n, matchend, status ); + if( temp ) { + temp = astFree( temp ); + +/* Free all results if no match was found. */ + } else if( result ) { + for( i = 0; i < *n; i++ ) result[ i ] = astFree( result[ i ] ); + result = astFree( result ); + *n = 0; + } + +/* Return the result */ + return result; +} + +char *ChrSuber( const char *test, const char *pattern, const char *subs[], + int nsub, int ignore_equals, char ***parts, int *npart, + const char **matchend, int *status ){ +/* +* Name: +* ChrSuber + +* Purpose: +* Performs substitutions on a supplied string. + +* Type: +* Private function. + +* Synopsis: +* #include "memory.h" +* char *ChrSuber( const char *test, const char *pattern, +* const char *subs[], int nsub, int ignore_equals, +* char ***parts, int *npart, const char **matchend, +* int *status ) + +* Description: +* This function performs the work for astChrSub and astChrSplitRE. + +* Parameters: +* test +* The string to be tested. +* pattern +* The template string. See "Template Syntax:" in the astChrSub + prologue. +* subs +* An array of strings that are to replace the sections of the test +* string that match each parenthesised sub-string in "pattern". The +* first element of "subs" replaces the part of the test string that +* matches the first parenthesised sub-string in the template, etc. +* +* If "nsub" is zero, then the "subs" pointer is ignored. In this +* case, and if parameter "ignore_equals" is zero, substitution strings +* may be specified by appended them to the end of the "pattern" string, +* separated by "=" characters +* nsub +* The number of substitution strings supplied in array "subs". +* ignore_equals +* If non-zero, any equals signs in the supplied pattern are +* treated literally, rather than being used to split the template +* from any substitution strigs. +* parts +* Address of a location at which to return a pointer to an array +* of character string pointers. The strings are the sub-sections +* of "test" that matched the parenthesised sub-sections of +* "template". The array will have "*npart" elements. Ignored if NULL. +* npart +* Address of a location at which to return the length of the +* "parts" array. Ignored if "parts" is NULL. +* matchend +* A pointer to a location at which to return a pointer to the +* character that follows the last character within the supplied test +* string that matched any parenthesises sub-section of "regexp". A +* NULL pointer is returned if no matches were found. A NULL pointer +* may be supplied if the location of the last matching character is +* not needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated string holding the result +* of the substitutions, or NULL if the test string does not match +* the template string. This string should be freed using astFree +* when no longer needed. If no substituions are specified then a +* copy of the test string is returned if it matches the template. + +* Notes: +* - A NULL pointer is returned if this function is invoked with the +* global error status set or if it should fail for any reason, or if +* the supplied test string does not match the template. + +*/ + +/* Local Variables: */ + char **sections; + char **temps; + char *cptr; + char *result; + char *temp; + char *template; + int i; + int nsec; + int ntemp; + size_t tlen; + +/* Initialise */ + result = NULL; + if( parts ) *npart = 0; + +/* Check global status */ + if( !astOK ) return result; + +/* If required, split the total "pattern" string into sections, using + (unescaped) "=" characters as the delimiter. The first section is the + actual template, and each subsequent section (if any) holds a + substitution string. */ + if( ! ignore_equals ) { + sections = astChrSplitC( pattern, '=', &nsec ); + +/* If equals signs are being treated literally, just take a copy of the + supplied pattern. */ + } else { + cptr = astStore( NULL, pattern, strlen( pattern ) + 1 ); + sections = &cptr; + nsec = 1; + } + + if( sections ) { + +/* If the caller did not provide any substitution strings, use the ones + appended to the end of the pattern string (if any). */ + if( nsub == 0 ) { + subs = (void *) ( sections + 1 ); + nsub = nsec - 1; + } + +/* Split the template section into sub-sections, using (unescaped) "|" + characters as the delimiter. Each sub-section is an alternate pattern + matching template. */ + temps = astChrSplitC( sections[ 0 ], '|', &ntemp ); + + } else { + temps = 0; + ntemp = 0; + } + +/* Loop round each template until all templates have been checked or a + match occurs.. */ + for( i = 0; i < ntemp && !result; i++ ) { + temp = temps[ i ]; + tlen = strlen( temp ); + +/* If the template starts with "^" or "(^", remove the "^" character. + Otherwise insert ".*?" at the start. Allocate three extra characters + in case we later need to add ".*?" to the end of the string. */ + if( temp[ 0 ] == '^' ) { + template = astMalloc( tlen + 3 ); + if( template ) { + strcpy( template, temp + 1 ); + tlen--; + } + + } else if( temp[ 0 ] == '(' && temp[ 1 ] == '^') { + template = astMalloc( tlen + 3 ); + if( template ) { + template[ 0 ] = '('; + strcpy( template + 1, temp + 2 ); + tlen--; + } + + } else { + template = astMalloc( tlen + 7 ); + if( template ) { + template[ 0 ] = '.'; + template[ 1 ] = '*'; + template[ 2 ] = '?'; + strcpy( template + 3, temp ); + tlen += 3; + } + } + +/* If the pattern ends with "$" or "$)", remove the "$" character. Otherwise + insert ".*?" at the end. */ + if( template[ tlen - 1 ] == '$' ) { + tlen--; + + } else if( template[ tlen - 2 ] == '$' && template[ tlen - 1 ] == ')' ) { + template[ tlen - 2 ] = ')'; + tlen--; + + } else { + template[ tlen ] = '.'; + template[ tlen + 1 ] = '*'; + template[ tlen + 2 ] = '?'; + tlen += 3; + } + +/* Ensure the string is terminated */ + template[ tlen ] = 0; + +/* See if the test string matches the current template. */ + result = ChrMatcher( test, test + strlen( test ), template, pattern, + subs, nsub, 0, 1, parts, npart, matchend, status ); + +/* Free resources. */ + template = astFree( template ); + } + + if( temps ) { + for( i = 0; i < ntemp; i++ ) temps[ i ] = astFree( temps[ i ] ); + temps = astFree( temps ); + } + + if( sections ) { + for( i = 0; i < nsec; i++ ) sections[ i ] = astFree( sections[ i ] ); + if( ! ignore_equals ) sections = astFree( sections ); + } + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astFree( result ); + +/* Return the result */ + return result; +} + +char *astChrSub_( const char *test, const char *pattern, const char *subs[], + int nsub, int *status ){ +/* +*++ +* Name: +c astChrSub +f AST_CHRSUB + +* Purpose: +* Performs substitutions on a supplied string. + +* Type: +* Public function. + +* Synopsis: +c #include "memory.h" +c char *astChrSub( const char *test, const char *pattern, +c const char *subs[], int nsub ) +f MATCH = AST_CHRSUB( TEST, PATTERN, RESULT, STATUS ) + +* Description: +* This function checks a supplied test string to see if it matches a +* supplied template. If it does, specified sub-sections of the test +* string may optionally be replaced by supplied substitution strings. +* The resulting string is returned. + +* Parameters: +c test +f TEST = CHARACTER * ( * ) (Given) +* The string to be tested. +* pattern +f PATTERN = CHARACTER * ( * ) (Given) +* The template string. See "Template Syntax:" below. +* subs +* An array of strings that are to replace the sections of the test +* string that match each parenthesised sub-string in "pattern". The +* first element of "subs" replaces the part of the test string that +* matches the first parenthesised sub-string in the template, etc. +* +* If "nsub" is zero, then the "subs" pointer is ignored. In this +* case, substitution strings may be specified by appended them to +* the end of the "pattern" string, separated by "=" characters. +* Note, if you need to include a literal "=" character in the +* pattern, precede it by an escape "\" character. +* nsub +* The number of substitution strings supplied in array "subs". +f RESULT = CHARACTER * ( * ) (Returned) +f Returned holding the result of the substitutions. If the test +f string does not match the template, then a blank string is +f returned. + +* Returned Value: +c astChrSub() +c A pointer to a dynamically allocated string holding the result +c of the substitutions, or NULL if the test string does not match +c the template string. This string should be freed using astFree +c when no longer needed. If no substituions are specified then a +c copy of the test string is returned if it matches the template. +f AST_CHRSUB = LOGICAL +f .TRUE. if the test string matched the supplied template, and +f .FALSE. otherwise. + +* Template Syntax: +* The template syntax is a minimal form of regular expression, The +* quantifiers allowed are "*", "?", "+", "{n}", "*?" and "+?" (the +* last two are non-greedy - they match the minimum length possible +* that still gives an overall match to the template). The only +* constraints allowed are "^" and "$". The following atoms are allowed: +* +* - [chars]: Matches any of the specified characters. +* +* - [^chars]: Matches anything but the specified characters. +* +* - .: Matches any single character. +* +* - x: Matches the character x so long as x has no other significance. +* +* - \x: Always matches the character x (except for [dDsSwW]). +* +* - \d: Matches a single digit. +* +* - \D: Matches anything but a single digit. +* +* - \w: Matches any alphanumeric character, and "_". +* +* - \W: Matches anything but alphanumeric characters, and "_". +* +* - \s: Matches white space. +* +* - \S: Matches anything but white space. +* +* Note, minus signs ("-") within brackets have no special significance, +* so ranges of characters must be specified explicitly. +* +* Multiple template strings can be concatenated, using the "|" +* character to separate them. The test string is compared against +* each one in turn until a match is found. +* +c Parentheses are used within each template to identify sub-strings +c that are to be replaced by the strings supplied in "sub". +c +c If "nsub" is supplied as zero, then substitution strings may be +c specified by appended them to the end of the "pattern" string, +c separated by "=" characters. If "nsub" is not zero, then any +c substitution strings appended to the end of "pattern" are ignored. +f +f Parentheses are used within each template to identify sub-strings +f that are to be replaced by new strings. The new strings are +f specified by appended them to the end of the "pattern" string, +f separated by "=" characters. +* +c Each element of "subs" +f Each new string +* may contain a reference to a token of the +* form "$1", "$2", etc. The "$1" token will be replaced by the part +* of the test string that matched the first parenthesised sub-string +* in "pattern". The "$2" token will be replaced by the part of the +* test string that matched the second parenthesised sub-string in +* "pattern", etc. +* + +c Notes: +c - A NULL pointer is returned if this function is invoked with the +c global error status set or if it should fail for any reason, or if +c the supplied test string does not match the template. + +*-- +*/ + +/* Call ChrSuber to do the work, without saving the matching parts of the + test string. */ + return ChrSuber( test, pattern, subs, nsub, 0, NULL, NULL, NULL, status ); +} + +void astChrTrunc_( char *text, int *status ){ +/* +*++ +* Name: +* astChrTrunc + +* Purpose: +* Terminate a string to exclude trailing spaces. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void astChrTrunc( char *text ) + +* Description: +* This function pokes a null character into the supplied string to +* remove any trailing spaces. + +* Parameters: +* text +* The string to be truncated. + +*-- +*/ + if( !text ) return; + text[ astChrLen( text ) ] = 0; +} + +void *astFree_( void *ptr, int *status ) { +/* +*++ +* Name: +* astFree + +* Purpose: +* Free previously allocated memory. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void *astFree( void *ptr ) + +* Description: +* This function frees memory that has previouly been dynamically +* allocated using one of the AST memory function. + +* Parameters: +* ptr +* Pointer to previously allocated memory. An error will result +* if the memory has not previously been allocated by another +* function in this module. However, a NULL pointer value is +* accepted (without error) as indicating that no memory has yet +* been allocated, so that no action is required. + +* Returned Value: +* astFree() +* Always returns a NULL pointer. + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + Memory *mem; /* Pointer to memory header */ + int isdynamic; /* Is the memory dynamically allocated? */ + size_t size; /* The usable size of the memory block */ + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* If the incoming pointer is NULL, do nothing. Otherwise, check if it + points at dynamically allocated memory (IsDynamic sets the global + error status if it does not). */ + if( ptr ) { + IS_DYNAMIC( ptr, isdynamic ); + } else { + isdynamic = 0; + } + if ( isdynamic ) { + +/* If OK, obtain a pointer to the memory header. */ + mem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY ); + +#ifdef MEM_DEBUG + DeIssue( mem, status ); +#endif + +/* If the memory block is small enough, and the cache is being used, put it + into the cache rather than freeing it, so that it can be reused. */ + size = mem->size; + if( use_cache && size <= MXCSIZE ) { + mem->next = cache[ size ]; + cache[ size ] = mem; + +/* Set the size to zero to indicate that the memory block has been freed. + The size of the block is implied by the Cache element it is stored in. */ + mem->size = (size_t) 0; + +/* Simply free other memory blocks, clearing the "magic number" and size + values it contains. This helps prevent accidental re-use of the memory. */ + } else { + mem->magic = (unsigned long) 0; + mem->size = (size_t) 0; + +/* Free the allocated memory. */ + FREE( mem ); + } + } + +/* Always return a NULL pointer. */ + return NULL; + +} + +void *astFreeDouble_( void *ptr, int *status ) { +/* +*++ +* Name: +* astFreeDouble + +* Purpose: +* Free previously double allocated memory. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void *astFreeDouble( void *ptr ) + +* Description: +* This function frees memory that has previouly been dynamically +* allocated using one of the AST memory function. It assumes that +* the supplied pointer is a pointer to an array of pointers. Each +* of these pointers is first freed, and then the supplied pointer +* is freed. +* +* Note, this routine should not be used with arrays allocated +* by astGrow since astGrow over-allocates and so there may be +* non-initialised pointers at the end of the array. + +* Parameters: +* ptr +* Pointer to previously allocated memory. An error will result +* if the memory has not previously been allocated by another +* function in this module. However, a NULL pointer value is +* accepted (without error) as indicating that no memory has yet +* been allocated, so that no action is required. + +* Returned Value: +* astFreeDouble() +* Always returns a NULL pointer. + +*-- +*/ + +/* Local Variables: */ + int iptr; /* Index of sub-pointer */ + int nptr; /* Number of sub-pointers */ + size_t size; /* The usable size of the memory block */ + void **ptrs; /* Pointer to array of pointers */ + +/* Check a pointer was supplied. */ + if( ! ptr ) return NULL; + +/* Get the size of the memory area. */ + size = astSizeOf( ptr ); + +/* Get the number of points this amount of memory could hold. */ + nptr = size/sizeof( void * ); + +/* Report an error if the size is not an integer multiple of an address + size. */ + if( nptr*sizeof( void * ) != size ) { + astError( AST__MEMIN, "Invalid attempt to free double allocated " + "memory: the supplied memory size (%lu bytes) is not " + "an integer multiple of %lu.", status, size, + sizeof( void * ) ); + + } else { + +/* Free each sub-pointer. */ + ptrs = (void **) ptr; + for( iptr = 0; iptr < nptr; iptr++ ) { + ptrs[ iptr ] = astFree( ptrs[ iptr ] ); + } + +/* Free the supplied pointer. */ + ptr = astFree( ptr ); + } + +/* Always return a NULL pointer. */ + return NULL; +} + +void *astGrow_( void *ptr, int n, size_t size, int *status ) { +/* +*++ +* Name: +* astGrow + +* Purpose: +* Allocate memory for an adjustable array. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void *astGrow( void *ptr, int n, size_t size ) + +* Description: +* This function allocates memory in which to store an array of +* data whose eventual size is unknown. It should be invoked +* whenever a new array size is determined and will appropriately +* increase the amount of memory allocated when necessary. In +* general, it will over-allocate in anticipation of future growth +* so that the amount of memory does not need adjusting on every +* invocation. + +* Parameters: +* ptr +* Pointer to previously allocated memory (or NULL if none has +* yet been allocated). +* n +* Number of array elements to be stored (may be zero). +* size +* The size of each array element. + +* Returned Value: +* astGrow() +* If the memory was allocated successfully, a pointer to the start +* of the possibly new memory region is returned (this may be the +* same as the original pointer). + +* Notes: +* - When new memory is allocated, the existing contents are preserved. +* - This function does not free memory once it is allocated, so +* the size allocated grows to accommodate the maximum size of the +* array (or "high water mark"). Other memory handling routines may +* be used to free the memory (or alter its size) if necessary. +* - If this function is invoked with the global error status set, +* or if it fails for any reason, the original pointer value is +* returned and the memory contents are unchanged. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int isdynamic; /* Is the memory dynamically allocated? */ + Memory *mem; /* Pointer to memory header */ + size_t newsize; /* New size to allocate */ + void *new; /* Result pointer */ + +/* Check the global error status. */ + if ( !astOK ) return ptr; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = ptr; + +/* Calculate the total size of memory needed. */ + size *= (size_t) n; + +/* If no memory has yet been allocated, allocate exactly the amount + required. */ + if ( !ptr ) { + new = astMalloc( size ); + +/* Otherwise, check that the incoming pointer identifies previously + allocated memory. */ + } else { + IS_DYNAMIC( ptr, isdynamic ); + if ( isdynamic ) { + +/* Obtain a pointer to the memory header and check if the new size + exceeds that already allocated. */ + mem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY ); + if ( mem->size < size ) { + +/* If so, calculate a possible new size by doubling the old + size. Increase this further if necessary. */ + newsize = mem->size * ( (size_t) 2 ); + if ( size > newsize ) newsize = size; + +/* Re-allocate the memory. */ + new = astRealloc( ptr, newsize ); + } + } + } + +/* Return the result. */ + return new; +} + +int astIsDynamic_( const void *ptr, int *status ) { +/* +*++ +* Name: +* astIsDynamic + +* Purpose: +* Returns a flag indicating if memory was allocated dynamically. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* int astIsDynamic_( const void *ptr ) + +* Description: +* This function takes a pointer to a region of memory and tests if +* the memory has previously been dynamically allocated using other +* functions from this module. It does this by checking for the +* presence of a "magic" number in the header which precedes the +* allocated memory. If the magic number is not present (or the +* pointer is invalid for any other reason), zero is returned. +* Otherwise 1 is returned. + +* Parameters: +* ptr +* Pointer to test. + +* Returned Value: +* astIsDynamic() +* Non-zero if the memory was allocated dynamically. Zero is returned +* if the supplied pointer is NULL. + +* Notes: +* - A value of zero is returned if this function is invoked with +* the global error status set, or if it fails for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + Memory *isdynmem; /* Pointer to memory header */ \ + +/* Check the global error status and the supplied pointer. */ + if ( !astOK || ! ptr ) return 0; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Derive a pointer to the memory header that precedes the + supplied region of memory. */ + isdynmem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY ); + +/* Check if the "magic number" in the header is valid, returning non-zero + if it is. */ + return ( isdynmem->magic == MAGIC( isdynmem, isdynmem->size ) ); +} + +void *astMalloc_( size_t size, int init, int *status ) { +/* +*++ +* Name: +* astMalloc + +* Purpose: +* Allocate memory. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void *astMalloc( size_t size ) + +* Description: +* This function allocates memory in a similar manner to the +* standard C "malloc" function, but with improved security +* (against memory leaks, etc.) and with error reporting. It also +* allows zero-sized memory allocation (without error), resulting +* in a NULL returned pointer value. + +* Parameters: +* size +* The size of the memory region required (may be zero). + +* Returned Value: +* astMalloc() +* If successful, the function returns a pointer to the start of +* the allocated memory region. If the size allocated is zero, this +* will be a NULL pointer. + +* Notes: +* - A pointer value of NULL is returned if this function is +* invoked with the global error status set or if it fails for any +* reason. +*-- + +* astMallocInit: +* - This function can be invoked using either the public astMalloc +* macro documented above, or the private astMallocInit macro. +* astMallocInit has the same interface as astMalloc, but calls calloc +* rather than malloc so that the allocated memory is filled with zeros. +* Ideally, we should use an extra layer in the calling heirarchy to +* remove the hidden "init" argument in the astMalloc_ interface, but +* astMalloc is time-critical in many situations and so it is included +* as a "hidden" argument. + +*/ + +/* Local Constants: */ +#define ERRBUF_LEN 80 + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + char *errstat; /* Pointer to system error message */ + Memory *mem; /* Pointer to space allocated by malloc */ + void *result; /* Returned pointer */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check that the size requested is not negative and report an error + if it is. */ + if ( size < (size_t) 0 ) { + astError( AST__MEMIN, + "Invalid attempt to allocate %lu bytes of memory.", status, + (unsigned long) size ); + +/* Otherwise, if the size is greater than zero, either get a previously + allocated memory block from the cache, or attempt to use malloc + to allocate the memory, including space for the header structure. */ + } else if ( size != (size_t ) 0 ) { + +/* If the cache is being used and a cached memory block of the required size + is available, remove it from the cache array and use it. */ + mem = ( use_cache && size <= MXCSIZE ) ? cache[ size ] : NULL; + if( mem ) { + cache[ size ] = mem->next; + mem->next = NULL; + mem->size = (size_t) size; + +/* Initialise the memory (but not the header) if required. */ + if( init ) (void) memset( (char *) mem + SIZEOF_MEMORY, 0, size ); + +/* Otherwise, allocate a new memory block using "malloc" or "calloc". */ + } else { + if( init ) { + mem = CALLOC( 1, SIZEOF_MEMORY + size ); + } else { + mem = MALLOC( SIZEOF_MEMORY + size ); + } + +/* Report an error if malloc failed. */ + if ( !mem ) { + +#if HAVE_STRERROR_R + strerror_r( errno, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( errno ); +#endif + astError( AST__NOMEM, "malloc: %s", status, errstat ); + astError( AST__NOMEM, "Failed to allocate %lu bytes of memory.", status, + (unsigned long) size ); + +/* If successful, set the "magic number" in the header and also store + the size. */ + } else { + mem->magic = MAGIC( mem, size ); + mem->size = size; + mem->next = NULL; + +#ifdef MEM_DEBUG + mem->id = -1; + mem->prev = NULL; +#endif + + } + } + +/* Do nothing more if no memory is being returned. */ + if( mem ) { + +#ifdef MEM_DEBUG + Issue( mem, status ); +#endif + +/* Increment the memory pointer to the start of the region of + allocated memory to be used by the caller.*/ + result = mem; + result = (char *) result + SIZEOF_MEMORY; + } + } + +/* Return the result. */ + return result; +} +#undef ERRBUF_LEN + +static char *ChrMatcher( const char *test, const char *end, const char *template, + const char *pattern, const char *subs[], int nsub, + int ignore, int expdoll, char ***mres, int *mlen, + const char **matchend, int *status ){ +/* +* Name: +* ChrMatcher + +* Purpose: +* Performs substitutions on a supplied string. + +* Type: +* Private function. + +* Synopsis: +* #include "memory.h" +* char *ChrMatcher( const char *test, const char *end, const char *template, +* const char *pattern, const char *subs[], int nsub, +* int ignore, int expdoll, char ***mres, int *mlen, +* const char **matchend, int *status ) + +* Description: +* This function is performs most of the work for astChrSub. + +* Parameters: +* test +* The string to be tested. +* end +* Pointer to the terminating null character at the end of "test". +* template +* The template string. See astChrSub for details. +* pattern +* The user supplied "pattern" string (only used for error messages). +* subs +* An array of strings holding the values that are to be substituted +* into each parenthesised substring in "test". +* nsub +* The length of the subs arrays. +* ignore +* If non-zero, then no substitutions are performed, and any +* inbalance in parentheses is ignored. +* expdoll +* If non-zero, then any "$1", "$2", etc, tokens in the +* substitution fields will be repalced by the corresponding fields +* in the test string. +* mres +* Address of a location at which to return a pointer to an array +* of character string pointers. The strings are the sub-sections +* of "test" that matched the parenthesised sub-sections of +* "template". The array will have "*m" elements. Ignored if NULL. +* mlen +* Address of a location at which to return the length of the +* returned "mres" array. Ignored if "mres" is NULL. +* matchend +* A pointer to a location at which to return a pointer to the +* character that follows the last character within the supplied test +* string that matched any parenthesises sub-section of "regexp". A +* NULL pointer is returned if no matches were found. A NULL pointer +* may be supplied if the location of the last matching character is +* not needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated string holding the result of the +* substitutions, or NULL if the test string does not match the template +* string. This string should be freed using astFree when no longer +* needed. + +* Notes: +* - A NULL pointer is returned if this function is invoked with the +* global error status set or if it should fail for any reason, or if +* the supplied test string does not match the template. +*/ + +/* Local Variables: */ + char **matches; + char **newsubs; + char **parts; + char *allowed; + char *r; + char *result; + char *sres; + char *stest; + char stemp[10]; + const char *aaa; + const char *aa; + const char *a; + const char *b; + int allow; + int dollar; + int end_sub; + int greedy; + int i; + int in_sub; + int ipart; + int match; + int matchlen; + int max_na; + int min_na; + int na; + int nb; + int nmatch; + int npart; + int partlen; + int reslen; + int start_sub; + size_t stl; + +/* Initialisation. */ + if( mres ) *mlen = 0; + aaa = NULL; + if( matchend ) *matchend = NULL; + +/* Check the global error status. */ + if( !astOK ) return NULL; + +/* more initialisation. */ + result = NULL; + allowed = NULL; + +/* Get memory for a set of pointers to copies of the test sub-strings that + fall between the sub-strings being replaced. */ + parts = astMalloc( sizeof( char *)*(size_t) ( nsub + 1 ) ); + +/* Get memory for a set of pointers to copies of the test sub-strings that + match the parenthesised sub-strings in the template. */ + matches = astMalloc( sizeof( char *)*(size_t) nsub ); + +/* Initialise pointers to the next test and template characters to read. */ + a = test; + b = template; + +/* Initialise the pointer to the start of the previous test character */ + aa = test; + +/* Assume the test string matches the template. */ + match = 1; + +/* The template pointer is not currently in a substitution field. */ + in_sub = 0; + +/* Initialise the number of substitution fields found so far. */ + npart = 0; + nmatch = 0; + +/* Loop until we have reached the end of either the test or template + string. We break out of the loop early if we find that the test string + does not match the template string. */ + while( match && *a && *b ) { + +/* Examine the string at the start of the template string. This returns a + string of allowed (or disallowed) characters that the next test character + can match, the number of template characters consumed, the minimum number + of test characters that must match the allowed character set, and a flag + indicating if the number of matching test characters can exceed the + minimum number or must be exactly equal to the minimum number. */ + allowed = CheckTempStart( template, b, pattern, allowed, &nb, &allow, + &min_na, &max_na, &start_sub, &end_sub, + &greedy, status ); + if( !astOK ) break; + +/* Increment the the pointer to the next template character. */ + b += nb; + +/* If the leading field in the template indicates the start of a + substitution field, record the test string up to the current point. */ + if( start_sub ){ + +/* Do nothing if we are ignoring substitutions. */ + if( ! ignore ){ + +/* Store a pointer to the first test character that matches the + substitution template. */ + aaa = a; + +/* Report an error and abort if we are already inside a substitution + field */ + if( in_sub ) { + astError( AST__BADSUB, "Invalid pattern matching template \"%s\": " + "missing ')'.", status, pattern ); + break; + } + +/* Indicate that we are now in a substitution field. */ + in_sub = 1; + +/* If possible, store a copy of the test string that started at the end + of the previous substitution field and ends at the current point. + First ensure the "parts" array is large enough since the string may + contain more than "nsub" parenthesised sub-strings. */ + parts = astGrow( parts, npart + 1, sizeof( char * ) ); + if( parts ) { + partlen = ( a - aa ); + parts[ npart ] = astStore( NULL, aa, partlen + 1 ); + if( parts[ npart ] ) { + parts[ npart ][ partlen ] = 0; + npart++; + } + } + } + +/* If the leading field in the template indicates the end of a + substitution field, initialise the start of the next part of the test + string. */ + } else if( end_sub ){ + +/* Do nothing if we are ignoring substitutions. */ + if( ! ignore ){ + +/* Report an error and abort if we are not currently in a substitution + field. */ + if( ! in_sub ) { + astError( AST__BADSUB, "Invalid pattern matching template \"%s\": " + "missing '('.", status, pattern ); + break; + } + +/* We are no longer in a substitution field. */ + in_sub = 0; + +/* If possible, store a copy of the test string that matched the + substitution template. */ + matches = astGrow( matches, nmatch + 1, sizeof( char * ) ); + if( matches ) { + matchlen = ( a - aaa ); + matches[ nmatch ] = astStore( NULL, aaa, matchlen + 1 ); + if( matches[ nmatch ] ) { + matches[ nmatch ][ matchlen ] = 0; + nmatch++; + if( matchend ) *matchend = a; + } + } + +/* Record the start of the next test string part. */ + aa = a; + } + +/* Otherwise, find how many characters at the front of the test string + match the leading field in the template. Find the number of leading + characters in the test string that are contained in the set of + characters allowed by the leading field in the template. */ + } else { + if( !allowed ) { + na = strlen( a ); + + } else if( allow ) { + na = strspn( a, allowed ); + + } else { + na = strcspn( a, allowed ); + } + +/* Check that the minmum number of matching characters is available. */ + if( na < min_na ){ + match = 0; + break; + } + +/* Dont match more characters than are needed. */ + if( na > max_na ) na = max_na; + +/* We cannot match more characters than are available. */ + if( na < max_na ) max_na = na; + +/* If we have exhausted the template, match the maximum number of + characters. */ + if( ! *b ) { + na = max_na; + +/* If we still have a match, we may choose to use fewer than the max + allowed number of test characters in order to allow the next template + field to be matched. Don't need to do this if we have reached the end + of the template. */ + } else if( max_na > min_na ) { + match = 0; + +/* If a greedy quantifier was used, try using a decreasing number of test + characters, starting at the maximum allowed and decreasing down to the + minimum, until a number is found which allows the rest of the string + to be matched. */ + if( greedy ) { + for( na = max_na; na >= min_na; na-- ) { + r = ChrMatcher( a + na, end, b, pattern, NULL, 0, 1, 0, + NULL, NULL, NULL, status ); + if( r ) { + match = 1; + r = astFree( r ); + break; + } + } + +/* If a non-greedy quantifier was used, try using an increasing number of + test characters, starting at the minimum allowed and increasing up to + the maximum, until a number is found which allows the rest of the string + to be matched. */ + } else { + for( na = min_na; na <= max_na; na++ ) { + r = ChrMatcher( a + na, end, b, pattern, NULL, 0, 1, 0, + NULL, NULL, NULL, status ); + if( r ) { + match = 1; + r = astFree( r ); + break; + } + } + } + } + +/* Increment the the pointer to the next test character. */ + a += na; + if( a > end ) a = end; + } + } + +/* If the test string is finished but the template string is not, see if + the next part of the template string will match a null test string. + But ignore the ends of substitution fields. */ + if( !*a && *b && match ) { + while( *b && *b != ')' ) { + allowed = CheckTempStart( template, b, pattern, allowed, &nb, &allow, + &min_na, &max_na, &start_sub, &end_sub, + &greedy, status ); + b += nb; + allowed = astFree( allowed ); + + if( min_na > 0 ) { + match = 0; + break; + } + } + } + +/* If the next character in the template is a closing parenthesis, then + we are finishing a substitution field. */ + if( match && *b == ')' ) { + +/*Check we are not ignoring substitutions. */ + if( ! ignore ){ + +/* Report an error and abort if we are not currently in a substitution + field. */ + if( ! in_sub ) { + astError( AST__BADSUB, "Invalid pattern matching template \"%s\": " + "missing '('.", status, pattern ); + } + +/* We are no longer in a substitution field. */ + in_sub = 0; + +/* If possible, store a copy of the test string that matched the + substitution field. */ + matches = astGrow( matches, nmatch + 1, sizeof( char * ) ); + if( matches ) { + matchlen = ( a - aaa ); + matches[ nmatch ] = astStore( NULL, aaa, matchlen + 1 ); + if( matches[ nmatch ] ) { + matches[ nmatch ][ matchlen ] = 0; + nmatch++; + if( matchend ) *matchend = a; + } + } + + aa = a; + } + b++; + } + +/* If the test string is finished but the template string is not, see if + the rest of the template string will match a null test string. */ + if( !*a && *b && match ) { + + while( *b ) { + allowed = CheckTempStart( template, b, pattern, allowed, &nb, &allow, + &min_na, &max_na, &start_sub, &end_sub, + &greedy, status ); + b += nb; + allowed = astFree( allowed ); + + if( min_na > 0 ) { + match = 0; + break; + } + } + + } + +/* No match if either string was not used completely. */ + if( *a || *b ) match = 0; + +/* Report an error if we are still inside a substitution field */ + if( match && in_sub && !ignore ) { + astError( AST__BADSUB, "Invalid pattern matching template \"%s\": " + "missing ')'.", status, pattern ); + match = 0; + } + +/* If we have a match, construct the returned string. */ + if( match && parts ) { + +/* Store the test string following the final substitution field. */ + parts = astGrow( parts, npart + 1, sizeof( char * ) ); + if( parts ) { + partlen = ( a - aa ); + parts[ npart ] = astStore( NULL, aa, partlen + 1 ); + if( parts[ npart ] ) { + parts[ npart ][ partlen ] = 0; + npart++; + } + } + +/* If required, expand $1, $2, etc within the replacement strings. */ + if( expdoll) { + newsubs = astMalloc( sizeof( char * )*nsub ); + if( newsubs ) { + for( i = 0; i < nsub; i++ ) { + stl = strlen( subs[ i ] ); + stest = astStore( NULL, subs[ i ], stl + 1 ); + for( dollar = 1; dollar <= nsub; dollar++ ) { + sprintf( stemp, ".*($%d).*", dollar ); + sres = ChrMatcher( stest, stest + stl, stemp, stemp, + (void *) ( matches + dollar - 1 ), + 1, 0, 0, NULL, NULL, NULL, status ); + if( sres ) { + (void) astFree( stest ); + stest = sres; + } + } + newsubs[ i ] = stest; + } + } + + } else { + newsubs = (char **) subs; + } + +/* Concatenate the sub-strings to form the final string. */ + reslen = 0; + for( ipart = 0; ipart < npart - 1; ipart++ ) { + result = astAppendString( result, &reslen, parts[ ipart ] ); + if( ipart < nsub ) { + result = astAppendString( result, &reslen, newsubs[ ipart ] ); + } else { + result = astAppendString( result, &reslen, matches[ ipart ] ); + } + } + result = astAppendString( result, &reslen, parts[ ipart ] ); + +/* Free resources. */ + if( newsubs && newsubs != (char **) subs ) { + for( i = 0; i < nsub; i++ ) { + newsubs[ i ] = astFree( newsubs[ i ] ); + } + newsubs = astFree( newsubs ); + } + } + + allowed = astFree( allowed ); + for( ipart = 0; ipart < npart; ipart++ ) { + parts[ ipart ] = astFree( parts[ ipart ] ); + } + parts = astFree( parts ); + +/* If required, return the array holding the test sub-strings that + matched the parenthesised template sub-strings, together with + the length of the array. Otherwise, free the memory holding these + strings. */ + if( mres ) { + *mres = matches; + *mlen = nmatch; + + } else if( matches ) { + for( i = 0; i < nmatch; i++ ) { + matches[ i ] = astFree( matches[ i ] ); + } + matches = astFree( matches ); + + } + +/* Return the result. */ + return result; +} + +int astMemCaching_( int newval, int *status ){ +/* +*++ +* Name: +* astMemCaching + +* Purpose: +* Controls whether allocated but unused memory is cached in this module. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* int astMemCaching( int newval ) + +* Description: +* This function sets a flag indicating if allocated but unused memory +* should be cached or not. It also returns the original value of the +* flag. +* +* If caching is switched on or off as a result of this call, then the +* current contents of the cache are discarded. +* +* Note, each thread has a separate cache. Calling this function +* affects only the currently executing thread. + +* Parameters: +* newval +* The new value for the MemoryCaching tuning parameter (see +* astTune in objectc.c). If AST__TUNULL is supplied, the current +* value is left unchanged. + +* Returned Value: +* astMemCaching() +* The original value of the MemoryCaching tuning parameter. + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + int i; + int result; + Memory *mem; + +#ifdef MEM_DEBUG + int id_list_size; + int *id_list; +#endif + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Store the original value of the tuning parameter. */ + result = use_cache; + +/* If a new value is to be set. */ + if( newval != AST__TUNULL ) { + +/* If the cache has been initialised, empty it. */ + if( cache_init ) { + +/* If we are listing the ID of every memory block in the cache, count the + number of blocks in the cache and then allocate an array to store the ID + values in. This is done so that we can sort them before displaying them. */ +#ifdef MEM_DEBUG + if( List_Cache ) { + Memory *next; + + id_list_size = 0; + for( i = 0; i <= MXCSIZE; i++ ) { + next = cache[ i ]; + while( next ) { + id_list_size++; + next = next->next; + } + } + + id_list = MALLOC( sizeof(int)*id_list_size ); + if( !id_list ) { + astError( AST__INTER, "astMemCaching: Cannot allocate %lu " + "bytes of memory", status, (unsigned long)(sizeof(int)*id_list_size) ); + } + + id_list_size = 0; + + } else { + id_list = NULL; + } +#endif + + for( i = 0; i <= MXCSIZE; i++ ) { + while( cache[ i ] ) { + mem = cache[ i ]; + cache[ i ] = mem->next; + mem->size = (size_t) i; + +#ifdef MEM_DEBUG + if( id_list ) { + id_list[ id_list_size++ ] = mem->id; + } +#endif + + FREE( mem ); + } + } + +/* If we are displaying the IDs of memory blocks still in the cache, sort + them using a bubblesort algorithm, then display them. */ +#ifdef MEM_DEBUG + if( id_list ) { + + if( id_list_size == 0 ) { + printf( "Emptying the AST memory cache - (the cache is " + "already empty)\n" ); + + } else { + int sorted, j, t; + + sorted = 0; + for( j = id_list_size - 2; !sorted && j >= 0; j-- ) { + sorted = 1; + for( i = 0; i <= j; i++ ) { + if( id_list[ i ] > id_list[ i + 1 ] ) { + sorted = 0; + t = id_list[ i ]; + id_list[ i ] = id_list[ i + 1 ]; + id_list[ i + 1 ] = t; + } + } + } + + printf( "Emptying the AST memory cache - freeing the " + "following memory blocks: "); + for( i = 0; i < id_list_size; i++ ) printf( "%d ", id_list[ i ] ); + printf( "\n" ); + + } + } +#endif + +/* Otherwise, initialise the cache array to hold a NULL pointer at every + element. */ + } else { + for( i = 0; i <= MXCSIZE; i++ ) cache[ i ] = NULL; + cache_init = 1; + } + +/* Store the new value. */ + use_cache = newval; + + } + +/* Return the original value. */ + return result; +} + +void *astRealloc_( void *ptr, size_t size, int *status ) { +/* +*++ +* Name: +* astRealloc + +* Purpose: +* Change the size of a dynamically allocated region of memory. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void *astRealloc( void *ptr, size_t size ) + +* Description: +* This function changes the size of a dynamically allocated region +* of memory, preserving its contents up to the minimum of the old +* and new sizes. This may involve copying the contents to a new +* location, so a new pointer is returned (and the old memory freed +* if necessary). +* +* This function is similar to the standard C "realloc" function +* except that it provides better security against programming +* errors and also supports the allocation of zero-size memory +* regions (indicated by a NULL pointer). + +* Parameters: +* ptr +* Pointer to previously allocated memory (or NULL if the +* previous size of the allocated memory was zero). +* size +* New size required for the memory region. This may be zero, in +* which case a NULL pointer is returned (no error results). It +* should not be negative. + +* Returned Value: +* astRealloc() +* If the memory was reallocated successfully, a pointer to the +* start of the new memory region is returned (this may be the same +* as the original pointer). If size was given as zero, a NULL +* pointer is returned. + +* Notes: +* - If this function is invoked with the error status set, or if +* it fails for any reason, the original pointer value is returned +* and the memory contents are unchanged. Note that this behaviour +* differs from that of the standard C "realloc" function which +* returns NULL if it fails. +*-- +*/ + +/* Local Constants: */ +#define ERRBUF_LEN 80 + +/* Local Variables: */ + astDECLARE_GLOBALS + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + char *errstat; /* Pointer to system error message */ + int isdynamic; /* Was memory allocated dynamically? */ + void *result; /* Returned pointer */ + Memory *mem; /* Pointer to memory header */ + +/* Check the global error status. */ + if ( !astOK ) return ptr; + +/* Initialise. */ + result = ptr; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* If a NULL pointer was supplied, use astMalloc to allocate some new + memory. */ + if ( !ptr ) { + result = astMalloc( size ); + +/* Otherwise, check that the pointer supplied points at memory + allocated by a function in this module (IsDynamic sets the global + error status if it does not). */ + } else { + IS_DYNAMIC( ptr, isdynamic ); + if ( isdynamic ) { + +/* Check that a negative size has not been given and report an error + if necessary. */ + if ( size < (size_t) 0 ) { + astError( AST__MEMIN, + "Invalid attempt to reallocate a block of memory to %ld bytes.", status, + (long) size ); + +/* If OK, obtain a pointer to the memory header. */ + } else { + mem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY ); + +/* If the new size is zero, free the old memory and set a NULL return + pointer value. */ + if ( size == (size_t) 0 ) { + astFree( ptr ); + result = NULL; + +/* Otherwise, reallocate the memory. */ + } else { + +/* If the cache is being used, for small memory blocks, do the equivalent of + mem = REALLOC( mem, SIZEOF_MEMORY + size ); + + using astMalloc, astFree and memcpy explicitly in order to ensure + that the memory blocks are cached. */ + if( use_cache && ( mem->size <= MXCSIZE || size <= MXCSIZE ) ) { + result = astMalloc( size ); + if( result ) { + if( mem->size < size ) { + memcpy( result, ptr, mem->size ); + } else { + memcpy( result, ptr, size ); + } + astFree( ptr ); + + } else { + result = ptr; + } + +/* For other memory blocks simply use realloc. */ + } else { + +#ifdef MEM_DEBUG + DeIssue( mem, status ); +#endif + + mem = REALLOC( mem, SIZEOF_MEMORY + size ); + +/* If this failed, report an error and return the original pointer + value. */ + if ( !mem ) { +#if HAVE_STRERROR_R + strerror_r( errno, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( errno ); +#endif + astError( AST__NOMEM, "realloc: %s", status, errstat ); + astError( AST__NOMEM, "Failed to reallocate a block of " + "memory to %ld bytes.", status, (long) size ); + +/* If successful, set the new "magic" value and size in the memory + header and obtain a pointer to the start of the region of memory to + be used by the caller. */ + } else { + mem->magic = MAGIC( mem, size ); + mem->size = size; + mem->next = NULL; +#ifdef MEM_DEBUG + mem->id = -1; + mem->prev = NULL; + Issue( mem, status ); +#endif + result = mem; + result = (char *) result + SIZEOF_MEMORY; + } + } + } + } + } + } + +/* Return the result. */ + return result; +} +#undef ERRBUF_LEN + +void astRemoveLeadingBlanks_( char *string, int *status ) { +/* +*++ +* Name: +* astRemoveLeadingBlanks + +* Purpose: +* Remove any leading white space from a string. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void astRemoveLeadingBlanks( char *string ) + +* Description: +* This function moves characters in the supplied string to the left +* in order to remove any leading white space. + +* Parameters: +* string +* Pointer to the string. + +*-- +*/ + +/* Local Variables: */ + char *c, *d; + +/* Check a string has been supplied. */ + if( string ){ + +/* Get a pointer to the first non-white character in the string. */ + c = string; + while( *c && isspace( *c ) ) c++; + +/* Do nothing more if there are no leading spaces. */ + if( c > string ) { + +/* Copy all characters (excluding the trailing null) to the left to + over-write the leading spaces. */ + d = string; + while( *c ) *(d++) = *(c++); + +/* Terminate the returned string. */ + *d = 0; + } + } +} + +size_t astSizeOf_( const void *ptr, int *status ) { +/* +*++ +* Name: +* astSizeOf + +* Purpose: +* Determine the size of a dynamically allocated region of memory. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* size_t astSizeOf( const void *ptr ) + +* Description: +* This function returns the size of a region of dynamically +* allocated memory. + +* Parameters: +* ptr +* Pointer to dynamically allocated memory (or NULL if the size +* of the allocated memory was zero). + +* Returned Value: +* astSizeOf() +* The allocated size. This will be zero if a NULL pointer was +* supplied (no error will result). + +* Notes: +* - A value of zero is returned if this function is invoked with +* the global error status set, or if it fails for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int isdynamic; /* Was the memory allocated dynamically? */ + size_t size; /* Memory size */ + +/* Check the global error status. */ + if ( !astOK ) return (size_t) 0; + +/* Initialise. */ + size = (size_t) 0; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check if a non-NULL valid pointer has been given. If so, extract + the memory size from the header which precedes it. */ + if ( ptr ){ + IS_DYNAMIC( ptr, isdynamic ); + if( isdynamic ) size = ( (Memory *) ( (char *) ptr - SIZEOF_MEMORY ) )->size; + } + +/* Return the result. */ + return size; +} + +static size_t SizeOfMemory( int *status ){ +/* +* Name: +* SizeOfMemory + +* Purpose: +* Returns the size of a Memory structure, padded to an 8 byte +* boundary. + +* Type: +* Private function. + +* Synopsis: +* size_t SizeOfMemory( int *status ) + +* Description: +* This function returns the size of a Memory structure used to +* store header information about any block of memory allocated by this +* module. The returned value may be larger than the actual size of +* the Memory structure in order to ensure that the pointer returned by +* astMalloc etc points to an 8 byte boundary. Failure to do this can +* result in some operating systems having problems. E.g Solaris +* requires this alignment if the returned pointer is going to be used to +* store doubles. + +* Parameters: +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The size to use for a Memory structure. + +* Notes: +* - The returned value is also stored in the module variable +* sizeof_memory. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get the basic size of a Memory structure. */ + sizeof_memory = sizeof( Memory ); + +/* Now increase the returned value to ensure it is a multiple of 8. Mask + off all but the last 3 bits, xor with 0x7 to get the remainder, add 1 + to make it a multiple of 8 bytes. */ + sizeof_memory += ((sizeof_memory & 0x7) ? ((sizeof_memory & 0x7) ^ 0x7) + 1 : 0); + +/* Return the value */ + return sizeof_memory; + +} + +size_t astTSizeOf_( const void *ptr, int *status ) { +/* +*+ +* Name: +* astTSizeOf + +* Purpose: +* Determine the total size of a dynamically allocated region of memory. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* size_t astTSizeOf( const void *ptr ) + +* Description: +* This function returns the size of a region of dynamically +* allocated memory, including the extra memory used to store +* the header information for the memory block (size and magic number). + +* Parameters: +* ptr +* Pointer to dynamically allocated memory (or NULL if the size +* of the allocated memory was zero). + +* Returned Value: +* The allocated size. This will be zero if a NULL pointer was +* supplied (no error will result). + +* Notes: +* - A value of zero is returned if this function is invoked with +* the global error status set, or if it fails for any reason. +* - This function is documented as protected because it should not +* be invoked by external code. However, it is available via the +* external C interface so that it may be used when writing (e.g.) +* foreign language or graphics interfaces. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int isdynamic; /* Was the memory allocated dynamically? */ + size_t size; /* Memory size */ + +/* Check the global error status. */ + if ( !astOK ) return (size_t) 0; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + size = (size_t) 0; + +/* Check if a non-NULL valid pointer has been given. If so, extract + the memory size from the header which precedes it. */ + if ( ptr ){ + IS_DYNAMIC( ptr, isdynamic ); + if( isdynamic ) size = SIZEOF_MEMORY + + ( (Memory *) ( (char *) ptr - SIZEOF_MEMORY ) )->size; + } + +/* Return the result. */ + return size; +} + +void *astStore_( void *ptr, const void *data, size_t size, int *status ) { +/* +*++ +* Name: +* astStore + +* Purpose: +* Store data in dynamically allocated memory. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* void *astStore( void *ptr, const void *data, size_t size ) + +* Description: +* This function stores data in dynamically allocated memory, +* allocating the memory (or adjusting the size of previously +* allocated memory) to match the amount of data to be stored. + +* Parameters: +* ptr +* Pointer to previously allocated memory (or NULL if none has +* yet been allocated). +* data +* Pointer to the start of the data to be stored. This may be +* given as NULL if there are no data, in which case it will be +* ignored and this function behaves like astRealloc, preserving +* the existing memory contents. +* size +* The total size of the data to be stored and/or the size of +* memory to be allocated. This may be zero, in which case the +* data parameter is ignored, any previously-allocated memory is +* freed and a NULL pointer is returned. + +* Returned Value: +* astStore() +* If the data were stored successfully, a pointer to the start of +* the possibly new memory region is returned (this may be the same +* as the original pointer). If size was given as zero, a NULL +* pointer is returned. + +* Notes: +* - This is a convenience function for use when storing data of +* arbitrary size in memory which is to be allocated +* dynamically. It is appropriate when the size of the data will +* not change frequently because the size of the memory region will +* be adjusted to fit the data on every invocation. +* - If this function is invoked with the error status set, or if +* it fails for any reason, the original pointer value is returned +* and the memory contents are unchanged. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int valid; /* Is the memory pointer usable? */ + void *new; /* Pointer to returned memory */ + +/* Check the global error status. */ + if ( !astOK ) return ptr; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = ptr; + +/* If the new size is zero, use astRealloc to free any previously + allocated memory. Also re-allocate the memory if the data pointer + is NULL (in which case we want to preserve its contents). */ + if ( ( size == (size_t) 0 ) || !data ) { + new = astRealloc( ptr, size ); + +/* In other cases, we do not want to preserve any memory + contents. Check if the incoming memory pointer is valid (IsDynamic + sets the global error status if it is not). */ + } else { + if ( !ptr ){ + valid = 1; + } else { + IS_DYNAMIC( ptr, valid ); + } + if( valid ) { + +/* Allocate the new memory. If successful, free the old memory (if + necessary) and copy the data into it. */ + new = astMalloc( size ); + if ( astOK ) { + if ( ptr ) ptr = astFree( ptr ); + (void) memcpy( new, data, size ); + +/* If memory allocation failed, do not free the old memory but return + a pointer to it. */ + } else { + new = ptr; + } + } + } + +/* Return the result. */ + return new; +} + +char *astString_( const char *chars, int nchars, int *status ) { +/* +*++ +* Name: +* astString + +* Purpose: +* Create a C string from an array of characters. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char *astString( const char *chars, int nchars ) + +* Description: +* This function allocates memory to hold a C string and fills the +* string with the sequence of characters supplied. It then +* terminates the string with a null character and returns a +* pointer to its start. The memory used for the string may later +* be de-allocated using astFree. +* +* This function is intended for constructing null terminated C +* strings from arrays of characters which are not null terminated, +* such as when importing a character argument from a Fortran 77 +* program. + +* Parameters: +* chars +* Pointer to the array of characters to be used to fill the string. +* nchars +* The number of characters in the array (zero or more). + +* Returned Value: +* astString() +* If successful, the function returns a pointer to the start of +* the allocated string. If the number of characters is zero, a +* zero-length string is still allocated and a pointer to it is +* returned. + +* Notes: +* - A pointer value of NULL is returned if this function is +* invoked with the global error status set or if it fails for any +* reason. +*-- +*/ + +/* Local Variables: */ + char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the number of characters in the string is valid and + report an error if it is not. */ + if ( nchars < 0 ) { + astError( AST__NCHIN, "astString: Invalid attempt to allocate a string " + "with %d characters.", status, nchars); + +/* Allocate memory to hold the string. */ + } else { + result = (char *) astMalloc( (size_t) ( nchars + 1 ) ); + +/* If successful, copy the characters into the string. */ + if ( astOK && result ) { + (void) memcpy( result, chars, (size_t) nchars ); + +/* Terminate the string. */ + result[ nchars ] = '\0'; + } + } + +/* Return the result. */ + return result; +} + +char **astStringArray_( const char *chars, int nel, int len, int *status ) { +/* +*++ +* Name: +* astStringArray + +* Purpose: +* Create an array of C strings from an array of characters. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char **astStringArray( const char *chars, int nel, int len ) + +* Description: +* This function turns an array of fixed-length character data into +* a dynamicllay allocated array of null-terminated C strings with +* an index array that may be used to access them. +* +* The array of character data supplied is assumed to hold "nel" +* adjacent fixed-length strings (without terminating nulls), each +* of length "len" characters. This function allocates memory and +* creates a null-terminated copy of each of these strings. It also +* creates an array of "nel" pointers which point at the start of +* each of these new strings. A pointer to this index array is +* returned. +* +* The memory used is allocated in a single block and should later +* be de-allocated using astFree. +s +* Parameters: +* chars +* Pointer to the array of input characters. The number of characters +* in this array should be at least equal to (nel * len). +* nel +* The number of fixed-length strings in the input character +* array. This may be zero but should not be negative. +* len +* The number of characters in each fixed-length input +* string. This may be zero but should not be negative. + +* Returned Value: +* astStringArray() +* A pointer to the start of the index array, which contains "nel" +* pointers pointing at the start of each null-terminated output +* string. +* +* The returned pointer should be passed to astFree to de-allocate +* the memory used when it is no longer required. This will free +* both the index array and the memory used by the strings it +* points at. + +* Notes: +* - A NULL pointer will also be returned if the value of "nel" is +* zero, in which case no memory is allocated. +* - A pointer value of NULL will also be returned if this function +* is invoked with the global error status set or if it fails for +* any reason. +*-- +*/ + +/* Local Variables: */ + char **result; /* Result pointer to return */ + char *out_str; /* Pointer to start of next output string */ + const char *in_str; /* Pointer to start of next input string */ + int i; /* Loop counter for array elements */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the array size is valid and report an error if it is + not. */ + if ( nel < 0 ) { + astError( AST__NELIN, + "astStringArray: Invalid attempt to allocate an array of " + "%d strings.", status, nel ); + +/* If the string length will be used, check that it is valid and + report an error if it is not. */ + } else if ( ( nel > 0 ) && ( len < 0 ) ) { + astError( AST__NCHIN, + "astStringArray: Invalid attempt to allocate an " + "array of strings with %d characters in each.", status, len ); + +/* Allocate memory to hold the array of string pointers plus the + string data (with terminating nulls). */ + } else { + result = astMalloc( sizeof( char * ) * (size_t) nel + + (size_t) ( nel * ( len + 1 ) ) ); + +/* If successful, initialise pointers to the start of the current + input and output strings. */ + if( astOK ){ + in_str = chars; + out_str = (char *) ( result + nel ); + +/* Loop to copy each string. */ + for ( i = 0; i < nel; i++ ) { + (void) memcpy( out_str, in_str, (size_t) len ); + +/* Terminate the output string. */ + out_str[ len ] = '\0'; + +/* Store a pointer to the start of the output string in the array of + character pointers. */ + result[ i ] = out_str; + +/* Increment the pointers to the start of the next string. */ + out_str += len + 1; + in_str += len; + } + } + } + +/* Return the result. */ + return result; +} + +char *astStringCase_( const char *string, int upper, int *status ) { +/* +*++ +* Name: +* astStringCase + +* Purpose: +* Convert a string to upper or lower case. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* char *astStringCase( const char string, int upper ) + +* Description: +* This function converts a supplied string to upper or lower case, +* storing the result in dynamically allocated memory. The astChrCase +* function is similar, but stores the result in a supplied buffer. + +* Parameters: +* string +* Pointer to the null terminated string to be converted. +* upper +* If non-zero, the string is converted to upper case. Otherwise it +* is converted to lower case. + +* Returned Value: +* astStringCase() +* If successful, the function returns a pointer to the start of +* the allocated string. The returned memory should be freed using +* astFree when no longer needed. + +* Notes: +* - A pointer value of NULL is returned if this function is +* invoked with the global error status set or if it fails for any +* reason. +*-- +*/ + +/* Local Variables: */ + char *pout; /* Pointer to next output character */ + char *result; /* Pointer value to return */ + const char *pin; /* Pointer to next input character */ + int i; /* Character index */ + int len; /* String length */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the length of the supplied string, excluding the trailing null. */ + len = strlen( string ); + +/* Allocate memory to hold the converted string, plus terminating null. */ + result = (char *) astMalloc( (size_t) ( len + 1 ) ); + +/* If successful, copy the characters into the string, converting each + one to the requested case. */ + if ( result ) { + pin = string; + pout = result; + + if( upper ) { + for( i = 0; i < len; i++ ) { + *(pout++) = toupper( (int) *(pin++) ); + } + + } else { + + for( i = 0; i < len; i++ ) { + *(pout++) = tolower( (int) *(pin++) ); + } + } + +/* Terminate the string. */ + *pout = '\0'; + } + +/* Return the result. */ + return result; +} + +size_t astChrLen_( const char *string, int *status ) { +/* +*++ +* Name: +* astChrLen + +* Purpose: +* Determine the used length of a string. + +* Type: +* Public function. + +* Synopsis: +* #include "memory.h" +* size_t astChrLen( const char *string ) + +* Description: +* This function returns the used length of a string. This excludes any +* trailing white space or non-printable characters (such as the +* trailing null character). + +* Parameters: +* string +* Pointer to the string. + +* Returned Value: +* astChrLen() +* The number of characters in the supplied string, not including the +* trailing newline, and any trailing white-spaces or non-printable +* characters. + +*-- +*/ + +/* Local Variables: */ + const char *c; /* Pointer to the next character to check */ + size_t ret; /* The returned string length */ + +/* Initialise the returned string length. */ + ret = 0; + +/* Check a string has been supplied. */ + if( string ){ + +/* Check each character in turn, starting with the last one. */ + ret = strlen( string ); + c = string + ret - 1; + while( ret ){ + if( isprint( (int) *c ) && !isspace( (int) *c ) ) break; + c--; + ret--; + } + } + +/* Return the answer. */ + return ret; + +} + +int astSscanf_( const char *str, const char *fmt, ...) { +/* +*+ +* Name: +* astSscanf + +* Purpose: +* A wrapper for the ANSI sscanf function. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* int astSscanf( const char *str, const char *fmt, ...) + +* Description: +* This function is a direct plug-in replacement for sscanf. It ensures ANSI +* behaviour is available on all platforms, including those (such as +* MacOS) on which have the native sscanf function exhibits non-ANSI +* behaviour. + +* Parameters: +* str +* Pointer to the string to be scanned. +* fmt +* Pointer to the format string which defines the fields to be +* looked for within "str". +* ... +* Pointers to locations at which to return the value of each +* succesfuly converted field, in the order specified in "fmt". + +* Returned Value: +* The number of fields which were succesfully read from "str". +*- +*/ + +/* Local Variables: */ + char *c; /* Pointer to the next character to check */ + char *newfor; /* Pointer to modified format string */ + const char *d; /* Pointer to the next character to check */ + int *status; /* Pointer to inherited status value */ + int iptr; /* Index into ptr array */ + int lfor; /* No. of characters in format string */ + int lstr; /* No. of characters in scanned string */ + int nc; /* No. of characters read from str */ + int nfld; /* No. of counted field specifiers found so far */ + int nptr; /* Np. of pointers stored */ + int ret; /* The returned number of conversions */ + va_list args; /* Variable argument list pointer */ + void *fptr; /* The next supplied pointer */ + void *ptr[ VMAXFLD ]; /* Array of supplied pointers */ + +/* Initialise the variable argument list pointer. */ + va_start( args, fmt ); + +/* Get a pointer to the integer holding the inherited status value. */ + status = astGetStatusPtr; + +/* Initialise the returned string length. */ + ret = 0; + +/* Check a string and format have been supplied. */ + if( str && fmt ){ + +/* Go through the format string, counting the number of field specifiers which + will return a value, and storing the corresponding points in the ptr + array. */ + nptr = 0; + c = (char *) fmt; + while( *c ) { + +/* Field specifiers are marked by a % sign. */ + if( *c == '%' ) { + +/* Look at the character following the % sign. Quit if the end of the string + has been reached. */ + c++; + if( *c ) { + +/* If the % sign is followed by a "*" or another "%", then there will be no + corresponding pointer in the variable argument list "args". Ignore such + field specifiers. */ + if( *c != '*' && *c != '%' ) { + +/* If possible store the corresponding pointer from the variable argument + list supplied to this function. Report an error if there are too many. */ + if ( nptr < VMAXFLD ) { + ptr[ nptr++ ] = va_arg( args, void *); + +/* If the current field specifier is "%n" the corresponding pointer + should be a pointer to an integer. We initialise the integer to zero. + We need to do this because sscanf does not include "%n" values in the + returned count of succesful conversions, and so there is no sure way + of knowing whether a value has been stored for a "%n" field, and so + whether it is safe to use it as an index into the supplied. */ + if( *c == 'n' ) *( (int *) ptr[ nptr - 1 ] ) = 0; + + } else { + astError( AST__INTER, "astSscanf: Format string " + "'%s' contains more than %d fields " + "(AST internal programming error).", status, + fmt, VMAXFLD ); + break; + } + } + +/* Move on the first character following the field specifier. */ + c++; + } + +/* If this is not the start of a field specifier, pass on. */ + } else { + c++; + } + } + +/* Fill any unused pointers with NULL. */ + for( iptr = nptr; iptr < VMAXFLD; iptr++ ) ptr[iptr] = NULL; + +/* Get the length of the string to be scanned. */ + lstr = strlen( str ); + +/* Get the length of the format string excluding any trailing white space. */ + lfor = astChrLen( fmt ); + +/* Bill Joye reports that MacOS sscanf fails to return the correct number of + characters read (using a %n conversion) if there is a space before the + %n. So check for this. Does the format string contain " %n"? */ + c = strstr( fmt, " %n" ); + if( c && astOK ) { + +/* Take a copy of the supplied format string (excluding any trailing spaces). */ + newfor = (char *) astStore( NULL, (void *) fmt, (size_t) lfor + 1 ); + if( newfor ) { + +/* Ensure the string is terminated (in case the supplied format string + has any trailing spaces). */ + newfor[ lfor ] = 0; + +/* Remove all spaces from before any %n. */ + c = strstr( (const char *) newfor, " %n" ); + while( c ) { + while( *(c++) ) *( c - 1 ) = *c; + c = strstr( newfor, " %n" ); + } + +/* Use the native sscanf with the modified format string. Note, we cannot + use vsscanf because it is not ANSI C. Instead, we list the pointers + explicitly. */ + ret = sscanf( str, newfor, ptr[0], ptr[1], ptr[2], ptr[3], + ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], + ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], + ptr[15], ptr[16], ptr[17], ptr[18], ptr[19] ); + +/* Now look through the original format string for conversions specifiers. + If any %n conversions are found which are preceded by a space, then + correct the returned character counts to include any spaces following the + corresponding point in the scanned string. */ + nfld = 0; + iptr = 0; + c = (char *) fmt; + while( *c ) { + +/* Field specifiers are marked by a % sign. */ + if( *c == '%' ) { + +/* Look at the character following the % sign. Quit if the end of the string + has been reached. */ + c++; + if( *c ) { + +/* If the % sign is followed by a "*" or another "%", then there will be no + corresponding pointer in the variable argument list "args". Ignore such + field specifiers. */ + if( *c != '*' && *c != '%' ) { + +/* Get the supplied pointer corresponding to this field specifier. */ + fptr = ptr[ iptr++ ]; + +/* Increment the number of matched fields required. "%n" specifiers are not + included in the value returned by sscanf so skip over them. */ + if( *c != 'n' ) { + nfld++; + +/* If the % sign is followed by a "n", and was preceded by a space, we + may need to correct the returned character count. */ + } else if( c > fmt + 1 && *(c-2) == ' ' ) { + +/* Do not correct the returned value if sscanf did not get as far as this + field specifier before an error occurred. */ + if( ret >= nfld ) { + +/* Get the original character count produced by sscanf. */ + nc = *( (int *) fptr ); + +/* For each space in "str" which follows, increment the returned count by + one (so long as the original count is not zero or more than the length + of the string - this is not foolproof, but I can't think of a better + check - all uses of %n in AST initialize the supplied count to zero + before calling sscanf so a value fo zero is a safe (ish) bet that the + supplied string doesn't match the supplied format). */ + if( nc > 0 && nc < lstr ) { + d = str + nc; + while( *(d++) == ' ' ) nc++; + *( (int *) fptr ) = nc; + } + } + } + } + +/* Move on the first character following the field specifier. */ + c++; + } + +/* If this is not the start of a field specifier, pass on. */ + } else { + c++; + } + } + +/* Release the temporary copy of the format string. */ + newfor = (char *) astFree( (void *) newfor ); + } + +/* If the format string should not trigger any known problems, use sscanf + directly. */ + } else if( astOK ) { + ret = sscanf( str, fmt, ptr[0], ptr[1], ptr[2], ptr[3], + ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], + ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], + ptr[15], ptr[16], ptr[17], ptr[18], ptr[19] ); + } + } + +/* Tidy up the argument pointer. */ + va_end( args ); + +/* Return the answer. */ + return ret; + +} + + +/* The next functions are used only when memory debugging is + switched on via the MEM_DEBUG macro. They can be used for locating + memory leaks, etc. */ +#ifdef MEM_DEBUG + +void astActiveMemory_( const char *label ) { +/* +*+ +* Name: +* astActiveMemory + +* Purpose: +* Display a list of any currently active AST memory pointers. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astActiveMemory( const char *label ) + +* Description: +* This function displays a list of the identifiers for all currently +* active AST memory chunks. The list is written to standard output +* using "printf", preceded by the supplied text. + +* Parameters: +* label +* A textual label to display before the memody id values (may be +* NULL). + +* Notes: +* - This function attempts to execute even if an error has occurred. +* - Memory blocks which are not usually freed are not reported. Such +* blocks are typically used by AST to hold internal state information. +*- +*/ + + Memory *next; + + if( label ) printf("%s: ", label ); + next = Active_List; + if( next ) { + while( next ) { + if( !next->perm ) { + printf( "%d(%s:%d) ", next->id, next->file, next->line ); + } + next = next->next; + } + } else { + printf("There are currently no active AST memory blocks."); + } + printf("\n"); + +} + +void astWatchMemory_( int id ) { +/* +*+ +* Name: +* astWatchMemory + +* Purpose: +* Indicate uses of the memory block with the specified identifier +* should be reported. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astWatchMemory( int id ) + +* Description: +* This function forces astMemoryAlarm to be invoked when key +* operations are performed on a specified memory block. These key +* operations include; allocation, freeing, copying and cloning of +* Objects, etc. +* +* astMemoryAlarm reports a message when called identifying the memory +* block and the action performed on it. When using a debugger, these +* events can be trapped and investigated by setting a debugger +* breakpoint in astMemoryAlarm_. + +* Parameters: +* id +* The identifier of the memory block which is to be watched. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*- +*/ + Watched_ID = id; +} + +int astMemoryId_( const void *ptr, int *status ){ +/* +*+ +* Name: +* astMemoryId + +* Purpose: +* Return the integer identifier for a memory block. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* int astMemoryId( const void *ptr ) + +* Description: +* This function returns the integer identifier associated with a +* memory block allocated by function sin this module. + +* Parameters: +* ptr +* The pointer (a genuine C pointer, not an encoded object +* identifier). + +* Returned Value: +* The integer identifier. A value of -1 is returned if "ptr" is NULL. + +*- +*/ + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + return ptr ? ((Memory *)(ptr-SIZEOF_MEMORY))->id : -1; +} + +void *astMemoryPtr_( int id ){ +/* +*+ +* Name: +* astMemoryPtr + +* Purpose: +* Return a pointer to the memory block with a given identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* void *astMemoryPtr( int id ) + +* Description: +* This function returns a pointer to the memory block with a given +* identifier. NULL is returned if the given identifier is not active. + +* Parameters: +* id +* The identifier for an active memory block. + +* Returned Value: +* The pointer to the memory block. NULL is returned if no active memory +* with the given ID can be found. Note, this is always a genuine C +* pointer (even for public Object pointers). + +*- +*/ + Memory *next; + void *ret; + + ret = NULL; + next = Active_List; + while( next ) { + if( next->id == id ) { + ret = next + 1; + break; + } + } + + return ret; +} + +void astMemoryAlarm_( const char *verb ){ +/* +*+ +* Name: +* astMemoryAlarm + +* Purpose: +* Called when a watched memory ID is used. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* void astMemoryAlarm( const char *verb ) + +* Description: +* This function is called when a watched memory ID is used. See +* astWatchMemory. + +* Parameters: +* verb +* Text to include in message. +*- +*/ + + printf( "astMemoryAlarm: Memory id %d has been %s.\n", Watched_ID, verb ); +} + +void astMemoryStats_( int reset, size_t *peak, size_t *current, int *status ) { +/* +*+ +* Name: +* astMemoryStats + +* Purpose: +* Return the current and peak AST memory usage. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astMemoryStats( int reset, size_t *peak, size_t *current ) + +* Description: +* This function returns the current amount of memory allocated +* using AST memory management functions, and the peak amount of +* simultaneously allocated memory since the last time the peak value +* was reset. + +* Parameters: +* reset +* If non-zero, the peak value is reset to the current usage +* upon return. +* peak +* Address at which to return the peak memory usage since the last +* reset, in bytes. +* current +* Address at which to return the current memory usage, in bytes. + +*- +*/ + + LOCK_DEBUG_MUTEX; + + if( peak ) *peak = Peak_Usage; + if( current ) *current = Current_Usage; + if( reset ) Peak_Usage = Current_Usage; + + UNLOCK_DEBUG_MUTEX; +} + +void astMemoryWarning_( size_t threshold, int *status ) { +/* +*+ +* Name: +* astMemoryWarning + +* Purpose: +* Issues a warning memory goes over a specified threshold. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astMemoryWarning( size_t threshold ) + +* Description: +* This function prints a warning message to standard output if the +* AST memory usage exceeds the specified threshold. + +* Parameters: +* threshold +* The memory allocation, in bytes, at which a warning should be issued, +* Supply zero to suppress warnings. + +* Notes: +* - This function is used to reset the threshold to zero when the first +* warning is issued in order to prevent a flood of warnings appearing. +* Therefore, setting a debugger breakpoint in this function +* ("astMemoryWarning_" - do not forget the trailing underscore) +* allows you to locate the point at which memory allocation first +* exceeds the threshold. + +*- +*/ + + LOCK_DEBUG_MUTEX; + + Warn_Usage = threshold; + + UNLOCK_DEBUG_MUTEX; +} + +void astMemoryUse_( const void *ptr, const char *verb, int *status ){ +/* +*+ +* Name: +* astMemoryUse + +* Purpose: +* Called to report the use of a memory block pointer. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* void astMemoryUse( void *ptr, const char *verb ) + +* Description: +* If the supplied memory block is being watched, astMemoryAlarm is +* called to report the use of the pointer. The reported text includes +* the supplied "verb". A memory block can be watched by calling +* astWatchMemory. + +* Parameters: +* ptr +* A pointer to the memory block being used. The pointer must have +* been returned by one of the AST memory management functions (e.g. +* astMalloc, astRealloc, etc). +* verb +* A verb indicating what is being done to the pointer. +*- +*/ + + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + + if( ptr && astMemoryId( ptr ) == Watched_ID ) { + if( !Quiet_Use || !strcmp( verb, ISSUED ) || + !strcmp( verb, FREED ) ) { + astMemoryAlarm( verb ); + } + } +} + +int astMemoryTune_( const char *name, int value, int *status ){ +/* +*+ +* Name: +* astMemoryTune + +* Purpose: +* Set a tuning parameter for the memory debugging functions. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* int astMemoryTune( const char *name, int value ) + +* Description: +* There are a few tuning parameters which control the behaviour of +* the memory debugging functions. This function allows these tuning +* parameters to be queried or set. + +* Parameters: +* name +* The name of the tuning parameter to query or set. Valid names are: +* +* "Keep_ID": A boolean flag indicating if a new ID should be issued +* for a cached memory block each time it is returned by astMalloc? +* Otherwise, the same ID value is used throughtout the life of a +* memory block. Default is zero (false). +* +* "List_Cache": A boolean flag which if non-zero (true) causes the +* ID of every memory block in the cache to be reported when the +* cache is emptied by astFlushMemory. +* +* "Quiet_Use": A boolean flag controlling the number of reports issued +* when a memory block is being watched (see astWatchMemory). If +* non-zero (true), then the only events which are reported are the +* issuing of a memory block pointer by astMalloc or astRealloc,and +* the freeing (or caching) of a memory block by astFree. If Quiet_Use +* is zero (the default), then additional reports are made for +* memory blocks used to hold AST Objects whenever the Object is +* copied, cloned, or checked. +* value +* The new value for the tuning parameter. If AST__TUNULL is +* supplied, the original value is left unchanged. + +* Returned Value: +* The original value of the tuning parameter. + +*- +*/ + + int result = AST__TUNULL; + + if( name ) { + + if( astChrMatch( name, "Keep_ID" ) ) { + result = Keep_ID; + if( value != AST__TUNULL ) Keep_ID = value; + + } else if( astChrMatch( name, "Quiet_Use" ) ) { + result = Quiet_Use; + if( value != AST__TUNULL ) Quiet_Use = value; + + } else if( astChrMatch( name, "List_Cache" ) ) { + result = List_Cache; + if( value != AST__TUNULL ) List_Cache = value; + + } else if( astOK ) { + astError( AST__TUNAM, "astMemoryTune: Unknown AST memory tuning " + "parameter specified \"%s\".", status, name ); + } + } + + return result; +} + +void astBeginPM_( int *status ) { +/* +*+ +* Name: +* astBeginPM + +* Purpose: +* Start a block of permanent memory allocations. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astBeginPM + +* Description: +* This function indicates that all memory allocations made by calls +* to other functions in this module (e.g. astMalloc), up to the +* astEndPM call which matches the astBeginPM call, will not usually +* be freed explicitly. Matching astBeginPM/astEndPM calls should be +* used to enclose all code which allocates memory which is never +* freed explitly by AST. Such memory allocations may be freed if +* required, using the astFlushMemory function (but note this should +* only be done once all use of AST by an application has finished). +* +* Matching pairs of astBeginPM/astEndPM calls can be nested up to a +* maximum depth of 20. + +*- +*/ + + LOCK_DEBUG_MUTEX; + +/* The global Perm_Mem flag indicates whether or not subsequent memory + management functions in this module should store pointers to allocated + blocks in the PM_List array. Push the current value of this flag + onto a stack, and set the value to 1. */ + if( PM_Stack_Size >= PM_STACK_MAXSIZE ){ + if( astOK ) { + astError( AST__INTER, "astBeginPM: Maximum stack size has been " + "exceeded (internal AST programming error)." , status); + } + + } else { + PM_Stack[ PM_Stack_Size++ ] = Perm_Mem; + Perm_Mem = 1; + } + UNLOCK_DEBUG_MUTEX; +} + +void astEndPM_( int *status ) { +/* +*+ +* Name: +* astEndPM + +* Purpose: +* End a block of permanent memory allocations. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astEndPM + +* Description: +* This function indicates the end of the block of permanent memory +* allocations started by the matching call to astBeginPM. See +* astBeginPM for further details. + +*- +*/ + + LOCK_DEBUG_MUTEX; + +/* The global Perm_Mem flag indicates whether or not subsequent memory + management functions in this module should store pointers to allocated + blocks in the PM_List array. Pop the value from the top of this stack. */ + if( PM_Stack_Size == 0 ){ + if( astOK ) { + astError( AST__INTER, "astEndPM: astEndPM called without " + "matching astBeginPM (internal AST programming error)." , status); + } + + } else { + Perm_Mem = PM_Stack[ --PM_Stack_Size ]; + } + + UNLOCK_DEBUG_MUTEX; +} + +void astFlushMemory_( int leak, int *status ) { +/* +*+ +* Name: +* astFlushMemory + +* Purpose: +* Free all permanent and cached memory blocks. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astFlushMemory( int leak ); + +* Description: +* This function should only be called once all use of AST by an +* application has finished. It frees any allocated but currently +* unused memory stored in an internal cache of unused memory +* pointers. (Note, it does not free any memory used permanently to +* store internal AST state information). +* +* It is not normally necessary to call this function since the memory +* will be freed anyway by the operating system when the application +* terminates. However, it can be called if required in order to +* stop memory management tools such as valgrind from reporting that +* the memory has not been freed at the end of an application. +* +* In addition, if "leak" is non-zero this function will also report +* an error if any active AST memory pointers remain which have not +* been freed (other than pointers for the cached and permanent +* memory described above). Leakage of active memory blocks can be +* investigated using astActiveMemory and astWatchMemory. + +* Parameters: +* leak +* Should an error be reported if any non-permanent memory blocks +* are found to be active? + +*- +*/ + +/* Local Variables: */ + Memory *next; + int nact; + int istat; + +/* Empty the cache. */ + astMemCaching( astMemCaching( AST__TUNULL ) ); + +/* Free and count all non-permanent memory blocks. */ + nact = 0; + next = Active_List; + while( Active_List ) { + next = Active_List->next; + if( !Active_List->perm ) { + nact++; + FREE( Active_List ); + } + Active_List = next; + } + +/* Report an error if any active pointers remained. if an error has + already occurred, use the existing status value. */ + if( nact && leak ){ + + if( astOK ) { + istat = AST__INTER; + } else { + istat = astStatus; + } + astError( istat, "astFlushMemory: %d AST memory blocks have not " + "been released (programming error).", status, nact ); + + } else { + printf("astFlushMemory: All AST memory blocks were released correctly.\n" ); + } +} + +void astCheckMemory_( int *status ) { +/* +*+ +* Name: +* astCheckMemory + +* Purpose: +* Check that all AST memory blocks have been released. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* astCheckMemory + +* Description: +* This macro reports an error if any active AST memory pointers +* remain which have not been freed (other than pointers for cached +* and "permanently allocated" memory). Leakage of active memory blocks +* can be investigated using astActiveMemory and astWatchMemory. +*- +*/ + +/* Local Variables: */ + Memory *next; + int nact; + int istat; + +/* Empty the cache. */ + astMemCaching( astMemCaching( AST__TUNULL ) ); + +/* Count all non-permanent memory blocks. */ + nact = 0; + next = Active_List; + while( Active_List ) { + next = Active_List->next; + if( !Active_List->perm ) nact++; + Active_List = next; + } + +/* Report an error if any active pointers remained. If an error has + already occurred, use the existing status value. */ + if( nact ){ + + if( astOK ) { + istat = AST__INTER; + } else { + istat = astStatus; + } + astError( istat, "astCheckMemory: %d AST memory blocks have not " + "been released (programming error).", status, nact ); + + } else { + printf("astCheckMemory: All AST memory blocks were released correctly.\n" ); + } +} + +static void Issue( Memory *mem, int *status ) { +/* +* Name: +* Issue + +* Purpose: +* Indicate that a pointer to a memory block has been issued. + +* Type: +* Private function. + +* Synopsis: +* #include "memory.h" +* void Issue( Memory *mem, int *status ); + +* Description: +* Initialises the extra debug items in the Memory header, and adds the +* Memory structure to the list of active memory blocks. + +* Parameters: +* mem +* Pointer to the Memory structure. +* status +* Pointer to the inherited status value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + +/* Return if no pointer was supplied. */ + if( !mem ) return; + + LOCK_DEBUG_MUTEX; + astGET_GLOBALS(NULL); + +/* Store a unique identifier for this pointer. Unless global Keep_ID is + non-zero, a new identifier is used each time the pointer becomes active + (i.e. each time it is remove from the cache or malloced). */ + if( !Keep_ID || mem->id < 0 ) mem->id = ++Next_ID; + +/* Record the file name and line number where it was issued. */ + if( AST__GLOBALS && AST__GLOBALS->Error.Current_File ) { + strncpy( mem->file, AST__GLOBALS->Error.Current_File, sizeof(mem->file) ); + mem->file[ sizeof(mem->file) - 1 ] = 0; + mem->line = AST__GLOBALS->Error.Current_Line; + } else { + mem->file[ 0 ] = 0; + mem->line = 0; + } + +/* Indicate if this is a permanent memory block (i.e. it will usually not + be freed by AST). */ + mem->perm = Perm_Mem; + +/* Add it to the double linked list of active pointers. */ + mem->next = Active_List; + mem->prev = NULL; + if( Active_List ) Active_List->prev = mem; + Active_List = mem; + +/* Report that the pointer is being issued. */ + astMemoryUse( (void *) mem + SIZEOF_MEMORY, ISSUED ); + +/* Update the current and peak memory usage. */ + Current_Usage += mem->size + SIZEOF_MEMORY; + if( Current_Usage > Peak_Usage ) Peak_Usage = Current_Usage; + +/* If the current allocation is above the threshold set using + astMemoryWarning, issue a warning message, and then reset the threshold + to zero to prevent further warnings being issued, and to allow a + debugger breakpoint to be set. */ + if( Current_Usage > Warn_Usage && + Warn_Usage > 0 ) { + printf( "Warning - AST memory allocation has exceeded %ld bytes - " + "dumping catalogue of active memory blocks to file 'memory.dump'\n", + Warn_Usage ); + +/* Create a file holding the details of all currently active memory blocks. It can be + examined using topcat. */ + FILE *fd = fopen( "memory.dump", "w" ); + if( fd ) { + Memory *next; + + fprintf( fd, "# id size perm file line\n"); + next = Active_List; + if( next ) { + while( next ) { + if( !next->perm ) { + fprintf( fd, "%d %zu %d %s %d\n", next->id, next->size, + next->perm, next->file, next->line ); + } + next = next->next; + } + } + + fclose(fd ); + } + + Warn_Usage = 0; + } + + UNLOCK_DEBUG_MUTEX; +} + +static void DeIssue( Memory *mem, int *status ) { +/* +* Name: +* DeIssue + +* Purpose: +* Indicate that a pointer to a memory block has been freed. + +* Type: +* Private function. + +* Synopsis: +* #include "memory.h" +* void DeIssue( Memeory *mem, int *status ); + +* Description: +* Initialises the extra debug items in the Memory header, and adds the +* Memory structure to the list of active memory blocks. + +* Parameters: +* mem +* Pointer to the Memory structure. +* status +* Pointer to the inherited status value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + Memory *next; + Memory *prev; + +/* Return if no pointer was supplied. */ + if( !mem ) return; + + LOCK_DEBUG_MUTEX; + astGET_GLOBALS(NULL); + +/* Report that the pointer is being freed. */ + astMemoryUse( (void *) mem + SIZEOF_MEMORY, FREED ); + +/* Remove the block from the double linked list of active pointers. */ + next = mem->next; + prev = mem->prev; + if( prev ) prev->next = next; + if( next ) next->prev = prev; + if( mem == Active_List ) Active_List = next; + mem->next = NULL; + mem->prev = NULL; + +/* Update the current memory usage. */ + Current_Usage -= mem->size + SIZEOF_MEMORY; + + UNLOCK_DEBUG_MUTEX; +} + + +#endif + + + + + + +/* The next functions are used only when profiling AST application. */ +#ifdef MEM_PROFILE + + +void astStartTimer_( const char *file, int line, const char *name, int *status ) { +/* +*+ +* Name: +* astStartTimer + +* Purpose: +* Measure the time spent until the corresponding call to astStopTimer. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* void astStartTimer( const char *name ); + +* Description: +* This function looks for a timer with the specified name within the +* current parent timer. If no timer with the given name is found, a +* new timer is created and initialised to zero. The current absolute +* time (elapsed, user and system) is recorded in the timer. The new +* timer then becomes the current timer. + +* Parameters: +* name +* A label for the timer. This should be unique within the +* enclosing parent timer. + +* Notes: +* - This function should only be used in a single-threaded environment. +* - This function returns without action if timers are currently +* disabled (see astEnableTimers). + +*- +*/ + +/* Local Variables: */ + int n, found, i; + AstTimer *t; + struct tms buf; + +/* Check inherited status. Also return if timers are currently disabled. */ + if( !Enable_Timers || *status != 0 ) return; + +/* See if a timer with the given name exists in the list of child timers + within the current timer. */ + found = 0; + if( Current_Timer ) { + for( i = 0; i < Current_Timer->nchild; i++ ) { + t = Current_Timer->children[ i ]; + if( !strcmp( t->name, name ) ) { + found = 1; + break; + } + } + } + +/* If not, create and initialise one now, and add it into the list of + children within the current timer. */ + if( !found ) { + t = astMalloc( sizeof( AstTimer ) ); + t->id = Timer_Count++; + t->et = 0; + t->ut = 0; + t->st = 0; + t->nentry = 0; + t->name = name; + t->file = file; + t->line = line; + t->parent = Current_Timer; + t->nchild = 0; + t->children = NULL; + + if( Current_Timer ) { + n = (Current_Timer->nchild)++; + Current_Timer->children = astGrow( Current_Timer->children, + sizeof( AstTimer *), + Current_Timer->nchild ); + Current_Timer->children[ n ] = t; + } + } + +/* Record the current absolute times (elapsed, user and system) within + the new timer. */ + t->e0 = times(&buf); + t->u0 = buf.tms_utime; + t->s0 = buf.tms_stime; + +/* Increment the number of entries into the timer. */ + (t->nentry)++; + +/* Use the new timer as the current timer until the corresponding call to + astStopTimer. */ + Current_Timer = t; +} + +void astEnableTimers_( int enable, int *status ) { +/* +*+ +* Name: +* astEnableTimers + +* Purpose: +* Set a global flag indicating if the use of AST timers is enabled. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* void astStartTimer( int enable ); + +* Description: +* This function sets a global flag that enables otr disables the user +* of AST Timers. If timers are disabled, the astStartTimer and +* astStopTimer functions will return without action. + +* Parameters: +* enable +* If non-zero, timers will be used. + +* Notes: +* - This function should only be used in a single-threaded environment. + +*- +*/ + Enable_Timers = enable; +} + +void astStopTimer_( int *status ) { +/* +*+ +* Name: +* astStopTimer + +* Purpose: +* Record the time spent since the corresponding call to astStartTimer. + +* Type: +* Protected function. + +* Synopsis: +* #include "memory.h" +* void astStopTimer; + +* Description: +* This function obtains the time increments since the corresponding +* call to astStartTimer, and adds these increments onto the total +* times stored in the current timer. It then changes the current +* timer to be the parent timer associated the current timer on entry. +* +* If the current timer on entry has no parent (i.e. is a top level +* timer), the times spent in the top-level timer, and all its +* descendent timers, are displayed. + +* Notes: +* - This function should only be used in a single-threaded environment. +* - This function returns without action if timers are currently +* disabled (see astEnableTimers). + +*- +*/ + +/* Local Variables: */ + AstTimer *flat; + AstTimer *t; + int i; + int nflat; + struct tms buf; + +/* Check inherited status. Also return if timers are currently disabled. */ + if( !Enable_Timers || !Current_Timer || *status != 0 ) return; + +/* Get the current absolute times, and thus find the elapsed times since the + corresponding call to astStartTimer. Use these elapsed times to increment + the total times spent in the timer. */ + Current_Timer->et += ( times(&buf) - Current_Timer->e0 ); + Current_Timer->st += ( buf.tms_stime - Current_Timer->s0 ); + Current_Timer->ut += ( buf.tms_utime - Current_Timer->u0 ); + +/* If this is a top level timer, display the times spent in the current + timer, and in all its descendent timers. This also frees the memory + used by the timers. */ + if( !Current_Timer->parent ) { + flat = NULL; + nflat = 0; + Current_Timer = ReportTimer( Current_Timer, 0, &flat, &nflat, status ); + +/* Sort and display the flat list of timers, then free the memory used by + the flat list. */ + qsort( flat, nflat, sizeof( AstTimer), CompareTimers2 ); + printf("\n\n"); + t = flat; + for( i = 0; i < nflat; i++,t++ ) { + printf( "%s (%s:%d): ", t->name, t->file, t->line ); + printf( "elapsed=%ld ", (long int) t->et ); +/* + printf( "system=%ld ", (long int) t->st ); + printf( "user=%ld ", (long int) t->ut ); +*/ + printf( "calls=%d ", t->nentry ); + printf("\n"); + } + flat = astFree( flat ); + +/* If this is not a top level timer, restore the parent timer as the + curent timer. */ + } else { + Current_Timer = Current_Timer->parent; + } +} + +static AstTimer *ReportTimer( AstTimer *t, int ind, AstTimer **flat, + int *nflat, int *status ) { +/* +* Name: +* ReportTimer + +* Purpose: +* Free and report the times spent in a given timer, and all descendents. + +* Type: +* Private function. + +* Synopsis: +* #include "memory.h" +* AstTimer *ReportTimer( AstTimer *t, int ind, AstTimer **flat, +* int *nflat, int *status ) + +* Description: +* This routines reports to standard output the times spent in the +* supplied timer. It then calls itself recursively to report the times +* spent in each of the child timers of the supplied timer. +* +* It also frees the memory used to hold the supplied timer. + +* Parameters: +* t +* Pointer to the AstTimer structure. +* ind +* The number of spaces of indentation to display before the timer +* details. +* flat +* Address of a pointer to the start of an array of AstTimers. The +* number of elements in this array is given by "*nflat". Each +* Timer in this array holds the accumulated total for all entries +* into a given timer, from all parent contexts. +* nflat +* Address of an int holding the current length of the "*flat" array. +* status +* Pointer to the inherited status value. +*/ + +/* Local Variables: */ + int found; + int i; + AstTimer *ft; + AstTimer *parent; + +/* Check inherited status */ + if( *status != 0 ) return NULL; + +/* Display a single line of text containing the times stored in the supplied + timer, preceded by the requested number of spaces. */ + for( i = 0; i < ind; i++ ) printf(" "); + printf( "%s (%s:%d): ", t->name, t->file, t->line ); + + printf( "id=%d ", t->id ); + printf( "elapsed=%ld ", (long int) t->et ); +/* + printf( "system=%ld ", (long int) t->st ); + printf( "user=%ld ", (long int) t->ut ); +*/ + printf( "calls=%d ", t->nentry ); + +/* If there are any children, end the line with an opening bvrace. */ + if( t->nchild ) printf("{"); + printf("\n"); + +/* If there is more than one child, sort them into descending order of + elapsed time usage. */ + if( t->nchild > 1 ) qsort( t->children, t->nchild, sizeof( AstTimer * ), + CompareTimers ); + +/* Increment the indentation and call this function recursively to + display and free each child timer. */ + ind += 3; + for( i = 0; i < t->nchild; i++ ) { + (t->children)[ i ] = ReportTimer( (t->children)[ i ], ind, flat, + nflat, status ); + } + +/* Delimit the children by displaying a closing brace. */ + if( t->nchild ) { + for( i = 0; i < ind - 3; i++ ) printf(" "); + printf("}\n"); + } + +/* See if this timer is contained within itself at a higher level. */ + parent = t->parent; + while( parent && ( parent->line != t->line || + strcmp( parent->file, t->file ) ) ) { + parent = parent->parent; + } + +/* If not, search for a timer in the "flat" array of timers that has the same + source file and line number. */ + if( !parent ) { + found = 0; + ft = *flat; + for( i = 0; i < *nflat; i++, ft++ ) { + if( ft->line == t->line && + !strcmp( ft->file, t->file ) ) { + found = 1; + break; + } + } + +/* If not found, add a new timer to the end of the "flat" array and + initialise it. */ + if( !found ) { + i = (*nflat)++; + *flat = astGrow( *flat, *nflat, sizeof( AstTimer ) ); + ft = (*flat) + i; + ft->id = 0; + ft->et = t->et; + ft->ut = t->ut; + ft->st = t->st; + ft->nentry = t->nentry; + ft->name = t->name; + ft->file = t->file; + ft->line = t->line; + ft->parent = NULL; + ft->nchild = 0; + ft->children = NULL; + + +/* If found, increment the properites to include the supplied timer. */ + } else { + ft->et += t->et; + ft->ut += t->ut; + ft->st += t->st; + ft->nentry += t->nentry; + } + } + +/* Free the memory used by the supplied timer. */ + t->children = astFree( t->children ); + return astFree( t ); +} + + +static int CompareTimers( const void *a, const void *b ){ + return ((*((AstTimer **) b ))->et) - ((*((AstTimer **) a ))->et); +} + +static int CompareTimers2( const void *a, const void *b ){ + return (((AstTimer *) b )->et) - (((AstTimer *) a )->et); +} + +#endif diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..7a631fb --- /dev/null +++ b/memory.h @@ -0,0 +1,347 @@ +#if !defined( MEMORY_INCLUDED ) /* Include this file only once */ +#define MEMORY_INCLUDED +/* +*+ +* Name: +* memory.h + +* Purpose: +* Define the interface to the Memory module. + +* Description: +* This module defines functions which wrap up and extend the +* standard C functions for performing memory allocation. They +* provide better security against memory leaks, etc., and should +* not be inter-mixed with the standard C functions. +* +* Note that this module is not a class implementation, although it +* resembles one. + +* Functions Defined: +* Public: +* None. +* +* Protected: +* astAppendString +* Append a string to another string which grows dynamically. +* astCalloc +* Allocate memory. +* astChrMatch +* Case-insensitive string comparison. +* astChrMatchN +* Case-insensitive string comparison of an most N characters. +* astFree +* Free previously allocated memory. +* astGrow +* Allocate memory for an adjustable array. +* astMalloc +* Allocate memory. +* astRealloc +* Change the size of a dynamically allocated region of memory. +* astSizeOf +* Determine the size of a dynamically allocated region of memory. +* astStore +* Store data in dynamically allocated memory. +* astString +* Create a C string from an array of characters. +* astStringArray +* Create an array of C strings from an array of characters. +* astStringCase +* Convert a string to upper or lower case. +* astChrLen +* Returns length of a string without trailing white space, etc. +* astChrTrunc +* Terminate a string to exclude trailing spaces. +* astSscanf +* Like sscanf, but fixes certain platform-specific bugs in the +* native sscanf implementation. +* astTSizeOf +* Determine the total size of a dynamically allocated region of memory. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: D.S. Berry (Starlink) + +* History: +* 8-JAN-1996 (RFWS): +* Original version. +* 26-JAN-1996 (RFWS) +* Added function interfaces. +* 20-JUN-1996 (RFWS): +* Added astString. +* 15-JUL-1996 (RFWS): +* Use improved prologue style, etc. and make all functions protected. +* 11-SEP-1996 (RFWS): +* Added astStringArray (original written by DSB). +* 18-MAR-1998 (RFWS): +* Make interface available for writing foreign language and +* graphics interfaces, etc. +* 18-MAR-1998 (RFWS): +* Added explicit arguments to function macros. +* 29-JAN-2002 (DSB): +* Added astChrLen and astSscanf. +* 15-NOV-2002 (DSB): +* Added astChrMatch astChrMatchN. +* 23-FEB-2006 (DSB): +* Added astMemCaching and AST__TUNULL. +* 2-MAR-2006 (DSB): +* Only use astSscanf if the system on which AST was configured +* showed the non-ANSI behaviour reported by Bill Joye. +* 27-JUN-2007 (DSB): +* Added astIsDynamic. +* 25-OCT-2007 (DSB): +* Added astRemoveLeadingBlanks. +* 22-FEB-2008 (DSB): +* Added astChrSub. +* 19-MAY-2010 (DSB): +* Added astStringCase. +* 26-MAR-2015 (DSB): +* Added astChrTrunc. + +*- +*/ + +/* Include files. */ +/* ============== */ +/* Configuration results. */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* C header files. */ +/* --------------- */ +#include +#include "error.h" + +/* Macros. */ +/* ======= */ + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST__TUNULL -99999 +#define AST__TUNULLC "" + +/* Type definitions */ +/* ================ */ + +#if defined(astCLASS) + +/* Header for allocated memory. */ +/* ---------------------------- */ +/* This stores a "magic" value so that dynamically allocated memory + can be recognised, together with the allocated size. It also + ensures correct alignment. */ +typedef struct Memory { + struct Memory *next; + unsigned long magic; + size_t size; + +#ifdef MEM_DEBUG + struct Memory *prev; /* Pointer to the previous linked Memory structure */ + int id; /* A unique identifier for every allocated memory chunk */ + int perm; /* Is this chunk part of an acceptable once-off "memory leak"? */ + int line; /* Line number in "file" (below). */ + char file[50];/* The source file that made the top-level call to AST */ +#endif + +} Memory; + +/* Define the largest size of a cached memory block in bytes. This does + not include the size of the Memory header. This does not need to be + too big because the vast majority of memory blocks allocated by AST are + less than a few hundred bytes. */ +#define MXCSIZE 300 + +#endif + + +#if defined(THREAD_SAFE) && defined(astCLASS) + +/* Define a structure holding all data items that are global within the + memory.c file. */ +typedef struct AstMemoryGlobals { + size_t Sizeof_Memory; + int Cache_Init; + int Use_Cache; + Memory *Cache[ MXCSIZE + 1 ]; + +} AstMemoryGlobals; + +#endif + +/* Function prototypes. */ +/* ==================== */ + +#if defined(THREAD_SAFE) && defined(astCLASS) +void astInitMemoryGlobals_( AstMemoryGlobals * ); +#endif + +#if defined(astCLASS) || 1 /* Nominally protected, but available for */ + /* use in developing (e.g.) foreign */ + /* language or graphics interfaces. */ +int astMemCaching_( int, int * ); +void astChrCase_( const char *, char *, int, int, int * ); +char **astChrSplit_( const char *, int *, int * ); +char **astChrSplitRE_( const char *, const char *, int *, const char **, int * ); +char **astChrSplitC_( const char *, char, int *, int * ); +int astChrMatch_( const char *, const char *, int * ); +int astChrMatchN_( const char *, const char *, size_t, int * ); +char **astStringArray_( const char *, int, int, int * ); +char *astStringCase_( const char *, int, int * ); +char *astString_( const char *, int, int * ); +int astSscanf_( const char *str, const char *format, ...); +size_t astSizeOf_( const void *, int * ); +int astIsDynamic_( const void *, int * ); +size_t astTSizeOf_( const void *, int * ); +void *astFree_( void *, int * ); +void *astFreeDouble_( void *, int * ); +void *astGrow_( void *, int, size_t, int * ); +void *astCalloc_( size_t, size_t, int * ); +void *astMalloc_( size_t, int, int * ); +void *astRealloc_( void *, size_t, int * ); +void *astStore_( void *, const void *, size_t, int * ); +size_t astChrLen_( const char *, int * ); +double astChr2Double_( const char *, int * ); +void astRemoveLeadingBlanks_( char *, int * ); +char *astAppendString_( char *, int *, const char *, int * ); +char *astAppendStringf_( char *, int *, const char *, ... )__attribute__((format(printf,3,4))); +char *astChrSub_( const char *, const char *, const char *[], int, int * ); +void astChrTrunc_( char *, int * ); + +#ifdef MEM_PROFILE +void astStartTimer_( const char *, int, const char *, int * ); +void astStopTimer_( int * ); +void astEnableTimers_( int, int * ); +#endif + + +#ifdef MEM_DEBUG +void astActiveMemory_( const char * ); +void astWatchMemory_( int ); +void astCheckMemory_( int * ); +void astFlushMemory_( int, int * ); +int astMemoryTune_( const char *, int, int * ); +void *astMemoryPtr_( int ); +void astMemoryAlarm_( const char * ); +void astMemoryStats_( int , size_t *, size_t *, int * ); +void astMemoryWarning_( size_t, int * ); +void astMemoryUse_( const void *, const char *, int * ); +int astMemoryId_( const void *, int * ); +void astBeginPM_( int * ); +void astEndPM_( int * ); +#endif + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These wrap up the functions defined by this module. */ + +#define astCalloc(nmemb,size) astERROR_INVOKE(astCalloc_(nmemb,size,STATUS_PTR)) +#define astChrMatch(str1,str2) astERROR_INVOKE(astChrMatch_(str1,str2,STATUS_PTR)) +#define astChrMatchN(str1,str2,n) astERROR_INVOKE(astChrMatchN_(str1,str2,n,STATUS_PTR)) +#define astFree(ptr) astERROR_INVOKE(astFree_(ptr,STATUS_PTR)) +#define astFreeDouble(ptr) astERROR_INVOKE(astFreeDouble_(ptr,STATUS_PTR)) +#define astGrow(ptr,n,size) astERROR_INVOKE(astGrow_(ptr,n,size,STATUS_PTR)) +#define astMalloc(size) astERROR_INVOKE(astMalloc_(size,0,STATUS_PTR)) +#define astMemCaching(flag) astERROR_INVOKE(astMemCaching_(flag,STATUS_PTR)) +#define astRealloc(ptr,size) astERROR_INVOKE(astRealloc_(ptr,size,STATUS_PTR)) +#define astSizeOf(ptr) astERROR_INVOKE(astSizeOf_(ptr,STATUS_PTR)) +#define astIsDynamic(ptr) astERROR_INVOKE(astIsDynamic_(ptr,STATUS_PTR)) +#define astTSizeOf(ptr) astERROR_INVOKE(astTSizeOf_(ptr,STATUS_PTR)) +#define astStore(ptr,data,size) astERROR_INVOKE(astStore_(ptr,data,size,STATUS_PTR)) +#define astAppendString(str1,nc,str2) astERROR_INVOKE(astAppendString_(str1,nc,str2,STATUS_PTR)) +#define astAppendStringf astAppendStringf_ +#define astString(chars,nchars) astERROR_INVOKE(astString_(chars,nchars,STATUS_PTR)) +#define astStringArray(chars,nel,len) astERROR_INVOKE(astStringArray_(chars,nel,len,STATUS_PTR)) +#define astStringCase(string,toupper) astERROR_INVOKE(astStringCase_(string,toupper,STATUS_PTR)) +#define astChrLen(string) astERROR_INVOKE(astChrLen_(string,STATUS_PTR)) +#define astChrTrunc(string) astERROR_INVOKE(astChrTrunc_(string,STATUS_PTR)) +#define astChr2Double(string) astERROR_INVOKE(astChr2Double_(string,STATUS_PTR)) +#define astRemoveLeadingBlanks(string) astERROR_INVOKE(astRemoveLeadingBlanks_(string,STATUS_PTR)) +#define astChrSub(test,template,subs,nsub) astERROR_INVOKE(astChrSub_(test,template,subs,nsub,STATUS_PTR)) +#define astChrCase(in,out,upper,blen) astERROR_INVOKE(astChrCase_(in,out,upper,blen,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astMallocInit(size) astMalloc_(size,1,STATUS_PTR) +#endif + +#ifdef HAVE_NONANSI_SSCANF +#define astSscanf astERROR_INVOKE(astSscanf_) +#else +#define astSscanf astERROR_INVOKE(sscanf) +#endif +#define astChrSplit(str,n) astERROR_INVOKE(astChrSplit_(str,n,STATUS_PTR)) +#define astChrSplitC(str,c,n) astERROR_INVOKE(astChrSplitC_(str,c,n,STATUS_PTR)) +#define astChrSplitRE(str,c,n,m) astERROR_INVOKE(astChrSplitRE_(str,c,n,m,STATUS_PTR)) + + +#ifdef MEM_PROFILE +#define astStartTimer(name) astERROR_INVOKE(astStartTimer_(__FILE__,__LINE__,name,STATUS_PTR)) +#define astStopTimer astERROR_INVOKE(astStopTimer_(STATUS_PTR)) +#define astEnableTimers(enable) astERROR_INVOKE(astEnableTimers_(enable,STATUS_PTR)) +#endif + + +/* Functions used for debugging memory leaks, etc */ +#ifdef MEM_DEBUG + +#define astActiveMemory(label) astERROR_INVOKE(astActiveMemory_(label)) +#define astMemoryTune(name,value) astERROR_INVOKE(astMemoryTune_(name,value,STATUS_PTR)) +#define astWatchMemory(id) astERROR_INVOKE(astWatchMemory_(id)) +#define astCheckMemory astERROR_INVOKE(astCheckMemory_(STATUS_PTR)) +#define astFlushMemory(leak) astERROR_INVOKE(astFlushMemory_(leak,STATUS_PTR)) +#define astBeginPM astERROR_INVOKE(astBeginPM_(STATUS_PTR)) +#define astEndPM astERROR_INVOKE(astEndPM_(STATUS_PTR)) +#define astMemoryPtr(id) astERROR_INVOKE(astMemoryPtr_(id)) +#define astMemoryAlarm(text) astERROR_INVOKE(astMemoryAlarm_(text)) +#define astMemoryStats(reset,peak,current) astERROR_INVOKE(astMemoryStats_(reset,peak,current,STATUS_PTR)) +#define astMemoryWarning(threshold) astERROR_INVOKE(astMemoryWarning_(threshold,STATUS_PTR)) +#define astMemoryUse(ptr,text) astERROR_INVOKE(astMemoryUse_(ptr,text,STATUS_PTR)) +#define astMemoryId(ptr) astERROR_INVOKE(astMemoryId_(ptr,STATUS_PTR)) +#else + +#define astActiveMemory(label) +#define astMemoryTune(name,value) +#define astWatchMemory(id) +#define astCheckMemory +#define astFlushMemory(leak) +#define astBeginPM +#define astEndPM +#define astMemoryPtr(id) NULL +#define astMemoryAlarm(text) +#define astMemoryStats(reset,peak,current) +#define astMemoryUse(ptr,text) +#define astMemoryId(ptr) +#define astMemoryWarning(threshold) + +#endif + +#endif + + + diff --git a/normmap.c b/normmap.c new file mode 100644 index 0000000..3013ad1 --- /dev/null +++ b/normmap.c @@ -0,0 +1,1664 @@ +/* +*class++ +* Name: +* NormMap + +* Purpose: +* Normalise coordinates using a supplied Frame. + +* Constructor Function: +c astNormMap +f AST_NORMMAP + +* Description: +* The NormMap class implements a Mapping which normalises coordinate +* values using the +c astNorm function +f AST_NORM routine +* of a supplied Frame. The number of inputs and outputs of a NormMap +* are both equal to the number of axes in the supplied Frame. +* +* The forward and inverse transformation of a NormMap are both +* defined but are identical (that is, they do not form a real inverse +* pair in that the inverse transformation does not undo the +* normalisation, instead it reapplies it). However, the +c astSimplify +f AST_SIMPLIFY +* function will replace neighbouring pairs of forward and inverse +* NormMaps by a single UnitMap. + +* Inheritance: +* The NormMap class inherits from the Mapping class. + +* Attributes: +* The NormMap class does not define any new attributes beyond +* those which are applicable to all Mappings. + +* Functions: +c The NormMap class does not define any new functions beyond those +f The NormMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 11-JUL-2005 (DSB): +* Original version. +* 23-AUG-2006 (DSB): +* Override astEqual. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS NormMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "unitmap.h" /* Unit Mappings */ +#include "normmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(NormMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(NormMap,Class_Init) +#define class_vtab astGLOBAL(NormMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstNormMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstNormMap *astNormMapId_( void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Member functions. */ +/* ================= */ + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two NormMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "normmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* NormMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two NormMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a NormMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the NormMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstNormMap *that; + AstNormMap *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two NormMap structures. */ + this = (AstNormMap *) this_object; + that = (AstNormMap *) that_object; + +/* Check the second object is a NormMap. We know the first is a + NormMap since we have arrived at this implementation of the virtual + function. */ + if( astIsANormMap( that ) ) { + +/* Check the Invert flags for the two NormMaps are equal. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + +/* Check the two Frames are equal. */ + if( astEqual( this->frame, that->frame ) ) { + result = 1; + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitNormMapVtab_( AstNormMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitNormMapVtab + +* Purpose: +* Initialise a virtual function table for a NormMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "normmap.h" +* void astInitNormMapVtab( AstNormMapVtab *vtab, const char *name ) + +* Class Membership: +* NormMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the NormMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsANormMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + mapping->RemoveRegions = RemoveRegions; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_mapsplit = mapping->MapSplit; + mapping->MapSplit = MapSplit; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + mapping->MapSplit = MapSplit; + mapping->Rate = Rate; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetDump( vtab, Dump, "NormMap", "Normalise axis values" ); + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* NormMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstNormMap *this; /* Pointer to NormMap structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the NormMap structure. */ + this = (AstNormMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->frame, mode, extra, fail ); + + return result; + +} +#endif + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a NormMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* NormMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated NormMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated NormMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated NormMap which is to be merged with +* its neighbours. This should be a cloned copy of the NormMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* NormMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated NormMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *frm1; + AstFrame *frm2; + AstMapping *smap; + AstNormMap *map; + AstNormMap *nmap1; + AstNormMap *nmap2; + int cancel; + int map_inv; + int nax; + int result; + +/* Initialise. */ + result = -1; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Initialisation to avoid compiler warnings. */ + nax = 0; + +/* Get a pointer to this NormMap. */ + map = (AstNormMap *) this; + +/* Temporarily set its Invert flag to the requested value. */ + map_inv = astGetInvert( map ); + astSetInvert( map, ( *invert_list )[ where ] ); + +/* First try to simplify the NormMap by simplifying its encapsulated + Frame. */ + smap = astSimplify( map->frame ); + +/* If any simplification took place, create a new NormMap with the + simplified Frame. */ + if( smap != (AstMapping *) map->frame ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astNormMap( (AstFrame *) smap, "", status ); + result = where; + +/* The only other simplication which can be performed is to cancel a NormMap + with its own inverse in series. */ + } else if( series ) { + +/* Indicate we have nothing to cancel with as yet. */ + cancel = -1; + +/* First consider the lower neighbour. */ + if( where > 0 && astIsANormMap( ( *map_list )[ where - 1 ] ) ) { + +/* Check the Invert flags are opposite */ + if( ( *invert_list )[ where ] != ( *invert_list )[ where - 1 ] ) { + nmap1 = map; + nmap2 = (AstNormMap *) ( *map_list )[ where - 1 ]; + +/* Check the encapsulated Frames are equal. */ + frm1 = nmap1->frame; + frm2 = nmap2->frame; + if( astEqual( frm1, frm2 ) ) cancel = where - 1; + nax = astGetNout( nmap1 ); + } + } + +/* Likewise consider the upper neighbour. */ + if( cancel == -1 && where + 1 < *nmap && + astIsANormMap( ( *map_list )[ where + 1 ] ) ) { + + if( ( *invert_list )[ where ] != ( *invert_list )[ where + 1 ] ) { + nmap1 = map; + nmap2 = (AstNormMap *) ( *map_list )[ where + 1 ]; + frm1 = nmap1->frame; + frm2 = nmap2->frame; + if( astEqual( frm1, frm2 ) ) cancel = where + 1; + nax = astGetNin( nmap1 ); + } + } + +/* If we can cancel with a neightbour, do so. */ + if( cancel != -1 ) { + (void) astAnnul( ( *map_list )[ where ] ); + (void) astAnnul( ( *map_list )[ cancel ] ); + ( *map_list )[ where ] = (AstMapping *) astUnitMap( nax, "", status ); + ( *invert_list )[ where ] = 0; + ( *map_list )[ cancel ] = (AstMapping *) astUnitMap( nax, "", status ); + ( *invert_list )[ cancel ] = 0; + result = ( cancel < where ) ? cancel : where; + } + } + +/* Free resources. */ + smap = astAnnul( smap ); + +/* Reset the original Invert attribute for the specified NormMap */ + astSetInvert( map, map_inv ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* NormMap. + +* Type: +* Private function. + +* Synopsis: +* #include "normmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* NormMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing NormMap. This is only possible if the specified inputs +* correspond to some subset of the NormMap outputs. That is, there +* must exist a subset of the NormMap outputs for which each output +* depends only on the selected NormMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied NormMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the NormMap to be split (the NormMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied NormMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied NormMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied NormMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstFrame *frm2; /* Pointer to new Frame */ + AstNormMap *this; /* Pointer to NormMap structure */ + int *result; /* Pointer to returned array */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the parent astMapSplit method to see if it can do the job. */ + result = (*parent_mapsplit)( this_map, nin, in, map, status ); + +/* If not, we provide a special implementation here. */ + if( !result ) { + +/* Get a pointer to the NormMap structure. */ + this = (AstNormMap *) this_map; + +/* Pick the requried axes from the encapsulated Frame. */ + frm2 = astPickAxes( this->frame, nin, in, NULL ); + +/* Create a new NormMap from this. */ + *map = (AstMapping *) astNormMap( frm2, "", status ); + +/* The returned list of output axes is a copy the supplied list of input + axes. */ + result = astStore( NULL, in, sizeof( int )*(size_t) nin ); + +/* Free resources. */ + frm2 = astAnnul( frm2 ); + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "normmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* NormMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + + return ( ax1 == ax2 ) ? 1.0 : 0.0; +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "normmap.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* NormMap method (over-rides the astRemoveRegions method inherited +* from the Mapping class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a CmpMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel CmpMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the NormMap class invokes the +* astRemoveRegions method on the encapsulated Frame, and returns a +* new NormMap containing the resulting Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *newfrm; /* New component Frame */ + AstMapping *result; /* Result pointer to return */ + AstNormMap *new; /* Pointer to new MormMap */ + AstNormMap *this; /* Pointer to NormMap structure */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the NormMap. */ + this = (AstNormMap *) this_mapping; + +/* Invoke the astRemoveRegions method on the component Frame. */ + newfrm = astRemoveRegions( this->frame ); + +/* If the Frame was not modified, just return a clone of the supplied + pointer. */ + if( this->frame == newfrm ) { + result = astClone( this ); + +/* Otherwise, we need to create a new NormMap to return. We take a deep + copy of the supplied NormMap and then modify the Frame so that we + retain any extra information in the supplied NormMap. */ + } else { + new = astCopy( this ); + (void) astAnnul( new->frame ); + new->frame = astClone( newfrm ); + result = (AstMapping *) new; + } + +/* Free resources. */ + newfrm = astAnnul( newfrm ); + +/* Annul the returned Mapping if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a NormMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "normmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* NormMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a NormMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required zoom +* factor. + +* Parameters: +* this +* Pointer to the NormMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the NormMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstNormMap *map; /* Pointer to NormMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *work; /* Work space for a single point */ + int coord; /* Loop counter for coordinates */ + int ncoord_in; /* Number of coordinates per input point */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the NormMap. */ + map = (AstNormMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + ncoord_in = astGetNcoord( in ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + work = astMalloc( sizeof( double )* ncoord_in ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + for ( point = 0; point < npoint; point++ ) { + for ( coord = 0; coord < ncoord_in; coord++ ) { + work[ coord ] = ptr_in[ coord ][ point ]; + } + astNorm( map->frame, work ); + for ( coord = 0; coord < ncoord_in; coord++ ) { + ptr_out[ coord ][ point ] = work[ coord ]; + } + } + } + +/* Free resources */ + work = astFree( work ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for NormMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for NormMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the +* Frame within the NormMap. +*/ + +/* Local Variables: */ + AstNormMap *in; /* Pointer to input NormMap */ + AstNormMap *out; /* Pointer to output NormMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + + in = (AstNormMap *) objin; + out = (AstNormMap *) objout; + out->frame = astCopy( in->frame ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for NormMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for NormMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstNormMap *this; /* Pointer to NormMap */ + + this = (AstNormMap *) obj; + this->frame = astAnnul( this->frame ); + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for NormMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the NormMap class to an output Channel. + +* Parameters: +* this +* Pointer to the NormMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstNormMap *this; /* Pointer to the NormMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the NormMap structure. */ + this = (AstNormMap *) this_object; + +/* Write out values representing the instance variables for the + NormMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* Frame. */ +/* ------ */ + astWriteObject( channel, "Frame", 1, 1, this->frame, + "Frame defining the normalisation" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsANormMap and astCheckNormMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(NormMap,Mapping) +astMAKE_CHECK(NormMap) + +AstNormMap *astNormMap_( void *frame_void, const char *options, int *status, ...) { +/* +*++ +* Name: +c astNormMap +f AST_NORMMAP + +* Purpose: +* Create a NormMap. + +* Type: +* Public function. + +* Synopsis: +c #include "normmap.h" +c AstNormMap *astNormMap( AstFrame *frame, const char *options, ... ) +f RESULT = AST_NORMMAP( FRAME, OPTIONS, STATUS ) + +* Class Membership: +* NormMap constructor. + +* Description: +* This function creates a new NormMap and optionally initialises its +* attributes. +* +* A NormMap is a Mapping which normalises coordinate values using the +c astNorm function +f AST_NORM routine +* of the supplied Frame. The number of inputs and outputs of a NormMap +* are both equal to the number of axes in the supplied Frame. +* +* The forward and inverse transformation of a NormMap are both +* defined but are identical (that is, they do not form a real inverse +* pair in that the inverse transformation does not undo the +* normalisation, instead it reapplies it). However, the +c astSimplify +f AST_SIMPLIFY +* function will replace neighbouring pairs of forward and inverse +* NormMaps by a single UnitMap. + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame which is to be used to normalise the +* supplied axis values. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new NormMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new NormMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astNormMap() +f AST_NORMMAP = INTEGER +* A pointer to the new NormMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* The Frame pointer */ + AstNormMap *new; /* Pointer to new NormMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate pointers to the Frame structures provided. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the NormMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitNormMap( NULL, sizeof( AstNormMap ), !class_init, &class_vtab, + "NormMap", frame ); + if( astOK ) { + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new NormMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new NormMap. */ + return new; +} + +AstNormMap *astNormMapId_( void *frame_void, const char *options, ... ) { +/* +* Name: +* astNormMapId_ + +* Purpose: +* Create a NormMap. + +* Type: +* Private function. + +* Synopsis: +* #include "normmap.h" +* AstNormMap *astNormMapId_( int ncoord, double zoom, +* const char *options, ... ) + +* Class Membership: +* NormMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astNormMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astNormMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astNormMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astNormMap_. + +* Returned Value: +* The ID value associated with the new NormMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* The Frame pointer */ + AstNormMap *new; /* Pointer to new NormMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate pointers to the Frame structures provided. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Initialise the NormMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitNormMap( NULL, sizeof( AstNormMap ), !class_init, &class_vtab, + "NormMap", frame ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new NormMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new NormMap. */ + return astMakeId( new ); +} + +AstNormMap *astInitNormMap_( void *mem, size_t size, int init, + AstNormMapVtab *vtab, const char *name, + AstFrame *frame, int *status ) { +/* +*+ +* Name: +* astInitNormMap + +* Purpose: +* Initialise a NormMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "normmap.h" +* AstNormMap *astInitNormMap( void *mem, size_t size, int init, +* AstNormMapVtab *vtab, const char *name, +* AstFrame *frame ) + +* Class Membership: +* NormMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new NormMap object. It allocates memory (if necessary) to accommodate +* the NormMap plus any additional data associated with the derived class. +* It then initialises a NormMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a NormMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the NormMap is to be initialised. +* This must be of sufficient size to accommodate the NormMap data +* (sizeof(NormMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the NormMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the NormMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the NormMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new NormMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* The Frame to use to do the normalisations. + +* Returned Value: +* A pointer to the new NormMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstNormMap *new; /* Pointer to new NormMap */ + int ncoord; /* Number of input and output coords */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitNormMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Get the number of axes in the Frame. */ + ncoord = astGetNaxes( frame ); + +/* Initialise a Mapping structure (the parent class) as the first component + within the NormMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstNormMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + ncoord, ncoord, 1, 1 ); + + if ( astOK ) { + +/* Initialise the NormMap data. */ +/* ---------------------------- */ +/* Store a pointer to the Frame. */ + new->frame = astClone( frame ); + +/* If an error occurred, clean up by deleting the new NormMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new NormMap. */ + return new; +} + +AstNormMap *astLoadNormMap_( void *mem, size_t size, + AstNormMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadNormMap + +* Purpose: +* Load a NormMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "normmap.h" +* AstNormMap *astLoadNormMap( void *mem, size_t size, +* AstNormMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* NormMap loader. + +* Description: +* This function is provided to load a new NormMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* NormMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a NormMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the NormMap is to be +* loaded. This must be of sufficient size to accommodate the +* NormMap data (sizeof(NormMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the NormMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the NormMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstNormMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new NormMap. If this is NULL, a pointer +* to the (static) virtual function table for the NormMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "NormMap" is used instead. + +* Returned Value: +* A pointer to the new NormMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstNormMap *new; /* Pointer to the new NormMap */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this NormMap. In this case the + NormMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstNormMap ); + vtab = &class_vtab; + name = "NormMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitNormMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built NormMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "NormMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Frame. */ +/* ------ */ + new->frame = astReadObject( channel, "frame", NULL ); + +/* If an error occurred, clean up by deleting the new NormMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new NormMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + diff --git a/normmap.h b/normmap.h new file mode 100644 index 0000000..5336e56 --- /dev/null +++ b/normmap.h @@ -0,0 +1,217 @@ +#if !defined( NORMMAP_INCLUDED ) /* Include this file only once */ +#define NORMMAP_INCLUDED +/* +*+ +* Name: +* normmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the NormMap class. + +* Invocation: +* #include "normmap.h" + +* Description: +* This include file defines the interface to the NormMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The NormMap class implements Mappings which use a Frame to +* normalise the input axis values. + +* Inheritance: +* The NormMap class inherits from the Mapping class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 11-JUL-2005 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "frame.h" /* Coordinate Frames */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* NormMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstNormMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstFrame *frame; /* Encapsulated Frame */ +} AstNormMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstNormMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +} AstNormMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstNormMapGlobals { + AstNormMapVtab Class_Vtab; + int Class_Init; +} AstNormMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitNormMapGlobals_( AstNormMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(NormMap) /* Check class membership */ +astPROTO_ISA(NormMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstNormMap *astNormMap_( void *, const char *, int *, ...); +#else +AstNormMap *astNormMapId_( void *, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstNormMap *astInitNormMap_( void *, size_t, int, AstNormMapVtab *, + const char *, AstFrame *, int * ); + +/* Vtab initialiser. */ +void astInitNormMapVtab_( AstNormMapVtab *, const char *, int * ); + +/* Loader. */ +AstNormMap *astLoadNormMap_( void *, size_t, AstNormMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckNormMap(this) astINVOKE_CHECK(NormMap,this,0) +#define astVerifyNormMap(this) astINVOKE_CHECK(NormMap,this,1) + +/* Test class membership. */ +#define astIsANormMap(this) astINVOKE_ISA(NormMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astNormMap astINVOKE(F,astNormMap_) +#else +#define astNormMap astINVOKE(F,astNormMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitNormMap(mem,size,init,vtab,name,frame) \ +astINVOKE(O,astInitNormMap_(mem,size,init,vtab,name,frame,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitNormMapVtab(vtab,name) astINVOKE(V,astInitNormMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadNormMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadNormMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckNormMap to validate NormMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#endif +#endif + + + + + diff --git a/nullregion.c b/nullregion.c new file mode 100644 index 0000000..6ef9cfa --- /dev/null +++ b/nullregion.c @@ -0,0 +1,2093 @@ +/* +*class++ +* Name: +* NullRegion + +* Purpose: +* A boundless region within a Frame. + +* Constructor Function: +c astNullRegion +f AST_NULLREGION + +* Description: +* The NullRegion class implements a Region with no bounds within a Frame. +* If the Negated attribute of a NullRegion is false, the NullRegion +* represents a Region containing no points. If the Negated attribute of +* a NullRegion is true, the NullRegion represents an infinite Region +* (that is, all points in the coordinate system are inside the NullRegion). + +* Inheritance: +* The NullRegion class inherits from the Region class. + +* Attributes: +* The NullRegion class does not define any new attributes beyond +* those which are applicable to all Regions. + +* Functions: +c The NullRegion class does not define any new functions beyond those +f The NullRegion class does not define any new routines beyond those +* which are applicable to all Regions. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 11-OCT-2004 (DSB): +* Original version. +* 20-JAN-2009 (DSB): +* Over-ride astRegBasePick. +* 26-JAN-2009 (DSB): +* Over-ride astMapMerge. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS NullRegion + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "nullregion.h" /* Interface definition for this class */ +#include "mapping.h" /* Position mappings */ +#include "circle.h" /* Circle regions */ +#include "prism.h" /* Extruded Regions */ +#include "unitmap.h" /* Unit Mapping */ +#include "cmpframe.h" /* Compound Frames */ +#include "cmpmap.h" /* Compound Mappings */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(NullRegion) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(NullRegion,Class_Init) +#define class_vtab astGLOBAL(NullRegion,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstNullRegionVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstNullRegion *astNullRegionId_( void *, void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *RegMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * ); +static AstRegion *GetDefUnc( AstRegion *, int * ); +static AstRegion *MergeNullRegion( AstNullRegion *, AstRegion *, int, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int Overlap( AstRegion *, AstRegion *, int * ); +static int OverlapX( AstRegion *, AstRegion *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void RegBaseBox( AstRegion *this, double *, double *, int * ); +static void GetRegionBounds( AstRegion *this, double *, double *, int * ); + +/* Member functions. */ +/* ================= */ +static AstRegion *GetDefUnc( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astGetDefUnc + +* Purpose: +* Obtain a pointer to the default uncertainty Region for a given Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "nullregion.h" +* AstRegion *astGetDefUnc( AstRegion *this ) + +* Class Membership: +* NullRegion member function (over-rides the astGetDefUnc protected +* method inherited from the Region class). + +* Description: +* This function returns a pointer to a Region which represents the +* default uncertainty associated with a position on the boundary of the +* given Region. The returned Region refers to the base Frame within the +* FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstRegion *result; + double *cen; + double rad; + int i; + int n; + +/* Initialise */ + result = NULL; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Create a Circle centred on the origin with zero radius. */ + n = astGetNaxes( this ); + cen = astMalloc( sizeof(double)*(size_t) n ); + if( cen ) { + for( i = 0; i < n; i++ ) cen[ i ] = 0.0; + rad = 0.0; + result = (AstRegion *) astCircle( this, 1, cen, &rad, NULL, "", status ); + cen = astFree( cen ); + } + +/* Return the default uncertainty Region. */ + return result; +} + +void astInitNullRegionVtab_( AstNullRegionVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitNullRegionVtab + +* Purpose: +* Initialise a virtual function table for a NullRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "nullregion.h" +* void astInitNullRegionVtab( AstNullRegionVtab *vtab, const char *name ) + +* Class Membership: +* NullRegion vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the NullRegion class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsANullRegion) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping->MapMerge = MapMerge; + + region->GetDefUnc = GetDefUnc; + region->Overlap = Overlap; + region->OverlapX = OverlapX; + region->RegBaseBox = RegBaseBox; + region->RegBaseMesh = RegBaseMesh; + region->GetRegionBounds = GetRegionBounds; + region->RegMesh = RegMesh; + region->RegBasePick = RegBasePick; + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDump( vtab, Dump, "NullRegion", "Boundless region" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a NullRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* NullRegion method (over-rides the protected astMapMerge method +* inherited from the Region class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated NullRegion in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated NullRegion with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated NullRegion which is to be merged with +* its neighbours. This should be a cloned copy of the NullRegion +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* NullRegion it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated NullRegion resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstNullRegion *oldint; /* Pointer to supplied NullRegion */ + AstMapping *map; /* Pointer to adjacent Mapping */ + AstMapping *new; /* Simplified or merged Region */ + int i1; /* Index of first Mapping merged */ + int i; /* Loop counter */ + int result; /* Result value to return */ + +/* Initialise. */ + result = -1; + i1 = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the NullRegion. */ + oldint = (AstNullRegion *) this; + +/* First of all, see if the NullRegion can be replaced by a simpler Region, + without reference to the neighbouring Regions in the list. */ +/* =====================================================================*/ + +/* Try to simplify the NullRegion. If the pointer value has changed, we assume + some simplification took place. */ + new = astSimplify( oldint ); + if( new != (AstMapping *) oldint ) { + +/* Annul the NullRegion pointer in the list and replace it with the new Region + pointer, and indicate that the forward transformation of the returned + Region should be used (not really needed but keeps things clean). */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = new; + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the NullRegion itself could not be simplified, see if it can be merged + with the Regions on either side of it in the list. We can only merge + in parallel. */ +/* =====================================================================*/ + } else if( ! series ){ + new = astAnnul( new ); + +/* Attempt to merge the NullRegion with its lower neighbour (if any). */ + if( where > 0 ) { + i1 = where - 1; + map = ( *map_list )[ where - 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergeNullRegion( oldint, (AstRegion *) map, + 0, status ); + } + } + +/* If this did not produced a merged Region, attempt to merge the NullRegion + with its upper neighbour (if any). */ + if( !new && where < *nmap - 1 ) { + i1 = where; + map = ( *map_list )[ where + 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergeNullRegion( oldint, (AstRegion *) map, + 1, status ); + } + } + +/* If succesfull... */ + if( new ){ + +/* Annul the first of the two Mappings, and replace it with the merged + Region. Also clear the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = new; + ( *invert_list )[ i1 ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i1 + 1 ] ); + for ( i = i1 + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + } + + } else { + new = astAnnul( new ); + } + +/* Return the result. */ + return result; +} + +static AstRegion *MergeNullRegion( AstNullRegion *this, AstRegion *reg, + int intfirst, int *status ) { +/* +* Name: +* MergeNullRegion + +* Purpose: +* Attempt to merge a NullRegion with another Region to form a Region of +* higher dimensionality. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* AstRegion *MergeNullRegion( AstNullRegion *this, AstRegion *reg, +* int intfirst, int *status ) + +* Class Membership: +* NullRegion member function. + +* Description: +* This function attempts to combine the supplied Regions together +* into a Region of higher dimensionality. + +* Parameters: +* this +* Pointer to a NullRegion. +* reg +* Pointer to another Region. +* intfirst +* If non-zero, then the NullRegion axes are put first in the new Region. +* Otherwise, the other Region's axes are put first. +* status +* Pointer to the inherited status value. + +* Returned Value: +* A pointer to a new region, or NULL if the supplied Regions could +* not be merged. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* Pointer to base Frame for "result" */ + AstFrame *cfrm; /* Pointer to current Frame for "result" */ + AstFrame *frm_reg; /* Pointer to Frame from "reg" */ + AstFrame *frm_this; /* Pointer to Frame from "this" */ + AstMapping *bcmap; /* Base->current Mapping for "result" */ + AstMapping *map_reg; /* Base->current Mapping from "reg" */ + AstMapping *map_this; /* Base->current Mapping from "this" */ + AstMapping *sbunc; /* Simplified uncertainty */ + AstRegion *bunc; /* Base Frame uncertainty Region */ + AstRegion *new; /* Pointer to new NullRegion in base Frame */ + AstRegion *result; /* Pointer to returned NullRegion in current Frame */ + AstRegion *unc_reg; /* Current Frame uncertainty Region from "reg" */ + AstRegion *unc_this; /* Current Frame uncertainty Region from "this" */ + double fac_reg; /* Ratio of used to default MeshSize for "reg" */ + double fac_this; /* Ratio of used to default MeshSize for "this" */ + int msz_reg; /* Original MeshSize for "reg" */ + int msz_reg_set; /* Was MeshSize originally set for "reg"? */ + int msz_this; /* Original MeshSize for "this" */ + int msz_this_set; /* Was MeshSize originally set for "this"? */ + int nax; /* Number of axes in "result" */ + int nax_reg; /* Number of axes in "reg" */ + int nax_this; /* Number of axes in "this" */ + int neg_reg; /* Negated attribute value for other supplied Region */ + int neg_this; /* Negated attribute value for supplied NullRegion */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get the Closed attributes of the two Regions. They must be the same in + each Region if we are to merge the Regions. In addition, in order to + merge, either both Regions must have a defined uncertainty, or neither + Region must have a defined Uncertainty. */ + if( astGetClosed( this ) == astGetClosed( reg ) && + astTestUnc( this ) == astTestUnc( reg ) ) { + +/* Get the Nagated attributes of the two Regions. */ + neg_this = astGetNegated( this ); + neg_reg = astGetNegated( reg ); + +/* Get the number of axes in the two supplied Regions. */ + nax_reg = astGetNaxes( reg ); + nax_this = astGetNaxes( this ); + +/* If the Regions can be combined, get the number of axes the + combination will have. */ + nax = nax_reg + nax_this; + +/* Get the base Frames from the two Region FrameSets, and combine them + into a single CmpFrame that will be used to create any new Region. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + frm_reg = astGetFrame( reg->frameset, AST__BASE ); + + if( intfirst ) { + bfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + bfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + +/* Indicate we do not yet have a merged Region. */ + new = NULL; + +/* We only check for merging with another NullRegion (other classes such + as Box and Interval check for merging of NullRegions with other classes). + The result will be an NullRegion. Both Regions must have the same value + for the Negated flag. */ + if( astIsANullRegion( reg ) && neg_this == neg_reg ) { + new = (AstRegion *) astNullRegion( bfrm, NULL, "", status ); + +/* Propagate remaining attributes of the supplied Region to it. */ + astRegOverlay( new, this, 1 ); + +/* Ensure the Negated flag is set correctly in the returned NullRegion. */ + if( neg_this ) { + astSetNegated( new, neg_this ); + } else { + astClearNegated( new ); + } + +/* If both the supplied Regions have uncertainty, assign the new Region an + uncertainty. */ + if( astTestUnc( this ) && astTestUnc( reg ) ) { + +/* Get the uncertainties from the two supplied Regions. */ + unc_this = astGetUncFrm( this, AST__BASE ); + unc_reg = astGetUncFrm( reg, AST__BASE ); + +/* Combine them into a single Region (a Prism), in the correct order. */ + if( intfirst ) { + bunc = (AstRegion *) astPrism( unc_this, unc_reg, "", status ); + } else { + bunc = (AstRegion *) astPrism( unc_reg, unc_this, "", status ); + } + +/* Attempt to simplify the Prism. */ + sbunc = astSimplify( bunc ); + +/* Use the simplified Prism as the uncertainty for the returned Region. */ + astSetUnc( new, sbunc ); + +/* Free resources. */ + sbunc = astAnnul( sbunc ); + bunc = astAnnul( bunc ); + unc_reg = astAnnul( unc_reg ); + unc_this = astAnnul( unc_this ); + } + +/* Get the current Frames from the two Region FrameSets, and combine them + into a single CmpFrame. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__CURRENT ); + frm_reg = astGetFrame( reg->frameset, AST__CURRENT ); + + if( intfirst ) { + cfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + cfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + +/* Get the base -> current Mappings from the two Region FrameSets, and + combine them into a single parallel CmpMap that connects bfrm and cfrm. */ + map_this = astGetMapping( ((AstRegion *) this)->frameset, AST__BASE, + AST__CURRENT ); + map_reg = astGetMapping( reg->frameset, AST__BASE, AST__CURRENT ); + + if( intfirst ) { + bcmap = (AstMapping *) astCmpMap( map_this, map_reg, 0, "", + status ); + } else { + bcmap = (AstMapping *) astCmpMap( map_reg, map_this, 0, "", + status ); + } + +/* Map the new Region into the new current Frame. */ + result = astMapRegion( new, bcmap, cfrm ); + +/* The filling factor in the returned is the product of the filling + factors for the two supplied Regions. */ + if( astTestFillFactor( reg ) || astTestFillFactor( this ) ) { + astSetFillFactor( result, astGetFillFactor( reg )* + astGetFillFactor( this ) ); + } + +/* If the MeshSize value is set in either supplied Region, set a value + for the returned Region which scales the default value by the + product of the scaling factors for the two supplied Regions. First see + if either MeshSize value is set. */ + msz_this_set = astTestMeshSize( this ); + msz_reg_set = astTestMeshSize( reg ); + if( msz_this_set || msz_reg_set ) { + +/* If so, get the two MeshSize values (one of which may be a default + value), and then clear them so that the default value will be returned + in future. */ + msz_this = astGetMeshSize( this ); + msz_reg = astGetMeshSize( reg ); + astClearMeshSize( this ); + astClearMeshSize( reg ); + +/* Get the ratio of the used MeshSize to the default MeshSize for both + Regions. */ + fac_this = (double)msz_this/(double)astGetMeshSize( this ); + fac_reg = (double)msz_reg/(double)astGetMeshSize( reg ); + +/* The MeshSize of the returned Returned is the default value scaled by + the product of the two ratios found above. */ + astSetMeshSize( result, fac_this*fac_reg*astGetMeshSize( result ) ); + +/* Re-instate the original MeshSize values for the supplied Regions (if + set) */ + if( msz_this_set ) astSetMeshSize( this, msz_this ); + if( msz_reg_set ) astSetMeshSize( reg, msz_reg ); + } + +/* Free remaining resources */ + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + map_this = astAnnul( map_this ); + map_reg = astAnnul( map_reg ); + bcmap = astAnnul( bcmap ); + new = astAnnul( new ); + cfrm = astAnnul( cfrm ); + } + } + +/* If an error has occurred, annul the returned pointer. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int Overlap( AstRegion *this, AstRegion *that, int *status ){ +/* +* Name: +* Overlap + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* int Overlap( AstRegion *this, AstRegion *that ) + +* Class Membership: +* NullRegion member function (over-rides the astOverlap method inherited +* from the Region class). + +* Description: +* This function returns an integer value indicating if the two +* supplied Regions overlap. The two Regions are converted to a commnon +* coordinate system before performing the check. If this conversion is +* not possible (for instance because the two Regions represent areas in +* different domains), then the check cannot be performed and a zero value +* is returned to indicate this. + +* Parameters: +* this +* Pointer to the first Region. +* that +* Pointer to the second Region. + +* Returned Value: +* astOverlap() +* A value indicating if there is any overlap between the two Regions. +* Possible values are: +* +* 0 - The check could not be performed because the second Region +* could not be mapped into the coordinate system of the first +* Region. +* +* 1 - There is no overlap between the two Regions. +* +* 2 - The first Region is completely inside the second Region. +* +* 3 - The second Region is completely inside the first Region. +* +* 4 - There is partial overlap between the two Regions. +* +* 5 - The Regions are identical. +* +* 6 - The second Region is the negation of the first Region. + +* Notes: +* - The returned values 5 and 6 do not check the value of the Closed +* attribute in the two Regions. +* - A value of zero will be returned if this function is invoked with the +* AST error status set, or if it should fail for any reason. + +* Implementation Notes: +* - This function is simply a wrap-up for the OverlapX function +* which performs the required processing but swaps the order of the +* two arguments. This is a trick to allow the astOverlap +* method to be over-ridden by derived classes on the basis of the +* class of either of the two arguments. +*/ + +/* Check the inherited status. */ + if ( !astOK ) return 0; + +/* Invoke the private "OverlapX" member function with the two arguments + swapped. */ + return OverlapX( that, this, status ); +} + +static int OverlapX( AstRegion *that, AstRegion *this, int *status ){ +/* +* Name: +* OverlapX + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* int OverlapX( AstRegion *that, AstRegion *this ) + +* Class Membership: +* NullRegion member function (over-rides the astOverlapX method inherited +* from the Region class). + +* Description: +* This function performs the processing for the public astOverlap +* method (as inherited from the Region class and over-ridden by the +* NullRegion class) and has exactly the same interface except that +* the order of the two arguments is swapped. This is a trick +* to allow the astOverlap method to be over-ridden by derived +* classes on the basis of the class of either of its two +* arguments. +* +* See the astOverlap method for details of the interface. + +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* FrameSet connecting Region Frames */ + int result; /* Returned value */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the Regions can be compared meaningfully. */ + fs = astConvert( this, that, "" ); + if( fs ) { + fs = astAnnul( fs ); + +/* If both the supplied Regions are NullRegion... */ + if( astIsANullRegion( this ) && astIsANullRegion( that ) ) { + +/* If the Negated attributes are equal, the Regions are identical. + Otherwise they are mutually exclusive. */ + if( astGetNegated( this ) == astGetNegated( that ) ) { + result = 5; + } else { + result = 6; + } + +/* If one of the supplied Region is a NullRegion containing no points, + then there is no overlap. */ + } else if( ( astIsANullRegion( this ) && !astGetNegated( this ) ) || + ( astIsANullRegion( that ) && !astGetNegated( that ) ) ) { + result = 1; + +/* If "that" is infinite and "this" is not infinite, then "this" is + entirely inside "that". */ + } else if( astIsANullRegion( that ) && astGetNegated( that ) ) { + result = 2; + +/* If "this" is infinite and "that" is not infinite, then "that" is + entirely inside "this". */ + } else if( astIsANullRegion( this ) && astGetNegated( this ) ){ + result = 3; + +/* Otherwise there is partial overlap. */ + } else { + result = 4; + } + } + +/* If not OK, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* NullRegion member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstNullRegion *this; /* Pointer to NullRegion structure */ + int i; /* Axis index */ + int nc; /* No. of axes in base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the NullRegion structure */ + this = (AstNullRegion *) this_region; + +/* Get the number of base Frame axes. */ + nc = astGetNin( this_region->frameset ); + +/* Set the upper bound less than the lower bound to indicate that the region + contains no points. */ + for( i = 0; i < nc; i++ ) { + lbnd[ i ] = 1.0; + ubnd[ i ] = -1.0; + } + +} + +static AstPointSet *RegBaseMesh( AstRegion *this, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* NullRegion member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. Note, a NullRegion has no boundary. This +* is indicated by returned a PointSet containing a single point with a +* value of AST__BAD on every axis. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a new PointSet containing a single point with value of +* AST__BAD on every axis. + +*- +*/ + +/* Local Variables: */ + AstPointSet *result; + double **ptr; + int i; + int nc; + +/* Initialise */ + result = NULL; + +/* Check the inherited status */ + if( !astOK ) return result; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this->basemesh ) { + result = astClone( this->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* Get the number of base Frame axes */ + nc = astGetNin( this->frameset ); + +/* Create the PointSet. */ + result = astPointSet( 1, nc, "", status ); + +/* Get a pointer to the axis values. */ + ptr = astGetPoints( result ); + +/* If OK, store AST__BAD on every axis. */ + if( ptr ) for( i = 0; i < nc; i++ ) ptr[ i ][ 0 ] = AST__BAD; + +/* Same the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK && result ) this->basemesh = astClone( result ); + + } + +/* Return the result. */ + return result; +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, + const int *axes, int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* NullRegion member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* The base Frame in the supplied Region */ + AstFrame *frm; /* The base Frame in the returned Region */ + AstRegion *bunc; /* The uncertainty in the supplied Region */ + AstRegion *result; /* Returned Region */ + AstRegion *unc; /* The uncertainty in the returned Region */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the base Frame of the encapsulated FrameSet. */ + bfrm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Create a Frame by picking the selected axes from the base Frame of the + encapsulated FrameSet. */ + frm = astPickAxes( bfrm, naxes, axes, NULL ); + +/* Get the uncertainty Region (if any) within the base Frame of the supplied + Region, and select the required axes from it. If the resulting Object + is not a Region, annul it so that the returned Region will have no + uncertainty. */ + if( astTestUnc( this_region ) ) { + bunc = astGetUncFrm( this_region, AST__BASE ); + unc = astPickAxes( bunc, naxes, axes, NULL ); + bunc = astAnnul( bunc ); + + if( ! astIsARegion( unc ) ) unc = astAnnul( unc ); + + } else { + unc = NULL; + } + +/* Create the new NullRegion. */ + result = (AstRegion *) astNullRegion( frm, unc, "", status ); + +/* Free resources */ + frm = astAnnul( frm ); + bfrm = astAnnul( bfrm ); + if( unc ) unc = astAnnul( unc ); + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void GetRegionBounds( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* GetRegionBounds + +* Purpose: +* Returns the bounding box of an un-negated Region in the current Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* void astGetRegionBounds( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* NullRegion member function (over-rides the astGetRegionBounds protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the current Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the current Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the Region. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the current Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the Region. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstNullRegion *this; /* Pointer to NullRegion structure */ + int i; /* Axis index */ + int nc; /* No. of axes in base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the NullRegion structure */ + this = (AstNullRegion *) this_region; + +/* Get the number of base Frame axes. */ + nc = astGetNin( this_region->frameset ); + +/* Set the upper bound less than the lower bound to indicate that the region + contains no points. */ + for( i = 0; i < nc; i++ ) { + lbnd[ i ] = 1.0; + ubnd[ i ] = -1.0; + } + +} + +static AstPointSet *RegMesh( AstRegion *this, int *status ){ +/* +* Name: +* RegMesh + +* Purpose: +* Return a PointSet containing points spread over the boundary of a +* Region. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* AstPointSet *astRegMesh( AstRegion *this, int *status ) + +* Class Membership: +* NullRegion member function (over-rides the astRegMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the current Frame of +* the encapsulated FrameSet. Note, a NullRegion has no boundary and +* so an error is reported if this function is called. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* NULL pointer. + +* Notes: +* - This implementation reports and error and returns NULL since a +* NullRegion has no boundary. +*- +*/ + + astError( AST__INTER, "astRegMesh(%s): The %s class does not implement " + "the astRegMesh method inherited from the Region class " + "(internal AST programming error).", status, astGetClass( this ), + astGetClass( this ) ); + return NULL; +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* NullRegion method (over-rides the astSimplify method inherited +* from the Region class). + +* Description: +* This function invokes the parent Region Simplify method, and then +* performs any further region-specific simplification. +* +* If the Mapping from base to current Frame is not a UnitMap, this +* will include attempting to fit a new Region to the boundary defined +* in the current Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame */ + AstMapping *map; /* Base->current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstRegion *new; /* Simplified Region */ + AstRegion *this; /* Pointer to supplied Region structure */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the supplied Region structure. */ + this = (AstRegion *) this_mapping; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. */ + new = (AstRegion *) (*parent_simplify)( this_mapping, status ); + +/* Is the Mapping from base Frame to current Frame in the Region a + UnitMap? If so, no simplification is possible. */ + map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT ); + if( astIsAUnitMap( map ) ){ + result = astClone( new ); + + } else { + +/* Create a NullRegion from the current Frame. */ + frm = astGetFrame( new->frameset, AST__CURRENT ); + result = (AstMapping *) astNullRegion( frm, astGetUnc( new, 0 ), "", status ); + +/* Free resources. */ + frm = astAnnul( frm ); + + } + map = astAnnul( map ); + new = astAnnul( new ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. + If the supplied Region had no uncertainty, ensure the returned Region + has no uncertainty. Otherwise, return a clone of the supplied pointer. */ + if( result != this_mapping ) astRegOverlay( result, this, 1 ); + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a NullRegion to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* NullRegion member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a NullRegion and a set of points encapsulated in a +* PointSet and transforms the points by setting axis values to +* AST__BAD for all points which are outside the region. Points inside +* the region are copied unchanged from input to output. + +* Parameters: +* this +* Pointer to the NullRegion. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The forward and inverse transformations are identical for a +* Region. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of axes in the Frame represented by the NullRegion. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstNullRegion *this; /* Pointer to NullRegion */ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_out; /* Pointer to output coordinate data */ + double *p; /* Pointer to next axis value */ + int coord; /* Zero-based index for coordinates */ + int ncoord_out; /* No. of coordinates per output point */ + int npoint_out; /* No. of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the NullRegion structure. */ + this = (AstNullRegion *) this_mapping; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, + containing a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* If the NullRegion has been inverted, it represents an infinite region + which includes all points, so just retain the copy of the supplied + PointSet created by the parent Transform method above. If the NullRegion + has not been inverted, it contains no points and so set all output points + bad. */ + if( !astGetNegated( this ) ) { + ncoord_out = astGetNcoord( result ); + npoint_out = astGetNpoint( result ); + ptr_out = astGetPoints( result ); + if ( astOK ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + p = ptr_out[ coord ]; + for ( point = 0; point < npoint_out; point++ ) { + *(p++) = AST__BAD; + } + } + } + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +/* None */ + +/* Destructor. */ +/* ----------- */ +/* None */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for NullRegion objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the NullRegion class to an output Channel. + +* Parameters: +* this +* Pointer to the NullRegion whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstNullRegion *this; /* Pointer to the NullRegion structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the NullRegion structure. */ + this = (AstNullRegion *) this_object; + +/* Write out values representing the instance variables for the + NullRegion class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsANullRegion and astCheckNullRegion functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(NullRegion,Region) +astMAKE_CHECK(NullRegion) + +AstNullRegion *astNullRegion_( void *frame_void, AstRegion *unc, const char *options, int *status, ...) { +/* +*++ +* Name: +c astNullRegion +f AST_NULLREGION + +* Purpose: +* Create a NullRegion. + +* Type: +* Public function. + +* Synopsis: +c #include "nullregion.h" +c AstNullRegion *astNullRegion( AstFrame *frame, AstRegion *unc, const char *options, ... ) +f RESULT = AST_NULLREGION( FRAME, UNC, OPTIONS, STATUS ) + +* Class Membership: +* NullRegion constructor. + +* Description: +* This function creates a new NullRegion and optionally initialises its +* attributes. +* +* A NullRegion is a Region with no bounds. If the Negated attribute of a +* NullRegion is false, the NullRegion represents a Region containing no +* points. If the Negated attribute of a NullRegion is true, the NullRegion +* represents an infinite Region containing all points within the +* coordinate system. + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame in which the region is defined. A deep +* copy is taken of the supplied Frame. This means that any +* subsequent changes made to the Frame using the supplied pointer +* will have no effect the Region. +c unc +f UNC = INTEGER (Given) +* An optional pointer to an existing Region which specifies the +* uncertainties associated with positions in the supplied Frame. +* The uncertainty in any point in the Frame is found by shifting the +* supplied "uncertainty" Region so that it is centred at the point +* being considered. The area covered by the shifted uncertainty +* Region then represents the uncertainty in the position. The +* uncertainty is assumed to be the same for all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created NullRegion. Alternatively, +f a null Object pointer (AST__NULL) +c a NULL Object pointer +* may be supplied, in which case a default uncertainty of zero is +* used. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new NullRegion. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new NullRegion. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astNullRegion() +f AST_NULLREGION = INTEGER +* A pointer to the new NullRegion. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstNullRegion *new; /* Pointer to new NullRegion */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the supplied Frame structure. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the NullRegion, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitNullRegion( NULL, sizeof( AstNullRegion ), !class_init, + &class_vtab, "NullRegion", frame, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new NullRegion's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new NullRegion. */ + return new; +} + +AstNullRegion *astNullRegionId_( void *frame_void, void *unc_void, + const char *options, ... ) { +/* +* Name: +* astNullRegionId_ + +* Purpose: +* Create a NullRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "nullregion.h" +* AstNullRegion *astNullRegionId_( AstFrame *frame, AstRegion *unc, const char *options, ... ) + +* Class Membership: +* NullRegion constructor. + +* Description: +* This function implements the external (public) interface to the +* astNullRegion constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astNullRegion_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astNullRegion_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astNullRegion_. + +* Returned Value: +* The ID value associated with the new NullRegion. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstNullRegion *new; /* Pointer to new NullRegion */ + AstRegion *unc; /* Pointer to Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Frame pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Obtain a Region pointer from the supplied "unc" ID and validate the + pointer to ensure it identifies a valid Region . */ + unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL; + +/* Initialise the NullRegion, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitNullRegion( NULL, sizeof( AstNullRegion ), !class_init, + &class_vtab, "NullRegion", frame, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new NullRegion's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new NullRegion. */ + return astMakeId( new ); +} + +AstNullRegion *astInitNullRegion_( void *mem, size_t size, int init, + AstNullRegionVtab *vtab, const char *name, + AstFrame *frame, AstRegion *unc, int *status ) { +/* +*+ +* Name: +* astInitNullRegion + +* Purpose: +* Initialise a NullRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "nullregion.h" +* AstNullRegion *astInitNullRegion_( void *mem, size_t size, int init, +* AstNullRegionVtab *vtab, const char *name, +* AstFrame *frame, AstRegion *unc ) + +* Class Membership: +* NullRegion initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new NullRegion object. It allocates memory (if necessary) to accommodate +* the NullRegion plus any additional data associated with the derived class. +* It then initialises a NullRegion structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a NullRegion at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the NullRegion is to be initialised. +* This must be of sufficient size to accommodate the NullRegion data +* (sizeof(NullRegion)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the NullRegion (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the NullRegion +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the NullRegion's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new NullRegion. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame in which the region is defined. +* unc +* A pointer to a Region which specifies the uncertainty of positions +* within the supplied Frame. A NULL pointer can be supplied, in which +* case default uncertainties of zero are used. If an uncertainty +* Region is supplied, it must be either a Box, a Circle or an Ellipse, +* and its encapsulated Frame must be related to the Frame supplied +* for parameter "frame" (i.e. astConvert should be able to find a +* Mapping between them). The centre of the supplied uncertainty +* Region is immaterial since it will be re-centred on the point +* being tested before use. A deep copy is taken of the supplied +* Region. + +* Returned Value: +* A pointer to the new NullRegion. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstNullRegion *new; /* Pointer to new NullRegion */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitNullRegionVtab( vtab, name ); + +/* Initialise a Region structure (the parent class) as the first component + within the NullRegion structure, allocating memory if necessary. */ + new = (AstNullRegion *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frame, NULL, unc ); + +/* If an error occurred, clean up by deleting the new NullRegion. */ + if ( !astOK ) new = astDelete( new ); + +/* Return a pointer to the new NullRegion. */ + return new; +} + +AstNullRegion *astLoadNullRegion_( void *mem, size_t size, AstNullRegionVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadNullRegion + +* Purpose: +* Load a NullRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "nullregion.h" +* AstNullRegion *astLoadNullRegion( void *mem, size_t size, AstNullRegionVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* NullRegion loader. + +* Description: +* This function is provided to load a new NullRegion using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* NullRegion structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a NullRegion at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the NullRegion is to be +* loaded. This must be of sufficient size to accommodate the +* NullRegion data (sizeof(NullRegion)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the NullRegion (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the NullRegion structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstNullRegion) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new NullRegion. If this is NULL, a pointer +* to the (static) virtual function table for the NullRegion class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "NullRegion" is used instead. + +* Returned Value: +* A pointer to the new NullRegion. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstNullRegion *new; /* Pointer to the new NullRegion */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this NullRegion. In this case the + NullRegion belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstNullRegion ); + vtab = &class_vtab; + name = "NullRegion"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitNullRegionVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built NullRegion. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "NullRegion" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* If an error occurred, clean up by deleting the new NullRegion. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new NullRegion pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + + + + + + diff --git a/nullregion.h b/nullregion.h new file mode 100644 index 0000000..bcfc501 --- /dev/null +++ b/nullregion.h @@ -0,0 +1,221 @@ +#if !defined( NULLREGION_INCLUDED ) /* Include this file only once */ +#define NULLREGION_INCLUDED +/* +*+ +* Name: +* nullregion.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the NullRegion class. + +* Invocation: +* #include "nullregion.h" + +* Description: +* This include file defines the interface to the NullRegion class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The NullRegion class implement a Region with no boundaries. + +* Inheritance: +* The NullRegion class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 11-OCT-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* NullRegion structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstNullRegion { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +} AstNullRegion; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstNullRegionVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +} AstNullRegionVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstNullRegionGlobals { + AstNullRegionVtab Class_Vtab; + int Class_Init; +} AstNullRegionGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitNullRegionGlobals_( AstNullRegionGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(NullRegion) /* Check class membership */ +astPROTO_ISA(NullRegion) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstNullRegion *astNullRegion_( void *, AstRegion *, const char *, int *, ...); +#else +AstNullRegion *astNullRegionId_( void *, AstRegion *, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstNullRegion *astInitNullRegion_( void *, size_t, int, AstNullRegionVtab *, + const char *, AstFrame *, AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitNullRegionVtab_( AstNullRegionVtab *, const char *, int * ); + +/* Loader. */ +AstNullRegion *astLoadNullRegion_( void *, size_t, AstNullRegionVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckNullRegion(this) astINVOKE_CHECK(NullRegion,this,0) +#define astVerifyNullRegion(this) astINVOKE_CHECK(NullRegion,this,1) + +/* Test class membership. */ +#define astIsANullRegion(this) astINVOKE_ISA(NullRegion,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astNullRegion astINVOKE(F,astNullRegion_) +#else +#define astNullRegion astINVOKE(F,astNullRegionId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitNullRegion(mem,size,init,vtab,name,frame,unc) \ +astINVOKE(O,astInitNullRegion_(mem,size,init,vtab,name,frame,unc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitNullRegionVtab(vtab,name) astINVOKE(V,astInitNullRegionVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadNullRegion(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadNullRegion_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckNullRegion to validate NullRegion pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#endif +#endif + + + + + diff --git a/object.c b/object.c new file mode 100644 index 0000000..f1faaa2 --- /dev/null +++ b/object.c @@ -0,0 +1,8657 @@ +/* +*class++ + +* Name: +* Object + +* Purpose: +* Base class for all AST Objects. + +* Constructor Function: +* None. + +* Description: +* This class is the base class from which all other classes in the +* AST library are derived. It provides all the basic Object +* behaviour and Object manipulation facilities required throughout +* the library. There is no Object constructor, however, as Objects +* on their own are not useful. + +* Inheritance: +* The Object base class does not inherit from any other class. + +* Attributes: +* All Objects have the following attributes: +* +* - Class: Object class name +* - ID: Object identification string +* - Ident: Permanent Object identification string +* - Nobject: Number of Objects in class +* - ObjSize: The in-memory size of the Object in bytes +* - RefCount: Count of active Object pointers +* - UseDefs: Allow use of default values for Object attributes? + +* Functions: +c The following functions may be applied to all Objects: +f The following routines may be applied to all Objects: +* +c - astAnnul: Annul a pointer to an Object +c - astBegin: Begin a new AST context +c - astClear: Clear attribute values for an Object +c - astClone: Clone a pointer to an Object +c - astCopy: Copy an Object +c - astDelete: Delete an Object +c - astEnd: End an AST context +c - astEscapes: Control whether graphical escape sequences are removed +c - astExempt: Exempt an Object pointer from AST context handling +c - astExport: Export an Object pointer to an outer context +c - astGet: Get an attribute value for an Object +c - astHasAttribute: Test if an Object has a named attribute +c - astImport: Import an Object pointer to the current context +c - astIsA: Test class membership +c - astLock: Lock an Object for use by the calling thread +c - astToString: Create an in-memory serialisation of an Object +c - astSame: Do two AST pointers refer to the same Object? +c - astSet: Set attribute values for an Object +c - astSet: Set an attribute value for an Object +c - astShow: Display a textual representation of an Object on standard +c output +c - astTest: Test if an attribute value is set for an Object +c - astTune: Set or get an integer AST tuning parameter +c - astTuneC: Set or get a character AST tuning parameter +c - astUnlock: Unlock an Object for use by other threads +c - astFromString: Re-create an Object from an in-memory serialisation +c - astVersion: Return the verson of the AST library being used. +f - AST_ANNUL: Annul a pointer to an Object +f - AST_BEGIN: Begin a new AST context +f - AST_CLEAR: Clear attribute values for an Object +f - AST_CLONE: Clone a pointer to an Object +f - AST_COPY: Copy an Object +f - AST_DELETE: Delete an Object +f - AST_END: End an AST context +f - AST_ESCAPES: Control whether graphical escape sequences are removed +f - AST_EXEMPT: Exempt an Object pointer from AST context handling +f - AST_EXPORT: Export an Object pointer to an outer context +f - AST_GET: Get an attribute value for an Object +f - AST_HASATTRIBUTE: Test if an Object has a named attribute +f - AST_IMPORT: Import an Object pointer to the current context +f - AST_ISA: Test class membership +f - AST_SAME: Do two AST pointers refer to the same Object? +f - AST_SET: Set attribute values for an Object +f - AST_SET: Set an attribute value for an Object +f - AST_SHOW: Display a textual representation of an Object on standard +f output +f - AST_TEST: Test if an attribute value is set for an Object +f - AST_TUNE: Set or get an integer AST tuning parameter +f - AST_TUNEC: Set or get a character AST tuning parameter +f - AST_VERSION: Return the verson of the AST library being used. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 1-FEB-1996 (RFWS): +* Original version. +* 22-APR-1996 (RFWS): +* Added attribute setting functions. +* 2-JUL-1996 (RFWS): +* Added functions to support the external interface (using +* identfiers). +* 10-SEP-1996 (RFWS): +* Added I/O facilities. +* 30-MAY-1997 (RFWS): +* Add ID attribute. +* 14-JUL-1997 (RFWS): +* Add astExempt function. +* 14-OCT-1997 (RFWS): +* Fixed uninitialised use of "dynamic" in astCopy_. +* 14-NOV-1997 (RFWS): +* Remove the subversive C "strtok" function. +* 20-JAN-1998 (RFWS): +* Make the astClear and astRVSet methods virtual. +* 29-APR-1998 (RFWS): +* Fixed bug in algorithm for encoding Object IDs. +* 15-SEP-1999 (RFWS) +* Made astAnnulId accessible from protected code. +* 12-APR-2000 (DSB): +* Zero all memory allocated for a new Object in InitObject before +* storing any new values in the memory. +* 3-APR-2001 (DSB): +* Added the Ident attribute. +* 28-SEP-2001 (DSB): +* Modified VSet to ensure a non-null string always follows the equal +* sign in the attribute setting passed to SetAttrib. +* 27-NOV-2002 (DSB): +* Modified astShow to use astWrite instead of astDump, so that +* invocations of astShow will be included in the count of the +* number of invocations of astWrite returned by astWriteInvocations. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitObjectVtab +* method. +* 8-FEB-2004 (DSB): +* Added astEscapes. +* 10-FEB-2004 (DSB): +* Added debug conditional code to keep track of memory leaks. +* 22-AUG-2004 (DSB): +* Added astEqual +* 27-JAN-2005 (DSB): +* Correct use of ->ident pointers, and added further DEBUG blocks. +* 11-MAR-2005 (DSB): +* Added attribute UseDefs. +* 14-FEB-2006 (DSB): +* Added attribute ObjSize. +* 23-FEB-2006 (DSB): +* Added MemoryCaching tuning parameter. +* 27-FEB-2006 (DSB): +* Include Objects returned by astCopy in the ObjectCaching system. +* 28-FEB-2006 (DSB): +* Use astOK to protect against errors within astGrow. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 26-MAY-2006 (DSB): +* Correct handling of commas within the attribute value supplied +* to astSetC. +* 30-MAY-2006 (DSB): +* Correct the correction made to handle commas within attribute +* 6-JUN-2007 (DSB): +* Fix harmless compiler warnings. +* 21-JUN-2007 (DSB): +* In astSet, ignore trailing spaces in the attribute name. +* 22-JUN-2007 (DSB): +* - Make astVSet return a pointer to dynamic memory holding the +* expanded setting string. +* - Add astSetVtab, and astCast. +* 27-JUN-2007 (DSB): +* Modify astInitObject so that it ignores the supplied "size" value +* if some memory is supplied. +* 2-JULY-2007 (DSB): +* Fix memory access problem in VSet. +* 20-SEP-2007 (DSB): +* In astDelete, ensure the error status is reset before calling +* astGrow to extend the vtab free list. +* 22-APR-2008 (DSB): +* Added astSame. +* 24-OCT-2008 (DSB): +* Prevent a mutex deadlock that could occur when annulling an +* Object ID. +* 28-JAN-2008 (DSB): +* Allow unlocked objects to be annulled using astAnnul. +* 14-OCT-2009 (DSB): +* Modify astCast to make it a virtual function and add type +* checking. +* 7-APR-2010 (DSB): +* Added method astHasAttribute. +* 24-AUG-2010 (DSB): +* Allow commas to be included in attribute values supplied to +* astSet or astVSet by putting quotes around the attribute value. +* 16-JUN-2011 (DSB): +* Added component "iref" to the Object structure. This is an +* integer identifier for each object that is unique within the +* class of the object. Useful for debugging. +* 22-JUL-2011 (DSB): +* Add methods astSetProxy and astGetProxy. +* 2-SEP-2011 (DSB): +* Add functions astToString and astFromString. +* 13-SEP-2013 (DSB): +* Report an error in astAnnul if the supplied object handle is owned by +* a different thread. Note, the Object itself does not need to be owned +* by the current thread, since it should be possible for a thread to +* relinquish a pointer to an object (i.e. a handle) without actually +* owning the object itself. +* 6-JAN-2014 (DSB): +* Added method astEnvSet. +* 9-APR-2015 (DSB): +* Modify VSet to handle "%s" setting strings (i.e. where the whole +* list of settings is provided as a single variable argument). +* This is needed because supplying the while settings string in +* place of "%s" is considered a security issue by many compilers. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Object + +#define INVALID_CONTEXT -1 /* Context value for handles that have no + associated Object */ +#define UNOWNED_CONTEXT -2 /* Context value for handles for objects + that are not locked by any thread */ + + +/* Include files. */ +/* ============== */ + +/* Configuration information */ +/* ------------------------ */ +#include "version.h" /* Version numbers */ +#if HAVE_CONFIG_H +#include +#endif + +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "channel.h" /* I/O channels */ +#include "object.h" /* Interface definition for this class */ +#include "plot.h" /* Plot class (for astStripEscapes) */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Type Definitions */ +/* ================ */ +/* Structure used to pass data between astToString/FromString and the + corresponding Channel source and sink functions. */ +typedef struct StringData { + char *ptr; /* Pointer to serialisation text */ + char *buff; /* Pointer to a buffer for a single line of text */ + int len; /* Current length of serialisation text */ +} StringData; + +/* Module Variables. */ +/* ================= */ + +/* The following globals have the same values in all threads and so do + not need to be in thread-specific data. */ +/* ------------------------------------------------------------------ */ + +/* Character-valued tuning parameters. */ +#define MAXLEN_TUNEC 200 +static char hrdel[ MAXLEN_TUNEC ] = "%-%^50+%s70+h%+"; +static char mndel[ MAXLEN_TUNEC ] = "%-%^50+%s70+m%+"; +static char scdel[ MAXLEN_TUNEC ] = "%-%^50+%s70+s%+"; +static char dgdel[ MAXLEN_TUNEC ] = "%-%^53+%s60+o%+"; +static char amdel[ MAXLEN_TUNEC ] = "%-%^20+%s85+'%+"; +static char asdel[ MAXLEN_TUNEC ] = "%-%^20+%s85+\"%+"; +static char exdel[ MAXLEN_TUNEC ] = "10%-%^50+%s70+"; + +/* A pointer full of zeros. */ +static AstObject *zero_ptr; + +/* A flag which indicates what should happen when an AST Object is + deleted. If this flag is non-zero, the memory used by the Object is + not freed, but a pointer to it is placed on the end of a list of free + memory chunk pointers so that the memory can be re-used if necessary + avoiding the need to re-allocate memory with malloc (which is slow). + A separate list of free memory chunks is kept for each class because + each class object will require chunks of a different size. Pointers + to these lists are stored in the virtual function table associated + with each class. All memory on these lists is freed when object + caching is switched off via the astTune function. */ +static int object_caching = 0; + +/* Set up global data access, mutexes, etc, needed for thread safety. */ +#ifdef THREAD_SAFE + +/* Define the initial values for the global data for this module. */ +#define GLOBAL_inits \ + globals->Retain_Esc = 0; \ + globals->Context_Level = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->AstGetC_Init = 0; \ + globals->AstGetC_Istr = 0; \ + globals->Active_Handles = NULL; \ + globals->Class_Init = 0; \ + globals->Nvtab = 0; \ + globals->Known_Vtabs = NULL; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Object) + +/* Define macros for accessing each item of thread specific global data. */ +#define retain_esc astGLOBAL(Object,Retain_Esc) +#define context_level astGLOBAL(Object,Context_Level) +#define active_handles astGLOBAL(Object,Active_Handles) +#define getattrib_buff astGLOBAL(Object,GetAttrib_Buff) +#define astgetc_strings astGLOBAL(Object,AstGetC_Strings) +#define astgetc_istr astGLOBAL(Object,AstGetC_Istr) +#define astgetc_init astGLOBAL(Object,AstGetC_Init) +#define class_init astGLOBAL(Object,Class_Init) +#define class_vtab astGLOBAL(Object,Class_Vtab) +#define nvtab astGLOBAL(Object,Nvtab) +#define known_vtabs astGLOBAL(Object,Known_Vtabs) + +/* mutex1 is used to prevent tuning parameters being accessed by more + than one thread at any one time. */ +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ); +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ); + +/* mutex2 is used to prevent the global lists of object handles being + accessed by more than one thread at any one time. */ +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* Each Object contains two mutexes. The primary mutex (mutex1) is used + to guard access to all aspects of the Object except for the "locker" + and "ref_count" items. The secondary mutex (mutex2) is used to guard + access to these two remaining items. We need this secondary mutex + since the "locker" and "ref_count" items need to be accessable within + a thread even if that thread has not locked the Object using astLock. + Define macros for accessing these two mutexes. */ +#define LOCK_PMUTEX(this) (pthread_mutex_lock(&((this)->mutex1))) +#define UNLOCK_PMUTEX(this) (pthread_mutex_unlock(&((this)->mutex1))) +#define LOCK_SMUTEX(this) (pthread_mutex_lock(&((this)->mutex2))) +#define UNLOCK_SMUTEX(this) (pthread_mutex_unlock(&((this)->mutex2))) + + + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Define the class virtual function table and its initialisation flag as + static variables. */ +static int class_init = 0; /* Virtual function table initialised? */ +static AstObjectVtab class_vtab; /* Virtual function table */ + +/* A list of pointers to all the known class virtual function tables. */ +static int nvtab = 0; +static AstObjectVtab **known_vtabs = NULL; + +/* A flag which indicates if AST functions which return text strings + should retain any graphical escape sequences (as interpreted by the + Plot class). */ +static int retain_esc = 0; + +/* Context level (Begin/End/Exempt/Export) */ +static int context_level = 0; + +/* Array of list heads for each context (each list is a list of Handle + structures). */ +static int *active_handles = NULL; + +/* String returned by GetAttrib. */ +static char getattrib_buff[ AST__GETATTRIB_BUFF_LEN + 1 ] = ""; + +/* Pointers to string buffers returned by astGetC. */ +static char *astgetc_strings[ AST__ASTGETC_MAX_STRINGS ]; + +/* Offset of next string in "AstGetC_Strings" */ +static int astgetc_istr = 0; + +/* "AstGetC_Strings" array initialised? */ +static int astgetc_init = 0; + +/* Null macros for mutex locking and unlocking */ +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 +#define LOCK_PMUTEX(this) +#define LOCK_SMUTEX(this) +#define UNLOCK_PMUTEX(this) +#define UNLOCK_SMUTEX(this) + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstObject *Cast( AstObject *, AstObject *, int * ); +static const char *GetID( AstObject *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetIdent( AstObject *, int * ); +static const char *Get( AstObject *, const char *, int * ); +static const char *FromStringSource( void ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); +static int HasAttribute( AstObject *, const char *, int * ); +static int Same( AstObject *, AstObject *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestID( AstObject *, int * ); +static int TestIdent( AstObject *, int * ); +static unsigned long Magic( const AstObject *, size_t, int * ); +static void CleanAttribs( AstObject *, int * ); +static void Clear( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearIdent( AstObject *, int * ); +static void ClearID( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void EmptyObjectCache( int * ); +static void ToStringSink( const char * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetID( AstObject *, const char *, int * ); +static void SetIdent( AstObject *, const char *, int * ); +static void Show( AstObject *, int * ); +static void VSet( AstObject *, const char *, char **, va_list, int * ); +static void EnvSet( AstObject *, int * ); + +static int GetUseDefs( AstObject *, int * ); +static int TestUseDefs( AstObject *, int * ); +static void ClearUseDefs( AstObject *, int * ); +static void SetUseDefs( AstObject *, int, int * ); + +#if defined(THREAD_SAFE) +static void ChangeThreadVtab( AstObject *, int * ); +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +AstObject *astAnnul_( AstObject *this, int *status ) { +/* +*++ +* Name: +c astAnnul +f AST_ANNUL + +* Purpose: +* Annul a pointer to an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astAnnul( AstObject *this ) +f CALL AST_ANNUL( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function annuls a pointer to an Object so that it is no +f This routine annuls a pointer to an Object so that it is no +* longer recognised as a valid pointer by the AST library. Any +* resources associated with the pointer are released and made +* available for re-use. +* +c This function also decrements the Object's RefCount attribute by +f This routine also decrements the Object's RefCount attribute by +* one. If this attribute reaches zero (which happens when the last +* pointer to the Object is annulled), then the Object is deleted. + +* Parameters: +c this +c The Object pointer to be annulled. +f THIS = INTEGER (Given and Returned) +f The Object pointer to be annulled. A null pointer value (AST__NULL) +f is always returned. +f STATUS = INTEGER (Given and Returned) +f The global status. + +c Returned Value: +c astAnnul() +c A null Object pointer (AST__NULL) is always returned. +c +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - This function will attempt to annul the pointer even if the +c Object is not currently locked by the calling thread (see astLock). +c - This function attempts to execute even if the AST error +c status is set +f - This routine attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. In +* particular, it will fail if the pointer suppled is not valid, +* but this will only be reported if the error status is clear on +* entry. +*-- +*/ + +/* Check the pointer to ensure it identifies a valid Object (this + generates an error if it doesn't). */ + if ( !astIsAObject( this ) ) return NULL; + +/* Get a lock on the object's secondary mutex. This mutex guards access + to the "ref_count" and "locker" components of the AstObject structure. */ + LOCK_SMUTEX(this); + +#ifdef MEM_DEBUG + { int rc; + char buf[100]; + rc = this->ref_count; + sprintf(buf,"annulled (refcnt: %d -> %d)", rc, rc-1 ); + astMemoryUse( this, buf ); + } +#endif + +/* Decrement the Object's reference count. */ + --(this->ref_count); + +/* Unlock the object's secondary mutex. */ + UNLOCK_SMUTEX(this); + +/* Decrement the Object's reference count and delete the Object if + necessary. */ + if ( !this->ref_count ) (void) astDelete( this ); + +/* Always return NULL. */ + return NULL; +} + +static AstObject *Cast( AstObject *this, AstObject *obj, int *status ) { +/* +*+ +* Name: +* astCast + +* Purpose: +* Cast an Object into an instance of a sub-class. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* AstObject *astCast( AstObject *this, AstObject *obj ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. Specifically, if "this" and "new" are +* of the same class, a copy of "this" is returned. If "this" is an +* instance of a subclass of "obj", then a copy of the component +* of "this" that matches the class of "obj" is returned. Otherwise, +* a NULL pointer is returned without error. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. NULL if "this" is not a sub-class of +* "obj", or if an error occurs. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstObject *new; + int generation_gap; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Check pointer have been supplied. */ + if( this && obj ) { + +/* See how many steps up the class inheritance ladder it is from "this" to + "obj". A positive value is returned if "this" is a sub-class of "obj". + A negative value is returned if "obj" is a sub-class of "this". Zero + is returned if they are of the same class. AST__COUSIN is returned if + they share a common ancestor but are not on the same line of descent. */ + generation_gap = astClassCompare( astVTAB( this ), astVTAB( obj ) ); + +/* If the two objects are of the same class, just return a copy of + "this". */ + if( generation_gap == 0 ) { + new = astCopy( this ); + +/* If "this" is a subclass of "obj", return a deep copy of "this" cast + into the class of "obj". */ + } else if( generation_gap != AST__COUSIN && generation_gap > 0 ) { + new = astCastCopy( this, obj ); + + } + } + +/* Return the new pointer. */ + return new; +} + +AstObject *astCastCopy_( AstObject *this, AstObject *obj, int *status ) { +/* +*+ +* Name: +* astCastCopy + +* Purpose: +* Cast an Object into an instance of a sub-class, without type-checking. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astCastCopy( AstObject *this, AstObject *obj ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. No checks are performed that "this" is +* a sub-class of "obj". +* +* It works by temporarily changing the vtab in "this" to be the same +* as in "obj", and then doing a deep copy, and then re-instating the +* original vtab. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstObject *new; + AstObjectVtab *this_vtab; + size_t this_size; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Check pointer have been supplied. */ + if( this && obj ) { + +/* Save a pointer to the original virtual function tables for "this". */ + this_vtab = astVTAB( this ); + +/* Temporarily change the vtab of "this" to that of "obJ". */ + this->vtab = astVTAB( obj ); + +/* Temporarily change the size of "this" to be the size of "obj". */ + this_size = this->size; + this->size = obj->size; + +/* Now take a copy of the object (now considered to be an instance of the + class specified by "obj"). */ + new = astCopy( this ); + +/* Re-instate the original Object vtab and size. */ + this->vtab = this_vtab; + this->size = this_size; + +/* The sub-clas to which "this" originally belonged may have extended the + range of values allowed for one or more of the attributes inherited from + the "obj" class. This means that the current attribute values stored + in the returned object may be inappropriate for the class of "obj". An + example is the System attribute defined by the Frame class, and extended + by sub-classes of Frame. So we now call astCleanAttribs to ensure that + any inappropriate attribute values are cleared in the returned object. */ + astCleanAttribs( new ); + } + +/* Return the new pointer. */ + return new; +} + +#if defined(THREAD_SAFE) +static void ChangeThreadVtab( AstObject *this, int *status ){ +/* +* Name: +* ChangeThreadVtab + +* Purpose: +* Modify an Object structure so that it refers to a vtab created by +* the currently executing thread. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void ChangeThreadVtab( AstObject *this, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* Each Object structure contains a pointer to a virtual function +* table (vtab) that identifies information about the class to +* which the Object belongs (function pointers, Object caches, +* etc). In order to avoid use of mutexes (which can slow down AST +* applications enormously), each thread has its own set of vtab +* structures (one for each AST class) stored in thread-specific +* data. Each time an Object is locked by the currently executing +* thread, this function should be called to change the vtab pointer +* in the Object to refer to the vtab relevant to the currently +* executing thread. + +* Parameters: +* this +* Pointer to the Object. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + const char *class; + int i; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific data for the currently executing thread. */ + astGET_GLOBALS(this); + +/* Get the class name for the supplied Object. This uses the existing + vtab pointer in the Object structure to locate the required GetClass + method and the class name. This vtab pointer may be for a vtab created + by a different thread to the one currently executing, but this shouldn't + matter since we are not modifying the vtab contents. */ + class = astGetClass( this ); + +/* Check a class name was obtained */ + if( class ) { + +/* Loop round the vtab structures created by the currently executing thread. */ + for( i = 0; i < nvtab; i++ ) { + +/* If the current vtab is for a class that matches the class of the + supplied Object, then store a pointer to the vtab in the Object + structure, and exit. */ + if( !strcmp( class, known_vtabs[ i ]->class ) ) { + this->vtab = known_vtabs[ i ]; + break; + } + } + } +} +#endif + +AstObject *astCheckLock_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astCheckLock + +* Purpose: +* Check that supplied Object is locked by the calling thread. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astCheckLock( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function reports an error if the supplied object has not +* previously been locked (using astLock) by the calling thread. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* A copy of the supplied pointer ("this") is returned. The Object +* reference count is not changed. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. + +*- +*/ + +/* This function does nothing in the non-threads version of libast. */ +#if defined(THREAD_SAFE) + +/* Local Variables; */ + AstObject *fail; + +/* Check the supplied pointer. */ + if( this ) { + +/* First use the private ManageLock function rather than the virtual + astManageLock method to check the top level Object is locked for use + by the current thread. This saves time and allows a more appropriate + error message to be issued. */ + if( ManageLock( this, AST__CHECKLOCK, 0, NULL, status ) ) { + if( astOK ) { + astError( AST__LCKERR, "astCheckLock(%s): The supplied %s cannot " + "be used since it is not locked for use by the current " + "thread (programming error).", status, astGetClass( this ), + astGetClass( this ) ); + } + +/* If the top level Object is locked, now use the virtual astManageLock + method to check any objects contained within the top level Object. */ + } else if( astManageLock( this, AST__CHECKLOCK, 0, &fail ) ) { + if( astOK ) { + astError( AST__LCKERR, "astCheckLock(%s): The supplied %s cannot " + "be used since a %s contained within the %s is not " + "locked for use by the current thread (programming " + "error).", status, astGetClass( this ), + astGetClass( this ), astGetClass( fail ), + astGetClass( this ) ); + } + } + } +#endif + +/* Return the supploed pointer. */ + return this; + +} + +int astClassCompare_( AstObjectVtab *class1, AstObjectVtab *class2, + int *status ) { +/* +*+ +* Name: +* astClassCompare + +* Purpose: +* Determine the relationship between two AST classes. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astClassCompare( AstObjectVtab *class1, AstObjectVtab *class2 ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the number of steps up the class inheritance +* ladder from the class specified by "class1" to the class specified +* by "class2". + +* Parameters: +* class1 +* Pointer to a virtual function table describing the first AST class. +* class2 +* Pointer to a virtual function table describing the second AST class. + +* Returned Value: +* The generation gap between "class1" and "class2". The result will be +* positive if "class1" is a subclass of "class2", negative if "class2" +* is a subclass of "class1", zero if they are of the same class (or +* an error occurs), or AST__COUSIN if they are not on the same line +* of descent. + +*- +*/ + +/* Local Variables: */ + AstClassIdentifier *class1_id; + AstClassIdentifier *class2_id; + AstClassIdentifier *id; + int *class1_check; + int *class2_check; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Check pointer have been supplied. */ + if( class1 && class2 ) { + +/* Get pointers to the AstClassIdentifier that identifies the top-level + class of each vtab. */ + class1_id = class1->top_id; + class2_id = class2->top_id; + +/* Class membership is specified by the "check" value in each class + identifier. Get the check values for both vtabs. */ + class1_check = class1_id->check; + class2_check = class2_id->check; + +/* Try walking up the class heirarchy of "class1" until the class of + "class2" is reached. The top-level AstObject class has a NULL "parent" + pointer in its class identifier structure. */ + id = class1_id; + while( id && ( id->check != class2_check ) ) { + id = id->parent; + result++; + } + +/* If "class1" is not a subclass of "class2", try walking up the class + heirarchy of "class2" until the class of "class1" is reached. */ + if( !id ) { + result = 0; + id = class2_id; + while( id && ( id->check != class1_check ) ) { + id = id->parent; + result--; + } + +/* If "class2" is not a subclass of "class1", return AST__COUSIN. */ + if( !id ) result = AST__COUSIN; + } + } + +/* Return the generation gap. */ + return result; +} + +static void CleanAttribs( AstObject *this_object, int *status ) { +/* +*+ +* Name: +* astCleanAttribs + +* Purpose: +* Clear any invalid set attribute values. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astCleanAttribs( AstObject *this, int *status ) + +* Class Membership: +* Object method. + +* Description: +* This function clears any attributes that are currently set to +* invalid values in the supplied object. This can happen for instance +* when an object is cast into an instance of a parent class using +* astCast, since sub-classes can extend the range of valid values +* an attribute can take. + +* Parameters: +* this +* Pointer to the Object to be cleaned. +*- +*/ + +/* The base Object class has no attributes that need cleaning. */ + +} + +static void Clear( AstObject *this, const char *attrib, int *status ) { +/* +*++ +* Name: +c astClear +f AST_CLEAR + +* Purpose: +* Clear attribute values for an Object. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "object.h" +c void astClear( AstObject *this, const char *attrib ) +f CALL AST_CLEAR( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function clears the values of a specified set of attributes +f This routine clears the values of a specified set of attributes +* for an Object. Clearing an attribute cancels any value that has +* previously been explicitly set for it, so that the standard +* default attribute value will subsequently be used instead. This +c also causes the astTest function to return the value zero for +f also causes the AST_TEST function to return the value .FALSE. for +* the attribute, indicating that no value has been set. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing a +c comma-separated list of the names of the attributes to be cleared. +f A character string containing a comma-separated list of the +f names of the attributes to be cleared. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +* - It does no harm to clear an attribute whose value has not been +* set. +* - An error will result if an attempt is made to clear the value +* of a read-only attribute. +*-- +*/ + +/* Local Variables: */ + char *buff; /* Pointer to character buffer */ + char *name; /* Pointer to individual attribute name */ + char *name_end; /* Pointer to null at end of name */ + int i; /* Loop counter for characters */ + int j; /* Non-blank character count */ + int len; /* Length of attrib string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the length of the attrib string. */ + len = (int) strlen( attrib ); + if ( len != 0 ) { + +/* Allocate memory and store a copy of the string. */ + buff = astStore( NULL, attrib, (size_t) ( len + 1 ) ); + if ( astOK ) { + +/* Loop to process each element in the comma-separated list. */ + name = buff; + while ( name ) { + +/* Change the comma at the end of each element to a null to terminate + the name. */ + if ( ( name_end = strchr( name, ',' ) ) ) *name_end = '\0'; + +/* Remove white space and upper case characters from the attribute + name. */ + for ( i = j = 0; name[ i ]; i++ ) { + if ( !isspace( name[ i ] ) ) name[ j++ ] = tolower( name[ i ] ); + } + +/* Terminate the attribute name and pass it to astClearAttrib to clear + the attribute (unless it is all blank, in which case we ignore + it). */ + name[ j ] = '\0'; + if ( j ) astClearAttrib( this, name ); + +/* Check for errors and abort if any clear operation fails. Otherwise, + process the next attribute. */ + if ( !astOK ) break; + name = name_end ? name_end + 1 : NULL; + } + } + +/* Free the memory allocated for the string buffer. */ + buff = astFree( buff ); + } +} + +static void ClearAttrib( AstObject *this, const char *attrib, int *status ) { +/* +*+ +* Name: +* astClearAttrib + +* Purpose: +* Clear an attribute value for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astClearAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Object method. + +* Description: +* This function clears the value of a specified attribute for an +* Object, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. + +* Notes: +* - The Object class does not have any writable attributes, so +* this function merely reports an error. It is intended to be +* extended by other class definitions. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* ID. */ +/* --- */ + if ( !strcmp( attrib, "id" ) ) { + astClearID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + astClearIdent( this ); + +/* UseDefs. */ +/* -------- */ + } else if ( !strcmp( attrib, "usedefs" ) ) { + astClearUseDefs( this ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute string matches any of the read-only + attributes of this class. If it does, then report an error. */ + } else if ( !strcmp( attrib, "class" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "objsize" ) || + !strcmp( attrib, "refcount" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Since no writable attributes are defined for the Object class, any + attempt to clear a value for anything else is also an error. */ + } else { + astError( AST__BADAT, "astClear: The attribute name \"%s\" is invalid " + "for a %s.", status, attrib, astGetClass( this ) ); + } +} + +AstObject *astClone_( AstObject *this, int *status ) { +/* +*++ +* Name: +c astClone +f AST_CLONE + +* Purpose: +* Clone (duplicate) an Object pointer. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astClone( AstObject *this ) +f RESULT = AST_CLONE( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a duplicate pointer to an existing +* Object. It also increments the Object's RefCount attribute to +* keep track of how many pointers have been issued. +* +* Note that this function is NOT equivalent to an assignment +* statement, as in general the two pointers will not have the same +* value. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Original pointer to the Object. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astClone() +f AST_CLONE = INTEGER +* A duplicate pointer to the same Object. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a lock on the object's secondary mutex. This mutex guards access + to the "ref_count" and "locker" components of the AstObject structure. */ + LOCK_SMUTEX(this); + +#ifdef MEM_DEBUG + { int rc; + char buf[100]; + rc = this->ref_count; + sprintf(buf,"cloned (refcnt: %d -> %d)", rc, rc+1 ); + astMemoryUse( this, buf ); + } +#endif + +/* Increment the Object's reference count. */ + this->ref_count++; + +/* Unlock the object's secondary mutex. */ + UNLOCK_SMUTEX(this); + +/* Return a new pointer to the Object. */ + return this; +} + +AstObject *astCopy_( const AstObject *this, int *status ) { +/* +*++ +* Name: +c astCopy +f AST_COPY + +* Purpose: +* Copy an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astCopy( const AstObject *this ) +f RESULT = AST_COPY( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +* This function creates a copy of an Object and returns a pointer +* to the resulting new Object. It makes a "deep" copy, which +* contains no references to any other Object (i.e. if the original +* Object contains references to other Objects, then the actual +* data are copied, not simply the references). This means that +* modifications may safely be made to the copy without indirectly +* affecting any other Object. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object to be copied. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astCopy() +f AST_COPY = INTEGER +* Pointer to the new Object. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstObject *new; /* Pointer to new object */ + AstObjectVtab *vtab; /* Pointer to object vtab */ + int i; /* Loop counter for copy constructors */ + +/* Initiallise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Re-use cached memory, or allocate new memory using the size of the input + object, to store the output Object. */ + + vtab = this->vtab; + if( object_caching ){ + + if( vtab->nfree > 0 ) { + new = vtab->free_list[ --(vtab->nfree) ]; + vtab->free_list[ vtab->nfree ] = NULL; + } else { + new = astMalloc( this->size ); + } + + } else { + new = astMalloc( this->size ); + } + + if ( astOK ) { + +/* Perform an initial byte-by-byte copy of the entire object + structure. */ + (void) memcpy( (void *) new, (const void *) this, this->size ); + +/* Initialise any components of the new Object structure that need to + differ from the input. */ + new->check = Magic( new, new->size, status ); + new->dynamic = 1; + new->ref_count = 1; + new->id = NULL; /* ID attribute is not copied (but Ident is copied) */ + new->proxy = NULL; + +/* Copy the persistent identifier string. */ + if( this->ident ) { + new->ident = astStore( NULL, this->ident, strlen( this->ident ) + 1 ); + } + +/* Create a new mutex for the new Object, and lock it for use by the + current thread. */ +#ifdef THREAD_SAFE + if( pthread_mutex_init( &(new->mutex1), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex1 for the new Object.", status, + vtab->class ); + } + if( pthread_mutex_init( &(new->mutex2), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex2 for the new Object.", status, + vtab->class ); + } + new->locker = -1; + new->globals = NULL; + (void) ManageLock( new, AST__LOCK, 0, NULL, status ); +#endif + +/* Loop to execute any copy constructors declared by derived classes. */ + for ( i = 0; i < vtab->ncopy; i++ ) { + +/* Invoke each copy constructor in turn. */ + (*vtab->copy[ i ])( this, new, status ); + +/* If any copy constructor fails, work backwards through the + corresponding destructor functions, invoking each in turn to undo + the copy operations that have been completed so far. */ + if ( !astOK ) { + for ( ; i >= 0; i-- ) { + (*vtab->delete[ i ])( new, status ); + } + +/* Zero the entire new Object structure (to prevent accidental re-use + of any of its values after deletion). */ + (void) memset( new, 0, new->size ); + +/* Free the Object's memory and ensure that a NULL pointer will be + returned. */ + new = astFree( new ); + +/* Quit trying to copy the Object. */ + break; + } + } + } + +/* If OK, increment the count of active objects. */ + if ( astOK ) vtab->nobject++; + +/* Return a pointer to the new Object. */ + return new; +} + +AstObject *astDelete_( AstObject *this, int *status ) { +/* +*++ +* Name: +c astDelete +f AST_DELETE + +* Purpose: +* Delete an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astDelete( AstObject *this ) +f CALL AST_DELETE( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function deletes an Object, freeing all resources +f This routine deletes an Object, freeing all resources +* associated with it and rendering any remaining pointers to the +* Object invalid. +* +* Note that deletion is unconditional, regardless of whether other +* pointers to the Object are still in use (possibly within other +* Objects). A safer approach is to defer deletion, until all +c references to an Object have expired, by using astBegin/astEnd +c (together with astClone and astAnnul if necessary). +f references to an Object have expired, by using AST_BEGIN/AST_END +f (together with AST_CLONE and AST_ANNUL if necessary). + +* Parameters: +c this +c Pointer to the Object to be deleted. +f THIS = INTEGER (Given and Returned) +f Pointer to the Object to be deleted. A null pointer value +f (AST__NULL) is always returned. +f STATUS = INTEGER (Given and Returned) +f The global status. + +c Returned Value: +c astDelete() +c A null Object pointer (AST__NULL) is always returned. +c +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - This function attempts to execute even if the AST error status +c is set +f - This routine attempts to execute even if STATUS is set to an error +f value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +*-- +*/ + +/* Local Variables: */ + AstObjectVtab *vtab; /* Pointer to virtual function table */ + int dynamic; /* Was memory allocated dynamically? */ + int i; /* Loop counter for destructors */ + int ifree; /* Index of next slot on free list */ + int status_value; /* AST error status value */ + size_t size; /* Object size */ + +/* Check the pointer to ensure it identifies a valid Object (this + generates an error if it doesn't). */ + if ( !astIsAObject( this ) ) return NULL; + +/* Loop through all the destructors associated with the Object by derived + classes (working up the class hierarchy). */ + for ( i = this->vtab->ndelete - 1; i >= 0; i-- ) { + +/* Invoke each destructor in turn. Attempt to continue even if destructors + fail. */ + ( *this->vtab->delete[ i ] )( this, status ); + } + +/* Free the ID strings. */ + this->id = astFree( this->id ); + this->ident = astFree( this->ident ); + +/* Attempt to unlock the Object and destroy its mutexes. */ +#if defined(THREAD_SAFE) + (void) ManageLock( this, AST__UNLOCK, 0, NULL, status ); + pthread_mutex_destroy( &(this->mutex1) ); + pthread_mutex_destroy( &(this->mutex2) ); +#endif + +/* Save the virtual function table address and note if the Object's + memory was allocated dynamically. Also note its size. */ + vtab = this->vtab; + dynamic = this->dynamic; + size = this->size; + +/* Zero the entire Object structure (to prevent accidental re-use of + any of its values after deletion). */ + (void) memset( this, 0, size ); + +/* If necessary, free the Object's memory. If object caching is switched + on, the memory is not in fact freed; it is merely placed onto the end + of the list of free memory blocks included in the virtual function table + of the AST class concerned. astGrow returns immediately if an error + has already occurred, so we need to reset the error status explicitly + before calling astGrow. */ + if ( dynamic ) { + if( object_caching ) { + ifree = (vtab->nfree)++; + + status_value = astStatus; + astClearStatus; + vtab->free_list = astGrow( vtab->free_list, vtab->nfree, + sizeof(AstObject *) ); + astSetStatus( status_value ); + + if( vtab->free_list ) vtab->free_list[ ifree ] = this; + } else { + (void) astFree( this ); + } + } + +/* Decrement the count of active Objects. */ + vtab->nobject--; + +/* Always return NULL. */ + return NULL; +} + +static void Dump( AstObject *this, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astDump + +* Purpose: +* Write an Object to a Channel. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astDump( AstObject *this, AstChannel *channel ) + +* Class Membership: +* Object method. + +* Description: +* This function writes an Object to a Channel, appending it to any +* previous Objects written to that Channel. + +* Parameters: +* this +* Pointer to the Object to be written. +* channel +* Pointer to the output Channel. +*- +*/ + +/* Local Variables: */ + AstObjectVtab *vtab; /* Pointer to virtual function table */ + const char *sval; /* Pointer to string value */ + int helpful; /* Helpful to show value even if not set? */ + int idump; /* Loop counter for dump functions */ + int ival; /* Attribute value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Write an initial "Begin" item, giving the class name of the Object + being written. Also supply a pointer to the comment associated with + the most recently-declared dump function in the Object's virtual + function table. This should describe the class to which the Object + belongs (assuming it has correctly declared its dump function). */ + astWriteBegin( channel, astGetClass( this ), + this->vtab->dump_comment[ this->vtab->ndump - 1 ] ); + +/* Write out instance variable information for the base Object + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* ID. */ +/* --- */ + set = TestID( this, status ); + sval = set ? GetID( this, status ) : astGetID( this ); + +/* Don't show an un-set ID value if it is blank. */ + helpful = ( sval && *sval ); + astWriteString( channel, "ID", set, helpful, sval, + "Object identification string" ); + +/* Ident. */ +/* --- */ + set = TestIdent( this, status ); + sval = set ? GetIdent( this, status ) : astGetIdent( this ); + +/* Don't show an un-set Ident value if it is blank. */ + helpful = ( sval && *sval ); + astWriteString( channel, "Ident", set, helpful, sval, + "Permanent Object identification string" ); + +/* UseDefs */ +/* ------- */ + set = TestUseDefs( this, status ); + ival = set ? GetUseDefs( this, status ) : astGetUseDefs( this ); + astWriteInt( channel, "UseDfs", set, 0, ival, + ival ? "Default attribute values can be used" : + "Default values cannot be used" ); + +/* RefCnt. */ +/* ------- */ + LOCK_SMUTEX(this); + ival = this->ref_count; + UNLOCK_SMUTEX(this); + + astWriteInt( channel, "RefCnt", 0, 0, ival, + "Count of active Object pointers" ); + + +/* Nobj. */ +/* ----- */ + vtab = this->vtab; + astWriteInt( channel, "Nobj", 0, 0, vtab->nobject, + "Count of active Objects in same class" ); + +/* Terminate the information above with an "IsA" item for the base + Object class. */ + astWriteIsA( channel, "Object", "AST Object" ); + +/* Now loop to perform the same operation for each additional class + from which the Object inherits (the Object class itself does not + declare a dump function). Invoke the dump function for each class + in turn, working down the class hierarchy, to write out instance + variable information for that class. */ + for ( idump = 0; idump < this->vtab->ndump; idump++ ) { + ( *this->vtab->dump[ idump ] )( this, channel, status ); + +/* Terminate the output from all except the final dump function with + an appropriate "IsA" item describing the class whose data have just + been written. */ + if ( idump != ( this->vtab->ndump - 1 ) ) { + astWriteIsA( channel, this->vtab->dump_class[ idump ], + this->vtab->dump_comment[ idump ] ); + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + +/* Terminate the output from the final dump function with an "End" + item to match the initial "Begin" item. */ + astWriteEnd( channel, astGetClass( this ) ); +} + +static void EmptyObjectCache( int *status ){ +/* +* Name: +* EmptyObjectCache + +* Purpose: +* Free all memory blocks currently on the free list of any class. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* EmptyObjectCache( int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function empties the cache of Object memory by freeing all +* memory blocks on the free_list of all classes. + +* Parameters: +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int iblock; /* Index of next entry in free list */ + int itab; /* Index of next virtual function table */ + AstObjectVtab *vtab; /* Pointer to next virtual function table */ + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Loop round all the virtual function tables which are known about. */ + for( itab = 0; itab < nvtab; itab++ ) { + vtab = known_vtabs[ itab ]; + +/* Free all memory blocks stored on the free list for this class. */ + for( iblock = 0; iblock < vtab->nfree; iblock++ ) { + (vtab->free_list)[ iblock ] = astFree( (vtab->free_list)[ iblock ] ); + } + +/* Free the memory used to hold the free list, and indicate it has zero + length. */ + vtab->free_list = astFree( vtab->free_list ); + vtab->nfree = 0; + } +} + +static void EnvSet( AstObject *this, int *status ) { +/* +*+ +* Name: +* astEnvSet + +* Purpose: +* Set default values for an Object's attributes. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astEnvSet( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function assigns a set of attribute values for an Object, +* the attributes and their values being specified by means of an +* environment variable of the form "_OPTIONS" that has +* a value of the form: +* +* "attribute1 = value1, attribute2 = value2, ... " +* +* Here, "attribute" specifies an attribute name and the value to +* the right of each "=" sign should be a suitable textual +* representation of the value to be assigned to that +* attribute. This will be interpreted according to the attribute's +* data type. + +* Parameters: +* this +* Pointer to the Object. + +* Notes: +* - See astVSet for details of how the setting strings are +* interpreted. +*- +*/ + +/* Local Variables: */ + char varname[ 100 ]; + const char *attrs = NULL; + const char *class = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the string holding default attribute values for the class of the + supplied object. This string is held in the class virtual function + table. */ + attrs = this->vtab->defaults; + +/* If this is the first time the defaults have been requested, get the + list of defaults from the environment variable "_OPTIONS" + and store in the virtual function table. */ + if( !attrs ) { + +/* Get the class name. */ + class = astGetClass( this ); + +/* Form the upper-case name of the environment variable. */ + if( class ) { + sprintf( varname, "%s_OPTIONS", class ); + astChrCase( NULL, varname, 1, sizeof( varname ) ); + +/* Get the value of the environment variable. */ + attrs = getenv( varname ); + +/* If no defaults were specified store the string "None". */ + if( ! attrs ) attrs = "None"; + +/* Store a copy in the virtual function table. */ + astBeginPM; + this->vtab->defaults = astStore( NULL, attrs, strlen( attrs ) + 1 ); + astEndPM; + } + } + +/* If any defaults were specified, set the corresponding attributes. */ + if( attrs && strcmp( attrs, "None" ) ) astSet( this, attrs, status ); + +} + +static int Equal( AstObject *this, AstObject *that, int *status ){ +/* +*+ +* Name: +* astEqual + +* Purpose: +* Check equality of two AST Objects. + +* Type: +* Public (but undocumented) function. + +* Synopsis: +* #include "object.h" +* int astEqual( AstObject *this, AstObject *this ) + +* Class Membership: +* Object virtual function. + +* Description: +* This function returns non-zero if the two pointers identify +* equivalent objects. + +* Parameters: +* this +* Pointer to the first Object. +* that +* Pointer to the second Object. + +* Returned Value: +* Non-zero if the objects are equivalent. + +* Notes: +* - This function is available in the public interface even though it is +* documented as protected. This is because it is difficult to document +* precisely which aspects of two Objects must be equal in order for this +* function to return a non-zero value. Each class of Object supplies +* its own Equal method that tests which-ever attributes the class +* considers to be significiant. +* - The implementation of this function provided by the base Object +* class simply compares the class names and the structure size. +* Sub-classes should override this method to provide more appropriate tests. +* - Zero is returned if an error has already occurred, or if +* this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + int result; + +/* Check inherited status */ + if( !astOK ) return 0; + +/* Objects are equivalent if they are the same object. */ + if( this == that ) { + result = 1; + +/* Otherwise, check the structure size and class names */ + } else { + result = ( this->size == that->size && + !strcmp( astGetClass( this ), astGetClass( that ) ) ); + } + + return result; +} + +static const char *Get( AstObject *this, const char *attrib, int *status ) { +/* +* Name: +* Get + +* Purpose: +* Get the value of a specified attribute for an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* const char *Get( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function returns a pointer to the value of a specified +* attribute for an Object, formatted as a character string. It is +* mainly a wrap-up used internally for invoking the astGetAttrib +* method. It converts the attribute name to lower case and removes +* white space before invoking the method. This saves derived +* classes that over-ride the astGetAttrib method from having to do +* this themselves. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This may contain mixed +* case and white space, but should not be composed entirely of +* white space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Object, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Object. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + char *buff; /* Pointer to local string buffer */ + const char *result; /* Pointer value to return */ + int i; /* Loop counter for characters */ + int j; /* Non-blank character count */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate a local buffer long enough to hold the attribute name + string. */ + buff = astMalloc( strlen( attrib ) + (size_t) 1 ); + if ( astOK ) { + +/* Copy the attribute name characters into the buffer, omitting all + white space and converting to lower case. */ + for ( i = j = 0; attrib[ i ]; i++ ) { + if ( !isspace( attrib[ i ] ) ) buff[ j++ ] = tolower( attrib[ i ] ); + } + +/* Terminate the copied string. */ + buff[ j ] = '\0'; + +/* If no characters were copied, the attribute name was blank, so + report an error. */ + if ( !j ) { + if( astOK ) astError( AST__BADAT, "astGet(%s): A blank attribute " + "name was given.", status, astGetClass( this ) ); + +/* Of OK, invoke astGetAttrib to obtain a pointer to the attribute + value formatted as a character string. */ + } else { + result = astGetAttrib( this, buff ); + +/* If required, strip out graphical escape sequences. */ + if( !astEscapes( -1 ) ) result = astStripEscapes( result ); + } + } + +/* Free the local string buffer. */ + buff = astFree( buff ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetAttrib( AstObject *this, const char *attrib, int *status ) { +/* +*+ +* Name: +* astGetAttrib + +* Purpose: +* Get the value of a specified attribute for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* const char *astGetAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a pointer to the value of a specified +* attribute for an Object, formatted as a character string. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Object, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Object. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + const char *result; /* Pointer value to return */ + int nobject; /* Nobject attribute value */ + int objsize; /* ObjSize attribute value */ + int ref_count; /* RefCount attribute value */ + int usedefs; /* UseDefs attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(this); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an + appropriate format. Set "result" to point at the result string. */ + +/* Class. */ +/* ------ */ + if ( !strcmp( attrib, "class" ) ) { + result = astGetClass( this ); + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + result = astGetID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astGetIdent( this ); + +/* UseDefs */ +/* ------- */ + } else if ( !strcmp( attrib, "usedefs" ) ) { + usedefs = astGetUseDefs( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", usedefs ); + result = getattrib_buff; + } + +/* Nobject. */ +/* -------- */ + } else if ( !strcmp( attrib, "nobject" ) ) { + nobject = astGetNobject( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nobject ); + result = getattrib_buff; + } + +/* ObjSize */ +/* ------- */ + } else if ( !strcmp( attrib, "objsize" ) ) { + objsize = astGetObjSize( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", objsize ); + result = getattrib_buff; + } + +/* RefCount. */ +/* --------- */ + } else if ( !strcmp( attrib, "refcount" ) ) { + ref_count = astGetRefCount( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ref_count ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, then report an error. */ + } else if( astOK ){ + astError( AST__BADAT, "astGet: The %s given does not have an attribute " + "called \"%s\".", status, astGetClass( this ), attrib ); + } + +/* Return the result. */ + return result; +} + +const char *astGetClass_( const AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetClass + +* Purpose: +* Obtain the value of the Class attribute for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* const char *astGetClass( const AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a pointer to the Class string for an +* Object. This contains the name of the class which created the +* Object. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* Pointer to a string containing the class name. + +* Notes: +* - This function does not check the global error status before +* executing. This is to allow it to be used to obtain class names +* for inclusion in error messages. +* - A pointer to an explanatory string will be returned if this +* function is given a pointer which does not identify an Object. +*- +*/ + +/* Local Variables: */ + const char *name; /* Pointer to returned string */ + +/* First check if the Object pointer supplied is NULL, and set the + returned pointer accordingly. */ + if ( !this ) { + name = ""; + +/* Also check if the supposed Object has the correct "magic number" in + its check field. If not, it is not an Object. */ + } else if ( this->check != Magic( this, this->size, status ) ) { + name = ""; + +/* If OK, obtain a pointer to the class name from the Object's virtual + function table. */ + } else { + name = this->vtab->class; + } + +/* Return the result. */ + return name; +} + +int astGetNobject_( const AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetNobject + +* Purpose: +* Obtain the value of the Nobject attribute for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astGetNobject( const AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the value of the Nobject attribute for an +* Object. This is a count of the number of active Objects in the +* same class as the Object supplied. This count does not include +* Objects in derived classes. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The number of active Objects. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the active object count. */ + return this->vtab->nobject; +} + +static int GetObjSize( AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetObjSize + +* Purpose: +* Determine the in-memory size of the Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* int astGetObjSize( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the in-memory size of an Object. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the object size. */ + return this->size; +} + +void *astGetProxy_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetProxy + +* Purpose: +* Get a pointer to the foreign language proxy used to represent a +* given AST Object. + +* Type: +* Undocumented public function. + +* Synopsis: +* #include "object.h" +* void *astGetProxy( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns any pointer stored previously in the AST +* Object using astSetProxy. If no such pointer has been stored, a +* NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* Pointer to the proxy object, or NULL. + +* Notes: +* - This function is public, but is currently undocumented since it +* is only of interest to people writing AST interfaces for other +* languages. +* - This function attempts to execute even if the AST error status +* is set on entry, although no further error report will be made +* if it subsequently fails under these circumstances. +*- +*/ + return this ? this->proxy : NULL; +} + +int astGetRefCount_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetRefCount + +* Purpose: +* Obtain the value of the RefCount attribute for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astGetRefCount( const AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the value of the read-only RefCount +* attribute for an Object. This is a "reference count" of the +* number of active pointers to it, as accounted for by astClone +* and astAnnul (plus the pointer issued when it was created). If +* the reference count for an Object falls to zero when astAnnul is +* invoked, the object will be deleted. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The reference count. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables; */ + int result; /* Returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a lock on the object's secondary mutex. This mutex guards access + to the "ref_count" and "locker" components of the AstObject structure. */ + LOCK_SMUTEX(this); + +/* Get the reference count. */ + result = this->ref_count; + +/* Unlock the object's secondary mutex. */ + UNLOCK_SMUTEX(this); + +/* Return the result. */ + return result; +} + +/* +*++ +* Name: +c astGet +f AST_GET + +* Purpose: +* Get an attribute value for an Object. + +* Type: +* Public functions. + +* Synopsis: +c #include "object.h" +c type astGet( AstObject *this, const char *attrib ) +f RESULT = AST_GET( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object methods. + +* Description: +* This is a family of functions which return a specified attribute +* value for an Object using one of several different data +* types. The type is selected by replacing in the function name +c by C, D, F, I or L, to obtain a result in const char* (i.e. string), +c double, float, int, or long format, respectively. +f by C, D, I, L or R, to obtain a result in Character, Double +f precision, Integer, Logical or Real format, respectively. +* +* If possible, the attribute value is converted to the type you +* request. If conversion is not possible, an error will result. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing the name of +c the attribute whose value is required. +f A character string containing the name of the attribute whose +f value is required. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGet() +f AST_GET = type +c The attribute value, in the data type corresponding to (or, +c in the case of astGetC, a pointer to a constant null-terminated +c character string containing this value). +f The attribute value, in the data type corresponding to . + +* Applicability: +* Object +* These functions apply to all Objects. + +* Examples: +c printf( "RefCount = %d\n", astGetI( z, "RefCount" ) ); +c Prints the RefCount attribute value for Object "z" as an int. +c title = astGetC( axis, "Title" ); +c Obtains a pointer to a null-terminated character string containing +c the Title attribute of Object "axis". +f WRITE( *, '('' RefCount = '', A10 )' ) AST_GETC( Z, 'RefCount', STATUS ) +f Prints the RefCount attribute value for Object Z as a character +f string. +f NAXES = AST_GETI( FRAME, 'Naxes', STATUS ) +f Obtains the value of the Naxes attribute for Object FRAME as an +f integer. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +* - An appropriate "null" value will be returned if this function +c is invoked with the AST error status set, or if it should +f is invoked with STATUS set to an error value, or if it should +* fail for any reason. This null value is zero for numeric +c values and NULL for pointer values. +f values, .FALSE. for logical values, and blank for character values. +f - Numerical attribute values of zero translate to logical value +f .FALSE. and all other numerical values translate to .TRUE.. +c - The pointer returned by astGetC is guaranteed to remain valid +c and the string to which it points will not be over-written for a +c total of 50 successive invocations of this function. After this, +c the memory containing the string may be re-used, so a copy of +c the string should be made if it is needed for longer than this. +*-- +*/ + +/* Define a macro that expands to implement the astGetX_ member + functions required. The arguments to this macro are: + + code + The character that appears at the end of the function name. + type + The C type of the function return value. + format + A quoted string containing a astSscanf format specifier that + will read the attribute value into a variable of the required + data type. This format should transfer 1 astSscanf value. +*/ +#define MAKE_GETX(code,type,format) \ +type astGet##code##_( AstObject *this, const char *attrib, int *status ) { \ +\ +/* Local Variables: */ \ + const char *str; /* Pointer to string attribute value */ \ + int nc; /* Number of characters read from string */ \ + int nval; /* Number of values read from string */ \ + type result; /* Value to return */ \ + type value; /* Converted value */ \ +\ +/* Initialise. */ \ + result = (type) 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Obtain the attribute value as a string. */ \ + str = Get( this, attrib, status ); \ + if ( astOK ) { \ +\ +/* Read the value from the string, ignoring surrounding white \ + space. */ \ + nc = 0; \ + nval = astSscanf( str, " " format " %n", &value, &nc ); \ +\ +/* Check that the number of values read was 1 and that all the \ + string's characters were consumed. If so, use the result. */ \ + if ( ( nval == 1 ) && ( nc >= (int) strlen( str ) ) ) { \ + result = value; \ +\ +/* If the read was unsuccessful, report an error. */ \ + } else if( astOK ) { \ + astError( AST__ATGER, "astGet" #code "(%s): The attribute " \ + "value \"%s=%s\" cannot be read using the requested data " \ + "type.", status,astGetClass( this ), attrib, str ); \ + } \ + } \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Use this macro to create all the GetX_ private member functions, + except SetC (which is handled separately). */ +MAKE_GETX(D,double,"%lf") +MAKE_GETX(F,float,"%f") +MAKE_GETX(I,int,"%d") +MAKE_GETX(L,long,"%ld") + +/* Handle GetC separately because memory must be allocated to hold the + returned character values. */ +const char *astGetC_( AstObject *this, const char *attrib, int *status ) { + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + const char *result; /* Pointer value to return */ + const char *value; /* Pointer to attribute value */ + int i; /* Loop count */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(this); + +/* If the "strings" array has not been initialised, fill it with + NULL pointers. */ + if ( !astgetc_init ) { + astgetc_init = 1; + for ( i = 0; i < AST__ASTGETC_MAX_STRINGS; i++ ) astgetc_strings[ i ] = NULL; + } + +/* Obtain a pointer to the required attribute value, formatted as a + character string. */ + value = Get( this, attrib, status ); + +/* If OK, store a copy of the resulting string in dynamically + allocated memory, putting a pointer to the copy into the next + element of the "astgetc_strings" array. (This process also de-allocates + any previously allocated memory pointed at by this "strings" + element, so the earlier string is effectively replaced by the new + one.) */ + if ( astOK ) { + + astBeginPM; + astgetc_strings[ astgetc_istr ] = astStore( astgetc_strings[ astgetc_istr ], + value, strlen( value ) + (size_t) 1 ); + astEndPM; + +/* If OK, return a pointer to the copy and increment "astgetc_istr" to use the + next element of "astgetc_strings" on the next invocation. Recycle + "astgetc_istr" to zero when all elements have been used. */ + if ( astOK ) { + result = astgetc_strings[ astgetc_istr++ ]; + if ( astgetc_istr == ( AST__ASTGETC_MAX_STRINGS - 1 ) ) astgetc_istr = 0; + } + } + +/* Return the result. */ + return result; + +} + +static int HasAttribute( AstObject *this, const char *attrib, int *status ) { +/* +*++ +* Name: +c astHasAttribute +f AST_HASATTRIBUTE + +* Purpose: +* Test if an Object has a named attribute. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astHasAttribute( AstObject *this, const char *attrib ) +f RESULT = AST_HASATTRIBUTE( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function returns a boolean result (0 or 1) to indicate +f This function returns a logical result to indicate +* whether the supplied Object has an attribute with the supplied name. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the first Object. +c attrib +f ATTRIB = INTEGER (Given) +c Pointer to a string holding the +f The +* name of the attribute to be tested. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astHasAttribute() +c One if the Object has the named attribute, otherwise zero. +f AST_SAME = LOGICAL +f .TRUE. if the Object has the named attribute, otherwise +f .FALSE. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - A value of zero will be returned if this function is invoked +c with the AST error status set, or if it should fail for any reason. +f - A value of .FALSE. will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any reason. +*-- +*/ + +/* Local Variables: */ + int oldrep; /* Original AST error reporting flag */ + int result; /* Returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Temporarily switch off error reporting. */ + oldrep = astReporting( 0 ); + +/* Attempt to get a value for the specified attribute. */ + (void) Get( this, attrib, status ); + +/* An error will have been reported if the object does not have the + requested attribute. Set the result and clear the error status. */ + if( !astOK ) { + result = 0; + astClearStatus; + } else { + result = 1; + } + +/* Re-instate the original error reporting flag. */ + (void) astReporting( oldrep ); + +/* Return the result. */ + return result; +} + +static unsigned long Magic( const AstObject *this, size_t size, int *status ) { +/* +* Name: +* Magic + +* Purpose: +* Generate a "magic number" for an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* unsigned long Magic( const AstObject *this, size_t size, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function generates a "magic number" which is a function of an Object +* pointer (address) and an Object size. This number may be stored in an +* Object to allow it to be recognised as a valid Object by other routines +* and to provide security against argument passing errors, etc. + +* Parameters: +* this +* Pointer to an Object. +* size +* The Object size. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The magic number. + +* Notes: +* - This function does not perform any error checking. +*/ + +/* Form the bit-wise exclusive OR between the Object address and the Object + size, then add 2 and invert the bits. Return the result as an unsigned + long integer. */ + return ~( ( ( (unsigned long) this ) ^ ( (unsigned long) size ) ) + + ( (unsigned long) 2 ) ); +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this, int mode, int extra, + AstObject **fail, int *status ) { +/* +*+ +* Name: +* astManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astManageLock( AstObject *this, int mode, int extra, +* AstObject **fail ) + +* Class Membership: +* Object method. + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread. +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. + +* Returned Value: +* A status value: +* 0 - Success. +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. +* 5 - Check failed - object is locked by a different thread +* 6 - Check failed - object is unlocked +* + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. + +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + if( fail ) *fail = NULL; + +/* Check the supplied point is not NULL. */ + if( ! this ) return result; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Get a lock on the object's secondary mutex. This gives us exclusive + access to the "locker" (and "ref_count") component in the AstObject + structure. All other components in the structure are guarded by the + primary mutex (this->mutex1). */ + if( LOCK_SMUTEX(this) ) { + result = 2; + +/* If the secondary mutex was locked succesfully, first deal with cases + where the caller wants to lock the Object for exclusive use by the + calling thread. */ + } else if( mode == AST__LOCK ) { + +/* If the Object is not currently locked, lock the Object primary mutex + and record the identity of the calling thread in the Object. */ + if( this->locker == -1 ) { + if( LOCK_PMUTEX(this) ) result = 2; + this->locker = AST__THREAD_ID; + this->globals = AST__GLOBALS; + ChangeThreadVtab( this, status ); + +/* If the Object is already locked by the calling thread, do nothing. */ + } else if( this->locker == AST__THREAD_ID ) { + +/* If the object is locked by a different thread, and the caller is + willing to wait, attempt to lock the Object primary mutex. This will + cause the calling thread to block until the Object is release by the + thread that currently has it locked. Then store the identity of the + calling thread (the new lock owner). We first need to release the + secondary mutex so that the other thread can modify the "locker" + component in the AstObject structure when it releases the Object + (using this function). We then re-lock the secondary mutex so this + thread can change the "locker" component safely. */ + } else if( extra ) { + if( UNLOCK_SMUTEX(this) ) { + result = 3; + } else if( LOCK_PMUTEX(this) ) { + result = 2; + } else if( LOCK_SMUTEX(this) ) { + result = 2; + } + this->locker = AST__THREAD_ID; + this->globals = AST__GLOBALS; + ChangeThreadVtab( this, status ); + +/* If the caller does not want to wait until the Object is available, + return a status of 1. */ + } else { + result = 1; + } + +/* Unlock the Object for use by other threads. */ + } else if( mode == AST__UNLOCK ) { + +/* Do nothing if the Object is currently unlocked. */ + if( this->locker == -1 ) { + +/* If the object is currently locked by the calling thread, clear the + identity of the thread that owns the lock and unlock the primary + mutex. */ + } else if( this->locker == AST__THREAD_ID ) { + this->locker = -1; + this->globals = NULL; + if( UNLOCK_PMUTEX(this) ) result = 3; + +/* Return an error status value if the Object is locked by another + thread. */ + } else { + result = 1; + } + +/* Check the Object is locked by the calling thread. Return a status of 1 if + not. */ + } else if( mode == AST__CHECKLOCK ) { + if( this->locker == -1 ) { + result = 6; + } else if( this->locker != AST__THREAD_ID ) { + result = 5; + } + +/* Return a status of 4 for any other modes. */ + } else { + result = 4; + } + +/* Unlock the secondary mutex so that other threads can access the "locker" + component in the Object to see if it is locked. */ + if( UNLOCK_SMUTEX(this) ) result = 3; + +/* If the operation failed, return a pointer to the failed object. */ + if( result && fail ) *fail = this; + +/* Return the status value */ + return result; +} +#endif + +char *astToString_( AstObject *this, int *status ) { +/* +c++ +* Name: +* astToString + +* Purpose: +* Create an in-memory serialisation of an Object + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* char *astToString( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a string holding a minimal textual +* serialisation of the supplied AST Object. The Object can re +* re-created from the serialisation using astFromString. + +* Parameters: +* this +* Pointer to the Object to be serialised. + +* Returned Value: +* astToString() +* Pointer to dynamically allocated memory holding the +* serialisation, or NULL if an error occurs. The pointer +* should be freed when no longer needed using astFree. + +c-- +*/ + +/* Local Variables: */ + StringData data; /* Data passed to the sink function */ + AstChannel *channel; /* Pointer to output Channel */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Create a Channel which will write to an expanding dynamically + allocated memory buffer. Set Channel attributes to exclude all + non-essential characters. */ + channel = astChannel( NULL, ToStringSink, "Comment=0,Full=-1,Indent=0", + status ); + +/* Initialise the data structure used to communicate with the sink + function, and store a pointer to it in the Channel. */ + data.ptr = NULL; + data.buff = NULL; + data.len = 0; + astPutChannelData( channel, &data ); + +/* Write the Object to the Channel. */ + astWrite( channel, this ); + +/* Annul the Channel pointer. */ + channel = astAnnul( channel ); + +/* Free the returned string if an error has occurred. */ + if( !astOK ) data.ptr = astFree( data.ptr ); + +/* Return the pointer. */ + return data.ptr; +} + +static void ToStringSink( const char *text ){ +/* +* Name: +* ToStringSink + +* Purpose: +* A Channel sink function for use by the astToString method. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* ToStringSink( const char *text ) + +* Class Membership: +* Object member function. + +* Description: +* This function appends the supplied line of text to the end of a +* dynamically growing memory block. + +* Parameters: +* text +* Pointer to the null-terminated line of text to be stored. + +*/ + +/* Local Variables: */ + StringData *data; /* Data passed to the sink function */ + int *status; /* Pointer to local status value */ + int status_value; /* Local status value */ + +/* Set up the local status */ + status_value = 0; + status = &status_value; + +/* Get a pointer to the structure holding the current memory pointer and + the length of the currently allocated memory. */ + data = astChannelData; + +/* Append the supplied text to the end of the string, and update the + string length. */ + data->ptr = astAppendString( data->ptr, &(data->len), text ); + +/* Append a newline character to the end of the string, and update the + string length. */ + data->ptr = astAppendString( data->ptr, &(data->len), "\n" ); +} + +void astSet_( void *this_void, const char *settings, int *status, ... ) { +/* +*++ +* Name: +c astSet +f AST_SET + +* Purpose: +* Set attribute values for an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astSet( AstObject *this, const char *settings, ... ) +f CALL AST_SET( THIS, SETTINGS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function assigns a set of attribute values to an Object, +f This routine assigns a set of attribute values to an Object, +* over-riding any previous values. The attributes and their new +* values are specified via a character string, which should +* contain a comma-separated list of the form: +* +* "attribute_1 = value_1, attribute_2 = value_2, ... " +* +* where "attribute_n" specifies an attribute name, and the value +* to the right of each "=" sign should be a suitable textual +* representation of the value to be assigned. This value will be +* interpreted according to the attribute's data type. +c +c The string supplied may also contain "printf"-style format +c specifiers, identified by "%" signs in the usual way. If +c present, these will be substituted by values supplied as +c additional optional arguments (using the normal "printf" rules) +c before the string is used. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c settings +f SETTINGS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing a +c comma-separated list of attribute settings in the form described +c above. +f A character string containing a comma-separated list of +f attribute settings in the form described above. +c ... +c Optional additional arguments which supply values to be +c substituted for any "printf"-style format specifiers that +c appear in the "settings" string. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Examples: +c astSet( map, "Report = 1, Zoom = 25.0" ); +c Sets the Report attribute for Object "map" to the value 1 and +c the Zoom attribute to 25.0. +c astSet( frame, "Label( %d ) =Offset along axis %d", axis, axis ); +c Sets the Label(axis) attribute for Object "frame" to a +c suitable string, where the axis number is obtained from +c "axis", a variable of type int. +c astSet( frame, "Title =%s", mystring ); +c Sets the Title attribute for Object "frame" to the contents of +c the string "mystring". +f CALL AST_SET( MAP, 'Report = 1, Zoom = 25.0', STATUS ) +f Sets the Report attribute for Object MAP to the value 1 and +f the Zoom attribute to 25.0. +f CALL AST_SET( FRAME, 'Label( 1 ) =Offset from cluster axis', STATUS ) +f Sets the Label(1) attribute for Object FRAME to a suitable +f string. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +* - White space may also surround attribute values, where it will +* generally be ignored (except for string-valued attributes where +* it is significant and forms part of the value to be assigned). +* - To include a literal comma in the value assigned to an attribute, +* the whole attribute value should be enclosed in quotation markes. +c Alternatively, you can use "%s" format and supply the value as a +c separate additional argument to astSet (or use the astSetC +c function instead). +c - The same procedure may be adopted if "%" signs are to be included +c and are not to be interpreted as format specifiers (alternatively, +c the "printf" convention of writing "%%" may be used). +* - An error will result if an attempt is made to set a value for +* a read-only attribute. +*-- + +* Implementation Notes: +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* Object identifier is of type (void *) and is converted and +* validated within the function itself. +* - This implementation of astSet is designed to be used within AST, +* and has an explicit status parameter. From outside AST, the astSet +* macro will invoke the astSetId_ function which does not have an +* status parameter. + +*-- +*/ + +/* Local Variables: */ + AstObject *this; /* Pointer to the Object structure */ + va_list args; /* Variable argument list */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain and validate a pointer to the Object structure. */ + this = astCheckObject( this_void ); + if ( astOK ) { + +/* Obtain the variable argument list and pass all arguments to the + astVSet method for interpretation. */ + va_start( args, status ); + astVSet( this, settings, NULL, args ); + va_end( args ); + } +} + +static void SetAttrib( AstObject *this, const char *setting, int *status ) { +/* +*+ +* Name: +* astSetAttrib + +* Purpose: +* Set an attribute value for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astSetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* Object method. + +* Description: +* This function assigns an attribute value for an Object, the attribute and +* its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the Object. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. + +* Notes: +* - The Object class does not have any writable attributes, so +* this function merely reports an error. It is intended to be +* extended by other class definitions. +*- +*/ + +/* Local Variables: */ + int id; /* Offset of ID string */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* ID. */ +/* --- */ + if ( nc = 0, ( 0 == astSscanf( setting, "id=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetID( this, setting + id ); + +/* Ident. */ +/* ------ */ + } else if ( nc = 0, ( 0 == astSscanf( setting, "ident=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetIdent( this, setting + id ); + +/* UseDefs */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "usedefs= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetUseDefs( this, ival ); + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class and use this to report an error + if it does. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + + } else if ( MATCH( "class" ) || + MATCH( "nobject" ) || + MATCH( "objsize" ) || + MATCH( "refcount" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Since no writable attributes are defined for the Object class, any + attempt to set a value for anything else is also an error. */ + } else { + astError( AST__BADAT, "astSet: The attribute setting \"%s\" is invalid " + "for a %s.", status, setting, astGetClass( this ) ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +void astSetCopy_( AstObjectVtab *vtab, + void (* copy)( const AstObject *, AstObject *, int * ), int *status ) { +/* +*+ +* Name: +* astSetCopy + +* Purpose: +* Declare a copy constructor for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetCopy( AstObjectVtab *vtab, +* void (* copy)( const AstObject *, AstObject * ) ) + +* Class Membership: +* Object method. + +* Description: +* This function is provided so that class definitions can declare a copy +* constructor to be associated with an Object that is being constructed. +* When a copy is later performed on the Object, the copy constructor of +* each class to which the Object belongs will be invoked in turn (working +* down the class hierarchy). The copy constructor is passed pointers to the +* source and destination Objects. It should implement the copy and return +* void. + +* Parameters: +* vtab +* Pointer to the Object's virtual function table, in which the copy +* constructor's pointer is to be stored for future use. +* copy +* Pointer to the copy constructor function. + +* Notes: +* - When an Object is copied, a byte-by-byte copy of its structure is +* automatically made before any copy constructors are invoked. A copy +* constructor need only be provided if this does not suffice (e.g. if the +* structure contains pointers to other data). +* - If a copy constructor is declared for a class, then a +* destructor for that class must also be declared (using +* astSetDelete) so that there is a one-to-one correspondence +* between copy constructors and their associated destructors. +* - Copy constructors should check the global error status in the normal +* way and should set it (and report an error) if they fail. +*- +*/ + + +/* Check the global status. */ + if ( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Expand the array of copy constructor pointers in the virtual function table + (if necessary) to accommodate the new one. */ + vtab->copy = astGrow( vtab->copy, vtab->ncopy + 1, + sizeof( void (*)( const AstObject *, AstObject * ) ) ); + +/* If OK, store the new function pointer and increment the count of copy + constructors. */ + if ( astOK ) { + vtab->copy[ vtab->ncopy++ ] = copy; + } + +/* Mark the end of the section in which memory allocations may never be freed + (other than by any AST exit handler). */ + astEndPM; + +} + +void astSetDelete_( AstObjectVtab *vtab, void (* delete)( AstObject *, int * ), int *status ) { +/* +*+ +* Name: +* astSetDelete + +* Purpose: +* Declare a destructor for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetDelete( AstObjectVtab *vtab, void (* delete)( AstObject * ) ) + +* Class Membership: +* Object method. + +* Description: +* This function is provided so that class definitions can declare a +* destructor to be associated with an Object. When the Object is later +* deleted, the destructor declared by each class to which the Object +* belongs will be invoked in turn (working up the class hierarchy). The +* destructor is passed a pointer to the Object. It should free any +* resources (e.g. memory) associated with it and return void. It should +* not free the memory containing the Object itself. + +* Parameters: +* vtab +* Pointer to the Object's virtual function table, in which the +* destructor's pointer is to be stored for future use. +* delete +* Pointer to the destructor function. + +* Notes: +* - A destructor need not be declared for a class if there are no +* resources to free. +* - If a destructor is declared for a class, then a copy +* constructor for that class must also be declared (using +* astSetCopy) so that there is a one-to-one correspondence between +* copy constructors and their associated destructors. +* - A destructor function should generally attempt to execute even +* if the global error status is set on entry, but should not +* report further errors in that case (errors should be reported +* normally if status is not set on entry). +*- +*/ + + +/* Check the global status. */ + if ( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Expand the array of destructor pointers in the virtual function table (if + necessary) to accommodate the new one. */ + vtab->delete = astGrow( vtab->delete, vtab->ndelete + 1, + sizeof( void (*)( AstObject * ) ) ); + +/* If OK, store the new function pointer and increment the count of + destructors. */ + if ( astOK ) { + vtab->delete[ vtab->ndelete++ ] = delete; + } + +/* Mark the end of the section in which memory allocations may never be freed + (other than by any AST exit handler). */ + astEndPM; + +} + +void astSetDump_( AstObjectVtab *vtab, + void (* dump)( AstObject *, AstChannel *, int * ), + const char *class, const char *comment, int *status ) { +/* +*+ +* Name: +* astSetDump + +* Purpose: +* Declare a dump function for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetDump( AstObjectVtab *vtab, +* void (* dump)( AstObject *, AstChannel * ), +* const char *class, const char *comment ) + +* Class Membership: +* Object method. + +* Description: +* This function is provided so that class definitions can declare +* a dump function to be associated with an Object that is being +* constructed. When the astWrite (or astShow or astToString) method +* is later used to write the Object to a Channel, the dump function +* of each class to which the Object belongs will be invoked in turn +* (working down the class hierarchy). The dump function is passed +* pointers to the Object and the output Channel. It should write +* out any internal values (e.g. instance variables) for its class +* that are to be kept (using the protected astWrite... methods of +* the Channel) and return void. + +* Parameters: +* vtab +* Pointer to the Object's virtual function table, in which the +* dump function's pointer is to be stored for future use. +* dump +* Pointer to the dump function. +* class +* Pointer to a constant null-terminated string (residing in +* static memory) containing the name of the class that is +* declaring the dump function. +* comment +* Pointer to a constant null-terminated string (residing in +* static memory) containing a comment to associate with the +* dump function. This should normally describe the purpose of +* the class that is declaring the dump function. + +* Notes: +* - Dump functions should check the global error status in the +* normal way and should set it (and report an error) if they fail. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Expand the arrays of pointers to dump functions and related data in + the virtual function table (if necessary) to accommodate the new + one. */ + vtab->dump = astGrow( vtab->dump, vtab->ndump + 1, + sizeof( void (*)( AstObject *, AstChannel * ) ) ); + vtab->dump_class = astGrow( vtab->dump_class, vtab->ndump + 1, + sizeof( char * ) ); + vtab->dump_comment = astGrow( vtab->dump_comment, vtab->ndump + 1, + sizeof( char * ) ); + +/* If OK, store the new pointers (to the dump function, class name and + comment) and increment the count of dump functions. */ + if ( astOK ) { + vtab->dump[ vtab->ndump ] = dump; + vtab->dump_class[ vtab->ndump ] = class; + vtab->dump_comment[ vtab->ndump ] = comment; + vtab->ndump++; + } + +/* Mark the end of the section in which memory allocations may never be + freed (other than by any AST exit handler). */ + astEndPM; +} + +void astSetProxy_( AstObject *this, void *proxy, int *status ) { +/* +*+ +* Name: +* astSetProxy + +* Purpose: +* Store a pointer to the foreign language proxy used to represent a +* given AST Object. + +* Type: +* Undocumented public function. + +* Synopsis: +* #include "object.h" +* void astSetProxy( AstObject *this, void *proxy ) + +* Class Membership: +* Object method. + +* Description: +* This function stores the supplied pointer in the AST Object so that +* it can be retrieved later using astGetProxy. +* +* The supplied pointer should point to a structure that is used +* to represent the AST Object within some external system. It is +* expected that the external system will check each object reference +* returned by AST to see if it has an associated proxy object. If not +* (i.e. if astGetProxy returns NULL), a new external object will be +* created to represent the AST Object, and a pointer to it will be +* stored in the AST Object using astSetProxy. If the AST Object +* already has a proxy, the AST reference is annulled and the existing +* proxy object is used by the external system. + +* Parameters: +* this +* Pointer to the Object. +* proxy +* Pointer to the proxy object, or NULL. + +* Notes: +* - The suppied pointer is not used within AST itself, other than to +* be returned by the astGetProxy method. +* - This function is public, but is currently undocumented since it +* is only of interest to people writing AST interfaces for other +* languages. +*- +*/ + if( !astOK ) return; + this->proxy = proxy; +} + +void astSetVtab_( AstObject *this, AstObjectVtab *vtab, int *status ) { +/* +*+ +* Name: +* astSetVtab + +* Purpose: +* Change the virtual function table associated with an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetVtab( AstObject *this, AstObjectVtab *vtab ) + +* Class Membership: +* Object method. + +* Description: +* This function changes the virtual function table associated with an +* Object. This may be needed, for instance, if a super-class +* initialises a parent class structure with a NULL vtab, causing the +* vtab of the parent class to be used instead of the super-class. +* Whilst the super-class object is being constructed its inherited methods +* will be determined by the parent class. Once the super-class object +* has been constructed, it can invoke this fuction in order to +* set the vtab to the super-class vtab, thus causing the method +* implementations provided by the super-cvlass to be used. + +* Parameters: +* this +* Pointer to the Object to be modified. +* vtab +* Pointer to the virtual function table to store in the Object. +*- +*/ + if( this ) this->vtab = vtab; +} + +static int Same( AstObject *this, AstObject *that, int *status ) { +/* +*++ +* Name: +c astSame +f AST_SAME + +* Purpose: +* Test if two AST pointers refer to the same Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astSame( AstObject *this, AstObject *that ) +f RESULT = AST_SAME( THIS, THAT, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function returns a boolean result (0 or 1) to indicate +f This function returns a logical result to indicate +* whether two pointers refer to the same Object. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the first Object. +c that +f THAT = INTEGER (Given) +* Pointer to the second Object. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSame() +c One if the two pointers refer to the same Object, otherwise zero. +f AST_SAME = LOGICAL +f .TRUE. if the two pointers refer to the same Object, otherwise +f .FALSE. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - Two independent Objects that happen to be identical are not +* considered to be the same Object by this function. +c - A value of zero will be returned if this function is invoked +c with the AST error status set, or if it should fail for any reason. +f - A value of .FALSE. will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any reason. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the result. */ + return ( this == that ) ? 1 : 0; +} + +/* +*++ +* Name: +c astSet +f AST_SET + +* Purpose: +* Set an attribute value for an Object. + +* Type: +* Public functions. + +* Synopsis: +c #include "object.h" +c void astSet( AstObject *this, const char *attrib, type value ) +f CALL AST_SET( THIS, ATTRIB, VALUE, STATUS ) + +* Class Membership: +* Object methods. + +* Description: +c This is a family of functions which set a specified attribute +f This is a family of routines which set a specified attribute +* value for an Object using one of several different data +c types. The type is selected by replacing in the function name +f types. The type is selected by replacing in the routine name +c by C, D, F, I or L, to supply a value in const char* (i.e. string), +c double, float, int, or long format, respectively. +f by C, D, I, L or R, to supply a value in Character, Double +f precision, Integer, Logical or Real format, respectively. +* +* If possible, the value you supply is converted to the type of +* the attribute. If conversion is not possible, an error will +* result. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing the +c name of the attribute whose value is to be set. +f A character string containing the name of the attribute whose +f value is to be set. +c value +f VALUE = type (Given) +c The value to be set for the attribute, in the data type corresponding +c to (or, in the case of astSetC, a pointer to a null-terminated +c character string containing this value). +f The value to be set for the attribute, in the data type corresponding +f to . +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c These functions apply to all Objects. +f These routines apply to all Objects. + +* Examples: +c astSetI( frame, "Preserve", 1 ); +c Sets the Preserve attribute value for Object "frame" to 1. +c astSetC( plot, "Format(1)", "%.2g" ); +c Sets the Format(1) attribute value for Object "plot" to the +c character string "%.2g". +f CALL AST_SETC( PLOT, 'Title', CVALUE, STATUS ) +f Sets the Title attribute value for Object PLOT to the contents +f of the character variable CVALUE. +f CALL AST_SETL( FRAME, 'Preserve', .TRUE., STATUS ); +f Sets the Preserve attribute value for Object FRAME to 1 (true). + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +f - The logical value .FALSE. will translate to a numerical attribute +f value of zero and logical .TRUE. will translate to one. +* - An error will result if an attempt is made to set a value for +* a read-only attribute. +*-- +*/ + +/* Define a macro that expands to implement the astSetX_ member + functions required. The arguments to this macro are: + + code + The character that appears at the end of the function name. + type + The C type of the function "value" parameter. + format + A quoted string containing a sprintf format specifier that will + format the supplied value as a character string. This format should + consume 2 sprintf arguments: a field width and the value to be + formatted. + fmtlen + The number of characters in the format specifier (above). + fieldsz + The value of the field width to be used by the format specifier. +*/ +#define MAKE_SETX(code,type,format,fmtlen,fieldsz) \ +void astSet##code##_( AstObject *this, const char *attrib, type value, int *status ) { \ +\ +/* Local Variables: */ \ + char *setting; /* Pointer to attribute setting string */ \ + int len; /* Length of attribute name */ \ +\ +/* Check the global status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain the length of the attribute name and allocate memory to hold \ + this name plus the format specifier to be appended to it. */ \ + len = (int) astChrLen( attrib ); \ + setting = astMalloc( (size_t) ( len + fmtlen + 2 ) ); \ +\ +/* Make a copy of the attribute name in the allocated memory. */ \ + if ( astOK ) { \ + (void) memcpy( setting, attrib, (size_t) len ); \ + setting[ len ] = 0; \ +\ +/* Append "=", followed by the format specifier, to construct a \ + suitable "setting" string for use by astSet. */ \ + (void) strcat( setting, "=" format ); \ +\ +/* Invoke astSet to set the attribute value. */ \ + astSet( this, setting, status, fieldsz, value ); \ + } \ +\ +/* Free the allocated memory. */ \ + setting = astFree( setting ); \ +} + +/* Use this macro to create all the SetX_ private member functions. */ +MAKE_SETX(D,double,"%.*g",4,DBL_DIG) +MAKE_SETX(F,float,"%.*g",4,FLT_DIG) +MAKE_SETX(I,int,"%.*d",4,1) +MAKE_SETX(L,long,"%.*ld",5,1) + + +/* The astSetC_ function is implemented separately so that commas can be + handled. Since astSetC can only be used to set a single attribute + value, we know that any commas in the supplied value are included + within the attribuite value, rather than being used as delimiters + between adjacent attribute settings. To avoid VSet using them as + delimiters, they are replaced here by '\r' before calling astSet, and + VSet then converts them back to commas. */ + +void astSetC_( AstObject *this, const char *attrib, const char *value, int *status ) { + +/* Local Variables: */ + char *d; /* Pointer to next setting character */ + char *newv; /* Pointer to new attribute value string */ + char *setting; /* Pointer to attribute setting string */ + const char *c; /* Pointer to next value character */ + int len; /* Length of attribute name */ + +/* Check the global status. */ + if ( !astOK ) return; + +/* Produce a copy of the supplied attribute value in which any commas + are replaced by carriage returns ("\r"). */ + newv = astMalloc( (size_t)( strlen( value ) + 1 ) ); + if( newv ) { + d = newv; + c = value; + while( *c ) { + if( *c == ',' ) { + *d = '\r'; + } else { + *d = *c; + } + c++; + d++; + } + *d = 0; + +/* Obtain the length of the attribute name and allocate memory to hold + this name plus the format specifier to be appended to it. */ + len = (int) astChrLen( attrib ); + setting = astMalloc( (size_t) ( len + 5 ) ); + +/* Make a copy of the attribute name in the allocated memory. */ + if ( astOK ) { + (void) memcpy( setting, attrib, (size_t) len ); + setting[ len ] = 0; + +/* Append "=", followed by the format specifier, to construct a + suitable "setting" string for use by astSet. */ + (void) strcat( setting, "=%*s" ); + +/* Invoke astSet to set the attribute value. */ + astSet( this, setting, status, 0, newv ); + } + +/* Free the allocated memory. */ + setting = astFree( setting ); + } + newv = astFree( newv ); +} + +static void Show( AstObject *this, int *status ) { +/* +*++ +* Name: +c astShow +f AST_SHOW + +* Purpose: +* Display a textual representation of an Object on standard output. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astShow( AstObject *this ) +f CALL AST_SHOW( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function displays a textual description of any AST Object +f This routine displays a textual description of any AST Object +* on standard output. It is provided primarily as an aid to +* debugging. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object to be displayed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. +*-- +*/ + +/* Local Variables: */ + AstChannel *channel; /* Pointer to output Channel */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Create a Channel which will write to standard output. */ + channel = astChannel( NULL, NULL, "", status ); + +/* Write the Object to the Channel. */ + astWrite( channel, this ); + +/* Annul the Channel pointer. */ + channel = astAnnul( channel ); +} + +int astTest_( AstObject *this, const char *attrib, int *status ) { +/* +*++ +* Name: +c astTest +f AST_TEST + +* Purpose: +* Test if an Object attribute value is set. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astTest( AstObject *this, const char *attrib ) +f RESULT = AST_TEST( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function returns a boolean result (0 or 1) to indicate +f This function returns a logical result to indicate +* whether a value has been explicitly set for one of an Object's +* attributes. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing +c the name of the attribute to be tested. +f A character string containing the name of the attribute to be +f tested. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTest() +c One if a value has previously been explicitly set for the attribute +c (and hasn't been cleared), otherwise zero. +f AST_TEST = LOGICAL +f .TRUE. if a value has previously been explicitly set for the +f attribute (and hasn't been cleared), otherwise .FALSE.. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +c - A value of zero will be returned if this function is invoked +f - A value of .FALSE. will be returned if this function is invoked +c with the AST error status set, or if it should fail for any reason. +f with STATUS set to an error value, or if it should fail for any reason. +c - A value of zero will also be returned if this function is used +f - A value of .FALSE. will also be returned if this function is used +* to test a read-only attribute, although no error will result. +*-- +*/ + +/* Local Variables: */ + char *buff; /* Pointer to character buffer */ + int i; /* Loop counter for characters */ + int j; /* Non-blank character count */ + int len; /* Length of attrib string */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain the length of the attrib string. */ + len = (int) strlen( attrib ); + +/* Allocate memory and store a copy of the string. */ + buff = astStore( NULL, attrib, (size_t) ( len + 1 ) ); + if ( astOK ) { + +/* Remove white space and upper case characters. */ + for ( i = j = 0; buff[ i ]; i++ ) { + if ( !isspace( buff[ i ] ) ) buff[ j++ ] = tolower( buff[ i ] ); + } + +/* Terminate the attribute name and pass it to astTestAttrib to test + the attribute. */ + buff[ j ] = '\0'; + result = astTestAttrib( this, buff ); + } + +/* Free the memory allocated for the string buffer. */ + buff = astFree( buff ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int TestAttrib( AstObject *this, const char *attrib, int *status ) { +/* +*+ +* Name: +* astTestAttrib + +* Purpose: +* Test if a specified attribute value is set for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* int astTestAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of an Object's attributes. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check the attribute name and test the appropriate attribute. */ + +/* ID. */ +/* --- */ + if ( !strcmp( attrib, "id" ) ) { + result = astTestID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astTestIdent( this ); + +/* UseDefs */ +/* ------- */ + } else if ( !strcmp( attrib, "usedefs" ) ) { + result = astTestUseDefs( this ); + +/* Test if the attribute string matches any of the read-only + attributes of this class. If it does, then return zero. */ + } else if ( !strcmp( attrib, "class" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "objsize" ) || + !strcmp( attrib, "refcount" ) ) { + result = 0; + +/* Any attempt to test any other attribute is an error. */ + } else if( astOK ){ + astError( AST__BADAT, "astTest: The attribute name \"%s\" is invalid " + "for a %s.", status, attrib, astGetClass( this ) ); + } + +/* Return the result, */ + return result; +} + +int astTune_( const char *name, int value, int *status ) { +/* +*++ +* Name: +c astTune +f AST_TUNE + +* Purpose: +* Set or get an integer-valued AST global tuning parameter. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astTune( const char *name, int value ) +f RESULT = AST_TUNE( NAME, VALUE, STATUS ) + +* Class Membership: +* Object function. + +* Description: +* This function returns the current value of an integer-valued AST +* global tuning parameter, optionally storing a new value for the +* parameter. For character-valued tuning parameters, see +c astTuneC. +f AST_TUNEC. + +* Parameters: +c name +f NAME = CHARACTER * ( * ) (Given) +* The name of the tuning parameter (case-insensitive). +c value +f VALUE = INTEGER (Given) +* The new value for the tuning parameter. If this is AST__TUNULL, +* the existing current value will be retained. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTune() +f AST_TUNE = INTEGER +c The original value of the tuning parameter. A default value will +* be returned if no value has been set for the parameter. + +* Tuning Parameters: +* ObjectCaching +* A boolean flag which indicates what should happen +* to the memory occupied by an AST Object when the Object is deleted +* (i.e. when its reference count falls to zero or it is deleted using +c astDelete). +f AST_DELETE). +* If this is zero, the memory is simply freed using the systems "free" +* function. If it is non-zero, the memory is not freed. Instead a +* pointer to it is stored in a pool of such pointers, all of which +* refer to allocated but currently unused blocks of memory. This allows +* AST to speed up subsequent Object creation by re-using previously +* allocated memory blocks rather than allocating new memory using the +* systems malloc function. The default value for this parameter is +* zero. Setting it to a non-zero value will result in Object memory +* being cached in future. Setting it back to zero causes any memory +* blocks currently in the pool to be freed. Note, this tuning parameter +* only controls the caching of memory used to store AST Objects. To +* cache other memory blocks allocated by AST, use MemoryCaching. +* MemoryCaching +* A boolean flag similar to ObjectCaching except +* that it controls caching of all memory blocks of less than 300 bytes +* allocated by AST (whether for internal or external use), not just +* memory used to store AST Objects. + +* Notes: +c - This function attempts to execute even if the AST error +c status is set +f - This routine attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +* - All threads in a process share the same AST tuning parameters +* values. +*-- +*/ + + int result = AST__TUNULL; + + if( name ) { + + LOCK_MUTEX1; + + if( astChrMatch( name, "ObjectCaching" ) ) { + result = object_caching; + if( value != AST__TUNULL ) { + object_caching = value; + if( !object_caching ) EmptyObjectCache( status ); + } + + } else if( astChrMatch( name, "MemoryCaching" ) ) { + result = astMemCaching( value ); + + } else if( astOK ) { + astError( AST__TUNAM, "astTune: Unknown AST tuning parameter " + "specified \"%s\".", status, name ); + } + + UNLOCK_MUTEX1; + + } + + return result; +} + +void astTuneC_( const char *name, const char *value, char *buff, + int bufflen, int *status ) { +/* +*++ +* Name: +c astTuneC +f AST_TUNEC + +* Purpose: +* Set or get a character-valued AST global tuning parameter. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astTuneC( const char *name, const char *value, char *buff, +c int bufflen ) +f CALL AST_TUNEC( NAME, VALUE, BUFF, STATUS ) + +* Class Membership: +* Object function. + +* Description: +* This function returns the current value of a character-valued +* AST global tuning parameter, optionally storing a new value +* for the parameter. For integer-valued tuning parameters, see +c astTune. +f AST_TUNE. + +* Parameters: +c name +f NAME = CHARACTER * ( * ) (Given) +* The name of the tuning parameter (case-insensitive). +c value +f VALUE = CHARACTER * ( ) (Given) +* The new value for the tuning parameter. If this is +f AST__TUNULLC, +c NULL, +* the existing current value will be retained. +c buff +f BUFF = CHARACTER * ( ) (Given) +* A character string in which to return the original value of +* the tuning parameter. An error will be reported if the buffer +* is too small to hold the value. +c NULL may be supplied if the old value is not required. +c bufflen +c The size of the supplied "buff" array. Ignored if "buff" is NULL. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Tuning Parameters: +* HRDel +* A string to be drawn following the hours field in a formatted +* sky axis value when "g" format is in use (see the Format +* attribute). This string may include escape sequences to produce +* super-scripts, etc. (see the Escapes attribute for details +* of the escape sequences allowed). The default value is +* "%-%^50+%s70+h%+" which produces a super-script "h". +* MNDel +* A string to be drawn following the minutes field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^50+%s70+m%+" which produces a super-script "m". +* SCDel +* A string to be drawn following the seconds field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^50+%s70+s%+" which produces a super-script "s". +* DGDel +* A string to be drawn following the degrees field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^53+%s60+o%+" which produces a super-script "o". +* AMDel +* A string to be drawn following the arc-minutes field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^20+%s85+'%+" which produces a super-script "'" (single quote). +* ASDel +* A string to be drawn following the arc-seconds field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^20+%s85+\"%+" which produces a super-script """ (double quote). +* EXDel +* A string to be drawn to introduce the exponent in a value when "g" +* format is in use. The default value is "10%-%^50+%s70+" which +* produces "10" followed by the exponent as a super-script. + +* Notes: +c - This function attempts to execute even if the AST error +c status is set +f - This routine attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +* - All threads in a process share the same AST tuning parameters +* values. +*-- +*/ + +/* Local Variables: */ + char *p = NULL; + int len; + +/* Check the name of a tuning parameter was supplied. */ + if( name ) { + +/* Serialise access to the tuning parameters since they are common to all + threads. */ + LOCK_MUTEX1; + +/* Get a pointer to the buffer that holds the value of the requested + tuning parameter. */ + if( astChrMatch( name, "hrdel" ) ) { + p = hrdel; + } else if( astChrMatch( name, "mndel" ) ) { + p = mndel; + } else if( astChrMatch( name, "scdel" ) ) { + p = scdel; + } else if( astChrMatch( name, "dgdel" ) ) { + p = dgdel; + } else if( astChrMatch( name, "amdel" ) ) { + p = amdel; + } else if( astChrMatch( name, "asdel" ) ) { + p = asdel; + } else if( astChrMatch( name, "exdel" ) ) { + p = exdel; + +/* Report an error if an the tuning parameter name is unknown. */ + } else if( astOK ) { + p = NULL; + astError( AST__TUNAM, "astTuneC: Unknown AST tuning parameter " + "specified \"%s\".", status, name ); + } + +/* If the existing value was found. */ + if( p ) { + +/* And is to be returned in the supplied buffer... */ + if( buff ) { + +/* Check that the buffer is long enough. If so, copy the current value + into the buffer, otherwise report an error. */ + len = strlen( p ) ; + if( len < bufflen ) { + strcpy( buff, p ); + } else { + astError( AST__TUNAM, "astTuneC: Supplied string variable " + "is too small - the current '%s' value (%s) has " + "%d characters.", status, name, p, len ); + } + } + +/* If a new value is to be stored.... */ + if( value ) { + +/* Report an error if it is too long to fit in the static buffer. */ + len = strlen( value ) ; + if( len >= MAXLEN_TUNEC ) { + astError( AST__TUNAM, "astTuneC: Supplied value for '%s' " + "(%s) is too long - must not be longer than %d " + "characters.", status, name, value, MAXLEN_TUNEC ); + +/* Otherwise, copy the new value into the static buffer. */ + } else { + strcpy( p, value ); + } + } + } + + UNLOCK_MUTEX1; + } +} + +AstObject *astFromString_( const char *string, int *status ) { +/* +c++ +* Name: +* astFromString + +* Purpose: +* Re-create an Object from an in-memory serialisation + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* AstObject *astFromString( const char *string ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a pointer to a new Object created from the +* supplied text string, which should have been created by astToString. + +* Parameters: +* string +* Pointer to a text string holding an Object serialisation created +* previously by astToString. + +* Returned Value: +* astFromString() +* Pointer to a new Object created from the supplied serialisation, +* or NULL if the serialisation was invalid, or an error occurred. + +c-- +*/ + +/* Local Variables: */ + StringData data; /* Data passed to the source function */ + AstChannel *channel; /* Pointer to output Channel */ + AstObject *result; /* Pointer to returned Object */ + +/* Check the global error status and supplied serialisation. */ + if ( !astOK || !string ) return NULL; + +/* Create a Channel which will read from the supplied serialisation. */ + channel = astChannel( FromStringSource, NULL, "", status ); + +/* Initialise the data structure used to communicate with the source + function, and store a pointer to it in the Channel. */ + data.ptr = (char *) string; + data.buff = NULL; + data.len = 0; + astPutChannelData( channel, &data ); + +/* Read an Object from the Channel. */ + result = astRead( channel ); + +/* Annul the Channel pointer. */ + channel = astAnnul( channel ); + +/* Free the line buffer. */ + data.buff = astFree( data.buff ); + +/* Annul the returned Object if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the Object pointer. */ + return result; +} + +static const char *FromStringSource( void ){ +/* +* Name: +* FromStringSource + +* Purpose: +* A Channel source function for use by the astFromString method. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* result = FromStringSource( void ) + +* Class Membership: +* Object member function. + +* Description: +* This function reads the next line of text from a serialisation and +* returns a pointer to it, or NULL if no lines remain. + +* Returned Value: +* Pointer to the null terminated line of text or NULL if no lines +* remain. +*/ + +/* Local Variables: */ + StringData *data; /* Data passed to the sink function */ + char *nl; /* Pointer to next newline character */ + int *status; /* Pointer to local status value */ + int nc; /* Number of characters to read from serialisation */ + int status_value; /* Local status value */ + +/* Set up the local status */ + status_value = 0; + status = &status_value; + +/* Get a pointer to the structure holding a pointer to the next line, and + to the buffer to return. */ + data = astChannelData; + +/* Return NULL if no text remains to be read. */ + if( !data->ptr || (data->ptr)[0] == 0 ) return NULL; + +/* Find the next newline (if any) in the serialisation. */ + nl = strchr( data->ptr, '\n' ); + +/* Get the number of characters to copy. */ + nc = nl ? nl - data->ptr : strlen( data->ptr ); + +/* Copy them into the returned buffer, including an extra character for + the terminating null. */ + data->buff = astStore( data->buff, data->ptr, nc + 1 ); + +/* Store the terminating null. */ + (data->buff)[ nc ] = 0; + +/* Update the pointer to the next character to read from the + serialisation. */ + data->ptr = nl ? nl + 1 : NULL; + +/* Return the buffer. */ + return data->buff; +} + +static void VSet( AstObject *this, const char *settings, char **text, + va_list args, int *status ) { +/* +*+ +* Name: +* astVSet + +* Purpose: +* Set values for an Object's attributes. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astVSet( AstObject *this, const char *settings, char **text, +* va_list args ) + +* Class Membership: +* Object method. + +* Description: +* This function assigns a set of attribute values for an Object, +* the attributes and their values being specified by means of a +* string containing a comma-separated list of the form: +* +* "attribute1 = value1, attribute2 = value2, ... " +* +* Here, "attribute" specifies an attribute name and the value to +* the right of each "=" sign should be a suitable textual +* representation of the value to be assigned to that +* attribute. This will be interpreted according to the attribute's +* data type. +* +* The string supplied may also contain "printf"-style format +* specifiers identified by a "%" sign in the usual way. If +* present, these will be substituted by values supplied as +* optional arguments (as a va_list variable argument list), using +* the normal "printf" rules, before the string is used. + +* Parameters: +* this +* Pointer to the Object. +* settings +* Pointer to a null-terminated string containing a +* comma-separated list of attribute settings. +* text +* Pointer to a location at which to return a pointer to dynamic +* memory holding a copy of the expanded setting string. This memory +* should be freed using astFree when no longer needed. If a NULL +* pointer is supplied, no string is created. +* args +* The variable argument list which contains values to be +* substituted for any "printf"-style format specifiers that +* appear in the "settings" string. + +* Notes: +* - Attribute names are not case sensitive. +* - White space may surround attribute names and will be ignored. +* - White space may also surround attribute values where it will +* be ignored (except for string-valued attributes where it is +* significant and forms part of the value to be assigned). +* - After this function has substituted values for "printf"-style +* format specifiers it splits the "settings" string into +* individual attribute settings which it passes one at a time to +* the protected astSetAttrib method (after removal of white space +* and conversion of attribute names to lower case). The +* astSetAttrib method should therefore be extended by derived +* classes which define new attributes, and this will allow the +* astVSet (and astSet) methods to have access to those attributes. +* - This function provides the same functionality as the astSet +* public method but accepts a va_list variable argument list +* instead of a variable number of arguments. It is provided for +* use by functions in other class implementations which accept a +* variable number of arguments and must therefore pass their +* argument list to this method in va_list form. +*- +*/ + +#define MIN_BUFF_LEN 1024 +#define ERRBUF_LEN 80 + +/* Local Variables: */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + char setting_buf[ MIN_BUFF_LEN ]; /* Expanded "%s" settting string */ + char *dyn_buf; /* Pointer to dynamic buffer for expanded setting */ + char *errstat; /* Pointer to error message */ + char *assign; /* Pointer to assigment substring */ + char *assign_end; /* Pointer to null at end of assignment */ + char *buff1; /* Pointer to temporary string buffer */ + char *buff2; /* Pointer to temporary string buffer */ + char *buff3; /* Pointer to temporary string buffer */ + char *eq1; /* Pointer to 1st equals sign */ + int buff_len; /* Length of temporary buffer */ + int expanded; /* Has the Settings string been expanded yet? */ + int i; /* Loop counter for characters */ + int j; /* Offset for revised assignment character */ + int len; /* Length of settings string */ + int lo; /* Convert next character to lower case? */ + int nc; /* Number of vsprintf output characters */ + int quoted; /* Are we in a quoted string? */ + int stat; /* Value of errno after an error */ + int tq; /* Test if the next non-space is a quote? */ + +/* Initialise */ + if( text ) *text = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the length of the "settings" string and test it is not + zero. If it is, there is nothing more to do. */ + len = (int) strlen( settings ); + if ( len != 0 ) { + +/* If the setting string is just "%s" (with optional trailing and leading + white space) then the variable argument potentially contains more than + one attribute setting, in which case we expand the setting string now + and use the expanded string in place of the supplied string in the rest + of this function. */ + nc = 0; + sscanf( settings, " %%s %n", &nc ); + if( nc == len ) { + +/* Expand the supplied string using a fixed-length buffer. This writes at + most MIN_BUFF_LEN characters to "buf", but returns the number of + characters that would have been needed to write the whole string. */ + len = vsnprintf( setting_buf, sizeof(setting_buf), settings, args ); + +/* If the fixed-length buffer is too short, use a dynamically allocated + buffer instead. */ + if( len + 1 > MIN_BUFF_LEN ) { + dyn_buf = astMalloc( len + 1 ); + if( astOK ) { + len = vsnprintf( dyn_buf, len + 1, settings, args ); + settings = dyn_buf; + } + } else { + dyn_buf = NULL; + settings = setting_buf; + } + +/* Indicate that "settings" has been expanded. */ + expanded = 1; + + } else { + expanded = 0; + dyn_buf = NULL; + } + +/* Allocate memory and store a copy of the string. */ + buff1 = astStore( NULL, settings, (size_t) ( len + 1 ) ); + if ( astOK ) { + +/* Convert each comma in the string into '\n'. This is to distinguish + commas initially present from those introduced by the formatting to + be performed below. We only do this if there is more than one equals + sign in the setting string, since otherwise any commas are probably + characters contained within a string attribute value. Ignore commas + that occur within quoted strings. */ + eq1 = strchr( buff1, '=' ); + if( eq1 && strchr( eq1 + 1, '=' ) ) { + quoted = 0; + for ( i = 0; i < len; i++ ) { + if( !quoted ) { + if ( buff1[ i ] == ',' ) { + buff1[ i ] = '\n'; + } else if( buff1[ i ] == '"' ) { + quoted = 1; + } + } else if( buff1[ i ] == '"' ){ + quoted = 0; + } + } + } + +/* Calculate a size for a further buffer twice the size of the first + one. Ensure it is not less than a minimum size and then allocate + this buffer. */ + buff_len = 2 * len; + if ( buff_len < MIN_BUFF_LEN ) buff_len = MIN_BUFF_LEN; + buff2 = astMalloc( (size_t) ( buff_len + 1 ) ); + if ( astOK ) { + +/* Use "vsprintf" to substitute values for any format specifiers in + the "settings" string, writing the resulting string into the second + buffer. If the "settings" string has already been expanded, then just + copy it. */ + errno = 0; + if( !expanded ) { + nc = vsprintf( buff2, buff1, args ); + } else { + strcpy( buff2, buff1 ); + nc = strlen( buff1 ); + } + +/* Get a copy of the expanded string to return as the function value and + convert newlines back to commas. */ + if( text ) { + *text = astStore( NULL, buff2, nc + 1 ); + if( *text ) { + for ( i = 0; i <= nc; i++ ) { + if ( (*text)[ i ] == '\n' ) (*text)[ i ] = ','; + } + } + } + +/* The possibilities for error detection are limited here, but check + if an error value was returned and report an error. Include + information from errno if it was set. */ + if ( nc < 0 ) { + if( astOK ) { + stat = errno; + + if( stat ) { +#if HAVE_STRERROR_R + strerror_r( stat, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( stat ); +#endif + } else { + *errbuf = 0; + errstat = errbuf; + } + + astError( AST__ATSER, "astVSet(%s): Error formatting an " + "attribute setting%s%s.", status, astGetClass( this ), + stat? " - " : "", errstat ); + astError( AST__ATSER, "The setting string was \"%s\".", status, + settings ); + } + +/* Also check that the result buffer did not overflow. If it did, + memory will probably have been corrupted but this cannot be + prevented with "vsprintf" (although we try and make the buffer + large enough). Report the error and abort. */ + } else if ( nc > buff_len ) { + if( astOK ) { + astError( AST__ATSER, "astVSet(%s): Internal buffer overflow " + "while formatting an attribute setting - the result " + "exceeds %d characters.", status, astGetClass( this ), + buff_len ); + astError( AST__ATSER, "The setting string was \"%s\".", status, + settings ); + } + +/* If all is OK, loop to process each formatted attribute assignment + (these are now separated by '\n' characters). */ + } else { + assign = buff2; + while ( assign ) { + +/* Change the '\n' at the end of each assignment to a null to + terminate it. */ + if ( ( assign_end = strchr( assign, '\n' ) ) ) { + *assign_end = '\0'; + } + +/* Before making the assignment, loop to remove white space and upper + case characters from the attribute name. */ + lo = 1; + tq = -1; + quoted = 0; + for ( i = j = 0; assign[ i ]; i++ ) { + +/* Note when an '=' sign is encountered (this signals the end of the + attribute name). */ + if ( assign[ i ] == '=' ) lo = 0; + +/* Before the '=' sign, convert all characters to lower case and move + everything to the left to eliminate white space. Afer the '=' sign, + copy all characters to their new location unchanged, except for any + delimiting quotes, which are removed. astSetC replaces commas in the + attribute value by '\r' characters. Reverse this now. */ + if ( !lo || !isspace( assign[ i ] ) ) { + if( assign[ i ] == '\r' ) { + assign[ j++ ] = ','; + + } else if( lo ) { + assign[ j++ ] = tolower( assign[ i ] ); + + } else { + assign[ j++ ] = assign[ i ]; + + if( tq > 0 && !isspace( assign[ i ] ) ) { + if( assign[ i ] == '"' ) { + quoted = 1; + j--; + } + tq = 0; + } + + } + } + +/* If the current character is the initial '=' sign, set "tq" positive, + meaning "check if the next non-space character is a quote". */ + if ( assign[ i ] == '=' && tq == -1 ) tq = 1; + } + +/* if the value was quoted. remove the trailing quote. */ + if( quoted ) { + j--; + while( isspace( assign[ j ] ) ) j--; + if( assign[ j ] == '"' ) j--; + j++; + } + +/* Terminate the revised assignment string and pass it to astSetAttrib + to make the assignment (unless the string was all blank, in which + case we ignore it). */ + assign[ j ] = '\0'; + if ( j ) { + +/* If there are no characters to the right of the equals sign append a + space after the equals sign. Without this, a string such as "Title=" + would not be succesfully matched against the attribute name "Title" + within SetAttrib. */ + if( assign[ j - 1 ] == '=' ) { + buff3 = astStore( NULL, assign, + (size_t) j + 2 ); + if ( astOK ) { + buff3[ j ] = ' '; + buff3[ j + 1 ] = '\0'; + astSetAttrib( this, buff3 ); + } + buff3 = astFree( buff3 ); + + } else { + astSetAttrib( this, assign ); + } + } + +/* Check for errors and abort if any assignment fails. Otherwise, + process the next assignment substring. */ + if ( !astOK ) break; + assign = assign_end ? assign_end + 1 : NULL; + } + } + } + +/* Free the memory allocated for string buffers. */ + buff2 = astFree( buff2 ); + dyn_buf = astFree( dyn_buf ); + } + buff1 = astFree( buff1 ); + } +} +#undef ERRBUF_LEN +#undef MIN_BUFF_LEN + +/* Attribute access functions. */ +/* --------------------------- */ +/* +*att++ +* Name: +* Class + +* Purpose: +* Object class name. + +* Type: +* Public attribute. + +* Synopsis: +* Character string, read-only. + +* Description: +* This attribute gives the name of the class to which an Object +* belongs. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* ID + +* Purpose: +* Object identification string. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute contains a string which may be used to identify +* the Object to which it is attached. There is no restriction on +* the contents of this string, which is not used internally by the +* AST library, and is simply returned without change when +* required. The default value is an empty string. +* +* An identification string can be valuable when, for example, +c several Objects have been stored in a file (using astWrite) and +f several Objects have been stored in a file (using AST_WRITE) and +c are later retrieved (using astRead). Consistent use of the ID +f are later retrieved (using AST_READ). Consistent use of the ID +* attribute allows the retrieved Objects to be identified without +* depending simply on the order in which they were stored. +* +* This attribute may also be useful during debugging, to +c distinguish similar Objects when using astShow to display them. +f distinguish similar Objects when using AST_SHOW to display them. + +* Applicability: +* Object +* All Objects have this attribute. + +* Notes: +* - Unlike most other attributes, the value of the ID attribute is +* not transferred when an Object is copied. Instead, its value is +* undefined (and therefore defaults to an empty string) in any +* copy. However, it is retained in any external representation of +c an Object produced by the astWrite function. +f an Object produced by the AST_WRITE routine. +*att-- +*/ +/* Clear the ID value by freeing the allocated memory and assigning a + NULL pointer. */ +astMAKE_CLEAR(Object,ID,id,astFree( this->id )) + +/* If the ID value is not set, supply a default in the form of a + pointer to the constant string "". */ +astMAKE_GET(Object,ID,const char *,NULL,( this->id ? this->id : "" )) + +/* Set an ID value by freeing any previously allocated memory, + allocating new memory and storing the string. */ +astMAKE_SET(Object,ID,const char *,id,astStore( this->id, value, + strlen( value ) + (size_t) 1 )) + +/* The ID value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Object,ID,( this->id != NULL )) + +/* +*att++ +* Name: +* Ident + +* Purpose: +* Permanent Object identification string. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute is like the ID attribute, in that it contains a +* string which may be used to identify the Object to which it is +* attached. The only difference between ID and Ident is that Ident +* is transferred when an Object is copied, but ID is not. + +* Applicability: +* Object +* All Objects have this attribute. + +*att-- +*/ +/* Clear the Ident value by freeing the allocated memory and assigning a + NULL pointer. */ +astMAKE_CLEAR(Object,Ident,ident,astFree( this->ident )) + +/* If the Ident value is not set, supply a default in the form of a + pointer to the constant string "". */ +astMAKE_GET(Object,Ident,const char *,NULL,( this->ident ? this->ident : "" )) + +/* Set an Ident value by freeing any previously allocated memory, + allocating new memory and storing the string. */ +astMAKE_SET(Object,Ident,const char *,ident,astStore( this->ident, value, + strlen( value ) + (size_t) 1 )) + +/* The Ident value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Object,Ident,( this->ident != NULL )) + +/* +*att++ +* Name: +* UseDefs + +* Purpose: +* Use default values for unspecified attributes? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute specifies whether default values should be used +* internally for object attributes which have not been assigned a +* value explicitly. If a non-zero value (the default) is supplied for +* UseDefs, then default values will be used for attributes which have +* not explicitly been assigned a value. If zero is supplied for UseDefs, +* then an error will be reported if an attribute for which no explicit +* value has been supplied is needed internally within AST. +* +* Many attributes (including the UseDefs attribute itself) are unaffected +* by the setting of the UseDefs attribute, and default values will always +* be used without error for such attributes. The "Applicability:" section +* below lists the attributes which are affected by the setting of UseDefs. + +* Note, UseDefs only affects access to attributes internally within +* AST. The public accessor functions such as +c astGetC +f AST_GETC +* is unaffected by the UseDefs attribute - default values will always +* be returned if no value has been set. Application code should use the +c astTest +f AST_TEST +* function if required to determine if a value has been set for an +* attribute. + +* Applicability: +* Object +* All Objects have this attribute, but ignore its setting except +* as described below for individual classes. +* FrameSet +* The default value of UseDefs for a FrameSet is redefined to be +* the UseDefs value of its current Frame. +* CmpFrame +* The default value of UseDefs for a CmpFrame is redefined to be +* the UseDefs value of its first component Frame. +* Region +* The default value of UseDefs for a Region is redefined to be +* the UseDefs value of its encapsulated Frame. +* Frame +* If UseDefs is zero, an error is reported when aligning Frames if the +* Epoch, ObsLat or ObsLon attribute is required but has not been +* assigned a value explicitly. +* SkyFrame +* If UseDefs is zero, an error is reported when aligning SkyFrames +* if any of the following attributes are required but have not been +* assigned a value explicitly: Epoch, Equinox. +* SpecFrame +* If UseDefs is zero, an error is reported when aligning SpecFrames +* if any of the following attributes are required but have not been +* assigned a value explicitly: Epoch, RefRA, RefDec, RestFreq, +* SourceVel, StdOfRest. +* DSBSpecFrame +* If UseDefs is zero, an error is reported when aligning DSBSpecFrames +* or when accessing the ImagFreq attribute if any of the following +* attributes are required but have not been assigned a value explicitly: +* Epoch, DSBCentre, IF. +*att-- +*/ +astMAKE_CLEAR(Object,UseDefs,usedefs,CHAR_MAX) +astMAKE_GET(Object,UseDefs,int,1,((this->usedefs!=CHAR_MAX)?this->usedefs:1)) +astMAKE_SET(Object,UseDefs,int,usedefs,((value)?1:0)) +astMAKE_TEST(Object,UseDefs,(this->usedefs!=CHAR_MAX)) + +/* +*att++ +* Name: +* Nobject + +* Purpose: +* Number of Objects in class. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the total number of Objects currently in +* existence in the same class as the Object whose attribute value +* is requested. This count does not include Objects which belong +* to derived (more specialised) classes. +* +* This attribute is mainly intended for debugging. It can be used +* to detect whether Objects which should have been deleted have, +* in fact, been deleted. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* ObjSize + +* Purpose: +* The in-memory size of the Object. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the total number of bytes of memory used by +* the Object. This includes any Objects which are encapsulated within +* the supplied Object. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* RefCount + +* Purpose: +* Count of active Object pointers. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the number of active pointers associated +* with an Object. It is modified whenever pointers are created or +c annulled (by astClone, astAnnul or astEnd for example). The count +f annulled (by AST_CLONE, AST_ANNUL or AST_END for example). The count +* includes the initial pointer issued when the Object was created. +* +* If the reference count for an Object falls to zero as the result +* of annulling a pointer to it, then the Object will be deleted. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* Standard class functions. */ +/* ========================= */ +/* +*+ +* Name: +* astCheck + +* Purpose: +* Validate class membership. + +* Type: +* Protected function. + +* Synopsis: +* #include "class.h" +* Ast *astCheck( Ast *this ) + +* Class Membership: +* class function. + +* Description: +* This function validates membership of the class called , +* or of any class derived from it. If the Object is not a member, +* or the pointer supplied does not identify a valid Object, an +* error is reported and the global error status is set to +* AST__OBJIN. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The function always returns a copy of the "this" pointer +* (whether it finds it valid or not). + +* Notes: +* - Each class provides a function (astCheck) of this form, +* where and are replaced by the class name (with +* appropriate capitalisation). +* - Normal error status checking is performed, so this function +* will not execute if the global error status is set on entry (the +* usual function value will be returned, however). +* - This function is primarily intended for validating Object +* pointers passed to member functions as part of a class +* interface. +*- +*/ + +/* Implement the astCheckObject function using the macro defined for this + purpose in the "object.h" header file. */ +astMAKE_CHECK(Object) + +int astIsAObject_( const AstObject *this, int *status ) { +/* +*++ +* Name: +c astIsA +f AST_ISA + +* Purpose: +* Test membership of a class by an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "class.h" +c int astIsA( const Ast *this ) +f RESULT = AST_ISA( THIS, STATUS ) + +* Class Membership: +* Class function. + +* Description: +* This is a family of functions which test whether an Object is a +c member of the class called , or of any class derived from +f member of the class called , or of any class derived from +* it. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astIsA() +c One if the Object belongs to the class called (or to a +c class derived from it), otherwise zero. +f AST_ISA = LOGICAL +f .TRUE. if the Object belongs to the class called (or to +f a class derived from it), otherwise .FALSE.. + +* Applicability: +* Object +* These functions apply to all Objects. + +* Examples: +c member = astIsAFrame( obj ); +c Tests whether Object "obj" is a member of the Frame class, or +c of any class derived from a Frame. +f MEMBER = AST_ISAFRAME( OBJ, STATUS ); +f Tests whether Object OBJ is a member of the Frame class, or +f of any class derived from a Frame. + +* Notes: +c - Every AST class provides a function (astIsA) of this +c form, where should be replaced by the class name. +f - Every AST class provides a function (AST_ISA) of this +f form, where should be replaced by the class name. +c - This function attempts to execute even if the AST error status +c is set +f - This function attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be made +* if it subsequently fails under these circumstances. +c - A value of zero will be returned if this function should fail +f - A value of .FALSE. will be returned if this function should fail +* for any reason. In particular, it will fail if the pointer +* supplied does not identify an Object of any sort. +*-- +*/ + +/* Local Variables: */ + int valid; /* Valid object? */ + +/* Since this is the base class, the implementation of this function + differs from that in derived classes (in that it fails and + potentially reports an error if the returned result is zero). */ + +/* Initialise. */ + valid = 0; + +/* Check if a NULL pointer was supplied (this can never be valid). If + OK, check if the Object contains the correct "magic number" in its + check field. */ + if ( !this || ( this->check != Magic( this, this->size, status ) ) ) { + +/* If it is not a valid Object, then report an error (but only if the + global error status has not already been set). */ + if ( astOK ) { + astError( AST__OBJIN, "astIsAObject(%s): Invalid Object pointer " + "given (points at address %p).", status, astGetClass( this ), + (void *) this ); + } + +/* Otherwise, note that the Object is valid. */ + } else { + valid = 1; + } + +/* Return the result. */ + return valid; +} + +void astInitObjectVtab_( AstObjectVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitObjectVtab + +* Purpose: +* Initialise a virtual function table for a Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astInitObjectVtab( AstObjectVtab *vtab, const char *name ) + +* Class Membership: +* Object vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Object class. + +* Parameters: +* vtab +* Pointer to the virtual function table. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int ivtab; /* Index of next entry in known_vtabs */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Initialise the contents of the class identifier. Since this is the + base class, we assign null values to the fields. */ + vtab->id.check = NULL; + vtab->id.parent = NULL; + +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->Clear = Clear; + vtab->ClearAttrib = ClearAttrib; + vtab->ClearID = ClearID; + vtab->ClearIdent = ClearIdent; + vtab->Dump = Dump; + vtab->Equal = Equal; + vtab->GetAttrib = GetAttrib; + vtab->GetID = GetID; + vtab->GetIdent = GetIdent; + vtab->HasAttribute = HasAttribute; + vtab->Same = Same; + vtab->SetAttrib = SetAttrib; + vtab->SetID = SetID; + vtab->SetIdent = SetIdent; + vtab->Show = Show; + vtab->TestAttrib = TestAttrib; + vtab->TestID = TestID; + vtab->TestIdent = TestIdent; + vtab->EnvSet = EnvSet; + vtab->VSet = VSet; + vtab->Cast = Cast; + vtab->GetObjSize = GetObjSize; + vtab->CleanAttribs = CleanAttribs; + + vtab->TestUseDefs = TestUseDefs; + vtab->SetUseDefs = SetUseDefs; + vtab->ClearUseDefs = ClearUseDefs; + vtab->GetUseDefs = GetUseDefs; + +#if defined(THREAD_SAFE) + vtab->ManageLock = ManageLock; +#endif + +/* Store the pointer to the class name. */ + vtab->class = name; + +/* Initialise the count of active objects and the number of destructors, + copy constructors and dump functions. */ + vtab->nobject = 0; + vtab->ndelete = 0; + vtab->ncopy = 0; + vtab->ndump = 0; + +/* Initialise the arrays of destructor, copy constructor and dump + function pointers. */ + vtab->delete = NULL; + vtab->copy = NULL; + vtab->dump = NULL; + vtab->dump_class = NULL; + vtab->dump_comment = NULL; + +/* Initialise the default attributes to use when creating objects. */ + vtab->defaults = NULL; + +/* The virtual function table for each class contains a list of pointers + to memory blocks which have previously been used to store an Object of + the same class, but which have since been deleted using astDelete. + These memory blocks are free to be re-used when a new Object of the + same class is initialised. This saves on the overheads associated with + continuously allocating small blocks of memory using malloc. */ + vtab->nfree = 0; + vtab->free_list = NULL; + +/* Add the supplied virtual function table pointer to the end of the list + of known vtabs. */ + ivtab = nvtab++; + + astBeginPM; + known_vtabs = astGrow( known_vtabs, nvtab, sizeof( AstObjectVtab *) ); + astEndPM; + + if( astOK && known_vtabs ) known_vtabs[ ivtab ] = vtab; + +/* Fill a pointer value with zeros (not necessarily the same thing as a + NULL pointer) for subsequent use. */ + (void) memset( &zero_ptr, 0, sizeof( AstObject * ) ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised. */ + if( vtab == &class_vtab ) class_init = 1; +} + +AstObject *astInitObject_( void *mem, size_t size, int init, + AstObjectVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitObject + +* Purpose: +* Initialise an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astInitObject( void *mem, size_t size, int init, +* AstObjectVtab *vtab, const char *name ) + +* Class Membership: +* Object initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Object. It allocates memory (if necessary) to accommodate the +* Object plus any additional data associated with the derived class. It +* then initialises an Object structure at the start of this memory. If the +* "init" flag is set, it also initialises the contents of a virtual +* function table for an Object at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Object is to be initialised. +* This must be of sufficient size to accommodate the Object data +* (sizeof(Object)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Object (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Object +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Object's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Object. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). + +* Returned Value: +* A pointer to the new Object. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstObject *new; /* Pointer to new Object */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Determine if memory must be allocated dynamically. If so, use the last + block of memory in the list of previously allocated but currently + unused blocks identified by the vtab "free_list" array, reducing the + length of the free list by one, and nullifying the entry in the list + for safety. If the list is originally empty, allocate memory for a new + object using astMalloc. */ + if( !mem ) { + if( object_caching ) { + if( vtab->nfree > 0 ) { + mem = vtab->free_list[ --(vtab->nfree) ]; + vtab->free_list[ vtab->nfree ] = NULL; + if( astSizeOf( mem ) != size && astOK ) { + astError( AST__INTER, "astInitObject(%s): Free block has size " + "%d but the %s requires %d bytes (internal AST " + "programming error).", status, vtab->class, + (int) astSizeOf( mem ), vtab->class, (int) size ); + } + } else { + mem = astMalloc( size ); + } + + } else { + mem = astMalloc( size ); + } + +/* If memory had already been allocated, adjust the "size" value to match + the size of the allocated memory. */ + } else { + size = astSizeOf( mem ); + } + +/* Obtain a pointer to the new Object. */ + if ( astOK ) { + new = (AstObject *) mem; + +/* Zero the entire new Object structure (to prevent accidental re-use + of any of its values after deletion). */ + (void) memset( new, 0, size ); + +/* If necessary, initialise the virtual function table. */ +/* ---------------------------------------------------- */ + if ( init ) astInitObjectVtab( vtab, name ); + if( astOK ) { + +/* Initialise the Object data. */ +/* --------------------------- */ +/* Store a unique "magic" value in the Object structure. This will be + used (e.g. by astIsAObject) to determine if a pointer identifies a + valid Object. Note that this differs from the practice in derived + classes, where this number is stored in the virtual function + table. We take a different approach here so that we need not follow + a pointer to the virtual function table obtained from a structure + that hasn't yet been validated as an Object. This minimises the + risk of a memory access violation. */ + new->check = Magic( new, size, status ); + +/* Associate the Object with its virtual function table. */ + new->vtab = vtab; + +/* Store the Object size and note if its memory was dynamically allocated. */ + new->size = size; + new->dynamic = astIsDynamic( new ); + +/* Initialise the reference count (of Object pointers in use). */ + new->ref_count = 1; + +/* Initialise the ID strings. */ + new->id = NULL; + new->ident = NULL; + +/* Use default values for unspecified attributes. */ + new->usedefs = CHAR_MAX; + +/* Increment the count of active Objects in the virtual function table. + Use the count as a unique identifier (unique within the class) for + the Object. */ + new->iref = vtab->nobject++; + +/* Initialise the pointer to an external object that acts as a proxy for + the AST Object within foreign language interfaces. */ + new->proxy = NULL; + } + +/* If an error occurred, clean up by deleting the new Object. Otherwise + lock the object for use by the currently executing thread. */ + if ( !astOK ) { + new = astDelete( new ); + +#ifdef THREAD_SAFE + } else { + if( pthread_mutex_init( &(new->mutex1), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex1 for the new Object.", status, + vtab->class ); + } + if( pthread_mutex_init( &(new->mutex2), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex2 for the new Object.", status, + vtab->class ); + } + new->locker = -1; + new->globals = NULL; + (void) ManageLock( new, AST__LOCK, 0, NULL, status ); + if( !astOK ) new = astDelete( new ); +#endif + } + } + +/* Return a pointer to the new Object. */ + return new; +} + +AstObject *astLoadObject_( void *mem, size_t size, + AstObjectVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadObject + +* Purpose: +* Load an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astLoadObject( void *mem, size_t size, +* AstObjectVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Object loader. + +* Description: +* This function is provided to load a new Object using data read +* from a Channel, and to allocate memory for it if necessary. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for an Object at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Object is to be +* loaded. This must be of sufficient size to accommodate the +* Object data (sizeof(Object)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Object (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Object structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstObject) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Object. If this is NULL, a pointer to +* the (static) virtual function table for the Object class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new Object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Object" is used instead. + +* Returned Value: +* A pointer to the new Object. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *new; /* Pointer to the new Object */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Object. In this case the + Object belongs to this class, so supply appropriate values for + initialising it and its virtual function table. */ + if ( !vtab ) { + size = sizeof( AstObject ); + vtab = &class_vtab; + name = "Object"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitObjectVtab( vtab, name ); + class_init = 1; + } + } + +/* There is no parent class to load, so simply initialise a new Object + structure as if a new Object were being created from scratch. */ + new = astInitObject( mem, size, 0, vtab, name ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Object" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + new->id = astReadString( channel, "id", NULL ); + new->ident = astReadString( channel, "ident", NULL ); + new->usedefs = astReadInt( channel, "usedfs", CHAR_MAX ); + +/* We simply read the values for the read-only attributes (just in + case they've been un-commented in the external representation) and + discard them. This prevents any possibility of error due to un-read + input. */ + (void) astReadInt( channel, "refcnt", 0 ); + (void) astReadInt( channel, "nobj", 0 ); + +/* Initialise the pointer to an external object that acts as a proxy for + the AST Object within foreign language interfaces. */ + new->proxy = NULL; + +/* If an error occurred, clean up by deleting the new Object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Object pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astClear_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,Clear))( this, attrib, status ); +} +void astClearAttrib_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,ClearAttrib))( this, attrib, status ); +} +void astDump_( AstObject *this, AstChannel *channel, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,Dump))( this, channel, status ); +} + +#if defined(THREAD_SAFE) +int astManageLock_( AstObject *this, int mode, int extra, AstObject **fail, + int *status ) { + if( !this ) return 0; + return (**astMEMBER(this,Object,ManageLock))( this, mode, extra, fail, status ); +} +#endif + +int astEqual_( AstObject *this, AstObject *that, int *status ) { + if ( !astOK ) return 0; + if( this == that ) return 1; + return (**astMEMBER(this,Object,Equal))( this, that, status ); +} +const char *astGetAttrib_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Object,GetAttrib))( this, attrib, status ); +} +void astSetAttrib_( AstObject *this, const char *setting, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,SetAttrib))( this, setting, status ); +} +void astShow_( AstObject *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,Show))( this, status ); +} +int astTestAttrib_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Object,TestAttrib))( this, attrib, status ); +} +void astEnvSet_( AstObject *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,EnvSet))( this, status ); +} +void astVSet_( AstObject *this, const char *settings, char **text, va_list args, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,VSet))( this, settings, text, args, status ); +} +int astGetObjSize_( AstObject *this, int *status ) { + if ( !astOK || !this ) return 0; + return (**astMEMBER(this,Object,GetObjSize))( this, status ); +} +void astCleanAttribs_( AstObject *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,CleanAttribs))( this, status ); +} +AstObject *astCast_( AstObject *this, AstObject *obj, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Object,Cast))( this, obj, status ); +} +int astSame_( AstObject *this, AstObject *that, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Object,Same))( this, that, status ); +} +int astHasAttribute_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Object,HasAttribute))( this, attrib, status ); +} + +/* External interface. */ +/* =================== */ +/* The following relates to the external interface to Objects and not + specifically to the implementation of the Object class itself + (although it contains external functions which replace the internal + versions defined earlier). */ + + +/* Type Definitions. */ +/* ----------------- */ +/* Define the Handle structure. This is attached to Objects when they + are accessed via the public (external, user-callable) interface. + Handles provide a buffer between the Object identifiers issued to + external users and the naked C pointers used to handle Objects + internally. They also implement the context levels used by + astBegin, astEnd, astExempt and astExport (which are only available + to external users). */ +typedef struct Handle { + AstObject *ptr; /* C Pointer to the associated Object */ + int context; /* Context level for this Object */ + int check; /* Check value to ensure validity */ + +#if defined(THREAD_SAFE) + int thread; /* Identifier for owning thread */ +#endif + +#if defined(MEM_DEBUG) + int id; /* The id associated with the memory block + holding the Object last associated with + this handle. */ + AstObjectVtab *vtab; /* Pointer to the firtual function table of + the Object last associated with this + handle. */ +#endif + +/* Links between Handles are implemented using integer offsets rather + than through pointers. */ + int flink; /* Backward link to previous Handle */ + int blink; /* Forward link to next Handle */ +} Handle; + +/* Define a union with an overlapping int and AstObject*. This is used + to transfer an integer bit pattern into and out of a pointer + variable used to store an Object identifier. This avoids any + implementation dependent aspects of integer-to-pointer + conversions. */ +typedef union IdUnion { + AstObject *pointer; + int integer; +} IdUnion; + +/* Define a union which allows a bit pattern to be accessed as a + signed or unsigned int. */ +typedef union MixedInts { + int i; + unsigned u; +} MixedInts; + +/* Static Variables. */ +/* ----------------- */ +/* The array of Handle structures is a pool of resources available to all + threads. Each thread has its own conext level and its own "active_handles" + array to identify the first Handle at each context level. */ +static Handle *handles = NULL; /* Pointer to allocated array of Handles */ +static int free_handles = -1; /* Offset to head of free Handle list */ +static int nhandles = 0; /* Number of Handles in "handles" array */ +static unsigned nids = 0; /* Number of IDs issued to external users */ + +#if defined(THREAD_SAFE) +static int unowned_handles = -1; /* Offset to head of unowned Handle + list. In a single threaded environment, + all handles must be owned by a thread. */ +#endif + +#ifdef MEM_DEBUG +static int Watched_Handle = -1; /* A handle index to be watched. Activity + on this handle is reported using + astHandleUse and astHandleAlarm. */ +#endif + + +/* External Interface Function Prototypes. */ +/* --------------------------------------- */ +/* MYSTATIC should normally be set to "static" to make the following + function have local symbols. But it may be set to blank for debugging + purposes in order to enable these functions to appear in a backtrace + such as produced by the astBacktrace function. */ +#define MYSTATIC + +/* Private functions associated with the external interface. */ +MYSTATIC AstObject *AssocId( int, int * ); +MYSTATIC int CheckId( AstObject *, int, int * ); +MYSTATIC void AnnulHandle( int, int * ); +MYSTATIC void InitContext( int * ); +MYSTATIC void InsertHandle( int, int *, int * ); +MYSTATIC void RemoveHandle( int, int *, int * ); + +#if defined(MEM_DEBUG) +MYSTATIC void CheckList( int *, int * ); +MYSTATIC void CheckInList( int, int *, int, int * ); +MYSTATIC int CheckThread( int, int *, int * ); +MYSTATIC const char *HandleString( int, char * ); +MYSTATIC const char *HeadString( int *, char * ); +#endif + + +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstObject *astDeleteId_( AstObject *, int * ); +void astBegin_( void ); +void astEnd_( int * ); +void astExemptId_( AstObject *, int * ); +void astImportId_( AstObject *, int * ); +void astSetId_( void *, const char *, ... ); +void astLockId_( AstObject *, int, int * ); +void astUnlockId_( AstObject *, int, int * ); + + +/* External Interface Functions. */ +/* ----------------------------- */ +MYSTATIC void AnnulHandle( int ihandle, int *status ) { +/* +* Name: +* AnnulHandle + +* Purpose: +* Annul a Handle and its associated Object pointer. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void AnnulHandle( int ihandle, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function annuls an active Handle by annulling the Object +* pointer it is associated with, deactivating the Handle and +* returning it to the free Handle list for re-use. +* +* The reference count for the associated Object is decremented by +* this function and the Object will be deleted if this reference +* count falls to zero. + +* Parameters: +* ihandle +* Array offset that identifies the Handle to be annulled in the +* "handles" array. This is fully validated by this function. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The Handle supplied should be active, otherwise an error will +* result and the Handle will not be touched (but no error report +* will be made if the global error status has already been set). +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *ptr; /* Object pointer */ + int context; /* Context level where Handle was issued */ + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Check that the handle offset supplied is valid and report an error + if it is not (but only if the global error status has not already + been set). */ + if ( ( ihandle < 0 ) || ( ihandle >= nhandles ) ) { + if ( astOK ) { + astError( AST__INHAN, "astAnnulHandle: Invalid attempt to annul an " + "Object Handle (no. %u).", status, ihandle ); + astError( AST__INHAN, "This Handle number is not valid (possible " + "internal programming error)." , status); + } + +/* If OK, obtain the Handle's context level. */ + } else { + context = handles[ ihandle ].context; + +/* If this indicates that the Handle isn't active, then report an + error (but only if the global error status has not already been + set). We allow handles that are currently not owned by any thread to + be annulled. */ + if ( context < 0 && context != UNOWNED_CONTEXT ) { + if ( astOK ) { + astError( AST__INHAN, "astAnnulHandle: Invalid attempt to annul " + "an Object Handle (no. %u).", status, ihandle ); + astError( AST__INHAN, "This Handle is not active (possible " + "internal programming error)." , status); + } + +/* If the Handle is active, annul its Object pointer. The astAnnul + function may call Delete functions supplied by any class, and these + Delete functions may involve annulling external Object IDs, which in + turn requires access to the handles array. For this reason, we release + the mutex that protects access to the handles arrays so that it can + potentially be re-aquired within astAnnul without causing deadlock. */ + } else { + +#ifdef MEM_DEBUG + astHandleUse( ihandle, "annulled using check value %d ", + handles[ ihandle ].check ); +#endif + + ptr = handles[ ihandle ].ptr; + UNLOCK_MUTEX2; + ptr = astAnnul( ptr ); + LOCK_MUTEX2; + +/* Remove the Handle from the active list for its context level. */ + if( context == UNOWNED_CONTEXT ) { + +#if defined(THREAD_SAFE) + RemoveHandle( ihandle, &unowned_handles, status ); +#else + if( astOK ) astError( AST__INTER, "AnnulHandle: reference to " + "'unowned_handles' in a non-thread-safe context " + "(internal AST programming error).", status ); +#endif + + } else if( active_handles ) { + RemoveHandle( ihandle, &active_handles[ context ], status ); + + } else if( astOK ){ + astError( AST__INTER, "AnnulHandle: active_handles array has " + "not been initialised (internal AST programming error).", + status ); + } + +/* Reset the Handle's "context" value (making it inactive) and its "check" + value (so it is no longer associated with an identifier value). */ + handles[ ihandle ].ptr = NULL; + handles[ ihandle ].context = INVALID_CONTEXT; + handles[ ihandle ].check = 0; +#if defined(THREAD_SAFE) + handles[ ihandle ].thread = -1; +#endif + +/* Place the modified Handle on the free Handles list ready for re-use. */ + InsertHandle( ihandle, &free_handles, status ); + + } + } +} + +AstObject *astAnnulId_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* AnnulId + +* Purpose: +* Annul an external Object identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astAnnulId( AstObject *this ) + +* Class Membership: +* Object member function. + +* Description: +* This function implements the external (public) interface to the +* astAnnul method. It accepts an active Object identifier, which +* it annuls, causing the associated Handle to be deactivated and +* returned to the free Handles list for re-use. It also causes the +* reference count of the associated Object to be decremented and +* the Object to be deleted if this reference count falls to zero. + +* Parameters: +* this +* The Object identifier to be annulled. + +* Returned Value: +* A NULL C pointer is always returned (this should be translated +* into an identifier value of zero for external use). + +* Notes: +* - This function attempts to execute even if the global error +* status is set. +* - The identifier supplied should be associated with an active +* Object, otherwise an error will result (but no error report will +* be made if the global error status has already been set). +* - This function is invoked via the astAnnul macro for external use. +* For internal use (from protected code which needs to handle external +* IDs) it should be invoked via the astAnnulId macro (since astAnnul +* expects a true C pointer as its argument when used internally). +*- +*/ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). Note, we use "astMakePointer_NoLockCheck", + rather than the usual "astMakePointer" since a thread should be able + to renounce interest in an object without needing to own the object. + If we used "astMakePointer" then a thread could not annul a pointer + unless it owned the object. But having annulled the pointer it could + then not unlock the object for use by another thread (since the + pointer it would need to do this has just been annulled). */ + if ( !astIsAObject( astMakePointer_NoLockCheck( this_id ) ) ) return NULL; + +/* Obtain the Handle offset for this Object and annul the Handle and + its associated Object pointer. Report an error if the handle is + currently owned by a different thread. That is, the *Object* need + not be locked by the current thread (as indicated by the use of + astMakePointer above), but the *handle* must be owned by the current + thread. */ + LOCK_MUTEX2; + AnnulHandle( CheckId( this_id, 1, status ), status ); + UNLOCK_MUTEX2; + +/* Always return a NULL pointer value. */ + return NULL; +} + +MYSTATIC AstObject *AssocId( int ihandle, int *status ) { +/* +* Name: +* AssocId + +* Purpose: +* Associate an identifier value with a Handle. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *AssocId( int ihandle ) + +* Class Membership: +* Object member function. + +* Description: +* This function takes a zero-based offset into the "handles" array +* that identifies a Handle associated with an active Object. It +* encodes this into an identifier value to be issued to an +* external user to identify that Handle and its Object, and then +* associates this identifier value with the Handle. + +* Parameters: +* ihandle +* Offset in the "handles" array that identifies the Handle, +* which should be active (i.e. associated with an active +* Object). This function will modify the "check" field in this +* Handle to associate it with the identifier value it returns. + +* Returned Value: +* The resulting identifier value. + +* Notes: +* - The returned identifier value is, in fact, an int. This +* allows the value to be passed easily to other languages +* (e.g. Fortran) and stored as an integer without loss of +* information. +* - The value is stored within C code as type AstObject*. This +* means that C code (e.g. function prototypes, etc.) need not be +* aware that an identifier (as opposed to an Object pointer) is +* being used, even though the actual bit patterns will be +* different. The same C code can then be used for both internal +* and external purposes so long as care is taken to convert +* between the two representations at appropriate points. +* - The reverse operation (conversion of an ID back to a handle +* offset) is performed by CheckId. +* - A zero identifier value will be returned if this function is +* invoked with the global status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstObject *result; /* Pointer value to return */ + MixedInts test; /* Union for testing encoding */ + MixedInts work; /* Union for encoding ID value */ + +/* Initialise. */ + result = astI2P( 0 ); + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Copy the Handle offset and clear the lowest 8 bits by shifting + left. */ + work.i = ihandle; + work.u = work.u << 8U; + +/* Make a copy of the result shifted right again. Test if any bits + have been lost in this process. If so, there are too many Handles + in use at once to encode them into IDs, so report an error. */ + test.u = work.u >> 8U; + if ( test.i != ihandle ) { + astError( AST__XSOBJ, "AssocId(%s): There are too many AST Objects in " + "use at once.", status, astGetClass( handles[ ihandle ].ptr ) ); + +/* If OK, scramble the value by exclusive-ORing with the bit pattern + in AST__FAC (a value unique to this library), also shifted left by + 8 bits. This makes it even less likely that numbers from other + sources will be accepted in error as valid IDs. */ + } else { + work.u ^= ( ( (unsigned) AST__FAC ) << 8U ); + +/* Fill the lowest 8 bits with a count of the total number of IDs + issued so far (which we increment here). This makes each ID unique, + so that an old one that identifies a Handle that has been annulled + and re-used (i.e. associated with a new ID) can be spotted. We + only use the lowest 8 bits of this count because this provides + adequate error detection to reveal programming errors and we do not + need higher security than this. We also prevent a count of zero + being used, as this could result in a zero identifier value (this + being reserved as the "null" value). */ + if ( ++nids > 255U ) nids = 1U; + work.u |= nids; + +/* Store the value as a check count in the Handle. This will be used + to validate the ID in future. */ + handles[ ihandle ].check = work.i; + +/* Pack the value into the pointer to be returned. */ + result = astI2P( work.i ); + } + +/* Return the result. */ + return result; +} + +void astBegin_( void ) { +/* +*++ +* Name: +c astBegin +f AST_BEGIN + +* Purpose: +* Begin a new AST context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astBegin +f CALL AST_BEGIN( STATUS ) + +* Class Membership: +* Object class function. + +* Description: +c This macro invokes a function to begin a new AST context. +c Any Object pointers +f This routine begins a new AST context. Any Object pointers +* created within this context will be annulled when it is later +c ended using astEnd (just as if astAnnul had been invoked), +f ended using AST_END (just as if AST_ANNUL had been invoked), +c unless they have first been exported using astExport or rendered +c exempt using astExempt. If +f unless they have first been exported using AST_EXPORT or rendered +f exempt using AST_EXEMPT. If +* annulling a pointer causes an Object's RefCount attribute to +* fall to zero (which happens when the last pointer to it is +* annulled), then the Object will be deleted. + +f Parameters: +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - astBegin attempts to execute even if the AST error status +c is set on entry. +f - This routine attempts to execute even if STATUS is set to an +f error value. +c - Contexts delimited by astBegin and astEnd may be nested to any +c depth. +f - Contexts delimited by AST_BEGIN and AST_END may be nested to any +f depth. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int stat; /* Copy of global status */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Now that the thread-specific data has been initialised, we can get a + pointer to the threads inherited status value. */ + status = astGetStatusPtr; + +/* Save and clear the global status so that memory allocation can be + performed within this function even under error conditions. */ + stat = astStatus; + astClearStatus; + +/* Ensure that the active_handles array has been initialised. */ + if ( !active_handles ) InitContext( status ); + +/* Extend the "active handles" array to accommodate a new context + level. This array contains integer offsets into the "handles" array + to identify the handle which is at the head of the list of active + handles for each context level. */ + astBeginPM; + active_handles = astGrow( active_handles, context_level + 2, + sizeof( int ) ); + astEndPM; + +/* Initialise the array element for the new context level to indicate + an empty Handle list. */ + if ( astOK ) active_handles[ ++context_level ] = -1; + +/* Restore the original global status value. */ + astSetStatus( stat ); +} + +MYSTATIC int CheckId( AstObject *this_id, int lock_check, int *status ) { +/* +* Name: +* CheckId + +* Purpose: +* Check an identifier value and convert it into a Handle offset. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* int CheckId( AstObject *this, int lock_check, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function takes an identifier value encoded by AssocId and +* validates it to ensure it is associated with an active +* Handle. If valid, it converts it back into a zero-based offset +* which may be used to access the Handle in the "handles" +* array. Otherwise, an error is reported. + +* Parameters: +* this +* The identifier value to be decoded. +* lock_check +* Should an error be reported if the handle is in an Object +* context for a different thread? +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The resulting Handle offset, or -1 if the identifier is not +* valid or any other error occurs. + +* Notes: +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* fails under these circumstances. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + MixedInts work; /* Union for decoding ID value */ + int id; /* ID value as an int */ + int ihandle; /* Result to return */ + +#ifdef MEM_DEBUG + int oldok = astOK; +#endif + +/* Initialise. */ + ihandle = -1; + +/* Get a pointer to thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Retrieve the integer Object identifier value from the pointer + supplied. */ + id = astP2I( this_id ); + +/* Check if a value of zero has been supplied and report an error if + it has. */ + if ( !id ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "zero)." , status); + } + +/* If OK, reverse the encoding process performed by AssocId to + retrieve the Handle offset. */ + } else { + work.i = id; + work.u = ( work.u ^ ( ( (unsigned) AST__FAC ) << 8U ) ) >> 8U; + +/* Check that the offset obtained doesn't extend beyond the limits of + the "handles" array. Report an error if it does. */ + if ( ( work.i < 0 ) || ( work.i >= nhandles ) ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "%d).", status, id ); + } + +/* See if the "check" field matches the ID value and the Handle is + valid (i.e. is associated with an active Object). If not, the + Handle has been annulled and possibly re-used, so report an + error. */ + } else if ( ( handles[ work.i ].check != id ) || + ( handles[ work.i ].context == INVALID_CONTEXT ) ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "%d).", status, id ); + astError( AST__OBJIN, "This pointer has been annulled, or the " + "associated Object deleted." , status); + } +#if defined(THREAD_SAFE) + } else if( lock_check && handles[ work.i ].context != UNOWNED_CONTEXT && + handles[ work.i ].thread != AST__THREAD_ID ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "%d).", status, id ); + astError( AST__OBJIN, "This pointer is currently owned by " + "another thread (possible programming error)." , status); + } +#endif + +/* If OK, set the Handle offset to be returned. */ + } else { + ihandle = work.i; + } + +#ifdef MEM_DEBUG + if ( oldok && !astOK && ( work.i >= 0 ) && ( work.i < nhandles ) ) { + char buf[200]; + astError( astStatus, "Handle properties: %s ", status, + HandleString( work.i, buf ) ); + } +#endif + + } + +/* Return the result. */ + return ihandle; +} + +AstObject *astDeleteId_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* astDeleteId + +* Purpose: +* Delete an Object via an identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astDeleteId_( AstObject *this ) + +* Class Membership: +* Object member function. + +* Description: +* This function implements the external (public) interface to the +* astDelete method. It accepts an active Object identifier and +* deletes the associated Object. Before doing so, it annuls all +* active identifiers associated with the object, deactivates their +* Handles and returns these Handles to the free Handles list for +* re-use. + +* Parameters: +* this +* An identifier for the Object to be deleted. + +* Returned Value: +* A NULL C pointer is always returned (this should be translated +* into an identifier value of zero for external use). + +* Notes: +* - This function attempts to execute even if the global error +* status is set. +* - The identifier supplied should be associated with an active +* Object, otherwise an error will result (but no error report will +* be made if the global error status has already been set). +* - Although this function is documented as "private" and should +* not be invoked directly from outside this class, it is not a +* static function and has a public prototype. This is because it +* must be invoked via the astDelete macro (defined in the +* "object.h" include file) as part of the public interface. +*- +*/ + +/* Local Variables: */ + AstObject *this; /* Pointer to Object */ + int i; /* Loop counter for Handles */ + int ihandle; /* Object Handle offset */ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). */ + if ( !astIsAObject( this = astMakePointer( this_id ) ) ) return NULL; + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Since the Object is to be deleted, we must annul all identifiers + that refer to it. Loop to inspect each currently allocated Handle + in the "handles" array. */ + for ( i = 0; i < nhandles; i++ ) { + +/* Select active handles and test if their Object pointer refers to + the Object to be deleted. */ + if ( ( handles[ i ].context != INVALID_CONTEXT ) && + ( handles[ i ].ptr == this ) ) { + +/* If so, explicitly set the reference count for the Object to 2 so + that it will not be deleted (yet) when we annul the pointer + associated with the Handle. */ + handles[ i ].ptr->ref_count = 2; + +/* Annul the Handle, which frees its resources, decrements the Object + reference count and makes any ID associated with the Handle become + invalid. */ + AnnulHandle( i, status ); + } + } + +/* If required, tell the user that the handle's object has been deleted. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "object-deleted" ); +#endif + } + + UNLOCK_MUTEX2; + +/* When all Handles associated with the Object have been annulled, + delete the object itself. This over-rides the reference count and + causes any remaining pointers to the Object (e.g. in internal code + or within other Objects) to become invalid. */ + this = astDelete( this ); + +/* Always return a NULL pointer value. */ + return NULL; +} + +void astEnd_( int *status ) { +/* +*++ +* Name: +c astEnd +f AST_END + +* Purpose: +* End an AST context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astEnd +f CALL AST_END( STATUS ) + +* Class Membership: +* Object class function. + +* Description: +c This macro invokes a function to end an AST context which was +f This routine ends an AST context which was +c begun with a matching invocation of astBegin. Any Object +f begun with a matching invocation of AST_BEGIN. Any Object +* pointers created within this context will be annulled (just as +c if astAnnul had been invoked) and will cease to be valid +f if AST_ANNUL had been invoked) and will cease to be valid +* afterwards, unless they have previously been exported using +c astExport or rendered exempt using astExempt. +f AST_EXPORT or rendered exempt using AST_EXEMPT. +* If annulling a pointer causes an Object's RefCount attribute to +* fall to zero (which happens when the last pointer to it is +* annulled), then the Object will be deleted. + +* Parameters: +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - astEnd attempts to execute even if the AST error status is set. +f - This routine attempts to execute even if STATUS is set to an +f error value. +c - Contexts delimited by astBegin and astEnd may be nested to any +c depth. +f - Contexts delimited by AST_BEGIN and AST_END may be nested to any +f depth. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int ihandle; /* Offset of Handle to be annulled */ + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Check that the current context level is at least 1, otherwise there + has been no matching use of astBegin, so report an error (unless + the global status has already been set). */ + if ( context_level < 1 ) { + if ( astOK ) { + astError( AST__ENDIN, "astEnd: Invalid use of astEnd without a " + "matching astBegin." , status); + } + +/* If OK, loop while there are still active Handles associated with + the current context level. First gain exclusive access to the handles + array. */ + } else if ( active_handles ) { + LOCK_MUTEX2; + while ( ( ihandle = active_handles[ context_level ] ) != -1 ) { + +/* Annul the Handle at the head of the active Handles list. */ + AnnulHandle( ihandle, status ); + +/* It is just posible that under error conditions inactive Handles + might get left in the active_handles list and AnnulHandle would + then fail. Since this would result in an infinite loop, we check to + see if the handle we have just annulled is still in the list. If + so, transfer it to the free Handles list for re-use. */ + if ( ihandle == active_handles[ context_level ] ) { + RemoveHandle( ihandle, &active_handles[ context_level ], status ); + InsertHandle( ihandle, &free_handles, status ); + } + } + +/* Ensure the context level is decremented unless it was zero to start + with. */ + context_level--; + +/* Relinquish access to the handles array. */ + UNLOCK_MUTEX2; + } + +} + +void astExemptId_( AstObject *this_id, int *status ) { +/* +*++ +* Name: +c astExempt +f AST_EXEMPT + +* Purpose: +* Exempt an Object pointer from AST context handling. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astExempt( AstObject *this ) +f CALL AST_EXEMPT( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function exempts an Object pointer from AST context +f This routine exempts an Object pointer from AST context +c handling, as implemented by astBegin and astEnd. This means that +f handling, as implemented by AST_BEGIN and AST_END. This means that +c the pointer will not be affected when astEnd is invoked and will +f the pointer will not be affected when AST_END is called and will +* remain active until the end of the program, or until explicitly +c annulled using astAnnul. +f annulled using AST_ANNUL. +* +c If possible, you should avoid using this function when writing +f If possible, you should avoid using this routine when writing +* applications. It is provided mainly for developers of other +* libraries, who may wish to retain references to AST Objects in +* internal data structures, and who therefore need to avoid the +c effects of astBegin and astEnd. +f effects of AST_BEGIN and AST_END. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Object pointer to be exempted from context handling. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. +*-- + +* Implementation Notes: +* - This function does not exist in the "protected" interface to +* the Object class and is not available to other class +* implementations. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int context; /* Handle context level */ + int ihandle; /* Offset of Handle in "handles" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + (void) astCheckObject( astMakePointer( this_id ) ); + if ( astOK ) { + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Extract the context level at which the Object was created. */ + context = handles[ ihandle ].context; + +/* Set the new context level to zero, where it cannot be affected by + ending any context. */ + handles[ ihandle ].context = 0; + +/* Remove the object's Handle from its original active Handles list + and insert it into the list appropriate to its new context + level. */ + +#if defined(THREAD_SAFE) + if( context == UNOWNED_CONTEXT ) { + RemoveHandle( ihandle, &unowned_handles, status ); + } else { + RemoveHandle( ihandle, &active_handles[ context ], status ); + } +#else + RemoveHandle( ihandle, &active_handles[ context ], status ); +#endif + + InsertHandle( ihandle, &active_handles[ 0 ], status ); + +/* If required, tell the user that the handle has been exempted. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "exempted" ); +#endif + } + + UNLOCK_MUTEX2; + } +} + +void astExportId_( AstObject *this_id, int *status ) { +/* +*++ +* Name: +c astExport +f AST_EXPORT + +* Purpose: +* Export an Object pointer to an outer context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astExport( AstObject *this ) +f CALL AST_EXPORT( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function exports an Object pointer from the current AST context +f This routine exports an Object pointer from the current AST context +* into the context that encloses the current one. This means that +* the pointer will no longer be annulled when the current context +c is ended (with astEnd), but only when the next outer context (if +f is ended (with AST_END), but only when the next outer context (if +* any) ends. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Object pointer to be exported. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - It is only sensible to apply this function to pointers that +f - It is only sensible to apply this routine to pointers that +* have been created within (or exported to) the current context +c and have not been rendered exempt using astExempt. +f and have not been rendered exempt using AST_EXEMPT. +* Applying it to an unsuitable Object pointer has no effect. +*-- + +* Implementation Notes: +* - This function does not exist in the "protected" interface to +* the Object class and is not available to other class +* implementations. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int context; /* Handle context level */ + int ihandle; /* Offset of Handle in "handles" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + (void) astCheckObject( astMakePointer( this_id ) ); + if ( astOK ) { + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Check that the current context level is at least 1 and report an + error if it is not. */ + if ( context_level < 1 ) { + if( astOK ) astError( AST__EXPIN, "astExport(%s): Attempt to export an Object " + "from context level zero.", status, + astGetClass( handles[ ihandle ].ptr ) ); + +/* Extract the context level at which the Object was created. */ + } else { + context = handles[ ihandle ].context; + +/* Check that the Object's existing context level is high enough to be + affected by being exported to the next outer context level. If not, + do nothing. */ + if ( context > ( context_level - 1 ) ) { + +/* Set the new context level. */ + handles[ ihandle ].context = context_level - 1; + +/* Remove the object's Handle from its original active Handles list + and insert it into the list appropriate to its new context + level. */ + RemoveHandle( ihandle, &active_handles[ context ], status ); + InsertHandle( ihandle, &active_handles[ context_level - 1 ], + status ); + +/* If required, tell the user that the handle has been exempted. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "exported from context level %d", + context_level ); +#endif + } + } + } + UNLOCK_MUTEX2; + } +} + +void astImportId_( AstObject *this_id, int *status ) { +/* +*++ +* Name: +c astImport +f AST_IMPORT + +* Purpose: +* Import an Object pointer to the current context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astImport( AstObject *this ) +f CALL AST_IMPORT( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function +f This routine +* imports an Object pointer that was created in a higher or lower +* level context, into the current AST context. +* This means that the pointer will be annulled when the current context +c is ended (with astEnd). +f is ended (with AST_END). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Object pointer to be imported. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +*-- + +* Implementation Notes: +* - This function does not exist in the "protected" interface to +* the Object class and is not available to other class +* implementations. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int context; /* Handle context level */ + int ihandle; /* Offset of Handle in "handles" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + (void) astCheckObject( astMakePointer( this_id ) ); + if ( astOK ) { + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Extract the context level at which the Object was created. */ + context = handles[ ihandle ].context; + +/* Do nothing if the Identifier already belongs to the current context. */ + if( context != context_level ) { + +/* Set the new context level. */ + handles[ ihandle ].context = context_level; + +/* Remove the object's Handle from its original active Handles list + and insert it into the list appropriate to its new context + level. */ + RemoveHandle( ihandle, &active_handles[ context ], status ); + InsertHandle( ihandle, &active_handles[ context_level ], status ); + +/* If required, tell the user that the handle has been imported. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "imported into context level %d", + context_level ); +#endif + } + } + UNLOCK_MUTEX2; + } +} + +void astLockId_( AstObject *this_id, int wait, int *status ) { +/* +c++ +* Name: +* astLock + +* Purpose: +* Lock an Object for exclusive use by the calling thread. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* void astLock( AstObject *this, int wait ) + +* Class Membership: +* Object method. + +* Description: +* The thread-safe public interface to AST is designed so that an +* error is reported if any thread attempts to use an Object that it +* has not previously locked for its own exclusive use using this +* function. When an Object is created, it is initially locked by the +* thread that creates it, so newly created objects do not need to be +* explicitly locked. However, if an Object pointer is passed to +* another thread, the original thread must first unlock it (using +* astUnlock) and the new thread must then lock it (using astLock) +* before the new thread can use the Object. +* +* The "wait" parameter determines what happens if the supplied Object +* is curently locked by another thread when this function is invoked. + +* Parameters: +* this +* Pointer to the Object to be locked. +* wait +* If the Object is curently locked by another thread then this +* function will either report an error or block. If a non-zero value +* is supplied for "wait", the calling thread waits until the object +* is available for it to use. Otherwise, an error is reported and +* the function returns immediately without locking the Object. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - The astAnnul function is exceptional in that it can be used on +* pointers for Objects that are not currently locked by the calling +* thread. All other AST functions will report an error. +* - The Locked object will belong to the current AST context. +* - This function returns without action if the Object is already +* locked by the calling thread. +* - If simultaneous use of the same object is required by two or more +* threads, astCopy should be used to to produce a deep copy of +* the Object for each thread. Each copy should then be unlocked by +* the parent thread (i.e. the thread that created the copy), and then +* locked by the child thread (i.e. the thread that wants to use the +* copy). +* - This function is only available in the C interface. +* - This function returns without action if the AST library has +* been built without POSIX thread support (i.e. the "-with-pthreads" +* option was not specified when running the "configure" script). +c-- +*/ + +/* This function odes nothing if thread support is not enabvled. */ +#if defined(THREAD_SAFE) + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *fail; /* Pointer to Object that failed to lock */ + AstObject *this; /* Pointer to Object */ + int ihandle; /* Index of supplied objetc handle */ + int lstat; /* Local status value */ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). Note, we use the "astMakePointer_NoLockCheck", + since the usual "astMakePointer" macro invokes astCheckLock to report + an error if the Object is not currently locked by the calling thread. */ + if ( !astIsAObject( this = astMakePointer_NoLockCheck( this_id ) ) ) return; + +/* Ensure the global data for this class is accessable. Do not use the + globals pointer stored in "*this" because "*this" may be locked by + another thread and so we would pick up the wrong globals. */ + astGET_GLOBALS(NULL); + +/* Ensure the running thread has sole access to the static handles arrays. */ + LOCK_MUTEX2; + +/* Ensure the Handles arrays have been initialised. */ + if ( !active_handles ) InitContext( status ); + +/* Get the Handle index for the supplied object identifier. No error is + reported if the handle is not curently associated with a thread. + However, an error is reported if the Handle is associated with any + thread other than the running thread. */ + ihandle = CheckId( this_id, 0, status ); + +/* We've finished with the handles arrays, for the moment. */ + UNLOCK_MUTEX2; + +/* Check the object pointer was valid. */ + if( ihandle != -1 ){ + +/* The protected astManageLock function implements the public functions, + astLock and astUnlock. */ + lstat = astManageLock( this, AST__LOCK, wait, &fail ); + if( astOK ) { + if( lstat == 1 ) { + if( fail == this ) { + astError( AST__LCKERR, "astLock(%s): Failed to lock the %s because" + " it is already locked by another thread (programming " + "error).", status, astGetClass( this ), + astGetClass( this ) ); + + } else { + astError( AST__LCKERR, "astLock(%s): Failed to lock the %s because" + " a %s contained within it is already locked by another " + "thread (programming error).", status, + astGetClass( this ), astGetClass( this ), + astGetClass( fail ) ); + } + + } else if( lstat == 2 ) { + astError( AST__LCKERR, "astLock(%s): Failed to lock a POSIX mutex.", status, + astGetClass( this ) ); + +/* If the Object is now locked for the running thread... */ + } else { + +/* We need access to the handles arrays again. */ + LOCK_MUTEX2; + +/* If the supplied handle is not currently assigned to any thread, assign + it to the running thread. */ + if( handles[ ihandle ].context == UNOWNED_CONTEXT ) { + RemoveHandle( ihandle, &unowned_handles, status ); + +#if defined(MEM_DEBUG) + astHandleUse( ihandle, "locked by thread %d at context level %d", + handles[ ihandle ].thread, context_level ); +#endif + + handles[ ihandle ].thread = AST__THREAD_ID; + handles[ ihandle ].context = context_level; + InsertHandle( ihandle, &active_handles[ context_level ], + status ); + } + +/* Finished with the handles arrays again. */ + UNLOCK_MUTEX2; + } + } + } +#endif +} + +void astUnlockId_( AstObject *this_id, int report, int *status ) { +/* +c++ +* Name: +* astUnlock + +* Purpose: +* Unlock an Object for use by other threads. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* void astUnlock( AstObject *this, int report ) + +* Class Membership: +* Object method. + +* Description: +* Unlocks an Object previously locked using astLock, so that other +* threads can use the Object. See astLock for further details. + +* Parameters: +* this +* Pointer to the Object to be unlocked. +* report +* If non-zero, an error will be reported if the supplied Object, +* or any Object contained within the supplied Object, is not +* currently locked by the running thread. If zero, such Objects +* will be left unchanged, and no error will be reported. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - All unlocked Objects are excluded from AST context handling until +* they are re-locked using astLock. +* - This function is only available in the C interface. +* - This function returns without action if the Object is not currently +* locked by any thread. If it is locked by the running thread, it is +* unlocked. If it is locked by another thread, an error will be reported +* if "error" is non-zero. +* - This function returns without action if the AST library has +* been built without POSIX thread support (i.e. the "-with-pthreads" +* option was not specified when running the "configure" script). +c-- +*/ + +/* This function odes nothing if thread support is not enabvled. */ +#if defined(THREAD_SAFE) + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstErrorContext error_context;/* Info about the current error context */ + AstObject *fail; /* Pointer to Object that failed */ + AstObject *this; /* Pointer to Object */ + int ihandle; /* Index of supplied objetc handle */ + int lstat; /* Local status value */ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). Note, we use the "astMakePointer_NoLockCheck", + since the usual "astMakePointer" macro invokes astCheckLock to report + an error if the Object is not currently locked by the calling thread. */ + if ( !astIsAObject( this = astMakePointer_NoLockCheck( this_id ) ) ) return; + +/* Ensure the global data for this class is accessable. */ + astGET_GLOBALS(this); + +/* Start a new error reporting context. This saves any existing error status + and then clear the status value. It also defer further error reporting. */ + astErrorBegin( &error_context ); + +/* Ensure the running thread has sole access to the static handles arrays. */ + LOCK_MUTEX2; + +/* Ensure the Handles arrays have been initialised. */ + if ( !active_handles ) InitContext( status ); + +/* Get the Handle index for the supplied object identifier. Report an error + if the handle is not curently associated with the running thread. */ + ihandle = CheckId( this_id, 1, status ); + +/* Break the associated of the handle with the current thread so that the + handle is not assigned to any thread. We do this before unlocking the + Object structure (using astManageLock) since as soon as astManageLock + returns, another thread that is waiting for the object to be unlocked + may start up and modify the handle properties. */ + if( ihandle >= 0 && handles[ ihandle ].context >= 0 ) { + RemoveHandle( ihandle, &active_handles[ handles[ ihandle ].context ], + status ); +#if defined(MEM_DEBUG) + astHandleUse( ihandle, "unlocked from thread %d at context " + "level %d", handles[ ihandle ].thread, + handles[ ihandle ].context ); +#endif + handles[ ihandle ].thread = -1; + handles[ ihandle ].context = UNOWNED_CONTEXT; + InsertHandle( ihandle, &unowned_handles, status ); + } + +/* We've finished with the handles arrays, for the moment. */ + UNLOCK_MUTEX2; + +/* Check the supplied object pointer was valid. */ + if( ihandle != -1 ){ + +/* The protected astManageLock function implements the public functions, + astLock and astUnlock. */ + lstat = astManageLock( this, AST__UNLOCK, 0, &fail ); + if( astOK ) { + if( lstat == 1 ) { + if( report ) { + if( fail == this ) { + astError( AST__LCKERR, "astUnlock(%s): Failed to unlock the %s " + "because it is locked by another thread (programming " + "error).", status, astGetClass( this ), + astGetClass( this ) ); + + } else { + astError( AST__LCKERR, "astUnlock(%s): Failed to unlock the %s " + "because a %s contained within it is locked by another " + "thread (programming error).", status, + astGetClass( this ), astGetClass( this ), + astGetClass( fail ) ); + } + } + + } else if( lstat == 3 ) { + astError( AST__LCKERR, "astUnlock(%s): Failed to unlock a POSIX mutex.", status, + astGetClass( this ) ); + + } + } + } + +/* End the error reporting context. If an error has occurred within this + function, then this will display the deferred error messages so long + as there was no error condition on entry to this function. If there + was an error condition on entry, then the original status value will be + re-instated. */ + astErrorEnd( &error_context ); + +#endif +} + +AstObject *astI2P_( int integer, int *status ) { +/* +*+ +* Name: +* astI2P + +* Purpose: +* Pack an integer Object ID into a pointer. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* AstObject *astI2P( int integer ) + +* Class Membership: +* Object class function. + +* Description: +* This function accepts an integer (int) value representing an +* Object identifier and packs its bit pattern into a pointer value +* (from which it may subsequently be retrieved using astP2I). +* +* These functions should be used to avoid any dependency on +* integer-to-pointer conversions (given that the values are not +* true pointers) which might affect the exchange of Object +* identifier values with other languages, such as Fortran 77, +* where they are stored as integers. + +* Parameters: +* integer +* The integer value to be stored. + +* Returned Value: +* The resulting pointer value (which is not usually a valid C pointer). + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + IdUnion temp; /* Overlapping int and pointer */ + +/* Clear the pointer value in the "temp" IdUnion and then set the + integer part of it to the value to be stored. */ + temp.pointer = zero_ptr; + temp.integer = integer; + +/* Return the resulting pointer value. */ + return temp.pointer; +} + +MYSTATIC void InitContext( int *status ) { +/* +* Name: +* InitContext + +* Purpose: +* Initialise the first AST context level. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void InitContext( int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function initialises the first AST context level (the level +* before any call to astBegin has been made). It should be invoked +* once, before any use is made of context levels. + +* Parameters: +* status +* Pointer to the inherited status variable. + +* Parameters: +* None. + +* Notes: +* - This function does nothing after the first successful invocation. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Check that the active_handles array hasn't already been allocated. */ + if ( !active_handles ) { + +/* Allocate and initialise the "active_handles" array. */ + + astBeginPM; + active_handles = astMalloc( sizeof( int ) ); + astEndPM; + + if ( astOK ) active_handles[ 0 ] = -1; + } +} + +MYSTATIC void InsertHandle( int ihandle, int *head, int *status ) { +/* +* Name: +* InsertHandle + +* Purpose: +* Insert a Handle into a list. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void InsertHandle( int ihandle, int *head, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function inserts a Handle structure into a doubly linked +* list of such structures composed of elements drawn from the +* "handles" array. The new list member is inserted in front of the +* member previously occupying the "head of list" position. + +* Parameters: +* ihandle +* Offset in the "handles" array that identifies the handle to +* be added to the list. +* head +* Address of an int which holds the offset in the "handles" +* array of the element at the head of the list (or -1 if the +* list is empty). This value will be updated to identify the +* new list member. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error chacking and does not +* generate errors. +* - The lists generated by this function use integer offsets into +* the "handles" array for their links, rather than pointers. This +* is because the array may be re-located in memory when it needs +* to be extended, so pointers to its element would not remain +* valid. +* - The list elements are drawn from the "handles" array in the +* first place so that they can be addressed by small integers (the +* offset in the array). This allows references to Handles to be +* encoded along with security information into an integer that is +* sufficiently short to be exported to other languages +* (e.g. Fortran) which might not be able to accommodate +* full-length C pointers. +*/ + + +#if defined(MEM_DEBUG) + char buf[80]; + astHandleUse( ihandle, "about to be inserted into %s (%d)", + HeadString( head, buf ), head ); + CheckList( head, status ); + CheckInList( ihandle, head, 0, status ); +#endif + +/* Check a head pointer was supplied (may not be if an error has + occurred). */ + if( ! head ) return; + +/* If the list is empty, the sole new element points at itself. */ + if ( *head == -1 ) { + handles[ ihandle ].flink = ihandle; + handles[ ihandle ].blink = ihandle; + +/* Otherwise, insert the new element in front of the element at the + head of the list. */ + } else { + handles[ ihandle ].flink = *head; + handles[ ihandle ].blink = handles[ *head ].blink; + handles[ ( handles[ *head ].blink) ].flink = ihandle; + handles[ *head ].blink = ihandle; + } + +/* Update the list head to identify the new element. */ + *head = ihandle; + +#if defined(MEM_DEBUG) + CheckList( head, status ); + astHandleUse( ihandle, "has been inserted into %s", buf ); +#endif +} + +AstObject *astMakeId_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astMakeId + +* Purpose: +* Issue an identifier for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astMakeId( AstObject *this ) + +* Class Membership: +* Object member function. + +* Description: +* This function takes a normal C pointer to an Object and +* associates an identifier with it. + +* Parameters: +* this +* Pointer to an Object. Note that this function copies this +* value (it is not cloned), so the caller should not annul the +* pointer afterwards. + +* Returned Value: +* The resulting identifier value. + +* Notes: +* - An identifier value of zero will be returned and the supplied +* Object pointer will be annulled if this function is invoked with +* the global error status set or if it should fail for any reason. +* - A zero identifier value will also be returned if a NULL object +* pointer is supplied, but this will not provoke an error. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *id; /* ID value to return */ + int ihandle; /* Handle offset */ + +/* Initialise. */ + id = astI2P( 0 ); + ihandle = 0; + +/* Check the global error status. */ + if ( astOK ) { + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(this); + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* If a non-NULL Object pointer was given, we must obtain a Handle + structure to associate with it (otherwise a zero identifier value + is returned without error). */ + if ( this ) { + +/* If the free Handles list is not empty, obtain a Handle from it. */ + if ( free_handles != -1 ) { + ihandle = free_handles; + RemoveHandle( ihandle, &free_handles, status ); + +/* Otherwise, allocate a new Handle by extending the "handles" array + and using the offset of the new element. */ + } else { + + astBeginPM; + handles = astGrow( handles, nhandles + 1, sizeof( Handle ) ); + astEndPM; + + if ( astOK ) { + ihandle = nhandles++; + + handles[ ihandle ].ptr = NULL; + handles[ ihandle ].context = INVALID_CONTEXT; + handles[ ihandle ].check = 0; + handles[ ihandle ].flink = -1; + handles[ ihandle ].blink = -1; +#if defined(THREAD_SAFE) + handles[ ihandle ].thread = 0; +#endif + +#if defined(MEM_DEBUG) + handles[ ihandle ].id = 0; + handles[ ihandle ].vtab = NULL; +#endif + } + } + +/* If the first AST context level has not yet been initialised, invoke + InitContext to initialise it and allocate memory for the + "active_handles" array which stores context information. */ + if ( astOK ) { + if ( !active_handles ) InitContext( status ); + +/* Store the Object pointer and current context level in the Handle. */ + if ( astOK ) { + handles[ ihandle ].ptr = this; + handles[ ihandle ].context = context_level; +#if defined(THREAD_SAFE) + handles[ ihandle ].thread = AST__THREAD_ID; +#endif + +/* Store extra debugging information in the handle if enabled */ +#if defined(MEM_DEBUG) + handles[ ihandle ].id = astMemoryId( this ); + handles[ ihandle ].vtab = this->vtab; + astHandleUse( ihandle, "associated with a %s (id %d)", + astGetClass( this ), astMemoryId( this )); +#endif + +/* Insert the Handle into the active Handles list for the current + context level. */ + InsertHandle( ihandle, &active_handles[ context_level ], status ); + +/* Associate an identifier value with the Handle. */ + id = AssocId( ihandle, status ); + +/* If an error occurred, clean up by annulling the Handle. This + ensures that the Object pointer is annulled and returns the unused + Handle to the Free Handle list. */ + if ( !astOK ) { + AnnulHandle( ihandle, status ); + this = NULL; + } + +/* If the Handle wasn't used (because of an earlier error), return it + to the free Handles list. */ + } else { + InsertHandle( ihandle, &free_handles, status ); + } + } + } + UNLOCK_MUTEX2; + } + +/* If a bad status value was either supplied or generated within this + function, then annul the supplied pointer (unless it is NULL). */ + if ( !astOK && this ) (void) astAnnul( this ); + +/* Return the identifier value. */ + return id; +} + +AstObject *astMakePointer_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* astMakePointer + +* Purpose: +* Obtain a true C pointer from an Object identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astMakePointer( AstObject *this ) + +* Class Membership: +* Object class function. + +* Description: +* This function accepts an external Object identifier and returns +* a true C Object pointer that may be de-referenced to access the +* Object's data and methods. + +* Parameters: +* this +* The identifier value. + +* Returned Value: +* The true C pointer value. + +* Notes: +* - In a thread-safe context, an error is reported if the supplied +* Object has not been locked by the calling thread (using astLock) +* prior to invoking this function. +* - The object reference count is not modified by this function, +* so the returned pointer should not be annulled by the caller. +* - Typically, this function should be used whenever a public +* function must accept identifier values directly (rather than +* having them translated automatically into pointers during +* argument validation by the astCheck range of functions). +* - Note that this function will NOT accept a true C pointer as an +* argument, even when invoked from internal code (with astCLASS +* defined). An identifier value MUST be supplied, and an error +* will result if it is not associated with an active Object. +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + AstObject *ptr; /* Pointer value to return */ + int ihandle; /* Handle offset in "handles" array */ + +/* Initialise. */ + ptr = NULL; + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Validate the identifier supplied and derive the Handle offset. */ + ihandle = CheckId( this_id, 1, status ); + +/* If the identifier was valid, extract the Object pointer from the + Handle. */ + if ( ihandle != -1 ) ptr = handles[ ihandle ].ptr; + + UNLOCK_MUTEX2; + +/* Return the result. */ + return ptr; +} + +AstObject *astMakePointer_NoLockCheck_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* astMakePointer_NoLockCheck + +* Purpose: +* Obtain a true C pointer from an Object identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astMakePointer_NoLockCheck( AstObject *this ) + +* Class Membership: +* Object class function. + +* Description: +* This function accepts an external Object identifier and returns +* a true C Object pointer that may be de-referenced to access the +* Object's data and methods. +* +* It is identicial to astMakePointer except that it does not check +* that the supplied Object is locked by the running thread. + +* Parameters: +* this +* The identifier value. + +* Returned Value: +* The true C pointer value. + +* Notes: +* - The object reference count is not modified by this function, +* so the returned pointer should not be annulled by the caller. +* - Typically, this function should be used whenever a public +* function must accept identifier values directly (rather than +* having them translated automatically into pointers during +* argument validation by the astCheck range of functions). +* - Note that this function will NOT accept a true C pointer as an +* argument, even when invoked from internal code (with astCLASS +* defined). An identifier value MUST be supplied, and an error +* will result if it is not associated with an active Object. +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + AstObject *ptr; /* Pointer value to return */ + int ihandle; /* Handle offset in "handles" array */ + +/* Initialise. */ + ptr = NULL; + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Validate the identifier supplied and derive the Handle offset. */ + ihandle = CheckId( this_id, 0, status ); + +/* If the identifier was valid, extract the Object pointer from the + Handle. */ + if ( ihandle != -1 ) ptr = handles[ ihandle ].ptr; + + UNLOCK_MUTEX2; + +/* Return the result. */ + return ptr; +} + +int astP2I_( AstObject *pointer, int *status ) { +/* +*+ +* Name: +* astP2I + +* Purpose: +* Extract an integer Object ID from a pointer. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* int astP2I( AstObject *pointer ) + +* Class Membership: +* Object class function. + +* Description: +* This function retrieves an integer (int) value representing an +* Object identifier from a pointer value (into which it has +* previously been packed using astI2P). +* +* These functions should be used to avoid any dependency on +* integer-to-pointer conversions (given that the values are not +* true pointers) which might affect the exchange of Object +* identifier values with other languages, such as Fortran 77, +* where they are stored as integers. + +* Parameters: +* pointer +* The pointer value into which the ID has previously been packed. + +* Returned Value: +* The extracted Object identifier value as an integer (int). + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + IdUnion temp; /* Overlapping int and pointer */ + +/* Store the pointer value supplied. */ + temp.pointer = pointer; + +/* Return the integer part of it. */ + return temp.integer; +} + +MYSTATIC void RemoveHandle( int ihandle, int *head, int *status ) { +/* +* Name: +* RemoveHandle + +* Purpose: +* Remove a Handle from a list. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void RemoveHandle( int ihandle, int *head ) + +* Class Membership: +* Object member function. + +* Description: +* This function removes a Handle structure from a doubly linked +* list of such structures composed of elements drawn from the +* "handles" array. The "head of list" position is updated if +* necessary. + +* Parameters: +* ihandle +* Offset in the "handles" array that identifies the Handle to +* be removed from the list (note that the Handle must actually +* be in the list, although this function does not check this). +* head +* Address of an int which holds the offset in the "handles" +* array of the element at the head of the list. This value will +* be updated if necessary to identify the new list head (or set +* to -1 if removing the Handle causes the list to become +* empty). + +* Notes: +* - This function does not perform error chacking and does not +* generate errors. +* - See the InsertHandle function for details of how and why lists +* of Handles are constructed. +*/ + + +#if defined(MEM_DEBUG) + char buf[80]; + astHandleUse( ihandle, "about to be removed from %s", HeadString( head, buf ) ); + CheckList( head, status ); + CheckInList( ihandle, head, 1, status ); +#endif + +/* Check a head pointer was supplied (may not be if an error has + occurred). */ + if( ! head ) return; + +/* Remove the Handle from the list by re-establishing links between + the elements on either side of it. */ + handles[ ( handles[ ihandle ].blink ) ].flink = handles[ ihandle ].flink; + handles[ ( handles[ ihandle ].flink ) ].blink = handles[ ihandle ].blink; + +/* If the element removed was at the head of the list, update the head + of list offset to identify the following element. */ + if ( ihandle == *head ) { + *head = handles[ ihandle ].flink; + +/* If the head of list still identifies the removed element, then note + that the list is now empty. */ + if ( *head == ihandle ) *head = -1; + } + +/* Make the removed element point at itself. */ + handles[ ihandle ].flink = ihandle; + handles[ ihandle ].blink = ihandle; + +#if defined(MEM_DEBUG) + astHandleUse( ihandle, "has been removed from %s", buf ); + CheckList( head, status ); +#endif +} + +void astSetId_( void *this_id_void, const char *settings, ... ) { +/* +* Name: +* astSetId_ + +* Purpose: +* Set values for an Object's attributes via an identifier. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void astSetId_( AstObject *this, const char *settings, ... ) + +* Class Membership: +* Object member function. + +* Description: +* This function implements the axternal interface to the astSet +* method (q.v.). It accepts an Object identifier, but otherwise +* behaves identically to astSet. + +* Parameters: +* this +* Object identifier. +* settings +* Pointer to a null-terminated string containing a +* comma-separated list of attribute settings. +* ... +* Optional arguments which supply values to be substituted for +* any "printf"-style format specifiers that appear in the +* "settings" string. + +* Notes: +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* Object identifier is of type (void *) and is converted and +* validated within the function itself. +*/ + +/* Local Variables: */ + AstObject *this; /* Pointer to the Object structure */ + int *status; /* Pointer to inherited status variable */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the integer holding the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + this = astCheckObject( astMakePointer( this_id_void ) ); + if ( astOK ) { + +/* Obtain the variable argument list and pass all arguments to the + astVSet method for interpretation. */ + va_start( args, settings ); + astVSet( this, settings, NULL, args ); + va_end( args ); + } +} + +int astThreadId_( AstObject *this_id, int ptr, int *status ) { +/* +c++ +* Name: +* astThread + +* Purpose: +* Determine the thread that owns an Object. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* int astThread( AstObject *this, int ptr ) + +* Class Membership: +* Object method. + +* Description: +* Returns an integer that indicates whether the supplied Object (or +* Object pointer) is currently unlocked, or is currently locked by +* the running thread, or another thread. + +* Parameters: +* this +* Pointer to the Object to be checked. +* ptr +* If non-zero, returns information about the supplied Object +* pointer, rather than the Object structure itself. See "Object +* Pointers and Structures" below. + +* Returned Value: +* astThread() +* A value of AST__UNLOCKED is returned if the Object (or pointer) +* is currently unlocked (i.e. has been unlocked using astUnlock +* but has not yet been locked using astLock). A value of +* AST__RUNNING is returned if the Object (or pointer) is currently +* locked by the running thread. A value of AST__OTHER is returned +* if the Object (or pointer) is currently locked by the another +* thread. + +* Object Pointers and Structures: +* At any one time, an AST Object can have several distinct pointers, +* any one of which can be used to access the Object structure. For +* instance, the astClone function will produce a new distinct pointer +* for a given Object. In fact, an AST "pointer" is not a real pointer +* at all - it is an identifier for a "handle" structure, encoded to +* make it look like a pointer. Each handle contains (amongst othere +* things) a "real" pointer to the Object structure. This allows more +* than one handle to refer to the same Object structure. So when you +* call astClone (for instance) you get back an identifier for a new +* handle that refers to the same Object as the supplied handle. +* +* In order to use an Object for anything useful, it must be locked +* for use by the running thread (either implicitly at creation or +* explicitly using astLock). The identity of the thread is stored in +* both the Object structure, and in the handle that was passed to +* astLock (or returned by the constructor function). Thus it is +* possible for a thread to have active pointers for Objects that are +* currently locked by another thread. In general, if such a pointer is +* passed to an AST function an error will be reported indicating that +* the Object is currently locked by another thread. The two exceptions +* to this is that astAnnul can be used to annull such a pointer, and +* this function can be used to return information about the pointer. +* +* The other practical consequence of this is that when astEnd is +* called, all active pointers currently owned by the running thread +* (at the current context level) are annulled. This includes pointers +* for Objects that are currently locked by other threads. +* +* If the "ptr" parameter is zero, then the returned value describes +* the Object structure itself. If "ptr" is non-zero, then the returned +* value describes the supplied Object pointer (i.e. handle), rather +* than the Object structure. + +* Notes: +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - This function is only available in the C interface. +* - This function always returns AST__RUNNING if the AST library has +* been built without POSIX thread support (i.e. the "-with-pthreads" +* option was not specified when running the "configure" script). +c-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int result; /* The returned value */ + +#if defined(THREAD_SAFE) + +/* More local Variables: */ + AstObject *this; + int ihandle; + int check; + +/* Ensure global variables are accessable. */ + astGET_GLOBALS(NULL); +#endif + +/* Initialise the returned value */ + result = AST__RUNNING; + +/* Nothing more to do if AST was not build with thread support. */ +#if defined(THREAD_SAFE) + +/* If the ownership of the handle is being queried... */ + if( ptr ) { + +/* Lock the mutex that guards access to the handles array */ + LOCK_MUTEX2; + +/* Check the supplied object identifier is valid and get the + corresponding index into the handles array. */ + ihandle = CheckId( this_id, 1, status ); + if( ihandle != -1 ) { + +/* Set the returned value on the basis of the threa didentifier stored in + the handle structure. */ + if( handles[ ihandle ].thread == -1 ) { + result = AST__UNLOCKED; + } else if( handles[ ihandle ].thread != AST__THREAD_ID ) { + result = AST__OTHER; + } + } + +/* Unlock the mutex that guards access to the handles array */ + UNLOCK_MUTEX2; + +/* Otherwise, the ownership of the Object is being queried. Obtain the + Object pointer from the ID supplied and validate the pointer to ensure + it identifies a valid Object (this generates an error if it doesn't). + Note, we use the "astMakePointer_NoLockCheck", since the usual + "astMakePointer" macro invokes astCheckLock to report an error if the + Object is not currently locked by the calling thread. */ + } else if( astIsAObject( this = astMakePointer_NoLockCheck( this_id ) ) ) { + +/* Determine which thread (if any) has the object locked, and set an + appropriate return value. */ + check = astManageLock( this, AST__CHECKLOCK, 0, NULL ); + + if( check == 5 ) { + result = AST__OTHER; + } else if( check == 6 ) { + result = AST__UNLOCKED; + } + } + +#endif + +/* Return the result. */ + return result; +} + +int astVersion_( int *status ) { +/* +*++ +* Name: +c astVersion +f AST_VERSION + +* Purpose: +* Return the version of the AST library being used. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astVersion +f RESULT = AST_VERSION() + +* Class Membership: +* Object class function. + +* Description: +c This macro invokes a function which +f This function +* returns an integer representing the version of the AST library +* being used. The library version is formatted as a string such as +* "2.0-7" which contains integers representing the "major version" (2), +* the "minor version" (0) and the "release" (7). The integer returned +* by this function combines all three integers together into a single +* integer using the expresion: +* +* (major version)*1E6 + (minor version)*1E3 + (release) + +* Returned Value: +c astVersion +f AST_VERSION = INTEGER +* The major version, minor version and release numbers for the AST +* library, encoded as a single integer. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +*-- +*/ + + return (int) AST__VMAJOR*1E6 + AST__VMINOR*1E3 + AST__RELEASE; +} + +int astEscapes_( int new_value, int *status ) { +/* +*++ +* Name: +c astEscapes +f AST_ESCAPES + +* Purpose: +* Control whether graphical escape sequences are included in strings. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astEscapes( int new_value ) +f RESULT = AST_ESCAPES( NEWVAL, STATUS ) + +* Class Membership: +* Object class function. + +* Description: +* The Plot class defines a set of escape sequences which can be +* included within a text string in order to control the appearance of +* sub-strings within the text. See the Escape attribute for a +* description of these escape sequences. It is usually inappropriate +* for AST to return strings containing such escape sequences when +* called by application code. For instance, an application which +* displays the value of the Title attribute of a Frame usually does +* not want the displayed string to include potentially long escape +* sequences which a human read would have difficuly interpreting. +* Therefore the default behaviour is for AST to strip out such escape +* sequences when called by application code. This default behaviour +* can be changed using this function. + +* Parameters: +c new_value +f NEWVAL = INTEGER (Given) +* A flag which indicates if escapes sequences should be included +* in returned strings. If zero is supplied, escape sequences will +* be stripped out of all strings returned by any AST function. If +* a positive value is supplied, then any escape sequences will be +* retained in the value returned to the caller. If a negative +* value is supplied, the current value of the flag will be left +* unchanged. + +* Returned Value: +c astEscapes +f AST_ESCAPES = INTEGER +* The value of the flag on entry to this function. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - This function also controls whether the +c astStripEscapes +f AST_STRIPESCAPES +* function removes escape sequences from the supplied string, or +* returns the supplied string without change. +* - This function attempts to execute even if an error has already +* occurred. + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + int old_val; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Save the old value. */ + old_val = retain_esc; + +/* Set the new value. */ + if( new_value > 0 ) { + retain_esc = 1; + + } else if( new_value == 0 ) { + retain_esc = 0; + } + +/* Return the old value. */ + return old_val; +} + + +#if defined(MEM_DEBUG) + +/* Check each handle in a list is uniquely connected to one other handle + in both the forward and backward directions. */ + +void CheckList( int *head, int *status ) { + int ok; + int ihandle; + char buf[200]; + astDECLARE_GLOBALS + if( !astOK ) return; + + astGET_GLOBALS(NULL); + + ok = 1; + if ( *head != -1 ) { + ihandle = *head; + if( handles[ handles[ ihandle ].blink ].flink != ihandle || + handles[ handles[ ihandle ].flink ].blink != ihandle ) { + ok = 0; + + } else { + if( CheckThread( ihandle, head, status ) ) { + ihandle= handles[ *head ].blink; + while( ihandle != *head ) { + if( handles[ handles[ ihandle ].blink ].flink != ihandle || + handles[ handles[ ihandle ].flink ].blink != ihandle || + CheckThread( ihandle, head, status ) == 0 ) { + ok = 0; + break; + } + ihandle= handles[ ihandle ].blink; + } + } + } + } + + if( !ok ) { + printf("CheckList error in %s\n", HeadString( head, buf ) ); + printf(" Central handle: %s\n", HandleString( ihandle, buf ) ); + + if( handles[ handles[ ihandle ].blink ].flink != ihandle ) { + printf(" Central handle->blink: %s\n", + HandleString( handles[ ihandle ].blink, buf ) ); + printf(" Central handle->blink->flink: %s\n", + HandleString( handles[ handles[ ihandle ].blink ].flink, buf ) ); + } + + if( handles[ handles[ ihandle ].flink ].blink != ihandle ) { + printf(" Central handle->flink: %s\n", + HandleString( handles[ ihandle ].flink, buf ) ); + printf(" Central handle->flink->blink: %s\n", + HandleString( handles[ handles[ ihandle ].flink ].blink, buf ) ); + } + } + +} + + +/* Check if a specified handle is, or is not, in a given list of handles. */ + +void CheckInList( int ihandle, int *head, int in, int *status ){ + char list[80], buf[200]; + int found = 0; + if( !astOK ) return; + + if ( *head != -1 ) { + if( ihandle == *head ) { + found = 1; + } else { + if( CheckThread( ihandle, head, status ) ) { + int jhandle= handles[ *head ].blink; + while( jhandle != *head ) { + if( ihandle == jhandle ) { + found = 1; + break; + } + jhandle= handles[ jhandle ].blink; + } + } + } + } + + if( in && !found ) { + printf("Error: Handle %s not in %s\n", HandleString( ihandle, buf ), + HeadString( head, list ) ); + } else if( !in && found ) { + printf("Error: Handle %s is in %s\n", HandleString( ihandle, buf ), + HeadString( head, list ) ); + } + +} + +/* Check that a handle is owned by the currently executing thread. */ + +int CheckThread( int ihandle, int *head, int *status ) { + int result = 1; + +#if defined(THREAD_SAFE) + + char buf[200]; + astDECLARE_GLOBALS + if( !astOK ) return result; + + astGET_GLOBALS(NULL); + + if( *head == unowned_handles ) { + if( handles[ ihandle ].thread != -1 ) { + printf("Handle %s has wrong thread: is %d, should " + "be -1 (i.e. unowned)\n", HandleString( ihandle, buf ), + handles[ ihandle ].thread ); + + result = 0; + } + + } else if( *head == free_handles ) { + if( handles[ ihandle ].thread != -1 ) { + printf("Handle %s has wrong thread: is %d, should " + "be -1 (i.e. free)\n", HandleString( ihandle, buf ), + handles[ ihandle ].thread ); + result = 0; + } + + } else if( handles[ ihandle ].thread != AST__THREAD_ID ) { + printf("Handle %s has wrong thread: is %d, should " + "be %d\n", HandleString( ihandle, buf ), + handles[ ihandle ].thread, AST__THREAD_ID ); + result = 0; + } + +#endif + + return result; +} + +void astWatchHandle_( int handle ){ + Watched_Handle = handle; +} + +void astHandleUse_( int handle, const char *verb, ... ){ + va_list args; + if( handle == Watched_Handle ) { + va_start( args, verb ); + astHandleAlarm( verb, args ); + va_end( args ); + } +} + +void astHandleAlarm_( const char *verb, va_list args ){ + char buff[200], hbuf[200]; + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + + vsprintf( buff, verb, args ); + +#if defined(THREAD_SAFE) + printf( "astHandleAlarm: Handle %s %s (current thread is %d).\n\n", + HandleString( Watched_Handle, hbuf ), buff, AST__THREAD_ID ); +#else + printf( "astHandleAlarm: Handle %s %s.\n\n", + HandleString( Watched_Handle, hbuf ), buff ); +#endif +} + +MYSTATIC const char *HandleString( int ihandle, char *buf ){ +#if defined(THREAD_SAFE) + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + + if( ihandle >= 0 ) { + sprintf( buf, "(index:%d v:%d c:%d t:%d i:%d cl:%s) [cur. thread: %d]", + ihandle, + handles[ ihandle ].check, + handles[ ihandle ].context, handles[ ihandle ].thread, + handles[ ihandle ].id, + handles[ ihandle ].vtab ? handles[ ihandle ].vtab->class : "", + AST__THREAD_ID ); + } else { + sprintf( buf, "(index:%d ) [cur. thread: %d]", ihandle, + AST__THREAD_ID ); + } + +#else + if( ihandle >= 0 ) { + sprintf( buf, "(index:%d v:%d c:%d i:%d cl:%s)", ihandle, + handles[ ihandle ].check, + handles[ ihandle ].context, handles[ ihandle ].id, + handles[ ihandle ].vtab ? handles[ ihandle ].vtab->class : "" ); + } else { + sprintf( buf, "(index:%d )", ihandle ); + } +#endif + return buf; +} + +MYSTATIC const char *HeadString( int *head, char *list ){ + int i; + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + + if( head == &free_handles ) { + strcpy( list, "free_handles" ); + +#if defined(THREAD_SAFE) + } else if( head == &unowned_handles ) { + strcpy( list, "unowned_handles" ); +#endif + + } else { + *list = 0; + for( i = 0; i <= context_level; i++ ) { + if( *head == active_handles[ i ] ) { + sprintf( list, "active_handles[%d]", i ); + break; + } + } + if( *list == 0 ) sprintf( list, "unknown handles list with head %d", + *head ); + } + return list; +} + +#endif diff --git a/object.f b/object.f new file mode 100644 index 0000000..34f92b2 --- /dev/null +++ b/object.f @@ -0,0 +1,70 @@ + CHARACTER * ( * ) FUNCTION AST_GETC( THIS, ATTRIB ) +*+ +* Name: +* AST_GETC + +* Purpose: +* Wrap up the C ast_getc_a function. + +* Language: +* Fortran 77 + +* Invocation: +* RESULT = AST_GETC( THIS, ATTRIB ) + +* Description: +* This function is a wrap-up of the C ast_getc_a function. It will +* rarely need to be used, but is provided for use on platforms +* where the normal mechanism for returning character strings as +* function results from C to Fortran (as defined in the f77.h +* include file) doesn't work. +* +* If this problem is encountered, the C macro NO_CHAR_FUNCTION +* should be defined (in CFLAGS) during C compilation. This will +* cause the function ast_getc_a to be built (instead of ast_getc) +* and this returns its the result via an additional initial +* argument. This Fortran function is then used simply to transfer +* the argument value to the function result. + +* Arguments: +* As for the C version of the Fortran-callable function ast_getc. + +* Returned Value: +* AST_GETC = CHARACTER * ( * ) +* The character return value required. + +* Notes: +* - The length of the returned result is limited to 200 characters +* by a local buffer. This length can be increased if necessary. + +* Authors: +* RFWS: R.F. Warren-Smith (STARLINK, RAL) +* {enter_new_authors_here} + +* History: +* 3-JUL-1996 (RFWS): +* Original version. +* {enter_changes_here} + +* Bugs: +* {note_any_bugs_here} + +*- + +* Type Definitions: + IMPLICIT NONE ! No implicit typing + +* Arguments Given: + INTEGER THIS + CHARACTER * ( * ) ATTRIB + +* Local Variables: + CHARACTER * ( 200 ) BUFF ! Local buffer for result +*. + +* Invoke the C function (with the additional argument). + CALL AST_GETC_A( BUFF, THIS, ATTRIB ) + +* Return the argument value as the function result. + AST_GETC = BUFF + END diff --git a/object.h.in b/object.h.in new file mode 100644 index 0000000..41901d5 --- /dev/null +++ b/object.h.in @@ -0,0 +1,1934 @@ +#if !defined( OBJECT_INCLUDED ) /* Include this file only once */ +#define OBJECT_INCLUDED +/* +*++ +* Name: +* object.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Object class. + +* Invocation: +* #include "object.h" + +* Description: +* This include file defines the interface to the Object class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* The Object class is the base class from which all other classes +* in the AST library are derived. This class provides all the +* basic Object behaviour and Object manipulation facilities +* required throughout the library. There is no Object constructor, +* however, as Objects on their own are not of much use. + +* Inheritance: +* The Object base class does not inherit from any other class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* Class (string) +* This is a read-only attribute containing the name of the +* class to which an Object belongs. +* ID (string) +* An identification string which may be used to identify the +* Object (e.g.) in debugging output, or when stored in an +* external medium such as a data file. There is no restriction +* on the string's contents. The default is an empty string. +* Ident (string) +* Like ID, this is an identification string which may be used +* to identify the Object. Unlike ID, Ident is transferred when an +* Object is copied. +* UseDefs (int) +* Should default values be used for unset attributes? +* Nobject (integer) +* This is a read-only attribute which gives the total number of +* Objects currently in existence in the same class as the +* Object given. It does not include Objects which belong to +* derived (more specialised) classes. This value is mainly +* intended for debugging, as it can be used to show whether +* Objects which should have been deleted have, in fact, been +* deleted. +* ObjSize (int) +* The in-memory size of the Object in bytes. +* RefCount (integer) +* This is a read-only Attribute which gives the "reference +* count" (the number of active pointers) associated with an +* Object. It is modified whenever pointers are created or +* annulled (by astClone or astAnnul/astEnd for example) and +* includes the initial pointer issued when the Object was +* created. If the reference count for an Object falls to zero +* as the result of annulling a pointer to it, then the Object +* will be deleted. + +* Methods Over-Ridden: +* None. + +* New Methods Defined: +* Public: +* astAnnul +* Annul a pointer to an Object. +* astClear +* Clear attribute values for an Object. +* astClone +* Clone a pointer to an Object. +* astCopy +* Copy an Object. +* astDelete +* Delete an Object. +* astExempt +* Exempt an Object pointer from AST context handling +* astExport +* Export an Object pointer to an outer context. +* astGet, where = C, D, F, I, L +* Get an attribute value for an Object. +* astImport +* Import an Object pointer into the current context. +* astSame +* Return true if two pointers refer to the same object. +* astSet +* Set attribute values for an Object. +* astSet, where = C, D, F, I, L +* Set an attribute value for an Object. +* astShow +* Display a textual representation of an Object on standard output. +* astTest +* Test if an attribute value is set for an Object. +* astTune +* Get or set the value of a global AST tuning parameter. +* +* Protected: +* astAnnulId +* Annul an external ID for an Object (for use from protected code +* which must handle external IDs). +* astClearAttrib +* Clear the value of a specified attribute for an Object. +* astClearID +* Clear the value of the ID attribute for an Object. +* astClearIdent +* Clear the value of the Ident attribute for an Object. +* astCast +* Return a deep copy of an object, cast into an instance of a +* parent class. +* astDump +* Write an Object to a Channel. +* astEqual +* Are two Objects equivalent? +* astGetAttrib +* Get the value of a specified attribute for an Object. +* astGetClass (deprecated synonym astClass) +* Obtain the value of the Class attribute for an Object. +* astGetID +* Obtain the value of the ID attribute for an Object. +* astGetIdent +* Obtain the value of the Ident attribute for an Object. +* astGetNobject +* Obtain the value of the Nobject attribute for an Object. +* astGetRefCount +* Obtain the value of the RefCount attribute for an Object. +* astSetAttrib +* Set the value of a specified attribute for an Object. +* astSetCopy +* Declare a copy constructor for an Object. +* astSetDelete +* Declare a destructor for an Object. +* astSetDump +* Declare a dump function for an Object. +* astSetVtab +* Chaneg the virtual function table associated with an Object. +* astSetID +* Set the value of the ID attribute for an Object. +* astSetIdent +* Set the value of the Ident attribute for an Object. +* astTestAttrib +* Test if a specified attribute value is set for an Object. +* astTestID +* Test whether the ID attribute for an Object is set. +* astTestIdent +* Test whether the Ident attribute for an Object is set. +* astVSet +* Set values for an Object's attributes. + +* Other Class Functions: +* Public: +* astBegin +* Begin a new AST context. +* astEnd +* End an AST context. +* astIsAObject +* Test class membership. +* astVersion +* Returns the AST library version number. +* astEscapes +* Remove escape sequences from returned text strings? +* astP2I +* Retrieve an int from a pointer. +* astI2P +* Pack an int into a pointer. +* +* Protected: +* astCheckObject +* Validate class membership. +* astInitObject +* Initialise an Object. +* astInitObjectVtab +* Initialise the virtual function table for the Object class. +* astLoadObject +* Load an Object. +* astMakeId +* Issue an identifier for an Object. +* astMakePointer +* Obtain a true C pointer from an Object identifier. + +* Macros: +* Public: +* AST__NULL +* Null Object pointer value. +* AST__VMAJOR +* The AST library major version number. +* AST__VMINOR +* The AST library minor version number. +* AST__RELEASE +* The AST library release number. +* +* Protected: +* astEQUAL +* Compare two doubles for equality. +* astMAX +* Return maximum of two values. +* astMIN +* Return minimum of two values. +* astMAKE_CHECK +* Implement the astCheck_ function for a class. +* astMAKE_CLEAR +* Implement a method to clear an attribute value for a class. +* astMAKE_GET +* Implement a method to get an attribute value for a class. +* astMAKE_ISA +* Implement the astIsA_ function for a class. +* astMAKE_SET +* Implement a method to set an attribute value for a class. +* astMAKE_TEST +* Implement a method to test if an attribute has been set for a +* class. +* astMEMBER +* Locate a member function. + +* Type Definitions: +* Public: +* AstObject +* Object type. +* +* Protected: +* AstObjectVtab +* Object virtual function table type. + +* Feature Test Macros: +* AST_CHECK_CLASS +* If the AST_CHECK_CLASS macro is defined, then Object class +* checking is enabled for all internal function invocations +* within the AST library. Otherwise, this checking is +* omitted. This macro should normally be defined as a compiler +* option during library development and debugging, but left +* undefined for software releases, so as to improve +* peformance. Class checking by the AST public interface is not +* affected by this macro. +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. +* astFORTRAN77 +* If the astFORTRAN77 macro is defined, reporting of error +* context information is suppressed. This is necessary when +* implementing foreign language interfaces to the AST library, as +* otherwise the file names and line numbers given would refer +* to the interface implementation rather than the user's own +* code. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 30-JAN-1996 (RFWS): +* Original version. +* 19-APR-1996 (RFWS): +* Added macros for implementing attribute access methods. +* 3-JUL-1996 (RFWS): +* Added new definitions to support the external interface. +* 10-SEP-1996 (RFWS): +* Added loader and related facilities. +* 30-MAY-1997 (RFWS): +* Add the ID attribute. +* 14-JUL-1997 (RFWS): +* Add astExempt function. +* 20-JAN-1998 (RFWS): +* Make the astClear and astVSet methods virtual. +* 15-SEP-1999 (RFWS): +* Made the astAnnulId function accessible to protected code. +* 3-APR-2001 (DSB): +* Added Ident attribute. +* 8-JAN-2003 (DSB): +* Added protected astInitObjectVtab method. +* 30-APR-2003 (DSB): +* Added macros AST__VMAJOR, AST__VMINOR and AST__RELEASE. +* Added astVersion function. +* 7-FEB-2004 (DSB): +* Added astEscapes function. +* 11-MAR-2005 (DSB): +* Added UseDefs attribute. +* 7-FEB-2006 (DSB): +* Added astTune function. +* 14-FEB-2006 (DSB): +* Added ObjSize attribute. +* 23-FEB-2006 (DSB): +* Moved AST__TUNULL from this file to memory.h. +* 10-MAY-2006 (DSB): +* Added astEQUAL, astMAX and astMIN. +* 26-MAY-2006 (DSB): +* Make all system includes unconditional, so that makeh is not +* confused when creating ast.h. +* 22-JUN-2007 (DSB): +* - Make astVSet return a pointer to dynamic memory holding the +* expanded setting string. +* - Add ast astSetVtab and astCast. +* 22-APR-2008 (DSB): +* Added astSame. +* 7-APR-2010 (DSB): +* Added astHasAttribute. +*-- +*/ + +/* Include files. */ +/* ============== */ +/* Configuration results. */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" /* Error reporting facilities */ +#include "version.h" /* Version number macros */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +#if defined(THREAD_SAFE) +#include +#endif + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Set to "1" (yes) or "0" (no) to indicate if AST was build with threads + support. */ +#define AST__THREADSAFE @THREADS@ + +#if defined(astCLASS ) +#define AST__GETATTRIB_BUFF_LEN 50 /* Length of string returned by GetAttrib. */ +#define AST__ASTGETC_MAX_STRINGS 50 /* Number of string values to buffer within astGetC */ + +/* Values supplied to astManageLock */ +#define AST__LOCK 1 /* Lock the object */ +#define AST__UNLOCK 2 /* Unlock the object */ +#define AST__CHECKLOCK 3 /* Check if the object is locked */ + +/* Values returned by astThread */ +#define AST__UNLOCKED 1 /* Object is unlocked */ +#define AST__RUNNING 2 /* Object is locked by the running thread */ +#define AST__OTHER 3 /* Object is locked by another thread */ + +#endif + +/* Value that indicates that two classes are not in direct line from each + other. */ +#if defined(astCLASS ) +#define AST__COUSIN -1000000 +#endif + +/* +*+ +* Name: +* astINVOKE + +* Type: +* Protected macro. + +* Purpose: +* Invoke an AST function. + +* Synopsis: +* #include "object.h" +* astINVOKE(rettype,function) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an invocation of an AST function, together +* with any additional actions required to support it. The actual +* expansion depends on whether the macro is expanded in internal +* code (astCLASS defined) or external code (astCLASS undefined) +* and it therefore hides the differences between these two +* interfaces. + +* Parameters: +* rettype +* A character to indicate the type of result returned by the function: +* +* V +* The function returns a value (including void or a pointer +* value, but excluding an Object pointer). astINVOKE will +* return the value unchanged. +* O +* The function returns an Object pointer. astINVOKE will +* convert it to an Object identifier if necessary. +* F +* The function returns a function pointer. astINVOKE will +* return it unchanged. This is typically used when the +* function has a variable argument list. In this case the +* function name is passed to astINVOKE without its argument +* list and a pointer to the function is returned which can +* then be supplied with an argument list. This avoids the +* need to define a macro with a variable number of arguments +* (which isn't allowed). +* function +* A normal invocation of the function returning the required +* result. In the case of a variable argument list, the +* argument list should be omitted so that the function is not +* invoked but a function pointer is returned that may then be +* used to invoke it. + +* Examples: +* #define astGetNobject(this) \ +* astINVOKE(V,astGetNobject_(astCheckObject(this))) +* Defines a macro to invoke the astGetNobject_ function which +* returns an int. +* #define astClone(this) \ +* astINVOKE(O,astClone_(astCheckObject(this))) +* Defines a macro to invoke the astClone_ function which +* returns an Object pointer. +* #define astSet astINVOKE(F,astSet_) +* Defines a macro to invoke the astSet_ function which has a +* variable argument list and returns void. The macro result is +* a pointer to the astSet_ function. This function must perform +* its own argument validation, as (e.g) astCheckObject cannot +* be invoked on its arguments via a macro. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*- +*/ + +/* Define astINVOKE, which records the current file and line number + (in case of error) using astAt, and then invokes the function + supplied as an argument of the astRetV_, astRetO_ or astRetF_ + macro. + + Suppress reporting of the file and line number from internal code + and from foreign language interfaces by not using astAt in these + cases. */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define astINVOKE(rettype,function) astRet##rettype##_(function) +#else +#define astINVOKE(rettype,function) \ +astERROR_INVOKE(astRet##rettype##_(function)) +#endif + +/* astRetF_ and astRetV_ currently do nothing. */ +#define astRetF_(x) (x) +#define astRetV_(x) (x) + +/* However, astRetO_ converts a pointer to an ID if necessary. */ +#if defined(astCLASS) +#define astRetO_(x) ((void *)(x)) +#else +#define astRetO_(x) ((void *)astMakeId_((AstObject *)(x),STATUS_PTR)) +#endif + +/* +*+ +* Name: +* astINVOKE_CHECK +* astINVOKE_ISA + +* Type: +* Protected macros. + +* Purpose: +* Invoke the astCheck_ and astIsA_ functions. + +* Synopsis: +* #include "object.h" +* astINVOKE_CHECK(class,this,force) +* astINVOKE_ISA(class,this) + +* Class Membership: +* Defined by the Object class. + +* Description: +* These macros expand to invocations of the standard +* astCheck_ and astIsA_ functions for a class. + +* Parameters: +* class +* The name (not the type) of the class for which the function +* is to be invoked. +* this +* The "this" argument (the Object pointer) to be passed to the +* function. +* force +* Type checking takes time, and so can be disabled within the +* protected context in order to save time. Setting "force" to +* zero causes the astINVOKE_CHECK macro to skip the class check +* in a protected context (it assumes that AST "knows what it is +* doing"). Setting "force" to a non-zero value forces the class +* check even in a protected context. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*- +*/ + +/* For the public interface (and also internally if AST_CHECK_CLASS is + defined), define astINVOKE_CHECK to invoke the astCheck + function. */ +#if !defined(astCLASS) || defined(AST_CHECK_CLASS) +#define astINVOKE_CHECK(class,this,force) \ +astCheck##class##_((Ast##class *)astEnsurePointer_(this),astGetStatusPtr) + +/* For the internal interface, astINVOKE_CHECK omits the + astCheck function (unless AST_CHECK_CLASS is defined). */ +#else + +#define astINVOKE_CHECK(class,this,force) ( (force) ? \ + ( astCheck##class##_((Ast##class *)astEnsurePointer_(this),astGetStatusPtr) ) : \ + ( (Ast##class *) astEnsurePointer_(this) ) ) + +#endif + +/* Define astINVOKE_ISA to invoke the astIsA function. */ +#if defined(astCLASS) /* Protected */ +#define astINVOKE_ISA(class,this) \ +astIsA##class##_((const Ast##class *)(this),status) +#else /* Public */ +#define astINVOKE_ISA(class,this) \ +astINVOKE(V,astIsA##class##_((const Ast##class *)astEnsurePointer_(this),astGetStatusPtr)) +#endif + +/* The astEnsurePointer_ macro ensures a true C pointer, converting + from an ID if necessary. */ +#if defined(astCLASS) /* Protected */ +#define astEnsurePointer_(x) ((void *)(x)) +#else /* Public */ +#define astEnsurePointer_(x) ((void *)astCheckLock_(astMakePointer_((AstObject *)(x),STATUS_PTR),STATUS_PTR)) +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_CHECK + +* Type: +* Protected macro. + +* Purpose: +* Implement the astCheck_ function for a class. + +* Synopsis: +* #include "object.h" +* astMAKE_CHECK(class) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of the public astCheck_ +* function (q.v.) which validates membership of a specified class. + +* Parameters: +* class +* The name (not the type) of the class whose membership is to be +* validated. + +* Notes: +* - This macro is provided so that class definitions can easiy +* implement the astCheck_ function, which is essentially the same +* for each class apart for a change of name. +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +#ifndef MEM_DEBUG + +/* Define the macro. */ +#define astMAKE_CHECK(class) \ +\ +/* Declare the function (see the object.c file for a prologue). */ \ +Ast##class *astCheck##class##_( Ast##class *this, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return this; \ +\ +/* Check if the object is a class member. */ \ + if ( !astIsA##class( this ) ) { \ +\ +/* If not, but the pointer was valid (which means it identifies an Object \ + of some sort), then report more information about why this Object is \ + unsuitable. */ \ + if ( astOK ) { \ + astError( AST__OBJIN, "Pointer to " #class " required, but pointer " \ + "to %s given.", status, astGetClass( this ) ); \ + } \ + } \ +\ +/* Return the pointer value supplied. */ \ + return this; \ +} + +/* Define the macro with memory debugging facilities. */ +#else + +#define astMAKE_CHECK(class) \ +\ +/* Declare the function (see the object.c file for a prologue). */ \ +Ast##class *astCheck##class##_( Ast##class *this, int *status ) { \ +\ + char buf[100]; \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return this; \ +\ +/* Check if the object is a class member. */ \ + if ( !astIsA##class( this ) ) { \ +\ +/* If not, but the pointer was valid (which means it identifies an Object \ + of some sort), then report more information about why this Object is \ + unsuitable. */ \ + if ( astOK ) { \ + astError( AST__OBJIN, "Pointer to " #class " required, but pointer " \ + "to %s given.", status, astGetClass( this ) ); \ + }\ +\ + } else { \ +\ +/* Call the astMemoryUse function to report it if the memory block is \ + being watched. */ \ + sprintf( buf, "checked (refcnt: %d)", astGetRefCount_( (AstObject *) this, status ) ); \ + astMemoryUse( this, buf ); \ + } \ +\ +/* Return the pointer value supplied. */ \ + return this; \ +} +#endif +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_CLEAR + +* Purpose: +* Implement a method to clear an attribute value for a class. + +* Type: +* Protected macro. + +* Synopsis: +* #include "object.h" +* astMAKE_CLEAR(class,attribute,component,assign) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( Ast *this ) +* +* and an external interface function of the form: +* +* void astClear_( Ast *this ) +* +* which implement a method for clearing a specified attribute value for +* a class. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. Label in "astClearLabel"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. + +* Examples: +* astMAKE_CLEAR(MyStuff,Flag,flag,-1) +* Implements the astClearFlag method for the MyStuff class which +* operates by setting the "flag" structure component to -1 to indicate +* that it has no value. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define astMAKE_CLEAR(class,attribute,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attribute( Ast##class *this, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Assign the "clear" value. */ \ + this->component = (assign); \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attribute##_( Ast##class *this, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,class,Clear##attribute))( this, status ); \ +} +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_CLEAR1 + +* Purpose: +* Implement a method to clear an attribute value for a class, reporting +* an error if the object has more than one reference. + +* Type: +* Protected macro. + +* Synopsis: +* #include "object.h" +* astMAKE_CLEAR1(class,attribute,component,assign) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( Ast *this ) +* +* and an external interface function of the form: +* +* void astClear_( Ast *this ) +* +* which implement a method for clearing a specified attribute value for +* a class. An error is reported if the object has a reference count that +* is greater than one. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. Label in "astClearLabel"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define astMAKE_CLEAR1(class,attribute,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attribute( Ast##class *this, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Report an error if the object has been cloned (i.e. has a reference \ + count that is greater than one). */ \ + if( astGetRefCount( this ) > 1 ) { \ + astError( AST__IMMUT, "astClear(%s): The " #attribute "attribute of " \ + "the supplied %s cannot be cleared because the %s has " \ + "been cloned (programming error).", status, \ + astGetClass(this), astGetClass(this), astGetClass(this) ); \ +\ +/* Otherwise, assign the "clear" value in the structure component. */ \ + } else { \ + this->component = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attribute##_( Ast##class *this, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,class,Clear##attribute))( this, status ); \ +} +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_GET + +* Purpose: +* Implement a method to get an attribute value for a class. + +* Type: +* Protected macro. + +* Synopsis: +* #include "object.h" +* astMAKE_GET(class,attribute,type,bad_value,assign) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( Ast *this ) +* +* and an external interface function of the form: +* +* astGet_( Ast *this ) +* +* which implement a method for getting a specified attribute value for a +* class. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the inherited error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. + +* Examples: +* astMAKE_GET(MyStuff,Flag,int,0,( this->flag == 1 )) +* Implements the astGetFlag method for the MyStuff class which operates +* by examining the integer "flag" structure component and comparing it +* with the value 1 to see if it is set. A value of 0 is returned if the +* method fails to complete successfully. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define astMAKE_GET(class,attribute,type,bad_value,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attribute( Ast##class *this, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Assign the result value. */ \ + result = (assign); \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attribute##_( Ast##class *this, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,class,Get##attribute))( this, status ); \ +} +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_ISA + +* Type: +* Protected macro. + +* Purpose: +* Implement the astIsA_ function for a class. + +* Synopsis: +* #include "object.h" +* astMAKE_ISA(class,parent) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of the public +* astIsA_ function (q.v.) which checks membership of a +* specified class. + +* Parameters: +* class +* The name (not the type) of the class whose membership is to be +* tested. +* parent +* The name of the parent class. + +* Notes: +* - This macro is provided so that class definitions can easiy +* implement the astIsA_ function, which is essentially the +* same for each class apart for a change of name. +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define astMAKE_ISA(class,parent) \ +\ +/* Declare the function (see the object.c file for a prologue). */ \ +int astIsA##class##_( const Ast##class *this, int *status ) { \ +\ +/* Local Variables: */ \ + int isa = 0; /* Is object a member? */ \ +\ +/* To test if the object is correctly constructed, we first test if it is a \ + member of the parent class. This improves the security of the test by \ + checking the object structure from the base Object class downwards \ + (without this, the "magic numbers" that identify classes might be \ + encountered by accident or we might address parts of the Object which \ + don't exist). */ \ + if ( astIsA##parent( this ) ) { \ +\ +/* Obtain the Object's size and check it is adequate for an object of the \ + proposed type (this avoids any attempt to access derived class data that \ + doesn't exist and therefore lies outside the memory allocated for the \ + object). */ \ + if ( ( (AstObject *) this )->size >= sizeof( Ast##class ) ) { \ +\ +/* If OK, see whether the check component in the object's virtual function \ + table matches the expected "magic" value. */ \ + isa = ( *astMEMBER(this,class,id.check) == &class_check ); \ + } \ + } \ +\ +/* Return the result. */ \ + return isa; \ +} +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_SET + +* Purpose: +* Implement a method to set an attribute value for a class. + +* Type: +* Protected macro. + +* Synopsis: +* #include "object.h" +* astMAKE_SET(class,attribute,type,component,assign) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( Ast *this, value ) +* +* and an external interface function of the form: +* +* void astSet_( Ast *this, value ) +* +* which implement a method for setting a specified attribute value for a +* class. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute to be set, as it appears in the function +* name (e.g. Label in "astSetLabel"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. + +* Examples: +* astMAKE_SET(MyStuff,Flag,int,flag,( value != 0 )) +* Implements the astSetFlag method for the MyStuff class which operates +* by setting the "flag" structure component to 0 or 1 depending on +* whether the "value" parameter is non-zero or not. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define astMAKE_SET(class,attribute,type,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attribute( Ast##class *this, type value, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Store the new value in the structure component. */ \ + this->component = (assign); \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attribute##_( Ast##class *this, type value, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,class,Set##attribute))( this, value, status ); \ +} +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_SET1 + +* Purpose: +* Implement a method to set an attribute value for a class, reporting +* an error if the object has more than one reference. + +* Type: +* Protected macro. + +* Synopsis: +* #include "object.h" +* astMAKE_SET1(class,attribute,type,component,assign) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( Ast *this, value ) +* +* and an external interface function of the form: +* +* void astSet_( Ast *this, value ) +* +* which implement a method for setting a specified attribute value for a +* class. An error is reported if the object has a reference count that +* is greater than one. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute to be set, as it appears in the function +* name (e.g. Label in "astSetLabel"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. + +*- +*/ + +/* Define the macro. */ +#define astMAKE_SET1(class,attribute,type,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attribute( Ast##class *this, type value, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Report an error if the object has been cloned (i.e. has a reference \ + count that is greater than one). */ \ + if( astGetRefCount( this ) > 1 ) { \ + astError( AST__IMMUT, "astSet(%s): The " #attribute "attribute of " \ + "the supplied %s cannot be changed because the %s has " \ + "been cloned (programming error).", status, \ + astGetClass(this), astGetClass(this), astGetClass(this) ); \ +\ +/* Otherwise, store the new value in the structure component. */ \ + } else { \ + this->component = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attribute##_( Ast##class *this, type value, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,class,Set##attribute))( this, value, status ); \ +} +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMAKE_TEST + +* Purpose: +* Implement a method to test if an attribute has been set for a class. + +* Type: +* Protected macro. + +* Synopsis: +* #include "object.h" +* astMAKE_TEST(class,attribute,assign) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( Ast *this ) +* +* and an external interface function of the form: +* +* int astTest_( Ast *this ) +* +* which implement a method for testing if a specified attribute has been +* set for a class. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attribute +* The name of the attribute to be tested, as it appears in the function +* name (e.g. Label in "astTestLabel"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. + +* Examples: +* astMAKE_TEST(MyStuff,Flag,( this->flag != -1 )) +* Implements the astTestFlag method for the MyStuff class which operates +* by testing the "flag" structure component to see if it is set to a +* value other than -1. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define astMAKE_TEST(class,attribute,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attribute( Ast##class *this, int *status ) { \ + int result; /* Value to return */ \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Assign the result value. */ \ + result = (assign); \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attribute##_( Ast##class *this, int *status ) { \ +\ +/* Check the inherited error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,class,Test##attribute))( this, status ); \ +} +#endif + +#if defined(astCLASS) /* Protected */ +/* +*+ +* Name: +* astMEMBER + +* Purpose: +* Locate a member function. + +* Type: +* Protected macro. + +* Synopsis: +* #include "object.h" +* astMEMBER(this,class,method) + +* Class Membership: +* Defined by the Object class. + +* Description: +* This macro evaluates to the address where the pointer to a +* specified Object member function is stored. Typically, this will +* be used to obtain a pointer to the member function so that it +* can be invoked. It may also be used to assign a new function +* pointer so that a derived class can re-define a virtual function +* and hence over-ride an inherited method. + +* Parameters: +* this +* Pointer to an Object belonging to the class for which the +* virtual function is required. This must either be the class +* that originally defined the method, or one derived from it. +* class +* Name of the class that originally defined the method. This +* may differ from (i.e. be an ancestor of) the class to which +* "this" belongs. +* method +* Name of the method whose member function is to be located. + +* Returned Value: +* The address where the member function pointer is stored (the +* type of the result is determined by the type of the member +* function itself). + +* Examples: +* astMEMBER(this,Gnome,astFish) +* Returns the address where the pointer to the function that +* implements the astFish method for the "this" object is +* stored. The Gnome class should be where the astFish method +* was first defined (i.e. from where it was inherited by +* "this"). +* (**astMEMBER(this,Gnome,astFish))( this, arg1, arg2 ); +* Invokes the virtual function that implements the astFish +* method for object "this" and passes it additional arguments +* "arg2" and "arg2". Again, Gnome should be the class that +* originally defined the astFish method. +* *astMEMBER(this,Gnome,astFish) = myFish; +* Stores a pointer to the myFish function so that it replaces +* the virtual function that previously implemented the astFish +* method for the "this" object. Note that all objects in the +* same class as "this" are affected, but objects in class +* "class" are not affected (unless it happens to be the class +* to which "this" belongs). + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*- +*/ + +/* A subsiduary macro that returns a pointer to the vtab of an object, + cast to an AstObjectVtab. */ +#define astVTAB(this) (((AstObject*)(this))->vtab) + +/* Define the macro. This functions by (a) casting the Object pointer + to type (AstObject *) and locating the Object's virtual function + table (b) casting the table pointer to the correct type + (AstClassVtab *) for the class in which the method pointer resides, + (c) locating the component holding the member function pointer, and + (d) taking its address. */ +#define astMEMBER(this,class,method) \ +(&((Ast##class##Vtab*)astVTAB(this))->method) + +#endif + +/* +*+ +* Name: +* astPROTO_CHECK +* astPROTO_ISA + +* Type: +* Protected macros. + +* Purpose: +* Prototype the astCheck_ and astIsA_ functions. + +* Synopsis: +* #include "object.h" +* astPROTO_CHECK(class) +* astPROTO_ISA(class) + +* Class Membership: +* Defined by the Object class. + +* Description: +* These macros expands to function prototypes for the +* astCheck_ and astIsA_ functions (q.v.) which +* validate and test for membership of a specified class. + +* Parameters: +* class +* The name (not the type) of the class whose membership is to +* be validated. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*- +*/ + +/* Define the macros. */ +#define astPROTO_CHECK(class) Ast##class *astCheck##class##_( Ast##class *, int * ); +#define astPROTO_ISA(class) int astIsA##class##_( const Ast##class *, int * ); + +/* Macros which return the maximum and minimum of two values. */ +#define astMAX(aa,bb) ((aa)>(bb)?(aa):(bb)) +#define astMIN(aa,bb) ((aa)<(bb)?(aa):(bb)) + +/* Check for equality of floating point values. We cannot compare bad values + directly because of the danger of floating point exceptions, so bad + values are dealt with explicitly. */ +#define astEQUALS(aa,bb,tol) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=(tol)*astMAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN)))) +#define astEQUAL(aa,bb) astEQUALS(aa,bb,1.0E5) + + +/* AST__NULL. */ +/* ---------- */ +/* Define the AST__NULL macro, which evaluates to a null Object + pointer. */ +#define AST__NULL (astI2P(0)) + + +#if defined(astCLASS) /* Protected */ + +/* Test the validy of an attribute value */ +/* ------------------------------------- */ +/* If the set attribute value is invalid, clear it. These macros should + be used in a context in which error reporting has been deferred by + calling astReporting( 0 ). */ + +#define astCLEAN_ATTRIB(attr) \ + if( astTest##attr(this) ) { \ + astSet##attr( this, astGet##attr( this ) ); \ + if( !astOK ) { \ + astClearStatus; \ + astClear##attr( this ); \ + } \ + } + +#define astCLEAN_INDEXED_ATTRIB(attr,index) \ + if( astTest##attr(this,index) ) { \ + astSet##attr( this, index, astGet##attr( this, index ) ); \ + if( !astOK ) { \ + astClearStatus; \ + astClear##attr( this, index ); \ + } \ + } + +#endif + + +#if defined(astCLASS) /* Protected */ +#define astSetVtabClassIdentifier(vtab,id_ptr) \ + ((AstObjectVtab *)(vtab))->top_id = (id_ptr) +#endif + +/* Type Definitions. */ +/* ================= */ + +/* Object structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstObject { + +/* Attributes specific to objects in this class. */ + unsigned long check; /* Check value to identify Objects */ + size_t size; /* Amount of memory used by Object */ + struct AstObjectVtab *vtab; /* Pointer to virtual function table */ + char dynamic; /* Memory allocated dynamically? */ + int ref_count; /* Number of active pointers to the Object */ + char *id; /* Pointer to ID string */ + char *ident; /* Pointer to Ident string */ + char usedefs; /* Use default attribute values? */ + int iref; /* Object index (unique within class) */ + void *proxy; /* A pointer to an external object that + acts as a foreign language proxy for the + AST object */ +#if defined(THREAD_SAFE) + int locker; /* Thread that has locked this Object */ + pthread_mutex_t mutex1; /* Guards access to all elements of the + Object except for the "locker" and + "ref_count" components */ + pthread_mutex_t mutex2; /* Guards access to the "locker" and + "ref_count" components */ + struct AstGlobals *globals; /* Pointer to thread-specific global data */ +#endif + +} AstObject; + +/* Class identifier structure */ +typedef struct AstClassIdentifier { + int *check; + struct AstClassIdentifier *parent; +} AstClassIdentifier; + +/* Virtual function table. */ +/* ----------------------- */ +/* The virtual function table makes a forward reference to the + AstChannel structure which is not defined until "channel.h" is + included (below). Hence make a preliminary definition available + now. */ +struct AstChannel; + +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstObjectVtab { + +/* A unique identifier for this class. */ + AstClassIdentifier id; + +/* Pointer to the structure that identifies the top-level class described + by the whole vtab (of which the AstObjectVtab is just the first, + lowest-level, component). */ + AstClassIdentifier *top_id; + +/* Pointer to a dynamically allocated string holding the default + attribute values to use when creating new objects. These are read from + environment variables of the form "_OPTIONS". */ + const char *defaults; + +/* Properties specific to this class. */ + void ( *CleanAttribs )( AstObject *, int * ); + AstObject *( *Cast )( AstObject *, AstObject *, int * ); + const char *( *GetID )( AstObject *, int * ); + const char *( *GetIdent )( AstObject *, int * ); + const char *(* GetAttrib)( AstObject *, const char *, int * ); + int (* TestAttrib)( AstObject *, const char *, int * ); + int (* TestID)( AstObject *, int * ); + int (* Same)( AstObject *, AstObject *, int * ); + int (* HasAttribute)( AstObject *, const char *, int * ); + int (* TestIdent)( AstObject *, int * ); + void (* Clear)( AstObject *, const char *, int * ); + void (* ClearAttrib)( AstObject *, const char *, int * ); + void (* ClearID)( AstObject *, int * ); + void (* ClearIdent)( AstObject *, int * ); + void (* Dump)( AstObject *, struct AstChannel *, int * ); + int (* Equal)( AstObject *, AstObject *, int * ); + void (* SetAttrib)( AstObject *, const char *, int * ); + void (* SetID)( AstObject *, const char *, int * ); + void (* SetIdent)( AstObject *, const char *, int * ); + void (* Show)( AstObject *, int * ); + void (* VSet)( AstObject *, const char *, char **, va_list, int * ); + void (* EnvSet)( AstObject *, int * ); + + void *(* GetProxy)( AstObject *, int * ); + void (* SetProxy)( AstObject *, void *, int * ); + + int (* GetObjSize)( AstObject *, int * ); + + int (* TestUseDefs)( AstObject *, int * ); + int (* GetUseDefs)( AstObject *, int * ); + void (* SetUseDefs)( AstObject *, int, int * ); + void (* ClearUseDefs)( AstObject *, int * ); + + const char *class; /* Pointer to class name string */ + void (** delete)( AstObject *, int * ); /* Pointer to array of destructors */ + void (** copy)( const AstObject *, AstObject *, int * ); /* Copy constructors */ + void (** dump)( AstObject *, struct AstChannel *, int * ); /* Dump functions */ + const char **dump_class; /* Dump function class string pointers */ + const char **dump_comment; /* Dump function comment string pointers */ + int ndelete; /* Number of destructors */ + int ncopy; /* Number of copy constructors */ + int ndump; /* Number of dump functions */ + int nobject; /* Number of active objects in the class */ + int nfree; /* No. of entries in "free_list" */ + AstObject **free_list; /* List of pointers for freed Objects */ + +#if defined(THREAD_SAFE) + int (* ManageLock)( AstObject *, int, int, AstObject **, int * ); +#endif + +} AstObjectVtab; +#endif + +#if defined(THREAD_SAFE) && defined(astCLASS) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstObjectGlobals { + AstObjectVtab Class_Vtab; + int Class_Init; + int Retain_Esc; + int Context_Level; + int *Active_Handles; + char GetAttrib_Buff[ AST__GETATTRIB_BUFF_LEN + 1 ]; + char *AstGetC_Strings[ AST__ASTGETC_MAX_STRINGS ]; + int AstGetC_Istr; + int AstGetC_Init; + int Nvtab; + AstObjectVtab **Known_Vtabs; +} AstObjectGlobals; + +#endif + +/* More include files. */ +/* =================== */ +/* The interface to the Channel class must be included here (after the + type definitions for the Object class) because "channel.h" itself + includes this file ("object.h"), although "object.h" refers to the + AstChannel structure above. This seems a little strange at first, + but is simply analogous to making a forward reference to a + structure type when recursively defining a normal C structure + (except that here the definitions happen to be in separate include + files). */ +#include "channel.h" + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Object) /* Validate class membership */ +astPROTO_ISA(Object) /* Test class membership */ + +/* NB. There is no constructor function for this class. */ + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstObject *astInitObject_( void *, size_t, int, AstObjectVtab *, + const char *, int * ); + +/* Vtab Initialiser. */ +void astInitObjectVtab_( AstObjectVtab *, const char *, int * ); + +/* Loader. */ +AstObject *astLoadObject_( void *, size_t, AstObjectVtab *, + const char *, AstChannel *channel, int * ); + +#if defined(THREAD_SAFE) +void astInitObjectGlobals_( AstObjectGlobals * ); +#endif + +#endif + +/* Prototypes for other class functions. */ +/* ------------------------------------- */ +#if !defined(astCLASS) /* Public */ +void astBegin_( void ); +void astEnd_( int * ); +#endif + +AstObject *astI2P_( int, int * ); +AstObject *astMakeId_( AstObject *, int * ); +AstObject *astMakePointer_( AstObject *, int * ); +AstObject *astMakePointer_NoLockCheck_( AstObject *, int * ); +int astP2I_( AstObject *, int * ); +int astVersion_( int * ); +int astEscapes_( int, int * ); +int astTune_( const char *, int, int * ); +void astTuneC_( const char *, const char *, char *, int, int * ); + +/* Prototypes for member functions. */ +/* -------------------------------- */ +#if defined(astCLASS) /* Protected */ +AstObject *astAnnul_( AstObject *, int * ); +AstObject *astDelete_( AstObject *, int * ); +void astSet_( void *, const char *, int *, ... ); + +#else /* Public */ +AstObject *astDeleteId_( AstObject *, int * ); +int astThreadId_( AstObject *, int, int * ); +void astExportId_( AstObject *, int * ); +void astImportId_( AstObject *, int * ); +void astSetId_( void *, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +AstObject *astAnnulId_( AstObject *, int * ); +AstObject *astCheckLock_( AstObject *, int * ); +AstObject *astClone_( AstObject *, int * ); +AstObject *astCopy_( const AstObject *, int * ); +AstObject *astFromString_( const char *, int * ); +char *astToString_( AstObject *, int * ); +const char *astGetC_( AstObject *, const char *, int * ); +double astGetD_( AstObject *, const char *, int * ); +float astGetF_( AstObject *, const char *, int * ); +int astEqual_( AstObject *, AstObject *, int * ); +int astGetI_( AstObject *, const char *, int * ); +int astHasAttribute_( AstObject *, const char *, int * ); +int astSame_( AstObject *, AstObject *, int * ); +int astTest_( AstObject *, const char *, int * ); +long astGetL_( AstObject *, const char *, int * ); +void *astGetProxy_( AstObject *, int * ); +void astClear_( AstObject *, const char *, int * ); +void astExemptId_( AstObject *, int * ); +void astLockId_( AstObject *, int, int * ); +void astSetC_( AstObject *, const char *, const char *, int * ); +void astSetD_( AstObject *, const char *, double, int * ); +void astSetF_( AstObject *, const char *, float, int * ); +void astSetI_( AstObject *, const char *, int, int * ); +void astSetL_( AstObject *, const char *, long, int * ); +void astSetProxy_( AstObject *, void *, int * ); +void astShow_( AstObject *, int * ); +void astUnlockId_( AstObject *, int, int * ); + +#if defined(astCLASS) /* Protected */ + +void astCleanAttribs_( AstObject *, int * ); +AstObject *astCast_( AstObject *, AstObject *, int * ); +AstObject *astCastCopy_( AstObject *, AstObject *, int * ); + +#if defined(THREAD_SAFE) +int astManageLock_( AstObject *, int, int, AstObject **, int * ); +#endif + +int astGetObjSize_( AstObject *, int * ); + +int astTestUseDefs_( AstObject *, int * ); +int astGetUseDefs_( AstObject *, int * ); +void astSetUseDefs_( AstObject *, int, int * ); +void astClearUseDefs_( AstObject *, int * ); + +const char *astGetAttrib_( AstObject *, const char *, int * ); +const char *astGetClass_( const AstObject *, int * ); +const char *astGetID_( AstObject *, int * ); +const char *astGetIdent_( AstObject *, int * ); +int astClassCompare_( AstObjectVtab *, AstObjectVtab *, int * ); +int astGetNobject_( const AstObject *, int * ); +int astGetRefCount_( AstObject *, int * ); +int astTestAttrib_( AstObject *, const char *, int * ); +int astTestID_( AstObject *, int * ); +int astTestIdent_( AstObject *, int * ); +void astClearAttrib_( AstObject *, const char *, int * ); +void astClearID_( AstObject *, int * ); +void astClearIdent_( AstObject *, int * ); +void astDump_( AstObject *, AstChannel *, int * ); +void astSetAttrib_( AstObject *, const char *, int * ); +void astSetCopy_( AstObjectVtab *, void (*)( const AstObject *, AstObject *, int * ), int * ); +void astSetDelete_( AstObjectVtab *, void (*)( AstObject *, int * ), int * ); +void astSetDump_( AstObjectVtab *, void (*)( AstObject *, AstChannel *, int * ), const char *, const char *, int * ); +void astSetVtab_( AstObject *, AstObjectVtab *, int * ); +void astSetID_( AstObject *, const char *, int * ); +void astSetIdent_( AstObject *, const char *, int * ); +void astEnvSet_( AstObject *, int * ); +void astVSet_( AstObject *, const char *, char **, va_list, int * ); + +#endif + + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Check class membership. */ +#define astCheckObject(this) astINVOKE_CHECK(Object,this,0) +#define astVerifyObject(this) astINVOKE_CHECK(Object,this,1) + +/* Test class membership. */ +#define astIsAObject(this) astINVOKE_ISA(Object,this) + +/* NB. There is no constructor function for this class. */ + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitObject(mem,size,init,vtab,name) \ +astINVOKE(O,astInitObject_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitObjectVtab(vtab,name) astINVOKE(V,astInitObjectVtab_(vtab,name,STATUS_PTR)) + +/* Loader. */ +#define astLoadObject(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadObject_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to other class functions. */ +/* ------------------------------------ */ +#if !defined(astCLASS) /* Public */ +#define astBegin astBegin_() +#define astEnd astINVOKE(V,astEnd_(STATUS_PTR)) +#else /* Protected */ +#define astMakePointer_NoLockCheck(id) ((void *)astMakePointer_NoLockCheck_((AstObject *)(id),STATUS_PTR)) +#endif + +#define astVersion astVersion_(STATUS_PTR) +#define astEscapes(int) astEscapes_(int,STATUS_PTR) +#define astTune(name,val) astTune_(name,val,STATUS_PTR) +#define astTuneC(name,value,buff,bufflen) astTuneC_(name,value,buff,bufflen,STATUS_PTR) +#define astI2P(integer) ((void *)astI2P_(integer,STATUS_PTR)) +#define astMakeId(pointer) ((void *)astMakeId_((AstObject *)(pointer),STATUS_PTR)) +#define astP2I(pointer) astP2I_((AstObject *)(pointer),STATUS_PTR) +#define astMakePointer(id) ((void *)astCheckLock_(astMakePointer_((AstObject *)(id),STATUS_PTR),STATUS_PTR)) +#define astToString(this) astINVOKE(V,astToString_(astCheckObject(this),STATUS_PTR)) +#define astFromString(string) astINVOKE(O,astFromString_(string,STATUS_PTR)) + +/* Interfaces to member functions. */ +/* ------------------------------- */ +/* Here we make use of astCheckObject (et al.) to validate Object + pointers before use. This provides a contextual error report if a + pointer to the wrong sort of object is supplied. In the case of an + external caller, it also performs the required conversion from an + Object identifier to a true C pointer. */ + +/* These functions require special treatment for external use because + they handle Object identifiers and their resources explicitly, and + must therefore be passed identifier values without conversion to C + pointers. */ + +#if defined(astCLASS) || defined(astFORTRAN77) /* Protected or Fotran interface */ +#define astAnnulId(this) astINVOKE(O,astAnnulId_((AstObject *)(this),STATUS_PTR)) +#endif + +#if defined(astCLASS) /* Protected only */ +#define astAnnul(this) astINVOKE(O,astAnnul_(astCheckObject(this),STATUS_PTR)) +#define astDelete(this) astINVOKE(O,astDelete_(astCheckObject(this),STATUS_PTR)) +#define astSet astINVOKE(F,astSet_) + +#else /* Public only */ +#define astAnnul(this) astINVOKE(O,astAnnulId_((AstObject *)(this),STATUS_PTR)) +#define astDelete(this) astINVOKE(O,astDeleteId_((AstObject *)(this),STATUS_PTR)) +#define astExport(this) astINVOKE(V,astExportId_((AstObject *)(this),STATUS_PTR)) +#define astImport(this) astINVOKE(V,astImportId_((AstObject *)(this),STATUS_PTR)) +#define astSet astINVOKE(F,astSetId_) +#define astThread(this,ptr) astINVOKE(V,astThreadId_((AstObject *)(this),ptr,STATUS_PTR)) +#endif + +/* Both.... */ +#define astLock(this,wait) astINVOKE(V,astLockId_((AstObject *)(this),wait,STATUS_PTR)) +#define astUnlock(this,report) astINVOKE(V,astUnlockId_((AstObject *)(this),report,STATUS_PTR)) +#define astEqual(this,that) astINVOKE(V,(((AstObject*)this==(AstObject*)that)||astEqual_(astCheckObject(this),astCheckObject(that),STATUS_PTR))) +#define astExempt(this) astINVOKE(V,astExemptId_((AstObject *)(this),STATUS_PTR)) +#define astClear(this,attrib) astINVOKE(V,astClear_(astCheckObject(this),attrib,STATUS_PTR)) +#define astClone(this) astINVOKE(O,astClone_(astCheckObject(this),STATUS_PTR)) +#define astCopy(this) astINVOKE(O,astCopy_(astCheckObject(this),STATUS_PTR)) +#define astGetC(this,attrib) astINVOKE(V,astGetC_(astCheckObject(this),attrib,STATUS_PTR)) +#define astGetD(this,attrib) astINVOKE(V,astGetD_(astCheckObject(this),attrib,STATUS_PTR)) +#define astGetF(this,attrib) astINVOKE(V,astGetF_(astCheckObject(this),attrib,STATUS_PTR)) +#define astGetI(this,attrib) \ +astINVOKE(V,astGetI_(astCheckObject(this),attrib,STATUS_PTR)) +#define astGetL(this,attrib) \ +astINVOKE(V,astGetL_(astCheckObject(this),attrib,STATUS_PTR)) +#define astSetC(this,attrib,value) \ +astINVOKE(V,astSetC_(astCheckObject(this),attrib,value,STATUS_PTR)) +#define astSetD(this,attrib,value) \ +astINVOKE(V,astSetD_(astCheckObject(this),attrib,value,STATUS_PTR)) +#define astSetF(this,attrib,value) \ +astINVOKE(V,astSetF_(astCheckObject(this),attrib,value,STATUS_PTR)) +#define astSetI(this,attrib,value) \ +astINVOKE(V,astSetI_(astCheckObject(this),attrib,value,STATUS_PTR)) +#define astSetL(this,attrib,value) \ +astINVOKE(V,astSetL_(astCheckObject(this),attrib,value,STATUS_PTR)) +#define astShow(this) \ +astINVOKE(V,astShow_(astCheckObject(this),STATUS_PTR)) +#define astTest(this,attrib) \ +astINVOKE(V,astTest_(astCheckObject(this),attrib,STATUS_PTR)) +#define astSame(this,that) \ +astINVOKE(V,astSame_(astCheckObject(this),astCheckObject(that),STATUS_PTR)) +#define astHasAttribute(this,attrib) \ +astINVOKE(V,astHasAttribute_(astCheckObject(this),attrib,STATUS_PTR)) +#define astGetProxy(this) \ +astINVOKE(V,astGetProxy_(astCheckObject(this),STATUS_PTR)) +#define astSetProxy(this,proxy) \ +astINVOKE(V,astSetProxy_(astCheckObject(this),proxy,STATUS_PTR)) + + +#if defined(astCLASS) /* Protected */ + +#if defined(THREAD_SAFE) +#define astManageLock(this,mode,extra,fail) \ +astINVOKE(V,astManageLock_(astCheckObject(this),mode, extra,fail,STATUS_PTR)) +#else +#define astManageLock(this,mode,extra,fail) +#endif + +#define astCleanAttribs(this) astINVOKE(V,astCleanAttribs_(astCheckObject(this),STATUS_PTR)) +#define astGetObjSize(this) astINVOKE(V,astGetObjSize_(astCheckObject(this),STATUS_PTR)) +#define astCast(this,obj) astINVOKE(O,astCast_(astCheckObject(this),astCheckObject(obj),STATUS_PTR)) +#define astCastCopy(this,obj) astCastCopy_((AstObject*)this,(AstObject*)obj,STATUS_PTR) + +#define astClearUseDefs(this) astINVOKE(V,astClearUseDefs_(astCheckObject(this),STATUS_PTR)) +#define astTestUseDefs(this) astINVOKE(V,astTestUseDefs_(astCheckObject(this),STATUS_PTR)) +#define astGetUseDefs(this) astINVOKE(V,astGetUseDefs_(astCheckObject(this),STATUS_PTR)) +#define astSetUseDefs(this,val) astINVOKE(V,astSetUseDefs_(astCheckObject(this),val,STATUS_PTR)) + +#define astClearAttrib(this,attrib) \ +astINVOKE(V,astClearAttrib_(astCheckObject(this),attrib,STATUS_PTR)) +#define astClearID(this) astINVOKE(V,astClearID_(astCheckObject(this),STATUS_PTR)) +#define astClearIdent(this) astINVOKE(V,astClearIdent_(astCheckObject(this),STATUS_PTR)) +#define astDump(this,channel) \ +astINVOKE(V,astDump_(astCheckObject(this),astCheckChannel(channel),STATUS_PTR)) + +#define astGetAttrib(this,attrib) \ +astINVOKE(V,astGetAttrib_(astCheckObject(this),attrib,STATUS_PTR)) +#define astGetClass(this) astINVOKE(V,astGetClass_((const AstObject *)(this),STATUS_PTR)) +#define astGetID(this) astINVOKE(V,astGetID_(astCheckObject(this),STATUS_PTR)) +#define astGetIdent(this) astINVOKE(V,astGetIdent_(astCheckObject(this),STATUS_PTR)) +#define astGetNobject(this) astINVOKE(V,astGetNobject_(astCheckObject(this),STATUS_PTR)) +#define astClassCompare(class1,class2) astClassCompare_(class1,class2,STATUS_PTR) +#define astGetRefCount(this) astINVOKE(V,astGetRefCount_(astCheckObject(this),STATUS_PTR)) +#define astSetAttrib(this,setting) \ +astINVOKE(V,astSetAttrib_(astCheckObject(this),setting,STATUS_PTR)) +#define astSetCopy(vtab,copy) \ +astINVOKE(V,astSetCopy_((AstObjectVtab *)(vtab),copy,STATUS_PTR)) +#define astSetDelete(vtab,delete) \ +astINVOKE(V,astSetDelete_((AstObjectVtab *)(vtab),delete,STATUS_PTR)) +#define astSetDump(vtab,dump,class,comment) \ +astINVOKE(V,astSetDump_((AstObjectVtab *)(vtab),dump,class,comment,STATUS_PTR)) +#define astSetVtab(object,vtab) \ +astINVOKE(V,astSetVtab_((AstObject *)object,(AstObjectVtab *)(vtab),STATUS_PTR)) +#define astSetID(this,id) astINVOKE(V,astSetID_(astCheckObject(this),id,STATUS_PTR)) +#define astSetIdent(this,id) astINVOKE(V,astSetIdent_(astCheckObject(this),id,STATUS_PTR)) +#define astVSet(this,settings,text,args) \ +astINVOKE(V,astVSet_(astCheckObject(this),settings,text,args,STATUS_PTR)) +#define astEnvSet(this) \ +astINVOKE(V,astEnvSet_(astCheckObject(this),STATUS_PTR)) +#define astTestAttrib(this,attrib) \ +astINVOKE(V,astTestAttrib_(astCheckObject(this),attrib,STATUS_PTR)) +#define astTestID(this) astINVOKE(V,astTestID_(astCheckObject(this),STATUS_PTR)) +#define astTestIdent(this) astINVOKE(V,astTestIdent_(astCheckObject(this),STATUS_PTR)) + +/* Deprecated synonym. */ +#define astClass(this) astGetClass(this) +#endif + +/* Extra stuff for debuging probnlems with object handles and memory usage */ +#ifdef MEM_DEBUG + +void astWatchHandle_( int ); +void astHandleUse_( int, const char *, ... ); +void astHandleAlarm_( const char *, va_list ); + +#define astWatchHandle astWatchHandle_ +#define astHandleUse astHandleUse_ +#define astHandleAlarm astHandleAlarm_ + +#else + +#define astWatchHandle +#define astHandleUse +#define astHandleAlarm + +#endif + +#endif + diff --git a/overgrid.pdf b/overgrid.pdf new file mode 100644 index 0000000..21922e8 Binary files /dev/null and b/overgrid.pdf differ diff --git a/overgrid_bw.pdf b/overgrid_bw.pdf new file mode 100644 index 0000000..457b75e Binary files /dev/null and b/overgrid_bw.pdf differ diff --git a/pal.h b/pal.h new file mode 100644 index 0000000..a007b6f --- /dev/null +++ b/pal.h @@ -0,0 +1,578 @@ +#ifndef PALHDEF +#define PALHDEF + +/* +*+ +* Name: +* pal.h + +* Purpose: +* Function prototypes for PAL routines. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Include file + +* Description: +* Function prototypes for PAL routines. + +* Authors: +* TIMJ: Tim Jenness (JAC, Hawaii) +* DSBJ: David S Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-08 (TIMJ): +* Initial version. Define all SLA prototypes in PAL form even +* though none are implemented. +* 16-FEB-2012 (DSB): +* If the PAL and ERFA libraries distributed within AST are being +* used, rename the public symbols defined by both to avoid clashes +* with any external PAL or ERFA library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 3 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +* USA. + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +/* Include configuration results in order to get any definition for the + EXTERNAL_PAL macro. This macro is set if the --with-external_pal + option was set when AST was configured. */ +#if HAVE_CONFIG_H +#include +#endif + +/* If we not using external PAL and ERFA libraries, rename all PAL + functions so that references to "palXxx" below get translated to + "astPalXxx". */ +#ifndef EXTERNAL_PAL +#include "pal2ast.h" +#endif + +/* Prototypes for all PAL functions. Note, these should be copied + verbatim from the pal.h file included in the pal library. We cannot + simply "include pal./pal.h" here as the macros established in + pal2ast.h above would then not be used to replace the names of the pal + function. */ + +/* ------------- start of pal/pal.h ------------------------------ */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void palAddet ( double rm, double dm, double eq, double *rc, double *dc ); + +void palAfin ( const char *string, int *iptr, float *a, int *j ); + +double palAirmas ( double zd ); + +void palAltaz ( double ha, double dec, double phi, + double *az, double *azd, double *azdd, + double *el, double *eld, double *eldd, + double *pa, double *pad, double *padd ); + +void palAmp ( double ra, double da, double date, double eq, + double *rm, double *dm ); + +void palAmpqk ( double ra, double da, double amprms[21], + double *rm, double *dm ); + +void palAop ( double rap, double dap, double date, double dut, + double elongm, double phim, double hm, double xp, + double yp, double tdk, double pmb, double rh, + double wl, double tlr, + double *aob, double *zob, double *hob, + double *dob, double *rob ); + +void palAoppa ( double date, double dut, double elongm, double phim, + double hm, double xp, double yp, double tdk, double pmb, + double rh, double wl, double tlr, double aoprms[14] ); + +void palAoppat ( double date, double aoprms[14] ); + +void palAopqk ( double rap, double dap, double aoprms[14], + double *aob, double *zob, double *hob, + double *dob, double *rob ); + +void palAtmdsp ( double tdk, double pmb, double rh, double wl1, + double a1, double b1, double wl2, double *a2, double *b2 ); + +void palAv2m ( float axvec[3], float rmat[3][3] ); + +float palBear ( float a1, float b1, float a2, float b2 ); + +void palCaf2r ( int ideg, int iamin, float asec, float *rad, int *j ); + +void palCaldj ( int iy, int im, int id, double *djm, int *j ); + +void palCalyd ( int iy, int im, int id, int *ny, int *nd, int *j ); + +void palCc2s ( float v[3], float *a, float *b ); + +void palCc62s ( float v[6], float *a, float *b, float *r, + float *ad, float *bd, float *rd ); + +void palCd2tf ( int ndp, float days, char *sign, int ihmsf[4] ); + +void palCldj ( int iy, int im, int id, double *djm, int *j ); + +void palClyd ( int iy, int im, int id, int *ny, int *nd, int *jstat ); + +void palCombn ( int nsel, int ncand, int list[], int *j ); + +void palCr2af ( int ndp, float angle, char *sign, int idmsf[4] ); + +void palCr2tf ( int ndp, float angle, char *sign, int ihmsf[4] ); + +void palCs2c ( float a, float b, float v[3] ); + +void palCs2c6 ( float a, float b, float r, float ad, + float bd, float rd, float v[6] ); + +void palCtf2d ( int ihour, int imin, float sec, float *days, int *j ); + +void palCtf2r ( int ihour, int imin, float sec, float *rad, int *j ); + +void palDaf2r ( int ideg, int iamin, double asec, double *rad, int *j ); + +void palDafin ( const char *string, int *iptr, double *a, int *j ); + +double palDat ( double dju ); + +void palDav2m ( double axvec[3], double rmat[3][3] ); + +double palDbear ( double a1, double b1, double a2, double b2 ); + +void palDbjin ( const char *string, int *nstrt, + double *dreslt, int *jf1, int *jf2 ); + +void palDc62s ( double v[6], double *a, double *b, double *r, + double *ad, double *bd, double *rd ); + +void palDcc2s ( double v[3], double *a, double *b ); + +void palDcmpf ( double coeffs[6], double *xz, double *yz, double *xs, + double *ys, double *perp, double *orient ); + +void palDcs2c ( double a, double b, double v[3] ); + +void palDd2tf ( int ndp, double days, char *sign, int ihmsf[4] ); + +void palDe2h ( double ha, double dec, double phi, + double *az, double *el ); + +void palDeuler ( const char *order, double phi, double theta, double psi, + double rmat[3][3] ); + +void palDfltin ( const char *string, int *nstrt, double *dreslt, int *jflag ); + +void palDh2e ( double az, double el, double phi, double *ha, double *dec); + +void palDimxv ( double dm[3][3], double va[3], double vb[3] ); + +void palDjcal ( int ndp, double djm, int iymdf[4], int *j ); + +void palDjcl ( double djm, int *iy, int *im, int *id, double *fd, int *j ); + +void palDm2av ( double rmat[3][3], double axvec[3] ); + +void palDmat ( int n, double *a, double *y, double *d, int *jf, int *iw ); + +void palDmoon ( double date, double pv[6] ); + +void palDmxm ( double a[3][3], double b[3][3], double c[3][3] ); + +void palDmxv ( double dm[3][3], double va[3], double vb[3] ); + +double palDpav ( double v1[3], double v2[3] ); + +void palDr2af ( int ndp, double angle, char *sign, int idmsf[4] ); + +void palDr2tf ( int ndp, double angle, char *sign, int ihmsf[4] ); + +double palDrange ( double angle ); + +double palDranrm ( double angle ); + +void palDs2c6 ( double a, double b, double r, double ad, double bd, + double rd, double v[6] ); + +void palDs2tp ( double ra, double dec, double raz, double decz, + double *xi, double *eta, int *j ); + +double palDsep ( double a1, double b1, double a2, double b2 ); + +double palDsepv ( double v1[3], double v2[3] ); + +double palDt ( double epoch ); + +void palDtf2d ( int ihour, int imin, double sec, double *days, int *j ); + +void palDtf2r ( int ihour, int imin, double sec, double *rad, int *j ); + +void palDtp2s ( double xi, double eta, double raz, double decz, + double *ra, double *dec ); + +void palDtp2v ( double xi, double eta, double v0[3], double v[3] ); + +void palDtps2c ( double xi, double eta, double ra, double dec, + double *raz1, double *decz1, + double *raz2, double *decz2, int *n ); + +void palDtpv2c ( double xi, double eta, double v[3], + double v01[3], double v02[3], int *n ); + +double palDtt ( double dju ); + +void palDv2tp ( double v[3], double v0[3], double *xi, double *eta, int *j ); + +double palDvdv ( double va[3], double vb[3] ); + +void palDvn ( double v[3], double uv[3], double *vm ); + +void palDvxv ( double va[3], double vb[3], double vc[3] ); + +void palE2h ( float ha, float dec, float phi, float *az, float *el ); + +void palEarth ( int iy, int id, float fd, float posvel[6] ); + +void palEcleq ( double dl, double db, double date, double *dr, double *dd ); + +void palEcmat ( double date, double rmat[3][3] ); + +void palEcor ( float rm, float dm, int iy, int id, float fd, + float *rv, float *tl ); + +void palEg50 ( double dr, double dd, double *dl, double *db ); + +void palEl2ue ( double date, int jform, double epoch, double orbinc, + double anode, double perih, double aorq, double e, + double aorl, double dm, double u[13], int *jstat ); + +double palEpb ( double date ); + +double palEpb2d ( double epb ); + +double palEpco ( char k0, char k, double e ); + +double palEpj ( double date ); + +double palEpj2d ( double epj ); + +void palEpv( double date, double ph[3], double vh[3], + double pb[3], double vb[3] ); + +void palEqecl ( double dr, double dd, double date, double *dl, double *db ); + +double palEqeqx ( double date ); + +void palEqgal ( double dr, double dd, double *dl, double *db ); + +void palEtrms ( double ep, double ev[3] ); + +void palEuler ( const char *order, float phi, float theta, float psi, + float rmat[3][3] ); + +void palEvp ( double date, double deqx, + double dvb[3], double dpb[3], + double dvh[3], double dph[3] ); + +void palFitxy ( int itype, int np, double xye[][2], double xym[][2], + double coeffs[6], int *j ); + +void palFk425 ( double r1950, double d1950, double dr1950, + double dd1950, double p1950, double v1950, + double *r2000, double *d2000, double *dr2000, + double *dd2000, double *p2000, double *v2000 ); + +void palFk45z ( double r1950, double d1950, double bepoch, + double *r2000, double *d2000 ); + +void palFk524 ( double r2000, double d2000, double dr2000, + double dd2000, double p2000, double v2000, + double *r1950, double *d1950, double *dr1950, + double *dd1950, double *p1950, double *v1950 ); + +void palFk52h ( double r5, double d5, double dr5, double dd5, + double *dr, double *dh, double *drh, double *ddh ); + +void palFk54z ( double r2000, double d2000, double bepoch, + double *r1950, double *d1950, + double *dr1950, double *dd1950 ); + +void palFk5hz ( double r5, double d5, double epoch, + double *rh, double *dh ); + +void palFlotin ( const char *string, int *nstrt, float *reslt, int *jflag ); + +void palGaleq ( double dl, double db, double *dr, double *dd ); + +void palGalsup ( double dl, double db, double *dsl, double *dsb ); + +void palGe50 ( double dl, double db, double *dr, double *dd ); + +void palGeoc ( double p, double h, double *r, double *z ); + +double palGmst ( double ut1 ); + +double palGmsta ( double date, double ut1 ); + +void palH2e ( float az, float el, float phi, float *ha, float *dec ); + +void palH2fk5 ( double dr, double dh, double drh, double ddh, + double *r5, double *d5, double *dr5, double *dd5 ); + +void palHfk5z ( double rh, double dh, double epoch, + double *r5, double *d5, double *dr5, double *dd5 ); + +void palImxv ( float rm[3][3], float va[3], float vb[3] ); + +void palInt2in ( const char *string, int *nstrt, int *ireslt, int *jflag ); + +void palIntin ( const char *string, int *nstrt, long *ireslt, int *jflag ); + +void palInvf ( double fwds[6], double bkwds[6], int *j ); + +void palKbj ( int jb, double e, char *k, int *j ); + +void palM2av ( float rmat[3][3], float axvec[3] ); + +void palMap ( double rm, double dm, double pr, double pd, + double px, double rv, double eq, double date, + double *ra, double *da ); + +void palMappa ( double eq, double date, double amprms[21] ); + +void palMapqk ( double rm, double dm, double pr, double pd, + double px, double rv, double amprms[21], + double *ra, double *da ); + +void palMapqkz ( double rm, double dm, double amprms[21], + double *ra, double *da ); + +void palMoon ( int iy, int id, float fd, float posvel[6] ); + +void palMxm ( float a[3][3], float b[3][3], float c[3][3] ); + +void palMxv ( float rm[3][3], float va[3], float vb[3] ); + +void palNut ( double date, double rmatn[3][3] ); + +void palNutc ( double date, double *dpsi, double *deps, double *eps0 ); + +void palNutc80 ( double date, double *dpsi, double *deps, double *eps0 ); + +void palOap ( const char *type, double ob1, double ob2, double date, + double dut, double elongm, double phim, double hm, + double xp, double yp, double tdk, double pmb, + double rh, double wl, double tlr, + double *rap, double *dap ); + +void palOapqk ( const char *type, double ob1, double ob2, double aoprms[14], + double *rap, double *dap ); + +int palObs( size_t n, const char * c, + char * ident, size_t identlen, + char * name, size_t namelen, + double * w, double * p, double * h ); + +double palPa ( double ha, double dec, double phi ); + +double palPav ( float v1[3], float v2[3] ); + +void palPcd ( double disco, double *x, double *y ); + +void palPda2h ( double p, double d, double a, + double *h1, int *j1, double *h2, int *j2 ); + +void palPdq2h ( double p, double d, double q, + double *h1, int *j1, double *h2, int *j2 ); + +void palPermut ( int n, int istate[], int iorder[], int *j ); + +void palPertel (int jform, double date0, double date1, + double epoch0, double orbi0, double anode0, + double perih0, double aorq0, double e0, double am0, + double *epoch1, double *orbi1, double *anode1, + double *perih1, double *aorq1, double *e1, double *am1, + int *jstat ); + +void palPertue ( double date, double u[13], int *jstat ); + +void palPlanel ( double date, int jform, double epoch, double orbinc, + double anode, double perih, double aorq, double e, + double aorl, double dm, double pv[6], int *jstat ); + +void palPlanet ( double date, int np, double pv[6], int *j ); + +void palPlante ( double date, double elong, double phi, int jform, + double epoch, double orbinc, double anode, double perih, + double aorq, double e, double aorl, double dm, + double *ra, double *dec, double *r, int *jstat ); + +void palPlantu ( double date, double elong, double phi, const double u[13], + double *ra, double *dec, double *r, int *jstat ); + +void palPm ( double r0, double d0, double pr, double pd, + double px, double rv, double ep0, double ep1, + double *r1, double *d1 ); + +void palPolmo ( double elongm, double phim, double xp, double yp, + double *elong, double *phi, double *daz ); + +void palPrebn ( double bep0, double bep1, double rmatp[3][3] ); + +void palPrec ( double ep0, double ep1, double rmatp[3][3] ); + +void palPrecl ( double ep0, double ep1, double rmatp[3][3] ); + +void palPreces ( const char sys[3], double ep0, double ep1, + double *ra, double *dc ); + +void palPrenut ( double epoch, double date, double rmatpn[3][3] ); + +void palPv2el ( const double pv[6], double date, double pmass, int jformr, + int *jform, double *epoch, double *orbinc, + double *anode, double *perih, double *aorq, double *e, + double *aorl, double *dm, int *jstat ); + +void palPv2ue ( const double pv[6], double date, double pmass, + double u[13], int *jstat ); + +void palPvobs ( double p, double h, double stl, double pv[6] ); + +void palPxy ( int np, double xye[][2], double xym[][2], + double coeffs[6], + double xyp[][2], double *xrms, double *yrms, double *rrms ); + +float palRange ( float angle ); + +float palRanorm ( float angle ); + +double palRcc ( double tdb, double ut1, double wl, double u, double v ); + +void palRdplan ( double date, int np, double elong, double phi, + double *ra, double *dec, double *diam ); + +void palRefco ( double hm, double tdk, double pmb, double rh, + double wl, double phi, double tlr, double eps, + double *refa, double *refb ); + +void palRefcoq ( double tdk, double pmb, double rh, double wl, + double *refa, double *refb ); + +void palRefro ( double zobs, double hm, double tdk, double pmb, + double rh, double wl, double phi, double tlr, double eps, + double *ref ); + +void palRefv ( double vu[3], double refa, double refb, double vr[3] ); + +void palRefz ( double zu, double refa, double refb, double *zr ); + +double palRverot ( double phi, double ra, double da, double st ); + +double palRvgalc ( double r2000, double d2000 ); + +double palRvlg ( double r2000, double d2000 ); + +double palRvlsrd ( double r2000, double d2000 ); + +double palRvlsrk ( double r2000, double d2000 ); + +void palS2tp ( float ra, float dec, float raz, float decz, + float *xi, float *eta, int *j ); + +float palSep ( float a1, float b1, float a2, float b2 ); + +float palSepv ( float v1[3], float v2[3] ); + +void palSmat ( int n, float *a, float *y, float *d, int *jf, int *iw ); + +void palSubet ( double rc, double dc, double eq, + double *rm, double *dm ); + +void palSupgal ( double dsl, double dsb, double *dl, double *db ); + +void palSvd ( int m, int n, int mp, int np, + double *a, double *w, double *v, double *work, + int *jstat ); + +void palSvdcov ( int n, int np, int nc, + double *w, double *v, double *work, double *cvm ); + +void palSvdsol ( int m, int n, int mp, int np, + double *b, double *u, double *w, double *v, + double *work, double *x ); + +void palTp2s ( float xi, float eta, float raz, float decz, + float *ra, float *dec ); + +void palTp2v ( float xi, float eta, float v0[3], float v[3] ); + +void palTps2c ( float xi, float eta, float ra, float dec, + float *raz1, float *decz1, + float *raz2, float *decz2, int *n ); + +void palTpv2c ( float xi, float eta, float v[3], + float v01[3], float v02[3], int *n ); + +void palUe2el ( const double u[13], int jformr, + int *jform, double *epoch, double *orbinc, + double *anode, double *perih, double *aorq, double *e, + double *aorl, double *dm, int *jstat ); + +void palUe2pv ( double date, double u[13], double pv[], int *jstat ); + +void palUnpcd ( double disco, double *x, double *y ); + +void palV2tp ( float v[3], float v0[3], float *xi, float *eta, int *j ); + +float palVdv ( float va[3], float vb[3] ); + +void palVn ( float v[3], float uv[3], float *vm ); + +void palVxv ( float va[3], float vb[3], float vc[3] ); + +void palXy2xy ( double x1, double y1, double coeffs[6], + double *x2, double *y2 ); + +double palZd ( double ha, double dec, double phi ); + +#ifdef __cplusplus +} +#endif + +/* ------------- end of pal/pal.h ------------------------------ */ + + + + +#endif diff --git a/pal/pal.h b/pal/pal.h new file mode 100644 index 0000000..7a40d5d --- /dev/null +++ b/pal/pal.h @@ -0,0 +1,549 @@ +#ifndef PALHDEF +#define PALHDEF + +/* +*+ +* Name: +* pal.h + +* Purpose: +* Function prototypes for PAL routines. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Include file + +* Description: +* Function prototypes for PAL routines. + +* Authors: +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* + +* History: +* 2012-02-08 (TIMJ): +* Initial version. Define all SLA prototypes in PAL form even +* though none are implemented. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void palAddet ( double rm, double dm, double eq, double *rc, double *dc ); + +void palAfin ( const char *string, int *iptr, float *a, int *j ); + +double palAirmas ( double zd ); + +void palAltaz ( double ha, double dec, double phi, + double *az, double *azd, double *azdd, + double *el, double *eld, double *eldd, + double *pa, double *pad, double *padd ); + +void palAmp ( double ra, double da, double date, double eq, + double *rm, double *dm ); + +void palAmpqk ( double ra, double da, double amprms[21], + double *rm, double *dm ); + +void palAop ( double rap, double dap, double date, double dut, + double elongm, double phim, double hm, double xp, + double yp, double tdk, double pmb, double rh, + double wl, double tlr, + double *aob, double *zob, double *hob, + double *dob, double *rob ); + +void palAoppa ( double date, double dut, double elongm, double phim, + double hm, double xp, double yp, double tdk, double pmb, + double rh, double wl, double tlr, double aoprms[14] ); + +void palAoppat ( double date, double aoprms[14] ); + +void palAopqk ( double rap, double dap, const double aoprms[14], + double *aob, double *zob, double *hob, + double *dob, double *rob ); + +void palAtmdsp ( double tdk, double pmb, double rh, double wl1, + double a1, double b1, double wl2, double *a2, double *b2 ); + +void palAv2m ( float axvec[3], float rmat[3][3] ); + +float palBear ( float a1, float b1, float a2, float b2 ); + +void palCaf2r ( int ideg, int iamin, float asec, float *rad, int *j ); + +void palCaldj ( int iy, int im, int id, double *djm, int *j ); + +void palCalyd ( int iy, int im, int id, int *ny, int *nd, int *j ); + +void palCc2s ( float v[3], float *a, float *b ); + +void palCc62s ( float v[6], float *a, float *b, float *r, + float *ad, float *bd, float *rd ); + +void palCd2tf ( int ndp, float days, char *sign, int ihmsf[4] ); + +void palCldj ( int iy, int im, int id, double *djm, int *j ); + +void palClyd ( int iy, int im, int id, int *ny, int *nd, int *jstat ); + +void palCombn ( int nsel, int ncand, int list[], int *j ); + +void palCr2af ( int ndp, float angle, char *sign, int idmsf[4] ); + +void palCr2tf ( int ndp, float angle, char *sign, int ihmsf[4] ); + +void palCs2c ( float a, float b, float v[3] ); + +void palCs2c6 ( float a, float b, float r, float ad, + float bd, float rd, float v[6] ); + +void palCtf2d ( int ihour, int imin, float sec, float *days, int *j ); + +void palCtf2r ( int ihour, int imin, float sec, float *rad, int *j ); + +void palDaf2r ( int ideg, int iamin, double asec, double *rad, int *j ); + +void palDafin ( const char *string, int *iptr, double *a, int *j ); + +double palDat ( double dju ); + +void palDav2m ( double axvec[3], double rmat[3][3] ); + +double palDbear ( double a1, double b1, double a2, double b2 ); + +void palDbjin ( const char *string, int *nstrt, + double *dreslt, int *jf1, int *jf2 ); + +void palDc62s ( double v[6], double *a, double *b, double *r, + double *ad, double *bd, double *rd ); + +void palDcc2s ( double v[3], double *a, double *b ); + +void palDcmpf ( double coeffs[6], double *xz, double *yz, double *xs, + double *ys, double *perp, double *orient ); + +void palDcs2c ( double a, double b, double v[3] ); + +void palDd2tf ( int ndp, double days, char *sign, int ihmsf[4] ); + +void palDe2h ( double ha, double dec, double phi, + double *az, double *el ); + +void palDeuler ( const char *order, double phi, double theta, double psi, + double rmat[3][3] ); + +void palDfltin ( const char *string, int *nstrt, double *dreslt, int *jflag ); + +void palDh2e ( double az, double el, double phi, double *ha, double *dec); + +void palDimxv ( double dm[3][3], double va[3], double vb[3] ); + +void palDjcal ( int ndp, double djm, int iymdf[4], int *j ); + +void palDjcl ( double djm, int *iy, int *im, int *id, double *fd, int *j ); + +void palDm2av ( double rmat[3][3], double axvec[3] ); + +void palDmat ( int n, double *a, double *y, double *d, int *jf, int *iw ); + +void palDmoon ( double date, double pv[6] ); + +void palDmxm ( double a[3][3], double b[3][3], double c[3][3] ); + +void palDmxv ( double dm[3][3], double va[3], double vb[3] ); + +double palDpav ( double v1[3], double v2[3] ); + +void palDr2af ( int ndp, double angle, char *sign, int idmsf[4] ); + +void palDr2tf ( int ndp, double angle, char *sign, int ihmsf[4] ); + +double palDrange ( double angle ); + +double palDranrm ( double angle ); + +void palDs2c6 ( double a, double b, double r, double ad, double bd, + double rd, double v[6] ); + +void palDs2tp ( double ra, double dec, double raz, double decz, + double *xi, double *eta, int *j ); + +double palDsep ( double a1, double b1, double a2, double b2 ); + +double palDsepv ( double v1[3], double v2[3] ); + +double palDt ( double epoch ); + +void palDtf2d ( int ihour, int imin, double sec, double *days, int *j ); + +void palDtf2r ( int ihour, int imin, double sec, double *rad, int *j ); + +void palDtp2s ( double xi, double eta, double raz, double decz, + double *ra, double *dec ); + +void palDtp2v ( double xi, double eta, double v0[3], double v[3] ); + +void palDtps2c ( double xi, double eta, double ra, double dec, + double *raz1, double *decz1, + double *raz2, double *decz2, int *n ); + +void palDtpv2c ( double xi, double eta, double v[3], + double v01[3], double v02[3], int *n ); + +double palDtt ( double dju ); + +void palDv2tp ( double v[3], double v0[3], double *xi, double *eta, int *j ); + +double palDvdv ( double va[3], double vb[3] ); + +void palDvn ( double v[3], double uv[3], double *vm ); + +void palDvxv ( double va[3], double vb[3], double vc[3] ); + +void palE2h ( float ha, float dec, float phi, float *az, float *el ); + +void palEarth ( int iy, int id, float fd, float posvel[6] ); + +void palEcleq ( double dl, double db, double date, double *dr, double *dd ); + +void palEcmat ( double date, double rmat[3][3] ); + +void palEcor ( float rm, float dm, int iy, int id, float fd, + float *rv, float *tl ); + +void palEg50 ( double dr, double dd, double *dl, double *db ); + +void palEl2ue ( double date, int jform, double epoch, double orbinc, + double anode, double perih, double aorq, double e, + double aorl, double dm, double u[13], int *jstat ); + +double palEpb ( double date ); + +double palEpb2d ( double epb ); + +double palEpco ( char k0, char k, double e ); + +double palEpj ( double date ); + +double palEpj2d ( double epj ); + +void palEpv( double date, double ph[3], double vh[3], + double pb[3], double vb[3] ); + +void palEqecl ( double dr, double dd, double date, double *dl, double *db ); + +double palEqeqx ( double date ); + +void palEqgal ( double dr, double dd, double *dl, double *db ); + +void palEtrms ( double ep, double ev[3] ); + +void palEuler ( const char *order, float phi, float theta, float psi, + float rmat[3][3] ); + +void palEvp ( double date, double deqx, + double dvb[3], double dpb[3], + double dvh[3], double dph[3] ); + +void palFitxy ( int itype, int np, double xye[][2], double xym[][2], + double coeffs[6], int *j ); + +void palFk425 ( double r1950, double d1950, double dr1950, + double dd1950, double p1950, double v1950, + double *r2000, double *d2000, double *dr2000, + double *dd2000, double *p2000, double *v2000 ); + +void palFk45z ( double r1950, double d1950, double bepoch, + double *r2000, double *d2000 ); + +void palFk524 ( double r2000, double d2000, double dr2000, + double dd2000, double p2000, double v2000, + double *r1950, double *d1950, double *dr1950, + double *dd1950, double *p1950, double *v1950 ); + +void palFk52h ( double r5, double d5, double dr5, double dd5, + double *dr, double *dh, double *drh, double *ddh ); + +void palFk54z ( double r2000, double d2000, double bepoch, + double *r1950, double *d1950, + double *dr1950, double *dd1950 ); + +void palFk5hz ( double r5, double d5, double epoch, + double *rh, double *dh ); + +void palFlotin ( const char *string, int *nstrt, float *reslt, int *jflag ); + +void palGaleq ( double dl, double db, double *dr, double *dd ); + +void palGalsup ( double dl, double db, double *dsl, double *dsb ); + +void palGe50 ( double dl, double db, double *dr, double *dd ); + +void palGeoc ( double p, double h, double *r, double *z ); + +double palGmst ( double ut1 ); + +double palGmsta ( double date, double ut1 ); + +void palH2e ( float az, float el, float phi, float *ha, float *dec ); + +void palH2fk5 ( double dr, double dh, double drh, double ddh, + double *r5, double *d5, double *dr5, double *dd5 ); + +void palHfk5z ( double rh, double dh, double epoch, + double *r5, double *d5, double *dr5, double *dd5 ); + +void palImxv ( float rm[3][3], float va[3], float vb[3] ); + +void palInt2in ( const char *string, int *nstrt, int *ireslt, int *jflag ); + +void palIntin ( const char *string, int *nstrt, long *ireslt, int *jflag ); + +void palInvf ( double fwds[6], double bkwds[6], int *j ); + +void palKbj ( int jb, double e, char *k, int *j ); + +void palM2av ( float rmat[3][3], float axvec[3] ); + +void palMap ( double rm, double dm, double pr, double pd, + double px, double rv, double eq, double date, + double *ra, double *da ); + +void palMappa ( double eq, double date, double amprms[21] ); + +void palMapqk ( double rm, double dm, double pr, double pd, + double px, double rv, double amprms[21], + double *ra, double *da ); + +void palMapqkz ( double rm, double dm, double amprms[21], + double *ra, double *da ); + +void palMoon ( int iy, int id, float fd, float posvel[6] ); + +void palMxm ( float a[3][3], float b[3][3], float c[3][3] ); + +void palMxv ( float rm[3][3], float va[3], float vb[3] ); + +void palNut ( double date, double rmatn[3][3] ); + +void palNutc ( double date, double *dpsi, double *deps, double *eps0 ); + +void palNutc80 ( double date, double *dpsi, double *deps, double *eps0 ); + +void palOap ( const char *type, double ob1, double ob2, double date, + double dut, double elongm, double phim, double hm, + double xp, double yp, double tdk, double pmb, + double rh, double wl, double tlr, + double *rap, double *dap ); + +void palOapqk ( const char *type, double ob1, double ob2, const double aoprms[14], + double *rap, double *dap ); + +int palObs( size_t n, const char * c, + char * ident, size_t identlen, + char * name, size_t namelen, + double * w, double * p, double * h ); + +double palPa ( double ha, double dec, double phi ); + +double palPav ( float v1[3], float v2[3] ); + +void palPcd ( double disco, double *x, double *y ); + +void palPda2h ( double p, double d, double a, + double *h1, int *j1, double *h2, int *j2 ); + +void palPdq2h ( double p, double d, double q, + double *h1, int *j1, double *h2, int *j2 ); + +void palPermut ( int n, int istate[], int iorder[], int *j ); + +void palPertel (int jform, double date0, double date1, + double epoch0, double orbi0, double anode0, + double perih0, double aorq0, double e0, double am0, + double *epoch1, double *orbi1, double *anode1, + double *perih1, double *aorq1, double *e1, double *am1, + int *jstat ); + +void palPertue ( double date, double u[13], int *jstat ); + +void palPlanel ( double date, int jform, double epoch, double orbinc, + double anode, double perih, double aorq, double e, + double aorl, double dm, double pv[6], int *jstat ); + +void palPlanet ( double date, int np, double pv[6], int *j ); + +void palPlante ( double date, double elong, double phi, int jform, + double epoch, double orbinc, double anode, double perih, + double aorq, double e, double aorl, double dm, + double *ra, double *dec, double *r, int *jstat ); + +void palPlantu ( double date, double elong, double phi, const double u[13], + double *ra, double *dec, double *r, int *jstat ); + +void palPm ( double r0, double d0, double pr, double pd, + double px, double rv, double ep0, double ep1, + double *r1, double *d1 ); + +void palPolmo ( double elongm, double phim, double xp, double yp, + double *elong, double *phi, double *daz ); + +void palPrebn ( double bep0, double bep1, double rmatp[3][3] ); + +void palPrec ( double ep0, double ep1, double rmatp[3][3] ); + +void palPrecl ( double ep0, double ep1, double rmatp[3][3] ); + +void palPreces ( const char sys[3], double ep0, double ep1, + double *ra, double *dc ); + +void palPrenut ( double epoch, double date, double rmatpn[3][3] ); + +void palPv2el ( const double pv[6], double date, double pmass, int jformr, + int *jform, double *epoch, double *orbinc, + double *anode, double *perih, double *aorq, double *e, + double *aorl, double *dm, int *jstat ); + +void palPv2ue ( const double pv[6], double date, double pmass, + double u[13], int *jstat ); + +void palPvobs ( double p, double h, double stl, double pv[6] ); + +void palPxy ( int np, double xye[][2], double xym[][2], + double coeffs[6], + double xyp[][2], double *xrms, double *yrms, double *rrms ); + +float palRange ( float angle ); + +float palRanorm ( float angle ); + +double palRcc ( double tdb, double ut1, double wl, double u, double v ); + +void palRdplan ( double date, int np, double elong, double phi, + double *ra, double *dec, double *diam ); + +void palRefco ( double hm, double tdk, double pmb, double rh, + double wl, double phi, double tlr, double eps, + double *refa, double *refb ); + +void palRefcoq ( double tdk, double pmb, double rh, double wl, + double *refa, double *refb ); + +void palRefro ( double zobs, double hm, double tdk, double pmb, + double rh, double wl, double phi, double tlr, double eps, + double *ref ); + +void palRefv ( double vu[3], double refa, double refb, double vr[3] ); + +void palRefz ( double zu, double refa, double refb, double *zr ); + +double palRverot ( double phi, double ra, double da, double st ); + +double palRvgalc ( double r2000, double d2000 ); + +double palRvlg ( double r2000, double d2000 ); + +double palRvlsrd ( double r2000, double d2000 ); + +double palRvlsrk ( double r2000, double d2000 ); + +void palS2tp ( float ra, float dec, float raz, float decz, + float *xi, float *eta, int *j ); + +float palSep ( float a1, float b1, float a2, float b2 ); + +float palSepv ( float v1[3], float v2[3] ); + +void palSmat ( int n, float *a, float *y, float *d, int *jf, int *iw ); + +void palSubet ( double rc, double dc, double eq, + double *rm, double *dm ); + +void palSupgal ( double dsl, double dsb, double *dl, double *db ); + +void palSvd ( int m, int n, int mp, int np, + double *a, double *w, double *v, double *work, + int *jstat ); + +void palSvdcov ( int n, int np, int nc, + double *w, double *v, double *work, double *cvm ); + +void palSvdsol ( int m, int n, int mp, int np, + double *b, double *u, double *w, double *v, + double *work, double *x ); + +void palTp2s ( float xi, float eta, float raz, float decz, + float *ra, float *dec ); + +void palTp2v ( float xi, float eta, float v0[3], float v[3] ); + +void palTps2c ( float xi, float eta, float ra, float dec, + float *raz1, float *decz1, + float *raz2, float *decz2, int *n ); + +void palTpv2c ( float xi, float eta, float v[3], + float v01[3], float v02[3], int *n ); + +void palUe2el ( const double u[13], int jformr, + int *jform, double *epoch, double *orbinc, + double *anode, double *perih, double *aorq, double *e, + double *aorl, double *dm, int *jstat ); + +void palUe2pv ( double date, double u[13], double pv[], int *jstat ); + +void palUnpcd ( double disco, double *x, double *y ); + +void palV2tp ( float v[3], float v0[3], float *xi, float *eta, int *j ); + +float palVdv ( float va[3], float vb[3] ); + +void palVn ( float v[3], float uv[3], float *vm ); + +void palVxv ( float va[3], float vb[3], float vc[3] ); + +void palXy2xy ( double x1, double y1, double coeffs[6], + double *x2, double *y2 ); + +double palZd ( double ha, double dec, double phi ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pal/pal1sofa.h b/pal/pal1sofa.h new file mode 100644 index 0000000..11f7446 --- /dev/null +++ b/pal/pal1sofa.h @@ -0,0 +1,142 @@ +/* +*+ +* Name: +* pal1sofa.h + +* Purpose: +* Mappings of ERFA names to SOFA names + +* Language: +* Starlink ANSI C + +* Type of Module: +* Include file + +* Invocation: +* #include "pal1sofa.h" + +* Description: +* PAL will work with both SOFA and ERFA libraries and the +* difference is generally a change in prefix. This include +* file maps the ERFA form of functions to the SOFA form +* and includes the relevant sofa.h vs erfa.h file. + +* Authors: +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* - PAL uses the ERFA form by default. + +* History: +* 2014-07-29 (TIMJ): +* Initial version +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2014 Tim Jenness +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#ifndef PAL1SOFAHDEF +#define PAL1SOFAHDEF + +#if HAVE_CONFIG_H +# include +#endif + +# if HAVE_SOFA_H + +# include "sofa.h" +# include "sofam.h" + + /* Must replace ERFA with SOFA */ + +# define eraA2af iauA2af +# define eraA2tf iauA2tf +# define eraAf2a iauAf2a +# define eraAnp iauAnp +# define eraAnpm iauAnpm +# define eraC2s iauC2s +# define eraCal2jd iauCal2jd +# define eraD2tf iauD2tf +# define eraDat iauDat +# define eraEe06a iauEe06a +# define eraEpb iauEpb +# define eraEpb2jd iauEpb2jd +# define eraEpj iauEpj +# define eraEpj2jd iauEpj2jd +# define eraEpv00 iauEpv00 +# define eraFk5hz iauFk5hz +# define eraGd2gc iauGd2gc +# define eraGmst06 iauGmst06 +# define eraHfk5z iauHfk5z +# define eraIr iauIr +# define eraJd2cal iauJd2cal +# define eraNut06a iauNut06a +# define eraObl06 iauObl06 +# define eraP06e iauP06e +# define eraPap iauPap +# define eraPas iauPas +# define eraPdp iauPdp +# define eraPlan94 iauPlan94 +# define eraPmat06 iauPmat06 +# define eraPn iauPn +# define eraPnm06a iauPnm06a +# define eraPxp iauPxp +# define eraRefco iauRefco +# define eraRm2v iauRm2v +# define eraRv2m iauRv2m +# define eraRx iauRx +# define eraRxp iauRxp +# define eraRxpv iauRxpv +# define eraRxr iauRxr +# define eraRy iauRy +# define eraRz iauRz +# define eraS2c iauS2c +# define eraSepp iauSepp +# define eraSeps iauSeps +# define eraStarpm iauStarpm +# define eraTf2a iauTf2a +# define eraTf2d iauTf2d +# define eraTr iauTr +# define eraTrxp iauTrxp + +/* These are from sofam.h */ + +# define ERFA_WGS84 WGS84 + +# define ERFA_DJ00 DJ00 +# define ERFA_DJY DJY +# define ERFA_DAU DAU + +# else + +# include "erfa.h" +# include "erfam.h" + +/* No further action required */ + +# endif + +#endif diff --git a/pal/palAddet.c b/pal/palAddet.c new file mode 100644 index 0000000..ce3ddab --- /dev/null +++ b/pal/palAddet.c @@ -0,0 +1,112 @@ +/* +*+ +* Name: +* palAddet + +* Purpose: +* Add the E-terms to a pre IAU 1976 mean place + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palAddet ( double rm, double dm, double eq, +* double *rc, double *dc ); + +* Arguments: +* rm = double (Given) +* RA without E-terms (radians) +* dm = double (Given) +* Dec without E-terms (radians) +* eq = double (Given) +* Besselian epoch of mean equator and equinox +* rc = double * (Returned) +* RA with E-terms included (radians) +* dc = double * (Returned) +* Dec with E-terms included (radians) + +* Description: +* Add the E-terms (elliptic component of annual aberration) +* to a pre IAU 1976 mean place to conform to the old +* catalogue convention. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* Most star positions from pre-1984 optical catalogues (or +* derived from astrometry using such stars) embody the +* E-terms. If it is necessary to convert a formal mean +* place (for example a pulsar timing position) to one +* consistent with such a star catalogue, then the RA,Dec +* should be adjusted using this routine. + +* See Also: +* Explanatory Supplement to the Astronomical Ephemeris, +* section 2D, page 48. + +* History: +* 2012-02-12(TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1999 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palAddet ( double rm, double dm, double eq, double *rc, double *dc ) { + double a[3]; /* The E-terms */ + double v[3]; + int i; + + /* Note the preference for IAU routines */ + + /* Retrieve the E-terms */ + palEtrms( eq, a ); + + /* Spherical to Cartesian */ + eraS2c( rm, dm, v ); + + /* Include the E-terms */ + for (i=0; i<3; i++) { + v[i] += a[i]; + } + + /* Cartesian to spherical */ + eraC2s( v, rc, dc ); + + /* Bring RA into conventional range */ + *rc = eraAnp( *rc ); + +} diff --git a/pal/palAmpqk.c b/pal/palAmpqk.c new file mode 100644 index 0000000..1e0681a --- /dev/null +++ b/pal/palAmpqk.c @@ -0,0 +1,128 @@ +/* +*+ +* Name: +* palAmpqk + +* Purpose: +* Convert star RA,Dec from geocentric apparent to mean place. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palAmpqk ( double ra, double da, double amprms[21], +* double *rm, double *dm ) + +* Arguments: +* ra = double (Given) +* Apparent RA (radians). +* da = double (Given) +* Apparent Dec (radians). +* amprms = double[21] (Given) +* Star-independent mean-to-apparent parameters (see palMappa): +* (0) time interval for proper motion (Julian years) +* (1-3) barycentric position of the Earth (AU) +* (4-6) not used +* (7) not used +* (8-10) abv: barycentric Earth velocity in units of c +* (11) sqrt(1-v*v) where v=modulus(abv) +* (12-20) precession/nutation (3,3) matrix +* rm = double (Returned) +* Mean RA (radians). +* dm = double (Returned) +* Mean Dec (radians). + +* Description: +* Convert star RA,Dec from geocentric apparent to mean place. The "mean" +* coordinate system is in fact close to ICRS. Use of this function +* is appropriate when efficiency is important and where many star +* positions are all to be transformed for one epoch and equinox. The +* star-independent parameters can be obtained by calling the palMappa +* function. + +* Authors: +* PTW: Pat Wallace (STFC) +* {enter_new_authors_here} + +* History: +* 2012-02-13 (PTW): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2000 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palAmpqk ( double ra, double da, double amprms[21], double *rm, + double *dm ){ + +/* Local Variables: */ + double ab1; /* sqrt(1-v*v) where v=modulus of Earth vel */ + double abv[3]; /* Earth velocity wrt SSB (c, FK5) */ + double p1[3], p2[3], p3[3]; /* work vectors */ + double ab1p1, p1dv, p1dvp1, w; + int i, j; + +/* Unpack some of the parameters */ + ab1 = amprms[11]; + for( i = 0; i < 3; i++ ) { + abv[i] = amprms[i + 8]; + } + +/* Apparent RA,Dec to Cartesian */ + eraS2c( ra, da, p3 ); + +/* Precession and nutation */ + eraTrxp( (double(*)[3]) &rms[12], p3, p2 ); + +/* Aberration */ + ab1p1 = ab1 + 1.0; + for( i = 0; i < 3; i++ ) { + p1[i] = p2[i]; + } + for( j = 0; j < 2; j++ ) { + p1dv = eraPdp( p1, abv ); + p1dvp1 = 1.0 + p1dv; + w = 1.0 + p1dv / ab1p1; + for( i = 0; i < 3; i++ ) { + p1[i] = ( p1dvp1 * p2[i] - w * abv[i] ) / ab1; + } + eraPn( p1, &w, p3 ); + for( i = 0; i < 3; i++ ) { + p1[i] = p3[i]; + } + } + +/* Mean RA,Dec */ + eraC2s( p1, rm, dm ); + *rm = eraAnp( *rm ); +} diff --git a/pal/palCaldj.c b/pal/palCaldj.c new file mode 100644 index 0000000..03c6b97 --- /dev/null +++ b/pal/palCaldj.c @@ -0,0 +1,99 @@ +/* +*+ +* Name: +* palCaldj + +* Purpose: +* Gregorian Calendar to Modified Julian Date + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palCaldj ( int iy, int im, int id, double *djm, int *j ); + +* Arguments: +* iy = int (Given) +* Year in the Gregorian calendar +* im = int (Given) +* Month in the Gergorian calendar +* id = int (Given) +* Day in the Gregorian calendar +* djm = double * (Returned) +* Modified Julian Date (JD-2400000.5) for 0 hrs +* j = status (Returned) +* 0 = OK. See eraCal2jd for other values. + +* Description: +* Modified Julian Date to Gregorian Calendar with special +* behaviour for 2-digit years relating to 1950 to 2049. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-11 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Notes: +* - Uses eraCal2jd +* - Unlike eraCal2jd this routine treats the years 0-100 as +* referring to the end of the 20th Century and beginning of +* the 21st Century. If this behaviour is not acceptable +* use the SOFA/ERFA routine directly or palCldj. +* Acceptable years are 00-49, interpreted as 2000-2049, +* 50-99, " " 1950-1999, +* all others, interpreted literally. +* - Unlike SLA this routine will work with negative years. + + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palCaldj ( int iy, int im, int id, double *djm, int *j ) { + int adj = 0; /* Year adjustment */ + double djm0; + + if (iy >= 0 && iy <= 49) { + adj = 2000; + } else if (iy >= 50 && iy <= 99) { + adj = 1900; + } + iy += adj; + + *j = eraCal2jd( iy, im, id, &djm0, djm ); +} diff --git a/pal/palDat.c b/pal/palDat.c new file mode 100644 index 0000000..724ad13 --- /dev/null +++ b/pal/palDat.c @@ -0,0 +1,95 @@ +/* +*+ +* Name: +* palDtt + +* Purpose: +* Return offset between UTC and TT + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* dat = palDat( double utc ); + +* Arguments: +* utc = double (Given) +* UTC date as a modified JD (JD-2400000.5) + +* Returned Value: +* dat = double +* TAI-UTC in seconds + +* Description: +* Increment to be applied to Coordinated Universal Time UTC to give +* International Atomic Time (TAI). + +* Authors: +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* - This routine converts the MJD argument to calendar date before calling +* the SOFA/ERFA eraDat function. +* - This routine matches the slaDat interface which differs from the eraDat +* interface. Consider coding directly to the SOFA/ERFA interface. +* - See eraDat for a description of error conditions when calling this function +* with a time outside of the UTC range. +* - The status argument from eraDat is ignored. This is reasonable since the +* error codes are mainly related to incorrect calendar dates when calculating +* the JD internally. + +* History: +* 2012-02-08 (TIMJ): +* Initial version +* Adapted with permission from the Fortran SLALIB library +* although the core algorithm is now from SOFA. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" + +#include "pal1sofa.h" + +double palDat ( double dju ) { + int iy; + int im; + int id; + int status; + double fd; + double deltat; + + eraJd2cal( PAL__MJD0, dju, + &iy, &im, &id, &fd ); + + status = eraDat( iy, im, id, fd, &deltat ); + return deltat; +} diff --git a/pal/palDe2h.c b/pal/palDe2h.c new file mode 100644 index 0000000..a250e9e --- /dev/null +++ b/pal/palDe2h.c @@ -0,0 +1,142 @@ +/* +*+ +* Name: +* palDe2h + +* Purpose: +* Equatorial to horizon coordinates: HA,Dec to Az,E + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDe2h( double ha, double dec, double phi, double * az, double * el ); + +* Arguments: +* ha = double * (Given) +* Hour angle (radians) +* dec = double * (Given) +* Declination (radians) +* phi = double (Given) +* Observatory latitude (radians) +* az = double * (Returned) +* Azimuth (radians) +* el = double * (Returned) +* Elevation (radians) + +* Description: +* Convert equatorial to horizon coordinates. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* - All the arguments are angles in radians. +* - Azimuth is returned in the range 0-2pi; north is zero, +* and east is +pi/2. Elevation is returned in the range +* +/-pi/2. +* - The latitude must be geodetic. In critical applications, +* corrections for polar motion should be applied. +* - In some applications it will be important to specify the +* correct type of hour angle and declination in order to +* produce the required type of azimuth and elevation. In +* particular, it may be important to distinguish between +* elevation as affected by refraction, which would +* require the "observed" HA,Dec, and the elevation +* in vacuo, which would require the "topocentric" HA,Dec. +* If the effects of diurnal aberration can be neglected, the +* "apparent" HA,Dec may be used instead of the topocentric +* HA,Dec. +* - No range checking of arguments is carried out. +* - In applications which involve many such calculations, rather +* than calling the present routine it will be more efficient to +* use inline code, having previously computed fixed terms such +* as sine and cosine of latitude, and (for tracking a star) +* sine and cosine of declination. + +* History: +* 2012-02-08 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include + +void +palDe2h ( double ha, double dec, double phi, double *az, double *el) { + + double sh; + double ch; + double sd; + double cd; + double sp; + double cp; + + double a; + + double x; + double y; + double z; + double r; + + /* Useful trig functions */ + sh = sin(ha); + ch = cos(ha); + sd = sin(dec); + cd = cos(dec); + sp = sin(phi); + cp = cos(phi); + + /* Az,El as x,y,z */ + x = -ch * cd * sp + sd * cp; + y = -sh * cd; + z = ch * cd * cp + sd * sp; + + /* To spherical */ + r = sqrt(x * x + y * y); + if (r == 0.) { + a = 0.; + } else { + a = atan2(y, x); + } + if (a < 0.) { + a += PAL__D2PI; + } + *az = a; + *el = atan2(z, r); + + return; +} diff --git a/pal/palDeuler.c b/pal/palDeuler.c new file mode 100644 index 0000000..17e536b --- /dev/null +++ b/pal/palDeuler.c @@ -0,0 +1,141 @@ +/* +*+ +* Name: +* palDeuler + +* Purpose: +* Form a rotation matrix from the Euler angles + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palDeuler ( const char *order, double phi, double theta, double psi, +* double rmat[3][3] ); + +* Arguments: +* order = const char[] (Given) +* Specifies about which axes the rotation occurs +* phi = double (Given) +* 1st rotation (radians) +* theta = double (Given) +* 2nd rotation (radians) +* psi = double (Given) +* 3rd rotation (radians) +* rmat = double[3][3] (Given & Returned) +* Rotation matrix + +* Description: +* A rotation is positive when the reference frame rotates +* anticlockwise as seen looking towards the origin from the +* positive region of the specified axis. +* +* The characters of ORDER define which axes the three successive +* rotations are about. A typical value is 'ZXZ', indicating that +* RMAT is to become the direction cosine matrix corresponding to +* rotations of the reference frame through PHI radians about the +* old Z-axis, followed by THETA radians about the resulting X-axis, +* then PSI radians about the resulting Z-axis. +* +* The axis names can be any of the following, in any order or +* combination: X, Y, Z, uppercase or lowercase, 1, 2, 3. Normal +* axis labelling/numbering conventions apply; the xyz (=123) +* triad is right-handed. Thus, the 'ZXZ' example given above +* could be written 'zxz' or '313' (or even 'ZxZ' or '3xZ'). ORDER +* is terminated by length or by the first unrecognized character. +* +* Fewer than three rotations are acceptable, in which case the later +* angle arguments are ignored. If all rotations are zero, the +* identity matrix is produced. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-08 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1997 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void +palDeuler( const char *order, double phi, double theta, double psi, + double rmat[3][3] ) { + int i = 0; + double rotations[3]; + + /* Initialise rmat */ + eraIr( rmat ); + + /* copy the rotations into an array */ + rotations[0] = phi; + rotations[1] = theta; + rotations[2] = psi; + + /* maximum three rotations */ + while (i < 3 && order[i] != '\0') { + + switch (order[i]) { + case 'X': + case 'x': + case '1': + eraRx( rotations[i], rmat ); + break; + + case 'Y': + case 'y': + case '2': + eraRy( rotations[i], rmat ); + break; + + case 'Z': + case 'z': + case '3': + eraRz( rotations[i], rmat ); + break; + + default: + /* break out the loop if we do not recognize something */ + i = 3; + + } + + /* Go to the next position */ + i++; + } + + return; +} diff --git a/pal/palDh2e.c b/pal/palDh2e.c new file mode 100644 index 0000000..65434b7 --- /dev/null +++ b/pal/palDh2e.c @@ -0,0 +1,133 @@ +/* +*+ +* Name: +* palDh2e + +* Purpose: +* Horizon to equatorial coordinates: Az,El to HA,Dec + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDh2e( double az, double el, double phi, double * ha, double * dec ); + +* Arguments: +* az = double (Given) +* Azimuth (radians) +* el = double (Given) +* Elevation (radians) +* phi = double (Given) +* Observatory latitude (radians) +* ha = double * (Returned) +* Hour angle (radians) +* dec = double * (Returned) +* Declination (radians) + +* Description: +* Convert horizon to equatorial coordinates. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* - All the arguments are angles in radians. +* - The sign convention for azimuth is north zero, east +pi/2. +* - HA is returned in the range +/-pi. Declination is returned +* in the range +/-pi/2. +* - The latitude is (in principle) geodetic. In critical +* applications, corrections for polar motion should be applied. +* - In some applications it will be important to specify the +* correct type of elevation in order to produce the required +* type of HA,Dec. In particular, it may be important to +* distinguish between the elevation as affected by refraction, +* which will yield the "observed" HA,Dec, and the elevation +* in vacuo, which will yield the "topocentric" HA,Dec. If the +* effects of diurnal aberration can be neglected, the +* topocentric HA,Dec may be used as an approximation to the +* "apparent" HA,Dec. +* - No range checking of arguments is done. +* - In applications which involve many such calculations, rather +* than calling the present routine it will be more efficient to +* use inline code, having previously computed fixed terms such +* as sine and cosine of latitude. + +* History: +* 2012-02-08 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1996 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include + +void +palDh2e ( double az, double el, double phi, double *ha, double *dec) { + + double sa; + double ca; + double se; + double ce; + double sp; + double cp; + + double x; + double y; + double z; + double r; + + /* Useful trig functions */ + sa = sin(az); + ca = cos(az); + se = sin(el); + ce = cos(el); + sp = sin(phi); + cp = cos(phi); + + /* HA,Dec as x,y,z */ + x = -ca * ce * sp + se * cp; + y = -sa * ce; + z = ca * ce * cp + se * sp; + + /* To HA,Dec */ + r = sqrt(x * x + y * y); + if (r == 0.) { + *ha = 0.; + } else { + *ha = atan2(y, x); + } + *dec = atan2(z, r); + + return; +} diff --git a/pal/palDjcal.c b/pal/palDjcal.c new file mode 100644 index 0000000..b6aac9e --- /dev/null +++ b/pal/palDjcal.c @@ -0,0 +1,97 @@ +/* +*+ +* Name: +* palDjcal + +* Purpose: +* Modified Julian Date to Gregorian Calendar + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palDjcal ( int ndp, double djm, int iymdf[4], int *j ); + +* Arguments: +* ndp = int (Given) +* Number of decimal places of days in fraction. +* djm = double (Given) +* Modified Julian Date (JD-2400000.5) +* iymdf[4] = int[] (Returned) +* Year, month, day, fraction in Gregorian calendar. +* j = status (Returned) +* 0 = OK. See eraJd2cal for other values. + +* Description: +* Modified Julian Date to Gregorian Calendar, expressed +* in a form convenient for formatting messages (namely +* rounded to a specified precision, and with the fields +* stored in a single array) + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-10 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Notes: +* - Uses eraJd2cal + +* Copyright: +* Copyright (C) 2004 Patrick T. Wallace +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palDjcal ( int ndp, double djm, int iymdf[4], int *j ) { + double frac = 0.0; + double nfd; + + *j = eraJd2cal( PAL__MJD0, djm, &(iymdf[0]), + &(iymdf[1]), &(iymdf[2]), + &frac); + + /* Convert ndp to a power of 10 */ + nfd = pow( 10., (double)ndp ); + + /* Multiply the fraction */ + frac *= nfd; + + /* and now we want to round to the nearest integer */ + iymdf[3] = (int)DNINT(frac); + +} diff --git a/pal/palDmat.c b/pal/palDmat.c new file mode 100644 index 0000000..2948f92 --- /dev/null +++ b/pal/palDmat.c @@ -0,0 +1,182 @@ +/* +*+ +* Name: +* palDmat + +* Purpose: +* Matrix inversion & solution of simultaneous equations + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palDmat( int n, double *a, double *y, double *d, int *jf, +* int *iw ); + +* Arguments: +* n = int (Given) +* Number of simultaneous equations and number of unknowns. +* a = double[] (Given & Returned) +* A non-singular NxN matrix (implemented as a contiguous block +* of memory). After calling this routine "a" contains the +* inverse of the matrix. +* y = double[] (Given & Returned) +* On input the vector of N knowns. On exit this vector contains the +* N solutions. +* d = double * (Returned) +* The determinant. +* jf = int * (Returned) +* The singularity flag. If the matrix is non-singular, jf=0 +* is returned. If the matrix is singular, jf=-1 & d=0.0 are +* returned. In the latter case, the contents of array "a" on +* return are undefined. +* iw = int[] (Given) +* Integer workspace of size N. + +* Description: +* Matrix inversion & solution of simultaneous equations +* For the set of n simultaneous equations in n unknowns: +* A.Y = X +* this routine calculates the inverse of A, the determinant +* of matrix A and the vector of N unknowns. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-11 (TIMJ): +* Combination of a port of the Fortran and a comparison +* with the obfuscated GPL C routine. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Notes: +* - Implemented using Gaussian elimination with partial pivoting. +* - Optimized for speed rather than accuracy with errors 1 to 4 +* times those of routines optimized for accuracy. + +* Copyright: +* Copyright (C) 2001 Rutherford Appleton Laboratory. +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" + +void palDmat ( int n, double *a, double *y, double *d, int *jf, int *iw ) { + + const double SFA = 1e-20; + + int k; + double*aoff; + + *jf=0; + *d=1.0; + for(k=0,aoff=a; kamx){ + amx=t; + imx=i; + aoff2=apos2; + } + } + } + if(amx0;){ + int ki=iw[k]; + if(k!=ki){ + int i; + double *apos = a; + for(i=0;i. + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include + +double palDrange( double angle ){ + double result = fmod( angle, PAL__D2PI ); + if( result > PAL__DPI ) { + result -= PAL__D2PI; + } else if( result < -PAL__DPI ) { + result += PAL__D2PI; + } + return result; +} + diff --git a/pal/palDs2tp.c b/pal/palDs2tp.c new file mode 100644 index 0000000..cbf582a --- /dev/null +++ b/pal/palDs2tp.c @@ -0,0 +1,127 @@ +/* +*+ +* Name: +* palDs2tp + +* Purpose: +* Spherical to tangent plane projection + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDs2tp( double ra, double dec, double raz, double decz, +* double *xi, double *eta, int *j ); + +* Arguments: +* ra = double (Given) +* RA spherical coordinate of point to be projected (radians) +* dec = double (Given) +* Dec spherical coordinate of point to be projected (radians) +* raz = double (Given) +* RA spherical coordinate of tangent point (radians) +* decz = double (Given) +* Dec spherical coordinate of tangent point (radians) +* xi = double * (Returned) +* First rectangular coordinate on tangent plane (radians) +* eta = double * (Returned) +* Second rectangular coordinate on tangent plane (radians) +* j = int * (Returned) +* status: 0 = OK, star on tangent plane +* 1 = error, star too far from axis +* 2 = error, antistar on tangent plane +* 3 = error, antistar too far from axis + +* Description: +* Projection of spherical coordinates onto tangent plane: +* "gnomonic" projection - "standard coordinates" + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-08 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1996 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include + +void +palDs2tp ( double ra, double dec, double raz, double decz, + double *xi, double *eta, int *j ) { + + const double TINY = 1.0e-6; + + double cdec; + double sdec; + double radif; + double cdecz; + double denom; + double sdecz; + double cradif; + double sradif; + + /* Trig functions */ + sdecz = sin(decz); + sdec = sin(dec); + cdecz = cos(decz); + cdec = cos(dec); + radif = ra - raz; + sradif = sin(radif); + cradif = cos(radif); + + /* Reciprocal of star vector length to tangent plane */ + denom = sdec * sdecz + cdec * cdecz * cradif; + + /* Handle vectors too far from axis */ + if (denom > TINY) { + *j = 0; + } else if (denom >= 0.) { + *j = 1; + denom = TINY; + } else if (denom > -TINY) { + *j = 2; + denom = -TINY; + } else { + *j = 3; + } + + /* Compute tangent plane coordinates (even in dubious cases) */ + *xi = cdec * sradif / denom; + *eta = (sdec * cdecz - cdec * sdecz * cradif) / denom; + + return; +} diff --git a/pal/palDtp2s.c b/pal/palDtp2s.c new file mode 100644 index 0000000..583a56d --- /dev/null +++ b/pal/palDtp2s.c @@ -0,0 +1,95 @@ +/* +*+ +* Name: +* palDtp2s + +* Purpose: +* Tangent plane to spherical coordinates + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDtp2s( double xi, double eta, double raz, double decz, +* double *ra, double *dec); + +* Arguments: +* xi = double (Given) +* First rectangular coordinate on tangent plane (radians) +* eta = double (Given) +* Second rectangular coordinate on tangent plane (radians) +* raz = double (Given) +* RA spherical coordinate of tangent point (radians) +* decz = double (Given) +* Dec spherical coordinate of tangent point (radians) +* ra = double * (Returned) +* RA spherical coordinate of point to be projected (radians) +* dec = double * (Returned) +* Dec spherical coordinate of point to be projected (radians) + +* Description: +* Transform tangent plane coordinates into spherical. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-08 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +#include + +void +palDtp2s ( double xi, double eta, double raz, double decz, + double *ra, double *dec ) { + + double cdecz; + double denom; + double sdecz; + double d; + + sdecz = sin(decz); + cdecz = cos(decz); + denom = cdecz - eta * sdecz; + d = atan2(xi, denom) + raz; + *ra = eraAnp(d); + *dec = atan2(sdecz + eta * cdecz, sqrt(xi * xi + denom * denom)); + + return; +} diff --git a/pal/palDtps2c.c b/pal/palDtps2c.c new file mode 100644 index 0000000..ecb3090 --- /dev/null +++ b/pal/palDtps2c.c @@ -0,0 +1,151 @@ +/* +*+ +* Name: +* palDtps2c + +* Purpose: +* Determine RA,Dec of tangent point from coordinates + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palDtps2c( double xi, double eta, double ra, double dec, +* double * raz1, double decz1, +* double * raz2, double decz2, int *n); + +* Arguments: +* xi = double (Given) +* First rectangular coordinate on tangent plane (radians) +* eta = double (Given) +* Second rectangular coordinate on tangent plane (radians) +* ra = double (Given) +* RA spherical coordinate of star (radians) +* dec = double (Given) +* Dec spherical coordinate of star (radians) +* raz1 = double * (Returned) +* RA spherical coordinate of tangent point, solution 1 (radians) +* decz1 = double * (Returned) +* Dec spherical coordinate of tangent point, solution 1 (radians) +* raz2 = double * (Returned) +* RA spherical coordinate of tangent point, solution 2 (radians) +* decz2 = double * (Returned) +* Dec spherical coordinate of tangent point, solution 2 (radians) +* n = int * (Returned) +* number of solutions: 0 = no solutions returned (note 2) +* 1 = only the first solution is useful (note 3) +* 2 = both solutions are useful (note 3) + + +* Description: +* From the tangent plane coordinates of a star of known RA,Dec, +* determine the RA,Dec of the tangent point. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* - The RAZ1 and RAZ2 values are returned in the range 0-2pi. +* - Cases where there is no solution can only arise near the poles. +* For example, it is clearly impossible for a star at the pole +* itself to have a non-zero XI value, and hence it is +* meaningless to ask where the tangent point would have to be +* to bring about this combination of XI and DEC. +* - Also near the poles, cases can arise where there are two useful +* solutions. The argument N indicates whether the second of the +* two solutions returned is useful. N=1 indicates only one useful +* solution, the usual case; under these circumstances, the second +* solution corresponds to the "over-the-pole" case, and this is +* reflected in the values of RAZ2 and DECZ2 which are returned. +* - The DECZ1 and DECZ2 values are returned in the range +/-pi, but +* in the usual, non-pole-crossing, case, the range is +/-pi/2. +* - This routine is the spherical equivalent of the routine sla_DTPV2C. + +* History: +* 2012-02-08 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +#include + +void +palDtps2c( double xi, double eta, double ra, double dec, + double * raz1, double * decz1, + double * raz2, double * decz2, int *n) { + + double x2; + double y2; + double sd; + double cd; + double sdf; + double r2; + + x2 = xi * xi; + y2 = eta * eta; + sd = sin(dec); + cd = cos(dec); + sdf = sd * sqrt(x2 + 1. + y2); + r2 = cd * cd * (y2 + 1.) - sd * sd * x2; + if (r2 >= 0.) { + double r; + double s; + double c; + + r = sqrt(r2); + s = sdf - eta * r; + c = sdf * eta + r; + if (xi == 0. && r == 0.) { + r = 1.; + } + *raz1 = eraAnp(ra - atan2(xi, r)); + *decz1 = atan2(s, c); + r = -r; + s = sdf - eta * r; + c = sdf * eta + r; + *raz2 = eraAnp(ra - atan2(xi, r)); + *decz2 = atan2(s, c); + if (fabs(sdf) < 1.) { + *n = 1; + } else { + *n = 2; + } + } else { + *n = 0; + } + return; +} diff --git a/pal/palDtt.c b/pal/palDtt.c new file mode 100644 index 0000000..f6a3714 --- /dev/null +++ b/pal/palDtt.c @@ -0,0 +1,77 @@ +/* +*+ +* Name: +* palDtt + +* Purpose: +* Return offset between UTC and TT + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* dtt = palDtt( double utc ); + +* Arguments: +* utc = double (Given) +* UTC date as a modified JD (JD-2400000.5) + +* Returned Value: +* dtt = double +* TT-UTC in seconds + +* Description: +* Increment to be applied to Coordinated Universal Time UTC to give +* Terrestrial Time TT (formerly Ephemeris Time ET) + +* Authors: +* TIMJ: Tim Jenness (JAC, Hawaii) +* PTW: Patrick T. Wallace +* {enter_new_authors_here} + +* Notes: +* - Consider a comprehensive upgrade to use the time transformations in SOFA's time +* cookbook: http://www.iausofa.org/sofa_ts_c.pdf. +* - See eraDat for a description of error conditions when calling this function +* with a time outside of the UTC range. This behaviour differs from slaDtt. + +* History: +* 2012-02-08 (TIMJ): +* Initial version +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" + +double palDtt( double utc ) { + return 32.184 + palDat( utc ); +} diff --git a/pal/palEcmat.c b/pal/palEcmat.c new file mode 100644 index 0000000..e9d9aeb --- /dev/null +++ b/pal/palEcmat.c @@ -0,0 +1,82 @@ +/* +*+ +* Name: +* palEcmat + +* Purpose: +* Form the equatorial to ecliptic rotation matrix - IAU 2006 +* precession model. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palEcmat( double date, double rmat[3][3] ) + +* Arguments: +* date = double (Given) +* TT as Modified Julian Date (JD-2400000.5). The difference +* between TT and TDB is of the order of a millisecond or two +* (i.e. about 0.02 arc-seconds). +* rmat = double[3][3] (Returned) +* Rotation matrix + +* Description: +* The equatorial to ecliptic rotation matrix is found and returned. +* The matrix is in the sense V(ecl) = RMAT * V(equ); the +* equator, equinox and ecliptic are mean of date. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-10 (DSB): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1996 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palEcmat( double date, double rmat[3][3] ) { + +/* Mean obliquity (the angle between the ecliptic and mean equator of + date). */ + double eps0 = eraObl06( PAL__MJD0, date ); + +/* Matrix */ + palDeuler( "X", eps0, 0.0, 0.0, rmat ); + +} diff --git a/pal/palEqgal.c b/pal/palEqgal.c new file mode 100644 index 0000000..9df3d09 --- /dev/null +++ b/pal/palEqgal.c @@ -0,0 +1,118 @@ +/* +*+ +* Name: +* palEqgal + +* Purpose: +* Convert from J2000.0 equatorial coordinates to Galactic + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palEqgal ( double dr, double dd, double *dl, double *db ); + +* Arguments: +* dr = double (Given) +* J2000.0 RA (radians) +* dd = double (Given) +* J2000.0 Dec (radians +* dl = double * (Returned) +* Galactic longitude (radians). +* db = double * (Returned) +* Galactic latitude (radians). + +* Description: +* Transformation from J2000.0 equatorial coordinates +* to IAU 1958 galactic coordinates. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* The equatorial coordinates are J2000.0. Use the routine +* palGe50 if conversion to B1950.0 'FK4' coordinates is +* required. + +* See Also: +* Blaauw et al, Mon.Not.R.Astron.Soc.,121,123 (1960) + +* History: +* 2012-02-12(TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1998 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palEqgal ( double dr, double dd, double *dl, double *db ) { + + double v1[3]; + double v2[3]; + +/* +* L2,B2 system of galactic coordinates +* +* P = 192.25 RA of galactic north pole (mean B1950.0) +* Q = 62.6 inclination of galactic to mean B1950.0 equator +* R = 33 longitude of ascending node +* +* P,Q,R are degrees +* +* Equatorial to galactic rotation matrix (J2000.0), obtained by +* applying the standard FK4 to FK5 transformation, for zero proper +* motion in FK5, to the columns of the B1950 equatorial to +* galactic rotation matrix: +*/ + double rmat[3][3] = { + { -0.054875539726,-0.873437108010,-0.483834985808 }, + { +0.494109453312,-0.444829589425,+0.746982251810 }, + { -0.867666135858,-0.198076386122,+0.455983795705 } + }; + + /* Spherical to Cartesian */ + eraS2c( dr, dd, v1 ); + + /* Equatorial to Galactic */ + eraRxp( rmat, v1, v2 ); + + /* Cartesian to spherical */ + eraC2s( v2, dl, db ); + + /* Express in conventional ranges */ + *dl = eraAnp( *dl ); + *db = eraAnpm( *db ); + +} diff --git a/pal/palEtrms.c b/pal/palEtrms.c new file mode 100644 index 0000000..4484682 --- /dev/null +++ b/pal/palEtrms.c @@ -0,0 +1,106 @@ +/* +*+ +* Name: +* palEtrms + +* Purpose: +* Compute the E-terms vector + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palEtrms ( double ep, double ev[3] ); + +* Arguments: +* ep = double (Given) +* Besselian epoch +* ev = double [3] (Returned) +* E-terms as (dx,dy,dz) + +* Description: +* Computes the E-terms (elliptic component of annual aberration) +* vector. +* +* Note the use of the J2000 aberration constant (20.49552 arcsec). +* This is a reflection of the fact that the E-terms embodied in +* existing star catalogues were computed from a variety of +* aberration constants. Rather than adopting one of the old +* constants the latest value is used here. +* +* See also: +* - Smith, C.A. et al., 1989. Astr.J. 97, 265. +* - Yallop, B.D. et al., 1989. Astr.J. 97, 274. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-12 (TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1996 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" + +void palEtrms ( double ep, double ev[3] ) { + + /* Use the J2000 aberration constant */ + const double ABCONST = 20.49552; + + double t, e, e0, p, ek, cp; + + /* Julian centuries since B1950 */ + t = (ep - 1950.) * .0100002135903; + + /* Eccentricity */ + e = .01673011 - (t * 1.26e-7 + 4.193e-5) * t; + + /* Mean obliquity */ + e0 = (84404.836 - ((t * .00181 + .00319) * t + 46.8495) * t) * + PAL__DAS2R; + + /* Mean longitude of perihelion */ + p = (((t * .012 + 1.65) * t + 6190.67) * t + 1015489.951) * + PAL__DAS2R; + + /* E-terms */ + ek = e * ABCONST * PAL__DAS2R; + cp = cos(p); + ev[0] = ek * sin(p); + ev[1] = -ek * cp * cos(e0); + ev[2] = -ek * cp * sin(e0); + +} diff --git a/pal/palEvp.c b/pal/palEvp.c new file mode 100644 index 0000000..b30a3a9 --- /dev/null +++ b/pal/palEvp.c @@ -0,0 +1,110 @@ +/* +*+ +* Name: +* palEvp + +* Purpose: +* Returns the barycentric and heliocentric velocity and position of the +* Earth. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palEvp( double date, double deqx, double dvb[3], double dpb[3], +* double dvh[3], double dph[3] ) + +* Arguments: +* date = double (Given) +* TDB (loosely ET) as a Modified Julian Date (JD-2400000.5) +* deqx = double (Given) +* Julian epoch (e.g. 2000.0) of mean equator and equinox of the +* vectors returned. If deqx <= 0.0, all vectors are referred to the +* mean equator and equinox (FK5) of epoch date. +* dvb = double[3] (Returned) +* Barycentric velocity (AU/s, AU) +* dpb = double[3] (Returned) +* Barycentric position (AU/s, AU) +* dvh = double[3] (Returned) +* heliocentric velocity (AU/s, AU) +* dph = double[3] (Returned) +* Heliocentric position (AU/s, AU) + +* Description: +* Returns the barycentric and heliocentric velocity and position of the +* Earth at a given epoch, given with respect to a specified equinox. +* For information about accuracy, see the function eraEpv00. + +* Authors: +* PTW: Pat Wallace (STFC) +* {enter_new_authors_here} + +* History: +* 2012-02-13 (PTW): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2005 Patrick T. Wallace +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palEvp( double date, double deqx, double dvb[3], double dpb[3], + double dvh[3], double dph[3] ){ + +/* Local Variables; */ + int i; + double pvh[2][3], pvb[2][3], d1, d2, r[3][3]; + +/* BCRS PV-vectors. */ + eraEpv00 ( 2400000.5, date, pvh, pvb ); + +/* Was precession to another equinox requested? */ + if ( deqx > 0.0 ) { + +/* Yes: compute precession matrix from J2000.0 to deqx. */ + eraEpj2jd ( deqx, &d1, &d2 ); + eraPmat06 ( d1, d2, r ); + +/* Rotate the PV-vectors. */ + eraRxpv ( r, pvh, pvh ); + eraRxpv ( r, pvb, pvb ); + } + +/* Return the required vectors. */ + for ( i = 0; i < 3; i++ ) { + dvh[i] = pvh[1][i] / PAL__SPD; + dvb[i] = pvb[1][i] / PAL__SPD; + dph[i] = pvh[0][i]; + dpb[i] = pvb[0][i]; + } +} diff --git a/pal/palFk45z.c b/pal/palFk45z.c new file mode 100644 index 0000000..643fd74 --- /dev/null +++ b/pal/palFk45z.c @@ -0,0 +1,186 @@ +/* +*+ +* Name: +* palFk45z + +* Purpose: +* Convert B1950.0 FK4 star data to J2000.0 FK5 assuming zero +* proper motion in the FK5 frame + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palFk45z( double r1950, double d1950, double bepoch, double *r2000, +* double *d2000 ) + +* Arguments: +* r1950 = double (Given) +* B1950.0 FK4 RA at epoch (radians). +* d1950 = double (Given) +* B1950.0 FK4 Dec at epoch (radians). +* bepoch = double (Given) +* Besselian epoch (e.g. 1979.3) +* r2000 = double (Returned) +* J2000.0 FK5 RA (Radians). +* d2000 = double (Returned) +* J2000.0 FK5 Dec(Radians). + +* Description: +* Convert B1950.0 FK4 star data to J2000.0 FK5 assuming zero +* proper motion in the FK5 frame (double precision) +* +* This function converts stars from the Bessel-Newcomb, FK4 +* system to the IAU 1976, FK5, Fricke system, in such a +* way that the FK5 proper motion is zero. Because such a star +* has, in general, a non-zero proper motion in the FK4 system, +* the routine requires the epoch at which the position in the +* FK4 system was determined. +* +* The method is from Appendix 2 of Ref 1, but using the constants +* of Ref 4. + +* Notes: +* - The epoch BEPOCH is strictly speaking Besselian, but if a +* Julian epoch is supplied the result will be affected only to +* a negligible extent. +* +* - Conversion from Besselian epoch 1950.0 to Julian epoch 2000.0 +* only is provided for. Conversions involving other epochs will +* require use of the appropriate precession, proper motion, and +* E-terms routines before and/or after palFk45z is called. +* +* - In the FK4 catalogue the proper motions of stars within 10 +* degrees of the poles do not embody the differential E-term effect +* and should, strictly speaking, be handled in a different manner +* from stars outside these regions. However, given the general lack +* of homogeneity of the star data available for routine astrometry, +* the difficulties of handling positions that may have been +* determined from astrometric fields spanning the polar and non-polar +* regions, the likelihood that the differential E-terms effect was not +* taken into account when allowing for proper motion in past +* astrometry, and the undesirability of a discontinuity in the +* algorithm, the decision has been made in this routine to include the +* effect of differential E-terms on the proper motions for all stars, +* whether polar or not. At epoch 2000, and measuring on the sky rather +* than in terms of dRA, the errors resulting from this simplification +* are less than 1 milliarcsecond in position and 1 milliarcsecond per +* century in proper motion. +* +* References: +* - Aoki,S., et al, 1983. Astron.Astrophys., 128, 263. +* - Smith, C.A. et al, 1989. "The transformation of astrometric +* catalog systems to the equinox J2000.0". Astron.J. 97, 265. +* - Yallop, B.D. et al, 1989. "Transformation of mean star places +* from FK4 B1950.0 to FK5 J2000.0 using matrices in 6-space". +* Astron.J. 97, 274. +* - Seidelmann, P.K. (ed), 1992. "Explanatory Supplement to +* the Astronomical Almanac", ISBN 0-935702-68-7. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-10 (DSB): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1998 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palFk45z( double r1950, double d1950, double bepoch, double *r2000, + double *d2000 ){ + +/* Local Variables: */ + double w; + int i; + int j; + double r0[3], a1[3], v1[3], v2[6]; /* Position and position+velocity vectors */ + + +/* CANONICAL CONSTANTS (see references) */ + +/* Vector A. */ + double a[3] = { -1.62557E-6, -0.31919E-6, -0.13843E-6 }; + +/* Vectors Adot. */ + double ad[3] = { 1.245E-3, -1.580E-3, -0.659E-3 }; + +/* Matrix M (only half of which is needed here). */ + double em[6][3] = { {0.9999256782, -0.0111820611, -0.0048579477}, + {0.0111820610, 0.9999374784, -0.0000271765}, + {0.0048579479, -0.0000271474, 0.9999881997}, + {-0.000551, -0.238565, 0.435739}, + {0.238514, -0.002667, -0.008541}, + {-0.435623, 0.012254, 0.002117} }; + + +/* Spherical to Cartesian. */ + eraS2c( r1950, d1950, r0 ); + +/* Adjust vector A to give zero proper motion in FK5. */ + w = ( bepoch - 1950.0 )/PAL__PMF; + for( i = 0; i < 3; i++ ) { + a1[ i ] = a[ i ] + w*ad[ i ]; + } + +/* Remove e-terms. */ + w = r0[ 0 ]*a1[ 0 ] + r0[ 1 ]*a1[ 1 ] + r0[ 2 ]*a1[ 2 ]; + for( i = 0; i < 3; i++ ) { + v1[ i ] = r0[ i ] - a1[ i ] + w*r0[ i ]; + } + +/* Convert position vector to Fricke system. */ + for( i = 0; i < 6; i++ ) { + w = 0.0; + for( j = 0; j < 3; j++ ) { + w += em[ i ][ j ]*v1[ j ]; + } + v2[ i ] = w; + } + +/* Allow for fictitious proper motion in FK4. */ + w = ( palEpj( palEpb2d( bepoch ) ) - 2000.0 )/PAL__PMF; + for( i = 0; i < 3; i++ ) { + v2[ i ] += w*v2[ i + 3 ]; + } + +/* Revert to spherical coordinates. */ + eraC2s( v2, &w, d2000 ); + *r2000 = eraAnp( w ); +} + + diff --git a/pal/palFk524.c b/pal/palFk524.c new file mode 100644 index 0000000..7a297e6 --- /dev/null +++ b/pal/palFk524.c @@ -0,0 +1,259 @@ +/* +*+ +* Name: +* palFk524 + +* Purpose: +* Convert J2000.0 FK5 star data to B1950.0 FK4. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palFk524( double r2000, double d2000, double dr2000, double dd2000, +* double p2000, double v2000, double *r1950, double *d1950, +* double *dr1950, double *dd1950, double *p1950, double *v1950 ) + +* Arguments: +* r2000 = double (Given) +* J2000.0 FK5 RA (radians). +* d2000 = double (Given) +* J2000.0 FK5 Dec (radians). +* dr2000 = double (Given) +* J2000.0 FK5 RA proper motion (rad/Jul.yr) +* dd2000 = double (Given) +* J2000.0 FK5 Dec proper motion (rad/Jul.yr) +* p2000 = double (Given) +* J2000.0 FK5 parallax (arcsec) +* v2000 = double (Given) +* J2000.0 FK5 radial velocity (km/s, +ve = moving away) +* r1950 = double * (Returned) +* B1950.0 FK4 RA (radians). +* d1950 = double * (Returned) +* B1950.0 FK4 Dec (radians). +* dr1950 = double * (Returned) +* B1950.0 FK4 RA proper motion (rad/Jul.yr) +* dd1950 = double * (Returned) +* B1950.0 FK4 Dec proper motion (rad/Jul.yr) +* p1950 = double * (Returned) +* B1950.0 FK4 parallax (arcsec) +* v1950 = double * (Returned) +* B1950.0 FK4 radial velocity (km/s, +ve = moving away) + +* Description: +* This function converts stars from the IAU 1976, FK5, Fricke +* system, to the Bessel-Newcomb, FK4 system. The precepts +* of Smith et al (Ref 1) are followed, using the implementation +* by Yallop et al (Ref 2) of a matrix method due to Standish. +* Kinoshita's development of Andoyer's post-Newcomb precession is +* used. The numerical constants from Seidelmann et al (Ref 3) are +* used canonically. + +* Notes: +* - The proper motions in RA are dRA/dt rather than +* cos(Dec)*dRA/dt, and are per year rather than per century. +* - Note that conversion from Julian epoch 2000.0 to Besselian +* epoch 1950.0 only is provided for. Conversions involving +* other epochs will require use of the appropriate precession, +* proper motion, and E-terms routines before and/or after +* FK524 is called. +* - In the FK4 catalogue the proper motions of stars within +* 10 degrees of the poles do not embody the differential +* E-term effect and should, strictly speaking, be handled +* in a different manner from stars outside these regions. +* However, given the general lack of homogeneity of the star +* data available for routine astrometry, the difficulties of +* handling positions that may have been determined from +* astrometric fields spanning the polar and non-polar regions, +* the likelihood that the differential E-terms effect was not +* taken into account when allowing for proper motion in past +* astrometry, and the undesirability of a discontinuity in +* the algorithm, the decision has been made in this routine to +* include the effect of differential E-terms on the proper +* motions for all stars, whether polar or not. At epoch 2000, +* and measuring on the sky rather than in terms of dRA, the +* errors resulting from this simplification are less than +* 1 milliarcsecond in position and 1 milliarcsecond per +* century in proper motion. +* +* References: +* - Smith, C.A. et al, 1989. "The transformation of astrometric +* catalog systems to the equinox J2000.0". Astron.J. 97, 265. +* - Yallop, B.D. et al, 1989. "Transformation of mean star places +* from FK4 B1950.0 to FK5 J2000.0 using matrices in 6-space". +* Astron.J. 97, 274. +* - Seidelmann, P.K. (ed), 1992. "Explanatory Supplement to +* the Astronomical Almanac", ISBN 0-935702-68-7. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-13 (DSB): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "math.h" + +void palFk524( double r2000, double d2000, double dr2000, double dd2000, + double p2000, double v2000, double *r1950, double *d1950, + double *dr1950, double *dd1950, double *p1950, double *v1950 ){ + +/* Local Variables; */ + double r, d, ur, ud, px, rv; + double sr, cr, sd, cd, x, y, z, w; + double v1[ 6 ], v2[ 6 ]; + double xd, yd, zd; + double rxyz, wd, rxysq, rxy; + int i, j; + +/* Small number to avoid arithmetic problems. */ + static const double tiny = 1.0-30; + +/* Canonical constants (see references). Constant vector and matrix. */ + double a[ 6 ] = { -1.62557E-6, -0.31919E-6, -0.13843E-6, + +1.245E-3, -1.580E-3, -0.659E-3 }; + double emi[ 6 ][ 6 ] = { + { 0.9999256795, 0.0111814828, 0.0048590039, + -0.00000242389840, -0.00000002710544, -0.00000001177742}, + {-0.0111814828, 0.9999374849, -0.0000271771, + 0.00000002710544, -0.00000242392702, 0.00000000006585 }, + {-0.0048590040, -0.0000271557, 0.9999881946, + 0.00000001177742, 0.00000000006585, -0.00000242404995 }, + {-0.000551, 0.238509, -0.435614, + 0.99990432, 0.01118145, 0.00485852 }, + {-0.238560, -0.002667, 0.012254, + -0.01118145, 0.99991613, -0.00002717}, + { 0.435730, -0.008541, 0.002117, + -0.00485852, -0.00002716, 0.99996684 } }; + +/* Pick up J2000 data (units radians and arcsec/JC). */ + r = r2000; + d = d2000; + ur = dr2000*PAL__PMF; + ud = dd2000*PAL__PMF; + px = p2000; + rv = v2000; + +/* Spherical to Cartesian. */ + sr = sin( r ); + cr = cos( r ); + sd = sin( d ); + cd = cos( d ); + + x = cr*cd; + y = sr*cd; + z = sd; + + w = PAL__VF*rv*px; + + v1[ 0 ] = x; + v1[ 1 ] = y; + v1[ 2 ] = z; + + v1[ 3 ] = -ur*y - cr*sd*ud + w*x; + v1[ 4 ] = ur*x - sr*sd*ud + w*y; + v1[ 5 ] = cd*ud + w*z; + +/* Convert position+velocity vector to BN system. */ + for( i = 0; i < 6; i++ ) { + w = 0.0; + for( j = 0; j < 6; j++ ) { + w += emi[ i ][ j ]*v1[ j ]; + } + v2[ i ] = w; + } + +/* Position vector components and magnitude. */ + x = v2[ 0 ]; + y = v2[ 1 ]; + z = v2[ 2 ]; + rxyz = sqrt( x*x + y*y + z*z ); + +/* Apply E-terms to position. */ + w = x*a[ 0 ] + y*a[ 1 ] + z*a[ 2 ]; + x += a[ 0 ]*rxyz - w*x; + y += a[ 1 ]*rxyz - w*y; + z += a[ 2 ]*rxyz - w*z; + +/* Recompute magnitude. */ + rxyz = sqrt( x*x + y*y + z*z ); + +/* Apply E-terms to both position and velocity. */ + x = v2[ 0 ]; + y = v2[ 1 ]; + z = v2[ 2 ]; + w = x*a[ 0 ] + y*a[ 1 ] + z*a[ 2 ]; + wd = x*a[ 3 ] + y*a[ 4 ] + z*a[ 5 ]; + x += a[ 0 ]*rxyz - w*x; + y += a[ 1 ]*rxyz - w*y; + z += a[ 2 ]*rxyz - w*z; + xd = v2[ 3 ] + a[ 3 ]*rxyz - wd*x; + yd = v2[ 4 ] + a[ 4 ]*rxyz - wd*y; + zd = v2[ 5 ] + a[ 5 ]*rxyz - wd*z; + +/* Convert to spherical. */ + rxysq = x*x + y*y; + rxy = sqrt( rxysq ); + + if( x == 0.0 && y == 0.0 ) { + r = 0.0; + } else { + r = atan2( y, x ); + if( r < 0.0 ) r += PAL__D2PI; + } + d = atan2( z, rxy ); + + if( rxy > tiny ) { + ur = ( x*yd - y*xd )/rxysq; + ud = ( zd*rxysq - z*( x*xd + y*yd ) )/( ( rxysq + z*z )*rxy ); + } + +/* Radial velocity and parallax. */ + if( px > tiny ) { + rv = ( x*xd + y*yd + z*zd )/( px*PAL__VF*rxyz ); + px /= rxyz; + } + +/* Return results. */ + *r1950 = r; + *d1950 = d; + *dr1950 = ur/PAL__PMF; + *dd1950 = ud/PAL__PMF; + *p1950 = px; + *v1950 = rv; +} diff --git a/pal/palFk54z.c b/pal/palFk54z.c new file mode 100644 index 0000000..b902b0d --- /dev/null +++ b/pal/palFk54z.c @@ -0,0 +1,113 @@ +/* +*+ +* Name: +* palFk54z + +* Purpose: +* Convert a J2000.0 FK5 star position to B1950.0 FK4 assuming +* zero proper motion and parallax. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palFk54z( double r2000, double d2000, double bepoch, double *r1950, +* double *d1950, double *dr1950, double *dd1950 ) + +* Arguments: +* r2000 = double (Given) +* J2000.0 FK5 RA (radians). +* d2000 = double (Given) +* J2000.0 FK5 Dec (radians). +* bepoch = double (Given) +* Besselian epoch (e.g. 1950.0). +* r1950 = double * (Returned) +* B1950 FK4 RA (radians) at epoch "bepoch". +* d1950 = double * (Returned) +* B1950 FK4 Dec (radians) at epoch "bepoch". +* dr1950 = double * (Returned) +* B1950 FK4 proper motion (RA) (radians/trop.yr)). +* dr1950 = double * (Returned) +* B1950 FK4 proper motion (Dec) (radians/trop.yr)). + +* Description: +* This function converts star positions from the IAU 1976, +* FK5, Fricke system to the Bessel-Newcomb, FK4 system. + +* Notes: +* - The proper motion in RA is dRA/dt rather than cos(Dec)*dRA/dt. +* - Conversion from Julian epoch 2000.0 to Besselian epoch 1950.0 +* only is provided for. Conversions involving other epochs will +* require use of the appropriate precession functions before and +* after this function is called. +* - The FK5 proper motions, the parallax and the radial velocity +* are presumed zero. +* - It is the intention that FK5 should be a close approximation +* to an inertial frame, so that distant objects have zero proper +* motion; such objects have (in general) non-zero proper motion +* in FK4, and this function returns those fictitious proper +* motions. +* - The position returned by this function is in the B1950 +* reference frame but at Besselian epoch BEPOCH. For comparison +* with catalogues the "bepoch" argument will frequently be 1950.0. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-13 (DSB): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palFk54z( double r2000, double d2000, double bepoch, double *r1950, + double *d1950, double *dr1950, double *dd1950 ){ + +/* Local Variables: */ + double r, d, px, rv, y; + +/* FK5 equinox J2000 (any epoch) to FK4 equinox B1950 epoch B1950. */ + palFk524( r2000, d2000, 0.0, 0.0, 0.0, 0.0, &r, &d, dr1950, dd1950, + &px, &rv ); + +/* Fictitious proper motion to epoch "bepoch". */ + y = bepoch - 1950.0; + *r1950 = r + *dr1950*y; + *d1950 = d + *dd1950*y; + +} + diff --git a/pal/palGaleq.c b/pal/palGaleq.c new file mode 100644 index 0000000..d0da1ee --- /dev/null +++ b/pal/palGaleq.c @@ -0,0 +1,118 @@ +/* +*+ +* Name: +* palGaleq + +* Purpose: +* Convert from galactic to J2000.0 equatorial coordinates + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palGaleq ( double dl, double db, double *dr, double *dd ); + +* Arguments: +* dl = double (Given) +* Galactic longitude (radians). +* db = double (Given) +* Galactic latitude (radians). +* dr = double * (Returned) +* J2000.0 RA (radians) +* dd = double * (Returned) +* J2000.0 Dec (radians) + +* Description: +* Transformation from IAU 1958 galactic coordinates to +* J2000.0 equatorial coordinates. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* The equatorial coordinates are J2000.0. Use the routine +* palGe50 if conversion to B1950.0 'FK4' coordinates is +* required. + +* See Also: +* Blaauw et al, Mon.Not.R.Astron.Soc.,121,123 (1960) + +* History: +* 2012-02-12(TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1998 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palGaleq ( double dl, double db, double *dr, double *dd ) { + + double v1[3]; + double v2[3]; + +/* +* L2,B2 system of galactic coordinates +* +* P = 192.25 RA of galactic north pole (mean B1950.0) +* Q = 62.6 inclination of galactic to mean B1950.0 equator +* R = 33 longitude of ascending node +* +* P,Q,R are degrees +* +* Equatorial to galactic rotation matrix (J2000.0), obtained by +* applying the standard FK4 to FK5 transformation, for zero proper +* motion in FK5, to the columns of the B1950 equatorial to +* galactic rotation matrix: +*/ + double rmat[3][3] = { + { -0.054875539726,-0.873437108010,-0.483834985808 }, + { +0.494109453312,-0.444829589425,+0.746982251810 }, + { -0.867666135858,-0.198076386122,+0.455983795705 } + }; + + /* Spherical to Cartesian */ + eraS2c( dl, db, v1 ); + + /* Galactic to equatorial */ + eraTrxp( rmat, v1, v2 ); + + /* Cartesian to spherical */ + eraC2s( v2, dr, dd ); + + /* Express in conventional ranges */ + *dr = eraAnp( *dr ); + *dd = eraAnpm( *dd ); + +} diff --git a/pal/palGalsup.c b/pal/palGalsup.c new file mode 100644 index 0000000..86b9255 --- /dev/null +++ b/pal/palGalsup.c @@ -0,0 +1,116 @@ +/* +*+ +* Name: +* palGalsup + +* Purpose: +* Convert from galactic to supergalactic coordinates + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palGalsup ( double dl, double db, double *dsl, double *dsb ); + +* Arguments: +* dl = double (Given) +* Galactic longitude. +* db = double (Given) +* Galactic latitude. +* dsl = double * (Returned) +* Supergalactic longitude. +* dsb = double * (Returned) +* Supergalactic latitude. + +* Description: +* Transformation from IAU 1958 galactic coordinates to +* de Vaucouleurs supergalactic coordinates. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* See Also: +* - de Vaucouleurs, de Vaucouleurs, & Corwin, Second Reference +* Catalogue of Bright Galaxies, U. Texas, page 8. +* - Systems & Applied Sciences Corp., Documentation for the +* machine-readable version of the above catalogue, +* Contract NAS 5-26490. +* +* (These two references give different values for the galactic +* longitude of the supergalactic origin. Both are wrong; the +* correct value is L2=137.37.) + +* History: +* 2012-02-12(TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1999 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palGalsup ( double dl, double db, double *dsl, double *dsb ) { + + double v1[3]; + double v2[3]; + +/* +* System of supergalactic coordinates: +* +* SGL SGB L2 B2 (deg) +* - +90 47.37 +6.32 +* 0 0 - 0 +* +* Galactic to supergalactic rotation matrix: +*/ + double rmat[3][3] = { + { -0.735742574804,+0.677261296414,+0.000000000000 }, + { -0.074553778365,-0.080991471307,+0.993922590400 }, + { +0.673145302109,+0.731271165817,+0.110081262225 } + }; + + /* Spherical to Cartesian */ + eraS2c( dl, db, v1 ); + + /* Galactic to Supergalactic */ + eraRxp( rmat, v1, v2 ); + + /* Cartesian to spherical */ + eraC2s( v2, dsl, dsb ); + + /* Express in conventional ranges */ + *dsl = eraAnp( *dsl ); + *dsb = eraAnpm( *dsb ); + +} diff --git a/pal/palGeoc.c b/pal/palGeoc.c new file mode 100644 index 0000000..9954d92 --- /dev/null +++ b/pal/palGeoc.c @@ -0,0 +1,83 @@ +/* +*+ +* Name: +* palGeoc + +* Purpose: +* Convert geodetic position to geocentric + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palGeoc( double p, double h, double * r, double *z ); + +* Arguments: +* p = double (Given) +* latitude (radians) +* h = double (Given) +* height above reference spheroid (geodetic, metres) +* r = double * (Returned) +* distance from Earth axis (AU) +* z = double * (Returned) +* distance from plane of Earth equator (AU) + +* Description: +* Convert geodetic position to geocentric. + +* Authors: +* PTW: Patrick T. Wallace +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* - Geocentric latitude can be obtained by evaluating atan2(z,r) +* - Uses WGS84 reference ellipsoid and calls eraGd2gc + +* History: +* 2012-03-01 (TIMJ): +* Initial version moved from palOne2One +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2004 Patrick T. Wallace +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palGeoc ( double p, double h, double *r, double *z ) { + double xyz[3]; + const double elong = 0.0; /* Use zero longitude */ + const double AU = 1.49597870E11; + /* WGS84 looks to be the closest match */ + eraGd2gc( ERFA_WGS84, elong, p, h, xyz ); + *r = xyz[0] / (AU * cos(elong) ); + *z = xyz[2] / AU; +} diff --git a/pal/palMappa.c b/pal/palMappa.c new file mode 100644 index 0000000..8bfb61d --- /dev/null +++ b/pal/palMappa.c @@ -0,0 +1,129 @@ +/* +*+ +* Name: +* palMappa + +* Purpose: +* Compute parameters needed by palAmpqk and palMapqk. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palMappa( double eq, double date, double amprms[21] ) + +* Arguments: +* eq = double (Given) +* epoch of mean equinox to be used (Julian) +* date = double (Given) +* TDB (JD-2400000.5) +* amprms = double[21] (Returned) +* star-independent mean-to-apparent parameters: +* - (0) time interval for proper motion (Julian years) +* - (1-3) barycentric position of the Earth (AU) +* - (4-6) heliocentric direction of the Earth (unit vector) +* - (7) (grav rad Sun)*2/(Sun-Earth distance) +* - (8-10) abv: barycentric Earth velocity in units of c +* - (11) sqrt(1-v**2) where v=modulus(abv) +* - (12-20) precession/nutation (3,3) matrix + +* Description: +* Compute star-independent parameters in preparation for +* transformations between mean place and geocentric apparent place. +* +* The parameters produced by this function are required in the +* parallax, aberration, and nutation/bias/precession parts of the +* mean/apparent transformations. +* +* The reference systems and timescales used are IAU 2006. + +* Notes: +* - For date, the distinction between the required TDB and TT +* is always negligible. Moreover, for all but the most +* critical applications UTC is adequate. +* - The vector amprms(1-3) is referred to the mean equinox and +* equator of epoch eq. +* - The parameters amprms produced by this function are used by +* palAmpqk, palMapqk and palMapqkz. + +* Authors: +* PTW: Pat Wallace (STFC) +* {enter_new_authors_here} + +* History: +* 2012-02-13 (PTW): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2003 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +#include + +void palMappa( double eq, double date, double amprms[21] ){ + +/* Local constants */ + +/* Gravitational radius of the Sun x 2 (2*mu/c**2, AU) */ + const double GR2 = 2.0 * 9.87063e-9; + +/* Local Variables; */ + int i; + double ebd[ 3 ], ehd[ 3 ], eh[ 3 ], e, vn[ 3 ], vm; + +/* Initialise so that unsused values are returned holding zero */ + memset( amprms, 0, 21*sizeof( *amprms ) ); + +/* Time interval for proper motion correction. */ + amprms[ 0 ] = eraEpj( PAL__MJD0, date ) - eq; + +/* Get Earth barycentric and heliocentric position and velocity. */ + palEvp( date, eq, ebd, &rms[ 1 ], ehd, eh ); + +/* Heliocentric direction of Earth (normalized) and modulus. */ + eraPn( eh, &e, &rms[ 4 ] ); + +/* Light deflection parameter */ + amprms[7] = GR2 / e; + +/* Aberration parameters. */ + for( i = 0; i < 3; i++ ) { + amprms[ i + 8 ] = ebd[ i ]*PAL__CR; + } + eraPn( &rms[8], &vm, vn ); + amprms[ 11 ] = sqrt( 1.0 - vm*vm ); + +/* NPB matrix. */ + palPrenut( eq, date, (double(*)[ 3 ]) &rms[ 12 ] ); +} diff --git a/pal/palMapqkz.c b/pal/palMapqkz.c new file mode 100644 index 0000000..abf525b --- /dev/null +++ b/pal/palMapqkz.c @@ -0,0 +1,127 @@ +/* +*+ +* Name: +* palMapqkz + +* Purpose: +* Quick mean to apparent place. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palMapqkz( double rm, double dm, double amprms[21], +* double *ra, double *da ) + +* Arguments: +* rm = double (Given) +* Mean RA (radians). +* dm = double (Given) +* Mean Dec (radians). +* amprms = double[21] (Given) +* Star-independent mean-to-apparent parameters (see palMappa): +* (0-3) not used +* (4-6) not used +* (7) not used +* (8-10) abv: barycentric Earth velocity in units of c +* (11) sqrt(1-v**2) where v=modulus(abv) +* (12-20) precession/nutation (3,3) matrix +* ra = double * (Returned) +* Apparent RA (radians). +* da = double * (Returned) +* Apparent Dec (radians). + +* Description: +* Quick mean to apparent place: transform a star RA,dec from +* mean place to geocentric apparent place, given the +* star-independent parameters, and assuming zero parallax +* and proper motion. +* +* Use of this function is appropriate when efficiency is important +* and where many star positions, all with parallax and proper +* motion either zero or already allowed for, and all referred to +* the same equator and equinox, are to be transformed for one +* epoch. The star-independent parameters can be obtained by +* calling the palMappa function. +* +* The corresponding function for the case of non-zero parallax +* and proper motion is palMapqk. +* +* The reference systems and timescales used are IAU 2006. +* +* Strictly speaking, the function is not valid for solar-system +* sources, though the error will usually be extremely small. + +* Authors: +* PTW: Pat Wallace (STFC) +* {enter_new_authors_here} + +* History: +* 2012-02-13 (PTW): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1999 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palMapqkz ( double rm, double dm, double amprms[21], double *ra, + double *da ){ + +/* Local Variables: */ + int i; + double ab1, abv[3], p[3], w, p1dv, p1dvp1, p2[3], p3[3]; + +/* Unpack scalar and vector parameters. */ + ab1 = amprms[11]; + for( i = 0; i < 3; i++ ) { + abv[i] = amprms[i+8]; + } + +/* Spherical to x,y,z. */ + eraS2c( rm, dm, p ); + +/* Aberration. */ + p1dv = eraPdp( p, abv ); + p1dvp1 = p1dv + 1.0; + w = 1.0 + p1dv / ( ab1 + 1.0 ); + for( i = 0; i < 3; i++ ) { + p2[i] = ( ( ab1 * p[i] ) + ( w * abv[i] ) ) / p1dvp1; + } + +/* Precession and nutation. */ + eraRxp( (double(*)[3]) &rms[12], p2, p3 ); + +/* Geocentric apparent RA,dec. */ + eraC2s( p3, ra, da ); + *ra = eraAnp( *ra ); +} diff --git a/pal/palOne2One.c b/pal/palOne2One.c new file mode 100644 index 0000000..ea70f3b --- /dev/null +++ b/pal/palOne2One.c @@ -0,0 +1,277 @@ +/* +*+ +* Name: +* palOne2One + +* Purpose: +* File containing simple PAL wrappers for SLA routines that are identical in SOFA + +* Invocation: +* Matches SLA API + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Description: +* Some SOFA/ERFA routines are identical to their SLA counterparts. PAL provides +* direct counterparts to these although it is generally a better idea to +* use the SOFA/ERFA routine directly in new code. +* +* The PAL routines with direct equivalents in SOFA/ERFA are: +* - palCldj +* - palDbear +* - palDaf2r +* - palDav2m +* - palDcc2s +* - palDcs2c +* - palDd2tf +* - palDimxv +* - palDm2av +* - palDjcl +* - palDmxm +* - palDmxv +* - palDpav +* - palDr2af +* - palDr2tf +* - palDranrm +* - palDsep +* - palDsepv +* - palDtf2d +* - palDtf2r +* - palDvdv +* - palDvn +* - palDvxv +* - palEpb +* - palEpb2d +* - palEpj +* - palEpj2d +* - palEqeqx +* - palFk5hz +* - palGmst +* - palGmsta +* - palHfk5z +* - palRefcoq + +* Authors: +* TIMJ: Tim Jenness (JAC, Hawaii) +* DSB: David S Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* - Do not call these functions from other PAL functions. Always use +* the SOFA/ERFA routines directly in new code. +* - These are implemented as real functions rather than C preprocessor +* macros so there may be a performance penalty in using the PAL +* version instead of the SOFA/ERFA version. +* - Routines that take MJDs have SOFA/ERFA equivalents that have an explicit +* MJD offset included. +* - palEqeqx, palGmst and palGmsta use the IAU 2006 precession model. + +* History: +* 2012-02-10 (TIMJ): +* Initial version +* Adapted with permission from the Fortran SLALIB library. +* 2012-03-23 (TIMJ): +* Update prologue. +* 2012-05-09 (DSBJ): +* Move palDrange into a separate file. +* 2014-07-15 (TIMJ): +* SOFA now has palRefcoq equivalent. +* {enter_further_changes_here} + +* Copyright: +* Copyeight (C) 2014 Tim Jenness +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palCldj ( int iy, int im, int id, double *djm, int *j ) { + double djm0; + *j = eraCal2jd( iy, im, id, &djm0, djm ); +} + +double palDbear ( double a1, double b1, double a2, double b2 ) { + return eraPas( a1, b1, a2, b2 ); +} + +/* Arguments differ slightly. Assumes that the sign is always positive + and dealt with externally. */ +void palDaf2r ( int ideg, int iamin, double asec, double *rad, int *j ) { + *j = eraAf2a( ' ', ideg, iamin, asec, rad ); +} + +void palDav2m ( double axvec[3], double rmat[3][3] ) { + eraRv2m( axvec, rmat ); +} + +void palDcc2s ( double v[3], double *a, double *b ) { + eraC2s( v, a, b ); +} + +void palDcs2c ( double a, double b, double v[3] ) { + eraS2c( a, b, v ); +} + +void palDd2tf ( int ndp, double days, char *sign, int ihmsf[4] ) { + eraD2tf( ndp, days, sign, ihmsf ); +} + +void palDimxv ( double dm[3][3], double va[3], double vb[3] ) { + eraTrxp( dm, va, vb ); +} + +void palDm2av ( double rmat[3][3], double axvec[3] ) { + eraRm2v( rmat, axvec ); +} + +/* Requires additional SLA MJD reference date */ +void palDjcl ( double djm, int *iy, int *im, int *id, double *fd, int *j ) { + *j = eraJd2cal( PAL__MJD0, djm, iy, im, id, fd ); +} + +void palDmxm ( double a[3][3], double b[3][3], double c[3][3] ) { + eraRxr( a, b, c ); +} + +void palDmxv ( double dm[3][3], double va[3], double vb[3] ) { + eraRxp( dm, va, vb ); +} + +double palDpav ( double v1[3], double v2[3] ) { + return eraPap( v1, v2 ); +} + +void palDr2af ( int ndp, double angle, char *sign, int idmsf[4] ) { + eraA2af( ndp, angle, sign, idmsf ); +} + +void palDr2tf( int ndp, double angle, char *sign, int ihmsf[4] ) { + eraA2tf( ndp, angle, sign, ihmsf ); +} + +double palDranrm ( double angle ) { + return eraAnp( angle ); +} + +double palDsep ( double a1, double b1, double a2, double b2 ) { + return eraSeps( a1, b1, a2, b2 ); +} + +double palDsepv ( double v1[3], double v2[3] ) { + return eraSepp( v1, v2 ); +} + +/* Assumes that the sign is always positive and is dealt with externally */ +void palDtf2d ( int ihour, int imin, double sec, double *days, int *j ) { + *j = eraTf2d( ' ', ihour, imin, sec, days ); +} + +/* Assumes that the sign is dealt with outside this routine */ +void palDtf2r ( int ihour, int imin, double sec, double *rad, int *j ) { + *j = eraTf2a( ' ', ihour, imin, sec, rad ); +} + +double palDvdv ( double va[3], double vb[3] ) { + return eraPdp( va, vb ); +} + +/* Note that the arguments are flipped */ +void palDvn ( double v[3], double uv[3], double *vm ) { + eraPn( v, vm, uv ); +} + +void palDvxv ( double va[3], double vb[3], double vc[3] ) { + eraPxp( va, vb, vc ); +} + +/* Requires additional SLA MJD reference date */ +double palEpb ( double date ) { + return eraEpb( PAL__MJD0, date ); +} + +double palEpb2d ( double epb ) { + double djm0, djm; + eraEpb2jd( epb, &djm0, &djm ); + return djm; +} + +/* Requires additional SLA MJD reference date */ +double palEpj ( double date ) { + return eraEpj( PAL__MJD0, date ); +} + +double palEpj2d ( double epj ) { + double djm0, djm; + eraEpj2jd( epj, &djm0, &djm ); + return djm; +} + +/* Requires additional SLA MJD reference date */ +double palEqeqx ( double date ) { + return eraEe06a( PAL__MJD0, date ); +} + +/* Do not use palEvp just yet */ + +void palFk5hz ( double r5, double d5, double epoch, + double *rh, double *dh ) { + /* Need to convert epoch to Julian date first */ + double date1, date2; + eraEpj2jd( epoch, &date1, &date2 ); + eraFk5hz( r5, d5, date1, date2, rh, dh ); +} + +/* Note that SOFA/ERFA has more accurate time arguments + and we use the 2006 precession model */ +double palGmst ( double ut1 ) { + return eraGmst06( PAL__MJD0, ut1, PAL__MJD0, ut1 ); +} + +/* Slightly better but still not as accurate as SOFA/ERFA */ + +double palGmsta( double date, double ut ) { + date += PAL__MJD0; + return eraGmst06( date, ut, date, ut ); +} + +void palHfk5z ( double rh, double dh, double epoch, + double *r5, double *d5, double *dr5, double *dd5 ) { + /* Need to convert epoch to Julian date first */ + double date1, date2; + eraEpj2jd( epoch, &date1, &date2 ); + eraHfk5z( rh, dh, date1, date2, r5, d5, dr5, dd5 ); +} + +void palRefcoq ( double tdk, double pmb, double rh, double wl, + double *refa, double *refb ) { + /* Note that SLA (and therefore PAL) uses units of kelvin + but SOFA/ERFA uses deg C */ + eraRefco( pmb, tdk - 273.15, rh, wl, refa, refb ); +} diff --git a/pal/palPrebn.c b/pal/palPrebn.c new file mode 100644 index 0000000..989ce59 --- /dev/null +++ b/pal/palPrebn.c @@ -0,0 +1,98 @@ +/* +*+ +* Name: +* palPrebn + +* Purpose: +* Generate the matrix of precession between two objects (old) + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palPrebn ( double bep0, double bep1, double rmatp[3][3] ); + +* Arguments: +* bep0 = double (Given) +* Beginning Besselian epoch. +* bep1 = double (Given) +* Ending Besselian epoch +* rmatp = double[3][3] (Returned) +* precession matrix in the sense V(BEP1) = RMATP * V(BEP0) + +* Description: +* Generate the matrix of precession between two epochs, +* using the old, pre-IAU1976, Bessel-Newcomb model, using +* Kinoshita's formulation + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* See Also: +* Kinoshita, H. (1975) 'Formulas for precession', SAO Special +* Report No. 364, Smithsonian Institution Astrophysical +* Observatory, Cambridge, Massachusetts. + +* History: +* 2012-02-12(TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1996 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" + +void palPrebn ( double bep0, double bep1, double rmatp[3][3] ) { + + double t,bigt, zeta, theta, z, tas2r, w; + + /* Interval between basic epoch B1850.0 and beginning epoch in TC */ + bigt = (bep0-1850)/100.; + + /* Interval over which precession required, in tropical centuries */ + t = (bep1-bep0)/100.; + + /* Euler angles */ + tas2r = t * PAL__DAS2R; + w = 2303.5548 + ( 1.39720 + 0.000059 * bigt) * bigt; + + zeta = ( w + ( 0.30242 - 0.000269 * bigt + 0.017996 * t ) * t ) * tas2r; + z = ( w + ( 1.09478 + 0.000387 * bigt + 0.018324 * t ) * t ) * tas2r; + theta = ( 2005.1125 + ( -0.85294 - 0.000365 * bigt ) * bigt + + (-0.42647 - 0.000365 * bigt - 0.041802 * t ) * t ) * tas2r; + + /* Rotation matrix */ + palDeuler("ZYZ", -zeta, theta, -z, rmatp); + +} diff --git a/pal/palPrec.c b/pal/palPrec.c new file mode 100644 index 0000000..678770d --- /dev/null +++ b/pal/palPrec.c @@ -0,0 +1,107 @@ +/* +*+ +* Name: +* palPrec + +* Purpose: +* Form the matrix of precession between two epochs (IAU 2006) + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palPrec( double ep0, double ep1, double rmatp[3][3] ) + +* Arguments: +* ep0 = double (Given) +* Beginning epoch +* ep1 = double (Given) +* Ending epoch +* rmatp = double[3][3] (Returned) +* Precession matrix + +* Description: +* The IAU 2006 precession matrix from ep0 to ep1 is found and +* returned. The matrix is in the sense V(EP1) = RMATP * V(EP0). +* The epochs are TDB (loosely TT) Julian epochs. +* +* Though the matrix method itself is rigorous, the precession +* angles are expressed through canonical polynomials which are +* valid only for a limited time span of a few hundred years around +* the current epoch. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-10 (DSB): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1996 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palPrec( double ep0, double ep1, double rmatp[3][3] ){ + +/* Local Variables: */ + double rmatq[3][3]; + double ep0_days; + double ep1_days; + +/* Convert supplied dates to days since J2000 */ + ep0_days = ( ep0 - 2000.0 )*ERFA_DJY; + ep1_days = ( ep1 - 2000.0 )*ERFA_DJY; + +/* If beginning epoch is J2000, just return the rotation matrix from + J2000 to EP1. */ + if( ep0 == 2000.0 ) { + eraPmat06( ERFA_DJ00, ep1_days, rmatp ); + +/* If end epoch is J2000, get the rotation matrix from J2000 to EP0 and + then transpose it to get the rotation matrix from EP0 to J2000. */ + } else if( ep1 == 2000.0 ) { + eraPmat06( ERFA_DJ00, ep0_days, rmatp ); + eraTr( rmatp, rmatp ); + +/* Otherwise. get the two matrices used above and multiply them + together. */ + } else { + eraPmat06( ERFA_DJ00, ep0_days, rmatp ); + eraTr( rmatp, rmatp ); + eraPmat06( ERFA_DJ00, ep1_days, rmatq ); + eraRxr( rmatp, rmatq, rmatp ); + } + +} diff --git a/pal/palPrenut.c b/pal/palPrenut.c new file mode 100644 index 0000000..04e36f3 --- /dev/null +++ b/pal/palPrenut.c @@ -0,0 +1,111 @@ +/* +*+ +* Name: +* palPrenut + +* Purpose: +* Form the matrix of bias-precession-nutation (IAU 2006/2000A) + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palPrenut( double epoch, double date, double rmatpn[3][3] ) + +* Arguments: +* epoch = double (Returned) +* Julian epoch for mean coordinates. +* date = double (Returned) +* Modified Julian Date (JD-2400000.5) for true coordinates. +* rmatpn = double[3][3] (Returned) +* combined NPB matrix + +* Description: +* Form the matrix of bias-precession-nutation (IAU 2006/2000A). +* The epoch and date are TT (but TDB is usually close enough). +* The matrix is in the sense v(true) = rmatpn * v(mean). + +* Authors: +* PTW: Pat Wallace (STFC) +* {enter_new_authors_here} + +* History: +* 2012-02-10 (PTW): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palPrenut ( double epoch, double date, double rmatpn[3][3] ){ + +/* Local Variables: */ + double bpa; + double bpia; + double bqa; + double chia; + double d1; + double d2; + double eps0; + double epsa; + double gam; + double oma; + double pa; + double phi; + double pia; + double psi; + double psia; + double r1[3][3]; + double r2[3][3]; + double thetaa; + double za; + double zetaa; + +/* Specified Julian epoch as a 2-part JD. */ + eraEpj2jd( epoch, &d1, &d2 ); + +/* P matrix, from specified epoch to J2000.0. */ + eraP06e( d1, d2, &eps0, &psia, &oma, &bpa, &bqa, &pia, &bpia, &epsa, + &chia, &za, &zetaa, &thetaa, &pa, &gam, &phi, &psi ); + eraIr( r1 ); + eraRz( -chia, r1 ); + eraRx( oma, r1 ); + eraRz( psia, r1 ); + eraRx( -eps0, r1 ); + +/* NPB matrix, from J2000.0 to date. */ + eraPnm06a( PAL__MJD0, date, r2 ); + +/* NPB matrix, from specified epoch to date. */ + eraRxr( r2, r1, rmatpn ); +} diff --git a/pal/palPvobs.c b/pal/palPvobs.c new file mode 100644 index 0000000..763d202 --- /dev/null +++ b/pal/palPvobs.c @@ -0,0 +1,108 @@ +/* +*+ +* Name: +* palPvobs + +* Purpose: +* Position and velocity of an observing station. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* palPvobs( double p, double h, double stl, double pv[6] ) + +* Arguments: +* p = double (Given) +* Latitude (geodetic, radians). +* h = double (Given) +* Height above reference spheroid (geodetic, metres). +* stl = double (Given) +* Local apparent sidereal time (radians). +* pv = double[ 6 ] (Returned) +* position/velocity 6-vector (AU, AU/s, true equator +* and equinox of date). + +* Description: +* Returns the position and velocity of an observing station. + +* Notes: +* - The WGS84 reference ellipsoid is used. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-16 (DSB): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "palmac.h" +#include "palmac.h" +#include "pal1sofa.h" + +void palPvobs( double p, double h, double stl, double pv[6] ){ + +/* Local Variables: */ + double xyz[3], z, r, s, c, v; + +/* Geodetic to geocentric conversion (WGS84 reference ellipsoid). */ + eraGd2gc( ERFA_WGS84, 0.0, p, h, xyz ); + +/* Convert from metres to AU */ + r = xyz[ 0 ]/ERFA_DAU; + z = xyz[ 2 ]/ERFA_DAU; + +/* Functions of ST. */ + s = sin( stl ); + c = cos( stl ); + +/* Speed. */ + v = PAL__SR*r; + +/* Position. */ + pv[ 0 ] = r*c; + pv[ 1 ] = r*s; + pv[ 2 ] = z; + +/* Velocity. */ + pv[ 3 ] = -v*s; + pv[ 4 ] = v*c; + pv[ 5 ] = 0.0; + +} + + diff --git a/pal/palRvgalc.c b/pal/palRvgalc.c new file mode 100644 index 0000000..2f01576 --- /dev/null +++ b/pal/palRvgalc.c @@ -0,0 +1,111 @@ +/* +*+ +* Name: +* palRvgalc + +* Purpose: +* Velocity component in a given direction due to the rotation +* of the Galaxy. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* double palRvgalc( double r2000, double d2000 ) + +* Arguments: +* r2000 = double (Given) +* J2000.0 mean RA (radians) +* d2000 = double (Given) +* J2000.0 mean Dec (radians) + +* Returned Value: +* Component of dynamical LSR motion in direction R2000,D2000 (km/s). + +* Description: +* This function returns the Component of dynamical LSR motion in +* the direction of R2000,D2000. The result is +ve when the dynamical +* LSR is receding from the given point on the sky. +* +* Notes: +* - The Local Standard of Rest used here is a point in the +* vicinity of the Sun which is in a circular orbit around +* the Galactic centre. Sometimes called the "dynamical" LSR, +* it is not to be confused with a "kinematical" LSR, which +* is the mean standard of rest of star catalogues or stellar +* populations. +* +* Reference: +* - The orbital speed of 220 km/s used here comes from Kerr & +* Lynden-Bell (1986), MNRAS, 221, p1023. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-16 (DSB): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +double palRvgalc( double r2000, double d2000 ){ + +/* Local Variables: */ + double vb[ 3 ]; + +/* +* LSR velocity due to Galactic rotation +* +* Speed = 220 km/s +* Apex = L2,B2 90deg, 0deg +* = RA,Dec 21 12 01.1 +48 19 47 J2000.0 +* +* This is expressed in the form of a J2000.0 x,y,z vector: +* +* VA(1) = X = -SPEED*COS(RA)*COS(DEC) +* VA(2) = Y = -SPEED*SIN(RA)*COS(DEC) +* VA(3) = Z = -SPEED*SIN(DEC) +*/ + + double va[ 3 ] = { -108.70408, +97.86251, -164.33610 }; + +/* Convert given J2000 RA,Dec to x,y,z. */ + eraS2c( r2000, d2000, vb ); + +/* Compute dot product with LSR motion vector. */ + return eraPdp( va, vb ); +} diff --git a/pal/palRvlg.c b/pal/palRvlg.c new file mode 100644 index 0000000..255801e --- /dev/null +++ b/pal/palRvlg.c @@ -0,0 +1,106 @@ +/* +*+ +* Name: +* palRvlg + +* Purpose: +* Velocity component in a given direction due to Galactic rotation +* and motion of the local group. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* double palRvlg( double r2000, double d2000 ) + +* Arguments: +* r2000 = double (Given) +* J2000.0 mean RA (radians) +* d2000 = double (Given) +* J2000.0 mean Dec (radians) + +* Returned Value: +* Component of SOLAR motion in direction R2000,D2000 (km/s). + +* Description: +* This function returns the velocity component in a given +* direction due to the combination of the rotation of the +* Galaxy and the motion of the Galaxy relative to the mean +* motion of the local group. The result is +ve when the Sun +* is receding from the given point on the sky. +* +* Reference: +* - IAU Trans 1976, 168, p201. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-16 (DSB): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +double palRvlg( double r2000, double d2000 ){ + +/* Local Variables: */ + double vb[ 3 ]; + +/* +* +* Solar velocity due to Galactic rotation and translation +* +* Speed = 300 km/s +* +* Apex = L2,B2 90deg, 0deg +* = RA,Dec 21 12 01.1 +48 19 47 J2000.0 +* +* This is expressed in the form of a J2000.0 x,y,z vector: +* +* VA(1) = X = -SPEED*COS(RA)*COS(DEC) +* VA(2) = Y = -SPEED*SIN(RA)*COS(DEC) +* VA(3) = Z = -SPEED*SIN(DEC) +*/ + + double va[ 3 ] = { -148.23284, +133.44888, -224.09467 }; + +/* Convert given J2000 RA,Dec to x,y,z. */ + eraS2c( r2000, d2000, vb ); + +/* Compute dot product with Solar motion vector. */ + return eraPdp( va, vb ); +} diff --git a/pal/palRvlsrd.c b/pal/palRvlsrd.c new file mode 100644 index 0000000..4c93426 --- /dev/null +++ b/pal/palRvlsrd.c @@ -0,0 +1,116 @@ +/* +*+ +* Name: +* palRvlsrd + +* Purpose: +* Velocity component in a given direction due to the Sun's motion +* with respect to the dynamical Local Standard of Rest. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* double palRvlsrd( double r2000, double d2000 ) + +* Arguments: +* r2000 = double (Given) +* J2000.0 mean RA (radians) +* d2000 = double (Given) +* J2000.0 mean Dec (radians) + +* Returned Value: +* Component of "peculiar" solar motion in direction R2000,D2000 (km/s). + +* Description: +* This function returns the velocity component in a given direction +* due to the Sun's motion with respect to the dynamical Local Standard +* of Rest. The result is +ve when the Sun is receding from the given +* point on the sky. + +* Notes: +* - The Local Standard of Rest used here is the "dynamical" LSR, +* a point in the vicinity of the Sun which is in a circular orbit +* around the Galactic centre. The Sun's motion with respect to the +* dynamical LSR is called the "peculiar" solar motion. +* - There is another type of LSR, called a "kinematical" LSR. A +* kinematical LSR is the mean standard of rest of specified star +* catalogues or stellar populations, and several slightly different +* kinematical LSRs are in use. The Sun's motion with respect to an +* agreed kinematical LSR is known as the "standard" solar motion. +* To obtain a radial velocity correction with respect to an adopted +* kinematical LSR use the routine sla_RVLSRK. + +* Reference: +* - Delhaye (1965), in "Stars and Stellar Systems", vol 5, p73. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-16 (DSB): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +double palRvlsrd( double r2000, double d2000 ){ + +/* Local Variables: */ + double vb[ 3 ]; + +/* +* Peculiar solar motion from Delhaye 1965: in Galactic Cartesian +* coordinates (+9,+12,+7) km/s. This corresponds to about 16.6 km/s +* towards Galactic coordinates L2 = 53 deg, B2 = +25 deg, or RA,Dec +* 17 49 58.7 +28 07 04 J2000. +* +* The solar motion is expressed here in the form of a J2000.0 +* equatorial Cartesian vector: +* +* VA(1) = X = -SPEED*COS(RA)*COS(DEC) +* VA(2) = Y = -SPEED*SIN(RA)*COS(DEC) +* VA(3) = Z = -SPEED*SIN(DEC) +*/ + + double va[ 3 ] = { +0.63823, +14.58542, -7.80116 }; + +/* Convert given J2000 RA,Dec to x,y,z. */ + eraS2c( r2000, d2000, vb ); + +/* Compute dot product with Solar motion vector. */ + return eraPdp( va, vb ); +} diff --git a/pal/palRvlsrk.c b/pal/palRvlsrk.c new file mode 100644 index 0000000..13bdcef --- /dev/null +++ b/pal/palRvlsrk.c @@ -0,0 +1,116 @@ +/* +*+ +* Name: +* palRvlsrk + +* Purpose: +* Velocity component in a given direction due to the Sun's motion +* with respect to an adopted kinematic Local Standard of Rest. + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* double palRvlsrk( double r2000, double d2000 ) + +* Arguments: +* r2000 = double (Given) +* J2000.0 mean RA (radians) +* d2000 = double (Given) +* J2000.0 mean Dec (radians) + +* Returned Value: +* Component of "standard" solar motion in direction R2000,D2000 (km/s). + +* Description: +* This function returns the velocity component in a given direction +* due to the Sun's motion with respect to an adopted kinematic +* Local Standard of Rest. The result is +ve when the Sun is receding +* from the given point on the sky. + +* Notes: +* - The Local Standard of Rest used here is one of several +* "kinematical" LSRs in common use. A kinematical LSR is the mean +* standard of rest of specified star catalogues or stellar +* populations. The Sun's motion with respect to a kinematical LSR +* is known as the "standard" solar motion. +* - There is another sort of LSR, the "dynamical" LSR, which is a +* point in the vicinity of the Sun which is in a circular orbit +* around the Galactic centre. The Sun's motion with respect to +* the dynamical LSR is called the "peculiar" solar motion. To +* obtain a radial velocity correction with respect to the +* dynamical LSR use the routine sla_RVLSRD. + +* Reference: +* - Delhaye (1965), in "Stars and Stellar Systems", vol 5, p73. + +* Authors: +* PTW: Pat Wallace (STFC) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* History: +* 2012-02-16 (DSB): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +double palRvlsrk( double r2000, double d2000 ){ + +/* Local Variables: */ + double vb[ 3 ]; + +/* +* Standard solar motion (from Methods of Experimental Physics, ed Meeks, +* vol 12, part C, sec 6.1.5.2, p281): +* +* 20 km/s towards RA 18h Dec +30d (1900). +* +* The solar motion is expressed here in the form of a J2000.0 +* equatorial Cartesian vector: +* +* VA(1) = X = -SPEED*COS(RA)*COS(DEC) +* VA(2) = Y = -SPEED*SIN(RA)*COS(DEC) +* VA(3) = Z = -SPEED*SIN(DEC) +*/ + + double va[ 3 ] = { -0.29000, +17.31726, -10.00141 }; + +/* Convert given J2000 RA,Dec to x,y,z. */ + eraS2c( r2000, d2000, vb ); + +/* Compute dot product with Solar motion vector. */ + return eraPdp( va, vb ); +} diff --git a/pal/palSubet.c b/pal/palSubet.c new file mode 100644 index 0000000..ada122a --- /dev/null +++ b/pal/palSubet.c @@ -0,0 +1,112 @@ +/* +*+ +* Name: +* palSubet + +* Purpose: +* Remove the E-terms from a pre IAU 1976 catalogue RA,Dec + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palSubet ( double rc, double dc, double eq, +* double *rm, double *dm ); + +* Arguments: +* rc = double (Given) +* RA with E-terms included (radians) +* dc = double (Given) +* Dec with E-terms included (radians) +* eq = double (Given) +* Besselian epoch of mean equator and equinox +* rm = double * (Returned) +* RA without E-terms (radians) +* dm = double * (Returned) +* Dec without E-terms (radians) + +* Description: +* Remove the E-terms (elliptic component of annual aberration) +* from a pre IAU 1976 catalogue RA,Dec to give a mean place. + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* Most star positions from pre-1984 optical catalogues (or +* derived from astrometry using such stars) embody the +* E-terms. This routine converts such a position to a +* formal mean place (allowing, for example, comparison with a +* pulsar timing position). + +* See Also: +* Explanatory Supplement to the Astronomical Ephemeris, +* section 2D, page 48. + +* History: +* 2012-02-12(TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palSubet ( double rc, double dc, double eq, double *rm, double *dm ) { + double a[3]; /* The E-terms */ + double v[3]; + double f; + int i; + + /* Note the preference for IAU routines */ + + /* Retrieve the E-terms */ + palEtrms( eq, a ); + + /* Spherical to Cartesian */ + eraS2c( rc, dc, v ); + + /* Include the E-terms */ + f = 1.0 + eraPdp( v, a ); + for (i=0; i<3; i++) { + v[i] = f*v[i] - a[i]; + } + + /* Cartesian to spherical */ + eraC2s( v, rm, dm ); + + /* Bring RA into conventional range */ + *rm = eraAnp( *rm ); + +} diff --git a/pal/palSupgal.c b/pal/palSupgal.c new file mode 100644 index 0000000..a8a9a03 --- /dev/null +++ b/pal/palSupgal.c @@ -0,0 +1,116 @@ +/* +*+ +* Name: +* palSupgal + +* Purpose: +* Convert from supergalactic to galactic coordinates + +* Language: +* Starlink ANSI C + +* Type of Module: +* Library routine + +* Invocation: +* void palSupgal ( double dsl, double dsb, double *dl, double *db ); + +* Arguments: +* dsl = double (Given) +* Supergalactic longitude. +* dsb = double (Given) +* Supergalactic latitude. +* dl = double * (Returned) +* Galactic longitude. +* db = double * (Returned) +* Galactic latitude. + +* Description: +* Transformation from de Vaucouleurs supergalactic coordinates +* to IAU 1958 galactic coordinates + +* Authors: +* PTW: Pat Wallace (STFC) +* TIMJ: Tim Jenness (JAC, Hawaii) +* {enter_new_authors_here} + +* See Also: +* - de Vaucouleurs, de Vaucouleurs, & Corwin, Second Reference +* Catalogue of Bright Galaxies, U. Texas, page 8. +* - Systems & Applied Sciences Corp., Documentation for the +* machine-readable version of the above catalogue, +* Contract NAS 5-26490. +* +* (These two references give different values for the galactic +* longitude of the supergalactic origin. Both are wrong; the +* correct value is L2=137.37.) + +* History: +* 2012-02-12(TIMJ): +* Initial version with documentation taken from Fortran SLA +* Adapted with permission from the Fortran SLALIB library. +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 1995 Rutherford Appleton Laboratory +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +#include "pal.h" +#include "pal1sofa.h" + +void palSupgal ( double dsl, double dsb, double *dl, double *db ) { + + double v1[3]; + double v2[3]; + +/* +* System of supergalactic coordinates: +* +* SGL SGB L2 B2 (deg) +* - +90 47.37 +6.32 +* 0 0 - 0 +* +* Galactic to supergalactic rotation matrix: +*/ + double rmat[3][3] = { + { -0.735742574804,+0.677261296414,+0.000000000000 }, + { -0.074553778365,-0.080991471307,+0.993922590400 }, + { +0.673145302109,+0.731271165817,+0.110081262225 } + }; + + /* Spherical to Cartesian */ + eraS2c( dsl, dsb, v1 ); + + /* Supergalactic to galactic */ + eraTrxp( rmat, v1, v2 ); + + /* Cartesian to spherical */ + eraC2s( v2, dl, db ); + + /* Express in conventional ranges */ + *dl = eraAnp( *dl ); + *db = eraAnpm( *db ); + +} diff --git a/pal/palmac.h b/pal/palmac.h new file mode 100644 index 0000000..207b843 --- /dev/null +++ b/pal/palmac.h @@ -0,0 +1,136 @@ +#ifndef PALMACDEF +#define PALMACDEF + +/* +*+ +* Name: +* palmac.h + +* Purpose: +* Macros used by the PAL library + +* Language: +* Starlink ANSI C + +* Type of Module: +* Include file + +* Description: +* A collection of useful macros provided and used by the PAL library + +* Authors: +* TIMJ: Tim Jenness (JAC, Hawaii) +* DSB: David Berry (JAC, Hawaii) +* {enter_new_authors_here} + +* Notes: +* + +* History: +* 2012-02-08 (TIMJ): +* Initial version. +* Adapted with permission from the Fortran SLALIB library. +* 2012-04-13 (DSB): +* Added PAL__DR2H and PAL__DR2S +* {enter_further_changes_here} + +* Copyright: +* Copyright (C) 2012 Science and Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Bugs: +* {note_any_bugs_here} +*- +*/ + +/* Pi */ +static const double PAL__DPI = 3.1415926535897932384626433832795028841971693993751; + +/* 2Pi */ +static const double PAL__D2PI = 6.2831853071795864769252867665590057683943387987502; + +/* pi/2: 90 degrees in radians */ +static const double PAL__DPIBY2 = 1.5707963267948966192313216916397514420985846996876; + +/* pi/180: degrees to radians */ +static const double PAL__DD2R = 0.017453292519943295769236907684886127134428718885417; + +/* Radians to arcseconds */ +static const double PAL__DR2AS = 2.0626480624709635515647335733077861319665970087963e5; + +/* Arcseconds to radians */ +static const double PAL__DAS2R = 4.8481368110953599358991410235794797595635330237270e-6; + +/* Radians to degrees */ +static const double PAL__DR2D = 57.295779513082320876798154814105170332405472466564; + +/* Hours to radians */ +static const double PAL__DH2R = 0.26179938779914943653855361527329190701643078328126; + +/* Radians to hours */ +static const double PAL__DR2H = 3.8197186342054880584532103209403446888270314977709; + +/* Radians to seconds of time */ +static const double PAL__DR2S = 1.3750987083139757010431557155385240879777313391975e4; + +/* Seconds of time to radians */ +static const double PAL__DS2R = 7.272205216643039903848712e-5; + +/* Start of SLA modified Julian date epoch */ +static const double PAL__MJD0 = 2400000.5; + +/* Light time for 1 AU (sec) */ +static const double PAL__CR = 499.004782; + +/* Seconds per day */ +static const double PAL__SPD = 86400.0; + +/* Km per sec to AU per tropical century + = 86400 * 36524.2198782 / 149597870 */ +static const double PAL__VF = 21.095; + +/* Radians per year to arcsec per century. This needs to be a macro since it + is an expression including other constants. */ +#define PAL__PMF (100.0*60.0*60.0*360.0/PAL__D2PI); + +/* Mean sidereal rate - the rotational angular velocity of Earth + in radians/sec from IERS Conventions (2003). */ +static const double PAL__SR = 7.2921150e-5; + +/* Gaussian gravitational constant (exact) */ +static const double PAL__GCON = 0.01720209895; + +/* DINT(A) - truncate to nearest whole number towards zero (double) */ +#define DINT(A) ((A)<0.0?ceil(A):floor(A)) + +/* DNINT(A) - round to nearest whole number (double) */ +#define DNINT(A) ((A)<0.0?ceil((A)-0.5):floor((A)+0.5)) + +/* DMAX(A,B) - return maximum value - evaluates arguments multiple times */ +#define DMAX(A,B) ((A) > (B) ? (A) : (B) ) + +/* DMIN(A,B) - return minimum value - evaluates arguments multiple times */ +#define DMIN(A,B) ((A) < (B) ? (A) : (B) ) + +/* We actually prefer to use C99 copysign() but we define this here as a backup + but it will not detect -0.0 so is not useful for palDfltin. */ +/* DSIGN(A,B) - magnitude of A with sign of B (double) */ +#define DSIGN(A,B) ((B)<0.0?-fabs(A):fabs(A)) + +#endif diff --git a/pal2ast.h b/pal2ast.h new file mode 100644 index 0000000..83bab9a --- /dev/null +++ b/pal2ast.h @@ -0,0 +1,134 @@ +#if !defined( PAL2AST_INCLUDED ) /* Include this file only once */ +#define PAL2AST_INCLUDED +/* +* Name: +* pal2ast.h + +* Type: +* C include file. + +* Purpose: +* Defines new names for symbols exported by the PAL library. + +* Invocation: +* #include "pal2ast.h" + +* Description: +* This include file defines a new name for each public function +* defined by the pal library. The names defined by PAL itself are +* of the form "palXxx" (e.g. palPvobs) - this include file defines +* a macro that translates each such name to the form "astPalXxx" +* (e.g. astPalPvobs). This is done so that the names do not clash +* with any external PAL library with which the application is linked. +* +* It should be included at the start of any AST source file that refers +* to PAL functions using the standard names (e.g. palPvobs). + +* Copyright: +* Copyright (C) 2012 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 16-FEB-2012 (DSB): +* Original version. +*/ + +/* Rename all PAL functions */ +#define palAddet astPalAddet +#define palAmpqk astPalAmpqk +#define palCaldj astPalCaldj +#define palDat astPalDat +#define palDe2h astPalDe2h +#define palDeuler astPalDeuler +#define palDh2e astPalDh2e +#define palDjcal astPalDjcal +#define palDmat astPalDmat +#define palDs2tp astPalDs2tp +#define palDtp2s astPalDtp2s +#define palDtps2c astPalDtps2c +#define palDtt astPalDtt +#define palEcmat astPalEcmat +#define palEqgal astPalEqgal +#define palEtrms astPalEtrms +#define palEvp astPalEvp +#define palFk45z astPalFk45z +#define palFk524 astPalFk524 +#define palFk54z astPalFk54z +#define palGaleq astPalGaleq +#define palGalsup astPalGalsup +#define palMappa astPalMappa +#define palMapqkz astPalMapqkz +#define palPrebn astPalPrebn +#define palPrec astPalPrec +#define palPrenut astPalPrenut +#define palPvobs astPalPvobs +#define palRvgalc astPalRvgalc +#define palRvlg astPalRvlg +#define palRvlsrd astPalRvlsrd +#define palRvlsrk astPalRvlsrk +#define palSubet astPalSubet +#define palSupgal astPalSupgal +#define palCldj astPalCldj +#define palDaf2r astPalDaf2r +#define palDav2m astPalDav2m +#define palDbear astPalDbear +#define palDcc2s astPalDcc2s +#define palDcs2c astPalDcs2c +#define palDd2tf astPalDd2tf +#define palDimxv astPalDimxv +#define palDjcl astPalDjcl +#define palDm2av astPalDm2av +#define palDmxm astPalDmxm +#define palDmxv astPalDmxv +#define palDpav astPalDpav +#define palDrange astPalDrange +#define palDranrm astPalDranrm +#define palDsep astPalDsep +#define palDsepv astPalDsepv +#define palDtf2d astPalDtf2d +#define palDtf2r astPalDtf2r +#define palDvdv astPalDvdv +#define palDvn astPalDvn +#define palDvxv astPalDvxv +#define palEpb astPalEpb +#define palEpb2d astPalEpb2d +#define palEpj astPalEpj +#define palEpj2d astPalEpj2d +#define palEqeqx astPalEqeqx +#define palFk5hz astPalFk5hz +#define palGeoc astPalGeoc +#define palGmst astPalGmst +#define palHfk5z astPalHfk5z + +/* Rename all PAL global variables */ +#define PAL__DPI AST__PALDPI +#define PAL__D2PI AST__PALD2PI +#define PAL__DD2R AST__PALDD2R +#define PAL__DR2AS AST__PALDR2AS +#define PAL__DAS2R AST__PALDAS2R +#define PAL__MJD0 AST__PALMJD0 +#define PAL__CR AST__PALCR +#define PAL__VF AST__PALVF +#define PAL__SR AST__PALSR + + +#endif diff --git a/palwrap.c b/palwrap.c new file mode 100644 index 0000000..ca9a38f --- /dev/null +++ b/palwrap.c @@ -0,0 +1,301 @@ +#if !defined( PAL_INCLUDED ) /* Include this file only once */ +#define PAL_INCLUDED +/* +* Name: +* pal.c + +* Purpose: +* A wrapper for all PAL functions used by AST. + +* Description: +* This file is a wrapper that includes all the source files from +* the PAL and ERFA libraries. This is done so that the function +* names can be changed from the usual "palXxx" and "eraXxx" forms to +* the private "astPalXxx" and "astIauXxx" forms. This renaming is +* performed via the macros defined in the pal2ast.h and erfa2ast.h +* include files. This is done in order to prevent clashes with any +* external PAL or ERFA libraries with which an application is linked. +* +* This file expects to find the publicly distributed and unmodified +* PAL and ERFA source code in a pair of subdirectory called "pal" and +* "erfa" within the current directory. The files within these +* subdirectories do not need to be compiled - only this wrapper file +* needs to be compiled. + +* Copyright: +* Copyright (C) 2011 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (JAC, Hawaii) + +* History: +* 16-FEB-2012 (DSB): +* Original version. +*/ + +/* Include configuration results in order to get any definition for the + EXTERNAL_PAL macro. This macro is set if the --with-external_pal + option was set when AST was configured. */ +#if HAVE_CONFIG_H +#include +#endif + +/* If an external PAL library is being used, we leave this file empty. */ +#ifndef EXTERNAL_PAL + +/* Rename all PAL functions so that references to "palXxx" in the files + included below get translated to "astPalXxx". */ +#include "pal2ast.h" + +/* Rename all ERFA functions so that references to "eraXxx" in the files + included below get translated to "astEraXxx". */ +#include "erfa2ast.h" + +/* Include code from all PAL source files */ +#include "pal/palAddet.c" +#include "pal/palAmpqk.c" +#include "pal/palCaldj.c" +#include "pal/palDat.c" +#include "pal/palDe2h.c" +#include "pal/palDeuler.c" +#include "pal/palDh2e.c" +#include "pal/palDjcal.c" +#include "pal/palDmat.c" +#include "pal/palDrange.c" +#include "pal/palDs2tp.c" +#include "pal/palDtp2s.c" +#include "pal/palDtps2c.c" +#include "pal/palDtt.c" +#include "pal/palEcmat.c" +#include "pal/palEqgal.c" +#include "pal/palEtrms.c" +#include "pal/palEvp.c" +#include "pal/palFk45z.c" +#include "pal/palFk524.c" +#include "pal/palFk54z.c" +#include "pal/palGaleq.c" +#include "pal/palGalsup.c" +#include "pal/palGeoc.c" +#include "pal/palMappa.c" +#include "pal/palMapqkz.c" +#include "pal/palOne2One.c" +#include "pal/palPrebn.c" +#include "pal/palPrec.c" +#include "pal/palPrenut.c" +#include "pal/palPvobs.c" +#include "pal/palRvgalc.c" +#include "pal/palRvlg.c" +#include "pal/palRvlsrd.c" +#include "pal/palRvlsrk.c" +#include "pal/palSubet.c" +#include "pal/palSupgal.c" + +/* Include code from all ERFA source files */ +#include "erfa/a2af.c" +#include "erfa/a2tf.c" +#include "erfa/af2a.c" +#include "erfa/anp.c" +#include "erfa/anpm.c" +#include "erfa/bi00.c" +#include "erfa/bp00.c" +#include "erfa/bp06.c" +#include "erfa/bpn2xy.c" +#include "erfa/c2i00a.c" +#include "erfa/c2i00b.c" +#include "erfa/c2i06a.c" +#include "erfa/c2ibpn.c" +#include "erfa/c2ixy.c" +#include "erfa/c2ixys.c" +#include "erfa/c2s.c" +#include "erfa/c2t00a.c" +#include "erfa/c2t00b.c" +#include "erfa/c2t06a.c" +#include "erfa/c2tcio.c" +#include "erfa/c2teqx.c" +#include "erfa/c2tpe.c" +#include "erfa/c2txy.c" +#include "erfa/cal2jd.c" +#include "erfa/cp.c" +#include "erfa/cpv.c" +#include "erfa/cr.c" +#include "erfa/d2dtf.c" +#include "erfa/d2tf.c" +#include "erfa/dat.c" +#include "erfa/dtdb.c" +#include "erfa/dtf2d.c" +#include "erfa/ee00.c" +#include "erfa/ee00a.c" +#include "erfa/ee00b.c" +#include "erfa/ee06a.c" +#include "erfa/eect00.c" +#include "erfa/eform.c" +#include "erfa/eo06a.c" +#include "erfa/eors.c" +#include "erfa/epb.c" +#include "erfa/epb2jd.c" +#include "erfa/epj.c" +#include "erfa/epj2jd.c" +#include "erfa/epv00.c" +#include "erfa/eqeq94.c" +#include "erfa/era00.c" +#include "erfa/fad03.c" +#include "erfa/fae03.c" +#include "erfa/faf03.c" +#include "erfa/faju03.c" +#include "erfa/fal03.c" +#include "erfa/falp03.c" +#include "erfa/fama03.c" +#include "erfa/fame03.c" +#include "erfa/fane03.c" +#include "erfa/faom03.c" +#include "erfa/fapa03.c" +#include "erfa/fasa03.c" +#include "erfa/faur03.c" +#include "erfa/fave03.c" +#include "erfa/fk52h.c" +#include "erfa/fk5hip.c" +#include "erfa/fk5hz.c" +#include "erfa/fw2m.c" +#include "erfa/fw2xy.c" +#include "erfa/gc2gd.c" +#include "erfa/gc2gde.c" +#include "erfa/gd2gc.c" +#include "erfa/gd2gce.c" +#include "erfa/gmst00.c" +#include "erfa/gmst06.c" +#include "erfa/gmst82.c" +#include "erfa/gst00a.c" +#include "erfa/gst00b.c" +#include "erfa/gst06.c" +#include "erfa/gst06a.c" +#include "erfa/gst94.c" +#include "erfa/h2fk5.c" +#include "erfa/hfk5z.c" +#include "erfa/ir.c" +#include "erfa/jd2cal.c" +#include "erfa/jdcalf.c" +#include "erfa/num00a.c" +#include "erfa/num00b.c" +#include "erfa/num06a.c" +#include "erfa/numat.c" +#include "erfa/nut00a.c" +#include "erfa/nut00b.c" +#include "erfa/nut06a.c" +#include "erfa/nut80.c" +#include "erfa/nutm80.c" +#include "erfa/obl06.c" +#include "erfa/obl80.c" +#include "erfa/p06e.c" +#include "erfa/p2pv.c" +#include "erfa/p2s.c" +#include "erfa/pap.c" +#include "erfa/pas.c" +#include "erfa/pb06.c" +#include "erfa/pdp.c" +#include "erfa/pfw06.c" +#include "erfa/plan94.c" +#include "erfa/pm.c" +#include "erfa/pmat00.c" +#include "erfa/pmat06.c" +#include "erfa/pmat76.c" +#include "erfa/pmp.c" +#include "erfa/pn.c" +#include "erfa/pn00.c" +#include "erfa/pn00a.c" +#include "erfa/pn00b.c" +#include "erfa/pn06.c" +#include "erfa/pn06a.c" +#include "erfa/pnm00a.c" +#include "erfa/pnm00b.c" +#include "erfa/pnm06a.c" +#include "erfa/pnm80.c" +#include "erfa/pom00.c" +#include "erfa/ppp.c" +#include "erfa/ppsp.c" +#include "erfa/pr00.c" +#include "erfa/prec76.c" +#include "erfa/pv2p.c" +#include "erfa/pv2s.c" +#include "erfa/pvdpv.c" +#include "erfa/pvm.c" +#include "erfa/pvmpv.c" +#include "erfa/pvppv.c" +#include "erfa/pvstar.c" +#include "erfa/pvu.c" +#include "erfa/pvup.c" +#include "erfa/pvxpv.c" +#include "erfa/pxp.c" +#include "erfa/refco.c" +#include "erfa/rm2v.c" +#include "erfa/rv2m.c" +#include "erfa/rx.c" +#include "erfa/rxp.c" +#include "erfa/rxpv.c" +#include "erfa/rxr.c" +#include "erfa/ry.c" +#include "erfa/rz.c" +#include "erfa/s00.c" +#include "erfa/s00a.c" +#include "erfa/s00b.c" +#include "erfa/s06.c" +#include "erfa/s06a.c" +#include "erfa/s2c.c" +#include "erfa/s2p.c" +#include "erfa/s2pv.c" +#include "erfa/s2xpv.c" +#include "erfa/sepp.c" +#include "erfa/seps.c" +#include "erfa/sp00.c" +#include "erfa/starpm.c" +#include "erfa/starpv.c" +#include "erfa/sxp.c" +#include "erfa/sxpv.c" +#include "erfa/taitt.c" +#include "erfa/taiut1.c" +#include "erfa/taiutc.c" +#include "erfa/tcbtdb.c" +#include "erfa/tcgtt.c" +#include "erfa/tdbtcb.c" +#include "erfa/tdbtt.c" +#include "erfa/tf2a.c" +#include "erfa/tf2d.c" +#include "erfa/tr.c" +#include "erfa/trxp.c" +#include "erfa/trxpv.c" +#include "erfa/tttai.c" +#include "erfa/tttcg.c" +#include "erfa/tttdb.c" +#include "erfa/ttut1.c" +#include "erfa/ut1tai.c" +#include "erfa/ut1tt.c" +#include "erfa/ut1utc.c" +#include "erfa/utctai.c" +#include "erfa/utcut1.c" +#include "erfa/xy06.c" +#include "erfa/xys00a.c" +#include "erfa/xys00b.c" +#include "erfa/xys06a.c" +#include "erfa/zp.c" +#include "erfa/zpv.c" +#include "erfa/zr.c" + +#endif + +#endif diff --git a/parallel.pdf b/parallel.pdf new file mode 100644 index 0000000..c02ef03 Binary files /dev/null and b/parallel.pdf differ diff --git a/pcdmap.c b/pcdmap.c new file mode 100644 index 0000000..b7723f1 --- /dev/null +++ b/pcdmap.c @@ -0,0 +1,3218 @@ +/* +*class++ +* Name: +* PcdMap + +* Purpose: +* Apply 2-dimensional pincushion/barrel distortion. + +* Constructor Function: +c astPcdMap +f AST_PCDMAP + +* Description: +* A PcdMap is a non-linear Mapping which transforms 2-dimensional +* positions to correct for the radial distortion introduced by some +* cameras and telescopes. This can take the form either of pincushion +* or barrel distortion, and is characterized by a single distortion +* coefficient. +* +* A PcdMap is specified by giving this distortion coefficient and the +* coordinates of the centre of the radial distortion. The forward +* transformation of a PcdMap applies the distortion: +* +* RD = R * ( 1 + C * R * R ) +* +* where R is the undistorted radial distance from the distortion +* centre (specified by attribute PcdCen), RD is the radial distance +* from the same centre in the presence of distortion, and C is the +* distortion coefficient (given by attribute Disco). +* +* The inverse transformation of a PcdMap removes the distortion +* produced by the forward transformation. The expression used to derive +* R from RD is an approximate inverse of the expression above. + +* Inheritance: +* The PcdMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* PcdMap also has the following attributes: +* +* - Disco: PcdMap pincushion/barrel distortion coefficient +* - PcdCen(axis): Centre coordinates of pincushion/barrel distortion + +* Functions: +c The PcdMap class does not define any new functions beyond those +f The PcdMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) + +* History: +* 18-MAY-1999 (DSB): +* Original version. +* 25-OCT-1999 (DSB): +* Fixed memory leak in MapMerge. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitPcdMapVtab +* method. +* 23-AUG-2006 (DSB): +* Override astEqual. +* 23-APR-2015 (DSB): +* Improve MapMerge. If a PcdMap can merge with its next-but-one +* neighbour, then swap the PcdMap with its neighbour, so that +* it is then next its next-but-one neighbour, and then merge the +* two Mappings into a single Mapping. Previously, only the swap +* was performed - not the merger. And the swap was only performed +* if the intervening neighbour could not itself merge. This could +* result in an infinite simplification loop, which was detected by +* CmpMap and and aborted, resulting in no useful simplification. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS PcdMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "unitmap.h" /* Unit mappings */ +#include "zoommap.h" /* Zoom mappings */ +#include "permmap.h" /* Axis permutations */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "pcdmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(PcdMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(PcdMap,Class_Init) +#define class_vtab astGLOBAL(PcdMap,Class_Vtab) +#define getattrib_buff astGLOBAL(PcdMap,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPcdMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPcdMap *astPcdMapId_( double, const double [2], const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ + +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static double GetDisco( AstPcdMap *, int * ); +static double GetPcdCen( AstPcdMap *, int, int * ); +static int CanMerge( AstMapping *, AstMapping *, int, int, int * ); +static int CanSwap( AstMapping *, AstMapping *, int, int, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestDisco( AstPcdMap *, int * ); +static int TestPcdCen( AstPcdMap *, int, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearDisco( AstPcdMap *, int * ); +static void ClearPcdCen( AstPcdMap *, int, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void PcdPerm( AstMapping **, int *, int, int * ); +static void PcdZoom( AstMapping **, int *, int, int * ); +static void PermGet( AstPermMap *, int **, int **, double **, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetDisco( AstPcdMap *, double, int * ); +static void SetPcdCen( AstPcdMap *, int, double, int * ); + +/* Function Macros */ +/* =============== */ +/* Macros which return the maximum and minimum of two values. */ +#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb)) +#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb)) + +/* Macro to check for equality of floating point values. We cannot +compare bad values directory because of the danger of floating point +exceptions, so bad values are dealt with explicitly. */ +#define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN)))) + +/* +* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "pcdmap.h" +* MAKE_CLEAR(attr,component,assign,nval) + +* Class Membership: +* Defined by the PcdMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstPcdMap *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear_( AstPcdMap *this, int axis ) +* +* which implement a method for clearing a single value in a specified +* multi-valued attribute for an axis of a PcdMap. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. PcdCen in "astClearPcdCen"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attr,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstPcdMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astClear" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Report an error if the object has been cloned (i.e. has a reference \ + count that is greater than one). */ \ + } else if( astGetRefCount( this ) > 1 ) { \ + astError( AST__IMMUT, "astClear(%s): The " #attr "attribute of " \ + "the supplied %s cannot be cleared because the %s has " \ + "been cloned (programming error).", status, \ + astGetClass(this), astGetClass(this), astGetClass(this) ); \ +\ +/* Assign the "clear" value. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attr##_( AstPcdMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,PcdMap,Clear##attr))( this, axis, status ); \ +} + + +/* +* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "pcdmap.h" +* MAKE_GET(attr,type,bad_value,assign,nval) + +* Class Membership: +* Defined by the PcdMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstPcdMap *this, int axis ) +* +* and an external interface function of the form: +* +* astGet_( AstPcdMap *this, int axis ) +* +* which implement a method for getting a single value from a specified +* multi-valued attribute for an axis of a PcdMap. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. PcdCen in "astGetPcdCen"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET(attr,type,bad_value,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstPcdMap *this, int axis, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Initialise */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astGet" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attr##_( AstPcdMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,PcdMap,Get##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "pcdmap.h" +* MAKE_SET(attr,type,component,assign,nval) + +* Class Membership: +* Defined by the PcdMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPcdMap *this, int axis, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstPcdMap *this, int axis, value ) +* +* which implement a method for setting a single value in a specified +* multi-valued attribute for a PcdMap. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. PcdCen in "astSetPcdCen"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET(attr,type,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstPcdMap *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astSet" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Report an error if the object has been cloned (i.e. has a reference \ + count that is greater than one). */ \ + } else if( astGetRefCount( this ) > 1 ) { \ + astError( AST__IMMUT, "astSet(%s): The " #attr "attribute of " \ + "the supplied %s cannot be changed because the %s has " \ + "been cloned (programming error).", status, \ + astGetClass(this), astGetClass(this), astGetClass(this) ); \ +\ +/* Store the new value in the structure component. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attr##_( AstPcdMap *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,PcdMap,Set##attr))( this, axis, value, status ); \ +} + +/* +* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a method to test if a single value has been set in a +* multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "pcdmap.h" +* MAKE_TEST(attr,assign,nval) + +* Class Membership: +* Defined by the PcdMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( AstPcdMap *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest_( AstPcdMap *this, int axis ) +* +* which implement a method for testing if a single value in a specified +* multi-valued attribute has been set for a class. + +* Parameters: +* attr +* The name of the attribute to be tested, as it appears in the function +* name (e.g. PcdCen in "astTestPcdCen"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. This can use the string "axis" to represent the zero-based +* index of the value within the attribute. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_TEST(attr,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attr( AstPcdMap *this, int axis, int *status ) { \ + int result; /* Value to return */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astTest" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attr##_( AstPcdMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,PcdMap,Test##attr))( this, axis, status ); \ +} + +/* Member functions. */ +/* ================= */ +static int CanMerge( AstMapping *map1, AstMapping *map2, int inv1, int inv2, int *status ){ +/* +* +* Name: +* CanMerge + +* Purpose: +* Checks if two Mappings can be merged. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* int CanMerge( AstMapping *map1, AstMapping *map2, int inv1, int inv2, int *status ) + +* Class Membership: +* PcdMap internal utility function. + +* Description: +* This function checks the two supplied Mappings to see if they +* can be merged into a single Mapping. One of the pair must be a PcdMap. + +* Parameters: +* map1 +* A pointer to the first mapping. +* map2 +* A pointer to the second mapping. +* inv1 +* The invert flag to use with the first mapping. +* inv2 +* The invert flag to use with the second mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if the Mappings can be merged, zero otherwise. + +*/ + + AstPcdMap *pcd; /* Pointer to PcdMap Mapping */ + AstPcdMap *pcd2; /* Pointer to second PcdMap Mapping */ + AstMapping *nopcd; /* Pointer to non-PcdMap Mapping */ + const char *class1; /* Pointer to map1 class string */ + const char *class2; /* Pointer to map2 class string */ + const char *nopcd_class; /* Pointer to non-PcdMap class string */ + int invert[ 2 ]; /* Original invert flags */ + int ret; /* Returned flag */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise */ + ret = 0; + pcd = NULL; + nopcd = NULL; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + invert[ 0 ] = astGetInvert( map1 ); + astSetInvert( map1, inv1 ); + + invert[ 1 ] = astGetInvert( map2 ); + astSetInvert( map2, inv2 ); + +/* Get the classes of the two mappings. */ + class1 = astGetClass( map1 ); + class2 = astGetClass( map2 ); + if( astOK ){ + +/* Get pointers to the PcdMap and the non-PcdMap Mapping. */ + if( !strcmp( class1, "PcdMap" ) ){ + pcd = (AstPcdMap * ) map1; + nopcd = map2; + nopcd_class = class2; + } else if( !strcmp( class2, "PcdMap" ) ){ + nopcd = map1; + pcd = (AstPcdMap * ) map2; + nopcd_class = class1; + } else { + nopcd_class = "unusable"; + } + +/* The Mappings can merge if the other Mapping is a UnitMap. */ + if( !strcmp( nopcd_class, "UnitMap" ) ){ + ret = 1; + +/* They can also merge if the other Mapping is also a PcdMap, and is the + invert of the other. */ + } else if( !strcmp( nopcd_class, "PcdMap" ) ){ + pcd2 = (AstPcdMap *) nopcd; + +/* Check the distortion coefficients are equal. */ + if( EQUAL( astGetDisco( pcd ), astGetDisco( pcd2 ) ) ){ + +/* Check the axis 0 centres are equal. */ + if( EQUAL( astGetPcdCen( pcd, 0 ), astGetPcdCen( pcd2, 0 ) ) ){ + +/* Check the axis 1 centres are equal. */ + if( EQUAL( astGetPcdCen( pcd, 1 ), astGetPcdCen( pcd2, 1 ) ) ){ + +/* Check the Invert flags are different. */ + if( astGetInvert( pcd ) != astGetInvert( pcd2 ) ){ + +/* If the Mappings have passed all these tests, they can be merged. */ + ret = 1; + } + } + } + } + } + } + +/* Re-instate the original settings of the Invert attributes for the + supplied Mappings. */ + astSetInvert( map1, invert[ 0 ] ); + astSetInvert( map2, invert[ 1 ] ); + +/* Return the answer. */ + return astOK ? ret : 0; +} + +static int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, int *status ){ +/* +* Name: +* CanSwap + +* Purpose: +* Determine if two Mappings could be swapped. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, int *status ) + +* Class Membership: +* PcdMap member function + +* Description: +* This function returns a flag indicating if the pair of supplied +* Mappings could be replaced by an equivalent pair of Mappings from the +* same classes as the supplied pair, but in reversed order. Each pair +* of Mappings is considered to be compounded in series. The supplied +* Mappings are not changed in any way. + +* Parameters: +* map1 +* The Mapping to be applied first. +* map2 +* The Mapping to be applied second. +* inv1 +* The invert flag to use with map1. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* inv2 +* The invert flag to use with map2. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if the Mappings could be swapped, 0 otherwise. + +* Notes: +* - One of the supplied pair of Mappings must be a PcdMap. +* - A value of 0 is returned if the two Mappings could be merged into +* a single Mapping. +* - A value of 0 is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *nopcd; /* Pointer to non-PcdMap Mapping */ + const char *class1; /* Pointer to map1 class string */ + const char *class2; /* Pointer to map2 class string */ + const char *nopcd_class; /* Pointer to non-PcdMap class string */ + double *consts; /* Pointer to constants array */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int invert[ 2 ]; /* Original invert flags */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + int ret; /* Returned flag */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise */ + ret = 0; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + invert[ 0 ] = astGetInvert( map1 ); + astSetInvert( map1, inv1 ); + + invert[ 1 ] = astGetInvert( map2 ); + astSetInvert( map2, inv2 ); + +/* Get the classes of the two mappings. */ + class1 = astGetClass( map1 ); + class2 = astGetClass( map2 ); + if( astOK ){ + +/* Get a pointer to the non-PcdMap Mapping. */ + if( !strcmp( class1, "PcdMap" ) ){ + nopcd = map2; + nopcd_class = class2; + } else { + nopcd = map1; + nopcd_class = class1; + } + +/* If the other Mapping is a ZoomMap, the Mappings can be swapped. */ + if( !strcmp( nopcd_class, "ZoomMap" ) ){ + ret = 1; + +/* If it is a PermMap, the Mappings can be swapped so long as the PermMap + simply swaps the two axes. */ + } else if( !strcmp( nopcd_class, "PermMap" ) ){ + +/* Get the number of input and output coordinates for the PermMap. */ + nin = astGetNin( nopcd ); + nout = astGetNout( nopcd ); + +/* Must be 2-dimensional to swap. */ + if( nin == 2 && nout == 2 ) { + +/* Get the axis permutation arrays and constants array for the PermMap. */ + PermGet( (AstPermMap *) nopcd, &outperm, &inperm, &consts, status ); + if( astOK ) { + +/* If the PermMap simply swaps the 2 axes (in both direction) we can + swap the two mappings. */ + ret = ( outperm[0] == 1 && outperm[1] == 0 && + inperm[0] == 1 && inperm[1] == 0 ); + +/* Free the axis permutation and constants arrays. */ + outperm = (int *) astFree( (void *) outperm ); + inperm = (int *) astFree( (void *) inperm ); + consts = (double *) astFree( (void *) consts ); + } + } + } + } + +/* Re-instate the original settings of the Invert attributes for the + supplied Mappings. */ + astSetInvert( map1, invert[ 0 ] ); + astSetInvert( map2, invert[ 1 ] ); + +/* Return the answer. */ + return astOK ? ret : 0; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a PcdMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PcdMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* PcdMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the PcdMap. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPcdMap *this; /* Pointer to the PcdMap structure */ + int axis; /* Axis number */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PcdMap structure. */ + this = (AstPcdMap *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* PcdCen(axis). */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "pcdcen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearPcdCen( this, axis - 1 ); + +/* PcdCen. */ +/* ------- */ + } else if ( !strcmp( attrib, "pcdcen" ) ) { + astClearPcdCen( this, 0 ); + astClearPcdCen( this, 1 ); + +/* Disco. */ +/* ------ */ + } else if ( !strcmp( attrib, "disco" ) ) { + astClearDisco( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two PcdMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* PcdMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two PcdMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a PcdMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the PcdMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPcdMap *that; + AstPcdMap *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two PcdMap structures. */ + this = (AstPcdMap *) this_object; + that = (AstPcdMap *) that_object; + +/* Check the second object is a PcdMap. We know the first is a + PcdMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAPcdMap( that ) ) { + +/* Check the Invert flags for the two PcdMaps are equal. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + +/* Check all the attributes are equal. */ + if( astEQUAL( this->pcdcen[0], that->pcdcen[0] ) && + astEQUAL( this->pcdcen[1], that->pcdcen[1] ) && + astEQUAL( this->disco, that->disco ) ) { + result = 1; + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a PcdMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PcdMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a PcdMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the PcdMap. +* attrib +* Pointer to a null terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the PcdMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the PcdMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPcdMap *this; /* Pointer to the PcdMap structure */ + const char *result; /* Pointer value to return */ + double dval; /* Double attribute value */ + int axis; /* Axis number */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the PcdMap structure. */ + this = (AstPcdMap *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Disco. */ +/* ------ */ + if ( !strcmp( attrib, "disco" ) ) { + dval = astGetDisco( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* PcdCen(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "pcdcen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetPcdCen( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* PcdCen. */ +/* ------- */ + } else if ( !strcmp( attrib, "pcdcen" ) ) { + dval = astGetPcdCen( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +void astInitPcdMapVtab_( AstPcdMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPcdMapVtab + +* Purpose: +* Initialise a virtual function table for a PcdMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "pcdmap.h" +* void astInitPcdMapVtab( AstPcdMapVtab *vtab, const char *name ) + +* Class Membership: +* PcdMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the PcdMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPcdMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->ClearDisco = ClearDisco; + vtab->SetDisco = SetDisco; + vtab->GetDisco = GetDisco; + vtab->TestDisco = TestDisco; + vtab->ClearPcdCen = ClearPcdCen; + vtab->SetPcdCen = SetPcdCen; + vtab->GetPcdCen = GetPcdCen; + vtab->TestPcdCen = TestPcdCen; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + object->Equal = Equal; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping->MapMerge = MapMerge; + +/* Declare the class dump function.*/ + astSetDump( vtab, Dump, "PcdMap", "Apply pincushion distortion" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a PcdMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* PcdMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated PcdMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated PcdMap with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated PcdMap which is to be merged with +* its neighbours. This should be a cloned copy of the PcdMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* PcdMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated PcdMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping **maplt; /* New mappings list pointer */ + AstMapping *map2; /* First mapping to check */ + AstMapping *newmap; /* Pointer to replacement Mapping */ + AstMapping *mc[2]; /* Copies of supplied Mappings to swap */ + AstMapping *smc0; /* Simplied Mapping */ + AstMapping *smc1; /* Simplied Mapping */ + const char *class1; /* Pointer to first Mapping class string */ + const char *class2; /* Pointer to second Mapping class string */ + const char *nclass; /* Pointer to neighbouring Mapping class */ + int i1; /* Index of first PcdMap to merge */ + int i2; /* Index of last PcdMap to merge */ + int i; /* Loop counter */ + int ic[2]; /* Copies of supplied invert flags to swap */ + int invert; /* Should the inverted Mapping be used? */ + int *invlt; /* New invert flags list pointer */ + int nmapt; /* No. of Mappings in list */ + int nstep1; /* No. of Mappings backwards to next mergable Mapping */ + int nstep2; /* No. of Mappings forward to next mergable Mapping */ + int result; /* Result value to return */ + int swaphi; /* Can PcdMap be swapped with higher neighbour? */ + int swaplo; /* Can PcdMap be swapped with lower neighbour? */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + i1 = 0; + i2 = 0; + +/* First of all, see if the PcdMap can be replaced by a simpler Mapping, + without reference to the neighbouring Mappings in the list. */ +/* ======================================================================*/ +/* If the distortion coefficient in the PcdMap is zero, the PcdMap can be + replaced by a UnitMap. */ + if( EQUAL( astGetDisco( (AstPcdMap *) ( *map_list )[ where ] ), 0.0 ) ){ + +/* Annul the PcdMap pointer in the list and replace it with a UnitMap + pointer, and indicate that the forward transformation of the returned + UnitMap should be used. */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astUnitMap( 2, "", status ); + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the PcdMap itself could not be simplified, see if it can be merged + with the Mappings on either side of it in the list. */ + } else { + +/* Store the classes of the neighbouring Mappings in the list. */ + class1 = ( where > 0 ) ? astGetClass( ( *map_list )[ where - 1 ] ) : NULL; + class2 = ( where < *nmap - 1 ) ? astGetClass( ( *map_list )[ where + 1 ] ) : NULL; + +/* In series. */ +/* ========== */ + if ( series ) { + +/* We first look to see if the PcdMap can be merged with one of its + neighbours, resulting in a reduction of one in the number of Mappings + in the list. A PcdMap can only merge directly with its own inverse, or + a UnitMap. */ + if( class1 && CanMerge( ( *map_list )[ where - 1 ], + ( *map_list )[ where ], + ( *invert_list )[ where - 1 ], + ( *invert_list )[ where ], status ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( class2 && CanMerge( ( *map_list )[ where ], + ( *map_list )[ where + 1 ], + ( *invert_list )[ where ], + ( *invert_list )[ where + 1 ], status ) ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } else { + nclass = NULL; + } + +/* If the PcdMap can merge with one of its neighbours, create the merged + Mapping. */ + if( nclass ){ + +/* If the neighbour is a PcdMap, it must be the inverse of the nominated + Mapping, and so they merge to form a UnitMap. */ + if( !strcmp( nclass, "PcdMap" ) ){ + newmap = (AstMapping *) astUnitMap( 2, "", status ); + invert = 0; + +/* If the neighbour is a UnitMap, they merge to form a clone of the + nominated PcdMap. */ + } else { + newmap = (AstMapping *) astClone( ( *map_list )[ where ] ); + invert = ( *invert_list )[ where ]; + } + +/* If succesfull... */ + if( astOK ){ + +/* Annul the first of the two Mappings, and replace it with the merged + Mapping. Also set the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = newmap; + ( *invert_list )[ i1 ] = invert; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i2 ] ); + for ( i = i2 + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + + } + +/* If the PcdMap could not merge directly with either of its neighbours, + we consider whether it would be worthwhile to swap the PcdMap with + either of its neighbours. This can only be done for certain classes + of Mapping (ZoomMaps & some PermMaps), and will usually require both + Mappings to be modified (unless they are commutative). The advantage of + swapping the order of the Mappings is that it may result in the PcdMap + being adjacent to a Mapping with which it can merge directly on the next + invocation of this function, thus reducing the number of Mappings + in the list. */ + } else { + +/* Set a flag if we could swap the PcdMap with its higher neighbour. */ + if( where + 1 < *nmap ){ + swaphi = CanSwap( ( *map_list )[ where ], + ( *map_list )[ where + 1 ], + ( *invert_list )[ where ], + ( *invert_list )[ where + 1 ], status ); + } else { + swaphi = 0; + } + +/* If so, step through each of the Mappings which follow the PcdMap, + looking for a Mapping with which the PcdMap could merge directly. Stop + when such a Mapping is found, or if a Mapping is found with which the + PcdMap could definitely not swap. Note the number of Mappings which + separate the PcdMap from the Mapping with which it could merge (if + any). */ + nstep2 = -1; + if( swaphi ){ + for( i2 = where + 1; i2 < *nmap; i2++ ){ + +/* See if we may be able to merge with this Mapping. If so, note the number + of steps between the two Mappings and leave the loop. */ + nclass = astGetClass( ( *map_list )[ i2 ] ); + + if( !strcmp( nclass, "UnitMap" ) || + !strcmp( nclass, "PcdMap" ) ){ + nstep2 = i2 - where - 1; + break; + } + +/* If there is no chance that we can swap with this Mapping, leave the loop + with -1 for the number of steps to indicate that no merging is possible. + PcdMaps can swap with ZoomMaps and some PermMaps. */ + if( strcmp( nclass, "ZoomMap" ) && + strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Do the same working forward from the PcdMap towards the start of the map + list. */ + if( where > 0 ){ + swaplo = CanSwap( ( *map_list )[ where - 1 ], + ( *map_list )[ where ], + ( *invert_list )[ where - 1 ], + ( *invert_list )[ where ], status ); + } else { + swaplo = 0; + } + + nstep1 = -1; + if( swaplo ){ + for( i1 = where - 1; i1 >= 0; i1-- ){ + + nclass = astGetClass( ( *map_list )[ i1 ] ); + + if( !strcmp( nclass, "UnitMap" ) || + !strcmp( nclass, "PcdMap" ) ){ + nstep1 = where - 1 - i1; + break; + } + + if( strcmp( nclass, "ZoomMap" ) && + strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Choose which neighbour to swap with so that the PcdMap moves towards the + nearest Mapping with which it can merge. */ + if( nstep1 != -1 && ( nstep2 == -1 || nstep2 > nstep1 ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + } else if( nstep2 != -1 ){ + nclass = class2; + i1 = where; + i2 = where + 1; + } else { + nclass = NULL; + } + +/* If there is a target Mapping in the list with which the PcdMap could + merge, replace the supplied Mappings with swapped Mappings to bring a + PcdMap closer to the target Mapping. */ + if( nclass ){ + +/* Swap the Mappings. */ + if( !strcmp( nclass, "ZoomMap" ) ){ + PcdZoom( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + + } else if( !strcmp( nclass, "PermMap" ) ){ + PcdPerm( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + } + +/* And then merge them. */ + if( where == i1 && where + 1 < *nmap ) { /* Merging upwards */ + map2 = astClone( (*map_list)[ where + 1 ] ); + nmapt = *nmap - where - 1; + maplt = *map_list + where + 1; + invlt = *invert_list + where + 1; + + (void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + *nmap = where + 1 + nmapt; + + } else if( where - 2 >= 0 ) { /* Merging downwards */ + map2 = astClone( (*map_list)[ where - 2 ] ); + nmapt = *nmap - where + 2; + maplt = *map_list + where - 2 ; + invlt = *invert_list + where - 2; + + (void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + *nmap = where - 2 + nmapt; + } + + result = i1; + +/* If there is no Mapping available for merging, it may still be + advantageous to swap with a neighbour because the swapped Mapping may + be simpler than the original Mappings. */ + } else if( swaphi || swaplo ) { + +/* Try swapping with each possible neighbour in turn. */ + for( i = 0; i < 2; i++ ) { + +/* Set up the class and pointers for the mappings to be swapped, first + the lower neighbour, then the upper neighbour. */ + if( i == 0 && swaplo ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( i == 1 && swaphi ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } else { + nclass = NULL; + } + +/* If we have a Mapping to swap with... */ + if( nclass ) { + +/* Take copies of the Mapping and Invert flag arrays so we do not change + the supplied values. */ + mc[ 0 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[0] ); + mc[ 1 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[1] ); + ic[ 0 ] = ( (*invert_list) + i1 )[0]; + ic[ 1 ] = ( (*invert_list) + i1 )[1]; + +/* Swap these Mappings. */ + if( !strcmp( nclass, "ZoomMap" ) ){ + PcdZoom( mc, ic, where - i1, status ); + } else if( !strcmp( nclass, "PermMap" ) ){ + PcdPerm( mc, ic, where - i1, status ); + } + +/* If neither of the swapped Mappings can be simplified further, then there + is no point in swapping the Mappings, so just annul the map copies. */ + smc0 = astSimplify( mc[0] ); + smc1 = astSimplify( mc[1] ); + + if( astGetClass( smc0 ) == astGetClass( mc[0] ) && + astGetClass( smc1 ) == astGetClass( mc[1] ) ) { + + mc[ 0 ] = (AstMapping *) astAnnul( mc[ 0 ] ); + mc[ 1 ] = (AstMapping *) astAnnul( mc[ 1 ] ); + +/* If one or both of the swapped Mappings could be simplified, then annul + the supplied Mappings and return the swapped mappings, storing the index + of the first modified Mapping. */ + } else { + (void ) astAnnul( ( (*map_list) + i1 )[0] ); + (void ) astAnnul( ( (*map_list) + i1 )[1] ); + + ( (*map_list) + i1 )[0] = mc[ 0 ]; + ( (*map_list) + i1 )[1] = mc[ 1 ]; + + ( (*invert_list) + i1 )[0] = ic[ 0 ]; + ( (*invert_list) + i1 )[1] = ic[ 1 ]; + + result = i1; + break; + } + +/* Annul the simplied Mappings */ + smc0 = astAnnul( smc0 ); + smc1 = astAnnul( smc1 ); + + } + } + } + } + +/* In parallel. */ +/* ============ */ +/* PcdMaps cannot combine in parallel with any other Mappings. */ + } + } + +/* Return the result. */ + return result; +} + +static void PermGet( AstPermMap *map, int **outperm, int **inperm, + double **consts, int *status ){ +/* +* Name: +* PermGet + +* Purpose: +* Get the axis permutation and constants array for a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* void PermGet( AstPermMap *map, int **outperm, int **inperm, +* double **const, int *status ) + +* Class Membership: +* PcdMap member function + +* Description: +* This function returns axis permutation and constants arrays which can +* be used to create a PermMap which is equivalent to the supplied PermMap. + +* Parameters: +* map +* The PermMap. +* outperm +* An address at which to return a popinter to an array of ints +* holding the output axis permutation array. The array should be +* released using astFree when no longer needed. +* inperm +* An address at which to return a popinter to an array of ints +* holding the input axis permutation array. The array should be +* released using astFree when no longer needed. +* consts +* An address at which to return a popinter to an array of doubles +* holding the constants array. The array should be released using +* astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - NULL pointers are returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding input positions for PermMap */ + AstPointSet *pset2; /* PointSet holding output positions for PermMap */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + double *cnst; /* Pointer to constants array */ + double cn; /* Potential new constant value */ + double ip; /* Potential output axis index */ + double op; /* Potential input axis index */ + int *inprm; /* Pointer to input axis permutation array */ + int *outprm; /* Pointer to output axis permutation array */ + int i; /* Axis count */ + int nc; /* Number of constants stored so far */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + +/* Initialise. */ + if( outperm ) *outperm = NULL; + if( inperm ) *inperm = NULL; + if( consts ) *consts = NULL; + +/* Check the global error status and the supplied pointers. */ + if ( !astOK || !outperm || !inperm || !consts ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + nc = 0; + +/* Get the number of input and output axes for the supplied PermMap. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* Allocate the memory for the returned arrays. */ + outprm = (int *) astMalloc( sizeof( int )* (size_t) nout ); + inprm = (int *) astMalloc( sizeof( int )* (size_t) nin ); + cnst = (double *) astMalloc( sizeof( double )* (size_t) ( nout + nin ) ); + +/* Returned the pointers to these arrays.*/ + *outperm = outprm; + *inperm = inprm; + *consts = cnst; + +/* Create two PointSets, each holding two points, which can be used for + input and output positions with the PermMap. */ + pset1 = astPointSet( 2, nin, "", status ); + pset2 = astPointSet( 2, nout, "", status ); + +/* Set up the two input positions to be [0,1,2...] and [-1,-1,-1,...]. The + first position is used to enumerate the axes, and the second is used to + check for constant axis values. */ + ptr1 = astGetPoints( pset1 ); + if( astOK ){ + for( i = 0; i < nin; i++ ){ + ptr1[ i ][ 0 ] = ( double ) i; + ptr1[ i ][ 1 ] = -1.0; + } + } + +/* Use the PermMap to transform these positions in the forward direction. */ + (void) astTransform( map, pset1, 1, pset2 ); + +/* Look at the mapped positions to determine the output axis permutation + array. */ + ptr2 = astGetPoints( pset2 ); + if( astOK ){ + +/* No constant axis valeus found yet. */ + nc = 0; + +/* Do each output axis. */ + for( i = 0; i < nout; i++ ){ + +/* If the output axis value is copied from an input axis value, the index + of the appropriate input axis will be in the mapped first position. */ + op = ptr2[ i ][ 0 ]; + +/* If the output axis value is assigned a constant value, the result of + mapping the two different input axis values will be the same. */ + cn = ptr2[ i ][ 1 ]; + if( op == cn ) { + +/* We have found another constant. Store it in the constants array, and + store the index of the constant in the output axis permutation array. */ + cnst[ nc ] = cn; + outprm[ i ] = -( nc + 1 ); + nc++; + +/* If the output axis values are different, then the output axis value + must be copied from the input axis value. */ + } else { + outprm[ i ] = (int) ( op + 0.5 ); + } + } + } + +/* Now do the same thing to determine the input permutation array. */ + if( astOK ){ + for( i = 0; i < nout; i++ ){ + ptr2[ i ][ 0 ] = ( double ) i; + ptr2[ i ][ 1 ] = -1.0; + } + } + + (void) astTransform( map, pset2, 0, pset1 ); + + if( astOK ){ + + for( i = 0; i < nin; i++ ){ + + ip = ptr1[ i ][ 0 ]; + cn = ptr1[ i ][ 1 ]; + if( ip == cn ) { + + cnst[ nc ] = cn; + inprm[ i ] = -( nc + 1 ); + nc++; + + } else { + inprm[ i ] = (int) ( ip + 0.5 ); + } + } + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* If an error has occurred, attempt to free the returned arrays. */ + if( !astOK ) { + *outperm = (int *) astFree( (void *) *outperm ); + *inperm = (int *) astFree( (void *) *inperm ); + *consts = (double *) astFree( (void *) *consts ); + } + +/* Return. */ + return; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a PcdMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* PcdMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a PcdMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the PcdMap. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPcdMap *this; /* Pointer to the PcdMap structure */ + double dval; /* Double attribute value */ + int axis; /* Index for the axis */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PcdMap structure. */ + this = (AstPcdMap *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Disco. */ +/* ------ */ + if ( nc = 0, + ( 1 == astSscanf( setting, "disco= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetDisco( this, dval ); + +/* PcdCen(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "pcdcen(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetPcdCen( this, axis - 1, dval ); + +/* PcdCen. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "pcdcen= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetPcdCen( this, 0, dval ); + astSetPcdCen( this, 1, dval ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a PcdMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PcdMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a PcdMap's attributes. + +* Parameters: +* this +* Pointer to the PcdMap. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPcdMap *this; /* Pointer to the PcdMap structure */ + int axis; /* Axis number */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the PcdMap structure. */ + this = (AstPcdMap *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* Disco. */ +/* ------ */ + if ( !strcmp( attrib, "disco" ) ) { + result = astTestDisco( this ); + +/* PcdCen. */ +/* ------- */ + } else if ( !strcmp( attrib, "pcdcen" ) ) { + result = astTestPcdCen( this, 0 ); + +/* PcdCen(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "pcdcen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestPcdCen( this, axis - 1 ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a PcdMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* PcdMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a PcdMap and a set of points encapsulated in a +* PointSet and transforms the points so as to map them into the +* required window. + +* Parameters: +* this +* Pointer to the PcdMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the PcdMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstPcdMap *map; /* Pointer to PcdMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *axin_0; /* Pointer to next input axis 0 value */ + double *axin_1; /* Pointer to next input axis 1 value */ + double *axout_0; /* Pointer to next output axis 0 value */ + double *axout_1; /* Pointer to next output axis 1 value */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + double dx; /* Undistorted X increment from centre */ + double dy; /* Undistorted Y increment from centre */ + double dxp; /* Distorted X increment from centre */ + double dyp; /* Distorted Y increment from centre */ + double disco; /* Distortion coefficient */ + double cen0; /* Centre of distortion on axis 0 */ + double cen1; /* Centre of distortion on axis 1 */ + double cr2; /* Constant */ + double a; /* Constant */ + double cr2a2; /* Constant */ + double f; /* Expansion factor */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the PcdMap. */ + map = (AstPcdMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Get the distortion coefficient and centre. */ + disco = astGetDisco( map ); + cen0 = astGetPcdCen( map, 0 ); + cen1 = astGetPcdCen( map, 1 ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if( astOK ){ + +/* Store pointers to the first input and output values on both axes. */ + axin_0 = ptr_in[ 0 ]; + axin_1 = ptr_in[ 1 ]; + axout_0 = ptr_out[ 0 ]; + axout_1 = ptr_out[ 1 ]; + +/* First deal with forward transformations. */ + if( forward ){ + + for( point = 0; point < npoint; point++ ){ + if( *axin_0 != AST__BAD && *axin_1 != AST__BAD ){ + dx = ( *axin_0 - cen0 ); + dy = ( *axin_1 - cen1 ); + f = 1.0 + disco*( dx*dx + dy*dy ); + dxp = dx*f; + dyp = dy*f; + *(axout_0++) = dxp + cen0; + *(axout_1++) = dyp + cen1; + + } else { + *(axout_0++) = AST__BAD; + *(axout_1++) = AST__BAD; + } + axin_0++; + axin_1++; + } + +/* Now deal with inverse transformations. */ + } else { + + for( point = 0; point < npoint; point++ ){ + if( *axin_0 != AST__BAD && *axin_1 != AST__BAD ){ + dxp = ( *axin_0 - cen0 ); + dyp = ( *axin_1 - cen1 ); + + cr2 = disco*( dxp*dxp + dyp*dyp ); + a = ( 2.0*cr2 + 1.0 )/( 3.0*cr2 + 1.0 ); + cr2a2 = cr2*a*a; + f = ( 2.0*cr2a2*a + 1.0 )/( 3.0*cr2a2 + 1.0 ); + + dx = dxp*f; + dy = dyp*f; + +/* The above approximate inverse is taken from SLA_UNPCD. If more accuracy +c is needed, the following code can be uncommented to iterate to a better +c solution. +c +c fl = 1.0 + disco*( dx*dx + dy*dy ); +c dx = dxp/fl; +c dy = dyp/fl; +c +c df = fabs( fl ); +c while( df > fabs( fl*1.0E-6 ) ){ +c f = 1.0 + disco*( dx*dx + dy*dy ); +c df = fabs( f - fl ); +c fl = f; +c +c dx = dxp/f; +c dy = dyp/f; +c } +*/ + *(axout_0++) = dx + cen0; + *(axout_1++) = dy + cen1; + + } else { + *(axout_0++) = AST__BAD; + *(axout_1++) = AST__BAD; + } + axin_0++; + axin_1++; + } + + } + + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void PcdZoom( AstMapping **maps, int *inverts, int ipc, int *status ){ +/* +* Name: +* PcdZoom + +* Purpose: +* Swap a PcdMap and a ZoomMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* void PcdZoom( AstMapping **maps, int *inverts, int ipc, int *status ) + +* Class Membership: +* PcdMap member function + +* Description: +* A list of two Mappings is supplied containing a PcdMap and a +* ZoomMap. These Mappings are annulled, and replaced with +* another pair of Mappings consisting of a PcdMap and a ZoomMap +* in the opposite order. These Mappings are chosen so that their +* combined effect is the same as the original pair of Mappings. + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* ipc +* The index within "maps" of the PcdMap. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPcdMap *pm2; /* Pointer to the returned PcdMap */ + AstPcdMap *pm; /* Pointer to the supplied PcdMap */ + AstZoomMap *zm2; /* Pointer to the returned ZoomMap */ + AstZoomMap *zm; /* Pointer to the supplied ZoomMap */ + double cen[2]; /* New distortion centre */ + double disc; /* Distortion coeff. for returned PcdMap */ + double disco; /* Distortion coeff. for supplied PcdMap */ + double xcen; /* Axis 0 centre for supplied PcdMap */ + double ycen; /* Axis 1 centre for supplied PcdMap */ + double zoom; /* Zoom factor for supplied ZoomMap */ + int old_pinv; /* Invert value for the supplied PcdMap */ + int old_zinv; /* Invert value for the supplied ZoomMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store pointers to the supplied PcdMap and the ZoomMap. */ + pm = (AstPcdMap *) maps[ ipc ]; + zm = (AstZoomMap *) maps[ 1 - ipc ]; + +/* Temporarily set the Invert attribute of the supplied Mappings to the + supplied values. */ + old_pinv = astGetInvert( pm ); + astSetInvert( pm, inverts[ ipc ] ); + + old_zinv = astGetInvert( zm ); + astSetInvert( zm, inverts[ 1 - ipc ] ); + +/* Get the zoom factor from the ZoomMap. */ + zoom = astGetZoom( zm ); + +/* Get the distortion coefficient from the PcdMap. */ + disco = astGetDisco( pm ); + +/* Get the distortion centre from the PcdMap. */ + xcen = astGetPcdCen( pm, 0 ); + ycen = astGetPcdCen( pm, 1 ); + +/* Re-instate the original value of the Invert attributes of the supplied + Mappings. */ + astSetInvert( pm, old_pinv ); + astSetInvert( zm, old_zinv ); + +/* Create the returned ZoomMap. */ + zm2 = astZoomMap( 2, zoom, "", status ); + +/* Find the attributes of the returned PcdMap. If the PCD map is applied + first... */ + if( ipc == 0 ) { + cen[ 0 ] = xcen*zoom; + cen[ 1 ] = ycen*zoom; + disc = disco/(zoom*zoom); + +/* If the PCD map is applied second... */ + } else { + cen[ 0 ] = xcen/zoom; + cen[ 1 ] = ycen/zoom; + disc = disco*zoom*zoom; + } + +/* Create the returned PcdMap. */ + pm2 = astPcdMap( disc, cen, "", status ); + if( inverts[ ipc ] ) astInvert( pm2 ); + +/* Replace the supplied Mappings with the ones created above, swapping the + order. */ + if( astOK ){ + (void) astAnnul( pm ); + (void) astAnnul( zm ); + + maps[ 1 - ipc ] = (AstMapping *) pm2; + inverts[ 1 - ipc ] = inverts[ ipc ]; + + maps[ ipc ] = (AstMapping *) zm2; + inverts[ ipc ] = 0; + + } + +/* Return. */ + return; +} + +static void PcdPerm( AstMapping **maps, int *inverts, int ipc, int *status ){ +/* +* Name: +* PcdPerm + +* Purpose: +* Swap a PcdMap and a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* void PcdPerm( AstMapping **maps, int *inverts, int ipc, int *status ) + +* Class Membership: +* PcdMap member function + +* Description: +* A list of two Mappings is supplied containing a PcdMap and a +* PermMap. These Mappings are annulled, and replaced with +* another pair of Mappings consisting of a PcdMap and a PermMap +* in the opposite order. These Mappings are chosen so that their +* combined effect is the same as the original pair of Mappings. + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* ipc +* The index within "maps" of the PcdMap. +* status +* Pointer to the inherited status variable. + +* Notes: +* - It should have been checked previously that the PermMap simply +* swaps axes 0 and 1. This is the only sort of PermMap which can be +* used here. + +*/ + + AstPermMap *rm; /* Pointer to the supplied PermMap */ + AstPermMap *rm2; /* Pointer to the returned PermMap */ + AstPcdMap *pm; /* Pointer to the supplied PcdMap */ + AstPcdMap *pm2; /* Pointer to the returned PcdMap */ + double cen[2]; /* Centre for new PcdMap */ + double disco; /* Distortion coeff. for supplied PcdMap */ + double xcen; /* Axis 0 centre for supplied PcdMap */ + double ycen; /* Axis 1 centre for supplied PcdMap */ + int old_rinv; /* Invert value for the supplied PermMap */ + int old_pinv; /* Invert value for the supplied PcdMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store pointers to the supplied PcdMap and the PermMap. */ + pm = (AstPcdMap *) maps[ ipc ]; + rm = (AstPermMap *) maps[ 1 - ipc ]; + +/* Temporarily set the Invert attribute of the supplied Mappings to the + supplied values. */ + old_pinv = astGetInvert( pm ); + astSetInvert( pm, inverts[ ipc ] ); + + old_rinv = astGetInvert( rm ); + astSetInvert( rm, inverts[ 1 - ipc ] ); + +/* Get the distortion coefficient from the PcdMap. */ + disco = astGetDisco( pm ); + +/* Get the distortion centre from the PcdMap. */ + xcen = astGetPcdCen( pm, 0 ); + ycen = astGetPcdCen( pm, 1 ); + +/* Re-instate the original value of the Invert attributes of the supplied + Mappings. */ + astSetInvert( pm, old_pinv ); + astSetInvert( rm, old_rinv ); + +/* Create the returned PermMap (unchanged). */ + rm2 = astClone( rm ); + +/* Create the returned PcdMap. */ + cen[ 0 ] = ycen; + cen[ 1 ] = xcen; + pm2 = astPcdMap( disco, cen, "", status ); + if( inverts[ ipc ] ) astInvert( pm2 ); + +/* Replace the supplied Mappings with the ones created above, swapping the + order. */ + if( astOK ){ + (void) astAnnul( pm ); + (void) astAnnul( rm ); + + old_pinv = inverts[ ipc ]; + + maps[ ipc ] = (AstMapping *) rm2; + inverts[ ipc ] = inverts[ 1 - ipc ]; + + maps[ 1 - ipc ] = (AstMapping *) pm2; + inverts[ 1 - ipc ] = old_pinv; + } + +/* Return. */ + return; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ +/* +*att++ +* Name: +* Disco + +* Purpose: +* PcdMap pincushion/barrel distortion coefficient. + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute specifies the pincushion/barrel distortion coefficient +* used by a PcdMap. This coefficient is set when the PcdMap is created, +* but may later be modified. If the attribute is cleared, its default +* value is zero, which gives no distortion. For pincushion distortion, +* the value should be positive. For barrel distortion, it should be +* negative. +* +* Note that the forward transformation of a PcdMap applies the +* distortion specified by this attribute and the inverse +* transformation removes this distortion. If the PcdMap is inverted +c (e.g. using astInvert), then the forward transformation will +f (e.g. using AST_INVERT), then the forward transformation will +* remove the distortion and the inverse transformation will apply +* it. The distortion itself will still be given by the same value of +* Disco. +* +* Note, the value of this attribute may changed only if the PcdMap +* has no more than one reference. That is, an error is reported if the +* PcdMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* PcdMap +* All PcdMaps have this attribute. + +*att-- +*/ +/* This ia a double value with a value of AST__BAD when undefined but + yielding a default of 0.0. */ +astMAKE_CLEAR1(PcdMap,Disco,disco,AST__BAD) +astMAKE_GET(PcdMap,Disco,double,0.0,( ( this->disco == AST__BAD ) ? + 0.0 : this->disco )) +astMAKE_SET1(PcdMap,Disco,double,disco,value) +astMAKE_TEST(PcdMap,Disco,( this->disco != AST__BAD )) + + +/* +*att++ +* Name: +* PcdCen(axis) + +* Purpose: +* Centre coordinates of pincushion/barrel distortion. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the centre of the pincushion/barrel +* distortion implemented by a PcdMap. It takes a separate value for +* each axis of the PcdMap so that, for instance, the settings +* "PcdCen(1)=345.0,PcdCen(2)=-104.4" specify that the pincushion +* distortion is centred at positions of 345.0 and -104.4 on axes 1 and 2 +* respectively. This attribute is set when a PcdMap is created, but may +* later be modified. If the attribute is cleared, the default value for +* both axes is zero. +* +* Note, the value of this attribute may changed only if the PcdMap +* has no more than one reference. That is, an error is reported if the +* PcdMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* PcdMap +* All PcdMaps have this attribute. + +* Notes: +* - If no axis is specified, (e.g. "PcdCen" instead of +* "PcdCen(2)"), then a "set" or "clear" operation will affect +* the attribute value of both axes, while a "get" or "test" +* operation will use just the PcdCen(1) value. +*att-- +*/ +/* The centre of the distortion. AST__BAD is stored if no value has been + supplied, resulting in default values of zero being used. */ +MAKE_CLEAR(PcdCen,pcdcen,AST__BAD,2) +MAKE_SET(PcdCen,double,pcdcen,value,2) +MAKE_TEST(PcdCen,( this->pcdcen[axis] != AST__BAD ),2) +MAKE_GET(PcdCen,double,0.0,(( this->pcdcen[axis] == AST__BAD ) ? + 0.0 : this->pcdcen[axis] ),2) + +/* Copy constructor. */ +/* ----------------- */ +/* No copy constructor is needed, as a byte-by-byte copy suffices. */ + +/* Destructor. */ +/* ----------- */ +/* No destructor is needed as no memory, etc. needs freeing. */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for PcdMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the PcdMap class to an output Channel. + +* Parameters: +* this +* Pointer to the PcdMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPcdMap *this; /* Pointer to the PcdMap structure */ + double dval; /* Attribute value */ + int set; /* Is a value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PcdMap structure. */ + this = (AstPcdMap *) this_object; + +/* Write out values representing the instance variables for the + PcdMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* PcdCen(0). */ +/* ---------- */ + set = TestPcdCen( this, 0, status ); + dval = set ? GetPcdCen( this, 0, status ) : astGetPcdCen( this, 0 ); + astWriteDouble( channel, "PcdCn0", set, 1, dval, "Distortion centre on first axis" ); + +/* PcdCen(1). */ +/* ---------- */ + set = TestPcdCen( this, 1, status ); + dval = set ? GetPcdCen( this, 1, status ) : astGetPcdCen( this, 1 ); + astWriteDouble( channel, "PcdCn1", set, 1, dval, "Distortion centre on second axis" ); + +/* Disco. */ +/* ------ */ + set = TestDisco( this, status ); + dval = set ? GetDisco( this, status ) : astGetDisco( this ); + astWriteDouble( channel, "Disco", set, 1, dval, "Distortion coefficient" ); + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPcdMap and astCheckPcdMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(PcdMap,Mapping) +astMAKE_CHECK(PcdMap) + +AstPcdMap *astPcdMap_( double disco, const double pcdcen[2], + const char *options, int *status, ...) { +/* +*++ +* Name: +c astPcdMap +f AST_PCDMAP + +* Purpose: +* Create a PcdMap. + +* Type: +* Public function. + +* Synopsis: +c #include "pcdmap.h" +c AstPcdMap *astPcdMap( double disco, const double pcdcen[2], +c const char *options, ... ) +f RESULT = AST_PCDMAP( DISCO, PCDCEN, OPTIONS, STATUS ) + +* Class Membership: +* PcdMap constructor. + +* Description: +* This function creates a new PcdMap and optionally initialises its +* attributes. +* +* A PcdMap is a non-linear Mapping which transforms 2-dimensional +* positions to correct for the radial distortion introduced by some +* cameras and telescopes. This can take the form either of pincushion +* or barrel distortion, and is characterized by a single distortion +* coefficient. +* +* A PcdMap is specified by giving this distortion coefficient and the +* coordinates of the centre of the radial distortion. The forward +* transformation of a PcdMap applies the distortion: +* +* RD = R * ( 1 + C * R * R ) +* +* where R is the undistorted radial distance from the distortion +* centre (specified by attribute PcdCen), RD is the radial distance +* from the same centre in the presence of distortion, and C is the +* distortion coefficient (given by attribute Disco). +* +* The inverse transformation of a PcdMap removes the distortion +* produced by the forward transformation. The expression used to derive +* R from RD is an approximate inverse of the expression above, obtained +* from two iterations of the Newton-Raphson method. The mismatch between +* the forward and inverse expressions is negligible for astrometric +* applications (to reach 1 milliarcsec at the edge of the Anglo-Australian +* Telescope triplet or a Schmidt field would require field diameters of +* 2.4 and 42 degrees respectively). +* +c If a PcdMap is inverted (e.g. using astInvert) then the roles of the +f If a PcdMap is inverted (e.g. using AST_INVERT) then the roles of the +* forward and inverse transformations are reversed; the forward +* transformation will remove the distortion, and the inverse +* transformation will apply it. + +* Parameters: +c disco +f DISCO = DOUBLE PRECISION (Given) +* The distortion coefficient. Negative values give barrel +* distortion, positive values give pincushion distortion, and +* zero gives no distortion. +c pcdcen +f PCDCEN( 2 ) = DOUBLE PRECISION (Given) +c A 2-element array containing the coordinates of the centre of the +f An array containing the coordinates of the centre of the +* distortion. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new PcdMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new PcdMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPcdMap() +f AST_PCDMAP = INTEGER +* A pointer to the new PcdMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPcdMap *new; /* Pointer to new PcdMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the PcdMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPcdMap( NULL, sizeof( AstPcdMap ), !class_init, &class_vtab, + "PcdMap", disco, pcdcen ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PcdMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new PcdMap. */ + return new; +} + +AstPcdMap *astPcdMapId_( double disco, const double pcdcen[2], + const char *options, ... ) { +/* +* Name: +* astPcdMapId_ + +* Purpose: +* Create a PcdMap. + +* Type: +* Private function. + +* Synopsis: +* #include "pcdmap.h" +* AstPcdMap *astPcdMapId_( double disco, const double pcdcen[2], +* const char *options, ... ) + +* Class Membership: +* PcdMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astPcdMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astPcdMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astPcdMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astPcdMap_. + +* Returned Value: +* The ID value associated with the new PcdMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPcdMap *new; /* Pointer to new PcdMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the PcdMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPcdMap( NULL, sizeof( AstPcdMap ), !class_init, &class_vtab, + "PcdMap", disco, pcdcen ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PcdMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new PcdMap. */ + return astMakeId( new ); +} + +AstPcdMap *astInitPcdMap_( void *mem, size_t size, int init, + AstPcdMapVtab *vtab, const char *name, + double disco, const double pcdcen[2], int *status ){ +/* +*+ +* Name: +* astInitPcdMap + +* Purpose: +* Initialise a PcdMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "pcdmap.h" +* AstPcdMap *astInitPcdMap( void *mem, size_t size, int init, +* AstPcdMapVtab *vtab, const char *name, +* double disco, const double pcdcen[2] ) + +* Class Membership: +* PcdMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new PcdMap object. It allocates memory (if necessary) to accommodate +* the PcdMap plus any additional data associated with the derived class. +* It then initialises a PcdMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a PcdMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the PcdMap is to be initialised. +* This must be of sufficient size to accommodate the PcdMap data +* (sizeof(PcdMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the PcdMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the PcdMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the PcdMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new PcdMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* disco +* Distortion coefficient. +* pcdcen +* Distortion centre. + +* Returned Value: +* A pointer to the new PcdMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPcdMap *new; /* Pointer to new PcdMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPcdMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Mapping structure (the parent class) as the first component + within the PcdMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstPcdMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 2, 2, 1, 1 ); + + if ( astOK ) { + +/* Initialise the PcdMap data. */ +/* ---------------------------- */ +/* Store the shift and scale for each axis. */ + new->pcdcen[0] = pcdcen[0]; + new->pcdcen[1] = pcdcen[1]; + new->disco = disco; + +/* If an error occurred, clean up by deleting the new PcdMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new PcdMap. */ + return new; +} + +AstPcdMap *astLoadPcdMap_( void *mem, size_t size, + AstPcdMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPcdMap + +* Purpose: +* Load a PcdMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "pcdmap.h" +* AstPcdMap *astLoadPcdMap( void *mem, size_t size, +* AstPcdMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* PcdMap loader. + +* Description: +* This function is provided to load a new PcdMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* PcdMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a PcdMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the PcdMap is to be +* loaded. This must be of sufficient size to accommodate the +* PcdMap data (sizeof(PcdMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the PcdMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the PcdMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPcdMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new PcdMap. If this is NULL, a pointer +* to the (static) virtual function table for the PcdMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "PcdMap" is used instead. + +* Returned Value: +* A pointer to the new PcdMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPcdMap *new; /* Pointer to the new PcdMap */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this PcdMap. In this case the + PcdMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPcdMap ); + vtab = &class_vtab; + name = "PcdMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPcdMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built PcdMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "PcdMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* PcdCen(0). */ +/* ---------- */ + new->pcdcen[0] = astReadDouble( channel, "pcdcn0", AST__BAD ); + if ( TestPcdCen( new, 0, status ) ) SetPcdCen( new, 0, new->pcdcen[0], status ); + +/* PcdCen(1). */ +/* ---------- */ + new->pcdcen[1] = astReadDouble( channel, "pcdcn1", AST__BAD ); + if ( TestPcdCen( new, 1, status ) ) SetPcdCen( new, 1, new->pcdcen[1], status ); + +/* Disco. */ +/* ------ */ + new->disco = astReadDouble( channel, "disco", AST__BAD ); + if ( TestDisco( new, status ) ) SetDisco( new, new->disco, status ); + +/* If an error occurred, clean up by deleting the new PcdMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new PcdMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + diff --git a/pcdmap.h b/pcdmap.h new file mode 100644 index 0000000..ca8fc24 --- /dev/null +++ b/pcdmap.h @@ -0,0 +1,357 @@ +#if !defined( PCDMAP_INCLUDED ) /* Include this file only once */ +#define PCDMAP_INCLUDED +/* +*+ +* Name: +* pcdmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the PcdMap class. + +* Invocation: +* #include "pcdmap.h" + +* Description: +* This include file defines the interface to the PcdMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The PcdMap class implements Mappings which perform pincushion +* distortion. + +* Inheritance: +* The PcdMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* Disco (double) +* This attribute holds the PcdMap distortion coefficient used by +* the forward transformation. This coefficient is set when a +* PcdMap is created, but may later be modified. The default value +* is zero, which gives no distortion. For pincushion distortion, +* the supplied value should be positive. For barrel distortion, it +* should be negative. +* +* Note that the forward transformation of a PcdMap applies the +* distortion corresponding to this attribute, and the inverse +* transformation removes this distortion. If a PcdMap is inverted +* (e.g. by using astInvert), then the forward transformation will +* remove the distortion and the inverse transformation will apply +* it. The distortion itself will still be given by the same value of +* Disco. +* PcdCen(axis) +* This attribute specifies the centre of a pincushion distortion. +* It takes a separate value for each axis of the PcdMap so that, for +* instance, the settings "PcdCen(1)=345.0,PcdCen(2)=-104.4" specify +* that the pincushion distortion is centred at values of 345.0 and +* -104.4 on axes 1 and 2 of the PcdMap. The default for both axes is +* zero. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astClearAttrib +* Clear an attribute value for a PcdMap. +* astGetAttrib +* Get an attribute value for a PcdMap. +* astSetAttrib +* Set an attribute value for a PcdMap. +* astTestAttrib +* Test if an attribute value has been set for a PcdMap. +* astTransform +* Apply a PcdMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astClearDisco +* Clear the Disco attribute value for a PcdMap. +* astGetDisco +* Get the Disco attribute value for a PcdMap. +* astSetDisco +* Set the Disco attribute value for a PcdMap. +* astTestDisco +* Test if a Disco attribute value has been set for a PcdMap. +* astClearPcdCen +* Clear the PcdCen attribute value for a PcdMap. +* astGetPcdCen +* Get the PcdCen attribute value for a PcdMap. +* astSetPcdCen +* Set the PcdCen attribute value for a PcdMap. +* astTestPcdCen +* Test if a PcdCen attribute value has been set for a PcdMap. + +* Other Class Functions: +* Public: +* astIsAPcdMap +* Test class membership. +* astPcdMap +* Create a PcdMap. +* +* Protected: +* astCheckPcdMap +* Validate class membership. +* astInitPcdMap +* Initialise a PcdMap. +* astInitPcdMapVtab +* Initialise the virtual function table for the PcdMap class. +* astLoadPcdMap +* Load a PcdMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstPcdMap +* PcdMap object type. +* +* Protected: +* AstPcdMapVtab +* PcdMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 18-MAY-1999 (DSB): +* Original version. +* 8-JAN-2003 (DSB): +* Added protected astInitPcdMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* PcdMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstPcdMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double disco; /* Distortion coefficient */ + double pcdcen[2]; /* Distortion centre */ + +} AstPcdMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPcdMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + double (*GetDisco)( AstPcdMap *, int * ); + int (* TestDisco)( AstPcdMap *, int * ); + void (* ClearDisco)( AstPcdMap *, int * ); + void (* SetDisco)( AstPcdMap *, double, int * ); + double (*GetPcdCen)( AstPcdMap *, int, int * ); + int (* TestPcdCen)( AstPcdMap *, int, int * ); + void (* ClearPcdCen)( AstPcdMap *, int, int * ); + void (* SetPcdCen)( AstPcdMap *, int, double, int * ); +} AstPcdMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstPcdMapGlobals { + +/* Define the thread-specific globals. */ + char GetAttrib_Buff[ 101 ]; + AstPcdMapVtab Class_Vtab; + int Class_Init; +} AstPcdMapGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(PcdMap) /* Check class membership */ +astPROTO_ISA(PcdMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPcdMap *astPcdMap_( double, const double [2], const char *, int *, ...); +#else +AstPcdMap *astPcdMapId_( double, const double [2], const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPcdMap *astInitPcdMap_( void *, size_t, int, AstPcdMapVtab *, + const char *, double, const double [2], int * ); + +/* Vtab initialiser. */ +void astInitPcdMapVtab_( AstPcdMapVtab *, const char *, int * ); + +/* Loader. */ +AstPcdMap *astLoadPcdMap_( void *, size_t, AstPcdMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitPcdMapGlobals_( AstPcdMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +double astGetDisco_( AstPcdMap *, int * ); +int astTestDisco_( AstPcdMap *, int * ); +void astClearDisco_( AstPcdMap *, int * ); +void astSetDisco_( AstPcdMap *, double, int * ); +double astGetPcdCen_( AstPcdMap *, int, int * ); +int astTestPcdCen_( AstPcdMap *, int, int * ); +void astClearPcdCen_( AstPcdMap *, int, int * ); +void astSetPcdCen_( AstPcdMap *, int, double, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPcdMap(this) astINVOKE_CHECK(PcdMap,this,0) +#define astVerifyPcdMap(this) astINVOKE_CHECK(PcdMap,this,1) + +/* Test class membership. */ +#define astIsAPcdMap(this) astINVOKE_ISA(PcdMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astPcdMap astINVOKE(F,astPcdMap_) +#else +#define astPcdMap astINVOKE(F,astPcdMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPcdMap(mem,size,init,vtab,name,disco,pcdcen) \ +astINVOKE(O,astInitPcdMap_(mem,size,init,vtab,name,disco,pcdcen,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPcdMapVtab(vtab,name) astINVOKE(V,astInitPcdMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadPcdMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPcdMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckPcdMap to validate PcdMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astClearDisco(this) astINVOKE(V,astClearDisco_(astCheckPcdMap(this),STATUS_PTR)) +#define astGetDisco(this) astINVOKE(V,astGetDisco_(astCheckPcdMap(this),STATUS_PTR)) +#define astSetDisco(this,value) \ +astINVOKE(V,astSetDisco_(astCheckPcdMap(this),value,STATUS_PTR)) +#define astTestDisco(this) astINVOKE(V,astTestDisco_(astCheckPcdMap(this),STATUS_PTR)) + +#define astClearPcdCen(this,axis) \ +astINVOKE(V,astClearPcdCen_(astCheckPcdMap(this),axis,STATUS_PTR)) +#define astGetPcdCen(this,axis) \ +astINVOKE(V,astGetPcdCen_(astCheckPcdMap(this),axis,STATUS_PTR)) +#define astSetPcdCen(this,axis,value) \ +astINVOKE(V,astSetPcdCen_(astCheckPcdMap(this),axis,value,STATUS_PTR)) +#define astTestPcdCen(this,axis) \ +astINVOKE(V,astTestPcdCen_(astCheckPcdMap(this),axis,STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/permmap.c b/permmap.c new file mode 100644 index 0000000..141b8d0 --- /dev/null +++ b/permmap.c @@ -0,0 +1,3199 @@ +/* +*class++ +* Name: +* PermMap + +* Purpose: +* Coordinate permutation Mapping. + +* Constructor Function: +c astPermMap +f AST_PERMMAP + +* Description: +* A PermMap is a Mapping which permutes the order of coordinates, +* and possibly also changes the number of coordinates, between its +* input and output. +* +* In addition to permuting the coordinate order, a PermMap may +* also assign constant values to coordinates. This is useful when +* the number of coordinates is being increased as it allows fixed +* values to be assigned to any new ones. + +* Inheritance: +* The PermMap class inherits from the Mapping class. + +* Attributes: +* The PermMap class does not define any new attributes beyond +* those which are applicable to all Mappings. + +* Functions: +c The PermMap class does not define any new functions beyond those +f The PermMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 29-FEB-1996 (RFWS): +* Original version. +* 26-SEP-1996 (RFWS): +* Added external interface and I/O facilities. +* 3-JUN-1997 (RFWS): +* Over-ride the MapMerge method. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitPermMapVtab +* method. +* 11-SEP-2003 (DSB): +* Added methods astGetInPerm and astGetOutPerm. +* 2-NOV-2004 (DSB): +* Added method astGetConstants. +* 14-MAR-2006 (DSB): +* Override astEqual. +* 2-MAY-2007 (DSB): +* Change MapSplit so that it does not try to use the +* implementation from the parent Mapping class, since this +* class can do a better job. +* 11-SEP-2007 (DSB): +* In MapSplit, check that the permuted axis index is less than the +* number of axes available. Use AST__BAD otherwise. +* 10-JAN-2011 (DSB): +* Add protected PermSplit attribute. +* 11-FEB-2011 (DSB): +* Do not allow MapSplit to return a Mapping with zero outputs. +* 22-NOV-2012 (DSB): +* When using a default inperm array (as indicated by a NULL pointer +* for inperm), ensure the array is padded with "-1" values if the +* number of inputs exceeds the number of outputs. Also do the +* equivalent for default outperm arrays. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS PermMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Macros */ +/* ============= */ +/* A macro that returns the inperm or outperm value to use for a given + index, taking account of the possibility that the inperm or outperm may + be NULL (implying a unit permutation), and that he numbers of inputs + and outputs may not be equal. "perms" is a pointer to the integer + permutation array (inperm or outperm), "i" is the index of the required + element of the permutation array, and "maxperm" is one more than the + maximum value allowed in the permutation array (i.e. the number of + PermMap outputs if "perms" is inperm, or PermMap inputs if "perms" is + outperm). */ +#define PERMVAL( perms, i, maxperm ) ( perms ? perms[ i ] : ( i < maxperm ? i : -1 )) + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(PermMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(PermMap,Class_Init) +#define class_vtab astGLOBAL(PermMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPermMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPermMap *astPermMapId_( int, const int [], int, const int [], const double [], const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double *GetConstants( AstPermMap *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int *GetInPerm( AstPermMap *, int * ); +static int *GetOutPerm( AstPermMap *, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int NullPerm( AstPermMap *, int, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); + +static void SetPermSplit( AstPermMap *, int, int * ); +static void ClearPermSplit( AstPermMap *, int * ); +static int TestPermSplit( AstPermMap *, int * ); +static int GetPermSplit( AstPermMap *, int * ); + + +/* Member functions. */ +/* ================= */ + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two PermMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "permmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* PermMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two PermMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a PermMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the PermMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPermMap *that; + AstPermMap *this; + int *that_inp; + int *that_outp; + int *this_inp; + int *this_outp; + int i; + int nin; + int nin_that; + int nout; + int nout_that; + int p1; + int p2; + int result; + int that_inp_len; + int that_outp_len; + int this_inp_len; + int this_outp_len; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two PermMap structures. */ + this = (AstPermMap *) this_object; + that = (AstPermMap *) that_object; + +/* Check the second object is a PermMap. We know the first is a + PermMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAPermMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNout( that ) == nout && astGetNin( that ) == nin ) { + +/* Assume the PermMaps are equivalent. */ + result = 1; + +/* Get the number of inputs and outputs in the second PermMap. */ + nin_that = astGetNin( that ); + nout_that = astGetNout( that ); + +/* Get pointers to the effective inperm and outperm array for each PermMap. + If the Invert flags of the two PermMaps are not equal, we swap the + arrays for the second PermMap in order to take account of the relative + inversion of the second PermMap. */ + this_inp = this->inperm; + this_outp = this->outperm; + + if( astGetInvert( this ) ) { + this_inp_len = nout; + this_outp_len = nin; + } else { + this_inp_len = nin; + this_outp_len = nout; + } + + if( astGetInvert( this ) != astGetInvert( that ) ) { + that_inp = that->outperm; + that_outp = that->inperm; + + if( astGetInvert( that ) ) { + that_inp_len = nin_that; + that_outp_len = nout_that; + } else { + that_inp_len = nout_that; + that_outp_len = nin_that; + } + + } else { + that_inp = that->inperm; + that_outp = that->outperm; + + if( astGetInvert( that ) ) { + that_inp_len = nout_that; + that_outp_len = nin_that; + } else { + that_inp_len = nin_that; + that_outp_len = nout_that; + } + } + +/* Loop round every PermMap input. */ + for( i = 0; i < nin; i++ ) { + +/* See what is fed to this input by the inverse transformation. A zero or + positive integer "p" value indicates that the input is fed from the + output with the corresponding index. A negative integer "p" value means + the input is fed a constant value stored at index (-p-1) in the + associated constants array. */ + p1 = PERMVAL( this_inp, i, this_outp_len ); + p2 = PERMVAL( that_inp, i, that_outp_len ); + +/* If the "p" values differ, we may have evidence that the PermMaps are + not equivalent. */ + if( p1 != p2 ) { + +/* If either "p" value is zero or positive, then the PermMaps are + definitely different since input "i" is fed from differing outputs, or + one is fed from an input and the other is fed a constant. */ + if( p1 >= 0 || p2 >= 0 ) { + result = 0; + break; + +/* If both "p" values are negative, then both inputs are fed a constant + value. The PermMaps differ if these constants differ. */ + } else if( this->constant[ -p1 - 1 ] != + that->constant[ -p2 - 1 ] ) { + result = 0; + break; + } + } + } + +/* If we have not yet discovered any evidence that the PermMaps differ, + go on to check each output in the same way that we have just checked the + inputs. */ + if( result ) { + for( i = 0; i < nout; i++ ) { + p1 = PERMVAL( this_outp, i, this_inp_len ); + p2 = PERMVAL( that_outp, i, that_inp_len ); + + if( p1 != p2 ) { + if( p1 >= 0 || p2 >= 0 ) { + result = 0; + break; + } else if( this->constant[ -p1 - 1 ] != + that->constant[ -p2 - 1 ] ) { + result = 0; + break; + } + } + } + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static double *GetConstants( AstPermMap *this, int *status ){ +/* +*+ +* Name: +* astGetConstants + +* Purpose: +* Return a pointer to the constants array of a PermMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "permmap.h" +* double *astGetConstants( AstPermMap *this ) + +* Class Membership: +* PermMap method + +* Description: +* This function returns a pointer to a dynamically allocated array +* holding a copy of the constants array supplied when the PermMap was +* created. +* +* Negative values in the arrays returned by the astGetInPerm and +* astGetOutPerm methods can be used as indices into the constants +* array returned by this method, if they are first negated and then +* decrement by one. Thus an inperm value of -3 refers to element 2 of +* the constants array. + +* Parameters: +* this +* Pointer to the PermMap. + +* Returned Value: +* A pointer to a dynamically allocated array holding a copy of the +* constants array. The pointer should be freed using astFree when it is +* no longer needed. NULL will be returned if the PermMap has no +* constants. + +* Notes: +* - A value of NULL will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + double *result; /* Pointer to the returned array */ + +/* Initialise the returned result. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate memory and put a copy of the InPerm array in it. */ + result = (double *) astStore( NULL, this->constant, astSizeOf( this->constant ) ); + +/* Return the result. */ + return result; +} + +static int *GetInPerm( AstPermMap *this, int *status ){ +/* +* Name: +* GetInPerm + +* Purpose: +* Return a pointer to the InPerm array of a PermMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "permmap.h" +* int *astGetInPerm( AstPermMap *this, int *status ) + +* Class Membership: +* PermMap method + +* Description: +* This function returns a pointer to a dynamically allocated array +* holding a copy of the InPerm array supplied when thre PermMap was +* created. + +* Parameters: +* this +* Pointer to the PermMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array holding a copy of the +* InPerm array. The pointer should be freed using astFree when it is +* no longer needed. The number of elements in the array will be given +* by the value of the Nin attribute. The value in element "i" is the +* zero-based index of the output axis which provides values for input +* "i" when the inverse transformation is used. If the value in element +* "i" is less than zero, then input "i" is fed a constant value. This +* constant value is stored in the "constants" array (see astGetConstants) +* at an index equal to the absolute value of inperm[i], minus 1. Thus +* if element 3 of the array returned by this function has value -2, +* then input axis 3 is fed the value held in constants[1]. If the +* value of element "i" of the returned inperm array is greater than +* or equal to the number of output axes, then input "i" will be fed +* the constant AST__BAD. + +* Notes: +* - A value of NULL will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int i; /* Loop count */ + int nin; /* Number of inputs. */ + int *result; /* Pointer to the returned array */ + +/* Initialise the returned result. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If no inperm array is stored, every input is derived from the + corresponding output. Therefore, return an array holding 0 to Nin-1. */ + if( !this->inperm ) { + nin = astGetNin( this ); + result = (int *) astMalloc( sizeof( int ) * (size_t) nin ); + if( astOK ) for( i = 0; i < nin; i++ ) result[ i ] = i; + +/* Otherwise, allocate memoy and put a copy of the InPerm array in it. */ + } else { + result = (int *) astStore( NULL, this->inperm, + sizeof( int ) * (size_t) astGetNin( this ) ); + } + +/* Return the result. */ + return result; +} + +static int *GetOutPerm( AstPermMap *this, int *status ){ +/* +* Name: +* GetOutPerm + +* Purpose: +* Return a pointer to the OutPerm array of a PermMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "permmap.h" +* int *astGetOutPerm( AstPermMap *this, int *status ) + +* Class Membership: +* PermMap method + +* Description: +* This function returns a pointer to a dynamically allocated array +* holding a copy of the OutPerm array supplied when thre PermMap was +* created. + +* Parameters: +* this +* Pointer to the PermMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array holding a copy of the +* OutPerm array. The pointer should be freed using astFree when it is +* no longer needed. The number of elements in the array will be given +* by the value of the Nout attribute. The value in element "i" is the +* zero-based index of the input axis which provides values for output +* "i" when the forward transformation is used. If the value in element +* "i" is less than zero, then output "i" is fed a constant value. This +* constant value is stored in the "constants" array (see astGetConstants) +* at an index equal to the absolute value of outperm[i], minus 1. Thus +* if element 3 of the array returned by this function has value -2, +* then output axis 3 is fed the value held in constants[1]. If the +* value of element "i" of the returned outperm array is greater than +* or equal to the number of input axes, then output "i" will be fed +* the constant AST__BAD. + +* Notes: +* - A value of NULL will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int i; /* Loop count */ + int nout; /* Number of outputs. */ + int *result; /* Pointer to the returned array */ + +/* Initialise the returned result. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If no outperm array is stored, every output is derived from the + corresponding input. Therefore, return an array holding 0 to Nout-1. */ + if( !this->outperm ) { + nout = astGetNout( this ); + result = (int *) astMalloc( sizeof( int ) * (size_t) nout ); + if( astOK ) for( i = 0; i < nout; i++ ) result[ i ] = i; + +/* Otherwise, allocate memory and put a copy of the OutPerm array in it. */ + } else { + result = (int *) astStore( NULL, this->outperm, + sizeof( int ) * (size_t) astGetNout( this ) ); + } + +/* Return the result. */ + return result; +} + +void astInitPermMapVtab_( AstPermMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPermMapVtab + +* Purpose: +* Initialise a virtual function table for a PermMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "permmap.h" +* void astInitPermMapVtab( AstPermMapVtab *vtab, const char *name ) + +* Class Membership: +* PermMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the PermMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPermMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->GetConstants = GetConstants; + vtab->GetInPerm = GetInPerm; + vtab->GetOutPerm = GetOutPerm; + vtab->ClearPermSplit = ClearPermSplit; + vtab->GetPermSplit = GetPermSplit; + vtab->SetPermSplit = SetPermSplit; + vtab->TestPermSplit = TestPermSplit; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + mapping->MapSplit = MapSplit; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + mapping->Rate = Rate; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "PermMap", "Coordinate permutation" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* PermMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated PermMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated PermMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated PermMap which is to be merged with +* its neighbours. This should be a cloned copy of the PermMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* PermMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated PermMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *map; /* Pointer to Mapping */ + AstMapping *new; /* Pointer to replacement Mapping */ + AstPermMap *permmap; /* Pointer to PermMap */ + const char *class; /* Pointer to Mapping class string */ + double *con; /* Pointer to constants array */ + double constant; /* Constant value */ + int *inperm; /* Pointer to "inperm" permutation array */ + int *newperm; /* Pointer to new permutation array */ + int *outperm; /* Pointer to "outperm" permutation array */ + int *perm; /* Pointer to individual permutation array */ + int back; /* Considering inverse transformation? */ + int coord; /* Loop counter for coordinates */ + int icon; /* Loop counter for constants */ + int iend; /* Loop ending value */ + int imap1; /* Index of first Mapping */ + int imap2; /* Index of last Mapping */ + int imap; /* Loop counter for Mappings */ + int inc; /* Loop increment */ + int invert; /* Invert attribute value */ + int istart; /* Loop starting value */ + int maxperm; /* Max value (+1) allowed in permutation array */ + int ncon; /* Number of constants */ + int ncoord_in; /* Effective number of input coordinates */ + int ncoord_out; /* Effective number of output coordinates */ + int ngone; /* Number of Mappings eliminated */ + int nin; /* Total number of input coordinates */ + int ninsum; /* Accumulated count of input coordinates */ + int nout; /* Total number of output coordinates */ + int noutsum; /* Accumulated count of output coordinates */ + int nperm; /* Number of permutation array elements */ + int p; /* Permuted coordinate index */ + int result; /* Result value to return */ + int simpler; /* Mapping(s) simplified? */ + int store_in; /* Need to store "inperm" array contents? */ + int store_out; /* Need to store "outperm" array contents? */ + int unit; /* Replacement Mapping is a UnitMap? */ + +/* Initialise the returned result. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + con = NULL; + inperm = outperm = NULL; + ncon = 0; + permmap = NULL; + +/* In series. */ +/* ---------- */ +/* Handle the case where the Mappings are connected in series. */ + if ( series ) { + +/* Search adjacent lower-numbered Mappings until one is found which is + not a PermMap or a UnitMap. */ + imap1 = where; + while ( ( imap1 - 1 ) >= 0 ) { + class = astGetClass( ( *map_list )[ imap1 - 1 ] ); + if ( astOK ) { + if ( strcmp( class, "PermMap" ) && + strcmp( class, "UnitMap" ) ) break; + imap1--; + } + } + +/* Similarly search adjacent higher-numbered Mappings. */ + imap2 = where; + while ( ( imap2 + 1 ) < *nmap ) { + class = astGetClass( ( *map_list )[ imap2 + 1 ] ); + if ( astOK ) { + if ( strcmp( class, "PermMap" ) && + strcmp( class, "UnitMap" ) ) break; + imap2++; + } + } + +/* Obtain a pointer to the first Mapping found and determine if it is + to be applied with its Invert attribute set. */ + map = ( *map_list )[ imap1 ]; + invert = ( *invert_list )[ imap1 ]; + +/* Use this first Mapping (allowing for how its Invert attribute is + currently set) to determine the number of input coordinates that + the simplified Mapping should have. */ + if ( astGetInvert( map ) ) { + nin = invert ? astGetNin( map ) : astGetNout( map ); + } else { + nin = invert ? astGetNout( map ) : astGetNin( map ); + } + +/* Repeat this process for the last Mapping found, to determine the + number of output coordinates for the simplified Mapping. */ + map = ( *map_list )[ imap2 ]; + invert = ( *invert_list )[ imap2 ]; + if ( astGetInvert( map ) ) { + nout = invert ? astGetNout( map ) : astGetNin( map ); + } else { + nout = invert ? astGetNin( map ) : astGetNout( map ); + } + +/* Allocate memory to hold input and output permutation arrays for the + simplified Mapping, together with a list of constants. */ + inperm = astMalloc( sizeof( int ) * (size_t) nin ); + outperm = astMalloc( sizeof( int ) * (size_t) nout ); + con = astMalloc( sizeof( double ) * (size_t) ( nin + nout ) ); + if ( astOK ) { + +/* Initialise the number of constants. */ + ncon = 0; + +/* Loop twice, to calculate the forward and inverse (backward) + simplified permutation arrays in turn. */ + for ( back = 0; back <= 1; back++ ) { + +/* Obtain a pointer to the appropriate (forward/inverse) permutation + array that we wish to fill, and obtain the number of elements it + will contain. Initialise the array contents to represent a null + permutation.*/ + newperm = back ? outperm : inperm; + nperm = back ? nout : nin; + for ( coord = 0; coord < nperm; coord++ ) newperm[ coord ] = coord; + +/* Set up limits to scan through the list of Mappings being merged in + either the forward or reverse order, as required. */ + istart = back ? imap2 : imap1; + iend = back ? imap1 - 1 : imap2 + 1; + inc = back ? -1 : 1; + +/* Loop through the Mappings, obtaining a pointer to each, together + with the value to be used for its Invert attribute. Invert this + attribute value if calculating the overall inverse (backward) + permutation array. */ + for ( imap = istart; imap != iend; imap += inc ) { + map = ( *map_list )[ imap ]; + invert = ( *invert_list )[ imap ]; + if ( back ) invert = !invert; + +/* Determine the class to which the Mapping belongs. */ + class = astGetClass( map ); + if ( astOK ) { + +/* If it is a PermMap, obtain a pointer to the PermMap structure and + hence to the relevant permutation array. Otherwise (if it is a + UnitMap), leave the permutation array pointer NULL, which indicates + a null permutation. */ + perm = NULL; + maxperm = astGetNout( map ); + if ( !strcmp( class, "PermMap" ) ) { + permmap = (AstPermMap *) map; + perm = invert ? permmap->outperm : permmap->inperm; + } + +/* Obtain the effective number of output coordinates associated with + this individual Mapping (when transforming points in the direction + to which this permutation array applies). */ + if ( astGetInvert( map ) ) { + ncoord_out = invert ? astGetNout( map ) : + astGetNin( map ); + } else { + ncoord_out = invert ? astGetNin( map ) : + astGetNout( map ); + } + +/* Loop through the elements of the simplified permutation array to + accumulate the effects of the current individual Mapping. */ + if ( astOK ) { + for ( coord = 0; coord < nperm; coord++ ) { + +/* Find the effective input coordinate for the current Mapping from + the permutation accumulated so far, and check this is not + negative. If it is, the accumulated permutation refers to a "bad" + coordinate value or a constant, so the current Mapping makes no + further difference. */ + p = newperm[ coord ]; + if ( p >= 0 ) { + +/* Otherwise, obtain the permuting effect of the current Mapping, + allowing for the possibility of its permutation array being NULL + (implying a null permutation). */ + p = PERMVAL( perm, p, maxperm ); + +/* If the permuted index refers to a valid (effective) output + coordinate for the individual Mapping, then accumulate its effect + in the overall permutation array. */ + if ( ( p >= 0 ) && ( p < ncoord_out ) ) { + newperm[ coord ] = p; + +/* Otherwise (this can only occur if the individual Mapping is a + PermMap), determine whether it refers to a "bad" coordinate value + or a constant. If the former, extract the constant's value, + otherwise use a constant value of AST__BAD. */ + } else { + if ( ( p < 0 ) && permmap->constant ) { + constant = permmap->constant[ (-p) - 1 ]; + } else { + constant = AST__BAD; + } + +/* If the result (however reached) is a coordinate value of AST__BAD, + then mark the accumulated permutation array with a value of -1 to + indicate this. */ + if ( constant == AST__BAD ) { + newperm[ coord ] = -1; + +/* Otherwise, search the array of constants to see if this one has + been encountered before. If not, append the new constant to the + list. */ + } else { + for ( icon = 0; icon < ncon; icon++ ) { + if ( con[ icon ] == constant ) break; + } + if ( icon == ncon ) con[ ncon++ ] = constant; + +/* Store a (negative) reference to the new constant in the accumulated + permutation array (note we use an extra offset of -1 here in + forming these references, so that the value -1 itself can be used + to indicate a "bad" coordinate value without an entry in the + constants array). */ + newperm[ coord ] = (-icon) - 2; + } + } + } + } + } + } + } + } + } + +/* In parallel. */ +/* ------------ */ +/* Handle the case where the Mappings are connected in parallel. */ + } else { + +/* Obtain a pointer to the nominated Mapping (which is a PermMap) and + determine if it is to be applied with its Invert attribute set. */ + map = ( *map_list )[ where ]; + invert = ( *invert_list )[ where ]; + +/* Use this nominated Mapping to initialise the counts of input and + output coordinates for the simplified Mapping (allowing for how its + Invert attribute is currently set). */ + if ( astGetInvert( map ) ) { + nin = invert ? astGetNin( map ) : astGetNout( map ); + nout = invert ? astGetNout( map ) : astGetNin( map ); + } else { + nin = invert ? astGetNout( map ) : astGetNin( map ); + nout = invert ? astGetNin( map ) : astGetNout( map ); + } + +/* Search adjacent lower-numbered Mappings until one is found which is + not a PermMap or a UnitMap. */ + imap1 = where; + while ( astOK && ( ( imap1 - 1 ) >= 0 ) ) { + map = ( *map_list )[ imap1 - 1 ]; + class = astGetClass( map ); + if ( astOK ) { + if ( strcmp( class, "PermMap" ) && + strcmp( class, "UnitMap" ) ) break; + +/* For each Mapping found, obtain the effective numbers of input and + output coordinates (allowing for all the direction flags, as above) + and accumulate the total count of input and output coordinates for + the overall simplified Mapping. */ + invert = ( *invert_list )[ imap1 - 1 ]; + if ( astGetInvert( map ) ) { + nin += ( invert ? astGetNin( map ) : astGetNout( map ) ); + nout += ( invert ? astGetNout( map ) : astGetNin( map ) ); + } else { + nin += ( invert ? astGetNout( map ) : astGetNin( map ) ); + nout += ( invert ? astGetNin( map ) : astGetNout( map ) ); + } + imap1--; + } + } + +/* Similarly search higher-numbered Mappings and accumulate their + coordinate counts. */ + imap2 = where; + while ( astOK && ( ( imap2 + 1 ) < *nmap ) ) { + map = ( *map_list )[ imap2 + 1 ]; + class = astGetClass( map ); + if ( astOK ) { + if ( strcmp( class, "PermMap" ) && + strcmp( class, "UnitMap" ) ) break; + invert = ( *invert_list )[ imap2 + 1 ]; + if ( astGetInvert( map ) ) { + nin += ( invert ? astGetNin( map ) : astGetNout( map ) ); + nout += ( invert ? astGetNout( map ) : astGetNin( map ) ); + } else { + nin += ( invert ? astGetNout( map ) : astGetNin( map ) ); + nout += ( invert ? astGetNin( map ) : astGetNout( map ) ); + } + imap2++; + } + } + +/* Allocate memory to hold input and output permutation arrays for the + simplified Mapping, together with a list of constants. */ + inperm = astMalloc( sizeof( int ) * (size_t) nin ); + outperm = astMalloc( sizeof( int ) * (size_t) nout ); + con = astMalloc( sizeof( double ) * (size_t) ( nin + nout ) ); + if ( astOK ) { + +/* Initialise the number of constants. */ + ncon = 0; + +/* Loop twice, to calculate the forward and inverse (backward) + simplified permutation arrays in turn. */ + for ( back = 0; back <= 1; back++ ) { + +/* Obtain a pointer to the appropriate (forward/inverse) permutation + array that we wish to fill, and obtain the number of elements it + will contain. */ + newperm = back ? outperm : inperm; + nperm = back ? nout : nin; + +/* Initialise counts of (effective) input and output coordinates. */ + ninsum = noutsum = 0; + +/* Loop through the Mappings, obtaining a pointer to each, together + with the value to be used for its Invert attribute. Invert this + attribute value if calculating the overall inverse (backward) + permutation array. */ + for ( imap = imap1; imap <= imap2; imap++ ) { + map = ( *map_list )[ imap ]; + invert = ( *invert_list )[ imap ]; + if ( back ) invert = !invert; + +/* Determine the class to which the Mapping belongs. */ + class = astGetClass( map ); + if ( astOK ) { + +/* If it is a PermMap, obtain a pointer to the PermMap structure and + hence to the relevant permutation array. Otherwise (if it is a + UnitMap), leave the permutation array pointer NULL, which indicates + a null permutation. */ + perm = NULL; + maxperm = astGetNout( map ); + if ( !strcmp( class, "PermMap" ) ) { + permmap = (AstPermMap *) map; + perm = invert ? permmap->outperm : permmap->inperm; + } + +/* Obtain the effective number of input and output coordinates + associated with this individual Mapping (when transforming points + in the direction to which this permutation array applies). */ + if ( astGetInvert( map ) ) { + ncoord_in = invert ? astGetNin( map ) : + astGetNout( map ); + ncoord_out = invert ? astGetNout( map ) : + astGetNin( map ); + } else { + ncoord_in = invert ? astGetNout( map ) : + astGetNin( map ); + ncoord_out = invert ? astGetNin( map ) : + astGetNout( map ); + } + +/* Loop through the (effective) input coordinates of the current + individual Mapping to accumulate their effect on the overall + permutation array. */ + if ( astOK ) { + for ( coord = 0; coord < ncoord_in; coord++ ) { + +/* Obtain the permuting effect of the current Mapping, allowing for + the possibility of its permutation array being NULL. */ + p = PERMVAL( perm, coord, maxperm ); + +/* If the permuted index refers to a valid (effective) output + coordinate for the individual Mapping, then accumulate its effect + on the overall permutation array, allowing for the coordinate + numbering offset produced by any Mappings already accumulated. */ + if ( ( p >= 0 ) && ( p < ncoord_out ) ) { + newperm[ coord + ninsum ] = p + noutsum; + +/* Otherwise (this can only occur if the individual Mapping is a + PermMap), determine whether it refers to a "bad" coordinate value + or a constant. If the former, extract the constant's value, + otherwise use a constant value of AST__BAD. */ + } else { + if ( ( p < 0 ) && permmap->constant ) { + constant = permmap->constant[ (-p) - 1 ]; + } else { + constant = AST__BAD; + } + +/* If the result (however reached) is a coordinate value of AST__BAD, + then mark the accumulated permutation array with a value of -1 to + indicate this. */ + if ( constant == AST__BAD ) { + newperm[ coord + ninsum ] = -1; + +/* Otherwise, search the array of constants to see if this one has + been encountered before. If not, append the new constant to the + list. */ + } else { + int icon; + for ( icon = 0; icon < ncon; icon++ ) { + if ( con[ icon ] == constant ) break; + } + if ( icon == ncon ) con[ ncon++ ] = constant; + +/* Store a (negative) reference to the new constant in the accumulated + permutation array (note we use an extra offset of -1 here in + forming these references, so that the value -1 itself can be used + to indicate a "bad" coordinate value without an entry in the + constants array). */ + newperm[ coord + ninsum ] = (-icon) - 2; + } + } + } + } + +/* Accumulate the counts of (effective) input and output coordinates + for each individual Mapping. */ + ninsum += ncoord_in; + noutsum += ncoord_out; + } + } + } + } + } + +/* Inspect each element of the accumulated "inperm" array to determine + if it needs to be stored by the replacement PermMap. */ + if ( astOK ) { + store_in = 0; + for ( coord = 0; coord < nin; coord++ ) { + +/* It need not be stored if it produces a null permutation, where each + input coordinate takes its value from the corresponding output + coordinate (or where a "bad" value results if there is no + corresponding output coordinate). Note any deviation from this + pattern. */ + if ( coord < nout ) { + store_in = store_in || ( inperm[ coord ] != coord ); + } else { + store_in = store_in || ( inperm[ coord ] != -1 ); + } + +/* Also convert permutation array values of -1 into non-existent + positive coordinate indices (indicating "bad" coordinate values) + and adjust (negative) references to constants by +1 to eliminate + the extra offset of -1 used temporarily above. This returns the + permutation array values to normal. */ + if ( inperm[ coord ] < 0 ) { + if ( !++inperm[ coord ] ) inperm[ coord ] = nout; + } + } + +/* Similarly inspect the "outperm" array and return its values to + normal. */ + store_out = 0; + for ( coord = 0; coord < nout; coord++ ) { + if ( coord < nin ) { + store_out = store_out || ( outperm[ coord ] != coord ); + } else { + store_out = store_out || ( outperm[ coord ] != -1 ); + } + if ( outperm[ coord ] < 0 ) { + if ( !++outperm[ coord ] ) outperm[ coord ] = nin; + } + } + +/* Determine how many adjacent Mappings can be eliminated by merging + them. */ + ngone = imap2 - imap1; + +/* Determine if the resultant PermMap can be simplified still further + to become a UnitMap (a null Mapping). This will be the case if both + the forward and inverse coordinate permutations it produces are + null, and if the number of input and output coordinates are + equal. */ + unit = !store_in && !store_out && ( nin == nout ); + +/* We must now determine whether we have actually produced any + simplification. This is important, because if we indicate a + simplification when none has, in fact, been achieved, then this + function may get called over and over again without end. */ + +/* Simplification is clearly evident if (a) Mappings have been + eliminated ("ngone" is non-zero), or (b) a PermMap has been reduced + to a UnitMap, or (c) where there was originally only one PermMap to + simplify, its invert flag was set (the replacement Mapping will + always have this flag cleared). */ + simpler = ngone || unit || ( *invert_list )[ where ]; + +/* If the above tests do not indicate simplification, then we can only + be considering the case where there was a single initial + PermMap. In this case we have also achieved simplification if + either the "inperm" or "outperm" array no longer needs storing + whereas previously it was stored. */ + permmap = (AstPermMap *) ( *map_list )[ where ]; + if ( !simpler ) { + simpler = ( !store_in && !NullPerm( permmap, 0, status ) ) || + ( !store_out && !NullPerm( permmap, 1, status ) ); + } + +/* If we still haven't detected any simplification, then compare the + original and replacement "inperm" arrays (if present) in detail for + equality. We declare simplification to have occurred if they + differ. */ + if ( !simpler && store_in ) { + for ( coord = 0; coord < nin; coord++ ) { + simpler = ( inperm[ coord ] != permmap->inperm[ coord ] ); + if ( simpler ) break; + } + } + +/* Similarly, if necessary, compare the original and replacement + "outperm" arrays. */ + if ( !simpler && store_out ) { + for ( coord = 0; coord < nout; coord++ ) { + simpler = ( outperm[ coord ] != permmap->outperm[ coord ] ); + if ( simpler ) break; + } + } + +/* Do nothing more unless there has been some simplification. */ + if ( simpler ) { + +/* If the PermMaps (and UnitMaps) can be replaced by a UnitMap, then + create the replacement. */ + if ( unit ) { + new = (AstMapping *) astUnitMap( nin, "", status ); + +/* Otherwise, create a replacement PermMap, setting as many arguments + to NULL in the constructor function as can be achieved without + affecting the result. */ + } else { + new = (AstMapping *) astPermMap( nin, store_in ? inperm : NULL, + nout, store_out ? outperm : NULL, + ncon ? con : NULL, "", status ); + } + +/* Annul the pointers to all the Mappings that are being replaced. */ + if ( astOK ) { + for ( imap = imap1; imap <= imap2; imap++ ) { + ( *map_list )[ imap ] = astAnnul( ( *map_list )[ imap ] ); + } + +/* Insert the new pointer and the associated invert flag. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Loop to close the resulting gap by moving subsequent elements down + in the arrays. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - ngone ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - ngone ] = ( *invert_list )[ imap ]; + } + +/* Clear the vacated elements at the end. */ + for ( imap = *nmap - ngone; imap < *nmap; imap++ ) { + ( *map_list )[ imap ] = NULL; + ( *invert_list )[ imap ] = 0; + } + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap ) -= ngone; + result = imap1; + } + } + } + +/* Free the workspace arrays. */ + inperm = astFree( inperm ); + outperm = astFree( outperm ); + con = astFree( con ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "permmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* PermMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing PermMap. This is only possible if the specified inputs +* correspond to some subset of the PermMap outputs. That is, there +* must exist a subset of the PermMap outputs for which each output +* depends only on the selected PermMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied PermMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the PermMap to be split (the PermMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied PermMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied PermMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied PermMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstPermMap *this; /* Pointer to PermMap structure */ + double *con; /* Pointer to constants array */ + int *inp; /* Input perm array to use with supplied PermMap */ + int *inpm; /* Input perm array to use with new PermMap */ + int *outp; /* Output perm array to use with supplied PermMap */ + int *outpm; /* Output perm array to use with new PermMap */ + int *result; /* Pointer to returned array */ + int i; /* Loop count */ + int iin; /* Mapping input index */ + int iout; /* Output index */ + int j; /* Loop count */ + int nout; /* No. of outputs in the new PermMap */ + int npin; /* No. of inputs in the supplied Mapping */ + int npout; /* No. of outputs in the supplied Mapping */ + int ok; /* Are input indices OK? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + + +/* Get a pointer to the PermMap structure. */ + this = (AstPermMap *) this_map; + +/* Get the number of inputs and outputs in the supplied PermMap. */ + npin = astGetNin( this ); + npout = astGetNout( this ); + +/* Check all input axis indices are valid. */ + ok = 1; + for( i = 0; i < nin; i++ ) { + if( in[ i ] < 0 || in[ i ] >= npin ) { + ok = 0; + break; + } + } + +/* Get pointers to the input and output permutation arrays and constant + array taking account of whether the PermMap has been inverted. */ + if( astGetInvert( this ) ) { + outp = this->inperm; + inp = this->outperm; + } else { + outp = this->outperm; + inp = this->inperm; + } + con = this->constant; + +/* The "normal" method, as described in the prologue. */ + if( astGetPermSplit( this ) == 0 ) { + +/* Allocate memory for the returned array of output indices. */ + result = astMalloc( sizeof( int )*(size_t) npout ); + +/* Allocate memory for the inperm and outperm arrays of the returned + PermMap. Make these the largest they could possible need to be. */ + inpm = astMalloc( sizeof( int )*(size_t) npin ); + outpm = astMalloc( sizeof( int )*(size_t) npout ); + if( astOK ) { + +/* Initialise number of outputs in returned PermMap. */ + nout = 0; + +/* Loop round each output of the supplied PermMap. */ + for( iout = 0; iout < npout; iout++ ) { + +/* Is this output fed by one of the selected inputs? If so store the input + index of the returned Mapping, which feeds this output and add this + output index to the list of returned outputs. */ + iin = PERMVAL( outp, iout, npin ); + if( iin >= 0 && iin < npin ) { + for( i = 0; i < nin; i++ ) { + if( in[ i ] == iin ) { + outpm[ nout ] = i; + result[ nout ] = iout; + nout++; + break; + } + } + } + } + +/* We now need to set up the inperm array for the returned PermMap. This + ensures that the inverse transformation in the returned Mapping provides + values for the selected inputs. Loop round all the selected inputs. */ + for( i = 0; i < nin; i++ ) { + iin = in[ i ]; + +/* Is this input constant or fed by one of the selected outputs? If so store + the output or constant index in the returned Mapping which feeds this + input. */ + ok = 0; + iout = PERMVAL( inp, iin, npout ); + if( iout >= 0 && iout < npout ) { + for( j = 0; j < nout; j++ ) { + if( result[ j ] == iout ) { + ok = 1; + inpm[ i ] = j; + break; + } + } + } else { + inpm[ i ] = iout; + ok = 1; + } + +/* If this input is fed by an output which has not been selected, then we + cannot produce the required Mapping. */ + if( !ok ) break; + } + +/* If possible produce the returned PermMap. Otherwise, free the returned + array. */ + if( ok && nout > 0 ) { + *map = (AstMapping *) astPermMap( nin, inpm, nout, outpm, con, "", status ); + } else { + result = astFree( result ); + } + +/* Free other resources. */ + inpm = astFree( inpm ); + outpm = astFree( outpm ); + } + +/* The "alternative" method. Only the inperm array is used - the outperm + array is assumed to be an exact inverse of the inperm array. In other + words, only the inverse transformation is used, and the forward + transformation is assumed to be the exact opposite. */ + } else { + +/* The returned array of output indices holds the "inperm" values for the + selected inputs. */ + result = astMalloc( sizeof( int )*(size_t) nin ); + if( astOK ) { + for( i = 0; i < nin; i++ ) { + result[ i ] = PERMVAL( inp, in[ i ], npout ); + +/* Check the input is not fed by a constant. */ + if( result[ i ] < 0 ) { + result = astFree( result ); + break; + +/* Check that the the output has not already been used. */ + } else { + for( j = 0; j < i; j++ ) { + if( result[ j ] == result[ i ] ) { + result = astFree( result ); + break; + } + } + } + } + +/* If the split was possible, the returned Mapping is a UnitMap. */ + if( result ) *map = (AstMapping *) astUnitMap( nin, " ", status ); + } + } + +/* If the returned Mapping has no outputs, do not return it. */ + if( !result && *map ) { + *map = astAnnul( *map ); + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static int NullPerm( AstPermMap *this, int forward, int *status ){ +/* +* Name: +* NullPerm + +* Purpose: +* See if a PermMap transformation represents a null axis permutation. + +* Type: +* Private function. + +* Synopsis: +* #include "permmap.h" +* int NullPerm( AstPermMap *this, int forward, int *status ) + +* Class Membership: +* PermMap method + +* Description: +* This function returns a logical value indicating if the specified +* transformation of the supplied PermMap is a null (i.e. unit) +* transformation. + +* Parameters: +* this +* Pointer to the PermMap. +* forward +* Check the forward transformation? Otherise, check the inverse +* transformation. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the specified transformation is a null axis permutation. +* Zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + int i; /* Coordinate index */ + int nin; /* Number of Mapping inputs */ + int nout; /* Number of Mapping outputs */ + int result; /* Returned value */ + +/* Initialise the returned result. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* First check the forward transformation, given by the outperm array. */ + if( forward ) { + +/* If no outperm array is stored, every output is derived from the + corresponding input. Therefore, return 1 indicating a null axis + permutation. */ + if( !this->outperm ) { + result = 1; + +/* Otherwise, check that every element in the outperm array indicates + that the output is derived from the input with the saem index. */ + } else { + result = 1; + nout = astGetNout( this ); + for( i = 0; i < nout; i++ ) { + if( this->outperm[ i ] != i ) { + result = 0; + break; + } + } + } + +/* Now check the inverse transformation, given by the inperm array. */ + } else { + +/* If no inperm array is stored, every input is derived from the + corresponding output. Therefore, return 1 indicating a null axis + permutation. */ + if( !this->inperm ) { + result = 1; + +/* Otherwise, check that every element in the inperm array indicates + that the input is derived from the output with the same index. */ + } else { + result = 1; + nin = astGetNin( this ); + for( i = 0; i < nin; i++ ) { + if( this->inperm[ i ] != i ) { + result = 0; + break; + } + } + } + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "permmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* PermMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + +/* Local Variables: */ + AstPermMap *map; + int *outperm; + int result; + +/* Check inherited status */ + if( !astOK ) return AST__BAD; + +/* Get a pointer to the PermMap structure. */ + map = (AstPermMap *) this; + +/* Obtain a pointer to the appropriate output coordinate permutation array, + according to whether the PermMap has been inverted. If the specified + output is derived from the specified input then the rate is unity. + Otherwise it is zero. */ + outperm = astGetInvert( this ) ? map->inperm : map->outperm; + if( outperm ) { + result = ( ax2 == outperm[ ax1 ] ) ? 1.0 : 0.0; + } else { + result = ( ax2 == ax1 ) ? 1.0 : 0.0; + } + + return result; +} + +static AstPointSet *Transform( AstMapping *map, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a PermMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "permmap.h" +* AstPointSet *Transform( AstMapping *map, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* PermMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a PermMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required coordinate +* permutation. + +* Parameters: +* map +* Pointer to the PermMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the PermMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPermMap *this; /* Pointer to PermMap to be applied */ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double constant; /* Constant coordinate value */ + int *perm; /* Pointer to permutation array */ + int coord; /* Loop counter for coordinates */ + int maxperm; /* Max value in permutation array */ + int ncoord_in; /* Number of coordinates per input point */ + int ncoord_out; /* Number of coordinates per output point */ + int npoint; /* Number of points */ + int p; /* Permuted coordinate index */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the PermMap. */ + this = (AstPermMap *) map; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( map, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + permutation needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + and output PointSets and obtain pointers for accessing the input and output + coordinate values. */ + ncoord_in = astGetNcoord( in ); + ncoord_out = astGetNcoord( result ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Obtain a pointer to the appropriate coordinate permutation array, according + to the direction of transformation required and whether the PermMap has + been inverted. Also get the maximum allowed value in the permutation array. */ + if ( ( astGetInvert( this ) != 0 ) == ( forward != 0 ) ) { + perm = this->inperm; + maxperm = ncoord_out; + } else { + perm = this->outperm; + maxperm = ncoord_in; + } + +/* Perform coordinate permutation. */ +/* ------------------------------- */ + if ( astOK ) { + +/* Loop to generate values for each output coordinate. */ + for ( coord = 0; coord < ncoord_out; coord++ ) { + +/* If the permutation array is not NULL, use it to look up which input + coordinate to use. Otherwise, use the corresponding input coordinate. */ + p = PERMVAL( perm, coord, maxperm ); + +/* If a valid input coordinate has been identified, simply copy the required + coordinate values from input to output. */ + if ( ( p >= 0 ) && ( p < ncoord_in ) ) { + (void) memcpy( ptr_out[ coord ], ptr_in[ p ], + sizeof( double ) * (size_t) npoint ); + +/* If the permuted coordinate index is negative, use it to index the "constant" + array to obtain a constant value to assign. If this array is NULL, use + AST__BAD as the constant. */ + } else if ( p < 0 ) { + constant = this->constant ? this->constant[ (-p) - 1 ] : AST__BAD; + +/* Assign the constant value to the output coordinate for all points. */ + for ( point = 0; point < npoint; point++ ) { + ptr_out[ coord ][ point ] = constant; + } + +/* In all other cases, simply assign the value AST__BAD to the output + coordinate for all points. */ + } else { + for ( point = 0; point < npoint; point++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* +*att+ +* Name: +* PermSplit + +* Purpose: +* The method to use when splitting a PermMap using astMapSplit. + +* Type: +* Protected attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute controls the behaviour of the implementation of the +* astMapSplit method provided by the PermMap class. If set to zero (the +* default), astMapSplit will split PermMaps according to the public +* documentation for the method. If set non-zero, the forward transformation +* of the PermMap defined by the "outperm" array will be ignored. +* Instead, the forward transformation is assumed to be the exact +* inverse of the inverse transformation. The Mapping returned will +* then be a UnitMap with Nin equal to the number of picked inputs, +* and the returned array of output indices will hold the "inperm" +* values for the picked inputs. Note, if any of these "inperm" values +* are negative (indicating that the inverse transformation supplies a +* constant value for the input), or if more than one of the selected +* inputs are fed (by the inverse transformation) by the same output, +* then the PermMap cannot be split. +* +* Note, the value of this attribute may changed only if the PermMap +* has no more than one reference. That is, an error is reported if the +* PermMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the astClone function. + +* Applicability: +* PermMap +* All PermMaps have this attribute. +*att- +*/ +astMAKE_CLEAR1(PermMap,PermSplit,permsplit,-INT_MAX) +astMAKE_GET(PermMap,PermSplit,int,0,( this->permsplit != -INT_MAX ? + this->permsplit : 0 )) +astMAKE_SET1(PermMap,PermSplit,int,permsplit,( value != 0 )) +astMAKE_TEST(PermMap,PermSplit,( this->permsplit != -INT_MAX )) + + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for PermMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for PermMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstPermMap *in; /* Pointer to input PermMap */ + AstPermMap *out; /* Pointer to output PermMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output PermMaps. */ + in = (AstPermMap *) objin; + out = (AstPermMap *) objout; + +/* For safety, first clear any references to the input memory from + the output PermMap. */ + out->inperm = NULL; + out->outperm = NULL; + out->constant = NULL; + +/* For each input array which is not NULL, make a copy in allocated memory, + storing a pointer to it in the output PermMap structure. */ + if ( in->inperm ) out->inperm = astStore( NULL, in->inperm, + astSizeOf( in->inperm ) ); + if ( in->outperm ) out->outperm = astStore( NULL, in->outperm, + astSizeOf( in->outperm ) ); + if ( in->constant ) out->constant = astStore( NULL, in->constant, + astSizeOf( in->constant ) ); + +/* If an error occurred, clean up by freeing all memory allocated above. */ + if ( !astOK ) { + out->inperm = astFree( out->inperm ); + out->outperm = astFree( out->outperm ); + out->constant = astFree( out->constant ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for PermMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for PermMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPermMap *this; /* Pointer to PermMap */ + +/* Obtain a pointer to the PermMap structure. */ + this = (AstPermMap *) obj; + +/* Free all memory allocated by the PermMap. */ + this->inperm = astFree( this->inperm ); + this->outperm = astFree( this->outperm ); + this->constant = astFree( this->constant ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for PermMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the PermMap class to an output Channel. + +* Parameters: +* this +* Pointer to the PermMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 150 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstPermMap *this; /* Pointer to the PermMap structure */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment strings */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword strings */ + int coord; /* Loop counter for coordinates */ + int iconst; /* Loop counter for constants */ + int invert; /* Invert attribute value */ + int ival; /* Integer value */ + int nconst; /* Number of constants */ + int nin; /* Number of input coordinates */ + int nout; /* Number of output coordinates */ + int set; /* Value is "set"? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PermMap structure. */ + this = (AstPermMap *) this_object; + +/* Determine if the PermMap is inverted and obtain the "true" number + of input and output coordinates by un-doing the effects of any + inversion. */ + invert = astGetInvert( this ); + nin = !invert ? astGetNin( this ) : astGetNout( this ); + nout = !invert ? astGetNout( this ) : astGetNin( this ); + +/* Initialise the count of constants in use. */ + nconst = 0; + +/* Write out values representing the instance variables for the + PermMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* PermSplit */ +/* --------- */ + set = TestPermSplit( this, status ); + ival = set ? GetPermSplit( this, status ) : astGetPermSplit( this ); + astWriteInt( channel, "pmsplt", set, 0, ival, + ival ? "Use alternative astMapSplit implementation" : + "Use normal astMapSplit implementation" ); + +/* "outperm" array contents. */ +/* ------------------------- */ +/* Write the boolean "OutCpy" value to indicate if output coordinates + are obtained simply by copying corresponding input + coordinates. This will be the case if "this->outperm" is NULL. */ + ival = this->outperm ? 0 : 1; + set = ( ival != 0 ); + astWriteInt( channel, "OutCpy", set, 0, ival, + ival ? "Output coordinates = input coordinates" : + "Output coordinates specified individually" ); + +/* If output coordinates are specified individually, create a keyword + for each element of the "outperm" array. */ + if ( this->outperm ) { + for ( coord = 0; coord < nout; coord++ ) { + (void) sprintf( key, "Out%d", coord + 1 ); + +/* Obtain the array value. If it refers to a coordinate that does not + exist, change the value to zero (indicating a "bad" value for this + coordinate). Create an appropriate comment. */ + ival = this->outperm[ coord ]; + if ( ival >= nin ) { + ival = 0; + (void) sprintf( comment, "Output coordinate %d is \"bad\"", + coord + 1 ); + +/* If the coordinate reference is valid, convert to 1-based coordinate + numbering and create an appropriate comment. */ + } else if ( ival >= 0 ) { + ival++; + (void) sprintf( comment, + "Output coordinate %d = input coordinate %d", + coord + 1, ival ); + +/* If the reference is to a constant, create an appropriate comment + (which depends on whether there are any constants). */ + } else { + if ( this->constant ) { + (void) sprintf( comment, + "Output coordinate %d = constant no. %d", + coord + 1, -ival ); + } else { + (void) sprintf( comment, "Output coordinate %d is \"bad\"", + coord + 1 ); + } + +/* Update the top constant number referenced. */ + if ( nconst < -ival ) nconst = -ival; + } + +/* Write out the array value with accompanying comment. */ + astWriteInt( channel, key, 1, 1, ival, comment ); + } + } + +/* "inperm" array contents. */ +/* ------------------------ */ +/* Write the boolean "InCpy" value to indicate if input coordinates + are obtained simply by copying corresponding output + coordinates. This will be the case if "this->inperm" is NULL. */ + ival = this->inperm ? 0 : 1; + set = ( ival != 0 ); + astWriteInt( channel, "InCpy", set, 0, ival, + ival ? "Input coordinates = output coordinates" : + "Input coordinates specified individually" ); + +/* If input coordinates are specified individually, create a keyword + for each element of the "inperm" array. */ + if ( this->inperm ) { + for ( coord = 0; coord < nin; coord++ ) { + (void) sprintf( key, "In%d", coord + 1 ); + +/* Obtain the array value. If it refers to a coordinate that does not + exist, change the value to zero (indicating a "bad" value for this + coordinate). Create an appropriate comment. */ + ival = this->inperm[ coord ]; + if ( ival >= nout ) { + ival = 0; + (void) sprintf( comment, "Input coordinate %d is \"bad\"", + coord + 1 ); + +/* If the coordinate reference is valid, convert to 1-based coordinate + numbering and create an appropriate comment. */ + } else if ( ival >= 0 ) { + ival++; + (void) sprintf( comment, + "Input coordinate %d = output coordinate %d", + coord + 1, ival ); + +/* If the reference is to a constant, create an appropriate comment + (which depends on whether there are any constants). */ + } else { + if ( this->constant ) { + (void) sprintf( comment, + "Input coordinate %d = constant no. %d", + coord + 1, -ival ); + } else { + (void) sprintf( comment, "Input coordinate %d is \"bad\"", + coord + 1 ); + } + +/* Update the top constant number referenced. */ + if ( nconst < -ival ) nconst = -ival; + } + +/* Write out the array value with accompanying comment. */ + astWriteInt( channel, key, 1, 1, ival, comment ); + } + } + +/* Number of constants. */ +/* -------------------- */ +/* First check if there are any constants, then write out how many + there are. */ + if ( !this->constant ) nconst = 0; + set = ( nconst != 0 ); + astWriteInt( channel, "Nconst", set, 0, nconst, "Number of constants" ); + +/* Constants. */ +/* ---------- */ +/* Loop to create a keyword and comment for each constant. */ + for ( iconst = 0; iconst < nconst; iconst++ ) { + (void) sprintf( key, "Con%d", iconst + 1 ); + (void) sprintf( comment, "Constant number %d", iconst + 1 ); + +/* Write out each constant value and comment. */ + set = ( this->constant[ iconst ] != AST__BAD ); + if ( set ) { + astWriteDouble( channel, key, 1, 1, this->constant[ iconst ], + comment ); + } else { + astWriteString( channel, key, 0, 1, "", comment ); + } + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPermMap and astCheckPermMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(PermMap,Mapping) +astMAKE_CHECK(PermMap) + +AstPermMap *astPermMap_( int nin, const int inperm[], int nout, + const int outperm[], const double constant[], + const char *options, int *status, ...) { +/* +*++ +* Name: +c astPermMap +f AST_PERMMAP + +* Purpose: +* Create a PermMap. + +* Type: +* Public function. + +* Synopsis: +c #include "permmap.h" +c AstPermMap *astPermMap( int nin, const int inperm[], int nout, +c const int outperm[], double constant[], +c const char *options, ... ) +f RESULT = AST_PERMMAP( NIN, INPERM, NOUT, OUTPERM, CONSTANT, OPTIONS, +f STATUS ) + +* Class Membership: +* PermMap constructor. + +* Description: +* This function creates a new PermMap and optionally initialises its +* attributes. +* +* A PermMap is a Mapping which permutes the order of coordinates, +* and possibly also changes the number of coordinates, between its +* input and output. +* +* In addition to permuting the coordinate order, a PermMap may +* also assign constant values to coordinates. This is useful when +* the number of coordinates is being increased as it allows fixed +* values to be assigned to any new ones. + +* Parameters: +c nin +f NIN = INTEGER (Given) +* The number of input coordinates. +c inperm +f INPERM = INTEGER( NIN ) (Given) +c An optional array with "nin" elements which, for each input +f An array which, for each input +* coordinate, should contain the number of the output +* coordinate whose value is to be used (note that this array +* therefore defines the inverse coordinate transformation). +* Coordinates are numbered starting from 1. +* +* For details of additional special values that may be used in +c this array, see the description of the "constant" parameter. +f this array, see the description of the CONSTANT argument. +c +c If a NULL pointer is supplied instead of an array, each input +c coordinate will obtain its value from the corresponding +c output coordinate (or will be assigned the value AST__BAD if +c there is no corresponding output coordinate). +c nout +f NOUT = INTEGER (Given) +* The number of output coordinates. +c outperm +f OUTPERM = INTEGER( NOUT ) (Given) +c An optional array with "nout" elements which, for each output +f An array which, for each output +* coordinate, should contain the number of the input coordinate +* whose value is to be used (note that this array therefore +* defines the forward coordinate transformation). Coordinates +* are numbered starting from 1. +* +* For details of additional special values that may be used in +c this array, see the description of the "constant" parameter. +f this array, see the description of the CONSTANT argument. +c +c If a NULL pointer is supplied instead of an array, each output +c coordinate will obtain its value from the corresponding +c input coordinate (or will be assigned the value AST__BAD if +c there is no corresponding input coordinate). +c constant +f CONSTANT = DOUBLE PRECISION( * ) (Given) +c An optional array containing values which may be assigned to +f An array containing values which may be assigned to +* input and/or output coordinates instead of deriving them +c from other coordinate values. If either of the "inperm" or +f from other coordinate values. If either of the INPERM or +c "outperm" arrays contains a negative value, it is used to +f OUTPERM arrays contains a negative value, it is used to +c address this "constant" array (such that -1 addresses the +f address this CONSTANT array (such that -1 addresses the +* first element, -2 addresses the second element, etc.) and the +* value obtained is used as the corresponding coordinate value. +* +* Care should be taken to ensure that locations lying outside +* the extent of this array are not accidentally addressed. The +c array is not used if the "inperm" and "outperm" arrays do not +f array is not used if the INPERM and OUTPERM arrays do not +* contain negative values. +c +c If a NULL pointer is supplied instead of an array, the +c behaviour is as if the array were of infinite length and +c filled with the value AST__BAD. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new PermMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new PermMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPermMap() +f AST_PERMMAP = INTEGER +* A pointer to the new PermMap. + +* Notes: +c - If either of the "inperm" or "outperm" arrays contains a +f - If either of the INPERM or OUTPERM arrays contains a +* zero value (or a positive value which does not identify a valid +* output/input coordinate, as appropriate), then the value +* AST__BAD is assigned as the new coordinate value. +* - This function does not attempt to ensure that the forward and +* inverse transformations performed by the PermMap are +* self-consistent in any way. You are therefore free to supply +* coordinate permutation arrays that achieve whatever effect is +* desired. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPermMap *new; /* Pointer to new PermMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the PermMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPermMap( NULL, sizeof( AstPermMap ), !class_init, &class_vtab, + "PermMap", nin, inperm, nout, outperm, constant ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PermMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new PermMap. */ + return new; +} + +AstPermMap *astPermMapId_( int nin, const int inperm[], int nout, + const int outperm[], const double constant[], + const char *options, ... ) { +/* +* Name: +* astPermMapId_ + +* Purpose: +* Create a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "permmap.h" +* AstPermMap *astPermMapId_( int nin, const int inperm[], int nout, +* const int outperm[], const double constant[], +* const char *options, ... ) + +* Class Membership: +* PermMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astPermMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astPermMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* This function also converts the coordinate numbering in the +* permutation arrays from 1-based (used externally) to zero-based +* (used internally). +* +* The variable argument list also prevents this function from +* invoking astPermMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astPermMap_. + +* Returned Value: +* The ID value associated with the new PermMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPermMap *new; /* Pointer to new PermMap */ + int *inperm1; /* Pointer to temporary copy of "inperm" */ + int *outperm1; /* Pointer to temporary copy of "outperm" */ + int coord; /* Loop counter for coordinates */ + +/* Variable argument list */ + va_list args; /* Get a pointer to the thread specific global data structure. */ + + int *status; /* Pointer to inherited status value */ + + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If the "nin" and "nout" values are acceptable, allocate memory to + hold temporary copies of the "inperm" and "outperm" arrays (but + only if these arrays are not NULL). */ + inperm1 = NULL; + outperm1 = NULL; + if ( ( nin >= 0 ) && ( nout >= 0 ) ) { + if ( inperm ) inperm1 = astMalloc( sizeof( int ) * (size_t) nin ); + if ( outperm ) outperm1 = astMalloc( sizeof( int ) * (size_t) nout ); + if ( astOK ) { + +/* If necessary, make a copy of the "inperm" array, converting any + zero values into (zero-based) coordinate numbers that do not exist, + indicating a "bad" coordinate value. */ + if ( inperm ) { + for ( coord = 0; coord < nin; coord++ ) { + if ( inperm[ coord ] < 0 ) { + inperm1[ coord ] = inperm[ coord ]; + } else if ( inperm[ coord ] == 0 ) { + inperm1[ coord ] = nout; + +/* Convert valid coordinate references from 1-based (used externally) + to zero-based (used internally). */ + } else { + inperm1[ coord ] = inperm[ coord ] - 1; + } + } + } + +/* Repeat this process on the "outperm" array. */ + if ( outperm ) { + for ( coord = 0; coord < nout; coord++ ) { + if ( outperm[ coord ] < 0 ) { + outperm1[ coord ] = outperm[ coord ]; + } else if ( outperm[ coord ] == 0 ) { + outperm1[ coord ] = nin; + } else { + outperm1[ coord ] = outperm[ coord ] - 1; + } + } + } + } + } + +/* Initialise the PermMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPermMap( NULL, sizeof( AstPermMap ), !class_init, &class_vtab, + "PermMap", nin, inperm1, nout, outperm1, constant ); + +/* If necessary, free the temporary arrays allocated above. */ + if ( ( nin >= 0 ) && ( nout >= 0 ) ) { + if ( inperm ) inperm1 = astFree( inperm1 ); + if ( outperm ) outperm1 = astFree( outperm1 ); + } + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PermMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new PermMap. */ + return astMakeId( new ); +} + +AstPermMap *astInitPermMap_( void *mem, size_t size, int init, + AstPermMapVtab *vtab, const char *name, + int nin, const int inperm[], + int nout, const int outperm[], + const double constant[], int *status ) { +/* +*+ +* Name: +* astInitPermMap + +* Purpose: +* Initialise a PermMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "permmap.h" +* AstPermMap *astInitPermMap( void *mem, size_t size, int init, +* AstPermMapVtab *vtab, const char *name, +* int nin, const int inperm[], +* int nout, const int outperm[], +* const double constant[] ) + +* Class Membership: +* PermMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new PermMap object. It allocates memory (if necessary) to accommodate +* the PermMap plus any additional data associated with the derived class. +* It then initialises a PermMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a PermMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the PermMap is to be initialised. +* This must be of sufficient size to accommodate the PermMap data +* (sizeof(PermMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the PermMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the PermMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the PermMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new PermMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* nin +* The number of input coordinate values per point. +* inperm +* Pointer to an array of int, with nin elements. For each input +* coordinate, the corresponding element of this array should contain the +* (zero-based) index of the output coordinate whose value is to be used. +* (Note that this array therefore defines the inverse coordinate +* transformation.) If a NULL value is supplied, the corresponding output +* coordinate value is used (or AST__BAD if there is no corresponding +* output coordinate). +* +* For details of additional special values that may be used in this +* array, see the description of the "constant" parameter. +* nout +* The number of output coordinate values per point. +* outperm +* Pointer to an array of int, with nout elements. For each output +* coordinate, the corresponding element of this array should contain the +* (zero-based) index of the input coordinate whose value is to be used. +* (Note that this array therefore defines the forward coordinate +* transformation.) If a NULL value is supplied, the corresponding input +* coordinate value is used (or AST__BAD if there is no corresponding +* input coordinate). +* +* For details of additional special values that may be used in this +* array, see the description of the "constant" parameter. +* constant +* Pointer to an array of double, which contains optional values which +* may be assigned to input and/or output coordinate values (instead of +* deriving them from other coordinate values). If either of the +* "inperm" or "outperm" arrays contains a negative value, it is used to +* address this "constant" array (such that -1 addresses the first +* element, -2 addresses the second element, etc.) and the value obtained +* is used as the corresponding coordinate value. Care should be taken +* to ensure that locations lying outside the extent of this array are +* not accidentally addressed. +* +* If a NULL value is supplied for this parameter, the behaviour is as +* if the constant array were of infinite length and filled with the +* value AST__BAD. + +* Returned Value: +* A pointer to the new PermMap. + +* Notes: +* - This function does not attempt to ensure that the forward and inverse +* transformations performed by the resulting PermMap are consistent in any +* way. The caller is therefore free to define the permutation arrays to +* achieve whatever effect is desired. +* - If either of the "inperm" or "outperm" arrays contains a positive +* value which does not identify a valid output/input coordinate (as +* appropriate), then the value AST__BAD is assigned as the new coordinate +* value. +* - This function makes a copy of the contents of the arrays supplied. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPermMap *new; /* Pointer to new PermMap */ + int i; /* Loop counter for coordinates */ + int neg; /* Most negative permutation index */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPermMapVtab( vtab, name ); + +/* Initialise a Mapping structure (the parent class) as the first component + within the PermMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstPermMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nout, 1, 1 ); + + if ( astOK ) { + +/* Initialise the PermMap data. */ +/* ---------------------------- */ + new->permsplit = -INT_MAX; + +/* Initialise the array pointers. */ + new->inperm = NULL; + new->outperm = NULL; + new->constant = NULL; + +/* If an "inperm" and/or "outperm" array has been supplied, allocate memory + and store a copy. */ + if ( inperm ) new->inperm = astStore( NULL, inperm, sizeof( int ) * + (size_t) nin ); + if ( outperm ) new->outperm = astStore( NULL, outperm, sizeof( int ) * + (size_t) nout ); + +/* If a "constant" array has been supplied, we must also store a copy of it, + but must first determine how many of its elements we need. */ + if ( constant ) { + +/* Loop through the "inperm" array (if supplied) to find the most negative + value it contains. This corresponds with the maximum index into the + constant array. */ + neg = 0; + if ( inperm ) { + for ( i = 0; i < nin; i++ ) { + if ( inperm[ i ] < neg ) neg = inperm[ i ]; + } + } + +/* Also perform this process on the "outperm" array (if supplied). */ + if ( outperm ) { + for ( i = 0; i < nout; i++ ) { + if ( outperm[ i ] < neg ) neg = outperm[ i ]; + } + } + +/* If a negative value was found, use its size to determine how many elements + of the "constant" array to store in allocated memory. */ + if ( neg < 0 ) { + new->constant = astStore( NULL, constant, sizeof( double ) * + (size_t) (-neg) ); + } + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) { + new = astDelete( new ); + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstPermMap *astLoadPermMap_( void *mem, size_t size, + AstPermMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPermMap + +* Purpose: +* Load a PermMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "permmap.h" +* AstPermMap *astLoadPermMap( void *mem, size_t size, +* AstPermMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* PermMap loader. + +* Description: +* This function is provided to load a new PermMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* PermMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a PermMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the PermMap is to be +* loaded. This must be of sufficient size to accommodate the +* PermMap data (sizeof(PermMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the PermMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the PermMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPermMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new PermMap. If this is NULL, a pointer +* to the (static) virtual function table for the PermMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "PermMap" is used instead. + +* Returned Value: +* A pointer to the new PermMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstPermMap *new; /* Pointer to the new PermMap */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword strings */ + int coord; /* Loop counter for coordinates */ + int iconst; /* Loop counter for constants */ + int in_cpy; /* Input coordinates obtained by copying? */ + int invert; /* Invert attribute value */ + int ival; /* Integer value */ + int nconst; /* Number of constants */ + int nin; /* Number of input coordinates */ + int nout; /* Number of output coordinates */ + int out_cpy; /* Output coordinates obtained by copying? */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this PermMap. In this case the + PermMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPermMap ); + vtab = &class_vtab; + name = "PermMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPermMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built PermMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "PermMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Initialise the PermMap's pointers. */ + new->inperm = NULL; + new->outperm = NULL; + new->constant = NULL; + +/* Determine if the PermMap is inverted and obtain the "true" number + of input and output coordinates by un-doing the effects of any + inversion. */ + invert = astGetInvert( new ); + nin = !invert ? astGetNin( new ) : astGetNout( new ); + nout = !invert ? astGetNout( new ) : astGetNin( new ); + +/* PermSplit. */ +/* ---------- */ + new->permsplit = astReadInt( channel, "pmsplt", -INT_MAX ); + if ( TestPermSplit( new, status ) ) SetPermSplit( new, new->permsplit, status ); + +/* InCpy and OutCpy. */ +/* ----------------- */ +/* Obtain boolean values via the "InCpy" and "OutCpy" keywords which + indicate if input/output coordinates should be obtained simply by + copying the corresponding output/input coordinates. */ + in_cpy = astReadInt( channel, "incpy", 0 ); + out_cpy = astReadInt( channel, "outcpy", 0 ); + +/* If coordinates are specified individually (not simply copied), then + allocate memory for the coordinate permutation arrays. */ + if ( !in_cpy ) new->inperm = astMalloc( sizeof( int ) * (size_t) nin ); + if ( !out_cpy ) new->outperm = astMalloc( sizeof( int ) * + (size_t) nout ); + +/* If an error occurred, ensure that all allocated memory is freed. */ + if ( !astOK ) { + if ( !in_cpy ) new->inperm = astFree( new->inperm ); + if ( !out_cpy ) new->outperm = astFree( new->outperm ); + +/* Otherwise read data into these arrays... */ + } else { + +/* "inperm" array contents. */ +/* ------------------------ */ +/* If required, create a keyword for each element of the "inperm" + array and read the element's value. */ + if ( !in_cpy ) { + for ( coord = 0; coord < nin; coord++ ) { + (void) sprintf( key, "in%d", coord + 1 ); + ival = astReadInt( channel, key, 0 ); + +/* If the value is zero (indicating a "bad" coordinate), convert it to + a (zero-based) coordinate number that doesn't exist. */ + if ( ival == 0 ) { + ival = nout; + +/* If the coordinate reference is valid, convert to zero-based + coordinate numbering for internal use. */ + } else if ( ival > 0 ) { + ival--; + } + +/* Store the value. */ + new->inperm[ coord ] = ival; + } + } + +/* "outperm" array contents. */ +/* ------------------------- */ +/* If required, create a keyword for each element of the "outperm" + array and read the element's value. */ + if ( !out_cpy ) { + for ( coord = 0; coord < nout; coord++ ) { + (void) sprintf( key, "out%d", coord + 1 ); + ival = astReadInt( channel, key, 0 ); + +/* If the value is zero (indicating a "bad" coordinate), convert it to + a (zero-based) coordinate number that doesn't exist. */ + if ( ival == 0 ) { + ival = nin; + +/* If the coordinate reference is valid, convert to zero-based + coordinate numbering for internal use. */ + } else if ( ival > 0 ) { + ival--; + } + +/* Store the value. */ + new->outperm[ coord ] = ival; + } + } + +/* Number of constants. */ +/* -------------------- */ +/* Determine the number of constants and allocate memory to hold + them. */ + nconst = astReadInt( channel, "nconst", 0 ); + if ( nconst < 0 ) nconst = 0; + new->constant = astMalloc( sizeof( double ) * (size_t) nconst ); + if ( astOK ) { + +/* Constants. */ +/* ---------- */ +/* Create a keyword for each constant and read its value. */ + for ( iconst = 0; iconst < nconst; iconst++ ) { + (void) sprintf( key, "con%d", iconst + 1 ); + new->constant[ iconst ] = + astReadDouble( channel, key, AST__BAD ); + } + } + } + +/* If an error occurred, clean up by deleting the new PermMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new PermMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +double *astGetConstants_( AstPermMap *this, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,PermMap,GetConstants))( this, status ); +} + +int *astGetInPerm_( AstPermMap *this, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,PermMap,GetInPerm))( this, status ); +} + +int *astGetOutPerm_( AstPermMap *this, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,PermMap,GetOutPerm))( this, status ); +} + + + + + + diff --git a/permmap.h b/permmap.h new file mode 100644 index 0000000..0115783 --- /dev/null +++ b/permmap.h @@ -0,0 +1,322 @@ +#if !defined( PERMMAP_INCLUDED ) /* Include this file only once */ +#define PERMMAP_INCLUDED +/* +*+ +* Name: +* permmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the PermMap class. + +* Invocation: +* #include "permmap.h" + +* Description: +* This include file defines the interface to the PermMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The PermMap class implements Mappings that perform permutation +* of the order of coordinate values, possibly also accompanied by +* changes in the number of coordinates (between input and output). +* +* In addition to permuting the coordinate order, coordinates may +* also be assigned constant values which are unrelated to other +* coordinate values. This facility is useful when the number of +* coordinates is being increased, as it allows fixed values to be +* assigned to the new coordinates. + +* Inheritance: +* The PermMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astGetConstants +* Obtain a copy of the constants array +* astGetInPerm +* Obtain a copy of the input permutation array +* astGetOutPerm +* Obtain a copy of the output permutation array + +* Other Class Functions: +* Public: +* astIsAPermMap +* Test class membership. +* astPermMap +* Create a PermMap. +* +* Protected: +* astCheckPermMap +* Validate class membership. +* astInitPermMap +* Initialise a PermMap. +* astInitPermMapVtab +* Initialise the virtual function table for the PermMap class. +* astLoadPermMap +* Load a PermMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstPermMap +* PermMap object type. +* +* Protected: +* AstPermMapVtab +* PermMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 29-FEB-1996 (RFWS): +* Original version. +* 26-SEP-1996 (RFWS): +* Added external interface and I/O facilities. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitPermMapVtab +* method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* PermMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstPermMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int *inperm; /* Pointer to input permutation array */ + int *outperm; /* Pointer to output permutation array */ + double *constant; /* Pointer to array of constant values */ + int permsplit; /* Method to use within MapSplit */ +} AstPermMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPermMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + double *(* GetConstants)( AstPermMap *, int * ); + int *(* GetInPerm)( AstPermMap *, int * ); + int *(* GetOutPerm)( AstPermMap *, int * ); + void (* SetPermSplit)( AstPermMap *, int, int * ); + void (* ClearPermSplit)( AstPermMap *, int * ); + int (* TestPermSplit)( AstPermMap *, int * ); + int (* GetPermSplit)( AstPermMap *, int * ); + +} AstPermMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstPermMapGlobals { + AstPermMapVtab Class_Vtab; + int Class_Init; +} AstPermMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitPermMapGlobals_( AstPermMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(PermMap) /* Check class membership */ +astPROTO_ISA(PermMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPermMap *astPermMap_( int, const int [], int, const int [], + const double [], const char *, int *, ...); +#else +AstPermMap *astPermMapId_( int, const int [], int, const int [], + const double [], const char *, ... )__attribute__((format(printf,6,7))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPermMap *astInitPermMap_( void *, size_t, int, AstPermMapVtab *, + const char *, int, const int [], int, + const int [], const double [], int * ); + +/* Vtab initialiser. */ +void astInitPermMapVtab_( AstPermMapVtab *, const char *, int * ); + +/* Loader. */ +AstPermMap *astLoadPermMap_( void *, size_t, AstPermMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +double *astGetConstants_( AstPermMap *, int * ); +int *astGetInPerm_( AstPermMap *, int * ); +int *astGetOutPerm_( AstPermMap *, int * ); +void astSetPermSplit_( AstPermMap *, int, int * ); +void astClearPermSplit_( AstPermMap *, int * ); +int astTestPermSplit_( AstPermMap *, int * ); +int astGetPermSplit_( AstPermMap *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPermMap(this) astINVOKE_CHECK(PermMap,this,0) +#define astVerifyPermMap(this) astINVOKE_CHECK(PermMap,this,1) + +/* Test class membership. */ +#define astIsAPermMap(this) astINVOKE_ISA(PermMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astPermMap astINVOKE(F,astPermMap_) +#else +#define astPermMap astINVOKE(F,astPermMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPermMap(mem,size,init,vtab,name,nin,inperm,nout,outperm,constant) \ +astINVOKE(O,astInitPermMap_(mem,size,init,vtab,name,nin,inperm,nout,outperm,constant,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPermMapVtab(vtab,name) astINVOKE(V,astInitPermMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadPermMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPermMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckPermMap to validate PermMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astGetConstants(this) astINVOKE(V,astGetConstants_(astCheckPermMap(this),STATUS_PTR)) +#define astGetInPerm(this) astINVOKE(V,astGetInPerm_(astCheckPermMap(this),STATUS_PTR)) +#define astGetOutPerm(this) astINVOKE(V,astGetOutPerm_(astCheckPermMap(this),STATUS_PTR)) +#define astSetPermSplit(this,permsplit) astINVOKE(V,astSetPermSplit_(astCheckPermMap(this),permsplit,STATUS_PTR)) +#define astClearPermSplit(this) astINVOKE(V,astClearPermSplit_(astCheckPermMap(this),STATUS_PTR)) +#define astTestPermSplit(this) astINVOKE(V,astTestPermSplit_(astCheckPermMap(this),STATUS_PTR)) +#define astGetPermSplit(this) astINVOKE(V,astGetPermSplit_(astCheckPermMap(this),STATUS_PTR)) +#endif + +#endif + + + + + diff --git a/pg3d.h b/pg3d.h new file mode 100644 index 0000000..e4dab63 --- /dev/null +++ b/pg3d.h @@ -0,0 +1,68 @@ +#if !defined( PG3D_INCLUDED ) /* Include this file only once */ +#define PG3D_INCLUDED +/* +*+ +* Name: +* pg3d.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the pg3d module + +* Invocation: +* #include "pg3d.h" + +* Description: +* This include file defines the interface to the pg3d module +* (implemented in file grf3d_pgplot.c) and provides the type +* definitions, function prototypes and macros, etc. needed to +* use this module. +* +* The functions in the pg3d interface provide control of the view +* of the 3D world coordinate system visible on the 2D PGPLOT +* viewport. They are provided for users of the PGPLOT implementation +* of the grf3D interface distributed with AST. No calls to these +* functions are made from within AST itself. + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (JACH - UCLan) + +* History: +* 20-JUN-2007 (DSB): +* Original version. +*- +*/ + +int PG3DRotateEye( int, float ); +int PG3DSetCamera( float[3], float[3], float[3], float ); +int PG3DSetEye( float[3] ); +int PG3DSetTarget( float[3] ); +int PG3DSetUp( float[3] ); +int PG3DSetScreen( float ); +int PG3DForward( float ); +int PG3DAutoCamera( float[3], float[3] ); +int PG3DFindNearest( int, float *, float *, float *, int * ); + +#endif diff --git a/plot.c b/plot.c new file mode 100644 index 0000000..368cb73 --- /dev/null +++ b/plot.c @@ -0,0 +1,32074 @@ +/* +*class++ +* Name: +* Plot + +* Purpose: +* Provide facilities for 2D graphical output. + +* Constructor Function: +c astPlot +f AST_PLOT + +* Description: +* This class provides facilities for producing 2D graphical output. +* A Plot is a specialised form of FrameSet, in which the base +* Frame describes a "graphical" coordinate system and is +* associated with a rectangular plotting area in the underlying +* graphics system. This plotting area is where graphical output +* appears. It is defined when the Plot is created. +* +* The current Frame of a Plot describes a "physical" coordinate +* system, which is the coordinate system in which plotting +* operations are specified. The results of each plotting operation +* are automatically transformed into graphical coordinates so as +* to appear in the plotting area (subject to any clipping which +* may be in effect). +* +* Because the Mapping between physical and graphical coordinates +* may often be non-linear, or even discontinuous, most plotting +* does not result in simple straight lines. The basic plotting +* element is therefore not a straight line, but a geodesic curve +c (see astCurve, astGenCurve and astPolyCurve). A Plot also provides facilities for +c drawing markers or symbols (astMark), text (astText) and grid lines +c (astGridLine). It is also possible to draw curvilinear axes with +c optional coordinate grids (astGrid). +f (see AST_CURVE, AST_GENCURVE and AST_POLYCURVE). A Plot also provides facilities +f for drawing markers or symbols (AST_MARK), text (AST_TEXT) and grid +f lines (AST_GRIDLINE). It is also possible to draw curvilinear axes +f with optional coordinate grids (AST_GRID). +* A range of Plot attributes is available to allow precise control +* over the appearance of graphical output produced by these +c functions. +f routines. +* +* You may select different physical coordinate systems in which to +* plot (including the native graphical coordinate system itself) +* by selecting different Frames as the current Frame of a Plot, +* using its Current attribute. You may also set up clipping (see +c astClip) to limit the extent of any plotting you perform, and +f AST_CLIP) to limit the extent of any plotting you perform, and +* this may be done in any of the coordinate systems associated +* with the Plot, not necessarily the one you are plotting in. +* +* Like any FrameSet, a Plot may also be used as a Frame. In this +* case, it behaves like its current Frame, which describes the +* physical coordinate system. +* +* When used as a Mapping, a Plot describes the inter-relation +* between graphical coordinates (its base Frame) and physical +* coordinates (its current Frame). It differs from a normal +* FrameSet, however, in that an attempt to transform points which +* lie in clipped areas of the Plot will result in bad coordinate +* values (AST__BAD). + +* Inheritance: +* The Plot class inherits from the FrameSet class. + +* Attributes: +* In addition to those attributes common to all FrameSets, every +* Plot also has the following attributes: +* +* - Abbrev: Abbreviate leading fields? +* - Border: Draw a border around valid regions of a Plot? +* - Clip: Clip lines and/or markers at the Plot boundary? +* - ClipOp: Combine Plot clipping limits using a boolean OR? +* - Colour(element): Colour index for a Plot element +* - DrawAxes(axis): Draw axes for a Plot? +* - DrawTitle: Draw a title for a Plot? +* - Escape: Allow changes of character attributes within strings? +* - Edge(axis): Which edges to label in a Plot +* - Font(element): Character font for a Plot element +* - Gap(axis): Interval between linearly spaced major axis values +* - Grf: Select the graphics interface to use. +* - Grid: Draw grid lines for a Plot? +* - Invisible: Draw graphics in invisible ink? +* - LabelAt(axis): Where to place numerical labels for a Plot +* - LabelUnits(axis): Use axis unit descriptions in a Plot? +* - LabelUp(axis): Draw numerical Plot labels upright? +* - Labelling: Label and tick placement option for a Plot +* - LogGap(axis): Interval between logarithmically spaced major axis values +* - LogPlot(axis): Map the plot onto the screen logarithmically? +* - LogTicks(axis): Space the major tick marks logarithmically? +* - MajTickLen(axis): Length of major tick marks for a Plot +* - MinTickLen(axis): Length of minor tick marks for a Plot +* - MinTick(axis): Density of minor tick marks for a Plot +* - NumLab(axis): Draw numerical axis labels for a Plot? +* - NumLabGap(axis): Spacing of numerical axis labels for a Plot +* - Size(element): Character size for a Plot element +* - Style(element): Line style for a Plot element +* - TextLab(axis): Draw descriptive axis labels for a Plot? +* - TextLabGap(axis): Spacing of descriptive axis labels for a Plot +* - TickAll: Draw tick marks on all edges of a Plot? +* - TitleGap: Vertical spacing for a Plot title +* - Tol: Plotting tolerance +* - Width(element): Line width for a Plot element + +* Functions: +c In addition to those functions applicable to all FrameSets, the +c following functions may also be applied to all Plots: +f In addition to those routines applicable to all FrameSets, the +f following routines may also be applied to all Plots: +* +c - astBBuf: Begin a new graphical buffering context +c - astBorder: Draw a border around valid regions of a Plot +c - astBoundingBox: Returns a bounding box for previously drawn graphics +c - astClip: Set up or remove clipping for a Plot +c - astCurve: Draw a geodesic curve +c - astEBuf: End the current graphical buffering context +c - astGenCurve: Draw a generalized curve +c - astGetGrfContext: Get the graphics context for a Plot +c - astGrfPop: Retrieve previously saved graphics functions +c - astGrfPush: Save the current graphics functions +c - astGrfSet: Register a graphics routine for use by a Plot +c - astGrid: Draw a set of labelled coordinate axes +c - astGridLine: Draw a grid line (or axis) for a Plot +c - astMark: Draw a set of markers for a Plot +c - astPolyCurve: Draw a series of connected geodesic curves +c - astRegionOutline: Draw the outline of an AST Region +c - astText: Draw a text string for a Plot +f - AST_BBUF: Begin a new graphical buffering context +f - AST_BORDER: Draw a border around valid regions of a Plot +f - AST_BOUNDINGBOX: Returns a bounding box for previously drawn graphics +f - AST_CLIP: Set up or remove clipping for a Plot +f - AST_CURVE: Draw a geodesic curve +f - AST_EBUF: End the current graphical buffering context +f - AST_GENCURVE: Draw a generalized curve +f - AST_GETGRFCONTEXT: Get the graphics context for a Plot +f - AST_GRFPOP: Retrieve previously saved graphics functions +f - AST_GRFPUSH: Save the current graphics functions +f - AST_GRFSET: Register a graphics routine for use by the Plot class +f - AST_GRID: Draw a set of labelled coordinate axes +f - AST_GRIDLINE: Draw a grid line (or axis) for a Plot +f - AST_MARK: Draw a set of markers for a Plot +f - AST_POLYCURVE: Draw a series of connected geodesic curves +f - AST_REGIONOUTLINE: Draw the outline of an AST Region +f - AST_TEXT: Draw a text string for a Plot + +* Graphical Elements: +* The colour index, character font, character size, line style and +* line width used for plotting can be set independently for +* various elements of the graphical output produced by a Plot. +* The different graphical elements are identified by appending the +* strings listed below as subscripts to the Plot attributes +* Colour(element), Font(element), Size(element), Style(element) +* and Width(element). These strings are case-insensitive and +* unambiguous abbreviations may be used. Elements of the graphical +* output which relate to individual axes can be referred to either +* independently (e.g. "(Grid1)" and "(Grid2)" ) or together (e.g. +* "(Grid)"): +* +c - Axes: Axis lines drawn through tick marks using astGrid +f - Axes: Axis lines drawn through tick marks using AST_GRID +c - Axis1: Axis line drawn through tick marks on axis 1 using astGrid +f - Axis1: Axis line drawn through tick marks on axis 1 using AST_GRID +c - Axis2: Axis line drawn through tick marks on axis 2 using astGrid +f - Axis2: Axis line drawn through tick marks on axis 2 using AST_GRID +c - Border: The Plot border drawn using astBorder, astGrid or astRegionOutline +f - Border: The Plot border drawn using AST_BORDER, AST_GRID or AST_REGIONOUTLINE +c - Curves: Geodesic curves drawn using astCurve, astGenCurve or astPolyCurve +f - Curves: Geodesic curves drawn using AST_CURVE, AST_GENCURVE or AST_POLYCURVE +c - Grid: Grid lines drawn using astGridLine or astGrid +f - Grid: Grid lines drawn using AST_GRIDLINE or AST_GRID +c - Grid1: Grid lines which cross axis 1, drawn using astGridLine or astGrid +f - Grid1: Grid lines which cross axis 1, drawn using AST_GRIDLINE or AST_GRID +c - Grid2: Grid lines which cross axis 2, drawn using astGridLine or astGrid +f - Grid2: Grid lines which cross axis 2, drawn using AST_GRIDLINE or AST_GRID +c - Markers: Graphical markers (symbols) drawn using astMark +f - Markers: Graphical markers (symbols) drawn using AST_MARK +c - NumLab: Numerical axis labels drawn using astGrid +f - NumLab: Numerical axis labels drawn using AST_GRID +c - NumLab1: Numerical labels for axis 1 drawn using astGrid +f - NumLab1: Numerical labels for axis 1 drawn using AST_GRID +c - NumLab2: Numerical labels for axis 2 drawn using astGrid +f - NumLab2: Numerical labels for axis 2 drawn using AST_GRID +c - Strings: Text strings drawn using astText +f - Strings: Text strings drawn using AST_TEXT +c - TextLab: Descriptive axis labels drawn using astGrid +f - TextLab: Descriptive axis labels drawn using AST_GRID +c - TextLab1: Descriptive label for axis 1 drawn using astGrid +f - TextLab1: Descriptive label for axis 1 drawn using AST_GRID +c - TextLab2: Descriptive label for axis 2 drawn using astGrid +f - TextLab2: Descriptive label for axis 2 drawn using AST_GRID +c - Ticks: Tick marks (both major and minor) drawn using astGrid +f - Ticks: Tick marks (both major and minor) drawn using AST_GRID +c - Ticks1: Tick marks (both major and minor) for axis 1 drawn using astGrid +f - Ticks1: Tick marks (both major and minor) for axis 1 drawn using AST_GRID +c - Ticks2: Tick marks (both major and minor) for axis 2 drawn using astGrid +f - Ticks2: Tick marks (both major and minor) for axis 2 drawn using AST_GRID +c - Title: The Plot title drawn using astGrid +f - Title: The Plot title drawn using AST_GRID + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 18-SEP-1996 (DSB): +* Original version. +* 25-FEB-1997 (RFWS): +* Tidied all public prologues. +* 18-AUG-1997 (DSB): +* Changes made to ensure that the first label on each axis is +* never abbreviated, and to avoid segmentation violation when NumLab +* is set to zero. +* 1-SEP-1997 (DSB): +* astGetGap changed so that it returns the default value which will +* be used (instead of AST__BAD) if no value has been set for Gap. +* The Border attribute modified so that it is off (zero) by default. +* 19-SEP-1997 (DSB): +* o Check that something has been plotted before using the bounding +* box to determine title and label positions. +* o Fixed bug which caused a tick mark at the pole to be draw at +* a random angle. +* o The size of the increment used to determine the tangent to a grid +* line at the position to a label has been reduced to make sure the +* labls are drawn parallel to grid line. +* o Correct the logic for catering with reversed axes when determining +* the displacement of a label's reference point from the associated +* axis. +* o Corrected logic which determined if two numerical labels overlap. +* o Corrected logic for determining when to abbreviate numerical +* labels. +* o Use of strtok replaced by local function FindWord. +* o Correct logic which determines which side of the axis to draw +* tick marks when using interior labelling. +* o If the base Frame of the FrameSet supplied to astPlot has more +* than 2 axes, then use a sub-frame formed from the first two axes, +* instead of simply reporting an error. +* o If the current Frame of the Plot supplied to astGrid or +* astBorder has more than 2 axes, then use a sub-frame formed from +* the first two axes, instead of simply reporting an error. +* o Default for Border is now to draw the border if exterior +* Labelling is used, but not to draw it if interior labelling is +* used. +* o Public astGet function now returns actual used values for all +* attributes. Protected astGetXyz functions still return the requested +* value (which may differ from the used value), or the "unset" value +* if no value has been set for the attribute. +* o The defaults for Edge now depend on Labelling. If exterior +* labelling was requested but cannot be produced using defaults of +* Edge(1)=Bottom and Edge(2)=Left, then these default Edge values +* are swapped. If exterior labelling is still not possible, the +* original default Edge values are re-instated. +* o Unset attributes which use dynamic defaults are now flagged as +* "unhelpful" in the dump function. +* o Added attribute Escape which allows text strings to include +* escape sequences (see function GrText). This attribute and +* associated functionality is currently not available for use, search +* for all occurences of ENABLE-ESCAPE for instructions on how to +* enable the facilities. +* o Strings now used instead of integers to represent "choice" +* attributes externally (eg Edge and Labelling). +* 24-NOV-1997 (DSB): +* o Fixed bug in function Grid which caused units to be included in +* SkyFrame axis labels by default. +* o Replaced calls to DrawText by calls to astGText, and replaced +* references to "U" and "D" justifications by "T" and "B". This +* stops labels drifting to the bottom left when GAIA zooms. +* 23-MAR-1998 (DSB): +* Added extra checks on global status into routine Grid to avoid +* segmentation violations occuring due to null pointers being used. +* 10-JUN-1998 (DSB): +* Modify DrawTicks so that ticks are drawn closer to singularities +* than previously. Also normalise this constraint to the screen size +* rather than the length of a major tick mark. +* 28-OCT-1998 (DSB): +* o Added method astPolyCurve. +* o Extract the Current Frame from the Plot prior to using Frame +* methods such as astOffset, astNorm, etc. +* o PlotLabel modified to ensure labels are abbreviated even if +* they are next to the "root" label (i.e. the label with most +* trailing zeros). +* o Modified description of Width attribute. Width no longer gives +* the absolute line width in inches. Instead it is a scale factor, +* where 1.0 corresponds to a "typical thin line" on the device. +* o Modified LabelUnits attribute so that the default value is zero +* for SkyAxes and non-zero for other Axes. +* 10-DEC-1998 (DSB): +* Modified all calls to the "pow" maths function to avoid using +* literal constants as arguments. This seems to cause segmentation +* violations on some systems. +* 16-JUL-1999 (DSB): +* Fixed memory leaks in EdgeCrossings and EdgeLabels. +* 16-SEP-1999 (DSB): +* Avoid writing out clipping limits if they are undefined. +* 12-OCT-1999 (DSB): +* o Modified use of the NumLab attribute so that setting it to zero +* does not prevent exterior labels from being produced. +* o Allow length of tick marks to be specified separately for +* both axes. +* 13-OCT-2000 (DSB): +* o Purge zero length sections from CurveData structures. +* o Increase tolerance for edge labels from 0.0005 to 0.005. +* 9-JAN-2001 (DSB): +* o Change argument "in" for astMark and astPolyCurve from type +* "const double (*)[]" to "const double *". +* o Check success of astReadString before using the returned +* pointer. +* o Change method for choosing default LabelAt values to ignore +* values which produce no visible labels. +* 10-JAN-2001 (DSB): +* o Modified FindMajTick to choose the size of fillable holes in +* the axis range on the basis of the number of ticks on the axis. +* This avoids holes being visible in the displayed tick marks when +* using very small gaps. +* 22-MAY-2001 (DSB): +* Added a check when using interior labelling, to ensure that the +* most appropriate edges are used for text labels. +* 13-JUN-2001 (DSB): +* Added public method astGenCurve, astGrfSet, astGrfPop, astGrfPush. +* Made DrawAxes attribute axis specific. +* 4-JUL-2001 (DSB): +* The Crv function used to have a restriction that if *any* +* subsection was very short, then *none* of the subsections were +* subdivided. This meant that long subsections which needed +* subdividing were not subdivided if there was also a very short +* subsection. To get round this problem the restriction was changed +* to "if *all* subsections are very short then none are divided. +* This was implemented by changing dl2_min to dl2_max, and adding +* a check for very short segments (which are then not sub-divided). +* 16-AUG-2001 (DSB): +* Remove the check for very short segments introduced above, as it +* caused south pole tan projection to include some spurious lines. +* 20-SEP-2001 (DSB): +* - Initialize baseframe to NULL in astInitPlot (prevents segvios). +* - Modified astInitPlot to allow the "frame" argument to the astPlot +* constructor to be a Plot. +* 10-JAN-2002 (DSB): +* - Added axis-specific graphical elements "axis1", "axis2", etc. +* - FullForm returns a match without ambiguity if the test string +* matches an option exactly, including length. +* 31-JAN-2002 (DSB): +* - Added RejectOOB to reject tick marks which are not in their primary +* domain. +* 14-FEB-2002 (DSB): +* - Relaxed the conditions for equality within the EQUALS macro. +* Guard aginst no ticks being found. +* 18-FEB-2002 (DSB): +* - Make a permanent copy of any old axis format string in TickMarks. +* Previously a mere pointer into the astGet string buffer was stored, +* which could be over-written after many calls to astGet. +* - If a user specifies an axis format, use it whether or not it +* results in any identical adjacent labels. +* 4-MAR-2002 (DSB): +* - Made fairly extesive changes to the creation and use of tick +* mark values in order to circumvent problems with CAR projections, +* and "1 to many" mappings (such as 2D cartesian->polar). The +* policy now is that axis normalization is only performed when +* necessary (i.e. to create labels for display, etc). Tick mark +* values are stored and handled as non-normalized values as much as +* possible. +* 13-JUN-2002 (DSB): +* Modified Norm1 to prevent major tick value from being removed if +* the supplied reference value is out of bounds, resulting in the +* Mapping producing bad values +* 14-JUN-2002 (DSB): +* Re-wrote PlotLabels to improve abbreviation of labels and the +* choice of which labels not to print. +* 14-AUG-2002 (DSB): +* - Added method astBoundingBox. +* - Added attribute Invisible. +* - Correct handling of "axis specific" plot elements cuch as +* (Axis1), (Axis2), etc. +* 12-SEP-2002 (DSB): +* - Modified Map1 to remove slow normalization method (it is now +* faster but the changes result in some longer-than-needed grids +* lines when (e.g.) plotting pixel coordins in Polar coords). +* - Modified Axlot so that SkyFrames positions which are out of +* their normal ranges are not rejected by Map1. +* 10-OCT-2002 (DSB): +* grfAttrs:Modified to test element attributes explicitly using the +* relevant TestUse functions, instead of relying on the +* "GetUse" function returning the NO constant if not set. +* - Modified Axplot so that SkyFrames positions which are out of +* their normal ranges are not rejected by Map1. +* - Only use tick marks which are within the axis range given by the +* Bottom and Top Axis attributes. +* - Norm1: If the normalized current frame coords are bad, do not +* reinstate the original unnormalized values. For instance, current +* Frame values which are outside the valid domain of the projection +* should result in bad values when normalized, not the original +* good values. The original comment stated "If the normalization +* produced bad coords (e.g. as may happen if the supplied refernce +* value corresponds to a point on the line through the tick mark +* which is outside the valid region of the mapping) leave the original +* tick mark values unchanged". +* - GetTicks: Limit maxticks to be no less than 8. +* 8-JAN-2003 (DSB): +* - Changed private InitVtab method to protected astInitPlotVtab +* method. +* - Use private IsASkyFrame method in place of astIsASkyFrame. +* - Modify PlotLabels to excluding exponents when counting trailing +* zeros, and also to pad trailing fields with trailing zeros up to +* the max number of decimal places when estimating label priorities. +* - Modified Overlap to ensure that axis labels are speced by at +* least two spaces. +* 22-JAN-2003 (DSB): +* - Modified PlotLabels so that labels are rejected in a regular +* pattern rather than semi-random. +* - Modified the way PlotLabels abbreviates leading fields. +* - Introdued the skipbad parameter for the Crv function, in order +* to provide some degree of protection against the Crv algorithm +* skipping over small sections of valid coordinates (such as when +* a curve crosses the plot very close to a corner of the plot). +* 25-MAR-2003 (DSB): +* - Modified FindMajTicks to avoid losing tick marks when dealing +* with high precision data. +* 8-AUG-2003 (DSB): +* - Modified PlotLabels to ensure that the root label for the +* second axis is not omitted due to it overlapping a label from +* the first axis (a different root label is now chosen if this would +* be the case). +* - Modify FindMajTicks to avoid tick marks which should be at +* exactly zero being placed at some very small non-zero axis value. +* 22-OCT-2003 (DSB): +* - DrawTicks modified to correctly reset graphical attributes and +* pass on to the next axis if an axis has zero length major and minor +* tick marks. +* 9-JAN-2004 (DSB): +* DrawGrid: Report error if no grid curves can be drawn. +* AxPlot: Initialise returned CDATA structure before checking argument +* validity. +* GetTicks: Calculate the reference value on the other axis using +* function "Typical" rather than simply using the man of the supplied +* values (the supplied values may be clustered around 0 and 2*PI if the +* field is centred on the origin, resulting in the mean being at about +* 1.PI and therefore inappropriate). +* 13-JAN-2004 (DSB): +* - Added LogPlot attribute, and the facility for mapping the base +* coordinate system logarithmically onto the plotting area instead of +* linearly. +* - Added LogTicks attribute, and the facility for spacing the +* major tick marks logarithmically instead of linearly. +* - Added LogGap attribute, and the facility for storing separate +* gap sizes for linear and log tick spacing. +* 15-JAN-2004 (DSB): +* - Added LogLabel attribute. +* - Re-instated the inclusion of escape sequences in strings (see +* function GrText). +* 12-FEB-2004 (DSB): +* - RightVector: Corrected usage of chh and chv. +* - GQch and GScales: Check that values returned by grf module are +* usable. +* - DrawAxis: Extend axis section by one section (if possible) at +* each end (overcomes problems where the axis does not reach a pole). +* - DrawAxis: Check axis does not extend beyond a pole. +* - Labels: Correct logic of loop which plots interior labels +* (previously it missed out labels if there were only 3) +* - Allow for some rounding error in FindMajTicks when comparing an +* axis value with a loweror upper axis limit. +* 19-FEB-2004 (DSB): +* - Reduced the dynamic range restriction for log ticks from 2 decades +* to 1. +* - Temporarily clear any error status before re-instating the +* original Format in TickMarks. +* - Add LogTicks to the GetAttrib function so that the value of the +* LogTicks attribute can be got by the public. +* - Modify Crv to include a check that he vector scale has not +* changed much between adjacent segments. +* - Modify Crv so that a segment is only subdivided if at least +* half of the subsegments are longer than the shortest significant +* length. Also put a restriction on subdivision so that +* subdivision only occurs if the bounding box of the segment being +* sub-divided is smaller than the bounding box of its parent +* segment. +* 27-FEB-2004 (DSB): +* - Reduce the default Tol value from 0.001 to 0.01 in order to +* speed up curve drawing.. +* - Use 0.1*Tol in Boundary because the boundary tracing algorithm +* seems to produce much worse visible errors than it should do for a +* given Tol. +* 2-MAR-2004 (DSB): +* - Corrected handling of bounding boxes in Crv so that +* subdivision is allowed if the bounding box shrinks on only 1 axis +* (previously required shrinkage on both axes but this fails if +* all the points are on a horizontal or vertical line). +* - Modified FindMajTicks to use a better algorithm for finding an +* appropriate nfill value (previously logplot=1 axes could have +* unfilled holes at the high end). +* - Modified GetTicks so that FindMajTicks is not called +* repeatedly with the same gap size. +* - Modify AxPlot/Map1 so that the axis curve is sampled logarithmically +* if the corresponding axis is mapped logarithmically. +* 10-MAR-2004 (DSB): +* - Modified Typical to give less weight to vaalues close to the +* edges of the range covered by the plotting area. +* - Increased minimum angle between curve and edge required to +* create an edge label from 3 degs to 5 degs. +* - Modified PlotLabels to ignore duplicate adjacent labels which +* determining overlap of labels. +* 17-MAR-2004 (DSB): +* - Modified Typical to give normal weight to edge bins in +* histogram if these bins contain all the counts. +* - Modified DrawTicks to add extra minor ticks below first major +* tick value and above last major tick value. +* - Norm1 can reject usable tick mark values because of an +* inappropriate value being used on the other axis (i.e. one for +* which the position is undefined in grapics coords). Therfoer +* Norm1 has been modified to use 3 different reference values +* in an attempt to find one which gives good axis values. +* 25-AUG-2004 (DSB): +* - Correct handling of "fmt" pointer in TickMarks function (identified +* and reported by Bill Joye). +* 14-SEP-2004 (DSB): +* - In EdgeLabels change definition of "distinct labels". Used to +* be that labels were distinct if they had different formatted +* labels. Now they are distinct if they have different floating +* point numerical values. Fixes a bug reported by Micah Johnson. +* - TickMarks re-structured to optimise the precision (no. of digits) +* even if a value has been assigned for the Format attribute, but only +* if the format specifier includes a wildcard precision specifier. For +* instance, to get graphical separators a format must be specified +* which included the "g" flag. As things were, this would prevent +* the optimisation of the digits value. Can now use "dms.*g" to +* allow the number of digits to be optimised. +* 29-SEP-2004 (DSB): +* - In FindMajTicks, begin the process of increasing "nfill" from +* a value of zero rather than one (in many cases no filling is +* needed). +* - In GetTicks (linear tick marks section) ensure that 10 +* *different* gap sizes are used before giving up. Previously, the +* 10 tests could include duplicated gap values. +* 8-NOV-2004 (DSB): +* - In Norm1, try more alternative "other axis" values before +* accepting that a tick mark value cannot be normalised. +* 2-FEB-2005 (DSB): +* - Avoid using astStore to allocate more storage than is supplied +* in the "data" pointer. This can cause access violations since +* astStore will then read beyond the end of the "data" area. +* 15-MAR-2005 (DSB): +* - Modified GridLines to use appropriate algorithm for choosing +* start of grid lines in cases where one axis has logarithmic tick +* spacing and the other has linear tick spacing. +* 21-MAR-2005 (DSB): +* - Added the Clip attribute. +* 12-JUL-2005 (DSB): +* - Modified AxPlot so that Map1 only normalises if neither axis +* is a SkyAxis. Previously it normalised if either axis was not a +* SkyAxis. +* 7-DEC-2005 (DSB): +* Free memory allocated by calls to astReadString. +* 18-JAN-2006 (DSB) +* Add Abbrev attribute. +* 14-FEB-2006 (DSB) +* Correct EdgeLabels to use gap size rather than EQUAL macro when +* comparing label values. +* 17-FEB-2006 (DSB) +* Added escape sequences "%h+" and "%g+". +* 21-MAR-2006 (DSB) +* Added extra status checks in TickMarks. +* 18-MAY-2006 (DSB) +* Trans: use correct PointSet when tranforming to arbitrary +* clipping frame. +* 26-MAY-2006 (DSB) +* Added LabelAt to TestAttrib. +* 2-JUN-2006 (DSB) +* - In MAKE_GET2, return the set value if a value has been set +* without recalculating the defaults. +* - Fix bug that could cause segvio in Grid if clipping is used. +* 5-JUN-2006 (DSB) +* Do not change the box returned by astBoundBox as a consequence +* of calling astGetAttrib. +* 19-JUN-2006 (DSB) +* Changed the default line 0.0 from zero to 1.0. +* 22-JUN-2006 (DSB) +* Include axis textual labels and title in the bounding box +* created by AST_GRID and returned by AST_BOUNDINGBOX. +* 26-JUN-2006 (DSB) +* Set the Direction attribute in the base Frame of a Plot if an +* axis is reversed. +* 29-JUN-2006 (DSB) +* - Guard against astGap calls that reach a minimum gap size. +* - Sort out splitting of long axis labels (such as date/time +* strings produced by TimeFrames). +* 30-JUN-2006 (DSB) +* If abbreviating labels, display the last field for identical +* neighbours rather than the whole value. +* 10-JUL-2006 (DSB) +* Make astStripEscapes public so it can be used by the NDF library. +* 7-AUG-2006 (DSB) +* Increase the number of attempts to find a new gap size from 5 to +* 25 in GetTicks. +* 24-OCT-2006 (DSB) +* Add the ForceExterior attribute so that SPLAT can have external +* axes even if there are no usable horizontal axis ticks (as requested +* by PWD). Currently this attribute is not included in the public +* documentation, as it may cause problems. If it seems to work OK +* then it can be made public. +* 25-JAN-2006 (DSB) +* Do not draw ticks marks that start outside the bounds of the +* axis they are labelling. +* 27-FEB-2007 (DSB) +* - Change nominal Crv_scerr value from 5.0 to 1.5 (this avoids gaps +* in HPX grid plots being bridged by grid lines). +* - Double the dimension of the grid used by GoodGrid to avoid +* missing the pointy bits in a HPX projection. +* 17-MAY-2007 (DSB) +* Exclude corner positions when determining the range of axis +* values covered by the plot. This gives better default gap sizes. +* 11-JUN-2007 (DSB) +* Plug memory leaks. +* 20-JUN-2007 (DSB) +* - Add attribute GrfContext. +* - Pass the GrfContext attribute value to each external grf function. +* External code that uses the astGrfSet function must be changed +* so that the external grf functions registered using astGrfSet +* accept this new parameter. +* 21-JUN-2007 (DSB) +* - Change GrfContext to be an Object rather than an integer. +* 22-JUN-2007 (DSB) +* - Do not dump the GrfContext Object since it may cause an +* infinite dumping loop. +* - Allow a NULL vtab to be supplied when initialising a Plot +* structure. This causes the vtab defined locally within this +* class to be used so that the new object behaves as a simple Plot. +* 25-JUN-2007 (DSB) +* - Free the graphics context object when then the Plot is deleted. +* - Fix memory leak in FullForm. +* - Since the grfcontext object is only used by external code, store +* a public object identifier for it in the Plot structure rather +* than a true C pointer. +* 26-JUN-2007 (DSB) +* Honour the LabelUp attribute value even if labels are drawn +* around the edges of the plot. +* 28-JUN-2007 (DSB) +* - Make all axis attribute arrays 3 elements long rather than 2. +* - Add the protected methods astCopyPlotDefaults and astMirror. +* - Add public method astGetGrfContext, remove astSetGrfContext. +* - Fix memory leak. +* 6-SEP-2007 (DSB): +* Dump and load any user-specified tick mark values. +* 20-OCT-2009 (DSB): +* - Modify SplitValue so that it only splits long values if +* previous long values were split, or if the value contains a +* space. +* - Take account of zero height bounding boxes in UpdateConcat. +* - Correct Dump so that it dumps attributes for all available +* axes (2 for a Plot, 3 for a Plot3D). +* 12-JAN-2010 (DSB): +* Fix various memory leaks. +* 4-MAR-2011 (DSB): +* - Added grf functions BBuf and EBuf. +* - Added public method astBBuf and astEBuf. +* 23-AUG-2011 (DSB): +* - If exterior labelling was requested but would produced too +* few labels, only swap to interior labelling if doing so would +* allow more labels to be drawn (i.e. do not swap to interior if +* it does not gain us anything). +* - Slightly decrease the dynamic range of an axis needed to produce +* logarithmic ticks (this is to avoid problems with round errors). +* 6-OCT-2011 (DSB): +* - Prevent grf qch and scales functions being called lots of times +* during each drawing operation (since the information returned by +* these functions will not change during the course of a single drawing +* operation). +* 11-OCT-2011 (DSB): +* - Combine multiple continuous polylines into a single polyline +* before calling the grf polyline function. Reducing the number of +* calls to the underlying graphics system can make a big difference +* if the graphics system is written in an interpreted language +* such as python. +* - Take account of differing axis scales when rotating vectors by +* 90 degrees. +* 12-OCT-2011 (DSB): +* - Fix the change made yesterday to correct rotation of numerical axis +* rotations. I forgot that the astGText function in the grf module +* expects the up vector in equally scaled coords, not graphics coords. +* - Take account of unequal axis scales when working out the +* length of each grid curve. +* 15-OCT-2011 (DSB): +* Always check that the grf module implements the scales function +* before trying to invoke the scales function. +* 21-MAY-2012 (DSB): +* Correct text strings used to represent the "Labelling" attribute +* within dumps of a Plot. Previously they were reversed. +* 7-JUN-2012 (DSB): +* Speed up plotting of CmpRegion boundaries by splitting the +* CmpRegion up into a set of disjoint Regions, and plotting each +* one separately. +* 16-JUN-2014 (DSB): +* - Prevent seg fault in PlotLabels caused by accessing +* uninitialised "atext" field stored within purged labels. +* - Choose a label with non-negative priority as the fall-back root label. +* 17-APR-2015 (DSB): +* Added method astRegionOutline. +* 20-APR-2015 (DSB): +* Draw Regions with higher accuracy, because Regions (i.e. Polygons) +* can be very non-smooth. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to the header + files that define class interfaces that they should make "protected" + symbols available. */ +#define astCLASS Plot + +/* Values for constants used in this class. */ +#define CRV_NSEG 14 /* No. of curve segments drawn by function Crv */ +#define CRV_NPNT 15 /* CRV_NSEG plus one */ +#define CRV_MXENT 10 /* Max. no. of recursive entries into function Crv */ +#define MAJTICKS_OPT 10 /* Optimum number of major axiss or grid lines */ +#define MAJTICKS_MAX 14 /* Max. number of major ticks or grid lines */ +#define MAJTICKS_MIN 6 /* Min. number of major ticks or grid lines */ +#define EDGETICKS_DIM 100 /* No. of edge samples used to find tick marks */ +#define LEFT 0 /* Id for the left edge of the plotting area */ +#define TOP 1 /* Id for the top edge of the plotting area */ +#define RIGHT 2 /* Id for the right edge of the plotting area */ +#define BOTTOM 3 /* Id for the bottom edge of the plotting area */ +#define NOSTYLE -999 /* A value which represents a null Style value */ +#define NOWIDTH -99.9 /* A value which represents a null Style value */ +#define NOFONT -999 /* A value which represents a null Style value */ +#define NOCOLOUR -999 /* A value which represents a null Style value */ +#define NOSIZE -99.9 /* A value which represents a null Style value */ + +#if defined(THREAD_SAFE) +#define GLOBALS_PROTO , AstGlobals * +#define GLOBALS_ARG , AstGlobals *AST__GLOBALS +#define GLOBALS_NAME , AST__GLOBALS +#else +#define GLOBALS_PROTO +#define GLOBALS_ARG +#define GLOBALS_NAME +#endif + +/* +* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot.h" +* MAKE_CLEAR(attr,component,assign,nval) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstPlot *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear_( AstPlot *this, int axis ) +* +* which implement a method for clearing a single value in a specified +* multi-valued attribute for an axis of a Plot. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. LabelAt in "astClearLabelAt"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). If a value of 0 is supplied, the +* value of the Plot's Nin attribute is used instead. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attr,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstPlot *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= ( nval ? nval : astGetNin( this ) ) ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astClear" #attr, astGetClass( this ), \ + axis + 1, ( nval ? nval : astGetNin( this ) ) ); \ +\ +/* Assign the "clear" value. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attr##_( AstPlot *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,Plot,Clear##attr))( this, axis, status ); \ +} + + +/* +* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot.h" +* MAKE_GET(attr,type,bad_value,assign,nval) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstPlot *this, int axis ) +* +* and an external interface function of the form: +* +* astGet_( AstPlot *this, int axis ) +* +* which implement a method for getting a single value from a specified +* multi-valued attribute for an axis of a Plot. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). If a value of 0 is supplied, the +* value of the Plot's Nin attribute is used instead. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET(attr,type,bad_value,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstPlot *this, int axis, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Initialise */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= ( nval ? nval : astGetNin( this ) ) ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astGet" #attr, astGetClass( this ), \ + axis + 1, ( nval ? nval : astGetNin( this ) ) ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attr##_( AstPlot *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,Plot,Get##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute +* for a Plot. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot.h" +* MAKE_SET(attr,type,component,assign,nval) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPlot *this, int axis, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstPlot *this, int axis, value ) +* +* which implement a method for setting a single value in a specified +* multi-valued attribute for a Plot. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. LabelAt in "astSetLabelAt"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). If a value of 0 is supplied, the +* value of the Plot's Nin attribute is used instead. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET(attr,type,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstPlot *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= ( nval ? nval : astGetNin( this ) ) ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astSet" #attr, astGetClass( this ), \ + axis + 1, ( nval ? nval : astGetNin( this ) ) ); \ +\ +/* Store the new value in the structure component. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attr##_( AstPlot *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,Plot,Set##attr))( this, axis, value, status ); \ +} + +/* +* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a method to test if a single value has been set in a +* multi-valued attribute for a class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot.h" +* MAKE_TEST(attr,assign,nval) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( AstPlot *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest_( AstPlot *this, int axis ) +* +* which implement a method for testing if a single value in a specified +* multi-valued attribute has been set for a class. + +* Parameters: +* attr +* The name of the attribute to be tested, as it appears in the function +* name (e.g. LabelAt in "astTestLabelAt"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. This can use the string "axis" to represent the zero-based +* index of the value within the attribute. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). If a value of 0 is supplied, the +* value of the Plot's Nin attribute is used instead. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_TEST(attr,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attr( AstPlot *this, int axis, int *status ) { \ + int result; /* Value to return */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= ( nval ? nval : astGetNin( this ) ) ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astTest" #attr, astGetClass( this ), \ + axis + 1, ( nval ? nval : astGetNin( this ) ) ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attr##_( AstPlot *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,Plot,Test##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_GET3 + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* MAKE_GET3(attr,attr,type,bad_value,assign,nval) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstPlot *this, int axis ) +* +* which implements a method for getting a single value from a specified +* multi-valued attribute for an axis of a Plot. Note, no public +* interface function is created. +* +* The value returned is the value which would actually be used if +* astGrid was called with the current set of Plot attributes. This +* includes calculating any dynamic defaults which would be used, and is +* consequently rather slow. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). The +* string "Used" is added on to the front of the supplied value. +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). If a value of 0 is supplied, the +* value of the Plot's Nin attribute is used instead. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET3(attr,type,bad_value,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type GetUsed##attr( AstPlot *, int, int *status ); \ +static type GetUsed##attr( AstPlot *this, int axis, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Initialise */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= ( nval ? nval : astGetNin( this ) ) ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astGetUsed" #attr, astGetClass( this ), \ + axis + 1, ( nval ? nval : astGetNin( this ) ) ); \ +\ +/* If the attribute is set, use its normal accessor. */\ + } else if( astTest##attr( this, axis ) ) {\ + result = astGet##attr( this, axis );\ +\ +/* Otherwise, re-calculate dynamic defaults by going through the motions of \ + drawing the grid. Nothing is actually drawn because we set the protected \ + attribute Ink to zero first. The calculated values are stored in the \ + Plot structure. */ \ + } else { \ + astSetInk( this, 0 ); \ + astGrid( this ); \ + astClearInk( this ); \ +\ +/* Assign the result value. */ \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* +* Name: +* MAKE_SET3 + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute +* for a Plot. This is identical to MAKE_SET except that no external +* interface function is created. + +* Type: +* Private macro. + +* Synopsis: +* MAKE_SET3(attr,type,component,assign,nval) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPlot *this, int axis, value ) +* +* which implements a method for setting a single value in a specified +* multi-valued attribute for a Plot. + +* Parameters: + * attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astSetLabel"). The +* string "Used" is added on to the front of the supplied value. +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). If a value of 0 is supplied, the +* value of the Plot's Nin attribute is used instead. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET3(attr,type,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void SetUsed##attr( AstPlot *, int, type, int *status ); \ +static void SetUsed##attr( AstPlot *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= ( nval ? nval : astGetNin( this ) ) ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astSetUsed" #attr, astGetClass( this ), \ + axis + 1, ( nval ? nval : astGetNin( this ) ) ); \ +\ +/* Store the new value in the structure component. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} + +/* +*+ +* Name: +* MAKE_GET2 + +* Purpose: +* Implement a method to get an attribute value for a class. + +* Type: +* Protected macro. + +* Synopsis: +* MAKE_GET2(class,attr,type,bad_value,assign) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static GetUsed( AstPlot *this ) +* +* which implement a method for getting a specified attribute value for a +* class. Note, no public interface function is created. +* +* The value returned is the value which would actually be used if +* astGrid was called with the current set of Plot attributes. This +* includes calculating any dynamic defaults which would be used, and is +* consequently rather slow. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). The +* string "Used" is added on to the front of the supplied value. +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_GET2(class,attr,type,bad_value,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type GetUsed##attr( Ast##class *, int *status ); \ +static type GetUsed##attr( Ast##class *this, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* If the attribute is set, use its normal accessor. */\ + if( astTest##attr( this ) ) {\ + result = astGet##attr( this );\ +\ +/* Otherwise, re-calculate dynamic defaults by going through the motions of \ + drawing the grid. Nothing is actually drawn because we set the protected \ + attribute Ink to zero first. The calculated values are stored in the \ + Plot structure. */ \ + } else { \ + astSetInk( this, 0 ); \ + astGrid( this ); \ + astClearInk( this ); \ +\ +/* Assign the result value. */ \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +*+ +* Name: +* MAKE_SET2 + +* Purpose: +* Implement a method to set an attribute value for a class. This +* is identical to astMAKE_SET except that it does not create an +* external interface function, and it does create a private function +* prototype. + +* Type: +* Protected macro. + +* Synopsis: +* MAKE_SET2(class,attr,type,component,assign) + +* Class Membership: +* Defined by the Plot class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void SetUsed( AstPlot *this, value ) +* +* which implements a method for setting a specified attribute value for a +* class. + +* Parameters: +* class +* The name (not the type) of the class to which the attribute belongs. +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. Label in "astSetLabel"). The string "Used" is added +* to the front. +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET2(class,attr,type,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void SetUsed##attr( Ast##class *, type, int *status ); \ +static void SetUsed##attr( Ast##class *this, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Store the new value in the structure component. */ \ + this->component = (assign); \ +} + + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ +#include "channel.h" /* I/O channels */ +#include "cmpmap.h" /* Compound mapping class */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "frame.h" /* Coordinate frame descriptions */ +#include "frameset.h" /* Parent FrameSet class */ +#include "grf.h" /* Low-level graphics interface */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "plot.h" /* Interface definition for this class */ +#include "pointset.h" /* Class holding lists of positions */ +#include "keymap.h" /* Hash maps */ +#include "skyaxis.h" /* Sky coordinate axes */ +#include "skyframe.h" /* Sky coordinate frames */ +#include "winmap.h" /* Scale and shift mappings */ +#include "mathmap.h" /* Algebraic mappings */ +#include "wcsmap.h" /* FITS-WCS projectsions */ +#include "unitmap.h" /* Unit mappings */ +#include "permmap.h" /* Axis permutations */ +#include "region.h" /* Frame regions */ +#include "globals.h" /* Thread-safe global data access */ + + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Type Definitions */ +/* ======================= */ +typedef struct LabelList { + double index; + char *text; + double x; + double y; + char *just; + double upx; + double upy; + double val; + int priority; + const char *atext; + int saved_prio; +} LabelList; + +/* Structure to hold static data used internally within the Crv function. */ +typedef struct CrvStatics { + double *pdl2; /* Pointer to next squared segment length */ + double *pdx; /* Pointer to next segment X increment */ + double *pdy; /* Pointer to next segment Y increment */ + double cosang; /* Cosine of angle between adjacent segments */ + double d0; /* Distance to start of first sub-segment */ + double delta; /* Distance between adjacent sub-segments */ + double dl; /* Segment length in graphics coordinates */ + double dll; /* Segment length for previous segment */ + double last_x; /* Graphics X at the end of the previous segment */ + double last_y; /* Graphics Y at the end of the previous segment */ + double limit2; /* Shortest acceptable squared segment length */ + double t1; /* Increment in X */ + double t2; /* Increment in Y */ + double t3; /* Squared segment length */ + double vx; /* X component of unit vector for current segment */ + double vxl; /* X component of unit vector for previous segment */ + double vy; /* Y component of unit vector for current segment */ + double vyl; /* Y component of unit vector for previous segment */ + int *seg0; /* Pointer to current segment OK flag */ + int *segm; /* Pointer to previous segment OK flag */ + int *segp; /* Pointer to next segment OK flag */ + int all_bad; /* Are all supplied positions bad or clipped? */ + int el; /* Total sub-segment count */ + int j; /* Sub-segment index */ + int last_ok; /* Was the previous position defined? */ + int nel; /* Total number of sub-segments */ + int nlong; /* No.of segments longer than limit2 */ + int nseg; /* Number of segments being sub-divided */ + int nshort; /* No.of segments shorter than limit2 */ + +#ifdef CRV_TRACE + int levels[100]; +#endif + +} CrvStatics; + +/* Structure to hold static data used internally within the Crv function. */ +typedef struct GetTicksStatics { + AstFrame *frame; /* Pointer to the current Frame */ + AstMapping *map; /* Pointer to Base->Current Mapping */ + AstPointSet *pset; /* Pointer to a PointSet holding physical coords */ + double **ptr; /* Pointer to physical coordinate values */ + double defgaps[ 2 ]; /* Initial test gaps for each axis */ + double typval[ 2 ]; /* Typical value on each axis */ + double width[ 2 ]; /* Range of used axis values */ + int maxticks; /* Max. number of ticks on each axis */ + int mintick; /* Min. number of ticks on each axis */ + int ngood[ 2 ]; /* No. of good physical values on each axis */ + int bad; /* Were any bad pixels found? */ +} GetTicksStatics; + +/* Structure to hold static data used internally within the EdgeCrossings + function. */ +typedef struct EdgeCrossingsStatics { + AstFrame *frame; /* Pointer to current Frame in Plot */ + AstPointSet *pset1; /* Graphics cooords at edge samples */ + AstPointSet *pset2; /* Physical cooords at edge samples */ + AstPointSet *pset4; /* Graphics cooords at offset edge samples */ + double **ptr1; /* Pointer to graphics coord. data */ + double **ptr2; /* Pointer to physical coord. data */ + double **ptr4; /* Pointer to graphics coord. data */ + double edgehi; /* High bound on varying graphics axis */ + double edgelo; /* Low bound on varying graphics axis */ + double edgeval; /* Constant graphics axis value along edge */ + double limit; /* Three times the RMS step size */ + int dim; /* Extended number of samples */ + int edgeax; /* Graphics axis to which edgeval refers */ + int paxis; /* Axis used in first invocation */ + int pedge; /* Edge used in first invocation */ +} EdgeCrossingsStatics; + + +/* Structure to hold static data used internally within the Map1 + function. */ +typedef struct Map1Statics { + AstPointSet *pset1; /* PointSet holding physical coords */ + AstPointSet *pset2; /* PointSet holding graphics coords */ + double **ptr1; /* Pointer to physical coord data */ + double *pax; /* Pointer to start of axis data */ + double *ptr2[ 2 ]; /* Pointers to graphics coord data */ + double *work1; /* Pointer to work space */ + double *work2; /* Pointer to work space */ + double axorig; /* Distance offset */ + double axscale; /* Distance scale */ + int neg; /* Negate axis values? */ + int nl; /* No. of points in pset1 and pset2 */ +} Map1Statics; + +/* Structure to hold static data used internally within the Map2 + function. */ +typedef struct Map2Statics { + AstPointSet *pset1; /* PointSet holding graphics coords */ + AstPointSet *pset2; /* PointSet holding physical coords */ + double **ptr2; /* Pointer to physical coord data */ + double *ptr1[ 2 ]; /* Pointers to graphics coord data */ + int nl; /* No. of points in pset1 and pset2 */ +} Map2Statics; + +/* Structure to hold static data used internally within the Map3 + function. */ +typedef struct Map3Statics { + AstPointSet *pset1; /* PointSet holding physical coords */ + AstPointSet *pset2; /* PointSet holding graphics coords */ + double **ptr1; /* Pointer to physical coord data */ + double *ptr2[ 2 ]; /* Pointers to graphics coord data */ + int nc; /* No. of physical axes */ + int nl; /* No. of points in pset1 and pset2 */ + double *pos; /* Pointer to memory for a single position */ +} Map3Statics; + +/* Structure to hold static data used internally within the Map4 + function. */ +typedef struct Map4Statics { + AstPointSet *pset1; /* PointSet holding distances */ + AstPointSet *pset2; /* PointSet holding physical coords */ + AstPointSet *pset3; /* PointSet holding graphics coords */ + int nl; /* No. of points in pset1 and pset2 */ +} Map4Statics; + +/* Structure to hold static data used internally within the Map5 + function. */ +typedef struct Map5Statics { + AstPointSet *pset1; /* PointSet holding physical coords */ + AstPointSet *pset2; /* PointSet holding graphics coords */ + double **ptr1; /* Pointer to physical coord data */ + double *ptr2[ 2 ]; /* Pointers to graphics coord data */ + int nl; /* No. of points in pset1 and pset2 */ +} Map5Statics; + +/* Structure to hold information about tick marks for a single axis. */ +typedef struct TickInfo{ + int nmajor; /* No. of major tick marks */ + int nminor; /* No. of minor tick marks */ + double *ticks; /* Pointer to array of major tick mark values */ + double *minticks; /* Pointer to array of minor tick mark values */ + char **labels; /* Pointer to array of major tick mark labels */ + double *start; /* Start pos'n on other axis for each curve section */ + double *length; /* Length on other axis of each curve section */ + int nsect; /* No. of sections in curve */ + char *fmt; /* Pointer to format string used to create labels */ + double gap; /* The gap between major ticks */ +} TickInfo; + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static void (* parent_removeframe)( AstFrameSet *, int, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, + AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Strings giving the label for the graphics items corresponding to + AST__BORDER_ID, AST__GRIDLINE_ID, etc. */ +static char *GrfLabels = "Border Curves Title Markers Strings Axis1 Axis2 Axis3 " + "NumLab1 NumLab2 NumLab3 TextLab1 TextLab2 TextLab3 " + "Ticks1 Ticks2 Ticks3 Grid1 Grid2 Grid3 Axes NumLab " + "TextLab Grid Ticks"; + +/* Text values used to represent edges externally. */ +static const char *xedge[4] = { "left", "top", "right", "bottom" }; + +/* Text values used to represent Labelling externally. */ +static const char *xlbling[2] = { "exterior", "interior" }; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GrfAttrs_nesting_t = 0; \ + globals->Crv_nent_t = 0; \ + globals->Box_lbnd_t[ 0 ] = FLT_MAX; \ + globals->Box_ubnd_t[ 0 ] = FLT_MIN; \ + globals->Boxp_lbnd_t[ 0 ] = FLT_MAX; \ + globals->Boxp_ubnd_t[ 0 ] = FLT_MIN; \ + globals->Box_lbnd_t[ 1 ] = FLT_MAX; \ + globals->Box_ubnd_t[ 1 ] = FLT_MIN; \ + globals->Boxp_lbnd_t[ 1 ] = FLT_MAX; \ + globals->Boxp_ubnd_t[ 1 ] = FLT_MIN; \ + globals->Boxp_freeze_t = 0; \ + globals->Map1_plot_t = NULL; \ + globals->Map1_map_t = NULL; \ + globals->Map1_frame_t = NULL; \ + globals->Map1_origin_t = NULL; \ + globals->Map1_statics_t = NULL; \ + globals->Map2_plot_t = NULL; \ + globals->Map2_map_t = NULL; \ + globals->Map2_statics_t = NULL; \ + globals->Map3_plot_t = NULL; \ + globals->Map3_map_t = NULL; \ + globals->Map3_frame_t = NULL; \ + globals->Map3_origin_t = NULL; \ + globals->Map3_end_t = NULL; \ + globals->Map3_statics_t = NULL; \ + globals->Map4_plot_t = NULL; \ + globals->Map4_map_t = NULL; \ + globals->Map4_umap_t = NULL; \ + globals->Map4_statics_t = NULL; \ + globals->Map5_plot_t = NULL; \ + globals->Map5_region_t = NULL; \ + globals->Map5_map_t = NULL; \ + globals->Map5_statics_t = NULL; \ + globals->Poly_n_t = 0; \ + globals->Poly_x_t = NULL; \ + globals->Poly_y_t = NULL; \ + globals->Poly_npoly_t = 0; \ + globals->Poly_np_t = NULL; \ + globals->Poly_xp_t = NULL; \ + globals->Poly_yp_t = NULL; \ + globals->Curve_data_t.nbrk = -1; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->SplitValue_Buff[ 0 ] = 0; \ + globals->StripEscapes_Buff[ 0 ] = 0; \ + globals->Grf_chv_t = AST__BAD; \ + globals->Grf_chh_t = AST__BAD; \ + globals->Grf_alpha_t = 0.0; \ + globals->Grf_beta_t = 0.0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Plot) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Plot,Class_Init) +#define class_vtab astGLOBAL(Plot,Class_Vtab) +#define grfattrs_nesting astGLOBAL(Plot,GrfAttrs_nesting_t) +#define grfattrs_attrs astGLOBAL(Plot,GrfAttrs_attrs_t) +#define Crv_limit astGLOBAL(Plot,Crv_limit_t) +#define Crv_scerr astGLOBAL(Plot,Crv_scerr_t) +#define Crv_tol astGLOBAL(Plot,Crv_tol_t) +#define Crv_ux0 astGLOBAL(Plot,Crv_ux0_t) +#define Crv_uy0 astGLOBAL(Plot,Crv_uy0_t) +#define Crv_vxl astGLOBAL(Plot,Crv_vxl_t) +#define Crv_vyl astGLOBAL(Plot,Crv_vyl_t) +#define Crv_xhi astGLOBAL(Plot,Crv_xhi_t) +#define Crv_xl astGLOBAL(Plot,Crv_xl_t) +#define Crv_xlo astGLOBAL(Plot,Crv_xlo_t) +#define Crv_yhi astGLOBAL(Plot,Crv_yhi_t) +#define Crv_yl astGLOBAL(Plot,Crv_yl_t) +#define Crv_ylo astGLOBAL(Plot,Crv_ylo_t) +#define Crv_vxbrk astGLOBAL(Plot,Crv_vxbrk_t) +#define Crv_vybrk astGLOBAL(Plot,Crv_vybrk_t) +#define Crv_xbrk astGLOBAL(Plot,Crv_xbrk_t) +#define Crv_ybrk astGLOBAL(Plot,Crv_ybrk_t) +#define Crv_len astGLOBAL(Plot,Crv_len_t) +#define Crv_ink astGLOBAL(Plot,Crv_ink_t) +#define Crv_nbrk astGLOBAL(Plot,Crv_nbrk_t) +#define Crv_nent astGLOBAL(Plot,Crv_nent_t) +#define Crv_out astGLOBAL(Plot,Crv_out_t) +#define Crv_clip astGLOBAL(Plot,Crv_clip_t) +#define Crv_map astGLOBAL(Plot,Crv_map_t) +#define Box_lbnd astGLOBAL(Plot,Box_lbnd_t) +#define Box_ubnd astGLOBAL(Plot,Box_ubnd_t) +#define Boxp_lbnd astGLOBAL(Plot,Boxp_lbnd_t) +#define Boxp_ubnd astGLOBAL(Plot,Boxp_ubnd_t) +#define Boxp_freeze astGLOBAL(Plot,Boxp_freeze_t) +#define Poly_x astGLOBAL(Plot,Poly_x_t) +#define Poly_y astGLOBAL(Plot,Poly_y_t) +#define Poly_n astGLOBAL(Plot,Poly_n_t) +#define Poly_xp astGLOBAL(Plot,Poly_xp_t) +#define Poly_yp astGLOBAL(Plot,Poly_yp_t) +#define Poly_np astGLOBAL(Plot,Poly_np_t) +#define Poly_npoly astGLOBAL(Plot,Poly_npoly_t) +#define Map1_ncoord astGLOBAL(Plot,Map1_ncoord_t) +#define Map1_plot astGLOBAL(Plot,Map1_plot_t) +#define Map1_map astGLOBAL(Plot,Map1_map_t) +#define Map1_frame astGLOBAL(Plot,Map1_frame_t) +#define Map1_origin astGLOBAL(Plot,Map1_origin_t) +#define Map1_length astGLOBAL(Plot,Map1_length_t) +#define Map1_axis astGLOBAL(Plot,Map1_axis_t) +#define Map1_statics astGLOBAL(Plot,Map1_statics_t) +#define Map1_norm astGLOBAL(Plot,Map1_norm_t) +#define Map1_log astGLOBAL(Plot,Map1_log_t) +#define Map2_ncoord astGLOBAL(Plot,Map2_ncoord_t) +#define Map2_plot astGLOBAL(Plot,Map2_plot_t) +#define Map2_map astGLOBAL(Plot,Map2_map_t) +#define Map2_x0 astGLOBAL(Plot,Map2_x0_t) +#define Map2_y0 astGLOBAL(Plot,Map2_y0_t) +#define Map2_deltax astGLOBAL(Plot,Map2_deltax_t) +#define Map2_deltay astGLOBAL(Plot,Map2_deltay_t) +#define Map2_statics astGLOBAL(Plot,Map1_statics_t) +#define Map3_ncoord astGLOBAL(Plot,Map3_ncoord_t) +#define Map3_plot astGLOBAL(Plot,Map3_plot_t) +#define Map3_map astGLOBAL(Plot,Map3_map_t) +#define Map3_frame astGLOBAL(Plot,Map3_frame_t) +#define Map3_origin astGLOBAL(Plot,Map3_origin_t) +#define Map3_end astGLOBAL(Plot,Map3_end_t) +#define Map3_scale astGLOBAL(Plot,Map3_scale_t) +#define Map3_statics astGLOBAL(Plot,Map3_statics_t) +#define Map4_ncoord astGLOBAL(Plot,Map4_ncoord_t) +#define Map4_plot astGLOBAL(Plot,Map4_plot_t) +#define Map4_map astGLOBAL(Plot,Map4_map_t) +#define Map4_umap astGLOBAL(Plot,Map4_umap_t) +#define Map4_statics astGLOBAL(Plot,Map4_statics_t) +#define Map5_plot astGLOBAL(Plot,Map5_plot_t) +#define Map5_region astGLOBAL(Plot,Map5_region_t) +#define Map5_map astGLOBAL(Plot,Map5_map_t) +#define Map5_ncoord astGLOBAL(Plot,Map5_ncoord_t) +#define Map5_statics astGLOBAL(Plot,Map5_statics_t) +#define Curve_data astGLOBAL(Plot,Curve_data_t) +#define getattrib_buff astGLOBAL(Plot,GetAttrib_Buff) +#define splitvalue_buff astGLOBAL(Plot,SplitValue_Buff) +#define stripescapes_buff astGLOBAL(Plot,StripEscapes_Buff) +#define Grf_chv astGLOBAL(Plot,Grf_chv_t) +#define Grf_chh astGLOBAL(Plot,Grf_chh_t) +#define Grf_alpha astGLOBAL(Plot,Grf_alpha_t) +#define Grf_beta astGLOBAL(Plot,Grf_beta_t) + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Variables used within astGrfAttrs_ */ +static double grfattrs_attrs[ GRF__NATTR ]; /* Saved attribute values */ +static int grfattrs_nesting = 0; /* Nesting level. */ + +/* Variables used to pass information to the curve drawing functions. See + the prologues of functions Crv and CrvLine for details. */ +static double Crv_limit; +static double Crv_scerr; +static double Crv_tol; +static double Crv_ux0; +static double Crv_uy0; +static double Crv_vxl; +static double Crv_vyl; +static double Crv_xhi; +static double Crv_xl; +static double Crv_xlo; +static double Crv_yhi; +static double Crv_yl; +static double Crv_ylo; +static float *Crv_vxbrk; +static float *Crv_vybrk; +static float *Crv_xbrk; +static float *Crv_ybrk; +static float Crv_len; +static int Crv_ink; +static int Crv_nbrk; +static int Crv_nent = 0; +static int Crv_out; +static int Crv_clip; +static void (*Crv_map)( int, double *, double *, double *, const char *, const char *, int * ); + +/* A cache of information calculated by the grf module. */ +static double Grf_chv = AST__BAD; +static double Grf_chh = AST__BAD; +static float Grf_alpha = 0.0; +static float Grf_beta = 0.0; + +/* The lower and upper bounds of the graphics coordinates enclosing all + lines and numerical labels drawn by astGrid. */ +static float Box_lbnd[ 2 ] = {FLT_MAX, FLT_MAX }; +static float Box_ubnd[ 2 ] = {FLT_MIN, FLT_MIN }; + +/* The lower and upper bounds of the graphics coordinates enclosing all + drawn graphics primatives, maintained by functions GLine, GMark and + DrawText. */ +static float Boxp_lbnd[ 2 ] = {FLT_MAX, FLT_MAX }; +static float Boxp_ubnd[ 2 ] = {FLT_MIN, FLT_MIN }; +static int Boxp_freeze = 0; + +/* Variables used to stored buffered poly lines (see functions Opoly, Bpoly + and Apoly). */ +static float *Poly_x = NULL; +static float *Poly_y = NULL; +static int Poly_n = 0; +static float **Poly_xp = NULL; +static float **Poly_yp = NULL; +static int *Poly_np = NULL; +static int Poly_npoly = 0; + +/* Variables used by function Map1. See the prologue of Map1 for details. */ +static int Map1_ncoord; +static AstPlot *Map1_plot = NULL; +static AstMapping *Map1_map = NULL; +static AstFrame *Map1_frame = NULL; +static const double *Map1_origin = NULL; +static double Map1_length; +static void *Map1_statics = NULL; +static int Map1_axis; +static int Map1_norm; +static int Map1_log; + +/* Variables used by function Map2. See the prologue of Map2 for details. */ +static int Map2_ncoord; +static AstPlot *Map2_plot = NULL; +static AstMapping *Map2_map = NULL; +static double Map2_x0; +static double Map2_y0; +static double Map2_deltax; +static double Map2_deltay; +static void *Map2_statics = NULL; + +/* Variables used by function Map3. See the prologue of Map3 for details. */ +static int Map3_ncoord; +static AstPlot *Map3_plot = NULL; +static AstMapping *Map3_map = NULL; +static AstFrame *Map3_frame = NULL; +static const double *Map3_origin = NULL; +static const double *Map3_end = NULL; +static double Map3_scale; +static void *Map3_statics = NULL; + +/* Variables used by function Map4. See the prologue of Map4 for details. */ +static int Map4_ncoord; +static AstPlot *Map4_plot = NULL; +static AstMapping *Map4_map = NULL; +static AstMapping *Map4_umap = NULL; +static void *Map4_statics = NULL; + +/* Variables used by function Map5. See the prologue of Map5 for details. */ +static AstPlot *Map5_plot = NULL; +static AstMapping *Map5_map = NULL; +static AstRegion *Map5_region = NULL; +static void *Map5_statics = NULL; +static int Map5_ncoord; + +/* A structure which stores information about the breaks in the last curve + drawn using the public methods "astGridLine" and "astCurve". */ +static AstPlotCurveData Curve_data; + +/* Buffers for strings returned by various functions. */ +static char splitvalue_buff[ 200 ]; +static char stripescapes_buff[ AST__PLOT_STRIPESCAPES_BUFF_LEN + 1 ]; +static char getattrib_buff[ 200 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPlotVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#endif + +/* Macro to reset the cache of values caclulated by the grf module. */ +#define RESET_GRF \ + Grf_chh = AST__BAD; \ + Grf_chv = AST__BAD; \ + Grf_alpha = 0.0; \ + Grf_beta = 0.0; + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ + +static double GetTol( AstPlot *, int * ); +static int TestTol( AstPlot *, int * ); +static void ClearTol( AstPlot *, int * ); +static void SetTol( AstPlot *, double, int * ); + +static int GetGrid( AstPlot *, int * ); +static int TestGrid( AstPlot *, int * ); +static void ClearGrid( AstPlot *, int * ); +static void SetGrid( AstPlot *, int, int * ); + +static int GetTickAll( AstPlot *, int * ); +static int TestTickAll( AstPlot *, int * ); +static void ClearTickAll( AstPlot *, int * ); +static void SetTickAll( AstPlot *, int, int * ); + +static int GetForceExterior( AstPlot *, int * ); +static int TestForceExterior( AstPlot *, int * ); +static void ClearForceExterior( AstPlot *, int * ); +static void SetForceExterior( AstPlot *, int, int * ); + +static int GetBorder( AstPlot *, int * ); +static int TestBorder( AstPlot *, int * ); +static void ClearBorder( AstPlot *, int * ); +static void SetBorder( AstPlot *, int, int * ); + +static int GetInvisible( AstPlot *, int * ); +static int TestInvisible( AstPlot *, int * ); +static void ClearInvisible( AstPlot *, int * ); +static void SetInvisible( AstPlot *, int, int * ); + +static int GetInk( AstPlot *, int * ); +static int TestInk( AstPlot *, int * ); +static void ClearInk( AstPlot *, int * ); +static void SetInk( AstPlot *, int, int * ); + +static int GetClipOp( AstPlot *, int * ); +static int TestClipOp( AstPlot *, int * ); +static void ClearClipOp( AstPlot *, int * ); +static void SetClipOp( AstPlot *, int, int * ); + +static int GetClip( AstPlot *, int * ); +static int TestClip( AstPlot *, int * ); +static void ClearClip( AstPlot *, int * ); +static void SetClip( AstPlot *, int, int * ); + +static int GetGrf( AstPlot *, int * ); +static int TestGrf( AstPlot *, int * ); +static void ClearGrf( AstPlot *, int * ); +static void SetGrf( AstPlot *, int, int * ); + +static int GetDrawTitle( AstPlot *, int * ); +static int TestDrawTitle( AstPlot *, int * ); +static void ClearDrawTitle( AstPlot *, int * ); +static void SetDrawTitle( AstPlot *, int, int * ); + +static int GetDrawAxes( AstPlot *, int, int * ); +static int TestDrawAxes( AstPlot *, int, int * ); +static void ClearDrawAxes( AstPlot *, int, int * ); +static void SetDrawAxes( AstPlot *, int, int, int * ); + +static int GetAbbrev( AstPlot *, int, int * ); +static int TestAbbrev( AstPlot *, int, int * ); +static void ClearAbbrev( AstPlot *, int, int * ); +static void SetAbbrev( AstPlot *, int, int, int * ); + +static int GetEscape( AstPlot *, int * ); +static int TestEscape( AstPlot *, int * ); +static void ClearEscape( AstPlot *, int * ); +static void SetEscape( AstPlot *, int, int * ); + +static double GetLabelAt( AstPlot *, int, int * ); +static int TestLabelAt( AstPlot *, int, int * ); +static void ClearLabelAt( AstPlot *, int, int * ); +static void SetLabelAt( AstPlot *, int, double, int * ); + +static double GetNumLabGap( AstPlot *, int, int * ); +static int TestNumLabGap( AstPlot *, int, int * ); +static void ClearNumLabGap( AstPlot *, int, int * ); +static void SetNumLabGap( AstPlot *, int, double, int * ); + +static double GetTextLabGap( AstPlot *, int, int * ); +static int TestTextLabGap( AstPlot *, int, int * ); +static void ClearTextLabGap( AstPlot *, int, int * ); +static void SetTextLabGap( AstPlot *, int, double, int * ); + +static double GetCentre( AstPlot *, int, int * ); +static int TestCentre( AstPlot *, int, int * ); +static void ClearCentre( AstPlot *, int, int * ); +static void SetCentre( AstPlot *, int, double, int * ); + +static double GetGap( AstPlot *, int, int * ); +static int TestGap( AstPlot *, int, int * ); +static void ClearGap( AstPlot *, int, int * ); +static void SetGap( AstPlot *, int, double, int * ); + +static int GetLabelling( AstPlot *, int * ); +static int TestLabelling( AstPlot *, int * ); +static void ClearLabelling( AstPlot *, int * ); +static void SetLabelling( AstPlot *, int, int * ); + +static double GetMajTickLen( AstPlot *, int, int * ); +static int TestMajTickLen( AstPlot *, int, int * ); +static void ClearMajTickLen( AstPlot *, int, int * ); +static void SetMajTickLen( AstPlot *, int, double, int * ); + +static double GetLogGap( AstPlot *, int, int * ); +static int TestLogGap( AstPlot *, int, int * ); +static void ClearLogGap( AstPlot *, int, int * ); +static void SetLogGap( AstPlot *, int, double, int * ); + +static double GetTitleGap( AstPlot *, int * ); +static int TestTitleGap( AstPlot *, int * ); +static void ClearTitleGap( AstPlot *, int * ); +static void SetTitleGap( AstPlot *, double, int * ); + +static double GetMinTickLen( AstPlot *, int, int * ); +static int TestMinTickLen( AstPlot *, int, int * ); +static void ClearMinTickLen( AstPlot *, int, int * ); +static void SetMinTickLen( AstPlot *, int, double, int * ); + +static int GetEdge( AstPlot *, int, int * ); +static int TestEdge( AstPlot *, int, int * ); +static void ClearEdge( AstPlot *, int, int * ); +static void SetEdge( AstPlot *, int, int, int * ); + +static int GetLabelUp( AstPlot *, int, int * ); +static int TestLabelUp( AstPlot *, int, int * ); +static void ClearLabelUp( AstPlot *, int, int * ); +static void SetLabelUp( AstPlot *, int, int, int * ); + +static int GetLogPlot( AstPlot *, int, int * ); +static int TestLogPlot( AstPlot *, int, int * ); +static void ClearLogPlot( AstPlot *, int, int * ); +static void SetLogPlot( AstPlot *, int, int, int * ); + +static int GetLogTicks( AstPlot *, int, int * ); +static int TestLogTicks( AstPlot *, int, int * ); +static void ClearLogTicks( AstPlot *, int, int * ); +static void SetLogTicks( AstPlot *, int, int, int * ); + +static int GetLogLabel( AstPlot *, int, int * ); +static int TestLogLabel( AstPlot *, int, int * ); +static void ClearLogLabel( AstPlot *, int, int * ); +static void SetLogLabel( AstPlot *, int, int, int * ); + +static int GetNumLab( AstPlot *, int, int * ); +static int TestNumLab( AstPlot *, int, int * ); +static void ClearNumLab( AstPlot *, int, int * ); +static void SetNumLab( AstPlot *, int, int, int * ); + +static int GetMinTick( AstPlot *, int, int * ); +static int TestMinTick( AstPlot *, int, int * ); +static void ClearMinTick( AstPlot *, int, int * ); +static void SetMinTick( AstPlot *, int, int, int * ); + +static int GetTextLab( AstPlot *, int, int * ); +static int TestTextLab( AstPlot *, int, int * ); +static void ClearTextLab( AstPlot *, int, int * ); +static void SetTextLab( AstPlot *, int, int, int * ); + +static int GetLabelUnits( AstPlot *, int, int * ); +static int TestLabelUnits( AstPlot *, int, int * ); +static void ClearLabelUnits( AstPlot *, int, int * ); +static void SetLabelUnits( AstPlot *, int, int, int * ); + +static int GetStyle( AstPlot *, int, int * ); +static int TestStyle( AstPlot *, int, int * ); +static void ClearStyle( AstPlot *, int, int * ); +static void SetStyle( AstPlot *, int, int, int * ); + +static int GetFont( AstPlot *, int, int * ); +static int TestFont( AstPlot *, int, int * ); +static void ClearFont( AstPlot *, int, int * ); +static void SetFont( AstPlot *, int, int, int * ); + +static int GetColour( AstPlot *, int, int * ); +static int TestColour( AstPlot *, int, int * ); +static void ClearColour( AstPlot *, int, int * ); +static void SetColour( AstPlot *, int, int, int * ); + +static double GetWidth( AstPlot *, int, int * ); +static int TestWidth( AstPlot *, int, int * ); +static void ClearWidth( AstPlot *, int, int * ); +static void SetWidth( AstPlot *, int, double, int * ); + +static double GetSize( AstPlot *, int, int * ); +static int TestSize( AstPlot *, int, int * ); +static void ClearSize( AstPlot *, int, int * ); +static void SetSize( AstPlot *, int, double, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static AstFrameSet *Fset2D( AstFrameSet *, int, int * ); +static AstKeyMap *GetGrfContext( AstPlot *, int * ); +static AstPlotCurveData **CleanCdata( AstPlotCurveData **, int * ); +static AstPlotCurveData **DrawGrid( AstPlot *, TickInfo **, int, const char *, const char *, int * ); +static AstPointSet *DefGap( AstPlot *, double *, int *, double *, int *, const char *, const char *, int * ); +static AstPointSet *GetDrawnTicks( AstPlot *, int, int, int * ); +static AstPointSet *Trans( AstPlot *, AstFrame *, AstMapping *, AstPointSet *, int, AstPointSet *, int, const char *, const char *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static TickInfo **CleanGrid( TickInfo **, int * ); +static TickInfo **GridLines( AstPlot *, double *, double *, int *, const char *, const char *, int * ); +static TickInfo *TickMarks( AstPlot *, int, double *, double *, int *, GetTicksStatics **, const char *, const char *, int * ); +static char **CheckLabels2( AstPlot *, AstFrame *, int, double *, int, char **, double, int * ); +static char *FindWord( char *, const char *, const char **, int * ); +static char *GrfItem( int, const char *, int *, int * ); +static const char *JustMB( AstPlot *, int, const char *, float *, float *, float, float, const char *, float, float, float, float, float *, float *, const char *, const char *, int * ); +static const char *SplitValue( AstPlot *, const char *, int, int *, int * ); +static double **MakeGrid( AstPlot *, AstFrame *, AstMapping *, int, int, double, double, double, double, int, AstPointSet **, AstPointSet**, int, const char *, const char *, int * ); +static double GetTicks( AstPlot *, int, double *, double **, int *, double **, int *, int, int *, double *, GetTicksStatics **, const char *, const char *, int * ); +static double GetUseSize( AstPlot *, int, int * ); +static double GetUseWidth( AstPlot *, int, int * ); +static double GoodGrid( AstPlot *, int *, AstPointSet **, AstPointSet **, const char *, const char *, int * ); +static double Typical( int, double *, double, double, double *, int * ); +static int Border( AstPlot *, int * ); +static int Boundary( AstPlot *, const char *, const char *, int * ); +static int BoxCheck( float *, float *, float *, float *, int * ); +static int CGAttrWrapper( AstPlot *, int, double, double *, int, int * ); +static int CGBBufWrapper( AstPlot *, int * ); +static int CGCapWrapper( AstPlot *, int, int, int * ); +static int CGEBufWrapper( AstPlot *, int * ); +static int CGFlushWrapper( AstPlot *, int * ); +static int CGLineWrapper( AstPlot *, int, const float *, const float *, int * ); +static int CGMarkWrapper( AstPlot *, int, const float *, const float *, int, int * ); +static int CGQchWrapper( AstPlot *, float *, float *, int * ); +static int CGScalesWrapper( AstPlot *, float *, float *, int * ); +static int CGTextWrapper( AstPlot *, const char *, float, float, const char *, float, float, int * ); +static int CGTxExtWrapper( AstPlot *, const char *, float, float, const char *, float, float, float *, float *, int * ); +static int CheckLabels( AstPlot *, AstFrame *, int, double *, int, int, char **, double, int * ); +static int ChrLen( const char *, int * ); +static int Compare_LL( const void *, const void * ); +static int Compared( const void *, const void * ); +static int CountGood( int, double *, int * ); +static int Cross( float, float, float, float, float, float, float, float, int * ); +static int CvBrk( AstPlot *, int, double *, double *, double *, int * ); +static int DrawRegion( AstPlot *, AstFrame *, const char *, const char *, int * ); +static int EdgeCrossings( AstPlot *, int, int, double, double *, double **, EdgeCrossingsStatics **, const char *, const char *, int * ); +static int EdgeLabels( AstPlot *, int, TickInfo **, AstPlotCurveData **, int, const char *, const char *, int * ); +static int FindDPTZ( AstFrame *, int, const char *, const char *, int *, int *, int * ); +static int FindMajTicks( AstMapping *, AstFrame *, int, double, double, double , double *, int, double *, double **, int * ); +static int FindMajTicks2( int, double, double, int, double *, double **, int * ); +static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * ); +static int Fpoly_ecmp( const void *, const void * ); +static int Fpoly_scmp( const void *, const void * ); +static int FullForm( const char *, const char *, const char *, const char *, const char *, int * ); +static int GCap( AstPlot *, int, int, int * ); +static int GVec( AstPlot *, AstMapping *, double *, int, double, AstPointSet **, AstPointSet **, double *, double *, double *, double *, int *, const char *, const char *, int * ); +static int GetUseColour( AstPlot *, int, int * ); +static int GetUseFont( AstPlot *, int, int * ); +static int GetUseStyle( AstPlot *, int, int * ); +static int GraphGrid( int, int, double, double, double, double, double **, int * ); +static int HasEscapes( const char *, int * ); +static int IdFind( int, int, int *, int *, int *, int * ); +static int Inside( int, float *, float *, float, float, int * ); +static int IsASkyAxis( AstFrame *, int, int * ); +static int IsASkyFrame( AstObject *, int * ); +static int Labelat( AstPlot *, TickInfo **, AstPlotCurveData **, double *, const char *, const char *, int * ); +static int Overlap( AstPlot *, int, int, const char *, float, float, const char *, float, float, float **, const char *, const char *, int * ); +static int PopGat( AstPlot *, float *, const char *, const char *, int * ); +static int TestUseColour( AstPlot *, int, int * ); +static int TestUseFont( AstPlot *, int, int * ); +static int TestUseSize( AstPlot *, int, int * ); +static int TestUseStyle( AstPlot *, int, int * ); +static int TestUseWidth( AstPlot *, int, int * ); +static int ToggleLogLin( AstPlot *, int, int, const char *, int * ); +static int TraceBorder( AstPlot *, AstMapping *, double, double, double, double, int, double, int[ 4 ], const char *, const char *, int * ); +static int Ustrcmp( const char *, const char *, int * ); +static int Ustrncmp( const char *, const char *, size_t, int * ); +static int swapEdges( AstPlot *, TickInfo **, AstPlotCurveData **, int * ); +static void AddCdt( AstPlotCurveData *, AstPlotCurveData *, const char *, const char *, int * ); +static void Apoly( AstPlot *, float, float, int * ); +static void AxPlot( AstPlot *, int, const double *, double, int, AstPlotCurveData *, const char *, const char *, int * ); +static void BBuf( AstPlot *, int * ); +static void BoundingBox( AstPlot *, float[2], float[2], int * ); +static void Bpoly( AstPlot *, float, float, int * ); +static void Clip( AstPlot *, int, const double [], const double [], int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void CopyPlotDefaults( AstPlot *, int, AstPlot *, int, int * ); +static void Crv( AstPlot *this, double *, double *, double *, int, double *, CrvStatics *, const char *, const char *, int * ); +static void CrvLine( AstPlot *this, double, double, double, double, const char *, const char *, int * ); +static void Curve( AstPlot *, const double [], const double [], int * ); +static void CurvePlot( AstPlot *, const double *, const double *, int , AstPlotCurveData *, const char *, const char *, int * ); +static void Delete( AstObject *, int * ); +static void DrawAxis( AstPlot *, TickInfo **, double *, double *, const char *, const char *, int * ); +static void DrawText( AstPlot *, int, int, const char *, float, float, const char *, float, float, float *, float *, float *, const char *, const char *, int * ); +static void DrawTicks( AstPlot *, TickInfo **, int, double *, double *, const char *, const char *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void EBuf( AstPlot *, int * ); +static void Fpoly( AstPlot *, const char *, const char *, int * ); +static void GAttr( AstPlot *, int, double, double *, int, const char *, const char *, int * ); +static void GBBuf( AstPlot *, const char *, const char *, int * )__attribute__((unused)); +static void GEBuf( AstPlot *, const char *, const char *, int * )__attribute__((unused)); +static void GFlush( AstPlot *, const char *, const char *, int * )__attribute__((unused)); +static void GLine( AstPlot *, int, const float *, const float *, const char *, const char *, int * ); +static void GMark( AstPlot *, int, const float *, const float *, int, const char *, const char *, int * ); +static void GQch( AstPlot *, float *, float *, const char *, const char *, int * ); +static void GScales( AstPlot *, float *, float *, const char *, const char *, int * ); +static void GText( AstPlot *, const char *, float, float, const char *, float, float, const char *, const char *, int * ); +static void GTxExt( AstPlot *, const char *, float , float, const char *, float, float, float *, float *, const char *, const char *, int * ); +static void GenCurve( AstPlot *, AstMapping *, int * ); +static void GrfPop( AstPlot *, int * ); +static void GrfPush( AstPlot *, int * ); +static void GrfSet( AstPlot *, const char *, AstGrfFun, int * ); +static void GrfWrapper( AstPlot *, const char *, AstGrfWrap, int * ); +static void Grid( AstPlot *, int * ); +static void GridLine( AstPlot *, int, const double [], double, int * ); +static void InterpEscape( AstPlot *, int, double, float *, float *, float, float, float, float, const char *, float *, double, double, double, double, double, const char *, const char *, int * ); +static void Labels( AstPlot *, TickInfo **, AstPlotCurveData **, double *, double *, const char *, const char *, int * ); +static void LinePlot( AstPlot *, double, double, double, double, int, AstPlotCurveData *, const char *, const char *, int * ); +static void Map1( int, double *, double *, double *, const char *, const char *, int * GLOBALS_PROTO ); +static void Map2( int, double *, double *, double *, const char *, const char *, int * GLOBALS_PROTO ); +static void Map3( int, double *, double *, double *, const char *, const char *, int * GLOBALS_PROTO ); +static void Map4( int, double *, double *, double *, const char *, const char *, int * GLOBALS_PROTO ); +static void Map5( int, double *, double *, double *, const char *, const char *, int * GLOBALS_PROTO ); +static void Mark( AstPlot *, int, int, int, const double *, int, int * ); +static void Mirror( AstPlot *, int, int * ); +static void Norm1( AstMapping *, int, int, double *, double, double, int * ); +static void Opoly( AstPlot *, int * ); +static void PlotLabels( AstPlot *, int, AstFrame *, int, LabelList *, char *, int, float **, const char *, const char *, int * ); +static void PolyCurve( AstPlot *, int, int, int, const double *, int * ); +static void PurgeCdata( AstPlotCurveData *, int * ); +static void PushGat( AstPlot *, float, const char *, const char *, int * ); +static void RegionOutline( AstPlot *, AstRegion *, int * ); +static void RemoveFrame( AstFrameSet *, int, int * ); +static void RightVector( AstPlot *, float *, float *, float *, float *, const char *, const char *, int * ); +static void SaveTick( AstPlot *, int, double, double, int, int * ); +static void SetTickValues( AstPlot *, int, int, double *, int, double *, int * ); +static void Text( AstPlot *, const char *, const double [], const float [], const char *, int * ); +static void TextLabels( AstPlot *, int, int *, const char *, const char *, int * ); +static void Ticker( AstPlot *, int, int, double, double *, double, int, int, EdgeCrossingsStatics **, const char *, const char *, int * ); +static void UpdateConcat( float *, float *, float, float, float, float, float *, float *, float, float, float *, float *, float *, float *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Functions which access class attributes. */ +/* ======================================= */ +/* Implement member functions to access the attributes associated with this + class using the macros defined for this purpose in the "object.h" file. For + a description of each attribute, see the class interface (in the associated + .h file). */ + +/* Tol. */ +/* ---- */ +/* +*att++ +* Name: +* Tol + +* Purpose: +* Plotting tolerance. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the plotting tolerance (or resolution) +* to be used for the graphical output produced by a Plot. Smaller +* values will result in smoother and more accurate curves being +* drawn, but may slow down the plotting process. Conversely, +* larger values may speed up the plotting process in cases where +* high resolution is not required. +* +* The Tol value should be given as a fraction of the minimum +* dimension of the plotting area, and should lie in the range +c from 1.0e-7 to 1.0. By default, a value of 0.01 is used. +f from 1.0E-7 to 1.0. By default, a value of 0.01 is used. + +* Applicability: +* Plot +* All Plots have this attribute. +*att-- +*/ +/* The plotting tolerance. Has a value of -1.0 when not set yielding a +default value of 0.01. Usable values are in the range 1.0E-7 to 1.0. */ +astMAKE_CLEAR(Plot,Tol,tol,-1.0) +astMAKE_GET(Plot,Tol,double,0.01,(this->tol == -1.0 ? 0.01 : this->tol)) +astMAKE_SET(Plot,Tol,double,tol,astMIN(astMAX(value,1.0E-7),1.0)) +astMAKE_TEST(Plot,Tol,( this->tol != -1.0 )) + +/* Grid. */ +/* ----- */ +/* +*att++ +* Name: +* Grid + +* Purpose: +* Draw grid lines for a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether grid lines (a grid of curves marking the "major" values +* on each axis) are drawn across the plotting area. +* +* If the Grid value of a Plot is non-zero, then grid lines will be +* drawn. Otherwise, short tick marks on the axes are used to mark +* the major axis values. The default behaviour is to use tick +* marks if the entire plotting area is filled by valid physical +* coordinates, but to draw grid lines otherwise. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The spacing between major axis values, which determines the +* spacing of grid lines, may be set using the Gap(axis) attribute. +*att-- +*/ +/* If non-zero use lines instead of tick marks in a coordinate grid. Has a +value of -1 when not set yielding a default value of 0. */ +astMAKE_CLEAR(Plot,Grid,grid,-1) +astMAKE_GET(Plot,Grid,int,0,(this->grid == -1 ? 0 : this->grid)) +astMAKE_SET(Plot,Grid,int,grid,( value ? 1 : 0 )) +astMAKE_TEST(Plot,Grid,( this->grid != -1 )) + +MAKE_GET2(Plot,Grid,int,0,this->ugrid) +MAKE_SET2(Plot,Grid,int,ugrid,( value ? 1 : 0 )) + +/* Invisible. */ +/* ---------- */ +/* +*att++ +* Name: +* Invisible + +* Purpose: +* Draw graphics using invisible ink? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of all graphics produced by +* Plot methods by determining whether graphics should be visible or +* invisible. +* +* If the Invisible value of a Plot is non-zero, then all the Plot +* methods which normally generate graphical output do not do so (you +* can think of them drawing with "invisible ink"). Such methods do, +* however, continue to do all the calculations which would be needed to +* produce the graphics. In particular, the bounding box enclosing the +* graphics is still calculated and can be retrieved as normal using +c astBoundingBox. The default value is zero, resulting in all methods +f AST_BOUNDINGBOX. The default value is zero, resulting in all methods +* drawing graphics as normal, using visible ink. + +* Applicability: +* Plot +* All Plots have this attribute. + +*att-- +*/ +/* If non-zero use invisible ink. Has a value of -1 when not set yielding +a default value of 0. */ +astMAKE_CLEAR(Plot,Invisible,invisible,-1) +astMAKE_GET(Plot,Invisible,int,0,(this->invisible == -1 ? 0 : this->invisible)) +astMAKE_SET(Plot,Invisible,int,invisible,( value ? 1 : 0 )) +astMAKE_TEST(Plot,Invisible,( this->invisible != -1 )) + +/* TickAll */ +/* ------- */ +/* +*att++ +* Name: +* TickAll + +* Purpose: +* Draw tick marks on all edges of a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether tick marks should be drawn on all edges of a Plot. +* +* If the TickAll value of a Plot is non-zero (the default), then +* tick marks will be drawn on all edges of the Plot. Otherwise, +* they will be drawn only on those edges where the numerical and +* descriptive axis labels are drawn (see the Edge(axis) +* attribute). + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - In some circumstances, numerical labels and tick marks are +* drawn along grid lines inside the plotting area, rather than +* around its edges (see the Labelling attribute). In this case, +* the value of the TickAll attribute is ignored. +*att-- +*/ +/* If non-zero put tick marks on opposite edges. Has a value of -1 when not +set yielding a default value of 1. */ +astMAKE_CLEAR(Plot,TickAll,tickall,-1) +astMAKE_GET(Plot,TickAll,int,1,(this->tickall == -1 ? 1 : this->tickall)) +astMAKE_SET(Plot,TickAll,int,tickall,( value ? 1 : 0 )) +astMAKE_TEST(Plot,TickAll,( this->tickall != -1 )) + +/* ForceExterior */ +/* ------------- */ +/* +*att+ +* Name: +* ForceExterior + +* Purpose: +* Force the use of exterior labelling? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by forcing +f coordinate grid (drawn with the AST_GRID routine) by forcing +* labels and tick marks to be drawn round the edges of the plot +* (rather than across the middle of the plot), even if there appear +* to be insufficient edge crossings to justify the use of exterior +* labelling. +* +* The default value of zero results in the decision about whether to +* use interior or exterior labelling being made purely on the basis +* of the value of the Labelling attribute. If ForceExterior is set to +* a non-zero value, then the Labelling attribute is ignored and exterior +* labelling will always be attempted, even if there appear to be +* insufficient edge labels to justify their use. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The value of this attribute is currently under investigation, and +* so this attribute prologue is currently marked as protected rather +* than public (in order to prevent it being included in the public +* documentation). +*att- +*/ +astMAKE_CLEAR(Plot,ForceExterior,forceexterior,-1) +astMAKE_GET(Plot,ForceExterior,int,0,(this->forceexterior == -1 ? 0 : this->forceexterior)) +astMAKE_SET(Plot,ForceExterior,int,forceexterior,( value ? 1 : 0 )) +astMAKE_TEST(Plot,ForceExterior,( this->forceexterior != -1 )) + +/* +*att++ +* Name: +* Border + +* Purpose: +* Draw a border around valid regions of a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether a border is drawn around regions corresponding to the +c valid physical coordinates of a Plot (c.f. astBorder). +f valid physical coordinates of a Plot (c.f. AST_BORDER). +* +* If the Border value of a Plot is non-zero, then this border will +* be drawn as part of the grid. Otherwise, the border is not drawn +* (although axis labels and tick marks will still appear, unless +* other relevant Plot attributes indicate that they should +* not). The default behaviour is to draw the border if tick marks +* and numerical labels will be drawn around the edges of the +* plotting area (see the Labelling attribute), but to omit it +* otherwise. + +* Applicability: +* Plot +* All Plots have this attribute. +*att-- +*/ +/* If non-zero draw the border. Has a value of -1 when not set, yeilding + a default of 1. */ +astMAKE_CLEAR(Plot,Border,border,-1) +astMAKE_SET(Plot,Border,int,border,( value ? 1 : 0 )) +astMAKE_TEST(Plot,Border,( this->border != -1 )) +astMAKE_GET(Plot,Border,int,1,(this->border == -1 ? 1 : this->border)) + +MAKE_SET2(Plot,Border,int,uborder,( value ? 1 : 0 )) +MAKE_GET2(Plot,Border,int,1,this->uborder) + +/* Clip */ +/* ---- */ +/* +*att++ +* Name: +* Clip + +* Purpose: +* Clip lines and/or markers at the Plot boundary? + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute controls whether curves and markers are clipped at the +* boundary of the graphics box specified when the Plot was created. A +* value of 3 implies both markers and curves are clipped at the Plot +* boundary. A value of 2 implies markers are clipped, but not curves. A +* value of 1 implies curves are clipped, but not markers. A value of +* zero implies neither curves nor markers are clipped. The default +* value is 1. Note, this attributes controls only the clipping +* performed internally within AST. The underlying graphics system may +* also apply clipping. In such cases, removing clipping using this +* attribute does not guarantee that no clipping will be visible in the +* final plot. +* +c The astClip function +f The AST_CLIP routine +* can be used to establish generalised clipping within arbitrary +* regions of the Plot. + +* Applicability: +* Plot +* All Plots have this attribute. +*att-- +*/ +astMAKE_CLEAR(Plot,Clip,clip,-1) +astMAKE_GET(Plot,Clip,int,0,(this->clip == -1 ? 1 : this->clip)) +astMAKE_TEST(Plot,Clip,( this->clip != -1 )) +astMAKE_SET(Plot,Clip,int,clip,((value>=0&&value<=3)?value:(astError( AST__ATTIN, "astSetClip(Plot): Invalid value %d supplied for Clip attribute", status, value ), this->clip))) + +/* ClipOp */ +/* ------ */ +/* +*att++ +* Name: +* ClipOp + +* Purpose: +* Combine Plot clipping limits using a boolean OR? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls how the clipping limits specified for +c each axis of a Plot (using the astClip function) are +f each axis of a Plot (using the AST_CLIP routine) are +* combined. This, in turn, determines which parts of the graphical +* output will be visible. +* +* If the ClipOp attribute of a Plot is zero (the default), +* graphical output is visible only if it satisfies the clipping +* limits on all the axes of the clipping Frame (a boolean +* AND). Otherwise, if ClipOp is non-zero, output is visible if it +* satisfies the clipping limits on one or more axes (a boolean +* OR). +* +* An important use of this attribute is to allow areas of a Plot +* to be left clear (e.g. as a background for some text). To +* achieve this, the lower and upper clipping bounds supplied to +c astClip should be reversed, and the ClipOp attribute of the +f AST_CLIP should be reversed, and the ClipOp attribute of the +* Plot should be set to a non-zero value. + +* Applicability: +* Plot +* All Plots have this attribute. +*att-- +*/ +/* If non-zero only 1axis need be within the clipping bounds to avoid a +point being clipped. Otherwise, all axes must be within bounds. */ +astMAKE_CLEAR(Plot,ClipOp,clipop,-1) +astMAKE_GET(Plot,ClipOp,int,0,(this->clipop == -1 ? 0 : this->clipop)) +astMAKE_SET(Plot,ClipOp,int,clipop,( value ? 1 : 0 )) +astMAKE_TEST(Plot,ClipOp,( this->clipop != -1 )) + +/* Grf. */ +/* ---- */ +/* +*att++ +* Name: +* Grf + +* Purpose: +c Use Grf functions registered through astGrfSet? +f Use Grf routines registered through AST_GRFSET? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +c This attribute selects the functions which are used to draw graphics by +c the Plot class. If it is zero, then the functions in the graphics +c interface selected at link-time are used (see the ast_link script). +c Otherwise, functions registered using astGrfSet are used. In this +c case, if a function is needed which has not been registered, +c then the function in the graphics interface selected at link-time is +c used. +f This attribute selects the routines which are used to draw graphics by +f the Plot class. If it is zero, then the routines in the graphics +f interface selected at link-time are used (see the ast_link script). +f Otherwise, routines registered using AST_GRFSET are used. In this +f case, if a routine is needed which has not been registered, +f then the routine in the graphics interface selected at link-time is +f used. + +* The default is to use the graphics interface + +* Applicability: +* Plot +* All Plots have this attribute. +* Plot3D +* The Plot3D class ignores this attributes, assuming a value of +* zero. + +* Notes: +* - The value of this attribute is not saved when the Plot is written +* out through a Channel to an external data store. On re-loading such +c a Plot using astRead, the attribute will be cleared, resulting in the +f a Plot using AST_READ, the attribute will be cleared, resulting in the +* graphics interface selected at link-time being used. + +*att-- +*/ +/* Use Grf routines registered using astGrfSet? Has a +value of -1 when not set yielding a default of 0. */ +astMAKE_CLEAR(Plot,Grf,grf,-1) +astMAKE_GET(Plot,Grf,int,0,(this->grf == -1 ? 0 : this->grf)) +astMAKE_SET(Plot,Grf,int,grf,( value ? 1 : 0 )) +astMAKE_TEST(Plot,Grf,( this->grf != -1 )) + +/* DrawTitle */ +/* --------- */ +/* +*att++ +* Name: +* DrawTitle + +* Purpose: +* Draw a title for a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether a title is drawn. +* +* If the DrawTitle value of a Plot is non-zero (the default), then +* the title will be drawn, otherwise it will be omitted. + +* Applicability: +* Plot +* All Plots have this attribute. +* Plot3D +* The Plot3D class ignores this attributes, assuming a value of +* zero. + +* Notes: +* - The text used for the title is obtained from the Plot's Title +* attribute. +* - The vertical placement of the title can be controlled using +* the TitleGap attribute. +*att-- +*/ +/* If non-zero add a title to the grid. Has a value of -1 when not +set yielding a default value of 1. */ +astMAKE_CLEAR(Plot,DrawTitle,drawtitle,-1) +astMAKE_GET(Plot,DrawTitle,int,1,(this->drawtitle == -1 ? 1 : this->drawtitle)) +astMAKE_SET(Plot,DrawTitle,int,drawtitle,( value ? 1 : 0 )) +astMAKE_TEST(Plot,DrawTitle,( this->drawtitle != -1 )) + +/* LabelUp. */ +/* ------- */ +/* +*att++ +* Name: +* LabelUp(axis) + +* Purpose: +* Draw numerical Plot labels upright? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether the numerical labels for each axis of a Plot should be +* drawn upright or not. It takes a separate value for each +* physical axis of a Plot so that, for instance, the setting +* "LabelUp(2)=1" specifies that numerical labels for the second +* axis should be drawn upright. +* +* If the LabelUp value of a Plot axis is non-zero, it causes +* numerical labels for that axis to be plotted upright (i.e. as +* normal, horizontal text), otherwise labels are drawn parallel to +* the axis to which they apply. +* +* The default is to produce upright labels if the labels are placed +* around the edge of the plot, and to produce labels that follow the +* axes if the labels are placed within the interior of the plot (see +* attribute Labelling). + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - In some circumstances, numerical labels and tick marks are +* drawn around the edges of the plotting area (see the Labelling +* attribute). In this case, the value of the LabelUp attribute is +* ignored. +* - If no axis is specified, (e.g. "LabelUp" instead of +* "LabelUp(2)"), then a "set" or "clear" operation will affect the +* attribute value of all the Plot axes, while a "get" or "test" +* operation will use just the LabelUp(1) value. +*att-- +*/ +/* Are numerical labels to be displayed on each axis? Has a value of -1 when +not set yielding a value of 0 (no) for both axes. */ +MAKE_CLEAR(LabelUp,labelup,-1,0) +MAKE_GET(LabelUp,int,0,( this->labelup[axis] == -1 ? 0 : this->labelup[axis] ),0) +MAKE_TEST(LabelUp,( this->labelup[axis] != -1 ),0) +MAKE_SET(LabelUp,int,labelup,( value ? 1 : 0 ),0) + +/* DrawAxes */ +/* -------- */ +/* +*att++ +* Name: +* DrawAxes(axis) + +* Purpose: +* Draw axes for a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether curves representing coordinate axes should be drawn. +* It takes a separate value for each physical axis of a +* Plot so that, for instance, the setting "DrawAxes(2)=0" +* specifies that no axis should be drawn for the second axis. +* +* If drawn, these axis lines will pass through any tick marks +* associated with numerical labels drawn to mark values on the +* axes. The location of these tick marks and labels (and hence the +* axis lines) is determined by the Plot's LabelAt(axis) attribute. +* +* If the DrawAxes value of a Plot is non-zero (the default), then +* axis lines will be drawn, otherwise they will be omitted. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - Axis lines are drawn independently of any coordinate grid +* lines (see the Grid attribute) so grid lines may be used to +* substitute for axis lines if required. +* - In some circumstances, numerical labels and tick marks are +* drawn around the edges of the plotting area (see the Labelling +* attribute). In this case, the value of the DrawAxes attribute +* is ignored. +* - If no axis is specified, (e.g. "DrawAxes" instead of +* "DrawAxes(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or +* "test" operation will use just the DrawAxes(1) value. +*att-- +*/ +/* If non-zero draw a curve through the tick marks. Has a value of -1 + when not set yielding a default value of 1. */ +MAKE_CLEAR(DrawAxes,drawaxes,-1,0) +MAKE_GET(DrawAxes,int,1,( this->drawaxes[axis] == -1 ? 1 : this->drawaxes[axis] ),0) +MAKE_TEST(DrawAxes,( this->drawaxes[axis] != -1 ),0) +MAKE_SET(DrawAxes,int,drawaxes,( value ? 1 : 0 ),0) + +/* Abbrev */ +/* -------- */ +/* +*att++ +* Name: +* Abbrev(axis) + +* Purpose: +* Abbreviate leading fields within numerical axis labels? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether matching leading fields should be removed from adjacent +* numerical axis labels. It takes a separate value for each physical +* axis of a Plot so that, for instance, the setting "Abbrev(2)=0" +* specifies that matching leading fields should not be removed on +* the second axis. +* +* If the Abbrev value of a Plot is non-zero (the default), then +* leading fields will be removed from adjacent axis labels if they +* are equal. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - If no axis is specified, (e.g. "Abbrev" instead of +* "Abbrev(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or +* "test" operation will use just the Abbrev(1) value. +*att-- +*/ +MAKE_CLEAR(Abbrev,abbrev,-1,0) +MAKE_GET(Abbrev,int,1,( this->abbrev[axis] == -1 ? 1 : this->abbrev[axis] ),0) +MAKE_TEST(Abbrev,( this->abbrev[axis] != -1 ),0) +MAKE_SET(Abbrev,int,abbrev,( value ? 1 : 0 ),0) + +/* Escape. */ +/* ------- */ +/* +*att++ +* Name: +* Escape + +* Purpose: +* Allow changes of character attributes within strings? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of text strings and numerical +c labels drawn by the astGrid and (for the Plot class) astText functions, +f labels drawn by the AST_GRID and (for the Plot class) AST_TEXT routines, +* by determining if any escape sequences contained within the strings +* should be used to control the appearance of the text, or should +* be printed literally. Note, the Plot3D class only interprets escape +* sequences within the +c astGrid function. +f AST_GRID routine. +* +* If the Escape value of a Plot is one (the default), then any +* escape sequences in text strings produce the effects described +* below when printed. Otherwise, they are printed literally. +* +c See also the astEscapes function. +f See also the AST_ESCAPES function. + +* Escape Sequences: +* Escape sequences are introduced into the text string by a percent +* "%" character. Any unrecognised, illegal or incomplete escape sequences +* are printed literally. The following escape sequences are +* currently recognised ("..." represents a string of one or more +* decimal digits): +* +* %% - Print a literal "%" character. +* +* %^...+ - Draw subsequent characters as super-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the super-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* %^+ - Draw subsequent characters with the normal base-line. +* +* %v...+ - Draw subsequent characters as sub-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the sub-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* +* %v+ - Draw subsequent characters with the normal base-line +* (equivalent to %^+). +* +* %>...+ - Leave a gap before drawing subsequent characters. +* The digits "..." give the size of the gap, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* +* %<...+ - Move backwards before drawing subsequent characters. +* The digits "..." give the size of the movement, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* +* %s...+ - Change the Size attribute for subsequent characters. The +* digits "..." give the new Size as a fraction of the +* "normal" Size, scaled so that a value of "100" corresponds +* to 1.0; +* +* %s+ - Reset the Size attribute to its "normal" value. +* +* %w...+ - Change the Width attribute for subsequent characters. The +* digits "..." give the new width as a fraction of the +* "normal" Width, scaled so that a value of "100" corresponds +* to 1.0; +* +* %w+ - Reset the Size attribute to its "normal" value. +* +* %f...+ - Change the Font attribute for subsequent characters. The +* digits "..." give the new Font value. +* +* %f+ - Reset the Font attribute to its "normal" value. +* +* %c...+ - Change the Colour attribute for subsequent characters. The +* digits "..." give the new Colour value. +* +* %c+ - Reset the Colour attribute to its "normal" value. +* +* %t...+ - Change the Style attribute for subsequent characters. The +* digits "..." give the new Style value. +* +* %t+ - Reset the Style attribute to its "normal" value. +* +* %h+ - Remember the current horizontal position (see "%g+") +* +* %g+ - Go to the horizontal position of the previous "%h+" (if any). +* +* %- - Push the current graphics attribute values onto the top of +* the stack (see "%+"). +* +* %+ - Pop attributes values of the top the stack (see "%-"). If +* the stack is empty, "normal" attribute values are restored. + +* Applicability: +* Plot +* All Plots have this attribute. +*att-- +*/ + +/* Has a value of -1 when not set yeilding a default of 1. */ +astMAKE_GET(Plot,Escape,int,1,(this->escape == -1 ? 1 : this->escape)) +astMAKE_CLEAR(Plot,Escape,escape,-1) +astMAKE_SET(Plot,Escape,int,escape,( value ? 1 : 0 )) +astMAKE_TEST(Plot,Escape,( this->escape != -1 )) + +/* LabelAt(axis). */ +/* -------------- */ +/* +*att++ +* Name: +* LabelAt(axis) + +* Purpose: +* Where to place numerical labels for a Plot + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* where numerical axis labels and associated tick marks are +* placed. It takes a separate value for each physical axis of a +* Plot so that, for instance, the setting "LabelAt(2)=10.0" +* specifies where the numerical labels and tick marks for the +* second axis should be drawn. +* +* For each axis, the LabelAt value gives the value on the other +* axis at which numerical labels and tick marks should be placed +c (remember that Plots suitable for use with astGrid may only +f (remember that Plots suitable for use with AST_GRID may only +* have two axes). For example, in a celestial (RA,Dec) coordinate +* system, LabelAt(1) gives a Dec value which defines a line (of +* constant Dec) along which the numerical RA labels and their +* associated tick marks will be drawn. Similarly, LabelAt(2) gives +* the RA value at which the Dec labels and ticks will be drawn. +* +* The default bahaviour is for the Plot to generate its own +* position for numerical labels and tick marks. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The LabelAt value should use the same units as are used +* internally for storing coordinate values on the appropriate +* axis. For example, with a celestial coordinate system, the +* LabelAt value should be in radians, not hours or degrees. +* - Normally, the LabelAt value also determines where the lines +* representing coordinate axes will be drawn, so that the tick +* marks will lie on these lines (but also see the DrawAxes +* attribute). +* - In some circumstances, numerical labels and tick marks are +* drawn around the edges of the plotting area (see the Labelling +* attribute). In this case, the value of the LabelAt attribute is +* ignored. +*att-- +*/ +/* The constant value on the other axis at which to place labels for + each axis. */ +MAKE_CLEAR(LabelAt,labelat,AST__BAD,0) +MAKE_GET(LabelAt,double,AST__BAD,this->labelat[axis],0) +MAKE_SET(LabelAt,double,labelat,value,0) +MAKE_TEST(LabelAt,( this->labelat[axis] != AST__BAD ),0) + +MAKE_GET3(LabelAt,double,AST__BAD,this->ulblat[axis],0) +MAKE_SET3(LabelAt,double,ulblat,value,0) + +/* Centre(axis). */ +/* ------------ */ +/* A value at which to place a tick mark. Other ticks marks are spaced at +regular distances from this one. AST__BAD is stored if no value is supplied, +resulting in Plot choosing its own value. */ +MAKE_CLEAR(Centre,centre,AST__BAD,0) +MAKE_GET(Centre,double,AST__BAD,this->centre[axis],0) +MAKE_SET(Centre,double,centre,value,0) +MAKE_TEST(Centre,( this->centre[axis] != AST__BAD ),0) + +MAKE_GET3(Centre,double,AST__BAD,this->ucentre[axis],0) +MAKE_SET3(Centre,double,ucentre,value,0) + +/* Ink */ +/* --- */ +/* A protected attribute indicating if astGrid should draw using visible +ink. The unset value is -1, yeilding a default value of 1. */ +astMAKE_CLEAR(Plot,Ink,ink,-1) +astMAKE_GET(Plot,Ink,int,1,(this->ink == -1 ? 1 : this->ink)) +astMAKE_SET(Plot,Ink,int,ink,( value ? 1 : 0 )) +astMAKE_TEST(Plot,Ink,( this->ink != -1 )) + +/* Gap(axis). */ +/* ---------- */ +/* +*att++ +* Name: +* Gap(axis) + +* Purpose: +* Interval between linearly spaced major axis values of a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* the linear interval between the "major" axis values of a Plot, at +* which (for example) major tick marks are drawn. It takes a separate +* value for each physical axis of the Plot so that, for instance, +* the setting "Gap(2)=3.0" specifies the difference between adjacent major +* values along the second axis. The Gap attribute is only used when +* the LogTicks attribute indicates that the spacing between major axis +* values is to be linear. If major axis values are logarithmically spaced +* then the gap is specified using attribute LogGap. +* +* The Gap value supplied will usually be rounded to the nearest +* "nice" value, suitable (e.g.) for generating axis labels, before +* use. To avoid this "nicing" you should set an explicit format +* for the axis using the Format(axis) or Digits/Digits(axis) +* attribute. The default behaviour is for the Plot to generate its +* own Gap value when required, based on the range of axis values +* to be represented. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The Gap value should use the same units as are used internally +* for storing coordinate values on the corresponding axis. For +* example, with a celestial coordinate system, the Gap value +* should be in radians, not hours or degrees. +* - If no axis is specified, (e.g. "Gap" instead of "Gap(2)"), +* then a "set" or "clear" operation will affect the attribute +* value of all the Plot axes, while a "get" or "test" operation +* will use just the Gap(1) value. +*att-- +*/ +/* The gap between tick marks on each axis. AST__BAD is stored if no +value has been supplied, resulting in default values being found. */ +MAKE_CLEAR(Gap,gap,AST__BAD,0) +MAKE_SET(Gap,double,gap,value,0) +MAKE_TEST(Gap,( this->gap[axis] != AST__BAD ),0) +MAKE_GET(Gap,double,AST__BAD,this->gap[axis],0) + +MAKE_SET3(Gap,double,ugap,value,0) +MAKE_GET3(Gap,double,AST__BAD,this->ugap[axis],0) + +/* LogGap(axis). */ +/* ---------- */ +/* +*att++ +* Name: +* LogGap(axis) + +* Purpose: +* Interval between major axis values of a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* the logarithmic interval between the "major" axis values of a Plot, at +* which (for example) major tick marks are drawn. It takes a separate +* value for each physical axis of the Plot so that, for instance, +* the setting "LogGap(2)=100.0" specifies the ratio between adjacent major +* values along the second axis. The LogGap attribute is only used when +* the LogTicks attribute indicates that the spacing between major axis +* values is to be logarithmic. If major axis values are linearly spaced +* then the gap is specified using attribute Gap. +* +* The LogGap value supplied will be rounded to the nearest power of 10. +* The reciprocal of the supplied value may be used if this is necessary +* to produce usable major axis values. If a zero or negative value is +* supplied, an error will be reported when the grid is drawn. The default +* behaviour is for the Plot to generate its own LogGap value when +* required, based on the range of axis values to be represented. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The LogGap value is a ratio between axis values and is therefore +* dimensionless. +* - If no axis is specified, (e.g. "LogGap" instead of "LogGap(2)"), +* then a "set" or "clear" operation will affect the attribute +* value of all the Plot axes, while a "get" or "test" operation +* will use just the LogGap(1) value. +*att-- +*/ +/* The logarithmic gap between tick marks on each axis. AST__BAD is stored if + no value has been supplied, resulting in default values being found. */ +MAKE_CLEAR(LogGap,loggap,AST__BAD,0) +MAKE_SET(LogGap,double,loggap,value,0) +MAKE_TEST(LogGap,( this->loggap[axis] != AST__BAD ),0) +MAKE_GET(LogGap,double,AST__BAD,this->loggap[axis],0) + +MAKE_SET3(LogGap,double,uloggap,value,0) +MAKE_GET3(LogGap,double,AST__BAD,this->uloggap[axis],0) + +/* LogPlot. */ +/* ------- */ +/* +*att++ +* Name: +* LogPlot(axis) + +* Purpose: +* Map the plot logarithmically onto the screen? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of all graphics produced by +* the Plot, by determining whether the axes of the plotting surface +* are mapped logarithmically or linearly onto the base Frame of the +* FrameSet supplied when the Plot was constructed. It takes a separate +* value for each axis of the graphics coordinate system (i.e. the +* base Frame in the Plot) so that, for instance, the setting +* "LogPlot(2)=1" specifies that the second axis of the graphics +* coordinate system (usually the vertical axis) should be mapped +* logarithmically onto the second axis of the base Frame of the +* FrameSet supplied when the Plot was constructed. +* +* If the LogPlot value of a Plot axis is non-zero, it causes that +* axis to be mapped logarithmically, otherwise (the default) the axis +* is mapped linearly. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The setting of the LogPlot attribute provides the default value +* for the related LogTicks attribute. By selecting suitable values for +* LogPlot and LogTicks, it is possible to have tick marks which are evenly +* spaced in value but which are mapped logarithmically onto the screen +* (and vice-versa). +* - An axis may only be mapped logarithmically if the visible part of +* the axis does not include the value zero. The visible part of the +* axis is that part which is mapped onto the plotting area, and is +* measured within the base Frame of the FrameSet which was supplied when +* the Plot was constructed. Any attempt to set LogPlot to a non-zero value +* will be ignored (without error) if the visible part of the axis +* includes the value zero +* - If no axis is specified, (e.g. "LogPlot" instead of +* "LogPlot(2)"), then a "set" or "clear" operation will affect the +* attribute value of all the Plot axes, while a "get" or "test" +* operation will use just the LogPlot(1) value. +*att-- +*/ +/* Are plot axes to be mapped logarithmically? Has a value of -1 when +not set yielding a value of 0 (no) for both axes. */ +MAKE_GET(LogPlot,int,0,( this->logplot[axis] == -1 ? 0 : this->logplot[axis] ),0) +MAKE_TEST(LogPlot,( this->logplot[axis] != -1 ),0) + +/* LogTicks. */ +/* ------- */ +/* +*att++ +* Name: +* LogTicks(axis) + +* Purpose: +* Space the major tick marks logarithmically? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether the major tick marks should be spaced logarithmically or +* linearly in axis value. It takes a separate value for each physical +* axis of the Plot so that, for instance, the setting "LogTicks(2)=1" +* specifies that the major tick marks on the second axis should be +* spaced logarithmically. +* +* If the LogTicks value for a physical axis is non-zero, the major +* tick marks on that axis will be spaced logarithmically (that is, +* there will be a constant ratio between the axis values at adjacent +* major tick marks). An error will be reported if the dynamic range of +* the axis (the ratio of the largest to smallest displayed axis value) +* is less than 10.0. If the LogTicks value is zero, the major tick marks +* will be evenly spaced (that is, there will be a constant difference +* between the axis values at adjacent major tick marks). The default is +* to produce logarithmically spaced tick marks if the corresponding +* LogPlot attribute is non-zero and the ratio of maximum axis value +* to minimum axis value is 100 or more. If either of these conditions +* is not met, the default is to produce linearly spaced tick marks. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The setting of the LogTicks attribute does not affect the mapping +* of the plot onto the screen, which is controlled by attribute LogPlot. +* By selecting suitable values for LogPlot and LogTicks, it is possible to +* have tick marks which are evenly spaced in value but which are mapped +* logarithmically onto the screen (and vica-versa). +* - An error will be reported when drawing an annotated axis grid if +* the visible part of the physical axis includes the value zero. +* - If no axis is specified, (e.g. "LogTicks" instead of +* "LogTicks(2)"), then a "set" or "clear" operation will affect the +* attribute value of all the Plot axes, while a "get" or "test" +* operation will use just the LogTicks(1) value. +*att-- +*/ +/* Are ticksto be spaced logarithmically? Has a value of -1 when + not set, yeielding a default value equal to the corresponding + LogPlot value. */ +MAKE_CLEAR(LogTicks,logticks,-1,0) +MAKE_GET(LogTicks,int,0,( this->logticks[axis] == -1 ? astGetLogPlot(this,axis) : this->logticks[axis] ),0) +MAKE_TEST(LogTicks,( this->logticks[axis] != -1 ),0) +MAKE_SET(LogTicks,int,logticks,( value ? 1 : 0 ),0) + +MAKE_SET3(LogTicks,int,ulgtk,value,0) +MAKE_GET3(LogTicks,int,0,this->ulgtk[axis],0) + +/* LogLabel. */ +/* -------- */ +/* +*att++ +* Name: +* LogLabel(axis) + +* Purpose: +* Use exponential format for numerical axis labels? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether the numerical axis labels should be in normal decimal form +* or should be represented as 10 raised to the appropriate power. +* That is, an axis value of 1000.0 will be drawn as "1000.0" if +* LogLabel is zero, but as "10^3" if LogLabel is non-zero. If +* graphical escape sequences are supported (see attribute Escape), +* the power in such exponential labels will be drawn as a small +* superscript instead of using a "^" character to represent +* exponentiation. +* +* The default is to produce exponential labels if the major tick +* marks are logarithmically spaced (see the LogTicks attribute). + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - If no axis is specified, (e.g. "LogLabel" instead of +* "LogLabel(2)"), then a "set" or "clear" operation will affect the +* attribute value of all the Plot axes, while a "get" or "test" +* operation will use just the LogLabel(1) value. +*att-- +*/ +/* Are labels to be drawn as 10**x? Has a value of -1 when not set, yeielding + a default value equal to the corresponding LogTicks value. */ +MAKE_CLEAR(LogLabel,loglabel,-1,0) +MAKE_GET(LogLabel,int,0,( this->loglabel[axis] == -1 ? astGetLogTicks(this,axis) : this->loglabel[axis] ),0) +MAKE_TEST(LogLabel,( this->loglabel[axis] != -1 ),0) +MAKE_SET(LogLabel,int,loglabel,( value ? 1 : 0 ),0) + +MAKE_SET3(LogLabel,int,ulglb,value,0) +MAKE_GET3(LogLabel,int,0,this->ulglb[axis],0) + +/* MajTickLen. */ +/* ----------- */ +/* +*att++ +* Name: +* MajTickLen(axis) + +* Purpose: +* Length of major tick marks for a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* the length of the major tick marks drawn on the axes of a Plot. +* It takes a separate value for each physical axis of the Plot so +* that, for instance, the setting "MajTickLen(2)=0" specifies the +* length of the major tick marks drawn on the second axis. +* +* The MajTickLen value should be given as a fraction of the +* minimum dimension of the plotting area. Negative values cause +* major tick marks to be placed on the outside of the +* corresponding grid line or axis (but subject to any clipping +* imposed by the underlying graphics system), while positive +* values cause them to be placed on the inside. +* +* The default behaviour depends on whether a coordinate grid is +* drawn inside the plotting area (see the Grid attribute). If so, +* the default MajTickLen value is zero (so that major ticks are +* not drawn), otherwise the default is +0.015. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - If no axis is specified, (e.g. "MajTickLen" instead of +* "MajTickLen(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or "test" +* operation will use just the MajTickLen(1) value. + +*att-- +*/ +/* Fractional length of major tick marks. Has a value of AST__BAD when not +set yielding a default value of 0.015. */ +MAKE_CLEAR(MajTickLen,majticklen,AST__BAD,0) +MAKE_SET(MajTickLen,double,majticklen,value,0) +MAKE_TEST(MajTickLen,( this->majticklen[axis] != AST__BAD ),0) +MAKE_GET(MajTickLen,double,0.0,( this->majticklen[axis] == AST__BAD ? 0.015 : this->majticklen[axis]),0) + +MAKE_SET3(MajTickLen,double,umjtkln,value,0) +MAKE_GET3(MajTickLen,double,0.0,this->umjtkln[axis],0) + +/* TitleGap. */ +/* --------- */ +/* +*att++ +* Name: +* TitleGap + +* Purpose: +* Vertical spacing for a Plot title. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* where the title of a Plot is drawn. +* +* Its value gives the spacing between the bottom edge of the title +* and the top edge of a bounding box containing all the other parts +* of the annotated grid. Positive values cause the title to be +* drawn outside the box, while negative values cause it to be drawn +* inside. +* +* The TitleGap value should be given as a fraction of the minimum +* dimension of the plotting area, the default value being +0.05. + +* Applicability: +* Plot +* All Plots have this attribute. +* Plot3D +* The Plot3D class ignores this attributes since it does not draw +* a Title. + +* Notes: +* - The text used for the title is obtained from the Plot's Title +* attribute. +*att-- +*/ +/* Fractional gap between titile and top edge. Has a value of AST__BAD when +not set yielding a default value of 0.05. */ +astMAKE_CLEAR(Plot,TitleGap,titlegap,AST__BAD) +astMAKE_GET(Plot,TitleGap,double,0.0,( this->titlegap == AST__BAD ? 0.05 : this->titlegap)) +astMAKE_SET(Plot,TitleGap,double,titlegap,value) +astMAKE_TEST(Plot,TitleGap,( this->titlegap != AST__BAD )) + +/* MinTickLen. */ +/* ----------- */ +/* +*att++ +* Name: +* MinTickLen(axis) + +* Purpose: +* Length of minor tick marks for a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* the length of the minor tick marks drawn on the axes of a Plot. +* It takes a separate value for each physical axis of the Plot so +* that, for instance, the setting "MinTickLen(2)=0" specifies the +* length of the minor tick marks drawn on the second axis. +* +* The MinTickLen value should be given as a fraction of the +* minimum dimension of the plotting area. Negative values cause +* minor tick marks to be placed on the outside of the +* corresponding grid line or axis (but subject to any clipping +* imposed by the underlying graphics system), while positive +* values cause them to be placed on the inside. +* +* The default value is +0.007. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The number of minor tick marks drawn is determined by the +* Plot's MinTick(axis) attribute. +* - If no axis is specified, (e.g. "MinTickLen" instead of +* "MinTickLen(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or "test" +* operation will use just the MinTickLen(1) value. + +*att-- +*/ +/* Fractional length of minor tick marks. Has a value of AST__BAD when not +set yielding a default value of 0.007. */ +MAKE_CLEAR(MinTickLen,minticklen,AST__BAD,0) +MAKE_SET(MinTickLen,double,minticklen,value,0) +MAKE_TEST(MinTickLen,( this->minticklen[axis] != AST__BAD ),0) +MAKE_GET(MinTickLen,double,0.0,( this->minticklen[axis] == AST__BAD ? 0.007 : this->minticklen[axis]),0) + +/* Labelling. */ +/* ---------- */ +/* +*att++ +* Name: +* Labelling + +* Purpose: +* Label and tick placement option for a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* the strategy for placing numerical labels and tick marks for a Plot. +* +* If the Labelling value of a Plot is "exterior" (the default), then +* numerical labels and their associated tick marks are placed +* around the edges of the plotting area, if possible. If this is +* not possible, or if the Labelling value is "interior", then they +* are placed along grid lines inside the plotting area. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The LabelAt(axis) attribute may be used to determine the exact +* placement of labels and tick marks that are drawn inside the +* plotting area. +*att-- +*/ +astMAKE_CLEAR(Plot,Labelling,labelling,-9999) +astMAKE_SET(Plot,Labelling,int,labelling,(value?1:0)) +astMAKE_TEST(Plot,Labelling,( this->labelling != -9999 )) +astMAKE_GET(Plot,Labelling,int,0,(this->labelling == -9999 ? 0 : this->labelling)) + +MAKE_SET2(Plot,Labelling,int,ulbling,(value?1:0)) +MAKE_GET2(Plot,Labelling,int,0,this->ulbling) + +/* Edge. */ +/* ----- */ +/* +*att++ +* Name: +* Edge(axis) + +* Purpose: +* Which edges to label in a Plot + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* which edges of a Plot are used for displaying numerical and +* descriptive axis labels. It takes a separate value for each +* physical axis of the Plot so that, for instance, the setting +* "Edge(2)=left" specifies which edge to use to display labels for +* the second axis. +* +* The values "left", "top", "right" and "bottom" (or any +* abbreviation) can be supplied for this attribute. The default is +* usually "bottom" for the first axis and "left" for the second +* axis. However, if exterior labelling was requested (see the +* Labelling attribute) but cannot be produced using these default +* Edge values, then the default values will be swapped if this +* enables exterior labelling to be produced. + +* Applicability: +* Plot +* All Plots have this attribute. +* Plot3D +* The Plot3D class ignores this attributes. Instead it uses its +* own RootCorner attribute to determine which edges of the 3D plot +* to label. + +* Notes: +* - In some circumstances, numerical labels will be drawn along +* internal grid lines instead of at the edges of the plotting area +* (see the Labelling attribute). In this case, the Edge attribute +* only affects the placement of the descriptive labels (these are +* drawn at the edges of the plotting area, rather than along the +* axis lines). +*att-- +*/ +/* The edges of the plotting area on which to place numerical labels + for axes 0 and 1. Has a value of -1 when not set yielding a value + of 3 (the bottom edge) for axis 0 and 0 (the left-hand edge) for + axis 1. */ +MAKE_CLEAR(Edge,edge,-1,0) +MAKE_GET(Edge,int,0,( this->edge[axis] == -1 ? (axis?LEFT:BOTTOM) : this->edge[axis] ),0) +MAKE_SET(Edge,int,edge,(abs( value % 4 )),0) +MAKE_TEST(Edge,( this->edge[axis] != -1 ),0) + +MAKE_GET3(Edge,int,0,this->uedge[axis],0) +MAKE_SET3(Edge,int,uedge,(abs( value % 4 )),0) + +/* NumLab. */ +/* -------- */ +/* +*att++ +* Name: +* NumLab(axis) + +* Purpose: +* Draw numerical axis labels for a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether labels should be drawn to represent the numerical values +* along each axis of a Plot. It takes a separate value for each +* physical axis of a Plot so that, for instance, the setting +* "NumLab(2)=1" specifies that numerical labels should be drawn +* for the second axis. +* +* If the NumLab value of a Plot axis is non-zero (the default), +* then numerical labels will be drawn for that axis, otherwise +* they will be omitted. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The drawing of associated descriptive axis labels for a Plot +* (describing the quantity being plotted along each axis) is +* controlled by the TextLab(axis) attribute. +* - If no axis is specified, (e.g. "NumLab" instead of +* "NumLab(2)"), then a "set" or "clear" operation will affect the +* attribute value of all the Plot axes, while a "get" or "test" +* operation will use just the NumLab(1) value. +*att-- +*/ +/* Are numerical labels to be displayed on each axis? Has a value of + -1 when not set yielding a value of 1 (yes) for both axes. */ +MAKE_CLEAR(NumLab,numlab,-1,0) +MAKE_GET(NumLab,int,1,( this->numlab[axis] == -1 ? 1 : this->numlab[axis] ),0) +MAKE_TEST(NumLab,( this->numlab[axis] != -1 ),0) +MAKE_SET(NumLab,int,numlab,( value ? 1 : 0 ),0) + +/* NumLabGap. */ +/* --------- */ +/* +*att++ +* Name: +* NumLabGap(axis) + +* Purpose: +* Spacing of numerical labels for a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* where numerical axis labels are placed relative to the axes they +* describe. It takes a separate value for each physical axis of a +* Plot so that, for instance, the setting "NumLabGap(2)=-0.01" +* specifies where the numerical label for the second axis should +* be drawn. +* +* For each axis, the NumLabGap value gives the spacing between the +* axis line (or edge of the plotting area, if appropriate) and the +* nearest edge of the corresponding numerical axis +* labels. Positive values cause the descriptive label to be placed +* on the opposite side of the line to the default tick marks, +* while negative values cause it to be placed on the same side. +* +* The NumLabGap value should be given as a fraction of the minimum +* dimension of the plotting area, the default value being +0.01. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - If no axis is specified, (e.g. "NumLabGap" instead of +* "NumLabGap(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or +* "test" operation will use just the NumLabGap(1) value. +*att-- +*/ +/* Fractional spacing between numeric labels and axes. Has a value of AST__BAD +when not set yielding a default value of 0.01. */ +MAKE_CLEAR(NumLabGap,numlabgap,AST__BAD,0) +MAKE_GET(NumLabGap,double,0.0,( this->numlabgap[ axis ] == AST__BAD ? 0.01 : this->numlabgap[axis]),0) +MAKE_SET(NumLabGap,double,numlabgap,value,0) +MAKE_TEST(NumLabGap,( this->numlabgap[axis] != AST__BAD ),0) + +/* MinTick. */ +/* -------- */ +/* +*att++ +* Name: +* MinTick(axis) + +* Purpose: +* Density of minor tick marks for a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* the density of minor tick marks which appear between the major +* axis values of a Plot. It takes a separate value for each +* physical axis of a Plot so that, for instance, the setting +* "MinTick(2)=2" specifies the density of minor tick marks along +* the second axis. +* +* The value supplied should be the number of minor divisions +* required between each pair of major axis values, this being one +* more than the number of minor tick marks to be drawn. By +* default, a value is chosen that depends on the gap between major +* axis values and the nature of the axis. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - If no axis is specified, (e.g. "MinTick" instead of +* "MinTick(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or +* "test" operation will use just the MinTick(1) value. +*att-- +*/ +/* How many divisions are there between major tick marks? Has a value +of -1 when not set yielding a value of 1 for both axes. */ +MAKE_CLEAR(MinTick,mintick,-1,0) +MAKE_GET(MinTick,int,1,( this->mintick[axis] == -1 ? 1 : this->mintick[axis] ),0) +MAKE_TEST(MinTick,( this->mintick[axis] != -1 ),0) +MAKE_SET(MinTick,int,mintick,( (value < 1)? 1 : value ),0) + +MAKE_GET3(MinTick,int,1,this->umintk[axis],0) +MAKE_SET3(MinTick,int,umintk,( (value < 1)? 1 : value ),0) + +/* TextLab. */ +/* --------- */ +/* +*att++ +* Name: +* TextLab(axis) + +* Purpose: +* Draw descriptive axis labels for a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether textual labels should be drawn to describe the quantity +* being represented on each axis of a Plot. It takes a separate +* value for each physical axis of a Plot so that, for instance, +* the setting "TextLab(2)=1" specifies that descriptive labels +* should be drawn for the second axis. +* +* If the TextLab value of a Plot axis is non-zero, then +* descriptive labels will be drawn for that axis, otherwise they +* will be omitted. The default behaviour is to draw descriptive +* labels if tick marks and numerical labels are being drawn around +* the edges of the plotting area (see the Labelling attribute), +* but to omit them otherwise. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The text used for the descriptive labels is derived from the +* Plot's Label(axis) attribute, together with its Unit(axis) +* attribute if appropriate (see the LabelUnits(axis) attribute). +* - The drawing of numerical axis labels for a Plot (which +* indicate values on the axis) is controlled by the NumLab(axis) +* attribute. +* - If no axis is specified, (e.g. "TextLab" instead of +* "TextLab(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or +* "test" operation will use just the TextLab(1) value. +*att-- +*/ +/* Are textual labels to be displayed on each axis? Has a value of -1 + when not set yielding a value of 1 (yes) for both axes. */ +MAKE_CLEAR(TextLab,textlab,-1,0) +MAKE_GET(TextLab,int,1,( this->textlab[axis] == -1 ? 1 : this->textlab[axis] ),0) +MAKE_TEST(TextLab,( this->textlab[axis] != -1 ),0) +MAKE_SET(TextLab,int,textlab,( value ? 1 : 0 ),0) + +MAKE_GET3(TextLab,int,1,this->utxtlb[axis],0) +MAKE_SET3(TextLab,int,utxtlb,( value ? 1 : 0 ),0) + +/* TextLabGap. */ +/* ----------- */ +/* +*att++ +* Name: +* TextLabGap(axis) + +* Purpose: +* Spacing of descriptive axis labels for a Plot. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* where descriptive axis labels are placed relative to the axes they +* describe. It takes a separate value for each physical axis of a +* Plot so that, for instance, the setting "TextLabGap(2)=0.01" +* specifies where the descriptive label for the second axis should +* be drawn. +* +* For each axis, the TextLabGap value gives the spacing between the +* descriptive label and the edge of a box enclosing all other parts +* of the annotated grid (excluding other descriptive labels). The gap +* is measured to the nearest edge of the label (i.e. the top or the +* bottom). Positive values cause the descriptive label to be placed +* outside the bounding box, while negative values cause it to be placed +* inside. +* +* The TextLabGap value should be given as a fraction of the minimum +* dimension of the plotting area, the default value being +0.01. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - If drawn, descriptive labels are always placed at the edges of +* the plotting area, even although the corresponding numerical +* labels may be drawn along axis lines in the interior of the +* plotting area (see the Labelling attribute). +* - If no axis is specified, (e.g. "TextLabGap" instead of +* "TextLabGap(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or +* "test" operation will use just the TextLabGap(1) value. +*att-- +*/ +/* Fractional spacing between numeric labels and axes. Has a value of AST__BAD +when not set yielding a default value of 0.01. */ +MAKE_CLEAR(TextLabGap,textlabgap,AST__BAD,0) +MAKE_GET(TextLabGap,double,0.0,( this->textlabgap[ axis ] == AST__BAD ? 0.01 : this->textlabgap[axis]),0) +MAKE_SET(TextLabGap,double,textlabgap,value,0) +MAKE_TEST(TextLabGap,( this->textlabgap[axis] != AST__BAD ),0) + +/* LabelUnits. */ +/* ----------- */ +/* +*att++ +* Name: +* LabelUnits(axis) + +* Purpose: +* Use axis unit descriptions in a Plot? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* whether the descriptive labels drawn for each axis of a Plot +* should include a description of the units being used on the +* axis. It takes a separate value for each physical axis of a +* Plot so that, for instance, the setting "LabelUnits(2)=1" +* specifies that a unit description should be included in the +* label for the second axis. +* +* If the LabelUnits value of a Plot axis is non-zero, a unit +* description will be included in the descriptive label for that +* axis, otherwise it will be omitted. The default behaviour is to +* include a unit description unless the current Frame of the Plot +* is a SkyFrame representing equatorial, ecliptic, galactic or +* supergalactic coordinates, in which case it is omitted. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - The text used for the unit description is obtained from the +* Plot's Unit(axis) attribute. +* - If no axis is specified, (e.g. "LabelUnits" instead of +* "LabelUnits(2)"), then a "set" or "clear" operation will affect +* the attribute value of all the Plot axes, while a "get" or +* "test" operation will use just the LabelUnits(1) value. +* - If the current Frame of the Plot is not a SkyFrame, but includes +* axes which were extracted from a SkyFrame, then the default behaviour +* is to include a unit description only for those axes which were not +* extracted from a SkyFrame. +*att-- +*/ +/* Are textual labels to include a string describing the axis units? Has a +value of -1 when not set yielding a default of 1. */ +MAKE_CLEAR(LabelUnits,labelunits,-1,0) +MAKE_TEST(LabelUnits,( this->labelunits[axis] != -1 ),0) +MAKE_SET(LabelUnits,int,labelunits,( value ? 1 : 0 ),0) + +MAKE_GET3(LabelUnits,int,1,this->ulbunit[axis],0) +MAKE_SET3(LabelUnits,int,ulbunit,( value ? 1 : 0 ),0) + +/* Style. */ +/* ------ */ +/* +*att++ +* Name: +* Style(element) + +* Purpose: +* Line style for a Plot element. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute determines the line style used when drawing each +* element of graphical output produced by a Plot. It takes a +* separate value for each graphical element so that, for instance, +* the setting "Style(border)=2" causes the Plot border to be drawn +* using line style 2 (which might result in, say, a dashed line). +* +* The range of integer line styles available and their appearance +* is determined by the underlying graphics system. The default +* behaviour is for all graphical elements to be drawn using the +* default line style supplied by this graphics system (normally, +* this is likely to give a solid line). + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - For a list of the graphical elements available, see the +* description of the Plot class. +* - If no graphical element is specified, (e.g. "Style" instead of +* "Style(border)"), then a "set" or "clear" operation will affect +* the attribute value of all graphical elements, while a "get" or +* "test" operation will use just the Style(Border) value. +*att-- +*/ +/* Line styles. Has a value of -1 when not set yielding a default of 1. */ +MAKE_CLEAR(Style,style,-1,AST__NPID) +MAKE_GET(Style,int,1,( this->style[axis] == -1 ? 1 : this->style[axis] ),AST__NPID) +MAKE_TEST(Style,( this->style[axis] != -1 ),AST__NPID) +MAKE_SET(Style,int,style,value,AST__NPID) + +/* Font. */ +/* ----- */ +/* +*att++ +* Name: +* Font(element) + +* Purpose: +* Character font for a Plot element. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute determines the character font index used when +* drawing each element of graphical output produced by a Plot. It +* takes a separate value for each graphical element so that, for +* instance, the setting "Font(title)=2" causes the Plot title to +* be drawn using font number 2. +* +* The range of integer font indices available and the appearance +* of the resulting text is determined by the underlying graphics +* system. The default behaviour is for all graphical elements to +* be drawn using the default font supplied by this graphics +* system. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - For a list of the graphical elements available, see the +* description of the Plot class. +* - If no graphical element is specified, (e.g. "Font" instead +* of "Font(title)"), then a "set" or "clear" operation will +* affect the attribute value of all graphical elements, while a +* "get" or "test" operation will use just the Font(TextLab) +* value. +*att-- +*/ +/* Character fonts. Has a value of -1 when not set yielding a default of 1. */ +MAKE_CLEAR(Font,font,-1,AST__NPID) +MAKE_GET(Font,int,1,( this->font[axis] == -1 ? 1 : this->font[axis] ),AST__NPID) +MAKE_TEST(Font,( this->font[axis] != -1 ),AST__NPID) +MAKE_SET(Font,int,font,value,AST__NPID) + +/* Colour. */ +/* ------- */ +/* +*att++ +* Name: +* Colour(element) + +* Purpose: +* Colour index for a Plot element. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute determines the colour index used when drawing +* each element of graphical output produced by a Plot. It takes a +* separate value for each graphical element so that, for instance, +* the setting "Colour(title)=2" causes the Plot title to be drawn +* using colour index 2. The synonym "Color" may also be used. +* +* The range of integer colour indices available and their +* appearance is determined by the underlying graphics system. The +* default behaviour is for all graphical elements to be drawn +* using the default colour index supplied by this graphics system +* (normally, this is likely to result in white plotting on a black +* background, or vice versa). +d +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - For a list of the graphical elements available, see the +* description of the Plot class. +* - If no graphical element is specified, (e.g. "Colour" instead +* of "Colour(title)"), then a "set" or "clear" operation will +* affect the attribute value of all graphical elements, while a +* "get" or "test" operation will use just the Colour(TextLab) +* value. +*att-- +*/ +/* Colours. Has a value of -1 when not set yielding a default of 1. */ +MAKE_CLEAR(Colour,colour,-1,AST__NPID) +MAKE_GET(Colour,int,1,( this->colour[axis] == -1 ? 1 : this->colour[axis] ),AST__NPID) +MAKE_TEST(Colour,( this->colour[axis] != -1 ),AST__NPID) +MAKE_SET(Colour,int,colour,value,AST__NPID) + +/* Width. */ +/* ------ */ +/* +*att++ +* Name: +* Width(element) + +* Purpose: +* Line width for a Plot element. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute determines the line width used when drawing each +* element of graphical output produced by a Plot. It takes a +* separate value for each graphical element so that, for instance, +* the setting "Width(border)=2.0" causes the Plot border to be +* drawn using a line width of 2.0. A value of 1.0 results in a +* line thickness which is approximately 0.0005 times the length of +* the diagonal of the entire display surface. +* +* The actual appearance of lines drawn with any particular width, +* and the range of available widths, is determined by the +* underlying graphics system. The default behaviour is for all +* graphical elements to be drawn using the default line width +* supplied by this graphics system. This will not necessarily +* correspond to a Width value of 1.0. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - For a list of the graphical elements available, see the +* description of the Plot class. +* - If no graphical element is specified, (e.g. "Width" instead of +* "Width(border)"), then a "set" or "clear" operation will affect +* the attribute value of all graphical elements, while a "get" or +* "test" operation will use just the Width(Border) value. +*att-- +*/ +/* Line widths. Has a value of AST__BAD when not set yielding a + default of 1.0. */ +MAKE_CLEAR(Width,width,AST__BAD,AST__NPID) +MAKE_GET(Width,double,1.0,( this->width[axis] == AST__BAD ? 1.0 : this->width[axis] ),AST__NPID) +MAKE_TEST(Width,( this->width[axis] != AST__BAD ),AST__NPID) +MAKE_SET(Width,double,width,(value!=0.00)?value:(astError(AST__ATTIN,"astSetWidth(Plot):Invalid zero value supplied for Width(%s) attribute", status,GrfItem(axis,NULL,NULL, status )),this->width[axis]),AST__NPID) + +/* Size. */ +/* ----- */ +/* +*att++ +* Name: +* Size(element) + +* Purpose: +* Character size for a Plot element. + +* Type: +* Public attribute. + +* Synopsis: +* Floating Point. + +* Description: +* This attribute determines the character size used when drawing +* each element of graphical output produced by a Plot. It takes a +* separate value for each graphical element so that, for instance, +* the setting "Size(title)=2.0" causes the Plot title to be drawn +* using twice the default character size. +* +* The range of character sizes available and the appearance of the +* resulting text is determined by the underlying graphics system. +* The default behaviour is for all graphical elements to be drawn +* using the default character size supplied by this graphics +* system. + +* Applicability: +* Plot +* All Plots have this attribute. + +* Notes: +* - For a list of the graphical elements available, see the +* description of the Plot class. +* - If no graphical element is specified, (e.g. "Size" instead +* of "Size(title)"), then a "set" or "clear" operation will +* affect the attribute value of all graphical elements, while a +* "get" or "test" operation will use just the Size(TextLab) +* value. +*att-- +*/ +/* Character sizes. Has a value of AST__BAD when not set yielding a default + of 1.0. */ +MAKE_CLEAR(Size,size,AST__BAD,AST__NPID) +MAKE_GET(Size,double,1.0,( this->size[axis] == AST__BAD ? 1.0 : this->size[axis] ),AST__NPID) +MAKE_TEST(Size,( this->size[axis] != AST__BAD ),AST__NPID) +MAKE_SET(Size,double,size,(value!=0.00)?value:(astError(AST__ATTIN,"astSetSize(Plot): Invalid zero value supplied for Size(%s) attribute", status,GrfItem(axis,NULL,NULL, status )),this->size[axis]),AST__NPID) + +/* Member functions. */ +/* ================= */ +static void AddCdt( AstPlotCurveData *cdt1, AstPlotCurveData *cdt2, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* AddCdt + +* Purpose: +* Append one AstPlotCurveData structure to another. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void AddCdt( AstPlotCurveData *cdt1, AstPlotCurveData *cdt2, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* The contents of the structure pointed to by "cdt2" is appended +* to the structure pointed to by "cdt1". + +* Parameters: +* cdt1 +* Pointer to the AstPlotCurveData structure to be modified. +* cdt2 +* Pointer to the AstPlotCurveData structure to be appended to cdt1. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - An error is reported if there is insufficient room in "cdt1" to +* store the information in "cdt2". + +*/ + +/* Local Variables: */ + int nbrk, i, j; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the total number of breaks described by both structures. */ + nbrk = cdt1->nbrk + cdt2->nbrk; + +/* Report an error if this number of breaks cannot be stored in a AstPlotCurveData + structure. */ + if( nbrk > AST__MXBRK ){ + astError( AST__CVBRK, "%s(%s): Number of breaks in curve " + "exceeds %d.", status, method, class, AST__MXBRK ); + +/* Otherwise, append the information. */ + } else { + +/* Store the index within "cdt1" of the next break to be added. */ + j = cdt1->nbrk; + +/* Add each the position and direction information for each of the breaks + in "cdt2". */ + for( i = 0; i < cdt2->nbrk; i++ ){ + cdt1->xbrk[ j ] = cdt2->xbrk[ i ]; + cdt1->ybrk[ j ] = cdt2->ybrk[ i ]; + cdt1->vxbrk[ j ] = cdt2->vxbrk[ i ]; + cdt1->vybrk[ j ] = cdt2->vybrk[ i ]; + +/* Increment the index of the next break in "cdt1". */ + j++; + } + +/* Update the number of breaks in "cdt1". */ + cdt1->nbrk = nbrk; + +/* Update the length of the curve described by "cdt1". */ + cdt1->length += cdt2->length; + +/* Update the flag indicating if the entire curve is outside the plotting + zone. */ + if( !cdt2->out ) cdt1->out = 0; + + } + +/* Return. */ + return; + +} + +static void Apoly( AstPlot *this, float x, float y, int *status ){ +/* +* Name: +* Apoly + +* Purpose: +* Append a another point to a poly line. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Apoly( AstPlot *this, float x, float y, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function appends the supplied point to the current poly line. + +* Parameters: +* x +* The graphics x coordinate. +* y +* The graphics y coordinate. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int ipoint; + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Extend the buffers, and add the supplied point to the end. */ + ipoint = Poly_n++; + Poly_x = astGrow( Poly_x, Poly_n, sizeof(*Poly_x) ); + Poly_y = astGrow( Poly_y, Poly_n, sizeof(*Poly_y) ); + if( astOK ) { + Poly_x[ ipoint ] = x; + Poly_y[ ipoint ] = y; + } + +/* Update the box containing all plotted lines. */ + Box_lbnd[ 0 ] = astMIN( x, Box_lbnd[ 0 ] ); + Box_ubnd[ 0 ] = astMAX( x, Box_ubnd[ 0 ] ); + Box_lbnd[ 1 ] = astMIN( y, Box_lbnd[ 1 ] ); + Box_ubnd[ 1 ] = astMAX( y, Box_ubnd[ 1 ] ); + +} + +static void AxPlot( AstPlot *this, int axis, const double *start, double length, + int ink, AstPlotCurveData *cdata, const char *method, const char *class, int *status ){ +/* +* +* Name: +* AxPlot + +* Purpose: +* Draw a curve with constant axis value. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void AxPlot( AstPlot *this, int axis, const double *start, double length, +* int ink, AstPlotCurveData *cdata, const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws a section of a curve of the specified length +* with constant value on a specified axis in the current Frame of the +* Plot, starting at the specified position. The algorithm used can handle +* discontinuities in the Mapping between the current Frame and graphics +* coordinates, and information describing any breaks in the curve +* (including the start and end of the curve) are returned in the supplied +* AstPlotCurveData structure. + +* Parameters: +* this +* Pointer to the Plot. +* axis +* The zero-based index of an axis within the current Frame of the Plot. +* The curve has a varying value on this axis. +* start +* A pointer to a an array holding the coordinates of the start of the +* curve within the current Frame of the Plot. +* length +* The length of the section of the curve to be drawn, given as an +* increment along the axis specified by parameter "axis". +* ink +* If zero, the curve is not actually drawn, but information about +* the breaks is still returned. If non-zero, the curve is also drawn. +* cdata +* A pointer to a structure in which to return information about the +* breaks in the curve. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - No curve is draw if the "start" array contains any bad values +* (i.e. values equal to AST__BAD), or if the "length" value is bad, +* or if a NULL pointer is supplied for "cdata". No errors are reported +* in these cases. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + double d[ CRV_NPNT ]; /* Offsets to evenly spaced points along curve */ + double x[ CRV_NPNT ]; /* X coords at evenly spaced points along curve */ + double y[ CRV_NPNT ]; /* Y coords at evenly spaced points along curve */ + double tol; /* Absolute tolerance value */ + int i; /* Loop count */ + int naxes; /* No. of axes in the base Frame */ + int ok; /* Are all start coords good? */ + int gridid; /* Identifier value for element being drawn */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +#ifdef CRV_TRACE + printf("AXPLOT: axis %d, start (%.*g,%.*g), length %.*g\n", + axis, DBL_DIG, start[0], DBL_DIG, start[1], DBL_DIG, length ); + getchar(); +#endif + + +/* Initialise any supplied cdata structure to hold safe values. */ + if( cdata ){ + cdata->length = 0.0; + cdata->out = 1; + cdata->nbrk = 0; + } + +/* Get the number of axes in the current Frame. */ + naxes = astGetNout( this ); + +/* Check the "start" parameter for bad values. */ + ok = 1; + for( i = 0; i < naxes; i++ ) { + if( start[ i ] == AST__BAD ){ + ok = 0; + break; + } + } + +/* Check the "length" parameter for bad values. */ + if( length == AST__BAD ) ok = 0; + +/* Check that the "cdata" pointer can be used. */ + if( !cdata ) ok = 0; + +/* Only proceed if the parameters are OK, and there has been no error. */ + if( ok && astOK ){ + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + if( axis == 0 ) { + gridid = AST__GRIDLINE2_ID; + } else { + gridid = AST__GRIDLINE1_ID; + } + astGrfAttrs( this, gridid, 1, GRF__LINE, method, class ); + +/* Ensure the globals holding the scaling from graphics coords to equally + scaled coords are available. */ + GScales( this, NULL, NULL, method, class, status ); + +/* Set up the externals used to communicate with the Map1 function... + The number of axes in the physical coordinate system (i.e. the current + Frame). */ + Map1_ncoord = naxes; + +/* See if tick marks are logarithmically or linearly spaced. */ + Map1_log = astGetLogTicks( this, axis ); + +/* A pointer to the Plot, the Current Frame and the Mapping. */ + Map1_plot = this; + Map1_frame = astGetFrame( this, AST__CURRENT ); + Map1_map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Physical coords at the start of the curve (dist=0). */ + Map1_origin = start; + +/* Length of the curve. */ + Map1_length = length; + +/* The index of the axis which the curve follows. */ + Map1_axis = axis; + +/* Decide whether to omit points not in their normal ranges. */ + Map1_norm = !IsASkyAxis( Map1_frame, 0, status ) && + !IsASkyAxis( Map1_frame, 1, status ); + +/* Convert the tolerance from relative to absolute graphics coordinates. */ + tol = astGetTol( this )*astMAX( this->xhi - this->xlo, + this->yhi - this->ylo ); + +/* Now set up the external variables used by the Crv and CrvLine function. */ + Crv_scerr = ( astGetLogPlot( this, 0 ) || + astGetLogPlot( this, 1 ) ) ? 100.0 : 1.5; + Crv_ux0 = AST__BAD; + Crv_tol = tol; + Crv_limit = 0.5*tol*tol; + Crv_map = Map1; + Crv_ink = ink; + Crv_xlo = this->xlo; + Crv_xhi = this->xhi; + Crv_ylo = this->ylo; + Crv_yhi = this->yhi; + Crv_out = 1; + Crv_xbrk = cdata->xbrk; + Crv_ybrk = cdata->ybrk; + Crv_vxbrk = cdata->vxbrk; + Crv_vybrk = cdata->vybrk; + Crv_clip = astGetClip( this ) & 1; + +/* Set up a list of points spread evenly over the curve. */ + for( i = 0; i < CRV_NPNT; i++ ){ + d[ i ] = ( (double) i)/( (double) CRV_NSEG ); + } + +/* Map these points into graphics coordinates. */ + Map1( CRV_NPNT, d, x, y, method, class, status GLOBALS_NAME ); + +/* Use Crv and Map1 to draw the curve. */ + Crv( this, d, x, y, 0, NULL, NULL, method, class, status ); + +/* End the current poly line. */ + Opoly( this, status ); + +/* Tidy up the static data used by Map1. */ + Map1( 0, NULL, NULL, NULL, method, class, status GLOBALS_NAME ); + +/* If no part of the curve could be drawn, set the number of breaks and the + length of the drawn curve to zero. */ + if( Crv_out ) { + Crv_nbrk = 0; + Crv_len = 0.0F; + +/* Otherwise, add an extra break to the returned structure at the position of + the last point to be plotted. */ + } else { + Crv_nbrk++; + if( Crv_nbrk > AST__PLOT_CRV_MXBRK ){ + astError( AST__CVBRK, "%s(%s): Number of breaks in curve " + "exceeds %d.", status, method, class, AST__PLOT_CRV_MXBRK ); + } else { + *(Crv_xbrk++) = (float) Crv_xl; + *(Crv_ybrk++) = (float) Crv_yl; + *(Crv_vxbrk++) = (float) -Crv_vxl; + *(Crv_vybrk++) = (float) -Crv_vyl; + } + } + +/* Store extra information about the curve in the returned structure, and + purge any zero length sections. */ + if( cdata ){ + cdata->length = Crv_len; + cdata->out = Crv_out; + cdata->nbrk = Crv_nbrk; + PurgeCdata( cdata, status ); + } + +/* Annul the Frame and Mapping. */ + Map1_frame = astAnnul( Map1_frame ); + Map1_map = astAnnul( Map1_map ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, gridid, 0, GRF__LINE, method, class ); + + } + +/* Return. */ + return; + +} + +static void BBuf( AstPlot *this, int *status ) { +/* +*++ +* Name: +c astBBuf +f AST_BBUF + +* Purpose: +* Begin a new graphical buffering context. + +* Type: +* Public function. + +* Synopsis: +c #include "plot.h" +c void astBBuf( AstPlot *this ) +f CALL AST_BBUF( THIS STATUS ) + +* Class Membership: +* Plot member function. + +* Description: +c This function +f This routine +* starts a new graphics buffering context. A matching call to the +c function astEBuf +f routine AST_EBUF +* should be used to end the context. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The nature of the buffering is determined by the underlying +* graphics system (as defined by the current grf module). Each call +c to this function +f to this routine +c to this function +f to this routine +* simply invokes the astGBBuf function in the grf module. + +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the active GRF BBuf function. */ + GBBuf( this, "astBBuf", astGetClass( this ), status ); + +} + +static int Boundary( AstPlot *this, const char *method, const char *class, int *status ){ +/* +* Name: +* Boundary + +* Purpose: +* Draw a boundary around regions containing valid physical positions. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Boundary( AstPlot *this, const char *method, const char *class, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function draws a boundary around the regions of the plotting +* area which contain valid, unclipped, physical coordinates, but does +* not include the intersections with the edges of the plotting area. +* +* Broadly, the algorithm is as follows: An initial coarse grid is +* created covering the entire plotting area. This grid consists of a +* regular square matrix of points in graphics coordinates, and the +* corresponding physical coordinates. An array of flags is created, +* one for each grid cell, indicating if the boundary passes through the +* cell. This is assumed to be the case if the cell has a mix of good and +* bad corners (i.e corners which have good or bad physical coordinates). +* This assumption does not locate all boundary cells though, since if +* the boundary passes into and out of a cell throught the same edge, +* the corners of the cell will be either all good or all bad. But for +* the moment, we just concentrate on the ones found using this simple +* assumption. For each such cell, a finer grid is then created covering +* the cell, and the boundary is drawn through this fine grid using +* TraceBorder. TraceBorder returns a set of four flags indicating which +* edges of the cell were intersected by the boundary. A check is then +* made on any of the four neighbouring cells into which the curve +* passes. If any of these cells were not flagged as boundary cells using +* the simple assumption described earlier, then they are flagged now +* (with a different flag value). Once all the cells located using the +* simple assumption have been processed, any further cells flagged +* with the new flag value are also processed using TraceBorder in the +* same way. This process is repeated until no extra boundary cells are +* found. + +* Parameters: +* this +* Pointer to a Plot. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A flag indicating if any regions containing invalid physical +* coordinates were found within the plotting area. + +* Notes: +* - This function assumes the physical coordinate Frame is +* 2-dimensional, and it should not be used if this is not the case. +* - A value of zero is returned if an error has already occurred, or +* if this function should fail for any reason. + +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* Pointer to base Plot Frame */ + AstFrame *cfrm; /* Pointer to current Plot Frame */ + AstMapping *map; /* Pointer to Plot mapping (graphics -> physical) */ + AstMapping *rmap; /* Pointer to Plot mapping (graphics -> physical) */ + AstRegion *breg; /* Region mapped into base Plot Frame */ + double blbnd[ 2 ]; /* Lower grid bounds in base frame */ + double bubnd[ 2 ]; /* Upper grid bounds in base frame */ + double dx; /* Plotting area width */ + double dy; /* Plotting area height */ + double power; /* Exponent in pow call */ + double rat; /* Ratio by which to reduce DIM */ + double tol; /* Fractional plotting tolerance */ + int dim; /* No. of points along each edge of coarse grid */ + int edges[ 4 ]; /* Flags indicating edges bisected by boundary */ + int rate_disabled; /* Was the astRate method initially disabled? */ + int ret; /* Any regions containing bad physical coords? */ + +/* Check global status. */ + if( !astOK ) return 0; + +/* Initialise the answer to indicate that no regions containing invalid + physical coordinates have been found. */ + ret = 0; + +/* Get the current Frame from the Plot. */ + cfrm = astGetFrame( this, AST__CURRENT ); + +/* If it is a region, we use a special method, if possible, to trace the + Region boundary. Otherwise, we use a grid tracing method that makes no + assumptions about the nature of the Mapping or Frame. */ + if( !DrawRegion( this, cfrm, method, class, status ) ) { + +/* Each basic element of the boundary drawn by the following algorithm + will be drawn at a multiple of 45 degrees to the horizontal. This can + cause noticable aliasing. For instance, if the border is a straight + line at 50 degrees to the horizontal, it will be drawn at 45 degrees + for long sections, followed by a vertical leap to catch up with where + it should be. Because of this we use a finer tolerance than for other + drawing. */ + tol = 0.25*astGetTol( this ); + +/* Set up the dimension of a coarse grid in graphics coordinates to cover the + whole plotting area. This is chosen to give a finer grid for smaller + plotting tolerances. Note, putting the power as a literal constant in + the call to pow seems to cause a segmentation violation on some systems. */ + power = -0.666666666; + dim = (int) 4*pow( tol, power ) + 10; + if( dim > 400 ) dim = 400; + if( dim < 3 ) dim = 3; + +/* Store the required plotting tolerance as a distance in graphics + coords. */ + dx = fabs( this->xhi - this->xlo ); + dy = fabs( this->xhi - this->xlo ); + tol *= ( ( dx > dy ) ? dx : dy ); + +/* Extract the Mapping from the Plot. */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Select the area covered by the coarse grid. If the current Frame is a + Region, we use the bounding box of Region after being mapped into + graphics coords. */ + if( astIsARegion( cfrm ) ) { + bfrm = astGetFrame( this, AST__BASE ); + +/* Get the Mapping from the current to the base Frame in the Plot, and + remove the effects of any Regions. */ + + astInvert( map ); + rmap = astRemoveRegions( map ); + astInvert( map ); + +/* Map the Region into the GRAPHICS frame. */ + breg = astMapRegion( cfrm, rmap, bfrm ); + astGetRegionBounds( breg, blbnd, bubnd ); + + rmap = astAnnul( rmap ); + bfrm = astAnnul( bfrm ); + breg = astAnnul( breg ); + + rat = ( ( bubnd[ 0 ] - blbnd[ 0 ] )*( bubnd[ 1 ] - blbnd[ 1 ] ) )/ + ( ( this->xhi - this->xlo )*( this->yhi - this->ylo ) ); + rat = sqrt( rat ); + dim = (int) ( rat*dim ); + if( dim < 3 ) dim = 3; + +/* If the current Frame is not a Region, use the whole plot. */ + } else { + blbnd[ 0 ] = this->xlo; + blbnd[ 1 ] = this->ylo; + bubnd[ 0 ] = this->xhi; + bubnd[ 1 ] = this->yhi; + } + +/* Disable the astRate method in order to improve the speed of + evaluating the Mapping in cases where the Mapping includes an + AstRateMap. Note the original value of the flag so that it can be + re-instated at the end. */ + rate_disabled = astRateState( 1 ); + +/* Draw the boundary. */ + ret = TraceBorder( this, map, blbnd[ 0 ], bubnd[ 0 ], blbnd[ 1 ], + bubnd[ 1 ], dim, tol, edges, method, class, status ); + +/* Re-instate the original setting of the "astRate disabled" flag. */ + astRateState( rate_disabled ); + +/* Release the remaining resources. */ + map = astAnnul( map ); + } + cfrm = astAnnul( cfrm ); + +/* If an error has occurred, return 0. */ + if( !astOK ) ret = 0; + +/* Return the answer. */ + return ret; +} + +static int Border( AstPlot *this_nd, int *status ){ +/* +*++ +* Name: +c astBorder +f AST_BORDER + +* Purpose: +* Draw a border around valid regions of a Plot. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c int astBorder( AstPlot *this ) +f RESULT = AST_BORDER( THIS, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +* This function draws a (line) border around regions of the +* plotting area of a Plot which correspond to valid, unclipped +* physical coordinates. For example, when plotting using an +* all-sky map projection, this function could be used to draw the +* boundary of the celestial sphere when it is projected on to the +* plotting surface. +* +* If the entire plotting area contains valid, unclipped physical +* coordinates, then the boundary will just be a rectangular box +* around the edges of the plotting area. +* +* If the Plot is a Plot3D, this method is applied individually to +* each of the three 2D Plots encapsulated within the Plot3D (each of +* these Plots corresponds to a single 2D plane in the 3D graphics +* system). In addition, if the entire plotting volume has valid +* coordinates in the 3D current Frame of the Plot3D, then additional +* lines are drawn along the edges of the 3D plotting volume so that +* the entire plotting volume is enclosed within a cuboid grid. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astBorder() +f AST_BORDER = LOGICAL +c Zero is returned if the plotting space is completely filled by +f .FALSE. is returned if the plotting space is completely filled by +* valid, unclipped physical coordinates (so that only a +c rectangular box was drawn around the edge). Otherwise, one is +f rectangular box was drawn around the edge). Otherwise, .TRUE. is +* returned. + +* Notes: +c - A value of zero will be returned if this function is invoked +f - A value of .FALSE. will be returned if this function is invoked +c with the AST error status set, or if it should fail for any +f with STATUS set to an error value, or if it should fail for any +* reason. +* - An error results if either the current Frame or the base Frame +* of the Plot is not 2-dimensional or (for a Plot3D) 3-dimensional. +* - An error also results if the transformation between the base +* and current Frames of the Plot is not defined (i.e. the Plot's +* TranForward attribute is zero). +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPlot *this; /* Plot with no more than 2 current axes */ + AstPlotCurveData cdata; /* Structure to receive break information */ + const char *class; /* Object class */ + const char *method; /* Current method */ + int inval; /* Were any bad regions found? */ + int naxes; /* No. of axes in the base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_nd); + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astBorder"; + class = astGetClass( this_nd ); + +/* Initialise the bounding box for primitives produced by this call. */ + if( !Boxp_freeze ) { + Boxp_lbnd[ 0 ] = FLT_MAX; + Boxp_lbnd[ 1 ] = FLT_MAX; + Boxp_ubnd[ 0 ] = FLT_MIN; + Boxp_ubnd[ 1 ] = FLT_MIN; + } + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this_nd ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Get a Plot with a 2D (or 1D) current Frame. */ + this = (AstPlot *) Fset2D( (AstFrameSet *) this_nd, AST__CURRENT, status ); + +/* Check the current Frame of the Plot is 2-D. */ + naxes = astGetNout( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the current " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__BORDER_ID, 1, GRF__LINE, method, class ); + +/* We first draw the intersections of the regions containing valid + physical coordinates with the edges of the plotting area. First do + the bottom edge. */ + LinePlot( this, this->xlo, this->ylo, this->xhi, this->ylo, + 1, &cdata, method, class, status ); + +/* Now do the right-hand edge. */ + LinePlot( this, this->xhi, this->ylo, this->xhi, this->yhi, + 1, &cdata, method, class, status ); + +/* Now do the top edge. */ + LinePlot( this, this->xhi, this->yhi, this->xlo, this->yhi, + 1, &cdata, method, class, status ); + +/* Now do the left-hand edge. */ + LinePlot( this, this->xlo, this->yhi, this->xlo, this->ylo, + 1, &cdata, method, class, status ); + +/* Now draw a curve following the boundary through the interior of the + plotting area. If the current Frame in the Plot is a Region, we use a + shorter method if possible. If this is not possible, we use a longer + method. */ + inval = Boundary( this, method, class, status ); + +/* Ensure all lines are flushed to the graphics system. */ + Fpoly( this, method, class, status ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__BORDER_ID, 0, GRF__LINE, method, class ); + +/* Annul the 2d plot. */ + this = astAnnul( this ); + +/* Return. */ + return inval; + +} + +static void BoundingBox( AstPlot *this, float lbnd[2], float ubnd[2], int *status ){ +/* +*++ +* Name: +c astBoundingBox +f AST_BOUNDINGBOX + +* Purpose: +* Return a bounding box for previously drawn graphics. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astBoundingBox( AstPlot *this, float lbnd[2], float ubnd[2] ) +f CALL AST_BOUNDINGBOX( THIS, LBND, UBND, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function returns the bounds of a box which just encompasess the +f This routine returns the bounds of a box which just encompasess the +* graphics produced by the previous call to any of the Plot methods +* which produce graphical output. If no such previous call has yet +* been made, or if the call failed for any reason, then the bounding box +c returned by this function is undefined. +f returned by this routine is undefined. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c lbnd +f LBND( 2 ) = REAL (Returned) +* A two element array in which is returned the lower limits of the +* bounding box on each of the two axes of the graphics coordinate +* system (the base Frame of the Plot). +c ubnd +f UBND( 2 ) = REAL (Returned) +* A two element array in which is returned the upper limits of the +* bounding box on each of the two axes of the graphics coordinate +* system (the base Frame of the Plot). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - An error results if the base Frame of the Plot is not +* 2-dimensional. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameSet *fset; /* Pointer to the Plot's FrameSet */ + int naxes; /* No. of axes in the base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Get a pointer to the FrameSet at the start of the Plot. */ + fset = (AstFrameSet *) this; + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( fset ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "astBoundingBox(%s): Number of axes (%d) in the " + "base Frame of the supplied %s is invalid - this number " + "should be 2.", status, astGetClass( this ), naxes, + astGetClass( this ) ); + } + +/* Return the bounding box. */ + lbnd[ 0 ] = Boxp_lbnd[ 0 ]; + lbnd[ 1 ] = Boxp_lbnd[ 1 ]; + ubnd[ 0 ] = Boxp_ubnd[ 0 ]; + ubnd[ 1 ] = Boxp_ubnd[ 1 ]; + +/* Return. */ + return; + +} + +static int BoxCheck( float *bx, float *by, float *cx, float *cy, int *status ) { +/* +* Name: +* BoxCheck + +* Purpose: +* See if two boxes overlap. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int BoxCheck( float *bx, float *by, float *cx, float *cy, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function returns a flag indicating if two trapezoidal boxes +* (box "b" and box "c") overlap or not. + +* Parameters: +* bx +* Pointer to an array holding the X coordinates at the 4 corners +* of box "b". +* by +* Pointer to an array holding the Y coordinates at the 4 corners +* of box "b". +* cx +* Pointer to an array holding the X coordinates at the 4 corners +* of box "c". +* cy +* Pointer to an array holding the Y coordinates at the 4 corners +* of box "c". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if the boxes do not overlap or an error has +* already occurred. Otherwise, 1 is returned. + +*/ + +/* Local Variables: */ + float x2; + float y2; + int i; + int ip; + int j; + int jp; + int ret; + +/* Assume the boxes do not overlap. */ + ret = 0; + +/* Check the inherited status. */ + if( !astOK ) return ret; + +/* Check each corner of box b to see if it is inside box c. */ + for( j = 0; j < 4 && ret == 0; j++ ){ + if( Inside( 4, cx, cy, bx[ j ], by[ j ], status ) ) ret = 1; + } + +/* Now check each corner of box c to see if it is inside box b. */ + for( j = 0; j < 4 && ret == 0; j++ ){ + if( Inside( 4, bx, by, cx[ j ], cy[ j ], status ) ) ret = 1; + } + +/* If no overlap has yet been found, we need to see if any of the edges + of the boxes intersect. For instance, in the case of a cross formed by + a vertical rectangle crossing a horizontal rectangle, the above checks + on the corners would not have revealed any overlap. */ + if( !ret ) { + +/* The following code assumes that the corners with indices 0, 1, 2, 3 + are adjacent round the edge of the box. This is the case if the line + joining corners 0 and 1 does not cross the line joining corners 2 and + 3 AND the line joining corners 1 and 2 does not cross the line joining + corners 3 and 0. If either of these conditions is not met swap the + corners around to correct it. First do box b. */ + if( Cross( bx[0], by[0], bx[1], by[1], + bx[2], by[2], bx[3], by[3], status ) ) { + x2 = bx[2]; + y2 = by[2]; + bx[2] = bx[1]; + by[2] = by[1]; + bx[1] = x2; + by[1] = y2; + + } else if( Cross( bx[1], by[1], bx[2], by[2], + bx[3], by[3], bx[0], by[0], status ) ) { + x2 = bx[2]; + y2 = by[2]; + bx[2] = bx[3]; + by[2] = by[3]; + bx[3] = x2; + by[3] = y2; + } + +/* Now do box c. */ + if( Cross( cx[0], cy[0], cx[1], cy[1], + cx[2], cy[2], cx[3], cy[3], status ) ) { + x2 = cx[2]; + y2 = cy[2]; + cx[2] = cx[1]; + cy[2] = cy[1]; + cx[1] = x2; + cy[1] = y2; + + } else if( Cross( cx[1], cy[1], cx[2], cy[2], + cx[3], cy[3], cx[0], cy[0], status ) ) { + x2 = cx[2]; + y2 = cy[2]; + cx[2] = cx[3]; + cy[2] = cy[3]; + cx[3] = x2; + cy[3] = y2; + } + +/* We now check each edge of box b to see if it overlaps any edge of box c. */ + for( j = 0; j < 4 && ret == 0; j++ ) { + +/* This edge of box b starts at the corner with index j. Get the index of the + corner at which the edge ends. */ + jp = j + 1; + if( jp == 4 ) jp = 0; + +/* Check to see if this edge of box b crosses each edge of box c in turn. */ + for( i = 0; i < 4 && ret == 0; i++ ) { + ip = i + 1; + if( ip == 4 ) ip = 0; + + ret = Cross( bx[j], by[j], bx[jp], by[jp], + cx[i], cy[i], cx[ip], cy[ip], status ); + + } + } + } + + return ret; +} + +static void Bpoly( AstPlot *this, float x, float y, int *status ){ +/* +* Name: +* Bpoly + +* Purpose: +* Begin a new poly line. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Bpoly( AstPlot *this, float x, float y, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws any current poly line, and then starts a new one +* at the supplied position. + +* Parameters: +* x +* The graphics x coordinate. +* y +* The graphics y coordinate. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int ignore; /* Is the new point the end of the current polyline? */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* See if the new point is co-incident with the end of the current + polyline. If so we assume the current polyline is to be re-started, + rather than starting a new polyline. */ + if( Poly_n > 0 ) { + ignore = ( astEQUALS( Poly_x[ Poly_n - 1 ], x, 1.0E8 ) && + astEQUALS( Poly_y[ Poly_n - 1 ], y, 1.0E8 ) ); + } else { + ignore = 0; + } + +/* If the supplied point is not at the end of the current polyline, draw + any existing poly line. This will empty the buffer. Then add the + supplied point into the buffer. */ + if( !ignore ) { + Opoly( this, status ); + Apoly( this, x, y, status ); + } + +} + + +static int CGCapWrapper( AstPlot *this, int cap, int value, int *status ) { +/* +* +* Name: +* CGCapWrapper + +* Purpose: +* Call a C implementation of the GCap Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGCapWrapper( AstPlot *this, int cap, int value, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GCap +* grf function to enquire or set a graphics attribute value. + +* Parameters: +* this +* The Plot. +* cap +* The capability to be inquired aboue. +* value +* The value ot assign to the capability. +* status +* Pointer to the inherited status value. + +* Returned Value: +* Non-zero if the grf module is capabale of performing the action +* requested by "cap". + +*/ + if( !astOK ) return 0; + return ( (AstGCapFun) this->grffun[ AST__GCAP ] )( astGrfConID(this), cap, value ); +} + +static int CGAttrWrapper( AstPlot *this, int attr, double value, + double *old_value, int prim, int *status ) { +/* +* +* Name: +* CGAttrWrapper + +* Purpose: +* Call a C implementation of the GAttr Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGAttrWrapper( AstPlot *this, int attr, double value, +* double *old_value, int prim, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GAttr +* grf function to enquire or set a graphics attribute value. + +* Parameters: +* this +* The Plot. +* attr +* An integer value identifying the required attribute. The +* following symbolic values are defined in grf.h: +* +* GRF__STYLE - Line style. +* GRF__WIDTH - Line width. +* GRF__SIZE - Character and marker size scale factor. +* GRF__FONT - Character font. +* GRF__COLOUR - Colour index. +* value +* A new value to store for the attribute. If this is AST__BAD +* no value is stored. +* old_value +* A pointer to a double in which to return the attribute value. +* If this is NULL, no value is returned. +* prim +* The sort of graphics primitive to be drawn with the new attribute. +* Identified by the following values defined in grf.h: +* GRF__LINE +* GRF__MARK +* GRF__TEXT +* status +* Pointer to the inherited status value. + +*/ + if( !astOK ) return 0; + return ( (AstGAttrFun) this->grffun[ AST__GATTR ] )( astGrfConID(this), attr, value, old_value, prim ); +} + +static int CGBBufWrapper( AstPlot *this, int *status ) { +/* +* +* Name: +* CGBBufWrapper + +* Purpose: +* Call a C implementation of the GBBuf Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGBBufWrapper( AstPlot *this ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GBBuf +* grf function to start a new graphics context. + +* Parameters: +* this +* The Plot. +* status +* Pointer to the inherited status value. + +*/ + if( !astOK ) return 0; + return ( (AstGBBufFun) this->grffun[ AST__GBBUF ])( astGrfConID(this) ); +} + +static int CGEBufWrapper( AstPlot *this, int *status ) { +/* +* +* Name: +* CGEBufWrapper + +* Purpose: +* Call a C implementation of the GEBuf Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGEBufWrapper( AstPlot *this ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GEBuf +* grf function to start a new graphics context. + +* Parameters: +* this +* The Plot. +* status +* Pointer to the inherited status value. + +*/ + if( !astOK ) return 0; + return ( (AstGEBufFun) this->grffun[ AST__GEBUF ])( astGrfConID(this) ); +} + +static int CGFlushWrapper( AstPlot *this, int *status ) { +/* +* +* Name: +* CGFlushWrapper + +* Purpose: +* Call a C implementation of the GFlush Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGFlushWrapper( AstPlot *this ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GFlush +* grf function to flush graphics. + +* Parameters: +* this +* The Plot. +* status +* Pointer to the inherited status value. + +*/ + if( !astOK ) return 0; + return ( (AstGFlushFun) this->grffun[ AST__GFLUSH ])( astGrfConID(this) ); +} + +static int CGLineWrapper( AstPlot *this, int n, const float *x, + const float *y, int *status ) { +/* +* +* Name: +* CGLineWrapper + +* Purpose: +* Call a C implementation of the GLine Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGLineWrapper( AstPlot *this, int n, const float *x, +* const float *y, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GLine +* grf function to draw a polyline. + +* Parameters: +* this +* The Plot. +* n +* The number of positions to be joined together. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* status +* Pointer to the inherited status variable. + +*/ + if( !astOK ) return 0; + return ( (AstGLineFun) this->grffun[ AST__GLINE ])( astGrfConID(this), n, x, y ); +} + +static int CGMarkWrapper( AstPlot *this, int n, const float *x, + const float *y, int type, int *status ) { +/* +* +* Name: +* CGMarkWrapper + +* Purpose: +* Call a C implementation of the GMark Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGMarkWrapper( AstPlot *this, int n, const float *x, +* const float *y, int type, int *status ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GMark grf +* function to draw markers. + +* Parameters: +* this +* The Plot. +* n +* The number of positions to be joined together. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* type +* An integer which can be used to indicate the type of marker symbol +* required. +* status +* Pointer to the inherited status value. + +*/ + if( !astOK ) return 0; + return ( (AstGMarkFun) this->grffun[ AST__GMARK ])( astGrfConID(this), n, x, y, type ); +} + +static int CGTextWrapper( AstPlot *this, const char *text, float x, float y, + const char *just, float upx, float upy, int *status ) { +/* +* +* Name: +* CGTextWrapper + +* Purpose: +* Call a C implementation of the GText Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGTextWrapper( AstPlot *this, const char *text, float x, float y, +* const char *just, float upx, float upy, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GText grf +* function to draw a text string. + +* Parameters: +* this +* The Plot. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* upx +* The x component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* left to right on the screen. +* upy +* The y component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* bottom to top on the screen. +* status +* Pointer to the inherited status value. + +*/ + if( !astOK ) return 0; + return ( (AstGTextFun) this->grffun[ AST__GTEXT ])( astGrfConID(this), text, x, y, just, upx, upy ); +} + +static int CGTxExtWrapper( AstPlot *this, const char *text, float x, float y, + const char *just, float upx, float upy, float *xb, + float *yb, int *status ) { +/* +* +* Name: +* CGTxExtWrapper + +* Purpose: +* Call a C implementation of the GTxExt Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGTxExtWrapper( AstPlot *this, const char *text, float x, float y, +* const char *just, float upx, float upy, float *xb, +* float *yb, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GTxExt +* grf function to find the extent of a text string. + +* Parameters: +* this +* The Plot. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* upx +* The x component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* left to right on the screen. +* upy +* The y component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* bottom to top on the screen. +* xb +* An array of 4 elements in which to return the x coordinate of +* each corner of the bounding box. +* yb +* An array of 4 elements in which to return the y coordinate of +* each corner of the bounding box. +* status +* Pointer to the inherited status variable. + +*/ + if( !astOK ) return 0; + return ( (AstGTxExtFun) this->grffun[ AST__GTXEXT ])( astGrfConID(this), text, x, y, just, upx, upy, xb, yb ); +} + +static int CGQchWrapper( AstPlot *this, float *chv, float *chh, int *status ) { +/* +* +* Name: +* CGQchWrapper + +* Purpose: +* Call a C implementation of the GQch Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGQchWrapper( AstPlot *this, float *chv, float *chh, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GQch +* grf function to find the extent of a text string. + +* Parameters: +* this +* The Plot. +* chv +* A pointer to the double which is to receive the height of +* characters drawn vertically. This will be an increment in the X +* axis +* chh +* A pointer to the double which is to receive the height of +* characters drawn vertically. This will be an increment in the Y +* axis +* status +* Pointer to the inherited status value. +*/ + if( !astOK ) return 0; + return ( (AstGQchFun) this->grffun[ AST__GQCH ])( astGrfConID(this), chv, chh ); +} + +static int CGScalesWrapper( AstPlot *this, float *alpha, float *beta, int *status ) { +/* +* +* Name: +* CGScalesWrapper + +* Purpose: +* Call a C implementation of the GScales Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CGScalesWrapper( AstPlot *this, float *alpha, float *beta, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function is a wrapper for a C implementation of the GScales +* grf function to find the extent of a text string. + +* Parameters: +* this +* The Plot. +* alpha +* A pointer to the location at which to return the scale for the +* X axis (i.e. Xnorm = alpha*Xworld). +* beta +* A pointer to the location at which to return the scale for the +* Y axis (i.e. Ynorm = beta*Yworld). +* status +* Pointer to the inherited status value. +*/ + if( !astOK ) return 0; + return ( (AstGScalesFun) this->grffun[ AST__GSCALES ])( astGrfConID(this), alpha, beta ); +} + +static int CheckLabels( AstPlot *this, AstFrame *frame, int axis, + double *ticks, int nticks, int force, char **list, + double refval, int *status ){ +/* +* Name: +* CheckLabels + +* Purpose: +* Create tick mark labels and check that adjacent labels are different. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CheckLabels( AstPlot *this, AstFrame *frame, int axis, double *ticks, +* int nticks, int force, char **list, double refval, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function formats the supplied ticks mark values using the +* astFormat method for the supplied Frame. Unless force is non-zero, it +* then checks all pairs of adjacent labels. If a pair is found which are +* identical then the memory holding the labels is released, and a value +* of zero is returned. Otherwise, a value of one is returned, indicating +* that adjacent labels are all different and the labels are returned. + +* Parameters: +* this +* Pointer to the Plot. +* frame +* Pointer to the Frame. +* axis +* The zero-based index of the axis to which the tick marks refer. +* ticks +* Pointer to an array holding the tick mark values. +* nticks +* The number of tick marks supplied by parameter "ticks". +* force +* If non-zero, then no check for identical adjacent labels is +* performed, and the labels are always considered to be OK. +* list +* Pointer to the start of an array of pointers. Each of the +* elements in this array receives a pointer to a string holding a +* formatted label. Each of these strings should be freed using +* astFree when no longer needed. +* refval +* A value to use for the other axis when normalizing. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if any pairs of identical adjacent labels were found. One +* otherwise. + +* Notes: +* - No error is reported if a pair of identical adjacent labels is +* found. +* - If an error has already occurred, or if this function should +* fail for any reason, a value of zero is returned, and the array of +* pointers identified by "list" is filled with NULL pointers. + + +*/ + +/* Local Variables: */ + const char *label; /* Pointer to formatted tick value */ + double val[ 2 ]; /* Workspace for normalizing */ + int i; /* Tick index */ + int len; /* Number of characters in curent label */ + int ok; /* The returned flag */ + +/* Fill the supplied label list with NULL pointers. */ + if( list ) { + for( i = 0; i < nticks; i++ ) list[ i ] = NULL; + } + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise the returned flag to indicate that all adjacent labels are + different. */ + ok = 1; + +/* Normalize and format the first tick mark value. */ + val[ axis ] = ticks[ 0 ]; + val[ 1 - axis ] = refval; + astNorm( frame, val ); + label = astFormat( frame, axis, val[ axis ] ); + +/* Allocate memory holding a copy of the formatted value, and store a + pointer to this copy in the list of labels. */ + if( label ){ + len = strlen( label ) + 1; + list[ 0 ] = (char *) astStore( NULL, (void *) label, len ); + } else { + ok = 0; + } + +/* Normalize and format each of the tick mark values in this batch. */ + for( i = 1; i < nticks && astOK && ok; i++ ){ + val[ axis ] = ticks[ i ]; + val[ 1 - axis ] = refval; + astNorm( frame, val ); + label = astFormat( frame, axis, val[ axis ] ); + if( label ){ + +/* Unless checks have been supressed, compare this label with the previous + label. If they are identical clear the returned flag. */ + if( !force && !strcmp( label, list[ i - 1 ] ) ) { + ok = 0; + +/* Allocate memory holding a copy of the label, and store a + pointer to this copy in the list of labels. */ + } else { + list[ i ] = (char *) astStore( NULL, (void *) label, strlen( label ) + 1 ); + } + + } else { + ok = 0; + } + + } + +/* If two adjacent labels were identical, or an error occurred, release the + memory used to store the labels. */ + if( !ok || !astOK ){ + for( i = 0; i < nticks; i++ ){ + if( list[ i ] ) list[ i ] = (char *) astFree( (void *) list[ i ] ); + } + } + +/* Ensure a value of zero is returned if an error has occurred. */ + if( !astOK ) ok = 0; + +/* Return the answer. */ + return ok; + +} + +static char **CheckLabels2( AstPlot *this, AstFrame *frame, int axis, + double *ticks, int nticks, char **old_list, + double refval, int *status ){ +/* +* Name: +* CheckLabels2 + +* Purpose: +* Check that labels cannot be shortened. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* char **CheckLabels2( AstPlot *this, AstFrame *frame, int axis, +* double *ticks, int nticks, char **old_list, +* double refval, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function formats the supplied ticks mark values using the +* astFormat method for the supplied Frame. It then compares the labels +* with the corresponding labels supplied in "old_list". If all of the +* new labels are shorter than, or equal in length to, the old labels, +* then memory is allocated to hold the new (shorter) labels, and a +* pointer to this memory is returned. If any new label is longer than +* the corresponding old label, then a NULL pointer is returned. +* +* No check is performed on whether or not there are any identical +* adjacent labels. + +* Parameters: +* this +* Pointer to the Plot. +* frame +* Pointer to the Frame. +* axis +* The zero-based index of the axis to which the tick marks refer. +* ticks +* Pointer to an array holding the tick mark values. +* nticks +* The number of tick marks supplied by parameter "ticks". +* old_list +* Pointer to the start of an array of pointers. Each of the +* elements in this array should hold a pointer to a string holding a +* formatted label. +* refval +* A value to use for the other axis when normalizing. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to an array of pointers. Each of these pointers points to +* a text string holding a shortened label. If a complete set of +* shortened labels could not be found (or if an error occurs), a NULL +* pointer is returned. + +* Notes: +* - The memory holding the returned shortened labels should be +* freed by cthe caller, together with the memory holding the pointers to +* the labels. +* - No error is reported if a pair of identical adjacent labels is +* found. +* - If an error has already occurred, or if this function should +* fail for any reason, a value of NULL is returned. + +*/ + +/* Local Variables: */ + char **list; /* The returned pointer */ + const char *label; /* Pointer to formatted tick value */ + double val[ 2 ]; /* Workspace for normalizing */ + int i; /* Tick index */ + int llen; /* Number of characters in curent label */ + int ok; /* Are the old labels OK to be used? */ + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Allocate memory to hold the pointers to the new labels. */ + list = (char **) astMalloc( sizeof( char * )*(size_t) nticks ); + if( list ) { + +/* Fill this array with NULLs for safety. */ + for( i = 0; i < nticks; i++ ) list[ i ] = NULL; + +/* Initialise a flag to indicate that all the new labels are + shorter than the old labels. */ + ok = 0; + +/* Normalize and format each of the tick mark values in this batch. */ + for( i = 0; i < nticks && astOK; i++ ){ + val[ axis ] = ticks[ i ]; + val[ 1 - axis ] = refval; + astNorm( frame, val ); + label = astFormat( frame, axis, val[ axis ] ); + if( label ){ + +/* Get the length of the new label. */ + llen = strlen( label ); + +/* Compare this label with the corresponding old label. If the new one is + longer than the old one, set the flag and leave the loop. */ + if( llen > strlen( old_list[ i ] ) ) { + ok = 1; + break; + } + +/* Store the new label. */ + list[ i ] = (char *) astStore( NULL, (void *) label, + (size_t) (llen + 1) ); + } + } + +/* If the old labels are to be used, or an error occurred, release the memory + used to store the new labels. */ + if( ok || !astOK ){ + for( i = 0; i < nticks; i++ ){ + if( list[ i ] ) list[ i ] = (char *) astFree( (void *) list[ i ] ); + } + list = (char **) astFree( (void *) list ); + } + + } + +/* Return the answer. */ + return list; + +} + +static int ChrLen( const char *string, int *status ){ +/* +* Name: +* ChrLen + +* Purpose: +* Return the length of a string excluding any trailing white space. + +* Type: +* Private function. + +* Synopsis: +* int ChrLen( const char *string, int *status ) + +* Class Membership: +* Plot + +* Description: +* This function returns the length of a string excluding any trailing +* white space. + +* Parameters: +* string +* Pointer to the string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The length of a string excluding any trailing white space. + +* Notes: +* - A value of zero is returned if a NULL pointer is supplied, or if an +* error has already occurred. + +*/ + +/* Local Variables: */ + const char *c; /* Pointer to the next character to check */ + int ret; /* The returned string length */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise the returned string length. */ + ret = 0; + +/* Check a string has been supplied. */ + if( string ){ + +/* Check each character in turn, starting with the last one. */ + ret = strlen( string ); + c = string + ret - 1; + while( ret ){ + if( !isspace( (int) *c ) ) break; + c--; + ret--; + } + } + +/* Return the answer. */ + return ret; + +} + +static AstPlotCurveData **CleanCdata( AstPlotCurveData **cdata, int *status ){ +/* +* Name: +* CleanCdata + +* Purpose: +* Release the structures holding curve break information. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* AstPlotCurveData **CleanCdata( AstPlotCurveData **cdata, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function releases the memory used to hold the curve break +* information returned by function DrawGrid, and returns a NULL pointer. + +* Parameters: +* cdata +* Pointer to the information to be freed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A NULL pointer. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. + +*/ + +/* Return if a NULL pointer has been supplied. */ + if( !cdata ) return NULL; + +/* Release each of the two structures in turn (if they exist). */ + (void) astFree( (void *) cdata[ 0 ] ); + (void) astFree( (void *) cdata[ 1 ] ); + +/* Release the memory used to hold the two AstPlotCurveData pointers. */ + (void) astFree( (void *) cdata ); + +/* Return. */ + return NULL; + +} + +static TickInfo **CleanGrid( TickInfo **grid, int *status ){ +/* +* Name: +* CleanGrid + +* Purpose: +* Release the structures holding grid information. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* TickInfo **CleanGrid( TickInfo **grid ) + +* Class Membership: +* Plot member function. + +* Description: +* This function releases the memory used to hold the grid information +* returned by function GridLines, and returns a NULL pointer. + +* Parameters: +* grid +* Pointer to the information to be freed. + +* Returned Value: +* A NULL pointer. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. + +*/ + +/* Local Variables: */ + TickInfo *info; /* Pointer to TickInfo structure being freed */ + int i; /* Axis index */ + int j; /* Tick mark index */ + +/* Return if a NULL pointer has been supplied. */ + if( !grid ) return NULL; + +/* Release each of the TickInfo structures in turn (if they exist). */ + for( i = 0; i < 2; i++ ){ + if( ( info = grid[ i ] ) ){ + +/* Release the memory holding major tick mark values. */ + (void) astFree( (void *) info->ticks ); + +/* Release the memory holding minor tick mark values. */ + (void) astFree( (void *) info->minticks ); + +/* Release the memory holding curve section starting positions. */ + (void) astFree( (void *) info->start ); + +/* Release the memory holding curve section lengths. */ + (void) astFree( (void *) info->length ); + +/* If there are any tick mark labels in the structure... */ + if( info->labels ){ + +/* Release the memory holding each tick mark label. */ + for( j = 0; j < info->nmajor; j++ ){ + (void) astFree( (void *) info->labels[ j ] ); + } + +/* Release the memory holding the pointers to the tick mark labels. */ + (void) astFree( (void *) info->labels ); + +/* Release the memory holding the format specification string. */ + (void) astFree( (void *) info->fmt ); + + } + +/* Release the TickInfo structure. */ + (void) astFree( (void *) info ); + } + } + +/* Release the memory used to hold the two TickInfo pointers. */ + (void) astFree( (void *) grid ); + +/* Return. */ + return NULL; + +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Plot. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Plot member function (over-rides the astClearAttrib protected +* method inherited from the FrameSet class). + +* Description: +* This function clears the value of a specified attribute for a +* Plot, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Plot. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPlot *this; /* Pointer to the Plot structure */ + char label[21]; /* Graphics item label */ + const char *class; /* Pointer to class string */ + int axis; /* Axis number */ + int id1; /* Plot object id */ + int id2; /* Plot object id */ + int id; /* Plot object id */ + int len; /* Length of attrib string */ + int nax; /* Number of base Frame axes */ + int nc; /* No. characters read by astSscanf */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot structure. */ + this = (AstPlot *) this_object; + +/* Get the number of base Frame axis (2 for a Plot, 3 for a Plot3D). */ + nax = astGetNin( this ); + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Edge(axis). */ +/* ------------ */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "edge(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearEdge( this, axis - 1 ); + +/* Grid. */ +/* ----- */ + } else if ( !strcmp( attrib, "grid" ) ) { + astClearGrid( this ); + +/* LabelUp */ +/* ------- */ + } else if ( !strcmp( attrib, "labelup" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearLabelUp( this, axis ); + +/* LabelUp(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelup(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLabelUp( this, axis - 1 ); + +/* LogPlot */ +/* ------- */ + } else if ( !strcmp( attrib, "logplot" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearLogPlot( this, axis ); + +/* LogPlot(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "logplot(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLogPlot( this, axis - 1 ); + +/* LogTicks */ +/* ------- */ + } else if ( !strcmp( attrib, "logticks" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearLogTicks( this, axis ); + +/* LogTicks(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "logticks(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLogTicks( this, axis - 1 ); + +/* LogLabel */ +/* ------- */ + } else if ( !strcmp( attrib, "loglabel" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearLogLabel( this, axis ); + +/* LogLabel(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "loglabel(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLogLabel( this, axis - 1 ); + +/* NumLab. */ +/* ---------- */ + } else if ( !strcmp( attrib, "numlab" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearNumLab( this, axis ); + +/* NumLab(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "numlab(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearNumLab( this, axis - 1 ); + +/* MinTick. */ +/* ---------- */ + } else if ( !strcmp( attrib, "mintick" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearMinTick( this, axis ); + +/* MinTick(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "mintick(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearMinTick( this, axis - 1 ); + +/* TextLab. */ +/* ---------- */ + } else if ( !strcmp( attrib, "textlab" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearTextLab( this, axis ); + +/* TextLab(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "textlab(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearTextLab( this, axis - 1 ); + +/* LabelUnits. */ +/* --------- */ + } else if ( !strcmp( attrib, "labelunits" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearLabelUnits( this, axis ); + +/* LabelUnits(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelunits(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLabelUnits( this, axis - 1 ); + +/* Style. */ +/* ------ */ + } else if ( !strcmp( attrib, "style" ) ) { + for( id = 0; id < AST__NPID; id++ ) astClearStyle( this, id ); + +/* Style(label). */ +/* --------------*/ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "style(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, attrib, "astClear", class, status ), + nax, &id1, &id2, &id3, status ); + astClearStyle( this, id1 ); + if( nid > 1 ) astClearStyle( this, id2 ); + if( nid > 2 ) astClearStyle( this, id3 ); + +/* Font. */ +/* ----- */ + } else if ( !strcmp( attrib, "font" ) ) { + for( id = 0; id < AST__NPID; id++ ) astClearFont( this, id ); + +/* Font(label). */ +/* -------------*/ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "font(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, attrib, "astClear", class, status ), + nax, &id1, &id2, &id3, status ); + astClearFont( this, id1 ); + if( nid > 1 ) astClearFont( this, id2 ); + if( nid > 2 ) astClearFont( this, id3 ); + +/* Colour. */ +/* ------- */ + } else if ( !strcmp( attrib, "colour" ) ) { + for( id = 0; id < AST__NPID; id++ ) astClearColour( this, id ); + +/* Colour(label). */ +/* ---------------*/ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "colour(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, attrib, "astClear", class, status ), + nax, &id1, &id2, &id3, status ); + astClearColour( this, id1 ); + if( nid > 1 ) astClearColour( this, id2 ); + if( nid > 2 ) astClearColour( this, id3 ); + +/* Color. */ +/* ------ */ + } else if ( !strcmp( attrib, "color" ) ) { + for( id = 0; id < AST__NPID; id++ ) astClearColour( this, id ); + +/* Color(label). */ +/* --------------*/ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "color(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, attrib, "astClear", class, status ), + nax, &id1, &id2, &id3, status ); + astClearColour( this, id1 ); + if( nid > 1 ) astClearColour( this, id2 ); + if( nid > 2 ) astClearColour( this, id3 ); + +/* Width. */ +/* ------ */ + } else if ( !strcmp( attrib, "width" ) ) { + for( id = 0; id < AST__NPID; id++ ) astClearWidth( this, id ); + +/* Width(label). */ +/* --------------*/ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "width(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, attrib, "astClear", class, status ), + nax, &id1, &id2, &id3, status ); + + astClearWidth( this, id1 ); + if( nid > 1 ) astClearWidth( this, id2 ); + if( nid > 2 ) astClearWidth( this, id3 ); + +/* Size. */ +/* ----- */ + } else if ( !strcmp( attrib, "size" ) ) { + for( id = 0; id < AST__NPID; id++ ) astClearSize( this, id ); + +/* Size(label). */ +/* -------------*/ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "size(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, attrib, "astClear", class, status ), + nax, &id1, &id2, &id3, status ); + astClearSize( this, id1 ); + if( nid > 1 ) astClearSize( this, id2 ); + if( nid > 2 ) astClearSize( this, id3 ); + +/* LabelAt(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelat(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLabelAt( this, axis - 1 ); + +/* Centre(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "centre(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearCentre( this, axis - 1 ); + +/* Gap. */ +/* ---- */ + } else if ( !strcmp( attrib, "gap" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearGap( this, axis ); + +/* Gap(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "gap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearGap( this, axis - 1 ); + +/* LogGap. */ +/* ----------- */ + } else if ( !strcmp( attrib, "loggap" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearLogGap( this, axis ); + +/* LogGap(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "loggap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearLogGap( this, axis - 1 ); + +/* NumLabGap. */ +/* ---------- */ + } else if ( !strcmp( attrib, "numlabgap" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearNumLabGap( this, axis ); + +/* NumLabGap(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "numlabgap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearNumLabGap( this, axis - 1 ); + +/* TextLabGap. */ +/* ----------- */ + } else if ( !strcmp( attrib, "textlabgap" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearTextLabGap( this, axis ); + +/* TextLabGap(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "textlabgap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearTextLabGap( this, axis - 1 ); + +/* TitleGap. */ +/* --------- */ + } else if ( !strcmp( attrib, "titlegap" ) ) { + astClearTitleGap( this ); + +/* MajTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "majticklen" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearMajTickLen( this, axis ); + +/* MajTickLen(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "majticklen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearMajTickLen( this, axis - 1 ); + +/* MinTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "minticklen" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearMinTickLen( this, axis ); + +/* MinTickLen(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "minticklen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearMinTickLen( this, axis - 1 ); + +/* Labelling. */ +/* -------- */ + } else if ( !strcmp( attrib, "labelling" ) ) { + astClearLabelling( this ); + +/* TickAll. */ +/* -------- */ + } else if ( !strcmp( attrib, "tickall" ) ) { + astClearTickAll( this ); + +/* ForceExterior */ +/* ------------- */ + } else if ( !strcmp( attrib, "forceexterior" ) ) { + astClearForceExterior( this ); + +/* Invisible. */ +/* ---------- */ + } else if ( !strcmp( attrib, "invisible" ) ) { + astClearInvisible( this ); + +/* Border. */ +/* ------- */ + } else if ( !strcmp( attrib, "border" ) ) { + astClearBorder( this ); + +/* ClipOp. */ +/* ------- */ + } else if ( !strcmp( attrib, "clipop" ) ) { + astClearClipOp( this ); + +/* Clip. */ +/* ----- */ + } else if ( !strcmp( attrib, "clip" ) ) { + astClearClip( this ); + +/* Grf. */ +/* ---- */ + } else if ( !strcmp( attrib, "grf" ) ) { + astClearGrf( this ); + +/* DrawTitle. */ +/* ---------- */ + } else if ( !strcmp( attrib, "drawtitle" ) ) { + astClearDrawTitle( this ); + +/* DrawAxes. */ +/* --------- */ + } else if ( !strcmp( attrib, "drawaxes" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearDrawAxes( this, axis ); + +/* Abbrev */ +/* ------ */ + } else if ( !strcmp( attrib, "abbrev" ) ) { + for( axis = 0; axis < nax; axis++ ) astClearAbbrev( this, axis ); + +/* DrawAxes(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "drawaxes(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearDrawAxes( this, axis - 1 ); + +/* Abbrev(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "abbrev(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearAbbrev( this, axis - 1 ); + +/* Escape. */ +/* ------- */ + } else if ( !strcmp( attrib, "escape" ) ) { + astClearEscape( this ); + +/* Tol. */ +/* ---- */ + } else if ( !strcmp( attrib, "tol" ) ) { + astClearTol( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearLogPlot( AstPlot *this, int axis, int *status ){ +/* +* +* Name: +* ClearLogPlot + +* Purpose: +* Clear the value for a LogPlot attribute + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void ClearLogPlot( AstPlot *this, int axis, int *status ) + +* Class Membership: +* Plot member function + +* Description: +* Assigns the default value to the LogPlot attribute of the specified +* axis, and also re-maps the base Frame of the Plot if necessary. + +* Parameters: +* this +* The Plot. +* axis +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int oldval; /* Original value of the attribute */ + int newval; /* Cleared (default) value of the attribute */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the axis index. */ + if( axis < 0 || axis >= 2 ){ + astError( AST__AXIIN, "astClearLogPlot(%s): Index (%d) is invalid for " + "attribute LogPlot - it should be in the range 1 to 2.", status, + astGetClass( this ), axis + 1 ); + +/* Do nothing if the attribute is not currently set. */ + } else if( astTestLogPlot( this, axis ) ){ + +/* Get the original value of the attribute. clear the value, and then get + the new (default) value. */ + oldval = this->logplot[ axis ]; + this->logplot[ axis ] = -1; + newval = astGetLogPlot( this, axis ); + +/* If the effective value has changed, attempt to remap the axis. If this + fails, re-instate the original value. */ + if( ( oldval != 0 ) != ( newval != 0 ) ) { + if( !ToggleLogLin( this, axis, oldval, "astClearLogPlot", status ) ) { + this->logplot[ axis ] = oldval; + } + } + } +} + +static void Clip( AstPlot *this, int iframe, const double lbnd[], + const double ubnd[], int *status ){ +/* +*++ +* Name: +c astClip +f AST_CLIP + +* Purpose: +* Set up or remove clipping for a Plot. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astClip( AstPlot *this, int iframe, const double lbnd[], +c const double ubnd[] ) +f CALL AST_CLIP( THIS, IFRAME, LBND, UBND, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function defines regions of a Plot which are to be clipped. +f This routine defines regions of a Plot which are to be clipped. +* Any subsequent graphical output created using the Plot will then +* be visible only within the unclipped regions of the plotting +* area. See also the Clip attribute. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c iframe +f IFRAME = INTEGER (Given) +* The index of the Frame within the Plot to which the clipping +c limits supplied in "lbnd" and "ubnd" (below) refer. Clipping +f limits supplied in LBND and UBND (below) refer. Clipping +* may be applied to any of the coordinate systems associated +* with a Plot (as defined by the Frames it contains), so this +* index may take any value from 1 to the number of Frames in +* the Plot (Nframe attribute). In addition, the values +* AST__BASE and AST__CURRENT may be used to specify the base +* and current Frames respectively. +* +* For example, a value of AST__CURRENT causes clipping to be +* performed in physical coordinates, while a value of AST__BASE +* would clip in graphical coordinates. Clipping may also be +* removed completely by giving a value of AST__NOFRAME. In this +* case any clipping bounds supplied (below) are ignored. +c lbnd +f LBND( * ) = DOUBLE PRECISION (Given) +* An array with one element for each axis of the clipping Frame +c (identified by the index "iframe"). This should contain the +f (identified by the index IFRAME). This should contain the +* lower bound, on each axis, of the region which is to remain +* visible (unclipped). +c ubnd +f UBND( * ) = DOUBLE PRECISION (Given) +* An array with one element for each axis of the clipping Frame +c (identified by the index "iframe"). This should contain the +f (identified by the index IFRAME). This should contain the +* upper bound, on each axis, of the region which is to remain +* visible (unclipped). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - Only one clipping Frame may be active at a time. This function +f - Only one clipping Frame may be active at a time. This routine +* will deactivate any previously-established clipping Frame before +* setting up new clipping limits. +c - The clipping produced by this function is in addition to that +f - The clipping produced by this routine is in addition to that +* specified by the Clip attribute which occurs at the edges of the +* plotting area +c established when the Plot is created (see astPlot). The +f established when the Plot is created (see AST_PLOT). The +* underlying graphics system may also impose further clipping. +* - When testing a graphical position for clipping, it is first +* transformed into the clipping Frame. The resulting coordinate on +* each axis is then checked against the clipping limits (given by +c "lbnd" and "ubnd"). By default, a position is clipped if any +f LBND and UBND). By default, a position is clipped if any +* coordinate lies outside these limits. However, if a non-zero +* value is assigned to the Plot's ClipOp attribute, then a +* position is only clipped if the coordinates on all axes lie +* outside their clipping limits. +* - If the lower clipping limit exceeds the upper limit for any +* axis, then the sense of clipping for that axis is reversed (so +* that coordinate values lying between the limits are clipped +* instead of those lying outside the limits). To produce a "hole" +* in a coordinate space (that is, an internal region where nothing +* is plotted), you should supply all the bounds in reversed order, +* and set the ClipOp attribute for the Plot to a non-zero value. +* - Either clipping limit may be set to the value AST__BAD, which +* is equivalent to setting it to infinity (or minus infinity for a +* lower bound) so that it is not used. +* - If a graphical position results in any bad coordinate values +* (AST__BAD) when transformed into the clipping Frame, then it is +* treated (for the purposes of producing graphical output) as if +* it were clipped. +* - When a Plot is used as a Mapping to transform points +c (e.g. using astTran2), any clipped output points are assigned +f (e.g. using AST_TRAN2), any clipped output points are assigned +* coordinate values of AST__BAD. +* - An error results if the base Frame of the Plot is not +* 2-dimensional. +*-- +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to the clipping Frame */ + AstFrameSet *fset; /* Pointer to the Plot's FrameSet */ + int i; /* Axis index */ + int ifrm; /* The validated frame index */ + int naxes; /* No. of axes in the base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + ifrm = 0; + +/* Get a pointer to the FrameSet at the start of the Plot. */ + fset = (AstFrameSet *) this; + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( fset ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "astClip(%s): Number of axes (%d) in the " + "base Frame of the supplied %s is invalid - this number " + "should be 2.", status, astGetClass( this ), naxes, + astGetClass( this ) ); + } + +/* If clipping is to be switched on, check the supplied frame index and + bounds. */ + if( iframe != AST__NOFRAME && astOK ) { + +/* Report an error if either of the bounds pointers is NULL.*/ + if( !lbnd ){ + astError( AST__CLPAX, "astClip(%s): A NULL pointer was " + "supplied for the array holding the lower bounds of " + "the clipping volume.", status, astGetClass( this ) ); + } else if( !ubnd ){ + astError( AST__CLPAX, "astClip(%s): A NULL pointer was " + "supplied for the array holding the upper bounds of " + "the clipping volume.", status, astGetClass( this ) ); + } + +/* Validate the clipping frame index. */ + ifrm = astValidateFrameIndex( fset, iframe, "astClip" ); + +/* Get the number of axes in the clipping frame. */ + fr = astGetFrame( this, ifrm ); + naxes = astGetNaxes( fr ); + fr = astAnnul( fr ); + + } + +/* Leave the current clipping information unchanged if an error has + occurred. */ + if( astOK ){ + +/* Remove all clipping information from the Plot. */ + this->clip_lbnd = (double *) astFree( (void *) this->clip_lbnd ); + this->clip_ubnd = (double *) astFree( (void *) this->clip_ubnd ); + this->clip_frame = AST__NOFRAME; + this->clip_axes = 0; + +/* If bounds have been supplied, set up new clipping information. */ + if( iframe != AST__NOFRAME ){ + +/* Store the information. */ + this->clip_frame = ifrm; + this->clip_lbnd = astStore( NULL, lbnd, sizeof(double)*(size_t)naxes ); + this->clip_ubnd = astStore( NULL, ubnd, sizeof(double)*(size_t)naxes ); + this->clip_axes = naxes; + +/* If an error has occurred, remove all clipping information. */ + if( !astOK ){ + this->clip_lbnd = (double *) astFree( (void *) this->clip_lbnd ); + this->clip_ubnd = (double *) astFree( (void *) this->clip_ubnd ); + this->clip_frame = AST__NOFRAME; + this->clip_axes = 0; + +/* Otherwise, replace any bounds of AST__BAD with suitable default + values. */ + } else { + for( i = 0; i < naxes; i++ ){ + if( this->clip_lbnd[ i ] == AST__BAD ) this->clip_lbnd[ i ] = -DBL_MAX; + if( this->clip_ubnd[ i ] == AST__BAD ) this->clip_ubnd[ i ] = DBL_MAX; + } + + } + + } + + } + +/* Return. */ + return; + +} + +static int Compared( const void *elem1, const void *elem2 ){ +/* +* Name: +* Compared + +* Purpose: +* Compare two "double" values. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Compared( const void *elem1, const void *elem2 ) + +* Class Membership: +* Plot method. + +* Description: +* This function compares the two "double" values to which pointers +* are supplied, and returns an integer indicating which is larger, +* checking for AST__BAD values. It is intended for use with the C +* Run-Time-Library sorting function "qsort". + +* Parameters: +* elem1 +* Pointer to the first "double". +* elem2 +* Pointer to the second "double". + +* Returned Value: +* Zero is returned if the values are equal. If the first is larger +* than the second then +1 is returned. Otherwise, -1 is returned. + +* Notes: +* - Values of AST__BAD are considered to be larger than any other +* value (other than another value of AST__BAD). +* - If both values are AST__BAD, then zero is returned. +* - This function executes even if an error has occurred. + +*/ + +/* Local Variables: */ + double *delem1; /* Pointer to the first "double" value */ + double *delem2; /* Pointer to the second "double" value */ + int ret; /* The returned value */ + +/* Get pointers to the two "double" values. */ + delem1 = (double *) elem1; + delem2 = (double *) elem2; + +/* Check the values for equality (including both values being AST__BAD). */ + if( *delem1 == *delem2 ){ + ret = 0; + +/* If the first is bad, then it is considered to be larger than the + second. */ + } else if( *delem1 == AST__BAD ){ + ret = 1; + +/* If the second is bad, then it is considered to be larger than the + first. */ + } else if( *delem2 == AST__BAD ){ + ret = -1; + +/* If the first is larger than the second, return 1. */ + } else if( *delem1 > *delem2 ){ + ret = 1; + +/* If the first is smaller than the second, return -1. */ + } else { + ret = -1; + + } + +/* Return the answer. */ + return ret; + +} + +static int Compare_LL( const void *elem1, const void *elem2 ){ +/* +* Name: +* Compare_LL + +* Purpose: +* Compare two LabelList structures as used by function PlotLabels. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Compare_LL( const void *elem1, const void *elem2 ) + +* Class Membership: +* Plot method. + +* Description: +* This function compares two "LabelList" structures as used by function +* PlotLabels, and returns an integer indicating which has a larger +* "index" value. This function is intended to be used with the C +* Run-Time-Library sorting function "qsort". + +* Parameters: +* elem1 +* Pointer to the first LabelList. +* elem2 +* Pointer to the second LabelList. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if the values are equal. If the first is larger +* than the second then +1 is returned. Otherwise, -1 is returned. + +* Notes: +* - This function executes even if an error has occurred. + +*/ + +/* Local Variables: */ + LabelList *ll1; /* Pointer to the first LabelList */ + LabelList *ll2; /* Pointer to the second LabelList */ + int ret; /* The returned value */ + +/* Get pointers to the two LabelList structures. */ + ll1 = (LabelList *) elem1; + ll2 = (LabelList *) elem2; + +/* Compare the indices for the two label's. */ + if( ll1->index < ll2->index ){ + ret = -1; + + } else if( ll1->index > ll2->index ){ + ret = 1; + + } else { + ret = 0; + } + +/* Return the answer. */ + return ret; + +} + +static void CopyPlotDefaults( AstPlot *this, int axis, AstPlot *dplot, + int daxis, int *status ){ +/* +*+ +* Name: +* astCopyPlotDefaults + +* Purpose: +* Copy used attribute defaults from one Plot to another. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "plot.h" +* void astCopyPlotDefaults( AstPlot *this, int axis, AstPlot *dplot, +* int daxis ) + +* Class Membership: +* Plot method. + +* Description: +* Some of the attributes used by the Plot class have dynamic default +* values that are determined during the process of drawing an annotated +* grid using astGrid. The dynamic default values are stored in a +* separate set of components within the Plot structure. This function +* copies these components from one Plot to another. + +* Parameters: +* this +* Pointer to a Plot containing the values ot be copied. +* axis +* The zero-based index of the axis within "this" for which the +* used defaults are to be copied. +* dplot +* A pointer to another Plot into which the default attribute +* values are to be copied. +* daxis +* The zero based index of the axis within "dplot" which is to +* receive the new values. + +*- +*/ + +/* Check the global status. */ + if( !astOK ) return; + + dplot->ulglb[ daxis ] = this->ulglb[ axis ]; + dplot->ulgtk[ daxis ] = this->ulgtk[ axis ]; + dplot->uloggap[ daxis ] = this->uloggap[ axis ]; + dplot->ugap[ daxis ] = this->ugap[ axis ]; + dplot->ucentre[ daxis ] = this->ucentre[ axis ]; + dplot->uedge[ daxis ] = this->uedge[ axis ]; + dplot->ulblat[ daxis ] = this->ulblat[ axis ]; + dplot->ulbunit[ daxis ] = this->ulbunit[ axis ]; + dplot->umintk[ daxis ] = this->umintk[ axis ]; + dplot->utxtlb[ daxis ] = this->utxtlb[ axis ]; + dplot->umjtkln[ daxis ] = this->umjtkln[ axis ]; + + dplot->ugrid = this->ugrid; + dplot->ulbling = this->ulbling; + dplot->uborder = this->uborder; +} + +static int CountGood( int n, double *data, int *status ){ +/* +* Name: +* CountGood + +* Purpose: +* Coount the number of non-bad values in an array. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int CountGood( int n, double *data, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function returns the number of elements in the supplied array +* which do not have the value AST__BAD. + +* Parameters: +* n +* The total number of elements in the array. +* data +* Pointer to the start of the array. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of good points in the array. + +* Notes: +* - A value of zero is returned if an error has already occurred. + +*/ + +/* Local Variables: */ + int i; + int ngood; + double *value; + +/* Check global status. */ + if( !astOK ) return 0; + +/* Initialise a pointer to the next array element, and the number of + good elements found so far. */ + value = data; + ngood = 0; + +/* Check each element. */ + for( i = 0; i < n; i++ ){ + if( *(value++) != AST__BAD ) ngood++; + } + +/* Return the answer. */ + return ngood; + +} + +static int Cross( float ax, float ay, float bx, float by, + float cx, float cy, float dx, float dy, int *status ){ +/* +* Name: +* Cross + +* Purpose: +* See if two line segments intersect. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Cross( float ax, float ay, float bx, float by, +* float cx, float cy, float dx, float dy, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function sees if the line segment (A,B) intersects the line +* segment (C,D). + +* Parameters: +* ax, ay +* The coordinates of A. +* bx, by +* The coordinates of B. +* cx, cy +* The coordinates of C. +* dx, dy +* The coordinates of D. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the line segments do not cross or if an error has already +* occurred, and 1 if they do. + +*/ + +/* Local Variables: */ + int ret; + float m1, m2, denom, num, t1, t2; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Get the fraction of the distance from A to B at which the line AB + intersects the line CD. */ + m1 = dx - cx; + m2 = dy - cy; + denom = (bx - ax)*m2 - (by-ay)*m1; + num = (ay - cy)*m1 - (ax - cx)*m2; + + if( denom != 0.0 ) { + t1 = num / denom; + +/* If the the intersection occurs within the segment of the line between A + and B... */ + if( t1 >= 0.0 && t1 <= 1.0 ){ + +/* ... then get the fraction of the distance from C to D at which the + line CD intersects the line AB. */ + m1 = bx - ax; + m2 = by - ay; + denom = (dx - cx)*m2 - (dy-cy)*m1; + num = (cy - ay)*m1 - (cx - ax)*m2; + + if( denom != 0.0 ) { + t2 = num / denom; + +/* If the the intersection occurs within the segment of the line between C + and D then the line segments intersect. */ + if( t2 >= 0.0 && t2 <= 1.0 ){ + ret = 1; + } else { + ret = 0; + } + +/* If the two lines are parallel, then they do not intersect. */ + } else { + ret = 0; + } + + } else { + ret = 0; + } + + } else { + ret = 0; + + } + + return ret; +} + +static void Crv( AstPlot *this, double *d, double *x, double *y, int skipbad, + double *box, CrvStatics *pstatics, const char *method, + const char *class, int *status ){ +/* +* Name: +* Crv + +* Purpose: +* Draw a curve. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Crv( AstPlot *this, double *d, double *x, double *y, int skipbad, +* double *box, CrvStatics *pstatics, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws a curve parameterised by the distance from some +* starting point. The function pointed to by the external variable +* Crv_map is used to transform distances along the curve into graphics +* coordinates (X,Y). The supplied function parameters defined the +* section of the curve to be drawn. +* +* The algorithm used needs no knowledge about the nature of the mapping +* performed by Crv_map, and can handle discontinuities in the curve. It +* first of all determines if any of the segments of the curve can be +* adequately represented by simply drawing a straight line through the +* supplied end points. This decision is based on several requirements such +* as keeping the angle between adjacent sections low and both ends being +* defined (i.e. X and Y not equal to AST__BAD). Any segments of the curve +* which satisfy the requirements are draw as straight lines. If any of +* the supplied curve segments cannot be drawn in this way, then they are +* split up into a set of evenly-spaced sub-segments and the graphics +* coordinates at the ends of these sub-segments are found using Crv_map. +* This function is then called recursively to draw the sub-segments. This +* recursion is limited in depth by the requirement that all the +* sub-segments must be longer than a specified lower limit. If this is not +* the case, then the curve is assumed to be dis-continuous and and the +* sub-segments are ignored. + +* Parameters: +* d +* Pointer to an array of CRV_NPNT values giving the distance along +* the curve from the starting point to each of CRV_NPNT points. They +* should increase monotonically, and should be in whatever units are +* used by the function pointed to by Crv_map. The curve is drawn from +* d[0] to d[CRV_NPNT]. +* x +* Pointer to an array of CRV_NPNT values giving the graphics X +* coordinate for the positions supplied in the array pointed to by +* parameter "d". +* y +* Pointer to an array of CRV_NPNT values giving the graphics Y +* coordinate for the positions supplied in the array pointed to by +* parameter "d". +* skipbad +* Controls what happens if all the supplied points are bad or +* outside the plotting area. If skipbad is non-zero, then it is +* assumed that the supplied points represent an entirely bad (or +* out of bounds) section of the curve, and this function will +* return without attempt to sub-divide any of the supplied points. +* If skipbad is zero, then it is assumed that we may be able to find +* some good points between the supplied bad points, and therefore +* this function will attempt to sub-divide the supplied points. +* Should be supplied as zero on the initial invocation. +* box +* Pointer to an array of 4 doubles houlding a bounding box within +* which the current segment must reside if it is to be sub-divided. +* Supplied in the order xlo, xhi, ylo, yhi. May be NULL in which +* case, no check is made on the bounding box. +* pstatics +* Pointer to a structure holding values for variables which were +* statically defined within this function prior to the thread-safe +* version of AST. If a NULL pointer is supplied, a new structure +* is created in dynamic memory and initialised. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* External Variables: +* Crv_nent = int (Read/Write) +* The number of recursive entries which have been made into +* this function. This should be set to zero before entering +* this function for the first time. +* Crv_ux0 = double (Read/Write) +* The X component in graphics coordinates of the unit vector +* along the previous segment of the curve. This should be set +* to AST__BAD initially to indicate that the previous section +* is not defined. +* Crv_uy0 = double (Read/Write) +* The Y component of the unit vector along the previous segment. +* Crv_limit = double (Read) +* The square of the maximum acceptable residual between the +* drawn curve and the true curve, in graphics coordinates. +* Crv_scerr = double (Read) +* If the ratio of the lengths of adjacent sub-segments is larger +* than Crv_scerr,then the seub-segments will be sub-divided. Note, +* if either axis is mapped logarithmically onto the screen, then +* there will naturally be large changes in scale. Crv_scerr should +* always be larger than 1.0. +* Crv_map = void (*)( int n, double *dd, double *xx, double *yy, +* const char *method, const char *class ) (Read) +* A pointer to a function which can be called to map "n" distances +* along the curve (supplied in "dd") into graphics coordinates +* (stored in "xx" and "yy"). See function "Map1" as an example. +* Crv_clip = int (Read) +* Should lines be clipped at the edge of the plotting area? + +* Notes: +* - The CRV_TRACE conditional compilation blocks in this function +* provide code which displays the recursive entries made to this +* function (and also pauses on initial entry until return is pressed). +* It is useful for investigating the details of the drawing of a +* curve. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + CrvStatics *statics; /* Pointer to structure holding static values */ + double *dd; /* Pointer to array holding sub-segment distances */ + double *pd; /* Pointer to next sub-segment distance */ + double *px; /* Pointer to next sub-segment x coord. */ + double *py; /* Pointer to next sub-segment y coord. */ + double *xx; /* Pointer to array holding sub-segment x coord.s */ + double *yy; /* Pointer to array holding sub-segment x coord.s */ + double bbox[4]; /* Bounding box for this segment */ + double dl2[ CRV_NSEG ];/* Squred segment lengths */ + double dx[ CRV_NSEG ]; /* X increment along each segment */ + double dy[ CRV_NSEG ]; /* Y increment along each segment */ + int i; /* Segment index */ + int seg_ok[ CRV_NSEG ];/* Flags indicating which segments can be drawn */ + int subdivide; /* Flag indicating if segments can be subdivided */ + +/* Check inherited status */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* If required, allocate memory for a structure to hold the static + variables need by this function. */ + if( ! pstatics ) { + statics = astMalloc( sizeof( CrvStatics ) ); + } else { + statics = pstatics; + } + +/* If this is the first entry, set up the minimum length for a + sub-segment in graphics coordinates. If any segment is less than + this minimum length, then recursion will stop and the curve will + be assumed to be dis-continuous. */ + if( !Crv_nent ) { + statics->limit2 = 20.0*Crv_limit/(CRV_NSEG*CRV_NSEG); + +#ifdef CRV_TRACE + statics->levels[ 0 ] = 0; +#endif + } + + +/* Increment the number of entries into this function. */ + Crv_nent++; + +#ifdef CRV_TRACE + for( i = 0; i < Crv_nent; i++ ) { + printf("%d ",statics->levels[ i ] ); + } + printf("\n"); + + if( getchar() == 'm' ) { + float ffx,ffy; + for( i = 0; i < CRV_NPNT; i++ ) { + ffx = x[i]; ffy = y[i]; + GMark( this, 1, &ffx, &ffy, 2, method, class, status ); + GFlush( this, method, class, status ); + } + } +#endif + +/* ====================================================================== + The first section of this function sets up some arrays holding + information which will be used later on. It looks at each of the segments + joing adjacent tabulated points, and finds and stores the increments in + X and Y along each segment, and the square of the segment length. It + also checks to see if the tabulated points are all bad, or if they are + all good. It also finds the lowest squared segment length. + ======================================================================*/ + +/* Look at the first tabulated point. If it is good, set a flag to indicate + that it can be used, store it as "the previous position" (i.e. the start of + the current segment). Also set a flag ("all_bad") to indicate if all + points looked at so far have been bad, or outside the plotting area. */ + if( *x != AST__BAD && *y != AST__BAD ){ + statics->last_ok = 1; + statics->last_x = *x; + statics->last_y = *y; + statics->all_bad = ( *x < Crv_xlo || *x > Crv_xhi || + *y < Crv_ylo || *y > Crv_yhi ) && Crv_clip; + } else { + statics->last_ok = 0; + statics->all_bad = 1; + } + +/* Initialise the bouding box for the this segment. */ + bbox[ 0 ] = DBL_MAX; + bbox[ 1 ] = -DBL_MAX; + bbox[ 2 ] = DBL_MAX; + bbox[ 3 ] = -DBL_MAX; + +/* Store pointers to the X and Y values for the "current position". This + is the position at the end of the current segment. This is initially + the second tabulated point. */ + px = x + 1; + py = y + 1; + +/* Store pointers to the increments and squared length for the current + segment. */ + statics->pdx = dx; + statics->pdy = dy; + statics->pdl2 = dl2; + +/* Initialise the number of long and short segments. */ + statics->nlong = 0; + statics->nshort = 0; + +/* Loop round each segment. */ + for( i = 0; i < CRV_NSEG; i++ ){ + +/* If the tabulated point marking the end of the segment is good... */ + if( *px != AST__BAD && *py != AST__BAD ){ + +/* Update the bounding box. */ + if( *px < bbox[ 0 ] ) bbox[ 0 ] = *px; + if( *px > bbox[ 1 ] ) bbox[ 1 ] = *px; + if( *py < bbox[ 2 ] ) bbox[ 2 ] = *py; + if( *py > bbox[ 3 ] ) bbox[ 3 ] = *py; + +/* If the point is within the plotting area, set the "statics->all_bad" flag to + indicate that at least 1 point is within the plotting area. */ + if( !Crv_clip || ( *px >= Crv_xlo && *px <= Crv_xhi && + *py >= Crv_ylo && *py <= Crv_yhi ) ) statics->all_bad = 0; + +/* If the point marking the start of the segment was also good, find and + store the increments and squared length for the segment, incrementing + the pointers ready for the next segment. */ + if( statics->last_ok ){ + statics->t1 = *px - statics->last_x; + statics->t2 = *py - statics->last_y; + statics->t3 = statics->t1*statics->t1 + statics->t2*statics->t2; + *(statics->pdx++) = statics->t1; + *(statics->pdy++) = statics->t2; + *(statics->pdl2++) = statics->t3; + +/* Count the number of segments which are, and are not, shorter than the + minimum significant length. */ + if( statics->t3 > statics->limit2 ) { + statics->nlong++; + } else { + statics->nshort++; + } + +/* If the start was bad, the length of the segment is not defined so store + bad values. */ + } else { + *(statics->pdx++) = AST__BAD; + *(statics->pdy++) = AST__BAD; + *(statics->pdl2++) = AST__BAD; + } + +/* The point at the end of the current segment becomes the point at the + start of the next segment. */ + statics->last_ok = 1; + statics->last_x = *(px++); + statics->last_y = *(py++); + +/* If the tabulated point marking the end of the current segment is bad, + the segment length is undefined so store bad values. */ + } else { + *(statics->pdx++) = AST__BAD; + *(statics->pdy++) = AST__BAD; + *(statics->pdl2++) = AST__BAD; + +/* The point at the end of the current segment becomes the point at the + start of the next segment. */ + statics->last_ok = 0; + px++; + py++; + } + } + +/* ====================================================================== + The next section of this function checks to see lines can be drawn + directly through any of the tabulated points. The flags in "seg_ok" + indicates if this is the case for each segment. + ======================================================================*/ + +/* The unit vector along the previous segment is supplied in external + variables Crv_ux0 and Crv_uy0. These will be AST__BAD if the direction + of the previous segment is undefined. */ + statics->vxl = Crv_ux0; + statics->vyl = Crv_uy0; + +/* The length of the previous segment is initially bad. */ + statics->dll = AST__BAD; + +/* Set up some pointers used to walk through the arrays holding the lengths + of each segment. */ + statics->pdl2 = dl2; + statics->pdx = dx; + statics->pdy = dy; + +/* Check each segment in turn to see if it can be drawn as a single + straight line. */ + for( i = 0; i < CRV_NSEG; i++ ){ + +/* A segment can only be drawn as a single line if both ends are good + and the distance between them is not zero. */ + if( *statics->pdl2 != AST__BAD && *statics->pdl2 > 0.0 ){ + +/* Get a unit vector in the direction of the current segment. */ + statics->dl = sqrt( *statics->pdl2 ); + statics->vx = *statics->pdx/statics->dl; + statics->vy = *statics->pdy/statics->dl; + +/* If a unit vector in the direction of the previous segment is available, + we check that the angle between the previous segment and the current + segment is not too high. */ + if( statics->vxl != AST__BAD ){ + statics->cosang = statics->vxl*statics->vx + statics->vyl*statics->vy; + +/* If the angle is too high, set a flag to indicate that the segment cannot + be drawn as a single line. Also, set the flag for the previous segment as + well. */ + if( statics->cosang < 0.8 || + ( *statics->pdl2 )*( 1.0 - statics->cosang*statics->cosang ) > Crv_limit ) { + seg_ok[ i ] = 0; + if( i > 0 ) seg_ok[ i - 1 ] = 0; + + +/* If the angle between this segment and the previous segment is not too + high, check that the scale has not changed too much. */ + } else { + +/* If the scale (=vector length) has changed a lot, set a flag to indicate + that the segment cannot be drawn as a single line. Also, set the flag for + the previous segment as well. */ + if( statics->dll != AST__BAD && ( statics->dl < statics->dll/Crv_scerr || statics->dl > statics->dll*Crv_scerr ) ) { + seg_ok[ i ] = 0; + if( i > 0 ) seg_ok[ i - 1 ] = 0; + +/* If the orientation and scale of this segment has not changed much from + the previous segment, the segment can be drawn as a straight line. */ + } else { + seg_ok[ i ] = 1; + } + } + +/* If no unit vector is available for the previous segment, then assume + we are re-starting the curve after a discontinuity. In this case, we + can draw it as a straight line. */ + } else { + seg_ok[ i ] = 1; + } + +/* Save the unit vector along the current segment for use next time. */ + statics->vxl = statics->vx; + statics->vyl = statics->vy; + +/* Save the length if the current segment for use next time. */ + statics->dll = statics->dl; + +/* If the length of the current segment is undefined, or zero, we cannot + draw it as a single line. Also, there is no direction vector to pass + on to the next time, so set them bad. */ + } else { + seg_ok[ i ] = 0; + statics->vxl = AST__BAD; + statics->vyl = AST__BAD; + statics->dll = AST__BAD; + } + +/* Point to the next segment. */ + statics->pdl2++; + statics->pdx++; + statics->pdy++; + + } + +/* Do not allow isolated segments to be OK. If a segment is flagged as being + OK, but both its neighbours are not OK, set the segment not OK as well. */ + statics->seg0 = seg_ok + 1; + statics->segm = seg_ok; + statics->segp = seg_ok + 2; + + if( !(*statics->seg0) ) *statics->segm = 0; + + for( i = 1; i < CRV_NSEG - 1; i++ ){ + if( !(*statics->segm) && !(*statics->segp) ) *statics->seg0 = 0; + statics->seg0++; + statics->segm++; + statics->segp++; + } + + if( !(*statics->segm) ) *statics->seg0 = 0; + +/* ====================================================================== + The next section of this function draws the curve. Each segment is drawn + as a straight line if the corresponding flag in "seg_ok" is set. + Segments for which the flag is not set are drawn by calling this function + recursivly. + ======================================================================*/ + +/* Get the parametric length (i.e. the increment in "d") of the sub-segments + within each subdivided segment. */ + statics->delta = ( d[ CRV_NSEG ] - d[ 0 ] )/(double)( CRV_NSEG*CRV_NSEG ); + +/* If we have made the maximum number of recursive entries into this + function, or if every supplied point was bad or outside the plotting + area, or if most of the segments were very short in graphics space, we will + not be attempting to subdivide any segments which cannot be drawn directly + as a straight line. If "skipbad" was supplied as zero, we ignore the + restriction which says that we must have some good points (since we + may find some good poits by a further sub-division). */ + subdivide = ( Crv_nent < CRV_MXENT && + ( !statics->all_bad || !skipbad ) && + statics->nlong > statics->nshort ); + +/* We do not sub-divide if the bounding box of the supplied points + is not at least 10% smaller than the supplied bouding box on either axis. */ + if( box && bbox[ 0 ] != DBL_MAX ) { + if( bbox[ 1 ] - bbox[ 0 ] > 0.9*( box[ 1 ] - box[ 0 ] ) && + bbox[ 3 ] - bbox[ 2 ] > 0.9*( box[ 3 ] - box[ 2 ] ) ) { + subdivide = 0; + } + } + +/* Initialise some pointers to the data defineding the subsegments. */ + dd = NULL; + xx = NULL; + yy = NULL; + +/* If we may be subdividing any segments, find which segments they are + and set up the offset to each sub-segment. */ + if( subdivide ){ + +/* Initialise the number of segments being subdivided. */ + statics->nseg = 0; + +/* Loop round each segment. */ + for( i = 0; i < CRV_NSEG; i++ ){ + +/* If the segment cannot be drawn directly as a straight line, we will + subdivide it. */ + if( !seg_ok[ i ] ){ + +/* Increment the number of segments being subdivided, and let the array + of subsegment offsets grow to accomodate it. */ + statics->nseg++; + dd = (double *) astGrow( dd, statics->nseg, sizeof(double)*( CRV_NSEG + 1 ) ); + if( !astOK ) break; + +/* Append the offset to each new subsegment to the "dd" array. */ + statics->el = ( statics->nseg - 1 )*( CRV_NSEG + 1 ); + statics->d0 = d[ i ]; + for( statics->j = 0; statics->j <= CRV_NSEG; statics->j++ ){ + dd[ statics->el++ ] = statics->d0; + statics->d0 += statics->delta; + } + } + } + +/* If any segments needed subdividing, get room to store the graphics + coordinates at each point, and then fill these arrays by calling + Crv_map to map the offsets in "dd" into graphics coordinates. */ + if( statics->nseg > 0 ){ + statics->nel = statics->nseg*( CRV_NSEG + 1 ); + xx = (double *) astMalloc( sizeof(double)*(size_t)statics->nel ); + yy = (double *) astMalloc( sizeof(double)*(size_t)statics->nel ); + Crv_map( statics->nel, dd, xx, yy, method, class, status GLOBALS_NAME ); + } + } + +/* If all has gone OK, we will draw each segment. Initialise pointers + used to walk through the "xx", "yy" and "dd" arrays. */ + if( astOK ){ + px = xx; + py = yy; + pd = dd; + +/* Draw each segment in turn. */ + for( i = 0; i < CRV_NSEG; i++ ){ + +/* If possible, draw it as a single straight line, and then store the + unit vector along the line in the appropriate external variables for + use by the next invocation of this function. */ + if( seg_ok[ i ] ){ + CrvLine( this, x[ i ], y[ i ], x[ i + 1 ], y[ i + 1 ], method, class, status ); + statics->dl = sqrt( dl2[ i ] ); + Crv_ux0 = dx[ i ]/statics->dl; + Crv_uy0 = dy[ i ]/statics->dl; + +/* Otherwise, if we are subdividing, and if the current segment is + not very short, we call this function recursively to draw the segment. + Increment pointers into the "xx", "yy" and "dd" arrays so that they + point to the start of the subsegment information for the next segment + to be subdivided. If all the graphics positions at this level were + bad or outside the plot, tell the next invocation of Crv to do no + further sub-divisions if it too finds all graphics positions to be bad or + outside the plot. */ + } else if( subdivide ) { + +#ifdef CRV_TRACE + statics->levels[ Crv_nent ] = i; +#endif + + Crv( this, pd, px, py, statics->all_bad, bbox, statics, method, + class, status ); + pd += CRV_NSEG + 1; + px += CRV_NSEG + 1; + py += CRV_NSEG + 1; + +/* Otherwise, we assume we have hit a discontinuity in the curve. Store + bad values for the unit vector along the previous sgment, and do not + draw anything. */ + } else { + Crv_ux0 = AST__BAD; + Crv_uy0 = AST__BAD; + } + } + } + +/* Free any memory used to store subsegment information. */ + if( dd ) dd = (double *) astFree( (void *) dd ); + if( xx ) xx = (double *) astFree( (void *) xx ); + if( yy ) yy = (double *) astFree( (void *) yy ); + +/* Decrement the number of recursive entries into this function. */ + Crv_nent--; + +/* Free the memory holding the static data values if we are leaving the + final entry. */ + if( ! pstatics ) statics = astFree( statics ); + +/* Return. */ + return; +} + +static int CvBrk( AstPlot *this, int ibrk, double *brk, double *vbrk, + double *len, int *status ){ +/* +*+ +* Name: +* astCvBrk + +* Purpose: +* Return information about breaks in the last curve drawn by astGridLine, +* astCurve or astGenCurve. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "plot.h" +* int CvBrk( AstPlot *this, int ibrk, double *brk, double *vbrk, +* double *len ) + +* Class Membership: +* Plot method. + +* Description: +* Curves drawn by astGridLine, astCurve or astGenCurve may contain breaks +* for several reasons (for instance, it may go outside the plotting area, +* or the mapping between physical and graphics coordinates may be +* discontinuous). This function returns information about such breaks. + +* Parameters: +* this +* Pointer to a Plot. +* ibrk +* The index of the break for which information is required. The first +* break has index 1. An error is reported if no break with the +* required index exists. The exception to this is that zero can be +* supplied, in which case the "brk" and "vbrk" parameters +* are ignored, but all other information is returned. +* brk +* A pointer to an array of 2 elements +* in which to return the X and Y graphics coordinates of the break. +* vbrk +* A pointer to an array of 2 elements +* in which to return the X and Y components of a unit vector in the +* graphics coordinate system. The vector is tangential to the curve +* at the requested break, and points back along the drawn section of +* the curve. +* len +* A pointer to a "double" in which to return the +* length of the drawn curve, in the graphics coordinate system. + +* Returned Value: +* astCvBrk() +* The number of breaks which occurred in the curve. + +* Notes: +* - Currently, this function may not be used to return information +* about curves drawn using astPolyCurve. +* - All curves contain at least two breaks; one at the start and one +* at the end. This is true even if the start and end of the curve are +* coincident. However, if the entire curve was outside the plotting area +* (i.e. if the length of the drawn curve is zero), then it will have no +* breaks. +* - If no curve has yet been drawn by astGridLine or astCurve, then -1 is +* returned for the function value, and the function parameter values are +* left unchanged. +* - The returned information refers to the most recent curve drawn by +* astGridLine or astCurve, even if that curve was drawn by a Plot other than +* the one supplied to this function. +* - NULL pointers may be supplied for "brk", "vbrk" or "len", in which +* case the corresponding values are not returned. +* - Zero is returned by this function if an error has already occurred, +* or if this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int ret; /* The number of breaks in the curve. */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Information about the most recent curve drawn by astGridLine or astCurve is + stored in the external structure "Curve_data". Get the number of breaks + in the last curve. This is initialised to -1 in astInitPlot when the + virtual function table for this class is initialised. */ + ret = Curve_data.nbrk; + +/* If a curve has been drawn, store the length of the drawn curve if + required. */ + if( ret != -1 ){ + if( len ) *len = (double) Curve_data.length; + +/* If a legal break index has been supplied, return the position and + direction at the requested break (if required). */ + if( ibrk > 0 && ibrk <= ret ){ + if( brk ){ + brk[ 0 ] = (double) Curve_data.xbrk[ ibrk - 1 ]; + brk[ 1 ] = (double) Curve_data.ybrk[ ibrk - 1 ]; + } + if( vbrk ){ + vbrk[ 0 ] = (double) Curve_data.vxbrk[ ibrk - 1 ]; + vbrk[ 1 ] = (double) Curve_data.vybrk[ ibrk - 1 ]; + } + +/* If an illegal break index has been supplied (other than zero), report + an error, and set the number of breaks to zero. */ + } else if( ibrk ){ + if( ret > 0 ){ + astError( AST__BDBRK, "astCvBrk(%s): The supplied break index " + "(%d) should be in the range [1,%d].", status, astGetClass(this), + ibrk, ret ); + ret = 0; + } else { + astError( AST__BDBRK, "astCvBrk(%s): The most recent curve " + "plotted by method astGridLine or astCurve had no breaks.", status, + astGetClass(this) ); + } + } + } + +/* If an error has occurred, return 0. */ + if( !astOK ) ret = 0; + +/* Return the result. */ + return ret; + +} + +static void CrvLine( AstPlot *this, double xa, double ya, double xb, double yb, + const char *method, const char *class, int *status ){ +/* +* Name: +* CrvLine + +* Purpose: +* Draw a straight line between two points, with clipping. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void CrvLine( AstPlot *this, double xa, double ya, double xb, double yb, +* const char *method, const char *class ) + +* Class Membership: +* Plot member function. + +* Description: +* This functions draws a straight line from (xa,ya) to (xb,yb), breaking +* the line at the edges of the plotting area defined by Crv_xlo, Crv_xhi, +* Crv_ylo and Crv_yhi if Crv_clip is non-zero. If the line does not start +* at the end of the previous line plotted by this function, then +* information describing the break in the curve is stored in external +* arrays. + +* Parameters: +* xa +* The graphics X coordinate at the start of the line. +* ya +* The graphics Y coordinate at the start of the line. +* xb +* The graphics X coordinate at the end of the line. +* yb +* The graphics Y coordinate at the end of the line. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. + +* External Variables: +* Crv_ink = int (Read) +* If zero then no line is drawn even if the line intersects the +* plotting space, but break information (etc) is still returned. +* Crv_clip = double (Read) +* Clip lines at boundary of plotting space? +* Crv_xlo = double (Read) +* Lower x limit of the plotting space. +* Crv_xhi = double (Read) +* Upper x limit of the plotting space. +* Crv_ylo = double (Read) +* Lower y limit of the plotting space. +* Crv_yhi = double (Read) +* Upper y limit of the plotting space. +* Crv_tol = double (Read) +* The tolerance for determining if 2 points are coincident. +* Crv_out = int (Read/Write) +* Returned as zero if the line intersects the plotting space. +* Unchanged otherwise. +* Crv_xbrk = float * (Read/Write) +* Pointer to the next available element in an array of AST__PLOT_CRV_MXBRK +* values containing the graphics X coordinates at which each break +* in the plotted curve occurred. A break is recorded if the starting +* point of the current line is not the same as the end point of +* the previous line. +* Crv_ybrk = float * (Read/Write) +* Pointer to the next available element in an array of AST__PLOT_CRV_MXBRK +* values containing the graphics Y coordinates at which each break +* in the plotted curve occurred. +* Crv_vxbrk = float * (Read/Write) +* Pointer to the next available element in an array of AST__PLOT_CRV_MXBRK +* values containing the X component of the unit vector (within the +* graphics coordinate system) parallel to the tangent to the curve +* at each break. The sense is such that the vector always points back +* along the plotted section of the curve. +* Crv_vybrk = float * (Read/Write) +* Pointer to the next available element in an array of AST__PLOT_CRV_MXBRK +* values containing the Y component of the unit vector parallel to +* the tangent to the curve at each break. +* Crv_nbrk = int (Write) +* The number of breaks for which information is returned in Crv_xbrk, +* etc. +* Crv_len = float (Write) +* The length of the section of the curve which has been drawn so far. +* Crv_xl = double (Write) +* The graphics X coordinate at the end of the last line drawn. +* Crv_yl = double (Write) +* The graphics Y coordinate at the end of the last line drawn. +* Crv_vxl = double (Write) +* The X component of the unit vector along the last line drawn. +* Crv_vyl = double (Write) +* The Y component of the unit vector along the last line drawn. +* Grf_alpha = float (Read) +* The factor for scaling graphics X axis values into equal scaled +* X axis values. +* Grf_beta = float (Read) +* The factor for scaling graphics Y axis values into equal scaled +* Y axis values. + +*/ + +/* local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + double a1; /* Distance from B to the lower x boundary */ + double a2; /* Distance from B to the upper x boundary */ + double a3; /* Distance from B to the lower y boundary */ + double a4; /* Distance from B to the upper y boundary */ + double aamax; /* Distance from supplied point B to the plotable point A */ + double aamin; /* Distance from supplied point B to the plotable point B */ + double dl; /* Length of plotted line segment */ + double dx; /* Difference in x between supplied points */ + double dy; /* Difference in y between supplied points */ + double t; /* Temporary storage */ + double xam; /* Modified xa position */ + double xbm; /* Modified xb position */ + double yam; /* Modified ya position */ + double ybm; /* Modified yb position */ + int plot; /* True if a line can be plotted */ + +/* Check inherited global status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + dl = 0.0; + xam = 0.0; + xbm = 0.0; + yam = 0.0; + ybm = 0.0; + +/* Store the shifts in x and y. */ + dx = xb - xa; + dy = yb - ya; + +/* Do nothing if the line is of zero length. */ + if( dx == 0.0 && dy == 0.0 ) return; + +/* If either end is outside the zone, replace the given coordinates with + the end coordinates of the section of the line which lies within the + zone. */ + if( Crv_clip && ( xa < Crv_xlo || xa > Crv_xhi || + xb < Crv_xlo || xb > Crv_xhi || + ya < Crv_ylo || ya > Crv_yhi || + yb < Crv_ylo || yb > Crv_yhi ) ){ + +/* Find the distance from point B towards point A at which the + line cuts the two x bounds of the zone (distance at point B is + 0.0, and at point A is 1.0). */ + if( dx != 0.0 ){ + a1 = ( xb - Crv_xlo )/dx; + a2 = ( xb - Crv_xhi )/dx; + +/* Ensure that a1 represents the highest plottable offset, and a2 the + lowest. */ + if( a1 < a2 ){ + t = a1; + a1 = a2; + a2 = t; + } + +/* If the line joining A and B is vertical... */ + } else { + +/* If the line is within the plottable x range, indicate that all + offsets are plottable (as far as the x range is concerned at least). */ + if( ( xa > Crv_xlo || astEQUALS( xa, Crv_xlo, 1.0E8 ) ) && + ( xa < Crv_xhi || astEQUALS( xa, Crv_xhi, 1.0E8 ) ) ){ + a1 = DBL_MAX; + a2 = -DBL_MAX; + +/* If the line is ouside the plottable x range, indicate that no + offsets are plottable. */ + } else { + a1 = 0.0; + a2 = 0.0; + } + } + +/* Find the fractional distance from point A to point B at which the + line cuts the two y bounds of the zone. */ + if( dy != 0.0 ){ + a3 = ( yb - Crv_ylo )/dy; + a4 = ( yb - Crv_yhi )/dy; + +/* Ensure that a3 represents the highest plottable offset, and a4 the + lowest. */ + if( a3 < a4 ){ + t = a3; + a3 = a4; + a4 = t; + } + +/* If the line joining A and B is horizontal... */ + } else { + +/* If the line is within the plottable y range, indicate that all + offsets are plottable (as far as the y range is concerned at least). */ + if( ( ya > Crv_ylo || astEQUALS( ya, Crv_ylo, 1.0E8 ) ) && + ( ya < Crv_yhi || astEQUALS( ya, Crv_yhi, 1.0E8 ) ) ){ + a3 = DBL_MAX; + a4 = -DBL_MAX; + +/* If the line is ouside the plottable y range, indicate that no + offsets are plottable. */ + } else { + a3 = 0.0; + a4 = 0.0; + } + } + +/* Find the fractional distances from point A to point B at the ends + of the plotable line. */ + aamin = astMIN( 1.0, astMAX( 0.0, astMAX( a2, a4 ) ) ); + aamax = astMAX( 0.0, astMIN( 1.0, astMIN( a1, a3 ) ) ); + +/* Store the end coordinates of the line joining the plotable points. */ + if( aamax > aamin ){ + xam = xb - aamax*dx; + yam = yb - aamax*dy; + xbm = xb - aamin*dx; + ybm = yb - aamin*dy; + plot = 1; + +/* Get the unit vector along the line and the length of the plotted section. */ + dx *= Grf_alpha; + dy *= Grf_beta; + dl = sqrt( dx*dx + dy*dy ); + dx /= dl; + dy /= dl; + dl *= astMAX( 0.0, aamax - aamin ); + +/* Clear the "plot" flag if the line does not intersect the plotting area. */ + } else { + plot = 0; + } + +/* If both ends of the line are within the plotting zone, draw the whole + line between the supplied end points. */ + } else { + xam = xa; + yam = ya; + xbm = xb; + ybm = yb; + plot = 1; + +/* Get the length of the line and the unit vector along the line. */ + dx *= Grf_alpha; + dy *= Grf_beta; + dl = sqrt( dx*dx + dy*dy ); + dx /= dl; + dy /= dl; + } + +/* If a line is to be plotted... */ + if( plot ){ + +/* If this is the first line to be plotted in the current curve, save + the start of the line as a break, and indicate that some of the curve + falls within the plotting zone. */ + if( Crv_out ){ + Crv_nbrk = 1; + *(Crv_xbrk++) = (float) xam; + *(Crv_ybrk++) = (float) yam; + *(Crv_vxbrk++) = (float) dx; + *(Crv_vybrk++) = (float) dy; + Crv_out = 0; + +/* Set the length of the curve plotted so far to the length of this first + segment. */ + Crv_len = (float) dl; + +/* Start a poly line. */ + if( Crv_ink ) Bpoly( this, (float) xam, (float) yam, status ); + +/* If this is not the first line to be plotted... */ + } else { + +/* ... increment the length of the curve plotted so far. */ + Crv_len += (float) dl; + +/* If the start of this line is not coincident with the end + of the previous line, save the previous and current positions as + breaks in the curve. Note, the previous vector is reversed so that + it points back towards the drawn section of the curve. Report an + error if the arrays are full. */ + if( fabs( xam - Crv_xl ) > Crv_tol || + fabs( yam - Crv_yl ) > Crv_tol ){ + Crv_nbrk += 2; + if( Crv_nbrk > AST__PLOT_CRV_MXBRK ){ + astError( AST__CVBRK, "%s(%s): Number of breaks in plotted " + "curve exceeds %d.", status, method, class, AST__PLOT_CRV_MXBRK ); + } else { + *(Crv_xbrk++) = (float) Crv_xl; + *(Crv_ybrk++) = (float) Crv_yl; + *(Crv_vxbrk++) = (float) -Crv_vxl; + *(Crv_vybrk++) = (float) -Crv_vyl; + *(Crv_xbrk++) = (float) xam; + *(Crv_ybrk++) = (float) yam; + *(Crv_vxbrk++) = (float) dx; + *(Crv_vybrk++) = (float) dy; + } + +/* Start a poly line. */ + if( Crv_ink ) Bpoly( this, (float) xam, (float) yam, status ); + } + } + +/* Append a section to the current poly line. */ + if( Crv_ink ) Apoly( this, (float) xbm, (float) ybm, status ); + +/* Save the position and vector at the end of the current line. */ + Crv_xl = xbm; + Crv_yl = ybm; + Crv_vxl = dx; + Crv_vyl = dy; + } + +/* Return. */ + return; +} + + +static void Curve( AstPlot *this, const double start[], + const double finish[], int *status ){ +/* +*++ +* Name: +c astCurve +f AST_CURVE + +* Purpose: +* Draw a geodesic curve. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astCurve( AstPlot *this, const double start[], +c const double finish[] ) +f CALL AST_CURVE( THIS, START, FINISH, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function draws a geodesic curve between two points in the +f This routine draws a geodesic curve between two points in the +* physical coordinate system of a Plot. The curve drawn is the +* path of shortest distance joining the two points (as defined by +c the astDistance function for the current Frame of the Plot). +f the AST_DISTANCE function for the current Frame of the Plot). +* For example, if the current Frame is a basic Frame, then the +* curve joining the two points will be a straight line in physical +* coordinate space. If the current Frame is more specialised and +* describes, for instance, a sky coordinate system, then the +* geodesic curve would be a great circle in physical coordinate +* space passing through the two sky positions given. +* +* Note that the geodesic curve is transformed into graphical +* coordinate space for plotting, so that a straight line in +* physical coordinates may result in a curved line being drawn if +* the Mapping involved is non-linear. Any discontinuities in the +* Mapping between physical and graphical coordinates are +c catered for, as is any clipping established using astClip. +f catered for, as is any clipping established using AST_CLIP. +* +c If you need to draw many geodesic curves end-to-end, then the +c astPolyCurve function is equivalent to repeatedly using +c astCurve, but will usually be more efficient. +f If you need to draw many geodesic curves end-to-end, then the +f AST_POLYCURVE routine is equivalent to repeatedly calling +f AST_CURVE, but will usually be more efficient. +* +c If you need to draw curves which are not geodesics, see astGenCurve +c or astGridLine. +f If you need to draw curves which are not geodesics, see AST_GENCURVE +f or AST_GRIDLINE. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c start +f START( * ) = DOUBLE PRECISION (Given) +* An array, with one element for each axis of the Plot, giving +* the physical coordinates of the first point on the geodesic +* curve. +c finish +f FINISH( * ) = DOUBLE PRECISION (Given) +* An array, with one element for each axis of the Plot, giving +* the physical coordinates of the second point on the geodesic +* curve. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - No curve is drawn if either of the "start" or "finish" arrays +c contains any coordinates with the value AST__BAD. +f - No curve is drawn if either of the START or FINISH arrays +f contains any coordinates with the value AST__BAD. +* - An error results if the base Frame of the Plot is not 2-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*-- +*/ +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const char *class; /* Object class */ + const char *method; /* Current method */ + int naxes; /* No. of axes in the base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astCurve"; + class = astGetClass( this ); + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Initialise the bounding box for primitives produced by this call. */ + if( !Boxp_freeze ) { + Boxp_lbnd[ 0 ] = FLT_MAX; + Boxp_lbnd[ 1 ] = FLT_MAX; + Boxp_ubnd[ 0 ] = FLT_MIN; + Boxp_ubnd[ 1 ] = FLT_MIN; + } + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Draw the curve. The break information is stored in an external structure + where it can be accessed by public methods which return information + about the most recently drawn curve. */ + CurvePlot( this, start, finish, 1, &Curve_data, method, class, status ); + +/* Ensure all lines are flushed to the graphics system. */ + Fpoly( this, method, class, status ); + +/* Return. */ + return; +} + +static void CurvePlot( AstPlot *this, const double *start, const double *finish, + int ink, AstPlotCurveData *cdata, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* CurvePlot + +* Purpose: +* Draw a geodesic curve. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void CurvePlot( AstPlot *this, const double *start, const double *finish, +* int ink, AstPlotCurveData *cdata, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws a geodesic curve between the supplied starting and +* finishing positions. The algorithm used can handle discontinuities in the +* Mapping between the current Frame and graphics coordinates, and +* information describing any breaks in the curve (including the start and +* end of the curve) are returned in the supplied AstPlotCurveData structure. + +* Parameters: +* this +* Pointer to the Plot. +* start +* A pointer to a an array holding the coordinates of the start of the +* curve within the current Frame of the Plot. +* finish +* A pointer to a an array holding the coordinates of the finish of the +* curve within the current Frame of the Plot. +* ink +* If zero, the curve is not actually drawn, but information about +* the breaks is still returned. If non-zero, the curve is also drawn. +* cdata +* A pointer to a structure in which to return information about the +* breaks in the curve. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - No curve is draw if the "start" or "finish" arrays contains any bad +* values, or if a NULL pointer is supplied for "cdata". No errors are +* reported in these cases. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + double d[ CRV_NPNT ]; /* Offsets to evenly spaced points along curve */ + double x[ CRV_NPNT ]; /* X coords at evenly spaced points along curve */ + double y[ CRV_NPNT ]; /* Y coords at evenly spaced points along curve */ + double tol; /* Absolute tolerance value */ + int i; /* Loop count */ + int naxes; /* No. of axes in the base Frame */ + int ok; /* Are all start coords good? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Get the number of axes in the current Frame. */ + naxes = astGetNout( this ); + +/* Check the "start" and "finish" parameter for bad values. */ + ok = 1; + for( i = 0; i < naxes; i++ ) { + if( start[ i ] == AST__BAD || finish[ i ] == AST__BAD ){ + ok = 0; + break; + } + } + +/* Check that the "cdata" pointer can be used. */ + if( !cdata ) ok = 0; + +/* Only proceed if the parameters are OK, and there has been no error. */ + if( ok && astOK ){ + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__CURVE_ID, 1, GRF__LINE, method, class ); + +/* Ensure the globals holding the scaling from graphics coords to equally + scaled coords are available. */ + GScales( this, NULL, NULL, method, class, status ); + +/* Set up the externals used to communicate with the Map3 function... + The number of axes in the physical coordinate system (i.e. the current + Frame). */ + Map3_ncoord = naxes; + +/* A pointer to the Plot, the Curretn Frame, and and Mapping. */ + Map3_plot = this; + Map3_frame = astGetFrame( this, AST__CURRENT ); + Map3_map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* The physical coordinates at the start of the curve. */ + Map3_origin = start; + +/* The physical coordinates at the end of the curve. */ + Map3_end = finish; + +/* The scale factor to convert "dist" values into physical offset values. */ + Map3_scale = astDistance( Map3_frame, start, finish ); + +/* Convert the tolerance from relative to absolute graphics coordinates. */ + tol = astGetTol( this )*astMAX( this->xhi - this->xlo, + this->yhi - this->ylo ); + +/* Now set up the external variables used by the Crv and CrvLine function. */ + Crv_scerr = ( astGetLogPlot( this, 0 ) || + astGetLogPlot( this, 1 ) ) ? 100.0 : 1.5; + Crv_ux0 = AST__BAD; + Crv_tol = tol; + Crv_limit = 0.5*tol*tol; + Crv_map = Map3; + Crv_ink = ink; + Crv_xlo = this->xlo; + Crv_xhi = this->xhi; + Crv_ylo = this->ylo; + Crv_yhi = this->yhi; + Crv_out = 1; + Crv_xbrk = cdata->xbrk; + Crv_ybrk = cdata->ybrk; + Crv_vxbrk = cdata->vxbrk; + Crv_vybrk = cdata->vybrk; + Crv_clip = astGetClip( this ) & 1; + +/* Set up a list of points spread evenly over the curve. */ + for( i = 0; i < CRV_NPNT; i++ ){ + d[ i ] = ( (double) i)/( (double) CRV_NSEG ); + } + +/* Map these points into graphics coordinates. */ + Map3( CRV_NPNT, d, x, y, method, class, status GLOBALS_NAME ); + +/* Use Crv and Map3 to draw the curve. */ + Crv( this, d, x, y, 0, NULL, NULL, method, class, status ); + +/* End the current poly line. */ + Opoly( this, status ); + +/* Tidy up the static data used by Map3. */ + Map3( 0, NULL, NULL, NULL, method, class, status GLOBALS_NAME ); + +/* If no part of the curve could be drawn, set the number of breaks and the + length of the drawn curve to zero. */ + if( Crv_out ) { + Crv_nbrk = 0; + Crv_len = 0.0F; + +/* Otherwise, add an extra break to the returned structure at the position of + the last point to be plotted. */ + } else { + Crv_nbrk++; + if( Crv_nbrk > AST__PLOT_CRV_MXBRK ){ + astError( AST__CVBRK, "%s(%s): Number of breaks in curve " + "exceeds %d.", status, method, class, AST__PLOT_CRV_MXBRK ); + } else { + *(Crv_xbrk++) = (float) Crv_xl; + *(Crv_ybrk++) = (float) Crv_yl; + *(Crv_vxbrk++) = (float) -Crv_vxl; + *(Crv_vybrk++) = (float) -Crv_vyl; + } + } + +/* Store extra information about the curve in the returned structure, and + purge any zero length sections. */ + if( cdata ){ + cdata->length = Crv_len; + cdata->out = Crv_out; + cdata->nbrk = Crv_nbrk; + PurgeCdata( cdata, status ); + } + +/* Annul the Frame and Mapping. */ + Map3_frame = astAnnul( Map3_frame ); + Map3_map = astAnnul( Map3_map ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__CURVE_ID, 0, GRF__LINE, method, class ); + + } + +/* Return. */ + return; + +} + + +static AstPointSet *DefGap( AstPlot *this, double *gaps, int *ngood, + double *frac, int *inval, const char *method, + const char *class, int *status ){ +/* +* Name: +* DefGap + +* Purpose: +* Find default gap sizes for the tick marks on the axes of a 2-D +* physical coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* AstPointSet *DefGap( AstPlot *this, double *gaps, int *ngood, +* double *frac, int *inval, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function returns default gap sizes for each axis in a 2-D Frame. +* The values are found by first obtaining a grid of points spread over +* the region containing good physical coordinates. The physical +* coordinate values (non-normalized) for each axis are sorted into +* increasing order. +* +* For linearly spaced tick marks, a set of quantile axis values is then +* found, and the median of the gaps between these quantiles is returned +* as the default gap for the axis. +* +* For logarithmically spaced tick marks, the returned gap size is the +* ratio between adjacent tick mark values, chosen to give an optimal +* number of ticks between the maximum and minimum axis values found in +* the grid. + +* Parameters: +* this +* Pointer to a Plot. +* gaps +* Pointer to an array in which to return the default gap value for +* each axis. +* ngood +* Pointer to an array in which toi return the number of good +* values in the returned PointSet for each axis. +* frac +* Pointer to a double in which to return the fraction of the +* plotting area containing good physical coordinates. +* inval +* Pointer to a location at which to return a flag indicating if +* any bad physical coordinates were encountered. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a PointSet holding the physical coordinate values at a +* set of points spread across the plotting area. The values on each +* axis are sorted into increasing order. The values will not have +* been normalized. + +* Notes: +* - The returned PointSet should be annulled when no longer needed. +* - This function assumes that the physical coordinate system is 2 +* dimensional, and it should not be used if this is not the case. +* - Gap sizes of 1.0, zero good points, and a NULL pointer are returned +* if an error has already occurred, or if this function should fail for +* any reason. + +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* Pointer to PointSet holding graphics coords */ + AstPointSet *pset2; /* Pointer to PointSet holding physical coords */ + double **ptr1; /* Pointer to graphics axis values */ + double **ptr2; /* Pointer to physical axis values */ + double dran; /* Dynamic range */ + double maxv; /* Maximum axis value */ + double minv; /* Minimum axis value */ + double qgap[ MAJTICKS_OPT ];/* Gaps between physical coordinate quantiles */ + int dim; /* Dimension of grid */ + int dk; /* The number of points between quantiles */ + int i; /* The quantile index */ + int j; /* Axis index */ + int k; /* Index into the sorted array of axis values */ + int logticks; /* Logarithmically spaced tick marks? */ + int n; /* Target number fo ticks */ + int psize; /* Total number of axis value */ + +/* Initialise the returned values. */ + gaps[ 0 ] = 1.0; + gaps[ 1 ] = 1.0; + ngood[ 0 ] = 0; + ngood[ 1 ] = 0; + *frac = 0.0; + *inval = 0; + +/* Check global status. */ + if( !astOK ) return NULL; + +/* Get two PointSets, one holding a grid of 2D graphics coordinates, + and one holding the corresponding (non-normalized) physical + coordinates. */ + dim = 0; + *frac = GoodGrid( this, &dim, &pset1, &pset2, method, class, status ); + +/* Get pointers to the data values in each PointSet. */ + ptr1 = astGetPoints( pset1 ); + ptr2 = astGetPoints( pset2 ); + +/* Store the number of elements in each PointSet. */ + psize = astGetNpoint( pset1 ); + +/* For each axis... */ + for( j = 0; j < 2 && astOK; j++ ){ + +/* Sort the axis values into increasing order. Any bad values are stored at + the end of the array on return. */ + qsort( (void *) ptr2[ j ], (size_t) psize, sizeof(double), Compared ); + +/* Count the number of non-bad values returned. */ + ngood[ j ] = CountGood( psize, ptr2[ j ], status ); + +/* Set the returned flag to indicate if any bad values were found. */ + if( ngood[ j ] < psize ) *inval = 1; + +/* Report an error if there are too few good points. */ + if( ngood[ j ] < MAJTICKS_OPT ){ + astError( AST__VSMAL, "%s(%s): The range of coordinate values " + "covered by axis %d is too small to plot.", status, method, + class, j + 1 ); + break; + } + +/* Get the maximum and minimum axis value */ + minv = ptr2[ j ][ 0 ]; + maxv = ptr2[ j ][ ngood[ j ] - 1 ]; + +/* See if ticks on this axis are spaced linearly or logarithmically. If a + value has been set for LogTicks used it, otherwise find a default value. + The default is 0 unless LogPlot is non-zero, the axis range does not + encompass zero and and the dynamic range is 90 or more. Set this + default value explicitly so that later functions will pick it up (it will + be cleared at the end of the astGrid function). */ + if( astTestLogTicks( this, j ) ) { + logticks = astGetLogTicks( this, j ); + } else { + logticks = 0; + if( astGetLogPlot( this, j ) && minv*maxv > 0.0 ) { + dran = maxv/minv; + if( dran >= 90.0 || dran <= 1.0/90.0 ) logticks = 1; + } + astSetLogTicks( this, j, logticks ); + } + +/* If no value has been supplied for LogLabel use the value of LogTicks + as the default. */ + if( !astTestLogLabel( this, j ) ) astSetLogLabel( this, j, logticks ); + +/* For linear gaps, find the gaps between adjacent evenly spaced quantiles. + The number of quantiles used equals the optimal number of major tick + marks. */ + if( !logticks ) { + dk = (int)( (double)ngood[ j ]/MAJTICKS_OPT ); + i = 0; + for( k = dk; k < ngood[ j ] && i < MAJTICKS_OPT; k += dk ){ + qgap[ i++ ] = ptr2[ j ][ k ] - ptr2[ j ][ k - dk ]; + } + +/* Find the median of the gaps between adjacent quantiles. */ + qsort( (void *) qgap, (size_t) i, sizeof(double), Compared ); + gaps[ j ] = qgap[ i/2 ]; + +/* If the test gap size is zero, use a fraction of the total range. Report + an error if the total range is zero. */ + if( gaps[ j ] <= 0.0 ){ + gaps[ j ] = ( ptr2[ j ][ ngood[ j ] - 1 ] - ptr2[ j ][ 0 ] )/MAJTICKS_OPT;; + if( gaps[ j ] <= 0.0 ){ + astError( AST__VSMAL, "%s(%s): The range of coordinate values " + "covered by axis %d is too small to plot.", status, method, + class, j + 1 ); + } + } + +/* For logarithmic gaps, use the Nth root of the ratio of the maximum and + minimum data value found. */ + } else if( astOK ) { + +/* Report an error if the max and min values are of opposite signs or + zero or equal. */ + if( maxv*minv <= 0.0 ) { + astError( AST__ZERAX, "%s(%s): The range of coordinate values " + "covered by axis %d includes the origin and so " + "logarithmic ticks cannot be produced.", status, method, + class, j + 1 ); + + } else if( maxv == minv ) { + astError( AST__VSMAL, "%s(%s): The range of coordinate values " + "covered by axis %d is too small to plot.", status, method, + class, j + 1 ); + +/* Otherwise find the gap to use. */ + } else { + +/* Store the maximum and minimum number of major tick marks along each + axis. These numbers are reduced if only a small part of the plotting + area contains valid coordinates, so that the tick marks do not end up + to close together. */ + n = (int) ( 0.5 + MAJTICKS_OPT*sqrt( *frac ) ); + if( n < 5 ) n = 5; + +/* Choose a gap size which makes this many gaps. */ + gaps[ j ] = pow( maxv/minv, 1.0/( n - 1.0 ) ); + } + } + } + +/* Annul the PointSet holding Graphics coordinates. */ + pset1 = astAnnul( pset1 ); + +/* If an error has occurred, annul the PointSet holding physical + coordinates, and return gaps of 1.0. */ + if( !astOK ) { + pset2 = astAnnul( pset2 ); + gaps[ 0 ] = 1.0; + gaps[ 1 ] = 1.0; + ngood[ 0 ] = 0; + ngood[ 1 ] = 0; + *frac = 0.0; + *inval = 0; + } + +/* Return the physical PointSet. */ + return pset2; + +} + +static void DrawAxis( AstPlot *this, TickInfo **grid, double *labelat, + double *gap, const char *method, const char *class, int *status ){ +/* +* +* Name: +* DrawAxis + +* Purpose: +* Draw a curve joining the major tick marks. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void DrawAxis( AstPlot *this, TickInfo **grid, double *labelat, +* double *gap, const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws a curve through interior tick marks on both axes. +* The curve is drawn even if it has already been drawn as part of a +* grid of curves, because it may have been assigned different graphics +* attributes to the grid curves. + +* Parameters: +* this +* A pointer to the Plot. +* grid +* A pointer to an array of two TickInfo pointers (one for each axis), +* each pointing to a TickInfo structure holding information about +* tick marks on the axis. See function GridLines. +* labelat +* A pointer to a 2 element array giving the constant axis values at +* which tick marks are put. Element 0 should give the axis 1 value at +* which tick marks for axis 0 are placed. Element 1 should give the +* axis 0 value at which tick marks for axis 1 are placed. +* gap +* Pointer to array of two values holding the gap between major +* tick marks on the two axes. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function assumes the current Frame of the Plot is 2 +* dimensional, and it should not be called if this is not the case. + +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to current Frame */ + AstPlotCurveData cdata;/* Somewhere to put the unneeded curve information */ + TickInfo *info; /* Pointer to the TickInfo for the current axis */ + double *value; /* Current tick value */ + double bot; /* Lowest axis value to be displayed */ + double diff; /* Difference between adjacent tick marks */ + double udiff; /* Used section length */ + double start[ 2 ]; /* The start of the curve in physical coordinates */ + double tmp; /* Temporary storage */ + double top; /* Highest axis value to be displayed */ + int axis; /* Current axis index */ + int axisid; /* ID value for current axis plotting attributes */ + int logticks; /* Are major ticks spaced logarithmically? */ + int tick; /* Current tick index */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Not the id value for the first axis. */ + axisid = AST__AXIS1_ID; + +/* Get a pointer to the current Frame. */ + frm = astGetFrame( this, AST__CURRENT ); + +/* Consider drawing a curve parallel to each axis in turn. */ + for( axis = 0; axis < 2; axis++ ){ + +/* Establish the correct graphical attributes for this axis as defined by + attributes with the supplied Plot. */ + astGrfAttrs( this, axisid, 1, GRF__LINE, method, class ); + +/* Check the axis is required. */ + if( astGetDrawAxes( this, axis ) ){ + +/* If the tick marks have been placed round the edges of the plotting + area, we do not need to draw the curves. */ + if( labelat[ axis ] != AST__BAD ){ + +/* Get the max and min values allowed on this axis. */ + bot = astGetBottom( frm, axis ); + top = astGetTop( frm, axis ); + if( bot > top ) { + tmp = top; + top = bot; + bot = tmp; + } + +/* Get a pointer to the structure containing information describing the + positions of the major tick marks along the current axis. */ + info = grid[ axis ]; + +/* Get a pointer to the axis value at the first major tick mark. */ + value = info->ticks; + +/* See if the major tick marks are logarithmically spaced on this axis. */ + logticks = astGetLogTicks( this, axis ); + +/* Initialise the difference between major tick marks. */ + diff = logticks ? 0.0 : gap[ axis ]; + +/* Loop round all ticks. */ + for( tick = 0; tick < info->nmajor; tick++, value++ ){ + +/* Update the difference between major tick marks if we are producing + logarithmically spaced ticks (in which "gap" is a ratio, not a + difference). */ + if( logticks ) diff = (*value)*( gap[ axis ] - 1.0 ); + +/* Note the starting point for this section. */ + start[ axis ] = *value; + start[ 1 - axis ] = labelat[ axis ]; + +/* If this is the first tick, draw an axis section going "backwards" in + case the first tick isn't at the lower visible bound. Limit the length + of this backwards section so that it does not extend beyond the minimum + axis value. */ + if( tick == 0 ) { + udiff = *value - bot; + if( udiff > diff ) udiff = diff; + if( udiff > 0.0 ) { + AxPlot( this, axis, start, -udiff, 1, &cdata, method, + class, status ); + } + } + +/* Limit the length of the section so that it does not extend beyond the + maximum axis value. */ + udiff = ( *value + diff > top ) ? top - *value : diff; + +/* Do not draw zero length sections. */ + if( udiff > 0.0 ) { + +/* Draw a curve parallel to the current axis, starting at the tick mark, + with length equal to the gap between tick marks. Do not draw sections + of the curve which are outside the primary domains of the physical axes. */ + AxPlot( this, axis, start, udiff, 1, &cdata, method, + class, status ); + } + + } + +/* Once the last section has been drawn, draw another axis section in case the + last tick isn't at the upper visible bound. Limit the length of this + section so that it does not extend beyond the maximum axis value. */ + udiff = top - start[ axis ]; + if( udiff > diff ) udiff = diff; + if( udiff > 0.0 ) { + AxPlot( this, axis, start, udiff, 1, &cdata, method, + class, status ); + } + } + } + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, axisid, 0, GRF__LINE, method, class ); + +/* Set up the id value for the next axis. */ + axisid = AST__AXIS2_ID; + + } + +/* Free the pointer to the current Frame. */ + frm = astAnnul( frm ); + +} + + +static AstPlotCurveData **DrawGrid( AstPlot *this, TickInfo **grid, int drawgrid, + const char *method, const char *class, int *status ){ +/* +* Name: +* DrawGrid + +* Purpose: +* Draw a grid of lines at the major tick mark positions on both axes. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* AstPlotCurveData **DrawGrid( AstPlot *this, TickInfo **grid, int drawgrid, +* const char *method, const char *class ) + +* Class Membership: +* Plot method. + +* Description: +* This function draw a grid of curves at the major tick mark +* positions on both axes, and returns information describing the +* breaks in the curves. If short tick marks are required rather +* than long curves (as specified by the Grid attribute of the supplied +* Plot), then the curves are not drawn but the break information is +* still returned. + +* Parameters: +* this +* Pointer to a Plot. +* grid +* A pointer to an array of two pointers (one for each axis), each +* pointing to a TickInfo structure. These describe the positions +* of the tick marks and should have been produced by function +* GridLines. +* drawgrid +* If non-zero, draw a grid of curves marking the major axis +* values. Otherwise, tick marks will be drawn at these values later. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. + +* Returned Value: +* A pointer to an array of two AstPlotCurveData pointers (one for each axis), +* each pointing to an array of AstPlotCurveData structures (one for each tick +* value). + +* Notes: +* - This function assumes that the physical coordinate system is 2 +* dimensional, and it should not be used if this is not the case. +* - If an error has already occurred, or if this function should fail +* for any reason, then a NULL pointer is returned. + +*/ + +/* Local Variables: */ + AstPlotCurveData **cdata;/* The returned pointer */ + AstPlotCurveData *cdt; /* Pointer to break info. for current tick mark */ + AstPlotCurveData tcdt; /* Pointer to break info. for current curve section */ + TickInfo *info; /* Tick mark information for a single axis */ + double start[ 2 ]; /* Strting position for current curve section */ + double total_length; /* Total curve length for all axis ticks */ + int i; /* Axis index */ + int j; /* Tick mark index */ + int k; /* Curve section index */ + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Allocate memory to hold two pointers, each pointing to an array of + AstPlotCurveData structure. */ + cdata = (AstPlotCurveData **) astMalloc( 2*sizeof( AstPlotCurveData *) ); + +/* If succesful, initialise the pointers. */ + if( astOK ){ + cdata[ 0 ] = NULL; + cdata[ 1 ] = NULL; + +/* Draw the curves marking the major tick values on each axis. If no grid is + required, we still do this in order to get information about the breaks + in the curves which will be used later to decide where to put the labels, + but we use "invisible ink". */ + for( i = 0; i < 2; i++ ){ + +/* Get a pointer to the TickInfo structure for this axis holding information + about where to put tick marks on this axis. */ + info = grid[ i ]; + +/* Allocate memory to hold information describing the breaks in each tick + mark curve. This takes the form of an array of AstPlotCurveData structures, + one for each tick mark. */ + cdata[ i ] = (AstPlotCurveData *) astMalloc( sizeof(AstPlotCurveData)* + (size_t) info->nmajor ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Initialise a pointer to the first AstPlotCurveData structure for this axis. */ + cdt = cdata[ i ]; + total_length = 0.0; + +/* Do each tick mark. */ + for( j = 0; j < info->nmajor; j++ ){ + +/* Store the starting point of the first section of the curve. */ + start[ i ] = (info->ticks)[ j ]; + start[ 1 - i ] = (info->start)[ 0 ]; + +/* Draw the first section of the curve parallel to the other axis, starting + at the values in "start", and extending for a length given in the TickInfo + structure. We use invisible ink if short tick marks are required instead + of a grid of curves. */ + AxPlot( this, 1 - i, start, (info->length)[ 0 ], + drawgrid, cdt, method, class, status ); + +/* Now draw any other sections in the curve. */ + for( k = 1; k < info->nsect; k++ ){ + +/* Modify the starting value on the other axis. The starting value on + the current axis remains set to the tick mark value. */ + start[ 1 - i ] = (info->start)[ k ]; + +/* Draw the curve, the information describing the breaks goes into + temporary storage in the local structure "tcdt". */ + AxPlot( this, 1 - i, start, (info->length)[ k ], + drawgrid, &tcdt, method, class, status ); + +/* Concatenate the break information for this section with the break + information describing the previous sections. */ + AddCdt( cdt, &tcdt, method, class, status ); + + } + +/* Increment the total length of curves drawn for all ticks on this axis. */ + total_length += cdt->length; + +/* Point to the AstPlotCurveData structure for the next tick mark. */ + cdt++; + + } + +/* Report an error if the total length of all curves on this axis is zero. + This can be caused for instance by bugs in the algorithm for finding + major tick values (which may cause AST__BAD tick mark values). */ + if( total_length == 0.0 && astOK ) { + astError( AST__INTER, "%s(%s): No grid curves can be drawn for " + "axis %d.", status, method, class, i + 1 ); + } + + } + + } + + } + +/* If an error has occurred, clean up the returned structures. */ + if( !astOK ) cdata = CleanCdata( cdata, status ); + +/* Return. */ + return cdata; + +} + +static int DrawRegion( AstPlot *this, AstFrame *frm, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* DrawRegion + +* Purpose: +* Draw the outline of a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int DrawRegion( AstPlot *this, AstFrame *frm, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* If the current Frame in the supplied Plot is a Region, this function +* draws a curve marking the outline of the Region. It returns without +* action otherwise. + +* Parameters: +* this +* Pointer to the Plot. +* frm +* Pointer to the current Frame in the Plot (possibly a Region). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if and only if a Region outline was drawn. + +*/ + +/* Local Variables: */ + AstMapping *map; /* Mapping with Region masking included */ + AstPlotCurveData cdata; /* Stores information about curve breaks */ + AstRegion **comps; /* List of component Regions */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + double d[ CRV_NPNT ]; /* Offsets to evenly spaced points along curve */ + double tol; /* Absolute tolerance value */ + double x[ CRV_NPNT ]; /* X coords at evenly spaced points along curve */ + double y[ CRV_NPNT ]; /* Y coords at evenly spaced points along curve */ + int i; /* Loop count */ + int icomp; /* Index of component Region */ + int ncomp; /* Number of component Regions */ + int result; /* The returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Check the current Frame is a Region, and is of a class that implements + the astRegTrace method. */ + if( astIsARegion( frm ) && + astRegTrace( (AstRegion *) frm, 0, NULL, NULL ) ){ + +/* Set up the externals used to communicate with the Map5 function... + The number of axes in the physical coordinate system (i.e. the + Region). */ + Map5_ncoord = astGetNaxes( frm ); + +/* A pointer to the Plot, the Region, and the Mapping. */ + Map5_plot = this; + Map5_region = (AstRegion *) frm; + +/* Also store a pointer to the Mapping, ensuring that the Mapping does + not contain any masking effects from the Region. */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + Map5_map = astRemoveRegions( map ); + map = astAnnul( map ); + +/* Convert the tolerance from relative to absolute graphics coordinates. + Make the tolerance smaller by a factor of 10 because Regions + (specifically Polygonsd) can have very crinkly edges. */ + tol = 0.1* astGetTol( this )*astMAX( this->xhi - this->xlo, + this->yhi - this->ylo ); + +/* Ensure the globals holding the scaling from graphics coords to equally + scaled coords are available. */ + GScales( this, NULL, NULL, method, class, status ); + +/* Now set up the external variables used by the Crv and CrvLine function. */ + Crv_scerr = ( astGetLogPlot( this, 0 ) || + astGetLogPlot( this, 1 ) ) ? 100.0 : 1.5; + Crv_ux0 = AST__BAD; + Crv_tol = tol; + Crv_limit = 0.5*tol*tol; + Crv_map = Map5; + Crv_ink = 1; + Crv_xlo = this->xlo; + Crv_xhi = this->xhi; + Crv_ylo = this->ylo; + Crv_yhi = this->yhi; + Crv_out = 1; + Crv_xbrk = cdata.xbrk; + Crv_ybrk = cdata.ybrk; + Crv_vxbrk = cdata.vxbrk; + Crv_vybrk = cdata.vybrk; + Crv_clip = astGetClip( this ) & 1; + +/* Attempt to split the Region into a set of disjoint component Regions. */ + comps = astRegSplit( (AstRegion *) frm, &ncomp ); + +/* Draw each one. */ + for( icomp = 0; icomp < ncomp; icomp++ ) { + +/* A pointer to the Region. */ + Map5_region = comps[ icomp ]; + +/* Set up a list of points spread evenly over the curve. */ + for( i = 0; i < CRV_NPNT; i++ ){ + d[ i ] = ( (double) i)/( (double) CRV_NSEG ); + } + +/* Map these points into graphics coordinates. */ + Map5( CRV_NPNT, d, x, y, method, class, status GLOBALS_NAME ); + +/* Use Crv and Map5 to draw the curve. */ + Crv( this, d, x, y, 0, NULL, NULL, method, class, status ); + +/* End the current poly line. */ + Opoly( this, status ); + +/* Tidy up the static data used by Map5. */ + Map5( 0, NULL, NULL, NULL, method, class, status GLOBALS_NAME ); + +/* Annul the component Region pointer. */ + comps[ icomp ] = astAnnul( Map5_region ); + } + +/* Free the memory holding the list of component Region pointers. */ + comps = astFree( comps ); + +/* Annul the Mapping. */ + Map5_map = astAnnul( Map5_map ); + +/* Indicate the outline was drawn. */ + result = 1; + } + +/* Return. */ + return result; +} + +static void DrawText( AstPlot *this, int ink, int esc, const char *text, + float x, float y, const char *just, float upx, + float upy, float *xbn, float *ybn, float *drop, + const char *method, const char *class, int *status ){ +/* +* Name: +* DrawText + +* Purpose: +* Draw a character string, potentially including superscripts and +* subscripts. + +* Synopsis: +* #include "plot.h" +* void DrawText( AstPlot *this, int ink, int esc, const char *text, +* float x, float y, const char *just, float upx, +* float upy, float *xbn, float *ybn, float *drop, +* const char *method, const char *class, int *status ) + +* Description: +* This function displays a character string at a given position +* using a specified up-vector, optionally interpreting any Plot escape +* sequences contained within the text. It also returns its bounding +* box. + +* Parameters: +* this +* The plot. +* ink +* If zero, nothing is drawn but the bounding box is still returned. +* esc +* Should escape sequences be interpreted? They will be printed +* literally otherwise. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The graphics X coordinate of the label's reference point. +* y +* The graphics Y coordinate of the label's reference point. +* just +* Pointer to a null-terminated character string identifying the +* reference point for the text being drawn. The first character in +* this string identifies the reference position in the "up" direction +* and may be "M", "B", "C" or "T" (for bottom, baseline, centre or +* top). The second character identifies the side-to-side reference +* position and may be "L", "C" or "R" (for left, centre or right). The +* string is case-insensitive, and only the first two characters are +* significant. +* upx +* The x component of the up-vector for the text. Positive values +* always refer to displacements from left to right on the screen, +* even if the graphics x axis increases in the opposite sense. +* upy +* The y component of the up-vector for the text. Positive values +* always refer to displacements from left to right on the screen, +* even if the graphics y axis increases in the opposite sense. +* xbn +* An array in which is returned the x coordinates at the corners +* of the bounding box. The order is: bottom left, top left, top +* right, bottom right. +* ybn +* An array in which is returned the Y coordinates at the corners +* of the bounding box (see xbn). +* drop +* Address of a float in which to return the distance between the +* bottom of the bounding box and the baseline of normal text. May +* be NULL. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The "B" option for the justification in the "up" direction refers +* to the base-line on which the text is written. Some characters +* ("y", "p", "g", etc) descend below this line. In addition, if the +* supplied text string includes any escape sequences which produce +* sub-scripts, then these will also descend below the base-line. To +* justify the bottom of the entire string (instead of just the +* base-line), specify "M" instead of "B" in the justification string. +* - See function astFindEscape for details of the supported escape +* sequences. +*/ + + +/* Local Variables: */ + astDECLARE_GLOBALS + char *a; + char *lt; + char cc; + const char *lj; + double ncol; + double nfont; + double nsize; + double nstyle; + double nwidth; + float alpha_hi; + float alpha_lo; + float beta_lo; + float beta_hi; + float cy; + float cx; + float dy; + float dx; + float height; + float hmx; + float hmy; + float ly; + float lx; + float rx; + float rlen; + float rise; + float rxu; + float ryu; + float ry; + float tdrop; + float tybn[ 4 ]; + float txbn[ 4 ]; + float ulen; + float uyu; + float uxu; + float uy; + float ux; + float width; + float x0; + float y0; + int estype; + int esval; + int got_esc; + int grfcap; + int i; + int nc; + +/* Check the global error status, and that we have something to plot, and + the reference position is good, and that the up vector is not zero. */ + if ( !astOK || !text || !text[ 0 ] || x == AST__BAD || y == AST__BAD || + ( upx == 0.0 && upy == 0.0 ) ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Initialise variables to avoid compiler warnings. */ + rx = 0.0f; + ry = 0.0f; + x0 = 0.0f; + y0 = 0.0f; + +/* Get an up vector which refers to the graphics coordinates in their correct + senses (the supplied values are reversed if the corresponding axis is + reversed). */ + ux = ( this->xrev ) ? -upx : upx; + uy = ( this->yrev ) ? -upy : upy; + +/* Find a vector which points from left to right along the text + baseline, taking account of any difference in the scales of the x + and y axes (is possible). This also scales the up vector so that it + has a length equal to the height of normal text, and scales the right + vector to have the same length (on the screen) as the up vector. */ + RightVector( this, &ux, &uy, &rx, &ry, method, class, status ); + +/* Create a unit right vector. */ + rlen = sqrt( rx*rx + ry*ry ); + rxu = rx/rlen; + ryu = ry/rlen; + +/* Create a unit up vector. */ + ulen = sqrt( ux*ux + uy*uy ); + uxu = ux/ulen; + uyu = uy/ulen; + +/* Some older GRF modules cannot plot strings with vertical justification + of "M". Check the capabilities of the grf module, and, if necessary, + modify the justification and the coords of the reference point to use + "B" instead of "M". This call also returns us the coords at the left + end of the baseline of normal text. */ + lx = x; + ly = y; + lj = JustMB( this, esc, text, &lx, &ly, upx, upy, just, uxu, uyu, rxu, + ryu, &x0, &y0, method, class, status ); + +/* Initialise the horizontal and verical limits of the total bounding box. */ + alpha_lo = FLT_MAX; + alpha_hi = -FLT_MAX; + beta_lo = FLT_MAX; + beta_hi = -FLT_MAX; + +/* Tell the grf module whether or not to interpret escape sequences,and + also note if the grf module is capable of interpreting escape + sequences. */ + grfcap = GCap( this, GRF__ESC, esc, status ); + +/* Forget the horizontal position remembered by any "%h+" escape sequences + from any previous string. */ + this->hmarkx = FLT_MAX; + this->hmarky = FLT_MAX; + +/* If escape sequences are being interpreted and the string contains some + escape sequences, but the grf module cannot interpret escape sequences, + split the supplied text up into sub-strings delimited by escape sequences + and plot each sub-string individually, modifying the reference point and + graphics attributes as indicated by the escape sequences. */ + if( esc && HasEscapes( text, status ) && !grfcap ) { + +/* Take a copy of the supplied text so that we can temporarily terminate + each sub-string by poking a null (0) character into it. */ + lt = (char *) astStore( NULL, (void *) text, strlen( text ) + 1 ); + +/* Indicate that the current baseline is at its normal height. */ + rise = 0.0; + +/* Get the graphical attribute values for normal text. */ + GAttr( this, GRF__SIZE, AST__BAD, &nsize, GRF__TEXT, method, class, status ); + GAttr( this, GRF__WIDTH, AST__BAD, &nwidth, GRF__TEXT, method, class, status ); + GAttr( this, GRF__FONT, AST__BAD, &nfont, GRF__TEXT, method, class, status ); + GAttr( this, GRF__STYLE, AST__BAD, &nstyle, GRF__TEXT, method, class, status ); + GAttr( this, GRF__COLOUR, AST__BAD, &ncol, GRF__TEXT, method, class, status ); + +/* The "concatenation point" (cx,cy) is the point where the normal baseline + crosses the left hand edge of the substring bounding box. Initialise + this to the left edge of the first substring. */ + cx = x0; + cy = y0; + +/* Loop round the whole string, drawing each sub-string. */ + a = lt; + while( *a && astOK ) { + +/* Examine the start of the remaining string and note if it begins with + an escape sequence. If so, the type and value of the escape sequnece + is returned. In either case the number of characters to the next + delimiter is returned. */ + got_esc = astFindEscape( a, &estype, &esval, &nc ); + +/* If the string starts with an escaped percent sign, modify things so that + we can draw the percent sign with the normal text drawing code below. */ + if( got_esc && estype == GRF__ESPER ) { + got_esc = 0; + a++; + nc = 1; + } + +/* If the string starts with any other escape sequence, modify the graphics + attributes and concatenation point as required by the escape sequence. */ + if( got_esc ) { + InterpEscape( this, estype, (double) esval, &cx, &cy, ux, uy, + rx, ry, lj, &rise, nsize, nstyle, nwidth, ncol, + nfont, method, class, status ); + +/* If the remaining string starts with normal text, draw it. */ + } else { + +/* Temporarily terminate the sub-string which ends with the next escape + sequence. */ + cc = a[ nc ]; + a[ nc ] = 0; + +/* We now have to decide on the reference point for this sub-string. If + the justification is "BL" then the reference point is just the current + concatenation point. */ + if( lj[ 0 ] == 'B' && lj[ 1 ] == 'L' ) { + lx = cx; + ly = cy; + +/* Otherwise, the reference point is offset from the concatenation point. + It would be simpler to draw all substrings with "BL" justification but + this causes problems with some grf modules (such as GAIA) which zoom + the display by modifying the position of text strings without also + modifying the size of text strings. Using the correct reference point + for all sub-strings minimises the drift which occurs when such a grf + modules zooms the display. */ + } else { + +/* Find the width and height of this substring, and the distance between the + bottom of the bounding box and the baseline (the drop). We do this + by calling this function recursively, using "BL" justification to + avoid infinite recursion. */ + +/* Forget the horizontal position remembered by any "%h+" escape sequences + from any previous string. Save and re-instate the position of the + horizontal mark since the call to DrawText may change it. */ + hmx = this->hmarkx; + hmy = this->hmarky; + + DrawText( this, 0, esc, a, cx, cy, "BL", upx, upy, txbn, tybn, + &tdrop, method, class, status ); + + this->hmarkx = hmx; + this->hmarky = hmy; + + dx = txbn[ 0 ] - txbn[ 3 ]; + dy = tybn[ 0 ] - tybn[ 3 ]; + width = sqrt( dx*dx + dy*dy ); + + dx = txbn[ 0 ] - txbn[ 1 ]; + dy = tybn[ 0 ] - tybn[ 1 ]; + height = sqrt( dx*dx + dy*dy ); + +/* First move right from the concatenation point by a fraction of the width + of the substring (0.5 for "C" and 1.0 for "R"). */ + if( lj[ 1 ] == 'C' ) { + width *= 0.5; + } else if( lj[ 1 ] != 'R' ) { + width = 0; + } + lx = cx + rxu*width; + ly = cy + ryu*width; + +/* Now move vertically by an amount which produes the requested vertical + justification. */ + if( lj[ 0 ] == 'T' ) { + height -= tdrop; + lx += height*uxu; + ly += height*uyu; + + } else if( lj[ 0 ] == 'C' ) { + height = 0.5*height - tdrop; + lx += height*uxu; + ly += height*uyu; + + } else if( lj[ 0 ] == 'M' ) { + lx -= tdrop*uxu; + ly -= tdrop*uyu; + } + } + +/* Draw it, and then find its real bounding box (i.e. using the correct + reference position found above). */ + if( ink ) GText( this, a, lx, ly, lj, upx, upy, method, class, status ); + GTxExt( this, a, lx, ly, lj, upx, upy, txbn, tybn, method, class, status ); + +/* Re-instate the orignal value of the terminator character.*/ + a[ nc ] = cc; + +/* Move the concatenation point to the right (i.e. in the direction of the text + baseline) by an amount equal to the width of the bounding box. Also + update the total bounding box limits.*/ + UpdateConcat( txbn, tybn, ux, uy, rx, ry, &cx, &cy, + x0, y0, &alpha_lo, &alpha_hi, &beta_lo, &beta_hi, status ); + } + +/* Move on to the next character. */ + a += nc; + } + +/* Free resources. */ + lt = astFree( lt ); + +/* Reset all attributes to their normal values. */ + GAttr( this, GRF__SIZE, nsize, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__WIDTH, nwidth, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__COLOUR, ncol, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__FONT, nfont, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__STYLE, nstyle, NULL, GRF__TEXT, method, class, status ); + +/* If any escape sequences can be interpreted by the grf module, just pass + the text string on to the grf module. */ + } else { + if( ink ) GText( this, text, lx, ly, lj, upx, upy, method, class, status ); + GTxExt( this, text, lx, ly, lj, upx, upy, txbn, tybn, method, class, status ); + +/* The corners in the bounding box returned by GTxExt are in no + particular order. But this function is contracted to return them + in a specified order. So we use UpdateConcat to find the verical and + horizontal limits of the box in a form which can be used to produce + the correct order. UpdateConcat will also update the concatenation point, + but that is irrelevant in this context. */ + UpdateConcat( txbn, tybn, ux, uy, rx, ry, &lx, &ly, x0, y0, &alpha_lo, + &alpha_hi, &beta_lo, &beta_hi, status ); + } + +/* Return the total bounding box,in the order bottom left, topleft, top + right, bottom right. */ + xbn[ 0 ] = x0 + alpha_lo*ux + beta_lo*rx; + ybn[ 0 ] = y0 + alpha_lo*uy + beta_lo*ry; + + xbn[ 1 ] = x0 + alpha_hi*ux + beta_lo*rx; + ybn[ 1 ] = y0 + alpha_hi*uy + beta_lo*ry; + + xbn[ 2 ] = x0 + alpha_hi*ux + beta_hi*rx; + ybn[ 2 ] = y0 + alpha_hi*uy + beta_hi*ry; + + xbn[ 3 ] = x0 + alpha_lo*ux + beta_hi*rx; + ybn[ 3 ] = y0 + alpha_lo*uy + beta_hi*ry; + +/* Return the drop. */ + if( drop ) *drop = -alpha_lo*ulen; + +/* Free memory.*/ + lj = astFree( (void *) lj ); + +/* If OK, update the box containing all drawn graphics primitives. */ + if( ink && astOK && !Boxp_freeze ) { + for( i = 0; i < 4; i++ ){ + Boxp_lbnd[ 0 ] = astMIN( xbn[ i ], Boxp_lbnd[ 0 ] ); + Boxp_ubnd[ 0 ] = astMAX( xbn[ i ], Boxp_ubnd[ 0 ] ); + Boxp_lbnd[ 1 ] = astMIN( ybn[ i ], Boxp_lbnd[ 1 ] ); + Boxp_ubnd[ 1 ] = astMAX( ybn[ i ], Boxp_ubnd[ 1 ] ); + } + } +} + +static void DrawTicks( AstPlot *this, TickInfo **grid, int drawgrid, + double *labelat, double *gap, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* DrawTicks + +* Purpose: +* Draw tick marks for a 2-D annotated coordinate grid. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void DrawTicks( AstPlot *this, TickInfo **grid, int drawgrid, +* double *labelat, double *gap, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws major and minor tick marks. It uses a different +* technique depending on whether the tick marks are to be put along the +* edges of the plotting area, or along a curve through the middle of the +* plotting area. The physical axis values at which to put tick marks +* are supplied in parameter "grid". +* +* If the ticks are placed on the edges of the plotting area, The +* EdgeCrossings function is used to find all points along the edge which +* have a physical axis value correspoinding to a tick value (there can in +* general be more than one point on an edge with a given physical axis +* value). Ticks are put at all such crossings. +* +* If ticks are placed within the plotting area, then they are put +* along a curve defined by the "other axis" values supplied in +* parameter "labelat". + +* Parameters: +* this +* A pointer to the Plot. +* grid +* A pointer to an array of two TickInfo pointers (one for each axis), +* each pointing to a TickInfo structure holding information about +* tick marks on the axis. See function GridLines. +* drawgrid +* If non-zero, then a grid of curves has been drawn to mark the +* major axis values. +* labelat +* A pointer to a 2 element array in holding the constant axis +* values at which tick marks are to be put. Element 0 should hold +* the axis 1 value at which tick marks for axis 0 are placed. Element +* 1 should hold the axis 0 value at which tick marks for axis +* 1 are placed. If ticks are to be placed round the edges of the +* plotting zone instead of within the plotting zone, then values of +* AST__BAD should be supplied. +* gap +* Pointer to array of two values holding the gap between major +* tick marks on the two axes. This will be the difference between, +* or the ratio of, adjacent tick mark values, depending on the +* setting of the LogTicks attribute. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function assumes the current Frame of the Plot is 2 +* dimensional, and it should not be called if this is not the case. +*/ + +/* Local Variables: */ + AstFrame *frame; /* Pointer to current Frame in Plot */ + AstMapping *mapping; /* Pointer to graphics->physical Mapping */ + AstPointSet *pset1; /* Pointer to PointSet holding physical coords. */ + AstPointSet *pset2; /* Pointer to PointSet holding graphics coords. */ + AstPointSet *pset3; /* Pointer to PointSet holding clipped graphics coords. */ + EdgeCrossingsStatics *ecstatics = NULL; /* For static data structure */ + TickInfo *info; /* Pointer to the TickInfo for the current axis */ + double *ptr1[2]; /* Pointer to physical data */ + double **ptr2; /* Pointer to graphics data */ + double **ptr3; /* Pointer to clipped graphics data */ + double *a; /* Pointer to next current axis value */ + double *b; /* Pointer to next other axis value */ + double *value; /* Current tick value */ + double *x; /* Pointer to next X value */ + double *xc; /* Pointer to next clipped X value */ + double *y; /* Pointer to next Y value */ + double *yc; /* Pointer to next clipped Y value */ + double a0; /* Physical axis value at base of tick */ + double axmax; /* Upper axis limit */ + double axmin; /* Lower axis limit */ + double delta1; /* Increment between minor ticks above major tick */ + double delta2; /* Increment between minor ticks below major tick */ + double diff; /* Difference between adjacent major ticks */ + double dl2; /* Squared increment between points */ + double dl2_limit; /* Minimum usable squared increment between points */ + double dl; /* Increment between points */ + double dx; /* X component of increment along tick mark */ + double dy; /* Y component of increment along tick mark */ + double ex; /* X component of increment between points */ + double ey; /* Y component of increment between points */ + double lblat2; /* Other axis value part way up each tick */ + double lblat; /* Other axis value at base of each tick */ + double mindim; /* Shortest dimension of plotting area */ + double minval; /* Current axis value at next minor tick */ + double mjsign; /* Sign of the major tick mark length */ + double mjtklen; /* Length of major tick marks */ + double mnsign; /* Sign of the minor tick mark length */ + double mntklen; /* Length of minor tick marks */ + double ux; /* X component of unit vector along tick mark */ + double uy; /* Y component of unit vector along tick mark */ + double val; /* Major axis value */ + double x0; /* X at base of tick */ + double x1; /* X at end of tick */ + double x2; /* Clipped X at base of tick */ + double y0; /* Y at base of tick */ + double y1; /* Y at end of tick */ + double y2; /* Clipped Y at base of tick */ + int *majflag; /* Pointer to next major/minor flag */ + int *majflags; /* Pointer to aray of major/minor flags */ + int axis; /* Current axis index */ + int ed; /* Doing a secondary edge? */ + int edge; /* Index of edge being ticked */ + int first; /* Is this the first tick to be drawn? */ + int gelid; /* ID for next graphical element to be drawn */ + int i; /* Minor tick mark index */ + int logticks; /* Are major tick marks logarithmically spaced? */ + int minhi; /* Highest minor tick mark index */ + int minlo; /* Lowest minor tick mark index */ + int nedge; /* No. of edges to be ticked for each axis */ + int nel; /* Actual number of tick marks to draw */ + int ntot; /* Maximum number of tick marks */ + int tick; /* Tick index */ + int lasttick; /* Index of last major tick mark */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + a = NULL; + delta1 = 0.0; + delta2 = 0.0; + lblat2 = 0.0; + uy = 0.0; + logticks = 0; + +/* Get the minimum dimension of the plotting ares. */ + mindim = astMIN( this->xhi - this->xlo, this->yhi - this->ylo ); + +/* Information about the drawn tick marks is saved in the Plot structure. + Reset this information now so that we are ready to store information + about the new ticks that will be drawn by this function invocation. */ + SaveTick( this, -1, 0.0, 0.0, 0, status ); + +/* If ticks are to be put round the edges of the plotting area... */ +/* ============================================================== */ + if( labelat[ 0 ] == AST__BAD || labelat[ 1 ] == AST__BAD ){ + +/* Store the number of edges to be ticked for each axis. */ + nedge = astGetTickAll( this )? 2 : 1; + +/* Do each required edge. */ + for( ed = 0; ed < nedge; ed++ ){ + +/* Initialize the id value for graphical element being drawn. */ + gelid = AST__TICKS1_ID; + +/* Do each axis. */ + for( axis = 0; axis < 2; axis++ ){ + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, gelid, 1, GRF__LINE, method, class ); + +/* Store the length in graphics coordinates of major tick marks for this + axis. Use a default of zero if a grid has been drawn. */ + if( astTestMajTickLen( this, axis ) || !drawgrid ){ + mjtklen = astGetMajTickLen( this, axis )*mindim; + } else { + mjtklen = 0.0; + } + +/* Store the length in graphics coordinates of minor tick marks. */ + mntklen = astGetMinTickLen( this, axis )*mindim; + +/* Get the edge to be labelled with the axis values. Edge 0 is the left hand + edge. Edge 1 is the top edge. Edge 2 is the right-hand edge. Edge 3 is + the bottom edge. */ + edge = ( astGetEdge( this, axis ) + ed*2 ) % 4; + if( edge < 0 ) edge = -edge; + +/* Store a pointer to the structure containing information describing the + positions of the major tick marks along this axis. */ + info = grid[ axis ]; + +/* Store a pointer to the axis value at the first major tick mark. */ + value = info->ticks; + +/* Minor tick marks are drawn on both sides of each major tick mark. They + are identified by an index number relative to major tick mark at zero. + Store the indices of the first and last minor tick marks. */ + minlo = ( 1 - info->nminor )/2; + minhi = info->nminor/2; + +/* If major ticks are linear, store the difference between minor tick marks. + This value will be the same for all major ticks. */ + logticks = astGetLogTicks( this, axis ); + if( !logticks ) { + delta1 = gap[ axis ]/(double)info->nminor; + delta2 = delta1; + } + +/* Loop round until all ticks have been done. */ + for( tick = 0; tick < info->nmajor; tick++ ){ + +/* Draw tick marks at all occurrences of the current major tick value on + the selected edge of the plotting area. */ + Ticker( this, edge, axis, *value, gap, mjtklen, + 1, ( ed == 0 ), &ecstatics, method, class, status ); + +/* If minor tick mark values were not supplied, calculate them as even + intervals between the major tick values. */ + if( !info->minticks ) { + +/* If major ticks are logarithmic, store the difference between minor tick + marks. This value will be different for each major tick. Also, since + the minor ticks are drawn on either side of the major tick, the minor + tick spacing above the major tick will be different to that below the + minor tick when using logarathmic ticks. "delta1" is the minor gap + above the major value, and "delta2" is the minor gap below the major + value. */ + if( logticks ) { + delta1 = (*value) * ( gap[ axis ] - 1.0 )/ + (double)info->nminor; + delta2 = delta1 / gap[ axis ]; + } + +/* Extra minor tick marks are drawn below the first major tick mark and + above the last major tick mark to fill in any gaps caused by axis + limits being exceeded. */ + if( tick == 0 ) { + minlo = 1 - info->nminor; + } if( tick == 1 ) { + minlo = ( 1 - info->nminor )/2; + } else if( tick == info->nmajor - 1 ) { + minhi = info->nminor - 1; + } + +/* Store the axis value at the first minor tick mark. */ + minval = *value + minlo*delta2; + +/* Loop round all the minor tick marks, storing the physical coordinates + defining the tick mark. */ + for( i = minlo; i <= minhi; i++ ){ + +/* Draw tick marks at all occurrences of the current minor tick value on + the selected edge of the plotting area. Do not do the minor tick mark + with index zero, since this corresponds to the position of the major + tick mark. */ + if( i ) Ticker( this, edge, axis, minval, gap, mntklen, + 0, ( ed == 0 ), &ecstatics, method, class, status ); + +/* Get the axis value at the next minor tick mark. */ + minval += ( i < 0 ) ? delta2 : delta1; + } + } + +/* Point to the next major tick value. */ + value++; + + } + +/* If minor tick mark values have been supplied, used them. */ + if( info->minticks ) { + value = info->minticks; + for( tick = 0; tick < info->nminor; tick++, value++ ){ + Ticker( this, edge, axis, *value, gap, mntklen, 0, + ( ed == 0 ), &ecstatics, method, class, status ); + } + } + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, gelid, 0, GRF__LINE, method, class ); + +/* Set up the id for the next graphical element to be drawn. */ + gelid = AST__TICKS2_ID; + } + } + +/* Free the static resources allocated within EdgeCrossings (called + by Ticker). */ + (void) EdgeCrossings( NULL, 0, 0, 0.0, NULL, NULL, &ecstatics, method, + class, status ); + +/* If ticks are to put within the interior of the plotting area... */ +/* ============================================================== */ + } else { + +/* Get the mapping from base Frame (graphics coords) to current Frame + (physical coords). */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Get the current Frame from the Plot. */ + frame = astGetFrame( this, AST__CURRENT ); + +/* Initialize the id value for graphical element being drawn. */ + gelid = AST__TICKS1_ID; + +/* Do each axis. */ + for( axis = 0; axis < 2; axis++ ){ + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, gelid, 1, GRF__LINE, method, class ); + +/* Store the length in graphics coordinates of major tick marks for this + axis. Use a default of zero if a grid has been drawn. */ + if( astTestMajTickLen( this, axis ) || !drawgrid ){ + mjtklen = astGetMajTickLen( this, axis )*mindim; + } else { + mjtklen = 0.0; + } + +/* Store the length in graphics coordinates of minor tick marks. */ + mntklen = astGetMinTickLen( this, axis )*mindim; + +/* Indicate that the tick mark lengths should not be negatated. */ + mjsign = 1.0; + mnsign = 1.0; + +/* Store the smallest squared distance in graphics coordinates which + can reliably be used to determine the direction of a tick mark. */ + dl2_limit = 0.0001*mindim; + dl2_limit *= dl2_limit; + +/* Store a pointer to the structure containing information describing the + positions of the major tick marks along this axis. */ + info = grid[ axis ]; + +/* Store a pointer to the axis value at the first major tick mark. */ + value = info->ticks; + +/* Get the maximum number of tick marks to be drawn on this axis. */ + ntot = 0; + ntot = info->nmajor; + if( info->minticks ) { + ntot += info->nmajor + info->nminor; + } else { + ntot += ( info->nmajor + 1 )*( info->nminor - 1 ); + } + +/* Pass on to the next axis if no ticks are to be drawn. */ + if( ntot ) { + +/* Allocate memory to hold the physical coordinates defining all the + required tick marks. Each tick mark is defined by 2 points. */ + ptr1[ 0 ] = (double *) astMalloc( sizeof(double)*(size_t)(2*ntot) ); + ptr1[ 1 ] = (double *) astMalloc( sizeof(double)*(size_t)(2*ntot) ); + +/* Allocate memory to hold a set of flags indicating whether each tick + mark is minor or major. */ + majflags = (int *) astMalloc( sizeof(int)*(size_t)ntot ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* If the tick values have been supplied using astSetTickValues, just + copy them into the above arrays. */ + if( info->minticks ) { + + a = ptr1[ axis ]; + b = ptr1[ 1 - axis ]; + majflag = majflags; + + for( tick = 0; tick <= info->nmajor; tick++ ){ + + val = info->ticks[ tick ]; + if( logticks ) { + diff = val * ( gap[ axis ] - 1.0 ); + lblat2 = labelat[ axis ] + 0.2*diff; + } else { + lblat2 = labelat[ axis ] + 0.2*gap[ 1 - axis ]; + } + + *(a++) = val; + *(b++) = labelat[ axis ]; + *(a++) = val; + *(b++) = lblat2; + *(majflag++) = 1; + } + + for( tick = 0; tick <= info->nminor; tick++ ){ + + val = info->minticks[ tick ]; + if( logticks ) { + diff = val * ( gap[ axis ] - 1.0 ); + lblat2 = labelat[ axis ] + 0.2*diff; + } else { + lblat2 = labelat[ axis ] + 0.2*gap[ 1 - axis ]; + } + + *(a++) = val; + *(b++) = labelat[ axis ]; + *(a++) = val; + *(b++) = lblat2; + *(majflag++) = 0; + } + +/* If the tick values were not supplied using astSetTickValues, then we + need to calculate the minor tick positions explicitly. */ + } else { + +/* Store pointers to the next point on each axis. "a" always refers to the + current axis. Also store the value on the other axis at which the tick + marks starts, and another value on the other axis which is used to + defined the tick mark directions. */ + a = ptr1[ axis ]; + b = ptr1[ 1 - axis ]; + majflag = majflags; + lblat = labelat[ axis ]; + +/* Store another value on the other axis which is used to defined the tick + mark directions, and the difference between minor tick marks. For + linearly spaced tick marks these values will be the same for all major + ticks. Minor ticks are always drawn above the correponding major + value (i.e. minlo == 1 ) and so we do not need to set delta2. */ + logticks = astGetLogTicks( this, axis ); + if( !logticks ) { + lblat2 = labelat[ axis ] + 0.2*gap[ 1 - axis ]; + delta1 = gap[ axis ]/(double)info->nminor; + } + +/* Store the indices of the first and last minor tick marks, relative to + a major tick mark. */ + minlo = 1; + minhi = info->nminor - 1; + +/* Get the axis limits. */ + axmin = astGetBottom( frame, axis ); + axmax = astGetTop( frame, axis ); + +/* Loop round until all ticks have been done. We include a hypothetical tick + at index -1 (i.e. one gap below the first listed tick value) in order + to get minor tick marks below the first major tick. But the + hypothetical major tick value is not included in the list of major tick + values to draw. */ + lasttick = info->nmajor - 1; + for( tick = -1; tick <= lasttick; tick++ ){ + +/* Get the major tick value. */ + if( tick == -1 ) { + if( !logticks ) { + val = (*value) - gap[ axis ]; + }else { + val = (*value)/gap[ axis ]; + } + } else { + val = *(value++); + } + +/* Now find the value on the other axis which is used to defined the tick + mark directions, and the difference between minor tick marks, in the + case of logarithmically spaced tick marks. These values will be + different for every major tick. Minor ticks are always drawn above the + correponding major value (i.e. minlo == 1 ) and so we do not need to set + delta2. */ + if( logticks ) { + diff = val * ( gap[ axis ] - 1.0 ); + lblat2 = labelat[ axis ] + 0.2*diff; + delta1 = diff / (double)info->nminor; + } + +/* If major tick marks are required, store the physical coordinates at the + start of the major tick mark, and at a point a little way up the major + tick mark. */ + if( tick > -1 ){ + *(a++) = val; + *(b++) = lblat; + *(a++) = val; + *(b++) = lblat2; + *(majflag++) = 1; + } + +/* Store the points defining the minor tick marks on either side of + this major tick mark. First store the axis value at the first minor + tick mark. */ + minval = val + minlo*delta1; + +/* Loop round all the minor tick marks, storing the physical coordinates + defining the tick mark. */ + for( i = minlo; i <= minhi; i++ ){ + +/* Do not do the minor tick mark with index zero, since this corresponds + to the position of the major tick mark. Do not do any minor ticks that + are outside the axis range. */ + if( i && minval >= axmin && minval <= axmax ){ + *(a++) = minval; + *(b++) = lblat; + *(a++) = minval; + *(b++) = lblat2; + *(majflag++) = 0; + } + +/* Get the axis value at the next minor tick mark. */ + minval += delta1; + } + } + } + } + +/* Adjust the size of the arrays to exclude any unused space. */ + nel = a - ptr1[axis]; + ptr1[axis] = (double *) astRealloc( (void *) ptr1[axis], + sizeof(double)*nel ); + ptr1[1-axis] = (double *) astRealloc( (void *) ptr1[1-axis], + sizeof(double)*nel ); + +/* Create a pointset holding these coordinates. */ + pset1 = astPointSet( nel, 2, "", status ); + astSetPoints( pset1, ptr1 ); + +/* Transform these physical coordinates into graphics coordinates, without + doing any clipping (this is so that tick marks are still drawn even if + they extend into the area containing clipped physical coordinates). */ + pset2 = astTransform( mapping, pset1, 0, NULL ); + ptr2 = astGetPoints( pset2 ); + +/* Transform them again this time with clipping. */ + pset3 = Trans( this, NULL, mapping, pset1, 0, NULL, 0, method, class, status ); + ptr3 = astGetPoints( pset3 ); + +/* Check the pointers can be used.*/ + if( astOK ){ + +/* Store pointers to the next point on each axis. */ + a = ptr1[ axis ]; + + x = ptr2[ 0 ]; + y = ptr2[ 1 ]; + + xc = ptr3[ 0 ]; + yc = ptr3[ 1 ]; + + majflag = majflags; + +/* Loop round all ticks (major and minor). */ + ux = AST__BAD; + first = 1; + for( tick = 0; tick < nel/2; tick++ ){ + +/* Store the physical axis value at the base of the tick mark (skip over + the physical axis value at the point up the tick mark). */ + a0 = *(a++); + a++; + +/* Store the x and y coordinates at the base of the tick mark. */ + x0 = *(x++); + y0 = *(y++); + +/* Store the x and y coordinates at a point up the tick mark. */ + x1 = *(x++); + y1 = *(y++); + +/* Store the clipped x and y coordinates at the base of the tick mark. */ + x2 = *(xc++); + y2 = *(yc++); + +/* Skip over the clipped x and y coordinates at the point up the tick mark. */ + xc++; + yc++; + +/* Check they are all valid, and that the start of the tick mark is within + the plotting area. */ + if( x0 != AST__BAD && y0 != AST__BAD && + x1 != AST__BAD && y1 != AST__BAD && + x2 != AST__BAD && y2 != AST__BAD && + x0 <= this->xhi && x0 >= this->xlo && + y0 <= this->yhi && y0 >= this->ylo ){ + +/* Get the increments in X and Y beyween the two points, and the squared + distance between the two points. */ + dx = x1 - x0; + dy = y1 - y0; + dl2 = dx*dx + dy*dy; + +/* Check the two points are not co-incident. */ + if( dl2 > dl2_limit ){ + +/* Store the distance between the two points. */ + dl = sqrt( dl2 ); + +/* If this is the first tick to be drawn on this axis, decide which + direction to draw the tick mark so that they will appear on the right + hand side of the axis as seen by someone moving along the axis in the + positive direction (the numerical labels are also drawn on the same + side). */ + if( first ){ + first = 0; + +/* If the next tick mark is not defined, make an arbitrary decision by + leaving the sign of the tick mark length unchanged. */ + if( tick + 1 < nel/2 && + *x != AST__BAD && *y != AST__BAD && + a0 != AST__BAD && *a != AST__BAD ){ + +/* Form the vector joining this tick mark to the next. */ + ex = *x - x0; + ey = *y - y0; + +/* Ensure this vector is in the positive direction of the axis. */ + if( *a < a0 ) { + ex = -ex; + ey = -ey; + } + +/* If a positive tick mark length would put the marks on the wrong side, + negate the tick mark length. */ + if( ex*dy > ey*dx ){ + mjsign = -1.0; + mnsign = -1.0; + } + } + } + +/* Store the unit vector in the direction of the tick mark. This is used + as the default vector for the next tick mark if the direction of the + next tick mark is indeterminate. */ + ux = dx/dl; + uy = dy/dl; + } + +/* Only draw this tickmark if its direction is known. */ + if( ux != AST__BAD ) { + +/* Get the position of the end of the tick mark. The length of the tick + mark depends on whether it is a major or minor tick mark. */ + if( *majflag ){ + x1 = x0 + mjsign*mjtklen*ux; + y1 = y0 + mjsign*mjtklen*uy; + } else { + x1 = x0 + mnsign*mntklen*ux; + y1 = y0 + mnsign*mntklen*uy; + } + +/* Save and draw the tick mark. */ + SaveTick( this, axis, x0, y0, *majflag, status ); + if( x0 != x1 || y0 != y1 ) { + Bpoly( this, (float) x0, (float) y0, status ); + Apoly( this, (float) x1, (float) y1, status ); + Opoly( this, status ); + } + } + } + +/* Point to the next major/minor flag. */ + majflag++; + } + } + +/* Free the memory holding the physical coordinates. */ + ptr1[ 0 ] = (double *) astFree( ( void *) ptr1[ 0 ] ); + ptr1[ 1 ] = (double *) astFree( ( void *) ptr1[ 1 ] ); + majflags = (int *) astFree( (void *) majflags ); + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + pset3 = astAnnul( pset3 ); + } + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, gelid, 0, GRF__LINE, method, class ); + +/* Set up the id for the next graphical element to be drawn. */ + gelid = AST__TICKS2_ID; + } + +/* Annul the pointers to the Mapping and Frame. */ + mapping = astAnnul( mapping ); + frame = astAnnul( frame ); + + } + +/* Return. */ + return; + +} + +static void EBuf( AstPlot *this, int *status ) { +/* +*++ +* Name: +c astEBuf +f AST_EBUF + +* Purpose: +* End the current graphical buffering context. + +* Type: +* Public function. + +* Synopsis: +c #include "plot.h" +c void astEBuf( AstPlot *this ) +f CALL AST_EBUF( THIS STATUS ) + +* Class Membership: +* Plot member function. + +* Description: +c This function +f This routine +* ends the current graphics buffering context. It should match a +* corresponding call to the +c astBBuf function. +f AST_EBUF routine. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The nature of the buffering is determined by the underlying +* graphics system (as defined by the current grf module). Each call +c to this function +f to this routine +* simply invokes the astGEBuf function in the grf module. + +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the active GRF EBuf function. */ + GEBuf( this, "astEBuf", astGetClass( this ), status ); +} + +static int EdgeLabels( AstPlot *this, int ink, TickInfo **grid, + AstPlotCurveData **cdata, int force, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* EdgeLabels + +* Purpose: +* Attempts to display labels for the major tick values around the edges +* of the plotting area. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int EdgeLabels( AstPlot *this, int ink, TickInfo **grid, +* AstPlotCurveData **cdata, int force, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function determines how many major tick value labels could be +* placed on the specified edges of the plotting area, and then if +* requested, and if sufficient such labels are found (more than 3 on +* each axis), they are drawn. To place a label on an edge, the curve +* defining the major tick value must cross the edge at a reasonably +* angle (at least 3 degrees). Labels are not drawn which would overlap +* other, previously drawn, labels. A flag is returned indicating if +* edge labels were (or could be) drawn. + +* Parameters: +* this +* A pointer to the Plot. +* ink +* If zero, then no labels are drawn, but the decision whether or +* not to draw them is still made and indicated in the returned function +* value. +* grid +* A pointer to an array of two TickInfo pointers (one for each axis), +* each pointing to a TickInfo structure holding information about +* tick marks on the axis. See function GridLines. +* cdata +* A pointer to an array of two AstPlotCurveData pointers (one for each axis), +* each pointing to an array of AstPlotCurveData structure (one for each +* major tick value on the axis), holding information about breaks +* in the curves drawn to mark the major tick values. See function +* DrawGrid. +* force +* If non-zero, then an attempt is made to draw edge labels even if +* it looks like insufficient edge labels can be produced. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If edge labels were drawn, 1 is returned. Otherwise 0 is returned. + +* Notes: +* - Zero is returned if an error has already occurred. +*/ + + +/* Local Variables: */ + AstFrame *frame; /* Pointer to current Frame */ + AstPlotCurveData *cdt; /* Pointer to the AstPlotCurveData for the next tick */ + LabelList *labellist; /* Pointer to a ingle list of labels to be plotted */ + LabelList *ll; /* Pointer to next label to be plotted */ + LabelList *llist[2]; /* Pointers to both lists of labels to be plotted */ + TickInfo *info; /* Pointer to the TickInfo for the current axis */ + const char *just[ 2 ]; /* Justification string */ + const char *text; /* Pointer to label text */ + double edgeval; /* Axis value at the labelled edge */ + double mindim; /* Minimum dimension of the plotting area */ + double oppval; /* Axis value on the edge opposite to the labels */ + double tol; /* Max. distance between a break and the edge */ + double txtgap; /* Absolute gap between labels and edges */ + float *box; /* Pointer to array of label bounding boxes */ + float *vxbrk; /* X component of unit vector at current break */ + float *vybrk; /* Y component of unit vector at current break */ + float *xbrk; /* X coord. of current break */ + float *ybrk; /* Y coord. of current break */ + float xref; /* X coordinate at label's reference position */ + float yref; /* Y coordinate at label's reference position */ + int axis; /* Current axis index */ + int brk; /* Current break index */ + int edge; /* The edge to be labelled */ + int edgeax; /* Index of axis parallel to the labelled edge */ + int edgelabs; /* Can edge labels be produced? */ + int esc; /* INterpret escape sequences? */ + int gelid; /* ID for next graphical element to be drawn */ + int ii; /* Index into existing labels */ + int maxlab; /* Number of distinct edge labels */ + int medge[2]; /* No. of distinct edge labels for each axis */ + int naxlab; /* Number of edge labels */ + int near; /* Draw a label on the near edge? */ + int nedge[2]; /* No. of edge labels for each axis */ + int ok; /* Can the current tick mark be labelled on the edge? */ + int labfound; /* Label value has already been used? */ + int tick; /* Tick index */ + int upright; /* Draw all labels upright? */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + xref = 0.0; + yref = 0.0; + +/* See if escape sequences in text strings are to be interpreted. */ + esc = astGetEscape( this ); + +/* Initialise the returned flag to indicate that edge labels cannot be + produced. */ + edgelabs = 0; + +/* Get the minimum dimension of the plotting ares. */ + mindim = astMIN( this->xhi - this->xlo, this->yhi - this->ylo ); + +/* Set up the tolerance for curve breaks occuring on an edge of + the plotting zone. */ + tol = 0.005*mindim; + +/* First, we get a list of all the labels which can be produced on each + axis. The list includes the labels reference position in graphics + coordinates, and the index of the major tick value which it + represents. We do not yet know whether enough of the grid lines cross + the required edge to make it feasable to use edge labelling, so we do + not yet draw the labels. + =====================================================================*/ + +/* Initialise pointers to arrays of structures holding information + about the labels which can be draw round the edge for both axes. */ + llist[ 0 ] = NULL; + llist[ 1 ] = NULL; + +/* Indicate that no labels can yet be drawn on either axis. */ + nedge[ 0 ] = 0; + nedge[ 1 ] = 0; + +/* The "nedge" array counts the number of labels on each edge. But some + of these labels may be for the same tick mark (if the tick mark curve has + more than 1 intersection with the edge). The "medge" array counts the + number of *distinct* tick mark labels (i.e. the number of tick mark + values which have 1 or more interesections with the edge). */ + medge[ 0 ] = 0; + medge[ 1 ] = 0; + +/* For each axis, identify the the usable edge labels. */ + for( axis = 0; axis < 2; axis++ ){ + +/* See if labels for this axis are to be drawn upright. */ + if( astTestLabelUp( this, axis ) ) { + upright = astGetLabelUp( this, axis ); + } else { + upright = 1; + } + +/* Store the required gap between the label text and the axis. */ + txtgap = astGetNumLabGap( this, axis )*mindim; + +/* Get the edge to be labelled with the axis values. Edge 0 is the left hand + edge. Edge 1 is the top edge. Edge 2 is the right-hand edge. Edge 3 is + the bottom edge. */ + edge = astGetEdge( this, axis ) % 4; + if( edge < 0 ) edge = -edge; + +/* If edge labels for the current axis are to go on the left hand edge of + the plotting area... */ + if( edge == 0 ){ + +/* Choose the justification based on the sign of the text gap. */ + if( !upright ) { + just[ axis ] = "BC"; + } else if( txtgap > 0.0 ){ + just[ axis ] = "CR"; + } else if( txtgap < 0.0 ){ + just[ axis ] = "CL"; + } else { + just[ axis ] = "CC"; + } + +/* Store the constant X axis value at the edge being labelled. Also store + the X value to use for the reference position for all labels. Take into + account whether or not the X axis is displayed reversed (i.e. with high + X values at the left hand side of the screen ). */ + if( !this->xrev ){ + edgeval = this->xlo; + oppval = this->xhi; + xref = (float)( edgeval - txtgap ); + } else { + edgeval = this->xhi; + oppval = this->xlo; + xref = (float)( edgeval + txtgap ); + } + +/* Indicate that the "edgeval" value refers to axis 1 (the X axis). */ + edgeax = 1; + +/* Do the same if the labels are to go on the top edge. */ + } else if( edge == 1 ){ + if( txtgap > 0.0 ){ + just[ axis ] = "BC"; + } else if( txtgap < 0.0 ){ + just[ axis ] = "TC"; + } else { + just[ axis ] = "CC"; + } + + if( !this->yrev ){ + edgeval = this->yhi; + oppval = this->ylo; + yref = (float)( edgeval + txtgap ); + } else { + edgeval = this->ylo; + oppval = this->yhi; + yref = (float)( edgeval - txtgap ); + } + + edgeax = 0; + +/* Do the same if the labels are to go on the right-hand edge. */ + } else if( edge == 2 ){ + + if( !upright ) { + just[ axis ] = "BC"; + } else if( txtgap > 0.0 ){ + just[ axis ] = "CL"; + } else if( txtgap < 0.0 ){ + just[ axis ] = "CR"; + } else { + just[ axis ] = "CC"; + } + + if( !this->xrev ){ + edgeval = this->xhi; + oppval = this->xlo; + xref = (float)( edgeval + txtgap ); + } else { + edgeval = this->xlo; + oppval = this->xhi; + xref = (float)( edgeval - txtgap ); + } + + edgeax = 1; + +/* Do the same if the labels are to go on the bottom edge. */ + } else { + if( txtgap > 0.0 ){ + just[ axis ] = "TC"; + } else if( txtgap < 0.0 ){ + just[ axis ] = "BC"; + } else { + just[ axis ] = "CC"; + } + + if( !this->yrev ){ + edgeval = this->ylo; + oppval = this->yhi; + yref = (float)( edgeval - txtgap ); + } else { + edgeval = this->yhi; + oppval = this->ylo; + yref = (float)( edgeval + txtgap ); + } + + edgeax = 0; + + } + +/* Get a pointer to the structure containing information describing the + positions of the major tick marks along this axis. */ + info = grid[ axis ]; + +/* Get a pointer to the structure containing information describing the + breaks in the curve which is parallel to the other axis and passes + through the first major tick mark. */ + cdt = cdata[ axis ]; + +/* Initialise the pointer to the list of text strings to be drawn. */ + labellist = NULL; + +/* Initialise the number of labels which can be placed on the near edge of + the plotting zone (some of which may be the same). */ + naxlab = 0; + +/* Initialise the number of distinct labelled tick mark values. */ + maxlab = 0; + +/* Loop round each of the major tick marks on the current axis. */ + for( tick = 0; cdt && info && tick < info->nmajor; tick++ ){ + +/* Store pointers to the values giving the position and unit direction + vector of the curve at the first break. */ + xbrk = cdt->xbrk; + ybrk = cdt->ybrk; + vxbrk = cdt->vxbrk; + vybrk = cdt->vybrk; + +/* Loop round each of the breaks in the curve which passes through the + current major tick mark, and is parallel to the other axis. */ + ok = 0; + for( brk = 0; brk < cdt->nbrk; brk++ ){ + +/* A label can be produced on the near edge of the plotting zone if the + current break occurs on, or close to, the edge, and the curve is not + nearly parallel to the axis (limit is 5 degs). */ + near = ( ( edgeax == 0 && + fabs( (double) *ybrk - edgeval ) < tol && + fabs( (double) *vybrk ) > 0.09 ) || + ( edgeax == 1 && + fabs( (double) *xbrk - edgeval ) < tol && + fabs( (double) *vxbrk ) > 0.09 ) ); + +/* Get the label text. */ + if( info->labels ) { + text = (info->labels)[ tick ]; + } else { + text = NULL; + } + +/* If a label can be produced, record the information needed to draw the + label. */ + if( near && text ){ + + labellist = (LabelList *) astGrow( (void *) labellist, naxlab + 1, sizeof(LabelList) ); + if ( !astOK ) break; + + if( edgeax == 0 ){ + (labellist + naxlab)->index = (double) *xbrk; + (labellist + naxlab)->x = (double) *xbrk; + (labellist + naxlab)->y = (double) yref; + } else { + (labellist + naxlab)->index = (double) *ybrk; + (labellist + naxlab)->x = (double) xref; + (labellist + naxlab)->y = (double) *ybrk; + } + + (labellist + naxlab)->text = (char *) astStore( NULL, (void *) text, strlen(text) + 1 ); + (labellist + naxlab)->just = (char *) astStore( NULL, (void *) just[ axis ], strlen(just[ axis ]) + 1 ); + +/* The up vector depends on which edge is being labelled and whether all + labels are being drawn upright or not. */ + if( edge == 1 || edge == 3 || upright ) { + (labellist + naxlab)->upx = 0.0; + (labellist + naxlab)->upy = 1.0; + } else if( edge == 0 ) { + (labellist + naxlab)->upx = -1.0; + (labellist + naxlab)->upy = 0.0; + } else { + (labellist + naxlab)->upx = 1.0; + (labellist + naxlab)->upy = 0.0; + } + + (labellist + naxlab)->val = (info->ticks)[ tick ]; + naxlab++; + +/* If this label has not already been included in the label list, indicate + that we have found another usable label. */ + labfound = 0; + for( ii = 0; ii < naxlab-1; ii++ ) { + if( fabs( (info->ticks)[ tick ] - + (labellist + ii)->val ) < 0.2*info->gap ) { + labfound = 1; + break; + } + } + if( !labfound ) ok = 1; + + } + +/* Increment the pointers to the values giving the position and unit direction + vector of the next break. */ + xbrk++; + ybrk++; + vxbrk++; + vybrk++; + + } + +/* If an error has occurred, break out of the loop. */ + if( !astOK ) break; + +/* If one or more labels could be produced for this tick mark value, + increment the number of labeled tick marks found. */ + if( ok ) maxlab++; + +/* Get a pointer to the curve through the next major tick mark. */ + cdt++; + + } + +/* If an error has occurred, break out of the loop. */ + if( !astOK ) break; + +/* Store the number of labels for this axis, and the pointer to the + drawable labels. */ + nedge[ axis ] = naxlab; + medge[ axis ] = maxlab; + llist[ axis ] = labellist; + } + +/* We now know how many labels would be produced on each axis if edge + labelling were to be used. We also know what those labelled values are, + and where the labels would be drawn. We now take the decision as to + whether there are enough of these labels to make edge labelling + feasable. If so, we carry on and draw the labels. There need to be + at least 3 labels on each axis for linear tick spacing and 2 for log + tick spacing (or a non-zero value supplied for "force")... + ================================================================= */ + if( astOK && ( ( medge[ 0 ] > ( astGetLogTicks( this, 0 ) ? 1 : 2 ) && + medge[ 1 ] > ( astGetLogTicks( this, 1 ) ? 1 : 2 ) ) + || force ) ) { + +/* Set the returned flag to indicate that edge labelling is being used. */ + edgelabs = 1; + +/* Initialise the pointer to the memory holding the bounding boxes for + all labels (used by function Overlap). */ + box = NULL; + +/* Get a pointer to the current Frame in the Plot. */ + frame = astGetFrame( this, AST__CURRENT ); + +/* Initialize the id value for graphical element being drawn. */ + gelid = AST__NUMLAB1_ID; + +/* If required, draw the labels for each axis in turn. */ + for( axis = 0; axis < 2 && ink; axis++ ){ + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, gelid, 1, GRF__TEXT, method, class ); + +/* Plot them. */ + info = grid[ axis ]; + PlotLabels( this, esc, frame, axis, llist[ axis ], info->fmt, + nedge[ axis ], &box, method, class, status ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, gelid, 0, GRF__TEXT, method, class ); + +/* Set up the id for the next graphical element to be drawn. */ + gelid = AST__NUMLAB2_ID; + + } + +/* Free the memory used to hold the bounding boxes. */ + box = (float *) astFree( (void *) box ); + +/* Annul the pointer to the Frame. */ + frame = astAnnul( frame ); + } + +/* Free the memory used to store the label information. */ + for( axis = 0; axis < 2; axis++ ){ + ll = llist[ axis ]; + if( ll ) { + for( tick = 0; tick < nedge[ axis ]; tick ++ ) { + ll->text = (char *) astFree( (void *) ll->text ); + ll->just = (char *) astFree( (void *) ll->just ); + ll++; + } + llist[ axis ] = (LabelList *) astFree( (void *) llist[ axis ] ); + } + } + +/* Return the flag indicating if edge labels were produced. */ + return edgelabs; + +} + +static int EdgeCrossings( AstPlot *this, int edge, int axis, double axval, + double *gap, double **cross, + EdgeCrossingsStatics **pstatics, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* EdgeCrossings + +* Purpose: +* Find all occurrences of a given physical axis value on an edge of the +* plotting area. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int EdgeCrossings( AstPlot *this, int edge, int axis, double axval, +* double *gap, double **cross, +* EdgeCrossingsStatics **pstatics, +* const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function finds all occurences of a given physical axis value +* along a specified edge of the plotting area. Firstly, a set of evenly +* spaced points ("edge samples") are placed along the edge and the +* corresponding physical coordinates are found. These physical coordinates +* are then offset slightly from their original positions in the direction +* of the "other" axis (i.e. index [ 1 - axis ] ), and transformed back +* into graphics coordinates. These coordinates give the tangent vector +* at each of the edge samples. +* +* To find the crossings, the supplied axis value is compared with the axis +* value at each sample in turn, starting from one end of the edge and +* working through to the other end. When a crossing is found, linear +* interpolation is used between the two adjacent edge samples to find a +* more accurate estimate of the crossing. The vector at the crossing +* is also estimated by linear interpolation between the vectors at the two +* adjacent samples. +* +* This basic algorithm fails if there is a discontinuity in the axis +* values along the edge. For instance, if the edge covers a range of +* Right Ascension from 23h to 1h, there will be a discontinuity at 0h +* at which the RA values suddenly jump from 2*PI to zero. This jump +* encompasses all normalised RA values and so every axis value would be +* given a crossing at this point. To avoid this, a bad sample is +* interposed between the two samples on either side of the +* discontinuity. This prevents any crossings from being placed at the +* discontinuity. +* +* There is a second problem related to discontinuities. If the supplied +* axis value is zero (using the above RA example again), then no +* crossings will be found, not only because of the extra bad sample, +* but also because the samples will not quite cover the range of axis +* values covered by the discontinuity because of the discrete nature +* of the samples). To get round this, the sections on either side +* of the discontinity are extended by a single sample. These extra +* samples are assumed to be conincident with the neighbouring sample, +* except that the value for the searched axis is modified to be a +* linear extension from the neighbouring samples. + + +* Parameters: +* this +* A pointer to the Plot. Supply a NULL pointer to release resources. +* edge +* The edge of the plotting area to be used. Edge 0 is the left hand +* edge. Edge 1 is the top edge. Edge 2 is the right-hand edge. Edge 3 +* is the bottom edge. +* axis +* The index of the axis to which "axval" refers. +* axval +* The physical axis value to be searched for. +* gap +* Pointer to array of two values holding the gap between major +* tick marks on the two axes. +* cross +* A pointer to the location at which to return a pointer to an +* array of doubles holding the crossing information. Each crossing +* is described by 4 doubles. The first pair are the graphiucs (x,y) +* coordinates of the point on the edge at which the crossing occurs. +* The second pair represents a unit vector in graphics coordinates +* which is tangential to the curve of constant axis value at the +* crossing. The memory allocated within this function to hold this +* data should be freed using astFree when no longer needed. If no +* crossings are found a NULL pointer is returned. +* pstatics +* Address of a pointer to a structure holding values for variables +* which were statically defined within this function prior to the +* thread-safe version of AST. If the pointer is supplied as NULL, +* then a new structure is allocated and initialised. Any supplied +* structure is freed if a NULL pointer is supplied for "this". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Return Value: +* The number of crossings found. + +* Notes: +* - This function allocates static resource on the first invocation +* which should be freed when no more calls are to be made, by making a +* final call with a NULL pointer supplied for "this". All other parameters +* are then ignored. +* - The static resources are re-initialised each time "edge" or +* "axis" changes, and so the calling function should be structure in +* order to minimise the number of times these parameter values change. +* - If an error has already occurred, or if this function should +* fail for any reason, zero is returned, and a NULL pointer is stored at +* "cross". + +*/ + +/* Local Variables: */ + EdgeCrossingsStatics *statics; /* Structure holding static data */ + AstMapping *mapping; /* Pointer to graphics->physical mapping */ + AstPointSet *pset1a; /* Physical cooords at offset edge samples */ + AstPointSet *pset2a; /* Physical cooords at offset edge samples */ + AstPointSet *pset3; /* Physical cooords at offset edge samples */ + AstPointSet *pset4a; /* Physical cooords at offset edge samples */ + double **ptr1a; /* Pointer to physical coord. data */ + double **ptr2a; /* Pointer to physical coord. data */ + double **ptr3; /* Pointer to physical coord. data */ + double **ptr4a; /* Pointer to physical coord. data */ + double *data; /* Pointer to next item of crossing information */ + double *p1; /* Pointer to graphics axis with constant value */ + double *p1a; /* Pointer to graphics axis with constant value */ + double *p2; /* Pointer to graphics axis with varying value */ + double *p2a; /* Pointer to graphics axis with varying value */ + double *q1; /* Pointer to physical axis being searched */ + double *q1a; /* Pointer to physical axis being searched */ + double *q2; /* Pointer to other physical axis */ + double *q2a; /* Pointer to other physical axis */ + double *v1; /* Pointer to vector component on axis 0 */ + double *v2; /* Pointer to vector component on axis 1 */ + double *v1a; /* Pointer to vector component on axis 0 */ + double *v2a; /* Pointer to vector component on axis 1 */ + double dd; /* The gap between edge samples */ + double diff; /* Squared differences between adjacent edge samples */ + double dl2; /* Squared vector length */ + double dl; /* Vector length */ + double dx; /* Vector X component */ + double dy; /* Vector Y component */ + double f; /* Weight for the current edge sample */ + double offset; /* Physical offset */ + double pp2; /* Varying graphics axis value at previous sample */ + double pq1; /* Required physical axis value at previous sample */ + double pv1; /* Previous vector component on axis 0 */ + double pv2; /* Previous vector component on axis 1 */ + double sum; /* Sum of squared differences between adjacent edge samples */ + double value; /* The current graphics axis value */ + double vx; /* Vector component on axis 0 at crossing */ + double vy; /* Vector component on axis 1 at crossing */ + double z; /* Varying graphics axis value at crossing */ + int i; /* Edge sample index */ + int iter; /* Iteration index */ + int larger; /* Is current axis value larger than target? */ + int logticks; /* Are major ticks logarithmically spaced? */ + int ncross; /* No. of crossings */ + int ndisc; /* No. of discontinuities along the edge */ + int nsum; /* Number of values summed in "sum" */ + int plarger; /* Was previous axis value larger than target? */ + +/* Get a pointer to the supplied statics object. */ + statics = *pstatics; + +/* If a NULL Plot pointer has been supplied, release the static + resources, and return. */ + if( !this ){ + if( statics ){ + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + if( statics->pset4 ) statics->pset4 = astAnnul( statics->pset4 ); + if( statics->frame ) statics->frame = astAnnul( statics->frame ); + *pstatics = astFree( statics ); + } + return 0; + } + +/* Initialise the number of crossings found, and the pointer to the place + to store them. */ + ncross = 0; + *cross = NULL; + +/* Check the global status. */ + if( !astOK ) return 0; + +/* If no statics structure was supplied, create one now and initialise it. */ + if( !statics ) { + statics = astMalloc( sizeof( EdgeCrossingsStatics ) ); + if( statics ) { + statics->frame = NULL; + statics->pset1 = NULL; + statics->pset2 = NULL; + statics->pset4 = NULL; + statics->ptr1 = NULL; + statics->ptr2 = NULL; + statics->ptr4 = NULL; + statics->paxis = -1; + statics->pedge = -1; + *pstatics = statics; + } + } + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + pp2 = 0.0; + pv1 = 0.0; + pv2 = 0.0; + plarger = 0; + +/* See if the major ticks on the other axis are logarithmically or + linearly spaced. */ + logticks = astGetLogTicks( this, 1 - axis ); + +/* Ensure that "edge" is in the range 0 - 3. */ + edge = edge % 4; + if( edge < 0 ) edge = -edge; + +/* If the edge or axis has changed since the last invocation, or if this is + the first invocation, initialise some static data. */ +/* ======================================================================*/ + if( statics->pedge == -1 || statics->pedge != edge || statics->paxis != axis ){ + +/* Save the edge and axis. */ + statics->pedge = edge; + statics->paxis = axis; + +/* Annull any previous static data objects */ + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + if( statics->pset4 ) statics->pset4 = astAnnul( statics->pset4 ); + if( statics->frame ) statics->frame = astAnnul( statics->frame ); + +/* Store some values so that the code does not need to consider each edge + separately. First deal with the left hand edge. */ + if( edge == 0 ){ + statics->edgeax = 0; + if( this->xrev ){ + statics->edgeval = this->xhi; + } else { + statics->edgeval = this->xlo; + } + statics->edgehi = this->yhi; + statics->edgelo = this->ylo; + +/* Now deal with the right hand edge. */ + } else if( edge == 2 ){ + statics->edgeax = 0; + if( this->xrev ){ + statics->edgeval = this->xlo; + } else { + statics->edgeval = this->xhi; + } + statics->edgehi = this->yhi; + statics->edgelo = this->ylo; + +/* Now deal with the bottom edge. */ + } else if( edge == 3 ){ + statics->edgeax = 1; + if( this->yrev ){ + statics->edgeval = this->yhi; + } else { + statics->edgeval = this->ylo; + } + statics->edgehi = this->xhi; + statics->edgelo = this->xlo; + + +/* Finally deal with the top edge. */ + } else { + statics->edgeax = 1; + if( this->yrev ){ + statics->edgeval = this->ylo; + } else { + statics->edgeval = this->yhi; + } + statics->edgehi = this->xhi; + statics->edgelo = this->xlo; + + } + +/* Get a pointer to the current Frame in the supplied Plot. */ + statics->frame = astGetFrame( this, AST__CURRENT ); + +/* Get a pointer to the mapping from base to current Frame in the supplied + Plot. */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Create a PointSet to hold the graphics coordinates at a set of + regularly spaced points along the specified edge of the plotting area. */ + pset1a = astPointSet( EDGETICKS_DIM, 2, "", status ); + ptr1a = astGetPoints( pset1a ); + +/* Create a PointSet to hold the corresponding physical coordinates. */ + pset2a = astPointSet( EDGETICKS_DIM, 2, "", status ); + ptr2a = astGetPoints( pset2a ); + +/* Check they can be used. */ + if( astOK ){ + +/* Set up the graphics coordinates. */ + dd = ( statics->edgehi - statics->edgelo )/(double)( EDGETICKS_DIM - 1 ); + value = statics->edgelo; + + p1 = ptr1a[ statics->edgeax ]; + p2 = ptr1a[ 1 - statics->edgeax ]; + + for( i = 0; i < EDGETICKS_DIM; i++ ){ + *(p1++) = statics->edgeval; + *(p2++) = value; + value += dd; + } + } + +/* Transform the graphics coordinates to physical coordinates, + *without* normalising them into their normal ranges. */ + (void) Trans( this, statics->frame, mapping, pset1a, 1, pset2a, 0, method, class, status ); + +/* Find the RMS step size along the axis. This is used to locate + discontinuities along the edge. Do three rejection iterations. */ + statics->limit = DBL_MAX; + for( iter = 0; iter < 3; iter ++ ){ + q1 = ptr2a[ axis ]; + pq1 = AST__BAD; + sum = 0.0; + nsum = 0; + + for( i = 0; i < EDGETICKS_DIM; i++ ){ + if( *q1 != AST__BAD && pq1 != AST__BAD ){ + diff = *q1 - pq1; + if( fabs( diff ) < statics->limit ){ + sum += diff*diff; + nsum++; + } + } + pq1 = *(q1++); + } + + if( nsum == 0 ) break; + statics->limit = 3.0*sqrt( sum/(double)nsum ); + } + +/* Now create another PointSet holding positions slightly offset from the + physical coordinates at the edge samples. The offset is in the direction + of the other physical axis. These positions are used to determine the + vector at the crossings. */ + if( nsum > 0 ){ + pset3 = astPointSet( EDGETICKS_DIM, 2, "", status ); + ptr3 = astGetPoints( pset3 ); + +/* Create a PointSet to hold the corresponding graphics coordinates. */ + pset4a = astPointSet( EDGETICKS_DIM, 2, "", status ); + ptr4a = astGetPoints( pset4a ); + +/* Check they can be used. */ + if( astOK ){ + +/* Copy the physical coordinates from PointSet 2 to PointSet 3, offseting + them slightly along the other axis. */ + p1 = ptr2a[ axis ]; + p2 = ptr2a[ 1 - axis ]; + + q1 = ptr3[ axis ]; + q2 = ptr3[ 1 - axis ]; + + offset = 0.2*gap[ 1 - axis ]; + + pq1 = AST__BAD; + + for( i = 0; i < EDGETICKS_DIM; i++ ){ + if( *p1 != AST__BAD && *p2 != AST__BAD ){ + if( logticks ) offset = 0.2*(*p2)*( gap[ 1 -axis ] - 1.0 ); + *(q2++) = *p2 + offset; + } else { + *(q2++) = AST__BAD; + } + pq1 = *(p1++); + *(q1++) = pq1; + p2++; + } + + } + +/* Transform the physical coordinates to graphics coordinates. */ + (void) Trans( this, NULL, mapping, pset3, 0, pset4a, 0, method, class, status ); + +/* Check they can be used. */ + if( astOK ){ + +/* Modify the contents of PointSet 4 to represent the unit vector in + graphics coordinates at each edge sample. */ + p1 = ptr1a[ 0 ]; + p2 = ptr1a[ 1 ]; + q1 = ptr4a[ 0 ]; + q2 = ptr4a[ 1 ]; + + for( i = 0; i < EDGETICKS_DIM; i++ ){ + if( *p1 != AST__BAD && *p2 != AST__BAD && + *q1 != AST__BAD && *q2 != AST__BAD ){ + + dx = *q1 - *p1; + dy = *q2 - *p2; + dl2 = dx*dx + dy*dy; + + if( dl2 > 0.0 ){ + dl = sqrt( dl2 ); + *q1 = dx/dl; + *q2 = dy/dl; + } else { + *q1 = AST__BAD; + *q2 = AST__BAD; + } + + } else { + *q1 = AST__BAD; + *q2 = AST__BAD; + } + + p1++; + p2++; + q1++; + q2++; + + } + + } + +/* Annul the PointSet holding offset physical cooridnates. */ + pset3 = astAnnul( pset3 ); + +/* Discontinuities in the axis values can cause problems. For instance, + using the above PointSets, no tick mark could be put at 0 hours RA + because of the discontinuity there. To get round this, 3 extra samples + are added at each discontinuity, the first extends the continuous section + which ends at the discontinuity, and the third extends the secion which + starts at the discontinuity. This results in the two sections overlapping + by one sample. The second is placed between these two and has a bad + axis value. It prevents crossings from being found in between the values + at the ends of the two sections. + + First count the number of discontinuities in the axis values. + Discontinuites are defined as steps of more than 9 times the RMS step + size. */ + q1 = ptr2a[ axis ]; + pq1 = AST__BAD; + statics->limit *= 3.0; + ndisc = 0; + + for( i = 0; i < EDGETICKS_DIM; i++ ){ + if( *q1 != AST__BAD && pq1 != AST__BAD ){ + if( fabs( *q1 - pq1 ) > statics->limit ) ndisc++; + } + pq1 = *(q1++); + } + +/* Store the size of the new PointSets holding the extra samples. */ + statics->dim = EDGETICKS_DIM + 3*ndisc; + +/* If there are no discontinuities, just clone the existing PointSets. */ + if( !ndisc ){ + statics->pset1 = astClone( pset1a ); + statics->pset2 = astClone( pset2a ); + statics->pset4 = astClone( pset4a ); + statics->ptr1 = astGetPoints( statics->pset1 ); + statics->ptr2 = astGetPoints( statics->pset2 ); + statics->ptr4 = astGetPoints( statics->pset4 ); + +/* Otherwise, create new PointSets. */ + } else { + statics->pset1 = astPointSet( statics->dim, 2, "", status ); + statics->ptr1 = astGetPoints( statics->pset1 ); + statics->pset2 = astPointSet( statics->dim, 2, "", status ); + statics->ptr2 = astGetPoints( statics->pset2 ); + statics->pset4 = astPointSet( statics->dim, 2, "", status ); + statics->ptr4 = astGetPoints( statics->pset4 ); + +/* Set up pointers used to walk through the arrays in the original + PointSets and the new PointSets. */ + p1 = statics->ptr1[ 0 ]; + p2 = statics->ptr1[ 1 ]; + q1 = statics->ptr2[ axis ]; + q2 = statics->ptr2[ 1 - axis ]; + v1 = statics->ptr4[ 0 ]; + v2 = statics->ptr4[ 1 ]; + + p1a = ptr1a[ 0 ]; + p2a = ptr1a[ 1 ]; + q1a = ptr2a[ axis ]; + q2a = ptr2a[ 1 - axis ]; + v1a = ptr4a[ 0 ]; + v2a = ptr4a[ 1 ]; + +/* Initialise the axis value at the previous sample. */ + pq1 = AST__BAD; + +/* Check all samples in the original PointSets. */ + for( i = 0; i < EDGETICKS_DIM; i++ ){ + +/* If this is the first point after a discontinuity... */ + if( *q1a != AST__BAD && pq1 != AST__BAD ){ + if( fabs( *q1a - pq1 ) > statics->limit ) { + +/* Insert an extra sample with the coordinates of the previous sample, + but with an axis value which is linearly extrapolated from the previous + samples. */ + *(p1++) = p1a[ 0 ]; + *(p2++) = p2a[ 0 ]; + *(v1++) = v1a[ -1 ]; + *(v2++) = v2a[ -1 ]; + *(q2++) = q2a[ -1 ]; + if( i > 1 && q1a[ -2 ] != AST__BAD ){ + *(q1++) = 2.0*pq1 - q1a[ -2 ]; + } else { + *(q1++) = pq1; + } + +/* Insert an extra sample with bad coordinates. */ + *(p1++) = AST__BAD; + *(p2++) = AST__BAD; + *(v1++) = AST__BAD; + *(v2++) = AST__BAD; + *(q2++) = AST__BAD; + *(q1++) = AST__BAD; + +/* Insert an extra sample with the cooridnates of the current sample, + but with an axis value which is linearly extrapolated from the + subsequent samples. */ + *(p1++) = p1a[ -1 ]; + *(p2++) = p2a[ -1 ]; + *(v1++) = *v1a; + *(v2++) = *v2a; + *(q2++) = *q2a; + if( i < EDGETICKS_DIM - 1 && q1a[ 1 ] != AST__BAD ){ + *(q1++) = 2.0*(*q1a) - q1a[ 1 ]; + } else { + *(q1++) = pq1; + } + + } + + } + +/* Save the current axis value. */ + pq1 = *q1a; + +/* Copy the current input values to the new PointSets, and move on the next + point in the original PointSets. */ + *(p1++) = *(p1a++); + *(p2++) = *(p2a++); + *(q1++) = *(q1a++); + *(q2++) = *(q2a++); + *(v1++) = *(v1a++); + *(v2++) = *(v2a++); + + } + + } + +/* Anull the original PointSets. */ + pset4a = astAnnul( pset4a ); + +/* If all the physical coordinates are bad, indicate this by setting the + limiting step size bad. */ + } else { + statics->limit = AST__BAD; + } + +/* Anull the original PointSets. */ + pset1a = astAnnul( pset1a ); + pset2a = astAnnul( pset2a ); + +/* Annul the pointer to the mapping from base to current Frame. */ + mapping = astAnnul( mapping ); + + } + +/* ======================================================================*/ +/* The initialisation has now been done. Check the physical coordinate data + can be used. */ + if( astOK && statics->limit != AST__BAD ){ + +/* Store pointers to the graphics and physical coordinates at the first + edge sample. */ + p1 = statics->ptr1[ statics->edgeax ]; /* Graphics axis with constant value */ + p2 = statics->ptr1[ 1 - statics->edgeax ]; /* Graphics axis with varying value */ + q1 = statics->ptr2[ axis ]; /* Physical axis values to be searched */ + q2 = statics->ptr2[ 1 - axis ]; /* The other physical axis */ + +/* Store pointers to the components of the unit vector at the first + edge sample. */ + v1 = statics->ptr4[ 0 ]; + v2 = statics->ptr4[ 1 ]; + +/* Inidicate that there is currently no "previous sample". */ + pq1 = AST__BAD; + +/* Check each point in turn... */ + for( i = 0; i < statics->dim; i++ ){ + +/* Skip this point if the physical coordinates are undefined. */ + if( *q1 != AST__BAD && *q2 != AST__BAD ){ + +/* Get a flag indicating if the required axis value has been exceeded at + the current edge sample. */ + larger = ( *q1 > axval ); + +/* If the state of this flag has changed since the previous edge sample, + and if we know where the previous sample was, we have found a + crossing. */ + if( pq1 != AST__BAD && larger != plarger ){ + +/* Find the distance from the previous physical axis value to the required + axis value, as a fraction of the distance from the previous axis value + to the current axis value. Since the flag has changed, we know that the + q1 value at this edge sample and the previous one must be different, so + we know that the denominator is not zero. */ + f = ( axval - pq1 )/( *q1 - pq1 ); + +/* Use linear interpolation to estimate the graphics axis value at the + crossing. */ + if( f != -1.0 ){ + z = pp2 + f*( *p2 - pp2 ); + +/* Use linear interpolation to estimate the two components of the unit + vector at the crossing. */ + if( *v1 != AST__BAD && pv1 != AST__BAD && + *v2 != AST__BAD && pv2 != AST__BAD ){ + vx = pv1 + f*( *v1 - pv1 ); + vy = pv2 + f*( *v2 - pv2 ); + +/* Normalise the vector. */ + dl2 = vx*vx + vy*vy; + if( dl2 > 0.0 ){ + dl = sqrt( dl2 ); + vx /= dl; + vy /= dl; + } else { + vx = AST__BAD; + vy = AST__BAD; + } + + } else { + vx = AST__BAD; + vy = AST__BAD; + } + +/* Grow the returned array to hold another crossing. */ + ncross++; + *cross = (double *) astGrow( (void *) *cross, ncross, + 4*sizeof( double ) ); + +/* If succesful, store the crossing. */ + if( astOK ) { + + data = *cross + 4*( ncross - 1 ); + if( statics->edgeax ){ + *(data++) = z; + *(data++) = statics->edgeval; + } else { + *(data++) = statics->edgeval; + *(data++) = z; + } + *(data++) = vx; + *(data++) = vy; + + } + + } + + } + +/* Save the flag for use on the next pass through this loop. */ + plarger = larger; + + } + +/* Save the varying graphics axis value and the required physical axis + value at the current edge sample (also save the vector). */ + pp2 = *p2; + pq1 = *q1; + pv1 = *v1; + pv2 = *v2; + +/* Point to the next edge sample. */ + p1++; + p2++; + q1++; + q2++; + v1++; + v2++; + + } + + } + +/* If an error has occurred, free the array holding the crossings, and + indicate that there are zero corssing. */ + if( !astOK ) { + *cross = (double *) astFree( (void *) *cross ); + ncross = 0; + } + +/* Return the answer. */ + return ncross; + +} + +int astFindEscape_( const char *text, int *type, int *value, int *nc, int *status ){ +/* +*+ +* Name: +* astFindEscape + +* Purpose: +* Check if a string starts with a graphics escape sequence. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot.h" +* int astFindEscape( const char *text, int *type, int *value, int *nc ) + +* Description: +* This function returns a flag indiciating if the first character in +* the supplied string is the start of a graphics escape sequence. If +* so, the type and associated value (if any) of the escape sequence +* are returned in "type" and "value", and the number of characters +* occupied by the escape sequence is returned in "nc". If the +* supplied text string does not begin with an escape sequence, the +* number of characters before the first escape sequence is returned in +* "nc" (the length of the string is returned in "nc" if the string +* contains no escape sequences). +* +* This function can be used by grf modules which wish to implement +* interpretation of escape sequences internally, rather than allowing the +* Plot class to do the interpretation. + +* Parameters: +* text +* Pointer to the string to be checked. +* type +* Pointer to a location at which to return the type of escape +* sequence. Each type is identified by a symbolic constant defined +* in grf.h. The returned value is undefined if the supplied text +* does not begin with an escape sequence. +* value +* Pointer to a lcation at which to return the integer value +* associated with the escape sequence. All usable values will be +* positive. Zero is returned if the escape sequence has no associated +* integer. A value of -1 indicates that the attribute identified by +* "type" should be reset to its "normal" value (as established using +* the astGAttr function, etc). The returned value is undefined if the +* supplied text does not begin with an escape sequence. +* nc +* Pointer to a location at which to return the number of +* characters read by this call. If the text starts with an escape +* sequence, the returned value will be the number of characters in +* the escape sequence. Otherwise, the returned value will be the +* number of characters prior to the first escape sequence, or the +* length of the supplied text if no escape sequence is found. + +* Returned Value: +* A non-zero value is returned if the supplied text starts with a +* graphics escape sequence, and zero is returned otherwise. + +* Escape Sequences: +* Escape sequences are introduced into the text string by a percent +* "%" character. The following escape sequences are currently recognised +* ("..." represents a string of one or more decimal digits): +* +* %% - Print a literal "%" character. +* +* %^...+ - Draw subsequent characters as super-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the super-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* %^+ - Draw subsequent characters with the normal base-line. +* +* %v...+ - Draw subsequent characters as sub-scripts. The digits +* "..." give the distance from the base-line of "normal" +* text to the base-line of the sub-script text, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* +* %v+ - Draw subsequent characters with the normal base-line +* (equivalent to %^+). +* +* %>...+ - Leave a gap before drawing subsequent characters. +* The digits "..." give the size of the gap, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* +* %<...+ - Move backwards before drawing subsequent characters. +* The digits "..." give the size of the movement, scaled +* so that a value of "100" corresponds to the height of +* "normal" text. +* +* %s...+ - Change the Size attribute for subsequent characters. The +* digits "..." give the new Size as a fraction of the +* "normal" Size, scaled so that a value of "100" corresponds +* to 1.0; +* +* %s+ - Reset the Size attribute to its "normal" value. +* +* %w...+ - Change the Width attribute for subsequent characters. The +* digits "..." give the new width as a fraction of the +* "normal" Width, scaled so that a value of "100" corresponds +* to 1.0; +* +* %w+ - Reset the Size attribute to its "normal" value. +* +* %f...+ - Change the Font attribute for subsequent characters. The +* digits "..." give the new Font value. +* +* %f+ - Reset the Font attribute to its "normal" value. +* +* %c...+ - Change the Colour attribute for subsequent characters. The +* digits "..." give the new Colour value. +* +* %c+ - Reset the Colour attribute to its "normal" value. +* +* %t...+ - Change the Style attribute for subsequent characters. The +* digits "..." give the new Style value. +* +* %t+ - Reset the Style attribute to its "normal" value. +* +* %h+ - Remember the current horizontal position (see "%g+") +* +* %g+ - Go to the horizontal position of the previous "%h+" (if any). +* +* %- - Push the current graphics attribute values onto the top of +* the stack (see "%+"). +* +* %+ - Pop attributes values of the top the stack (see "%-"). If +* the stack is empty, "normal" attribute values are restored. + +* Notes: +* - Zero is returned if an error has already occurred. +*- +*/ + +/* Local Variables: */ + int result; + const char *a; + const char *b; + int nd; + const char *perc; + +/* Initialise */ + result = 0; + *type = GRF__ESPER; + *value = 0; + *nc = 0; + perc = NULL; + +/* Check inherited status and supplied pointer. */ + if( !astOK || !text ) return result; + +/* Loop round, looking for percent signs. Break out of the loop when a + complete escape sequence has been found and read, leaving the "b" pointer + pointing to the first character following the escape sequence. */ + b = NULL; + a = text; + while( ( a = strchr( a, '%' ) ) ) { + perc = a; + +/* Compare the following character to each known escape sequence type. */ + a++; + if( *a == '%') { + *type = GRF__ESPER; + b = a + 1; + break; + + } else if( *a == '^') { + *type = GRF__ESSUP; + + } else if( *a == 'v') { + *type = GRF__ESSUB; + + } else if( *a == '>') { + *type = GRF__ESGAP; + + } else if( *a == '<') { + *type = GRF__ESBAC; + + } else if( *a == 's') { + *type = GRF__ESSIZ; + + } else if( *a == 'w') { + *type = GRF__ESWID; + + } else if( *a == 'f') { + *type = GRF__ESFON; + + } else if( *a == 'c') { + *type = GRF__ESCOL; + + } else if( *a == 'g') { + *type = GRF__ESG; + + } else if( *a == 'h') { + *type = GRF__ESH; + + } else if( *a == 't') { + *type = GRF__ESSTY; + + } else if( *a == '-') { + *type = GRF__ESPSH; + b = a + 1; + break; + + } else if( *a == '+') { + *type = GRF__ESPOP; + b = a + 1; + break; + +/* If the next character is illegal, skip to the next percent sign. */ + } else { + continue; + } + +/* The escape sequence looks legal so far, so move on to the next + character (if any). */ + if( *(++a) ){ + +/* If the next character is a "+" sign, the attribute needs to be reset + to its "normal" value. Indicate this by returning a value of "-1" (all + usable values will be positive). */ + if( *a == '+' ) { + *value = -1; + b = a + 1; + break; + +/* Otherwise, to be a legal escape sequence, this character must be the + first in a sequence of digits, terminated by a "+" sign.*/ + } else if( (nd = 0, astSscanf( a, "%d%n+", value, &nd ))) { + b = a + nd + 1; + break; + } + } + } + +/* Was a usable escape sequence found at the start of the supplied text? + If so, return a function value of 1 and store the number of characters in + the escape sequence. */ + if( b && perc == text ) { + result = 1; + *nc = b - perc; + +/* Otherwise, return the preset function value of zero. If an escape + sequence was found later in the text, return the number of characters + prior to the escape sequence. */ + } else if( b ) { + *nc = perc - text; + +/* Otherwise, if no escape sequence was found, return the length of the + supplied text. */ + } else { + *nc = strlen( text ); + } + +/* Return the result. */ + return result; +} + +static int FindMajTicks( AstMapping *map, AstFrame *frame, int axis, + double refval, double width, double gap, double *cen, int ngood, + double *data, double **tick_data, int *status ){ +/* +* Name: +* FindMajTicks + +* Purpose: +* Place the major tick marks for a physical coordinate axis. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int FindMajTicks( AstMapping *map, AstFrame *frame, int axis, +* double refval, double width, double gap, double *cen, int ngood, +* double *data, double **tick_data ) + +* Class Membership: +* Plot member function. + +* Description: +* The caller supplies an array of axis values (non-normalized), sorted +* into ascending order (with any bad values at the end), together with +* the gap size for the axis. The array of axis values is assumed to cover +* the entire range which the axis can take within the plotting zone. The +* first tick mark is placed just below the smallest axis value, at a +* position which is an integral number of gaps away from the value +* supplied in "cen" (if a value of AST__BAD is supplied for "cen" then +* "cen = 0.0" is assumed). Notionally, tick marks are then placed at +* intervals given by "gap" all the way upto, and just beyond, the +* largest axis value. However, it could be that large sections of the +* axis are not actually present within the plotting zone. For instance, +* an RA axis covering the two hour range from 23h to 1h (centred on +* 0h), will have some values at zero and some at 23.999.., but there +* will be a large range inbetween these limits which is not represented +* in the plotting area (i.e. the 22h range from 1h to 23h centred on +* 12h). For this reason, tick marks are removed if there are no axis +* values inbetween the tick mark and either of its neighbours. However, +* small "holes" in the axis coverage are allowed, and ticks marks are +* returned covering such small holes. Extra tick marks are also placed +* at each end of the range to guard against the supplied array of axis +* values not entirely covering the range of axis values in the plotting +* area. +* +* For SkyFrames, positions which have latitude values outside the +* normal ranges are ignored. Longitude ranges are not checked to +* avoid problems with CAR projections. +* +* The returned tick mark values are placed into their primary domain +* using the Norm1 method, but are NOT normalised using the astNorm +* method for the supplied Frame. Duplicate tick marks values are +* removed from the returned list. + +* Parameters: +* map +* Mapping from the Plot Base Frame to Plot Current Frame. +* frame +* Pointer to the Frame. +* axis +* Zero-based index of the axis being used. +* refval +* Value to use for the other axis (index [1-axis]) when placing +* the tick mark values into their primary domain. +* width +* Range of used values on the other axis (index [1-axis]). +* gap +* The supplied value for the gaps between ticks on the axis. +* cen +* Pointer to the supplied axis value at which to put a central tick. +* Other ticks will be placed evenly on either side of this tick. If +* AST__BAD is provided, a value will be used which would put a tick +* at an axis value of zero. The used value is returned. +* ngood +* The number of good values in the array pointer to by "data" (i.e. +* values not equal to AST__BAD). +* data +* A pointer to an array holding sorted axis values (non-normalized) +* covering the entire plotting area. +* tick_data +* A pointer to a place at which to store a pointer to an array +* holding the returned tick mark values for the axis. + +* Returned Value: +* The number of major tick mark values stored in the array pointer to +* by "*tick_data". + +* Notes: +* - If an error has already occurred, or if this function should fail +* for any reason, then a NULL pointer is returned in "tick_data", and zero +* is returned for the function value. +*/ + +/* Local Variables: */ + double *r; /* Pointer to next tick value to be read */ + double *ticks; /* Pointer to the axis values at the major tick marks */ + double *w; /* Pointer to last tick value to be written */ + double bot; /* Lowest axis value to be displayed */ + double centre; /* The axis value at the first tick mark */ + double delta; /* A safe distance from an axis limit */ + double f; /* The nearest acceptable tick mark index */ + double tmp; /* Temporary storage */ + double top; /* Highest axis value to be displayed */ + int inc; /* This times increase in nticks */ + int k; /* Tick mark index */ + int linc; /* Last times increase in nticks */ + int lnfill; /* Last used value for nfill */ + int nfill; /* No of tick marks to extend by at edges of coverage */ + int nsame; /* Number of equal inc values there have been */ + int nticks; /* Number of major tick marks used */ + int ntnew; /* This times new value of nticks */ + int use_nfill; /* nfill value which started this run of equal inc values */ + +/* Initialise the returned pointer. */ + *tick_data = NULL; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + nsame = 0; + use_nfill = 0; + +/* Decide where to put the first major tick. Use any value supplied by + the caller. Otherwise put it an integral number of gaps away from the + origin. This would result in the origin being at a major tick mark. */ + if( cen && *cen != AST__BAD ) { + centre = *cen; + } else { + centre = astCentre( frame, axis, data[ 0 ], gap ); + if( cen ) *cen = centre; + } + +/* Find the number of candidate tick marks assuming an nfill value of 0. */ + nfill = 0; + nticks = FindMajTicks2( nfill, gap, centre, ngood, data, &ticks, status ); + +/* Loop round increasing the nfill value until an unreasonably large value + of nfill is reached. The loop will exit early via a break statement when + all small holes in the axis coverage are filled in. */ + lnfill = nfill; + linc = -100000; + while( nfill < 100 && astOK ){ + +/* Increment the number of ticks added as "padding" at the edges of any + gaps in the coverage of axis values. */ + nfill++; + +/* Form a new set of tick mark values using this new nfill value */ + ticks = (double *) astFree( (void *) ticks ); + ntnew = FindMajTicks2( nfill, gap, centre, ngood, data, &ticks, status ); + +/* We need to know if the rate of increase of nticks has settled down to + a constant value. Inititially increasing nfill will cause the total + number of ticks (nticks) to increase rapidly. But this rate of + increase will get less as any small gaps in axis coverage are filled in. + We break out of the while loop when the rate of increase has settled + down to a constant value (indicating that only very large holes are left + in the axis coverage). Find the increase in the number of ticks caused by + the increase in the nfill value made in this loop. If this increase is the + same as the increase caused by the previous loop, increment the number of + equal increases there have been. If the increase is different to last time, + reset the number of equal increases to zero. */ + inc = ntnew - nticks; + if( inc == linc ) { + nsame++; + } else { + nsame = 0; + use_nfill = nfill; + } + +/* If the past 3 increases in nfill has not caused any change in the rate + of increase of nticks, then re-create the ticks for the value of nfill + which started the current run of equal increment values, and leave the + loop. */ + if( nsame == 3 ) { + ticks = (double *) astFree( (void *) ticks ); + nticks = FindMajTicks2( use_nfill, gap, centre, ngood, data, &ticks, status ); + break; + } + +/* Save this times values for use in the next loop. */ + linc = inc; + nticks = ntnew; + } + +/* Remove ticks which are not within the axis ranges to be displayed. + Ticks which are very close to the limit are moved to a safe (but + visually negligable) distance away from the limit). */ + bot = astGetBottom( frame, axis ); + top = astGetTop( frame, axis ); + if( bot > top ) { + tmp = top; + top = bot; + bot = tmp; + } + delta = 0.05*gap; + r = ticks; + for( k = 0; k < nticks; k++ ){ + if( *r != AST__BAD ) { + if( fabs( *r - bot ) < delta ) { + *r = bot + delta; + } else if( fabs( *r - top ) < delta ) { + *r = top - delta; + } else if( *r < bot || *r > top ) { + *r = AST__BAD; + } + } + r++; + } + +/* Use the Mapping to place each tick mark value in its primary domain. + This is a sort of normalization, similar but different to that performed + by the astNorm method. */ + Norm1( map, axis, nticks, ticks, refval, width, status ); + +/* Check for success. */ + if( astOK ){ + +/* Ensure that all ticks marks are offset from the "centre" value by an + integer multiple of the gap size. This is done by changing each tick + value to the closest acceptable value. Also ensure that values close to + zero (i.e. less than 1E-10 of the gap size) are set exactly to zero. */ + r = ticks; + for( k = 0; k < nticks; k++ ){ + if( *r != AST__BAD ) { + f = floor( 0.5 + ( *r - centre )/gap ); + *r = f*gap + centre; + if( fabs( *r ) < 1.0E-10*gap ) *r = 0.0; + r++; + } else { + r++; + } + } + +/* Sort the tick values into increasing order. */ + qsort( (void *) ticks, (size_t) nticks, sizeof(double), Compared ); + +/* Remove any duplicate or BAD tick values by shuffling the higher unique + values down to over-write them. We subtract the centre value of both + tick values before comparing them for equality in order to avoid + unnecessarily removing tick marks in high precsion data. */ + r = ticks + 1; + w = ticks; + for( k = 1; k < nticks && astOK; k++ ){ + if( *r != AST__BAD && !astEQUALS( *r-centre, *w-centre, 1.0E8 ) ){ + w++; + *w = *r; + } + r++; + } + +/* Modify the number of ticks to exclude the duplicate ones. */ + nticks = (int) ( w - ticks ) + 1; + + } + +/* If an error has occurred, free the memory holding the major tick mark + values, and indicate that zero tick marks have been found. */ + if( !astOK ){ + ticks = (double *) astFree( (void *) ticks ); + nticks = 0; + } + +/* Store the pointer to the major tick mark values. */ + *tick_data = ticks; + +/* Return the number of major ticks. */ + return nticks; + +} +static int FindMajTicks2( int nfill, double gap, double centre, int ngood, + double *data, double **tick_data, int *status ){ +/* +* Name: +* FindMajTicks2 + +* Purpose: +* Find candidate major tick marks for FindMajTicks. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int FindMajTicks2( int nfill, double gap, double centre, int ngood, +* double *data, double **tick_data, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* A service routine for function FindMajTicks. + +* Parameters: +* nfill +* Number of tick marks to extend by at edges of coverage +* gap +* The supplied value for the gaps between ticks on the axis. +* centre +* The supplied axis value at which to put a central tick. +* ngood +* The number of good values in the array pointer to by "data" (i.e. +* values not equal to AST__BAD). +* data +* A pointer to an array holding sorted axis values covering the +* entire plotting area. +* tick_data +* A pointer to a place at which to store a pointer to an array +* holding the returned tick mark values for the axis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of major tick mark values stored in the array pointer to +* by "*tick_data". + +* Notes: +* - If an error has already occurred, or if this function should fail +* for any reason, then a NULL pointer is returned in "tick_data", and zero +* is returned for the function value. +*/ + +/* Local Variables: */ + double *ticks; /* Pointer to the axis values at the major tick marks */ + int i; /* Index of current axis value */ + int j; /* Index of filled in tick */ + int k; /* Tick mark index */ + int klast; /* Index of the previous tick mark */ + int nticks; /* Number of major tick marks used */ + +/* Initialise the returned pointer. */ + *tick_data = NULL; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + nticks = 0; + +/* Reserve memory to hold a reasonable number of tick mark axis values. + This memory is later extended as necessary. */ + ticks = (double *) astMalloc( sizeof(double)*( 6*nfill + 14 ) ); + +/* Check that the pointer can be used. */ + if( astOK ){ + +/* Put the first tick marks just below the lowest axis value (in case + the grid did not sample the entire range of the axis). */ + k = floor( ( data[ 0 ] - centre )/gap ); + + for ( i = 0; i < nfill; i++ ){ + ticks[ i ] = gap*(double)( k - nfill + i ) + centre; + } + ticks[ nfill ] = gap*(double)( k ) + centre; + +/* Initialise the number of major tick marks found so far. */ + nticks = nfill + 1; + +/* Loop round each of the remaining good ordered axis values. */ + klast = k; + for( i = 1; i < ngood && astOK; i++ ) { + +/* Find the tick marks enclosing the axis value. The tick mark placed at + "centre" is called tick mark zero, and tick marks are indexed (positive + or negative) from an origin at "centre". Find the index of the more + negative of the two tick marks enclosing the axis value. */ + k = floor( ( data[ i ] - centre )/gap ); + +/* Ensure that the tick marks enclosing the current axis value are used. + Some extra tick marks are used at the start and end of any gaps in + the axis coverage. This is done to "fill in" small holes caused by the + grid of physical coordinate values not completely covering the + plotting area. Large holes, such as occur on an RA axis covering the 2 + hour range from 23 hours to 1 hour are left without any tick marks in + them (the "hole" in this case is the 22 hours range from 1 hour to 23 + hours). */ + for( j = 0; j < nfill + 1; j++ ){ + if( k - klast > nfill + 2 - j ) { + ticks = (double *) astGrow( ticks, nticks + 1, sizeof( double ) ); + if( astOK ) ticks[ nticks++ ] = + gap*(double)( klast + nfill + 1 - j ) + centre; + } + if( k - klast > nfill - j ) { + ticks = (double *) astGrow( ticks, nticks + 1, sizeof( double ) ); + if( astOK ) ticks[ nticks++ ] = + gap*(double)( k - nfill + j ) + centre; + } + } + +/* Save the index of the current tick mark. */ + klast = k; + + } + +/* Add extra tick marks beyond the end in case the grid did not sample + the entire range of the axis. */ + ticks = (double *) astGrow( ticks, nticks + nfill + 1, sizeof( double ) ); + for( i = 0; i < nfill && astOK; i++ ){ + ticks[ nticks++ ] = gap*(double)( klast + i + 1 ) + centre; + } + + } + +/* If an error has occurred, free the memory holding the major tick mark + values, and indicate that zero tick marks have been found. */ + if( !astOK ){ + ticks = (double *) astFree( (void *) ticks ); + nticks = 0; + } + +/* Store the pointer to the major tick mark values. */ + *tick_data = ticks; + +/* Return the number of major ticks. */ + return nticks; + +} + +static int FindDPTZ( AstFrame *fr, int axis, const char *fmt, + const char *text, int *ndp, int *ntz, int *status ) { +/* +* Name: +* FindDPTZ + +* Purpose: +* Find the number of decimal places and trailing zeros in a label. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int FindDPTZ( AstFrame *fr, int axis, const char *fmt, +* const char *text, int *ndp, int *ntz, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* The supplied label is split into fields using the astFields method of +* the supplied frame. The number of decimal places in the last +* field is returned in *ndp, and the total number of trailing zeros +* (excluding exponents) is returned in *ntz. + +* Parameters: +* fr +* The frame. +* axis +* The axis index to which the label applies. +* fmt +* The format string used to format the label. +* text +* The text of the label. +* ndp +* Pointer to an int in which to return the number of decimal +* places in the final field. +* ntz +* Pointer to an int in which to return the number of trailing zeros. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if and only if a non-zero digit is found in any field. + +*/ + +/* Local Constants: */ +#define MAXFLD 10 + +/* Local Variables: */ + char *fields[ MAXFLD ]; + const char *a; + const char *dot; + const char *ff; + double junk; + int fnc; + int i; + int j; + int l; + int mxnd; + int nc[ MAXFLD ]; + int nf; + int result; + +/* Initialise */ + *ndp = 0; + *ntz = 0; + result = 0; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Split the label up into fields. */ + nf = astFields( fr, axis, fmt, text, MAXFLD, fields, nc, &junk ); + if( nf > 0 ) { + +/* Search the last fields (assumed to be the least significant) for a + decimal point. */ + ff = fields[ nf - 1 ]; + fnc = nc[ nf - 1 ]; + dot = strchr( ff, '.' ); + if( dot && ( ff - dot >= fnc ) ) dot = NULL; + +/* Find the number of digits following the decimal point. */ + if( dot ) { + *ndp = strspn( dot + 1, "0123456789" ); + mxnd = fnc - ( dot - ff ) - 1; + if( *ndp > mxnd ) *ndp = mxnd; + } else { + *ndp = 0; + } + +/* Loop through all the fields, from least significant to most significant, + counting the number of trailing zeros. */ + *ntz = 0; + for( i = nf - 1; i >= 0; i-- ) { + l = strspn( fields[ i ], "-+0123456789." ); + if( l > nc[ i ] ) l = nc[ i ]; + a = fields[ i ] + l - 1; + for( j = l - 1; j >= 0; j--,a-- ){ + if( *a == '0' ) { + (*ntz)++; + } else if( isdigit( *a ) ) { + result = 1; + break; + } + } + if( j >= 0 ) break; + } + } + +/* Return the result. */ + return result; + +/* Undefine local constants: */ +#undef MAXFLD + +} + +static int FindString( int n, const char *list[], const char *test, + const char *text, const char *method, + const char *class, int *status ){ +/* +* Name: +* FindString + +* Purpose: +* Find a given string within an array of character strings. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int FindString( int n, const char *list[], const char *test, +* const char *text, const char *method, const char *class, +* int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function identifies a supplied string within a supplied +* array of valid strings, and returns the index of the string within +* the array. The test option may not be abbreviated, but case is +* insignificant. + +* Parameters: +* n +* The number of strings in the array pointed to be "list". +* list +* A pointer to an array of legal character strings. +* test +* A candidate string. +* text +* A string giving a description of the object, parameter, +* attribute, etc, to which the test value refers. +* This is only for use in constructing error messages. It should +* start with a lower case letter. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The index of the identified string within the supplied array, starting +* at zero. + +* Notes: +* - A value of -1 is returned if an error has already occurred, or +* if this function should fail for any reason (for instance if the +* supplied option is not specified in the supplied list). + +*/ + +/* Local Variables: */ + int ret; /* The returned index */ + +/* Check global status. */ + if( !astOK ) return -1; + +/* Compare the test string with each element of the supplied list. Leave + the loop when a match is found. */ + for( ret = 0; ret < n; ret++ ) { + if( !Ustrcmp( test, list[ ret ], status ) ) break; + } + +/* Report an error if the supplied test string does not match any element + in the supplied list. */ + if( ret >= n ) { + astError( AST__OPT, "%s(%s): Illegal value '%s' supplied for %s.", status, + method, class, test, text ); + ret = -1; + } + +/* Return the answer. */ + return ret; +} + +static char *FindWord( char *ptr, const char *d, const char **p, int *status ) { +/* +* Name: +* FindWord + +* Purpose: +* Return a copy of the next word in a supplied string. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* char *FindWord( char *ptr, const char *d, const char **p, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function locates the start and end of the first word in the +* string pointed to by *p, and returns a copy of the word. The pointer +* *p is modified to point to the start of the following word (if any). +* The characters which delimit words are supplied in string "d". + +* Parameters: +* ptr +* A pointer to a character string in which to store the returned +* word. The memory holding this string should have been allocated +* using one of the functions in the AST "memory" module. The memory +* area will be modified in size to fit the returned word. A NULL +* pointer may be supplied if no memory has yet been allocated. +* Any memory pointed to by ptr is freed if a NULL pointer is +* returned by this function (i.e. if no word is found). +* d +* A string holding the characters which are to be used as word +* delimiters. +* p +* The address of a character string pointer. On entry, this pointer +* identifies the start of the string to be searched. On exit, it is +* modified to point to the start of the following word. It is +* returned NULL if there are no more words. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated character string holding the +* next word, or NULL if no word could be found. + +*/ + +/* Local Variables: */ + const char *a, *b, *c; + char *ret; + int nc; + +/* Free any allocated memory and return if any of the supplied pointers + (except ptr) is NULL, or if an error has occurred. */ + if( !astOK || !d || !p || !*p ) { + (void) astFree( (void *) ptr ); + return NULL; + } + +/* Get a pointer to the first character which is not in "d". Terminate + the loop if a null character is encountered. */ + a = *p; + while( *a && strchr( d, (int) *a ) ) a++; + +/* Get a pointer to the next character which is in "d". Terminate + the loop if a null character is encountered. */ + b = a; + while( *b && !strchr( d, (int) *b ) ) b++; + +/* Get a pointer to the next character which is not in "d". Terminate + the loop if a null character is encountered. */ + c = b; + while( *c && strchr( d, (int) *c ) ) c++; + +/* Adjust the supplied pointer so that it points to the start of the next + word. */ + if( *c ){ + *p = c; + } else { + *p = NULL; + } + +/* Get a null-terminated copy of the word between a and b. */ + nc = b - a; + if( nc > 0 ) { + ret = (char *) astStore( (void *) ptr, (void *) a, (size_t) (nc + 1) ); + ret[ nc ] = 0; + } else { + ret = astFree( (void *) ptr ); + } + + return ret; +} + +static const char *SplitValue( AstPlot *this, const char *value, int axis, + int *split, int *status ) { +/* +* Name: +* FormatValue + +* Purpose: +* Format a coordinate value for a Frame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* const char *SplitValue( AstPlot *this, const char *value, +* int axis, int *split ) + +* Class Membership: +* Plot member function + +* Description: +* This function splits long formatted values (such as the date/time +* format produced by the TimeFrame class) if possible onto two lines +* by inclusion of Plot escape sequences. + +* Parameters: +* this +* Pointer to the Plot. +* value +* The formatted coordinate value. +* axis +* Indicates whether or not short lines should be split by +* including a blank first line. If zero, and if "*split" is non-zero, +* then short lines are put onto the second line,and the first line +* is blank. +* split +* Pointer to an integer that controls behaviour: +* +* 0 - Split the line if it is too long, and return a value of +1 +* in *split. +* 1 - Split the line even if it does not need splitting, making +* the first line blank and the second line containing all the +* supplied text (*split is unchanged on exit). + +* Returned Value: +* A pointer to a static buffer containing a null-terminated string +* holding the (possibly split) formatted value. This will be a copy of +* the supplied pointer if the string does not need to be split. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + char *d; + const char *result; + float rsp; + int aft_end; + int aft_start; + int bef_end; + int bef_start; + int i; + int id; + int idmin; + int imin; + int l; + int naft; + int nbef; + int nlong; + int nshort; + int nsp; + +/* Initialise */ + result = value; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Do nothing more if the formatted value already contains graphical + escape sequences, or if graphical escapes sequences are not being + interpreted. */ + if( value && astGetEscape( this ) && !HasEscapes( value, status ) ) { + +/* Attempt to find a space close to the centre of the formatted string. */ + l = strlen( value ); + idmin = 2*l; + imin = -1; + for( i = 0; i < l; i++ ) { + if( isspace( value[ i ] ) ) { + id = abs( i - l/2 ); + if( id < idmin ) { + idmin = id; + imin = i; + } + } + } + +/* We split the line if previous lines have been split (i.e. if *split was + non-zero on entry) or if this line is long AND it contains a space. This + means that a sequence of long labels will not be split unless they contain + spaces. */ + if( *split || ( l > 9 && imin != -1 ) ) { + *split = 1; + +/* Initialse the pointer into the returned buffer at which the next + character will be placed. */ + d = splitvalue_buff; + +/* If no spaces were found... */ + if( imin == -1 ) { + +/* If axis is zero, we add a blank first line. */ + if( axis == 0 ) { + +/* Fill the first line with spaces. */ + for( i = 0; i < l; i++ ) *(d++) = ' '; + +/* Add an escape sequence that moves down by one character height. */ + d += sprintf( d, "%%v170+" ); + } + +/* Add the whole of the supplied text. */ + for( i = 0; i < l; i++ ) *(d++) = value[ i ]; + +/* If a space was found... */ + } else { + +/* Find the first and last non-blank characters before the mid-space. */ + bef_start = -1; + bef_end = -1; + for( i = 0; i < imin; i++ ) { + if( !isspace( value[ i ] ) ) { + if( bef_start == -1 ) bef_start = i; + bef_end = i; + } + } + +/* Find the first and last non-blank characters after the mid-space. */ + aft_start = -1; + aft_end = -1; + for( i = imin + 1; i < l; i++ ) { + if( !isspace( value[ i ] ) ) { + if( aft_start == -1 ) aft_start = i; + aft_end = i; + } + } + +/* How many significant characters before and after the space? */ + nbef = bef_end - bef_start + 1; + naft = aft_end - aft_start + 1; + +/* Get the lengths of the longer and shorter line. */ + if( nbef > naft ) { + nlong = nbef; + nshort = naft; + } else { + nlong = naft; + nshort = nbef; + } + +/* Find the fractional number of spaces before the significant text of the + shorter line.*/ + rsp = 0.5*( nlong - nshort + 1 ); + +/* If the top line is the shorter line, put some spaces in at the start. */ + if( nbef < naft ) { + nsp = (int) rsp; + for( i = 0; i < nsp; i++ ) *(d++) = ' '; + } + +/* Add the significant text from the top line. */ + for( i = bef_start; i <= bef_end; i++ ) *(d++) = value[ i ]; + +/* Add an escape sequence that moves down by one character height. */ + d += sprintf( d, "%%v100+" ); + + +/* Add an escape sequence that moves to the left by the required amount. */ + d += sprintf( d, "%%<%d+", (int) ( 60.0*( (float) nlong - rsp )) ); + +/* Add the significant text from the bottom line. */ + for( i = aft_start; i <= aft_end; i++ ) *(d++) = value[ i ]; + + } + +/* Terminate it. */ + *d = 0; + +/* Return a pointer to the buffer. */ + result = splitvalue_buff; + } + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static void Fpoly( AstPlot *this, const char *method, const char *class, + int *status ){ +/* +* Name: +* Fpoly + +* Purpose: +* Flush all stored poly lines to the graphics system. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Fpoly( AstPlot *this, const char *method, const char *class, +* int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function sends all previously drawn poly lines to the graphics +* system for rendering, and frees the memory used to hold the poly +* lines. It attempts to reduce the number of graphics calls by +* concatenating continuous polylines together. + +* Parameters: +* this +* Pointer to the Plot. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + float xmid; + float xt; + float ymid; + float *xnew; + float *ynew; + int polylen; + float *xp1; + float *xp2; + float *yp1; + float *yp2; + float yt; + int *ekey; + int *p; + int *skey; + int *drawn; + int ihi; + int ikey; + int ilo; + int imid; + int ipass; + int ipoint; + int ipoly; + int jpoly; + int kpoly; + int *polylist; + int npoly; + int np; + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Output any pending polyline. */ + Opoly( this, status ); + +/* If there is just one polyline to output, just draw it and then free + the memory used to hold the polyline. */ + if( Poly_npoly == 1 ) { + GLine( this, Poly_np[ 0 ], Poly_xp[ 0 ], Poly_yp[ 0 ], method, class, + status ); + Poly_xp[ 0 ] = astFree( Poly_xp[ 0 ] ); + Poly_yp[ 0 ] = astFree( Poly_yp[ 0 ] ); + Poly_np[ 0 ] = 0; + +/* If there are multiple polylines to output, see if any of them can be + combined before drawing them. */ + } else if( Poly_npoly > 1 ) { + +/* No polyline buffer allocated yet. */ + xnew = NULL; + ynew = NULL; + +/* Allocate an array to hold the order in which polylines should be + concatenated. Each value in this array will be the index of one of the + original polylines. A positive index indicates that the polyline + should be appended in its original order. A negative index indicates + that the polyline should be appended in reversed order. Polyline zero + is always appended in its original order. */ + polylist = astMalloc( Poly_npoly*sizeof( int ) ); + npoly = 0; + +/* Create an array of drawn, one for each individual polyline. The flag + is zero if the corresponding polyline has not yet been drawn. */ + drawn = astCalloc( Poly_npoly, sizeof( int ) ); + +/* Create two sorted keys for the polylines - one that sorts them into + increasing x at the start of the polyline, and another that sorts them + into increasing x at the end of the polyline. */ + skey = astMalloc( Poly_npoly*sizeof( int ) ); + ekey = astMalloc( Poly_npoly*sizeof( int ) ); + if( astOK ) { + + p = skey; + for( ipoly = 0; ipoly < Poly_npoly; ipoly++ ) *(p++) = ipoly; + qsort( skey, Poly_npoly, sizeof(int), Fpoly_scmp ); + + p = ekey; + for( ipoly = 0; ipoly < Poly_npoly; ipoly++ ) *(p++) = ipoly; + qsort( ekey, Poly_npoly, sizeof(int), Fpoly_ecmp ); + + } + +/* Continue to search for separate polylines that can be combined together + until we know there are no more. */ + while( astOK ) { + +/* Search for the first polyline that has not already been drawn. */ + for( ipoly = 0; ipoly < Poly_npoly; ipoly++ ) { + if( !drawn[ ipoly ] ) break; + } + +/* Leave the loop if no more polylines remain to be plotted. */ + if( ipoly == Poly_npoly ) break; + +/* Initialise the list of polylines to hold the polyline found above, in + its forward sense. */ + polylist[ 0 ] = ipoly; + npoly = 1; + drawn[ 0 ] = 1; + +/* Initialise the concatenation point to be the end of the polyline found + above. Also, initialise the total number of points in the combined + polyline (polylen). */ + ipoint = Poly_np[ ipoly ] - 1; + xt = Poly_xp[ ipoly ][ ipoint ]; + yt = Poly_yp[ ipoly ][ ipoint ]; + polylen = ipoint + 1; + +/* Loop until we can find no more polylines to append to the list. + A polyline can be appended if it starts or ends at the current + concatenation point. */ + while( astOK ) { + +/* On the first pass through the next loop, search for a polyline that + starts at the concatenation point. If no such polyline is found, do + a second pass in which we search for a polyline that ends at the + concatenation point. Do not include any previously drawn polylines + in the search. */ + for( ipass = 0; ipass < 2; ipass++ ) { + +/* We use a binary chop to find a polyline which starts (or ends) at the + x value of the concatenation point. */ + jpoly = -1; + ilo = 0; + ihi = Poly_npoly - 1; + while( 1 ) { + imid = ( ilo + ihi )/2; + if( ipass == 0 ) { + jpoly = skey[ imid ]; + xmid = Poly_xp[ jpoly ][ 0 ]; + } else { + jpoly = ekey[ imid ]; + xmid = Poly_xp[ jpoly ][ Poly_np[ jpoly ] - 1 ]; + } + if( astEQUALS( xmid, xt, 1.0E8 ) ) { + ikey = imid; + break; + } else if( xmid > xt ) { + if( ihi == imid ) { + if( ipass == 0 ) { + jpoly = skey[ ilo ]; + xmid = Poly_xp[ jpoly ][ 0 ]; + } else { + jpoly = ekey[ ilo ]; + xmid = Poly_xp[ jpoly ][ Poly_np[ jpoly ] - 1 ]; + } + if( !astEQUALS( xmid, xt, 1.0E8 ) ) jpoly = -1; + ikey = ilo; + break; + } + ihi = imid; + } else { + if( ilo == imid ) { + if( ipass == 0 ) { + jpoly = skey[ ihi ]; + xmid = Poly_xp[ jpoly ][ 0 ]; + } else { + jpoly = ekey[ ihi ]; + xmid = Poly_xp[ jpoly ][ Poly_np[ jpoly ] - 1 ]; + } + if( !astEQUALS( xmid, xt, 1.0E8 ) ) jpoly = -1; + ikey = ihi; + break; + } + ilo = imid; + } + } + +/* If found, there may be more than one such polyline. So we now search + for a polyline that also has the y value of the concatenation point. */ + if( jpoly != -1 ) { + +/* If the polyline found above starts (or ends) at the same Y value as the + concatenation point, then we have found the required polyline. */ + if( ipass == 0 ) { + ymid = Poly_yp[ jpoly ][ 0 ]; + } else { + ymid = Poly_yp[ jpoly ][ Poly_np[ jpoly ] - 1 ]; + } + if( astEQUALS( ymid, yt, 1.0E8 ) && !drawn[ jpoly ] ) break; + jpoly = -1; + +/* Otherwise, search down the list, starting at the polyline found above. */ + if( imid > 0 ) { + for( ikey = imid - 1; ikey >= 0; ikey-- ) { + if( ipass == 0 ) { + kpoly = skey[ ikey ]; + xmid = Poly_xp[ kpoly ][ 0 ]; + ymid = Poly_yp[ kpoly ][ 0 ]; + } else { + kpoly = ekey[ ikey ]; + xmid = Poly_xp[ kpoly ][ Poly_np[ kpoly ] - 1 ]; + ymid = Poly_yp[ kpoly ][ Poly_np[ kpoly ] - 1 ]; + } + if( astEQUALS( xmid, xt, 1.0E8 ) ) { + if( astEQUALS( ymid, yt, 1.0E8 ) && !drawn[ kpoly ] ) { + jpoly = kpoly; + break; + } + } else { + break; + } + } + if( jpoly != -1 ) break; + } + +/* Now search up the list, starting at the polyline found above. */ + if( imid < Poly_npoly - 1 ) { + for( ikey = imid + 1; ikey < Poly_npoly; ikey++ ) { + if( ipass == 0 ) { + kpoly = skey[ ikey ]; + xmid = Poly_xp[ kpoly ][ 0 ]; + ymid = Poly_yp[ kpoly ][ 0 ]; + } else { + kpoly = ekey[ ikey ]; + xmid = Poly_xp[ kpoly ][ Poly_np[ kpoly ] - 1 ]; + ymid = Poly_yp[ kpoly ][ Poly_np[ kpoly ] - 1 ]; + } + if( astEQUALS( xmid, xt, 1.0E8 ) ) { + if( astEQUALS( ymid, yt, 1.0E8 ) && !drawn[ kpoly ] ) { + jpoly = kpoly; + break; + } + } else { + break; + } + } + if( jpoly != -1 ) break; + } + } + } + +/* If a polyline was found that can be combined with the total polyline, + increment the total number of points in the total polyline, add it to + the list, and update the concatenation point. Note, we can omit the + start or end point of the new polyline since it will already be + present in the total polyline, hence the " - 1 " below. */ + if( ipass < 2 ) { + ipoint = Poly_np[ jpoly ] - 1; + + if( ipass == 0 ) { + polylist[ npoly++ ] = jpoly; + xt = Poly_xp[ jpoly ][ ipoint ]; + yt = Poly_yp[ jpoly ][ ipoint ]; + } else { + polylist[ npoly++ ] = -jpoly; + xt = Poly_xp[ jpoly ][ 0 ]; + yt = Poly_yp[ jpoly ][ 0 ]; + } + + polylen += ipoint; + +/* Indicate the polyline has been drawn. */ + drawn[ jpoly ] = 1; + +/* If we cannot find any polyline that starts or ends at the + concatenation point, then we have completed the total polyline. So break + out of the loop, and move on to draw the total polyline. */ + } else { + break; + } + } + +/* If a single polyline is to be drawn, just draw it. */ + if( npoly == 1 ) { + jpoly = polylist[ 0 ]; + GLine( this, Poly_np[ jpoly ], Poly_xp[ jpoly ], + Poly_yp[ jpoly ], method, class, status ); + +/* If more than one polyline is to be drawn, ensure we have arrays that + are large enough to hold all the vertices in the combined polyline. */ + } else { + xnew = astRealloc( xnew, polylen*sizeof( float ) ); + ynew = astRealloc( ynew, polylen*sizeof( float ) ); + if( astOK ) { + +/* Loop round all the polylines that are to be combined to form the total + polyline, and copy all the vertex coordinates into the above arrays. */ + xp1 = xnew; + yp1 = ynew; + for( ipoly = 0; ipoly < npoly; ipoly++ ) { + +/* Index of the next polyline to include in the total polyline. */ + jpoly = polylist[ ipoly ]; + +/* The jpoly value is positive if the polylline is to be inclued in its + original direction. */ + if( jpoly >= 0 ) { + +/* Use the whole of the first polyline. */ + if( ipoly == 0 ) { + np = Poly_np[ jpoly ]; + xp2 = Poly_xp[ jpoly ]; + yp2 = Poly_yp[ jpoly ]; + +/* Omit eh first point of subsequent polylines since it will be the same + as the last pointy already in the total polyline. */ + } else { + np = Poly_np[ jpoly ] - 1; + xp2 = Poly_xp[ jpoly ] + 1; + yp2 = Poly_yp[ jpoly ] + 1; + } + +/* Copy the vertex values in their original order, and update the + pointers to the next element of the total polyline. */ + memcpy( xp1, xp2, np*sizeof(float) ); + memcpy( yp1, yp2, np*sizeof(float) ); + xp1 += np; + yp1 += np; + +/* The jpoly value is negative if the polyline is to be included in its + reversed direction. */ + } else { + jpoly = -jpoly; + +/* Get the number of points to copy. Omit the last point if this is not + the first polyline, since it is already in the total polyline. */ + if( ipoly == 0 ) { + np = Poly_np[ jpoly ]; + } else { + np = Poly_np[ jpoly ] - 1; + } + +/* Copy the individual values in reversed order. */ + xp2 = Poly_xp[ jpoly ] + np - 1; + yp2 = Poly_yp[ jpoly ] + np - 1; + for( ipoint = 0; ipoint < np; ipoint++ ) { + *(xp1++) = *(xp2--); + *(yp1++) = *(yp2--); + } + } + } + +/* And finally, draw the total polyline. */ + GLine( this, polylen, xnew, ynew, method, class, status ); + } + } + } + +/* Free all the individual polylines. */ + for( ipoly = 0; ipoly < Poly_npoly; ipoly++ ) { + Poly_xp[ ipoly ] = astFree( Poly_xp[ ipoly ] ); + Poly_yp[ ipoly ] = astFree( Poly_yp[ ipoly ] ); + Poly_np[ ipoly ] = 0; + } + +/* Free other resources. */ + polylist = astFree( polylist ); + drawn = astFree( drawn ); + xnew = astFree( xnew ); + ynew = astFree( ynew ); + skey = astFree( skey ); + ekey = astFree( ekey ); + } + +/* Indicate that all polylines have been sent to the graphics system. */ + Poly_npoly = 0; +} + +static int Fpoly_ecmp( const void *a, const void *b ){ +/* +* Name: +* Fpoly_ecmp + +* Purpose: +* Compare two polylines ending X position + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Fpoly_ecmp( const void *a, const void *b ) + +* Class Membership: +* Plot member function. + +* Description: +* This function is designed to be used as a comparison function with +* the "qsort" function. It is used in function Fpoly. +* +* If orders the two polylines on the basis of the X coordinate at +* their ends. + +* Parameters: +* a +* Pointer to an int holding the index of the first polyline. +* b +* Pointer to an int holding the index of the second polyline. + +* Returned Value: +* -1 if the first polyline ends at a lower X than the second. +* +1 if the first polyline ends at a higher X than the second. +* 0 if the two polylines end at the same X. + +*/ + +/* Local Variables: */ + float xa; /* X at end of first polyline */ + float xb; /* X at end of second polyline */ + int result = 0; /* Returned value */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get the x coord at the end of the two polylines. */ + xa = Poly_xp[ *( (int *) a) ][ Poly_np[ *( (int *) a) ] - 1 ]; + xb = Poly_xp[ *( (int *) b) ][ Poly_np[ *( (int *) b) ] - 1 ]; + +/* Compare them. */ + if( xa < xb ) { + result = -1; + } else if( xa > xb ){ + result = 1; + } + + return result; +} + +static int Fpoly_scmp( const void *a, const void *b ){ +/* +* Name: +* Fpoly_scmp + +* Purpose: +* Compare two polylines starting X position + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Fpoly_scmp( const void *a, const void *b ) + +* Class Membership: +* Plot member function. + +* Description: +* This function is designed to be used as a comparison function with +* the "qsort" function. It is used in function Fpoly. +* +* If orders the two polylines on the basis of the X coordinate at +* their starts. + +* Parameters: +* a +* Pointer to an int holding the index of the first polyline. +* b +* Pointer to an int holding the index of the second polyline. + +* Returned Value: +* -1 if the first polyline starts at a lower X than the second. +* +1 if the first polyline starts at a higher X than the second. +* 0 if the two polylines starts at the same X. + +*/ + +/* Local Variables: */ + float xa; /* X at start of first polyline */ + float xb; /* X at start of second polyline */ + int result = 0; /* Returned value */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get the x coord at the start of the two polylines. */ + xa = Poly_xp[ *( (int *) a) ][ 0 ]; + xb = Poly_xp[ *( (int *) b) ][ 0 ]; + +/* Compare them. */ + if( xa < xb ) { + result = -1; + } else if( xa > xb ){ + result = 1; + } + + return result; +} + +static AstFrameSet *Fset2D( AstFrameSet *fset, int ifrm, int *status ) { +/* +* Name: +* Fset2D + +* Purpose: +* Create a FrameSet with no more than 2 dimensions for a given Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* AstFrameSet *Fset2D( AstFrameSet *fset, int ifrm, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function checks a specified Frame in the supplied FrameSet. +* If the Frame has more than 2 dimensions, a new Frame is added to +* the FrameSet containing just the first two axes of the specified +* Frame. A PermMap is used to connect this Frame to the specified +* Frame, which supplied bad values for any missing axes. If the +* specified Frame is the base Frame in the supplied FrameSet, then the +* new Frame becomes the base Frame in the returned FrameSet. Like-wise, +* if the specified Frame is the current Frame, then the new Frame +* will be the current Frame in the returned FrameSet. +* +* If the specified Frame does not have more than 2 axes, then a clone +* of the FrameSet pointer is returned, otherwise the returned pointer +* points to a copy of the supplied FrameSet with the new 2-D Frame +* added. + +* Parameters: +* fset +* Pointer to the FrameSet. +* ifrm +* The index of the Frame to check. This should be AST__BASE or +* AST_CURRENT. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a FrameSet in which the Frame with index given by ifrm +* has no more than 2 axes. +*/ + +/* Local Variables: */ + AstFrame *frm; + AstFrame *newfrm; + AstFrameSet *ret; + AstPermMap *map; + double zero; + int *inperm; + int axes[2]; + int i; + int ic; + int nax; + +/* Check the inherited status. */ + if( !astOK ) return NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + map = NULL; + +/* Get a pointer to the requested Frame in the supplied FrameSet. */ + frm = astGetFrame( fset, ifrm ); + +/* See how many dimensions the specified Frame of the supplied FrameSet + has. */ + nax = astGetNaxes( frm ); + +/* If it is more than 2-dimensionbal, create a 2D Frame by picking + axes 1 and 2 from the original Frame. */ + if( nax > 2 ) { + axes[ 0 ] = 0; + axes[ 1 ] = 1; + newfrm = astPickAxes( frm, 2, axes, NULL ); + +/* Create a PermMap to describe the mapping between the two Frames. + Use zero as the value for unknown axes (the optional mapping which + can be returned by astPickAxes uses AST__BAD for unknown axes). */ + inperm = (int *) astMalloc( sizeof(int)*(size_t) nax ); + if( astOK ){ + inperm[ 0 ] = 0; + inperm[ 1 ] = 1; + for( i = 2; i < nax; i++ ) inperm[ i ] = -1; + zero = 0.0; + map = astPermMap( nax, inperm, 2, axes, &zero, "", status ); + inperm = (int *) astFree( (void *) inperm ); + } + +/* Get a copy of the supplied FrameSet. */ + ret = astCopy( fset ); + +/* Add the new Frame to the FrameSet (it becomes the current Frame). */ + ic = astGetCurrent( ret ); + astAddFrame( ret, ifrm, map, newfrm ); + newfrm = astAnnul( newfrm ); + +/* If the new Frame was derived from the base frame, set the new base + Frame, and re-instate the original current Frame */ + if( ifrm == AST__BASE ){ + astSetBase( ret, astGetCurrent( ret ) ); + astSetCurrent( ret, ic ); + } + +/* If the specified Frame in the supplied FrameSet is 2-dimensional, just + return a clone of it. */ + } else { + ret = astClone( fset ); + } + +/* Annul the pointer to the original Frame. */ + frm = astAnnul( frm ); + + return ret; + +} + +static int FullForm( const char *list, const char *test, const char *text, + const char *method, const char *class, int *status ){ +/* +* Name: +* FullForm + +* Purpose: +* Identify the full form of an option string. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int FullForm( const char *list, const char *test, const char *text, +* const char *method, const char *class, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function identifies a supplied test option within a supplied +* list of valid options, and returns the index of the option within +* the list. The test option may be abbreviated, and case is +* insignificant. + +* Parameters: +* list +* A list of space separated option strings. +* test +* A candidate option string. +* text +* A string giving the context in which the supplied test option +* was supplied. For instance, this may be an attribute setting string. +* This is only for use in constructing error messages. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The index of the identified option within the supplied list, starting +* at zero. + +* Notes: +* - A value of -1 is returned if an error has already occurred, or +* if this function should fail for any reason (for instance if the +* supplied option is not uniquely specified in the supplied list). + +*/ + +/* Local Variables: */ + char *option; /* Pointer to a copy of the next option */ + const char *p; /* Pointer to the start of the next word */ + int i; /* Current option index */ + int len; /* Length of supplied option */ + int nmatch; /* Number of matching options */ + int ret; /* The returned index */ + +/* Initialise the answer to indicate that the option has not been + uniquely identified. */ + ret = -1; + +/* Check global status. */ + if( !astOK ) return ret; + +/* Save the number of characters in the supplied test option (excluding + trailing spaces). */ + len = ChrLen( test, status ); + +/* Compare the supplied test option against each of the known options in + turn. Count the number of matches. */ + nmatch = 0; + p = list; + option = FindWord( NULL, " ", &p, status ); + i = 0; + while( option ){ + +/* If the test string and the current option are identical (including + length). use the current option. */ + +/* If every character in the supplied label matches the corresponding + character in the current test label we have a match. Increment the + number of matches and save the current item index. If the test string + and the current option are identical (including length), use the + current option. */ + + if( !Ustrncmp( test, option, len, status ) ) { + ret = i; + if( ChrLen( option, status ) == len ) { + nmatch = 1; + option = astFree( option ); + break; + } else { + nmatch++; + } + } + +/* Get a pointer to the next option. */ + option = FindWord( option, " ", &p, status ); + i++; + } + +/* Report an error if no match was found, and return -1. */ + if( !nmatch ){ + astError( AST__OPT, "%s(%s): Option '%.*s' is unknown in '%.*s'.", status, + method, class, len, test, ChrLen( text, status ), text ); + ret = -1; + +/* Report an error if the label was ambiguous, and return -1. */ + } else if( nmatch > 1 ){ + astError( AST__OPT, "%s(%s): Option '%.*s' is ambiguous in '%.*s'.", status, + method, class, len, test, ChrLen( text, status ), text ); + ret = -1; + } + +/* Return the answer. */ + return ret; +} + +static void GAttr( AstPlot *this, int attr, double value, double *old_value, + int prim, const char *method, const char *class, int *status ) { +/* +* +* Name: +* GAttr + +* Purpose: +* Call the GAttr Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GAttr( AstPlot *this, int attr, double value, double *old_value, +* int prim, const char *method, const char *class, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GAttr grf function to enquire or set a +* graphics attribute value. It either calls the version registered using +* astGrfSet, or the version in the linked grf module. The linked version +* is used if the Grf attribute is zero, or if no function has been +* registered for GAttr using astGrfSet. + +* Parameters: +* this +* The Plot. +* attr +* An integer value identifying the required attribute. The +* following symbolic values are defined in grf.h: +* +* GRF__STYLE - Line style. +* GRF__WIDTH - Line width. +* GRF__SIZE - Character and marker size scale factor. +* GRF__FONT - Character font. +* GRF__COLOUR - Colour index. +* value +* A new value to store for the attribute. If this is AST__BAD +* no value is stored. +* old_value +* A pointer to a double in which to return the attribute value. +* If this is NULL, no value is returned. +* prim +* The sort of graphics primitive to be drawn with the new attribute. +* Identified by the following values defined in grf.h: +* GRF__LINE +* GRF__MARK +* GRF__TEXT +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. Also return if there is nothing to do. */ + if ( !astOK || ( !old_value && value == AST__BAD ) ) return; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GATTR ] ) { + grf_status = ( *( this->GAttr ) )( this, attr, value, old_value, prim, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGAttr( attr, value, old_value, prim ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGAttr. ", status, method, + class ); + } + +} + +static AstKeyMap *GetGrfContext( AstPlot *this, int *status ){ +/* +*++ +* Name: +c astGetGrfContext +f AST_GETGRFCONTEXT + +* Purpose: +* Return the KeyMap that describes a Plot's graphics context. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c AstKeyMap *astGetGrfContext( AstPlot *this ) +f RESULT = AST_GETGRFCONTEXT( THIS, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function +f This routine +* returns a reference to a KeyMap that will be passed to any drawing +c functions registered using astGrfSet. +f routines registered using AST_GRFSET. +* This KeyMap can be used by an application to pass information to +c the drawing functions +f the drawing routines +* about the context in which they are being called. The contents of +* the KeyMap are never accessed byt the Plot class itself. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetGrfContext() +f AST_GETGRFCONTEXT = INTEGER +* A pointer to the graphics context KeyMap. The returned pointer +* should be annulled when it is no longer needed. + +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Ensure that a grfcon KeyMap exists. */ + (void) astGrfConID(this); + +/* Return a cloned pointer to the KeyMap. */ + return astClone( this->grfcontext ); +} + +AstKeyMap *astGrfConID_( AstPlot *this, int *status ) { +/* +*+ +* +* Name: +* astGrfConID + +* Purpose: +* Ensure a GrfContext KeyMap exists and return an ID for it. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot.h" +* AstKeyMap *astGrfConID( AstPlot *this ) + +* Class Membership: +* Plot private function. + +* Description: +* This function creates a GrfContext KeyMap if the Plot does not +* currently have one, and returns an ID for it. + +* Parameters: +* this +* The Plot. + +* Returned Value: +* ID for the GrfContext KeyMap. + +*- +*/ + if( !this->grfcontext ) { + this->grfcontext = astKeyMap("", status ); + this->grfcontextID = astMakeId( astClone( this->grfcontext ) ); + astExempt( this->grfcontextID ); + } + return this->grfcontextID; +} + +static void GScales( AstPlot *this, float *alpha, float *beta, + const char *method, const char *class, int *status ) { +/* +* +* Name: +* GScales + +* Purpose: +* Call the GScales Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GScales( AstPlot *this, float *alpha, float *beta, +* const char *method, const char *class, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GScales grf function, either calling the +* version registered using astGrfSet, or the version in the linked grf +* module. The linked version is used if the Grf attribute is zero, or if +* no function has been registered for GScales using astGrfSet. + +* Parameters: +* this +* The Plot. +* alpha +* A pointer to the location at which to return the scale for the +* X axis (i.e. Xnorm = alpha*Xworld). +* beta +* A pointer to the location at which to return the scale for the +* Y axis (i.e. Ynorm = beta*Yworld). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* if we already have the required values, return them. */ + if( Grf_alpha != 0.0 && Grf_beta != 0.0 ) { + if( alpha ) *alpha = Grf_alpha; + if( beta ) *beta = Grf_beta; + return; + } + +/* Check that the grf mdoule can give us scales information. */ + if( GCap( this, GRF__SCALES, 1, status ) ) { + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This is called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GSCALES ] ) { + grf_status = ( *( this->GScales ) )( this, &Grf_alpha, &Grf_beta, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGScales( &Grf_alpha, &Grf_beta ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Check neither value is zero. */ + if( grf_status && ( Grf_alpha == 0.0 || Grf_beta == 0.0 ) ) { + astError( AST__GRFER, "astGScales: Returned axis scales are %g and %g " + "but zero is illegal!", status, Grf_alpha, Grf_beta ); + grf_status = 0; + } + +/* Report an error if anything went wrong, and return safe values. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGScales. ", status, method, + class ); + Grf_alpha = 1.0; + Grf_beta = 1.0; + } + +/* If the grf module is not capable of giving us scale information, then + just assume the the axes are equally scaled, except for any axis reversal + indicated in the supplied Plot. */ + } else { + Grf_alpha = ( this->xrev ) ? -1.0 : 1.0; + Grf_beta = ( this->yrev ) ? -1.0 : 1.0; + } + +/* Store them for future use. */ + if( alpha ) *alpha = Grf_alpha; + if( beta ) *beta = Grf_beta; +} + +static int GCap( AstPlot *this, int cap, int value, int *status ){ +/* +* +* Name: +* GCap + +* Purpose: +* Call the GCap Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int GCap( AstPlot *this, int cap, int value, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GCap grf function to inquire a capability +* of the grf module, either calling the version registered using +* astGrfSet, or the version in the linked grf module. The linked +* version is used if the Grf attribute is zero, or if no function +* has been registered for GCap using astGrfSet. + +* Parameters: +* this +* The Plot. +* cap +* The capability to be inquired aboue. +* value +* The value ot assign to the capability. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the grf module is capabale of performing the action +* requested by "cap". + +*/ + +/* Local Variables: */ + int result; /* Value retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This is called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GCAP ] ) { + result = ( *( this->GCap ) )( this, cap, value, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + result = astGCap( cap, value ); + + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Return the result. */ + return result; +} + +static void GenCurve( AstPlot *this, AstMapping *map, int *status ){ +/* +*++ +* Name: +c astGenCurve +f AST_GENCURVE + +* Purpose: +* Draw a generalized curve. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astGenCurve( AstPlot *this, astMapping *map ) +f CALL AST_GENCURVE( THIS, MAP ) + +* Class Membership: +* Plot method. + +* Description: +c This function draws a general user-defined curve defined by the +f This routine draws a general user-defined curve defined by the +* supplied Mapping. Note that the curve is transformed into graphical +* coordinate space for plotting, so that a straight line in +* physical coordinates may result in a curved line being drawn if +* the Mapping involved is non-linear. Any discontinuities in the +* Mapping between physical and graphical coordinates are +c catered for, as is any clipping established using astClip. +f catered for, as is any clipping established using AST_CLIP. +* +c If you need to draw simple straight lines (geodesics), astCurve +c or astPolyCurve will usually be easier to use and faster. +f If you need to draw simple straight lines (geodesics), AST_CURVE +f or AST_POLYCURVE will usually be easier to use and faster. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c map +f MAP = INTEGER (Given) +* Pointer to a Mapping. This Mapping should have 1 input +* coordinate representing offset along the required curve, +* normalized so that the start of the curve is at offset 0.0, +* and the end of the curve is at offset 1.0. Note, this offset +* does not need to be linearly related to distance along the curve. +* The number of output coordinates should equal the number of axes +* in the current Frame of the Plot. The Mapping should map a +* specified offset along the curve, into the corresponding +* coordinates in the current Frame of the Plot. The inverse +* transformation need not be defined. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - An error results if the base Frame of the Plot is not 2-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*-- +*/ +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const char *class; /* Object class */ + const char *method; /* Current method */ + double d[ CRV_NPNT ]; /* Offsets to evenly spaced points along curve */ + double tol; /* Absolute tolerance value */ + double x[ CRV_NPNT ]; /* X coords at evenly spaced points along curve */ + double y[ CRV_NPNT ]; /* Y coords at evenly spaced points along curve */ + int i; /* Loop count */ + int naxes; /* No. of axes in the base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astGenCurve"; + class = astGetClass( this ); + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Only proceed if there has been no error. */ + if( astOK ){ + +/* Initialise the bounding box for primitives produced by this call. */ + if( !Boxp_freeze ) { + Boxp_lbnd[ 0 ] = FLT_MAX; + Boxp_lbnd[ 1 ] = FLT_MAX; + Boxp_ubnd[ 0 ] = FLT_MIN; + Boxp_ubnd[ 1 ] = FLT_MIN; + } + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__CURVE_ID, 1, GRF__LINE, method, class ); + +/* Ensure the globals holding the scaling from graphics coords to equally + scaled coords are available. */ + GScales( this, NULL, NULL, method, class, status ); + +/* Set up the externals used to communicate with the Map4 function... */ + Map4_ncoord = astGetNout( this ); + Map4_plot = this; + Map4_map = astGetMapping( this, AST__BASE, AST__CURRENT ); + Map4_umap = map; + +/* Convert the tolerance from relative to absolute graphics coordinates. */ + tol = astGetTol( this )*astMAX( this->xhi - this->xlo, + this->yhi - this->ylo ); + +/* Now set up the external variables used by the Crv and CrvLine function. */ + Crv_scerr = ( astGetLogPlot( this, 0 ) || + astGetLogPlot( this, 1 ) ) ? 100.0 : 1.5; + Crv_ux0 = AST__BAD; + Crv_tol = tol; + Crv_limit = 0.5*tol*tol; + Crv_map = Map4; + Crv_ink = 1; + Crv_xlo = this->xlo; + Crv_xhi = this->xhi; + Crv_ylo = this->ylo; + Crv_yhi = this->yhi; + Crv_out = 1; + Crv_xbrk = Curve_data.xbrk; + Crv_ybrk = Curve_data.ybrk; + Crv_vxbrk = Curve_data.vxbrk; + Crv_vybrk = Curve_data.vybrk; + Crv_clip = astGetClip( this ) & 1; + +/* Set up a list of points spread evenly over the curve. */ + for( i = 0; i < CRV_NPNT; i++ ){ + d[ i ] = ( (double) i)/( (double) CRV_NSEG ); + } + +/* Map these points into graphics coordinates. */ + Map4( CRV_NPNT, d, x, y, method, class, status GLOBALS_NAME ); + +/* Use Crv and Map4 to draw the curve. */ + Crv( this, d, x, y, 0, NULL, NULL, method, class, status ); + +/* End the current poly line. */ + Opoly( this, status ); + +/* Tidy up the static data used by Map4. */ + Map4( 0, NULL, NULL, NULL, method, class, status GLOBALS_NAME ); + +/* If no part of the curve could be drawn, set the number of breaks and the + length of the drawn curve to zero. */ + if( Crv_out ) { + Crv_nbrk = 0; + Crv_len = 0.0F; + +/* Otherwise, add an extra break to the returned structure at the position of + the last point to be plotted. */ + } else { + Crv_nbrk++; + if( Crv_nbrk > AST__PLOT_CRV_MXBRK ){ + astError( AST__CVBRK, "%s(%s): Number of breaks in curve " + "exceeds %d.", status, method, class, AST__PLOT_CRV_MXBRK ); + } else { + *(Crv_xbrk++) = (float) Crv_xl; + *(Crv_ybrk++) = (float) Crv_yl; + *(Crv_vxbrk++) = (float) -Crv_vxl; + *(Crv_vybrk++) = (float) -Crv_vyl; + } + } + +/* Store extra information about the curve in the returned structure, and + purge any zero length sections. */ + Curve_data.length = Crv_len; + Curve_data.out = Crv_out; + Curve_data.nbrk = Crv_nbrk; + PurgeCdata( &Curve_data, status ); + +/* Annul the Mapping. */ + Map4_map = astAnnul( Map4_map ); + +/* Ensure all lines are flushed to the graphics system. */ + Fpoly( this, method, class, status ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__CURVE_ID, 0, GRF__LINE, method, class ); + } + +/* Return. */ + return; + +} + +static int GetLabelUnits( AstPlot *this, int axis, int *status ) { +/* +* Name: +* GetLabelUnits + +* Purpose: +* Return the value of the LabelUnits attribute for a Plot axis. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int GetLabelUnits( AstPlot *this, int axis, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function returns the value of the LabelUnits attribute for a +* Plot axis, supplying a suitable default if not set. + +* Parameters: +* this +* The Plot. +* axis +* The axis index (zero based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The attribute value. + +*/ + +/* Local Variables: */ + AstFrame *fr; /* The current Frame in the Plot */ + AstFrame *primframe; /* The primary Frame holding the requested axis */ + AstSystemType system; /* The SkyFrame System attribute */ + int primaxis; /* Index of requested axis in the primary frame */ + int ret; /* The returned value */ + +/* Initialise. */ + ret = 0; + +/* Check global status. */ + if( !astOK ) return ret; + +/* If a value has been set, return it. */ + ret = this->labelunits[ axis ]; + +/* If no value has been set, find a default. */ + if( ret == -1 ) { + +/* Assume "no" for any SkyAxis axes within the current frame of the Plot, + and "yes" for other axes. Get a pointer to the current Frame of the + Plot. */ + fr = astGetFrame( this, AST__CURRENT ); + +/* The current Frame may be a CmpFrame. So find the primary Frame containing + the requested axis. The primary Frame is guaranteed not to be a CmpFrame. */ + astPrimaryFrame( fr, axis, &primframe, &primaxis ); + +/* If the primary Frame is a SkyFrame representing ICRS, equatorial, ecliptic, + galactic or supergalactic coords, use a default of "no" for LabelUnits. + Otherwise use a default of "yes". */ + ret = 1; + if( IsASkyFrame( (AstObject *) primframe, status ) ) { + system = astGetSystem( primframe ); + if( system == AST__ICRS || + system == AST__FK4 || + system == AST__FK4_NO_E || + system == AST__FK5 || + system == AST__GAPPT || + system == AST__ECLIPTIC || + system == AST__GALACTIC || + system == AST__SUPERGALACTIC ) ret = 0; + } + +/* Annul the frame pointers. */ + primframe = astAnnul( primframe ); + fr = astAnnul( fr ); + } + +/* Return the answer. */ + return ret; +} + +static void GBBuf( AstPlot *this, const char *method, + const char *class, int *status ) { +/* +* +* Name: +* GBBuf + +* Purpose: +* Call the GBBuf Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GBBuf( AstPlot *this, const char *method, +* const char *class, int *status ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GBBuf grf function to begin a new graphics +* context, either calling the version registered using astGrfSet, or +* the version in the linked grf module. The linked version is used +* if the Grf attribute is zero, or if no function has been registered +* for GBBuf using astGrfSet. + +* Parameters: +* this +* The Plot. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GBBUF ] ) { + grf_status = ( *( this->GBBuf ) )( this, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGBBuf(); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGBBuf. ", status, method, + class ); + } + +} + +static void GEBuf( AstPlot *this, const char *method, + const char *class, int *status ) { +/* +* +* Name: +* GEBuf + +* Purpose: +* Call the GEBuf Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GEBuf( AstPlot *this, const char *method, +* const char *class, int *status ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GEBuf grf function to end the current graphics +* context, either calling the version registered using astGrfSet, or +* the version in the linked grf module. The linked version is used +* if the Grf attribute is zero, or if no function has been registered +* for GEBuf using astGrfSet. + +* Parameters: +* this +* The Plot. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GEBUF ] ) { + grf_status = ( *( this->GEBuf ) )( this, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGEBuf(); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGEBuf. ", status, method, + class ); + } + +} + +static void GFlush( AstPlot *this, const char *method, + const char *class, int *status ) { +/* +* +* Name: +* GFlush + +* Purpose: +* Call the Gflush Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GFlush( AstPlot *this, const char *method, +* const char *class, int *status ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the Gflush grf function to flush graphics, either +* calling the version registered using astGrfSet, or the version in the +* linked grf module. The linked version is used if the Grf attribute +* is zero, or if no function has been registered for Gflush using +* astGrfSet. + +* Parameters: +* this +* The Plot. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GFLUSH ] ) { + grf_status = ( *( this->GFlush ) )( this, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGFlush(); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGFlush. ", status, method, + class ); + } + +} + +static void GLine( AstPlot *this, int n, const float *x, + const float *y, const char *method, + const char *class, int *status ) { +/* +* +* Name: +* GLine + +* Purpose: +* Call the Gline Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GLine( AstPlot *this, int n, const float *x, +* const float *y, const char *method, +* const char *class, int *status ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the Gline grf function to draw a polyline, either +* calling the version registered using astGrfSet, or the version in the +* linked grf module. The linked version is used if the Grf attribute +* is zero, or if no function has been registered for Gline using +* astGrfSet. + +* Parameters: +* this +* The Plot. +* n +* The number of positions to be joined together. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int i; /* Loop count */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Do not draw anything if we are using "invisible ink". */ + if( astGetInvisible( this ) ) { + grf_status = 1; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This is called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + } else if( astGetGrf( this ) && this->grffun[ AST__GLINE ] ) { + grf_status = ( *( this->GLine ) )( this, n, x, y, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGLine( n, x, y ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGLine. ", status, method, + class ); + +/* Otherwise, update the box containing all drawn graphics primitives. */ + } else if( !Boxp_freeze ){ + for( i = 0; i < n; i++ ) { + Boxp_lbnd[ 0 ] = astMIN( x[ i ], Boxp_lbnd[ 0 ] ); + Boxp_ubnd[ 0 ] = astMAX( x[ i ], Boxp_ubnd[ 0 ] ); + Boxp_lbnd[ 1 ] = astMIN( y[ i ], Boxp_lbnd[ 1 ] ); + Boxp_ubnd[ 1 ] = astMAX( y[ i ], Boxp_ubnd[ 1 ] ); + } + } + +} + +static void GMark( AstPlot *this, int n, const float *x, + const float *y, int type, const char *method, + const char *class, int *status ) { +/* +* +* Name: +* GMark + +* Purpose: +* Call the GMark Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GMark( AstPlot *this, int n, const float *x, +* const float *y, int type, const char *method, +* const char *class, int *status ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GMark grf function to draw markers, either +* calling the version registered using astGrfSet, or the version in the +* linked grf module. The linked version is used if the Grf attribute +* is zero, or if no function has been registered for GMark using +* astGrfSet. + +* Parameters: +* this +* The Plot. +* n +* The number of positions to be joined together. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* type +* An integer which can be used to indicate the type of marker symbol +* required. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int i; /* Loop count */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Do not draw anything if we are using "invisible ink". */ + if( astGetInvisible( this ) ) { + grf_status = 1; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This is called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + } else if( astGetGrf( this ) && this->grffun[ AST__GMARK ] ) { + grf_status = ( *( this->GMark ) )( this, n, x, y, type, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGMark( n, x, y, type ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGMark. ", status, method, + class ); + +/* Otherwise, update the box containing all drawn graphics primitives. */ + } else if( !Boxp_freeze ){ + for( i = 0; i < n; i++ ) { + Boxp_lbnd[ 0 ] = astMIN( x[ i ], Boxp_lbnd[ 0 ] ); + Boxp_ubnd[ 0 ] = astMAX( x[ i ], Boxp_ubnd[ 0 ] ); + Boxp_lbnd[ 1 ] = astMIN( y[ i ], Boxp_lbnd[ 1 ] ); + Boxp_ubnd[ 1 ] = astMAX( y[ i ], Boxp_ubnd[ 1 ] ); + } + } + +} + +static void GQch( AstPlot *this, float *chv, float *chh, const char *method, + const char *class, int *status ) { +/* +* +* Name: +* GQch + +* Purpose: +* Call the GQch Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GQch( AstPlot *this, float *chv, float *chh, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GQch grf function, either calling the +* version registered using astGrfSet, or the version in the linked grf +* module. The linked version is used if the Grf attribute is zero, or if +* no function has been registered for GQch using astGrfSet. + +* Parameters: +* this +* The Plot. +* chv +* A pointer to the double which is to receive the height of +* characters drawn with a vertical baseline . This will be an +* increment in the X axis. +* chh +* A pointer to the double which is to receive the height of +* characters drawn with a horizontal baseline. This will be an +* increment in the Y axis. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* if we already have the required values, return them. */ + if( Grf_chh != AST__BAD && Grf_chv != AST__BAD ) { + *chh = Grf_chh; + *chv = Grf_chv; + return; + } + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This is called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GQCH ] ) { + grf_status = ( *( this->GQch ) )( this, chv, chh, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGQch( chv, chh ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Check neither value is zero. */ + if( grf_status && ( *chh == 0.0 || *chv == 0.0 ) ) { + astError( AST__GRFER, "astGQch: Returned text heights are %g and %g " + "but zero is illegal!", status, *chv, *chh ); + grf_status = 0; + } + +/* Report an error if anything went wrong, and return safe values. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGQch. ", status, method, + class ); + *chh = 1.0; + *chv = 1.0; + } + +/* Store them for future use. */ + Grf_chh = *chh; + Grf_chv = *chv; +} + +static void GText( AstPlot *this, const char *text, float x, float y, + const char *just, float upx, float upy, + const char *method, const char *class, int *status ) { +/* +* +* Name: +* GText + +* Purpose: +* Call the GText Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GText( AstPlot *this, const char *text, float x, float y, +* const char *just, float upx, float upy, +* const char *method, const char *class, int *status ) { + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GText grf function to draw a text string, either +* calling the version registered using astGrfSet, or the version in the +* linked grf module. The linked version is used if the Grf attribute +* is zero, or if no function has been registered for GText using +* astGrfSet. + +* Parameters: +* this +* The Plot. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* upx +* The x component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* left to right on the screen. +* upy +* The y component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* bottom to top on the screen. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Do not draw anything if we are using "invisible ink". */ + if( astGetInvisible( this ) ) { + grf_status = 1; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This is called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + } else if( astGetGrf( this ) && this->grffun[ AST__GTEXT ] ) { + grf_status = ( *( this->GText ) )( this, text, x, y, just, upx, upy, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGText( text, x, y, just, upx, upy ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGText. ", status, method, + class ); + } + +} + +static void GTxExt( AstPlot *this, const char *text, float x, float y, + const char *just, float upx, float upy, float *xbn, + float *ybn, const char *method, const char *class, int *status ) { +/* +* +* Name: +* GTxExt + +* Purpose: +* Call the GTxExt Grf function. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void GTxExt( AstPlot *this, const char *text, float x, float y, +* const char *just, float upx, float upy, float *xbn, +* float *ybn, const char *method, const char *class, int *status ) + +* Class Membership: +* Plot private function. + +* Description: +* This function calls the GTxExt grf function to find the extent +* of a text string, either calling the version registered using +* astGrfSet, or the version in the linked grf module. The linked +* version is used if the Grf attribute is zero, or if no function +* has been registered for GTxExt using astGrfSet. + +* Parameters: +* this +* The Plot. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* upx +* The x component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* left to right on the screen. +* upy +* The y component of the up-vector for the text, in graphics world +* coordinates. If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* bottom to top on the screen. +* xbn +* An array of 4 elements in which to return the x coordinate of +* each corner of the bounding box. +* ybn +* An array of 4 elements in which to return the y coordinate of +* each corner of the bounding box. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The corners are returned in no particular order. + +*/ + +/* Local Variables: */ + int grf_status; /* Status retruned from Grf function */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If the Grf attribute is set to a non-zero value, use the Grf function + registered using astGrfSet (so long as a function has been supplied). + This is called via a wrapper which adapts the interface to suit the + language in which the function is written. */ + if( astGetGrf( this ) && this->grffun[ AST__GTXEXT ] ) { + grf_status = ( *( this->GTxExt ) )( this, text, x, y, just, upx, upy, + xbn, ybn, status ); + +/* Otherwise, use the function in the external Grf module, selected at + link-time using ast_link options.*/ + } else { + grf_status = astGTxExt( text, x, y, just, upx, upy, xbn, ybn ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Report an error if anything went wrong. */ + if( !grf_status ) { + astError( AST__GRFER, "%s(%s): Graphics error in astGTxExt. ", status, method, + class ); + } +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Plot. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Plot member function (over-rides the protected astGetAttrib +* method inherited from the FrameSet class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Plot, formatted as a character string. +* +* The value returned is the value which would actually be used if +* astGrid was called with the current set of attribute values. This +* may not always be the same as the value set by the user. For +* instance, if Labelling is set to "exterior" by the user, it may not +* be possible to produce exterior labels, in which case interior labels +* will be produced. If this function is used to get the value of +* Labelling in this situation, then the value actually used (i.e. +* interior) will be returned instead of the requested value (i.e. +* exterior). +* +* Some attributes have dynamic defaults, (i.e. the behaviour if not +* set depends on the values of other attributes). If the value for +* such an attribute is enquired using this function, then the dynamic +* default value actually used will be returned if no value has been +* set explicitly for the attribute. + +* Parameters: +* this +* Pointer to the Plot. +* attrib +* Pointer to a null terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Plot, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Plot. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPlot *this; /* Pointer to the Plot structure */ + const char *result; /* Pointer value to return */ + char label[21]; /* Graphics item label */ + double dval; /* Double attribute value */ + int axis; /* Axis number */ + int ival; /* Int attribute value */ + int len; /* Length of attrib string */ + int nax; /* Number of base Frame axes */ + int nc; /* No. characters read by astSscanf */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Plot structure. */ + this = (AstPlot *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Get the number of base Frame axis (2 for a Plot, 3 for a Plot3D). */ + nax = astGetNin( this ); + +/* Indicate that the current bound box should not be changed during the + execution of this function (this may happen if a grid is drawn to get + the default value for an attribute such as Labelling). */ + Boxp_freeze = 1; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Tol. */ +/* ---- */ + if ( !strcmp( attrib, "tol" ) ) { + dval = astGetTol( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Grid. */ +/* ----- */ + } else if ( !strcmp( attrib, "grid" ) ) { + ival = GetUsedGrid( this, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* TickAll. */ +/* -------- */ + } else if ( !strcmp( attrib, "tickall" ) ) { + ival = astGetTickAll( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* ForceExterior. */ +/* -------------- */ + } else if ( !strcmp( attrib, "forceexterior" ) ) { + ival = astGetForceExterior( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Invisible. */ +/* ---------- */ + } else if ( !strcmp( attrib, "invisible" ) ) { + ival = astGetInvisible( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Border. */ +/* ------- */ + } else if ( !strcmp( attrib, "border" ) ) { + ival = GetUsedBorder( this, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* ClipOp. */ +/* ------- */ + } else if ( !strcmp( attrib, "clipop" ) ) { + ival = astGetClipOp( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Clip. */ +/* ----- */ + } else if ( !strcmp( attrib, "clip" ) ) { + ival = astGetClip( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Grf. */ +/* ---- */ + } else if ( !strcmp( attrib, "grf" ) ) { + ival = astGetGrf( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* DrawTitle. */ +/* --------- */ + } else if ( !strcmp( attrib, "drawtitle" ) ) { + ival = astGetDrawTitle( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Escape. */ +/* ------- */ + } else if ( !strcmp( attrib, "escape" ) ) { + ival = astGetEscape( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LabelAt(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelat(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = GetUsedLabelAt( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Centre(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "centre(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = GetUsedCentre( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Gap. */ +/* ---- */ + } else if ( !strcmp( attrib, "gap" ) ) { + dval = GetUsedGap( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Gap(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "gap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = GetUsedGap( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* LogGap. */ +/* ---- */ + } else if ( !strcmp( attrib, "loggap" ) ) { + dval = GetUsedLogGap( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* LogGap(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "loggap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = GetUsedLogGap( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* NumLabGap. */ +/* -------- */ + } else if ( !strcmp( attrib, "numlabgap" ) ) { + dval = astGetNumLabGap( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* NumLabGap(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "numlabgap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetNumLabGap( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* TextLabGap. */ +/* ----------- */ + } else if ( !strcmp( attrib, "textlabgap" ) ) { + dval = astGetTextLabGap( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* TextLabGap(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "textlabgap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetTextLabGap( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* LabelUp. */ +/* -------- */ + } else if ( !strcmp( attrib, "labelup" ) ) { + ival = astGetLabelUp( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LabelUp(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelup(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = astGetLabelUp( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LogPlot. */ +/* -------- */ + } else if ( !strcmp( attrib, "logplot" ) ) { + ival = astGetLogPlot( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LogPlot(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "logplot(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = astGetLogPlot( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LogLabel. */ +/* -------- */ + } else if ( !strcmp( attrib, "loglabel" ) ) { + ival = GetUsedLogLabel( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LogLabel(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "loglabel(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetUsedLogLabel( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LogTicks. */ +/* -------- */ + } else if ( !strcmp( attrib, "logticks" ) ) { + ival = GetUsedLogTicks( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LogTicks(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "logticks(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetUsedLogTicks( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* NumLab. */ +/* -------- */ + } else if ( !strcmp( attrib, "numlab" ) ) { + ival = astGetNumLab( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* NumLab(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "numlab(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = astGetNumLab( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* MinTick. */ +/* -------- */ + } else if ( !strcmp( attrib, "mintick" ) ) { + ival = GetUsedMinTick( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* MinTick(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "mintick(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetUsedMinTick( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* TextLab. */ +/* ---------- */ + } else if ( !strcmp( attrib, "textlab" ) ) { + ival = GetUsedTextLab( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* TextLab(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "textlab(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetUsedTextLab( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* DrawAxes. */ +/* ----------- */ + } else if ( !strcmp( attrib, "drawaxes" ) ) { + ival = astGetDrawAxes( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* DrawAxes(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "drawaxes(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetDrawAxes( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Abbrev. */ +/* ----------- */ + } else if ( !strcmp( attrib, "abbrev" ) ) { + ival = astGetAbbrev( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Abbrev(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "abbrev(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetAbbrev( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LabelUnits. */ +/* ----------- */ + } else if ( !strcmp( attrib, "labelunits" ) ) { + ival = GetUsedLabelUnits( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LabelUnits(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelunits(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetUsedLabelUnits( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Style. */ +/* ------ */ + } else if ( !strcmp( attrib, "style" ) ) { + ival = GetUseStyle( this, AST__BORDER_ID, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Style(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "style(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + ival = GetUseStyle( this, FullForm( GrfLabels, label, attrib, "astGet", astGetClass( this ), status ), status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Font. */ +/* ----- */ + } else if ( !strcmp( attrib, "font" ) ) { + ival = GetUseFont( this, AST__TEXTLABS_ID, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Font(label). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "font(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + ival = GetUseFont( this, FullForm( GrfLabels, label, attrib, "astGet", astGetClass( this ), status ), status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Colour. */ +/* ------- */ + } else if ( !strcmp( attrib, "colour" ) ) { + ival = GetUseColour( this, AST__TEXTLABS_ID, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Colour(label). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "colour(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + ival = GetUseColour( this, FullForm( GrfLabels, label, attrib, "astGet", astGetClass( this ), status ), status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Color. */ +/* ------ */ + } else if ( !strcmp( attrib, "color" ) ) { + ival = GetUseColour( this, AST__TEXTLABS_ID, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Color(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "color(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + ival = GetUseColour( this, FullForm( GrfLabels, label, attrib, "astGet", astGetClass( this ), status ), status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Width. */ +/* ------ */ + } else if ( !strcmp( attrib, "width" ) ) { + dval = GetUseWidth( this, AST__BORDER_ID, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + +/* Width(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "width(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + dval = GetUseWidth( this, FullForm( GrfLabels, label, attrib, "astGet", astGetClass( this ), status ), status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Size. */ +/* ----- */ + } else if ( !strcmp( attrib, "size" ) ) { + dval = GetUseSize( this, AST__TEXTLABS_ID, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Size(label). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "size(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + dval = GetUseSize( this, FullForm( GrfLabels, label, attrib, "astGet", astGetClass( this ), status ), status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* TitleGap. */ +/* --------- */ + } else if ( !strcmp( attrib, "titlegap" ) ) { + dval = astGetTitleGap( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* MajTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "majticklen" ) ) { + dval = GetUsedMajTickLen( this, 0, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* MajTickLen(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "majticklen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = GetUsedMajTickLen( this, axis - 1, status ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* MinTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "minticklen" ) ) { + dval = astGetMinTickLen( this, 0 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* MinTickLen(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "minticklen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetMinTickLen( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Labelling. */ +/* ---------- */ + } else if ( !strcmp( attrib, "labelling" ) ) { + ival = GetUsedLabelling( this, status ); + if ( astOK ) { + result = ival ? xlbling[1] : xlbling[0]; + } + +/* Edge(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "edge(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = GetUsedEdge( this, axis - 1, status ); + if ( astOK ) { + if( ival == LEFT ){ + result = "left"; + } else if( ival == RIGHT ){ + result = "right"; + } else if( ival == TOP ){ + result = "top"; + } else if( ival == BOTTOM ){ + result = "bottom"; + } else { + result = ""; + } + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Unfreeze the bound box so that it may be updated by subsequent + plotting functions. */ + Boxp_freeze = 0; + +/* Return the result. */ + return result; +} + +static AstPointSet *GetDrawnTicks( AstPlot *this, int axis, int major, int *status ){ +/* +*+ +* Name: +* astGetDrawnTicks + +* Purpose: +* Return information about the ticks last drawn by astGrid. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "plot.h" +* AstPointSet *GetDrawnTicks( AstPlot *this, int axis, int major ) + +* Class Membership: +* Plot method. + +* Description: +* This function returns a PointSet holding information about the +* tick marks (either major and minor) that were drawn by the previous +* invocation of astGrid. A NULL pointer is returned if astGrid has +* not yet been invoked. + +* Parameters: +* this +* Pointer to a Plot. +* axis +* The zero-based axis of the axis for which tick mark information +* is required. +* major +* Supply a non-zero value if information about the major tick +* marks is to be returned, and zero if information about the minor +* tick marks is to be returned. + +* Returned Value: +* A pointSet with one point for every tick of the requested type drawn +* by astGrid for the specified axis. Each point has 2 coordinate values, +* being the graphics coordinates at the start of the tick mark. The +* returned PointSet pointer should be annulled when no longer needed. + +*- +*/ + +/* Local Variables: */ + AstPointSet *result = NULL; + double *ptr[ 3 ]; + int n; + +/* Check the global status. */ + if( !astOK ) return result; + +/* Report an error if the supplied axis value is incorrect. */ + if( axis < 0 || axis > 1 ) { + astError( AST__INTER, "astGetDrawnTicks(Plot): Supplied \"axis\" " + "value is %d - should 0 or 1 (internal AST programming " + "error).", status, axis ); + n = 0; + +/* If OK, get the number of stored tick marks. */ + } else { + n = major ? this->majtickcount[ axis ] : this->mintickcount[ axis ]; + } + +/* Check that information is available. */ + if( n > 0 && astOK ) { + +/* Create a PointSet with the required size. */ + result = astPointSet( n, 2, "", status ); + +/* Store pointers to the arrays within the Plot that hold the required + tick marks positions and types. */ + ptr[ 0 ] = major ? this->majtickgx[ axis ] : this->mintickgx[ axis ]; + ptr[ 1 ] = major ? this->majtickgy[ axis ] : this->mintickgy[ axis ]; + astSetPoints( result, ptr ); + } + +/* Return the PointSet. */ + return result; +} + +static double GetTicks( AstPlot *this, int axis, double *cen, double **ticks, + int *nmajor, double **minticks, int *nminor, + int format_set, int *inval, double *refval, + GetTicksStatics **pstatics, const char *method, + const char *class, int *status ){ +/* +* Name: +* GetTicks + +* Purpose: +* Obtain a list of logarithmically or linearly spaced tick mark values for +* a single axis in a 2-D physical coordinate Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* double GetTicks( AstPlot *this, int axis, double *cen, double **ticks, +* int *nmajor, double **minticks, int *nminor, +* int format_set, int *inval, double *refval, +* GetTicksStatics **pstatics, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* For linearly spaced major ticks the "gap" returned by this function +* is the constant difference between adjacent major tick marks. For +* logarithmically spaced major ticks the "gap" returned by this +* function is the constant ratio between adjacent major tick marks. +* +* If a gap size has been specified using attribute Gap (or LogGap for +* logarithmic ticks) supplied, the specified value is returned, and used +* to determine the tick values. If no gap size is supplied, a default +* gap size is used and returned. +* +* All this is over-ridden if the astSetTickValues method has been +* called to store explicit tick mark values in the Plot structure. +* In this case, the values supplied using astSetTickValues are +* returned. + +* Parameters: +* this +* The Plot. Supplying a NULL pointer will cause statics resources +* to be released. +* axis +* The zero-based index of the axis to use. +* cen +* Pointer to the supplied axis value at which to put a single +* central tick. Other ticks will be placed evenly on either side +* of this tick. If AST__BAD is provided, a value will be used +* which would put a tick at an axis value of one. The used value +* is returned. +* ticks +* Pointer to a place at which to return a pointer to the memory in +* which are stored the major tick values to be used. This pointer +* should be freed using astFree when no longer needed. The number of +* values in the array is given by the value returned by parameter +* "nmajor". +* nmajor +* A pointer to a location at which to return the number of major +* ticks. +* minticks +* Pointer to a place at which to return a pointer to the memory in +* which are stored the minor tick values to be used. This pointer +* should be freed using astFree when no longer needed. The number of +* values in the array is given by the value returned by parameter +* "nminor". The minor tick marks values returned in this array are +* the ones stored in the Plot via a call to the astSetTickValues +* function. If this function has not been called, then a NULL +* pointer is returned, and the "nminor" value is returned holding the +* number of divisions between major ticks. +* nminor +* A pointer to a location at which to return either the number of +* division into which each gap should be divided when drawing minor +* tick marks (if "*minticks" is returned holding NULL), or the +* total number of minor tick values stored in "*minticks" (if +* "*minticks" is returned non-NULL). The number of divisions +* between major tick values is one more than the number of minor +* tick marks. +* format_set +* Indicates if an explicit format has been set for the axis. If +* not, "cen" is always assumed to be AST__BAD, and any specified +* Gap value is rounded to the nearest "nice" value. This has +* to be done because the algorithm for choosing a format avoiding +* unnecessary precision only works if the gap size causes 1 digit to +* change between adjacent labels. +* inval +* A pointer to a location at which to return a flag indicating if +* any invalid physical coordinates were encountered. +* refval +* A pointer to a location at which to return a value for the other +* axis which can be used when normalizing the returned tick mark +* values. +* pstatics +* Address of a pointer to a structure holding static data values +* used within this function. A NULL pointer should be supplied on +* the first invocation (dynamic memory will then be allocated to +* hold ths structure). The memory is freed when a NULL value for +* "this" is supplied. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The used gap size. + +* Notes: +* - This function allocates some static resources on its first +* invocation, which should be released when no longer needed, or when +* a different Plot is supplied, by calling this function with a NULL +* pointer for parameter "this". All other parameters are ignored. +* - This function assumes that the physical coordinate system is 2 +* dimensional, and it should not be used if this is not the case. +* - An error is reported if the region containing valid physical +* coordinates is too small to use. +* - If an error has already occurred, or if this function should fail +* for any reason, then a NULL pointer is returned in "ticks", zero +* is returned for the number of major and minor ticks marks. +*/ + +/* Local Variables: */ + GetTicksStatics *statics; /* Pointer to statics structure */ + double *tick; /* Pointer to next tick value */ + double cen0; /* Supplied value of cen */ + double dran; /* Dynamic range of axis values */ + double frac; /* Fraction of plot area holding good coords */ + double gap; /* Supplied value for Gap or LogGap */ + double log_used_gap; /* Log10( the used gap size ) */ + double maxv; /* Max axis value */ + double minv; /* Min axis value */ + double new_used_gap; /* New value for the used gap size */ + double old_used_gap; /* Old value for the used gap size */ + double test_gap; /* Trial gap size */ + double used_cen; /* Used value of cen */ + double used_gap; /* The used gap size */ + int findcen; /* Find a new centre value? */ + int gap_too_small; /* Test gap too small? */ + int gap_too_large; /* Test gap too large? */ + int i; /* Axis index */ + int ihi; /* Highest tick mark index */ + int ilo; /* Lowest tick mark index */ + int nochange; /* No. of ineffective attempts to change gap size */ + +/* Initialise the returned information. */ + *ticks = NULL; + *minticks = NULL; + *nmajor = 0; + *nminor = 0; + +/* Get a pointer to the supplied statics object. */ + statics = *pstatics; + +/* If a NULL pointer has been supplied for "this", release the resources + allocated on the first call to this function, and return. */ + if( !this ){ + if( statics ) { + if( statics->map ) statics->map = astAnnul( statics->map ); + if( statics->pset ) statics->pset = astAnnul( statics->pset ); + if( statics->frame ) statics->frame = astAnnul( statics->frame ); + *pstatics = astFree( statics ); + } + return 0.0; + } + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* If no statics structure was supplied, create one now and initialise it. */ + if( !statics ) { + statics = astMalloc( sizeof( GetTicksStatics ) ); + if( statics ) { + statics->pset=NULL; + *pstatics = statics; + } + } + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + maxv = 0.0; + minv = 0.0; + used_cen = 0.0; + used_gap = 0.0; + ihi = 0; + ilo = 0; + +/* If this is the first call to this function, do some initialisation. */ + if( !statics->pset ){ + +/* Get the Mapping from Base to Current Frame in the Plot. */ + statics->map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Get a pointer to the current Frame from the Plot. */ + statics->frame = astGetFrame( this, AST__CURRENT ); + +/* Get initial guesses at suitable gaps for each axis. A PointSet is + returned holding sorted values (non-normalized) for the physical axes. */ + statics->pset = DefGap( this, statics->defgaps, statics->ngood, &frac, &statics->bad, method, class, status ); + +/* Store the maximum and minimum number of major tick marks along each + axis. These numbers are reduced if only a small part of the plotting + area contains valid coordinates, so that the tick marks do not end up + to close together. */ + statics->maxticks = (int) ( 0.5 + MAJTICKS_MAX*sqrt( frac ) ); + statics->mintick = (int) ( 0.5 + MAJTICKS_MIN*sqrt( frac ) ); + if( statics->mintick < 3 ) statics->mintick = 3; + if( statics->maxticks < 8 ) statics->maxticks = 8; + if( statics->maxticks < statics->mintick ) statics->maxticks = statics->mintick; + +/* Get a pointer to the data in the PointSet. */ + statics->ptr = astGetPoints( statics->pset ); + +/* Find a typical value on each axis. */ + for( i = 0; i < 2 && astOK; i++ ){ + statics->typval[ i ] = Typical( statics->ngood[ i ], statics->ptr[ i ], -DBL_MAX, DBL_MAX, + statics->width + i, status ); + } + } + +/* Return the flag indicating if any regions of invalid physical coordinates + were found. */ + *inval = statics->bad; + +/* Return the typical value on the other axis. */ + *refval = statics->typval[ 1 - axis ]; + +/* See if any tick marks values have been stored in the Plot structure + using astSetTickValues. If so, copy the tick mark values to the + returned arrays and exit with a gap size determined by the first two + ticks. */ + if( this->nmajtickval[ axis ] > 0 ) { + *ticks = astStore( NULL, this->majtickval[ axis ], + this->nmajtickval[ axis ]*sizeof(double) ); + *minticks = astStore( NULL, this->mintickval[ axis ], + this->nmintickval[ axis ]*sizeof(double) ); + *nmajor = this->nmajtickval[ axis ]; + *nminor = this->nmintickval[ axis ]; + + if( *nmajor > 1 && (*ticks)[ 0 ] != AST__BAD && + (*ticks)[ 1 ] != AST__BAD ) { + used_gap = fabs( (*ticks)[ 1 ] - (*ticks)[ 0 ] ); + } else { + used_gap = AST__BAD; + } + + return used_gap; + + } + +/* See if the user has specified a gap size. The default for astGetLogTicks is + determined in DefGaps, so we can now decide whether to use attribute Gap + or LogGap to get the user-supplied gap size. Obtain the requested gap + attribute values for both physical axes. */ + if( astGetLogTicks( this, axis ) ) { + gap = astGetLogGap( this, axis ); + } else { + gap = astGetGap( this, axis ); + } + +/* Find the maximum and minimum value in the plotting area. DefGap will + have reported an error if minv*maxv is negative or zero. */ + if( astOK ) { + maxv = statics->ptr[ axis ][ statics->ngood[ axis ] - 1 ]; + minv = statics->ptr[ axis ][ 0 ]; + } + +/* First deal with logarithmically spaced ticks. */ + dran = ( minv != 0.0 ) ? maxv/minv : 0.0; + if( astGetLogTicks( this, axis ) ) { + +/* If the ratio of max and min data value is not larger than 10, report an + error. */ + dran = ( minv != 0.0 ) ? maxv/minv :0.0; + if( dran < 10.0 && dran > 0.1 ) { + astError( AST__VSMAL, "%s(%s): Cannot produce logarithmically " + "spaced major tick marks on axis %d since the dynamic " + "range of the axis is too small.", status, method, class, axis + 1 ); + } + +/* Should we find a new value for "cen"? */ + findcen = !cen || *cen == AST__BAD || !format_set; + +/* Try to find a "nice" gap size, so long as the caller has not supplied + a gap size. The default gap size obtained above is our initial guess. */ + if( gap == AST__BAD && astOK ){ + +/* Start off using the default gap found during the initialisation. */ + test_gap = statics->defgaps[ axis ]; + +/* Loop round until a gap size is found which gives an acceptable number + of tick marks. Upto 10 gap sizes are tried. */ + for( i = 0; i < 10 && astOK; i++ ){ + +/* Find a "nice" gap size close to the current test gap size. Also find + the number of minor tick marks to use with the nice gap size. Gaps for + logarithmic axes are always powers of ten. */ + log_used_gap = (int) ( log10( test_gap ) + 0.5 ); + if( log_used_gap == 0.0 ) { + log_used_gap = ( test_gap > 1.0 ) ? 1.0 : -1.0; + } + *nminor = 9; + used_gap = pow( 10.0, log_used_gap ); + +/* If no value has been supplied for *cen, choose a value which would put + a major tick mark at the value 1 (or -1), and which is mid way between + the maximum and minimum axis value. */ + if( findcen ) { + used_cen = pow( used_gap, (int) ( 0.5*log10( maxv*minv ) / + log_used_gap ) ); + if( maxv < 0 ) used_cen = -used_cen; + } else { + used_cen = *cen; + } + +/* Find the index of the highest tick which is not larger than the lowest + axis value. */ + if( log_used_gap > 0.0 ) { + ilo = floor( log10( minv/used_cen )/log_used_gap ); + } else { + ilo = ceil( log10( minv/used_cen )/log_used_gap ); + } + +/* Find the index of the lowest tick which is not less than the highest + axis value. */ + if( log_used_gap > 0.0 ) { + ihi = ceil( log10( maxv/used_cen )/log_used_gap ); + } else { + ihi = floor( log10( maxv/used_cen )/log_used_gap ); + } + +/* Find the total number of tick marks. */ + *nmajor = ihi - ilo + 1; + +/* If the number of ticks is unacceptable, try a different gap size. If the + gap was too large to produce any ticks, try using half the gap size. */ + if( *nmajor <= 0 ) { + test_gap = sqrt( test_gap ); + +/* If there were some ticks, but not enough, decrease the gap size in + proportion to the shortfall. */ + } else if( *nmajor < statics->mintick ){ + test_gap = pow( test_gap, (double)( *nmajor )/(double)( statics->mintick ) ); + +/* If there were too many ticks, increase the gap size in proportion to the + excess. */ + } else if( *nmajor > statics->maxticks ){ + test_gap = pow( test_gap, (double)( *nmajor )/(double)( statics->maxticks ) ); + +/* If the number of ticks is acceptable, break out of the loop early.*/ + } else { + break; + } + } + +/* Increase the tick coverage by one at each end to cover up the gaps. */ + ilo--; + ihi++; + *nmajor += 2; + +/* If an explicit gap size was supplied, use it. */ + } else if( astOK ) { + +/* Check it is usable. */ + if( gap == 0.0 && astOK ) { + astError( AST__ATTIN, "%s(%s): Invalid value zero given for " + "attribute LogGap(%d).", status, method, class, axis + 1 ); + + } else if( gap < 0.0 && astOK ) { + astError( AST__ATTIN, "%s(%s): Invalid negative value %f given for " + "attribute LogGap(%d).", status, method, class, gap, axis + 1 ); + +/* If necessary, take its reciprocal in order to ensure that the absolute + tick mark values get smaller or larger as required. */ + } else { + + used_gap = gap; + if( fabs( maxv ) < fabs( minv ) ) { + if( gap > 1.0 ) used_gap = 1.0/gap; + } else { + if( gap < 1.0 ) used_gap = 1.0/gap; + } + +/* Find the nearest power of 10 ( do not allow 10**0 (=1.0) to be used). */ + log_used_gap = (int) ( log10( used_gap ) + 0.5 ); + if( log_used_gap == 0.0 ) { + log_used_gap = ( gap > 1.0 ) ? 1.0 : -1.0; + } + used_gap = pow( 10.0, log_used_gap ); + +/* We always use 9 minor intervals. */ + *nminor = 9; + +/* If no value has been supplied for *cen, choose a value which would put + a major tick mark at the value 1 (or -1), and which is mid way between + the maximum and minimum axis value. */ + if( findcen ) { + used_cen = pow( used_gap, (int) ( 0.5*log10( maxv*minv ) / + log_used_gap ) ); + if( maxv < 0 ) used_cen = -used_cen; + } else { + used_cen = *cen; + } + +/* Find the index of the highest tick which is not larger than the lowest + axis value. */ + if( log_used_gap > 0.0 ) { + ilo = floor( log10( minv/used_cen )/log_used_gap ); + } else { + ilo = ceil( log10( minv/used_cen )/log_used_gap ); + } + +/* Find the index of the lowest tick which is not less than the highest + axis value. */ + if( log_used_gap > 0.0 ) { + ihi = ceil( log10( maxv/used_cen )/log_used_gap ); + } else { + ihi = floor( log10( maxv/used_cen )/log_used_gap ); + } + +/* Find the total number of tick marks. */ + *nmajor = ihi - ilo + 1; + if( *nmajor < 2 && astOK ) { + astError( AST__ATTIN, "%s(%s): Unusable value %f given for " + "attribute LogGap(%d).", status, method, class, gap, axis + 1 ); + + } + } + } + +/* Allocate memory to hold the tick values themselves. */ + *ticks = (double *) astMalloc( sizeof( double )*( *nmajor ) ); + if( astOK ) { + +/* Store them. */ + tick = *ticks; + for( i = ilo; i <= ihi; i++, tick++ ) { + *tick = used_cen*pow( used_gap, i ); + } + } + +/* Store returned centre value. */ + if( cen ) *cen = used_cen; + +/* Now deal with linearly spaced ticks */ + } else { + +/* Store the supplied value of cen. */ + cen0 = ( cen ) ? *cen : AST__BAD; + +/* If no format has been set for the axis, ensure AST__BAD is used for cen. */ + if( !format_set ) cen0 = AST__BAD; + +/* Try to find a "nice" gap size, so long as the caller has not supplied + a gap size. The default gap size obtained above is our initial guess. */ + if( gap == AST__BAD ){ + old_used_gap = AST__BAD; + +/* Start of using the default gap found during the initialisation. */ + test_gap = statics->defgaps[ axis ]; + used_gap = 0.0; + +/* Initialise flags saying the test gap is too large or too small */ + gap_too_large = 0; + gap_too_small = 0; + +/* So far, there have been no ineffective attempts to change the gap + size. */ + nochange = 0; + +/* Loop round until a gap size is found which gives an acceptable number + of tick marks. Upto 10 gap sizes are tried. */ + for( i = 0; i < 10 && astOK; i++ ){ + +/* Find a "nice" gap size close to the current test gap size. Also find + the number of minor tick marks to use with the nice gap size. */ + new_used_gap = astGap( statics->frame, axis, test_gap, nminor ); + +/* Find the number and positions of major tick marks which would result + from using this gap size. Annul the memory used to hold any previous tick + data first. Only do this if the gap being used has actually changed, + otherwise we just retain the values created from the previous run with + this gap size. */ + if( new_used_gap != used_gap ) { + nochange = 0; + old_used_gap = used_gap; + used_gap = new_used_gap; + if( *ticks ) *ticks = astFree( *ticks ); + if( cen ) *cen = cen0; + *nmajor = FindMajTicks( statics->map, statics->frame, axis, *refval, statics->width[ 1-axis ], + used_gap, cen, statics->ngood[ axis ], + statics->ptr[ axis ], ticks, status ); + +/* If the gap size has not changed do an extra pass through this loop, + but only do this a maximum of 25 times in succession. */ + } else if( nochange < 25 ) { + nochange++; + i--; + + } else if( astOK ){ + astError( AST__VSMAL, "%s(%s): Cannot produce enough major " + "tick marks on axis %d using the current axis " + "format (\"%s\").", status, method, class, axis + 1, + astGetFormat( statics->frame, axis ) ); + break; + } + +/* If the number of ticks is unacceptable, try a different gap size. If the + gap was too large to produce any ticks, try using half the gap size. */ + if( *nmajor == 0 ) { + test_gap *= 0.5; + gap_too_large = 1; + gap_too_small = 0; + +/* If there were some ticks, but not enough... */ + } else if( *nmajor < statics->mintick ){ + +/* If the previous test gap produced too many ticks, use the current gap + size. */ + if( gap_too_small ) { + break; + +/* Otherwise, decrease the gap size in proportion to the shortfall. */ + } else { + test_gap *= (double)( *nmajor )/(double)( statics->mintick ); + gap_too_large = 1; + gap_too_small = 0; + } + +/* If there were too many ticks... */ + } else if( *nmajor > statics->maxticks ){ + +/* If the previous test gap produced too few ticks, use the previous gap + size. */ + if( gap_too_large ) { + used_gap = old_used_gap; + if( *ticks ) *ticks = astFree( *ticks ); + if( cen ) *cen = cen0; + *nmajor = FindMajTicks( statics->map, statics->frame, axis, *refval, statics->width[ 1-axis ], + used_gap, cen, statics->ngood[ axis ], + statics->ptr[ axis ], ticks, status ); + break; + +/* Otherwise, increase the gap size in proportion to the excess. */ + } else { + test_gap *= (double)( *nmajor )/(double)( statics->maxticks ); + gap_too_small = 1; + gap_too_large = 0; + } + +/* If the number of ticks is acceptable, break out of the loop early.*/ + } else { + break; + } + } + +/* If an explicit gap size was supplied, use it. */ + } else { + +/* Find a likely value for the number of minor tick marks to use, and find + a nice gap close to the supplied gap (unless an explicit format has + been set). */ + if( format_set ){ + used_gap = gap; + (void) astGap( statics->frame, axis, used_gap, nminor ); + } else { + used_gap = astGap( statics->frame, axis, gap, nminor ); + } + +/* Find where the major ticks should be put. */ + if( cen ) *cen = cen0; + *nmajor = FindMajTicks( statics->map, statics->frame, axis, *refval, statics->width[ 1-axis ], + used_gap, cen, statics->ngood[ axis ], statics->ptr[ axis ], + ticks, status ); + } + } + +/* Report an error if no ticks can be found. */ + if( *nmajor == 0 && astOK ) { + astError( AST__GRFER, "%s(%s): Cannot find any usable tick mark values. ", status, method, + class ); + } + +/* If an error has occurred, annul the memory used to hold tick data, and + return zero ticks. */ + if( !astOK ) { + *ticks = (double *) astFree( (void *) *ticks ); + *nmajor = 0; + *nminor = 0; + used_gap = 0.0; + } + +/* Return. */ + return used_gap; +} + +static double GoodGrid( AstPlot *this, int *dim, AstPointSet **pset1, + AstPointSet **pset2, const char *method, + const char *class, int *status ){ +/* +* Name: +* GoodGrid + +* Purpose: +* Create a grid covering the region containing good coordinates in a +* 2-D physical coordinate Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* double GoodGrid( AstPlot *this, int *dim, AstPointSet **pset1, +* AstPointSet **pset2, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function creates two PointSets, one holding a square grid of +* graphics coordinates, and the other holding the corresponding physical +* coordinates (not normalized). The grid covers just the area containing +* good physical coordinates. The points are stored row by row in the +* returned PointSets. + +* Parameters: +* this +* The Plot. +* dim +* A pointer to an integer in which to store the number of samples +* along each edge of the returned grid. +* pset1 +* A pointer to a location at which to store a pointer to the +* PointSet holding the graphics coordinates. +* pset2 +* A pointer to a location at which to store a pointer to the +* PointSet holding the physical coordinates. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The fraction of the plotting area containing good physical +* coordinates. + +* Notes: +* - This function assumes that the physical coordinate system is 2 +* dimensional, and it should not be used if this is not the case. +* - The returned PointSets should be annulled when no longer needed, +* using astAnnul. +* - An error is reported if the region containing valid physical +* coordinates is too small to use. +* - A function value of zero, and NULL pointers are returned if an error +* has already occurred, or if this function should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to the Current Frame in the Plot */ + AstMapping *map; /* Pointer to "graphics to physical" mapping */ + double **ptr1; /* Pointer to physical axis value data */ + double **ptr2; /* Pointer to graphics axis value data */ + double *pa; /* Pointer to next value on 1st physical axis */ + double *pb; /* Pointer to next value on 2nd physical axis */ + double *px; /* Pointer to next value on 1st graphics axis */ + double *py; /* Pointer to next value on 2nd graphics axis */ + double dx; /* Cell size along graphics X (1st) axis */ + double dy; /* Cell size along graphics Y (2nd) axis */ + double frac; /* Fraction of good physical coordinates */ + double xmax; /* High X bound of region containing good phy. coords */ + double xmin; /* Low X bound of region containing good phy. coords */ + double ymax; /* High Y bound of region containing good phy. coords */ + double ymin; /* Low Y bound of region containing good phy. coords */ + int j; /* Element offset */ + int ngood; /* Number of grid points with good physical coords */ + int size; /* Number of grid points */ + +/* Initialise the returned PointSet pointers. */ + *pset1 = NULL; + *pset2 = NULL; + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + ptr1 = NULL; + frac = 0.0; + xmax = 0.0; + xmin = 0.0; + ymax = 0.0; + ymin = 0.0; + +/* Get the Mapping from base (graphics) to current (physical) Frame in the + supplied Plot. */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Get a pointer to the Current Frame in the Plot. */ + frm = astGetFrame( this, AST__CURRENT ); + +/* Initialise the grid dimension. */ + *dim = 16; + +/* We need a grid which has at least 4 good points. */ + ngood = 0; + while( ngood < 4 && astOK ){ + +/* Double the grid dimension. */ + *dim *= 2; + +/* Report an error if the grid is now too big. */ + if( *dim >= 512 ){ + astError( AST__VSMAL, "%s(%s): The area of the plot containing " + "usable coordinates on both axes is too small.", status, method, + class ); + break; + } + +/* Get two PointSets, one holding a regular grid of graphics coordinates, + and the other holding the corresponding physical coordinates. The grid + covers the entire plotting area with the current grid dimension. A + pointer to the physical axis values is returned. */ + ptr2 = MakeGrid( this, frm, map, 1, *dim, this->xlo, this->xhi, this->ylo, + this->yhi, 2, pset1, pset2, 0, method, class, status ); + +/* Get the number of graphics axis values. */ + size = astGetNpoint( *pset1 ); + +/* Get a pointer to the graphics axis values. */ + ptr1 = astGetPoints( *pset1 ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* Find the bounds in graphics coordinates of the area enclosing the + good physical positions in the grid, and count the good positions. */ + ngood = 0; + + pa = ptr2[ 0 ]; + pb = ptr2[ 1 ]; + px = ptr1[ 0 ]; + py = ptr1[ 1 ]; + + xmin = DBL_MAX; + xmax = -DBL_MAX; + ymin = DBL_MAX; + ymax = -DBL_MAX; + + for( j = 0; j < size; j++ ){ + if( *pa != AST__BAD && *pb != AST__BAD ){ + if( *px < xmin ) xmin = *px; + if( *px > xmax ) xmax = *px; + if( *py < ymin ) ymin = *py; + if( *py > ymax ) ymax = *py; + ngood++; + } + px++; + py++; + pa++; + pb++; + } + } + } + +/* Store approximate fraction of the plotting area containing good + physical coordinates. */ + if( astOK ) { + frac = ( (double) ngood )/(double)( astGetNpoint( *pset1 ) ); + +/* Get the size of each grid cell. */ + dx = ptr1[0][1] - ptr1[0][0]; + dy = ptr1[1][1] - ptr1[1][0]; + +/* Extend the area containing good points by one grid cell. */ + xmax += dx; + xmin -= dx; + ymax += dy; + ymin -= dy; + +/* If the area containing good points is significantly smaller than + the supplied area, create a new grid covering just the area containing + good positions. */ + if( ( xmax - xmin ) < 0.9*( this->xhi - this->xlo ) || + ( ymax - ymin ) < 0.9*( this->yhi - this->ylo ) ){ + +/* Find a new grid dimension which results in a cell size similar to + the one used to create the grid, but covering only the region containing + good physical coordinates. */ + *dim *= astMAX( (xmax - xmin)/(this->xhi - this->xlo), + (ymax - ymin)/(this->yhi - this->ylo) ); + if( *dim < 32 ) *dim = 32; + +/* Annul the PointSet holding the current grid. */ + *pset1 = astAnnul( *pset1 ); + *pset2 = astAnnul( *pset2 ); + +/* Create the new grid covering the region containing good physical + coordinates. */ + (void) MakeGrid( this, frm, map, 1, *dim, xmin, xmax, ymin, ymax, 2, + pset1, pset2, 0, method, class, status ); + } + } + +/* Annul the Mapping from base to current Frame, and the pointer to the + Current Frame. */ + map = astAnnul( map ); + frm = astAnnul( frm ); + +/* If an error has occurred, annul the two pointsets and indicate that + there are no good points in the plotting area. */ + if( !astOK ){ + *pset1 = astAnnul( *pset1 ); + *pset2 = astAnnul( *pset2 ); + frac = 0.0; + } + +/* Return. */ + return frac; + +} + +static int GraphGrid( int dim, int disk, double xlo, double xhi, double ylo, + double yhi, double **ptr1, int *status ){ +/* +* Name: +* GraphGrid + +* Purpose: +* Fill an array with a square grid of graphics coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int GraphGrid( int dim, int disk, double xlo, double xhi, double ylo, +* double yhi, double **ptr1, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function fills the supplied array with a square grid of graphics +* coordinates covering the supplied area. The points are stored row by +* row, i.e. if the cell size for the grid is (dx,dy), the first point +* is (xmin,ymin), followed by (xmin+dx,ymin), (xmin+2*dx,ymin), up to +* (xmin+(dim-1)*dx,ymin), followed by the next row (xmin,ymin+dy), +* (xmin+dx,ymin+dy), etc. + +* Parameters: +* dim +* The number of samples along each edge of the grid. +* disk +* If non-zero, the corners of the grid are omitted, resulting in a +* grid that is more disk like than rectangular. +* xlo +* The lower bound on the first axis of the region to be covered +* by the grid. +* xhi +* The upper bound on the first axis of the region to be covered +* by the grid. +* ylo +* The lower bound on the second axis of the region to be covered +* by the grid. +* yhi +* The upper bound on the second axis of the region to be covered +* by the grid. +* ptr1 +* A pointer to an array of two pointers giving the start of the two +* arrays to receive the values for each of the two axes of the graphics +* coordinate data. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of points in the grid. If "disk" is zero, this will be +* the square of "dim". If "disk" is non-zero, this will be less than +* the square of "dim" to account for the lack of corner points. + +*/ + +/* Local Variables: */ + double *px; + double *py; + double cen; + double dx; + double dy2; + double dy; + double dx2; + double r2; + double y; + int i; + int j; + int ok; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Find the cell size. */ + dx = ( xhi - xlo )/(double)( dim - 1 ); + dy = ( yhi - ylo )/(double)( dim - 1 ); + +/* Store the mid cell index. */ + cen = 0.5*( dim - 1 ); + +/* Store the squared radius of the disk. */ + r2 = 1.9*cen*cen; + +/* Initialise pointers to the start of the two arrays to recieve the + returned graphics values for each axis. */ + px = ptr1[ 0 ]; + py = ptr1[ 1 ]; + +/* Loop round row. */ + for( j = 0; j < dim; j++ ){ + dy2 = j - cen; + dy2 *= dy2; + +/* Get the Y coordinate of the current row. */ + y = ylo + j*dy; + +/* Loop round each column in the current row. */ + for( i = 0; i < dim; i++ ){ + +/* If we are forming a disk rather than a square, check if this point is + sufficiently close to the centre to be included in the disk. */ + if( disk ) { + dx2 = i - cen; + dx2 *= dx2; + ok = ( dx2 + dy2 <= r2 ); + } else { + ok = 1; + } + +/* Store the coordinates of the current grid point. */ + if( ok ) { + *(px++) = xlo + i*dx; + *(py++) = y; + } + } + } + +/* Return the used length of the PointSet. */ + return (int)( px - ptr1[ 0 ] ); +} + +static void GrfPop( AstPlot *this, int *status ) { +/* +*++ +* Name: +c astGrfPop +f AST_GRFPOP + +* Purpose: +* Restore previously saved graphics functions used by a Plot. + +* Type: +* Public function. + +* Synopsis: +c #include "plot.h" +c void astGrfPop( AstPlot *this ) +f CALL AST_GRFPOP( THIS STATUS ) + +* Class Membership: +* Plot member function. + +* Description: +c This function restores a snapshot of the graphics functions +c stored previously by calling astGrfPush. The restored graphics +c functions become the current graphics functions used by the Plot. +* +c The astGrfPush and astGrfPop functions are intended for situations +c where it is necessary to make temporary changes to the graphics +c functions used by the Plot. The current functions should first be +c saved by calling astGrfPush. New functions should then be registered +c using astGrfSet. The required graphics should then be produced. +c Finally, astGrfPop should be called to restore the original graphics +c functions. +f The AST_GRFPUSH and AST_GRFPOP functions are intended for situations +f where it is necessary to make temporary changes to the graphics +f functions used by the Plot. The current functions should first be +f saved by calling AST_GRFPUSH. New functions should then be registered +f using AST_GRFSET. The required graphics should then be produced. +f Finally, AST_GRFPOP should be called to restore the original graphics +f functions. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +f - This routine returns without action if there are no snapshots to +c - This function returns without action if there are no snapshots to +* restore. No error is reported in this case. + +*-- +*/ + +/* Local Variables: */ + AstGrfPtrs *newframe; /* Pointer to the stack frame to restore */ + int i; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check the stack is not already empty. */ + if( this->grfnstack > 0 ) { + this->grfnstack--; + + if( astOK ) { + newframe = this->grfstack + this->grfnstack; + + for( i = 0; i < AST__NGRFFUN; i++ ) { + this->grffun[i] = (newframe->grffun)[i]; + } + this->GAttr = newframe->GAttr; + this->GBBuf = newframe->GBBuf; + this->GEBuf = newframe->GEBuf; + this->GFlush = newframe->GFlush; + this->GLine = newframe->GLine; + this->GMark = newframe->GMark; + this->GText = newframe->GText; + this->GCap = newframe->GCap; + this->GTxExt = newframe->GTxExt; + this->GScales = newframe->GScales; + this->GQch = newframe->GQch; + } + } +} + +static void GrfPush( AstPlot *this, int *status ) { +/* +*++ +* Name: +c astGrfPush +f AST_GRFPUSH + +* Purpose: +* Save the current graphics functions used by a Plot. + +* Type: +* Public function. + +* Synopsis: +c #include "plot.h" +c void astGrfPush( AstPlot *this ) +f CALL AST_GRFPUSH( THIS STATUS ) + +* Class Membership: +* Plot member function. + +* Description: +c This function takes a snapshot of the graphics functions which are +f This routine takes a snapshot of the graphics functions which are +* currently registered with the supplied Plot, and saves the snapshot +* on a first-in-last-out stack within the Plot. The snapshot can be +* restored later using function +c astGrfPop. +f AST_GRFPOP. +* +c The astGrfPush and astGrfPop functions are intended for situations +c where it is necessary to make temporary changes to the graphics +c functions used by the Plot. The current functions should first be +c saved by calling astGrfPush. New functions should then be registered +c using astGrfSet. The required graphics should then be produced. +c Finally, astGrfPop should be called to restore the original graphics +c functions. +f The AST_GRFPUSH and AST_GRFPOP functions are intended for situations +f where it is necessary to make temporary changes to the graphics +f functions used by the Plot. The current functions should first be +f saved by calling AST_GRFPUSH. New functions should then be registered +f using AST_GRFSET. The required graphics should then be produced. +f Finally, AST_GRFPOP should be called to restore the original graphics +f functions. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + AstGrfPtrs *newframe; /* Pointer to the new stack frame */ + int i; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Increment the number of frames on the stack. */ + this->grfnstack++; + +/* Ensure the stack is large enough to hold this many frames. */ + this->grfstack = (AstGrfPtrs *) astGrow( (void *) this->grfstack, + this->grfnstack, sizeof( AstGrfPtrs ) ); + if( astOK ) { + +/* Get a pointer to the new stack frame. */ + newframe = this->grfstack + this->grfnstack - 1; + +/* Copy the graphics function pointers from the main Plot attributes + to the new stack frame. */ + for( i = 0; i < AST__NGRFFUN; i++ ) { + (newframe->grffun)[i] = this->grffun[i]; + } + newframe->GAttr = this->GAttr; + newframe->GBBuf = this->GBBuf; + newframe->GEBuf = this->GEBuf; + newframe->GFlush = this->GFlush; + newframe->GLine = this->GLine; + newframe->GMark = this->GMark; + newframe->GText = this->GText; + newframe->GCap = this->GCap; + newframe->GTxExt = this->GTxExt; + newframe->GQch = this->GQch; + newframe->GScales = this->GScales; + } +} + +static void GrfSet( AstPlot *this, const char *name, AstGrfFun fun, int *status ){ +/* +*++ +* Name: +c astGrfSet +f AST_GRFSET + +* Purpose: +c Register a graphics function for use by a Plot. +f Register a graphics routine for use by a Plot. + +* Type: +* Public function. + +* Synopsis: +c #include "plot.h" +c void astGrfSet( AstPlot *this, const char *name, AstGrfFun fun ) +f CALL AST_GRFSET( THIS, NAME, FUN, STATUS ) + +* Class Membership: +* Plot member function. + +* Description: +c This function can be used to select the underlying graphics +c functions to be used when the supplied Plot produces graphical output. +c If this function is not called prior to producing graphical +c output, then the underlying graphics functions selected at +c link-time (using the ast_link command) will be used. To use +c alternative graphics functions, call this function before +c the graphical output is created, specifying the graphics +c functions to be used. This will register the function for future +c use, but the function will not actually be used until the Grf +c attribute is given a non-zero value. +f This routine can be used to select the underlying graphics +f routines to be used when the supplied Plot produces graphical output. +f If this routine is not called prior to producing graphical +f output, then the underlying graphics routines selected at +f link-time (using the ast_link command) will be used. To use +f alternative graphics routines, call this routine before +f the graphical output is created, specifying the graphics +f routines to be used. This will register the routine for future +f use, but the routine will not actually be used until the Grf +f attribute is given a non-zero value. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c name +f NAME = CHARACTER * ( * ) (Given) +c A name indicating the graphics function to be replaced. +c Various graphics functions are used by the +c Plot class, and any combination of them may be supplied by calling +c this function once for each function to be replaced. If any of the +c graphics functions are not replaced in this way, the +c corresponding functions in the graphics interface selected at +c link-time (using the ast_link command) are used. The allowed +c names are: +f A name indicating the graphics routine to be replaced. +f Various graphics routines are used by the +f Plot class, and any combination of them may be supplied by calling +f this routine once for each routine to be replaced. If any of the +f graphics routines are not replaced in this way, the +f corresponding routines in the graphics interface selected at +f link-time (using the ast_link command) are used. The allowed +f function names are: +* +* - Attr - Enquire or set a graphics attribute value +* - BBuf - Start a new graphics buffering context +* - Cap - Inquire a capability +* - EBuf - End the current graphics buffering context +* - Flush - Flush all pending graphics to the output device +* - Line - Draw a polyline (i.e. a set of connected lines) +* - Mark - Draw a set of markers +* - Qch - Return the character height in world coordinates +* - Scales - Get the axis scales +* - Text - Draw a character string +* - TxExt - Get the extent of a character string +* +* The string is case insensitive. For details of the interface +* required for each, see the sections below. +c fun +f FUN = INTEGER FUNCTION (Given) +c A Pointer to the function to be used to provide the +c functionality indicated by parameter name. The interface for +c each function is described below, but the function pointer should +c be cast to a type of AstGrfFun when calling astGrfSet. +f The name of the routine to be used to provide the +f functionality indicated by parameter NAME (the name +f should also appear in a Fortran EXTERNAL statement in the +f routine which invokes AST_GRFSET). +* +c Once a function has been provided, a null pointer can be supplied +c in a subsequent call to astGrfSet to reset the function to the +c corresponding function in the graphics interface selected at +c link-time. +f Once a routine has been provided, the "null" routine AST_NULL can +f be supplied in a subsequent call to astGrfSet to reset the routine +f to the corresponding routine in the graphics interface selected at +f link-time. AST_NULL is defined in the AST_PAR include file. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Function Interfaces: +* All the functions listed below (except for "Cap") should return an +* integer value of 0 if an error occurs, and 1 otherwise. All x and y +* values refer +f to "graphics cordinates" as defined by the GRAPHBOX parameter of +f the AST_PLOT call which created the Plot. +c to "graphics cordinates" as defined by the graphbox parameter of +c the astPlot call which created the Plot. +* +c The first parameter ("grfcon") +f The first argument (GRFCON) +* for each function is an AST KeyMap pointer that can be used by the +* called function to establish the context in which it is being called. +* The contents of the KeyMap are determined by the calling +* application, which should obtain a pointer to the KeyMap using the +f AST_GETGRFCONTEXT routine, +c astGetGrfContext function, +* and then store any necessary information in the KeyMap using the +* methods of the KeyMap class. Note, the functions listed below +* should never annul or delete the supplied KeyMap pointer. + +* Attr: +* The "Attr" function returns the current value of a specified graphics +* attribute, and optionally establishes a new value. The supplied +* value is converted to an integer value if necessary before use. +* It requires the following interface: +* +c int Attr( AstObject *grfcon, int attr, double value, double *old_value, int prim ) +f INTEGER FUNCTION ATTR( GRFCON, ATT, VAL, OLDVAL, PRIM ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - attr - An integer value identifying the required attribute. +c The following symbolic values are defined in grf.h: +f - ATT = INTEGER (Given) - An integer identifying the required attribute. +f The following symbolic values are defined in GRF_PAR: +* GRF__STYLE (Line style), +* GRF__WIDTH (Line width), +* GRF__SIZE (Character and marker size scale factor), +* GRF__FONT (Character font), +* GRF__COLOUR (Colour index). +c - value - +f - VAL = DOUBLE PRECISION (Given) - +c A new value to store for the attribute. If this is AST__BAD +* no value is stored. +c - old_value - A pointer to a double in which to return +f - OLDVAL = DOUBLE PRECISION (Returned) - Returned holding +* the attribute value. +c If this is NULL, no value is returned. +c - prim - +f - PRIM = INTEGER (Given) - +* The sort of graphics primitive to be drawn with the new attribute. +c Identified by the following values defined in grf.h: +f Identified by the following values defined in GRF_PAR: +* GRF__LINE, +* GRF__MARK, +* GRF__TEXT. + +* BBuf: +* The "BBuf" function should start a new graphics buffering context. +* A matching call to the function "EBuf" should be used to end the +* context. The nature of the buffering is determined by the underlying +* graphics system. +* +c int BBuf( AstObject *grfcon ) +f INTEGER FUNCTION BBUF( GRFCON ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. + +* Cap: +* The "Cap" function is called to determine if the grf module has a +* given capability, as indicated by the "cap" argument: +* +c int Cap( AstObject *grfcon, int cap, int value ) +f INTEGER FUNCTION CAP( GRFCON, CAP, VALUE ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - cap - +f - CAP = INTEGER (Given) +* The capability being inquired about. This will be one of the +c following constants defined in grf.h: +f following constants defined in GRF_PAR: +* +* GRF__SCALES: This function should return a non-zero value if the +* "Scales" function is implemented, and zero otherwise. The supplied +c "value" argument should be ignored. +f VALUE argument should be ignored. +* +* GRF__MJUST: This function should return a non-zero value if +* the "Text" and "TxExt" functions recognise "M" as a +* character in the justification string. If the first character of +* a justification string is "M", then the text should be justified +* with the given reference point at the bottom of the bounding box. +* This is different to "B" justification, which requests that the +* reference point be put on the baseline of the text, since some +* characters hang down below the baseline. If the "Text" or +* "TxExt" function cannot differentiate between "M" and "B", +* then this function should return zero, in which case "M" +* justification will never be requested by Plot. The supplied +c "value" argument should be ignored. +f VALUE argument should be ignored. +* +* GRF__ESC: This function should return a non-zero value if the +* "Text" and "TxExt" functions can recognise and interpret +* graphics escape sequences within the supplied string (see +* attribute Escape). Zero should be returned if escape sequences +* cannot be interpreted (in which case the Plot class will interpret +c them itself if needed). The supplied "value" argument should be +f them itself if needed). The supplied VALUE argument should be +* ignored only if escape sequences cannot be interpreted by "Text" and +c "TxExt". Otherwise, "value" indicates whether "Text" and "TxExt" +c should interpret escape sequences in subsequent calls. If "value" is +f "TxExt". Otherwise, VALUE indicates whether "Text" and "TxExt" +f should interpret escape sequences in subsequent calls. If VALUE is +* non-zero then escape sequences should be interpreted by "Text" and +* "TxExt". Otherwise, they should be drawn as literal text. +* +c - value - +f - VALUE = INTEGER (Given) +c The use of this parameter depends on the value of "cap" as +f The use of this parameter depends on the value of CAP as +* described above. + +* - Returned Function Value: +c The value returned by the function depends on the value of "cap" +f The value returned by the function depends on the value of CAP +* as described above. Zero should be returned if the supplied +* capability is not recognised. + +* EBuf: +* The "EBuf" function should end the current graphics buffering +* context. See the description of "BBuf" above for further details. +* It requires the following interface: +* +c int EBuf( AstObject *grfcon ) +f INTEGER FUNCTION EBUF( GRFCON ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. + +* Flush: +* The "Flush" function ensures that the display device is up-to-date, +* by flushing any pending graphics to the output device. It +* requires the following interface: +* +c int Flush( AstObject *grfcon ) +f INTEGER FUNCTION FLUSH( GRFCON ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. + +* Line: +* The "Line" function displays lines joining the given positions and +* requires the following interface: +* +c int Line( AstObject *grfcon, int n, const float *x, const float *y ) +f INTEGER FUNCTION LINE( GRFCON, N, X, Y ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - n - The number of positions to be joined together. +f - N = INTEGER (Given) - The number of positions to be joined together. +c - x - A pointer to an array holding the "n" x values. +f - X( N ) = REAL (Given) - An array holding the "n" x values. +c - y - A pointer to an array holding the "n" y values. +f - Y( N ) = REAL (Given) - An array holding the "n" y values. + +* Mark: +* The "Mark" function displays markers at the given positions. It +* requires the following interface: +* +c int Mark( AstObject *grfcon, int n, const float *x, const float *y, int type ) +f INTEGER FUNCTION MARK( GRFCON, N, X, Y, TYPE ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - n - The number of positions to be marked. +f - N = INTEGER (Given) - The number of positions to be marked. +c - x - A pointer to an array holding the "n" x values. +f - X( N ) = REAL (Given) - An array holding the "n" x values. +c - y - A pointer to an array holding the "n" y values. +f - Y( N ) = REAL (Given) - An array holding the "n" y values. +c - type - An integer which can be used to indicate the type of marker +c symbol required. +f - TYPE = INTEGER (Given) - An integer which can be used to indicate +f the type of marker symbol required. + +* Qch: +* The "Qch" function returns the heights of characters drawn vertically +* and horizontally in graphics coordinates. It requires the following +* interface: +* +c int Qch( AstObject *grfcon, float *chv, float *chh ) +f INTEGER FUNCTION QCH( GRFCON, CHV, CHH ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - chv - A pointer to the float which is to receive the height of +f - CHV = REAL (Returned) The height of +* characters drawn with a vertical baseline. This will be an +* increment in the X axis. +c - chh - A pointer to the float which is to receive the height of +f - CHH = REAL (Returned) The height of +* characters drawn with a horizontal baseline. This will be an +* increment in the Y axis. + +* Scales: +* The "Scales" function returns two values (one for each axis) which +* scale increments on the corresponding axis into a "normal" coordinate +* system in which: 1) the axes have equal scale in terms of (for instance) +* millimetres per unit distance, 2) X values increase from left to +* right, and 3) Y values increase from bottom to top. It requires the +* following interface: +* +c int Scales( AstObject *grfcon, float *alpha, float *beta ) +f INTEGER FUNCTION SCALES( GRFCON, ALPHA, BETA ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - alpha - A pointer to the float which is to receive the +f - ALPHA = REAL (Returned) The +* scale for the X axis (i.e. Xnorm = alpha*Xworld). +c - beta - A pointer to the float which is to receive the +f - BETA = REAL (Returned) The +* scale for the Y axis (i.e. Ynorm = beta*Yworld). + +* Text: +* The "Text" function displays a character string at a given +* position using a specified justification and up-vector. It +* requires the following interface: +* +c int Text( AstObject *grfcon, const char *text, float x, float y, const char *just, +c float upx, float upy ) +f INTEGER FUNCTION TEXT( GRFCON, TEXT, X, Y, JUST, UPX, UPY ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - text - Pointer to a null-terminated character string to be displayed. +f - TEXT = CHARACTER * ( * ) (Given) - The string to be displayed. +c - x - The reference x coordinate. +f - X = REAL (Given) - The reference x coordinate. +c - y - The reference y coordinate. +f - Y = REAL (Given) - The reference y coordinate. +c - just - A character string which specifies the location within the +f - JUST = CHARACTER * ( * ) (Given ) - A string which specifies the +f location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. Note, "bottom" +* corresponds to the base-line of normal text. Some characters +* (eg "y", "g", "p", etc) descend below the base-line. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +c - upx - The x component of the up-vector for the text. +f - UPX = REAL (Given) - The x component of the up-vector for the text. +* If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* left to right on the screen. +c - upy - The y component of the up-vector for the text. +f - UPX = REAL (Given) - The y component of the up-vector for the text. +* If necessary the supplied value should be negated +* to ensure that positive values always refer to displacements from +* bottom to top on the screen. + +* TxExt: +* The "TxExt" function returns the corners of a box which would enclose +* the supplied character string if it were displayed using the +* Text function described above. The returned box includes any leading +* or trailing spaces. It requires the following interface: +* +c int TxExt( AstObject *grfcon, const char *text, float x, float y, const char *just, +c float upx, float upy, float *xb, float *yb ) +f INTEGER FUNCTION TXEXT( GRFCON, TEXT, X, Y, JUST, UPX, UPY, XB, YB ) +* +c - grfcon - +f - GRFCON = INTEGER (Given) - +* A KeyMap containing information passed from the calling application. +c - text - Pointer to a null-terminated character string to be displayed. +f - TEXT = CHARACTER * ( * ) (Given) - The string to be displayed. +c - x - The reference x coordinate. +f - X = REAL (Given) - The reference x coordinate. +c - y - The reference y coordinate. +f - Y = REAL (Given) - The reference y coordinate. +c - just - A character string which specifies the location within the +f - JUST = CHARACTER * ( * ) (Given ) - A string which specifies the +f location within the +* text string which is to be placed at the reference position +* given by x and y. See "Text" above. +c - upx - The x component of the up-vector for the text. +f - UPX = REAL (Given) - The x component of the up-vector for the text. +* See "Text" above. +c - upy - The y component of the up-vector for the text. +f - UPX = REAL (Given) - The y component of the up-vector for the text. +* See "Text" above. +c - xb - An array of 4 elements in which to return the x coordinate of +f - XB( 4 ) = REAL (Returned) - Returned holding the x coordinate of +* each corner of the bounding box. +c - yb - An array of 4 elements in which to return the y coordinate of +f - YB( 4 ) = REAL (Returned) - Returned holding the y coordinate of +* each corner of the bounding box. + +*-- +*/ + +/* Local Variables: */ + const char *class; /* Object class */ + const char *method; /* Current method */ + int ifun; /* Index into grf function list */ + void (* wrapper)(); /* Wrapper function for C Grf routine*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + wrapper = NULL; + +/* Store the current method and class for inclusion in error messages + generated by lower level functions. */ + method = "astGrfSet"; + class = astClass( this ); + +/* Identify the supplied function name and get its integer index into the + list of grf functions. */ + ifun = astGrfFunID( name, method, class ); + +/* Store the pointer. */ + if( astOK ) { + this->grffun[ifun] = fun; + +/* In general, the interface to each Grf function will differ for + different languages. So we need a wrapper function with a known fixed + interface which can be used to invoke the actual Grf function with + an interface suited to the language in use. Call astGrfWrapper to store + a wrapper to a suitable function which can invoke the supplied + grf function. Here, we assume that the supplied function has a C + interface, so we set up a C wrapper. If this function is being called + from another language, then the interface for this function within + that language should set up an appropriate wrapper after calling this + function, thus over-riding the C wrapper set up here. */ + if( ifun == AST__GATTR ) { + wrapper = (AstGrfWrap) CGAttrWrapper; + + } else if( ifun == AST__GBBUF ) { + wrapper = (AstGrfWrap) CGBBufWrapper; + + } else if( ifun == AST__GEBUF ) { + wrapper = (AstGrfWrap) CGEBufWrapper; + + } else if( ifun == AST__GFLUSH ) { + wrapper = (AstGrfWrap) CGFlushWrapper; + + } else if( ifun == AST__GLINE ) { + wrapper = (AstGrfWrap) CGLineWrapper; + + } else if( ifun == AST__GMARK ) { + wrapper = (AstGrfWrap) CGMarkWrapper; + + } else if( ifun == AST__GTEXT ) { + wrapper = (AstGrfWrap) CGTextWrapper; + + } else if( ifun == AST__GCAP ) { + wrapper = (AstGrfWrap) CGCapWrapper; + + } else if( ifun == AST__GTXEXT ) { + wrapper = (AstGrfWrap) CGTxExtWrapper; + + } else if( ifun == AST__GSCALES ) { + wrapper = (AstGrfWrap) CGScalesWrapper; + + } else if( ifun == AST__GQCH ) { + wrapper = (AstGrfWrap) CGQchWrapper; + + } else if( astOK ) { + astError( AST__INTER, "%s(%s): AST internal programming error - " + "Grf function id %d not yet supported.", status, method, class, + ifun ); + } + astGrfWrapper( this, name, wrapper ); + } +} + +int astGrfFunID_( const char *name, const char *method, const char *class, int *status ) { +/* +*+ +* Name: +* astGrfFunID + +* Purpose: +* Return the integer identifier for a given GRF routine. + +* Type: +* Hidden public function. + +* Synopsis: +* #include "plot.h" +* int astGrfFunID( const char *name, const char *method, +* const char *class ) + +* Class Membership: +* Plot member function. + +* Description: +* This function returns an integer identifying the named grf function. +* An error is reported if the named function is unknown. This function +* is used by non-class modules within AST (e.g. fplot.c) which is why +* it is public. It is not intended to be used by the public. + +* Parameters: +* name +* The grf function name. Any unambiguous abbreviation will do. +* Case is ignored. The full list of grf function names is: +* "Attr BBuf EBuf Scales Flush Line Mark Qch Text TxExt". See +* grf_pgplot.c for details of these functions. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. + +*- +*/ + +/* Note that the list of identifiers here must be in the same order as the + sorted values of the constants AST__GATTR, AST__GFLUSH, etc */ + return FullForm( "Attr Flush Line Mark Text TxExt Scales Qch Cap BBuf " + "EBuf", name, "Grf function name (programming error)", + method, class, status ); +} + +static char *GrfItem( int item, const char *text, int *axis, int *status ){ +/* +* Name: +* GrfItem + +* Purpose: +* Return the textual name corresponding to a specified graphical item +* index. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* char *GrfItem( int item, const char *text, int *axis, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function returns a textual description of the graphical item +* with the supplied index. + +* Parameters: +* item +* The index of the graphical item. +* text +* A pointer to a string which will be appended to the textual +* description of the graphical item. May be NULL. +* axis +* Pointer to a place in which to return the index (0,1, or 2) of +* the axis to which the attribute refers, If the attribute does +* not refer to a specific axis, -1 is returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated string holding the textual +* description of the graphical item, followed by any supplied "text". + +* Notes: +* - An error is reported and a NULL pointer returned if the +* index does not correspond to any graphical item. +* - The returned pointer should be freed using astFree when it is no +* longer needed. +* - This function attempts to execute even if an error has already +* occurred. + +*/ + +/* Local Variables: */ + char *desc; /* Pointer to the item description */ + char *ret; /* Pointer to the returned string */ + int dlen; /* Length of the item description */ + + if( axis ) *axis = -1; + + if( item == AST__BORDER_ID ) { + desc = "Border"; + + } else if ( item == AST__GRIDLINE_ID ) { + desc = "Gridline"; + + } else if ( item == AST__GRIDLINE1_ID ) { + desc = "Axis 1 gridline"; + if( axis ) *axis = 0; + + } else if ( item == AST__GRIDLINE2_ID ) { + desc = "Axis 2 gridline"; + if( axis ) *axis = 1; + + } else if ( item == AST__GRIDLINE3_ID ) { + desc = "Axis 3 gridline"; + if( axis ) *axis = 2; + + } else if ( item == AST__CURVE_ID ) { + desc = "Curve"; + + } else if ( item == AST__NUMLABS_ID ) { + desc = "Numerical labels"; + + } else if ( item == AST__TEXTLABS_ID ) { + desc = "Textual labels"; + + } else if ( item == AST__TITLE_ID ) { + desc = "Title"; + + } else if ( item == AST__MARKS_ID ) { + desc = "Markers"; + + } else if ( item == AST__TEXT_ID ) { + desc = "Text string"; + + } else if ( item == AST__TICKS_ID ) { + desc = "Major and minor ticks"; + + } else if ( item == AST__AXIS1_ID ) { + desc = "Axis 1"; + if( axis ) *axis = 0; + + } else if ( item == AST__AXIS2_ID ) { + desc = "Axis 2"; + if( axis ) *axis = 1; + + } else if ( item == AST__AXIS3_ID ) { + desc = "Axis 3"; + if( axis ) *axis = 2; + + } else if ( item == AST__NUMLAB1_ID ) { + desc = "Axis 1 numerical labels"; + if( axis ) *axis = 0; + + } else if ( item == AST__NUMLAB2_ID ) { + desc = "Axis 2 numerical labels"; + if( axis ) *axis = 1; + + } else if ( item == AST__NUMLAB3_ID ) { + desc = "Axis 3 numerical labels"; + if( axis ) *axis = 2; + + } else if ( item == AST__TEXTLAB1_ID ) { + desc = "Axis 1 textual label"; + if( axis ) *axis = 0; + + } else if ( item == AST__TEXTLAB2_ID ) { + desc = "Axis 2 textual label"; + if( axis ) *axis = 1; + + } else if ( item == AST__TEXTLAB3_ID ) { + desc = "Axis 3 textual label"; + if( axis ) *axis = 2; + + } else if ( item == AST__TICKS1_ID ) { + desc = "Axis 1 tick marks"; + if( axis ) *axis = 0; + + } else if ( item == AST__TICKS2_ID ) { + desc = "Axis 2 tick marks"; + if( axis ) *axis = 1; + + } else if ( item == AST__TICKS3_ID ) { + desc = "Axis 3 tick marks"; + if( axis ) *axis = 2; + + } else { + desc = NULL; + if( astOK ){ + astError( AST__INTER, "GrfItem: AST internal programming error - " + "Invalid graphical item index %d supplied to GrfItem.", status, + item ); + } + } + + if( desc ) { + dlen = strlen( desc ); + + if( text ) { + ret = astStore( NULL, desc, dlen + strlen( text ) + 1 ); + if( ret ) strcpy( ret + dlen, text ); + } else { + ret = astStore( NULL, desc, dlen + 1 ); + } + + } else { + ret = NULL; + } + +/* Return the answer. */ + return ret; +} + +static void GrfWrapper( AstPlot *this, const char *name, AstGrfWrap wrapper, int *status ) { +/* +*+ +* Name: +* astGrfWrapper + +* Purpose: +* Register a wrapper function for a F77 or C Grf function. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot.h" +* void astGrfWrapper( AstPlot *this, const char *name, AstGrfWrap wrapper) + +* Description: +* This function stores a pointer to the supplied wrapper function +* within the plot, associating it with the grf function indicated by +* the "name" parameter. The supplied wrapper function should call the +* named grf function, using an interface appropriate to the language +* in which the grf function is written. + +* Parameters: +* this +* The plot. +* name +* A name indicating the graphics function which is called by the +* supplied wrapper function. See astGrfSet for details. +* wrapper +* A pointer to the wrapper function. This will be cast to a +* specific type for the named grf function before being store +* in the Plot. +*- +*/ + +/* Local Variables: */ + const char *class; /* Object class */ + const char *method; /* Current method */ + int ifun; /* Index into grf function list */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the current method and class for inclusion in error messages + generated by lower level functions. */ + method = "astGrfWrapper"; + class = astClass( this ); + +/* Identify the supplied function name and get its integer index into the + list of grf functions. */ + ifun = astGrfFunID( name, method, class ); + +/* Cast the wrapper to an interface appropriate for the wrapped grf + function, and store it in the appropriate component of the Plot. */ + if( ifun == AST__GATTR ) { + this->GAttr = (AstGAttrWrap) wrapper; + + } else if( ifun == AST__GBBUF ) { + this->GBBuf = (AstGBBufWrap) wrapper; + + } else if( ifun == AST__GEBUF ) { + this->GEBuf = (AstGEBufWrap) wrapper; + + } else if( ifun == AST__GFLUSH ) { + this->GFlush = (AstGFlushWrap) wrapper; + + } else if( ifun == AST__GLINE ) { + this->GLine = (AstGLineWrap) wrapper; + + } else if( ifun == AST__GMARK ) { + this->GMark = (AstGMarkWrap) wrapper; + + } else if( ifun == AST__GTEXT ) { + this->GText = (AstGTextWrap) wrapper; + + } else if( ifun == AST__GCAP ) { + this->GCap = (AstGCapWrap) wrapper; + + } else if( ifun == AST__GTXEXT ) { + this->GTxExt = (AstGTxExtWrap) wrapper; + + } else if( ifun == AST__GSCALES ) { + this->GScales = (AstGScalesWrap) wrapper; + + } else if( ifun == AST__GQCH ) { + this->GQch = (AstGQchWrap) wrapper; + + } else if( astOK ) { + astError( AST__INTER, "%s(%s): AST internal programming error - " + "Grf function id %d not yet supported.", status, method, class, + ifun ); + } +} + +static void Grid( AstPlot *this_nd, int *status ){ +/* +*++ +* Name: +c astGrid +f AST_GRID + +* Purpose: +* Draw a set of labelled coordinate axes. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astGrid( AstPlot *this ) +f CALL AST_GRID( THIS, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function draws a complete annotated set of +f This routine draws a complete annotated set of +* coordinate axes for a Plot with (optionally) a coordinate grid +* superimposed. Details of the axes and grid can be controlled by +* setting values for the various attributes defined by the Plot +* class (q.v.). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - If the supplied Plot is a Plot3D, the axes will be annotated +* using three 2-dimensional Plots, one for each 2D plane in the 3D +* current coordinate system. The plots will be "pasted" onto 3 faces +* of the cuboid graphics volume specified when the Plot3D was +* constructed. The faces to be used can be controlled by the "RootCorner" +* attribute. +* - An error results if either the current Frame or the base Frame +* of the Plot is not 2-dimensional or (for a Plot3D) 3-dimensional. +* - An error also results if the transformation between the base +* and current Frames of the Plot is not defined in either +* direction (i.e. the Plot's TranForward or TranInverse attribute +* is zero). +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPlot *this; /* Plot with 2d current Frame */ + AstPlotCurveData **cdata;/* Pointer to info. about breaks in curves */ + TickInfo **grid; /* Pointer to info. about tick marks */ + const char *class; /* Object class */ + const char *method; /* Current method */ + double cen[ 2 ]; /* Position of first tick mark */ + double gap[ 2 ]; /* Gap between tick marks */ + double labelat[ 2 ]; /* Axis values at which tick marks are put */ + int axis; /* Physical axis index */ + int border; /* Draw a border? */ + int clip; /* Original Clip attribute value */ + int dounits[2]; /* Include Units in each axis label? */ + int drawgrid; /* Is a grid of lines to be drawn? */ + int clredge; /* Clear the Edge attributes before returning? */ + int edgeticks; /* Draw labels round edges of plotting area? */ + int escs; /* Original astEscapes value */ + int ink; /* Draw the grid with visible ink? */ + int inval; /* Were areas of invalid coordinates found? */ + int labelling; /* Value of Labelling attribute */ + int loglabelset[2]; /* Were the LogLabel attributes set initially? */ + int logticksset[2]; /* Were the LogTicks attributes set initially? */ + int naxes; /* No. of axes in the base or current Frame */ + int oldedge0; /* Default value for Edge(1) */ + int oldedge1; /* Default value for Edge(2) */ + int useint; /* Do interior labels give us an advantage? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_nd); + +/* Store the current method and class for inclusion in error messages + generated by lower level functions. */ + method = "astGrid"; + class = astClass( this_nd ); + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this_nd ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Ensure AST functions included graphical escape sequences in any + returned text strings. */ + escs = astEscapes( 1 ); + +/* Note if attributes which have complex dynamic defaults are set + initially. */ + logticksset[0] = astTestLogTicks( this_nd, 0 ); + logticksset[1] = astTestLogTicks( this_nd, 1 ); + loglabelset[0] = astTestLogLabel( this_nd, 0 ); + loglabelset[1] = astTestLogLabel( this_nd, 1 ); + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Get a Plot with a 2D (or 1D) current Frame. */ + this = (AstPlot *) Fset2D( (AstFrameSet *) this_nd, AST__CURRENT, status ); + +/* Check the current Frame of the Plot is 2-D. */ + naxes = astGetNout( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the current " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Ensure that all lines are clipped at the plot boundary.*/ + if( astTestClip( this ) ) { + clip = astGetClip( this ); + astSetClip( this, 1 ); + } else { + clip = -1; + } + +/* If the protected attribute "Ink" is set to zero, then the plot + is drawn in "invisble ink" (i.e. all the calculations needed to + produce the grid are performed, but nothing is actually drawn). */ + ink = astGetInk( this ); + +/* Initialise the bounds of the box containing all plotted lines and + numerical labels. */ + Box_lbnd[ 0 ] = FLT_MAX; + Box_lbnd[ 1 ] = FLT_MAX; + Box_ubnd[ 0 ] = FLT_MIN; + Box_ubnd[ 1 ] = FLT_MIN; + +/* Obtain the requested centre attribute values for both physical axes. */ + for( axis = 0; axis < 2; axis++ ){ + cen[ axis ] = astGetCentre( this, axis ); + } + +/* Determine where to put the major axis values. */ + grid = GridLines( this, cen, gap, &inval, method, class, status ); + +/* If the user has set an explicit value for Grid, use it. */ + if( astTestGrid( this ) ){ + drawgrid = astGetGrid( this ); + +/* If not, the default for Grid is based on whether or not there are any + invalid regions. */ + } else if( inval ){ + drawgrid = 1; + + } else { + drawgrid = 0; + } + +/* Draw the curves marking the major tick values on each axis. Information + is returned describing the positions of the breaks in these curves. */ + cdata = DrawGrid( this, grid, ( ink && drawgrid ), method, class, status ); + +/* See if labels and tick marks will be drawn round the edges of the + plotting area, rather than within it (no labels are actually drawn + yet). Interior labels can always be produced, in which case edgeticks + is set explicitly to zero to indicate that ticks will be internal. + Exterior labelling may or may not be possible. If it is requested, + check to see if it is possible. */ + clredge = 0; + labelling = astGetLabelling( this ); + if( labelling ){ + edgeticks = 0; + } else { + edgeticks = EdgeLabels( this, 0, grid, cdata, 0, method, class, status ); + +/* If the external labelling was requested, but could not be produced... */ + if( !edgeticks ) { + +/* and if the Edge attributes have not been set... */ + if( !astTestEdge( this, 0 ) && !astTestEdge( this, 1 ) ) { + +/* Try flipping the default Edge values, to see if this allows us to + honour the requested Labelling scheme. */ + oldedge0 = astGetEdge( this, 0 ); + oldedge1 = astGetEdge( this, 1 ); + astSetEdge( this, 0, oldedge1 ); + astSetEdge( this, 1, oldedge0 ); + +/* See if exterior labels could be drawn with these new edges. */ + edgeticks = EdgeLabels( this, 0, grid, cdata, 0, method, class, status ); + +/* If this would allow us to use the requested labelling scheme, retain + the new Edge values, setting a flag to indicate that they will need to be + cleared before returning. Otherwise, clear them. */ + if( edgeticks ) { + clredge = 1; + + } else { + astClearEdge( this, 0 ); + astClearEdge( this, 1 ); + } + } + } + } + +/* If edge ticks can still not be produced, but the ForceExterior attribute + has a non-zero value, attempt to create exterior labels even though it + looks like there may be insufficient of them to justify their use. */ + if( !edgeticks && astGetForceExterior( this ) ) { + edgeticks = EdgeLabels( this, 0, grid, cdata, 1, method, class, status ); + } + +/* We may also need to swap edge values when using interior labelling in + order to ensure that the text labels are placed on appropriate edges of + the plotting box. */ + if( !edgeticks && !astTestEdge( this, 0 ) && !astTestEdge( this, 1 ) ) { + if( swapEdges( this, grid, cdata, status ) ) { + oldedge0 = astGetEdge( this, 0 ); + oldedge1 = astGetEdge( this, 1 ); + astSetEdge( this, 0, oldedge1 ); + astSetEdge( this, 1, oldedge0 ); + clredge = 1; + } + } + +/* If edge ticks are being used, store bad values for the labelat values to + indicate that labels are not being drawn within the interior of the + plotting area. */ + if( edgeticks ){ + labelat[ 0 ] = AST__BAD; + labelat[ 1 ] = AST__BAD; + +/* Otherwise, see where interior labels and tick marks should go (the axis + values are put in "labelat"). */ + } else { + useint = Labelat( this, grid, cdata, labelat, method, class, status ); + +/* If interior labelling does not allow us to draw any more ticks, revert + to edge labelling if that is what the user requested. */ + if( !useint && !labelling ) { + labelat[ 0 ] = AST__BAD; + labelat[ 1 ] = AST__BAD; + edgeticks = EdgeLabels( this, 0, grid, cdata, 1, method, class, status ); + } + } + +/* See if a border is required. By default, a border is drawn only when + using exterior labelling. */ + if( astTestBorder( this ) ) { + border = astGetBorder( this ); + } else { + border = edgeticks; + } + +/* See if the Units string is to be inluded in the label. */ + dounits[ 0 ] = astGetLabelUnits( this, 0 ); + dounits[ 1 ] = astGetLabelUnits( this, 1 ); + +/* The rest is not done if no output is required. */ + if( ink ) { + +/* Draw tick marks (major and minor). */ + DrawTicks( this, grid, drawgrid, labelat, gap, method, class, status ); + +/* If required, ensure that curves through the tick marks have been drawn */ + DrawAxis( this, grid, labelat, gap, method, class, status ); + +/* If required, draw a curve around the edge of the area containing valid + physical coordinates. */ + if( border ) (void) astBorder( this ); + +/* Draw the numerical labels at the major tick values. */ + Labels( this, grid, cdata, gap, labelat, method, class, status ); + +/* Draw the textual labels for each axis and a title. */ + TextLabels( this, edgeticks, dounits, method, class, status ); + } + +/* Ensure all lines are flushed to the graphics system. */ + Fpoly( this, method, class, status ); + +/* Store the actual values used for all attributes which have dynamic + defaults. Check the global status to ensure the pointer "grid" can be + used without the possibility of a segmentation violation. */ + for( axis = 0; axis < 2 && astOK ; axis++ ) { + SetUsedLogTicks( this_nd, axis, astGetLogTicks( this, axis ), status ); + SetUsedLogLabel( this_nd, axis, astGetLogLabel( this, axis ), status ); + + if( astGetLogTicks( this, axis ) ) { + SetUsedLogGap( this_nd, axis, gap[ axis ], status ); + } else { + SetUsedGap( this_nd, axis, gap[ axis ], status ); + } + SetUsedCentre( this_nd, axis, cen[ axis ], status ); + SetUsedEdge( this_nd, axis, astGetEdge( this, axis ), status ); + SetUsedLabelAt( this_nd, axis, labelat[ axis ], status ); + SetUsedLabelUnits( this_nd, axis, dounits[ axis ], status ); + +/* If explicit minor tick values were supplied using astSetTickValues, + then set MinTick to the average number of minor tick divisions per major + tick division. */ + if( grid[ axis ]->minticks ) { + SetUsedMinTick( this_nd, axis, + ( grid[ axis ]->nminor + grid[ axis ]->nmajor )/ + ( grid[ axis ]->nmajor - 1 ), status ); + } else { + SetUsedMinTick( this_nd, axis, grid[ axis ]->nminor, status ); + } + + if( astTestTextLab( this, axis ) ) { + SetUsedTextLab( this_nd, axis, astGetTextLab( this, axis ), status ); + } else { + SetUsedTextLab( this_nd, axis, edgeticks, status ); + } + + if( astTestMajTickLen( this, axis ) ) { + SetUsedMajTickLen( this_nd, axis, astGetMajTickLen( this, axis ), status ); + } else { + SetUsedMajTickLen( this_nd, axis, drawgrid ? 0.0 : + astGetMajTickLen( this, axis ), status ); + } + + } + + SetUsedGrid( this_nd, drawgrid, status ); + SetUsedLabelling( this_nd, edgeticks ? 0 : 1, status ); + SetUsedBorder( this_nd, border, status ); + +/* Free the memory used to hold information about the curves. */ + cdata = CleanCdata( cdata, status ); + +/* Free the memory used to hold information about the tick marks. */ + grid = CleanGrid( grid, status ); + +/* If required clear attributes. */ + if( clredge ) { + astClearEdge( this, 0 ); + astClearEdge( this, 1 ); + } + + if( !logticksset[ 0 ] ) astClearLogTicks( this, 0 ); + if( !logticksset[ 1 ] ) astClearLogTicks( this, 1 ); + if( !loglabelset[ 0 ] ) astClearLogLabel( this, 0 ); + if( !loglabelset[ 1 ] ) astClearLogLabel( this, 1 ); + +/* Restore the original value of the Clip attribute. */ + if( clip != -1 ) astSetClip( this, clip ); + +/* Free the 2D Plot. */ + this = astAnnul( this ); + +/* Restore the original value of the flag which says whether graphical + escape sequences should be incldued in any returned text strings. */ + astEscapes( escs ); + +/* Copy the total bounding box to the box which is returned by + astBoundingBox. */ + if( !Boxp_freeze ){ + Boxp_lbnd[ 0 ] = Box_lbnd[ 0 ]; + Boxp_lbnd[ 1 ] = Box_lbnd[ 1 ]; + Boxp_ubnd[ 0 ] = Box_ubnd[ 0 ]; + Boxp_ubnd[ 1 ] = Box_ubnd[ 1 ]; + } + +/* Return. */ + return; + +} + +static void GridLine( AstPlot *this, int axis, const double start[], + double length, int *status ){ +/* +*++ +* Name: +c astGridLine +f AST_GRIDLINE + +* Purpose: +* Draw a grid line (or axis) for a Plot. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astGridLine( AstPlot *this, int axis, const double start[], +c double length ) +f CALL AST_GRIDLINE( THIS, AXIS, START, LENGTH, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function draws a curve in the physical coordinate system of +f This routine draws a curve in the physical coordinate system of +* a Plot by varying only one of the coordinates along the length +* of the curve. It is intended for drawing coordinate axes, +* coordinate grids, and tick marks on axes (but note that these +c are also available via the more comprehensive astGrid function). +f are also available via the more comprehensive AST_GRID routine). +* +* The curve is transformed into graphical coordinate space for +* plotting, so that a straight line in physical coordinates may +* result in a curved line being drawn if the Mapping involved is +* non-linear. Any discontinuities in the Mapping between physical +* and graphical coordinates are catered for, as is any +c clipping established using astClip. +f clipping established using AST_CLIP. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c axis +f AXIS = INTEGER (Given) +* The index of the Plot axis whose physical coordinate value is +* to be varied along the length of the curve (all other +* coordinates will remain fixed). This value should lie in the +* range from 1 to the number of Plot axes (Naxes attribute). +c start +f START( * ) = DOUBLE PRECISION (Given) +* An array, with one element for each axis of the Plot, giving +* the physical coordinates of the start of the curve. +c length +f LENGTH = DOUBLE PRECISION (Given) +* The length of curve to be drawn, given as an increment along +* the selected physical axis. This may be positive or negative. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +c - No curve is drawn if the "start" array contains any +c coordinates with the value AST__BAD, nor if "length" has this value. +f - No curve is drawn if the START array contains any +f coordinates with the value AST__BAD, nor if LENGTH has this value. +* - An error results if the base Frame of the Plot is not 2-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*-- +*/ +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const char *class; /* Object class */ + const char *method; /* Current method */ + int naxes; /* No. of axes in the base Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astGridLine"; + class = astGetClass( this ); + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Initialise the bounding box for primatives produced by this call. */ + if( !Boxp_freeze ) { + Boxp_lbnd[ 0 ] = FLT_MAX; + Boxp_lbnd[ 1 ] = FLT_MAX; + Boxp_ubnd[ 0 ] = FLT_MIN; + Boxp_ubnd[ 1 ] = FLT_MIN; + } + +/* Validate the axis index, converting the axis index to be zero-based. */ + (void) astValidateAxis( this, axis - 1, 1, method ); + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Draw the curve. The break information is stored in an external structure + where it can be accessed by public methods which return information + about the most recently drawn curve. */ + AxPlot( this, axis - 1, start, length, 1, &Curve_data, method, class, status ); + +/* Ensure all lines are flushed to the graphics system. */ + Fpoly( this, method, class, status ); +} + +static TickInfo **GridLines( AstPlot *this, double *cen, double *gap, + int *inval, const char *method, const char *class, int *status ){ +/* +* Name: +* GridLines + +* Purpose: +* Obtain information desribing the major tick values within the plotting +* area. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* TickInfo **GridLines( AstPlot *this, double *cen, double *gap, +* int *inval, const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* A pointer is returned which points to an array of two pointers. Each +* of these pointers points to a TickInfo structure which holds +* information about the ticks on a single axis. These structures, +* together with the array holding the two pointers, should be released +* when no longer needed using CleanGrid. + +* Parameters: +* this +* The Plot. +* cen +* A pointer to an array holding axis values at which to put a single +* central tick. Other ticks are placed evenly on either side of this +* tick. If AST__BAD is provided, a value will be used which would put a +* tick at an axis value of zero. +* gap +* A pointer to an array holding the gaps between ticks required on +* each axis. If this is AST__BAD a suitable default value will be used +* and returned in place of the AST__BAD value. +* inval +* A pointer to a location at which to return a flag indicating if +* any invalid physical coordinates were encountered while deciding on +* the tick values. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to an array of two TickInfo pointers. + +* Notes: +* - This function assumes that the physical coordinate system is 2 +* dimensional, and it should not be used if this is not the case. +* - If an error has already occurred, or if this function should fail +* for any reason, then a NULL pointer is returned. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + GetTicksStatics *statics = NULL; /* Pointer to static data for GetTicks */ + TickInfo **info; /* Returned array of two TickInfo pointers */ + double *lengths; /* Pointer to lengths of each curve section */ + double *starts; /* Pointer to start of each curve section */ + double *ticks; /* Pointer to tick mark values */ + double bot; /* Lowest axis value to display */ + double end; /* Axis value at end of curve section */ + double tmp; /* Temp storage */ + double top; /* Highest axis value to display */ + int i; /* Tick mark index */ + int j; /* Axis index */ + int k; /* Section index */ + int logticks[ 2 ]; /* Uses logarithmicaly spaced tick marks? */ + int nticks; /* Number of tick marks */ + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Get memory to hold two TickInfo pointers. */ + info = (TickInfo **) astMalloc( 2*sizeof( TickInfo *) ); + +/* If succesfull... */ + if( astOK ){ + +/* Initialise the two pointers. */ + info[ 0 ] = NULL; + info[ 1 ] = NULL; + +/* Obtain the tick mark values, and the corresponding formatted labels for + each axis. */ + for( j = 0; j < 2; j++ ){ + info[ j ] = TickMarks( this, j, cen + j, gap + j, inval, + &statics, method, class, status ); + logticks[ j ] = astGetLogTicks( this, j ); + } + +/* Release the resources allocated in the first call to TickMarks. */ + for( j = 0; j < 2; j++ ){ + (void) TickMarks( NULL, j, NULL, gap, NULL, &statics, method, class, + status ); + } + +/* Each major tick value for axis "j" may be marked with a curve parallel + to axis "1-j" drawn across the entire plotting area. We need to decide + where to start drawing this curve and how long it should be. We can + simply use the minimum value on axis "1-j" together with the tick value + on axis "j" to define the starting position. The length could be taken + as the difference between the maximum and minimum values on axis "1-j". + However, this may not be right in some situations. For instance if the + plotting area covers a small range of Right Ascension from 23:59:59 to + 00:00:01. Using the difference between the maximum and minimum values + to give the length of the curve would result in the curve starting at + 00:00:00 (the minimum axis value) and extending for a length of 23:59:59 + (the axis range). To get round this sort of problem, the curve is split + up into sections with lengths and starting positions determined by the + tick mark values on axis "1-j". The first section starts at the minimum + axis value and extends upto the first missing tick mark (in the RA + example, this would be at 00:00:01). Subsequent sections starts at the + next tick mark (23:59:59 in the RA example) and extends upto the next + missing tick mark, or the last tick mark if none are missing. */ + +/* Get the current frame. */ + fr = astGetFrame( this, AST__CURRENT ); + +/* If either axis has log tick spacing, use the simple approach which + assumes that each curve has only one section. */ + if( logticks[ 0 ] || logticks[ 1 ] ) { + +/* Find the start and length of the curve for each tick mark on axis "j". */ + for( j = 0; j < 2 && astOK; j++ ){ + +/* Find the axis range to display on the other axis. */ + bot = astGetBottom( fr, 1 - j ); + top = astGetTop( fr, 1 - j ); + if( bot > top ) { + tmp = top; + top = bot; + bot = tmp; + } + +/* Get a pointer to the major tick mark values on the other axis ("1-j"), + together with the number of them. */ + ticks = info[ 1 - j ]->ticks; + nticks = info[ 1 - j ]->nmajor; + +/* Reserve memory to hold the starts and lengths of each section of the + grid line marking the major ticks on the axis "j". There will only be + one section. */ + starts = (double *) astMalloc( sizeof(double) ); + lengths = (double *) astMalloc( sizeof(double) ); + info[ j ]->start = starts; + info[ j ]->length = lengths; + +/* Check that the pointers can be used. */ + if( astOK ) { + +/* The section starts one gap below the first tick, and ends one gap above + the first tick. Limit both to the displayed range of the axis. */ + if( logticks[ 1 - j ] ) { + starts[ 0 ] = astMIN( top, astMAX( bot, ticks[ 0 ]/gap[ 1 - j ] ) ); + end = astMIN( top, astMAX( bot, ticks[ nticks - 1 ]*gap[ 1 - j ] ) ); + } else { + starts[ 0 ] = astMIN( top, astMAX( bot, ticks[ 0 ] - gap[ 1 - j ] ) ); + end = astMIN( top, astMAX( bot, ticks[ nticks - 1 ] + gap[ 1 - j ] ) ); + } + +/* Store the length of the section. */ + lengths[ 0 ] = end - starts[ 0 ]; + +/* Store the number of sections in the returned structure. */ + info[ 0 ]->nsect = 1; + + } + } + +/* If both axes have linear tick spacing, use the complete approach. */ + } else { + +/* Find the start and length of each section of the curve for each tick + mark on axis "j". */ + for( j = 0; j < 2 && astOK; j++ ){ + +/* Find the axis range to display on the other axis. */ + bot = astGetBottom( fr, 1 - j ); + top = astGetTop( fr, 1 - j ); + if( bot > top ) { + tmp = top; + top = bot; + bot = tmp; + } + +/* Get a pointer to the major tick mark values on the other axis ("1-j"), + together with the number of them. */ + ticks = info[ 1 - j ]->ticks; + nticks = info[ 1 - j ]->nmajor; + +/* Reserve memory to hold the starts and lengths of each section of the + grid line marking the major ticks on the axis "j". The allocated + arrays are the largest that could possibly be needed (i.e. if every + tick mark starts a new section). */ + starts = (double *) astMalloc( sizeof(double)*(size_t) nticks ); + lengths = (double *) astMalloc( sizeof(double)*(size_t) nticks ); + info[ j ]->start = starts; + info[ j ]->length = lengths; + +/* Check that the pointers can be used. */ + if( astOK ) { + +/* Loop round each of the major tick marks on axis "1-j". */ + k = 0; + i = 0; + while( i < nticks ){ + +/* Record the start of the next section of the grid lines. */ + starts[ k ] = ticks[ i++ ]; + +/* Tick marks should be regularly spaced by the values in "gap". Check each + tick mark until a missing tick mark is found. The section ends at the + start of the gap. */ + while( i < nticks && + ( ticks[ i ] - ticks[ i - 1 ] ) < 1.5*gap[ 1 - j ] ) i++; + +/* Record the length of the section. */ + lengths[ k ] = ticks[ i - 1 ] - starts[ k ]; + +/* The section is extended at start and end by one gap in order to "cover + up the joins". Limit this to the displayed range of the axis. */ + starts[ k ] -= gap[ 1 - j]; + lengths[ k ] += 2.0*gap[ 1 - j ]; + +/* Limit the start and end to the displayed range of the axis. */ + end = starts[ k ] + lengths[ k ]; + starts[ k ] = astMIN( top, astMAX( bot, starts[ k ] ) ); + lengths[ k ] = astMIN( top, astMAX( bot, end ) ) - starts[ k ]; + +/* Increment the number of sections. */ + k++; + } + +/* Store the number of sections in the returned structure. */ + info[j]->nsect = k; + + } + } + } + +/* Annull the current frame. */ + fr = astAnnul( fr ); + + } + +/* If an error has occurred, clean up the returned TickInfo structures. */ + if( !astOK ) info = CleanGrid( info, status ); + +/* Return. */ + return info; + +} + +void astGrfAttrs_( AstPlot *this, int id, int set, int prim, const char *method, const char *class, int *status ){ +/* +*+ +* Name: +* astGrfAttrs + +* Purpose: +* Establish values for the graphics attributes for a given object. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot.h" +* void astGrfAttrs( AstPlot *this, int id, int set, int prim, const char *method, const char *class ) + +* Class Membership: +* Plot member function. + +* Description: +* This function should be called with "set=1" to establish the correct +* graphics attributes prior to drawing specific graphical objects. Once +* the object has been drawn, it should be called again with "set=0" to +* re-establish the original graphics attributes. +* +* NOTE, each type of graphical object identified by "id" should be +* drawn entirely by calls to just one of GMark, GText or GLine +* as indicated by argument "prim". + +* Parameters: +* this +* A pointer to the Plot. +* id +* An integer specifying the graphical object to be drawn. +* set +* If non-zero then the attributes for the specified object are set +* to the values indicated by the corresponding Plot attributes, +* and the current values are saved in a static array. If zero, then +* the values are set to the values stored in the static array. +* prim +* Indicates the sort of graphics primative used to draw the +* object. This must be one of (defined in grf.h) : +* +* GRF__LINE +* GRF__MARK +* GRF__TEXT +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. + +* Notes: +* - This function should always be called in pairs with set=1 on the +* first call and set=0 on the second call. +* - If a pair of calls is nested within another pair of calls, the +* inner pair has no effect. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + double *attr; /* Pointer to the next attribute value */ + double dval; /* Floating point attribute value */ + int ival; /* Integer attribute value */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Set up a pointer to the next element in "grfattrs_attrs". */ + attr = grfattrs_attrs; + +/* If we are setting new values, increment the nesting level, otherwise + decrement it. */ + if( set ){ + grfattrs_nesting++; + } else { + grfattrs_nesting--; + } + +/* If we are changing any line attributes, ensure all lines are flushed to + the graphics system. */ + if( prim == GRF__LINE ) Fpoly( this, method, class, status ); + +/* First deal with cases where we are establishing new values for the + graphics attributes by setting them to the values of the corresponding + Plot attributes. Only do this if we are at nesting level one. */ + if( set && grfattrs_nesting == 1 ){ + +/* See if a value has been set in the Plot for the line style attribute for + the specified object, If so, use the value. */ + if( TestUseStyle( this, id, status ) ) { + ival = GetUseStyle( this, id, status ); + +/* Save the current value, and establish the new value. */ + GAttr( this, GRF__STYLE, (double) ival, attr++, prim, method, + class, status ); + +/* If no style was specified, retain the current value. Indicate that no + new value has been established by setting the old value bad. */ + } else { + *(attr++) = AST__BAD; + } + +/* Do the same for the line width attribute. */ + if( TestUseWidth( this, id, status ) ){ + dval = GetUseWidth( this, id, status ); + GAttr( this, GRF__WIDTH, dval, attr++, prim, method, class, status ); + } else { + *(attr++) = AST__BAD; + } + +/* Do the same for the character size attribute. */ + if( TestUseSize( this, id, status ) ) { + dval = GetUseSize( this, id, status ); + GAttr( this, GRF__SIZE, dval, attr++, prim, method, class, status ); + } else { + *(attr++) = AST__BAD; + } + +/* Do the same for the character font attribute. */ + if( TestUseFont( this, id, status ) ){ + ival = GetUseFont( this, id, status ); + GAttr( this, GRF__FONT, (double) ival, attr++, prim, method, class, status ); + } else { + *(attr++) = AST__BAD; + } + +/* Do the same for the colour attribute. */ + if( TestUseColour( this, id, status ) ) { + ival = GetUseColour( this, id, status ); + GAttr( this, GRF__COLOUR, (double) ival, attr++, prim, method, + class, status ); + } else { + *(attr++) = AST__BAD; + } + + } + +/* Now deal with cases where we are re-establishing old values saved on a + previous call to this function. Only do this if we are at nesting + level zero. */ + if( !set && !grfattrs_nesting ){ + GAttr( this, GRF__STYLE, *(attr++), NULL, prim, method, class, status ); + GAttr( this, GRF__WIDTH, *(attr++), NULL, prim, method, class, status ); + GAttr( this, GRF__SIZE, *(attr++), NULL, prim, method, class, status ); + GAttr( this, GRF__FONT, *(attr++), NULL, prim, method, class, status ); + GAttr( this, GRF__COLOUR, *(attr++), NULL, prim, method, class, status ); + } + +/* Return. */ + return; + +} + +static int GVec( AstPlot *this, AstMapping *mapping, double *phy, + int axis, double delta, AstPointSet **pset1, + AstPointSet **pset2, double *gx, double *gy, + double *dx, double *dy, int *flag, const char *method, + const char *class, int *status ){ +/* +* Name: +* GVec + +* Purpose: +* Returns a unit vector parallel to a physical axis at a given point. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int GVec( AstPlot *this, AstMapping *mapping, double *phy, +* int axis, double delta, AstPointSet **pset1, +* AstPointSet **pset2, double *gx, double *gy, +* double *dx, double *dy, int *flag, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function returns a unit vector (in the graphics coordinate +* system) in the positive direction of the specified physical axis, +* at the given physical position. It works by transforming the given +* physical position, together with another very close position, and +* returning the vector between them. It is possible for a +* discontinuity to occur between these two close positions, +* resulting in a very large meaningless vector prior to +* normalisation. For this reason two vectors are found, one joining +* the given position to a close position higher up the axis, and one +* joining a close position lower down the axis to the given position. +* If these two vectors differ in magnitude by a large factor, then +* the shorter of the two vectors is normalised and returned. +* Otherwise, the vector which is closest in direction to the vector +* supplied in [dx,dy] is returned. The returned vector is reversed if +* necessary so that it always points in the positive direction of the +* axis. +* +* If neither of the two vectors can be found (i.e. if the graphics +* coordinates are bad, or coincident), then the vector supplied in +* [dx,dy] is returned unchanged, and a function value of zero is +* returned. Otherwise, a function value of one is returned. + +* Parameters: +* this +* Pointer to the Plot. +* mapping +* Pointer to the Mapping from the base Frame of the Plot ot the +* current Frame. +* phy +* Pointer to an array holding the coordinates in the current Frame +* of the Plot at which the tangent vector is required. +* axis +* The index of the axis within the current Frame for which the +* tangent vector is required. +* delta +* The increment in the axis value to use between positions defining +* the vectors. +* pset1 +* A pointer to a place at which to store a pointer to a PointSet +* holding current Frame coordinates. This PointSet pointer should +* be supplied as NULL on the first call to this function, resulting +* in a new PointSet being created and a pointer to it returned. +* Subsequent calls to this function shopuld retain the pointer +* returned by the first call. The PointSet pointer should be +* annulled using astAnnul when no longer needed. +* pset2 +* A pointer to a place at which to store a pointer to a PointSet +* holding base Frame coordinates. This PointSet is managed in the +* same way as "pset1". +* gx +* A pointer to a double in which to return the graphics X +* coordinate of the position supplied by "phy". +* gy +* A pointer to a double in which to return the graphics Y +* coordinate of the position supplied by "phy". +* dx +* A pointer to a double in which to return the X component +* of the unit tangent vector. This should be supplied holding a +* "default" unit vector which is left unchanged if no new vector +* can be found. +* dy +* A pointer to a double in which to return the Y component +* of the unit tangent vector. This should be supplied holding a +* "default" unit vector which is left unchanged if no new vector +* can be found. +* flag +* A pointer to an int in which to return a flag indicating which +* of the two vectors was returned. Zero is returned if the vector +* was estimated by moving in a positive direction along the axis +* from the position supplied by "phy". One is returned if the vector +* was estimated by moving in a negative direction along the axis +* from the position supplied by "phy" (in this case the returned +* vector will have been negated so that it refers to the positive +* direction). +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One is returned if the vector was found succesfully, Zero is returned +* if the vector could not be estimated for any reason. No error is +* reported if the failure was due to the nature of the mapping. + +* Notes: +* - If an error has already occurred, or if this function should fail +* for any reason, then a NULL pointer is returned. +*/ + +/* Local Variables: */ + double dd1; /* Length of positive vector */ + double dd2; /* Length of negative vector */ + double dx1; /* X component of positive vector */ + double dx2; /* X component of negative vector */ + double dy1; /* Y component of positive vector */ + double dy2; /* Y component of negative vector */ + double **ptr1; /* Pointers to physical coordinate data */ + double **ptr2; /* Pointers to graphics coordinate data */ + int i; /* Axis index */ + int nphy; /* No. of axes in current (physical) Frame */ + int ret; /* Was a vector estimated succesfully? */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + dx1 = 0.0; + dx2 = 0.0; + dy1 = 0.0; + dy2 = 0.0; + +/* Initialise the returned value to indicate that the vector can not + be found. */ + ret = 0; + +/* Get the number of physical coordinates from the mapping. */ + nphy = astGetNout( mapping ); + +/* If no PointSets have been supplied, create some now. PointSet 1 + contains physical coordinates, PointSet 2 contains graphics + coordinates. */ + if( !(*pset1) ) *pset1 = astPointSet( 3, nphy, "", status ); + if( !(*pset2) ) *pset2 = astPointSet( 3, 2, "", status ); + +/* Get pointers to the PointSet data. */ + ptr1 = astGetPoints( *pset1 ); + ptr2 = astGetPoints( *pset2 ); + +/* Check the PointSets can be used. */ + if( astOK ){ + +/* Store the physical coordinates of three close points on a curve + parallel to the given axis, with the centre point at the given + position. */ + for( i = 0; i < nphy; i++ ){ + ptr1[ i ][ 0 ] = phy[ i ]; + ptr1[ i ][ 1 ] = phy[ i ]; + ptr1[ i ][ 2 ] = phy[ i ]; + } + + if( phy[ axis ] != AST__BAD ){ + ptr1[ axis ][ 0 ] = phy[ axis ] - delta; + ptr1[ axis ][ 2 ] = phy[ axis ] + delta; + } + +/* Find the corresponding graphics coordinates. */ + (void) Trans( this, NULL, mapping, *pset1, 0, *pset2, 0, method, class, status ); + +/* Check the central position is OK. */ + *gx = ptr2[ 0 ][ 1 ]; + *gy = ptr2[ 1 ][ 1 ]; + if( astOK && *gx != AST__BAD && *gy != AST__BAD ){ + +/* Get the unit vector between the central position and the position at + the higher physical axis value. Also get the length of the vector + joining the two positions. */ + if( ptr2[ 0 ][ 2 ] != AST__BAD && ptr2[ 1 ][ 2 ] != AST__BAD ){ + dx1 = ptr2[ 0 ][ 2 ] - *gx; + dy1 = ptr2[ 1 ][ 2 ] - *gy; + dd1 = dx1*dx1 + dy1*dy1; + + if( dd1 > 0.0 ) { + dd1 = sqrt( dd1 ); + dx1 /= dd1; + dy1 /= dd1; + } else { + dd1 = AST__BAD; + } + + } else { + dd1 = AST__BAD; + } + +/* Do the same for the position with lower physical axis value, + reversing the direction of the vector so that it refers to the + positive direction. */ + if( ptr2[ 0 ][ 0 ] != AST__BAD && ptr2[ 1 ][ 0 ] != AST__BAD ){ + dx2 = *gx - ptr2[ 0 ][ 0 ]; + dy2 = *gy - ptr2[ 1 ][ 0 ]; + dd2 = dx2*dx2 + dy2*dy2; + + if( dd2 > 0.0 ) { + dd2 = sqrt( dd2 ); + dx2 /= dd2; + dy2 /= dd2; + } else { + dd2 = AST__BAD; + } + + } else { + dd2 = AST__BAD; + } + +/* Only overwrite the supplied vector if at least one of the two tangent + vectors was defined. */ + if( dd1 != AST__BAD || dd2 != AST__BAD ){ + +/* If the first vector was not defined, return the second. */ + if( dd1 == AST__BAD ){ + *dx = dx2; + *dy = dy2; + *flag = 1; + ret = 1; + +/* If the second vector was not defined, return the first. */ + } else if( dd2 == AST__BAD ){ + *dx = dx1; + *dy = dy1; + *flag = 0; + ret = 1; + +/* If the first vector is much longer than the second, return the + second. */ + } else if( dd1 > 100.0*dd2 ){ + *dx = dx2; + *dy = dy2; + *flag = 1; + ret = 1; + +/* If the second vector is much longer than the first, return the + first. */ + } else if( dd2 > 100.0*dd1 ){ + *dx = dx1; + *dy = dy1; + *flag = 0; + ret = 1; + +/* If both vectors are defined and of comparable length, return the + vector which is most nearly parallel to the supplied vector. Note, we + assume that the supplied vector [dx,dy] is a unit vector. */ + } else if( *dx != AST__BAD && *dx != AST__BAD ){ + if( ( dx1*(*dx) + dy1*(*dy) )/dd1 > + ( dx2*(*dx) + dy2*(*dy) )/dd2 ){ + *dx = dx1; + *dy = dy1; + *flag = 0; + ret = 1; + + } else { + *dx = dx2; + *dy = dy2; + *flag = 1; + ret = 1; + } + +/* If no vector was supplied, return the shorter of the two vectors. */ + } else if( dd1 < dd2 ){ + *dx = dx1; + *dy = dy1; + *flag = 0; + ret = 1; + + } else { + *dx = dx2; + *dy = dy2; + *flag = 1; + ret = 1; + + } + + } + + } + + } + +/* Return the answer. */ + return ret; + +} + +static int HasEscapes( const char *text, int *status ) { +/* +* Name: +* HasEscapes + +* Purpose: +* Check if a text string contains any escape sequences. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int HasEscapes( const char *text, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function checks if a text string contains any escape sequences +* (see attribute Escape). + +* Parameters: +* text +* The text to check. +* status +* Pointer to the inherited status variable. + +* Returned: +* Non-zero if any escape sequences are found in the text. Zero +* otherwise. + +*/ + +/* Local Variables: */ + int result; + int type; + int value; + int nc; + +/* Initialise. */ + result = 0; + +/* Check the global error status and the supplied pointer. */ + if ( !astOK || ! text ) return result; + +/* Does the string begin with an escape sequence? */ + if( astFindEscape( text, &type, &value, &nc ) ){ + result = 1; + +/* If not, there must be an escape sequence later in the string if the + number of characters skipped by the above call to astFindEscape is less + than the length of the string. */ + } else if( nc < strlen( text ) ) { + result = 1; + } + +/* Return the result. */ + return result; +} + +static int IdFind( int id, int nax, int *id1, int *id2, int *id3, int *status ) { +/* +* Name: +* IdFind + +* Purpose: +* Find the numerical identifiers to use for a given identifier. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int IdFind( int id, int nax, int *id1, int *id2, int *id3, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* The supplied integer should be a numerical identifier for a +* graphical element of a plot (AST__MARKS_ID, AST__CURVES_ID, etc), or a +* "psuedo-identifier" which represents two other genuine identifiers. +* If the supplied value is a genuine identifier then it is returned +* in *id1, and *id2 is returned equal to -1. If the supplied value +* is a pseudo-identifier then the two corresponding genuine +* identifiers are returned in *id1 and *id2 + +* For instance, if "id" is AST__AXIS1_ID (a genuine id), then *id1 is +* returned equal to AST__AXIS1_ID and *id2 is returned equal to -1. If +* "id" is AST__AXES_ID (a pseudo-identifier), then *id1 is returned equal +* to AST__AXIS1_ID and *id2 is returned equal to AST__AXIS2_ID. + +* Genuine identifiers all have values which are less than the value +* of AST__NPID. Pseudo-identifiers have values which are greater than +* or equal to the value of AST__NPID. + +* Parameters: +* id +* The supplied identifier (genuine or pseudo). +* nax +* The number of axes spanning graphics space (2 or 3). +* id1 +* Pointer to the int at which to return the first genuine +* identifier corresponding to "id". +* id2 +* Pointer to the int at which to return the second genuine +* identifier corresponding to "id" (or -1 if "id" is a genuine +* identifier). +* id3 +* Pointer to the int at which to return the third genuine +* identifier corresponding to "id" (or -1 if "id" has no third +* genuine identifier). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of genuine identifiers corresponding to the supplied +* (possibly Pseudo-) identifier. This will be in the range 1 to 3. + +*/ + +/* Local Variables: */ + int ret; + +/* Initialise returned values. */ + *id1 = id; + *id2 = -1; + *id3 = -1; + ret = 0; + +/* Check the local error status. */ + if ( !astOK ) return ret; + +/* Assume a genuine identifier was supplied. */ + ret = 1; + +/* Check against all known pseudo-identifier. */ + if( id == AST__AXES_ID ) { + ret = nax; + *id1 = AST__AXIS1_ID; + *id2 = AST__AXIS2_ID; + if( nax == 3 ) *id3 = AST__AXIS3_ID; + + } else if( id == AST__GRIDLINE_ID ) { + ret = nax; + *id1 = AST__GRIDLINE1_ID; + *id2 = AST__GRIDLINE2_ID; + if( nax == 3 ) *id3 = AST__GRIDLINE3_ID; + + } else if( id == AST__NUMLABS_ID ) { + ret = nax; + *id1 = AST__NUMLAB1_ID; + *id2 = AST__NUMLAB2_ID; + if( nax == 3 ) *id3 = AST__NUMLAB3_ID; + + } else if( id == AST__TEXTLABS_ID ) { + ret = nax; + *id1 = AST__TEXTLAB1_ID; + *id2 = AST__TEXTLAB2_ID; + if( nax == 3 ) *id3 = AST__TEXTLAB3_ID; + + } else if( id == AST__TICKS_ID ) { + ret = nax; + *id1 = AST__TICKS1_ID; + *id2 = AST__TICKS2_ID; + if( nax == 3 ) *id3 = AST__TICKS3_ID; + + } else if( id >= AST__NPID ) { + astError( AST__INTER, "AST internal programming error - " + "function IdFind in class Plot does not yet support " + "pseudo-identifier value %d", status, id ); + } + +/* Return the answer. */ + return ret; + +} + +void astInitPlotVtab_( AstPlotVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPlotVtab + +* Purpose: +* Initialise a virtual function table for a Plot. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot.h" +* void astInitPlotVtab( AstPlotVtab *vtab, const char *name ) + +* Class Membership: +* Plot vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Plot class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameSetVtab *fset; /* Pointer to FrameSet component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameSetVtab( (AstFrameSetVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This will be + used (by astIsAPlot) to determine if an object belongs to this class. + We can conveniently use the address of the (static) class_init variable to + generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameSetVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->BBuf = BBuf; + vtab->Border = Border; + vtab->BoundingBox = BoundingBox; + vtab->ClearGrid = ClearGrid; + vtab->ClearTol = ClearTol; + vtab->Clip = Clip; + vtab->CopyPlotDefaults = CopyPlotDefaults; + vtab->Curve = Curve; + vtab->CvBrk = CvBrk; + vtab->EBuf = EBuf; + vtab->GenCurve = GenCurve; + vtab->GetDrawnTicks = GetDrawnTicks; + vtab->GetGrid = GetGrid; + vtab->GetTol = GetTol; + vtab->GrfPop = GrfPop; + vtab->GrfPush = GrfPush; + vtab->GrfSet = GrfSet; + vtab->GrfWrapper = GrfWrapper; + vtab->Grid = Grid; + vtab->GridLine = GridLine; + vtab->Mark = Mark; + vtab->Mirror = Mirror; + vtab->PolyCurve = PolyCurve; + vtab->RegionOutline = RegionOutline; + vtab->SetGrid = SetGrid; + vtab->SetTickValues = SetTickValues; + vtab->SetTol = SetTol; + vtab->TestGrid = TestGrid; + vtab->TestTol = TestTol; + vtab->Text = Text; + + vtab->ClearTickAll = ClearTickAll; + vtab->SetTickAll = SetTickAll; + vtab->GetTickAll = GetTickAll; + vtab->TestTickAll = TestTickAll; + + vtab->ClearForceExterior = ClearForceExterior; + vtab->SetForceExterior = SetForceExterior; + vtab->GetForceExterior = GetForceExterior; + vtab->TestForceExterior = TestForceExterior; + + vtab->ClearInvisible = ClearInvisible; + vtab->SetInvisible = SetInvisible; + vtab->GetInvisible = GetInvisible; + vtab->TestInvisible = TestInvisible; + vtab->ClearBorder = ClearBorder; + vtab->SetBorder = SetBorder; + vtab->GetBorder = GetBorder; + vtab->TestBorder = TestBorder; + vtab->ClearInk = ClearInk; + vtab->SetInk = SetInk; + vtab->GetInk = GetInk; + vtab->TestInk = TestInk; + vtab->ClearClipOp = ClearClipOp; + vtab->SetClipOp = SetClipOp; + vtab->GetClipOp = GetClipOp; + vtab->TestClipOp = TestClipOp; + vtab->ClearClip = ClearClip; + vtab->SetClip = SetClip; + vtab->GetClip = GetClip; + vtab->TestClip = TestClip; + vtab->ClearGrf = ClearGrf; + vtab->SetGrf = SetGrf; + vtab->GetGrf = GetGrf; + vtab->TestGrf = TestGrf; + vtab->ClearDrawTitle = ClearDrawTitle; + vtab->SetDrawTitle = SetDrawTitle; + vtab->GetDrawTitle = GetDrawTitle; + vtab->TestDrawTitle = TestDrawTitle; + vtab->ClearLabelUp = ClearLabelUp; + vtab->SetLabelUp = SetLabelUp; + vtab->GetLabelUp = GetLabelUp; + vtab->TestLabelUp = TestLabelUp; + vtab->ClearLogPlot = ClearLogPlot; + vtab->SetLogPlot = SetLogPlot; + vtab->GetLogPlot = GetLogPlot; + vtab->TestLogPlot = TestLogPlot; + vtab->ClearLogTicks = ClearLogTicks; + vtab->SetLogTicks = SetLogTicks; + vtab->GetLogTicks = GetLogTicks; + vtab->TestLogTicks = TestLogTicks; + vtab->ClearLogLabel = ClearLogLabel; + vtab->SetLogLabel = SetLogLabel; + vtab->GetLogLabel = GetLogLabel; + vtab->TestLogLabel = TestLogLabel; + vtab->ClearDrawAxes = ClearDrawAxes; + vtab->SetDrawAxes = SetDrawAxes; + vtab->GetDrawAxes = GetDrawAxes; + vtab->TestDrawAxes = TestDrawAxes; + vtab->ClearAbbrev = ClearAbbrev; + vtab->SetAbbrev = SetAbbrev; + vtab->GetAbbrev = GetAbbrev; + vtab->TestAbbrev = TestAbbrev; + vtab->ClearEscape = ClearEscape; + vtab->SetEscape = SetEscape; + vtab->GetEscape = GetEscape; + vtab->TestEscape = TestEscape; + vtab->ClearLabelling = ClearLabelling; + vtab->SetLabelling = SetLabelling; + vtab->GetLabelling = GetLabelling; + vtab->TestLabelling = TestLabelling; + vtab->ClearMajTickLen = ClearMajTickLen; + vtab->SetMajTickLen = SetMajTickLen; + vtab->GetMajTickLen = GetMajTickLen; + vtab->TestMajTickLen = TestMajTickLen; + vtab->ClearLogGap = ClearLogGap; + vtab->SetLogGap = SetLogGap; + vtab->GetLogGap = GetLogGap; + vtab->TestLogGap = TestLogGap; + vtab->ClearTitleGap = ClearTitleGap; + vtab->SetTitleGap = SetTitleGap; + vtab->GetTitleGap = GetTitleGap; + vtab->TestTitleGap = TestTitleGap; + vtab->ClearMinTickLen = ClearMinTickLen; + vtab->SetMinTickLen = SetMinTickLen; + vtab->GetMinTickLen = GetMinTickLen; + vtab->TestMinTickLen = TestMinTickLen; + vtab->ClearNumLabGap = ClearNumLabGap; + vtab->SetNumLabGap = SetNumLabGap; + vtab->GetNumLabGap = GetNumLabGap; + vtab->TestNumLabGap = TestNumLabGap; + vtab->ClearTextLabGap = ClearTextLabGap; + vtab->SetTextLabGap = SetTextLabGap; + vtab->GetTextLabGap = GetTextLabGap; + vtab->TestTextLabGap = TestTextLabGap; + vtab->ClearLabelAt = ClearLabelAt; + vtab->SetLabelAt = SetLabelAt; + vtab->GetLabelAt = GetLabelAt; + vtab->TestLabelAt = TestLabelAt; + vtab->ClearCentre = ClearCentre; + vtab->SetCentre = SetCentre; + vtab->GetCentre = GetCentre; + vtab->TestCentre = TestCentre; + vtab->ClearGap = ClearGap; + vtab->SetGap = SetGap; + vtab->GetGap = GetGap; + vtab->TestGap = TestGap; + vtab->ClearEdge = ClearEdge; + vtab->SetEdge = SetEdge; + vtab->GetEdge = GetEdge; + vtab->TestEdge = TestEdge; + vtab->ClearNumLab = ClearNumLab; + vtab->SetNumLab = SetNumLab; + vtab->GetNumLab = GetNumLab; + vtab->TestNumLab = TestNumLab; + vtab->ClearMinTick = ClearMinTick; + vtab->SetMinTick = SetMinTick; + vtab->GetMinTick = GetMinTick; + vtab->TestMinTick = TestMinTick; + vtab->ClearTextLab = ClearTextLab; + vtab->SetTextLab = SetTextLab; + vtab->GetTextLab = GetTextLab; + vtab->TestTextLab = TestTextLab; + vtab->ClearLabelUnits = ClearLabelUnits; + vtab->SetLabelUnits = SetLabelUnits; + vtab->GetLabelUnits = GetLabelUnits; + vtab->TestLabelUnits = TestLabelUnits; + vtab->ClearStyle = ClearStyle; + vtab->SetStyle = SetStyle; + vtab->GetStyle = GetStyle; + vtab->TestStyle = TestStyle; + vtab->ClearFont = ClearFont; + vtab->SetFont = SetFont; + vtab->GetFont = GetFont; + vtab->TestFont = TestFont; + vtab->ClearColour = ClearColour; + vtab->SetColour = SetColour; + vtab->GetColour = GetColour; + vtab->TestColour = TestColour; + vtab->ClearWidth = ClearWidth; + vtab->SetWidth = SetWidth; + vtab->GetWidth = GetWidth; + vtab->TestWidth = TestWidth; + vtab->ClearSize = ClearSize; + vtab->SetSize = SetSize; + vtab->GetSize = GetSize; + vtab->TestSize = TestSize; + vtab->GetGrfContext = GetGrfContext; + +/* Save the inherited pointers to methods that will be extended, and replace + them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + fset = (AstFrameSetVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_removeframe = fset->RemoveFrame; + fset->RemoveFrame = RemoveFrame; + +/* Declare the destructor and copy constructor. */ + astSetDelete( (AstObjectVtab *) vtab, Delete ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + +/* Declare the class dump function. */ + astSetDump( vtab, Dump, "Plot", "Provide facilities for 2D graphical output" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int Inside( int n, float *cx, float *cy, float x, float y, int *status ){ +/* +* Name: +* Inside + +* Purpose: +* See if a given point is inside a 2-d polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Inside( int n, float *cx, float *cy, float x, float y, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function determines if the position given by x and y, is inside +* or outside the polygon specified by the vertices given in arrays cx +* and cy. + +* Parameters: +* n +* The number of vertices in the polygon. +* cx +* A pointer to an array holding the x coordinates at the "n" vertices. +* cy +* A pointer to an array holding the y coordinates at the "n" vertices. +* x +* The x coordinate of the test point. +* y +* The y coordinate of the test point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A boolean flag indicating if the test point is inside the polygon. + +* Notes: +* - The algorithm used only works for convex polygons. +* - The polygon is closed by joining the last vertex to the first +* vertex. +* - Zero is returned if an error has occurred. + +*/ + +/* Local Variables: */ + int i; /* Index of the current vertex */ + int j; /* Index of the next vertex */ + int ret; /* Is the test point inside the polygon? */ + int sgn; /* Which side of the first edge is the test point? */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise the returned value to indicate that the point is inside the + box. */ + ret = 1; + +/* Get the sign of the angle between the vector joining vertex 1 to vertex + 0, and the vector joining the test point to vertex zero. */ + if( ( cx[ 1 ] - cx[ 0 ] )*( y - cy[ 0 ] ) > + ( x - cx[ 0 ] )*( cy[ 1 ] - cy[ 0 ] ) ){ + sgn = 1; + } else { + sgn = -1; + } + +/* Check that the remaining test point is on the same side of the remaining + sides. */ + for( i = 1; i < n; i++ ){ + +/* Get the index of the next vertex, joining the last vertex up with + vertex zero. */ + j = i + 1; + if( j >= n ) j -= n; + +/* Get the sign of the angle between the vector joining vertex j to vertex + i, and the vector joining the test point to vertex i. If the sign is + opposite to that found for vertex zero, then the test point is outside + the polygon. Break out of the loop if this is the case. */ + if( ( cx[ j ] - cx[ i ] )*( y - cy[ i ] ) > + ( x - cx[ i ] )*( cy[ j ] - cy[ i ] ) ){ + + if( sgn == -1 ) { + ret = 0; + break; + } + + } else { + + if( sgn == 1 ) { + ret = 0; + break; + } + + } + + } + + +/* Return the answer. */ + return ret; + +} + +static void InterpEscape( AstPlot *this, int type, double value, float *x, + float *y, float ux, float uy, float rx, float ry, + const char *just, float *rise, double nsize, + double nstyle, double nwidth, double ncol, + double nfont, const char *method, const char *class, int *status ){ +/* +* Name: +* InterpEscape + +* Purpose: +* Prepare things for drawing the next part of a string which includes +* graphics escape sequences. + +* Synopsis: +* #include "plot.h" +* void InterpEscape( AstPlot *this, int type, double value, float *x, +* float *y, float ux, float uy, float rx, float ry, +* const char *just, float *rise, double nsize, +* double nstyle, double nwidth, double ncol, +* double nfont, const char *method, const char *class, int *status ) + +* Description: +* This function modifies the current graphics attributes, the supplied +* reference position, in preparation for drawing another sub-string +* from a string containing graphics escape sequences. The type and +* value of an escape sequence preceding the substring is supplied. +* Note, this function ignored escape sequences which represent an +* escaped percent sign. Such escape sequences are drawn as normal +* text by the claling function. + +* Parameters: +* this +* The plot. +* type +* The type of escape sequence. Each type is identified by a symbolic +* constant defined in grf.h. +* value +* The value associated with the escape sequence. All usable values +* will be positive. A value of -1 shold be supplied if the attribute +* identified by "type" should be reset to its "normal" value (as +* established using the astGAttr function, etc). +* x +* Pointer to a double holding the x coordinate at the concatenation +* point. This will be modified on exit if the escape sequence +* requires it. +* y +* Pointer to a double holding the y coordinate at the concatenation +* point. This will be modified on exit if the escape sequence +* requires it. +* ux +* The x component of the up-vector for the text, in graphics coords. +* The length of this vector should be equal to the height of normal +* text drawn with this up-vector. +* uy +* The y component of the up-vector for the text. See "ux". +* rx +* The x component of the right-vector for the text. The length of this +* vector should be equal to the height of normal text drawn with the +* supplied up-vector. +* ry +* The y component of the right-vector for the text. see "rx". +* just +* The justification being used for each substring. +* rise +* Pointer to a float holding the height of the current baseline +* above the normal baseline, given as a percentage of the height of +* normal text. May be negative for sub-scripts. May be modified on +* exit if the escape sequence effects the height of the baseline. +* nsize +* The size of normal text. +* nstyle +* The style of normal text. +* nwidth +* The width of normal text. +* ncol +* The colour of normal text. +* nfont +* The font of normal text. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + float new_rise; + float t1, t2; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Type GRF__ESSUP: Move the concatenation point in the up direction, and + update the current baseline height. If the value is -1, then reset the + baseline to its normal height. */ + if( type == GRF__ESSUP ) { + if( value > 0 ) { + *x += 0.01*ux*( value - *rise ); + *y += 0.01*uy*( value - *rise ); + *rise = value; + } else { + *x -= 0.01*ux*(*rise); + *y -= 0.01*uy*(*rise); + *rise = 0; + } + +/* Type GRF__ESSUB: Move the concatenation point in the down direction, and + update the current baseline height. If the value is -1, then reset the + baseline to its normal height. */ + } else if( type == GRF__ESSUB ) { + if( value > 0 ) { + *x -= 0.01*ux*( value + *rise ); + *y -= 0.01*uy*( value + *rise ); + *rise = -value; + } else { + *x -= 0.01*ux*(*rise); + *y -= 0.01*uy*(*rise); + *rise = 0; + } + +/* Type GRF__ESGAP: Move the concatenation point to the right. */ + } else if( type == GRF__ESGAP ) { + *x += 0.01*rx*value; + *y += 0.01*ry*value; + +/* Type GRF__ESBAC: Move the concatenation point to the left. */ + } else if( type == GRF__ESBAC ) { + *x -= 0.01*rx*value; + *y -= 0.01*ry*value; + +/* Remember the current horizontal position. */ + } else if( type == GRF__ESH ) { + this->hmarkx = *x; + this->hmarky = *y; + +/* Go to the previously stored horizontal position. */ + } else if( type == GRF__ESG ) { + if( this->hmarkx != FLT_MAX && this->hmarky != FLT_MAX ) { + t1 = ( *x - this->hmarkx )*rx + ( *y - this->hmarky )*ry; + t2 = rx*rx + ry*ry; + *x -= rx*t1/t2; + *y -= ry*t1/t2; + } + +/* Type GRF__ESSIZ: Change the text size. */ + } else if( type == GRF__ESSIZ ) { + if( value < 0 ) value = 100.0; + GAttr( this, GRF__SIZE, 0.01*value*nsize, NULL, GRF__TEXT, + method, class, status ); + +/* Type GRF__ESWID: Change the text width. */ + } else if( type == GRF__ESWID ) { + if( value < 0 ) value = 100.0; + GAttr( this, GRF__WIDTH, 0.01*value*nwidth, NULL, GRF__TEXT, + method, class, status ); + +/* Type GRF__ESFON: Change the text font. */ + } else if( type == GRF__ESFON ) { + if( value < 0 ) value = nfont; + GAttr( this, GRF__FONT, value, NULL, GRF__TEXT, method, class, status ); + +/* Type GRF__ESCOL: Change the text colour. */ + } else if( type == GRF__ESCOL ) { + if( value < 0 ) value = ncol; + GAttr( this, GRF__COLOUR, value, NULL, GRF__TEXT, method, class, status ); + +/* Type GRF__ESSTY: Change the text style. */ + } else if( type == GRF__ESSTY ) { + if( value < 0 ) value = nstyle; + GAttr( this, GRF__STYLE, value, NULL, GRF__TEXT, method, class, status ); + +/* Type GRF__ESPSH: Push current attributes onto a stack. */ + } else if( type == GRF__ESPSH ) { + PushGat( this, *rise, method, class, status ); + +/* Type GRF__ESSTY: Reset everything to normal. */ + } else if( type == GRF__ESPOP ) { + + if( !PopGat( this, &new_rise, method, class, status ) ) { + new_rise = 0.0; + GAttr( this, GRF__SIZE, nsize, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__WIDTH, nwidth, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__COLOUR, ncol, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__FONT, nfont, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__STYLE, nstyle, NULL, GRF__TEXT, method, class, status ); + } + + *x -= 0.01*ux*( *rise - new_rise ); + *y -= 0.01*uy*( *rise - new_rise ); + *rise = new_rise; + + } +} + +static int IsASkyAxis( AstFrame *frm, int axis, int *status ) { +/* +* Name: +* IsASkyAxis + +* Purpose: +* Checks if a specified axis of the supplied Frame is a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int IsASkyAxis( AstFrame *frm, int axis, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function checks if if a specified axis of the supplied Frame is +* a SkyAxis. + +* Parameters: +* frm +* The Frame. +* axis +* The zero-based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A boolean flag indicating if the axis is a SkyAxis. + +*/ + +/* Local Variables: */ + int ret; + AstAxis *ax; + +/* initialise */ + ret = 0; + +/* Check the global status. */ + if( !astOK ) return ret; + +/* Extract the required axis from the Frame and test if it is a SkyAxis. + Then free the axis memory. */ + ax = astGetAxis( frm, axis ); + ret = astIsASkyAxis( ax ); + ax = astAnnul( ax ); + +/* Return the answer. */ + return ret; + +} + +static int IsASkyFrame( AstObject *obj, int *status ) { +/* +* Name: +* IsASkyFrame + +* Purpose: +* Checks if the supplied Object ca be treated as a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int IsASkyFrame( AstObject *obj, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function checks if the supplied Object is a SkyFrame or a +* FrameSet which has a SkyFrame as its current Frame. + +* Parameters: +* obj +* The object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A boolean flag indicating if the Object can be treated as SkyFrame. + +*/ + +/* Local Variables: */ + int ret; + AstFrame *frm; + +/* initialise */ + ret = 0; + +/* Check the global status. */ + if( !astOK ) return ret; + +/* If the Object is a SkyFrame, return 1. */ + if( astIsASkyFrame( obj ) ) { + ret = 1; + +/* If the Object is a FrameSet, check its current Frame recursively, + using this method. */ + } else if( astIsAFrameSet( obj ) ) { + frm = astGetFrame( (AstFrameSet *) obj, AST__CURRENT ); + ret = IsASkyFrame( (AstObject *) frm, status ); + frm = astAnnul( frm ); + } + +/* Return the answer. */ + return ret; + +} + +static const char *JustMB( AstPlot *this, int esc, const char *text, float *x, + float *y, float upx, float upy, const char *just, + float uxu, float uyu, float rxu, float ryu, + float *x0, float *y0, const char *method, + const char *class, int *status ){ +/* +* Name: +* JustMB + +* Purpose: +* Modifies a "M" vertical reference point to be a "B" reference point, +* if necessary. + +* Synopsis: +* #include "plot.h" +* const char *JustMB( AstPlot *this, int esc, const char *text, float *x, +* float *y, float upx, float upy, const char *just, +* float uxu, float uyu, float rxu, float ryu, +* float *x0, float *y0, const char *method, +* const char *class, int *status ) + +* Description: +* This function is used to modify the reference point and justification +* of a string by converting the vertical "M" justification option (which +* indicates that the reference point refers to the bottom of the +* bounding box) into a corresponding "B" option (which indicates that +* the reference point refers to the text baseline). The reference +* point is modified accordingly. +* +* This is only done if the grf module does not support "M" +* justification. Otherwise, the supplied justification string and +* reference point coordinates are returned unchanged. + +* Parameters: +* this +* The plot. +* esc +* Should escape sequences be interpreted? They will be printed +* literally otherwise. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* Pointer to a double holding the x coordinate at the reference +* point. This is modified on exit if the supplied "just" string +* indicates that the supplied value refers to the bottom of the +* bounding box, and the grf module does not support such +* justification. In this case, the returned value is a point on +* the baseline of the text which would result in the bottom of +* the bounding box being at the supplied position. +* y +* Pointer to a double holding the y coordinate at the reference +* point. This is modified on exit if the supplied "just" string +* indicates that the supplied value refers to the bottom of the +* bounding box, and the grf module does not support such +* justification. In this case, the returned value is a point on +* the baseline of the text which would result in the bottom of +* the bounding box being at the supplied position. +* upx +* The x component of the up-vector for the text. Positive values +* always refer to displacements from left to right on the screen, +* even if the graphics x axis increases in the opposite sense. +* upy +* The y component of the up-vector for the text. Positive values +* always refer to displacements from left to right on the screen, +* even if the graphics y axis increases in the opposite sense. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", 'B' for "baseline" or "M" for "bottom", and +* specifies the vertical location of the reference position. Note, +* "baseline" corresponds to the base-line of normal text,and "M" +* corresponds to the bottom of the bounding box. Some characters +* (eg "y", "g", "p", etc) and sub-scripts descend below the base-line. +* The second character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* uxu +* X component of normalised up-vector, in graphics coords. +* uyu +* Y component of normalised up-vector, in graphics coords. +* rxu +* X component of normalised right-vector, in graphics coords. +* ryu +* Y component of normalised right-vector, in graphics coords. +* x0 +* Address of a float at which to return the x coordinate at the +* left end of the baseline of the whole string. +* y0 +* Address of a float at which to return the y coordinate at the +* left end of the baseline of the whole string. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated string which contains the +* justification to use in future. This pointer should be freed using +* astFree when no longer needed. This string will contain a full +* upper-case justification string which can be used by the current +* grf module. + +* Notes; +* - NULL is returned if an error has occurred. + +*/ + +/* Local Variables: */ + char cc; + float drop; + float dx; + float dy; + float f; + float height; + float width; + float xbn[ 4 ]; + float ybn[ 4 ]; + int called; + char *result; + +/* Check inherited status */ + if( !astOK ) return NULL; + +/* Allocate memory for the returned string. */ + result = astMalloc( sizeof( char )*3 ); + if( astOK ) { + +/* Fill in any missing parts of the justification string. */ + if( just ){ + cc = toupper( just[ 0 ] ); + result[ 0 ] = ( cc == 'T' || cc == 'C' || cc == 'B' || cc == 'M' ) ? cc : 'C'; + + cc = toupper( just[ 1 ] ); + result[ 1 ] = ( cc == 'L' || cc == 'C' || cc == 'R' ) ? cc : 'C'; + + } else { + result[ 0 ] = 'C'; + result[ 1 ] = 'C'; + } + + result[ 2 ] = 0; + +/* Indicate that DrawText has not been called. */ + called = 0; + +/* The justfication need not be changed unless the requested vertical + justification is "bottom" (m), AND the grf module does not support "M" + justification. */ + if( ( result[ 0 ] == 'M' ) && !GCap( this, GRF__MJUST, 1, status ) ){ + +/* Find the bounding box which would result from putting the left end of + the baseline at the specified position. */ + DrawText( this, 0, esc, text, *x, *y, "BL", upx, upy, xbn, ybn, + &drop, method, class, status ); + +/* Indicate that DrawText has not been called. */ + called = 1; + +/* Get the vector from the bottom left corner of the bounding box to the + reference point (on the base-line), and add this vector on to the reference + point. */ + *x += *x - xbn[ 0 ]; + *y += *y - ybn[ 0 ]; + +/* Modified the returned justification string. */ + result[ 0 ] = 'B'; + } + +/* If the returned justification is "BL", then the left end of the + baseline is just the returned reference point. */ + if( result[ 0 ] == 'B' && result[ 1 ] == 'L' ) { + *x0 = *x; + *y0 = *y; + +/* Otherwise, we work out the coords of the left end of the baseline from + the values returned by DrawText above. Call DrawText now if it was not + called above. */ + } else { + if( ! called ) { + DrawText( this, 0, esc, text, *x, *y, "BL", upx, upy, xbn, ybn, + &drop, method, class, status ); + } + +/* Find the height and width of the bounding box. */ + dx = xbn[ 0 ] - xbn[ 3 ]; + dy = ybn[ 0 ] - ybn[ 3 ]; + width = sqrt( dx*dx + dy*dy ); + + dx = xbn[ 0 ] - xbn[ 1 ]; + dy = ybn[ 0 ] - ybn[ 1 ]; + height = sqrt( dx*dx + dy*dy ); + +/* For "C" and "R" horizontal justification we first need to move the + returned reference point left by 0.5 or 1.0 times the width of the whole + string respectively. */ + if( result[ 1 ] == 'C' ) { + f = 0.5; + + } else if( result[ 1 ] == 'R' ) { + f = 1.0; + + } else { + f = 0.0; + } + + f *= width; + + *x0 = *x - f*rxu; + *y0 = *y - f*ryu; + +/* Unless the vertical justification is "B", we also need to move the + concatenation point vertically to put it on the baseline. */ + if( result[ 0 ] == 'T' ) { + f = height - drop; + + } else if( result[ 0 ] == 'C' ) { + f = 0.5*height - drop; + + } else if( result[ 0 ] == 'M' ) { + f = -drop; + + } else { + f = 0.0; + } + + *x0 -= f*uxu; + *y0 -= f*uyu; + } + } + +/* Return the result. */ + return result; +} + +static int Labelat( AstPlot *this, TickInfo **grid, AstPlotCurveData **cdata, + double *labelat, const char *method, const char *class, + int *status ){ +/* +* +* Name: +* Labelat + +* Purpose: +* Determine the other axis value at which to place interior labels +* and tick marks. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Labelat( AstPlot *this, TickInfo **grid, AstPlotCurveData **cdata, +* double *labelat, const char *method, const char *class ) + +* Class Membership: +* Plot member function. + +* Description: +* If tick marks and labels are to be placed within the plotting area, +* the tick values stored in "grid" determine their position on one +* axis, and their position on the other axis is determined by this +* function. If a value has been set for the "LabelAt" attribute, then +* it is used, otherwise the "other axis" value on the longest curve +* parallel to the "other axis" is used (although the curve "other axis +* = zero" is used if it passes through the plotting area and is not too +* short). The effective length assigned to each curve is reduced in +* proportion to the number of tick marks which are close to the edge +* of the plotting area. +* +* A flag is returned indicating if the two axes appear to be +* independent, in which case there is nothing to be gained by using +* interior labelling. + +* Parameters: +* this +* A pointer to the Plot. +* grid +* A pointer to an array of two TickInfo pointers (one for each axis), +* each pointing to a TickInfo structure holding information about +* tick values on the axis. See function GridLines. +* cdata +* A pointer to an array of two AstPlotCurveData pointers (one for each axis), +* each pointing to an array of AstPlotCurveData structure (one for each +* major tick value on the axis), holding information about breaks +* in the curves drawn to mark the major tick values. See function +* DrawGrid. +* labelat +* A pointer to a 2 element array in which to store the constant axis +* values at which tick marks are put. Element 0 is returned holding +* the axis 1 value at which tick marks for axis 0 are placed. Element +* 1 is returned holding the axis 0 value at which tick marks for axis +* 1 are placed. +* flags +* A pointer to a 2 element array. Each element is a flag which is +* returned non-zero if the corresponding axis +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. + +* Returned Value: +* Zero if and only if the lines of constant axis value are all the +* same length for all tick marks on either axis. If this is so, using +* interior labelling will not enable any more major ticks to be +* drawn, and so there is no reason to switch to interior labelling (unless +* the user has specifically requested interior labelling). + +* Notes: +* - This function assumes the current Frame of the Plot is 2 +* dimensional, and it should not be called if this is not the case. +*/ + +/* Local Variables: */ + AstMapping *mapping; /* Mapping from graphics to physical coords */ + AstPointSet *pset2; /* Pointset for graphical tick positions */ + AstPointSet *pset[ 2 ];/* Pointsets for physical tick positions */ + AstPlotCurveData *cdt; /* Pointer to the AstPlotCurveData for the next tick */ + TickInfo *info; /* Pointer to the TickInfo for the current axis */ + double **ptr2; /* Pointers to graphics pointset data */ + double *ptr1[ 2 ]; /* Pointers to physical pointset data */ + double *tvals[ 2 ]; /* Pointers to arrays of other axis values */ + double *value; /* Current tick value */ + double efflen; /* Effective length of current curve */ + double lim; /* Largest insignificant axis value */ + double margin; /* Width of margin around plotting area */ + double maxlen; /* Effective length of longest curve */ + double minlen; /* Effective length of shortest (non-zero) curve */ + double x; /* Tick X value */ + double xhi; /* Upper limit on acceptable X range */ + double xlo; /* Lower limit on acceptable X range */ + double y; /* Tick Y value */ + double yhi; /* Upper limit on acceptable Y range */ + double ylo; /* Lower limit on acceptable Y range */ + double zerolen; /* Effective length of curve for other axis = 0.0 */ + int axis; /* Current axis index */ + int i; /* Tick index for this axis */ + int nin; /* No. of counted ticks */ + int result; /* Does interior labelling allow more ticks to be drawn? */ + int tick; /* Tick index */ + +/* Check the global status. */ + if( !astOK ) return 0; + +/* Initialise */ + result = 1; + +/* Create two PointSets to hold a set of tick mark positions along each + axis. The values on "axis" will be taken from the info structure. For + each axis create an array to hold values for the "other" axis. */ + for( axis = 0; axis < 2; axis++ ){ + info = grid[ axis ]; + pset[ axis ] = astPointSet( info->nmajor, 2, "", status ); + tvals[ axis ] = (double *) astMalloc( sizeof(double)*(size_t)(info->nmajor) ); + } + +/* Get the mapping from Base (graphics) frame the Current (physical) */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Get the bounds of the area in which tick marks must occur to be + counted. This is the total plotting area minus a 5% margin at each + edge. */ + margin = 0.05*( this->xhi - this->xlo ); + xlo = this->xlo + margin; + xhi = this->xhi - margin; + + margin = 0.05*( this->yhi - this->ylo ); + ylo = this->ylo + margin; + yhi = this->yhi - margin; + +/* Do each axis. */ + for( axis = 0; axis < 2 && astOK; axis++ ){ + +/* Find the longest and shortest curves parallel to the axis being labelled. + Also find the length of the curve which passes through the origin of the + other axis which is within the plotting area. We need to do this even + if LabelAt has been set since we need to calculate the returned flag. */ + +/* Store pointers to the arrays holding tick mark physical coordinates, + and set these in the PointSet. */ + ptr1[ axis ] = grid[ axis ]->ticks; + ptr1[ 1 - axis ] = tvals[ axis ]; + astSetPoints( pset[ axis ], ptr1 ); + +/* Get a pointer to the structure containing information describing the + positions of the major tick marks along the other axis. */ + info = grid[ 1 - axis ]; + +/* Get a pointer to the other axis value at the first other axis major tick + mark. */ + value = info->ticks; + +/* Get a limit on absolute magnitude for an axis value to be consider + equal to zero. */ + lim = 1.0E-6*fabs( value[ 1 ] - value [ 0 ] ); + +/* Get a pointer to the structure containing information describing the + breaks in the curve which passes through the first major tick mark. */ + cdt = cdata[ 1 - axis ]; + +/* Initialise the effective length of the longest and shortest curves, and + the curve passing through the origin. */ + maxlen = -1.0; + minlen = DBL_MAX; + zerolen = 0.0; + labelat[ axis ] = AST__BAD; + +/* Loop round each of the major tick marks on the other axis. */ + for( tick = 0; tick < info->nmajor && astOK; tick++ ){ + +/* Fill the array of other axis values with the current other axis value. */ + for( i = 0; i < grid[ axis ]->nmajor; i++ ){ + tvals[ axis ][ i ] = *value; + } + +/* Transform the tick positions from the current frame (i.e. physical + coordinates) to the base frame (i.e. graphics coordinates) using + the inverse Mapping. */ + pset2 = Trans( this, NULL, mapping, pset[ axis ], 0, NULL, 0, + method, class, status ); + +/* Get pointers to the graphics coordinates. */ + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + +/* Count the number of graphics positions which are well within the plotting + area. */ + nin = 0; + for( i = 0; i < grid[ axis ]->nmajor; i++ ){ + x = ptr2[ 0 ][ i ]; + y = ptr2[ 1 ][ i ]; + if( x != AST__BAD && x > xlo && x < xhi && + y != AST__BAD && y > ylo && y < yhi ) nin++; + } + +/* Find the effective length of this curve.*/ + efflen = sqrt( (float) nin )*cdt->length; + +/* If the curve through this tick mark has a greater effective length than any + other found so far, record it. */ + if( efflen > maxlen ){ + maxlen = efflen; + labelat[ axis ] = *value; + } + +/* If the curve through this tick mark has a smaller non-zero effective length + than any other found so far, record it. */ + if( efflen < minlen && efflen > 0.0 ) minlen = efflen; + +/* If this tick mark is at the origin, note the effective length. */ + if( fabs( *value ) <= lim ) zerolen = efflen; + +/* Get a pointer to the curve through the next major tick mark. */ + cdt++; + +/* Get a pointer to the axis value at the next major tick mark. */ + value++; + + } + +/* Free resources. */ + pset2 = astAnnul( pset2 ); + } + +/* Use the curve through the origin unless it is significantly shorter + than the longest curve. */ + if( zerolen > 0.4*maxlen ) labelat[ axis ] = 0.0; + +/* Return a flag if the lengths of the shortest and longest curves are nearly + equal. */ + if( ( maxlen - minlen )/( maxlen + minlen ) < 1.0E-5 ) result = 0; + +/* If the LabelAt attribute has been set, use it in preference to the + value found above. */ + if( astTestLabelAt( this, axis ) ){ + labelat[ axis ] = astGetLabelAt( this, axis ); + } + } + +/* Release resources. */ + for( axis = 0; axis < 2; axis++ ){ + if( pset[ axis ] ) pset[ axis ] = astAnnul( pset[ axis ] ); + if( tvals[ axis ] ) tvals[ axis ] = (double *) astFree( (void *) tvals[ axis ] ); + } + mapping = astAnnul( mapping ); + +/* Return. */ + return result; + +} + +static void Labels( AstPlot *this, TickInfo **grid, AstPlotCurveData **cdata, + double *gap, double *labelat, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* Labels + +* Purpose: +* Draw numerical axis labels for a 2-D annotated coordinate grid. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Labels( AstPlot *this, TickInfo **grid, AstPlotCurveData **cdata, +* double *gap, double *labelat, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* The policy for placing labels for the major tick values is broadly as +* follows: if possible, labels for a given physical axis are placed on +* one edge of the plotting area, at the place where the curve for a +* major tick value crosses the edge. If very few of the curves cross +* the edge, then the label for a curve is placed at the intersection +* of that curve with the longest of the curves representing the major +* tick values on the other axis. + +* Parameters: +* this +* A pointer to the Plot. +* grid +* A pointer to an array of two TickInfo pointers (one for each axis), +* each pointing to a TickInfo structure holding information about +* tick values on the axis. See function GridLines. +* cdata +* A pointer to an array of two AstPlotCurveData pointers (one for each axis), +* each pointing to an array of AstPlotCurveData structure (one for each +* major tick value on the axis), holding information about breaks +* in the curves drawn to mark the major tick values. See function +* DrawGrid. +* gap +* Pointer to array of two values holding the gap between major +* tick values on the two axes. +* labelat +* A pointer to a 2 element array holding the constant axis +* values at which tick marks are put. Element 0 should hold +* the axis 1 value at which tick marks for axis 0 are placed. Element +* 1 should hold the axis 0 value at which tick marks for axis +* 1 are placed. If labels are to be placed round the edges of the +* plotting zone instead of within the plotting zone, then values of +* AST__BAD should be supplied. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function assumes the current Frame of the Plot is 2 +* dimensional, and it should not be called if this is not the case. +*/ + +/* Local Variables: */ + AstFrame *frame; /* Pointer to current Frame */ + AstMapping *mapping; /* Pointer to graphics->physical Mapping */ + AstPointSet *pset1; /* Pointer to PointSet holding physical coords. */ + AstPointSet *pset2; /* Pointer to PointSet holding graphics coords. */ + AstPlotCurveData *cdt; /* Pointer to the AstPlotCurveData for the next tick */ + LabelList *labellist; /* Pointer to list of labels to be plotted */ + LabelList *ll; /* Pointer to next label to be plotted */ + TickInfo *info; /* Pointer to the TickInfo for the current axis */ + char just_buf[3]; /* Buffer to hold a justification string */ + const char *just; /* Justification string */ + const char *text; /* Pointer to label text */ + double *used; /* Pointer to list of used label values */ + double *value; /* Current tick value */ + double diff; /* Difference between adjacent major tick marks */ + double dx; /* Text base-line X component */ + double dy; /* Text base-line Y component */ + double gx; /* Reference position graphics X coord. */ + double gy; /* Reference position graphics Y coord. */ + double mindim; /* Shortest dimension of plotting area */ + double offx; /* X component of offset vector */ + double offy; /* Y component of offset vector */ + double rlen; /* Length of perpendicular vector */ + double rx; /* X comp of vector perpendicular to (dx,dy) */ + double ry; /* Y comp of vector perpendicular to (dx,dy) */ + double sin45; /* Sine of 45 degrees */ + double txtgap; /* Absolute gap between labels and edges */ + double upx; /* Text up-vector X component */ + double upy; /* Text up-vector Y component */ + double val[ 2 ]; /* Physical coordinates */ + float *box; /* Pointer to array of label bounding boxes */ + float alpha; /* Factor to convert graphics X to equal scaled X */ + float beta; /* Factor to convert graphics Y to equal scaled Y */ + int axis; /* Current axis index */ + int esc; /* Interpret escape sequences? */ + int flag; /* Flag indicating which way the base-vector points */ + int iused; /* Index into list of used axis values */ + int last; /* The index of the last tick to use */ + int logticks; /* ARe major ticks spaced logarithmically? */ + int nlab; /* The number of labels to be plotted */ + int nused; /* Number of used axis values */ + int t0; /* Index of central tick */ + int tick; /* Current tick index */ + int tinc; /* Increment between ticks */ + int upfree; /* Are we free to change the up-vector? */ + int gelid; /* ID for next graphical element to be drawn */ + +/* Check the global status. */ + if( !astOK ) return; + +/* See if escape sequences in text strings are to be interpreted */ + esc = astGetEscape( this ); + +/* Empty the list of bounding boxes kept by the Overlap function. */ + (void) Overlap( this, 0, 0, NULL, 0.0, 0.0, NULL, 0.0, 0.0, NULL, + method, class, status ); + +/* If required, draw the labels around the edges of the plotting area. */ + if( labelat[ 0 ] == AST__BAD || labelat[ 1 ] == AST__BAD ){ + (void) EdgeLabels( this, 1, grid, cdata, 1, method, class, status ); + +/* Otherwise, draw labels within the interior of the plotting area. */ + } else { + +/* Find the scale factors for the two axes which scale graphics coordinates + into a "standard" equal scaled coordinate system in which: 1) the axes + have equal scale in terms of (for instance) millimetres per unit distance, + 2) X values increase from left to right, 3) Y values increase from bottom + to top. */ + GScales( this, &alpha, &beta, method, class, status ); + +/* Get the minimum dimension of the plotting area in equal scaled coords. */ + mindim = astMIN( fabs( alpha*(this->xhi - this->xlo) ), + fabs( beta*(this->yhi - this->ylo) ) ); + +/* Store a value for the sine of 45 degrees. */ + sin45 = 1.0/sqrt( 2.0 ); + +/* Initialise the pointer to the memory holding the bounding boxes for + all labels (used by function Overlap). */ + box = NULL; + +/* Get a pointer to the current Frame in the Plot. */ + frame = astGetFrame( this, AST__CURRENT ); + +/* Get a pointer to the mapping form the base Frame to the current Frame in + the Plot. */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Initialize the id value for graphical element being drawn. */ + gelid = AST__NUMLAB1_ID; + +/* Do each axis. */ + for( axis = 0; axis < 2; axis++ ){ + +/* See of major ticks are spaced logarithmically on this axis. */ + logticks = astGetLogTicks( this, axis ); + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, gelid, 1, GRF__TEXT, method, class ); + +/* Get a pointer to the structure containing information describing the + positions of the major tick marks along this axis. */ + info = grid[ axis ]; + +/* Only progress if there are some labels stored within the structure. */ + if( info->labels ) { + +/* Initialise the pointer to the list of text strings to be drawn. */ + labellist = NULL; + nlab = 0; + +/* See if numerical labels are always to be drawn horizontal. If so, set + a flag and initialise a vertical up-vector. */ + if( astGetLabelUp( this, axis ) ){ + upfree = 0; + upx = 0.0; + upy = 1.0; + +/* Otherwise, clear the flag and indicate that we do not yet have an + up-vector. */ + } else { + upfree = 1; + upx = AST__BAD; + upy = AST__BAD; + } + +/* Indicate that the tangent vector to the other axis is not yet + known. */ + dx = AST__BAD; + dy = AST__BAD; + gx = AST__BAD; + gy = AST__BAD; + +/* Store the gap to put next to the label text. This is in equal scaled + coords, not graphics coords. */ + txtgap = astGetNumLabGap( this, axis )*mindim; + +/* Get a pointer to the structure containing information describing the + breaks in the curve which passes through the first major tick mark. */ + cdt = cdata[ axis ]; + +/* Get a pointer to the axis value at the first major tick mark. */ + value = info->ticks; + +/* Initialise pointers to two PointSets which will be created and used + within function GVec. */ + pset1 = NULL; + pset2 = NULL; + +/* Get memory to hold the axis values at which labels have been put. */ + used = (double *) astMalloc( sizeof(double)*(size_t)info->nmajor ); + nused = 0; + +/* The tick marks are done in two batches, each batch working out from the + middle. This is done because there may be extra tick marks outside the + normal ranges at the extremes, and these should not be given the + priority caused by doing them first. Store the mid-tick index, the + current tick index, and the increment between ticks. The ticks from the + middle up to the highest index are done first. */ + t0 = info->nmajor/2; + tick = t0 - 1; + tinc = 1; + +/* Loop round until all ticks have been done. */ + last = info->nmajor - 1; + while( (tick += tinc) >= 0 && astOK ){ + +/* If we have done the highest tick index, start again at the tick just + below middle, and work done towards index zero. */ + if( tick == info->nmajor ){ + tick = t0 - 1; + tinc = -1; + } + +/* Store the reference position for the label . */ + val[ axis ] = value[ tick ]; + val[ 1 - axis ] = labelat[ axis ]; + +/* Store the difference between this tick and the next. */ + if( logticks ) { + diff = value[ tick ]*( gap[ axis ] - 1.0 ); + } else { + diff = gap[ axis ]; + } + +/* See if this axis value has already been used. */ + for( iused = 0; iused < nused; iused++ ){ + if( fabs( val[ axis ] - used[ iused ] ) < + 1.0E-3*diff ) break; + } + +/* If the axis value has already been used, don't use it again. */ + if( iused >= nused || nused == 0 ){ + used[ nused++ ] = val[ axis ]; + +/* We now need to decide where to put the reference point for the text + string, and what justification to use. Assuming that NumLabGap is +ve, + the labels are drawn on the left hand side of the axis as seen by + someone moving along the axis in the positive direction, with an + up-vector which is normal to the axis tangent. First, find the graphics + coordinates at the point being labelled, and the unit tangent-vector + parallel to the axis being labelled. If the tangent vector is not defined, + then the tangent vector used for the previous label is re-used. This + unit tangent vector is expressed in graphics coords. */ + GVec( this, mapping, val, axis, 0.01*diff, &pset1, + &pset2, &gx, &gy, &dx, &dy, &flag, method, class, + status ); + +/* If we now have a tangent vector and good graphics coordinates for the + label's reference position... */ + if( dx != AST__BAD && dy != AST__BAD && + gx != AST__BAD && gy != AST__BAD ){ + +/* Convert the unit tangent vector from graphics coords to equal-scaled coords. */ + dx *= alpha; + dy *= beta; + +/* Rotate through 90 degrees to get a vector perpendicular to the axis in + equal scaled coords. This vector points to the left as you move along + the physical axis in the positive direction. Find its length. */ + rx = -dy; + ry = dx; + rlen = sqrt( rx*rx + ry*ry ); + +/* The reference position for the text is displaced away from the + reference position normal to the axis on the left hand side by the + "txtgap" value. */ + offx = rx*txtgap/rlen; + offy = ry*txtgap/rlen; + gx += offx/alpha; + gy += offy/beta; + +/* The up-vector and justification for the text depends on whether or + not the up-vector is free to rotate. If it is free, the up-vector is + chosen so that the text is not upside-down. Note, the up-vector is + specified in the equally scaled coordinate system. */ + if( upfree ){ + + if( dx < -0.01*fabs( alpha ) ){ + upx = -rx; + upy = -ry; + just = ( txtgap < 0.0 )? "BC" : "TC"; + } else { + upx = rx; + upy = ry; + just = ( txtgap < 0.0 )? "TC" : "BC"; + } + if( txtgap == 0.0 ) just = "CC"; + +/* If the up vector is required to be vertical, a system is used which + tries to put the centre of the text string on or near the offset + vector. */ + } else { + upx = 0.0; + upy = 1.0; + + if( offy > fabs(txtgap)*sin45 ){ + just_buf[0] = 'B'; + } else if( offy < -fabs(txtgap)*sin45 ){ + just_buf[0] = 'T'; + } else { + just_buf[0] = 'C'; + } + if( txtgap == 0.0 ) just_buf[0] = 'C'; + + if( offx < -fabs(txtgap)*sin45 ){ + just_buf[1] = 'R'; + } else if( offx > fabs(txtgap)*sin45 ){ + just_buf[1] = 'L'; + } else { + just_buf[1] = 'C'; + } + if( txtgap == 0.0 ) just_buf[1] = 'C'; + + just_buf[2] = 0; + just = just_buf; + } + +/* Get the label text. */ + text = (info->labels)[ tick ]; + if( text ){ + +/* Check that the reference position is within the plotting area. + If so, add it to the list of labels to be drawn. */ + if( gx >= this->xlo && gx <= this->xhi && + gy >= this->ylo && gy <= this->yhi ){ + + labellist = (LabelList *) astGrow( (void *) labellist, nlab + 1, sizeof(LabelList) ); + if ( astOK ) { + (labellist + nlab)->index = tick; + (labellist + nlab)->text = (char *) astStore( NULL, (void *) text, strlen(text) + 1 ); + (labellist + nlab)->x = gx; + (labellist + nlab)->y = gy; + (labellist + nlab)->just = (char *) astStore( NULL, (void *) just, strlen(just) + 1 ); + (labellist + nlab)->upx = upx; + (labellist + nlab)->upy = upy; + (labellist + nlab)->val = val[ axis ]; + nlab++; + } else { + break; + } + } + } + } + } + } + +/* If any labels were stored, draw the text strings, and then release the + memory used to hold the text, etc. */ + if( nlab > 0 ) { + PlotLabels( this, esc, frame, axis, labellist, info->fmt, nlab, + &box, method, class, status ); + ll = labellist; + for( tick = 0; tick < nlab; tick ++ ) { + ll->text = (char *) astFree( (void *) ll->text ); + ll->just = (char *) astFree( (void *) ll->just ); + ll++; + } + labellist = (LabelList *) astFree( (void *) labellist ); + } + +/* Free the memory used to hold the axis values at which labels have + been put. */ + used = (double *) astFree( (void *) used ); + +/* Annul the PointSets (if used). */ + if( pset1 ) pset1 = astAnnul( pset1 ); + if( pset2 ) pset2 = astAnnul( pset2 ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, gelid, 0, GRF__TEXT, method, class ); + +/* Set up the id for the next graphical element to be drawn. */ + gelid = AST__NUMLAB2_ID; + + } + } + +/* Free the memory used to hold the bounding boxes. */ + box = (float *) astFree( (void *) box ); + +/* Annul the pointers to the Frame and the Mapping. */ + mapping = astAnnul( mapping ); + frame = astAnnul( frame ); + + } + +/* Return. */ + return; + +} + +static void LinePlot( AstPlot *this, double xa, double ya, double xb, + double yb, int ink, AstPlotCurveData *cdata, + const char *method, const char *class, int *status ){ +/* +* +* Name: +* LinePlot + +* Purpose: +* Draws a straight line omitting bad regions. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void LinePlot( AstPlot *this, double xa, double ya, double xb, +* double yb, int ink, AstPlotCurveData *cdata, +* const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws a straight line between two positions in graphics +* coordinates but leaves gaps in the line where it passes through +* regions which have no corresponding physical coordinates. + +* Parameters: +* this +* Pointer to the Plot. +* xa +* The graphics X coordinate at the start of the line. +* ya +* The graphics Y coordinate at the start of the line. +* xb +* The graphics X coordinate at the end of the line. +* yb +* The graphics Y coordinate at the end of the line. +* ink +* If zero, the line is not actually drawn, but information about +* the breaks is still returned. If non-zero, the line is also drawn. +* cdata +* A pointer to a structure in which to return information about the +* breaks in the line. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - No curve is draw if any of the start or end positions are bad +* (i.e. equal to AST__BAD), or if a NULL pointer is supplied for "cdata". +* No errors are reported in these cases. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + double d[ CRV_NPNT ]; /* Offsets to evenly spaced points along curve */ + double x[ CRV_NPNT ]; /* X coords at evenly spaced points along curve */ + double y[ CRV_NPNT ]; /* Y coords at evenly spaced points along curve */ + double tol; /* Absolute tolerance value */ + int i; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Check the supplied values are usable. */ + if( xa == AST__BAD || ya == AST__BAD || + xb == AST__BAD || yb == AST__BAD || + !cdata ) return; + +/* Convert the tolerance from relative to absolute graphics coordinates. */ + tol = astGetTol( this )*astMAX( this->xhi - this->xlo, this->yhi - this->ylo ); + +/* Ensure the globals holding the scaling from graphics coords to equally + scaled coords are available. */ + GScales( this, NULL, NULL, method, class, status ); + +/* Set up the external variables used by the Crv and CrvLine function (see + their prologues for details). */ + Crv_scerr = ( astGetLogPlot( this, 0 ) || + astGetLogPlot( this, 1 ) ) ? 100.0 : 1.5; + Crv_ux0 = AST__BAD; + Crv_limit = 0.5*tol*tol; + Crv_tol = tol; + Crv_map = Map2; + Crv_ink = ink; + Crv_len = 0.0F; + Crv_xlo = this->xlo; + Crv_xhi = this->xhi; + Crv_ylo = this->ylo; + Crv_yhi = this->yhi; + Crv_out = 1; + Crv_xbrk = cdata->xbrk; + Crv_ybrk = cdata->ybrk; + Crv_vxbrk = cdata->vxbrk; + Crv_vybrk = cdata->vybrk; + Crv_clip = astGetClip( this ) & 1; + +/* Create a set of evenly spaced values between 0.0 and 1.0. These are the + offsets the edge of the plotting zone at which the mapping is tested. */ + for( i = 0; i < CRV_NPNT; i++ ){ + d[ i ] = ( (double) i)/( (double) CRV_NSEG ); + } + +/* Now set up the externals used to communicate with the Map2 function. + Map2 transforms a set of offsets between zero and one into a set of + corresponding graphics coordinates, with bad values substituted for any + offsets which correspond to points outside the domain of the mapping. */ + +/* The number of axes in the physical coordinate system (i.e. the current + Frame). */ + Map2_ncoord = astGetNout( this ); + +/* A pointer to the mapping from graphics world cordinates to physical + coordinates. */ + Map2_plot = this; + Map2_map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* The graphics coordinates corresponding to an offset of zero (i.e. + the start of the line). */ + Map2_x0 = xa; + Map2_y0 = ya; + +/* The increments in X and Y between offset zero (the start of the + line) and offset 1 (the end of the line). */ + Map2_deltax = xb - xa; + Map2_deltay = yb - ya; + +/* Get the graphics coordinates corresponding to the initial set of + offsets. */ + Map2( CRV_NPNT, d, x, y, method, class, status GLOBALS_NAME ); + +/* Use Crv and Map2 to draw the intersection of the straight line with + the region containing valid physical coordinates. */ + Crv( this, d, x, y, 0, NULL, NULL, method, class, status ); + +/* End the current poly line. */ + Opoly( this, status ); + +/* Tidy up the static data used by Map2. */ + Map2( 0, NULL, NULL, NULL, method, class, status GLOBALS_NAME ); + +/* If no part of the curve could be drawn, set the number of breaks and the + length of the drawn curve to zero. */ + if( Crv_out ) { + Crv_nbrk = 0; + Crv_len = 0.0F; + +/* Otherwise, add an extra break to the returned structure at the position of + the last point to be plotted. */ + } else { + Crv_nbrk++; + if( Crv_nbrk > AST__PLOT_CRV_MXBRK ){ + astError( AST__CVBRK, "%s(%s): Number of breaks in curve " + "exceeds %d.", status, method, class, AST__PLOT_CRV_MXBRK ); + } else { + *(Crv_xbrk++) = (float) Crv_xl; + *(Crv_ybrk++) = (float) Crv_yl; + *(Crv_vxbrk++) = (float) -Crv_vxl; + *(Crv_vybrk++) = (float) -Crv_vyl; + } + } + +/* Store extra information about the curve in the returned structure, and + purge any zero length sections. */ + if( cdata ){ + cdata->length = Crv_len; + cdata->out = Crv_out; + cdata->nbrk = Crv_nbrk; + PurgeCdata( cdata, status ); + } + +/* Annul the Mapping. */ + Map2_map = astAnnul( Map2_map ); + +/* Return. */ + return; + +} + +static double **MakeGrid( AstPlot *this, AstFrame *frm, AstMapping *map, + int disk, int dim, double xlo, double xhi, + double ylo, double yhi, int nphy, AstPointSet **pset1, + AstPointSet **pset2, int norm, const char *method, + const char *class, int *status ){ +/* +* Name: +* MakeGrid + +* Purpose: +* Create a square grid of graphics coordinates and the corresponding +* physical coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* double **MakeGrid( AstPlot *this, AstFrame *frm, AstMapping *map, +* int disk, int dim, double xlo, double xhi, double ylo, +* double yhi, int nphy, AstPointSet **pset1, +* AstPointSet **pset2, int norm, const char *method, +* const char *class, int *status ){ + +* Class Membership: +* Plot member function. + +* Description: +* This function creates two PointSets, one holding a square grid of +* graphics coordinates covering the supplied area, and the other +* holding the corresponding physical coordinates. The points are +* stored row by row in the returned PointSets, i.e. if the cell size +* for the grid is (dx,dy), the first point is (xmin,ymin), followed +* by (xmin+dx,ymin), (xmin+2*dx,ymin), up to (xmin+(dim-1)*dx,ymin), +* followed by the next row (xmin,ymin+dy), (xmin+dx,ymin+dy), etc. + +* Parameters: +* this +* The Plot. +* frm +* A pointer to the Current Frame in the Plot. If this is supplied +* NULL, then a pointer is found within this function if required (i.e. +* if "norm" is non-zero). +* map +* The Mapping from graphics to physical coordinates, extracted from +* the Plot. +* disk +* If non-zero, the corners of the grid are omitted form the +* returned PointSets, resulting in a grid that is more disk like than +* rectangular. +* dim +* The number of samples along each edge of the grid. +* xlo +* The lower bound on the first axis of the region to be covered +* by the grid. +* xhi +* The upper bound on the first axis of the region to be covered +* by the grid. +* ylo +* The lower bound on the second axis of the region to be covered +* by the grid. +* yhi +* The upper bound on the second axis of the region to be covered +* by the grid. +* nphy +* The number of axes in the physical cooridinate system. +* pset1 +* A pointer to a location at which to store a pointer to the +* PointSet holding the graphics coordinates. +* pset2 +* A pointer to a location at which to store a pointer to the +* PointSet holding the physical coordinates. +* norm +* If non-zero the physical cooridnates are normalised using the +* Plot's astNorm method. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the physical coordinate data stored in the PointSet +* "pset2". + +* Notes: +* - The returned PointSets should be annulled when no longer needed, +* using astAnnul. +* - NULL pointers are returned if an error has already occurred, or +* if this function should fail for any reason. +*/ + +/* Local Variables: */ + double **ptr1; /* Pointers to graphics axis values */ + double **ptr2; /* Pointers to physical axis values */ + int size; /* No. of points in the grid */ + +/* Initialise the returned pointers. */ + *pset1 = NULL; + *pset2 = NULL; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Create two PointSets. We assume for the moment that they cover the + full grid, including corners. */ + size = dim*dim; + *pset1 = astPointSet( size, 2, "", status ); + *pset2 = astPointSet( size, nphy, "", status ); + +/* Get pointers to the data arrays for the two PointSets. */ + ptr1 = astGetPoints( *pset1 ); + ptr2 = astGetPoints( *pset2 ); + +/* Create a grid covering the supplied area. */ + size = GraphGrid( dim, disk, xlo, xhi, ylo, yhi, ptr1, status ); + +/* If the corners are being omitted, reduce the number of points in the + two PointSets. */ + if( disk ) { + astSetNpoint( *pset1, size ); + astSetNpoint( *pset2, size ); + } + +/* Transform these graphics positions to physical coordinates. */ + Trans( this, frm, map, *pset1, 1, *pset2, norm, method, class, status ); + +/* If an error has occurred, annul the two pointsets. */ + if( !astOK ){ + *pset1 = astAnnul( *pset1 ); + *pset2 = astAnnul( *pset2 ); + ptr2 = NULL; + } + +/* Return. */ + return ptr2; + +} + + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* Plot member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstPlot *this; /* Pointer to Plot structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the Plot structure. */ + this = (AstPlot *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* If defined, ensure the grfcontext KeyMap contained within the Plot is + locked, unlocked or checked. */ + if( this->grfcontext ) { + if( !result ) result = astManageLock( this->grfcontext, mode, extra, fail ); + +/* Also lock or unlock the associated object handle. */ + if( mode == AST__LOCK ) { + if( !result ) astLock( this->grfcontextID, extra ); + + } else if( mode == AST__UNLOCK ) { + if( !result ) astUnlock( this->grfcontextID, 0 ); + + } + } + + return result; + +} +#endif + +static void Map1( int n, double *dist, double *x, double *y, + const char *method, const char *class, + int *status GLOBALS_ARG ){ +/* +* Name: +* Map1 + +* Purpose: +* Find graphics coordinates at given distances along a curve +* parallel to a physical axis. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Map1( int n, double *dist, double *x, double *y, +* const char *method, const char *class, +* int *status [,AstGlobals *AST__GLOBALS] ) + +* Class Membership: +* Plot member function. + +* Description: +* The supplied distances are converted into physical coordinates +* using the scalings described by various external variables, and then +* these physical coordinates are mapped into graphics coordinates. + +* Parameters: +* n +* The number of points to map. Static resources are released but +* no points are mapped if zero is supplied. +* dist +* A pointer to an array holding "n" distances. A "dist" value of +* zero corresponds to the starting position supplied in external +* variable Map1_origin. A "dist" value of one corresponds to the +* finishing position which is a distance Map1_length away from +* Map1_origin, moving in the positive direction of the axis given +* by Map1_axis. "dist" values can be either linearly or +* logarithmically related to axis values (see Map1_log). +* x +* A pointer to an array in which to store the "n" graphics X +* coordinate values corresponding to the positions in "dist". +* y +* A pointer to an array in which to store the "n" graphics Y +* coordinate values corresponding to the positions in "dist". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +* AST__GLOBALS +* Only present if compiled with -DTHREAD_SAFE. It is a pointer to +* the structure holding the global data for the executing thread. +* It is passed as a function parameter, rather than being accessed +* within this function using the astGET_GLOBALS(NULL) macro (as +* other Object-less functions do) in order to avoid the time +* overheads of calling astGET_GLOBALS(NULL) . This function is +* time-critical. + +* External Variables: +* Map1_log = int (Read) +* If zero, then "dist" in learly related to axis value. Otherwise +* it is linearly related to log10(axis value). +* Map1_ncoord = int (Read) +* The number of axes in the physical coordinate system. +* Map1_axis = int (Read) +* The zero-based index of the axis which the curve follows (i.e. +* the axis which changes value along the curve). +* Map1_statics = Map1Statics * (Read and Write) +* Pointer to a structure holding other static data used by Map1. +* Map1_origin = const double * (Read) +* A pointer to an array holding the physical coordinate value on +* each axis at the start of the curve (i.e. at dist = 0.0). +* Map1_length = double (Read) +* The scale factor to convert "dist" values into increments +* along the physical axis given by Map1_axis. +* Map1_plot = AstPlot * (Read) +* A pointer to the Plot defining the mapping from graphics cordinates +* to physical coordinates. +* Map1_map = AstMapping * (Read) +* A pointer to the mapping from graphics cordinates to physical +* coordinates extracted from the Plot. +* Map1_frame = AstFrame * (Read) +* A pointer to the Current Frame in the Plot. +* Map1_norm = int (Read) +* A flag indicating if physical coordinate values which are not in +* the normal ranges of the corresponding axes should be considered +* bad. + +* Notes: +* - On the first call, this function allocates static resources which +* are used by subsequent invocation. These resources should be freed before +* calling this function with new values for any of the external variables, +* or when no longer needed, by calling this function with "n" supplied as +* zero. +* - If an error has already occurred, this runction returns without +* action ,except that if "n" is supplied as zero then static resources +* are released even if an error has already occurred. + +*/ + +/* Local Constants: */ + Map1Statics *statics; /* Pointer to structure holding static data */ + double *p; /* Pointer to next value */ + double axval; /* Axis origin value */ + int i, j; /* Loop counts */ + +/* Convert the global "void *" pointer to a Map1Statics pointer */ + statics = (Map1Statics *) Map1_statics; + +/* If zero points were supplied, release static resources and return. */ + if( n == 0 ){ + if( statics ) { + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + if( statics->work1 ) statics->work1 = (double *) astFree( (void *) statics->work1 ); + if( statics->work2 ) statics->work2 = (double *) astFree( (void *) statics->work2 ); + Map1_statics = astFree( statics ); + } + return; + } + +/* Otherwise, check the inherited global status. */ + if( !astOK ) return; + +/* Create and initialise a structure to hold extra static information if + this has not already been done. */ + if( !statics ) { + statics = astMalloc( sizeof( Map1Statics ) ); + if( statics ) { + statics->pset1 = NULL; + statics->pset2 = NULL; + statics->ptr1 = NULL; + statics->pax = NULL; + statics->ptr2[ 0 ] = NULL; + statics->ptr2[ 1 ] = NULL; + statics->work1 = NULL; + statics->work2 = NULL; + statics->nl = 0; + Map1_statics = statics; + } + } + +/* If the number of points to be mapped is different to last time, + set up some PointSets to store the specified number of points. */ + if( n != statics->nl ){ + statics->nl = n; + +/* Create a PointSet to hold the physical coordinates corresponding to + the supplied offsets. First annul any existing PointSet. */ + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + statics->pset1 = astPointSet( n, Map1_ncoord, "", status ); + statics->ptr1 = astGetPoints( statics->pset1 ); + +/* Create a PointSet to hold the corresponding graphics coordinates. + The supplied "x" and "y" arrays will be used to store the data + so we do not need to get pointers to the data using astGetPoints. First + annul any existing PointSet. */ + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + statics->pset2 = astPointSet( n, 2, "", status ); + +/* Get work space to hold two positions. */ + statics->work1 = (double *) astRealloc( (void *) statics->work1, + sizeof(double)*(size_t)Map1_ncoord ); + statics->work2 = (double *) astRealloc( (void *) statics->work2, + sizeof(double)*(size_t)Map1_ncoord ); + +/* Check the pointer can be used. */ + if( astOK ){ + +/* Store a pointer to the start of the memory which will be used to store + the physical data for the axis being drawn. */ + statics->pax = statics->ptr1[ Map1_axis ]; + +/* Fill the PointSet which is used to hold physical data with the physical + coordinates at the start of the curve. */ + for( i = 0; i < Map1_ncoord; i++ ){ + axval = Map1_origin[ i ]; + p = statics->ptr1[ i ]; + for( j = 0; j < n; j++ ) *(p++) = axval; + } + +/* Store the scale and offset to apply to the "dist" values. If Map1_log is + zero (linear axes) then applying these values gives axis value directly. + If Map1_log is non-zero (log axes) then applying these values gives + log10( axis value). */ + if( Map1_log ) { + statics->neg = ( Map1_origin[ Map1_axis ] < 0 ); + statics->axorig = log10( fabs( Map1_origin[ Map1_axis ] ) ); + statics->axscale = log10( fabs( Map1_origin[ Map1_axis ] + + Map1_length ) ) - statics->axorig; + } else { + statics->axorig = Map1_origin[ Map1_axis ]; + statics->axscale = Map1_length; + } + } + } + +/* Check the initialisation went OK (if done). */ + if( astOK ){ + +/* Loop round each offset along the curve, converting the normalised offset + in the range [0,1] to a physical coordinate and storing in PointSet 1. */ + p = statics->pax; + for( i = 0; i < n; i++){ + *(p++) = statics->axorig + statics->axscale*dist[ i ]; + } + if( Map1_log ) { + p = statics->pax; + for( i = 0; i < n; i++,p++ ){ + *p = statics->neg ? -pow( 10.0, *p ) : pow( 10.0, *p ); + } + } + +/* Store pointers to the results arrays in PointSet 2. */ + statics->ptr2[ 0 ] = x; + statics->ptr2[ 1 ] = y; + astSetPoints( statics->pset2, statics->ptr2 ); + +/* Map all the positions into graphics coordinates. */ + (void) Trans( Map1_plot, NULL, Map1_map, statics->pset1, 0, statics->pset2, 1, method, class, status ); + +/* If points not in their normal ranges are to be set bad... */ + if( Map1_norm ) { + +/* The following code simply normalizes the physical position, and if this + produces any change, the graphics positions are set bad. */ + for( i = 0; i < n; i++){ + for( j = 0; j < Map1_ncoord; j++) statics->work1[j] = statics->ptr1[j][i]; + astNorm( Map1_frame, statics->work1 ); + for( j = 0; j < Map1_ncoord; j++) { + if( !astEQUALS( statics->work1[j], statics->ptr1[j][i], 1.0E8 ) ) { + statics->ptr2[0][i] = AST__BAD; + statics->ptr2[1][i] = AST__BAD; + break; + } + } + } + } + } + +/* Return. */ + return; + +} + +static void Map2( int n, double *dist, double *x, double *y, + const char *method, const char *class, + int *status GLOBALS_ARG ){ +/* +* Name: +* Map2 + +* Purpose: +* Find which graphics coordinates have good physical coordinates +* at given distances along a straight line. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Map2( int n, double *dist, double *x, double *y, +* const char *method, const char *class, +* int *status [,AstGlobals *AST__GLOBALS] ) + +* Class Membership: +* Plot member function. + +* Description: +* The supplied distances refer to the distance along a straight line +* in the graphics coordinate system. The returned graphics coordinates +* correspond to the supplied distances, except that any position for +* which there are no defined physical coordinates is returned bad. + +* Parameters: +* n +* The number of points to map. Static resources are released but +* no points are mapped if zero is supplied. +* dist +* A pointer to an array holding "n" distances. A "dist" value of +* zero corresponds to the graphics position supplied in external +* variables (Map2_x0, Map2_y0). A "dist" value of one corresponds to +* the graphics position which is offset from the start by the vector +* (Map2_deltax, Map2_deltay). +* x +* A pointer to an array in which to store the "n" graphics X +* coordinate values corresponding to the positions in "dist", +* except that any which have no corresponding physical coordinates +* are set to AST__BAD. +* y +* A pointer to an array in which to store the "n" graphics Y +* coordinate values corresponding to the positions in "dist", +* except that any which have no corresponding physical coordinates +* are set to AST__BAD. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +* AST__GLOBALS +* Only present if compiled with -DTHREAD_SAFE. It is a pointer to +* the structure holding the global data for the executing thread. +* It is passed as a function parameter, rather than being accessed +* within this function using the astGET_GLOBALS(NULL) macro (as +* other Object-less functions do) in order to avoid the time +* overheads of calling astGET_GLOBALS(NULL) . This function is +* time-critical. + +* External Variables: +* Map2_ncoord = int (Read) +* The number of axes in the physical coordinate system. +* Map2_x0 = double (Read) +* The graphics X coordinate at the start of the line (i.e. at dist +* = 0.0). +* Map2_y0 = double (Read) +* The graphics Y coordinate at the start of the line (i.e. at dist +* = 0.0). +* Map2_deltax = double (Read) +* The increment along the graphics X axis between the start and +* end of the line. +* Map2_deltay = double (Read) +* The increment along the graphics Y axis between the start and +* end of the line. +* Map2_plot = AstPlot * (Read) +* A pointer to the Plot defining the mapping from graphics cordinates +* to physical coordinates. +* Map2_map = AstMapping * (Read) +* A pointer to the mapping from graphics cordinates to physical +* coordinates, extracted from the Plot. +* Map2_statics = Map2Statics * (Read and Write) +* Pointer to a structure holding other static data used by Map2. + +* Notes: +* - On the first call, this function allocates static resources which +* are used by subsequent invocation. These resources should be freed before +* calling this function with new values for any of the external variables, +* or when no longer needed, by calling this function with "n" supplied as +* zero. +* - If an error has already occurred, this runction returns without +* action ,except that if "n" is supplied as zero then static resources +* are released even if an error has already occurred. + +*/ +/* Local Constants: */ + Map2Statics *statics; /* Pointer to structure holding static data */ + int i, j; /* Loop counts */ + double *p; /* Pointer to next physical value */ + double *px; /* Pointer to next x graphics value */ + double *py; /* Pointer to next y graphics value */ + +/* Convert the global "void *" pointer to a Map2Statics pointer */ + statics = (Map2Statics *) Map2_statics; + +/* If zero points were supplied, release static resources and return. */ + if( n == 0 ){ + if( statics ) { + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + Map2_statics = astFree( statics ); + } + return; + } + +/* Otherwise, check the inherited global status. */ + if( !astOK ) return; + +/* Create and initialise a structure to hold extra static information if + this has not already been done. */ + if( !statics ) { + statics = astMalloc( sizeof( Map2Statics ) ); + if( statics ) { + statics->pset1 = NULL; + statics->pset2 = NULL; + statics->ptr2 = NULL; + statics->ptr1[ 0 ] = NULL; + statics->ptr1[ 1 ] = NULL; + statics->nl = 0; + Map2_statics = statics; + } + } + +/* If the number of points to be mapped is different to last time, + set up some PointSets to store the specified number of points. */ + if( n != statics->nl ){ + statics->nl = n; + +/* Create a PointSet to hold the graphics coordinates corresponding to + the supplied offsets. The supplied arrays will be used to hold the + data for this PointSet, and so astGetPoints is not called. */ + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + statics->pset1 = astPointSet( n, 2, "", status ); + +/* Create a PointSet to hold the corresponding physical coordinates, and + get pointers to the associated axis values. */ + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + statics->pset2 = astPointSet( n, Map2_ncoord, "", status ); + statics->ptr2 = astGetPoints( statics->pset2 ); + } + + +/* Check the initialisation went OK (if done). */ + if( astOK ){ + +/* Store pointers to the results arrays in PointSet 1. */ + statics->ptr1[ 0 ] = x; + statics->ptr1[ 1 ] = y; + astSetPoints( statics->pset1, statics->ptr1 ); + +/* Loop round each offset along the curve, converting the normalised offset + in the range [0,1] to graphics coordinate and storing in PointSet 1. */ + px = x; + py = y; + for( i = 0; i < n; i++){ + *(px++) = Map2_x0 + Map2_deltax*dist[ i ]; + *(py++) = Map2_y0 + Map2_deltay*dist[ i ]; + } + +/* Map all the positions into physical coordinates. */ + (void) Trans( Map2_plot, NULL, Map2_map, statics->pset1, 1, statics->pset2, 0, method, class, status ); + +/* Check the physical coordinates for bad values, setting the corresponding + graphics coordinates bad. */ + for( j = 0; j < Map2_ncoord; j++ ){ + p = statics->ptr2[ j ]; + px = x; + py = y; + + for( i = 0; i < n; i++){ + if( *(p++) == AST__BAD ){ + *(px++) = AST__BAD; + *(py++) = AST__BAD; + } else { + px++; + py++; + } + } + } + } + +/* Return. */ + return; + +} + +static void Map3( int n, double *dist, double *x, double *y, + const char *method, const char *class, + int *status GLOBALS_ARG ){ +/* +* Name: +* Map3 + +* Purpose: +* Find graphics coordinates at given distances along a geodesic curve +* between two physical coordinate positions. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Map3( int n, double *dist, double *x, double *y, +* const char *method, const char *class, +* int *status [,AstGlobals *AST__GLOBALS] ) + +* Class Membership: +* Plot member function. + +* Description: +* The supplied distances are converted into physical offsets along the +* geodesic curve joining the starting and finishing points given by +* externals Map3_origin and Map3_end. The physical coordinates at these +* offsets are found, and transformed into graphics coordinates. + +* Parameters: +* n +* The number of points to map. Static resources are released but +* no points are mapped if zero is supplied. +* dist +* A pointer to an array holding "n" distances. A "dist" value of +* zero corresponds to the starting position supplied in external +* variable Map3_origin. A "dist" value of one corresponds to the +* finishing position given by Map3_end. +* x +* A pointer to an array in which to store the "n" graphics X +* coordinate values corresponding to the positions in "dist". +* y +* A pointer to an array in which to store the "n" graphics Y +* coordinate values corresponding to the positions in "dist". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +* AST__GLOBALS +* Only present if compiled with -DTHREAD_SAFE. It is a pointer to +* the structure holding the global data for the executing thread. +* It is passed as a function parameter, rather than being accessed +* within this function using the astGET_GLOBALS(NULL) macro (as +* other Object-less functions do) in order to avoid the time +* overheads of calling astGET_GLOBALS(NULL) . This function is +* time-critical. + +* External Variables: +* Map3_ncoord = int (Read) +* The number of axes in the physical coordinate system. +* Map3_origin = const double * (Read) +* A pointer to an array holding the physical coordinate value on +* each axis at the start of the curve (i.e. at dist = 0.0). +* Map3_end = const double * (Read) +* A pointer to an array holding the physical coordinate value on +* each axis at the end of the curve (i.e. at dist = 1.0). +* Map3_scale = double (Read) +* The scale factor to convert "dist" values into physical offsets +* along the geodesic curve. +* Map3_statics = Map3Statics * (Read and Write) +* Pointer to a structure holding other static data used by Map3. +* Map3_plot = AstPlot * (Read) +* A pointer to the Plot defining the mapping from graphics cordinates +* to physical coordinates. +* Map3_map = AstMapping * (Read) +* A pointer to the mapping from graphics cordinates to physical +* coordinates extracted from the Plot. +* Map3_frame = AstFrame * (Read) +* A pointer to the Current Frame in the Plot. + +* Notes: +* - On the first call, this function allocates static resources which +* are used by subsequent invocation. These resources should be freed before +* calling this function with new values for any of the external variables, +* or when no longer needed, by calling this function with "n" supplied as +* zero. +* - If an error has already occurred, this runction returns without +* action ,except that if "n" is supplied as zero then static resources +* are released even if an error has already occurred. + +*/ + +/* Local Constants: */ + Map3Statics *statics; /* Pointer to structure holding static data */ + int i, j; /* Loop counts */ + +/* Convert the global "void *" pointer to a Map3Statics pointer */ + statics = (Map3Statics *) Map3_statics; + +/* If zero points were supplied, release static resources and return. */ + if( n == 0 ){ + if( statics ) { + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + if( statics->pos ) statics->pos = (double *) astFree( (void *) statics->pos ); + Map3_statics = astFree( statics ); + } + return; + } + +/* Otherwise, check the inherited global status. */ + if( !astOK ) return; + +/* Create and initialise a structure to hold extra static information if + this has not already been done. */ + if( !statics ) { + statics = astMalloc( sizeof( Map3Statics ) ); + if( statics ) { + statics->pset1 = NULL; + statics->pset2 = NULL; + statics->ptr1 = NULL; + statics->ptr2[ 0 ] = NULL; + statics->ptr2[ 1 ] = NULL; + statics->nc = 0; + statics->nl = 0; + statics->pos = NULL; + Map3_statics = statics; + } + } + +/* If the number of points to be mapped is different to last time, + set up some PointSets to store the specified number of points. */ + if( n != statics->nl ){ + statics->nl = n; + +/* Create a PointSet to hold the physical coordinates corresponding to + the supplied offsets. First annul any existing PointSet. */ + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + statics->pset1 = astPointSet( n, Map3_ncoord, "", status ); + statics->ptr1 = astGetPoints( statics->pset1 ); + +/* Create a PointSet to hold the corresponding graphics coordinates. + The supplied "x" and "y" arrays will be used to store the data + so we do not need to get pointers to the data using astGetPoints. First + annul any existing PointSet. */ + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + statics->pset2 = astPointSet( n, 2, "", status ); + + } + +/* If the number of physical axes is different to last time, allocate + memory to hold a single physical position. */ + if( statics->nc != Map3_ncoord ){ + statics->nc = Map3_ncoord; + statics->pos = (double *) astMalloc( sizeof(double)*(size_t)Map3_ncoord ); + } + +/* Check the initialisation went OK (if done). */ + if( astOK ){ + +/* Loop round each offset along the curve, converting the normalised offset + in the range [0,1] to a physical offset, and then into a physical + position, and store in PointSet 1. */ + for( i = 0; i < n; i++){ + astOffset( Map3_frame, Map3_origin, Map3_end, Map3_scale*dist[ i ], + statics->pos ); + + for( j = 0; j < Map3_ncoord; j++ ){ + statics->ptr1[ j ][ i ] = statics->pos[ j ]; + } + + } + +/* Store pointers to the results arrays in PointSet 2. */ + statics->ptr2[ 0 ] = x; + statics->ptr2[ 1 ] = y; + astSetPoints( statics->pset2, statics->ptr2 ); + +/* Map all the positions into graphics coordinates. */ + (void) Trans( Map3_plot, NULL, Map3_map, statics->pset1, 0, statics->pset2, 1, method, class, status ); + } + +/* Return. */ + return; + +} + +static void Map4( int n, double *dist, double *x, double *y, + const char *method, const char *class, + int *status GLOBALS_ARG ){ +/* +* Name: +* Map4 + +* Purpose: +* Find graphics coordinates at given distances along a user +* specified curve. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Map4( int n, double *dist, double *x, double *y, +* const char *method, const char *class, +* int *status [,AstGlobals *AST__GLOBALS] ) + +* Class Membership: +* Plot member function. + +* Description: +* The supplied distances are converted into physical coordinates using +* the Mapping Map4_umap. These physical coordinates are transformed into +* graphics coordinates. + +* Parameters: +* n +* The number of points to map. Static resources are released but +* no points are mapped if zero is supplied. +* dist +* A pointer to an array holding "n" distances. A "dist" value of +* zero corresponds to the starting position supplied in external +* variable Map3_origin. A "dist" value of one corresponds to the +* finishing position given by Map3_end. +* x +* A pointer to an array in which to store the "n" graphics X +* coordinate values corresponding to the positions in "dist". +* y +* A pointer to an array in which to store the "n" graphics Y +* coordinate values corresponding to the positions in "dist". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +* AST__GLOBALS +* Only present if compiled with -DTHREAD_SAFE. It is a pointer to +* the structure holding the global data for the executing thread. +* It is passed as a function parameter, rather than being accessed +* within this function using the astGET_GLOBALS(NULL) macro (as +* other Object-less functions do) in order to avoid the time +* overheads of calling astGET_GLOBALS(NULL) . This function is +* time-critical. + +* External Variables: +* Map4_ncoord = int (Read) +* The number of axes in the physical coordinate system. +* Map4_plot = AstPlot * (Read) +* A pointer to the Plot defining the mapping from graphics cordinates +* to physical coordinates. +* Map4_map = AstMapping * (Read) +* A pointer to the mapping from graphics cordinates to physical +* coordinates extracted from the Plot. +* Map4_statics = Map4Statics * (Read and Write) +* Pointer to a structure holding other static data used by Map4. +* Map4_umap = AstMapping * (Read) +* A pointer to the mapping from distance along the curve to physical +* coordinates. + +* Notes: +* - On the first call, this function allocates static resources which +* are used by subsequent invocation. These resources should be freed before +* calling this function with new values for any of the external variables, +* or when no longer needed, by calling this function with "n" supplied as +* zero. +* - If an error has already occurred, this runction returns without +* action ,except that if "n" is supplied as zero then static resources +* are released even if an error has already occurred. + +*/ + +/* Local Variables: */ + Map4Statics *statics; /* Pointer to structure holding static data */ + double *ptr1[ 1 ]; /* Pointer to distances data */ + double *ptr3[ 2 ]; /* Pointers to graphics coord data */ + +/* Convert the global "void *" pointer to a Map4Statics pointer */ + statics = (Map4Statics *) Map4_statics; + +/* If zero points were supplied, release static resources and return. */ + if( n == 0 ){ + if( statics ) { + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + if( statics->pset3 ) statics->pset3 = astAnnul( statics->pset3 ); + Map4_statics = astFree( statics ); + } + return; + } + +/* Otherwise, check the inherited global status. */ + if( !astOK ) return; + +/* Create and initialise a structure to hold extra static information if + this has not already been done. */ + if( !statics ) { + statics = astMalloc( sizeof( Map4Statics ) ); + if( statics ) { + statics->pset1 = NULL; + statics->pset2 = NULL; + statics->pset3 = NULL; + statics->nl = 0; + Map4_statics = statics; + } + } + +/* If the number of points to be mapped is different to last time, + set up some PointSets to store the specified number of points. */ + if( n != statics->nl ){ + statics->nl = n; + +/* Create a PointSet to hold the distances along the curve. First annul any + existing PointSet. */ + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + statics->pset1 = astPointSet( n, 1, "", status ); + +/* Create a PointSet to hold the physical coordinates corresponding to + the supplied distances. First annul any existing PointSet. */ + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + statics->pset2 = astPointSet( n, Map4_ncoord, "", status ); + +/* Create a PointSet to hold the corresponding graphics coordinates. + First annul any existing PointSet. */ + if( statics->pset3 ) statics->pset3 = astAnnul( statics->pset3 ); + statics->pset3 = astPointSet( n, 2, "", status ); + + } + +/* Check the initialisation went OK (if done). */ + if( astOK ){ + +/* Use Map4_umap to convert the supplied distances into physical coords + (i.e. coords in the current Frame of the Plot). */ + ptr1[ 0 ] = dist; + astSetPoints( statics->pset1, ptr1 ); + (void) astTransform( Map4_umap, statics->pset1, 1, statics->pset2 ); + +/* Store pointers to the results arrays in PointSet 2. */ + ptr3[ 0 ] = x; + ptr3[ 1 ] = y; + astSetPoints( statics->pset3, ptr3 ); + +/* Now transform these physical coords into graphical coords, + incorporating clipping. */ + (void) Trans( Map4_plot, NULL, Map4_map, statics->pset2, 0, statics->pset3, 1, method, class, status ); + } + +/* Return. */ + return; + +} + +static void Map5( int n, double *dist, double *x, double *y, + const char *method, const char *class, + int *status GLOBALS_ARG ){ +/* +* Name: +* Map5 + +* Purpose: +* Find graphics coordinates at given distances along the boundary of +* a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Map5( int n, double *dist, double *x, double *y, +* const char *method, const char *class, +* int *status [,AstGlobals *AST__GLOBALS] ) + +* Class Membership: +* Plot member function. + +* Description: +* The supplied distances are converted into physical coordinates +* using the Region specified by an external variable, and then +* these physical coordinates are mapped into graphics coordinates. + +* Parameters: +* n +* The number of points to map. Static resources are released but +* no points are mapped if zero is supplied. +* dist +* A pointer to an array holding "n" distances. A "dist" value of +* zero corresponds to the starting position supplied in external +* variable Map1_origin. A "dist" value of one corresponds to the +* finishing position which is a distance Map1_length away from +* Map1_origin, moving in the positive direction of the axis given +* by Map1_axis. "dist" values can be either linearly or +* logarithmically related to axis values (see Map1_log). +* x +* A pointer to an array in which to store the "n" graphics X +* coordinate values corresponding to the positions in "dist". +* y +* A pointer to an array in which to store the "n" graphics Y +* coordinate values corresponding to the positions in "dist". +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +* AST__GLOBALS +* Only present if compiled with -DTHREAD_SAFE. It is a pointer to +* the structure holding the global data for the executing thread. +* It is passed as a function parameter, rather than being accessed +* within this function using the astGET_GLOBALS(NULL) macro (as +* other Object-less functions do) in order to avoid the time +* overheads of calling astGET_GLOBALS(NULL) . This function is +* time-critical. + +* External Variables: +* Map1_log = int (Read) +* If zero, then "dist" in learly related to axis value. Otherwise +* it is linearly related to log10(axis value). +* Map1_ncoord = int (Read) +* The number of axes in the physical coordinate system. +* Map1_axis = int (Read) +* The zero-based index of the axis which the curve follows (i.e. +* the axis which changes value along the curve). +* Map1_statics = Map1Statics * (Read and Write) +* Pointer to a structure holding other static data used by Map1. +* Map1_origin = const double * (Read) +* A pointer to an array holding the physical coordinate value on +* each axis at the start of the curve (i.e. at dist = 0.0). +* Map1_length = double (Read) +* The scale factor to convert "dist" values into increments +* along the physical axis given by Map1_axis. +* Map1_plot = AstPlot * (Read) +* A pointer to the Plot defining the mapping from graphics cordinates +* to physical coordinates. +* Map1_map = AstMapping * (Read) +* A pointer to the mapping from graphics cordinates to physical +* coordinates extracted from the Plot. +* Map1_frame = AstFrame * (Read) +* A pointer to the Current Frame in the Plot. +* Map1_norm = int (Read) +* A flag indicating if physical coordinate values which are not in +* the normal ranges of the corresponding axes should be considered +* bad. + +* Notes: +* - On the first call, this function allocates static resources which +* are used by subsequent invocation. These resources should be freed before +* calling this function with new values for any of the external variables, +* or when no longer needed, by calling this function with "n" supplied as +* zero. +* - If an error has already occurred, this runction returns without +* action ,except that if "n" is supplied as zero then static resources +* are released even if an error has already occurred. + +*/ + +/* Local Constants: */ + Map5Statics *statics; /* Pointer to structure holding static data */ + +/* Convert the global "void *" pointer to a Map5Statics pointer */ + statics = (Map5Statics *) Map5_statics; + +/* If zero points were supplied, release static resources and return. */ + if( n == 0 ){ + if( statics ) { + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + Map5_statics = astFree( statics ); + } + return; + } + +/* Otherwise, check the inherited global status. */ + if( !astOK ) return; + +/* Create and initialise a structure to hold extra static information if + this has not already been done. */ + if( !statics ) { + statics = astMalloc( sizeof( Map3Statics ) ); + if( statics ) { + statics->pset1 = NULL; + statics->pset2 = NULL; + statics->ptr1 = NULL; + statics->ptr2[ 0 ] = NULL; + statics->ptr2[ 1 ] = NULL; + statics->nl = 0; + Map5_statics = statics; + } + } + +/* If the number of points to be mapped is different to last time, + set up some PointSets to store the specified number of points. */ + if( n != statics->nl ){ + statics->nl = n; + +/* Create a PointSet to hold the physical coordinates corresponding to + the supplied offsets. First annul any existing PointSet. */ + if( statics->pset1 ) statics->pset1 = astAnnul( statics->pset1 ); + statics->pset1 = astPointSet( n, Map5_ncoord, "", status ); + statics->ptr1 = astGetPoints( statics->pset1 ); + +/* Create a PointSet to hold the corresponding graphics coordinates. + The supplied "x" and "y" arrays will be used to store the data + so we do not need to get pointers to the data using astGetPoints. First + annul any existing PointSet. */ + if( statics->pset2 ) statics->pset2 = astAnnul( statics->pset2 ); + statics->pset2 = astPointSet( n, 2, "", status ); + } + +/* Get the physical coords at the required positions along the Region + border. */ + astRegTrace( Map5_region, n, dist, statics->ptr1 ); + +/* Store pointers to the results arrays in PointSet 2. */ + statics->ptr2[ 0 ] = x; + statics->ptr2[ 1 ] = y; + astSetPoints( statics->pset2, statics->ptr2 ); + +/* Map all the positions into graphics coordinates. */ + (void) Trans( Map5_plot, NULL, Map5_map, statics->pset1, 0, + statics->pset2, 1, method, class, status ); + +/* Return. */ + return; +} + +static void Mark( AstPlot *this, int nmark, int ncoord, int indim, + const double *in, int type, int *status ){ +/* +*++ +* Name: +c astMark +f AST_MARK + +* Purpose: +* Draw a set of markers for a Plot. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astMark( AstPlot *this, int nmark, int ncoord, int indim, +c const double *in, int type ) +f CALL AST_MARK( THIS, NMARK, NCOORD, INDIM, IN, TYPE, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function draws a set of markers (symbols) at positions +f This routine draws a set of markers (symbols) at positions +* specified in the physical coordinate system of a Plot. The +* positions are transformed into graphical coordinates to +* determine where the markers should appear within the plotting +* area. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c nmark +f NMARK = INTEGER (Given) +* The number of markers to draw. This may be zero, in which +* case nothing will be drawn. +c ncoord +f NCOORD = INTEGER (Given) +* The number of coordinates being supplied for each mark +* (i.e. the number of axes in the current Frame of the Plot, as +* given by its Naxes attribute). +c indim +f INDIM = INTEGER (Given) +c The number of elements along the second dimension of the "in" +f The number of elements along the first dimension of the IN +* array (which contains the marker coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +c given should not be less than "nmark". +f given should not be less than NMARK. +c in +f IN( INDIM, NCOORD ) = DOUBLE PRECISION (Given) +c The address of the first element of a 2-dimensional array of +c shape "[ncoord][indim]" giving the +c physical coordinates of the points where markers are to be +c drawn. These should be stored such that the value of +c coordinate number "coord" for input mark number "mark" is +c found in element "in[coord][mark]". +f A 2-dimensional array giving the physical coordinates of the +f points where markers are to be drawn. These should be +f stored such that the value of coordinate number COORD for +f input mark number MARK is found in element IN(MARK,COORD). +c type +f TYPE = INTEGER (Given) +* A value specifying the type (e.g. shape) of marker to be +* drawn. The set of values which may be used (and the shapes +* that will result) is determined by the underlying graphics +* system. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Markers are not drawn at positions which have any coordinate +* equal to the value AST__BAD (or where the transformation into +* graphical coordinates yields coordinates containing the value +* AST__BAD). +c - If any marker position is clipped (see astClip), then the +f - If any marker position is clipped (see AST_CLIP), then the +* entire marker is not drawn. +* - An error results if the base Frame of the Plot is not 2-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *mapping; /* Pointer to graphics->physical mapping */ + AstPointSet *pset1; /* PointSet holding physical positions */ + AstPointSet *pset2; /* PointSet holding graphics positions */ + const char *class; /* Object class */ + const char *method; /* Current method */ + const double **ptr1; /* Pointer to physical positions */ + double **ptr2; /* Pointer to graphics positions */ + double *xpd; /* Pointer to next double precision x value */ + double *ypd; /* Pointer to next double precision y value */ + double xx; /* X axis value */ + double yy; /* Y axis value */ + float *x; /* Pointer to single precision x values */ + float *xpf; /* Pointer to next single precision x value */ + float *y; /* Pointer to single precision y values */ + float *ypf; /* Pointer to next single precision y value */ + int axis; /* Axis index */ + int clip; /* Clips marks at plot boundary? */ + int i; /* Loop count */ + int naxes; /* No. of axes in the base Frame */ + int nn; /* Number of good marker positions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Store the current method and class for inclusion in error messages + generated by lower level functions. */ + method = "astMark"; + class = astClass( this ); + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Also validate the input array dimension argument. */ + if ( astOK && ( indim < nmark ) ) { + astError( AST__DIMIN, "%s(%s): The input array dimension value " + "(%d) is invalid.", status, method, class, indim ); + astError( AST__DIMIN, "This should not be less than the number of " + "markers being drawn (%d).", status, nmark ); + } + +/* Initialise the bounding box for primatives produced by this call. */ + if( !Boxp_freeze ) { + Boxp_lbnd[ 0 ] = FLT_MAX; + Boxp_lbnd[ 1 ] = FLT_MAX; + Boxp_ubnd[ 0 ] = FLT_MIN; + Boxp_ubnd[ 1 ] = FLT_MIN; + } + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__MARKS_ID, 1, GRF__MARK, method, class ); + +/* Create a PointSet to hold the supplied physical coordinates. */ + pset1 = astPointSet( nmark, ncoord, "", status ); + +/* Allocate memory to hold pointers to the first value on each axis. */ + ptr1 = (const double **) astMalloc( sizeof( const double * )* + (size_t)( ncoord )); + +/* Check the pointer can be used, then store pointers to the first value + on each axis. */ + if( astOK ){ + for( axis = 0; axis < ncoord; axis++ ){ + ptr1[ axis ] = in + axis*indim; + } + } + +/* Store these pointers in the PointSet. */ + astSetPoints( pset1, (double **) ptr1 ); + +/* Transform the supplied data from the current frame (i.e. physical + coordinates) to the base frame (i.e. graphics coordinates) using + the inverse Mapping defined by the Plot. */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + pset2 = Trans( this, NULL, mapping, pset1, 0, NULL, 0, method, class, status ); + mapping = astAnnul( mapping ); + +/* Get pointers to the graphics coordinates. */ + ptr2 = astGetPoints( pset2 ); + +/* Allocate memory to hold single precision versions of the graphics + coordinates. */ + x = (float *) astMalloc( sizeof( float )*(size_t) nmark ); + y = (float *) astMalloc( sizeof( float )*(size_t) nmark ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* Store pointers to the next single and double precision x and y + values. */ + xpf = x; + ypf = y; + xpd = ptr2[ 0 ]; + ypd = ptr2[ 1 ]; + +/* Convert the double precision values to single precision, rejecting + any bad marker positions. If clipping is switched on, also clip any + markers with centres outside the plotting area. */ + clip = astGetClip( this ) & 2; + nn = 0; + for( i = 0; i < nmark; i++ ){ + if( *xpd != AST__BAD && *ypd != AST__BAD ){ + xx = *(xpd++); + yy = *(ypd++); + if( !clip || ( xx >= this->xlo && xx <= this->xhi && + yy >= this->ylo && yy <= this->yhi ) ) { + nn++; + *(xpf++) = (float) xx; + *(ypf++) = (float) yy; + } + } else { + xpd++; + ypd++; + } + } + +/* Draw the remaining markers. */ + GMark( this, nn, x, y, type, method, class, status ); + + } + +/* Free the memory used to store single precision graphics coordinates. */ + x = (float *) astFree( (void *) x ); + y = (float *) astFree( (void *) y ); + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Free the memory holding the pointers to the first value on each axis. */ + ptr1 = (const double **) astFree( (void *) ptr1 ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__MARKS_ID, 0, GRF__MARK, method, class ); + +/* Return */ + return; +} + +static void Mirror( AstPlot *this, int axis, int *status ){ +/* +*+ +* Name: +* astMirror + +* Purpose: +* Flip a graphics axis of a Plot. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "plot.h" +* void astMirror( AstPlot *this, int axis ) + +* Class Membership: +* Plot method. + +* Description: +* This function referses the direction of a specified graphics axis +* in the Plot. + +* Parameters: +* this +* Pointer to a Plot. +* axis +* The zero-based axis of the axis to mirror. + +*- +*/ + +/* Check the global status. */ + if( !astOK ) return; + + if( axis == 0 ) { + this->xrev = ( this->xrev == 0 ); + + } else if( axis == 1 ){ + this->yrev = ( this->yrev == 0 ); + + } else { + astError( AST__INTER, "astMirror(%s): Illegal axis index (%d) " + "supplied (internal AST programming error).", status, + astGetClass( this ), axis ); + } +} + +static void Norm1( AstMapping *map, int axis, int nv, double *vals, + double refval, double width, int *status ){ +/* +* Name: +* Norm1 + +* Purpose: +* Use a Mapping to normalize an array of axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Norm1( AstMapping *map, int axis, int nv, double *vals, +* double refval, double width, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* The normalization of a position in physical space has two parts; +* firstly, the Mapping may determine a form of normalization; +* secondly, the Frame may provide an additional normalizion by the +* astNorm method. This function implements normalization using a +* Mapping, by transforming the physical position into Graphics position, +* and then back into a physical position. For instance, if the Mapping +* represents a mapping of Cartesian graphics axes onto a 2D polar +* coordinate system, a physical theta value of 3.PI will be normalized by +* the Mapping into a theta value of 1.PI (probably, but it depends on +* the Mapping). In this case, the Mapping normalization may well be the +* only normalization available, since the 2D polar coord. system will +* probably use a simple Frame to represent the (radius,theta) system, +* and a simple Frame defines no normalization (i.e. the astNorm method +* returns the supplied position unchanged). +* +* Complications arise though because it is not possible to normalise +* a single axis value - you can only normalize a complete position. +* Therefore some value must be supplied for the other axis. We +* should use the LabelAt value, but we do not yet know what the LabelAt +* value will be. Instead, we try first using the supplied "refval" +* which should be close to the mode of the other aixs values. Usually +* the value used is not very important. However, for some complex +* projections (such as quad-cubes, TSC, etc) the choice can be more +* critical since some positions on the ksy correspond to undefined +* graphics positions (e.g the face edges in a TSC projection). +* Therefore, if the supplied refval results in any positions being +* undefined we refine the process by transforming the undefined +* positaons again using a different refval. We do this twice to bump +* up the likelihood of finding a suitable reference value. + +* Parameters: +* mapping +* The Mapping from Graphics Frame to the current Frame. +* axis +* The index of the axis for which values are supplied in "vals". +* nv +* The number of values supplied in "vals". +* vals +* Pointer to an array of axis values. On exit they are normalized. +* refval +* The preffered constant value to use for the other axis when +* normalizing the values in "vals". +* width +* The range of used values for the other axis. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding physical coords */ + AstPointSet *pset2; /* PointSet holding graphics coords */ + double **ptr1; /* Pointer to physical coords data */ + double *a; /* Pointer to next axis value */ + double *b; /* Pointer to next axis value */ + int i; /* Loop count */ + int itry; /* Loop count for re-try loop */ + int nbad; /* No. of bad values found after transformation */ + int *flags; /* Pointer to flags array */ + +/* Check the inherited global status. */ + if( !astOK ) return; + +/* Store the supplied positions in a PointSet. */ + pset1 = astPointSet( nv, 2, "", status ); + ptr1 = astGetPoints( pset1 ); + if( astOK ) { + a = ptr1[ axis ]; + b = ptr1[ 1 - axis ]; + for( i = 0; i < nv; i++){ + *(a++) = vals[ i ]; + *(b++) = refval; + } + } + +/* Transform the supplied positions into the Base Frame. */ + pset2 = astTransform( map, pset1, 0, NULL ); + +/* Transform the Base Frame positions back into the Current Frame. */ + (void) astTransform( map, pset2, 1, pset1 ); + +/* Allocate memory to hold a flag for each position which is non-zero if + we currently have a good axis value to return for the position. */ + flags = (int *) astMalloc( sizeof(int)* (size_t) nv ); + +/* If good, store these values back in the supplied array. If the + transformed values are bad, retain the original good values for the + moment in "vals", and also copy the good values back into pset1. So + at the end, pset1 will contain the original good values at any points + which produced bad values after the above transformation - the other + points in pset1 will be bad. */ + nbad = 0; + if( astOK ) { + a = ptr1[ axis ]; + for( i = 0; i < nv; i++, a++ ){ + if( *a != AST__BAD ) { + vals[ i ] = *a; + *a = AST__BAD; + flags[ i ] = 1; + } else if( vals[ i ] != AST__BAD ) { + nbad++; + *a = vals[ i ]; + flags[ i ] = 0; + } else { + flags[ i ] = 1; + } + } + } + +/* We now try normalising any remaining bad positions using different + values for the other axis. This may result in some or all of the + remaining points being normalised succesfully. */ + for( itry = 0; itry < 10; itry++ ) { + +/* If the above transformation produced any bad values, try again with a + different value on the other axis. */ + if( astOK && nbad > 0 ) { + b = ptr1[ 1 - axis ]; + for( i = 0; i < nv; i++){ + *(b++) = refval + 0.1*( itry + 1 )*width; + } + +/* Transform to graphics coords and back to world coords. */ + (void) astTransform( map, pset1, 0, pset2 ); + (void) astTransform( map, pset2, 1, pset1 ); + +/* Copy any good positions back into the returned vals array. Count + remaining bad positions. */ + a = ptr1[ axis ]; + nbad = 0; + for( i = 0; i < nv; i++, a++ ){ + if( *a != AST__BAD ) { + vals[ i ] = *a; + flags[ i ] = 1; + *a = AST__BAD; + } else if( !flags[ i ] ) { + nbad++; + *a = vals[ i ]; + } + } + } + +/* If the above transformation produced any bad values, try again with a + different value on the other axis. */ + if( astOK && nbad > 0 ) { + b = ptr1[ 1 - axis ]; + for( i = 0; i < nv; i++){ + *(b++) = refval - 0.1*( itry + 1 )*width; + } + +/* Transform to graphics coords and back to world coords. */ + (void) astTransform( map, pset1, 0, pset2 ); + (void) astTransform( map, pset2, 1, pset1 ); + +/* Copy any good positions back into the returned vals array. Count + remaining bad positions. */ + a = ptr1[ axis ]; + nbad = 0; + for( i = 0; i < nv; i++, a++ ){ + if( *a != AST__BAD ) { + vals[ i ] = *a; + flags[ i ] = 1; + *a = AST__BAD; + } else if( !flags[ i ] ) { + nbad++; + *a = vals[ i ]; + } + } + } + } + +/* Free resources. */ + flags = (int *) astFree( flags ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +} + +static void Opoly( AstPlot *this, int *status ){ +/* +* Name: +* Opoly + +* Purpose: +* Draws the current poly line. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Opoly( AstPlot *this, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function draws the current poly line, and empties the buffer. + +* Parameters: +* this +* Pointer to the Plot. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int ipoly; /* Index of new polyline */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Draw the poly-line if needed. */ + if( Poly_n > 0 ) { + +/* Extend the global arrays that hold pointers to the polylines already + drawn. */ + ipoly = Poly_npoly++; + astBeginPM; + Poly_xp = astGrow( Poly_xp, Poly_npoly, sizeof(float*) ); + Poly_yp = astGrow( Poly_yp, Poly_npoly, sizeof(float*) ); + Poly_np = astGrow( Poly_np, Poly_npoly, sizeof(int) ); + astEndPM; + + if( astOK ) { + +/* Add pointers to the new polyline to the end of the above extended + arrays. */ + Poly_xp[ ipoly ] = Poly_x; + Poly_yp[ ipoly ] = Poly_y; + Poly_np[ ipoly ] = Poly_n; + +/* Indicate that the current polyline is now empty. */ + Poly_x = NULL; + Poly_y = NULL; + Poly_n = 0; + } + } +} + +static int Overlap( AstPlot *this, int mode, int esc, const char *text, float x, + float y, const char *just, float upx, float upy, + float **work, const char *method, const char *class, int *status ){ +/* +* Name: +* Overlap + +* Purpose: +* See if a major tick value label would overlap any of the previously +* drawn labels. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Overlap( AstPlot *this, int mode, int esc, const char *text, float x, +* float y, const char *just, float upx, float upy, +* float **work, const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* The operation of this function is determined by the "mode" parameter. + +* A record is kept of the bounding boxes enclosing all the displayed +* labels. If the bounding box of the new label defined by the given +* parameter values would overlap any of the old bounding boxes, 0 is +* returned. Otherwise 1 is returned and the bounding box for the new +* label is added to the list of old bounding boxes. + +* This function also updates the external variables Box_lbnd and +* Box_ubnd which hold the lower and upper bounds of the area enclosing +* all used labels. + +* Parameters: +* this +* A pointer to the Plot. +* mode +* - If -1, find the bounding box of the supplied label, add it +* to the list of stored bounding box, and return 1 if it overlaps +* any previously stored bounding boxes. +* - If -2, leave the bounding boxes unchanged and return the +* number of bounding boxes currently stored. No other action is taken +* and all other arguments are ignored. +* - Otherwise, reset the number of stored bounding boxes to the +* value of mode, and return the new number of bounding boxes. No +* action is taken if mode is less than zero or greater than the current +* number of stored boxes. No other action is taken and all other +* arguments are ignored. +* esc +* Should escape sequences in the text be interpreted? +* text +* A pointer to the label text string. +* x +* The graphics X coordinate of the label's reference point. +* y +* The graphics Y coordinate of the label's reference point. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. The first character may be 'T' for "top", +* 'C' for "centre", or 'B' for "bottom", and specifies the +* vertical location of the reference position. The second +* character may be 'L' for "left", 'C' for "centre", or 'R' +* for "right", and specifies the horizontal location of the +* reference position. If the string has less than 2 characters +* then 'C' is used for the missing characters. +* upx +* The x component of the up-vector for the text. +* upy +* The y component of the up-vector for the text. +* work +* A pointer to a place at which to store a pointer to an array of +* floats holding the old bounding boxes. Memory to hold this array +* is allocated automatically within this function. The pointer to +* the array should be supplied as NULL on the first call to this +* function, and the array should be freed using astFree when no +* longer needed. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* See parameter "mode." + +* Notes: +* - Zero is returned if an error has occurred, or if this function +* should fail for any reason. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int nbox = 0; /* Number of boxes stored in "work" */ + int ret; /* Does the new label overlap a previous label? */ + int i; /* Box index */ + float *cx; /* Pointer to next corner's X value */ + float *cy; /* Pointer to next corner's Y value */ + float xbn[ 4 ]; /* X coords at corners of new label's bounding box */ + float ybn[ 4 ]; /* Y coords at corners of new label's bounding box */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Initialise the returned value to indicate no overlap has been found. */ + ret = 0; + +/* Get the number of bounding boxes in the supplied work array. */ + if( work && *work ) { + nbox = (*work)[ 0 ]; + } else { + nbox = 0; + } + +/* If required, return the number of bounding boxes currently stored. */ + if( mode == -2 ) return nbox; + +/* If required, reset the number of bounding boxes currently stored, and + return the new number. */ + if( mode >= 0 ) { + if( mode < nbox && work && *work ) { + nbox = mode; + (*work)[ 0 ] = nbox; + } + return nbox; + } + +/* If no work array has been supplied, allocate one now with room for + 10 boxes. Each box requires 8 floats, 2 for each of the 4 corners. The + X graphics coordinates at the 4 corners are stored in the first 4 floats, + and the corresponding Y graphics coordinates in the second group of 4 + floats. */ + if( work && !(*work) ) { + *work = (float *) astMalloc( 81*sizeof(float) ); + if( astOK ) { + nbox = 0; + (*work)[ 0 ] = 0; + } + } + +/* Check the global status. */ + if( !astOK ) return ret; + +/* Get the bounds of the box containing the new label. */ + DrawText( this, 0, esc, text, x, y, just, upx, upy, + xbn, ybn, NULL, method, class, status ); + +/* If the bounding box was obtained succesfully... */ + if( astOK ) { + +/* Check for an overlap between the box and each of the previous boxes. */ + cx = *work + 1; + cy = cx + 4; + for( i = 0; i < nbox; i++ ){ + + if( BoxCheck( xbn, ybn, cx, cy, status ) ) { + ret = 1; + break; + } + +/* Increment the pointers to the next box. */ + cx += 8; + cy += 8; + + } + +/* If no overlap was found, add the new box to the list. */ + if( !ret ){ + *work = (float *) astGrow( (void *) *work, 8*nbox + 9, sizeof(float) ); + cx = *work + 1 + 8*nbox; + cy = cx + 4; + for( i = 0; i < 4; i++ ){ + cx[ i ] = xbn[ i ]; + cy[ i ] = ybn[ i ]; + } + (*work)[ 0 ]++; + +/* Extend the bounds of the global bounding box held externally to include + the new box. */ + for( i = 0; i < 4; i++ ){ + Box_lbnd[ 0 ] = astMIN( xbn[ i ], Box_lbnd[ 0 ] ); + Box_ubnd[ 0 ] = astMAX( xbn[ i ], Box_ubnd[ 0 ] ); + Box_lbnd[ 1 ] = astMIN( ybn[ i ], Box_lbnd[ 1 ] ); + Box_ubnd[ 1 ] = astMAX( ybn[ i ], Box_ubnd[ 1 ] ); + } + } + } + +/* If an error has occur, return a value of 0. */ + if( !astOK ) ret = 0; + +/* Return the answer. */ + return ret; + +} + +static void PlotLabels( AstPlot *this, int esc, AstFrame *frame, int axis, + LabelList *list, char *fmt, int nlab, float **box, + const char *method, const char *class, int *status ) { +/* +* +* Name: +* PlotLabels + +* Purpose: +* Draws the numerical labels which have been selected for display. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void PlotLabels( AstPlot *this, int esc, AstFrame *frame, int axis, +* LabelList *list, char *fmt, int nlab, float **box, +* const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function displays the numerical labels supplied in the +* structure pointed to by "list". Overlapping labels are omitted, +* and redundant leading fields are removed from adjacent labels. +* Nothing is plotted if the NumLab attribute for the axis is false. + +* Parameters: +* this +* A pointer to the Plot. +* esc +* Interpret escape sequences in labels? +* frame +* A pointer to the current Frame of the Plot. +* axis +* The axis index (0 or 1). +* list +* A pointer to the LabelList structure holding information about +* the selected numerical labels. +* fmt +* A pointer to a null terminated string holding the format +* specification used to create the labels. +* nlab +* The number of labels described by "list". +* box +* A pointer to a place at which to store a pointer to an array of +* floats holding the bounding boxes of displayed labels. Memory to +* hold this array is allocated automatically within this function. +* The pointer to the array should be supplied as NULL on the first +* call to this function, and the array should be freed using astFree +* when no longer needed. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + LabelList *ll; /* Pointer to next label structure */ + LabelList *llhi; /* Pointer to higher neighbouring label structure */ + LabelList *lllo; /* Pointer to lower neighbouring label structure */ + char *text; /* Pointer to label text */ + const char *latext; /* Axis label at previous label */ + const char *texthi; /* Pointer to text abbreviated with higher neighbour */ + const char *textlo; /* Pointer to text abbreviated with lower neighbour */ + float tolx; /* Min allowed X interval between labels */ + float toly; /* Min allowed Y interval between labels */ + float xbn[ 4 ]; /* X coords at corners of new label's bounding box */ + float ybn[ 4 ]; /* Y coords at corners of new label's bounding box */ + int abb; /* Abbreviate leading fields? */ + int dp; /* Number of decimal places */ + int found; /* Non-zero digit found? */ + int hilen; /* Length of texthi */ + int i; /* Label index */ + int j; /* Label index offset */ + int jgap; /* Gap in index between rejected labels */ + int lab0; /* Index of middle label */ + int lolen; /* Length of textlo */ + int mxdp; /* Maximum number of decimal places */ + int nbox; /* The number of boinding boxes supplied */ + int nexti; /* Index of next label to retain */ + int nleft; /* No. of labels left */ + int nz; /* Number of trailing zeros in this label */ + int nzmax; /* Max. number of trailing zeros */ + int odd; /* DO we have a strange axis? */ + int off; /* Offset from central label */ + int olap; /* Any overlap found? */ + int prio; /* Current priority */ + int root; /* Index of unabbreviated label */ + int root_found; /* Has the root label been decided on? */ + int rootoff; /* Distance from middle to root label */ + int split; /* Indicates whether to split labels into 2 lines */ + +/* Return without action if an error has occurred, or there are no labels to + draw. */ + if( !astOK || nlab == 0 || !list || !astGetNumLab( this, axis ) ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + rootoff = 0; + +/* Get the number of bounding boxes describing the labels already drawn + (this will be non-zero only if this is the second axis to be labelled). */ + nbox = Overlap( this, -2, 0, NULL, 0.0, 0.0, NULL, 0.0, 0.0, box, method, + class, status ); + +/* Ensure the labels are sorted into increasing index order. */ + qsort( (void *) list, (size_t) nlab, sizeof(LabelList), Compare_LL ); + +/* Complex curves can have multiple edge crossings very close together. + This means that the same label can sometimes be included more than once + in the supplied list at the same (x,y) position. Purge duplicate labels + by setting their priority to -1. Initialise the priority of the remaining + labels to zero. */ + tolx = 0.02*fabs( this->xhi - this->xlo ); + toly = 0.02*fabs( this->yhi - this->ylo ); + ll = list; + ll->priority = 0; + ll->saved_prio = 0; + + for( i = 1; i < nlab; i++ ) { + ll++; + ll->priority = 0; + ll->saved_prio = 0; + for( j = 0; j < i; j++ ){ + if( !strcmp( ll->text, list[ j ].text ) ) { + if( fabs( ll->x - list[ j ].x ) < tolx && + fabs( ll->y - list[ j ].y ) < toly ) { + ll->priority = -1; + ll->saved_prio = -1; + break; + } + } + } + } + +/* Find the maximum number of decimal places in any label. */ + mxdp = 0; + ll = list - 1; + for( i = 0; i < nlab; i++ ) { + ll++; + FindDPTZ( frame, axis, fmt, ll->text, &dp, &nz, status ); + if( dp > mxdp ) mxdp = dp; + } + +/* Indicate that we do not yet know whether SplitValue should split labels + into two lines or not. */ + split = 0; + +/* Find the highest priority label (the "root" label). This label is + never abbreviated to remove leading fields, and is never omitted due to + overlaps with other labels. To find this label, each label is assigned a + priority equal to the number of trailing zeros in the label text. If the + text has fewer than the maximum number of decimal places, pretend the text + is padded with trailing zeros to bring the number of decimal places up to + the maximum. The root label is the highest priority label, giving + preference to labels which occur in the middle of the index order. At the + same time, initialize the abbreviated text for each label to be equal to + the unabbreviated text. */ + lab0 = nlab/2; + nzmax = -1; + ll = list - 1; + root = -1; + root_found = 0; + for( i = 0; i < nlab; i++ ) { + ll++; + if( ll->priority > -1 ) { + text = ll->text; + +/* Find the number of decimal places and the number of trailing zeros in + this label. Note if a non-zero digit was found in the label. */ + found = FindDPTZ( frame, axis, fmt, text, &dp, &nz, status ); + +/* Add on some extra trailing zeros to make the number of decimal places + up to the maximum value. */ + nz += mxdp - dp; + +/* Store the priority for this label. */ + ll->priority = nz; + ll->saved_prio = nz; + +/* Note the highest priority of any label. */ + if( nz > nzmax ) nzmax = nz; + +/* We will use this label as the root label if: + + - We have not already found the root label + + AND + + - It does not overlap any labels drawn for a previous axis + + AND + + - We do not currently have a candidate root label, or + - The priority for this label is higher than the priority of the current + root label, or + - The priority for this label is equal to the priority of the current + root label and this label is closer to the centre of the axis, or + - The label value is zero. */ + + if( root == -1 || + nz > list[ root ].priority || + ( nz == list[ root ].priority && abs( i - lab0 ) < rootoff ) || + !found ) { + + if( !root_found ) { + + if( axis == 0 || !Overlap( this, -1, esc, + SplitValue( this, ll->text, + axis, &split, status ), + (float) ll->x, (float) ll->y, + ll->just, (float) ll->upx, + (float) ll->upy, box, method, + class, status ) ) { + root = i; + rootoff = abs( i - lab0 ); + +/* If the label value was zero, we will use label as the root label, + regardless of the priorities of later labels. */ + if( !found ) root_found = 1; + } + +/* Reset the list of bounding boxes to exclude any box added above. */ + Overlap( this, nbox, esc, NULL, 0.0, 0.0, NULL, 0.0, 0.0, box, + method, class, status ); + + } + } + } + +/* Initialise the abbreviated text to be the same as the full text. */ + ll->atext = ll->text; + } + +/* If all the labels overlapped labels on a previous axis, arbitrarily + use the label with non-genative priority that is closest to the middle + as the root label (this should never happen but is included to avoid + segmentation violations occurring in error conditions such as the + txExt function being buggy and cuasing spurious overlaps). */ + if( root == -1 ) { + for( off = 0; off < (nlab-1)/2; off++ ) { + root = nlab/2 + off; + if( list[ root ].priority >= 0 ) break; + root = nlab/2 - off; + if( list[ root ].priority >= 0 ) break; + } + if( root == -1 ) { + astError( AST__PLFMT, "%s(%s): Cannot produce labels for axis %d.", + status, method, class, axis + 1 ); + root = nlab/2; + } + } + +/* Assign a priority higher than any other priority to the root label. */ + list[ root ].priority = nzmax + 1; + list[ root ].saved_prio = nzmax + 1; + +/* See if leading fields are to be abbreviated */ + abb = astGetAbbrev( this, axis ); + +/* The following process may have removed some labels which define the + missing fields in neighbouring abbreviated fields, so that the user + would not be able to tell what value the abbvreviated leading fields + should have. We therefore loop back and perform the abbreviation + process again, omitting the removed labels this time. Continue doing + this until no further labels are removed. */ + jgap = 1; + olap = 1; + odd = 0; + while( olap && !odd ) { + +/* We now attempt to abbreviate the remaining labels (i.e. those which + have not been rejected on an earlier pass through this loop). Labels + are abbreviated in order of their priority. Higher priority labels are + abbreviated first (except that the root label, which has the highest + priority, is never abbreviated). Each label is abbreviated by comparing + it with the nearest label with a higher priority. */ + +/* Loop through all the priority values, starting with the highest + priority (excluding the root label so that the root label is never + abbreviated), and working downwards to finish with zero priority. */ + prio = nzmax + 1; + while( prio-- > 0 ) { + +/* Look for labels which have the current priority. */ + ll = list - 1; + for( i = 0; i < nlab; i++ ) { + ll++; + if( ll->priority == prio ) { + +/* Find the closest label to this one on the high index side which has a + higher priority. */ + llhi = NULL; + for( j = i + 1; j < nlab; j++ ) { + if( list[ j ].priority > prio ) { + llhi = list + j; + break; + } + } + +/* If no higher priority neighbour was found on the high index side, + use the nearest label with the current priority on the high index side. */ + if( !llhi ) { + for( j = i + 1; j < nlab; j++ ) { + if( list[ j ].priority == prio ) { + llhi = list + j; + break; + } + } + } + +/* Find the closest label to this one on the low index side which has a + higher priority. */ + lllo = NULL; + for( j = i - 1; j >= 0; j-- ) { + if( list[ j ].priority > prio ) { + lllo = list + j; + break; + } + } + +/* If no higher priority neighbour was found on the low index side, + use the nearest label with the current priority on the low index side. */ + if( !lllo ) { + for( j = i - 1; j >= 0; j-- ) { + if( list[ j ].priority == prio ) { + lllo = list + j; + break; + } + } + } + +/* If we are not abbreviating, use the full text as the abbreviated text.*/ + if( !abb ) { + ll->atext = ll->text; + +/* Otherwise, if only one of these two neighbouring labels was found, we + abbreviate the current label by comparing it with the one found + neighbouring label. If they are identical, we use the last field as + the abbreviated text. */ + } else if( !lllo ) { + ll->atext = astAbbrev( frame, axis, fmt, llhi->text, + ll->text ); + + } else if( !llhi ) { + ll->atext = astAbbrev( frame, axis, fmt, lllo->text, + ll->text ); + +/* If two neighbouring labels were found, we abbreviate the current label + by comparing it with both neighbouring labels, and choosing the shorter + abbreviation. */ + } else { + textlo = abb ? astAbbrev( frame, axis, fmt, lllo->text, + ll->text ) : ll->text; + texthi = abb ? astAbbrev( frame, axis, fmt, llhi->text, + ll->text ) : ll->text; + + lolen = strlen( textlo ); + hilen = strlen( texthi ); + if( lolen > 0 && lolen < hilen ) { + ll->atext = textlo; + } else { + ll->atext = texthi; + } + } + +/* If the two fields are identical, the abbreviated text returned by + astAbbrev will be a null string. In this case, find the start of the + last field in the formatted value (using astAbbrev again), and use + that as the abbreviated text. */ + if( !(ll->atext)[0] ) { + ll->atext = astAbbrev( frame, axis, fmt, NULL, ll->text ); + } + } + } + } + +/* Find the bounding box of the root label and add it to the list of bounding + boxes. */ + nleft = 1; + ll = list + root; + olap = Overlap( this, -1, esc, + SplitValue( this, ll->atext, axis, &split, status ), + (float) ll->x, (float) ll->y, ll->just, (float) ll->upx, + (float) ll->upy, box, method, class, status ); + +/* Now look for labels which would overlap. First, check labels above the root + label. Do not count overlaps where the two abbreviated labels have the same text. */ + ll = list + root; + latext = ll->atext; + for( i = root + 1; i < nlab; i++ ) { + ll++; + if( ll->priority >= 0 ) { + if( strcmp( ll->atext, latext ) ) { + if( Overlap( this, -1, esc, + SplitValue( this, ll->atext, axis, &split, status ), + (float) ll->x, (float) ll->y, ll->just, + (float) ll->upx, (float) ll->upy, box, method, + class, status ) ){ + olap++; + } else { + nleft++; + } + } + latext = ll->atext; + } + } + +/* Now check the labels below the root label. */ + ll = list + root; + latext = ll->atext; + for( i = root - 1; i >= 0; i-- ) { + ll--; + if( ll->priority >= 0 ) { + if( strcmp( ll->atext, latext ) ) { + if( Overlap( this, -1, esc, + SplitValue( this, ll->atext, axis, &split, status ), + (float) ll->x, (float) ll->y, ll->just, + (float) ll->upx, (float) ll->upy, box, method, + class, status ) ){ + olap++; + } else { + nleft++; + } + } + latext = ll->atext; + } + } + +/* If only one overlap was found, and this is the second axis, ignore it + since it is probably caused by the crossing of the two axes. */ + if( axis == 1 && olap == 1 ) olap = 0; + +/* If we are now only plotting every 3rd label, or if there are less than + 3 labels left, and there are still overlapping labels, then we must have + a very odd axis (such as logarithmically spaced ticks on a linearly mapped + axis). In this case, we will re-instate the orignal label priorities and + then leave this loop so that we attempt to plot all labels. Also retain + original priorities if the axis is mapped logarithmically onto the + screen. */ + if( olap && ( jgap == 3 || nleft < 3 || astGetLogPlot( this, axis ) ) ){ + jgap = 0; + odd = 1; + } else { + odd = 0; + } + +/* If any labels overlapped, re-instate the priority of all previously + excluded labels (using the copy of the label's real priority stored in + "saved_prio"), and then remove labels (by setting their priorities + negative) to increase the gap between labels, and try again. */ + if( olap ) { + jgap++; + + nexti = root + jgap; + for( i = root + 1; i < nlab; i++ ) { + if( i == nexti ) { + nexti += jgap; + list[ i ].priority = list[ i ].saved_prio; + } else { + list[ i ].priority = -1; + } + } + + nexti = root - jgap; + for( i = root - 1; i >= 0; i-- ) { + if( i == nexti ) { + nexti -= jgap; + list[ i ].priority = list[ i ].saved_prio; + } else { + list[ i ].priority = -1; + } + } + +/* Reset the abbreviated text to be the full text. */ + for( i = 0; i < nlab - 1; i++ ) list[ i ].atext = list[ i ].text; + + } + +/* Rest the list of bounding boxes to exclude the boxes added above. */ + Overlap( this, nbox, esc, NULL, 0.0, 0.0, NULL, 0.0, 0.0, box, method, + class, status ); + } + +/* We can now draw the abbreviated labels (ignoring rejected labels). */ + ll = list-1; + for( i = 0; i < nlab; i++ ) { + ll++; + if( ll->priority >= 0 ) { + +/* Check that this label does not overlap any labels drawn for previous + axes (we know from the above processing that it will not overlap any + other label on the current axis). */ + if( !Overlap( this, -1, esc, + SplitValue( this, ll->atext, axis, &split, status ), + (float) ll->x, (float) ll->y, ll->just, (float) ll->upx, + (float) ll->upy, box, method, class, status ) ) { + +/* Draw the abbreviated label text, and get the bounds of the box containing + the new label, splitting long formatted values (such as produced by + TimeFrames) into two lines. */ + DrawText( this, 1, esc, + SplitValue( this, ll->atext, axis, &split, status ), + (float) ll->x, (float) ll->y, ll->just, (float) ll->upx, + (float) ll->upy, xbn, ybn, NULL, method, class, status ); + } + } + } +} + +static void PolyCurve( AstPlot *this, int npoint, int ncoord, int indim, + const double *in, int *status ){ +/* +*++ +* Name: +c astPolyCurve +f AST_POLYCURVE + +* Purpose: +* Draw a series of connected geodesic curves. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astPolyCurve( AstPlot *this, int npoint, int ncoord, int indim, +c const double *in ) +f CALL AST_POLYCURVE( THIS, NPOINT, NCOORD, INDIM, IN, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +c This function joins a series of points specified in the physical +c coordinate system of a Plot by drawing a sequence of geodesic +c curves. It is equivalent to making repeated use of the astCurve +c function (q.v.), except that astPolyCurve will generally be more +c efficient when drawing many geodesic curves end-to-end. A +c typical application of this might be in drawing contour lines. +f This routine joins a series of points specified in the physical +f coordinate system of a Plot by drawing a sequence of geodesic +f curves. It is equivalent to making repeated calls to the +f AST_CURVE routine (q.v.), except that AST_POLYCURVE will +f generally be more efficient when drawing many geodesic curves +f end-to-end. A typical application of this might be in drawing +f contour lines. +* +c As with astCurve, full account is taken of the Mapping between +c physical and graphical coordinate systems. This includes any +c discontinuities and clipping established using astClip. +f As with AST_CURVE, full account is taken of the Mapping between +f physical and graphical coordinate systems. This includes any +f discontinuities and clipping established using AST_CLIP. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c npoint +f NPOINT = INTEGER (Given) +* The number of points between which geodesic curves are to be drawn. +c ncoord +f NCOORD = INTEGER (Given) +* The number of coordinates being supplied for each point (i.e. +* the number of axes in the current Frame of the Plot, as given +* by its Naxes attribute). +c indim +f INDIM = INTEGER (Given) +c The number of elements along the second dimension of the "in" +f The number of elements along the first dimension of the IN +* array (which contains the input coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +c given should not be less than "npoint". +f given should not be less than NPOINT. +c in +f IN( INDIM, NCOORD ) = DOUBLE PRECISION (Given) +c The address of the first element in a 2-dimensional array of shape +c "[ncoord][indim]" giving the +c physical coordinates of the points which are to be joined in +c sequence by geodesic curves. These should be stored such that +c the value of coordinate number "coord" for point number +c "point" is found in element "in[coord][point]". +f A 2-dimensional array giving the physical coordinates of the +f points which are to be joined in sequence by geodesic +f curves. These should be stored such that the value of +f coordinate number COORD for input point number POINT is found +f in element IN(POINT,COORD). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - No curve is drawn on either side of any point which has any +* coordinate equal to the value AST__BAD. +* - An error results if the base Frame of the Plot is not +* 2-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*-- +*/ +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const char *class; /* Object class */ + const char *method; /* Current method */ + const double **in_ptr; /* Pointer to array of data pointers */ + double *finish; /* Pointer to array holding segment end position */ + double *start; /* Pointer to array holding segment start position */ + double d[ CRV_NPNT ]; /* Offsets to evenly spaced points along curve */ + double tol; /* Absolute tolerance value */ + double x[ CRV_NPNT ]; /* X coords at evenly spaced points along curve */ + double y[ CRV_NPNT ]; /* Y coords at evenly spaced points along curve */ + int coord; /* Loop counter for coordinates */ + int i; /* Loop count */ + int naxes; /* No. of Frame axes */ + int ok; /* Are all start and end coords good? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astPolyCurve"; + class = astGetClass( this ); + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Initialise the bounding box for primatives produced by this call. */ + if( !Boxp_freeze ) { + Boxp_lbnd[ 0 ] = FLT_MAX; + Boxp_lbnd[ 1 ] = FLT_MAX; + Boxp_ubnd[ 0 ] = FLT_MIN; + Boxp_ubnd[ 1 ] = FLT_MIN; + } + +/* Check the current Frame of the Plot has ncoord axes. */ + naxes = astGetNout( this ); + if( naxes != ncoord && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the current " + "Frame of the supplied %s is invalid - this number should " + "be %d (possible programming error).", status, method, class, naxes, + class, ncoord ); + } + +/* Check the array dimension argument. */ + if ( astOK && ( indim < npoint ) ) { + astError( AST__DIMIN, "%s(%s): The array dimension value " + "(%d) is invalid.", status, method, class, indim ); + astError( AST__DIMIN, "This should not be less than the number of " + "points being drawn (%d).", status, npoint ); + } + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Allocate memory to hold the array of data pointers, the start position, + and the end position. */ + if ( astOK ) { + in_ptr = (const double **) astMalloc( sizeof( const double * ) * + (size_t) ncoord ); + start = (double *) astMalloc( sizeof( double ) * (size_t) ncoord ); + finish = (double *) astMalloc( sizeof( double ) * (size_t) ncoord ); + +/* Set up externals used to communicate with the Map3 function... + The number of axes in the physical coordinate system (i.e. the current + Frame). */ + Map3_ncoord = ncoord; + +/* A pointer to the Plot, the Current Frame, and and Mapping. */ + Map3_plot = this; + Map3_frame = astGetFrame( this, AST__CURRENT ); + Map3_map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Convert the tolerance from relative to absolute graphics coordinates. */ + tol = astGetTol( this )*astMAX( this->xhi - this->xlo, + this->yhi - this->ylo ); + +/* Ensure the globals holding the scaling from graphics coords to equally + scaled coords are available. */ + GScales( this, NULL, NULL, method, class, status ); + +/* Now set up the external variables used by the Crv and CrvLine function. */ + Crv_scerr = ( astGetLogPlot( this, 0 ) || + astGetLogPlot( this, 1 ) ) ? 100.0 : 1.5; + Crv_tol = tol; + Crv_limit = 0.5*tol*tol; + Crv_map = Map3; + Crv_ink = 1; + Crv_xlo = this->xlo; + Crv_xhi = this->xhi; + Crv_ylo = this->ylo; + Crv_yhi = this->yhi; + Crv_clip = astGetClip( this ) & 1; + +/* Set up a list of points spread evenly over each curve segment. */ + for( i = 0; i < CRV_NPNT; i++ ){ + d[ i ] = ( (double) i)/( (double) CRV_NSEG ); + } + +/* Initialise the data pointers to locate the coordinate data in + the "in" array. */ + if ( astOK ) { + for ( coord = 0; coord < ncoord; coord++ ) { + in_ptr[ coord ] = in + coord * indim; + } + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__CURVE_ID, 1, GRF__LINE, method, class ); + +/* Loop round each curve segment. */ + for( i = 1 ; i < npoint; i++ ) { + +/* Store the start position and check it for bad values. Increment the + pointers to the next position on each axis, so that they refer to the + finish point of the current curve segment. */ + ok = 1; + for( coord = 0; coord < ncoord; coord++ ) { + if( *( in_ptr[coord] ) == AST__BAD ){ + ok = 0; + } else { + start[ coord ] = *( in_ptr[coord] ); + } + ( in_ptr[coord] )++; + } + +/* Store the end position and check it for bad values. Do not increment + the axis pointers. This means that they will refer to the start position + of the next curve segment on the next pass through this loop. */ + for( coord = 0; coord < ncoord; coord++ ) { + if( *( in_ptr[coord] ) == AST__BAD ){ + ok = 0; + } else { + finish[ coord ] = *( in_ptr[coord] ); + } + } + +/* Pass on to the next curve segment if either the start or finish position + was bad. */ + if( ok ) { + +/* Set up the remaining externals used to communicate with the Map3 + function... */ + +/* The physical coordinates at the start of the curve. */ + Map3_origin = start; + +/* The physical coordinates at the end of the curve. */ + Map3_end = finish; + +/* The scale factor to convert "dist" values into physical offset values. */ + Map3_scale = astDistance( Map3_frame, start, finish ); + +/* Now set up the remaining external variables used by the Crv and CrvLine + function. */ + Crv_ux0 = AST__BAD; + Crv_out = 1; + Crv_xbrk = Curve_data.xbrk; + Crv_ybrk = Curve_data.ybrk; + Crv_vxbrk = Curve_data.vxbrk; + Crv_vybrk = Curve_data.vybrk; + +/* Map the evenly spread distances between "start" and "finish" into graphics + coordinates. */ + Map3( CRV_NPNT, d, x, y, method, class, status GLOBALS_NAME ); + +/* Use Crv and Map3 to draw the curve segment. */ + Crv( this, d, x, y, 0, NULL, NULL, method, class, status ); + +/* If no part of the curve could be drawn, set the number of breaks and the + length of the drawn curve to zero. */ + if( Crv_out ) { + Crv_nbrk = 0; + Crv_len = 0.0F; + +/* Otherwise, add an extra break to the returned structure at the position of + the last point to be plotted. */ + } else { + Crv_nbrk++; + if( Crv_nbrk > AST__PLOT_CRV_MXBRK ){ + astError( AST__CVBRK, "%s(%s): Number of breaks in curve " + "exceeds %d.", status, method, class, AST__PLOT_CRV_MXBRK ); + } else { + *(Crv_xbrk++) = (float) Crv_xl; + *(Crv_ybrk++) = (float) Crv_yl; + *(Crv_vxbrk++) = (float) -Crv_vxl; + *(Crv_vybrk++) = (float) -Crv_vyl; + } + } + +/* Store extra information about the curve in the returned structure, and + purge any zero length sections. */ + Curve_data.length = Crv_len; + Curve_data.out = Crv_out; + Curve_data.nbrk = Crv_nbrk; + PurgeCdata( &Curve_data, status ); + } + } + +/* End the last poly line. */ + Opoly( this, status ); + +/* Tidy up the static data used by Map3. */ + Map3( 0, NULL, NULL, NULL, method, class, status GLOBALS_NAME ); + +/* Ensure all lines are flushed to the graphics system. */ + Fpoly( this, method, class, status ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__CURVE_ID, 0, GRF__LINE, method, class ); + } + +/* Annul the Frame and Mapping. */ + Map3_frame = astAnnul( Map3_frame ); + Map3_map = astAnnul( Map3_map ); + +/* Free the memory used for the data pointers, and start and end positions. */ + in_ptr = (const double **) astFree( (void *) in_ptr ); + start = (double *) astFree( (void *) start ); + finish = (double *) astFree( (void *) finish ); + } +} + +static int PopGat( AstPlot *this, float *rise, const char *method, + const char *class, int *status ) { +/* +* Name: +* PopGat + +* Purpose: +* Pop current graphical attributes for text from a stack. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int PopGat( AstPlot *this, float *rise, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function restores the current graphical attributes for text +* from the values on a stack. Current attributes are left unchanged if +* the stack is empty. + +* Parameters: +* this +* Pointer to the Plot. +* rise +* Pointer to a location at which to return thhe height of the baseline +* above the normal baseline, expressed as a percentage of the normal +* character height. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Returns zero if the stack is empty, and 1 otherwise. + +*/ + +/* Local Variables: */ + AstGat *gat; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Check there is at least one AstGat structure on the stack. */ + if( this->ngat ) { + +/* Decrement the number of entries in the stack, and get a pointer to the + AstGat structure. Nullify the pointer on the stack. */ + gat = (this->gat)[ --(this->ngat) ]; + (this->gat)[ this->ngat ] = NULL; + +/* Restore the values in the AstGat structure */ + *rise = gat->rise; + GAttr( this, GRF__SIZE, gat->size, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__WIDTH, gat->width, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__COLOUR, gat->col, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__FONT, gat->font, NULL, GRF__TEXT, method, class, status ); + GAttr( this, GRF__STYLE, gat->style, NULL, GRF__TEXT, method, class, status ); + +/* Free the AstGat structure. */ + gat = astFree( gat ); + +/* Indicate success.*/ + result = 1; + } + +/* Return the result. */ + return result; + +} + +static void PurgeCdata( AstPlotCurveData *cdata, int *status ){ +/* +* +* Name: +* AstPlotCurveData + +* Purpose: +* Remove any zero length sections from the description of a curve. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void PurgeCdata( AstPlotCurveData *cdata ) + +* Class Membership: +* Plot member function. + +* Description: +* This function removes any zero length sections from the supplied +* AstPlotCurveData struture, which describes a multi-section curve. + +* Parameters: +* cdata +* A pointer to the structure containing information about the +* breaks in a curve. + +*/ + +/* Local Variables: */ + int brk; /*Break index */ + int i; /*Break index */ + +/* Check the global error status. */ + if ( !astOK || !cdata ) return; + +/* Loop round all breaks. */ + brk = 0; + while( brk < cdata->nbrk ) { + +/* If this break and the next one are co-incident, remove both breaks. */ + if( cdata->xbrk[ brk ] == cdata->xbrk[ brk + 1 ] && + cdata->ybrk[ brk ] == cdata->ybrk[ brk + 1 ] ) { + +/* Shuffle down the higher elements of all the arrays in the curve data. */ + for( i = brk + 2; i < cdata->nbrk; i++ ){ + cdata->xbrk[ i - 2 ] = cdata->xbrk[ i ]; + cdata->ybrk[ i - 2 ] = cdata->ybrk[ i ]; + cdata->vxbrk[ i - 2 ] = cdata->vxbrk[ i ]; + cdata->vybrk[ i - 2 ] = cdata->vybrk[ i ]; + } + +/* Decrement the number of breaks in the curve data. */ + cdata->nbrk -= 2; + +/* If the section is not zero length, move on to the next pair of breaks. */ + } else { + brk += 2; + } + } +} + +static void PushGat( AstPlot *this, float rise, const char *method, + const char *class, int *status ) { +/* +* Name: +* PushGat + +* Purpose: +* Push current graphical attributes for text onto a stack. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void PushGat( AstPlot *this, float rise, const char *method, +* const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function stores the current graphical attributes for text +* on a stack. + +* Parameters: +* this +* Pointer to the Plot. +* rise +* The height of the baseline above the normal baseline, expressed +* as a percentage of the normal character height. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstGat *new_gat; + +/* Check inherited status */ + if( !astOK ) return; + +/* Allocate memory for a new AstGat structure to store the graphical + attributes. */ + new_gat = astMalloc( sizeof( AstGat ) ); + if( astOK ) { + +/* Store the height of the current baseline above the normal baseline, + expressed as a percentage of a normal character height. */ + new_gat->rise = rise; + +/* Store the current graphical attribute values. */ + GAttr( this, GRF__SIZE, AST__BAD, &(new_gat->size), GRF__TEXT, method, class, status ); + GAttr( this, GRF__WIDTH, AST__BAD, &(new_gat->width), GRF__TEXT, method, class, status ); + GAttr( this, GRF__FONT, AST__BAD, &(new_gat->font), GRF__TEXT, method, class, status ); + GAttr( this, GRF__STYLE, AST__BAD, &(new_gat->style), GRF__TEXT, method, class, status ); + GAttr( this, GRF__COLOUR, AST__BAD, &(new_gat->col), GRF__TEXT, method, class, status ); + +/* Increment the number of AstGat structures on the stack.*/ + this->ngat++; + +/* Extend the array of AstGat pointers in the Plot structure so that there + is room for the new one. */ + this->gat = (AstGat **) astGrow( this->gat, this->ngat, sizeof( AstGat * ) ); + if( astOK ) { + +/* Store the new pointer. */ + (this->gat)[ this->ngat - 1 ] = new_gat; + + } + } +} + +static void RegionOutline( AstPlot *this, AstRegion *region, int *status ){ +/* +*++ +* Name: +c astRegionOutline +f AST_RegionOutline + +* Purpose: +* Draw the outline of an AST Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astRegionOutline( AstPlot *this, AstRegion *region ) +f CALL AST_REGIONOUTLINE( THIS, REGION, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +* This function draws an outline around the supplied AST Region object. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c region +f REGION = INTEGER (Given) +* Pointer to the Region. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ +/* Local Variables: */ + AstFrameSet *fs; + AstMapping *map; + const char *class; + const char *method; + int ibase; + int icurr; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astRegionOutline"; + class = astGetClass( this ); + +/* Save the base Frame index within the Plot, since astConvert will + change it. */ + ibase = astGetBase( this ); + +/* Get the FrameSet that converts from the current Frame of the Plot, to the + Frame represented by the Region. Check a conversion was found. */ + fs = astConvert( this, region, " " ); + +/* Re-instate the original base Frame. */ + astSetBase( this, ibase ); + +/* Report an error if the Region could not be mapped into the current + Frame of the Plot. */ + if( !fs ) { + if( astOK ) { + astError( AST__NOCNV, "%s(%s): Cannot find a mapping from the " + "%d-dimensional Plot coordinate system (%s) to the " + "%d-dimensional Region coordinate system (%s).", status, + method, class, astGetNout( this ), astGetTitle( this ), + astGetNout( region ), astGetTitle( region ) ); + } + +/* If a transformation from Plot to Region was found... */ + } else { + +/* Add the Region as a new Frame into the Plot, connecting it to the + current Frame using the FrameSet found above. It becomes the new current + Frame. First record the index of the original current Frame since + astAddFrame will modify the FrameSet to use a different current Frame. */ + icurr = astGetCurrent( this ); + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + astAddFrame( this, icurr, map, region ); + +/* Draw the outline of the Region (now the current Frame in the Plot). */ + astBorder( this ); + +/* Tidy up by removing the Region (i.e. the current Frame) from the Plot and + re-instating the original current Frame. */ + astRemoveFrame( this, AST__CURRENT ); + astSetCurrent( this, icurr ); + +/* Free resources. */ + map = astAnnul( map ); + fs = astAnnul( fs ); + } +} + +static void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status ) { +/* +* Name: +* RemoveFrame + +* Purpose: +* Remove a Frame from a Plot. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "plot.h" +* void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status ) + +* Class Membership: +* Plot member function (over-rides the astRemoveFrame public +* method inherited from the FrameSet class). + +* Description: +* This function removes a Frame from a Plot. All other Frames +* in the Plot have their indices re-numbered from one (if +* necessary), but are otherwise unchanged. +* +* If the index of the clipping Frame is changed, the index value +* stored in the Plot is updated. If the clipping Frame itself is +* removed, all clipping information is removed from the Plot. + +* Parameters: +* this_fset +* Pointer to the FrameSet component of the Plot. +* iframe +* The index within the Plot of the Frame to be removed. +* This value should lie in the range from 1 to the number of +* Frames in the Plot (as given by its Nframe attribute). +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPlot *this; /* Pointer to Plot structure */ + int ifrm; /* Validated frame index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot structure. */ + this = (AstPlot *) this_fset; + +/* Validate the frame index. */ + ifrm = astValidateFrameIndex( this_fset, iframe, "astRemoveFrame" ); + +/* Invoke the parent astRemoveFrame method to remove the Frame. */ + (*parent_removeframe)( this_fset, iframe, status ); + +/* If the index of the removed Frame is smaller than the clipping Frame + index, then decrement the clipping Frame index so that the same Frame + will be used in future. */ + if( astOK ){ + if( ifrm < this->clip_frame ){ + (this->clip_frame)--; + +/* If the clipping fgrame itself has been removed, indicate that no + clipping should nbow be performed. */ + } else if( ifrm == this->clip_frame ){ + astClip( this, AST__NOFRAME, NULL, NULL ); + } + } +} + +static void RightVector( AstPlot *this, float *ux, float *uy, float *rx, + float *ry, const char *method, const char *class, int *status ){ +/* +* Name: +* RightVector + +* Purpose: +* Return a vector in the direction of the base line of normal text. + +* Synopsis: +* #include "plot.h" +* void RightVector( AstPlot *this, float *ux, float *uy, float *rx, +* float *ry, const char *method, const char *class, int *status ) + +* Description: +* This function returns a vector which points from left to right along +* the text baseline, taking account of any difference in the scales of +* the x and y axes. It also scales the supplied up vector so that it has +* a length equal to the height of normal text, and scales the returned +* right vector to have the same length (on the screen) as the up vector. + +* Parameters: +* this +* The plot. +* ux +* Pointer to a float holding the x component of the up-vector for the +* text, in graphics coords. Scaled on exit so that the up vector has +* length equal to the height of normal text. +* uy +* Pointer to a float holding the y component of the up-vector for the +* text, in graphics coords. Scaled on exit so that the up vector has +* length equal to the height of normal text. +* rx +* Pointer to a double in which will be put the x component of a +* vector parallel to the baseline of normal text. +* ry +* Pointer to a double in which will be put the y component of a +* vector parallel to the baseline of normal text. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Local Variables: */ + float alpha; /* Scale factor for X axis */ + float beta; /* Scale factor for Y axis */ + float chv; + float chh; + float l; /* Normalisation constant */ + float a; + float b; + float nl; /* Character height in standard coordinates */ + +/* Check inherited status */ + if( !astOK ) return; + +/* Find the scale factors for the two axes which scale graphics coordinates + into a "standard" coordinate system in which: 1) the axes have equal scale + in terms of (for instance) millimetres per unit distance, 2) X values + increase from left to right, 3) Y values increase from bottom to top. */ + GScales( this, &alpha, &beta, method, class, status ); + +/* Convert the up-vector into "standard" system in which the axes have + equal scales, and increase left-to-right, bottom-to-top. */ + *ux *= alpha; + *uy *= beta; + +/* Normalise this up-vector. */ + l = sqrt( (*ux)*(*ux) + (*uy)*(*uy) ); + if( l <= 0.0 ) { + *ux = 0.0; + *uy = 1.0; + } else { + *ux /= l; + *uy /= l; + } + +/* Find the height in "standard" coordinates of "normal" text draw with the + requested up-vector. */ + GQch( this, &chv, &chh, method, class, status ); + a = (*ux)/(chv*alpha); + b = (*uy)/(chh*beta); + nl = 1.0/sqrt( a*a + b*b ); + +/* Scale the up-vector so that is has length equal to the height of "normal" + text with the specified up-vector. */ + *ux *= nl; + *uy *= nl; + +/* Get the vector along the base-line of the text, by rotating the + up-vector by 90 degrees clockwise. */ + *rx = *uy; + *ry = -*ux; + +/* Convert both vectors back from standard coords to normal world coords */ + *ux /= alpha; + *uy /= beta; + *rx /= alpha; + *ry /= beta; + +} + +static void SaveTick( AstPlot *this, int axis, double gx, double gy, + int major, int *status ){ +/* +* Name: +* SaveTick + +* Purpose: +* Save info about a drawn tick in the Plot structure. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void SaveTick( AstPlot *this, int axis, double gx, double gy, +* int major, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function stores the start position and type of each drawn +* tick in the given Plot. + +* Parameters: +* this +* Pointer to the Plot. +* axis +* The zero-based axis index to which the tick refers. If a +* negative value is specified then all information about drawn ticks +* curently stored in the Plot is erased. +* gx +* The graphics X position at the start of the tick mark. +* gy +* The graphics Y position at the start of the tick mark. +* major +* Non-zero if the tick mark is a major tick. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + int *count; + double *tickgx; + double *tickgy; + +/* Free the tick info in the supplied Plot if required. Do this before + checking the error status. */ + if( axis < 0 ) { + for( i = 0; i < 3; i++ ) { + this->majtickgx[ i ] = astFree( this->majtickgx[ i ] ); + this->majtickgy[ i ] = astFree( this->majtickgy[ i ] ); + this->mintickgx[ i ] = astFree( this->mintickgx[ i ] ); + this->mintickgy[ i ] = astFree( this->mintickgy[ i ] ); + this->mintickcount[ i ] = 0; + this->majtickcount[ i ] = 0; + } + return; + } + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get pointers to the arrays to use, and ensure the arrays are big + enough to hold the new tick. */ + if( major ) { + count = this->majtickcount + axis; + tickgx = this->majtickgx[ axis ]; + tickgy = this->majtickgy[ axis ]; + } else { + count = this->mintickcount + axis; + tickgx = this->mintickgx[ axis ]; + tickgy = this->mintickgy[ axis ]; + } + +/* Ensure the arrays are big enough to hold the new tick. */ + i = *count; + tickgx = astGrow( tickgx, i + 1, sizeof( double ) ); + tickgy = astGrow( tickgy, i + 1, sizeof( double ) ); + +/* If memory was allocated succesfully, store the new tick information in + the expanded arrays. */ + if( astOK ) { + tickgx[ i ] = gx; + tickgy[ i ] = gy; + *count = i + 1; + +/* Store the potentially updated array pointers. */ + if( major ) { + this->majtickgx[ axis ] = tickgx; + this->majtickgy[ axis ] = tickgy; + } else { + this->mintickgx[ axis ] = tickgx; + this->mintickgy[ axis ] = tickgy; + } + } +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Plot. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Plot member function (over-rides the astSetAttrib protected +* method inherited from the FrameSet class). + +* Description: +* This function assigns an attribute value for a Plot, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Plot. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPlot *this; /* Pointer to the Plot structure */ + char label[21]; /* Graphics item label */ + const char *class; /* Pointer to class string */ + double dval; /* Double attribute value */ + int axis; /* Index for the axis */ + int edge; /* Index of edge within list */ + int id1; /* Plot object id */ + int id2; /* Plot object id */ + int id; /* Plot object id */ + int ival; /* Int attribute value */ + int len; /* Length of setting string */ + int nax; /* Number of graphics frame axes */ + int nc; /* Number of characters read by astSscanf */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot structure. */ + this = (AstPlot *) this_object; + +/* Get the number of base Frame axis (2 for a Plot, 3 for a Plot3D). */ + nax = astGetNin( this ); + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Tol. */ +/* ---- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "tol= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetTol( this, dval ); + +/* Grid. */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "grid= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetGrid( this, ival ); + +/* TickAll. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "tickall= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetTickAll( this, ival ); + +/* ForceExterior */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "forceexterior= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetForceExterior( this, ival ); + +/* Invisible. */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "invisible= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetInvisible( this, ival ); + +/* Border. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "border= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetBorder( this, ival ); + +/* ClipOp. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "clipop= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetClipOp( this, ival ); + +/* Clip. */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "clip= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetClip( this, ival ); + +/* Grf. */ +/* ---- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "grf= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetGrf( this, ival ); + +/* DrawTitle. */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "drawtitle= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetDrawTitle( this, ival ); + +/* DrawAxes. */ +/* --------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "drawaxes= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetDrawAxes( this, axis, ival ); + +/* DrawAxes(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "drawaxes(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetDrawAxes( this, axis - 1, ival ); + +/* Abbrev. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "abbrev= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetAbbrev( this, axis, ival ); + +/* Abbrev(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "abbrev(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetAbbrev( this, axis - 1, ival ); + +/* Escape. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "escape= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetEscape( this, ival ); + +/* Edge(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "edge(%d)= %n%*s %n", &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + edge = FullForm( "left right top bottom", setting + ival, setting, + "astSet", astGetClass( this ), status ); + if( edge == 0 ) { + astSetEdge( this, axis - 1, LEFT ); + } else if( edge == 1 ) { + astSetEdge( this, axis - 1, RIGHT ); + } else if( edge == 2 ) { + astSetEdge( this, axis - 1, TOP ); + } else if( edge == 3 ) { + astSetEdge( this, axis - 1, BOTTOM ); + } + +/* LabelAt (axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "labelat(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetLabelAt( this, axis - 1, dval ); + +/* Centre(axis). */ +/* ------------ */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "centre(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetCentre( this, axis - 1, dval ); + +/* Gap. */ +/* ---- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "gap= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetGap( this, axis, dval ); + +/* Gap(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "gap(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetGap( this, axis - 1, dval ); + +/* LogGap. */ +/* ---- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "loggap= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetLogGap( this, axis, dval ); + +/* LogGap(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "loggap(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetLogGap( this, axis - 1, dval ); + +/* NumLabGap. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "numlabgap= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetNumLabGap( this, axis, dval ); + +/* NumLabGap(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "numlabgap(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetNumLabGap( this, axis - 1, dval ); + +/* TextLabGap. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "textlabgap= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetTextLabGap( this, axis, dval ); + +/* TextLabGap(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "textlabgap(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetTextLabGap( this, axis - 1, dval ); + +/* LabelUp. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "labelup= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetLabelUp( this, axis, ival ); + +/* LabelUp(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "labelup(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetLabelUp( this, axis - 1, ival ); + +/* LogPlot. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "logplot= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetLogPlot( this, axis, ival ); + +/* LogPlot(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "logplot(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetLogPlot( this, axis - 1, ival ); + +/* LogTicks. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "logticks= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetLogTicks( this, axis, ival ); + +/* LogTicks(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "logticks(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetLogTicks( this, axis - 1, ival ); + +/* LogLabel. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "loglabel= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetLogLabel( this, axis, ival ); + +/* LogLabel(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "loglabel(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetLogLabel( this, axis - 1, ival ); + +/* NumLab. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "numlab= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetNumLab( this, axis, ival ); + +/* NumLab(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "numlab(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetNumLab( this, axis - 1, ival ); + +/* MinTick. */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "mintick= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetMinTick( this, axis, ival ); + +/* MinTick(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "mintick(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetMinTick( this, axis - 1, ival ); + +/* TextLab. */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "textlab= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetTextLab( this, axis, ival ); + +/* TextLab(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "textlab(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetTextLab( this, axis - 1, ival ); + +/* LabelUnits. */ +/* --------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "labelunits= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetLabelUnits( this, axis, ival ); + +/* LabelUnits(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "labelunits(%d)= %d %n", + &axis, &ival, &nc ) ) + && ( nc >= len ) ) { + astSetLabelUnits( this, axis - 1, ival ); + +/* Style. */ +/* ------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "style= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( id = 0; id < AST__NPID; id++ ) astSetStyle( this, id, ival ); + +/* Style(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "style(%20[^()])= %d %n", + label, &ival, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, "Style", "astSet", class, status ), + nax, &id1, &id2, &id3, status ); + astSetStyle( this, id1, ival ); + if( nid > 1 ) astSetStyle( this, id2, ival ); + if( nid > 2 ) astSetStyle( this, id3, ival ); + +/* Font. */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "font= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( id = 0; id < AST__NPID; id++ ) astSetFont( this, id, ival ); + +/* Font(label). */ +/* ------------ */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "font(%20[^()])= %d %n", + label, &ival, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, "Font", "astSet", class, status ), + nax, &id1, &id2, &id3, status ); + astSetFont( this, id1, ival ); + if( nid > 1 ) astSetFont( this, id2, ival ); + if( nid > 2 ) astSetFont( this, id3, ival ); + +/* Colour. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "colour= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( id = 0; id < AST__NPID; id++ ) astSetColour( this, id, ival ); + +/* Colour(label). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "colour(%20[^()])= %d %n", + label, &ival, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, "Colour", "astSet", class, status ), + nax, &id1, &id2, &id3, status ); + astSetColour( this, id1, ival ); + if( nid > 1 ) astSetColour( this, id2, ival ); + if( nid > 2 ) astSetColour( this, id3, ival ); + +/* Color. */ +/* ------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "color= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + for( id = 0; id < AST__NPID; id++ ) astSetColour( this, id, ival ); + +/* Color(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "color(%20[^()])= %d %n", + label, &ival, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, "Color", "astSet", class, status ), + nax, &id1, &id2, &id3, status ); + astSetColour( this, id1, ival ); + if( nid > 1 ) astSetColour( this, id2, ival ); + if( nid > 2 ) astSetColour( this, id3, ival ); + +/* Width. */ +/* ------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "width= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( id = 0; id < AST__NPID; id++ ) astSetWidth( this, id, dval ); + +/* Width(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "width(%20[^()])= %lg %n", + label, &dval, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, "Width", "astSet", class, status ), + nax, &id1, &id2, &id3, status ); + astSetWidth( this, id1, dval ); + if( nid > 1 ) astSetWidth( this, id2, dval ); + if( nid > 2 ) astSetWidth( this, id3, dval ); + +/* Size. */ +/* ----- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "size= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( id = 0; id < AST__NPID; id++ ) astSetSize( this, id, dval ); + +/* Size(label). */ +/* ------------ */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "size(%20[^()])= %lg %n", + label, &dval, &nc ) ) + && ( nc >= len ) ) { + class = astGetClass( this ); + + nid = IdFind( FullForm( GrfLabels, label, "Size", "astSet", class, status ), + nax, &id1, &id2, &id3, status ); + astSetSize( this, id1, dval ); + if( nid > 1 ) astSetSize( this, id2, dval ); + if( nid > 2 ) astSetSize( this, id3, dval ); + +/* TitleGap. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "titlegap= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetTitleGap( this, dval ); + +/* MajTickLen. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "majticklen= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetMajTickLen( this, axis, dval ); + +/* MajTickLen(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "majticklen(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetMajTickLen( this, axis - 1, dval ); + +/* MinTickLen. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "minticklen= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + for( axis = 0; axis < nax; axis++ ) astSetMinTickLen( this, axis, dval ); + +/* MinTickLen(axis). */ +/* ----------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "minticklen(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetMinTickLen( this, axis - 1, dval ); + +/* Labelling. */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "labelling= %n%*s %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetLabelling( this, FullForm( "exterior interior", + setting + ival, setting, + "astSet", astGetClass( this ), status ) + ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void SetLogPlot( AstPlot *this, int axis, int ival, int *status ){ +/* +* +* Name: +* SetLogPlot + +* Purpose: +* Set a new value for a LogPlot attribute + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void SetLogPlot( AstPlot *this, int axis, int ival, int *status ) + +* Class Membership: +* Plot member function + +* Description: +* Assigns a new value to the LogPlot attribute of the specified axis, +* and also re-maps the base Frame of the Plot if necessary. + +* Parameters: +* this +* The Plot. +* axis +* Zero based axis index. +* ival +* The new value for the ogPlot attribute. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int oldval; /* Original attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the axis index. */ + if( axis < 0 || axis >= 2 ){ + astError( AST__AXIIN, "astSetLogPlot(%s): Index (%d) is invalid for " + "attribute LogPlot - it should be in the range 1 to 2.", status, + astGetClass( this ), axis + 1 ); + +/* If the axis index is OK, store the original attribute value. We use + astGetLogPlot to get the value rather than directly accessing + "this->logplot" in order to get the any default value in the case of + no value having been set. */ + } else { + oldval = astGetLogPlot( this, axis ); + +/* If the current attribute value will change, attempt to re-map the plot + axis. If the attempt succeeds, toggle the current attribute value. */ + if( ( ival != 0 ) != ( oldval != 0 ) ){ + if( ToggleLogLin( this, axis, oldval, "astSetLogPlot", status ) ){ + this->logplot[ axis ] = ( !oldval ); + } + +/* If the current attribute value will not change, just store the supplied + value (this is not redundant because it may cause a previously defaulted + value to become an explicitly set value ). */ + } else { + this->logplot[ axis ] = oldval; + } + } +} + +static void SetTickValues( AstPlot *this, int axis, int nmajor, double *major, + int nminor, double *minor, int *status ){ +/* +*+ +* Name: +* astSetTickValues + +* Purpose: +* Store the tick mark values to use for a given Plot axis. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "plot.h" +* void astSetTickValues( AstPlot *this, int axis, int nmajor, +* double *major, int nminor, double *minor ) + +* Class Membership: +* Plot method. + +* Description: +* This function stores a set of tick mark values that should be used by +* subsequent calls to astGrid. + +* Parameters: +* this +* Pointer to a Plot. +* axis +* The zero-based index of the axis for which tick marks values +* have been supplied. +* nmajor +* The number of major tick mark values. If zero is supplied then +* the other parameters are ignored, and subsequent calls to +* astGrid will itself determine the tick values to be used. +* major +* Pointer to an array holding "nmajor" values for axis "axis" in +* the current Frame of the suppled Plot. Major tick marks will be +* drawn at these values. +* nminor +* The number of minor tick mark values. +* minor +* Pointer to an array holding "nminor" values for axis "axis" in +* the current Frame of the suppled Plot. Minor tick marks will be +* drawn at these values. + +*- +*/ + +/* Check the global status. */ + if( !astOK ) return; + +/* Report an error if the supplied axis value is incorrect. */ + if( axis < 0 || axis >= astGetNin( this ) ) { + astError( AST__INTER, "astSetTickValues(Plot): Supplied \"axis\" " + "value is %d - should in the range 0 to %d (internal AST " + "programming error).", status, axis, astGetNin( this ) - 1 ); + +/* Otherwise store or clear the values. */ + } else if( nmajor > 0 ){ + this->nmajtickval[ axis ] = nmajor; + this->majtickval[ axis ] = astStore( this->majtickval[ axis ], major, + sizeof( double )*nmajor ); + this->nmintickval[ axis ] = nminor; + this->mintickval[ axis ] = astStore( this->mintickval[ axis ], minor, + sizeof( double )*nminor ); + +/* Sort them into increasing order. */ + qsort( (void *) this->majtickval[ axis ], (size_t) nmajor, + sizeof(double), Compared ); + + qsort( (void *) this->mintickval[ axis ], (size_t) nminor, + sizeof(double), Compared ); + + } else { + this->nmajtickval[ axis ] = 0; + this->majtickval[ axis ] = astFree( this->majtickval[ axis ] ); + this->nmintickval[ axis ] = 0; + this->mintickval[ axis ] = astFree( this->mintickval[ axis ] ); + } +} + +const char *astStripEscapes_( const char *text, int *status ) { +/* +*++ +* Name: +c astStripEscapes +f AST_STRIPESCAPES + +* Purpose: +* Remove AST escape sequences from a string. + +* Type: +* Public function. + +* Synopsis: +c #include "plot.h" +c const char *astStripEscapes( const char *text ) +f RESULT = AST_STRIPESCAPES( TEXT ) + +* Description: +* This function removes AST escape sequences from a supplied string, +* returning the resulting text as the function value. The behaviour +* of this function can be controlled by invoking the +c astEscapes function, +f AST_ESCAPES routine, +* which can be used to supress or enable the removal of escape +* sequences by this function. +* +* AST escape sequences are used by the Plot class to modify the +* appearance and position of sub-strings within a plotted text string. +* See the "Escape" attribute for further information. + +* Parameters: +c text +c Pointer to the string to be checked. +f TEXT +f The string to be checked. + +* Returned Value: +c astStripEscapes() +f AST_STRIPESCAPES = CHARACTER +c Pointer to the modified string. If no escape sequences were found +c in the supplied string, then a copy of the supplied pointer is +c returned. Otherwise, the pointer will point to a static buffer +c holding the modified text. This text will be over-written by +c subsequent invocations of this function. If the astEscapes function +f The modified string. If the AST_ESCAPES routine +* has been called indicating that escape sequences should not be +* stripped, then the supplied string is returned without change. + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + const char *a; + char *b; + int nc; + int ncc; + int type; + int value; + const char *result; + +/* Initialise */ + result= text; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check inherited status and supplied pointer. Also return if the + string contains no escape sequences or if stripping of escapes has + been supressed. */ + if( !astOK || astEscapes( -1 ) || !text || !HasEscapes( text, status ) ) return result; + +/* Initialise a pointer to the next character to be read from the + supplied string. */ + a = text; + +/* Initialise a pointer to the next character to be written to the + returned string. */ + b = stripescapes_buff; + +/* Note the available space left in the buffer. */ + ncc = AST__PLOT_STRIPESCAPES_BUFF_LEN; + +/* Loop until all the string has been read, or the buffer is full. */ + while( *a && ncc > 0 ){ + +/* If the remaining string starts with an escape sequence, increment the + read point by the length of the escape sequence, but leave the write + pointer where it is. */ + if( astFindEscape( a, &type, &value, &nc ) ) { + a += nc; + +/* If the remaining string does not start with an escape sequence, copy + the following text from the read position to the write position. */ + } else { + if( nc > ncc ) nc = ncc; + memcpy( b, a, sizeof( char )*nc ); + a += nc; + b += nc; + ncc -= nc; + } + } + +/* Terminate the returned string. */ + *b = 0; + +/* Return the result.*/ + return stripescapes_buff; +} + +static int swapEdges( AstPlot *this, TickInfo **grid, AstPlotCurveData **cdata, int *status ) { +/* +* Name: +* swapEdges + +* Purpose: +* Determine if edges for text labels should be swapped. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int swapEdges( AstPlot *this, TickInfo **grid, AstPlotCurveData **cdata, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* or not it is necessary to swap the Edges(0) and Edges(1) attributes +* in order to place textual labels on appropriate edges. This should +* only be used if the attributes have not explicitly been set, and if +* interior labelling is being used. The sides are determines by +* looking at the bounding box of tick marks on axis 0, in graphics +* coordinates. The returned value causes the label for axis 0 to be +* placed on the edge parallel to the longest side of this bounding box. +* The label for axis 1 is placed parallel to the shortest side of this +* bounding box. + +* Parameters: +* this +* A pointer to the Plot. +* grid +* A pointer to an array of two TickInfo pointers (one for each axis), +* each pointing to a TickInfo structure holding information about +* tick marks on the axis. See function GridLines. +* cdata +* A pointer to an array of two AstPlotCurveData pointers (one for each axis), +* each pointing to an array of AstPlotCurveData structure (one for each +* major tick value on the axis), holding information about breaks +* in the curves drawn to mark the major tick values. See function +* DrawGrid. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the edges should be swapped, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPlotCurveData *cdt; /* Pointer to the AstPlotCurveData for the next tick */ + TickInfo *info; /* Pointer to the TickInfo for the current axis */ + float xmax; /* Max graphics X value */ + float xmin; /* Min graphics X value */ + float ymax; /* Max graphics Y value */ + float ymin; /* Min graphics Y value */ + int oldedge; /* The original edge for axis 0 */ + int result; /* Swap edges? */ + int tick; /* Tick index */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure containing information describing the + positions of the major tick marks along axis 0. */ + info = grid[ 0 ]; + +/* Get a pointer to the structure containing information describing + the breaks in the curve which is parallel to axis 1 and passes + through the first major tick mark on axis 0. */ + cdt = cdata[ 0 ]; + +/* Initialise the graphiocs X and Y bounds of the area covered by the + axis. */ + xmax = -1.0E10; + xmin = 1.0E10; + ymax = -1.0E10; + ymin = 1.0E10; + +/* Loop round each of the major tick marks on axis 0. */ + for( tick = 0; cdt && info && tick < info->nmajor; tick++ ){ + +/* Update the max and min graphics X and Y coords covered by the axis. */ + if( cdt->nbrk > 0 ) { + xmax = astMAX( xmax, cdt->xbrk[0] ); + xmin = astMIN( xmin, cdt->xbrk[0] ); + ymax = astMAX( ymax, cdt->ybrk[0] ); + ymin = astMIN( ymin, cdt->ybrk[0] ); + } + +/* Get a pointer to the curve through the next major tick mark. */ + cdt++; + + } + +/* See which edge axis 0 would normally be labelled on. */ + oldedge = astGetEdge( this, 0 ); + +/* If the X range is larger than the Y range, the textual label should be + placed on the bottom edge. If required, indicate that the edges must + be swapped to achieve this. */ + if( xmax - xmin > ymax - ymin ) { + if( oldedge == 0 || oldedge == 2 ) result = 1; + +/* If the X range is smaller than the Y range, the textual label should be + placed on the left edge. If required, indicate that the edges must + be swapped to achieve this. */ + } else { + if( oldedge == 1 || oldedge == 3 ) result = 1; + } + + return result; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Plot. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Plot member function (over-rides the astTestAttrib protected +* method inherited from the FrameSet class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Plot's attributes. + +* Parameters: +* this +* Pointer to the Plot. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPlot *this; /* Pointer to the Plot structure */ + char label[21]; /* Graphics item label */ + int axis; /* Axis number */ + int len; /* Length of attrib string */ + int nax; /* Number of base Frame axes */ + int nc; /* No. characters read by astSscanf */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Plot structure. */ + this = (AstPlot *) this_object; + +/* Get the number of base Frame axis (2 for a Plot, 3 for a Plot3D). */ + nax = astGetNin( this ); + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* Tol. */ +/* ---- */ + if ( !strcmp( attrib, "tol" ) ) { + result = astTestTol( this ); + +/* Edge(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "edge(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestEdge( this, axis - 1 ); + +/* Grid. */ +/* ----- */ + } else if ( !strcmp( attrib, "grid" ) ) { + result = astTestGrid( this ); + +/* TickAll. */ +/* -------- */ + } else if ( !strcmp( attrib, "tickall" ) ) { + result = astTestTickAll( this ); + +/* ForceExterior */ +/* ------------- */ + } else if ( !strcmp( attrib, "forceexterior" ) ) { + result = astTestForceExterior( this ); + +/* Invisible. */ +/* ---------- */ + } else if ( !strcmp( attrib, "invisible" ) ) { + result = astTestInvisible( this ); + +/* Border. */ +/* ------- */ + } else if ( !strcmp( attrib, "border" ) ) { + result = astTestBorder( this ); + +/* ClipOp. */ +/* ------- */ + } else if ( !strcmp( attrib, "clipop" ) ) { + result = astTestClipOp( this ); + +/* Clip. */ +/* ----- */ + } else if ( !strcmp( attrib, "clip" ) ) { + result = astTestClip( this ); + +/* Grf. */ +/* ---- */ + } else if ( !strcmp( attrib, "grf" ) ) { + result = astTestGrf( this ); + +/* DrawTitle. */ +/* ---------- */ + } else if ( !strcmp( attrib, "drawtitle" ) ) { + result = astTestDrawTitle( this ); + +/* DrawAxes. */ +/* --------- */ + } else if ( !strcmp( attrib, "drawaxes" ) ) { + result = astTestDrawAxes( this, 0 ); + +/* DrawAxes(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "drawaxes(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestDrawAxes( this, axis - 1 ); + +/* Abbrev. */ +/* --------- */ + } else if ( !strcmp( attrib, "abbrev" ) ) { + result = astTestAbbrev( this, 0 ); + +/* Abbrev(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "abbrev(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestAbbrev( this, axis - 1 ); + +/* Escape. */ +/* ------- */ + } else if ( !strcmp( attrib, "escape" ) ) { + result = astTestEscape( this ); + +/* Gap. */ +/* ---- */ + } else if ( !strcmp( attrib, "gap" ) ) { + result = astTestGap( this, 0 ); + +/* Gap(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "gap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestGap( this, axis - 1 ); + +/* LabelAt(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelat(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLabelAt( this, axis - 1 ); + +/* LogGap. */ +/* ---- */ + } else if ( !strcmp( attrib, "loggap" ) ) { + result = astTestLogGap( this, 0 ); + +/* LogGap(axis). */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "loggap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLogGap( this, axis - 1 ); + +/* NumLabGap. */ +/* --------- */ + } else if ( !strcmp( attrib, "numlabgap" ) ) { + result = astTestNumLabGap( this, 0 ); + +/* NumLabGap(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "numlabgap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestNumLabGap( this, axis - 1 ); + +/* TextLabGap. */ +/* --------- */ + } else if ( !strcmp( attrib, "textlabgap" ) ) { + result = astTestTextLabGap( this, 0 ); + +/* TextLabGap(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "textlabgap(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestTextLabGap( this, axis - 1 ); + +/* LabelUp. */ +/* -------- */ + } else if ( !strcmp( attrib, "labelup" ) ) { + result = astTestLabelUp( this, 0 ); + +/* LabelUp(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelup(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLabelUp( this, axis - 1 ); + +/* LogPlot. */ +/* -------- */ + } else if ( !strcmp( attrib, "logplot" ) ) { + result = astTestLogPlot( this, 0 ); + +/* LogPlot(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "logplot(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLogPlot( this, axis - 1 ); + +/* LogTicks. */ +/* -------- */ + } else if ( !strcmp( attrib, "logticks" ) ) { + result = astTestLogTicks( this, 0 ); + +/* LogTicks(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "logticks(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLogTicks( this, axis - 1 ); + +/* LogLabel. */ +/* -------- */ + } else if ( !strcmp( attrib, "loglabel" ) ) { + result = astTestLogLabel( this, 0 ); + +/* LogLabel(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "loglabel(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLogLabel( this, axis - 1 ); + +/* NumLab. */ +/* -------- */ + } else if ( !strcmp( attrib, "numlab" ) ) { + result = astTestNumLab( this, 0 ); + +/* NumLab(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "numlab(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestNumLab( this, axis - 1 ); + +/* MinTick. */ +/* -------- */ + } else if ( !strcmp( attrib, "mintick" ) ) { + result = astTestMinTick( this, 0 ); + +/* MinTick(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "mintick(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestMinTick( this, axis - 1 ); + +/* TextLab. */ +/* ---------- */ + } else if ( !strcmp( attrib, "textlab" ) ) { + result = astTestTextLab( this, 0 ); + +/* TextLab(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "textlab(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestTextLab( this, axis - 1 ); + +/* LabelUnits. */ +/* --------- */ + } else if ( !strcmp( attrib, "labelunits" ) ) { + result = astTestLabelUnits( this, 0 ); + +/* LabelUnits(axis). */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "labelunits(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestLabelUnits( this, axis - 1 ); + +/* Style. */ +/* ------ */ + } else if ( !strcmp( attrib, "style" ) ) { + result = TestUseStyle( this, AST__BORDER_ID, status ); + +/* Style(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "style(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + result = TestUseStyle( this, FullForm( GrfLabels, label, attrib, "astTest", astGetClass( this ), status ), status ); + +/* Font. */ +/* ----- */ + } else if ( !strcmp( attrib, "font" ) ) { + result = TestUseFont( this, AST__TEXTLABS_ID, status ); + +/* Font(label). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "font(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + result = TestUseFont( this, FullForm( GrfLabels, label, attrib, "astTest", astGetClass( this ), status ), status ); + +/* Colour. */ +/* ------- */ + } else if ( !strcmp( attrib, "colour" ) ) { + result = TestUseColour( this, AST__TEXTLABS_ID, status ); + +/* Color. */ +/* ------- */ + } else if ( !strcmp( attrib, "color" ) ) { + result = TestUseColour( this, AST__TEXTLABS_ID, status ); + +/* Colour(label). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "colour(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + result = TestUseColour( this, FullForm( GrfLabels, label, attrib, "astTest", astGetClass( this ), status ), status ); + +/* Color(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "color(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + result = TestUseColour( this, FullForm( GrfLabels, label, attrib, "astTest", astGetClass( this ), status ), status ); + +/* Width. */ +/* ------ */ + } else if ( !strcmp( attrib, "width" ) ) { + result = TestUseWidth( this, AST__BORDER_ID, status ); + +/* Width(label). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "width(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + result = TestUseWidth( this, FullForm( GrfLabels, label, attrib, "astTest", astGetClass( this ), status ), status ); + +/* Size. */ +/* ----- */ + } else if ( !strcmp( attrib, "size" ) ) { + result = TestUseSize( this, AST__TEXTLABS_ID, status ); + +/* Size(label). */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "size(%20[^()])%n", label, &nc ) ) + && ( nc >= len ) ) { + result = TestUseSize( this, FullForm( GrfLabels, label, attrib, "astTest", astGetClass( this ), status ), status ); + +/* TitleGap. */ +/* --------- */ + } else if ( !strcmp( attrib, "titlegap" ) ) { + result = astTestTitleGap( this ); + +/* MajTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "majticklen" ) ) { + result = astTestMajTickLen( this, 0 ); + +/* MajTickLen(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "majticklen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestMajTickLen( this, axis - 1 ); + +/* MinTickLen. */ +/* ----------- */ + } else if ( !strcmp( attrib, "minticklen" ) ) { + result = astTestMinTickLen( this, 0 ); + +/* MinTickLen(axis). */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "minticklen(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestMinTickLen( this, axis - 1 ); + +/* Labelling. */ +/* -------- */ + } else if ( !strcmp( attrib, "labelling" ) ) { + result = astTestLabelling( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static void Text( AstPlot *this, const char *text, const double pos[], + const float up[], const char *just, int *status ){ +/* +*++ +* Name: +c astText +f AST_TEXT + +* Purpose: +* Draw a text string for a Plot. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "plot.h" +c void astText( AstPlot *this, const char *text, const double pos[], +c const float up[], const char *just ) +f CALL AST_TEXT( THIS, TEXT, POS, UP, JUST, STATUS ) + +* Class Membership: +* Plot method. + +* Description: +* This function draws a string of text at a position specified in +* the physical coordinate system of a Plot. The physical position +* is transformed into graphical coordinates to determine where the +* text should appear within the plotting area. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Plot. +c text +f TEXT = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing the +f A character string containing the +* text to be drawn. Trailing white space is ignored. +c pos +f POS( * ) = DOUBLE PRECISION (Given) +* An array, with one element for each axis of the Plot, giving +* the physical coordinates of the point where the reference +* position of the text string is to be placed. +c up +f UP( * ) = REAL (Given) +* An array holding the components of a vector in the "up" +* direction of the text (in graphical coordinates). For +c example, to get horizontal text, the vector {0.0f,1.0f} should +f example, to get horizontal text, the vector [0.0,1.0] should +* be supplied. For a basic Plot, 2 values should be supplied. For +* a Plot3D, 3 values should be supplied, and the actual up vector +* used is the projection of the supplied up vector onto the text plane +* specified by the current value of the Plot3D's Norm attribute. +c just +f JUST = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string identifying the +f A character string identifying the +* reference point for the text being drawn. The first character in +* this string identifies the reference position in the "up" direction +* and may be "B" (baseline), "C" (centre), "T" (top) or "M" (bottom). +* The second character identifies the side-to-side reference position +* and may be "L" (left), "C" (centre) or "R" (right ). The string is +* case-insensitive, and only the first two characters are significant. +* +* For example, a value of "BL" means that the left end of the +* baseline of the original (un-rotated) text is to be drawn at the +c position given by "pos". +f position given by POS. + +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The Plot3D class currently does not interpret graphical escape +* sequences contained within text displayed using this method. +* - Text is not drawn at positions which have any coordinate equal +* to the value AST__BAD (or where the transformation into +* graphical coordinates yields coordinates containing the value +* AST__BAD). +c - If the plotting position is clipped (see astClip), then no +f - If the plotting position is clipped (see AST_CLIP), then no +* text is drawn. +* - An error results if the base Frame of the Plot is not +* 2-dimensional or (for a Plot3D) 3-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *mapping; /* Pointer to graphics->physical mapping */ + AstPointSet *pset1; /* PointSet holding physical positions */ + AstPointSet *pset2; /* PointSet holding graphics positions */ + const char *class; /* Object class */ + const char *method; /* Current method */ + const double **ptr1; /* Pointer to physical positions */ + char ljust[3]; /* Upper case copy of "just" */ + char *ltext; /* Local copy of "text" excluding trailing spaces */ + double **ptr2; /* Pointer to graphics positions */ + float xbn[ 4 ]; /* X coords of text bounding box. */ + float ybn[ 4 ]; /* Y coord of text bounding box. */ + int axis; /* Axis index */ + int escs; /* Original astEscapes value */ + int naxes; /* No. of axes in the base Frame */ + int ncoord; /* No. of axes in the current Frame */ + int ulen; /* Length of "text" excluding trailing spaces */ + +/* Check the global error status. */ + if ( !astOK || !text ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Store the current method and class for inclusion in error messages + generated by lower level functions. */ + method = "astText"; + class = astClass( this ); + +/* Check the base Frame of the Plot is 2-D. */ + naxes = astGetNin( this ); + if( naxes != 2 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 2.", status, method, class, naxes, class ); + } + +/* Ensure AST functions included graphical escape sequences in any + returned text strings. */ + escs = astEscapes( 1 ); + +/* Initialise the bounding box for primatives produced by this call. */ + if( !Boxp_freeze ) { + Boxp_lbnd[ 0 ] = FLT_MAX; + Boxp_lbnd[ 1 ] = FLT_MAX; + Boxp_ubnd[ 0 ] = FLT_MIN; + Boxp_ubnd[ 1 ] = FLT_MIN; + } + +/* Indicate that the GRF module should re-calculate it's cached values + (in case the state of the graphics system has changed since the last + thing was drawn). */ + RESET_GRF; + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__TEXT_ID, 1, GRF__TEXT, method, class ); + +/* Get the number of coordinates in the physical coordinate frame. */ + ncoord = astGetNout( this ); + +/* Create a PointSet to hold the supplied physical coordinates. */ + pset1 = astPointSet( 1, ncoord, "", status ); + +/* Allocate memory to hold pointers to the first value on each axis. */ + ptr1 = (const double **) astMalloc( sizeof( const double * )* + (size_t)( ncoord )); + +/* Check the pointer can be used, then store pointers to the first value + on each axis. */ + if( astOK ){ + for( axis = 0; axis < ncoord; axis++ ){ + ptr1[ axis ] = pos + axis; + } + } + +/* Store these pointers in the PointSet. */ + astSetPoints( pset1, (double **) ptr1 ); + +/* Transform the supplied data from the current frame (i.e. physical + coordinates) to the base frame (i.e. graphics coordinates) using + the inverse Mapping defined by the Plot. */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + pset2 = Trans( this, NULL, mapping, pset1, 0, NULL, 0, method, class, status ); + mapping = astAnnul( mapping ); + +/* Get pointers to the graphics coordinates. */ + ptr2 = astGetPoints( pset2 ); + +/* Take a copy of the string excluding any trailing white space. */ + ulen = ChrLen( text, status ); + ltext = (char *) astStore( NULL, (void *) text, ulen + 1 ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* Terminate the local copy of the text string. */ + ltext[ ulen ] = 0; + +/* Produce an upper-case copy of the first two characters in "just". */ + ljust[0] = (char) toupper( (int) just[0] ); + ljust[1] = (char) toupper( (int) just[1] ); + ljust[2] = 0; + +/* Convert the double precision values to single precision, checking for + bad positions. */ + if( ptr2[0][0] != AST__BAD && ptr2[1][0] != AST__BAD ){ + +/* Draw the text string. */ + DrawText( this, 1, astGetEscape( this ), ltext, (float) ptr2[0][0], + (float) ptr2[1][0], ljust, up[ 0 ], up[ 1 ], xbn, ybn, + NULL, method, class, status ); + } + +/* Free the local copy of the string. */ + ltext = (char *) astFree( (void *) ltext ); + + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Free the memory holding the pointers to the first value on each axis. */ + ptr1 = (const double **) astFree( (void *) ptr1 ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__TEXT_ID, 0, GRF__TEXT, method, class ); + +/* Restore the original value of the flag which says whether graphical + escape sequences should be incldued in any returned text strings. */ + astEscapes( escs ); + +/* Return */ + return; +} + +static void TextLabels( AstPlot *this, int edgeticks, int dounits[2], + const char *method, const char *class, int *status ){ +/* +* +* Name: +* TextLabels + +* Purpose: +* Draw textual labels round a grid. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void TextLabels( AstPlot *this, int edgeticks, int dounits[2], +* const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function displays a textual label for each physical axis, and a +* title. The text strings are obtained from the Label and Title +* attributes of the current Frame in the Plot. + +* Parameters: +* this +* A pointer to the Plot. +* edgeticks +* If tick marks and numerical labels were drawn around the edges +* of the plotting area, this should be supplied as 1. Otherwise it +* should be supplied as zero. +* dounits +* Flags indicating if the axis Units string should be included in +* the edge labels. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *new_text; /* Pointer to complete text string (inc. units) */ + char *just; /* Pointer to axis label justification string */ + const char *text; /* Pointer to text string to be displayed */ + const char *units; /* Pointer to text string describing the units */ + double mindim; /* Minimum dimension of plotting area */ + double xrange; /* Width of plotting area */ + double yrange; /* Height of plotting area */ + float txtgap; /* Gap between bounding box and text string */ + float upx; /* X component of text up-vector */ + float upy; /* Y component of text up-vector */ + float xbn[ 4 ]; /* X coords at corners of new label's bounding box */ + float ybn[ 4 ]; /* Y coords at corners of new label's bounding box */ + float xref; /* Graphics X coord. at text ref. position */ + float yref; /* Graphics Y coord. at text ref. position */ + float xlo, xhi, ylo, yhi;/* The original bounding box (excluding labels) */ + int axis; /* Axis index */ + int draw; /* Should label be drawn? */ + int edge; /* Edge to be labelled */ + int esc; /* Interpret escape sequences? */ + int gelid; /* ID for next graphical element to be drawn */ + int tlen; /* No. of characetsr in label */ + int ulen; /* No. of characetsr in units */ + +/* Check the global status. */ + if( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Get the minimum dimension of the plotting area. */ + xrange = this->xhi - this->xlo; + yrange = this->yhi - this->ylo; + mindim = astMIN( xrange, yrange ); + +/* Take a copy of the bounding box which encloses all other parts of the + annotated grid. If nothing has been plotted, use an area 20 % smaller + than the plotting area. */ + if( Box_lbnd[ 0 ] != FLT_MAX ) { + xlo = Box_lbnd[ 0 ]; + xhi = Box_ubnd[ 0 ]; + ylo = Box_lbnd[ 1 ]; + yhi = Box_ubnd[ 1 ]; + } else { + xlo = this->xlo + 0.2*xrange; + xhi = this->xhi - 0.2*xrange; + ylo = this->ylo + 0.2*yrange; + yhi = this->yhi - 0.2*yrange; + } + +/* See if escape sequences are to be interpreted within the labels. */ + esc = astGetEscape( this ); + +/* Initialize the id value for graphical element being drawn. */ + gelid = AST__TEXTLAB1_ID; + +/* Now write a label for each physical axis. */ + for( axis = 0; axis < 2 && astOK; axis++ ){ + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, gelid, 1, GRF__TEXT, method, class ); + +/* See if the label is to be drawn. If an explicit value has not been set + for the TextLab attribute, the default is to draw the label if tick + marks were draw round the edge of the plotting area, and not to + otherwise. */ + if( astTestTextLab( this, axis ) ){ + draw = astGetTextLab( this, axis ); + } else { + draw = edgeticks; + } + +/* If so get the label. */ + if( draw ){ + text = astGetLabel( this, axis ); + tlen = ChrLen( text, status ); + +/* If required, get the units string and concatenate it with the label (inside + parenthesise). Ignore trailing spaces. */ + if( dounits[ axis ] ){ + units = astGetUnit( this, axis ); + if( units && units[0] ){ + ulen = ChrLen( units, status ); + new_text = astMalloc( tlen + ulen + 4 ); + if( new_text ) memcpy( new_text, text, tlen ); + + text = new_text + tlen; + + memcpy( (void *) text, " (", 2 ); + text += 2; + + memcpy( (char *) text, units, ulen ); + text += ulen; + + memcpy( (char *) text, ")", 1 ); + text += 1; + + ( (char *) text )[0] = 0; + + text = new_text; + + } else { + new_text = astStore( NULL, (void *) text, tlen + 1 ); + new_text[ tlen ] = 0; + text = new_text; + units = NULL; + } + + } else { + new_text = astStore( NULL, (void *) text, tlen + 1 ); + new_text[ tlen ] = 0; + text = new_text; + units = NULL; + } + +/* Get the gap between the edge of the bounding box and the closest edge + of the text string. */ + txtgap = (float)( mindim*astGetTextLabGap( this, axis ) ); + +/* Get the edge to be labelled. Edge 0 is the left hand edge. Edge 1 is the + top edge. Edge 2 is the right-hand edge. Edge 3 is the bottom edge. */ + edge = astGetEdge( this, axis ) % 4; + if( edge < 0 ) edge = -edge; + +/* If the label is to be put on the left hand edge... */ + if( edge == 0 ){ + +/* Set the up vector so that the text is written from bottom to top. */ + upx = -1.0; + upy = 0.0; + +/* Justify the bottom of the whole bounding box (not just the text + base-line). */ + just = "MC"; + +/* Set the x reference position just outside the box enclosing all the + graphics drawn so far. The reference point refers to the centre of the + text string in both directions ("CC" justification). Take account of + whether or not the x axis is reversed. Do not allow the text to be + written outside the whole plotting surface. */ + if( this->xrev ){ + xref = xhi + txtgap; + } else { + xref = xlo - txtgap; + } + +/* The Y reference position is at the mid point vertically. */ + yref = 0.5*( astMIN( yhi, this->yhi ) + + astMAX( ylo, this->ylo ) ); + +/* Do the same for the top edge. */ + } else if( edge == 1 ){ + upx = 0.0; + upy = 1.0; + just = "MC"; + if( this->yrev ){ + yref = ylo - txtgap; + } else { + yref = yhi + txtgap; + } + xref = 0.5*( astMIN( xhi, this->xhi ) + + astMAX( xlo, this->xlo ) ); + +/* Do the same for the right-hand edge. */ + } else if( edge == 2 ){ + upx = 1.0; + upy = 0.0; + just = "MC"; + if( this->xrev ){ + xref = xlo - txtgap; + } else { + xref = xhi + txtgap; + } + yref = 0.5*( astMIN( yhi, this->yhi ) + + astMAX( ylo, this->ylo ) ); + +/* Do the same for the bottom edge. */ + } else { + upx = 0.0; + upy = 1.0; + just = "TC"; + if( this->yrev ){ + yref = yhi + txtgap; + } else { + yref = ylo - txtgap; + } + xref = 0.5*( astMIN( xhi, this->xhi ) + + astMAX( xlo, this->xlo ) ); + } + +/* Display the label. */ + DrawText( this, 1, esc, text, xref, yref, just, + upx, upy, xbn, ybn, NULL, method, class, status ); + +/* Release the memory allocated to store the axis label;. */ + new_text = (char *) astFree( (void *) new_text ); + text = NULL; + + } + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, gelid, 0, GRF__TEXT, method, class ); + +/* Set up the id for the next graphical element to be drawn. */ + gelid = AST__TEXTLAB2_ID; + + } + +/* See if the title is to be drawn. */ + if( astOK && astGetDrawTitle( this ) ){ + +/* If so get the title. */ + text = astGetTitle( this ); + +/* Create a copy from which trailing spaces have been removed. */ + tlen = ChrLen( text, status ); + new_text = (char *) astStore( NULL, (void *) text, tlen + 1 ); + new_text[ tlen ] = 0; + text = new_text; + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__TITLE_ID, 1, GRF__TEXT, method, class ); + +/* Take a copy of the bounding box which encloses all other parts of the + annotated grid (this may have been extended by the above code). If + nothing has been plotted, use an area 20 % smaller than the plotting + area. */ + if( Box_lbnd[ 0 ] != FLT_MAX ) { + xlo = Box_lbnd[ 0 ]; + xhi = Box_ubnd[ 0 ]; + ylo = Box_lbnd[ 1 ]; + yhi = Box_ubnd[ 1 ]; + } else { + xlo = this->xlo + 0.2*xrange; + xhi = this->xhi - 0.2*xrange; + ylo = this->ylo + 0.2*yrange; + yhi = this->yhi - 0.2*yrange; + } + +/* Get the graphics coordinates of the bottom centre point of the title. + The X centre is put at the mid point of the used x axis range + (restricted to the range of the plotting area). */ + xref = 0.5*( astMIN( xhi, this->xhi ) + + astMAX( xlo, this->xlo ) ); + +/* The Y centre is put a "TitleGap" distance outside the box containing + the everything else. */ + if( this->yrev ){ + yref = ylo - (float)( mindim*astGetTitleGap( this ) ); + } else { + yref = yhi + (float)( mindim*astGetTitleGap( this ) ); + } + +/* Display the title. Justify the bottom of the whole bounding box (not + just the text base-line). */ + DrawText( this, 1, esc, text, xref, yref, "MC", 0.0F, 1.0F, + xbn, ybn, NULL, method, class, status ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__TITLE_ID, 0, GRF__TEXT, method, class ); + +/* Release the memory allocated to store the title. */ + new_text = (char *) astFree( (void *) new_text ); + text = NULL; + } + +/* Include the labels in the bounding box held in global variables + Box_lbnd/ubnd. */ + if( Box_lbnd[ 0 ] != FLT_MAX ) { + Box_lbnd[ 0 ] = astMIN( Box_lbnd[ 0 ], Boxp_lbnd[ 0 ] ); + Box_ubnd[ 0 ] = astMAX( Box_ubnd[ 0 ], Boxp_ubnd[ 0 ] ); + Box_lbnd[ 1 ] = astMIN( Box_lbnd[ 1 ], Boxp_lbnd[ 1 ] ); + Box_ubnd[ 1 ] = astMAX( Box_ubnd[ 1 ], Boxp_ubnd[ 1 ] ); + } else { + Box_lbnd[ 0 ] = Boxp_lbnd[ 0 ]; + Box_ubnd[ 0 ] = Boxp_ubnd[ 0 ]; + Box_lbnd[ 1 ] = Boxp_lbnd[ 1 ]; + Box_ubnd[ 1 ] = Boxp_ubnd[ 1 ]; + } + +/* Return. */ + return; + +} + +static void UpdateConcat( float *xbn, float *ybn, float ux, float uy, + float rx, float ry, float *x, float *y, + float x0, float y0, float *alpha_lo, + float *alpha_hi, float *beta_lo, float *beta_hi, + int *status ){ + +/* +* Name: +* UpdateConcat + +* Purpose: +* Update the concatenation point for a text string in preparation for +* appending another string. + +* Synopsis: +* #include "plot.h" +* void UpdateConcat( float *xbn, float *ybn, float ux, float uy, +* float rx, float ry, float *x, float *y, +* float x0, float y0, float *alpha_lo, float *alpha_hi, +* float *beta_lo, float *beta_hi, int *status ) + +* Description: +* This function modifies the supplied concatenation point (x,y) by moving +* it to the right along the baseline of the text by an amount equal +* to the width of the supplied sub-string bounding box. It also updates +* the supplied total bounding box so that it includes the supplied +* sub-string bounding box. + +* Parameters: +* xbn +* Pointer to an array holding the x coord at the four corners of +* the sub-string bounding box. +* ybn +* Pointer to an array holding the y coord at the four corners of +* the sub-string bounding box. +* ux +* The x component of the up-vector for the text, in graphics coords. +* The length of this vector should be equal to the height of normal +* text drawn with this up-vector. +* uy +* The y component of the up-vector for the text. See "ux". +* rx +* The x component of the right-vector for the text. The length of this +* vector should be equal to the height of normal text drawn with the +* supplied up-vector. +* ry +* The y component of the right-vector for the text. see "rx". +* x +* Pointer to a float holding the x coord of the concatenation point +* of the text just drawn. This is changed on exit so that the next +* string will be appended to the end of the text just drawn. +* y +* Pointer to a float holding the y coord of the concatenation point +* of the text just drawn. This is changed on exit so that the next +* string will be appended to the end of the text just drawn. +* x0 +* The X coord at the left end of the baseline of the total string. +* y0 +* The Y coord at the left end of the baseline of the total string. +* alpha_lo +* Pointer to a double holding a value which gives the position of the +* bottom edge of the total bounding box. The value is such that +* (x0,y0) + alpha_lo*(ux,uy) is a point on the bottom edge of the +* total bounding box. The returned value is updated so that the +* total bounding box includes the supplied sub-string bounding box. +* alpha_hi +* Pointer to a double holding a value which gives the position of the +* top edge of the total bounding box. The value is such that +* (x0,y0) + alpha_hi*(ux,uy) is a point on the top edge of the +* total bounding box. The returned value is updated so that the +* total bounding box includes the supplied sub-string bounding box. +* beta_lo +* Pointer to a double holding a value which gives the position of the +* left edge of the total bounding box. The value is such that +* (x0,y0) + beta_lo*(rx,ry) is a point on the left edge of the +* total bounding box. The returned value is updated so that the +* total bounding box includes the supplied sub-string bounding box. +* beta_hi +* Pointer to a double holding a value which gives the position of the +* right edge of the total bounding box. The value is such that +* (x0,y0) + beta_hi*(rx,ry) is a point on the right edge of the +* total bounding box. The returned value is updated so that the +* total bounding box includes the supplied sub-string bounding box. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + float ahi; + float alo; + float alpha; + float beta; + float bhi; + float blo; + float denom; + float dx; + float dy; + float xc; + float yc; + int ic; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check the supplied up and right vectors are not parallel. */ + denom = ux*ry - uy*rx; + if( denom != 0.0 ) { + +/* Get the coords at the centre of the sub-string bounding box. */ + xc = ( xbn[ 0 ] + xbn[ 1 ] + xbn[ 2 ] + xbn[ 3 ] )/4.0; + yc = ( ybn[ 0 ] + ybn[ 1 ] + ybn[ 2 ] + ybn[ 3 ] )/4.0; + +/* For each of the 4 corners of the sub-string bounding box, we consider the + vector from the centre of the bounding box to the corner. We want to express + this vector, vx, as a sum of a vector in the up direction and a vector + in the right direction: + + vx = alpha*ux + beta+rx + vy = alpha*uy + beta+ry + + where alpha and beta are constants which are different for each + corner vector. We also want to find the minimum and maximum alpha and + beta covered by the box. First initialise the limits. */ + alo = 0.0; + ahi = 0.0; + blo = 0.0; + bhi = 0.0; + +/* Now find alpha and beta for each of the four corners. */ + for( ic = 0; ic < 4; ic++ ) { + dx = xbn[ ic ] - xc; + dy = ybn[ ic ] - yc; + alpha = ( dx*ry - dy*rx ) /denom; + beta = ( dy*ux - dx*uy ) /denom; + +/* Extend the bounds in alpha and beta. */ + if( alpha < alo ) alo = alpha; + if( alpha > ahi ) ahi = alpha; + if( beta < blo ) blo = beta; + if( beta > bhi ) bhi = beta; + +/* The bottom left corner has negative values for both alpha and beta. + Commence the process of updating the concatenation point by subtracting + off the coordinates at the bottom left corner. For zero height bounding + boxes (such as may be produced if the text is completely blank), the + "alpha" value should be zero, but may be slightly non-zero due to + rounding errors. Allow for this (assuming non-blank text will always + produce a boundiong box that is at least 1.0E-4 of the up vector in + height). We do the same for bounding box width (although zero width + boxes will probably not occur). */ + if( alpha < 1.0E-4 ) { + if( beta < 1.0E-4 ) { + *x -= xbn[ ic ]; + *y -= ybn[ ic ]; + +/* The bottom right corner has negative alpha and positive beta. Complete + the process of updating the concatenation point by adding on the + coordinates at the bottom right corner. */ + } else if( beta > -1.0E-4 ) { + *x += xbn[ ic ]; + *y += ybn[ ic ]; + } + } + } + +/* Also express the vector from (x0,y0) to (xc,yc) as a sum of a vector + in the up direction and a vector in the right direction: + + xc - x0 = alpha*ux + beta*rx + yc - y0 = alpha*uy + beta*ry + + Find alpha and beta. */ + + dx = xc - x0; + dy = yc - y0; + alpha = ( dx*ry - dy*rx ) /denom; + beta = ( dy*ux - dx*uy ) /denom; + +/* We can now express the vector from (x0,y0) to each of the 4 corners as + a sum of alpha*up and beta*right. Form the bounds of the sub-string in + terms of up-vectors and right vectors from (x0,y0) instead of from the + centre of the box. */ + alo += alpha; + ahi += alpha; + blo += beta; + bhi += beta; + +/* Extend the supplied alpha and beta bounding box to include these limits. */ + if( alo < *alpha_lo ) *alpha_lo = alo; + if( ahi > *alpha_hi ) *alpha_hi = ahi; + if( blo < *beta_lo ) *beta_lo = blo; + if( bhi > *beta_hi ) *beta_hi = bhi; + } +} + +static void Ticker( AstPlot *this, int edge, int axis, double value, + double *gap, double tklen, int majtick, int save, + EdgeCrossingsStatics **pstatics, const char *method, + const char *class, int *status ){ +/* +* +* Name: +* Ticker + +* Purpose: +* Draw edge tick marks for a given axis value. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void Ticker( AstPlot *this, int edge, int axis, double value, +* double *gap, double tklen, int majtick, int save, +* EdgeCrossingsStatics **pstatics, const char *method, +* const char *class, int *status ){ + +* Class Membership: +* Plot member function. + +* Description: +* This function draws tick marks at all occurences of a given +* physical axis value on a given edge of the plotting area. + +* Parameters: +* this +* A pointer to the Plot. +* edge +* The edge of the plotting area to be ticked. Edge 0 is the left hand +* edge. Edge 1 is the top edge. Edge 2 is the right-hand edge. Edge 3 +* is the bottom edge. +* axis +* The index of the axis to which "value" refers. The tick mark extends +* parallel to this axis. +* value +* The physical axis value at which to place the tick mark. +* gap +* Pointer to array of two values holding the gap between major +* tick marks on the two axes. +* tklen +* The length of the tick, in graphics units. +* majtick +* Non-zero if the tick mark being drawn is a major tick, and zero +* if it is a minor tick. +* save +* If non-zero, the tick mark position will be saved in the Plot structure. +* pstatics +* Address of a pointer to a structure holding values for variables +* which were statically defined within this function prior to the +* thread-safe version of AST. If the pointer is supplied as NULL, +* then a new structure is allocated and initialised. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double *cross; /* Pointer to crossings information */ + double *vx; /* Pointer to next X vector component value */ + double *vy; /* Pointer to next Y vector component value */ + double *x; /* Pointer to next X value */ + double *y; /* Pointer to next Y value */ + double xe; /* X at tick end */ + double ye; /* Y at tick end */ + int j; /* Crossing index */ + int ncross; /* No. of crossings of tick value and edge */ + +/* Check the global status. */ + if( !astOK ) return; + +/* See where the current major tick value crosses the edge. */ + ncross = EdgeCrossings( this, edge, axis, value, gap, &cross, pstatics, + method, class, status ); + +/* Do nothing if the supplied axis value does not occur on the specified + edge of the plotting area. */ + if( ncross ){ + +/* Draw major tick marks at the crossings. */ + x = cross; + y = cross + 1; + vx = cross + 2; + vy = cross + 3; + +/* Draw a tick mark at each occurence of the axis value on the specified + edge. */ + for( j = 0; j < ncross; j++ ){ + +/* Check the tick mark position and directionm are defined. */ + if( *vx != AST__BAD && *vy != AST__BAD && + *x != AST__BAD && *y != AST__BAD ){ + +/* Ensure the tick mark will point into the plotting area, no matter which + edge it is on. First ensure the direction vector refers to a system in + which X increases to the left and Y increases upwards. */ + if( this->xrev ) *vx = -*vx; + if( this->yrev ) *vy = -*vy; + +/* If necessary reverse the vector so that it points into the plotting + area. How to do this depends on which edge is being ticked. */ + if( ( edge == 0 && *vx < 0.0 ) || /* left-hand edge */ + ( edge == 1 && *vy > 0.0 ) || /* Top edge */ + ( edge == 2 && *vx > 0.0 ) || /* Right-hand edge */ + ( edge == 3 && *vy < 0.0 ) ){ /* Bottom edge */ + + *vx = -*vx; + *vy = -*vy; + } + +/* Now ensure the direction vector refers to a the native graphics system + taking account of any reversal of axes. */ + if( this->xrev ) *vx = -*vx; + if( this->yrev ) *vy = -*vy; + +/* Do not draw the tick if the start of the tick is outside the bounds of + the axis it is labelling. */ + if( ( ( edge == 1 || edge == 3 ) && + *x < this->xhi && *x > this->xlo ) || + ( ( edge == 0 || edge == 2 ) && + *y < this->yhi && *y > this->ylo ) ) { + +/* Store the x and y graphics coords of the far end of the tick mark */ + xe = *x + tklen*(*vx); + ye = *y + tklen*(*vy); + +/* Ensure the far end of the tick mark is within the bounds of the axis + it is labelling. If not, redice the length of the tick mark until it is.*/ + if( edge == 1 || edge == 3 ) { /* Top or bottom edge */ + if( xe > this->xhi ) { + ye = *y + tklen*(*vy)*( this->xhi - *x )/(xe - *x ); + xe = this->xhi; + } else if( xe < this->xlo ) { + ye = *y + tklen*(*vy)*( this->xlo - *x )/(xe - *x ); + xe = this->xlo; + } + + } else { /* Left or right edge */ + if( ye > this->yhi ) { + xe = *x + tklen*(*vx)*( this->yhi - *y )/(ye - *y ); + ye = this->yhi; + } else if( ye < this->ylo ) { + xe = *x + tklen*(*vx)*( this->ylo - *y )/(ye - *y ); + ye = this->ylo; + } + } + +/* Draw the tick mark as a straight line of the specified length. */ + if( save ) SaveTick( this, axis, *x, *y, majtick, status ); + if( *x != xe || *y != ye ) { + Bpoly( this, (float) *x, (float) *y, status ); + Apoly( this, (float) xe, (float) ye, status ); + Opoly( this, status ); + } + } + +/* Move on to the next crossing. */ + x += 4; + y += 4; + vx += 4; + vy += 4; + } + } + +/* Free the memory holding the crossings. */ + if( cross ) cross = (double *) astFree( (void *) cross ); + + } + +/* Return. */ + return; + +} + +static TickInfo *TickMarks( AstPlot *this, int axis, double *cen, double *gap, + int *inval, GetTicksStatics **pstatics, + const char *method, const char *class, int *status ){ +/* +* Name: +* TickMarks + +* Purpose: +* Obtain a list of tick mark values and labels for a single axis in a 2-D +* physical coordinate Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* TickInfo *TickMarks( AstPlot *this, int axis, double *cen, double *gap, +* int *inval, GetTicksStatics **pstatics, +* const char *method, const char *class, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* A set of tick marks values and corresponding formatted labels are +* found for an axis which result in all adjacent labels being different, +* but using the minimum number of digits of precision in the formatting. +* This algorithm is over-ridden if the caller has set an explicit Format +* string for the axis. The gap between tick marks can be specified by +* the caller or a default value may be found automatically. + +* Parameters: +* this +* The Plot. +* axis +* The zero-based index of the axis to use. +* cen +* Pointer to the supplied axis value at which to put a single +* central tick. Other ticks will be placed evenly on either side of +* this tick. If AST__BAD is provided, a value will be used which +* would put a tick at an axis value of zero. The used value is +* returned. +* gap +* The supplied values for the gaps between ticks on the axis. If +* this is AST__BAD a suitable default value will be used and +* returned in place of the AST__BAD value. +* inval +* A pointer to a location at which to return a flag indicating if +* any invalid physical coordinates were encountered while deciding on +* the tick values. +* pstatics +* Address of a pointer to a structure holding static data values +* used within the GetTicks function. A NULL pointer should be supplied +* on the first invocation (dynamic memory will then be allocated to +* hold ths structure). The memory is freed when a NULL value for +* "this" is supplied. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a TickInfo structure holding information about the tick +* marks (no. of major and minor ticks, the major tick mark values and +* labels). This structure should be freed, together with its contents, +* using astFree when it is no longer needed. + +* Notes: +* - The returned labels are NOT abbreviated to remove identical +* leading fields. +* - This function allocates some static resources on its first +* invocation, which should be released when no longer needed, or when +* a different Plot is supplied, by calling this function with a NULL +* pointer for parameter "this". All other parameters (except axis) are +* ignored. +* - This function assumes that the physical coordinate system is 2 +* dimensional, and it should not be used if this is not the case. +* - An error is reported if the region containing valid physical +* coordinates is too small to use. +* - If an error has already occurred, or if this function should fail +* for any reason, then a NULL pointer is returned. +*/ + +/* Local Constants: */ +#define MAXFLD 10 + +/* Local Variables: */ + AstAxis *ax; /* Pointer to the axis. */ + AstFrame *frame; /* Pointer to the current Frame in the Plot */ + TickInfo *ret; /* Pointer to the returned structure. */ + char **labels; /* Pointer to list of formatted labels */ + char **newlabels; /* Pointer to new list of shortened formatted labels */ + char **oldlabels; /* Pointer to old list of formatted labels */ + char *fields[ MAXFLD ]; /* Pointers to starts of fields in a label */ + char *old_format; /* Original Format string */ + char *used_fmt; /* Copy of format string actually used */ + const char *a; /* Pointer to next character to consider */ + const char *fmt; /* Format string actually used */ + double *ticks; /* Pointer to major tick mark values */ + double *minticks; /* Pointer to minor tick mark values */ + double cen0; /* Supplied value of cen */ + double junk; /* Unused value */ + double refval; /* Value for other axis to use when normalizing */ + double used_gap; /* The gap size actually used */ + int bot_digits; /* Digits value which makes labels as short as possible */ + int digits; /* New Digits value */ + int digset; /* Did the format string fix the no. of digits to use? */ + int fmtset; /* Was a format set? */ + int i; /* Tick index. */ + int nc[ MAXFLD ]; /* Lengths of fields in a label */ + int nf; /* Number of fields in a label */ + int nmajor; /* No. of major tick marks */ + int nminor; /* No. of minor tick marks */ + int ok; /* Are all adjacent labels different? */ + int reset_fmt; /* Re-instate the original state of the Format attribute? */ + + +/* If a NULL pointer has been supplied for "this", release the resources + allocated within GetTicks, and return. */ + if( !this ){ + (void) GetTicks( NULL, axis, NULL, &ticks, &nmajor, &minticks, &nminor, + 0, inval, &refval, pstatics, method, class, status ); + return NULL; + } + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Initialise the returned pointer. */ + ret = NULL; + +/* Store the supplied value of cen. */ + cen0 = cen ? *cen : AST__BAD ; + +/* Get a pointer to the current Frame from the Plot. */ + frame = astGetFrame( this, AST__CURRENT ); + +/* Initialise a flag to indicate that all adjacent labels are different. */ + ok = 0; + +/* Initialise the pointer to the list of formatted tick mark values to + indicate that no memory has yet been obtained. */ + labels = NULL; + +/* Initialise the pointer to a copy of the used format string to indicate + that no memory has yet been obtained. */ + used_fmt = NULL; + +/* Get a pointer to the axis. */ + ax = astGetAxis( frame, axis ); + +/* See if a value has been set for the axis Format. */ + fmtset = astTestFormat( frame, axis ); + +/* Get an initial set of tick mark values. This also establishes defaults for + LogTicks and LogLabel attributes, and so must be done before the + following block which uses the LogLabel attribute. */ + used_gap = GetTicks( this, axis, cen, &ticks, &nmajor, &minticks, &nminor, + fmtset, inval, &refval, pstatics, method, class, status ); + +/* See if exponential labels using superscript powers are required. */ + old_format = NULL; + reset_fmt = 0; + if( astGetLogLabel( this, axis ) && astGetEscape( this ) && + GCap( this, GRF__SCALES, 1, status ) ) { + +/* Save a copy of the Frame's Format value, if set. It will be + re-instated at the end of this function. */ + if( fmtset ) { + fmt = astGetFormat( frame, axis ); + old_format = astStore( NULL, (void *) fmt, strlen(fmt) + 1 ); + } + +/* Temporarily use a format of "%&g" to get "10**x" style axis labels, + with super-scripted "x". */ + astSetFormat( frame, axis, "%&g" ); + +/* Not all subclasses of Frame support this format specifier, so format a + test value, and see if it has two fields, the first of which is "10". + If not, we cannot use log labels so re-instate the original format. */ + nf = astFields( frame, axis, "%&g", astFormat( frame, axis, 1.0E4 ), + MAXFLD, fields, nc, &junk ); + if( nf != 2 || nc[ 0 ] != 2 || strncmp( fields[ 0 ], "10", 2 ) ) { + if( old_format ) { + astSetFormat( frame, axis, old_format ); + old_format = astFree( old_format); + } else { + astClearFormat( frame, axis ); + } + +/* If the "%&g" format is usable, note that we should reset the Format + back to its original state before leaving this function. */ + } else { + reset_fmt = 1; + } + } + +/* If a value has been set for the axis Format, see if the format string + contains a wildcard precision specifier ".*". If so, we are free to + vary the number of digits used in the label in order to produce + distinct labels. If no value has been set for the axis Format, we are + also free to vary the number of digits. */ + digset = 0; + if( fmtset ) { + fmt = astGetFormat( frame, axis ); + if( fmt ) { + digset = 1; + a = fmt; + while( (a = strchr( a, '.' )) ){ + if( *(++a) == '*' ) { + digset = 0; + break; + } + } + } + } + +/* If the axis precision has been specified, either through the Format string + or Digits value, or the Frame Digits value, we should use it so that the + user's attempts to get a specific result are not foiled. */ + if( digset || astTestAxisDigits( ax ) || astTestDigits( frame ) ){ + +/* Reserve memory to hold pointers to the formatted labels. */ + labels = (char **) astMalloc( sizeof(char *)*(size_t)nmajor ); + +/* Format the labels. We do not check that all adjacent labels are distinct + in order not to foil the users choice of format. That is, "ok" is set + non-zero by the call to CheckLabels, even if some identical adjacent + labels are found. */ + ok = CheckLabels( this, frame, axis, ticks, nmajor, 1, labels, refval, status ); + +/* Note the format used. */ + fmt = astGetFormat( frame, axis ); + if( fmt ) used_fmt = (char *) astStore( used_fmt, (void *) fmt, strlen( fmt ) + 1 ); + +/* If no precision has been specified for the axis, we need to find a + Digits value which gives different labels, but without using any more + digits than necessary. */ + } else if( astOK ){ + +/* Reserve memory to hold pointers to an initial set of labels formatted + with the default digits value. */ + labels = (char **) astMalloc( sizeof(char *)*(size_t)nmajor ); + +/* Produce these default labels. */ + CheckLabels( this, frame, axis, ticks, nmajor, 1, labels, refval, status ); + +/* The first task is to decide what the smallest usable number of digits + is. Starting at the default number of digits used above to produce the + default labels, we reduce the number of digits until one or more of the + formatted labels *increases* in length. This can happen for instance if + printf decides to include an exponent in the label. The *absolute* + minimum value 1. Set this first. */ + bot_digits = 1; + oldlabels = labels; + for( digits = astGetDigits( frame ) - 1; digits > 0; digits-- ){ + astSetAxisDigits( ax, digits ); + +/* CheckLabels2 formats the labels with the decreased number of digits, + and compares them with the old labels in "labels". If any of the new labels + are longer than the corresponding old labels, then a null pointer is + returned. Otherwise, a pointer is returned to the new set of labels. */ + newlabels = CheckLabels2( this, frame, axis, ticks, nmajor, + oldlabels, refval, status ); + +/* Free the old labels unless they are the orignal labels (which are + needed below). */ + if( oldlabels != labels ) { + for( i = 0; i < nmajor; i++ ){ + if( oldlabels[ i ] ) oldlabels[ i ] = (char *) astFree( (void *) oldlabels[ i ] ); + } + oldlabels = (char **) astFree( (void *) oldlabels ); + } + +/* If any of the labels got longer as a result of reducing the digits + value, then use the previous number of digits as the lowest possible + number of digits. Break out of the loop. */ + if( !newlabels ) { + bot_digits = digits + 1; + break; + } + +/* If none of the labels got longer, we arrive here. Use the shorter labels + for the next pass round this loop. */ + oldlabels = newlabels; + } + +/* Free any remaining labels. */ + if( oldlabels && oldlabels != labels ) { + for( i = 0; i < nmajor; i++ ){ + if( oldlabels[ i ] ) oldlabels[ i ] = (char *) astFree( (void *) oldlabels[ i ] ); + } + oldlabels = (char **) astFree( (void *) oldlabels ); + } + +/* Now loop round increasing the number of digits in the formatted labels + from the lowest usable value found above until all adjacent labels are + different. An arbitrary upper limit of 1000 is used for Digits to stop it + looping for ever. */ + for( digits = bot_digits; digits < 1000; digits++ ){ + +/* Store the new Digits value. */ + astSetAxisDigits( ax, digits ); + +/* Free memory used to hold the current set of labels. A new set will be + created by the following call to CheckLabels. */ + if( labels ) { + for( i = 0; i < nmajor; i++ ) labels[ i ] = astFree( labels[ i ] ); + } + +/* Break out of the loop if a Digits value has been found which results + in all adjacent labels being different. Note the format used (we know + the Format attribute is currently unset, but the default Format string + reflects the current value of the Digits attribute). */ + if( CheckLabels( this, frame, axis, ticks, nmajor, 0, labels, refval, status ) ) { + ok = 1; + fmt = astGetFormat( frame, axis ); + used_fmt = (char *) astStore( NULL, (void *) fmt, strlen( fmt ) + 1 ); + break; + } + } + +/* Clear the Digits value. */ + astClearAxisDigits( ax ); + } + +/* Annul the pointer to the Axis. */ + ax = astAnnul( ax ); + +/* Re-instate the original format specifier if required. */ + if( reset_fmt ) { + if( old_format ) { + astSetFormat( frame, axis, old_format ); + old_format = astFree( old_format); + } else { + astClearFormat( frame, axis ); + } + } + +/* If suitable labels were found... */ + if( ok && astOK ) { + +/* Store the used gap size. */ + *gap = used_gap; + +/* If the caller has specified the number of minor tick marks to use, + use the specified value rather than the value found above. */ + if( astTestMinTick( this, axis ) ){ + nminor = astGetMinTick( this, axis ); + } + +/* Allocate memory for the returned structure. */ + ret = (TickInfo *) astMalloc( sizeof( TickInfo ) ); + +/* If the pointer can be used, store the information. */ + if( astOK ){ + ret->nmajor = nmajor; + ret->nminor = nminor; + ret->ticks = ticks; + ret->minticks = minticks; + ret->labels = labels; + ret->fmt = used_fmt; + used_fmt = NULL; + ret->start = NULL; + ret->length = NULL; + ret->nsect = 0; + ret->gap = used_gap; + } + +/* If no suitable labels were found report an error. */ + } else if( astOK ){ + if( fmtset ){ + astError( AST__PLFMT, "%s(%s): No numerical labels can be produced " + "for axis %d using the supplied %s format string '%s'.", status, + method, class, axis + 1, astGetClass( frame ), + astGetFormat( frame, axis ) ); + } else { + astError( AST__PLFMT, "%s(%s): No suitable format can be found to " + "produce numerical labels for axis %d of a %s.", status, + method, class, axis + 1, astGetClass( frame ) ); + } + } + +/* Release any remaining resources. */ + frame = astAnnul( frame ); + if( used_fmt ) used_fmt = astFree( used_fmt ); + +/* If an error has occurred, release the returned information. */ + if( !astOK ){ + ticks = (double *) astFree( (void *) ticks ); + minticks = (double *) astFree( (void *) minticks ); + if( labels ){ + for( i = 0; i < nmajor; i++ ) { + labels[ i ] = (char *) astFree( (void *) labels[ i ] ); + } + labels = (char **) astFree( (void *) labels ); + if( ret ) (void ) astFree( (void *) ret->fmt ); + } + ret = (TickInfo *) astFree( (void *) ret ); + } + +/* Return the structure. */ + return ret; + +/* Undefine local constants. */ +#undef MAXFLD + +} + +static int TraceBorder( AstPlot *this, AstMapping *map, double xlo, double xhi, + double ylo, double yhi, int dim, double tol, + int edges[ 4 ], const char *method, const char *class, + int *status ) { +/* +* Name: +* TraceBorder + +* Purpose: +* Trace the boundary between good and bad physical coordinates through a +* fine grid. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int TraceBorder( AstPlot *this, AstMapping *map, double xlo, double xhi, +* double ylo, double yhi, int dim, double tol, +* int edges[ 4 ], const char *method, const char *class, +* int *status ) { + +* Class Membership: +* Plot member function. + +* Description: +* A rectangular grid of points in graphics coords is created covering +* the region specified by (xlo,xhi,ylo,yhi), using "dim" points along +* each axis. This grid of points is converted into physical (WCS) +* coords, and a flag is associatred with each point saying whether +* the WCS coords are good or bad. The cells in this grid are then +* scanned from bottom left to top right in raster fashion (each cell +* has a grid point at each of its 4 corners). If a cell has a mix of +* bad and good corners, the good/bad boundary must pass through it. +* If the grid is sufficiently fine (as defined by "tol") then this +* function draws a single straight line through each cell as an +* approximation to the good bad boundary. This line joins the centres +* of the two cells edges through which the boundary passes (as +* indicated by the fact that one end of the edge has good WCS coords +* and the other end has bad WCS coords). If the grid is not +* sufficiently fine to meet the "tol" requirement, then this function +* is invoked recursively to draw the curve through each cell through +* which the boundary passes. + +* Parameters: +* this +* The plot. +* map +* The Graphics->WCS mapping. +* xlo +* The lower bounds on the graphics X axis of the rectangle being +* considered. +* xhi +* The upper bounds on the graphics X axis of the rectangle being +* considered. +* ylo +* The lower bounds on the graphics Y axis of the rectangle being +* considered. +* yhi +* The upper bounds on the graphics Y axis of the rectangle being +* considered. +* dim +* The number of points along one edge of the fine grid. +* tol +* The plotting tolerance. Once each cell is smaller than this +* distance (in graphics coords), the cell is drawn. Otherwise, +* this function is invoked recursively to draw the cell using a +* finer grid. +* edges +* A pointer to an array of 4 int, in which will be returned +* flags indicating if the good/bad boundary intersects any of the +* edges of the grid. These flags are stored in the order left, +* top, right, bottom. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPointSet *pset1; + AstPointSet *pset2; + double **ptr2; + double *px1; + double *px2; + double *py1; + double *py2; + double cxhi; + double cxlo; + double cyhi; + double cylo; + double dx; + double dy; + float xc; + float yc; + int *bndry; + int *drawn; + int bad1; + int bad2; + int bad3; + int bad4; + int icell; + int icol; + int irow; + int lastcell; + int ncell; + int recurse; + int result; + int sedges[ 4 ]; + int totcells; + +/* Initialise returned edge flags */ + edges[ 0 ] = 0; + edges[ 1 ] = 0; + edges[ 2 ] = 0; + edges[ 3 ] = 0; + +/* Initialise the returned flag to indicate that no bad regions were + found. */ + result = 0; + +/* Check the global status. */ + if ( !astOK ) return result; + +/* Create a grid of graphics and WCS coords covering the specified region + of graphics coords. "ptr2" is used to access the WCS coords at each + point in the grid. */ + ptr2 = MakeGrid( this, NULL, map, 0, dim, xlo, xhi, ylo, yhi, + 2, &pset1, &pset2, 0, method, class, status ); + +/* The number of cells along each axis of the grid is one less than the + number of points. Also get the number of cells in the whole grid. */ + ncell = dim - 1; + totcells = ncell*ncell; + +/* Store the dimensions of each cell in graphics coords. */ + dx = ( xhi - xlo )/ncell; + dy = ( yhi - ylo )/ncell; + +/* Set a flag indicating if the cell size is larger than the required + plotting tolerance. If so, we will call this function recursively to + draw the curve using a finer grid. */ + recurse = ( dx > tol || dy > tol ); + +/* If we have not yet reached the plotting tolerance, allocate work arrays + with one element for each cell in the grid. */ + if( recurse ) { + bndry = astMalloc( sizeof( int )*totcells ); + drawn = astMalloc( sizeof( int )*totcells ); + } else { + bndry = NULL; + drawn = NULL; + } + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* If required, initialise work arrays to hold zero. */ + if( recurse ) { + +/* Initialise "boundary passes through cell" flags to zero. */ + memset( bndry, 0, sizeof( int )*totcells ); + +/* Initialise "cell has been drawn" flags to zero. */ + memset( drawn, 0, sizeof( int )*totcells ); + } + +/* Store the Y graphics coords at the bottom and top of the first row. */ + cylo = ylo; + cyhi = ylo + dy; + +/* Store pointers to the physical coords at the bottom left corner of the + first cell in the first row. */ + px1 = ptr2[ 0 ]; + py1 = ptr2[ 1 ]; + +/* Store pointers to the physical coords at the top left corner of the + first cell in the first row. */ + px2 = px1 + dim; + py2 = py1 + dim; + +/* Store the index of the last cell in a row or column. */ + lastcell = ncell - 1; + +/* Initialise index of next cell. */ + icell = 0; + +/* Loop round every row of cells in this grid. */ + for( irow = 0; irow < ncell; irow++ ) { + +/* See if the physical coords are bad at the bottom left corner of the + first cell in this row. At the same time, increment the pointers so + they refer to the bottom right corner of the first cell in this row. */ + bad1 = ( *px1 == AST__BAD || *py1 == AST__BAD ); + +/* Increment the pointers. Do not do it in the above "if" statement since + the or (!!) means that the second expression may never be evaluated. */ + px1++; + py1++; + +/* See if the physical coords are bad at the top left corner of the + first cell in this row. At the same time, increment the pointers so + they refer to the top right corner of the first cell in this row. */ + bad2 = ( *px2 == AST__BAD || *py2 == AST__BAD ); + px2++; + py2++; + +/* Loop round every cell in the current row of cells. */ + for( icol = 0; icol < ncell; icol++, icell++ ) { + +/* See if the physical coords are bad at the bottom right corner of the + current cell in this row. At the same time, increment the pointers so + they refer to the bottom right corner of the next cell in this row. */ + bad3 = ( *px1 == AST__BAD || *py1 == AST__BAD ); + px1++; + py1++; + +/* See if the physical coords are bad at the top right corner of the + current cell in this row. At the same time, increment the pointers so + they refer to the top right corner of the next cell in this row. */ + bad4 = ( *px2 == AST__BAD || *py2 == AST__BAD ); + px2++; + py2++; + +/* Set the returned flag non-zero if any invalidpositions are found. */ + if( bad1 || bad2 || bad3 || bad4 ) result = 1; + +/* If there are a mixture of good and bad corners, the good/bad boundary + must pass through the current cell. */ + if( bad2 != bad1 || bad3 != bad1 || bad4 != bad1 ) { + +/* If we have not yet reached the required plotting tolerance, set a flag + to indicate that the boundary should be plotted through this cell + using a recirsive call to this function. */ + if( recurse ) { + bndry[ icell ] = 1; + +/* If we have reached the required plotting tolerance, draw the boundary + as a straight line between the centres of the edges through which the + boundary enteres and leaves the current cell. */ + } else { + +/* Get the upper and lower graphics X bounds of the current cell. */ + cxlo = xlo + icol*dx; + cxhi = cxlo + dx; + +/* If an edge of the current cell has good coords at one end but bad + coords at the other, the boundary is assumed to pass through the edge + at its centre. Normally, we expect only two cell edges to have this + property (i.e the boundary enters the cell through one edge and leaves + through the other). However, sometimes all four edges may have this + property. In this case, two sections of the boundary must pass through + the cell, and there is no way of knowing which edges connect together + (short of further recursion), and we arbitrarily decide to join opposite + edges. */ + if( bad1 == bad4 && bad2 == bad3 ) { + +/* Draw a horizontal line through the cell centre */ + yc = 0.5*( cylo + cyhi ); + Bpoly( this, (float) cxlo, yc, status ); + Apoly( this, (float) cxhi, yc, status ); + +/* Draw a vertical line through the cell centre */ + xc = 0.5*( cxlo + cxhi ); + Bpoly( this, xc, (float) cylo, status ); + Apoly( this, xc, (float) cyhi, status ); + +/* If the boundary passes through the left hand edge, it must also have + passed through the right edge of the previous cell in the row (unless + this is the first cell in the row), so we do not need to begin a new + polyline (we can just extend the existing polyline). */ + } else if( bad1 != bad2 ) { + +/* If this is the first cell in the row, begin a new polyline. */ + yc = 0.5*( cylo + cyhi ); + if( icol == 0 ) Bpoly( this, (float) cxlo, yc, status ); + +/* and through the top edge, draw a line between the centres of the left + and top edges. */ + if( bad2 != bad4 ) { + xc = 0.5*( cxlo + cxhi ); + Apoly( this, xc, (float) cyhi, status ); + +/* or through the right edge, draw a line between the centres of the left + and right edges. */ + } else if( bad3 != bad4 ) { + Apoly( this, (float) cxhi, yc, status ); + +/* Otherwise, draw a line between the centres of the left and bottom edges. */ + } else { + xc = 0.5*( cxlo + cxhi ); + Apoly( this, xc, (float) cylo, status ); + } + +/* If the boundary passes through the top edge (we do not need to check + the left edge because that was done above)... */ + } else if( bad4 != bad2 ) { + +/* and through the right edge, draw a line between the centres of the top + and right edges. */ + if( bad3 != bad4 ) { + xc = 0.5*( cxlo + cxhi ); + yc = 0.5*( cylo + cyhi ); + Bpoly( this, xc, (float) cyhi, status ); + Apoly( this, (float) cxhi, yc, status ); + +/* Otherwise, draw a line between the centres of the top and bottom edges. */ + } else { + xc = 0.5*( cxlo + cxhi ); + Bpoly( this, xc, (float) cyhi, status ); + Apoly( this, xc, (float) cylo, status ); + } + +/* If the boundary passes through the right edge it must also pass + throught the bottom edge since all other combinations will have been + trapped above. */ + } else { + xc = 0.5*( cxlo + cxhi ); + yc = 0.5*( cylo + cyhi ); + Bpoly( this, xc, (float) cylo, status ); + Apoly( this, (float) cxhi, yc, status ); + } + +/* If the current cell is on the edge of the grid, set flags in the + returned "edges" array to indicate that the boundary passes out of + the grid on the appropriate edge. */ + if( icol == 0 ) { + if( bad1 != bad2 ) edges[ 0 ] = 1; /* Left edge */ + } else if( icol == lastcell ) { + if( bad3 != bad4 ) edges[ 2 ] = 1; /* Right edge */ + } + + if( irow == 0 ) { + if( bad1 != bad3 ) edges[ 3 ] = 1; /* Bottom edge */ + } else if( irow == lastcell ) { + if( bad2 != bad4 ) edges[ 1 ] = 1; /* Top edge */ + } + } + } + +/* The flags for the right hand corners of the current cell can be + re-used as the flags for the left hand corners of the next cell. */ + bad1 = bad3; + bad2 = bad4; + } + +/* Store the Y graphics coords at the bottom and top of the next row. */ + cylo = cyhi; + cyhi = cylo + dy; + } + +/* If we have not yet reached the required plotting tolerance, call this + function recursively to draw the boundary through the cells identified + above. On each pass through this loop, we may discover more boundary + cells in the grid, in addition to those found above. Continue looping + until no further boundary cells are found. */ + while( recurse ) { + +/* Assume that the current pass though this loop will result in all boundary + cells being draw, in which case we can then leave the loop. */ + recurse = 0; + +/* Store the Y graphics coords at the bottom and top of the first row. */ + cylo = ylo; + cyhi = ylo + dy; + +/* Initialise the cell index */ + icell = 0; + +/* Loop round every row of cells in this grid. */ + for( irow = 0; irow < ncell; irow++ ) { + +/* Loop round every cell in the current row of cells. */ + for( icol = 0; icol < ncell; icol++, icell++ ) { + +/* If the good/bad boundary passes through the current cell we need to + draw it unless it has already been drawn. */ + if( bndry[ icell ] && ! drawn[ icell ] ){ + +/* Get the upper and lower graphics X bounds of the current cell. */ + cxlo = xlo + icol*dx; + cxhi = cxlo + dx; + +/* Call this function recursively to draw the boundary through the current + cell, setting the returned flag non-zero if any bad positions are found. */ + if( TraceBorder( this, map, cxlo, cxhi, cylo, cyhi, 3, tol, + sedges, method, class, status ) ) result = 1; + +/* The boundary may have passed out of the current cell and then back + into the cell on the same edge (i.e. a small loop that pokes out into + a neighbouring cell). Such neighbouring cells may not have been + identified by the earlier section of this function, so we now ensure + that any such cells are flagged as boundary cells. */ + +/* If the boundary passed out of the left edge of the cell... */ + if( sedges[ 0 ] ) { + +/* If the current cell is at the left edge of the grid, indicate that the + boundary passes out of the left edge of the grid. */ + if( icol == 0 ) { + edges[ 0 ] = 1; /* Left edge */ + +/* Otherwise, if the left hand neighbour of the current cell is not + flagged as a boundary cell, flag it now and indicate that another pass + though the loop is needed to draw the extra cell. */ + } else if( ! bndry[ icell - 1 ] ) { + bndry[ icell - 1 ] = 1; + recurse = 1; + } + } + +/* If the boundary passed out of the top edge of the cell... */ + if( sedges[ 1 ] ) { + +/* If the current cell is at the top edge of the grid, indicate that the + boundary passes out of the top edge of the grid. */ + if( irow == lastcell ) { + edges[ 1 ] = 1; /* Top edge */ + +/* Otherwise, ensure that the upper neighbour of the current cell is + flagged as a boundary cell. */ + } else { + bndry[ icell + ncell ] = 1; + } + } + +/* If the boundary passed out of the right edge of the cell... */ + if( sedges[ 2 ] ) { + +/* If the current cell is at the right edge of the grid, indicate that the + boundary passes out of the right edge of the grid. */ + if( icol == lastcell ) { + edges[ 2 ] = 1; /* Right edge */ + +/* Otherwise, ensure that the right hand neighbour of the current cell is + flagged as a boundary cell. */ + } else { + bndry[ icell + 1 ] = 1; + } + } + +/* If the boundary passed out of the bottom edge of the cell... */ + if( sedges[ 3 ] ) { + +/* If the current cell is at the bottom edge of the grid, indicate that the + boundary passes out of the bottom edge of the grid. */ + if( irow == 0 ) { + edges[ 3 ] = 1; /* Bottom edge */ + +/* Otherwise, if the lower neighbour of the current cell is not flagged + as a boundary cell, flag it now and indicate that another pass though + the loop is needed to draw the extra cell. */ + } else if( ! bndry[ icell - ncell ] ) { + bndry[ icell - ncell ] = 1; + recurse = 1; + } + } + +/* Indicate this cell has been drawn. */ + drawn[ icell ] = 1; + } + } + +/* Store the Y graphics coords at the bottom and top of the next row. */ + cylo += dy; + cyhi = cylo + dy; + } + } + } + +/* Free resources */ + bndry = astFree( bndry ); + drawn = astFree( drawn ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Trans( AstPlot *this, AstFrame *frm, AstMapping *mapping, + AstPointSet *in, int forward, AstPointSet *out, + int norm, const char *method, const char *class, int *status ) { +/* +* Name: +* Trans + +* Purpose: +* Use a Mapping to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* AstPointSet *Trans( AstPlot *this, AstFrame *frm, AstMapping *mapping, +* AstPointSet *in, int forward, AstPointSet *out, +* int norm, const char *method, const char *class ) + +* Class Membership: +* Plot member function. + +* Description: +* This performs the same task as the protected method astTransform +* but uses the astTransform method for the supplied Mapping instead +* the parent method for the Plot. This allows the Mapping to be +* extracted from the Plot using astGetMapping once, rather than every +* time a mapping is performed. + +* Parameters: +* this +* Pointer to the Plot (only used to access clipping attributes and +* other methods). +* frm +* Pointer to the Current Frame in the Plot. If this is NULL, then +* a pointer for the Current Frame is found within this function if +* required (i.e. if "forward" and "norm" are both non-zero). +* mapping +* Pointer to the Mapping extracted from the Plot. If this is NULL, then +* a pointer for the base->current Mapping is found within this function. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate +* transformation should be applied while a zero value requests the +* inverse transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* norm +* The normalisation of returned physical coordinates is only done +* if "norm" is non-zero. Otherwise they are left as returned by +* astTransform. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - Clipping is only performed as set up using the astClip method. +* In particular, the clipping specified by the arguments to the astPlot +* constructor function is NOT performed. This is done in order to improve +* the efficiency of the curve drawing method astGridLine. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the Plot being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstFrame *cfr; /* Pointer to the Current Frame */ + AstFrame *fr; /* Pointer to the clipping Frame */ + AstMapping *map; /* Pointer to output->clip mapping */ + AstPointSet *clip; /* Positions in clipping Frame */ + AstPointSet *result; /* Positions in output Frame */ + double **ptr_clip; /* Pointer to clipping Frame data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *work; /* Pointer to array holding an o/p position */ + double axval; /* Axis value in clipping frame */ + double lbnd; /* Lower bound on current clipping axis */ + double ubnd; /* Upper bound on current clipping axis */ + int axin; /* Is the axis value within the allowed range? */ + int clip_norm; /* Normalise the clipping positions? */ + int clip_or; /* Combine axes using a logical OR? */ + int clipit; /* Should the current point be clipped? */ + int i; /* Point index */ + int iframe; /* Validated index for clipping Frame */ + int j; /* Axis index */ + int naxes; /* Number of axes in clipping Frame */ + int ncoord_out; /* Number of coordinates per output point */ + int npoint; /* Number of points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Ensure we have a Mapping */ + if( !mapping ) mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent FrameSet class. */ + result = astTransform( mapping, in, forward, out ); + +/* Get the dimensions of the returned data, and an array of pointers to + the axis values. */ + ncoord_out = astGetNcoord( result ); + npoint = astGetNpoint( result ); + ptr_out = astGetPoints( result ); + +/* If we have done a forward mapping, we now normalise the returned physical + positions if required using the astNorm method for the supplied object. */ + if( forward && norm ){ + +/* If no Frame was supplied, get a pointer to the Current Frame. Otherwise, + use the supplied pointer. */ + if( !frm ) { + cfr = astGetFrame( this, AST__CURRENT ); + } else { + cfr = frm; + } + +/* Get work space to hold a single positions. */ + work = (double *) astMalloc( sizeof(double)*(size_t)ncoord_out ); + +/* Check the work space and axis pointers can be used. */ + if( astOK ){ + +/* Now loop through every position, copying the axis values to the work array, + normalising them using astNorm, and copying them back to the returned + PointSet. */ + for( i = 0; i < npoint; i++ ){ + for( j = 0; j < ncoord_out; j++ ) work[ j ] = ptr_out[ j ][ i ]; + astNorm( cfr, work ); + for( j = 0; j < ncoord_out; j++ ) ptr_out[ j ][ i ] = work[ j ]; + } + } + +/* Free the work space. */ + work = (double *) astFree( (void *) work ); + +/* Annul the pointer to the Current Frame if it was obtained in this + function. */ + if( !frm ) cfr = astAnnul( cfr ); + + } + +/* Clipping is only performed if the bounds of a clipping region are + available for both axes. */ + if( this->clip_lbnd && this->clip_ubnd ){ + +/* Validate and translate the index of the clipping Frame. */ + iframe = astValidateFrameIndex( this, this->clip_frame, method ); + +/* Obtain a pointer to the clipping Frame and determine how many axes it + has. */ + fr = astGetFrame( this, iframe ); + naxes = astGetNaxes( fr ); + +/* Report an error if the number of bounds does not equal the number of + axes in the clipping Frame. */ + if( astOK && naxes != this->clip_axes ){ + astError( AST__CLPAX, "%s%s): The supplied %s specifies clipping " + "in %d dimensions, but the clipping Frame ('%s') has " + "%d axes.", status, method, class, class, this->clip_axes, + astGetTitle( fr ), naxes ); + } + +/* Set a flag indicating if the coordinates in the clipping frame need to + be normalised. */ + clip_norm = 1; + +/* We now obtain a pointer to a PointSet holding the corresponding + coordinates in the clipping frame. If the clipping frame is the + base frame, then take a clone of the PointSet holding base frame + coordinates. */ + if( iframe == astGetBase( this ) ){ + if( forward ){ + clip = astClone( in ); + } else { + clip = astClone( result ); + } + +/* If the clipping frame is the current frame, then take a clone of the + PointSet holding current coordinates. Note, if the returned physical + coordinates have already been normalised, we don't need to normalise + the clipping coordinates. */ + } else if( iframe == astGetCurrent( this ) ){ + if( forward ){ + clip = astClone( result ); + if( norm ) clip_norm = 0; + } else { + clip = astClone( in ); + } + +/* If the clipping Frame is neither the base nor the current Frame, we need + to map the returned normalised points into the clipping Frame. */ + } else { + if( forward ){ + map = astGetMapping( this, AST__CURRENT, iframe ); + } else { + map = astGetMapping( this, AST__BASE, iframe ); + } + clip = astTransform( map, result, 1, NULL ); + map = astAnnul( map ); + } + +/* Get a pointer to the coordinate data in the clipping Frame. */ + ptr_clip = astGetPoints( clip ); + +/* If necessary, normalise the coordinates in the clipping frame. */ + if( clip_norm ){ + +/* Get work space to hold a single position. */ + work = (double *) astMalloc( sizeof(double)*(size_t)naxes ); + +/* Check the work space and axis pointers can be used. */ + if( astOK ){ + +/* Now loop through every position, copying the axis values to the work array, + normalising them using astNorm, and copying them back to the clipping + PointSet. */ + for( i = 0; i < npoint; i++ ){ + for( j = 0; j < naxes; j++ ) work[ j ] = ptr_clip[ j ][ i ]; + astNorm( fr, work ); + for( j = 0; j < naxes; j++ ) ptr_clip[ j ][ i ] = work[ j ]; + } + } + +/* Free the work space. */ + work = (double *) astFree( (void *) work ); + + } + +/* If all has gone ok, we will now clip the returned points. */ + if( astOK ){ + +/* Get the logical operation to be used to determine if a point is to be + clipped. A zero value means that a logical AND is to be performed + between the axes (i.e. all axes must be within the supplied bounds for a + point to be retained). A non-zero value means that a logical OR is to be + performed between the axes (i.e. only a single axis need be within the + supplied bounds for a point to be retained). */ + clip_or = astGetClipOp( this ); + +/* Do each point in turn. */ + for( j = 0; j < npoint; j++ ){ + +/* If all axes must fall within the supplied range to avoid the point being + clipped (i.e. if clip_or is 0), then assume initially that the point + is not to be clipped. This will be toggled as soon as the first + out-of-bounds point is found. If, on the other hand, the point is + only clipped if all axis values are out-of-bounds, then assume + initially that the point is to be clipped. This will be toggled as + soon as the first axis value is found which is not out-of-bounds. */ + clipit = clip_or; + +/* Check each axis value for the current point. */ + for( i = 0; i < naxes; i++ ){ + axval = ptr_clip[ i ][ j ]; + +/* Chekc that it is not bad. */ + if( axval != AST__BAD ){ + +/* Store the bounds of the clipping volume on this axis. */ + lbnd = this->clip_lbnd[ i ]; + ubnd = this->clip_ubnd[ i ]; + +/* Set a flag indicating if the axis value is within the specified range. + If the supplied bounds are reversed, they specify the range to exclude, + otherwise they specify the range to include. */ + if( lbnd <= ubnd ){ + axin = ( axval >= lbnd && axval <= ubnd ); + } else { + axin = ( axval < ubnd || axval > lbnd ); + } + +/* If the point is within the range and only one such point is + required to avoid the point being clipped, indicate that the point + should not be clipped, and leave the loop. */ + if( axin && clip_or ){ + clipit = 0; + break; + +/* If the point is not within the range and we only one such point is + required to cause the point to be clipped, indicate that the point + should be clipped, and leave the loop. */ + } else if( !axin && !clip_or ){ + clipit = 1; + break; + } + +/* Clip the point if any axis value is bad in the clipping Frame. */ + } else { + clipit = 1; + break; + } + + } + +/* If the point is to be clipped, set all returned axis values bad. */ + if( clipit ) { + for( i = 0; i < naxes; i++ ) ptr_out[ i ][ j ] = AST__BAD; + } + + } + + } + +/* Annul the PointSet holding clipping Frame positions. */ + if( clip ) clip = astAnnul( clip ); + +/* Annul the clipping Frame pointer. */ + fr = astAnnul( fr ); + + } + +/* If an error has occurred, annul the result. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; + +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Use a Plot to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out ) + +* Class Membership: +* Plot member function (over-rides the astTransform protected +* method inherited from the FrameSet class). + +* Description: +* This function takes a Plot and a set of points encapsulated in a +* PointSet and transforms the points from graphics coordinates to +* physical coordinates (in the forward direction). If the returned +* positions are physical coordinates (i.e. if a forward mapping is +* performed) they are normalised using the astNorm method of the supplied +* Plot. The returned axis values are set to AST__BAD for any positions +* which are outside the clipping volume set up by the astClip method. + +* Parameters: +* this +* Pointer to the Plot. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate +* transformation should be applied while a zero value requests the +* inverse transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - Clipping is only performed as set up using the astClip method. +* In particular, the clipping specified by the arguments to the astPlot +* constructor function is NOT performed. This is done in order to improve +* the efficiency of the curve drawing method astGridLine. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the Plot being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstMapping *map; /* Pointer to the mapping */ + AstPointSet *result; /* Positions in output Frame */ + AstPlot *plot; /* The Plot */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the Plot. */ + plot = (AstPlot *) this; + +/* Get the Mapping from the base to the current Frame. */ + map = astGetMapping( plot, AST__BASE, AST__CURRENT ); + +/* Do the transformation. */ + result = Trans( plot, NULL, map, in, forward, out, 1, "astTransform", + astGetClass( this ), status ); + +/* Annul the mapping. */ + map = astAnnul( map ); + +/* If an error has occurred, annul the result. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; + +} + +static double Typical( int n, double *value, double lolim, double hilim, + double *width, int *status ) { +/* +* Name: +* Typical + +* Purpose: +* Return a typical value within the supplied array of values. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* double Typical( int n, double *value, double lolim, double hilim, +* double *width, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This function locates the approximate mode of the supplied values, +* and returns one of the supplied values which is close to the modal +* value. Values outside an indicated range are ignored. + +* Parameters: +* n +* The number of data values. +* value +* A pointer to an array of "n" values. +* lolim +* Values less than lolim are ignored. Supply as -DBL_MAX if there +* is no lower limit. +* hilim +* Values greater than hilim are ignored. Supply as DBL_MAX if there +* is no upper limit. +* width +* Pointer to a double in which to return the width (i,e, data range) +* of the non-empty histogram cells. This is an estimate of the +* range of used values in the supplied array. NULL may be supplied. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A typical value from the supplied array. AST__BAD is returned only +* if an error has occurred, or if all the supplied values are AST__BAD +* or outside the specified range. + +*/ + +/* Local Variables: */ + double *a; /* Pointer to next value */ + double cnt; /* Modified count */ + double delta; /* Bin size */ + double maxval; /* Maximum supplied value */ + double mean; /* Mean supplied value */ + double minval; /* Minimum supplied value */ + double result; /* The returned value. */ + double w0; /* Rate of increase of weight with dist from edge */ + double w1; /* Weight for left edge */ + double w2; /* Weight for right edge */ + double w; /* Weight for this bin */ + int *hist; /* Pointer to first cell of histogram array */ + int i; /* Loop count */ + int ibin; /* Bin index */ + int maxcnt; /* Maximum no. of values in any bin */ + int modify; /* Modify the effect of the edge bins? */ + int nbin; /* No. of bins in histogram */ + int nc; /* Total number of points in histogram */ + int ngood; /* No. of good values supplied */ + int nonemp; /* No. of non-empty bins in hstogram */ + +/* Initialise. */ + result = AST__BAD; + if( width ) *width = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + ibin = 0; + +/* Find the minimum and maximum value in the supplied array, which are + also within the supplied limits. Also store the first good value + encountered in "result". */ + minval = DBL_MAX; + maxval = -DBL_MAX; + a = value; + ngood = 0; + for( i = 0; i < n; i++, a++ ) { + if( *a != AST__BAD ) { + if( *a >= lolim && *a <= hilim ) { + if( *a < minval ) minval = *a; + if( *a > maxval ) maxval = *a; + if( ngood == 0 ) result = *a; + ngood++; + } + } + } + +/* Initialise the returned width to the total data range. */ + if( width && maxval != -DBL_MAX ) *width = maxval - minval; + +/* If less than 3 points were found, we will return the first. Otherwise, if + 3 or more good values were found, find a typical value. */ + if( ngood > 2 ) { + +/* We will form a histogram of the supplied values in order to find the + mode. The number of bins in this histogram is chosen so that there + is an average of 2 points per bin. Find the number of bins. */ + nbin = ( ngood + 1 )/2; + +/* Find the bin size. If zero (i.e. if all values are equal), return the + first good value established above. */ + delta = ( maxval - minval )/ nbin; + if( delta > 0.0 ) { + +/* Allocat ememory for the histogram. */ + hist = astMalloc( sizeof(int)*(size_t)nbin ); + if( hist ) { + +/* Initialise the histogram. */ + for( i = 0; i < nbin; i++ ) hist[ i ] = 0; + +/* Form the histogram. Form the mean data value at the same time. */ + mean = 0.0; + a = value; + nc = 0; + for( i = 0; i < n; i++, a++ ){ + if( *a != AST__BAD ) { + if( *a >= lolim && *a <= hilim ) { + mean += *a; + ibin = (int) ( ( *a - minval )/ delta ); + if( ibin == nbin ) ibin--; + hist[ ibin ]++; + nc++; + } + } + } + + mean /= ngood; + +/* We tend to prefer not to use reference values which are very close the + the limits since they can give problems with regard to normalization + (rounding errors can knock them over the edge), so we modify the counts + in each bin of the histogram to reduce the impact of bins near the edge. + However, we do not do this if the number of bins is very small or if + all the counts are in the edge bins. */ + modify = ( nbin > 4 && + ( hist[ 0 ] + hist[ nbin - 1 ] < 0.98*ngood ) ); + +/* Find the bin with the highest modified count. If there is more than one bin + with the highest modified count, choose the one which is closest to the + mean data value found above. Also count the number of non-empty bins. */ + nonemp = 0; + maxcnt = 0; + w0 = nbin/2; + for( i = 0; i < nbin; i++ ) { + + cnt = hist[ i ]; + if( cnt ) nonemp++; + + if( modify ) { + w1 = i*w0; + w2 = ( nbin - 1 - i )*w0; + w = ( w1 < w2 ) ? w1 :w2; + if( w < 1.0 ) cnt *= w; + } + + if( cnt > maxcnt ) { + maxcnt = cnt; + ibin = i; + + } else if( cnt == maxcnt ) { + if( fabs( minval + ( i - 0.5 )*delta - mean ) < + fabs( minval + ( ibin - 0.5 )*delta - mean ) ) { + maxcnt = cnt; + ibin = i; + } + } + } + +/* Free the histogram memory. */ + hist = astFree( hist ); + +/* If required, return the width of the non-empty bins. */ + if( width ) *width = nonemp*delta; + +/* Call this function recursively to refine the value, restricting + attention to those data values which are within the range of the bin + found above. */ + if( maxcnt < nc && ibin*delta > 1000.0*DBL_EPSILON*fabs(maxval) ) { + minval += ibin*delta; + maxval = minval + delta; + result = Typical( n, value, minval, maxval, NULL, status ); + } + } + } + } + +/* Return the result. */ + return result; +} + +static int GetUseColour( AstPlot *this, int id, int *status ) { +/* +* Name: +* GetUseColour + +* Purpose: +* Get the Colour value to use for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int GetUseColour( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This returns the Colour value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the colour +* for the first set specific value is returned. For example, if the +* Colour for AST__AXES_ID is requested, then the colour for AST__AXIS1_ID +* will be returned if set, and otherwise the colour for AST__AXIS2_ID will +* be returned. If AST__AXIS2_ID is not set either, then the default for +* AST__AXIS2_ID will be returned. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Colour value to use. + +*/ + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return NOCOLOUR; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the value of the first + set genuine identifier. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + if( nid > 1 ) { + if( astTestColour( this, id1 ) ) { + id = id1; + + } else if( nid > 1 && astTestColour( this, id2 ) ) { + id = id2; + + } else if( nid > 2 && astTestColour( this, id3 ) ) { + id = id3; + + } else { + id = id1; + } + } + +/* Return the result. */ + return astGetColour( this, id ); + +} + +static int GetUseFont( AstPlot *this, int id, int *status ) { +/* +* Name: +* GetUseFont + +* Purpose: +* Get the Font value to use for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int GetUseFont( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This returns the Font value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the Font +* for the first set specific value is returned. For example, if the +* Font for AST__AXES_ID is requested, then the Font for AST__AXIS1_ID +* will be returned if set, and otherwise the Font for AST__AXIS2_ID will +* be returned. If AST__AXIS2_ID is not set either, then the default for +* AST__AXIS2_ID will be returned. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Font value to use. + +*/ + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return NOFONT; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the value of the first set + genuine identifier. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + if( nid > 1 ) { + if( astTestFont( this, id1 ) ) { + id = id1; + + } else if( nid > 1 && astTestFont( this, id2 ) ) { + id = id2; + + } else if( nid > 2 && astTestFont( this, id3 ) ) { + id = id3; + + } else { + id = id1; + } + } + +/* Return the result. */ + return astGetFont( this, id ); + +} + +static double GetUseSize( AstPlot *this, int id, int *status ) { +/* +* Name: +* GetUseSize + +* Purpose: +* Get the Size value to use for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* double GetUseSize( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This returns the Size value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the Size +* for the first set specific value is returned. For example, if the +* Size for AST__AXES_ID is requested, then the Size for AST__AXIS1_ID +* will be returned if set, and otherwise the Size for AST__AXIS2_ID will +* be returned. If AST__AXIS2_ID is not set either, then the default for +* AXIS2_ID will be returned. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Size value to use. + +*/ + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return NOSIZE; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the value of the first set + genuine identifier. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + if( nid > 1 ) { + if( astTestSize( this, id1 ) ) { + id = id1; + + } else if( nid > 1 && astTestSize( this, id2 ) ) { + id = id2; + + } else if( nid > 2 && astTestSize( this, id3 ) ) { + id = id3; + + } else { + id = id1; + } + } + +/* Return the result. */ + return astGetSize( this, id ); + +} + +static int GetUseStyle( AstPlot *this, int id, int *status ) { +/* +* Name: +* GetUseStyle + +* Purpose: +* Get the Style value to use for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int GetUseStyle( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This returns the Style value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the style +* for the first set specific value is returned. For example, if the +* Style for AST__AXES_ID is requested, then the style for AST__AXIS1_ID +* will be returned if set, and otherwise the style for AST__AXIS2_ID will +* be returned. If AST__AXIS2_ID is not set either, then the default for +* AST__AXIS2_ID will be returned. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Style value to use. + +*/ + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return NOSTYLE; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the value of the first set + genuine identifier. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + if( nid > 1 ) { + if( astTestStyle( this, id1 ) ) { + id = id1; + + } else if( nid > 1 && astTestStyle( this, id2 ) ) { + id = id2; + + } else if( nid > 2 && astTestStyle( this, id3 ) ) { + id = id3; + + } else { + id = id1; + } + } + +/* Return the result. */ + return astGetStyle( this, id ); + +} + +static double GetUseWidth( AstPlot *this, int id, int *status ) { +/* +* Name: +* GetUseWidth + +* Purpose: +* Get the Width value to use for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* double GetUseWidth( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This returns the Width value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the Width +* for the first set specific value is returned. For example, if the +* Width for AST__AXES_ID is requested, then the Width for AST__AXIS1_ID +* will be returned if set, and otherwise the Width for AST__AXIS2_ID will +* be returned. If AST__AXIS2_ID is not set either, then the default for +* AST__AXIS2_ID will be returned. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Width value to use. + +*/ + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return NOWIDTH; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the value of the first set + genuine identifier. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + if( nid > 1 ) { + if( astTestWidth( this, id1 ) ) { + id = id1; + + } else if( nid > 1 && astTestWidth( this, id2 ) ) { + id = id2; + + } else if( nid > 2 && astTestWidth( this, id3 ) ) { + id = id3; + + } else { + id = id1; + } + } + +/* Return the result. */ + return astGetWidth( this, id ); + +} + +static int TestUseColour( AstPlot *this, int id, int *status ) { +/* +* Name: +* TestUseColour + +* Purpose: +* Test the Colour value for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int TestUseColour( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This tests the Colour value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the element +* is considered to be set if all the corresponding specific values are +* set. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Colour value state (1 if set, zero otherwise). + +*/ + +/* Local Variables: */ + int ret; + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the logical AND of the + test flags for the genuine identifiers. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + ret = astTestColour( this, id1 ); + if( nid > 1 ) ret = ret && astTestColour( this, id2 ); + if( nid > 2 ) ret = ret && astTestColour( this, id3 ); + +/* Return the result. */ + return ret; + +} + +static int TestUseFont( AstPlot *this, int id, int *status ) { +/* +* Name: +* TestUseFont + +* Purpose: +* Test the Font value for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int TestUseFont( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This tests the Font value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the element +* is considered to be set if all the corresponding specific values are +* set. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Font value state (1 if set, zero otherwise). + +*/ + +/* Local Variables: */ + int ret; + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the logical AND of the + test flags for the genuine identifiers. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + ret = astTestFont( this, id1 ); + if( nid > 1 ) ret = ret && astTestFont( this, id2 ); + if( nid > 2 ) ret = ret && astTestFont( this, id3 ); + +/* Return the result. */ + return ret; + +} + +static int TestUseSize( AstPlot *this, int id, int *status ) { +/* +* Name: +* TestUseSize + +* Purpose: +* Test the Size value for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int TestUseSize( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This tests the Size value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the element +* is considered to be set if all the corresponding specific values are +* set. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Size value state (1 if set, zero otherwise). + +*/ + +/* Local Variables: */ + int ret; + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the logical AND of the + test flags for the genuine identifiers. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + ret = astTestSize( this, id1 ); + if( nid > 1 ) ret = ret && astTestSize( this, id2 ); + if( nid > 2 ) ret = ret && astTestSize( this, id3 ); + +/* Return the result. */ + return ret; + +} + +static int TestUseStyle( AstPlot *this, int id, int *status ) { +/* +* Name: +* TestUseStyle + +* Purpose: +* Test the Style value for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int TestUseStyle( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This tests the Style value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the element +* is considered to be set if all the corresponding specific values are +* set. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Style value state (1 if set, zero otherwise). + +*/ + +/* Local Variables: */ + int ret; + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the logical AND of the + test flags for the genuine identifiers. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + ret = astTestStyle( this, id1 ); + if( nid > 1 ) ret = ret && astTestStyle( this, id2 ); + if( nid > 2 ) ret = ret && astTestStyle( this, id3 ); + +/* Return the result. */ + return ret; + +} + +static int TestUseWidth( AstPlot *this, int id, int *status ) { +/* +* Name: +* TestUseWidth + +* Purpose: +* Test the Width value for a specified graphical element. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int TestUseWidth( AstPlot *this, int id, int *status ) + +* Class Membership: +* Plot member function. + +* Description: +* This tests the Width value for the graphical element specified by +* id. If an element related to a generic value is being accessed (e.g +* "Axes" is generic, "Axis1" and "Axis2" are not), then the element +* is considered to be set if all the corresponding specific values are +* set. + +* Parameters: +* this +* Pointer to the Plot. +* id +* An integer specifying the graphical element to be drawn. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Width value state (1 if set, zero otherwise). + +*/ + +/* Local Variables: */ + int ret; + +/* Local Variables: */ + int id1; /* First genuine identifier */ + int id2; /* Second genuine identifier */ + int id3; /* Third genuine identifier */ + int nid; /* Number of genuine attributes */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* See if the supplied identifier is a psuedo-identifier representing two + or three other genuine identifiers. If so, return the logical AND of the + test flags for the genuine identifiers. */ + nid = IdFind( id, astGetNin( this ), &id1, &id2, &id3, status ); + ret = astTestWidth( this, id1 ); + if( nid > 1 ) ret = ret && astTestWidth( this, id2 ); + if( nid > 2 ) ret = ret && astTestWidth( this, id3 ); + +/* Return the result. */ + return ret; + +} + +static int ToggleLogLin( AstPlot *this, int axis, int islog, + const char *method, int *status ){ +/* +* +* Name: +* ToggleLogLin + +* Purpose: +* Toggle the nature of the Plot axis mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int ToggleLogLin( AstPlot *this, int axis, int islog, +* const char *method, int *status ) + +* Class Membership: +* Plot member function + +* Description: +* Each axis in the graphics Frame of a Plot can be mapped linearly or +* logarithmically onto the corresponding axis in the base Frame of +* the FrameSet supplied whtn the Plot was constructed. This function +* toggles the nature of the specified axis; if it is currently +* logarithmic it becomes linear, and if it is linear it becomes +* logarithmic. +* +* If the Frame canot be re-maped (for instance because the visible +* part of the axis includes the value zero), then zero is returned +* but no error is reported. + +* Parameters: +* this +* The Plot. +* axis +* Zero based axis index. +* islog +* Is the axis currently logarithmic? If so, this function remaps +* it so that it is linear (and vice-versa). +* method +* Pointer to a null-terminated string holding the name of the calling +* method (only used within error mesages). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the attempt to re-map the graphics Frame was succesful, +* zero otherwise. + +*/ + +/* Local Variables: */ + AstCmpMap *remap1; /* 1D Mapping to re-map the graphics Frame */ + AstCmpMap *remap2; /* 2D Mapping to re-map the graphics Frame */ + AstMathMap *logmap; /* 1D Logarithmic axis Mapping */ + AstUnitMap *unitmap; /* 1D Unit mapping */ + AstWinMap *linmap; /* 1D Linear axis Mapping */ + char fwdexp[ 25 + 2*DBL_DIG ]; /* Forward log mapping expression */ + char invexp[ 28 + 2*DBL_DIG ]; /* Inverse log mapping expression */ + const char *fwd[1]; /* Pointer to pass to MathMap constructor */ + const char *inv[1]; /* Pointer to pass to MathMap constructor */ + double a; /* Constant for log expression */ + double b1; /* Original base Frame axis value at first edge */ + double b2; /* Original base Frame axis value at second edge */ + double b; /* Constant for log expression */ + double c; /* Constant for log expression */ + double g1; /* Graphics axis value at first edge */ + double g2; /* Graphics axis value at second edge */ + int result; /* Returned value */ + +/* Inotialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the corresponding axis limits in the graphics coordinate system + and the original base Frame coordinate system. */ + if( axis == 0 ) { + if( this->xrev ) { + g1 = this->xhi; + g2 = this->xlo; + } else { + g1 = this->xlo; + g2 = this->xhi; + } + b1 = this->bbox[ 0 ]; + b2 = this->bbox[ 2 ]; + + } else { + if( this->yrev ) { + g1 = this->yhi; + g2 = this->ylo; + } else { + g1 = this->ylo; + g2 = this->yhi; + } + b1 = this->bbox[ 1 ]; + b2 = this->bbox[ 3 ]; + } + +/* Check the limits are usable (e.g. the base Frame values will be bad + if this Plot was restored from a dump of a Plot created before the + LogPlot attributes were added). */ + if( b1 != AST__BAD && b2 != AST__BAD && g1 != g2 && b1 != b2 && + b1*b2 > 0.0 ) { + +/* Form the 1D Mapping which maps the specified axis linearly onto the plotting + surface. The forward transformation goes from graphics to base Frame. */ + linmap = astWinMap( 1, &g1, &g2, &b1, &b2, "", status ); + +/* Form the 1D Mapping which maps the specified axis logarithmically onto the + plotting surface. The forward transformation goes from graphics to base + Frame. */ + c = log10( b1/b2 ); + a = ( g1 - g2 )/c; + + if( b1 > 0.0 ) { + b = ( g2*log10( b1 ) - g1*log10( b2 ) )/c; + (void) sprintf( invexp, "g=%.*g*log10(b)+%.*g", DBL_DIG, a, DBL_DIG, b ); + (void) sprintf( fwdexp, "b=pow(10,(g-%.*g)/%.*g)", DBL_DIG, b, DBL_DIG, a ); + + } else { + b = ( g2*log10( -b1 ) - g1*log10( -b2 ) )/c; + (void) sprintf( invexp, "g=%.*g*log10(-b)+%.*g", DBL_DIG, a, DBL_DIG, b ); + (void) sprintf( fwdexp, "b=-pow(10,(g-%.*g)/%.*g)", DBL_DIG, b, DBL_DIG, a ); + } + + fwd[ 0 ] = (const char *) fwdexp; + inv[ 0 ] = (const char *) invexp; + logmap = astMathMap( 1, 1, 1, fwd, 1, inv, "SimpFI=1,SimpIF=1", status ); + +/* If the axis was previously logarithmic, get the Mapping with which to remap + the graphics Frame so that it becomes linearly related to the base Frame + in the FrameSet supplied when the Plot was constructed. */ + if( islog ) { + astInvert( linmap ); + remap1 = astCmpMap( logmap, linmap, 1, "", status ); + +/* If the axis was previously linear, store the new value and get the Mapping + with which to remap the graphics Frame so that it becomes logarithmically + related to the base Frame in the FrameSet supplied when the Plot was + constructed. */ + } else { + astInvert( logmap ); + remap1 = astCmpMap( linmap, logmap, 1, "", status ); + } + +/* Add a 1D UnitMap to map the unaltered mapping. */ + unitmap = astUnitMap( 1, "", status ); + if( axis == 0 ) { + remap2 = astCmpMap( remap1, unitmap, 0, "", status ); + } else { + remap2 = astCmpMap( unitmap, remap1, 0, "", status ); + } + +/* Remap the base (graphics) Frame in the Plot. */ + astRemapFrame( this, AST__BASE, remap2 ); + +/* Free resources. */ + remap1 = astAnnul( remap1 ); + remap2 = astAnnul( remap2 ); + logmap = astAnnul( logmap ); + linmap = astAnnul( linmap ); + unitmap = astAnnul( unitmap ); + +/* Indicate success. */ + if( astOK ) result = 1; + + } + +/* Return the result. */ + return result; +} + +static int Ustrcmp( const char *a, const char *b, int *status ){ +/* +* Name: +* Ustrncmp + +* Purpose: +* A case blind version of strcmp. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Ustrcmp( const char *a, const char *b ) + +* Class Membership: +* Plot member function. + +* Description: +* Returns 0 if there are no differences between the two strings, and 1 +* otherwise. Comparisons are case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strcmp" does. +* - This function attempts to execute even if an error has occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Loop round each character. */ + while( 1 ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + +static int Ustrncmp( const char *a, const char *b, size_t n, int *status ){ +/* +* Name: +* Ustrncmp + +* Purpose: +* A case blind version of strncmp. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* int Ustrncmp( const char *a, const char *b, size_t n ) + +* Class Membership: +* Plot member function. + +* Description: +* Returns 0 if there are no differences between the first "n" +* characters of the two strings, and 1 otherwise. Comparisons are +* case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. +* n +* The maximum number of characters to compare. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference +* between the two strings, whereas "strncmp" does. +* - This function attempts to execute even if an error has +* occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int i; /* Character index */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Compare up to "n" characters. */ + for( i = 0; i < (int) n; i++ ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Plot objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Plot objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPlot *this; /* Pointer to Plot */ + int i; + +/* Obtain a pointer to the Plot structure. */ + this = (AstPlot *) obj; + +/* Free the clipping bounds arrays. */ + this->clip_lbnd = (double *) astFree( (void *) this->clip_lbnd ); + this->clip_ubnd = (double *) astFree( (void *) this->clip_ubnd ); + +/* Free the Grf function stack */ + this->grfstack = (AstGrfPtrs *) astFree( (void *) this->grfstack ); + +/* Free the graphics attribute stack. */ + for( i = this->ngat - 1; i >= 0; i-- ) { + this->gat[ i ] = astFree( this->gat[ i ] ); + } + +/* Free the graphics context pointer. */ + if( this->grfcontext ) { + this->grfcontext = astAnnul( this->grfcontext ); + this->grfcontextID = astAnnulId( this->grfcontextID ); + } + +/* Free the information about the tick marks to draw. */ + for( i = 0; i < 3; i++ ) { + this->majtickval[ i ] = astFree( this->majtickval[ i ] ); + this->mintickval[ i ] = astFree( this->mintickval[ i ] ); + this->nmajtickval[ i ] = 0; + this->nmintickval[ i ] = 0; + } + +/* Free the information about the drawn tick marks. */ + SaveTick( this, -1, 0.0, 0.0, 0, status ); +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Plot objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Plot objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstPlot *in; /* Pointer to input Plot */ + AstPlot *out; /* Pointer to output Plot */ + int axis; /* Zero based axis index */ + int n; /* Number of ticks saved */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Plots. */ + in = (AstPlot *) objin; + out = (AstPlot *) objout; + +/* For safety, first clear any references to the input memory from + the output Plot. */ + out->clip_lbnd = NULL; + out->clip_ubnd = NULL; + out->gat = NULL; + out->ngat = 0; + + for( axis = 0; axis < 3; axis++ ) { + out->majtickgx[ axis ] = NULL; + out->majtickgy[ axis ] = NULL; + out->majtickcount[ axis ] = 0; + out->mintickgx[ axis ] = NULL; + out->mintickgy[ axis ] = NULL; + out->mintickcount[ axis ] = 0; + out->majtickval[ axis ] = NULL; + out->nmajtickval[ axis ] = 0; + out->mintickval[ axis ] = NULL; + out->nmintickval[ axis ] = 0; + } + +/* Copy the clipping bounds arrays. */ + out->clip_lbnd = (double *) astStore( NULL, (void *) in->clip_lbnd, + sizeof(double)*(size_t)(in->clip_axes) ); + out->clip_ubnd = (double *) astStore( NULL, (void *) in->clip_ubnd, + sizeof(double)*(size_t)(in->clip_axes) ); + +/* Copy the Grf function stack */ + out->grfstack = (AstGrfPtrs *) astStore( NULL, (void *) in->grfstack, + sizeof(AstGrfPtrs)*(size_t)(in->grfnstack )); + +/* Copy the information about drawn tick marks. */ + for( axis = 0; axis < 3; axis++ ) { + n = in->majtickcount[ axis ]; + out->majtickgx[ axis ] = (double *) astStore( NULL, in->majtickgx[ axis ], + n*sizeof( double ) ); + out->majtickgy[ axis ] = (double *) astStore( NULL, in->majtickgy[ axis ], + n*sizeof( double ) ); + out->majtickcount[ axis ] = n; + + n = in->mintickcount[ axis ]; + out->mintickgx[ axis ] = (double *) astStore( NULL, in->mintickgx[ axis ], + n*sizeof( double ) ); + out->mintickgy[ axis ] = (double *) astStore( NULL, in->mintickgy[ axis ], + n*sizeof( double ) ); + out->mintickcount[ axis ] = n; + + n = in->nmajtickval[ axis ]; + out->majtickval[ axis ] = (double *) astStore( NULL, in->majtickval[ axis ], + n*sizeof( double ) ); + out->nmajtickval[ axis ] = n; + + n = in->nmintickval[ axis ]; + out->mintickval[ axis ] = (double *) astStore( NULL, in->mintickval[ axis ], + n*sizeof( double ) ); + out->nmintickval[ axis ] = n; + } + +/* If an error occurred, free any allocated memory. */ + if ( !astOK ) { + out->clip_lbnd = (double *) astFree( out->clip_lbnd ); + out->clip_ubnd = (double *) astFree( out->clip_ubnd ); + out->grfstack = (AstGrfPtrs *) astFree( out->grfstack ); + SaveTick( out, -1, 0.0, 0.0, 0, status ); + } +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Plot objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Plot class to an output Channel. + +* Parameters: +* this +* Pointer to the Plot whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstPlot *this; /* Pointer to the Plot structure */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char *comment; /* Pointer to comment string */ + double dval; /* Double precision value */ + int ax; /* Axis to which element refers */ + int axis; /* Zero based axis index */ + int id; /* Zero based graphical object id */ + int ival; /* Integer value */ + int itick; /* Tick mark index */ + int nax; /* Number of base Frame axes */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot structure. */ + this = (AstPlot *) this_object; + +/* Get the number of graphics (base) frame axes - 2 for a Plot, 3 for a + Plot3D. */ + nax = astGetNin( this ); + +/* Write out values representing the instance variables for the + Plot class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Tol. */ +/* ---- */ + set = TestTol( this, status ); + dval = set ? GetTol( this, status ) : astGetTol( this ); + astWriteDouble( channel, "Tol", set, 0, dval, "Plotting tolerance" ); + +/* Grid. */ +/* ----- */ + set = TestGrid( this, status ); + ival = set ? GetGrid( this, status ) : astGetGrid( this ); + astWriteInt( channel, "Grid", set, 0, ival, "Is a grid required?" ); + +/* TickAll. */ +/* -------- */ + set = TestTickAll( this, status ); + ival = set ? GetTickAll( this, status ) : astGetTickAll( this ); + astWriteInt( channel, "TckAll", set, 1, ival, "Put ticks on all edges?" ); + +/* ForceExterior. */ +/* -------------- */ + set = TestForceExterior( this, status ); + ival = set ? GetForceExterior( this, status ) : astGetForceExterior( this ); + astWriteInt( channel, "FrcExt", set, 1, ival, "Force exterior labelling?" ); + +/* Invisible. */ +/* ---------- */ + set = TestInvisible( this, status ); + ival = set ? GetInvisible( this, status ) : astGetInvisible( this ); + astWriteInt( channel, "Invsbl", set, 1, ival, "Use invisible ink?" ); + +/* Border. */ +/* ------- */ + set = TestBorder( this, status ); + ival = set ? GetBorder( this, status ) : astGetBorder( this ); + astWriteInt( channel, "Border", set, 0, ival, "Draw a border round the grid?" ); + +/* ClipOp. */ +/* ------- */ + set = TestClipOp( this, status ); + ival = set ? GetClipOp( this, status ) : astGetClipOp( this ); + astWriteInt( channel, "ClpOp", set, 0, ival, "Clip using logical OR?" ); + +/* Clip. */ +/* ----- */ + set = TestClip( this, status ); + ival = set ? GetClip( this, status ) : astGetClip( this ); + astWriteInt( channel, "Clip", set, 0, ival, + ((ival == 0)?"Do not clip at plot edges": + ((ival == 1)?"Clip curves at plot edges": + ((ival == 2)?"Clip markers at plot edges": + "Clip markers and curves at plot edges")))); + +/* DrawTitle. */ +/* --------- */ + set = TestDrawTitle( this, status ); + ival = set ? GetDrawTitle( this, status ) : astGetDrawTitle( this ); + astWriteInt( channel, "DrwTtl", set, 1, ival, "Add a title to the grid?" ); + +/* DrawAxesUnits(axis). */ +/* ----------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestDrawAxes( this, axis, status ); + ival = set ? GetDrawAxes( this, axis, status ) : astGetDrawAxes( this, axis ); + (void) sprintf( buff, "DrwAxs%d", axis + 1 ); + astWriteInt( channel, buff, set, 0, ival, "Draw axis through ticks?" ); + } + +/* Abbrev(axis). */ +/* ------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestAbbrev( this, axis, status ); + ival = set ? GetAbbrev( this, axis, status ) : astGetAbbrev( this, axis ); + (void) sprintf( buff, "Abbrv%d", axis + 1 ); + astWriteInt( channel, buff, set, 0, ival, "Abbreviate numerical axis labels?" ); + } + +/* Escape. */ +/* ------- */ + set = TestEscape( this, status ); + ival = set ? GetEscape( this, status ) : astGetEscape( this ); + astWriteInt( channel, "Escape", set, 1, ival, "Interpret escape sequences?" ); + +/* LabelAt(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestLabelAt( this, axis, status ); + dval = set ? GetLabelAt( this, axis, status ) : astGetLabelAt( this, axis ); + if( dval != AST__BAD ){ + (void) sprintf( buff, "LblAt%d", axis + 1 ); + astWriteDouble( channel, buff, set, 0, dval, "Put numerical labels at" ); + } + } + +/* Centre(axis). */ +/* ------------ */ + for( axis = 0; axis < nax; axis++ ){ + set = TestCentre( this, axis, status ); + dval = set ? GetCentre( this, axis, status ) : astGetCentre( this, axis ); + if( dval != AST__BAD ){ + (void) sprintf( buff, "Cen%d", axis + 1 ); + astWriteDouble( channel, buff, set, 0, dval, "Tick mark origin" ); + } + } + +/* Gap(axis). */ +/* ---------- */ +/* Discovering the default value requires a lot of calculation. Only + write out this attribute if an explicit value has been set. */ + for( axis = 0; axis < nax; axis++ ){ + if( astTestGap( this, axis ) ) { + dval = astGetGap( this, axis ); + if( dval != AST__BAD ){ + (void) sprintf( buff, "Gap%d", axis + 1 ); + astWriteDouble( channel, buff, set, 0, dval, "Difference between ticks" ); + } + } + } + +/* LogGap(axis). */ +/* ------------- */ +/* Discovering the default value requires a lot of calculation. Only + write out this attribute if an explicit value has been set. */ + for( axis = 0; axis < nax; axis++ ){ + if( astTestLogGap( this, axis ) ) { + dval = astGetLogGap( this, axis ); + if( dval != AST__BAD ){ + (void) sprintf( buff, "LgGap%d", axis + 1 ); + astWriteDouble( channel, buff, set, 0, dval, "Ratio between ticks" ); + } + } + } + +/* NumLabGap(axis). */ +/* ---------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestNumLabGap( this, axis, status ); + dval = set ? GetNumLabGap( this, axis, status ) : astGetNumLabGap( this, axis ); + if( dval != AST__BAD ) { + (void) sprintf( buff, "NmGap%d", axis + 1 ); + astWriteDouble( channel, buff, set, 1, dval, "Spacing of numerical labels" ); + } + } + +/* TextLabGap(axis). */ +/* ----------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestTextLabGap( this, axis, status ); + dval = set ? GetTextLabGap( this, axis, status ) : astGetTextLabGap( this, axis ); + if( dval != AST__BAD ) { + (void) sprintf( buff, "TxGap%d", axis + 1 ); + astWriteDouble( channel, buff, set, 1, dval, "Spacing of descriptive labels" ); + } + } + +/* LabelUp(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestLabelUp( this, axis, status ); + ival = set ? GetLabelUp( this, axis, status ) : astGetLabelUp( this, axis ); + (void) sprintf( buff, "LblUp%d", axis + 1 ); + astWriteInt( channel, buff, set, 1, ival, "Draw numerical labels upright?" ); + } + +/* LogPlot(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestLogPlot( this, axis, status ); + ival = set ? GetLogPlot( this, axis, status ) : astGetLogPlot( this, axis ); + (void) sprintf( buff, "LgPlt%d", axis + 1 ); + astWriteInt( channel, buff, set, 1, ival, "Map plot axis logarithmically?" ); + } + +/* LogTicks(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestLogTicks( this, axis, status ); + ival = set ? GetLogTicks( this, axis, status ) : astGetLogTicks( this, axis ); + (void) sprintf( buff, "LgTck%d", axis + 1 ); + astWriteInt( channel, buff, set, 1, ival, "Space ticks logarithmically?" ); + } + +/* LogLabel(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestLogLabel( this, axis, status ); + ival = set ? GetLogLabel( this, axis, status ) : astGetLogLabel( this, axis ); + (void) sprintf( buff, "LgLbl%d", axis + 1 ); + astWriteInt( channel, buff, set, 1, ival, "Scientific notation for labels?" ); + } + +/* NumLab(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestNumLab( this, axis, status ); + ival = set ? GetNumLab( this, axis, status ) : astGetNumLab( this, axis ); + (void) sprintf( buff, "NmLbl%d", axis + 1 ); + astWriteInt( channel, buff, set, 1, ival, "Draw numerical labels?" ); + } + +/* MinTick(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestMinTick( this, axis, status ); + ival = set ? GetMinTick( this, axis, status ) : astGetMinTick( this, axis ); + (void) sprintf( buff, "MnTks%d", axis + 1 ); + astWriteInt( channel, buff, set, 0, ival, "No. of sub-divisions " + "between major tick marks" ); + } + +/* TextLab(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestTextLab( this, axis, status ); + ival = set ? GetTextLab( this, axis, status ) : astGetTextLab( this, axis ); + (void) sprintf( buff, "TxLbl%d", axis + 1 ); + astWriteInt( channel, buff, set, 0, ival, "Draw textual label?" ); + } + +/* LabelUnits(axis). */ +/* ----------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestLabelUnits( this, axis, status ); + ival = set ? GetLabelUnits( this, axis, status ) : astGetLabelUnits( this, axis ); + (void) sprintf( buff, "LbUnt%d", axis + 1 ); + astWriteInt( channel, buff, set, 0, ival, "Add units to axis label?" ); + } + +/* Style(object). */ +/* -------------- */ + for( id = 0; id < AST__NPID; id++ ){ + set = TestStyle( this, id, status ); + ival = set ? GetStyle( this, id, status ) : astGetStyle( this, id ); + (void) sprintf( buff, "Style%d", id + 1 ); + comment = GrfItem( id, " line style", &ax, status ); + if( ax < nax ) astWriteInt( channel, buff, set, 0, ival, comment ); + comment = (char *) astFree( (void *) comment ); + } + +/* Font(object). */ +/* ------------- */ + for( id = 0; id < AST__NPID; id++ ){ + set = TestFont( this, id, status ); + ival = set ? GetFont( this, id, status ) : astGetFont( this, id ); + (void) sprintf( buff, "Font%d", id + 1 ); + comment = GrfItem( id, " character font", &ax, status ); + if( ax < nax ) astWriteInt( channel, buff, set, 0, ival, comment ); + comment = (char *) astFree( (void *) comment ); + } + +/* Colour(object). */ +/* --------------- */ + for( id = 0; id < AST__NPID; id++ ){ + set = TestColour( this, id, status ); + ival = set ? GetColour( this, id, status ) : astGetColour( this, id ); + (void) sprintf( buff, "Col%d", id + 1 ); + comment = GrfItem( id, " colour index", &ax, status ); + if( ax < nax ) astWriteInt( channel, buff, set, 0, ival, comment ); + comment = (char *) astFree( (void *) comment ); + } + +/* Width(object). */ +/* -------------- */ + for( id = 0; id < AST__NPID; id++ ){ + set = TestWidth( this, id, status ); + dval = set ? GetWidth( this, id, status ) : astGetWidth( this, id ); + if( dval != AST__BAD ) { + (void) sprintf( buff, "Width%d", id + 1 ); + comment = GrfItem( id, " line width", &ax, status ); + if( ax < nax ) astWriteDouble( channel, buff, set, 0, dval, comment ); + comment = (char *) astFree( (void *) comment ); + } + } + +/* Size(object). */ +/* ------------- */ + for( id = 0; id < AST__NPID; id++ ){ + set = TestSize( this, id, status ); + dval = set ? GetSize( this, id, status ) : astGetSize( this, id ); + if( dval != AST__BAD ) { + (void) sprintf( buff, "Size%d", id + 1 ); + comment = GrfItem( id, " character size", &ax, status ); + if( ax < nax ) astWriteDouble( channel, buff, set, 0, dval, comment ); + comment = (char *) astFree( (void *) comment ); + } + } + +/* TitleGap. */ +/* --------- */ + set = TestTitleGap( this, status ); + dval = set ? GetTitleGap( this, status ) : astGetTitleGap( this ); + if( dval != AST__BAD ) astWriteDouble( channel, "TtlGap", set, 1, dval, + "Gap between title and edge" ); + +/* MajTickLen(axis). */ +/* ----------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestMajTickLen( this, axis, status ); + dval = set ? GetMajTickLen( this, axis, status ) : astGetMajTickLen( this, axis ); + if( dval != AST__BAD ) { + (void) sprintf( buff, "MjTkLn%d", axis + 1 ); + astWriteDouble( channel, buff, set, 0, dval, "Major tick length" ); + } + } + +/* MinTickLen(axis). */ +/* ----------------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestMinTickLen( this, axis, status ); + dval = set ? GetMinTickLen( this, axis, status ) : astGetMinTickLen( this, axis ); + if( dval != AST__BAD ) { + (void) sprintf( buff, "MnTkLn%d", axis + 1 ); + astWriteDouble( channel, buff, set, 1, dval, "Minor tick length" ); + } + } + +/* Labelling. */ +/* ---------- */ + set = TestLabelling( this, status ); + ival = set ? GetLabelling( this, status ) : astGetLabelling( this ); + comment = "Labelling scheme"; + astWriteString( channel, "Lbling", set, 0, xlbling[ival], comment ); + +/* Edge(axis). */ +/* ----------- */ + for( axis = 0; axis < nax; axis++ ){ + set = TestEdge( this, axis, status ); + ival = set ? GetEdge( this, axis, status ) : astGetEdge( this, axis ); + (void) sprintf( buff, "Edge%d", axis + 1 ); + comment = "Edge used to label an axis"; + astWriteString( channel, buff, set, 0, xedge[ival], comment ); + } + +/* Now do instance variables which are not attributes. */ +/* =================================================== */ + +/* Only write out clipping information if set. */ + if( this->clip_lbnd && this->clip_ubnd ){ + +/* The lower bounds of the clipping volume. */ + for( axis = 0; axis < this->clip_axes; axis++ ){ + (void) sprintf( buff, "ClpLb%d", axis + 1 ); + if( this->clip_lbnd && (this->clip_lbnd)[ axis ] != AST__BAD ){ + astWriteDouble( channel, buff, 1, 0, (this->clip_lbnd)[ axis ], + "Lower bound of clipping region" ); + } + } + +/* The upper bounds of the clipping volume. */ + for( axis = 0; axis < this->clip_axes; axis++ ){ + (void) sprintf( buff, "ClpUb%d", axis + 1 ); + if( this->clip_ubnd && (this->clip_ubnd)[ axis ] != AST__BAD ){ + astWriteDouble( channel, buff, 1, 0, (this->clip_ubnd)[ axis ], + "Upper bound of clipping region" ); + } + } + +/* The number of bounds supplied for the clipping volume. */ + astWriteInt( channel, "ClpAxs", 1, 0, this->clip_axes, + "No. of bounds for clipping region" ); + +/* The index of the clipping Frame within the Plot. */ + astWriteInt( channel, "ClpFrm", 1, 0, this->clip_frame, + "Index of clipping Frame" ); + } + +/* The bounds of the plotting area in graphics coords. */ + astWriteDouble( channel, "Xlo", 1, 1, this->xlo, + "Lower X bound of plotting area" ); + astWriteDouble( channel, "Ylo", 1, 1, this->ylo, + "Lower Y bound of plotting area" ); + astWriteDouble( channel, "Xhi", 1, 1, this->xhi, + "Upper X bound of plotting area" ); + astWriteDouble( channel, "Yhi", 1, 1, this->yhi, + "Upper Y bound of plotting area" ); + +/* Axis reversal flags. */ + astWriteInt( channel, "Xrev", 1, 0, this->xrev, "X axis reversed?" ); + astWriteInt( channel, "Yrev", 1, 0, this->yrev, "Y axis reversed?" ); + +/* The bounds of the plotting area in the base Frame of the FrameSet + supplied when the Plot was constructed. */ + astWriteDouble( channel, "Xb1", 1, 1, this->bbox[ 0 ], + "Lower X bound of supplied base Frame" ); + astWriteDouble( channel, "Yb1", 1, 1, this->bbox[ 1 ], + "Lower Y bound of supplied base Frame" ); + astWriteDouble( channel, "Xb2", 1, 1, this->bbox[ 2 ], + "Upper X bound of supplied base Frame" ); + astWriteDouble( channel, "Yb2", 1, 1, this->bbox[ 3 ], + "Upper Y bound of supplied base Frame" ); + +/* User-specified tick values */ + for( axis = 0; axis < 3; axis++ ) { + + if( this->nmajtickval[ axis ] > 0 ) { + sprintf( buff, "NMjTk%d", axis + 1 ); + astWriteInt( channel, buff, 1, 1, this->nmajtickval[ axis ], "" ); + + for( itick = 0; itick < this->nmajtickval[ axis ]; itick++ ) { + sprintf( buff, "MjTk%d_%d", axis + 1, itick + 1 ); + astWriteDouble( channel, buff, 1, 1, + this->majtickval[ axis ][ itick ], "" ); + } + } + + if( this->nmintickval[ axis ] > 0 ) { + sprintf( buff, "NMnTk%d", axis + 1 ); + astWriteInt( channel, buff, 1, 1, this->nmintickval[ axis ], "" ); + + for( itick = 0; itick < this->nmintickval[ axis ]; itick++ ) { + sprintf( buff, "MnTk%d_%d", axis + 1, itick + 1 ); + astWriteDouble( channel, buff, 1, 1, + this->mintickval[ axis ][ itick ], "" ); + } + } + } + +/* Return. */ + return; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPlot and astCheckPlot functions using + the macros defined for this purpose in the "object.h" header + file. */ +astMAKE_ISA(Plot,FrameSet) +astMAKE_CHECK(Plot) + +AstPlot *astPlot_( void *frame_void, const float *graphbox, + const double *basebox, const char *options, int *status, ...) { +/* +*+ +* Name: +* astPlot + +* Purpose: +* Create a Plot. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot.h" +* AstPlot *astPlot( AstFrame *frame, const float *graphbox, +* const double *basebox, const char *options, ..., int *status ) + +* Class Membership: +* Plot constructor. + +* Description: +* This function creates a new Plot and optionally initialises +* its attributes. +* +* The supplied Frame (or the base frame if a FrameSet was supplied) is +* assumed to be related to the graphics world coordinate system by a +* simple shift and scale along each axis. The mapping between graphics +* world coordinates and this Frame is specified by supplying the +* coordinates in both systems at the bottom left and top right corners +* of a box on the graphics device. By default, no graphics will be +* produced outside the supplied box, but this default behaviour can be +* changed by setting explicit values for the various clipping attributes. + +* Parameters: +* frame +* A pointer to a Frame or FrameSet to be annotated. If a NULL pointer +* is supplied, then a default 2-D Frame will be created to which labels, +* etc, can be attached by setting the relevant Frame attributes. +* graphbox +* A pointer to an array of 4 values giving the graphics world +* coordinates of the bottom left and top right corners of a box on +* the graphics output device. The first pair of values should be the +* coordinates of the bottom left corner of the box and the second +* pair of values should be the coordinates of the top right corner. +* The horizontal axis should be given first in each pair. +* basebox +* A pointer to an array of 4 values giving the coordinates in the +* supplied Frame, or base frame of the supplied FrameSet, at the +* bottom left and top right corners of the box specified by parameter +* graphbox. These should be supplied in the same order as for +* parameter "graphbox". +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new Plot. The syntax used is the same as +* for the astSet method and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of arguments may follow it in order to +* supply values to be substituted for these specifiers. The +* rules for supplying these are identical to those for the +* astSet method (and for the C "printf" function). + +* Returned Value: +* A pointer to the new Plot. + +* Notes: +* - The base Frame of the created Plot corresponds to the graphics world +* coordinate system, and should not, in general, be changed. +* - The current Frame of the created Plot corresponds to the Frame +* given by parameter "frame". If a FrameSet was supplied then its +* current Frame becomes the current Frame of the created Plot. +* - If the supplied Frame, or base Frame if a FrameSet was supplied, +* has more than 2 axes, then the sub-Frame defined by the first 2 axes +* is used. +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic Plot constructor which +* is available via the protected interface to the Plot class. +* A public interface is provided by the astPlotId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "frame" parameter is of type (void *) and is converted and +* validated within the function itself. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPlot *new; /* Pointer to new Plot */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + new = NULL; + +/* Obtain and validate a pointer to any supplied Frame structure. */ + if( frame_void ){ + frame = astCheckFrame( frame_void ); + } else { + frame = NULL; + } + +/* Check the pointer can be used. */ + if ( astOK ) { + +/* Initialise the Plot, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPlot( NULL, sizeof( AstPlot ), !class_init, + &class_vtab, "Plot", frame, graphbox, + basebox ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + Plot's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new Plot. */ + return new; +} + +AstPlot *astInitPlot_( void *mem, size_t size, int init, AstPlotVtab *vtab, + const char *name, AstFrame *frame, const float *graphbox, + const double *basebox, int *status ) { +/* +*+ +* Name: +* astInitPlot + +* Purpose: +* Initialise a Plot. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot.h" +* AstPlot *astInitPlot( void *mem, size_t size, int init, +* AstPlotVtab *vtab, const char *name, +* AstFrame *frame, const float *graphbox, +* const double *basebox ) + +* Class Membership: +* Plot initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Plot object. It allocates memory (if necessary) to accommodate +* the Plot plus any additional data associated with the derived class. +* It then initialises a Plot structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Plot at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Plot is to be created. This +* must be of sufficient size to accommodate the Plot data +* (sizeof(Plot)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Plot (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Plot +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Plot's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Plot. If NULL, the vtab associated with this class +* (Plot) will be used. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame or Frameset to be annotated. +* graphbox +* A pointer to an array of 4 values giving the graphics coordinates +* of the bottom left and top right corners of a box on the graphics +* output device. The first pair of values should be the graphics +* coordinates of the bottom left corner of the box and the second +* pair of values are the graphics coordinates of the top right corner. +* The horizontal axis should be given first in each pair. +* basebox +* A pointer to an array of 4 values giving the coordinates in the +* supplied Frame or base Frame of the supplied FrameSet at the bottom +* left and top right corners of the box specified by parameter graphbox. +* These should be supplied in the same order as for parameter "graphbox". + +* Returned Value: +* A pointer to the new Plot. + +* Notes: +* - If the supplied Frame, or base Frame if a FrameSet was supplied, +* has more than 2 axes, then the sub-Frame defined by the first 2 axes +* is used. +* - The current Frame of the supplied FrameSet need not be 2-dimensional. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *baseframe; /* Pointer to base frame */ + AstFrame *graphicsframe; /* Pointer to graphics frame */ + AstFrameSet *fset0; /* The n-D FrameSet to be annotated */ + AstFrameSet *fset; /* The 2-D FrameSet to be annotated */ + AstPlot *new; /* Pointer to new Plot */ + AstWinMap *map; /* Mapping for converting bbox -> gbox */ + char *mess; /* Pointer to a descriptive message */ + double gbox[ 4 ]; /* Double precision version of "graphbox" */ + int axis; /* Axis index, 0 or 1 */ + int bi; /* Index of base frame */ + int ci; /* Index of current frame */ + int i; /* Loop count */ + int id; /* Plot object id */ + int naxes; /* No. of axes in frame */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(frame); + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + fset = NULL; + mess = NULL; + +/* If no vtab was supplied, use the vtab for this class (Plot). */ + if( !vtab ) { + vtab = &class_vtab; + if ( !class_init ) { + astInitPlotVtab( vtab, "Plot" ); + class_init = 1; + } + +/* If necessary, initialise the virtual function table. */ + } else if ( init ) { + astInitPlotVtab( vtab, name ); + } + +/* Initialise. */ + new = NULL; + baseframe = NULL; + +/* First of all we need to ensure that we have a FrameSet and a base + Frame on which to base the new Plot. If a NULL Frame pointer was + supplied, create a default 2-D Frame, and then create a FrameSet + containing just this default Frame. Also store a pointer to a + message which can be used to describe the object within error + messages. */ + if( !frame ){ + baseframe = astFrame( 2, "", status ); + fset = astFrameSet( baseframe, "", status ); + mess = "default 2-d Frame"; + +/* If an object was supplied, report an error if it is not a Frame or + an object derived from a Frame (such as a FrameSet). */ + } else if( !astIsAFrame( frame ) ){ + if( astOK ){ + astError( AST__BDOBJ, "astInitPlot(%s): Supplied Object (class '%s') " + "is not a Frame.", status, name, astGetClass( frame ) ); + } + +/* If the supplied object is a Plot or an object derived from a Plot (a Plot + is a sort of Frame and so will pass the above test), extract a + FrameSet from the Plot, and clear the Domain attribute for any existing + Frames which have Domain GRAPHICS. */ + } else if( astIsAPlot( frame ) ){ + fset0 = astFrameSet( frame, "", status ); + fset = astCopy( fset0 ); + fset0 = astAnnul( fset0 ); + + for( i = 0; i < astGetNframe( fset ); i++ ) { + graphicsframe = astGetFrame( fset, i ); + if( !strcmp( astGetDomain( graphicsframe ), "GRAPHICS" ) ) { + astClearDomain( graphicsframe ); + } + graphicsframe = astAnnul( graphicsframe ); + } + + baseframe = astGetFrame( fset, astGetBase( fset ) ); + mess = "base Frame of the supplied Plot"; + +/* If the object is not a FrameSet, create a FrameSet holding the + supplied Frame. If the Frame is not 2D, an extra 2D Frame is + included in the FrameSet derived from axes 1 and 2 of the supplied + Frame. This new Frame becomes the base Frame. */ + } else if( !astIsAFrameSet( frame ) ){ + fset0 = astFrameSet( frame, "", status ); + mess = "supplied Frame"; + fset = Fset2D( fset0, AST__BASE, status ); + fset0 = astAnnul( fset0 ); + baseframe = astGetFrame( fset, astGetBase( fset ) ); + +/* If a FrameSet was supplied, ensure it has a 2D base Frame. + If the supplied FrameSet is not 2D, then a new base Frame is + inserted into it which is derived from axes 1 and 2 of the + original base Frame. */ + } else { + fset = Fset2D( (AstFrameSet *) frame, AST__BASE, status ); + baseframe = astGetFrame( fset, astGetBase( fset ) ); + mess = "base Frame of the supplied FrameSet"; + } + +/* Check that there are 2 axes in the base frame of the FrameSet. */ + naxes = astGetNaxes( baseframe ); + if ( naxes != 2 && astOK ) { + astError( AST__NAXIN, "astInitPlot(%s): Number of axes (%d) in the %s " + "is invalid - this number should be 2.", status, name, naxes, mess ); + } + +/* Check that neither dimension of the graphbox is zero. */ + if( ( graphbox[ 2 ] == graphbox[ 0 ] || + graphbox[ 3 ] == graphbox[ 1 ] ) && astOK ){ + astError( AST__BADBX, "astInitPlot(%s): The plotting area has zero size " + "in the graphics world coordinate system.", status, name ); + } + +/* Check that neither dimension of the graphbox is bad. */ + if( astISBAD(graphbox[0]) || astISBAD(graphbox[1]) || + astISBAD(graphbox[2]) || astISBAD(graphbox[3]) ) { + astError( AST__BADBX, "astInitPlot(%s): The plotting area has undefined limits " + "in the graphics world coordinate system.", status, name ); + } + +/* Check that neither dimension of the basebox is zero. */ + if( astISBAD(basebox[2]) || astISBAD(basebox[0]) ) { + astError( AST__BADBX, "astInitPlot(%s): The limits of the horizontal " + "axis of the %s are undefined or bad.", status, name, name ); + } else if( astISBAD(basebox[3]) || astISBAD(basebox[1]) ) { + astError( AST__BADBX, "astInitPlot(%s): The limits of the vertical " + "axis of the %s are undefined or bad.", status, name, name ); + } + +/* Create a Frame which describes the graphics world coordinate system. */ + graphicsframe = astFrame( 2, + "Domain=GRAPHICS,Title=Graphical Coordinates", status ); + +/* Initialise a FrameSet structure (the parent class) as the first + component within the Plot structure, allocating memory if necessary. + The new FrameSet is initialised to hold the graphics Frame created + above. */ + new = (AstPlot *) astInitFrameSet( mem, size, 0, (AstFrameSetVtab *) vtab, + name, graphicsframe ); + + if ( astOK ) { + +/* Initialise the Plot data. */ +/* ----------------------------- */ + +/* Get a double precision version of "graphbox". */ + gbox[ 0 ] = (double) graphbox[ 0 ]; + gbox[ 1 ] = (double) graphbox[ 1 ]; + gbox[ 2 ] = (double) graphbox[ 2 ]; + gbox[ 3 ] = (double) graphbox[ 3 ]; + +/* Store the bounds in graphics coordinates of the clipping box, ensuring + that the low bound is lower than the high bound. Set flags to indicate + if the supplied bounds has to be reversed to do this (some graphics + system have the Y axis increasing from the top of the screen to the + bottom). */ + if( graphbox[ 0 ] <= graphbox[ 2 ] ){ + new->xlo = gbox[ 0 ]; + new->xhi = gbox[ 2 ]; + new->xrev = 0; + } else { + new->xhi = gbox[ 0 ]; + new->xlo = gbox[ 2 ]; + new->xrev = 1; + astSetDirection( graphicsframe, 0, 0 ); + } + if( graphbox[ 1 ] <= graphbox[ 3 ] ){ + new->ylo = gbox[ 1 ]; + new->yhi = gbox[ 3 ]; + new->yrev = 0; + } else { + new->yhi = gbox[ 1 ]; + new->ylo = gbox[ 3 ]; + new->yrev = 1; + astSetDirection( graphicsframe, 1, 0 ); + } + +/* Store the bounds of the Plot within the base Frame of the supplied + FrameSet. */ + new->bbox[ 0 ] = basebox[ 0 ]; + new->bbox[ 1 ] = basebox[ 1 ]; + new->bbox[ 2 ] = basebox[ 2 ]; + new->bbox[ 3 ] = basebox[ 3 ]; + +/* We initially assume that the base Frame of the supplied FrameSet is + mapped lineary onto the graphics frame. This may be changed later by + assigning values to the LogPlot attributes. Create a WinMap which + maps the base box (within the base Frame of the supplied FrameSet) + onto the graphics box. */ + map = astWinMap( 2, gbox, gbox + 2, basebox, basebox + 2, "", status ); + +/* Get the index of the current (physical) and base (pixel) Frames in + the supplied FrameSet. */ + bi = astGetBase( fset ); + ci = astGetCurrent( fset ); + +/* Temporarily set the current Frame to be the pixel frame. */ + astSetCurrent( fset, bi ); + +/* Add the supplied FrameSet into the Plot (i.e. FrameSet) created + earlier. This leaves the graphics frame with index 1 in the + returned Plot. We use the linear mapping initially. */ + astAddFrame( (AstFrameSet *) new, 1, map, fset ); + map = astAnnul( map ); + +/* Set the current Frame in the Plot to be the physical coordinate Frame + (with index incremented by one because the graphics Frame has been added). */ + astSetCurrent( (AstFrameSet *) new, ci + 1 ); + +/* Re-establish the original current Frame in the supplied FrameSet. */ + astSetCurrent( fset, ci ); + +/* Store a value of -1.0 for Tol to indicate that no value has yet been + set. This will cause a default value of 0.001 to be used. */ + new->tol = -1.0; + +/* Set up default clipping information which gives no clipping. */ + new->clip_frame = AST__NOFRAME; + new->clip_lbnd = NULL; + new->clip_ubnd = NULL; + new->clip_axes = 0; + +/* Is a grid covering the plotting area required? Store a value of -1 + to indicate that no value has yet been set. This will cause a default + value of 0 (no) to be used. */ + new->grid = -1; + +/* Are tick marks to be placed on both edges in a pair of opposite edges? + Store a value of -1 to indicate that no value has yet been set. This will + cause a default value of 1 (yes) to be used. */ + new->tickall = -1; + +/* Graphics context identifier */ + new->grfcontext = NULL; + new->grfcontextID = NULL; + +/* Shoudl ast Grid draw a boundary round the regions of valid coordinates? + Store a value of -1 to indicate that no value has yet been set. This will + cause a default value of 1 (yes) to be used. */ + new->border = -1; + +/* Should graphics be drawn invisible? Store a value of -1 to indicate that + no value has yet been set. This will cause a default value of 0 (no) to + be used. */ + new->invisible = -1; + +/* By default clip markers but not curves at the boundary of the plotting + area. This was the only behaviour available prior to the introduction of + the Clip attribute, and is chosen as the default to maintain backwards + compatibility. */ + new->clip = -1; + +/* Is clipping to be done using a logical OR operation between the axes? + Store a value of -1 to indicate that no value has yet been set. This will + cause a default value of 0 (no, i.e. a logical AND) to be used. */ + new->clipop = -1; + +/* Is the graphics interface registered using astGrfSet to be used? + Store a value of -1 to indicate that no value has yet been set. This will + cause a default value of 0 (no, i.e. use the graphics interface + selected at link-time) to be used. */ + new->grf = -1; + +/* Wrapper functions to call the drawing routines. These are the + default wrappers which call GRF routines written in C. Alternative + wrappers are defined in fplot.c for use with GRF routines written in + F77. */ + new->GAttr = CGAttrWrapper; + new->GBBuf = CGBBufWrapper; + new->GEBuf = CGEBufWrapper; + new->GFlush = CGFlushWrapper; + new->GLine = CGLineWrapper; + new->GMark = CGMarkWrapper; + new->GText = CGTextWrapper; + new->GCap = CGCapWrapper; + new->GTxExt = CGTxExtWrapper; + new->GScales = CGScalesWrapper; + new->GQch = CGQchWrapper; + + for( i = 0; i < AST__NGRFFUN; i++ ) new->grffun[i] = NULL; + new->grfstack = NULL; + new->grfnstack = 0; + +/* Is a title to be added to an annotated grid? Store a value of -1 to + indicate that no value has yet been set. This will cause a default value + of 1 (yes) to be used. */ + new->drawtitle = -1; + +/* Are escape sequences within text strings to be interpreted? If not, + they are printed literally. Store a value of -1 when not set. + This will cause a default value of 1 (yes) to be used. */ + new->escape = -1; + +/* A boolean attribute indicating where numerical labels are to be + placed; zero implies round the edges of the plotting area; non-zero + implies within the plotting area. The unset value of -9999 yields a + default of zero. */ + new->labelling = -9999; + +/* Graphics attributes. Default behaviour is to use the current values. */ + for( id = 0; id < AST__NPID; id++ ){ + new->style[ id ] = -1; + new->font[ id ] = -1; + new->colour[ id ] = -1; + new->width[ id ] = AST__BAD; + new->size[ id ] = AST__BAD; + } + +/* The space between the top edge and the grid title as a fraction of the + minimum dimension of the plotting area. Store AST__BAD to indicate that no + value has been set. This will cause a default of 0.05 to be used. */ + new->titlegap = AST__BAD; + +/* Initialise the protected Ink attribute so that visible ink is used. */ + new->ink = -1; + +/* A stack of AstGat pointers used to store the graphical attributes for + text within strings which include graphical escape sequences. */ + new->gat = NULL; + new->ngat = 0; + +/* Now set the attribute values for each axis. The arrays stored in the + Plot struture allow for 3 graphics axes (e.g. as used by a Plot3D) so + we initialise 3 axes here even though the Plot class only uses 2. */ + for( axis = 0; axis < 3; axis++ ) { + +/* Are curves to be drawn through the tick marks even if no grid is + produced? Store a value of -1 to indicate that no value has yet been + set. This will cause a default value of 1 (yes) to be used. */ + new->drawaxes[ axis ] = -1; + +/* Are adjacent numerical axis labels to be abbreviated by removing matching + leading fields? Store a value of -1 to indicate that no value has yet been + set. This will cause a default value of 1 (yes) to be used. */ + new->abbrev[ axis ] = -1; + +/* The length of the major tick marks as a fraction of the minimum + dimension of the plotting area. Store AST__BAD to indicate that no + value has been set. This will cause a default of 0.015 to be used. */ + new->majticklen[ axis ] = AST__BAD; + +/* The length of the minor tick marks as a fraction of the minimum + dimension of the plotting area. Store AST__BAD to indicate that no + value has been set. This will cause a default of 0.007 to be used. */ + new->minticklen[ axis ] = AST__BAD; + +/* Are numeric labels to be drawn upright? Store a value of -1 to indicate + that no value has yet been set. This will cause a default value of 0 (no) + to be used. */ + new->labelup[ axis ] = -1; + +/* The space between an axis and its numeric labels as a fraction of the + minimum dimension of the plotting area. Store AST__BAD to indicate that no + value has been set. This will cause a default of 0.01 to be used. */ + new->numlabgap[ axis ] = AST__BAD; + new->textlabgap[ axis ] = AST__BAD; + +/* The edges on which to put labels for axes 1 and 2. Store values of -1 + to indicate that no values have been set. This will cause default values + to be used. */ + new->edge[ axis ] = -1; + +/* The placement of labels within the plotting area will be done + automatically by default. */ + new->labelat[ axis ] = AST__BAD; + +/* The central tick is placed automatically by default. */ + new->centre[ axis ] = AST__BAD; + +/* The gap between tick marks and the number of minor tick marks will be + found automatically by default. */ + new->gap[ axis ] = AST__BAD; + new->loggap[ axis ] = AST__BAD; + new->mintick[ axis ] = -1; + +/* Both axes will be labelled by default. */ + new->numlab[ axis ] = -1; + new->textlab[ axis ] = -1; + new->labelunits[ axis ] = -1; + +/* Log/lin attributes. Default value is to use linear axes. */ + new->logplot[ axis ] = -1; + new->logticks[ axis ] = -1; + new->loglabel[ axis ] = -1; + +/* Initialise the components used to store the values actually used + for attributes which have dynamic defaults. */ + new->ulglb[ axis ] = new->loglabel[ axis ]; + new->ulgtk[ axis ] = new->logticks[ axis ]; + new->uloggap[ axis ] = new->loggap[ axis ]; + new->ugap[ axis ] = new->gap[ axis ]; + new->ucentre[ axis ] = new->centre[ axis ]; + new->uedge[ axis ] = new->edge[ axis ]; + new->ulblat[ axis ] = new->labelat[ axis ]; + new->ulbunit[ axis ] = new->labelunits[ axis ]; + new->umintk[ axis ] = new->mintick[ axis ]; + new->utxtlb[ axis ] = new->textlab[ axis ]; + new->umjtkln[ axis ] = new->majticklen[ axis ]; + +/* Initialise the arrays used to hold information describing the tick + marks that have been drawn for the axis. */ + new->majtickgx[ axis ] = NULL; + new->majtickgy[ axis ] = NULL; + new->majtickcount[ axis ] = 0; + new->mintickgx[ axis ] = NULL; + new->mintickgy[ axis ] = NULL; + new->mintickcount[ axis ] = 0; + new->nmajtickval[ axis ] = 0; + new->majtickval[ axis ] = NULL; + new->nmintickval[ axis ] = 0; + new->mintickval[ axis ] = NULL; + } + + new->ugrid = new->grid; + new->ulbling = new->labelling; + new->uborder = new->border; + + } + +/* Annul the frame. */ + graphicsframe = astAnnul( graphicsframe ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + +/* Annul the pointer to the base Frame and FrameSet. */ + baseframe = astAnnul( baseframe ); + fset = astAnnul( fset ); + +/* Return a pointer to the new object. */ + return new; +} + +AstPlot *astLoadPlot_( void *mem, size_t size, + AstPlotVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPlot + +* Purpose: +* Load a Plot. + +* Type: +* Protected function. + +* Synopsis: +* #include "Plot.h" +* AstPlot *astLoadPlot( void *mem, size_t size, +* AstPlotVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Plot loader. + +* Description: +* This function is provided to load a new Plot using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Plot structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Plot at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the Plot is to be +* loaded. This must be of sufficient size to accommodate the +* Plot data (sizeof(Plot)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Plot (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Plot structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPlot) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Plot. If this is NULL, a pointer +* to the (static) virtual function table for the Plot class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Plot" is used instead. + +* Returned Value: +* A pointer to the new Plot. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPlot *new; /* Pointer to the new Plot */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char *text; /* Textual version of integer value */ + int axis; /* Zero based axis index */ + int id; /* Zero based graphical object id */ + int i; /* Loop count */ + int itick; /* Tick mark index */ + int nax; /* Number of base Frame axes */ + int ntick; /* Total number of ticks */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Plot. In this case the + Plot belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPlot ); + vtab = &class_vtab; + name = "Plot"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPlotVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Plot. */ + new = astLoadFrameSet( mem, size, (AstFrameSetVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Get the number of graphics (base) frame axes - 2 for a Plot, 3 for a + Plot3D. */ + nax = astGetNin( new ); + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Plot" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Tol. */ +/* ---- */ + new->tol = astReadDouble( channel, "tol", -1.0 ); + if ( TestTol( new, status ) ) SetTol( new, new->tol, status ); + +/* Grid. */ +/* ----- */ + new->grid = astReadInt( channel, "grid", -1 ); + if ( TestGrid( new, status ) ) SetGrid( new, new->grid, status ); + +/* TickAll. */ +/* -------- */ + new->tickall = astReadInt( channel, "tckall", -1 ); + if ( TestTickAll( new, status ) ) SetTickAll( new, new->tickall, status ); + +/* ForceExterior. */ +/* -------- */ + new->forceexterior = astReadInt( channel, "frcext", -1 ); + if ( TestForceExterior( new, status ) ) SetForceExterior( new, new->forceexterior, status ); + +/* Invisible. */ +/* ---------- */ + new->invisible = astReadInt( channel, "invsbl", -1 ); + if ( TestInvisible( new, status ) ) SetInvisible( new, new->invisible, status ); + +/* Border. */ +/* -------- */ + new->border = astReadInt( channel, "border", -1 ); + if ( TestBorder( new, status ) ) SetBorder( new, new->border, status ); + +/* ClipOp. */ +/* ------- */ + new->clipop = astReadInt( channel, "clpop", -1 ); + if ( TestClipOp( new, status ) ) SetClipOp( new, new->clipop, status ); + +/* Clip. */ +/* ----- */ + new->clip = astReadInt( channel, "clip", -1 ); + if ( TestClip( new, status ) ) SetClip( new, new->clip, status ); + +/* DrawTitle. */ +/* --------- */ + new->drawtitle = astReadInt( channel, "drwttl", -1 ); + if ( TestDrawTitle( new, status ) ) SetDrawTitle( new, new->drawtitle, status ); + +/* LabelUp(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "lblup%d", axis + 1 ); + new->labelup[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestLabelUp( new, axis, status ) ) SetLabelUp( new, axis, + new->labelup[ axis ], status ); + } + +/* LogPlot(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "lgplt%d", axis + 1 ); + new->logplot[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestLogPlot( new, axis, status ) ) SetLogPlot( new, axis, + new->logplot[ axis ], status ); + } + +/* LogTicks(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "lgtck%d", axis + 1 ); + new->logticks[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestLogTicks( new, axis, status ) ) SetLogTicks( new, axis, + new->logticks[ axis ], status ); + } + +/* LogLabel(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "lglbl%d", axis + 1 ); + new->loglabel[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestLogLabel( new, axis, status ) ) SetLogLabel( new, axis, + new->loglabel[ axis ], status ); + } + +/* DrawAxes. */ +/* --------- */ + new->drawaxes[ 0 ] = astReadInt( channel, "drwaxs", -1 ); + + if( new->drawaxes[ 0 ] != -1 ) { + new->drawaxes[ 1 ] = new->drawaxes[ 0 ]; + if ( TestDrawAxes( new, 0, status ) ) SetDrawAxes( new, 0, new->drawaxes[ 0 ], status ); + if ( TestDrawAxes( new, 1, status ) ) SetDrawAxes( new, 1, new->drawaxes[ 1 ], status ); + + } else { + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "drwaxs%d", axis + 1 ); + new->drawaxes[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestDrawAxes( new, axis, status ) ) SetDrawAxes( new, axis, + new->drawaxes[ axis ], status ); + } + } + +/* Abbrev. */ +/* ------- */ + new->abbrev[ 0 ] = astReadInt( channel, "abbrv", -1 ); + + if( new->abbrev[ 0 ] != -1 ) { + new->abbrev[ 1 ] = new->abbrev[ 0 ]; + if ( TestAbbrev( new, 0, status ) ) SetAbbrev( new, 0, new->abbrev[ 0 ], status ); + if ( TestAbbrev( new, 1, status ) ) SetAbbrev( new, 1, new->abbrev[ 1 ], status ); + + } else { + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "abbrv%d", axis + 1 ); + new->abbrev[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestAbbrev( new, axis, status ) ) SetAbbrev( new, axis, + new->abbrev[ axis ], status ); + } + } + + +/* Escape. */ +/* ------- */ + new->escape = astReadInt( channel, "escape", -1 ); + if ( TestEscape( new, status ) ) SetEscape( new, new->escape, status ); + +/* LabelAt(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "lblat%d", axis + 1 ); + new->labelat[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestLabelAt( new, axis, status ) ) SetLabelAt( new, axis, + new->labelat[ axis ], status ); + } + +/* Centre(axis). */ +/* ------------ */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "cen%d", axis + 1 ); + new->centre[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestCentre( new, axis, status ) ) SetCentre( new, axis, + new->centre[ axis ], status ); + } + +/* Gap(axis). */ +/* ---------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "gap%d", axis + 1 ); + new->gap[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestGap( new, axis, status ) ) SetGap( new, axis, new->gap[ axis ], status ); + } + +/* LogGap(axis). */ +/* ------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "lggap%d", axis + 1 ); + new->loggap[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestLogGap( new, axis, status ) ) SetLogGap( new, axis, new->loggap[ axis ], status ); + } + +/* NumLabGap(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "nmgap%d", axis + 1 ); + new->numlabgap[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestNumLabGap( new, axis, status ) ) SetNumLabGap( new, axis, + new->numlabgap[ axis ], status ); + } + +/* TextLabGap(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "txgap%d", axis + 1 ); + new->textlabgap[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestTextLabGap( new, axis, status ) ) SetTextLabGap( new, axis, + new->textlabgap[ axis ], status ); + } + +/* NumLab(axis). */ +/* ---------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "nmlbl%d", axis + 1 ); + new->numlab[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestNumLab( new, axis, status ) ) SetNumLab( new, axis, + new->numlab[ axis ], status ); + } + +/* MinTick(axis). */ +/* --------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "mntks%d", axis + 1 ); + new->mintick[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestMinTick( new, axis, status ) ) SetMinTick( new, axis, + new->mintick[ axis ], status ); + } + +/* TextLab(axis). */ +/* -------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "txlbl%d", axis + 1 ); + new->textlab[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestTextLab( new, axis, status ) ) SetTextLab( new, axis, + new->textlab[ axis ], status ); + } + +/* LabelUnits(axis). */ +/* --------------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "lbunt%d", axis + 1 ); + new->labelunits[ axis ] = astReadInt( channel, buff, -1 ); + if ( TestLabelUnits( new, axis, status ) ) SetLabelUnits( new, axis, + new->labelunits[ axis ], status ); + } + +/* Style(object). */ +/* ------------ */ + for( id = 0; id < AST__NPID; id++ ){ + (void) sprintf( buff, "style%d", id + 1 ); + new->style[ id ] = astReadInt( channel, buff, -1 ); + if ( TestStyle( new, id, status ) ) SetStyle( new, id, new->style[ id ], status ); + } + +/* Font(object). */ +/* ----------- */ + for( id = 0; id < AST__NPID; id++ ){ + (void) sprintf( buff, "font%d", id + 1 ); + new->font[ id ] = astReadInt( channel, buff, -1 ); + if ( TestFont( new, id, status ) ) SetFont( new, id, new->font[ id ], status ); + } + +/* Colour(object). */ +/* --------------- */ + for( id = 0; id < AST__NPID; id++ ){ + (void) sprintf( buff, "col%d", id + 1 ); + new->colour[ id ] = astReadInt( channel, buff, -1 ); + if ( TestColour( new, id, status ) ) SetColour( new, id, new->colour[ id ], status ); + } + +/* Width(object). */ +/* ------------ */ + for( id = 0; id < AST__NPID; id++ ){ + (void) sprintf( buff, "width%d", id + 1 ); + new->width[ id ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestWidth( new, id, status ) ) SetWidth( new, id, new->width[ id ], status ); + } + +/* Size(object). */ +/* ----------- */ + for( id = 0; id < AST__NPID; id++ ){ + (void) sprintf( buff, "size%d", id + 1 ); + new->size[ id ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestSize( new, id, status ) ) SetSize( new, id, new->size[ id ], status ); + } + +/* TitleGap. */ +/* --------- */ + new->titlegap = astReadDouble( channel, "ttlgap", AST__BAD ); + if ( TestTitleGap( new, status ) ) SetTitleGap( new, new->titlegap, status ); + +/* MajTickLen. */ +/* ----------- */ +/* Retained in order to read old Plots - new Plots use MajTickLen(axis). */ + new->majticklen[ 0 ] = astReadDouble( channel, "mjtkln", AST__BAD ); + if( new->majticklen[ 0 ] != AST__BAD ) { + new->majticklen[ 1 ] = new->majticklen[ 0 ]; + if ( TestMajTickLen( new, 0, status ) ) SetMajTickLen( new, 0, new->majticklen[ 0 ], status ); + if ( TestMajTickLen( new, 1, status ) ) SetMajTickLen( new, 1, new->majticklen[ 1 ], status ); + +/* MajTickLen(axis). */ +/* ----------------- */ + } else { + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "mjtkln%d", axis + 1 ); + new->majticklen[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestMajTickLen( new, axis, status ) ) SetMajTickLen( new, axis, + new->majticklen[ axis ], status ); + } + } + +/* MinTickLen. */ +/* ----------- */ +/* Retained in order to read old Plots - new Plots use MinTickLen(axis). */ + new->minticklen[ 0 ] = astReadDouble( channel, "mntkln", AST__BAD ); + if( new->minticklen[ 0 ] != AST__BAD ) { + new->minticklen[ 1 ] = new->minticklen[ 0 ]; + if ( TestMinTickLen( new, 0, status ) ) SetMinTickLen( new, 0, new->minticklen[ 0 ], status ); + if ( TestMinTickLen( new, 1, status ) ) SetMinTickLen( new, 1, new->minticklen[ 1 ], status ); + +/* MinTickLen(axis). */ +/* ----------------- */ + } else { + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "mntkln%d", axis + 1 ); + new->minticklen[ axis ] = astReadDouble( channel, buff, AST__BAD ); + if ( TestMinTickLen( new, axis, status ) ) SetMinTickLen( new, axis, + new->minticklen[ axis ], status ); + } + } + +/* Labelling. */ +/* ---------- */ + text = astReadString( channel, "lbling", " " ); + if( astOK && strcmp( text, " " ) ) { + new->labelling = FindString( 2, xlbling, text, + "the Plot component 'Lbling'", + "astRead", astGetClass( channel ), status ); + } else { + new->labelling = -9999; + } + if ( TestLabelling( new, status ) ) SetLabelling( new, new->labelling, status ); + text = astFree( text ); + +/* Edge(axis). */ +/* ----------- */ + for( axis = 0; axis < nax; axis++ ){ + (void) sprintf( buff, "edge%d", axis + 1 ); + text = astReadString( channel, buff, " " ); + if( astOK && strcmp( text, " " ) ) { + new->edge[ axis ] = FindString( 4, xedge, text, + "the Plot component 'Edge'", + "astRead", astGetClass( channel ), status ); + } else { + new->edge[ axis ] = -1; + } + if ( TestEdge( new, axis, status ) ) SetEdge( new, axis, + new->edge[ axis ], status ); + text = astFree( text ); + } + +/* Now do instance variables which are not attributes. */ +/* =================================================== */ + +/* We have no graphics context. */ + new->grfcontext = NULL; + new->grfcontextID = NULL; + +/* Initialise the protected Ink attribute so that visible ink is used. */ + new->ink = -1; + +/* The number of bounds supplied for the clipping volume. */ + new->clip_axes = astReadInt( channel, "clpaxs", 0 ); + if ( new->clip_axes < 0 ) new->clip_axes = 0; + +/* The index of the clipping Frame within the Plot. */ + new->clip_frame = astReadInt( channel, "clpfrm", AST__NOFRAME ); + +/* If necessary, allocate memory to hold the bounds of the clipping volume. */ + if( new->clip_axes > 0 ){ + new->clip_lbnd = astMalloc( sizeof( double ) * (size_t) new->clip_axes ); + new->clip_ubnd = astMalloc( sizeof( double ) * (size_t) new->clip_axes ); + +/* If an error occurs, ensure that all allocated memory is freed. */ + if ( !astOK ) { + new->clip_lbnd = (double *) astFree( (void *) new->clip_lbnd ); + new->clip_ubnd = (double *) astFree( (void *) new->clip_ubnd ); + +/* Otherwise, store the bounds. Use extreme defaults if no values are + available. */ + } else { + for( axis = 0; axis < new->clip_axes; axis++ ){ + (void) sprintf( buff, "clplb%d", axis + 1 ); + new->clip_lbnd[ axis ] = astReadDouble( channel, buff, -DBL_MAX ); + + (void) sprintf( buff, "clpub%d", axis + 1 ); + new->clip_ubnd[ axis ] = astReadDouble( channel, buff, DBL_MAX ); + } + } + +/* If there are no clipping axes, store NULL pointers for the bounds + arrays. */ + } else { + new->clip_lbnd = NULL; + new->clip_ubnd = NULL; + } + +/* The bounds of the plotting area in graphics coords. */ + new->xlo = astReadDouble( channel, "xlo", 0.0 ); + new->xhi = astReadDouble( channel, "xhi", 1.0 ); + new->ylo = astReadDouble( channel, "ylo", 0.0 ); + new->yhi = astReadDouble( channel, "yhi", 1.0 ); + +/* Axis reversal flags. */ + new->xrev = astReadInt( channel, "xrev", 0 ); + new->yrev = astReadInt( channel, "yrev", 0 ); + +/* The bounds of the plotting area in the base Frame of the FrameSet + supplied when the Plot was constructed. */ + new->bbox[ 0 ] = astReadDouble( channel, "xb1", AST__BAD ); + new->bbox[ 1 ] = astReadDouble( channel, "yb1", AST__BAD ); + new->bbox[ 2 ] = astReadDouble( channel, "xb2", AST__BAD ); + new->bbox[ 3 ] = astReadDouble( channel, "yb2", AST__BAD ); + +/* Grf. */ + new->grf = -1; + new->GAttr = CGAttrWrapper; + new->GBBuf = CGBBufWrapper; + new->GEBuf = CGEBufWrapper; + new->GFlush = CGFlushWrapper; + new->GLine = CGLineWrapper; + new->GMark = CGMarkWrapper; + new->GText = CGTextWrapper; + new->GCap = CGCapWrapper; + new->GTxExt = CGTxExtWrapper; + new->GScales = CGScalesWrapper; + new->GQch = CGQchWrapper; + for( i = 0; i < AST__NGRFFUN; i++ ) new->grffun[i] = NULL; + new->grfstack = NULL; + new->grfnstack = 0; + +/* A stack of AstGat pointers used to store the graphical attributes for + text within strings which include graphical escape sequences. */ + new->gat = NULL; + new->ngat = 0; + +/* Arrays holding user-specified major and minot tick mark values. */ + for( axis = 0; axis < 3; axis++ ) { + sprintf( buff, "nmjtk%d", axis + 1 ); + ntick = astReadInt( channel, buff, 0 ); + new->nmajtickval[ axis ] = ntick; + new->majtickval[ axis ] = astMalloc( ntick*sizeof( double ) ); + + for( itick = 0; itick < ntick; itick++ ) { + sprintf( buff, "mjtk%d_%d", axis + 1, itick + 1 ); + new->majtickval[ axis ][ itick ] = astReadDouble( channel, buff, + AST__BAD ); + } + + sprintf( buff, "nmntk%d", axis + 1 ); + ntick = astReadInt( channel, buff, 0 ); + new->nmintickval[ axis ] = ntick; + new->mintickval[ axis ] = astMalloc( ntick*sizeof( double ) ); + + for( itick = 0; itick < ntick; itick++ ) { + sprintf( buff, "mntk%d_%d", axis + 1, itick + 1 ); + new->mintickval[ axis ][ itick ] = astReadDouble( channel, buff, + AST__BAD ); + } + + } + +/* Initialise the arrays used to hold information describing the tick + marks that have already been drawn for each axis. */ + for( axis = 0; axis < 3; axis++ ) { + new->majtickgx[ axis ] = NULL; + new->majtickgy[ axis ] = NULL; + new->majtickcount[ axis ] = 0; + new->mintickgx[ axis ] = NULL; + new->mintickgy[ axis ] = NULL; + new->mintickcount[ axis ] = 0; + } + +/* If an error occurred, clean up by deleting the new Plot. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Plot pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astBBuf_( AstPlot *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,BBuf))(this, status ); +} + +int astBorder_( AstPlot *this, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,Plot,Border))(this, status ); +} + +void astBoundingBox_( AstPlot *this, float lbnd[2], float ubnd[2], int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,BoundingBox))(this,lbnd,ubnd, status ); +} + +void astClip_( AstPlot *this, int iframe, const double lbnd[], +const double ubnd[], int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,Clip))(this,iframe,lbnd,ubnd, status ); +} + +void astGrid_( AstPlot *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,Grid))(this, status ); +} + +int astCvBrk_( AstPlot *this, int ibrk, double *brk, double *vbrk, + double *len, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,Plot,CvBrk))(this,ibrk,brk,vbrk,len, status ); +} + +void astEBuf_( AstPlot *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,EBuf))(this, status ); +} + +void astMirror_( AstPlot *this, int axis, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,Mirror))(this,axis, status ); +} + +AstPointSet *astGetDrawnTicks_( AstPlot *this, int axis, int major, int *status ){ + if( !astOK ) return NULL; + return (**astMEMBER(this,Plot,GetDrawnTicks))(this,axis,major, status ); +} + +void astSetTickValues_( AstPlot *this, int axis, int nmajor, double *major, + int nminor, double *minor, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,SetTickValues))(this,axis,nmajor,major,nminor,minor, status ); +} + +void astCopyPlotDefaults_( AstPlot *this, int axis, AstPlot *dplot, + int daxis, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,CopyPlotDefaults))(this,axis,dplot,daxis, status ); +} + +int astGetLabelUnits_( AstPlot *this, int axis, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,Plot,GetLabelUnits))(this,axis, status ); +} + +void astMark_( AstPlot *this, int nmark, int ncoord, int indim, + const double *in, int type, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Plot,Mark))( this, nmark, ncoord, indim, in, type, status ); +} + +void astText_( AstPlot *this, const char *text, const double pos[], + const float up[], const char *just, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Plot,Text))( this, text, pos, up, just, status ); +} + +void astGridLine_( AstPlot *this, int axis, const double start[], double length, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,GridLine))(this,axis,start,length, status ); +} + +void astCurve_( AstPlot *this, const double start[], const double finish[], int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,Curve))(this,start,finish, status ); +} + +void astGenCurve_( AstPlot *this, AstMapping *map, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,GenCurve))(this,map, status ); +} + +void astPolyCurve_( AstPlot *this, int npoint, int ncoord, int dim, + const double *in, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,PolyCurve))(this,npoint,ncoord,dim,in, status ); +} + +void astRegionOutline_( AstPlot *this, AstRegion *region, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,RegionOutline))(this,region,status); +} + +void astGrfSet_( AstPlot *this, const char *name, AstGrfFun fun, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,GrfSet))(this,name,fun, status ); +} + +void astGrfPush_( AstPlot *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,GrfPush))(this, status ); +} + +void astGrfPop_( AstPlot *this, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,GrfPop))(this, status ); +} + +void astGrfWrapper_( AstPlot *this, const char *name, AstGrfWrap wrapper, int *status ){ + if( !astOK ) return; + (**astMEMBER(this,Plot,GrfWrapper))(this,name,wrapper, status ); +} + +void astSetLogPlot_( AstPlot *this, int axis, int value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Plot,SetLogPlot))( this, axis, value, status ); +} + +void astClearLogPlot_( AstPlot *this, int axis, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Plot,ClearLogPlot))( this, axis, status ); +} + +AstKeyMap *astGetGrfContext_( AstPlot *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Plot,GetGrfContext))( this, status ); +} + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstPlot *astPlotId_( void *frame_void, const float graphbox[4], + const double basebox[4], const char *options, ... ) { +/* +*++ +* Name: +c astPlot +f AST_PLOT + +* Purpose: +* Create a Plot. + +* Type: +* Public function. + +* Synopsis: +c #include "plot.h" +c AstPlot *astPlot( AstFrame *frame, const float graphbox[ 4 ], +c const double basebox[ 4 ], const char *options, ... ) +f RESULT = AST_PLOT( FRAME, GRAPHBOX, BASEBOX, OPTIONS, STATUS ) + +* Class Membership: +* Plot constructor. + +* Description: +* This function creates a new Plot and optionally initialises its +* attributes. +* +* A Plot is a specialised form of FrameSet, in which the base +* Frame describes a "graphical" coordinate system and is +* associated with a rectangular plotting area in the underlying +* graphics system. This plotting area is where graphical output +* appears. It is defined when the Plot is created. +* +* The current Frame of a Plot describes a "physical" coordinate +* system, which is the coordinate system in which plotting +* operations are specified. The results of each plotting operation +* are automatically transformed into graphical coordinates so as +* to appear in the plotting area (subject to any clipping which +* may be in effect). +* +* Because the Mapping between physical and graphical coordinates +* may often be non-linear, or even discontinuous, most plotting +* does not result in simple straight lines. The basic plotting +* element is therefore not a straight line, but a geodesic curve +c (see astCurve). A Plot also provides facilities for drawing +c markers or symbols (astMark), text (astText) and grid lines +c (astGridLine). It is also possible to draw curvilinear axes with +c optional coordinate grids (astGrid). +f (see AST_CURVE). A Plot also provides facilities for drawing +f markers or symbols (AST_MARK), text (AST_TEXT) and grid lines +f (AST_GRIDLINE). It is also possible to draw curvilinear axes +f with optional coordinate grids (AST_GRID). +* A range of Plot attributes is available to allow precise control +* over the appearance of graphical output produced by these +c functions. +f routines. +* +* You may select different physical coordinate systems in which to +* plot (including the native graphical coordinate system itself) +* by selecting different Frames as the current Frame of a Plot, +* using its Current attribute. You may also set up clipping (see +c astClip) to limit the extent of any plotting you perform, and +f AST_CLIP) to limit the extent of any plotting you perform, and +* this may be done in any of the coordinate systems associated +* with the Plot, not necessarily the one you are plotting in. +* +* Like any FrameSet, a Plot may also be used as a Frame. In this +* case, it behaves like its current Frame, which describes the +* physical coordinate system. +* +* When used as a Mapping, a Plot describes the inter-relation +* between graphical coordinates (its base Frame) and physical +* coordinates (its current Frame). It differs from a normal +* FrameSet, however, in that an attempt to transform points which +* lie in clipped areas of the Plot will result in bad coordinate +* values (AST__BAD). + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* Pointer to a Frame describing the physical coordinate system +* in which to plot. A pointer to a FrameSet may also be given, +* in which case its current Frame will be used to define the +* physical coordinate system and its base Frame will be mapped +* on to graphical coordinates (see below). +* +* If a null Object pointer (AST__NULL) is given, a default +* 2-dimensional Frame will be used to describe the physical +* coordinate system. Labels, etc. may then be attached to this +* by setting the appropriate Frame attributes +* (e.g. Label(axis)) for the Plot. +c graphbox +f GRAPHBOX( 4 ) = REAL (Given) +* An array giving the position and extent of the plotting area +* (on the plotting surface of the underlying graphics system) +* in which graphical output is to appear. This must be +* specified using graphical coordinates appropriate to the +* underlying graphics system. +* +* The first pair of values should give the coordinates of the +* bottom left corner of the plotting area and the second pair +* should give the coordinates of the top right corner. The +* coordinate on the horizontal axis should be given first in +* each pair. Note that the order in which these points are +* given is important because it defines up, down, left and +* right for subsequent graphical operations. +c basebox +f BASEBOX( 4 ) = DOUBLE PRECISION (Given) +* An array giving the coordinates of two points in the supplied +* Frame (or in the base Frame if a FrameSet was supplied) which +* correspond to the bottom left and top right corners of the +* plotting area, as specified above. This range of coordinates +* will be mapped linearly on to the plotting area. The +* coordinates should be given in the same order as above. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Plot. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Plot. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPlot() +f AST_PLOT +* A pointer to the new Plot. + +* Notes: +* - The base Frame of the returned Plot will be a new Frame which +* is created by this function to represent the coordinate system +* of the underlying graphics system (graphical coordinates). It is +* given a Frame index of 1 within the Plot. The choice of base +* Frame (Base attribute) should not, in general, be changed once a +* Plot has been created (although you could use this as a way of +* moving the plotting area around on the plotting surface). +c - If a Frame is supplied (via the "frame" pointer), then it +f - If a Frame is supplied (via the FRAME pointer), then it +* becomes the current Frame of the new Plot and is given a Frame +* index of 2. +c - If a FrameSet is supplied (via the "frame" pointer), then +f - If a FrameSet is supplied (via the FRAME pointer), then +* all the Frames within this FrameSet become part of the new Plot +* (where their Frame indices are increased by 1), with the +* FrameSet's current Frame becoming the current Frame of the Plot. +* - If a null Object pointer (AST__NULL) is supplied (via the +c "frame" pointer), then the returned Plot will contain two +f FRAME pointer), then the returned Plot will contain two +* Frames, both created by this function. The base Frame will +* describe graphics coordinates (as above) and the current Frame +* will be a basic Frame with no attributes set (this will +* therefore give default values for such things as the Plot Title +* and the Label on each axis). Physical coordinates will be mapped +* linearly on to graphical coordinates. +* - An error will result if the Frame supplied (or the base Frame +* if a FrameSet was supplied) is not 2-dimensional. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astPlot constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astPlot_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "frame" parameter is of type +* (void *) and is converted from an ID value to a pointer and +* validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astPlot_ directly, so it must be a +* re-implementation of it in all respects, except for the +* conversions between IDs and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPlot *new; /* Pointer to new Plot */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get apointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + new = NULL; + +/* Obtain a Frame pointer from any ID supplied and validate the + pointer to ensure it identifies a valid Frame. */ + if( frame_void ){ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + } else { + frame = NULL; + } + +/* Check the pointer can be used. */ + if ( astOK ) { + +/* Initialise the Plot, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPlot( NULL, sizeof( AstPlot ), !class_init, + &class_vtab, "Plot", frame, graphbox, + basebox ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + Plot's attributes. */ + + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new Plot. */ + return astMakeId( new ); + +} + + + + diff --git a/plot.f b/plot.f new file mode 100644 index 0000000..27e83b4 --- /dev/null +++ b/plot.f @@ -0,0 +1,71 @@ + CHARACTER * ( * ) FUNCTION AST_STRIPESCAPES( THIS, ATTRIB ) +*+ +* Name: +* AST_STRIPESCAPES + +* Purpose: +* Wrap up the C AST_STRIPESCAPES_A function. + +* Language: +* Fortran 77 + +* Invocation: +* RESULT = AST_STRIPESCAPES( THIS, ATTRIB ) + +* Description: +* This function is a wrap-up of the C ast_stripescapes_a function. It +* will rarely need to be used, but is provided for use on platforms +* where the normal mechanism for returning character strings as +* function results from C to Fortran (as defined in the f77.h +* include file) doesn't work. +* +* If this problem is encountered, the C macro NO_CHAR_FUNCTION +* should be defined (in CFLAGS) during C compilation. This will +* cause the function ast_stripescapes_a to be built (instead of +* ast_stripescapes) and this returns its the result via an additional +* initial argument. This Fortran function is then used simply to +* transfer the argument value to the function result. + +* Arguments: +* As for the C version of the Fortran-callable function ast_stripescapes. + +* Returned Value: +* AST_STRIPESCAPES = CHARACTER * ( * ) +* The character return value required. + +* Notes: +* - The length of the returned result is limited to 300 characters +* by a local buffer. This length can be increased if necessary. + +* Authors: +* DSB: David S Berry (JAC) +* {enter_new_authors_here} + +* History: +* 10-JUL-2006 (DSB): +* Original version. +* {enter_changes_here} + +* Bugs: +* {note_any_bugs_here} + +*- + +* Type Definitions: + IMPLICIT NONE ! No implicit typing + +* Arguments Given: + INTEGER THIS + CHARACTER * ( * ) ATTRIB + +* Local Variables: + CHARACTER * ( 300 ) BUFF ! Local buffer for result +*. + +* Invoke the C function (with the additional argument). + CALL AST_STRIPESCAPES_A( BUFF, THIS, ATTRIB ) + +* Return the argument value as the function result. + AST_STRIPESCAPES = BUFF + + END diff --git a/plot.h b/plot.h new file mode 100644 index 0000000..6f702ab --- /dev/null +++ b/plot.h @@ -0,0 +1,1417 @@ +#if !defined( PLOT_INCLUDED ) /* Include this file only once */ +#define PLOT_INCLUDED +/* +*+ +* Name: +* plot.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Plot class. + +* Invocation: +* #include "plot.h" + +* Description: +* This include file defines the interface to the Plot class and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this class. +* +* The Plot class provides facilities for producing graphical information +* describing positions within coordinate systems. These include the +* creation of annotated coordinate axes, the plotting of markers at given +* physical positions, etc. + +* Inheritance: +* The Plot class inherits from the FrameSet class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 18-SEP-1996 (DSB): +* Original version. +* 28-OCT-1998 (DSB): +* Added method astPolyCurve. +* 12-OCT-1999 (DSB): +* Allow tick marks to be specified separately for both axes. +* 9-JAN-2001 (DSB): +* Change argument "in" for astMark and astPolyCurve from type +* "const double (*)[]" to "const double *". +* 13-JUN-2001 (DSB): +* Added methods astGenCurve, astGrfSet, astGrfPop, astGrfPush and +* attribute Grf. +* 8-JAN-2003 (DSB): +* Added protected astInitPlotVtab method. +* 13-JAN-2004 (DSB): +* Added bbox, logticks and logplot to the AstPlot structure. Added +* LogPlot and LogTicks accessor methods. +* 19-JAN-2004 (DSB): +* Added loggap and loglabel to the AstPlot structure. Added +* LogGap and LogLabel accessor methods. +* 21-MAR-2005 (DSB): +* - Added the Clip attribute. +* 24-OCT-2006 (DSB): +* - Remove duplicated documentation from prologue. +* - Add ForceExterior attribute. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "frameset.h" /* Parent FrameSet class */ +#include "keymap.h" +#include "region.h" + +#if defined(astCLASS) /* Protected */ +#include "grf.h" +#endif + +/* C header files. */ +/* --------------- */ +#include + +/* Macros. */ +/* ======= */ + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST__NPID 20 /* No. of different genuine plot object id's */ + +#define AST__GATTR 0 /* Identifiers for GRF functions */ +#define AST__GFLUSH 1 /* Note, if any items are added or changed here, */ +#define AST__GLINE 2 /* make sure that the astGrfFunID function is */ +#define AST__GMARK 3 /* updated in plot.c */ +#define AST__GTEXT 4 +#define AST__GTXEXT 5 +#define AST__GSCALES 6 +#define AST__GQCH 7 +#define AST__GCAP 8 +#define AST__GBBUF 9 +#define AST__GEBUF 10 + +#define AST__NGRFFUN 11 /* No. of Grf functions used by Plot */ + +#if defined(astCLASS) /* Protected */ +#define AST__MXBRK 100 /* Max. no. of breaks in a drawn curve */ + +/* Identifiers for the graphical elements of an annotated coord grid. + "Pseudo-elements" (i.e. values used to indicate a group of other + genuine elements) should come at the end of the list. The number of + genuine elements should be stored in AST__NPID. */ +#define AST__BORDER_ID 0 /* Id for astBorder curves */ +#define AST__CURVE_ID 1 /* Id for astCurve, astGenCurve or astPolyCurve curves */ +#define AST__TITLE_ID 2 /* Id for textual title */ +#define AST__MARKS_ID 3 /* Id for marks drawn by astMark */ +#define AST__TEXT_ID 4 /* Id for text strings drawn by astText */ +#define AST__AXIS1_ID 5 /* Id for axis 1 through interior tick marks */ +#define AST__AXIS2_ID 6 /* Id for axis 2 through interior tick marks */ +#define AST__AXIS3_ID 7 /* Id for axis 2 through interior tick marks */ +#define AST__NUMLAB1_ID 8 /* Id for numerical labels */ +#define AST__NUMLAB2_ID 9 /* Id for numerical labels */ +#define AST__NUMLAB3_ID 10 /* Id for numerical labels */ +#define AST__TEXTLAB1_ID 11 /* Id for textual axis labels */ +#define AST__TEXTLAB2_ID 12 /* Id for textual axis labels */ +#define AST__TEXTLAB3_ID 13 /* Id for textual axis labels */ +#define AST__TICKS1_ID 14 /* Id for major and minor tick marks */ +#define AST__TICKS2_ID 15 /* Id for major and minor tick marks */ +#define AST__TICKS3_ID 16 /* Id for major and minor tick marks */ +#define AST__GRIDLINE1_ID 17 /* Id for axis 1 astGridLine AST__curves */ +#define AST__GRIDLINE2_ID 18 /* Id for axis 2 astGridLine AST__curves */ +#define AST__GRIDLINE3_ID 19 /* Id for axis 2 astGridLine AST__curves */ +#define AST__AXES_ID 20 /* Id for axes through interior tick marks */ +#define AST__NUMLABS_ID 21 /* Id for numerical labels */ +#define AST__TEXTLABS_ID 22 /* Id for textual axis labels */ +#define AST__GRIDLINE_ID 23 /* Id for astGridLine AST__curves */ +#define AST__TICKS_ID 24 /* Id for major and minor tick marks */ + +/* Define constants used to size global arrays in this module. */ +#define AST__PLOT_CRV_MXBRK 1000 /* Max. no. of breaks allowed in a plotted curve */ +#define AST__PLOT_STRIPESCAPES_BUFF_LEN 50 /* Length of string returned by astStripEscapes */ + +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions */ +/* ================ */ + +/* Pre-declare the AstPlot structure so that it can be used within the + GRF function typedefs. */ +struct AstPlot; + +/* Interfaces for GRF functions */ +/* ---------------------------- */ +/* A general interface into which actual Grf functions should be cast + before being passed as an argument to astGrfSet. */ +typedef void (* AstGrfFun)( void ); + +/* Interfaces for specific Grf funstions implemented in C (other languages + may have different interfaces). */ +typedef int (* AstGAttrFun)( AstKeyMap *, int, double, double *, int ); +typedef int (* AstGFlushFun)( AstKeyMap * ); +typedef int (* AstGBBufFun)( AstKeyMap * ); +typedef int (* AstGEBufFun)( AstKeyMap * ); +typedef int (* AstGLineFun)( AstKeyMap *, int, const float *, const float * ); +typedef int (* AstGMarkFun)( AstKeyMap *, int, const float *, const float *, int ); +typedef int (* AstGTextFun)( AstKeyMap *, const char *, float, float, const char *, float, float ); +typedef int (* AstGCapFun)( AstKeyMap *, int, int ); +typedef int (* AstGTxExtFun)( AstKeyMap *, const char *, float, float, const char *, float, float, float *, float * ); +typedef int (* AstGScalesFun)( AstKeyMap *, float *, float * ); +typedef int (* AstGQchFun)( AstKeyMap *, float *, float * ); + +/* A general interface into which Grf Wrapper functions should be cast + before being passed as an argument to astGrfWrapper. */ +typedef void (* AstGrfWrap)( void ); + +/* Interfaces for the wrapper functions which wrap specific Grf funstions. */ +typedef int (* AstGAttrWrap)( struct AstPlot *, int, double, double *, int, int * ); +typedef int (* AstGBBufWrap)( struct AstPlot *, int * ); +typedef int (* AstGEBufWrap)( struct AstPlot *, int * ); +typedef int (* AstGFlushWrap)( struct AstPlot *, int * ); +typedef int (* AstGLineWrap)( struct AstPlot *, int, const float *, const float *, int * ); +typedef int (* AstGMarkWrap)( struct AstPlot *, int, const float *, const float *, int, int * ); +typedef int (* AstGTextWrap)( struct AstPlot *, const char *, float, float, const char *, float, float, int * ); +typedef int (* AstGCapWrap)( struct AstPlot *, int, int, int * ); +typedef int (* AstGTxExtWrap)( struct AstPlot *, const char *, float, float, const char *, float, float, float *, float *, int * ); +typedef int (* AstGScalesWrap)( struct AstPlot *, float *, float *, int * ); +typedef int (* AstGQchWrap)( struct AstPlot *, float *, float *, int * ); + +/* A structure in which a collection of Grf function pointers can be + stored. */ +typedef struct AstGrfPtrs { + AstGrfFun grffun[AST__NGRFFUN]; + AstGAttrWrap GAttr; + AstGBBufWrap GBBuf; + AstGEBufWrap GEBuf; + AstGFlushWrap GFlush; + AstGLineWrap GLine; + AstGMarkWrap GMark; + AstGTextWrap GText; + AstGCapWrap GCap; + AstGTxExtWrap GTxExt; + AstGScalesWrap GScales; + AstGQchWrap GQch; +} AstGrfPtrs; + +/* Structure holding current graphical attribute values for text. */ +typedef struct AstGat { + float rise; + double size; + double width; + double col; + double font; + double style; +} AstGat; + +/* Plot structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstPlot { + +/* Attributes inherited from the parent class. */ + AstFrameSet parent; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *clip_lbnd; + double *clip_ubnd; + double centre[ 3 ]; + double gap[ 3 ]; + double loggap[ 3 ]; + double labelat[ 3 ]; + double majticklen[ 3 ]; + double minticklen[ 3 ]; + double numlabgap[ 3 ]; + double size[ AST__NPID ]; + double textlabgap[ 3 ]; + double titlegap; + double tol; + double ucentre[ 3 ]; + double ugap[ 3 ]; + double uloggap[ 3 ]; + double ulblat[ 3 ]; + double umjtkln[ 3 ]; + double width[ AST__NPID ]; + double *majtickgx[ 3 ]; + double *majtickgy[ 3 ]; + double *mintickgx[ 3 ]; + double *mintickgy[ 3 ]; + int majtickcount[ 3 ]; + int mintickcount[ 3 ]; + int nmajtickval[ 3 ]; + double *majtickval[ 3 ]; + int nmintickval[ 3 ]; + double *mintickval[ 3 ]; + double xhi; + double xlo; + double yhi; + double ylo; + double bbox[ 4 ]; + int border; + int clip_axes; + int clip_frame; + int clip; + int clipop; + int colour[ AST__NPID ]; + int drawaxes[ 3 ]; + int abbrev[ 3 ]; + int escape; + int drawtitle; + int edge[ 3 ]; + int font[ AST__NPID ]; + int grf; + int grid; + int invisible; + int labelling; + int labelunits[ 3 ]; + int labelup[ 3 ]; + int mintick[ 3 ]; + int numlab[ 3 ]; + int style[ AST__NPID ]; + int textlab[ 3 ]; + int tickall; + int forceexterior; + int uborder; + int uedge[ 3 ]; + int ugrid; + int ulbling; + int ulbunit[ 3 ]; + int ulgtk[ 3 ]; + int ulglb[ 3 ]; + int umintk[ 3 ]; + int utxtlb[ 3 ]; + int xrev; + int yrev; + int ink; + int logplot[ 3 ]; + int logticks[ 3 ]; + int loglabel[ 3 ]; + AstGrfFun grffun[AST__NGRFFUN]; + AstGAttrWrap GAttr; + AstGBBufWrap GBBuf; + AstGEBufWrap GEBuf; + AstGFlushWrap GFlush; + AstGLineWrap GLine; + AstGMarkWrap GMark; + AstGTextWrap GText; + AstGCapWrap GCap; + AstGTxExtWrap GTxExt; + AstGScalesWrap GScales; + AstGQchWrap GQch; + AstGrfPtrs *grfstack; + int grfnstack; + AstGat **gat; + int ngat; + AstKeyMap *grfcontext; + AstKeyMap *grfcontextID; + float hmarkx; + float hmarky; + +} AstPlot; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ + +typedef struct AstPlotVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameSetVtab FrameSet_vtab;/* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstKeyMap *(* GetGrfContext)( AstPlot *, int * ); + AstPointSet *(* GetDrawnTicks)( AstPlot *, int, int, int * ); + int (* Border)( AstPlot *, int * ); + int (* CvBrk)( AstPlot *, int, double *, double *, double *, int * ); + void (* BBuf)( AstPlot *, int * ); + void (* BoundingBox)( AstPlot *, float[2], float[2], int * ); + void (* Clip)( AstPlot *, int, const double [], const double [], int * ); + void (* CopyPlotDefaults)( AstPlot *, int, AstPlot *, int, int * ); + void (* Curve)( AstPlot *, const double [], const double [], int * ); + void (* DrawExtraTicks)( AstPlot *, int, AstPointSet *, int * ); + void (* EBuf)( AstPlot *, int * ); + void (* GenCurve)( AstPlot *, AstMapping *, int * ); + void (* GrfPop)( AstPlot *, int * ); + void (* GrfPush)( AstPlot *, int * ); + void (* GrfSet)( AstPlot *, const char *, AstGrfFun, int * ); + void (* GrfWrapper)( AstPlot *, const char *, AstGrfWrap, int * ); + void (* Grid)( AstPlot *, int * ); + void (* GridLine)( AstPlot *, int, const double [], double, int * ); + void (* Mark)( AstPlot *, int, int, int, const double *, int, int * ); + void (* Mirror)( AstPlot *, int, int * ); + void (* PolyCurve)( AstPlot *, int, int, int, const double *, int * ); + void (* RegionOutline)( AstPlot *, AstRegion *, int * ); + void (* SetTickValues)( AstPlot *, int, int, double *, int, double *, int * ); + void (* Text)( AstPlot *, const char *, const double [], const float [], const char *, int * ); + + double (* GetTol)( AstPlot *, int * ); + int (* TestTol)( AstPlot *, int * ); + void (* SetTol)( AstPlot *, double, int * ); + void (* ClearTol)( AstPlot *, int * ); + + int (* GetGrid)( AstPlot *, int * ); + int (* TestGrid)( AstPlot *, int * ); + void (* SetGrid)( AstPlot *, int, int * ); + void (* ClearGrid)( AstPlot *, int * ); + + int (* GetTickAll)( AstPlot *, int * ); + int (* TestTickAll)( AstPlot *, int * ); + void (* SetTickAll)( AstPlot *, int, int * ); + void (* ClearTickAll)( AstPlot *, int * ); + + int (* GetForceExterior)( AstPlot *, int * ); + int (* TestForceExterior)( AstPlot *, int * ); + void (* SetForceExterior)( AstPlot *, int, int * ); + void (* ClearForceExterior)( AstPlot *, int * ); + + int (* GetInvisible)( AstPlot *, int * ); + int (* TestInvisible)( AstPlot *, int * ); + void (* SetInvisible)( AstPlot *, int, int * ); + void (* ClearInvisible)( AstPlot *, int * ); + + int (* GetBorder)( AstPlot *, int * ); + int (* TestBorder)( AstPlot *, int * ); + void (* SetBorder)( AstPlot *, int, int * ); + void (* ClearBorder)( AstPlot *, int * ); + + int (* GetClipOp)( AstPlot *, int * ); + int (* TestClipOp)( AstPlot *, int * ); + void (* SetClipOp)( AstPlot *, int, int * ); + void (* ClearClipOp)( AstPlot *, int * ); + + int (* GetClip)( AstPlot *, int * ); + int (* TestClip)( AstPlot *, int * ); + void (* SetClip)( AstPlot *, int, int * ); + void (* ClearClip)( AstPlot *, int * ); + + int (* GetGrf)( AstPlot *, int * ); + int (* TestGrf)( AstPlot *, int * ); + void (* SetGrf)( AstPlot *, int, int * ); + void (* ClearGrf)( AstPlot *, int * ); + + int (* GetDrawTitle)( AstPlot *, int * ); + int (* TestDrawTitle)( AstPlot *, int * ); + void (* SetDrawTitle)( AstPlot *, int, int * ); + void (* ClearDrawTitle)( AstPlot *, int * ); + + int (* GetLabelUp)( AstPlot *, int, int * ); + int (* TestLabelUp)( AstPlot *, int, int * ); + void (* SetLabelUp)( AstPlot *, int, int, int * ); + void (* ClearLabelUp)( AstPlot *, int, int * ); + + int (* GetLogPlot)( AstPlot *, int, int * ); + int (* TestLogPlot)( AstPlot *, int, int * ); + void (* SetLogPlot)( AstPlot *, int, int, int * ); + void (* ClearLogPlot)( AstPlot *, int, int * ); + + int (* GetLogTicks)( AstPlot *, int, int * ); + int (* TestLogTicks)( AstPlot *, int, int * ); + void (* SetLogTicks)( AstPlot *, int, int, int * ); + void (* ClearLogTicks)( AstPlot *, int, int * ); + + int (* GetLogLabel)( AstPlot *, int, int * ); + int (* TestLogLabel)( AstPlot *, int, int * ); + void (* SetLogLabel)( AstPlot *, int, int, int * ); + void (* ClearLogLabel)( AstPlot *, int, int * ); + + int (* GetDrawAxes)( AstPlot *, int, int * ); + int (* TestDrawAxes)( AstPlot *, int, int * ); + void (* SetDrawAxes)( AstPlot *, int, int, int * ); + void (* ClearDrawAxes)( AstPlot *, int, int * ); + + int (* GetAbbrev)( AstPlot *, int, int * ); + int (* TestAbbrev)( AstPlot *, int, int * ); + void (* SetAbbrev)( AstPlot *, int, int, int * ); + void (* ClearAbbrev)( AstPlot *, int, int * ); + + int (* GetEscape)( AstPlot *, int * ); + int (* TestEscape)( AstPlot *, int * ); + void (* SetEscape)( AstPlot *, int, int * ); + void (* ClearEscape)( AstPlot *, int * ); + + int (* GetLabelling)( AstPlot *, int * ); + int (* TestLabelling)( AstPlot *, int * ); + void (* SetLabelling)( AstPlot *, int, int * ); + void (* ClearLabelling)( AstPlot *, int * ); + + double (* GetMajTickLen)( AstPlot *, int, int * ); + int (* TestMajTickLen)( AstPlot *, int, int * ); + void (* SetMajTickLen)( AstPlot *, int, double, int * ); + void (* ClearMajTickLen)( AstPlot *, int, int * ); + + double (* GetMinTickLen)( AstPlot *, int, int * ); + int (* TestMinTickLen)( AstPlot *, int, int * ); + void (* SetMinTickLen)( AstPlot *, int, double, int * ); + void (* ClearMinTickLen)( AstPlot *, int, int * ); + + double (* GetNumLabGap)( AstPlot *, int, int * ); + int (* TestNumLabGap)( AstPlot *, int, int * ); + void (* SetNumLabGap)( AstPlot *, int, double, int * ); + void (* ClearNumLabGap)( AstPlot *, int, int * ); + + double (* GetTextLabGap)( AstPlot *, int, int * ); + int (* TestTextLabGap)( AstPlot *, int, int * ); + void (* SetTextLabGap)( AstPlot *, int, double, int * ); + void (* ClearTextLabGap)( AstPlot *, int, int * ); + + double (* GetTitleGap)( AstPlot *, int * ); + int (* TestTitleGap)( AstPlot *, int * ); + void (* SetTitleGap)( AstPlot *, double, int * ); + void (* ClearTitleGap)( AstPlot *, int * ); + + double (* GetLabelAt)( AstPlot *, int, int * ); + int (* TestLabelAt)( AstPlot *, int, int * ); + void (* SetLabelAt)( AstPlot *, int, double, int * ); + void (* ClearLabelAt)( AstPlot *, int, int * ); + + double (* GetGap)( AstPlot *, int, int * ); + int (* TestGap)( AstPlot *, int, int * ); + void (* SetGap)( AstPlot *, int, double, int * ); + void (* ClearGap)( AstPlot *, int, int * ); + + double (* GetLogGap)( AstPlot *, int, int * ); + int (* TestLogGap)( AstPlot *, int, int * ); + void (* SetLogGap)( AstPlot *, int, double, int * ); + void (* ClearLogGap)( AstPlot *, int, int * ); + + double (* GetCentre)( AstPlot *, int, int * ); + int (* TestCentre)( AstPlot *, int, int * ); + void (* SetCentre)( AstPlot *, int, double, int * ); + void (* ClearCentre)( AstPlot *, int, int * ); + + int (* GetEdge)( AstPlot *, int, int * ); + int (* TestEdge)( AstPlot *, int, int * ); + void (* SetEdge)( AstPlot *, int, int, int * ); + void (* ClearEdge)( AstPlot *, int, int * ); + + int (* GetNumLab)( AstPlot *, int, int * ); + int (* TestNumLab)( AstPlot *, int, int * ); + void (* SetNumLab)( AstPlot *, int, int, int * ); + void (* ClearNumLab)( AstPlot *, int, int * ); + + int (* GetMinTick)( AstPlot *, int, int * ); + int (* TestMinTick)( AstPlot *, int, int * ); + void (* SetMinTick)( AstPlot *, int, int, int * ); + void (* ClearMinTick)( AstPlot *, int, int * ); + + int (* GetTextLab)( AstPlot *, int, int * ); + int (* TestTextLab)( AstPlot *, int, int * ); + void (* SetTextLab)( AstPlot *, int, int, int * ); + void (* ClearTextLab)( AstPlot *, int, int * ); + + int (* GetLabelUnits)( AstPlot *, int, int * ); + int (* TestLabelUnits)( AstPlot *, int, int * ); + void (* SetLabelUnits)( AstPlot *, int, int, int * ); + void (* ClearLabelUnits)( AstPlot *, int, int * ); + + int (* GetStyle)( AstPlot *, int, int * ); + int (* TestStyle)( AstPlot *, int, int * ); + void (* SetStyle)( AstPlot *, int, int, int * ); + void (* ClearStyle)( AstPlot *, int, int * ); + + int (* GetFont)( AstPlot *, int, int * ); + int (* TestFont)( AstPlot *, int, int * ); + void (* SetFont)( AstPlot *, int, int, int * ); + void (* ClearFont)( AstPlot *, int, int * ); + + int (* GetColour)( AstPlot *, int, int * ); + int (* TestColour)( AstPlot *, int, int * ); + void (* SetColour)( AstPlot *, int, int, int * ); + void (* ClearColour)( AstPlot *, int, int * ); + + double (* GetWidth)( AstPlot *, int, int * ); + int (* TestWidth)( AstPlot *, int, int * ); + void (* SetWidth)( AstPlot *, int, double, int * ); + void (* ClearWidth)( AstPlot *, int, int * ); + + double (* GetSize)( AstPlot *, int, int * ); + int (* TestSize)( AstPlot *, int, int * ); + void (* SetSize)( AstPlot *, int, double, int * ); + void (* ClearSize)( AstPlot *, int, int * ); + + int (* GetInk)( AstPlot *, int * ); + int (* TestInk)( AstPlot *, int * ); + void (* SetInk)( AstPlot *, int, int * ); + void (* ClearInk)( AstPlot *, int * ); + +} AstPlotVtab; + +/* Structure holding information about curves drawn by astGridLine and + astCurve. */ +typedef struct AstPlotCurveData{ + int out; /* Was the curve completely outside the clipping area? */ + int nbrk; /* The number of breaks in the curve. */ + float xbrk[ AST__PLOT_CRV_MXBRK ]; /* Graphics X coordinate at each break. */ + float ybrk[ AST__PLOT_CRV_MXBRK ]; /* Graphics Y coordinate at each break. */ + float vxbrk[ AST__PLOT_CRV_MXBRK ]; /* X comp. of unit tangent vector */ + float vybrk[ AST__PLOT_CRV_MXBRK ]; /* Y comp. of unit tangent vector */ + float length; /* Drawn length of the curve in graphics coordinates */ +} AstPlotCurveData; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstPlotGlobals { + AstPlotVtab Class_Vtab; + int Class_Init; + double GrfAttrs_attrs_t[ GRF__NATTR ]; + int GrfAttrs_nesting_t; + double Crv_limit_t; + double Crv_scerr_t; + double Crv_tol_t; + double Crv_ux0_t; + double Crv_uy0_t; + double Crv_vxl_t; + double Crv_vyl_t; + double Crv_xhi_t; + double Crv_xl_t; + double Crv_xlo_t; + double Crv_yhi_t; + double Crv_yl_t; + double Crv_ylo_t; + float *Crv_vxbrk_t; + float *Crv_vybrk_t; + float *Crv_xbrk_t; + float *Crv_ybrk_t; + float Crv_len_t; + int Crv_ink_t; + int Crv_nbrk_t; + int Crv_nent_t; + int Crv_out_t; + int Crv_clip_t; + void (*Crv_map_t)( int, double *, double *, double *, const char *, const char *, int *, struct AstGlobals * ); + void *Crv_mapstatics_t; + float Box_lbnd_t[ 2 ]; + float Box_ubnd_t[ 2 ]; + float Boxp_lbnd_t[ 2 ]; + float Boxp_ubnd_t[ 2 ]; + int Boxp_freeze_t; + float *Poly_x_t; + float *Poly_y_t; + int Poly_n_t; + float **Poly_xp_t; + float **Poly_yp_t; + int *Poly_np_t; + int Poly_npoly_t; + int Map1_ncoord_t; + AstPlot *Map1_plot_t; + AstMapping *Map1_map_t; + AstFrame *Map1_frame_t; + const double *Map1_origin_t; + double Map1_length_t; + int Map1_axis_t; + void *Map1_statics_t; + int Map1_norm_t; + int Map1_log_t; + int Map2_ncoord_t; + AstPlot *Map2_plot_t; + AstMapping *Map2_map_t; + double Map2_x0_t; + double Map2_y0_t; + double Map2_deltax_t; + double Map2_deltay_t; + void *Map2_statics_t; + int Map3_ncoord_t; + AstPlot *Map3_plot_t; + AstMapping *Map3_map_t; + AstFrame *Map3_frame_t; + const double *Map3_origin_t; + const double *Map3_end_t; + double Map3_scale_t; + void *Map3_statics_t; + int Map4_ncoord_t; + AstPlot *Map4_plot_t; + AstMapping *Map4_map_t; + AstMapping *Map4_umap_t; + void *Map4_statics_t; + int Map5_ncoord_t; + AstPlot *Map5_plot_t; + AstMapping *Map5_map_t; + AstRegion *Map5_region_t; + void *Map5_statics_t; + AstPlotCurveData Curve_data_t; + char GetAttrib_Buff[ 200 ]; + char SplitValue_Buff[ 200 ]; + char StripEscapes_Buff[ AST__PLOT_STRIPESCAPES_BUFF_LEN + 1 ]; + double Grf_chv_t; + double Grf_chh_t; + float Grf_alpha_t; + float Grf_beta_t; +} AstPlotGlobals; + +#endif +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Plot) /* Check class membership */ +astPROTO_ISA(Plot) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPlot *astPlot_( void *, const float *, const double *, const char *, int *, ...); +#else +AstPlot *astPlotId_( void *, const float [], const double [], const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPlot *astInitPlot_( void *, size_t, int, AstPlotVtab *, + const char *, AstFrame *, const float *, const double *, int * ); + +/* Vtab initialiser. */ +void astInitPlotVtab_( AstPlotVtab *, const char *, int * ); + +/* Loader. */ +AstPlot *astLoadPlot_( void *, size_t, AstPlotVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitPlotGlobals_( AstPlotGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + AstKeyMap *astGetGrfContext_( AstPlot *, int * ); + AstKeyMap *astGrfConID_( AstPlot *, int * ); + const char *astStripEscapes_( const char *, int * ); + int astBorder_( AstPlot *, int * ); + int astFindEscape_( const char *, int *, int *, int *, int * ); + void astBBuf_( AstPlot *, int * ); + void astBoundingBox_( AstPlot *, float[2], float[2], int * ); + void astClip_( AstPlot *, int, const double [], const double [], int * ); + void astCurve_( AstPlot *, const double [], const double [], int * ); + void astEBuf_( AstPlot *, int * ); + void astGenCurve_( AstPlot *, AstMapping *, int * ); + void astGrfPop_( AstPlot *, int * ); + void astGrfPush_( AstPlot *, int * ); + void astGrfSet_( AstPlot *, const char *, AstGrfFun, int * ); + void astGridLine_( AstPlot *, int, const double [], double, int * ); + void astGrid_( AstPlot *, int * ); + void astMark_( AstPlot *, int, int, int, const double *, int, int * ); + void astPolyCurve_( AstPlot *, int, int, int, const double *, int * ); + void astRegionOutline_( AstPlot *, AstRegion *, int * ); + void astText_( AstPlot *, const char *, const double [], const float [], const char *, int * ); + + void astGrfWrapper_( AstPlot *, const char *, AstGrfWrap, int * ); + int astGrfFunID_( const char *, const char *, const char *, int * ); + +#if defined(astCLASS) /* Protected */ + int astCvBrk_( AstPlot *, int, double *, double *, double *, int * ); + void astCopyPlotDefaults_( AstPlot *, int, AstPlot *, int, int * ); + void astMirror_( AstPlot *, int, int * ); + AstPointSet *astGetDrawnTicks_( AstPlot *, int, int, int * ); + void astSetTickValues_( AstPlot *, int, int, double *, int, double *, int * ); + void astDrawExtraTicks_( AstPlot *, int, AstPointSet *, int * ); + void astGrfAttrs_( AstPlot *, int, int, int, const char *, const char *, int * ); + + double astGetTol_( AstPlot *, int * ); + int astTestTol_( AstPlot *, int * ); + void astSetTol_( AstPlot *, double, int * ); + void astClearTol_( AstPlot *, int * ); + + int astGetGrid_( AstPlot *, int * ); + int astTestGrid_( AstPlot *, int * ); + void astSetGrid_( AstPlot *, int, int * ); + void astClearGrid_( AstPlot *, int * ); + + int astGetTickAll_( AstPlot *, int * ); + int astTestTickAll_( AstPlot *, int * ); + void astSetTickAll_( AstPlot *, int, int * ); + void astClearTickAll_( AstPlot *, int * ); + + int astGetForceExterior_( AstPlot *, int * ); + int astTestForceExterior_( AstPlot *, int * ); + void astSetForceExterior_( AstPlot *, int, int * ); + void astClearForceExterior_( AstPlot *, int * ); + + int astGetInvisible_( AstPlot *, int * ); + int astTestInvisible_( AstPlot *, int * ); + void astSetInvisible_( AstPlot *, int, int * ); + void astClearInvisible_( AstPlot *, int * ); + + int astGetBorder_( AstPlot *, int * ); + int astTestBorder_( AstPlot *, int * ); + void astSetBorder_( AstPlot *, int, int * ); + void astClearBorder_( AstPlot *, int * ); + + int astGetClip_( AstPlot *, int * ); + int astTestClip_( AstPlot *, int * ); + void astSetClip_( AstPlot *, int, int * ); + void astClearClip_( AstPlot *, int * ); + + int astGetClipOp_( AstPlot *, int * ); + int astTestClipOp_( AstPlot *, int * ); + void astSetClipOp_( AstPlot *, int, int * ); + void astClearClipOp_( AstPlot *, int * ); + + int astGetGrf_( AstPlot *, int * ); + int astTestGrf_( AstPlot *, int * ); + void astSetGrf_( AstPlot *, int, int * ); + void astClearGrf_( AstPlot *, int * ); + + int astGetDrawTitle_( AstPlot *, int * ); + int astTestDrawTitle_( AstPlot *, int * ); + void astSetDrawTitle_( AstPlot *, int, int * ); + void astClearDrawTitle_( AstPlot *, int * ); + + int astGetLabelUp_( AstPlot *, int, int * ); + int astTestLabelUp_( AstPlot *, int, int * ); + void astSetLabelUp_( AstPlot *, int, int, int * ); + void astClearLabelUp_( AstPlot *, int, int * ); + + int astGetLogPlot_( AstPlot *, int, int * ); + int astTestLogPlot_( AstPlot *, int, int * ); + void astSetLogPlot_( AstPlot *, int, int, int * ); + void astClearLogPlot_( AstPlot *, int, int * ); + + int astGetLogTicks_( AstPlot *, int, int * ); + int astTestLogTicks_( AstPlot *, int, int * ); + void astSetLogTicks_( AstPlot *, int, int, int * ); + void astClearLogTicks_( AstPlot *, int, int * ); + + int astGetLogLabel_( AstPlot *, int, int * ); + int astTestLogLabel_( AstPlot *, int, int * ); + void astSetLogLabel_( AstPlot *, int, int, int * ); + void astClearLogLabel_( AstPlot *, int, int * ); + + int astGetDrawAxes_( AstPlot *, int, int * ); + int astTestDrawAxes_( AstPlot *, int, int * ); + void astSetDrawAxes_( AstPlot *, int, int, int * ); + void astClearDrawAxes_( AstPlot *, int, int * ); + + int astGetAbbrev_( AstPlot *, int, int * ); + int astTestAbbrev_( AstPlot *, int, int * ); + void astSetAbbrev_( AstPlot *, int, int, int * ); + void astClearAbbrev_( AstPlot *, int, int * ); + + int astGetEscape_( AstPlot *, int * ); + int astTestEscape_( AstPlot *, int * ); + void astSetEscape_( AstPlot *, int, int * ); + void astClearEscape_( AstPlot *, int * ); + + double astGetLabelAt_( AstPlot *, int, int * ); + int astTestLabelAt_( AstPlot *, int, int * ); + void astSetLabelAt_( AstPlot *, int, double, int * ); + void astClearLabelAt_( AstPlot *, int, int * ); + + double astGetGap_( AstPlot *, int, int * ); + int astTestGap_( AstPlot *, int, int * ); + void astSetGap_( AstPlot *, int, double, int * ); + void astClearGap_( AstPlot *, int, int * ); + + double astGetLogGap_( AstPlot *, int, int * ); + int astTestLogGap_( AstPlot *, int, int * ); + void astSetLogGap_( AstPlot *, int, double, int * ); + void astClearLogGap_( AstPlot *, int, int * ); + + double astGetCentre_( AstPlot *, int, int * ); + int astTestCentre_( AstPlot *, int, int * ); + void astSetCentre_( AstPlot *, int, double, int * ); + void astClearCentre_( AstPlot *, int, int * ); + + int astGetLabelling_( AstPlot *, int * ); + int astTestLabelling_( AstPlot *, int * ); + void astSetLabelling_( AstPlot *, int, int * ); + void astClearLabelling_( AstPlot *, int * ); + + double astGetMajTickLen_( AstPlot *, int, int * ); + int astTestMajTickLen_( AstPlot *, int, int * ); + void astSetMajTickLen_( AstPlot *, int, double, int * ); + void astClearMajTickLen_( AstPlot *, int, int * ); + + double astGetMinTickLen_( AstPlot *, int, int * ); + int astTestMinTickLen_( AstPlot *, int, int * ); + void astSetMinTickLen_( AstPlot *, int, double, int * ); + void astClearMinTickLen_( AstPlot *, int, int * ); + + double astGetNumLabGap_( AstPlot *, int, int * ); + int astTestNumLabGap_( AstPlot *, int, int * ); + void astSetNumLabGap_( AstPlot *, int, double, int * ); + void astClearNumLabGap_( AstPlot *, int, int * ); + + double astGetTextLabGap_( AstPlot *, int, int * ); + int astTestTextLabGap_( AstPlot *, int, int * ); + void astSetTextLabGap_( AstPlot *, int, double, int * ); + void astClearTextLabGap_( AstPlot *, int, int * ); + + double astGetTitleGap_( AstPlot *, int * ); + int astTestTitleGap_( AstPlot *, int * ); + void astSetTitleGap_( AstPlot *, double, int * ); + void astClearTitleGap_( AstPlot *, int * ); + + int astGetEdge_( AstPlot *, int, int * ); + int astTestEdge_( AstPlot *, int, int * ); + void astSetEdge_( AstPlot *, int, int, int * ); + void astClearEdge_( AstPlot *, int, int * ); + + int astGetMinTick_( AstPlot *, int, int * ); + int astTestMinTick_( AstPlot *, int, int * ); + void astSetMinTick_( AstPlot *, int, int, int * ); + void astClearMinTick_( AstPlot *, int, int * ); + + int astGetNumLab_( AstPlot *, int, int * ); + int astTestNumLab_( AstPlot *, int, int * ); + void astSetNumLab_( AstPlot *, int, int, int * ); + void astClearNumLab_( AstPlot *, int, int * ); + + int astGetTextLab_( AstPlot *, int, int * ); + int astTestTextLab_( AstPlot *, int, int * ); + void astSetTextLab_( AstPlot *, int, int, int * ); + void astClearTextLab_( AstPlot *, int, int * ); + + int astGetLabelUnits_( AstPlot *, int, int * ); + int astTestLabelUnits_( AstPlot *, int, int * ); + void astSetLabelUnits_( AstPlot *, int, int, int * ); + void astClearLabelUnits_( AstPlot *, int, int * ); + + int astGetStyle_( AstPlot *, int, int * ); + int astTestStyle_( AstPlot *, int, int * ); + void astSetStyle_( AstPlot *, int, int, int * ); + void astClearStyle_( AstPlot *, int, int * ); + + int astGetFont_( AstPlot *, int, int * ); + int astTestFont_( AstPlot *, int, int * ); + void astSetFont_( AstPlot *, int, int, int * ); + void astClearFont_( AstPlot *, int, int * ); + + int astGetColour_( AstPlot *, int, int * ); + int astTestColour_( AstPlot *, int, int * ); + void astSetColour_( AstPlot *, int, int, int * ); + void astClearColour_( AstPlot *, int, int * ); + + double astGetWidth_( AstPlot *, int, int * ); + int astTestWidth_( AstPlot *, int, int * ); + void astSetWidth_( AstPlot *, int, double, int * ); + void astClearWidth_( AstPlot *, int, int * ); + + double astGetSize_( AstPlot *, int, int * ); + int astTestSize_( AstPlot *, int, int * ); + void astSetSize_( AstPlot *, int, double, int * ); + void astClearSize_( AstPlot *, int, int * ); + + int astGetInk_( AstPlot *, int * ); + int astTestInk_( AstPlot *, int * ); + void astSetInk_( AstPlot *, int, int * ); + void astClearInk_( AstPlot *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class to make + them easier to invoke (e.g. to avoid type mis-matches when passing pointers + to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them to + validate their own arguments. We must use a cast when passing object + pointers (so that they can accept objects from derived classes). */ + +/* Check class membership. */ +#define astCheckPlot(this) astINVOKE_CHECK(Plot,this,0) +#define astVerifyPlot(this) astINVOKE_CHECK(Plot,this,1) + +/* Test class membership. */ +#define astIsAPlot(this) astINVOKE_ISA(Plot,this) + +#if defined(astCLASS) /* Protected */ +#define astPlot astINVOKE(F,astPlot_) +#else +#define astPlot astINVOKE(F,astPlotId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPlot(mem,size,init,vtab,name,frame,graph,base) \ +astINVOKE(O,astInitPlot_(mem,size,init,vtab,name,frame,graph,base,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPlotVtab(vtab,name) astINVOKE(V,astInitPlotVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadPlot(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPlot_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to member functions. */ +/* ------------------------------- */ +/* Here we make use of astCheckPlot (et al.) to validate Plot + pointers before use. This provides a contextual error report if a + pointer to the wrong sort of object is supplied. */ + + +#define astGetGrfContext(this) \ +astINVOKE(O,astGetGrfContext_(astCheckPlot(this),STATUS_PTR)) + +#define astBorder(this) \ +astINVOKE(V,astBorder_(astCheckPlot(this),STATUS_PTR)) + +#define astBoundingBox(this,lbnd,ubnd) \ +astINVOKE(V,astBoundingBox_(astCheckPlot(this),lbnd,ubnd,STATUS_PTR)) + +#define astClip(this,iframe,lbnd,ubnd) \ +astINVOKE(V,astClip_(astCheckPlot(this),iframe,lbnd,ubnd,STATUS_PTR)) + +#define astMark(this,nmark,ncoord,indim,in,type) \ +astINVOKE(V,astMark_(astCheckPlot(this),nmark,ncoord,indim,in,type,STATUS_PTR)) + +#define astText(this,text,pos,up,just) \ +astINVOKE(V,astText_(astCheckPlot(this),text,pos,up,just,STATUS_PTR)) + +#define astGrid(this) \ +astINVOKE(V,astGrid_(astCheckPlot(this),STATUS_PTR)) + +#define astGridLine(this,axis,start,length) \ +astINVOKE(V,astGridLine_(astCheckPlot(this),axis,start,length,STATUS_PTR)) + +#define astCurve(this,start,finish) \ +astINVOKE(V,astCurve_(astCheckPlot(this),start,finish,STATUS_PTR)) + +#define astGenCurve(this,map) \ +astINVOKE(V,astGenCurve_(astCheckPlot(this),astCheckMapping(map),STATUS_PTR)) + +#define astPolyCurve(this,npoint,ncoord,dim,in) \ +astINVOKE(V,astPolyCurve_(astCheckPlot(this),npoint,ncoord,dim,in,STATUS_PTR)) + +#define astRegionOutline(this,region) \ +astINVOKE(V,astRegionOutline_(astCheckPlot(this),astCheckRegion(region),STATUS_PTR)) + +#define astGrfSet(this,name,fun) \ +astINVOKE(V,astGrfSet_(astCheckPlot(this),name,fun,STATUS_PTR)) + +#define astGrfPush(this) \ +astINVOKE(V,astGrfPush_(astCheckPlot(this),STATUS_PTR)) + +#define astGrfPop(this) \ +astINVOKE(V,astGrfPop_(astCheckPlot(this),STATUS_PTR)) + +#define astBBuf(this) \ +astINVOKE(V,astBBuf_(astCheckPlot(this),STATUS_PTR)) + +#define astEBuf(this) \ +astINVOKE(V,astEBuf_(astCheckPlot(this),STATUS_PTR)) + + +#define astGrfFunID(name,method,class) astGrfFunID_(name,method,class,STATUS_PTR) +#define astFindEscape(text,type,value,nc) astFindEscape_(text,type,value,nc,STATUS_PTR) +#define astStripEscapes(text) astStripEscapes_(text,STATUS_PTR) +#define astGrfConID(this) astGrfConID_(this,STATUS_PTR) + +#define astGrfWrapper(this,name,wrapper) \ +astINVOKE(V,astGrfWrapper_(astCheckPlot(this),name,wrapper,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ + +#define astGrfAttrs(this,id,set,prim,method,class) \ +astGrfAttrs_(astCheckPlot(this),id,set,prim,method,class,STATUS_PTR) + +#define astCvBrk(this,ibrk,brk,vbrk,len) \ +astINVOKE(V,astCvBrk_(astCheckPlot(this),ibrk,brk,vbrk,len,STATUS_PTR)) + +#define astCopyPlotDefaults(this,axis,dplot,daxis) \ +astINVOKE(V,astCopyPlotDefaults_(astCheckPlot(this),axis,astCheckPlot(dplot),daxis,STATUS_PTR)) + +#define astMirror(this,axis) \ +astINVOKE(V,astMirror_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astGetDrawnTicks(this,axis,major) \ +astINVOKE(O,astGetDrawnTicks_(astCheckPlot(this),axis,major,STATUS_PTR)) + +#define astSetTickValues(this,axis,nmajor,major,nminor,minor) \ +astINVOKE(V,astSetTickValues_(astCheckPlot(this),axis,nmajor,major,nminor,minor,STATUS_PTR)) + +#define astDrawExtraTicks(this,axis,pset) \ +astINVOKE(V,astDrawExtraTicks_(astCheckPlot(this),axis,astCheckPointSet(pset),STATUS_PTR)) + +#define astClearTol(this) \ +astINVOKE(V,astClearTol_(astCheckPlot(this),STATUS_PTR)) +#define astGetTol(this) \ +astINVOKE(V,astGetTol_(astCheckPlot(this),STATUS_PTR)) +#define astSetTol(this,tol) \ +astINVOKE(V,astSetTol_(astCheckPlot(this),tol,STATUS_PTR)) +#define astTestTol(this) \ +astINVOKE(V,astTestTol_(astCheckPlot(this),STATUS_PTR)) + +#define astClearGrid(this) \ +astINVOKE(V,astClearGrid_(astCheckPlot(this),STATUS_PTR)) +#define astGetGrid(this) \ +astINVOKE(V,astGetGrid_(astCheckPlot(this),STATUS_PTR)) +#define astSetGrid(this,grid) \ +astINVOKE(V,astSetGrid_(astCheckPlot(this),grid,STATUS_PTR)) +#define astTestGrid(this) \ +astINVOKE(V,astTestGrid_(astCheckPlot(this),STATUS_PTR)) + +#define astClearInk(this) \ +astINVOKE(V,astClearInk_(astCheckPlot(this),STATUS_PTR)) +#define astGetInk(this) \ +astINVOKE(V,astGetInk_(astCheckPlot(this),STATUS_PTR)) +#define astSetInk(this,ink) \ +astINVOKE(V,astSetInk_(astCheckPlot(this),ink,STATUS_PTR)) +#define astTestInk(this) \ +astINVOKE(V,astTestInk_(astCheckPlot(this),STATUS_PTR)) + +#define astClearTickAll(this) \ +astINVOKE(V,astClearTickAll_(astCheckPlot(this),STATUS_PTR)) +#define astGetTickAll(this) \ +astINVOKE(V,astGetTickAll_(astCheckPlot(this),STATUS_PTR)) +#define astSetTickAll(this,tickall) \ +astINVOKE(V,astSetTickAll_(astCheckPlot(this),tickall,STATUS_PTR)) +#define astTestTickAll(this) \ +astINVOKE(V,astTestTickAll_(astCheckPlot(this),STATUS_PTR)) + +#define astClearForceExterior(this) \ +astINVOKE(V,astClearForceExterior_(astCheckPlot(this),STATUS_PTR)) +#define astGetForceExterior(this) \ +astINVOKE(V,astGetForceExterior_(astCheckPlot(this),STATUS_PTR)) +#define astSetForceExterior(this,frcext) \ +astINVOKE(V,astSetForceExterior_(astCheckPlot(this),frcext,STATUS_PTR)) +#define astTestForceExterior(this) \ +astINVOKE(V,astTestForceExterior_(astCheckPlot(this),STATUS_PTR)) + +#define astClearBorder(this) \ +astINVOKE(V,astClearBorder_(astCheckPlot(this),STATUS_PTR)) +#define astGetBorder(this) \ +astINVOKE(V,astGetBorder_(astCheckPlot(this),STATUS_PTR)) +#define astSetBorder(this,border) \ +astINVOKE(V,astSetBorder_(astCheckPlot(this),border,STATUS_PTR)) +#define astTestBorder(this) \ +astINVOKE(V,astTestBorder_(astCheckPlot(this),STATUS_PTR)) + +#define astClearClip(this) \ +astINVOKE(V,astClearClip_(astCheckPlot(this),STATUS_PTR)) +#define astGetClip(this) \ +astINVOKE(V,astGetClip_(astCheckPlot(this),STATUS_PTR)) +#define astSetClip(this,clip) \ +astINVOKE(V,astSetClip_(astCheckPlot(this),clip,STATUS_PTR)) +#define astTestClip(this) \ +astINVOKE(V,astTestClip_(astCheckPlot(this),STATUS_PTR)) + +#define astClearClipOp(this) \ +astINVOKE(V,astClearClipOp_(astCheckPlot(this),STATUS_PTR)) +#define astGetClipOp(this) \ +astINVOKE(V,astGetClipOp_(astCheckPlot(this),STATUS_PTR)) +#define astSetClipOp(this,clipop) \ +astINVOKE(V,astSetClipOp_(astCheckPlot(this),clipop,STATUS_PTR)) +#define astTestClipOp(this) \ +astINVOKE(V,astTestClipOp_(astCheckPlot(this),STATUS_PTR)) + +#define astClearInvisible(this) \ +astINVOKE(V,astClearInvisible_(astCheckPlot(this),STATUS_PTR)) +#define astGetInvisible(this) \ +astINVOKE(V,astGetInvisible_(astCheckPlot(this),STATUS_PTR)) +#define astSetInvisible(this,invisible) \ +astINVOKE(V,astSetInvisible_(astCheckPlot(this),invisible,STATUS_PTR)) +#define astTestInvisible(this) \ +astINVOKE(V,astTestInvisible_(astCheckPlot(this),STATUS_PTR)) + +#define astClearGrf(this) \ +astINVOKE(V,astClearGrf_(astCheckPlot(this),STATUS_PTR)) +#define astGetGrf(this) \ +astINVOKE(V,astGetGrf_(astCheckPlot(this),STATUS_PTR)) +#define astSetGrf(this,grf) \ +astINVOKE(V,astSetGrf_(astCheckPlot(this),grf,STATUS_PTR)) +#define astTestGrf(this) \ +astINVOKE(V,astTestGrf_(astCheckPlot(this),STATUS_PTR)) + +#define astClearDrawTitle(this) \ +astINVOKE(V,astClearDrawTitle_(astCheckPlot(this),STATUS_PTR)) +#define astGetDrawTitle(this) \ +astINVOKE(V,astGetDrawTitle_(astCheckPlot(this),STATUS_PTR)) +#define astSetDrawTitle(this,drawtitle) \ +astINVOKE(V,astSetDrawTitle_(astCheckPlot(this),drawtitle,STATUS_PTR)) +#define astTestDrawTitle(this) \ +astINVOKE(V,astTestDrawTitle_(astCheckPlot(this),STATUS_PTR)) + +#define astClearDrawAxes(this,axis) \ +astINVOKE(V,astClearDrawAxes_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetDrawAxes(this,axis) \ +astINVOKE(V,astGetDrawAxes_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetDrawAxes(this,axis,drawaxes) \ +astINVOKE(V,astSetDrawAxes_(astCheckPlot(this),axis,drawaxes,STATUS_PTR)) +#define astTestDrawAxes(this,axis) \ +astINVOKE(V,astTestDrawAxes_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearAbbrev(this,axis) \ +astINVOKE(V,astClearAbbrev_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetAbbrev(this,axis) \ +astINVOKE(V,astGetAbbrev_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetAbbrev(this,axis,abbrev) \ +astINVOKE(V,astSetAbbrev_(astCheckPlot(this),axis,abbrev,STATUS_PTR)) +#define astTestAbbrev(this,axis) \ +astINVOKE(V,astTestAbbrev_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearEscape(this) \ +astINVOKE(V,astClearEscape_(astCheckPlot(this),STATUS_PTR)) +#define astGetEscape(this) \ +astINVOKE(V,astGetEscape_(astCheckPlot(this),STATUS_PTR)) +#define astSetEscape(this,escape) \ +astINVOKE(V,astSetEscape_(astCheckPlot(this),escape,STATUS_PTR)) +#define astTestEscape(this) \ +astINVOKE(V,astTestEscape_(astCheckPlot(this),STATUS_PTR)) + +#define astClearLabelAt(this,axis) \ +astINVOKE(V,astClearLabelAt_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetLabelAt(this,axis) \ +astINVOKE(V,astGetLabelAt_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetLabelAt(this,axis,labelat) \ +astINVOKE(V,astSetLabelAt_(astCheckPlot(this),axis,labelat,STATUS_PTR)) +#define astTestLabelAt(this,axis) \ +astINVOKE(V,astTestLabelAt_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearGap(this,axis) \ +astINVOKE(V,astClearGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetGap(this,axis) \ +astINVOKE(V,astGetGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetGap(this,axis,gap) \ +astINVOKE(V,astSetGap_(astCheckPlot(this),axis,gap,STATUS_PTR)) +#define astTestGap(this,axis) \ +astINVOKE(V,astTestGap_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearLogGap(this,axis) \ +astINVOKE(V,astClearLogGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetLogGap(this,axis) \ +astINVOKE(V,astGetLogGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetLogGap(this,axis,gap) \ +astINVOKE(V,astSetLogGap_(astCheckPlot(this),axis,gap,STATUS_PTR)) +#define astTestLogGap(this,axis) \ +astINVOKE(V,astTestLogGap_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearCentre(this,axis) \ +astINVOKE(V,astClearCentre_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetCentre(this,axis) \ +astINVOKE(V,astGetCentre_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetCentre(this,axis,centre) \ +astINVOKE(V,astSetCentre_(astCheckPlot(this),axis,centre,STATUS_PTR)) +#define astTestCentre(this,axis) \ +astINVOKE(V,astTestCentre_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearMajTickLen(this,axis) \ +astINVOKE(V,astClearMajTickLen_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetMajTickLen(this,axis) \ +astINVOKE(V,astGetMajTickLen_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetMajTickLen(this,axis,majticklen) \ +astINVOKE(V,astSetMajTickLen_(astCheckPlot(this),axis,majticklen,STATUS_PTR)) +#define astTestMajTickLen(this,axis) \ +astINVOKE(V,astTestMajTickLen_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearMinTickLen(this,axis) \ +astINVOKE(V,astClearMinTickLen_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetMinTickLen(this,axis) \ +astINVOKE(V,astGetMinTickLen_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetMinTickLen(this,axis,minticklen) \ +astINVOKE(V,astSetMinTickLen_(astCheckPlot(this),axis,minticklen,STATUS_PTR)) +#define astTestMinTickLen(this,axis) \ +astINVOKE(V,astTestMinTickLen_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearNumLabGap(this,axis) \ +astINVOKE(V,astClearNumLabGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetNumLabGap(this,axis) \ +astINVOKE(V,astGetNumLabGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetNumLabGap(this,axis,numlabgap) \ +astINVOKE(V,astSetNumLabGap_(astCheckPlot(this),axis,numlabgap,STATUS_PTR)) +#define astTestNumLabGap(this,axis) \ +astINVOKE(V,astTestNumLabGap_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearTextLabGap(this,axis) \ +astINVOKE(V,astClearTextLabGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetTextLabGap(this,axis) \ +astINVOKE(V,astGetTextLabGap_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetTextLabGap(this,axis,textlabgap) \ +astINVOKE(V,astSetTextLabGap_(astCheckPlot(this),axis,textlabgap,STATUS_PTR)) +#define astTestTextLabGap(this,axis) \ +astINVOKE(V,astTestTextLabGap_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearTitleGap(this) \ +astINVOKE(V,astClearTitleGap_(astCheckPlot(this),STATUS_PTR)) +#define astGetTitleGap(this) \ +astINVOKE(V,astGetTitleGap_(astCheckPlot(this),STATUS_PTR)) +#define astSetTitleGap(this,titlegap) \ +astINVOKE(V,astSetTitleGap_(astCheckPlot(this),titlegap,STATUS_PTR)) +#define astTestTitleGap(this) \ +astINVOKE(V,astTestTitleGap_(astCheckPlot(this),STATUS_PTR)) + +#define astClearLabelling(this) \ +astINVOKE(V,astClearLabelling_(astCheckPlot(this),STATUS_PTR)) +#define astGetLabelling(this) \ +astINVOKE(V,astGetLabelling_(astCheckPlot(this),STATUS_PTR)) +#define astSetLabelling(this,labelling) \ +astINVOKE(V,astSetLabelling_(astCheckPlot(this),labelling,STATUS_PTR)) +#define astTestLabelling(this) \ +astINVOKE(V,astTestLabelling_(astCheckPlot(this),STATUS_PTR)) + +#define astClearEdge(this,axis) \ +astINVOKE(V,astClearEdge_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetEdge(this,axis) \ +astINVOKE(V,astGetEdge_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetEdge(this,axis,edge) \ +astINVOKE(V,astSetEdge_(astCheckPlot(this),axis,edge,STATUS_PTR)) +#define astTestEdge(this,axis) \ +astINVOKE(V,astTestEdge_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearMinTick(this,axis) \ +astINVOKE(V,astClearMinTick_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetMinTick(this,axis) \ +astINVOKE(V,astGetMinTick_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetMinTick(this,axis,mintick) \ +astINVOKE(V,astSetMinTick_(astCheckPlot(this),axis,mintick,STATUS_PTR)) +#define astTestMinTick(this,axis) \ +astINVOKE(V,astTestMinTick_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearNumLab(this,axis) \ +astINVOKE(V,astClearNumLab_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetNumLab(this,axis) \ +astINVOKE(V,astGetNumLab_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetNumLab(this,axis,numlab) \ +astINVOKE(V,astSetNumLab_(astCheckPlot(this),axis,numlab,STATUS_PTR)) +#define astTestNumLab(this,axis) \ +astINVOKE(V,astTestNumLab_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearLabelUp(this,axis) \ +astINVOKE(V,astClearLabelUp_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetLabelUp(this,axis) \ +astINVOKE(V,astGetLabelUp_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetLabelUp(this,axis,labelup) \ +astINVOKE(V,astSetLabelUp_(astCheckPlot(this),axis,labelup,STATUS_PTR)) +#define astTestLabelUp(this,axis) \ +astINVOKE(V,astTestLabelUp_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearLogPlot(this,axis) \ +astINVOKE(V,astClearLogPlot_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetLogPlot(this,axis) \ +astINVOKE(V,astGetLogPlot_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetLogPlot(this,axis,logplot) \ +astINVOKE(V,astSetLogPlot_(astCheckPlot(this),axis,logplot,STATUS_PTR)) +#define astTestLogPlot(this,axis) \ +astINVOKE(V,astTestLogPlot_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearLogTicks(this,axis) \ +astINVOKE(V,astClearLogTicks_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetLogTicks(this,axis) \ +astINVOKE(V,astGetLogTicks_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetLogTicks(this,axis,logticks) \ +astINVOKE(V,astSetLogTicks_(astCheckPlot(this),axis,logticks,STATUS_PTR)) +#define astTestLogTicks(this,axis) \ +astINVOKE(V,astTestLogTicks_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearLogLabel(this,axis) \ +astINVOKE(V,astClearLogLabel_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetLogLabel(this,axis) \ +astINVOKE(V,astGetLogLabel_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetLogLabel(this,axis,loglabel) \ +astINVOKE(V,astSetLogLabel_(astCheckPlot(this),axis,loglabel,STATUS_PTR)) +#define astTestLogLabel(this,axis) \ +astINVOKE(V,astTestLogLabel_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearTextLab(this,axis) \ +astINVOKE(V,astClearTextLab_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetTextLab(this,axis) \ +astINVOKE(V,astGetTextLab_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetTextLab(this,axis,textlab) \ +astINVOKE(V,astSetTextLab_(astCheckPlot(this),axis,textlab,STATUS_PTR)) +#define astTestTextLab(this,axis) \ +astINVOKE(V,astTestTextLab_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearLabelUnits(this,axis) \ +astINVOKE(V,astClearLabelUnits_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetLabelUnits(this,axis) \ +astINVOKE(V,astGetLabelUnits_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetLabelUnits(this,axis,labelunits) \ +astINVOKE(V,astSetLabelUnits_(astCheckPlot(this),axis,labelunits,STATUS_PTR)) +#define astTestLabelUnits(this,axis) \ +astINVOKE(V,astTestLabelUnits_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearStyle(this,axis) \ +astINVOKE(V,astClearStyle_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetStyle(this,axis) \ +astINVOKE(V,astGetStyle_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetStyle(this,axis,style) \ +astINVOKE(V,astSetStyle_(astCheckPlot(this),axis,style,STATUS_PTR)) +#define astTestStyle(this,axis) \ +astINVOKE(V,astTestStyle_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearFont(this,axis) \ +astINVOKE(V,astClearFont_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetFont(this,axis) \ +astINVOKE(V,astGetFont_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetFont(this,axis,font) \ +astINVOKE(V,astSetFont_(astCheckPlot(this),axis,font,STATUS_PTR)) +#define astTestFont(this,axis) \ +astINVOKE(V,astTestFont_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearColour(this,axis) \ +astINVOKE(V,astClearColour_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetColour(this,axis) \ +astINVOKE(V,astGetColour_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetColour(this,axis,colour) \ +astINVOKE(V,astSetColour_(astCheckPlot(this),axis,colour,STATUS_PTR)) +#define astTestColour(this,axis) \ +astINVOKE(V,astTestColour_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearWidth(this,axis) \ +astINVOKE(V,astClearWidth_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetWidth(this,axis) \ +astINVOKE(V,astGetWidth_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetWidth(this,axis,width) \ +astINVOKE(V,astSetWidth_(astCheckPlot(this),axis,width,STATUS_PTR)) +#define astTestWidth(this,axis) \ +astINVOKE(V,astTestWidth_(astCheckPlot(this),axis,STATUS_PTR)) + +#define astClearSize(this,axis) \ +astINVOKE(V,astClearSize_(astCheckPlot(this),axis,STATUS_PTR)) +#define astGetSize(this,axis) \ +astINVOKE(V,astGetSize_(astCheckPlot(this),axis,STATUS_PTR)) +#define astSetSize(this,axis,size) \ +astINVOKE(V,astSetSize_(astCheckPlot(this),axis,size,STATUS_PTR)) +#define astTestSize(this,axis) \ +astINVOKE(V,astTestSize_(astCheckPlot(this),axis,STATUS_PTR)) +#endif +#endif + + + + + diff --git a/plot3d.c b/plot3d.c new file mode 100644 index 0000000..ea045bd --- /dev/null +++ b/plot3d.c @@ -0,0 +1,8587 @@ +/* +*class++ +* Name: +* Plot3D + +* Purpose: +* Provide facilities for 3D graphical output. + +* Constructor Function: +c astPlot3D +f AST_PLOT3D + +* Description: +* A Plot3D is a specialised form of Plot that provides facilities +* for producing 3D graphical output, including fully annotated 3D +* coordinate grids. The base Frame in a Plot3D describes a 3-dimensional +* "graphical" coordinate system. The axes of this coordinate system are +* assumed to be right-handed (that is, if X appears horizontally to the +* right and Y vertically upwards, then Z is out of the screen towards +* the viewer), and are assumed to be equally scaled (that is, the same +* units are used to measure positions on each of the 3 axes). The upper +* and lower bounds of a volume within this graphical coordinate system +* is specified when the Plot3D is created, and all subsequent graphics +* are "drawn" in this volume. +* +* The Plot3D class does not itself include any ability to draw on a +* graphics device. Instead it calls upon function in an externally +* supplied module (the "grf3d" module) to do the required drawing. +* A module should be written that implements the functions of the +* grf3d interface using the facilities of a specific graphics system +* This module should then be linked into the application so that the +* Plot3D class can use its functions (see the description of the +* ast_link commands for details of how to do this). The grf3d interface +* defines a few simple functions for drawing primitives such as straight +* lines, markers and character strings. These functions all accept +* positions in the 3D graphics coordinate system (the base Frame of the +* Plot3D), and so the grf3d module must also manage the projection of +* these 3D coordinates onto the 2D viewing surface, including the choice +* of "eye"/"camera" position, direction of viewing, etc. The AST +* library includes a sample implementation of the grf3d interface +* based on the PGPLOT graphics system (see file grf3d_pgplot.c). This +* implementation also serves to document the grf3d interface itself and +* should be consulted for details before writing a new implementation. +* +* The current Frame of a Plot3D describes a "physical" 3-dimensional +* coordinate system, which is the coordinate system in which plotting +* operations are specified when invoking the methods of the Plot3D +* class. The results of each plotting operation are automatically +* transformed into 3D graphical coordinates before being plotted +* using the facilities of the grf3d module linked into the application. +* Note, at least one of the three axes of the current Frame must be +* independent of the other two current Frame axes. +* +* You may select different physical coordinate systems in which to +* plot (including the native graphical coordinate system itself) +* by selecting different Frames as the current Frame of a Plot3D, +* using its Current attribute. +* +* Like any FrameSet, a Plot3D may also be used as a Frame. In this +* case, it behaves like its current Frame, which describes the +* physical coordinate system. +* +* When used as a Mapping, a Plot3D describes the inter-relation +* between 3D graphical coordinates (its base Frame) and 3D physical +* coordinates (its current Frame). +* +* Although the Plot3D class inherits from the Plot class, several of +* the facilities of the Plot class are not available in the Plot3D +* class, and an error will be reported if any attempt is made to use +* them. Specifically, the Plot3D class does not support clipping +* using the +* astClip function. +f AST_CLIP routine. +* Nor does it support the specification of graphics primitive functions +* at run-time using the +c astGrfSet, astGrfPop, astGrfPush and astGetGrfContext functions. +f AST_GRFSET, AST_GRFPOP, AST_GRFPUSH, and AST_GETGRFCONTEXT routines. + +* Inheritance: +* The Plot3D class inherits from the Plot class. + +* Attributes: +* In addition to those attributes common to all Plots, every +* Plot3D also has the following attributes: +* +* - Norm: Normal vector defining the 2D plane used for text and markers +* - RootCorner: Specifies which edges of the 3D box should be annotated. +* +* Some attributes of the Plot class refer to specific physical +* coordinate axes (e.g. Gap, LabelUp, DrawAxes, etc). For a basic +* Plot, the axis index must be 1 or 2, but for a Plot3D the axis index +* can be 1, 2 or 3. +* +* Certain Plot attributes are ignored by the Plot3D class (e.g. Edge, +* DrawTitle, TitleGap, etc). Consult the Plot attribute documentation +* for details. All other Plot attributes can be set for a specific +* plane of the 3-d plot by appending one of the strings "_XY", "_XZ" +* or "_YZ" to the end of the Plot attribute name. For instance, +* "Grid_YZ" refers to the "Grid" attribute for the plane spanning +* the second (Y) and third (Z) axes of the 3-d plot. + + +* Functions: +c The Plot3D class does not define any new functions beyond those +f The Plot3D class does not define any new routines beyond those +* which are applicable to all Plots. Note, however, that the +* following methods inherited from the Plot class cannot be used with +* a Plot3D and will report an error if called: +c - astBoundingBox, astClip, astCurve, astGenCurve, +c astGetGrfContext, astGrfPop, astGrfPush, astGrfSet, astGridLine, +c astPolyCurve. +f - AST_BOUNDINGBOX, AST_CLIP, AST_CURVE, AST_GENCURVE, +f AST_GETGRFCONTEXT, AST_GRFPOP, AST_GRFPUSH, AST_GRFSET, +f AST_GRIDLINE, AST_POLYCURVE. + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 6-JUN-2007 (DSB): +* Original version. +* 6-SEP-2007 (DSB): +* Re-code the astGrid function. +* 12-NOV-2007 (DSB): +* Clear up compiler warnings. +* 4-MAY-2012 (DSB): +* Avoid segvio in Grid if no ticks are drawn. +* 21-MAY-2012 (DSB): +* In astLoadPlot3D, do not call SetRootCorner as it requires an +* active graphics system to be present which may not yet have been +* established. Also establish the grf routines to be used. +* 5-OCT-2015 (DSB): +* Allow Plot attributes to be set for specific planes. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Plot3D + +/* Integers identifying the 3 plotting surfaces */ +#define XY 1 +#define XZ 2 +#define YZ 3 + +/* Integers identifying the 8 corners of the graphics cube. */ +#define LLL 0 +#define ULL 1 +#define LUL 2 +#define UUL 3 +#define LLU 4 +#define ULU 5 +#define LUU 6 +#define UUU 7 + +/* Identify the 4 edges of a Plot */ +#define LEFT 0 +#define TOP 1 +#define RIGHT 2 +#define BOTTOM 3 + +/* A macros that returns a pointer to the Plot that spans a given plane. */ +#define GET_PLOT(plane) ( \ + ( plane == XY ) ? this->plotxy : ( \ + ( plane == XZ ) ? this->plotxz : ( \ + ( plane == YZ ) ? this->plotyz : NULL ) ) ) + + +/* +* +* Name: +* MAKE_CLEAR3 + +* Purpose: +* Implement a method to clear a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_CLEAR3(attr,component,assign,nval) + +* Class Membership: +* Defined by the Plot3D class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstPlot *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear_( AstPlot *this, int axis ) +* +* which implement a method for clearing a single value in a specified +* multi-valued attribute for an axis of a Plot3D. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. LabelAt in "astClearLabelAt"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR3(attr,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstPlot3D *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astClear" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the "clear" value. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attr##_( AstPlot3D *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,Plot3D,Clear##attr))( this, axis, status ); \ +} + + +/* +* +* Name: +* MAKE_GET3 + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_GET3(attr,type,bad_value,assign,nval) + +* Class Membership: +* Defined by the Plot3D class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstPlot3D *this, int axis ) +* +* and an external interface function of the form: +* +* astGet_( AstPlot3D *this, int axis ) +* +* which implement a method for getting a single value from a specified +* multi-valued attribute for an axis of a Plot3D. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET3(attr,type,bad_value,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstPlot3D *this, int axis, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Initialise */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astGet" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attr##_( AstPlot3D *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,Plot3D,Get##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_SET3 + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute +* for a Plot3D. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_SET3(attr,type,component,assign,nval) + +* Class Membership: +* Defined by the Plot3D class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPlot3D *this, int axis, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstPlot3D *this, int axis, value ) +* +* which implement a method for setting a single value in a specified +* multi-valued attribute for a Plot3D. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. LabelAt in "astSetLabelAt"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). If a value of 0 is supplied, the +* value of the Plot3D's Nin attribute is used instead. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET3(attr,type,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstPlot3D *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astSet" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Store the new value in the structure component. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attr##_( AstPlot3D *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,Plot3D,Set##attr))( this, axis, value, status ); \ +} + +/* +* +* Name: +* MAKE_TEST3 + +* Purpose: +* Implement a method to test if a single value has been set in a +* multi-valued attribute for a class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_TEST3(attr,assign,nval) + +* Class Membership: +* Defined by the Plot3D class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( AstPlot3D *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest_( AstPlot3D *this, int axis ) +* +* which implement a method for testing if a single value in a specified +* multi-valued attribute has been set for a class. + +* Parameters: +* attr +* The name of the attribute to be tested, as it appears in the function +* name (e.g. LabelAt in "astTestLabelAt"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. This can use the string "axis" to represent the zero-based +* index of the value within the attribute. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_TEST3(attr,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attr( AstPlot3D *this, int axis, int *status ) { \ + int result; /* Value to return */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astTest" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attr##_( AstPlot3D *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,Plot3D,Test##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_CLEAR2 + +* Purpose: +* Implement a method to clear an element-specific attribute inherited +* from the Plot class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_CLEAR2(attr) + +* Class Membership: +* Defined by the Plot3d class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstPlot *this, int element ) +* +* which implements a method for clearing one of the element-specific +* attributes (e.g. Size, Colour, Width, etc) inherited from the +* parent Plot class. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. LabelAt in "astClearSize"). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR2(attr) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstPlot *this_plot, int element, int *status ) { \ +\ +/* Local Variables: */ \ + AstPlot3D *this; \ + int axis3d; \ + int elem2d1; \ + int elem2d2; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Clear the attribute value in the parent Plot structure. */ \ + (*parent_clear##attr)( this_plot, element, status ); \ +\ +/* If OK, clear the attribute in the encapsulated Plots. */ \ + if( astOK ) { \ + this = (AstPlot3D *) this_plot; \ +\ +/* Get the zero-based index of the 3D axis to which the supplied element \ + refers. Use an index of -1 to indicate that the element does not \ + relate to a specific axis. Also get the corresponding elements to use \ + with the two Plots that share the specified 3D axis. */ \ + axis3d = Element2D( this, element, &elem2d1, &elem2d2, status ); \ +\ +/* If the element is not axis-specific, clear the attribute value in all \ + three plots. */ \ + if( axis3d == -1 ) { \ + astClear##attr( this->plotxy, element ); \ + astClear##attr( this->plotxz, element ); \ + astClear##attr( this->plotyz, element ); \ +\ +/* Otherwise, clear the attribute in the two plots that share the \ + specified 3D axis. */ \ + } else { \ + astClear##attr( GET_PLOT(this->axis_plot1[ axis3d ]), elem2d1 ); \ + astClear##attr( GET_PLOT(this->axis_plot2[ axis3d ]), elem2d2 ); \ + } \ + } \ +} + +/* +* +* Name: +* MAKE_SET2 + +* Purpose: +* Implement a method to set an element-specific attribute inherited +* from the Plot class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_SET2(attr,type) + +* Class Membership: +* Defined by the Plot3d class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPlot *this, int element, type value ) +* +* which implements a method for setting one of the element-specific +* attributes (e.g. Size, Colour, Width, etc) inherited from the +* parent Plot class. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. LabelAt in "astClearSize"). +* type +* The attribute data type. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_SET2(attr,type) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstPlot *this_plot, int element, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstPlot3D *this; \ + int axis3d; \ + int elem2d1; \ + int elem2d2; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Set the attribute value in the parent Plot structure. */ \ + (*parent_set##attr)( this_plot, element, value, status ); \ +\ +/* If OK, set the attribute in the encapsulated Plots. */ \ + if( astOK ) { \ + this = (AstPlot3D *) this_plot; \ +\ +/* Get the zero-based index of the 3D axis to which the supplied element \ + refers. Use an index of -1 to indicate that the element does not \ + relate to a specific axis. Also get the corresponding elements to use \ + with the two Plots that share the specified 3D axis. */ \ + axis3d = Element2D( this, element, &elem2d1, &elem2d2, status ); \ +\ +/* If the element is not axis-specific, clear the attribute value in all \ + three plots. */ \ + if( axis3d == -1 ) { \ + astSet##attr( this->plotxy, element, value ); \ + astSet##attr( this->plotxz, element, value ); \ + astSet##attr( this->plotyz, element, value ); \ +\ +/* Otherwise, clear the attribute in the two plots that share the \ + specified 3D axis. */ \ + } else { \ + astSet##attr( GET_PLOT(this->axis_plot1[ axis3d ]), elem2d1, value ); \ + astSet##attr( GET_PLOT(this->axis_plot2[ axis3d ]), elem2d2, value ); \ + } \ + } \ +} + + +/* +* +* Name: +* MAKE_CLEAR1 + +* Purpose: +* Implement a method to clear an attribute inherited from the Plot class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_CLEAR1(attr) + +* Class Membership: +* Defined by the Plot3d class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstPlot *this ) +* +* which implements a method for clearing a Plot3D attribute inherited +* from the parent Plot class. It clears the attribute in all three +* plots encapsulated within the Plot3D. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. LabelAt in "astClearLabelAt"). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR1(attr) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstPlot *this_plot, int *status ) { \ +\ +/* Local Variables: */ \ + AstPlot3D *this; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Clear the attribute value in the parent Plot structure. */ \ + (*parent_clear##attr)( this_plot, status ); \ +\ +/* If OK, clear the attribute in all three of the encapsulated Plots. */ \ + if( astOK ) { \ + this = (AstPlot3D *) this_plot; \ + astClear##attr( this->plotxy ); \ + astClear##attr( this->plotxz ); \ + astClear##attr( this->plotyz ); \ + } \ +} + +/* +* +* Name: +* MAKE_SET1 + +* Purpose: +* Implement a method to set an attribute inherited from the Plot class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_SET1(attr,type) + +* Class Membership: +* Defined by the Plot3d class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPlot *this, type value ) +* +* which implements a method for setting a Plot3D attribute inherited +* from the parent Plot class. It sets the attribute in all three +* plots encapsulated within the Plot3D. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. LabelAt in "astSetLabelAt"). +* type +* The C data type for the attribute value. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_SET1(attr,type) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstPlot *this_plot, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstPlot3D *this; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Set the attribute value in the parent Plot structure. */ \ + (*parent_set##attr)( this_plot, value, status ); \ +\ +/* If OK, set the attribute in all three of the encapsulated Plots. */ \ + if( astOK ) { \ + this = (AstPlot3D *) this_plot; \ + astSet##attr( this->plotxy, value ); \ + astSet##attr( this->plotxz, value ); \ + astSet##attr( this->plotyz, value ); \ + } \ +} + +/* +* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear an attribute inherited from the Plot class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_CLEAR(attr,whichplots) + +* Class Membership: +* Defined by the Plot3d class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstPlot *this, int axis ) +* +* which implements a method for clearing an axis specific Plot3D +* attribute inherited from the parent Plot class. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. LabelAt in "astClearLabelAt"). +* whichplots +* A value indicating which Plots should be affected. A negative value +* means "all threee plots", a value of zero means "just the two plots +* that touch at the specified axis in 3D space", a positive value +* means "just the Plot that is used to label th 3D axis." + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attr,whichplots) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstPlot *this_plot, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstPlot *plot; \ + AstPlot3D *this; \ + int axis2d; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Clear the attribute value in the parent Plot structure. This will \ + validate the axis index. */ \ + (*parent_clear##attr)( this_plot, axis, status ); \ +\ +/* If OK, clear the attribute for the relevant axis, or axes, of the Plots \ + encapsulated inside the Plot3D. First get a pointer to the Plot3D \ + structure. */ \ + if( astOK ) { \ + this = (AstPlot3D *) this_plot; \ +\ +/* If requested clear the attribute in all three Plots. */ \ + if( whichplots < 0 ) { \ + astClear##attr( this->plotxy, axis ); \ + astClear##attr( this->plotxz, axis ); \ + astClear##attr( this->plotyz, axis ); \ +\ +/* Each axis in 3D graphics space is described by two of the encapsulated \ + Plots, but only one of these two Plots is used to generate labels for \ + the axis. Now deal with cases where we are clearing the attribute \ + value in both of the two Plots that describe the axis. */ \ + } else if ( whichplots == 0 ) { \ + if( axis == 0 ) { \ + astClear##attr( this->plotxy, 0 ); \ + astClear##attr( this->plotxz, 0 ); \ +\ + } else if( axis == 1 ) { \ + astClear##attr( this->plotxy, 1 ); \ + astClear##attr( this->plotyz, 0 ); \ +\ + } else { \ + astClear##attr( this->plotxz, 1 ); \ + astClear##attr( this->plotyz, 1 ); \ + } \ +\ +/* Now deal with cases where we are clearing the attribute value only in \ + the Plot that is used to label the axis. */ \ + } else { \ + plot = AxisPlot( this, axis, &axis2d, status ); \ + astClear##attr( plot, axis2d ); \ + } \ + } \ +} + + +/* +* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get the value of an attribute inherited from the +* Plot class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot.h" +* MAKE_GET(attr,type,bad_value) + +* Class Membership: +* Defined by the Plot3D class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstPlot *this, int axis ) +* +* which implements a method for getting a value for an axis specific +* attribute for a Plot3D. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET(attr,type,bad_value) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstPlot *this_plot, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstPlot *plot; \ + AstPlot3D *this; \ + int axis2d; \ + type result; \ +\ +/* Initialise */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* See if the attribute value is set in the parent Plot structure. If so, \ + use the parent get method to get its value. */ \ + if( astTest##attr( this_plot, axis ) ) { \ + result = (*parent_get##attr)( this_plot, axis, status ); \ +\ +/* If the attribute value is not set in the parent Plot structure, get \ + the default value from the Plot that is used to label the 3D axis. The \ + parent test method called above will have reported an error if the axis \ + index is invalid, so check astOK here. */ \ + } else if( astOK ) { \ + this = (AstPlot3D *) this_plot; \ + plot = AxisPlot( this, axis, &axis2d, status ); \ + result = astGet##attr( plot, axis2d ); \ + } \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set a value for an attribute inherited from the +* Plot class. + +* Type: +* Private macro. + +* Synopsis: +* #include "plot3d.h" +* MAKE_SET(attr,type,whichplots) + +* Class Membership: +* Defined by the Plot3d class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPlot *this, int axis, value ) +* +* which implements a method for setting a value for an axis specific +* attribute inherited from the parent Plot class. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. LabelAt in "astSetLabelAt"). +* type +* The C type of the attribute. +* whichplots +* A value indicating which Plots should be affected. A negative value +* means "all threee plots", a value of zero means "just the two plots +* that touch at the specified axis in 3D space", a positive value +* means "just the Plot that is used to label the 3D axis." + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET(attr,type,whichplots) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstPlot *this_plot, int axis, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstPlot3D *this; \ + AstPlot *plot; \ + int axis2d; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Set the supplied value in the parent Plot class. This will validate \ + the axis index. */ \ + (*parent_set##attr)( this_plot, axis, value, status ); \ +\ +/* If this went OK, also set the value for the appropriate axis of the \ + appropriate encapsulated Plot(s). First get a pointer to the Plot3D \ + structure. */ \ + if( astOK ) { \ + this = (AstPlot3D *) this_plot; \ +\ +/* If requested set the attribute in all three Plots. */ \ + if( whichplots < 0 ) { \ + astSet##attr( this->plotxy, axis, value ); \ + astSet##attr( this->plotxz, axis, value ); \ + astSet##attr( this->plotyz, axis, value ); \ +\ +/* Each axis in 3D graphics space is described by two of the encapsulated \ + Plots, but only one of these two Plots is used to generate labels for \ + the axis. First deal with cases where we are setting the attribute \ + value in both of the two Plots that describe the axis. */ \ + } else if( whichplots == 0 ) { \ + if( axis == 0 ) { \ + astSet##attr( this->plotxy, 0, value ); \ + astSet##attr( this->plotxz, 0, value ); \ +\ + } else if( axis == 1 ) { \ + astSet##attr( this->plotxy, 1, value ); \ + astSet##attr( this->plotyz, 0, value ); \ +\ + } else { \ + astSet##attr( this->plotxz, 1, value ); \ + astSet##attr( this->plotyz, 1, value ); \ + } \ +\ +/* Now deal with cases where we are setting the attribute value only in \ + the Plot that is used to label the axis. */ \ + } else { \ + plot = AxisPlot( this, axis, &axis2d, status ); \ + astSet##attr( plot, axis2d, value ); \ + } \ + } \ +} + + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "cmpframe.h" /* Compound Frames */ +#include "cmpmap.h" /* Compound Mappings */ +#include "unitmap.h" /* Unit mappings */ +#include "permmap.h" /* Axis permutations */ +#include "winmap.h" /* Scale and shift mappings */ +#include "frame.h" /* Coordinate systems */ +#include "frameset.h" /* Inter-related coordinate systems */ +#include "keymap.h" /* Hash array */ +#include "plot.h" /* Interface definition for parent class */ +#include "plot3d.h" /* Interface definition for this class */ +#include "grf3d.h" /* The grf3D interface */ +#include "pointset.h" /* Sets of points */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are used or extended by this + class. */ +static AstObject *(* parent_cast)( AstObject *, AstObject *, int * ); +static void (* parent_removeframe)( AstFrameSet *, int, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static void (* parent_vset)( AstObject *, const char *, char **, va_list, int * ); +static void (* parent_clear)( AstObject *, const char *, int * ); +static void (* parent_clearcurrent)( AstFrameSet *, int * ); +static void (* parent_setcurrent)( AstFrameSet *, int, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* A FrameSet pointer that is used when calling astCast. */ +static AstFrameSet *dummy_frameset = NULL; + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Plot3D) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Plot3D,Class_Init) +#define class_vtab astGLOBAL(Plot3D,Class_Vtab) +#define getattrib_buff astGLOBAL(Plot3D,GetAttrib_Buff) + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 ); +#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPlot3DVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#define LOCK_MUTEX3 +#define UNLOCK_MUTEX3 + +#endif + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstFrameSet *Fset3D( AstFrameSet *, int, int * ); +static AstKeyMap *GetGrfContext( AstPlot *, int * ); +static AstObject *Cast( AstObject *, AstObject *, int * ); +static AstPlot *AxisPlot( AstPlot3D *, int, int *, int * ); +static AstPointSet *ExtendTicks( AstPlot *, AstPointSet *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *RootCornerString( int, int * ); +static int Attr3D( AstKeyMap *, int, double, double *, int, int * ); +static int Border( AstPlot *, int * ); +static int Element2D( AstPlot3D *, int, int *, int *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); +static int Plot3DAttr( AstKeyMap *, int, double, double *, int ); +static int Plot3DCap( AstKeyMap *, int, int ); +static int Plot3DFlush( AstKeyMap * ); +static int Plot3DLine( AstKeyMap *, int, const float *, const float * ); +static int Plot3DMark( AstKeyMap *, int, const float *, const float *, int ); +static int Plot3DQch( AstKeyMap *, float *, float * ); +static int Plot3DScales( AstKeyMap *, float *, float * ); +static int Plot3DText( AstKeyMap *, const char *, float, float, const char *, float, float ); +static int Plot3DTxExt( AstKeyMap *, const char *, float, float, const char *, float, float, float *, float * ); +static int RootCornerInt( const char *, int * ); +static void BoundingBox( AstPlot *, float[2], float[2], int * ); +static void ChangeRootCorner( AstPlot3D *, int, int, int * ); +static void Clear( AstObject *, const char *, int * ); +static void ClearCurrent( AstFrameSet *, int * ); +static void Clip( AstPlot *, int, const double [], const double [], int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void CreatePlots( AstPlot3D *, AstFrameSet *, const float *, const double *, int * ); +static void Curve( AstPlot *, const double [], const double [], int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GenCurve( AstPlot *, AstMapping *, int * ); +static void GrfPop( AstPlot *, int * ); +static void GrfPush( AstPlot *, int * ); +static void GrfSet( AstPlot *, const char *, AstGrfFun, int * ); +static void Grid( AstPlot *, int * ); +static void GridLine( AstPlot *, int, const double [], double, int * ); +static void Mark( AstPlot *, int, int, int, const double *, int, int * ); +static void PolyCurve( AstPlot *, int, int, int, const double *, int * ); +static void RemoveFrame( AstFrameSet *, int, int * ); +static void Set3DGrf( AstPlot3D *, AstPlot *, int, int * ); +static void SetCurrent( AstFrameSet *, int, int * ); +static void SetPlotAttr( AstPlot *, int, int[ 2 ], int * ); +static void SetTickValues( AstPlot *, int, int, double *, int, double *, int * ); +static void SplitFrameSet( AstFrameSet *, AstFrameSet **, int[2], int[2], AstFrameSet **, int[2], int[2], AstFrameSet **, int[2], int[2], int *, int * ); +static void StoreAxisInfo( AstPlot3D *, int[2], int[2], int[2], int[2], int[2], int[2], int * ); +static void Text( AstPlot *, const char *, const double [], const float [2], const char *, int * ); +static void UpdatePlots( AstPlot3D *, int * ); +static void VSet( AstObject *, const char *, char **, va_list, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Declare private member functions that access Plot3D attributes. + --------------------------------------------------------------*/ + +/* Axis independent... */ + +#define DECLARE_PLOT3D_ACCESSORS(attr,type) \ + static type Get##attr(AstPlot3D *,int *); \ + static void Set##attr(AstPlot3D *,type,int *); \ + static void Clear##attr(AstPlot3D *,int *); \ + static int Test##attr(AstPlot3D *,int *); + +DECLARE_PLOT3D_ACCESSORS(RootCorner,int) + +#undef DECLARE_PLOT3D_ACCESSORS + + +/* Axis specific... */ + +#define DECLARE_PLOT3D_ACCESSORS(attr,type) \ + static type Get##attr(AstPlot3D *,int,int *); \ + static void Set##attr(AstPlot3D *,int,type,int *); \ + static void Clear##attr(AstPlot3D *,int,int *); \ + static int Test##attr(AstPlot3D *,int,int *); + +DECLARE_PLOT3D_ACCESSORS(Norm,double) + +#undef DECLARE_PLOT3D_ACCESSORS + + +/* Declare private member functions that access axis-specific attributes + inherited from the Plot class. Also declare pointers to hold the parent + function pointers. + ----------------------------------------------------------------------*/ + +#define DECLARE_PLOT_ACCESSORS(attr,type) \ + static type Get##attr(AstPlot *,int,int *); \ + static void Set##attr(AstPlot *,int,type,int *); \ + static void Clear##attr(AstPlot *,int,int *); \ + static type (*parent_get##attr)(AstPlot *,int,int *); \ + static void (*parent_set##attr)(AstPlot *,int,type,int *); \ + static void (*parent_clear##attr)(AstPlot *,int,int *); + +DECLARE_PLOT_ACCESSORS(MinTick,int) +DECLARE_PLOT_ACCESSORS(Abbrev,int) +DECLARE_PLOT_ACCESSORS(Gap,double) +DECLARE_PLOT_ACCESSORS(LogGap,double) +DECLARE_PLOT_ACCESSORS(LogPlot,int) +DECLARE_PLOT_ACCESSORS(LogTicks,int) +DECLARE_PLOT_ACCESSORS(LogLabel,int) +DECLARE_PLOT_ACCESSORS(LabelUp,int) +DECLARE_PLOT_ACCESSORS(DrawAxes,int) +DECLARE_PLOT_ACCESSORS(LabelUnits,int) +DECLARE_PLOT_ACCESSORS(MinTickLen,double) +DECLARE_PLOT_ACCESSORS(MajTickLen,double) +DECLARE_PLOT_ACCESSORS(NumLab,int) +DECLARE_PLOT_ACCESSORS(NumLabGap,double) +DECLARE_PLOT_ACCESSORS(TextLab,int) +DECLARE_PLOT_ACCESSORS(TextLabGap,double) + +#undef DECLARE_PLOT_ACCESSORS + + +/* Declare private member functions that access element-specific attributes + inherited from the Plot class. Also declare pointers to hold the parent + function pointers. + ----------------------------------------------------------------------*/ + +#define DECLARE_PLOT_ACCESSORS(attr,type) \ + static void Set##attr(AstPlot *,int,type,int *); \ + static void Clear##attr(AstPlot *,int,int *); \ + static void (*parent_set##attr)(AstPlot *,int,type,int *); \ + static void (*parent_clear##attr)(AstPlot *,int,int *); + +DECLARE_PLOT_ACCESSORS(Style,int) +DECLARE_PLOT_ACCESSORS(Font,int) +DECLARE_PLOT_ACCESSORS(Colour,int) +DECLARE_PLOT_ACCESSORS(Width,double) +DECLARE_PLOT_ACCESSORS(Size,double) + +#undef DECLARE_PLOT_ACCESSORS + + +/* Declare private member functions that access attributes inherited from + the Plot class that do not need to override the Get method. This + includes attributes that are not axis-specific or that do not have + dynamic defaults. Also declare pointers to hold the parent function + pointers. + ----------------------------------------------------------------------*/ +#define DECLARE_PLOT_ACCESSORS(attr,type) \ + static void Set##attr(AstPlot *,type,int *); \ + static void Clear##attr(AstPlot *,int *); \ + static void (*parent_set##attr)(AstPlot *,type,int *); \ + static void (*parent_clear##attr)(AstPlot *,int *); + +DECLARE_PLOT_ACCESSORS(Ink,int) +DECLARE_PLOT_ACCESSORS(Tol,double) +DECLARE_PLOT_ACCESSORS(Invisible,int) +DECLARE_PLOT_ACCESSORS(TickAll,int) +DECLARE_PLOT_ACCESSORS(ForceExterior,int) +DECLARE_PLOT_ACCESSORS(Border,int) +DECLARE_PLOT_ACCESSORS(Clip,int) +DECLARE_PLOT_ACCESSORS(ClipOp,int) +DECLARE_PLOT_ACCESSORS(Escape,int) +DECLARE_PLOT_ACCESSORS(Grid,int) +DECLARE_PLOT_ACCESSORS(Labelling,int) + +#undef DECLARE_PLOT_ACCESSORS + + + +/* Member functions. */ +/* ================= */ + +static int Attr3D( AstKeyMap *grfconID, int attr, double value, + double *old_value, int prim, int *status ){ +/* +* Name: +* Attr3D + +* Purpose: +* Get or set the value of a 3D grf attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* Attr3D( AstKeyMap *grfconID, int attr, double value, double *old_value, +* int prim, int *status ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function gets or sets the current value of a specified graphics +* attribute in the parent Plot structure of a Plot3D. It forwards the +* call to the grf3D module being used by this Plot3D. It should be +* registered with the parent Plot using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. +* attr +* An integer value identifying the required attribute. This should +* be one of the symbolic values defined in grf.h. +* value +* A new value to store for the attribute. If this is AST__BAD +* no value is stored. +* old_value +* A pointer to a double in which to return the attribute value. +* If this is NULL, no value is returned. +* prim +* The sort of graphics primitive to be drawn with the new attribute. +* Identified by one of the values defined in grf.h. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + int result; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Use the function in the external Grf3D module, selected at link-time + using ast_link options. */ + result = astG3DAttr( attr, value, old_value, prim ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Return the result. */ + return result; +} + +static AstPlot *AxisPlot( AstPlot3D *this, int axis3d, int *axis2d, int *status ){ +/* +* Name: +* AxisPlot + +* Purpose: +* Find the Plot used to label a 3D axis. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* AstPlot *AxisPlot( AstPlot3D *this, int axis3d, int *axis2d, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function returns a pointer to the encapsulated 2D Plot that +* is used to label the given 3D axis. It also returns the index +* of the labelled axis within the 2D Plot. + +* Parameters: +* this +* Pointer to a Plot3D. +* axis3d +* A zero-based axis index within the Plot3D. +* axis2d +* Pointer to an int in which to put the index of the labelled axis +* within the returned 2D Plot. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Plot used to label the 3D axis. Do not annul this +* pointer. + +*- +*/ + +/* Local Variables: */ + AstPlot *plot; + +/* Check the global status. */ + if( !astOK ) return NULL; + +/* Return the required information form the Plot3D structure. */ + plot = GET_PLOT( this->axis_plot1[ axis3d ] ); + if( ! plot ) { + astError( AST__INTER, "AxisPlot(Plot3D): Illegal value %d " + "for axis3d (internal AST programming error).", status, + this->axis_plot1[ axis3d ] ); + } + + *axis2d = this->axis_index1[ axis3d ]; + + return plot; +} + +static int Border( AstPlot *this_plot, int *status ){ +/* +* Name: +* Border + +* Purpose: +* Draw a border around valid regions of a Plot. + +* Type: +* Private member function. + +* Synopsis: +* #include "plot3d.h" +* int Border( AstPlot *this, int *status ) + +* Class Membership: +* Plot method (overrides the astBorder method inherited from the +* Plot class) + +* Description: +* This function draws a (line) border around regions of the +* plotting area of a Plot which correspond to valid, unclipped +* physical coordinates. For example, when plotting using an +* all-sky map projection, this function could be used to draw the +* boundary of the celestial sphere when it is projected on to the +* plotting surface. +* +* If the entire plotting area contains valid, unclipped physical +* coordinates, then the boundary will just be a rectangular box +* around the edges of the plotting area. + +* Parameters: +* this +* Pointer to the Plot. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if the plotting area is completely filled by +* valid, unclipped physical coordinates (so that only a +* rectangular box was drawn around the edge). Otherwise, one is +* returned. + +* Notes: +* - The Plot3D implementation of this method, invokes the astBorder +* method on each of the three encapsulated Plots, and returns the +* logical OR of the three returned flags. +* - A value of zero will be returned if this function is invoked +* with the AST error status set, or if it should fail for any +* reason. +* - An error results if either the current Frame or the base Frame +* of the Plot is not 2-dimensional, or (for a Plot3D) 3-dimensional. +* - An error also results if the transformation between the base +* and current Frames of the Plot is not defined (i.e. the Plot's +* TranForward attribute is zero). +*/ + +/* Local Variables: */ + AstPlot3D *this; + const char *class; + const char *method; + float x1; + float y1; + float z1; + float x[ 2 ]; + float y[ 2 ]; + float z[ 2 ]; + int flag1; + int flag2; + int flag3; + int naxes; + int ok; + int result; + int root_corner; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_plot; + +/* Store the current method, and the class of the supplied object for use + in error messages.*/ + method = "astBorder"; + class = astGetClass( this ); + +/* Check the base Frame of the Plot is 3-D. */ + naxes = astGetNin( this ); + if( naxes != 3 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 3.", status, method, class, naxes, class ); + } + +/* Check the current Frame of the Plot is 3-D. */ + naxes = astGetNout( this ); + if( naxes != 3 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the current " + "Frame of the supplied %s is invalid - this number should " + "be 3.", status, method, class, naxes, class ); + } + +/* Invoke the astBorder method on each of the three encapsulated Plots. */ + flag1 = astBorder( this->plotxy ); + flag2 = astBorder( this->plotxz ); + flag3 = astBorder( this->plotyz ); + +/* If no bad values were encountered in any of the Plots, draw lines + along the remaining plot edges. */ + result = ( flag1 || flag2 || flag3 ); + if( !result ) { + +/* The three remaining edges ot be drawn all meet at the corner + diagonally opposite the root corner. Get the root corner. */ + root_corner = astGetRootCorner( this ); + +/* The (x0,y0,z0) position is the graphics coords at the corner + diagonally opposite the root corner. The x1, y1 and z1 values + are the graphics x, y and z values at the ends of the three + lines that remain to be drawn. */ + if( root_corner & 1 ) { + x[ 0 ] = this->gbox[ 0 ]; + x1 = this->gbox[ 3 ]; + } else { + x[ 0 ] = this->gbox[ 3 ]; + x1 = this->gbox[ 0 ]; + } + + if( root_corner & 2 ) { + y[ 0 ] = this->gbox[ 1 ]; + y1 = this->gbox[ 4 ]; + } else { + y[ 0 ] = this->gbox[ 4 ]; + y1 = this->gbox[ 1 ]; + } + + if( root_corner & 4 ) { + z[ 0 ] = this->gbox[ 2 ]; + z1 = this->gbox[ 5 ]; + } else { + z[ 0 ] = this->gbox[ 5 ]; + z1 = this->gbox[ 2 ]; + } + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__BORDER_ID, 1, GRF__LINE, method, class ); + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Draw the remaining line parallel to the X axis. */ + x[ 1 ] = x1; + y[ 1 ] = y[ 0 ]; + z[ 1 ] = z[ 0 ]; + ok = astG3DLine( 2, x, y, z ); + +/* Draw the remaining line parallel to the Y axis. */ + x[ 1 ] = x[ 0 ]; + y[ 1 ] = y1; + z[ 1 ] = z[ 0 ]; + ok = ok && astG3DLine( 2, x, y, z ); + +/* Draw the remaining line parallel to the X axis. */ + x[ 1 ] = x[ 0 ]; + y[ 1 ] = y[ 0 ]; + z[ 1 ] = z1; + ok = ok && astG3DLine( 2, x, y, z ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__BORDER_ID, 0, GRF__LINE, method, class ); + +/* Report an error if anything went wrong in the grf3d module. */ + if( !ok && astOK ) { + astError( AST__GRFER, "%s(%s): Graphics error in astG3DLine. ", status, + method, class ); + } + } + +/* Return zero if an error has occurrred. */ + if( !astOK ) result = 0; + +/* Return a flag indicating if any bad values were encountered in any of + the Plots. */ + return result; +} + +static AstObject *Cast( AstObject *this_object, AstObject *obj, int *status ) { +/* +* Name: +* Cast + +* Purpose: +* Cast an Object into an instance of a sub-class. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* AstObject *Cast( AstObject *this, AstObject *obj, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the protected astCast +* method inherited from the Frame class). + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. Specifically, if "this" and "new" are +* of the same class, a copy of "this" is returned. If "this" is an +* instance of a subclass of "obj", then a copy of the component +* of "this" that matches the class of "obj" is returned. Otherwise, +* a NULL pointer is returned without error. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. NULL if "this" is not a sub-class of +* "obj", or if an error occurs. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables; */ + AstObject *new; + astDECLARE_GLOBALS + int generation_gap; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* See how many steps up the class inheritance ladder it is from "obj" + to this class (Plot3D). A positive value is returned if Plot3D + is a sub-class of "obj". A negative value is returned if "obj" is + a sub-class of Plot3D. Zero is returned if "obj" is a Plot3D. + AST__COUSIN is returned if "obj" is not on the same line of descent + as Plot3D. */ + generation_gap = astClassCompare( (AstObjectVtab *) &class_vtab, + astVTAB( obj ) ); + +/* If "obj" is a Plot3D or a sub-class of Plot3D, we can cast by + truncating the vtab for "this" so that it matches the vtab of "obJ", + and then taking a deep copy of "this". */ + if( generation_gap <= 0 && generation_gap != AST__COUSIN ) { + new = astCastCopy( this_object, obj ); + +/* If "obj" is a Plot (the parent class), we cast by returning a deep + copy of the Plot covering the XY face. */ + } else if( generation_gap == 1 ) { + new = astCopy( ( (AstPlot3D *) this_object)->plotxy ); + +/* If "obj" is a FrameSet or higher, we attempt to use the implementation + inherited from the parent class to cast the FrameSet component into the + class indicated by "obj". */ + } else { + new = (*parent_cast)( this_object, obj, status ); + } + +/* Return the new pointer. */ + return new; +} + +static void ChangeRootCorner( AstPlot3D *this, int old, int new, int *status ){ +/* +* Name: +* ChangeRootCorner + +* Purpose: +* Use a new RootCorner value. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void ChangeRootCorner( AstPlot3D *this, int old, int new, int *status ) + +* Class Membership: +* Plot method. + +* Description: +* This function sets the attributes of the encapsulated Plots so that +* labels appear on the edges of the 3D graphics cube that join at the +* specified new root corner. It also reverses the graphics axes in +* the encapsulated Plots as required in order to ensure that the Plots +* look "normal" when viewed from the outside of the 3D graphics cube. +* This happens if a the Plot used to label a specific axis moves from +* one face the the 3D graphics cube to the opposite face. + +* Parameters: +* this +* Pointer to a Plot3D. +* old +* The old RootCorner value. +* new +* The new RootCorner value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Each RootCorner value is in the range 0 to 7 and is a 3 bit +* bit-mask. Bit 0 describes the 3D graphics X axis, bit 1 describes +* the graphics Y axis and bit 2 describes the graphics Z axis. If a +* bit is set it means that the corner is at the upper bound on the +* corresponding axis. If a bit is unset it means that the corner is +* at the lower bound on the corresponding axis. + +*- +*/ + +/* Local Variables: */ + AstKeyMap *grfcon; + AstPlot *plot; + AstPlot *plots[ 24 ]; + int edges[ 24 ]; + int axes[ 24 ]; + int axis2d; + int edge; + int i; + int np; + int xeqy; + int xeqz; + +/* Check the global status. */ + if( !astOK ) return; + +/* If the corner has moved on the 3D X axis (from upper X bound to lower X + bound or vice-versa), mirror the axis of the encapsulated Plot that is + perpendicular to the 3D X axis. This means that the Plot can be thought + of as being viewed from the outside of the 3D graphics cube. Also, + update the constant X axis value at which the YZ plane is drawn. */ + if( ( old & 1 ) != ( new & 1 ) ) astMirror( this->plotyz, 0 ); + grfcon = (AstKeyMap *) astGetGrfContext( this->plotyz ); + astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 1 ) ? 3 : 0 ], "Constant X value" ); + astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" ); + grfcon= astAnnul( grfcon ); + +/* Likewise mirror the other two axes if required. */ + if( ( old & 2 ) != ( new & 2 ) ) astMirror( this->plotxz, 0 ); + grfcon = (AstKeyMap *) astGetGrfContext( this->plotxz ); + astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 2 ) ? 4 : 1 ], "Constant Y value" ); + astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" ); + grfcon= astAnnul( grfcon ); + + if( ( old & 4 ) != ( new & 4 ) ) astMirror( this->plotxy, 0 ); + grfcon = (AstKeyMap *) astGetGrfContext( this->plotxy ); + astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 4 ) ? 5 : 2 ], "Constant Z value" ); + astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" ); + grfcon= astAnnul( grfcon ); + +/* Set a flag saying whether the limits are equal at the new corner for + the X and Y axes. */ + xeqy = ( ( ( new & 1 ) > 0 ) == ( ( new & 2 ) > 0 ) ); + +/* Set a flag saying whether the limits are equal at the new corner for + the X and Z axes. */ + xeqz = ( ( ( new & 1 ) > 0 ) == ( ( new & 4 ) > 0 ) ); + +/* Ensure all Edge attributes are clear. This means that the public + attribute accessors routines used below will return dynamic defaults for + the Edge attributes. */ + astClearEdge( this->plotxy, 0 ); + astClearEdge( this->plotxy, 1 ); + astClearEdge( this->plotxz, 0 ); + astClearEdge( this->plotxz, 1 ); + astClearEdge( this->plotyz, 0 ); + astClearEdge( this->plotyz, 1 ); + +/* So far we have recorded no edges changes. */ + np = 0; + +/* We now adjust the Edge attributes in the Plot used to annotate the 3D + X axis in order to get the X axis labels on the correct edge of the 3D + graphics cube. Get the Plot used to produce X axis labels (this will + be either this->plotxy or this->plotxz). */ + plot = AxisPlot( this, 0, &axis2d, status ); + +/* See what edge of the Plot is used to annotate the first of the two WCS + axis described by the 2D Plot. If the Edge(1) attribute has not been + assigned a value, then a dynamic default will be used by the Plot class. + We want to know what this dynamic default is, so we first cleared the + attribute above. Now we set the attribute value explicitly to the dynamic + default returned by astGetC. Note, we use astGetC rather than astGetEdge + because the dynamic default is not calculated when calling astGetEdge. */ + astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" )); + edge = astGetEdge( plot, 0 ); + astClearEdge( plot, 0 ); + +/* If the 3D X axis is labelled using the Plot that spans the XY plane... */ + if( plot == this->plotxy ) { + +/* ... and if the new root corner is at the upper limit on the Y axis... */ + if( new & 2 ) { + +/* If the first WCS axis is currently labelled on either the top or bottom + edge, ensure it is labelled on the upper Y (top) edge. */ + if( edge == 3 || edge == 1 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = TOP; + +/* Otherwise ensure that the second WCS axis is labelled on the upper Y (top) + edge. */ + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = TOP; + } + +/* If the new root corner is at the lower limit on the Y axis... */ + } else { + +/* If the first WCS axis is currently labelled on either the top or bottom + edge, ensure it is labelled on the lower Y (bottom) edge. */ + if( edge == 3 || edge == 1 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = BOTTOM; + +/* Otherwise ensure that the second WCS axis is labelled on the lower Y + (bottom) edge. */ + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = BOTTOM; + } + } + +/* If the 3D X axis is labelled using the Plot that spans the XZ plane... */ + } else { + +/* ... and if the new root corner is at the upper limit on the Z axis... */ + if( new & 4 ) { + +/* If the first WCS axis is currently labelled on either the top or bottom + edge, ensure it is labelled on the upper Z (top) edge. */ + if( edge == 3 || edge == 1 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = TOP; + +/* Otherwise ensure that the second WCS axis is labelled on the upper Y (top) + edge. */ + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = TOP; + } + +/* If the new root corner is at the lower limit on the Z axis... */ + } else { + +/* If the first WCS axis is currently labelled on either the top or bottom + edge, ensure it is labelled on the lower Z (bottom) edge. */ + if( edge == 3 || edge == 1 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = BOTTOM; + +/* Otherwise ensure that the second WCS axis is labelled on the lower Z + (bottom) edge. */ + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = BOTTOM; + } + } + } + +/* We now adjust the Edge attributes in the Plot used to annotate the 3D + Y axis in order to get the Y axis labels on the correct edge of the 3D + graphics cube. Get the Plot used to produce Y axis labels. */ + plot = AxisPlot( this, 1, &axis2d, status ); + +/* See what edge of the Plot is used to annotate the first of the two WCS + axis described by the Plot. */ + astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" )); + edge = astGetEdge( plot, 0 ); + astClearEdge( plot, 0 ); + +/* If the 3D Y axis is labelled using the Plot that spans the XY plane... */ + if( plot == this->plotxy ) { + +/* ... and if the new root corner is at the same limit on the X and Z axes, + put Y labels on the right side of the Plot. */ + if( xeqz ) { + if( edge == 0 || edge == 2 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = RIGHT; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = RIGHT; + } + +/* If the new root corner is at a different limit on the X and Z axes, + put Y labels on the left side of the Plot. */ + } else { + if( edge == 0 || edge == 2 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = LEFT; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = LEFT; + } + } + +/* If the 3D Y axis is labelled using the Plot that spans the YZ plane... */ + } else { + +/* ... and if the new root corner is at the upper Z limit, put Y labels on + the top of the Plot. */ + if( new & 4 ) { + if( edge == 1 || edge == 3 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = TOP; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = TOP; + } + +/* If the new root corner is at the lower Z limit, put Y labels on the + bottom of the Plot. */ + } else { + if( edge == 1 || edge == 3 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = BOTTOM; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = BOTTOM; + } + } + } + +/* We now adjust the Edge attributes in the Plot used to annotate the 3D + Z axis in order to get the Z axis labels on the correct edge of the 3D + graphics cube. Get the Plot used to produce Z axis labels. */ + plot = AxisPlot( this, 2, &axis2d, status ); + +/* See what edge of the Plot is used to annotate the first of the two WCS + axis described by the Plot. */ + astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" )); + edge = astGetEdge( plot, 0 ); + astClearEdge( plot, 0 ); + +/* If the 3D Z axis is labelled using the Plot that spans the XZ plane... */ + if( plot == this->plotxz ) { + +/* ... and if the new root corner is at the same limit on the X and Y axes, + put Z labels on the left side of the Plot. */ + if( xeqy ) { + if( edge == 0 || edge == 2 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = LEFT; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = LEFT; + } + +/* If the new root corner is at a different limit on the X and Y axes, + put Y labels on the right side of the Plot. */ + } else { + if( edge == 0 || edge == 2 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = RIGHT; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = RIGHT; + } + } + +/* If the 3D Z axis is labelled using the Plot that spans the YZ plane... */ + } else { + +/* ... and if the new root corner is at the same limit on the X and Y axes, + put Z labels on the right side of the Plot. */ + if( xeqz ) { + if( edge == 0 || edge == 2 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = RIGHT; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = RIGHT; + } + +/* If the new root corner is at a different limit on the X and Y axes, + put Y labels on the left side of the Plot. */ + } else { + if( edge == 0 || edge == 2 ) { + plots[ np ] = plot; + axes[ np ] = 0; + edges[ np++ ] = LEFT; + } else { + plots[ np ] = plot; + axes[ np ] = 1; + edges[ np++ ] = LEFT; + } + } + } + +/* Apply the set of edge changes determined above. */ + for( i = 0; i < np; i++ ) { + astSetEdge( plots[ i ], axes[ i ], edges[ i ] ); + } + +/* Ensure that the 2 Plot axes that are not being used have suitable values + for their attributes. That is, no labels are drawn, and the ticked + edges are the one that meet at the new RootCorner. */ + + if( !astTestEdge( this->plotxy, 0 ) ) { + astSetEdge( this->plotxy, 0, ( new & 2 ) ? TOP : BOTTOM ); + } + + if( !astTestEdge( this->plotxy, 1 ) ) { + astSetEdge( this->plotxy, 1, xeqz ? RIGHT: LEFT ); + } + + if( !astTestEdge( this->plotxz, 0 ) ) { + astSetEdge( this->plotxz, 0, ( new & 4 ) ? TOP : BOTTOM ); + } + + if( !astTestEdge( this->plotxz, 1 ) ) { + astSetEdge( this->plotxz, 1, xeqy ? LEFT : RIGHT ); + } + + if( !astTestEdge( this->plotyz, 0 ) ) { + astSetEdge( this->plotyz, 0, ( new & 4 ) ? TOP : BOTTOM ); + } + + if( !astTestEdge( this->plotyz, 1 ) ) { + astSetEdge( this->plotyz, 1, xeqy ? RIGHT : LEFT ); + } + + +} + +static void Clear( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* Clear + +* Purpose: +* Clear attribute values for a Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void Clear( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the public astClear method +* inherited from the Object class). + +* Description: +* This function clears the values of a specified set of attributes +* for a Plot3D. Clearing an attribute cancels any value that has +* previously been explicitly set for it, so that the standard +* default attribute value will subsequently be used instead. This +* also causes the astTest function to return the value zero for +* the attribute, indicating that no value has been set. + +* Parameters: +* this +* Pointer to the Plot3D. +* attrib +* Pointer to a null-terminated character string containing a +* comma-separated list of the names of the attributes to be +* cleared. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function preserves the integrity of the Plot3D (if +* possible) by appropriately modifying the three encapsulated Plots. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent astClear method to clear the Plot3D's attribute values. */ + (*parent_clear)( this_object, attrib, status ); + +/* Update the three 2D Plots stored in the Plot3D structure so that they + reflect this modified FrameSet. */ + UpdatePlots( (AstPlot3D *) this_object, status ); + +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot.h" +* void ClearAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Plot3D member function (over-rides the astClearAttrib protected +* method inherited from the Plot class). + +* Description: +* This function clears the value of a specified attribute for a +* Plot3D, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Plot3D. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +*/ + +/* Local Variables: */ + AstPlot3D *this; /* Pointer to the Plot3D structure */ + AstPlot *plot; /* Pointer to specific Plot */ + char attname[50]; /* Plot attribute base name */ + char patt[50]; /* Plot attribute full name */ + char spec[10]; /* Plane specification */ + int axis; /* Axis index */ + int len; /* Length of attrib string */ + int nc; /* Number of characters read */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Norm. */ +/* ----------- */ + if ( !strcmp( attrib, "norm" ) ) { + astClearNorm( this, 0 ); + astClearNorm( this, 1 ); + astClearNorm( this, 2 ); + +/* Norm(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearNorm( this, axis - 1 ); + +/* RootCorner. */ +/* ----------- */ + } else if ( !strcmp( attrib, "rootcorner" ) ) { + astClearRootCorner( this ); + +/* ..._XY etc */ +/* ---------- */ + } else if ( nc = 0, + ( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec, + &nc ) ) ) { + + if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) { + plot = this->plotxy; + } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) { + plot = this->plotyz; + } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) { + plot = this->plotxz; + } else { + plot = NULL; + } + + if( plot ) { + sprintf( patt, "%s%s", attname, attrib + nc ); + astClearAttrib( plot, patt ); + + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearCurrent( AstFrameSet *this_frameset, int *status ) { +/* +* Name: +* ClearCurrent + +* Purpose: +* Clear the value of the Current attribute for a Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int astClearCurrent( AstFrameSet *this ) + +* Class Membership: +* Plot3D member function (over-rides the public astClearCurrent method +* inherited from the FrameSet class). + +* Description: +* This function clears the value of the Current attribute for a +* Plot3D. This attribute is an index that identifies the current +* Frame for the Plot3D. + +* Parameters: +* this +* Pointer to the Plot3D. +*/ + +/* Invoke the parent astClearCurrent method. */ + (*parent_clearcurrent)( this_frameset, status ); + +/* Update the three 2D Plots stored in the Plot3D structure so that they + reflect this modified FrameSet. */ + UpdatePlots( (AstPlot3D *) this_frameset, status ); +} + +static void ClearRootCorner( AstPlot3D *this, int *status ){ +/* +*+ +* Name: +* astClearRootCorner + +* Purpose: +* Clear the RootCorner attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "plot3d.h" +* void astClearRootCorner( AstPlot3D *this ) + +* Class Membership: +* Plot method. + +* Description: +* This function clears the RootCorner attribute. + +* Parameters: +* this +* Pointer to a Plot3D. + +*- +*/ + +/* Local Variables: */ + int old; + int new; + +/* Check the global status. */ + if( !astOK ) return; + +/* Get the current rootcorner value. */ + old = astGetRootCorner( this ); + +/* Clear the RootCorner attribute. */ + this->rootcorner = -1; + +/* Get the new (default) rootcorner value. */ + new = astGetRootCorner( this ); + +/* If the root corner has changed, mirror any axes of the encapsulated Plots + that need mirroring (this is done to ensure that Plots look right when + viewed from the outside of the graphics cube), and modify the Edge + attributes in the encapsulated Plots to ensure the labels appear on the + requested edges of the 3D graphics cube. . */ + if( old != new ) ChangeRootCorner( this, old, new, status ); +} + +static void CreatePlots( AstPlot3D *this, AstFrameSet *fset, const float *gbox, + const double *bbox, int *status ) { +/* +* Name: +* CreatePlots + +* Purpose: +* Create three 2D plots and store in the Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void CreatePlots( AstPlot3D *this, AstFrameSet *fset, const float *gbox, + const double *bbox, int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function splits the supplied FrameSet up into 3 independent 2D +* FrameSets, each describing a 2D plane in the supplied 3D FrameSet. +* It then uses these 2D FrameSets to create three Plots, one for each +* plane in the graphics plotting space, and stores them in the Plot3D. +* +* Each of the three Plots is notionally pasted onto one face of the +* 3D graphics cube (the RootCorner attribute is used to determine which +* of the two parallel faces a particular Plot is pasted onto). The +* Plot is pasted in such a way that, when viewed from the outside of +* the graphics cube, the first graphics axis increases left to right +* and the second increases bottom to top (this assumes that "up" is +* parallel to the 3D Z axis). +* +* Initially, the Plots are created assuming the default RootCorner +* value ("LLL"). They will be changed later if the value of the +* RootCorner attribute is changed. + +* Parameters: +* this +* Pointer to the Plot3D. +* fset +* Pointer to the FrameSet. +* gbox +* A pointer to an array of 6 values giving the graphics coordinates +* of the bottom left and top right corners of a box on the graphics +* output device. The first triple of values should be the graphics +* coordinates of the bottom left corner of the box and the second +* triple of values are the graphics coordinates of the top right corner. +* bbox +* A pointer to an array of 6 values giving the coordinates in the +* supplied Frame or base Frame of the supplied FrameSet at the bottom +* left and top right corners of the box specified by parameter gbox. +* These should be supplied in the same order as for parameter "gbox". +* status +* Pointer to the inherited status variable. + +* Notes: +* - Each returned plot has 3 Frames: Frame 1 is the base (GRAPHICS) +* Frame; Frame 2 is spanned by 2 of the 3 axes in the base Frame of +* the supplied FrameSet; Frame 3 is the current Frame and is spanned +* by 2 of the 3 axes in the current Frame of the supplied FrameSet. +* Any future changes to this function that alter this structure should +* reflected in equivalent changes to function UpdatePlots. + +*/ + +/* Local Variables: */ + AstFrameSet *fsetxy; + AstFrameSet *fsetxz; + AstFrameSet *fsetyz; + double basebox2d[ 4 ]; + float graphbox2d[ 4 ]; + int baseplane; + int labelxy[ 2 ]; + int labelxz[ 2 ]; + int labelyz[ 2 ]; + int wcsxy[ 2 ]; + int wcsxz[ 2 ]; + int wcsyz[ 2 ]; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Split the supplied FrameSet up into 3 FrameSets, each with a 2D base + and current Frame. Each of these FrameSets describes one plane of + the 3D cube. One of them will be spanned by two axes picked from the + supplied 3D FrameSet. The other two FrameSets will each include a copy + of the remaining 3rd axis from the supplied FrameSet, plus an extra + dummy axis. These dummy axes will never be labelled. */ + SplitFrameSet( fset, &fsetxy, labelxy, wcsxy, &fsetxz, labelxz, wcsxz, + &fsetyz, labelyz, wcsyz, &baseplane, status ); + +/* If OK, annul any existing 2D plots. */ + if( astOK ) { + if( this->plotxy ) this->plotxy = astAnnul( this->plotxy ); + if( this->plotxz ) this->plotxz = astAnnul( this->plotxz ); + if( this->plotyz ) this->plotyz = astAnnul( this->plotyz ); + +/* Create three Plots; one for each 2D plane in the graphics plotting + space. Set the attributes of these plots so that the required axes are + labelled and other axes are left blank. The "graphbox2d" and "basebox2d" + values used to create each Plot define the sense, as well as the extent, + of each axis. The first pair of values in each give the lower left corner + of the Plot and the second pair give the top right corner. We want each + Plot to have X increasing left to right and Y increasing bottom to + top when viewed from the outside of the cube. We assume an initial + RootCorner value of "LLL" (that is, the Plots are pasted onto the cube + faces that meet at the lower limit on every axis). */ + graphbox2d[ 0 ] = gbox[ 3 ]; + graphbox2d[ 1 ] = gbox[ 1 ]; + graphbox2d[ 2 ] = gbox[ 0 ]; + graphbox2d[ 3 ] = gbox[ 4 ]; + + basebox2d[ 0 ] = bbox[ 3 ]; + basebox2d[ 1 ] = bbox[ 1 ]; + basebox2d[ 2 ] = bbox[ 0 ]; + basebox2d[ 3 ] = bbox[ 4 ]; + + if( this->plotxy ) this->plotxy = astAnnul( this->plotxy ); + this->plotxy = astPlot( fsetxy, graphbox2d, basebox2d, "", status ); + SetPlotAttr( this->plotxy, XY, labelxy, status ); + + graphbox2d[ 0 ] = gbox[ 0 ]; + graphbox2d[ 1 ] = gbox[ 2 ]; + graphbox2d[ 2 ] = gbox[ 3 ]; + graphbox2d[ 3 ] = gbox[ 5 ]; + + basebox2d[ 0 ] = bbox[ 0 ]; + basebox2d[ 1 ] = bbox[ 2 ]; + basebox2d[ 2 ] = bbox[ 3 ]; + basebox2d[ 3 ] = bbox[ 5 ]; + + this->plotxz = astPlot( fsetxz, graphbox2d, basebox2d, "", status ); + SetPlotAttr( this->plotxz, XZ, labelxz, status ); + + graphbox2d[ 0 ] = gbox[ 4 ]; + graphbox2d[ 1 ] = gbox[ 2 ]; + graphbox2d[ 2 ] = gbox[ 1 ]; + graphbox2d[ 3 ] = gbox[ 5 ]; + + basebox2d[ 0 ] = bbox[ 4 ]; + basebox2d[ 1 ] = bbox[ 2 ]; + basebox2d[ 2 ] = bbox[ 1 ]; + basebox2d[ 3 ] = bbox[ 5 ]; + + this->plotyz = astPlot( fsetyz, graphbox2d, basebox2d, "", status ); + SetPlotAttr( this->plotyz, YZ, labelyz, status ); + +/* Store information that allows each 3D WCS axis to be associatedf with + a pair of Plots. Also store the WCS axis within each Plot that + corresponds to the 3D WCS axis. */ + StoreAxisInfo( this, labelxy, wcsxy, labelxz, wcsxz, labelyz, wcsyz, status ); + +/* Store the Plot that spans two connected 3D axes. */ + this->baseplot = baseplane; + +/* Free resources */ + fsetxy = astAnnul( fsetxy ); + fsetxz = astAnnul( fsetxz ); + fsetyz = astAnnul( fsetyz ); + + } +} + +static int Element2D( AstPlot3D *this, int element, int *elem2d1, + int *elem2d2, int *status ){ +/* +* Name: +* Element2D + +* Purpose: +* Convert a 3D graphics element identifier to a corresponding pair of +* 2D identifiers. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Element2D( AstPlot3D *this, int element, int *elem2d1, +* int *elem2d2, int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function takes an integer identifier for an element of a 3D +* annotated grid (e.g. ticks, axis 1 labels, border, etc), and returns +* a element identifers that can be used with the encapsualted 2D Plots. + +* Parameters: +* this +* Pointer to the Plot2D structure. +* element +* The 3D element identifier to convert. +* elem2d1 +* Pointer to an int in which to return the 2D element identifier +* to use with the first of the two Plots that span the axis to +* which the 3D element identifier refers. Returned holding 0 if +* the given 3D element identifier is not axis specific. +* elem2d2 +* Pointer to an int in which to return the 2D element identifier +* to use with the second of the two Plots that span the axis to +* which the 3D element identifier refers. Returned holding 0 if +* the given 3D element identifier is not axis specific. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The zero-based index of the 3D axis to which the given element +* identifier refers, or -1 if the element identifier is not axis +* specific. + +*/ + +/* Local Variables: */ + int axis3d; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get the zero-based index of the 3D axis to which the supplied element + refers. Use an index of -1 to indicate that the element does not + relate to a specific axis. Also get the corresponding element to use + with the two Plots that share the speified 3D axis. */ + +/* Define a macro used to set the 2d element identifiers for a given 3d + element identifier. */ + +#define SET_ELEM2D(id1,id2) \ + *elem2d1 = this->axis_index1[ axis3d ] ? id2 : id1; \ + *elem2d2 = this->axis_index2[ axis3d ] ? id2 : id1; + + if( element == AST__BORDER_ID ){ + axis3d = -1; + + } else if( element == AST__CURVE_ID ){ + axis3d = -1; + + } else if( element == AST__TITLE_ID ){ + axis3d = -1; + + } else if( element == AST__MARKS_ID ){ + axis3d = -1; + + } else if( element == AST__TEXT_ID ){ + axis3d = -1; + + } else if( element == AST__AXIS1_ID ){ + axis3d = 0; + SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID) + + } else if( element == AST__AXIS2_ID ){ + axis3d = 1; + SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID) + + } else if( element == AST__AXIS3_ID ){ + axis3d = 2; + SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID) + + } else if( element == AST__NUMLAB1_ID ){ + axis3d = 0; + SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID) + + } else if( element == AST__NUMLAB2_ID ){ + axis3d = 1; + SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID) + + } else if( element == AST__NUMLAB3_ID ){ + axis3d = 2; + SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID) + + } else if( element == AST__TEXTLAB1_ID ){ + axis3d = 0; + SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID) + + } else if( element == AST__TEXTLAB2_ID ){ + axis3d = 1; + SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID) + + } else if( element == AST__TEXTLAB3_ID ){ + axis3d = 2; + SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID) + + } else if( element == AST__TICKS1_ID ){ + axis3d = 0; + SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID) + + } else if( element == AST__TICKS2_ID ){ + axis3d = 1; + SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID) + + } else if( element == AST__TICKS3_ID ){ + axis3d = 2; + SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID) + + } else if( element == AST__GRIDLINE1_ID ){ + axis3d = 0; + SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID) + + } else if( element == AST__GRIDLINE2_ID ){ + axis3d = 1; + SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID) + + } else if( element == AST__GRIDLINE3_ID ){ + axis3d = 2; + SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID) + + } else { + axis3d = 0; + astError( AST__INTER, "Element2D(Plot3D): The MAKE_CLEAR2 macro " + "does not yet support element index %d (internal " + "AST programming error).", status, element ); + } + +#undef SET_ELEM2D + + return axis3d; + +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Plot3Ds are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the astEqual protected +* method inherited from the Plot Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two Plot3Ds are equivalent. + +* Parameters: +* this +* Pointer to the first Plot3D. +* that +* Pointer to the second Plot3D. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the Plot3Ds are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPlot3D *that; /* Pointer to the second Plot3D structure */ + AstPlot3D *this; /* Pointer to the first Plot3D structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent Plot class. This checks + that the Plots are both of the same class (amongst other things). */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Obtain pointers to the two Plot3D structures. */ + this = (AstPlot3D *) this_object; + that = (AstPlot3D *) that_object; + +/* Check the encapsulated Plots for equality. */ + result = ( astEqual( this->plotxz, that->plotxz ) && + astEqual( this->plotyz, that->plotyz ) && + astEqual( this->plotxy, that->plotxy ) ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static AstPointSet *ExtendTicks( AstPlot *plot, AstPointSet *ticks, int *status ){ +/* +* Name: +* ExtendTicks + +* Purpose: +* Add an extra tick to the start and end of a list of tick marks. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* AstPointSet *ExtendTicks( AstPlot *plot, AstPointSet *ticks, int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function takes a list of tick marks drawn using the supplied +* Plot, and adds in an extra tick mark at the start and end of the +* supplied list of ticks, returning the expanded list in a new +* PointSet. The extra points are guaranteed to fall outside the area +* enclosed within the supplied Plot. + +* Parameters: +* plot +* The Plot that was used to generate the list of tick marks. +* ticks +* A PointSet holding the 2D graphics coordinates (within the base +* Frame of the supplied Plot) at which each tick mark starts. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a new PointSet that has 2 more entries than the +* supplied PointSet. The first point is a new tick mark, then comes +* all the ticks mark positions supplied in "ticks", and finally there +* is another new tick mark. + +*/ + +/* Local Variables: */ + AstPointSet *result; + double **ptr_in; + double **ptr_out; + double *a_in; + double *a_out; + double *b_in; + double *b_out; + double *p; + double delta; + double hi; + double lo; + double range[ 2 ]; + double v; + int axis; + int i; + int increasing; + int np; + +/* Check inherited status */ + if( !astOK || !ticks ) return NULL; + +/* Get the number of tick marsk in the supplied PointSet and get pointers + to the 3D Graphics values contained in the PointSet. */ + np = astGetNpoint( ticks ); + ptr_in = astGetPoints( ticks ); + +/* Create the returned PointSet with room for an extra pair of ticks. Get + pointers to its data arrays */ + result = astPointSet( np + 2, 2, "", status ); + ptr_out = astGetPoints( result ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Find the index of the 2D graphics axis (0 or 1) that varies along the + set of tick marks. We do this by finding the max and min value + supplied for each axis, and then choosing the axis that has the highest + range. */ + for( axis = 0; axis < 2; axis++ ) { + hi = -DBL_MAX; + lo = DBL_MAX; + p = ptr_in[ axis ]; + + for( i = 0; i < np; i++, p++ ) { + v = *p; + if( v != AST__BAD ) { + if( v > hi ) hi = v; + if( v < lo ) lo = v; + } + } + + if( lo != DBL_MAX ) { + range[ axis ] = hi - lo; + } else { + astError( AST__INTER, "ExtendTicks{Plot3D): no good ticks on " + "axis %d (internal AST prgramming error).", status, axis ); + } + } + + +/* Get the index of the axis with the largest range (the other range + should be zero). */ + axis = ( range[ 0 ] > range[ 1 ] ) ? 0 : 1; + +/* Copy the input graphics positions to the output PointSet, and add an + extra position at the beginning and end of the output PointSet. */ + if( axis == 0 ) { + lo = plot->xlo; + hi = plot->xhi; + a_in = ptr_in[ 0 ]; + b_in = ptr_in[ 1 ]; + a_out = ptr_out[ 0 ]; + b_out = ptr_out[ 1 ]; + + } else { + lo = plot->ylo; + hi = plot->yhi; + a_in = ptr_in[ 1 ]; + b_in = ptr_in[ 0 ]; + a_out = ptr_out[ 1 ]; + b_out = ptr_out[ 0 ]; + } + + delta = 0.2*( hi - lo ); + + if( a_in[ 0 ] < a_in[ 1 ] ) { + increasing = 1; + *(a_out++) = lo - delta; + } else { + increasing = 0; + *(a_out++) = hi + delta; + } + + *(b_out++) = *b_in; + for( i = 0; i < np; i++ ) { + *(a_out++) = *(a_in++); + *(b_out++) = *(b_in++); + } + *(b_out++) = b_in[ -1 ]; + + + if( increasing ) { + *(a_out++) = hi + delta; + } else { + *(a_out++) = lo - delta; + } + } + +/* Return the extended PointSet. */ + return result; +} + +static AstFrameSet *Fset3D( AstFrameSet *fset, int ifrm, int *status ) { +/* +* Name: +* Fset3D + +* Purpose: +* Create a FrameSet with no more than 3 dimensions for a given Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* AstFrameSet *Fset3D( AstFrameSet *fset, int ifrm, int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function checks a specified Frame in the supplied FrameSet. +* If the Frame has more than 3 dimensions, a new Frame is added to +* the FrameSet containing just the first three axes of the specified +* Frame. A PermMap is used to connect this Frame to the specified +* Frame, which supplied bad values for any missing axes. If the +* specified Frame is the base Frame in the supplied FrameSet, then the +* new Frame becomes the base Frame in the returned FrameSet. Like-wise, +* if the specified Frame is the current Frame, then the new Frame +* will be the current Frame in the returned FrameSet. +* +* If the specified Frame does not have more than 3 axes, then a clone +* of the FrameSet pointer is returned, otherwise the returned pointer +* points to a copy of the supplied FrameSet with the new 3-D Frame +* added. + +* Parameters: +* fset +* Pointer to the FrameSet. +* ifrm +* The index of the Frame to check. This should be AST__BASE or +* AST_CURRENT. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a FrameSet in which the Frame with index given by ifrm +* has no more than 3 axes. +*/ + +/* Local Variables: */ + AstFrame *frm; + AstFrame *newfrm; + AstFrameSet *ret; + AstPermMap *map; + double zero; + int *inperm; + int axes[3]; + int i; + int ic; + int nax; + +/* Check the inherited status. */ + if( !astOK ) return NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + map = NULL; + +/* Get a pointer to the requested Frame in the supplied FrameSet. */ + frm = astGetFrame( fset, ifrm ); + +/* See how many dimensions the specified Frame of the supplied FrameSet + has. */ + nax = astGetNaxes( frm ); + +/* If it is more than 3-dimensionbal, create a 3D Frame by picking + axes 1, 2 and 3 from the original Frame. */ + if( nax > 3 ) { + axes[ 0 ] = 0; + axes[ 1 ] = 1; + axes[ 2 ] = 2; + newfrm = astPickAxes( frm, 3, axes, NULL ); + +/* Create a PermMap to describe the mapping between the two Frames. + Use zero as the value for unknown axes (the optional mapping which + can be returned by astPickAxes uses AST__BAD for unknown axes). */ + inperm = (int *) astMalloc( sizeof(int)*(size_t) nax ); + if( astOK ){ + inperm[ 0 ] = 0; + inperm[ 1 ] = 1; + inperm[ 2 ] = 2; + for( i = 3; i < nax; i++ ) inperm[ i ] = -1; + zero = 0.0; + map = astPermMap( nax, inperm, 3, axes, &zero, "", status ); + inperm = (int *) astFree( (void *) inperm ); + } + +/* Get a copy of the supplied FrameSet. */ + ret = astCopy( fset ); + +/* Add the new Frame to the FrameSet (it becomes the current Frame). */ + ic = astGetCurrent( ret ); + astAddFrame( ret, ifrm, map, newfrm ); + newfrm = astAnnul( newfrm ); + +/* If the new Frame was derived from the base frame, set the new base + Frame, and re-instate the original current Frame */ + if( ifrm == AST__BASE ){ + astSetBase( ret, astGetCurrent( ret ) ); + astSetCurrent( ret, ic ); + } + +/* If the specified Frame in the supplied FrameSet is 3-dimensional, just + return a clone of it. */ + } else { + ret = astClone( fset ); + } + +/* Annul the pointer to the original Frame. */ + frm = astAnnul( frm ); + + return ret; + +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the protected astGetAttrib +* method inherited from the FrameSet class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Plot3D, formatted as a character string. + +* Parameters: +* this +* Pointer to the Plot3D. +* attrib +* Pointer to a null terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Plot3D, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Plot3D. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstPlot *plot; /* Pointer to specific Plot */ + AstPlot3D *this; /* Pointer to the Plot3D structure */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char attname[50]; /* Plot attribute base name */ + char patt[50]; /* Plot attribute full name */ + char spec[10]; /* Plane specification */ + const char *result; /* Pointer value to return */ + double dval; /* Floating point attribute value */ + int axis; /* Axis index */ + int ival; /* Int attribute value */ + int len; /* Length of attrib string */ + int nc; /* Number of character read */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Norm(axis). */ +/* ----------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetNorm( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* RootCorner. */ +/* ----------- */ + } else if ( !strcmp( attrib, "rootcorner" ) ) { + ival = astGetRootCorner( this ); + result = RootCornerString( ival, status ); + if( !result && astOK ) { + astError( AST__INTER, "astGetAttrib(Plot3D): Illegal value %d " + "for RootCorner attribute (internal AST programming " + "error).", status, ival ); + } + +/* ..._XY etc */ +/* ---------- */ + } else if ( nc = 0, + ( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec, + &nc ) ) ) { + + if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) { + plot = this->plotxy; + } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) { + plot = this->plotyz; + } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) { + plot = this->plotxz; + } else { + plot = NULL; + } + + if( plot ) { + sprintf( patt, "%s%s", attname, attrib + nc ); + result = astGetAttrib( plot, patt ); + + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Plot3D, +* in bytes. + +* Parameters: +* this +* Pointer to the Plot3D. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPlot3D *this; /* Pointer to Plot3D structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the Plot3D structure. */ + this = (AstPlot3D *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->plotxy ); + result += astGetObjSize( this->plotxz ); + result += astGetObjSize( this->plotyz ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void Grid( AstPlot *this_plot, int *status ) { +/* +* Name: +* Grid + +* Purpose: +* Draw a set of labelled coordinate axes. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void Grid( AstPlot *this, int *status ) + +* Class Membership: +* Plot member function (over-rides the astGrid method inherited from +* the Plot class). + +* Description: +* This function draws a complete annotated set of 3-dimensional +* coordinate axes for a Plot3D with (optionally) a coordinate grid +* superimposed. + +* Parameters: +* this +* Pointer to the Plot3D. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPlot *baseplot; + AstPlot *plot; + AstPlot3D *this; + AstPointSet *majticks; + AstPointSet *minticks; + AstPointSet *tmp; + AstPointSet *wcsmajticks; + AstPointSet *wcsminticks; + const char *edge; + double **ptrmin; + double **ptrmaj; + double gcon; + int base_wax2d; + int itick; + int new_gaxis; + int new_plot; + int new_wax2d; + int nmaj; + int nmin; + int perm[ 2 ]; + int rootcorner; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_plot; + +/* Invoke the astGrid method on the 2D base Plot. Both WCS axes in this + Plot are inherited from the 3D FrameSet that was supplied when the Plot3D + was constructed, and will be labelled. The other two Plots encapsulated + in the Plot3D only inherit a single axis from the original 3D FrameSet + (a dummy axis is used for the second WCS axis in these Plots). */ + baseplot = GET_PLOT( this->baseplot ); + astGrid( baseplot ); + +/* We next use astGrid to draw a grid on the 2D plane that shares the first + base plot graphics axis. Get the identifier for this Plot and the 2D + graphics axis index within the Plot that corresponds to the first base + plot graphics axis. Also get the constant value on the 3rd graphics + axis over the base plot */ + rootcorner = astGetRootCorner( this ); + if( this->baseplot == XY ) { + new_plot = XZ; + new_gaxis = 0; + gcon = this->gbox[ ( rootcorner & 4 ) ? 5 : 2 ]; + + } else if( this->baseplot == XZ ) { + new_plot = XY; + new_gaxis = 0; + gcon = this->gbox[ ( rootcorner & 2 ) ? 4 : 1 ]; + + } else { + new_plot = XY; + new_gaxis = 1; + gcon = this->gbox[ ( rootcorner & 1 ) ? 3 : 0 ]; + } + +/* Get a pointer to the Plot upon which astGrid is about to be invoked. */ + plot = GET_PLOT( new_plot ); + +/* Find which 2D WCS axis was labelled on graphics axis 0 (the bottom or + top edge) of the base plot. */ + if( ( edge = astGetC( baseplot, "Edge(1)" ) ) ) { + base_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1; + } else { + base_wax2d = 0; + } + +/* Get the 2D graphics coords within the base Plot at which the tick + marks were drawn for this 2D WCS axis. Extend the PointSets holding + the major tick values to include an extra tick above and below the + ticks drawn by astGrid. These extra ticks are placed outside the + bounds of the plot. This ensures that the curves on the other axis + extend the full width of the plot. */ + tmp = astGetDrawnTicks( baseplot, base_wax2d, 1 ); + if( tmp ) { + majticks = ExtendTicks( baseplot, tmp, status ); + nmaj = astGetNpoint( majticks ); + ptrmaj = astGetPoints( majticks ); + tmp = astAnnul( tmp ); + } else { + majticks = NULL; + nmaj = 0; + ptrmaj = NULL; + } + + minticks = astGetDrawnTicks( baseplot, base_wax2d, 0 ); + if( minticks ) { + nmin = astGetNpoint( minticks ); + ptrmin = astGetPoints( minticks ); + } else { + nmin = 0; + ptrmin = NULL; + } + +/* All the tick marks will have a constant value for the 2D graphics axis + that is not being ticked (axis 1 at the moment). Change this constant + value to be the value appropriate to the new Plot. */ + if( ptrmaj && ptrmin ) { + for( itick = 0; itick < nmaj; itick++ ) ptrmaj[ 1 ][ itick ] = gcon; + for( itick = 0; itick < nmin; itick++ ) ptrmin[ 1 ][ itick ] = gcon; + } + +/* If required, permute the axes in the tick mark positions. */ + if( new_gaxis != 0 ) { + perm[ 0 ] = 1; + perm[ 1 ] = 0; + if( majticks ) astPermPoints( majticks, 1, perm ); + if( minticks ) astPermPoints( minticks, 1, perm ); + } + +/* Transform the tick mark positions from 2D graphics coords to 2D WCS + coords. */ + wcsmajticks = majticks ? astTransform( plot, majticks, 1, NULL ) : NULL; + wcsminticks = minticks ? astTransform( plot, minticks, 1, NULL ) : NULL; + +/* Find the index of the 2D WCS axis that will be labelled on the bottom + or top edge (i.e. 2D graphics axis zero) of the new Plot. */ + if( ( edge = astGetC( plot, "Edge(1)" ) ) ) { + new_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1; + } else { + new_wax2d = 0; + } + +/* Use the other WCS axis if we are ticking the left or right edge (i.e. + 2D graphics axis one) of the new Plot. This gives us the index of the + 2D WCS axis for which tick values are to be stored in the Plot. */ + if( new_gaxis == 1 ) new_wax2d = 1 - new_wax2d; + +/* Store the tick mark values to be used on this WCS axis. */ + ptrmaj = wcsmajticks ? astGetPoints( wcsmajticks ) : NULL; + ptrmin = wcsminticks ? astGetPoints( wcsminticks ) : NULL; + if( ptrmaj && ptrmin ) { + astSetTickValues( plot, new_wax2d, nmaj, ptrmaj[ new_gaxis ], + nmin, ptrmin[ new_gaxis ] ); + } + +/* Invoke the astGrid method on this plot. */ + astGrid( plot ); + +/* Clear the stored tick values in the plot. */ + astSetTickValues( plot, new_wax2d, 0, NULL, 0, NULL ); + +/* Free resources */ + if( wcsmajticks ) wcsmajticks = astAnnul( wcsmajticks ); + if( wcsminticks ) wcsminticks = astAnnul( wcsminticks ); + if( majticks ) majticks = astAnnul( majticks ); + if( minticks ) minticks = astAnnul( minticks ); + +/* We next use astGrid to draw a grid on the 2D plane that shares the + second base plot graphics axis. Get the identifier for this Plot and the + 2D graphics axis index within the Plot that corresponds to the first + base plot graphics axis. */ + if( this->baseplot == XY ) { + new_plot = YZ; + new_gaxis = 0; + + } else if( this->baseplot == XZ ) { + new_plot = YZ; + new_gaxis = 1; + + } else { + new_plot = XZ; + new_gaxis = 1; + } + +/* Get a pointer to the Plot upon which astGrid is about to be invoked. */ + plot = GET_PLOT( new_plot ); + +/* Find which 2D WCS axis was labelled on graphics axis 1 (the left or + right edge) of the base plot. */ + base_wax2d = 1 - base_wax2d; + +/* Get the 2D graphics coords within the base Plot at which the tick + marks were drawn for this 2D WCS axis. Extend the PointSets holding + the major tick values to include an extra tick above and below the + ticks drawn by astGrid. These extra ticks are placed outside the + bounds of the plot. This ensures that the curves on the other axis + extend the full width of the plot. */ + tmp = astGetDrawnTicks( baseplot, base_wax2d, 1 ); + if( tmp ) { + majticks = ExtendTicks( baseplot, tmp, status ); + nmaj = astGetNpoint( majticks ); + ptrmaj = astGetPoints( majticks ); + tmp = astAnnul( tmp ); + } else { + majticks = NULL; + nmaj = 0; + ptrmaj = NULL; + } + + minticks = astGetDrawnTicks( baseplot, base_wax2d, 0 ); + if( minticks ) { + nmin = astGetNpoint( minticks ); + ptrmin = astGetPoints( minticks ); + } else { + nmin = 0; + ptrmin = NULL; + } + +/* All the tick marks will have a constant value for the 2D graphics axis + that is not being ticked (axis 0 at the moment). Change this constant + value to be the value appropriate to the new Plot. */ + if( ptrmaj && ptrmin ) { + for( itick = 0; itick < nmaj; itick++ ) ptrmaj[ 0 ][ itick ] = gcon; + for( itick = 0; itick < nmin; itick++ ) ptrmin[ 0 ][ itick ] = gcon; + } + +/* If required, permute the axes in the tick mark positions. */ + if( new_gaxis != 1 ) { + perm[ 0 ] = 1; + perm[ 1 ] = 0; + if( majticks ) astPermPoints( majticks, 1, perm ); + if( minticks ) astPermPoints( minticks, 1, perm ); + } + +/* Transform the tick mark positions from 2D graphics coords to 2D WCS + coords. */ + wcsmajticks = majticks ? astTransform( plot, majticks, 1, NULL ) : NULL; + wcsminticks = minticks ? astTransform( plot, minticks, 1, NULL ) : NULL; + +/* Find the index of the 2D WCS axis that will be labelled on the bottom + or top edge (i.e. 2D graphics axis zero) of the new Plot. */ + if( ( edge = astGetC( plot, "Edge(1)" ) ) ) { + new_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1; + } else { + new_wax2d = 0; + } + +/* Use the other WCS axis if we are ticking the left or right edge (i.e. + 2D graphics axis one) of the new Plot. This gives us the index of the + 2D WCS axis for which tick values are to be stored in the Plot. */ + if( new_gaxis == 1 ) new_wax2d = 1 - new_wax2d; + +/* Store the tick mark values to be used on this WCS axis. */ + ptrmaj = wcsmajticks ? astGetPoints( wcsmajticks ) : NULL; + ptrmin = wcsminticks ? astGetPoints( wcsminticks ) : NULL; + if( ptrmaj && ptrmin ) { + astSetTickValues( plot, new_wax2d, nmaj, ptrmaj[ new_gaxis ], + nmin, ptrmin[ new_gaxis ] ); + } + +/* Invoke the astGrid method on this plot. */ + astGrid( plot ); + +/* Clear the stored tick values in the plot. */ + astSetTickValues( plot, new_wax2d, 0, NULL, 0, NULL ); + +/* Free resources */ + if( wcsmajticks ) wcsmajticks = astAnnul( wcsmajticks ); + if( wcsminticks ) wcsminticks = astAnnul( wcsminticks ); + if( majticks ) majticks = astAnnul( majticks ); + if( minticks ) minticks = astAnnul( minticks ); + +} + +void astInitPlot3DVtab_( AstPlot3DVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPlot3DVtab + +* Purpose: +* Initialise a virtual function table for a Plot3D. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot3d.h" +* void astInitPlot3DVtab( AstPlot3DVtab *vtab, const char *name ) + +* Class Membership: +* Plot3D vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Plot3D class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *dummy_frame; /* The Frame to put in dummy_frameset */ + AstPlotVtab *plot; /* Pointer to Plot component of Vtab */ + AstFrameSetVtab *fset; /* Pointer to FrameSet component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitPlotVtab( (AstPlotVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPlot3D) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstPlotVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +#define SET_PLOT3D_ACCESSORS(attr) \ + vtab->Set##attr = Set##attr; \ + vtab->Get##attr = Get##attr; \ + vtab->Test##attr = Test##attr; \ + vtab->Clear##attr = Clear##attr; + +SET_PLOT3D_ACCESSORS(RootCorner) +SET_PLOT3D_ACCESSORS(Norm) + +#undef SET_PLOT3D_ACCESSORS + + + + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + fset = (AstFrameSetVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + plot = (AstPlotVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_vset = object->VSet; + object->VSet = VSet; + + parent_cast = object->Cast; + object->Cast = Cast; + + parent_clear = object->Clear; + object->Clear = Clear; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_clearcurrent = fset->ClearCurrent; + fset->ClearCurrent = ClearCurrent; + + parent_setcurrent = fset->SetCurrent; + fset->SetCurrent = SetCurrent; + + parent_removeframe = fset->RemoveFrame; + fset->RemoveFrame = RemoveFrame; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + +/* Define a macro to override attribute accessors inherited from the + parent Plot class. First do axis specific attributes. */ + +#define SET_PLOT_ACCESSORS(attr) \ + parent_get##attr = plot->Get##attr; \ + plot->Get##attr = Get##attr; \ + parent_set##attr = plot->Set##attr; \ + plot->Set##attr = Set##attr; \ + parent_clear##attr = plot->Clear##attr; \ + plot->Clear##attr = Clear##attr; + +/* Use the above macro to override all the inherited attribute accessors. */ +SET_PLOT_ACCESSORS(MinTick) +SET_PLOT_ACCESSORS(Abbrev) +SET_PLOT_ACCESSORS(Gap) +SET_PLOT_ACCESSORS(LogGap) +SET_PLOT_ACCESSORS(LogPlot) +SET_PLOT_ACCESSORS(LogTicks) +SET_PLOT_ACCESSORS(LogLabel) +SET_PLOT_ACCESSORS(LabelUp) +SET_PLOT_ACCESSORS(DrawAxes) +SET_PLOT_ACCESSORS(LabelUnits) +SET_PLOT_ACCESSORS(MinTickLen) +SET_PLOT_ACCESSORS(MajTickLen) +SET_PLOT_ACCESSORS(NumLab) +SET_PLOT_ACCESSORS(NumLabGap) +SET_PLOT_ACCESSORS(TextLab) +SET_PLOT_ACCESSORS(TextLabGap) + +#undef SET_PLOT_ACCESSORS + + +/* Now do attributes that are not axis specific. */ + +#define SET_PLOT_ACCESSORS(attr) \ + parent_set##attr = plot->Set##attr; \ + plot->Set##attr = Set##attr; \ + parent_clear##attr = plot->Clear##attr; \ + plot->Clear##attr = Clear##attr; + +SET_PLOT_ACCESSORS(Ink) +SET_PLOT_ACCESSORS(Tol) +SET_PLOT_ACCESSORS(Invisible) +SET_PLOT_ACCESSORS(TickAll) +SET_PLOT_ACCESSORS(ForceExterior) +SET_PLOT_ACCESSORS(Border) +SET_PLOT_ACCESSORS(Clip) +SET_PLOT_ACCESSORS(ClipOp) +SET_PLOT_ACCESSORS(Escape) +SET_PLOT_ACCESSORS(Grid) +SET_PLOT_ACCESSORS(Labelling) +SET_PLOT_ACCESSORS(Style) +SET_PLOT_ACCESSORS(Font) +SET_PLOT_ACCESSORS(Colour) +SET_PLOT_ACCESSORS(Width) +SET_PLOT_ACCESSORS(Size) + +#undef SET_PLOT_ACCESSORS + + +/* Store replacement pointers for methods which will be over-ridden by new + member functions implemented here. */ + plot->Grid = Grid; + plot->Text = Text; + plot->SetTickValues = SetTickValues; + plot->PolyCurve = PolyCurve; + plot->Border = Border; + plot->BoundingBox = BoundingBox; + plot->GetGrfContext = GetGrfContext; + plot->GrfPop = GrfPop; + plot->GrfPush = GrfPush; + plot->GrfSet = GrfSet; + plot->GridLine = GridLine; + plot->Mark = Mark; + plot->Curve = Curve; + plot->GenCurve = GenCurve; + plot->Clip = Clip; + mapping->Transform = Transform; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "Plot3D", "Provide facilities for 3D graphical output" ); + +/* Create a FrameSet that can be used when calling astCast to indicate + the class to which we want to cast. */ + LOCK_MUTEX3 + if( !dummy_frameset ) { + dummy_frame = astFrame( 1, " ", status ); + dummy_frameset = astFrameSet( dummy_frame, " ", status ); + dummy_frame = astAnnul( dummy_frame ); + } + UNLOCK_MUTEX3 + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstPlot3D *this; /* Pointer to Plot3D structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the Plot3D structure. */ + this = (AstPlot3D *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->plotxy, mode, extra, fail ); + if( !result ) result = astManageLock( this->plotxz, mode, extra, fail ); + if( !result ) result = astManageLock( this->plotyz, mode, extra, fail ); + + return result; + +} +#endif + +static void Mark( AstPlot *this_plot, int nmark, int ncoord, int indim, + const double *in, int type, int *status ){ +/* +* Name: +* Mark + +* Purpose: +* Draw a set of markers for a Plot3D. + +* Type: +* Private member function. + +* Synopsis: +* #include "plot3d.h" +* void Mark( AstPlot *this, int nmark, int ncoord, int indim, +* const double *in, int type, int *status ) + +* Class Membership: +* Plot3d member function (overrides the astMark method inherited form +* the parent Plot class). + +* Description: +* This function draws a set of markers (symbols) at positions +* specified in the physical coordinate system of a Plot3D. The +* positions are transformed into graphical coordinates to +* determine where the markers should appear within the plotting +* area. +* +* They are drawn on a 2D plane that has a normal vector given by the +* current value of the Plot3D's "Norm" attribute. + +* Parameters: +* this +* Pointer to the Plot3D. +* nmark +* The number of markers to draw. This may be zero, in which +* case nothing will be drawn. +* ncoord +* The number of coordinates being supplied for each mark +* (i.e. the number of axes in the current Frame of the Plot, as +* given by its Naxes attribute). +* indim +* The number of elements along the second dimension of the "in" +* array (which contains the marker coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +* given should not be less than "nmark". +* in +* The address of the first element of a 2-dimensional array of +* shape "[ncoord][indim]" giving the +* physical coordinates of the points where markers are to be +* drawn. These should be stored such that the value of +* coordinate number "coord" for input mark number "mark" is +* found in element "in[coord][mark]". +* type +* A value specifying the type (e.g. shape) of marker to be +* drawn. The set of values which may be used (and the shapes +* that will result) is determined by the underlying graphics +* system. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Markers are not drawn at positions which have any coordinate +* equal to the value AST__BAD (or where the transformation into +* graphical coordinates yields coordinates containing the value +* AST__BAD). +* - An error results if the base Frame of the Plot is not 3-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*/ + +/* Local Variables: */ + AstMapping *mapping; /* Pointer to graphics->physical mapping */ + AstPlot3D *this; /* Pointer to the Plot3D structure */ + AstPointSet *pset1; /* PointSet holding physical positions */ + AstPointSet *pset2; /* PointSet holding graphics positions */ + const char *class; /* Object class */ + const char *method; /* Current method */ + const double **ptr1; /* Pointer to physical positions */ + double **ptr2; /* Pointer to graphics positions */ + double *xpd; /* Pointer to next double precision x value */ + double *ypd; /* Pointer to next double precision y value */ + double *zpd; /* Pointer to next double precision z value */ + float *x; /* Pointer to single precision x values */ + float *xpf; /* Pointer to next single precision x value */ + float *y; /* Pointer to single precision y values */ + float *ypf; /* Pointer to next single precision y value */ + float *z; /* Pointer to single precision z values */ + float *zpf; /* Pointer to next single precision z value */ + float norm[ 3 ]; /* Single precision normal vector */ + int axis; /* Axis index */ + int i; /* Loop count */ + int naxes; /* No. of axes in the base Frame */ + int nn; /* Number of good marker positions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_plot; + +/* Store the current method and class for inclusion in error messages + generated by lower level functions. */ + method = "astMark"; + class = astClass( this ); + +/* Check the base Frame of the Plot is 3-D. */ + naxes = astGetNin( this ); + if( naxes != 3 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 3.", status, method, class, naxes, class ); + } + +/* Also validate the input array dimension argument. */ + if ( astOK && ( indim < nmark ) ) { + astError( AST__DIMIN, "%s(%s): The input array dimension value " + "(%d) is invalid.", status, method, class, indim ); + astError( AST__DIMIN, "This should not be less than the number of " + "markers being drawn (%d).", status, nmark ); + } + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__MARKS_ID, 1, GRF__MARK, method, class ); + +/* Create a PointSet to hold the supplied physical coordinates. */ + pset1 = astPointSet( nmark, ncoord, "", status ); + +/* Allocate memory to hold pointers to the first value on each axis. */ + ptr1 = (const double **) astMalloc( sizeof( const double * )* + (size_t)( ncoord )); + +/* Check the pointer can be used, then store pointers to the first value + on each axis. */ + if( astOK ){ + for( axis = 0; axis < ncoord; axis++ ){ + ptr1[ axis ] = in + axis*indim; + } + } + +/* Store these pointers in the PointSet. */ + astSetPoints( pset1, (double **) ptr1 ); + +/* Transform the supplied data from the current frame (i.e. physical + coordinates) to the base frame (i.e. graphics coordinates) using + the inverse Mapping defined by the Plot. */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + pset2 = astTransform( mapping, pset1, 0, NULL ); + mapping = astAnnul( mapping ); + +/* Get pointers to the graphics coordinates. */ + ptr2 = astGetPoints( pset2 ); + +/* Allocate memory to hold single precision versions of the graphics + coordinates. */ + x = (float *) astMalloc( sizeof( float )*(size_t) nmark ); + y = (float *) astMalloc( sizeof( float )*(size_t) nmark ); + z = (float *) astMalloc( sizeof( float )*(size_t) nmark ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* Store pointers to the next single and double precision x, y and z + values. */ + xpf = x; + ypf = y; + zpf = z; + xpd = ptr2[ 0 ]; + ypd = ptr2[ 1 ]; + zpd = ptr2[ 2 ]; + +/* Convert the double precision values to single precision, rejecting + any bad marker positions. */ + nn = 0; + for( i = 0; i < nmark; i++ ){ + if( *xpd != AST__BAD && *ypd != AST__BAD && *zpd != AST__BAD ){ + nn++; + *(xpf++) = (float) *(xpd++); + *(ypf++) = (float) *(ypd++); + *(zpf++) = (float) *(zpd++); + } else { + xpd++; + ypd++; + zpd++; + } + } + +/* If the nornmal vector has non-zero length, draw the remaining markers. */ + norm[ 0 ] = (float) astGetNorm( this, 0 ); + norm[ 1 ] = (float) astGetNorm( this, 1 ); + norm[ 2 ] = (float) astGetNorm( this, 2 ); + + if( norm[ 0 ] != 0.0 || norm[ 1 ] != 0.0 || norm[ 2 ] != 0.0 ){ + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + + if( !astG3DMark( nn, x, y, z, type, norm ) ) { + astError( AST__GRFER, "%s(%s): Graphics error in astG3DMark. ", status, + method, class ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + + } else if( astOK ) { + astError( AST__ATTIN, "%s(%s): The vector specified by the Norm " + "attribute has zero length.", status, method, class ); + } + } + +/* Free the memory used to store single precision graphics coordinates. */ + x = (float *) astFree( (void *) x ); + y = (float *) astFree( (void *) y ); + z = (float *) astFree( (void *) z ); + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Free the memory holding the pointers to the first value on each axis. */ + ptr1 = (const double **) astFree( (void *) ptr1 ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__MARKS_ID, 0, GRF__MARK, method, class ); + +/* Return */ + return; +} + +static int Plot3DAttr( AstKeyMap *grfconID, int attr, double value, + double *old_value, int prim ){ +/* +* Name: +* Plot3DAttr + +* Purpose: +* Get or set the value of a 3D grf attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DAttr( AstKeyMap *grfconID, int attr, double value, +* double *old_value, int prim ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* get or set the current value of a specified graphics attribute. It +* forwards the call to the grf3D module being used by this Plot3D. It +* should be registered with each of the 2D Plots using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is used to identify which of +* the three Plots is calling this function. +* attr +* An integer value identifying the required attribute. This should +* be one of the symbolic values defined in grf.h. +* value +* A new value to store for the attribute. If this is AST__BAD +* no value is stored. +* old_value +* A pointer to a double in which to return the attribute value. +* If this is NULL, no value is returned. +* prim +* The sort of graphics primitive to be drawn with the new attribute. +* Identified by one of the values defined in grf.h. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + int result; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Use the function in the external Grf3D module, selected at link-time + using ast_link options. Note, attribute values are the same for each + of the three Plot. */ + result = astG3DAttr( attr, value, old_value, prim ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Return the result. */ + return result; +} + +static int Plot3DCap( AstKeyMap *grfconID, int cap, int value ){ +/* +* Name: +* Plot3DCap + +* Purpose: +* Determine if the 3D grf module has a given capability. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DCap( AstKeyMap *grfconID, int cap, int value ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* determine if a given graphics capability is available. It forwards +* the call to the grf3D module being used by this Plot3D. It should be +* registered with each of the 2D Plots using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. +* cap +* The capability being inquired about. This will be one of the +* following constants defined in grf.h: GRF__SCALES, GRF__MJUST, +* GRF__ESC, +* value +* The use of this parameter depends on the value of "cap" as +* described in the documentation for the astGrfSet function in the +* Plot class. + +* Returned Value: +* The value returned by the function depends on the value of "cap" +* as described in the astGrfSet documentation. Zero is returned if +* the supplied capability is not recognised. + +*/ + +/* Local Variables: */ + int result; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* The astGScales function is implemented by code within the Plot3D class + (not within the grf3D module). The Plot3D class assumes all axes are + equally scaled. */ + if( cap == GRF__SCALES ) { + result = 1; + +/* All grf3D modules are assumed to support "M" justification. */ + } else if( cap == GRF__MJUST ) { + result = 1; + +/* Forward all other capability requests to the grf3D module. */ + } else { + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + + result = astG3DCap( cap, value ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + } + +/* Return the result. */ + return result; +} + +static int Plot3DFlush( AstKeyMap *grfconID ){ +/* +* Name: +* Plot3DFlush + +* Purpose: +* Flush any buffered graphical output. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DFlush( AstKeyMap *grfconID ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* ensure that the display device is up-to-date by flushing any pending +* graphics to the output device. It forwards the call to the grf3D module +* being used by this Plot3D. It should be registered with each of the +* 2D Plots using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + int result; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Use the function in the external Grf3D module, selected at link-time + using ast_link options. */ + result = astG3DFlush(); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Return the result. */ + return result; +} + +static int Plot3DLine( AstKeyMap *grfconID, int n, const float *x, const float *y ){ +/* +* Name: +* Plot3DLine + +* Purpose: +* Draw a polyline on a 2D surface. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DLine( AstKeyMap *grfconID, int n, const float *x, +* const float *y ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* draw a polyline. It forwards the call to the grf3D module being used +* by this Plot3D. It should be registered with each of the 2D Plots +* using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. +* n +* The number of positions to be joined together. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + AstKeyMap *grfcon; + double gcon; + float *work; + float *x3d = NULL; + float *y3d = NULL; + float *z3d = NULL; + int i; + int plane; + int result = 0; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a genuine pointer from the supplied grfcon identifier. */ + grfcon = astMakePointer( grfconID ); + +/* Report an error if no grfcon object was supplied. */ + if( !grfcon ) { + astError( AST__INTER, "astG3DLine(Plot3D): No grfcon Object supplied " + "(internal AST programming error)." , status); + +/* If a grfcon Object was supplied, get the graphics box array out of it. */ + } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) { + astError( AST__INTER, "astG3DLine(Plot3D): No \"Gcon\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* Also get the plane index out of it. */ + } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) { + astError( AST__INTER, "astG3DLine(Plot3D): No \"Plane\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + } + +/* Allocate memory to hold the "n" values for the missing coordinate. */ + work = astMalloc( sizeof( float )*(size_t) n ); + if( work ) { + +/* Set up pointers to the x, y and z arrays in the 3D graphics system. + Fill the missing array with suitable values (the constant value of + the third axis on the plane being drawn). */ + if( plane == XY ) { + x3d = (float *) x; + y3d = (float *) y; + z3d = work; + + for( i = 0; i < n; i++ ) z3d[ i ] = gcon; + + } else if( plane == XZ ) { + x3d = (float *) x; + y3d = work; + z3d = (float *) y; + for( i = 0; i < n; i++ ) y3d[ i ] = gcon; + + } else if( plane == YZ ) { + x3d = work; + y3d = (float *) x; + z3d = (float *) y; + for( i = 0; i < n; i++ ) x3d[ i ] = gcon; + + } else { + astError( AST__INTER, "astG3DLine(Plot3D): Illegal plane " + "identifier %d supplied (internal AST programming " + "error).", status, plane ); + } + +/* If ok, draw the lines in the 3D graphics coordinate space. */ + if( x3d ) { + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Draw the line */ + result = astG3DLine( n, x3d, y3d, z3d ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + } + } + +/* Free resources. */ + work = astFree( work ); + +/* Return the result. */ + return result; +} + +static int Plot3DMark( AstKeyMap *grfconID, int n, const float *x, + const float *y, int type ){ +/* +* Name: +* Plot3DMark + +* Purpose: +* Draw a set of markers. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DMark( AstKeyMap *grfconID, int n, const float *x, +* const float *y, int type ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* draw a set of markers. It forwards the call to the grf3D module being +* used by this Plot3D. It should be registered with each of the 2D Plots +* using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. +* n +* The number of markers to draw. +* x +* A pointer to an array holding the "n" x values. +* y +* A pointer to an array holding the "n" y values. +* type +* An integer which can be used to indicate the type of marker symbol +* required. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + AstKeyMap *grfcon; + double gcon; + float *work; + float *x3d = NULL; + float *y3d = NULL; + float *z3d = NULL; + float norm[ 3 ]; + int i; + int plane; + int rc; + int result = 0; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a genuine pointer from the supplied grfcon identifier. */ + grfcon = astMakePointer( grfconID ); + +/* Report an error if no grfcon object was supplied. */ + if( !grfcon ) { + astError( AST__INTER, "astG3DMark(Plot3D): No grfcon Object supplied " + "(internal AST programming error)." , status); + +/* If a grfcon Object was supplied, get the graphics box array out of it. */ + } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) { + astError( AST__INTER, "astG3DMark(Plot3D): No \"Gcon\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* If a grfcon Object was supplied, get the RootCorner value out of it. */ + } else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) { + astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* Also get the plane index out of it. */ + } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) { + astError( AST__INTER, "astG3DMark(Plot3D): No \"Plane\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + } + +/* Allocate memory to hold the "n" values for the missing coordinate. */ + work = astMalloc( sizeof( float )*(size_t) n ); + if( work ) { + +/* Set up pointers to the x, y and z arrays in the 3D graphics system. + Fill the missing array with suitable values (the constant value of + the third axis on the plane being drawn). Set the normal vector for + the markers so that they are drawn in the plane described by the Plot.*/ + if( plane == XY ) { + x3d = (float *) x; + y3d = (float *) y; + z3d = work; + for( i = 0; i < n; i++ ) z3d[ i ] = gcon; + norm[ 0 ] = 0; + norm[ 1 ] = 0; + norm[ 2 ] = ( rc & 4 ) ? 1 : -1; + + } else if( plane == XZ ) { + x3d = (float *) x; + y3d = work; + z3d = (float *) y; + for( i = 0; i < n; i++ ) y3d[ i ] = gcon; + norm[ 0 ] = 0; + norm[ 1 ] = ( rc & 2 ) ? 1 : -1; + norm[ 2 ] = 0; + + } else if( plane == YZ ) { + x3d = work; + y3d = (float *) x; + z3d = (float *) y; + for( i = 0; i < n; i++ ) x3d[ i ] = gcon; + norm[ 0 ] = ( rc & 1 ) ? 1 : -1; + norm[ 1 ] = 0; + norm[ 2 ] = 0; + + } else { + astError( AST__INTER, "astG3DMark(Plot3D): Illegal plane " + "identifier %d supplied (internal AST programming " + "error).", status, plane ); + } + +/* If ok, draw the markers in the 3D graphics coordinate space. */ + if( x3d ) { + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Draw the markers */ + result = astG3DMark( n, x3d, y3d, z3d, type, norm ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + } + } + +/* Free resources. */ + work = astFree( work ); + +/* Return the result. */ + return result; +} + +static int Plot3DQch( AstKeyMap *grfconID, float *chv, float *chh ){ +/* +* Name: +* Plot3DQch + +* Purpose: +* Get the current text size. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DQch( AstKeyMap *grfconID, float *chv, float *chh ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* get the current text size. It forwards the call to the grf3D module +* being used by this Plot3D. It should be registered with each of the +* 2D Plots using astGrfSet. +* +* The grf3D module assumes that the 3D graphics axes are equally +* scaled and therefore the text height does not depend on the text +* orientation. Therefore, "chv" and "chh" are returned holding the +* same value. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. +* chv +* A pointer to the float which is to receive the height of +* characters drawn with a vertical baseline in the 2D Plot. This +* will be an increment in the 2D X axis. +* chh +* A pointer to the float which is to receive the height of +* characters drawn with a horizontal baseline in the 2D Plot. This +* will be an increment in the 2D Y axis. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + int result; + float ch; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* Use the function in the external Grf3D module, selected at link-time + using ast_link options. Note, text height is the same for each + of the three Plot. */ + result = astG3DQch( &ch ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + +/* Store the value in both the returned values. */ + *chv = ch; + *chh = ch; + +/* Return the error flag. */ + return result; +} + +static int Plot3DScales( AstKeyMap *grfconID, float *alpha, float *beta ){ +/* +* Name: +* Plot3DScales + +* Purpose: +* Get the 2D axis scales. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DScales( AstKeyMap *grfconID, float *alpha, float *beta ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* get the relative scales of the 2D axes. Since the grf3D module +* assumes that all graphics axes are equally scaled, it just returns 1.0 +* for alpha and beta. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. +* alpha +* A pointer to the float which is to receive the scale for the X +* axis +* beta +* A pointer to the float which is to receive the scale for the Y +* axis + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + + *alpha = 1.0; + *beta = 1.0; + return 1; +} + +static int Plot3DText( AstKeyMap *grfconID, const char *text, float x, float y, + const char *just, float upx, float upy ){ +/* +* Name: +* Plot3DText + +* Purpose: +* Draw a text string. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DText( AstKeyMap *grfconID, const char *text, float x, float y, +* const char *just, float upx, float upy ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* draw a text string. It forwards the call to the grf3D module being +* used by this Plot3D. It should be registered with each of the 2D Plots +* using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. +* upx +* The x component of the up-vector for the text. +* upy +* The y component of the up-vector for the text. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + AstKeyMap *grfcon; + double gcon; + float norm[ 3 ]; + float ref[ 3 ]; + float up[ 3 ]; + int plane; + int rc; + int result = 0; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a genuine pointer from the supplied grfcon identifier. */ + grfcon = astMakePointer( grfconID ); + +/* Report an error if no grfcon object was supplied. */ + if( !grfcon ) { + astError( AST__INTER, "astG3DText(Plot3D): No grfcon Object supplied " + "(internal AST programming error)." , status); + +/* If a grfcon Object was supplied, get the graphics box array out of it. */ + } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) { + astError( AST__INTER, "astG3DText(Plot3D): No \"Gcon\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* If a grfcon Object was supplied, get the RootCorner value out of it. */ + } else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) { + astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* Also get the plane index out of it. */ + } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) { + astError( AST__INTER, "astG3DText(Plot3D): No \"Plane\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* If OK, draw the text. */ + } else { + +/* Set up the reference, up and normal vectors so that the text appears + on the required plane. */ + if( plane == XY ) { + ref[ 0 ] = x; + ref[ 1 ] = y; + ref[ 2 ] = gcon; + norm[ 0 ] = 0; + norm[ 1 ] = 0; + norm[ 2 ] = ( rc & 4 ) ? 1 : -1; + up[ 0 ] = upx; + up[ 1 ] = upy; + up[ 2 ] = 0; + + } else if( plane == XZ ) { + ref[ 0 ] = x; + ref[ 1 ] = gcon; + ref[ 2 ] = y; + norm[ 0 ] = 0; + norm[ 1 ] = ( rc & 2 ) ? 1 : -1; + norm[ 2 ] = 0; + up[ 0 ] = upx; + up[ 1 ] = 0; + up[ 2 ] = upy; + + } else if( plane == YZ ) { + ref[ 0 ] = gcon; + ref[ 1 ] = x; + ref[ 2 ] = y; + norm[ 0 ] = ( rc & 1 ) ? 1 : -1; + norm[ 1 ] = 0; + norm[ 2 ] = 0; + up[ 0 ] = 0; + up[ 1 ] = upx; + up[ 2 ] = upy; + + } else { + astError( AST__INTER, "astG3DText(Plot3D): Illegal plane " + "identifier %d supplied (internal AST programming " + "error).", status, plane ); + } + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If ok, draw the markers in the 3D graphics coordinate space. */ + if( astOK ) result = astG3DText( text, ref, just, up, norm ); + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + } + +/* Return the result. */ + return result; +} + +static int Plot3DTxExt( AstKeyMap *grfconID, const char *text, float x, float y, + const char *just, float upx, float upy, float *xb, + float *yb ){ +/* +* Name: +* Plot3DTxExt + +* Purpose: +* Determine the plotted extent of a text string. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int Plot3DTxExt( AstKeyMap *grfconID, const char *text, float x, +* float y, const char *just, float upx, float upy, +* float *xb, float *yb ) + +* Class Membership: +* Plot3D member function. + +* Description: +* This function is called by one of the three encapsulated 2D Plots to +* determine the extent a string would have if plotted using Plot3DText. +* It forwards the call to the grf3D module being used by this Plot3D. It +* should be registered with each of the 2D Plots using astGrfSet. + +* Parameters: +* grfconID +* The Plot's GrfContext KeyMap. This is +* used to identify which of the three Plots is calling this function. +* text +* Pointer to a null-terminated character string to be displayed. +* x +* The reference x coordinate. +* y +* The reference y coordinate. +* just +* A character string which specifies the location within the +* text string which is to be placed at the reference position +* given by x and y. +* upx +* The x component of the up-vector for the text. +* upy +* The y component of the up-vector for the text. +* xb +* An array of 4 elements in which to return the x coordinate of +* each corner of the bounding box. +* yb +* An array of 4 elements in which to return the y coordinate of +* each corner of the bounding box. + +* Returned Value: +* An integer value of 0 is returned if an error occurs, and 1 otherwise. + +*/ + +/* Local Variables: */ + AstKeyMap *grfcon; + double gcon; + float *xb3d = NULL; + float *yb3d = NULL; + float *zb3d = NULL; + float bl[ 3 ]; + float norm[ 3 ]; + float ref[ 3 ]; + float unused[ 4 ]; + float up[ 3 ]; + int plane; + int rc; + int result = 0; + int *status; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a genuine pointer from the supplied grfcon identifier. */ + grfcon = astMakePointer( grfconID ); + +/* Report an error if no grfcon object was supplied. */ + if( !grfcon ) { + astError( AST__INTER, "astG3DTxExt(Plot3D): No grfcon Object supplied " + "(internal AST programming error)." , status); + +/* If a grfcon Object was supplied, get the graphics box array out of it. */ + } else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) { + astError( AST__INTER, "astG3DTxExt(Plot3D): No \"Gcon\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* If a grfcon Object was supplied, get the RootCorner value out of it. */ + } else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) { + astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* Also get the plane index out of it. */ + } else if( !astMapGet0I( grfcon, "Plane", &plane ) ) { + astError( AST__INTER, "astG3DTxExt(Plot3D): No \"Plane\" key found " + "in the supplied grfcon Object (internal AST programming " + "error)." , status); + +/* If OK, get the extent of the text. */ + } else { + +/* Set up the reference, up and normal vectors so that the text appears + on the required plane. */ + if( plane == XY ) { + ref[ 0 ] = x; + ref[ 1 ] = y; + ref[ 2 ] = gcon; + norm[ 0 ] = 0; + norm[ 1 ] = 0; + norm[ 2 ] = ( rc & 4 ) ? 1 : -1; + up[ 0 ] = upx; + up[ 1 ] = upy; + up[ 2 ] = 0; + xb3d = xb; + yb3d = yb; + zb3d = unused; + + } else if( plane == XZ ) { + ref[ 0 ] = x; + ref[ 1 ] = gcon; + ref[ 2 ] = y; + norm[ 0 ] = 0; + norm[ 1 ] = ( rc & 2 ) ? 1 : -1; + norm[ 2 ] = 0; + up[ 0 ] = upx; + up[ 1 ] = 0; + up[ 2 ] = upy; + xb3d = xb; + yb3d = unused; + zb3d = yb; + + } else if( plane == YZ ) { + ref[ 0 ] = gcon; + ref[ 1 ] = x; + ref[ 2 ] = y; + norm[ 0 ] = ( rc & 1 ) ? 1 : -1; + norm[ 1 ] = 0; + norm[ 2 ] = 0; + up[ 0 ] = 0; + up[ 1 ] = upx; + up[ 2 ] = upy; + xb3d = unused; + yb3d = xb; + zb3d = yb; + + } else { + astError( AST__INTER, "astG3DTxExt(Plot3D): Illegal plane " + "identifier %d supplied (internal AST programming " + "error).", status, plane ); + } + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + +/* If ok, get the extent of the text. */ + if( astOK ) result = astG3DTxExt( text, ref, just, up, norm, xb3d, yb3d, + zb3d, bl ); +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + } + +/* Return the result. */ + return result; +} + +static void PolyCurve( AstPlot *this, int npoint, int ncoord, int indim, + const double *in, int *status ){ +/* +* Name: +* PolyCurve + +* Purpose: +* Draw a series of connected geodesic curves. + +* Type: +* Private member function. + +* Synopsis: +* #include "plot3d.h" +* void PolyCurve( AstPlot *this, int npoint, int ncoord, int indim, +* const double *in, int *status ) + +* Class Membership: +* Plot method (overrides the astPolyCurve method inherited form the +* Plot class) + +* Description: +* This function joins a series of points specified in the physical +* coordinate system of a Plot by drawing a sequence of geodesic +* curves. It is equivalent to making repeated use of the astCurve +* function (q.v.), except that astPolyCurve will generally be more +* efficient when drawing many geodesic curves end-to-end. A +* typical application of this might be in drawing contour lines. +* +* As with astCurve, full account is taken of the Mapping between +* physical and graphical coordinate systems. This includes any +* discontinuities and clipping established using astClip. + +* Parameters: +* this +* Pointer to the Plot. +* npoint +* The number of points between which geodesic curves are to be drawn. +* ncoord +* The number of coordinates being supplied for each point (i.e. +* the number of axes in the current Frame of the Plot, as given +* by its Naxes attribute). +* indim +* The number of elements along the second dimension of the "in" +* array (which contains the input coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +* given should not be less than "npoint". +* in +* The address of the first element in a 2-dimensional array of shape +* "[ncoord][indim]" giving the +* physical coordinates of the points which are to be joined in +* sequence by geodesic curves. These should be stored such that +* the value of coordinate number "coord" for point number +* "point" is found in element "in[coord][point]". +* status +* Pointer to the inherited status variable. + +* Notes: +* - No curve is drawn on either side of any point which has any +* coordinate equal to the value AST__BAD. +* - An error results if the base Frame of the Plot is not +* 2-dimensional. +* - An error also results if the transformation between the +* current and base Frames of the Plot is not defined (i.e. the +* Plot's TranInverse attribute is zero). +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + + astError( AST__INTER, "astPolyCurve(%s): The astPolyCurve " + "method cannot be used with a %s (programming error).", status, + astGetClass( this ), astGetClass( this ) ); +} + +static void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status ) { +/* +* Name: +* RemoveFrame + +* Purpose: +* Remove a Frame from a Plot3D. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "plot.h" +* void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the astRemoveFrame public +* method inherited from the Plot class). + +* Description: +* This function removes a Frame from a Plot3D. All other Frames +* in the Plot3D have their indices re-numbered from one (if +* necessary), but are otherwise unchanged. +* +* If the index of the original base Frame is changed, the index value +* stored in the Plot3D is updated. If the base Frame itself is +* removed, an error is reported. + +* Parameters: +* this_fset +* Pointer to the FrameSet component of the Plot3D. +* iframe +* The index within the Plot3D of the Frame to be removed. +* This value should lie in the range from 1 to the number of +* Frames in the Plot3D (as given by its Nframe attribute). +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPlot3D *this; /* Pointer to Plot3D structure */ + int ifrm; /* Validated frame index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_fset; + +/* Validate the frame index. This translated AST__BASE and AST__CURRENT + into actual Frame indices. */ + ifrm = astValidateFrameIndex( this_fset, iframe, "astRemoveFrame" ); + +/* Report an error if an attempt is made to delete the Frame that defines + the mapping onto the screen (the original base Frame in the FrameSet + supplied when the Plot3D was constructed). */ + if( ifrm == this->pix_frame ){ + astError( AST__PXFRRM, "astRemoveFrame(%s): Cannot delete Frame " + "number %d from the supplied %s since it is the Frame " + "that defines the mapping onto the graphics plane.", status, + astGetClass( this ), iframe, astGetClass( this ) ); + +/* Otherwise, invoke the parent astRemoveFrame method to remove the Frame. */ + } else { + (*parent_removeframe)( this_fset, iframe, status ); + +/* If the index of the removed Frame is smaller than the original base Frame + index, then decrement the original base Frame index so that the same Frame + will be used in future. */ + if( astOK ){ + if( ifrm < this->pix_frame ) (this->pix_frame)--; + } + } +} + +static int RootCornerInt( const char *rootcorner, int *status ){ +/* +* Name: +* RootCornerInt + +* Purpose: +* Convert a RootCorner string to an integer. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int RootCornerInt( const char *rootcorner, int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function converts a RootCorner string to an integer. + +* Parameters: +* rootcorner +* The string value to convert. Should be 3 characters long +* and contain nothing but "U" or "L" (upper or lower case). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The integer value that is is the protected equivalent of the +* supplied string. A negative value is returned if an error has +* already occurred, of the supplied string is not legal. + +*/ + +/* Local Variables: */ + int result; + +/* Initialise */ + result = -1; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Compare thr supplied value with every legal value. */ + if( astChrMatch( rootcorner, "LLL" ) ) { + result = LLL; + + } else if( astChrMatch( rootcorner, "ULL" ) ) { + result = ULL; + + } else if( astChrMatch( rootcorner, "LUL" ) ) { + result = LUL; + + } else if( astChrMatch( rootcorner, "UUL" ) ) { + result = UUL; + + } else if( astChrMatch( rootcorner, "LLU" ) ) { + result = LLU; + + } else if( astChrMatch( rootcorner, "ULU" ) ) { + result = ULU; + + } else if( astChrMatch( rootcorner, "LUU" ) ) { + result = LUU; + + } else if( astChrMatch( rootcorner, "UUU" ) ) { + result = UUU; + + } + +/* Return the result. */ + return result; +} + +static const char *RootCornerString( int rootcorner, int *status ){ +/* +* Name: +* RootCornerString + +* Purpose: +* Convert a RootCorner integer to a string. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* const char *RootCornerString( int rootcorner, int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function converts a RootCorner integer to a string. + +* Parameters: +* rootcorner +* The integer value to convert. Should be in the range 0 to 7. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a static string that is the public equivalent of the +* supplied integer. A NULL pointer is returned if an error has +* already occurred, of the supplied integer is not legal. + +*/ + +/* Local Variables: */ + char *result; + +/* Initialise */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Compare thr supplied value with every legal value. */ + if( rootcorner == LLL ) { + result = "LLL"; + + } else if( rootcorner == ULL ) { + result = "ULL"; + + } else if( rootcorner == LUL ) { + result = "LUL"; + + } else if( rootcorner == UUL ) { + result = "UUL"; + + } else if( rootcorner == LLU ) { + result = "LLU"; + + } else if( rootcorner == ULU ) { + result = "ULU"; + + } else if( rootcorner == LUU ) { + result = "LUU"; + + } else if( rootcorner == UUU ) { + result = "UUU"; + + } + +/* Return the result. */ + return result; +} + +static void Set3DGrf( AstPlot3D *this, AstPlot *plot, int plane, int *status ){ +/* +* Name: +* Set3DGrf + +* Purpose: +* Associate GRF functions with a Plot. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void Set3DGrf( AstPlot3D *this, AstPlot *plot, int plane, int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function registers grf functions defined in this class with +* the supplied Plot, so that, whenever the Plot draws anything, the +* plotting request is caught by this class and converted into an +* appropriate grf3D call. + +* Parameters: +* this +* Pointer to the Plot3D. +* plot +* Pointer to the Plot. +* plane +* An integer identifier for the plane within 3D GRAPHICS +* coordinates upon which the supplied Plot should draw. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstKeyMap *grfcon; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Register the plotting functions defined in this class, so that the + plot will call these functions to do its 2D plotting. */ + astGrfSet( plot, "Attr", (AstGrfFun) Plot3DAttr ); + astGrfSet( plot, "Cap", (AstGrfFun) Plot3DCap ); + astGrfSet( plot, "Flush", (AstGrfFun) Plot3DFlush ); + astGrfSet( plot, "Line", (AstGrfFun) Plot3DLine ); + astGrfSet( plot, "Mark", (AstGrfFun) Plot3DMark ); + astGrfSet( plot, "Qch", (AstGrfFun) Plot3DQch ); + astGrfSet( plot, "Scales", (AstGrfFun) Plot3DScales ); + astGrfSet( plot, "Text", (AstGrfFun) Plot3DText ); + astGrfSet( plot, "TxExt", (AstGrfFun) Plot3DTxExt ); + +/* Ensure that the Plot uses the grf interface registered using + astGrfSet. */ + astSetGrf( plot, 1 ); + +/* When the above functions are called, they need to know which plane + they are drawing on. So we put this information into the GrfContext + KeyMap stored in the Plot. This KeyMap will be passed to the above + drawing functions when they are called from within the Plot class. */ + grfcon = astGetGrfContext( plot ); + astMapPut0I( grfcon, "Plane", plane, "The 2D plane being drawn on" ); + if( plane == XY ) { + astMapPut0D( grfcon, "Gcon", this->gbox[2], "Constant Z value" ); + } else if( plane == XZ ) { + astMapPut0D( grfcon, "Gcon", this->gbox[1], "Constant Y value" ); + } else { + astMapPut0D( grfcon, "Gcon", this->gbox[0], "Constant X value" ); + } + astMapPut0I( grfcon, "RootCorner", astGetRootCorner(this), "The labelled corner" ); + grfcon = astAnnul( grfcon ); +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the astSetAttrib protected +* method inherited from the Plot class). + +* Description: +* This function assigns an attribute value for a Plot3D, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Plot3D. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPlot *plot; /* Pointer to specific Plot */ + AstPlot3D *this; /* Pointer to the Plot3D structure */ + char pat[30]; /* Regular expression pattern */ + char spec[10]; /* Plane specification */ + const char *null = ""; /* Pointer to null string */ + const char *psetting; /* Pointer to plot-specific attrib setting */ + double dval; /* Floating point attribute value */ + int axis; /* Axis index */ + int ival; /* Int attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Norm(axis). */ +/* ----------- */ + if ( nc = 0, + ( 2 == astSscanf( setting, "norm(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetNorm( this, axis - 1, dval ); + +/* RootCorner. */ +/* ----------- */ + } else if( nc = 0, + ( 0 == astSscanf( setting, "rootcorner=%n%*[^\n]%n", &ival, &nc ) ) + && ( nc >= len ) ) { + ival = RootCornerInt( setting + ival, status ); + if( astOK && ival < 0 ) { + astError( AST__ATTIN, "astSetAttrib(Plot3D): Unusable value \"%s\" " + "given for attribute RootCorner.", status, setting + ival ); + } else { + astSetRootCorner( this, ival ); + } + +/* ..._XY etc */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "%*[a-z]_%[xyz]%n", spec, &nc ) ) ) { + + if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) { + plot = this->plotxy; + } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) { + plot = this->plotyz; + } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) { + plot = this->plotxz; + } else { + plot = NULL; + } + + if( plot ) { + sprintf( pat, ".*(_%s).*", spec ); + psetting = astChrSub( setting, pat, &null, 1 ); + astSetAttrib( plot, psetting ); + psetting = astFree( (void *) psetting ); + + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void SetCurrent( AstFrameSet *this_frameset, int iframe, int *status ) { +/* +* Name: +* SetCurrent + +* Purpose: +* Set a value for the Current attribute of a Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int astSetCurrent( AstFrameSet *this, int iframe, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the public astSetCurrent method +* inherited from the FrameSet class). + +* Description: +* This function sets a value for the Current attribute of a +* Plot3D. This attribute is an index that identifies the current +* Frame for the Plot3D. + +* Parameters: +* this +* Pointer to the Plot3D. +* iframe +* Value to be set for the Current attribute. +* status +* Pointer to the inherited status variable. +*/ + +/* Invoke the parent astSetCurrent method. */ + (*parent_setcurrent)( this_frameset, iframe, status ); + +/* Update the three 2D Plots stored in the Plot3D structure so that they + reflect this modified FrameSet. */ + UpdatePlots( (AstPlot3D *) this_frameset, status ); +} + +static void SetPlotAttr( AstPlot *plot, int plane, int label[ 2 ], int *status ){ +/* +* Name: +* SetPlotAttr + +* Purpose: +* Set the attributes ofr one of the encapsulated Plots. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void SetPlotAttr( AstPlot *plot, int plane, int label[ 2 ], int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function sets the attributes of one of the encapsulated Plots. + +* Parameters: +* plot +* Pointer to the Plot to modify. +* plane +* The 3D plane spanned by the 2D plot. +* label +* Array indicating if each WCS axis should be labelled or not. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int axis; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Ensure that the Plot uses the grf interface registered using + astGrfSet. */ + astSetGrf( plot, 1 ); + +/* Ensure that no title is drawn. */ + astSetDrawTitle( plot, 0 ); + +/* For each axis, ensure that no axis labels or ticks are produced unless + the axis is indicated as a labelled axis in the supplied array. */ + for( axis = 0; axis < 2; axis++ ) { + if( !label[ axis ] ) { + astSetLabelUnits( plot, axis, 0 ); + astSetNumLab( plot, axis, 0 ); + astSetTextLab( plot, axis, 0 ); + } + } +} + +static void SetRootCorner( AstPlot3D *this, int rootcorner, int *status ){ +/* +*+ +* Name: +* astSetRootCorner + +* Purpose: +* Set a new value for the RootCorner attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "plot3d.h" +* void astSetRootCorner( AstPlot3D *this, int rootcorner ) + +* Class Membership: +* Plot method. + +* Description: +* This function sets a new value for the RootCorner attribute. + +* Parameters: +* this +* Pointer to a Plot3D. +* rootcorner +* The new RootCorner value. + +*- +*/ + +/* Check the global status. */ + if( !astOK ) return; + +/* Report an error if the new value is out of bounds. */ + if( rootcorner < 0 || rootcorner > 7 ){ + astError( AST__ATTIN, "astSetRootCorner(Plot3D): Invalid value %d " + "supplied for RootCorner attribute", status,rootcorner); + +/* If the new corner is OK, mirror any axes of the encapsulated Plots + that need mirroring (this is done to ensure that Plots look right when + viewed from the outside of the graphics cube), and modify the Edge + attributes in the encapsulated Plots to ensure the labels appear on the + requested edges of the 3D graphics cube. . */ + } else { + ChangeRootCorner( this, astGetRootCorner(this), rootcorner, status ); + +/* Store the new value. */ + this->rootcorner = rootcorner; + } +} + +static void SetTickValues( AstPlot *this, int axis, int nmajor, double *major, + int nminor, double *minor, int *status ){ +/* +* Name: +* SetTickValues + +* Purpose: +* Store the tick mark values to use for a given Plot axis. + +* Type: +* Private member function. + +* Synopsis: +* #include "plot3d.h" +* void SetTickValues( AstPlot *this, int axis, int nmajor, +* double *major, int nminor, double *minor, int *status ) + +* Class Membership: +* Plot method (overrides the astSetTickValues method inherited form +* the Plot class) + +* Description: +* This function stores a set of tick mark values that should be used by +* subsequent calls to astGrid. + +* Parameters: +* this +* Pointer to a Plot. +* axis +* The zero-based index of the axis for which tick marks values +* have been supplied. +* nmajor +* The number of major tick mark values. If zero is supplied then +* the other parameters are ignored, and subsequent calls to +* astGrid will itself determine the tick values to be used. +* major +* Pointer to an array holding "nmajor" values for axis "axis" in +* the current Frame of the suppled Plot. Major tick marks will be +* drawn at these values. +* nminor +* The number of minor tick mark values. +* minor +* Pointer to an array holding "nminor" values for axis "axis" in +* the current Frame of the suppled Plot. Minor tick marks will be +* drawn at these values. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global status. */ + if( !astOK ) return; + + astError( AST__INTER, "astSetTickValues(%s): The astSetTickValues " + "method cannot be used with a %s (programming error).", status, + astGetClass( this ), astGetClass( this ) ); +} + +static void SplitFrameSet( AstFrameSet *fset, + AstFrameSet **fsetxy, int labelxy[2], int wcsxy[2], + AstFrameSet **fsetxz, int labelxz[2], int wcsxz[2], + AstFrameSet **fsetyz, int labelyz[2], int wcsyz[2], + int *baseplane, int *status ){ +/* +* Name: +* SplitFrameSet + +* Purpose: +* Split a 3D FrameSet into three 2D FrameSets. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void SplitFrameSet( AstFrameSet *fset, +* AstFrameSet **fsetxy, int labelxy[2], int wcsxy[2], +* AstFrameSet **fsetxz, int labelxz[2], int wcsxz[2], +* AstFrameSet **fsetyz, int labelyz[2], int wcsyz[2], +* int *baseplane, int *status ) + +* Class Membership: +* Plot3D member function + +* Description: +* This function returns 3 FrameSets, each of which has 2-dimensional +* base and current Frames. Each of the 2D base Frames span a single +* plane in the 3D base Frame of the supplied FrameSet. Likewise, each +* of the 2D current Frames span a single plane in the 3D current Frame +* of the supplied FrameSet. An error is reported if there is no +* one-to-one association between the three planes in the supplied +* current Frame and the 3 planes in the supplied base Frame. + +* Parameters: +* fset +* Pointer to a FrameSet that has a 3D base Frame and a 3D current +* Frame. +* fsetxy +* Pointer to a location at which to return a pointer to a new FrameSet +* that has a 2D base Frame and a 2D current. The base Frame spans +* axes 1 and 2 of the 3D base Frame in "fset". +* labelxy +* Returned holding flags indicating if the two axes of the current +* Frame in *fsetxy should be labelled or not. +* wcsxy +* Returned holding the zero based axis index within the current Frame +* of the supplied FrameSet that corresponds to each of the two +* current Frame axes in the "fsetxy" FrameSet. +* fsetxz +* Pointer to a location at which to return a pointer to a new FrameSet +* that has a 2D base Frame and a 2D current. The base Frame spans +* axes 1 and 3 of the 3D base Frame in "fset". +* labelxz +* Returned holding flags indicating if the two axes of the current +* Frame in *fsetxz should be labelled or not. +* wcsxz +* Returned holding the zero based axis index within the current Frame +* of the supplied FrameSet that corresponds to each of the two +* current Frame axes in the "fsetxz" FrameSet. +* fsetyz +* Pointer to a location at which to return a pointer to a new FrameSet +* that has a 2D base Frame and a 2D current. The base Frame spans +* axes 2 and 3 of the 3D base Frame in "fset". +* labelyz +* Returned holding flags indicating if the two axes of the current +* Frame in *fsetyz should be labelled or not. +* wcsyz +* Returned holding the zero based axis index within the current Frame +* of the supplied FrameSet that corresponds to each of the two +* current Frame axes in the "fsetxy" FrameSet. +* baseplane +* Index of the plane that is spanned by two connected 3D axes. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Null pointers will be returned for the three new FrameSets if this +* function is invoked with the global status set, or if it should fail +* for any reason. +* - Each returned FrameSet has 2 Frames: Frame 1 is the base Frame +* and is spanned by 2 of the 3 axes in the base Frame of the supplied +* FrameSet; Frame 2 is the current Frame and is spanned by 2 of the 3 +* axes in the current Frame of the supplied FrameSet. Any future changes +* to this function that alter this structure should reflected in +* equivalent changes to functions CreatePlots and UpdatePlots. +*/ + +/* Local Variables: */ + AstFrame *bfrm2d; + AstFrame *bfrm; + AstFrame *cfrm1d; + AstFrame *cfrm2d; + AstFrame *cfrm; + AstFrame *dummy; + AstFrameSet *fset1; + AstFrameSet *fset2; + AstFrameSet *fset3; + AstMapping *map1d; + AstMapping *map2d; + AstMapping *map; + AstMapping *smap; + AstUnitMap *unit1d; + int *axout; + int *other_axout; + int axin2[ 2 ]; + int axin[ 2 ]; + int i; + int label1[ 2 ]; + int label2[ 2 ]; + int label3[ 2 ]; + int other_axin; + int wcsax1[ 2 ]; + int wcsax2[ 2 ]; + int wcsax3[ 2 ]; + +/* Initialise. */ + *fsetxy = NULL; + *fsetxz = NULL; + *fsetyz = NULL; + *baseplane = 0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the simplified base -> current Mapping form the supplied FrameSet. + Also get the base and current Frames themselves. */ + map = astGetMapping( fset, AST__BASE, AST__CURRENT ); + smap = astSimplify( map ); + map = astAnnul( map ); + cfrm = astGetFrame( fset, AST__CURRENT ); + bfrm = astGetFrame( fset, AST__BASE ); + +/* Create a 1D UnitMap. */ + unit1d = astUnitMap( 1, "", status ); + +/* First try to identify a pair of base Frame axes that map onto a pair + of current Frame axes. This will be the case for instance if the 3D + FrameSet describes a spectral cube in which two of the axes are spanned + by a SkyFrame. We try all three possible pairs of axes in turn until a + pair is found succesfully. */ + for( i = 0; i < 3 && ! *fsetxy; i++ ) { + axin[ 0 ] = ( i < 2 ) ? 0 : 1; + axin[ 1 ] = ( i == 0 ) ? 1 : 2; + +/* Try to get a Mapping that connects the inputs given by axin to a + distinct subset of outputs. */ + axout = astMapSplit( smap, 2, axin, &map2d ); + +/* If succesful, check that the 2 inputs correspond to exactly 2 outputs. */ + if( map2d ){ + if( astGetNout( map2d ) == 2 ) { + +/* Get the index of the other input axis (the one not included in the pair). */ + other_axin = 3 - axin[ 0 ] - axin[ 1 ]; + +/* Try to get a Mapping that connects this remaining input to a distinct + subset of outputs. */ + other_axout = astMapSplit( smap, 1, &other_axin, &map1d ); + +/* If succesful, check that the 1 input correspond to exactly 1 output. */ + if( map1d ){ + if( astGetNout( map1d ) == 1 ) { + +/* Pick the two axes from the 3D base and current Frames that correspond to + the paired axes found above. */ + bfrm2d = astPickAxes( bfrm, 2, axin, NULL ); + cfrm2d = astPickAxes( cfrm, 2, axout, NULL ); + +/* Pick the axis from the 3D current Frame that correspond to + the other axis. */ + cfrm1d = astPickAxes( cfrm, 1, other_axout, NULL ); + +/* Construct a FrameSet using these 2D Frames and Mapping. */ + fset1 = astFrameSet( bfrm2d, "", status ); + astAddFrame( fset1, AST__BASE, map2d, cfrm2d ); + + bfrm2d = astAnnul( bfrm2d ); + cfrm2d = astAnnul( cfrm2d ); + map2d = astAnnul( map2d ); + +/* Indicate that both axes in the current Frame of this FrameSet should + be labelled. */ + label1[ 0 ] = 1; + label1[ 1 ] = 1; + +/* Store the index of the axis within the supplied current Frame that + corresponds to each axis in the current Frame of the FrameSet created + above. */ + wcsax1[ 0 ] = axout[ 0 ]; + wcsax1[ 1 ] = axout[ 1 ]; + +/* Pick the two axes from the 3D base Frame that correspond to the first of + the paired axes, and the unpaired axis. Order them so that we get the + 2 axes in the order xy, xz or yz. Also, store the index of the axis + within the supplied current Frame that corresponds to each axis in + the current Frame of the FrameSet created below. */ + if( i < 2 ) { + axin2[ 0 ] = axin[ 0 ]; + axin2[ 1 ] = other_axin; + wcsax2[ 0 ] = axout[ 0 ]; + wcsax2[ 1 ] = other_axout[ 0 ]; + } else { + axin2[ 0 ] = other_axin; + axin2[ 1 ] = axin[ 0 ]; + wcsax2[ 0 ] = other_axout[ 0 ]; + wcsax2[ 1 ] = axout[ 0 ]; + } + bfrm2d = astPickAxes( bfrm, 2, axin2, NULL ); + +/* The corresponding current Frame in the new FrameSet will be a compound + 2D Frame holding the other current Frame axis and a copy of the input + base Frame axis. Combine them so that we get the 2 axes in the order + xy, xz or yz. */ + dummy = astPickAxes( bfrm, 1, axin, NULL ); + if( i < 2 ) { + cfrm2d = (AstFrame *) astCmpFrame( dummy, cfrm1d, "", status ); + } else { + cfrm2d = (AstFrame *) astCmpFrame( cfrm1d, dummy, "", status ); + } + dummy = astAnnul( dummy ); + +/* The Mapping that joins this 2D base Frame to the 2D current Frame uses + the above 1D mapping for the other axis, and a UnitMap for the copied + base Frame axis. */ + if( i < 2 ) { + map2d = (AstMapping *) astCmpMap( unit1d, map1d, 0, "", status ); + } else { + map2d = (AstMapping *) astCmpMap( map1d, unit1d, 0, "", status ); + } + +/* Construct a FrameSet using these 2D Frames and Mapping. */ + fset2 = astFrameSet( bfrm2d, "", status ); + astAddFrame( fset2, AST__BASE, map2d, cfrm2d ); + + bfrm2d = astAnnul( bfrm2d ); + cfrm2d = astAnnul( cfrm2d ); + map2d = astAnnul( map2d ); + +/* Indicate that only one of the axes in the current Frame of this FrameSet + should be labelled. */ + if( i < 2 ) { + label2[ 0 ] = 0; + label2[ 1 ] = 1; + } else { + label2[ 0 ] = 1; + label2[ 1 ] = 0; + } + +/* Pick the two axes from the 3D base Frame that correspond to the second + of the paired axes, and the unpaired axis. Order them so that we get the + 2 axes in the order xy, xz or yz. Also, store the index of the axis + within the supplied current Frame that corresponds to each axis in + the current Frame of the FrameSet created below. */ + if( i == 0 ) { + axin2[ 0 ] = axin[ 1 ]; + axin2[ 1 ] = other_axin; + wcsax3[ 0 ] = axout[ 1 ]; + wcsax3[ 1 ] = other_axout[ 0 ]; + } else { + axin2[ 0 ] = other_axin; + axin2[ 1 ] = axin[ 1 ]; + wcsax3[ 0 ] = other_axout[ 0 ]; + wcsax3[ 1 ] = axout[ 1 ]; + } + bfrm2d = astPickAxes( bfrm, 2, axin2, NULL ); + +/* The corresponding current Frame in the new FrameSet will be a compound + 2D Frame holding the other current Frame axis and a copy of the input + base Frame axis. Combine them so that we get the 2 axes in the order + xy, xz or yz. */ + dummy = astPickAxes( bfrm, 1, axin + 1, NULL ); + if( i == 0 ) { + cfrm2d = (AstFrame *) astCmpFrame( dummy, cfrm1d, "", status ); + } else { + cfrm2d = (AstFrame *) astCmpFrame( cfrm1d, dummy, "", status ); + } + dummy = astAnnul( dummy ); + +/* The Mapping that joins this 2D base Frame to the 2D current Frame uses + the above 1D mapping for the other axis, and a UnitMap for the copied + base Frame axis. */ + if( i == 0 ) { + map2d = (AstMapping *) astCmpMap( unit1d, map1d, 0, "", status ); + } else { + map2d = (AstMapping *) astCmpMap( map1d, unit1d, 0, "", status ); + } + +/* Construct a FrameSet using these 2D Frames and Mapping. */ + fset3 = astFrameSet( bfrm2d, "", status ); + astAddFrame( fset3, AST__BASE, map2d, cfrm2d ); + + bfrm2d = astAnnul( bfrm2d ); + cfrm2d = astAnnul( cfrm2d ); + map2d = astAnnul( map2d ); + +/* Indicate that neither axis in the current Frame of this FrameSet + should be labelled. */ + label3[ 0 ] = 0; + label3[ 1 ] = 0; + +/* Store each FrameSet in the correct returned pointer. */ + if( i == 0 ) { + *baseplane = XY; + *fsetxy = fset1; + *fsetxz = fset2; + *fsetyz = fset3; + + labelxy[ 0 ] = label1[ 0 ]; + labelxy[ 1 ] = label1[ 1 ]; + labelxz[ 0 ] = label2[ 0 ]; + labelxz[ 1 ] = label2[ 1 ]; + labelyz[ 0 ] = label3[ 0 ]; + labelyz[ 1 ] = label3[ 1 ]; + + wcsxy[ 0 ] = wcsax1[ 0 ]; + wcsxy[ 1 ] = wcsax1[ 1 ]; + wcsxz[ 0 ] = wcsax2[ 0 ]; + wcsxz[ 1 ] = wcsax2[ 1 ]; + wcsyz[ 0 ] = wcsax3[ 0 ]; + wcsyz[ 1 ] = wcsax3[ 1 ]; + + } else if( i == 1 ) { + *baseplane = XZ; + *fsetxy = fset2; + *fsetxz = fset1; + *fsetyz = fset3; + + labelxy[ 0 ] = label2[ 0 ]; + labelxy[ 1 ] = label2[ 1 ]; + labelxz[ 0 ] = label1[ 0 ]; + labelxz[ 1 ] = label1[ 1 ]; + labelyz[ 0 ] = label3[ 0 ]; + labelyz[ 1 ] = label3[ 1 ]; + + wcsxy[ 0 ] = wcsax2[ 0 ]; + wcsxy[ 1 ] = wcsax2[ 1 ]; + wcsxz[ 0 ] = wcsax1[ 0 ]; + wcsxz[ 1 ] = wcsax1[ 1 ]; + wcsyz[ 0 ] = wcsax3[ 0 ]; + wcsyz[ 1 ] = wcsax3[ 1 ]; + + } else { + *baseplane = YZ; + *fsetxy = fset2; + *fsetxz = fset3; + *fsetyz = fset1; + + labelxy[ 0 ] = label2[ 0 ]; + labelxy[ 1 ] = label2[ 1 ]; + labelxz[ 0 ] = label3[ 0 ]; + labelxz[ 1 ] = label3[ 1 ]; + labelyz[ 0 ] = label1[ 0 ]; + labelyz[ 1 ] = label1[ 1 ]; + + wcsxy[ 0 ] = wcsax2[ 0 ]; + wcsxy[ 1 ] = wcsax2[ 1 ]; + wcsxz[ 0 ] = wcsax3[ 0 ]; + wcsxz[ 1 ] = wcsax3[ 1 ]; + wcsyz[ 0 ] = wcsax1[ 0 ]; + wcsyz[ 1 ] = wcsax1[ 1 ]; + + } + +/* Free resources */ + cfrm1d = astAnnul( cfrm1d ); + } + +/* Free resources */ + map1d = astAnnul( map1d ); + other_axout = astFree( other_axout ); + } + } + +/* Free resources */ + if( map2d ) map2d = astAnnul( map2d ); + axout = astFree( axout ); + +/* Leave the loop if we now have the required FrameSets, or an error has + occurred. */ + if( *fsetxy || !astOK ) break; + + } + } + +/* Free resources */ + cfrm = astAnnul( cfrm ); + bfrm = astAnnul( bfrm ); + smap = astAnnul( smap ); + unit1d = astAnnul( unit1d ); + +/* Return null pointers if an error has occurred. */ + if( !astOK ) { + *fsetxy = astAnnul( *fsetxy ); + *fsetxz = astAnnul( *fsetxz ); + *fsetyz = astAnnul( *fsetyz ); + +/* Report an error if the supplied FrameSet could not be split into 3 + independent 2D planes. */ + } if( ! *fsetxy ) { + astError( AST__3DFSET, "astInitPlot3D(Plot3D): Supplied %s contains " + "no independent axes.", status, astGetClass( fset ) ); + } +} + +static void StoreAxisInfo( AstPlot3D *this, int labelxy[2], int wcsxy[2], + int labelxz[2], int wcsxz[2], int labelyz[2], + int wcsyz[2], int *status ) { +/* +* Name: +* StoreAxisInfo + +* Purpose: +* Store information connecting 3D axis with associuated 2D Plot axes. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void StoreAxisInfo( AstPlot3D *this, int labelxy[2], int wcsxy[2], +* int labelxz[2], int wcsxz[2], int labelyz[2], +* int wcsyz[2], int *status ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function stores information inside the Plot3D that allows each +* 3D axis to be associated with the 2 Plots that share the 3D axis, +* and with the axis index within each of these two Plots. + +* Parameters: +* this +* Pointer to the Plot3D. +* labelxy +* Flags indicating if the two axes of the current Frame in the XY +* Plot should be labelled or not. +* wcsxy +* The zero based axis index within the 3D current Frame that +* corresponds to each of the two current Frame axes in the XY Plot. +* A value of -1 should be used for 2D axes that do not correspond +* to any 3D axis. +* labelxz +* Flags indicating if the two axes of the current Frame in the XZ +* Plot should be labelled or not. +* wcsxz +* The zero based axis index within the 3D current Frame that +* corresponds to each of the two current Frame axes in the XZ Plot. +* A value of -1 should be used for 2D axes that do not correspond +* to any 3D axis. +* labelyz +* Flags indicating if the two axes of the current Frame in the YZ +* Plot should be labelled or not. +* wcsyz +* The zero based axis index within the 3D current Frame that +* corresponds to each of the two current Frame axes in the YZ Plot. +* A value of -1 should be used for 2D axes that do not correspond +* to any 3D axis. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int axis2d; + int axis3d; + int gotfirst; + int gotsecond; + int temp; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Store information that allows each 3D WCS axis to be associated with + a pair of Plots. Also store the WCS axis within each Plot that + corresponds to the 3D WCS axis. Do each 3D WCS axis in turn. */ + for( axis3d = 0; axis3d < 3; axis3d++ ) { + +/* Indicate we have not yet found either of the two Plots that share the + current 3D axis. */ + gotfirst = 0; + gotsecond = 0; + +/* Check each of the 2 axes in the plot spanning the XY face of the 3D + graphics cube. Break early if we have found both Plots for the current + 3D WCS axis. */ + for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) { + +/* See if this 2D axis corresponds to the required 3D axis. If so, store + the Plot identifier and the 2D axis index. */ + if( wcsxy[ axis2d ] == axis3d ) { + if( gotfirst ) { + this->axis_plot2[ axis3d ] = XY; + this->axis_index2[ axis3d ] = axis2d; + gotsecond = 1; + } else { + this->axis_plot1[ axis3d ] = XY; + this->axis_index1[ axis3d ] = axis2d; + gotfirst = 1; + } + } + } + +/* Check the plot spanning the XZ face in the same way. */ + for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) { + if( wcsxz[ axis2d ] == axis3d ) { + if( gotfirst ) { + this->axis_plot2[ axis3d ] = XZ; + this->axis_index2[ axis3d ] = axis2d; + gotsecond = 1; + } else { + this->axis_plot1[ axis3d ] = XZ; + this->axis_index1[ axis3d ] = axis2d; + gotfirst = 1; + } + } + } + +/* Check the plot spanning the YZ face in the same way. */ + for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) { + if( wcsyz[ axis2d ] == axis3d ) { + if( gotfirst ) { + this->axis_plot2[ axis3d ] = YZ; + this->axis_index2[ axis3d ] = axis2d; + gotsecond = 1; + } else { + this->axis_plot1[ axis3d ] = YZ; + this->axis_index1[ axis3d ] = axis2d; + gotfirst = 1; + } + } + } + } + +/* Ensure that the first Plot within each pair is the one that is used to + generate labels for the 3D axis. */ + for( axis2d = 0; axis2d < 2; axis2d++ ) { + if( labelxy[ axis2d ] ) { + axis3d = wcsxy[ axis2d ]; + if( this->axis_plot1[ axis3d ] != XY ){ + temp = this->axis_plot1[ axis3d ]; + this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ]; + this->axis_plot2[ axis3d ] = temp; + + temp = this->axis_index1[ axis3d ]; + this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ]; + this->axis_index1[ axis3d ] = temp; + } + } + } + + for( axis2d = 0; axis2d < 2; axis2d++ ) { + if( labelxz[ axis2d ] ) { + axis3d = wcsxz[ axis2d ]; + if( this->axis_plot1[ axis3d ] != XZ ){ + temp = this->axis_plot1[ axis3d ]; + this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ]; + this->axis_plot2[ axis3d ] = temp; + + temp = this->axis_index1[ axis3d ]; + this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ]; + this->axis_index1[ axis3d ] = temp; + } + } + } + + for( axis2d = 0; axis2d < 2; axis2d++ ) { + if( labelyz[ axis2d ] ) { + axis3d = wcsyz[ axis2d ]; + if( this->axis_plot1[ axis3d ] != YZ ){ + temp = this->axis_plot1[ axis3d ]; + this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ]; + this->axis_plot2[ axis3d ] = temp; + + temp = this->axis_index1[ axis3d ]; + this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ]; + this->axis_index1[ axis3d ] = temp; + } + } + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the astTestAttrib protected +* method inherited from the Plot class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Plot3D's attributes. + +* Parameters: +* this +* Pointer to the Plot3D. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPlot *plot; /* Pointer to specific Plot */ + AstPlot3D *this; /* Pointer to the Plot3D structure */ + char attname[50]; /* Plot attribute base name */ + char patt[50]; /* Plot attribute full name */ + char spec[10]; /* Plane specification */ + int axis; /* Axis index */ + int len; /* Length of attrib string */ + int nc; /* Number of character read */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* Norm. */ +/* ----- */ + if ( !strcmp( attrib, "norm" ) ) { + result = astTestNorm( this, 0 ) || + astTestNorm( this, 1 ) || + astTestNorm( this, 2 ); + +/* Norm(axis). */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestNorm( this, axis - 1 ); + +/* RootCorner. */ +/* ----------- */ + } else if ( !strcmp( attrib, "rootcorner" ) ) { + result = astTestRootCorner( this ); + + +/* ..._XY etc */ +/* ---------- */ + } else if ( nc = 0, + ( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec, + &nc ) ) ) { + + if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) { + plot = this->plotxy; + } else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) { + plot = this->plotyz; + } else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) { + plot = this->plotxz; + } else { + plot = NULL; + } + + if( plot ) { + sprintf( patt, "%s%s", attname, attrib + nc ); + result = astTestAttrib( plot, patt ); + + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static void Text( AstPlot *this_plot, const char *text, const double pos[], + const float up[], const char *just, int *status ){ +/* +* Name: +* Text + +* Purpose: +* Draw a text string for a Plot3D. + +* Type: +* Private member function. + +* Synopsis: +* #include "plot3d.h" +* void Text( AstPlot *this, const char *text, const double pos[], +* const float up[], const char *just, int *status ) + +* Class Membership: +* Plot3D method (overrides the Text method inherited form the Plot +* class). + +* Description: +* This function draws a string of text at a position specified in +* the physical coordinate system of a Plot3D. The physical position +* is transformed into graphical coordinates to determine where the +* text should appear within the plotting area. +* +* The text is drawn on a 2D plane that has a normal vector given by the +* current value of the Plot3D's "Norm" attribute. + +* Parameters: +* this +* Pointer to the Plot3D. +* text +* Pointer to a null-terminated character string containing the +* text to be drawn. Trailing white space is ignored. +* pos +* An array, with one element for each axis of the Plot3D, giving +* the physical coordinates of the point where the reference +* position of the text string is to be placed. +* up +* An array holding the components of a vector in the "up" +* direction of the text (in graphical coordinates). For +* example, to get horizontal text, the vector {0.0f,1.0f} should +* be supplied. "Up" is taken to be the projection of the positive +* Z axis onto the plane specified by the current value of the +* just +* Pointer to a null-terminated character string identifying the +* reference point for the text being drawn. The first character in +* this string identifies the reference position in the "up" direction +* and may be "B" (baseline), "C" (centre), "T" (top) or "M" (bottom). +* The second character identifies the side-to-side reference position +* and may be "L" (left), "C" (centre) or "R" (right ). The string is +* case-insensitive, and only the first two characters are significant. +* +* For example, a value of "BL" means that the left end of the +* baseline of the original (un-rotated) text is to be drawn at the +* position given by "pos". +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapping *mapping; /* Pointer to graphics->physical mapping */ + AstPlot3D *this; /* Pointer to the Plot3D structure */ + AstPointSet *pset1; /* PointSet holding physical positions */ + AstPointSet *pset2; /* PointSet holding graphics positions */ + char *ltext; /* Local copy of "text" excluding trailing spaces */ + char ljust[3]; /* Upper case copy of "just" */ + const char *class; /* Object class */ + const char *method; /* Current method */ + const double **ptr1; /* Pointer to physical positions */ + double **ptr2; /* Pointer to graphics positions */ + float norm[ 3 ]; /* Single precision normal vector */ + float ref[ 3 ]; /* Single precision ref position */ + int axis; /* Axis index */ + int escs; /* Original astEscapes value */ + int naxes; /* No. of axes in the base Frame */ + int ncoord; /* No. of axes in the current Frame */ + int ulen; /* Length of "text" excluding trailing spaces */ + +/* Check the global error status. */ + if ( !astOK || !text ) return; + +/* Store a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_plot; + +/* Store the current method and class for inclusion in error messages + generated by lower level functions. */ + method = "astText"; + class = astClass( this ); + +/* Check the base Frame of the Plot is 3-D. */ + naxes = astGetNin( this ); + if( naxes != 3 && astOK ){ + astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base " + "Frame of the supplied %s is invalid - this number should " + "be 3.", status, method, class, naxes, class ); + } + +/* Ensure AST functions do not included graphical escape sequences in any + returned text strings. This is because the Plot3D implementation of + this method cannot currently handle graphical escape sequences. */ + escs = astEscapes( 0 ); + +/* Establish the correct graphical attributes as defined by attributes + with the supplied Plot. */ + astGrfAttrs( this, AST__TEXT_ID, 1, GRF__TEXT, method, class ); + +/* Get the number of coordinates in the physical coordinate frame. */ + ncoord = astGetNout( this ); + +/* Create a PointSet to hold the supplied physical coordinates. */ + pset1 = astPointSet( 1, ncoord, "", status ); + +/* Allocate memory to hold pointers to the first value on each axis. */ + ptr1 = (const double **) astMalloc( sizeof( const double * )* + (size_t)( ncoord )); + +/* Check the pointer can be used, then store pointers to the first value + on each axis. */ + if( astOK ){ + for( axis = 0; axis < ncoord; axis++ ){ + ptr1[ axis ] = pos + axis; + } + } + +/* Store these pointers in the PointSet. */ + astSetPoints( pset1, (double **) ptr1 ); + +/* Transform the supplied data from the current frame (i.e. physical + coordinates) to the base frame (i.e. graphics coordinates) using + the inverse Mapping defined by the Plot. */ + mapping = astGetMapping( this, AST__BASE, AST__CURRENT ); + pset2 = astTransform( mapping, pset1, 0, NULL ); + mapping = astAnnul( mapping ); + +/* Get pointers to the graphics coordinates. */ + ptr2 = astGetPoints( pset2 ); + +/* Take a copy of the string excluding any trailing white space. */ + ulen = astChrLen( text ); + ltext = (char *) astStore( NULL, (void *) text, ulen + 1 ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* Terminate the local copy of the text string. */ + ltext[ ulen ] = 0; + +/* Produce an upper-case copy of the first two characters in "just". */ + ljust[0] = (char) toupper( (int) just[0] ); + ljust[1] = (char) toupper( (int) just[1] ); + ljust[2] = 0; + +/* Convert the double precision values to single precision, checking for + bad positions. */ + if( ptr2[0][0] != AST__BAD && ptr2[1][0] != AST__BAD && + ptr2[2][0] != AST__BAD ){ + ref[ 0 ] = (float) ptr2[0][0]; + ref[ 1 ] = (float) ptr2[1][0]; + ref[ 2 ] = (float) ptr2[2][0]; + +/* If the nornmal vector has non-zero length, draw the text. */ + norm[ 0 ] = (float) astGetNorm( this, 0 ); + norm[ 1 ] = (float) astGetNorm( this, 1 ); + norm[ 2 ] = (float) astGetNorm( this, 2 ); + +/* Since we are about to call an external function which may not be + thread safe, prevent any other thread from executing the following code + until the current thread has finished executing it. */ + LOCK_MUTEX2; + + if( norm[ 0 ] != 0.0 || norm[ 1 ] != 0.0 || norm[ 2 ] != 0.0 ){ + if( !astG3DText( ltext, ref, ljust, (float *) up, norm ) ) { + astError( AST__GRFER, "%s(%s): Graphics error in astG3DText. ", status, + method, class ); + } + } else if( astOK ) { + astError( AST__ATTIN, "%s(%s): The vector specified by the Norm " + "attribute has zero length.", status, method, class ); + } + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2; + } + +/* Free the local copy of the string. */ + ltext = (char *) astFree( (void *) ltext ); + + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Free the memory holding the pointers to the first value on each axis. */ + ptr1 = (const double **) astFree( (void *) ptr1 ); + +/* Re-establish the original graphical attributes. */ + astGrfAttrs( this, AST__TEXT_ID, 0, GRF__TEXT, method, class ); + +/* Restore the original value of the flag which says whether graphical + escape sequences should be incldued in any returned text strings. */ + astEscapes( escs ); + +/* Return */ + return; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Use a Plot to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the astTransform protected +* method inherited from the Plot class). + +* Description: +* This function takes a Plot3D and a set of points encapsulated in a +* PointSet and transforms the points from graphics coordinates to +* physical coordinates (in the forward direction). No clipping or +* normalisation is applied. + +* Parameters: +* this +* Pointer to the Plot3D. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate +* transformation should be applied while a zero value requests the +* inverse transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the Plot being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstMapping *map; /* Pointer to the mapping */ + AstPointSet *result; /* Positions in output Frame */ + AstPlot3D *this; /* The Plot3D */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the Plot3D. */ + this = (AstPlot3D *) this_mapping; + +/* Get the Mapping from the base to the current Frame. */ + map = astGetMapping( this, AST__BASE, AST__CURRENT ); + +/* Do the transformation. */ + result = astTransform( map, in, forward, out ); + +/* Annul the mapping. */ + map = astAnnul( map ); + +/* If an error has occurred, annul the result. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; + +} + +static void UpdatePlots( AstPlot3D *this, int *status ) { +/* +* Name: +* UpdatePlots + +* Purpose: +* Update the three 2D plots stored in the Plot3D. + +* Type: +* Private function. + +* Synopsis: +* #include "plot3d.h" +* void UpdatePlots( AstPlot3D *this ) + +* Class Membership: +* Plot3D method. + +* Description: +* This function splits the parent FrameSet up into 3 independent 2D +* FrameSets, each describing a 2D plane in the supplied 3D FrameSet. +* It then uses these 2D FrameSets to update the three Plots in the +* Plot3D, by removing the existing current Frame and adding in the +* new current Frame with the associated Mapping. + +* Parameters: +* this +* Pointer to the Plot3D. + +* Notes: +* - Each of the 3 plots has 3 Frames: Frame 1 is the base (GRAPHICS) +* Frame; Frame 2 is spanned by 2 of the 3 axes in the base Frame of +* the supplied FrameSet; Frame 3 is the current Frame and is spanned +* by 2 of the 3 axes in the current Frame of the supplied FrameSet. +* Any future changes to this function that alter this structure should +* reflected in equivalewnt changes to function CreatePlots. +*/ + +/* Local Variables: */ + AstFrame *frm; + AstFrameSet *fsetxy; + AstFrameSet *fsetxz; + AstFrameSet *fsetyz; + AstFrameSet *fset; + AstMapping *map; + int baseplane; + int labelxy[ 2 ]; + int labelxz[ 2 ]; + int labelyz[ 2 ]; + int wcsxy[ 2 ]; + int wcsxz[ 2 ]; + int wcsyz[ 2 ]; + int rootcorner; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Return without action if the Plot3D does not contain the three 2D + Plots. This may be the case for instance if this function is called + before the Plot3D is fully constructed. */ + if( this->plotxy && this->plotxz && this->plotyz ){ + +/* We need a FrameSet that is equivalent to the one that was used to + construct the Plot3D (i.e. a FrameSet that does not have the GRAPHICS + Frame that was added by the Plot3D constructor). So take a copy of the + parent FrameSet, remove the GRAPHICS Frame (Frame 1) and set the base + Frame to be the Frame that was the base Frame when the Plot3D was + constructed. */ + fset = (AstFrameSet *) astCast( this, dummy_frameset ); + astSetBase( fset, this->pix_frame ); + astRemoveFrame( fset, 1 ); + +/* Split the supplied FrameSet up into 3 FrameSets, each with a 2D base + and current Frame. Each of these FrameSets describes one plane of + the 3D cube. */ + SplitFrameSet( fset, &fsetxy, labelxy, wcsxy, &fsetxz, labelxz, wcsxz, + &fsetyz, labelyz, wcsyz, &baseplane, status ); + +/* First do the XY Plot. Extract the current Frame and base->current + Mapping from the new FrameSet. It is assumed that he base Frame in the + new FrameSet is equivalent to Frame 2 in the existing Plot. This + assumption is based on the way the CreatePlots and SplitFrameSet + functions work, and should be reviewed if either of these functions + is changed. */ + frm = astGetFrame( fsetxy, 2 ); + map = astGetMapping( fsetxy, 1, 2 ); + +/* Add the new Frame into the existing Plot using the Mapping to connect + it to Frame 2 in the Plot. It becomes the current Frame in the Plot. */ + astAddFrame( this->plotxy, 2, map, frm ); + +/* Delete the original current Frame in the Plot since it is not needed + any more. */ + astRemoveFrame( this->plotxy, 3 ); + +/* Set the Plot attributes. */ + SetPlotAttr( this->plotxy, XY, labelxy, status ); + +/* Free resources */ + fsetxy = astAnnul( fsetxy ); + map = astAnnul( map ); + frm = astAnnul( frm ); + +/* Do the same for the XZ plot. */ + frm = astGetFrame( fsetxz, 2 ); + map = astGetMapping( fsetxz, 1, 2 ); + astAddFrame( this->plotxz, 2, map, frm ); + astRemoveFrame( this->plotxz, 3 ); + SetPlotAttr( this->plotxz, XZ, labelxz, status ); + fsetxz = astAnnul( fsetxz ); + map = astAnnul( map ); + frm = astAnnul( frm ); + +/* Do the same for the YZ plot. */ + frm = astGetFrame( fsetyz, 2 ); + map = astGetMapping( fsetyz, 1, 2 ); + astAddFrame( this->plotyz, 2, map, frm ); + astRemoveFrame( this->plotyz, 3 ); + SetPlotAttr( this->plotyz, YZ, labelyz, status ); + fsetyz = astAnnul( fsetyz ); + map = astAnnul( map ); + frm = astAnnul( frm ); + +/* Store information that allows each 3D WCS axis to be associatedf with + a pair of Plots. Also store the WCS axis within each Plot that + corresponds to the 3D WCS axis. */ + StoreAxisInfo( this, labelxy, wcsxy, labelxz, wcsxz, labelyz, wcsyz, status ); + +/* Set up the Edges attributes in the encapsulated Plots to produce labels + on the required edges of the 3D graphics cube. */ + rootcorner = astGetRootCorner( this ); + ChangeRootCorner( this, rootcorner, rootcorner, status ); + +/* Store the plane spanned by two connected 3D axes. */ + this->baseplot = baseplane; + +/* Free remaining resources */ + fset = astAnnul( fset ); + } +} + +static void VSet( AstObject *this_object, const char *settings, + char **text, va_list args, int *status ) { +/* +* Name: +* VSet + +* Purpose: +* Set values for a Plot3D's attributes. + +* Type: +* Private function. + +* Synopsis: +* #include "frameset.h" +* void VSet( AstObject *this, const char *settings, char **text, +* va_list args, int *status ) + +* Class Membership: +* Plot3D member function (over-rides the protected astVSet +* method inherited from the Object class). + +* Description: +* This function assigns a set of attribute values for a Plot3D, +* the attributes and their values being specified by means of a +* string containing a comma-separated list of the form: +* +* "attribute1 = value1, attribute2 = value2, ... " +* +* Here, "attribute" specifies an attribute name and the value to +* the right of each "=" sign should be a suitable textual +* representation of the value to be assigned to that attribute. This +* will be interpreted according to the attribute's data type. +* +* The string supplied may also contain "printf"-style format +* specifiers identified by a "%" sign in the usual way. If +* present, these will be substituted by values supplied as +* optional arguments (as a va_list variable argument list), using +* the normal "printf" rules, before the string is used. + +* Parameters: +* this +* Pointer to the Plot3D. +* settings +* Pointer to a null-terminated string containing a +* comma-separated list of attribute settings. +* text +* Pointer to a location at which to return a pointer to dynamic +* memory holding a copy of the expanded setting string. This memory +* should be freed using astFree when no longer needed. If a NULL +* pointer is supplied, no string is created. +* args +* The variable argument list which contains values to be +* substituted for any "printf"-style format specifiers that +* appear in the "settings" string. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function preserves the integrity of the Plot3D (if +* possible) by appropriately modifying the three encapsulated Plots. +*/ + +/* Invoke the parent astVSet method to set the Plot3D's attribute values. */ + (*parent_vset)( this_object, settings, text, args, status ); + +/* Update the three 2D Plots stored in the Plot3D structure so that they + reflect this modified FrameSet. */ + UpdatePlots( (AstPlot3D *) this_object, status ); +} + +/* Functions which access Plot3D class attributes. */ +/* ----------------------------------------------- */ + +/* RootCorner. */ +/* ----------- */ +/* +*att++ +* Name: +* RootCorner + +* Purpose: +* Specifies which edges of the 3D box should be annotated. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls the appearance of an annotated +c coordinate grid (drawn with the astGrid function) by determining +f coordinate grid (drawn with the AST_GRID routine) by determining +* which edges of the cube enclosing the 3D graphics space are used +* for displaying numerical and descriptive axis labels. The attribute +* value identifies one of the eight corners of the cube within +* which graphics are being drawn (i.e. the cube specified by the +c "graphbox" parameter when astPlot3D +f GRAPHBOX argument when AST_PLOT3D +* was called tp create the Plot3D). Axis labels and tick marks will +* be placed on the three cube edges that meet at the given corner. +* +* The attribute value should consist of three character, each of +* which must be either "U" or "L". The first character in the string +* specifies the position of the corner on the first graphics axis. +* If the character is "U" then the corner is at the upper bound on the +* first graphics axis. If it is "L", then the corner is at the lower +* bound on the first axis. Likewise, the second and third characters +* in the string specify the location of the corner on the second and +* third graphics axes. +* +* For instance, corner "LLL" is the corner that is at the lower bound +* on all three graphics axes, and corner "ULU" is at the upper bound +* on axes 1 and 3 but at the lower bound on axis 2. +* +* The default value is "LLL". + +* Applicability: +* Plot3D +* All Plot3Ds have this attribute. + +*att-- +*/ + +/* Internally, the RootCorner is represented as an integer bit mask, with + bit zero for the X axis, bit 1 for the Y axis and bit 2 for the Z axis. + A bit is set if the corner is at the upper bound on the axis and unset + if it is at the lower bound. A value of -1 indicates that the attribue + has not been assigned a value. */ +astMAKE_GET(Plot3D,RootCorner,int,0,( this->rootcorner == -1 ? 0 : this->rootcorner)) +astMAKE_TEST(Plot3D,RootCorner,( this->rootcorner != -1 )) + + +/* Norm(axis). */ +/* ----------- */ +/* +*att++ +* Name: +* Norm(axis) + +* Purpose: +* Specifies the plane upon which a Plot3D draws text and markers. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the appearance of text and markers drawn +* by a Plot3D. It specifies the orientation of the plane upon which +* text and markers will be drawn by all subsequent invocations of the +c astText and astMark functions. +f AST_TEXT and AST_MARK functions. +* +* When setting or getting the Norm attribute, the attribute name must +* be qualified by an axis index in the range 1 to 3. The 3 elements of +* the Norm attribute are together interpreted as a vector in 3D graphics +* coordinates that is normal to the plane upon which text and marks +* should be drawn. When testing or clearing the attribute, the axis +* index is optional. If no index is supplied, then clearing the Norm +* attribute will clear all three elements, and testing the Norm attribute +* will return a non-zero value if any of the three elements are set. +* +* The default value is 1.0 for each of the 3 elements. The length of +* the vector is insignificant, but an error will be reported when +* attempting to draw text or markers if the vector has zero length. + +* Applicability: +* Plot +* All Plot3Ds have this attribute. + +*att-- +*/ +MAKE_CLEAR3(Norm,norm,AST__BAD,3) +MAKE_SET3(Norm,double,norm,value,3) +MAKE_TEST3(Norm,( this->norm[axis] != AST__BAD ),3) +MAKE_GET3(Norm,double,AST__BAD,(this->norm[axis]!=AST__BAD?this->norm[axis]:1.0),3) + + + +/* Functions which access Plot class attributes. */ +/* --------------------------------------------- */ + +/* First do axis specific attributes. */ + +#define MAKE_ALL(attr,type,badval,whichplots) \ + MAKE_CLEAR(attr,whichplots) \ + MAKE_SET(attr,type,whichplots) \ + MAKE_GET(attr,type,badval ) + +MAKE_ALL(MinTick,int,0,0) +MAKE_ALL(Abbrev,int,0,1) +MAKE_ALL(Gap,double,AST__BAD,1) +MAKE_ALL(LogGap,double,AST__BAD,1) +MAKE_ALL(LogPlot,int,0,0) +MAKE_ALL(LogTicks,int,0,0) +MAKE_ALL(LogLabel,int,0,1) +MAKE_ALL(LabelUp,int,0,1) +MAKE_ALL(DrawAxes,int,0,0) +MAKE_ALL(LabelUnits,int,0,1) +MAKE_ALL(MinTickLen,double,0.0,0) +MAKE_ALL(MajTickLen,double,0.0,0) +MAKE_ALL(NumLab,int,0,1) +MAKE_ALL(NumLabGap,double,AST__BAD,1) +MAKE_ALL(TextLab,int,0,1) +MAKE_ALL(TextLabGap,double,AST__BAD,1) + +#undef MAKE_ALL + + +/* Now do attributes that are not axis specific. */ + +#define MAKE_ALL(attr,type) \ + MAKE_CLEAR1(attr) \ + MAKE_SET1(attr,type) + +MAKE_ALL(Ink,int) +MAKE_ALL(Tol,double) +MAKE_ALL(Invisible,int) +MAKE_ALL(TickAll,int) +MAKE_ALL(ForceExterior,int) +MAKE_ALL(Border,int) +MAKE_ALL(Clip,int) +MAKE_ALL(ClipOp,int) +MAKE_ALL(Escape,int) +MAKE_ALL(Grid,int) +MAKE_ALL(Labelling,int) + +#undef MAKE_ALL + + + +/* First do element-specific attributes. */ + +#define MAKE_ALL(attr,type) \ + MAKE_CLEAR2(attr) \ + MAKE_SET2(attr,type) + +MAKE_ALL(Style,int) +MAKE_ALL(Font,int) +MAKE_ALL(Colour,int) +MAKE_ALL(Width,double) +MAKE_ALL(Size,double) + +#undef MAKE_ALL + + + + + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Plot3D objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Plot3D objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstPlot3D *in; /* Pointer to input Plot3D */ + AstPlot3D *out; /* Pointer to output Plot3D */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Plot3Ds. */ + in = (AstPlot3D *) objin; + out = (AstPlot3D *) objout; + +/* Nullify the pointers stored in the output object since these will + currently be pointing at the input data (since the output is a simple + byte-for-byte copy of the input). Otherwise, the input data could be + freed by accidient if the output object is deleted due to an error + occuring in this function. */ + out->plotxy = NULL; + out->plotxz = NULL; + out->plotyz = NULL; + +/* Copy the encapsulated Plots */ + if( in->plotxy ) out->plotxy = astCopy( in->plotxy ); + if( in->plotxz ) out->plotxz = astCopy( in->plotxz ); + if( in->plotyz ) out->plotyz = astCopy( in->plotyz ); + +/* If an error has occurred, free the output resources. */ + if( !astOK ) Delete( (AstObject *) out, status ); + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Plot3D objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Plot3D objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPlot3D *this; + +/* Release the memory referred to in the Plot3D structure. */ + this = (AstPlot3D *) obj; + if( this ) { + this->plotxy = astDelete( this->plotxy ); + this->plotxz = astDelete( this->plotxz ); + this->plotyz = astDelete( this->plotyz ); + } +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Plot3D objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Plot3D class to an output Channel. + +* Parameters: +* this +* Pointer to the Plot3D whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstPlot3D *this; + double dval; + char key[ KEY_LEN + 1 ]; + int axis; + int helpful; + int ival; + int set; + const char *text; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Plot3D structure. */ + this = (AstPlot3D *) this_object; + +/* Write out values representing the instance variables for the + Plot3D class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + + +/* Norm. */ +/* ----- */ + for ( axis = 0; axis < 3; axis++ ) { + dval = astGetNorm( this, axis ); + helpful = ( dval != -DBL_MAX ); + (void) sprintf( key, "Norm%d", axis + 1 ); + astWriteDouble( channel, key, 0, helpful, dval, "Text plane normal vector"); + } + +/* RootCorner. */ +/* ----------- */ + set = TestRootCorner( this, status ); + ival = set ? GetRootCorner( this, status ) : astGetRootCorner( this ); + text = RootCornerString( ival, status ); + if( text ) { + astWriteString( channel, "RootCn", set, 1, text, "Corner where labelled axes meet" ); + } else if( astOK ) { + astError( AST__INTER, "astDump(Plot3D): Illegal value %d found for " + "RootCorner attribute (interbal AST programming error).", status, + ival ); + } + +/* Labelled axes */ + astWriteInt( channel, "AxPlX1", 1, 1, this->axis_plot1[0], + "Plot used to label the 3D X axis" ); + astWriteInt( channel, "AxPlY1", 1, 1, this->axis_plot1[1], + "Plot used to label the 3D Y axis" ); + astWriteInt( channel, "AxPlZ1", 1, 1, this->axis_plot1[2], + "Plot used to label the 3D Z axis" ); + astWriteInt( channel, "AxInX1", 1, 1, this->axis_index1[0], + "Plot axis index used to label the 3D X axis" ); + astWriteInt( channel, "AxInY1", 1, 1, this->axis_index1[1], + "Plot axis index used to label the 3D Y axis" ); + astWriteInt( channel, "AxInZ1", 1, 1, this->axis_index1[2], + "Plot axis index used to label the 3D Z axis" ); + +/* Unlabelled axes */ + astWriteInt( channel, "AxPlX2", 1, 1, this->axis_plot2[0], + "Other Plot touching the 3D X axis" ); + astWriteInt( channel, "AxPlY2", 1, 1, this->axis_plot2[1], + "Other Plot touching the 3D Y axis" ); + astWriteInt( channel, "AxPlZ2", 1, 1, this->axis_plot2[2], + "Other Plot touching the 3D Z axis" ); + astWriteInt( channel, "AxInX2", 1, 1, this->axis_index2[0], + "Other Plot axis index touching the 3D X axis" ); + astWriteInt( channel, "AxInY2", 1, 1, this->axis_index2[1], + "Other Plot axis index touching the 3D Y axis" ); + astWriteInt( channel, "AxInZ2", 1, 1, this->axis_index2[2], + "Other Plot axis index touching the 3D Z axis" ); + +/* The Plot that spans the two connected axes. */ + astWriteInt( channel, "BasePl", 1, 1, this->baseplot, + "Plot spanning two connected 3D axes" ); + +/* XY Plot */ + astWriteObject( channel, "PlotXY", 1, 1, this->plotxy, + "Plot describing the XY plane" ); + +/* XZ Plot */ + astWriteObject( channel, "PlotXZ", 1, 1, this->plotxz, + "Plot describing the XZ plane" ); + +/* YZ Plot */ + astWriteObject( channel, "PlotYZ", 1, 1, this->plotyz, + "Plot describing the YZ plane" ); + +/* The index within the Plot3D FrameSet, of the original base Frame in + the FrameSet supplied when the Plot3D was constructed. */ + astWriteInt( channel, "PixFrm", 1, 0, this->pix_frame, + "Index of original base Frame" ); + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPlot3D and astCheckPlot3D functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Plot3D,Plot) +astMAKE_CHECK(Plot3D) + + +AstPlot3D *astPlot3D_( void *frame_void, const float *graphbox, + const double *basebox, const char *options, int *status, ...) { +/* +*+ +* Name: +* astPlot3D + +* Purpose: +* Create a Plot3D. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot3d.h" +* AstPlot3D *astPlot3D( AstFrame *frame, const float *graphbox, +* const double *basebox, const char *options, ..., int *status ) + +* Class Membership: +* Plot3D constructor. + +* Description: +* This function creates a new Plot3D and optionally initialises its +* attributes. +* +* The supplied Frame (or the base frame if a FrameSet was supplied) is +* assumed to be related to the graphics world coordinate system by a +* simple shift and scale along each axis. The mapping between graphics +* world coordinates and this Frame is specified by supplying the +* coordinates in both systems at the lower bounds and upper bounds corners +* of a box on the graphics device. By default, no graphics will be +* produced outside the supplied box, but this default behaviour can be +* changed by setting explicit values for the various clipping attributes. + +* Parameters: +* frame +* A pointer to a Frame or FrameSet to be annotated. If a NULL pointer +* is supplied, then a default 3-D Frame will be created to which labels, +* etc, can be attached by setting the relevant Frame attributes. +* graphbox +* A pointer to an array of 6 values giving the graphics world +* coordinates of the lower bounds and upper bound corners of a box on +* the graphics output device. The first triple of values should be the +* coordinates of the lower bounds corner of the box and the second +* triple of values should be the coordinates of the upper bounds corner. +* basebox +* A pointer to an array of 6 values giving the coordinates in the +* supplied Frame, or base frame of the supplied FrameSet, at the +* lower bounds corner and upper bounds corners of the box specified +* by parameter graphbox. These should be supplied in the same order as +* for parameter "graphbox". +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new Plot3D. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new Plot3D. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic Plot3D constructor which +* is available via the protected interface to the Plot3D class. +* A public interface is provided by the astPlot3DId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPlot3D *new; /* Pointer to new Plot3D */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + new = NULL; + +/* Obtain and validate a pointer to any supplied Frame structure. */ + if( frame_void ){ + frame = astCheckFrame( frame_void ); + } else { + frame = NULL; + } + +/* Check the pointer can be used. */ + if ( astOK ) { + +/* Initialise the Plot3D, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPlot3D( NULL, sizeof( AstPlot3D ), !class_init, + &class_vtab, "Plot3D", frame, graphbox, + basebox ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + Plot's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new Plot. */ + return new; +} + +AstPlot3D *astInitPlot3D_( void *mem, size_t size, int init, + AstPlot3DVtab *vtab, const char *name, + AstFrame *frame, const float *graphbox, + const double *basebox, int *status ) { +/* +*+ +* Name: +* astInitPlot3D + +* Purpose: +* Initialise a Plot3D. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot3d.h" +* AstPlot3D *astInitPlot3D( void *mem, size_t size, int init, +* AstPlotVtab *vtab, const char *name, +* AstFrame *frame, const float *graphbox, +* const double *basebox ) + +* Class Membership: +* Plot3D initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new Plot3D object. It allocates memory (if +* necessary) to accommodate the Plot3D plus any additional data +* associated with the derived class. It then initialises a +* Plot3D structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual function +* table for a Plot3D at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Plot3D is to be +* created. This must be of sufficient size to accommodate the +* Plot3D data (sizeof(Plot3D)) plus any data used by +* the derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Plot3D (plus derived +* class data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also stored +* in the Plot3D structure, so a valid value must be supplied +* even if not required for allocating memory. +* init +* A logical flag indicating if the Plot3D's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Plot3D. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object belongs +* (it is this pointer value that will subsequently be returned by +* the astGetClass method). +* fset +* A pointer to the Frame or Frameset to be annotated. +* graphbox +* A pointer to an array of 6 values giving the graphics coordinates +* of the bottom left and top right corners of a box on the graphics +* output device. The first triple of values should be the graphics +* coordinates of the bottom left corner of the box and the second +* triple of values are the graphics coordinates of the top right corner. +* basebox +* A pointer to an array of 6 values giving the coordinates in the +* supplied Frame or base Frame of the supplied FrameSet at the bottom +* left and top right corners of the box specified by parameter graphbox. +* These should be supplied in the same order as for parameter "graphbox". + +* Returned Value: +* A pointer to the new Plot3D. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFrame *baseframe = NULL; /* Pointer to base frame */ + AstFrame *graphicsframe = NULL; /* Pointer to graphics frame */ + AstFrameSet *fset0 = NULL; /* The n-D FrameSet to be annotated */ + AstFrameSet *fset = NULL; /* The 2-D FrameSet to be annotated */ + AstMapping *map = NULL; /* Mapping for converting bbox -> gbox */ + AstPlot3D *new = NULL; /* Pointer to new Plot3D */ + char *mess = NULL; /* Pointer to a descriptive message */ + double djunkbox[ 4 ] = { 0.0, 0.0, 1.0, 1.0 }; /* Dummy 2D basebox */ + float fjunkbox[ 4 ] = { 0.0, 0.0, 1.0, 1.0 }; /* Dummy 2D graphbox */ + int bi; /* Index of base frame */ + int ci; /* Index of current frame */ + int i; /* Loop count */ + int ii; /* Loop count */ + int naxes; /* No. of axes in frame */ + int nfrm; /* Number of Frames in frameset */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPlot3DVtab( vtab, name ); + +/* First of all we need to ensure that we have a FrameSet and a base + Frame on which to base the new Plot3D. If a NULL Frame pointer was + supplied, create a default 3-D Frame, and then create a FrameSet + containing just this default Frame. Also store a pointer to a + message which can be used to describe the object within error + messages. */ + if( !frame ){ + baseframe = astFrame( 3, "", status ); + fset = astFrameSet( baseframe, "", status ); + mess = "default 3-d Frame"; + +/* If an object was supplied, report an error if it is not a Frame or + an object derived from a Frame (such as a FrameSet). */ + } else if( !astIsAFrame( frame ) ){ + if( astOK ){ + astError( AST__BDOBJ, "astInitPlot3D(%s): Supplied Object (class '%s') " + "is not a Frame.", status, name, astGetClass( frame ) ); + } + +/* If the supplied object is a Plot3D or an object derived from a Plot3D + (a Plot3D is a sort of Frame and so will pass the above test), extract a + FrameSet from the Plot3D, and clear the Domain attribute for any existing + Frames which have Domain GRAPHICS. */ + } else if( astIsAPlot3D( frame ) ){ + fset0 = astFrameSet( frame, "", status ); + fset = astCopy( fset0 ); + fset0 = astAnnul( fset0 ); + + for( i = 0; i < astGetNframe( fset ); i++ ) { + graphicsframe = astGetFrame( fset, i ); + if( !strcmp( astGetDomain( graphicsframe ), "GRAPHICS" ) ) { + astClearDomain( graphicsframe ); + } + graphicsframe = astAnnul( graphicsframe ); + } + + baseframe = astGetFrame( fset, astGetBase( fset ) ); + mess = "base Frame of the supplied Plot3D"; + +/* If the object is not a FrameSet, create a FrameSet holding the + supplied Frame. If the Frame is not 3D, an extra 3D Frame is + included in the FrameSet derived from axes 1, 2 and 3 of the supplied + Frame. This new Frame becomes the base Frame. */ + } else if( !astIsAFrameSet( frame ) ){ + fset0 = astFrameSet( frame, "", status ); + mess = "supplied Frame"; + fset = Fset3D( fset0, AST__BASE, status ); + fset0 = astAnnul( fset0 ); + baseframe = astGetFrame( fset, astGetBase( fset ) ); + +/* If a FrameSet was supplied, ensure it has a 3D base Frame. + If the supplied FrameSet is not 3D, then a new base Frame is + inserted into it which is derived from axes 1, 2 and 3 of the + original base Frame. */ + } else { + fset = Fset3D( (AstFrameSet *) frame, AST__BASE, status ); + baseframe = astGetFrame( fset, astGetBase( fset ) ); + mess = "base Frame of the supplied FrameSet"; + } + +/* Check that there are 3 axes in the base frame of the FrameSet. */ + naxes = astGetNaxes( baseframe ); + if ( naxes != 3 && astOK ) { + astError( AST__NAXIN, "astInitPlot3D(%s): Number of axes (%d) in the %s " + "is invalid - this number should be 3.", status, name, naxes, mess ); + } + +/* Check that no dimension of the graphbox is bad. */ + if( astISBAD(graphbox[0]) || astISBAD(graphbox[1]) || + astISBAD(graphbox[2]) || astISBAD(graphbox[3]) || + astISBAD(graphbox[4]) || astISBAD(graphbox[5]) ) { + astError( AST__BADBX, "astInitPlot3D(%s): The plotting volume has undefined limits " + "in the graphics world coordinate system.", status, name ); + } + +/* Check that no dimension of the graphbox is zero. */ + if( ( graphbox[ 3 ] == graphbox[ 0 ] || + graphbox[ 4 ] == graphbox[ 1 ] || + graphbox[ 5 ] == graphbox[ 2 ] ) && astOK ){ + astError( AST__BADBX, "astInitPlot3D(%s): The plotting volume has zero size " + "in the graphics world coordinate system.", status, name ); + } + +/* Check that no dimension of the basebox is bad. */ + if( astISBAD(basebox[0]) || astISBAD(basebox[1]) || + astISBAD(basebox[2]) || astISBAD(basebox[3]) || + astISBAD(basebox[4]) || astISBAD(basebox[5]) ) { + astError( AST__BADBX, "astInitPlot3D(%s): The limits of " + "the %s are undefined or bad.", status, name, name ); + } + +/* Create a Frame which describes the graphics world coordinate system. */ + graphicsframe = astFrame( 3, "Domain=GRAPHICS,Title=Graphical Coordinates", status ); + +/* Initialise a 2D Plot structure (the parent class) as the first component + within the Plot3D structure, allocating memory if necessary. We supply + dummy arguments since we will not be using the parent Plot class to + draw anything. We supply a NULL vtab pointer so that methods defined by + the Plot class will be used during the construction of the Plot3D. Once + the Plot3D is fully constructed, we will use astSetVtab to establish + the correct vtab. */ + new = (AstPlot3D *) astInitPlot( mem, size, 0, NULL, name, NULL, fjunkbox, + djunkbox ); + if ( astOK ) { + +/* Initialise the Plot3D data. */ +/* ----------------------------- */ + +/* Remove all Frames from the parent FrameSet except for the base (2D graphics) + Frame (we leave this last Frame since an error is reported if the last + Frame is removed from a FrameSet). We do this by repeatedly removing the + first Frame, causing all remaining Frames to have their index reduced by 1. + When the base Frame arrives at index 1, we skip it and start removing the + second frame instead. */ + nfrm = astGetNframe( new ); + i = 1; + for( ii = 0; ii < nfrm; ii++ ) { + if( i > 1 || astGetBase( new ) != 1 ) { + astRemoveFrame( new, i ); + } else { + i = 2; + } + } + +/* Add in the 3D graphics Frame, using a PermMap to connect it to the + 2D graphics Frame. */ + map = (AstMapping *) astPermMap( 2, NULL, 3, NULL, NULL, "", status ); + astAddFrame( new, 1, map, graphicsframe ); + map = astAnnul( map ); + +/* And remove the 2D GRAPHICS Frame, leaving just the 3D GRAPHICS Frame + in the FrameSet, with index 1. */ + astRemoveFrame( new, 1 ); + +/* Get the index of the current (physical) and base (pixel) Frames in + the supplied FrameSet. */ + bi = astGetBase( fset ); + ci = astGetCurrent( fset ); + +/* Temporarily set the current Frame to be the pixel frame. */ + astSetCurrent( fset, bi ); + +/* Get a double precision version of "graphbox", and store it in the + Plot3D structure. */ + new->gbox[ 0 ] = (double) graphbox[ 0 ]; + new->gbox[ 1 ] = (double) graphbox[ 1 ]; + new->gbox[ 2 ] = (double) graphbox[ 2 ]; + new->gbox[ 3 ] = (double) graphbox[ 3 ]; + new->gbox[ 4 ] = (double) graphbox[ 4 ]; + new->gbox[ 5 ] = (double) graphbox[ 5 ]; + +/* The base Frame of the supplied FrameSet is mapped linearly onto the + graphics frame. Create a WinMap that maps the base box (within the + base Frame of the supplied FrameSet) onto the graphics box. */ + map = (AstMapping *) astWinMap( 3, new->gbox, new->gbox + 3, basebox, + basebox + 3, "", status ); + +/* Add the supplied FrameSet into the Plot3D (i.e. FrameSet) created + earlier. This leaves the graphics frame with index 1 in the + returned Plot3D. */ + astAddFrame( (AstFrameSet *) new, 1, map, fset ); + map = astAnnul( map ); + +/* Set the current Frame in the Plot to be the physical coordinate Frame + (with index incremented by one because the graphics Frame has been added). */ + astSetCurrent( (AstFrameSet *) new, ci + 1 ); + +/* Note the index of the original base Frame in the Plot3D FrameSet */ + new->pix_frame = bi + 1; + +/* Re-establish the original current Frame in the supplied FrameSet. */ + astSetCurrent( fset, ci ); + +/* Initialise the Plot pointers. */ + new->plotxy = NULL; + new->plotxz = NULL; + new->plotyz = NULL; + +/* Initialise other attributes */ + new->rootcorner = -1; + +/* Initialise the normal vector to the plane used by astText and astMark. */ + new->norm[ 0 ] = AST__BAD; + new->norm[ 1 ] = AST__BAD; + new->norm[ 2 ] = AST__BAD; + +/* Create three 2D Plots to describe the three planes in the cube. */ + CreatePlots( new, fset, graphbox, basebox, status ); + +/* Ensure that attempts to use the graphics interface of the parent + Plot structure get forwarded to the relevant 3D routines defined in + this class. */ + astGrfSet( new, "Attr", (AstGrfFun) Attr3D ); + astSetGrf( new, 1 ); + +/* Change the virtual function table stored in the new Plot3D, from the Plot + vtab (established when astINitPlot was called above), to the supplied + vtab. */ + if( vtab ) astSetVtab( new, vtab ); + +/* Ensure that these Plots use the grf functions defined by this class + (Plot3D). This means that whenever a Plot draws anything, it will use + the appropriate grf function defined in this class to do the drawing. + The grf functions defined in this class, convert the grf call into a + grf3D call apprpriate the plane spanned by the Plot. */ + Set3DGrf( new, new->plotxy, XY, status ); + Set3DGrf( new, new->plotxz, XZ, status ); + Set3DGrf( new, new->plotyz, YZ, status ); + +/* Set up the Edges attributes in the encapsulated Plots so that labels + appear on the requited edges. Initially, the root corner is "LLL" + (i.e. the lower bound on every axis). */ + ChangeRootCorner( new, 0, 0, status ); + } + +/* Annul the frame. */ + graphicsframe = astAnnul( graphicsframe ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + +/* Annul the pointer to the base Frame and FrameSet. */ + baseframe = astAnnul( baseframe ); + fset = astAnnul( fset ); + +/* Return a pointer to the new object. */ + return new; +} + + +AstPlot3D *astLoadPlot3D_( void *mem, size_t size, AstPlot3DVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPlot3D + +* Purpose: +* Load a Plot3D. + +* Type: +* Protected function. + +* Synopsis: +* #include "plot3d.h" +* AstPlot3D *astLoadPlot3D( void *mem, size_t size, +* AstPlot3DVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Plot3D loader. + +* Description: +* This function is provided to load a new Plot3D using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Plot3D structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the Plot3D is to be +* loaded. This must be of sufficient size to accommodate the +* Plot3D data (sizeof(Plot3D)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Plot3D (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Plot3D structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPlot3D) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Plot3D. If this is NULL, a pointer +* to the (static) virtual function table for the Plot3D class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Plot3D" is used instead. + +* Returned Value: +* A pointer to the new Plot3D. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstPlot3D *new; + char key[ KEY_LEN + 1 ]; + char *text; + int axis; + int i; + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Plot3D. In this case the + Plot3D belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPlot3D ); + vtab = &class_vtab; + name = "Plot3D"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPlot3DVtab( vtab, name ); + class_init = 1; + } + } + +/* Allocate memory to hold the new Object. We allocate it now rather than + waiting for astInitObject to allocate it so that we can pass a NULL + vtab pointer to the Plot loader, thus causing the Plot loader to use the + function implementations provided by the Plot class rather than those + provided by the class being instantiated. */ + if( !mem ) mem = astMalloc( size ); + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Plot3D. Pass a NULL vtab pointer so that the "new" object + will be loaded using Plot methods rather than than Plot3D methods. + This is important because the implementations provided by the Plot3D + class for the Plot attribute accessors require the existence of the + encapsulated Plots held within the Plot3D, but these have not yet been + created. */ + new = astLoadPlot( mem, size, NULL, name, channel ); + if ( astOK ) { + +/* Now modify the new object to use the supplied vtab. */ + astSetVtab( new, vtab ); + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Plot3D" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Norm. */ +/* ----- */ + for( axis = 0; axis < 3; axis++ ) { + (void) sprintf( key, "norm%d", axis + 1 ); + new->norm[ axis ] = astReadDouble( channel, key, AST__BAD ); + if( TestNorm( new, axis, status ) ) SetNorm( new, axis, new->norm[ axis ], status ); + } + +/* RootCorner. */ +/* ----------- */ + text = astReadString( channel, "rootcn", " " ); + if( astOK && strcmp( text, " " ) ) { + new->rootcorner = RootCornerInt( text, status ); + if( new->rootcorner < 0 && astOK ) { + astError( AST__INTER, "astRead(Plot3D): Corrupt Plot3D contains " + "invalid RootCorner attribute value (%s).", status, text ); + } + } else { + new->rootcorner = -1; + } + text = astFree( text ); + +/* Labelled axes */ + new->axis_plot1[0] = astReadInt( channel, "axplx1", -1 ); + new->axis_plot1[1] = astReadInt( channel, "axply1", -1 ); + new->axis_plot1[2] = astReadInt( channel, "axplz1", -1 ); + + new->axis_index1[0] = astReadInt( channel, "axinx1", -1 ); + new->axis_index1[1] = astReadInt( channel, "axiny1", -1 ); + new->axis_index1[2] = astReadInt( channel, "axinz1", -1 ); + +/* Unlabelled axes */ + new->axis_plot2[0] = astReadInt( channel, "axplx2", -1 ); + new->axis_plot2[1] = astReadInt( channel, "axply2", -1 ); + new->axis_plot2[2] = astReadInt( channel, "axplz2", -1 ); + + new->axis_index2[0] = astReadInt( channel, "axinx2", -1 ); + new->axis_index2[1] = astReadInt( channel, "axiny2", -1 ); + new->axis_index2[2] = astReadInt( channel, "axinz2", -1 ); + +/* Plot that spans two connected 3D axes. */ + new->baseplot = astReadInt( channel, "basepl", -1 ); + +/* XY Plot */ + new->plotxy = astReadObject( channel, "plotxy", NULL ); + +/* XZ Plot */ + new->plotxz = astReadObject( channel, "plotxz", NULL ); + +/* YZ Plot */ + new->plotyz = astReadObject( channel, "plotyz", NULL ); + +/* The index within the Plot3D FrameSet, of the original base Frame in + the FrameSet supplied when the Plot3D was constructed. */ + new->pix_frame = astReadInt( channel, "pixfrm", AST__NOFRAME ); + +/* Ensure that these Plots use the grf functions defined by this class + (Plot3D). This means that whener a Plot draws anything, it will use + the appropriate grf function defined in this class to do the drawing. + The grf functions defined in this class, convert the grf call into a + grf3D call apprpriate the plane spanned by the Plot. */ + Set3DGrf( new, new->plotxy, XY, status ); + Set3DGrf( new, new->plotxz, XZ, status ); + Set3DGrf( new, new->plotyz, YZ, status ); + +/* For attributes of the parent Plot class will have been loaded + each attribute that has a set value in the parent Plot structure, + re-set the value so that it gets copied to the copy the to the + encapsulated Plots. First do axis specific attributes. */ + +#define COPY_ATTR(attr,nval) \ + for( i = 0; i < nval; i++ ) { \ + if( astTest##attr(new,i) ) astSet##attr(new,i,astGet##attr(new,i)); \ + } + + COPY_ATTR(MinTick,3) + COPY_ATTR(Abbrev,3) + COPY_ATTR(Gap,3) + COPY_ATTR(LogGap,3) + COPY_ATTR(LogPlot,3) + COPY_ATTR(LogTicks,3) + COPY_ATTR(LogLabel,3) + COPY_ATTR(LabelUp,3) + COPY_ATTR(DrawAxes,3) + COPY_ATTR(LabelUnits,3) + COPY_ATTR(MinTickLen,3) + COPY_ATTR(MajTickLen,3) + COPY_ATTR(NumLab,3) + COPY_ATTR(NumLabGap,3) + COPY_ATTR(TextLab,3) + COPY_ATTR(TextLabGap,3) + + COPY_ATTR(Style,AST__NPID) + COPY_ATTR(Font,AST__NPID) + COPY_ATTR(Colour,AST__NPID) + COPY_ATTR(Width,AST__NPID) + COPY_ATTR(Size,AST__NPID) + +#undef COPY_ATTR + +/* Now do attributes that are not axis specific. */ + +#define COPY_ATTR(attr) \ + if( astTest##attr(new) ) astSet##attr(new,astGet##attr(new)); + + COPY_ATTR(Ink) + COPY_ATTR(Tol) + COPY_ATTR(Invisible) + COPY_ATTR(TickAll) + COPY_ATTR(ForceExterior) + COPY_ATTR(Border) + COPY_ATTR(Clip) + COPY_ATTR(ClipOp) + COPY_ATTR(Escape) + COPY_ATTR(Grid) + COPY_ATTR(Labelling) + +#undef COPY_ATTR + +/* If an error occurred, clean up by deleting the new Plot3D. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Plot3D pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astClearRootCorner_( AstPlot3D *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Plot3D,ClearRootCorner))( this, status ); +} + +void astSetRootCorner_( AstPlot3D *this, int value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Plot3D,SetRootCorner))( this, value, status ); +} + + + + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPlot3D *astPlot3DId_( void *frame_void, const float graphbox[6], + const double basebox[6], const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstPlot3D *astPlot3DId_( void *frame_void, const float graphbox[6], + const double basebox[6], const char *options, ... ) { +/* +*++ +* Name: +c astPlot3D +f AST_PLOT3D + +* Purpose: +* Create a Plot3D. + +* Type: +* Public function. + +* Synopsis: +c #include "plot3d.h" +c AstPlot3D *astPlot3D( AstFrame *frame, const float graphbox[ 6 ], +c const double basebox[ 6 ], const char *options, ... ) +f RESULT = AST_PLOT3D( FRAME, GRAPHBOX, BASEBOX, OPTIONS, STATUS ) + +* Class Membership: +* Plot3D constructor. + +* Description: +* This function creates a new Plot3D and optionally initialises +* its attributes. +* +* A Plot3D is a specialised form of Plot that provides facilities +* for producing 3D graphical output. + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* Pointer to a Frame describing the physical coordinate system +* in which to plot. A pointer to a FrameSet may also be given, +* in which case its current Frame will be used to define the +* physical coordinate system and its base Frame will be mapped +* on to graphical coordinates (see below). +* +* If a null Object pointer (AST__NULL) is given, a default +* 3-dimensional Frame will be used to describe the physical +* coordinate system. Labels, etc. may then be attached to this +* by setting the appropriate Frame attributes +* (e.g. Label(axis)) for the Plot. +c graphbox +f GRAPHBOX( 6 ) = REAL (Given) +* An array giving the position and extent of the plotting volume +* (within the plotting space of the underlying graphics system) +* in which graphical output is to appear. This must be +* specified using graphical coordinates appropriate to the +* underlying graphics system. +* +* The first triple of values should give the coordinates of the +* bottom left corner of the plotting volume and the second triple +* should give the coordinates of the top right corner. The +* coordinate on the horizontal axis should be given first in +* each pair. Note that the order in which these points are +* given is important because it defines up, down, left and +* right for subsequent graphical operations. +c basebox +f BASEBOX( 6 ) = DOUBLE PRECISION (Given) +* An array giving the coordinates of two points in the supplied +* Frame (or in the base Frame if a FrameSet was supplied) which +* correspond to the bottom left and top right corners of the +* plotting volume, as specified above. This range of coordinates +* will be mapped linearly on to the plotting area. The +* coordinates should be given in the same order as above. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Plot3D. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Plot3D. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPlot3D() +f AST_PLOT3D = INTEGER +* A pointer to the new Plot3D. + +* Notes: +* - The base Frame of the returned Plot3D will be a new Frame which +* is created by this function to represent the coordinate system +* of the underlying graphics system (graphical coordinates). It is +* given a Frame index of 1 within the Plot3D. The choice of base +* Frame (Base attribute) should not, in general, be changed once a +* Plot3D has been created (although you could use this as a way of +* moving the plotting area around on the plotting surface). +c - If a Frame is supplied (via the "frame" pointer), then it +f - If a Frame is supplied (via the FRAME pointer), then it +* becomes the current Frame of the new Plot3D and is given a Frame +* index of 2. +c - If a FrameSet is supplied (via the "frame" pointer), then +f - If a FrameSet is supplied (via the FRAME pointer), then +* all the Frames within this FrameSet become part of the new Plot3D +* (where their Frame indices are increased by 1), with the +* FrameSet's current Frame becoming the current Frame of the Plot3D. +* - At least one of the three axes of the current Frame must be +* independent of the other two current Frame axes. +* - If a null Object pointer (AST__NULL) is supplied (via the +c "frame" pointer), then the returned Plot3D will contain two +f FRAME pointer), then the returned Plot3D will contain two +* Frames, both created by this function. The base Frame will +* describe graphics coordinates (as above) and the current Frame +* will be a basic Frame with no attributes set (this will +* therefore give default values for such things as the Plot3D Title +* and the Label on each axis). Physical coordinates will be mapped +* linearly on to graphical coordinates. +* - An error will result if the Frame supplied (or the base Frame +* if a FrameSet was supplied) is not 3-dimensional. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astPlot3D constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astPlot3D_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "frame" parameter is of type +* (void *) and is converted from an ID value to a pointer and +* validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astPlot3D_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPlot3D *new; /* Pointer to new Plot3D */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + new = NULL; + +/* Obtain a Frame pointer from any ID supplied and validate the + pointer to ensure it identifies a valid Frame. */ + if( frame_void ){ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + } else { + frame = NULL; + } + +/* Check the pointer can be used. */ + if ( astOK ) { + +/* Initialise the Plot3D, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPlot3D( NULL, sizeof( AstPlot3D ), !class_init, + &class_vtab, "Plot3D", frame, graphbox, + basebox ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + Plot3D's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new Plot3D. */ + return astMakeId( new ); + +} + + + +/* Macros that define method to override the methods of the Plot class + that are not currently implemented by this class. They just report an + error if called. */ + +#define METHOD1(name) \ +static void name(ARGLIST,int *status){ \ + if( !astOK ) return; \ + astError( AST__INTER, "ast##name(%s): The ast##name " \ + "method cannot be used with a %s (programming error).", status, \ + astGetClass( this ), astGetClass( this ) ); \ +} + +#define METHOD2(name,rettype,retval) \ +static rettype name(ARGLIST,int *status){ \ + if( !astOK ) return retval; \ + astError( AST__INTER, "ast##name(%s): The ast##name " \ + "method cannot be used with a %s (programming error).", status, \ + astGetClass( this ), astGetClass( this ) ); \ + return retval; \ +} + + +#define ARGLIST AstPlot *this +METHOD2(GetGrfContext,AstKeyMap *,NULL) +#undef ARGLIST + +#define ARGLIST AstPlot *this +METHOD1(GrfPop) +#undef ARGLIST + +#define ARGLIST AstPlot *this +METHOD1(GrfPush) +#undef ARGLIST + +#define ARGLIST AstPlot *this, const char *name, AstGrfFun fun +METHOD1(GrfSet) +#undef ARGLIST + +#define ARGLIST AstPlot *this, int axis, const double start[], double length +METHOD1(GridLine) +#undef ARGLIST + +#define ARGLIST AstPlot *this, float lbnd[2], float ubnd[2] +METHOD1(BoundingBox) +#undef ARGLIST + +#define ARGLIST AstPlot *this, int iframe, const double lbnd[], const double ubnd[] +METHOD1(Clip) +#undef ARGLIST + +#define ARGLIST AstPlot *this, const double start[], const double finish[] +METHOD1(Curve) +#undef ARGLIST + +#define ARGLIST AstPlot *this, AstMapping *map +METHOD1(GenCurve) +#undef ARGLIST + + + + + + + + diff --git a/plot3d.h b/plot3d.h new file mode 100644 index 0000000..4826b44 --- /dev/null +++ b/plot3d.h @@ -0,0 +1,258 @@ +#if !defined( PLOT3D_INCLUDED ) /* Include this file only once */ +#define PLOT3D_INCLUDED +/* +*+ +* Name: +* plot3d.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Plot3D class. + +* Invocation: +* #include "plot3d.h" + +* Description: +* This include file defines the interface to the Plot3D class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 6-JUN-2007 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "plot.h" /* Parent Plot class */ + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + + +#if defined(astCLASS) /* Protected */ + +#endif + +/* Type Definitions. */ +/* ================= */ + +/* Plot3D structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstPlot3D { + +/* Attributes inherited from the parent class. */ + AstPlot plot; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstPlot *plotxy; /* Plot describing the XY plane */ + AstPlot *plotxz; /* Plot describing the XZ plane */ + AstPlot *plotyz; /* Plot describing the YZ plane */ + double gbox[6]; /* Graphics box */ + int pix_frame; /* Index of original base Frame */ + int rootcorner; /* Corner at junction of the annotated axes */ + int baseplot; /* The Plot that is used to label 2 3D axes */ + int axis_plot1[3]; /* The Plot used to label each 3D axis */ + int axis_index1[3]; /* The axis index within the axis_plot1 Plot */ + int axis_plot2[3]; /* The other Plot touching each 3D axis */ + int axis_index2[3]; /* The axis index within the axis_plot2 Plot */ + double norm[3]; /* Normal vector for text plane */ +} AstPlot3D; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPlot3DVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstPlotVtab plot_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + + int (* GetRootCorner)( AstPlot3D *, int * ); + int (* TestRootCorner)( AstPlot3D *, int * ); + void (* SetRootCorner)( AstPlot3D *, int, int * ); + void (* ClearRootCorner)( AstPlot3D *, int * ); + + double (* GetNorm)( AstPlot3D *, int, int * ); + int (* TestNorm)( AstPlot3D *, int, int * ); + void (* SetNorm)( AstPlot3D *, int, double, int * ); + void (* ClearNorm)( AstPlot3D *, int, int * ); + +} AstPlot3DVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstPlot3DGlobals { + AstPlot3DVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstPlot3DGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Plot3D) /* Check class membership */ +astPROTO_ISA(Plot3D) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstPlot3D *astPlot3D_( void *, const float *, const double *, const char *, int *, ...); +#else +AstPlot3D *astPlot3DId_( void *, const float [], const double [], const char *, ... ); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPlot3D *astInitPlot3D_( void *, size_t, int, AstPlot3DVtab *, + const char *, AstFrame *, const float *, + const double *, int * ); + +/* Vtab initialiser. */ +void astInitPlot3DVtab_( AstPlot3DVtab *, const char *, int * ); + +/* Loader. */ +AstPlot3D *astLoadPlot3D_( void *, size_t, + AstPlot3DVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitPlot3DGlobals_( AstPlot3DGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +#if defined(astCLASS) /* Protected */ + + int astGetRootCorner_( AstPlot3D *, int * ); + int astTestRootCorner_( AstPlot3D *, int * ); + void astSetRootCorner_( AstPlot3D *, int, int * ); + void astClearRootCorner_( AstPlot3D *, int * ); + + double astGetNorm_( AstPlot3D *, int, int * ); + int astTestNorm_( AstPlot3D *, int, int * ); + void astSetNorm_( AstPlot3D *, int, double, int * ); + void astClearNorm_( AstPlot3D *, int, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPlot3D(this) astINVOKE_CHECK(Plot3D,this,0) +#define astVerifyPlot3D(this) astINVOKE_CHECK(Plot3D,this,1) + +/* Test class membership. */ +#define astIsAPlot3D(this) astINVOKE_ISA(Plot3D,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astPlot3D astINVOKE(F,astPlot3D_) +#else +#define astPlot3D astINVOKE(F,astPlot3DId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPlot3D(mem,size,init,vtab,name,frame,graph,base) \ +astINVOKE(O,astInitPlot3D_(mem,size,init,vtab,name,frame,graph,base,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPlot3DVtab(vtab,name) astINVOKE(V,astInitPlot3DVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadPlot3D(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPlot3D_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) + +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +/* Here we make use of astCheckPlot3D to validate Plot3D pointers + before use. This provides a contextual error report if a pointer to + the wrong sort of object is supplied. */ + +#if defined(astCLASS) /* Protected */ + +#define astGetRootCorner(this) astINVOKE(V,astGetRootCorner_(astCheckPlot3D(this),STATUS_PTR)) +#define astTestRootCorner(this) astINVOKE(V,astTestRootCorner_(astCheckPlot3D(this),STATUS_PTR)) +#define astClearRootCorner(this) astINVOKE(V,astClearRootCorner_(astCheckPlot3D(this),STATUS_PTR)) +#define astSetRootCorner(this,value) astINVOKE(V,astSetRootCorner_(astCheckPlot3D(this),value,STATUS_PTR)) + +#define astGetNorm(this,axis) astINVOKE(V,astGetNorm_(astCheckPlot3D(this),axis,STATUS_PTR)) +#define astTestNorm(this,axis) astINVOKE(V,astTestNorm_(astCheckPlot3D(this),axis,STATUS_PTR)) +#define astClearNorm(this,axis) astINVOKE(V,astClearNorm_(astCheckPlot3D(this),axis,STATUS_PTR)) +#define astSetNorm(this,axis,value) astINVOKE(V,astSetNorm_(astCheckPlot3D(this),axis,value,STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/pointlist.c b/pointlist.c new file mode 100644 index 0000000..61117c2 --- /dev/null +++ b/pointlist.c @@ -0,0 +1,3407 @@ +/* +*class++ +* Name: +* PointList + +* Purpose: +* A collection of points in a Frame. + +* Constructor Function: +c astPointList +f AST_POINTLIST + +* Description: +* The PointList class implements a Region which represents a collection +* of points in a Frame. + +* Inheritance: +* The PointList class inherits from the Region class. + +* Attributes: +* In addition to those attributes common to all Regions, every +* PointList also has the following attributes: +* +* - ListSize: The number of positions stored in the PointList + + +* Functions: +c The PointList class does not define any new functions beyond those +f The PointList class does not define any new routines beyond those +* which are applicable to all Regions. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-MAR-2004 (DSB): +* Original version. +* 20-JAN-2009 (DSB): +* Over-ride astRegBasePick. +* 21-JAN-2009 (DSB): +* - Add methods astGetEnclosure, astSetEnclosure and astPoints, and +* attribute ListSize. +* - Override astGetObjSize and astEqual. +* 26-JAN-2009 (DSB): +* Change protected constructor to accept a PointSet rather than an +* array of doubles. +* 6-FEB-2009 (DSB): +* Over-ride astMapMerge. +* 9-FEB-2009 (DSB): +* Move methods astGetEnclosure and astSetEnclosure to Region class. +* 8-JUL-2009 (DSB): +* In Transform, use "ptr2", not "ptr", if we are creating a mask. +*class-- + +* Implementation Deficiencies: +* - Use of simple arrays to hold lists of points is probably not +* efficient for large numbers of points. For instance, use of k-tree +* structures instead of arrays could result in a much more efficient +* implementation of the Transform function. Maybe the PointSet class +* should be extended to provide a k-tree representation as well as a +* simple array. + +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS PointList + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "pointlist.h" /* Interface definition for this class */ +#include "mapping.h" /* Position mappings */ +#include "unitmap.h" /* Unit Mapping */ +#include "frame.h" /* Coordinate systems */ +#include "cmpframe.h" /* Compound Frames */ +#include "cmpmap.h" /* Compound Mappings */ +#include "prism.h" /* Extruded Regions */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(PointList) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(PointList,Class_Init) +#define class_vtab astGLOBAL(PointList,Class_Vtab) +#define getattrib_buff astGLOBAL(PointList,GetAttrib_Buff) + + +#include + + +#else + +static char getattrib_buff[ 101 ]; + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPointListVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPointList *astPointListId_( void *, int, int, int, const double *, void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +static int MaskLD( AstRegion *, AstMapping *, int, int, const int[], const int ubnd[], long double [], long double, int * ); +#endif +static int MaskB( AstRegion *, AstMapping *, int, int, const int[], const int[], signed char[], signed char, int * ); +static int MaskD( AstRegion *, AstMapping *, int, int, const int[], const int[], double[], double, int * ); +static int MaskF( AstRegion *, AstMapping *, int, int, const int[], const int[], float[], float, int * ); +static int MaskI( AstRegion *, AstMapping *, int, int, const int[], const int[], int[], int, int * ); +static int MaskL( AstRegion *, AstMapping *, int, int, const int[], const int[], long int[], long int, int * ); +static int MaskS( AstRegion *, AstMapping *, int, int, const int[], const int[], short int[], short int, int * ); +static int MaskUB( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned char[], unsigned char, int * ); +static int MaskUI( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned int[], unsigned int, int * ); +static int MaskUL( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned long int[], unsigned long int, int * ); +static int MaskUS( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned short int[], unsigned short int, int * ); + +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *RegBasePick( AstRegion *, int, const int *, int * ); +static int GetClosed( AstRegion *, int * ); +static int GetListSize( AstPointList *, int * ); +static int GetObjSize( AstObject *, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void PointListPoints( AstPointList *, AstPointSet **, int *); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void RegBaseBox( AstRegion *, double *, double *, int * ); +static AstRegion *MergePointList( AstPointList *, AstRegion *, int, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + + +/* Member functions. */ +/* ================= */ + +static void ClearAttrib( AstObject *this_object, const char *attrib, + int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a PointList. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PointList member function (over-rides the astClearAttrib +* protected method inherited from the Object class). + +* Description: +* This function clears the value of a specified attribute for a +* PointList, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the PointList. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPointList *this; /* Pointer to the PointList structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PointList structure. */ + this = (AstPointList *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Test if the name matches any of the read-only attributes of this + class. If it does, then report an error. */ + if ( !strcmp( attrib, "listsize" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, + int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a PointList. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PointList member function (over-rides the protected astGetAttrib +* method inherited from the Region class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a PointList, formatted as a character string. + +* Parameters: +* this +* Pointer to the PointList. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the PointList, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the PointList. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPointList *this; /* Pointer to the PointList structure */ + const char *result; /* Pointer value to return */ + int ival; /* Integer attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the PointList structure. */ + this = (AstPointList *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an + appropriate format. Set "result" to point at the result string. */ + +/* ListSize. */ +/* -------- */ + if ( !strcmp( attrib, "listsize" ) ) { + ival = astGetListSize( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetClosed( AstRegion *this, int *status ) { +/* +* Name: +* GetClosed + +* Purpose: +* Get the value of the CLosed attribute for a PointList. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* int GetClosed( AstRegion *this, int *status ) + +* Class Membership: +* PointList member function (over-rides the astGetClosed method +* inherited from the Region class). + +* Description: +* This function returns the value of the Closed attribute for a +* PointList. Since points have zero volume they consist entirely of +* boundary. Therefore the Region is always considered to be closed +* unless it has been negated, in which case it is always assumed to +* be open. + +* Parameters: +* this +* Pointer to the PointList. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to use for the Closed attribute. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* The value to be used for the Closed attribute is always the opposite + of the Negated attribute. */ + return ( astGetNegated( this ) == 0 ); +} + +static int GetListSize( AstPointList *this, int *status ) { +/* +*+ +* Name: +* astGetListSize + +* Purpose: +* Determine how many points there are in a PointList. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointlist.h" +* int astGetListSize( AstPointList *this ) + +* Class Membership: +* PointList method. + +* Description: +* This function returns the number of points stored in a Point|List. + +* Parameters: +* this +* Pointer to the PointList. + +* Returned Value: +* The number of points in the PointList. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the number of points by querying the PointSet that holds the + points. */ + return astGetNpoint( ((AstRegion *) this)->points ); +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* PointList member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied PointList, +* in bytes. + +* Parameters: +* this +* Pointer to the PointList. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPointList *this; /* Pointer to PointList structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the PointList structure. */ + this = (AstPointList *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->lbnd ); + result += astGetObjSize( this->ubnd ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitPointListVtab_( AstPointListVtab *vtab, const char *name, + int *status ) { +/* +*+ +* Name: +* astInitPointListVtab + +* Purpose: +* Initialise a virtual function table for a PointList. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointlist.h" +* void astInitPointListVtab( AstPointListVtab *vtab, const char *name ) + +* Class Membership: +* PointList vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the PointList class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPointList) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->GetListSize = GetListSize; + vtab->PointListPoints = PointListPoints; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping->MapMerge = MapMerge; + + region->RegBaseMesh = RegBaseMesh; + region->RegBaseBox = RegBaseBox; + region->RegBasePick = RegBasePick; + region->RegPins = RegPins; + region->GetClosed = GetClosed; + region->MaskB = MaskB; + region->MaskD = MaskD; + region->MaskF = MaskF; + region->MaskI = MaskI; + region->MaskL = MaskL; + region->MaskS = MaskS; + region->MaskUB = MaskUB; + region->MaskUI = MaskUI; + region->MaskUL = MaskUL; + region->MaskUS = MaskUS; +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + region->MaskLD = MaskLD; +#endif + +/* Declare the class dump function. There is no copy constructor or + destructor. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "PointList", "Collection of points" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +/* +* Name: +* Mask + +* Purpose: +* Mask a region of a data grid. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* int Mask( AstRegion *this, AstMapping *map, int inside, int ndim, +* const int lbnd[], const int ubnd[], in[], +* val ) + +* Class Membership: +* PointList function method (replaces the astMask methods +* inherited from the parent Region class). + +* Description: +* This is a set of functions for masking out regions within gridded data +* (e.g. an image). The functions modifies a given data grid by +* assigning a specified value to all samples which are inside (or outside +* if "inside" is zero) the specified Region. +* +* You should use a masking function which matches the numerical +* type of the data you are processing by replacing in +* the generic function name astMask by an appropriate 1- or +* 2-character type code. For example, if you are masking data +* with type "float", you should use the function astMaskF (see +* the "Data Type Codes" section below for the codes appropriate to +* other numerical types). + +* Parameters: +* this +* Pointer to a Region. +* map +* Pointer to a Mapping. The forward transformation should map +* positions in the coordinate system of the supplied Region +* into pixel coordinates as defined by the "lbnd" and "ubnd" +* parameters. A NULL pointer can be supplied if the coordinate +* system of the supplied Region corresponds to pixel coordinates. +* This is equivalent to supplying a UnitMap. +* +* The number of inputs for this Mapping (as given by its Nin attribute) +* should match the number of axes in the supplied Region (as given +* by the Naxes attribute of the Region). The number of outputs for the +* Mapping (as given by its Nout attribute) should match the number of +* grid dimensions given by the value of "ndim" below. +* inside +* A boolean value which indicates which pixel are to be masked. If +* a non-zero value is supplied, then all grid pixels which are inside +* the supplied Region are assigned the value given by "val", +* and all other pixels are left unchanged. If zero is supplied, then +* all grid pixels which are not inside the supplied Region are +* assigned the value given by "val", and all other pixels are left +* unchanged. Note, the Negated attribute of the Region is used to +* determine which pixel are inside the Region and which are outside. +* So the inside of a Region which has not been negated is the same as +* the outside of the corresponding negated Region. +* ndim +* The number of dimensions in the input grid. This should be at +* least one. +* lbnd +* Pointer to an array of integers, with "ndim" elements, +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +* ubnd +* Pointer to an array of integers, with "ndim" elements, +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +* Note that "lbnd" and "ubnd" together define the shape +* and size of the input grid, its extent along a particular +* (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the +* index "j" to be zero-based). They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre. +* in +* Pointer to an array, with one element for each pixel in the +* input grid, containing the data to be masked. The +* numerical type of this array should match the 1- or +* 2-character type code appended to the function name (e.g. if +* you are using astMaskF, the type of each array element +* should be "float"). +* +* The storage order of data within this array should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +* (i.e. Fortran array indexing is used). +* +* On exit, the samples specified by "inside" are set to the value +* of "val". All other samples are left unchanged. +* val +* This argument should have the same type as the elements of +* the "in" array. It specifies the value used to flag the +* masked data (see "inside"). + +* Returned Value: +* The number of pixels to which a value of "badval" has been assigned. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Data Type Codes: +* To select the appropriate masking function, you should +* replace in the generic function name astMask with a +* 1- or 2-character data type code, so as to match the numerical +* type of the data you are processing, as follows: +* - D: double +* - F: float +* - L: long int +* - UL: unsigned long int +* - I: int +* - UI: unsigned int +* - S: short int +* - US: unsigned short int +* - B: byte (signed char) +* - UB: unsigned byte (unsigned char) +* +* For example, astMaskD would be used to process "double" +* data, while astMaskS would be used to process "short int" +* data, etc. +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_MASK(X,Xtype) \ +static int Mask##X( AstRegion *this, AstMapping *map, int inside, int ndim, \ + const int lbnd[], const int ubnd[], \ + Xtype in[], Xtype val, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *grid_frame; /* Pointer to Frame describing grid coords */ \ + AstPointSet *pset1; /* Pointer to base Frame positions */ \ + AstPointSet *pset2; /* Pointer to current Frame positions */ \ + AstRegion *used_region; /* Pointer to Region to be used by astResample */ \ + Xtype *temp; /* Pointer to temp storage for retained points */ \ + double **ptr2; /* Pointer to pset2 data values */ \ + int *iv; /* Pointer to index array */ \ + int i; /* Point index */ \ + int idim; /* Loop counter for coordinate dimensions */ \ + int ii; /* Vectorised point index */ \ + int j; /* Axis index */ \ + int nax; /* Number of Region axes */ \ + int negated; /* Has Region been negated? */ \ + int nin; /* Number of Mapping input coordinates */ \ + int nout; /* Number of Mapping output coordinates */ \ + int npnt; /* Number of points in PointList */ \ + int result; /* Result value to return */ \ + int vlen; /* Length of vectorised array */ \ +\ +/* Initialise. */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Obtain value for the Naxes attribute of the Region. */ \ + nax = astGetNaxes( this ); \ +\ +/* If supplied, obtain values for the Nin and Nout attributes of the Mapping. */ \ + if( map ) { \ + nin = astGetNin( map ); \ + nout = astGetNout( map ); \ +\ +/* If OK, check that the number of mapping inputs matches the \ + number of axes in the Region. Report an error if necessary. */ \ + if ( astOK && ( nax != nin ) ) { \ + astError( AST__NGDIN, "astMask"#X"(%s): Bad number of mapping " \ + "inputs (%d).", status, astGetClass( this ), nin ); \ + astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ + "to specify a position.", status, \ + astGetClass( this ), nax, ( nax == 1 ) ? "" : "s" ); \ + } \ +\ +/* If OK, check that the number of mapping outputs matches the \ + number of grid dimensions. Report an error if necessary. */ \ + if ( astOK && ( ndim != nout ) ) { \ + astError( AST__NGDIN, "astMask"#X"(%s): Bad number of mapping " \ + "outputs (%d).", status, astGetClass( this ), nout ); \ + astError( AST__NGDIN, "The pixel grid requires %d coordinate value%s " \ + "to specify a position.", status, \ + ndim, ( ndim == 1 ) ? "" : "s" ); \ + } \ +\ +/* Create a new Region by mapping the supplied Region with the supplied \ + Mapping.*/ \ + grid_frame = astFrame( ndim, "Domain=grid", status ); \ + used_region = astMapRegion( this, map, grid_frame ); \ + grid_frame = astAnnul( grid_frame ); \ +\ +/* If no Mapping was supplied check that the number of grid dimensions \ + matches the number of axes in the Region.*/ \ + } else if ( astOK && ( ( ndim != nax ) || ( ndim < 1 ) ) ) { \ + used_region = NULL; \ + astError( AST__NGDIN, "astMask"#X"(%s): Bad number of input grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim ); \ + if ( ndim != nax ) { \ + astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ + "to specify an input position.", status, \ + astGetClass( this ), nax, ( nax == 1 ) ? "" : "s" ); \ + } \ +\ +/* If no Mapping was supplied and the parameters look OK, clone the \ + supplied Region pointer for use later on. */ \ + } else { \ + used_region = astClone( this ); \ + } \ +\ +/* Check that the lower and upper bounds of the input grid are \ + consistent. Report an error if any pair is not. */ \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim; idim++ ) { \ + if ( lbnd[ idim ] > ubnd[ idim ] ) { \ + astError( AST__GBDIN, "astMask"#X"(%s): Lower bound of " \ + "input grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd[ idim ], ubnd[ idim ] ); \ + astError( AST__GBDIN, "Error in input dimension %d.", status, \ + idim + 1 ); \ + break; \ + } \ + } \ + } \ +\ +/* Get the PointSet in the base Frame of the Region's FrameSet, and \ + transform to the current (GRID) Frame. */ \ + pset1 = used_region->points; \ + pset2 = astRegTransform( used_region, pset1, 1, NULL, NULL ); \ + ptr2 =astGetPoints( pset2 ); \ +\ +/* Allocate memory to hold the corresponding vector indices. */ \ + npnt = astGetNpoint( pset2 ); \ + iv = astMalloc( sizeof(int)*(size_t) npnt ); \ + if( astOK ) { \ +\ +/* Convert the transformed GRID positions into integer indices into the \ + vectorised data array. Also form the total size of the data array. */ \ + vlen = 0; \ + for( i = 0; i < npnt; i++ ) { \ + vlen = 1; \ + ii = 0; \ + for( j = 0; j < ndim; j++ ) { \ + ii += vlen*( (int)( ptr2[ j ][ i ] + 0.5 ) - lbnd[ j ] ); \ + vlen *= ubnd[ i ] - lbnd[ i ] + 1; \ + } \ + iv[ i ] = ii; \ + } \ +\ +/* See if the Region is negated. */ \ + negated = astGetNegated( used_region ); \ +\ +/* If necessary, set the transformed pixel coords to the supplied value. */ \ + if( ( inside && !negated ) || ( !inside && negated ) ) { \ + for( i = 0; i < npnt; i++ ) in[ iv[ i ] ] = val; \ + result = npnt; \ +\ +/* If necessary, set all except the transformed pixel coords to the supplied \ + value. */ \ + } else { \ + temp = astMalloc( sizeof( Xtype )*(size_t)npnt ); \ + if( astOK ) { \ + for( i = 0; i < npnt; i++ ) temp[ i ] = in[ iv[ i ] ]; \ + for( i = 0; i < vlen; i++ ) in[ i ] = val; \ + for( i = 0; i < npnt; i++ ) in[ iv[ i ] ] = temp[ i ]; \ + result = vlen - npnt; \ + } \ + temp = astFree( temp ); \ + } \ + } \ +\ +/* Free resources */ \ + iv = astFree( iv ); \ + pset2 = astAnnul( pset2 ); \ + used_region = astAnnul( used_region ); \ +\ +/* If an error occurred, clear the returned result. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_MASK(LD,long double) +#endif +MAKE_MASK(D,double) +MAKE_MASK(F,float) +MAKE_MASK(L,long int) +MAKE_MASK(UL,unsigned long int) +MAKE_MASK(I,int) +MAKE_MASK(UI,unsigned int) +MAKE_MASK(S,short int) +MAKE_MASK(US,unsigned short int) +MAKE_MASK(B,signed char) +MAKE_MASK(UB,unsigned char) + +/* Undefine the macro. */ +#undef MAKE_MASK + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a PointList. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* PointList method (over-rides the protected astMapMerge method +* inherited from the Region class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated PointList in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated PointList with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated PointList which is to be merged with +* its neighbours. This should be a cloned copy of the PointList +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* PointList it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated PointList resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstPointList *oldint; /* Pointer to supplied PointList */ + AstMapping *map; /* Pointer to adjacent Mapping */ + AstMapping *new; /* Simplified or merged Region */ + int i1; /* Index of first Mapping merged */ + int i; /* Loop counter */ + int result; /* Result value to return */ + +/* Initialise. */ + result = -1; + i1 = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the PointList. */ + oldint = (AstPointList *) this; + +/* First of all, see if the PointList can be replaced by a simpler Region, + without reference to the neighbouring Regions in the list. */ +/* =====================================================================*/ + +/* Try to simplify the PointList. If the pointer value has changed, we assume + some simplification took place. */ + new = astSimplify( oldint ); + if( new != (AstMapping *) oldint ) { + +/* Annul the PointList pointer in the list and replace it with the new Region + pointer, and indicate that the forward transformation of the returned + Region should be used (not really needed but keeps things clean). */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = new; + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the PointList itself could not be simplified, see if it can be merged + with the Regions on either side of it in the list. We can only merge + in parallel. */ +/* =====================================================================*/ + } else if( ! series ){ + new = astAnnul( new ); + +/* Attempt to merge the PointList with its lower neighbour (if any). */ + if( where > 0 ) { + i1 = where - 1; + map = ( *map_list )[ where - 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergePointList( oldint, (AstRegion *) map, + 0, status ); + } + } + +/* If this did not produced a merged Region, attempt to merge the PointList + with its upper neighbour (if any). */ + if( !new && where < *nmap - 1 ) { + i1 = where; + map = ( *map_list )[ where + 1 ]; + if( astIsARegion( map ) ) { + new = (AstMapping *) MergePointList( oldint, (AstRegion *) map, + 1, status ); + } + } + +/* If succesfull... */ + if( new ){ + +/* Annul the first of the two Mappings, and replace it with the merged + Region. Also clear the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = new; + ( *invert_list )[ i1 ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i1 + 1 ] ); + for ( i = i1 + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + } + + } else { + new = astAnnul( new ); + } + +/* Return the result. */ + return result; +} + +static AstRegion *MergePointList( AstPointList *this, AstRegion *reg, + int plsfirst, int *status ) { +/* +* Name: +* MergePointList + +* Purpose: +* Attempt to merge a PointList with another Region to form a Region of +* higher dimensionality. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* AstRegion *MergePointList( AstPointList *this, AstRegion *reg, +* int plsfirst, int *status ) + +* Class Membership: +* PointList member function. + +* Description: +* This function attempts to combine the supplied Regions together +* into a Region of higher dimensionality. + +* Parameters: +* this +* Pointer to a PointList. +* reg +* Pointer to another Region. +* plsfirst +* If non-zero, then the PointList axes are put first in the new Region. +* Otherwise, the other Region's axes are put first. +* status +* Pointer to the inherited status value. + +* Returned Value: +* A pointer to a new region, or NULL if the supplied Regions could +* not be merged. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* Pointer to base Frame for "result" */ + AstFrame *cfrm; /* Pointer to current Frame for "result" */ + AstFrame *frm_reg; /* Pointer to Frame from "reg" */ + AstFrame *frm_this; /* Pointer to Frame from "this" */ + AstMapping *bcmap; /* Base->current Mapping for "result" */ + AstMapping *map_reg; /* Base->current Mapping from "reg" */ + AstMapping *map_this; /* Base->current Mapping from "this" */ + AstMapping *sbunc; /* Simplified uncertainty */ + AstPointSet *pset_new; /* PointSet for new PointList */ + AstPointSet *pset_reg; /* PointSet from reg */ + AstPointSet *pset_this; /* PointSet from this */ + AstRegion *bunc; /* Base Frame uncertainty Region */ + AstRegion *new; /* Pointer to new PointList in base Frame */ + AstRegion *result; /* Pointer to returned PointList in current Frame */ + AstRegion *unc_reg; /* Current Frame uncertainty Region from "reg" */ + AstRegion *unc_this; /* Current Frame uncertainty Region from "this" */ + double **ptr_new; /* PointSet data pointers for new PointList */ + double **ptr_reg; /* PointSet data pointers for reg */ + double **ptr_this; /* PointSet data pointers for this */ + double fac_reg; /* Ratio of used to default MeshSize for "reg" */ + double fac_this; /* Ratio of used to default MeshSize for "this" */ + int i; /* Axis index */ + int msz_reg; /* Original MeshSize for "reg" */ + int msz_reg_set; /* Was MeshSize originally set for "reg"? */ + int msz_this; /* Original MeshSize for "this" */ + int msz_this_set; /* Was MeshSize originally set for "this"? */ + int nax; /* Number of axes in "result" */ + int nax_reg; /* Number of axes in "reg" */ + int nax_this; /* Number of axes in "this" */ + int neg_reg; /* Negated attribute value for other supplied Region */ + int neg_this; /* Negated attribute value for supplied PointList */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get the Closed attributes of the two Regions. They must be the same in + each Region if we are to merge the Regions. In addition, in order to + merge, either both Regions must have a defined uncertainty, or neither + Region must have a defined Uncertainty. */ + if( astGetClosed( this ) == astGetClosed( reg ) && + astTestUnc( this ) == astTestUnc( reg ) ) { + +/* Get the Nagated attributes of the two Regions. */ + neg_this = astGetNegated( this ); + neg_reg = astGetNegated( reg ); + +/* We only check for merging with another PointList (other classes such + as Box and Interval check for merging of PointLists with other classes). + The result will be an PointList. Both Regions must have the same value + for the Negated flag, and can contain only a single point. */ + if( astIsAPointList( reg ) && neg_this == neg_reg && + astGetListSize( this ) == 1 && + astGetListSize( (AstPointList *) reg ) == 1 ) { + +/* Get the number of axes in the two supplied Regions. */ + nax_reg = astGetNaxes( reg ); + nax_this = astGetNaxes( this ); + +/* Get the number of axes the combination will have. */ + nax = nax_reg + nax_this; + +/* Get the base Frames from the two Region FrameSets, and combine them + into a single CmpFrame that will be used to create the new Region. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + frm_reg = astGetFrame( reg->frameset, AST__BASE ); + + if( plsfirst ) { + bfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + bfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + +/* Get pointers to the coordinate data in the two PointLists */ + pset_this = ((AstRegion *) this)->points; + ptr_this = astGetPoints( pset_this ); + pset_reg = reg->points; + ptr_reg = astGetPoints( pset_reg ); + +/* Create a PointSet to hold the points for the returned PointList. */ + pset_new = astPointSet( 1, nax, "", status ); + +/* Get pointers to its data. */ + ptr_new = astGetPoints( pset_new ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the point positions fon the selected axes into the arrays allocated + above, in the requested order. */ + if( plsfirst ) { + for( i = 0; i < nax_this; i++ ) { + ptr_new[ i ][ 0 ] = ptr_this[ i ][ 0 ]; + } + for( ; i < nax; i++ ) { + ptr_new[ i ][ 0 ] = ptr_reg[ i - nax_this ][ 0 ]; + } + + } else { + for( i = 0; i < nax_reg; i++ ) { + ptr_new[ i ][ 0 ] = ptr_reg[ i ][ 0 ]; + } + for( ; i < nax; i++ ) { + ptr_new[ i ][ 0 ] = ptr_this[ i - nax_reg ][ 0 ]; + } + } + +/* Create the new PointList. */ + new = (AstRegion *) astPointList( bfrm, pset_new, NULL, "", + status ); + +/* Propagate remaining attributes of the supplied Region to it. */ + astRegOverlay( new, this, 1 ); + +/* Ensure the Negated flag is set correctly in the returned PointList. */ + if( neg_this ) { + astSetNegated( new, neg_this ); + } else { + astClearNegated( new ); + } + +/* If both the supplied Regions have uncertainty, assign the new Region an + uncertainty. */ + if( astTestUnc( this ) && astTestUnc( reg ) ) { + +/* Get the uncertainties from the two supplied Regions. */ + unc_this = astGetUncFrm( this, AST__BASE ); + unc_reg = astGetUncFrm( reg, AST__BASE ); + +/* Combine them into a single Region (a Prism), in the correct order. */ + if( plsfirst ) { + bunc = (AstRegion *) astPrism( unc_this, unc_reg, "", status ); + } else { + bunc = (AstRegion *) astPrism( unc_reg, unc_this, "", status ); + } + +/* Attempt to simplify the Prism. */ + sbunc = astSimplify( bunc ); + +/* Use the simplified Prism as the uncertainty for the returned Region. */ + astSetUnc( new, sbunc ); + +/* Free resources. */ + sbunc = astAnnul( sbunc ); + bunc = astAnnul( bunc ); + unc_reg = astAnnul( unc_reg ); + unc_this = astAnnul( unc_this ); + } + +/* Get the current Frames from the two Region FrameSets, and combine them + into a single CmpFrame. */ + frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__CURRENT ); + frm_reg = astGetFrame( reg->frameset, AST__CURRENT ); + + if( plsfirst ) { + cfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status ); + } else { + cfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status ); + } + +/* Get the base -> current Mappings from the two Region FrameSets, and + combine them into a single parallel CmpMap that connects bfrm and cfrm. */ + map_this = astGetMapping( ((AstRegion *) this)->frameset, AST__BASE, + AST__CURRENT ); + map_reg = astGetMapping( reg->frameset, AST__BASE, AST__CURRENT ); + + if( plsfirst ) { + bcmap = (AstMapping *) astCmpMap( map_this, map_reg, 0, "", + status ); + } else { + bcmap = (AstMapping *) astCmpMap( map_reg, map_this, 0, "", + status ); + } + +/* Map the new Region into the new current Frame. */ + result = astMapRegion( new, bcmap, cfrm ); + +/* The filling factor in the returned is the product of the filling + factors for the two supplied Regions. */ + if( astTestFillFactor( reg ) || astTestFillFactor( this ) ) { + astSetFillFactor( result, astGetFillFactor( reg )* + astGetFillFactor( this ) ); + } + +/* If the MeshSize value is set in either supplied Region, set a value + for the returned Region which scales the default value by the + product of the scaling factors for the two supplied Regions. First see + if either MeshSize value is set. */ + msz_this_set = astTestMeshSize( this ); + msz_reg_set = astTestMeshSize( reg ); + if( msz_this_set || msz_reg_set ) { + +/* If so, get the two MeshSize values (one of which may be a default + value), and then clear them so that the default value will be returned + in future. */ + msz_this = astGetMeshSize( this ); + msz_reg = astGetMeshSize( reg ); + astClearMeshSize( this ); + astClearMeshSize( reg ); + +/* Get the ratio of the used MeshSize to the default MeshSize for both + Regions. */ + fac_this = (double)msz_this/(double)astGetMeshSize( this ); + fac_reg = (double)msz_reg/(double)astGetMeshSize( reg ); + +/* The MeshSize of the returned Returned is the default value scaled by + the product of the two ratios found above. */ + astSetMeshSize( result, fac_this*fac_reg*astGetMeshSize( result ) ); + +/* Re-instate the original MeshSize values for the supplied Regions (if + set) */ + if( msz_this_set ) astSetMeshSize( this, msz_this ); + if( msz_reg_set ) astSetMeshSize( reg, msz_reg ); + } + +/* Free remaining resources */ + frm_this = astAnnul( frm_this ); + frm_reg = astAnnul( frm_reg ); + map_this = astAnnul( map_this ); + map_reg = astAnnul( map_reg ); + bcmap = astAnnul( bcmap ); + cfrm = astAnnul( cfrm ); + new = astAnnul( new ); + } + bfrm = astAnnul( bfrm ); + pset_new = astAnnul( pset_new ); + + } + } + +/* If an error has occurred, annul the returned pointer. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +void PointListPoints( AstPointList *this, AstPointSet **pset, int *status) { +/* +*+ +* Name: +* astPointListPoints + +* Purpose: +* Return the defining points of a PointList. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointlist.h" +* astPointListPoints( AstPointList *this, AstPointSet **pset ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns the axis values at the points defining the +* supplied PointList. + +* Parameters: +* this +* Pointer to the PointList. +* pset +* Address of a location at which to return a pointer to a PointSet +* holding the points in the PointList, in the base Frame of the +* encapsulated FrameSet. The returned Pointer should be annulled +* when no longer needed. + +*- +*/ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Return a clone of the PointSet holding the points defining the PointList. */ + *pset = astClone( ((AstRegion *) this)->points ); + +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* void astRegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* PointList member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to encapsulated Frame */ + AstPointList *this; /* Pointer to PointList structure */ + AstPointSet *pset; /* Pointer to PointSet defining the Region */ + double **ptr; /* Pointer to PointSet data */ + double *p; /* Pointer to next axis value */ + double *lb; /* Pointer to lower limit array */ + double *ub; /* Pointer to upper limit array */ + double d; /* Axis offset from refernce value */ + double p0; /* Reference axis value */ + int ic; /* Axis index */ + int ip; /* Point index */ + int nb; /* Number of bytes to be copied */ + int nc; /* No. of axes in base Frame */ + int np; /* No. of points in PointSet */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the PointList structure. */ + this = (AstPointList *) this_region; + +/* Calculate the number of bytes in each array. */ + nb = sizeof( double )*(size_t) astGetNaxes( this ); + +/* If the base Frame bounding box has not yet been found, find it now and + store it in the PointList structure. */ + if( !this->lbnd || !this->ubnd ) { + +/* Allocate memory to store the bounding box in the PointList structure. */ + lb = astMalloc( nb ); + ub = astMalloc( nb ); + +/* Get the axis values for the PointSet which defines the location and + extent of the region in the base Frame of the encapsulated FrameSet. */ + pset = this_region->points; + ptr = astGetPoints( pset ); + nc = astGetNcoord( pset ); + np = astGetNpoint( pset ); + +/* Get a pointer to the base Frame in the encaposulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Find the bounds on each axis in turn. */ + for( ic = 0; ic < nc; ic++ ) { + +/* We first find the max and min axis offsets from the first point. We + used astAxDistance to cater for the possbility that the Frame may be a + SkyFrame and thus have circular redundancy. */ + p = ptr[ ic ] + 1; + p0 = p[ -1 ]; + lb[ ic ] = 0.0; + ub[ ic ] = 0.0; + for( ip = 1; ip < np; ip++, p++ ) { + d = astAxDistance( frm, ic + 1, p0, *p ); + if( d < lb[ ic ] ) lb[ ic ] = d; + if( d > ub[ ic ] ) ub[ ic ] = d; + } + +/* Now convert these offsets to actual axis values. */ + lb[ ic ] = astAxOffset( frm, ic + 1, p0, lb[ ic ] ); + ub[ ic ] = astAxOffset( frm, ic + 1, p0, ub[ ic ] ); + + } + } + +/* Free resources */ + frm = astAnnul( frm ); + +/* Store the pointers in the PointList structure. */ + if( astOK ) { + this->lbnd = lb; + this->ubnd = ub; + } else { + this->lbnd = astFree( this->lbnd ); + this->ubnd = astFree( this->ubnd ); + } + } + +/* If the bounding box has been found succesfully, copy it into the supplied + arrays. */ + if( astOK ) { + memcpy( lbnd, this->lbnd, nb ); + memcpy( ubnd, this->ubnd, nb ); + } + +} + +static AstPointSet *RegBaseMesh( AstRegion *this, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* PointList member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the accuracies which were +* supplied when the Region was created. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Local Variables: */ + AstPointSet *result; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this->basemesh ) { + result = astClone( this->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* It is just a copy of the encapsulated PointSet. */ + result = astCopy( this->points ); + +/* Same the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK && result ) this->basemesh = astClone( result ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, + const int *axes, int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* PointList member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* The base Frame in the supplied Region */ + AstFrame *frm; /* The base Frame in the returned Region */ + AstPointSet *pset; /* Holds axis values defining the supplied Region */ + AstPointSet *pset_new; /* Holds axis values defining the returned Region */ + AstRegion *bunc; /* The uncertainty in the supplied Region */ + AstRegion *result; /* Returned Region */ + AstRegion *unc; /* The uncertainty in the returned Region */ + double **ptr; /* Holds axis values defining the supplied Region */ + double **ptr_new; /* Holds axis values defining the returned Region */ + double *p; /* Pointer to next input axis value */ + double *q; /* Pointer to next output axis value */ + int i; /* Index of axis within returned Region */ + int j; /* Point index */ + int npnt; /* Number of points in PointList */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the base Frame of the encapsulated FrameSet. */ + bfrm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Create a Frame by picking the selected axes from the base Frame of the + encapsulated FrameSet. */ + frm = astPickAxes( bfrm, naxes, axes, NULL ); + +/* Get the uncertainty Region (if any) within the base Frame of the supplied + Region, and select the required axes from it. If the resulting Object + is not a Region, annul it so that the returned Region will have no + uncertainty. */ + if( astTestUnc( this_region ) ) { + bunc = astGetUncFrm( this_region, AST__BASE ); + unc = astPickAxes( bunc, naxes, axes, NULL ); + bunc = astAnnul( bunc ); + + if( ! astIsARegion( unc ) ) unc = astAnnul( unc ); + + } else { + unc = NULL; + } + +/* Get pointers to the coordinate data in the parent Region structure. */ + pset = this_region->points; + ptr = astGetPoints( pset ); + npnt = astGetNpoint( pset ); + +/* Create a PointSet to hold the points for the returned PointList. */ + pset_new = astPointSet( npnt, naxes, "", status ); + +/* Get pointers to its data. */ + ptr_new = astGetPoints( pset_new ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the point positions on the selected axes into the arrays allocated + above. */ + for( i = 0; i < naxes; i++ ) { + p = ptr[ axes[ i ] ]; + q = ptr_new[ i ]; + for( j = 0; j < npnt; j++ ) *(q++) = *(p++); + } + +/* Create the new PointList. */ + result = (AstRegion *) astPointList( frm, pset_new, unc, "", status ); + } + +/* Free resources */ + frm = astAnnul( frm ); + bfrm = astAnnul( bfrm ); + if( unc ) unc = astAnnul( unc ); + pset_new = astAnnul( pset_new ); + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given PointList. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* PointList member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given PointList. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied PointList "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the PointList. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "this". +* Each element in the returned array is set to 1 if the +* corresponding position in "this" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstPointList *pl; /* Pointer to PointList holding supplied points */ + AstPointList *this; /* Pointer to the PointList structure. */ + AstPointSet *pset2; /* Supplied points masked by this PointList */ + AstPointSet *pset3; /* This PointList masked by supplied points */ + double **ptr2; /* Pointer to axis values in "pset2" */ + double **ptr3; /* Pointer to axis values in "pset3" */ + double **ptr; /* Pointer to axis values in "this" */ + double *p; /* Pointer to next axis value to read */ + int ic; /* Axis index */ + int icurr; /* Index of original current Frame in "this" */ + int ip; /* Point index */ + int nc; /* No. of axes in Box base frame */ + int neg_old; /* Original Negated flag for "this" */ + int np; /* No. of supplied points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the Box structure. */ + this = (AstPointList *) this_region; + +/* Temporarily ensure that the current Frame in "this" is the same as the + base Frame. We need to do this since the supplied points are in the + base Frame of "this", but the astTransform method below assumes + that it is transforming points in the current Frame of the Region. */ + icurr = astGetCurrent( this_region->frameset ); + astSetCurrent( this_region->frameset, AST__BASE ); + +/* Get pointer to the supplied axis values, the number of points and the + number of axis values per point. */ + ptr = astGetPoints( pset ); + np = astGetNpoint( pset ); + nc = astGetNcoord( pset ); + +/* All the supplied points should be within the supplied PointsList region + (given that "within" implies some tolerance). Transform the positions + using this PointList and check if any of the resulting points fell + outside this PointList. We need to ensure that the PointList is not + negated first. */ + neg_old = astGetNegated( this ); + astSetNegated( this, 0 ); + pset2 = astTransform( this, pset, 1, NULL ); + ptr2 = astGetPoints( pset2 ); + +/* Check pointers can be used. */ + if( astOK ) { + +/* Check there are no bad points (i.e. check that none of the points are + outside the supplied PointList). The algorithm used to do this depends + on whether we need to create an output mask. */ + result = 1; + if( mask ) { + +/* Create the returned mask array. */ + *mask = astMalloc( np ); + if( astOK ) { + +/* Initialise the mask elements on the basis of the first axis values */ + result = 1; + p = ptr2[ 0 ]; + for( ip = 0; ip < np; ip++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ ip ] = 0; + } else { + (*mask)[ ip ] = 1; + } + } + +/* Now check for bad values on other axes. */ + for( ic = 1; ic < nc; ic++ ) { + p = ptr2[ ic ]; + for( ip = 0; ip < np; ip++ ) { + if( *(p++) == AST__BAD ) { + result = 0; + (*mask)[ ip ] = 0; + } + } + } + } + +/* If no output mask is to be made, we can break out of the check as soon + as the first bad value is found. */ + } else { + for( ic = 0; ic < nc && result; ic++ ){ + p = ptr2[ ic ]; + for( ip = 0; ip < np; ip++,p++ ){ + if( *p == AST__BAD ) { + result = 0; + break; + } + } + } + } + +/* If this check was passed, we perform a similar check in the opposite + direction: we create a new PointList from the supplied list of points, + and then we transform the points associated with the supplied PointList + using the new PointList. This checks that all the points in the + supplied PointList are close to the supplied points. Create the new + PointList holding the supplied points. */ + if( result ) { + pl = astPointList( unc, pset, unc, "", status ); + +/* Transform the points in "this" PointList using the new PointList as a + Mapping. */ + pset3 = astTransform( pl, this_region->points, 1, NULL ); + ptr3 = astGetPoints( pset3 ); + +/* Check pointers can be used. */ + if( astOK ) { + for( ic = 0; ic < nc && result; ic++ ){ + p = ptr3[ ic ]; + for( ip = 0; ip < np; ip++,p++ ){ + if( *p == AST__BAD ) { + result = 0; + break; + } + } + } + } + +/* Free resources. */ + pl = astAnnul( pl ); + pset3 = astAnnul( pset3 ); + + } + } + + pset2 = astAnnul( pset2 ); + +/* Re-instate the original current Frame in "this". */ + astSetCurrent( this_region->frameset, icurr ); + +/* Re-instate the original Negated flag for "this". */ + astSetNegated( this, neg_old ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, + int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a PointList. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* PointList member function (over-rides the astSetAttrib protected +* method inherited from the Object class). + +* Description: +* This function assigns an attribute value for a PointList, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the PointList. +* setting +* Pointer to a null-terminated string specifying the new +* attribute value. +*/ + +/* Local Variables: */ + AstPointList *this; /* Pointer to the PointList structure */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PointList structure. */ + this = (AstPointList *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* Use this macro to report an error if a read-only attribute has been + specified. */ + if ( MATCH( "listsize" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", + status, setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* PointList method (over-rides the astSimplify method inherited +* from the Region class). + +* Description: +* This function invokes the parent Region Simplify method, and then +* performs any further region-specific simplification. +* +* If the Mapping from base to current Frame is not a UnitMap, this +* will include attempting to fit a new Region to the boundary defined +* in the current Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to encapsulated Frame */ + AstMapping *map; /* Pointer to frameset Mapping */ + AstMapping *result; /* Result pointer to return */ + AstPointList *new2; /* Pointer to simplified Region */ + AstPointSet *pset1; /* Original base Frame positions */ + AstPointSet *pset2; /* Current Frame Frame positions */ + AstRegion *new; /* Pointer to simplified Region */ + AstRegion *this; /* Pointer to original Region structure */ + AstRegion *unc; /* Pointer to new uncertainty Region */ + double **ptr2; /* Pointer to current Frame points */ + int simpler; /* Has some simplication taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_mapping; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. */ + new = (AstRegion *) (*parent_simplify)( this_mapping, status ); + +/* Note if any simplification took place. This is assumed to be the case + if the pointer returned by the above call is different to the supplied + pointer. */ + simpler = ( new != this ); + +/* Get the Mapping from base to current Frame. We can modify the PointList so + that a UnitMap can be used. This is good because it means that the + serialised PointList is simpler since the Dump function only needs to + record one Frame instead of the whole FrameSet. */ + map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT ); + if( !astIsAUnitMap( map ) ){ + +/* Get a pointer to the current Region Frame */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + +/* Get the PointSet which holds the base Frame positions defining this + PointList. */ + pset1 = this->points; + +/* Transform the PointSet using this Mapping. */ + pset2 = astTransform( map, pset1, 1, NULL ); + ptr2 = astGetPoints( pset2 ); + +/* Get the Region describing the positional uncertainty within the + supplied PointList, in its current Frame. */ + unc = astGetUncFrm( new, AST__CURRENT ); + +/* Create a new PointList, and use it in place of the original. */ + new2 = astPointList( fr, pset2, unc, "", status ); + (void) astAnnul( new ); + new = (AstRegion *) new2; + simpler = 1; + +/* Free resources. */ + fr = astAnnul( fr ); + pset2 = astAnnul( pset2 ); + unc = astAnnul( unc ); + } + +/* Free resources. */ + map = astAnnul( map ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. + If the supplied Region had no uncertainty, ensure the returned Region + has no uncertainty. Otherwise, return a clone of the supplied pointer. */ + if( simpler ){ + astRegOverlay( new, this, 1 ); + result = (AstMapping *) new; + + } else { + new = astAnnul( new ); + result = astClone( this ); + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, + int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a PointList. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PointList member function (over-rides the astTestAttrib protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate +* whether a value has been set for one of a PointList's attributes. + +* Parameters: +* this +* Pointer to the PointList. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPointList *this; /* Pointer to the PointList structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the PointList structure. */ + this = (AstPointList *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* Test if the name matches any of the read-only attributes of this + class. If it does, then return zero. */ + if ( !strcmp( attrib, "listsize" ) ){ + result = 0; + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a PointList to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "pointlist.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* PointList member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a PointList and a set of points encapsulated in a +* PointSet and transforms the points by setting axis values to +* AST__BAD for all points which are outside the region covered by the +* PointList. PointList inside the region are copied unchanged from input +* to output. + +* Parameters: +* this +* Pointer to the PointList. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The forward and inverse transformations are identical for a +* Region. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of axes in the Frame represented by the PointList. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *in_base; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *ps1; /* Pointer to accumulation PointSet */ + AstPointSet *ps2; /* Pointer to accumulation PointSet */ + AstPointSet *ps3; /* Pointer for swapping PointSet pointers */ + AstPointSet *pset_base; /* PointList positions in "unc" base Frame */ + AstPointSet *pset_reg; /* Pointer to Region PointSet */ + AstPointSet *result; /* Pointer to output PointSet */ + AstRegion *this; /* Pointer to the Region structure */ + AstRegion *unc; /* Pointer to uncertainty Region */ + double **ptr1; /* Pointer to mask pointer array */ + double **ptr_base; /* Pointer to axis values for "pset_base" */ + double **ptr_out; /* Pointer to output coordinate data */ + double *cen_orig; /* Pointer to array holding original centre coords */ + double *mask; /* Pointer to mask axis values */ + int coord; /* Zero-based index for coordinates */ + int ncoord_base; /* No. of coordinates per base Frame point */ + int ncoord_out; /* No. of coordinates per output point */ + int npoint; /* No. of supplied input test points */ + int nrp; /* No. of points in Region PointSet */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Avoid -Wall compiler warnings. */ + ps1 = NULL; + ps2 = NULL; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_mapping; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, + containing a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet to transform the supplied positions + from the current Frame in the encapsulated FrameSet (the Frame + represented by the Region), to the base Frame (the Frame in which the + Region is defined). */ + in_base = astRegTransform( this, in, 0, NULL, NULL ); + +/* The PointSet pointer returned above may be a clone of the "in" + pointer. If so take a copy of the PointSet so we can change it safely. */ + if( in_base == in ) { + ps3 = astCopy( in_base ); + (void) astAnnul( in_base ); + in_base = ps3; + ps3 = NULL; + } + +/* Determine the numbers of points and coordinates per point from the base + Frame PointSet and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( in_base ); + ncoord_base = astGetNcoord( in_base ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* Get the axis values for the PointSet which defines the location and + extent of the region in the base Frame, and check them. */ + pset_reg = this->points; + nrp = astGetNpoint( pset_reg ); + if( astGetNcoord( pset_reg ) != ncoord_base && astOK ) { + astError( AST__INTER, "astTransform(PointList): Illegal number of " + "coords (%d) in the Region - should be %d " + "(internal AST programming error).", status, astGetNcoord( pset_reg ), + ncoord_base ); + } + +/* Get the base Frame uncertainty Region. Temporarily set its negated flag. */ + unc = astGetUncFrm( this, AST__BASE ); + astSetNegated( unc, 1 ); + +/* Transform the PointList PointSet into the base Frame of the uncertainty + Region, and get pointers to the corresponding axis value. */ + pset_base = astRegTransform( unc, pset_reg, 0, NULL, NULL ); + ptr_base = astGetPoints( pset_base ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* Save the original base Frame centre coords of the uncertainty Region. */ + cen_orig = astRegCentre( unc, NULL, NULL, 0, AST__BASE ); + +/* We use the PointSet created above as the initial input to astTransform + below. Also indicate we currently have no output PointSet. This will + cause a new PointSet to be created on the first pass through the loop + below. */ + ps1 = astClone( in_base ); + ps2 = NULL; + +/* Loop round all the points in the PointList. */ + for ( point = 0; point < nrp; point++ ) { + +/* Centre the uncertainty Region at this PointList position. Note, the + base Frame of the PointList should be the same as the current Frame + of the uncertainty Region. */ + astRegCentre( unc, NULL, ptr_base, point, AST__BASE ); + +/* Use the uncertainty Region to transform the supplied PointSet. This + will set supplied points bad if they are within the uncertainty Region + (since the uncertainty Region has been negated above). */ + ps2 = astTransform( unc, ps1, 0, ps2 ); + +/* Use the output PointSet created above as the input for the next + position. This causes bad points to be accumulated in the output + PointSet. */ + ps3 = ps2; + ps2 = ps1; + ps1 = ps3; + + } + +/* Re-instate the original centre coords of the uncertainty Region. */ + astRegCentre( unc, cen_orig, NULL, 0, AST__BASE ); + cen_orig = astFree( cen_orig ); + +/* The ps1 PointSet will now be a copy of the supplied PointSet but with + positions set to bad if they are inside any of the re-centred uncertainty + Regions. If this PointList has been negated, this is what we want so + we just transfer this bad position mask to the result PointSet. If this + PointList has not been negated we need to invert the bad position + mask. Get a pointer to the first axis of the resulting PointSet. */ + ptr1 = astGetPoints( ps1 ); + if( astOK ) { + mask = ptr1[ 0 ]; + +/* Apply the mask to the returned PointSet, inverted if necessary. */ + if( astGetNegated( this ) ) { + for ( point = 0; point < npoint; point++, mask++ ) { + if( *mask == AST__BAD ) { + for( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + + } else { + for ( point = 0; point < npoint; point++, mask++ ) { + if( *mask != AST__BAD ) { + for( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + } + } + +/* Clear the negated flag for the uncertainty Region. */ + astClearNegated( unc ); + +/* Free resources */ + in_base = astAnnul( in_base ); + pset_base = astAnnul( pset_base ); + unc = astAnnul( unc ); + if( ps2 ) ps2 = astAnnul( ps2 ); + if( ps1 ) ps1 = astAnnul( ps1 ); + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* ListSize + +* Purpose: +* Number of points in a PointList. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This is a read-only attribute giving the number of points in a +* PointList. This value is determined when the PointList is created. + +* Applicability: +* PointList +* All PointLists have this attribute. +*att-- +*/ + + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for PointList objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for PointList objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstPointList *in; /* Pointer to input PointList */ + AstPointList *out; /* Pointer to output PointList */ + int nb; /* Number of bytes */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output PointLists. */ + in = (AstPointList *) objin; + out = (AstPointList *) objout; + +/* For safety, first clear any references to the input memory from + the output PointList. */ + out->lbnd = NULL; + out->ubnd = NULL; + +/* Copy dynamic memory contents */ + if( in->lbnd && in->ubnd ) { + nb = sizeof( double )*astGetNaxes( in ); + out->lbnd = astStore( NULL, in->lbnd, nb ); + out->ubnd = astStore( NULL, in->ubnd, nb ); + } +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for PointList objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for PointList objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPointList *this; /* Pointer to PointList */ + +/* Obtain a pointer to the PointList structure. */ + this = (AstPointList *) obj; + +/* Annul all resources. */ + this->lbnd = astFree( this->lbnd ); + this->ubnd = astFree( this->ubnd ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for PointList objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the PointList class to an output Channel. + +* Parameters: +* this +* Pointer to the PointList whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPointList *this; /* Pointer to the PointList structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PointList structure. */ + this = (AstPointList *) this_object; + +/* Write out values representing the instance variables for the + PointList class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPointList and astCheckPointList functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(PointList,Region) +astMAKE_CHECK(PointList) + +AstPointList *astPointList_( void *frame_void, AstPointSet *points, + AstRegion *unc, const char *options, + int *status, ...) { +/* +*+ +* Name: +* astPointList + +* Purpose: +* Create a PointList. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointlist.h" +* AstPointList *astPointList( AstFrame *frame, AstPointSet *points, +* AstRegion *unc, const char *options, +* int *status, ...) { + +* Class Membership: +* PointList constructor. + +* Description: +* This function implements the protected interface to the astPointList +* constructor function, returning a true C pointer. The parameter list +* differs from the public constructor, in that the positions are +* defined by a PointSet rather than an array of doubles. + +* Parameters: +* frame +* A pointer to the Frame in which the region is defined. A deep +* copy is taken of the supplied Frame. This means that any +* subsequent changes made to the Frame using the supplied pointer +* will have no effect the Region. +* points +* A PointSet holding the physical coordinates of the points. +* unc +* An optional pointer to an existing Region which specifies the +* uncertainties associated with each point in the PointList being +* created. The uncertainty at any point in the PointList is found by +* shifting the supplied "uncertainty" Region so that it is centred at +* the point being considered. The area covered by the shifted +* uncertainty Region then represents the uncertainty in the position. +* The uncertainty is assumed to be the same for all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created Box. Alternatively, a NULL Object pointer +* may be supplied, in which case a default uncertainty is used +* equivalent to a box 1.0E-6 of the size of the bounding box of the +* PointList being created. +* +* The uncertainty Region has two uses: 1) when the astOverlap +* function compares two Regions for equality the uncertainty Region +* is used to determine the tolerance on the comparison, and 2) +* when a Region is mapped into a different coordinate system and +* subsequently simplified (using astSimplify), the uncertainties are +* used to determine if the transformed boundary can be accurately +* represented by a specific shape of Region. +* options +* Pointer to a null-terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new PointList. The syntax used is identical to +* that for the astSet function and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status value. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of additional arguments may follow it in +* order to supply values to be substituted for these +* specifiers. The rules for supplying these are identical to +* those for the astSet function (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new PointList. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPointList *new; /* Pointer to new PointList */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the supplied Frame structure. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the PointList, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPointList( NULL, sizeof( AstPointList ), !class_init, + &class_vtab, "PointList", frame, points, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PointList's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new PointList. */ + return new; +} + +AstPointList *astPointListId_( void *frame_void, int npnt, int ncoord, int dim, + const double *points, void *unc_void, + const char *options, ... ) { +/* +*++ +* Name: +c astPointList +f AST_POINTLIST + +* Purpose: +* Create a PointList. + +* Type: +* Public function. + +* Synopsis: +c #include "pointlist.h" +c AstPointList *astPointList( AstFrame *frame, int npnt, int ncoord, int dim, +c const double *points, AstRegion *unc, +c const char *options, ... ) +f RESULT = AST_POINTLIST( FRAME, NPNT, COORD, DIM, POINTS, UNC, OPTIONS, STATUS ) + +* Class Membership: +* PointList constructor. + +* Description: +* This function creates a new PointList object and optionally initialises +* its attributes. +* +* A PointList object is a specialised type of Region which represents a +* collection of points in a coordinate Frame. + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame in which the region is defined. A deep +* copy is taken of the supplied Frame. This means that any +* subsequent changes made to the Frame using the supplied pointer +* will have no effect the Region. +c npnt +f NPNT = INTEGER (Given) +* The number of points in the Region. +c ncoord +f NCOORD = INTEGER (Given) +* The number of coordinates being supplied for each point. This +* must equal the number of axes in the supplied Frame, given by +* its Naxes attribute. +c dim +f DIM = INTEGER (Given) +c The number of elements along the second dimension of the "points" +f The number of elements along the first dimension of the POINTS +* array (which contains the point coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +c given should not be less than "npnt". +f given should not be less than NPNT. +c points +f POINTS( DIM, NCOORD ) = DOUBLE PRECISION (Given) +c The address of the first element of a 2-dimensional array of +c shape "[ncoord][dim]" giving the physical coordinates of the +c points. These should be stored such that the value of coordinate +c number "coord" for point number "pnt" is found in element +c "in[coord][pnt]". +f A 2-dimensional array giving the physical coordinates of the +f points. These should be stored such that the value of coordinate +f number COORD for point number PNT is found in element IN(PNT,COORD). +c unc +f UNC = INTEGER (Given) +* An optional pointer to an existing Region which specifies the uncertainties +* associated with each point in the PointList being created. The +* uncertainty at any point in the PointList is found by shifting the +* supplied "uncertainty" Region so that it is centred at the point +* being considered. The area covered by the shifted uncertainty Region +* then represents the uncertainty in the position. The uncertainty is +* assumed to be the same for all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created Box. Alternatively, +f a null Object pointer (AST__NULL) +c a NULL Object pointer +* may be supplied, in which case a default uncertainty is used +* equivalent to a box 1.0E-6 of the size of the bounding box of the +* PointList being created. +* +* The uncertainty Region has two uses: 1) when the +c astOverlap +f AST_OVERLAP +* function compares two Regions for equality the uncertainty +* Region is used to determine the tolerance on the comparison, and 2) +* when a Region is mapped into a different coordinate system and +* subsequently simplified (using +c astSimplify), +f AST_SIMPLIFY), +* the uncertainties are used to determine if the transformed boundary +* can be accurately represented by a specific shape of Region. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new PointList. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new PointList. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPointList() +f AST_POINTLIST = INTEGER +* A pointer to the new PointList. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPointList *new; /* Pointer to new PointList */ + AstPointSet *pset; /* Pointer to PointSet holding points */ + AstRegion *unc; /* Pointer to Region structure */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const double *q; /* Pointer to next supplied axis value */ + double **ptr; /* Pointer to data in pset */ + double *p; /* Pointer to next PointSet axis value */ + int *status; /* Pointer to inherited status value */ + int i; /* Axis index */ + int j; /* Point index */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Frame pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Create a PointSet and store the supplied points in it. */ + pset = astPointSet( npnt, ncoord , "", status ); + ptr = astGetPoints( pset ); + if( astOK ) { + for( i = 0; i < ncoord; i++ ) { + p = ptr[ i ]; + q = points + i*dim; + for( j = 0; j < npnt; j++ ) *(p++) = *(q++); + } + } + +/* Obtain a Region pointer from the supplied "unc" ID and validate the + pointer to ensure it identifies a valid Region . */ + unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL; + +/* Initialise the PointList, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPointList( NULL, sizeof( AstPointList ), !class_init, + &class_vtab, "PointList", frame, pset, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PointList's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Free resources. */ + pset = astAnnul( pset ); + +/* Return an ID value for the new PointList. */ + return astMakeId( new ); +} + +AstPointList *astInitPointList_( void *mem, size_t size, int init, + AstPointListVtab *vtab, const char *name, + AstFrame *frame, AstPointSet *points, + AstRegion *unc, int *status ) { +/* +*+ +* Name: +* astInitPointList + +* Purpose: +* Initialise a PointList. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointlist.h" +* AstPointList *astInitPointList( void *mem, size_t size, int init, +* AstPointListVtab *vtab, const char *name, +* AstFrame *frame, AstPointSet *points, +* AstRegion *unc, int *status ) + +* Class Membership: +* PointList initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new PointList object. It allocates memory (if necessary) to accommodate +* the PointList plus any additional data associated with the derived class. +* It then initialises a PointList structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a PointList at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the PointList is to be initialised. +* This must be of sufficient size to accommodate the PointList data +* (sizeof(PointList)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the PointList (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the PointList +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the PointList's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new PointList. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame in which the region is defined. +* points +* A PointSet containing the Points for the PointList. +* unc +* A pointer to a Region which specifies the uncertainty in the +* supplied positions (all points in the new PointList being +* initialised are assumed to have the same uncertainty). A NULL +* pointer can be supplied, in which case default uncertainties equal +* to 1.0E-6 of the dimensions of the new PointList's bounding box are +* used. If an uncertainty Region is supplied, it must be either a Box, +* a Circle or an Ellipse, and its encapsulated Frame must be related +* to the Frame supplied for parameter "frame" (i.e. astConvert +* should be able to find a Mapping between them). Two positions +* the "frame" Frame are considered to be co-incident if their +* uncertainty Regions overlap. The centre of the supplied +* uncertainty Region is immaterial since it will be re-centred on the +* point being tested before use. A deep copy is taken of the supplied +* Region. + +* Returned Value: +* A pointer to the new PointList. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPointList *new; /* Pointer to new PointList */ + int ncoord; /* No. of axes in PointSet */ + int nin; /* No. of axes in Frame */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPointListVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the number of axis values per position is correct. */ + nin = astGetNaxes( frame ); + ncoord = astGetNcoord( points ); + if( nin != ncoord ) { + astError( AST__NCPIN, "astInitPointList(): Bad number of coordinate " + "values (%d).", status, ncoord ); + astError( AST__NCPIN, "The %s given requires %d coordinate value(s) for " + "each input point.", status, astGetClass( frame ), nin ); + } + +/* Initialise a Region structure (the parent class) as the first component + within the PointList structure, allocating memory if necessary. */ + if( astOK ) { + new = (AstPointList *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frame, points, unc ); + if ( astOK ) { + +/* Initialise the PointList data. */ +/* ------------------------------ */ + new->lbnd = NULL; + new->ubnd = NULL; + +/* If an error occurred, clean up by deleting the new PointList. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new PointList. */ + return new; +} + +AstPointList *astLoadPointList_( void *mem, size_t size, AstPointListVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPointList + +* Purpose: +* Load a PointList. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointlist.h" +* AstPointList *astLoadPointList( void *mem, size_t size, AstPointListVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* PointList loader. + +* Description: +* This function is provided to load a new PointList using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* PointList structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a PointList at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the PointList is to be +* loaded. This must be of sufficient size to accommodate the +* PointList data (sizeof(PointList)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the PointList (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the PointList structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPointList) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new PointList. If this is NULL, a pointer +* to the (static) virtual function table for the PointList class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "PointList" is used instead. + +* Returned Value: +* A pointer to the new PointList. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPointList *new; /* Pointer to the new PointList */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this PointList. In this case the + PointList belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPointList ); + vtab = &class_vtab; + name = "PointList"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPointListVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built PointList. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "PointList" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* If an error occurred, clean up by deleting the new PointList. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new PointList pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +int astGetListSize_( AstPointList *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,PointList,GetListSize))( this, status ); +} +void astPointListPoints_( AstPointList *this, AstPointSet **pset, int *status) { + if ( !astOK ) return; + (**astMEMBER(this,PointList,PointListPoints))( this, pset, status ); + return; +} + diff --git a/pointlist.h b/pointlist.h new file mode 100644 index 0000000..a2e35b7 --- /dev/null +++ b/pointlist.h @@ -0,0 +1,239 @@ +#if !defined( POINTLIST_INCLUDED ) /* Include this file only once */ +#define POINTLIST_INCLUDED +/* +*+ +* Name: +* pointlist.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the PointList class. + +* Invocation: +* #include "pointlist.h" + +* Description: +* This include file defines the interface to the PointList class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The PointList class implements a Region which represents a collection +* of points in a Frame. + +* Inheritance: +* The PointList class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-AUG-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* PointList structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstPointList { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *lbnd; /* Lower axis limits of bounding box */ + double *ubnd; /* Upper axis limits of bounding box */ +} AstPointList; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPointListVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* GetListSize)( AstPointList *, int * ); + void (* PointListPoints)( AstPointList *, AstPointSet **, int * ); +} AstPointListVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstPointListGlobals { + AstPointListVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstPointListGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitPointListGlobals_( AstPointListGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(PointList) /* Check class membership */ +astPROTO_ISA(PointList) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPointList *astPointList_( void *, AstPointSet *, AstRegion *, const char *, int *, ...); +#else +AstPointList *astPointListId_( void *, int, int, int, const double *, AstRegion *, const char *, ... )__attribute__((format(printf,7,8))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPointList *astInitPointList_( void *, size_t, int, AstPointListVtab *, + const char *, AstFrame *, AstPointSet *, + AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitPointListVtab_( AstPointListVtab *, const char *, int * ); + +/* Loader. */ +AstPointList *astLoadPointList_( void *, size_t, AstPointListVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +int astGetListSize_( AstPointList *, int * ); +void astPointListPoints_( AstPointList *, AstPointSet **, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPointList(this) astINVOKE_CHECK(PointList,this,0) +#define astVerifyPointList(this) astINVOKE_CHECK(PointList,this,1) + +/* Test class membership. */ +#define astIsAPointList(this) astINVOKE_ISA(PointList,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astPointList astINVOKE(F,astPointList_) +#else +#define astPointList astINVOKE(F,astPointListId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPointList(mem,size,init,vtab,name,frame,points,unc) \ +astINVOKE(O,astInitPointList_(mem,size,init,vtab,name,frame,points,unc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPointListVtab(vtab,name) astINVOKE(V,astInitPointListVtab_(vtab,name,STATUS_PTR)) + +/* Loader. */ +#define astLoadPointList(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPointList_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckPointList to validate PointList pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astGetListSize(this) \ +astINVOKE(V,astGetListSize_(astCheckPointList(this),STATUS_PTR)) +#define astPointListPoints(this,pset) \ +astINVOKE(V,astPointListPoints_(astCheckPointList(this),pset,STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/pointset.c b/pointset.c new file mode 100644 index 0000000..a704334 --- /dev/null +++ b/pointset.c @@ -0,0 +1,3207 @@ +/* +* Name: +* pointset.c + +* Purpose: +* Implement the PointSet class. + +* Description: +* This file implements the PointSet class. For a description of +* the class and its interface, see the .h file of the same name. + +* Inheritance: +* The PointSet class inherits from the Object class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 1-FEB-1996 (RFWS): +* Original version. +* 27-SEP-1996 (RFWS): +* Added external interface and I/O facilities. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitPointSetVtab +* method. +* 9-SEP-2004 (DSB): +* Added astPermPoints. +* 5-OCT-2004 (DSB): +* Bug fix in astLoadPointSet - npoint was used as size for new array +* of pointers (changed to ncoord). +* 19-OCT-2004 (DSB): +* Added astSetNpoint. +* 2-NOV-2004 (DSB): +* - Do not write out AST__BAD axis values in the Dump function. +* - Override astEqual method. +* - Add protected PointAccuracy attribute. +* 7-JAN-2005 (DSB): +* Added astAppendPoints. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 22-FEB-2006 (DSB): +* Avoid allocating memory for "acc" unless needed. +* 1-MAY-2009 (DSB): +* Added astBndPoints. +* 16-JUN-2011 (DSB): +* Added astReplaceNan. +* 2-OCT-2012 (DSB): +* Check for Infs as well as NaNs. +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS PointSet + +/* Values that control the behaviour of the astReplaceNaN method. */ +#define IGNORE_NANS 0 +#define REPLACE_NANS 1 +#define REPORT_NANS 2 + +/* +* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "pointset.h" +* MAKE_CLEAR(attr,component,assign) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstPointSet *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear_( AstPointSet *this, int axis ) +* +* which implement a method for clearing a single value in a specified +* multi-valued attribute for an axis of a PointSet. The "axis" value +* must be in the range 0 to (ncoord-1). + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. PointAccuracy in "astClearPointAccuracy"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attr,component,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstPointSet *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= this->ncoord ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astClear" #attr, astGetClass( this ), \ + axis + 1, this->ncoord ); \ +\ +/* Assign the "clear" value. */ \ + } else if( this->component ){ \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attr##_( AstPointSet *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,PointSet,Clear##attr))( this, axis, status ); \ +} + + +/* +* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "pointset.h" +* MAKE_GET(attr,type,bad_value,assign) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstPointSet *this, int axis ) +* +* and an external interface function of the form: +* +* astGet_( AstPointSet *this, int axis ) +* +* which implement a method for getting a single value from a specified +* multi-valued attribute for an axis of a PointSet. The "axis" value +* must be in the range 0 to (ncoord-1). + + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. PointAccuracy in "astGetPointAccuracy"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET(attr,type,bad_value,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstPointSet *this, int axis, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Initialise */ \ + result = bad_value; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= this->ncoord ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astGet" #attr, astGetClass( this ), \ + axis + 1, this->ncoord ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attr##_( AstPointSet *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,PointSet,Get##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute +* for a PointSet. + +* Type: +* Private macro. + +* Synopsis: +* #include "pointset.h" +* MAKE_SET(attr,type,component,assign,null) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstPointSet *this, int axis, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstPointSet *this, int axis, value ) +* +* which implement a method for setting a single value in a specified +* multi-valued attribute for a PointSet. The "axis" value must be in +* the range 0 to (ncoord-1). + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. PointAccuracy in "astSetPointAccuracy"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* null +* The value to initialise newly created array elements to. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET(attr,type,component,assign,null) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstPointSet *this, int axis, type value, int *status ) { \ + int i; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= this->ncoord ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astSet" #attr, astGetClass( this ), \ + axis + 1, this->ncoord ); \ +\ +/* Store the new value in the structure component. */ \ + } else { \ + if( !this->component ){ \ + this->component = astMalloc( this->ncoord*sizeof( type ) ); \ + for( i = 0; i < this->ncoord; i++ ) { \ + this->component[ i ] = null; \ + } \ + } \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attr##_( AstPointSet *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,PointSet,Set##attr))( this, axis, value, status ); \ +} + +/* +* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a method to test if a single value has been set in a +* multi-valued attribute for a class. + +* Type: +* Private macro. + +* Synopsis: +* #include "pointset.h" +* MAKE_TEST(attr,assign) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( AstPointSet *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest_( AstPointSet *this, int axis ) +* +* which implement a method for testing if a single value in a specified +* multi-valued attribute has been set for a class. The "axis" value +* must be in the range 0 to (ncoord-1). + +* Parameters: +* attr +* The name of the attribute to be tested, as it appears in the function +* name (e.g. PointAccuracy in "astTestPointAccuracy"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. This can use the string "axis" to represent the zero-based +* index of the value within the attribute. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_TEST(attr,assign) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attr( AstPointSet *this, int axis, int *status ) { \ + int result; /* Value to return */ \ +\ + result= 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= this->ncoord ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astTest" #attr, astGetClass( this ), \ + axis + 1, this->ncoord ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attr##_( AstPointSet *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,PointSet,Test##attr))( this, axis, status ); \ +} + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* This static variable is used to hold an IEEE 754 quiet double precision + Nan value. */ +static double ast_nan; + +/* This static variable is used to hold an IEEE 754 quiet single precision + Nan value. */ +static float ast_nanf; + +/* Enable or disable the astReplaceNan method. */ +static int replace_nan = -1; + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(PointSet) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(PointSet,Class_Init) +#define class_vtab astGLOBAL(PointSet,Class_Vtab) +#define getattrib_buff astGLOBAL(PointSet,GetAttrib_Buff) + +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ); +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPointSetVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPointSet *astPointSetId_( int, int, const char *, int *, ...); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static const char *GetAttrib( AstObject *, const char *, int * ); +static double **GetPoints( AstPointSet *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetNcoord( const AstPointSet *, int * ); +static int GetNpoint( const AstPointSet *, int * ); +static int GetObjSize( AstObject *, int * ); +static int ReplaceNaN( AstPointSet *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static AstPointSet *AppendPoints( AstPointSet *, AstPointSet *, int * ); +static void BndPoints( AstPointSet *, double *, double *, int * ); +static void CheckPerm( AstPointSet *, const int *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void PermPoints( AstPointSet *, int, const int[], int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetPoints( AstPointSet *, double **, int * ); +static void SetNpoint( AstPointSet *, int, int * ); +static void SetSubPoints( AstPointSet *, int, int, AstPointSet *, int * ); + +static double GetPointAccuracy( AstPointSet *, int, int * ); +static int TestPointAccuracy( AstPointSet *, int, int * ); +static void ClearPointAccuracy( AstPointSet *, int, int * ); +static void SetPointAccuracy( AstPointSet *, int, double, int * ); + +/* Member functions. */ +/* ================= */ +static AstPointSet *AppendPoints( AstPointSet *this, AstPointSet *that, int *status ) { +/* +*+ +* Name: +* astAppendPoints + +* Purpose: +* Append one PointSet to another. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* AstPointSet *astAppendPoints( AstPointSet *this, AstPointSet *that ) + +* Class Membership: +* PointSet method. + +* Description: +* This function creates a new PointSet containing all the points in +* "this" followed by all the points in "that". + +* Parameters: +* this +* Pointer to the first PointSet. +* that +* Pointer to the second PointSet. + +* Returned Value: +* Pointer to the new PointSet. + +* Notes: +* - Axis accuracies are copied from "this". +* - The Ncoord attribute of the two PointSets must match. +* - NULL will be returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPointSet *result; + double **ptr; + double **ptr1; + double **ptr2; + int ic; + int n1; + int n2; + int ncoord; + size_t nb2; + size_t nb1; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check the two PointSets have the same Ncoord value. */ + ncoord = astGetNcoord( this ); + if( ncoord != astGetNcoord( that ) ) { + astError( AST__NPTIN, "astAppendPoints(%s): Number of coordinates " + "per point differ in the two supplied PointSets.", status, + astGetClass( this ) ); + +/* Calculate the new size for the PointSet. */ + } else { + n1 = astGetNpoint( this ); + n2 = astGetNpoint( that ); + +/* Create the new PointSet and get pointers to its data. */ + result = astPointSet( n1 + n2, ncoord, "", status ); + ptr1 = astGetPoints( this ); + ptr2 = astGetPoints( that ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Copy the axis values for each coordinate in turn. */ + nb1 = sizeof( double )*(size_t) n1; + nb2 = sizeof( double )*(size_t) n2; + for( ic = 0; ic < ncoord; ic++ ) { + memcpy( ptr[ ic ], ptr1[ ic ], nb1 ); + memcpy( ptr[ ic ] + n1, ptr2[ ic ], nb2 ); + } + +/* Copy any axis accuracies from "this". */ + result->acc = this->acc ? + astStore( NULL, this->acc, sizeof( double )*(size_t) ncoord ) + : NULL; + } + } + +/* Annul the result if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void BndPoints( AstPointSet *this, double *lbnd, double *ubnd, int *status ) { +/* +*+ +* Name: +* astBndPoints + +* Purpose: +* Find the axis bounds of the points in a PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* void astBndPoints( AstPointSet *this, double *lbnd, double *ubnd ) + +* Class Membership: +* PointSet method. + +* Description: +* This function returns the lower and upper limits of the axis values +* of the points in a PointSet. + +* Parameters: +* this +* Pointer to the first PointSet. +* lbnd +* Pointer to an array in which to return the lowest value for +* each coordinate. The length of the array should equal the number +* returned by astGetNcoord. +* ubnd +* Pointer to an array in which to return the highest value for +* each coordinate. The length of the array should equal the number +* returned by astGetNcoord. + +*- +*/ + +/* Local Variables: */ + double **ptr; + double *p; + double lb; + double ub; + int ic; + int ip; + int nc; + int np; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get pointers to the PointSet data, the number of axes adn the number + of points. */ + ptr = astGetPoints( this ); + nc = astGetNcoord( this ); + np = astGetNpoint( this ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Loop round each axis. */ + for( ic = 0; ic < nc; ic++ ) { + +/* Initialise the bounds for this axis. */ + lb = AST__BAD; + ub = AST__BAD; + +/* Search for the first good point. Use it to initialise the bounds and + break out of the loop. */ + p = ptr[ ic ]; + for( ip = 0; ip < np; ip++,p++ ) { + if( *p != AST__BAD ) { + lb = ub = *p; + break; + } + } + +/* Search through the remaining points. Update the bounds if the axis + value is good. */ + for( ; ip < np; ip++,p++ ) { + if( *p != AST__BAD ) { + if( *p < lb ) { + lb = *p; + } else if( *p > ub ) { + ub = *p; + } + } + } + +/* Store the returned bounds. */ + lbnd[ ic ] = lb; + ubnd[ ic ] = ub; + } + } +} + +static void CheckPerm( AstPointSet *this, const int *perm, const char *method, int *status ) { +/* +*+ +* Name: +* astCheckPerm + +* Purpose: +* Check that an array contains a valid permutation. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* void astCheckPerm( AstPointSet *this, const int *perm, const char *method ) + +* Class Membership: +* PointSet method. + +* Description: +* This function checks the validity of a permutation array that +* will be used to permute the order of a PointSet's axes. If the +* permutation specified by the array is not valid, an error is +* reported and the global error status is set. Otherwise, the +* function returns without further action. + +* Parameters: +* this +* Pointer to the PointSet. +* perm +* Pointer to an array of integers with the same number of +* elements as there are axes in the PointSet. For each axis, the +* corresponding integer gives the (zero based) axis index to be +* used to identify the axis values for that axis (using the +* un-permuted axis numbering). To be valid, the integers in +* this array should therefore all lie in the range zero to +* (ncoord-1) inclusive, where "ncoord" is the number of PointSet +* axes, and each value should occur exactly once. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate a permutation array. This method name is used +* solely for constructing error messages. + +* Notes: +* - Error messages issued by this function refer to the external +* (public) numbering system used for axes (which is one-based), +* whereas zero-based axis indices are used internally. +*- +*/ + +/* Local Variables: */ + int *there; /* Pointer to temporary array */ + int coord; /* Loop counter for axes */ + int ncoord; /* Number of PointSet axes */ + int valid; /* Permutation array is valid? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise. */ + valid = 1; + +/* Obtain the number of PointSet axes and allocate a temporary array of + integers with the same number of elements. */ + ncoord = astGetNcoord( this ); + there = astMalloc( sizeof( int ) * (size_t) ncoord ); + if ( astOK ) { + +/* Initialise the temporary array to zero. */ + for ( coord = 0; coord < ncoord; coord++ ) there[ coord ] = 0; + +/* Scan the permutation array, checking that each permuted axis index it + contains is within the correct range. Note an error and quit checking + if an invalid value is found. */ + for ( coord = 0; coord < ncoord; coord++ ) { + if ( ( perm[ coord ] < 0 ) || ( perm[ coord ] >= ncoord ) ) { + valid = 0; + break; + +/* Use the temporary array to count how many times each valid axis index + occurs. */ + } else { + there[ perm[ coord ] ]++; + } + } + +/* If all the axis indices were within range, check to ensure that each value + occurred only once. */ + if ( valid ) { + for ( coord = 0; coord < ncoord; coord++ ) { + +/* Note an error and quit checking if any value did not occur exactly once. */ + if ( there[ coord ] != 1 ) { + valid = 0; + break; + } + } + } + } + +/* Free the temporary array. */ + there = astFree( there ); + +/* If an invalid permutation was detected and no other error has + occurred, then report an error (note we convert to one-based axis + numbering in the error message). */ + if ( !valid && astOK ) { + astError( AST__PRMIN, "%s(%s): Invalid coordinate permutation array.", status, + method, astGetClass( this ) ); + astError( AST__PRMIN, "Each coordinate index should lie in the range 1 to %d " + "and should occur only once.", status, ncoord ); + } +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a PointSet. + +* Type: +* Private function. + +* Synopsis: +* #include "pointset.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PointSet member function (over-rides the astClearAttrib +* protected method inherited from the Object class). + +* Description: +* This function clears the value of a specified attribute for a +* PointSet, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the PointSet. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPointSet *this; /* Pointer to the PointSet structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PointSet structure. */ + this = (AstPointSet *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* Test if the name matches any of the read-only attributes of this + class. If it does, then report an error. */ + if ( !strcmp( attrib, "ncoord" ) || + !strcmp( attrib, "npoint" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two PointSets are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "pointset.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* PointSet member function (over-rides the astEqual protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two PointSets are equivalent. + +* Parameters: +* this +* Pointer to the first PointSet. +* that +* Pointer to the second PointSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the PointSets are equivalent, zero otherwise. + +* Notes: +* - The two PointSets are considered equivalent if they have the same +* number of points, the same number of axis values per point, and the +* same axis values to within the absolute tolerance specified by the +* Accuracy attribute of the two PointSets. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local constants: */ +#define SMALL sqrt(DBL_MIN) + +/* Local Variables: */ + AstPointSet *that; /* Pointer to the second PointSet structure */ + AstPointSet *this; /* Pointer to the first PointSet structure */ + double **ptr_that; /* Pointer to axis values in second PointSet */ + double **ptr_this; /* Pointer to axis values in first PointSet */ + double *p_that; /* Pointer to next axis value in second PointSet */ + double *p_this; /* Pointer to next axis value in first PointSet */ + double acc1; /* Absolute accuracy for 1st PointSet axis value */ + double acc2; /* Absolute accuracy for 2nd PointSet axis value */ + double acc; /* Combined absolute accuracy */ + double acc_that; /* PointAccuracy attribute for 2nd PointSet */ + double acc_this; /* PointAccuracy attribute for 1st PointSet */ + int ic; /* Axis index */ + int ip; /* Point index */ + int nc; /* No. of axis values per point */ + int np; /* No. of points in each PointSet */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent Object class. This checks + that the Objects are both of the same class (amongst other things). */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Obtain pointers to the two PointSet structures. */ + this = (AstPointSet *) this_object; + that = (AstPointSet *) that_object; + +/* Check the number of points and the number of axis values per point are + equal in the two PointSets. */ + np = astGetNpoint( this ); + nc = astGetNcoord( this ); + if( np == astGetNpoint( that ) && nc == astGetNcoord( that ) ) { + +/* Get pointers to the axis values.*/ + ptr_this = astGetPoints( this ); + ptr_that = astGetPoints( that ); + if( astOK ) { + +/* Assume the PointSets are equal until proven otherwise. */ + result = 1; + +/* Loop round each axis, until we find a difference. */ + for( ic = 0; ic < nc && result; ic++ ) { + +/* Get pointers to the next value for this axis. */ + p_this = ptr_this[ ic ]; + p_that = ptr_that[ ic ]; + +/* Get the absolute accuracies for this axis. The default value for this + attribute is AST__BAD. */ + acc_this = astGetPointAccuracy( this, ic ); + acc_that = astGetPointAccuracy( that, ic ); + +/* If both accuracies are available, combine them in quadrature. */ + if( acc_this != AST__BAD && acc_that != AST__BAD ) { + acc = sqrt( acc_this*acc_this + acc_that*acc_that ); + +/* Loop round all points on this axis */ + for( ip = 0; ip < np; ip++, p_this++, p_that++ ){ + +/* If either value is bad we do not need to compare values. */ + if( *p_this == AST__BAD || *p_that == AST__BAD ) { + +/* If one value is bad and one is good, they differ, so break. If both + values are bad they are equal so we continue. */ + if( *p_this != AST__BAD || *p_that != AST__BAD ) { + result = 0; + break; + } + +/* Otherwise (if both axis values are good), compare axis values, and break if + they differ by more than the absolute accuracy. */ + } else if( fabs( *p_this - *p_that ) > acc ) { + result = 0; + break; + } + } + +/* If either accuracy is unavailable, we use a default relative accuracy. */ + } else { + +/* Loop round all points on this axis */ + for( ip = 0; ip < np; ip++, p_this++, p_that++ ){ + +/* If either value is bad we do not need to compare values. */ + if( *p_this == AST__BAD || *p_that == AST__BAD ) { + +/* If one value is bad and one is good, they differ, so break. If both + values are bad they are equal so we continue. */ + if( *p_this != AST__BAD || *p_that != AST__BAD ) { + result = 0; + break; + } + +/* Otherwise (if both axis values are good), find the absolute error for + both values. */ + } else { + + if( acc_this == AST__BAD ) { + acc1 = fabs(*p_this)*DBL_EPSILON; + if( acc1 < SMALL ) acc1 = SMALL; + acc1 *= 1.0E3; + } else { + acc1 = acc_this; + } + + if( acc_that == AST__BAD ) { + acc2 = fabs(*p_that)*DBL_EPSILON; + if( acc2 < SMALL ) acc2 = SMALL; + acc2 *= 1.0E3; + } else { + acc2 = acc_that; + } + +/* Combine them in quadrature. */ + acc = sqrt( acc1*acc1 + acc2*acc2 ); + +/* Compare axis values, and break if they differ by more than the + absolute accuracy. */ + if( fabs( *p_this - *p_that ) > acc ) { + result = 0; + break; + } + } + } + } + } + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +#undef SMALL +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a PointSet. + +* Type: +* Private function. + +* Synopsis: +* #include "pointset.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PointSet member function (over-rides the protected astGetAttrib +* method inherited from the Object class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a PointSet, formatted as a character string. + +* Parameters: +* this +* Pointer to the PointSet. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the PointSet, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the PointSet. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPointSet *this; /* Pointer to the PointSet structure */ + const char *result; /* Pointer value to return */ + int ncoord; /* Ncoord attribute value */ + int npoint; /* Npoint attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the PointSet structure. */ + this = (AstPointSet *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* Ncoord. */ +/* ------- */ + if ( !strcmp( attrib, "ncoord" ) ) { + ncoord = astGetNcoord( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ncoord ); + result = getattrib_buff; + } + +/* Npoint. */ +/* ------- */ + } else if ( !strcmp( attrib, "npoint" ) ) { + npoint = astGetNpoint( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", npoint ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "pointset.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* PointSet member function (over-rides the astGetObjSize protected +* method inherited from the Object class). + +* Description: +* This function returns the in-memory size of the supplied PointSet, +* in bytes. + +* Parameters: +* this +* Pointer to the Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *this; /* Pointer to PointSet structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the PointSet structure. */ + this = (AstPointSet *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astTSizeOf( this->ptr ); + result += astTSizeOf( this->values ); + result += astTSizeOf( this->acc ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetNcoord( const AstPointSet *this, int *status ) { +/* +*+ +* Name: +* astGetNcoord + +* Purpose: +* Get the number of coordinate values per point from a PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* int astGetNcoord( const AstPointSet *this ) + +* Class Membership: +* PointSet method. + +* Description: +* This function returns the number of coordinate values per point (1 or +* more) for a PointSet. + +* Parameters: +* this +* Pointer to the PointSet. + +* Returned Value: +* The number of coordinate values per point. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the number of coordinate values. */ + return this->ncoord; +} + +static int GetNpoint( const AstPointSet *this, int *status ) { +/* +*+ +* Name: +* astGetNpoint + +* Purpose: +* Get the number of points in a PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* int astGetNpoint( const AstPointSet *this ) + +* Class Membership: +* PointSet method. + +* Description: +* This function returns the number of points (1 or more) in a PointSet. + +* Parameters: +* this +* Pointer to the PointSet. + +* Returned Value: +* The number of points. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the number of points. */ + return this->npoint; +} + +static double **GetPoints( AstPointSet *this, int *status ) { +/* +*+ +* Name: +* astGetPoints + +* Purpose: +* Get a pointer for the coordinate values associated with a PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* double **astGetPoints( AstPointSet *this ) + +* Class Membership: +* PointSet method. + +* Description: +* This function returns a pointer which grants access to the coordinate +* values associated with a PointSet. If the PointSet has previously had +* coordinate values associated with it, this pointer will identify these +* values. Otherwise, it will point at a newly-allocated region of memory +* (associated with the PointSet) in which new coordinate values may be +* stored. + +* Parameters: +* this +* Pointer to the PointSet. + +* Returned Value: +* A pointer to an array of type double* with ncoord elements (where ncoord +* is the number of coordinate values per point). Each element of this array +* points at an array of double, of size npoint (where npoint is the number +* of points in the PointSet), containing the values of that coordinate for +* each point in the set. Hence, the value of the i'th coordinate for the +* j'th point (where i and j are counted from zero) is given by ptr[i][j] +* where ptr is the returned pointer value. + +* Notes: +* - The returned pointer points at an array of pointers allocated +* internally within the PointSet. The values in this array may be changed +* by the caller, who is reponsible for ensuring that they continue to +* point at valid arrays of coordinate values. +* - No attempt should be made to de-allocate memory allocated by a +* PointSet to store coordinate values or pointers to them. This memory +* will be freed when the PointSet is deleted. +* - No count is kept of the number of pointers issued for the PointSet +* coordinate values. The caller must keep track of these. +* - A NULL pointer is returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int i; /* Loop counter for coordinates */ + int nval; /* Number of values to be stored */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* If the PointSet has an existing array of pointers (which point at coordinate + values), we will simply return a pointer to it. Otherwise, we must allocate + space to hold new coordinate values. */ + if( !this->ptr ) { + +/* Determine the number of coordinate values to be stored and allocate memory + to hold them, storing the pointer to this values array in the PointSet + structure. */ + nval = this->npoint * this->ncoord; + this->values = (double *) astMalloc( sizeof( double ) * (size_t) nval ); + +#ifdef DEBUG + for( i = 0; i < nval; i++ ) this->values[ i ] = 0.0; +#endif + +/* If OK, also allocate memory for the array of pointers into this values + array, storing a pointer to this pointer array in the PointSet structure. */ + if ( astOK ) { + this->ptr = (double **) astMalloc( sizeof( double * ) + * (size_t) this->ncoord ); + +/* If OK, initialise the pointer array to point into the values array. */ + if ( astOK ) { + for ( i = 0; i < this->ncoord; i++ ) { + this->ptr[ i ] = this->values + ( i * this->npoint ); + } + +/* If we failed to allocate the pointer array, then free the values array. */ + } else { + this->values = (double *) astFree( (void *) this->values ); + } + } + +#ifdef DEBUG + } else { + +/* Check for bad values */ + if( this->values ) { + int i, j; + for( i = 0; astOK && i < this->ncoord; i++ ) { + for( j = 0; j < this->npoint; j++ ) { + if( !finite( (this->ptr)[ i ][ j ] ) ) { + astError( AST__INTER, "astGetPoints(PointSet): Non-finite " + "axis value returned.", status); + break; + } + } + } + } + +#endif + } + +/* Return the required pointer. */ + return this->ptr; +} + +void astInitPointSetVtab_( AstPointSetVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPointSetVtab + +* Purpose: +* Initialise a virtual function table for a PointSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointset.h" +* void astInitPointSetVtab( AstPointSetVtab *vtab, const char *name ) + +* Class Membership: +* PointSet vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the PointSet class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + const char *envvar; /* Pointer to environment variable value */ + size_t i; /* Index of next byte in NaN value */ + unsigned char *p; /* Pointer to next byte in NaN value */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitObjectVtab( (AstObjectVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPointSet) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstObjectVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->AppendPoints = AppendPoints; + vtab->BndPoints = BndPoints; + vtab->GetNcoord = GetNcoord; + vtab->GetNpoint = GetNpoint; + vtab->GetPoints = GetPoints; + vtab->PermPoints = PermPoints; + vtab->ReplaceNaN = ReplaceNaN; + vtab->SetPoints = SetPoints; + vtab->SetNpoint = SetNpoint; + vtab->SetSubPoints = SetSubPoints; + + vtab->GetPointAccuracy = GetPointAccuracy; + vtab->SetPointAccuracy = SetPointAccuracy; + vtab->TestPointAccuracy = TestPointAccuracy; + vtab->ClearPointAccuracy = ClearPointAccuracy; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + parent_equal = object->Equal; + object->Equal = Equal; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "PointSet", "Container for a set of points" ); + +/* Calculate single and double precision NaN values and store in static + module variables. Setting all bits to 1 produces a quiet NaN. */ + LOCK_MUTEX1 + p = (unsigned char *) &ast_nan; + for( i = 0; i < sizeof( ast_nan ); i++ ) *(p++) = 255; + p = (unsigned char *) &ast_nanf; + for( i = 0; i < sizeof( ast_nanf ); i++ ) *(p++) = 255; + +/* See what action the astReplaceNaN method should perform. This + is determined by the value of the AST_REPLACE_NAN environment + variable. Not set = do not check for NaNs, "1" = replace NaNs with + AST__BAD silently, anything else = report an error if any NaNs are + encountered. */ + if( replace_nan == -1 ) { + envvar = getenv( "AST_REPLACE_NAN" ); + if( !envvar ) { + replace_nan = IGNORE_NANS; + } else if( !strcmp( envvar, "1" ) ) { + replace_nan = REPLACE_NANS; + } else { + replace_nan = REPORT_NANS; + } + } + UNLOCK_MUTEX1 + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +double astCheckNaN_( double value ) { +/* +*+ +* Name: +* astCheckNaN + +* Purpose: +* Substitute a NaN for a supplied value if the supplied value is +* AST__NAN. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointset.h" +* double astCheckNaN( double value ) + +* Class Membership: +* PointSet method. + +* Description: +* If the supplied double is AST__NAN then this function returns an +* IEEE double precision NaN value. Otherwise it returns the supplied +* value. + +* Parameters: +* valuethis +* The value to check. + +* Returned Value: +* The suppleid value, or NaN. + +* Notes: +* - This function does not check the inherited status. + +*- +*/ + return ( value == AST__NAN ) ? ast_nan : value; +} + +float astCheckNaNF_( float value ) { +/* +*+ +* Name: +* astCheckNaNF + +* Purpose: +* Substitute a NaN for a supplied value if the supplied value is +* AST__NANF. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointset.h" +* float astCheckNaNF( float value ) + +* Class Membership: +* PointSet method. + +* Description: +* If the supplied float is AST__NANF then this function returns an +* IEEE single precision NaN value. Otherwise it returns the supplied +* value. + +* Parameters: +* valuethis +* The value to check. + +* Returned Value: +* The suppleid value, or NaN. + +* Notes: +* - This function does not check the inherited status. + +*- +*/ + return ( value == AST__NANF ) ? ast_nanf : value; +} + +static void PermPoints( AstPointSet *this, int forward, const int perm[], int *status ) { +/* +*+ +* Name: +* astPermPoints + +* Purpose: +* Permute the order of a PointSet's axes. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* void astPermPoints( AstPointSet *this, int forward, const int perm[] ) + +* Class Membership: +* PointSet method. + +* Description: +* This function permutes the order in which a PointSet's axes occur. + +* Parameters: +* this +* Pointer to the PointSet. +* forward +* The direction in which the permutation is to be applied. This +* controls the use of the "perm" arrays. If a non-zero value is +* given, then the indices into the "perm" array correspond to the +* indices of the coordinates in the returned PointSet, and the +* values stored in the "perm" array correspond to the indices of +* the coordinates in the supplied PointSet. If a zero value is +* given, then the indices into the "perm" array correspond to the +* indices of the coordinates in the supplied PointSet, and the +* values stored in the "perm" array correspond to the indices of +* the coordinates in the returnedPointSet. +* perm +* An array of int (with one element for each axis of the PointSet) +* which lists the axes in their new order. How this array is use +* depends on the value supplied for "forward". + +* Notes: +* - Only genuine permutations of the axis order are permitted, so +* each axis must be referenced exactly once in the "perm" array. +* - If more than one axis permutation is applied to a PointSet, the +* effects are cumulative. +*- +*/ + +/* Local Variables: */ + double **old; /* Pointer to copy of old pointer array */ + int coord; /* Loop counter for axes */ + int ncoord; /* Number of axes */ + +/* Check the global error status. Return without action if no data is + associated with the PointSet. */ + if ( !astOK || !this->ptr ) return; + +/* Validate the permutation array, to check that it describes a genuine + permutation. */ + CheckPerm( this, perm, "astPermPoints", status ); + +/* Obtain the number of PointSet axes. */ + ncoord = astGetNcoord( this ); + +/* Allocate memory and use it to store a copy of the old pointers array for + the PointSet. */ + old = astStore( NULL, this->ptr, sizeof( double * ) * (size_t) ncoord ); + +/* Apply the new axis permutation cumulatively to the old one and store the + result in the PointSet. */ + if ( astOK ) { + if( forward ) { + for ( coord = 0; coord < ncoord; coord++ ) { + this->ptr[ coord ] = old[ perm[ coord ] ]; + } + } else { + for ( coord = 0; coord < ncoord; coord++ ) { + this->ptr[ perm[ coord ] ] = old[ coord ]; + } + } + } + +/* Free the temporary copy of the old array. */ + old = astFree( old ); +} + +static int ReplaceNaN( AstPointSet *this, int *status ) { +/* +*+ +* Name: +* astReplaceNaN + +* Purpose: +* Check for NaNs in a PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* int astReplaceNaNs( AstPointSet *this ) + +* Class Membership: +* PointSet method. + +* Description: +* The behaviour of this method is determined by the AST_REPLACE_NAN +* environment variable. If AST_REPLACE_NAN is undefined, then the +* method returns without action. If AST_REPLACE_NAN is "1", then the +* method examines the supplied PointSet and replaces any NaN values +* with AST__BAD. If AST_REPLACE_NAN has any other value, any NaNs in +* the supplied PointSet are still replaced, but in addition an error +* is reported. + +* Parameters: +* this +* Pointer to the PointSet. + +* Returned Value: +* Non-zero if any NaN values were found in the PointSet. If AST_REPLACE_NAN +* is undefined, then zero is always returned. + +* Notes: +* The value of the AST_REPLACE_NAN environment variable is obtained +* only once, when the PointSet virtual function table is first +* created. This value is saved for all subsequent invocations of this +* method. + +*- +*/ + +/* Local Variables: */ + double **ptr; + double *p0; + double *p; + int ic; + int nc; + int np; + int result; + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Unless the AST_REPALCE_NAN environment variable is undefined, check + for NaNs in the supplied PointSet, replacing any with AST__BAD. */ + if( replace_nan != IGNORE_NANS ) { + ptr = astGetPoints( this ); + if( ptr ) { + nc = astGetNcoord( this ); + np = astGetNpoint( this ); + for( ic = 0; ic < nc; ic++ ) { + p = ptr[ ic ]; + p0 = p + np; + for( ; p < p0; p++ ) { + if( !astISFINITE(*p) ) { + result = 1; + *p = AST__BAD; + } + } + } + +/* If any NaNs were found, and AST_REPLACE_NAN is not set to "1", report + an error. */ + if( result && replace_nan == REPORT_NANS ) { + astError( AST__ISNAN, "astReplaceNan(%s): One or more NaN values " + "were encountered within an AST PointSet.", status, + astGetClass( this ) ); + } + } + } + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a PointSet. + +* Type: +* Private function. + +* Synopsis: +* #include "pointset.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* PointSet member function (over-rides the astSetAttrib protected +* method inherited from the Object class). + +* Description: +* This function assigns an attribute value for a PointSet, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the PointSet. +* setting +* Pointer to a null-terminated string specifying the new +* attribute value. +*/ + +/* Local Variables: */ + AstPointSet *this; /* Pointer to the PointSet structure */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PointSet structure. */ + this = (AstPointSet *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* Use this macro to report an error if a read-only attribute has been + specified. */ + if ( MATCH( "ncoord" ) || + MATCH( "npoint" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void SetNpoint( AstPointSet *this, int npoint, int *status ) { +/* +*+ +* Name: +* astSetNpoint + +* Purpose: +* Reduce the number of points in a PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* void astSetNpoint( AstPointSet *this, int npoint ) + +* Class Membership: +* PointSet method. + +* Description: +* This function reduces the number of points stored in a PointSet. +* Points with indices beyond the new size will be discarded. + +* Parameters: +* this +* Pointer to the PointSet. +* npoint +* The new value for the number of points in the PointSet. Must be +* less than or equal to the original size of the PointSet, and +* greater than zero. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check the new size is valid. */ + if( npoint < 1 || npoint > this->npoint ) { + astError( AST__NPTIN, "astSetNpoint(%s): Number of points (%d) is " + "not valid.", status, astGetClass( this ), npoint ); + astError( AST__NPTIN, "Should be in the range 1 to %d.", status, this->npoint ); + +/* Store the new size. */ + } else { + this->npoint = npoint; + } +} + +static void SetPoints( AstPointSet *this, double **ptr, int *status ) { +/* +*+ +* Name: +* astSetPoints + +* Purpose: +* Associate coordinate values with a PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* void astSetPoints( AstPointSet *this, double **ptr ) + +* Class Membership: +* PointSet method. + +* Description: +* This function associates coordinate values with a PointSet by storing an +* array of pointers to the values within the PointSet object. A pointer to +* this pointer array will later be returned when astGetPoints is used to +* locate the coordinate values. If values are already associated with the +* PointSet, the array of pointers to them is over-written by the new values +* (any internally allocated memory holding the actual coordinate values +* first being freed). + +* Parameters: +* this +* Pointer to the PointSet. +* ptr +* Pointer to an array of type double* with ncoord elements (where ncoord +* is the number of coordinate values per point in the PointSet). Each +* element of this array should point at an array of double with npoint +* elements (where npoint is the number of points in the PointSet), +* containing the values of that coordinate for each point in the set. +* Hence, the value of the i'th coordinate for the j'th point (where i +* and j are counted from zero) should be given by ptr[i][j]. + +* Returned Value: +* void + +* Notes: +* - It is the caller's responsibility to ensure that the pointer supplied +* points at a valid array of pointers that point at arrays of coordinate +* values. This is only superficially validated by this function, which then +* simply stores a copy of the supplied array of pointers for later use. +* The caller must also manage any allocation (and freeing) of memory for +* these coordinate values. +* - This functon makes a copy of the array of pointers supplied, but does +* not copy the coordinate values they point at. If a PointSet containing a +* copy of the coordinate values is required, internal memory should be +* allocated within the PointSet by calling astGetPoints before storing any +* pointer, and then copying the values into this memory. Alternatively, +* using astCopy to produce a deep copy of a PointSet will also copy the +* coordinate values. +* - A NULL pointer may be supplied as the "ptr" argument, in which case +* any previously stored array of pointers will be cancelled (and internal +* memory freed if necessary) and subsequent use of astGetPoints will then +* cause memory to be allocated internally by the PointSet to hold new +* values. +*- +*/ + +/* Local Variables: */ + int i; /* Loop counter for coordinates */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If the pointer supplied is not NULL, inspect each pointer in the array it + points at to check that none of these are NULL. This validates (to some + extent) the caller's data structure. Report an error and quit checking if a + NULL pointer is found. */ + if ( ptr ) { + for ( i = 0; i < this->ncoord; i++ ) { + if ( !ptr[ i ] ) { + astError( AST__PDSIN, "astSetPoints(%s): Invalid NULL pointer in " + "element %d of array of pointers to coordinate values.", status, + astGetClass( this ), i ); + break; + } + } + } + +/* Do not carry on if the data structure is obviously invalid. */ + if ( astOK ) { + +/* Free any memory previously allocated to store coordinate values. */ + this->values = (double *) astFree( (void *) this->values ); + +/* If a new array of pointers has been provided, (re)allocate memory and store + a copy of the array in it, saving a pointer to this copy in the PointSet + structure. */ + if ( ptr ) { + this->ptr = (double **) astStore( (void *) this->ptr, + (const void *) ptr, + sizeof( double * ) + * (size_t) this->ncoord ); + +/* If no pointer array was provided, free the previous one (if any). */ + } else { + this->ptr = (double **) astFree( (void *) this->ptr ); + } + } +} + +static void SetSubPoints( AstPointSet *point1, int point, int coord, + AstPointSet *point2, int *status ) { +/* +*+ +* Name: +* astSetSubPoints + +* Purpose: +* Associate a subset of one PointSet with another PointSet. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "pointset.h" +* void astSetSubPoints( AstPointSet *point1, int point, int coord, +* AstPointSet *point2 ) + +* Class Membership: +* PointSet method. + +* Description: +* This function selects a subset of the coordinate values associated with +* one PointSet and associates them with another PointSet. The second +* PointSet may then be used to access the subset. Any previous coordinate +* value association with the second PointSet is replaced. + +* Parameters: +* point1 +* Pointer to the first PointSet, from which a subset is to be selected. +* point +* The index of the first point (counting from zero) which is to appear +* in the subset (the number of points is determined by the size of the +* second PointSet). +* coord +* The index of the first coordinate (counting from zero) which is to +* appear in the subset (the number of coordinates is determined by the +* size of the second PointSet). +* point2 +* Second PointSet, with which the subset of coordinate values is to be +* associated. + +* Returned Value: +* void + +* Notes: +* - The range of points and coordinates selected must lie entirely within +* the first PointSet. +* - This function does not make a copy of the coordinate values, but +* merely stores pointers to the required subset of values associated with +* the first PointSet. If a PointSet containing a copy of the subset's +* coordinate values is required, then astCopy should be used to make a +* deep copy from the second PointSet. +* - If the first PointSet does not yet have coordinate values associated +* with it, then space will be allocated within it to hold values (so that +* the second PointSet has somewhere to point at). +*- +*/ + +/* Local Variables: */ + double ** ptr2; /* Pointer to new pointer array */ + double **ptr1; /* Pointer to original pointer array */ + int i; /* Loop counter for coordinates */ + int ncoord1; /* Number of coordinates in first PointSet */ + int ncoord2; /* Number of coordinates in second PointSet */ + int npoint1; /* Number of points in first PointSet */ + int npoint2; /* Number of points in second PointSet */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the sizes of both PointSets. */ + npoint1 = astGetNpoint( point1 ); + npoint2 = astGetNpoint( point2 ); + ncoord1 = astGetNcoord( point1 ); + ncoord2 = astGetNcoord( point2 ); + +/* Check if the range of points required lies within the first PointSet and + report an error if it does not. */ + if ( astOK ) { + if ( ( point < 0 ) || ( point + npoint2 > npoint1 ) ) { + astError( AST__PTRNG, "astSetSubPoints(%s): Range of points in " + "output %s (%d to %d) lies outside the input %s extent " + "(0 to %d).", status, + astGetClass( point1 ), astGetClass( point2 ), point, + point + npoint2, astGetClass( point1 ), npoint1 ); + +/* Similarly check that the range of coordinates is valid. */ + } else if ( ( coord < 0 ) || ( coord + ncoord2 > ncoord1 ) ) { + astError( AST__CORNG, "astSetSubPoints(%s): Range of coordinates in " + "output %s (%d to %d) lies outside the input %s extent " + "(0 to %d).", status, + astGetClass( point1 ), astGetClass( point2 ), coord, + coord + ncoord2, astGetClass( point1 ), ncoord1 ); + +/* Obtain a pointer for the coordinate values associated with the first + PointSet (this will cause internal memory to be allocated if it is not + yet associated with coordinate values). */ + } else { + ptr1 = astGetPoints( point1 ); + +/* Allocate a temporary array to hold new pointer values. */ + ptr2 = (double **) astMalloc( sizeof( double * ) * (size_t) ncoord2 ); + +/* Initialise this pointer array to point at the required subset of coordinate + values. */ + if ( astOK ) { + for ( i = 0; i < ncoord2; i++ ) { + ptr2[ i ] = ptr1[ i + coord ] + point; + } + +/* Associate the second PointSet with this new pointer array. This will free + any internally allocated memory and replace any existing coordinate value + association. */ + astSetPoints( point2, ptr2 ); + } + +/* Free the temporary pointer arry. */ + ptr2 = (double **) astFree( (void * ) ptr2 ); + } + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a PointSet. + +* Type: +* Private function. + +* Synopsis: +* #include "pointset.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PointSet member function (over-rides the astTestAttrib protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate +* whether a value has been set for one of a PointSet's attributes. + +* Parameters: +* this +* Pointer to the PointSet. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *this; /* Pointer to the PointSet structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the PointSet structure. */ + this = (AstPointSet *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* Test if the name matches any of the read-only attributes of this + class. If it does, then return zero. */ + if ( !strcmp( attrib, "ncoord" ) || + !strcmp( attrib, "npoint" ) ) { + result = 0; + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ + +/* +*att+ +* Name: +* PointAccuracy + +* Purpose: +* The absolute accuracies for all points in the PointSet. + +* Type: +* Protected attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute holds the absolute accuracy for each axis in the +* PointSet. It has a separate value for each axis. It is used when +* comparing two PointSets using the protected astEqual method inherited +* from the Object class. The default value for each axis is AST__BAD +* which causes the a default accuracy of each axis value to be calculated +* as 1.0E8*min( abs(axis value)*DBL_EPSILON, DBL_MIN ). + +* Applicability: +* PointSet +* All PointSets have this attribute. +*att- +*/ +MAKE_CLEAR(PointAccuracy,acc,AST__BAD) +MAKE_GET(PointAccuracy,double,AST__BAD,this->acc?this->acc[axis]:AST__BAD) +MAKE_SET(PointAccuracy,double,acc,((value!=AST__BAD)?fabs(value):AST__BAD),AST__BAD) +MAKE_TEST(PointAccuracy,(this->acc?this->acc[axis]!=AST__BAD:0)) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for PointSet objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for PointSet objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the coordinate +* values (if any) associated with the input PointSet. +*/ + +/* Local Variables: */ + AstPointSet *in; /* Pointer to input PointSet */ + AstPointSet *out; /* Pointer to output PointSet */ + int i; /* Loop counter for coordinates */ + int nval; /* Number of values to store */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output PointSets. */ + in = (AstPointSet *) objin; + out = (AstPointSet *) objout; + +/* For safety, first clear any references to the input coordinate values from + the output PointSet. */ + out->ptr = NULL; + out->values = NULL; + out->acc = NULL; + +/* Copy axis accuracies. */ + if( in->acc ){ + out->acc = astStore( NULL, in->acc, sizeof( double )*(size_t) in->ncoord ); + } + +/* If the input PointSet is associated with coordinate values, we must + allocate memory in the output PointSet to hold a copy of them. */ + if ( in->ptr ) { + +/* Determine the number of coordinate values to be stored and allocate memory + to hold them, storing a pointer to this memory in the output PointSet. */ + nval = in->npoint * in->ncoord; + out->values = (double *) astMalloc( sizeof( double ) * (size_t) nval ); + +/* If OK, also allocate memory for the array of pointers into this values + array, storing a pointer to this pointer array in the output PointSet. */ + if ( astOK ) { + out->ptr = (double **) astMalloc( sizeof( double * ) + * (size_t) in->ncoord ); + +/* If OK, initialise the new pointer array. */ + if ( astOK ) { + for ( i = 0; i < in->ncoord; i++ ) { + out->ptr[ i ] = out->values + ( i * in->npoint ); + } + +/* If we failed to allocate the pointer array, then free the values array. */ + } else { + out->values = (double *) astFree( (void *) out->values ); + } + } + +/* Copy the values for each coordinate from the input to the output. Use a + memory copy to avoid floating point errors if the data are + un-initialised. */ + if ( astOK ) { + for ( i = 0; i < in->ncoord; i++ ) { + (void) memcpy( (void *) out->ptr[ i ], + (const void *) in->ptr[ i ], + sizeof( double ) * (size_t) in->npoint ); + } + } + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for PointSet objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for PointSet objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPointSet *this; /* Pointer to PointSet */ + +/* Obtain a pointer to the PointSet structure. */ + this = (AstPointSet *) obj; + +/* Free memory holding axis accuracies. */ + this->acc = astFree( this->acc ); + +/* Free any pointer array and associated coordinate values array, */ + this->ptr = (double **) astFree( (void *) this->ptr ); + this->values = (double *) astFree( (void *) this->values ); + +/* Clear the remaining PointSet variables. */ + this->npoint = 0; + this->ncoord = 0; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for PointSet objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the PointSet class to an output Channel. + +* Parameters: +* this +* Pointer to the PointSet whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. + +* Notes: +* - It is not recommended that PointSets containing large numbers +* of points be written out, as the coordinate data will be +* formatted as text and this will not be very efficient. +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstPointSet *this; /* Pointer to the PointSet structure */ + char key[ KEY_LEN + 1 ]; /* Buffer for keywords */ + int coord; /* Loop counter for coordinates */ + int i; /* Counter for coordinate values */ + int ival; /* Integer value */ + int makeComment; /* Include a comment? */ + int point; /* Loop counter for points */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PointSet structure. */ + this = (AstPointSet *) this_object; + +/* Write out values representing the instance variables for the + PointSet class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Npoint. */ +/* ------- */ + astWriteInt( channel, "Npoint", 1, 1, this->npoint, + "Number of points" ); + +/* Ncoord. */ +/* ------- */ + astWriteInt( channel, "Ncoord", 1, 1, this->ncoord, + "Number of coordinates per point" ); + +/* Axis Acuracies. */ +/* --------------- */ + for ( coord = 0; coord < this->ncoord; coord++ ) { + if( astTestPointAccuracy( this, coord ) ) { + (void) sprintf( key, "Acc%d", coord + 1 ); + astWriteDouble( channel, key, 1, 1, astGetPointAccuracy( this, coord ), + (coord == 0 ) ? "Axis accuracies..." : "" ); + } + } + +/* Coordinate data. */ +/* ---------------- */ +/* Write an "Empty" value to indicate whether or not the PointSet + contains data. */ + ival = ( this->ptr == NULL ); + set = ( ival != 0 ); + astWriteInt( channel, "Empty", set, 0, ival, + ival ? "PointSet is empty" : + "PointSet contains data" ); + +/* If it contains data, create a suitable keyword for each coordinate + value in turn. */ + if ( this->ptr ) { + makeComment = 1; + i = 0; + for ( point = 0; point < this->npoint; point++ ) { + for ( coord = 0; coord < this->ncoord; coord++ ) { + i++; + (void) sprintf( key, "X%d", i ); + +/* Write the value out if good. Only supply a comment for the first good value. */ + if( this->ptr[ coord ][ point ] != AST__BAD ) { + astWriteDouble( channel, key, 1, 1, this->ptr[ coord ][ point ], + ( makeComment ) ? "Coordinate values..." : "" ); + makeComment = 0; + } + } + } + } + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPointSet and astCheckPointSet functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(PointSet,Object) +astMAKE_CHECK(PointSet) + +AstPointSet *astPointSet_( int npoint, int ncoord, const char *options, int *status, ...) { +/* +*+ +* Name: +* astPointSet + +* Purpose: +* Create a PointSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointset.h" +* AstPointSet *astPointSet( int npoint, int ncoord, +* const char *options, ..., int *status ) + +* Class Membership: +* PointSet constructor. + +* Description: +* This function creates a new PointSet and optionally initialises its +* attributes. + +* Parameters: +* npoint +* The number of points to be stored in the PointSet (must be at +* least 1). +* ncoord +* The number of coordinate values associated with each point +* (must be at least 1). +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new PointSet. The syntax used is the same as +* for the astSet method and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of arguments may follow it in order to +* supply values to be substituted for these specifiers. The +* rules for supplying these are identical to those for the +* astSet method (and for the C "printf" function). + +* Returned Value: +* A pointer to the new PointSet. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPointSet *new; /* Pointer to new PointSet */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the PointSet, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPointSet( NULL, sizeof( AstPointSet ), !class_init, + &class_vtab, "PointSet", npoint, ncoord ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + PointSet's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new PointSet. */ + return new; +} + +AstPointSet *astPointSetId_( int npoint, int ncoord, + const char *options, int *status, ...) { +/* +* Name: +* astPointSetId_ + +* Purpose: +* Create a PointSet. + +* Type: +* Private function. + +* Synopsis: +* #include "pointset.h" +* AstPointSet *astPointSetId_( int npoint, int ncoord, +* const char *options, ... ) + +* Class Membership: +* PointSet constructor. + +* Description: +* This function implements the external (public) interface to the +* astPointSet constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astPointSet_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* +* The variable argument list also prevents this function from +* invoking astPointSet_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. + +* Parameters: +* As for astPointSet_. + +* Returned Value: +* The ID value associated with the new PointSet. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPointSet *new; /* Pointer to new PointSet */ + va_list args; /* Variable argument list */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise the PointSet, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPointSet( NULL, sizeof( AstPointSet ), !class_init, + &class_vtab, "PointSet", npoint, ncoord ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + PointSet's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new PointSet. */ + return astMakeId( new ); +} + +AstPointSet *astInitPointSet_( void *mem, size_t size, int init, + AstPointSetVtab *vtab, const char *name, + int npoint, int ncoord, int *status ) { +/* +*+ +* Name: +* astInitPointSet + +* Purpose: +* Initialise a PointSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointset.h" +* AstPointSet *astInitPointSet( void *mem, size_t size, int init, +* AstPointSetVtab *vtab, const char *name, +* int npoint, int ncoord ) + +* Class Membership: +* PointSet initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new PointSet object. It allocates memory (if necessary) to accommodate +* the PointSet plus any additional data associated with the derived class. +* It then initialises a PointSet structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a PointSet at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the PointSet is to be created. This +* must be of sufficient size to accommodate the PointSet data +* (sizeof(PointSet)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the PointSet (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the PointSet +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the PointSet's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new PointSet. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* npoint +* The number of points in the PointSet (must be at least 1). +* ncoord +* The number of coordinate values associated with each point (must be +* at least 1). + +* Returned Value: +* A pointer to the new PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPointSet *new; /* Pointer to new PointSet */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPointSetVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the initialisation values for validity, reporting an error if + necessary. */ + if ( npoint < 1 ) { + astError( AST__NPTIN, "astInitPointSet(%s): Number of points (%d) is " + "not valid.", status, name, npoint ); + } else if ( ncoord < 1 ) { + astError( AST__NCOIN, "astInitPointSet(%s): Number of coordinates per " + "point (%d) is not valid.", status, name, ncoord ); + } + +/* Initialise an Object structure (the parent class) as the first component + within the PointSet structure, allocating memory if necessary. */ + new = (AstPointSet *) astInitObject( mem, size, 0, + (AstObjectVtab *) vtab, name ); + + if ( astOK ) { + +/* Initialise the PointSet data. */ +/* ----------------------------- */ +/* Store the number of points and number of coordinate values per point. */ + new->npoint = npoint; + new->ncoord = ncoord; + +/* Initialise pointers to the pointer array and associated coordinate + values array. */ + new->ptr = NULL; + new->values = NULL; + new->acc = NULL; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstPointSet *astLoadPointSet_( void *mem, size_t size, + AstPointSetVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPointSet + +* Purpose: +* Load a PointSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "pointset.h" +* AstPointSet *astLoadPointSet( void *mem, size_t size, +* AstPointSetVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* PointSet loader. + +* Description: +* This function is provided to load a new PointSet using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* PointSet structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the PointSet is to be +* loaded. This must be of sufficient size to accommodate the +* PointSet data (sizeof(PointSet)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the PointSet (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the PointSet structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPointSet) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new PointSet. If this is NULL, a pointer +* to the (static) virtual function table for the PointSet class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "PointSet" is used instead. + +* Returned Value: +* A pointer to the new PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstPointSet *new; /* Pointer to the new PointSet */ + char key[ KEY_LEN + 1 ]; /* Buffer for keywords */ + double acc; /* Accuracy value */ + int coord; /* Loop counter for coordinates */ + int empty; /* PointSet empty? */ + int i; /* Counter for coordinate values */ + int point; /* Loop counter for points */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this PointSet. In this case the + PointSet belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPointSet ); + vtab = &class_vtab; + name = "PointSet"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPointSetVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built PointSet. */ + new = astLoadObject( mem, size, (AstObjectVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Initialise the PointSet's data pointers. */ + new->ptr = NULL; + new->values = NULL; + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "PointSet" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Npoint. */ +/* ------- */ + new->npoint = astReadInt( channel, "npoint", 1 ); + if ( new->npoint < 1 ) new->npoint = 1; + +/* Ncoord. */ +/* ------- */ + new->ncoord = astReadInt( channel, "ncoord", 1 ); + if ( new->ncoord < 1 ) new->ncoord = 1; + +/* Axis Acuracies. */ +/* --------------- */ + new->acc = NULL; + for ( coord = 0; coord < new->ncoord; coord++ ) { + (void) sprintf( key, "acc%d", coord + 1 ); + acc = astReadDouble( channel, key, AST__BAD ); + if( !new->acc && acc != AST__BAD ) { + new->acc = astMalloc( sizeof( double )*(size_t) new->ncoord ); + if( new->acc ) { + for( i = 0; i < coord - 1; i++ ) new->acc[ i ] = AST__BAD; + } + } + if( new->acc ) new->acc[ coord ] = acc; + } + +/* Coordinate data. */ +/* ---------------- */ +/* Read a value for the "Empty" keyword to see whether the PointSet + contains data. */ + empty = astReadInt( channel, "empty", 0 ); + +/* If it does, allocate memory to hold the coordinate data and + pointers. */ + if ( astOK && !empty ) { + new->ptr = astMalloc( sizeof( double * ) * (size_t) new->ncoord ); + new->values = astMalloc( sizeof( double ) * + (size_t) ( new->npoint * new->ncoord ) ); + if ( astOK ) { + +/* Initialise the array of pointers into the main data array. */ + for ( coord = 0; coord < new->ncoord; coord++ ) { + new->ptr[ coord ] = new->values + ( coord * new->npoint ); + } + +/* Create a keyword for each coordinate value to be read. */ + i = 0; + for ( point = 0; point < new->npoint; point++ ) { + for ( coord = 0; coord < new->ncoord; coord++ ) { + i++; + (void) sprintf( key, "x%d", i ); + +/* Read and assign the values. */ + new->ptr[ coord ][ point ] = + astReadDouble( channel, key, AST__BAD ); + } + } + } + +/* If an error occurred, clean up by freeing the memory allocated + above, thus emptying the PointSet. */ + if ( !astOK ) { + new->ptr = astFree( new->ptr ); + new->values = astFree( new->values ); + } + } + +/* If an error occurred, clean up by deleting the new PointSet. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new PointSet pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +int astGetNpoint_( const AstPointSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,PointSet,GetNpoint))( this, status ); +} +int astGetNcoord_( const AstPointSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,PointSet,GetNcoord))( this, status ); +} +double **astGetPoints_( AstPointSet *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,PointSet,GetPoints))( this, status ); +} +void astPermPoints_( AstPointSet *this, int forward, const int perm[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,PointSet,PermPoints))( this, forward, perm, status ); +} +void astSetPoints_( AstPointSet *this, double **ptr, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,PointSet,SetPoints))( this, ptr, status ); +} +void astSetNpoint_( AstPointSet *this, int npoint, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,PointSet,SetNpoint))( this, npoint, status ); +} +void astSetSubPoints_( AstPointSet *point1, int point, int coord, + AstPointSet *point2, int *status ) { + if ( !astOK ) return; + (**astMEMBER(point1,PointSet,SetSubPoints))( point1, point, coord, point2, status ); +} +AstPointSet *astAppendPoints_( AstPointSet *this, AstPointSet *that, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,PointSet,AppendPoints))( this, that, status ); +} +void astBndPoints_( AstPointSet *this, double *lbnd, double *ubnd, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,PointSet,BndPoints))( this, lbnd, ubnd, status ); +} + +int astReplaceNaN_( AstPointSet *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,PointSet,ReplaceNaN))( this, status ); +} + + + + + diff --git a/pointset.h b/pointset.h new file mode 100644 index 0000000..65eaec4 --- /dev/null +++ b/pointset.h @@ -0,0 +1,707 @@ +#if !defined( POINTSET_INCLUDED ) /* Include this file only once */ +#define POINTSET_INCLUDED +/* +*+ +* Name: +* pointset.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the PointSet class. + +* Invocation: +* #include "pointset.h" + +* Description: +* This include file defines the interface to the PointSet class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. +* +* The PointSet class encapsulates sets of coordinate values +* representing points in an N-dimensional space, to which +* coordinate transformations may be applied. It also provides +* memory allocation facilities for coordinate values. + +* Inheritance: +* The PointSet class inherits from the Object class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* Ncoord (integer) +* A read-only attribute that gives the number of coordinates +* for each point in a PointSet (i.e. the number of dimensions +* of the space in which the points reside). This value is +* determined when the PointSet is created. +* Npoint (integer) +* A read-only attribute that gives the number of points that +* can be stored in the PointSet. This value is determined when +* the PointSet is created. +* PointAccuracy (floating point) +* This stores the absolute accuracies for each axis in the PointSet. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* ClearAttrib +* Clear an attribute value for a PointSet. +* GetAttrib +* Get an attribute value for a PointSet. +* SetAttrib +* Set an attribute value for a PointSet. +* TestAttrib +* Test if an attribute value has been set for a PointSet. + +* New Methods Defined: +* Public: +* astAppendPoints +* Append one PointSet to another. +* astBndPoints +* Find the axis bounds of the points in a PointSet. +* astGetPoints +* Get a pointer to the coordinate values associated with a PointSet. +* astPermPoints +* Permute coordinates within a PointSet. +* astSetPoints +* Associate coordinate values with a PointSet. +* astSetNpoint +* Reduce the size of a PointSet. +* astSetSubPoints +* Associate one PointSet with a subset of another. +* +* Protected: +* astGetNpoint +* Get the number of points in a PointSet. +* astGetNcoord +* Get the number of coordinate values per point from a PointSet. +* astGetPointAccuracy +* Get the curent value of the PointAcuracy attribute for an axis. +* astSetPointAccuracy +* Set a new value for the PointAcuracy attribute for an axis. +* astTestPointAccuracy +* Test the value of the PointAcuracy attribute for an axis. +* astClearPointAccuracy +* Clear the value of the PointAcuracy attribute for an axis. + +* Other Class Functions: +* Public: +* astIsAPointSet +* Test class membership. +* astPointSet +* Create a PointSet. +* +* Protected: +* astCheckPointSet +* Validate class membership. +* astInitPointSet +* Initialise a PointSet. +* astInitPointSetVtab +* Initialise the virtual function table for the PointSet class. +* astLoadPointSet +* Load a PointSet. + +* Macros: +* Public: +* AST__BAD +* Bad value flag for coordinate data. +* +* Protected: +* astISBAD +* Check if a value is AST__BAD or NaN. +* astISGOOD +* Check if a value is not AST__BAD or NaN. +* astISNAN +* Check if a value is NaN. + +* Type Definitions: +* Public: +* AstPointSet +* PointSet object type. +* +* Protected: +* AstPointSetVtab +* PointSet virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 30-JAN-1996 (RFWS): +* Original version. +* 27-SEP-1996 (RFWS): +* Added external interface and I/O facilities. +* 8-JAN-2003 (DSB): +* Added protected astInitPointSetVtab method. +* 2-NOV-2004 (DSB): +* Added PointAccuracy attribute. +*- +*/ + +/* Include files. */ +/* ============== */ + +/* Configuration results. */ +/* ---------------------- */ +#if HAVE_CONFIG_H +#include +#endif + +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ + +/* C header files. */ +/* --------------- */ +#include +#if defined(astCLASS) /* Protected */ +#include +#include + +#if !HAVE_DECL_ISNAN +# if HAVE_ISNAN + /* Seems that math.h does not include a prototype for isnan etc */ + int isnan( double ); +# else + /* isnan is not available prior to C99 so define + alternative macros Note multiple evaluations of "x" in these + macros!!! */ +# define isnan(x) ((x) != (x)) +# endif +#endif + +#if !HAVE_DECL_ISFINITE +# if HAVE_ISFINITE + /* Seems that math.h does not include a prototype for isfinite */ + int isfinite( double ); +# else + /* isfinite is not available prior to C99 so define + alternative macros. Note multiple evaluations of "x" in these + macros!!! */ +# define isfinite(x) (!isnan(x) && ((x) != (1.0/0.0)) && ((x) != (-1.0/0.0))) +# endif +#endif +#endif + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* +*+ +* Name: +* AST__BAD + +* Type: +* Public macro. + +* Purpose: +* Bad value flag for coordinate data. + +* Synopsis: +* #include "pointset.h" +* const double AST__BAD + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to a const double value that is used to flag +* coordinate values that are "bad" (i.e. undefined or +* meaningless). Classes that implement coordinate transformations +* should test coordinate values against this value, and +* appropriately propagate bad values to their output. +*- +*/ + +/* Define AST__BAD to be the most negative (normalised) double + value. */ + +#define AST__BAD (-(DBL_MAX)) + +/* +*+ +* Name: +* AST__NAN + +* Type: +* Public macro. + +* Purpose: +* A value representing the double precision IEEE NaN value. + +* Synopsis: +* #include "pointset.h" +* const double AST__NAN + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to a const double value that is used to indicate +* that a IEEE NaN value should be used. Note, AST__NAN itself is a finite +* double precision floating point value a little below the maximum +* allowed value for a double. This value can be used as flag to +* indicate that the corresponding IEEE NaN value should be used in its +* place. + +*- +*/ +#define AST__NAN (-(0.95*DBL_MAX)) + +/* +*+ +* Name: +* AST__NANF + +* Type: +* Public macro. + +* Purpose: +* A value representing the single precision IEEE NaN value. + +* Synopsis: +* #include "pointset.h" +* const double AST__NANF + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to a const float value that is used to indicate +* that a IEEE NaN value should be used. Note, AST__NANF itself is a finite +* single precision floating point value a little below the maximum +* allowed value for a float. This value can be used as flag to +* indicate that the corresponding IEEE NaN value should be used in its +* place. + +*- +*/ +#define AST__NANF ((float)-(0.95*FLT_MAX)) + +#if defined(astCLASS) /* Protected */ + +/* +*+ +* Name: +* astISNAN + +* Type: +* Protected macro. + +* Purpose: +* Test if a double is NaN. + +* Synopsis: +* #include "pointset.h" +* astISNAN(value) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to a integer valued expression which is zero +* if and only if the supplied value equals NaN ("Not a Number"). + +* Parameters: +* value +* The value to be tested. This should be a double. + +* Examples: +* if( astISNAN(x) ) x = AST__BAD; +* If "x" is NaN replace it with AST__BAD. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +* - On some system it is possible that the supplied macro argument +* "x" may be evaluated multiple times. Therefore the evaluation of "x" +* should have no side effects. +*- +*/ + +#define astISNAN(value) isnan(value) + +/* +*+ +* Name: +* astISFINITE + +* Type: +* Protected macro. + +* Purpose: +* Test if a double is neither NaN nor Inf. + +* Synopsis: +* #include "pointset.h" +* astISFINITE(value) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to a integer valued expression which is zero +* if and only if the supplied value equals NaN ("Not a Number") or Inf. + +* Parameters: +* value +* The value to be tested. This should be a double. + +* Examples: +* if( !astISFINITE(x) ) x = AST__BAD; +* If "x" is NaN or Inf replace it with AST__BAD. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +* - On some system it is possible that the supplied macro argument +* "x" may be evaluated multiple times. Therefore the evaluation of "x" +* should have no side effects. +*- +*/ + +#define astISFINITE(value) isfinite(value) + +/* +*+ +* Name: +* astISGOOD + +* Type: +* Protected macro. + +* Purpose: +* Test if a double is neither AST__BAD, NaN or Inf. + +* Synopsis: +* #include "pointset.h" +* astISGOOD(value) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to a integer valued expression which is zero +* if and only if the supplied value equals AST__BAD or is NaN ("Not a +* Number") or "Inf". + +* Parameters: +* value +* The value to be tested. This should be a double. + +* Examples: +* if( astISGOOD(x) ) y = x; +* Checks that "x" is usable before assigning it to y. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +* - On some system it is possible that the supplied macro argument +* "x" may be evaluated multiple times. Therefore the evaluation of "x" +* should have no side effects. +*- +*/ + +#define astISGOOD(value) ( (value) != AST__BAD && astISFINITE(value) ) + + +/* +*+ +* Name: +* astISBAD + +* Type: +* Protected macro. + +* Purpose: +* Test if a double is either AST__BAD, NaN, or Inf. + +* Synopsis: +* #include "pointset.h" +* astISBAD(value) + +* Class Membership: +* Defined by the PointSet class. + +* Description: +* This macro expands to a integer valued expression which is non-zero +* if and only if the supplied value equals AST__BAD or is NaN ("Not a +* Number"), or is Inf. + +* Parameters: +* value +* The value to be tested. This should be a double. + +* Examples: +* if( astISBAD(x) ) astError( ... ); +* Reports an error if "x" is bad. + +* Notes: +* - To avoid problems with some compilers, you should not leave +* any white space around the macro arguments. +* - On some system it is possible that the supplied macro argument +* "x" may be evaluated multiple times. Therefore the evaluation of "x" +* should have no side effects. +*- +*/ + +#define astISBAD(value) ( (value) == AST__BAD || !astISFINITE(value)) + +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* PointSet structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstPointSet { + +/* Attributes inherited from the parent class. */ + AstObject object; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double **ptr; /* Pointer to array of pointers to values */ + double *values; /* Pointer to array of coordinate values */ + int ncoord; /* Number of coordinate values per point */ + int npoint; /* Number of points */ + double *acc; /* Axis accuracies */ +} AstPointSet; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPointSetVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstObjectVtab object_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstPointSet *(* AppendPoints)( AstPointSet *, AstPointSet *, int * ); + double **(* GetPoints)( AstPointSet *, int * ); + int (* GetNcoord)( const AstPointSet *, int * ); + int (* GetNpoint)( const AstPointSet *, int * ); + void (* BndPoints)( AstPointSet *, double *, double *, int * ); + void (* PermPoints)( AstPointSet *, int, const int[], int * ); + void (* SetNpoint)( AstPointSet *, int, int * ); + void (* SetPoints)( AstPointSet *, double **, int * ); + void (* SetSubPoints)( AstPointSet *, int, int, AstPointSet *, int * ); + int (* ReplaceNaN)( AstPointSet *, int * ); + + double (* GetPointAccuracy)( AstPointSet *, int, int * ); + int (* TestPointAccuracy)( AstPointSet *, int, int * ); + void (* ClearPointAccuracy)( AstPointSet *, int, int * ); + void (* SetPointAccuracy)( AstPointSet *, int, double, int * ); + +} AstPointSetVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstPointSetGlobals { + AstPointSetVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstPointSetGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(PointSet) /* Check class membership */ +astPROTO_ISA(PointSet) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPointSet *astPointSet_( int, int, const char *, int *, ...); +#else +AstPointSet *astPointSetId_( int, int, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPointSet *astInitPointSet_( void *, size_t, int, AstPointSetVtab *, + const char *, int, int, int * ); + +/* Vtab initialiser. */ +void astInitPointSetVtab_( AstPointSetVtab *, const char *, int * ); + +/* Loader. */ +AstPointSet *astLoadPointSet_( void *, size_t, AstPointSetVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitPointSetGlobals_( AstPointSetGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +double **astGetPoints_( AstPointSet *, int * ); +void astPermPoints_( AstPointSet *, int, const int[], int * ); +void astSetPoints_( AstPointSet *, double **, int * ); +void astSetNpoint_( AstPointSet *, int, int * ); +void astSetSubPoints_( AstPointSet *, int, int, AstPointSet *, int * ); +AstPointSet *astAppendPoints_( AstPointSet *, AstPointSet *, int * ); +void astBndPoints_( AstPointSet *, double *, double *, int * ); +int astReplaceNaN_( AstPointSet *, int * ); + +# if defined(astCLASS) /* Protected */ +int astGetNcoord_( const AstPointSet *, int * ); +int astGetNpoint_( const AstPointSet *, int * ); + +double astGetPointAccuracy_( AstPointSet *, int, int * ); +int astTestPointAccuracy_( AstPointSet *, int, int * ); +void astClearPointAccuracy_( AstPointSet *, int, int * ); +void astSetPointAccuracy_( AstPointSet *, int, double, int * ); + +double astCheckNaN_( double ); +float astCheckNaNF_( float ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPointSet(this) astINVOKE_CHECK(PointSet,this,0) +#define astVerifyPointSet(this) astINVOKE_CHECK(PointSet,this,1) + +/* Test class membership. */ +#define astIsAPointSet(this) astINVOKE_ISA(PointSet,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astPointSet astINVOKE(F,astPointSet_) +#else +#define astPointSet astINVOKE(F,astPointSetId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPointSet(mem,size,init,vtab,name,npoint,ncoord) \ +astINVOKE(O,astInitPointSet_(mem,size,init,vtab,name,npoint,ncoord,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPointSetVtab(vtab,name) astINVOKE(V,astInitPointSetVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadPointSet(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPointSet_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckPointSet to validate PointSet pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astGetPoints(this) \ +astINVOKE(V,astGetPoints_(astCheckPointSet(this),STATUS_PTR)) +#define astPermPoints(this,forward,perm) \ +astINVOKE(V,astPermPoints_(astCheckPointSet(this),forward,perm,STATUS_PTR)) +#define astSetPoints(this,ptr) \ +astINVOKE(V,astSetPoints_(astCheckPointSet(this),ptr,STATUS_PTR)) +#define astSetNpoint(this,np) \ +astINVOKE(V,astSetNpoint_(astCheckPointSet(this),np,STATUS_PTR)) +#define astSetSubPoints(point1,point,coord,point2) \ +astINVOKE(V,astSetSubPoints_(astCheckPointSet(point1),point,coord,astCheckPointSet(point2),STATUS_PTR)) +#define astAppendPoints(this,that) \ +astINVOKE(O,astAppendPoints_(astCheckPointSet(this),astCheckPointSet(that),STATUS_PTR)) +#define astBndPoints(this,lbnd,ubnd) \ +astINVOKE(V,astBndPoints_(astCheckPointSet(this),lbnd,ubnd,STATUS_PTR)) +#define astReplaceNaN(this) \ +astINVOKE(V,astReplaceNaN_(astCheckPointSet(this),STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astGetNpoint(this) \ +astINVOKE(V,astGetNpoint_(astCheckPointSet(this),STATUS_PTR)) +#define astGetNcoord(this) \ +astINVOKE(V,astGetNcoord_(astCheckPointSet(this),STATUS_PTR)) + +#define astClearPointAccuracy(this,axis) \ +astINVOKE(V,astClearPointAccuracy_(astCheckPointSet(this),axis,STATUS_PTR)) +#define astGetPointAccuracy(this,axis) \ +astINVOKE(V,astGetPointAccuracy_(astCheckPointSet(this),axis,STATUS_PTR)) +#define astSetPointAccuracy(this,axis,value) \ +astINVOKE(V,astSetPointAccuracy_(astCheckPointSet(this),axis,value,STATUS_PTR)) +#define astTestPointAccuracy(this,axis) \ +astINVOKE(V,astTestPointAccuracy_(astCheckPointSet(this),axis,STATUS_PTR)) + +#define astCheckNaNF(value) astCheckNaNF_(value) +#define astCheckNaN(value) astCheckNaN_(value) + + +#endif +#endif + + + + + diff --git a/polygon.c b/polygon.c new file mode 100644 index 0000000..1dfae39 --- /dev/null +++ b/polygon.c @@ -0,0 +1,7087 @@ +/* +*class++ +* Name: +* Polygon + +* Purpose: +* A polygonal region within a 2-dimensional Frame. + +* Constructor Function: +c astPolygon +f AST_POLYGON + +* Description: +* The Polygon class implements a polygonal area, defined by a +* collection of vertices, within a 2-dimensional Frame. The vertices +* are connected together by geodesic curves within the encapsulated Frame. +* For instance, if the encapsulated Frame is a simple Frame then the +* geodesics will be straight lines, but if the Frame is a SkyFrame then +* the geodesics will be great circles. Note, the vertices must be +* supplied in an order such that the inside of the polygon is to the +* left of the boundary as the vertices are traversed. Supplying them +* in the reverse order will effectively negate the polygon. +* +* Within a SkyFrame, neighbouring vertices are always joined using the +* shortest path. Thus if an edge of 180 degrees or more in length is +* required, it should be split into section each of which is less +* than 180 degrees. The closed path joining all the vertices in order +* will divide the celestial sphere into two disjoint regions. The +* inside of the polygon is the region which is circled in an +* anti-clockwise manner (when viewed from the inside of the celestial +* sphere) when moving through the list of vertices in the order in +* which they were supplied when the Polygon was created (i.e. the +* inside is to the left of the boundary when moving through the +* vertices in the order supplied). + +* Inheritance: +* The Polygon class inherits from the Region class. + +* Attributes: +* In addition to those attributes common to all Regions, every +* Polygon also has the following attributes: +* - SimpVertices: Simplify by transforming the vertices? + +* Functions: +c In addition to those functions applicable to all Regions, the +c following functions may also be applied to all Polygons: +f In addition to those routines applicable to all Regions, the +f following routines may also be applied to all Polygons: +* +c - astDownsize: Reduce the number of vertices in a Polygon. +f - AST_DOWNSIZE: Reduce the number of vertices in a Polygon. +c - astConvex: Create a Polygon giving the convex hull of a pixel array +f - AST_CONVEX: Create a Polygon giving the convex hull of a pixel array +c - astOutline: Create a Polygon outlining values in a pixel array +f - AST_OUTLINE: Create a Polygon outlining values in a pixel array + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-OCT-2004 (DSB): +* Original version. +* 28-MAY-2009 (DSB): +* Added astDownsize. +* 29-MAY-2009 (DSB): +* Added astOutline. +* 30-JUN-2009 (DSB): +* Override astGetBounded. +* 4-NOV-2013 (DSB): +* Modify RegPins so that it can handle uncertainty regions that straddle +* a discontinuity. Previously, such uncertainty Regions could have a huge +* bounding box resulting in matching region being far too big. +* 6-DEC-2013 (DSB): +* Reverse the order of the vertices when the Polygon is created, +* if necessary, to ensure that the unnegated Polygon is bounded. +* The parent Region class assumes that unnegated regions are +* bounded. +* 6-JAN-2014 (DSB): +* Free edges when clearing the cache, not when establishing a new +* cache, as the number of edges may have changed. +* 10-JAN-2014 (DSB): +* - Remove unused parameter description in prologue of for astOutline +* 24-FEB-2014 (DSB): +* Added astConvex. +* 25-FEB-2014 (DSB): +* Added attribute SimpVertices. +* 7-SEP-2015 (DSB): +* Shrink outline polygons by a small fraction of a pixel, in order +* to avoid placing vertices exactly on pixel edges. This is because +* rounding errors in subsequent code may push the vertices into +* neighbouring pixels, which may have bad WCS coords (e.g. +* vertices on the boundary of a polar cusp in an HPX map). +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Polygon + +/* Define a macro for testing if a pixel value satisfies the requirements + specified by and . Compiler optimisation should remove + all the "if" testing from this expression. */ +#define ISVALID(V,OperI,Value) ( \ + ( OperI == AST__LT ) ? ( (V) < Value ) : ( \ + ( OperI == AST__LE ) ? ( (V) <= Value ) : ( \ + ( OperI == AST__EQ ) ? ( (V) == Value ) : ( \ + ( OperI == AST__GE ) ? ( (V) >= Value ) : ( \ + ( OperI == AST__NE ) ? ( (V) != Value ) : ( \ + (V) > Value \ + ) \ + ) \ + ) \ + ) \ + ) \ +) + +/* Macros specifying whether a point is inside, outside or on the + boundary of the polygon. */ +#define UNKNOWN 0 +#define IN 1 +#define OUT 2 +#define ON 3 + +/* Size of pertubation (in pixels) used to avoid placing vertices exactly + on a pixel edge. */ +#define DELTA 0.01 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "box.h" /* Box Regions */ +#include "wcsmap.h" /* Definitons of AST__DPI etc */ +#include "polygon.h" /* Interface definition for this class */ +#include "mapping.h" /* Position mappings */ +#include "unitmap.h" /* Unit Mapping */ +#include "pal.h" /* SLALIB library interface */ +#include "frame.h" /* Coordinate system description */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Type definitions. */ +/* ================= */ + +/* A structure that holds information about an edge of the new Polygon + being created by astDownsize. The edge is a line betweeen two of the + vertices of the original Polygon. */ +typedef struct Segment { + int i1; /* Index of starting vertex within old Polygon */ + int i2; /* Index of ending vertex within old Polygon */ + double error; /* Max geodesic distance from any old vertex to the line */ + int imax; /* Index of the old vertex at which max error is reached */ + struct Segment *next;/* Pointer to next Segment in a double link list */ + struct Segment *prev;/* Pointer to previous Segment in a double link list */ +} Segment; + + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static void (* parent_resetcache)( AstRegion *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Polygon) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Polygon,Class_Init) +#define class_vtab astGLOBAL(Polygon,Class_Vtab) +#define getattrib_buff astGLOBAL(Polygon,GetAttrib_Buff) + + +#include + + +#else + +static char getattrib_buff[ 51 ]; + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPolygonVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPolygon *astPolygonId_( void *, int, int, const double *, void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ + +/* Define a macro that expands to a single prototype for function + FindInsidePoint for a given data type and operation. */ +#define FINDINSIDEPOINT_PROTO0(X,Xtype,Oper) \ +static void FindInsidePoint##Oper##X( Xtype, const Xtype *, const int[2], const int[2], int *, int *, int *, int * ); + +/* Define a macro that expands to a set of prototypes for all operations + for function FindInsidePoint for a given data type. */ +#define FINDINSIDEPOINT_PROTO(X,Xtype) \ +FINDINSIDEPOINT_PROTO0(X,Xtype,LT) \ +FINDINSIDEPOINT_PROTO0(X,Xtype,LE) \ +FINDINSIDEPOINT_PROTO0(X,Xtype,EQ) \ +FINDINSIDEPOINT_PROTO0(X,Xtype,GE) \ +FINDINSIDEPOINT_PROTO0(X,Xtype,GT) \ +FINDINSIDEPOINT_PROTO0(X,Xtype,NE) + +/* Use the above macros to define all FindInsidePoint prototypes for all + data types and operations. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +FINDINSIDEPOINT_PROTO(LD,long double) +#endif +FINDINSIDEPOINT_PROTO(D,double) +FINDINSIDEPOINT_PROTO(L,long int) +FINDINSIDEPOINT_PROTO(UL,unsigned long int) +FINDINSIDEPOINT_PROTO(I,int) +FINDINSIDEPOINT_PROTO(UI,unsigned int) +FINDINSIDEPOINT_PROTO(S,short int) +FINDINSIDEPOINT_PROTO(US,unsigned short int) +FINDINSIDEPOINT_PROTO(B,signed char) +FINDINSIDEPOINT_PROTO(UB,unsigned char) +FINDINSIDEPOINT_PROTO(F,float) + +/* Define a macro that expands to a single prototype for function + TraceEdge for a given data type and operation. */ +#define TRACEEDGE_PROTO0(X,Xtype,Oper) \ +static AstPointSet *TraceEdge##Oper##X( Xtype, const Xtype *, const int[2], const int[2], int, int, int, int, int, int * ); + +/* Define a macro that expands to a set of prototypes for all operations + for function TraceEdge for a given data type. */ +#define TRACEEDGE_PROTO(X,Xtype) \ +TRACEEDGE_PROTO0(X,Xtype,LT) \ +TRACEEDGE_PROTO0(X,Xtype,LE) \ +TRACEEDGE_PROTO0(X,Xtype,EQ) \ +TRACEEDGE_PROTO0(X,Xtype,GE) \ +TRACEEDGE_PROTO0(X,Xtype,GT) \ +TRACEEDGE_PROTO0(X,Xtype,NE) + +/* Use the above macros to define all TraceEdge prototypes for all + data types and operations. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +TRACEEDGE_PROTO(LD,long double) +#endif +TRACEEDGE_PROTO(D,double) +TRACEEDGE_PROTO(L,long int) +TRACEEDGE_PROTO(UL,unsigned long int) +TRACEEDGE_PROTO(I,int) +TRACEEDGE_PROTO(UI,unsigned int) +TRACEEDGE_PROTO(S,short int) +TRACEEDGE_PROTO(US,unsigned short int) +TRACEEDGE_PROTO(B,signed char) +TRACEEDGE_PROTO(UB,unsigned char) +TRACEEDGE_PROTO(F,float) + +/* Define a macro that expands to a single prototype for function + PartHull for a given data type and operation. */ +#define PARTHULL_PROTO0(X,Xtype,Oper) \ +static void PartHull##Oper##X( Xtype, const Xtype[], int, int, int, int, int, int, int, const int[2], double **, double **, int *, int * ); + +/* Define a macro that expands to a set of prototypes for all operations + for function PartHull for a given data type. */ +#define PARTHULL_PROTO(X,Xtype) \ +PARTHULL_PROTO0(X,Xtype,LT) \ +PARTHULL_PROTO0(X,Xtype,LE) \ +PARTHULL_PROTO0(X,Xtype,EQ) \ +PARTHULL_PROTO0(X,Xtype,GE) \ +PARTHULL_PROTO0(X,Xtype,GT) \ +PARTHULL_PROTO0(X,Xtype,NE) + +/* Use the above macros to define all PartHull prototypes for all + data types and operations. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +PARTHULL_PROTO(LD,long double) +#endif +PARTHULL_PROTO(D,double) +PARTHULL_PROTO(L,long int) +PARTHULL_PROTO(UL,unsigned long int) +PARTHULL_PROTO(I,int) +PARTHULL_PROTO(UI,unsigned int) +PARTHULL_PROTO(S,short int) +PARTHULL_PROTO(US,unsigned short int) +PARTHULL_PROTO(B,signed char) +PARTHULL_PROTO(UB,unsigned char) +PARTHULL_PROTO(F,float) + +/* Define a macro that expands to a single prototype for function + ConvexHull for a given data type and operation. */ +#define CONVEXHULL_PROTO0(X,Xtype,Oper) \ +static AstPointSet *ConvexHull##Oper##X( Xtype, const Xtype[], const int[2], int, int, int, int * ); + +/* Define a macro that expands to a set of prototypes for all operations + for function ConvexHull for a given data type. */ +#define CONVEXHULL_PROTO(X,Xtype) \ +CONVEXHULL_PROTO0(X,Xtype,LT) \ +CONVEXHULL_PROTO0(X,Xtype,LE) \ +CONVEXHULL_PROTO0(X,Xtype,EQ) \ +CONVEXHULL_PROTO0(X,Xtype,GE) \ +CONVEXHULL_PROTO0(X,Xtype,GT) \ +CONVEXHULL_PROTO0(X,Xtype,NE) + +/* Use the above macros to define all ConvexHull prototypes for all + data types and operations. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +CONVEXHULL_PROTO(LD,long double) +#endif +CONVEXHULL_PROTO(D,double) +CONVEXHULL_PROTO(L,long int) +CONVEXHULL_PROTO(UL,unsigned long int) +CONVEXHULL_PROTO(I,int) +CONVEXHULL_PROTO(UI,unsigned int) +CONVEXHULL_PROTO(S,short int) +CONVEXHULL_PROTO(US,unsigned short int) +CONVEXHULL_PROTO(B,signed char) +CONVEXHULL_PROTO(UB,unsigned char) +CONVEXHULL_PROTO(F,float) + +/* Define a macro that expands to a single prototype for function + FindBoxEdge for a given data type and operation. */ +#define FINDBOXEDGE_PROTO0(X,Xtype,Oper) \ +static void FindBoxEdge##Oper##X( Xtype, const Xtype[], int, int, int, int, int *, int *, int *, int * ); + +/* Define a macro that expands to a set of prototypes for all operations + for function FindBoxEdge for a given data type. */ +#define FINDBOXEDGE_PROTO(X,Xtype) \ +FINDBOXEDGE_PROTO0(X,Xtype,LT) \ +FINDBOXEDGE_PROTO0(X,Xtype,LE) \ +FINDBOXEDGE_PROTO0(X,Xtype,EQ) \ +FINDBOXEDGE_PROTO0(X,Xtype,GE) \ +FINDBOXEDGE_PROTO0(X,Xtype,GT) \ +FINDBOXEDGE_PROTO0(X,Xtype,NE) + +/* Use the above macros to define all FindBoxEdge prototypes for all + data types and operations. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +FINDBOXEDGE_PROTO(LD,long double) +#endif +FINDBOXEDGE_PROTO(D,double) +FINDBOXEDGE_PROTO(L,long int) +FINDBOXEDGE_PROTO(UL,unsigned long int) +FINDBOXEDGE_PROTO(I,int) +FINDBOXEDGE_PROTO(UI,unsigned int) +FINDBOXEDGE_PROTO(S,short int) +FINDBOXEDGE_PROTO(US,unsigned short int) +FINDBOXEDGE_PROTO(B,signed char) +FINDBOXEDGE_PROTO(UB,unsigned char) +FINDBOXEDGE_PROTO(F,float) + + + + + + + + + +/* Non-generic function prototypes. */ +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *DownsizePoly( AstPointSet *, double, int, AstFrame *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstPolygon *Downsize( AstPolygon *, double, int, int * ); +static Segment *AddToChain( Segment *, Segment *, int * ); +static Segment *NewSegment( Segment *, int, int, int, int * ); +static Segment *RemoveFromChain( Segment *, Segment *, int * ); +static double Polywidth( AstFrame *, AstLineDef **, int, int, double[ 2 ], int * ); +static int GetBounded( AstRegion *, int * ); +static int IntCmp( const void *, const void * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static void Cache( AstPolygon *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void EnsureInside( AstPolygon *, int * ); +static void FindMax( Segment *, AstFrame *, double *, double *, int, int, int * ); +static void RegBaseBox( AstRegion *this, double *, double *, int * ); +static void ResetCache( AstRegion *this, int * ); +static void SetPointSet( AstPolygon *, AstPointSet *, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); +static void SmoothPoly( AstPointSet *, int, double, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); + +static int GetSimpVertices( AstPolygon *, int * ); +static int TestSimpVertices( AstPolygon *, int * ); +static void ClearSimpVertices( AstPolygon *, int * ); +static void SetSimpVertices( AstPolygon *, int, int * ); + + +/* Member functions. */ +/* ================= */ +static Segment *AddToChain( Segment *head, Segment *seg, int *status ){ +/* +* Name: +* AddToChain + +* Purpose: +* Add a Segment into the linked list of Segments, maintaining the +* required order in the list. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* Segment *AddToChain( Segment *head, Segment *seg, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* The linked list of Segments maintained by astDownsize is searched +* from the high error end (the head), until a Segment is foound which +* has a lower error than the supplied segment. The supplied Segment +* is then inserted into the list at that point. + +* Parameters: +* head +* The Segment structure at the head of the list (i.e. the segment +* with maximum error). +* seg +* The Segment to be added into the list. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the link head (which will have changed if "seg" has a +* higher error than the original head). + +*/ + +/* Local Variables: */ + Segment *tseg; + +/* Check the global error status. */ + if ( !astOK ) return head; + +/* If the list is empty, return the supplied segment as the new list + head. */ + if( !head ) { + head = seg; + +/* If the supplied segment has a higher error than the original head, + insert the new segment in front of the original head. */ + } else if( seg->error > head->error ){ + seg->next = head; + head->prev = seg; + head = seg; + +/* Otherwise, move down the list from the head until a segment is found + which has a lower error than the supplied Segment. Then insert the + supplied segment into the list in front of it. */ + } else { + tseg = head; + seg->next = NULL; + + while( tseg->next ) { + if( seg->error > tseg->next->error ) { + seg->next = tseg->next; + seg->prev = tseg; + tseg->next->prev = seg; + tseg->next = seg; + break; + } + tseg = tseg->next; + } + + if( !seg->next ) { + tseg->next = seg; + seg->prev = tseg; + } + } + +/* Return the new head. */ + return head; +} + +static void Cache( AstPolygon *this, int *status ){ +/* +* Name: +* Cache + +* Purpose: +* Calculate intermediate values and cache them in the Polygon structure. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void Cache( AstPolygon *this, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* This function uses the PointSet stored in the parent Region to calculate +* some intermediate values which are useful in other methods. These +* values are stored within the Polygon structure. + +* Parameters: +* this +* Pointer to the Polygon. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to base Frame in Polygon */ + double **ptr; /* Pointer to data in the encapsulated PointSet */ + double end[ 2 ]; /* Start position for edge */ + double maxwid; /* Maximum polygon width found so far */ + double polcen[ 2 ]; /* Polygon centre perpendicular to current edge */ + double polwid; /* Polygon width perpendicular to current edge */ + double start[ 2 ]; /* Start position for edge */ + int i; /* Axis index */ + int nv; /* Number of vertices in Polygon */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do nothing if the cached information is up to date. */ + if( this->stale ) { + +/* Get a pointer to the base Frame. */ + frm = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + +/* Get the number of vertices. */ + nv = astGetNpoint( ((AstRegion *) this)->points ); + +/* Get pointers to the coordinate data in the parent Region structure. */ + ptr = astGetPoints( ((AstRegion *) this)->points ); + +/* Free any existing edge information in the Polygon structure. */ + if( this->edges ) { + for( i = 0; i < nv; i++ ) { + this->edges[ i ] = astFree( this->edges[ i ] ); + } + +/* Allocate memory to store new edge information if necessary. */ + } else { + this->edges = astMalloc( sizeof( AstLineDef *)*(size_t) nv ); + this->startsat = astMalloc( sizeof( double )*(size_t) nv ); + } + +/* Check pointers can be used safely. */ + if( this->edges ) { + +/* Create and store a description of each edge. Also form the total + distance round the polygon, and the distance from the first vertex + at which each edge starts. */ + this->totlen = 0.0; + start[ 0 ] = ptr[ 0 ][ nv - 1 ]; + start[ 1 ] = ptr[ 1 ][ nv - 1 ]; + + for( i = 0; i < nv; i++ ) { + end[ 0 ] = ptr[ 0 ][ i ]; + end[ 1 ] = ptr[ 1 ][ i ]; + this->edges[ i ] = astLineDef( frm, start, end ); + start[ 0 ] = end[ 0 ]; + start[ 1 ] = end[ 1 ]; + + this->startsat[ i ] = this->totlen; + this->totlen += this->edges[ i ]->length; + } + +/* We now look for a point that is inside the polygon. We want a point + that is well within the polygon, since points that are only just inside + the polygon can give numerical problems. Loop round each edge with + non-zero length. */ + maxwid = -1.0; + for( i = 0; i < nv; i++ ) { + if( this->edges[ i ]->length > 0.0 ) { + +/* We define another line perpendicular to the current edge, passing + through the mid point of the edge, extending towards the inside of the + polygon. The following function returns the distance we can travel + along this line before we hit any of the other polygon edges. It also + puts the position corresponding to half that distance into "polcen". */ + polwid = Polywidth( frm, this->edges, i, nv, polcen, status ); + +/* If the width of the polygon perpendicular to the current edge is + greater than the width perpdeicular to any other edge, record the + width and also store the current polygon centre. */ + if( polwid > maxwid && polwid != AST__BAD ) { + maxwid = polwid; + (this->in)[ 0 ] = polcen[ 0 ]; + (this->in)[ 1 ] = polcen[ 1 ]; + } + } + } + +/* If no width was found it probably means that the polygon vertices were + given in clockwise order, resulting in the above process probing the + infinite extent outside the polygonal hole. In this case any point + outside the hole will do, so we use the current contents of the + "polcen" array. Set a flag indicating if the vertices are stored in + anti-clockwise order. */ + if( maxwid < 0.0 ) { + (this->in)[ 0 ] = polcen[ 0 ]; + (this->in)[ 1 ] = polcen[ 1 ]; + this->acw = 0; + } else { + this->acw = 1; + } + } + +/* Free resources */ + frm = astAnnul( frm ); + +/* Indicate cached information is up to date. */ + this->stale = 0; + } +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Polygon member function (over-rides the astClearAttrib protected +* method inherited from the Region class). + +* Description: +* This function clears the value of a specified attribute for a +* Polygon, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Polygon. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPolygon *this; /* Pointer to the Polygon structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Polygon structure. */ + this = (AstPolygon *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* SimpVertices. */ +/* ------------- */ + if ( !strcmp( attrib, "simpvertices" ) ) { + astClearSimpVertices( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +/* +*++ +* Name: +c astConvex +f AST_CONVEX + +* Purpose: +* Create a new Polygon representing the convex hull of a 2D data grid. + +* Type: +* Public function. + +* Synopsis: +c #include "polygon.h" +c AstPolygon *astConvex( value, int oper, const array[], +c const int lbnd[2], const int ubnd[2], int starpix ) +f RESULT = AST_CONVEX( VALUE, OPER, ARRAY, LBND, UBND, STARPIX, STATUS ) + +* Class Membership: +* Polygon method. + +* Description: +* This is a set of functions that create the shortest Polygon that +* encloses all pixels with a specified value within a gridded +* 2-dimensional data array (e.g. an image). +* +* A basic 2-dimensional Frame is used to represent the pixel coordinate +* system in the returned Polygon. The Domain attribute is set to +* "PIXEL", the Title attribute is set to "Pixel coordinates", and the +* Unit attribute for each axis is set to "pixel". All other +* attributes are left unset. The nature of the pixel coordinate system +* is determined by parameter +c "starpix". +f STARPIX. +* +* You should use a function which matches the numerical type of the +* data you are processing by replacing in the generic function +* name +c astConvex +f AST_CONVEX +c by an appropriate 1- or 2-character type code. For example, if you +* are procesing data with type +c "float", you should use the function astConvexF +f REAL, you should use the function AST_CONVEXR +* (see the "Data Type Codes" section below for the codes appropriate to +* other numerical types). + +* Parameters: +c value +f VALUE = (Given) +* A data value that specifies the pixels to be included within the +* convex hull. +c oper +f OPER = INTEGER (Given) +* Indicates how the +c "value" +f VALUE +* parameter is used to select the required pixels. It can +* have any of the following values: +c - AST__LT: include pixels with value less than "value". +c - AST__LE: include pixels with value less than or equal to "value". +c - AST__EQ: include pixels with value equal to "value". +c - AST__NE: include pixels with value not equal to "value". +c - AST__GE: include pixels with value greater than or equal to "value". +c - AST__GT: include pixels with value greater than "value". +f - AST__LT: include pixels with value less than VALUE. +f - AST__LE: include pixels with value less than or equal to VALUE. +f - AST__EQ: include pixels with value equal to VALUE. +f - AST__NE: include pixels with value not equal to VALUE. +f - AST__GE: include pixels with value greater than or equal to VALUE. +f - AST__GT: include pixels with value greater than VALUE. +c array +f ARRAY( * ) = (Given) +c Pointer to a +f A +* 2-dimensional array containing the data to be processed. The +* numerical type of this array should match the 1- or +* 2-character type code appended to the function name (e.g. if +c you are using astConvexF, the type of each array element +c should be "float"). +f you are using AST_CONVEXR, the type of each array element +f should be REAL). +* +* The storage order of data within this array should be such +* that the index of the first grid dimension varies most +* rapidly and that of the second dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c lbnd +f LBND( 2 ) = INTEGER (Given) +c Pointer to an array of two integers +f An array +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +c ubnd +f UBND( 2) = INTEGER (Given) +c Pointer to an array of two integers +f An array +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +c Note that "lbnd" and "ubnd" together define the shape +f Note that LBND and UBND together define the shape +* and size of the input grid, its extent along a particular +c (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the +c index "j" to be zero-based). They also define +f (J'th) dimension being UBND(J)-LBND(J)+1. They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre or upper corner, as selected by parameter +c "starpix". +f STARPIX. +c starpix +f STARPIX = LOGICAL (Given) +* A flag indicating the nature of the pixel coordinate system used +* to describe the vertex positions in the returned Polygon. If +c non-zero, +f .TRUE., +* the standard Starlink definition of pixel coordinate is used in +* which a pixel with integer index I spans a range of pixel coordinate +* from (I-1) to I (i.e. pixel corners have integral pixel coordinates). +c If zero, +f If .FALSE., +* the definition of pixel coordinate used by other AST functions +c such as astResample, astMask, +f such as AST_RESAMPLE, AST_MASK, +* etc., is used. In this definition, a pixel with integer index I +* spans a range of pixel coordinate from (I-0.5) to (I+0.5) (i.e. +* pixel centres have integral pixel coordinates). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astConvex() +f AST_CONVEX = INTEGER +* A pointer to the required Polygon. +c NULL +f AST__NULL +* is returned without error if the array contains no pixels that +* satisfy the criterion specified by +c "value" and "oper". +f VALUE and OPER. + +* Notes: +c - NULL +f - AST__NULL +* will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. + +* Data Type Codes: +* To select the appropriate masking function, you should +c replace in the generic function name astConvex with a +f replace in the generic function name AST_CONVEX with a +* 1- or 2-character data type code, so as to match the numerical +* type of the data you are processing, as follows: +c - D: double +c - F: float +c - L: long int +c - UL: unsigned long int +c - I: int +c - UI: unsigned int +c - S: short int +c - US: unsigned short int +c - B: byte (signed char) +c - UB: unsigned byte (unsigned char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - UI: INTEGER (treated as unsigned) +f - S: INTEGER*2 (short integer) +f - US: INTEGER*2 (short integer, treated as unsigned) +f - B: BYTE (treated as signed) +f - UB: BYTE (treated as unsigned) +* +c For example, astConvexD would be used to process "double" +c data, while astConvexS would be used to process "short int" +c data, etc. +f For example, AST_CONVEXD would be used to process DOUBLE +f PRECISION data, while AST_CONVEXS would be used to process +f short integer data (stored in an INTEGER*2 array), etc. +f +f For compatibility with other Starlink facilities, the codes W +f and UW are provided as synonyms for S and US respectively (but +f only in the Fortran interface to AST). + +*-- +*/ +/* Define a macro to implement the function for a specific data + type. Note, this function cannot be a virtual function since the + argument list does not include a Polygon, and so no virtual function + table is available. */ +#define MAKE_CONVEX(X,Xtype) \ +AstPolygon *astConvex##X##_( Xtype value, int oper, const Xtype array[], \ + const int lbnd[2], const int ubnd[2], \ + int starpix, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *frm; /* Frame in which to define the Polygon */ \ + AstPointSet *candidate; /* Candidate polygon vertices */ \ + AstPolygon *result; /* Result value to return */ \ + int xdim; /* Number of pixels per row */ \ + int ydim; /* Number of rows */ \ + static double junk[ 6 ] = {0.0, 0.0, 1.0, 1.0, 0.0, 1.0 }; /* Junk poly */ \ +\ +/* Initialise. */ \ + result = NULL; \ + candidate = NULL; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Get the array dimensions. */ \ + xdim = ubnd[ 0 ] - lbnd[ 0 ] + 1; \ + ydim = ubnd[ 1 ] - lbnd[ 1 ] + 1; \ +\ +/* Get the basic concvex hull. */ \ + if( oper == AST__LT ) { \ + candidate = ConvexHullLT##X( value, array, lbnd, starpix, xdim, ydim, status ); \ +\ + } else if( oper == AST__LE ) { \ + candidate = ConvexHullLE##X( value, array, lbnd, starpix, xdim, ydim, status ); \ +\ + } else if( oper == AST__EQ ) { \ + candidate = ConvexHullEQ##X( value, array, lbnd, starpix, xdim, ydim, status ); \ +\ + } else if( oper == AST__NE ) { \ + candidate = ConvexHullNE##X( value, array, lbnd, starpix, xdim, ydim, status ); \ +\ + } else if( oper == AST__GE ) { \ + candidate = ConvexHullGE##X( value, array, lbnd, starpix, xdim, ydim, status ); \ +\ + } else if( oper == AST__GT ) { \ + candidate = ConvexHullGT##X( value, array, lbnd, starpix, xdim, ydim, status ); \ +\ + } else if( astOK ){ \ + astError( AST__OPRIN, "astConvex"#X": Invalid operation code " \ + "(%d) supplied (programming error).", status, oper ); \ + } \ +\ +/* Check some good selected values were found. */ \ + if( candidate ) { \ +\ +/* Create a default Polygon with 3 junk vertices. */ \ + frm = astFrame( 2, "Domain=PIXEL,Unit(1)=pixel,Unit(2)=pixel," \ + "Title=Pixel coordinates", status ); \ + result = astPolygon( frm, 3, 3, junk, NULL, " ", status ); \ +\ +/* Change the PointSet within the Polygon to the one created above. */ \ + SetPointSet( result, candidate, status ); \ +\ +/* Free resources. */ \ + frm = astAnnul( frm ); \ + candidate = astAnnul( candidate ); \ + } \ +\ +/* If an error occurred, clear the returned result. */ \ + if ( !astOK ) result = astAnnul( result ); \ +\ +/* Return the result. */ \ + return result; \ +} + + +/* Expand the above macro to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_CONVEX(LD,long double) +#endif +MAKE_CONVEX(D,double) +MAKE_CONVEX(L,long int) +MAKE_CONVEX(UL,unsigned long int) +MAKE_CONVEX(I,int) +MAKE_CONVEX(UI,unsigned int) +MAKE_CONVEX(S,short int) +MAKE_CONVEX(US,unsigned short int) +MAKE_CONVEX(B,signed char) +MAKE_CONVEX(UB,unsigned char) +MAKE_CONVEX(F,float) + +/* Undefine the macros. */ +#undef MAKE_CONVEX + +/* +* Name: +* ConvexHull + +* Purpose: +* Find the convex hull enclosing selected pixels in a 2D array. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* AstPointSet *ConvexHull( value, const array[], +* const int lbnd[2], int starpix, +* int xdim, int ydim, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* This function uses an algorithm similar to "Andrew's Monotone Chain +* Algorithm" to create a list of vertices describing the convex hull +* enclosing the selected pixels in the supplied array. The vertices +* are returned in a PointSet. + +* Parameters: +* value +* A data value that specifies the pixels to be selected. +* array +* Pointer to a 2-dimensional array containing the data to be +* processed. The numerical type of this array should match the 1- +* or 2-character type code appended to the function name. +* lbnd +* The lower pixel index bounds of the array. +* starpix +* If non-zero, the usual Starlink definition of pixel coordinate +* is used (integral values at pixel corners). Otherwise, the +* system used by other AST functions such as astResample is used +* (integral values at pixel centres). +* xdim +* The number of pixels along each row of the array. +* ydim +* The number of rows in the array. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A PointSet holding the vertices of the convex hull, or NULL if an +* error occurs. + +* Notes: +* - The following code has been designed with a view to it being +* multi-threaded at some point. + +*/ + +/* Define a macro to implement the function for a specific data + type and operation. */ +#define MAKE_CONVEXHULL(X,Xtype,Oper,OperI) \ +static AstPointSet *ConvexHull##Oper##X( Xtype value, const Xtype array[], \ + const int lbnd[2], int starpix, \ + int xdim, int ydim, int *status ) { \ +\ +/* Local Variables: */ \ + AstPointSet *result; \ + double **ptr; \ + double *xv1; \ + double *xv2; \ + double *xv3; \ + double *xv4; \ + double *xvert; \ + double *yv1; \ + double *yv2; \ + double *yv3; \ + double *yv4; \ + double *yvert; \ + int nv1; \ + int nv2; \ + int nv3; \ + int nv4; \ + int nv; \ + int xhi; \ + int xhiymax; \ + int xhiymin; \ + int xlo; \ + int xloymax; \ + int xloymin; \ + int yhi; \ + int yhixmax; \ + int yhixmin; \ + int ylo; \ + int yloxmax; \ + int yloxmin; \ +\ +/* Initialise */ \ + result = NULL; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Find the lowest Y value at any selected pixel, and find the max and \ + min X value of the selected pixels at that lowest Y value. */ \ + FindBoxEdge##Oper##X( value, array, xdim, ydim, 1, 1, &ylo, &yloxmax, \ + &yloxmin, status ); \ +\ +/* Skip if there are no selected values in the array. */ \ + if( ylo > 0 ) { \ +\ +/* Find the highest Y value at any selected pixel, and find the max and \ + min X value of the selected pixels at that highest Y value. */ \ + FindBoxEdge##Oper##X( value, array, xdim, ydim, 1, 0, &yhi, &yhixmax, \ + &yhixmin, status ); \ +\ +/* Find the lowest X value at any selected pixel, and find the max and \ + min Y value of the selected pixels at that lowest X value. */ \ + FindBoxEdge##Oper##X( value, array, xdim, ydim, 0, 1, &xlo, &xloymax, \ + &xloymin, status ); \ +\ +/* Find the highest X value at any selected pixel, and find the max and \ + min Y value of the selected pixels at that highest X value. */ \ + FindBoxEdge##Oper##X( value, array, xdim, ydim, 0, 0, &xhi, &xhiymax, \ + &xhiymin, status ); \ +\ +/* Create a list of vertices for the bottom right corner of the bounding \ + box of the selected pixels. */ \ + PartHull##Oper##X( value, array, xdim, ydim, yloxmax, ylo, xhi, xhiymin, \ + starpix, lbnd, &xv1, &yv1, &nv1, status ); \ +\ +/* Create a list of vertices for the top right corner of the bounding \ + box of the selected pixels. */ \ + PartHull##Oper##X( value, array, xdim, ydim, xhi, xhiymax, yhixmax, yhi, \ + starpix, lbnd, &xv2, &yv2, &nv2, status ); \ +\ +/* Create a list of vertices for the top left corner of the bounding \ + box of the selected pixels. */ \ + PartHull##Oper##X( value, array, xdim, ydim, yhixmin, yhi, xlo, xloymax, \ + starpix, lbnd, &xv3, &yv3, &nv3, status ); \ +\ +/* Create a list of vertices for the bottom left corner of the bounding \ + box of the selected pixels. */ \ + PartHull##Oper##X( value, array, xdim, ydim, xlo, xloymin, yloxmin, ylo, \ + starpix, lbnd, &xv4, &yv4, &nv4, status ); \ +\ +/* Concatenate the four vertex lists and store them in the returned \ + PointSet. */ \ + nv = nv1 + nv2 + nv3 + nv4; \ + result = astPointSet( nv, 2, " ", status ); \ + ptr = astGetPoints( result ); \ + if( astOK ) { \ + xvert = ptr[ 0 ]; \ + yvert = ptr[ 1 ]; \ +\ + memcpy( xvert, xv1, nv1*sizeof( double ) ); \ + memcpy( yvert, yv1, nv1*sizeof( double ) ); \ + xvert += nv1; \ + yvert += nv1; \ +\ + memcpy( xvert, xv2, nv2*sizeof( double ) ); \ + memcpy( yvert, yv2, nv2*sizeof( double ) ); \ + xvert += nv2; \ + yvert += nv2; \ +\ + memcpy( xvert, xv3, nv3*sizeof( double ) ); \ + memcpy( yvert, yv3, nv3*sizeof( double ) ); \ + xvert += nv3; \ + yvert += nv3; \ +\ + memcpy( xvert, xv4, nv4*sizeof( double ) ); \ + memcpy( yvert, yv4, nv4*sizeof( double ) ); \ + } \ +\ +/* Free resources. */ \ + xv1 = astFree( xv1 ); \ + xv2 = astFree( xv2 ); \ + xv3 = astFree( xv3 ); \ + xv4 = astFree( xv4 ); \ + yv1 = astFree( yv1 ); \ + yv2 = astFree( yv2 ); \ + yv3 = astFree( yv3 ); \ + yv4 = astFree( yv4 ); \ + } \ +\ +/* Free the returned PointSet if an error occurred. */ \ + if( result && !astOK ) result = astAnnul( result ); \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Define a macro that uses the above macro to to create implementations + of ConvexHull for all operations. */ +#define MAKEALL_CONVEXHULL(X,Xtype) \ +MAKE_CONVEXHULL(X,Xtype,LT,AST__LT) \ +MAKE_CONVEXHULL(X,Xtype,LE,AST__LE) \ +MAKE_CONVEXHULL(X,Xtype,EQ,AST__EQ) \ +MAKE_CONVEXHULL(X,Xtype,NE,AST__NE) \ +MAKE_CONVEXHULL(X,Xtype,GE,AST__GE) \ +MAKE_CONVEXHULL(X,Xtype,GT,AST__GT) + +/* Expand the above macro to generate a function for each required + data type and operation. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKEALL_CONVEXHULL(LD,long double) +#endif +MAKEALL_CONVEXHULL(D,double) +MAKEALL_CONVEXHULL(L,long int) +MAKEALL_CONVEXHULL(UL,unsigned long int) +MAKEALL_CONVEXHULL(I,int) +MAKEALL_CONVEXHULL(UI,unsigned int) +MAKEALL_CONVEXHULL(S,short int) +MAKEALL_CONVEXHULL(US,unsigned short int) +MAKEALL_CONVEXHULL(B,signed char) +MAKEALL_CONVEXHULL(UB,unsigned char) +MAKEALL_CONVEXHULL(F,float) + +/* Undefine the macros. */ +#undef MAKE_CONVEXHULL +#undef MAKEALL_CONVEXHULL + +static AstPolygon *Downsize( AstPolygon *this, double maxerr, int maxvert, + int *status ) { +/* +*++ +* Name: +c astDownsize +f AST_DOWNSIZE + +* Purpose: +* Reduce the number of vertices in a Polygon. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "polygon.h" +c AstPolygon *astDownsize( AstPolygon *this, double maxerr, int maxvert ) +f RESULT = AST_DOWNSIZE( THIS, MAXERR, MAXVERT, STATUS ) + +* Class Membership: +* Polygon method. + +* Description: +* This function returns a pointer to a new Polygon that contains a +* subset of the vertices in the supplied Polygon. The subset is +* chosen so that the returned Polygon is a good approximation to +* the supplied Polygon, within the limits specified by the supplied +* parameter values. That is, the density of points in the returned +* Polygon is greater at points where the curvature of the boundary of +* the supplied Polygon is greater. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Polygon. +c maxerr +f MAXERR = DOUBLE PRECISION (Given) +* The maximum allowed discrepancy between the supplied and +* returned Polygons, expressed as a geodesic distance within the +* Polygon's coordinate frame. If this is zero or less, the +* returned Polygon will have the number of vertices specified by +c maxvert. +f MAXVERT. +c maxvert +f MAXVERT = INTEGER (Given) +* The maximum allowed number of vertices in the returned Polygon. +* If this is less than 3, the number of vertices in the returned +* Polygon will be the minimum needed to achieve the maximum +* discrepancy specified by +c maxerr. +f MAXERR. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astDownsize() +f AST_DOWNSIZE = INTEGER +* Pointer to the new Polygon. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstFrame *frm; /* Base Frame from the Polygon */ + AstPointSet *pset; /* PointSet holding vertices of downsized polygon */ + AstPolygon *result; /* Returned pointer to new Polygon */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the base Frame of the Polygon. */ + frm = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE ); + +/* Create a PointSet holding the vertices of the downsized polygon. */ + pset = DownsizePoly( ((AstRegion *) this)->points, maxerr, maxvert, + frm, status ); + +/* Take a deep copy of the supplied Polygon. */ + result = astCopy( this ); + +/* Change the PointSet within the result Polygon to the one created above. */ \ + SetPointSet( result, pset, status ); \ + +/* Free resources. */ + frm = astAnnul( frm ); + pset = astAnnul( pset ); + +/* If an error occurred, annul the returned Polygon. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *DownsizePoly( AstPointSet *pset, double maxerr, + int maxvert, AstFrame *frm, int *status ) { +/* +* Name: +* DownsizePoly + +* Purpose: +* Reduce the number of vertices in a Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* AstPointSet *DownsizePoly( AstPointSet *pset, double maxerr, int maxvert, +* AstFrame *frm, int *status ) + +* Class Membership: +* Polygon member function. + +* Description: +* This function returns a pointer to a new PointSet that contains a +* subset of the vertices in the supplied PointSet. The subset is +* chosen so that the returned polygon is a good approximation to +* the supplied polygon, within the limits specified by the supplied +* parameter values. That is, the density of points in the returned +* polygon is greater at points where the curvature of the boundary of +* the supplied polygon is greater. + +* Parameters: +* pset +* Pointer to the PointSet holding the polygon vertices. +* maxerr +* The maximum allowed discrepancy between the supplied and +* returned Polygons, expressed as a geodesic distance within the +* Polygon's coordinate frame. If this is zero or less, the +* returned Polygon will have the number of vertices specified by +* maxvert. +* maxvert +* The maximum allowed number of vertices in the returned Polygon. +* If this is less than 3, the number of vertices in the returned +* Polygon will be the minimum needed to achieve the maximum +* discrepancy specified by +* maxerr. +* frm +* Pointer to the Frame in which the polygon is defined. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new PointSet. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Returned pointer to new PointSet */ + Segment *head; /* Pointer to new polygon edge with highest error */ + Segment *seg1; /* Pointer to new polygon edge */ + Segment *seg2; /* Pointer to new polygon edge */ + Segment *seg3; /* Pointer to new polygon edge */ + double **ptr; /* Pointer to arrays of axis values */ + double *x; /* Pointer to array of X values for old Polygon */ + double *xnew; /* Pointer to array of X values for new Polygon */ + double *y; /* Pointer to array of Y values for old Polygon */ + double *ynew; /* Pointer to array of Y values for new Polygon */ + double x1; /* Lowest X value at any vertex */ + double x2; /* Highest X value at any vertex */ + double y1; /* Lowest Y value at any vertex */ + double y2; /* Highest Y value at any vertex */ + int *newpoly; /* Holds indices of retained input vertices */ + int i1; /* Index of first vertex added to output polygon */ + int i1x; /* Index of vertex with lowest X */ + int i1y; /* Index of vertex with lowest Y */ + int i2; /* Index of second vertex added to output polygon */ + int i2x; /* Index of vertex with highest X */ + int i2y; /* Index of vertex with highest Y */ + int i3; /* Index of third vertex added to output polygon */ + int iadd; /* Normalised vertex index */ + int iat; /* Index at which to store new vertex index */ + int newlen; /* Number of vertices currently in new Polygon */ + int nv; /* Number of vertices in old Polygon */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the number of vertices in the supplied polygon. */ + nv = astGetNpoint( pset ); + +/* If the maximum error allowed is zero, and the maximum number of + vertices is equal to or greater than the number in the supplied + polygon, just return a deep copy of the supplied PointSet. */ + if( maxerr <= 0.0 && ( maxvert < 3 || maxvert >= nv ) ) { + result = astCopy( pset ); + +/* Otherwise... */ + } else { + +/* Get pointers to the X and Y arrays holding the vertex coordinates in + the supplied polygon. */ + ptr = astGetPoints( pset ); + x = ptr[ 0 ]; + y = ptr[ 1 ]; + +/* Allocate memory for an array to hold the original indices of the vertices + to be used to create the returned Polygon. This array is expanded as + needed. */ + newpoly = astMalloc( 10*sizeof( int ) ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* We first need to decide on three widely spaced vertices which form a + reasonable triangular approximation to the whole polygon. First find + the vertices with the highest and lowest Y values, and the highest and + lowest X values. */ + x1 = DBL_MAX; + x2 = -DBL_MAX; + y1 = DBL_MAX; + y2 = -DBL_MAX; + + i1y = i1x = 0; + i2y = i2x = nv/2; + + for( i3 = 0; i3 < nv; i3++ ) { + if( y[ i3 ] < y1 ) { + y1 = y[ i3 ]; + i1y = i3; + } else if( y[ i3 ] > y2 ) { + y2 = y[ i3 ]; + i2y = i3; + } + + if( x[ i3 ] < x1 ) { + x1 = x[ i3 ]; + i1x = i3; + } else if( x[ i3 ] > x2 ) { + x2 = x[ i3 ]; + i2x = i3; + } + } + +/* Use the axis which spans the greater range. */ + if( y2 - y1 > x2 - x1 ) { + i1 = i1y; + i2 = i2y; + } else { + i1 = i1x; + i2 = i2x; + } + +/* The index with vertex i1 is definitely going to be one of our three + vertices. We are going to use the line from i1 to i2 to choose the two + other vertices to use. Create a structure describing the segment of the + Polygon from the lowest value on the selected axis (X or Y) to the + highest value. As always, the polygons is traversed in an anti-clockwise + direction. */ + seg1 = NewSegment( NULL, i1, i2, nv, status ); + +/* Find the vertex within this segment which is furthest away from the + line on the right hand side (as moving from vertex i1 to vertex i2). */ + FindMax( seg1, frm, x, y, nv, 0, status ); + +/* Likewise, create a structure describing the remained of the Polygon + (i.e. the segment from the highest value on the selected axis to the + lowest value). Then find the vertex within this segment which is + furthest away from the line on the right hand side. */ + seg2 = NewSegment( NULL, i2, i1, nv, status ); + FindMax( seg2, frm, x, y, nv, 0, status ); + +/* Select the segment for which the found vertex is furthest from the + line. */ + if( seg2->error > seg1->error ) { + +/* If the second segment, we will use the vertex that is farthest from + the line as one of our threee vertices. To ensure that movement from + vertex i1 to i2 to i3 is anti-clockwise, we must use this new vertex + as vertex i3, not i2. */ + i3 = seg2->imax; + +/* Create a description of the polygon segment from vertex i1 to i3, and + find the vertex which is furthest to the right of the line joining the + two vertices. We use this as the middle vertex (i2). */ + seg1 = NewSegment( seg1, i1, i3, nv, status ); + FindMax( seg1, frm, x, y, nv, 0, status ); + i2 = seg1->imax; + +/* Do the same if we are choosing the other segment, ordering the + vertices to retain anti-clockwise movement from i1 to i2 to i3. */ + } else { + i2 = seg1->imax; + seg1 = NewSegment( seg1, i2, i1, nv, status ); + FindMax( seg1, frm, x, y, nv, 0, status ); + i3 = seg1->imax; + } + +/* Ensure the vertex indices are in the first cycle. */ + if( i2 >= nv ) i2 -= nv; + if( i3 >= nv ) i3 -= nv; + +/* Create Segment structures to describe each of these three edges. */ + seg1 = NewSegment( seg1, i1, i2, nv, status ); + seg2 = NewSegment( seg2, i2, i3, nv, status ); + seg3 = NewSegment( NULL, i3, i1, nv, status ); + +/* Record these 3 vertices in an array holding the original indices of + the vertices to be used to create the returned Polygon. */ + newpoly[ 0 ] = i1; + newpoly[ 1 ] = i2; + newpoly[ 2 ] = i3; + +/* Indicate the new polygon currently has 3 vertices. */ + newlen = 3; + +/* Search the old vertices between the start and end of segment 3, looking + for the vertex which lies furthest from the line of segment 3. The + residual between this point and the line is stored in the Segment + structure, as is the index of the vertex at which this maximum residual + occurred. */ + FindMax( seg3, frm, x, y, nv, 1, status ); + +/* The "head" variable points to the head of a double linked list of + Segment structures. This list is ordered by residual, so that the + Segment with the maximum residual is at the head, and the Segment + with the minimum residual is at the tail. Initially "seg3" is at the + head. */ + head = seg3; + +/* Search the old vertices between the start and end of segment 1, looking + for the vertex which lies furthest from the line of segment 1. The + residual between this point and the line is stored in the Segment + structure, as is the index of the vertex at which this maximum residual + occurred. */ + FindMax( seg1, frm, x, y, nv, 1, status ); + +/* Insert segment 1 into the linked list of Segments, at a position that + maintains the ordering of the segments by error. Thus the head of the + list will still have the max error. */ + head = AddToChain( head, seg1, status ); + +/* Do the same for segment 2. */ + FindMax( seg2, frm, x, y, nv, 1, status ); + head = AddToChain( head, seg2, status ); + +/* If the maximum allowed number of vertices in the output Polygon is + less than 3, allow any number of vertices up to the number in the + input Polygon (termination will then be determined just by "maxerr"). */ + if( maxvert < 3 ) maxvert = nv; + +/* Loop round adding an extra vertex to the returned Polygon until the + maximum residual between the new and old polygons is no more than + "maxerr". Abort early if the specified maximum number of vertices is + reached. */ + while( head->error > maxerr && newlen < maxvert ) { + +/* The segment at the head of the list has the max error (that is, it is + the segment that departs most from the supplied Polygon). To make the + new polygon a better fit to the old polygon, we add the vertex that is + furthest away from this segment to the new polygon. Remember that a + polygon is cyclic so if the vertex has an index that is greater than the + number of vertices in the old polygon, reduce the index by the number + of vertices in the old polygon. */ + iadd = head->imax; + if( iadd >= nv ) iadd -= nv; + iat = newlen++; + newpoly = astGrow( newpoly, newlen, sizeof( int ) ); + if( !astOK ) break; + newpoly[ iat ] = iadd; + +/* We now split the segment that had the highest error into two segments. + The split occurs at the vertex that had the highest error. */ + seg1 = NewSegment( NULL, head->imax, head->i2, nv, status ); + seg2 = head; + seg2->i2 = head->imax; + +/* We do not know where these two new segments should be in the ordered + linked list, so remove them from the list. */ + head = RemoveFromChain( head, seg1, status ); + head = RemoveFromChain( head, seg2, status ); + +/* Find the vertex that deviates most from the first of these two new + segments, and then add the segment into the list of vertices, using + the maximum deviation to determine the position of the segment within + the list. */ + FindMax( seg1, frm, x, y, nv, 1, status ); + head = AddToChain( head, seg1, status ); + +/* Do the same for the second new segment. */ + FindMax( seg2, frm, x, y, nv, 1, status ); + head = AddToChain( head, seg2, status ); + } + +/* Now we have reached the required accuracy, free resources. */ + while( head ) { + seg1 = head; + head = head->next; + seg1 = astFree( seg1 ); + } + +/* If no vertices have been left out, return a deep copy of the supplied + PointSet. */ + if( newlen == nv ) { + result = astCopy( pset ); + +/* Otherwise, sort the indices of the vertices to be retained so that they + are in the same order as they were in the supplied Polygon. */ + } else if( astOK ){ + qsort( newpoly, newlen, sizeof( int ), IntCmp ); + +/* Create a new PointSet and get pointers to its axis values. */ + result = astPointSet( newlen, 2, " ", status ); + ptr = astGetPoints( result ); + xnew = ptr[ 0 ]; + ynew = ptr[ 1 ]; + +/* Copy the axis values for the retained vertices from the old to the new + PointSet. */ + if( astOK ) { + for( iat = 0; iat < newlen; iat++ ) { + *(xnew++) = x[ newpoly[ iat ] ]; + *(ynew++) = y[ newpoly[ iat ] ]; + } + } + } + } + +/* Free resources. */ + newpoly = astFree( newpoly ); + } + +/* If an error occurred, annul the returned PointSet. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void EnsureInside( AstPolygon *this, int *status ){ +/* +* Name: +* EnsureInside + +* Purpose: +* Ensure the unnegated Polygon represents the inside of the polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void EnsureInside( AstPolygon *this, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* Reversing the order of the vertices of a Polygon is like negating +* the Polygon. But the parent Region class assumes that an unnegated +* region bounded by closed curves (e.g. boxes, circles, ellipses, etc) +* is bounded. So we need to have a way to ensure that a Polygon also +* follows this convention. So this function reverses the order of the +* vertices in the Polygon, if necessary, to ensure that the unnegated +* Polygon is bounded. + +* Parameters: +* this +* The Polygon. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstRegion *this_region; + double **ptr; + double *p; + double *q; + double tmp; + int bounded; + int i; + int j; + int jmid; + int negated; + int np; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Is the unnegated Polygon unbounded? If so, we need to reverse the + vertices. */ + bounded = astGetBounded( this ); + negated = astGetNegated( this ); + if( ( bounded && negated ) || ( !bounded && !negated ) ) { + this_region = (AstRegion *) this; + +/* Get a pointer to the arrays holding the coordinates at the Polygon + vertices. */ + ptr = astGetPoints( this_region->points ); + +/* Get the number of vertices. */ + np = astGetNpoint( this_region->points ); + +/* Store the index of the last vertex to swap. For odd "np" the central + vertex does not need to be swapped. */ + jmid = np/2; + +/* Loop round the two axes spanned by the Polygon. */ + for( i = 0; i < 2; i++ ) { + +/* Get pointers to the first pair of axis values to be swapped - i.e. the + first and last axis values. */ + p = ptr[ i ]; + q = p + np - 1; + +/* Loop round all pairs of axis values. */ + for( j = 0; j < jmid; j++ ) { + +/* Swap the pair. */ + tmp = *p; + *(p++) = *q; + *(q--) = tmp; + } + } + +/* Invert the value of the "Negated" attribute to cancel out the effect + of the above vertex reversal. */ + astNegate( this ); + +/* Indicate the cached information in the Polygon structure is stale. */ + this->stale = 1; + } +} + +/* +* Name: +* FindBoxEdge + +* Purpose: +* Find an edge of the bounding box containing the selected pixels. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void FindBoxEdge( value, const array[], +* int xdim, int ydim, int axis, int low, +* int *val, int *valmax, int *valmin, +* int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* This function search for an edge of the bounding box containing the +* selected pixels in the supplied array. + +* Parameters: +* value +* A data value that specifies the pixels to be selected. +* array +* Pointer to a 2-dimensional array containing the data to be +* processed. The numerical type of this array should match the 1- +* or 2-character type code appended to the function name. +* xdim +* The number of pixels along each row of the array. +* ydim +* The number of rows in the array. +* axis +* The index (0 or 1) of the pixel axis perpendicular to the edge of the +* bounding box being found. +* low +* If non-zero, the lower edge of the bounding box on the axis +* specified by "axis" is found and returned. Otherwise, the higher +* edge of the bounding box on the axis specified by "axis" is +* found and returned. +* val +* Address of an int in which to return the value on axis "axis" of +* the higher or lower (as specified by "low") edge of the bounding +* box. +* valmax +* Address of an int in which to return the highest value on axis +* "1-axis" of the selected pixels on the returned edge. +* valmin +* Address of an int in which to return the lowest value on axis +* "1-axis" of the selected pixels on the returned edge. +* status +* Pointer to the inherited status variable. + +* Notes; +* - Zero is returned for "*val" if no good values are found, or if +* an error occurs. + +*/ + +/* Define a macro to implement the function for a specific data + type and operation. */ +#define MAKE_FINDBOXEDGE(X,Xtype,Oper,OperI) \ +static void FindBoxEdge##Oper##X( Xtype value, const Xtype array[], int xdim, \ + int ydim, int axis, int low, int *val, \ + int *valmax, int *valmin, int *status ) { \ +\ +/* Local Variables: */ \ + int astep; \ + int bstep; \ + int a0; \ + int a1; \ + int b0; \ + int b1; \ + int inc; \ + int a; \ + int b; \ + const Xtype *pc; \ +\ +/* Initialise. */ \ + *val = 0; \ + *valmin = 0; \ + *valmax = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* If we are finding an edge that is parallel to the X axis... */ \ + if( axis ) { \ +\ +/* Get the vector step between adjacent pixels on the selected axis, and \ + on the other axis. */ \ + astep = xdim; \ + bstep = 1; \ +\ +/* Get the first and last value to check on the selected axis, and the \ + increment between checks. */ \ + if( low ) { \ + a0 = 1; \ + a1 = ydim; \ + inc = 1; \ + } else { \ + a0 = ydim; \ + a1 = 1; \ + inc = -1; \ + } \ +\ +/* The first and last value to check on the other axis. */ \ + b0 = 1; \ + b1 = xdim; \ +\ +/* Do the same if we are finding an edge that is parallel to the Y axis. */ \ + } else { \ + astep = 1; \ + bstep = xdim; \ +\ + if( low ) { \ + a0 = 1; \ + a1 = xdim; \ + inc = 1; \ + } else { \ + a0 = xdim; \ + a1 = 1; \ + inc = -1; \ + } \ +\ + b0 = 1; \ + b1 = ydim; \ + } \ +\ +/* Loop round the axis values. */ \ + a = a0; \ + while( 1 ) { \ +\ +/* Get a pointer to the first value to be checked at this axis value. */ \ + pc = array + (a - 1)*astep + (b0 - 1)*bstep; \ +\ +/* Scan the other axis to find the first and last selected pixel. */ \ + for( b = b0; b <= b1; b++ ) { \ + if( ISVALID(*pc,OperI,value) ) { \ + if( *valmin == 0 ) *valmin = b; \ + *valmax = b; \ + } \ + pc += bstep; \ + } \ +\ +/* If any selected pixels were found, store the axis value and exit. */ \ + if( *valmax ) { \ + *val = a; \ + break; \ + } \ +\ +/* Move on to the next axis value. */ \ + if( a != a1 ) { \ + a += inc; \ + } else { \ + break; \ + } \ +\ + } \ +} + +/* Define a macro that uses the above macro to to create implementations + of FindBoxEdge for all operations. */ +#define MAKEALL_FINDBOXEDGE(X,Xtype) \ +MAKE_FINDBOXEDGE(X,Xtype,LT,AST__LT) \ +MAKE_FINDBOXEDGE(X,Xtype,LE,AST__LE) \ +MAKE_FINDBOXEDGE(X,Xtype,EQ,AST__EQ) \ +MAKE_FINDBOXEDGE(X,Xtype,NE,AST__NE) \ +MAKE_FINDBOXEDGE(X,Xtype,GE,AST__GE) \ +MAKE_FINDBOXEDGE(X,Xtype,GT,AST__GT) + +/* Expand the above macro to generate a function for each required + data type and operation. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKEALL_FINDBOXEDGE(LD,long double) +#endif +MAKEALL_FINDBOXEDGE(D,double) +MAKEALL_FINDBOXEDGE(L,long int) +MAKEALL_FINDBOXEDGE(UL,unsigned long int) +MAKEALL_FINDBOXEDGE(I,int) +MAKEALL_FINDBOXEDGE(UI,unsigned int) +MAKEALL_FINDBOXEDGE(S,short int) +MAKEALL_FINDBOXEDGE(US,unsigned short int) +MAKEALL_FINDBOXEDGE(B,signed char) +MAKEALL_FINDBOXEDGE(UB,unsigned char) +MAKEALL_FINDBOXEDGE(F,float) + +/* Undefine the macros. */ +#undef MAKE_FINDBOXEDGE +#undef MAKEALL_FINDBOXEDGE + +/* +* Name: +* FindInsidePoint + +* Purpose: +* Find a point that is inside the required outline. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void FindInsidePoint( value, const array[], +* const int lbnd[ 2 ], const int ubnd[ 2 ], +* int *inx, int *iny, int *iv, +* int *status ); + +* Class Membership: +* Polygon member function + +* Description: +* The central pixel in the array is checked to see if its value meets +* the requirements implied by and "value". If so, its pixel +* indices and vector index are returned> if not, a spiral search is +* made until such a pixel value is found. + +* Parameters: +* value +* The data value defining valid pixels. +* array +* The data array. +* lbnd +* The lower pixel index bounds of the array. +* ubnd +* The upper pixel index bounds of the array. +* inx +* Pointer to an int in which to return the X pixel index of the +* first point that meets the requirements implied by and +* "value". +* iny +* Pointer to an int in which to return the Y pixel index of the +* first point that meets the requirements implied by and +* "value". +* iv +* Pointer to an int in which to return the vector index of the +* first point that meets the requirements implied by and +* "value". +* status +* Pointer to the inherited status variable. + +* Notes: +* - must be one of LT, LE, EQ, NE, GE, GT. + + +*/ + +/* Define a macro to implement the function for a specific data + type and operation. */ +#define MAKE_FINDINSIDEPOINT(X,Xtype,Oper,OperI) \ +static void FindInsidePoint##Oper##X( Xtype value, const Xtype array[], \ + const int lbnd[ 2 ], const int ubnd[ 2 ], \ + int *inx, int *iny, int *iv, \ + int *status ){ \ +\ +/* Local Variables: */ \ + const Xtype *pv; /* Pointer to next data value to test */ \ + const char *text; /* Pointer to text describing oper */ \ + int cy; /* Central row index */ \ + int iskin; /* Index of spiral layer being searched */ \ + int nskin; /* Number of spiral layers to search */ \ + int nx; /* Pixel per row */ \ + int tmp; /* Temporary storage */ \ + int xhi; /* High X pixel index bound of current skin */ \ + int xlo; /* Low X pixel index bound of current skin */ \ + int yhi; /* High X pixel index bound of current skin */ \ + int ylo; /* Low X pixel index bound of current skin */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Find number of pixels in one row of the array. */ \ + nx = ( ubnd[ 0 ] - lbnd[ 0 ] + 1 ); \ +\ +/* Find the pixel indices of the central pixel */ \ + *inx = ( ubnd[ 0 ] + lbnd[ 0 ] )/2; \ + *iny = ( ubnd[ 1 ] + lbnd[ 1 ] )/2; \ +\ +/* Initialise the vector index and pointer to refer to the central pixel. */ \ + *iv = ( *inx - lbnd[ 0 ] ) + nx*( *iny - lbnd[ 1 ] ) ; \ + pv = array + (*iv); \ +\ +/* Test the pixel value, returning if it is valid. This \ + relies on the compiler optimisation to remove the "if" statements \ + for all but the operation appropriate to the function being defined. */ \ + if( OperI == AST__LT ) { \ + if( *pv < value ) return; \ +\ + } else if( OperI == AST__LE ) { \ + if( *pv <= value ) return; \ +\ + } else if( OperI == AST__EQ ) { \ + if( *pv == value ) return; \ +\ + } else if( OperI == AST__NE ) { \ + if( *pv != value ) return; \ +\ + } else if( OperI == AST__GE ) { \ + if( *pv >= value ) return; \ +\ + } else { \ + if( *pv > value ) return; \ + } \ +\ +/* The central pixel is invalid if we arrive here. So we need to do a \ + spiral search out from the centre looking for a valid pixel. Record \ + the central row index (this is the row at which we jump to the next \ + outer skin when doing the spiral search below). */ \ + cy = *iny; \ +\ +/* Find how many skins can be searched as part of the spiral search \ + before the edge of the array is encountered. */ \ + nskin = ubnd[ 0 ] - *inx; \ + tmp = *inx - lbnd[ 0 ]; \ + if( tmp < nskin ) nskin = tmp; \ + tmp = ubnd[ 1 ] - *iny; \ + if( tmp < nskin ) nskin = tmp; \ + tmp = *iny - lbnd[ 1 ]; \ + if( tmp < nskin ) nskin = tmp; \ +\ +/* Initialise the skin box bounds to be just the central pixel. */ \ + xlo = xhi = *inx; \ + ylo = yhi = *iny; \ +\ +/* Loop round each skin looking for a valid test pixel. */ \ + for( iskin = 0; iskin < nskin; iskin++ ) { \ +\ +/* Increment the upper and lower bounds of the box forming the next \ + skin. */ \ + xhi++; \ + xlo--; \ + yhi++; \ + ylo--; \ +\ +/* Initialise things for the first pixel in the new skin by moving one \ + pixel to the right. */ \ + pv++; \ + (*iv)++; \ + (*inx)++; \ +\ +/* Move up the right hand edge of the box corresponding to the current \ + skin. We start at the middle of the right hand edge. */ \ + for( *iny = cy; *iny <= yhi; (*iny)++ ) { \ +\ +/* Test the pixel value, returning if it is valid. This relies on the \ + compiler optimisation to remove the "if" statements for all but the \ + operation appropriate to the function being defined. */ \ + if( OperI == AST__LT ) { \ + if( *pv < value ) return; \ +\ + } else if( OperI == AST__LE ) { \ + if( *pv <= value ) return; \ +\ + } else if( OperI == AST__EQ ) { \ + if( *pv == value ) return; \ +\ + } else if( OperI == AST__NE ) { \ + if( *pv != value ) return; \ +\ + } else if( OperI == AST__GE ) { \ + if( *pv >= value ) return; \ +\ + } else { \ + if( *pv > value ) return; \ + } \ +\ +/* Move up a pixel. */ \ + pv += nx; \ + *iv += nx; \ + } \ +\ +/* Move down a pixel so that *iny == yhi. */ \ + pv -= nx; \ + *iv -= nx; \ + (*iny)--; \ +\ +/* Move left along the top edge of the box corresponding to the current \ + skin. */ \ + for( *inx = xhi; *inx >= xlo; (*inx)-- ) { \ +\ +/* Test the pixel value, returning if it is valid. */ \ + if( OperI == AST__LT ) { \ + if( *pv < value ) return; \ +\ + } else if( OperI == AST__LE ) { \ + if( *pv <= value ) return; \ +\ + } else if( OperI == AST__EQ ) { \ + if( *pv == value ) return; \ +\ + } else if( OperI == AST__NE ) { \ + if( *pv != value ) return; \ +\ + } else if( OperI == AST__GE ) { \ + if( *pv >= value ) return; \ +\ + } else { \ + if( *pv > value ) return; \ + } \ +\ +/* Move left a pixel. */ \ + pv--; \ + (*iv)--; \ + } \ +\ +/* Move right a pixel so that *inx == xlo. */ \ + pv++; \ + (*iv)++; \ + (*inx)++; \ +\ +/* Move down along the left hand edge of the box corresponding to the current \ + skin. */ \ + for( *iny = yhi; *iny >= ylo; (*iny)-- ) { \ +\ +/* Test the pixel value, returning if it is valid. */ \ + if( OperI == AST__LT ) { \ + if( *pv < value ) return; \ +\ + } else if( OperI == AST__LE ) { \ + if( *pv <= value ) return; \ +\ + } else if( OperI == AST__EQ ) { \ + if( *pv == value ) return; \ +\ + } else if( OperI == AST__NE ) { \ + if( *pv != value ) return; \ +\ + } else if( OperI == AST__GE ) { \ + if( *pv >= value ) return; \ +\ + } else { \ + if( *pv > value ) return; \ + } \ +\ +/* Move down a pixel. */ \ + pv -= nx; \ + *iv -= nx; \ + } \ +\ +/* Move up a pixel so that *iny == ylo. */ \ + pv += nx; \ + *iv += nx; \ + (*iny)++; \ +\ +/* Move right along the bottom edge of the box corresponding to the current \ + skin. */ \ + for( *inx = xlo; *inx <= xhi; (*inx)++ ) { \ +\ +/* Test the pixel value, returning if it is valid. */ \ + if( OperI == AST__LT ) { \ + if( *pv < value ) return; \ +\ + } else if( OperI == AST__LE ) { \ + if( *pv <= value ) return; \ +\ + } else if( OperI == AST__EQ ) { \ + if( *pv == value ) return; \ +\ + } else if( OperI == AST__NE ) { \ + if( *pv != value ) return; \ +\ + } else if( OperI == AST__GE ) { \ + if( *pv >= value ) return; \ +\ + } else { \ + if( *pv > value ) return; \ + } \ +\ +/* Move right a pixel. */ \ + pv++; \ + (*iv)++; \ + } \ +\ +/* Move left a pixel so that *inx == xhi. */ \ + pv--; \ + (*iv)--; \ + (*inx)--; \ +\ +/* Move up the right hand edge of the box correspionding to the current \ + skin. We stop just below the middle of the right hand edge. */ \ + for( *iny = ylo; *iny < cy; (*iny)++ ) { \ +\ +/* Test the pixel value, returning if it is valid. This relies on the \ + compiler optimisation to remove the "if" statements for all but the \ + operation appropriate to the function being defined. */ \ + if( OperI == AST__LT ) { \ + if( *pv < value ) return; \ +\ + } else if( OperI == AST__LE ) { \ + if( *pv <= value ) return; \ +\ + } else if( OperI == AST__EQ ) { \ + if( *pv == value ) return; \ +\ + } else if( OperI == AST__NE ) { \ + if( *pv != value ) return; \ +\ + } else if( OperI == AST__GE ) { \ + if( *pv >= value ) return; \ +\ + } else { \ + if( *pv > value ) return; \ + } \ +\ +/* Move up a pixel. */ \ + pv += nx; \ + *iv += nx; \ + } \ + } \ +\ +/* Report an error if no inside pooint could be found. */ \ + if( OperI == AST__LT ) { \ + text = "less than"; \ + } else if( OperI == AST__LE ) { \ + text = "less than or equal to"; \ + } else if( OperI == AST__EQ ) { \ + text = "equal to"; \ + } else if( OperI == AST__NE ) { \ + text = "not equal to"; \ + } else if( OperI == AST__GE ) { \ + text = "greater than or equal to"; \ + } else { \ + text = "greater than"; \ + } \ + astError( AST__NONIN, "astOutline"#X": Could not find a pixel value %s " \ + "%g in the supplied array.", status, text, (double) value ); \ +} + +/* Define a macro that uses the above macro to to create implementations + of FindInsidePoint for all operations. */ +#define MAKEALL_FINDINSIDEPOINT(X,Xtype) \ +MAKE_FINDINSIDEPOINT(X,Xtype,LT,AST__LT) \ +MAKE_FINDINSIDEPOINT(X,Xtype,LE,AST__LE) \ +MAKE_FINDINSIDEPOINT(X,Xtype,EQ,AST__EQ) \ +MAKE_FINDINSIDEPOINT(X,Xtype,GE,AST__GE) \ +MAKE_FINDINSIDEPOINT(X,Xtype,GT,AST__GT) \ +MAKE_FINDINSIDEPOINT(X,Xtype,NE,AST__NE) + +/* Expand the above macro to generate a function for each required + data type and operation. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKEALL_FINDINSIDEPOINT(LD,long double) +#endif +MAKEALL_FINDINSIDEPOINT(D,double) +MAKEALL_FINDINSIDEPOINT(L,long int) +MAKEALL_FINDINSIDEPOINT(UL,unsigned long int) +MAKEALL_FINDINSIDEPOINT(I,int) +MAKEALL_FINDINSIDEPOINT(UI,unsigned int) +MAKEALL_FINDINSIDEPOINT(S,short int) +MAKEALL_FINDINSIDEPOINT(US,unsigned short int) +MAKEALL_FINDINSIDEPOINT(B,signed char) +MAKEALL_FINDINSIDEPOINT(UB,unsigned char) +MAKEALL_FINDINSIDEPOINT(F,float) + +/* Undefine the macros. */ +#undef MAKE_FINDINSIDEPOINT +#undef MAKEALL_FINDINSIDEPOINT + +static void FindMax( Segment *seg, AstFrame *frm, double *x, double *y, + int nv, int abs, int *status ){ +/* +* Name: +* FindMax + +* Purpose: +* Find the maximum discrepancy between a given line segment and the +* Polygon being downsized. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void FindMax( Segment *seg, AstFrame *frm, double *x, double *y, +* int nv, int abs, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* The supplied Segment structure describes a range of vertices in +* the polygon being downsized. This function checks each of these +* vertices to find the one that lies furthest from the line joining the +* first and last vertices in the segment. The maximum error, and the +* vertex index at which this maximum error is found, is stored in the +* Segment structure. + +* Parameters: +* seg +* The structure describing the range of vertices to check. It +* corresponds to a candidate edge in the downsized polygon. +* frm +* The Frame in which the positions are defined. +* x +* Pointer to the X axis values in the original Polygon. +* y +* Pointer to the Y axis values in the original Polygon. +* nv +* Total number of vertics in the old Polygon.. +* abs +* If non-zero, then the stored maximum is the position with +* maximum absolute error. Otherwise, the stored maximum is the +* position with maximum positive error (positive errors are to the +* right when travelling from start to end of the segment). +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding vertex positions */ + AstPointSet *pset2; /* PointSet holding offset par/perp components */ + double **ptr1; /* Pointers to "pset1" data arrays */ + double **ptr2; /* Pointers to "pset2" data arrays */ + double *px; /* Pointer to next X value */ + double *py; /* Pointer to next Y value */ + double ax; /* X value at start */ + double ay; /* Y value at start */ + double ba; /* Distance between a and b */ + double bax; /* X increment from a to b */ + double bay; /* Y increment from a to b */ + double cax; /* X increment from a to c */ + double cay; /* Y increment from a to c */ + double end[ 2 ]; /* Position of starting vertex */ + double error; /* Error value for current vertex */ + double start[ 2 ]; /* Position of starting vertex */ + int i1; /* Starting index (always in first cycle) */ + int i2; /* Ending index converted to first cycle */ + int i2b; /* Upper vertex limit in first cycle */ + int i; /* Loop count */ + int n; /* Number of vertices to check */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Stuff needed for handling cyclic redundancy of vertex indices. */ + i1 = seg->i1; + i2 = seg->i2; + n = i2 - i1 - 1; + i2b = i2; + if( i2 >= nv ) { + i2 -= nv; + i2b = nv; + } + +/* If the segment has no intermediate vertices, set the segment error to + zero and return. */ + if( n < 1 ) { + seg->error = 0.0; + seg->imax = i1; + +/* For speed, we use simple plane geometry if the Polygon is defined in a + simple Frame. */ + } else if( !strcmp( astGetClass( frm ), "Frame" ) ) { + +/* Point "a" is the vertex that marks the start of the segment. Point "b" + is the vertex that marks the end of the segment. */ + ax = x[ i1 ]; + ay = y[ i1 ]; + bax = x[ i2 ] - ax; + bay = y[ i2 ] - ay; + ba = sqrt( bax*bax + bay*bay ); + +/* Initialise the largest error found so far. */ + seg->error = -1.0; + +/* Check the vertices from the start (plus one) up to the end (minus one) + or the last vertex (which ever comes first). */ + for( i = i1 + 1; i < i2b; i++ ) { + +/* Position "c" is the vertex being tested. Find the squared distance from + "c" to the line joining "a" and "b". */ + cax = x[ i ] - ax; + cay = y[ i ] - ay; + error = ( bay*cax - cay*bax )/ba; + if( abs ) error = fabs( error ); + +/* If this is the largest value found so far, record it. Note the error + here is a squared distance. */ + if( error > seg->error ) { + seg->error = error; + seg->imax = i; + } + } + +/* If the end vertex is in the next cycle, check the remaining vertex + posI would have thought a telentitions in the same way. */ + if( i2b != i2 ) { + + for( i = 0; i < i2; i++ ) { + + cax = x[ i ] - ax; + cay = y[ i ] - ay; + error = ( bay*cax - cay*bax )/ba; + if( abs ) error = fabs( error ); + + if( error > seg->error ) { + seg->error = error; + seg->imax = i + i2b; + } + + } + } + +/* If the polygon is not defined in a simple Frame, we use the overloaded + Frame methods to do the geometry. */ + } else { + +/* Create a PointSet to hold the positions of the vertices to test. We do + not need to test the start or end vertex. */ + pset1 = astPointSet( n, 2, " ", status ); + ptr1 = astGetPoints( pset1 ); + if( astOK ) { + +/* Copy the vertex axis values form the start (plus one) up to the end + (minus one) vertex or the last vertex (which ever comes first). */ + px = ptr1[ 0 ]; + py = ptr1[ 1 ]; + + for( i = i1 + 1; i < i2b; i++ ){ + *(px++) = x[ i ]; + *(py++) = y[ i ]; + } + +/* If the end vertex is in the next cycle, copy the remaining vertex + positions into the PointSet. */ + if( i2b != i2 ) { + for( i = 0; i < i2; i++ ) { + *(px++) = x[ i ]; + *(py++) = y[ i ]; + } + } + +/* Record the start and end vertex positions. */ + start[ 0 ] = x[ i1 ]; + start[ 1 ] = y[ i1 ]; + end[ 0 ] = x[ i2 ]; + end[ 1 ] = y[ i2 ]; + +/* Resolve the vector from the start to each vertex into two components, + parallel and perpendicular to the start->end vector. */ + pset2 = astResolvePoints( frm, start, end, pset1, NULL ); + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + +/* Find the vertex with largest perpendicular component. */ + seg->error = -1.0; + py = ptr2[ 1 ]; + for( i = 1; i <= n; i++ ) { + + error = *(py++); + if( abs ) error = fabs( error ); + + if( error > seg->error ) { + seg->error = error; + seg->imax = i + i1; + } + + } + } + +/* Free resources. */ + pset2 = astAnnul( pset2 ); + } + pset1 = astAnnul( pset1 ); + } +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Polygon member function (over-rides the protected astGetAttrib +* method inherited from the Region class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Polygon, formatted as a character string. + +* Parameters: +* this +* Pointer to the Polygon. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Polygon, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Polygon. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPolygon *this; /* Pointer to the Polygon structure */ + const char *result; /* Pointer value to return */ + int ival; /* Integer attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Polygon structure. */ + this = (AstPolygon *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* SimpVertices. */ +/* ------------- */ + if ( !strcmp( attrib, "simpvertices" ) ) { + ival = astGetSimpVertices( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static int GetBounded( AstRegion *this, int *status ) { +/* +* Name: +* GetBounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* int GetBounded( AstRegion *this, int *status ) + +* Class Membership: +* Polygon method (over-rides the astGetBounded method inherited from +* the Region class). + +* Description: +* This function returns a flag indicating if the Region is bounded. +* The implementation provided by the base Region class is suitable +* for Region sub-classes representing the inside of a single closed +* curve (e.g. Circle, Interval, Box, etc). Other sub-classes (such as +* CmpRegion, PointList, etc ) may need to provide their own +* implementations. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Region is bounded. Zero otherwise. + +*/ + +/* Local Variables: */ + int neg; /* Has the Polygon been negated? */ + int result; /* Returned result */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Ensure cached information is available. */ + Cache( (AstPolygon *) this, status ); + +/* See if the Polygon has been negated. */ + neg = astGetNegated( this ); + +/* If the polygon vertices are stored in anti-clockwise order, then the + polygon is bounded if it has not been negated. */ + if( ( (AstPolygon *) this)->acw ) { + result = (! neg ); + +/* If the polygon vertices are stored in clockwise order, then the + polygon is bounded if it has been negated. */ + } else { + result = neg; + } + +/* Return the result. */ + return result; +} + +void astInitPolygonVtab_( AstPolygonVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPolygonVtab + +* Purpose: +* Initialise a virtual function table for a Polygon. + +* Type: +* Protected function. + +* Synopsis: +* #include "polygon.h" +* void astInitPolygonVtab( AstPolygonVtab *vtab, const char *name ) + +* Class Membership: +* Polygon vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Polygon class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPolygon) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->Downsize = Downsize; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_resetcache = region->ResetCache; + region->ResetCache = ResetCache; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + region->RegPins = RegPins; + region->RegBaseMesh = RegBaseMesh; + region->RegBaseBox = RegBaseBox; + region->RegTrace = RegTrace; + region->GetBounded = GetBounded; + + vtab->ClearSimpVertices = ClearSimpVertices; + vtab->GetSimpVertices = GetSimpVertices; + vtab->SetSimpVertices = SetSimpVertices; + vtab->TestSimpVertices = TestSimpVertices; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDump( vtab, Dump, "Polygon", "Polygonal region" ); + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int IntCmp( const void *a, const void *b ){ +/* +* Name: +* IntCmp + +* Purpose: +* An integer comparison function for the "qsort" function. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* int IntCmp( const void *a, const void *b ) + +* Class Membership: +* Polygon member function + +* Description: +* See the docs for "qsort". + +* Parameters: +* a +* Pointer to the first int +* b +* Pointer to the second int + +* Returnd Value: +* Positive, negative or zero, depending on whether a is larger than, +* equal to, or less than b. + +*/ + + return *((int*)a) - *((int*)b); +} + +static Segment *NewSegment( Segment *seg, int i1, int i2, int nvert, + int *status ){ +/* +* Name: +* NewSegment + +* Purpose: +* Initialise a structure describing a segment of the new Polygon +* created by astDownsize. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* Segment *NewSegment( Segment *seg, int i1, int i2, int nvert, +* int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* This function initialises the contents of a structure describing +* the specified range of vertices within a Polygon. The cyclic nature +* of vertex indices is taken into account. +* +* If no structure is supplied, memory is allocated to hold a new +* structure. + +* Parameters: +* seg +* Pointer to a structure to initialise, or NULL if a new structure +* is to be allocated. +* i1 +* The index of a vertex within the old Polygon (supplied to +* astDownsize) that marks the start of the new line segment in +* the downsized polygon. +* i2 +* The index of a vertex within the old Polygon (supplied to +* astDownsize) that marks the end of the new line segment in +* the downsized polygon. +* nvert +* Total number of vertics in the old Polygon.. +* status +* Pointer to the inherited status variable. + +* Returnd Value: +* Pointer to the initialised Segment structure. It should be freed using +* astFree when no longer needed. + +*/ + +/* Local Variables: */ + Segment *result; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure to be initialised, allocating memory + for a new structure if none was supplied. */ + result = seg ? seg : astMalloc( sizeof( Segment ) ); + +/* Check the pointer can be used safely. */ + if( result ) { + +/* If the supplied ending index is less than the starting index, the + ending index must have gone all the way round the polygon and started + round again. Increase the ending index by the number of vertices to + put it in the same cycle as the starting index. */ + if( i2 < i1 ) i2 += nvert; + +/* If the supplied starting index is within the first cycle (i.e. zero -> + nvert-1 ), use the indices as they are (which may mean that the ending + index is greater than nvert, but this is handled correctly by other + functions). */ + if( i1 < nvert ) { + result->i1 = i1; + result->i2 = i2; + +/* If the supplied starting index is within the second cycle (i.e. nvert + or greater) the ending index will be even greater, so we can reduce + both by "nvert" to put them both in the first cycle. The goal is that + the starting index should always be in the first cycle, but the ending + index may possibly be in the second cycle. */ + } else { + result->i1 = i1 - nvert; + result->i2 = i2 - nvert; + } + +/* Nullify the links to other Segments */ + result->next = NULL; + result->prev = NULL; + } + +/* Return the pointer to the new Segment structure. */ + return result; +} + +/* +*++ +* Name: +c astOutline +f AST_OUTLINE + +* Purpose: +* Create a new Polygon outling values in a 2D data grid. + +* Type: +* Public function. + +* Synopsis: +c #include "polygon.h" +c AstPolygon *astOutline( value, int oper, const array[], +c const int lbnd[2], const int ubnd[2], double maxerr, +c int maxvert, const int inside[2], int starpix ) +f RESULT = AST_OUTLINE( VALUE, OPER, ARRAY, LBND, UBND, MAXERR, +f MAXVERT, INSIDE, STARPIX, STATUS ) + +* Class Membership: +* Polygon method. + +* Description: +* This is a set of functions that create a Polygon enclosing a single +* contiguous set of pixels that have a specified value within a gridded +* 2-dimensional data array (e.g. an image). +* +* A basic 2-dimensional Frame is used to represent the pixel coordinate +* system in the returned Polygon. The Domain attribute is set to +* "PIXEL", the Title attribute is set to "Pixel coordinates", and the +* Unit attribute for each axis is set to "pixel". All other +* attributes are left unset. The nature of the pixel coordinate system +* is determined by parameter +c "starpix". +f STARPIX. +* +* The +c "maxerr" and "maxvert" +f MAXERR and MAXVERT +* parameters can be used to control how accurately the returned +* Polygon represents the required region in the data array. The +* number of vertices in the returned Polygon will be the minimum +* needed to achieve the required accuracy. +* +* You should use a function which matches the numerical type of the +* data you are processing by replacing in the generic function +* name +c astOutline +f AST_OUTLINE +c by an appropriate 1- or 2-character type code. For example, if you +* are procesing data with type +c "float", you should use the function astOutlineF +f REAL, you should use the function AST_OUTLINER +* (see the "Data Type Codes" section below for the codes appropriate to +* other numerical types). + +* Parameters: +c value +f VALUE = (Given) +* A data value that specifies the pixels to be outlined. +c oper +f OPER = INTEGER (Given) +* Indicates how the +c "value" +f VALUE +* parameter is used to select the outlined pixels. It can +* have any of the following values: +c - AST__LT: outline pixels with value less than "value". +c - AST__LE: outline pixels with value less than or equal to "value". +c - AST__EQ: outline pixels with value equal to "value". +c - AST__NE: outline pixels with value not equal to "value". +c - AST__GE: outline pixels with value greater than or equal to "value". +c - AST__GT: outline pixels with value greater than "value". +f - AST__LT: outline pixels with value less than VALUE. +f - AST__LE: outline pixels with value less than or equal to VALUE. +f - AST__EQ: outline pixels with value equal to VALUE. +f - AST__NE: outline pixels with value not equal to VALUE. +f - AST__GE: outline pixels with value greater than or equal to VALUE. +f - AST__GT: outline pixels with value greater than VALUE. +c array +f ARRAY( * ) = (Given) +c Pointer to a +f A +* 2-dimensional array containing the data to be processed. The +* numerical type of this array should match the 1- or +* 2-character type code appended to the function name (e.g. if +c you are using astOutlineF, the type of each array element +c should be "float"). +f you are using AST_OUTLINER, the type of each array element +f should be REAL). +* +* The storage order of data within this array should be such +* that the index of the first grid dimension varies most +* rapidly and that of the second dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +c lbnd +f LBND( 2 ) = INTEGER (Given) +c Pointer to an array of two integers +f An array +* containing the pixel index of the first pixel in the input grid +* along each dimension. +c ubnd +f UBND( 2) = INTEGER (Given) +c Pointer to an array of two integers +f An array +* containing the pixel index of the last pixel in the input grid +* along each dimension. +* +c Note that "lbnd" and "ubnd" together define the shape +f Note that LBND and UBND together define the shape +* and size of the input pixel grid, its extent along a particular +c (j'th) dimension being ubnd[j]-lbnd[j]+1 pixels. +f (J'th) dimension being UBND(J)-LBND(J)+1 pixels. +* For FITS images, +c the lbnd values will be 1 and the ubnd +f the LBND values will be 1 and the UBND +* values will be equal to the NAXISi header values. Other +* data systems, such as the Starlink NDF system, allow an +c arbitrary pixel origin to be used (i.e. lbnd +f arbitrary pixel origin to be used (i.e. LBND +* is not necessarily 1). +* +* These bounds also define the input grid's floating point coordinate +* system, each pixel having unit extent along each dimension with +* integral coordinate values at its centre or upper corner, as selected +* by parameter +c "starpix". +f STARPIX. +c maxerr +f MAXERR = DOUBLE PRECISION (Given) +* Together with +c "maxvert", +f MAXVERT, +* this determines how accurately the returned Polygon represents +* the required region of the data array. It gives the target +* discrepancy between the returned Polygon and the accurate outline +* in the data array, expressed as a number of pixels. Insignificant +* vertices are removed from the accurate outline, one by one, until +* the number of vertices remaining in the returned Polygon equals +c "maxvert", +f MAXVERT, +* or the largest discrepancy between the accurate outline and the +* returned Polygon is greater than +c "maxerr". If "maxerr" +f MAXERR. If MAXERR +* is zero or less, its value is ignored and the returned Polygon will +* have the number of vertices specified by +c "maxvert". +f MAXVERT. +c maxvert +f MAXVERT = INTEGER (Given) +* Together with +c "maxerr", +f MAXERR, +* this determines how accurately the returned Polygon represents +* the required region of the data array. It gives the maximum +* allowed number of vertices in the returned Polygon. Insignificant +* vertices are removed from the accurate outline, one by one, until +* the number of vertices remaining in the returned Polygon equals +c "maxvert", +f MAXVERT, +* or the largest discrepancy between the accurate outline and the +* returned Polygon is greater than +c "maxerr". If "maxvert" +f MAXERR. If MAXVERT +* is less than 3, its value is ignored and the number of vertices in +* the returned Polygon will be the minimum needed to ensure that the +* discrepancy between the accurate outline and the returned +* Polygon is less than +c "maxerr". +f MAXERR. +c inside +f INSIDE( 2 ) = INTEGER (Given) +c Pointer to an array of two integers +f An array +* containing the pixel indices of a pixel known to be inside the +* required region. This is needed because the supplied data +* array may contain several disjoint areas of pixels that satisfy +* the criterion specified by +c "value" and "oper". +f VALUE and OPER. +* In such cases, the area described by the returned Polygon will +* be the one that contains the pixel specified by +c "inside". +f INSIDE. +* If the specified pixel is outside the bounds given by +c "lbnd" and "ubnd", +f LBND and UBND, +* or has a value that does not meet the criterion specified by +c "value" and "oper", +f VALUE and OPER, +* then this function will search for a suitable pixel. The search +* starts at the central pixel and proceeds in a spiral manner until +* a pixel is found that meets the specified crierion. +c starpix +f STARPIX = LOGICAL (Given) +* A flag indicating the nature of the pixel coordinate system used +* to describe the vertex positions in the returned Polygon. If +c non-zero, +f .TRUE., +* the standard Starlink definition of pixel coordinate is used in +* which a pixel with integer index I spans a range of pixel coordinate +* from (I-1) to I (i.e. pixel corners have integral pixel coordinates). +c If zero, +f If .FALSE., +* the definition of pixel coordinate used by other AST functions +c such as astResample, astMask, +f such as AST_RESAMPLE, AST_MASK, +* etc., is used. In this definition, a pixel with integer index I +* spans a range of pixel coordinate from (I-0.5) to (I+0.5) (i.e. +* pixel centres have integral pixel coordinates). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astOutline() +f AST_OUTLINE = INTEGER +* A pointer to the required Polygon. + +* Notes: +* - This function proceeds by first finding a very accurate polygon, +* and then removing insignificant vertices from this fine polygon +* using +c astDownsize. +f AST_DOWNSIZE. +* - The returned Polygon is the outer boundary of the contiguous set +* of pixels that includes ths specified "inside" point, and satisfy +* the specified value requirement. This set of pixels may potentially +* include "holes" where the pixel values fail to meet the specified +* value requirement. Such holes will be ignored by this function. +c - NULL +f - AST__NULL +* will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. + +* Data Type Codes: +* To select the appropriate masking function, you should +c replace in the generic function name astOutline with a +f replace in the generic function name AST_OUTLINE with a +* 1- or 2-character data type code, so as to match the numerical +* type of the data you are processing, as follows: +c - D: double +c - F: float +c - L: long int +c - UL: unsigned long int +c - I: int +c - UI: unsigned int +c - S: short int +c - US: unsigned short int +c - B: byte (signed char) +c - UB: unsigned byte (unsigned char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - UI: INTEGER (treated as unsigned) +f - S: INTEGER*2 (short integer) +f - US: INTEGER*2 (short integer, treated as unsigned) +f - B: BYTE (treated as signed) +f - UB: BYTE (treated as unsigned) +* +c For example, astOutlineD would be used to process "double" +c data, while astOutlineS would be used to process "short int" +c data, etc. +f For example, AST_OUTLINED would be used to process DOUBLE +f PRECISION data, while AST_OUTLINES would be used to process +f short integer data (stored in an INTEGER*2 array), etc. +f +f For compatibility with other Starlink facilities, the codes W +f and UW are provided as synonyms for S and US respectively (but +f only in the Fortran interface to AST). + +*-- +*/ +/* Define a macro to implement the function for a specific data + type. Note, this function cannot be a virtual function since the + argument list does not include a Polygon, and so no virtual function + table is available. */ +#define MAKE_OUTLINE(X,Xtype) \ +AstPolygon *astOutline##X##_( Xtype value, int oper, const Xtype array[], \ + const int lbnd[2], const int ubnd[2], double maxerr, \ + int maxvert, const int inside[2], int starpix, \ + int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *frm; /* Frame in which to define the Polygon */ \ + AstPointSet *candidate; /* Candidate polygon vertices */ \ + AstPointSet *pset; /* PointSet holding downsized polygon vertices */ \ + AstPolygon *result; /* Result value to return */ \ + const Xtype *pv; /* Pointer to next test point */ \ + Xtype v; /* Value of current pixel */ \ + double **ptr; /* Pointers to PointSet arrays */ \ + int boxsize; /* Half width of smoothign box in vertices */ \ + int inx; /* X index of inside point */ \ + int iny; /* Y index of inside point */ \ + int iv; /* Vector index of next test pixel */ \ + int ixv; /* X pixel index of next test point */ \ + int nv0; /* Number of vertices in accurate outline */ \ + int nx; /* Length of array x axis */ \ + int smooth; /* Do we need to smooth the polygon? */ \ + int stop_at_invalid; /* Indicates when to stop rightwards search */ \ + int tmp; /* Alternative boxsize */ \ + int valid; /* Does current pixel meet requirements? */ \ + static double junk[ 6 ] = {0.0, 0.0, 1.0, 1.0, 0.0, 1.0 }; /* Junk poly */ \ +\ +/* Initialise. */ \ + result = NULL; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Avoid compiler warnings. */ \ + iv = 0; \ +\ +/* If we are going to be smoothing the polygon before downsizing it, we \ + need to ensure that the full polygon is retained within TraceEdge. if \ + this is not the case, TraceEdge can remove all vertices from straight \ + lines, except for the vertices that mark the beinning and end of the \ + straight line. */ \ + smooth = ( maxerr > 0.0 || maxvert > 2 ); \ +\ +/* Store the X dimension of the array. */ \ + nx = ubnd[ 0 ] - lbnd[ 0 ] + 1; \ +\ +/* See if a valid inside point was supplied. It must be inside the bounds \ + of the array, and must have a pixel value that meets the specified \ + requirement. */ \ + inx = inside[ 0 ]; \ + iny = inside[ 1 ]; \ + valid = ( inx >= lbnd[ 0 ] && inx <= ubnd[ 0 ] && \ + iny >= lbnd[ 1 ] && iny <= ubnd[ 1 ] ); \ +\ + if( valid ) { \ + iv = ( inx - lbnd[ 0 ] ) + (iny - lbnd[ 1 ] )*nx; \ + v = array[ iv ]; \ +\ + if( oper == AST__LT ) { \ + valid = ( v < value ); \ +\ + } else if( oper == AST__LE ) { \ + valid = ( v <= value ); \ +\ + } else if( oper == AST__EQ ) { \ + valid = ( v == value ); \ +\ + } else if( oper == AST__NE ) { \ + valid = ( v != value ); \ +\ + } else if( oper == AST__GE ) { \ + valid = ( v >= value ); \ +\ + } else if( oper == AST__GT ) { \ + valid = ( v > value ); \ +\ + } else if( astOK ){ \ + astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \ + "(%d) supplied (programming error).", status, oper ); \ + } \ + } \ +\ +/* If no valid inside point was supplied, find one now. */ \ + if( !valid ) { \ +\ + if( oper == AST__LT ) { \ + FindInsidePointLT##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \ +\ + } else if( oper == AST__LE ) { \ + FindInsidePointLE##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \ +\ + } else if( oper == AST__EQ ) { \ + FindInsidePointEQ##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \ +\ + } else if( oper == AST__NE ) { \ + FindInsidePointNE##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \ +\ + } else if( oper == AST__GE ) { \ + FindInsidePointGE##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \ +\ + } else if( oper == AST__GT ) { \ + FindInsidePointGT##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \ +\ + } else if( astOK ){ \ + astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \ + "(%d) supplied (programming error).", status, oper ); \ + } \ + } \ +\ +/* We now need to find a point on the boundary of the region containing \ + the inside point. Starting at the inside point, move to the right \ + through the array until a pixel is found which fails to meet the value \ + requirement or the edge of the array is reached. */ \ +\ + candidate = NULL; \ + pv = array + iv; \ + ixv = inx; \ + stop_at_invalid = 1; \ +\ + while( ++ixv <= ubnd[ 0 ] ) { \ +\ +/* Get the next pixel value. */ \ + v = *(++pv); \ +\ +/* See if it meets the value requirement. */ \ + if( oper == AST__LT ) { \ + valid = ( v < value ); \ +\ + } else if( oper == AST__LE ) { \ + valid = ( v <= value ); \ +\ + } else if( oper == AST__EQ ) { \ + valid = ( v == value ); \ +\ + } else if( oper == AST__NE ) { \ + valid = ( v != value ); \ +\ + } else if( oper == AST__GE ) { \ + valid = ( v >= value ); \ +\ + } else if( oper == AST__GT ) { \ + valid = ( v > value ); \ +\ + } else if( astOK ){ \ + astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \ + "(%d) supplied (programming error).", status, oper ); \ + break; \ + } \ +\ +/* If we are currently looking for the next invalid pixel, and this pixel \ + is invalid... */ \ + if( stop_at_invalid ) { \ + if( ! valid ) { \ +\ +/* The current pixel may be on the required polygon, or it may be on the \ + edge of a hole contained within the region being outlined. We would \ + like to jump over such holes so that we can continue to look for the \ + real edge of the region being outlined. In order to determine if we \ + have reached a hole, we trace the edge that passes through the current \ + pixel, forming a candidate polygon in the process. In the process, We \ + see if the inside point falls within this candidate polygon. If it does \ + then the polygon is accepted as the required polygon. Otherwise, it is \ + rejected as a mere hole, and we continue moving away from the inside \ + point, looking for a new edge. */ \ + if( oper == AST__LT ) { \ + candidate = TraceEdgeLT##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__LE ) { \ + candidate = TraceEdgeLE##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__EQ ) { \ + candidate = TraceEdgeEQ##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__NE ) { \ + candidate = TraceEdgeNE##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__GE ) { \ + candidate = TraceEdgeGE##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__GT ) { \ + candidate = TraceEdgeGT##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( astOK ){ \ + astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \ + "(%d) supplied (programming error).", status, oper ); \ + } \ +\ +/* If the candidate polygon is the required polygon, break out of the \ + loop. Otherwise, indicate that we want to continue moving right, \ + across the hole, until we reach the far side of the hole (i.e. find \ + the next valid pixel). */ \ + if( candidate ) { \ + break; \ + } else { \ + stop_at_invalid = 0; \ + } \ + } \ +\ +/* If we are currently looking for the next valid pixel, and the current \ + pixel is valid... */ \ + } else if( valid ) { \ +\ +/* We have reached the far side of a hole. Continue moving right, looking \ + now for the next invalid pixel (which may be on the required polygon). */ \ + stop_at_invalid = 1; \ + } \ + } \ +\ +/* If we have not yet found the required polygon, we must have reached \ + the right hand edge of the array. So we now follow the edge of the \ + array round until we meet the boundary of the required region. */ \ + if( !candidate ) { \ + if( oper == AST__LT ) { \ + candidate = TraceEdgeLT##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__LE ) { \ + candidate = TraceEdgeLE##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__EQ ) { \ + candidate = TraceEdgeEQ##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__NE ) { \ + candidate = TraceEdgeNE##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__GE ) { \ + candidate = TraceEdgeGE##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( oper == AST__GT ) { \ + candidate = TraceEdgeGT##X( value, array, lbnd, ubnd, iv - 1, \ + ixv - 1, iny, starpix, smooth, status ); \ +\ + } else if( astOK ){ \ + astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \ + "(%d) supplied (programming error).", status, oper ); \ + } \ + } \ +\ +/* If required smooth the full resolution polygon before downsizing it. */ \ + if( smooth ) { \ +\ +/* Initially, set the boxsize to be equal to the required accouracy. */ \ + if( maxerr > 0 ) { \ + boxsize = (int) maxerr; \ + } else { \ + boxsize = INT_MAX; \ + } \ +\ +/* Determine a second box size equal to the average number of vertices in \ + the accurate outline, per vertex in the returned Polygon. */ \ + nv0 = astGetNpoint( candidate ); \ + if( maxvert > 2 ) { \ + tmp = nv0/(2*maxvert); \ + } else { \ + tmp = INT_MAX; \ + } \ +\ +/* Use the minimum of the two box sizes. */ \ + if( tmp < boxsize ) boxsize = tmp; \ +\ +/* Ensure the box is sufficiently small to allow at least 10 full boxes \ + (=20 half boxes) around the polygon. */ \ + tmp = nv0/20; \ + if( tmp < boxsize ) boxsize = tmp; \ + if( boxsize == 0 ) boxsize = 1; \ +\ +/* Smooth the polygon. */ \ + SmoothPoly( candidate, boxsize, 1.0, status ); \ + } \ +\ +/* Reduce the number of vertices in the outline. */ \ + frm = astFrame( 2, "Domain=PIXEL,Unit(1)=pixel,Unit(2)=pixel," \ + "Title=Pixel coordinates", status ); \ + pset = DownsizePoly( candidate, maxerr, maxvert, frm, status ); \ +\ +/* Create a default Polygon with 3 junk vertices. */ \ + result = astPolygon( frm, 3, 3, junk, NULL, " ", status ); \ +\ +/* Change the PointSet within the Polygon to the one created above. */ \ + SetPointSet( result, pset, status ); \ +\ +/* Free resources. Note, we need to free the arrays within the candidate \ + PointSet explicitly, since they were not created as part of the \ + construction of the PointSet (see TraceEdge). */ \ + pset = astAnnul( pset ); \ + frm = astAnnul( frm ); \ + ptr = astGetPoints( candidate ); \ + if( astOK ) { \ + astFree( ptr[ 0 ] ); \ + astFree( ptr[ 1 ] ); \ + } \ + candidate = astAnnul( candidate ); \ +\ +/* If an error occurred, clear the returned result. */ \ + if ( !astOK ) result = astAnnul( result ); \ +\ +/* Return the result. */ \ + return result; \ +} + + +/* Expand the above macro to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_OUTLINE(LD,long double) +#endif +MAKE_OUTLINE(D,double) +MAKE_OUTLINE(L,long int) +MAKE_OUTLINE(UL,unsigned long int) +MAKE_OUTLINE(I,int) +MAKE_OUTLINE(UI,unsigned int) +MAKE_OUTLINE(S,short int) +MAKE_OUTLINE(US,unsigned short int) +MAKE_OUTLINE(B,signed char) +MAKE_OUTLINE(UB,unsigned char) +MAKE_OUTLINE(F,float) + +/* Undefine the macros. */ +#undef MAKE_OUTLINE + +/* +* Name: +* PartHull + +* Purpose: +* Find the convex hull enclosing selected pixels in one corner of a 2D array. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void PartHull( value, const array[], int xdim, +* int ydim, int xs, int ys, int xe, int ye, +* int starpix, const int lbnd[2], double **xvert, +* double **yvert, int *nvert, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* This function uses an algorithm similar to "Andrew's Monotone Chain +* Algorithm" to create a list of vertices describing one corner of the +* convex hull enclosing the selected pixels in the supplied array. +* The corner is defined to be the area of the array to the right of +* the line from (xs,ys) to (xe,ye). + +* Parameters: +* value +* A data value that specifies the pixels to be selected. +* array +* Pointer to a 2-dimensional array containing the data to be +* processed. The numerical type of this array should match the 1- +* or 2-character type code appended to the function name. +* xdim +* The number of pixels along each row of the array. +* ydim +* The number of rows in the array. +* xs +* The X GRID index of the first pixel on the line to be checked. +* ys +* The Y GRID index of the first pixel on the line to be checked. +* xe +* The X GRID index of the last pixel on the line to be checked. +* ye +* The Y GRID index of the last pixel on the line to be checked. +* starpix +* If non-zero, the usual Starlink definition of pixel coordinate +* is used (integral values at pixel corners). Otherwise, the +* system used by other AST functions such as astResample is used +* (integral values at pixel centres). +* lbnd +* The lower pixel index bounds of the array. +* xvert +* Address of a pointer in which to return a pointer to the list +* of GRID x values on the hull. +* yvert +* Address of a pointer in which to return a pointer to the list +* of GRID y values on the hull. +* nvert +* Address of a pointer in which to return the number of points in +* the returned xvert and yvert arrays. +* status +* Pointer to the inherited status variable. + +*/ + +/* Define a macro to implement the function for a specific data + type and operation. */ +#define MAKE_PARTHULL(X,Xtype,Oper,OperI) \ +static void PartHull##Oper##X( Xtype value, const Xtype array[], int xdim, \ + int ydim, int xs, int ys, int xe, int ye, \ + int starpix, const int lbnd[2], \ + double **xvert, double **yvert, int *nvert, \ + int *status ) { \ +\ +/* Local Variables: */ \ + const Xtype *pc; \ + double *pxy; \ + double dx2; \ + double dx1; \ + double dy1; \ + double dy2; \ + double off; \ + double xdelta; \ + int ivert; \ + int ix; \ + int iy; \ + int x0; \ + int x1; \ + int xl; \ + int xlim; \ + int xr; \ + int yinc; \ +\ +/* Initialise */ \ + *yvert = NULL; \ + *xvert = NULL; \ + *nvert = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* If the line has zero length. just return a single vertex. */ \ + if( xs == xe && ys == ye ) { \ + *xvert = astMalloc( sizeof( double ) ); \ + *yvert = astMalloc( sizeof( double ) ); \ + if( astOK ) { \ + if( starpix ) { \ + (*xvert)[ 0 ] = xs + lbnd[ 0 ] - 1.5; \ + (*yvert)[ 0 ] = ys + lbnd[ 1 ] - 1.5; \ + } else { \ + (*xvert)[ 0 ] = xs + lbnd[ 0 ] - 1.0; \ + (*yvert)[ 0 ] = ys + lbnd[ 1 ] - 1.0; \ + } \ + *nvert = 1; \ + } \ + return; \ + } \ +\ +/* Otherwise check the line is sloping. */ \ + if( xs == xe ) { \ + astError( AST__INTER, "astOutline(Polygon): Bounding box " \ + "has zero width (internal AST programming error).", \ + status ); \ + return; \ + } else if( ys == ye ) { \ + astError( AST__INTER, "astOutline(Polygon): Bounding box " \ + "has zero height (internal AST programming error).", \ + status ); \ + return; \ + } \ +\ +/* Calculate the difference in length between adjacent rows of the area \ + to be tested. */ \ + xdelta = ((double)( xe - xs ))/((double)( ye - ys )); \ +\ +/* The left and right X limits */ \ + if( xe > xs ) { \ + xl = xs; \ + xr = xe; \ + } else { \ + xl = xe; \ + xr = xs; \ + } \ +\ +/* Get the increment in row number as we move from the start to the end \ + of the line. */ \ + yinc = ( ye > ys ) ? 1 : -1; \ +\ +/* Loop round all rows that cross the region to be tested, from start to \ + end of the supplied line. */ \ + iy = ys; \ + while( astOK ) { \ +\ +/* Get the GRID X coord where the line crosses this row. */ \ + xlim = (int)( 0.5 + xs + xdelta*( iy - ys ) ); \ +\ +/* Get the index of the first and last columns to be tested on this row. */ \ + if( yinc < 0 ) { \ + x0 = xl; \ + x1 = xlim; \ + } else { \ + x0 = xlim; \ + x1 = xr; \ + } \ +\ +/* Get a pointer to the first pixel to be tested at this row. */ \ + pc = array + ( iy - 1 )*xdim + x0 - 1; \ +\ +/* Loop round all columns to be tested in this row. */ \ + for( ix = x0; ix <= x1 && astOK; ix++,pc++ ) { \ +\ +/* Ignore pixels that are not selected. */ \ + if( ISVALID(*pc,OperI,value) ) { \ +\ +/* If this is the very first pixel, initialise the hull to contain just \ + the first pixel. */ \ + if( *nvert == 0 ){ \ + *xvert = astMalloc( 200*sizeof( double ) ); \ + *yvert = astMalloc( 200*sizeof( double ) ); \ + if( astOK ) { \ + (*xvert)[ 0 ] = ix; \ + (*yvert)[ 0 ] = iy; \ + *nvert = 1; \ + } else { \ + break; \ + } \ +\ +/* Otherwise.... */ \ + } else { \ +\ +/* Loop until the hull has been corrected to include the current pixel. */ \ + while( 1 ) { \ +\ +/* If the hull currently contains only one pixel, add the current pixel to \ + the end of the hull. */ \ + if( *nvert == 1 ){ \ + (*xvert)[ 1 ] = ix; \ + (*yvert)[ 1 ] = iy; \ + *nvert = 2; \ + break; \ +\ +/* Otherwise... */ \ + } else { \ +\ +/* Extend the line from the last-but-one pixel on the hull to the last \ + pixel on the hull, and see if the current pixel is to the left of \ + this line. If it is, it too is on the hull and so push it onto the end \ + of the list of vertices. */ \ + dx1 = (*xvert)[ *nvert - 1 ] - (*xvert)[ *nvert - 2 ]; \ + dy1 = (*yvert)[ *nvert - 1 ] - (*yvert)[ *nvert - 2 ]; \ + dx2 = ix - (*xvert)[ *nvert - 2 ]; \ + dy2 = iy - (*yvert)[ *nvert - 2 ]; \ +\ + if( dx1*dy2 > dx2*dy1 ) { \ + ivert = (*nvert)++; \ + *xvert = astGrow( *xvert, *nvert, sizeof( double ) ); \ + *yvert = astGrow( *yvert, *nvert, sizeof( double ) ); \ + if( astOK ) { \ + (*xvert)[ ivert ] = ix; \ + (*yvert)[ ivert ] = iy; \ + } \ +\ +/* Leave the loop now that the new point is on the hull. */ \ + break; \ +\ +/* If the new point is to the left of the line, then the last point \ + previously thought to be on hull is in fact not on the hull, so remove \ + it. We then loop again to compare the new pixel with modified hull. */ \ + } else { \ + (*nvert)--; \ + } \ + } \ + } \ + } \ + } \ + } \ +\ + if( iy == ye ) { \ + break; \ + } else { \ + iy += yinc; \ + } \ +\ + } \ +\ +/* Convert GRID coords to PIXEL coords. */ \ + if( astOK ) { \ + pxy = *xvert; \ + off = starpix ? lbnd[ 0 ] - 1.5 : lbnd[ 0 ] - 1.0; \ + for( ivert = 0; ivert < *nvert; ivert++ ) *(pxy++) += off; \ +\ + pxy = *yvert; \ + off = starpix ? lbnd[ 1 ] - 1.5 : lbnd[ 1 ] - 1.0; \ + for( ivert = 0; ivert < *nvert; ivert++ ) *(pxy++) += off; \ +\ +/* Free lists if an error has occurred. */ \ + } else { \ + *xvert = astFree( *xvert ); \ + *yvert = astFree( *yvert ); \ + *nvert = 0; \ + } \ +} + +/* Define a macro that uses the above macro to to create implementations + of PartHull for all operations. */ +#define MAKEALL_PARTHULL(X,Xtype) \ +MAKE_PARTHULL(X,Xtype,LT,AST__LT) \ +MAKE_PARTHULL(X,Xtype,LE,AST__LE) \ +MAKE_PARTHULL(X,Xtype,EQ,AST__EQ) \ +MAKE_PARTHULL(X,Xtype,NE,AST__NE) \ +MAKE_PARTHULL(X,Xtype,GE,AST__GE) \ +MAKE_PARTHULL(X,Xtype,GT,AST__GT) + +/* Expand the above macro to generate a function for each required + data type and operation. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKEALL_PARTHULL(LD,long double) +#endif +MAKEALL_PARTHULL(D,double) +MAKEALL_PARTHULL(L,long int) +MAKEALL_PARTHULL(UL,unsigned long int) +MAKEALL_PARTHULL(I,int) +MAKEALL_PARTHULL(UI,unsigned int) +MAKEALL_PARTHULL(S,short int) +MAKEALL_PARTHULL(US,unsigned short int) +MAKEALL_PARTHULL(B,signed char) +MAKEALL_PARTHULL(UB,unsigned char) +MAKEALL_PARTHULL(F,float) + +/* Undefine the macros. */ +#undef MAKE_PARTHULL +#undef MAKEALL_PARTHULL + +static double Polywidth( AstFrame *frm, AstLineDef **edges, int i, int nv, + double cen[ 2 ], int *status ){ +/* +* Name: +* Polywidth + +* Purpose: +* Find the width of a polygon perpendicular to a given edge. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* double Polywidth( AstFrame *frm, AstLineDef **edges, int i, int nv, +* double cen[ 2 ], int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* This function defines a line perpendicular to a given polygon edge, +* passing through the mid point of the edge, extending towards the +* inside of the polygon. It returns the distance that can be travelled +* along this line before any of the other polygon edges are hit (the +* "width" of the polygon perpendicular to the given edge). It also +* puts the position corresponding to half that distance into "cen". + +* Parameters: +* frm +* The Frame in which the lines are defined. +* edges +* Array of "nv" pointers to AstLineDef structures, each defining an +* edge of the polygon. +* i +* The index of the edge that is to define the polygon width. +* nv +* Total number of edges. +* cen +* An array into which are put the coords of the point half way +* along the polygon width line. +* status +* Pointer to the inherited status variable. + +* Returnd Value: +* The width of the polygon perpendicular to the given edge, or +* AST__BAD if the width cannot be determined (usually because the +* vertices been supplied in a clockwise direction, effectively +* negating the Polygon). + +*/ + +/* Local Variables: */ + AstLineDef *line; + double *cross; + double d; + double end[ 2 ]; + double l1; + double l2; + double result; + double start[ 2 ]; + int j; + +/* Check the global error status. */ + result = AST__BAD; + if ( !astOK ) return result; + +/* Create a Line description for a line perpendicular to the specified + edge, passing through the mid point of the edge, and extending towards + the inside of the polygon. First move away from the start along the + line to the mid point. This gives the start of the new line. */ + l1 = 0.5*( edges[ i ]->length ); + astLineOffset( frm, edges[ i ], l1, 0.0, start ); + +/* We now move away from this position at right angles to the line. We + start off by moving 5 times the length of the specified edge. For + some Frames (e.g. SkyFrames) this may result in a position that is + much too close (i.e. if it goes all the way round the great circle + and comes back to the beginning). Therefore, we check that the end + point is the requested distance from the start point, and if not, we + halve the length of the line and try again. */ + l2 = 10.0*l1; + while( 1 ) { + astLineOffset( frm, edges[ i ], l1, l2, end ); + d = astDistance( frm, start, end ); + if( d != AST__BAD && fabs( d - l2 ) < 1.0E-6*l2 ) break; + l2 *= 0.5; + } + +/* Create a description of the required line. */ + line = astLineDef( frm, start, end ); + +/* Loop round every edge, except for the supplied edge. */ + for( j = 0; j < nv; j++ ) { + if( j != i ) { + +/* Find the position at which the line created above crosses the current + edge. Skip to the next edge if the line does not intersect the edge + within the length of the edge. */ + if( astLineCrossing( frm, line, edges[ j ], &cross ) ) { + +/* Find the distance between the crossing point and the line start. */ + d = astDistance( frm, start, cross ); + +/* If this is less than the smallest found so far, record it. */ + if( d != AST__BAD && ( d < result || result == AST__BAD ) ) { + result = d; + } + } + +/* Free resources */ + cross = astFree( cross ); + } + } + line = astFree( line ); + +/* If a width was found, return the point half way across the polygon. */ + if( result != AST__BAD ) { + astOffset( frm, start, end, 0.5*result, cen ); + +/* The usual reason for not finding a width is if the polygon vertices + are supplied in clockwise order, effectively negating the polygon, and + resulting in the "inside" of the polygon being the infinite region + outside a polygonal hole. In this case, the end point of the line + perpendicular to the initial edge can be returned as a representative + "inside" point. */ + } else { + cen[ 0 ] = end[ 0 ]; + cen[ 1 ] = end[ 1 ]; + } + +/* Return the width. */ + return result; + +} + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* Polygon member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to encapsulated Frame */ + AstPointSet *pset; /* Pointer to PointSet defining the Region */ + AstPolygon *this; /* Pointer to Polygon structure */ + AstRegion *reg; /* Base Frame equivalent of supplied Polygon */ + double **ptr; /* Pointer to PointSet data */ + double *x; /* Pointer to next X axis value */ + double *y; /* Pointer to next Y axis value */ + double dist; /* Offset along an axis */ + double x0; /* The first X axis value */ + double y0; /* The first Y axis value */ + int ip; /* Point index */ + int np; /* No. of points in PointSet */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Polygon structure. */ + this = (AstPolygon *) this_region; + +/* If the base Frame bounding box has already been found, return the + values stored in the Polygon structure. */ + if( this->lbnd[ 0 ] != AST__BAD ) { + lbnd[ 0 ] = this->lbnd[ 0 ]; + lbnd[ 1 ] = this->lbnd[ 1 ]; + ubnd[ 0 ] = this->ubnd[ 0 ]; + ubnd[ 1 ] = this->ubnd[ 1 ]; + +/* If the base Frame bounding box has not yet been found, find it now and + store it in the Polygon structure. */ + } else { + +/* Get the axis values for the PointSet which defines the location and + extent of the region in the base Frame of the encapsulated FrameSet. */ + pset = this_region->points; + ptr = astGetPoints( pset ); + np = astGetNpoint( pset ); + +/* Get a pointer to the base Frame in the frameset encapsulated by the + parent Region structure. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Find the upper and lower bounds of the box enclosing all the vertices. + The box is expressed in terms of axis offsets from the first vertex, in + order to avoid problems with boxes that cross RA=0 or RA=12h */ + lbnd[ 0 ] = 0.0; + lbnd[ 1 ] = 0.0; + ubnd[ 0 ] = 0.0; + ubnd[ 1 ] = 0.0; + + x = ptr[ 0 ]; + y = ptr[ 1 ]; + + x0 = *x; + y0 = *y; + + for( ip = 0; ip < np; ip++, x++, y++ ) { + + dist = astAxDistance( frm, 1, x0, *x ); + if( dist < lbnd[ 0 ] ) { + lbnd[ 0 ] = dist; + } else if( dist > ubnd[ 0 ] ) { + ubnd[ 0 ] = dist; + } + + dist = astAxDistance( frm, 2, y0, *y ); + if( dist < lbnd[ 1 ] ) { + lbnd[ 1 ] = dist; + } else if( dist > ubnd[ 1 ] ) { + ubnd[ 1 ] = dist; + } + + } + +/* Convert the box bounds to absolute values, rather than values relative + to the first vertex. */ + lbnd[ 0 ] += x0; + lbnd[ 1 ] += y0; + ubnd[ 0 ] += x0; + ubnd[ 1 ] += y0; + +/* The astNormBox requires a Mapping which can be used to test points in + this base Frame. Create a copy of the Polygon and then set its + FrameSet so that the current Frame in the copy is the same as the base + Frame in the original. */ + reg = astCopy( this ); + astSetRegFS( reg, frm ); + astSetNegated( reg, 0 ); + +/* Normalise this box. */ + astNormBox( frm, lbnd, ubnd, reg ); + +/* Free resources */ + reg = astAnnul( reg ); + frm = astAnnul( frm ); + +/* Store it in the olygon structure for future use. */ + this->lbnd[ 0 ] = lbnd[ 0 ]; + this->lbnd[ 1 ] = lbnd[ 1 ]; + this->ubnd[ 0 ] = ubnd[ 0 ]; + this->ubnd[ 1 ] = ubnd[ 1 ]; + + } +} + +static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* Polygon member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. Annul the pointer using astAnnul when it +* is no longer needed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Local Variables: */ + AstFrame *frm; /* Base Frame in encapsulated FrameSet */ + AstPointSet *result; /* Returned pointer */ + AstPolygon *this; /* The Polygon structure */ + double **rptr; /* Pointers to returned mesh data */ + double **vptr; /* Pointers to vertex data */ + double *lens; /* Pointer to work space holding edge lengths */ + double d; /* Length of this edge */ + double delta; /* Angular separation of points */ + double end[ 2 ]; /* End position */ + double mp; /* No. of mesh points per unit distance */ + double p[ 2 ]; /* Position in 2D Frame */ + double start[ 2 ]; /* Start position */ + double total; /* Total length of polygon */ + int ip; /* Point index */ + int iv; /* Vertex index */ + int n; /* No. of points on this edge */ + int next; /* Index of next point in returned PointSet */ + int np; /* No. of points in returned PointSet */ + int nv; /* No. of polygon vertices */ + +/* Initialise */ + result= NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this_region->basemesh ) { + result = astClone( this_region->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* Get a pointer to the Polygon structure. */ + this = (AstPolygon *) this_region; + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Get the number of vertices and pointers to the vertex axis values. */ + nv = astGetNpoint( this_region->points ); + vptr = astGetPoints( this_region->points ); + +/* Allocate memory to hold the geodesic length of each edge. */ + lens = astMalloc( sizeof( double )*(size_t) nv ); + + if( astOK ) { + +/* Find the total geodesic distance around the boundary. */ + total = 0.0; + + start[ 0 ] = vptr[ 0 ][ 0 ]; + start[ 1 ] = vptr[ 1 ][ 0 ]; + + for( iv = 1; iv < nv; iv++ ) { + end[ 0 ] = vptr[ 0 ][ iv ]; + end[ 1 ] = vptr[ 1 ][ iv ]; + + d = astDistance( frm, start, end ); + if( d != AST__BAD ) total += fabs( d ); + lens[ iv ] = d; + start[ 0 ] = end[ 0 ]; + start[ 1 ] = end[ 1 ]; + } + + end[ 0 ] = vptr[ 0 ][ 0 ]; + end[ 1 ] = vptr[ 1 ][ 0 ]; + + d = astDistance( frm, start, end ); + if( d != AST__BAD ) total += fabs( d ); + lens[ 0 ] = d; + +/* Find the number of mesh points per unit geodesic distance. */ + if( total > 0.0 ){ + mp = astGetMeshSize( this )/total; + +/* Find the total number of mesh points required. */ + np = 0; + for( iv = 0; iv < nv; iv++ ) { + if( lens[ iv ] != AST__BAD ) np += 1 + (int)( lens[ iv ]*mp ); + } + +/* Create a suitable PointSet to hold the returned positions. */ + result = astPointSet( np, 2, "", status ); + rptr = astGetPoints( result ); + if( astOK ) { + +/* Initialise the index of the next point to be added to the returned + PointSet. */ + next = 0; + +/* Loop round each good edge of the polygon. The edge ends at vertex "iv". */ + start[ 0 ] = vptr[ 0 ][ 0 ]; + start[ 1 ] = vptr[ 1 ][ 0 ]; + + for( iv = 1; iv < nv; iv++ ) { + end[ 0 ] = vptr[ 0 ][ iv ]; + end[ 1 ] = vptr[ 1 ][ iv ]; + if( lens[ iv ] != AST__BAD ) { + +/* Add the position of the starting vertex to the returned mesh. */ + rptr[ 0 ][ next ] = start[ 0 ]; + rptr[ 1 ][ next ] = start[ 1 ]; + next++; + +/* Find the number of points on this edge, and the geodesic distance + between them. */ + n = 1 + (int) ( lens[ iv ]*mp ); + +/* If more than one point, find the distance between points. */ + if( n > 1 ) { + delta = lens[ iv ]/n; + +/* Loop round the extra points. */ + for( ip = 1; ip < n; ip++ ) { + +/* Find the position of the next mesh point. */ + astOffset( frm, start, end, delta*ip, p ); + +/* Add it to the mesh. */ + rptr[ 0 ][ next ] = p[ 0 ]; + rptr[ 1 ][ next ] = p[ 1 ]; + next++; + + } + } + } + +/* The end of this edge becomes the start of the next. */ + start[ 0 ] = end[ 0 ]; + start[ 1 ] = end[ 1 ]; + } + +/* Now do the edge which ends at the first vertex. */ + end[ 0 ] = vptr[ 0 ][ 0 ]; + end[ 1 ] = vptr[ 1 ][ 0 ]; + if( lens[ 0 ] != AST__BAD ) { + rptr[ 0 ][ next ] = start[ 0 ]; + rptr[ 1 ][ next ] = start[ 1 ]; + next++; + + n = 1 + (int)( lens[ 0 ]*mp ); + if( n > 1 ) { + delta = lens[ 0 ]/n; + for( ip = 1; ip < n; ip++ ) { + astOffset( frm, start, end, delta*ip, p ); + rptr[ 0 ][ next ] = p[ 0 ]; + rptr[ 1 ][ next ] = p[ 1 ]; + next++; + } + } + } + +/* Check the PointSet size was correct. */ + if( next != np && astOK ) { + astError( AST__INTER, "astRegBaseMesh(%s): Error in the " + "allocated PointSet size (%d) - should have " + "been %d (internal AST programming error).", status, + astGetClass( this ), np, next ); + } + +/* Save the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK ) this_region->basemesh = astClone( result ); + + } + + } else if( astOK ) { + astError( AST__BADIN, "astRegBaseMesh(%s): The boundary of " + "the supplied %s has an undefined length.", status, + astGetClass( this ), astGetClass( this ) ); + } + + } + +/* Free resources. */ + frm = astAnnul( frm ); + lens = astFree( lens ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* Polygon member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Polygon. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Polygon "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Polygon. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstFrame *frm; /* Base Frame in supplied Polygon */ + AstPointSet *pset1; /* Pointer to copy of supplied PointSet */ + AstPointSet *pset2; /* Pointer to PointSet holding resolved components */ + AstPolygon *this; /* Pointer to the Polygon structure. */ + AstRegion *tunc; /* Uncertainity Region from "this" */ + double **ptr1; /* Pointer to axis values in "pset1" */ + double **ptr2; /* Pointer to axis values in "pset2" */ + double **vptr; /* Pointer to axis values at vertices */ + double *safe; /* An interior point in "this" */ + double edge_len; /* Length of current edge */ + double end[2]; /* Position of end of current edge */ + double l1; /* Length of bounding box diagonal */ + double l2; /* Length of bounding box diagonal */ + double lbnd_tunc[2]; /* Lower bounds of "this" uncertainty Region */ + double lbnd_unc[2]; /* Lower bounds of supplied uncertainty Region */ + double par; /* Parallel component */ + double parmax; /* Max acceptable parallel component */ + double prp; /* Perpendicular component */ + double start[2]; /* Position of start of current edge */ + double ubnd_tunc[2]; /* Upper bounds of "this" uncertainty Region */ + double ubnd_unc[2]; /* Upper bounds of supplied uncertainty Region */ + double wid; /* Width of acceptable margin around polygon */ + int *m; /* Pointer to next mask value */ + int i; /* Edge index */ + int ip; /* Point index */ + int np; /* No. of supplied points */ + int nv; /* No. of vertices */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the Polygon structure. */ + this = (AstPolygon *) this_region; + +/* Check the supplied PointSet has 2 axis values per point. */ + if( astGetNcoord( pset ) != 2 && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axis " + "values per point (%d) in the supplied PointSet - should be " + "2 (internal AST programming error).", status, astGetClass( this ), + astGetNcoord( pset ) ); + } + +/* Get the number of axes in the uncertainty Region and check it is also 2. */ + if( unc && astGetNaxes( unc ) != 2 && astOK ) { + astError( AST__INTER, "astRegPins(%s): Illegal number of axes (%d) " + "in the supplied uncertainty Region - should be 2 " + "(internal AST programming error).", status, astGetClass( this ), + astGetNaxes( unc ) ); + } + +/* Get pointers to the axis values at the polygon vertices. */ + vptr = astGetPoints( this_region->points ); + +/* Get the number of vertices. */ + nv = astGetNpoint( this_region->points ); + +/* Take a copy of the supplied PointSet and get pointers to its axis + values,and its size */ + pset1 = astCopy( pset ); + ptr1 = astGetPoints( pset1 ); + np = astGetNpoint( pset1 ); + +/* Create a PointSet to hold the resolved components and get pointers to its + axis data. */ + pset2 = astPointSet( np, 2, "", status ); + ptr2 = astGetPoints( pset2 ); + +/* Create a mask array if required. */ + if( mask ) *mask = astMalloc( sizeof(int)*(size_t) np ); + +/* Get the centre of the region in the base Frame. We use this as a "safe" + interior point within the region. */ + safe = astRegCentre( this, NULL, NULL, 0, AST__BASE ); + +/* We now find the maximum distance on each axis that a point can be from the + boundary of the Polygon for it still to be considered to be on the boundary. + First get the Region which defines the uncertainty within the Polygon + being checked (in its base Frame), re-centre it on the interior point + found above (to avoid problems if the uncertainty region straddles a + discontinuity), and get its bounding box. The current Frame of the + uncertainty Region is the same as the base Frame of the Polygon. */ + tunc = astGetUncFrm( this, AST__BASE ); + if( safe ) astRegCentre( tunc, safe, NULL, 0, AST__CURRENT ); + astGetRegionBounds( tunc, lbnd_tunc, ubnd_tunc ); + +/* Find the geodesic length within the base Frame of "this" of the diagonal of + the bounding box. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + l1 = astDistance( frm, lbnd_tunc, ubnd_tunc ); + +/* Also get the Region which defines the uncertainty of the supplied + points and get its bounding box. First re-centre the uncertainty at the + interior position to avoid problems from uncertainties that straddle a + discontinuity. */ + if( unc ) { + if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT ); + astGetRegionBounds( unc, lbnd_unc, ubnd_unc ); + +/* Find the geodesic length of the diagonal of this bounding box. */ + l2 = astDistance( frm, lbnd_unc, ubnd_unc ); + +/* Assume zero uncertainty if no "unc" Region was supplied. */ + } else { + l2 = 0.0; + } + +/* The required border width is half of the total diagonal of the two bounding + boxes. */ + if( astOK ) { + wid = 0.5*( l1 + l2 ); + +/* Loop round each edge of the polygon. Edge "i" starts at vertex "i-1" + and ends at vertex "i". Edge zero starts at vertex "nv-1" and ends at + vertex zero. */ + start[ 0 ] = vptr[ 0 ][ nv - 1 ]; + start[ 1 ] = vptr[ 1 ][ nv - 1 ]; + for( i = 0; i < nv; i++ ) { + end[ 0 ] = vptr[ 0 ][ i ]; + end[ 1 ] = vptr[ 1 ][ i ]; + +/* Find the length of this edge. */ + edge_len = astDistance( frm, start, end ); + +/* Resolve all the supplied mesh points into components parallel and + perpendicular to this edge. */ + (void) astResolvePoints( frm, start, end, pset1, pset2 ); + +/* A point is effectively on this edge if the parallel component is + greater than (-wid) and less than (edge_len+wid) AND the perpendicular + component has an absolute value less than wid. Identify such positions + and set them bad in pset1. */ + parmax = edge_len + wid; + for( ip = 0; ip < np; ip++ ) { + par = ptr2[ 0 ][ ip ]; + prp = ptr2[ 1 ][ ip ]; + + if( par != AST__BAD && prp != AST__BAD ) { + if( par > -wid && par < parmax && prp > -wid && prp < wid ) { + ptr1[ 0 ][ ip ] = AST__BAD; + ptr1[ 1 ][ ip ] = AST__BAD; + } + } + } + +/* The end of the current edge becomes the start of the next. */ + start[ 0 ] = end[ 0 ]; + start[ 1 ] = end[ 1 ]; + } + +/* See if any good points are left in pset1. If so, it means that those + points were not on any edge of hte Polygon. We use two alogorithms + here depending on whether we are creating a mask array, since we can + abort the check upon finding the first good point if we are not + producing a mask. */ + result = 1; + if( mask ) { + m = *mask; + for( ip = 0; ip < np; ip++, m++ ) { + if( ptr1[ 0 ][ ip ] != AST__BAD && + ptr1[ 1 ][ ip ] != AST__BAD ) { + *m = 0; + result = 0; + } else { + *m = 1; + } + } + } else { + for( ip = 0; ip < np; ip++ ) { + if( ptr1[ 0 ][ ip ] != AST__BAD && + ptr1[ 1 ][ ip ] != AST__BAD ) { + result = 0; + break; + } + } + } + } + +/* Free resources. */ + tunc = astAnnul( tunc ); + frm = astAnnul( frm ); + safe = astFree( safe ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr, + int *status ){ +/* +*+ +* Name: +* RegTrace + +* Purpose: +* Return requested positions on the boundary of a 2D Region. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* int astTraceRegion( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* Polygon member function (overrides the astTraceRegion method +* inherited from the parent Region class). + +* Description: +* This function returns positions on the boundary of the supplied +* Region, if possible. The required positions are indicated by a +* supplied list of scalar parameter values in the range zero to one. +* Zero corresponds to some arbitrary starting point on the boundary, +* and one corresponds to the end (which for a closed region will be +* the same place as the start). + +* Parameters: +* this +* Pointer to the Region. +* n +* The number of positions to return. If this is zero, the function +* returns without action (but the returned function value still +* indicates if the method is supported or not). +* dist +* Pointer to an array of "n" scalar parameter values in the range +* 0 to 1.0. +* ptr +* A pointer to an array of pointers. The number of elements in +* this array should equal tthe number of axes in the Frame spanned +* by the Region. Each element of the array should be a pointer to +* an array of "n" doubles, in which to return the "n" values for +* the corresponding axis. The contents of the arrays are unchanged +* if the supplied Region belongs to a class that does not +* implement this method. + +* Returned Value: +* Non-zero if the astTraceRegion method is implemented by the class +* of Region supplied, and zero if not. + +*- +*/ + +/* Local Variables; */ + AstFrame *frm; + AstMapping *map; + AstPointSet *bpset; + AstPointSet *cpset; + AstPolygon *this; + double **bptr; + double d; + double p[ 2 ]; + int i; + int j0; + int j; + int ncur; + int nv; + int monotonic; + +/* Check inherited status, and the number of points to return, returning + a non-zero value to indicate that this class supports the astRegTrace + method. */ + if( ! astOK || n == 0 ) return 1; + +/* Get a pointer to the Polygon structure. */ + this = (AstPolygon *) this_region; + +/* Ensure cached information is available. */ + Cache( this, status ); + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* We first determine the required positions in the base Frame of the + Region, and then transform them into the current Frame. Get the + base->current Mapping, and the number of current Frame axes. */ + map = astGetMapping( this_region->frameset, AST__BASE, AST__CURRENT ); + +/* If it's a UnitMap we do not need to do the transformation, so put the + base Frame positions directly into the supplied arrays. */ + if( astIsAUnitMap( map ) ) { + bpset = NULL; + bptr = ptr; + ncur = 2; + +/* Otherwise, create a PointSet to hold the base Frame positions (known + to be 2D since this is an polygon). */ + } else { + bpset = astPointSet( n, 2, " ", status ); + bptr = astGetPoints( bpset ); + ncur = astGetNout( map ); + } + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Get the number of vertices. */ + nv = astGetNpoint( this_region->points ); + +/* If we have a reasonable number of pointsand there are a reasonable + number of vertices, we can be quicker if we know if the parameteric + distances are monotonic increasing. Find out now. */ + if( n > 5 && nv > 5 ) { + + monotonic = 1; + for( i = 1; i < n; i++ ) { + if( dist[ i ] < dist[ i - 1 ] ) { + monotonic = 0; + break; + } + } + + } else { + monotonic = 0; + } + +/* Loop round each point. */ + j0 = 1; + for( i = 0; i < n; i++ ) { + +/* Get the required round the polygon, starting from vertex zero. */ + d = dist[ i ]*this->totlen; + +/* Loop round each vertex until we find one which is beyond the required + point. If the supplied distances are monotonic increasing, we can + start the checks at the same vertex that was used for the previous + since we know there will never be a backward step. */ + for( j = j0; j < nv; j++ ) { + if( this->startsat[ j ] > d ) break; + } + +/* If the distances are monotonic increasing, record the vertex that we + have reached so far. */ + if( monotonic ) j0 = j; + +/* Find the distance to travel beyond the previous vertex. */ + d -= this->startsat[ j - 1 ]; + +/* Find the position, that is the required distance from the previous + vertex towards the next vertex. */ + astLineOffset( frm, this->edges[ j - 1 ], d, 0.0, p ); + +/* Store the resulting axis values. */ + bptr[ 0 ][ i ] = p[ 0 ]; + bptr[ 1 ][ i ] = p[ 1 ]; + } + } + +/* If required, transform the base frame positions into the current + Frame, storing them in the supplied array. Then free resources. */ + if( bpset ) { + cpset = astPointSet( n, ncur, " ", status ); + astSetPoints( cpset, ptr ); + + (void) astTransform( map, bpset, 1, cpset ); + + cpset = astAnnul( cpset ); + bpset = astAnnul( bpset ); + } + +/* Free remaining resources. */ + map = astAnnul( map ); + frm = astAnnul( frm ); + +/* Return a non-zero value to indicate that this class supports the + astRegTrace method. */ + return 1; +} + +static Segment *RemoveFromChain( Segment *head, Segment *seg, int *status ){ +/* +* Name: +* RemoveFromChain + +* Purpose: +* Remove a Segment from the link list maintained by astDownsize. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* Segment *RemoveFromChain( Segment *head, Segment *seg, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* The supplied Segment is removed form the list, and the gap is +* closed up. + +* Parameters: +* head +* The Segment structure at the head of the list. +* seg +* The Segment to be removed from the list. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the link head (which will have changed if "seg" was the +* original head). + +*/ + +/* Check the global error status. */ + if ( !astOK ) return head; + +/* If the Segment was the original head, make the next segment the new + head. */ + if( head == seg ) head = seg->next; + +/* Close up the links between the Segments on either side of the segment + being removed. */ + if( seg->prev ) seg->prev->next = seg->next; + if( seg->next ) seg->next->prev = seg->prev; + +/* Nullify the links in the segment being removed. */ + seg->next = NULL; + seg->prev = NULL; + +/* Return the new head. */ + return head; +} + +static void ResetCache( AstRegion *this_region, int *status ){ +/* +* Name: +* ResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void ResetCache( AstRegion *this, int *status ) + +* Class Membership: +* Region member function (overrides the astResetCache method +* inherited from the parent Region class). + +* Description: +* This function clears cached information from the supplied Region +* structure. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPolygon *this; + int i; + int nv; + +/* Get a pointer to the Polygon structure. */ + this = (AstPolygon *) this_region; + +/* If a Polygon was supplied, indicate cached information needs to be + recalculated. */ + if( this ) { + this->stale = 1; + this->lbnd[ 0 ] = AST__BAD; + +/* Free any edge structures (number of vertices may be about to change so + this cannot be left until the next call to "Cache()". */ + if( this->edges ) { + nv = astGetNpoint( this_region->points ); + for( i = 0; i < nv; i++ ) { + this->edges[ i ] = astFree( this->edges[ i ] ); + } + this->edges = astFree( this->edges ); + } + +/* Clear the cache of the parent class. */ + (*parent_resetcache)( this_region, status ); + } +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Polygon member function (extends the astSetAttrib method inherited from +* the Region class). + +* Description: +* This function assigns an attribute value for a Polygon, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the Polygon. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void +*/ + +/* Local Vaiables: */ + AstPolygon *this; /* Pointer to the Polygon structure */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Polygon structure. */ + this = (AstPolygon *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* SimpVertices. */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "simpvertices= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetSimpVertices( this, ival ); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetPointSet( AstPolygon *this, AstPointSet *pset, int *status ){ +/* +* Name: +* SetPointSet + +* Purpose: +* Store a new set of vertices in an existing Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void SetPointSet( AstPolygon *this, AstPointSet *pset, int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* The PointSet in the supplied Polygon is annulled, and replaced by a +* clone of the supplied PointSet pointer. + +* Parameters: +* this +* Pointer to the Polygon to be changed. +* pset +* The PointSet containing the new vertex information. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Indicate the cached information in the polygon will need to be + re-calculated when needed. */ + astResetCache( this ); + +/* Annul the pointer to the PointSet already in the supplied Polygon. */ + (void) astAnnul( ((AstRegion *) this)->points ); + +/* Store a clone of the supplied new PointSet pointer. */ + ((AstRegion *) this)->points = astClone( pset ); + +} + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* Polygon method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Indicate cached information eeds re-calculating. */ + astResetCache( this_region ); + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Polygon method (over-rides the astSimplify method inherited +* from the Region class). + +* Description: +* This function invokes the parent Region Simplify method, and then +* performs any further region-specific simplification. +* +* If the Mapping from base to current Frame is not a UnitMap, this +* will include attempting to fit a new Region to the boundary defined +* in the current Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame */ + AstMapping *map; /* Base -> current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstPointSet *mesh; /* Mesh of current Frame positions */ + AstPointSet *ps2; /* Polygon PointSet in current Frame */ + AstPolygon *newpol; /* New Polygon */ + AstRegion *new; /* Pointer to simplified Region */ + AstRegion *this; /* Pointer to supplied Region structure */ + AstRegion *unc; /* Pointer to uncertainty Region */ + double **ptr2; /* Pointer axis values in "ps2" */ + double *mem; /* Pointer to array holding new vertex coords */ + double *p; /* Pointer to next vertex coords */ + double *q; /* Pointer to next vertex coords */ + int iv; /* Vertex index */ + int nv; /* Number of vertices in polygon */ + int ok; /* Are the new polygon vertices good? */ + int simpler; /* Has some simplication taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the supplied Region structure. */ + this = (AstRegion *) this_mapping; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. */ + new = (AstRegion *) (*parent_simplify)( this_mapping, status ); + +/* Note if any simplification took place. This is assumed to be the case + if the pointer returned by the above call is different to the supplied + pointer. */ + simpler = ( new != this ); + +/* We attempt to simplify the Polygon by re-defining it within its current + Frame. Transforming the Polygon from its base to its current Frame may + result in the region no having the same edges. If required, we test this + by transforming a set of bounds on the Polygon boundary. This can only + be done if the current Frame is 2-dimensional. Also, there is only any + point in doing it if the Mapping from base to current Frame in the + Polygon is not a UnitMap. */ + map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT ); + if( !astIsAUnitMap( map ) && astGetNout( map ) == 2 ) { + +/* Get a pointer to the Frame represented by the Polgon. */ + frm = astGetFrame( new->frameset, AST__CURRENT ); + +/* Get the Region describing the positional uncertainty in this Frame. */ + unc = astGetUncFrm( new, AST__CURRENT ); + +/* Get the positions of the vertices within this Frame. */ + ps2 = astRegTransform( this, this->points, 1, NULL, NULL ); + ptr2 = astGetPoints( ps2 ); + +/* Get the number of vertices. */ + nv = astGetNpoint( ps2 ); + +/* Re-organise the vertex axis values into the form required by the + Polygon constructor function. */ + mem = astMalloc( sizeof( double)*(size_t)( 2*nv ) ); + if( astOK ) { + ok = 1; + p = mem; + q = ptr2[ 0 ]; + for( iv = 0; iv < nv; iv++ ) { + if( ( *(p++) = *(q++) ) == AST__BAD ) ok = 0; + } + q = ptr2[ 1 ]; + for( iv = 0; iv < nv; iv++ ) *(p++) = *(q++); + +/* Create a new Polygon using these transformed vertices. */ + if( ok ) { + newpol = astPolygon( frm, nv, nv, mem, unc, "", status ); + +/* If the SimpVertices attribute is zero, we now check that the + transformation has not bent the edges of the polygon significantly. + If it has, we annul the new Polygon. */ + if( !astGetSimpVertices( this ) ) { + +/* Get a mesh of points covering the Polygon in this Frame. */ + mesh = astRegMesh( new ); + +/* See if all points within the mesh created from the original Polygon fall + on the boundary of the new Polygon, to within the uncertainty of the + Region. If not, annul the new Polgon. */ + if( !astRegPins( newpol, mesh, NULL, NULL ) ) { + newpol = astAnnul( newpol ); + } + +/* Free the mesh. */ + mesh = astAnnul( mesh ); + } + +/* If we still have a new polygon, use the new Polygon in place of the + original Region. */ + if( newpol ) { + (void) astAnnul( new ); + new = (AstRegion *) newpol; + simpler = 1; + } + } + } + +/* Free other resources. */ + frm = astAnnul( frm ); + unc = astAnnul( unc ); + ps2 = astAnnul( ps2 ); + mem = astFree( mem ); + } + map = astAnnul( map ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. + If the supplied Region had no uncertainty, ensure the returned Region + has no uncertainty. Otherwise, return a clone of the supplied pointer. */ + if( simpler ){ + astRegOverlay( new, this, 1 ); + result = (AstMapping *) new; + + } else { + new = astAnnul( new ); + result = astClone( this ); + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void SmoothPoly( AstPointSet *pset, int boxsize, double strength, + int *status ) { +/* +* Name: +* SmoothPoly + +* Purpose: +* Smoooth a polygon assuming plane geometry. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void SmoothPoly( AstPointSet *pset, int boxsize, double strength, +* int *status ) + +* Class Membership: +* Polygon member function + +* Description: +* This function smooths a polygon, without changing the number of +* vertices. It assumes plane geometry, so should not be used to +* smooth polygons defined within a SkyFrame or CmpFrame. +* +* Each vertex is replaced by a new vertex determined as follows: the +* mean X and Y axis value of the vertices in a section of the polygon +* centred on the vertex being replaced are found (the length of the +* section is given by parameter "boxsize"). The new vertex position +* is then the weighted mean of this mean (X,Y) position and the old +* vertex position. The weight for the mean (X,Y) position is given +* by parameter "strength", and the weight for the old vertex +* position is (1.0 - strength) + +* Parameters: +* pset +* A PointSet holding the polygon vertices. +* boxsize +* Half width of the box filter, given as a number of vertices. +* strength +* The weight to use for the mean (X,Y) position when finding each +* new vertex position. Should be in the range 0.0 to 1.0. A value +* of zero results in no change being made to the polygon. A value +* of 1.0 results in the returned polygon being fully smoothed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double **ptr; + double *newx; + double *newy; + double *nx; + double *ny; + double *oldx; + double *oldy; + double *ox; + double *oy; + double *px; + double *py; + double *qx; + double *qy; + double a; + double b; + double sx; + double sy; + int half_width; + int i; + int nv; + int top; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the number of vertices. */ + nv = astGetNpoint( pset ); + +/* Get pointers to arrays holding the supplied vertex positions. */ + ptr = astGetPoints( pset ); + oldx = ptr[ 0 ]; + oldy = ptr[ 1 ]; + +/* Allocate arrays to hold the returned vertex positions. */ + newx = astMalloc( nv*sizeof( double ) ); + newy = astMalloc( nv*sizeof( double ) ); + +/* Check these pointers can be used safely. */ + if( astOK ) { + +/* Get weighting factors for the fully smoothed and unsmoothed positions. */ + a = strength; + b = 1.0 - a; + +/* Ensure the box size is sufficiently small for there to be room for + two boxes along the polygon. */ + half_width = nv/4 - 1; + if( boxsize < half_width ) half_width = boxsize; + if( half_width < 1 ) half_width = 1; + +/* Modify the weight for the fully smoothed position to include the + normalisation factor needed to account for the box width. */ + a /= 2*half_width + 1; + +/* Find the sum of the x and y coordinates within a box centred on the + first vertex. This includes vertices from the end of the polygon. */ + px = oldx + 1; + qx = oldx + nv; + sx = (oldx)[ 0 ]; + + py = oldy + 1; + qy = oldy + nv; + sy = (oldy)[ 0 ]; + + for( i = 0; i < half_width; i++ ) { + sx += *(px++) + *(--qx); + sy += *(py++) + *(--qy); + } + +/* Replacing vertices within the first half box will include vertices at + both ends of the polygon. Set up the pointers accordingly, and then + find replacements for each vertex in the first half box.*/ + ox = oldx; + oy = oldy; + nx = newx; + ny = newy; + for( i = 0; i < half_width; i++ ) { + +/* Form the new vertex (x,y) values as the weighted mean of the mean + (x,y) values in the box, and the old (x,y) values. */ + *(nx++) = a*sx + b*( *(ox++) ); + *(ny++) = a*sy + b*( *(oy++) ); + +/* Add in the next vertex X and Y axis values to the running sums, and + remove the position that has now passed out of the box. */ + sx += *(px++) - *(qx++); + sy += *(py++) - *(qy++); + } + +/* Adjust the pointer for the rest of the polygon, up to one half box away + from the end. In this section, the smoothing box does not touch either + end of the polygon. */ + top = nv - half_width - 1; + qx = oldx; + qy = oldy; + for( ; i < top; i++ ){ + +/* Form the new vertex (x,y) values as the weighted mean of the mean + (x,y) values in the box, and the old (x,y) values. */ + *(nx++) = a*sx + b*( *(ox++) ); + *(ny++) = a*sy + b*( *(oy++) ); + +/* Add in the next vertex X and Y axis values to the running sums, and + remove the position that has now passed out of the box. */ + sx += *(px++) - *(qx++); + sy += *(py++) - *(qy++); + } + +/* Now do the last half box (which includes vertices from the start of + the polygon). */ + top = nv; + px = oldx; + py = oldy; + for( ; i < top; i++ ){ + *(nx++) = a*sx + b*( *(ox++) ); + *(ny++) = a*sy + b*( *(oy++) ); + sx += *(px++) - *(qx++); + sy += *(py++) - *(qy++); + } + +/* Replace the data points in the PointSet. */ + ptr[ 0 ] = newx; + ptr[ 1 ] = newy; + oldx = astFree( oldx ); + oldy = astFree( oldy ); + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Polygon member function (over-rides the astTestAttrib protected +* method inherited from the Region class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Polygon's attributes. + +* Parameters: +* this +* Pointer to the Polygon. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPolygon *this; /* Pointer to the Polygon structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Polygon structure. */ + this = (AstPolygon *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* SimpVertices. */ +/* ------------- */ + if ( !strcmp( attrib, "simpvertices" ) ) { + result = astTestSimpVertices( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +/* +* Name: +* TraceEdge + +* Purpose: +* Find a point that is inside the required outline. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* void TraceEdge( value, const array[], +* const int lbnd[ 2 ], const int ubnd[ 2 ], +* int iv0, int ix0, int iy0, int starpix, +* int full, int *status ); + +* Class Membership: +* Polygon member function + +* Description: +* This function forms a polygon enclosing the region of the data +* array specified by and "value". If this polygon contains +* the point "(inx,iny)", then a PointSet is returned holding the +* pixel coordinates of the Polygon vertices. If the polygon +* does not contain "(inx,iny)", a NULL pointer is returned. +* +* Each vertex in the polygon corresponds to a corner of a pixel in +* the data array. + +* Parameters: +* value +* The data value defining valid pixels. +* array +* The data array. +* lbnd +* The lower pixel index bounds of the array. +* ubnd +* The upper pixel index bounds of the array. +* iv0 +* The vector index of a pixel inside the region such that the +* pixel to the right is NOT inside the region. This defines the +* start of the polygon. +* ix0 +* The X pixel index of the pixel specified by "iv0". +* inx +* The X pixel index of a point which must be inside the polygon +* for the polygon to be acceptable. +* iny +* The Y pixel index of a point which must be inside the polygon +* for the polygon to be acceptable. +* starpix +* If non-zero, the usual Starlink definition of pixel coordinate +* is used (integral values at pixel corners). Otherwise, the +* system used by other AST functions such as astResample is used +* (integral values at pixel centres). +* full +* If non-zero, the full polygon is stored. If zero, vertices in the +* middle of straight sections of the Polygon are omitted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a PointSet holding the vertices of the polygon, or +* NULL if the polygon did not contain "(inx,iny)". + +* Notes: +* - must be one of LT, LE, EQ, GE, GT, NE. + + +*/ + +/* Define a macro to implement the function for a specific data + type and operation. */ +#define MAKE_TRACEEDGE(X,Xtype,Oper,OperI) \ +static AstPointSet *TraceEdge##Oper##X( Xtype value, const Xtype array[], \ + const int lbnd[ 2 ], const int ubnd[ 2 ], \ + int iv0, int ix0, int iy0, \ + int starpix, int full, \ + int *status ){ \ +\ +/* Local Variables: */ \ + AstPointSet *result; /* Pointer to text describing oper */ \ + const Xtype *pa; /* Pointer to current valid pixel value */ \ + const Xtype *pb; /* Pointer to neigbouring valid pixel value */ \ + const Xtype *pc; /* Pointer to neigbouring valid pixel value */ \ + double *ptr[ 2 ]; /* PointSet data pointers */ \ + double *xvert; /* Pointer to array holding vertex X axis values */ \ + double *yvert; /* Pointer to array holding vertex Y axis values */ \ + double dx; /* Pertubation in X (pixels) to avoid the pixel edge */ \ + double dy; /* Pertubation in Y (pixels) to avoid the pixel edge */ \ + double xx; /* Pixel X coord at corner */ \ + double yy; /* Pixel Y coord at corner */ \ + int at; /* The pixel corner to draw to */ \ + int done; /* Have we arrived back at the start of the polygon? */ \ + int ii; /* Index of new vertex */ \ + int ix; /* X pixel index of current valid pixel */ \ + int iy; /* Y pixel index of current valid pixel */ \ + int nright; /* Overall number of right hand turns along polygon */ \ + int nvert; /* Number of vertices */ \ + int nx; /* Pixels per row */ \ +\ +/* Initialise */ \ + result = NULL; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +\ +/* Initialise pointers to arrays holding the X and Y pixel coordinates at \ + the vertices of the polygon. */ \ + xvert = NULL; \ + yvert = NULL; \ + nvert = 0; \ +\ +/* Find number of pixels in one row of the array. */ \ + nx = ( ubnd[ 0 ] - lbnd[ 0 ] + 1 ); \ +\ +/* The four corners of a pixel are numbered as follows: 0=bottom left, \ + 1=top left, 2=top right, 3=bottom right. The following algorithm moves \ + along pixel edges, from corner to corner, using the above numbering \ + scheme to identify the corners. We start the polygon by moving from the \ + bottom right to the top right corner of pixel "(ix0,iy0)". */ \ + ix = ix0; \ + iy = iy0; \ + at = 2; \ +\ +/* Store a pointer to the first good pixel value. */ \ + pa = array + ( ix - lbnd[ 0 ] ) + nx*( iy - lbnd[ 1 ] ) ; \ +\ +/* We count the number of times the polygon turns to the right compared \ + to the left. Initialise it to zero. */ \ + nright = 0; \ +\ +/* Loop round tracing out the polygon until we arrive back at the \ + beginning. The Polygon class requires that the inside of the polygon \ + is to the left as we move round the polygon in an anti-clockwise \ + direction. So at each corner, we attempt to move to the next \ + anti-clockwise good pixel corner. */ \ + done = 0; \ + while( !done ) { \ +\ +/* If we have arrived at the bottom left corner of the good pixel, we must \ + have come from the top left corner since all movements around a pixel \ + must be anti-clockwise. */ \ + if( at == 0 ) { \ +\ +/* Note the pixel coordinates at the bottom left corner of the current \ + pixel. */ \ + if( starpix ) { \ + xx = ix - 1.0; \ + yy = iy - 1.0; \ + } else { \ + xx = ix - 0.5; \ + yy = iy - 0.5; \ + } \ +\ +/* Get a pointer to lower left pixel value */ \ + pb = pa - nx - 1; \ +\ +/* Get a pointer to lower mid pixel value. */ \ + pc = pb + 1; \ +\ +/* If the lower left pixel is within the array and meets the validity \ + requirements, move to the left along its top edge. */ \ + if( iy > lbnd[ 1 ] && ix > lbnd[ 0 ] && ISVALID(*pb,OperI,value) ) { \ + nright++; \ + pa = pb; \ + at = 1; \ + ix--; \ + iy--; \ + dx = DELTA; \ + dy = -DELTA; \ +\ +/* Otherwise, if lower mid pixel is good, move down its left edge. */ \ + } else if( iy > lbnd[ 1 ] && ISVALID(*pc,OperI,value) ) { \ + pa = pc; \ + at = 0; \ + iy--; \ + dx = DELTA; \ + dy = 0.0; \ +\ +/* Otherwise, move to the right along the bottom edge of the current pixel. */ \ + } else { \ + nright--; \ + at = 3; \ + dx = DELTA; \ + dy = DELTA; \ + } \ +\ +/* If the polygon bends at this point, or if we will be smoothing the \ + polygon, append the pixel coordinates at this pixel corner to the \ + polygon. */ \ + if( full || pa != pc ) ADD( xx, yy ); \ +\ +/* If we have arrived at the top left corner of the good pixel, we must \ + have come from the top right corner. */ \ + } else if( at == 1 ) { \ +\ +/* Note the pixel coordinates at the top left corner of the current \ + pixel. */ \ + if( starpix ) { \ + xx = ix - 1.0; \ + yy = iy; \ + } else { \ + xx = ix - 0.5; \ + yy = iy + 0.5; \ + } \ +\ +/* Get a pointer to upper left pixel value */ \ + pb = pa + nx - 1; \ +\ +/* Get a pointer to mid left pixel value. */ \ + pc = pa - 1; \ +\ +/* If upper left pixel is good, move up its left edge. */ \ + if( iy < ubnd[ 1 ] && ix > lbnd[ 0 ] && ISVALID(*pb,OperI,value) ) { \ + nright++; \ + pa = pb; \ + at = 2; \ + ix--; \ + iy++; \ + dx = -DELTA; \ + dy = -DELTA; \ +\ +/* Otherwise, if left mid pixel is good, move left along its top edge. */ \ + } else if( ix > lbnd[ 0 ] && ISVALID(*pc,OperI,value) ) { \ + pa = pc; \ + at = 1; \ + ix--; \ + dx = 0.0; \ + dy = -DELTA; \ +\ +/* Otherwise, move down the left edge of the current pixel. */ \ + } else { \ + nright--; \ + at = 0; \ + dx = DELTA; \ + dy = -DELTA; \ + } \ +\ +/* If the polygon bends at this point, or if we will be smoothing the \ + polygon, append the pixel coordinates at this pixel corner to the \ + polygon. */ \ + if( full || pa != pc ) ADD( xx, yy ); \ +\ +/* If we have arrived at the top right corner of the good pixel, we must \ + have come from the bottom right corner. */ \ + } else if( at == 2 ) { \ +\ +/* Note the pixel coordinates at the top right corner of the current \ + pixel. */ \ + if( starpix ) { \ + xx = ix; \ + yy = iy; \ + } else { \ + xx = ix + 0.5; \ + yy = iy + 0.5; \ + } \ +\ +/* Pointer to upper right pixel value */ \ + pb = pa + nx + 1; \ +\ +/* Pointer to top mid pixel value. */ \ + pc = pa + nx; \ +\ +/* If upper right pixel is good, move right along its bottom edge. */ \ + if( iy < ubnd[ 1 ] && ix < ubnd[ 0 ] && ISVALID(*pb,OperI,value) ){ \ + nright++; \ + pa = pb; \ + at = 3; \ + ix++; \ + iy++; \ + dx = -DELTA; \ + dy = DELTA; \ +\ +/* Otherwise, if upper mid pixel is good, move up its right edge. */ \ + } else if( iy < ubnd[ 1 ] && ISVALID(*pc,OperI,value) ) { \ + pa = pc; \ + at = 2; \ + iy++; \ + dx = -DELTA; \ + dy = 0.0; \ +\ +/* Otherwise, move left along the top edge of the current pixel. */ \ + } else { \ + nright--; \ + at = 1; \ + dx = -DELTA; \ + dy = -DELTA; \ + } \ +\ +/* If the polygon bends at this point, or if we will be smoothing the \ + polygon, append the pixel coordinates at this pixel corner to the \ + polygon. */ \ + if( full || pa != pc ) ADD( xx, yy ); \ +\ +/* Arrived at bottom right corner of good pixel from lower left. */ \ + } else { \ +\ +/* Note the pixel coordinates at the bottom right corner of the current \ + pixel. */ \ + if( starpix ) { \ + xx = ix; \ + yy = iy - 1.0; \ + } else { \ + xx = ix + 0.5; \ + yy = iy - 0.5; \ + } \ +\ +/* Pointer to lower right pixel value */ \ + pb = pa - ( nx - 1 ); \ +\ +/* Pointer to mid right pixel value. */ \ + pc = pa + 1; \ +\ +/* If lower right pixel is good, move down its left edge. */ \ + if( iy > lbnd[ 1 ] && ix < ubnd[ 0 ] && ISVALID(*pb,OperI,value) ) { \ + nright++; \ + pa = pb; \ + at = 0; \ + ix++; \ + iy--; \ + dx = DELTA; \ + dy = DELTA; \ +\ +/* Otherwise, if right mid pixel is good, move right along its lower edge. */ \ + } else if( ix < ubnd[ 0 ] && ISVALID(*pc,OperI,value) ) { \ + pa = pc; \ + at = 3; \ + ix++; \ + dx = 0.0; \ + dy = DELTA; \ +\ +/* Otherwise, move up the right edge of the current pixel. */ \ + } else { \ + nright--; \ + at = 2; \ + dx = -DELTA; \ + dy = DELTA; \ + } \ +\ +/* If the polygon bends at this point, or if we will be smoothing the \ + polygon, append the pixel coordinates at this pixel corner to the \ + polygon. */ \ + if( full || pa != pc ) ADD( xx, yy ); \ + } \ +\ +/* If we have arrived back at the start, break out of the loop. */ \ + if( ix == ix0 && iy == iy0 && at == 2 ) done = 1; \ + } \ +\ +/* If we have circled round to the right, the polygon will not enclosed \ + the specified position, so free resources and return a NULL pointer. */ \ + if( nright > 0 ) { \ + xvert = astFree( xvert ); \ + yvert = astFree( yvert ); \ +\ +/* If we have circled round to the left, the polygon will enclose \ + the specified position, so create and return a PointSet. */ \ + } else { \ + result = astPointSet( nvert, 2, " ", status ); \ + ptr[ 0 ] = xvert; \ + ptr[ 1 ] = yvert; \ + astSetPoints( result, ptr ); \ + } \ +\ +/* Annul the returned PointSet if anythign went wrong. */ \ + if( !astOK && result ) result = astAnnul( result ); \ +\ +/* Return the PointSet pointer. */ \ + return result; \ +} + +/* Define a macro to add a vertex position to dynamically allocated + arrays of X and Y positions. We offset the supplied position by + a small fraction of a pixel towards the centre of hte polygon to + avoid placing vertices exactly on the edge, which may cause problems + later for pixels that are on the edge of an area of bad pixel. */ +#define ADD(X,Y) {\ + ii = nvert++; \ + xvert = (double *) astGrow( xvert, nvert, sizeof( double ) ); \ + yvert = (double *) astGrow( yvert, nvert, sizeof( double ) ); \ + if( astOK ) { \ + xvert[ ii ] = (X+dx); \ + yvert[ ii ] = (Y+dy); \ + } \ +} + +/* Define a macro that uses the above macro to to create implementations + of TraceEdge for all operations. */ +#define MAKEALL_TRACEEDGE(X,Xtype) \ +MAKE_TRACEEDGE(X,Xtype,LT,AST__LT) \ +MAKE_TRACEEDGE(X,Xtype,LE,AST__LE) \ +MAKE_TRACEEDGE(X,Xtype,EQ,AST__EQ) \ +MAKE_TRACEEDGE(X,Xtype,NE,AST__NE) \ +MAKE_TRACEEDGE(X,Xtype,GE,AST__GE) \ +MAKE_TRACEEDGE(X,Xtype,GT,AST__GT) + +/* Expand the above macro to generate a function for each required + data type and operation. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKEALL_TRACEEDGE(LD,long double) +#endif +MAKEALL_TRACEEDGE(D,double) +MAKEALL_TRACEEDGE(L,long int) +MAKEALL_TRACEEDGE(UL,unsigned long int) +MAKEALL_TRACEEDGE(I,int) +MAKEALL_TRACEEDGE(UI,unsigned int) +MAKEALL_TRACEEDGE(S,short int) +MAKEALL_TRACEEDGE(US,unsigned short int) +MAKEALL_TRACEEDGE(B,signed char) +MAKEALL_TRACEEDGE(UB,unsigned char) +MAKEALL_TRACEEDGE(F,float) + +/* Undefine the macros. */ +#undef MAKE_TRACEEDGE +#undef MAKEALL_TRACEEDGE + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a Polygon to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Polygon member function (over-rides the astTransform protected +* method inherited from the Region class). + +* Description: +* This function takes a Polygon and a set of points encapsulated in a +* PointSet and transforms the points by setting axis values to +* AST__BAD for all points which are outside the region. Points inside +* the region are copied unchanged from input to output. + +* Parameters: +* this +* Pointer to the Polygon. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - The forward and inverse transformations are identical for a +* Region. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of axes in the Frame represented by the Polygon. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to base Frame in FrameSet */ + AstLineDef *a; /* Line from inside point to test point */ + AstLineDef *b; /* Polygon edge */ + AstPointSet *in_base; /* PointSet holding base Frame input positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + AstPolygon *this; /* Pointer to Polygon */ + double **ptr_in; /* Pointer to input base Frame coordinate data */ + double **ptr_out; /* Pointer to output current Frame coordinate data */ + double *px; /* Pointer to array of first axis values */ + double *py; /* Pointer to array of second axis values */ + double p[ 2 ]; /* Current test position */ + int closed; /* Is the boundary part of the Region? */ + int i; /* Edge index */ + int icoord; /* Coordinate index */ + int in_region; /* Is the point inside the Region? */ + int ncoord_out; /* No. of current Frame axes */ + int ncross; /* Number of crossings */ + int neg; /* Has the Region been negated? */ + int npoint; /* No. of input points */ + int nv; /* No. of vertices */ + int point; /* Loop counter for input points */ + int pos; /* Is test position in, on, or outside boundary? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the Polygon structure. */ + this = (AstPolygon *) this_mapping; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, + containing a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* Get the number of points to be transformed. */ + npoint = astGetNpoint( result ); + +/* Get a pointer to the output axis values. */ + ptr_out = astGetPoints( result ); + +/* Find the number of axes in the current Frame. This need not be 2 (the + number of axes in the *base* Frame must be 2 however). */ + ncoord_out = astGetNcoord( result ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet to transform the supplied positions + from the current Frame in the encapsulated FrameSet (the Frame + represented by the Region), to the base Frame (the Frame in which the + Region is defined). This call also returns a pointer to the base Frame + of the encapsulated FrameSet. Note, the returned pointer may be a + clone of the "in" pointer, and so we must be carefull not to modify the + contents of the returned PointSet. */ + in_base = astRegTransform( this, in, 0, NULL, &frm ); + ptr_in = astGetPoints( in_base ); + +/* Get the number of vertices in the polygon. */ + nv = astGetNpoint( ((AstRegion *) this)->points ); + +/* See if the boundary is part of the Region. */ + closed = astGetClosed( this ); + +/* See if the Region has been negated. */ + neg = astGetNegated( this ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + px = ptr_in[ 0 ]; + py = ptr_in[ 1 ]; + +/* Loop round each supplied point in the base Frame of the polygon. */ + for ( point = 0; point < npoint; point++, px++, py++ ) { + +/* If the input point is bad, indicate that bad output values should be + returned. */ + if( *px == AST__BAD || *py == AST__BAD ) { + in_region = 0; + +/* Otherwise, we first determine if the point is inside, outside, or on, + the Polygon boundary. Initialially it is unknown. */ + } else { + +/* Ensure cached information is available.*/ + Cache( this, status ); + +/* Create a definition of the line from a point which is inside the + polygon to the supplied point. This is a structure which includes + cached intermediate information which can be used to speed up + subsequent calculations. */ + p[ 0 ] = *px; + p[ 1 ] = *py; + a = astLineDef( frm, this->in, p ); + +/* We now determine the number of times this line crosses the polygon + boundary. Initialise the number of crossings to zero. */ + ncross = 0; + pos = UNKNOWN; + +/* Loop rouind all edges of the polygon. */ + for( i = 0; i < nv; i++ ) { + b = this->edges[ i ]; + +/* If this point is on the current edge, then we need do no more checks + since we know it is either inside or outside the polygon (depending on + whether the polygon is closed or not). */ + if( astLineContains( frm, b, 0, p ) ) { + pos = ON; + break; + +/* Otherwise, see if the two lines cross within their extent. If so, + increment the number of crossings. */ + } else if( astLineCrossing( frm, b, a, NULL ) ) { + ncross++; + } + } + +/* Free resources */ + a = astFree( a ); + +/* If the position is not on the boundary, it is inside the boundary if + the number of crossings is even, and outside otherwise. */ + if( pos == UNKNOWN ) pos = ( ncross % 2 == 0 )? IN : OUT; + +/* Whether the point is in the Region depends on whether the point is + inside the polygon boundary, whether the Polygon has been negated, and + whether the polygon is closed. */ + if( neg ) { + if( pos == IN ) { + in_region = 0; + } else if( pos == OUT ) { + in_region = 1; + } else if( closed ) { + in_region = 1; + } else { + in_region = 0; + } + + } else { + if( pos == IN ) { + in_region = 1; + } else if( pos == OUT ) { + in_region = 0; + } else if( closed ) { + in_region = 1; + } else { + in_region = 0; + } + } + } + +/* If the point is not inside the Region, store bad output values. */ + if( !in_region ) { + for ( icoord = 0; icoord < ncoord_out; icoord++ ) { + ptr_out[ icoord ][ point ] = AST__BAD; + } + } + } + } + +/* Free resources */ + in_base = astAnnul( in_base ); + frm = astAnnul( frm ); + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* SimpVertices + +* Purpose: +* Simplify a Polygon by transforming its vertices? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls the behaviour of the +c astSimplify +f AST_SIMPLIFY +* method when applied to a Polygon. The simplified Polygon is created +* by transforming the vertices from the Frame in which the Polygon +* was originally defined into the Frame currently represented by the +* Polygon. If SimpVertices is non-zero (the default) then this +* simplified Polygon is returned without further checks. If SimpVertices +* is zero, a check is made that the edges of the new Polygon do not +* depart significantly from the edges of the original Polygon (as +* determined by the uncertainty associated with the Polygon). This +* could occur, for instance, if the Mapping frrm the original to the +* current Frame is highly non-linear. If this check fails, the +* original unsimplified Polygon is returned without change. + +* Applicability: +* Polygon +* All Polygons have this attribute. + +*att-- +*/ +astMAKE_CLEAR(Polygon,SimpVertices,simp_vertices,-INT_MAX) +astMAKE_GET(Polygon,SimpVertices,int,0,( ( this->simp_vertices != -INT_MAX ) ? + this->simp_vertices : 1 )) +astMAKE_SET(Polygon,SimpVertices,int,simp_vertices,( value != 0 )) +astMAKE_TEST(Polygon,SimpVertices,( this->simp_vertices != -INT_MAX )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Polygon objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Polygon objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstPolygon *out; /* Pointer to output Polygon */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the output Polygon. */ + out = (AstPolygon *) objout; + +/* For safety, first clear any references to the input memory from + the output Polygon. */ + out->edges = NULL; + out->startsat = NULL; + +/* Indicate cached information needs nre-calculating. */ + astResetCache( (AstPolygon *) out ); +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Polygon objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Polygon objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPointSet *ps; /* Pointer to PointSet inside Region */ + AstPolygon *this; /* Pointer to Polygon */ + int i; /* Index of vertex */ + int istat; /* Original AST error status */ + int nv; /* Number of vertices */ + int rep; /* Original error reporting state */ + +/* Obtain a pointer to the Polygon structure. */ + this = (AstPolygon *) obj; + +/* Annul all resources. */ + ps = ((AstRegion *) this)->points; + if( this->edges && ps ) { + +/* Ensure we get a value for "nv" even if an error has occurred. */ + istat = astStatus; + astClearStatus; + rep = astReporting( 0 ); + + nv = astGetNpoint( ps ); + + astSetStatus( istat ); + astReporting( rep ); + +/* Free the structures holding the edge information. */ + for( i = 0; i < nv; i++ ) { + this->edges[ i ] = astFree( this->edges[ i ] ); + } + this->edges = astFree( this->edges ); + this->startsat = astFree( this->startsat ); + + } +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Polygon objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Polygon class to an output Channel. + +* Parameters: +* this +* Pointer to the Polygon whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPolygon *this; /* Pointer to the Polygon structure */ + int ival; /* Integer attribute value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Polygon structure. */ + this = (AstPolygon *) this_object; + +/* Write out values representing the instance variables for the + Polygon class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* SimpVertices. */ +/* ------------ */ +/* Write out the forward-inverse simplification flag. */ + set = TestSimpVertices( this, status ); + ival = set ? GetSimpVertices( this, status ) : astGetSimpVertices( this ); + astWriteInt( channel, "SimpVT", set, 0, ival, "Simplify by transforming vertices?" ); + +/* A flag indicating the convention used for determining the interior of + the polygon. A zero value indicates that the old AST system is in + use (inside to the left when moving anti-clockwise round the vertices + as viewed from the outside of the celestial sphere). A non-zero value + indicates the STC system is in use (inside to the left when moving + anti-clockwise round the vertices as viewed from the inside of the + celestial sphere). AST currently uses the STC system. */ + astWriteInt( channel, "Order", 1, 0, 1, "Polygon uses STC vertex order convention" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPolygon and astCheckPolygon functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Polygon,Region) +astMAKE_CHECK(Polygon) + +AstPolygon *astPolygon_( void *frame_void, int npnt, int dim, + const double *points, AstRegion *unc, + const char *options, int *status, ...) { +/* +*++ +* Name: +c astPolygon +f AST_POLYGON + +* Purpose: +* Create a Polygon. + +* Type: +* Public function. + +* Synopsis: +c #include "polygon.h" +c AstPolygon *astPolygon( AstFrame *frame, int npnt, int dim, +c const double *points, AstRegion *unc, +c const char *options, ... ) +f RESULT = AST_POLYGON( FRAME, NPNT, DIM, POINTS, UNC, OPTIONS, STATUS ) + +* Class Membership: +* Polygon constructor. + +* Description: +* This function creates a new Polygon object and optionally initialises +* its attributes. +* +* The Polygon class implements a polygonal area, defined by a +* collection of vertices, within a 2-dimensional Frame. The vertices +* are connected together by geodesic curves within the encapsulated Frame. +* For instance, if the encapsulated Frame is a simple Frame then the +* geodesics will be straight lines, but if the Frame is a SkyFrame then +* the geodesics will be great circles. Note, the vertices must be +* supplied in an order such that the inside of the polygon is to the +* left of the boundary as the vertices are traversed. Supplying them +* in the reverse order will effectively negate the polygon. +* +* Within a SkyFrame, neighbouring vertices are always joined using the +* shortest path. Thus if an edge of 180 degrees or more in length is +* required, it should be split into section each of which is less +* than 180 degrees. The closed path joining all the vertices in order +* will divide the celestial sphere into two disjoint regions. The +* inside of the polygon is the region which is circled in an +* anti-clockwise manner (when viewed from the inside of the celestial +* sphere) when moving through the list of vertices in the order in +* which they were supplied when the Polygon was created (i.e. the +* inside is to the left of the boundary when moving through the +* vertices in the order supplied). + +* Parameters: +c frame +f FRAME = INTEGER (Given) +* A pointer to the Frame in which the region is defined. It must +* have exactly 2 axes. A deep copy is taken of the supplied Frame. +* This means that any subsequent changes made to the Frame using the +* supplied pointer will have no effect the Region. +c npnt +f NPNT = INTEGER (Given) +* The number of points in the Region. +c dim +f DIM = INTEGER (Given) +c The number of elements along the second dimension of the "points" +f The number of elements along the first dimension of the POINTS +* array (which contains the point coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +c given should not be less than "npnt". +f given should not be less than NPNT. +c points +f POINTS( DIM, 2 ) = DOUBLE PRECISION (Given) +c The address of the first element of a 2-dimensional array of +c shape "[2][dim]" giving the physical coordinates of the vertices. +c These should be stored such that the value of coordinate +c number "coord" for point number "pnt" is found in element +c "in[coord][pnt]". +f A 2-dimensional array giving the physical coordinates of the +f vertices. These should be stored such that the value of coordinate +f number COORD for point number PNT is found in element IN(PNT,COORD). +c unc +f UNC = INTEGER (Given) +* An optional pointer to an existing Region which specifies the +* uncertainties associated with the boundary of the Polygon being created. +* The uncertainty in any point on the boundary of the Polygon is found by +* shifting the supplied "uncertainty" Region so that it is centred at +* the boundary point being considered. The area covered by the +* shifted uncertainty Region then represents the uncertainty in the +* boundary position. The uncertainty is assumed to be the same for +* all points. +* +* If supplied, the uncertainty Region must be of a class for which +* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.) +* or be a Prism containing centro-symetric component Regions. A deep +* copy of the supplied Region will be taken, so subsequent changes to +* the uncertainty Region using the supplied pointer will have no +* effect on the created Polygon. Alternatively, +f a null Object pointer (AST__NULL) +c a NULL Object pointer +* may be supplied, in which case a default uncertainty is used +* equivalent to a box 1.0E-6 of the size of the Polygon being created. +* +* The uncertainty Region has two uses: 1) when the +c astOverlap +f AST_OVERLAP +* function compares two Regions for equality the uncertainty +* Region is used to determine the tolerance on the comparison, and 2) +* when a Region is mapped into a different coordinate system and +* subsequently simplified (using +c astSimplify), +f AST_SIMPLIFY), +* the uncertainties are used to determine if the transformed boundary +* can be accurately represented by a specific shape of Region. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Polygon. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Polygon. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPolygon() +f AST_POLYGON = INTEGER +* A pointer to the new Polygon. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPolygon *new; /* Pointer to new Polygon */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the supplied Frame structure. */ + frame = astCheckFrame( frame_void ); + +/* Initialise the Polygon, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPolygon( NULL, sizeof( AstPolygon ), !class_init, + &class_vtab, "Polygon", frame, npnt, + dim, points, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Polygon's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Polygon. */ + return new; +} + +AstPolygon *astPolygonId_( void *frame_void, int npnt, int dim, + const double *points, void *unc_void, + const char *options, ... ) { +/* +* Name: +* astPolygonId_ + +* Purpose: +* Create a Polygon. + +* Type: +* Private function. + +* Synopsis: +* #include "polygon.h" +* AstPolygon *astPolygonId_( void *frame_void, int npnt, +* int dim, const double *points, void *unc_void, +* const char *options, ... ) + +* Class Membership: +* Polygon constructor. + +* Description: +* This function implements the external (public) interface to the +* astPolygon constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astPolygon_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astPolygon_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astPolygon_. + +* Returned Value: +* The ID value associated with the new Polygon. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *frame; /* Pointer to Frame structure */ + AstPolygon *new; /* Pointer to new Polygon */ + AstRegion *unc; /* Pointer to Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Frame pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Frame. */ + frame = astVerifyFrame( astMakePointer( frame_void ) ); + +/* Obtain a Region pointer from the supplied "unc" ID and validate the + pointer to ensure it identifies a valid Region . */ + unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL; + +/* Initialise the Polygon, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPolygon( NULL, sizeof( AstPolygon ), !class_init, + &class_vtab, "Polygon", frame, npnt, dim, + points, unc ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Polygon's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Polygon. */ + return astMakeId( new ); +} + + +AstPolygon *astInitPolygon_( void *mem, size_t size, int init, AstPolygonVtab *vtab, + const char *name, AstFrame *frame, int npnt, + int dim, const double *points, AstRegion *unc, int *status ) { +/* +*+ +* Name: +* astInitPolygon + +* Purpose: +* Initialise a Polygon. + +* Type: +* Protected function. + +* Synopsis: +* #include "polygon.h" +* AstPolygon *astInitPolygon( void *mem, size_t size, int init, AstPolygonVtab *vtab, +* const char *name, AstFrame *frame, int npnt, +* int dim, const double *points, AstRegion *unc ) + +* Class Membership: +* Polygon initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Polygon object. It allocates memory (if necessary) to accommodate +* the Polygon plus any additional data associated with the derived class. +* It then initialises a Polygon structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Polygon at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Polygon is to be initialised. +* This must be of sufficient size to accommodate the Polygon data +* (sizeof(Polygon)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Polygon (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Polygon +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Polygon's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Polygon. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* frame +* A pointer to the Frame in which the region is defined. +* npnt +* The number of points in the Region. +* dim +* The number of elements along the second dimension of the "points" +* array (which contains the point coordinates). This value is +* required so that the coordinate values can be correctly +* located if they do not entirely fill this array. The value +* given should not be less than "npnt". +* points +* The address of the first element of a 2-dimensional array of +* shape "[2][dim]" giving the physical coordinates of the +* points. These should be stored such that the value of coordinate +* number "coord" for point number "pnt" is found in element +* "in[coord][pnt]". +* unc +* A pointer to a Region which specifies the uncertainty in the +* supplied positions (all points in the new Polygon being +* initialised are assumed to have the same uncertainty). A NULL +* pointer can be supplied, in which case default uncertainties equal +* to 1.0E-6 of the dimensions of the new Polygon's bounding box are +* used. If an uncertainty Region is supplied, it must be either a Box, +* a Circle or an Ellipse, and its encapsulated Frame must be related +* to the Frame supplied for parameter "frame" (i.e. astConvert +* should be able to find a Mapping between them). Two positions +* the "frame" Frame are considered to be co-incident if their +* uncertainty Regions overlap. The centre of the supplied +* uncertainty Region is immaterial since it will be re-centred on the +* point being tested before use. A deep copy is taken of the supplied +* Region. + +* Returned Value: +* A pointer to the new Polygon. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPolygon *new; /* Pointer to new Polygon */ + AstPointSet *pset; /* Pointer to PointSet holding points */ + const double *q; /* Pointer to next supplied axis value */ + double **ptr; /* Pointer to data in pset */ + double *p; /* Pointer to next PointSet axis value */ + int i; /* Axis index */ + int j; /* Point index */ + int nin; /* No. of axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPolygonVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the number of axis values per position is correct. */ + nin = astGetNaxes( frame ); + if( nin != 2 ) { + astError( AST__BADIN, "astInitPolygon(%s): The supplied %s has %d " + "axes - polygons must have exactly 2 axes.", status, name, + astGetClass( frame ), nin ); + +/* If so create a PointSet and store the supplied points in it. Check + none are bad. */ + } else { + pset = astPointSet( npnt, 2, "", status ); + ptr = astGetPoints( pset ); + for( i = 0; i < 2 && astOK; i++ ) { + p = ptr[ i ]; + q = points + i*dim; + for( j = 0; j < npnt; j++ ) { + if( (*(p++) = *(q++)) == AST__BAD ) { + astError( AST__BADIN, "astInitPolygon(%s): One or more " + "bad axis values supplied for the vertex " + "number %d.", status, name, j + 1 ); + break; + } + } + } + +/* Initialise a Region structure (the parent class) as the first component + within the Polygon structure, allocating memory if necessary. */ + new = (AstPolygon *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frame, pset, unc ); + if ( astOK ) { + +/* Initialise the Polygon data. */ +/* ------------------------------ */ + new->lbnd[ 0 ] = AST__BAD; + new->ubnd[ 0 ] = AST__BAD; + new->lbnd[ 1 ] = AST__BAD; + new->ubnd[ 1 ] = AST__BAD; + new->simp_vertices = -INT_MAX; + new->edges = NULL; + new->startsat = NULL; + new->totlen = 0.0; + new->acw = 1; + new->stale = 1; + +/* Ensure the vertices are stored such that the unnegated Polygon + represents the inside of the polygon. */ + EnsureInside( new, status ); + +/* If an error occurred, clean up by deleting the new Polygon. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Free resources. */ + pset = astAnnul( pset ); + + } + +/* Return a pointer to the new Polygon. */ + return new; +} + +AstPolygon *astLoadPolygon_( void *mem, size_t size, AstPolygonVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPolygon + +* Purpose: +* Load a Polygon. + +* Type: +* Protected function. + +* Synopsis: +* #include "polygon.h" +* AstPolygon *astLoadPolygon( void *mem, size_t size, AstPolygonVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Polygon loader. + +* Description: +* This function is provided to load a new Polygon using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Polygon structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Polygon at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Polygon is to be +* loaded. This must be of sufficient size to accommodate the +* Polygon data (sizeof(Polygon)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Polygon (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Polygon structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPolygon) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Polygon. If this is NULL, a pointer +* to the (static) virtual function table for the Polygon class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Polygon" is used instead. + +* Returned Value: +* A pointer to the new Polygon. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPolygon *new; /* Pointer to the new Polygon */ + int order; /* Is the new (STC) order convention used? */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Polygon. In this case the + Polygon belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPolygon ); + vtab = &class_vtab; + name = "Polygon"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPolygonVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Polygon. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Polygon" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + + new->simp_vertices = astReadInt( channel, "simpvt", -INT_MAX ); + if ( TestSimpVertices( new, status ) ) SetSimpVertices( new, new->simp_vertices, status ); + +/* A flag indicating what order the vertices are stored in. See the Dump + function. */ + order = astReadInt( channel, "order", 0 ); + +/* Initialise other class properties. */ + new->lbnd[ 0 ] = AST__BAD; + new->ubnd[ 0 ] = AST__BAD; + new->lbnd[ 1 ] = AST__BAD; + new->ubnd[ 1 ] = AST__BAD; + new->edges = NULL; + new->startsat = NULL; + new->totlen = 0.0; + new->acw = 1; + new->stale = 1; + +/* If the order in which the vertices were written used the old AST + convention, negate the Polygon so that it is consistent with the + current conevtion (based on STC). */ + if( ! order ) astNegate( new ); + +/* Ensure the vertices are stored such that the unnegated Polygon + represents the inside of the polygon. */ + EnsureInside( new, status ); + +/* If an error occurred, clean up by deleting the new Polygon. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Polygon pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + +AstPolygon *astDownsize_( AstPolygon *this, double maxerr, int maxvert, + int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Polygon,Downsize))( this, maxerr, maxvert, status ); +} + + diff --git a/polygon.h b/polygon.h new file mode 100644 index 0000000..2cd46b6 --- /dev/null +++ b/polygon.h @@ -0,0 +1,353 @@ +#if !defined( POLYGON_INCLUDED ) /* Include this file only once */ +#define POLYGON_INCLUDED +/* +*+ +* Name: +* polygon.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Polygon class. + +* Invocation: +* #include "polygon.h" + +* Description: +* This include file defines the interface to the Polygon class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Polygon class implements a Region which represents a collection +* of points in a Frame. + +* Inheritance: +* The Polygon class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-OCT-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "frame.h" /* Coordinate systems */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "timeframe.h" /* For AST__LT definition */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Flags used to indicate how astOutline should define the pixel + region to be outlined. We omit AST__LT here since it is defined in + timeframe.h (with value 11). */ +#define AST__LE 2 +#define AST__EQ 3 +#define AST__GE 4 +#define AST__GT 5 +#define AST__NE 6 + +/* Type Definitions. */ +/* ================= */ +/* Polygon structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstPolygon { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double in[2]; /* A point which is inside the polygon */ + double lbnd[2]; /* Lower axis limits of bounding box */ + double ubnd[2]; /* Upper axis limits of bounding box */ + AstLineDef **edges; /* Cached description of edges */ + double *startsat; /* Perimeter distance to each vertex */ + double totlen; /* Total perimeter distance round polygon */ + int acw; /* Are vertices stored in anti-clockwise order? */ + int stale; /* Is cached information stale? */ + int simp_vertices; /* Simplify by transforming vertices? */ +} AstPolygon; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPolygonVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstPolygon *(* Downsize)( AstPolygon *, double, int, int * ); + + int (* GetSimpVertices)( AstPolygon *, int * ); + int (* TestSimpVertices)( AstPolygon *, int * ); + void (* ClearSimpVertices)( AstPolygon *, int * ); + void (* SetSimpVertices)( AstPolygon *, int, int * ); + +} AstPolygonVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstPolygonGlobals { + AstPolygonVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 51 ]; +} AstPolygonGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitPolygonGlobals_( AstPolygonGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Polygon) /* Check class membership */ +astPROTO_ISA(Polygon) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPolygon *astPolygon_( void *, int, int, const double *, AstRegion *, const char *, int *, ...); +#else +AstPolygon *astPolygonId_( void *, int, int, const double *, AstRegion *, const char *, ... )__attribute__((format(printf,6,7))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPolygon *astInitPolygon_( void *, size_t, int, AstPolygonVtab *, const char *, AstFrame *, int, int, const double *, AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitPolygonVtab_( AstPolygonVtab *, const char *, int * ); + +/* Loader. */ +AstPolygon *astLoadPolygon_( void *, size_t, AstPolygonVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstPolygon *astDownsize_( AstPolygon *, double, int, int * ); + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +AstPolygon *astOutlineLD_( long double, int, const long double[], const int[2], const int[2], double, int, const int[2], int, int * ); +#endif +AstPolygon *astOutlineB_( signed char, int, const signed char[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineD_( double, int, const double[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineF_( float, int, const float[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineI_( int, int, const int[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineL_( long int, int, const long int[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineS_( short int, int, const short int[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineUB_( unsigned char, int, const unsigned char[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineUI_( unsigned int, int, const unsigned int[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineUL_( unsigned long int, int, const unsigned long int[], const int[2], const int[2], double, int, const int[2], int, int * ); +AstPolygon *astOutlineUS_( unsigned short int, int, const unsigned short int[], const int[2], const int[2], double, int, const int[2], int, int * ); + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +AstPolygon *astConvexLD_( long double, int, const long double[], const int[2], const int[2], int, int * ); +#endif +AstPolygon *astConvexB_( signed char, int, const signed char[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexD_( double, int, const double[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexF_( float, int, const float[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexI_( int, int, const int[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexL_( long int, int, const long int[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexS_( short int, int, const short int[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexUB_( unsigned char, int, const unsigned char[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexUI_( unsigned int, int, const unsigned int[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexUL_( unsigned long int, int, const unsigned long int[], const int[2], const int[2], int, int * ); +AstPolygon *astConvexUS_( unsigned short int, int, const unsigned short int[], const int[2], const int[2], int, int * ); + +# if defined(astCLASS) /* Protected */ +int astGetSimpVertices_( AstPolygon *, int * ); +int astTestSimpVertices_( AstPolygon *, int * ); +void astClearSimpVertices_( AstPolygon *, int * ); +void astSetSimpVertices_( AstPolygon *, int, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPolygon(this) astINVOKE_CHECK(Polygon,this,0) +#define astVerifyPolygon(this) astINVOKE_CHECK(Polygon,this,1) + +/* Test class membership. */ +#define astIsAPolygon(this) astINVOKE_ISA(Polygon,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astPolygon astINVOKE(F,astPolygon_) +#else +#define astPolygon astINVOKE(F,astPolygonId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPolygon(mem,size,init,vtab,name,frame,npnt,indim,points,unc) \ +astINVOKE(O,astInitPolygon_(mem,size,init,vtab,name,frame,npnt,indim,points,unc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPolygonVtab(vtab,name) astINVOKE(V,astInitPolygonVtab_(vtab,name,STATUS_PTR)) + +/* Loader. */ +#define astLoadPolygon(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPolygon_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckPolygon to validate Polygon pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astDownsize(this,maxerr,maxvert) \ +astINVOKE(O,astDownsize_(astCheckPolygon(this),maxerr,maxvert,STATUS_PTR)) + + + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define astOutlineLD(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineLD_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#endif + +#define astOutlineB(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineB_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineD(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineD_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineF(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineF_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineI(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineI_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineL(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineL_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineS(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineS_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineUB(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineUB_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineUI(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineUI_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineUL(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineUL_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) +#define astOutlineUS(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix) \ +astINVOKE(O,astOutlineUS_(value,oper,array,lbnd,ubnd,maxerr,maxvert,inside,starpix,STATUS_PTR)) + + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define astConvexLD(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O,astConvexLD_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#endif + +#define astConvexB(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexB_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexD(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexD_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexF(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexF_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexI(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexI_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexL(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexL_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexS(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexS_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexUB(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexUB_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexUI(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexUI_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexUL(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexUL_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) +#define astConvexUS(value,oper,array,lbnd,ubnd,starpix) \ +astINVOKE(O, astConvexUS_(value,oper,array,lbnd,ubnd,starpix,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astClearSimpVertices(this) \ +astINVOKE(V,astClearSimpVertices_(astCheckPolygon(this),STATUS_PTR)) +#define astGetSimpVertices(this) \ +astINVOKE(V,astGetSimpVertices_(astCheckPolygon(this),STATUS_PTR)) +#define astSetSimpVertices(this,value) \ +astINVOKE(V,astSetSimpVertices_(astCheckPolygon(this),value,STATUS_PTR)) +#define astTestSimpVertices(this) \ +astINVOKE(V,astTestSimpVertices_(astCheckPolygon(this),STATUS_PTR)) +#endif +#endif + + + + + diff --git a/polymap.c b/polymap.c new file mode 100644 index 0000000..5265b95 --- /dev/null +++ b/polymap.c @@ -0,0 +1,5506 @@ +/* +*class++ +* Name: +* PolyMap + +* Purpose: +* Map coordinates using polynomial functions. + +* Constructor Function: +c astPolyMap +f AST_POLYMAP + +* Description: +* A PolyMap is a form of Mapping which performs a general polynomial +* transformation. Each output coordinate is a polynomial function of +* all the input coordinates. The coefficients are specified separately +* for each output coordinate. The forward and inverse transformations +* are defined independantly by separate sets of coefficients. If no +* inverse transformation is supplied, an iterative method can be used +* to evaluate the inverse based only on the forward transformation. + +* Inheritance: +* The PolyMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* PolyMap also has the following attributes: +* +* - IterInverse: Provide an iterative inverse transformation? +* - NiterInverse: Maximum number of iterations for iterative inverse +* - TolInverse: Target relative error for iterative inverse + +* Functions: +c In addition to those functions applicable to all Objects, the +c following functions may also be applied to all Mappings: +f In addition to those routines applicable to all Objects, the +f following routines may also be applied to all Mappings: +* +c - astPolyTran: Fit a PolyMap inverse or forward transformation +f - AST_POLYTRAN: Fit a PolyMap inverse or forward transformation + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2011 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 27-SEP-2003 (DSB): +* Original version. +* 13-APR-2005 (DSB): +* Changed the keys used by the Dump/astLoadPolyMap functions. They +* used to exceed 8 characters and consequently caused problems for +* FitsChans. +* 20-MAY-2005 (DSB): +* Correct the indexing of keywords produced in the Dump function. +* 20-APR-2006 (DSB): +* Guard against undefined transformations in Copy. +* 10-MAY-2006 (DSB): +* Override astEqual. +* 4-JUL-2008 (DSB): +* Fixed loop indexing problems in Equal function. +* 27-MAY-2011 (DSB): +* Added public method astPolyTran. +* 18-JUL-2011 (DSB): +* - Added attributes IterInverse, NiterInverse and TolInverse. +* - Do not report an error if astPolyTran fails to fit an inverse. +* 15-OCT-2011 (DSB): +* Improve argument checking and error reporting in PolyTran +* 8-MAY-2014 (DSB): +* Move to using CMinPack for minimisations. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS PolyMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "cmpmap.h" /* Compound mappings */ +#include "polymap.h" /* Interface definition for this class */ +#include "unitmap.h" /* Unit mappings */ +#include "cminpack/cminpack.h" /* Levenberg - Marquardt minimization */ +#include "pal.h" /* SLALIB function definitions */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(PolyMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(PolyMap,Class_Init) +#define class_vtab astGLOBAL(PolyMap,Class_Vtab) +#define getattrib_buff astGLOBAL(LutMap,GetAttrib_Buff) + +#include + + +#else + +static char getattrib_buff[ 101 ]; + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPolyMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + + +/* Type Definitions */ +/* ================ */ + +/* Structure used to pass data to the Levenberg - Marquardt non-linear + minimization algorithm. */ +typedef struct MinPackData { + int order; /* Max power of X1 or X2, plus one. */ + int nsamp; /* No. of polynomial samples to fit */ + int init_jac; /* Has the constant Jacobian been found yet? */ + double *xp1; /* Pointer to powers of X1 (1st poly i/p) at all samples */ + double *xp2; /* Pointer to powers of X2 (2nd poly i/p) at all samples */ + double *y[ 2 ]; /* Pointers to Y1 and Y2 values at all samples */ +} MinPackData; + + + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPolyMap *astPolyMapId_( int, int, int, const double[], int, const double[], const char *, ... ); + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstPolyMap **GetJacobian( AstPolyMap *, int * ); +static AstPolyMap *PolyTran( AstPolyMap *, int, double, double, int, const double *, const double *, int * ); +static double **SamplePoly1D( AstPolyMap *, int, double **, double, double, int, int *, double[2], int * ); +static double **SamplePoly2D( AstPolyMap *, int, double **, const double *, const double *, int, int *, double[4], int * ); +static double *FitPoly1D( int, double, int, double **, double[2], int *, double *, int * ); +static double *FitPoly2D( int, double, int, double **, double[4], int *, double *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); +static int GetTranForward( AstMapping *, int * ); +static int GetTranInverse( AstMapping *, int * ); +static int MPFunc1D( void *, int, int, const double *, double *, double *, int, int ); +static int MPFunc2D( void *, int, int, const double *, double *, double *, int, int ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int ReplaceTransformation( AstPolyMap *, int, double, double, int, const double *, const double *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *obj, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void FreeArrays( AstPolyMap *, int, int * ); +static void IterInverse( AstPolyMap *, AstPointSet *, AstPointSet *, int * ); +static void LMFunc1D( const double *, double *, int, int, void * ); +static void LMFunc2D( const double *, double *, int, int, void * ); +static void LMJacob1D( const double *, double *, int, int, void * ); +static void LMJacob2D( const double *, double *, int, int, void * ); +static void StoreArrays( AstPolyMap *, int, int, const double *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static int GetIterInverse( AstPolyMap *, int * ); +static int TestIterInverse( AstPolyMap *, int * ); +static void ClearIterInverse( AstPolyMap *, int * ); +static void SetIterInverse( AstPolyMap *, int, int * ); + +static int GetNiterInverse( AstPolyMap *, int * ); +static int TestNiterInverse( AstPolyMap *, int * ); +static void ClearNiterInverse( AstPolyMap *, int * ); +static void SetNiterInverse( AstPolyMap *, int, int * ); + +static double GetTolInverse( AstPolyMap *, int * ); +static int TestTolInverse( AstPolyMap *, int * ); +static void ClearTolInverse( AstPolyMap *, int * ); +static void SetTolInverse( AstPolyMap *, double, int * ); + + +/* Member functions. */ +/* ================= */ + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* PolyMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the PolyMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPolyMap *this; /* Pointer to the PolyMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PolyMap structure. */ + this = (AstPolyMap *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* IterInverse. */ +/* ------------ */ + if ( !strcmp( attrib, "iterinverse" ) ) { + astClearIterInverse( this ); + +/* NiterInverse. */ +/* ------------- */ + } else if ( !strcmp( attrib, "niterinverse" ) ) { + astClearNiterInverse( this ); + +/* TolInverse. */ +/* ----------- */ + } else if ( !strcmp( attrib, "tolinverse" ) ) { + astClearTolInverse( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two PolyMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two PolyMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a PolyMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the PolyMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPolyMap *that; + AstPolyMap *this; + int i, j, k; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two PolyMap structures. */ + this = (AstPolyMap *) this_object; + that = (AstPolyMap *) that_object; + +/* Check the second object is a PolyMap. We know the first is a + PolyMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAPolyMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two PolyMaps differ, it may still be possible + for them to be equivalent. First compare the PolyMaps if their Invert + flags are the same. In this case all the attributes of the two PolyMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + + result = 1; + + for( i = 0; i < nout && result; i++ ) { + if( this->ncoeff_f[ i ] != that->ncoeff_f[ i ] || + this->mxpow_i[ i ] != that->mxpow_i[ i ] ) { + result = 0; + } + } + + + if( this->coeff_f && that->coeff_f ) { + for( i = 0; i < nout && result; i++ ) { + for( j = 0; j < this->ncoeff_f[ i ] && result; j++ ) { + if( !astEQUAL( this->coeff_f[ i ][ j ], + that->coeff_f[ i ][ j ] ) ) { + result = 0; + } + } + } + } + + if( this->power_f && that->power_f ) { + for( i = 0; i < nout && result; i++ ) { + for( j = 0; j < this->ncoeff_f[ i ] && result; j++ ) { + for( k = 0; k < nin && result; k++ ) { + if( this->power_f[ i ][ j ][ k ] != + that->power_f[ i ][ j ][ k ] ) { + result = 0; + } + } + } + } + } + + for( i = 0; i < nin && result; i++ ) { + if( this->ncoeff_i[ i ] != that->ncoeff_i[ i ] || + this->mxpow_f[ i ] != that->mxpow_f[ i ] ) { + result = 0; + } + } + + + if( this->coeff_i && that->coeff_i ) { + for( i = 0; i < nin && result; i++ ) { + for( j = 0; j < this->ncoeff_i[ i ] && result; j++ ) { + if( !astEQUAL( this->coeff_i[ i ][ j ], + that->coeff_i[ i ][ j ] ) ) { + result = 0; + } + } + } + } + + if( this->power_i && that->power_i ) { + for( i = 0; i < nin && result; i++ ) { + for( j = 0; j < this->ncoeff_i[ i ] && result; j++ ) { + for( k = 0; k < nout && result; k++ ) { + if( this->power_i[ i ][ j ][ k ] != + that->power_i[ i ][ j ][ k ] ) { + result = 0; + } + } + } + } + } + +/* If the Invert flags for the two PolyMaps differ, the attributes of the two + PolyMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a PolyMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static double *FitPoly1D( int nsamp, double acc, int order, double **table, + double scales[2], int *ncoeff, double *racc, + int *status ){ +/* +* Name: +* FitPoly1D + +* Purpose: +* Fit a (1-in,1-out) polynomial to a supplied set of data. + +* Type: +* Private function. + +* Synopsis: +* double *FitPoly1D( int nsamp, double acc, int order, double **table, +* double scales[2], int *ncoeff, double *racc, +* int *status ) + +* Description: +* This function fits a least squares 1D polynomial curve to the +* positions in a supplied table. For the purposes of this function, +* the polynomial input is refered to as x1 and the output as y1. So +* the polynomial is: +* +* y1 = P1( x1 ) + +* Parameters: +* nsamp +* The number of (x1,y1) positions in the supplied table. +* acc +* The required accuracy, expressed as an offset within the polynomials +* output space. +* order +* The maximum power (plus one) of x1 within P1. So for instance, a +* value of 3 refers to a quadratic polynomial. +* table +* Pointer to an array of 2 pointers. Each of these pointers points +* to an array of "nsamp" doubles, being the scaled and sampled values +* for x1 and y1 in that order. +* scales +* Array holding the scaling factors for the two columns of the table. +* Multiplying the table values by the scale factor produces PolyMap +* input or output axis values. +* ncoeff +* Pointer to an ant in which to return the number of coefficients +* described by the returned array. +* racc +* Pointer to a double in which to return the achieved accuracy +* (which may be greater than "acc"). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to an array of doubles defining the polynomial in the +* form required by the PolyMap contructor. The number of coefficients +* is returned via "ncoeff". If the polynomial could not be found, +* then NULL is returned. The returned pointer should be freed using +* astFree when no longer needed. + +*/ + +/* Local Variables: */ + MinPackData data; + double *coeffs; + double *pc; + double *pr; + double *px1; + double *pxp1; + double *result; + double *work1; + double *work2; + double *work4; + double f1; + double f2; + double maxterm; + double term; + double tv; + double x1; + int *work3; + int info; + int k; + int ncof; + int w1; + +/* Initialise returned value */ + result = NULL; + *ncoeff = 0; + *racc = 10*acc; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Number of coefficients per poly. */ + ncof = order; + +/* Initialise the elements of the structure. */ + data.order = order; + data.nsamp = nsamp; + data.init_jac = 1; + data.xp1 = astMalloc( nsamp*order*sizeof( double ) ); + data.xp2 = NULL; + data.y[ 0 ] = table[ 1 ]; + data.y[ 1 ] = NULL; + +/* Work space to hold coefficients. */ + coeffs = astMalloc( ncof*sizeof( double ) ); + +/* Other work space. */ + work1 = astMalloc( nsamp*sizeof( double ) ); + work2 = astMalloc( ncof*nsamp*sizeof( double ) ); + work3 = astMalloc( ncof*sizeof( int ) ); + work4 = astMalloc( (5*ncof+nsamp)*sizeof( double ) ); + if( astOK ) { + +/* Get pointers to the supplied x1 values. */ + px1 = table[ 0 ]; + +/* Get pointers to the location for the next power of x1. */ + pxp1 = data.xp1; + +/* Loop round all samples. */ + for( k = 0; k < nsamp; k++ ) { + +/* Get the current x1 value. */ + x1 = *(px1++); + +/* Find all the required powers of x1 and store them in the "xp1" + component of the data structure. */ + tv = 1.0; + for( w1 = 0; w1 < order; w1++ ) { + *(pxp1++) = tv; + tv *= x1; + } + } + +/* The initial guess at the coefficient values represents a unit + transformation in PolyMap axis values. */ + for( k = 0; k < ncof; k++ ) coeffs[ k ] = 0.0; + coeffs[ 1 ] = scales[ 0 ]/scales[ 1 ]; + +/* Find the best coefficients */ + info = lmder1( MPFunc1D, &data, nsamp, ncof, coeffs, work1, work2, nsamp, + sqrt(DBL_EPSILON), work3, work4, (5*ncof+nsamp) ); + if( info == 0 ) astError( AST__MNPCK, "astPolyMap(PolyTran): Minpack error " + "detected (possible programming error).", status ); + +/* Return the achieved accuracy. */ + pr = work1; + tv = 0.0; + for( k = 0; k < nsamp; k++,pr++ ) tv += (*pr)*(*pr); + *racc = scales[ 1 ]*sqrt( tv/nsamp ); + +/* The best fitting polynomial coefficient found above relate to the + polynomial between the scaled positions stored in "table". These + scaled positions are related to PolyMap input/output axis values via + the scale factors supplied in "scales". Find the initial factor for the + current output. */ + f1 = scales[ 1 ]; + f2 = 1.0; + +/* Look at each coefficient. */ + pc = coeffs; + for( w1 = 0; w1 < order; w1++,pc++ ) { + +/* Get a pointer to the powers of X1 appropriate for the current coefficient, + at the first sample. */ + pxp1 = data.xp1 + w1; + +/* We find the contribution which this coefficient makes to the total + polynomial value. Find the maximum contribution made at any sample + points. */ + maxterm = 0.0; + for( k = 0; k < nsamp; k++ ) { + +/* Get the absolute value of the polynomial term that uses the current + coefficient. */ + term = fabs( ( *pc )*( *pxp1 ) ); + +/* Update the maximum term found at any sample. */ + if( term > maxterm ) maxterm = term; + +/* Increment the pointers to refer to the next sample. */ + pxp1 += order; + } + +/* If the maximum contribution made by this term is less than the + required accuracy, set the coefficient value to zero. */ + if( maxterm*f1 < acc ) { + *pc = 0.0; + +/* Scale the best fitting polynomial coefficient found above to take + account of the fact that the tabulated input and output positions in + "table" were are not actual PolyMap input and output axis values, but + are scaled by the factors stored in "scales". */ + } else { + *pc *= f1/f2; + } + + f2 *= scales[ 0 ]; + } + +/* Convert the array of coefficients into PolyMap form. */ + result = astMalloc( ncof*3*sizeof( double ) ); + + *ncoeff = 0; + pr = result; + pc = coeffs; + for( w1 = 0; w1 < order; w1++,pc++ ) { + if( *pc != 0.0 ) { + *(pr++) = *pc; + *(pr++) = 1; + *(pr++) = w1; + (*ncoeff)++; + } + } + +/* Truncate the returned array. */ + result = astRealloc( result, (*ncoeff)*3*sizeof( double ) ); + } + +/* Free resources. */ + coeffs = astFree( coeffs ); + data.xp1 = astFree( data.xp1 ); + work1 = astFree( work1 ); + work2 = astFree( work2 ); + work3 = astFree( work3 ); + work4 = astFree( work4 ); + +/* Return the coefficient array. */ + return result; + +} + +static double *FitPoly2D( int nsamp, double acc, int order, double **table, + double scales[4], int *ncoeff, double *racc, + int *status ){ +/* +* Name: +* FitPoly2D + +* Purpose: +* Fit a (2-in,2-out) polynomial to a supplied set of data. + +* Type: +* Private function. + +* Synopsis: +* double *FitPoly2D( int nsamp, double acc, int order, double **table, +* double scales[4], int *ncoeff, double *racc, +* int *status ) + +* Description: +* This function fits a pair of least squares 2D polynomial surfaces +* to the positions in a supplied table. For the purposes of this +* function, the polynomial inputs are refered to as (x1,x2) and the +* outputs as (y1,y2). So the two polynomials are: +* +* y1 = P1( x1, x2 ) +* y2 = P2( x1, x2 ) +* +* P1 and P2 have the same maximum power on each input (specified by +* the "order" parameter). + +* Parameters: +* nsamp +* The number of (x1,x2,y1,y2) positions in the supplied table. +* acc +* The required accuracy, expressed as a geodesic distance within +* the polynomials output space. +* order +* The maximum power (plus one) of x1 or x2 within P1 and P2. So for +* instance, a value of 3 refers to a quadratic polynomial. Note, cross +* terms with total powers greater than or equal to "order" are not +* included in the fit. So the maximum number of terms in the fitted +* polynomial is order*(order+1)/2. +* table +* Pointer to an array of 4 pointers. Each of these pointers points +* to an array of "nsamp" doubles, being the scaled and sampled values +* for x1, x2, y1 or y2 in that order. +* scales +* Array holding the scaling factors for the four columns of the table. +* Multiplying the table values by the scale factor produces PolyMap +* input or output axis values. +* ncoeff +* Pointer to an ant in which to return the number of coefficients +* described by the returned array. +* racc +* Pointer to a double in which to return the achieved accuracy +* (which may be greater than "acc"). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to an array of doubles defining the polynomial in the +* form required by the PolyMap contructor. The number of coefficients +* is returned via "ncoeff". If the polynomial could not be found with +* sufficient accuracy , then NULL is returned. The returned pointer +* should be freed using astFree when no longer needed. + +*/ + +/* Local Variables: */ + MinPackData data; + double *coeffs; + double *pc; + double *pr; + double *px1; + double *px2; + double *pxp1; + double *pxp2; + double *result; + double *work1; + double *work2; + double *work4; + double f1; + double f20; + double f2; + double f3; + double facc; + double maxterm; + double term; + double tv; + double x1; + double x2; + int *work3; + int info; + int iout; + int k; + int ncof; + int w12; + int w1; + int w2; + +/* Initialise returned value */ + result = NULL; + *ncoeff = 0; + *racc = 10*acc; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Number of coefficients per poly. */ + ncof = order*( order + 1 )/2; + +/* Initialise the elements of the structure. */ + data.order = order; + data.nsamp = nsamp; + data.init_jac = 1; + data.xp1 = astMalloc( nsamp*order*sizeof( double ) ); + data.xp2 = astMalloc( nsamp*order*sizeof( double ) ); + data.y[ 0 ] = table[ 2 ]; + data.y[ 1 ] = table[ 3 ]; + +/* Work space to hold coefficients. */ + coeffs = astMalloc( 2*ncof*sizeof( double ) ); + +/* Other work space. */ + work1 = astMalloc( 2*nsamp*sizeof( double ) ); + work2 = astMalloc( 4*ncof*nsamp*sizeof( double ) ); + work3 = astMalloc( 2*ncof*sizeof( int ) ); + work4 = astMalloc( 2*(5*ncof+nsamp)*sizeof( double ) ); + if( astOK ) { + +/* Get pointers to the supplied x1 and x2 values. */ + px1 = table[ 0 ]; + px2 = table[ 1 ]; + +/* Get pointers to the location for the next power of x1 and x2. */ + pxp1 = data.xp1; + pxp2 = data.xp2; + +/* Loop round all samples. */ + for( k = 0; k < nsamp; k++ ) { + +/* Get the current x1 and x2 values. */ + x1 = *(px1++); + x2 = *(px2++); + +/* Find all the required powers of x1 and store them in the "xp1" + component of the data structure. */ + tv = 1.0; + for( w1 = 0; w1 < order; w1++ ) { + *(pxp1++) = tv; + tv *= x1; + } + +/* Find all the required powers of x2 and store them in the "xp2" + comonent of the data structure. */ + tv = 1.0; + for( w2 = 0; w2 < order; w2++ ) { + *(pxp2++) = tv; + tv *= x2; + } + } + +/* The initial guess at the coefficient values represents a unit + transformation in PolyMap axis values. */ + for( k = 0; k < 2*ncof; k++ ) coeffs[ k ] = 0.0; + coeffs[ 1 ] = scales[ 0 ]/scales[ 2 ]; + coeffs[ 5 ] = scales[ 1 ]/scales[ 3 ]; + +/* Find the best coefficients */ + info = lmder1( MPFunc2D, &data, 2*nsamp, 2*ncof, coeffs, work1, work2, + 2*nsamp, sqrt(DBL_EPSILON), work3, work4, 2*(5*ncof+nsamp) ); + if( info == 0 ) astError( AST__MNPCK, "astPolyMap(PolyTran): Minpack error " + "detected (possible programming error).", status ); + +/* Return the achieved accuracy. */ + pr = work1; + tv = 0.0; + for( k = 0; k < 2*nsamp; k++,pr++ ) tv += (*pr)*(*pr); + facc = 1.0/(scales[2]*scales[2]) + 1.0/(scales[3]*scales[3]); + *racc = sqrt( tv/(2*nsamp*facc) ); + +/* Pointer to the first coefficient. */ + pc = coeffs; + +/* Look at coefficients for each output in turn. */ + for( iout = 0; iout < 2 && astOK; iout++ ) { + +/* The best fitting polynomial coefficient found above relate to the + polynomial between the scaled positions stored in "table". These + scaled positions are related to PolyMap input/output axis values via + the scale factors supplied in "scales". Find the initial factor for the + current output. */ + f1 = scales[ 2 + iout ]; + +/* Look at each coefficient for the current output. */ + f20 = 1.0; + for( w12 = 0; w12 < order; w12++ ) { + f3 = 1.0; + f2 = f20; + for( w2 = 0; w2 <= w12; w2++,pc++ ) { + w1 = w12 - w2; + +/* Get pointers to the powers of X1 and X2 appropriate for the current + coefficient, at the first sample. */ + pxp1 = data.xp1 + w1; + pxp2 = data.xp2 + w2; + +/* We find the contribution which this coefficient makes to the total + polynomial value. Find the maximum contribution made at any sample + points. */ + maxterm = 0.0; + for( k = 0; k < nsamp; k++ ) { + +/* Get the absolute value of the polynomial term that uses the current + coefficient. */ + term = fabs( ( *pc )*( *pxp1 )*( *pxp2 ) ); + +/* Update the maximum term found at any sample. */ + if( term > maxterm ) maxterm = term; + +/* Increment the pointers to refer to the next sample. */ + pxp1 += order; + pxp2 += order; + } + +/* If the maximum contribution made by this term is less than the + required accuracy, set the coefficient value to zero. */ + if( maxterm*f1 < acc ) { + *pc = 0.0; + +/* Scale the best fitting polynomial coefficient found above to take + account of the fact that the tabulated input and output positions in + "table" were are not actual PolyMap input and output axis values, but + are scaled by the factors stored in "scales". */ + } else { + *pc *= f1/( f2*f3 ); + } + + f2 /= scales[ 0 ]; + f3 *= scales[ 1 ]; + } + + f20 *= scales[ 0 ]; + } + } + +/* Convert the array of coefficients into PolyMap form. */ + result = astMalloc( 2*ncof*4*sizeof( double ) ); + + *ncoeff = 0; + pr = result; + pc = coeffs; + for( iout = 0; iout < 2 && astOK; iout++ ) { + for( w12 = 0; w12 < order; w12++ ) { + for( w2 = 0; w2 <= w12; w2++,pc++ ) { + w1 = w12 - w2; + if( *pc != 0.0 ) { + *(pr++) = *pc; + *(pr++) = iout + 1; + *(pr++) = w1; + *(pr++) = w2; + (*ncoeff)++; + } + } + } + } + +/* Truncate the returned array. */ + result = astRealloc( result, (*ncoeff)*4*sizeof( double ) ); + } + +/* Free resources. */ + coeffs = astFree( coeffs ); + data.xp1 = astFree( data.xp1 ); + data.xp2 = astFree( data.xp2 ); + work1 = astFree( work1 ); + work2 = astFree( work2 ); + work3 = astFree( work3 ); + work4 = astFree( work4 ); + +/* Return the coefficient array. */ + return result; + +} + +static void FreeArrays( AstPolyMap *this, int forward, int *status ) { +/* +* Name: +* FreeArrays + +* Purpose: +* Free the dynamic arrays contained within a PolyMap + +* Type: +* Private function. + +* Synopsis: +* void FreeArrays( AstPolyMap *this, int forward, int *status ) + +* Description: +* This function frees all the dynamic arrays allocated as part of a +* PolyMap. + +* Parameters: +* this +* Pointer to the PolyMap. +* forward +* If non-zero, the arrays for the forward transformation are freed. +* Otherwise, the arrays for the inverse transformation are freed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + int nin; /* Number of inputs */ + int nout; /* Number of outputs */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Get the number of inputs and outputs of the uninverted Mapping. */ + nin = ( (AstMapping *) this )->nin; + nout = ( (AstMapping *) this )->nout; + +/* Free the dynamic arrays for the forward transformation. */ + if( forward ) { + + if( this->coeff_f ) { + for( i = 0; i < nout; i++ ) { + this->coeff_f[ i ] = astFree( this->coeff_f[ i ] ); + } + this->coeff_f = astFree( this->coeff_f ); + } + + if( this->power_f ) { + for( i = 0; i < nout; i++ ) { + if( this->ncoeff_f && this->power_f[ i ] ) { + for( j = 0; j < this->ncoeff_f[ i ]; j++ ) { + this->power_f[ i ][ j ] = astFree( this->power_f[ i ][ j ] ); + } + } + this->power_f[ i ] = astFree( this->power_f[ i ] ); + } + this->power_f = astFree( this->power_f ); + } + + this->ncoeff_f = astFree( this->ncoeff_f ); + this->mxpow_f = astFree( this->mxpow_f ); + +/* Free the dynamic arrays for the inverse transformation. */ + } else { + + if( this->coeff_i ) { + for( i = 0; i < nin; i++ ) { + this->coeff_i[ i ] = astFree( this->coeff_i[ i ] ); + } + this->coeff_i = astFree( this->coeff_i ); + } + + if(this->power_i ) { + for( i = 0; i < nin; i++ ) { + if( this->ncoeff_i && this->power_i[ i ] ) { + for( j = 0; j < this->ncoeff_i[ i ]; j++ ) { + this->power_i[ i ][ j ] = astFree( this->power_i[ i ][ j ] ); + } + } + this->power_i[ i ] = astFree( this->power_i[ i ] ); + } + this->power_i = astFree( this->power_i ); + } + + this->ncoeff_i = astFree( this->ncoeff_i ); + this->mxpow_i = astFree( this->mxpow_i ); + } +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a PolyMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the PolyMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the PolyMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the PolyMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPolyMap *this; /* Pointer to the PolyMap structure */ + const char *result; /* Pointer value to return */ + double dval; /* Floating point attribute value */ + int ival; /* Integer attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the PolyMap structure. */ + this = (AstPolyMap *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* IterInverse. */ +/* ------------ */ + if ( !strcmp( attrib, "iterinverse" ) ) { + ival = astGetIterInverse( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* NiterInverse. */ +/* ------------- */ + } else if ( !strcmp( attrib, "niterinverse" ) ) { + ival = astGetNiterInverse( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* TolInverse. */ +/* ----------- */ + } else if ( !strcmp( attrib, "tolinverse" ) ) { + dval = astGetTolInverse( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static AstPolyMap **GetJacobian( AstPolyMap *this, int *status ){ +/* +* Name: +* GetJacobian + +* Purpose: +* Get a description of a Jacobian matrix for the fwd transformation +* of a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* AstPolyMap **GetJacobian( AstPolyMap *this, int *status ) + +* Description: +* This function returns a set of PolyMaps which define the Jacobian +* matrix of the forward transformation of the supplied PolyMap. +* +* The Jacobian matrix has "nout" rows and "nin" columns, where "nin" +* and "nout" are the number of inputs and outputs of the supplied PolyMap. +* Row "i", column "j" of the matrix holds the rate of change of the +* i'th PolyMap output with respect to the j'th PolyMap input. +* +* Since the values in the Jacobian matrix vary across the input space +* of the PolyMap, the matrix is returned in the form of a set of new +* PolyMaps which generate the elements of the Jacobian for any given +* position in the input space. The "nout" values in a single column of +* the Jacobian matrix are generated by the "nout" outputs from a single +* new PolyMap. The whole matrix is described by "nin" PolyMaps. +* +* The returned PolyMaps are cached in the supplied PolyMap object in +* order to speed up subsequent calls to this function. + +* Parameters: +* this +* The PolyMap for which the Jacbian is required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to an array of "nin" PolyMap pointers, where "nin" is the number +* of inputs for the sipplied PolyMap. The returned array should not be changed +* in any way, and the PolyMaps should not be freed (they will be freed when +* the supplied PolyMap is deleted). + +*/ + +/* Local Variables: */ + double *coeffs; + double *pc; + int icof; + int icol; + int iin; + int irow; + int ncof; + int ncof_row; + int ncof_total; + int nin; + int nout; + int power; + +/* Check inherited status */ + if( !astOK ) return NULL; + +/* Ensure there is a Jacobian stored in the PolyMap. */ + if( !this->jacobian ) { + +/* Get the number of inputs and outputs. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + +/* Allocate memory to hold pointers to the PolyMaps used to describe the + Jacobian matrix. */ + this->jacobian = astCalloc( nin, sizeof(AstPolyMap *) ); + +/* Find the total number of coefficients used to describe the supplied + PolyMap (forward transformation) and allocate work space to hold the + coefficients for a single new PolyMap forward transformation. */ + ncof = 0; + for( irow = 0; irow < nout; irow++ ) { + ncof += this->ncoeff_f[ irow ]; + } + coeffs = astMalloc( ncof*( 2 + nin )*sizeof( double ) ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* The Jacobian matrix has "nout" rows and "nin" columns. The "nout" values + in a single column of the Jacobian matrix corresponds to the "nout" outputs + from a single PolyMap. The whole matrix is described by "nin" PolyMaps. + Loop over each column of the Matrix, creating the corresponding PolyMap + for each. */ + for( icol = 0; icol < nin; icol++ ) { + +/* Initialise the total number of coefficients used to describe the + element of the PolyMap. */ + ncof_total = 0; + +/* Loop over each row of the Jacobian matrix (i.e. each PolyMap output). */ + pc = coeffs; + for( irow = 0; irow < nout; irow++ ) { + +/* Loop over each coefficient used in the polynomial that generates the + current PolyMap output. */ + ncof_row = this->ncoeff_f[ irow ]; + for( icof = 0; icof < ncof_row; icof++ ) { + +/* Get the power of input "icol" associated with the current coefficient. */ + power = (int)( this->power_f[ irow ][ icof ][ icol ] + 0.5 ); + +/* We can skip the coefficient if the power is zero. */ + if( power > 0 ) { + ncof_total++; + +/* Store the coefficient value, modified so that it describes a + polynomial that has been differentiated with respect to input "icol". */ + *(pc++) = this->coeff_f[ irow ][ icof ]*power; + +/* Store the output PolyMap to which the coeff relates. */ + *(pc++) = irow + 1; + +/* Store the powers of the inputs associated with the coeff. These are + the same as the original powers, except that the power of "icol" + (the input with respect to which the output has been differentiated) + is reduced by one. */ + for( iin = 0; iin < nin; iin++ ) { + if( iin != icol ) { + *(pc++) = this->power_f[ irow ][ icof ][ iin ]; + } else { + *(pc++) = this->power_f[ irow ][ icof ][ iin ] - 1; + } + } + } + } + } + +/* Create the PolyMap and store a pointer to it in the jacobian array in + the supplied PolyMap. */ + (this->jacobian)[ icol ] = astPolyMap( nin, nout, ncof_total, coeffs, + 0, NULL, " ", status ); + } + } + +/* Free resources */ + coeffs = astFree( coeffs ); + } + +/* Return the Jacobian. */ + return this->jacobian; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied PolyMap, +* in bytes. + +* Parameters: +* this +* Pointer to the PolyMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPolyMap *this; + int ic; + int nc; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the PolyMap structure. */ + this = (AstPolyMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + if( this->jacobian ) { + nc = astGetNin( this ); + for( ic = 0; ic < nc; ic++ ) { + result += astGetObjSize( (this->jacobian)[ ic ] ); + } + result += sizeof( AstPolyMap * )*nc; + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetTranForward( AstMapping *this, int *status ) { +/* +* +* Name: +* GetTranForward + +* Purpose: +* Determine if a PolyMap defines a forward coordinate transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int GetTranForward( AstMapping *this, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astGetTranForward method +* inherited from the Mapping class). + +* Description: +* This function returns a value indicating if the PolyMap is able +* to perform a forward coordinate transformation. + +* Parameters: +* this +* Pointer to the PolyMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the forward coordinate transformation is not defined, or 1 if it +* is. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPolyMap *map; /* Pointer to PolyMap to be queried */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the PolyMap. */ + map = (AstPolyMap *) this; + +/* Return the result. */ + return map->ncoeff_f ? 1 : 0; +} + +static int GetTranInverse( AstMapping *this, int *status ) { +/* +* +* Name: +* GetTranInverse + +* Purpose: +* Determine if a PolyMap defines an inverse coordinate transformation. + +* Type: +* Private function. + +* Synopsis: +* #include "matrixmap.h" +* int GetTranInverse( AstMapping *this, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astGetTranInverse method +* inherited from the Mapping class). + +* Description: +* This function returns a value indicating if the PolyMap is able +* to perform an inverse coordinate transformation. + +* Parameters: +* this +* Pointer to the PolyMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the inverse coordinate transformation is not defined, or 1 if it +* is. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPolyMap *map; /* Pointer to PolyMap to be queried */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the PolyMap. */ + map = (AstPolyMap *) this; + +/* Return the result. */ + return ( map->ncoeff_i || astGetIterInverse( map ) ) ? 1 : 0; +} + +void astInitPolyMapVtab_( AstPolyMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPolyMapVtab + +* Purpose: +* Initialise a virtual function table for a PolyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "polymap.h" +* void astInitPolyMapVtab( AstPolyMapVtab *vtab, const char *name ) + +* Class Membership: +* PolyMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the PolyMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPolyMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->PolyTran = PolyTran; + + vtab->ClearIterInverse = ClearIterInverse; + vtab->GetIterInverse = GetIterInverse; + vtab->SetIterInverse = SetIterInverse; + vtab->TestIterInverse = TestIterInverse; + + vtab->ClearNiterInverse = ClearNiterInverse; + vtab->GetNiterInverse = GetNiterInverse; + vtab->SetNiterInverse = SetNiterInverse; + vtab->TestNiterInverse = TestNiterInverse; + + vtab->ClearTolInverse = ClearTolInverse; + vtab->GetTolInverse = GetTolInverse; + vtab->SetTolInverse = SetTolInverse; + vtab->TestTolInverse = TestTolInverse; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + mapping->GetTranForward = GetTranForward; + mapping->GetTranInverse = GetTranInverse; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the destructor and copy constructor. */ + astSetDelete( (AstObjectVtab *) vtab, Delete ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + +/* Declare the class dump function. */ + astSetDump( vtab, Dump, "PolyMap", "Polynomial transformation" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void IterInverse( AstPolyMap *this, AstPointSet *out, AstPointSet *result, + int *status ){ +/* +* Name: +* IterInverse + +* Purpose: +* Use an iterative method to evaluate the inverse transformation of a +* PolyMap at a set of output positions. + +* Type: +* Private function. + +* Synopsis: +* void IterInverse( AstPolyMap *this, AstPointSet *out, AstPointSet *result, +* int *status ) + +* Description: +* This function transforms a set of PolyMap output positions using +* the inverse transformation of the PolyMap, to generate the corresponding +* input positions. An iterative Newton-Raphson method is used which +* only required the forward transformation of the PolyMap to be deifned. + +* Parameters: +* this +* The PolyMap. +* out +* A PointSet holding the PolyMap output positions that are to be +* transformed using the inverse transformation. +* result +* A PointSet into which the generated PolyMap input positions are to be +* stored. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPointSet *work; + AstPointSet **ps_jac; + AstPolyMap **jacob; + double *vec; + double *pb; + double **ptr_work; + double ***ptr_jac; + double *mat; + double **ptr_out; + double **ptr_in; + double *pa; + double det; + double maxerr; + double vlensq; + double xlensq; + double xx; + int *flags; + int *iw; + int fwd; + int icol; + int icoord; + int ipoint; + int irow; + int iter; + int maxiter; + int nconv; + int ncoord; + int npoint; + int sing; + +/* Check inherited status */ + if( !astOK ) return; + +/* Check the PolyMap has equal numbers of inputs and outputs. */ + ncoord = astGetNin( this ); + if( ncoord != astGetNout( this ) ) { + astError( AST__INTER, "astTransform(%s): Supplied %s has unequal numbers" + " of inputs and outputs and therefore an iterative inverse " + "cannot be used (internal AST Programming errpr).", status, + astGetClass(this), astGetClass(this) ); + } + +/* Get information about the Jacobian matrix for the forward polynomial + transformation. This matrix is a ncoord X ncoord matrix, in which + element (row=I,col=J) is the rate of change of output coord I with + respect to input coord J, of the supplied PolyMap's forward transformation. + The numerical values of the matrix vary depending on where it is + evaluated within the input space of the PolyMap. For this reason, the + "jacob" variable holds a vector of "ncoord" PolyMaps. The outputs of + each of these PolyMaps corresponds to a single column in the Jacobian + matrix. */ + jacob = GetJacobian( this, status ); + +/* Get the number of points to be transformed. */ + npoint = astGetNpoint( out ); + +/* Get another PointSet to hold intermediate results. */ + work = astPointSet( npoint, ncoord, " ", status ); + +/* See if the PolyMap has been inverted. */ + fwd = !astGetInvert( this ); + +/* Get pointers to the data arrays for all PointSets. Note, here "in" and + "out" refer to inputs and outputs of the PolyMap (i.e. the forward + transformation). These are respectively *outputs* and *inputs* of the + inverse transformation. */ + ptr_in = astGetPoints( result ); /* Returned input positions */ + ptr_out = astGetPoints( out ); /* Supplied output positions */ + ptr_work = astGetPoints( work ); /* Work space */ + +/* Allocate an array of PointSets to hold the elements of the Jacobian + matrix. */ + ptr_jac = astMalloc( sizeof( double ** )*ncoord ); + ps_jac = astCalloc( ncoord, sizeof( AstPointSet * ) ); + if( astOK ) { + for( icoord = 0; icoord < ncoord; icoord++ ) { + ps_jac[ icoord ] = astPointSet( npoint, ncoord, " ", status ); + ptr_jac[ icoord ] = astGetPoints( ps_jac[ icoord ] ); + } + } + +/* Allocate an array to hold flags indicating if each position has + converged. Initialise it to hold zero at every element. */ + flags = astCalloc( npoint, sizeof( int ) ); + +/* Allocate memory to hold the Jacobian matrix at a single point. */ + mat = astMalloc( sizeof( double )*ncoord*ncoord ); + +/* Allocate memory to hold the offset vector. */ + vec = astMalloc( sizeof( double )*ncoord ); + +/* Allocate memory to hold work space for palDmat. */ + iw = astMalloc( sizeof( int )*ncoord ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Store the initial guess at the required input positions. We assume initially + that the inverse transformation is a unit mapping, and so we just copy + the supplied outputs positions to the results PointSet holding the + corresponding input positions. */ + for( icoord = 0; icoord < ncoord; icoord++ ) { + memcpy( ptr_in[ icoord ], ptr_out[ icoord ], sizeof( double )*npoint ); + } + +/* Get the maximum number of iterations to perform. */ + maxiter = astGetNiterInverse( this ); + +/* Get the target relative error for the returned input axis values, and + square it. */ + maxerr = astGetTolInverse( this ); + maxerr *= maxerr; + +/* Initialise the number of positions which have reached the required + accuracy. */ + nconv = 0; + +/* Loop round doing iterations of a Newton-Raphson algorithm, until + all points have achieved the required relative error, or the + maximum number of iterations have been performed. */ + for( iter = 0; iter < maxiter && nconv < npoint && astOK; iter++ ) { + +/* Use the forward transformation of the supplied PolyMap to transform + the current guesses at the required input positions into the + corresponding output positions. Store the results in the "work" + PointSet. */ + (void) astTransform( this, result, fwd, work ); + +/* Modify the work PointSet so that it holds the offsets from the output + positions produced by the current input position guesses, and the + required output positions. */ + for( icoord = 0; icoord < ncoord; icoord++ ) { + pa = ptr_out[ icoord ]; + pb = ptr_work[ icoord ]; + for( ipoint = 0; ipoint< npoint; ipoint++,pa++,pb++ ) { + if( *pa != AST__BAD && *pb != AST__BAD ){ + *pb = *pa - *pb; + } else { + *pb = AST__BAD; + } + } + } + +/* Evaluate the elements of the Jacobian matrix at the current input + position guesses. */ + for( icoord = 0; icoord < ncoord; icoord++ ) { + (void) astTransform( jacob[ icoord ], result, 1, ps_jac[ icoord ] ); + } + +/* For each position, we now invert the matrix equation + + Dy = Jacobian.Dx + + to find a guess at the vector (dx) holding the offsets from the + current input positions guesses to their required values. Loop over all + points. */ + for( ipoint = 0; ipoint < npoint; ipoint++ ) { + +/* Do not change positions that have already converged. */ + if( !flags[ ipoint ] ) { + +/* Get the numerical values for the elements of the Jacobian matrix at + the current point. */ + pa = mat; + for( irow = 0; irow < ncoord; irow++ ) { + for( icol = 0; icol < ncoord; icol++ ) { + *(pa++) = ptr_jac[ icol ][ irow ][ ipoint ]; + } + +/* Store the offset from the current output position to the required + output position. */ + vec[ irow ] = ptr_work[ irow ][ ipoint ]; + } + +/* Find the corresponding offset from the current input position to the required + input position. */ + palDmat( ncoord, mat, vec, &det, &sing, iw ); + +/* If the matrix was singular, the input position cannot be evaluated so + store a bad value for it and indicate it has converged. */ + if( sing ) { + for( icoord = 0; icoord < ncoord; icoord++ ) { + ptr_in[ icoord ][ ipoint ] = AST__BAD; + } + flags[ ipoint ] = 1; + nconv++; + +/* Otherwise, update the input position guess. */ + } else { + vlensq = 0.0; + xlensq = 0.0; + pa = vec; + for( icoord = 0; icoord < ncoord; icoord++,pa++ ) { + xx = ptr_in[ icoord ][ ipoint ] + (*pa); + ptr_in[ icoord ][ ipoint ] = xx; + xlensq += xx*xx; + vlensq += (*pa)*(*pa); + } + +/* Check for convergence. */ + if( vlensq < maxerr*xlensq ) { + flags[ ipoint ] = 1; + nconv++; + } + } + } + } + } + } + +/* Free resources. */ + vec = astFree( vec ); + iw = astFree( iw ); + mat = astFree( mat ); + flags = astFree( flags ); + work = astAnnul( work ); + + if( ps_jac ) { + for( icoord = 0; icoord < ncoord; icoord++ ) { + ps_jac[ icoord ] = astAnnul( ps_jac[ icoord ] ); + } + ps_jac = astFree( ps_jac ); + } + + ptr_jac = astFree( ptr_jac ); + +} + +static void LMFunc1D( const double *p, double *hx, int m, int n, void *adata ){ +/* +* Name: +* LMFunc1D + +* Purpose: +* Evaluate a test 1D polynomal. + +* Type: +* Private function. + +* Synopsis: +* void LMFunc1D( const double *p, double *hx, int m, int n, void *adata ) + +* Description: +* This function finds the residuals implied by a supplied set of +* candidate polynomial coefficients. Each residual is a candidate +* polynomial evaluated at one of the sample points, minus the +* supplied target value for the polynomial at that test point. +* +* The minimisation process minimises the sum of the squared residuals. + +* Parameters: +* p +* An array of "m" coefficients for the candidate polynomial. The +* coefficients are ordered C0, C1, C2, etc. +* hx +* An array in which to return the "n" residuals. The residual at +* sample "k" is returned in element (k). +* m +* The length of the "p" array. This should be equal to order. +* n +* The length of the "hx" array. This should be equal to nsamp. +* adata +* Pointer to a structure holding the sample positions and values, +* and other information. + +*/ + +/* Local Variables: */ + MinPackData *data; + double *px1; + double *py; + const double *vp; + double *vr; + double res; + int k; + int w1; + +/* Get a pointer to the data structure. */ + data = (MinPackData *) adata; + +/* Initialise a pointer to the current returned residual value. */ + vr = hx; + +/* Initialise a pointer to the sampled Y values for the polynomial output. */ + py = data->y[ 0 ]; + +/* Initialise a pointer to the powers of the input X values at the curent + (i.e. first) sample. */ + px1 = data->xp1; + +/* Loop over the index of the sample to which this residual refers. */ + for( k = 0; k < data->nsamp; k++ ) { + +/* Initialise a pointer to the first coefficient (the constant term) for the + polynomial output coordinate. */ + vp = p; + +/* Initialise this residual to hold the sampled Y value. Increment the + pointer to the next sampled value for the current polynomial output. */ + res = -( *(py++) ); + +/* Loop over the coefficients. */ + for( w1 = 0; w1 < data->order; w1++ ) { + +/* Increment the residual by the value of the current term Cw1*(x1^w1). + Increment the pointer to the next coefficient (C). Also increment the + pointer to the next higher power of X1. */ + res += ( *(vp++) )*( *(px1++) ); + } + +/* Store the complete residual in the returned array, and increment the + pointer to the next residual. */ + *(vr++) = res; + } +} + +static void LMFunc2D( const double *p, double *hx, int m, int n, void *adata ){ +/* +* Name: +* LMFunc2D + +* Purpose: +* Evaluate a test 2D polynomal. + +* Type: +* Private function. + +* Synopsis: +* void LMFunc2D( const double *p, double *hx, int m, int n, void *adata ) + +* Description: +* This function finds the residuals implied by a supplied set of +* candidate polynomial coefficients. Each residual is a candidate +* polynomial (either P1 or P2) evaluated at one of the sample points +* (x1,x2), minus the supplied target value for the polynomial at that +* test point. +* +* The minimisation process minimises the sum of the squared residuals. + +* Parameters: +* p +* An array of "m" coefficients for the candidate polynomial. All the +* coefficients for polynomial P1 come first, followed by those for P2. +* Within each each polynomial, the coefficients are order C00, C10, +* C01, C20, C11, C02, C30, C21, C12, C03, etc. So the coefficient +* of (x1^j*x2^k) (=Cjk) for polynomial Pi is stored in element +* [k + (j + k)*(j + k + 1)/2 + i*order*(order+1)/2] of the "p" array. +* hx +* An array in which to return the "n" residuals. The residual at +* sample "k" for polynomial "i" is returned in element (k + nsamp*i). +* m +* The length of the "p" array. This should be equal to order*(order+1). +* n +* The length of the "hx" array. This should be equal to 2*nsamp. +* adata +* Pointer to a structure holding the sample positions and values, +* and other information. + +*/ + +/* Local Variables: */ + MinPackData *data; + const double *vp0; + const double *vp; + double *py; + double *px1; + double *px10; + double *px20; + double *vr; + double res; + double *px2; + int iout; + int k; + int w12; + int w2; + +/* Get a pointer to the data structure. */ + data = (MinPackData *) adata; + +/* Initialise a pointer to the current returned residual value. */ + vr = hx; + +/* Initilise a pointer to the first coefficient (the constant term) for the + current (i.e. first) polynomial output coordinate. */ + vp0 = p; + +/* Loop over each polynomial output coordinate. */ + for( iout = 0; iout < 2; iout++ ) { + +/* Initialise a pointer to the sampled Y values for the first polynomial + output. */ + py = data->y[ iout ]; + +/* Initialise pointers to the powers of the input X values at the curent + (i.e. first) sample. */ + px10 = data->xp1; + px20 = data->xp2; + +/* Loop over the index of the sample to which this residual refers. */ + for( k = 0; k < data->nsamp; k++ ) { + +/* Reset the pointer to the first coefficient (the constant term) + for the current polynomial output coordinate. */ + vp = vp0; + +/* Initialise this residual to hold the sampled Y value. Increment the + pointer to the next sampled value for the current polynomial output. */ + res = -( *(py++) ); + +/* The w12 value is the sum of the powers of X1 and X2. So w12=0 + corresponds to the constant term in the polynomial, and (e.g.) w12=6 + corresponds to all the terms for which the sum of the powerss (w1+w2) + is 6. Loop over all possible w12 values. */ + for( w12 = 0; w12 < data->order; w12++ ) { + +/* The next coeff refers to (x1^w12)*(x2^0). Get pointers to the values + holding x1^w12 and x2^0. */ + px1 = px10++; + px2 = px20; + +/* Loop over powers of x2. The corresponding power of x1 is "w12-x2", but + is not explicitly needed here. So x1 moves down from w12 to zero, as + w2 moves up from zero to w12. */ + for( w2 = 0; w2 <= w12; w2++ ) { + +/* Increment the residual by the value of the current term Cw1w2*(x1^w1)*(x2^w2). + Increment the pointer tio the next coefficient (C). Also decrement the + pointer to the next lower power of X1, and increment the pointer to the next + higher power of X2. */ + res += ( *(vp++) )*( *(px1--) )*( *(px2++) ); + } + } + +/* Move on to the x2 powers for the next sample. Don't need to do this + for x1 since px10 is incremented within the above loop. */ + px20 += data->order; + +/* Store the complete residual in the returned array, and increment the + pointer to the next residual. */ + *(vr++) = res; + } + +/* Get a pointer to the first coefficient (the constant term) for the + next polynomial output coordinate. */ + vp0 += data->order*( 1 + data->order )/2; + } +} + +static void LMJacob1D( const double *p, double *jac, int m, int n, void *adata ){ +/* +* Name: +* LMJacob1D + +* Purpose: +* Evaluate the Jacobian matrix of a test 1D polynomal. + +* Type: +* Private function. + +* Synopsis: +* void LMJacob1D( const double *p, double *jac, int m, int n, void *adata ) + +* Description: +* This function finds the Jacobian matrix that describes the rate of +* change of every residual with respect to every polynomial coefficient. +* Each residual is a candidate polynomial evaluated at one of the sample +* points minus the supplied target value for the polynomial at that test +* point. +* +* For a polynomial the Jacobian matrix is constant (i.e. does not +* depend on the values of the polynomial coefficients). So we only +* evaluate it on the first call. + +* Parameters: +* p +* An array of "m" coefficients for the candidate polynomial. +* jac +* An array in which to return the "m*n" elements of the Jacobian +* matrix. The rate of change of residual "r" with respect to +* coefficient "c" is returned in element "r + c*n". The residual +* at sample "k" of polynomial Pi has an "r" index of (k + nsamp*i). +* The coefficient of (x1^j) for polynomial Pi has a "c" index +* of j. +* m +* The length of the "p" array. This should be equal to order. +* n +* The number of residuals. This should be equal to nsamp. +* adata +* Pointer to a structure holding the sample positions and values, +* and other information. + +*/ + +/* Local Variables: */ + MinPackData *data; + int k; + int w1; + +/* Get a pointer to the data structure. */ + data = (MinPackData *) adata; + +/* The Jacobian of the residuals with respect to the polynomial + coefficients is constant (i.e. does not depend on the values of the + polynomial coefficients). So we only need to calculate it once. If + this is the first call, calculate the Jacobian and return it in "jac". + otherwise, just return immediately retaining the supplied "jac" values + (which will be the values returned by the previous call to this + function). */ + if( data->init_jac ) { + data->init_jac = 0; + +/* Loop over all residuals. */ + for( k = 0; k < n; k++ ) { + +/* Loop over all parameters (i.e. polynomial coefficients). */ + for( w1 = 0; w1 < m; w1++ ) { + +/* Store the Jacobian. */ + jac[ k + w1*n ] = (data->xp1)[ w1 + k*data->order ]; + } + } + } +} + +static void LMJacob2D( const double *p, double *jac, int m, int n, void *adata ){ +/* +* Name: +* LMJacob2D + +* Purpose: +* Evaluate the Jacobian matrix of a test 2D polynomal. + +* Type: +* Private function. + +* Synopsis: +* void LMJacob2D( const double *p, double *jac, int m, int n, void *adata ) + +* Description: +* This function finds the Jacobian matrix that describes the rate of +* change of every residual with respect to every polynomial coefficient. +* Each residual is a candidate polynomial (either P1 or P2) evaluated +* at one of the sample points (x1,x2), minus the supplied target value +* for the polynomial at that test point. +* +* For a polynomial the Jacobian matrix is constant (i.e. does not +* depend on the values of the polynomial coefficients). So we only +* evaluate it on the first call. + +* Parameters: +* p +* An array of "m" coefficients for the candidate polynomial. All the +* coefficients for polynomial P1 come first, followed by those for P2. +* Within each each polynomial, the coefficients are order C00, C10, +* C01, C20, C11, C02, C30, C21, C12, C03, etc. So the coefficient +* of (x1^j*x2^k) (=Cjk) for polynomial Pi is stored in element +* [k + (j + k)*(j + k + 1)/2 + i*order*(order+1)/2] of the "p" array. +* jac +* An array in which to return the "m*n" elements of the Jacobian +* matrix. The rate of change of residual "r" with respect to +* coefficient "c" is returned in element "r + c*n". The residual +* at sample "k" of polynomial Pi has an "r" index of (k + nsamp*i). +* The coefficient of (x1^j*x2^k) for polynomial Pi has a "c" index +* of [k + (j + k)*(j + k + 1)/2 + i*order*(order+1)/2]. +* m +* The length of the "p" array. This should be equal to order*(order+1). +* n +* The number of residuals. This should be equal to 2*nsamp. +* adata +* Pointer to a structure holdin gthe sample positions and values, +* and other information. + +*/ + +/* Local Variables: */ + MinPackData *data; + int iout; + int k; + int ncof; + int vp; + int vr; + int w1; + int w12; + int w2; + +/* Get a pointer to the data structure. */ + data = (MinPackData *) adata; + +/* The Jacobian of the residuals with respect to the polynomial + coefficients is constant (i.e. does not depend on the values of the + polynomial coefficients). So we only need to calculate it once. If + this is the first call, calculate the Jacobian and return it in "jac". + otherwise, just return immediately retaining the supplied "jac" values + (which will be the values returned by the previous call to this + function). */ + if( data->init_jac ) { + data->init_jac = 0; + +/* Store the number of coefficients in one polynomial. */ + ncof = data->order*( 1 + data->order )/2; + +/* Loop over all residuals. */ + for( vr = 0; vr < n; vr++ ) { + +/* Calculate the polynomial output index, and sample index, that creates + the current residual. */ + iout = vr/data->nsamp; + k = vr - iout*data->nsamp; + +/* Loop over all parameters (i.e. polynomial coefficients). */ + for( vp = 0; vp < m; vp++ ) { + +/* If this coefficient is not used in the creation of the current + polynomial output value, then the Jacobian value is zero. */ + if( vp/ncof != iout ) { + jac[ vr + vp*n ] = 0.0; + +/* Otherwise, get the powers of the two polynomial inputs, to which + the current coefficient relates. */ + } else { + w12 = ( -1.0 + sqrt( 1.0 + 8.0*(vp - iout*ncof) ) )/2.0; + w2 = vp - iout*ncof - w12*( w12 + 1 )/2; + w1 = w12 - w2; + +/* Store the Jacobian. */ + jac[ vr + vp*n ] = (data->xp1)[ w1 + k*data->order ]* + (data->xp2)[ w2 + k*data->order ]; + } + } + } + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstPolyMap *this; + int ic; + int result; + int nc; + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointer to the PolyMap structure. */ + this = (AstPolyMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( this->jacobian ) { + nc = astGetNin( this ); + for( ic = 0; ic < nc && result; ic++ ) { + result = astManageLock( (this->jacobian)[ ic ], mode, + extra, fail ); + } + } + + return result; + +} +#endif + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* PolyMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated PolyMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated PolyMap with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated PolyMap which is to be merged with +* its neighbours. This should be a cloned copy of the PolyMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* PolyMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated PolyMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstPolyMap *pmap0; /* Nominated PolyMap */ + AstPolyMap *pmap1; /* Neighbouring PolyMap */ + int i; /* Index of neighbour */ + int iax_in; /* Index of input coordinate */ + int iax_out; /* Index of output coordinate */ + int ico; /* Index of coefficient */ + int inv0; /* Supplied Invert flag for nominated PolyMap */ + int inv1; /* Supplied Invert flag for neighbouring PolyMap */ + int nc; /* Number of coefficients */ + int nin; /* Number of input coordinates for nominated PolyMap */ + int nout; /* Number of output coordinates for nominated PolyMap */ + int ok; /* Are PolyMaps equivalent? */ + int result; /* Result value to return */ + int swap0; /* Swap inputs and outputs for nominated PolyMap? */ + int swap1; /* Swap inputs and outputs for neighbouring PolyMap? */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Save a pointer to the nominated PolyMap. */ + pmap0 = (AstPolyMap *) ( *map_list )[ where ]; + +/* The only simplification which can currently be performed is to merge a PolyMap + with its own inverse. This can only be done in series. Obviously, + there are potentially other simplications which could be performed, but + time does not currently allow these to be coded. */ + if( series ) { + +/* Set a flag indicating if "input" and "output" needs to be swapped for + the nominated PolyMap. */ + inv0 = ( *invert_list )[ where ]; + swap0 = ( inv0 != astGetInvert( pmap0 ) ); + +/* Get the number of inputs and outputs to the nominated PolyMap. */ + nin = !swap0 ? astGetNin( pmap0 ) : astGetNout( pmap0 ); + nout = !swap0 ? astGetNout( pmap0 ) : astGetNin( pmap0 ); + +/* Check each neighbour. */ + for( i = where - 1; i <= where + 1; i += 2 ) { + +/* Continue with the next pass if the neighbour does not exist. */ + if( i < 0 || i >= *nmap ) continue; + +/* Continue with the next pass if this neighbour is not a PermMap. */ + if( strcmp( "PolyMap", astGetClass( ( *map_list )[ i ] ) ) ) continue; + +/* Get a pointer to it. */ + pmap1 = (AstPolyMap *) ( *map_list )[ i ]; + +/* Check it is used in the opposite direction to the nominated PolyMap. */ + if( ( *invert_list )[ i ] == ( *invert_list )[ where ] ) continue; + +/* Set a flag indicating if "input" and "output" needs to be swapped for + the neighbouring PolyMap. */ + inv1 = ( *invert_list )[ i ]; + swap1 = ( inv1 != astGetInvert( pmap1 ) ); + +/* Check the number of inputs and outputs are equal to the nominated + PolyMap. */ + if( astGetNin( pmap1 ) != (!swap1 ? nin : nout ) && + astGetNout( pmap1 ) != (!swap1 ? nout : nin ) ) continue; + +/* Check the forward coefficients are equal. */ + ok = 1; + for( iax_out = 0; iax_out < nout && ok; iax_out++ ) { + nc = pmap1->ncoeff_f[ iax_out ]; + if( nc != pmap0->ncoeff_f[ iax_out ] ) continue; + + for( ico = 0; ico < nc && ok; ico++ ) { + + if( !astEQUAL( pmap1->coeff_f[ iax_out ][ ico ], + pmap0->coeff_f[ iax_out ][ ico ] ) ){ + ok = 0; + + } else { + for( iax_in = 0; iax_in < nin && ok; iax_in++ ) { + ok = ( pmap1->power_f[ iax_out ][ ico ][ iax_in ] == + pmap0->power_f[ iax_out ][ ico ][ iax_in ] ); + } + } + } + } + if( !ok ) continue; + +/* Check the inverse coefficients are equal. */ + ok = 1; + for( iax_in = 0; iax_in < nin && ok; iax_in++ ) { + nc = pmap1->ncoeff_i[ iax_in ]; + if( nc != pmap0->ncoeff_i[ iax_in ] ) continue; + + for( ico = 0; ico < nc && ok; ico++ ) { + + if( !astEQUAL( pmap1->coeff_i[ iax_in ][ ico ], + pmap0->coeff_i[ iax_in ][ ico ] ) ){ + ok = 0; + + } else { + for( iax_out = 0; iax_out < nout && ok; iax_out++ ) { + ok = ( pmap1->power_i[ iax_in ][ ico ][ iax_out ] == + pmap0->power_i[ iax_in ][ ico ][ iax_out ] ); + } + } + } + } + if( !ok ) continue; + +/* If we get this far, then the nominated PolyMap and the current + neighbour cancel each other out, so replace each by a UnitMap. */ + (void) astAnnul( pmap0 ); + (void) astAnnul( pmap1 ); + if( i < where ) { + ( *map_list )[ where ] = (AstMapping *) astUnitMap( nout, "", status ); + ( *map_list )[ i ] = (AstMapping *) astUnitMap( nout, "", status ); + ( *invert_list )[ where ] = 0; + ( *invert_list )[ i ] = 0; + result = i; + } else { + ( *map_list )[ where ] = (AstMapping *) astUnitMap( nin, "", status ); + ( *map_list )[ i ] = (AstMapping *) astUnitMap( nin, "", status ); + ( *invert_list )[ where ] = 0; + ( *invert_list )[ i ] = 0; + result = where; + } + +/* Leave the loop. */ + break; + } + } + +/* Return the result. */ + return result; +} + +static int MPFunc1D( void *p, int m, int n, const double *x, double *fvec, + double *fjac, int ldfjac, int iflag ) { +/* +* Name: +* MPFunc1D + +* Purpose: +* Evaluate a test 1D polynomal or its Jacobian. + +* Type: +* Private function. + +* Synopsis: +* int MPFunc1D( void *p, int m, int n, const double *x, double *fvec, +* double *fjac, int ldfjac, int iflag ) + +* Description: +* This function finds the residuals or Jacobian implied by a supplied +* set of candidate polynomial coefficients. Each residual is a candidate +* polynomial evaluated at one of the sample points, minus the +* supplied target value for the polynomial at that test point. +* +* The minimisation process minimises the sum of the squared residuals. + +* Parameters: +* p +* A pointer to data needed to evaluate the required residuals or +* Jacobians. +* m +* The number of residuals. +* n +* The number of coefficients. +* x +* An array of "n" coefficients for the candidate polynomial. +* fvec +* An array in which to return the "m" residuals. +* fjac +* An array in which to return the "mxn" Jacobian values. +* ldflac +* Unused (should always be equal to "m"). +* iflag +* If 1 calculate the residuals, otherwise calculate the Jacobians. + +*/ + +/* If required, calculate the function values at X, and return this + vector in fvec. Do not alter fjac. */ + if( iflag == 1 ) { + LMFunc1D( x, fvec, n, m, p ); + +/* Otherwise, calculate the Jacobian values at X, and return this + matrix in fjac. Do not alter fvec. */ + } else { + LMJacob1D( x, fjac, n, m, p ); + } + + return 0; +} + +static int MPFunc2D( void *p, int m, int n, const double *x, double *fvec, + double *fjac, int ldfjac, int iflag ) { +/* +* Name: +* MPFunc1D + +* Purpose: +* Evaluate a test 2D polynomal or its Jacobian. + +* Type: +* Private function. + +* Synopsis: +* int MPFunc2D( void *p, int m, int n, const double *x, double *fvec, +* double *fjac, int ldfjac, int iflag ) + +* Description: +* This function finds the residuals or Jacobian implied by a supplied +* set of candidate polynomial coefficients. Each residual is a candidate +* polynomial evaluated at one of the sample points, minus the +* supplied target value for the polynomial at that test point. +* +* The minimisation process minimises the sum of the squared residuals. + +* Parameters: +* p +* A pointer to data needed to evaluate the required residuals or +* Jacobians. +* m +* The number of residuals. +* n +* The number of coefficients. +* x +* An array of "n" coefficients for the candidate polynomial. +* fvec +* An array in which to return the "m" residuals. +* fjac +* An array in which to return the "mxn" Jacobian values. +* ldflac +* Unused (should always be equal to "m"). +* iflag +* If 1 calculate the residuals, otherwise calculate the Jacobians. + +*/ + +/* If required, calculate the function values at X, and return this + vector in fvec. Do not alter fjac. */ + if( iflag == 1 ) { + LMFunc2D( x, fvec, n, m, p ); + +/* Otherwise, calculate the Jacobian values at X, and return this + matrix in fjac. Do not alter fvec. */ + } else { + LMJacob2D( x, fjac, n, m, p ); + + } + + return 0; +} + +static AstPolyMap *PolyTran( AstPolyMap *this, int forward, double acc, + double maxacc, int maxorder, const double *lbnd, + const double *ubnd, int *status ){ +/* +*++ +* Name: +c astPolyTran +f AST_POLYTRAN + +* Purpose: +* Fit a PolyMap inverse or forward transformation. + +* Type: +* Public function. + +* Synopsis: +c #include "polymap.h" +c AstPolyMap *astPolyTran( AstPolyMap *this, int forward, double acc, +c double maxacc, int maxorder, const double *lbnd, +c const double *ubnd ) +f RESULT = AST_POLYTRAN( THIS, FORWARD, ACC, MAXACC, MAXORDER, LBND, +f UBND, STATUS ) + +* Class Membership: +* PolyMap method. + +* Description: +* This function creates a new PolyMap which is a copy of the supplied +* PolyMap, in which a specified transformation (forward or inverse) +* has been replaced by a new polynomial transformation. The +* coefficients of the new transformation are estimated by sampling +* the other transformation and performing a least squares polynomial +* fit in the opposite direction to the sampled positions and values. +* +* This method can only be used on (1-input,1-output) or (2-input,2-output) +* PolyMaps. +* +* The transformation to create is specified by the +c "forward" parameter. +f FORWARD parameter. +* In what follows "X" refers to the inputs of the PolyMap, and "Y" to +* the outputs of the PolyMap. The forward transformation transforms +* input values (X) into output values (Y), and the inverse transformation +* transforms output values (Y) into input values (X). Within a PolyMap, +* each transformation is represented by an independent set of +* polynomials, P_f or P_i: Y=P_f(X) for the forward transformation and +* X=P_i(Y) for the inverse transformation. +* +c The "forward" +f The FORWARD +* parameter specifies the transformation to be replaced. If it is +c non-zero, +f is .TRUE., +* a new forward transformation is created +* by first finding the input values (X) using the inverse transformation +* (which must be available) at a regular grid of points (Y) covering a +* rectangular region of the PolyMap's output space. The coefficients of +* the required forward polynomial, Y=P_f(X), are chosen in order to +* minimise the sum of the squared residuals between the sampled values +* of Y and P_f(X). +* +c If "forward" is zero (probably the most likely case), +f If FORWARD is .FALSE. (probably the most likely case), +* a new inverse transformation is created by +* first finding the output values (Y) using the forward transformation +* (which must be available) at a regular grid of points (X) covering a +* rectangular region of the PolyMap's input space. The coefficients of +* the required inverse polynomial, X=P_i(Y), are chosen in order to +* minimise the sum of the squared residuals between the sampled values +* of X and P_i(Y). +* +* This fitting process is performed repeatedly with increasing +* polynomial orders (starting with linear) until the target +* accuracy is achieved, or a specified maximum order is reached. If +* the target accuracy cannot be achieved even with this maximum-order +* polynomial, the best fitting maximum-order polynomial is returned so +* long as its accuracy is better than +c "maxacc". +f MAXACC. +* If it is not, an error is reported. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the original Mapping. +c forward +f FORWARD = LOGICAL (Given) +c If non-zero, +f If .TRUE., +* the forward PolyMap transformation is replaced. Otherwise the +* inverse transformation is replaced. +c acc +f ACC = DOUBLE (Given) +* The target accuracy, expressed as a geodesic distance within +* the PolyMap's input space (if +c "forward" is zero) or output space (if "forward" is non-zero). +f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.). +c maxacc +f MAXACC = DOUBLE (Given) +* The maximum allowed accuracy for an acceptable polynomial, +* expressed as a geodesic distance within the PolyMap's input +* space (if +c "forward" is zero) or output space (if "forward" is non-zero). +f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.). +c maxorder +f MAXORDER = INTEGER (Given) +* The maximum allowed polynomial order. This is one more than the +* maximum power of either input axis. So for instance, a value of +* 3 refers to a quadratic polynomial. Note, cross terms with total +* powers greater than or equal to +c maxorder +f MAXORDER +* are not inlcuded in the fit. So the maximum number of terms in +* each of the fitted polynomials is +c maxorder*(maxorder+1)/2. +f MAXORDER*(MAXORDER+1)/2. +c lbnd +f LBND( * ) = DOUBLE PRECISION (Given) +c Pointer to an +f An +* array holding the lower bounds of a rectangular region within +* the PolyMap's input space (if +c "forward" is zero) or output space (if "forward" is non-zero). +f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.). +* The new polynomial will be evaluated over this rectangle. The +* length of this array should equal the value of the PolyMap's Nin +* or Nout attribute, depending on +c "forward". +f FORWARD. +c ubnd +f UBND( * ) = DOUBLE PRECISION (Given) +c Pointer to an +f An +* array holding the upper bounds of a rectangular region within +* the PolyMap's input space (if +c "forward" is zero) or output space (if "forward" is non-zero). +f FORWARD is .FALSE.) or output space (if FORWARD is .TRUE.). +* The new polynomial will be evaluated over this rectangle. The +* length of this array should equal the value of the PolyMap's Nin +* or Nout attribute, depending on +c "forward". +f FORWARD. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPolyTran() +f AST_POLYTRAN = INTEGER +* A pointer to the new PolyMap. +c A NULL pointer +f AST__NULL +* will be returned if the fit fails to achieve the accuracy +* specified by +c "maxacc", +f MAXACC, +* but no error will be reported. + +* Notes: +* - This function can only be used on 1D or 2D PolyMaps which have +* the same number of inputs and outputs. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstPolyMap *result; + int ok; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Take a copy of the supplied PolyMap. */ + result = astCopy( this ); + +/* Replace the required transformation. */ + ok = ReplaceTransformation( result, forward, acc, maxacc, maxorder, lbnd, + ubnd, status ); + +/* If an error occurred, or the fit was not good enough, annul the returned + PolyMap. */ + if ( !ok || !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int ReplaceTransformation( AstPolyMap *this, int forward, double acc, + double maxacc, int maxorder, const double *lbnd, + const double *ubnd, int *status ){ +/* +* Name: +* ReplaceTransformation + +* Purpose: +* Create a new inverse or forward transformation for a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* int ReplaceTransformation( AstPolyMap *this, int forward, double acc, +* double maxacc, int maxorder, const double *lbnd, +* const double *ubnd, int *status ) + +* Description: +* This function creates a new forward or inverse transformation for +* the supplied PolyMap (replacing any existing transformation), by +* sampling the other transformation and performing a least squares +* polynomial fit to the sample positions and values. +* +* The transformation to create is specified by the "forward" parameter. +* In what follows "X" refers to the inputs of the PolyMap, and "Y" to +* the outputs of the PolyMap. The forward transformation transforms +* input values (X) into output values (Y), and the inverse transformation +* transforms output values (Y) into input values (X). Within a PolyMap, +* each transformation is represented by an independent set of +* polynomials: Y=P_f(X) for the forward transformation and X=P_i(Y) +* for the inverse transformation. +* +* If "forward" is zero then a new inverse transformation is created by +* first finding the output values (Y) using the forward transformation +* (which must be available) at a regular grid of points (X) covering a +* rectangular region of the PolyMap's input space. The coefficients of +* the required inverse polynomial, X=P_i(Y), are chosen in order to +* minimise the sum of the squared residuals between the sampled values +* of X and P_i(Y). +* +* If "forward" is non-zero then a new forward transformation is created +* by first finding the input values (X) using the inverse transformation +* (which must be available) at a regular grid of points (Y) covering a +* rectangular region of the PolyMap's output space. The coefficients of +* the required forward polynomial, Y=P_f(X), are chosen in order to +* minimise the sum of the squared residuals between the sampled values +* of Y and P_f(X). +* +* This fitting process is performed repeatedly with increasing +* polynomial orders (starting with linear) until the target +* accuracy is achieved, or a specified maximum order is reached. If +* the target accuracy cannot be achieved even with this maximum-order +* polynomial, the best fitting maximum-order polynomial is returned so +* long as its accuracy is better than "maxacc". + +* Parameters: +* this +* The PolyMap. +* forward +* If non-zero, then the forward PolyMap transformation is +* replaced. Otherwise the inverse transformation is replaced. +* acc +* The target accuracy, expressed as a geodesic distance within +* the PolyMap's input space (if "forward" is zero) or output +* space (if "forward" is non-zero). +* maxacc +* The maximum allowed accuracy for an acceptable polynomial, +* expressed as a geodesic distance within the PolyMap's input +* space (if "forward" is zero) or output space (if "forward" is +* non-zero). +* maxorder +* The maximum allowed polynomial order. This is one more than the +* maximum power of either input axis. So for instance, a value of +* 3 refers to a quadratic polynomial. Note, cross terms with total +* powers greater than or equal to maxorder are not inlcuded in the +* fit. So the maximum number of terms in each of the fitted polynomials +* is maxorder*(maxorder+1)/2. +* lbnd +* An array holding the lower bounds of a rectangular region within +* the PolyMap's input space (if "forward" is zero) or output +* space (if "forward" is non-zero). The new polynomial will be +* evaluated over this rectangle. +* ubnd +* An array holding the upper bounds of a rectangular region within +* the PolyMap's input space (if "forward" is zero) or output +* space (if "forward" is non-zero). The new polynomial will be +* evaluated over this rectangle. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Non-zero if a fit was performed succesfully, to at least the +* maximum allowed accuracy. Zero if the fit failed, or an error +* occurred. + +* Notes: +* - No error is reported if the fit fails to achieve the required +* maximum accuracy. +* - An error is reported if the transformation that is not being +* replaced is not defined. +* - An error is reported if the PolyMap does not have equal numbers +* of inputs and outputs. +* - An error is reported if the PolyMap has more than 2 inputs or outputs. + +*/ + +/* Local Variables: */ + double **table; + double *cofs; + double racc; + double scales[ 4 ]; + int ndim; + int ncof; + int nsamp; + int order; + int result; + +/* Check inherited status */ + if( !astOK ) return 0; + +/* Check the PolyMap can be used. */ + ndim = astGetNin( this ); + if( astGetNout( this ) != ndim && astOK ) { + astError( AST__BADNI, "astPolyTran(%s): Supplied %s has " + "different number of inputs (%d) and outputs (%d).", + status, astGetClass( this ), astGetClass( this ), ndim, + astGetNout( this ) ); + + } else if( ndim > 2 && astOK ) { + astError( AST__BADNI, "astPolyTran(%s): Supplied %s has " + "too many inputs and outputs (%d) - must be 1 or 2.", + status, astGetClass( this ), astGetClass( this ), ndim ); + } + + if( forward != astGetInvert( this ) ){ + if( ! this->ncoeff_i && astOK ) { + astError( AST__NODEF, "astPolyTran(%s): Supplied %s has " + "no inverse transformation.", status, astGetClass( this ), + astGetClass( this ) ); + } + } else { + if( ! this->ncoeff_f && astOK ) { + astError( AST__NODEF, "astPolyTran(%s): Supplied %s has " + "no forward transformation.", status, astGetClass( this ), + astGetClass( this ) ); + } + } + +/* Check the bounds can be used. */ + if( lbnd[ 0 ] >= ubnd[ 0 ] && astOK ) { + astError( AST__NODEF, "astPolyTran(%s): Supplied upper " + "bound for the first axis (%g) is less than or equal to the " + "supplied lower bound (%g).", status, astGetClass( this ), + lbnd[ 0 ], ubnd[ 0 ] ); + } else if( ndim == 2 && lbnd[ 1 ] >= ubnd[ 1 ] && astOK ) { + astError( AST__NODEF, "astPolyTran(%s): Supplied upper " + "bound for the second axis (%g) is less than or equal to the " + "supplied lower bound (%g).", status, astGetClass( this ), + lbnd[ 1 ], ubnd[ 1 ] ); + } + +/* Initialise pointer to work space. */ + table = NULL; + +/* Loop over increasing polynomial orders until the required accuracy is + achieved, up to the specified maximum order. The "order" value is one more + than the maximum power in the polynomial (so a quadratic has "order" 3). */ + if( maxorder < 2 ) maxorder = 2; + for( order = 2; order <= maxorder; order++ ) { + +/* First do 2D PolyMaps. */ + if( ndim == 2 ) { + +/* Sample the requested polynomial transformation at a grid of points. This + grid covers the user-supplied region, using 2*order points on each + axis. If the PolyMap is 1D, then it will be treated as a 2D polynomial + in which the second output is a unit transformation. */ + table = SamplePoly2D( this, !forward, table, lbnd, ubnd, 2*order, + &nsamp, scales, status ); + +/* Fit the polynomial. Always fit a linear polynomial ("order" 2) to any + dummy second axis. If successfull, replace the PolyMap transformation + and break out of the order loop. */ + cofs = FitPoly2D( nsamp, acc, order, table, scales, &ncof, &racc, + status ); + +/* Now do 1D PolyMaps. */ + } else { + table = SamplePoly1D( this, !forward, table, lbnd[ 0 ], ubnd[ 0 ], + 2*order, &nsamp, scales, status ); + cofs = FitPoly1D( nsamp, acc, order, table, scales, &ncof, &racc, + status ); + } + +/* If the fit was succesful, replace the PolyMap transformation and break + out of the order loop. */ + if( cofs && ( racc < acc || ( racc < maxacc && order == maxorder ) ) ) { + StoreArrays( this, forward, ncof, cofs, status ); + break; + } else { + cofs = astFree( cofs ); + } + } + +/* If no fit was produced, return zero. */ + result = cofs ? 1 : 0; + +/* Free resources. */ + cofs = astFree( cofs ); + table = astFreeDouble( table ); + +/* Return the result. */ + return result; +} + +static double **SamplePoly1D( AstPolyMap *this, int forward, double **table, + double lbnd, double ubnd, int npoint, int *nsamp, + double scales[2], int *status ){ +/* +* Name: +* SamplePoly1D + +* Purpose: +* Create a table of input and output positions for a 1D PolyMap. + +* Type: +* Private function. + +* Synopsis: +* double **SamplePoly1D( AstPolyMap *this, int forward, double **table, +* double lbnd, double ubnd, int npoint, int *nsamp, +* double scales[2], int *status ) + +* Description: +* This function creates a table containing samples of the requested +* polynomial transformation at a grid of input points. This grid covers +* the user-supplied region, using "npoint" points. + +* Parameters: +* this +* The PolyMap. +* forward +* If non-zero, then the forward PolyMap transformation is sampled. +* Otherwise the inverse transformation is sampled. +* table +* Pointer to a previous table created by this function, which is +* to be re-used, or NULL. +* lbnd +* The lower bounds of the region within the PolyMap's 1D input space +* (if "forward" is non-zero) or output space (if "forward" is zero). +* The new polynomial will be evaluated over this region. +* ubnd +* The upper bounds of the region within the PolyMap's 1D input space +* (if "forward" is non-zero) or output space (if "forward" is zero). +* The new polynomial will be evaluated over this region. +* npoint +* The number of points to use. +* nsamp +* Address of an int in which to return the total number of samples +* in the returned table. +* scales +* Array in which to return the scaling factors for the two columns +* of the returned table. Multiplying the returned table values by +* the scale factor produces PolyMap input or output axis values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to an array of 2 pointers. Each of these pointers points +* to an array of "nsamp" doubles, being the sampled values for y1 +* and x1 in that order. Here x1 is the input value for the sampled +* transformation (these are spaced on the regular grid specified +* by lbnd, ubnd and npoint), and y1 is the output position produced +* by the sampled transformation. The returned values are scaled so +* that each column has an RMS value of 1.0. The scaling factors that +* convert scaled values into original values are returned in "scales". +* The returned pointer should be freed using astFreeDouble when no +* longer needed. + +*/ + +/* Local Variables: */ + AstPointSet *ps1; + AstPointSet *ps2; + double **result; + double *p0; + double *p1; + double *ptr1[ 1 ]; + double *ptr2[ 1 ]; + double delta0; + double rms; + double sum; + double val0; + int i; + int icol; + +/* Initialise returned value */ + result = table; + *nsamp = 0; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Ensure we have a table of the correct size. */ + *nsamp = npoint; + if( !result ) result = astCalloc( 2, sizeof( double * ) ); + if( result ) { + for( i = 0; i < 2; i++ ) { + result[ i ] = astRealloc( result[ i ] , (*nsamp)*sizeof( double ) ); + } + } + +/* Work out the step sizes for the grid. */ + delta0 = ( ubnd - lbnd )/( npoint - 1 ); + +/* Create a PointSet to hold the grid of input positions. Use column 1 + of the table to hold the PointSet values. */ + ps1 = astPointSet( *nsamp, 1, " ", status ); + ptr1[ 0 ] = result[ 1 ]; + astSetPoints( ps1, ptr1 ); + +/* Create a PointSet to hold the grid of output positions. Use column 0 + of the table to hold the PointSet values. */ + ps2 = astPointSet( *nsamp, 1, " ", status ); + ptr2[ 0 ] = result[ 0 ]; + astSetPoints( ps2, ptr2 ); + if( astOK ) { + +/* Calculate the grid of input positions and store in the PointSet and + therefore also in the returned table. */ + val0 = lbnd; + p0 = ptr1[ 0 ]; + for( i = 0; i < npoint; i++ ) { + *(p0++) = val0; + val0 += delta0; + } + +/* Transform the input grid to get the output grid. */ + (void) astTransform( this, ps1, forward, ps2 ); + +/* Scale each column in turn. */ + for( icol = 0; icol < 2; icol++ ) { + +/* Find the RMS of the values in the column. */ + sum = 0.0; + p0 = result[ icol ]; + p1 = p0 + (*nsamp); + for( ; p0 < p1; p0++ ) sum += ( *p0 )*( *p0 ); + rms = sqrt( sum/(*nsamp) ); + +/* Divide the table values by the RMS. */ + p0 = result[ icol ]; + p1 = p0 + (*nsamp); + for( ; p0 < p1; p0++ ) *p0 /= rms; + +/* Return the RMS as the scale factor. */ + scales[ icol ] = rms; + } + } + +/* Free resources */ + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + +/* If an error occurred, free the returned array. */ + if( !astOK ) result = astFreeDouble( result ); + +/* Return a pointer to the table. */ + return result; +} + +static double **SamplePoly2D( AstPolyMap *this, int forward, double **table, + const double *lbnd, const double *ubnd, int npoint, + int *nsamp, double scales[4], int *status ){ +/* +* Name: +* SamplePoly2D + +* Purpose: +* Create a table of input and output positions for a 2D PolyMap. + +* Type: +* Private function. + +* Synopsis: +* double **SamplePoly2D( AstPolyMap *this, int forward, double **table, +* const double *lbnd, const double *ubnd, int npoint, +* int *nsamp, double scales[4], int *status ) + +* Description: +* This function creates a table containing samples of the requested +* polynomial transformation at a grid of input points. This grid covers +* the user-supplied region, using "npoint" points on each axis. + +* Parameters: +* this +* The PolyMap. +* forward +* If non-zero, then the forward PolyMap transformation is sampled. +* Otherwise the inverse transformation is sampled. +* table +* Pointer to a previous table created by this function, which is +* to be re-used, or NULL. +* lbnd +* An array holding the lower bounds of a rectangular region within +* the PolyMap's input space (if "forward" is non-zero) or output +* space (if "forward" is zero). The new polynomial will be +* evaluated over this rectangle. +* ubnd +* An array holding the upper bounds of a rectangular region within +* the PolyMap's input space (if "forward" is non-zero) or output +* space (if "forward" is zero). The new polynomial will be +* evaluated over this rectangle. +* npoint +* The number of points along each edge of the grid. +* nsamp +* Address of an int in which to return the total number of samples +* in the returned table. +* scales +* Array in which to return the scaling factors for the four +* columns of the returned table. Multiplying the returned table +* values by the scale factor produces PolyMap input or output axis +* values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to an array of 4 pointers. Each of these pointers points +* to an array of "nsamp" doubles, being the sampled values for y1, +* y2, x1 or x2 in that order. Here (x1,x2) are the input values +* for the sampled transformation (these are spaced on the regular +* grid specified by lbnd, ubnd and npoint), and (y1,y2) are the +* output positions produced by the sampled transformation. The +* returned values are scaled so that each column has an RMS value +* of 1.0. The scaling factors that convert scaled values into +* original values are returned in "scales". The returned pointer +* should be freed using astFreeDouble when no longer needed. + +*/ + +/* Local Variables: */ + AstPointSet *ps1; + AstPointSet *ps2; + double **result; + double *p0; + double *p1; + double *ptr1[ 2 ]; + double *ptr2[ 2 ]; + double delta1; + double delta0; + double rms; + double sum; + double val0; + double val1; + int i; + int icol; + int j; + +/* Initialise returned value */ + result = table; + *nsamp = 0; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Ensure we have a table of the correct size. */ + *nsamp = npoint*npoint; + if( !result ) result = astCalloc( 4, sizeof( double * ) ); + if( result ) { + for( i = 0; i < 4; i++ ) { + result[ i ] = astRealloc( result[ i ] , (*nsamp)*sizeof( double ) ); + } + } + +/* Work out the step sizes for the grid. */ + delta0 = ( ubnd[ 0 ] - lbnd[ 0 ] )/( npoint - 1 ); + delta1 = ( ubnd[ 1 ] - lbnd[ 1 ] )/( npoint - 1 ); + +/* Create a PointSet to hold the grid of input positions. Use columns 2 + and 3 of the table to hold the PointSet values. */ + ps1 = astPointSet( *nsamp, 2, " ", status ); + ptr1[ 0 ] = result[ 2 ]; + ptr1[ 1 ] = result[ 3 ]; + astSetPoints( ps1, ptr1 ); + +/* Create a PointSet to hold the grid of output positions. Use columns 0 + and 1 of the table to hold the PointSet values. */ + ps2 = astPointSet( *nsamp, 2, " ", status ); + ptr2[ 0 ] = result[ 0 ]; + ptr2[ 1 ] = result[ 1 ]; + astSetPoints( ps2, ptr2 ); + if( astOK ) { + +/* Calculate the grid of input positions and store in the PointSet and + therefore also in the returned table. */ + val0 = lbnd[ 0 ]; + p0 = ptr1[ 0 ]; + p1 = ptr1[ 1 ]; + for( i = 0; i < npoint; i++ ) { + val1 = lbnd[ 1 ]; + for( j = 0; j < npoint; j++ ) { + *(p0++) = val0; + *(p1++) = val1; + val1 += delta1; + } + val0 += delta0; + } + +/* Transform the input grid to get the output grid. */ + (void) astTransform( this, ps1, forward, ps2 ); + +/* Scale each pair of columns in turn. Use the ssame scale factor for + each axis in order to ensure an isotropic metric. */ + for( icol = 0; icol < 4; icol += 2 ) { + +/* Find the RMS of the values in the two columns. */ + sum = 0.0; + p0 = result[ icol ]; + p1 = p0 + (*nsamp); + for( ; p0 < p1; p0++ ) sum += ( *p0 )*( *p0 ); + + p0 = result[ icol + 1 ]; + p1 = p0 + (*nsamp); + for( ; p0 < p1; p0++ ) sum += ( *p0 )*( *p0 ); + + rms = sqrt( sum/(2*(*nsamp)) ); + +/* Divide the table values by the RMS. */ + p0 = result[ icol ]; + p1 = p0 + (*nsamp); + for( ; p0 < p1; p0++ ) *p0 /= rms; + + p0 = result[ icol + 1 ]; + p1 = p0 + (*nsamp); + for( ; p0 < p1; p0++ ) *p0 /= rms; + +/* Return the RMS as the scale factor. */ + scales[ icol ] = rms; + scales[ icol + 1 ] = rms; + } + } + +/* Free resources */ + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + +/* If an error occurred, free the returned array. */ + if( !astOK ) result = astFreeDouble( result ); + +/* Return a pointer to the table. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* PolyMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a PolyMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the PolyMap. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstPolyMap *this; /* Pointer to the PolyMap structure */ + double dval; /* Floating point attribute value */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PolyMap structure. */ + this = (AstPolyMap *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* IterInverse. */ +/* ------------ */ + if ( nc = 0, + ( 1 == astSscanf( setting, "iterinverse= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetIterInverse( this, ival ); + +/* NiterInverse. */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "niterinverse= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetNiterInverse( this, ival ); + +/* TolInverse. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "tolinverse= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetTolInverse( this, dval ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void StoreArrays( AstPolyMap *this, int forward, int ncoeff, + const double *coeff, int *status ){ +/* +* Name: +* StoreArrays + +* Purpose: +* Store the dynamic arrays for a single transformation within a PolyMap + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* void StoreArrays( AstPolyMap *this, int forward, int ncoeff, +* const double *coeff, int *status ) + +* Class Membership: +* PolyMap initialiser. + +* Description: +* This function sets up the arrays within a PolyMap structure that +* describes either the forward or inverse transformation. + +* Parameters: +* this +* The PolyMap. +* forward +* If non-zero, replace the forward transformation. Otherwise, +* replace the inverse transformation. +* ncoeff +* The number of non-zero coefficients necessary to define the +* specified transformation of the PolyMap. If zero is supplied, the +* transformation will be undefined. +* coeff +* An array containing "ncof*( 2 + nin )" elements. Each group +* of "2 + nin" adjacent elements describe a single coefficient of +* the transformation. Within each such group, the first +* element is the coefficient value; the next element is the +* integer index of the PolyMap output which uses the coefficient +* within its defining polynomial (the first output has index 1); +* the remaining elements of the group give the integer powers to +* use with each input coordinate value (powers must not be +* negative). +* status +* Pointer to inherited status. +*/ + +/* Local Variables: */ + const double *group; /* Pointer to start of next coeff. description */ + int *pows; /* Pointer to powers for current coeff. */ + int gsize; /* Length of each coeff. description */ + int i; /* Loop count */ + int ico; /* Index of next coeff. for current input or output */ + int iin; /* Input index extracted from coeff. description */ + int iout; /* Output index extracted from coeff. description */ + int j; /* Loop count */ + int nin; /* Number of inputs */ + int nout; /* Number of outputs */ + int pow; /* Power extracted from coeff. description */ + +/* Check the global status. */ + if ( !astOK ) return; + +/* Get the number of inputs and outputs. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + +/* First Free any existing arrays. */ + FreeArrays( this, forward, status ); + +/* Now initialise the forward transformation, if required. */ + if( forward && ncoeff > 0 ) { + +/* Create the arrays decribing the forward transformation. */ + this->ncoeff_f = astMalloc( sizeof( int )*(size_t) nout ); + this->mxpow_f = astMalloc( sizeof( int )*(size_t) nin ); + this->power_f = astMalloc( sizeof( int ** )*(size_t) nout ); + this->coeff_f = astMalloc( sizeof( double * )*(size_t) nout ); + if( astOK ) { + +/* Initialise the count of coefficients for each output coordinate to zero. */ + for( i = 0; i < nout; i++ ) this->ncoeff_f[ i ] = 0; + +/* Initialise max power for each input coordinate to zero. */ + for( j = 0; j < nin; j++ ) this->mxpow_f[ j ] = 0; + +/* Scan through the supplied forward coefficient array, counting the + number of coefficients which relate to each output. Also find the + highest power used for each input axis. Report errors if any unusable + values are found in the supplied array. */ + group = coeff; + gsize = 2 + nin; + for( i = 0; i < ncoeff && astOK; i++, group += gsize ) { + + iout = floor( group[ 1 ] + 0.5 ); + if( iout < 1 || iout > nout ) { + astError( AST__BADCI, "astInitPolyMap(%s): Forward " + "coefficient %d referred to an illegal output " + "coordinate %d.", status, astGetClass( this ), i + 1, + iout ); + astError( AST__BADCI, "This number should be in the " + "range 1 to %d.", status, nout ); + break; + } + + this->ncoeff_f[ iout - 1 ]++; + + for( j = 0; j < nin; j++ ) { + pow = floor( group[ 2 + j ] + 0.5 ); + if( pow < 0 ) { + astError( AST__BADPW, "astInitPolyMap(%s): Forward " + "coefficient %d has a negative power (%d) " + "for input coordinate %d.", status, + astGetClass( this ), i + 1, pow, j + 1 ); + astError( AST__BADPW, "All powers should be zero or " + "positive." , status); + break; + } + if( pow > this->mxpow_f[ j ] ) this->mxpow_f[ j ] = pow; + } + } + +/* Allocate the arrays to store the input powers associated with each + coefficient, and the coefficient values. Reset the coefficient count + for each axis to zero afterwards so that we can use the array as an index + to the next vacant slot withint he following loop. */ + for( i = 0; i < nout; i++ ) { + this->power_f[ i ] = astMalloc( sizeof( int * )* + (size_t) this->ncoeff_f[ i ] ); + this->coeff_f[ i ] = astMalloc( sizeof( double )* + (size_t) this->ncoeff_f[ i ] ); + this->ncoeff_f[ i ] = 0; + } + + if( astOK ) { + +/* Extract the coefficient values and powers form the supplied array and + store them in the arrays created above. */ + group = coeff; + for( i = 0; i < ncoeff && astOK; i++, group += gsize ) { + iout = floor( group[ 1 ] + 0.5 ) - 1; + ico = ( this->ncoeff_f[ iout ] )++; + this->coeff_f[ iout ][ ico ] = group[ 0 ]; + + pows = astMalloc( sizeof( int )*(size_t) nin ); + this->power_f[ iout ][ ico ] = pows; + if( astOK ) { + for( j = 0; j < nin; j++ ) { + pows[ j ] = floor( group[ 2 + j ] + 0.5 ); + } + } + } + } + } + } + +/* Now initialise the inverse transformation, if required. */ + if( !forward && ncoeff > 0 ) { + +/* Create the arrays decribing the inverse transformation. */ + this->ncoeff_i = astMalloc( sizeof( int )*(size_t) nin ); + this->mxpow_i = astMalloc( sizeof( int )*(size_t) nout ); + this->power_i = astMalloc( sizeof( int ** )*(size_t) nin ); + this->coeff_i = astMalloc( sizeof( double * )*(size_t) nin ); + if( astOK ) { + +/* Initialise the count of coefficients for each input coordinate to zero. */ + for( i = 0; i < nin; i++ ) this->ncoeff_i[ i ] = 0; + +/* Initialise max power for each output coordinate to zero. */ + for( j = 0; j < nout; j++ ) this->mxpow_i[ j ] = 0; + +/* Scan through the supplied inverse coefficient array, counting the + number of coefficients which relate to each input. Also find the + highest power used for each output axis. Report errors if any unusable + values are found in the supplied array. */ + group = coeff; + + gsize = 2 + nout; + for( i = 0; i < ncoeff && astOK; i++, group += gsize ) { + + iin = floor( group[ 1 ] + 0.5 ); + if( iin < 1 || iin > nin ) { + astError( AST__BADCI, "astInitPolyMap(%s): Inverse " + "coefficient %d referred to an illegal input " + "coordinate %d.", status, astGetClass( this ), + i + 1, iin ); + astError( AST__BADCI, "This number should be in the " + "range 1 to %d.", status, nin ); + break; + } + + this->ncoeff_i[ iin - 1 ]++; + + for( j = 0; j < nout; j++ ) { + pow = floor( group[ 2 + j ] + 0.5 ); + if( pow < 0 ) { + astError( AST__BADPW, "astInitPolyMap(%s): Inverse " + "coefficient %d has a negative power (%d) " + "for output coordinate %d.", status, + astGetClass( this ), i + 1, pow, j + 1 ); + astError( AST__BADPW, "All powers should be zero or " + "positive." , status); + break; + } + if( pow > this->mxpow_i[ j ] ) this->mxpow_i[ j ] = pow; + } + } + +/* Allocate the arrays to store the output powers associated with each + coefficient, and the coefficient values. Reset the coefficient count + for each axis to zero afterwards so that we can use the array as an index + to the next vacant slot within the following loop. */ + for( i = 0; i < nin; i++ ) { + this->power_i[ i ] = astMalloc( sizeof( int * )* + (size_t) this->ncoeff_i[ i ] ); + this->coeff_i[ i ] = astMalloc( sizeof( double )* + (size_t) this->ncoeff_i[ i ] ); + this->ncoeff_i[ i ] = 0; + } + + if( astOK ) { + +/* Extract the coefficient values and powers form the supplied array and + store them in the arrays created above. */ + group = coeff; + for( i = 0; i < ncoeff && astOK; i++, group += gsize ) { + iin = floor( group[ 1 ] + 0.5 ) - 1; + ico = ( this->ncoeff_i[ iin ] )++; + this->coeff_i[ iin ][ ico ] = group[ 0 ]; + + pows = astMalloc( sizeof( int )*(size_t) nout ); + this->power_i[ iin ][ ico ] = pows; + if( astOK ) { + for( j = 0; j < nout; j++ ) { + pows[ j ] = floor( group[ 2 + j ] + 0.5 ); + } + } + } + } + } + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a PolyMap's attributes. + +* Parameters: +* this +* Pointer to the PolyMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPolyMap *this; /* Pointer to the PolyMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the PolyMap structure. */ + this = (AstPolyMap *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* IterInverse. */ +/* ------------ */ + if ( !strcmp( attrib, "iterinverse" ) ) { + result = astTestIterInverse( this ); + +/* NiterInverse. */ +/* ------------- */ + } else if ( !strcmp( attrib, "niterinverse" ) ) { + result = astTestNiterInverse( this ); + +/* TolInverse. */ +/* ----------- */ + } else if ( !strcmp( attrib, "tolinverse" ) ) { + result = astTestTolInverse( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a PolyMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* PolyMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a PolyMap and a set of points encapsulated in a +* PointSet and transforms the points. + +* Parameters: +* this +* Pointer to the PolyMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of columns in the PolyMap being applied. +* - The number of coordinate values per point in the output PointSet will +* equal the number of rows in the PolyMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstPolyMap *map; /* Pointer to PolyMap to be applied */ + double **coeff; /* Pointer to coefficient value arrays */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double **work; /* Pointer to exponentiated axis values */ + double *outcof; /* Pointer to next coefficient value */ + double *pwork; /* Pointer to exponentiated axis values */ + double outval; /* Output axis value */ + double term; /* Term to be added to output value */ + double x; /* Input axis value */ + double xp; /* Exponentiated input axis value */ + int ***power; /* Pointer to coefficient power arrays */ + int **outpow; /* Pointer to next set of axis powers */ + int *mxpow; /* Pointer to max used power for each input */ + int *ncoeff; /* Pointer to no. of coefficients */ + int in_coord; /* Index of output coordinate */ + int ico; /* Coefficient index */ + int ip; /* Axis power */ + int nc; /* No. of coefficients in polynomial */ + int ncoord_in; /* Number of coordinates per input point */ + int ncoord_out; /* Number of coordinates per output point */ + int npoint; /* Number of points */ + int out_coord; /* Index of output coordinate */ + int point; /* Loop counter for points */ + int pow; /* Next axis power */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the PolyMap. */ + map = (AstPolyMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* If we are using the inverse transformatiom, and the IterInverse + attribute is non-zero, use an iterative inverse algorithm rather than any + inverse transformation defined within the PolyMap. */ + if( !forward && astGetIterInverse(map) ) { + IterInverse( map, in, result, status ); + +/* Otherwise, determine the numbers of points and coordinates per point from + the input and output PointSets and obtain pointers for accessing the input + and output coordinate values. */ + } else { + ncoord_in = astGetNcoord( in ); + ncoord_out = astGetNcoord( result ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Get a pointer to the arrays holding the required coefficient + values and powers, according to the direction of mapping required. */ + if ( forward ) { + ncoeff = map->ncoeff_f; + coeff = map->coeff_f; + power = map->power_f; + mxpow = map->mxpow_f; + } else { + ncoeff = map->ncoeff_i; + coeff = map->coeff_i; + power = map->power_i; + mxpow = map->mxpow_i; + } + +/* Allocate memory to hold the required powers of the input axis values. */ + work = astMalloc( sizeof( double * )*(size_t) ncoord_in ); + for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) { + work[ in_coord ] = astMalloc( sizeof( double )*(size_t) ( mxpow[ in_coord ] + 1 ) ); + } + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* Loop to apply the polynomial to each point in turn.*/ + for ( point = 0; point < npoint; point++ ) { + +/* Find the required powers of the input axis values and store them in + the work array. */ + for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) { + pwork = work[ in_coord ]; + pwork[ 0 ] = 1.0; + x = ptr_in[ in_coord ][ point ]; + if( x == AST__BAD ) { + for( ip = 1; ip <= mxpow[ in_coord ]; ip++ ) pwork[ ip ] = AST__BAD; + } else { + for( ip = 1; ip <= mxpow[ in_coord ]; ip++ ) { + pwork[ ip ] = pwork[ ip - 1 ]*x; + } + } + } + +/* Loop round each output. */ + for( out_coord = 0; out_coord < ncoord_out; out_coord++ ) { + +/* Initialise the output value. */ + outval = 0.0; + +/* Get pointers to the coefficients and powers for this output. */ + outcof = coeff[ out_coord ]; + outpow = power[ out_coord ]; + +/* Loop round all polynomial coefficients.*/ + nc = ncoeff[ out_coord ]; + for ( ico = 0; ico < nc && outval != AST__BAD; + ico++, outcof++, outpow++ ) { + +/* Initialise the current term to be equal to the value of the coefficient. + If it is bad, store a bad output value. */ + term = *outcof; + if( term == AST__BAD ) { + outval = AST__BAD; + +/* Otherwise, loop round all inputs */ + } else { + for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) { + +/* Get the power of the current input axis value used by the current + coefficient. If it is zero, pass on. */ + pow = (*outpow)[ in_coord ]; + if( pow > 0 ) { + +/* Get the axis value raised to the appropriate power. */ + xp = work[ in_coord ][ pow ]; + +/* If bad, set the output value bad and break. */ + if( xp == AST__BAD ) { + outval = AST__BAD; + break; + +/* Otherwise multiply the current term by the exponentiated axis value. */ + } else { + term *= xp; + } + } + } + } + +/* Increment the output value by the current term of the polynomial. */ + outval += term; + + } + +/* Store the output value. */ + ptr_out[ out_coord ][ point ] = outval; + + } + } + } + +/* Free resources. */ + for( in_coord = 0; in_coord < ncoord_in; in_coord++ ) { + work[ in_coord ] = astFree( work[ in_coord ] ); + } + work = astFree( work ); + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* IterInverse + +* Purpose: +* Provide an iterative inverse transformation? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute indicates whether the inverse transformation of +* the PolyMap should be implemented via an iterative Newton-Raphson +* approximation that uses the forward transformation to transform +* candidate input positions until an output position is found which +* is close to the required output position. By default, an iterative +* inverse is provided if, and only if, no inverse polynomial was supplied +* when the PolyMap was constructed. +* +* The NiterInverse and TolInverse attributes provide parameters that +* control the behaviour of the inverse approcimation method. + +* Applicability: +* PolyMap +* All PolyMaps have this attribute. + +* Notes: +* - An iterative inverse can only be used if the PolyMap has equal +* numbers of inputs and outputs, as given by the Nin and Nout +* attributes. An error will be reported if IterInverse is set non-zero +* for a PolyMap that does not meet this requirement. + +*att-- +*/ +astMAKE_CLEAR(PolyMap,IterInverse,iterinverse,-INT_MAX) +astMAKE_GET(PolyMap,IterInverse,int,0,( ( this->iterinverse == -INT_MAX ) ? + (this->ncoeff_i == 0) : this->iterinverse )) +astMAKE_SET(PolyMap,IterInverse,int,iterinverse, + (((astGetNin(this)==astGetNout(this))||!value)?((value?1:0)):(astError(AST__ATTIN,"astSetIterInverse(%s):" + "Cannot use an iterative inverse because the %s has unequal numbers of " + "inputs and outputs.", status, astGetClass(this),astGetClass(this)),this->iterinverse))) +astMAKE_TEST(PolyMap,IterInverse,( this->iterinverse != -INT_MAX )) + +/* NiterInverse. */ +/* --------- */ +/* +*att++ +* Name: +* NiterInverse + +* Purpose: +* Maximum number of iterations for the iterative inverse transformation. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute controls the iterative inverse transformation +* used if the IterInverse attribute is non-zero. +* +* Its value gives the maximum number of iterations of the +* Newton-Raphson algorithm to be used for each transformed position. +* The default value is 4. See also attribute TolInverse. + +* Applicability: +* PolyMap +* All PolyMaps have this attribute. + +*att-- +*/ +astMAKE_CLEAR(PolyMap,NiterInverse,niterinverse,-INT_MAX) +astMAKE_GET(PolyMap,NiterInverse,int,0,( this->niterinverse == -INT_MAX ? 4 : this->niterinverse)) +astMAKE_SET(PolyMap,NiterInverse,int,niterinverse,value) +astMAKE_TEST(PolyMap,NiterInverse,( this->niterinverse != -INT_MAX )) + +/* TolInverse. */ +/* ----------- */ +/* +*att++ +* Name: +* TolInverse + +* Purpose: +* Target relative error for the iterative inverse transformation. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute controls the iterative inverse transformation +* used if the IterInverse attribute is non-zero. +* +* Its value gives the target relative error in teh axis values of +* each transformed position. Further iterations will be performed +* until the target relative error is reached, or the maximum number +* of iterations given by attribute NiterInverse is reached. + +* The default value is 1.0E-6. + +* Applicability: +* PolyMap +* All PolyMaps have this attribute. +*att-- +*/ +astMAKE_CLEAR(PolyMap,TolInverse,tolinverse,AST__BAD) +astMAKE_GET(PolyMap,TolInverse,double,0.0,( this->tolinverse == AST__BAD ? 1.0E-6 : this->tolinverse)) +astMAKE_SET(PolyMap,TolInverse,double,tolinverse,value) +astMAKE_TEST(PolyMap,TolInverse,( this->tolinverse != AST__BAD )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for PolyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for PolyMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the +* coefficients associated with the input PolyMap. +*/ + + +/* Local Variables: */ + AstPolyMap *in; /* Pointer to input PolyMap */ + AstPolyMap *out; /* Pointer to output PolyMap */ + int nin; /* No. of input coordinates */ + int nout; /* No. of output coordinates */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output PolyMaps. */ + in = (AstPolyMap *) objin; + out = (AstPolyMap *) objout; + +/* Nullify the pointers stored in the output object since these will + currently be pointing at the input data (since the output is a simple + byte-for-byte copy of the input). Otherwise, the input data could be + freed by accidient if the output object is deleted due to an error + occuring in this function. */ + out->ncoeff_f = NULL; + out->power_f = NULL; + out->coeff_f = NULL; + out->mxpow_f = NULL; + + out->ncoeff_i = NULL; + out->power_i = NULL; + out->coeff_i = NULL; + out->mxpow_i = NULL; + + out->jacobian = NULL; + +/* Get the number of inputs and outputs of the uninverted Mapping. */ + nin = ( (AstMapping *) in )->nin; + nout = ( (AstMapping *) in )->nout; + +/* Copy the number of coefficients associated with each output of the forward + transformation. */ + if( in->ncoeff_f ) { + out->ncoeff_f = (int *) astStore( NULL, (void *) in->ncoeff_f, + sizeof( int )*(size_t) nout ); + +/* Copy the maximum power of each input axis value used by the forward + transformation. */ + out->mxpow_f = (int *) astStore( NULL, (void *) in->mxpow_f, + sizeof( int )*(size_t) nin ); + +/* Copy the coefficient values used by the forward transformation. */ + if( in->coeff_f ) { + out->coeff_f = astMalloc( sizeof( double * )*(size_t) nout ); + if( astOK ) { + for( i = 0; i < nout; i++ ) { + out->coeff_f[ i ] = (double *) astStore( NULL, (void *) in->coeff_f[ i ], + sizeof( double )*(size_t) in->ncoeff_f[ i ] ); + } + } + } + +/* Copy the input axis powers associated with each coefficient of the forward + transformation. */ + if( in->power_f ) { + out->power_f = astMalloc( sizeof( int ** )*(size_t) nout ); + if( astOK ) { + for( i = 0; i < nout; i++ ) { + out->power_f[ i ] = astMalloc( sizeof( int * )*(size_t) in->ncoeff_f[ i ] ); + if( astOK ) { + for( j = 0; j < in->ncoeff_f[ i ]; j++ ) { + out->power_f[ i ][ j ] = (int *) astStore( NULL, (void *) in->power_f[ i ][ j ], + sizeof( int )*(size_t) nin ); + } + } + } + } + } + } + +/* Do the same for the inverse transformation. */ + if( in->ncoeff_i ) { + out->ncoeff_i = (int *) astStore( NULL, (void *) in->ncoeff_i, + sizeof( int )*(size_t) nin ); + + out->mxpow_i = (int *) astStore( NULL, (void *) in->mxpow_i, + sizeof( int )*(size_t) nout ); + + if( in->coeff_i ) { + out->coeff_i = astMalloc( sizeof( double * )*(size_t) nin ); + if( astOK ) { + for( i = 0; i < nin; i++ ) { + out->coeff_i[ i ] = (double *) astStore( NULL, (void *) in->coeff_i[ i ], + sizeof( double )*(size_t) in->ncoeff_i[ i ] ); + } + } + } + + if( in->power_i ) { + out->power_i = astMalloc( sizeof( int ** )*(size_t) nin ); + if( astOK ) { + for( i = 0; i < nin; i++ ) { + out->power_i[ i ] = astMalloc( sizeof( int * )*(size_t) in->ncoeff_i[ i ] ); + if( astOK ) { + for( j = 0; j < in->ncoeff_i[ i ]; j++ ) { + out->power_i[ i ][ j ] = (int *) astStore( NULL, (void *) in->power_i[ i ][ j ], + sizeof( int )*(size_t) nout ); + } + } + } + } + } + } + +/* If an error has occurred, free al the resources allocated above. */ + if( !astOK ) { + FreeArrays( out, 1, status ); + FreeArrays( out, 0, status ); + } + + return; + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for PolyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for PolyMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPolyMap *this; + int nc; + int ic; + int lstat; + int error; + +/* Obtain a pointer to the PolyMap structure. */ + this = (AstPolyMap *) obj; + +/* Free the arrays. */ + FreeArrays( this, 1, status ); + FreeArrays( this, 0, status ); + +/* Free the resources used to store the Jacobian of the forward + transformation. */ + if( this->jacobian ) { + +/* Get the number of PolyMap inputs. We need to clear any error status + first since astGetNin returns zero if an error has occurred. The + Jacobian will only be non-NULL if the number of inputs and outputs + are equal. */ + error = !astOK; + if( error ) { + lstat = astStatus; + astClearStatus; + } + nc = astGetNin( this ); + if( error ) astSetStatus( lstat ); + + for( ic = 0; ic < nc; ic++ ) { + (this->jacobian)[ ic ] = astAnnul( (this->jacobian)[ ic ] ); + } + this->jacobian = astFree( this->jacobian ); + } +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for PolyMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the PolyMap class to an output Channel. + +* Parameters: +* this +* Pointer to the PolyMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstPolyMap *this; /* Pointer to the PolyMap structure */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char comm[ 100 ]; /* Buffer for comment string */ + double dval; /* Floating point attribute value */ + int i; /* Loop index */ + int iv; /* Vectorised keyword index */ + int ival; /* Integer value */ + int j; /* Loop index */ + int k; /* Loop index */ + int nin; /* No. of input coords */ + int nout; /* No. of output coords */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the PolyMap structure. */ + this = (AstPolyMap *) this_object; + +/* Find the number of inputs and outputs of the uninverted Mapping. */ + nin = ( (AstMapping *) this )->nin; + nout = ( (AstMapping *) this )->nout; + +/* Write out values representing the instance variables for the + PolyMap class. */ + +/* First do the forward transformation arrays. Check they are used. */ + if( this->ncoeff_f ) { + +/* Store the maximum power of each input axis value used by the forward + transformation. */ + for( i = 0; i < nin; i++ ){ + (void) sprintf( buff, "MPF%d", i + 1 ); + (void) sprintf( comm, "Max. power of input %d in any forward polynomial", i + 1 ); + astWriteInt( channel, buff, 1, 1, (this->mxpow_f)[ i ], comm ); + } + +/* Store the number of coefficients associated with each output of the forward + transformation. */ + for( i = 0; i < nout; i++ ){ + (void) sprintf( buff, "NCF%d", i + 1 ); + (void) sprintf( comm, "No. of coeff.s for forward polynomial %d", i + 1 ); + astWriteInt( channel, buff, 1, 1, (this->ncoeff_f)[ i ], comm ); + } + +/* Store the coefficient values used by the forward transformation. */ + iv = 1; + for( i = 0; i < nout; i++ ){ + for( j = 0; j < this->ncoeff_f[ i ]; j++, iv++ ){ + if( (this->coeff_f)[ i ][ j ] != AST__BAD ) { + (void) sprintf( buff, "CF%d", iv ); + (void) sprintf( comm, "Coeff %d of forward polynomial %d", j + 1, i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->coeff_f)[ i ][ j ], comm ); + } + } + } + +/* Store the input axis powers associated with each coefficient of the forward + transformation. */ + iv = 1; + for( i = 0; i < nout; i++ ){ + for( j = 0; j < this->ncoeff_f[ i ]; j++ ){ + for( k = 0; k < nin; k++, iv++ ){ + if( (this->power_f)[ i ][ j ][ k ] > 0 ) { + (void) sprintf( buff, "PF%d", iv ); + (void) sprintf( comm, "Power of i/p %d for coeff %d of fwd poly %d", k + 1, j + 1, i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->power_f)[ i ][ j ][ k ], comm ); + } + } + } + } + } + +/* Now do the inverse transformation arrays. Check they are used. */ + if( this->ncoeff_i ) { + +/* Store the maximum power of each output axis value used by the inverse + transformation. */ + for( i = 0; i < nout; i++ ){ + (void) sprintf( buff, "MPI%d", i + 1 ); + (void) sprintf( comm, "Max. power of output %d in any inverse polynomial", i + 1 ); + astWriteInt( channel, buff, 1, 1, (this->mxpow_i)[ i ], comm ); + } + +/* Store the number of coefficients associated with each input of the inverse + transformation. */ + for( i = 0; i < nin; i++ ){ + (void) sprintf( buff, "NCI%d", i + 1 ); + (void) sprintf( comm, "No. of coeff.s for inverse polynomial %d", i + 1 ); + astWriteInt( channel, buff, 1, 1, (this->ncoeff_i)[ i ], comm ); + } + +/* Store the coefficient values used by the inverse transformation. */ + iv = 1; + for( i = 0; i < nin; i++ ){ + for( j = 0; j < this->ncoeff_i[ i ]; j++, iv++ ){ + if( (this->coeff_i)[ i ][ j ] != AST__BAD ) { + (void) sprintf( buff, "CI%d", iv ); + (void) sprintf( comm, "Coeff %d of inverse polynomial %d", j + 1, i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->coeff_i)[ i ][ j ], comm ); + } + } + } + +/* Store the output axis powers associated with each coefficient of the inverse + transformation. */ + iv = 1; + for( i = 0; i < nin; i++ ){ + for( j = 0; j < this->ncoeff_i[ i ]; j++ ){ + for( k = 0; k < nout; k++, iv++ ){ + if( (this->power_i)[ i ][ j ][ k ] > 0 ) { + (void) sprintf( buff, "PI%d", iv ); + (void) sprintf( comm, "Power of o/p %d for coeff %d of inv poly %d", k + 1, j + 1, i + 1 ); + astWriteDouble( channel, buff, 1, 1, (this->power_i)[ i ][ j ][ k ], comm ); + } + } + } + } + } + +/* Use an iterative inverse? */ + set = TestIterInverse( this, status ); + ival = set ? GetIterInverse( this, status ) : astGetIterInverse( this ); + astWriteInt( channel, "IterInv", set, 0, ival, ival ? "Use an iterative inverse" : "Do not use an iterative inverse" ); + +/* Max number of iterations for iterative inverse. */ + set = TestNiterInverse( this, status ); + ival = set ? GetNiterInverse( this, status ) : astGetNiterInverse( this ); + astWriteInt( channel, "NiterInv", set, 0, ival, "Max number of iterations for iterative inverse" ); + +/* Target relative error for iterative inverse. */ + set = TestTolInverse( this, status ); + dval = set ? GetTolInverse( this, status ) : astGetTolInverse( this ); + astWriteDouble( channel, "TolInv", set, 0, dval, "Target relative error for iterative inverse" ); + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPolyMap and astCheckPolyMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(PolyMap,Mapping) +astMAKE_CHECK(PolyMap) + +AstPolyMap *astPolyMap_( int nin, int nout, int ncoeff_f, const double coeff_f[], + int ncoeff_i, const double coeff_i[], const char *options, int *status, ...){ +/* +*++ +* Name: +c astPolyMap +f AST_POLYMAP + +* Purpose: +* Create a PolyMap. + +* Type: +* Public function. + +* Synopsis: +c #include "polymap.h" +c AstPolyMap *astPolyMap( int nin, int nout, int ncoeff_f, const double coeff_f[], +c int ncoeff_i, const double coeff_i[], +c const char *options, ... ) +f RESULT = AST_POLYMAP( NIN, NOUT, NCOEFF_F, COEFF_F, NCOEFF_I, COEFF_I, +f OPTIONS, STATUS ) + +* Class Membership: +* PolyMap constructor. + +* Description: +* This function creates a new PolyMap and optionally initialises +* its attributes. +* +* A PolyMap is a form of Mapping which performs a general polynomial +* transformation. Each output coordinate is a polynomial function of +* all the input coordinates. The coefficients are specified separately +* for each output coordinate. The forward and inverse transformations +* are defined independantly by separate sets of coefficients. If no +* inverse transformation is supplied, an iterative method can be used +* to evaluate the inverse based only on the forward transformation. + +* Parameters: +c nin +f NIN = INTEGER (Given) +* The number of input coordinates. +c nout +f NOUT = INTEGER (Given) +* The number of output coordinates. +c ncoeff_f +f NCOEFF_F = INTEGER (Given) +* The number of non-zero coefficients necessary to define the +* forward transformation of the PolyMap. If zero is supplied, the +* forward transformation will be undefined. +c coeff_f +f COEFF_F( * ) = DOUBLE PRECISION (Given) +* An array containing +c "ncoeff_f*( 2 + nin )" elements. Each group of "2 + nin" +f "NCOEFF_F*( 2 + NIN )" elements. Each group of "2 + NIN" +* adjacent elements describe a single coefficient of the forward +* transformation. Within each such group, the first element is the +* coefficient value; the next element is the integer index of the +* PolyMap output which uses the coefficient within its defining +* polynomial (the first output has index 1); the remaining elements +* of the group give the integer powers to use with each input +* coordinate value (powers must not be negative, and floating +* point values are rounded to the nearest integer). +c If "ncoeff_f" is zero, a NULL pointer may be supplied for "coeff_f". +* +* For instance, if the PolyMap has 3 inputs and 2 outputs, each group +* consisting of 5 elements, A groups such as "(1.2, 2.0, 1.0, 3.0, 0.0)" +* describes a coefficient with value 1.2 which is used within the +* definition of output 2. The output value is incremented by the +* product of the coefficient value, the value of input coordinate +* 1 raised to the power 1, and the value of input coordinate 2 raised +* to the power 3. Input coordinate 3 is not used since its power is +* specified as zero. As another example, the group "(-1.0, 1.0, +* 0.0, 0.0, 0.0 )" describes adds a constant value -1.0 onto +* output 1 (it is a constant value since the power for every input +* axis is given as zero). +* +c Each final output coordinate value is the sum of the "ncoeff_f" terms +c described by the "ncoeff_f" groups within the supplied array. +f Each final output coordinate value is the sum of the "NCOEFF_F" terms +f described by the "NCOEFF_F" groups within the supplied array. +c ncoeff_i +f NCOEFF_I = INTEGER (Given) +* The number of non-zero coefficients necessary to define the +* inverse transformation of the PolyMap. If zero is supplied, the +* inverse transformation will be undefined. +c coeff_i +f COEFF_I( * ) = DOUBLE PRECISION (Given) +* An array containing +c "ncoeff_i*( 2 + nout )" elements. Each group of "2 + nout" +f "NCOEFF_I*( 2 + NOUT )" elements. Each group of "2 + NOUT" +* adjacent elements describe a single coefficient of the inverse +c transformation, using the same schame as "coeff_f", +f transformation, using the same schame as "COEFF_F", +* except that "inputs" and "outputs" are transposed. +c If "ncoeff_i" is zero, a NULL pointer may be supplied for "coeff_i". +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new PolyMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new PolyMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPolyMap() +f AST_POLYMAP = INTEGER +* A pointer to the new PolyMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPolyMap *new; /* Pointer to new PolyMap */ + va_list args; /* Variable argument list */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise the PolyMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPolyMap( NULL, sizeof( AstPolyMap ), !class_init, + &class_vtab, "PolyMap", nin, nout, + ncoeff_f, coeff_f, ncoeff_i, coeff_i ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PolyMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new PolyMap. */ + return new; +} + +AstPolyMap *astPolyMapId_( int nin, int nout, int ncoeff_f, const double coeff_f[], + int ncoeff_i, const double coeff_i[], const char *options, ... ){ +/* +* Name: +* astPolyMapId_ + +* Purpose: +* Create a PolyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "polymap.h" +* AstPolyMap *astPolyMap( int nin, int nout, int ncoeff_f, const double coeff_f[], +* int ncoeff_i, const double coeff_i[], +* const char *options, ... ) + +* Class Membership: +* PolyMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astPolyMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astPolyMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astPolyMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astPolyMap_. + +* Returned Value: +* The ID value associated with the new PolyMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPolyMap *new; /* Pointer to new PolyMap */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the PolyMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPolyMap( NULL, sizeof( AstPolyMap ), !class_init, + &class_vtab, "PolyMap", nin, nout, + ncoeff_f, coeff_f, ncoeff_i, coeff_i ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new PolyMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new PolyMap. */ + return astMakeId( new ); +} + +AstPolyMap *astInitPolyMap_( void *mem, size_t size, int init, + AstPolyMapVtab *vtab, const char *name, + int nin, int nout, int ncoeff_f, const double coeff_f[], + int ncoeff_i, const double coeff_i[], int *status ){ +/* +*+ +* Name: +* astInitPolyMap + +* Purpose: +* Initialise a PolyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "polymap.h" +* AstPolyMap *astInitPolyMap( void *mem, size_t size, int init, +* AstPolyMapVtab *vtab, const char *name, +* int nin, int nout, int ncoeff_f, +* const double coeff_f[], int ncoeff_i, +* const double coeff_i[] ) + +* Class Membership: +* PolyMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new PolyMap object. It allocates memory (if necessary) to accommodate +* the PolyMap plus any additional data associated with the derived class. +* It then initialises a PolyMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a PolyMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the PolyMap is to be initialised. +* This must be of sufficient size to accommodate the PolyMap data +* (sizeof(PolyMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the PolyMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the PolyMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the PolyMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new PolyMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* nin +* The number of input coordinate values per point. This is the +* same as the number of columns in the matrix. +* nout +* The number of output coordinate values per point. This is the +* same as the number of rows in the matrix. +* ncoeff_f +* The number of non-zero coefficients necessary to define the +* forward transformation of the PolyMap. If zero is supplied, the +* forward transformation will be undefined. +* coeff_f +* An array containing "ncoeff_f*( 2 + nin )" elements. Each group +* of "2 + nin" adjacent elements describe a single coefficient of +* the forward transformation. Within each such group, the first +* element is the coefficient value; the next element is the +* integer index of the PolyMap output which uses the coefficient +* within its defining polynomial (the first output has index 1); +* the remaining elements of the group give the integer powers to +* use with each input coordinate value (powers must not be +* negative) +* +* For instance, if the PolyMap has 3 inputs and 2 outputs, each group +* consisting of 5 elements, A groups such as "(1.2, 2.0, 1.0, 3.0, 0.0)" +* describes a coefficient with value 1.2 which is used within the +* definition of output 2. The output value is incremented by the +* product of the coefficient value, the value of input coordinate +* 1 raised to the power 1, and the value of input coordinate 2 raised +* to the power 3. Input coordinate 3 is not used since its power is +* specified as zero. As another example, the group "(-1.0, 1.0, +* 0.0, 0.0, 0.0 )" describes adds a constant value -1.0 onto +* output 1 (it is a constant value since the power for every input +* axis is given as zero). +* +* Each final output coordinate value is the sum of the "ncoeff_f" terms +* described by the "ncoeff_f" groups within the supplied array. +* ncoeff_i +* The number of non-zero coefficients necessary to define the +* inverse transformation of the PolyMap. If zero is supplied, the +* inverse transformation will be undefined. +* coeff_i +* An array containing +* "ncoeff_i*( 2 + nout )" elements. Each group of "2 + nout" +* adjacent elements describe a single coefficient of the inverse +* transformation, using the same schame as "coeff_f", except that +* "inputs" and "outputs" are transposed. + +* Returned Value: +* A pointer to the new PolyMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPolyMap *new; /* Pointer to new PolyMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPolyMapVtab( vtab, name ); + +/* Initialise a Mapping structure (the parent class) as the first component + within the PolyMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstPolyMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nout, 1, 1 ); + if ( astOK ) { + +/* Initialise the PolyMap data. */ +/* ---------------------------- */ + +/* First initialise the pointers in case of errors. */ + new->ncoeff_f = NULL; + new->power_f = NULL; + new->coeff_f = NULL; + new->mxpow_f = NULL; + + new->ncoeff_i = NULL; + new->power_i = NULL; + new->coeff_i = NULL; + new->mxpow_i = NULL; + +/* Store the forward transformation. */ + StoreArrays( new, 1, ncoeff_f, coeff_f, status ); + +/* Store the inverse transformation. */ + StoreArrays( new, 0, ncoeff_i, coeff_i, status ); + +/* Other class attributes. */ + new->iterinverse = -INT_MAX; + new->niterinverse = -INT_MAX; + new->tolinverse = AST__BAD; + new->jacobian = NULL; + +/* If an error occurred, clean up by deleting the new PolyMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new PolyMap. */ + return new; +} + +AstPolyMap *astLoadPolyMap_( void *mem, size_t size, + AstPolyMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPolyMap + +* Purpose: +* Load a PolyMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "polymap.h" +* AstPolyMap *astLoadPolyMap( void *mem, size_t size, +* AstPolyMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* PolyMap loader. + +* Description: +* This function is provided to load a new PolyMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* PolyMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a PolyMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the PolyMap is to be +* loaded. This must be of sufficient size to accommodate the +* PolyMap data (sizeof(PolyMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the PolyMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the PolyMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPolyMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new PolyMap. If this is NULL, a pointer +* to the (static) virtual function table for the PolyMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "PolyMap" is used instead. + +* Returned Value: +* A pointer to the new PolyMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +/* Local Variables: */ + AstPolyMap *new; /* Pointer to the new PolyMap */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int i; /* Loop index */ + int iv; /* Vectorised keyword index */ + int j; /* Loop index */ + int k; /* Loop index */ + int nin; /* No. of input coords */ + int nout; /* No. of output coords */ + int undef; /* Is the transformation undefined? */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this PolyMap. In this case the + PolyMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPolyMap ); + vtab = &class_vtab; + name = "PolyMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPolyMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built PolyMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Get the number of inputs and outputs for the uninverted Mapping. */ + nin = ( (AstMapping *) new )->nin; + nout = ( (AstMapping *) new )->nout; + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "PolyMap" ); + +/* Allocate memory to hold the forward arrays. */ + new->ncoeff_f = astMalloc( sizeof( int )*(size_t) nout ); + new->mxpow_f = astMalloc( sizeof( int )*(size_t) nin ); + new->power_f = astMalloc( sizeof( int ** )*(size_t) nout ); + new->coeff_f = astMalloc( sizeof( double * )*(size_t) nout ); + if( astOK ) { + +/* Assume the forward transformation is defined. */ + undef = 0; + +/* Get the maximum power of each input axis value used by the forward + transformation. Set a flag "undef" if no values relating to the + forward transformation are found (this indicates that the forward + transformation is not defined). */ + for( i = 0; i < nin && !undef; i++ ){ + (void) sprintf( buff, "mpf%d", i + 1 ); + (new->mxpow_f)[ i ] = astReadInt( channel, buff, INT_MAX ); + if( (new->mxpow_f)[ i ] == INT_MAX ) undef = 1; + } + +/* Get the number of coefficients associated with each output of the forward + transformation. */ + for( i = 0; i < nout && !undef; i++ ){ + (void) sprintf( buff, "ncf%d", i + 1 ); + (new->ncoeff_f)[ i ] = astReadInt( channel, buff, INT_MAX ); + if( (new->ncoeff_f)[ i ] == INT_MAX ) undef = 1; + } + +/* Get the coefficient values used by the forward transformation. This + uses new style vectorised key names if available. Otherwise it uses + old style indexed names (which were superceded by vectorised names + because they are shorter and so work better with FitsChans). */ + iv = 0; + for( i = 0; i < nout && !undef; i++ ){ + + (new->coeff_f)[ i ] = astMalloc( sizeof( double )* + (size_t) new->ncoeff_f[ i ] ); + if( astOK ) { + for( j = 0; j < new->ncoeff_f[ i ]; j++ ){ + (void) sprintf( buff, "cf%d", ++iv ); + (new->coeff_f)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD ); + if( (new->coeff_f)[ i ][ j ] == AST__BAD ) { + (void) sprintf( buff, "cf%d_%d", i + 1, j + 1 ); + (new->coeff_f)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD ); + } + } + } + } + +/* Get the input axis powers associated with each coefficient of the forward + transformation. */ + iv = 0; + for( i = 0; i < nout && !undef; i++ ){ + (new->power_f)[ i ] = astMalloc( sizeof( int * )* + (size_t) new->ncoeff_f[ i ] ); + if( astOK ) { + for( j = 0; j < new->ncoeff_f[ i ]; j++ ){ + (new->power_f)[ i ][ j ] = astMalloc( sizeof( int )* (size_t) nin ); + if( astOK ) { + for( k = 0; k < nin; k++ ){ + (void) sprintf( buff, "pf%d", ++iv ); + (new->power_f)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 ); + if( (new->power_f)[ i ][ j ][ k ] == 0 ) { + (void) sprintf( buff, "pf%d_%d_%d", i + 1, j + 1, k + 1 ); + (new->power_f)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 ); + } + } + } + } + } + } + +/* Free the arrays if the forward transformation is undefined. */ + if( undef ) { + new->ncoeff_f = astFree( new->ncoeff_f ); + new->mxpow_f = astFree( new->mxpow_f ); + new->power_f = astFree( new->power_f ); + new->coeff_f = astFree( new->coeff_f ); + } + } + +/* Allocate memory to hold the inverse arrays. */ + new->ncoeff_i = astMalloc( sizeof( int )*(size_t) nin ); + new->mxpow_i = astMalloc( sizeof( int )*(size_t) nout ); + new->power_i = astMalloc( sizeof( int ** )*(size_t) nin ); + new->coeff_i = astMalloc( sizeof( double * )*(size_t) nin ); + if( astOK ) { + +/* Assume the inverse transformation is defined. */ + undef = 0; + +/* Get the maximum power of each output axis value used by the inverse + transformation. Set a flag "undef" if no values relating to the + inverse transformation are found (this indicates that the inverse + transformation is not defined). */ + for( i = 0; i < nout && !undef; i++ ){ + (void) sprintf( buff, "mpi%d", i + 1 ); + (new->mxpow_i)[ i ] = astReadInt( channel, buff, INT_MAX ); + if( (new->mxpow_i)[ i ] == INT_MAX ) undef = 1; + } + +/* Get the number of coefficients associated with each input of the inverse + transformation. */ + for( i = 0; i < nin && !undef; i++ ){ + (void) sprintf( buff, "nci%d", i + 1 ); + (new->ncoeff_i)[ i ] = astReadInt( channel, buff, INT_MAX ); + if( (new->ncoeff_i)[ i ] == INT_MAX ) undef = 1; + } + +/* Get the coefficient values used by the inverse transformation. */ + iv = 0; + for( i = 0; i < nin && !undef; i++ ){ + + (new->coeff_i)[ i ] = astMalloc( sizeof( double )* + (size_t) new->ncoeff_i[ i ] ); + if( astOK ) { + for( j = 0; j < new->ncoeff_i[ i ]; j++ ){ + (void) sprintf( buff, "ci%d", ++iv ); + (new->coeff_i)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD ); + if( (new->coeff_i)[ i ][ j ] == AST__BAD ) { + (void) sprintf( buff, "ci%d_%d", i + 1, j + 1 ); + (new->coeff_i)[ i ][ j ] = astReadDouble( channel, buff, AST__BAD ); + } + } + } + } + +/* Get the output axis powers associated with each coefficient of the inverse + transformation. */ + iv = 0; + for( i = 0; i < nin && !undef; i++ ){ + (new->power_i)[ i ] = astMalloc( sizeof( int * )* + (size_t) new->ncoeff_i[ i ] ); + if( astOK ) { + for( j = 0; j < new->ncoeff_i[ i ]; j++ ){ + (new->power_i)[ i ][ j ] = astMalloc( sizeof( int )* (size_t) nout ); + if( astOK ) { + for( k = 0; k < nout; k++ ){ + (void) sprintf( buff, "pi%d", ++iv ); + (new->power_i)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 ); + if( (new->power_i)[ i ][ j ][ k ] == 0 ) { + (void) sprintf( buff, "pi%d_%d_%d", i + 1, j + 1, k + 1 ); + (new->power_i)[ i ][ j ][ k ] = astReadInt( channel, buff, 0 ); + } + } + } + } + } + } + +/* Free the arrays if the inverse transformation is undefined. */ + if( undef ) { + new->ncoeff_i = astFree( new->ncoeff_i ); + new->mxpow_i = astFree( new->mxpow_i ); + new->power_i = astFree( new->power_i ); + new->coeff_i = astFree( new->coeff_i ); + } + } + +/* Whether to use an iterative inverse transformation. */ + new->iterinverse = astReadInt( channel, "iterinv", -INT_MAX ); + if ( TestIterInverse( new, status ) ) SetIterInverse( new, new->iterinverse, status ); + +/* Max number of iterations for iterative inverse transformation. */ + new->niterinverse = astReadInt( channel, "niterinv", -INT_MAX ); + if ( TestNiterInverse( new, status ) ) SetNiterInverse( new, new->niterinverse, status ); + +/* Target relative error for iterative inverse transformation. */ + new->tolinverse = astReadDouble( channel, "tolinv", AST__BAD ); + if ( TestTolInverse( new, status ) ) SetTolInverse( new, new->tolinverse, status ); + +/* The Jacobian of the PolyMap's forward transformation has not yet been + found. */ + new->jacobian = NULL; + +/* If an error occurred, clean up by deleting the new PolyMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new PolyMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +AstPolyMap *astPolyTran_( AstPolyMap *this, int forward, double acc, + double maxacc, int maxorder, const double *lbnd, + const double *ubnd, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,PolyMap,PolyTran))( this, forward, acc, + maxacc, maxorder, lbnd, + ubnd, status ); +} + + + + diff --git a/polymap.h b/polymap.h new file mode 100644 index 0000000..65138b8 --- /dev/null +++ b/polymap.h @@ -0,0 +1,354 @@ +#if !defined( POLYMAP_INCLUDED ) /* Include this file only once */ +#define POLYMAP_INCLUDED +/* +*+ +* Name: +* polymap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the PolyMap class. + +* Invocation: +* #include "polymap.h" + +* Description: +* This include file defines the interface to the PolyMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* A PolyMap is a form of Mapping which performs a general polynomial +* transformation. Each output coordinate is a polynomial function of +* all the input coordinates. The coefficients are specified separately +* for each output coordinate. The forward and inverse transformations +* are defined independantly by separate sets of coefficients. + +* Inheritance: +* The PolyMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astTransform +* Apply a PolyMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsAPolyMap +* Test class membership. +* astPolyMap +* Create a PolyMap. +* +* Protected: +* astCheckPolyMap +* Validate class membership. +* astInitPolyMap +* Initialise a PolyMap. +* astInitPolyMapVtab +* Initialise the virtual function table for the PolyMap class. +* astLoadPolyMap +* Load a PolyMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstPolyMap +* PolyMap object type. +* +* Protected: +* AstPolyMapVtab +* PolyMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 28-SEP-2003 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* PolyMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstPolyMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int *ncoeff_f; /* No. of coeffs for each forward polynomial */ + int *mxpow_f; /* Max power of each i/p axis for each forward polynomial */ + int ***power_f; /* Pointer to i/p powers for all forward coefficients */ + double **coeff_f; /* Pointer to values of all forward coefficients */ + int *ncoeff_i; /* No. of coeffs for each inverse polynomial */ + int *mxpow_i; /* Max power of each i/p axis for each inverse polynomial */ + int ***power_i; /* Pointer to i/p powers for all inverse coefficients */ + double **coeff_i; /* Pointer to values of all inverse coefficients */ + int iterinverse; /* Use an iterative inverse? */ + int niterinverse; /* Max number of iterations for iterative inverse */ + double tolinverse; /* Target relative error for iterative inverse */ + struct AstPolyMap **jacobian;/* PolyMaps defining Jacobian of forward transformation */ +} AstPolyMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPolyMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstPolyMap *(* PolyTran)( AstPolyMap *, int, double, double, int, const double *, const double *, int * ); + int (*GetIterInverse)( AstPolyMap *, int * ); + int (* TestIterInverse)( AstPolyMap *, int * ); + void (* ClearIterInverse)( AstPolyMap *, int * ); + void (* SetIterInverse)( AstPolyMap *, int, int * ); + + int (*GetNiterInverse)( AstPolyMap *, int * ); + int (* TestNiterInverse)( AstPolyMap *, int * ); + void (* ClearNiterInverse)( AstPolyMap *, int * ); + void (* SetNiterInverse)( AstPolyMap *, int, int * ); + + double (*GetTolInverse)( AstPolyMap *, int * ); + int (* TestTolInverse)( AstPolyMap *, int * ); + void (* ClearTolInverse)( AstPolyMap *, int * ); + void (* SetTolInverse)( AstPolyMap *, double, int * ); + +} AstPolyMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstPolyMapGlobals { + AstPolyMapVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__GETATTRIB_BUFF_LEN + 1 ]; +} AstPolyMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitPolyMapGlobals_( AstPolyMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(PolyMap) /* Check class membership */ +astPROTO_ISA(PolyMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPolyMap *astPolyMap_( int, int, int, const double[], int, const double[], const char *, int *, ...); +#else +AstPolyMap *astPolyMapId_( int, int, int, const double[], int, const double[], const char *, ... )__attribute__((format(printf,7,8))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPolyMap *astInitPolyMap_( void *, size_t, int, AstPolyMapVtab *, const char *, int, int, int, const double[], int, const double[], int * ); + +/* Vtab initialiser. */ +void astInitPolyMapVtab_( AstPolyMapVtab *, const char *, int * ); + +/* Loader. */ +AstPolyMap *astLoadPolyMap_( void *, size_t, AstPolyMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstPolyMap *astPolyTran_( AstPolyMap *, int, double, double, int, const double *, const double *, int * ); + +# if defined(astCLASS) /* Protected */ + int astGetIterInverse_( AstPolyMap *, int * ); + int astTestIterInverse_( AstPolyMap *, int * ); + void astClearIterInverse_( AstPolyMap *, int * ); + void astSetIterInverse_( AstPolyMap *, int, int * ); + + int astGetNiterInverse_( AstPolyMap *, int * ); + int astTestNiterInverse_( AstPolyMap *, int * ); + void astClearNiterInverse_( AstPolyMap *, int * ); + void astSetNiterInverse_( AstPolyMap *, int, int * ); + + double astGetTolInverse_( AstPolyMap *, int * ); + int astTestTolInverse_( AstPolyMap *, int * ); + void astClearTolInverse_( AstPolyMap *, int * ); + void astSetTolInverse_( AstPolyMap *, double, int * ); +#endif + + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPolyMap(this) astINVOKE_CHECK(PolyMap,this,0) +#define astVerifyPolyMap(this) astINVOKE_CHECK(PolyMap,this,1) + +/* Test class membership. */ +#define astIsAPolyMap(this) astINVOKE_ISA(PolyMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astPolyMap astINVOKE(F,astPolyMap_) +#else +#define astPolyMap astINVOKE(F,astPolyMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPolyMap(mem,size,init,vtab,name,nin,nout,ncoeff_f,coeff_f,ncoeff_i,coeff_i) \ +astINVOKE(O,astInitPolyMap_(mem,size,init,vtab,name,nin,nout,ncoeff_f,coeff_f,ncoeff_i,coeff_i,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPolyMapVtab(vtab,name) astINVOKE(V,astInitPolyMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadPolyMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPolyMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckPolyMap to validate PolyMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astPolyTran(this,forward,acc,maxacc,maxorder,lbnd,ubnd) \ +astINVOKE(O,astPolyTran_(astCheckPolyMap(this),forward,acc,maxacc,maxorder,lbnd,ubnd,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ + +#define astClearIterInverse(this) \ + astINVOKE(V,astClearIterInverse_(astCheckPolyMap(this),STATUS_PTR)) +#define astGetIterInverse(this) \ + astINVOKE(V,astGetIterInverse_(astCheckPolyMap(this),STATUS_PTR)) +#define astSetIterInverse(this,value) \ + astINVOKE(V,astSetIterInverse_(astCheckPolyMap(this),value,STATUS_PTR)) +#define astTestIterInverse(this) \ + astINVOKE(V,astTestIterInverse_(astCheckPolyMap(this),STATUS_PTR)) + +#define astClearNiterInverse(this) \ + astINVOKE(V,astClearNiterInverse_(astCheckPolyMap(this),STATUS_PTR)) +#define astGetNiterInverse(this) \ + astINVOKE(V,astGetNiterInverse_(astCheckPolyMap(this),STATUS_PTR)) +#define astSetNiterInverse(this,value) \ + astINVOKE(V,astSetNiterInverse_(astCheckPolyMap(this),value,STATUS_PTR)) +#define astTestNiterInverse(this) \ + astINVOKE(V,astTestNiterInverse_(astCheckPolyMap(this),STATUS_PTR)) + +#define astClearTolInverse(this) \ + astINVOKE(V,astClearTolInverse_(astCheckPolyMap(this),STATUS_PTR)) +#define astGetTolInverse(this) \ + astINVOKE(V,astGetTolInverse_(astCheckPolyMap(this),STATUS_PTR)) +#define astSetTolInverse(this,value) \ + astINVOKE(V,astSetTolInverse_(astCheckPolyMap(this),value,STATUS_PTR)) +#define astTestTolInverse(this) \ + astINVOKE(V,astTestTolInverse_(astCheckPolyMap(this),STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/prepare_all b/prepare_all new file mode 100755 index 0000000..cda505d --- /dev/null +++ b/prepare_all @@ -0,0 +1,41 @@ + +chmod +x ./getnewversion +./getnewversion + +AST_VERSION="`cat ./version.number`" +export AST_VERSION + +echo "AST_VERSION $AST_VERSION" + +PATH="$PWD:$PATH" +export PATH + +AST_DEV=$PWD +export AST_DEV + +AST_REF=$PWD +export AST_REF + +PKG="ast" +export PKG + +PKG_REF=$PWD +export PKG_REF + +PKG_DIR=$PWD +export PKG_DIR + +PROJECTDIR=$PWD +export PROJECTDIR + +MAKEFILE="$PWD/grp-ref.make" +export MAKEFILE + +EXPORT="$HOME" +export EXPORT + +( + builddocs + buildhyperdocs + rmk release +) 2>&1 | tee ~/ast_release.log diff --git a/prepare_docs b/prepare_docs new file mode 100755 index 0000000..8fc0358 --- /dev/null +++ b/prepare_docs @@ -0,0 +1,16 @@ + +AST_DEV=$PWD +export AST_DEV + +AST_REF=$PWD +export AST_REF + +chmod +x ./getnewversion +./getnewversion +AST_VERSION="`cat ./version.number`" +echo "AST_VERSION $AST_VERSION" +export AST_VERSION + +( + builddocs +) 2>&1 | tee ~/ast_release.log diff --git a/prepare_hyperdocs b/prepare_hyperdocs new file mode 100755 index 0000000..3d6e1e4 --- /dev/null +++ b/prepare_hyperdocs @@ -0,0 +1,37 @@ + +chmod +x ./getnewversion +./getnewversion +AST_VERSION="`cat ./version.number`" +echo "AST_VERSION $AST_VERSION" +export AST_VERSION + +PATH="$PWD:$PATH" +export PATH + +AST_DEV=$PWD +export AST_DEV + +AST_REF=$PWD +export AST_REF + +PKG="ast" +export PKG + +PKG_REF=$PWD +export PKG_REF + +PKG_DIR=$PWD +export PKG_DIR + +PROJECTDIR=$PWD +export PROJECTDIR + +MAKEFILE="$PWD/grp-ref.make" +export MAKEFILE + +EXPORT="$HOME" +export EXPORT + +( + buildhyperdocs +) 2>&1 | tee ~/ast_release.log diff --git a/prepare_release b/prepare_release new file mode 100755 index 0000000..84b8dff --- /dev/null +++ b/prepare_release @@ -0,0 +1,39 @@ + +chmod +x ./getnewversion +./getnewversion + +AST_VERSION="`cat ./version.number`" +export AST_VERSION + +echo "AST_VERSION $AST_VERSION" + +PATH="$PWD:$PATH" +export PATH + +AST_DEV=$PWD +export AST_DEV + +AST_REF=$PWD +export AST_REF + +PKG="ast" +export PKG + +PKG_REF=$PWD +export PKG_REF + +PKG_DIR=$PWD +export PKG_DIR + +PROJECTDIR=$PWD +export PROJECTDIR + +MAKEFILE="$PWD/grp-ref.make" +export MAKEFILE + +EXPORT="$HOME" +export EXPORT + +( + rmk release +) 2>&1 | tee ~/ast_release.log diff --git a/prism.c b/prism.c new file mode 100644 index 0000000..f4e26c9 --- /dev/null +++ b/prism.c @@ -0,0 +1,4448 @@ +/* +*class++ +* Name: +* Prism + +* Purpose: +* An extrusion of a region into higher dimensions. + +* Constructor Function: +c astPrism +f AST_PRISM + +* Description: +* A Prism is a Region which represents an extrusion of an existing Region +* into one or more orthogonal dimensions (specified by another Region). +* If the Region to be extruded has N axes, and the Region defining the +* extrusion has M axes, then the resulting Prism will have (M+N) axes. +* A point is inside the Prism if the first N axis values correspond to +* a point inside the Region being extruded, and the remaining M axis +* values correspond to a point inside the Region defining the extrusion. +* +* As an example, a cylinder can be represented by extruding an existing +* Circle, using an Interval to define the extrusion. Ih this case, the +* Interval would have a single axis and would specify the upper and +* lower limits of the cylinder along its length. + +* Inheritance: +* The Prism class inherits from the Region class. + +* Attributes: +* The Prism class does not define any new attributes beyond those +* which are applicable to all Regions. + +* Functions: +c The Prism class does not define any new functions beyond those +f The Prism class does not define any new routines beyond those +* which are applicable to all Regions. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 17-DEC-2004 (DSB): +* Original version. +* 11-MAY-2005 (DSB): +* Overlap modified to allow testing of overlap between prisms and +* intervals. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 9-OCT-2007 (DSB): +* Guard against all axes being extrusion axes in EquivPrism. +* 20-JAN-2009 (DSB): +* Over-ride astRegBasePick. +* 22-JAN-2009 (DSB): +* - Allow any class of Region to be used to define the extrusion axes. +* - Over-ride the astMapList method. +* 19-MAR-2009 (DSB): +* Over-ride the astDecompose method. +* 14-AUG-2014 (DSB): +* Over-ride the astGetRegionBounds method. +* 9-SEP-2014 (DSB): +* Record the pointer to the Prism implementation of RegBaseMesh +* within the class virtual function table. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Prism + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "region.h" /* Regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "prism.h" /* Interface definition for this class */ +#include "cmpmap.h" /* Compound Mappings */ +#include "cmpframe.h" /* Compound Frames */ +#include "unitmap.h" /* Unit Mappings */ +#include "interval.h" /* Axis intervals */ +#include "pointlist.h" /* Points within Frames */ +#include "permmap.h" /* Axis permutations */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *(* parent_getdefunc)( AstRegion *, int * ); +static double (*parent_getfillfactor)( AstRegion *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_maplist)( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int (* parent_overlapx)( AstRegion *, AstRegion *, int * ); +static void (* parent_clearclosed)( AstRegion *, int * ); +static void (* parent_clearmeshsize)( AstRegion *, int * ); +static void (* parent_setclosed)( AstRegion *, int, int * ); +static void (* parent_setmeshsize)( AstRegion *, int, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static void (*parent_getregionbounds)( AstRegion *, double *, double *, int * ); +static void (*parent_regclearattrib)( AstRegion *, const char *, char **, int * ); +static void (*parent_regsetattrib)( AstRegion *, const char *, char **, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Prism) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Prism,Class_Init) +#define class_vtab astGLOBAL(Prism,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstPrismVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstPrism *astPrismId_( void *, void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *GetDefUnc( AstRegion *, int * ); +static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * ); +static double *RegCentre( AstRegion *this, double *, double **, int, int, int * ); +static double GetFillFactor( AstRegion *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetBounded( AstRegion *, int * ); +static int GetObjSize( AstObject *, int * ); +static int MapList( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int Overlap( AstRegion *, AstRegion *, int * ); +static int OverlapX( AstRegion *, AstRegion *, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static void ClearClosed( AstRegion *, int * ); +static void ClearMeshSize( AstRegion *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GetRegions( AstPrism *, AstRegion **, AstRegion **, int *, int * ); +static void GetRegionBounds( AstRegion *, double *, double *, int * ); +static void RegBaseBox( AstRegion *, double *, double *, int * ); +static void RegClearAttrib( AstRegion *, const char *, char **, int * ); +static void RegSetAttrib( AstRegion *, const char *, char **, int * ); +static void SetClosed( AstRegion *, int, int * ); +static void SetMeshSize( AstRegion *, int, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Member functions. */ +/* ================= */ +AstRegion *astConvertToPrism_( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astConvertToPrism + +* Purpose: +* Convert a supplied Region into a Prism if possible. + +* Type: +* Protected function. + +* Synopsis: +* #include "prism.h" +* AstRegion *astConvertToPrism( AstRegion *this, int *status ) + +* Description: +* This function attempts to split the supplied Region into two +* regions defined within separate coordinate system. If this is +* possible, and if either one of the two resulting Regions can be +* simplified, then the two simplified Regions are joined into a Prism +* equivalent to the supplied Region. The Prism is then simplified and +* returned. +* +* If the supplied Region cannot be split into two components, or if +* neither of the two components can eb simplified, then a clone of the +* supplied Region pointer is returned. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the equivalent simplified Prism, or a clone of the +* supplied Region pointer. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*- +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame in supplied Region */ + AstFrame *pickfrm1; /* Frame formed by picking current subset of axes */ + AstFrame *pickfrm2; /* Frame formed by picking all other axes */ + AstMapping *junk; /* Unused Mapping pointer */ + AstMapping *map; /* Base -> current Mapping */ + AstPrism *prism; /* Prism combining all axes */ + AstPrism *newprism; /* Prism combining all axes, in original Frame */ + AstRegion *result; /* Result pointer to return */ + AstRegion *sp1; /* Simplified region spanning selected axes */ + AstRegion *sp2; /* Simplified region spanning unselected axes */ + AstUnitMap *um; /* A UnitMap */ + int *ax; /* Pointer to array of selecte axis indices */ + int *perm; /* Axis permutation array */ + int axis; /* Axis index */ + int bitmask; /* Integer with set bits for selected axes */ + int i; /* Loop index */ + int mask; /* Integer with a set bit at current axis */ + int nax; /* Number of selected axes */ + int nin; /* No. of base Frame axes (Mapping inputs) */ + int nout; /* No. of current Frame axes (Mapping outputs) */ + int topmask; /* Integer that selects all axes */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the Mapping from base to current Frame. */ + map = astGetMapping( this->frameset, AST__BASE, AST__CURRENT ); + +/* Get the number of inputs and outputs for the Mapping. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* Allocate memory to hold the indices of the current Frame axes in the + current subset */ + ax = astMalloc( sizeof( int )* nout ); + if( ax ) { + +/* We need to scan through all possible subsets of the current Frame + axes, looking for a subset that results in the Region being split. + We use the binary pattern of bits in "bitmask" to indicate if the + corresponding axes should be included in the subset of axes. + Loop round all possible combinations, until a combination is found + that results in a Prism being formed. */ + topmask = pow( 2, nout ); + for( bitmask = 1; bitmask < topmask && !result; bitmask++ ) { + +/* Store the indices of the axes forming the current subset. */ + nax = 0; + mask = 1; + for( axis = 0; axis < nout; axis++ ) { + if( bitmask & mask ) ax[ nax++ ] = axis; + mask <<= 1; + } + +/* See if the current subset of current Frame axes can be split off from + the Region. If it can, the Frame pointer returned by astPickAxes will identify + a Region. */ + pickfrm1 = astPickAxes( this, nax, ax, &junk ); + junk = astAnnul( junk ); + if( astIsARegion( pickfrm1 ) ) { + +/* Check that the remaining (unselected) axes can also be picked into a + new Region. */ + nax = 0; + mask = 1; + for( axis = 0; axis < nout; axis++ ) { + if( ( bitmask & mask ) == 0 ) ax[ nax++ ] = axis; + mask <<= 1; + } + + pickfrm2 = astPickAxes( this, nax, ax, &junk ); + junk = astAnnul( junk ); + if( astIsARegion( pickfrm2 ) ) { + +/* See if either of these picked Regions can be simplified. */ + sp1 = astSimplify( pickfrm1 ); + sp2 = astSimplify( pickfrm2 ); + if( (AstFrame *) sp1 != pickfrm1 || + (AstFrame *) sp2 != pickfrm2 ) { + +/* If so form a Prism containing the simplified Regions. */ + prism = astPrism( sp1, sp2, " ", status ); + +/* Permute the axes of the Prism so that they are in the same order as + in the Box. */ + perm = astMalloc( sizeof( int )*nout ); + if( perm ) { + + for( i = 0; i < nout; i++ ) perm[ i ] = -1; + + for( i = 0; i < nax; i++ ) { + perm[ ax[ i ] ] = i + ( nout - nax ); + } + + nax = 0; + for( i = 0; i < nout; i++ ) { + if( perm[ i ] == -1 ) perm[ i ] = nax++; + } + + astPermAxes( prism, perm ); + perm = astFree( perm ); + } + +/* Put the original current Frame back in (in place of the CmpFrame + containined in the Prism). */ + frm = astGetFrame( this->frameset, AST__CURRENT ); + um = astUnitMap( nout, " ", status ); + newprism = astMapRegion( prism, um, frm ); + um = astAnnul( um ); + frm = astAnnul( frm ); + +/* Attempt to simplify the Prism. */ + result = astSimplify( newprism ); + +/* Free resources */ + prism = astAnnul( prism ); + newprism = astAnnul( newprism ); + } + + sp1 = astAnnul( sp1 ); + sp2 = astAnnul( sp2 ); + } + pickfrm2 = astAnnul( pickfrm2 ); + } + + pickfrm1 = astAnnul( pickfrm1 ); + } + + ax = astFree( ax ); + } + + map = astAnnul( map ); + +/* If no Prism could be made, return a clone of the supplied Region + pointer. */ + if( !result ) result = astClone( this ); + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static void Decompose( AstMapping *this_mapping, AstMapping **map1, + AstMapping **map2, int *series, int *invert1, + int *invert2, int *status ) { +/* +* +* Name: +* Decompose + +* Purpose: +* Decompose a Prism into two component Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void Decompose( AstMapping *this, AstMapping **map1, +* AstMapping **map2, int *series, +* int *invert1, int *invert2, int *status ) + +* Class Membership: +* Prism member function (over-rides the protected astDecompose +* method inherited from the Mapping class). + +* Description: +* This function returns pointers to two Mappings which, when applied +* either in series or parallel, are equivalent to the supplied Mapping. +* +* Since the Frame class inherits from the Mapping class, Frames can +* be considered as special types of Mappings and so this method can +* be used to decompose either CmpMaps, CmpFrames, CmpRegions or Prisms. + +* Parameters: +* this +* Pointer to the Mapping. +* map1 +* Address of a location to receive a pointer to first component +* Mapping. +* map2 +* Address of a location to receive a pointer to second component +* Mapping. +* series +* Address of a location to receive a value indicating if the +* component Mappings are applied in series or parallel. A non-zero +* value means that the supplied Mapping is equivalent to applying map1 +* followed by map2 in series. A zero value means that the supplied +* Mapping is equivalent to applying map1 to the lower numbered axes +* and map2 to the higher numbered axes, in parallel. +* invert1 +* The value of the Invert attribute to be used with map1. +* invert2 +* The value of the Invert attribute to be used with map2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component rames using the returned +* pointers will be reflected in the supplied CmpFrame. + +*- +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstPrism *) this_mapping; + +/* The components Frames of a Prism are considered to be parallel + Mappings. */ + if( series ) *series = 0; + +/* The Frames are returned in their original order whether or not the + Prism has been inverted. */ + if( map1 ) *map1 = astClone( this->region1 ); + if( map2 ) *map2 = astClone( this->region2 ); + +/* The invert flags dont mean anything for a Region, but we return them + anyway. If the Prism has been inverted, return inverted Invert flags. */ + if( astGetInvert( this ) ) { + if( invert1 ) *invert1 = astGetInvert( this->region1 ) ? 0 : 1; + if( invert2 ) *invert2 = astGetInvert( this->region2 ) ? 0 : 1; + +/* If the Prism has not been inverted, return the current Invert flags. */ + } else { + if( invert1 ) *invert1 = astGetInvert( this->region1 ); + if( invert2 ) *invert2 = astGetInvert( this->region2 ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Objects are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* int Equal( AstObject *this_object, AstObject *that_object, int *status ) + +* Class Membership: +* Prism member function (over-rides the astEqual protected +* method inherited from the Region class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two Prisms are equivalent. + +* Parameters: +* this +* Pointer to the first Prism. +* that +* Pointer to the second Prism. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the Prisms are equivalent, zero otherwise. + +* Notes: +* - The Prisms are equivalent if their component Regions are +* equivalent and if they have the same boolean operation, negation +* and closed flags. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPrism *that; + AstPrism *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent Region class. This checks + that the Objects are both of the same class, and have the same Negated + and Closed flags (amongst other things). */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Obtain pointers to the two Prism structures. */ + this = (AstPrism *) this_object; + that = (AstPrism *) that_object; + +/* Test their first component Regions for equality. */ + if( astEqual( this->region1, that->region1 ) ) { + +/* Test their second component Regions for equality. */ + if( astEqual( this->region2, that->region2 ) ) result = 1; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Define a function to set an attribute value for a Prism. + +* Type: +* Private macro. + +* Synopsis: +* #include "prism.h" +* MAKE_SET(attribute,lattribute,type) + +* Class Membership: +* Defined by the Prism class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstRegion *this, value ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the component Regions. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,lattribute,type) \ +static void Set##attribute( AstRegion *this_region, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstPrism *this; /* Pointer to the Prism structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to set the value in the parent Region structure. */ \ + (*parent_set##lattribute)( this_region, value, status ); \ +\ +/* Also set the value in the two component Regions. */ \ + this = (AstPrism *) this_region; \ + astSet##attribute( this->region1, value ); \ + astSet##attribute( this->region2, value ); \ +} + +/* Use the above macro to create accessors for the MeshSize and Closed + attributes. */ +MAKE_SET(MeshSize,meshsize,int) +MAKE_SET(Closed,closed,int) + +/* Undefine the macro. */ +#undef MAKE_SET + +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Define a function to clear an attribute value for a Prism. + +* Type: +* Private macro. + +* Synopsis: +* #include "prism.h" +* MAKE_CLEAR(attribute,lattribute) + +* Class Membership: +* Defined by the Prism class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear( AstRegion *this ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the component Regions. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute,lattribute) \ +static void Clear##attribute( AstRegion *this_region, int *status ) { \ +\ +/* Local Variables: */ \ + AstPrism *this; /* Pointer to the Prism structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to clear the value in the parent Region structure. */ \ + (*parent_clear##lattribute)( this_region, status ); \ +\ +/* Also clear the value in the two component Regions. */ \ + this = (AstPrism *) this_region; \ + astClear##attribute( this->region1 ); \ + astClear##attribute( this->region2 ); \ +} + +/* Use the above macro to create accessors for the MeshSize and Closed + attributes. */ +MAKE_CLEAR(MeshSize,meshsize) +MAKE_CLEAR(Closed,closed) + +/* Undefine the macro. */ +#undef MAKE_CLEAR + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Prism member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Prism, +* in bytes. + +* Parameters: +* this +* Pointer to the Prism. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the Prism structure. */ + this = (AstPrism *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astGetObjSize( this->region1 ); + result += astGetObjSize( this->region2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetBounded( AstRegion *this_region, int *status ) { +/* +* Name: +* GetBounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* int GetBounded( AstRegion *this, int *status ) + +* Class Membership: +* Prism method (over-rides the astGetBounded method inherited from +* the Region class). + +* Description: +* This function returns a flag indicating if the Region is bounded. +* The implementation provided by the base Region class is suitable +* for Region sub-classes representing the inside of a single closed +* curve (e.g. Circle, Ellipse, Box, etc). Other sub-classes (such as +* Prism, PointList, etc ) may need to provide their own +* implementations. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Region is bounded. Zero otherwise. + +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + int neg; /* Negated flag to use with the Prism */ + int reg1b; /* Is the first component Region bounded?*/ + int reg2b; /* Is the second component Region bounded?*/ + int result; /* Returned result */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* Get the component Regions, and the Negated value for the Prism. The + returned Regions represent a region within the base Frame of the FrameSet + encapsulated by the parent Region structure. */ + GetRegions( this, ®1, ®2, &neg, status ); + +/* If the Prism has been inverted, temporarily invert the components. */ + if( neg ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* See if either of the component Regions is bounded. */ + reg1b = astGetBounded( reg1 ); + reg2b = astGetBounded( reg2 ); + +/* If the Prism has been inverted, re-invert the components to bring them + back to their original states. */ + if( neg ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* The Prism is bounded only if both of the component Regions are bounded. */ + result = ( reg1b && reg2b ); + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + +/* Return zero if an error occurred. */ + if( !astOK ) result = 0; + +/* Return the required pointer. */ + return result; +} + +static double GetFillFactor( AstRegion *this_region, int *status ) { +/* +* Name: +* GetFillFactor + +* Purpose: +* Obtain the value of the FillFactor attribute for a Prism. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* double GetFillFactor( AstRegion *this, int *status ) + +* Class Membership: +* Prism member function (over-rides the astGetFillFactor method inherited +* from the Region class). + +* Description: +* This function returns the value of the FillFactor attribute for a +* Prism. A suitable default value is returned if no value has +* previously been set. + +* Parameters: +* this +* Pointer to the Prism. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The FillFactor value to use. + +*/ + +/* Local Variables: */ + AstPrism *this; + double f1; + double f2; + double result; + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Initialise. */ + result = AST__BAD; + +/* Obtain a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* See if a FillFactor value has been set. If so, use the parent + astGetFillFactor method to obtain it. */ + if ( astTestFillFactor( this ) ) { + result = (*parent_getfillfactor)( this_region, status ); + +/* Otherwise, we will generate a default value equal to the product of + the FillFactor values of the two component Regions. */ + } else { + f1 = astGetFillFactor( this->region1 ); + f2 = astGetFillFactor( this->region2 ); + if( f1 != AST__BAD && f2 != AST__BAD ) result = f1*f2; + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static void GetRegionBounds( AstRegion *this_region, double *lbnd, + double *ubnd, int *status ){ +/* +* +* Name: +* GetRegionBounds + +* Purpose: +* Returns the bounding box of Prism. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void GetRegionBounds( AstRegion *this_region, double *lbnd, +* double *ubnd, int *status ) + +* Class Membership: +* Prism member function (over-rides the protected astGetRegionBounds +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower limits of a box which just +* encompasses the supplied Prism. The limits are returned as axis +* values within the Frame represented by the Prism. The value of the +* Negated attribute is ignored (i.e. it is assumed that the Prism has +* not been negated). + +* Parameters: +* this +* Pointer to the Prism. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Prism. It should have at least as many elements as +* there are axes in the Prism. If an axis has no lower limit, the +* returned value will be the largest possible negative value. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Prism. It should have at least as many elements as +* there are axes in the Prism. If an axis has no upper limit, the +* returned value will be the largest possible positive value. + +* Notes: +* - The value of the Negated attribute is ignored (i.e. it is assumed that +* the Prism has not been negated). +* - If an axis has no extent on an axis then the lower limit will be +* returned larger than the upper limit. Note, this is different to an +* axis which has a constant value (in which case both lower and upper +* limit will be returned set to the constant value). +* - If the bounds on an axis cannot be determined, AST__BAD is returned for +* both upper and lower bounds +* - The implementation of this method for Prisms attempts to split the Prism +* into two separate Regions spanning indepenent sets of axes, and then uses +* the astGetRegionBouinds method to get the bounds on these two Regions. Care +* has to be taken because the Prism may have been remapped into a different +* Frame since it was created. + +*- +*/ + +/* Local Variables: */ + AstFrame *cfrm1; /* Frame spanning current axes for 1st component Region */ + AstFrame *cfrm2; /* Frame spanning current axes for 2nd component Region */ + AstFrame *cfrm; /* Current Frame for total Prism */ + AstMapping *map1; /* Base->Current Mapping for axes of 1st component Region */ + AstMapping *map2; /* Base->Current Mapping for axes of 2nd component Region */ + AstMapping *map; /* Case->Current mapping for total Prism */ + AstPrism *this; /* Pointer to Prism structure */ + AstRegion *reg1; /* 1st component Region Mapping into Prism's Frame */ + AstRegion *reg2; /* 2nd component Region Mapping into Prism's Frame */ + int *baxes; /* Indicies of Base Frame axes to be picked */ + int *caxes; /* Indicies of Current Frame axes to be picked */ + int iax; /* Axis index */ + int nax1; /* Number of axes in first component Region */ + int nax1_out; /* Number of current Frame axes in first component Region */ + int nax2; /* Number of axes in second component Region */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* Initialise */ + cfrm1 = NULL; + cfrm2 = NULL; + map1 = NULL; + map2 = NULL; + nax1_out = 0; + +/* The number of base-frame axes spanned by the two component Regions. */ + nax1 = astGetNaxes( this->region1 ); + nax2 = astGetNaxes( this->region2 ); + +/* Work space to hold the base frame indices for a single component + FrameSet. */ + baxes = astMalloc( ( nax1 + nax2 )*sizeof( int ) ); + if( astOK ) { + +/* Get the Mapping from Base to Current Frame from the Prism's FrameSet, + and get a pointer to the current Frame. */ + map = astGetMapping( this_region->frameset, AST__BASE, AST__CURRENT ); + cfrm = astGetFrame( this_region->frameset, AST__CURRENT ); + +/* Attempt to split the FrameSet encapsulated within the Prism into two - + one containing the axes spanned by the first component Region and + another containing the axes spanned by the second component Region. + First store the zero-based indices of the base Frame axes + corresponding to the first component Region. */ + for( iax = 0; iax < nax1; iax++ ) baxes[ iax ] = iax; + +/* Attempt to split these inputs from the total Mapping, thus creating a + Mapping that converts just the axes spanned by the first component + Region. */ + caxes = astMapSplit( map, nax1, baxes, &map1 ); + +/* If the Mapping could be split, get a Frame spanning the current Frame + axes corresponding to the first component Region. */ + if( caxes ) { + nax1_out = astGetNout( map1 ); + cfrm1 = astPickAxes( cfrm, nax1_out, caxes, NULL ); + caxes = astFree( caxes ); + } + +/* Do the same thing for the second component Region. */ + for( iax = 0; iax < nax2; iax++ ) baxes[ iax ] = iax + nax1; + caxes = astMapSplit( map, nax2, baxes, &map2 ); + if( caxes ) { + cfrm2 = astPickAxes( cfrm, astGetNout( map2 ), caxes, NULL ); + caxes = astFree( caxes ); + } + +/* Free resources. */ + cfrm = astAnnul( cfrm ); + map = astAnnul( map ); + } + baxes = astFree( baxes ); + +/* If the Prism mapping could not be split, use the parent GetRegionBounds + method. */ + if( !map1 || !map2 ) { + (*parent_getregionbounds)( this_region, lbnd, ubnd, status ); + +/* Otherwise, we get the bounds of the two component Regions separately. */ + } else { + +/* Remap the first component Region using the FrameSet created above. */ + reg1 = astMapRegion( this->region1, map1, cfrm1 ); + +/* Get the bounds of this Region, storing the results at the start of the + returned lbnd/ubnd arrays. */ + astGetRegionBounds( reg1, lbnd, ubnd ); + reg1 = astAnnul( reg1 ); + +/* Do the same thing for the second component Region, storing the results + at the end of the returned lbnd/ubnd arrays. */ + reg2 = astMapRegion( this->region2, map2, cfrm2 ); + astGetRegionBounds( reg2, lbnd + nax1_out, ubnd + nax1_out ); + reg2 = astAnnul( reg2 ); + +/* What about the possibility that the axes of the Prism have been + permuted? */ + + } + +/* Free resources. */ + if( map1 ) map1 = astAnnul( map1 ); + if( map2 ) map2 = astAnnul( map2 ); + if( cfrm1 ) cfrm1 = astAnnul( cfrm1 ); + if( cfrm2 ) cfrm2 = astAnnul( cfrm2 ); + +} + +static void GetRegions( AstPrism *this, AstRegion **reg1, AstRegion **reg2, + int *neg, int *status ) { +/* +* +* Name: +* GetRegions + +* Purpose: +* Get the component Regions of a Prism. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void GetRegions( AstPrism *this, AstRegion **reg1, AstRegion **reg2, +* int *neg, int *status ) + +* Class Membership: +* Prism member function + +* Description: +* This function returns pointers to the two Regions in a Prism, together +* with the Negated flag for the Prism. +* +* The current Frames in both the returned component Regions will be +* equivalent to componets of the base Frame in the FrameSet encapsulated +* by the parent Region structure. + +* Parameters: +* this +* Pointer to the Prism. +* reg1 +* Address of a location to receive a pointer to first component +* Region. This is the region which is to be extruded. +* reg2 +* Address of a location to receive a pointer to second component +* Region. This will be an Interval defining the axes along which +* the first Region is to be extruded. +* neg +* Pointer to an int in which to return the value of the Negated +* attribute of the Prism. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The returned pointers should be annuled using astAnnul when no +* longer needed. +* - The Frames represented by the returned Regions (that is, the +* current Frames in their encapsulated FrameSets) are equivalent to the +* base Frame in the FrameSet encapsulated within the parent Region. +* - Any changes made to the component Regions using the returned +* pointers will be reflected in the supplied Prism. + +*- +*/ + +/* Initialise */ + if( reg1 ) *reg1 = NULL; + if( reg2 ) *reg2 = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the required values.*/ + *reg1 = astClone( this->region1 ); + *reg2 = astClone( this->region2 ); + *neg = astGetNegated( (AstRegion *) this ); +} + +static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) { +/* +* Name: +* GetDefUnc + +* Purpose: +* Obtain a pointer to the default uncertainty Region for a given Region. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* AstRegion *GetDefUnc( AstRegion *this ) + +* Class Membership: +* Prism method (over-rides the astGetDefUnc method inherited from +* the Region class). + +* This function returns a pointer to a Region which represents the +* default uncertainty associated with a position on the boundary of the +* given Region. The returned Region refers to the base Frame within the +* FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + AstRegion *bunc1; /* Uncertainty Region for 1st component */ + AstRegion *bunc2; /* Uncertainty Region for 2nd component */ + AstRegion *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* Construct a default uncertainty Region from the uncertainty Regions + in the two component Regions. The current Frames in the component + Regions are equivalent to the base Frame in the parent Region structure. + So we may need to map the component uncertainty into the current Region of + the parent if required later on. */ + bunc1 = astGetUncFrm( this->region1, AST__CURRENT ); + bunc2 = astGetUncFrm( this->region2, AST__CURRENT ); + +/* Combine them into a Prism. */ + result = (AstRegion *) astPrism( bunc1, bunc2, "", status ); + +/* Free resources. */ + bunc1 = astAnnul( bunc1 ); + bunc2 = astAnnul( bunc2 ); + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +void astInitPrismVtab_( AstPrismVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitPrismVtab + +* Purpose: +* Initialise a virtual function table for a Prism. + +* Type: +* Protected function. + +* Synopsis: +* #include "prism.h" +* void astInitPrismVtab( AstPrismVtab *vtab, const char *name ) + +* Class Membership: +* Prism vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Prism class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAPrism) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* None. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + frame = (AstFrameVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_maplist = mapping->MapList; + mapping->MapList = MapList; + + parent_getdefunc = region->GetDefUnc; + region->GetDefUnc = GetDefUnc; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_clearclosed = region->ClearClosed; + region->ClearClosed = ClearClosed; + + parent_clearmeshsize = region->ClearMeshSize; + region->ClearMeshSize = ClearMeshSize; + + parent_setclosed = region->SetClosed; + region->SetClosed = SetClosed; + + parent_setmeshsize = region->SetMeshSize; + region->SetMeshSize = SetMeshSize; + + parent_getfillfactor = region->GetFillFactor; + region->GetFillFactor = GetFillFactor; + + parent_overlapx = region->OverlapX; + region->OverlapX = OverlapX; + + parent_regsetattrib = region->RegSetAttrib; + region->RegSetAttrib = RegSetAttrib; + + parent_regclearattrib = region->RegClearAttrib; + region->RegClearAttrib = RegClearAttrib; + + parent_getregionbounds = region->GetRegionBounds; + region->GetRegionBounds = GetRegionBounds; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping->Decompose = Decompose; + region->RegBaseBox = RegBaseBox; + region->RegBaseMesh = RegBaseMesh; + region->RegPins = RegPins; + region->GetBounded = GetBounded; + region->RegCentre = RegCentre; + region->Overlap = Overlap; + region->RegBasePick = RegBasePick; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "Prism", "Region extrusion into higher dimensions" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* CmpMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the Prism structure. */ + this = (AstPrism *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->region1, mode, extra, fail ); + if( !result ) result = astManageLock( this->region2, mode, extra, fail ); + + return result; + +} +#endif + +static int MapList( AstMapping *this_mapping, int series, int invert, + int *nmap, AstMapping ***map_list, int **invert_list, + int *status ) { +/* +* Name: +* MapList + +* Purpose: +* Decompose a Prism into a sequence of simpler Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapList( AstMapping *this, int series, int invert, int *nmap, +* AstMapping ***map_list, int **invert_list ) + +* Class Membership: +* Prism member function (over-rides the protected astMapList +* method inherited from the Region class). + +* Description: +* This function decomposes a Prism into a sequence of simpler +* Regions which may be applied in parallel to achieve the same +* effect. The Prism is decomposed as far as possible, but it is +* not guaranteed that this will necessarily yield any more than +* one Region, which may actually be the original Prism supplied. + +* Parameters: +* this +* Pointer to the Prism to be decomposed (the Prism is not +* actually modified by this function). +* series +* Prisms always apply their component Regions in parallel, so this +* value should always be zero. This function will return without +* action if it is non-zero. +* invert +* Inverting a Region has no effect on the properties of the Region +* and so this parameter is ignored. +* nmap +* The address of an int which holds a count of the number of +* individual Regions in the decomposition. On entry, this +* should count the number of Regions already in the +* "*map_list" array (below). On exit, it is updated to include +* any new Regions appended by this function. +* map_list +* Address of a pointer to an array of Region pointers. On +* entry, this array pointer should either be NULL (if no +* Regions have yet been obtained) or should point at a +* dynamically allocated array containing Region pointers +* ("*nmap" in number) which have been obtained from a previous +* invocation of this function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Region pointers that result from the decomposition +* requested. These pointers will be appended to any previously +* present, and the array pointer will be updated as necessary +* to refer to the enlarged array (any space released by the +* original array will be freed automatically). +* +* The new Region pointers returned will identify a sequence of +* Regions which, when applied (in parallel) in order, will be +* equivalent to the original Prism. +* +* All the Region pointers returned by this function should be +* annulled by the caller, using astAnnul, when no longer +* required. The dynamic array holding these pointers should +* also be freed, using astFree. +* invert_list +* Address of a pointer to an array of int. On entry, this array +* pointer should either be NULL (if no Regions have yet been +* obtained) or should point at a dynamically allocated array +* containing Invert attribute values ("*nmap" in number) which +* have been obtained from a previous invocation of this +* function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Invert attribute values that result from the +* decomposition requested. These values will be appended to any +* previously present, and the array pointer will be updated as +* necessary to refer to the enlarged array (any space released +* by the original array will be freed automatically). +* +* The new Invert values returned will always be zero since a +* Region is unaffected by its Invert setting. +* +* The dynamic array holding these values should be freed by the +* caller, using astFree, when no longer required. + +* Returned Value: +* Zero is always returned. + +* Notes: +* - It is unspecified to what extent the original Prism and the +* individual (decomposed) Regions are +* inter-dependent. Consequently, the individual Regions cannot be +* modified without risking modification of the original Prism. +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then the *nmap value, the +* list of Region pointers and the list of Invert values will all +* be returned unchanged. +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the Prism structure. */ + this = (AstPrism *) this_mapping; + +/* When considered as a CmpMap, a Prism always combines its component + Mappings (Regions) in parallel. So check that a parallel decomposition + was requested. */ + if ( ! series ) { + +/* Concatenate the Mapping lists obtained from each component Region. */ + (void) astMapList( (AstMapping *) this->region1, 0, 0, nmap, map_list, + invert_list ); + (void) astMapList( (AstMapping *) this->region2, 0, 0, nmap, map_list, + invert_list ); + +/* If the Prism does not combine its components in the way required + by the decomposition (series or parallel), then we cannot decompose + it. In this case it must be appended to the Mapping list as a + single entity. We can use the parent class method to do this. */ + } else { + (void) ( *parent_maplist )( this_mapping, series, invert, nmap, + map_list, invert_list, status ); + } + + return 0; +} + +static int Overlap( AstRegion *this, AstRegion *that, int *status ){ +/* +* Name: +* Overlap + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* int Overlap( AstRegion *this, AstRegion *that, int *status ) + +* Class Membership: +* Prism member function (over-rides the astOverlap method inherited +* from the Region class). + +* Description: +* This function returns an integer value indicating if the two +* supplied Regions overlap. The two Regions are converted to a commnon +* coordinate system before performing the check. If this conversion is +* not possible (for instance because the two Regions represent areas in +* different domains), then the check cannot be performed and a zero value +* is returned to indicate this. + +* Parameters: +* this +* Pointer to the first Region. +* that +* Pointer to the second Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* astOverlap() +* A value indicating if there is any overlap between the two Regions. +* Possible values are: +* +* 0 - The check could not be performed because the second Region +* could not be mapped into the coordinate system of the first +* Region. +* +* 1 - There is no overlap between the two Regions. +* +* 2 - The first Region is completely inside the second Region. +* +* 3 - The second Region is completely inside the first Region. +* +* 4 - There is partial overlap between the two Regions. +* +* 5 - The Regions are identical. +* +* 6 - The second Region is the negation of the first Region. + +* Notes: +* - The returned values 5 and 6 do not check the value of the Closed +* attribute in the two Regions. +* - A value of zero will be returned if this function is invoked with the +* AST error status set, or if it should fail for any reason. + +*/ + +/* Local Variables: */ + AstFrame *bfrm; + AstFrame *efrm; + AstFrameSet *fs; + AstMapping *bmap; + AstMapping *emap; + AstMapping *map1; + AstMapping *map2; + AstMapping *map; + AstMapping *smap; + AstRegion *that_base2; + AstRegion *that_base; + AstRegion *that_ext2; + AstRegion *that_ext; + AstRegion *this_reg1; + AstRegion *this_reg2; + int *inax; + int *outax; + int i; + int nbase; + int next; + int rbase; + int result; + int rext; + int that_neg; + int this_neg; + +/* A table indicating how to combine together the overlap state of the + extrusion Regions with the overlap state of the other (base) Region. + The first index represents the value returned by the astOverlap method + when used to determine the overlap of the base Regions in the two + supplied Prisms. The second index represents the value returned by the + astOverlap method when used to determine the overlap of the extrusion + Regions in the two supplied Prisms. The integer values stored in the + array represent the astOverlap value describing the overlap of the two + Prisms. */ + static int rtable[ 7 ][ 7 ] = { { 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 1, 1, 1 }, + { 0, 1, 2, 4, 4, 2, 1 }, + { 0, 1, 4, 3, 4, 3, 1 }, + { 0, 1, 4, 4, 4, 4, 1 }, + { 0, 1, 2, 3, 4, 5, 1 }, + { 0, 1, 1, 1, 1, 1, 6 } }; + +/* Initialise */ + result = 0; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Get pointers to the base and extrusion Regions within "this", and also + the nagated flag. */ + GetRegions( (AstPrism *) this, &this_reg1, &this_reg2, &this_neg, status ); + +/* Get the number of axes in the base and extrusion Regions of "this". */ + nbase = astGetNaxes( this_reg1 ); + next = astGetNaxes( this_reg2 ); + +/* Get a FrameSet which goes from the Frame represented by "this" to the + Frame represented by "that". Check that the conection is defined. */ + fs = astConvert( this, that, "" ); + if( fs ) { + +/* Get the forward Mapping from the above FrameSet. */ + map2 = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Get a pointer to the Mapping from base to current Frame in "this". */ + map1 = astGetMapping( this->frameset, AST__BASE, AST__CURRENT ); + +/* Combine these Mappings to get the Mapping from the base Frame of "this" + to the current Frame of "that". */ + map = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + +/* Simplify this Mapping. */ + smap = astSimplify( map ); + +/* See if the extrusion axes in "this" correspond to a unique set of axes + in the current Frame of "that". */ + inax = astMalloc( sizeof(int)*(size_t)next ); + for( i = 0; i < next; i++ ) inax[ i ] = nbase + i; + outax = astMapSplit( smap, next, inax, &emap ); + +/* If they do, attempt to create a Region by picking the axes from "that" + that correspond to the extrusion axes in "this". */ + if( emap && astGetNout( emap ) == next ) { + that_ext = (AstRegion *) astPickAxes( that, next, outax, NULL ); + +/* If the picked axes formed a Region, see if the base axes can also be + picked in the same way. */ + if( astIsARegion( that_ext ) ) { + outax = astFree( outax ); + inax = astGrow( inax, (size_t)nbase, sizeof( int ) ); + for( i = 0; i < nbase; i++ ) inax[ i ] = i; + outax = astMapSplit( smap, nbase, inax, &bmap ); + if( bmap && astGetNout( bmap ) == nbase ) { + that_base = (AstRegion *) astPickAxes( that, nbase, outax, NULL ); + if( astIsARegion( that_base ) ) { + +/* Map the two Regions picked from "that" into the Frames of the two + sub-regions within "this". */ + astInvert( emap ); + efrm = astGetFrame( this_reg2->frameset, AST__CURRENT ); + that_ext2 = astMapRegion( that_ext, emap, efrm ); + + astInvert( bmap ); + bfrm = astGetFrame( this_reg1->frameset, AST__CURRENT ); + that_base2 = astMapRegion( that_base, bmap, bfrm ); + +/* We can test separately for overlap of the two extrusion Regions, and for + overlap of the two base Regions, and then combine the returned flags to + represent overlap of the whole Prism. */ + rbase = astOverlap( this_reg1, that_base2 ); + rext = astOverlap( this_reg2, that_ext2 ); + result = rtable[ rbase ][ rext ]; + +/* The values in the rtable array assume that neither of the supplied + Prisms have been negated. Modify the value obtained from rtable to + take account of negation of either or both of the supplied Prisms. */ + that_neg = astGetNegated( that ); + if( this_neg ) { + if( that_neg ) { + if( result == 1 ) { + result = 4; + } else if( result == 2 ) { + result = 3; + } else if( result == 3 ) { + result = 2; + } + } else { + if( result == 1 ) { + result = 3; + } else if( result == 2 ) { + result = 4; + } else if( result == 3 ) { + result = 1; + } else if( result == 5 ) { + result = 6; + } else if( result == 6 ) { + result = 5; + } + } + } else if( that_neg ){ + if( result == 1 ) { + result = 2; + } else if( result == 2 ) { + result = 1; + } else if( result == 3 ) { + result = 4; + } else if( result == 5 ) { + result = 6; + } else if( result == 6 ) { + result = 5; + } + } + +/* Free resources */ + efrm = astAnnul( efrm ); + that_ext2 = astAnnul( that_ext2 ); + bfrm = astAnnul( bfrm ); + that_base2 = astAnnul( that_base2 ); + } + that_base = astAnnul( that_base ); + bmap = astAnnul( bmap ); + } + } + emap = astAnnul( emap ); + that_ext = astAnnul( that_ext ); + } + outax = astFree( outax ); + inax = astFree( inax ); + smap = astAnnul( smap ); + map = astAnnul( map ); + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + fs = astAnnul( fs ); + } + this_reg1 = astAnnul( this_reg1 ); + this_reg2 = astAnnul( this_reg2 ); + +/* If overlap could not be determined using the above implementation, try + using the implementation inherited from the parent Region class. Use + OverlapX rather than Overlap since a) it is OverlapX that does the work, + and b) calling Overlap could end us in an infinite loop. */ + if( !result ) result = (*parent_overlapx)( this, that, status ); + +/* If not OK, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int OverlapX( AstRegion *that, AstRegion *this, int *status ){ +/* +* Name: +* OverlapX + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* int OverlapX( AstRegion *that, AstRegion *this ) + +* Class Membership: +* Prism member function (over-rides the astOverlapX method inherited +* from the Region class). + +* Description: +* This function performs the processing for the public astOverlap +* method and has exactly the same interface except that the order +* of the two arguments is swapped. This is a trick to allow +* the astOverlap method to be over-ridden by derived classes on +* the basis of the class of either of its two arguments. +* +* See the astOverlap method for details of the interface. +*/ + +/* Local Variables; */ + int result; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* We know that "that" is a Prism, so call the private Overlap method, + and then modify the returned value to take account of the fact that the + two Regions are swapped. */ + result = Overlap( that, this, status ); + + if( result == 2 ){ + result = 3; + } else if( result == 3 ){ + result = 2; + } + + return result; +} + + +static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, + int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, +* int *status ) + +* Class Membership: +* Prism member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + int nax; /* Number of axes in Frame */ + int neg; /* Negated flag for Prism */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Prism structure */ + this = (AstPrism *) this_region; + +/* Get pointers to the component Regions. */ + GetRegions( this, ®1, ®2, &neg, status ); + +/* The base Frame of the parent Region structure is equivalent to a + CmpFrame containing the current Frames of the component Regions. + Get the no. of axes in the first component Frame. */ + nax = astGetNaxes( reg1 ); + +/* Get the bounding boxes of the component Regions in these Frame, + storing the values in the supplied arrays. */ + astGetRegionBounds( reg1, lbnd, ubnd ); + astGetRegionBounds( reg2, lbnd + nax, ubnd + nax ); + +/* Free resources.*/ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); +} + +static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Return a PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* AstPointSet *RegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* Prism member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. Annul the pointer using astAnnul when it +* is no longer needed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + + +/* Local Variables: */ + AstPointSet *grid1; /* PointSet holding grid for region1 */ + AstPointSet *grid2; /* PointSet holding grid for region2 */ + AstPointSet *mesh1; /* PointSet holding mesh for region1 */ + AstPointSet *mesh2; /* PointSet holding mesh for region2 */ + AstPointSet *result; /* Returned pointer */ + AstPrism *this; /* The Prism structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **pg1; /* Pointer to grid1 arrays */ + double **pg2; /* Pointer to grid2 arrays */ + double **pm1; /* Pointer to mesh1 arrays */ + double **pm2; /* Pointer to mesh2 arrays */ + double **ptr; /* Pointer to returned mesh arrays */ + int gsz1; /* Preferred grid size for region1 */ + int gsz2; /* Preferred grid size for region2 */ + int hasMesh1; /* Does 1st component Region have a mesh? */ + int hasMesh2; /* Does 2nd component Region have a mesh? */ + int i; /* Index of next mesh position */ + int ii; /* Index of next results position */ + int j; /* Index of next grid position */ + int jc; /* Axis index */ + int msz1; /* Preferred mesh size for region1 */ + int msz2; /* Preferred mesh size for region2 */ + int msz; /* Original MeshSize */ + int mszp; /* MeshSize value for Prism */ + int nax1; /* Number of axes in region1 */ + int nax2; /* Number of axes in region2 */ + int nax; /* Number of axes in Prism */ + +/* Initialise */ + result= NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* If the Region structure contains a pointer to a PointSet holding + a previously created mesh, return it. */ + if( this_region->basemesh ) { + result = astClone( this_region->basemesh ); + +/* Otherwise, create a new mesh. */ + } else { + +/* Get pointers to the component regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* A mesh can only be produced for a Region if it is bounded when either + negated or un-negated. See if meshes can be produced for the component + Regions. */ + hasMesh1 = astGetBounded( reg1 ); + if( !hasMesh1 ){ + astNegate( reg1 ); + hasMesh1 = astGetBounded( reg1 ); + astNegate( reg1 ); + } + + hasMesh2 = astGetBounded( reg2 ); + if( !hasMesh2 ){ + astNegate( reg2 ); + hasMesh2 = astGetBounded( reg2 ); + astNegate( reg2 ); + } + +/* If either Region does not have a mesh we cannot produce a mesh for the + Prism. */ + if( !hasMesh1 || !hasMesh2 ) { + if( astOK ) astError( AST__INTER, "astRegBaseMesh(%s): No mesh " + "can be produced for the %s bacause one of its component " + "Regions is unbounded.", status, astGetClass( this ), astGetClass( this ) ); + +/* Otherwise we can create a mesh for the Prism. */ + } else { + +/* Determine the grid sizes and mesh sizes to use for the two components. + This aims to produce a total number of points in the returned Prism + mesh roughly equal to the MeshSize attribute of the Prism. It also + aims to divide the mesh points equally between the end faces of the + prism, and the side walls. We remember that the grid used to represent + any 1-D region always has a size of 2, regardless of the setting of + MeshSize. */ + mszp = astGetMeshSize( this ); + msz1 = ( astGetNaxes( reg1 ) == 1 ) ? 2 : sqrt( 0.5*mszp ); + gsz2 = 0.5*mszp/msz1; + msz2 = ( astGetNaxes( reg2 ) == 1 ) ? 2 : sqrt( 0.5*mszp ); + gsz1 = 0.5*mszp/msz2; + +/* First, get a boundary mesh for the second component Region defining the + prism extrusion. For instance, if the Region is 1-dimensional, this mesh + will consist of the two values on the Region axis: the lower and upper + bounds of the Region. */ + msz = astTestMeshSize( reg2 ) ? astGetMeshSize( reg2 ) : -1; + astSetMeshSize( reg2, msz2 ); + mesh2 = astRegMesh( reg2 ); + +/* Also get a grid of points spread throughout the extent (i.e. not + merely on the boundary) of the second Region. */ + astSetMeshSize( reg2, gsz2 ); + grid2 = astRegGrid( reg2 ); + +/* Re-instate the original MeshSize for the second Region. */ + if( msz == -1 ) { + astClearMeshSize( reg2 ); + } else { + astSetMeshSize( reg2, msz ); + } + +/* Similarly, get a boundary mesh and a volume grid for the first Region. */ + msz = astTestMeshSize( reg1 ) ? astGetMeshSize( reg1 ) : -1; + astSetMeshSize( reg1, msz1 ); + mesh1 = astRegMesh( reg1 ); + + astSetMeshSize( reg1, gsz1 ); + grid1 = astRegGrid( reg1 ); + + if( msz == -1 ) { + astClearMeshSize( reg1 ); + } else { + astSetMeshSize( reg1, msz ); + } + +/* Note the number of axes in the two component Regions. */ + nax1 = astGetNcoord( mesh1 ); + nax2 = astGetNcoord( mesh2 ); + +/* The above mesh and grid sizes are only approximate. Find the values + actually used. */ + msz1 = astGetNpoint( mesh1 ); + gsz1 = astGetNpoint( grid1 ); + msz2 = astGetNpoint( mesh2 ); + gsz2 = astGetNpoint( grid2 ); + +/* Create the returned PointSet. */ + msz = gsz1*msz2 + msz1*gsz2; + nax= astGetNaxes( this ); + result = astPointSet( msz, nax, "", status ); + +/* Get pointers to the data in all 5 PointSets. */ + ptr = astGetPoints( result ); + pm1 = astGetPoints( mesh1 ); + pg1 = astGetPoints( grid1 ); + pm2 = astGetPoints( mesh2 ); + pg2 = astGetPoints( grid2 ); + +/* Check pointers can be de-referenced safely. */ + if( astOK ) { + +/* Initialise the index of the next point to be written to the results + array. */ + ii = 0; + +/* First, replicate the volume grid covering the first region at every + point in the boundary mesh covering the second Region. */ + for( i = 0; i < msz2; i++ ) { + for( j = 0; j < gsz1; j++, ii++ ) { + for( jc = 0; jc < nax1; jc++ ) { + ptr[ jc ][ ii ] = pg1[ jc ][ j ]; + } + for( ; jc < nax; jc++ ) { + ptr[ jc ][ ii ] = pm2[ jc - nax1 ][ i ]; + } + } + } + +/* Now, replicate the volume grid covering the second region at every + point in the boundary mesh covering the first Region. */ + for( i = 0; i < msz1; i++ ) { + for( j = 0; j < gsz2; j++, ii++ ) { + for( jc = 0; jc < nax1; jc++ ) { + ptr[ jc ][ ii ] = pm1[ jc ][ i ]; + } + for( ; jc < nax; jc++ ) { + ptr[ jc ][ ii ] = pg2[ jc -nax1 ][ j ]; + } + } + } + } + +/* Free resources. */ + mesh1 = astAnnul( mesh1 ); + mesh2 = astAnnul( mesh2 ); + grid1 = astAnnul( grid1 ); + grid2 = astAnnul( grid2 ); + } + +/* Save the returned pointer in the Region structure so that it does not + need to be created again next time this function is called. */ + if( astOK && result ) this_region->basemesh = astClone( result ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, + const int *axes, int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* Prism member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *frm1; /* Axes picked from the 1st encapsulated Region */ + AstFrame *frm2; /* Axes picked from the 2nd encapsulated Region */ + AstPrism *this; /* Pointer to Prism structure */ + AstRegion *result; /* Returned Region */ + int *axes1; /* Axes to pick from 1st input encapsulated Region */ + int *axes2; /* Axes to pick from 2nd input encapsulated Region */ + int i; /* Output axis index */ + int j; /* Input axis index */ + int nax1; /* No. of axes in 1st input encapsulated Region */ + int nax2; /* No. of axes in 2nd input encapsulated Region */ + int naxes1; /* No. of axes in 1st output encapsulated Region */ + int naxes2; /* No. of axes in 2nd output encapsulated Region */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Prism information. */ + this = (AstPrism *) this_region; + +/* Get the number of axes in each of the two encapsulated Regions. */ + nax1 = astGetNaxes( this->region1 ); + nax2 = astGetNaxes( this->region2 ); + +/* Allocate memory to hold the indices of the axes selected from each of + the two encapsulated Regions. */ + axes1 = astMalloc( sizeof( *axes1 )*nax1 ); + axes2 = astMalloc( sizeof( *axes2 )*nax2 ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Initialise the counters that count the number of axes selected from + each of the two encapsulated Regions. */ + naxes1 = 0; + naxes2 = 0; + +/* For each of the axes to be selected from the prism... */ + for( i = 0; i < naxes; i++ ) { + j = axes[ i ]; + +/* ... if the current selected axis is part of the first encapsulated + Region, add its index into the array that holds the axes to be selected + from the first encapsulated region. Increment the number of axes + selected from the first encapsulated region. */ + if( j < nax1 ) { + axes1[ naxes1++ ] = j; + +/* If the current selected axis is part of the second encapsulated Region, + add its index into the array that holds the axes to be selected from + the second encapsulated region. Note, the index is converted from a + Prism axis index to a sub-region axis index by subtracting "nax1". + Also increment the number of axes selected from the second encapsulated + region. */ + } else { + axes2[ naxes2++ ] = j - nax1; + } + } + +/* Pick any required axes from the first sub-region. If the result of the + selection is not a Region, we cannot pick the required axes, so annul + the object holding the selected axes. */ + if( naxes1 ) { + frm1 = astPickAxes( this->region1, naxes1, axes1, NULL ); + if( frm1 && !astIsARegion( frm1 ) ) frm1 = astAnnul( frm1 ); + } else { + frm1 = NULL; + } + +/* Pick any required axes from the second sub-region. If the result of the + selection is not a Region, we cannot pick the required axes, so annul + the object holding the selected axes. */ + if( naxes2 ) { + frm2 = astPickAxes( this->region2, naxes2, axes2, NULL ); + if( frm2 && !astIsARegion( frm2 ) ) frm2 = astAnnul( frm2 ); + } else { + frm2 = NULL; + } + +/* If we are selecting axes only from the first sub-region, and the above + selection was succesful, just return a clone of the Region holding the + axes selected from the first sub-region. */ + if( naxes1 > 0 && naxes2 == 0 && frm1 ) { + result = astClone( frm1 ); + +/* If we are selecting axes only from the second sub-region, and the above + selection was succesful, just return a clone of the Region holding the + axes selected from the second sub-region. */ + } else if( naxes1 == 0 && naxes2 > 0 && frm2 ) { + result = astClone( frm2 ); + +/* If we are selecting axes from both sub-regions, and both the above + selections were succesful, joing them together into a Prism. */ + } else if( naxes1 > 0 && naxes2 > 0 && frm1 && frm2 ) { + result = (AstRegion *) astPrism( (AstRegion *) frm1, + (AstRegion *) frm2, + "", status ); + } + +/* Free resources */ + if( frm1 ) frm1 = astAnnul( frm1 ); + if( frm2 ) frm2 = astAnnul( frm2 ); + } + + axes1 = astFree( axes1 ); + axes2 = astFree( axes2 ); + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static double *RegCentre( AstRegion *this_region, double *cen, double **ptr, + int index, int ifrm, int *status ){ +/* +* Name: +* RegCentre + +* Purpose: +* Re-centre a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* double *RegCentre( AstRegion *this, double *cen, double **ptr, +* int index, int ifrm, int *status ) + +* Class Membership: +* Prism member function (over-rides the astRegCentre protected +* method inherited from the Region class). + +* Description: +* This function shifts the centre of the supplied Region to a +* specified position, or returns the current centre of the Region. + +* Parameters: +* this +* Pointer to the Region. +* cen +* Pointer to an array of axis values, giving the new centre. +* Supply a NULL value for this in order to use "ptr" and "index" to +* specify the new centre. +* ptr +* Pointer to an array of points, one for each axis in the Region. +* Each pointer locates an array of axis values. This is the format +* returned by the PointSet method astGetPoints. Only used if "cen" +* is NULL. +* index +* The index of the point within the arrays identified by "ptr" at +* which is stored the coords for the new centre position. Only used +* if "cen" is NULL. +* ifrm +* Should be AST__BASE or AST__CURRENT. Indicates whether the centre +* position is supplied and returned in the base or current Frame of +* the FrameSet encapsulated within "this". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If both "cen" and "ptr" are NULL then a pointer to a newly +* allocated dynamic array is returned which contains the centre +* coords of the Region. This array should be freed using astFree when +* no longer needed. If either of "ptr" or "cen" is not NULL, then a +* NULL pointer is returned. + +* Notes: +* - Some Region sub-classes do not have a centre. Such classes will report +* an AST__INTER error code if this method is called with either "ptr" or +* "cen" not NULL. If "ptr" and "cen" are both NULL, then no error is +* reported if this method is invoked on a Region of an unsuitable class, +* but NULL is always returned. +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double *bc; /* Base Frame centre position */ + double *cen1; /* Centre of first component Region */ + double *cen2; /* Centre of second component Region */ + double *result; /* Returned pointer */ + double *tmp; /* Temporary array pointer */ + double *total; /* Pointer to total base Frame centre array */ + int i; /* Coordinate index */ + int nax1; /* Number of axes in first component Region */ + int nax2; /* Number of axes in second component Region */ + int ncb; /* Number of base frame coordinate values per point */ + int ncc; /* Number of current frame coordinate values per point */ + int neg; /* Prism negated flag */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* Get the component Regions, and the Negated flag for the Prism. */ + GetRegions( this, ®1, ®2, &neg, status ); + +/* Get the number of current Frame axes in each component Region. The sum + of these will equal the number of base Frame axes in the parent Region + structure. */ + nax1 = astGetNaxes( reg1 ); + nax2 = astGetNaxes( reg2 ); + ncb = nax1 + nax2; + +/* If we are getting (not setting) the current centre... */ + if( !ptr && !cen ) { + +/* Get the centre from the two component Regions in their current Frames. */ + cen1 = astRegCentre( reg1, NULL, NULL, 0, AST__CURRENT ); + cen2 = astRegCentre( reg2, NULL, NULL, 0, AST__CURRENT ); + +/* If both component regions are re-centerable, join the two centre + arrays together into a single array. */ + if( cen1 && cen2 ) { + total = astMalloc( sizeof(double)*(size_t)ncb ); + if( total ) { + for( i = 0; i < nax1; i++ ) total[ i ] = cen1[ i ]; + for( i = 0; i < nax2; i++ ) total[ i + nax1 ] = cen2[ i ]; + +/* The current Frames of the component Regions correspond to the base + Frame of the parent Region structure. If the original centre is + required in the current Frame, transform it using the base->current + Mapping from the parent Region structure. */ + if( ifrm == AST__CURRENT ) { + result = astRegTranPoint( this_region, total, 1, 1 ); + total = astFree( total ); + } else { + result = total; + } + } + } + +/* Free the individual centre arrays. */ + cen1 = astFree( cen1 ); + cen2 = astFree( cen2 ); + +/* If we are setting a new centre... */ + } else { + +/* If the new centre is supplied in the current Frame of the parent + Region structure, transform it into the base Frame (i.e. the Frames + represented by the component Regions). */ + if( ifrm == AST__CURRENT ) { + if( cen ) { + bc = astRegTranPoint( this_region, cen, 1, 0 ); + } else { + ncc = astGetNaxes( this_region ); + tmp = astMalloc( sizeof( double)*(size_t)ncc ); + if( astOK ) { + for( i = 0; i < ncc; i++ ) tmp[ i ] = ptr[ i ][ index ]; + } + bc = astRegTranPoint( this_region, tmp, 1, 0 ); + tmp = astFree( tmp ); + } + + } else { + if( cen ) { + bc = cen; + } else { + bc = astMalloc( sizeof( double)*(size_t)ncb ); + if( astOK ) { + for( i = 0; i < ncb; i++ ) bc[ i ] = ptr[ i ][ index ]; + } + } + } + +/* Set the centre in the two component Regions in their current Frames. */ + astRegCentre( reg1, bc, NULL, 0, AST__CURRENT ); + astRegCentre( reg2, bc + nax1, NULL, 0, AST__CURRENT ); + +/* Free resources. */ + if( bc != cen ) bc = astFree( bc ); + } + + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + +/* Return the result. */ + return result; +} + +static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Prism. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* Prism member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Prism. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Prism "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Prism. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Local variables: */ + AstPointSet *ps1; /* Points projected into 1st component Region */ + AstPointSet *ps1b; /* "ps1" in base Frame of 1st component Region */ + AstPointSet *ps1b2; /* "ps1b" transformed using 1st Region */ + AstPointSet *ps2b2; /* "ps2b" transformed using 2nd Region */ + AstPointSet *ps2; /* Points projected into 2nd component Region */ + AstPointSet *ps2b; /* "ps2" in base Frame of 2nd component Region */ + AstPrism *this; /* Pointer to the Prism structure. */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + AstRegion *unc1; /* Base Frame uncertainty in 1st component Region */ + AstRegion *unc2; /* Base Frame uncertainty in 2nd component Region */ + double **ptr1b2; /* Pointer to axis values in "ps1b2" */ + double **ptr2b2; /* Pointer to axis values in "ps2b2" */ + int *mask1; /* Mask for first component boundary */ + int *mask2; /* Mask for second component boundary */ + int cl1; /* Original Closed flag for reg1 */ + int cl2; /* Original Closed flag for reg2 */ + int i; /* Point index */ + int j; /* Axis index */ + int nax1; /* Number of axes in first component Region */ + int nax2; /* Number of axes in second component Region */ + int np; /* Number of points in supplied PointSet */ + int on; /* Is this point on the Prism boundary? */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* Get pointers to the two component Regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* Temporarily ensure the components are closed. */ + cl1 = astTestClosed( reg1 ) ? astGetClosed( reg1 ) : INT_MAX; + cl2 = astTestClosed( reg2 ) ? astGetClosed( reg2 ) : INT_MAX; + astSetClosed( reg1, cl1 ); + astSetClosed( reg2, cl2 ); + +/* A point in the coordinate system represented by the Prism is on the + boundary of the Prism if: + 1) it is on the boundary of one of the coomponent Regions AND + 2) it is on or within the boundary of the other component Region. + + First look for points on the boundary of the first component Region. + Create a PointSet holding just the axes from the supplied PointSet + which relate to the first component Region. */ + np = astGetNpoint( pset ); + nax1 = astGetNaxes( reg1 ); + ps1 = astPointSet( np, nax1, "", status ); + astSetSubPoints( pset, 0, 0, ps1 ); + +/* Get a mask which indicates if each supplied point is on or off the + boundary of the first component Region. astRegPins expects its "pset" + argument to contain positions in the base Frame of the Region, so + we must first transform the supplied points into the base Frame of + "reg1". We must also get the uncertainty in the base Frame of the + component Region. */ + ps1b = astRegTransform( reg1, ps1, 0, NULL, NULL ); + unc1 = astGetUncFrm( reg1, AST__BASE ); + astRegPins( reg1, ps1b, unc1, &mask1 ); + +/* Also determine which of the points are on or in the boundary by using + "reg1" as a Mapping to transform the supplied points. */ + ps1b2 = astTransform( reg1, ps1b, 1, NULL ); + +/* Do the same for the second component Region */ + nax2 = astGetNaxes( reg2 ); + ps2 = astPointSet( np, nax2, "", status ); + astSetSubPoints( pset, 0, nax1, ps2 ); + ps2b = astRegTransform( reg2, ps2, 0, NULL, NULL ); + unc2 = astGetUncFrm( reg2, AST__BASE ); + astRegPins( reg2, ps2b, unc2, &mask2 ); + ps2b2 = astTransform( reg2, ps2b, 1, NULL ); + +/* Get pointers to the data in all the relevant PointSets. */ + ptr1b2 = astGetPoints( ps1b2 ); + ptr2b2 = astGetPoints( ps2b2 ); + +/* Check pointers can be dereferenced safely. */ + if( astOK ) { + +/* Assume all points are on the boundary of the Prism. */ + result = 1; + +/* Check each point. */ + for( i = 0; i < np; i++ ) { + +/* Assume this point is on the boundary of the Prism. */ + on = 1; + +/* If this point is on the boundary of both component Regions, it is on + the boundary of the Prism. If it is on the boundary of the first + component Region but not on the boundary of the second, then it is + still on the boundary of the Prism if it is within the volume + reporesented by the second. */ + if( mask1[ i ] ) { + if( !mask2[ i ] ) { + for( j = 0; j < nax2; j++ ) { + if( ptr2b2[ j ][ i ] == AST__BAD ) { + on = 0; + break; + } + } + } + +/* If this point is on the boundary of the second component Region but + not the first, it is on the boundary of the Prism if it is within the + volume reporesented by the first. */ + } else { + if( mask2[ i ] ) { + for( j = 0; j < nax1; j++ ) { + if( ptr1b2[ j ][ i ] == AST__BAD ) { + on = 0; + break; + } + } + +/* If this point is on the boundary of neither component Region, it is not + on the boundary of the Prism. */ + } else { + on = 0; + } + } + +/* Use "mask1" to return the Prism's mask. Clear the returned flag if + this point is not on the boundary of the Prism. */ + mask1[ i ] = on; + if( !on ) result = 0; + } + } + +/* Re-instate the original values of the Closed attribute for the + component Regions. */ + if( cl1 == INT_MAX ) { + astClearClosed( reg1 ); + } else { + astSetClosed( reg1, cl1 ); + } + if( cl2 == INT_MAX ) { + astClearClosed( reg2 ); + } else { + astSetClosed( reg2, cl2 ); + } + +/* Return "mask1" as the Prism's mask if required. Otherwise free it. */ + if( mask ) { + *mask = mask1; + } else { + mask1 = astFree( mask1 ); + } + +/* Free other resources */ + mask2 = astFree( mask2 ); + ps1 = astAnnul( ps1 ); + ps1b = astAnnul( ps1b ); + ps1b2 = astAnnul( ps1b2 ); + ps2 = astAnnul( ps2 ); + unc1 = astAnnul( unc1 ); + ps2b = astAnnul( ps2b ); + ps2b2 = astAnnul( ps2b2 ); + unc2 = astAnnul( unc2 ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astAnnul( *mask ); + } + +/* Return the result. */ + return result; +} + +static void RegClearAttrib( AstRegion *this_region, const char *attrib, + char **base_attrib, int *status ) { +/* +* Name: +* RegClearAttrib + +* Purpose: +* Clear an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* void RegClearAttrib( AstRegion *this, const char *attrib, +* char **base_attrib, int *status ) + +* Class Membership: +* Prism member function (over-rides the astRegClearAttrib method +* inherited from the Region class). + +* Description: +* This function clears the value of a named attribute in both the base +* and current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* attrib +* Pointer to a null terminated string holding the attribute name. +* NOTE, IT SHOULD BE ENTIRELY LOWER CASE. +* base_attrib +* Address of a location at which to return a pointer to the null +* terminated string holding the attribute name which was cleared in +* the base Frame of the encapsulated FrameSet. This may differ from +* the supplied attribute if the supplied attribute contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPrism *this; + AstRegion *creg; + char *batt; + char buf1[ 100 ]; + char buf2[ 255 ]; + int axis; + int len; + int nax1; + int nc; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* Use the RegClearAttrib method inherited from the parent class to clear the + attribute in the current and base Frames in the FrameSet encapsulated by + the parent Region structure. */ + (*parent_regclearattrib)( this_region, attrib, &batt, status ); + +/* We now propagate the setting down to the component Regions. The current + Frames in the component Regions together form a CmpFrame which is + equivalent to the base Frame in the parent FrameSet. Switch off error + reporting whilst we apply the setting to the component Regions. */ + rep = astReporting( 0 ); + +/* If the setting which was applied to the base Frame of the parent FrameSet + is qualified by an axis index, modify the axis index to refer to component + Region which defines the axis. First parse the base Frame attribute setting + to locate any axis index. */ + len = strlen( batt ); + if( nc = 0, ( 2 == astSscanf( batt, "%[^(](%d) %n", buf1, &axis, + &nc ) ) && ( nc >= len ) ) { + +/* If found, convert the axis index from one-based to zero-based. */ + axis--; + +/* Get a pointer to the component Region containing the specified axis, and + create a new setting with the same attribute name but with the axis index + appropriate to the component Region which defines the axis. */ + nax1 = astGetNaxes( this->region1 ); + if( axis < nax1 ) { + creg = this->region1; + } else { + creg = this->region2; + axis -= nax1; + } + sprintf( buf2, "%s(%d)", buf1, axis + 1 ); + +/* Apply the setting to the relevant component Region. */ + astRegClearAttrib( creg, buf2, NULL ); + +/* If the setting is not qualified by an axis index, apply it to both + component Regions. */ + } else { + astRegClearAttrib( this->region1, batt, NULL ); + astRegClearAttrib( this->region2, batt, NULL ); + } + +/* Annul the error if the attribute was not recognised by the component + Regions. Then switch error reporting back on. */ + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + +/* If required, return the base Frame setting string. Otherwise free it. */ + if( base_attrib ) { + *base_attrib = batt; + } else { + batt = astFree( batt ); + } + +} + +static void RegSetAttrib( AstRegion *this_region, const char *setting, + char **base_setting, int *status ) { +/* +* Name: +* RegSetAttrib + +* Purpose: +* Set an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* void RegSetAttrib( AstRegion *this, const char *setting, +* char **base_setting, int *status ) + +* Class Membership: +* Prism method (over-rides the astRegSetAttrib method inherited from +* the Region class). + +* Description: +* This function assigns an attribute value to both the base and +* current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* setting +* Pointer to a null terminated attribute setting string. NOTE, IT +* SHOULD BE ENTIRELY LOWER CASE. The supplied string will be +* interpreted using the public interpretation implemented by +* astSetAttrib. This can be different to the interpretation of the +* protected accessor functions. For instance, the public +* interpretation of an unqualified floating point value for the +* Epoch attribute is to interpet the value as a gregorian year, +* but the protected interpretation is to interpret the value as an +* MJD. +* base_setting +* Address of a location at which to return a pointer to the null +* terminated attribute setting string which was applied to the +* base Frame of the encapsulated FrameSet. This may differ from +* the supplied setting if the supplied setting contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstPrism *this; + AstRegion *creg; + char *bset; + char buf1[ 100 ]; + char buf2[ 255 ]; + int axis; + int len; + int nax1; + int nc; + int rep; + int value; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Prism structure. */ + this = (AstPrism *) this_region; + +/* Use the RegSetAttrib method inherited from the parent class to apply the + setting to the current and base Frames in the FrameSet encapsulated by the + parent Region structure. */ + (*parent_regsetattrib)( this_region, setting, &bset, status ); + +/* We now propagate the setting down to the component Regions. The current + Frames in the component Regions together form a CmpFrame which is + equivalent to the base Frame in the parent FrameSet. Switch off error + reporting whilst we apply the setting to the component Regions. */ + rep = astReporting( 0 ); + +/* If the setting which was applied to the base Frame of the parent FrameSet + is qualified by an axis index, modify the axis index to refer to component + Region which defines the axis. First parse the base Frame attribute setting + to locate any axis index. */ + len = strlen( bset ); + if( nc = 0, ( 2 == astSscanf( bset, "%[^(](%d)= %n%*s %n", buf1, &axis, + &value, &nc ) ) && ( nc >= len ) ) { + +/* If found, convert the axis index from one-based to zero-based. */ + axis--; + +/* Get a pointer to the component Region containing the specified axis, and + create a new setting with the same attribute name but with the axis index + appropriate to the component Region which defines the axis. */ + nax1 = astGetNaxes( this->region1 ); + if( axis < nax1 ) { + creg = this->region1; + } else { + creg = this->region2; + axis -= nax1; + } + sprintf( buf2, "%s(%d)=%s", buf1, axis + 1, bset + value ); + +/* Apply the setting to the relevant component Region. */ + astRegSetAttrib( creg, buf2, NULL ); + +/* If the setting is not qualified by an axis index, apply it to both + component Regions. */ + } else { + astRegSetAttrib( this->region1, bset, NULL ); + astRegSetAttrib( this->region2, bset, NULL ); + } + +/* Annul the error if the attribute was not recognised by the component + Regions. Then switch error reporting back on. */ + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + +/* If required, return the base Frame setting string. Otherwise free it. */ + if( base_setting ) { + *base_setting = bset; + } else { + bset = astFree( bset ); + } + +} + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* Prism method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *cfrm; /* Frame containing required axes */ + AstRegion *creg; /* Pointer to component Region structure */ + int *axes; /* Pointer to array of axis indices */ + int i; /* Loop count */ + int nax1; /* No.of axes in 1st component Frame */ + int nax2; /* No.of axes in 2nd component Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* If either component Region has a dummy FrameSet use this method + recursively to give them a FrameSet containing the corresponding axes + from the supplied Frame. */ + creg = ((AstPrism *) this_region )->region1; + if( creg ) { + nax1 = astGetNaxes( creg ); + if( !astGetRegionFS( creg ) ) { + axes = astMalloc( sizeof( int )*(size_t) nax1 ); + if( astOK ) for( i = 0; i < nax1; i++ ) axes[ i ] = i; + cfrm = astPickAxes( frm, nax1, axes, NULL ); + astSetRegFS( creg, cfrm ); + axes = astFree( axes ); + cfrm = astAnnul( cfrm ); + } + + } else { + nax1 = 0; + } + + creg = ((AstPrism *) this_region )->region2; + if( creg && !astGetRegionFS( creg ) ) { + nax2 = astGetNaxes( creg ); + axes = astMalloc( sizeof( int )*(size_t) nax2 ); + if( astOK ) for( i = 0; i < nax2; i++ ) axes[ i ] = nax1 + i; + cfrm = astPickAxes( frm, nax2, axes, NULL ); + astSetRegFS( creg, cfrm ); + axes = astFree( axes ); + cfrm = astAnnul( cfrm ); + } + +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Prism method (over-rides the astSimplify method inherited from +* the Region class). + +* Description: +* This function simplifies a Prism to eliminate redundant +* computational steps, or to merge separate steps which can be +* performed more efficiently in a single operation. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new pointer to the (possibly simplified) Region. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. + +* Deficiencies: +* - Currently, this function does not attempt to map the component +* Regions into the current Frame of the parent Region structure. + +*/ + +/* Local Variables: */ + AstFrame *cfrm; /* Current Frame */ + AstFrame *newfrm1; /* Current Frame axes for reg1 */ + AstFrame *newfrm2; /* Current Frame axes for reg2 */ + AstFrameSet *fs; /* Parent FrameSet */ + AstMapping *bcmap; /* Base->current Mapping */ + AstMapping *nmap1; /* Reg1->current Mapping */ + AstMapping *map1; /* First component Region from a CmpMap */ + AstMapping *map2; /* Second component Region from a CmpMap */ + int series; /* Apply component Mappings in series? */ + int invert1; /* Use inverted first component Mapping? */ + int invert2; /* Use inverted second component Mapping? */ + AstMapping *nmap2; /* Reg2->current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstCmpMap *cmpmap; /* Result pointer to return */ + AstMapping *smap; /* Simplified CmpMap */ + AstRegion *new2; /* New simplified Region */ + AstRegion *new; /* New Region */ + AstRegion *newreg1; /* Reg1 mapped into current Frame */ + AstRegion *newreg2; /* Reg2 mapped into current Frame */ + AstRegion *reg1; /* First component Region */ + AstRegion *reg2; /* Second component Region */ + AstRegion *reg; /* This Region */ + AstRegion *snewreg1; /* Simplified newreg1 */ + AstRegion *snewreg2; /* Simplified newreg2 */ + AstRegion *unc; /* Uncertainty Region from supplied Prism */ + int *axin; /* Indices of Mapping inputs to use */ + int *axout1; /* Indices of cfrm axes corresponding to reg1 */ + int *axout2; /* Indices of cfrm axes corresponding to reg2 */ + int *perm; /* Axis permutation array */ + int i; /* Loop count */ + int nax1; /* Number of axes in first component Region */ + int nax2; /* Number of axes in second component Region */ + int naxt; /* Total number of axes in current Frame */ + int naxout1; /* Number of current axes for reg1 */ + int naxout2; /* Number of current axes for reg2 */ + int neg; /* Negated flag for supplied Prism */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Region structure */ + reg = (AstRegion *) this_mapping; + +/* Get the component Regions, and the Negated flag for the Prism. */ + GetRegions( (AstPrism *) this_mapping, ®1, ®2, &neg, status ); + +/* The above Regions describe areas within the base Frame of the FrameSet + encapsulated by the parent Region structure. Get the current Frame in + this FrameSet and the base->current Mapping. */ + fs = reg->frameset; + cfrm = astGetFrame( fs, AST__CURRENT ); + bcmap = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Combine the two Regions into a parallel CmpMap (this uses the fact + that a Region is a type of Mapping). Then simplify the CmpMap. This + will result in the astMapMerge methods defined by sub-classes of + Regions being used to merge adjacent regions. */ + cmpmap = astCmpMap( reg1, reg2, 0, "", status ); + smap = astSimplify( cmpmap ); + cmpmap = astAnnul( cmpmap ); + +/* Initially, assume we cannot form a new Region from the simplified + CmpMap. */ + new = NULL; + +/* If the result is a region, use it. */ + if( astIsARegion( smap ) ) { + new = astClone( smap ); + +/* If the result is a parallel CmpMap, get its two components and check + they are Regions. If so, and if they are not the same as the original + two component Regions, form a new Prism from them. */ + } else if( astIsACmpMap( smap ) ) { + astDecompose( smap, &map1, &map2, &series, &invert1, &invert2 ); + if( ! series && astIsARegion( map1 ) && astIsARegion( map2 ) ) { + if( (AstRegion *) map1 != reg1 || (AstRegion *) map2 != reg2 ) { + new = (AstRegion *) astPrism( (AstRegion *) map1, + (AstRegion *) map2, "", status ); + } + } + +/* Free resources */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + } + + smap = astAnnul( smap ); + +/* If the above produced a simplified Region, map it into the current Frame + of "this" and simplify it. */ + if( new ) { + new2 = astMapRegion( new, bcmap, cfrm ); + (void) astAnnul( new ); + new = astSimplify( new2 ); + new2 = astAnnul( new2 ); + +/* If the above did not produced a result, try a different approach. */ + } else { + +/* Get the number of axes in each component Region. */ + nax1 = astGetNaxes( reg1 ); + nax2 = astGetNaxes( reg2 ); + +/* Use astMapSplit to see if the axes of the first component Region correspond + to a distinct set of axes within the current Frame. If this is the case, + then a Mapping is returned by astMapSplit which maps the axes of the first + component Region into the corresponding current Frame axes. Also returned + is a list of the axes within the current Frame which correspnd to the + axes of the first component Region. */ + nmap1 = NULL; + axout1 = NULL; + axin = astMalloc( sizeof( int )*(size_t)nax1 ); + if( astOK ) { + for( i = 0; i < nax1; i++ ) axin[ i ] = i; + axout1 = astMapSplit( bcmap, nax1, axin, &nmap1 ); + axin = astFree( axin ); + } + +/* Do the same for the second component. */ + nmap2 = NULL; + axout2 = NULL; + axin = astMalloc( sizeof( int )*(size_t)nax2 ); + if( astOK ) { + for( i = 0; i < nax2; i++ ) axin[ i ] = i + nax1; + axout2 = astMapSplit( bcmap, nax2, axin, &nmap2 ); + axin = astFree( axin ); + } + +/* Assume for the moment that the component Regions cannot be simplified. + In this case we will use a clone of the supplied Prism. */ + new = astClone( this_mapping ); + +/* Determine the number of outputs from these Mappings. */ + if( nmap1 ){ + naxout1 = astGetNout( nmap1 ); + } else { + naxout1 = 0; + } + if( nmap2 ){ + naxout2 = astGetNout( nmap2 ); + } else { + naxout2 = 0; + } + +/* Determine the number of axes in the current Frame of the Prism. */ + naxt = astGetNout( bcmap ); + +/* If the second component does not contribute any axes to the total + Prism, we can ignore it. */ + if( naxout1 == naxt && naxout2 == 0 ) { + newfrm1 = astPickAxes( cfrm, naxout1, axout1, NULL ); + newreg1 = astMapRegion( reg1, nmap1, newfrm1 ); + (void) astAnnul( new ); + new = astSimplify( newreg1 ); + if( neg ) astNegate( new ); + perm = astMalloc( sizeof( int )*(size_t) ( naxout1 ) ); + if( astOK ) { + for( i = 0; i < naxout1; i++ ) perm[ i ] = axout1[ i ]; + astPermAxes( new, perm ); + perm = astFree( perm ); + } + newfrm1 = astAnnul( newfrm1 ); + newreg1 = astAnnul( newreg1 ); + +/* If the first component does not contribute any axes to the total + Prism, we can ignore it. */ + } else if( naxout1 == 0 && naxout2 == naxt ) { + newfrm2 = astPickAxes( cfrm, naxout2, axout2, NULL ); + newreg2 = astMapRegion( reg2, nmap2, newfrm2 ); + (void) astAnnul( new ); + new = astSimplify( newreg2 ); + if( neg ) astNegate( new ); + perm = astMalloc( sizeof( int )*(size_t) ( naxout2 ) ); + if( astOK ) { + for( i = 0; i < naxout2; i++ ) perm[ i ] = axout2[ i ]; + astPermAxes( new, perm ); + perm = astFree( perm ); + } + newfrm2 = astAnnul( newfrm2 ); + newreg2 = astAnnul( newreg2 ); + +/* If both component Regions correspond to a distinct subspace within the + current Frame, then we can try to express each component Region within + the current Frame. */ + } else if( nmap1 && nmap2 ) { + +/* Create a Frame representing the subspace of the current Frame which + corresponds to the axes of the first component Region. */ + newfrm1 = astPickAxes( cfrm, naxout1, axout1, NULL ); + +/* Remap the first component Region so that it represents an area in this + subspace. */ + newreg1 = astMapRegion( reg1, nmap1, newfrm1 ); + +/* Attempt to simplify the remapped Region. */ + snewreg1 = astSimplify( newreg1 ); + +/* Do the same for the second component Region. */ + naxout2 = astGetNout( nmap2 ); + newfrm2 = astPickAxes( cfrm, naxout2, axout2, NULL ); + newreg2 = astMapRegion( reg2, nmap2, newfrm2 ); + snewreg2 = astSimplify( newreg2 ); + +/* If either component Region was simplified, create a new Prism from the + simplified Regions. */ + if( snewreg1 != newreg1 || snewreg2 != newreg2 ) { + (void) astAnnul( new ); + new = (AstRegion *) astPrism( snewreg1, snewreg2, "", status ); + +/* Ensure the new Prism has the same Negated attribute as the original. */ + if( neg ) astNegate( new ); + +/* Ensure that the new Prism has the same axis order as the original + current Frame. */ + perm = astMalloc( sizeof( int )*(size_t) ( naxout1 + naxout2 ) ); + if( astOK ) { + for( i = 0; i < naxout1; i++ ) perm[ i ] = axout1[ i ]; + for( ; i < naxout1 + naxout2; i++ ) perm[ i ] = axout2[ i - naxout1 ]; + astPermAxes( new, perm ); + perm = astFree( perm ); + } + } + +/* Free resources. */ + newfrm1 = astAnnul( newfrm1 ); + newfrm2 = astAnnul( newfrm2 ); + newreg1 = astAnnul( newreg1 ); + newreg2 = astAnnul( newreg2 ); + snewreg1 = astAnnul( snewreg1 ); + snewreg2 = astAnnul( snewreg2 ); + } + +/* Free resources. */ + if( axout1 ) axout1 = astFree( axout1 ); + if( axout2 ) axout2 = astFree( axout2 ); + if( nmap1 ) nmap1 = astAnnul( nmap1 ); + if( nmap2 ) nmap2 = astAnnul( nmap2 ); + } + +/* If we have created a new Region, ensure any user-supplied uncertainty + that has been stored explicitly with the supplied Prism is passed on + to the new Region. */ + if( new ) { + if( astTestUnc( reg ) ) { + unc = astGetUnc( reg, 0 ); + astSetUnc( new, unc ); + } + +/* Now invoke the parent Simplify method inherited from the Region class. + This will simplify the encapsulated FrameSet and uncertainty Region. */ + result = (*parent_simplify)( (AstMapping *) new, status ); + new = astAnnul( new ); + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + cfrm = astAnnul( cfrm ); + bcmap = astAnnul( bcmap ); + +/* If any simplification could be performed, copy Region attributes from + the supplied Region to the returned Region, and return a pointer to it. */ + if( result != this_mapping ) astRegOverlay( result, (AstRegion *) this_mapping, 0 ); + +/* If an error occurred, annul the returned Mapping. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a Prism to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "prism.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Prism member function (over-rides the astTransform method inherited +* from the Region class). + +* Description: +* This function takes a Prism and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Region. +* This implies applying each of the Prism's component Regions in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the Prism. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the Prism being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstCmpMap *map; /* CmpMap containing component Regions */ + AstPointSet *psb; /* Pointer to base Frame PointSet */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + AstPrism *this; /* Pointer to the Prism structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **ptr_out; /* Pointer to output coordinate data */ + double **ptrb; /* Pointer to base Frame axis values */ + int coord; /* Zero-based index for coordinates */ + int good; /* Is the point inside the Prism? */ + int ncoord_out; /* No. of coordinates per output point */ + int ncoord_tmp; /* No. of coordinates per base Frame point */ + int neg; /* Has Prism been negated? */ + int npoint; /* No. of points */ + int point; /* Loop counter for points */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a Pointer to the Prism structure */ + this = (AstPrism *) this_mapping; + +/* Get the component Regions, and the Negated value for the Prism. */ + GetRegions( this, ®1, ®2, &neg, status ); + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, containing + a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet in the parent Region structure to + transform the supplied positions from the current Frame in the + encapsulated FrameSet (the Frame represented by the Prism), to the + base Frame (the Frame in which the component Regions are defined). Note, + the returned pointer may be a clone of the "in" pointer, and so we + must be carefull not to modify the contents of the returned PointSet. */ + pset_tmp = astRegTransform( this, in, 0, NULL, NULL ); + +/* Form a parallel CmpMap from the two component Regions. */ + map = astCmpMap( reg1, reg2, 0, "", status ); + +/* Apply the Mapping to the PointSet containing positions in the base Frame + of the parent Region structure (which is the same as the combination of + the current Frames of the component Regions). */ + psb = astTransform( map, pset_tmp, 1, NULL ); + +/* Annul the Mapping pointer. */ + map = astAnnul( map ); + +/* Determine the numbers of points and coordinates per point for these base + Frame PointSets and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( pset_tmp ); + ncoord_tmp = astGetNcoord( pset_tmp ); + ptrb = astGetPoints( psb ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + for ( point = 0; point < npoint; point++ ) { + good = 1; + + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + if( ptrb[ coord ][ point ] == AST__BAD ){ + good = 0; + break; + } + } + + if( good == neg ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + psb = astAnnul( psb ); + pset_tmp = astAnnul( pset_tmp ); + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Prism objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Prism objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Regions within the Prism. +*/ + +/* Local Variables: */ + AstPrism *in; /* Pointer to input Prism */ + AstPrism *out; /* Pointer to output Prism */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Prisms. */ + in = (AstPrism *) objin; + out = (AstPrism *) objout; + +/* For safety, start by clearing any references to the input component + Regions from the output Prism. */ + out->region1 = NULL; + out->region2 = NULL; + +/* Make copies of these Regions and store pointers to them in the output + Prism structure. */ + out->region1 = astCopy( in->region1 ); + out->region2 = astCopy( in->region2 ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Prism objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Prism objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to Prism */ + +/* Obtain a pointer to the Prism structure. */ + this = (AstPrism *) obj; + +/* Annul the pointers to the component Regions. */ + this->region1 = astAnnul( this->region1 ); + this->region2 = astAnnul( this->region2 ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Prism objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Prism class to an output Channel. + +* Parameters: +* this +* Pointer to the Prism whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstPrism *this; /* Pointer to the Prism structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Prism structure. */ + this = (AstPrism *) this_object; + +/* Write out values representing the instance variables for the Prism + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* First Region. */ +/* -------------- */ + astWriteObject( channel, "RegionA", 1, 1, this->region1, + "First component Region" ); + +/* Second Region. */ +/* --------------- */ + astWriteObject( channel, "RegionB", 1, 1, this->region2, + "Second component Region" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAPrism and astCheckPrism functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Prism,Region) +astMAKE_CHECK(Prism) + +AstPrism *astPrism_( void *region1_void, void *region2_void, + const char *options, int *status, ...) { +/* +*+ +* Name: +* astPrism + +* Purpose: +* Create a Prism. + +* Type: +* Protected function. + +* Synopsis: +* #include "prism.h" +* AstPrism *astPrism( AstRegion *region1, AstRegion *region2, +* const char *options, ..., int *status ) + +* Class Membership: +* Prism constructor. + +* Description: +* This function creates a new Prism and optionally initialises its +* attributes. + +* Parameters: +* region1 +* Pointer to the Region to be extruded. +* region2 +* Pointer to the Region defining the extent of the extrusion. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new Prism. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new Prism. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic Prism constructor which is +* available via the protected interface to the Prism class. A +* public interface is provided by the astPrismId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "region1" and "region2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPrism *new; /* Pointer to new Prism */ + AstRegion *region1; /* Pointer to first Region structure */ + AstRegion *region2; /* Pointer to second Region structure */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Region structures provided. */ + region1 = astCheckRegion( region1_void ); + region2 = astCheckRegion( region2_void ); + if ( astOK ) { + +/* Initialise the Prism, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPrism( NULL, sizeof( AstPrism ), !class_init, + &class_vtab, "Prism", region1, region2 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new Prism's + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new Prism. */ + return new; +} + +AstPrism *astPrismId_( void *region1_void, void *region2_void, + const char *options, ... ) { +/* +*++ +* Name: +c astPrism +f AST_PRISM + +* Purpose: +* Create a Prism. + +* Type: +* Public function. + +* Synopsis: +c #include "prism.h" +c AstPrism *astPrism( AstRegion *region1, AstRegion *region2, +c const char *options, ... ) +f RESULT = AST_PRISM( REGION1, REGION2, OPTIONS, STATUS ) + +* Class Membership: +* Prism constructor. + +* Description: +* This function creates a new Prism and optionally initialises +* its attributes. +* +* A Prism is a Region which represents an extrusion of an existing Region +* into one or more orthogonal dimensions (specified by another Region). +* If the Region to be extruded has N axes, and the Region defining the +* extrusion has M axes, then the resulting Prism will have (M+N) axes. +* A point is inside the Prism if the first N axis values correspond to +* a point inside the Region being extruded, and the remaining M axis +* values correspond to a point inside the Region defining the extrusion. +* +* As an example, a cylinder can be represented by extruding an existing +* Circle, using an Interval to define the extrusion. Ih this case, the +* Interval would have a single axis and would specify the upper and +* lower limits of the cylinder along its length. + +* Parameters: +c region1 +f REGION1 = INTEGER (Given) +* Pointer to the Region to be extruded. +c region2 +f REGION2 = INTEGER (Given) +* Pointer to the Region defining the extent of the extrusion. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Prism. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Prism. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astPrism() +f AST_PRISM = INTEGER +* A pointer to the new Prism. + +* Notes: +* - Deep copies are taken of the supplied Regions. This means that +* any subsequent changes made to the component Regions using the +* supplied pointers will have no effect on the Prism. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astPrism constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astPrism_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "region1" and "region2" parameters +* are of type (void *) and are converted from an ID value to a +* pointer and validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astPrism_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPrism *new; /* Pointer to new Prism */ + AstRegion *region1; /* Pointer to first Region structure */ + AstRegion *region2; /* Pointer to second Region structure */ + int *status; /* Pointer to inherited status value */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain the Region pointers from the ID's supplied and validate the + pointers to ensure they identify valid Regions. */ + region1 = astVerifyRegion( astMakePointer( region1_void ) ); + region2 = astVerifyRegion( astMakePointer( region2_void ) ); + if ( astOK ) { + +/* Initialise the Prism, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitPrism( NULL, sizeof( AstPrism ), !class_init, + &class_vtab, "Prism", region1, region2 ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new Prism's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new Prism. */ + return astMakeId( new ); +} + +AstPrism *astInitPrism_( void *mem, size_t size, int init, AstPrismVtab *vtab, + const char *name, AstRegion *region1, + AstRegion *region2, int *status ) { +/* +*+ +* Name: +* astInitPrism + +* Purpose: +* Initialise a Prism. + +* Type: +* Protected function. + +* Synopsis: +* #include "prism.h" +* AstPrism *astInitPrism_( void *mem, size_t size, int init, +* AstPrismVtab *vtab, const char *name, +* AstRegion *region1, AstRegion *region2 ) + +* Class Membership: +* Prism initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Prism object. It allocates memory (if necessary) to +* accommodate the Prism plus any additional data associated with the +* derived class. It then initialises a Prism structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a Prism at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Prism is to be initialised. +* This must be of sufficient size to accommodate the Prism data +* (sizeof(Prism)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the Prism (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* Prism structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the Prism's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Prism. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* region1 +* Pointer to the first Region. +* region2 +* Pointer to the second Region. + +* Returned Value: +* A pointer to the new Prism. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstPrism *new; /* Pointer to new Prism */ + AstFrame *frm1; /* Frame encapsulated by 1st Region */ + AstFrame *frm2; /* Frame encapsulated by 2nd Region */ + AstFrame *frm; /* CmpFrame formed from frm1 and frm2 */ + AstMapping *map; /* Mapping between two supplied Regions */ + AstRegion *reg1; /* Copy of first supplied Region */ + AstRegion *reg2; /* Copy of second supplied Region */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitPrismVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + reg2 = NULL; + +/* Take a copy of the two supplied Regions. */ + reg1 = astCopy( region1 ); + reg2 = astCopy( region2 ); + +/* Form a CmpFrame representing the combined Frame of these two Regions. */ + frm1 = astRegFrame( reg1 ); + frm2 = astRegFrame( reg2 ); + frm = (AstFrame *) astCmpFrame( frm1, frm2, "", status ); + +/* Initialise a Region structure (the parent class) as the first component + within the Prism structure, allocating memory if necessary. A NULL + PointSet is suppled as the two component Regions will perform the function + of defining the Region shape. The base Frame of the FrameSet in the + parent Region structure will be the CmpFrame formed from the two component + Regions. */ + if ( astOK ) { + new = (AstPrism *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, frm, NULL, NULL ); + +/* Initialise the Prism data. */ +/* --------------------------- */ +/* Store pointers to the component Regions. */ + new->region1 = reg1; + new->region2 = reg2; + +/* If the base->current Mapping in the FrameSet within a component Region + is a UnitMap, then the FrameSet does not need to be included in the + Dump of the new Prism. Set the RegionFS attribute of the component + Region to zero to flag this. */ + map = astGetMapping( reg1->frameset, AST__BASE, AST__CURRENT ); + if( astIsAUnitMap( map ) ) astSetRegionFS( reg1, 0 ); + map = astAnnul( map ); + + map = astGetMapping( reg2->frameset, AST__BASE, AST__CURRENT ); + if( astIsAUnitMap( map ) ) astSetRegionFS( reg2, 0 ); + map = astAnnul( map ); + +/* If an error occurred, clean up by annulling the Region pointers and + deleting the new object. */ + if ( !astOK ) { + new->region1 = astAnnul( new->region1 ); + new->region2 = astAnnul( new->region2 ); + new = astDelete( new ); + } + } + +/* Free resources */ + frm = astAnnul( frm ); + frm1 = astAnnul( frm1 ); + frm2 = astAnnul( frm2 ); + +/* Return a pointer to the new object. */ + return new; +} + +AstPrism *astLoadPrism_( void *mem, size_t size, AstPrismVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadPrism + +* Purpose: +* Load a Prism. + +* Type: +* Protected function. + +* Synopsis: +* #include "prism.h" +* AstPrism *astLoadPrism( void *mem, size_t size, AstPrismVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Prism loader. + +* Description: +* This function is provided to load a new Prism using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Prism structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Prism at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the Prism is to be +* loaded. This must be of sufficient size to accommodate the +* Prism data (sizeof(Prism)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Prism (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Prism structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstPrism) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Prism. If this is NULL, a pointer to +* the (static) virtual function table for the Prism class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Prism" is used instead. + +* Returned Value: +* A pointer to the new Prism. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrame *cfrm; /* Frame containing required axes */ + AstFrame *f1; /* Base Frame in parent Region */ + AstPrism *new; /* Pointer to the new Prism */ + AstRegion *creg; /* Pointer to component Region */ + int *axes; /* Pointer to array of axis indices */ + int i; /* Loop count */ + int nax1; /* No.of axes in 1st component Frame */ + int nax2; /* No.of axes in 2nd component Frame */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Prism. In this case the + Prism belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstPrism ); + vtab = &class_vtab; + name = "Prism"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitPrismVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Prism. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Prism" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* First Region. */ +/* -------------- */ + new->region1 = astReadObject( channel, "regiona", NULL ); + +/* Second Region. */ +/* --------------- */ + new->region2 = astReadObject( channel, "regionb", NULL ); + +/* Either component Region may currently contain a dummy FrameSet rather than + the correct FrameSet (see the Dump function for this class). In this case, + the correct FrameSet will have copies of selected axes from the base Frame + of the new Prism as both its current and base Frames, and these are + connected by a UnitMap (this is equivalent to a FrameSet containing a + single Frame). However if the new Prism being loaded has itself got a dummy + FrameSet, then we do not do this since we do not yet know what the correct + FrameSet is. In this case we wait until the parent Region invokes the + astSetRegFS method on the new Prism. */ + if( !astRegDummyFS( new ) ) { + f1 = astGetFrame( ((AstRegion *) new)->frameset, AST__BASE ); + + creg = new->region1; + nax1 = astGetNaxes( creg ); + if( astRegDummyFS( creg ) ) { + axes = astMalloc( sizeof( int )*(size_t) nax1 ); + if( astOK ) for( i = 0; i < nax1; i++ ) axes[ i ] = i; + cfrm = astPickAxes( f1, nax1, axes, NULL ); + astSetRegFS( creg, cfrm ); + axes = astFree( axes ); + cfrm = astAnnul( cfrm ); + } + + creg = new->region2; + if( astRegDummyFS( creg ) ) { + nax2 = astGetNaxes( creg ); + axes = astMalloc( sizeof( int )*(size_t) nax2 ); + if( astOK ) for( i = 0; i < nax2; i++ ) axes[ i ] = nax1 + i; + cfrm = astPickAxes( f1, nax2, axes, NULL ); + astSetRegFS( creg, cfrm ); + axes = astFree( axes ); + cfrm = astAnnul( cfrm ); + } + + f1 = astAnnul( f1 ); + } + +/* If an error occurred, clean up by deleting the new Prism. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Prism pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* None. */ + + + diff --git a/prism.h b/prism.h new file mode 100644 index 0000000..ab14c77 --- /dev/null +++ b/prism.h @@ -0,0 +1,238 @@ +#if !defined( PRISM_INCLUDED ) /* Include this file only once */ +#define PRISM_INCLUDED +/* +*+ +* Name: +* prism.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Prism class. + +* Invocation: +* #include "prism.h" + +* Description: +* This include file defines the interface to the Prism class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Prism class implement a Region which represents an extrusion of +* another Region into higher dimensions. For instance, a Prism can be +* used to represent a cylinder, which is an extrusion of a circle into a +* 3rd dimension. + +* Inheritance: +* The Prism class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 17-DEC-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ------- */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* Prism structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +typedef struct AstPrism { + +/* Attributes inherited from the parent class. */ + AstRegion region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstRegion *region1; /* First component Region */ + AstRegion *region2; /* Second component Region */ + +} AstPrism; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstPrismVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +} AstPrismVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstPrismGlobals { + AstPrismVtab Class_Vtab; + int Class_Init; +} AstPrismGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitPrismGlobals_( AstPrismGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Prism) /* Check class membership */ +astPROTO_ISA(Prism) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstPrism *astPrism_( void *, void *, const char *, int *, ...); +#else +AstPrism *astPrismId_( void *, void *, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstPrism *astInitPrism_( void *, size_t, int, AstPrismVtab *, + const char *, AstRegion *, AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitPrismVtab_( AstPrismVtab *, const char *, int * ); + +/* Loader. */ +AstPrism *astLoadPrism_( void *, size_t, AstPrismVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +AstRegion *astConvertToPrism_( AstRegion *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckPrism(this) astINVOKE_CHECK(Prism,this,0) +#define astVerifyPrism(this) astINVOKE_CHECK(Prism,this,1) + +/* Test class membership. */ +#define astIsAPrism(this) astINVOKE_ISA(Prism,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astPrism astINVOKE(F,astPrism_) +#else +#define astPrism astINVOKE(F,astPrismId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitPrism(mem,size,init,vtab,name,reg1,reg2) \ +astINVOKE(O,astInitPrism_(mem,size,init,vtab,name,reg1,reg2,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitPrismVtab(vtab,name) astINVOKE(V,astInitPrismVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadPrism(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadPrism_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckPrism to validate Prism pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astConvertToPrism(this) astConvertToPrism_(this,STATUS_PTR) +#endif +#endif + + + + + diff --git a/proj.c b/proj.c new file mode 100644 index 0000000..8b92c21 --- /dev/null +++ b/proj.c @@ -0,0 +1,4840 @@ +/*============================================================================ +* +* WCSLIB - an implementation of the FITS WCS proposal. +* Copyright (C) 1995-2002, Mark Calabretta +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Library General Public License as published +* by the Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library +* General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street,Fifth Floor, Boston, MA 02110-1301, USA +* +* Correspondence concerning WCSLIB may be directed to: +* Internet email: mcalabre@atnf.csiro.au +* Postal address: Dr. Mark Calabretta, +* Australia Telescope National Facility, +* P.O. Box 76, +* Epping, NSW, 2121, +* AUSTRALIA +* +* +*============================================================================= +* +* This version of proj.c is based on the version in wcslib-2.9, but has +* been modified in the following ways by the Starlink project (e-mail: +* ussc@star.rl.ac.uk): +* - The copysign macro is now always defined within this file +* instead of only being defined if the COPYSIGN macro has previously +* been defined. +* - Sine values which are slightly larger than 1.0 are now treated +* as 1.0 in function astCYPrev. +* - The maximum number of projection parameters has been changed from +* 10 to 100. +* - The maximum number of projection parameters is given by the +* WCSLIB_MXPAR macro (defined in proj.h) instead of being hard-wired. +* - The names of all functions and structures have been chanegd to avoid +* clashes with wcslib. This involves adding "Ast" or "ast" at the +* front and changing the capitalisation. +* - Include string.h (for strcpy and strcmp prototypes). +* - Include stdlib.h (for abs prototype). +* - Comment out declarations of npcode and pcodes variables (they +* are not needed by AST) in order to avoid clash with similar names +* in other modules imported as part of other software systems (e.g. +* SkyCat). +* - astZPNfwd: Loop from prj->n to zero, not from MAXPAR to zero. +* - astZPNfwd: Only return "2" if prj->n is larger than 2. +* - Lots of variables are initialised to null values in order to +* avoid "use of uninitialised variable" messages from compilers which +* are not clever enough to work out that the uninitialised variable is +* not in fact ever used. +* - Use dynamic rather than static memory for the parameter arrays in +* the AstPrjPrm structure.Override astGetObjSize. This is to +* reduce the in-memory size of a WcsMap. +* - HPX and XPH projections included from a more recent version of WCSLIB, +* and modified to use scalar instead of vector positions +* - The expressions for xc in astHPXrev and phic in astHPXfwd have +* been conditioned differently to the WCSLIB code in order to improve +* accuracy of the floor function for arguments very slightly below an +* integer value. + +*============================================================================= +* +* C implementation of the spherical map projections recognized by the FITS +* "World Coordinate System" (WCS) convention. +* +* Summary of routines +* ------------------- +* Each projection is implemented via separate functions for the forward, +* *fwd(), and reverse, *rev(), transformation. +* +* Initialization routines, *set(), compute intermediate values from the +* projection parameters but need not be called explicitly - see the +* explanation of prj.flag below. +* +* astPRJset astPRJfwd astPRJrev Driver routines (see below). +* +* astAZPset astAZPfwd astAZPrev AZP: zenithal/azimuthal perspective +* astSZPset astSZPfwd astSZPrev SZP: slant zenithal perspective +* astTANset astTANfwd astTANrev TAN: gnomonic +* astSTGset astSTGfwd astSTGrev STG: stereographic +* astSINset astSINfwd astSINrev SIN: orthographic/synthesis +* astARCset astARCfwd astARCrev ARC: zenithal/azimuthal equidistant +* astZPNset astZPNfwd astZPNrev ZPN: zenithal/azimuthal polynomial +* astZEAset astZEAfwd astZEArev ZEA: zenithal/azimuthal equal area +* astAIRset astAIRfwd astAIRrev AIR: Airy +* astCYPset astCYPfwd astCYPrev CYP: cylindrical perspective +* astCEAset astCEAfwd astCEArev CEA: cylindrical equal area +* astCARset astCARfwd astCARrev CAR: Cartesian +* astMERset astMERfwd astMERrev MER: Mercator +* astSFLset astSFLfwd astSFLrev SFL: Sanson-Flamsteed +* astPARset astPARfwd astPARrev PAR: parabolic +* astMOLset astMOLfwd astMOLrev MOL: Mollweide +* astAITset astAITfwd astAITrev AIT: Hammer-Aitoff +* astCOPset astCOPfwd astCOPrev COP: conic perspective +* astCOEset astCOEfwd astCOErev COE: conic equal area +* astCODset astCODfwd astCODrev COD: conic equidistant +* astCOOset astCOOfwd astCOOrev COO: conic orthomorphic +* astBONset astBONfwd astBONrev BON: Bonne +* astPCOset astPCOfwd astPCOrev PCO: polyconic +* astTSCset astTSCfwd astTSCrev TSC: tangential spherical cube +* astCSCset astCSCfwd astCSCrev CSC: COBE quadrilateralized spherical cube +* astQSCset astQSCfwd astQSCrev QSC: quadrilateralized spherical cube +* astHPXset astHPXfwd astHPXrev HPX: HEALPix projection +* astXPHset astXPHfwd astXPHrev XPH: HEALPix polar, aka "butterfly" +* +* +* Driver routines; astPRJset(), astPRJfwd() & astPRJrev() +* ---------------------------------------------- +* A set of driver routines are available for use as a generic interface to +* the specific projection routines. The interfaces to astPRJfwd() and astPRJrev() +* are the same as those of the forward and reverse transformation routines +* for the specific projections (see below). +* +* The interface to astPRJset() differs slightly from that of the initialization +* routines for the specific projections and unlike them it must be invoked +* explicitly to use astPRJfwd() and astPRJrev(). +* +* Given: +* pcode[4] const char +* WCS projection code. +* +* Given and/or returned: +* prj AstPrjPrm* Projection parameters (see below). +* +* Function return value: +* int Error status +* 0: Success. +* +* +* Initialization routine; *set() +* ------------------------------ +* Initializes members of a AstPrjPrm data structure which hold intermediate +* values. Note that this routine need not be called directly; it will be +* invoked by astPRJfwd() and astPRJrev() if the "flag" structure member is +* anything other than a predefined magic value. +* +* Given and/or returned: +* prj AstPrjPrm* Projection parameters (see below). +* +* Function return value: +* int Error status +* 0: Success. +* 1: Invalid projection parameters. +* +* Forward transformation; *fwd() +* ----------------------------- +* Compute (x,y) coordinates in the plane of projection from native spherical +* coordinates (phi,theta). +* +* Given: +* phi, const double +* theta Longitude and latitude of the projected point in +* native spherical coordinates, in degrees. +* +* Given and returned: +* prj AstPrjPrm* Projection parameters (see below). +* +* Returned: +* x,y double* Projected coordinates. +* +* Function return value: +* int Error status +* 0: Success. +* 1: Invalid projection parameters. +* 2: Invalid value of (phi,theta). +* +* Reverse transformation; *rev() +* ----------------------------- +* Compute native spherical coordinates (phi,theta) from (x,y) coordinates in +* the plane of projection. +* +* Given: +* x,y const double +* Projected coordinates. +* +* Given and returned: +* prj AstPrjPrm* Projection parameters (see below). +* +* Returned: +* phi, double* Longitude and latitude of the projected point in +* theta native spherical coordinates, in degrees. +* +* Function return value: +* int Error status +* 0: Success. +* 1: Invalid projection parameters. +* 2: Invalid value of (x,y). +* 1: Invalid projection parameters. +* +* Projection parameters +* --------------------- +* The AstPrjPrm struct consists of the following: +* +* int flag +* This flag must be set to zero whenever any of p[] or r0 are set +* or changed. This signals the initialization routine to recompute +* intermediaries. flag may also be set to -1 to disable strict bounds +* checking for the AZP, SZP, TAN, SIN, ZPN, and COP projections. +* +* double r0 +* r0; The radius of the generating sphere for the projection, a linear +* scaling parameter. If this is zero, it will be reset to the default +* value of 180/pi (the value for FITS WCS). +* +* double p[] +* Contains the projection parameters associated with the +* longitude axis. +* +* The remaining members of the AstPrjPrm struct are maintained by the +* initialization routines and should not be modified. This is done for the +* sake of efficiency and to allow an arbitrary number of contexts to be +* maintained simultaneously. +* +* char code[4] +* Three-letter projection code. +* +* double phi0, theta0 +* Native longitude and latitude of the reference point, in degrees. +* +* double w[10] +* int n +* Intermediate values derived from the projection parameters. +* +* int (*astPRJfwd)() +* int (*astPRJrev)() +* Pointers to the forward and reverse projection routines. +* +* Usage of the p[] array as it applies to each projection is described in +* the prologue to each trio of projection routines. +* +* Argument checking +* ----------------- +* Forward routines: +* +* The values of phi and theta (the native longitude and latitude) +* normally lie in the range [-180,180] for phi, and [-90,90] for theta. +* However, all forward projections will accept any value of phi and will +* not normalize it. +* +* The forward projection routines do not explicitly check that theta lies +* within the range [-90,90]. They do check for any value of theta which +* produces an invalid argument to the projection equations (e.g. leading +* to division by zero). The forward routines for AZP, SZP, TAN, SIN, +* ZPN, and COP also return error 2 if (phi,theta) corresponds to the +* overlapped (far) side of the projection but also return the +* corresponding value of (x,y). This strict bounds checking may be +* relaxed by setting prj->flag to -1 (rather than 0) when these +* projections are initialized. +* +* Reverse routines: +* +* Error checking on the projected coordinates (x,y) is limited to that +* required to ascertain whether a solution exists. Where a solution does +* exist no check is made that the value of phi and theta obtained lie +* within the ranges [-180,180] for phi, and [-90,90] for theta. +* +* Accuracy +* -------- +* Closure to a precision of at least 1E-10 degree of longitude and latitude +* has been verified for typical projection parameters on the 1 degree grid +* of native longitude and latitude (to within 5 degrees of any latitude +* where the projection may diverge). +* +* Author: Mark Calabretta, Australia Telescope National Facility +* $Id$ +*===========================================================================*/ + +/* Set the name of the module we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. NB, this module is not a proper AST + class, but it defines this macro sanyway in order to get the protected + symbols defined in memory.h */ + +#include +#include +#include +#include "wcsmath.h" +#include "wcstrig.h" +#include "memory.h" +#include "proj.h" + +/* Following variables are not needed in AST and are commented out to + avoid name clashes with other software systems (e.g. SkyCat) which + defines them. + +int npcode = 28; +char pcodes[28][4] = + {"AZP", "SZP", "TAN", "STG", "SIN", "ARC", "ZPN", "ZEA", "AIR", "CYP", + "CEA", "CAR", "MER", "COP", "COE", "COD", "COO", "SFL", "PAR", "MOL", + "AIT", "BON", "PCO", "TSC", "CSC", "QSC", "HPX", "XPH"}; +*/ + +const int WCS__AZP = 101; +const int WCS__SZP = 102; +const int WCS__TAN = 103; +const int WCS__STG = 104; +const int WCS__SIN = 105; +const int WCS__ARC = 106; +const int WCS__ZPN = 107; +const int WCS__ZEA = 108; +const int WCS__AIR = 109; +const int WCS__CYP = 201; +const int WCS__CEA = 202; +const int WCS__CAR = 203; +const int WCS__MER = 204; +const int WCS__SFL = 301; +const int WCS__PAR = 302; +const int WCS__MOL = 303; +const int WCS__AIT = 401; +const int WCS__COP = 501; +const int WCS__COE = 502; +const int WCS__COD = 503; +const int WCS__COO = 504; +const int WCS__BON = 601; +const int WCS__PCO = 602; +const int WCS__TSC = 701; +const int WCS__CSC = 702; +const int WCS__QSC = 703; +const int WCS__HPX = 801; +const int WCS__XPH = 802; + +/* Map error number to error message for each function. */ +const char *astPRJset_errmsg[] = { + 0, + "Invalid projection parameters"}; + +const char *astPRJfwd_errmsg[] = { + 0, + "Invalid projection parameters", + "Invalid value of (phi,theta)"}; + +const char *astPRJrev_errmsg[] = { + 0, + "Invalid projection parameters", + "Invalid value of (x,y)"}; + + +#define copysign(X, Y) ((Y) < 0.0 ? -fabs(X) : fabs(X)) +#define icopysign(X, Y) ((Y) < 0.0 ? -abs(X) : abs(X)) + + + +/*==========================================================================*/ + +int astPRJset(pcode, prj) + +const char pcode[4]; +struct AstPrjPrm *prj; + +{ + /* Set pointers to the forward and reverse projection routines. */ + if (strcmp(pcode, "AZP") == 0) { + astAZPset(prj); + } else if (strcmp(pcode, "SZP") == 0) { + astSZPset(prj); + } else if (strcmp(pcode, "TAN") == 0) { + astTANset(prj); + } else if (strcmp(pcode, "STG") == 0) { + astSTGset(prj); + } else if (strcmp(pcode, "SIN") == 0) { + astSINset(prj); + } else if (strcmp(pcode, "ARC") == 0) { + astARCset(prj); + } else if (strcmp(pcode, "ZPN") == 0) { + astZPNset(prj); + } else if (strcmp(pcode, "ZEA") == 0) { + astZEAset(prj); + } else if (strcmp(pcode, "AIR") == 0) { + astAIRset(prj); + } else if (strcmp(pcode, "CYP") == 0) { + astCYPset(prj); + } else if (strcmp(pcode, "CEA") == 0) { + astCEAset(prj); + } else if (strcmp(pcode, "CAR") == 0) { + astCARset(prj); + } else if (strcmp(pcode, "MER") == 0) { + astMERset(prj); + } else if (strcmp(pcode, "SFL") == 0) { + astSFLset(prj); + } else if (strcmp(pcode, "PAR") == 0) { + astPARset(prj); + } else if (strcmp(pcode, "MOL") == 0) { + astMOLset(prj); + } else if (strcmp(pcode, "AIT") == 0) { + astAITset(prj); + } else if (strcmp(pcode, "COP") == 0) { + astCOPset(prj); + } else if (strcmp(pcode, "COE") == 0) { + astCOEset(prj); + } else if (strcmp(pcode, "COD") == 0) { + astCODset(prj); + } else if (strcmp(pcode, "COO") == 0) { + astCOOset(prj); + } else if (strcmp(pcode, "BON") == 0) { + astBONset(prj); + } else if (strcmp(pcode, "PCO") == 0) { + astPCOset(prj); + } else if (strcmp(pcode, "TSC") == 0) { + astTSCset(prj); + } else if (strcmp(pcode, "CSC") == 0) { + astCSCset(prj); + } else if (strcmp(pcode, "QSC") == 0) { + astQSCset(prj); + } else if (strcmp(pcode, "HPX") == 0) { + astHPXset(prj); + } else if (strcmp(pcode, "XPH") == 0) { + astXPHset(prj); + } else { + /* Unrecognized projection code. */ + return 1; + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astPRJfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + return prj->astPRJfwd(phi, theta, prj, x, y); +} + +/*--------------------------------------------------------------------------*/ + +int astPRJrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + return prj->astPRJrev(x, y, prj, phi, theta); +} + +/*============================================================================ +* AZP: zenithal/azimuthal perspective projection. +* +* Given: +* prj->p[1] Distance parameter, mu in units of r0. +* prj->p[2] Tilt angle, gamma in degrees. +* +* Given and/or returned: +* prj->flag AZP, or -AZP if prj->flag is given < 0. +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "AZP" +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->w[0] r0*(mu+1) +* prj->w[1] tan(gamma) +* prj->w[2] sec(gamma) +* prj->w[3] cos(gamma) +* prj->w[4] sin(gamma) +* prj->w[5] asin(-1/mu) for |mu| >= 1, -90 otherwise +* prj->w[6] mu*cos(gamma) +* prj->w[7] 1 if |mu*cos(gamma)| < 1, 0 otherwise +* prj->astPRJfwd Pointer to astAZPfwd(). +* prj->astPRJrev Pointer to astAZPrev(). +*===========================================================================*/ + +int astAZPset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "AZP"); + prj->flag = icopysign(WCS__AZP, prj->flag); + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->w[0] = prj->r0*(prj->p[1] + 1.0); + if (prj->w[0] == 0.0) { + return 1; + } + + prj->w[3] = astCosd(prj->p[2]); + if (prj->w[3] == 0.0) { + return 1; + } + + prj->w[2] = 1.0/prj->w[3]; + prj->w[4] = astSind(prj->p[2]); + prj->w[1] = prj->w[4] / prj->w[3]; + + if (fabs(prj->p[1]) > 1.0) { + prj->w[5] = astASind(-1.0/prj->p[1]); + } else { + prj->w[5] = -90.0; + } + + prj->w[6] = prj->p[1] * prj->w[3]; + prj->w[7] = (fabs(prj->w[6]) < 1.0) ? 1.0 : 0.0; + + prj->astPRJfwd = astAZPfwd; + prj->astPRJrev = astAZPrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astAZPfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, b, cphi, cthe, r, s, t; + + if (abs(prj->flag) != WCS__AZP) { + if (astAZPset(prj)) return 1; + } + + cphi = astCosd(phi); + cthe = astCosd(theta); + + s = prj->w[1]*cphi; + t = (prj->p[1] + astSind(theta)) + cthe*s; + if (t == 0.0) { + return 2; + } + + r = prj->w[0]*cthe/t; + *x = r*astSind(phi); + *y = -r*cphi*prj->w[2]; + + /* Bounds checking. */ + if (prj->flag > 0) { + /* Overlap. */ + if (theta < prj->w[5]) { + return 2; + } + + /* Divergence. */ + if (prj->w[7] > 0.0) { + t = prj->p[1] / sqrt(1.0 + s*s); + + if (fabs(t) <= 1.0) { + s = astATand(-s); + t = astASind(t); + a = s - t; + b = s + t + 180.0; + + if (a > 90.0) a -= 360.0; + if (b > 90.0) b -= 360.0; + + if (theta < ((a > b) ? a : b)) { + return 2; + } + } + } + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astAZPrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double a, b, r, s, t, ycosg; + const double tol = 1.0e-13; + + if (abs(prj->flag) != WCS__AZP) { + if (astAZPset(prj)) return 1; + } + + ycosg = y*prj->w[3]; + + r = sqrt(x*x + ycosg*ycosg); + if (r == 0.0) { + *phi = 0.0; + *theta = 90.0; + } else { + *phi = astATan2d(x, -ycosg); + + s = r / (prj->w[0] + y*prj->w[4]); + t = s*prj->p[1]/sqrt(s*s + 1.0); + + s = astATan2d(1.0, s); + + if (fabs(t) > 1.0) { + t = copysign(90.0,t); + if (fabs(t) > 1.0+tol) { + return 2; + } + } else { + t = astASind(t); + } + + a = s - t; + b = s + t + 180.0; + + if (a > 90.0) a -= 360.0; + if (b > 90.0) b -= 360.0; + + *theta = (a > b) ? a : b; + } + + return 0; +} + +/*============================================================================ +* SZP: slant zenithal perspective projection. +* +* Given: +* prj->p[1] Distance of the point of projection from the centre of the +* generating sphere, mu in units of r0. +* prj->p[2] Native longitude, phi_c, and ... +* prj->p[3] Native latitude, theta_c, on the planewards side of the +* intersection of the line through the point of projection +* and the centre of the generating sphere, phi_c in degrees. +* +* Given and/or returned: +* prj->flag SZP, or -SZP if prj->flag is given < 0. +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "SZP" +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->w[0] 1/r0 +* prj->w[1] xp = -mu*cos(theta_c)*sin(phi_c) +* prj->w[2] yp = mu*cos(theta_c)*cos(phi_c) +* prj->w[3] zp = mu*sin(theta_c) + 1 +* prj->w[4] r0*xp +* prj->w[5] r0*yp +* prj->w[6] r0*zp +* prj->w[7] (zp - 1)^2 +* prj->w[8] asin(1-zp) if |1 - zp| < 1, -90 otherwise +* prj->astPRJfwd Pointer to astSZPfwd(). +* prj->astPRJrev Pointer to astSZPrev(). +*===========================================================================*/ + +int astSZPset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "SZP"); + prj->flag = icopysign(WCS__SZP, prj->flag); + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->w[0] = 1.0/prj->r0; + + prj->w[3] = prj->p[1] * astSind(prj->p[3]) + 1.0; + if (prj->w[3] == 0.0) { + return 1; + } + + prj->w[1] = -prj->p[1] * astCosd(prj->p[3]) * astSind(prj->p[2]); + prj->w[2] = prj->p[1] * astCosd(prj->p[3]) * astCosd(prj->p[2]); + prj->w[4] = prj->r0 * prj->w[1]; + prj->w[5] = prj->r0 * prj->w[2]; + prj->w[6] = prj->r0 * prj->w[3]; + prj->w[7] = (prj->w[3] - 1.0) * prj->w[3] - 1.0; + + if (fabs(prj->w[3] - 1.0) < 1.0) { + prj->w[8] = astASind(1.0 - prj->w[3]); + } else { + prj->w[8] = -90.0; + } + + prj->astPRJfwd = astSZPfwd; + prj->astPRJrev = astSZPrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSZPfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, b, cphi, cthe, s, sphi, t; + + if (abs(prj->flag) != WCS__SZP) { + if (astSZPset(prj)) return 1; + } + + cphi = astCosd(phi); + sphi = astSind(phi); + cthe = astCosd(theta); + s = 1.0 - astSind(theta); + + t = prj->w[3] - s; + if (t == 0.0) { + return 2; + } + + *x = (prj->w[6]*cthe*sphi - prj->w[4]*s)/t; + *y = -(prj->w[6]*cthe*cphi + prj->w[5]*s)/t; + + /* Bounds checking. */ + if (prj->flag > 0) { + /* Divergence. */ + if (theta < prj->w[8]) { + return 2; + } + + /* Overlap. */ + if (fabs(prj->p[1]) > 1.0) { + s = prj->w[1]*sphi - prj->w[2]*cphi; + t = 1.0/sqrt(prj->w[7] + s*s); + + if (fabs(t) <= 1.0) { + s = astATan2d(s, prj->w[3] - 1.0); + t = astASind(t); + a = s - t; + b = s + t + 180.0; + + if (a > 90.0) a -= 360.0; + if (b > 90.0) b -= 360.0; + + if (theta < ((a > b) ? a : b)) { + return 2; + } + } + } + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSZPrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double a, b, c, d, r2, sth1, sth2, sthe, sxy, t, x1, xp, y1, yp, z; + const double tol = 1.0e-13; + + if (abs(prj->flag) != WCS__SZP) { + if (astSZPset(prj)) return 1; + } + + xp = x*prj->w[0]; + yp = y*prj->w[0]; + r2 = xp*xp + yp*yp; + + x1 = (xp - prj->w[1])/prj->w[3]; + y1 = (yp - prj->w[2])/prj->w[3]; + sxy = xp*x1 + yp*y1; + + if (r2 < 1.0e-10) { + /* Use small angle formula. */ + z = r2/2.0; + *theta = 90.0 - R2D*sqrt(r2/(1.0 + sxy)); + + } else { + t = x1*x1 + y1*y1; + a = t + 1.0; + b = sxy - t; + c = r2 - sxy - sxy + t - 1.0; + d = b*b - a*c; + + /* Check for a solution. */ + if (d < 0.0) { + return 2; + } + d = sqrt(d); + + /* Choose solution closest to pole. */ + sth1 = (-b + d)/a; + sth2 = (-b - d)/a; + sthe = (sth1 > sth2) ? sth1 : sth2; + if (sthe > 1.0) { + if (sthe-1.0 < tol) { + sthe = 1.0; + } else { + sthe = (sth1 < sth2) ? sth1 : sth2; + } + } + + if (sthe < -1.0) { + if (sthe+1.0 > -tol) { + sthe = -1.0; + } + } + + if (sthe > 1.0 || sthe < -1.0) { + return 2; + } + + *theta = astASind(sthe); + + z = 1.0 - sthe; + } + + *phi = astATan2d(xp - x1*z, -(yp - y1*z)); + + return 0; +} + +/*============================================================================ +* TAN: gnomonic projection. +* +* Given and/or returned: +* prj->flag TAN, or -TAN if prj->flag is given < 0. +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "TAN" +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->astPRJfwd Pointer to astTANfwd(). +* prj->astPRJrev Pointer to astTANrev(). +*===========================================================================*/ + +int astTANset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "TAN"); + prj->flag = icopysign(WCS__TAN, prj->flag); + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->astPRJfwd = astTANfwd; + prj->astPRJrev = astTANrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astTANfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double r, s; + + if (abs(prj->flag) != WCS__TAN) { + if(astTANset(prj)) return 1; + } + + s = astSind(theta); + if (s == 0.0) { + return 2; + } + + r = prj->r0*astCosd(theta)/s; + *x = r*astSind(phi); + *y = -r*astCosd(phi); + + if (prj->flag > 0 && s < 0.0) { + return 2; + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astTANrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double r; + + if (abs(prj->flag) != WCS__TAN) { + if (astTANset(prj)) return 1; + } + + r = sqrt(x*x + y*y); + if (r == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(x, -y); + } + *theta = astATan2d(prj->r0, r); + + return 0; +} + +/*============================================================================ +* STG: stereographic projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "STG" +* prj->flag STG +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->w[0] 2*r0 +* prj->w[1] 1/(2*r0) +* prj->astPRJfwd Pointer to astSTGfwd(). +* prj->astPRJrev Pointer to astSTGrev(). +*===========================================================================*/ + +int astSTGset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "STG"); + prj->flag = WCS__STG; + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 360.0/PI; + prj->w[1] = PI/360.0; + } else { + prj->w[0] = 2.0*prj->r0; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astSTGfwd; + prj->astPRJrev = astSTGrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSTGfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double r, s; + + if (prj->flag != WCS__STG) { + if (astSTGset(prj)) return 1; + } + + s = 1.0 + astSind(theta); + if (s == 0.0) { + return 2; + } + + r = prj->w[0]*astCosd(theta)/s; + *x = r*astSind(phi); + *y = -r*astCosd(phi); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSTGrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double r; + + if (prj->flag != WCS__STG) { + if (astSTGset(prj)) return 1; + } + + r = sqrt(x*x + y*y); + if (r == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(x, -y); + } + *theta = 90.0 - 2.0*astATand(r*prj->w[1]); + + return 0; +} + +/*============================================================================ +* SIN: orthographic/synthesis projection. +* +* Given: +* prj->p[1:2] Obliqueness parameters, xi and eta. +* +* Given and/or returned: +* prj->flag SIN, or -SIN if prj->flag is given < 0. +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "SIN" +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->w[0] 1/r0 +* prj->w[1] xi**2 + eta**2 +* prj->w[2] xi**2 + eta**2 + 1 +* prj->w[3] xi**2 + eta**2 - 1 +* prj->astPRJfwd Pointer to astSINfwd(). +* prj->astPRJrev Pointer to astSINrev(). +*===========================================================================*/ + +int astSINset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "SIN"); + prj->flag = icopysign(WCS__SIN, prj->flag); + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->w[0] = 1.0/prj->r0; + prj->w[1] = prj->p[1]*prj->p[1] + prj->p[2]*prj->p[2]; + prj->w[2] = prj->w[1] + 1.0; + prj->w[3] = prj->w[1] - 1.0; + + prj->astPRJfwd = astSINfwd; + prj->astPRJrev = astSINrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSINfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double cphi, cthe, sphi, t, z; + + if (abs(prj->flag) != WCS__SIN) { + if (astSINset(prj)) return 1; + } + + t = (90.0 - fabs(theta))*D2R; + if (t < 1.0e-5) { + if (theta > 0.0) { + z = t*t/2.0; + } else { + z = 2.0 - t*t/2.0; + } + cthe = t; + } else { + z = 1.0 - astSind(theta); + cthe = astCosd(theta); + } + + cphi = astCosd(phi); + sphi = astSind(phi); + *x = prj->r0*(cthe*sphi + prj->p[1]*z); + *y = -prj->r0*(cthe*cphi - prj->p[2]*z); + + /* Validate this solution. */ + if (prj->flag > 0) { + if (prj->w[1] == 0.0) { + /* Orthographic projection. */ + if (theta < 0.0) { + return 2; + } + } else { + /* "Synthesis" projection. */ + t = -astATand(prj->p[1]*sphi - prj->p[2]*cphi); + if (theta < t) { + return 2; + } + } + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSINrev (x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + const double tol = 1.0e-13; + double a, b, c, d, r2, sth1, sth2, sthe, sxy, x0, x1, xp, y0, y1, yp, z; + + if (abs(prj->flag) != WCS__SIN) { + if (astSINset(prj)) return 1; + } + + /* Compute intermediaries. */ + x0 = x*prj->w[0]; + y0 = y*prj->w[0]; + r2 = x0*x0 + y0*y0; + + if (prj->w[1] == 0.0) { + /* Orthographic projection. */ + if (r2 != 0.0) { + *phi = astATan2d(x0, -y0); + } else { + *phi = 0.0; + } + + if (r2 < 0.5) { + *theta = astACosd(sqrt(r2)); + } else if (r2 <= 1.0) { + *theta = astASind(sqrt(1.0 - r2)); + } else { + return 2; + } + + } else { + /* "Synthesis" projection. */ + x1 = prj->p[1]; + y1 = prj->p[2]; + sxy = x0*x1 + y0*y1; + + if (r2 < 1.0e-10) { + /* Use small angle formula. */ + z = r2/2.0; + *theta = 90.0 - R2D*sqrt(r2/(1.0 + sxy)); + + } else { + a = prj->w[2]; + b = sxy - prj->w[1]; + c = r2 - sxy - sxy + prj->w[3]; + d = b*b - a*c; + + /* Check for a solution. */ + if (d < 0.0) { + return 2; + } + d = sqrt(d); + + /* Choose solution closest to pole. */ + sth1 = (-b + d)/a; + sth2 = (-b - d)/a; + sthe = (sth1 > sth2) ? sth1 : sth2; + if (sthe > 1.0) { + if (sthe-1.0 < tol) { + sthe = 1.0; + } else { + sthe = (sth1 < sth2) ? sth1 : sth2; + } + } + + if (sthe < -1.0) { + if (sthe+1.0 > -tol) { + sthe = -1.0; + } + } + + if (sthe > 1.0 || sthe < -1.0) { + return 2; + } + + *theta = astASind(sthe); + z = 1.0 - sthe; + } + + xp = -y0 + prj->p[2]*z; + yp = x0 - prj->p[1]*z; + if (xp == 0.0 && yp == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(yp,xp); + } + } + + return 0; +} + +/*============================================================================ +* ARC: zenithal/azimuthal equidistant projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "ARC" +* prj->flag ARC +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->w[0] r0*(pi/180) +* prj->w[1] (180/pi)/r0 +* prj->astPRJfwd Pointer to astARCfwd(). +* prj->astPRJrev Pointer to astARCrev(). +*===========================================================================*/ + +int astARCset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "ARC"); + prj->flag = WCS__ARC; + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astARCfwd; + prj->astPRJrev = astARCrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astARCfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double r; + + if (prj->flag != WCS__ARC) { + if (astARCset(prj)) return 1; + } + + r = prj->w[0]*(90.0 - theta); + *x = r*astSind(phi); + *y = -r*astCosd(phi); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astARCrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double r; + + if (prj->flag != WCS__ARC) { + if (astARCset(prj)) return 1; + } + + r = sqrt(x*x + y*y); + if (r == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(x, -y); + } + *theta = 90.0 - r*prj->w[1]; + + return 0; +} + +/*============================================================================ +* ZPN: zenithal/azimuthal polynomial projection. +* +* Given: +* prj->p[0:WCSLIB_MXPAR-1] Polynomial coefficients. +* +* Given and/or returned: +* prj->flag ZPN, or -ZPN if prj->flag is given < 0. +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "ZPN" +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->n Degree of the polynomial, N. +* prj->w[0] Co-latitude of the first point of inflection (N > 2). +* prj->w[1] Radius of the first point of inflection (N > 2). +* prj->astPRJfwd Pointer to astZPNfwd(). +* prj->astPRJrev Pointer to astZPNrev(). +*===========================================================================*/ + +int astZPNset(prj) + +struct AstPrjPrm *prj; + +{ + int i, j, k, plen; + double d, d1, d2, r, zd, zd1, zd2; + const double tol = 1.0e-13; + + strcpy(prj->code, "ZPN"); + prj->flag = icopysign(WCS__ZPN, prj->flag); + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + /* Find the highest non-zero coefficient. */ + plen = astSizeOf( prj->p )/sizeof( double ); + for (k = plen-1; k >= 0 && prj->p[k] == 0.0; k--); + if (k < 0) return 1; + + prj->n = k; + + if (k >= 3) { + /* Find the point of inflection closest to the pole. */ + zd1 = 0.0; + d1 = prj->p[1]; + if (d1 <= 0.0) { + return 1; + } + + /* Find the point where the derivative first goes negative. */ + for (i = 0; i < 180; i++) { + zd2 = i*D2R; + d2 = 0.0; + for (j = k; j > 0; j--) { + d2 = d2*zd2 + j*prj->p[j]; + } + + if (d2 <= 0.0) break; + zd1 = zd2; + d1 = d2; + } + + if (i == 180) { + /* No negative derivative -> no point of inflection. */ + zd = PI; + } else { + /* Find where the derivative is zero. */ + for (i = 1; i <= 10; i++) { + zd = zd1 - d1*(zd2-zd1)/(d2-d1); + + d = 0.0; + for (j = k; j > 0; j--) { + d = d*zd + j*prj->p[j]; + } + + if (fabs(d) < tol) break; + + if (d < 0.0) { + zd2 = zd; + d2 = d; + } else { + zd1 = zd; + d1 = d; + } + } + } + + r = 0.0; + for (j = k; j >= 0; j--) { + r = r*zd + prj->p[j]; + } + prj->w[0] = zd; + prj->w[1] = r; + } + + prj->astPRJfwd = astZPNfwd; + prj->astPRJrev = astZPNrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astZPNfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + int j; + double r, s; + + if (abs(prj->flag) != WCS__ZPN) { + if (astZPNset(prj)) return 1; + } + + s = (90.0 - theta)*D2R; + + r = 0.0; + for (j = prj->n; j >= 0; j--) { + r = r*s + prj->p[j]; + } + r = prj->r0*r; + + *x = r*astSind(phi); + *y = -r*astCosd(phi); + + if (prj->flag > 0 && s > prj->w[0] && prj->n > 2 ) { + return 2; + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astZPNrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + int i, j, k; + double a, b, c, d, lambda, r, r1, r2, rt, zd, zd1, zd2; + const double tol = 1.0e-13; + + if (abs(prj->flag) != WCS__ZPN) { + if (astZPNset(prj)) return 1; + } + + k = prj->n; + + r = sqrt(x*x + y*y)/prj->r0; + + if (k < 1) { + /* Constant - no solution. */ + return 1; + } else if (k == 1) { + /* Linear. */ + zd = (r - prj->p[0])/prj->p[1]; + } else if (k == 2) { + /* Quadratic. */ + a = prj->p[2]; + b = prj->p[1]; + c = prj->p[0] - r; + + d = b*b - 4.0*a*c; + if (d < 0.0) { + return 2; + } + d = sqrt(d); + + /* Choose solution closest to pole. */ + zd1 = (-b + d)/(2.0*a); + zd2 = (-b - d)/(2.0*a); + zd = (zd1zd2) ? zd1 : zd2; + if (zd < 0.0) { + if (zd < -tol) { + return 2; + } + zd = 0.0; + } else if (zd > PI) { + if (zd > PI+tol) { + return 2; + } + zd = PI; + } + } else { + /* Higher order - solve iteratively. */ + zd1 = 0.0; + r1 = prj->p[0]; + zd2 = prj->w[0]; + r2 = prj->w[1]; + + if (r < r1) { + if (r < r1-tol) { + return 2; + } + zd = zd1; + } else if (r > r2) { + if (r > r2+tol) { + return 2; + } + zd = zd2; + } else { + /* Disect the interval. */ + for (j = 0; j < 100; j++) { + lambda = (r2 - r)/(r2 - r1); + if (lambda < 0.1) { + lambda = 0.1; + } else if (lambda > 0.9) { + lambda = 0.9; + } + + zd = zd2 - lambda*(zd2 - zd1); + + rt = 0.0; + for (i = k; i >= 0; i--) { + rt = (rt * zd) + prj->p[i]; + } + + if (rt < r) { + if (r-rt < tol) break; + r1 = rt; + zd1 = zd; + } else { + if (rt-r < tol) break; + r2 = rt; + zd2 = zd; + } + + if (fabs(zd2-zd1) < tol) break; + } + } + } + + if (r == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(x, -y); + } + *theta = 90.0 - zd*R2D; + + return 0; +} + +/*============================================================================ +* ZEA: zenithal/azimuthal equal area projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "ZEA" +* prj->flag ZEA +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->w[0] 2*r0 +* prj->w[1] 1/(2*r0) +* prj->astPRJfwd Pointer to astZEAfwd(). +* prj->astPRJrev Pointer to astZEArev(). +*===========================================================================*/ + +int astZEAset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "ZEA"); + prj->flag = WCS__ZEA; + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 360.0/PI; + prj->w[1] = PI/360.0; + } else { + prj->w[0] = 2.0*prj->r0; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astZEAfwd; + prj->astPRJrev = astZEArev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astZEAfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double r; + + if (prj->flag != WCS__ZEA) { + if (astZEAset(prj)) return 1; + } + + r = prj->w[0]*astSind((90.0 - theta)/2.0); + *x = r*astSind(phi); + *y = -r*astCosd(phi); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astZEArev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double r, s; + const double tol = 1.0e-12; + + if (prj->flag != WCS__ZEA) { + if (astZEAset(prj)) return 1; + } + + r = sqrt(x*x + y*y); + if (r == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(x, -y); + } + + s = r*prj->w[1]; + if (fabs(s) > 1.0) { + if (fabs(r - prj->w[0]) < tol) { + *theta = -90.0; + } else { + return 2; + } + } else { + *theta = 90.0 - 2.0*astASind(s); + } + + return 0; +} + +/*============================================================================ +* AIR: Airy's projection. +* +* Given: +* prj->p[1] Latitude theta_b within which the error is minimized, in +* degrees. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "AIR" +* prj->flag AIR +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->w[0] 2*r0 +* prj->w[1] ln(cos(xi_b))/tan(xi_b)**2, where xi_b = (90-theta_b)/2 +* prj->w[2] 1/2 - prj->w[1] +* prj->w[3] 2*r0*prj->w[2] +* prj->w[4] tol, cutoff for using small angle approximation, in +* radians. +* prj->w[5] prj->w[2]*tol +* prj->w[6] (180/pi)/prj->w[2] +* prj->astPRJfwd Pointer to astAIRfwd(). +* prj->astPRJrev Pointer to astAIRrev(). +*===========================================================================*/ + +int astAIRset(prj) + +struct AstPrjPrm *prj; + +{ + const double tol = 1.0e-4; + double cxi; + + strcpy(prj->code, "AIR"); + prj->flag = WCS__AIR; + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->w[0] = 2.0*prj->r0; + if (prj->p[1] == 90.0) { + prj->w[1] = -0.5; + prj->w[2] = 1.0; + } else if (prj->p[1] > -90.0) { + cxi = astCosd((90.0 - prj->p[1])/2.0); + prj->w[1] = log(cxi)*(cxi*cxi)/(1.0-cxi*cxi); + prj->w[2] = 0.5 - prj->w[1]; + } else { + return 1; + } + + prj->w[3] = prj->w[0] * prj->w[2]; + prj->w[4] = tol; + prj->w[5] = prj->w[2]*tol; + prj->w[6] = R2D/prj->w[2]; + + prj->astPRJfwd = astAIRfwd; + prj->astPRJrev = astAIRrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astAIRfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double cxi, r, txi, xi; + + if (prj->flag != WCS__AIR) { + if (astAIRset(prj)) return 1; + } + + if (theta == 90.0) { + r = 0.0; + } else if (theta > -90.0) { + xi = D2R*(90.0 - theta)/2.0; + if (xi < prj->w[4]) { + r = xi*prj->w[3]; + } else { + cxi = astCosd((90.0 - theta)/2.0); + txi = sqrt(1.0-cxi*cxi)/cxi; + r = -prj->w[0]*(log(cxi)/txi + prj->w[1]*txi); + } + } else { + return 2; + } + + *x = r*astSind(phi); + *y = -r*astCosd(phi); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astAIRrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + int j; + double cxi, lambda, r, r1, r2, rt, txi, x1, x2, xi; + const double tol = 1.0e-12; + + if (prj->flag != WCS__AIR) { + if (astAIRset(prj)) return 1; + } + + r = sqrt(x*x + y*y)/prj->w[0]; + + if (r == 0.0) { + xi = 0.0; + } else if (r < prj->w[5]) { + xi = r*prj->w[6]; + } else { + /* Find a solution interval. */ + x1 = 1.0; + r1 = 0.0; + for (j = 0; j < 30; j++) { + x2 = x1/2.0; + txi = sqrt(1.0-x2*x2)/x2; + r2 = -(log(x2)/txi + prj->w[1]*txi); + + if (r2 >= r) break; + x1 = x2; + r1 = r2; + } + if (j == 30) return 2; + + for (j = 0; j < 100; j++) { + /* Weighted division of the interval. */ + lambda = (r2-r)/(r2-r1); + if (lambda < 0.1) { + lambda = 0.1; + } else if (lambda > 0.9) { + lambda = 0.9; + } + cxi = x2 - lambda*(x2-x1); + + txi = sqrt(1.0-cxi*cxi)/cxi; + rt = -(log(cxi)/txi + prj->w[1]*txi); + + if (rt < r) { + if (r-rt < tol) break; + r1 = rt; + x1 = cxi; + } else { + if (rt-r < tol) break; + r2 = rt; + x2 = cxi; + } + } + if (j == 100) return 2; + + xi = astACosd(cxi); + } + + if (r == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(x, -y); + } + *theta = 90.0 - 2.0*xi; + + return 0; +} + +/*============================================================================ +* CYP: cylindrical perspective projection. +* +* Given: +* prj->p[1] Distance of point of projection from the centre of the +* generating sphere, mu, in units of r0. +* prj->p[2] Radius of the cylinder of projection, lambda, in units of +* r0. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "CYP" +* prj->flag CYP +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*lambda*(pi/180) +* prj->w[1] (180/pi)/(r0*lambda) +* prj->w[2] r0*(mu + lambda) +* prj->w[3] 1/(r0*(mu + lambda)) +* prj->astPRJfwd Pointer to astCYPfwd(). +* prj->astPRJrev Pointer to astCYPrev(). +*===========================================================================*/ + +int astCYPset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "CYP"); + prj->flag = WCS__CYP; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + + prj->w[0] = prj->p[2]; + if (prj->w[0] == 0.0) { + return 1; + } + + prj->w[1] = 1.0/prj->w[0]; + + prj->w[2] = R2D*(prj->p[1] + prj->p[2]); + if (prj->w[2] == 0.0) { + return 1; + } + + prj->w[3] = 1.0/prj->w[2]; + } else { + prj->w[0] = prj->r0*prj->p[2]*D2R; + if (prj->w[0] == 0.0) { + return 1; + } + + prj->w[1] = 1.0/prj->w[0]; + + prj->w[2] = prj->r0*(prj->p[1] + prj->p[2]); + if (prj->w[2] == 0.0) { + return 1; + } + + prj->w[3] = 1.0/prj->w[2]; + } + + prj->astPRJfwd = astCYPfwd; + prj->astPRJrev = astCYPrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCYPfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double s; + + if (prj->flag != WCS__CYP) { + if (astCYPset(prj)) return 1; + } + + s = prj->p[1] + astCosd(theta); + if (s == 0.0) { + return 2; + } + + *x = prj->w[0]*phi; + *y = prj->w[2]*astSind(theta)/s; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCYPrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double eta; + double a; + const double tol = 1.0e-13; + + if (prj->flag != WCS__CYP) { + if (astCYPset(prj)) return 1; + } + + *phi = x*prj->w[1]; + eta = y*prj->w[3]; + + a = eta*prj->p[1]/sqrt(eta*eta+1.0); + if( fabs( a ) < 1.0 ) { + *theta = astATan2d(eta,1.0) + astASind( a ); + + } else if( fabs( a ) < 1.0 + tol ) { + if( a > 0.0 ){ + *theta = astATan2d(eta,1.0) + 90.0; + } else { + *theta = astATan2d(eta,1.0) - 90.0; + } + + } else { + return 2; + } + + return 0; +} + +/*============================================================================ +* CEA: cylindrical equal area projection. +* +* Given: +* prj->p[1] Square of the cosine of the latitude at which the +* projection is conformal, lambda. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "CEA" +* prj->flag CEA +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/180) +* prj->w[1] (180/pi)/r0 +* prj->w[2] r0/lambda +* prj->w[3] lambda/r0 +* prj->astPRJfwd Pointer to astCEAfwd(). +* prj->astPRJrev Pointer to astCEArev(). +*===========================================================================*/ + +int astCEAset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "CEA"); + prj->flag = WCS__CEA; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + if (prj->p[1] <= 0.0 || prj->p[1] > 1.0) { + return 1; + } + prj->w[2] = prj->r0/prj->p[1]; + prj->w[3] = prj->p[1]/prj->r0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = R2D/prj->r0; + if (prj->p[1] <= 0.0 || prj->p[1] > 1.0) { + return 1; + } + prj->w[2] = prj->r0/prj->p[1]; + prj->w[3] = prj->p[1]/prj->r0; + } + + prj->astPRJfwd = astCEAfwd; + prj->astPRJrev = astCEArev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCEAfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + if (prj->flag != WCS__CEA) { + if (astCEAset(prj)) return 1; + } + + *x = prj->w[0]*phi; + *y = prj->w[2]*astSind(theta); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCEArev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double s; + const double tol = 1.0e-13; + + if (prj->flag != WCS__CEA) { + if (astCEAset(prj)) return 1; + } + + s = y*prj->w[3]; + if (fabs(s) > 1.0) { + if (fabs(s) > 1.0+tol) { + return 2; + } + s = copysign(1.0,s); + } + + *phi = x*prj->w[1]; + *theta = astASind(s); + + return 0; +} + +/*============================================================================ +* CAR: Cartesian projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "CAR" +* prj->flag CAR +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/180) +* prj->w[1] (180/pi)/r0 +* prj->astPRJfwd Pointer to astCARfwd(). +* prj->astPRJrev Pointer to astCARrev(). +*===========================================================================*/ + +int astCARset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "CAR"); + prj->flag = WCS__CAR; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astCARfwd; + prj->astPRJrev = astCARrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCARfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + if (prj->flag != WCS__CAR) { + if (astCARset(prj)) return 1; + } + + *x = prj->w[0]*phi; + *y = prj->w[0]*theta; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCARrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + if (prj->flag != WCS__CAR) { + if (astCARset(prj)) return 1; + } + + *phi = prj->w[1]*x; + *theta = prj->w[1]*y; + + return 0; +} + +/*============================================================================ +* MER: Mercator's projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "MER" +* prj->flag MER +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/180) +* prj->w[1] (180/pi)/r0 +* prj->astPRJfwd Pointer to astMERfwd(). +* prj->astPRJrev Pointer to astMERrev(). +*===========================================================================*/ + +int astMERset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "MER"); + prj->flag = WCS__MER; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astMERfwd; + prj->astPRJrev = astMERrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astMERfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + if (prj->flag != WCS__MER) { + if (astMERset(prj)) return 1; + } + + if (theta <= -90.0 || theta >= 90.0) { + return 2; + } + + *x = prj->w[0]*phi; + *y = prj->r0*log(astTand((90.0+theta)/2.0)); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astMERrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + if (prj->flag != WCS__MER) { + if (astMERset(prj)) return 1; + } + + *phi = x*prj->w[1]; + *theta = 2.0*astATand(exp(y/prj->r0)) - 90.0; + + return 0; +} + +/*============================================================================ +* SFL: Sanson-Flamsteed ("global sinusoid") projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "SFL" +* prj->flag SFL +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/180) +* prj->w[1] (180/pi)/r0 +* prj->astPRJfwd Pointer to astSFLfwd(). +* prj->astPRJrev Pointer to astSFLrev(). +*===========================================================================*/ + +int astSFLset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "SFL"); + prj->flag = WCS__SFL; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astSFLfwd; + prj->astPRJrev = astSFLrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSFLfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + if (prj->flag != WCS__SFL) { + if (astSFLset(prj)) return 1; + } + + *x = prj->w[0]*phi*astCosd(theta); + *y = prj->w[0]*theta; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astSFLrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double w; + + if (prj->flag != WCS__SFL) { + if (astSFLset(prj)) return 1; + } + + w = cos(y/prj->r0); + if (w == 0.0) { + *phi = 0.0; + } else { + *phi = x*prj->w[1]/cos(y/prj->r0); + } + *theta = y*prj->w[1]; + + return 0; +} + +/*============================================================================ +* PAR: parabolic projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "PAR" +* prj->flag PAR +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/180) +* prj->w[1] (180/pi)/r0 +* prj->w[2] pi*r0 +* prj->w[3] 1/(pi*r0) +* prj->astPRJfwd Pointer to astPARfwd(). +* prj->astPRJrev Pointer to astPARrev(). +*===========================================================================*/ + +int astPARset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "PAR"); + prj->flag = WCS__PAR; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + prj->w[2] = 180.0; + prj->w[3] = 1.0/prj->w[2]; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = 1.0/prj->w[0]; + prj->w[2] = PI*prj->r0; + prj->w[3] = 1.0/prj->w[2]; + } + + prj->astPRJfwd = astPARfwd; + prj->astPRJrev = astPARrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astPARfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double s; + + if (prj->flag != WCS__PAR) { + if (astPARset(prj)) return 1; + } + + s = astSind(theta/3.0); + *x = prj->w[0]*phi*(1.0 - 4.0*s*s); + *y = prj->w[2]*s; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astPARrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double s, t; + + if (prj->flag != WCS__PAR) { + if (astPARset(prj)) return 1; + } + + s = y*prj->w[3]; + if (s > 1.0 || s < -1.0) { + return 2; + } + + t = 1.0 - 4.0*s*s; + if (t == 0.0) { + if (x == 0.0) { + *phi = 0.0; + } else { + return 2; + } + } else { + *phi = prj->w[1]*x/t; + } + + *theta = 3.0*astASind(s); + + return 0; +} + +/*============================================================================ +* MOL: Mollweide's projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "MOL" +* prj->flag MOL +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] sqrt(2)*r0 +* prj->w[1] sqrt(2)*r0/90 +* prj->w[2] 1/(sqrt(2)*r0) +* prj->w[3] 90/r0 +* prj->astPRJfwd Pointer to astMOLfwd(). +* prj->astPRJrev Pointer to astMOLrev(). +*===========================================================================*/ + +int astMOLset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "MOL"); + prj->flag = WCS__MOL; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->w[0] = SQRT2*prj->r0; + prj->w[1] = prj->w[0]/90.0; + prj->w[2] = 1.0/prj->w[0]; + prj->w[3] = 90.0/prj->r0; + prj->w[4] = 2.0/PI; + + prj->astPRJfwd = astMOLfwd; + prj->astPRJrev = astMOLrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astMOLfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + int j; + double gamma, resid, u, v, v0, v1; + const double tol = 1.0e-13; + + if (prj->flag != WCS__MOL) { + if (astMOLset(prj)) return 1; + } + + if (fabs(theta) == 90.0) { + *x = 0.0; + *y = copysign(prj->w[0],theta); + } else if (theta == 0.0) { + *x = prj->w[1]*phi; + *y = 0.0; + } else { + u = PI*astSind(theta); + v0 = -PI; + v1 = PI; + v = u; + for (j = 0; j < 100; j++) { + resid = (v - u) + sin(v); + if (resid < 0.0) { + if (resid > -tol) break; + v0 = v; + } else { + if (resid < tol) break; + v1 = v; + } + v = (v0 + v1)/2.0; + } + + gamma = v/2.0; + *x = prj->w[1]*phi*cos(gamma); + *y = prj->w[0]*sin(gamma); + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astMOLrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double s, y0, z; + const double tol = 1.0e-12; + + if (prj->flag != WCS__MOL) { + if (astMOLset(prj)) return 1; + } + + y0 = y/prj->r0; + s = 2.0 - y0*y0; + if (s <= tol) { + if (s < -tol) { + return 2; + } + s = 0.0; + + if (fabs(x) > tol) { + return 2; + } + *phi = 0.0; + } else { + s = sqrt(s); + *phi = prj->w[3]*x/s; + } + + z = y*prj->w[2]; + if (fabs(z) > 1.0) { + if (fabs(z) > 1.0+tol) { + return 2; + } + z = copysign(1.0,z) + y0*s/PI; + } else { + z = asin(z)*prj->w[4] + y0*s/PI; + } + + if (fabs(z) > 1.0) { + if (fabs(z) > 1.0+tol) { + return 2; + } + z = copysign(1.0,z); + } + + *theta = astASind(z); + + return 0; +} + +/*============================================================================ +* AIT: Hammer-Aitoff projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "AIT" +* prj->flag AIT +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] 2*r0**2 +* prj->w[1] 1/(2*r0)**2 +* prj->w[2] 1/(4*r0)**2 +* prj->w[3] 1/(2*r0) +* prj->astPRJfwd Pointer to astAITfwd(). +* prj->astPRJrev Pointer to astAITrev(). +*===========================================================================*/ + +int astAITset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "AIT"); + prj->flag = WCS__AIT; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->w[0] = 2.0*prj->r0*prj->r0; + prj->w[1] = 1.0/(2.0*prj->w[0]); + prj->w[2] = prj->w[1]/4.0; + prj->w[3] = 1.0/(2.0*prj->r0); + + prj->astPRJfwd = astAITfwd; + prj->astPRJrev = astAITrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astAITfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double cthe, w; + + if (prj->flag != WCS__AIT) { + if (astAITset(prj)) return 1; + } + + cthe = astCosd(theta); + w = sqrt(prj->w[0]/(1.0 + cthe*astCosd(phi/2.0))); + *x = 2.0*w*cthe*astSind(phi/2.0); + *y = w*astSind(theta); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astAITrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double s, u, xp, yp, z; + const double tol = 1.0e-13; + + if (prj->flag != WCS__AIT) { + if (astAITset(prj)) return 1; + } + + u = 1.0 - x*x*prj->w[2] - y*y*prj->w[1]; + if (u < 0.0) { + if (u < -tol) { + return 2; + } + + u = 0.0; + } + + z = sqrt(u); + s = z*y/prj->r0; + if (fabs(s) > 1.0) { + if (fabs(s) > 1.0+tol) { + return 2; + } + s = copysign(1.0,s); + } + + xp = 2.0*z*z - 1.0; + yp = z*x*prj->w[3]; + if (xp == 0.0 && yp == 0.0) { + *phi = 0.0; + } else { + *phi = 2.0*astATan2d(yp, xp); + } + *theta = astASind(s); + + return 0; +} + +/*============================================================================ +* COP: conic perspective projection. +* +* Given: +* prj->p[1] sigma = (theta2+theta1)/2 +* prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the +* latitudes of the standard parallels, in degrees. +* +* Given and/or returned: +* prj->flag COP, or -COP if prj->flag is given < 0. +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "COP" +* prj->phi0 0.0 +* prj->theta0 sigma +* prj->w[0] C = sin(sigma) +* prj->w[1] 1/C +* prj->w[2] Y0 = r0*cos(delta)*cot(sigma) +* prj->w[3] r0*cos(delta) +* prj->w[4] 1/(r0*cos(delta) +* prj->w[5] cot(sigma) +* prj->astPRJfwd Pointer to astCOPfwd(). +* prj->astPRJrev Pointer to astCOPrev(). +*===========================================================================*/ + +int astCOPset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "COP"); + prj->flag = icopysign(WCS__COP, prj->flag); + prj->phi0 = 0.0; + prj->theta0 = prj->p[1]; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->w[0] = astSind(prj->p[1]); + if (prj->w[0] == 0.0) { + return 1; + } + + prj->w[1] = 1.0/prj->w[0]; + + prj->w[3] = prj->r0*astCosd(prj->p[2]); + if (prj->w[3] == 0.0) { + return 1; + } + + prj->w[4] = 1.0/prj->w[3]; + prj->w[5] = 1.0/astTand(prj->p[1]); + + prj->w[2] = prj->w[3]*prj->w[5]; + + prj->astPRJfwd = astCOPfwd; + prj->astPRJrev = astCOPrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCOPfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, r, s, t; + + if (abs(prj->flag) != WCS__COP) { + if (astCOPset(prj)) return 1; + } + + t = theta - prj->p[1]; + s = astCosd(t); + if (s == 0.0) { + return 2; + } + + a = prj->w[0]*phi; + r = prj->w[2] - prj->w[3]*astSind(t)/s; + + *x = r*astSind(a); + *y = prj->w[2] - r*astCosd(a); + + if (prj->flag > 0 && r*prj->w[0] < 0.0) { + return 2; + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCOPrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double a, dy, r; + + if (abs(prj->flag) != WCS__COP) { + if (astCOPset(prj)) return 1; + } + + dy = prj->w[2] - y; + r = sqrt(x*x + dy*dy); + if (prj->p[1] < 0.0) r = -r; + + if (r == 0.0) { + a = 0.0; + } else { + a = astATan2d(x/r, dy/r); + } + + *phi = a*prj->w[1]; + *theta = prj->p[1] + astATand(prj->w[5] - r*prj->w[4]); + + return 0; +} + +/*============================================================================ +* COE: conic equal area projection. +* +* Given: +* prj->p[1] sigma = (theta2+theta1)/2 +* prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the +* latitudes of the standard parallels, in degrees. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "COE" +* prj->flag COE +* prj->phi0 0.0 +* prj->theta0 sigma +* prj->w[0] C = (sin(theta1) + sin(theta2))/2 +* prj->w[1] 1/C +* prj->w[2] Y0 = chi*sqrt(psi - 2C*astSind(sigma)) +* prj->w[3] chi = r0/C +* prj->w[4] psi = 1 + sin(theta1)*sin(theta2) +* prj->w[5] 2C +* prj->w[6] (1 + sin(theta1)*sin(theta2))*(r0/C)**2 +* prj->w[7] C/(2*r0**2) +* prj->w[8] chi*sqrt(psi + 2C) +* prj->astPRJfwd Pointer to astCOEfwd(). +* prj->astPRJrev Pointer to astCOErev(). +*===========================================================================*/ + +int astCOEset(prj) + +struct AstPrjPrm *prj; + +{ + double theta1, theta2; + + strcpy(prj->code, "COE"); + prj->flag = WCS__COE; + prj->phi0 = 0.0; + prj->theta0 = prj->p[1]; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + theta1 = prj->p[1] - prj->p[2]; + theta2 = prj->p[1] + prj->p[2]; + + prj->w[0] = (astSind(theta1) + astSind(theta2))/2.0; + if (prj->w[0] == 0.0) { + return 1; + } + + prj->w[1] = 1.0/prj->w[0]; + + prj->w[3] = prj->r0/prj->w[0]; + prj->w[4] = 1.0 + astSind(theta1)*astSind(theta2); + prj->w[5] = 2.0*prj->w[0]; + prj->w[6] = prj->w[3]*prj->w[3]*prj->w[4]; + prj->w[7] = 1.0/(2.0*prj->r0*prj->w[3]); + prj->w[8] = prj->w[3]*sqrt(prj->w[4] + prj->w[5]); + + prj->w[2] = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*astSind(prj->p[1])); + + prj->astPRJfwd = astCOEfwd; + prj->astPRJrev = astCOErev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCOEfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, r; + + if (prj->flag != WCS__COE) { + if (astCOEset(prj)) return 1; + } + + a = phi*prj->w[0]; + if (theta == -90.0) { + r = prj->w[8]; + } else { + r = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*astSind(theta)); + } + + *x = r*astSind(a); + *y = prj->w[2] - r*astCosd(a); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCOErev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double a, dy, r, w; + const double tol = 1.0e-12; + + if (prj->flag != WCS__COE) { + if (astCOEset(prj)) return 1; + } + + dy = prj->w[2] - y; + r = sqrt(x*x + dy*dy); + if (prj->p[1] < 0.0) r = -r; + + if (r == 0.0) { + a = 0.0; + } else { + a = astATan2d(x/r, dy/r); + } + + *phi = a*prj->w[1]; + if (fabs(r - prj->w[8]) < tol) { + *theta = -90.0; + } else { + w = (prj->w[6] - r*r)*prj->w[7]; + if (fabs(w) > 1.0) { + if (fabs(w-1.0) < tol) { + *theta = 90.0; + } else if (fabs(w+1.0) < tol) { + *theta = -90.0; + } else { + return 2; + } + } else { + *theta = astASind(w); + } + } + + return 0; +} + +/*============================================================================ +* COD: conic equidistant projection. +* +* Given: +* prj->p[1] sigma = (theta2+theta1)/2 +* prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the +* latitudes of the standard parallels, in degrees. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "COD" +* prj->flag COD +* prj->phi0 0.0 +* prj->theta0 sigma +* prj->w[0] C = r0*sin(sigma)*sin(delta)/delta +* prj->w[1] 1/C +* prj->w[2] Y0 = delta*cot(delta)*cot(sigma) +* prj->w[3] Y0 + sigma +* prj->astPRJfwd Pointer to astCODfwd(). +* prj->astPRJrev Pointer to astCODrev(). +*===========================================================================*/ + +int astCODset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "COD"); + prj->flag = WCS__COD; + prj->phi0 = 0.0; + prj->theta0 = prj->p[1]; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + if (prj->p[2] == 0.0) { + prj->w[0] = prj->r0*astSind(prj->p[1])*D2R; + } else { + prj->w[0] = prj->r0*astSind(prj->p[1])*astSind(prj->p[2])/prj->p[2]; + } + + if (prj->w[0] == 0.0) { + return 1; + } + + prj->w[1] = 1.0/prj->w[0]; + prj->w[2] = prj->r0*astCosd(prj->p[2])*astCosd(prj->p[1])/prj->w[0]; + prj->w[3] = prj->w[2] + prj->p[1]; + + prj->astPRJfwd = astCODfwd; + prj->astPRJrev = astCODrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCODfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, r; + + if (prj->flag != WCS__COD) { + if (astCODset(prj)) return 1; + } + + a = prj->w[0]*phi; + r = prj->w[3] - theta; + + *x = r*astSind(a); + *y = prj->w[2] - r*astCosd(a); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCODrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double a, dy, r; + + if (prj->flag != WCS__COD) { + if (astCODset(prj)) return 1; + } + + dy = prj->w[2] - y; + r = sqrt(x*x + dy*dy); + if (prj->p[1] < 0.0) r = -r; + + if (r == 0.0) { + a = 0.0; + } else { + a = astATan2d(x/r, dy/r); + } + + *phi = a*prj->w[1]; + *theta = prj->w[3] - r; + + return 0; +} + +/*============================================================================ +* COO: conic orthomorphic projection. +* +* Given: +* prj->p[1] sigma = (theta2+theta1)/2 +* prj->p[2] delta = (theta2-theta1)/2, where theta1 and theta2 are the +* latitudes of the standard parallels, in degrees. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "COO" +* prj->flag COO +* prj->phi0 0.0 +* prj->theta0 sigma +* prj->w[0] C = ln(cos(theta2)/cos(theta1))/ln(tan(tau2)/tan(tau1)) +* where tau1 = (90 - theta1)/2 +* tau2 = (90 - theta2)/2 +* prj->w[1] 1/C +* prj->w[2] Y0 = psi*tan((90-sigma)/2)**C +* prj->w[3] psi = (r0*cos(theta1)/C)/tan(tau1)**C +* prj->w[4] 1/psi +* prj->astPRJfwd Pointer to astCOOfwd(). +* prj->astPRJrev Pointer to astCOOrev(). +*===========================================================================*/ + +int astCOOset(prj) + +struct AstPrjPrm *prj; + +{ + double cos1, cos2, tan1, tan2, theta1, theta2; + + strcpy(prj->code, "COO"); + prj->flag = WCS__COO; + prj->phi0 = 0.0; + prj->theta0 = prj->p[1]; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + theta1 = prj->p[1] - prj->p[2]; + theta2 = prj->p[1] + prj->p[2]; + + tan1 = astTand((90.0 - theta1)/2.0); + cos1 = astCosd(theta1); + + if (theta1 == theta2) { + prj->w[0] = astSind(theta1); + } else { + tan2 = astTand((90.0 - theta2)/2.0); + cos2 = astCosd(theta2); + prj->w[0] = log(cos2/cos1)/log(tan2/tan1); + } + if (prj->w[0] == 0.0) { + return 1; + } + + prj->w[1] = 1.0/prj->w[0]; + + prj->w[3] = prj->r0*(cos1/prj->w[0])/pow(tan1,prj->w[0]); + if (prj->w[3] == 0.0) { + return 1; + } + prj->w[2] = prj->w[3]*pow(astTand((90.0 - prj->p[1])/2.0),prj->w[0]); + prj->w[4] = 1.0/prj->w[3]; + + prj->astPRJfwd = astCOOfwd; + prj->astPRJrev = astCOOrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCOOfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, r; + + if (prj->flag != WCS__COO) { + if (astCOOset(prj)) return 1; + } + + a = prj->w[0]*phi; + if (theta == -90.0) { + if (prj->w[0] < 0.0) { + r = 0.0; + } else { + return 2; + } + } else { + r = prj->w[3]*pow(astTand((90.0 - theta)/2.0),prj->w[0]); + } + + *x = r*astSind(a); + *y = prj->w[2] - r*astCosd(a); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCOOrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double a, dy, r; + + if (prj->flag != WCS__COO) { + if (astCOOset(prj)) return 1; + } + + dy = prj->w[2] - y; + r = sqrt(x*x + dy*dy); + if (prj->p[1] < 0.0) r = -r; + + if (r == 0.0) { + a = 0.0; + } else { + a = astATan2d(x/r, dy/r); + } + + *phi = a*prj->w[1]; + if (r == 0.0) { + if (prj->w[0] < 0.0) { + *theta = -90.0; + } else { + return 2; + } + } else { + *theta = 90.0 - 2.0*astATand(pow(r*prj->w[4],prj->w[1])); + } + + return 0; +} + +/*============================================================================ +* BON: Bonne's projection. +* +* Given: +* prj->p[1] Bonne conformal latitude, theta1, in degrees. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "BON" +* prj->flag BON +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[1] r0*pi/180 +* prj->w[2] Y0 = r0*(cot(theta1) + theta1*pi/180) +* prj->astPRJfwd Pointer to astBONfwd(). +* prj->astPRJrev Pointer to astBONrev(). +*===========================================================================*/ + +int astBONset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "BON"); + prj->flag = WCS__BON; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[1] = 1.0; + prj->w[2] = prj->r0*astCosd(prj->p[1])/astSind(prj->p[1]) + prj->p[1]; + } else { + prj->w[1] = prj->r0*D2R; + prj->w[2] = prj->r0*(astCosd(prj->p[1])/astSind(prj->p[1]) + prj->p[1]*D2R); + } + + prj->astPRJfwd = astBONfwd; + prj->astPRJrev = astBONrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astBONfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, r; + + if (prj->p[1] == 0.0) { + /* Sanson-Flamsteed. */ + return astSFLfwd(phi, theta, prj, x, y); + } + + if (prj->flag != WCS__BON) { + if (astBONset(prj)) return 1; + } + + r = prj->w[2] - theta*prj->w[1]; + a = prj->r0*phi*astCosd(theta)/r; + + *x = r*astSind(a); + *y = prj->w[2] - r*astCosd(a); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astBONrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double a, cthe, dy, r; + + if (prj->p[1] == 0.0) { + /* Sanson-Flamsteed. */ + return astSFLrev(x, y, prj, phi, theta); + } + + if (prj->flag != WCS__BON) { + if (astBONset(prj)) return 1; + } + + dy = prj->w[2] - y; + r = sqrt(x*x + dy*dy); + if (prj->p[1] < 0.0) r = -r; + + if (r == 0.0) { + a = 0.0; + } else { + a = astATan2d(x/r, dy/r); + } + + *theta = (prj->w[2] - r)/prj->w[1]; + cthe = astCosd(*theta); + if (cthe == 0.0) { + *phi = 0.0; + } else { + *phi = a*(r/prj->r0)/cthe; + } + + return 0; +} + +/*============================================================================ +* PCO: polyconic projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "PCO" +* prj->flag PCO +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/180) +* prj->w[1] 1/r0 +* prj->w[2] 2*r0 +* prj->astPRJfwd Pointer to astPCOfwd(). +* prj->astPRJrev Pointer to astPCOrev(). +*===========================================================================*/ + +int astPCOset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "PCO"); + prj->flag = WCS__PCO; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + prj->w[2] = 360.0/PI; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = 1.0/prj->w[0]; + prj->w[2] = 2.0*prj->r0; + } + + prj->astPRJfwd = astPCOfwd; + prj->astPRJrev = astPCOrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astPCOfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double a, cthe, cotthe, sthe; + + if (prj->flag != WCS__PCO) { + if (astPCOset(prj)) return 1; + } + + cthe = astCosd(theta); + sthe = astSind(theta); + a = phi*sthe; + + if (sthe == 0.0) { + *x = prj->w[0]*phi; + *y = 0.0; + } else { + cotthe = cthe/sthe; + *x = prj->r0*cotthe*astSind(a); + *y = prj->r0*(cotthe*(1.0 - astCosd(a)) + theta*D2R); + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astPCOrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + int j; + double f, fneg, fpos, lambda, tanthe, theneg, thepos, w, xp, xx, ymthe, yp; + const double tol = 1.0e-12; + + if (prj->flag != WCS__PCO) { + if (astPCOset(prj)) return 1; + } + + w = fabs(y*prj->w[1]); + if (w < tol) { + *phi = x*prj->w[1]; + *theta = 0.0; + } else if (fabs(w-90.0) < tol) { + *phi = 0.0; + *theta = copysign(90.0,y); + } else { + /* Iterative solution using weighted division of the interval. */ + if (y > 0.0) { + thepos = 90.0; + } else { + thepos = -90.0; + } + theneg = 0.0; + + xx = x*x; + ymthe = y - prj->w[0]*thepos; + fpos = xx + ymthe*ymthe; + fneg = -999.0; + + for (j = 0; j < 64; j++) { + if (fneg < -100.0) { + /* Equal division of the interval. */ + *theta = (thepos+theneg)/2.0; + } else { + /* Weighted division of the interval. */ + lambda = fpos/(fpos-fneg); + if (lambda < 0.1) { + lambda = 0.1; + } else if (lambda > 0.9) { + lambda = 0.9; + } + *theta = thepos - lambda*(thepos-theneg); + } + + /* Compute the residue. */ + ymthe = y - prj->w[0]*(*theta); + tanthe = astTand(*theta); + f = xx + ymthe*(ymthe - prj->w[2]/tanthe); + + /* Check for convergence. */ + if (fabs(f) < tol) break; + if (fabs(thepos-theneg) < tol) break; + + /* Redefine the interval. */ + if (f > 0.0) { + thepos = *theta; + fpos = f; + } else { + theneg = *theta; + fneg = f; + } + } + + xp = prj->r0 - ymthe*tanthe; + yp = x*tanthe; + if (xp == 0.0 && yp == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(yp, xp)/astSind(*theta); + } + } + + return 0; +} + +/*============================================================================ +* TSC: tangential spherical cube projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "TSC" +* prj->flag TSC +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/4) +* prj->w[1] (4/pi)/r0 +* prj->astPRJfwd Pointer to astTSCfwd(). +* prj->astPRJrev Pointer to astTSCrev(). +*===========================================================================*/ + +int astTSCset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "TSC"); + prj->flag = WCS__TSC; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 45.0; + prj->w[1] = 1.0/45.0; + } else { + prj->w[0] = prj->r0*PI/4.0; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astTSCfwd; + prj->astPRJrev = astTSCrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astTSCfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + int face; + double cthe, l, m, n, rho, x0, xf, y0, yf; + const double tol = 1.0e-12; + + x0 = 0.0; + xf = 0.0; + y0 = 0.0; + yf = 0.0; + + if (prj->flag != WCS__TSC) { + if (astTSCset(prj)) return 1; + } + + cthe = astCosd(theta); + l = cthe*astCosd(phi); + m = cthe*astSind(phi); + n = astSind(theta); + + face = 0; + rho = n; + if (l > rho) { + face = 1; + rho = l; + } + if (m > rho) { + face = 2; + rho = m; + } + if (-l > rho) { + face = 3; + rho = -l; + } + if (-m > rho) { + face = 4; + rho = -m; + } + if (-n > rho) { + face = 5; + rho = -n; + } + + if (face == 0) { + xf = m/rho; + yf = -l/rho; + x0 = 0.0; + y0 = 2.0; + } else if (face == 1) { + xf = m/rho; + yf = n/rho; + x0 = 0.0; + y0 = 0.0; + } else if (face == 2) { + xf = -l/rho; + yf = n/rho; + x0 = 2.0; + y0 = 0.0; + } else if (face == 3) { + xf = -m/rho; + yf = n/rho; + x0 = 4.0; + y0 = 0.0; + } else if (face == 4) { + xf = l/rho; + yf = n/rho; + x0 = 6.0; + y0 = 0.0; + } else if (face == 5) { + xf = m/rho; + yf = l/rho; + x0 = 0.0; + y0 = -2.0; + } + + if (fabs(xf) > 1.0) { + if (fabs(xf) > 1.0+tol) { + return 2; + } + xf = copysign(1.0,xf); + } + if (fabs(yf) > 1.0) { + if (fabs(yf) > 1.0+tol) { + return 2; + } + yf = copysign(1.0,yf); + } + + *x = prj->w[0]*(xf + x0); + *y = prj->w[0]*(yf + y0); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astTSCrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double l, m, n, xf, yf; + + if (prj->flag != WCS__TSC) { + if (astTSCset(prj)) return 1; + } + + xf = x*prj->w[1]; + yf = y*prj->w[1]; + + /* Check bounds. */ + if (fabs(xf) <= 1.0) { + if (fabs(yf) > 3.0) return 2; + } else { + if (fabs(xf) > 7.0) return 2; + if (fabs(yf) > 1.0) return 2; + } + + /* Map negative faces to the other side. */ + if (xf < -1.0) xf += 8.0; + + /* Determine the face. */ + if (xf > 5.0) { + /* face = 4 */ + xf = xf - 6.0; + m = -1.0/sqrt(1.0 + xf*xf + yf*yf); + l = -m*xf; + n = -m*yf; + } else if (xf > 3.0) { + /* face = 3 */ + xf = xf - 4.0; + l = -1.0/sqrt(1.0 + xf*xf + yf*yf); + m = l*xf; + n = -l*yf; + } else if (xf > 1.0) { + /* face = 2 */ + xf = xf - 2.0; + m = 1.0/sqrt(1.0 + xf*xf + yf*yf); + l = -m*xf; + n = m*yf; + } else if (yf > 1.0) { + /* face = 0 */ + yf = yf - 2.0; + n = 1.0/sqrt(1.0 + xf*xf + yf*yf); + l = -n*yf; + m = n*xf; + } else if (yf < -1.0) { + /* face = 5 */ + yf = yf + 2.0; + n = -1.0/sqrt(1.0 + xf*xf + yf*yf); + l = -n*yf; + m = -n*xf; + } else { + /* face = 1 */ + l = 1.0/sqrt(1.0 + xf*xf + yf*yf); + m = l*xf; + n = l*yf; + } + + if (l == 0.0 && m == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(m, l); + } + *theta = astASind(n); + + return 0; +} + +/*============================================================================ +* CSC: COBE quadrilateralized spherical cube projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "CSC" +* prj->flag CSC +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/4) +* prj->w[1] (4/pi)/r0 +* prj->astPRJfwd Pointer to astCSCfwd(). +* prj->astPRJrev Pointer to astCSCrev(). +*===========================================================================*/ + +int astCSCset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "CSC"); + prj->flag = WCS__CSC; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 45.0; + prj->w[1] = 1.0/45.0; + } else { + prj->w[0] = prj->r0*PI/4.0; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astCSCfwd; + prj->astPRJrev = astCSCrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCSCfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + int face; + float cthe, eta, l, m, n, rho, xi; + const float tol = 1.0e-7; + + float a, a2, a2b2, a4, ab, b, b2, b4, ca2, cb2, x0, xf, y0, yf; + const float gstar = 1.37484847732; + const float mm = 0.004869491981; + const float gamma = -0.13161671474; + const float omega1 = -0.159596235474; + const float d0 = 0.0759196200467; + const float d1 = -0.0217762490699; + const float c00 = 0.141189631152; + const float c10 = 0.0809701286525; + const float c01 = -0.281528535557; + const float c11 = 0.15384112876; + const float c20 = -0.178251207466; + const float c02 = 0.106959469314; + + eta = 0.0; + xi = 0.0; + x0 = 0.0; + y0 = 0.0; + + if (prj->flag != WCS__CSC) { + if (astCSCset(prj)) return 1; + } + + cthe = astCosd(theta); + l = cthe*astCosd(phi); + m = cthe*astSind(phi); + n = astSind(theta); + + face = 0; + rho = n; + if (l > rho) { + face = 1; + rho = l; + } + if (m > rho) { + face = 2; + rho = m; + } + if (-l > rho) { + face = 3; + rho = -l; + } + if (-m > rho) { + face = 4; + rho = -m; + } + if (-n > rho) { + face = 5; + rho = -n; + } + + if (face == 0) { + xi = m; + eta = -l; + x0 = 0.0; + y0 = 2.0; + } else if (face == 1) { + xi = m; + eta = n; + x0 = 0.0; + y0 = 0.0; + } else if (face == 2) { + xi = -l; + eta = n; + x0 = 2.0; + y0 = 0.0; + } else if (face == 3) { + xi = -m; + eta = n; + x0 = 4.0; + y0 = 0.0; + } else if (face == 4) { + xi = l; + eta = n; + x0 = 6.0; + y0 = 0.0; + } else if (face == 5) { + xi = m; + eta = l; + x0 = 0.0; + y0 = -2.0; + } + + a = xi/rho; + b = eta/rho; + + a2 = a*a; + b2 = b*b; + ca2 = 1.0 - a2; + cb2 = 1.0 - b2; + + /* Avoid floating underflows. */ + ab = fabs(a*b); + a4 = (a2 > 1.0e-16) ? a2*a2 : 0.0; + b4 = (b2 > 1.0e-16) ? b2*b2 : 0.0; + a2b2 = (ab > 1.0e-16) ? a2*b2 : 0.0; + + xf = a*(a2 + ca2*(gstar + b2*(gamma*ca2 + mm*a2 + + cb2*(c00 + c10*a2 + c01*b2 + c11*a2b2 + c20*a4 + c02*b4)) + + a2*(omega1 - ca2*(d0 + d1*a2)))); + yf = b*(b2 + cb2*(gstar + a2*(gamma*cb2 + mm*b2 + + ca2*(c00 + c10*b2 + c01*a2 + c11*a2b2 + c20*b4 + c02*a4)) + + b2*(omega1 - cb2*(d0 + d1*b2)))); + + if (fabs(xf) > 1.0) { + if (fabs(xf) > 1.0+tol) { + return 2; + } + xf = copysign(1.0,xf); + } + if (fabs(yf) > 1.0) { + if (fabs(yf) > 1.0+tol) { + return 2; + } + yf = copysign(1.0,yf); + } + + *x = prj->w[0]*(x0 + xf); + *y = prj->w[0]*(y0 + yf); + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astCSCrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + int face; + float l, m, n; + + float a, b, xf, xx, yf, yy, z0, z1, z2, z3, z4, z5, z6; + const float p00 = -0.27292696; + const float p10 = -0.07629969; + const float p20 = -0.22797056; + const float p30 = 0.54852384; + const float p40 = -0.62930065; + const float p50 = 0.25795794; + const float p60 = 0.02584375; + const float p01 = -0.02819452; + const float p11 = -0.01471565; + const float p21 = 0.48051509; + const float p31 = -1.74114454; + const float p41 = 1.71547508; + const float p51 = -0.53022337; + const float p02 = 0.27058160; + const float p12 = -0.56800938; + const float p22 = 0.30803317; + const float p32 = 0.98938102; + const float p42 = -0.83180469; + const float p03 = -0.60441560; + const float p13 = 1.50880086; + const float p23 = -0.93678576; + const float p33 = 0.08693841; + const float p04 = 0.93412077; + const float p14 = -1.41601920; + const float p24 = 0.33887446; + const float p05 = -0.63915306; + const float p15 = 0.52032238; + const float p06 = 0.14381585; + + l = 0.0; + m = 0.0; + n = 0.0; + + if (prj->flag != WCS__CSC) { + if (astCSCset(prj)) return 1; + } + + xf = x*prj->w[1]; + yf = y*prj->w[1]; + + /* Check bounds. */ + if (fabs(xf) <= 1.0) { + if (fabs(yf) > 3.0) return 2; + } else { + if (fabs(xf) > 7.0) return 2; + if (fabs(yf) > 1.0) return 2; + } + + /* Map negative faces to the other side. */ + if (xf < -1.0) xf += 8.0; + + /* Determine the face. */ + if (xf > 5.0) { + face = 4; + xf = xf - 6.0; + } else if (xf > 3.0) { + face = 3; + xf = xf - 4.0; + } else if (xf > 1.0) { + face = 2; + xf = xf - 2.0; + } else if (yf > 1.0) { + face = 0; + yf = yf - 2.0; + } else if (yf < -1.0) { + face = 5; + yf = yf + 2.0; + } else { + face = 1; + } + + xx = xf*xf; + yy = yf*yf; + + z0 = p00 + xx*(p10 + xx*(p20 + xx*(p30 + xx*(p40 + xx*(p50 + xx*(p60)))))); + z1 = p01 + xx*(p11 + xx*(p21 + xx*(p31 + xx*(p41 + xx*(p51))))); + z2 = p02 + xx*(p12 + xx*(p22 + xx*(p32 + xx*(p42)))); + z3 = p03 + xx*(p13 + xx*(p23 + xx*(p33))); + z4 = p04 + xx*(p14 + xx*(p24)); + z5 = p05 + xx*(p15); + z6 = p06; + + a = z0 + yy*(z1 + yy*(z2 + yy*(z3 + yy*(z4 + yy*(z5 + yy*z6))))); + a = xf + xf*(1.0 - xx)*a; + + z0 = p00 + yy*(p10 + yy*(p20 + yy*(p30 + yy*(p40 + yy*(p50 + yy*(p60)))))); + z1 = p01 + yy*(p11 + yy*(p21 + yy*(p31 + yy*(p41 + yy*(p51))))); + z2 = p02 + yy*(p12 + yy*(p22 + yy*(p32 + yy*(p42)))); + z3 = p03 + yy*(p13 + yy*(p23 + yy*(p33))); + z4 = p04 + yy*(p14 + yy*(p24)); + z5 = p05 + yy*(p15); + z6 = p06; + + b = z0 + xx*(z1 + xx*(z2 + xx*(z3 + xx*(z4 + xx*(z5 + xx*z6))))); + b = yf + yf*(1.0 - yy)*b; + + if (face == 0) { + n = 1.0/sqrt(a*a + b*b + 1.0); + l = -b*n; + m = a*n; + } else if (face == 1) { + l = 1.0/sqrt(a*a + b*b + 1.0); + m = a*l; + n = b*l; + } else if (face == 2) { + m = 1.0/sqrt(a*a + b*b + 1.0); + l = -a*m; + n = b*m; + } else if (face == 3) { + l = -1.0/sqrt(a*a + b*b + 1.0); + m = a*l; + n = -b*l; + } else if (face == 4) { + m = -1.0/sqrt(a*a + b*b + 1.0); + l = -a*m; + n = -b*m; + } else if (face == 5) { + n = -1.0/sqrt(a*a + b*b + 1.0); + l = -b*n; + m = -a*n; + } + + if (l == 0.0 && m == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(m, l); + } + *theta = astASind(n); + + return 0; +} + +/*============================================================================ +* QSC: quadrilaterilized spherical cube projection. +* +* Given and/or returned: +* prj->r0 r0; reset to 180/pi if 0. +* +* Returned: +* prj->code "QSC" +* prj->flag QSC +* prj->phi0 0.0 +* prj->theta0 0.0 +* prj->w[0] r0*(pi/4) +* prj->w[1] (4/pi)/r0 +* prj->astPRJfwd Pointer to astQSCfwd(). +* prj->astPRJrev Pointer to astQSCrev(). +*===========================================================================*/ + +int astQSCset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "QSC"); + prj->flag = WCS__QSC; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 45.0; + prj->w[1] = 1.0/45.0; + } else { + prj->w[0] = prj->r0*PI/4.0; + prj->w[1] = 1.0/prj->w[0]; + } + + prj->astPRJfwd = astQSCfwd; + prj->astPRJrev = astQSCrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astQSCfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + int face; + double cthe, eta, l, m, n, omega, p, rho, rhu, t, tau, x0, xf, xi, y0, yf; + const double tol = 1.0e-12; + + eta = 0.0; + x0 = 0.0; + xf = 0.0; + xi = 0.0; + y0 = 0.0; + yf = 0.0; + + if (prj->flag != WCS__QSC) { + if (astQSCset(prj)) return 1; + } + + if (fabs(theta) == 90.0) { + *x = 0.0; + *y = copysign(2.0*prj->w[0],theta); + return 0; + } + + cthe = astCosd(theta); + l = cthe*astCosd(phi); + m = cthe*astSind(phi); + n = astSind(theta); + + face = 0; + rho = n; + if (l > rho) { + face = 1; + rho = l; + } + if (m > rho) { + face = 2; + rho = m; + } + if (-l > rho) { + face = 3; + rho = -l; + } + if (-m > rho) { + face = 4; + rho = -m; + } + if (-n > rho) { + face = 5; + rho = -n; + } + + rhu = 1.0 - rho; + + if (face == 0) { + xi = m; + eta = -l; + if (rhu < 1.0e-8) { + /* Small angle formula. */ + t = (90.0 - theta)*D2R; + rhu = t*t/2.0; + } + x0 = 0.0; + y0 = 2.0; + } else if (face == 1) { + xi = m; + eta = n; + if (rhu < 1.0e-8) { + /* Small angle formula. */ + t = theta*D2R; + p = fmod(phi,360.0); + if (p < -180.0) p += 360.0; + if (p > 180.0) p -= 360.0; + p *= D2R; + rhu = (p*p + t*t)/2.0; + } + x0 = 0.0; + y0 = 0.0; + } else if (face == 2) { + xi = -l; + eta = n; + if (rhu < 1.0e-8) { + /* Small angle formula. */ + t = theta*D2R; + p = fmod(phi,360.0); + if (p < -180.0) p += 360.0; + p = (90.0 - p)*D2R; + rhu = (p*p + t*t)/2.0; + } + x0 = 2.0; + y0 = 0.0; + } else if (face == 3) { + xi = -m; + eta = n; + if (rhu < 1.0e-8) { + /* Small angle formula. */ + t = theta*D2R; + p = fmod(phi,360.0); + if (p < 0.0) p += 360.0; + p = (180.0 - p)*D2R; + rhu = (p*p + t*t)/2.0; + } + x0 = 4.0; + y0 = 0.0; + } else if (face == 4) { + xi = l; + eta = n; + if (rhu < 1.0e-8) { + /* Small angle formula. */ + t = theta*D2R; + p = fmod(phi,360.0); + if (p > 180.0) p -= 360.0; + p *= (90.0 + p)*D2R; + rhu = (p*p + t*t)/2.0; + } + x0 = 6; + y0 = 0.0; + } else if (face == 5) { + xi = m; + eta = l; + if (rhu < 1.0e-8) { + /* Small angle formula. */ + t = (90.0 + theta)*D2R; + rhu = t*t/2.0; + } + x0 = 0.0; + y0 = -2; + } + + if (xi == 0.0 && eta == 0.0) { + xf = 0.0; + yf = 0.0; + } else if (-xi >= fabs(eta)) { + omega = eta/xi; + tau = 1.0 + omega*omega; + xf = -sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); + yf = (xf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); + } else if (xi >= fabs(eta)) { + omega = eta/xi; + tau = 1.0 + omega*omega; + xf = sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); + yf = (xf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); + } else if (-eta > fabs(xi)) { + omega = xi/eta; + tau = 1.0 + omega*omega; + yf = -sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); + xf = (yf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); + } else if (eta > fabs(xi)) { + omega = xi/eta; + tau = 1.0 + omega*omega; + yf = sqrt(rhu/(1.0-1.0/sqrt(1.0+tau))); + xf = (yf/15.0)*(astATand(omega) - astASind(omega/sqrt(tau+tau))); + } + + if (fabs(xf) > 1.0) { + if (fabs(xf) > 1.0+tol) { + return 2; + } + xf = copysign(1.0,xf); + } + if (fabs(yf) > 1.0) { + if (fabs(yf) > 1.0+tol) { + return 2; + } + yf = copysign(1.0,yf); + } + + *x = prj->w[0]*(xf + x0); + *y = prj->w[0]*(yf + y0); + + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astQSCrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + int direct, face; + double l, m, n, omega, rho, rhu, tau, xf, yf, w; + const double tol = 1.0e-12; + + l = 0.0; + m = 0.0; + n = 0.0; + + if (prj->flag != WCS__QSC) { + if (astQSCset(prj)) return 1; + } + + xf = x*prj->w[1]; + yf = y*prj->w[1]; + + /* Check bounds. */ + if (fabs(xf) <= 1.0) { + if (fabs(yf) > 3.0) return 2; + } else { + if (fabs(xf) > 7.0) return 2; + if (fabs(yf) > 1.0) return 2; + } + + /* Map negative faces to the other side. */ + if (xf < -1.0) xf += 8.0; + + /* Determine the face. */ + if (xf > 5.0) { + face = 4; + xf = xf - 6.0; + } else if (xf > 3.0) { + face = 3; + xf = xf - 4.0; + } else if (xf > 1.0) { + face = 2; + xf = xf - 2.0; + } else if (yf > 1.0) { + face = 0; + yf = yf - 2.0; + } else if (yf < -1.0) { + face = 5; + yf = yf + 2.0; + } else { + face = 1; + } + + direct = (fabs(xf) > fabs(yf)); + if (direct) { + if (xf == 0.0) { + omega = 0.0; + tau = 1.0; + rho = 1.0; + rhu = 0.0; + } else { + w = 15.0*yf/xf; + omega = astSind(w)/(astCosd(w) - SQRT2INV); + tau = 1.0 + omega*omega; + rhu = xf*xf*(1.0 - 1.0/sqrt(1.0 + tau)); + rho = 1.0 - rhu; + } + } else { + if (yf == 0.0) { + omega = 0.0; + tau = 1.0; + rho = 1.0; + rhu = 0.0; + } else { + w = 15.0*xf/yf; + omega = astSind(w)/(astCosd(w) - SQRT2INV); + tau = 1.0 + omega*omega; + rhu = yf*yf*(1.0 - 1.0/sqrt(1.0 + tau)); + rho = 1.0 - rhu; + } + } + + if (rho < -1.0) { + if (rho < -1.0-tol) { + return 2; + } + + rho = -1.0; + rhu = 2.0; + w = 0.0; + } else { + w = sqrt(rhu*(2.0-rhu)/tau); + } + + if (face == 0) { + n = rho; + if (direct) { + m = w; + if (xf < 0.0) m = -m; + l = -m*omega; + } else { + l = w; + if (yf > 0.0) l = -l; + m = -l*omega; + } + } else if (face == 1) { + l = rho; + if (direct) { + m = w; + if (xf < 0.0) m = -m; + n = m*omega; + } else { + n = w; + if (yf < 0.0) n = -n; + m = n*omega; + } + } else if (face == 2) { + m = rho; + if (direct) { + l = w; + if (xf > 0.0) l = -l; + n = -l*omega; + } else { + n = w; + if (yf < 0.0) n = -n; + l = -n*omega; + } + } else if (face == 3) { + l = -rho; + if (direct) { + m = w; + if (xf > 0.0) m = -m; + n = -m*omega; + } else { + n = w; + if (yf < 0.0) n = -n; + m = -n*omega; + } + } else if (face == 4) { + m = -rho; + if (direct) { + l = w; + if (xf < 0.0) l = -l; + n = l*omega; + } else { + n = w; + if (yf < 0.0) n = -n; + l = n*omega; + } + } else if (face == 5) { + n = -rho; + if (direct) { + m = w; + if (xf < 0.0) m = -m; + l = m*omega; + } else { + l = w; + if (yf < 0.0) l = -l; + m = l*omega; + } + } + + if (l == 0.0 && m == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(m, l); + } + *theta = astASind(n); + + return 0; +} + +/*============================================================================ +* HPX: HEALPix projection. +* +* Given: +* prj->p[1] H - the number of facets in longitude. +* prj->p[2] K - the number of facets in latitude +* +* Given and/or returned: +* prj->r0 Reset to 180/pi if 0. +* prj->phi0 Reset to 0.0 +* prj->theta0 Reset to 0.0 +* +* Returned: +* prj->flag HPX +* prj->code "HPX" +* prj->n True if K is odd. +* prj->w[0] r0*(pi/180) +* prj->w[1] (180/pi)/r0 +* prj->w[2] (K-1)/K +* prj->w[3] 90*K/H +* prj->w[4] (K+1)/2 +* prj->w[5] 90*(K-1)/H +* prj->w[6] 180/H +* prj->w[7] H/360 +* prj->w[8] (90*K/H)*r0*(pi/180) +* prj->w[9] (180/H)*r0*(pi/180) +* prj->astPRJfwd Pointer to astHPXfwd(). +* prj->astPRJrev Pointer to astHPXrev(). + + +*===========================================================================*/ + +int astHPXset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "HPX"); + prj->flag = WCS__HPX; + prj->phi0 = 0.0; + prj->theta0 = 0.0; + + prj->n = ((int)prj->p[2])%2; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = R2D/prj->r0; + } + + prj->w[2] = (prj->p[2] - 1.0) / prj->p[2]; + prj->w[3] = 90.0 * prj->p[2] / prj->p[1]; + prj->w[4] = (prj->p[2] + 1.0) / 2.0; + prj->w[5] = 90.0 * (prj->p[2] - 1.0) / prj->p[1]; + prj->w[6] = 180.0 / prj->p[1]; + prj->w[7] = prj->p[1] / 360.0; + prj->w[8] = prj->w[3] * prj->w[0]; + prj->w[9] = prj->w[6] * prj->w[0]; + + prj->astPRJfwd = astHPXfwd; + prj->astPRJrev = astHPXrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astHPXfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double abssin, sigma, sinthe, phic; + int hodd; + + if( prj->flag != WCS__HPX ) { + if( astHPXset( prj ) ) return 1; + } + + sinthe = astSind( theta ); + abssin = fabs( sinthe ); + +/* Equatorial zone */ + if( abssin <= prj->w[2] ) { + *x = prj->w[0] * phi; + *y = prj->w[8] * sinthe; + +/* Polar zone */ + } else { + +/* DSB - The expression for phic is conditioned differently to the + WCSLIB code in order to improve accuracy of the floor function for + arguments very slightly below an integer value. */ + hodd = ((int)prj->p[1]) % 2; + if( !prj->n && theta <= 0.0 ) hodd = 1 - hodd; + if( hodd ) { + phic = -180.0 + (2.0*floor( prj->w[7] * phi + 1/2 ) + prj->p[1] ) * prj->w[6]; + } else { + phic = -180.0 + (2.0*floor( prj->w[7] * phi ) + prj->p[1] + 1 ) * prj->w[6]; + } + + sigma = sqrt( prj->p[2]*( 1.0 - abssin )); + + *x = prj->w[0] *( phic + ( phi - phic )*sigma ); + + *y = prj->w[9] * ( prj->w[4] - sigma ); + if( theta < 0 ) *y = -*y; + + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astHPXrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double absy, sigma, t, yr, xc; + int hodd; + + if (prj->flag != WCS__HPX) { + if (astHPXset(prj)) return 1; + } + + yr = prj->w[1]*y; + absy = fabs( yr ); + +/* Equatorial zone */ + if( absy <= prj->w[5] ) { + *phi = prj->w[1] * x; + t = yr/prj->w[3]; + if( t < -1.0 || t > 1.0 ) { + return 2; + } else { + *theta = astASind( t ); + } + +/* Polar zone */ + } else if( absy <= 90 ){ + + +/* DSB - The expression for xc is conditioned differently to the + WCSLIB code in order to improve accuracy of the floor function for + arguments very slightly below an integer value. */ + hodd = ((int)prj->p[1]) % 2; + if( !prj->n && yr <= 0.0 ) hodd = 1 - hodd; + if( hodd ) { + xc = -180.0 + (2.0*floor( prj->w[7] * x + 1/2 ) + prj->p[1] ) * prj->w[6]; + } else { + xc = -180.0 + (2.0*floor( prj->w[7] * x ) + prj->p[1] + 1 ) * prj->w[6]; + } + + sigma = prj->w[4] - absy / prj->w[6]; + + if( sigma == 0.0 ) { + return 2; + } else { + + t = ( x - xc )/sigma; + if( fabs( t ) <= prj->w[6] ) { + *phi = prj->w[1] *( xc + t ); + } else { + return 2; + } + } + + t = 1.0 - sigma*sigma/prj->p[2]; + if( t < -1.0 || t > 1.0 ) { + return 2; + } else { + *theta = astASind( t ); + if( y < 0 ) *theta = -*theta; + } + + } else { + return 2; + } + + return 0; +} + +/*============================================================================ +* XPH: HEALPix polar, aka "butterfly" projection. +* +* Given and/or returned: +* prj->r0 Reset to 180/pi if 0. +* prj->phi0 Reset to 0.0 if undefined. +* prj->theta0 Reset to 0.0 if undefined. +* +* Returned: +* prj->flag XPH +* prj->code "XPH" +* prj->w[0] r0*(pi/180)/sqrt(2) +* prj->w[1] (180/pi)/r0/sqrt(2) +* prj->w[2] 2/3 +* prj->w[3] tol (= 1e-4) +* prj->w[4] sqrt(2/3)*(180/pi) +* prj->w[5] 90 - tol*sqrt(2/3)*(180/pi) +* prj->w[6] sqrt(3/2)*(pi/180) +* prj->astPRJfwd Pointer to astXPHfwd(). +* prj->astPRJrev Pointer to astXPHrev(). +*===========================================================================*/ + +int astXPHset(prj) + +struct AstPrjPrm *prj; + +{ + strcpy(prj->code, "XPH"); + prj->flag = WCS__XPH; + + if (prj->r0 == 0.0) { + prj->r0 = R2D; + prj->w[0] = 1.0; + prj->w[1] = 1.0; + } else { + prj->w[0] = prj->r0*D2R; + prj->w[1] = R2D/prj->r0; + } + + prj->w[0] /= sqrt(2.0); + prj->w[1] /= sqrt(2.0); + prj->w[2] = 2.0/3.0; + prj->w[3] = 1e-4; + prj->w[4] = sqrt(prj->w[2])*R2D; + prj->w[5] = 90.0 - prj->w[3]*prj->w[4]; + prj->w[6] = sqrt(1.5)*D2R; + + prj->astPRJfwd = astXPHfwd; + prj->astPRJrev = astXPHrev; + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astXPHfwd(phi, theta, prj, x, y) + +const double phi, theta; +struct AstPrjPrm *prj; +double *x, *y; + +{ + double abssin, chi, eta, psi, sigma, sinthe, xi; + + if (prj->flag != WCS__XPH) { + if (astXPHset(prj)) return 1; + } + + /* Do phi dependence. */ + chi = phi; + if (180.0 <= fabs(chi)) { + chi = fmod(chi, 360.0); + if (chi < -180.0) { + chi += 360.0; + } else if (180.0 <= chi) { + chi -= 360.0; + } + } + + /* phi is also recomputed from chi to avoid rounding problems. */ + chi += 180.0; + psi = fmod(chi, 90.0); + + /* y is used to hold phi (rounded). */ + *x = psi; + *y = chi - 180.0; + + /* Do theta dependence. */ + sinthe = astSind(theta); + abssin = fabs(sinthe); + + if (abssin <= prj->w[2]) { + /* Equatorial regime. */ + xi = *x; + eta = 67.5 * sinthe; + + } else { + /* Polar regime. */ + if (theta < prj->w[5]) { + sigma = sqrt(3.0*(1.0 - abssin)); + } else { + sigma = (90.0 - theta)*prj->w[6]; + } + + xi = 45.0 + (*x - 45.0)*sigma; + eta = 45.0 * (2.0 - sigma); + if (theta < 0.0) eta = -eta; + } + + xi -= 45.0; + eta -= 90.0; + + /* Recall that y holds phi. */ + if (*y < -90.0) { + *x = prj->w[0]*(-xi + eta); + *y = prj->w[0]*(-xi - eta); + + } else if (*y < 0.0) { + *x = prj->w[0]*(+xi + eta); + *y = prj->w[0]*(-xi + eta); + + } else if (*y < 90.0) { + *x = prj->w[0]*( xi - eta); + *y = prj->w[0]*( xi + eta); + + } else { + *x = prj->w[0]*(-xi - eta); + *y = prj->w[0]*( xi - eta); + } + + return 0; + +} + +/*--------------------------------------------------------------------------*/ + +int astXPHrev(x, y, prj, phi, theta) + +const double x, y; +struct AstPrjPrm *prj; +double *phi, *theta; + +{ + double abseta, eta, eta1, sigma, xi, xi1, xr, yr; + const double tol = 1.0e-12; + + if (prj->flag != WCS__XPH) { + if (astXPHset(prj)) return 1; + } + + + xr = x*prj->w[1]; + yr = y*prj->w[1]; + if (xr <= 0.0 && 0.0 < yr) { + xi1 = -xr - yr; + eta1 = xr - yr; + *phi = -180.0; + } else if (xr < 0.0 && yr <= 0.0) { + xi1 = xr - yr; + eta1 = xr + yr; + *phi = -90.0; + } else if (0.0 <= xr && yr < 0.0) { + xi1 = xr + yr; + eta1 = -xr + yr; + *phi = 0.0; + } else { + xi1 = -xr + yr; + eta1 = -xr - yr; + *phi = 90.0; + } + + xi = xi1 + 45.0; + eta = eta1 + 90.0; + abseta = fabs(eta); + + if (abseta <= 90.0) { + if (abseta <= 45.0) { + /* Equatorial regime. */ + *phi += xi; + *theta = astASind(eta/67.5); + + /* Bounds checking. */ + if (45.0+tol < fabs(xi1)) return 2; + + } else { + /* Polar regime. */ + sigma = (90.0 - abseta) / 45.0; + + /* Ensure an exact result for points on the boundary. */ + if (xr == 0.0) { + if (yr <= 0.0) { + *phi = 0.0; + } else { + *phi = 180.0; + } + } else if (yr == 0.0) { + if (xr < 0.0) { + *phi = -90.0; + } else { + *phi = 90.0; + } + } else { + *phi += 45.0 + xi1/sigma; + } + + if (sigma < prj->w[3]) { + *theta = 90.0 - sigma*prj->w[4]; + } else { + *theta = astASind(1.0 - sigma*sigma/3.0); + } + if (eta < 0.0) *theta = -(*theta); + + /* Bounds checking. */ + if (eta < -45.0 && eta+90.0+tol < fabs(xi1)) return 2; + } + + } else { + /* Beyond latitude range. */ + *phi = 0.0; + *theta = 0.0; + return 2; + } + + return 0; +} + + diff --git a/proj.h b/proj.h new file mode 100644 index 0000000..61e4746 --- /dev/null +++ b/proj.h @@ -0,0 +1,181 @@ +/*============================================================================= +* +* WCSLIB - an implementation of the FITS WCS proposal. +* Copyright (C) 1995-2002, Mark Calabretta +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Library General Public License as published +* by the Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library +* General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street,Fifth Floor, Boston, MA 02110-1301, USA +* +* Correspondence concerning WCSLIB may be directed to: +* Internet email: mcalabre@atnf.csiro.au +* Postal address: Dr. Mark Calabretta, +* Australia Telescope National Facility, +* P.O. Box 76, +* Epping, NSW, 2121, +* AUSTRALIA +* +* Author: Mark Calabretta, Australia Telescope National Facility +* $Id$ +*============================================================================= +* +* This version of proj.h is based on the version in wcslib-2.9, but has +* been modified in the following ways by the Starlink project (e-mail: +* ussc@star.rl.ac.uk): +* - Support for non-ANSI C prototypes removed +* - Changed the name of the WCSLIB_PROJ macro to WCSLIB_PROJ_INCLUDED +* - Changed names of all functions and structures to avoid name +* clashes with wcslib. +* - Change the maximum number of projection parameters to 100. +* - Added definition of macro WCSLIB_MXPAR, and use it to define +* size of projection parameter array within AstPrjPrm structure. +* - Added component "p2" to the AstPrjPrm structure to hold projection +* parameters associated with the longitude axis (for use within +* the tpn.c file which holds an implementation of the old "TAN with +* correction terms" projection). +* - Added prototypes for TPN projection functions (defined in file +* tpn.c). +* - Added prototypes for HPX projection functions. +* - Added prototypes for XPH projection functions. +*===========================================================================*/ + +#ifndef WCSLIB_PROJ_INCLUDED +#define WCSLIB_PROJ_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#define WCSLIB_MXPAR 100 + +extern int npcode; +extern char pcodes[26][4]; + +struct AstPrjPrm { + char code[4]; + int flag; + double phi0, theta0; + double r0; + double *p; + double *p2; + double w[20]; + int n; + int (*astPRJfwd)(const double, const double, + struct AstPrjPrm *, + double *, double *); + int (*astPRJrev)(const double, const double, + struct AstPrjPrm *, + double *, double *); +}; + + int astPRJset(const char [], struct AstPrjPrm *); + int astPRJfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astPRJrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astAZPset(struct AstPrjPrm *); + int astAZPfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astAZPrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astSZPset(struct AstPrjPrm *); + int astSZPfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astSZPrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astTANset(struct AstPrjPrm *); + int astTANfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astTANrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astSTGset(struct AstPrjPrm *); + int astSTGfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astSTGrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astSINset(struct AstPrjPrm *); + int astSINfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astSINrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astARCset(struct AstPrjPrm *); + int astARCfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astARCrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astZPNset(struct AstPrjPrm *); + int astZPNfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astZPNrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astZEAset(struct AstPrjPrm *); + int astZEAfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astZEArev(const double, const double, struct AstPrjPrm *, double *, double *); + int astAIRset(struct AstPrjPrm *); + int astAIRfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astAIRrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCYPset(struct AstPrjPrm *); + int astCYPfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCYPrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCEAset(struct AstPrjPrm *); + int astCEAfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCEArev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCARset(struct AstPrjPrm *); + int astCARfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCARrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astMERset(struct AstPrjPrm *); + int astMERfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astMERrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astSFLset(struct AstPrjPrm *); + int astSFLfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astSFLrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astPARset(struct AstPrjPrm *); + int astPARfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astPARrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astMOLset(struct AstPrjPrm *); + int astMOLfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astMOLrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astAITset(struct AstPrjPrm *); + int astAITfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astAITrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCOPset(struct AstPrjPrm *); + int astCOPfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCOPrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCOEset(struct AstPrjPrm *); + int astCOEfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCOErev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCODset(struct AstPrjPrm *); + int astCODfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCODrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCOOset(struct AstPrjPrm *); + int astCOOfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCOOrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astBONset(struct AstPrjPrm *); + int astBONfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astBONrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astPCOset(struct AstPrjPrm *); + int astPCOfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astPCOrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astTSCset(struct AstPrjPrm *); + int astTSCfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astTSCrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astCSCset(struct AstPrjPrm *); + int astCSCfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astCSCrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astQSCset(struct AstPrjPrm *); + int astQSCfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astQSCrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astHPXset(struct AstPrjPrm *); + int astHPXfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astHPXrev(const double, const double, struct AstPrjPrm *, double *, double *); + int astXPHset(struct AstPrjPrm *); + int astXPHfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astXPHrev(const double, const double, struct AstPrjPrm *, double *, double *); + + int astTPNset(struct AstPrjPrm *); + int astTPNfwd(const double, const double, struct AstPrjPrm *, double *, double *); + int astTPNrev(const double, const double, struct AstPrjPrm *, double *, double *); + +extern const char *astPRJset_errmsg[]; +extern const char *astPRJfwd_errmsg[]; +extern const char *astPRJrev_errmsg[]; + +#ifdef __cplusplus +}; +#endif + +#endif /* WCSLIB_PROJ_INCLUDED */ diff --git a/ratemap.c b/ratemap.c new file mode 100644 index 0000000..1e343a6 --- /dev/null +++ b/ratemap.c @@ -0,0 +1,2011 @@ +/* +*class++ +* Name: +* RateMap + +* Purpose: +* Mapping which represents differentiation. + +* Constructor Function: +c astRateMap +f AST_RATEMAP + +* Description: +* A RateMap is a Mapping which represents a single element of the +* Jacobian matrix of another Mapping. The Mapping for which the +* Jacobian is required is specified when the new RateMap is created, +* and is referred to as the "encapsulated Mapping" below. +* +* The number of inputs to a RateMap is the same as the number of inputs +* to its encapsulated Mapping. The number of outputs from a RateMap +* is always one. This one output equals the rate of change of a +* specified output of the encapsulated Mapping with respect to a +* specified input of the encapsulated Mapping (the input and output +* to use are specified when the RateMap is created). +* +* A RateMap which has not been inverted does not define an inverse +* transformation. If a RateMap has been inverted then it will define +* an inverse transformation but not a forward transformation. + +* Inheritance: +* The RateMap class inherits from the Mapping class. + +* Attributes: +* The RateMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The RateMap class does not define any new functions beyond those +f The RateMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 10-FEB-2004 (DSB): +* Original version. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 10-MAY-2006 (DSB): +* Override astEqual. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS RateMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "ratemap.h" /* Interface definition for this class */ +#include "unitmap.h" /* Unit Mappings */ +#include "frame.h" /* Frames */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(RateMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(RateMap,Class_Init) +#define class_vtab astGLOBAL(RateMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstRateMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstRateMap *astRateMapId_( void *, int, int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two RateMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "ratemap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* RateMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two RateMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a RateMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the RateMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstRateMap *that; + AstRateMap *this; + int nin; + int nout; + int result; + int that_inv; + int this_inv; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two RateMap structures. */ + this = (AstRateMap *) this_object; + that = (AstRateMap *) that_object; + +/* Check the second object is a RateMap. We know the first is a + RateMap since we have arrived at this implementation of the virtual + function. */ + if( astIsARateMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two RateMaps differ, it may still be possible + for them to be equivalent. First compare the RateMaps if their Invert + flags are the same. In this case all the attributes of the two RateMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + +/* Temporarily re-instate the original Invert flag values. */ + this_inv = astGetInvert( this->map ); + that_inv = astGetInvert( that->map ); + astSetInvert( this->map, this->invert ); + astSetInvert( that->map, that->invert ); + + if( astEqual( this->map, that->map ) && + this->iin == that->iin && + this->iout == that->iout ){ + result = 1; + } + +/* Restore the original Invert flag values. */ + astSetInvert( this->map, this_inv ); + astSetInvert( that->map, that_inv ); + +/* If the Invert flags for the two RateMaps differ, the attributes of the two + RateMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a RateMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "ratemap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* RateMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied RateMap, +* in bytes. + +* Parameters: +* this +* Pointer to the RateMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstRateMap *this; /* Pointer to RateMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the RateMap structure. */ + this = (AstRateMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astGetObjSize( this->map ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitRateMapVtab_( AstRateMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitRateMapVtab + +* Purpose: +* Initialise a virtual function table for a RateMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "ratemap.h" +* void astInitRateMapVtab( AstRateMapVtab *vtab, const char *name ) + +* Class Membership: +* RateMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the RateMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsARateMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* None. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + mapping->RemoveRegions = RemoveRegions; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_mapsplit = mapping->MapSplit; + mapping->MapSplit = MapSplit; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "RateMap", "Differential Mapping" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* RateMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstRateMap *this; /* Pointer to RateMap structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the RateMap structure. */ + this = (AstRateMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->map, mode, extra, fail ); + + return result; + +} +#endif + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a RateMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* RateMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated RateMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated RateMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated RateMap which is to be merged with +* its neighbours. This should be a cloned copy of the RateMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* RateMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated RateMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *emap1; + AstMapping *emap2; + AstMapping *emap; + AstMapping *smap; + AstRateMap *map; + AstRateMap *rmap1; + AstRateMap *rmap2; + int cancel; + int map_inv; + int nax; + int old_inv2; + int old_inv; + int old_winv; + int result; + +/* Initialise. */ + result = -1; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Initialisation to avoid compiler warnings. */ + nax = 0; + +/* Get a pointer to this RateMap. */ + map = (AstRateMap *) this; + +/* Temporarily set its Invert flag to the requested value. */ + map_inv = astGetInvert( map ); + astSetInvert( map, ( *invert_list )[ where ] ); + +/* Get the encapsulated Mapping, and temporarily set its Invert attribute + back to the value it had when the RateMap was created, saving the current + Invert value so that it can be re-instated later. */ + emap = map->map; + old_inv = astGetInvert( emap ); + astSetInvert( emap, map->invert ); + +/* First try to simplify the RateMap by simplifying its encapsulated + Mapping. */ + smap = astSimplify( emap ); + +/* If any simplification took place, create a new RateMap with the + simplified mapping. */ + if( smap != emap ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astRateMap( smap, map->iout, map->iin, "", status ); + result = where; + +/* The only other simplication which can be performed is to cancel a RateMap + with its own inverse in series. */ + } else if( series ) { + +/* Indicate we have nothing to cancel with as yet. */ + cancel = -1; + +/* First consider the lower neighbour. */ + if( where > 0 && astIsARateMap( ( *map_list )[ where - 1 ] ) ) { + +/* Check the Invert flags are opposite */ + if( ( *invert_list )[ where ] != ( *invert_list )[ where - 1 ] ) { + rmap1 = map; + rmap2 = (AstRateMap *) ( *map_list )[ where - 1 ]; + +/* Check the input and output indices are equal. */ + if( rmap1->iin == rmap2->iin && + rmap1->iout == rmap2->iout ) { + +/* Check the encapsulated Mappings are equal. */ + emap1 = emap; + emap2 = rmap2->map; + old_winv = astGetInvert( rmap2 ); + astSetInvert( rmap2, ( *invert_list )[ where - 1 ] ); + old_inv2 = astGetInvert( emap2 ); + astSetInvert( emap2, rmap2->invert ); + + if( astEqual( emap1, emap2 ) ) cancel = where - 1; + + astSetInvert( emap2, old_inv2 ); + astSetInvert( rmap2, old_winv ); + + nax = astGetNout( rmap1 ); + } + } + } + +/* Likewise consider the upper neighbour. */ + if( cancel == -1 && where + 1 < *nmap && + astIsARateMap( ( *map_list )[ where + 1 ] ) ) { + + if( ( *invert_list )[ where ] != ( *invert_list )[ where + 1 ] ) { + rmap1 = map; + rmap2 = (AstRateMap *) ( *map_list )[ where + 1 ]; + if( rmap1->iin == rmap2->iin && + rmap1->iout == rmap2->iout ) { + emap1 = emap; + emap2 = rmap2->map; + old_winv = astGetInvert( rmap2 ); + astSetInvert( rmap2, ( *invert_list )[ where + 1 ] ); + old_inv2 = astGetInvert( emap2 ); + astSetInvert( emap2, rmap2->invert ); + + if( astEqual( emap1, emap2 ) ) cancel = where + 1; + + astSetInvert( emap2, old_inv2 ); + astSetInvert( rmap2, old_winv ); + + nax = astGetNin( rmap1 ); + } + } + } + +/* If we can cancel with a neightbour, do so. */ + if( cancel != -1 ) { + (void) astAnnul( ( *map_list )[ where ] ); + (void) astAnnul( ( *map_list )[ cancel ] ); + ( *map_list )[ where ] = (AstMapping *) astUnitMap( nax, "", status ); + ( *invert_list )[ where ] = 0; + ( *map_list )[ cancel ] = (AstMapping *) astUnitMap( nax, "", status ); + ( *invert_list )[ cancel ] = 0; + result = ( cancel < where ) ? cancel : where; + } + } + +/* Free resources. */ + smap = astAnnul( smap ); + +/* Reset the original Invert attribute for the encapsulated Mapping. */ + astSetInvert( emap, old_inv ); + +/* Reset the original Invert attribute for the specified RateMap */ + astSetInvert( map, map_inv ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* RateMap. + +* Type: +* Private function. + +* Synopsis: +* #include "ratemap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* RateMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing RateMap. This is only possible if the specified inputs +* correspond to some subset of the RateMap outputs. That is, there +* must exist a subset of the RateMap outputs for which each output +* depends only on the selected RateMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied RateMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the RateMap to be split (the RateMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied RateMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied RateMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied RateMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstMapping *emap; /* Pointer to Mapping encapsulated by RateMap */ + AstMapping *remap; /* Split Mapping encapsulated by RateMap */ + AstRateMap *this; /* Pointer to RateMap structure */ + int *eres; /* Outputs used by split Mapping */ + int *result; /* Array holding returned output inedx */ + int ax1; /* New index of output being differentiated */ + int ax2; /* New index of output being varied */ + int i; /* Loop count */ + int nout; /* No. of outputs in the split Mapping */ + int old_inv; /* Original Invert flag for emap */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the parent astMapSplit method to see if it can do the job. */ + result = (*parent_mapsplit)( this_map, nin, in, map, status ); + +/* If not, we provide a special implementation here. Note we cannot + produce the Mapping if the RaterMap has been inverted. */ + if( !result && !astGetInvert( this_map ) ) { + +/* Get a pointer to the RateMap structure. */ + this = (AstRateMap *) this_map; + +/* Temporarily reset the Invert attribute of the encapsulated Mapping + back to the value it had when the RateMap was created. */ + emap = this->map; + old_inv = astGetInvert( emap ); + astSetInvert( emap, this->invert ); + +/* Attempt to split the encapsulated Mapping */ + eres = astMapSplit( emap, nin, in, &remap ); + +/* We can only continue if this was succesful. */ + if( eres ) { + +/* Check that the input which the RateMap varies is one of the selected + inputs. */ + ax2 = -1; + for( i = 0; i < nin; i++ ) { + if( in[ i ] == this->iin ) { + ax2 = i; + break; + } + } + +/* Check that the output which the RateMap differentiates is one of the + outputs of the "remap" Mapping. */ + ax1 = -1; + nout = astGetNout( remap ); + for( i = 0; i < nout; i++ ) { + if( eres[ i ] == this->iout ) { + ax1 = i; + break; + } + } + +/* If possible create the required Mapping and returned array. */ + if( ax1 != -1 && ax2 != -1 ) { + *map = (AstMapping *) astRateMap( remap, ax1, ax2, "", status ); + result = astMalloc( sizeof( int ) ); + if( astOK ) *result= 0; + } + +/* Free resources */ + eres = astFree( eres ); + remap = astAnnul( remap ); + } + +/* Re-instate the original Invert flag in the Mapping encapsulated by the + supplied RateMap. */ + astSetInvert( emap, old_inv ); + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "ratemap.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* RateMap method (over-rides the astRemoveRegions method inherited +* from the Mapping class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a CmpMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel CmpMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the RateMap class invokes the +* astRemoveRegions method on the encapsulated Mapping, and returns a +* new RateMap containing the resulting Mapping. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstMapping *newmap; /* New component Mapping */ + AstMapping *result; /* Result pointer to return */ + AstRateMap *new; /* Pointer to new RateMap */ + AstRateMap *this; /* Pointer to RateMap structure */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the RateMap. */ + this = (AstRateMap *) this_mapping; + +/* Invoke the astRemoveRegions method on the component Mapping. */ + newmap = astRemoveRegions( this->map ); + +/* If the Mapping was not modified, just return a clone of the supplied + pointer. */ + if( this->map == newmap ) { + result = astClone( this ); + +/* Otherwise, we need to create a new RateMap to return. */ + } else { + +/* If the new Mapping is a Frame (as will be the case if the original + Mapping was a Region), use a UnitMap instead. */ + if( astIsAFrame( newmap ) ) { + (void) astAnnul( newmap ); + newmap = (AstMapping *) astUnitMap( astGetNin( this ), " ", status ); + } + +/* Take a deep copy of the supplied RateMap and then modify the Mapping + so that we retain any extra information in the supplied RateMap. */ + new = astCopy( this ); + (void) astAnnul( new->map ); + new->map = astClone( newmap ); + result = (AstMapping *) new; + } + +/* Free resources. */ + newmap = astAnnul( newmap ); + +/* Annul the returned Mapping if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a RateMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "ratemap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* RateMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a RateMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Mapping. +* This implies applying each of the RateMap's component Mappings in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the RateMap. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the RateMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstMapping *emap; + AstPointSet *result; + AstRateMap *map; + double **ptr2; + double **ptr; + double *pout; + double *work; + int ic; + int iin; + int iout; + int ipoint; + int ncoord; + int npoint; + int old_inv; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the RateMap. */ + map = (AstRateMap *) this; + +/* Apply the parent Mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We now extend the parent astTransform method by applying the component + Mappings of the RateMap to generate the output coordinate values. */ + +/* Determine whether to apply the forward or inverse Mapping, according to the + direction specified and whether the Mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* The RateMap class does not have an inverse transformation. */ + if( !forward ) { + astError( AST__INTER, "astTransform(%s): The %s class does not have " + "an inverse transformation (AST internal programming error).", status, + astGetClass( this ), astGetClass( this ) ); + +/* Otherwise use the astRate method on the encapsulated Maping to + determine the required rate of change at each supplied input point. */ + } else { + +/* Temporarily reset the Invert attribute of the encapsulated Mapping + back to the value it had when the RateMap was created. */ + emap = map->map; + old_inv = astGetInvert( emap ); + astSetInvert( emap, map->invert ); + +/* Note the indices of the input and output to use. */ + iin = map->iin; + iout = map->iout; + +/* Get pointers to the axis values in the supplied PointSet. */ + ptr = astGetPoints( in ); + ncoord = astGetNcoord( in ); + npoint = astGetNpoint( in ); + +/* Work space to hold an input position. */ + work = astMalloc( sizeof( double )*(size_t) ncoord ); + +/* Get a pointer to the axis values in the results PointSet. */ + ptr2 = astGetPoints( result ); + pout = ptr2[ 0 ]; + if( astOK ) { + +/* Loop round each point in the supplied PointSet. */ + for( ipoint = 0; ipoint < npoint; ipoint++ ) { + +/* Copy this point into the work array. */ + for( ic = 0; ic < ncoord; ic++ ) work[ ic ] = ptr[ ic ][ ipoint ]; + +/* Find the rate of change of the specified output of the encapsulated + Mapping with respect to the specified input. */ + *(pout++) = astRate( emap, work, iout, iin ); + } + } + +/* Re-instate the original Invert flag. */ + astSetInvert( emap, old_inv ); + +/* Free resources */ + work = astFree( work ); + + } + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for RateMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for RateMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Mappings within the RateMap. +*/ + +/* Local Variables: */ + AstRateMap *in; /* Pointer to input RateMap */ + AstRateMap *out; /* Pointer to output RateMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output RateMaps. */ + in = (AstRateMap *) objin; + out = (AstRateMap *) objout; + +/* For safety, start by clearing any references to the input component + Mappings from the output RateMap. */ + out->map = NULL; + +/* Make copies of these Mappings and store pointers to them in the output + RateMap structure. */ + out->map = astCopy( in->map ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for RateMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for RateMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstRateMap *this; /* Pointer to RateMap */ + +/* Obtain a pointer to the RateMap structure. */ + this = (AstRateMap *) obj; + +/* Annul the pointers to the component Mappings. */ + this->map = astAnnul( this->map ); + +/* Clear the remaining RateMap variables. */ + this->invert = 0; + this->iin = 0; + this->iout = 0; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for RateMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the RateMap class to an output Channel. + +* Parameters: +* this +* Pointer to the RateMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstRateMap *this; /* Pointer to the RateMap structure */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the RateMap structure. */ + this = (AstRateMap *) this_object; + +/* Write out values representing the instance variables for the RateMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Input axis. */ +/* ------------ */ + ival = this->iin; + set = ( ival != 0 ); + astWriteInt( channel, "IIn", set, 0, ival, "Index of Mapping input" ); + +/* Output axis. */ +/* ------------ */ + ival = this->iout; + set = ( ival != 0 ); + astWriteInt( channel, "IOut", set, 0, ival, "Index of Mapping output" ); + +/* Invert flag. */ +/* ------------ */ + ival = this->invert; + set = ( ival != 0 ); + astWriteInt( channel, "Inv", set, 0, ival, + ival ? "Mapping used in inverse direction" : + "Mapping used in forward direction" ); + +/* Mapping. */ +/* -------- */ + astWriteObject( channel, "Map", 1, 1, this->map, + "Mapping to be differentiated" ); + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsARateMap and astCheckRateMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(RateMap,Mapping) +astMAKE_CHECK(RateMap) + +AstRateMap *astRateMap_( void *map_void, int ax1, int ax2, const char *options, int *status, ...) { +/* +*+ +* Name: +* astRateMap + +* Purpose: +* Create a RateMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "ratemap.h" +* AstRateMap *astRateMap( AstMapping *map, int ax1, int ax2, const char *options, int *status, ... ) + +* Class Membership: +* RateMap constructor. + +* Description: +* This function creates a new RateMap and optionally initialises its +* attributes. + +* Parameters: +* map +* Pointer to the Mapping to differentiate. +* ax1 +* zero-based index of the "map" output which is to be differentiated. +* ax2 +* Zero-based index of the "map" input which is to be varied. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new RateMap. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new RateMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic RateMap constructor which is +* available via the protected interface to the RateMap class. A +* public interface is provided by the astRateMapId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "map" parameter is of type (void *) and is converted and validated +* within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRateMap *new; /* Pointer to new RateMap */ + AstMapping *map; /* Pointer to Mapping structure */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Mapping structures provided. */ + map = astCheckMapping( map_void ); + if ( astOK ) { + +/* Initialise the RateMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitRateMap( NULL, sizeof( AstRateMap ), !class_init, &class_vtab, + "RateMap", map, ax1, ax2 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new RateMap's + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new RateMap. */ + return new; +} + +AstRateMap *astRateMapId_( void *map_void, int ax1, int ax2, + const char *options, ... ) { +/* +*++ +* Name: +c astRateMap +f AST_RATEMAP + +* Purpose: +* Create a RateMap. + +* Type: +* Public function. + +* Synopsis: +c #include "ratemap.h" +c AstRateMap *astRateMap( AstMapping *map, int ax1, int ax2, +c const char *options, ... ) +f RESULT = AST_RATEMAP( MAP, AX1, AX2, OPTIONS, STATUS ) + +* Class Membership: +* RateMap constructor. + +* Description: +* This function creates a new RateMap and optionally initialises +* its attributes. +* +* A RateMap is a Mapping which represents a single element of the +* Jacobian matrix of another Mapping. The Mapping for which the +* Jacobian is required is specified when the new RateMap is created, +* and is referred to as the "encapsulated Mapping" below. +* +* The number of inputs to a RateMap is the same as the number of inputs +* to its encapsulated Mapping. The number of outputs from a RateMap +* is always one. This one output equals the rate of change of a +* specified output of the encapsulated Mapping with respect to a +* specified input of the encapsulated Mapping (the input and output +* to use are specified when the RateMap is created). +* +* A RateMap which has not been inverted does not define an inverse +* transformation. If a RateMap has been inverted then it will define +* an inverse transformation but not a forward transformation. + +* Parameters: +c map +f MAP = INTEGER (Given) +* Pointer to the encapsulated Mapping. +c ax1 +f AX1 = INTEGER (Given) +* Index of the output from the encapsulated Mapping for which the +* rate of change is required. This corresponds to the delta +* quantity forming the numerator of the required element of the +* Jacobian matrix. The first axis has index 1. +c ax2 +f AX2 = INTEGER (Given) +* Index of the input to the encapsulated Mapping which is to be +* varied. This corresponds to the delta quantity forming the +* denominator of the required element of the Jacobian matrix. +* The first axis has index 1. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new RateMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new RateMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astRateMap() +f AST_RATEMAP = INTEGER +* A pointer to the new RateMap. + +* Notes: +* - The forward transformation of the encapsulated Mapping must be +* defined. +c - Note that the component Mappings supplied are not copied by +c astRateMap (the new RateMap simply retains a reference to +c them). They may continue to be used for other purposes, but +c should not be deleted. If a RateMap containing a copy of its +c component Mappings is required, then a copy of the RateMap should +c be made using astCopy. +f - Note that the component Mappings supplied are not copied by +f AST_RATEMAP (the new RateMap simply retains a reference to +f them). They may continue to be used for other purposes, but +f should not be deleted. If a RateMap containing a copy of its +f component Mappings is required, then a copy of the RateMap should +f be made using AST_COPY. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astRateMap constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astRateMap_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "map" parameter is of type +* (void *) and is converted from an ID value to a pointer and +* validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astRateMap_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRateMap *new; /* Pointer to new RateMap */ + AstMapping *map; /* Pointer to Mapping structure */ + va_list args; /* Variable argument list */ + +/* Pointer to inherited status value */ + int *status; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain the Mapping pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Mapping. */ + map = astVerifyMapping( astMakePointer( map_void ) ); + if ( astOK ) { + +/* Initialise the RateMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitRateMap( NULL, sizeof( AstRateMap ), !class_init, &class_vtab, + "RateMap", map, ax1 - 1, ax2 - 1 ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new RateMap's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new RateMap. */ + return astMakeId( new ); +} + +AstRateMap *astInitRateMap_( void *mem, size_t size, int init, + AstRateMapVtab *vtab, const char *name, + AstMapping *map, int ax1, int ax2, int *status ) { +/* +*+ +* Name: +* astInitRateMap + +* Purpose: +* Initialise a RateMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "ratemap.h" +* AstRateMap *astInitRateMap( void *mem, size_t size, int init, +* AstRateMapVtab *vtab, const char *name, +* AstMapping *map, int ax1, int ax2 ) + +* Class Membership: +* RateMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new RateMap object. It allocates memory (if necessary) to +* accommodate the RateMap plus any additional data associated with the +* derived class. It then initialises a RateMap structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a RateMap at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the RateMap is to be initialised. +* This must be of sufficient size to accommodate the RateMap data +* (sizeof(RateMap)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the RateMap (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* RateMap structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the RateMap's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new RateMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* map +* Pointer to the Mapping. +* ax1 +* Zero-based index of output axis. +* ax2 +* Zero-based index of input axis. + +* Returned Value: +* A pointer to the new RateMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstRateMap *new; /* Pointer to new RateMap */ + int nin; /* No. input coordinates for RateMap */ + int nout; /* No. output coordinates for RateMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitRateMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Report an error if "map" has no forward transformation. */ + if( !astGetTranForward( map ) && astOK ) { + astError( AST__INTRD, "astInitRateMap(%s): The supplied Mapping " + "is not able to transform coordinates in the forward direction.", status, + name ); + } + +/* Check that the input and output axis indices are valid. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + if( ( ax1 < 0 || ax1 >= nout ) && astOK ) { + astError( AST__INNCO, "astInitRateMap(%s): The output axis %d is out " + "of range - it should be in the range 1 to %d.", status, name, + ax1 + 1, nout ); + } + if( ( ax2 < 0 || ax2 >= nin ) && astOK ) { + astError( AST__INNCO, "astInitRateMap(%s): The input axis %d is out " + "of range - it should be in the range 1 to %d.", status, name, + ax2 + 1, nin ); + } + +/* Initialise a Mapping structure (the parent class) as the first component + within the RateMap structure, allocating memory if necessary. Specify + the number of input and output coordinates and in which directions the + Mapping should be defined. */ + if ( astOK ) { + new = (AstRateMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, 1, 1, 0 ); + + if ( astOK ) { + +/* Initialise the RateMap data. */ +/* --------------------------- */ +/* Store a pointer to the encapsulated Mapping. */ + new->map = astClone( map ); + +/* Save the initial values of the inversion flag for this Mapping. */ + new->invert = astGetInvert( map ); + +/* Save the input and output axis indices. */ + new->iout = ax1; + new->iin = ax2; + +/* If an error occurred, clean up by annulling the Mapping pointers and + deleting the new object. */ + if ( !astOK ) { + new->map = astAnnul( new->map ); + new = astDelete( new ); + } + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstRateMap *astLoadRateMap_( void *mem, size_t size, + AstRateMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadRateMap + +* Purpose: +* Load a RateMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "ratemap.h" +* AstRateMap *astLoadRateMap( void *mem, size_t size, +* AstRateMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* RateMap loader. + +* Description: +* This function is provided to load a new RateMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* RateMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a RateMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the RateMap is to be +* loaded. This must be of sufficient size to accommodate the +* RateMap data (sizeof(RateMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the RateMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the RateMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstRateMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new RateMap. If this is NULL, a pointer to +* the (static) virtual function table for the RateMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "RateMap" is used instead. + +* Returned Value: +* A pointer to the new RateMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRateMap *new; /* Pointer to the new RateMap */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this RateMap. In this case the + RateMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstRateMap ); + vtab = &class_vtab; + name = "RateMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitRateMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built RateMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "RateMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Invert flag. */ +/* ------------ */ + new->invert = astReadInt( channel, "inv", 0 ); + new->invert = ( new->invert != 0 ); + +/* Input and output axes. */ +/* ---------------------- */ + new->iin = astReadInt( channel, "iin", 0 ); + new->iout = astReadInt( channel, "iout", 0 ); + +/* Mapping. */ +/* -------- */ + new->map = astReadObject( channel, "map", NULL ); + +/* If an error occurred, clean up by deleting the new RateMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new RateMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* None. */ + + + + diff --git a/ratemap.h b/ratemap.h new file mode 100644 index 0000000..b26bbd6 --- /dev/null +++ b/ratemap.h @@ -0,0 +1,276 @@ +#if !defined( RATEMAP_INCLUDED ) /* Include this file only once */ +#define RATEMAP_INCLUDED +/* +*+ +* Name: +* ratemap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the RateMap class. + +* Invocation: +* #include "ratemap.h" + +* Description: +* This include file defines the interface to the RateMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The RateMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astMapMerge +* Merge a RateMap within a sequence of Mappings. +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsARateMap +* Test class membership. +* astRateMap +* Create a RateMap. +* +* Protected: +* astCheckRateMap +* Validate class membership. +* astInitRateMap +* Initialise a RateMap. +* astInitRateMapVtab +* Initialise the virtual function table for the RateMap class. +* astLoadRateMap +* Load a RateMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstRateMap +* RateMap object type. +* +* Protected: +* AstRateMapVtab +* RateMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 7-DEC-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* RateMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstRateMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstMapping *map; /* Pointer to the Mapping */ + int invert; /* Inversion flag for Mapping */ + int iin; /* Index of Mapping input to vary */ + int iout; /* Index of Mapping output to measure */ +} AstRateMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstRateMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +/* None. */ +} AstRateMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstRateMapGlobals { + AstRateMapVtab Class_Vtab; + int Class_Init; +} AstRateMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitRateMapGlobals_( AstRateMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(RateMap) /* Check class membership */ +astPROTO_ISA(RateMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstRateMap *astRateMap_( void *, int, int, const char *, int *, ...); +#else +AstRateMap *astRateMapId_( void *, int, int, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstRateMap *astInitRateMap_( void *, size_t, int, AstRateMapVtab *, + const char *, AstMapping *, int, int, int * ); + +/* Vtab initialiser. */ +void astInitRateMapVtab_( AstRateMapVtab *, const char *, int * ); + +/* Loader. */ +AstRateMap *astLoadRateMap_( void *, size_t, AstRateMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +/* None. */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckRateMap(this) astINVOKE_CHECK(RateMap,this,0) +#define astVerifyRateMap(this) astINVOKE_CHECK(RateMap,this,1) + +/* Test class membership. */ +#define astIsARateMap(this) astINVOKE_ISA(RateMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astRateMap astINVOKE(F,astRateMap_) +#else +#define astRateMap astINVOKE(F,astRateMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitRateMap(mem,size,init,vtab,name,map,iin,iout) \ +astINVOKE(O,astInitRateMap_(mem,size,init,vtab,name,astCheckMapping(map),iin,iout,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitRateMapVtab(vtab,name) astINVOKE(V,astInitRateMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadRateMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadRateMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckRateMap to validate RateMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +/* None. */ +#endif + + + + + diff --git a/region.c b/region.c new file mode 100644 index 0000000..0089b27 --- /dev/null +++ b/region.c @@ -0,0 +1,13226 @@ +/* +*class++ +* Name: +* Region + +* Purpose: +* Represents a region within a coordinate system. + +* Constructor Function: +* None. + +* Description: +* This class provides the basic facilities for describing a region within +* a specified coordinate system. However, the Region class does not +* have a constructor function of its own, as it is simply a container +* class for a family of specialised sub-classes such as Circle, Box, etc, +* which implement Regions with particular shapes. +* +* All sub-classes of Region require a Frame to be supplied when the Region +* is created. This Frame describes the coordinate system in which the +* Region is defined, and is referred to as the "encapsulated Frame" below. +* Constructors will also typically required one or more positions to be +* supplied which define the location and extent of the region. These +* positions must be supplied within the encapsulated Frame. +* +* The Region class inherits from the Frame class, and so a Region can be +* supplied where-ever a Frame is expected. In these cases, supplying a +* Region is equivalent to supplying a reference to its encapsulated Frame. +* Thus all the methods of the Frame class can be used on the Region class. +* For instance, the +c astFormat function +f AST_FORMAT routine +* may be used on a Region to format an axis value. +* +* In addition, since Frame inherits from Mapping, a Region is also a sort +* of Mapping. Transforming positions by supplying a Region to one of the +c astTran functions +f AST_TRAN routines +* is the way to determine if a given position is inside or outside the +* Region. When used as a Mapping, most classes of Frame are equivalent to +* a UnitMap. However, the Region class modifies this behaviour so that a +* Region acts like a UnitMap only for input positions which are within the +* area represented by the Region. Input positions which are outside the +* area produce bad output values (i.e. the output values are equal to +* AST__BAD). This behaviour is the same for both the forward and the +* inverse transformation. In this sense the "inverse transformation" +* is not a true inverse of the forward transformation, since applying +* the forward transformation to a point outside the Region, and then +* applying the inverse transformation results, in a set of AST__BAD axis +* values rather than the original axis values. If required, the +c astRemoveRegions +f AST_REMOVEREGIONS +* function can be used to remove the "masking" effect of any Regions +* contained within a compound Mapping or FrameSet. It does this by +* replacing each Region with a UnitMap or equivalent Frame (depending +* on the context in which the Region is used). +* +* If the coordinate system represented by the Region is changed (by +* changing the values of one or more of the attribute which the Region +* inherits from its encapsulated Frame), the area represented by +* the Region is mapped into the new coordinate system. For instance, let's +* say a Circle (a subclass of Region) is created, a SkyFrame being +* supplied to the constructor so that the Circle describes a circular +* area on the sky in FK4 equatorial coordinates. Since Region inherits +* from Frame, the Circle will have a System attribute and this attribute +* will be set to "FK4". If the System attribute of the Region is then +* changed from FK4 to FK5, the circular area represented by the Region +* will automatically be mapped from the FK4 system into the FK5 system. +* In general, changing the coordinate system in this way may result in the +* region changing shape - for instance, a circle may change into an +* ellipse if the transformation from the old to the new coordinate system +* is linear but with different scales on each axis. Thus the specific +* class of a Region cannot be used as a guarantee of the shape in any +* particular coordinate system. If the +c astSimplify function +f AST_SIMPLIFY routine +* is used on a Region, it will endeavour to return a new Region of +* a sub-class which accurately describes the shape in the current +* coordinate system of the Region (but this may not always be possible). +* +* It is possible to negate an existing Region so that it represents all +* areas of the encapsulated Frame except for the area specified when +* the Region was created. + +* Inheritance: +* The Region class inherits from the Frame class. + +* Attributes: +* In addition to those attributes common to all Frames, every +* Region also has the following attributes: +* +* - Adaptive: Should the area adapt to changes in the coordinate system? +* - Negated: Has the original region been negated? +* - Closed: Should the boundary be considered to be inside the region? +* - MeshSize: Number of points used to create a mesh covering the Region +* - FillFactor: Fraction of the Region which is of interest +* - Bounded: Is the Region bounded? +* +* Every Region also inherits any further attributes that belong +* to the encapsulated Frame, regardless of that Frame's class. (For +* example, the Equinox attribute, defined by the SkyFrame class, is +* inherited by any Region which represents a SkyFrame.) + +* Functions: +c In addition to those functions applicable to all Frames, the +c following functions may also be applied to all Regions: +f In addition to those routines applicable to all Frames, the +f following routines may also be applied to all Regions: +* +c - astGetRegionBounds: Get the bounds of a Region +f - AST_GETREGIONBOUNDS: Get the bounds of a Region +c - astGetRegionFrame: Get a copy of the Frame represent by a Region +f - AST_GETREGIONFRAME: Get a copy of the Frame represent by a Region +c - astGetRegionFrameSet: Get a copy of the Frameset encapsulated by a Region +f - AST_GETREGIONFRAMESET: Get a copy of the Frameset encapsulated by a Region +c - astGetRegionMesh: Get a mesh of points covering a Region +f - AST_GETREGIONMESH: Get a mesh of points covering a Region +c - astGetRegionPoints: Get the positions that define a Region +f - AST_GETREGIONPOINTS: Get the positions that define a Region +c - astGetUnc: Obtain uncertainty information from a Region +f - AST_GETUNC: Obtain uncertainty information from a Region +c - astMapRegion: Transform a Region into a new coordinate system +f - AST_MAPREGION: Transform a Region into a new coordinate system +c - astNegate: Toggle the value of the Negated attribute +f - AST_NEGATE: Toggle the value of the Negated attribute +c - astOverlap: Determines the nature of the overlap between two Regions +f - AST_OVERLAP: Determines the nature of the overlap between two Regions +c - astMask: Mask a region of a data grid +f - AST_MASK: Mask a region of a data grid +c - astSetUnc: Associate a new uncertainty with a Region +f - AST_SETUNC: Associate a new uncertainty with a Region +c - astShowMesh: Display a mesh of points on the surface of a Region +f - AST_SHOWMESH: Display a mesh of points on the surface of a Region + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (STARLINK) + +* History: +* 3-DEC-2003 (DSB): +* Original version. +* 12-MAY-2005 (DSB): +* Override astNormBox method. +* 12-AUG-2005 (DSB): +* Override ObsLat and ObsLon accessor methods. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 2-MAR-2006 (DSB): +* Changed AST_LONG_DOUBLE to HAVE_LONG_DOUBLE. +* 14-MAR-2006 (DSB): +* Added astGetRefFS. +* 28-MAY-2007 (DSB): +* - Added protected function astBndMesh. +* 14-JAN-2009 (DSB): +* Override the astIntersect method. +* 20-JAN-2009 (DSB): +* Change astPickAxes so that it returns a Region rather than a +* Frame if possible. This included adding method astRegBasePick. +* 9-FEB-2009 (DSB): +* Move PointList methods astGetEnclosure and astSetEnclosure to +* Region. +* 18-FEB-2009 (DSB): +* Remove methods astGetEnclosure and astSetEnclosure. +* 15-JUN-2009 (DSB): +* Modify MapRegion to use FrameSets properly. +* 18-JUN-2009 (DSB): +* Override ObsAlt accessor methods. +* 7-SEP-2009 (DSB): +* Fix astMask to avoid reading variance values from the data array. +* 8-SEP-2009 (DSB): +* Fix bugs in astOverlap that could result in wrong results if +* either region is unbounded. +* 4-JAN-2010 (DSB): +* Fix bug in GetRegionBounds (it was assumed implicitly that the base +* Frame had the same number of axes as the current Frame). +* 18-MAR-2011 (DSB): +* Added astGetRegionMesh public method. +* 22-MAR-2011 (DSB): +* Improve uniformity of points produced by astRegBaseGrid method. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +* 17-MAY-2011 (DSB): +* In RegBaseGrid, accept the final try even if it is not within 5% +* of the required meshsize. +* 27-APR-2012 (DSB): +* Store a negated copy of itself with each Region. Changing the Negated +* attribute of a Region causes the cached information to be reset, and +* re-calculating it can be an expensive operation. So instead of changing +* "Negatated" in "this", access the negated copy of "this" using the +* new protected method astGetNegation. +* 7-JUN-2012 (DSB): +* Added protected astRegSplit method to split a Region into disjoint +* component regions. +* 15-JUN-2012 (DSB): +* Guard against division by zero in RegBase Grid if "ipr" is zero. +* 7-NOV-2013 (DSB): +* Added method astGetRegionFrameSet. +* 3-FEB-2014 (DSB): +* Fix bug masking regions that have no overlap with the supplied array. +* 17-APR-2015 (DSB): +* Added Centre. +*class-- + +* Implementation Notes: +* - All sub-classes must over-ride the following abstract methods declared +* in this class: astRegBaseBox, astRegBaseMesh, astRegPins, astRegCentre. +* They must also extend the astTransform method. In addition they should +* usually extend astSimplify. + +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Region + +/* Value for Ident attribute of of an encapsulated FrameSet which + indicates that it is a dummy FrameSet (see astRegDummy). */ +#define DUMMY_FS "ASTREGION-DUMMY" + +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Define a function to clear an attribute value for a Region. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_CLEAR(attribute) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear( AstFrame *this ) +* +* that clears the value of a specified attribute for the encapsulated +* FrameSet within a Region (this). This function is intended to over-ride +* the astClear method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute) \ +static void Clear##attribute( AstFrame *this_frame, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Obtain a pointer to the encapsulated FrameSet and invoke its \ + astClear method. The protected astClear##attribute method is not used \ + because we want the current Frame of the FrameSet tp be re-mapped if \ + necessary. */ \ + astClear( this->frameset, #attribute ); \ +} + +/* +* Name: +* MAKE_CLEAR_AXIS + +* Purpose: +* Define a function to clear an attribute value for a Region axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_CLEAR_AXIS(attribute) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear( AstFrame *this, int axis ) +* +* that clears the value of a specified attribute for an axis of +* the encapsulated FrameSet within a Region (this). This function is +* intended to over-ride the astClear method inherited +* from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR_AXIS(attribute) \ +static void Clear##attribute( AstFrame *this_frame, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ + char buf[100]; /* Buffer for attribute name */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astClear" #attribute ); \ +\ +/* We use the public astSetx method rather than the protected \ + astSet#attribute method so that the current Frame in the encapsulated \ + FrameSet will be re-mapped if necessary. Construct the attribute name. */ \ + sprintf( buf, "%s(%d)", #attribute, axis + 1 ); \ +\ +/* Obtain a pointer to the Region's encapsulated FrameSet and invoke its \ + astClear method. The protected astClear#attribute method is notused \ + since we want the current Frame of the encapsulated FrameSet to be \ + remapped if required. */ \ + astClear( this->frameset, buf ); \ +} + +/* +* Name: +* MAKE_GET + +* Purpose: +* Define a function to get an attribute value for a Region. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_GET(attribute,type) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static Get( AstFrame *this ) +* +* that gets the value of a specified attribute for the encapsulated +* FrameSet of a Region (this). This function is intended to over-ride +* the astGet method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_GET(attribute,type) \ +static type Get##attribute( AstFrame *this_frame, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ + type result; /* Value to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (type) 0; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Obtain a pointer to the encapsulated FrameSet and invoke its \ + astGet method. */ \ + result = astGet##attribute( this->frameset ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = (type) 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* Name: +* MAKE_GET_AXIS + +* Purpose: +* Define a function to get an attribute value for a Region axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_GET_AXIS(attribute,type) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static Get( AstFrame *this, int axis ) +* +* that gets the value of a specified attribute for an axis of the +* encapsulated FrameSet within a Region (this). This function is intended +* to over-ride the astGet method inherited from the Frame +* class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_GET_AXIS(attribute,type) \ +static type Get##attribute( AstFrame *this_frame, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ + type result; /* Value to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (type) 0; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astGet" #attribute ); \ +\ +/* Obtain a pointer to the Region's encapsulated FrameSet and invoke its \ + astGet method. */ \ + result = astGet##attribute( this->frameset, axis ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = (type) 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* Name: +* MAKE_SET_SYSTEM + +* Purpose: +* Define a function to set a System attribute value for a Region. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_SET_SYSTEM(attribute) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstFrame *this, AstSystemType value ) +* +* that sets the value of a specified attribute for the encapsulated +* FrameSet of a Region (this). This function is intended to over-ride the +* astSet method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_SET_SYSTEM(attribute) \ +static void Set##attribute( AstFrame *this_frame, AstSystemType value, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ + const char *text; /* Pointer to system string */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Convert the supplied value to a string using the astSystemString + method of the current Frame in the encapsulated FrameSet. */ \ + text = astSystemString( this->frameset, value ); \ +\ +/* Set the value by invoking the public astSetC method on the encapusulated \ + FrameSet. This ensures that the current Frame of the encapsulated \ + FrameSet is re-mapped if necessary. */ \ + astSetC( this->frameset, #attribute, text ); \ +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Define a function to set an attribute value for a Region. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_SET(attribute,type,x) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstFrame *this, value ) +* +* that sets the value of a specified attribute for the encapsulated +* FrameSet of a Region (this). This function is intended to over-ride the +* astSet method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +* x +* The single character code for the astSetx function for the given C +* type. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,type,x) \ +static void Set##attribute( AstFrame *this_frame, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Set the value by invoking the public astSetx method on the encapusulated \ + FrameSet. This ensures that the current Frame of the encapsulated \ + FrameSet is re-mapped if necessary. */ \ + astSet##x( this->frameset, #attribute, value ); \ +} + +/* +* Name: +* MAKE_SET_AXIS + +* Purpose: +* Define a function to set an attribute value for a Region axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_SET_AXIS(attribute,type,x) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstFrame *this, int axis, value ) +* +* that sets the value of a specified attribute for an axis of the +* encapsulated FrameSet within a Region (this). This function is intended +* to over-ride the astSet method inherited from the Frame +* class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +* x +* The single character code for the astSetx function for the given C +* type. +*/ + +/* Define the macro. */ +#define MAKE_SET_AXIS(attribute,type,x) \ +static void Set##attribute( AstFrame *this_frame, int axis, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ + char buf[100]; /* Buffer for attribute name */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astSet" #attribute ); \ +\ +/* We use the public astSetx method rather than the protected \ + astSet#attribute method so that the current Frame in the encapsulated \ + FrameSet will be re-mapped if necessary. Construct the attribute name. */ \ + sprintf( buf, "%s(%d)", #attribute, axis + 1 ); \ +\ +/* Obtain a pointer to the Region's encapsulated FrameSet and invoke its \ + astSet method. */ \ + astSet##x( this->frameset, buf, value ); \ +} + +/* +* Name: +* MAKE_TEST + +* Purpose: +* Define a function to test if an attribute value is set for a Region. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_TEST(attribute) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static int Test( AstFrame *this ) +* +* that returns a boolean result (0 or 1) to indicate if the value +* of a specified attribute for the encapsulated FrameSet within a +* Region (this) is set. This function is intended to over-ride the +* astTest method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_TEST(attribute) \ +static int Test##attribute( AstFrame *this_frame, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to Region structure */ \ + int result; /* Result to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Obtain a pointer to the Region's encapsulated FrameSet and invoke its \ + astTest method. */ \ + result = astTest##attribute( this->frameset ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* +* Name: +* MAKE_TEST_AXIS + +* Purpose: +* Define a function to test if an attribute value is set for a Region +* axis. + +* Type: +* Private macro. + +* Synopsis: +* #include "region.h" +* MAKE_TEST_AXIS(attribute) + +* Class Membership: +* Defined by the Region class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static int Test( AstFrame *this, int axis ) +* +* that returns a boolean result (0 or 1) to indicate if the value +* of a specified attribute for an axis of the encapsulated FrameSet +* within a Region (this) is set. This function is intended to over-ride +* the astTest method inherited from the Frame class. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +*/ + +/* Define the macro. */ +#define MAKE_TEST_AXIS(attribute) \ +static int Test##attribute( AstFrame *this_frame, int axis, int *status ) { \ +\ +/* Local Variables: */ \ + AstRegion *this; /* Pointer to the Region structure */ \ + int result; /* Value to return */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Obtain a pointer to the Region structure. */ \ + this = (AstRegion *) this_frame; \ +\ +/* Validate the axis index supplied. */ \ + (void) astValidateAxis( this, axis, 1, "astTest" #attribute ); \ +\ +/* Obtain a pointer to the Region's encapsulated FrameSet and invoke its \ + astTest method. */ \ + result = astTest##attribute( this->frameset, axis ); \ +\ +/* If an error occurred, clear the result value. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "mapping.h" /* Coordinate Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Coordinate permutation Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "frame.h" /* Parent Frame class */ +#include "frameset.h" /* Interconnected coordinate systems */ +#include "region.h" /* Interface definition for this class */ +#include "circle.h" /* Circular regions */ +#include "box.h" /* Box regions */ +#include "cmpregion.h" /* Compound regions */ +#include "ellipse.h" /* Elliptical regions */ +#include "pointset.h" /* Sets of points */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_getusedefs)( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Region) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Region,Class_Init) +#define class_vtab astGLOBAL(Region,Class_Vtab) +#define getattrib_buff astGLOBAL(Region,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstRegionVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +static int MaskLD( AstRegion *, AstMapping *, int, int, const int[], const int ubnd[], long double [], long double, int * ); +#endif +static int MaskB( AstRegion *, AstMapping *, int, int, const int[], const int[], signed char[], signed char, int * ); +static int MaskD( AstRegion *, AstMapping *, int, int, const int[], const int[], double[], double, int * ); +static int MaskF( AstRegion *, AstMapping *, int, int, const int[], const int[], float[], float, int * ); +static int MaskI( AstRegion *, AstMapping *, int, int, const int[], const int[], int[], int, int * ); +static int MaskL( AstRegion *, AstMapping *, int, int, const int[], const int[], long int[], long int, int * ); +static int MaskS( AstRegion *, AstMapping *, int, int, const int[], const int[], short int[], short int, int * ); +static int MaskUB( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned char[], unsigned char, int * ); +static int MaskUI( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned int[], unsigned int, int * ); +static int MaskUL( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned long int[], unsigned long int, int * ); +static int MaskUS( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned short int[], unsigned short int, int * ); + +static AstAxis *GetAxis( AstFrame *, int, int * ); +static AstFrame *GetRegionFrame( AstRegion *, int * ); +static AstFrameSet *GetRegionFrameSet( AstRegion *, int * ); +static AstFrame *PickAxes( AstFrame *, int, const int[], AstMapping **, int * ); +static AstFrame *RegFrame( AstRegion *, int * ); +static AstFrameSet *Conv( AstFrameSet *, AstFrameSet *, int * ); +static AstFrameSet *Convert( AstFrame *, AstFrame *, const char *, int * ); +static AstFrameSet *ConvertX( AstFrame *, AstFrame *, const char *, int * ); +static AstFrameSet *FindFrame( AstFrame *, AstFrame *, const char *, int * ); +static AstFrameSet *GetRegFS( AstRegion *, int * ); +static AstLineDef *LineDef( AstFrame *, const double[2], const double[2], int * ); +static AstMapping *RegMapping( AstRegion *, int * ); +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstObject *Cast( AstObject *, AstObject *, int * ); +static AstPointSet *BTransform( AstRegion *, AstPointSet *, int, AstPointSet *, int * ); +static AstPointSet *BndBaseMesh( AstRegion *, double *, double *, int * ); +static AstPointSet *BndMesh( AstRegion *, double *, double *, int * ); +static AstPointSet *GetSubMesh( int *, AstPointSet *, int * ); +static AstPointSet *RegBaseGrid( AstRegion *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *RegGrid( AstRegion *, int * ); +static AstPointSet *RegMesh( AstRegion *, int * ); +static AstPointSet *RegTransform( AstRegion *, AstPointSet *, int, AstPointSet *, AstFrame **, int * ); +static AstPointSet *ResolvePoints( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); +static AstRegion *MapRegion( AstRegion *, AstMapping *, AstFrame *, int * ); +static AstRegion *RegBasePick( AstRegion *, int, const int *, int * ); +static AstRegion **RegSplit( AstRegion *, int *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static const char *Abbrev( AstFrame *, int, const char *, const char *, const char *, int * ); +static const char *Format( AstFrame *, int, double, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static const int *GetPerm( AstFrame *, int * ); +static double *RegCentre( AstRegion *, double *, double **, int, int, int * ); +static double Angle( AstFrame *, const double[], const double[], const double[], int * ); +static double AxAngle( AstFrame *, const double[], const double[], int, int * ); +static double AxDistance( AstFrame *, int, double, double, int * ); +static double AxOffset( AstFrame *, int, double, double, int * ); +static double Distance( AstFrame *, const double[], const double[], int * ); +static double Centre( AstFrame *, int, double, double, int * ); +static double Gap( AstFrame *, int, double, int *, int * ); +static double Offset2( AstFrame *, const double[2], double, double, double[2], int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetNaxes( AstFrame *, int * ); +static int GetObjSize( AstObject *, int * ); +static int GetUseDefs( AstObject *, int * ); +static int IsUnitFrame( AstFrame *, int * ); +static int LineContains( AstFrame *, AstLineDef *, int, double *, int * ); +static int LineCrossing( AstFrame *, AstLineDef *, AstLineDef *, double **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int Overlap( AstRegion *, AstRegion *, int * ); +static int OverlapX( AstRegion *, AstRegion *, int * ); +static int RegDummyFS( AstRegion *, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static int Unformat( AstFrame *, int, const char *, double *, int * ); +static int ValidateAxis( AstFrame *, int, int, const char *, int * ); +static void CheckPerm( AstFrame *, const int *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GetRegionBounds( AstRegion *, double *, double *, int * ); +static void GetRegionBounds2( AstRegion *, double *, double *, int * ); +static void GetRegionMesh( AstRegion *, int, int, int, int *, double *, int * ); +static void GetRegionPoints( AstRegion *, int, int, int *, double *, int * ); +static void Intersect( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * ); +static void LineOffset( AstFrame *, AstLineDef *, double, double, double[2], int * ); +static void MatchAxes( AstFrame *, AstFrame *, int *, int * ); +static void MatchAxesX( AstFrame *, AstFrame *, int *, int * ); +static void Negate( AstRegion *, int * ); +static void Norm( AstFrame *, double[], int * ); +static void NormBox( AstFrame *, double[], double[], AstMapping *, int * ); +static void Offset( AstFrame *, const double[], const double[], double, double[], int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void PermAxes( AstFrame *, const int[], int * ); +static void RegBaseBox( AstRegion *, double *, double *, int * ); +static void RegBaseBox2( AstRegion *, double *, double *, int * ); +static void RegClearAttrib( AstRegion *, const char *, char **, int * ); +static void RegOverlay( AstRegion *, AstRegion *, int, int * ); +static void RegSetAttrib( AstRegion *, const char *, char **, int * ); +static void ReportPoints( AstMapping *, int, AstPointSet *, AstPointSet *, int * ); +static void ResetCache( AstRegion *, int * ); +static void Resolve( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); +static void SetAxis( AstFrame *, int, AstAxis *, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); +static void ShowMesh( AstRegion *, int, const char *, int * ); +static void ValidateAxisSelection( AstFrame *, int, const int *, const char *, int * ); +static AstRegion *GetNegation( AstRegion *, int * ); + +static int GetBounded( AstRegion *, int * ); +static AstRegion *GetDefUnc( AstRegion *, int * ); + +static AstRegion *GetUncFrm( AstRegion *, int, int * ); +static AstRegion *GetUnc( AstRegion *, int, int * ); +static int TestUnc( AstRegion *, int * ); +static void ClearUnc( AstRegion *, int * ); +static void SetUnc( AstRegion *, AstRegion *, int * ); + +static const char *GetDomain( AstFrame *, int * ); +static int TestDomain( AstFrame *, int * ); +static void ClearDomain( AstFrame *, int * ); +static void SetDomain( AstFrame *, const char *, int * ); + +static const char *GetFormat( AstFrame *, int, int * ); +static int TestFormat( AstFrame *, int, int * ); +static void ClearFormat( AstFrame *, int, int * ); +static void SetFormat( AstFrame *, int, const char *, int * ); + +static const char *GetLabel( AstFrame *, int, int * ); +static int TestLabel( AstFrame *, int, int * ); +static void ClearLabel( AstFrame *, int, int * ); +static void SetLabel( AstFrame *, int, const char *, int * ); + +static const char *GetSymbol( AstFrame *, int, int * ); +static int TestSymbol( AstFrame *, int, int * ); +static void ClearSymbol( AstFrame *, int, int * ); +static void SetSymbol( AstFrame *, int, const char *, int * ); + +static const char *GetTitle( AstFrame *, int * ); +static void SetTitle( AstFrame *, const char *, int * ); +static void ClearTitle( AstFrame *, int * ); +static int TestTitle( AstFrame *, int * ); + +static const char *GetUnit( AstFrame *, int, int * ); +static int TestUnit( AstFrame *, int, int * ); +static void ClearUnit( AstFrame *, int, int * ); +static void SetUnit( AstFrame *, int, const char *, int * ); + +static int GetDigits( AstFrame *, int * ); +static int TestDigits( AstFrame *, int * ); +static void ClearDigits( AstFrame *, int * ); +static void SetDigits( AstFrame *, int, int * ); + +static int GetDirection( AstFrame *, int, int * ); +static int TestDirection( AstFrame *, int, int * ); +static void ClearDirection( AstFrame *, int, int * ); +static void SetDirection( AstFrame *, int, int, int * ); + +static int GetActiveUnit( AstFrame *, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static void SetActiveUnit( AstFrame *, int, int * ); + +static int GetMatchEnd( AstFrame *, int * ); +static int TestMatchEnd( AstFrame *, int * ); +static void ClearMatchEnd( AstFrame *, int * ); +static void SetMatchEnd( AstFrame *, int, int * ); + +static int GetMaxAxes( AstFrame *, int * ); +static int TestMaxAxes( AstFrame *, int * ); +static void ClearMaxAxes( AstFrame *, int * ); +static void SetMaxAxes( AstFrame *, int, int * ); + +static int GetMinAxes( AstFrame *, int * ); +static int TestMinAxes( AstFrame *, int * ); +static void ClearMinAxes( AstFrame *, int * ); +static void SetMinAxes( AstFrame *, int, int * ); + +static int GetPermute( AstFrame *, int * ); +static int TestPermute( AstFrame *, int * ); +static void ClearPermute( AstFrame *, int * ); +static void SetPermute( AstFrame *, int, int * ); + +static int GetPreserveAxes( AstFrame *, int * ); +static int TestPreserveAxes( AstFrame *, int * ); +static void ClearPreserveAxes( AstFrame *, int * ); +static void SetPreserveAxes( AstFrame *, int, int * ); + +static double GetBottom( AstFrame *, int, int * ); +static int TestBottom( AstFrame *, int, int * ); +static void ClearBottom( AstFrame *, int, int * ); +static void SetBottom( AstFrame *, int, double, int * ); + +static double GetTop( AstFrame *, int, int * ); +static int TestTop( AstFrame *, int, int * ); +static void ClearTop( AstFrame *, int, int * ); +static void SetTop( AstFrame *, int, double, int * ); + +static double GetEpoch( AstFrame *, int * ); +static int TestEpoch( AstFrame *, int * ); +static void ClearEpoch( AstFrame *, int * ); +static void SetEpoch( AstFrame *, double, int * ); + +static double GetObsAlt( AstFrame *, int * ); +static int TestObsAlt( AstFrame *, int * ); +static void ClearObsAlt( AstFrame *, int * ); +static void SetObsAlt( AstFrame *, double, int * ); + +static double GetObsLat( AstFrame *, int * ); +static int TestObsLat( AstFrame *, int * ); +static void ClearObsLat( AstFrame *, int * ); +static void SetObsLat( AstFrame *, double, int * ); + +static double GetObsLon( AstFrame *, int * ); +static int TestObsLon( AstFrame *, int * ); +static void ClearObsLon( AstFrame *, int * ); +static void SetObsLon( AstFrame *, double, int * ); + +static AstSystemType GetSystem( AstFrame *, int * ); +static int TestSystem( AstFrame *, int * ); +static void ClearSystem( AstFrame *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); + +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static int TestAlignSystem( AstFrame *, int * ); +static void ClearAlignSystem( AstFrame *, int * ); +static void SetAlignSystem( AstFrame *, AstSystemType, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static int GetNegated( AstRegion *, int * ); +static int TestNegated( AstRegion *, int * ); +static void ClearNegated( AstRegion *, int * ); +static void SetNegated( AstRegion *, int, int * ); + +static int GetClosed( AstRegion *, int * ); +static int TestClosed( AstRegion *, int * ); +static void ClearClosed( AstRegion *, int * ); +static void SetClosed( AstRegion *, int, int * ); + +static int GetMeshSize( AstRegion *, int * ); +static int TestMeshSize( AstRegion *, int * ); +static void ClearMeshSize( AstRegion *, int * ); +static void SetMeshSize( AstRegion *, int, int * ); + +static double GetFillFactor( AstRegion *, int * ); +static int TestFillFactor( AstRegion *, int * ); +static void ClearFillFactor( AstRegion *, int * ); +static void SetFillFactor( AstRegion *, double, int * ); + +static int GetRegionFS( AstRegion *, int * ); +static int TestRegionFS( AstRegion *, int * ); +static void ClearRegionFS( AstRegion *, int * ); +static void SetRegionFS( AstRegion *, int, int * ); + +static int GetAdaptive( AstRegion *, int * ); +static int TestAdaptive( AstRegion *, int * ); +static void ClearAdaptive( AstRegion *, int * ); +static void SetAdaptive( AstRegion *, int, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Member functions. */ +/* ================= */ + +static const char *Abbrev( AstFrame *this_frame, int axis, const char *fmt, + const char *str1, const char *str2, int *status ) { +/* +* Name: +* Abbrev + +* Purpose: +* Abbreviate a formatted Region axis value by skipping leading fields. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* const char *Abbrev( AstFrame *this, int axis, const char *fmt, +* const char *str1, const char *str2, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astAbbrev +* method inherited from the Frame class). + +* Description: +* This function compares two Region axis values that have been +* formatted (using astFormat) and determines if they have any +* redundant leading fields (i.e. leading fields in common which +* can be suppressed when tabulating the values or plotting them on +* the axis of a graph). + +* Parameters: +* this +* Pointer to the Region +* axis +* The number of the Region axis for which the values have +* been formatted (axis numbering starts at zero for the first +* axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format specification used to format the two values. +* str1 +* Pointer to a constant null-terminated string containing the +* first formatted value. +* str1 +* Pointer to a constant null-terminated string containing the +* second formatted value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer into the "str2" string which locates the first +* character in the first field that differs between the two +* formatted values. +* +* If the two values have no leading fields in common, the returned +* value will point at the start of string "str2". If the two +* values are equal, it will point at the terminating null at the +* end of this string. + +* Notes: +* - This function assumes that the format specification used was +* the same when both values were formatted and that they both +* apply to the same Region axis. +* - A pointer to the start of "str2" will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return str2; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astAbbrev" ); + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astAbbrev method to perform the processing. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astAbbrev( fr, axis, fmt, str1, str2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = str2; + +/* Return the result. */ + return result; +} + +static double Angle( AstFrame *this_frame, const double a[], + const double b[], const double c[], int *status ) { +/* +* Name: +* Angle + +* Purpose: +* Calculate the angle subtended by two points at a third point. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double Angle( AstFrame *this, const double a[], const double b[], +* const double c[], int *status ) + +* Class Membership: +* Region member function (over-rides the protected astAngle +* method inherited from the Frame class). + +* Description: +* This function finds the angle at point B between the line joining points +* A and B, and the line joining points C and B. These lines will in fact be +* geodesic curves appropriate to the Frame in use. For instance, in +* SkyFrame, they will be great circles. + +* Parameters: +* this +* Pointer to the Frame. +* a +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the first point. +* b +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the second point. +* c +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the third point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* astAngle +* The angle in radians, from the line AB to the line CB. If the +* Frame is 2-dimensional, it will be in the range $\pm \pi$, +* and positive rotation is in the same sense as rotation from +* the positive direction of axis 2 to the positive direction of +* axis 1. If the Frame has more than 2 axes, a positive value will +* always be returned in the range zero to $\pi$. + +* Notes: +* - A value of AST__BAD will also be returned if points A and B are +* co-incident, or if points B and C are co-incident. +* - A value of AST__BAD will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke this + Frame's astAngle method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astAngle( fr, a, b, c ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static double AxAngle( AstFrame *this_frame, const double a[], const double b[], int axis, int *status ) { +/* +* Name: +* AxAngle + +* Purpose: +* Returns the angle from an axis, to a line through two points. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double AxAngle( AstFrame *this, const double a[], const double b[], int axis, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astAxAngle +* method inherited from the Frame class). + +* Description: +* This function finds the angle, as seen from point A, between the positive +* direction of a specified axis, and the geodesic curve joining point +* A to point B. + +* Parameters: +* this +* Pointer to the Frame. +* a +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the first point. +* b +* An array of double, with one element for each Frame axis +* (Naxes attribute) containing the coordinates of the second point. +* axis +* The number of the Frame axis from which the angle is to be +* measured (one-based) +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The angle in radians, from the positive direction of the +* specified axis, to the line AB. If the Frame is 2-dimensional, +* it will be in the range $\pm \pi$, and positive rotation is in +* the same sense as rotation from the positive direction of axis 2 +* to the positive direction of axis 1. If the Frame has more than 2 +* axes, a positive value will always be returned in the range zero +* to $\pi$. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the require +* position angle is undefined. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxAngle" ); + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astAxAngle method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astAxAngle( fr, a, b, axis ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static double AxDistance( AstFrame *this_frame, int axis, double v1, double v2, int *status ) { +/* +* Name: +* AxDistance + +* Purpose: +* Find the distance between two axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double AxDistance( AstFrame *this, int axis, double v1, double v2, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astAxDistance +* method inherited from the Frame class). + +* Description: +* This function returns a signed value representing the axis increment +* from axis value v1 to axis value v2. +* +* For a simple Frame, this is a trivial operation returning the +* difference between the two axis values. But for other derived classes +* of Frame (such as a SkyFrame) this is not the case. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +* v1 +* The first axis value. +* v2 +* The second axis value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The distance between the two axis values. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input vaues has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxDistance" ); + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astAxDistance method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astAxDistance( fr, axis, v1, v2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static double AxOffset( AstFrame *this_frame, int axis, double v1, double dist, int *status ) { +/* +* Name: +* AxOffset + +* Purpose: +* Add an increment onto a supplied axis value. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double AxOffset( AstFrame *this, int axis, double v1, double dist, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astAxOffset +* method inherited from the Frame class). + +* Description: +* This function returns an axis value formed by adding a signed axis +* increment onto a supplied axis value. +* +* For a simple Frame, this is a trivial operation returning the +* sum of the two supplied values. But for other derived classes +* of Frame (such as a SkyFrame) this is not the case. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The index of the axis to which the supplied values refer. The +* first axis has index 1. +* v1 +* The original axis value. +* dist +* The axis increment to add to the original axis value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The incremented axis value. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input vaues has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis - 1, 1, "astAxOffset" ); + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astAxOffset method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astAxOffset( fr, axis, v1, dist ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static AstPointSet *BndBaseMesh( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +*+ +* Name: +* astBndBaseMesh + +* Purpose: +* Return a PointSet containing points spread around part of the boundary +* of a Region, in the base Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstPointSet *astBndBaseMesh( AstRegion *this, double *lbnd, double *ubnd ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a PointSet containing a set of points on the +* boundary of the intersection between the supplied Region and the +* supplied (current Frame) box. The mesh points refer to the base +* Frame. If the boundary of the supplied Region does not intersect the +* supplied box, then a PointSet containing a single bad point is +* returned. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array holding the lower limits of the axis values +* within the required box. Defined in the current Frame of the Region. +* ubnd +* Pointer to an array holding the upper limits of the axis values +* within the required box. Defined in the current Frame of the Region. + +* Returned Value: +* Pointer to the PointSet holding the base Frame mesh. The axis values +* in this PointSet will have associated accuracies derived from the +* uncertainties which were supplied when the Region was created. +* +* If the Region does not intersect the supplied box, the returned +* PointSet will contain a single point with a value of AST__BAD on +* every axis. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstBox *box; + AstCmpRegion *cmpreg; + AstPointSet *result; + double **ptr; + int ic; + int nc; + +/* Check the local error status. */ + if ( !astOK ) return NULL; + +/* Form a Box describing the required box. */ + box = astBox( this, 1, lbnd, ubnd, NULL, "", status ); + +/* Check there is partial overlap between the Regions.*/ + if( astOverlap( this, box ) > 3 ) { + +/* Form a CmpRegion representing the intersection between the supplied + Region and the above box. */ + cmpreg = astCmpRegion( this, box, AST__AND, "", status ); + +/* Get the boundary mesh. */ + result = astRegBaseMesh( cmpreg ); + +/* Free resources. */ + cmpreg = astAnnul( cmpreg ); + +/* If the boundary of the supplied Region does not intersect the box, + return a PointSet containing a single bad position. */ + } else { + nc = astGetNin( this->frameset ); + result = astPointSet( 1, nc, "", status ); + ptr = astGetPoints( result ); + if( ptr ) { + for( ic = 0; ic < nc; ic++ ) ptr[ ic ][ 0 ] = AST__BAD; + } + } + +/* Free resources. */ + box = astAnnul( box ); + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +static AstPointSet *BndMesh( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +*+ +* Name: +* astBndMesh + +* Purpose: +* Return a PointSet containing points spread around part of the boundary +* of a Region, in the current Frame. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstPointSet *astBndMesh( AstRegion *this, double *lbnd, double *ubnd ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a PointSet containing a set of points on the +* boundary of the intersection between the supplied Region and the +* supplied box. The points refer to the current Frame of the +* encapsulated FrameSet. If the boundary of the supplied Region does +* not intersect the supplied box, then a PointSet containing a single +* bad point is returned. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array holding the lower limits of the axis values +* within the required box. Defined in the current Frame of the Region. +* ubnd +* Pointer to an array holding the upper limits of the axis values +* within the required box. Defined in the current base Frame of the +* Region. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the uncertainties which were +* supplied when the Region was created. +* +* If the Region does not intersect the supplied box, the returned +* PointSet will contain a single point with a value of AST__BAD on +* every axis. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstMapping *map; + AstPointSet *ps1; + AstPointSet *result; + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get the current->base Mapping from the Region. */ + map = astGetMapping( this->frameset, AST__CURRENT, AST__BASE ); + +/* Use astBndBaseMesh to get a mesh of base Frame points within this base + Frame bounding box. */ + ps1 = astBndBaseMesh( this, lbnd, ubnd ); + +/* Transform it into the current Frame. */ + if( ps1 ) result = astTransform( map, ps1, 0, NULL ); + +/* Free resources. */ + map = astAnnul( map ); + ps1 = astAnnul( ps1 ); + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +static AstPointSet *BTransform( AstRegion *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +*+ +* Name: +* astBTransform + +* Purpose: +* Use a Region to transform a set of points in the base Frame. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "circle.h" +* AstPointSet *astBTransform( AstRegion *this, AstPointSet *in, + int forward, AstPointSet *out ) + +* Class Membership: +* Region member function + +* Description: +* This function takes a Region and a set of points within the base +* Frame of the Region, and transforms the points by setting axis values +* to AST__BAD for all points which are outside the region. Points inside +* the region are copied unchanged from input to output. + +* Parameters: +* this +* Pointer to the Region. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - This is identical to the astTransform method for a Region except +* that the supplied and returned points refer to the base Frame of +* the Region, rather than the current Frame. +*- +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + int old; /* Origial value of "nomap" flag */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Save the current value of the "nomap" flag for this Region,and then + set it. Doing this tells the astRegMapping function (called by + astRegTransform) to assume a unit map connects base and current Frame. */ + old = this->nomap; + this->nomap = 1; + +/* Invoke the usual astTransform method. The above setting of the "nomap" + flag will cause the astTransform method to treat the base Frame as the + current Frame. */ + result = astTransform( this, in, forward, out ); + +/* Reset the "nomap" flag. */ + this->nomap = old; + +/* Return a pointer to the output PointSet. */ + return result; +} + +static AstObject *Cast( AstObject *this_object, AstObject *obj, int *status ) { +/* +* Name: +* Cast + +* Purpose: +* Cast an Object into an instance of a sub-class. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstObject *Cast( AstObject *this, AstObject *obj, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astCast +* method inherited from the Frame class). + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. Specifically, if "this" and "new" are +* of the same class, a copy of "this" is returned. If "this" is an +* instance of a subclass of "obj", then a copy of the component +* of "this" that matches the class of "obj" is returned. Otherwise, +* a NULL pointer is returned without error. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. NULL if "this" is not a sub-class of +* "obj", or if an error occurs. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables; */ + AstFrame *cfrm; + AstObject *new; + astDECLARE_GLOBALS + int generation_gap; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* See how many steps up the class inheritance ladder it is from "obj" + to this class (Region). A positive value is returned if Region is + a sub-class of "obj". A negative value is returned if "obj" is a + sub-class of Region. Zero is returned if "obj" is a Region. + AST__COUSIN is returned if "obj" is not on the same line of descent + as Region. */ + generation_gap = astClassCompare( (AstObjectVtab *) &class_vtab, + astVTAB( obj ) ); + +/* If "obj" is a Region or a sub-class of Region, we can cast by + truncating the vtab for "this" so that it matches the vtab of "obJ", + and then taking a deep copy of "this". */ + if( generation_gap <= 0 && generation_gap != AST__COUSIN ) { + new = astCastCopy( this_object, obj ); + +/* If "obj" is not a Region or a sub-class of Region (e.g. a Frame or + some sub-class of Frame), we attempt to cast the current Frame of the + encapsulated FrameSet into the class indicated by "obj". */ + } else { + cfrm = astGetFrame( ((AstRegion *) this_object)->frameset, AST__CURRENT ); + new = astCast( cfrm, obj ); + cfrm = astAnnul( cfrm ); + } + +/* Return the new pointer. */ + return new; +} + +static double Centre( AstFrame *this_frame, int axis, double value, double gap, int *status ) { +/* +* Name: +* Centre + +* Purpose: +* Find a "nice" central value for tabulating Frame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double Centre( AstFrame *this_frame, int axis, double value, +* double gap, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astCentre method +* inherited from the Frame class). + +* Description: +* This function returns an axis value which produces a nice formatted +* value suitable for a major tick mark on a plot axis, close to the +* supplied axis value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which a central value +* is to be found. +* value +* An arbitrary axis value in the section that is being plotted. +* gap +* The gap size. + +* Returned Value: +* The nice central axis value. + +* Notes: +* - A value of zero is returned if the supplied gap size is zero. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astCentre" ); + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astCentre method to obtain the required value. Annul the + Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astCentre( fr, axis, value, gap ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static void CheckPerm( AstFrame *this_frame, const int *perm, const char *method, int *status ) { +/* +* Name: +* CheckPerm + +* Purpose: +* Check that an array contains a valid permutation. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void CheckPerm( AstFrame *this, const int *perm, const char *method, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astCheckPerm +* method inherited from the Frame class). + +* Description: +* This function checks the validity of a permutation array that +* will be used to permute the order of a Frame's axes. If the +* permutation specified by the array is not valid, an error is +* reported and the global error status is set. Otherwise, the +* function returns without further action. + +* Parameters: +* this +* Pointer to the Frame. +* perm +* Pointer to an array of integers with the same number of +* elements as there are axes in the Frame. For each axis, the +* corresponding integer gives the (zero based) axis index to be +* used to identify the information for that axis (using the +* un-permuted axis numbering). To be valid, the integers in +* this array should therefore all lie in the range zero to +* (naxes-1) inclusive, where "naxes" is the number of Frame +* axes, and each value should occur exactly once. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate a permutation array. This method name is used +* solely for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Error messages issued by this function refer to the external +* (public) numbering system used for axes (which is one-based), +* whereas zero-based axis indices are used internally. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke this + Frame's astCheckPerm method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astCheckPerm( fr, perm, method ); + fr = astAnnul( fr ); + +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Region member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* Region, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Region. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* We first handle attributes that apply to the Region as a whole + (rather than to the encapsulated FrameSet). */ + +/* Negated */ +/* ------- */ + if ( !strcmp( attrib, "negated" ) ) { + astClearNegated( this ); + +/* Closed */ +/* ------ */ + } else if ( !strcmp( attrib, "closed" ) ) { + astClearClosed( this ); + +/* FillFactor */ +/* ---------- */ + } else if ( !strcmp( attrib, "fillfactor" ) ) { + astClearFillFactor( this ); + +/* MeshSize */ +/* -------- */ + } else if ( !strcmp( attrib, "meshsize" ) ) { + astClearMeshSize( this ); + +/* Adaptive */ +/* -------- */ + } else if ( !strcmp( attrib, "adaptive" ) ) { + astClearAdaptive( this ); + + +/* We now check for atttributes of superclasses which apply to the Region + as a whole. We do not want to pass these on to the encapsulated FrameSet. */ + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + astClearID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + astClearIdent( this ); + +/* Invert. */ +/* ------- */ + } else if ( !strcmp( attrib, "invert" ) ) { + astClearInvert( this ); + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + astClearReport( this ); + + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class (including those of all superclasses). + If it does, then report an error. */ + } else if ( !strcmp( attrib, "class" ) || + !strcmp( attrib, "nin" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "nout" ) || + !strcmp( attrib, "bounded" ) || + !strcmp( attrib, "refcount" ) || + !strcmp( attrib, "tranforward" ) || + !strcmp( attrib, "traninverse" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass unrecognised attributes on to the Region's encapsulated FrameSet for + further interpretation. Do not pass on FrameSet attributes since we + pretend to the outside world that the encapsulated FrameSet is actually a + Frame. */ + } else if ( strcmp( attrib, "base" ) && + strcmp( attrib, "current" ) && + strcmp( attrib, "nframe" ) ) { + +/* If the Region is to adapt to coordinate system chanmges, use the public + astClear method so that the current Frame in the encapsulated FrameSet will + be re-mapped if the attribute changes require it. */ + if( astGetAdaptive( this ) ) { + astClear( this->frameset, attrib ); + +/* If the Region is not to adapt to coordinate system chanmges, use the + astRegSetAttrib method which assigns the attribute setting to both + current and base Frames in the FrameSet without causing any remapping to + be performed. */ + } else { + astRegClearAttrib( this, attrib, NULL ); + } + } +} + +static AstFrameSet *Conv( AstFrameSet *from, AstFrameSet *to, int *status ){ +/* +* Name: +* Conv + +* Purpose: +* Find Mapping between Frames + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstFrameSet *Conv( AstFrameSet *from, AstFrameSet *to, int *status ); + +* Class Membership: +* Region member function + +* Description: +* This function provides a convenient interface for astConvert. +* It is like astConvert except it does not alter the base Frames of +* the supplied FrameSets and does not require a Domain list. + +* Parameters: +* from +* Pointer to the source FrameSet. +* to +* Pointer to the source FrameSet. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The conversion FrameSet (see astConvert). + +*/ + +/* Local Variables: */ + AstFrameSet *result; /* FrameSet to return */ + int from_base; /* Index of original base Frame in "from" */ + int to_base; /* Index of original base Frame in "to" */ + +/* Check the global error status. */ + if( !astOK ) return NULL; + +/* Note the indices of the base Frames in the FrameSets. */ + to_base = astGetBase( to ); + from_base = astGetBase( from ); + +/* Invoke astConvert. */ + result = astConvert( from, to, "" ); + +/* Re-instate original base Frames. */ + astSetBase( to, to_base ); + astSetBase( from, from_base ); + +/* Return the result. */ + return result; +} + +static AstFrameSet *Convert( AstFrame *from, AstFrame *to, + const char *domainlist, int *status ) { +/* +* Name: +* Convert + +* Purpose: +* Determine how to convert between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstFrameSet *Convert( AstFrame *from, AstFrame *to, +* const char *domainlist, int *status ) + +* Class Membership: +* Region member function (over-rides the public astConvert +* method inherited fromm the Frame class). + +* Description: +* This function compares two Regions and determines whether it +* is possible to convert between the coordinate systems which +* their current Frames represent. If conversion is possible, it +* returns a FrameSet which describes the conversion and which may +* be used (as a Mapping) to transform coordinate values in either +* direction. + +* Parameters: +* from +* Pointer to a Region whose current Frame represents the +* "source" coordinate system. Note that the Base attribute of +* the Region may be modified by this function. +* to +* Pointer to a Region whose current Frame represents the +* "destination" coordinate system. Note that the Base +* attribute of the Region may be modified by this function. +* domainlist +* Pointer to a null-terminated character string containing a +* comma-separated list of Frame domains. This may be used to +* define a priority order for the different intermediate +* coordinate systems that might be used to perform the +* conversion. +* +* The function will first try to obtain a conversion by making +* use only of intermediate Frames whose Domain attribute +* matches the first domain in this list. If this fails, the +* second domain in the list will be used, and so on, until +* conversion is achieved. A blank domain (e.g. two consecutive +* commas) indicates that all Frames should be considered, +* regardless of their Domain attributes. The list is +* case-insensitive and all white space is ignored. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the requested coordinate conversion is possible, the +* function returns a pointer to a FrameSet which describes the +* conversion. Otherwise, a null Object pointer (AST__NULL) is +* returned without error. +* +* If a FrameSet is returned, it will contain two Frames. Frame +* number 1 (its base Frame) will describe the source coordinate +* system, corresponding to the "from" parameter. Frame number 2 +* (its current Frame) will describe the destination coordinate +* system, corresponding to the "to" parameter. The Mapping +* which inter-relates these Frames will perform the required +* conversion between the two coordinate systems. + +* Notes: +* - The returned FrameSet will not contain any Regions. If one or +* more of the supplied Frames are in fact Regions, the corresponding +* Frames in any returned FrameSet will described the encapsulated +* Frame, without any region information. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrameSet *result; /* Returned FrameSet */ + +/* Check the inherited status. */ + if ( !astOK ) return NULL; + +/* If the "from" pointer is a Region, get a pointer to the current Frame of + the encapsulated FrameSet and use it instead of the supplied pointer. */ + if( astIsARegion( from ) ) { + from = astGetFrame( ((AstRegion *) from)->frameset, AST__CURRENT ); + } else { + from = astClone( from ); + } + +/* If the "to" pointer is a Region, get a pointer to the current Frame of + the encapsulated FrameSet and use it instead of the supplied pointer. */ + if( astIsARegion( to ) ) { + to = astGetFrame( ((AstRegion *) to)->frameset, AST__CURRENT ); + } else { + to = astClone( to ); + } + +/* Now invoke astConvert on the above Frames. */ + result = astConvert( from, to, domainlist ); + +/* Annul the pointers used above. */ + from = astAnnul( from ); + to = astAnnul( to ); + +/* Return the result */ + return result; +} + +static AstFrameSet *ConvertX( AstFrame *to, AstFrame *from, + const char *domainlist, int *status ) { +/* +* Name: +* ConvertX + +* Purpose: +* Determine how to convert between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstFrameSet *astConvertX( AstFrame *to, AstFrame *from, +* const char *domainlist ) + +* Class Membership: +* Region member function (over-rides the protected astConvertX +* method inherited from the Frame class). + +* Description: +* This function performs the processing for the public astConvert +* method and has exactly the same interface except that the order +* of the first two arguments is swapped. This is a trick to allow +* the astConvert method to be over-ridden by derived classes on +* the basis of the class of either of its first two arguments. +* +* See the astConvert method for details of the interface. +*- +*/ + +/* Local Variables: */ + AstFrameSet *result; /* Returned FrameSet */ + +/* Check the inherited status. */ + if ( !astOK ) return NULL; + +/* If the "to" pointer is a Region, get a pointer to the current Frame of + the encapsulated FrameSet and use it instead of the supplied pointer. */ + if( astIsARegion( to ) ) { + to = astGetFrame( ((AstRegion *) to)->frameset, AST__CURRENT ); + } else { + to = astClone( to ); + } + +/* If the "from" pointer is a Region, get a pointer to the current Frame of + the encapsulated FrameSet and use it instead of the supplied pointer. */ + if( astIsARegion( from ) ) { + from = astGetFrame( ((AstRegion *) from)->frameset, AST__CURRENT ); + } else { + from = astClone( from ); + } + +/* Now invoke astConvertX on the above Frames. */ + result = astConvertX( to, from, domainlist ); + +/* Annul the pointers used above. */ + from = astAnnul( from ); + to = astAnnul( to ); + +/* Return the result */ + return result; +} + +static double Distance( AstFrame *this_frame, const double point1[], + const double point2[], int *status ) { +/* +* Name: +* Distance + +* Purpose: +* Calculate the distance between two points. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double Distance( AstFrame *this, const double point1[], +* const double point2[], int *status ) + +* Class Membership: +* Region member function (over-rides the protected astDistance +* method inherited from the Frame class). + +* Description: +* This function finds the distance between two points whose +* Region coordinates are given. The distance calculated is that +* along the geodesic curve that joins the two points. + +* Parameters: +* this +* Pointer to the Region. +* point1 +* An array of double, with one element for each Region axis +* containing the coordinates of the first point. +* point2 +* An array of double, with one element for each Region axis +* containing the coordinates of the second point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The distance between the two points. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astDistance method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astDistance( fr, point1, point2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Objects are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int Equal( AstObject *this_object, AstObject *that_object, int *status ) + +* Class Membership: +* Region member function (over-rides the astEqual protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two Regions are equivalent. + +* Parameters: +* this +* Pointer to the first Region. +* that +* Pointer to the second Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the Regions are equivalent, zero otherwise. + +* Notes: +* - The Regions are equivalent if they are of the same class, have +* equal PointSets, have equal base Frames, have equal current Frames, +* and if the Mapping between base Frames is a UnitMap. In addition, the +* Negated attribute must have the same value in both Regions, as must +* the Closed attribute. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *bf1; + AstFrame *bf2; + AstFrame *cf1; + AstFrame *cf2; + AstMapping *m1; + AstMapping *m2; + AstRegion *that; + AstRegion *this; + const char *class1; + const char *class2; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the two objects have the same class. */ + class1 = astGetClass( this_object ); + class2 = astGetClass( that_object ); + if( astOK && !strcmp( class1, class2 ) ) { + +/* Obtain pointers to the two Region structures. */ + this = (AstRegion *) this_object; + that = (AstRegion *) that_object; + +/* Test their PointSets for equality. */ + if( astEqual( this->points, that->points ) ){ + +/* Test their base Frames for equality. */ + bf1 = astGetFrame( this->frameset, AST__BASE ); + bf2 = astGetFrame( that->frameset, AST__BASE ); + if( astEqual( bf1, bf2 ) ){ + +/* Test their current Frames for equality. */ + cf1 = astGetFrame( this->frameset, AST__CURRENT ); + cf2 = astGetFrame( that->frameset, AST__CURRENT ); + if( astEqual( cf1, cf2 ) ){ + +/* Get the two Mappings and check that they are equal */ + m1 = astGetMapping( this->frameset, AST__BASE, AST__CURRENT ); + m2 = astGetMapping( that->frameset, AST__BASE, AST__CURRENT ); + if( astEqual( m1, m2 ) ) { + +/* Test the Negated and Closed flags are equal */ + if( astGetNegated( this ) == astGetNegated( that ) && + astGetClosed( this ) == astGetClosed( that ) ) { + result = 1; + } + } + +/* Free resources. */ + m1 = astAnnul( m1 ); + m2 = astAnnul( m2 ); + } + + cf1 = astAnnul( cf1 ); + cf2 = astAnnul( cf2 ); + } + + bf1 = astAnnul( bf1 ); + bf2 = astAnnul( bf2 ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void ClearUnc( AstRegion *this, int *status ){ +/* +*+ +* Name: +* astClearUnc + +* Purpose: +* Erase any uncertainty information in a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astClearUnc( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function erases all uncertainty information, whether default +* or not, from a Region. + +* Parameters: +* this +* Pointer to the Region. + +*- +*/ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Annul any user-supplied uncertainty. Also indicate that cached + information may now be out of date. */ + if( this->unc ) { + this->unc = astAnnul( this->unc ); + astResetCache( this ); + } + +/* Annul any default uncertainty. */ + if( this->defunc ) this->defunc = astAnnul( this->defunc ); + +} + +static AstFrameSet *FindFrame( AstFrame *target_frame, AstFrame *template, + const char *domainlist, int *status ) { +/* +* Name: +* FindFrame + +* Purpose: +* Find a coordinate system with specified characteristics. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstFrameSet *FindFrame( AstFrame *target, AstFrame *template, +* const char *domainlist, int *status ) + +* Class Membership: +* Region member function (over-rides the astFindFrame method +* inherited from the Frame class). + +* Description: +* This function uses a "template" Frame to search a Region to +* identify a coordinate system which has a specified set of +* characteristics. If a suitable coordinate system can be found, +* the function returns a pointer to a FrameSet which describes the +* required coordinate system and how to convert coordinates to and +* from it. + +* Parameters: +* target +* Pointer to the target Region. +* template +* Pointer to the template Frame, which should be an instance of +* the type of Frame you wish to find. +* domainlist +* Pointer to a null-terminated character string containing a +* comma-separated list of Frame domains. This may be used to +* establish a priority order for the different types of +* coordinate system that might be found. +* +* The function will first try to find a suitable coordinate +* system whose Domain attribute equals the first domain in this +* list. If this fails, the second domain in the list will be +* used, and so on, until a result is obtained. A blank domain +* (e.g. two consecutive commas) indicates that any coordinate +* system is acceptable (subject to the template) regardless of +* its domain. +* +* This list is case-insensitive and all white space is ignored. +* If you do not wish to restrict the domain in this way, you +* should supply an empty string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If the search is successful, the function returns a pointer to a +* FrameSet which contains the Frame found and a description of how +* to convert to (and from) the coordinate system it +* represents. Otherwise, a null Object pointer (AST__NULL) is +* returned without error. +* +* If a FrameSet is returned, it will contain two Frames. Frame +* number 1 (its base Frame) represents the target coordinate +* system and will be the same as the target. Frame number 2 (its +* current Frame) will be a Frame representing the coordinate system +* which the function found. The Mapping which inter-relates these two +* Frames will describe how to convert between their respective coordinate +* systems. Note, the Frames in this FrameSet will not be Regions - +* that is, they will be simple Frames or other derived classes. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + AstFrameSet *result; /* Pointer to result FrameSet */ + AstFrame *fr; /* Pointer to encapsulated Frame */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the astFindFrame method on the current Frame of the + encapsulated FrameSet within the target Region. */ + fr = astGetFrame( ((AstRegion *) target_frame)->frameset, AST__CURRENT ); + result = astFindFrame( fr, template, domainlist ); + fr = astAnnul( fr ); + +/* Return the result. */ + return result; +} + +static const char *Format( AstFrame *this_frame, int axis, double value, int *status ) { +/* +* Name: +* Format + +* Purpose: +* Format a coordinate value for a Region axis. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* const char *Format( AstFrame *this, int axis, double value, int *status ) + +* Class Membership: +* Region member function (over-rides the astFormat method +* inherited from the Frame class). + +* Description: +* This function returns a pointer to a string containing the +* formatted (character) version of a coordinate value for a +* Region axis. The formatting applied is that specified by a +* previous invocation of the astSetFormat method. A suitable +* default format is applied if necessary. + +* Parameters: +* this +* Pointer to the Region. +* axis +* The number of the axis (zero-based) for which formatting is +* to be performed. +* value +* The coordinate value to be formatted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Region object, or at static memory. The contents of +* the string may be over-written or the pointer may become invalid +* following a further invocation of the same function or deletion +* of the Region. A copy of the string should therefore be made +* if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astFormat" ); + +/* Obtain a pointer to the Region's current Frame and invoke the + astFormat method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astFormat( fr, axis, value ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static double Gap( AstFrame *this_frame, int axis, double gap, int *ntick, int *status ) { +/* +* Name: +* Gap + +* Purpose: +* Find a "nice" gap for tabulating Region axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astGap method +* inherited from the Frame class). + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted values for a Region axis, the returned gap +* size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the Region. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Gap value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGap" ); + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astGap method to obtain the required gap value. Annul the + Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astGap( fr, axis, gap, ntick ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Region member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Region, +* in bytes. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstRegion *this; /* Pointer to Region structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the Region structure. */ + this = (AstRegion *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->frameset ); + result += astGetObjSize( this->points ); + result += astGetObjSize( this->basemesh ); + result += astGetObjSize( this->basegrid ); + result += astGetObjSize( this->unc ); + result += astGetObjSize( this->negation ); + result += astGetObjSize( this->defunc ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Region, formatted as a character string. + +* Parameters: +* this +* Pointer to the Region. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Region, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Region. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRegion *this; /* Pointer to the Region structure */ + const char *result; /* Pointer value to return */ + double dval; /* Floating point attribute value */ + int ival; /* Integer attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* We first handle attributes that apply to the Region as a whole + (rather than to the encapsulated FrameSet). */ + +/* Negated */ +/* ------- */ + if ( !strcmp( attrib, "negated" ) ) { + ival = astGetNegated( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Closed */ +/* ------ */ + } else if ( !strcmp( attrib, "closed" ) ) { + ival = astGetClosed( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Adaptive */ +/* -------- */ + } else if ( !strcmp( attrib, "adaptive" ) ) { + ival = astGetAdaptive( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* FillFactor */ +/* ---------- */ + } else if ( !strcmp( attrib, "fillfactor" ) ) { + dval = astGetFillFactor( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* MeshSize */ +/* -------- */ + } else if ( !strcmp( attrib, "meshsize" ) ) { + ival = astGetMeshSize( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Bounded */ +/* ------- */ + } else if ( !strcmp( attrib, "bounded" ) ) { + ival = astGetBounded( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Now get the values of attributes inherited from parent classes. We do + this to avoid the request being passed on to the encapsulated FrameSet + below. */ + +/* Class. */ +/* ------ */ + } else if ( !strcmp( attrib, "class" ) ) { + result = astGetClass( this ); + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + result = astGetID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astGetIdent( this ); + +/* Invert. */ +/* ------- */ + } else if ( !strcmp( attrib, "invert" ) ) { + ival = astGetInvert( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Nin. */ +/* ---- */ + } else if ( !strcmp( attrib, "nin" ) ) { + ival = astGetNin( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Nobject. */ +/* -------- */ + } else if ( !strcmp( attrib, "nobject" ) ) { + ival = astGetNobject( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Nout. */ +/* ----- */ + } else if ( !strcmp( attrib, "nout" ) ) { + ival = astGetNout( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* RefCount. */ +/* --------- */ + } else if ( !strcmp( attrib, "refcount" ) ) { + ival = astGetRefCount( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + ival = astGetReport( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* TranForward. */ +/* ------------ */ + } else if ( !strcmp( attrib, "tranforward" ) ) { + ival = astGetTranForward( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* TranInverse. */ +/* ------------ */ + } else if ( !strcmp( attrib, "traninverse" ) ) { + ival = astGetTranInverse( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Pass unrecognised attributes on to the Region's encapsulated FrameSet for + further interpretation. Do not pass on FrameSet attributes since we + pretend to the outside world that the encapsulated FrameSet is actually a + Frame. */ + } else if ( strcmp( attrib, "base" ) && + strcmp( attrib, "current" ) && + strcmp( attrib, "nframe" ) ) { + result = astGetAttrib( this->frameset, attrib ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static int GetBounded( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astGetBounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Protected function. + +* Synopsis: +* int astGetBounded( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a flag indicating if the Region is bounded. +* The implementation provided by the base Region class is suitable +* for Region sub-classes representing the inside of a single closed +* curve (e.g. Circle, Ellipse, Box, etc). Other sub-classes (such as +* CmpRegion, PointList, etc ) may need to provide their own +* implementations. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* Non-zero if the Region is bounded. Zero otherwise. + +*- +*/ + +/* For Regions which are defined by one or more closed curves such as Circles, + Boxes, etc, the Region is bounded so long as it has not been negated. + Classes for which this is not true should over-ride this implementation. */ + return !astGetNegated( this ); +} + +static AstAxis *GetAxis( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetAxis + +* Purpose: +* Obtain a pointer to a specified Axis from a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstAxis *GetAxis( AstFrame *this, int axis, int *status ) + +* Class Membership: +* Region member function (over-rides the astGetAxis method +* inherited from the Frame class). + +* Description: +* This function returns a pointer to the Axis object associated +* with one of the axes of the current Frame of a Region. This +* object describes the quantity which is represented along that +* axis. + +* Parameters: +* this +* Pointer to the Region. +* axis +* The number of the axis (zero-based) for which an Axis pointer +* is required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the requested Axis object. + +* Notes: +* - The reference count of the requested Axis object will be +* incremented by one to reflect the additional pointer returned by +* this function. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstAxis *result; /* Pointer to Axis */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGetAxis" ); + +/* Obtain a pointer to the Region's encapsulated FrameSet and invoke + this FrameSet's astGetAxis method to obtain the required Axis + pointer. */ + result = astGetAxis( this->frameset, axis ); + +/* If an error occurred, annul the result. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstRegion *GetDefUnc( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astGetDefUnc + +* Purpose: +* Obtain a pointer to the default uncertainty Region for a given Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstRegion *astGetDefUnc( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a pointer to a Region which represents the +* default uncertainty associated with a position on the boundary of the +* given Region. The returned Region refers to the base Frame within the +* FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* Base Frame of supplied Region */ + AstRegion *result; /* Returned pointer */ + double *lbnd; /* Ptr. to array holding axis lower bounds */ + double *ubnd; /* Ptr. to array holding axis upper bounds */ + double c; /* Central axis value */ + double hw; /* Half width of uncertainty interval */ + int i; /* Axis index */ + int nax; /* Number of base Frame axes */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the base Frame in the supplied Region. */ + bfrm = astGetFrame( this->frameset, AST__BASE ); + +/* Get the number of base Frame axes. */ + nax = astGetNaxes( bfrm ); + +/* Get the base frame bounding box of the supplied Region. The astRegBaseBox + assumes the supplied Region has not been inverted. But if the Region + contains other Regions (e.g. a Prism or CmpRegion, etc) then this + assumption needs to be propagated to the component Regions, which + astRegBaseBox does not do. For this reason we use astRegBaseBox2 + instead. */ + lbnd = astMalloc( sizeof( double)*(size_t) nax ); + ubnd = astMalloc( sizeof( double)*(size_t) nax ); + astRegBaseBox2( this, lbnd, ubnd ); + +/* Create a Box covering 1.0E-6 of this bounding box, centred on the + centre of the box. */ + if( astOK ) { + for( i = 0; i < nax; i++ ) { + if( ubnd[ i ] != DBL_MAX && lbnd[ i ] != -DBL_MAX ) { + hw = fabs( 0.5E-6*( ubnd[ i ] - lbnd[ i ] ) ); + c = 0.5*( ubnd[ i ] + lbnd[ i ] ); + if( hw == 0.0 ) hw = c*0.5E-6; + ubnd[ i ] = c + hw; + lbnd[ i ] = c - hw; + } else { + ubnd[ i ] = 0.0; + lbnd[ i ] = 0.0; + } + } + result = (AstRegion *) astBox( bfrm, 1, lbnd, ubnd, NULL, "", status ); + } + +/* Free resources. */ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + bfrm = astAnnul( bfrm ); + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +static AstRegion *GetNegation( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astGetNegation + +* Purpose: +* Obtain a pointer to a negated copy of the supplied Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstRegion *GetNegation( AstRegion *this, int *status ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a pointer to a Region which is a negated +* copy of "this". The copy is cached in the Region structure for +* future use. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* If the Region struture does not contain a pointer to a negated copy of + itself, create one now. */ + if( ! this->negation ) { + this->negation = astCopy( this ); + astNegate( this->negation ); + } + +/* Return a clone of the negation pointer. */ + return astClone( this->negation ); +} + +static AstFrameSet *GetRegFS( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astGetRegFS + +* Purpose: +* Obtain a pointer to the FrameSet encapsulated within a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstFrameSet *astGetRegFS( AstRegion *this ) + +* Class Membership: +* Region virtual function + +* Description: +* This function returns a pointer to the FrameSet encapsulated by the +* Region. This is a clone, not a deep copy, of the pointer stored +* in the Region. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the FrameSet. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return the required pointer. */ + return astClone( this->frameset ); +} + +static AstPointSet *GetSubMesh( int *mask, AstPointSet *in, int *status ) { +/* +* Name: +* GetSubMesh + +* Purpose: +* Extract a selection of points from a PointSet. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstPointSet *GetSubMesh( int *mask, AstPointSet *in, int *status ) + +* Class Membership: +* Region member function + +* Description: +* This function creates a new PointSet holding points selected from a +* supplied PointSet. An integer mask is supplied to indicate which +* points should be selected. + +* Parameters: +* mask +* Pointer to a mask array, Its size should be equal to the number +* of points in the supplied PointSet. Each corresponding point will +* be copied if the mask value is zero. +* in +* Pointer to the PointSet holding the input positions. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + double **ptr_in; /* Pointers to input axis values */ + double **ptr_out; /* Pointers to output axis values */ + double *pin; /* Pointer to next input axis value */ + double *pout; /* Pointer to next output axis value */ + int *m; /* Pointer to next mask element */ + int ic; /* Axis index */ + int ip; /* Point index */ + int nc; /* Number of axes in both PointSets */ + int npin; /* Number of points in input PointSet */ + int npout; /* Number of points in output PointSet */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the length of the mask. */ + npin = astGetNpoint( in ); + +/* Count the number of zeros in the mask. */ + npout = 0; + m = mask; + for( ip = 0; ip < npin; ip++ ) { + if( *(m++) == 0 ) npout++; + } + +/* Create the output PointSet and get pointers to its data arrays. */ + nc = astGetNcoord( in ); + result = astPointSet( npout, nc, "", status ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Check pointers can be dereferenced safely. */ + if( astOK ) { + +/* Copy the required axis values from the input to the output. */ + for( ic = 0; ic < nc; ic++ ) { + pin = ptr_in[ ic ]; + pout = ptr_out[ ic ]; + m = mask; + for( ip = 0; ip < npin; ip++, pin++, m++ ) { + if( *m == 0 ) *(pout++) = *pin; + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; + +} + +static AstRegion *GetUnc( AstRegion *this, int def, int *status ){ +/* +*++ +* Name: +c astGetUnc +f AST_GETUNC + +* Purpose: +* Obtain uncertainty information from a Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c AstRegion *astGetUnc( AstRegion *this, int def ) +f RESULT = AST_GETUNC( THIS, DEF, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* This function returns a Region which represents the uncertainty +* associated with positions within the supplied Region. See +c astSetUnc +f AST_SETUNC +* for more information about Region uncertainties and their use. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c def +f DEF = LOGICAL (Given) +* Controls what is returned if no uncertainty information has been +* associated explicitly with the supplied Region. If +c a non-zero value +f .TRUE. +* is supplied, then the default uncertainty Region used internally +* within AST is returned (see "Applicability" below). If +c zero is supplied, then NULL +f .FALSE. is supplied, then AST__NULL +* will be returned (without error). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetUnc() +f AST_GETUNC = INTEGER +* A pointer to a Region describing the uncertainty in the supplied +* Region. + +* Applicability: +* CmpRegion +* The default uncertainty for a CmpRegion is taken from one of the +* two component Regions. If the first component Region has a +* non-default uncertainty, then it is used as the default uncertainty +* for the parent CmpRegion. Otherwise, if the second component Region +* has a non-default uncertainty, then it is used as the default +* uncertainty for the parent CmpRegion. If neither of the +* component Regions has non-default uncertainty, then the default +* uncertainty for the CmpRegion is 1.0E-6 of the bounding box of +* the CmpRegion. +* Prism +* The default uncertainty for a Prism is formed by combining the +* uncertainties from the two component Regions. If a component +* Region does not have a non-default uncertainty, then its default +* uncertainty will be used to form the default uncertainty of the +* parent Prism. +* Region +* For other classes of Region, the default uncertainty is 1.0E-6 +* of the bounding box of the Region. If the bounding box has zero +* width on any axis, then the uncertainty will be 1.0E-6 of the +* axis value. + +* Notes: +* - If uncertainty information is associated with a Region, and the +* coordinate system described by the Region is subsequently changed +* (e.g. by changing the value of its System attribute, or using the +c astMapRegion +f AST_MAPREGION +* function), then the uncertainty information returned by this function +* will be modified so that it refers to the coordinate system currently +* described by the supplied Region. +f - A null Object pointer (AST__NULL) will be returned if this +f function is invoked with STATUS set to an error value, or if it +c - A null Object pointer (NULL) will be returned if this +c function is invoked with the AST error status set, or if it +* should fail for any reason. + +*-- +*/ + +/* Local Variables: */ + AstRegion *result; /* Pointer to returned uncertainty Region */ + AstRegion *unc; /* Pointer to original uncertainty Region */ + +/* Initialise */ + result = NULL; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Check that we have an uncertainty Region to return (either assigned or + default). */ + if( def || astTestUnc( this ) ) { + +/* Obtain the uncertainty Region and take a copy so that we can modify it + without affecting the supplied Region. */ + unc = astGetUncFrm( this, AST__CURRENT ); + result = astCopy( unc ); + unc = astAnnul( unc ); + +/* In its current context, the uncertainty region is known to refer to + the Frame of the supplied Region and so its RegionFS attribute will be + set to zero, indicating that the uncertainty FrameSet need not be + dumped. However, outside of AST this information cannot be implied, so + clear the RegionFS attribute so that the returned pointer will include + Frame information if it is dumped to a Channel. */ + astClearRegionFS( result ); + + } + +/* Return the result. */ + return result; + +} + +static AstRegion *GetUncFrm( AstRegion *this, int ifrm, int *status ) { +/* +*+ +* Name: +* astGetUncFrm + +* Purpose: +* Obtain a pointer to the uncertainty Region for a given Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstRegion *astGetUncFrm( AstRegion *this, int ifrm ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a pointer to a Region which represents the +* uncertainty associated with a position on the boundary of the given +* Region. The returned Region can refer to the either the base or +* the current Frame within the FrameSet encapsulated by the supplied +* Region as specified by the "ifrm" parameter. If the returned Region is +* re-centred at some point on the boundary of the supplied Region, then +* the re-centred Region will represent the region in which the true +* boundary position could be. + +* Parameters: +* this +* Pointer to the Region. +* ifrm +* The index of a Frame within the FrameSet encapsulated by "this". +* The returned Region will refer to the requested Frame. It should +* be either AST__CURRENT or AST__BASE. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A default uncertainty Region will be created if the supplied Region +* does not have an uncertainty Region. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame from supplied Region */ + AstMapping *map; /* Supplied to uncertainty Mapping */ + AstRegion *result; /* Returned pointer */ + AstRegion *unc; /* Base frame uncertainty Region to use */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the Region has an explicitly assigned base-frame uncertainty Region, + use it. */ + if( this->unc ) { + unc = this->unc; + +/* If not, use the default base-frame uncertainty Region, creating it if + necessary. */ + } else { + if( !this->defunc ) this->defunc = astGetDefUnc( this ); + unc = this->defunc; + } + +/* If the uncertainty Region is the base Frame is required, just return a + clone of the uncertainty Region pointer. The Frame represented by an + uncertainty Region will always (barring bugs!) be the base Frame of + its parent Region. */ + if( ifrm == AST__BASE ) { + result = astClone( unc ); + +/* If the uncertainty Region is the current Frame is required... */ + } else { + +/* Get a Mapping from the Frame represented by the uncertainty Region + (the Region base Frame) to the Region current Frame. */ + map = astGetMapping( this->frameset, AST__BASE, AST__CURRENT ); + +/* If this is a UnitMap, the uncertainty Region is already in the correct + Frame, so just return the stored pointer. */ + if( astIsAUnitMap( map ) ) { + result = astClone( unc ); + +/* Otherwise, use this Mapping to map the uncertainty Region into the current + Frame. */ + } else { + frm = astGetFrame( this->frameset, AST__CURRENT ); + result = astMapRegion( unc, map, frm ); + +/* Free resources. */ + frm = astAnnul( frm ); + } + + map = astAnnul( map ); + } + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +static int GetUseDefs( AstObject *this_object, int *status ) { +/* +* Name: +* GetUseDefs + +* Purpose: +* Get the value of the UseDefs attribute for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int GetUseDefs( AstObject *this_object, int *status ) { + +* Class Membership: +* Region member function (over-rides the protected astGetUseDefs +* method inherited from the Frame class). + +* Description: +* This function returns the value of the UseDefs attribute for a +* Region. supplying a suitable default. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - The USeDefs value. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + int result; /* Value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_object; + +/* If the UseDefs value for the Region has been set explicitly, use the + Get method inherited from the parent Frame class to get its value. */ + if( astTestUseDefs( this ) ) { + result = (*parent_getusedefs)( this_object, status ); + +/* Otherwise, supply a default value equal to the UseDefs value of the + encapsulated Frame. */ + } else { + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astGetUseDefs( fr ); + fr = astAnnul( fr ); + } + +/* Return the result. */ + return result; +} + +static int TestUnc( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astTestUnc + +* Purpose: +* Does the Region contain non-default uncertainty information? + +* Type: +* Protected function. + +* Synopsis: +* int astTestUnc( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a flag indicating if the uncertainty Region in +* the supplied Region was supplied explicit (i.e. is not a default +* uncertainty Region). + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* Non-zero if the uncertainty Region was supplied explicitly. +* Zero otherwise. + +* Notes: +* - Classes of Region that encapsulate two or more other Regions +* inherit their default uncertainty from the encapsulated Regions. +* Non-default uncertainty in the component Regions does not imply +* that the parent Region has non-default uncertainty. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + + return ( this->unc != NULL ); +} + +static AstFrame *RegFrame( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astRegFrame + +* Purpose: +* Obtain a pointer to the current Frame for a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstFrame *astRegFrame( AstRegion *this ) + +* Class Membership: +* Region virtual function + +* Description: +* This function returns a pointer to the current Frame in the encapsulated +* FrameSet. This is a clone, not a deep copy, of the pointer stored +* in the FrameSet. For a deep copy, use astGetRegionFrame. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Frame. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return the required pointer. */ + return astGetFrame( this->frameset, AST__CURRENT ); +} + +static AstMapping *RegMapping( AstRegion *this, int *status ) { +/* +*+ +* Name: +* astRegMapping + +* Purpose: +* Obtain a pointer to the simplified base->current Mapping for a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstMapping *astRegMapping( AstRegion *this ) + +* Class Membership: +* Region member function + +* Description: +* This function returns a pointer to the Mapping from the base to the +* current Frame int he encapsulated FrameSet. The returned Mapping is +* simplified before being returned. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Mapping. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstMapping *map; /* Unsimplified Mapping */ + AstMapping *result; /* Simplified Mapping */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the "nomap" flag is set in the Region structure, re return a + UnitMap. */ + if( this->nomap ) { + result = (AstMapping *) astUnitMap( astGetNin( this->frameset ), "", status ); + +/* Otherwise use the Mapping from the Region's FrameSet. */ + } else { + +/* Get the Mapping */ + map = astGetMapping( this->frameset, AST__BASE, AST__CURRENT ); + +/* Simplify it. */ + result = astSimplify( map ); + +/* Annul the pointer to the unsimplified Mapping */ + map = astAnnul( map ); + } + +/* Return the required pointer. */ + return result; +} + +static int GetNaxes( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetNaxes + +* Purpose: +* Determine how many axes a Region has. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int GetNaxes( AstFrame *this, int *status ) + +* Class Membership: +* Region member function (over-rides the astGetNaxes method +* inherited from the Frame class). + +* Description: +* This function returns the number of axes for a Region. This is equal +* to the number of axes in its current Frame. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of Region axes (zero or more). + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's current Frame. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + +/* Obtain the number of axes in this Frame. */ + result = astGetNaxes( fr ); + +/* Annul the current Frame pointer. */ + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static const int *GetPerm( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetPerm + +* Purpose: +* Access the axis permutation array for the current Frame of a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* const int *GetPerm( AstFrame *this, int *status ) + +* Class Membership: +* Region member function (over-rides the astGetPerm protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the axis permutation array +* for the current Frame of a Region. This array constitutes a +* lookup-table that converts between an axis number supplied +* externally and the corresponding index in the Frame's internal +* axis arrays. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the current Frame's axis permutation array (a +* constant array of int). Each element of this contains the +* (zero-based) internal axis index to be used in place of the +* external index which is used to address the permutation +* array. If the current Frame has zero axes, this pointer will be +* NULL. + +* Notes: +* - The pointer returned by this function gives direct access to +* data internal to the Frame object. It remains valid only so long +* as the Frame exists. The permutation array contents may be +* modified by other functions which operate on the Frame and this +* may render the returned pointer invalid. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to Region structure */ + const int *result; /* Result pointer value */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's current Frame and then obtain a + pointer to its axis permutation array. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astGetPerm( fr ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static AstFrame *GetRegionFrame( AstRegion *this, int *status ) { +/* +*++ +* Name: +c astGetRegionFrame +f AST_GETREGIONFRAME + +* Purpose: +* Obtain a pointer to the encapsulated Frame within a Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c AstFrame *astGetRegionFrame( AstRegion *this ) +f RESULT = AST_GETREGIONFRAME( THIS, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* This function returns a pointer to the Frame represented by a +* Region. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetRegionFrame() +f AST_GETREGIONFRAME = INTEGER +* A pointer to a deep copy of the Frame represented by the Region. +* Using this pointer to modify the Frame will have no effect on +* the Region. To modify the Region, use the Region pointer directly. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstFrame *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the current Frame of the encapsulated FrameSet. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + +/* Take a deep copy of it, and then annul the original pointer. */ + result = astCopy( fr ); + fr = astAnnul( fr ); + +/* If not OK, annul the returned pointer. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstFrameSet *GetRegionFrameSet( AstRegion *this, int *status ) { +/* +*++ +* Name: +c astGetRegionFrameSet +f AST_GETREGIONFRAMESET + +* Purpose: +* Obtain a pointer to the encapsulated FrameSet within a Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c AstFrame *astGetRegionFrameSet( AstRegion *this ) +f RESULT = AST_GETREGIONFRAMESET( THIS, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* This function returns a pointer to the FrameSet encapsulated by a +* Region. The base Frame is the Frame in which the box was originally +* defined, and the current Frame is the Frame into which the Region +* is currently mapped (i.e. it will be the same as the Frame returned +c by astGetRegionFrame). +f by AST_GETREGIONFRAME). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetRegionFrameSet() +f AST_GETREGIONFRAMESET = INTEGER +* A pointer to a deep copy of the FrameSet represented by the Region. +* Using this pointer to modify the FrameSet will have no effect on +* the Region. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return a deep copy of the encapsulated FrameSet. */ + return astCopy( this->frameset ); +} + +void astInitRegionVtab_( AstRegionVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitRegionVtab + +* Purpose: +* Initialise a virtual function table for a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astInitRegionVtab( AstRegionVtab *vtab, const char *name ) + +* Class Membership: +* Region vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Region class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsARegion) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ + +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->ClearNegated = ClearNegated; + vtab->GetNegated = GetNegated; + vtab->SetNegated = SetNegated; + vtab->TestNegated = TestNegated; + + vtab->ClearRegionFS = ClearRegionFS; + vtab->GetRegionFS = GetRegionFS; + vtab->SetRegionFS = SetRegionFS; + vtab->TestRegionFS = TestRegionFS; + + vtab->ClearClosed = ClearClosed; + vtab->GetClosed = GetClosed; + vtab->SetClosed = SetClosed; + vtab->TestClosed = TestClosed; + + vtab->ClearMeshSize = ClearMeshSize; + vtab->GetMeshSize = GetMeshSize; + vtab->SetMeshSize = SetMeshSize; + vtab->TestMeshSize = TestMeshSize; + + vtab->ClearAdaptive = ClearAdaptive; + vtab->GetAdaptive = GetAdaptive; + vtab->SetAdaptive = SetAdaptive; + vtab->TestAdaptive = TestAdaptive; + + vtab->ClearFillFactor = ClearFillFactor; + vtab->GetFillFactor = GetFillFactor; + vtab->SetFillFactor = SetFillFactor; + vtab->TestFillFactor = TestFillFactor; + + vtab->ResetCache = ResetCache; + vtab->RegTrace = RegTrace; + vtab->GetBounded = GetBounded; + vtab->TestUnc = TestUnc; + vtab->ClearUnc = ClearUnc; + vtab->GetRegionFrame = GetRegionFrame; + vtab->GetRegionFrameSet = GetRegionFrameSet; + vtab->MapRegion = MapRegion; + vtab->Overlap = Overlap; + vtab->OverlapX = OverlapX; + vtab->Negate = Negate; + vtab->BndMesh = BndMesh; + vtab->BndBaseMesh = BndBaseMesh; + vtab->RegBaseGrid = RegBaseGrid; + vtab->RegBaseMesh = RegBaseMesh; + vtab->RegSplit = RegSplit; + vtab->RegBaseBox = RegBaseBox; + vtab->RegBaseBox2 = RegBaseBox2; + vtab->RegBasePick = RegBasePick; + vtab->RegCentre = RegCentre; + vtab->RegGrid = RegGrid; + vtab->RegMesh = RegMesh; + vtab->RegClearAttrib = RegClearAttrib; + vtab->RegSetAttrib = RegSetAttrib; + vtab->GetDefUnc = GetDefUnc; + vtab->GetNegation = GetNegation; + vtab->GetUncFrm = GetUncFrm; + vtab->SetUnc = SetUnc; + vtab->GetUnc = GetUnc; + vtab->ShowMesh = ShowMesh; + vtab->GetRegionBounds = GetRegionBounds; + vtab->GetRegionBounds2 = GetRegionBounds2; + vtab->GetRegionMesh = GetRegionMesh; + vtab->GetRegionPoints = GetRegionPoints; + vtab->RegOverlay = RegOverlay; + vtab->RegFrame = RegFrame; + vtab->RegDummyFS = RegDummyFS; + vtab->RegMapping = RegMapping; + vtab->RegPins = RegPins; + vtab->RegTransform = RegTransform; + vtab->BTransform = BTransform; + vtab->GetRegFS = GetRegFS; + vtab->SetRegFS = SetRegFS; + vtab->MaskB = MaskB; + vtab->MaskD = MaskD; + vtab->MaskF = MaskF; + vtab->MaskI = MaskI; + vtab->MaskL = MaskL; + vtab->MaskS = MaskS; + vtab->MaskUB = MaskUB; + vtab->MaskUI = MaskUI; + vtab->MaskUL = MaskUL; + vtab->MaskUS = MaskUS; +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + vtab->MaskLD = MaskLD; +#endif + +/* Save the inherited pointers to methods that will be extended, and store + replacement pointers for methods which will be over-ridden by new member + functions implemented here. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + frame = (AstFrameVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_getusedefs = object->GetUseDefs; + object->GetUseDefs = GetUseDefs; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + object->Cast = Cast; + object->Equal = Equal; + object->ClearAttrib = ClearAttrib; + object->GetAttrib = GetAttrib; + object->SetAttrib = SetAttrib; + object->TestAttrib = TestAttrib; + + mapping->ReportPoints = ReportPoints; + mapping->RemoveRegions = RemoveRegions; + mapping->Simplify = Simplify; + + frame->Abbrev = Abbrev; + frame->Angle = Angle; + frame->AxAngle = AxAngle; + frame->AxDistance = AxDistance; + frame->AxOffset = AxOffset; + frame->CheckPerm = CheckPerm; + frame->ClearDigits = ClearDigits; + frame->ClearDirection = ClearDirection; + frame->ClearDomain = ClearDomain; + frame->ClearFormat = ClearFormat; + frame->ClearLabel = ClearLabel; + frame->ClearMatchEnd = ClearMatchEnd; + frame->ClearMaxAxes = ClearMaxAxes; + frame->ClearMinAxes = ClearMinAxes; + frame->ClearPermute = ClearPermute; + frame->ClearPreserveAxes = ClearPreserveAxes; + frame->ClearSymbol = ClearSymbol; + frame->ClearTitle = ClearTitle; + frame->ClearUnit = ClearUnit; + frame->Convert = Convert; + frame->ConvertX = ConvertX; + frame->Distance = Distance; + frame->FindFrame = FindFrame; + frame->Format = Format; + frame->Centre = Centre; + frame->Gap = Gap; + frame->GetAxis = GetAxis; + frame->GetDigits = GetDigits; + frame->GetDirection = GetDirection; + frame->GetDomain = GetDomain; + frame->GetFormat = GetFormat; + frame->GetLabel = GetLabel; + frame->GetMatchEnd = GetMatchEnd; + frame->GetMaxAxes = GetMaxAxes; + frame->GetMinAxes = GetMinAxes; + frame->GetNaxes = GetNaxes; + frame->GetPerm = GetPerm; + frame->GetPermute = GetPermute; + frame->GetPreserveAxes = GetPreserveAxes; + frame->GetSymbol = GetSymbol; + frame->GetTitle = GetTitle; + frame->GetUnit = GetUnit; + frame->Intersect = Intersect; + frame->IsUnitFrame = IsUnitFrame; + frame->Match = Match; + frame->Norm = Norm; + frame->NormBox = NormBox; + frame->Offset = Offset; + frame->Offset2 = Offset2; + frame->Overlay = Overlay; + frame->PermAxes = PermAxes; + frame->PickAxes = PickAxes; + frame->Resolve = Resolve; + frame->ResolvePoints = ResolvePoints; + frame->SetAxis = SetAxis; + frame->SetDigits = SetDigits; + frame->SetDirection = SetDirection; + frame->SetDomain = SetDomain; + frame->SetFormat = SetFormat; + frame->SetLabel = SetLabel; + frame->SetMatchEnd = SetMatchEnd; + frame->SetMaxAxes = SetMaxAxes; + frame->SetMinAxes = SetMinAxes; + frame->SetPermute = SetPermute; + frame->SetPreserveAxes = SetPreserveAxes; + frame->SetSymbol = SetSymbol; + frame->SetTitle = SetTitle; + frame->SetUnit = SetUnit; + frame->SubFrame = SubFrame; + frame->SystemCode = SystemCode; + frame->SystemString = SystemString; + frame->TestDigits = TestDigits; + frame->TestDirection = TestDirection; + frame->TestDomain = TestDomain; + frame->TestFormat = TestFormat; + frame->TestLabel = TestLabel; + frame->TestMatchEnd = TestMatchEnd; + frame->TestMaxAxes = TestMaxAxes; + frame->TestMinAxes = TestMinAxes; + frame->TestPermute = TestPermute; + frame->TestPreserveAxes = TestPreserveAxes; + frame->TestSymbol = TestSymbol; + frame->TestTitle = TestTitle; + frame->TestUnit = TestUnit; + frame->Unformat = Unformat; + frame->ValidateAxis = ValidateAxis; + frame->ValidateAxisSelection = ValidateAxisSelection; + frame->ValidateSystem = ValidateSystem; + frame->LineDef = LineDef; + frame->LineContains = LineContains; + frame->LineCrossing = LineCrossing; + frame->LineOffset = LineOffset; + frame->MatchAxes = MatchAxes; + frame->MatchAxesX = MatchAxesX; + + frame->GetActiveUnit = GetActiveUnit; + frame->SetActiveUnit = SetActiveUnit; + frame->TestActiveUnit = TestActiveUnit; + + frame->GetTop = GetTop; + frame->SetTop = SetTop; + frame->TestTop = TestTop; + frame->ClearTop = ClearTop; + + frame->GetBottom = GetBottom; + frame->SetBottom = SetBottom; + frame->TestBottom = TestBottom; + frame->ClearBottom = ClearBottom; + + frame->GetEpoch = GetEpoch; + frame->SetEpoch = SetEpoch; + frame->TestEpoch = TestEpoch; + frame->ClearEpoch = ClearEpoch; + + frame->ClearObsAlt = ClearObsAlt; + frame->TestObsAlt = TestObsAlt; + frame->GetObsAlt = GetObsAlt; + frame->SetObsAlt = SetObsAlt; + + frame->ClearObsLat = ClearObsLat; + frame->TestObsLat = TestObsLat; + frame->GetObsLat = GetObsLat; + frame->SetObsLat = SetObsLat; + + frame->ClearObsLon = ClearObsLon; + frame->TestObsLon = TestObsLon; + frame->GetObsLon = GetObsLon; + frame->SetObsLon = SetObsLon; + + frame->GetSystem = GetSystem; + frame->SetSystem = SetSystem; + frame->TestSystem = TestSystem; + frame->ClearSystem = ClearSystem; + + frame->GetAlignSystem = GetAlignSystem; + frame->SetAlignSystem = SetAlignSystem; + frame->TestAlignSystem = TestAlignSystem; + frame->ClearAlignSystem = ClearAlignSystem; + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "Region", + "An area within a coordinate system" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void Intersect( AstFrame *this_frame, const double a1[2], + const double a2[2], const double b1[2], + const double b2[2], double cross[2], + int *status ) { +/* +* Name: +* Intersect + +* Purpose: +* Find the point of intersection between two geodesic curves. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void Intersect( AstFrame *this_frame, const double a1[2], +* const double a2[2], const double b1[2], +* const double b2[2], double cross[2], +* int *status ) + +* Class Membership: +* Region member function (over-rides the astIntersect method +* inherited from the Frame class). + +* Description: +* This function finds the coordinate values at the point of +* intersection between two geodesic curves. Each curve is specified +* by two points on the curve. + +* Parameters: +* this +* Pointer to the SkyFrame. +* a1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the first +* geodesic curve. +* a2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on the +* first geodesic curve. +* b1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the second +* geodesic curve. +* b2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on +* the second geodesic curve. +* cross +* An array of double, with one element for each Frame axis +* in which the coordinates of the required intersection +* point will be returned. These will be AST__BAD if the curves do +* not intersect. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - For SkyFrames each curve will be a great circle, and in general +* each pair of curves will intersect at two diametrically opposite +* points on the sky. The returned position is the one which is +* closest to point "a1". +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astIntersect method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astIntersect( fr, a1, a2, b1, b2, cross ); + fr = astAnnul( fr ); +} + +static int IsUnitFrame( AstFrame *this, int *status ){ +/* +* Name: +* IsUnitFrame + +* Purpose: +* Is this Frame equivalent to a UnitMap? + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int IsUnitFrame( AstFrame *this, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astIsUnitFrame +* method inherited from the Frame class). + +* Description: +* This function returns a flag indicating if the supplied Frame is +* equivalent to a UnitMap when treated as a Mapping (note, the Frame +* class inherits from Mapping and therefore every Frame is also a Mapping). + +* Parameters: +* this +* Pointer to the Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the supplied Frame is equivalent to +* a UnitMap when treated as a Mapping. + +*- +*/ + +/* Check the global error status. */ + if( !astOK ) return 0; + +/* The Region class is never equivalent to a UnitMap. */ + return 0; +} + +static int LineContains( AstFrame *this_frame, AstLineDef *l, int def, double *point, int *status ) { +/* +* Name: +* LineContains + +* Purpose: +* Determine if a line contains a point. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astLineContains +* method inherited from the Frame class). + +* Description: +* This function determines if the supplied point is on the supplied +* line within the supplied Frame. The start point of the line is +* considered to be within the line, but the end point is not. The tests +* are that the point of closest approach of the line to the point should +* be between the start and end, and that the distance from the point to +* the point of closest aproach should be less than 1.0E-7 of the length +* of the line. + +* Parameters: +* this +* Pointer to the Frame. +* l +* Pointer to the structure defining the line. +* def +* Should be set non-zero if the "point" array was created by a +* call to astLineCrossing (in which case it may contain extra +* information following the axis values),and zero otherwise. +* point +* Point to an array containing the axis values of the point to be +* tested, possibly followed by extra cached information (see "def"). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the line contains the point. + +* Notes: +* - The pointer supplied for "l" should have been created using the +* astLineDef method. These structures contained cached information about +* the lines which improve the efficiency of this method when many +* repeated calls are made. An error will be reported if the structure +* does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + int result; /* Returned value */ + +/* Initialise */ + result =0; + +/* Obtain a pointer to the Region's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( ((AstRegion *) this_frame)->frameset, AST__CURRENT ); + result = astLineContains( fr, l, def, point ); + fr = astAnnul( fr ); + +/* Return the result. */ + return result; +} + +static int LineCrossing( AstFrame *this_frame, AstLineDef *l1, AstLineDef *l2, + double **cross, int *status ) { +/* +* Name: +* LineCrossing + +* Purpose: +* Determine if two lines cross. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, +* double **cross, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astLineCrossing +* method inherited from the Frame class). + +* Description: +* This function determines if the two suplied line segments cross, +* and if so returns the axis values at the point where they cross. +* A flag is also returned indicating if the crossing point occurs +* within the length of both line segments, or outside one or both of +* the line segments. + +* Parameters: +* this +* Pointer to the Frame. +* l1 +* Pointer to the structure defining the first line. +* l2 +* Pointer to the structure defining the second line. +* cross +* Pointer to a location at which to put a pointer to a dynamically +* alocated array containing the axis values at the crossing. If +* NULL is supplied no such array is returned. Otherwise, the returned +* array should be freed using astFree when no longer needed. If the +* lines are parallel (i.e. do not cross) then AST__BAD is returned for +* all axis values. Note usable axis values are returned even if the +* lines cross outside the segment defined by the start and end points +* of the lines. The order of axes in the returned array will take +* account of the current axis permutation array if appropriate. Note, +* sub-classes such as SkyFrame may append extra values to the end +* of the basic frame axis values. A NULL pointer is returned if an +* error occurs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the lines cross at a point which is +* within the [start,end) segment of both lines. If the crossing point +* is outside this segment on either line, or if the lines are parallel, +* zero is returned. Note, the start point is considered to be inside +* the length of the segment, but the end point is outside. + +* Notes: +* - The pointers supplied for "l1" and "l2" should have been created +* using the astLineDef method. These structures contained cached +* information about the lines which improve the efficiency of this method +* when many repeated calls are made. An error will be reported if +* either structure does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + int result; /* Returned value */ + +/* Initialise */ + result =0; + +/* Obtain a pointer to the Region's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( ((AstRegion *) this_frame)->frameset, AST__CURRENT ); + result = astLineCrossing( fr, l1, l2, cross ); + fr = astAnnul( fr ); + +/* Return the result. */ + return result; +} + +static AstLineDef *LineDef( AstFrame *this_frame, const double start[2], + const double end[2], int *status ) { +/* +* Name: +* LineDef + +* Purpose: +* Creates a structure describing a line segment in a 2D Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstLineDef *LineDef( AstFrame *this, const double start[2], +* const double end[2], int *status ) + +* Class Membership: +* Region member function (over-rides the protected astLineDef +* method inherited from the Frame class). + +* Description: +* This function creates a structure containing information describing a +* given line segment within the supplied 2D Frame. This may include +* information which allows other methods such as astLineCrossing to +* function more efficiently. Thus the returned structure acts as a +* cache to store intermediate values used by these other methods. + +* Parameters: +* this +* Pointer to the Frame. Must have 2 axes. +* start +* An array of 2 doubles marking the start of the line segment. +* end +* An array of 2 doubles marking the end of the line segment. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the memory structure containing the description of the +* line. This structure should be freed using astFree when no longer +* needed. A NULL pointer is returned (without error) if any of the +* supplied axis values are AST__BAD. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstLineDef *result; /* Returned value */ + +/* Initialise */ + result = NULL; + +/* Obtain a pointer to the Region's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( ((AstRegion *) this_frame)->frameset, AST__CURRENT ); + result = astLineDef( fr, start, end ); + fr = astAnnul( fr ); + +/* Return the result. */ + return result; +} + +static void LineOffset( AstFrame *this_frame, AstLineDef *line, double par, + double prp, double point[2], int *status ){ +/* +* Name: +* LineOffset + +* Purpose: +* Find a position close to a line. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void LineOffset( AstFrame *this, AstLineDef *line, double par, +* double prp, double point[2], int *status ) + +* Class Membership: +* Region member function (over-rides the protected astLineOffset +* method inherited from the Frame class). + +* Description: +* This function returns a position formed by moving a given distance along +* the supplied line, and then a given distance away from the supplied line. + +* Parameters: +* this +* Pointer to the Frame. +* line +* Pointer to the structure defining the line. +* par +* The distance to move along the line from the start towards the end. +* prp +* The distance to move at right angles to the line. Positive +* values result in movement to the left of the line, as seen from +* the observer, when moving from start towards the end. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The pointer supplied for "line" should have been created using the +* astLineDef method. This structure contains cached information about the +* line which improves the efficiency of this method when many repeated +* calls are made. An error will be reported if the structure does not +* refer to the Frame specified by "this". +*/ + + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + +/* Obtain a pointer to the Region's current Frame and then invoke the + method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( ((AstRegion *) this_frame)->frameset, AST__CURRENT ); + astLineOffset( fr, line, par, prp, point ); + fr = astAnnul( fr ); +} + + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* Region member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstRegion *this; /* Pointer to Region structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the Region structure. */ + this = (AstRegion *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->frameset, mode, extra, fail ); + if( !result ) result = astManageLock( this->points, mode, extra, fail ); + if( !result ) result = astManageLock( this->unc, mode, extra, fail ); + if( !result ) result = astManageLock( this->negation, mode, extra, fail ); + if( !result ) result = astManageLock( this->defunc, mode, extra, fail ); + if( !result ) result = astManageLock( this->basemesh, mode, extra, fail ); + if( !result ) result = astManageLock( this->basegrid, mode, extra, fail ); + + return result; + +} +#endif + +static AstRegion *MapRegion( AstRegion *this, AstMapping *map0, + AstFrame *frame0, int *status ) { +/* +*+ +* Name: +* astMapRegion + +* Purpose: +* Transform a Region into a new Frame using a given Mapping. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "region.h" +* AstRegion *astMapRegion( AstRegion *this, AstMapping *map, +* AstFrame *frame ) + +* Class Membership: +* Region method. + +* Description: +* This function returns a pointer to a new Region which corresponds to +* supplied Region in some other specified coordinate system. A +* Mapping is supplied which transforms positions between the old and new +* coordinate systems. The new Region may not be of the same class as +* the original region. + +* Parameters: +* this +* Pointer to the Region. +* map +* Pointer to a Mapping which transforms positions from the +* coordinate system represented by the supplied Region to the +* coordinate system specified by "frame". The supplied Mapping should +* define both forward and inverse transformations, and these +* transformations should form a genuine inverse pair. That is, +* transforming a position using the forward transformation and then +* using the inverse transformation should produce the original input +* position. Some Mapping classes (such as PermMap, MathMap, SphMap) +* can result in Mappings for which this is not true. +* frame +* Pointer to a Frame describing the coordinate system in which +* the new Region is required. + +* Returned Value: +* astMapRegion() +* A pointer to a new Region. This Region will represent the area +* within the coordinate system specified by "frame" which corresponds +* to the supplied Region. + +* Notes: +* - This is the protected implementation of this function - it does +* not simplify the returned Region. The public implementation is +* astMapRegionID, which simplifies the returned Region. +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFrame *frame; + AstFrameSet *fs; + AstMapping *map; + AstPointSet *ps2; + AstPointSet *ps1; + AstPointSet *pst; + AstRegion *result; + double **ptr1; + double **ptr2; + int i; + int icurr; + int j; + int nax1; + int nax2; + int np; + int ok; + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If a FrameSet was supplied for the Mapping, use the base->current + Mapping */ + if( astIsAFrameSet( map0 ) ) { + map = astGetMapping( (AstFrameSet *) map0, AST__BASE, AST__CURRENT ); + } else { + map = astClone( map0 ); + } + +/* If a FrameSet was supplied for the Frame, use the current Frame. */ + if( astIsAFrameSet( frame0 ) ) { + frame = astGetFrame( (AstFrameSet *) frame0, AST__CURRENT ); + } else { + frame = astClone( frame0 ); + } + +/* First check the Mapping is suitable. It must defined both a forward + and an inverse Mapping. */ + if( !astGetTranInverse( map ) ) { + astError( AST__NODEF, "astMapRegion(%s): The supplied %s does not " + "define an inverse transformation.", status, astGetClass( this ), + astGetClass( map ) ); + } else if( !astGetTranForward( map ) ) { + astError( AST__NODEF, "astMapRegion(%s): The supplied %s does not " + "define a forward transformation.", status, astGetClass( this ), + astGetClass( map ) ); + } + +/* It must not introduce any bad axis values. We can only perform this + test reliably if the supplied Region has not bad axis values. */ + ps1 = this->points; + if( ps1 ) { + nax1 = astGetNcoord( ps1 ); + np = astGetNpoint( ps1 ); + ptr1 = astGetPoints( ps1 ); + if( ptr1 ) { + ok = 1; + for( i = 0; i < nax1 && ok; i++ ){ + for( j = 0; j < np; j++ ) { + if( ptr1[ i ][ j ] == AST__BAD ){ + ok = 0; + break; + } + } + } + if( ok ) { + pst = astRegTransform( this, ps1, 1, NULL, NULL ); + ps2 = astTransform( map, pst, 1, NULL ); + nax2 = astGetNcoord( ps2 ); + ptr2 = astGetPoints( ps2 ); + if( ptr2 ) { + for( i = 0; i < nax2 && ok; i++ ){ + for( j = 0; j < np; j++ ) { + if( ptr2[ i ][ j ] == AST__BAD ){ + ok = 0; + break; + } + } + } + if( !ok ) { + astError( AST__NODEF, "astMapRegion(%s): The region which " + "results from using the supplied %s to transform " + "the supplied %s is undefined.", status, astGetClass( this ), + astGetClass( map ), astGetClass( this ) ); + } + } + ps2 = astAnnul( ps2 ); + pst = astAnnul( pst ); + } + } + } + +/* Take a deep copy of the supplied Region. */ + result = astCopy( this ); + +/* Get a pointer to the encapsulated FrameSet. */ + if( astOK ) { + fs = result->frameset; + +/* Add in the new Frame and Mapping. First note the index of the original + current Frame. */ + icurr = astGetCurrent( fs ); + astAddFrame( fs, AST__CURRENT, map, frame ); + +/* Remove the original current Frame. */ + astRemoveFrame( fs, icurr ); + +/* The base and current Frames of the resulting FrameSet are now (in + general) different and so the Region should include its FrameSet in any + Dump. */ + astSetRegionFS( result, 1 ); + } + +/* Since the Mapping has been changed, any cached information calculated + on the basis of the Mapping properties may no longer be up to date. */ + astResetCache( this ); + +/* Free resources */ + map = astAnnul( map ); + frame = astAnnul( frame ); + +/* If not OK, annul the returned pointer. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +/* +*++ +* Name: +c astMask +f AST_MASK + +* Purpose: +* Mask a region of a data grid. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c int astMask( AstRegion *this, AstMapping *map, int inside, int ndim, +c const int lbnd[], const int ubnd[], in[], +c val ) +f RESULT = AST_MASK( THIS, MAP, INSIDE, NDIM, LBND, UBND, IN, VAL, +f STATUS ) + +* Class Membership: +* Mapping method. + +* Description: +* This is a set of functions for masking out regions within gridded data +* (e.g. an image). The functions modifies a given data grid by +* assigning a specified value to all samples which are inside (or outside +c if "inside" is zero) +f if INSIDE is .FALSE.) +* the specified Region. +* +* You should use a masking function which matches the numerical +* type of the data you are processing by replacing in +c the generic function name astMask by an appropriate 1- or +f the generic function name AST_MASK by an appropriate 1- or +* 2-character type code. For example, if you are masking data +c with type "float", you should use the function astMaskF (see +f with type REAL, you should use the function AST_MASKR (see +* the "Data Type Codes" section below for the codes appropriate to +* other numerical types). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to a Region. +c map +f MAP = INTEGER (Given) +* Pointer to a Mapping. The forward transformation should map +* positions in the coordinate system of the supplied Region +* into pixel coordinates as defined by the +c "lbnd" and "ubnd" parameters. A NULL pointer +f LBND and UBND arguments. A value of AST__NULL +* can be supplied if the coordinate system of the supplied Region +* corresponds to pixel coordinates. This is equivalent to +* supplying a UnitMap. +* +* The number of inputs for this Mapping (as given by its Nin attribute) +* should match the number of axes in the supplied Region (as given +* by the Naxes attribute of the Region). +* The number of outputs for the Mapping (as given by its Nout attribute) +* should match the number of +c grid dimensions given by the value of "ndim" +f grid dimensions given by the value of NDIM +* below. +c inside +f INSIDE = INTEGER (Given) +* A boolean value which indicates which pixel are to be masked. If +c a non-zero value +f .TRUE. +* is supplied, then all grid pixels with centres inside the supplied +* Region are assigned the value given by +c "val", +f VAL, +* and all other pixels are left unchanged. If +c zero +f .FALSE. +* is supplied, then all grid pixels with centres not inside the supplied +* Region are assigned the value given by +c "val", +f VAL, +* and all other pixels are left unchanged. Note, the Negated +* attribute of the Region is used to determine which pixel are +* inside the Region and which are outside. So the inside of a Region +* which has not been negated is the same as the outside of the +* corresponding negated Region. +* +* For types of Region such as PointList which have zero volume, +* pixel centres will rarely fall exactly within the Region. For +* this reason, the inclusion criterion is changed for zero-volume +* Regions so that pixels are included (or excluded) if any part of +* the Region passes through the pixel. For a PointList, this means +* that pixels are included (or excluded) if they contain at least +* one of the points listed in the PointList. +c ndim +f NDIM = INTEGER (Given) +* The number of dimensions in the input grid. This should be at +* least one. +c lbnd +f LBND( NDIM ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim" elements, +f An array +* containing the coordinates of the centre of the first pixel +* in the input grid along each dimension. +c ubnd +f UBND( NDIM ) = INTEGER (Given) +c Pointer to an array of integers, with "ndim" elements, +f An array +* containing the coordinates of the centre of the last pixel in +* the input grid along each dimension. +* +c Note that "lbnd" and "ubnd" together define the shape +f Note that LBND and UBND together define the shape +* and size of the input grid, its extent along a particular +c (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the +c index "j" to be zero-based). They also define +f (J'th) dimension being UBND(J)-LBND(J)+1. They also define +* the input grid's coordinate system, each pixel having unit +* extent along each dimension with integral coordinate values +* at its centre. +c in +f IN( * ) = (Given and Returned) +c Pointer to an array, with one element for each pixel in the +f An array, with one element for each pixel in the +* input grid, containing the data to be masked. The +* numerical type of this array should match the 1- or +* 2-character type code appended to the function name (e.g. if +c you are using astMaskF, the type of each array element +c should be "float"). +f you are using AST_MASKR, the type of each array element +f should be REAL). +* +* The storage order of data within this array should be such +* that the index of the first grid dimension varies most +* rapidly and that of the final dimension least rapidly +c (i.e. Fortran array indexing is used). +f (i.e. normal Fortran array storage order). +* +* On exit, the samples specified by +c "inside" are set to the value of "val". +f INSIDE are set to the value of VAL. +* All other samples are left unchanged. +c val +f VAL = (Given) +* This argument should have the same type as the elements of +c the "in" array. It specifies the value used to flag the +f the IN array. It specifies the value used to flag the +* masked data (see +c "inside"). +f INSIDE). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMask() +f AST_MASK = INTEGER +* The number of pixels to which a value of +c "badval" +f BADVAL +* has been assigned. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - An error will be reported if the overlap of the Region and +* the array cannot be determined. + +* Data Type Codes: +* To select the appropriate masking function, you should +c replace in the generic function name astMask with a +f replace in the generic function name AST_MASK with a +* 1- or 2-character data type code, so as to match the numerical +* type of the data you are processing, as follows: +c - D: double +c - F: float +c - L: long int +c - UL: unsigned long int +c - I: int +c - UI: unsigned int +c - S: short int +c - US: unsigned short int +c - B: byte (signed char) +c - UB: unsigned byte (unsigned char) +f - D: DOUBLE PRECISION +f - R: REAL +f - I: INTEGER +f - UI: INTEGER (treated as unsigned) +f - S: INTEGER*2 (short integer) +f - US: INTEGER*2 (short integer, treated as unsigned) +f - B: BYTE (treated as signed) +f - UB: BYTE (treated as unsigned) +* +c For example, astMaskD would be used to process "double" +c data, while astMaskS would be used to process "short int" +c data, etc. +f For example, AST_MASKD would be used to process DOUBLE +f PRECISION data, while AST_MASKS would be used to process +f short integer data (stored in an INTEGER*2 array), etc. +f +f For compatibility with other Starlink facilities, the codes W +f and UW are provided as synonyms for S and US respectively (but +f only in the Fortran interface to AST). + +*-- +*/ +/* Define a macro to implement the function for a specific data + type. */ +#define MAKE_MASK(X,Xtype) \ +static int Mask##X( AstRegion *this, AstMapping *map, int inside, int ndim, \ + const int lbnd[], const int ubnd[], \ + Xtype in[], Xtype val, int *status ) { \ +\ +/* Local Variables: */ \ + AstFrame *grid_frame; /* Pointer to Frame describing grid coords */ \ + AstRegion *used_region; /* Pointer to Region to be used by astResample */ \ + Xtype *c; /* Pointer to next array element */ \ + Xtype *d; /* Pointer to next array element */ \ + Xtype *out; /* Pointer to the array used for resample output */ \ + Xtype *tmp_out; /* Pointer to temporary output array */ \ + double *lbndgd; /* Pointer to array holding lower grid bounds */ \ + double *ubndgd; /* Pointer to array holding upper grid bounds */ \ + int *lbndg; /* Pointer to array holding lower grid bounds */ \ + int *ubndg; /* Pointer to array holding upper grid bounds */ \ + int idim; /* Loop counter for coordinate dimensions */ \ + int ipix; /* Loop counter for pixel index */ \ + int nax; /* Number of Region axes */ \ + int nin; /* Number of Mapping input coordinates */ \ + int nout; /* Number of Mapping output coordinates */ \ + int npix; /* Number of pixels in supplied array */ \ + int npixg; /* Number of pixels in bounding box */ \ + int result; /* Result value to return */ \ +\ +/* Initialise. */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Obtain value for the Naxes attribute of the Region. */ \ + nax = astGetNaxes( this ); \ +\ +/* If supplied, obtain values for the Nin and Nout attributes of the Mapping. */ \ + if( map ) { \ + nin = astGetNin( map ); \ + nout = astGetNout( map ); \ +\ +/* If OK, check that the number of mapping inputs matches the \ + number of axes in the Region. Report an error if necessary. */ \ + if ( astOK && ( nax != nin ) ) { \ + astError( AST__NGDIN, "astMask"#X"(%s): Bad number of mapping " \ + "inputs (%d).", status, astGetClass( this ), nin ); \ + astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ + "to specify a position.", status, \ + astGetClass( this ), nax, ( nax == 1 ) ? "" : "s" ); \ + } \ +\ +/* If OK, check that the number of mapping outputs matches the \ + number of grid dimensions. Report an error if necessary. */ \ + if ( astOK && ( ndim != nout ) ) { \ + astError( AST__NGDIN, "astMask"#X"(%s): Bad number of mapping " \ + "outputs (%d).", status, astGetClass( this ), nout ); \ + astError( AST__NGDIN, "The pixel grid requires %d coordinate value%s " \ + "to specify a position.", status, \ + ndim, ( ndim == 1 ) ? "" : "s" ); \ + } \ +\ +/* Create a new Region by mapping the supplied Region with the supplied \ + Mapping. The resulting Region represents a region in grid coordinates. */ \ + grid_frame = astFrame( ndim, "Domain=grid", status ); \ + used_region = astMapRegion( this, map, grid_frame ); \ + grid_frame = astAnnul( grid_frame ); \ +\ +/* If no Mapping was supplied check that the number of grid dimensions \ + matches the number of axes in the Region.*/ \ + } else if ( astOK && ( ( ndim != nax ) || ( ndim < 1 ) ) ) { \ + used_region = NULL; \ + astError( AST__NGDIN, "astMask"#X"(%s): Bad number of input grid " \ + "dimensions (%d).", status, astGetClass( this ), ndim ); \ + if ( ndim != nax ) { \ + astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ + "to specify an input position.", status, \ + astGetClass( this ), nax, ( nax == 1 ) ? "" : "s" ); \ + } \ +\ +/* If no Mapping was supplied and the parameters look OK, clone the \ + supplied Region pointer for use later on. */ \ + } else { \ + used_region = astClone( this ); \ + } \ +\ +/* Check that the lower and upper bounds of the input grid are \ + consistent. Report an error if any pair is not. */ \ + if ( astOK ) { \ + for ( idim = 0; idim < ndim; idim++ ) { \ + if ( lbnd[ idim ] > ubnd[ idim ] ) { \ + astError( AST__GBDIN, "astMask"#X"(%s): Lower bound of " \ + "input grid (%d) exceeds corresponding upper bound " \ + "(%d).", status, astGetClass( this ), \ + lbnd[ idim ], ubnd[ idim ] ); \ + astError( AST__GBDIN, "Error in input dimension %d.", status, \ + idim + 1 ); \ + break; \ + } \ + } \ + } \ +\ +/* Allocate memory, and then get the bounding box of this new Region in its \ + current Frame (grid coordinates). This bounding box assumes the region \ + has not been negated. */ \ + lbndg = astMalloc( sizeof( int )*(size_t) ndim ); \ + ubndg = astMalloc( sizeof( int )*(size_t) ndim ); \ + lbndgd = astMalloc( sizeof( double )*(size_t) ndim ); \ + ubndgd = astMalloc( sizeof( double )*(size_t) ndim ); \ + if( astOK ) { \ + astGetRegionBounds( used_region, lbndgd, ubndgd ); \ +\ +/* We convert the floating point bounds to integer pixel bounds, and at \ + the same time expand the box by 2 pixels at each edge to ensure that \ + rounding errors etc do not cause any of the Region to fall outside (or \ + on) the box. Do not let the expanded box extend outside the supplied \ + array bounds. Also note the total number of pixels in the supplied \ + array, and in the bounding box. */ \ + npix = 1; \ + npixg = 1; \ + for ( idim = 0; idim < ndim; idim++ ) { \ + if( lbndgd[ idim ] != AST__BAD && ubndgd[ idim ] != AST__BAD ) { \ + lbndg[ idim ] = astMAX( lbnd[ idim ], (int)( lbndgd[ idim ] + 0.5 ) - 2 ); \ + ubndg[ idim ] = astMIN( ubnd[ idim ], (int)( ubndgd[ idim ] + 0.5 ) + 2 ); \ + } else { \ + lbndg[ idim ] = lbnd[ idim ]; \ + ubndg[ idim ] = ubnd[ idim ]; \ + } \ + npix *= ( ubnd[ idim ] - lbnd[ idim ] + 1 ); \ + if( npixg >= 0 ) npixg *= ( ubndg[ idim ] - lbndg[ idim ] + 1 ); \ + } \ +\ +/* If the bounding box is null, fill the mask with the supplied value if \ + we assigning the value to the outside of the region (do the opposite if \ + the Region has been negated). */ \ + if( npixg <= 0 && astOK ) { \ + if( ( inside != 0 ) == ( astGetNegated( used_region ) != 0 ) ) { \ + c = in; \ + for( ipix = 0; ipix < npix; ipix++ ) *(c++) = val; \ + result = npix; \ + } \ +\ +/* If the bounding box is null, return without action. */ \ + } else if( npixg > 0 && astOK ) { \ +\ +/* All points outside this box are either all inside, or all outside, the \ + Region. So we can speed up processing by setting all the points which are \ + outside the box to the supplied data value (if required). This is \ + faster than checking each point individually using the Transform method \ + of the Region. We do this by supplying an alternative output array to \ + the resampling function below, which has been pre-filled with "val" at \ + every pixel. */ \ + if( ( inside != 0 ) == ( astGetNegated( used_region ) != 0 ) ) { \ +\ +/* Allocate memory for the alternative output array, and fill it with \ + "val". */ \ + tmp_out = astMalloc( sizeof( Xtype )*(size_t) npix ); \ + if( tmp_out ) { \ + c = tmp_out; \ + for( ipix = 0; ipix < npix; ipix++ ) *(c++) = val; \ + result = npix - npixg; \ + } \ +\ +/* Indicate that we will use this temporary array rather than the \ + supplied array. */ \ + out = tmp_out; \ +\ +/* If the outside of the grid box is outside the region of interest it \ + will be unchanged in the resturned array. Therefore we can use the \ + supplied array as the output array below. */ \ + } else { \ + tmp_out = NULL; \ + out = in; \ + } \ +\ +/* Temporarily invert the Region if required. The Region Transform methods \ + leave interior points unchanged and assign AST__BAD to exterior points. \ + This is the opposite of what we want (which is to leave exterior \ + points unchanged and assign VAL to interior points), so we negate the \ + region if the inside is to be assigned the value VAL.*/ \ + if( inside ) astNegate( used_region ); \ +\ +/* Invoke astResample to mask just the region inside the bounding box found \ + above (specified by lbndg and ubndg), since all the points outside this \ + box will already contain their required value. */ \ + result += astResample##X( used_region, ndim, lbnd, ubnd, in, NULL, AST__NEAREST, \ + NULL, NULL, 0, 0.0, 100, val, ndim, \ + lbnd, ubnd, lbndg, ubndg, out, NULL ); \ +\ +/* Revert to the original setting of the Negated attribute. */ \ + if( inside ) astNegate( used_region ); \ +\ +/* If required, copy the output data from the temporary output array to \ + the supplied array, and then free the temporary output array. */ \ + if( tmp_out ) { \ + c = tmp_out; \ + d = in; \ + for( ipix = 0; ipix < npix; ipix++ ) *(d++) = *(c++); \ + tmp_out = astFree( tmp_out ); \ + }\ + }\ + } \ +\ +/* Free resources */ \ + ubndg = astFree( ubndg ); \ + lbndg = astFree( lbndg ); \ + ubndgd = astFree( ubndgd ); \ + lbndgd = astFree( lbndgd ); \ + used_region = astAnnul( used_region ); \ +\ +/* If an error occurred, clear the returned result. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_MASK(LD,long double) +#endif +MAKE_MASK(D,double) +MAKE_MASK(L,long int) +MAKE_MASK(UL,unsigned long int) +MAKE_MASK(I,int) +MAKE_MASK(UI,unsigned int) +MAKE_MASK(S,short int) +MAKE_MASK(US,unsigned short int) +MAKE_MASK(B,signed char) +MAKE_MASK(UB,unsigned char) +MAKE_MASK(F,float) + +/* Undefine the macro. */ +#undef MAKE_MASK + + + +static int Match( AstFrame *this_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astMatch +* method inherited from the Frame class). + +* Description: +* This function matches the current Frame of a "template" Region +* to a "target" frame and determines whether it is possible to +* convert coordinates between them. If it is, a Mapping that +* performs the transformation is returned along with a new Frame +* that describes the coordinate system that results when this +* Mapping is applied to the current Frame of the target +* Region. In addition, information is returned to allow the axes +* in this "result" Frame to be associated with the corresponding +* axes in the target and template Frames from which they are +* derived. + +* Parameters: +* template +* Pointer to the template Region, whose current Frame +* describes the coordinate system (or set of possible +* coordinate systems) into which we wish to convert our +* coordinates. +* target +* Pointer to the target Frame. This describes the coordinate +* system in which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the index of the axis in the +* template Region's current Frame from which it is +* derived. If it is not derived from any template Region +* axis, a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the index of the target Frame axis +* from which it is derived. If it is not derived from any +* target Frame axis, a value of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will +* be returned if the requested coordinate conversion is +* possible. If returned, the forward transformation of this +* Mapping may be used to convert coordinates between the target +* Frame and the result Frame (see below) and the inverse +* transformation will convert in the opposite direction. +* result +* Address of a location where a pointer to a new Frame will be +* returned if the requested coordinate conversion is +* possible. If returned, this Frame describes the coordinate +* system that results from applying the returned Mapping +* (above) to the "target" coordinate system. In general, this +* Frame will combine attributes from (and will therefore be +* more specific than) both the target Frame and the current +* Frame of the template Region. In particular, when the +* template allows the possibility of transformaing to any one +* of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate +* conversion is possible. Otherwise zero is returned (this will +* not in itself result in an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to Region's current Frame */ + int match; /* Result to be returned */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Invoke the parent astMatch method on the current Frame within the + encapsulated FrameSet within the Region. */ + fr = astGetFrame( ((AstRegion *) this_frame)->frameset, AST__CURRENT ); + match = astMatch( fr, target, matchsub, template_axes, target_axes, map, result ); + fr = astAnnul( fr ); + +/* Return the result. */ + return match; +} + +static void MatchAxes( AstFrame *frm1_frame, AstFrame *frm2, int *axes, + int *status ) { +/* +* Name: +* MatchAxes + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void MatchAxes( AstFrame *frm1, AstFrame *frm2, int *axes ) +* int *status ) + +* Class Membership: +* Region member function (over-rides the protected astMatchAxes +* method inherited from the Frame class). + +* Description: +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +* frm1 +* Pointer to the first Frame. +* frm2 +* Pointer to the second Frame. +* axes +* Pointer to an +* integer array in which to return the indices of the axes (within +* the second Frame) that correspond to each axis within the first +* Frame. Axis indices start at 1. A value of zero will be stored +* in the returned array for each axis in the first Frame that has +* no corresponding axis in the second Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the first Frame. +* status +* Pointer to inherited status value. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping +* can be found between them using astFindFrame or astConvert. Thus, +* "corresponding axes" are not necessarily identical. For instance, +* SkyFrame axes in two Frames will match even if they describe +* different celestial coordinate systems +*/ + +/* Local Variables: */ + AstFrame *frm1; /* Pointer to Region's current Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the astMatchAxesX method on frm2, passing it the current Frame + within the encapsulated FrameSet within the Region as "frm1". */ + frm1 = astGetFrame( ((AstRegion *) frm1_frame)->frameset, AST__CURRENT ); + astMatchAxesX( frm2, frm1, axes ); + frm1 = astAnnul( frm1 ); +} + +static void MatchAxesX( AstFrame *frm2_frame, AstFrame *frm1, int *axes, + int *status ) { +/* +* Name: +* MatchAxesX + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void MatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes ) +* int *status ) + +* Class Membership: +* Region member function (over-rides the protected astMatchAxesX +* method inherited from the Frame class). + +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +* frm2 +* Pointer to the second Frame. +* frm1 +* Pointer to the first Frame. +* axes +* Pointer to an integer array in which to return the indices of +* the axes (within the first Frame) that correspond to each axis +* within the second Frame. Axis indices start at 1. A value of zero +* will be stored in the returned array for each axis in the second +* Frame that has no corresponding axis in the first Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the second Frame. +* status +* Pointer to inherited status value. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping +* can be found between them using astFindFrame or astConvert. Thus, +* "corresponding axes" are not necessarily identical. For instance, +* SkyFrame axes in two Frames will match even if they describe +* different celestial coordinate systems +*/ + +/* Local Variables: */ + AstFrame *frm2; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the current Frame in the FrameSet. */ + frm2 = astGetFrame( ((AstRegion *) frm2_frame)->frameset, AST__CURRENT ); + +/* Invoke the astMatchAxesX on the current Frame. */ + astMatchAxesX( frm2, frm1, axes ); + +/* Free resources */ + frm2 = astAnnul( frm2 ); +} + +static void Negate( AstRegion *this, int *status ) { +/* +*++ +* Name: +c astNegate +f AST_NEGATE + +* Purpose: +* Negate the area represented by a Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c void astNegate( AstRegion *this ) +f CALL AST_NEGATE( THIS, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* This function negates the area represented by a Region. That is, +* points which were previously inside the region will then be +* outside, and points which were outside will be inside. This is +* acomplished by toggling the state of the Negated attribute for +* the supplied region. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Toggle the Negated attribute. */ + astSetNegated( this, astGetNegated( this ) ? 0 : 1 ); + +} + +static void Norm( AstFrame *this_frame, double value[], int *status ) { +/* +* Name: +* Norm + +* Purpose: +* Normalise a set of Region coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void Norm( AstAxis *this, double value[], int *status ) + +* Class Membership: +* Region member function (over-rides the astNorm method +* inherited from the Frame class). + +* Description: +* This function converts a set of coordinate values for the +* current Frame of a Region, which might potentially be +* unsuitable for display to a user (for instance, may lie outside +* the expected range of values) into a set of acceptable +* alternative values suitable for display. +* +* Typically, for Frames whose axes represent cyclic values (such +* as angles or positions on the sky), this function wraps an +* arbitrary set of coordinates, so that they lie within the first +* cycle (say zero to 2*pi or -pi/2 to +pi/2). For Frames with +* ordinary linear axes, without constraints, this function will +* typically return the original coordinate values unchanged. + +* Parameters: +* this +* Pointer to the Region. +* value +* An array of double, with one element for each Region axis. +* This should contain the initial set of coordinate values, +* which will be modified in place. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to the current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astNorm method to obtain the new values. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astNorm( fr, value ); + fr = astAnnul( fr ); +} + +static void NormBox( AstFrame *this_frame, double lbnd[], double ubnd[], + AstMapping *reg, int *status ) { +/* +* Name: +* NormBox + +* Purpose: +* Extend a box to include effect of any singularities in the Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void astNormBox( AstFrame *this, double lbnd[], double ubnd[], +* AstMapping *reg, int *status ) + +* Class Membership: +* Region member function (over-rides the astNormBox method inherited +* from the Frame class). + +* Description: +* This function modifies a supplied box to include the effect of any +* singularities in the co-ordinate system represented by the Frame. +* For a normal Cartesian coordinate system, the box will be returned +* unchanged. Other classes of Frame may do other things. For instance, +* a SkyFrame will check to see if the box contains either the north +* or south pole and extend the box appropriately. + +* Parameters: +* this +* Pointer to the Frame. +* lbnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* lower axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* ubnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* upper axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* reg +* A Mapping which should be used to test if any singular points are +* inside or outside the box. The Mapping should leave an input +* position unchanged if the point is inside the box, and should +* set all bad if the point is outside the box. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to the current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astNormBox method to obtain the new values. Annul the Frame + pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astNormBox( fr, lbnd, ubnd, reg ); + fr = astAnnul( fr ); +} + +static void Offset( AstFrame *this_frame, const double point1[], + const double point2[], double offset, double point3[], int *status ) { +/* +* Name: +* Offset + +* Purpose: +* Calculate an offset along a geodesic curve. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "region.h" +* void Offset( AstFrame *this, +* const double point1[], const double point2[], +* double offset, double point3[], int *status ) + +* Class Membership: +* Region member function (over-rides the protected astOffset +* method inherited from the Frame class). + +* Description: +* This function finds the Region coordinate values of a point +* which is offset a specified distance along the geodesic curve +* between two other points. + +* Parameters: +* this +* Pointer to the Region. +* point1 +* An array of double, with one element for each Region axis. +* This should contain the coordinates of the point marking the +* start of the geodesic curve. +* point2 +* An array of double, with one element for each Region axis +* This should contain the coordinates of the point marking the +* end of the geodesic curve. +* offset +* The required offset from the first point along the geodesic +* curve. If this is positive, it will be towards the second +* point. If it is negative, it will be in the opposite +* direction. This offset need not imply a position lying +* between the two points given, as the curve will be +* extrapolated if necessary. +* point3 +* An array of double, with one element for each Region axis +* in which the coordinates of the required point will be +* returned. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - "Bad" coordinate values will also be returned if the two +* points supplied are coincident (or otherwise fail to uniquely +* specify a geodesic curve) but the requested offset is non-zero. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astOffset method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astOffset( fr, point1, point2, offset, point3 ); + fr = astAnnul( fr ); +} + +static double Offset2( AstFrame *this_frame, const double point1[2], + double angle, double offset, double point2[2], int *status ){ +/* +* Name: +* Offset2 + +* Purpose: +* Calculate an offset along a geodesic curve in a 2D Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* double Offset2( AstFrame *this, const double point1[2], double angle, +* double offset, double point2[2], int *status ); + +* Class Membership: +* Region member function (over-rides the protected astOffset2 +* method inherited from the Frame class). + +* Description: +* This function finds the Frame coordinate values of a point which +* is offset a specified distance along the geodesic curve at a +* given angle from a specified starting point. It can only be +* used with 2-dimensional Frames. +* +* For example, in a basic Frame, this offset will be along the +* straight line joining two points. For a more specialised Frame +* describing a sky coordinate system, however, it would be along +* the great circle passing through two sky positions. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This should contain the coordinates of the +* point marking the start of the geodesic curve. +* angle +* The angle (in radians) from the positive direction of the second +* axis, to the direction of the required position, as seen from +* the starting position. Positive rotation is in the sense of +* rotation from the positive direction of axis 2 to the positive +* direction of axis 1. +* offset +* The required offset from the first point along the geodesic +* curve. If this is positive, it will be in the direction of the +* given angle. If it is negative, it will be in the opposite +* direction. +* point2 +* An array of double, with one element for each Frame axis +* in which the coordinates of the required point will be returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The direction of the geodesic curve at the end point. That is, the +* angle (in radians) between the positive direction of the second +* axis and the continuation of the geodesic curve at the requested +* end point. Positive rotation is in the sense of rotation from +* the positive direction of axis 2 to the positive direction of axis 1. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - An error will be reported if the Frame is not 2-dimensional. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astOffset2 method for this Frame. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astOffset2( fr, point1, angle, offset, point2 ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static int Overlap( AstRegion *this, AstRegion *that, int *status ){ +/* +*++ +* Name: +c astOverlap +f AST_OVERLAP + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c int astOverlap( AstRegion *this, AstRegion *that ) +f RESULT = AST_OVERLAP( THIS, THAT, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* This function returns an integer value indicating if the two +* supplied Regions overlap. The two Regions are converted to a commnon +* coordinate system before performing the check. If this conversion is +* not possible (for instance because the two Regions represent areas in +* different domains), then the check cannot be performed and a zero value +* is returned to indicate this. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the first Region. +c that +f THAT = INTEGER (Given) +* Pointer to the second Region. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astOverlap() +f AST_OVERLAP = INTEGER +* A value indicating if there is any overlap between the two Regions. +* Possible values are: +* +* 0 - The check could not be performed because the second Region +* could not be mapped into the coordinate system of the first +* Region. +* +* 1 - There is no overlap between the two Regions. +* +* 2 - The first Region is completely inside the second Region. +* +* 3 - The second Region is completely inside the first Region. +* +* 4 - There is partial overlap between the two Regions. +* +* 5 - The Regions are identical to within their uncertainties. +* +* 6 - The second Region is the exact negation of the first Region +* to within their uncertainties. + +* Notes: +* - The returned values 5 and 6 do not check the value of the Closed +* attribute in the two Regions. +* - A value of zero will be returned if this function is invoked with the +* AST error status set, or if it should fail for any reason. +*-- + +* Implementation Notes: +* This function is simply a wrap-up for the protected astOverlapX +* method which performs the required processing but swaps the order +* of the two arguments. This is a trick to allow the astOverlap method +* to be over-ridden by derived classes on the basis of the class of either +* of the two arguments. +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Invoke the "astOverlapX" method with the two arguments swapped. */ + return astOverlapX( that, this ); +} + +static int OverlapX( AstRegion *that, AstRegion *this, int *status ){ +/* +*+ +* Name: +* astOverlapX + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "region.h" +* int astOverlapX( AstRegion *that, AstRegion *this ) + +* Class Membership: +* Region method. + +* Description: +* This function performs the processing for the public astOverlap +* method and has exactly the same interface except that the order +* of the two arguments is swapped. This is a trick to allow +* the astOverlap method to be over-ridden by derived classes on +* the basis of the class of either of its two arguments. +* +* See the astOverlap method for details of the interface. +*- +*/ + +/* Local Variables: */ + AstFrame *bfrm_reg1; /* Pointer to base Frame in "reg1" Frame */ + AstFrame *frm_reg1; /* Pointer to current Frame in "reg1" Frame */ + AstFrameSet *fs0; /* FrameSet connecting Region Frames */ + AstFrameSet *fs; /* FrameSet connecting Region Frames */ + AstMapping *cmap; /* Mapping connecting Region Frames */ + AstMapping *map; /* Mapping form "reg2" current to "reg1" base */ + AstMapping *map_reg1; /* Pointer to current->base Mapping in "reg1" */ + AstPointSet *ps1; /* Mesh covering second Region */ + AstPointSet *ps3; /* Mesh covering first Region */ + AstPointSet *ps4; /* Mesh covering first Region */ + AstPointSet *ps2; /* Mesh covering second Region */ + AstPointSet *reg2_mesh; /* Mesh covering second Region */ + AstPointSet *reg1_mesh; /* Mesh covering first Region */ + AstPointSet *reg2_submesh; /* Second Region mesh minus boundary points */ + AstRegion *reg1; /* Region to use as the first Region */ + AstRegion *reg2; /* Region to use as the second Region */ + AstRegion *unc1; /* "unc" mapped into Frame of first Region */ + AstRegion *unc; /* Uncertainty in second Region */ + double **ptr1; /* Pointer to mesh axis values */ + double **ptr; /* Pointer to pointset data */ + double *p; /* Pointer to next axis value */ + int *mask; /* Mask identifying common boundary points */ + int allbad; /* Were all axis values bad? */ + int allgood; /* Were all axis values good? */ + int bnd1; /* Does reg1 have a finite boundary */ + int bnd2; /* Does reg2 have a finite boundary */ + int bnd_that; /* Does "that" have a finite boundary */ + int bnd_this; /* Does "this" have a finite boundary */ + int case1; /* First region inside second region? */ + int first; /* First pass? */ + int good; /* Any good axis values found? */ + int i; /* Mesh axis index */ + int iax; /* Axis index */ + int inv0; /* Original FrameSet Invert flag */ + int ip; /* Index of point */ + int j; /* Mesh point index */ + int nc; /* Number of axis values per point */ + int np; /* Number of points in mesh */ + int result; /* Value to return */ + int reg1_neg; /* Was "reg1" negated to make it bounded? */ + int reg2_neg; /* Was "reg2" negated to make it bounded? */ + int that_neg; /* Was "that" negated to make it bounded? */ + int this_neg; /* Was "this" negated to make it bounded? */ + int touch; /* Do the Regions touch? */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Return 5 if the two Regions are equal using the astEqual method. */ + if( astEqual( this, that ) ) { + return 5; + +/* Return 6 if the two Regions are equal using the Equal method after + temporarily negating the first. */ + } else { + astNegate( this ); + result = astEqual( this, that ); + astNegate( this ); + if( result ) return 6; + } + +/* Get a FrameSet which connects the Frame represented by the second Region + to the Frame represented by the first Region. Check that the conection is + defined. */ + fs0 = astConvert( that, this, "" ); + if( !fs0 ) return 0; + inv0 = astGetInvert( fs0 ); + +/* The rest of this function tests for overlap by representing one of the + Regions as a mesh of points along its boundary, and then checking to see + if any of the points in this mesh fall inside or outside the other Region. + This can only be done if the Region has a boundary of finite length (e.g. + Circles, Boxes, etc). Other Regions (e.g. some Intervals) do not have + finite boundaries and consequently report an error if an attempt is made + to represent them using a boundary mesh. We now therefore check to see if + either of the two Regions has a finite boundary length. This will be the + case if the region is bounded, or if it can be made bounded simply by + negating it. If a Region is unbounded regardless of the setting of its + Negated flag, then it does not have a finite boundary. We leave the + Negated attributes (temporaily) set to the values that cause the + Regions to be bounded. Set flags to indicate if the Regions have been + negated. */ + bnd_this = astGetBounded( this ); + if( !bnd_this ) { + astNegate( this ); + bnd_this = astGetBounded( this ); + if( ! bnd_this ) { + astNegate( this ); + this_neg = 0; + } else { + this_neg = 1; + } + } else { + this_neg = 0; + } + + bnd_that = astGetBounded( that ); + if( !bnd_that ) { + astNegate( that ); + bnd_that = astGetBounded( that ); + if( ! bnd_that ) { + astNegate( that ); + that_neg = 0; + } else { + that_neg = 1; + } + } else { + that_neg = 0; + } + +/* If neither Regions has a finite boundary, then we cannot currently + determine any overlap, so report an error. Given more time, it + is probably possible to think of some way of determining overlap + between two unbounded Regions, but it will probably not be a common + requirement and so is currently put off to a rainy day. */ + if( !bnd_this && !bnd_that && astOK ) { + astError( AST__INTER, "astOverlap(Region): Neither of the two " + "supplied Regions (classes %s and %s) has a finite " + "boundary.", status, astGetClass(this), astGetClass(that) ); + astError( AST__INTER, "The current implementation of astOverlap " + "cannot determine the overlap between two Regions " + "unless at least one of them has a finite boundary." , status); + } + +/* If only one of the two Regions has a finite boundary, we must use its + mesh first. Choose the finite boundary Region as the "second" region. + Also store a flag indicating if the first Region has a finite boundary. */ + if( bnd_that ) { + reg1 = this; + reg2 = that; + bnd1 = bnd_this; + bnd2 = bnd_that; + reg1_neg = this_neg; + reg2_neg = that_neg; + } else { + reg1 = that; + reg2 = this; + bnd1 = bnd_that; + bnd2 = bnd_this; + reg1_neg = that_neg; + reg2_neg = this_neg; + } + +/* We may need to try again with the above selections swapped. We only do + this once though. Set a flag to indicate that we are about to start the + first pass. */ + first = 1; +L1: + +/* Get a FrameSet which connects the Frame represented by the second Region + to the Frame represented by the first Region. Check that the conection is + defined. */ + fs = astClone( fs0 ); + astSetInvert( fs, (reg2 == that ) ? inv0 : 1 - inv0 ); + if( fs ) { + +/* Get a pointer to the Frame represented by the first Region. */ + frm_reg1 = astGetFrame( reg1->frameset, AST__CURRENT ); + +/* Get a pointer to the Mapping from current to base Frame in the first + Region. */ + map_reg1 = astGetMapping( reg1->frameset, AST__CURRENT, AST__BASE ); + +/* Get the Mapping from the current Frame of the second Region to the + current Frame of the first Region. */ + cmap = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Combine these Mappings to get the Mapping from current Frame of the + second region to the base Frame of the first Region. */ + map = (AstMapping *) astCmpMap( cmap, map_reg1, 1, "", status ); + +/* Get a mesh of points covering the second Region. These points are + within the current Frame of the second Region. */ + reg2_mesh = astRegMesh( reg2 ); + +/* Transform this mesh into the base Frame of the first Region. */ + ps1 = astTransform( map, reg2_mesh, 1, NULL ); + +/* Check there are some good points in the transformed pointset. */ + good = 0; + np = astGetNpoint( ps1 ); + nc = astGetNcoord( ps1 ); + ptr1 = astGetPoints( ps1 ); + if( ptr1 ) { + for( i = 0; i < nc && !good; i++ ) { + for( j = 0; j < np; j++ ) { + if( ptr1[ i ][ j ] != AST__BAD ) { + good = 1; + break; + } + } + } + } + +/* If the transformed mesh contains no good points, swap the regions and + try again. */ + if( !good ) { + fs = astAnnul( fs ); + frm_reg1 = astAnnul( frm_reg1 ); + map_reg1 = astAnnul( map_reg1 ); + cmap = astAnnul( cmap ); + map = astAnnul( map ); + reg2_mesh = astAnnul( reg2_mesh ); + ps1 = astAnnul( ps1 ); + + if( first ) { + first = 0; + + if( !bnd_that ) { + reg1 = this; + reg2 = that; + bnd1 = bnd_this; + bnd2 = bnd_that; + reg1_neg = this_neg; + reg2_neg = that_neg; + } else { + reg1 = that; + reg2 = this; + bnd1 = bnd_that; + bnd2 = bnd_this; + reg1_neg = that_neg; + reg2_neg = this_neg; + } + goto L1; + + } else { + return 0; + } + } + +/* Also transform the Region describing the positional uncertainty within + the second supplied Region into the base Frame of the first supplied + Region. */ + unc = astGetUncFrm( reg2, AST__CURRENT ); + bfrm_reg1 = astGetFrame( reg1->frameset, AST__BASE ); + unc1 = astMapRegion( unc, map, bfrm_reg1 ); + +/* See if all points within this transformed mesh fall on the boundary of + the first Region, to within the joint uncertainty of the two Regions. If + so the two Regions have equivalent boundaries. We can only do this is + the first region is bounded. */ + if( astRegPins( reg1, ps1, unc1, &mask ) && good ) { + +/* If the boundaries are equivalent, the Regions are either identical or + are mutually exclusive. To distinguish between these cases, we + looked at the Bounded attributes. If the Bounded attribute is the same + for both Regions then they are identical, otherwise they are mutually + exclusive. */ + result = ( ( !reg1_neg && bnd1 ) == ( !reg2_neg && bnd2 ) ) ? 5 : 6; + +/* If the boundaries of the two Regions are not equivalent. */ + } else { + +/* Create a new PointSet containing those points from the mesh which are + not on the boundary of the first Region. These points are identified by + the mask array created by the astRegPins method above. */ + reg2_submesh = GetSubMesh( mask, reg2_mesh, status ); + +/* Transform the points in the submesh of the second Region into the + current Frame of the first Region. */ + (void ) astAnnul( ps1 ); + ps1 = astTransform( cmap, reg2_submesh, 1, NULL ); + +/* Transform this submesh using the first Region as a Mapping. Any points + outside the first region will be set bad in the output PointSet. */ + ps2 = astTransform( (AstMapping *) reg1, ps1, 1, NULL ); + +/* Get the number of axes and points in this PointSet. */ + nc = astGetNcoord( ps2 ); + np = astGetNpoint( ps2 ); + +/* Note if there were any common points (i.e. points on the boundary of + both regions). */ + touch = ( astGetNpoint( reg2_mesh ) != np ); + +/* Get pointers to the axis data in this PointSet, and check they can be + used safely. */ + ptr = astGetPoints( ps2 ); + if( astOK ) { + +/* Loop round all points checking if the axis values are bad. We want a + flag saying if there are any good axis values and another flag saying if + there are any bad axis values. */ + allbad = 1; + allgood = 1; + for( iax = 0; iax < nc; iax++ ) { + p = ptr[ iax ]; + for( ip = 0; ip < np; ip++,p++ ) { + if( *p == AST__BAD ) { + allgood = 0; + if( !allbad ) break; + } else { + allbad = 0; + if( !allgood ) break; + } + } + } + +/* If the entire mesh of the (potentially negated) second Region was either + on the boundary of, or inside, the (potentially negated) first region, + determine the result depending on whether the regions have been + negated and whether they are bounded. Check for impossible states (or + maybe just errors in my logic). */ + if( allgood ) { + +/* Second region has a mesh so it must be bounded. */ + if( !bnd2 && astOK ) { + astError( AST__INTER, "astOverlap(%s): Inconsistent " + "state 1 (internal AST programming error).", + status, astGetClass( this ) ); + +/* If the first region has been made bounded by negating it... */ + } else if( reg1_neg ) { + if( bnd1 ) { + +/* If the second region has been made bounded by negating it, then the + unnegated first region is completely inside the unnegated second region. */ + if( reg2_neg ) { + result = 2; + +/* If the second region was bounded without negating it, then there is + no overlap between the unnegated first region and the second region. */ + } else { + result = 1; + } + +/* If the first region has been negated then it should not be unbounded. + This is ensured by the nature of the code that sets the "this_neg" and + "that_neg" flags above. */ + } else if( astOK ) { + astError( AST__INTER, "astOverlap(%s): Inconsistent " + "state 2 (internal AST programming error).", + status, astGetClass( this ) ); + } + +/* If the first region was bounded without negating it, but the second + region was made bounded by negating it, there is partial overlap. */ + } else if( reg2_neg ) { + result = 4; + +/* If the first region was bounded without negating it, but the second + region was also bounded without negating it, the second region is + completely inside the first region. */ + } else { + result = 3; + } + +/* If part of the mesh of the second Region was inside the first region, + and part was outside, then there is partial ocverlap. */ + } else if( !allbad ) { + result = 4; + +/* If no part of the mesh of the (possibly negated) second Region was inside + the (possibly negated) first region ... */ + } else { + +/* First deal with cases where the first region is unbounded. */ + if( !bnd1 ) { + if( reg1_neg && astOK ) { + astError( AST__INTER, "astOverlap(%s): Inconsistent " + "state 5 (internal AST programming error).", + status, astGetClass( this ) ); + } else if( reg2_neg ){ + result = 2; + } else { + result = 1; + } + +/* The second region has a mesh so it must be bounded. */ + } else if( !bnd2 && astOK ) { + astError( AST__INTER, "astOverlap(%s): Inconsistent " + "state 6 (internal AST programming error).", + status, astGetClass( this ) ); + +/* So now we know both (possibly negated) regions are bounded. */ + } else { + +/* We know that none of the reg2 mesh points are inside the bounded reg1. + But this still leaves two cases: 1) reg1 could be contained completely + within reg2, or 2) there is no overlap between reg2 and reg1. To + distinguish between these two cases we use reg2 to transform a point + on the boundary of reg1. First get a mesh on the boundary of reg1. */ + reg1_mesh = astRegMesh( reg1 ); + +/* Transform this mesh into the coordinate system of the second Region. */ + ps3 = astTransform( cmap, reg1_mesh, 0, NULL ); + +/* Transform the points in this mesh using the second Region as a Mapping. + Any points outside the second region will be set bad in the output + PointSet. */ + ps4 = astTransform( (AstMapping *) reg2, ps3, 1, NULL ); + +/* Get pointers to the axis data in this PointSet,and check they can be + used safely. */ + ptr = astGetPoints( ps4 ); + if( astOK ) { + +/* Test the firts point and set a flag indicating if we are in case 1 (if + not, we must be in case 2). */ + case1 = ( ptr[ 0 ][ 0 ] != AST__BAD ); + +/* Apply logic similar to the other cases to determine the result. */ + if( reg1_neg ) { + if( case1 == ( reg2_neg != 0 ) ) { + result = 3; + } else { + result = 4; + } + } else { + if( case1 == ( reg2_neg != 0 ) ) { + result = 1; + } else { + result = 2; + } + } + } + +/* Free resources. */ + reg1_mesh = astAnnul( reg1_mesh ); + ps3 = astAnnul( ps3 ); + ps4 = astAnnul( ps4 ); + } + } + } + +/* If there was no intersection or overlap, but the regions touch, then we + consider there to be an intersection if either region is closed. */ + if( touch && result == 1 ) { + if( astGetClosed( this) || astGetClosed( that ) ) result = 4; + } + +/* Free resources.*/ + reg2_submesh = astAnnul( reg2_submesh ); + ps2 = astAnnul( ps2 ); + } + +/* Free resources.*/ + fs = astAnnul( fs ); + bfrm_reg1 = astAnnul( bfrm_reg1 ); + frm_reg1 = astAnnul( frm_reg1 ); + map_reg1 = astAnnul( map_reg1 ); + cmap = astAnnul( cmap ); + map = astAnnul( map ); + ps1 = astAnnul( ps1 ); + reg2_mesh = astAnnul( reg2_mesh ); + unc = astAnnul( unc ); + unc1 = astAnnul( unc1 ); + if( mask) mask = astFree( mask ); + } + fs0 = astAnnul( fs0 ); + +/* The returned value should take account of whether "this" or "that" is + the first Region. If "this" was used as the first Region, then the + result value calculated above is already correct. If "that" was used as + the first Region, then we need to change the result to swap "this" and + "that". */ + if( reg1 == that ) { + if( result == 2 ) { + result = 3; + } else if( result == 3 ) { + result = 2; + } + } + +/* Re-instate the original Negated flags. */ + if( this_neg ) astNegate( this ); + if( that_neg ) astNegate( that ); + +/* If not OK, return zero. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static void Overlay( AstFrame *template_frame, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template Region on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astOverlay +* method inherited from the Frame class). + +* Description: +* This function overlays attributes from the current Frame of a +* Region on to another Frame, so as to over-ride selected +* attributes of that second Frame. Normally only those attributes +* which have been specifically set in the template will be +* transferred. This implements a form of defaulting, in which a +* Frame acquires attributes from the template, but retains its +* original attributes (as the default) if new values have not +* previously been explicitly set in the template. + +* Parameters: +* template +* Pointer to the template Region, for whose current Frame +* values should have been explicitly set for any attribute +* which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of +* the "result" Frame (see below). For each axis in the result +* frame, the corresponding element of this array should contain +* the (zero-based) index of the axis in the current Frame of +* the template Region to which it corresponds. This array is +* used to establish from which template Frame axis any +* axis-dependent attributes should be obtained. +* +* If any axis in the result Frame is not associated with a +* template Frame axis, the corresponding element of this array +* should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the current Frame in the Region and invoke its + astOverlay method to overlay its attributes. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( ((AstRegion *) template_frame)->frameset, AST__CURRENT ); + astOverlay( fr, template_axes, result ); + fr = astAnnul( fr ); +} + +static void PermAxes( AstFrame *this_frame, const int perm[], int *status ) { +/* +* Name: +* PermAxes + +* Purpose: +* Permute the order of a Region's axes. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void PermAxes( AstFrame *this, const int perm[], int *status ) + +* Class Membership: +* Region member function (over-rides the astPermAxes method +* inherited from the Frame class). + +* Description: +* This function permutes the order in which the axes in the +* current Frame of a Region occur. + +* Parameters: +* this +* Pointer to the Region. +* perm +* An array of int (with one element for each axis of the +* Region's current Frame) which lists the axes in their new +* order. Each element of this array should be a (zero-based) +* axis index identifying the axes according to their old +* (un-permuted) order. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Only genuine permutations of the axis order are permitted, so +* each axis must be referenced exactly once in the "perm" array. +* - If more than one axis permutation is applied to the same Frame +* in a Region, the effects are cumulative. +*/ + +/* Local Variables: */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Invoke the astPermAxes method on the encapsulated FrameSet. */ + astPermAxes( this->frameset, perm ); + +} + +static AstFrame *PickAxes( AstFrame *this_frame, int naxes, const int axes[], + AstMapping **map, int *status ) { +/* +* Name: +* PickAxes + +* Purpose: +* Create a new Frame by picking axes from a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstFrame *PickAxes( AstFrame *this, int naxes, const int axes[], +* AstMapping **map, int *status ) + +* Class Membership: +* Region member function (over-rides the astPickAxes protected +* method inherited from the Frame class). + +* Description: +* This function creates a new Frame whose axes are copies of axes +* picked from the encapsulated Frame of an existing Region. Other +* Frame attributes are also copied from this existing Frame to the +* new Frame. Zero or more of the original axes may be picked in +* any order, but each can be used only once. Additional axes (with +* default characteristics) may be included in the new Frame if +* required. +* +* Optionally, a Mapping that converts between the original Frame's +* axes and those of the new Frame may also be returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of axes required in the new Frame. +* axes +* Pointer to an array of int with naxes elements. This should +* contain (zero based) axis indices specifying the axes which +* are to be included in the new Frame, in the order +* required. Each axis index may occur only once. +* +* If additional (default) axes are also to be included, the +* corresponding elements of this array should be set to -1. +* map +* Address of a location to receive a pointer to a new +* Mapping. This will be a PermMap (or a UnitMap as a special +* case) that describes the axis permutation that has taken +* place between the current Frame of the Region and the new +* Frame. The forward transformation will convert from the +* original Region's axes to the new one's, and vice versa. +* +* If this Mapping is not required, a NULL value may be supplied +* for this parameter. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new Frame. + +* Notes: +* - The class of object returned may differ from that of the +* original current Frame, depending on which axes are +* selected. For example, if a single axis is picked from a +* SkyFrame (which always has two axes), the resulting Frame cannot +* be a valid SkyFrame, so will revert to the parent class (Frame) +* instead. +* - The new Frame contains a deep copy of all the data selected +* from the original current Frame. Modifying the new Frame will +* therefore not affect the Region or the Frames it contains. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *cfrm; /* Current Frame from input Region */ + AstFrame *frame; /* Pointer to Frame to be returned */ + AstMapping *cbmap; /* Base->current Mapping from input Region */ + AstMapping *fsmap; /* Mapping from selected current to base axes */ + AstRegion *breg; /* Region spanning selected base Frame axes */ + AstRegion *creg; /* Region spanning selected current Frame axes */ + AstRegion *this; /* Pointer to Region structure */ + int *base_axes; /* Holds selected base frame axis indices */ + int def; /* Were any default axes requested? */ + int i; /* Axis index */ + int nbase; /* No. of selected base Frame axes */ + +/* Initialise the returned pointers. */ + if ( map ) *map = NULL; + frame = NULL; + +/* Check the global error status. */ + if ( !astOK ) return frame; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Check that a valid set of axes is being selected . */ + astValidateAxisSelection( this, naxes, axes, "astPickAxes" ); + +/* Pick the required axes from the current Frame of the encapsulated + FrameSet. */ + cfrm = astGetFrame( this->frameset, AST__CURRENT ); + frame = astPickAxes( cfrm, naxes, axes, map ); + +/* See if any default axes are to be included in the returned Frame. */ + def = 0; + for( i = 0; i < naxes; i++ ) { + if( axes[ i ] < 0 ) def = 1; + } + +/* Regions cannot yet include extra default axes in the returned Frame + so return a basic Frame if any default axes were requested. */ + if( ! def ) { + +/* We now see if the requested set of current Frame axes correspond to a + unique set of base Frame axes. If they do, we may be able to return a + Region spanning the selected axes rather than just a Frame. The check + is performed by attempting to split the current->base Mapping. */ + cbmap = astGetMapping( this->frameset, AST__CURRENT, AST__BASE ); + base_axes = astMapSplit( cbmap, naxes, axes, &fsmap ); + +/* Check the Mapping could be split. */ + if( base_axes ) { + +/* Store the number of base Frame axes that correspond to the requested + set of current Frame axes. */ + nbase = astGetNout( fsmap ); + +/* Attempt to create a new Region that spans the corresponding set of + base Frame axes. */ + breg = astRegBasePick( this, nbase, base_axes ); + if( breg ) { + +/* Use the split Mapping to map the base Frame region into the requested + Frame. We invert the "fsmap" first so that it maps the selected base + Frame axes into the selected current Frame axes. */ + astInvert( fsmap ); + creg = astMapRegion( breg, fsmap, frame ); + +/* Copy properties from the old Region to the new Region. */ + astRegOverlay( creg, this, 0 ); + +/* Return this new Region in place of the simple Frame found above. */ + (void) astAnnul( frame ); + frame = (AstFrame *) creg; + +/* Free resources */ + breg = astAnnul( breg ); + } + fsmap = astAnnul( fsmap ); + base_axes = astFree( base_axes ); + } + cbmap = astAnnul( cbmap ); + } + cfrm = astAnnul( cfrm ); + +/* If an error occurred, annul the Mapping pointer (if requested) and + the new Frame pointer. */ + if ( !astOK ) { + if ( map ) *map = astAnnul( *map ); + frame = astAnnul( frame ); + } + +/* Return the pointer to the new Frame. */ + return frame; +} + +static void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +*+ +* Name: +* astRegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astRegBaseBox( AstRegion *this, double *lbnd, double *ubnd ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. + +*- +*/ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* This abstract implementation simply reports an error. All sub-classes of + Region should over-ride this to return appropriate values. */ + astError( AST__INTER, "astRegBaseBox(%s): The %s class does not implement " + "the astRegBaseBox method inherited from the Region class " + "(internal AST programming error).", status, astGetClass( this ), + astGetClass( this ) ); +} + +static void RegBaseBox2( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +*+ +* Name: +* astRegBaseBox2 + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astRegBaseBox2( AstRegion *this, double *lbnd, double *ubnd ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function is similar to astRegBaseBox in that it returns the +* upper and lower axis bounds of a Region in the base Frame of the +* encapsulated FrameSet. But, in addition to assuming that the +* supplied Region has not been negated, it also assumes that any +* component Regions contained within the supplied Region have not been +* negated. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. + +*- +*/ + +/* This base class implementation simply calls astRegBaseBox. Sub-classes + which contain component Regions should override it. */ + astRegBaseBox( this, lbnd, ubnd ); + +} + +static AstPointSet *RegBaseGrid( AstRegion *this, int *status ){ +/* +*+ +* Name: +* astRegBaseGrid + +* Purpose: +* Return a PointSet containing points spread through the volume of a +* Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstPointSet *astRegBaseGrid( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a PointSet containing a set of points spread +* through the volume of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* Pointer to the PointSet. If the Region is unbounded, a NULL pointer +* will be returned. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstBox *box; + AstFrame *frmb; + AstPointSet *ps1; + AstPointSet *ps2; + AstPointSet *result; + double **ptr2; + double **ptr; + double *lbnd; + double *ubnd; + int good; + int ic; + int ip; + int ipr; + int meshsize; + int naxb; + int np; + int npnt2; + int ntry; + +/* Initialise */ + result= NULL; + +/* Check the local error status. */ + if ( !astOK ) return NULL; + +/* If the Region structure contains a pointer to a PointSet holding + positions spread over the volume of the Region in the base Frame, + return it. */ + if( this->basegrid ) { + result = astClone( this->basegrid ); + +/* Otherwise, check the Region is bounded. */ + } else if( astGetBounded( this ) ) { + +/* Get the original MeshSize attribute. */ + meshsize = astGetMeshSize( this ); + +/* Get the base Frame bounding box. */ + naxb = astGetNin( this->frameset ); + lbnd = astMalloc( sizeof( double )*(size_t)naxb ); + ubnd = astMalloc( sizeof( double )*(size_t)naxb ); + astRegBaseBox( this, lbnd, ubnd ); + +/* Create a Box covering this bounding box. */ + frmb = astGetFrame( this->frameset, AST__BASE ); + box = astBox( frmb, 1, lbnd, ubnd, NULL, "", status ); + +/* Loop until we have a grid of nearly the right size. Make at most three + attempts. */ + ipr = 0; + np = meshsize; + ntry = 0; + while( ntry++ < 3 ) { + +/* Copy the MeshSize attribute to the new Box since this will be used by + the invocation of astRegBaseGrid below. */ + astSetMeshSize( box, np ); + +/* Invoke the Box astRegGrid method. Note, the Box class overrides this + implementation of astRegBaseGrid and does not (must not) invoke this + implementation, in order to avoid an infinite loop. */ + ps1 = astRegBaseGrid( box ); + +/* Some of the base Frame points in the above bounding box will fall outside + the supplied Region. Use the Region as a Mapping to determine which they + are. Since the points are base Frame points, use astBTransform rather + than astTransform. */ + ps2 = astBTransform( this, ps1, 1, NULL ); + +/* We now create a PointSet which is a copy of "ps2" but with all the bad + points (i.e. the points in the bounding box grid which are not inside + the supplied Region) removed. Create a result PointSet which is the same + size as "ps2", then copy just the good points from "ps2" to the result + PointSet, keeping a record of the number of points copied. */ + ptr2 = astGetPoints( ps2 ); + npnt2 = astGetNpoint( ps2 ); + result = astPointSet( npnt2, naxb, "", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Initialise the index of the next point to be stored in "result". */ + ipr = 0; + +/* Loop round all points in "ps2" */ + for( ip = 0; ip < npnt2; ip++ ) { + +/* Copy each axis value for this point from "ps2" to "result". If a bad + axis value is encountered, flag that the point is bad and break out of + the axis loop. */ + good = 1; + for( ic = 0; ic < naxb; ic++ ) { + if( ptr2[ ic ][ ip ] == AST__BAD ) { + good = 0; + break; + } else { + ptr[ ic ][ ipr ] = ptr2[ ic ][ ip ]; + } + } + +/* If the current point has no bad axis values, increment the index of + the next point to be stored in "result". */ + if( good ) ipr++; + } + } + +/* Free resources */ + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + +/* Leave the loop if an error has occurred. */ + if( !astOK ) break; + +/* If the number of points in the grid is within 5% of the target value, + it is good enough, so break. */ + if( fabs( (double)( ipr - meshsize ) )/meshsize < 0.05 ) break; + +/* Otherwise, adjust the target size of the grid by the ratio by which it + is in error. Don't do this if we have reached the maximum number of + re-tries. */ + if( ntry < 3 ) { + if( ipr == 0 ) { + np *= 10; + } else { + np *= (double)meshsize/(double)ipr; + } + result = astAnnul( result ); + } + } + +/* Truncate the "result" PointSet to exclude any unused space at the end + of the axis values arrays. */ + if( astOK ) astSetNpoint( result, ipr ); + +/* Free resources */ + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + frmb = astAnnul( frmb ); + box = astAnnul( box ); + +/* Cache the new grid for future use. */ + if( astOK ) this->basegrid = astClone( result ); + } + +/* Annul the result if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result */ + return result; +} + +static AstRegion **RegSplit( AstRegion *this, int *nlist, int *status ){ +/* +*+ +* Name: +* astRegSplit + +* Purpose: +* Split a Region into a list of disjoint component Regions. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstRegion **astRegSplit( AstRegion *this, int *nlist ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function splits the supplied Region into a set of disjoint +* component Regions. If the Region cannot be split, then the returned +* array contains only one pointer - a clone of the supplied Region +* pointer. + +* Parameters: +* this +* Pointer to the Region. +* nlist +* Pointer to an int in which to return the number of elements in +* the returned array. + +* Returned Value: +* Pointer to dynamically alloctaed memory holding an array of Region +* pointers. The length of this array is given by the value returned +* in "*nlist". The pointers in the returned array should be annulled +* using astAnnul when no longer needed, and the memory used to hold +* the array should be freed using astFree. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables; */ + AstRegion **result; + +/* Initialise. */ + result = NULL; + *nlist = 0; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* The base class just returns an array containing a clone of the + supplied Region pointer. */ + result = astMalloc( sizeof( *result ) ); + if( astOK ) { + result[ 0 ] = astClone( this ); + *nlist = 1; + } + + if( !astOK ) { + result = astFree( result ); + *nlist = 0; + } + + return result; +} + +static AstPointSet *RegBaseMesh( AstRegion *this, int *status ){ +/* +*+ +* Name: +* astRegBaseMesh + +* Purpose: +* Return a PointSet containing points spread around the boundary of a +* Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstPointSet *astRegBaseMesh( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a PointSet containing a set of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the uncertainties which were +* supplied when the Region was created. +* +* If the Region has no boundary (i.e. is equivalent to a NullRegion), the +* returned PointSet will contain a single point with a value of AST__BAD +* on every axis. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Check the local error status. */ + if ( !astOK ) return NULL; + +/* This abstract method must be over-ridden by each concrete sub-class. + Report an error if this null imlementation is called.*/ + astError( AST__INTER, "astRegBaseMesh(%s): The %s class does not implement " + "the astRegBaseMesh method inherited from the Region class " + "(internal AST programming error).", status, astGetClass( this ), + astGetClass( this ) ); + return NULL; +} + +static AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, + int *status ){ +/* +*+ +* Name: +* astRegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstRegion *astRegBasePick( AstRegion *this, int naxes, const int *axes ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - This base implementation returns NULL unless all base Frame axes +* are selected (possibly in a permuted order). +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ +/* Local Variables: */ + AstFrame *fr; /* Pointer to the Region's base Frame */ + AstRegion *result; /* The returned Region pointer */ + int found; /* Has the current axis index been found yet? */ + int i; /* Axis index */ + int j; /* Index into the "axes" array */ + int nax; /* No. of base Frame axes */ + int ok; /* Are we doing a genuine axis permutation? */ + int unit; /* Is the axis permutation a unit map? */ + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the base Frame int he encapsulated FrameSet. */ + fr = astGetFrame( this->frameset, AST__BASE ); + +/* See how many axes it has. We only proceed if we are selecting all axes + in the base Frame. */ + nax = astGetNaxes( fr ); + if( nax == naxes ) { + +/* We now check that the axes array is a genuine permutation of all axes. + This means that all axis indices must occur once, and only once, within + the "axes" array. Look for each axis index in turn. */ + unit = 1; + ok = 1; + for( i = 0; i < nax && ok; i++ ) { + +/* Check each element of the axes array to see if it holds the axis index + currently being looked for. */ + found = 0; + for( j = 0; j < nax; j++ ) { + +/* If so, if this axis index has already been found, break out of the + loop. */ + if( axes[ j ] == i ) { + if( found ) { + ok = 0; + break; + } + found = 1; + +/* Note if we do not have a unit map (i.e. each axis is permuted onto itself). */ + if( i != j ) unit = 0; + } + } + +/* If the axis index was not found, we do not have a genuine axis + permutation. */ + if( !found ) ok = 0; + } + +/* If we have a genuine axis permutation, create a Region which is a copy + of the supplied region and set it to represent its base Frame. */ + if( ok ) { + result = astCopy( this ); + astSetRegFS( result, fr ); + +/* If the axis selection is not equivalent to a unit mapping, we now + permute the axes. */ + if( !unit ) astPermAxes( result, axes ); + } + } + +/* Free resources. */ + fr = astAnnul( fr ); + +/* Returned the result. */ + return result; +} + +static double *RegCentre( AstRegion *this, double *cen, double **ptr, + int index, int ifrm, int *status ){ +/* +*+ +* Name: +* astRegCentre + +* Purpose: +* Re-centre a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* double *astRegCentre( AstRegion *this, double *cen, double **ptr, +* int index, int ifrm ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function shifts the centre of the supplied Region to a +* specified position, or returns the current centre of the Region. + +* Parameters: +* this +* Pointer to the Region. +* cen +* Pointer to an array of axis values, giving the new centre. +* Supply a NULL value for this in order to use "ptr" and "index" to +* specify the new centre. +* ptr +* Pointer to an array of pointers, one for each axis in the Region. +* Each pointer locates an array of axis values. This is the format +* returned by the PointSet method astGetPoints. Only used if "cen" +* is NULL. +* index +* The index of the point within the arrays identified by "ptr" at +* which is stored the coords for the new centre position. Only used +* if "cen" is NULL. +* ifrm +* Should be AST__BASE or AST__CURRENT. Indicates whether the centre +* position is supplied and returned in the base or current Frame of +* the FrameSet encapsulated within "this". + +* Returned Value: +* If both "cen" and "ptr" are NULL then a pointer to a newly +* allocated dynamic array is returned which contains the centre +* coords of the Region. This array should be freed using astFree when +* no longer needed. If either of "ptr" or "cen" is not NULL, then a +* NULL pointer is returned. + +* Notes: +* - Any bad (AST__BAD) centre axis values are ignored. That is, the +* centre value on such axes is left unchanged. +* - Some Region sub-classes do not have a centre. Such classes will report +* an AST__INTER error code if this method is called with either "ptr" or +* "cen" not NULL. If "ptr" and "cen" are both NULL, then no error is +* reported if this method is invoked on a Region of an unsuitable class, +* but NULL is always returned. + +*- +*/ + +/* Local Variables: */ + double *result; + +/* Initialise */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* This abstract method must be over-ridden by each concrete sub-class + which allows the centre to be shifted. Report an error if this null + imlementation is called to set a new centre. If it is called to + enquire the current centre, then return a NULL pointer. */ + if( ptr || cen ) astError( AST__INTER, "astRegCentre(%s): The %s " + "class does not implement the astRegCentre method " + "inherited from the Region class (internal AST " + "programming error).", status, astGetClass( this ), + astGetClass( this ) ); + + return NULL; +} + +static void RegClearAttrib( AstRegion *this, const char *aattrib, + char **base_attrib, int *status ) { +/* +*+ +* Name: +* astRegClearAttrib + +* Purpose: +* Clear an attribute value for a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astRegClearAttrib( AstRegion *this, const char *aattrib, +* char **base_attrib ) + +* Class Membership: +* Region virtual function + +* Description: +* This function clears the value of a named attribute in both the base +* and current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* aattrib +* Pointer to a null terminated string holding the attribute name. +* base_attrib +* Address of a location at which to return a pointer to the null +* terminated string holding the attribute name which was cleared in +* the base Frame of the encapsulated FrameSet. This may differ from +* the supplied attribute if the supplied attribute contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +*- +*/ + +/* Local Variables: */ + AstFrame *frm; + AstMapping *junkmap; + AstMapping *map; + AstRegion *unc; + char *attrib; + char *battrib; + char buf1[ 100 ]; + int *outs; + int axis; + int baxis; + int i; + int len; + int nc; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Produce a lower case version of the attribute name string */ + nc = strlen( aattrib ); + attrib = astMalloc( nc + 1 ); + for( i = 0; i < nc; i++ ) attrib[ i ] = tolower( aattrib[ i ] ); + attrib[ nc ] = 0; + +/* Clear the attribute in the current Frame in the encapsulated FrameSet. + Use the protected astClearAttrib method which does not cause the Frame + to be remapped within the FrameSet. */ + frm = astGetFrame( this->frameset, AST__CURRENT ); + astClearAttrib( frm, attrib ); + frm = astAnnul( frm ); + +/* Indicate that we should use the supplied attribute name with the base Frame. */ + battrib = NULL; + +/* If the attribute name contains an axis number, we need to create a new + attribute name which refers to the corresponding base Frame axis + (since the base<->current Mapping may permute the axes). First parse the + supplied attribute name to locate any axis index. */ + len = strlen( attrib ); + if( nc = 0, ( 2 == astSscanf( attrib, "%[^(](%d) %n", buf1, &axis, + &nc ) ) && ( nc >= len ) ) { + +/* If found, convert the axis index from one-based to zero-based. */ + axis--; + +/* See if the specified current Frame axis is connected to one and only + one base Frame axis. If so, get the index of the base Frame axis. */ + map = astGetMapping( this->frameset, AST__CURRENT, AST__BASE ); + outs = astMapSplit( map, 1, &axis, &junkmap ); + if( junkmap && astGetNout( junkmap ) == 1 ) { + baxis = outs[ 0 ]; + +/* If the base Frame axis index is different to the current Frame axis + index, create a new attribute name string using the base Frame axis index. */ + if( baxis != axis ) { + battrib = astMalloc( strlen( attrib ) + 10 ); + if( battrib ) sprintf( battrib, "%s(%d)", buf1, baxis + 1 ); + } + +/* If there is no one base Frame axis which corresponds to the supplied + current Frame axis, report an error. */ + } else if( astOK ) { + astError( AST__INTER, "astRegClearAttrib(%s): Unable to clear " + "attribute \"%s\" in the base Frame of the %s", status, + astGetClass( this ), attrib, astGetClass( this ) ); + astError( AST__INTER, "There is no base Frame axis corresponding " + "to current Frame axis %d\n", status, axis + 1 ); + } + +/* Free resources */ + outs = astFree( outs ); + if( junkmap ) junkmap = astAnnul( junkmap ); + map = astAnnul( map ); + } + +/* Clear the appropriate attribute name in the base Frame. This time ensure + that any error caused by the attribute name is annulled. Also clear it in + any uncertainty Region (the current Frame of the uncertainty Region is + assumed to be equivalent to the base Frame of the parent Region). */ + frm = astGetFrame( this->frameset, AST__BASE ); + if( frm ) { + rep = astReporting( 0 ); + astClearAttrib( frm, battrib ? battrib : attrib ); + if( astTestUnc( this ) ) { + unc = astGetUncFrm( this, AST__BASE ); + astRegClearAttrib( unc, battrib ? battrib : attrib, NULL ); + unc = astAnnul( unc ); + } + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + } + frm = astAnnul( frm ); + +/* If required return the modified base Frame attribute name. Otherwise, + free it. */ + if( base_attrib ) { + if( battrib ) { + *base_attrib = battrib; + } else { + *base_attrib = astStore( NULL, attrib, strlen( attrib ) + 1 ); + } + } else { + battrib = astFree( battrib ); + } + +/* Since the base Frame has been changed, any cached information calculated + on the basis of the base Frame properties may no longer be up to date. */ + astResetCache( this ); + +/* Free resources. */ + attrib = astFree( attrib ); + +} + +static AstPointSet *RegGrid( AstRegion *this, int *status ){ +/* +*+ +* Name: +* astRegGrid + +* Purpose: +* Return a PointSet containing points spread through the volume of a +* Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstPointSet *astRegGrid( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a PointSet containing a mesh of points spread +* throughout the volume of the Region. The points refer to the current +* Frame of the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the uncertainties which were +* supplied when the Region was created. Annul the pointer using +* astAnnul when it is no longer needed. + +* Notes: +* - It should not be assumed that the returned points are evenly +* spaced withint he volume. +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables; */ + AstMapping *map; /* Base -> current Frame Mapping */ + AstPointSet *result; /* Pointer to returned PointSet */ + +/* Initialise the returned pointer */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* If the Region structure does not contain a pointer to a PointSet holding + positions evenly spread over the volume of the Region in the base + Frame, create one now. Note, we cannot cache the grid in the current + Frame in this way since the current Frame grid depends on the proprties + of the current Frame (e.g. System) which can be changed at any time. */ + if( !this->basegrid ) this->basegrid = astRegBaseGrid( this ); + +/* Get the simplified base->current Mapping */ + map = astRegMapping( this ); + +/* If the Mapping is a UnitMap, just return a clone of the PointSet + pointer stored in the Region structure. */ + if( astIsAUnitMap( map ) ){ + result = astClone( this->basegrid ); + +/* Otherwise, create a new PointSet holding the above points transformed + into the current Frame. */ + } else { + result = astTransform( map, this->basegrid, 1, NULL ); + } + +/* Free resources.*/ + map = astAnnul( map ); + +/* If an error has occurred, annul the returned PointSet. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *RegMesh( AstRegion *this, int *status ){ +/* +*+ +* Name: +* astRegMesh + +* Purpose: +* Return a PointSet containing points spread over the boundary of a +* Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstPointSet *astRegMesh( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the current Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* Pointer to the PointSet. The axis values in this PointSet will have +* associated accuracies derived from the uncertainties which were +* supplied when the Region was created. Annul the pointer using +* astAnnul when it is no longer needed. + +* Notes: +* - It should not be assumed that the returned points are evenly +* spaced on the boundary. +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables; */ + AstMapping *map; /* Base -> current Frame Mapping */ + AstPointSet *bmesh; /* Base Frame mesh */ + AstPointSet *result; /* Pointer to returned PointSet */ + +/* Initialise the returned pointer */ + result = NULL; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to a PointSet holding positions evenly spread over the + boundary of the Region in the base Frame. */ + bmesh = astRegBaseMesh( this ); + +/* Get the simplified base->current Mapping */ + map = astRegMapping( this ); + +/* If the Mapping is a UnitMap, just return a clone of the mesh PointSet + pointer. */ + if( astIsAUnitMap( map ) ){ + result = astClone( bmesh ); + +/* Otherwise, create a new PointSet holding the above points transformed + into the current Frame. */ + } else { + result = astTransform( map, bmesh, 1, NULL ); + } + +/* Free resources.*/ + bmesh = astAnnul( bmesh ); + map = astAnnul( map ); + +/* If an error has occurred, annul the returned PointSet. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int RegDummyFS( AstRegion *this, int *status ){ +/* +*+ +* Name: +* astRegDummyFS + +* Purpose: +* Check if a Region has a dummy FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* int astRegDummyFS( AstRegion *this ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a flag indicating if the supplied Region has +* a dummy FrameSet. +* +* The astDump method for a Region may choose not to include the +* Region's FrameSet in the dump, depending on the value of the +* RegionFS attribute and the nature of the FrameSet. If the FrameSet +* is omitted from the Dump, then special action has to be taken when +* the dump is subsequently read in and used to re-create the Region. +* On encounterting such a dump, the astLoadRegion function will create +* a dummy FrameSet and associate it with the reconstructed Region. +* The new Region should not be used however until this dummy FrameSet +* has been replaced by the correct FrameSet. Performing this replacement +* is the responsibility of the parent class (i.e. the class which choose +* to omit the FrameSet from the dump). These will usually be Region +* classes which encapsulate other Regions, such as CmpRegion, Prism, +* Stc, etc. +* +* This function can be used by astLoad... methods in sub-classes to +* determine if a newly loaded component Region has a dummy FrameSet. If +* so the astLoad function should either use the astSetRegFS method to +* store a new FrameSet in the component Region. If the parent Region +* itself has a dummy FrameSet (i.e. is a component Region contained +* within a higher level Region) then it cannot do this and should +* ignore the presence of the dummy FrameSet (it then becomes the +* responsibility of hte parent Region to load appropriate FrameSets +* into all its components). + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* Non-zero if the Region has a dummy FrameSet. + +*- +*/ + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* The Ident attribute of the FrameSet will be set to DUMMY_FS if the + FrameSet is a dummy. */ + return !strcmp( astGetIdent( this->frameset ), DUMMY_FS ); +} + +static int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +*+ +* Name: +* astRegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* int astRegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Region. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Region "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Region. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*- +*/ + +/* Check the inherited status. */ + if( !astOK ) return 0; + +/* This abstract implementation simply reports an error. All sub-classes of + Region should over-ride this to return appropriate values. */ + astError( AST__INTER, "astRegPins(%s): The %s class does not implement " + "the astRegPins method inherited from the Region class " + "(internal AST programming error).", status, astGetClass( this ), + astGetClass( this ) ); + return 0; +} + +static void GetRegionBounds( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +*++ +* Name: +c astGetRegionBounds +f AST_GETREGIONBOUNDS + +* Purpose: +* Returns the bounding box of Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c void astGetRegionBounds( AstRegion *this, double *lbnd, double *ubnd ) +f CALL AST_GETREGIONBOUNDS( THIS, LBND, UBND, STATUS ) + +* Class Membership: +* Region method. + +* Description: +c This function +f This routine +* returns the upper and lower limits of a box which just encompasses +* the supplied Region. The limits are returned as axis values within +* the Frame represented by the Region. The value of the Negated +* attribute is ignored (i.e. it is assumed that the Region has not +* been negated). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c lbnd +f LBND() = DOUBLE PRECISION (Returned) +c Pointer to an +f An +* array in which to return the lower axis bounds covered by the Region. +* It should have at least as many elements as there are axes in the +* Region. If an axis has no lower limit, the returned value will +* be the largest possible negative value. +c ubnd +f UBND() = DOUBLE PRECISION (Returned) +c Pointer to an +f An +* array in which to return the upper axis bounds covered by the Region. +* It should have at least as many elements as there are axes in the +* Region. If an axis has no upper limit, the returned value will +* be the largest possible positive value. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - The value of the Negated attribute is ignored (i.e. it is assumed that +* the Region has not been negated). +* - If an axis has no extent on an axis then the lower limit will be +* returned larger than the upper limit. Note, this is different to an +* axis which has a constant value (in which case both lower and upper +* limit will be returned set to the constant value). +* - If the bounds on an axis cannot be determined, AST__BAD is returned for +* both upper and lower bounds + +*-- +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame */ + AstMapping *smap; /* Simplified base -> current Mapping */ + AstPointSet *bmesh; /* PointSet holding base Frame mesh */ + AstPointSet *cmesh; /* PointSet holding current Frame mesh */ + double **bptr; /* Pointer to PointSet coord arrays */ + double *blbnd; /* Lower bounds in base Frame */ + double *bubnd; /* Upper bounds in base Frame */ + double *p; /* Array of values for current axis */ + double width; /* Width of bounding box on i'th axis */ + int i; /* Axis count */ + int ip; /* Index of current corner */ + int j; /* Timer for low/high swaps */ + int jmax; /* Increment between low/high swaps */ + int lo; /* Assign low bound to next corner? */ + int nbase; /* Number of base Frame axes */ + int ncur; /* Number of current Frame axes */ + int npos; /* Number of box corners */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Get the simplified base to current Mapping. */ + smap = astRegMapping( this ); + +/* If the simplified Mapping is a UnitMap, just store the base box bounds + in the returned arrays */ + if( astIsAUnitMap( smap ) ) { + astRegBaseBox( this, lbnd, ubnd ); + +/* Otherwise, we get a mesh of points over the boundary of the Region within + the base Frame, transform them into the current Frame, and find their bounds. */ + } else { + +/* If the Region is bounded, we can get a genuine mesh of points on the + boundary of the Region. */ + if( astGetBounded( this ) ) { + bmesh = astRegBaseMesh( this ); + +/* If the Region is not bounded, no mesh can be created so we use the + corners of the base frame bounding box instead. */ + } else { + +/* Get workspace to hold the bounds of the region within the base Frame. */ + nbase = astGetNin( smap ); + blbnd = astMalloc( sizeof( double )*nbase ); + bubnd = astMalloc( sizeof( double )*nbase ); + +/* Get the base Frame bounding box. */ + astRegBaseBox( this, blbnd, bubnd ); + +/* Get the number of corners in the base Frame bounding box. */ + npos = pow( 2, nbase ); + +/* Create a PointSet to hold the positions at the corners in the base + frame box. */ + bmesh = astPointSet( npos, nbase, " ", status ); + bptr = astGetPoints( bmesh ); + if( bptr ) { + +/* Store the coordinates of the box corners in the PointSet. */ + jmax = 1; + for( i = 0; i < nbase; i++ ) { + p = bptr[ i ]; + + lo = 1; + j = 0; + for( ip = 0; ip < npos; ip++,j++ ) { + if( j == jmax ) { + lo = 1 - lo; + j = 0; + } + p[ ip ] = lo ? blbnd[ i ] : bubnd[ i ]; + } + + jmax *= 2; + } + } + +/* Release resources. */ + blbnd = astFree( blbnd ); + bubnd = astFree( bubnd ); + } + +/* Create a new PointSet holding the above points transformed into the + current Frame. */ + cmesh = astTransform( smap, bmesh, 1, NULL ); + +/* Get the axis bounds of this PointSet. */ + astBndPoints( cmesh, lbnd, ubnd ); + +/* There is a possibility that these bounds may span a singularity in the + coordinate system such as the RA=0 line in a SkyFrame. So for each + axis we ensure the width (i.e. "ubnd-lbnd" ) is correct. */ + frm = astGetFrame( this->frameset, AST__CURRENT ); + ncur = astGetNaxes( frm ); + + for( i = 0; i < ncur; i++ ) { + width = astAxDistance( frm, i + 1, lbnd[ i ], ubnd[ i ] ); + if( width != AST__BAD ) { + ubnd[ i ] = lbnd[ i ] + width; + } else { + ubnd[ i ] = AST__BAD; + lbnd[ i ] = AST__BAD; + } + } + +/* Release resources. */ + frm = astAnnul( frm ); + bmesh = astAnnul( bmesh ); + cmesh = astAnnul( cmesh ); + } + smap = astAnnul( smap ); +} + +static void GetRegionBounds2( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +*+ +* Name: +* astGetRegionBounds + +* Purpose: +* Returns the bounding box of Region. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "region.h" +* void astGetRegionBounds2( AstRegion *this, double *lbnd, double *ubnd ) + +* Class Membership: +* Region method. + +* Description: +* This function is like astGetRegionBounds, in that it returns the upper +* and lower limits of a box which just encompasses the supplied Region, +* as axis values within the Frame represented by the Region. But, in +* addition to assuming that the supplied Region has not been negated, it +* also assumes that any component Regions contained within the supplied +* Region have not been negated. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region. It should have at least as many elements +* as there are axes in the Region. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region. It should have at least as many elements +* as there are axes in the Region. + +* Notes: +* - The value of the Negated attribute is ignored (i.e. it is assumed that +* the Region has not been negated). The Nagated attributes of any +* component Regions are also ignored. + +*- +*/ + +/* Local Variables: */ + AstMapping *smap; /* Simplified base -> current Mapping */ + double *lbndb; /* Pointer to lower bounds on base box */ + double *ubndb; /* Pointer to upper bounds on base box */ + int i; /* Axis count */ + int nbase; /* Number of base Frame axes */ + int ncur; /* Number of current Frame axes */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Find the number of axes in the base and current Frames of the + encapsulated FrameSet. */ + nbase = astGetNin( this->frameset ); + ncur = astGetNout( this->frameset ); + +/* Get the bounding box in the base Frame of the encapsulated FrameSet. */ + lbndb = astMalloc( sizeof( double )*(size_t) nbase ); + ubndb = astMalloc( sizeof( double )*(size_t) nbase ); + astRegBaseBox2( this, lbndb, ubndb ); + +/* Get the simplified base to current Mapping. */ + smap = astRegMapping( this ); + +/* Check pointers can be used safely. */ + if( smap ) { + +/* If the simplified Mapping is a UnitMap, just copy the base box bounds + to the returned arrays */ + if( astIsAUnitMap( smap ) ) { + for( i = 0; i < ncur; i++ ) { + lbnd[ i ] = lbndb[ i ]; + ubnd[ i ] = ubndb[ i ]; + } + +/* Otherwise, use astMapBox to find the corresponding current Frame + limits. */ + } else { + for( i = 0; i < ncur; i++ ) { + astMapBox( smap, lbndb, ubndb, 1, i, lbnd + i, ubnd + i, + NULL, NULL ); + } + } + } + +/* Release resources. */ + smap = astAnnul( smap ); + lbndb = astFree( lbndb ); + ubndb = astFree( ubndb ); +} + +static void GetRegionMesh( AstRegion *this, int surface, int maxpoint, + int maxcoord, int *npoint, double *points, + int *status ){ +/* +*++ +* Name: +c astGetRegionMesh +f AST_GETREGIONMESH + +* Purpose: +* Return a mesh of points covering the surface or volume of a Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c void astGetRegionMesh( AstRegion *this, int surface, int maxpoint, +c int maxcoord, int *npoint, double *points ) +f CALL AST_GETREGIONMESH( THIS, SURFACE, MAXPOINT, MAXCOORD, NPOINT, +f POINTS, STATUS ) + +* Class Membership: +* Region method. + +* Description: +c This function +f This routine +* returns the axis values at a mesh of points either covering the +* surface (i.e. boundary) of the supplied Region, or filling the +* interior (i.e. volume) of the Region. The number of points in +* the mesh is approximately equal to the MeshSize attribute. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c surface +f SURFACE = LOGICAL (Given) +c If non-zero, +f If .TRUE., +* the returned points will cover the surface or the Region. +* Otherwise, they will fill the interior of the Region. +c maxpoint +f MAXPOINT = INTEGER (Given) +* If zero, the number of points in the mesh is returned in +c "*npoint", +f NPOINT, +* but no axis values are returned and all other parameters are ignored. +* If not zero, the supplied value should be the length of the +c second dimension of the "points" +f first dimension of the POINTS +* array. An error is reported if the number of points in the mesh +* exceeds this number. +c maxcoord +f MAXCOORD = INTEGER (Given) +* The length of the +c first dimension of the "points" array. +f second dimension of the POINTS array. +* An error is reported if the number of axes in the supplied Region +* exceeds this number. +c npoint +f NPOINT = INTEGER (Returned) +c A pointer to an integer in which to return the +f The +* number of points in the returned mesh. +c points +f POINTS( MAXPOINT, MAXCOORD ) = DOUBLE PRECISION (Returned) +c The address of the first element in a 2-dimensional array of +c shape "[maxcoord][maxpoint]", in which to return the coordinate +c values at the mesh positions. These are stored such that the +c value of coordinate number "coord" for point number "point" is +c found in element "points[coord][point]". +f An array in which to return the coordinates values at the mesh +f positions. These are stored such that the value of coordinate +f number COORD for point number POINT is found in element +f POINTS(POINT,COORD). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - An error is reported if the Region is unbounded. +* - If the coordinate system represented by the Region has been +* changed since it was first created, the returned axis values refer +* to the new (changed) coordinate system, rather than the original +* coordinate system. Note however that if the transformation from +* original to new coordinate system is non-linear, the shape within +* the new coordinate system may be distorted, and so may not match +* that implied by the name of the Region subclass (Circle, Box, etc). + +*-- +*/ + +/* Local Variables: */ + AstPointSet *pset; /* PointSet holding mesh/grid axis values */ + double **ptr; /* Pointer to mesh/grid axes values */ + double *p; /* Pointer to next input axis value */ + double *q; /* Pointer to next output axis value */ + int j; /* Axis index */ + int nc; /* No. of axes to copy */ + +/* Initialise */ + *npoint = 0; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Report an error if the Region is unbounded. */ + if( !astGetBounded( this ) ) { + if( astOK ) astError( AST__MBBNF, "astGetRegionMesh(%s): The supplied %s" + " is unbounded so no mesh can be created to cover " + "it.", status, astGetClass( this ), astGetClass( this ) ); + } else { + +/* Get the mesh or grid as required. If only the size of the mesh or grid + is required, get it in the base Frame as there is no need to spend the + extra time transforming it into the current Frame. */ + if( maxpoint == 0 ){ + if( surface ) { + pset = astRegBaseMesh( this ); + } else { + pset = astRegBaseGrid( this ); + } + } else { + if( surface ) { + pset = astRegMesh( this ); + } else { + pset = astRegGrid( this ); + } + } + +/* Return the number of points in the mesh or grid. */ + *npoint = astGetNpoint( pset ); + +/* Do nothing more unless a non-zero array size was supplied. */ + if( *npoint > 0 && maxpoint != 0 && astOK ) { + +/* Check the supplied array is large enough. */ + if( *npoint > maxpoint ) { + astError( AST__DIMIN, "astGetRegionMesh(%s): The supplied " + "array can hold up to %d points but the %s supplied " + "has %d points on its mesh (programming error).", + status, astGetClass( this ), maxpoint, astGetClass( this ), + *npoint ); + } + +/* Get the dimensionality of the PointSet, and get a pointer to the axis + values. */ + nc = astGetNcoord( pset ); + ptr = astGetPoints( pset ); + +/* Check pointers can be used safely. */ + if ( astOK ) { + +/* Check the supplied array has room for all the axes. */ + if( nc > maxcoord ) { + astError( AST__DIMIN, "astGetRegionMesh(%s): The supplied " + "array can hold up to %d axes but the %s supplied " + "has %d axes (programming error).", status, + astGetClass( this ), maxcoord, astGetClass( this ), nc ); + +/* If all is OK, copy the current Frame axis values into the supplied array. */ + } else { + +/* Loop round the axes to be copied. */ + for( j = 0; j < nc; j++ ) { + +/* Get points to the first element of the input and output arrays. */ + p = ptr[ j ]; + q = points + j*maxpoint; + +/* Copying the axis values. */ + (void) memcpy( q, p, sizeof( double )*( *npoint ) ); + } + } + } + } + +/* Free resources. */ + pset = astAnnul( pset ); + } +} + +static void GetRegionPoints( AstRegion *this, int maxpoint, int maxcoord, + int *npoint, double *points, int *status ){ +/* +*++ +* Name: +c astGetRegionPoints +f AST_GETREGIONPOINTS + +* Purpose: +* Returns the positions that define the given Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c void astGetRegionPoints( AstRegion *this, int maxpoint, int maxcoord, +c int *npoint, double *points ) +f CALL AST_GETREGIONPOINTS( THIS, MAXPOINT, MAXCOORD, NPOINT, POINTS, +f STATUS ) + +* Class Membership: +* Region method. + +* Description: +c This function +f This routine +* returns the axis values at the points that define the supplied +* Region. The particular meaning of these points will depend on the +* type of class supplied, as listed below under "Applicability:". + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c maxpoint +f MAXPOINT = INTEGER (Given) +* If zero, the number of points needed to define the Region is +* returned in +c "*npoint", +f NPOINT, +* but no axis values are returned and all other parameters are ignored. +* If not zero, the supplied value should be the length of the +c second dimension of the "points" +f first dimension of the POINTS +* array. An error is reported if the number of points needed to define +* the Region exceeds this number. +c maxcoord +f MAXCOORD = INTEGER (Given) +* The length of the +c first dimension of the "points" array. +f second dimension of the POINTS array. +* An error is reported if the number of axes in the supplied Region +* exceeds this number. +c npoint +f NPOINT = INTEGER (Returned) +c A pointer to an integer in which to return the +f The +* number of points defining the Region. +c points +f POINTS( MAXPOINT, MAXCOORD ) = DOUBLE PRECISION (Returned) +c The address of the first element in a 2-dimensional array of +c shape "[maxcoord][maxpoint]", in which to return +c the coordinate values at the positions that define the Region. +c These are stored such that the value of coordinate number +c "coord" for point number "point" is found in element +c "points[coord][point]". +f An array in which to return the coordinates values at the +f positions that define the Region. These are stored such that the +f value of coordinate number COORD for point number POINT +f is found in element POINTS(POINT,COORD). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Region +* All Regions have this attribute. +* Box +* The first returned position is the Box centre, and the second is +* a Box corner. +* Circle +* The first returned position is the Circle centre, and the second is +* a point on the circumference. +* CmpRegion +* Returns a value of zero for +c "*npoint" +f NPOINT +* and leaves the supplied array contents unchanged. To find the +* points defining a CmpRegion, use this method on the component +* Regions, which can be accessed by invoking +c astDecompose +f AST_DECOMPOSE +* on the CmpRegion. +* Ellipse +* The first returned position is the Ellipse centre. The second is +* the end of one of the axes of the ellipse. The third is some +* other point on the circumference of the ellipse, distinct from +* the second point. +* Interval +* The first point corresponds to the lower bounds position, and +* the second point corresponds to the upper bounds position. These +* are reversed to indicate an extcluded interval rather than an +* included interval. See the Interval constructor for more +* information. +* NullRegion +* Returns a value of zero for +c "*npoint" +f NPOINT +* and leaves the supplied array contents unchanged. +* PointList +* The positions returned are those that were supplied when the +* PointList was constructed. +* Polygon +* The positions returned are the vertex positions that were supplied +* when the Polygon was constructed. +* Prism +* Returns a value of zero for +c "*npoint" +f NPOINT +* and leaves the supplied array contents unchanged. To find the +* points defining a Prism, use this method on the component +* Regions, which can be accessed by invoking +c astDecompose +f AST_DECOMPOSE +* on the CmpRegion. + +* Notes: +* - If the coordinate system represented by the Region has been +* changed since it was first created, the returned axis values refer +* to the new (changed) coordinate system, rather than the original +* coordinate system. Note however that if the transformation from +* original to new coordinate system is non-linear, the shape within +* the new coordinate system may be distorted, and so may not match +* that implied by the name of the Region subclass (Circle, Box, etc). + +*-- +*/ + +/* Local Variables: */ + AstPointSet *pset; /* PointSet holding PointList axis values */ + double **ptr; /* Pointer to axes values in the PointList */ + double *p; /* Pointer to next input axis value */ + double *q; /* Pointer to next output axis value */ + int j; /* Axis index */ + int nc; /* No. of axes to copy */ + +/* Initialise */ + *npoint = 0; + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Return the number of points used to define the Region, if any. */ + *npoint = this->points ? astGetNpoint( this->points ) : 0; + +/* Do nothing more unless a non-zero array size was supplied. */ + if( *npoint > 0 && maxpoint != 0 ) { + +/* Transform the base Frame axis values into the current Frame. */ + pset = astTransform( this->frameset, this->points, 1, NULL ); + +/* Get the dimensionality of this PointList, and get a pointer to the axis + values. */ + nc = astGetNcoord( pset ); + ptr = astGetPoints( pset ); + +/* Check pointers can be used safely. */ + if ( astOK ) { + +/* Check the supplied array has room for all the axis values. */ + if( nc > maxcoord ) { + astError( AST__DIMIN, "astGetRegionPoints(%s): The supplied " + "array can hold up to %d axes but the %s supplied " + "has %d axes (programming error).", status, + astGetClass( this ), maxcoord, astGetClass( this ), nc ); + + } else if( *npoint > maxpoint ) { + astError( AST__DIMIN, "astGetRegionPoints(%s): The supplied " + "array can hold up to %d points but the %s supplied " + "requires %d points to describe it (programming " + "error).", status, astGetClass( this ), maxpoint, + astGetClass( this ), *npoint ); + +/* If all is OK, copy the transformed axis values into the supplied array. */ + } else { + +/* Loop round the axes to be copied. */ + for( j = 0; j < nc; j++ ) { + +/* Get points to the first element of the input and output arrays. */ + p = ptr[ j ]; + q = points + j*maxpoint; + +/* Copying the axis values. */ + (void) memcpy( q, p, sizeof( double )*( *npoint ) ); + } + } + } + +/* Free resources. */ + pset = astAnnul( pset ); + + } +} + +static void RegOverlay( AstRegion *this, AstRegion *that, int unc, int *status ){ +/* +*+ +* Name: +* astRegOverlay + +* Purpose: +* Copy properties from one Region to another. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astRegOverlay( AstRegion *this, AstRegion *that, int unc ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function copies selected properties from "that" to "this". +* It is intended to be called by sub-classes which need to create a +* similar copy of an existing Region. For instance, subclass +* implementations of the Simplify method will usually use this +* function to ensure that the simplified Region loooks like the original +* Region. + +* Parameters: +* this +* Pointer to the new Region. +* that +* Pointer to the old Region. +* unc +* If non-zero, any uncertainty in "this" is cleared if "that" has +* no uncertainty. If zero, any uncertainty in "this" is left +* unchanged. +*- +*/ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Copy the required attribute values. */ + this->negated = that->negated; + this->closed = that->closed; + this->regionfs = that->regionfs; + this->adaptive = that->adaptive; + +/* Clear things that depend on the number of axes. */ + if( astGetNaxes( this ) == astGetNaxes( that ) ) { + if( astTestMeshSize( that ) ) astSetMeshSize( this, astGetMeshSize( that ) ); + if( astTestFillFactor( that ) ) astSetFillFactor( this, astGetFillFactor( that ) ); + } else { + astClearMeshSize( this ); + astClearFillFactor( this ); + } + +/* If required, clear uncertainty in "this" if "that" has no uncertainty. */ + if( unc && !astTestUnc( that ) ) astClearUnc( this ); + +} + +static void RegSetAttrib( AstRegion *this, const char *asetting, + char **base_setting, int *status ) { +/* +*+ +* Name: +* astRegSetAttrib + +* Purpose: +* Set an attribute value for a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astRegSetAttrib( AstRegion *this, const char *asetting, +* char **base_setting ) + +* Class Membership: +* Region virtual function + +* Description: +* This function assigns an attribute value to both the base and +* current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* asetting +* Pointer to a null terminated attribute setting string. The supplied +* string will be interpreted using the public interpretation +* implemented by astSetAttrib. This can be different to the +* interpretation of the protected accessor functions. For instance, +* the public interpretation of an unqualified floating point value for +* the Epoch attribute is to interpet the value as a gregorian year, +* but the protected interpretation is to interpret the value as an +* MJD. +* base_setting +* Address of a location at which to return a pointer to the null +* terminated attribute setting string which was applied to the +* base Frame of the encapsulated FrameSet. This may differ from +* the supplied setting if the supplied setting contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +*- +*/ + +/* Local Variables: */ + AstFrame *frm; + AstMapping *junkmap; + AstMapping *map; + AstRegion *unc; + char *setting; + char *bsetting; + char buf1[ 100 ]; + int *outs; + int axis; + int baxis; + int i; + int len; + int nc; + int rep; + int value; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Produce a lower case version of the setting string */ + nc = strlen( asetting ); + setting = astMalloc( nc + 1 ); + for( i = 0; i < nc; i++ ) setting[ i ] = tolower( asetting[ i ] ); + setting[ nc ] = 0; + +/* Apply the setting to the current Frame in the encapsulated FrameSet. + Use the protected astSetAttrib method which does not cause the Frame + to be remapped within the FrameSet. */ + frm = astGetFrame( this->frameset, AST__CURRENT ); + astSetAttrib( frm, setting ); + frm = astAnnul( frm ); + +/* Indicate that we should use the supplied setting with the base Frame. */ + bsetting = NULL; + +/* If the attribute name contains an axis number, we need to create a new + attribute setting which refers to the corresponding base Frame axis + (since the base<->current Mapping may permute the axes). First parse the + supplied attribute setting to locate any axis index. */ + len = strlen( setting ); + if( nc = 0, ( 2 == astSscanf( setting, "%[^(](%d)= %n%*s %n", buf1, &axis, + &value, &nc ) ) && ( nc >= len ) ) { + +/* If found, convert the axis index from one-based to zero-based. */ + axis--; + +/* See if the specified current Frame axis is connected to one and only + one base Frame axis. If so, get the index of the base Frame axis. */ + map = astGetMapping( this->frameset, AST__CURRENT, AST__BASE ); + outs = astMapSplit( map, 1, &axis, &junkmap ); + if( junkmap && astGetNout( junkmap ) == 1 ) { + baxis = outs[ 0 ]; + +/* If the base Frame axis index is different to the current Frame axis + index, create a new setting string using the base Frame axis index. */ + if( baxis != axis ) { + bsetting = astMalloc( strlen( setting ) + 10 ); + if( bsetting ) { + sprintf( bsetting, "%s(%d)=%s", buf1, baxis + 1, setting + value ); + } + } + +/* If there is no one base Frame axis which corresponds to the supplied + current Frame axis, report an error. */ + } else if( astOK ) { + astError( AST__INTER, "astRegSetAttrib(%s): Unable to apply " + "attribute setting \"%s\" to the base Frame in the %s", status, + astGetClass( this ), setting, astGetClass( this ) ); + astError( AST__INTER, "There is no base Frame axis corresponding " + "to current Frame axis %d\n", status, axis + 1 ); + } + +/* Free resources */ + outs = astFree( outs ); + if( junkmap ) junkmap = astAnnul( junkmap ); + map = astAnnul( map ); + } + +/* Apply the appropriate attribute setting to the base Frame. This time + ensure that any error caused by the attribute setting is annulled. + Also apply it to any uncertainty Region (the current Frame of the + uncertainty Region is assumed to be equivalent to the base Frame of the + parent Region). */ + frm = astGetFrame( this->frameset, AST__BASE ); + if( frm ) { + rep = astReporting( 0 ); + astSetAttrib( frm, bsetting ? bsetting : setting ); + if( astTestUnc( this ) ) { + unc = astGetUncFrm( this, AST__BASE ); + astRegSetAttrib( unc, bsetting ? bsetting : setting, NULL ); + unc = astAnnul( unc ); + } + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + } + frm = astAnnul( frm ); + +/* If required return the modified base Frame setting. Otherwise, free it. */ + if( base_setting ) { + if( bsetting ) { + *base_setting = bsetting; + } else { + *base_setting = astStore( NULL, setting, strlen( setting ) + 1 ); + } + } else { + bsetting = astFree( bsetting ); + } + +/* Since the base Frame has been changed, any cached information calculated + on the basis of the base Frame properties may no longer be up to date. */ + astResetCache( this ); + +/* Free resources. */ + setting = astFree( setting ); + +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* Region method (over-rides the astRemoveRegions method inherited +* from the Frame class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a CmpMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel CmpMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the Region class just returns the +* equivalent Frame. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* The Region class just returns a pointer to a deep copy of the Region's + equivalent Frame. */ + return astGetRegionFrame( (AstRegion *)this_mapping ); +} + +static void ReportPoints( AstMapping *this_mapping, int forward, + AstPointSet *in_points, AstPointSet *out_points, int *status ) { +/* +* Name: +* ReportPoints + +* Purpose: +* Report the effect of transforming a set of points using a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void ReportPoints( AstMapping *this, int forward, +* AstPointSet *in_points, AstPointSet *out_points, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astReportPoints +* method inherited from the Frame class). + +* Description: +* This function reports the coordinates of a set of points before +* and after being transformed by a Region, by writing them to +* standard output. + +* Parameters: +* this +* Pointer to the Region. +* forward +* A non-zero value indicates that the Region's forward +* coordinate transformation has been applied, while a zero +* value indicates the inverse transformation. +* in_points +* Pointer to a PointSet which is associated with the +* coordinates of a set of points before the Region was +* applied. +* out_points +* Pointer to a PointSet which is associated with the +* coordinates of the same set of points after the Region has +* been applied. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_mapping; + +/* Obtain a pointer to the Region's current Frame and invoke its + astReportPoints method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astReportPoints( (AstMapping *) fr, forward, in_points, out_points ); + fr = astAnnul( fr ); + +} + +static void ResetCache( AstRegion *this, int *status ){ +/* +*+ +* Name: +* astResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astResetCache( AstRegion *this ) + +* Class Membership: +* Region virtual function + +* Description: +* This function clears cached information from the supplied Region +* structure. + +* Parameters: +* this +* Pointer to the Region. +*- +*/ + if( this ) { + if( this->basemesh ) this->basemesh = astAnnul( this->basemesh ); + if( this->basegrid ) this->basegrid = astAnnul( this->basegrid ); + if( this->negation ) this->negation = astAnnul( this->negation ); + } +} + + +static void Resolve( AstFrame *this_frame, const double point1[], + const double point2[], const double point3[], + double point4[], double *d1, double *d2, int *status ){ +/* +* Name: +* Resolve + +* Purpose: +* Resolve a vector into two orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void Resolve( AstFrame *this, const double point1[], +* const double point2[], const double point3[], +* double point4[], double *d1, double *d2, int *status ); + +* Class Membership: +* Region member function (over-rides the protected astResolve +* method inherited from the Frame class). + +* Description: +* This function resolves a vector into two perpendicular components. +* The vector from point 1 to point 2 is used as the basis vector. +* The vector from point 1 to point 3 is resolved into components +* parallel and perpendicular to this basis vector. The lengths of the +* two components are returned, together with the position of closest +* aproach of the basis vector to point 3. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vector to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* point3 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the vector to be +* resolved. +* point4 +* An array of double, with one element for each Frame axis +* in which the coordinates of the point of closest approach of the +* basis vector to point 3 will be returned. +* d1 +* The address of a location at which to return the distance from +* point 1 to point 4 (that is, the length of the component parallel +* to the basis vector). Positive values are in the same sense as +* movement from point 1 to point 2. +* d2 +* The address of a location at which to return the distance from +* point 4 to point 3 (that is, the length of the component +* perpendicular to the basis vector). The value is always positive. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Each vector used in this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the required +* output values are undefined. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke this + Frame's astResolve method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astResolve( fr, point1, point2, point3, point4, d1, d2 ); + fr = astAnnul( fr ); + +} + +static AstPointSet *ResolvePoints( AstFrame *this_frame, const double point1[], + const double point2[], AstPointSet *in, + AstPointSet *out, int *status ) { +/* +* Name: +* ResolvePoints + +* Purpose: +* Resolve a set of vectors into orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstPointSet *ResolvePoints( AstFrame *this, const double point1[], +* const double point2[], AstPointSet *in, +* AstPointSet *out ) + +* Class Membership: +* Region member function (over-rides the astResolvePoints method +* inherited from the Frame class). + +* Description: +* This function takes a Frame and a set of vectors encapsulated +* in a PointSet, and resolves each one into two orthogonal components, +* returning these two components in another PointSet. +* +* This is exactly the same as the public astResolve method, except +* that this method allows many vectors to be processed in a single call, +* thus reducing the computational cost of overheads of many +* individual calls to astResolve. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vectors to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* in +* Pointer to the PointSet holding the ends of the vectors to be +* resolved. +* out +* Pointer to a PointSet which will hold the length of the two +* resolved components. A NULL value may also be given, in which +* case a new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. The first axis will +* hold the lengths of the vector components parallel to the basis vector. +* These values will be signed (positive values are in the same sense as +* movement from point 1 to point 2. The second axis will hold the lengths +* of the vector components perpendicular to the basis vector. These +* values will always be positive. + +* Notes: +* - The number of coordinate values per point in the input +* PointSet must match the number of axes in the supplied Frame. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and 2 coordinate values per point. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke this + Frame's astResolve method. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astResolvePoints( fr, point1, point2, in, out ); + fr = astAnnul( fr ); + +/* Return a pointer to the output PointSet. */ + return result; + +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Region member function (extends the astSetAttrib method +* inherited from the Frame class). + +* Description: +* This function assigns an attribute value for a Region, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Region. +* setting +* Pointer to a null terminated string specifying the new +* attribute value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This protected method is intended to be invoked by the Object +* astSet method and makes additional attributes accessible to it. +*/ + +/* Local Variables: */ + AstRegion *this; /* Pointer to the Region structure */ + double dval; /* Floating point attribute value */ + int ival; /* Integer attribute value */ + int id; /* Offset of ID string */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* We first handle attributes that apply to the Region as a whole + (rather than to the encapsulated Frame). */ + +/* Negated */ +/* ------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "negated= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetNegated( this, ival ); + +/* Closed */ +/*------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "closed= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetClosed( this, ival ); + +/* FillFactor */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "fillfactor= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetFillFactor( this, dval ); + +/* MeshSize */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "meshsize= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetMeshSize( this, ival ); + +/* Adaptive */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "adaptive= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetAdaptive( this, ival ); + +/* Now do attributes inherited from parent classes. We do these here to + avoid the settings being passed on to the encapsulated FrameSet below. */ + +/* ID. */ +/* --- */ + } else if ( nc = 0, ( 0 == astSscanf( setting, "id=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetID( this, setting + id ); + +/* Ident. */ +/* ------ */ + } else if ( nc = 0, ( 0 == astSscanf( setting, "ident=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetIdent( this, setting + id ); + +/* Invert. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "invert= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetInvert( this, ival ); + +/* Report. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "report= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetReport( this, ival ); + +/* Define macros to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +#define AXISMATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "(%*d)=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + } else if ( MATCH( "class" ) || + MATCH( "nin" ) || + MATCH( "nobject" ) || + MATCH( "bounded" ) || + MATCH( "nout" ) || + MATCH( "refcount" ) || + MATCH( "tranforward" ) || + MATCH( "traninverse" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass unrecognised attributes on to the Region's encapsulated FrameSet for + further interpretation. Do not pass on FrameSet attributes since we + pretend to the outside world that the encapsulated FrameSet is actually a + Frame. */ + } else if ( !MATCH( "base" ) && + !MATCH( "current" ) && + !MATCH( "nframe" ) ) { + +/* If the Region is to adapt to coordinate system chanmges, use the public + astSet method so that the current Frame in the encapsulated FrameSet will + be re-mapped if the attribute changes require it. */ + if( astGetAdaptive( this ) ) { + astSet( this->frameset, setting, status ); + +/* If the Region is not to adapt to coordinate system chanmges, use the + astRegSetAttrib method which assigns the attribute setting to both + current and base Frames in the FrameSet without causing any remapping to + be performed. */ + } else { + astRegSetAttrib( this, setting, NULL ); + } + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void SetAxis( AstFrame *this_frame, int axis, AstAxis *newaxis, int *status ) { +/* +* Name: +* SetAxis + +* Purpose: +* Set a new Axis for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void SetAxis( AstFrame *this, int axis, AstAxis *newaxis, int *status ) + +* Class Membership: +* Region member function (over-rides the astSetAxis method +* inherited from the Frame class). + +* Description: +* This function allows a new Axis object to be associated with one +* of the axes of the current Frame in a Region, replacing the +* previous one. Each Axis object contains a description of the +* quantity represented along one of the Frame's axes, so this +* function allows this description to be exchanged for another +* one. + +* Parameters: +* this +* Pointer to the Region. +* axis +* The index (zero-based) of the axis whose associated Axis +* object is to be replaced. +* newaxis +* Pointer to the new Axis object. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index supplied. */ + (void) astValidateAxis( this, axis, 1, "astSetAxis" ); + +/* Obtain a pointer to the Region's current Frame and invoke this + Frame's astSetAxis method to assign the new Axis object. Annul the + Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astSetAxis( fr, axis, newaxis ); + fr = astAnnul( fr ); +} + +static void SetRegFS( AstRegion *this, AstFrame *frm, int *status ) { +/* +*+ +* Name: +* astSetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astSetRegFS( AstRegion *this, AstFrame *frm ) + +* Class Membership: +* Region virtual function. + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +*- +*/ + +/* Local Variables: */ + AstFrame *f1; /* Copy of supplied Frame */ + AstFrame *f2; /* Copy of supplied Frame */ + AstFrameSet *fs; /* New FrameSet */ + AstRegion *unc; /* Uncertainty Region */ + AstUnitMap *um; /* UnitMap connecting base anc current Frames */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Take a copy of the supplied Frame. */ + f1 = astCopy( frm ); + +/* Create the new FrameSet. First take another copy of the supplied Frame + so that modifications using the supplied pointer will not affect the new + FrameSet. We create two copies (rather than 1) because the base and + current Frames must be independant objects - otherwise attribute changes + done to one will also appear in the other. Then construct the FrameSet + containing the two Frame copies connected by a UnitMap. */ + f2 = astCopy( f1 ); + fs = astFrameSet( f1, "", status ); + um = astUnitMap( astGetNaxes( f1 ), "", status ); + astAddFrame( fs, AST__BASE, um, f2 ); + um = astAnnul( um ); + f2 = astAnnul( f2 ); + +/* Annul any existing FrameSet */ + if( this->frameset ) (void) astAnnul( this->frameset ); + +/* Use the new FrameSet */ + this->frameset = fs; + +/* If any uncertainty Region has a zero value for its RegionFS attribute, + it will currently contain a dummy FrameSet rather than the correct + FrameSet. The correct FrameSet has copies of the base Frame of the new + Region as both its current and base Frames, and these are connected by + a UnitMap (this is equivalent to a FrameSet containing a single Frame). */ + if( astTestUnc( this ) ) { + unc = astGetUncFrm( this, AST__BASE ); + if( unc && !astGetRegionFS( unc ) ) astSetRegFS( unc, f1 ); + unc = astAnnul( unc ); + } + +/* Free remaining resourvces */ + f1 = astAnnul( f1 ); + +} + +static void SetUnc( AstRegion *this, AstRegion *unc, int *status ){ +/* +*++ +* Name: +c astSetUnc +f AST_SETUNC + +* Purpose: +* Store uncertainty information in a Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c void astSetUnc( AstRegion *this, AstRegion *unc ) +f CALL AST_SETUNC( THIS, UNC, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* Each Region (of any class) can have an "uncertainty" which specifies +* the uncertainties associated with the boundary of the Region. This +* information is supplied in the form of a second Region. The uncertainty +* in any point on the boundary of a Region is found by shifting the +* associated "uncertainty" Region so that it is centred at the boundary +* point being considered. The area covered by the shifted uncertainty +* Region then represents the uncertainty in the boundary position. +* The uncertainty is assumed to be the same for all points. +* +* The uncertainty is usually specified when the Region is created, but +* this +c function +f routine +* allows it to be changed at any time. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region which is to be assigned a new uncertainty. +c unc +f UNC = INTEGER (Given) +* Pointer to the new uncertainty Region. This must be of a class for +* which all instances are centro-symetric (e.g. Box, Circle, Ellipse, +* etc.) or be a Prism containing centro-symetric component Regions. +* A deep copy of the supplied Region will be taken, so subsequent +* changes to the uncertainty Region using the supplied pointer will +* have no effect on the Region +c "this". +f THIS. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame from FrameSet */ + AstFrameSet *fs2; /* FrameSet from "unc" current Frame to "this" base Frame */ + AstFrameSet *fs; /* FrameSet in "this" supplied Region */ + AstMapping *map2; /* Base->current Mapping from FrameSet */ + AstMapping *map; /* Base->current Mapping from FrameSet */ + AstMapping *smap; /* Simplified base->current Mapping */ + double *cen0; /* Pointer to array holding original centre */ + double **ptr_reg; /* Pointer to axis values for Region's Pointset */ + int changed; /* Has the uncertainty been changed? */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Annul any existing uncertainty Region. */ + if( this->unc ) { + this->unc = astIsAObject( this->unc ) ? + astAnnul( this->unc ) : NULL; + changed = 1; + } else { + changed = 0; + } + +/* Check an uncertainty Region was supplied, and is of a usable class + (i.e. a class which can be re-centred). */ + cen0 = unc ? astRegCentre( unc, NULL, NULL, 0, 0 ) : NULL; + if( cen0 ) { + cen0 = astFree( cen0 ); + +/* Map it into the same Frame as that represented by the base Frame in + the supplied Region. */ + fs = this->frameset; + astInvert( fs ); + fs2 = Conv( unc->frameset, fs, status ); + astInvert( fs ); + + if( fs2 ) { + map = astGetMapping( fs2, AST__BASE, AST__CURRENT ); + frm = astGetFrame( fs2, AST__CURRENT ); + this->unc = astMapRegion( unc, map, frm ); + if( this->unc ) { + +/* Ensure the Region is bounded. We know that negating an unbounded + Region will make it bounded because we know that the Region consists of + Circles, Boxes and/or Ellipses, all of which have this property. */ + if( !astGetBounded( this->unc ) ) astNegate( this->unc ); + +/* If the base Frame in the uncertainty Region is the same as the base + Frame in the Region being dumped, then we do no need to include the + FrameSet in the dump of the uncertainty Region. Since the current + Frame in the uncertainty Region always corresponds to the base Frame of + its parent Region, we only need to check if the base->current Mapping + in the uncertainty Region's FrameSet is a UnitMap or not (after + simplification). If it is, set the RegionFS attribute of the uncertainty + Region to zero (i.e. false). This will cause the FrameSet to be omitted + from the Dump. */ + map2 = astGetMapping( this->unc->frameset, AST__BASE, AST__CURRENT ); + smap = astSimplify( map2 ); + if( astIsAUnitMap( smap ) ) astSetRegionFS( this->unc, 0 ); + +/* Re-centre the uncertainty Region at the first position in the PointSet + associated with the Region structure (if any). */ + if( this->points ) { + ptr_reg = astGetPoints( this->points ); + astRegCentre( this->unc, NULL, ptr_reg, 0, AST__CURRENT ); + } + +/* Set a flag indicating that the uncertainty in the Region has changed. */ + changed = 1; + +/* Free resources */ + map2 = astAnnul( map2 ); + smap = astAnnul( smap ); + } + frm = astAnnul( frm ); + fs2 = astAnnul( fs2 ); + map = astAnnul( map ); + +/* Report error if conversion between Frames is not possible. */ + } else if( astOK ) { + astError( AST__BADIN, "astSetUnc(%s): Bad %d dimensional " + "uncertainty Frame (%s %s) supplied.", status, astGetClass(this), + astGetNaxes(unc), astGetDomain(unc), astGetTitle(unc) ); + astError( AST__NCPIN, "Cannot convert it to the Frame of the " + "new %s.", status, astGetClass( this ) ); + } + +/* Report an error if it is not of a usable class. */ + } else if( unc && astOK ){ + astError( AST__BADIN, "astSetUnc(%s): Bad uncertainty shape " + "(%s) supplied.", status, astGetClass( this ), astGetClass(unc) ); + astError( AST__NCPIN, "The uncertainty Region must be an instance of " + "a centro-symetric subclass of Region (e.g. Box, Circle, " + "Ellipse, etc)." , status); + } + +/* If the uncertainty in the Region has changed, indicate that any cached + information in the Region is now out of date. */ + if( changed ) astResetCache( this ); + +} + +static void ShowMesh( AstRegion *this, int format, const char *ttl, int *status ){ +/* +*++ +* Name: +c astShowMesh +f AST_SHOWMESH + +* Purpose: +* Display a mesh of points covering the surface of a Region. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c void astShowMesh( AstRegion *this, int format, const char *ttl ) +f CALL AST_SHOWMESH( THIS, FORMAT, TTL, STATUS ) + +* Class Membership: +* Region method. + +* Description: +c This function +f This routine +* writes a table to standard output containing the axis values at a +* mesh of points covering the surface of the supplied Region. Each row +* of output contains a tab-separated list of axis values, one for +* each axis in the Frame encapsulated by the Region. The number of +* points in the mesh is determined by the MeshSize attribute. +* +* The table is preceded by a given title string, and followed by a +* single line containing the word "ENDMESH". + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c format +f FORMAT = LOGICAL (Given) +* A boolean value indicating if the displayed axis values should +* be formatted according to the Format attribute associated with +* the Frame's axis. Otherwise, they are displayed as simple +* floating point values. +c ttl +f TTL = CHARACTER * ( * ) (Given) +* A title to display before displaying the first position. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + AstPointSet *ps; /* PointSet holding mesh */ + char *buffer = NULL; /* Buffer for line output text */ + char buf[ 40 ]; /* Buffer for floating poitn value */ + double **ptr; /* Pointers to the mesh data */ + int i; /* Axis index */ + int j; /* Position index */ + int nax; /* Number of axes */ + int nc; /* Number of characters in buffer */ + int np; /* Number of axis values per position */ + +/* Check the inherited status. */ + if( !astOK ) return; + +/* Get a PointSet holding the mesh */ + ps = astRegMesh( this ); + if( ps ) { + +/* Get the number of axis values per position, and the number of positions. */ + nax = astGetNcoord( ps ); + np = astGetNpoint( ps ); + +/* Get a pointer to the mesh data, and check it can be used. */ + ptr = astGetPoints( ps ); + if( ptr ) { + +/* Display the title. */ + if( ttl ) printf( "\n%s\n\n", ttl ); + +/* Loop round all positions. */ + for( j = 0; j < np; j++ ) { + +/* Reset the current buffer length to zero. */ + nc = 0; + +/* Loop round all axes */ + for( i = 0; i < nax; i++ ){ + +/* If the axis value is bad, append " in the end of the output buffer. */ + if( ptr[ i ][ j ] == AST__BAD ){ + buffer = astAppendString( buffer, &nc, "" ); + +/* Otherwise, if required, append the formatted value to the end of the + buffer. */ + } else if( format ){ + buffer = astAppendString( buffer, &nc, + astFormat( this, i, ptr[ i ][ j ] ) ); + +/* Otherwise, append the floating point value to the end of the buffer. */ + } else { + sprintf( buf, "%g", ptr[ i ][ j ] ); + buffer = astAppendString( buffer, &nc, buf ); + } +/* Add a separating tab to the end of the buffer. */ + buffer = astAppendString( buffer, &nc, "\t" ); + } + +/* Display the line buffer. */ + printf( "%s\n", buffer ); + } + } + +/* Print out a marker for th eend of the list. */ + printf( "ENDMESH\n\n" ); + +/* Release resources. */ + ps = astAnnul( ps ); + buffer = astFree( buffer ); + } +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify the Mapping represented by a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Region method (over-rides the astSimplify method inherited +* from the Frame class). + +* Description: +* This function simplifies the encapsulated FrameSet and any +* uncertainty Region in the supplied Region. This is different to +* the Simplify method in the parent Frame class which always returns +* a UnitMap. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the simplified Region. A cloned pointer to the +* supplied Region will be returned if no simplication could be +* performed. + +* Notes: +* - This implementation just simplifies the encapsulated FrameSet +* and uncertainty Region. Sub-classes should usually provide their own +* implementation which invokes this implemetation, and then continues to +* check for further simplifications (such as fitting a new region to the +* current Frame). +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *bfrm; /* Pointer to "this" baseFrame */ + AstFrameSet *fs; /* Pointer to encapsulated FrameSet */ + AstMapping *map; /* Base->current Mapping for "this" */ + AstMapping *result; /* Result pointer to return */ + AstPointSet *pset1; /* Base Frame centre position */ + AstPointSet *pset2; /* Current Frame centre position */ + AstRegion *new; /* Pointer to simplified Region */ + AstRegion *sunc; /* Simplified uncertainty Region */ + AstRegion *this; /* Pointer to original Region structure */ + AstRegion *unc; /* Original uncertainty Region */ + double **ptr1; /* Pointer to axis values in "pset1" */ + double *cen; /* Original centre of uncertainty Region */ + double *lbnd; /* Lower bounds of "this" bounding box */ + double *orig_cen; /* Original centre for uncertainty Region */ + double *s1_lbnd; /* Lower bounds of "unc" when centred at lbnd */ + double *s1_ubnd; /* Upper bounds of "unc" when centred at lbnd */ + double *s2_lbnd; /* Lower bounds of "unc" when centred at ubnd */ + double *s2_ubnd; /* Upper bounds of "unc" when centred at ubnd */ + double *ubnd; /* Upper bounds of "this" bounding box */ + double delta; /* Half width of test box */ + double w1; /* Width of "s1" bounding box */ + double w2; /* Width of "s2" bounding box */ + int ic; /* Axis index */ + int naxb; /* No. of base Frame axes in "this" */ + int nin; /* Number of base Frame axes in "this" */ + int nout; /* Number of current Frame axes in "this" */ + int ok; /* Can we use the simplified uncertainty? */ + int simpler; /* Has some simplication taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_mapping; + +/* Take a deep copy of the supplied Region. This is so that the returned + pointer will have a diferent value to the supplied pointer if any + simplication takes place. */ + new = astCopy( this ); + +/* Simplify the encapsulated FrameSet, and note if any simplification took + place. */ + fs = astSimplify( new->frameset ); + simpler = ( fs != new->frameset ); + +/* If so, annull the existing FrameSet and use the simpler FrameSet. */ + if( simpler ) { + (void) astAnnul( new->frameset ); + new->frameset = astClone( fs ); + } + fs = astAnnul( fs ); + +/* If the Region has default uncertainty, we simplify the uncertainty + Region simply by deleting it. It will be regenerated when needed, + using the simplified Region. */ + if( new->defunc ) new->defunc = astAnnul( new->defunc ); + +/* If the Region's uncertainty was supplied explicitly, try simplifying + the unncertainty Region. */ + if( astTestUnc( new ) ){ + +/* Obtain the Region's uncertainty. */ + unc = astGetUncFrm( new, AST__BASE ); + +/* Get the base->current Mapping from "this". */ + map = astGetMapping( this->frameset, AST__BASE, AST__CURRENT ); + +/* If it has different numbers of inputs and outputs (e.g. a PermMap used + to take a slice through a Region), we need to ensure that the + uncertainty Region is centred on the slice. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + if( nin != nout ) { + +/* Get the current centre of the uncertainty Region in its current Frame + (the same as the base Frame of "this"). */ + cen = astRegCentre( unc, NULL, NULL, 0, AST__CURRENT ); + +/* Store it in a PointSet so it can be transformed. */ + pset1 = astPointSet( 1, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + if( astOK ) for( ic = 0; ic < nin; ic++ ) ptr1[ ic ][ 0 ] = cen[ ic ]; + +/* Transform into the curent Frame of "this", and then back into the base + Frame. */ + pset2 = astTransform( map, pset1, 1, NULL ); + (void) astTransform( map, pset2, 0, pset1 ); + +/* Re-centre the uncertainty Region at this position. */ + astRegCentre( unc, NULL, ptr1, 0, AST__CURRENT ); + +/* Free resources. */ + cen = astFree( cen ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + } + +/* Free resources. */ + map = astAnnul( map ); + +/* Try simplifying the uncertainty. Only proceed if the uncertainty can + be simplified. */ + sunc = astSimplify( unc ); + if( sunc != unc ) { + +/* If the uncertainty can be simplified it means that the base->current + Mapping in the uncertainty Region is sufficiently linear to allow the + uncertainty shape to retain its form when transformed from the base to + the current Frane. But this has only been tested at the current centre + position in the uncertainty Region. The uncertainty Region should + describe the whole of "this" Region, and so we need to check that the + simplified uncertainty does not change as we move it around within "this" + Region. To do this, we re-centre the uncertainty region at opposite + corners of a large test box, and then we find the bounding box of the + re-centred uncertainty Region. If this uncertainty bounding box changes + from corner to corner of the test box, then we do not simplify the + uncertainty Region. If "this" is bounded, we use the bounding box of + "this" as the test box. Otherwise we use a box 100 times the size of the + uncertainty Region. */ + +/* Note the original base Frame centre of the simplified uncertainty Region. */ + orig_cen = astRegCentre( sunc, NULL, NULL, 0, AST__BASE ); + +/* Allocate memory to hold the bounds of the test box. */ + naxb = astGetNin( this->frameset ); + lbnd = astMalloc( sizeof( double )*(size_t)naxb ); + ubnd = astMalloc( sizeof( double )*(size_t)naxb ); + +/* If possible, get the base Frame bounding box of "this" and use it as + the test box. */ + if( astGetBounded( this ) ) { + astRegBaseBox( this, lbnd, ubnd ); + +/* Otherwise, store the bounds of a box which is 100 times the size of + the uncertainty region, centred on the current centre of the uncertainty + region (we know all uncertainty regions are bounded). */ + } else { + astGetRegionBounds( sunc, lbnd, ubnd ); + for( ic = 0; ic < naxb; ic++ ) { + delta = 0.5*fabs( ubnd[ ic ] - lbnd[ ic ] ); + lbnd[ ic ] = orig_cen[ ic ] - delta; + ubnd[ ic ] = orig_cen[ ic ] + delta; + } + } + +/* Re-centre it at the lower bounds of the test box. This is in the base Frame + of "this" which is the same as the current Frame of "sunc". */ + astRegCentre( sunc, lbnd, NULL, 0, AST__CURRENT ); + +/* Get the bounding box of the re-centred uncertainty Region, within its + current Frame, which is the same as the base Frame of "this". */ + s1_lbnd = astMalloc( sizeof( double )*(size_t)naxb ); + s1_ubnd = astMalloc( sizeof( double )*(size_t)naxb ); + astGetRegionBounds( sunc, s1_lbnd, s1_ubnd ); + +/* Now re-centre the uncertainty Region at the upper bounds of the test + box. */ + astRegCentre( sunc, ubnd, NULL, 0, AST__CURRENT ); + +/* Get the bounding box of the re-centred uncertainty Region. */ + s2_lbnd = astMalloc( sizeof( double )*(size_t)naxb ); + s2_ubnd = astMalloc( sizeof( double )*(size_t)naxb ); + astGetRegionBounds( sunc, s2_lbnd, s2_ubnd ); + +/* Get a pointer to the base Frame of "this". */ + bfrm = astGetFrame( this->frameset, AST__BASE ); + +/* The "ok" flag is initialised to indicate that the simplified uncertainty + Region should not be used. */ + ok = 0; + +/* Check pointers can be referenced safely */ + if( astOK ) { + +/* Now indicate that the simplified uncertainty Region should be used. */ + ok = 1; + +/* Loop round all axes of the base Frame of "this". */ + for( ic = 0; ic < naxb; ic++ ) { + +/* Get the width of the two bounding boxes on this axis. */ + w1 = s1_ubnd[ ic ] - s1_lbnd[ ic ]; + w2 = s2_ubnd[ ic ] - s2_lbnd[ ic ]; + +/* If these differ by more than 0.1% then we determine that the simplified + uncertainty Region varies in size across the bounding box of "this", and + so we do not use the simplified uncertainty Region. The figure of 0.1% + is arbitrary. */ + if( fabs( w1 - w2 ) > 0.005*( fabs( w1 ) + fabs( w2 ) ) ) { + ok = 0; + break; + } + } + } + +/* Reinstate the original base Frame centre of the simplified uncertainty Region. */ + astRegCentre( sunc, orig_cen, NULL, 0, AST__BASE ); + +/* Free resources. */ + orig_cen = astFree( orig_cen ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + s1_lbnd = astFree( s1_lbnd ); + s1_ubnd = astFree( s1_ubnd ); + s2_lbnd = astFree( s2_lbnd ); + s2_ubnd = astFree( s2_ubnd ); + bfrm = astAnnul( bfrm ); + +/* If we can use the simplified uncertainty Region, indicate that we have + performed some simplification, and store the new uncertainty Region. */ + if( ok ) { + simpler = 1; + astSetUnc( new, sunc ); + } + } + +/* Free resources */ + unc = astAnnul( unc ); + sunc = astAnnul( sunc ); + } + +/* If any simplification could be performed, return the new Region. + Otherwise, return a clone of the supplied pointer. */ + if( simpler ){ + result = (AstMapping *) new; + } else { + new = astAnnul( new ); + result = astClone( this ); + } + +/* If an error occurred, annul the returned pointer. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static int SubFrame( AstFrame *this_frame, AstFrame *template, + int result_naxes, + const int *target_axes, const int *template_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a Region and convert to the new coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int SubFrame( AstFrame *target, AstFrame *template, int result_naxes, +* const int *target_axes, const int *template_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astSubFrame +* method inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the +* axes from the current Frame of a "target" Region and creates a +* new Frame with copies of the selected axes assembled in the +* requested order. It then optionally overlays the attributes of a +* "template" Frame on to the result. It returns both the resulting +* Frame and a Mapping that describes how to convert between the +* coordinate systems described by the current Frame of the target +* Region and the result Frame. If necessary, this Mapping takes +* account of any differences in the Frames' attributes due to the +* influence of the template. + +* Parameters: +* target +* Pointer to the target Region, from whose current Frame the +* axes are to be selected. +* template +* Pointer to the template Frame, from which new attributes for +* the result Frame are to be obtained. Optionally, this may be +* NULL, in which case no overlaying of template attributes will +* be performed. +* result_naxes +* Number of axes to be selected from the target Region. This +* number may be greater than or less than the number of axes in +* the Region's current Frame (or equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving +* a list of the (zero-based) axis indices of the axes to be +* selected from the current Frame of the target Region. The +* order in which these are given determines the order in which +* the axes appear in the result Frame. If any of the values in +* this array is set to -1, the corresponding result axis will +* not be derived from the target Region, but will be assigned +* default attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This +* should contain a list of the template axes (given as +* zero-based axis indices) with which the axes of the result +* Frame are to be associated. This array determines which axes +* are used when overlaying axis-dependent attributes of the +* template on to the result. If any element of this array is +* set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not +* used and a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned +* Mapping. The forward transformation of this Mapping will +* describe how to convert coordinates from the coordinate +* system described by the current Frame of the target Region +* to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is +* possible between the current Frame of the target Region and +* the result Frame. Otherwise zero is returned and *map and +* *result are returned as NULL (but this will not in itself result +* in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not +* always be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to Region's current Frame */ + int match; /* Result to be returned */ + +/* Initialise. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Invoke the parent astSubFrame method on the Frame represented by the + region. */ + fr = astGetFrame( ((AstRegion *) this_frame)->frameset, AST__CURRENT ); + match = astSubFrame( fr, template, result_naxes, target_axes, template_axes, + map, result ); + fr = astAnnul( fr ); + +/* Return the result. */ + return match; +} + +static AstSystemType SystemCode( AstFrame *this_frame, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astSystemCode +* method inherited from the Frame class). + +* Description: +* This function converts a string used for the external description of +* a coordinate system into a Frame coordinate system type code (System +* attribute value). It is the inverse of the astSystemString function. + +* Parameters: +* this +* Pointer to the Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the coordinate system +* description was not recognised. This does not produce an error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astSystemCode method for this Frame. Annul the Frame pointer afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astSystemCode( fr, system ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BADSYSTEM; + +/* Return the result. */ + return result; +} + +static const char *SystemString( AstFrame *this_frame, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astSystemString +* method inherited from the Frame class). + +* Description: +* This function converts a Frame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astSystemString method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astSystemString( fr, system ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result pointer. */ + return result; + +} + +static int RegTrace( AstRegion *this, int n, double *dist, double **ptr, int *status ){ +/* +*+ +* Name: +* astRegTrace + +* Purpose: +* Return requested positions on the boundary of a 2D Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* int astRegTrace( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* Region virtual function + +* Description: +* This function returns positions on the boundary of the supplied +* Region, if possible. The required positions are indicated by a +* supplied list of scalar parameter values in the range zero to one. +* Zero corresponds to some arbitrary starting point on the boundary, +* and one corresponds to the end (which for a closed region will be +* the same place as the start). + +* Parameters: +* this +* Pointer to the Region. +* n +* The number of positions to return. If this is zero, the function +* returns without action (but the returned function value still +* indicates if the method is supported or not). +* dist +* Pointer to an array of "n" scalar parameter values in the range +* 0 to 1.0. +* ptr +* A pointer to an array of pointers. The number of elements in +* this array should equal tthe number of axes in the Frame spanned +* by the Region. Each element of the array should be a pointer to +* an array of "n" doubles, in which to return the "n" values for +* the corresponding axis. The contents of the arrays are unchanged +* if the supplied Region belongs to a class that does not +* implement this method. + +* Returned Value: +* Non-zero if the astRegTrace method is implemented by the class +* of Region supplied, and zero if not. + +*- +*/ + +/* Concrete sub-classes of Region must over-ride this method. */ + return 0; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Region member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Region's attributes. + +* Parameters: +* this +* Pointer to the Region. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstRegion *this; /* Pointer to the Region structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* We first handle attributes that apply to the Region as a whole + (rather than to the encapsulated FrameSet). */ + +/* Negated. */ +/* -------- */ + if ( !strcmp( attrib, "negated" ) ) { + result = astTestNegated( this ); + +/* Closed. */ +/* ------- */ + } else if ( !strcmp( attrib, "closed" ) ) { + result = astTestClosed( this ); + +/* FillFactor */ +/* ---------- */ + } else if ( !strcmp( attrib, "fillfactor" ) ) { + result = astTestFillFactor( this ); + +/* MeshSize */ +/* -------- */ + } else if ( !strcmp( attrib, "meshsize" ) ) { + result = astTestMeshSize( this ); + +/* Adaptive */ +/* -------- */ + } else if ( !strcmp( attrib, "adaptive" ) ) { + result = astTestAdaptive( this ); + +/* Now do attributes inherited from parent classes. This is so that the + attribute test will not be passed on to the encpasulated FrameSet below. */ + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + result = astTestID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astTestIdent( this ); + +/* Invert. */ +/* ------- */ + } else if ( !strcmp( attrib, "invert" ) ) { + result = astTestInvert( this ); + +/* Report. */ +/* ------- */ + } else if ( !strcmp( attrib, "report" ) ) { + result = astTestReport( this ); + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + } else if ( !strcmp( attrib, "class" ) || + !strcmp( attrib, "nin" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "bounded" ) || + !strcmp( attrib, "nout" ) || + !strcmp( attrib, "refcount" ) || + !strcmp( attrib, "tranforward" ) || + !strcmp( attrib, "traninverse" ) ) { + result = 0; + +/* Pass unrecognised attributes on to the Region's encapsulated FrameSet for + further interpretation. Do not pass on FrameSet attributes since we + pretend to the outside world that the encapsulated FrameSet is actually a + Frame. */ + } else if ( strcmp( attrib, "base" ) && + strcmp( attrib, "current" ) && + strcmp( attrib, "nframe" ) ) { + result = astTestAttrib( this->frameset, attrib ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +double *astRegTranPoint_( AstRegion *this, double *in, int np, int forward, int *status ){ +/* +*+ +* Name: +* astRegTranPoint + +* Purpose: +* Transform points between the base and current Frames in a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* double *astRegTranPoint( AstRegion *this, double *in, int np, int forward ) + +* Class Membership: +* Region member function + +* Description: +* This function transforms one or more points between the base and +* current Frames of the FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* The Region pointer. +* in +* Pointer to a 1-d array holding the axis values to be transformed. +* If "forward" is non-zero, the number of axis values supplied for +* each position should equal the number of axes in the base Frame +* of the FrameSet encapsulated by "this". If "forward" is zero, the +* number of axis values supplied for each position should equal the +* number of axes in the current Frame of the FrameSet encapsulated by +* "this". All the axis values for a position should be in adjacent +* elements of the array. +* np +* The number of points supplied in "in". +* forward +* If non-zero, the supplied points are assumed to refer to the base +* Frame of the encapsulated FrameSet, and they are transformed to the +* current Frame. If zero, the supplied points are assumed to refer to +* the current Frame of the encapsulated FrameSet, and they are +* transformed to the base Frame. + +* Returned Value: +* Pointer to a new dynamically allocated array holding the +* transformed axis values. If "forward" is non-zero, the number of axis +* values for each position will be equal the number of axes in the +* current Frame of the FrameSet encapsulated by "this". If "forward" is +* zero, the number of axis values for each position will be equal to the +* number of axes in the base Frame of the FrameSet encapsulated by "this". +* All the axis values for a position will be in adjacent elements of the +* array. The array should be freed using astFree when no longer needed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + AstMapping *map; + AstPointSet *pset_in; + AstPointSet *pset_out; + double **ptr_in; + double **ptr_out; + double *p; + double *result; + int ic; + int ip; + int naxin; + int naxout; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the required Mapping. */ + if( forward ) { + map = astGetMapping( this->frameset, AST__BASE, AST__CURRENT ); + } else { + map = astGetMapping( this->frameset, AST__CURRENT, AST__BASE ); + } + +/* Get the number of axis values per input and per output point. */ + naxin = astGetNin( map ); + naxout = astGetNout( map ); + +/* Create a pointSet holding the supplied axis values. */ + pset_in = astPointSet( np, naxin, "", status ); + +/* Get pointers to the memory used to store axis values within this + PointSet. */ + ptr_in = astGetPoints( pset_in ); + +/* Allocate the output array. */ + result = astMalloc( sizeof( double )*(size_t)( naxout*np ) ); + +/* Check the pointers can be used. */ + if( astOK ) { + +/* Store the supplied axis values in the PointSet memory. */ + p = in; + for( ip = 0; ip < np; ip++ ) { + for( ic = 0; ic < naxin; ic++ ) ptr_in[ ic ][ ip ] = *(p++); + } + +/* Transform the PointSet. */ + pset_out = astTransform( map, pset_in, 1, NULL ); + +/* Get a pointer to the memory in the transformed PointSet. */ + ptr_out = astGetPoints( pset_out ); + + if( pset_out && astStatus == AST__INTER ) { + p = in; + for( ip = 0; ip < np; ip++ ) { + for( ic = 0; ic < naxin; ic++ ) printf("%.*g\n", DBL_DIG, *(p++) ); + } + } + + if( astOK ) { + +/* Store the resulting axis values in the output array. */ + p = result; + for( ip = 0; ip < np; ip++ ) { + for( ic = 0; ic < naxout; ic++ ) *(p++) = ptr_out[ ic ][ ip ]; + } + } + +/* Free resources. */ + pset_out = astAnnul( pset_out ); + } + pset_in = astAnnul( pset_in ); + map = astAnnul( map ); + +/* Return NULL if anything went wrong. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result.*/ + return result; +} + +static AstPointSet *RegTransform( AstRegion *this, AstPointSet *in, + int forward, AstPointSet *out, AstFrame **frm, int *status ) { +/* +*+ +* Name: +* astRegTransform + +* Purpose: +* Transform a set of points using the encapsulated FrameSet. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstPointSet *astRegTransform( AstRegion *this, AstPointSet *in, +* int forward, AstPointSet *out, +* AstFrameSet **frm ) + +* Class Membership: +* Region virtual function + +* Description: +* This function takes a Region and a set of points encapsulated +* in a PointSet, and applies either the forward or inverse +* coordinate transformation represented by the encapsulated FrameSet. +* It also returned a pointer to either the current or base Frame in +* the FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* in +* Pointer to the PointSet holding the input coordinate data. If +* NULL then the "points" PointSet within the supplied Region +* ("this") is used. +* forward +* A non-zero value indicates that the forward coordinate transformation +* (from base to current) should be applied, while a zero value requests +* the inverse transformation (from current to base). +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* frm +* Location at which to return a pointer to a Frame. If "forward" +* is non-zero, the current Frame in the encapsulated FrameSet will +* be returned. Otherwise, the base Frame is returned. The returned +* pointer should be annulled when no longer needed. May be NULL if +* no pointer is needed. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. If "out" is NULL, +* the returned pointer will be a clone of "in" if the Mapping is a +* UnitMap. If "out" is not NULL, then the supplied "out" PointSet will +* be used and returned. + +* Notes: +* - An error will result if the Region supplied does not define +* the requested coordinate transformation (either forward or +* inverse). +* - The number of coordinate values per point in the input +* PointSet must match the number of input coordinates for the +* Region being applied (or number of output coordinates if the +* inverse transformation is requested). This will be equal to the +* number of axes in the Region's base Frame (or the current +* Frame for the inverse transformation). +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and coordinate values per point to +* accommodate the result (e.g. the number of Region output +* coordinates, or number of input coordinates if the inverse +* transformation is requested). Any excess space will be ignored. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstMapping *smap; /* Pointer to simplified Mapping */ + AstPointSet *result; /* Pointer value to return */ + +/* Initialise */ + if( frm ) *frm = NULL; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* If no input PointSet was provided, use the PointSet in the Region. */ + if( !in ) { + if( this->points ) { + in = this->points; + } else { + astError( AST__INTER, "astRegTransform(%s): No PointSet supplied " + "and the supplied %s has no PointSet (internal AST " + "programming error)", status, astGetClass( this ),astGetClass( this ) ); + } + } + +/* Get the simplified Mapping from base to current Frame. */ + smap = astRegMapping( this ); + +/* If it is a UnitMap, return a clone of the input PointSet unless an + explicit output PointSet has been supplied. */ + if( astIsAUnitMap( smap ) && !out ) { + result = astClone( in ); + +/* Otherwise use the Mapping to transform the supplied positions. */ + } else { + result = astTransform( smap, in, forward, out ); + } + +/* Return a pointer to the appropriate Frame. */ + if( frm ) *frm = astGetFrame( this->frameset, forward ? AST__CURRENT : AST__BASE ); + +/* Release resources. */ + smap = astAnnul( smap ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static int Unformat( AstFrame *this_frame, int axis, const char *string, + double *value, int *status ) { +/* +* Name: +* Unformat + +* Purpose: +* Read a formatted coordinate value for a Region axis. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int Unformat( AstFrame *this, int axis, const char *string, +* double *value, int *status ) + +* Class Membership: +* Region member function (over-rides the public astUnformat +* method inherited from the Frame class). + +* Description: +* This function reads a formatted coordinate value for a Region +* axis (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the Region. +* axis +* The number of the Region axis for which the coordinate +* value is to be read (axis numbering starts at zero for the +* first axis). +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will be +* returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + double coord; /* Coordinate value read */ + int nc; /* Number of characters read */ + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astUnformat" ); + +/* Obtain a pointer to the Region's current Frame and invoke the + astUnformat method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + nc = astUnformat( fr, axis, string, &coord ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the number of characters read. */ + if ( !astOK ) { + nc = 0; + +/* Otherwise, if characters were read, return the coordinate value. */ + } else if ( nc ) { + *value = coord; + } + +/* Return the number of characters read. */ + return nc; +} + +static int ValidateAxis( AstFrame *this_frame, int axis, int fwd, + const char *method, int *status ) { +/* +* Name: +* ValidateAxis + +* Purpose: +* Validate and permute a Region's axis index. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int ValidateAxis( AstFrame *this, int axis, int fwd, const char *method, +* int *status ) + +* Class Membership: +* Region member function (over-rides the protected +* astValidateAxis method inherited from the Frame class). + +* Description: +* This function checks the validity of an index (zero-based) which +* is to be used to address one of the coordinate axes of the +* current Frame in a Region. If the index is valid, it is +* permuted using the axis permutation array associated with the +* Region's current Frame and the (zero-based) permuted axis +* index is returned. This gives the index the axis had when the +* Frame was first created. If the axis index supplied is not +* valid, an error is reported and the global error status is set. + +* Parameters: +* this +* Pointer to the Region. +* axis +* The axis index (zero-based) to be checked. To be valid, it +* must lie between zero and (naxes-1) inclusive, where "naxes" +* is the number of coordinate axes associated with the +* Region's current Frame. +* fwd +* If non-zero, the suppplied axis index is assumed to be an +* "external" axis index, and the corresponding "internal" axis index +* is returned as the function value. Otherwise, the suppplied axis +* index is assumed to be an "internal" axis index, and the +* corresponding "external" axis index is returned as the function +* value. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The permuted axis index - either "internal" or "external" as +* specified by "fwd". + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + int naxes; /* Number of Region axes */ + int result; /* Permuted axis index */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_frame; + +/* Determine the number of Region axes. */ + naxes = astGetNaxes( this ); + if ( astOK ) { + +/* If the Region has no axes, report an error (convert to 1-based + axis numbering for the benefit of the public interface). */ + if ( naxes == 0 ) { + astError( AST__AXIIN, "%s(%s): Invalid attempt to use an axis index " + "(%d) for a %s which has no axes.", status, method, + astGetClass( this ), axis + 1, astGetClass( this ) ); + +/* Otherwise, check the axis index for validity and report an error if + it is not valid (again, convert to 1-based axis numbering). */ + } else if ( ( axis < 0 ) || ( axis >= naxes ) ) { + astError( AST__AXIIN, "%s(%s): Axis index (%d) invalid - it should " + "be in the range 1 to %d.", status, method, astGetClass( this ), + axis + 1, naxes ); + +/* If the axis index was valid, obtain a pointer to the Region's + current Frame and invoke this Frame's astValidateAxis method to + obtain the permuted axis index. Annul the Frame pointer + afterwards. */ + } else { + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astValidateAxis( fr, axis, fwd, "astValidateAxis" ); + fr = astAnnul( fr ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static void ValidateAxisSelection( AstFrame *this_frame, int naxes, + const int *axes, const char *method, int *status ) { +/* +* Name: +* ValidateAxisSelection + +* Purpose: +* Check that a set of axes selected from a Frame is valid. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void ValidateAxisSelection( AstFrame *this, int naxes, +* const int *axes, const char *method, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astValidateAxisSelection +* method inherited from the Frame class). + +* Description: +* This function checks the validity of an array of (zero-based) +* axis indices that specify a set of axes to be selected from a +* Frame. To be valid, no axis should be selected more than +* once. In assessing this, any axis indices that do not refer to +* valid Frame axes (e.g. are set to -1) are ignored. +* +* If the axis selection is valid, this function returns without further +* action. Otherwise, an error is reported and the global error status is +* set. + +* Parameters: +* this +* Pointer to the Frame. +* naxes +* The number of axes to be selected (may be zero). +* axes +* Pointer to an array of int with naxes elements that contains the +* (zero based) axis indices to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis selection. This method name is used +* solely for constructing error messages. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke this + Frame's astValidateAxisSelection method. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + astValidateAxisSelection( fr, naxes, axes, method ); + fr = astAnnul( fr ); + +} + +static int ValidateSystem( AstFrame *this_frame, AstSystemType system, const char *method, int *status ) { +/* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* Region member function (over-rides the protected astValidateSystem +* method inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST_BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + AstFrame *fr; /* Pointer to FrameSet's current Frame */ + AstRegion *this; /* Pointer to the Region structure */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the FrameSet structure. */ + this = (AstRegion *) this_frame; + +/* Obtain a pointer to the Region's encapsulated Frame and invoke the + astValidateSystem method for this Frame. Annul the Frame pointer + afterwards. */ + fr = astGetFrame( this->frameset, AST__CURRENT ); + result = astValidateSystem( this, system, method ); + fr = astAnnul( fr ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BADSYSTEM; + +/* Return the result. */ + return result; +} + +/* Region Attributes. */ +/* -------------------- */ + +/* +*att++ +* Name: +* Adaptive + +* Purpose: +* Should the area adapt to changes in the coordinate system? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* The coordinate system represented by a Region may be changed by +* assigning new values to attributes such as System, Unit, etc. +* For instance, a Region representing an area on the sky in ICRS +* coordinates may have its System attribute changed so that it +* represents (say) Galactic coordinates instead of ICRS. This +* attribute controls what happens when the coordinate system +* represented by a Region is changed in this way. +* +* If Adaptive is non-zero (the default), then area represented by the +* Region adapts to the new coordinate system. That is, the numerical +* values which define the area represented by the Region are changed +* by mapping them from the old coordinate system into the new coordinate +* system. Thus the Region continues to represent the same physical +* area. +* +* If Adaptive is zero, then area represented by the Region does not adapt +* to the new coordinate system. That is, the numerical values which +* define the area represented by the Region are left unchanged. Thus +* the physical area represented by the Region will usually change. +* +* As an example, consider a Region describe a range of wavelength from +* 2000 Angstrom to 4000 Angstrom. If the Unit attribute for the Region +* is changed from Angstrom to "nm" (nanometre), what happens depends +* on the setting of Adaptive. If Adaptive is non-zero, the Mapping +* from the old to the new coordinate system is found. In this case it +* is a simple scaling by a factor of 0.1 (since 1 Angstrom is 0.1 nm). +* This Mapping is then used to modify the numerical values within the +* Region, changing 2000 to 200 and 4000 to 400. Thus the modified +* region represents 200 nm to 400 nm, the same physical space as +* the original 2000 Angstrom to 4000 Angstrom. However, if Adaptive +* had been zero, then the numerical values would not have been changed, +* resulting in the final Region representing 2000 nm to 4000 nm. +* +* Setting Adaptive to zero can be necessary if you want correct +* inaccurate attribute settings in an existing Region. For instance, +* when creating a Region you may not know what Epoch value to use, so +* you would leave Epoch unset resulting in some default value being used. +* If at some later point in the application, the correct Epoch value +* is determined, you could assign the correct value to the Epoch +* attribute. However, you would first need to set Adaptive temporarily +* to zero, because otherwise the area represented by the Region would +* be Mapped from the spurious default Epoch to the new correct Epoch, +* which is not what is required. + +* Applicability: +* Region +* All Regions have this attribute. +*att-- +*/ + +/* This is a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of 1. */ +astMAKE_CLEAR(Region,Adaptive,adaptive,-INT_MAX) +astMAKE_GET(Region,Adaptive,int,1,( ( this->adaptive == -INT_MAX ) ? + 1 : this->adaptive )) +astMAKE_SET(Region,Adaptive,int,adaptive,( value != 0 )) +astMAKE_TEST(Region,Adaptive,( this->adaptive != -INT_MAX )) + +/* +*att++ +* Name: +* Negated + +* Purpose: +* Region negation flag. + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls whether a Region represents the "inside" or +* the "outside" of the area which was supplied when the Region was +* created. If the attribute value is zero (the default), the Region +* represents the inside of the original area. However, if it is non-zero, +* it represents the outside of the original area. The value of this +* attribute may be toggled using the +c astNegate function. +f AST_NEGATE routine. + +* Note, whether the boundary is considered to be inside the Region or +* not is controlled by the Closed attribute. Changing the value of +* the Negated attribute does not change the value of the Closed attribute. +* Thus, if Region is closed, then the boundary of the Region will be +* inside the Region, whatever the setting of the Negated attribute. + +* Applicability: +* Region +* All Regions have this attribute. +*att-- +*/ + +/* This is a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(Region,Negated,negated,(astResetCache(this),-INT_MAX)) +astMAKE_GET(Region,Negated,int,0,( ( this->negated == -INT_MAX ) ? + 0 : this->negated )) +astMAKE_SET(Region,Negated,int,negated,(astResetCache(this),( value != 0 ))) +astMAKE_TEST(Region,Negated,( this->negated != -INT_MAX )) + +/* +*att++ +* Name: +* Bounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This is a read-only attribute indicating if the Region is bounded. +* A Region is bounded if it is contained entirely within some +* finite-size bounding box. + +* Applicability: +* Region +* All Regions have this attribute. +*att-- +*/ + +/* +*att+ +* Name: +* RegionFS + +* Purpose: +* Should Region FrameSet be dumped? + +* Type: +* Protected attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute indicates whether the FrameSet encapsulated by the +* Region should be included in the dump produced by the Dump function. +* +* If set to a non-zero value (the default), the FrameSet in the Region +* will always be included in the dump as usual. If set to zero, the +* FrameSet will only be included in the dump if the Mapping from base +* to current Frame is not a UnitMap. If the base->current Mapping is +* a UnitMap, the FrameSet is omitted from the dump. If the dump is +* subsequently used to re-create the Region, the new Region will have a +* default FrameSet containing a single default Frame with the appropriate +* number of axes. +* +* This facility is indended to reduce the size of textual dumps of +* Regions in situations where the Frame to which the Region refers can +* be implied by the context in which the Region is used. This is +* often the case when a Region is encapsulated within another Region. +* In such cases the current Frame of the encapsulated Region will +* usually be equivalent to the base Frame of the parent Region +* structure, and so can be re-instated (by calling the astSetRegFS +* method) even if the FrameSet is omitted from the dump of the +* encapsulated Region. Note if the base->current Mapping in the FrameSet +* in the encapsulated Region is not a UnitMap, then we should always +* dump the FrameSet regardless of the setting of RegionFS. This is because +* the parent Region structure will not know how to convert the PointSet +* stored in the encapsulated Region into its own base Frame if the +* FrameSet is not available. + +* Applicability: +* Region +* All Regions have this attribute. +*att- +*/ + +/* This is a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of one. */ +astMAKE_CLEAR(Region,RegionFS,regionfs,-INT_MAX) +astMAKE_TEST(Region,RegionFS,( this->regionfs != -INT_MAX )) +astMAKE_SET(Region,RegionFS,int,regionfs,( value != 0 )) +astMAKE_GET(Region,RegionFS,int,1,( ( this->regionfs == -INT_MAX ) ? + 1 : this->regionfs )) + +/* +*att++ +* Name: +* FillFactor + +* Purpose: +* Fraction of the Region which is of interest. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute indicates the fraction of the Region which is of +* interest. AST does not use this attribute internally for any purpose. +* Typically, it could be used to indicate the fraction of the Region for +* which data is available. +* +* The supplied value must be in the range 0.0 to 1.0, and the default +* value is 1.0 (except as noted below). + +* Applicability: +* Region +* All Regions have this attribute. +* CmpRegion +* The default FillFactor for a CmpRegion is the FillFactor of its +* first component Region. +* Prism +* The default FillFactor for a Prism is the product of the +* FillFactors of its two component Regions. +* Stc +* The default FillFactor for an Stc is the FillFactor of its +* encapsulated Region. +*att-- +*/ + +astMAKE_CLEAR(Region,FillFactor,fillfactor,AST__BAD) +astMAKE_GET(Region,FillFactor,double,1.0,( ( this->fillfactor == AST__BAD ) ? + 1.0 : this->fillfactor )) +astMAKE_TEST(Region,FillFactor,( this->fillfactor != AST__BAD )) +astMAKE_SET(Region,FillFactor,double,fillfactor,((value<0.0||value>1.0)?( + astError(AST__ATSER,"astSetFillFactor(%s): Invalid value (%g) supplied " + "for attribute FillFactor.", status,astGetClass(this),value), + astError(AST__ATSER,"FillFactor values should be in the range 0.0 to 1.0", status), + this->fillfactor):value)) + +/* +*att++ +* Name: +* MeshSize + +* Purpose: +* Number of points used to represent the boundary of a Region. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute controls how many points are used when creating a +* mesh of points covering the boundary or volume of a Region. Such a +* mesh is returned by the +c astGetRegionMesh +f AST_GETREGIONMESH +* method. The boundary mesh is also used when testing for overlap +* between two Regions: each point in the bomdary mesh of the first +* Region is checked to see if it is inside or outside the second Region. +* Thus, the reliability of the overlap check depends on the value assigned +* to this attribute. If the value used is very low, it is possible for +* overlaps to go unnoticed. High values produce more reliable results, but +* can result in the overlap test being very slow. The default value is 200 +* for two dimensional Regions and 2000 for three or more dimensional +* Regions (this attribute is not used for 1-dimensional regions since the +* boundary of a simple 1-d Region can only ever have two points). A +* value of five is used if the supplied value is less than five. + +* Applicability: +* Region +* All Regions have this attribute. +* CmpRegion +* The default MeshSize for a CmpRegion is the MeshSize of its +* first component Region. +* Stc +* The default MeshSize for an Stc is the MeshSize of its +* encapsulated Region. +*att-- +*/ +/* If the value of MeshSize is set or cleared, annul the PointSet used to + cache a mesh of base Frame boundary points. This will force a new + PointSet to be created next time it is needed. See function RegMesh. */ +astMAKE_CLEAR(Region,MeshSize,meshsize,(astResetCache(this),-INT_MAX)) +astMAKE_SET(Region,MeshSize,int,meshsize,(astResetCache(this),( value > 5 ? value : 5 ))) +astMAKE_TEST(Region,MeshSize,( this->meshsize != -INT_MAX )) +astMAKE_GET(Region,MeshSize,int,0,( ( this->meshsize == -INT_MAX)?((astGetNaxes(this)==1)?2:((astGetNaxes(this)==2)?200:2000)): this->meshsize )) + +/* +*att++ +* Name: +* Closed + +* Purpose: +* Should the boundary be considered to be inside the region? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls whether points on the boundary of a Region +* are considered to be inside or outside the region. If the attribute +* value is non-zero (the default), points on the boundary are considered +* to be inside the region (that is, the Region is "closed"). However, +* if the attribute value is zero, points on the bounary are considered +* to be outside the region. + +* Applicability: +* Region +* All Regions have this attribute. +* PointList +* The value of the Closed attribute is ignored by PointList regions. +* If the PointList region has not been negated, then it is always +* assumed to be closed. If the PointList region has been negated, then +* it is always assumed to be open. This is required since points +* have zero volume and therefore consist entirely of boundary. +* CmpRegion +* The default Closed value for a CmpRegion is the Closed value of its +* first component Region. +* Stc +* The default Closed value for an Stc is the Closed value of its +* encapsulated Region. +*att-- +*/ +/* This is a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of 1. */ +astMAKE_CLEAR(Region,Closed,closed,(astResetCache(this),-INT_MAX)) +astMAKE_GET(Region,Closed,int,1,( ( this->closed == -INT_MAX ) ? + 1 : this->closed )) +astMAKE_SET(Region,Closed,int,closed,(astResetCache(this),( value != 0 ))) +astMAKE_TEST(Region,Closed,( this->closed != -INT_MAX )) + +/* Access to attributes of the encapsulated Frame. */ +/* ----------------------------------------------- */ +/* Use the macros defined at the start of this file to implement + private member functions that give access to the attributes of the + encapsulated Frame of a Region and its axes. These functions over-ride + the attribute access methods inherited from the Frame class. */ + +/* Clear, Get, Set and Test axis-independent Frame attributes. */ +MAKE_CLEAR(Digits) +MAKE_CLEAR(Domain) +MAKE_CLEAR(MatchEnd) +MAKE_CLEAR(MaxAxes) +MAKE_CLEAR(MinAxes) +MAKE_CLEAR(Permute) +MAKE_CLEAR(PreserveAxes) +MAKE_CLEAR(Title) + +MAKE_GET(Digits,int) +MAKE_GET(Domain,const char *) +MAKE_GET(MatchEnd,int) +MAKE_GET(MaxAxes,int) +MAKE_GET(MinAxes,int) +MAKE_GET(Permute,int) +MAKE_GET(PreserveAxes,int) +MAKE_GET(Title,const char *) +MAKE_SET(Digits,int,I) +MAKE_SET(Domain,const char *,C) +MAKE_SET(MatchEnd,int,I) +MAKE_SET(MaxAxes,int,I) +MAKE_SET(MinAxes,int,I) +MAKE_SET(Permute,int,I) +MAKE_SET(PreserveAxes,int,I) +MAKE_SET(Title,const char *,C) +MAKE_TEST(Digits) +MAKE_TEST(Domain) +MAKE_TEST(MatchEnd) +MAKE_TEST(MaxAxes) +MAKE_TEST(MinAxes) +MAKE_TEST(Permute) +MAKE_TEST(PreserveAxes) +MAKE_TEST(Title) + +MAKE_GET(ActiveUnit,int) +MAKE_SET(ActiveUnit,int,I) +MAKE_TEST(ActiveUnit) + +MAKE_GET(System,AstSystemType) +MAKE_SET_SYSTEM(System) +MAKE_TEST(System) +MAKE_CLEAR(System) + +MAKE_GET(AlignSystem,AstSystemType) +MAKE_SET_SYSTEM(AlignSystem) +MAKE_TEST(AlignSystem) +MAKE_CLEAR(AlignSystem) + +MAKE_GET(Epoch,double) +MAKE_SET(Epoch,double,D) +MAKE_TEST(Epoch) +MAKE_CLEAR(Epoch) + +MAKE_GET(ObsLon,double) +MAKE_SET(ObsLon,double,D) +MAKE_TEST(ObsLon) +MAKE_CLEAR(ObsLon) + +MAKE_GET(ObsLat,double) +MAKE_SET(ObsLat,double,D) +MAKE_TEST(ObsLat) +MAKE_CLEAR(ObsLat) + +MAKE_GET(ObsAlt,double) +MAKE_SET(ObsAlt,double,D) +MAKE_TEST(ObsAlt) +MAKE_CLEAR(ObsAlt) + +/* Clear, Get, Set and Test axis-dependent Frame attributes. */ +MAKE_CLEAR_AXIS(Direction) +MAKE_CLEAR_AXIS(Format) +MAKE_CLEAR_AXIS(Label) +MAKE_CLEAR_AXIS(Symbol) +MAKE_CLEAR_AXIS(Unit) +MAKE_GET_AXIS(Direction,int) +MAKE_GET_AXIS(Format,const char *) +MAKE_GET_AXIS(Label,const char *) +MAKE_GET_AXIS(Symbol,const char *) +MAKE_GET_AXIS(Unit,const char *) +MAKE_SET_AXIS(Direction,int,I) +MAKE_SET_AXIS(Format,const char *,C) +MAKE_SET_AXIS(Label,const char *,C) +MAKE_SET_AXIS(Symbol,const char *,C) +MAKE_SET_AXIS(Unit,const char *,C) +MAKE_TEST_AXIS(Direction) +MAKE_TEST_AXIS(Format) +MAKE_TEST_AXIS(Label) +MAKE_TEST_AXIS(Symbol) +MAKE_TEST_AXIS(Unit) + +MAKE_GET_AXIS(Bottom,double) +MAKE_SET_AXIS(Bottom,double,D) +MAKE_TEST_AXIS(Bottom) +MAKE_CLEAR_AXIS(Bottom) + +MAKE_GET_AXIS(Top,double) +MAKE_SET_AXIS(Top,double,D) +MAKE_TEST_AXIS(Top) +MAKE_CLEAR_AXIS(Top) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Region objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Region objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstRegion *in; /* Pointer to input Region */ + AstRegion *out; /* Pointer to output Region */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Regions. */ + in = (AstRegion *) objin; + out = (AstRegion *) objout; + +/* For safety, first clear any references to the input memory from + the output Region. */ + out->basemesh = NULL; + out->basegrid = NULL; + out->frameset = NULL; + out->points = NULL; + out->unc = NULL; + out->negation = NULL; + out->defunc = NULL; + +/* Now copy each of the above structures. */ + out->frameset = astCopy( in->frameset ); + if( in->points ) out->points = astCopy( in->points ); + if( in->basemesh ) out->basemesh = astCopy( in->basemesh ); + if( in->basegrid ) out->basegrid = astCopy( in->basegrid ); + if( in->unc ) out->unc = astCopy( in->unc ); + if( in->negation ) out->negation = astCopy( in->negation ); + if( in->defunc ) out->defunc = astCopy( in->defunc ); +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Region objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Region objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstRegion *this; /* Pointer to Region */ + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) obj; + +/* Annul all resources. */ + this->frameset = astAnnul( this->frameset ); + if( this->points ) this->points = astAnnul( this->points ); + if( this->basemesh ) this->basemesh = astAnnul( this->basemesh ); + if( this->basegrid ) this->basegrid = astAnnul( this->basegrid ); + if( this->unc ) this->unc = astAnnul( this->unc ); + if( this->negation ) this->negation = astAnnul( this->negation ); + if( this->defunc ) this->defunc = astAnnul( this->defunc ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Region objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Region class to an output Channel. + +* Parameters: +* this +* Pointer to the Region whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ +#define COM_LEN 50 /* Maximum length of a comment */ + +/* Local Variables: */ + AstFrame *fr; /* Pointer to the current Frame */ + AstMapping *smap; /* Base->current Mapping */ + AstRegion *this; /* Pointer to the Region structure */ + AstRegion *unc; /* Pointer to the uncertainty Region */ + double dval; /* Floating point attribute value */ + int ival; /* Integer attribute value */ + int set; /* Attribute value set? */ + int unit; /* Base->current is unitmap? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Region structure. */ + this = (AstRegion *) this_object; + +/* Write out values representing the instance variables for the + Region class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Negated. */ +/* -------- */ + set = TestNegated( this, status ); + ival = set ? GetNegated( this, status ) : astGetNegated( this ); + astWriteInt( channel, "Negate", (ival != 0), 0, ival, + ival ? "Region negated" : "Region not negated" ); + +/* FillFactor */ +/* ---------- */ + set = TestFillFactor( this, status ); + dval = set ? GetFillFactor( this, status ) : astGetFillFactor( this ); + astWriteDouble( channel, "Fill", set, 0, dval,"Region fill factor" ); + +/* MeshSize. */ +/* --------- */ + set = TestMeshSize( this, status ); + ival = set ? GetMeshSize( this, status ) : astGetMeshSize( this ); + astWriteInt( channel, "MeshSz", set, 0, ival, + "No. of points used to represent boundary" ); + +/* Closed. */ +/* ------- */ + set = TestClosed( this, status ); + ival = set ? GetClosed( this, status ) : astGetClosed( this ); + astWriteInt( channel, "Closed", set, 0, ival, + ival ? "Boundary is inside" : "Boundary is outside" ); + +/* Adaptive */ +/* -------- */ + set = TestAdaptive( this, status ); + ival = set ? GetAdaptive( this, status ) : astGetAdaptive( this ); + astWriteInt( channel, "Adapt", (ival != 0), 0, ival, + ival ? "Region adapts to coord sys changes" : "Region does not adapt to coord sys changes" ); + +/* FrameSet */ +/* -------- */ + +/* If the vertices are the same in both base and current Frames (i.e. + if the Frames are connected by a UnitMap), then just dump the current + Frame (unless the RegionFS attribute is zero, in which case the + current Frame can be determined from the higher level context of the + Region and so does not need to be dumped- e.g. if the Region is contained + within another Region the parent Region will define the current Frame). + Otherwise, dump the whole FrameSet. */ + ival = astGetRegionFS( this ); + smap = astRegMapping( this ); + if( ( unit = astIsAUnitMap( smap ) ) ){ + set = 0; + if( ival ) { + fr = astGetFrame( this->frameset, AST__CURRENT ); + astWriteObject( channel, "Frm", 1, 1, fr, "Coordinate system" ); + fr = astAnnul( fr ); + } + } else { + set = ( ival == 0 ); + astWriteObject( channel, "FrmSet", 1, 1, this->frameset, + "Original & current coordinate systems" ); + } + +/* Annul the Mapping pointers */ + smap = astAnnul( smap ); + +/* RegionFS */ +/* -------- */ + astWriteInt( channel, "RegFS", set, 0, ival, + ival ? "Include Frame in dump" : "Do not include Frame in dump" ); + +/* Points */ +/* ------ */ + if( this->points ) { + astWriteObject( channel, "Points", 1, 1, this->points, + "Points defining the shape" ); + +/* If the FrameSet was not included in the dump, then the loaded will use + the PointSet to determine the number of axes in the frame spanned by + the Region. If there is no PointSet, then we must explicitly include + an item giving the number of axes.*/ + } else { + astWriteInt( channel, "RegAxes", 1, 1, astGetNaxes( this ), + "Number of axes spanned by the Region" ); + } + +/* Uncertainty */ +/* ----------- */ +/* Only dump the uncertinaty Region if required. */ + if( astTestUnc( this ) ) { + unc = astGetUncFrm( this, AST__BASE ); + astWriteObject( channel, "Unc", 1, 1, unc, + "Region defining positional uncertainties." ); + unc = astAnnul( unc ); + } + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsARegion and astCheckRegion functions using + the macros defined for this purpose in the "object.h" header + file. */ +astMAKE_ISA(Region,Frame) +astMAKE_CHECK(Region) + +AstRegion *astInitRegion_( void *mem, size_t size, int init, + AstRegionVtab *vtab, const char *name, + AstFrame *frame, AstPointSet *pset, + AstRegion *unc, int *status ){ +/* +*+ +* Name: +* astInitRegion + +* Purpose: +* Initialise a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstRegion *astInitRegion( void *mem, size_t size, int init, +* AstRegionVtab *vtab, const char *name, +* AstFrame *frame, AstpointSet *pset, +* AstRegion *unc ) + +* Class Membership: +* Region initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new Region object. It allocates memory (if +* necessary) to accommodate the Region plus any additional data +* associated with the derived class. It then initialises a +* Region structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for a Region at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Region is to be +* created. This must be of sufficient size to accommodate the +* Region data (sizeof(Region)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Region (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Region structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A logical flag indicating if the Region's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Region. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* frame +* Pointer to the encapsulated Frame. A deep copy of this Frame is +* taken. This means that subsequent changes to the supplied Frame +* will have no effect on the new Region. +* pset +* A PointSet holding the points which define the Region. These +* positions should refer to the given Frame. May be NULL. +* unc +* A pointer to a Region which specifies the uncertainty in the +* supplied positions (all points on the boundary of the new Region +* being initialised are assumed to have the same uncertainty). A NULL +* pointer can be supplied, in which case default uncertainties equal to +* 1.0E-6 of the dimensions of the new Region's bounding box are used. +* If an uncertainty Region is supplied, it must be of a class for +* which all instances are centro-symetric (e.g. Box, Circle, Ellipse, +* etc.) or be a Prism containing centro-symetric component Regions. +* Its encapsulated Frame must be related to the Frame supplied for +* parameter "frame" (i.e. astConvert should be able to find a Mapping +* between them). Two positions in the "frame" Frame are considered to be +* co-incident if their uncertainty Regions overlap. The centre of the +* supplied uncertainty Region is immaterial since it will be re-centred +* on the point being tested before use. A deep copy is taken of the +* supplied Region. + +* Returned Value: +* A pointer to the new Region. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstFrame *f0; /* Frame to use */ + AstRegion *new; /* Pointer to new Region */ + int nax; /* No. of axes in supplied Frame */ + int ncoord; /* Coords per point */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if( init ) astInitRegionVtab( vtab, name ); + +/* Note the number of axes in the supplied Frame. */ + nax = astGetNaxes( frame ); + +/* Check the pointset if supplied. */ + if( pset ) { + +/* Note the number of axes per point in the supplied PointSet */ + ncoord = astGetNcoord( pset ); + +/* If OK, check that the number of coordinates per point matches the number + of axes in the Frame. Report an error if these numbers do not match. */ + if ( astOK && ( ncoord != nax ) ) { + astError( AST__NCPIN, "astInitRegion(%s): Bad number of coordinate " + "values per point (%d).", status, name, ncoord ); + astError( AST__NCPIN, "The %s given requires %d coordinate value(s) " + "for each point.", status, astGetClass( frame ), nax ); + } + } + +/* Initialise a Frame structure (the parent class) as the first + component within the Region structure, allocating memory if + necessary. Give this Frame zero axes as the Frame information will be + specified by the encapsulated FrameSet. */ + new = (AstRegion *) astInitFrame( mem, size, 0, (AstFrameVtab *) vtab, + name, 0 ); + if ( astOK ) { + +/* Initialise the Region data. */ +/* ----------------------------- */ + new->frameset = NULL; + new->points = NULL; + new->unc = NULL; + new->meshsize = -INT_MAX; + new->adaptive = -INT_MAX; + new->basemesh = NULL; + new->basegrid = NULL; + new->negated = -INT_MAX; + new->closed = -INT_MAX; + new->regionfs = -INT_MAX; + new->fillfactor = AST__BAD; + new->defunc = NULL; + new->nomap = 0; + new->negation = NULL; + +/* If the supplied Frame is a Region, gets its encapsulated Frame. If a + FrameSet was supplied, use its current Frame, otherwise use the + supplied Frame. */ + if( astIsARegion( frame ) ) { + f0 = astGetFrame( ((AstRegion *) frame)->frameset, AST__CURRENT ); + + } else if( astIsAFrameSet( frame ) ) { + f0 = astGetFrame( (AstFrameSet *) frame, AST__CURRENT ); + + } else { + f0 = astClone( frame ); + } + +/* Store a clone of the supplied PointSet pointer. */ + new->points = pset ? astClone( pset ) : NULL; + + +#ifdef DEBUG + if( pset ) { + double **ptr; + double lim; + int ii,jj, np; + ptr = astGetPoints( pset ); + np = astGetNpoint( pset ); + lim = sqrt( DBL_MAX ); + for( ii = 0; astOK && ii < ncoord; ii++ ) { + for( jj = 0; jj < np; jj++ ) { + if( fabs( ptr[ ii ][ jj ] ) > lim ) { + if( !strcmp( name, "Interval" ) ) { + if( ptr[ ii ][ jj ] != AST__BAD && + ptr[ ii ][ jj ] != DBL_MAX && + ptr[ ii ][ jj ] != -DBL_MAX ) { + astError( AST__INTER, "astInitRegion(%s): suspicious " + "axis value (%g) supplied.", status, name, ptr[ ii ][ jj ] ); + break; + } + } else { + astError( AST__INTER, "astInitRegion(%s): suspicious " + "axis value (%g) supplied.", status, name, + ptr[ ii ][ jj ] ); + break; + } + } + } + } + } +#endif + +/* Form a FrameSet consisting of two copies of the supplied Frame connected + together by a UnitMap, and store in the Region structure. We use the + private SetRegFS rather than the protected astSetRegFS because this + initialiser may be being called from a subclass which over-rides + astSetRegFS. If this were the case, then the implementation of + astSetRegFS provided by the subclass may access information within the + subclass structure which has not yet been initialised. */ + SetRegFS( new, f0, status ); + f0 = astAnnul( f0 ); + +/* Store any uncertainty Region. Use the private SetUnc rather than + astSetUnc to avoid subclass implementations using subclass data which + has not yet been initialised. */ + SetUnc( new, unc, status ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstRegion *astLoadRegion_( void *mem, size_t size, + AstRegionVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadRegion + +* Purpose: +* Load a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* AstRegion *astLoadRegion( void *mem, size_t size, +* AstRegionVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Region loader. + +* Description: +* This function is provided to load a new Region using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Region structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the Region is to be +* loaded. This must be of sufficient size to accommodate the +* Region data (sizeof(Region)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Region (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Region structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstRegion) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Region. If this is NULL, a pointer +* to the (static) virtual function table for the Region class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Region" is used instead. + +* Returned Value: +* A pointer to the new Region. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstFrame *f1; /* Base Frame for encapsulated FrameSet */ + AstRegion *new; /* Pointer to the new Region */ + int nax; /* No. of axes in Frame */ + int naxpt; /* No. of axes in per point */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Region. In this case the + Region belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstRegion ); + vtab = &class_vtab; + name = "Region"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitRegionVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Region. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Region" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Negated */ +/* ------- */ + new->negated = astReadInt( channel, "negate", -INT_MAX ); + if ( TestNegated( new, status ) ) SetNegated( new, new->negated, status ); + +/* FillFactor */ +/* ---------- */ + new->fillfactor = astReadDouble( channel, "fill", AST__BAD ); + if ( TestFillFactor( new, status ) ) SetFillFactor( new, new->fillfactor, status ); + +/* MeshSize */ +/* -------- */ + new->meshsize = astReadInt( channel, "meshsz", -INT_MAX ); + if ( TestMeshSize( new, status ) ) SetMeshSize( new, new->meshsize, status ); + +/* Closed */ +/* ------ */ + new->closed = astReadInt( channel, "closed", -INT_MAX ); + if ( TestClosed( new, status ) ) SetClosed( new, new->closed, status ); + +/* Adaptive */ +/* -------- */ + new->adaptive = astReadInt( channel, "adapt", -INT_MAX ); + if ( TestAdaptive( new, status ) ) SetAdaptive( new, new->adaptive, status ); + +/* Points */ +/* ------ */ + new->points = astReadObject( channel, "points", NULL ); + +/* If some points were found, ensure that they are in a PointSet and get + the number of axis values per point. */ + if( new->points ){ + if( astIsAPointSet( new->points) ) { + naxpt = astGetNcoord( new->points ); + } else { + naxpt = 0; + astError( AST__REGIN, "astLoadRegion(%s): Corrupt %s specifies points " + "using a %s (should be a PointSet).", status, astGetClass( new ), + astGetClass( new ), astGetClass( new->points ) ); + } + +/* If no PointSet was loaded, attempt to determine the number of axes + spanned by the Region by reading the RegAxes value. */ + } else { + naxpt = astReadInt( channel, "regaxes", 0 ); + } + +/* Uncertainty */ +/* ----------- */ + new->unc = astReadObject( channel, "unc", NULL ); + new->defunc = NULL; + +/* FrameSet */ +/* -------- */ +/* First see if the dump contains a single Frame. If so, create a + FrameSet from it and a copy of itself, using a UnitMap to connect the + two. */ + new->nomap = 0; + new->frameset = NULL; + f1 = astReadObject( channel, "frm", NULL ); + if( f1 ) { + new->regionfs = 1; + nax = astGetNaxes( f1 ); + astSetRegFS( new, f1 ); + f1 = astAnnul( f1 ); + +/* If no Frame was found in the dump, look for a FrameSet. */ + } else { + new->frameset = astReadObject( channel, "frmset", NULL ); + if( new->frameset ) { + nax = astGetNaxes( new->frameset ); + +/* If a FrameSet was found, the value of the RegionFS attribute is still + unknown and so we must read it from an attribute as normal. */ + new->regionfs = astReadInt( channel, "regfs", 1 ); + if ( TestRegionFS( new, status ) ) SetRegionFS( new, new->regionfs, status ); + + } else { + nax = 0; + } + } + +/* If neither a Frame nor a FrameSet was found, create a default FrameSet + and set the RegionFS attribute false, to indicate that the FrameSet + should not be used. */ + if( !new->frameset ){ + nax = naxpt ? naxpt : 1; + f1 = astFrame( nax, "", status ); + new->frameset = astFrameSet( f1, "", status ); + astSetIdent( new->frameset, DUMMY_FS ); + f1 = astAnnul( f1 ); + new->regionfs = 0; + } + +/* Report an error if the number of axis values per point in the pointset is + incorrect. */ + if ( astOK && new->points && ( naxpt != nax ) ) { + astError( AST__REGIN, "astLoadRegion(%s): Corrupt %s contains " + " incorrect number of coordinate values per point (%d).", status, + astGetClass( new ), astGetClass( new ), naxpt ); + astError( AST__REGIN, "The %s requires %d coordinate value(s) " + "for each point.", status, astGetClass( new ), nax ); + } + +/* Initialise other fields which are used as caches for values derived + from the attributes set above. */ + new->basemesh = NULL; + new->basegrid = NULL; + +/* If an error occurred, clean up by deleting the new Region. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Region pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astRegClearAttrib_( AstRegion *this, const char *attrib, char **base_attrib, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Region,RegClearAttrib))( this, attrib, base_attrib, status ); +} +void astRegSetAttrib_( AstRegion *this, const char *setting, char **base_setting, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Region,RegSetAttrib))( this, setting, base_setting, status ); +} +void astNegate_( AstRegion *this, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,Negate))( this, status ); +} +AstFrame *astGetRegionFrame_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,GetRegionFrame))( this, status ); +} +AstFrameSet *astGetRegionFrameSet_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,GetRegionFrameSet))( this, status ); +} +AstRegion *astMapRegion_( AstRegion *this, AstMapping *map, AstFrame *frame, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,MapRegion))( this, map, frame, status ); +} +int astOverlap_( AstRegion *this, AstRegion *that, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Region,Overlap))( this, that, status ); +} +int astOverlapX_( AstRegion *that, AstRegion *this, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(that,Region,OverlapX))( that, this, status ); +} +AstFrame *astRegFrame_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegFrame))( this, status ); +} +AstRegion *astRegBasePick_( AstRegion *this, int naxes, const int *axes, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegBasePick))( this, naxes, axes, status ); +} +AstPointSet *astBTransform_( AstRegion *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,BTransform))( this, in, forward, out, status ); +} +AstPointSet *astRegTransform_( AstRegion *this, AstPointSet *in, + int forward, AstPointSet *out, + AstFrame **frm, int *status ) { + if( frm ) *frm = NULL; + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegTransform))( this, in, forward, out, frm, status ); +} +int astRegPins_( AstRegion *this, AstPointSet *pset, AstRegion *unc, int **mask, int *status ){ + if( mask ) *mask = NULL; + if ( !astOK ) return 0; + return (**astMEMBER(this,Region,RegPins))( this, pset, unc, mask, status ); +} +AstMapping *astRegMapping_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegMapping))( this, status ); +} +int astRegDummyFS_( AstRegion *this, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Region,RegDummyFS))( this, status ); +} +int astGetBounded_( AstRegion *this, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Region,GetBounded))( this, status ); +} +int astTestUnc_( AstRegion *this, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Region,TestUnc))( this, status ); +} +void astClearUnc_( AstRegion *this, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,ClearUnc))( this, status ); +} +void astRegBaseBox_( AstRegion *this, double *lbnd, double *ubnd, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,RegBaseBox))( this, lbnd, ubnd, status ); +} +void astRegBaseBox2_( AstRegion *this, double *lbnd, double *ubnd, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,RegBaseBox2))( this, lbnd, ubnd, status ); +} +void astResetCache_( AstRegion *this, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,ResetCache))( this, status ); +} +int astRegTrace_( AstRegion *this, int n, double *dist, double **ptr, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Region,RegTrace))( this, n, dist, ptr, status ); +} +void astGetRegionBounds_( AstRegion *this, double *lbnd, double *ubnd, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,GetRegionBounds))( this, lbnd, ubnd, status ); +} +void astGetRegionMesh_( AstRegion *this, int surface, int maxpoint, + int maxcoord, int *npoint, double *points, + int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,GetRegionMesh))( this, surface, maxpoint, maxcoord, + npoint, points, status ); +} +void astGetRegionPoints_( AstRegion *this, int maxpoint, int maxcoord, + int *npoint, double *points, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,GetRegionPoints))( this, maxpoint, maxcoord, + npoint, points, status ); +} +void astShowMesh_( AstRegion *this, int format, const char *ttl, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,ShowMesh))( this, format,ttl, status ); +} +void astGetRegionBounds2_( AstRegion *this, double *lbnd, double *ubnd, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,GetRegionBounds2))( this, lbnd, ubnd, status ); +} +void astRegOverlay_( AstRegion *this, AstRegion *that, int unc, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,RegOverlay))( this, that, unc, status ); +} +AstPointSet *astRegGrid_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegGrid))( this, status ); +} +AstPointSet *astRegMesh_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegMesh))( this, status ); +} +double *astRegCentre_( AstRegion *this, double *cen, double **ptr, int index, + int ifrm, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegCentre))( this, cen, ptr, index, ifrm, status ); +} +AstRegion *astGetNegation_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,GetNegation))( this, status ); +} +AstRegion *astGetUncFrm_( AstRegion *this, int ifrm, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,GetUncFrm))( this, ifrm, status ); +} +AstRegion *astGetDefUnc_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,GetDefUnc))( this, status ); +} +AstRegion *astGetUnc_( AstRegion *this, int def, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,GetUnc))( this, def, status ); +} +void astSetUnc_( AstRegion *this, AstRegion *unc, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,SetUnc))( this, unc, status ); +} +AstFrameSet *astGetRegFS_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,GetRegFS))( this, status ); +} +void astSetRegFS_( AstRegion *this, AstFrame *frm, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Region,SetRegFS))( this, frm, status ); +} +AstPointSet *astRegBaseMesh_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegBaseMesh))( this, status ); +} +AstRegion **astRegSplit_( AstRegion *this, int *nlist, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegSplit))( this, nlist, status ); +} +AstPointSet *astRegBaseGrid_( AstRegion *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,RegBaseGrid))( this, status ); +} +AstPointSet *astBndBaseMesh_( AstRegion *this, double *lbnd, double *ubnd, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,BndBaseMesh))( this, lbnd, ubnd, status ); +} +AstPointSet *astBndMesh_( AstRegion *this, double *lbnd, double *ubnd, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Region,BndMesh))( this, lbnd, ubnd, status ); +} + +#define MAKE_MASK_(X,Xtype) \ +int astMask##X##_( AstRegion *this, AstMapping *map, int inside, int ndim, \ + const int lbnd[], const int ubnd[], Xtype in[], \ + Xtype val, int *status ) { \ + if ( !astOK ) return 0; \ + return (**astMEMBER(this,Region,Mask##X))( this, map, inside, ndim, lbnd, \ + ubnd, in, val, status ); \ +} +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +MAKE_MASK_(LD,long double) +#endif +MAKE_MASK_(D,double) +MAKE_MASK_(F,float) +MAKE_MASK_(L,long int) +MAKE_MASK_(UL,unsigned long int) +MAKE_MASK_(I,int) +MAKE_MASK_(UI,unsigned int) +MAKE_MASK_(S,short int) +MAKE_MASK_(US,unsigned short int) +MAKE_MASK_(B,signed char) +MAKE_MASK_(UB,unsigned char) +#undef MAKE_MASK_ + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ + +/* Special interface function implementations. */ +/* ------------------------------------------- */ + + +AstRegion *astMapRegionId_( AstRegion *this, AstMapping *map, AstFrame *frame, int *status ) { +/* +*++ +* Name: +c astMapRegion +f AST_MAPREGION + +* Purpose: +* Transform a Region into a new Frame using a given Mapping. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "region.h" +c AstRegion *astMapRegion( AstRegion *this, AstMapping *map, +c AstFrame *frame ) +f RESULT = AST_MAPREGION( THIS, MAP, FRAME, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* This function returns a pointer to a new Region which corresponds to +* supplied Region described by some other specified coordinate system. A +* Mapping is supplied which transforms positions between the old and new +* coordinate systems. The new Region may not be of the same class as +* the original region. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Region. +c map +f MAP = INTEGER (Given) +* Pointer to a Mapping which transforms positions from the +* coordinate system represented by the supplied Region to the +* coordinate system specified by +c "frame". +f FRAME. +* The supplied Mapping should define both forward and inverse +* transformations, and these transformations should form a genuine +* inverse pair. That is, transforming a position using the forward +* transformation and then using the inverse transformation should +* produce the original input position. Some Mapping classes (such +* as PermMap, MathMap, SphMap) can result in Mappings for which this +* is not true. +c frame +f FRAME = INTEGER (Given) +* Pointer to a Frame describing the coordinate system in which +* the new Region is required. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astMapRegion() +f AST_MAPREGION = INTEGER +* A pointer to a new Region. This Region will represent the area +* within the coordinate system specified by +c "frame" +f FRAME +* which corresponds to the supplied Region. + +* Notes: +* - The uncertainty associated with the supplied Region is modified +* using the supplied Mapping. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - The only difference between this public interface and the protected +* astMapRegion interface is that this implementation additionally +* simplifies the returned Region. The protected implementation does +* not do this since doing so can lead to infinite recursion because +* it is sometimes necessary for Simplify to call astMapRegion. + +*/ + +/* Local Variables: */ + AstRegion *new; /* Pointer to new Region */ + AstRegion *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the protected astMapRegion function. */ + new = astMapRegion( this, map, frame ); + +/* Simplify the resulting Region. */ + result = astSimplify( new ); + +/* Free resources. */ + new = astAnnul( new ); + +/* If not OK, annul the returned pointer. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + + + + + + + + + + + + diff --git a/region.h b/region.h new file mode 100644 index 0000000..b6ffd4e --- /dev/null +++ b/region.h @@ -0,0 +1,515 @@ +#if !defined( REGION_INCLUDED ) /* Include this file only once */ +#define REGION_INCLUDED +/* +*+ +* Name: +* region.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Region class. + +* Invocation: +* #include "region.h" + +* Description: +* This include file defines the interface to the Region class and +* provides the type definitions, function prototypes and macros, etc. +* needed to use this class. + +* Inheritance: +* The Region class inherits from the Frame class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 5-DEC-2003 (DSB): +* Original version. +* 2-MAR-2006 (DSB): +* Changed AST_LONG_DOUBLE to HAVE_LONG_DOUBLE. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "frame.h" /* Parent Frame class */ + +/* Macros. */ +/* ======= */ + +/* Type Definitions. */ +/* ================= */ +/* Region structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +typedef struct AstRegion { + +/* Attributes inherited from the parent class. */ + AstFrame parent; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstFrameSet *frameset; /* FrameSet holding original and current Frames */ + AstPointSet *points; /* Points defining region location and extent */ + struct AstRegion *unc; /* Region specifying position uncertainties */ + double fillfactor; /* Fill factor (0.0->1.0) */ + int regionfs; /* Include FrameSet in dump? */ + int negated; /* Has the Region been negated? */ + int closed; /* Is the boundary part of the Region? */ + int meshsize; /* No. of points on boundary mesh */ + struct AstRegion *defunc; /* Default uncertainty Region */ + AstPointSet *basemesh; /* Base frame mesh covering the boundary */ + AstPointSet *basegrid; /* Base frame grid covering the boundary */ + int adaptive; /* Does the Region adapt to coord sys changes? */ + int nomap; /* Ignore the Region's FrameSet? */ + struct AstRegion *negation;/* Negated copy of "this" */ +} AstRegion; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstRegionVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* Overlap)( AstRegion *, AstRegion *, int * ); + int (* OverlapX)( AstRegion *, AstRegion *, int * ); + AstRegion *(* MapRegion)( AstRegion *, AstMapping *, AstFrame *, int * ); + AstFrame *(* GetRegionFrame)( AstRegion *, int * ); + AstFrameSet *(* GetRegionFrameSet)( AstRegion *, int * ); + AstFrame *(* RegFrame)( AstRegion *, int * ); + AstFrameSet *(* GetRegFS)( AstRegion *, int * ); + AstPointSet *(* RegTransform)( AstRegion *, AstPointSet *, int, AstPointSet *, AstFrame **, int * ); + AstPointSet *(* BTransform)( AstRegion *, AstPointSet *, int, AstPointSet *, int * ); + void (* Negate)( AstRegion *, int * ); + void (* RegBaseBox)( AstRegion *, double *, double *, int * ); + void (* RegBaseBox2)( AstRegion *, double *, double *, int * ); + void (* RegSetAttrib)( AstRegion *, const char *, char **, int * ); + void (* RegClearAttrib)( AstRegion *, const char *, char **, int * ); + void (* GetRegionBounds)( AstRegion *, double *, double *, int * ); + void (* ShowMesh)( AstRegion *, int, const char *, int * ); + void (* GetRegionBounds2)( AstRegion *, double *, double *, int * ); + void (* ClearUnc)( AstRegion *, int * ); + void (* RegOverlay)( AstRegion *, AstRegion *, int, int * ); + void (* GetRegionMesh)( AstRegion *, int, int, int, int *, double *, int * ); + void (* GetRegionPoints)( AstRegion *, int, int, int *, double *, int * ); + int (* GetBounded)( AstRegion *, int * ); + int (* TestUnc)( AstRegion *, int * ); + int (* RegDummyFS)( AstRegion *, int * ); + int (* RegPins)( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); + AstMapping *(* RegMapping)( AstRegion *, int * ); + AstPointSet *(* RegMesh)( AstRegion *, int * ); + AstPointSet *(* RegGrid)( AstRegion *, int * ); + AstPointSet *(* RegBaseMesh)( AstRegion *, int * ); + AstPointSet *(* RegBaseGrid)( AstRegion *, int * ); + AstRegion **(* RegSplit)( AstRegion *, int *, int * ); + AstPointSet *(* BndBaseMesh)( AstRegion *, double *, double *, int * ); + AstPointSet *(* BndMesh)( AstRegion *, double *, double *, int * ); + AstRegion *(* GetNegation)( AstRegion *, int * ); + AstRegion *(* GetUncFrm)( AstRegion *, int, int * ); + AstRegion *(* GetUnc)( AstRegion *, int, int * ); + AstRegion *(* GetDefUnc)( AstRegion *, int * ); + AstRegion *(* RegBasePick)( AstRegion *this, int, const int *, int * ); + void (* ResetCache)( AstRegion *, int * ); + int (* RegTrace)( AstRegion *, int, double *, double **, int * ); + void (* SetUnc)( AstRegion *, AstRegion *, int * ); + void (* SetRegFS)( AstRegion *, AstFrame *, int * ); + double *(* RegCentre)( AstRegion *, double *, double **, int, int, int * ); + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ + int (* MaskLD)( AstRegion *, AstMapping *, int, int, const int[], const int ubnd[], long double [], long double, int * ); +#endif + int (* MaskB)( AstRegion *, AstMapping *, int, int, const int[], const int[], signed char[], signed char, int * ); + int (* MaskD)( AstRegion *, AstMapping *, int, int, const int[], const int[], double[], double, int * ); + int (* MaskF)( AstRegion *, AstMapping *, int, int, const int[], const int[], float[], float, int * ); + int (* MaskI)( AstRegion *, AstMapping *, int, int, const int[], const int[], int[], int, int * ); + int (* MaskL)( AstRegion *, AstMapping *, int, int, const int[], const int[], long int[], long int, int * ); + int (* MaskS)( AstRegion *, AstMapping *, int, int, const int[], const int[], short int[], short int, int * ); + int (* MaskUB)( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned char[], unsigned char, int * ); + int (* MaskUI)( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned int[], unsigned int, int * ); + int (* MaskUL)( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned long int[], unsigned long int, int * ); + int (* MaskUS)( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned short int[], unsigned short int, int * ); + + int (* GetNegated)( AstRegion *, int * ); + int (* TestNegated)( AstRegion *, int * ); + void (* ClearNegated)( AstRegion *, int * ); + void (* SetNegated)( AstRegion *, int, int * ); + + int (* GetRegionFS)( AstRegion *, int * ); + int (* TestRegionFS)( AstRegion *, int * ); + void (* ClearRegionFS)( AstRegion *, int * ); + void (* SetRegionFS)( AstRegion *, int, int * ); + + int (* GetClosed)( AstRegion *, int * ); + int (* TestClosed)( AstRegion *, int * ); + void (* ClearClosed)( AstRegion *, int * ); + void (* SetClosed)( AstRegion *, int, int * ); + + int (* GetMeshSize)( AstRegion *, int * ); + int (* TestMeshSize)( AstRegion *, int * ); + void (* ClearMeshSize)( AstRegion *, int * ); + void (* SetMeshSize)( AstRegion *, int, int * ); + + double (* GetFillFactor)( AstRegion *, int * ); + int (* TestFillFactor)( AstRegion *, int * ); + void (* ClearFillFactor)( AstRegion *, int * ); + void (* SetFillFactor)( AstRegion *, double, int * ); + + int (* GetAdaptive)( AstRegion *, int * ); + int (* TestAdaptive)( AstRegion *, int * ); + void (* ClearAdaptive)( AstRegion *, int * ); + void (* SetAdaptive)( AstRegion *, int, int * ); + +} AstRegionVtab; +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstRegionGlobals { + AstRegionVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstRegionGlobals; + +#endif +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Region) /* Check class membership */ +astPROTO_ISA(Region) /* Test class membership */ + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstRegion *astInitRegion_( void *, size_t, int, AstRegionVtab *, const char *, + AstFrame *, AstPointSet *, AstRegion *, int * ); + +/* Vtab initialiser. */ +void astInitRegionVtab_( AstRegionVtab *, const char *, int * ); + +/* Loader. */ +AstRegion *astLoadRegion_( void *, size_t, AstRegionVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitRegionGlobals_( AstRegionGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +AstFrame *astGetRegionFrame_( AstRegion *, int * ); +AstFrameSet *astGetRegionFrameSet_( AstRegion *, int * ); +int astOverlap_( AstRegion *, AstRegion *, int * ); +void astNegate_( AstRegion *, int * ); + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +int astMaskLD_( AstRegion *, AstMapping *, int, int, const int[], const int[], long double [], long double, int * ); +#endif +int astMaskB_( AstRegion *, AstMapping *, int, int, const int[], const int[], signed char[], signed char, int * ); +int astMaskD_( AstRegion *, AstMapping *, int, int, const int[], const int[], double[], double, int * ); +int astMaskF_( AstRegion *, AstMapping *, int, int, const int[], const int[], float[], float, int * ); +int astMaskI_( AstRegion *, AstMapping *, int, int, const int[], const int[], int[], int, int * ); +int astMaskL_( AstRegion *, AstMapping *, int, int, const int[], const int[], long int[], long int, int * ); +int astMaskS_( AstRegion *, AstMapping *, int, int, const int[], const int[], short int[], short int, int * ); +int astMaskUB_( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned char[], unsigned char, int * ); +int astMaskUI_( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned int[], unsigned int, int * ); +int astMaskUL_( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned long int[], unsigned long int, int * ); +int astMaskUS_( AstRegion *, AstMapping *, int, int, const int[], const int[], unsigned short int[], unsigned short int, int * ); +void astSetUnc_( AstRegion *, AstRegion *, int * ); +AstRegion *astGetNegation_( AstRegion *, int * ); +AstRegion *astGetUnc_( AstRegion *, int, int * ); +void astGetRegionBounds_( AstRegion *, double *, double *, int * ); +void astShowMesh_( AstRegion *, int, const char *, int * ); +void astGetRegionMesh_( AstRegion *, int, int, int, int *, double *, int * ); +void astGetRegionPoints_( AstRegion *, int, int, int *, double *, int * ); + +#if defined(astCLASS) /* Protected */ +void astGetRegionBounds2_( AstRegion *, double *, double *, int * ); +AstRegion *astMapRegion_( AstRegion *, AstMapping *, AstFrame *, int * ); +AstFrame *astRegFrame_( AstRegion *, int * ); +AstPointSet *astRegTransform_( AstRegion *, AstPointSet *, int, AstPointSet *, AstFrame **, int * ); +AstPointSet *astBTransform_( AstRegion *, AstPointSet *, int, AstPointSet *, int * ); +void astRegBaseBox_( AstRegion *, double *, double *, int * ); +void astRegBaseBox2_( AstRegion *, double *, double *, int * ); +void astRegSetAttrib_( AstRegion *, const char *, char **, int * ); +void astRegClearAttrib_( AstRegion *, const char *, char **, int * ); +void astClearUnc_( AstRegion *, int * ); +void astRegOverlay_( AstRegion *, AstRegion *, int, int * ); +int astGetBounded_( AstRegion *, int * ); +int astTestUnc_( AstRegion *, int * ); +int astRegDummyFS_( AstRegion *, int * ); +int astRegPins_( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +AstMapping *astRegMapping_( AstRegion *, int * ); +AstPointSet *astRegMesh_( AstRegion *, int * ); +AstPointSet *astRegGrid_( AstRegion *, int * ); +AstPointSet *astRegBaseMesh_( AstRegion *, int * ); +AstPointSet *astRegBaseGrid_( AstRegion *, int * ); +AstPointSet *astBndBaseMesh_( AstRegion *, double *, double *, int * ); +AstRegion **astRegSplit_( AstRegion *, int *, int * ); +AstPointSet *astBndMesh_( AstRegion *, double *, double *, int * ); +AstRegion *astGetUncFrm_( AstRegion *, int, int * ); +AstRegion *astGetDefUnc_( AstRegion *, int * ); +AstRegion *astRegBasePick_( AstRegion *this, int, const int *, int * ); +int astOverlapX_( AstRegion *, AstRegion *, int * ); +AstFrameSet *astGetRegFS_( AstRegion *, int * ); +void astSetRegFS_( AstRegion *, AstFrame *, int * ); +double *astRegCentre_( AstRegion *, double *, double **, int, int, int * ); +double *astRegTranPoint_( AstRegion *, double *, int, int, int * ); +void astResetCache_( AstRegion *, int * ); +int astRegTrace_( AstRegion *, int, double *, double **, int * ); + +int astGetNegated_( AstRegion *, int * ); +int astTestNegated_( AstRegion *, int * ); +void astClearNegated_( AstRegion *, int * ); +void astSetNegated_( AstRegion *, int, int * ); + +int astGetRegionFS_( AstRegion *, int * ); +int astTestRegionFS_( AstRegion *, int * ); +void astClearRegionFS_( AstRegion *, int * ); +void astSetRegionFS_( AstRegion *, int, int * ); + +int astGetMeshSize_( AstRegion *, int * ); +int astTestMeshSize_( AstRegion *, int * ); +void astClearMeshSize_( AstRegion *, int * ); +void astSetMeshSize_( AstRegion *, int, int * ); + +int astGetClosed_( AstRegion *, int * ); +int astTestClosed_( AstRegion *, int * ); +void astClearClosed_( AstRegion *, int * ); +void astSetClosed_( AstRegion *, int, int * ); + +double astGetFillFactor_( AstRegion *, int * ); +int astTestFillFactor_( AstRegion *, int * ); +void astClearFillFactor_( AstRegion *, int * ); +void astSetFillFactor_( AstRegion *, double, int * ); + +int astGetAdaptive_( AstRegion *, int * ); +int astTestAdaptive_( AstRegion *, int * ); +void astClearAdaptive_( AstRegion *, int * ); +void astSetAdaptive_( AstRegion *, int, int * ); + +#else /* Public only */ +AstRegion *astMapRegionId_( AstRegion *, AstMapping *, AstFrame *, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class to make + them easier to invoke (e.g. to avoid type mis-matches when passing pointers + to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them to + validate their own arguments. We must use a cast when passing object + pointers (so that they can accept objects from derived classes). */ + +/* Check class membership. */ +#define astCheckRegion(this) astINVOKE_CHECK(Region,this,0) +#define astVerifyRegion(this) astINVOKE_CHECK(Region,this,1) + +/* Test class membership. */ +#define astIsARegion(this) astINVOKE_ISA(Region,this) + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitRegion(mem,size,init,vtab,name,frame,pset,acc)\ +astINVOKE(O,astInitRegion_(mem,size,init,vtab,name,astCheckFrame(frame),pset,acc,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitRegionVtab(vtab,name) astINVOKE(V,astInitRegionVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadRegion(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadRegion_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckRegion to validate Region pointers before + use. This provides a contextual error report if a pointer to the wrong sort + of object is supplied. */ +#define astGetRegionFrame(this) \ +astINVOKE(O,astGetRegionFrame_(astCheckRegion(this),STATUS_PTR)) +#define astGetRegionFrameSet(this) \ +astINVOKE(O,astGetRegionFrameSet_(astCheckRegion(this),STATUS_PTR)) +#define astNegate(this) \ +astINVOKE(V,astNegate_(astCheckRegion(this),STATUS_PTR)) +#define astOverlap(this,that) \ +astINVOKE(V,astOverlap_(astCheckRegion(this),astCheckRegion(that),STATUS_PTR)) + +#if HAVE_LONG_DOUBLE /* Not normally implemented */ +#define astMaskLD(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskLD_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#endif + +#define astMaskB(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskB_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskD(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskD_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskF(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskF_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskI(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskI_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskL(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskL_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskS(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskS_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskUB(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskUB_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskUI(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskUI_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskUL(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskUL_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astMaskUS(this,map,inside,ndim,lbnd,ubnd,in,val) \ +astINVOKE(V,astMaskUS_(astCheckRegion(this),(map?astCheckMapping(map):NULL),inside,ndim,lbnd,ubnd,in,val,STATUS_PTR)) +#define astSetUnc(this,unc) astINVOKE(V,astSetUnc_(astCheckRegion(this),unc?astCheckRegion(unc):NULL,STATUS_PTR)) +#define astGetUnc(this,def) astINVOKE(O,astGetUnc_(astCheckRegion(this),def,STATUS_PTR)) +#define astGetRegionBounds(this,lbnd,ubnd) astINVOKE(V,astGetRegionBounds_(astCheckRegion(this),lbnd,ubnd,STATUS_PTR)) +#define astShowMesh(this,format,ttl) astINVOKE(V,astShowMesh_(astCheckRegion(this),format,ttl,STATUS_PTR)) +#define astGetRegionMesh(this,surface,maxpoint,maxcoord,npoint,points) \ +astINVOKE(V,astGetRegionMesh_(astCheckRegion(this),surface,maxpoint,maxcoord,npoint,points,STATUS_PTR)) +#define astGetRegionPoints(this,maxpoint,maxcoord,npoint,points) \ +astINVOKE(V,astGetRegionPoints_(astCheckRegion(this),maxpoint,maxcoord,npoint,points,STATUS_PTR)) + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +#if defined(astCLASS) /* Protected */ + +#define astGetNegation(this) astINVOKE(O,astGetNegation_(astCheckRegion(this),STATUS_PTR)) +#define astGetRegionBounds2(this,lbnd,ubnd) astINVOKE(V,astGetRegionBounds2_(astCheckRegion(this),lbnd,ubnd,STATUS_PTR)) +#define astClearUnc(this) astINVOKE(V,astClearUnc_(astCheckRegion(this),STATUS_PTR)) +#define astGetBounded(this) astINVOKE(V,astGetBounded_(astCheckRegion(this),STATUS_PTR)) +#define astGetUncFrm(this,ifrm) astINVOKE(O,astGetUncFrm_(astCheckRegion(this),ifrm,STATUS_PTR)) +#define astGetDefUnc(this) astINVOKE(O,astGetDefUnc_(astCheckRegion(this),STATUS_PTR)) +#define astMapRegion(this,map,frame) astINVOKE(O,astMapRegion_(astCheckRegion(this),astCheckMapping(map),astCheckFrame(frame),STATUS_PTR)) +#define astOverlapX(that,this) astINVOKE(V,astOverlapX_(astCheckRegion(that),astCheckRegion(this),STATUS_PTR)) +#define astRegBaseBox(this,lbnd,ubnd) astINVOKE(V,astRegBaseBox_(astCheckRegion(this),lbnd,ubnd,STATUS_PTR)) +#define astRegBaseBox2(this,lbnd,ubnd) astINVOKE(V,astRegBaseBox2_(astCheckRegion(this),lbnd,ubnd,STATUS_PTR)) +#define astRegSetAttrib(this,setting,bset) astINVOKE(V,astRegSetAttrib_(astCheckRegion(this),setting,bset,STATUS_PTR)) +#define astRegClearAttrib(this,setting,batt) astINVOKE(V,astRegClearAttrib_(astCheckRegion(this),setting,batt,STATUS_PTR)) +#define astRegBaseMesh(this) astINVOKE(O,astRegBaseMesh_(astCheckRegion(this),STATUS_PTR)) +#define astRegBasePick(this,naxes,axes) astINVOKE(O,astRegBasePick_(astCheckRegion(this),naxes,axes,STATUS_PTR)) +#define astRegBaseGrid(this) astINVOKE(O,astRegBaseGrid_(astCheckRegion(this),STATUS_PTR)) +#define astRegSplit(this,nlist) astINVOKE(V,astRegSplit_(astCheckRegion(this),nlist,STATUS_PTR)) +#define astBndBaseMesh(this,lbnd,ubnd) astINVOKE(O,astBndBaseMesh_(astCheckRegion(this),lbnd,ubnd,STATUS_PTR)) +#define astBndMesh(this,lbnd,ubnd) astINVOKE(O,astBndMesh_(astCheckRegion(this),lbnd,ubnd,STATUS_PTR)) +#define astRegCentre(this,cen,ptr,index,ifrm) astINVOKE(V,astRegCentre_(astCheckRegion(this),cen,ptr,index,ifrm,STATUS_PTR)) +#define astRegFrame(this) astINVOKE(O,astRegFrame_(astCheckRegion(this),STATUS_PTR)) +#define astRegGrid(this) astINVOKE(O,astRegGrid_(astCheckRegion(this),STATUS_PTR)) +#define astRegMesh(this) astINVOKE(O,astRegMesh_(astCheckRegion(this),STATUS_PTR)) +#define astRegOverlay(this,that,unc) astINVOKE(V,astRegOverlay_(astCheckRegion(this),astCheckRegion(that),unc,STATUS_PTR)) +#define astRegDummyFS(this) astINVOKE(V,astRegDummyFS_(astCheckRegion(this),STATUS_PTR)) +#define astRegMapping(this) astINVOKE(O,astRegMapping_(astCheckRegion(this),STATUS_PTR)) +#define astRegPins(this,pset,unc,mask) astINVOKE(V,astRegPins_(astCheckRegion(this),astCheckPointSet(pset),unc?astCheckRegion(unc):unc,mask,STATUS_PTR)) +#define astRegTranPoint(this,in,np,forward) astRegTranPoint_(this,in,np,forward,STATUS_PTR) +#define astGetRegFS(this) astINVOKE(O,astGetRegFS_(astCheckRegion(this),STATUS_PTR)) +#define astSetRegFS(this,frm) astINVOKE(V,astSetRegFS_(astCheckRegion(this),astCheckFrame(frm),STATUS_PTR)) +#define astTestUnc(this) astINVOKE(V,astTestUnc_(astCheckRegion(this),STATUS_PTR)) +#define astResetCache(this) astINVOKE(V,astResetCache_(astCheckRegion(this),STATUS_PTR)) +#define astRegTrace(this,n,dist,ptr) astINVOKE(V,astRegTrace_(astCheckRegion(this),n,dist,ptr,STATUS_PTR)) + +/* Since a NULL PointSet pointer is acceptable for "out", we must omit the + argument checking in that case. (But unfortunately, "out" then gets + evaluated twice - this is unlikely to matter, but is there a better way?) */ + +#define astRegTransform(this,in,forward,out,frm) \ +astINVOKE(O,astRegTransform_(astCheckRegion(this),in?astCheckPointSet(in):NULL,forward,(out)?astCheckPointSet(out):NULL,frm,STATUS_PTR)) + +#define astBTransform(this,in,forward,out) \ +astINVOKE(O,astBTransform_(astCheckRegion(this),in?astCheckPointSet(in):NULL,forward,(out)?astCheckPointSet(out):NULL,STATUS_PTR)) + +#define astClearNegated(this) astINVOKE(V,astClearNegated_(astCheckRegion(this),STATUS_PTR)) +#define astGetNegated(this) astINVOKE(V,astGetNegated_(astCheckRegion(this),STATUS_PTR)) +#define astSetNegated(this,negated) astINVOKE(V,astSetNegated_(astCheckRegion(this),negated,STATUS_PTR)) +#define astTestNegated(this) astINVOKE(V,astTestNegated_(astCheckRegion(this),STATUS_PTR)) + +#define astClearAdaptive(this) astINVOKE(V,astClearAdaptive_(astCheckRegion(this),STATUS_PTR)) +#define astGetAdaptive(this) astINVOKE(V,astGetAdaptive_(astCheckRegion(this),STATUS_PTR)) +#define astSetAdaptive(this,adaptive) astINVOKE(V,astSetAdaptive_(astCheckRegion(this),adaptive,STATUS_PTR)) +#define astTestAdaptive(this) astINVOKE(V,astTestAdaptive_(astCheckRegion(this),STATUS_PTR)) + +#define astClearRegionFS(this) astINVOKE(V,astClearRegionFS_(astCheckRegion(this),STATUS_PTR)) +#define astGetRegionFS(this) astINVOKE(V,astGetRegionFS_(astCheckRegion(this),STATUS_PTR)) +#define astSetRegionFS(this,fs) astINVOKE(V,astSetRegionFS_(astCheckRegion(this),fs,STATUS_PTR)) +#define astTestRegionFS(this) astINVOKE(V,astTestRegionFS_(astCheckRegion(this),STATUS_PTR)) + +#define astClearMeshSize(this) astINVOKE(V,astClearMeshSize_(astCheckRegion(this),STATUS_PTR)) +#define astGetMeshSize(this) astINVOKE(V,astGetMeshSize_(astCheckRegion(this),STATUS_PTR)) +#define astSetMeshSize(this,meshsize) astINVOKE(V,astSetMeshSize_(astCheckRegion(this),meshsize,STATUS_PTR)) +#define astTestMeshSize(this) astINVOKE(V,astTestMeshSize_(astCheckRegion(this),STATUS_PTR)) + +#define astClearClosed(this) astINVOKE(V,astClearClosed_(astCheckRegion(this),STATUS_PTR)) +#define astGetClosed(this) astINVOKE(V,astGetClosed_(astCheckRegion(this),STATUS_PTR)) +#define astSetClosed(this,closed) astINVOKE(V,astSetClosed_(astCheckRegion(this),closed,STATUS_PTR)) +#define astTestClosed(this) astINVOKE(V,astTestClosed_(astCheckRegion(this),STATUS_PTR)) + +#define astClearFillFactor(this) astINVOKE(V,astClearFillFactor_(astCheckRegion(this),STATUS_PTR)) +#define astGetFillFactor(this) astINVOKE(V,astGetFillFactor_(astCheckRegion(this),STATUS_PTR)) +#define astSetFillFactor(this,ff) astINVOKE(V,astSetFillFactor_(astCheckRegion(this),ff,STATUS_PTR)) +#define astTestFillFactor(this) astINVOKE(V,astTestFillFactor_(astCheckRegion(this),STATUS_PTR)) + +#else /* Public only */ +#define astMapRegion(this,map,frame) astINVOKE(O,astMapRegionId_(astCheckRegion(this),astCheckMapping(map),astCheckFrame(frame),STATUS_PTR)) +#endif + +#endif + + + + + diff --git a/selectfc b/selectfc new file mode 100755 index 0000000..abf0dc7 --- /dev/null +++ b/selectfc @@ -0,0 +1,29 @@ +#! /usr/bin/env perl + + $key = "c"; + $select = 1; + +# Read switches. + while ( $_ = $ARGV[0], /^-/ ) { + shift; + last if /^--$/; + if ( /^-f/ ) { $key = "f" }; + } + +# Read input lines. + line: while (<>) { + +# Detect start of prologue and initialise prologue text. + if ( /^[fc]\+ *$/ ) { + $select = 0; + if ( /^$key\+ *$/o ) { $select = 1; } + next line; + } + +# Detect end of prologue. + if ( /^[fc]- *$/ ) { $select = 1; next line } + +# Process the prologue contents. + if ( $select ) { print; } + + } diff --git a/selectormap.c b/selectormap.c new file mode 100644 index 0000000..87a6f45 --- /dev/null +++ b/selectormap.c @@ -0,0 +1,1838 @@ +/* +*class++ +* Name: +* SelectorMap + +* Purpose: +* A Mapping that locates positions within one of a set of alternate +* Regions. + +* Constructor Function: +c astSelectorMap +f AST_SELECTORMAP + +* Description: +* A SelectorMap is a Mapping that identifies which Region contains +* a given input position. +* +* A SelectorMap encapsulates a number of Regions that all have the same +* number of axes and represent the same coordinate Frame. The number of +* inputs (Nin attribute) of the SelectorMap equals the number of axes +* spanned by one of the encapsulated Region. All SelectorMaps have only +* a single output. SelectorMaps do not define an inverse transformation. +* +* For each input position, the forward transformation of a SelectorMap +* searches through the encapsulated Regions (in the order supplied when +* the SelectorMap was created) until a Region is found which contains +* the input position. The index associated with this Region is +* returned as the SelectorMap output value (the index value is the +* position of the Region within the list of Regions supplied when the +* SelectorMap was created, starting at 1 for the first Region). If an +* input position is not contained within any Region, a value of zero is +* returned by the forward transformation. +* +* If a compound Mapping contains a SelectorMap in series with its own +* inverse, the combination of the two adjacent SelectorMaps will be +* replaced by a UnitMap when the compound Mapping is simplified using +c astSimplify. +f AST_SIMPLIFY. +* +* In practice, SelectorMaps are often used in conjunction with SwitchMaps. + +* Inheritance: +* The SelectorMap class inherits from the Mapping class. + +* Attributes: +* The SelectorMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The SelectorMap class does not define any new functions beyond those +f The SelectorMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 15-MAR-2006 (DSB): +* Original version. +* 18-MAY-2006 (DSB): +* - Change logic for detecting interior points in function Transform. +* - Added BADVAL to contructor argument list. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SelectorMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "unitmap.h" /* Unit Mappings */ +#include "channel.h" /* I/O channels */ +#include "selectormap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SelectorMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SelectorMap,Class_Init) +#define class_vtab astGLOBAL(SelectorMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSelectorMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSelectorMap *astSelectorMapId_( int, void **, double, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two SelectorMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "selectormap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* SelectorMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two SelectorMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a SelectorMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the SelectorMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSelectorMap *that; + AstSelectorMap *this; + int i; + int nin; + int nreg; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two SelectorMap structures. */ + this = (AstSelectorMap *) this_object; + that = (AstSelectorMap *) that_object; + +/* Check the second object is a SelectorMap. We know the first is a + SelectorMap since we have arrived at this implementation of the virtual + function. */ + if( astIsASelectorMap( that ) ) { + +/* Check they have the same number of inputs. */ + nin = astGetNin( this ); + if( astGetNin( that ) == nin ) { + +/* Check they contain the same number of Regions, and have the same badval. */ + nreg = this->nreg; + if( that->nreg == nreg || + astEQUAL( that->badval, this->badval) ) { + +/* Loop over the Regions, breaking as soon as two unequal Regions are + found. */ + result = 1; + for( i = 0; i < nreg; i++ ) { + if( !astEqual( this->reg[ i ], that->reg[ i ] ) ) { + result = 0; + break; + } + } + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "selectormap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SelectorMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SelectorMap, +* in bytes. + +* Parameters: +* this +* Pointer to the SelectorMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSelectorMap *this; + int i; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SelectorMap structure. */ + this = (AstSelectorMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + for( i = 0; i < this->nreg; i++ ) { + result += astGetObjSize( this->reg[ i ] ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitSelectorMapVtab_( AstSelectorMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSelectorMapVtab + +* Purpose: +* Initialise a virtual function table for a SelectorMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "selectormap.h" +* void astInitSelectorMapVtab( AstSelectorMapVtab *vtab, const char *name ) + +* Class Membership: +* SelectorMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SelectorMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASelectorMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* None. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "SelectorMap", "Region identification Mapping" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* SelectorMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstSelectorMap *this; /* Pointer to SelectorMap structure */ + int i; /* Loop count */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the SelectorMap structure. */ + this = (AstSelectorMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + for( i = 0; i < this->nreg; i++ ) { + if( !result ) result = astManageLock( this->reg[ i ], mode, extra, fail ); + } + + return result; + +} +#endif + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a SelectorMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* SelectorMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated SelectorMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated SelectorMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated SelectorMap which is to be merged with +* its neighbours. This should be a cloned copy of the SelectorMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* SelectorMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated SelectorMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *new; + AstRegion **sreg; + AstSelectorMap *map; + AstSelectorMap *slneb; + int equal; + int i; + int ilo; + int nreg; + int result; + int simp; + +/* Initialise.*/ + result = -1; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Get a pointer to this SelectorMap, and note the number of Regions. */ + map = (AstSelectorMap *) this; + nreg = map->nreg; + +/* Attempt to simplify the SelectorMap on its own. */ +/* ============================================= */ + +/* Try to simplify each of the encapsulated Regions, noting if any + simplification takes place. */ + simp = 0; + sreg = astMalloc( sizeof( AstRegion * )*nreg ); + if( astOK ) { + for( i = 0; i < nreg; i++ ) { + sreg[ i ] = astSimplify( map->reg[ i ] ); + simp = simp || ( sreg[ i ] != map->reg[ i ] ); + } + +/* If any simplification took place, construct a new SelectorMap from these + simplified Mappings. */ + if( simp ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astSelectorMap( nreg, + (void **) sreg, + map->badval, "", status ); + result = where; + } + +/* Release resources. */ + if( sreg ) { + for( i = 0; i < nreg; i++ ) sreg[ i ] = astAnnul( sreg[ i ] ); + sreg = astFree( sreg ); + } + } + +/* If possible, merge the SelectorMap with a neighbouring SelectorMap. */ +/* =============================================================== */ +/* Only do this if no change was made above, and we are combining the + Mappings in series. */ + if( result == -1 && series ) { + +/* Is the higher neighbour a SelectorMap? If so get a pointer to it, and + note the index of the lower of the two adjacent SelectorMaps. */ + if( where < ( *nmap - 1 ) && + astIsASelectorMap( ( *map_list )[ where + 1 ] ) ){ + slneb = (AstSelectorMap *) ( *map_list )[ where + 1 ]; + ilo = where; + +/* If not, is the lower neighbour a SelectorMap? If so get a pointer to it, and + note the index of the lower of the two adjacent SelectorMaps. */ + } else if( where > 0 && + astIsASelectorMap( ( *map_list )[ where - 1 ] ) ){ + slneb = (AstSelectorMap *) ( *map_list )[ where - 1 ]; + ilo = where - 1; + + } else { + slneb = NULL; + } + +/* If a neighbouring SelectorMap was found, we can replace the pair by a + UnitMap if the two SelectorMaps are equal but have opposite values for + their Invert flags. Temporarily invert the neighbour, then compare + the two SelectorMaps for equality, then re-invert the neighbour. */ + if( slneb ) { + astInvert( slneb ); + equal = astEqual( map, slneb ); + astInvert( slneb ); + +/* If the two SelectorMaps are equal but opposite, annul the first of the two + Mappings, and replace it with a UnitMap. Also set the invert flag. */ + if( equal ) { + new = (AstMapping *) astUnitMap( astGetNin( ( *map_list )[ ilo ] ), "", status ); + (void) astAnnul( ( *map_list )[ ilo ] ); + ( *map_list )[ ilo ] = new; + ( *invert_list )[ ilo ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ ilo + 1 ] ); + for ( i = ilo + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = where; + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a SelectorMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "selectormap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* SelectorMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a SelectorMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Mapping. +* This implies applying each of the SelectorMap's component Mappings in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the SelectorMap. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the SelectorMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *ps1; + AstPointSet *ps2; + AstPointSet *result; + AstPointSet *tps; + AstRegion *reg; + AstSelectorMap *map; + double **ptr_out; + double **ptr1; + double **ptr2; + double **tptr; + double *p2; + double *pout; + double badval; + int bad; + int closed; + int icoord; + int ipoint; + int ireg; + int ncoord; + int npoint; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SelectorMap. */ + map = (AstSelectorMap *) this; + +/* Apply the parent Mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We now extend the parent astTransform method by applying the component + Mappings of the SelectorMap to generate the output coordinate values. */ + +/* Check we are implementing the original forward transformation (the + inverse transformation is not defined). */ + if( forward != astGetInvert( this ) ) { + +/* Get the number of input axes and the number of points. */ + ncoord = astGetNcoord( in ); + npoint = astGetNpoint( in ); + +/* Create two temporary PointSets to hold copies of the input points. */ + ps1 = astCopy( in ); + ptr1 = astGetPoints( ps1 ); + ps2 = astPointSet( npoint, ncoord, "", status ); + ptr2 = astGetPoints( ps2 ); + +/* Get a pointer to the output data */ + ptr_out = astGetPoints( result ); + if( astOK ) { + +/* Initialise the output array to hold -1 at any points that have + bad input axis values, and zero at all other points. */ + pout = ptr_out[ 0 ]; + for( ipoint = 0; ipoint < npoint; ipoint++ ) { + bad = 0; + for( icoord = 0; icoord < ncoord; icoord++ ) { + if( ptr1[ icoord ][ ipoint ] == AST__BAD ) { + bad = 1; + break; + } + } + *(pout++) = bad ? -1 : 0; + } + +/* Loop round all Regions. */ + for( ireg = 1; ireg <= map->nreg; ireg++ ) { + reg = map->reg[ ireg - 1 ]; + +/* Temporarily Negate the Region. */ + astNegate( reg ); + closed = astGetClosed( reg ); + astSetClosed( reg, !closed ); + +/* Transform the remaining input positions. Good input positions which + are within the Region will be bad in the output. */ + ps2 = astTransform( reg, ps1, 1, ps2 ); + +/* Loop round all positions. */ + p2 = ptr2[ 0 ]; + pout = ptr_out[ 0 ]; + for( ipoint = 0; ipoint < npoint; ipoint++, p2++, pout++ ) { + +/* Any position that has not already been assigned to a Region and is bad + in the output PointSet must be contained within the current Region, so + assign the (one-based) index of the current Region to the output element. */ + if( *pout == 0 && *p2 == AST__BAD ) *pout = ireg; + } + +/* Negate the Region to get it back to its original state. */ + astSetClosed( reg, closed ); + astNegate( reg ); + +/* Swap the input and output PointSets. */ + tps = ps1; + ps1 = ps2; + ps2 = tps; + tptr = ptr1; + ptr1 = ptr2; + ptr2 = tptr; + } + +/* Replace -1 values in the output (that indicate that the input position + had at least one bad axis value) with the "badval".*/ + badval = map->badval; + pout = ptr_out[ 0 ]; + for( ipoint = 0; ipoint < npoint; ipoint++, pout++ ) { + if( *pout == -1 ) *pout = badval; + } + } + +/* Free resources. */ + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + } + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SelectorMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SelectorMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the +* Regions within the SelectorMap. +*/ + +/* Local Variables: */ + AstSelectorMap *in; /* Pointer to input SelectorMap */ + AstSelectorMap *out; /* Pointer to output SelectorMap */ + int i; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SelectorMaps. */ + in = (AstSelectorMap *) objin; + out = (AstSelectorMap *) objout; + +/* For safety, start by clearing any references to the input Regions. */ + out->reg = NULL; + out->nreg = 0; + +/* Make copies of the Regions, and store pointers to them in the output + SelectorMap structure. */ + out->reg = astMalloc( sizeof( AstRegion * )*( in->nreg ) ); + if( astOK ) { + for( i = 0; i < in->nreg; i++ ) { + out->reg[ i ] = astCopy( in->reg[ i ] ); + } + out->nreg = in->nreg; + } + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SelectorMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SelectorMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSelectorMap *this; /* Pointer to SelectorMap */ + int i; + +/* Obtain a pointer to the SelectorMap structure. */ + this = (AstSelectorMap *) obj; + +/* Free dynamically allocated resources. */ + for( i = 0; i < this->nreg; i++ ) { + this->reg[ i ] = astAnnul( this->reg[ i ] ); + } + this->reg = astFree( this->reg ); + +/* Clear the remaining SelectorMap variables. */ + this->nreg = 0; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SelectorMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SelectorMap class to an output Channel. + +* Parameters: +* this +* Pointer to the SelectorMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSelectorMap *this; + int i; + char buf[ 20 ]; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SelectorMap structure. */ + this = (AstSelectorMap *) this_object; + +/* Write out values representing the instance variables for the SelectorMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Loop to dump each Region. */ +/* ------------------------- */ +/* The coordinate Frame of the Regions is defined by the first Region. + The Frame information is omitted from the second and subsequent + Regions by setting the protected RegionFS attribute to zero. */ + for( i = 0; i < this->nreg; i++ ) { + sprintf( buf, "Reg%d", i + 1 ); + if( i > 0 ) astSetRegionFS( this->reg[ i ], 0 ); + astWriteObject( channel, buf, 1, 1, this->reg[ i ], + "Region of input space" ); + if( i > 0 ) astClearRegionFS( this->reg[ i ] ); + } + +/* BadVal. */ +/* ------- */ + if( this->badval != AST__BAD ) { + astWriteDouble( channel, "BadVal", 1, 1, this->badval, + "Output value for bad input positions" ); + } + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASelectorMap and astCheckSelectorMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SelectorMap,Mapping) +astMAKE_CHECK(SelectorMap) + +AstSelectorMap *astSelectorMap_( int nreg, void **regs_void, double badval, + const char *options, int *status, ...) { +/* +*+ +* Name: +* astSelectorMap + +* Purpose: +* Create a SelectorMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "selectormap.h" +* AstSelectorMap *astSelectorMap( int nreg, AstRegion **regs, +* double badval, const char *options, ... ) + +* Class Membership: +* SelectorMap constructor. + +* Description: +* This function creates a new SelectorMap and optionally initialises its +* attributes. + +* Parameters: +* nreg +* The number of Regions supplied. +* regs +* An array of pointers to the Regions. Deep copies of these +* Regions are taken. +* badval +* The value to be returned by the forward transformation of the +* SelectorMap for any input positions that have a bad (AST__BAD) +* value on any axis. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new SelectorMap. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new SelectorMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic SelectorMap constructor which is +* available via the protected interface to the SelectorMap class. A +* public interface is provided by the astSelectorMapId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "map1" and "map2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSelectorMap *new; /* Pointer to new SelectorMap */ + AstRegion **regs; /* Array of Region pointers */ + int i; /* Region index */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Report an error if no Regions have been supplied. */ + if( nreg <= 0 ) astError( AST__BDPAR, "astSelectorMap(SelectorMap): " + "Bad number of Regions (%d) specified.", status, nreg ); + +/* Otherwise create an array to hold the Region pointers. */ + regs = astMalloc( sizeof( AstRegion * )*nreg ); + +/* Obtain and validate pointers to the Region structures provided. */ + if( astOK ) { + for( i = 0; i < nreg; i++ ) { + regs[ i ] = astCheckRegion( regs_void[ i ] ); + } + } + + if ( astOK ) { + +/* Initialise the SelectorMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSelectorMap( NULL, sizeof( AstSelectorMap ), !class_init, &class_vtab, + "SelectorMap", nreg, regs, badval ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new SelectorMap's + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free memory used to hold the Regions pointers. */ + regs = astFree( regs ); + +/* Return a pointer to the new SelectorMap. */ + return new; +} + +AstSelectorMap *astSelectorMapId_( int nreg, void **regs_void, double badval, + const char *options, ... ) { +/* +*++ +* Name: +c astSelectorMap +f AST_SELECTORMAP + +* Purpose: +* Create a SelectorMap. + +* Type: +* Public function. + +* Synopsis: +c #include "selectormap.h" +c AstSelectorMap *astSelectorMap( int nreg, AstRegion *regs[], +c double badval, const char *options, ... ) +f RESULT = AST_SELECTORMAP( NREG, REGS, BADVAL, OPTIONS, STATUS ) + +* Class Membership: +* SelectorMap constructor. + +* Description: +* This function creates a new SelectorMap and optionally initialises +* its attributes. +* +* A SelectorMap is a Mapping that identifies which Region contains +* a given input position. +* +* A SelectorMap encapsulates a number of Regions that all have the same +* number of axes and represent the same coordinate Frame. The number of +* inputs (Nin attribute) of the SelectorMap equals the number of axes +* spanned by one of the encapsulated Region. All SelectorMaps have only +* a single output. SelectorMaps do not define an inverse transformation. +* +* For each input position, the forward transformation of a SelectorMap +* searches through the encapsulated Regions (in the order supplied when +* the SelectorMap was created) until a Region is found which contains +* the input position. The index associated with this Region is +* returned as the SelectorMap output value (the index value is the +* position of the Region within the list of Regions supplied when the +* SelectorMap was created, starting at 1 for the first Region). If an +* input position is not contained within any Region, a value of zero is +* returned by the forward transformation. +* +* If a compound Mapping contains a SelectorMap in series with its own +* inverse, the combination of the two adjacent SelectorMaps will be +* replaced by a UnitMap when the compound Mapping is simplified using +c astSimplify. +f AST_SIMPLIFY. +* +* In practice, SelectorMaps are often used in conjunction with SwitchMaps. + +* Parameters: +c nreg +f NREG = INTEGER (Given) +* The number of supplied Regions. +c regs +f REGS( NREG ) = INTEGER (Given) +* An array of pointers to the Regions. All the supplied Regions must +* relate to the same coordinate Frame. The number of axes in this +* coordinate Frame defines the number of inputs for the SelectorMap. +c badval +f BADVAL = DOUBLE PRECISION (Given) +* The value to be returned by the forward transformation of the +* SelectorMap for any input positions that have a bad (AST__BAD) +* value on any axis. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SelectorMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SelectorMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSelectorMap() +f AST_SELECTORMAP = INTEGER +* A pointer to the new SelectorMap. + +* Notes: +* - Deep copies are taken of the supplied Regions. This means that +* any subsequent changes made to the component Regions using the +* supplied pointers will have no effect on the SelectorMap. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astSelectorMap constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astSelectorMap_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "map1" and "map2" parameters +* are of type (void *) and are converted from an ID value to a +* pointer and validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astSelectorMap_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSelectorMap *new; /* Pointer to new SelectorMap */ + AstRegion **regs; /* Array of Region pointers */ + int i; /* Region index */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Report an error if no Regions have been supplied. */ + if( nreg <= 0 ) astError( AST__BDPAR, "astSelectorMap(SelectorMap): " + "Bad number of Regions (%d) specified.", status, nreg ); + +/* Create an array to hold the Region pointers. */ + regs = astMalloc( sizeof( AstRegion * )*nreg ); + +/* Obtain and validate pointers to the Region structures provided. */ + if( astOK ) { + for( i = 0; i < nreg; i++ ) { + regs[ i ] = astVerifyRegion( astMakePointer(regs_void[ i ]) ); + } + } + + if ( astOK ) { + +/* Initialise the SelectorMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSelectorMap( NULL, sizeof( AstSelectorMap ), !class_init, + &class_vtab, "SelectorMap", nreg, regs, + badval ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new SelectorMap's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free memory used to hold the Regions pointers. */ + regs = astFree( regs ); + +/* Return an ID value for the new SelectorMap. */ + return astMakeId( new ); +} + +AstSelectorMap *astInitSelectorMap_( void *mem, size_t size, int init, + AstSelectorMapVtab *vtab, const char *name, + int nreg, AstRegion **regs, double badval, int *status ) { +/* +*+ +* Name: +* astInitSelectorMap + +* Purpose: +* Initialise a SelectorMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "selectormap.h" +* AstSelectorMap *astInitSelectorMap( void *mem, size_t size, int init, +* AstSelectorMapVtab *vtab, const char *name, +* int nreg, AstRegion **regs, double badval ) + +* Class Membership: +* SelectorMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SelectorMap object. It allocates memory (if necessary) to +* accommodate the SelectorMap plus any additional data associated with the +* derived class. It then initialises a SelectorMap structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a SelectorMap at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SelectorMap is to be initialised. +* This must be of sufficient size to accommodate the SelectorMap data +* (sizeof(SelectorMap)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the SelectorMap (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* SelectorMap structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the SelectorMap's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SelectorMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* nreg +* The number of Regions supplied. +* regs +* An array holdiong pointers to the Regions. Deep copies are taken +* of these Regions. +* badval +* The value to be returned by the forward transformation of the +* SelectorMap for any input positions that have a bad (AST__BAD) +* value on any axis. + +* Returned Value: +* A pointer to the new SelectorMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstFrame *f0; /* Frame from first Region */ + AstFrame *f1; /* Frame from current Region */ + AstSelectorMap *new; /* Pointer to new SelectorMap */ + int equal; /* Are Frames equal? */ + int i; /* Loop count */ + int nin; /* No. input coordinates for SelectorMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSelectorMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check that all Regions refer to the same Frame. */ + f0 = astRegFrame( regs[ 0 ] ); + for( i = 1; i < nreg; i++ ) { + f1 = astRegFrame( regs[ i ] ); + equal = astEqual( f1, f0 ); + f1 = astAnnul( f1 ); + + if( !equal ) { + if( astOK ) { + astError( AST__BADNI, "astInitSelectorMap(%s): Region " + "number %d does not refer to the same coordinate " + "Frame as the first Region.", status, name, i + 1 ); + } + } + } + + nin = astGetNin( regs[ 0 ] ); + f0 = astAnnul( f0 ); + +/* Initialise a Mapping structure (the parent class) as the first component + within the SelectorMap structure, allocating memory if necessary. Specify + the number of input and output coordinates and in which directions the + Mapping should be defined. */ + if ( astOK ) { + new = (AstSelectorMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, 1, 1, 0 ); + if ( astOK ) { + +/* Initialise the SelectorMap data. */ +/* -------------------------------- */ + +/* Create an array for the Region pointers. */ + new->reg = astMalloc( sizeof( AstRegion * )*nreg ); + +/* Store pointers to deep copies of the Regions. */ + if( astOK ) { + new->nreg = nreg; + for( i = 0; i < nreg; i++ ) { + new->reg[ i ] = astCopy( regs[ i ] ); + } + } else { + new->nreg = 0; + } + +/* Store other items */ + new->badval = badval; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSelectorMap *astLoadSelectorMap_( void *mem, size_t size, + AstSelectorMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSelectorMap + +* Purpose: +* Load a SelectorMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "selectormap.h" +* AstSelectorMap *astLoadSelectorMap( void *mem, size_t size, +* AstSelectorMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SelectorMap loader. + +* Description: +* This function is provided to load a new SelectorMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SelectorMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a SelectorMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the SelectorMap is to be +* loaded. This must be of sufficient size to accommodate the +* SelectorMap data (sizeof(SelectorMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SelectorMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SelectorMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSelectorMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SelectorMap. If this is NULL, a pointer to +* the (static) virtual function table for the SelectorMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SelectorMap" is used instead. + +* Returned Value: +* A pointer to the new SelectorMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSelectorMap *new; + AstFrameSet *fs; + AstRegion *reg; + int i; + char buf[ 20 ]; + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SelectorMap. In this case the + SelectorMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSelectorMap ); + vtab = &class_vtab; + name = "SelectorMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSelectorMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SelectorMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SelectorMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + + +/* Loop to load each Region. */ +/* ------------------------- */ + new->reg = NULL; + i = 0; + fs = NULL; + while( astOK ) { + sprintf( buf, "reg%d", i + 1 ); + reg = astReadObject( channel, buf, NULL ); + if( reg ) { + new->reg = astGrow( new->reg, i + 1, sizeof( AstRegion *) ); + if( astOK ) { + new->reg[ i ] = reg; + +/* All but the first Region may have a dummy FrameSet rather than the + correct FrameSet. The correct FrameSet will be a copy of the FrameSet + from the first Region. */ + if( i == 0 ) { + fs = astGetRegFS( reg ); + } else if( astRegDummyFS( reg ) ){ + astSetRegFS( reg, fs ); + } + + i++; + } + } else { + break; + } + } + + fs = astAnnul( fs ); + +/* Number of Regions. */ + new->nreg = i; + + +/* BadVal. */ +/* ------- */ + new->badval = astReadDouble( channel, "badval", AST__BAD ); + +/* If an error occurred, clean up by deleting the new SelectorMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SelectorMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* None. */ + + + + diff --git a/selectormap.h b/selectormap.h new file mode 100644 index 0000000..36d83a5 --- /dev/null +++ b/selectormap.h @@ -0,0 +1,277 @@ +#if !defined( SELECTORMAP_INCLUDED ) /* Include this file only once */ +#define SELECTORMAP_INCLUDED +/* +*+ +* Name: +* selectormap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SelectorMap class. + +* Invocation: +* #include "selectormap.h" + +* Description: +* This include file defines the interface to the SelectorMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The SelectorMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astMapMerge +* Merge a SelectorMap within a sequence of Mappings. +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsASelectorMap +* Test class membership. +* astSelectorMap +* Create a SelectorMap. +* +* Protected: +* astCheckSelectorMap +* Validate class membership. +* astInitSelectorMap +* Initialise a SelectorMap. +* astInitSelectorMapVtab +* Initialise the virtual function table for the SelectorMap class. +* astLoadSelectorMap +* Load a SelectorMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstSelectorMap +* SelectorMap object type. +* +* Protected: +* AstSelectorMapVtab +* SelectorMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 13-MAR-2006 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "region.h" /* Coordinate Regions (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* SelectorMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstSelectorMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int nreg; /* The number of Regions in the SelectorMap */ + AstRegion **reg; /* Array of Region pointers */ + double badval; /* Output value for positions with bad axis values */ + +} AstSelectorMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSelectorMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +/* None. */ +} AstSelectorMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstSelectorMapGlobals { + AstSelectorMapVtab Class_Vtab; + int Class_Init; +} AstSelectorMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitSelectorMapGlobals_( AstSelectorMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SelectorMap) /* Check class membership */ +astPROTO_ISA(SelectorMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstSelectorMap *astSelectorMap_( int, void **, double, const char *, int *, ...); +#else +AstSelectorMap *astSelectorMapId_( int, void **, double, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSelectorMap *astInitSelectorMap_( void *, size_t, int, AstSelectorMapVtab *, + const char *, int, AstRegion **, double, int * ); + +/* Vtab initialiser. */ +void astInitSelectorMapVtab_( AstSelectorMapVtab *, const char *, int * ); + +/* Loader. */ +AstSelectorMap *astLoadSelectorMap_( void *, size_t, AstSelectorMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +/* None. */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSelectorMap(this) astINVOKE_CHECK(SelectorMap,this,0) +#define astVerifySelectorMap(this) astINVOKE_CHECK(SelectorMap,this,1) + +/* Test class membership. */ +#define astIsASelectorMap(this) astINVOKE_ISA(SelectorMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astSelectorMap astINVOKE(F,astSelectorMap_) +#else +#define astSelectorMap astINVOKE(F,astSelectorMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSelectorMap(mem,size,init,vtab,name,nreg,regs,badval) \ +astINVOKE(O,astInitSelectorMap_(mem,size,init,vtab,name,nreg,regs,badval,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSelectorMapVtab(vtab,name) astINVOKE(V,astInitSelectorMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSelectorMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSelectorMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckSelectorMap to validate SelectorMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +/* None. */ +#endif + + + + + diff --git a/series.pdf b/series.pdf new file mode 100644 index 0000000..71d8624 Binary files /dev/null and b/series.pdf differ diff --git a/shiftmap.c b/shiftmap.c new file mode 100644 index 0000000..eed6e1d --- /dev/null +++ b/shiftmap.c @@ -0,0 +1,1617 @@ +/* +*class++ +* Name: +* ShiftMap + +* Purpose: +* Add a constant value to each coordinate. + +* Constructor Function: +c astShiftMap +f AST_SHIFTMAP + +* Description: +* A ShiftMap is a linear Mapping which shifts each axis by a +* specified constant value. + +* Inheritance: +* The ShiftMap class inherits from the Mapping class. + +* Attributes: +* The ShiftMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The ShiftMap class does not define any new functions beyond those +f The ShiftMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) + +* History: +* 15-AUG-2003 (DSB): +* Original version. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 10-MAY-2006 (DSB): +* Override astEqual. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS ShiftMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "matrixmap.h" /* Linear mappings */ +#include "unitmap.h" /* Unit mappings */ +#include "zoommap.h" /* Zoom mappings */ +#include "permmap.h" /* Axis permutations */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "winmap.h" /* Window mappings */ +#include "shiftmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(ShiftMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(ShiftMap,Class_Init) +#define class_vtab astGLOBAL(ShiftMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstShiftMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstShiftMap *astShiftMapId_( int, const double [], const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ + +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int GetObjSize( AstObject *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); + +/* Member functions. */ +/* ================= */ + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two ShiftMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "shiftmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* ShiftMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two ShiftMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a ShiftMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the ShiftMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstShiftMap *that; + AstShiftMap *this; + int i; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two ShiftMap structures. */ + this = (AstShiftMap *) this_object; + that = (AstShiftMap *) that_object; + +/* Check the second object is a ShiftMap. We know the first is a + ShiftMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAShiftMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two ShiftMaps differ, it may still be possible + for them to be equivalent. First compare the ShiftMaps if their Invert + flags are the same. In this case all the attributes of the two ShiftMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + result = 1; + for( i = 0; i < nin; i++ ) { + if( !astEQUAL( this->shift[ i ], that->shift[ i ] ) ) { + result = 0; + break; + } + } + +/* If the Invert flags for the two ShiftMaps differ, the attributes of the two + ShiftMaps must be inversely related to each other. */ + } else { + + result = 1; + for( i = 0; i < nin; i++ ) { + if( !astEQUAL( this->shift[ i ], -(that->shift[ i ] ) ) ) { + result = 0; + break; + } + } + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a ShiftMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* ShiftMap member function (over-rides the protected astGetIsLinear +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsLinear attribute for a +* Frame, which is always one. + +* Parameters: +* this +* Pointer to the ShiftMap. +* status +* Pointer to the inherited status variable. +*/ + return 1; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "shiftmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* ShiftMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied ShiftMap, +* in bytes. + +* Parameters: +* this +* Pointer to the ShiftMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstShiftMap *this; /* Pointer to ShiftMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the ShiftMap structure. */ + this = (AstShiftMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->shift ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +void astInitShiftMapVtab_( AstShiftMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitShiftMapVtab + +* Purpose: +* Initialise a virtual function table for a ShiftMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "shiftmap.h" +* void astInitShiftMapVtab( AstShiftMapVtab *vtab, const char *name ) + +* Class Membership: +* ShiftMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the ShiftMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAShiftMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + mapping->Rate = Rate; + mapping->MapSplit = MapSplit; + mapping->GetIsLinear = GetIsLinear; + +/* Declare the class dump, copy and delete functions.*/ + astSetDump( vtab, Dump, "ShiftMap", "Shift each coordinate axis" ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + astSetDelete( (AstObjectVtab *) vtab, Delete ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a ShiftMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* ShiftMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated ShiftMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated ShiftMap with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated ShiftMap which is to be merged with +* its neighbours. This should be a cloned copy of the ShiftMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* ShiftMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated ShiftMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstWinMap *w1; /* Pointer to replacement Mapping */ + AstShiftMap *sm; /* Pointer to this ShiftMap */ + double *aa; /* Pointer to shift terms for new WinMap */ + double *bb; /* Pointer to scale terms for new WinMap */ + int i; /* Axis count */ + int nin; /* Number of axes */ + int result; /* Returned value */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* A ShiftMap is equivalent to a WinMap with unit scaling. The policy on + simplifying a ShiftMap is to convert it to the equivalent WinMap and let + the WinMap class do the simplifying. Create the returned WinMap, initially + with undefined corners. */ + nin = astGetNin( this ); + w1 = astWinMap( nin, NULL, NULL, NULL, NULL, "", status ); + +/* If succesful, store the scale and shift terms in the WinMap. The scale + terms are unity. */ + if( astOK ){ + sm = (AstShiftMap *) this; + + bb = w1->b; + aa = w1->a; + for( i = 0; i < nin; i++ ) { + *(bb++) = 1.0; + *(aa++) = ( *invert_list )[ where ] ? -(sm->shift)[ i ] : (sm->shift)[ i ]; + } + +/* Replace the supplied ShiftMap with the new WinMap and reset the invert + flag. */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) w1; + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + } + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* ShiftMap. + +* Type: +* Private function. + +* Synopsis: +* #include "shiftmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* ShiftMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing ShiftMap. This is only possible if the specified inputs +* correspond to some subset of the ShiftMap outputs. That is, there +* must exist a subset of the ShiftMap outputs for which each output +* depends only on the selected ShiftMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied ShiftMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the ShiftMap to be split (the ShiftMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied ShiftMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied ShiftMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied ShiftMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstShiftMap *newsm; /* Pointer to returned ShiftMap */ + AstShiftMap *this; /* Pointer to ShiftMap structure */ + int *result; /* Pointer to returned array */ + int i; /* Loop count */ + int iin; /* Mapping input index */ + int mnin; /* No. of Mapping inputs */ + int ok; /* Are input indices OK? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the ShiftMap structure. */ + this = (AstShiftMap *) this_map; + +/* Allocate memory for the returned array and create a ShiftMap with the + required number of axes and initially unsorted shifts. */ + result = astMalloc( sizeof( int )*(size_t) nin ); + newsm = astShiftMap( nin, this->shift, "", status ); + *map = (AstMapping *) newsm; + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Store the required shifts in the new ShiftMap. At the same time check + that each axis is valid. */ + mnin = astGetNin( this ); + ok = 1; + for( i = 0; i < nin; i++ ) { + iin = in[ i ]; + if( iin >= 0 && iin < mnin ) { + (newsm->shift)[ i ] = (this->shift)[ iin ]; + result[ i ] = iin; + } else { + ok = 0; + break; + } + } + +/* If the "in" array contained any invalid values, free the returned + resources. */ + if( !ok ) { + result = astFree( result ); + *map = astAnnul( *map ); + +/* If the indices are good, invert the returned ShiftMap if the supplied + ShiftMap is inverted. */ + } else { + if( astGetInvert( this ) ) astInvert( *map ); + } + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "shiftmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* ShiftMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + + return ( ax1 == ax2 ) ? 1.0 : 0.0; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a ShiftMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "shiftmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* ShiftMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a ShiftMap and a set of points encapsulated in a +* PointSet and transforms the points so as to map them into the +* required window. + +* Parameters: +* this +* Pointer to the ShiftMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the ShiftMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstShiftMap *map; /* Pointer to ShiftMap to be applied */ + const char *class; /* Object class */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *axin; /* Pointer to next input axis value */ + double *axout; /* Pointer to next output axis value */ + double a; /* Shift for current axis */ + int coord; /* Loop counter for coordinates */ + int ncoord; /* Number of coordinates per point */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the ShiftMap. */ + map = (AstShiftMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + ncoord = astGetNcoord( in ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Report an error if the ShiftMap does not contain any shifts. */ + if( !map->shift && astOK ){ + class = astGetClass( this ); + astError( AST__BADSM, "astTransform(%s): The supplied %s does not " + "contain any shift information.", status, class, class ); + } + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if( astOK ){ + +/* Apply the mapping to each axis. */ + for( coord = 0; coord < ncoord; coord++ ){ + +/* Store pointers to the first input and output values on this axis. */ + axin = ptr_in[ coord ]; + axout = ptr_out[ coord ]; + +/* Get the value to add to each axis value. */ + a = (map->shift)[ coord ]; + +/* If the shift is bad store bad output values. */ + if( a == AST__BAD ){ + for( point = 0; point < npoint; point++ ) *(axout++) = AST__BAD; + +/* Otherwise, shift this axis, taking account of whether the mapping is + inverted or not. */ + } else { + if( !forward ) a = -a; + + for( point = 0; point < npoint; point++ ){ + if( *axin != AST__BAD ){ + *(axout++) = (*axin) + a; + } else { + *(axout++) = AST__BAD; + } + axin++; + } + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for ShiftMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for ShiftMap objects. + +* Parameters: +* objin +* Pointer to the ShiftMap to be copied. +* objout +* Pointer to the ShiftMap being constructed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstShiftMap *out; /* Pointer to output ShiftMap */ + AstShiftMap *in; /* Pointer to input ShiftMap */ + int ncoord; /* No. of axes for the mapping */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the input and output ShiftMaps. */ + in= (AstShiftMap *) objin; + out = (AstShiftMap *) objout; + +/* Get the number of coordinates mapped by the ShiftMap. */ + ncoord = astGetNin( in ); + +/* Allocate memory holding copies of the shifts defining the mapping. */ + out->shift = (double *) astStore( NULL, (void *) in->shift, + sizeof(double)*(size_t)ncoord ); + +/* If an error occurred, free any allocated memory. */ + if ( !astOK ) { + out->shift = (double *) astFree( (void *) out->shift ); + } + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for ShiftMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for ShiftMap objects. + +* Parameters: +* obj +* Pointer to the ShiftMap to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This destructor does nothing and exists only to maintain a +* one-to-one correspondence between destructors and copy +* constructors. +*/ + +/* Local Variables: */ + AstShiftMap *this; /* Pointer to ShiftMap */ + +/* Obtain a pointer to the ShiftMap structure. */ + this = (AstShiftMap *) obj; + +/* Free the memory holding the shifts. */ + this->shift = (double *) astFree( (void *) this->shift ); + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for ShiftMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the ShiftMap class to an output Channel. + +* Parameters: +* this +* Pointer to the ShiftMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 50 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstShiftMap *this; /* Pointer to the ShiftMap structure */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment string */ + int axis; /* Axis index */ + int ncoord; /* No. of axes for mapping */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the ShiftMap structure. */ + this = (AstShiftMap *) this_object; + +/* Get the number of coordinates to be mapped. */ + ncoord = astGetNin( this ); + +/* Write out values representing the instance variables for the + ShiftMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* The shifts. */ + for( axis = 0; axis < ncoord; axis++ ){ + (void) sprintf( buff, "Sft%d", axis + 1 ); + (void) sprintf( comment, "Shift for axis %d", axis + 1 ); + astWriteDouble( channel, buff, (this->shift)[ axis ] != 0.0, 0, + (this->shift)[ axis ], comment ); + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAShiftMap and astCheckShiftMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(ShiftMap,Mapping) +astMAKE_CHECK(ShiftMap) + +AstShiftMap *astShiftMap_( int ncoord, const double shift[], const char *options, int *status, ...) { +/* +*++ +* Name: +c astShiftMap +f AST_SHIFTMAP + +* Purpose: +* Create a ShiftMap. + +* Type: +* Public function. + +* Synopsis: +c #include "shiftmap.h" +c AstShiftMap *astShiftMap( int ncoord, const double shift[], +c const char *options, ... ) +f RESULT = AST_SHIFTMAP( NCOORD, SHIFT, OPTIONS, STATUS ) + +* Class Membership: +* ShiftMap constructor. + +* Description: +* This function creates a new ShiftMap and optionally initialises its +* attributes. +* +* A ShiftMap is a linear Mapping which shifts each axis by a +* specified constant value. + +* Parameters: +c ncoord +f NCOORD = INTEGER (Given) +* The number of coordinate values for each point to be +* transformed (i.e. the number of dimensions of the space in +* which the points will reside). The same number is applicable +* to both input and output points. +c shift +f SHIFT( NCOORD ) = DOUBLE PRECISION (Given) +* An array containing the values to be added on to the input +* coordinates in order to create the output coordinates. A separate +* value should be supplied for each coordinate. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new ShiftMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new ShiftMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astShiftMap() +f AST_SHIFTMAP = INTEGER +* A pointer to the new ShiftMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstShiftMap *new; /* Pointer to new ShiftMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the ShiftMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitShiftMap( NULL, sizeof( AstShiftMap ), !class_init, &class_vtab, + "ShiftMap", ncoord, shift ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new ShiftMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new ShiftMap. */ + return new; +} + +AstShiftMap *astShiftMapId_( int ncoord, const double shift[], + const char *options, ... ) { +/* +* Name: +* astShiftMapId_ + +* Purpose: +* Create a ShiftMap. + +* Type: +* Private function. + +* Synopsis: +* #include "shiftmap.h" +* AstShiftMap *astShiftMapId_( int ncoord, const double shift[], +* const char *options, ... ) + +* Class Membership: +* ShiftMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astShiftMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astShiftMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astShiftMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astShiftMap_. + +* Returned Value: +* The ID value associated with the new ShiftMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstShiftMap *new; /* Pointer to new ShiftMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the ShiftMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitShiftMap( NULL, sizeof( AstShiftMap ), !class_init, &class_vtab, + "ShiftMap", ncoord, shift ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new ShiftMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new ShiftMap. */ + return astMakeId( new ); +} + +AstShiftMap *astInitShiftMap_( void *mem, size_t size, int init, + AstShiftMapVtab *vtab, const char *name, + int ncoord, const double *shift, int *status ) { +/* +*+ +* Name: +* astInitShiftMap + +* Purpose: +* Initialise a ShiftMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "shiftmap.h" +* AstShiftMap *astInitShiftMap( void *mem, size_t size, int init, +* AstShiftMapVtab *vtab, const char *name, +* int ncoord, const double *shift ) + +* Class Membership: +* ShiftMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new ShiftMap object. It allocates memory (if necessary) to accommodate +* the ShiftMap plus any additional data associated with the derived class. +* It then initialises a ShiftMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a ShiftMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the ShiftMap is to be initialised. +* This must be of sufficient size to accommodate the ShiftMap data +* (sizeof(ShiftMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the ShiftMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the ShiftMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the ShiftMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new ShiftMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* ncoord +* The number of coordinate values per point. +* shift +* Pointer to an array of shifts, one for each coordinate. + +* Returned Value: +* A pointer to the new ShiftMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstShiftMap *new; /* Pointer to new ShiftMap */ + int axis; /* Axis index */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitShiftMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Mapping structure (the parent class) as the first component + within the ShiftMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstShiftMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + ncoord, ncoord, 1, 1 ); + + if ( astOK ) { + +/* Initialise the ShiftMap data. */ +/* ---------------------------- */ +/* Allocate memory to hold the shift for each axis. */ + new->shift = (double *) astMalloc( sizeof(double)*(size_t)ncoord ); + +/* Check the pointers can be used */ + if( astOK ){ + +/* Store the shift and scale for each axis. */ + for( axis = 0; axis < ncoord; axis++ ){ + (new->shift)[ axis ] = shift ? shift[ axis ] : AST__BAD; + } + + } + +/* If an error occurred, clean up by deleting the new ShiftMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new ShiftMap. */ + return new; +} + +AstShiftMap *astLoadShiftMap_( void *mem, size_t size, + AstShiftMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadShiftMap + +* Purpose: +* Load a ShiftMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "shiftmap.h" +* AstShiftMap *astLoadShiftMap( void *mem, size_t size, +* AstShiftMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* ShiftMap loader. + +* Description: +* This function is provided to load a new ShiftMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* ShiftMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a ShiftMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the ShiftMap is to be +* loaded. This must be of sufficient size to accommodate the +* ShiftMap data (sizeof(ShiftMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the ShiftMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the ShiftMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstShiftMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new ShiftMap. If this is NULL, a pointer +* to the (static) virtual function table for the ShiftMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "ShiftMap" is used instead. + +* Returned Value: +* A pointer to the new ShiftMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstShiftMap *new; /* Pointer to the new ShiftMap */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int axis; /* Axis index */ + int ncoord; /* The number of coordinate axes */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this ShiftMap. In this case the + ShiftMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstShiftMap ); + vtab = &class_vtab; + name = "ShiftMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitShiftMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built ShiftMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Get the number of axis for the mapping. */ + ncoord = astGetNin( (AstMapping *) new ); + +/* Allocate memory to hold the shifts. */ + new->shift = (double *) astMalloc( sizeof(double)*(size_t)ncoord ); + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "ShiftMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* The shifts. */ + for( axis = 0; axis < ncoord; axis++ ){ + (void) sprintf( buff, "sft%d", axis + 1 ); + (new->shift)[ axis ] = astReadDouble( channel, buff, 0.0 ); + } + } + +/* If an error occurred, clean up by deleting the new ShiftMap. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the new ShiftMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + diff --git a/shiftmap.h b/shiftmap.h new file mode 100644 index 0000000..a9b1346 --- /dev/null +++ b/shiftmap.h @@ -0,0 +1,290 @@ +#if !defined( SHIFTMAP_INCLUDED ) /* Include this file only once */ +#define SHIFTMAP_INCLUDED +/* +*+ +* Name: +* shiftmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the ShiftMap class. + +* Invocation: +* #include "shiftmap.h" + +* Description: +* This include file defines the interface to the ShiftMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The ShiftMap class implements Mappings which shift each coordinate +* by a fixed amount. + +* Inheritance: +* The ShiftMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* ClearAttrib +* Clear an attribute value for a ShiftMap. +* GetAttrib +* Get an attribute value for a ShiftMap. +* SetAttrib +* Set an attribute value for a ShiftMap. +* TestAttrib +* Test if an attribute value has been set for a ShiftMap. +* astMapMerge +* Simplify a sequence of Mappings containing a ShiftMap. +* astTransform +* Apply a ShiftMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsAShiftMap +* Test class membership. +* astShiftMap +* Create a ShiftMap. +* +* Protected: +* astCheckShiftMap +* Validate class membership. +* astInitShiftMap +* Initialise a ShiftMap. +* astInitShiftMapVtab +* Initialise the virtual function table for the ShiftMap class. +* astLoadShiftMap +* Load a ShiftMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstShiftMap +* ShiftMap object type. +* +* Protected: +* AstShiftMapVtab +* ShiftMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 14-AUG-2003 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* ShiftMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstShiftMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *shift; /* Pointer to array of shifts */ + +} AstShiftMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstShiftMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + +} AstShiftMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstShiftMapGlobals { + AstShiftMapVtab Class_Vtab; + int Class_Init; +} AstShiftMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitShiftMapGlobals_( AstShiftMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(ShiftMap) /* Check class membership */ +astPROTO_ISA(ShiftMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstShiftMap *astShiftMap_( int, const double [], const char *, int *, ...); +#else +AstShiftMap *astShiftMapId_( int, const double [], const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstShiftMap *astInitShiftMap_( void *, size_t, int, AstShiftMapVtab *, + const char *, int, const double *, int * ); + +/* Vtab initialiser. */ +void astInitShiftMapVtab_( AstShiftMapVtab *, const char *, int * ); + +/* Loader. */ +AstShiftMap *astLoadShiftMap_( void *, size_t, AstShiftMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckShiftMap(this) astINVOKE_CHECK(ShiftMap,this,0) +#define astVerifyShiftMap(this) astINVOKE_CHECK(ShiftMap,this,1) + +/* Test class membership. */ +#define astIsAShiftMap(this) astINVOKE_ISA(ShiftMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astShiftMap astINVOKE(F,astShiftMap_) +#else +#define astShiftMap astINVOKE(F,astShiftMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define \ +astInitShiftMap(mem,size,init,vtab,name,ncoord,shift) \ +astINVOKE(O,astInitShiftMap_(mem,size,init,vtab,name,ncoord,shift,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitShiftMapVtab(vtab,name) astINVOKE(V,astInitShiftMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadShiftMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadShiftMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckShiftMap to validate ShiftMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#endif + +#endif + + + + + diff --git a/simpexamp.pdf b/simpexamp.pdf new file mode 100644 index 0000000..b65f58e Binary files /dev/null and b/simpexamp.pdf differ diff --git a/skyaxis.c b/skyaxis.c new file mode 100644 index 0000000..bccd8d2 --- /dev/null +++ b/skyaxis.c @@ -0,0 +1,4962 @@ +/* +*class++ +* Name: +* SkyAxis + +* Purpose: +* Store celestial axis information. + +* Constructor Function: +* None. + +* Description: +* The SkyAxis class is used to store information associated with a +* particular axis of a SkyFrame. It is used internally by the AST +* library and has no constructor function. You should encounter it +c only within textual output (e.g. from astWrite). +f only within textual output (e.g. from AST_WRITE). + +* Inheritance: +* The SkyAxis class inherits from the Axis class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2008 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: B.S. Berry (Starlink) + +* History: +* 1-MAR-1996 (RFWS): +* Original version. +* 19-APR-1996 (RFWS): +* Tidied up, etc. +* 8-MAY-1996 (RFWS): +* Remove leading minus sign from formatted HMS string if all +* fields are zero. +* 9-MAY-1996 (RFWS): +* Fixed bug in rounding of fractional parts of HMS strings and +* improved algorithm to cope gracefully with requests for +* excessive numbers of decimal places. +* 13-MAY-1996 (RFWS): +* Over-ride the astGetAxisDirection method so that a SkyAxis +* with the AsTime attribute set is displayed in reverse by +* default. +* 17-MAY-1996 (RFWS): +* Change AxisNorm to return a bad coordinate value if a bad +* value is given. +* 11-SEP-1996 (RFWS): +* Added AxisGap and DHmsGap (written by DSB). +* 26-FEB-1998 (RFWS): +* Over-ride the astAxisUnformat method. +* 6-MAR-1998 (RFWS): +* Add formatting options to omit degrees/hours field and change +* all affected functions. +* 10-AUG-2000 (DSB): +* Fixed bug in DHmsFormat which could cause (for instance) a formatted +* galactic longitude value of zero to be formated as "-0.-0". +* 29-AUG-2001 (DSB): +* Added AxisDistance and AxisOffset. +* 10-OCT-2002 (DSB): +* Over-ride the astGetAxisTop and astGetAxisBottom methods so that a +* SkyAxis with the IsLatitude attribute set is legal between plus +* and minus 90 degrees. +* 8-JAN-2003 (DSB): +* - Changed private InitVtab method to protected astInitSkyAxisVtab +* method. +* - Modify DHmsGap to avoid decimal gap "4" which produces things +* like "0.0 0.4 0.8 1.2 1.6 2.0" ("4" replaced by "5"). +* 24-JAN-2004 (DSB): +* o Added AxisFields. +* o Added 'g' format character which produces graphical separators. +* o Modified AxisAbbrev to use AxisFields so that delimiters which +* include digits can be recognised. +* 13-SEP-20904 (DSB): +* Modify AxisFields to correct usage of the "p" pointer in the +* case that the first and only field begins with a minus sign. +* 15-SEP-2004 (DSB): +* Modified ParseDHmsFormat so that the number of decimal places +* is specified by Digits if the given format string include a ".*" +* precision (e.g. "dms.*"). +* 18-MAR-2005 (DSB): +* Invoke methods inherited from parent Axis class if the format +* string starts with a '%' character. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 30-JUN-2006 (DSB): +* Guard against a null "str1" value in AxisAbbrev. +* 7-AUG-2007 (DSB): +* Added CentreZero attribute. +* 1-FEB-2008 (DSB): +* Modified AxisUnformat to allow the final numerical field to include +* an exponent. +* 13-OCT-2011 (DSB): +* Use tuning parameters to store graphical delimiters. +* 27-APR-2015 (DSB): +* Added InternalUNit attribute.. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to the header + files that define class interfaces that they should make "protected" + symbols available. */ +#define astCLASS SkyAxis + +/* Header files. */ +/* ============= */ +#include "ast_err.h" /* Error code definitions */ + +/* Interface definitions. */ +/* ---------------------- */ +#include "pal.h" /* SLALIB interface */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "pointset.h" /* Sets of points (for AST__BAD) */ +#include "axis.h" /* Axis (parent) class interface */ +#include "skyaxis.h" /* Interface definition for this class */ +#include "globals.h" /* Thread-safe global data access */ +#include "wcsmap.h" /* For constants */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getaxislabel)( AstAxis *, int * ); +static const char *(* parent_getaxissymbol)( AstAxis *, int * ); +static const char *(* parent_getaxisunit)( AstAxis *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static int (*parent_getaxisdirection)( AstAxis *this, int * ); +static void (* parent_axisoverlay)( AstAxis *, AstAxis *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static double (*parent_getaxisbottom)( AstAxis *this, int * ); +static double (*parent_getaxistop)( AstAxis *this, int * ); +static const char *(* parent_axisformat)( AstAxis *, double, int * ); +static double (*parent_axisgap)( AstAxis *, double, int *, int * ); +static int (*parent_axisunformat)( AstAxis *, const char *, double *, int * ); +static int (*parent_axisfields)( AstAxis *, const char *, const char *, int, char **, int *, double *, int * ); + +/* Factors for converting between hours, degrees and radians. */ +static double hr2rad; +static double deg2rad; +static double pi; +static double piby2; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->DHmsFormat_Buff[ 0 ] = 0; \ + globals->DHmsUnit_Buff[ 0 ] = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetAxisFormat_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SkyAxis) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SkyAxis,Class_Init) +#define class_vtab astGLOBAL(SkyAxis,Class_Vtab) +#define dhmsformat_buff astGLOBAL(SkyAxis,DHmsFormat_Buff) +#define dhmsunit_buff astGLOBAL(SkyAxis,DHmsUnit_Buff) +#define getattrib_buff astGLOBAL(SkyAxis,GetAttrib_Buff) +#define getaxisformat_buff astGLOBAL(SkyAxis,GetAxisFormat_Buff) + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char dhmsformat_buff[ AST__SKYAXIS_DHMSFORMAT_BUFF_LEN + 1 ]; +static char dhmsunit_buff[ AST__SKYAXIS_DHMSUNIT_BUFF_LEN + 1 ]; +static char getattrib_buff[ AST__SKYAXIS_GETATTRIB_BUFF_LEN + 1 ]; +static char getaxisformat_buff[ AST__SKYAXIS_GETAXISFORMAT_BUFF_LEN + 1 ]; + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSkyAxisVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#endif + + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSkyAxis *astSkyAxisId_( const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static const char *AxisAbbrev( AstAxis *, const char *, const char *, const char *, int * ); +static const char *AxisFormat( AstAxis *, double, int * ); +static int GetObjSize( AstObject *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetAxisFormat( AstAxis *, int * ); +static const char *GetAxisInternalUnit( AstAxis *, int * ); +static const char *GetAxisLabel( AstAxis *, int * ); +static const char *GetAxisSymbol( AstAxis *, int * ); +static const char *GetAxisUnit( AstAxis *, int * ); +static const char *DHmsFormat( const char *, int, double, int * ); +static const char *DHmsUnit( const char *, int, int, int * ); +static double AxisGap( AstAxis *, double, int *, int * ); +static double AxisDistance( AstAxis *, double, double, int * ); +static double AxisOffset( AstAxis *, double, double, int * ); +static double DHmsGap( const char *, int, double, int *, int * ); +static double GetAxisTop( AstAxis *, int * ); +static double GetAxisBottom( AstAxis *, int * ); +static int AxisIn( AstAxis *, double, double, double, int, int * ); +static int AxisFields( AstAxis *, const char *, const char *, int, char **, int *, double *, int * ); +static int AxisUnformat( AstAxis *, const char *, double *, int * ); +static int GetAxisAsTime( AstSkyAxis *, int * ); +static int GetAxisDirection( AstAxis *, int * ); +static int GetAxisIsLatitude( AstSkyAxis *, int * ); +static int GetAxisCentreZero( AstSkyAxis *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestAxisAsTime( AstSkyAxis *, int * ); +static int TestAxisFormat( AstAxis *, int * ); +static int TestAxisInternalUnit( AstAxis *, int * ); +static int TestAxisIsLatitude( AstSkyAxis *, int * ); +static int TestAxisCentreZero( AstSkyAxis *, int * ); +static void AxisNorm( AstAxis *, double *, int * ); +static void AxisOverlay( AstAxis *, AstAxis *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearAxisAsTime( AstSkyAxis *, int * ); +static void ClearAxisFormat( AstAxis *, int * ); +static void ClearAxisIsLatitude( AstSkyAxis *, int * ); +static void ClearAxisCentreZero( AstSkyAxis *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void ParseDHmsFormat( const char *, int, char *, int *, int *, int *, int *, int *, int *, int *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetAxisAsTime( AstSkyAxis *, int, int * ); +static void SetAxisFormat( AstAxis *, const char *, int * ); +static void SetAxisIsLatitude( AstSkyAxis *, int, int * ); +static void SetAxisCentreZero( AstSkyAxis *, int, int * ); + +/* Member functions. */ +/* ================= */ +static const char *AxisAbbrev( AstAxis *this_axis, const char *fmt, + const char *str1, const char *str2, int *status ) { +/* +* Name: +* AxisAbbrev + +* Purpose: +* Abbreviate a formatted SkyAxis value by skipping leading fields. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *AxisAbbrev( AstAxis *this, const char *fmt, +* const char *str1, const char *str2 ) + +* Class Membership: +* SkyAxis member function (over-rides the protected astAxisAbbrev +* method inherited from the Axis class). + +* Description: +* This function compares two SkyAxis values that have been +* formatted with the supplied format specifier (using astAxisFormat) +* and determines if they have any redundant leading fields (i.e. +* leading fields in common which can be suppressed when tabulating +* the values or plotting them on the axis of a graph). + +* Parameters: +* this +* Pointer to the SkyAxis. +* fmt +* Pointer to a constant null-terminated string containing the +* format specifier used to format the two values. +* str1 +* Pointer to a constant null-terminated string containing the +* first formatted value. If this is null, the returned pointer +* points to the start of the final field in str2. +* str2 +* Pointer to a constant null-terminated string containing the +* second formatted value. + +* Returned Value: +* A pointer into the "str2" string which locates the first +* character in the first field that differs between the two +* formatted values. +* +* If the two values have no leading fields in common, the returned +* value will point at the start of string "str2". If the two +* values are equal, it will point at the terminating null at the +* end of this string. + +* Notes: +* - A pointer to the start of "str2" will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +*/ + +/* Local Variables: */ + char *fld1[ 3 ]; /* Pointers to start of each field in str1 */ + char *fld2[ 3 ]; /* Pointers to start of each field in str2 */ + const char *result; /* Result pointer to return */ + int i; /* Loop counter for string fields */ + int nf1; /* Number of fields found in str1 */ + int nf2; /* Number of fields found in str2 */ + int nc1[ 3 ]; /* Length of each field in str1 */ + int nc2[ 3 ]; /* Length of each field in str2 */ + +/* Initialise. */ + result = str2; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Find the fields within the "str2" string. */ + nf2 = astAxisFields( this_axis, fmt, str2, 3, fld2, nc2, NULL ); + +/* If "str1" was not supplied, return a pointer to the final field in + "str2". */ + if( !str1 ) { + result = fld2[ nf2 - 1 ]; + +/* Otherwise, find the fields within the "str1" string. */ + } else { + nf1 = astAxisFields( this_axis, fmt, str1, 3, fld1, nc1, NULL ); + +/* Loop to inspect corresponding fields from each string. */ + for ( i = 0; i < nf1 && i < nf2; i++ ) { + +/* If the fields are different, break out of the loop. */ + if ( nc1[ i ] != nc2[ i ] || + strncmp( fld1[ i ], fld2[ i ], nc1[ i ] ) ) { + break; + +/* Otherwise, move the returned poitner on to point to the start of the + next field in str2. If we are already at the last field in str2, + return a pointer to the terminating null. */ + } else { + if ( i + 1 < nf2 ) { + result = fld2[ i + 1 ]; + } else { + result = strchr( str2, '\0' ); + } + } + } + } + +/* Return the result. */ + return result; +} + +static double AxisDistance( AstAxis *this_axis, double v1, double v2, int *status ) { +/* +* Name: +* AxisDistance + +* Purpose: +* Find the distance between two axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* AxisDistance( AstAxis *this, double v1, double v2 ) + +* Class Membership: +* SkyAxis member function (over-rides the protected astAxisDistance +* method inherited from the Axis class). + +* Description: +* This function returns a signed value representing the axis increment +* from axis value v1 to axis value v2. +* +* For a SkyAxis, the angular difference between the two supplied axis +* values is normalized into the range +PI to -PI. + +* Parameters: +* this +* Pointer to the SkyAxis. +* v1 +* The first axis value +* v2 +* The second axis value + +* Returned Value: +* The axis increment from v1 to v2. + +* Notes: +* - A value of AST__BAD is returned if either axis value is AST__BAD. +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + double result; /* Returned gap size */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check both axis values are OK, and form the returned increment, + normalizing it into the range +PI to -PI. */ + if( v1 != AST__BAD && v2 != AST__BAD ) result = palDrange( v2 - v1 ); + +/* Return the result. */ + return result; +} + +static int AxisFields( AstAxis *this_axis, const char *fmt, const char *str, + int maxfld, char **fields, int *nc, double *val, int *status ) { +/* +* Name: +* AxisFields + +* Purpose: +* Identify numerical fields within a formatted SkyAxis value. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* int AxisFields( AstAxis *this_axis, const char *fmt, const char *str, +* int maxfld, char **fields, int *nc, double *val ) + +* Class Membership: +* SkyAxis member function (over-rides the protected astAxisFields +* method inherited from the Axis class). + +* Description: +* This function identifies the numerical fields within a SkyAxis value +* that have been formatted using astAxisFormat. It assumes that the +* value was formatted using the supplied format string. It also +* returns the equivalent floating point value in radians. + +* Parameters: +* this +* Pointer to the SkyAxis. +* fmt +* Pointer to a constant null-terminated string containing the +* format used when creating "str". +* str +* Pointer to a constant null-terminated string containing the +* formatted value. +* maxfld +* The maximum number of fields to identify within "str". +* fields +* A pointer to an array of at least "maxfld" character pointers. +* Each element is returned holding a pointer to the start of the +* corresponding field in "str" (in the order in which they occur +* within "str"), or NULL if no corresponding field can be found. +* nc +* A pointer to an array of at least "maxfld" integers. Each +* element is returned holding the number of characters in the +* corresponding field, or zero if no corresponding field can be +* found. +* val +* Pointer to a location at which to store the radians value +* equivalent to the returned field values. If this is NULL, +* it is ignored. + +* Returned Value: +* The number of fields succesfully identified and returned. + +* Notes: +* - Leading and trailing spaces are ignored. +* - If the formatted value is not consistent with the supplied format +* string, then a value of zero will be returned, "fields" will be +* returned holding NULLs, "nc" will be returned holding zeros, and +* "val" is returned holding VAL__BAD. +* - Fields are counted from the start of the formatted string. If the +* string contains more than "maxfld" fields, then trailing fields are +* ignored. +* - If this function is invoked with the global error status set, or +* if it should fail for any reason, then a value of zero will be returned +* as the function value, and "fields", "nc" and "val" will be returned +* holding their supplied values + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char sep; /* Field separator character */ + char tbuf[50]; /* Buffer for terminator string */ + char *p; /* Pointer to next character */ + char *t; /* Pointer to start of terminator string */ + char *term; /* Pointer to terminator string */ + double dval; /* Value read from string */ + double value; /* Equivalent radians value */ + int as_time; /* Format the value as a time? */ + int dh; /* Hours field required? */ + int ifld; /* Field index */ + int lead_zero; /* Add leading zeros? */ + int min; /* Minutes field required? */ + int ndp; /* Number of decimal places */ + int ok; /* Value and format consistent? */ + int plus; /* Add leading plus sign? */ + int result; /* Result fields count to return */ + int sec; /* Seconds field required? */ + int sign; /* The sign of the radians value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_axis); + +/* If the format string starts with a "%" call the method inherited from + the parent Axis class. */ + if( fmt[ 0 ] == '%' ) { + return (*parent_axisfields)( this_axis, fmt, str, maxfld, fields, nc, + val, status ); + } + +/* Initialise. */ + result = 0; + for( ifld = 0; ifld < maxfld; ifld++ ) { + fields[ ifld ] = NULL; + nc[ ifld ] = 0; + } + if( val ) *val = AST__BAD; + +/* Parse the format specifier. */ + ParseDHmsFormat( fmt, astGetAxisDigits( this_axis ), &sep, &plus, &lead_zero, + &as_time, &dh, &min, &sec, &ndp, status ); + +/* Only proceed if the format was parsed succesfully, and the supplied arrays + are not of zero size. */ + if( astOK && maxfld > 0 ) { + +/* Indicate that we have not yet found any inconsistency between the + formatted value and the forat string. */ + ok = 1; + +/* Variable "p" points to the next character to be read from the + formatted string. Initialise it to point to the first non-space + character. */ + p = (char *) str; + while( *p == ' ' ) p++; + +/* Note the start of the first field. */ + fields[ 0 ] = p; + +/* If the first non-blank character is a + or - sign, skip it and note + the sign of the final value. */ + sign = 1; + if( *p == '-' ) { + sign = -1; + p++; + } else if( *p == '+' ) { + p++; + } + +/* Initialise the equivalent radian value. */ + value = 0.0; + +/* If the format string specifies a degrees or hours field, it should be + the first field. */ + if( dh ) { + +/* If the format indicates that fields are separated by characters, or if + there is a minutes or seconds field, then the first field should end with + the appropriate separator. In these cases locate the terminator,and + store the length of the first field. */ + if( sep == 'l' || sep == 'g' || min || sec ) { + + if( sep == 'l' ) { + term = as_time ? "h" : "d"; + + } else if( sep == 'g' ) { + astTuneC( as_time ? "hrdel":"dgdel", NULL, tbuf, + sizeof( tbuf ) ); + term = tbuf; + + } else { + tbuf[ 0 ] = sep; + tbuf[ 1 ] = '\0'; + term = tbuf; + } + + t = strstr( p, term ); + if( t ) { + nc[ 0 ] = t - fields[ 0 ]; + } else { + ok = 0; + } + +/* Move on to the first character following the terminator. */ + p = t + strlen( term ); + +/* In all other cases, the first field is the only field and is not + terminated. Note its length (ignoring trailing spaces). Move the + pointer on by the length of the field, remembering that any leading + minus sign has already been skipped. */ + } else { + nc[ 0 ] = astChrLen( fields[ 0 ] ); + p += nc[ result ]; + if( sign == -1 ) p--; + } + +/* Read a numerical value from the first field. */ + if( astSscanf( fields[ 0 ], "%lg", &dval ) ) { + value = fabs( dval ); + } else { + ok = 0; + } + +/* Increment then number of returned fields if OK */ + if( ok ) result++; + + } + +/* If the format string specifies a minutes field, it should be the next + field. */ + if( min && ok ) { + +/* Note the start of the next field. */ + fields[ result ] = p; + +/* If the format indicates that fields are separated by characters, or if + there is a seconds field, then this field should end with the appropriate + separator. In these cases locate the terminator,and store the length of + this field. */ + if( sep == 'l' || sep == 'g' || sec ) { + if( sep == 'l' ) { + term = "m"; + + } else if( sep == 'g' ) { + astTuneC( as_time ? "mndel":"amdel", NULL, tbuf, + sizeof( tbuf ) ); + term = tbuf; + + } else { + tbuf[ 0 ] = sep; + tbuf[ 1 ] = '\0'; + term = tbuf; + } + + t = strstr( p, term ); + if( t ) { + nc[ result ] = t - fields[ result ]; + } else { + ok = 0; + } + +/* Move on to the first character following the terminator. */ + p = t + strlen( term ); + +/* In all other cases, this field is not terminated. Note its length + (ignoring trailing spaces). */ + } else { + nc[ result ] = astChrLen( fields[ result ] ); + p += nc[ result ]; + } + +/* Read a numerical value from this field. */ + if( astSscanf( fields[ result ], "%lg", &dval ) ) { + value += dval/60.0; + } else { + ok = 0; + } + +/* Increment then number of returned fields if OK */ + if( ok ) result++; + + } + +/* If the format string specifies a seconds field, it should be the next + field. */ + if( sec && ok ) { + +/* Note the start of the next field. */ + fields[ result ] = p; + +/* If the format indicates that fields are separated by characters, then this + field should end with the appropriate separator. In this case locate the + terminator,and store the length of this field. */ + if( sep == 'l' || sep == 'g' ) { + if( sep == 'l' ) { + term = "s"; + } else { + astTuneC( as_time ? "scdel":"asdel", NULL, tbuf, + sizeof( tbuf ) ); + term = tbuf; + } + + t = strstr( p, term ); + if( t ) { + nc[ result ] = t - fields[ result ]; + } else { + ok = 0; + } + +/* Move on to the first character following the terminator. */ + p = t + strlen( term ); + +/* In all other cases, this field is not terminated. Note its length + (ignoring trailing spaces). */ + } else { + nc[ result ] = astChrLen( fields[ result ] ); + p += nc[ result ]; + } + +/* Read a numerical value from this field. */ + if( astSscanf( fields[ result ], "%lg", &dval ) ) { + value += dval/3600.0; + } else { + ok = 0; + } + +/* Increment then number of returned fields if OK */ + if( ok ) result++; + + } + +/* Check that nothing is left.*/ + if( astChrLen( p ) > 0 ) ok = 0; + +/* If OK, convert the axis value to radians. */ + if( ok ) { + if( val ) { + *val = sign*value*( as_time ? hr2rad : deg2rad ); + } + +/* Otherwise, return zero. */ + } else { + result = 0; + for( ifld = 0; ifld < maxfld; ifld++ ) { + fields[ ifld ] = NULL; + nc[ ifld ] = 0; + } + } + } + +/* Return the result. */ + return result; +} + +static const char *AxisFormat( AstAxis *this_axis, double value, int *status ) { +/* +* Name: +* AxisFormat + +* Purpose: +* Format a coordinate value for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *AxisFormat( AstAxis *this, double value, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astAxisFormat method inherited +* from the Axis class). + +* Description: +* This function returns a pointer to a string containing the formatted +* (character) version of a coordinate value for a SkyAxis. The formatting +* applied is that specified by a previous invocation of the +* astSetAxisFormat method. A suitable default format is applied if +* necessary. + +* Parameters: +* this +* Pointer to the SkyAxis. +* value +* The coordinate value to be formatted (in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted value. + +* Notes: +* - The returned string pointer may point at memory allocated within +* the SkyAxis object, or at static memory. The contents of the string may +* be over-written or the pointer may become invalid following a further +* invocation of the same function or deletion of the SkyAxis. A copy of the +* string should therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *fmt; /* Pointer to format specifier */ + const char *result; /* Pointer to result string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Obtain a pointer to the format specifier to be used. Note we use a private + member function to obtain this (not a method) in case derived classes have + extended the syntax of this string. */ + fmt = GetAxisFormat( this_axis, status ); + +/* If the format string starts with a percent, use the AxisFormat method + inherited from the parent Axis class. Otherwise, format using the + syntax of this class. */ + if ( astOK ) { + if( fmt[ 0 ] == '%' ) { + result = (*parent_axisformat)( this_axis, value, status ); + } else { + result = DHmsFormat( fmt, astGetAxisDigits( this ), value, status ); + } + } + +/* Return the result. */ + return result; +} + +static double AxisGap( AstAxis *this_axis, double gap, int *ntick, int *status ) { +/* +* Name: +* AxisGap + +* Purpose: +* Find a "nice" gap for tabulating SkyAxis values. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* double AxisGap( AstAxis *this, double gap, int *ntick, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the protected astAxisGap +* method inherited from the Axis class). + +* Description: +* This function returns a gap size in radians which produces a +* nicely spaced series of formatted SkyAxis values, the returned +* gap size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the SkyAxis. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - The returned gap size is influenced by the format string +* specified for the SkyAxis by a previous invocation of the +* astSetAxisFormat method. A suitable default format is used if +* necessary. +* - A value of zero is returned if the supplied gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *fmt; /* Pointer to Format string */ + double result; /* Returned gap size */ + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Obtain a pointer to the format string to be used. Note we use a + private member function to obtain this (not a method) in case + derived classes have extended the syntax of this string. */ + fmt = GetAxisFormat( this_axis, status ); + +/* Obtain the closest "nice" gap size. */ + if ( astOK ) result = DHmsGap( fmt, astGetAxisDigits( this ), gap, ntick, status ); + +/* If the format string starts with a percent, use the AxisGap method + inherited from the parent Axis class. Otherwise, use the method + provided by this class. */ + if ( astOK ) { + if( fmt[ 0 ] == '%' ) { + result = (*parent_axisgap)( this_axis, gap, ntick, status ); + } else { + result = DHmsGap( fmt, astGetAxisDigits( this ), gap, ntick, status ); + } + } + +/* Return the result. */ + return result; +} + +static int AxisIn( AstAxis *this, double lo, double hi, double val, int closed, int *status ){ +/* +* Name: +* AxisIn + +* Purpose: +* Test if an axis value lies within a given interval. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "skyaxis.h" +* int AxisIn( AstAxis *this, double lo, double hi, double val, int closed, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astAxisIn method inherited +* from the Axis class). + +* Description: +* This function returns non-zero if a given axis values lies within a +* given axis interval. +* +* The SkyAxis implementation of this method treats the supplied +* numerical values as non-cyclic (e.g. lo=10, hi = 350 implies that +* val = 180 is inside and zero is outside: lo = 10, hi = 400 would imply +* that all angles are inside: lo = -10, hi = 10 would imply that 180 is +* outside and zero is inside). But when testing a supplied value, adding +* or subtracting multiples of 2.PI from the supplied value will make no +* difference to whether the point is inside or outside). + +* Parameters: +* this +* Pointer to the Axis. +* lo +* The lower axis limit of the interval. +* hi +* The upper axis limit of the interval. +* val +* The axis value to be tested. +* closed +* If non-zero, then the lo and hi axis values are themselves +* considered to be within the interval. Otherwise they are outside. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the test value is inside the interval. + +*/ + +/* For speed, omit the astOK check since no pointers are being used. */ + +/* Deal with closed intervals. */ + if( closed ) { + +/* If the supplied value is greater than the upper limit, subtract 2.PI until + it is not. */ + while( val > hi ) val -= 2*pi; + +/* If the value is now less than the lower limit, add 2.PI until it is not. */ + while( val < lo ) val += 2*pi; + +/* The axis value is in the range if its numerical value is less than or + equal to the end value. */ + return ( val <= hi ); + +/* Now deal with open intervals. */ + } else { + +/* If the supplied value is greater than or equal to the upper limit, subtract + 2.PI until it is not. */ + while( val >= hi ) val -= 2*pi; + +/* If the value is now less than or equal to the lower limit, add 2.PI until + it is not. */ + while( val <= lo ) val += 2*pi; + +/* The axis value is in the range if its numerical value is less than the + end value. */ + return ( val < hi ); + } +} + +static void AxisNorm( AstAxis *this_axis, double *value, int *status ) { +/* +* Name: +* AxisNorm + +* Purpose: +* Normalise a SkyAxis coordinate value. + +* Type: +* Public virtual function. + +* Synopsis: +* #include "skyaxis.h" +* void AxisNorm( AstAxis *this, double *value, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astAxisNorm method inherited +* from the Axis class). + +* Description: +* This function converts a SkyAxis coordinate value which might +* potentially be unsuitable for display to a user (for instance, +* may lie outside the expected range of values) into an acceptable +* alternative value suitable for display. +* +* For a SkyAxis that is a longitude axis, values are wrapped into +* the range zero to 2*pi, while for a latitude axis, they are +* wrapped into the range -pi to +pi. The astAxisCentreZero method +* is used to determine which algorithm to apply. + +* Parameters: +* this +* Pointer to the SkyAxis. +* value +* Pointer to the coordinate value to be normalised, which will +* be modified in place. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + int centrezero; /* SkyAxis range centred on zero? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* If the coordinate value is bad, then return it unchanged. Otherwise, + determine if the SkyAxis range is centred on zero or PI. */ + if ( *value != AST__BAD ) { + centrezero = astGetAxisCentreZero( this ); + +/* Wrap the value into the appropriate range. */ + if ( astOK ) *value = centrezero ? palDrange( *value ) : + palDranrm( *value ); + } +} + +static double AxisOffset( AstAxis *this_axis, double v1, double dist, int *status ) { +/* +* +* Name: +* AxisOffset + +* Purpose: +* Add an increment onto a supplied axis value. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* AxisOffset( AstSkyAxis *this, double v1, double dist ) + +* Class Membership: +* SkyAxis member function (over-rides the protected astAxisOffset +* method inherited from the Axis class). + +* Description: +* This function returns an axis value formed by adding a signed axis +* increment onto a supplied axis value. +* +* For a SkyFrame, the result is normalized into the correct angular +* range (+PI to -PI for latitude axes, and 0 to 2*PI for longitude axes). + +* Parameters: +* this +* Pointer to the SkyAxis. +* v1 +* The supplied axis value +* dist +* The axis increment + +* Returned Value: +* The axis value which is the specified increment away from v1. + +* Notes: +* - A value of AST__BAD is returned if either axis value is AST__BAD. +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + double result; /* Returned gap size */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check both axis values are OK, and form the returned axis value. */ + if( v1 != AST__BAD && dist != AST__BAD ) { + result = v1 + dist; + AxisNorm( this_axis, &result, status ); + } + +/* Return the result. */ + return result; +} + +static void AxisOverlay( AstAxis *template_axis, AstAxis *result, int *status ) { +/* +* Name: +* AxisOverlay + +* Purpose: +* Overlay the attributes of a template SkyAxis on to another Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* void AxisOverlay( AstAxis *template, AstAxis *result, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astAxisOverlay method inherited +* from the Axis class). + +* Description: +* This function overlays attributes of a SkyAxis (the "template") on to +* another Axis, so as to over-ride selected attributes of that second +* Axis. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which an Axis acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. + +* Parameters: +* template +* Pointer to the template SkyAxis, for which values should have been +* explicitly set for any attribute which is to be transferred. +* result +* Pointer to the Axis which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void +*/ + +/* Local Variables: */ + AstSkyAxis *template; /* Pointer to the SkyAxis structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the template SkyAxis structure. */ + template = (AstSkyAxis *) template_axis; + +/* Invoke the parent astAstOverlay method to overlay inherited attributes. */ + (*parent_axisoverlay)( template_axis, result, status ); + +/* Test if the "result" Axis is a SkyAxis (if not, it cannot acquire any + further attributes, so there is nothing more to do). */ + if ( astIsASkyAxis( result ) && astOK ) { + +/* Overlay the Format attribute if it is set in the template. Note that we + use private member functions (not methods) to access the Format value, since + derived classes may extend the syntax of this string and we should not + overlay a string whose syntax cannot be interpreted by the result Axis. */ + if ( TestAxisFormat( template_axis, status ) ) { + SetAxisFormat( result, GetAxisFormat( template_axis, status ), status ); + } + +/* Overlay the AsTime attribute in the same way, but this time using methods + to access it. */ + if ( astTestAxisAsTime( template ) ) { + astSetAxisAsTime( result, astGetAxisAsTime( template ) ); + } + +/* Also overlay the IsLatitude attribute. */ + if ( astTestAxisIsLatitude( template ) ) { + astSetAxisIsLatitude( result, astGetAxisIsLatitude( template ) ); + } + +/* Also overlay the CentreZero attribute. */ + if ( astTestAxisCentreZero( template ) ) { + astSetAxisCentreZero( result, astGetAxisCentreZero( template ) ); + } + } +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astClearAttrib protected +* method inherited from the Axis class). + +* Description: +* This function clears the value of a specified attribute for a +* SkyAxis, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the SkyAxis. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* AsTime. */ +/* ------- */ + if ( !strcmp( attrib, "astime" ) ) { + astClearAxisAsTime( this ); + +/* IsLatitude. */ +/* ----------- */ + } else if ( !strcmp( attrib, "islatitude" ) ) { + astClearAxisIsLatitude( this ); + +/* CentreZero. */ +/* ----------- */ + } else if ( !strcmp( attrib, "centrezero" ) ) { + astClearAxisCentreZero( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearAxisFormat( AstAxis *this_axis, int *status ) { +/* +* Name: +* ClearAxisFormat + +* Purpose: +* Clear the Format attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* void ClearAxisFormat( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astClearAxisFormat method +* inherited from the Axis class). + +* Description: +* This function clears the Format attribute of a SkyAxis, as if no value +* had ever been set for it. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Free any memory allocated to hold the Format string and reset the string + pointer to NULL. */ + this->skyformat = astFree( this->skyformat ); +} + +static const char *DHmsFormat( const char *fmt, int digs, double value, int *status ) { +/* +* Name: +* DHmsFormat + +* Purpose: +* Format a value representing degrees/hours, minutes and seconds. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *DHmsFormat( const char *fmt, int digs, double value, int *status ) + +* Class Membership: +* SkyAxis member function. + +* Description: +* This function formats a value representing an angle in radians +* into a text string giving degrees/hours, minutes and seconds +* according to a format specifier supplied. See the "Format +* Specifier" section for details of the formats available. + +* Parameters: +* fmt +* Pointer to a null terminated string containing the format +* specifier. +* digs +* The default number of digits of precision to use. This is used +* if the given format specifier indicates the number of decimal +* places to use with the string ".*". In this case, the number of +* decimal places produced will be chosen so that the total number +* of digits of precision is equal to "digs". +* double +* The value to be formatted (in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null terminated character string containing the +* formatted value. + +* Format Specifier: +* The format specifier supplied via the "fmt" parameter should +* contain zero or more of the following characters to specify the +* format required. These characters may occur in any order, but +* the following is recommended for clarity: +* +* '+' +* Indicates that a plus sign should be prefixed to positive +* values. By default, no plus sign is used. +* 'z' +* Indicates that leading zeros should be prefixed to the value +* so that the first field is always of constant width, as would +* be required in a fixed-width table. (Leading zeros are always +* prefixed to any fields that follow.) By default, no leading +* zeros are added. +* 'i' +* Use the standard ISO field separator (a colon) between +* fields. This is the default behaviour. +* 'b' +* Use a blank to separate fields. +* 'l' +* Use a letter ('d'/'h', 'm' or 's' as appropriate) to separate +* and identify fields. +* 'g' +* As 'l', but escape sequences are included in the returned +* character string which cause the separators ('h', 'd', 'm', etc) +* to be drawn as small super-scripts when plotted by the astText +* or astGrid. +* 'd' +* Express the value as an angle and include a degrees +* field. Expressing the value as an angle is also the default +* behaviour if neither 'h' nor 't' is given, and expressing it +* in degrees is the default if neither 'm' nor 's' is given. +* 'h' +* Express the value as a time instead of an angle (where 24 +* hours correspond to 360 degrees) and include an hours +* field. Expressing times in hours is the default if 't' is +* given without either 'm' or 's'. +* 'm' +* Include a minutes field. By default this is not included. +* 's' +* Include a seconds field. By default this is not +* included. This request is ignored if 'd' or 'h' is given, +* unless a minutes field is also included. +* 't' +* Express the value as a time instead of an angle (where 24 +* hours correspond to 360 degrees). This option is ignored if +* either 'd' or 'h' is given and is intended for use in cases +* where the value is to be expressed purely in minutes and/or +* seconds of time (no hours field). If 't' is given without +* 'd', 'h', 'm' or 's' being present, then it is equivalent to +* 'h'. +* '.' +* Indicates that decimal places are to be given for the final +* field in the formatted string (whichever field this is). The +* '.' should be followed immediately by a zero or positive integer +* which gives the number of decimal places required. The '.' may +* also be followed by asterisk (i.e. '.*') which causes the number +* of decimal places to be chosen so that the total number of digits +* is equal to the value of Digits. +* +* Format specifiers are not case sensitive. If several characters +* make conflicting requests (e.g. if both 'i' and 'l' appear in a +* format specifier), then the character occurring last takes +* precedence, except that 'd' and 'h' always override 't'. + +* Notes: +* - The result string may be stored in static memory. Its contents +* may be over-written or the returned pointer may become invalid +* following a further invocation of this function. A copy of the +* string should therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Acknowledgements: +* - This function is a close approximation to a Fortran 77 routine +* written by Clive Davenhall which implements the system of format +* specifiers for angles described in his document on the CAT +* catalogue access library (Starlink User Note 181). Some minor +* improvements have been made to ensure better behaviour when +* results are rounded to a specified number of decimal places. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *term; /* Pointer to terminator string */ + char sep; /* Field separator character */ + char tbuf[50]; /* Buffer for terminator string */ + const char *result; /* Pointer to result string */ + double absvalue; /* Absolute value in radians */ + double fract; /* Fractional part of final field */ + double idh; /* Integer number of degrees/hours */ + double ifract; /* Fractional part expressed as an integer */ + double imin; /* Integer number of minutes */ + double isec; /* Integer number of seconds */ + double shift; /* Factor for rounding fractional part */ + double test; /* Test value to determine rounding */ + int as_time; /* Format the value as a time? */ + int dh; /* Degrees/hours field required? */ + int lead_zero; /* Add leading zeros? */ + int min; /* Minutes field required? */ + int ndp; /* Number of decimal places */ + int plus; /* Add leading plus sign? */ + int pos; /* Position to add next character */ + int positive; /* Value is positive (or zero)? */ + int sec; /* Seconds field required? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + result = NULL; + +/* Check if a bad coordinate value has been given and return an + appropriate string. */ + if ( value == AST__BAD ) { + result = ""; + +/* Otherwise... */ + } else { + +/* Parse the format specifier. */ + ParseDHmsFormat( fmt, digs, &sep, &plus, &lead_zero, + &as_time, &dh, &min, &sec, &ndp, status ); + +/* Break the value into fields. */ +/* ---------------------------- */ +/* Restrict the number of decimal places requested, if necessary, so + that under the worst case the buffer for the result string is not + likely to overflow. */ + if ( astOK ) { + if ( ( ndp + 11 ) > AST__SKYAXIS_DHMSFORMAT_BUFF_LEN ) ndp = AST__SKYAXIS_DHMSFORMAT_BUFF_LEN - 11; + +/* Some operating systems have a "minus zero" value (for instance + "-1.2*0" would give "-0"). This value is numerically equivalent to + zero, but is formated as "-0" instead of "0". The leading minus sign + confuses the following code, and so ensure now that all zero values + are the usual "+0". */ + if ( value == 0.0 ) value = 0.0; + +/* Determine if the value to be formatted is positive and obtain its + absolute value in radians. */ + positive = ( value >= 0.0 ); + absvalue = positive ? value : -value; + +/* Convert this to an absolute number of degrees or hours, as + required. */ + fract = absvalue / ( as_time ? hr2rad : deg2rad ); + +/* If a degrees/hours field is required, extract the whole number of + degrees/hours and the remaining fractional part of a + degree/hour. */ + idh = 0.0; + if ( dh ) fract = modf( fract, &idh ); + +/* If a minutes field is required, convert the value remaining to + minutes and extract the whole number of minutes and the remaining + fractional part of a minute. */ + imin = 0.0; + if ( min ) fract = modf( fract * 60.0, &imin ); + +/* If a seconds field is required, convert the value remaining to + seconds (allowing for the absence of a minutes field if necessary) + and extract the whole number of seconds and the remaining + fractional part of a second. */ + isec = 0.0; + if ( sec ) { + if ( !min ) fract *= 60.0; + fract = modf( fract * 60.0, &isec ); + } + +/* Round to the required number of decimal places. */ +/* ----------------------------------------------- */ +/* We must now round the fractional part (of whichever field) to the + required number of decimal places. Calculate the power of 10 that + brings the least significant digit into the units column. Scale the + fractional part by this factor and truncate to an integer (but + stored as a double to prevent possible integer overflow if the + number of decimal places is excessive). */ + shift = pow( 10.0, (double) ndp ); + ifract = floor( fract * shift ); + +/* Next we must determine if truncation was adequate, or whether we + should round upwards instead. This process is more subtle than it + seems because if a value with a 5 as the final digit is converted + to radians and then back again, it may no longer end in 5 (because + it cannot be represented exactly in radians) and so may round + either up or down. If we want to recover the original (textual) + value, we must compare the value we are formatting not with a test + value whose last digit is 5, but with the closest number to this + that can be represented exactly in radians. + + To do this, we add 0.5 to our truncated value, divide by the scale + factor (to get the truncated fractional part, but now with a + trailing digit 5 appended) and then combine this fractional part + with the value of all the other fields. Finally, we convert this + test value back into radians. */ + test = ( 0.5 + ifract ) / shift; + if ( sec ) test = ( isec + test ) / 60.0; + if ( min ) { + test = ( imin + test ) / 60.0; + } else if ( sec ) { + test /= 60.0; + } + if ( dh ) test += idh; + test *= ( as_time ? hr2rad : deg2rad ); + +/* We now compare the absolute value we are formatting with this test + value. If it is not smaller than it, we should have rounded up + instead of truncating the final digit of the fractional part, so + increment the integer representation of the truncated fractional + part by 1.0 to compensate. */ + if ( absvalue >= test ) ifract += 1.0; + +/* Divide by the scale factor to obtain the correctly rounded + fractional part. Then check if this fractional part is 1.0. If so, + rounding has caused it to overflow into the units column of the + final field, so clear the fractional part. */ + fract = ( ifract / shift ); + if ( fract >= 1.0 ) { + ifract = 0.0; + +/* If a seconds field is present, propagate the overflow up through + each field in turn, but omitting fields which are not required. Be + careful about possible rounding errors when comparing integer + values stored as double. */ + if ( sec ) { + isec += 1.0; + if ( ( floor( isec + 0.5 ) > 59.5 ) && min ) { + isec = 0.0; + imin += 1.0; + if ( ( floor( imin + 0.5 ) > 59.5 ) && dh ) { + imin = 0.0; + idh += 1.0; + } + } + +/* Omit the seconds field if it is not present. */ + } else if ( min ) { + imin += 1.0; + if ( ( floor( imin + 0.5 ) > 59.5 ) && dh ) { + imin = 0.0; + idh += 1.0; + } + +/* If only the degree/hour field is present, simply increment it. */ + } else { + idh += 1.0; + } + } + +/* Construct the result string. */ +/* ---------------------------- */ +/* We now have the value of each field and the information about how + they are to be formatted, so we can combine them into the required + string. */ + +/* If each field is either not required or equal to zero, disregard + any sign. */ + if ( !positive && ( !dh || floor( idh + 0.5 ) < 0.5 ) && + ( !min || floor( imin + 0.5 ) < 0.5 ) && + ( !sec || floor( isec + 0.5 ) < 0.5 ) && + ( floor( ifract + 0.5 ) < 0.5 ) ) { + positive = 1; + } + +/* Use "pos" to identify where the next character should be + added. Insert a leading '+' or '-' sign if required. */ + pos = 0; + if ( !positive ) { + dhmsformat_buff[ pos++ ] = '-'; + } else if ( plus ) { + dhmsformat_buff[ pos++ ] = '+'; + } + +/* Use "sprintf" to format the degrees/hours field, if required. Set + the minimum field width according to whether padding with leading + zeros is required and whether the value represents hours (2 digits) + or degrees (3 digits). */ + if ( dh ) { + pos += sprintf( dhmsformat_buff + pos, "%0*.0f", + lead_zero ? ( as_time ? 2 : 3 ) : 1, idh ); + +/* If letters are being used as field separators, and there are more + fields to follow, append "d" or "h" as necessary. */ + if ( min || sec ) { + if ( sep == 'l' ) { + dhmsformat_buff[ pos++ ] = ( as_time ? 'h' : 'd' ); + } else if( sep == 'g' ) { + astTuneC( as_time ? "hrdel":"dgdel", NULL, tbuf, + sizeof( tbuf ) ); + term = tbuf; + pos += sprintf( dhmsformat_buff + pos, "%s", term ); + } + } + } + +/* If a minutes field is required, first add an appropriate non-letter + field separator if needed. */ + if ( min ) { + if ( ( sep != 'l' && sep != 'g' ) && dh ) dhmsformat_buff[ pos++ ] = sep; + +/* Then format the minutes field with a leading zero to make it two + digits if necessary. */ + pos += sprintf( dhmsformat_buff + pos, "%0*.0f", ( dh || lead_zero ) ? 2 : 1, + imin ); + +/* If letters are being used as field separators, and there is another + field to follow, append the separator. */ + if ( sec ) { + if ( sep == 'l' ) { + dhmsformat_buff[ pos++ ] = 'm'; + } else if( sep == 'g' ) { + astTuneC( as_time ? "mndel":"amdel", NULL, tbuf, + sizeof( tbuf ) ); + term = tbuf; + pos += sprintf( dhmsformat_buff + pos, "%s", term ); + } + } + } + +/* Similarly, if a seconds field is required, first add an appropriate + non-letter field separator if needed. */ + if ( sec ) { + if ( ( sep != 'l' && sep != 'g' ) && ( dh || min ) ) dhmsformat_buff[ pos++ ] = sep; + +/* Then format the seconds field with a leading zero to make it two + digits if necessary. */ + pos += sprintf( dhmsformat_buff + pos, "%0*.0f", + ( dh || min || lead_zero ) ? 2 : 1, isec ); + } + +/* If decimal places are needed, add a decimal point followed by the + integer representation of the correctly rounded fractional part, + padded with leading zeros if necessary. */ + if ( ndp > 0 ) { + dhmsformat_buff[ pos++ ] = '.'; + pos += sprintf( dhmsformat_buff + pos, "%0*.0f", ndp, ifract ); + } + +/* If letters are being used as separators, append the appropriate one + to the final field. */ + if ( sep == 'l' ) { + dhmsformat_buff[ pos++ ] = ( sec ? 's' : ( min ? 'm' : + ( as_time ? 'h' : 'd' ) ) ); + } else if ( sep == 'g' ) { + astTuneC( as_time ? ( sec ? "scdel" : ( min ? "mndel" : "hrdel" ) ) : + ( sec ? "asdel" : ( min ? "amdel" : "dgdel" ) ), + NULL, tbuf, sizeof( tbuf ) ); + term = tbuf; + pos += sprintf( dhmsformat_buff + pos, "%s", term ); + } + +/* Terminate the result string and return a pointer to it. */ + dhmsformat_buff[ pos ] = '\0'; + result = dhmsformat_buff; + } + } + +/* Return the result. */ + return result; +} + +static double DHmsGap( const char *fmt, int digs, double gap, int *ntick, int *status ) { +/* +* Name: +* DHmsGap + +* Purpose: +* Find a "nice" gap for formatted SkyAxis values. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* double DHmsGap( const char *fmt, int digs, double gap, int *ntick, int *status ) + +* Class Membership: +* SkyAxis member function. + +* Description: +* This function returns a gap size in radians which produces a +* nicely spaced series of formatted SkyAxis values, the returned +* gap size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* fmt +* Pointer to a constant null-terminated string containing the +* format specifier which will be used to format the SkyAxis +* values. +* digs +* The default number of digits of precision to use. This is used +* if the given format specifier indicates the number of decimal +* places to use with the string ".*". In this case, the number of +* decimal places produced will be chosen so that the total number +* of digits of precision is equal to "digs". +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is +* negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Constants: */ +#define BUFF_LEN 50 /* Length of character buffer */ + +/* Local Variables: */ + char buff[ BUFF_LEN + 1 ]; /* Buffer for formatted scaled "nice" value */ + char sep; /* Field separator character */ + const double *table; /* Pointer to nice gap table */ + const int *nticks; /* Pointer to number of subdivisions table */ + double field_value[ 3 ]; /* Formatted field values in radians */ + double scale; /* Power of ten scaling factor */ + double scaled_table_value; /* Scaled "nice" value to test against */ + int as_time; /* Format the value as a time? */ + int decimal; /* Use nice decimal gap value? */ + int dh; /* Degrees/hours field required? */ + int field; /* ID of most significant formatted field */ + int i; /* Look-up-table index */ + int iter; /* Iteration count */ + int lead_zero; /* Add leading zeros? */ + int min; /* Minutes field required? */ + int ndp; /* Number of decimal places */ + int plus; /* Add leading plus sign? */ + int positive; /* Value is positive (or zero)? */ + int sec; /* Seconds field required? */ + +/* Local Data: */ +/* ----------- */ +/* Table of nice decimal gaps. */ + const double dec_table[] = { 1.0, 2.0, 5.0, 5.0, 10.0, -1.0 }; + const int dec_nticks[] = { 5, 4, 5, 5, 5 }; + +/* Table of nice degrees gaps. */ + const double deg_table[] = + { 1.0, 2.0, 5.0, 10.0, 30.0, 45.0, 60.0, 90.0, 180.0, 360.0, -1.0 }; + const int deg_nticks[] = + { 4, 4, 5, 5, 6, 3, 6, 3, 3, 4 }; + +/* Table of nice hours gaps. */ + const double hr_table[] = { 1.0, 2.0, 3.0, 6.0, 12.0, 24.0, -1.0 }; + const int hr_nticks[] = { 4, 4, 6, 6, 4, 4 }; + +/* Table of nice minutes or seconds gaps. */ + const double minsec_table[] = { 1.0, 2.0, 5.0, 10.0, 30.0, 60.0, -1.0 }; + const int minsec_nticks[] = { 4, 4, 5, 5, 6, 4 }; + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Check that the supplied gap size is not zero. */ + if ( gap != 0.0 ) { + +/* Parse the format specifier. */ + ParseDHmsFormat( fmt, digs, &sep, &plus, &lead_zero, &as_time, &dh, &min, + &sec, &ndp, status ); + +/* If OK, calculate the value of each formatted field in radians. */ + if ( astOK ) { + field_value[ 0 ] = ( as_time ? hr2rad : deg2rad ); + field_value[ 1 ] = field_value[ 0 ] / 60.0; + field_value[ 2 ] = field_value[ 0 ] / 3600.0; + +/* Determine if the suggested gap size is positive and obtain its + absolute value. */ + positive = ( gap >= 0.0 ); + if ( !positive ) gap = -gap; + +/* Perform two iterations to determine the optimum gap value. This is + because the method of choosing the gap value depends on the initial + value. If a nice decimal gap is chosen on the first iteration, + this may round the suggested gap value downwards, making it + preferable to choose the gap value using a different method on the + second iteration. */ + for ( iter = 0; iter < 2; iter++ ) { + +/* Decide which is the most significant field that the suggested gap + value will occupy when formatted. Also decide whether to use a + special "nice" gap value specific to that field, or simply to use a + generic nice decimal gap value. Perform all tests on the gap size + in radians, so as to avoid any rounding problems from conversion + into degrees/hours, minutes or seconds. */ + decimal = 0; + +/* Suggested values exceeding one degree/hour. */ +/* ------------------------------------------- */ + if ( gap > field_value[ 0 ] ) { + +/* If a degree/hour field is present, use a special gap value, unless + the suggested value exceeds the normal range of this field (in + which case use a decimal gap). */ + if ( dh ) { + field = 1; + decimal = ( gap > ( field_value[ 0 ] * + ( as_time ? 24.0 : 360.0 ) ) ); + +/* If the most significant field is not degrees/hours, then its normal + range will be exceeded, so use a decimal gap. */ + } else if ( min ) { + field = 2; + decimal = 1; + } else { + field = 3; + decimal = 1; + } + +/* Suggested values exceeding one minute. */ +/* -------------------------------------- */ + } else if ( gap > field_value[ 1 ] ) { + +/* If a minutes field is present, the suggested value will lie within + its normal range, so use a special gap value. */ + if ( min ) { + field = 2; + +/* Otherwise, if the most significant field is seconds, its normal + range will be exceeded, so use a decimal gap value. */ + } else if ( sec ) { + field = 3; + decimal = 1; + +/* If only a degrees/hours field is present, then only digits after + the decimal point can be affected, so use a decimal gap value. */ + } else { + field = 1; + decimal = 1; + } + +/* Suggested values exceeding one second. */ +/* -------------------------------------- */ + } else if ( gap > field_value[ 2 ] ) { + +/* If a seconds field is present, the suggested value will lie within + its normal range, so use a special gap value. */ + if ( sec ) { + field = 3; + +/* If the least significant field is degrees/hours or minutes, then + only digits after the decimal point can be affected, so use a + decimal gap value. */ + } else if ( min ) { + field = 2; + decimal = 1; + } else { + field = 1; + decimal = 1; + } + +/* Suggested values less than one second. */ +/* -------------------------------------- */ + } else { + +/* Only digits after the decimal point can be affected, so decide + which is the least significant field present and use a decimal + gap. */ + if ( sec ) { + field = 3; + } else if ( min ) { + field = 2; + } else { + field = 1; + } + decimal = 1; + } + +/* If a decimal gap value is required, select the appropriate table of + gap values and numbers of subdivisions. */ + if ( decimal ) { + table = dec_table; + nticks = dec_nticks; + +/* Find a power of ten divisor which scales the suggested value (when + formatted) into the range 1.0 to 10.0. */ + scale = pow( 10.0, + floor( log10( gap / field_value[ field - 1 ] ) ) ); + +/* Look the scaled value up in the table, comparing values in radians + to avoid rounding problems due to conversion to/from + degrees/radians, etc. */ + for ( i = 0; table[ i + 1 ] > 0.0; i++ ) { + +/* We must be careful about rounding errors here. If, for example, we + read in a value of 0.15 as the suggested gap value, the scaled + "nice" value we would be comparing it with would be 0.1 times the + values in the nice values table. The relevant value in this table + is 1.5 (i.e. 0.5 * ( 1.0 + 2.0 ) ), so we would compute 0.1 * 1.5 + as the test value. However, this is probably not equal (to machine + precision) to the number that results when a formatted value of + 0.15 is read, because 0.1 isn't exactly representable. Since it is + the formatted appearance of the numbers which matters, we want a + new scaled nice table containing the numbers that result from + reading the formatted values 0.1, 0.2, etc. To achieve this effect, + we format the scaled table value using the default floating point + precision (which rounds to a relatively small number of decimal + digits) and then read the value back again. */ + (void ) sprintf( buff, "%g", scale * + 0.5 * ( table[ i ] + table[ i + 1 ] ) ); + (void) astSscanf( buff, "%lf", &scaled_table_value ); + +/* Now test the suggested gap value against the scaled table value. */ + if ( gap < ( field_value[ field - 1 ] * + scaled_table_value ) ) break; + } + +/* Return the nice gap value and the number of subdivisions. */ + gap = scale * field_value[ field - 1 ] * table[ i ]; + if ( ntick ) *ntick = nticks[ i ]; + +/* If a special gap value appropriate to the field is required, then + select the table of gap values and numbers of subdivisions + according to which field we are considering and whether it contains + degrees or hours. */ + } else { + if ( field == 1 ) { + if ( as_time ) { + table = hr_table; + nticks = hr_nticks; + } else { + table = deg_table; + nticks = deg_nticks; + } + } else { + table = minsec_table; + nticks = minsec_nticks; + } + +/* Search the table for a suitable gap. We do not need to format and + unformat the test value here (as we did above) because the table + values are being used literally and not being scaled. */ + for ( i = 0; table[ i + 1 ] > 0.0; i++ ) { + if ( gap < ( field_value[ field - 1 ] * + 0.5 * ( table[ i ] + table[ i + 1 ] ) ) ) break; + } + +/* Return the nice gap value and the number of subdivisions. */ + gap = field_value[ field - 1 ] * table[ i ]; + if ( ntick ) *ntick = nticks[ i ]; + } + } + +/* After iterations are complete, restore the original sign. */ + if ( !positive ) gap = -gap; + } + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) gap = 0.0; + +/* Return the result. */ + return gap; + +/* Undefine macros local to this function */ +#undef BUFF_LEN +} + +static const char *DHmsUnit( const char *fmt, int digs, int output, int *status ) { +/* +* Name: +* DHmsUnit + +* Purpose: +* Generate a unit string to describe a formatted angle or time. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *DHmsUnit( const char *fmt, int digs, int output, int *status ) + +* Class Membership: +* SkyAxis member function. + +* Description: +* This function generates a string that may be used to describe +* either (a) the units of an angle or time that has been formatted +* for output using the DHmsFormat function, or (b) a suitable +* format to be used for an angle or time that is to be supplied as +* an input coordinate value. + +* Parameters: +* fmt +* Pointer to a null terminated string containing the format +* specifier used to format coordinate values. For details of +* the syntax of this string, see the DHmsFormat function. +* digs +* The default number of digits of precision to use. This is used +* if the given format specifier indicates the number of decimal +* places to use with the string ".*". In this case, the number of +* decimal places produced will be chosen so that the total number +* of digits of precision is equal to "digs". +* output +* If non-zero, the returned string will be in a form suitable +* for describing the units/format of output produced using +* DHmsFormat. +* +* If zero, the returned string will be in a form suitable for +* describing a suggested input format, which will subsequently +* be read using AxisUnformat. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null terminated string containing the unit description. + +* Notes: +* - The result string may be stored in static memory. Its contents +* may be over-written or the returned pointer may become invalid +* following a further invocation of this function. A copy of the +* string should therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char dpchar; /* Character to indicate decimal places */ + char sep; /* Field separator character */ + const char *result; /* Pointer to result string */ + const int maxdp = 6; /* Maximum number of decimal places to show */ + int as_time; /* Value formatted as a time? */ + int dh; /* Degrees/hours field required? */ + int dp; /* Loop counter for decimal places */ + int lead_zero; /* Add leading zeros? */ + int min; /* Minutes field required? */ + int ndp; /* Number of decimal places */ + int plus; /* Leading plus sign required? */ + int pos; /* Position to add next character */ + int sec; /* Seconds field required? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Parse the format specifier. */ + ParseDHmsFormat( fmt, digs, &sep, &plus, &lead_zero, &as_time, &dh, &min, + &sec, &ndp, status ); + +/* If the units string is required to describe formatted output and + the field separators are letters (e.g. giving "01h23m45s" or + "012d34m56s"), then the units will already be clear so return a + pointer to an empty units string. */ + if ( astOK ) { + if ( output && ( sep == 'l' || sep == 'g' ) ) { + result = ""; + +/* Otherwise, if the units string is required to describe formatted + output and there is only one field present, then select an + appropriate string. */ + } else if ( output && dh && !min && !sec ) { + result = as_time ? "hours" : "degrees"; + + } else if ( output && !dh && min && !sec ) { + result = as_time ? "minutes of time" : "arcminutes"; + + } else if ( output && !dh && !min && sec ) { + result = as_time ? "seconds of time" : "arcseconds"; + +/* If there is more than one field present, or we want to describe how + to supply formatted input, then we will generate a units string of + the general form "ddd:mm:ss.sss" or "hh:mm:ss.s" or + similar. Initialise the output character count and the character to + be used to represent decimal places. */ + } else { + pos = 0; + dpchar = 'd'; + +/* Decide which field separator to use (use a space if letters were + requested since it is easier to input). */ + if ( sep == 'l' || sep == 'g' ) sep = ' '; + +/* Start with the "ddd" or "hh" field, if required, and update the + decimal places character appropriately. */ + if ( dh ) { + pos += sprintf( dhmsunit_buff, "%s", as_time ? "hh" : "ddd" ); + dpchar = as_time ? 'h' : 'd'; + } + +/* If a minutes field is present, add a separator if necessary and + "mm" and update the decimal places character. */ + if ( min ) { + if ( dh ) dhmsunit_buff[ pos++ ] = sep; + dhmsunit_buff[ pos++ ] = 'm'; + dhmsunit_buff[ pos++ ] = 'm'; + dpchar = 'm'; + } + +/* Repeat this process for the seconds field, if present. */ + if ( sec ) { + if ( dh || min ) dhmsunit_buff[ pos++ ] = sep; + dhmsunit_buff[ pos++ ] = 's'; + dhmsunit_buff[ pos++ ] = 's'; + dpchar = 's'; + } + +/* If decimal places are present, add a decimal point and then loop to + add further instances of the decimal places character to represent + the digits that follow. */ + if ( ndp > 0 ) { + dhmsunit_buff[ pos++ ] = '.'; + for ( dp = 0; dp < ndp; dp++ ) { + if ( dp < maxdp ) { + dhmsunit_buff[ pos++ ] = dpchar; + +/* After showing the maximum number of decimal places, simply add an + ellipsis and quit (otherwise the result gets boring to look at). */ + } else { + dhmsunit_buff[ pos - 1 ] = '.'; + dhmsunit_buff[ pos - 2 ] = '.'; + dhmsunit_buff[ pos - 3 ] = '.'; + break; + } + } + } + +/* Terminate the result string and return a pointer to it. */ + dhmsunit_buff[ pos ] = '\0'; + result = dhmsunit_buff; + } + } + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SkyAxis, +* in bytes. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to SkyAxis structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SkyAxis structure. */ + this = (AstSkyAxis *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->skyformat ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the protected astGetAttrib +* method inherited from the Axis class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a SkyAxis, formatted as a character string. + +* Parameters: +* this +* Pointer to the SkyAxis. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the SkyAxis, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the SkyAxis. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *result; /* Pointer value to return */ + int as_time; /* AsTime attribute value */ + int centrezero; /* CentreZero attribute value */ + int is_latitude; /* IsLatitude attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* AsTime. */ +/* ------- */ + if ( !strcmp( attrib, "astime" ) ) { + as_time = astGetAxisAsTime( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", as_time ); + result = getattrib_buff; + } + +/* IsLatitude. */ +/* ----------- */ + } else if ( !strcmp( attrib, "islatitude" ) ) { + is_latitude = astGetAxisIsLatitude( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", is_latitude ); + result = getattrib_buff; + } + +/* CentreZero. */ +/* ----------- */ + } else if ( !strcmp( attrib, "centrezero" ) ) { + centrezero= astGetAxisCentreZero( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", centrezero ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static double GetAxisBottom( AstAxis *this_axis, int *status ) { +/* +* Name: +* GetAxisBottom + +* Purpose: +* Obtain the value of the Bottom attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* double GetAxisBottom( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisBottom method +* inherited from the Axis class). + +* Description: +* This function returns a value for the Bottom attribute of a SkyAxis. +* This attribute indicates the lowest legal value for the axis. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The atribute value. A suitable default value is supplied if necessary. + +* Notes: +* - A value of -DBL_MAX will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables. */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return -DBL_MAX; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Check if a value has been set for the Bottom attribute. If so, obtain + this value. */ + if ( astTestAxisBottom( this ) ) { + result = (*parent_getaxisbottom)( this_axis, status ); + +/* Otherwise, supply a default of -pi/2 for latitude axes, and -DBL_MAX + for longitude axes. */ + } else { + result = astGetAxisIsLatitude( this ) ? -piby2 : -DBL_MAX; + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -DBL_MAX; + +/* Return the result. */ + return result; +} + +static const char *GetAxisInternalUnit( AstAxis *this, int *status ){ +/* +* Name: +* GetAxisInternalUnit + +* Purpose: +* Return the unit string for unformatted Axis values + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* const char *GetAxisInternalUnit( AstAxis *this ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisInternalUnit method +* inherited from the Axis class). + +* Description: +* This function returns the axis InternalUnit attribute. For sky +* axes, the InternalUnit is always "rad" (radians). + +* Parameters: +* this +* Pointer to the Axis. + +* Returned Value: +* - Pointer to a null-terminated string containing the internal +* unit string. + +* Notes: +* - The returned pointer points to a static memory buffer. The +* contents of this buffer will be over-written on each invocation of +* this function. A copy of the returned string should therefore be +* taken if it will be needed later. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + return "rad"; +} + +static double GetAxisTop( AstAxis *this_axis, int *status ) { +/* +* Name: +* GetAxisTop + +* Purpose: +* Obtain the value of the Top attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* double GetAxisTop( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisTop method +* inherited from the Axis class). + +* Description: +* This function returns a value for the Top attribute of a SkyAxis. +* This attribute indicates the highest legal value for the axis. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The atribute value. A suitable default value is supplied if necessary. + +* Notes: +* - A value of DBL_MAX will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables. */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return DBL_MAX; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Check if a value has been set for the Top attribute. If so, obtain + this value. */ + if ( astTestAxisTop( this ) ) { + result = (*parent_getaxistop)( this_axis, status ); + +/* Otherwise, supply a default of pi/2 for latitude axes, and DBL_MAX + for longitude axes. */ + } else { + result = astGetAxisIsLatitude( this ) ? piby2 : DBL_MAX; + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = DBL_MAX; + +/* Return the result. */ + return result; +} + +static int GetAxisDirection( AstAxis *this_axis, int *status ) { +/* +* Name: +* GetAxisDirection + +* Purpose: +* Obtain the value of the Direction attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* int GetAxisDirection( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisDirection method +* inherited from the Axis class). + +* Description: +* This function returns a value for the Direction attribute of a SkyAxis. +* This attribute indicates in which direction the SkyAxis's values should +* increase when represented on a graph (1 for the conventional direction, +* 0 for reverse direction). + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero or one, according to the attribute setting. A suitable default +* value is supplied if necessary. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables. */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Check if a value has been set for the Direction attribute. If so, obtain + this value. */ + if ( astTestAxisDirection( this ) ) { + result = (*parent_getaxisdirection)( this_axis, status ); + +/* Otherwise, supply a default of 1 unless the SkyAxis values are being + formatted as times (instead of angles) by default. */ + } else { + result = !astGetAxisAsTime( this ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static const char *GetAxisFormat( AstAxis *this_axis, int *status ) { +/* +* Name: +* GetAxisFormat + +* Purpose: +* Obtain a pointer to the Format attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *GetAxisFormat( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisFormat method inherited +* from the Axis class). + +* Description: +* This function returns a pointer to the Format attribute associated with +* a SkyAxis and provides a suitable default if necessary. This string +* attribute contains the format specifier that will be interpreted by the +* astAxisFormat method when formatting a value for the SkyAxis. The default +* Format may depend on other attribute settings, in particular on the +* Digits and AsTime attributes. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Format string (null terminated). + +* Notes: +* - The pointer returned may point at memory allocated within the SkyAxis +* object, or at static memory. The contents of the string may be +* over-written or the pointer may become invalid following a further +* invocation of the same function, deletion of the SkyAxis, or assignment +* of a new Format value. A copy of the string should therefore be made if +* necessary. +* - This function will return a NULL pointer if it is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *result; /* Pointer to result string */ + int as_time; /* Format SkyAxis values as times? */ + int digits; /* Number of digits of precision */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_axis); + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Obtain a pointer to the Format string stored in the SkyAxis structure. Note + we do not use a method to obtain this, because we want a string with a + syntax appropriate to this class, and derived classes may have extended the + syntax. */ + result = this->skyformat; + +/* If no Format string has been set, we must generate a default one. Determine + how many digits of precision are to be used by default and whether the + SkyAxis values are to be formatted as times (instead of angles). */ + if ( !result ) { + digits = astGetAxisDigits( this ); + as_time = astGetAxisAsTime( this ); + if ( astOK ) { + +/* If formatting values as times, use the number of digits to select an + appropriate Format string and obtain a pointer to it. */ + if ( as_time ) { + if ( digits <= 2 ) { + result = "h"; + } else if ( digits == 3 ) { + result = "hm"; + } else if ( digits == 4 ) { + result = "hm"; + } else if ( digits == 5 ) { + result = "hms"; + } else if ( digits == 6 ) { + result = "hms"; + +/* Construct the Format string in a buffer if necessary. */ + } else { + (void) sprintf( getaxisformat_buff, "hms.%d", digits - 6 ); + result = getaxisformat_buff; + } + +/* Similarly, select a Format for expressing an angle if necessary. */ + } else { + if ( digits <= 3 ) { + result = "d"; + } else if ( digits == 4 ) { + result = "dm"; + } else if ( digits == 5 ) { + result = "dm"; + } else if ( digits == 6 ) { + result = "dms"; + } else if ( digits == 7 ) { + result = "dms"; + } else { + (void) sprintf( getaxisformat_buff, "dms.%d", digits - 7 ); + result = getaxisformat_buff; + } + } + } + } + +/* Return the result. */ + return result; +} + +static const char *GetAxisLabel( AstAxis *this_axis, int *status ) { +/* +* Name: +* GetAxisLabel + +* Purpose: +* Obtain a pointer to the Label attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *GetAxisLabel( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisLabel method inherited +* from the Axis class). + +* Description: +* This function returns a pointer to the Label attribute associated with +* a SkyAxis and provides a suitable default if necessary. This string +* attribute specifies the label to be attached to the SkyAxis when it is +* represented in (e.g.) a graph. It is intended purely for interpretation +* by human readers and not by software. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Label string (null terminated). + +* Notes: +* - The pointer returned may point at memory allocated within the SkyAxis +* object, or at static memory. The contents of the string may be +* over-written or the pointer may become invalid following a further +* invocation of the same function, deletion of the SkyAxis, or assignment +* of a new Label value. A copy of the string should therefore be made if +* necessary. +* - This function will return a NULL pointer if it is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *result; /* Pointer value to be returned */ + int as_time; /* SkyAxis values formatted as times? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Test if the Label attribute is set. If so, use the parent astGetAxisLabel + method to get a pointer to it. */ + if ( astTestAxisLabel( this ) ) { + result = (*parent_getaxislabel)( this_axis, status ); + +/* Otherwise, return a pointer to a suitable default string, using the result + of the astGetAxisAsTime method to determine whether a string describing + time or angle is more appropriate. */ + } else { + as_time = astGetAxisAsTime( this ); + if ( !astTestAxisIsLatitude( this ) ) { + result = as_time ? "Angle on sky expressed as time" : + "Angle on sky"; + } else if ( astGetAxisIsLatitude( this ) ) { + result = as_time ? "Sky latitude expressed as time" : + "Sky latitude"; + } else { + result = as_time ? "Sky longitude expressed as time" : + "Sky longitude"; + } + } + +/* If an error occurred, clear the result pointer. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetAxisSymbol( AstAxis *this_axis, int *status ) { +/* +* Name: +* GetAxisSymbol + +* Purpose: +* Obtain a pointer to the Symbol attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *GetAxisSymbol( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisSymbol method inherited +* from the Axis class). + +* Description: +* This function returns a pointer to the Symbol attribute associated with +* a SkyAxis and provides a suitable default if necessary. This string +* attribute specifies the symbol to be used to represent coordinate values +* for the SkyAxis in "short form", such as in algebraic expressions where a +* full description would be inappropriate. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Symbol string (null terminated). + +* Notes: +* - The pointer returned may point at memory allocated within the SkyAxis +* object, or at static memory. The contents of the string may be +* over-written or the pointer may become invalid following a further +* invocation of the same function, deletion of the SkyAxis, or assignment +* of a new Symbol value. A copy of the string should therefore be made if +* necessary. +* - This function will return a NULL pointer if it is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Test if the Symbol attribute is set. If so, use the parent astGetAxisSymbol + method to get a pointer to it. */ + if ( astTestAxisSymbol( this ) ) { + result = (*parent_getaxissymbol)( this_axis, status ); + +/* If a value has been set for the IsLatitude attribute, use it to decide + whether to use "delta" (for latitude) or "alpha" (for longitude). */ + } else if ( astTestAxisIsLatitude( this ) ) { + result = astGetAxisIsLatitude( this ) ? "delta" : "alpha"; + +/* Otherwise, use the AsTime attribute to decide whether the SkyAxis is + likely to be a longitude or latitude axis (the former usually having values + formatted as times). */ + } else { + result = astGetAxisAsTime( this ) ? "alpha" : "delta"; + } + +/* If an error occurred, clear the result pointer. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetAxisUnit( AstAxis *this_axis, int *status ) { +/* +* Name: +* GetAxisUnit + +* Purpose: +* Obtain a pointer to the Unit attribute for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* const char *GetAxisUnit( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astGetAxisUnit method inherited +* from the Axis class). + +* Description: +* This function returns a pointer to the Unit attribute associated with +* a SkyAxis and provides a suitable default if necessary. This string +* attribute describes the unit used to represent formatted coordinate +* values on the SkyAxis. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Unit string (null terminated). + +* Notes: +* - The pointer returned may point at memory allocated within the SkyAxis +* object, or at static memory. The contents of the string may be +* over-written or the pointer may become invalid following a further +* invocation of the same function, deletion of the SkyAxis, or assignment +* of a new Unit value. A copy of the string should therefore be made if +* necessary. +* - This function will return a NULL pointer if it is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *fmt; /* Pointer to format specifier */ + const char *result; /* Pointer to result string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise */ + result = NULL; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Test if the Unit attribute is set. If so, invoke the parent astGetAxisUnit + method to obtain a pointer to it. */ + if ( astTestAxisUnit( this ) ) { + result = (*parent_getaxisunit)( this_axis, status ); + +/* If we must provide a default, obtain a pointer to the format specifier used + to format SkyAxis values. Use a private member function (not a method) to + access this, in case derived classes have extended the syntax of this + string. */ + } else { + fmt = GetAxisFormat( this_axis, status ); + +/* If the format string starts with a percent, use "rad" as the default units + string. Otherwise, use the format specifier to generate a matching + default Unit string and obtain a pointer to it. */ + if ( astOK ) { + if( fmt[ 0 ] == '%' ) { + result = "rad"; + } else { + result = DHmsUnit( fmt, astGetAxisDigits( this_axis ), 1, status ); + } + } + } + +/* Return the result. */ + return result; +} + +void astInitSkyAxisVtab_( AstSkyAxisVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSkyAxisVtab + +* Purpose: +* Initialise a virtual function table for a SkyAxis. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyaxis.h" +* void astInitSkyAxisVtab( AstSkyAxisVtab *vtab, const char *name ) + +* Class Membership: +* SkyAxis vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SkyAxis class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstAxisVtab *axis; /* Pointer to Axis component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + int stat; /* SLALIB status */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitAxisVtab( (AstAxisVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASkyAxis) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstAxisVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->ClearAxisAsTime = ClearAxisAsTime; + vtab->ClearAxisIsLatitude = ClearAxisIsLatitude; + vtab->ClearAxisCentreZero = ClearAxisCentreZero; + vtab->GetAxisAsTime = GetAxisAsTime; + vtab->GetAxisIsLatitude = GetAxisIsLatitude; + vtab->GetAxisCentreZero = GetAxisCentreZero; + vtab->SetAxisAsTime = SetAxisAsTime; + vtab->SetAxisIsLatitude = SetAxisIsLatitude; + vtab->SetAxisCentreZero = SetAxisCentreZero; + vtab->TestAxisAsTime = TestAxisAsTime; + vtab->TestAxisIsLatitude = TestAxisIsLatitude; + vtab->TestAxisCentreZero = TestAxisCentreZero; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + axis = (AstAxisVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_axisoverlay = axis->AxisOverlay; + axis->AxisOverlay = AxisOverlay; + parent_getaxisdirection = axis->GetAxisDirection; + axis->GetAxisDirection = GetAxisDirection; + parent_getaxislabel = axis->GetAxisLabel; + axis->GetAxisLabel = GetAxisLabel; + parent_getaxissymbol = axis->GetAxisSymbol; + axis->GetAxisSymbol = GetAxisSymbol; + parent_getaxisunit = axis->GetAxisUnit; + axis->GetAxisUnit = GetAxisUnit; + + parent_getaxistop = axis->GetAxisTop; + axis->GetAxisTop = GetAxisTop; + + parent_getaxisbottom = axis->GetAxisBottom; + axis->GetAxisBottom = GetAxisBottom; + + parent_axisformat = axis->AxisFormat; + axis->AxisFormat = AxisFormat; + + parent_axisunformat = axis->AxisUnformat; + axis->AxisUnformat = AxisUnformat; + + parent_axisgap = axis->AxisGap; + axis->AxisGap = AxisGap; + + parent_axisfields = axis->AxisFields; + axis->AxisFields = AxisFields; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + axis->AxisAbbrev = AxisAbbrev; + axis->AxisIn = AxisIn; + axis->AxisDistance = AxisDistance; + axis->AxisOffset = AxisOffset; + axis->AxisNorm = AxisNorm; + axis->ClearAxisFormat = ClearAxisFormat; + axis->GetAxisFormat = GetAxisFormat; + axis->SetAxisFormat = SetAxisFormat; + axis->TestAxisFormat = TestAxisFormat; + axis->GetAxisInternalUnit = GetAxisInternalUnit; + axis->TestAxisInternalUnit = TestAxisInternalUnit; + +/* Declare the destructor, copy constructor and dump function. */ + astSetDelete( vtab, Delete ); + astSetCopy( vtab, Copy ); + astSetDump( vtab, Dump, "SkyAxis", "Celestial coordinate axis" ); + +/* Initialize constants for converting between hours, degrees and radians. */ + LOCK_MUTEX2 + palDtf2r( 1, 0, 0.0, &hr2rad, &stat ); + palDaf2r( 1, 0, 0.0, °2rad, &stat ); + palDaf2r( 180, 0, 0.0, &pi, &stat ); + piby2 = 0.5*pi; + UNLOCK_MUTEX2 + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void ParseDHmsFormat( const char *fmt, int digs, char *sep, int *plus, + int *lead_zero, int *as_time, int *dh, int *min, + int *sec, int *ndp, int *status ) { +/* +* Name: +* ParseDHmsFormat + +* Purpose: +* Parse a format specifier for degrees/hours, minutes and seconds. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* void ParseDHmsFormat( const char *fmt, int digs, char *sep, int *plus, +* int *lead_zero, int *as_time, int *dh, int *min, +* int *sec, int *ndp, int *status ) + +* Class Membership: +* SkyAxis member function. + +* Description: +* This function parses a SkyAxis format specifier which describes +* how to convert an angle in radians into a text string with +* separate fields for degrees/hours, minutes and seconds. + +* Parameters: +* fmt +* Pointer to a null terminated string containing the format +* specifier. For details of the syntax of this string, see the +* DHmsFormat function. +* digs +* The default number of digits of precision to use. This is used +* if the given format specifier indicates the number of decimal +* places to use with the string ".*". In this case, the returned +* value for "ndp" will be set to produce the number of digits of +* precision given by "digs". +* sep +* Pointer to a location in which a single character will be +* returned to indicate which separator should be used to +* separate the fields. The returned value will be one of ' ' +* (use a blank as the separator), ':' (use a colon as the +* separator) or 'l' (use one of the letters "hdms" as +* appropriate) or 'g' (use one of the letters "hdms" but +* include suitable escape sequences to allow the Plot class to draw +* the letter as a small super-script). +* plus +* Pointer to an int in which a boolean value will be returned +* to indicate if a plus sign should be prefixed to positive +* values. +* lead_zero +* Pointer to an int in which a boolean value will be returned +* to indicate if leading zeros should be prefixed to the value +* so that the first field is always of constant (maximum) +* width, as would be required in a fixed-width table. Leading +* zeros are always prefixed to any fields that follow. +* as_time +* Pointer to an int in which a boolean value will be returned +* to indicate whether the value is to be formatted as a time +* (e.g. in hours) rather than as an angle (in degrees). +* dh +* Pointer to an int in which a boolean value will be returned +* to indicate whether a degrees or hours field is required. +* min +* Pointer to an int in which a boolean value will be returned +* to indicate whether a minutes field is required. +* sec +* Pointer to an int in which a boolean value will be returned +* to indicate whether a seconds field is required. +* ndp +* Pointer to an int in which to return the number of digits +* required following the decimal point in the final field. A +* value of zero indicates that the decimal point should be +* omitted. See parameter "digs". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Acknowledgements: +* - This function is a close approximation to a Fortran 77 routine +* written by Clive Davenhall which implements the system of format +* specifiers for angles described in his document on the CAT +* catalogue access library (Starlink User Note 181). It supports +* the same format specifiers. +*/ + +/* Local Variables: */ + int decpos; /* Offset of decimal point */ + int i; /* Loop counter for format characters */ + int ndpval; /* Number of decimal places required */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise. */ + *as_time = -1; + *lead_zero = 0; + *dh = 0; + *min = 0; + *ndp = 0; + *plus = 0; + *sec = 0; + *sep = ':'; + decpos = -1; + +/* Loop to inspect and classify each character. */ + for ( i = 0; fmt[ i ]; i++ ) { + switch ( fmt[ i ] ) { + +/* Note if a '+' sign is needed. */ + case '+': + *plus = 1; + break; + +/* Note if leading zeros are needed. */ + case 'Z': case 'z': + *lead_zero = 1; + break; + +/* Set the required separator. Note we only use graphical separators if + astEscapes indicates that escape sequences are currently being used. */ + case 'I': case 'i': + *sep = ':'; + break; + case 'B': case 'b': + *sep = ' '; + break; + case 'L': case 'l': + *sep = 'l'; + break; + case 'G': case 'g': + *sep = astEscapes( -1 ) ? 'g' : 'l'; + break; + +/* Note if the value is to be formatted as a time (but not if a + degrees or hours field has already been specified). */ + case 'T': case 't': + if ( *as_time == -1 ) *as_time = 1; + break; + +/* Note if a degrees or hours field is required (and hence whether the + value is to be formatted as a time or an angle). */ + case 'H': case 'h': + *dh = 1; + *as_time = 1; + break; + case 'D': case 'd': + *dh = 1; + *as_time = 0; + break; + +/* Note if a minutes field is required. */ + case 'M': case 'm': + *min = 1; + break; + +/* Note if a seconds field is required. */ + case 'S': case 's': + *sec = 1; + break; + +/* Note if decimal places are required. */ + case '.': + decpos = i; + } + } + +/* Format the value as an angle by default. */ + if ( *as_time == -1 ) *as_time = 0; + +/* Use degrees (or hours) as the default field. */ + if ( !*min && !*sec ) *dh = 1; + +/* Omit the seconds field if the degrees/hours field is present but + the minutes field is not. */ + if ( *dh && !*min ) *sec = 0; + +/* Determine the default number of decimal places following the final field. + This is the number which will be used if the format specifier does not + indicate how many decimal places should be produced. It is shosen to + produce the requested total number of digits of precision. */ + +/* If decimal places are required, attempt to read the integer value + following the decimal point which specifies how many. If successful, + and a valid (positive or zero) result was obtained, note its value. If + an asterisk follows the decimal point, use a value determined by the + supplied "digs" value. */ + if ( ( decpos >= 0 ) && ( decpos < ( i - 1 ) ) ) { + + if ( astSscanf( fmt + decpos + 1, "%d", &ndpval ) == 1 ) { + if ( ndpval >= 0 ) *ndp = ndpval; + + } else if ( fmt[ decpos + 1 ] == '*' ) { + *ndp = digs; + if( *as_time ) { + *ndp = ( digs > 2 ) ? digs : 2; + if( *dh ) *ndp -= 2; + } else { + *ndp = ( digs > 3 ) ? digs : 3; + if( *dh ) *ndp -= 3; + } + if( *min ) *ndp -= 2; + if( *sec ) *ndp -= 2; + if( *ndp < 0 ) *ndp = 0; + } + } +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astSetAttrib method +* inherited from the Axis class). + +* Description: +* This function assigns an attribute value for a SkyAxis, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the SkyAxis. +* setting +* Pointer to a null terminated string specifying the new +* attribute value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + int as_time; /* Format values as times? */ + int centrezero; /* SkyAxis range centred on zero? */ + int is_latitude; /* SkyAxis is a latitude axis? */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* AsTime. */ +/* ------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "astime= %d %n", &as_time, &nc ) ) + && ( nc >= len ) ) { + astSetAxisAsTime( this, as_time ); + +/* IsLatitude. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "islatitude= %d %n", &is_latitude, &nc ) ) + && ( nc >= len ) ) { + astSetAxisIsLatitude( this, is_latitude ); + +/* CentreZero. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "centrezero= %d %n", ¢rezero, &nc ) ) + && ( nc >= len ) ) { + astSetAxisCentreZero( this, centrezero ); + +/* Pass any unrecognised attribute setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetAxisFormat( AstAxis *this_axis, const char *format, int *status ) { +/* +* Name: +* SetAxisFormat + +* Purpose: +* Set a value for the Format attribute of a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* void SetAxisFormat( AstAxis *this, const char *format ) + +* Class Membership: +* SkyAxis member function (over-rides the astSetAxisFormat method inherited +* from the Axis class). + +* Description: +* This function sets a new value for the Format attribute of a SkyAxis. + +* Parameters: +* this +* Pointer to the SkyAxis. +* format +* Pointer to a null terminated string containing the new Format value. + +* Returned Value: +* void + +* Notes: +* - For details of the syntax of the Format string, see the DHmsFormat +* function. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* Store a pointer to a copy of the Format string in the SkyAxis structure. */ + this->skyformat = astStore( this->skyformat, format, + strlen( format ) + (size_t) 1 ); +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astTestAttrib protected +* method inherited from the Axis class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate +* whether a value has been set for one of a SkyAxis' attributes. + +* Parameters: +* this +* Pointer to the SkyAxis. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + +/* AsTime. */ +/* ------- */ + if ( !strcmp( attrib, "astime" ) ) { + result = astTestAxisAsTime( this ); + +/* IsLatitude. */ +/* ----------- */ + } else if ( !strcmp( attrib, "islatitude" ) ) { + result = astTestAxisIsLatitude( this ); + +/* CentreZero. */ +/* ----------- */ + } else if ( !strcmp( attrib, "centrezero" ) ) { + result = astTestAxisCentreZero( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int TestAxisFormat( AstAxis *this_axis, int *status ) { +/* +* Name: +* TestAxisFormat + +* Purpose: +* Test if a value has been set for the Format attribute of a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* int TestAxisFormat( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astTestAxisFormat method +* inherited from the Axis class). + +* Description: +* This function returns 0 or 1 to indicate whether a value has been set +* for the Format attribute of a SkyAxis. + +* Parameters: +* this +* Pointer to the SkyAxis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if no Format value has been set, otherwise one. + +* Notes: +* - This function will return a value of zero if it is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_axis; + +/* The Format string has been set if the pointer to it is not NULL. */ + result = ( this->skyformat != NULL ); + +/* Return the result. */ + return result; +} + +static int TestAxisInternalUnit( AstAxis *this, int *status ){ +/* +* Name: +* TestAxisInternalUnit + +* Purpose: +* Test if a InternalUnit attribute value is set for an Axis. + +* Type: +* Private function. + +* Synopsis: +* #include "axis.h" +* int TestAxisInternalUnit( AstAxis *this, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astTestAxisInternalUnit +* protected method inherited from the Axis class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate +* whether a value has been set for the InternalUnit string. + +* Parameters: +* this +* Pointer to the Axis. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Tell the world that we know what value to use for InternalUnit (i.e. + "rad"). */ + return 1; +} + +static int AxisUnformat( AstAxis *this_axis, const char *string, + double *value, int *status ) { +/* +* Name: +* AxisUnformat + +* Purpose: +* Read a formatted coordinate value for a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* int AxisUnformat( AstAxis *axis, const char *string, double *value, int *status ) + +* Class Membership: +* SkyAxis member function (over-rides the astAxisUnformat method +* inherited from the Axis class). + +* Description: +* This function reads a formatted coordinate value for a SkyAxis +* (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the SkyAxis. +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will be +* returned (in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*- +*/ + +/* Local Constants: */ +#define FMT_LEN 50 /* Length of format buffer */ + +/* Local Variables: */ + char fmtbuf[ FMT_LEN + 1 ]; /* Buffer for C format specification */ + char fmtsep; /* Format field separator character */ + char last_sep; /* Previous separator character */ + char sep; /* Separator character */ + char sep_used; /* Separator character being used */ + char sign[ 2 ]; /* Sign character as string */ + const char *field_start[ 3 ]; /* Pointer to start of each field */ + const char *fmt; /* Pointer to SkyAxis Format string */ + const char *s; /* Pointer to current reading position */ + const char *string_start; /* Pointer to first significant character */ + double field[ 3 ]; /* Field values */ + double testval; /* Value to test for invalid fields */ + int angle_or_time; /* Value known to be angle or time? */ + int as_time; /* Value is a time (else an angle)? */ + int decimal; /* Decimal point in field? */ + int dh; /* Hours field required? */ + int digs; /* Default no. of digits of precision */ + int exponent; /* Exponent at end of field? */ + int field_id[ 3 ]; /* Field identification (0 = don't know) */ + int final; /* Final field read? */ + int good_sep; /* Separator character valid? */ + int i; /* Loop counter for characters */ + int ifield; /* Loop counter for fields */ + int lead_zero; /* Add leading zeros? */ + int len; /* Significant length of string */ + int m; /* Number of characters read by astSscanf */ + int match; /* Character pattern matches? */ + int min; /* Minutes field required? */ + int n; /* Number of characters read by astSscanf */ + int nc; /* Total no. characters read */ + int nchar; /* Number of characters in erroneous value */ + int ndp; /* Number of decimal places */ + int next_id; /* Next field ID to use (0 = don't know) */ + int nfield; /* Number of fields read */ + int nread; /* No. characters read for current field */ + int plus; /* Add leading plus sign? */ + int positive; /* Value is positive? */ + int sec; /* Seconds field required? */ + int sep_angle_or_time; /* Separator indicates angle or time? */ + int sep_field_id; /* Field ID from separator (0 = don't know) */ + int sep_index; /* Index of separator character in table */ + int sep_len; /* Length of separator plus trailing space */ + int suffix_sep; /* Field has a suffix separator? */ + +/* Local Data: */ + const char *sep_list = /* List of separator characters recognised */ + " :hHdDmM'sS\""; + + const int angle_or_time_list[] = /* Whether separator indicates angle or + time (1 or 2). Zero => don't know. */ + { 0, 0, 2, 2, 1, 1, 0, 0, 1, 0, 0, 1 }; + + const int field_id_list[] = /* Whether separator identifies previous field + (1, 2, or 3). Zero => doesn't identify. */ + { 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3 }; + + const double fieldvalue[ 3 ] = /* Nominal field values (degrees/hours) */ + { 1.0, 1.0 / 60.0, 1.0 / 3600.0 }; + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Obtain the SkyAxis Format string. If its starts with a "%" sign, use + the parent AxisUnformat method inherited from the Axis class. Use + a private method to obtain the Format string, in case the syntax has been + over-ridden by a derived class. */ + fmt = GetAxisFormat( this_axis, status ); + if( fmt && fmt[0] == '%' ) { + nc = (*parent_axisunformat)( this_axis, string, value, status ); + +/* Otherwise, parse it to determine the default choice of input format. */ + } else if( astOK ){ + digs = astGetAxisDigits( this_axis ); + ParseDHmsFormat( fmt, digs, &fmtsep, &plus, &lead_zero, &as_time, &dh, + &min, &sec, &ndp, status ); + +/* Initialise a pointer into the string and advance it to the first + non-white space character. Save a copy of this pointer. */ + s = string; + while ( isspace( (int) *s ) ) s++; + string_start = s; + +/* Read sign information. */ +/* ---------------------- */ +/* Attempt to read an optional sign character ("+" or "-"), possibly + surrounded by white space. Set a flag to indicate if the returned + value should be positive or not. Increment the string pointer to + the next significant character. */ + positive = 1; + n = 0; + if ( 1 == astSscanf( s, " %1[+-] %n", sign, &n ) ) { + positive = ( *sign == '+' ); + s += n; + } + +/* Loop to read field information. */ +/* ------------------------------- */ +/* Initialise, then loop to read the values of up to three fields and + to identify the separators that accompany them. */ + angle_or_time = 0; + last_sep = '\0'; + next_id = 0; + nfield = 0; + sep_used = '\0'; + suffix_sep = 0; + sep_len = 0; + for ( ifield = 0; ifield < 3; ifield++ ) { + +/* Set the default field value. */ + field[ ifield ] = 0.0; + +/* If a prefix separator was identified for the second and subsequent + fields (when the previous field was being read), then step over the + prefix, including any following white space. */ + if ( ifield && !suffix_sep ) s += sep_len; + +/* Note where in the input string the field's numerical value + starts. */ + field_start[ ifield ] = s; + +/* Each field must consist of a string of digits, possibly surrounded + by white space, except that an optional decimal point may also be + present (in which case it indicates the final field). Since we want + to exclude signs, etc. from these fields, we must first identify a + valid sequence of digits, before attempting to read them as a number. + Start by assuming that we will find a decimal point but not an + exponent. */ + decimal = 1; + exponent = 0; + +/* Match a field and obtain its value. */ +/* ----------------------------------- */ +/* Look for a character sequence like "12.345", or similar, setting a + flag to identify a match. */ + n = 0; + match = ( 0 == astSscanf( s, "%*[0123456789].%*[0123456789]%n", &n ) ) + && n; + +/* If that failed, then look for a sequence like "12.", or similar. */ + if ( !match ) { + n = 0; + match = ( 0 == astSscanf( s, "%*[0123456789].%n", &n ) ) && n; + } + +/* If that also failed, then look for a sequence like ".12", or similar. */ + if ( !match ) { + n = 0; + match = ( 0 == astSscanf( s, ".%*[0123456789]%n", &n ) ) && n; + } + +/* If that also failed, then look for a sequence containing digits only. */ + if ( !match ) { + n = 0; + match = ( 0 == astSscanf( s, "%*[0123456789]%n", &n ) ) && n; + +/* Note we have not found a decimal point. */ + decimal = 0; + } + +/* Now look for numbers that end with an exponent. First check that the + string starts with a sequence of digits with or without a decimal point. */ + if( match ) { + +/* See if the numbers are followed by an exponent with an explicit sign + character. If so, increment the number of characters in the numerical + string prefix. */ + m = 0; + if( ( 0 == astSscanf( s + n, "%*1[Ee]%*1[+-]%*[0123456789]%n", &m ) ) + && m ) { + n += m; + exponent = 1; + +/* If the above check failed, see if the numbers are followed by an exponent + without an explicit sign character. If so, increment the number of + characters in the numerical string prefix. */ + } else { + m = 0; + if( ( 0 == astSscanf( s + n, "%*1[Ee]%*[0123456789]%n", &m ) ) + && m ) { + n += m; + exponent = 1; + } + } + } + +/* If we identified a suitable sequence of characters above, we will + now read them as a number. To prevent any subsequent characters + being included as part of this number, the field width must be + restricted to the length of the sequence we found. Write a format + specification to read a double with this field width, followed by + optional white space, and to return the total number of characters + read. */ + nread = 0; + if ( match ) { + (void) sprintf( fmtbuf, "%%%dlf %%n", n ); + +/* Use this format specification to read the field value. If + successful, increment the string pointer to the next significant + character. */ + if ( 1 == astSscanf( s, fmtbuf, field + ifield, &nread ) ) s += nread; + } + +/* Note the total number of characters read up to the end of the + numerical value in this field (including any following white + space). */ + nc = s - string; + +/* Identify the following separator. */ +/* --------------------------------- */ +/* We will now attempt to identify the field separator (if any) which + follows the field we have just read. By default, we behave as if + the separator is a space. Note we have actually found a space (at + least) if extra white space characters were read as part of the + field value above. */ + sep = ' '; + good_sep = ( nread > n ); + +/* Look for one of the recognised separator characters. If one is + found, save a copy of it and note we appear (so far) to have a + valid separator. */ + sep_len = 0; + if ( *s && strchr( sep_list, *s ) ) { + sep = *s; + good_sep = 1; + +/* Set "sep_len" to the number of characters associated with the + separator. This includes any following white space. */ + while ( isspace( (int) s[ ++sep_len ] ) ); + } + +/* Identify the separator character by looking it up in the separator + list (this just uses a space if no valid separator has been + found). */ + sep_index = strchr( sep_list, sep ) - sep_list; + +/* Determine if the separator can be used to identify the field which + preceded it and if it allows us to determine whether an angle or a + time is being read. Both of these properties are specified in data + tables (with zero indicating that the separator didn't supply any + information). */ + sep_field_id = field_id_list[ sep_index ]; + sep_angle_or_time = angle_or_time_list[ sep_index ]; + +/* Validate the separator. */ +/* ----------------------- */ +/* Now perform further checks that the separator is valid + (i.e. conforms to the required syntax). If it appears to identify + the previous field (i.e. is a "suffix" separator like "m" or "s"), + then it is valid only if its field ID is no less than the ID value + that would be used next, based on previous fields (if any), and no + less than the current field number. This ensures that fields occur + in the correct order without duplication. */ + if ( good_sep ) { + if ( sep_field_id ) { + good_sep = ( sep_field_id >= next_id ) && + ( sep_field_id > ifield ); + +/* Otherwise (i.e. we appear to have a "prefix" separator like ":" or + " "), it is valid if it is the first one used, or if it matches the + previous one used. Keep a note of the first such separator used for + checking subsequent ones. */ + } else { + good_sep = !sep_used || ( sep == sep_used ); + if ( !sep_used ) sep_used = sep; + } + } + +/* If the separator seems OK and we don't yet know whether we are reading + an angle or a time, then use whatever information the separator + provides about this. */ + if ( good_sep ) { + if ( !angle_or_time ) { + angle_or_time = sep_angle_or_time; + +/* If we already know whether we are reading an angle or a time and + the current separator also contains information about this, then + check that these sources of information are compatible. This + prevents inconsistent use of angle/time field separators. */ + } else { + good_sep = !sep_angle_or_time || + ( sep_angle_or_time == angle_or_time ); + } + } + +/* Update the count of characters read for this field and note if we + have identified a valid suffix separator. */ + if ( good_sep ) nread += sep_len; + suffix_sep = good_sep && sep_field_id; + +/* Identify which field was read. */ +/* ------------------------------ */ +/* If we have a valid suffix separator, store the field ID. Also make + a note of the ID to use for the next field. */ + if ( suffix_sep ) { + field_id[ ifield ] = sep_field_id; + next_id = sep_field_id + 1; + +/* Step over the separator (plus any following white space) and update + the total number of characters read (prefix separators are not + accounted for until we start to read the next field). */ + s += sep_len; + nc = s - string;; + +/* If the separator does not identify the current field, then assign a + field ID based on the previous field (if any). Update the ID to use + for the next field, if known. */ + } else { + field_id[ ifield ] = next_id; + if ( next_id ) next_id++; + } + +/* Count fields and exit when done. */ +/* -------------------------------- */ +/* If no characters have been read for the current field, then + disregard the field if: (a) it is the first one (i.e. there is + nothing to read), or (b) it follows a white space separator + (because trailing space does not delimit an extra field). In either + case, we have now read all the fields. Otherwise, increment the + count of fields read. */ + final = 0; + if ( !nread && ( !ifield || isspace( (int) last_sep ) ) ) { + final = 1; + } else { + nfield++; + } + +/* We have also read all the fields if: (a) the last one contained a + decimal point, or (b) the last one ended with an exponent, or (c) + the next character is not a valid field separator, or (d) we have + read the seconds field so the next field ID would exceed 3. */ + final = final || decimal || exponent || !good_sep || ( next_id > 3 ); + +/* Quit reading if we have read the final field. Otherwise, save the + separator character and attempt to read the next field. */ + if ( final ) break; + last_sep = sep; + } + +/* Complete the identification of fields. */ +/* -------------------------------------- */ +/* Although we have propagated field IDs from earlier ones to later + ones in the loop above, we have still not done the reverse. This + means there there may still be some leading fields which have not + been positively identified (i.e. still have a field ID of zero). In + fact, all the fields we have read might still be unidentified at + this point. */ + +/* Calculate the field ID that would apply to the final field we have + read in the absence of any other information. This depends on the + number of leading fields that are expected to be missing. */ + next_id = nfield + ( dh ? 0 : ( min ? 1 : 2 ) ); + if ( next_id > 3 ) next_id = 3; + +/* Loop through the fields in reverse order, propagating any positive + identifications backwards towards the first field. If no fields + have been positively identified, then they are simply numbered + consecutively based on the value calculated above. */ + for ( ifield = nfield - 1; ifield >= 0; ifield-- ) { + if ( field_id[ ifield ] ) { + next_id = field_id[ ifield ] - 1; + } else { + field_id[ ifield ] = next_id--; + } + } + +/* Handle inability to read any value. */ +/* ----------------------------------- */ +/* If no fields were read, then check to see if we are trying to read + the string "" (or similar) possibly surrounded by, or + containing, white space. If so, return the coordinate value + AST__BAD. */ + if ( !nfield ) { + if ( n = 0, + ( 0 == astSscanf( string, " < %*1[Bb] %*1[Aa] %*1[Dd] > %n", &n ) + && n ) ) { + *value = AST__BAD; + nc = n; + +/* If the string still cannot be read, then return a function value of + zero. */ + } else { + nc = 0; + } + +/* Finally determine angle or time. */ +/* -------------------------------- */ +/* If one or more fields have been read, check if we know whether to + interpret the value as an angle or a time (if not, we continue to + use the default choice obtained from the SkyAxis Format string). */ + } else { + if ( angle_or_time ) as_time = ( angle_or_time == 2 ); + +/* Validate field values. */ +/* ---------------------- */ +/* If OK, check all fields except the first one for a valid value (we + allow the first field to be unconstrained, so that angles and times + outside the conventional ranges can be represented). We only need + to test for values over 60.0, since negative values can't be + read. */ + if ( astOK ) { + for ( ifield = 1; ifield < nfield; ifield++ ) { + if ( field[ ifield ] >= 60.0 ) { + +/* If a suspect field is found, we must now re-read it. This is + because values like "59.9999..." are valid, even if they round up + to 60, whereas "60" isn't. To distinguish these cases, we read the + digits that occur before the decimal point (if any). Determine how + many such digits there are. */ + n = 0; + if ( ( 0 == astSscanf( field_start[ ifield ], + "%*[0123456789]%n", &n ) ) && n ) { + +/* If there are none (this shouldn't happen), the field is + valid. Otherwise, construct a format specification to read these + digits as a floating point number. */ + (void) sprintf( fmtbuf, "%%%dlf", n ); + +/* Read the digits and compare the result with 60.0. Report an error + and quit if necessary, limiting the string length in the error + message to include just the significant characters in the value + read. */ + if ( ( 1 == astSscanf( field_start[ ifield ], fmtbuf, + &testval ) ) + && ( testval >= 60.0 ) ) { + nchar = nc - ( string_start - string ); + for ( i = len = 0; i < nchar; i++ ) { + if ( !isspace( (int) string_start[ i ] ) ) { + len = i + 1; + } + } + astError( AST__UNFER, "Invalid %s%s value in sky " + "coordinate \"%.*s\".", status, as_time ? "" : "arc", + ( field_id[ ifield ] == 2 ) ? "minutes" : + "seconds", + len, string_start ); + break; + } + } + } + } + } + +/* Calculate final result. */ +/* ----------------------- */ +/* If OK, calculate the result by summing the field values and converting + to radians. */ + if ( astOK ) { + *value = 0.0; + for ( ifield = 0; ifield < nfield; ifield++ ) { + *value += field[ ifield ] * + fieldvalue[ field_id[ ifield ] - 1 ] * + ( as_time ? hr2rad : deg2rad ); + } + +/* Change sign if necessary. */ + if ( !positive ) *value = - *value; + } + } + } + +/* If an error occurred, set the number of characters read to zero. */ + if ( !astOK ) nc = 0; + +/* Return the number of characters read. */ + return nc; + +/* Undefine macros local to this function. */ +#undef FMT_LEN +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with the + SkyAxis class using the macros defined for this purpose in the "object.h" + file. For a description of each attribute, see the class interface (in the + associated .h file). */ + +/* AsTime. */ +/* ------- */ +/* The value is constrained to be -INT_MAX, 0 or 1, with -INT_MAX for + "undefined". The default value is 0 unless the "IsLatitude" + attribute has been explicitly set to 0, in which case "AsTime" + defaults to 1. */ +astMAKE_CLEAR(SkyAxis,AxisAsTime,as_time,-INT_MAX) +astMAKE_GET(SkyAxis,AxisAsTime,int,0,( ( this->as_time != -INT_MAX ) ? + this->as_time : + ( astTestAxisIsLatitude( this ) && + !astGetAxisIsLatitude( this ) ) )) +astMAKE_SET(SkyAxis,AxisAsTime,int,as_time,( value != 0 )) +astMAKE_TEST(SkyAxis,AxisAsTime,( this->as_time != -INT_MAX )) + +/* IsLatitude. */ +/* ----------- */ +/* The value is constrained to be -INT_MAX, 0 or 1, with -INT_MAX for + "undefined". The default value is 0. */ +astMAKE_CLEAR(SkyAxis,AxisIsLatitude,is_latitude,-INT_MAX) +astMAKE_GET(SkyAxis,AxisIsLatitude,int,0,( this->is_latitude != -INT_MAX ? + this->is_latitude : 0 )) +astMAKE_SET(SkyAxis,AxisIsLatitude,int,is_latitude,( value != 0 )) +astMAKE_TEST(SkyAxis,AxisIsLatitude,( this->is_latitude != -INT_MAX )) + +/* CentreZero. */ +/* ----------- */ +/* The value is constrained to be -INT_MAX, 0 or 1, with -INT_MAX for + "undefined". The default value is equal to the value of IsLatitude. */ +astMAKE_CLEAR(SkyAxis,AxisCentreZero,centrezero,-INT_MAX) +astMAKE_GET(SkyAxis,AxisCentreZero,int,0,( this->centrezero != -INT_MAX ? + this->centrezero : astGetAxisIsLatitude( this ) )) +astMAKE_SET(SkyAxis,AxisCentreZero,int,centrezero,( value != 0 )) +astMAKE_TEST(SkyAxis,AxisCentreZero,( this->centrezero != -INT_MAX )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SkyAxis objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SkyAxis objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstSkyAxis *in; /* Pointer to input SkyAxis */ + AstSkyAxis *out; /* Pointer to output SkyAxis */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SkyAxis structures. */ + in = (AstSkyAxis *) objin; + out = (AstSkyAxis *) objout; + +/* For safety, first clear any references to the input memory from + the output SkyAxis. */ + out->skyformat = NULL; + +/* Make copies of the allocated strings. */ + if ( in->skyformat ) out->skyformat = astStore( NULL, in->skyformat, + strlen( in->skyformat ) + (size_t) 1 ); + +/* If an error occurred, clean up by freeing all memory allocated above. */ + if ( !astOK ) { + out->skyformat = astFree( out->skyformat ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SkyAxis objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SkyAxis objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) obj; + +/* Free all allocated memory. */ + this->skyformat = astFree( this->skyformat ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SkyAxis objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SkyAxis class to an output Channel. + +* Parameters: +* this +* Pointer to the SkyAxis whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstAxis *this_axis; /* Pointer to Axis structure */ + AstSkyAxis *this; /* Pointer to the SkyAxis structure */ + const char *sval; /* Pointer to string value */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyAxis structure. */ + this = (AstSkyAxis *) this_object; + +/* Write out values representing the instance variables for the + SkyAxis class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Format. */ +/* ------- */ +/* We must write out the Format value stored locally as it over-rides + that provided by the Axis class. */ + this_axis = (AstAxis *) this; + set = TestAxisFormat( this_axis, status ); + sval = set ? GetAxisFormat( this_axis, status ) : astGetAxisFormat( this ); + astWriteString( channel, "Format", set, 0, sval, "Format specifier" ); + +/* IsLatitude. */ +/* ----------- */ + set = TestAxisIsLatitude( this, status ); + ival = set ? GetAxisIsLatitude( this, status ) : astGetAxisIsLatitude( this ); + astWriteInt( channel, "IsLat", set, 0, ival, + ival ? "Latitude axis (not longitude)" : + "Longitude axis (not latitude)" ); + +/* CentreZero. */ +/* ----------- */ + set = TestAxisCentreZero( this, status ); + ival = set ? GetAxisCentreZero( this, status ) : astGetAxisCentreZero( this ); + astWriteInt( channel, "CnZer", set, 0, ival, + ival ? "Display axis values in range -PI -> +PI" : + "Display axis values in range 0 -> 2.PI" ); + +/* AsTime. */ +/* ------- */ + set = TestAxisAsTime( this, status ); + ival = set ? GetAxisAsTime( this, status ) : astGetAxisAsTime( this ); + astWriteInt( channel, "AsTime", set, 0, ival, + ival ? "Display values as times (not angles)" : + "Display values as angles (not times)" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASkyAxis and astCheckSkyAxis functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SkyAxis,Axis) +astMAKE_CHECK(SkyAxis) + +AstSkyAxis *astSkyAxis_( const char *options, int *status, ...) { +/* +*+ +* Name: +* astSkyAxis + +* Purpose: +* Create a SkyAxis. + +* Type: +* Public function. + +* Synopsis: +* #include "skyaxis.h" +* AstSkyAxis *astSkyAxis( const char *options, int *status, ... ) + +* Class Membership: +* SkyAxis constructor. + +* Description: +* This function creates a new SkyAxis and optionally initialises its +* attributes. + +* Parameters: +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new SkyAxis. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new SkyAxis. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyAxis *new; /* Pointer to new SkyAxis */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the SkyAxis, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSkyAxis( NULL, sizeof( AstSkyAxis ), !class_init, &class_vtab, + "SkyAxis" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new SkyAxis' + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SkyAxis. */ + return new; +} + +AstSkyAxis *astSkyAxisId_( const char *options, ... ) { +/* +* Name: +* astSkyAxisId_ + +* Purpose: +* Create a SkyAxis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyaxis.h" +* AstSkyAxis *astSkyAxisId_( const char *options, ... ) + +* Class Membership: +* SkyAxis constructor. + +* Description: +* This function implements the external (public) interface to the +* astSkyAxis constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astSkyAxis_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astSkyAxis_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astSkyAxis_. + +* Returned Value: +* The ID value associated with the new SkyAxis. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyAxis *new; /* Pointer to new SkyAxis */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the SkyAxis, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSkyAxis( NULL, sizeof( AstSkyAxis ), !class_init, &class_vtab, + "SkyAxis" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new SkyAxis' + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new SkyAxis. */ + return astMakeId( new ); +} + +AstSkyAxis *astInitSkyAxis_( void *mem, size_t size, int init, + AstSkyAxisVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSkyAxis + +* Purpose: +* Initialise a SkyAxis. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyaxis.h" +* AstSkyAxis *astInitSkyAxis( void *mem, size_t size, int init, +* AstSkyAxisVtab *vtab, const char *name ) + +* Class Membership: +* SkyAxis initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SkyAxis object. It allocates memory (if necessary) to accommodate +* the SkyAxis plus any additional data associated with the derived class. +* It then initialises a SkyAxis structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a SkyAxis at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SkyAxis is to be created. This +* must be of sufficient size to accommodate the SkyAxis data +* (sizeof(SkyAxis)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the SkyAxis (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the SkyAxis +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the SkyAxis's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SkyAxis. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astClass +* method). + +* Returned Value: +* A pointer to the new SkyAxis. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSkyAxis *new; /* Pointer to the new SkyAxis */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSkyAxisVtab( vtab, name ); + +/* Initialise an Axis structure (the parent class) as the first component + within the SkyAxis structure, allocating memory if necessary. */ + new = (AstSkyAxis *) astInitAxis( mem, size, 0, (AstAxisVtab *) vtab, + name ); + + if ( astOK ) { + +/* Initialise the SkyAxis data. */ +/* ---------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->as_time = -INT_MAX; + new->is_latitude = -INT_MAX; + new->centrezero = -INT_MAX; + new->skyformat = NULL; + +/* If an error occurred, clean up by deleting the new SkyAxis. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SkyAxis. */ + return new; +} + +AstSkyAxis *astLoadSkyAxis_( void *mem, size_t size, + AstSkyAxisVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSkyAxis + +* Purpose: +* Load a SkyAxis. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyaxis.h" +* AstSkyAxis *astLoadSkyAxis( void *mem, size_t size, +* AstSkyAxisVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SkyAxis loader. + +* Description: +* This function is provided to load a new SkyAxis using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SkyAxis structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a SkyAxis at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the SkyAxis is to be +* loaded. This must be of sufficient size to accommodate the +* SkyAxis data (sizeof(SkyAxis)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SkyAxis (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SkyAxis structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSkyAxis) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SkyAxis. If this is NULL, a pointer +* to the (static) virtual function table for the SkyAxis class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SkyAxis" is used instead. + +* Returned Value: +* A pointer to the new SkyAxis. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyAxis *new; /* Pointer to the new SkyAxis */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SkyAxis. In this case the + SkyAxis belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSkyAxis ); + vtab = &class_vtab; + name = "SkyAxis"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSkyAxisVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SkyAxis. */ + new = astLoadAxis( mem, size, (AstAxisVtab *) vtab, name, channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SkyAxis" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Format. */ +/* ------- */ +/* Note that string values do not require any additional processing. */ + new->skyformat = astReadString( channel, "format", NULL ); + +/* IsLatitude. */ +/* ----------- */ + new->is_latitude = astReadInt( channel, "islat", -INT_MAX ); + if ( TestAxisIsLatitude( new, status ) ) { + SetAxisIsLatitude( new, new->is_latitude, status ); + } + +/* CentreZero. */ +/* ----------- */ + new->centrezero = astReadInt( channel, "cnzer", -INT_MAX ); + if ( TestAxisCentreZero( new, status ) ) { + SetAxisCentreZero( new, new->centrezero, status ); + } + +/* AsTime. */ +/* ------- */ + new->as_time = astReadInt( channel, "astime", -INT_MAX ); + if ( TestAxisAsTime( new, status ) ) SetAxisAsTime( new, new->as_time, status ); + +/* If an error occurred, clean up by deleting the new SkyAxis. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SkyAxis pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* (No more to define at present.) */ + + + + + diff --git a/skyaxis.h b/skyaxis.h new file mode 100644 index 0000000..5c2f7ac --- /dev/null +++ b/skyaxis.h @@ -0,0 +1,428 @@ +#if !defined( SKYAXIS_INCLUDED ) /* Include this file only once */ +#define SKYAXIS_INCLUDED +/* +*+ +* Name: +* skyaxis.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SkyAxis class. + +* Invocation: +* #include "skyaxis.h" + +* Description: +* This include file defines the interface to the SkyAxis class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The SkyAxis class extends the Axis class to represent angles on +* the sky measured in radians. It provides alternative formatting +* facilities for representing these coordinate values either as +* angles (in degrees) or as time (in hours) using sexagesimal +* notation. It also provides alternative defaults for certain +* attributes and adds new attributes and methods of its own which +* are needed to manipulate angular coordinates on the sky. + +* Inheritance: +* The SkyAxis class inherits from the Axis class. + +* Attributes Over-Ridden: +* Format (string) +* The SkyAxis class defines a new syntax for this string. +* Label (string) +* The SkyAxis class defines new default values. These may +* depend on other attribute settings. +* Symbol (string) +* The SkyAxis class defines new default values. These may +* depend on other attribute settings. +* Unit (string) +* The SkyAxis class defines new default values. These may +* depend on other attribute settings. + +* New Attributes Defined: +* AsTime (integer) +* A boolean value which indicates whether SkyAxis coordinate +* values should be formatted for display as times (instead of +* angles). It is used to determine the default format to use if +* no explicit value has been set for the Format attribute. +* CentreZero (integer) +* A boolean value which indicates whether a SkyAxis value should +* be normalised into the range [-PI,+PI] or [0,2.PI] when astNorm +* is used. +* IsLatitude (integer) +* A boolean value which indicates whether a SkyAxis is a +* latitude axis (as opposed to a longitude axis). It is used to +* determine default axis labels and symbols. It also determines the +* default value for the "AsTime" attribute (since longitudes on +* the sky are usually expressed as times). + +* Methods Over-Ridden: +* Public: +* astAxisFormat +* Format a coordinate value for a SkyAxis. +* astAxisNorm +* Normalise a SkyAxis coordinate value. +* astAxisUnformat +* Read a formatted coordinate value for a SkyAxis. + +* Protected: +* astAxisAbbrev +* Abbreviate a formatted SkyAxis value by skipping leading fields. +* astAxisDistance +* Find the distance between two SkyAxis values. +* astAxisGap +* Find a "nice" gap for tabulating SkyAxis values. +* astClearAxisFormat +* Clear the Format attribute for a SkyAxis. +* astGetAxisDirection +* Obtain the value of the Direction attribute for a SkyAxis. +* astGetAxisFormat +* Obtain a pointer to the Format attribute for a SkyAxis. +* astGetAxisLabel +* Obtain a pointer to the Label attribute for a SkyAxis. +* astGetAxisSymbol +* Obtain a pointer to the Symbol attribute for a SkyAxis. +* astGetAxisUnit +* Obtain a pointer to the Unit attribute for a SkyAxis. +* astSetAxisFormat +* Set a value for the Format attribute of a SkyAxis. +* astTestAxisFormat +* Test if a value has been set for the Format attribute of a SkyAxis. +* astAxisOffset +* Add an increment onto a supplied SkyAxis value. +* astAxisOverlay +* Overlay the attributes of a template SkyAxis on to another Axis. +* astSetAttrib +* Set an attribute value for a SkyAxis. + +* New Methods Defined: +* Public: +* None. + +* Protected: +* astClearAxisAsTime +* Clear the AsTime attribute for a SkyAxis. +* astClearAxisCentreZero +* Clear the CentreZero attribute for a SkyAxis. +* astClearAxisIsLatitude +* Clear the IsLatitude attribute for a SkyAxis. +* astGetAxisAsTime +* Obtain the value of the AsTime attribute for a SkyAxis. +* astGetAxisIsLatitude +* Obtain the value of the IsLatitude attribute for a SkyAxis. +* astGetAxisCentreZero +* Obtain the value of the CentreZero attribute for a SkyAxis. +* astSetAxisAsTime +* Set a value for the AsTime attribute of a SkyAxis. +* astSetAxisIsLatitude +* Set a value for the IsLatitude attribute of a SkyAxis. +* astSetAxisCentreZero +* Set a value for the CentreZero attribute of a SkyAxis. +* astTestAxisAsTime +* Test if a value has been set for the AsTime attribute of a SkyAxis. +* astTestAxisIsLatitude +* Test if a value has been set for the IsLatitude attribute of a +* SkyAxis. +* astTestAxisCentreZero +* Test if a value has been set for the CentreZero attribute of a +* SkyAxis. + +* Other Class Functions: +* Public: +* astIsASkyAxis +* Test class membership. +* astSkyAxis +* Create an SkyAxis. + +* Protected: +* astCheckSkyAxis +* Validate class membership. +* astInitSkyAxis +* Initialise an SkyAxis. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstSkyAxis +* SkyAxis object type. + +* Protected: +* AstSkyAxisVtab +* SkyAxis virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 29-MAR-1996 (RFWS): +* Original version. +* 25-APR-1996 (RFWS): +* Made all attribute access functions protected. +* 13-MAY-1996 (RFWS): +* Documented over-riding of the astGetAxisDirection method. +* 26-FEB-1998 (RFWS): +* Over-ride the astAxisUnformat method. +* 8-JAN-2003 (DSB): +* Added protected astInitSkyAxisVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "axis.h" /* Coordinate axes (parent class) */ + +/* Macros */ +/* ====== */ + +/* Define constants used to size global arrays in this module. */ +/* Define numerical constants for use in thie module. */ +#define AST__SKYAXIS_GETAXISFORMAT_BUFF_LEN 50 +#define AST__SKYAXIS_DHMSFORMAT_BUFF_LEN 70 +#define AST__SKYAXIS_DHMSUNIT_BUFF_LEN 17 +#define AST__SKYAXIS_GETATTRIB_BUFF_LEN 50 + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + + +/* Type Definitions. */ +/* ================= */ +/* SkyAxis structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstSkyAxis { + +/* Attributes inherited from the parent class. */ + AstAxis axis; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + char *skyformat; /* Pointer to sky format string */ + int as_time; /* Format angles as time (hours)? */ + int is_latitude; /* SkyAxis is a latitude axis? */ + int centrezero; /* Normalised range is zero-centred? */ +} AstSkyAxis; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSkyAxisVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstAxisVtab axis_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* GetAxisAsTime)( AstSkyAxis *, int * ); + int (* GetAxisIsLatitude)( AstSkyAxis *, int * ); + int (* GetAxisCentreZero)( AstSkyAxis *, int * ); + int (* TestAxisAsTime)( AstSkyAxis *, int * ); + int (* TestAxisIsLatitude)( AstSkyAxis *, int * ); + int (* TestAxisCentreZero)( AstSkyAxis *, int * ); + void (* ClearAxisAsTime)( AstSkyAxis *, int * ); + void (* ClearAxisIsLatitude)( AstSkyAxis *, int * ); + void (* ClearAxisCentreZero)( AstSkyAxis *, int * ); + void (* SetAxisAsTime)( AstSkyAxis *, int, int * ); + void (* SetAxisIsLatitude)( AstSkyAxis *, int, int * ); + void (* SetAxisCentreZero)( AstSkyAxis *, int, int * ); +} AstSkyAxisVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstSkyAxisGlobals { + AstSkyAxisVtab Class_Vtab; + int Class_Init; + char DHmsFormat_Buff[ AST__SKYAXIS_DHMSFORMAT_BUFF_LEN + 1 ]; + char DHmsUnit_Buff[ AST__SKYAXIS_DHMSUNIT_BUFF_LEN + 1 ]; + char GetAttrib_Buff[ AST__SKYAXIS_GETATTRIB_BUFF_LEN + 1 ]; + char GetAxisFormat_Buff[ AST__SKYAXIS_GETAXISFORMAT_BUFF_LEN + 1 ]; + char *GhDelim; + char *GmDelim; + char *GsDelim; + char *GdDelim; + char *GamDelim; + char *GasDelim; +} AstSkyAxisGlobals; + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SkyAxis) /* Check class membership */ +astPROTO_ISA(SkyAxis) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstSkyAxis *astSkyAxis_( const char *, int *, ...); +#else +AstSkyAxis *astSkyAxisId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSkyAxis *astInitSkyAxis_( void *, size_t, int, AstSkyAxisVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitSkyAxisVtab_( AstSkyAxisVtab *, const char *, int * ); + +/* Loader. */ +AstSkyAxis *astLoadSkyAxis_( void *, size_t, AstSkyAxisVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitSkyAxisGlobals_( AstSkyAxisGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +#if defined(astCLASS) /* Protected */ +int astGetAxisAsTime_( AstSkyAxis *, int * ); +int astGetAxisIsLatitude_( AstSkyAxis *, int * ); +int astGetAxisCentreZero_( AstSkyAxis *, int * ); +int astTestAxisAsTime_( AstSkyAxis *, int * ); +int astTestAxisIsLatitude_( AstSkyAxis *, int * ); +int astTestAxisCentreZero_( AstSkyAxis *, int * ); +void astClearAxisAsTime_( AstSkyAxis *, int * ); +void astClearAxisIsLatitude_( AstSkyAxis *, int * ); +void astClearAxisCentreZero_( AstSkyAxis *, int * ); +void astSetAxisAsTime_( AstSkyAxis *, int, int * ); +void astSetAxisIsLatitude_( AstSkyAxis *, int, int * ); +void astSetAxisCentreZero_( AstSkyAxis *, int, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSkyAxis(this) astINVOKE_CHECK(SkyAxis,this,0) +#define astVerifySkyAxis(this) astINVOKE_CHECK(SkyAxis,this,1) + +/* Test class membership. */ +#define astIsASkyAxis(this) astINVOKE_ISA(SkyAxis,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astSkyAxis astINVOKE(F,astSkyAxis_) +#else +#define astSkyAxis astINVOKE(F,astSkyAxisId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSkyAxis(mem,size,init,vtab,name) \ +astINVOKE(O,astInitSkyAxis_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSkyAxisVtab(vtab,name) astINVOKE(V,astInitSkyAxisVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSkyAxis(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSkyAxis_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ + +/* Here we make use of astCheckSkyAxis to validate SkyAxis pointers + before use. This provides a contextual error report if a pointer to + the wrong sort of object is supplied. */ +#if defined(astCLASS) /* Protected */ + +#define astClearAxisAsTime(this) \ +astINVOKE(V,astClearAxisAsTime_(astCheckSkyAxis(this),STATUS_PTR)) +#define astClearAxisIsLatitude(this) \ +astINVOKE(V,astClearAxisIsLatitude_(astCheckSkyAxis(this),STATUS_PTR)) +#define astGetAxisAsTime(this) \ +astINVOKE(V,astGetAxisAsTime_(astCheckSkyAxis(this),STATUS_PTR)) +#define astGetAxisIsLatitude(this) \ +astINVOKE(V,astGetAxisIsLatitude_(astCheckSkyAxis(this),STATUS_PTR)) +#define astSetAxisAsTime(this,value) \ +astINVOKE(V,astSetAxisAsTime_(astCheckSkyAxis(this),value,STATUS_PTR)) +#define astSetAxisIsLatitude(this,value) \ +astINVOKE(V,astSetAxisIsLatitude_(astCheckSkyAxis(this),value,STATUS_PTR)) +#define astTestAxisAsTime(this) \ +astINVOKE(V,astTestAxisAsTime_(astCheckSkyAxis(this),STATUS_PTR)) +#define astTestAxisIsLatitude(this) \ +astINVOKE(V,astTestAxisIsLatitude_(astCheckSkyAxis(this),STATUS_PTR)) + +#define astClearAxisCentreZero(this) \ +astINVOKE(V,astClearAxisCentreZero_(astCheckSkyAxis(this),STATUS_PTR)) +#define astGetAxisCentreZero(this) \ +astINVOKE(V,astGetAxisCentreZero_(astCheckSkyAxis(this),STATUS_PTR)) +#define astSetAxisCentreZero(this,value) \ +astINVOKE(V,astSetAxisCentreZero_(astCheckSkyAxis(this),value,STATUS_PTR)) +#define astTestAxisCentreZero(this) \ +astINVOKE(V,astTestAxisCentreZero_(astCheckSkyAxis(this),STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/skyframe.c b/skyframe.c new file mode 100644 index 0000000..cf651c5 --- /dev/null +++ b/skyframe.c @@ -0,0 +1,12440 @@ +/* +*class++ +* Name: +* SkyFrame + +* Purpose: +* Celestial coordinate system description. + +* Constructor Function: +c astSkyFrame +f AST_SKYFRAME + +* Description: +* A SkyFrame is a specialised form of Frame which describes +* celestial longitude/latitude coordinate systems. The particular +* celestial coordinate system to be represented is specified by +* setting the SkyFrame's System attribute (currently, the default +* is ICRS) qualified, as necessary, by a mean Equinox value and/or +* an Epoch. +* +* For each of the supported celestial coordinate systems, a SkyFrame +* can apply an optional shift of origin to create a coordinate system +* representing offsets within the celestial coordinate system from some +* specified reference point. This offset coordinate system can also be +* rotated to define new longitude and latitude axes. See attributes +* SkyRef, SkyRefIs, SkyRefP and AlignOffset. +* +* All the coordinate values used by a SkyFrame are in +* radians. These may be formatted in more conventional ways for +c display by using astFormat. +f display by using AST_FORMAT. +* For a SkyFrame, the Unit attribute describes the formatted value of +* a SkyFrame axis, and may for instance be "h:m:s", indicating that a +* formatted axis value contains colon-separated fields for hours, minutes +* and seconds. On the other hand, the InternalUnit attribute for a +* SkyFrame is always set to "rad" (i.e. radians), indicating that the +* unformatted (i.e. floating point) axis values used by application code +* are always in units of radians + +* Inheritance: +* The SkyFrame class inherits from the Frame class. + +* Attributes: +* In addition to those attributes common to all Frames, every +* SkyFrame also has the following attributes: +* +* - AlignOffset: Align SkyFrames using the offset coordinate system? +* - AsTime(axis): Format celestial coordinates as times? +* - Equinox: Epoch of the mean equinox +* - IsLatAxis: Is the specified axis the latitude axis? +* - IsLonAxis: Is the specified axis the longitude axis? +* - LatAxis: Index of the latitude axis +* - LonAxis: Index of the longitude axis +* - NegLon: Display longitude values in the range [-pi,pi]? +* - Projection: Sky projection description. +* - SkyRef: Position defining location of the offset coordinate system +* - SkyRefIs: Selects the nature of the offset coordinate system +* - SkyRefP: Position defining orientation of the offset coordinate system +* - SkyTol: Smallest significant shift in sky coordinates + +* Functions: +* In addition to those +c functions +f routines +* applicable to all Frames, the following +c functions +f routines +* may also be applied to all SkyFrames: +* +c - astSkyOffsetMap: Obtain a Mapping from absolute to offset coordinates +f - AST_SKYOFFSETMAP: Obtain a Mapping from absolute to offset coordinates + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) +* BEC: Brad Cavanagh (JAC, Hawaii) + +* History: +* 4-MAR-1996 (RFWS): +* Original version. +* 17-MAY-1996 (RFWS): +* Tidied up, etc. +* 31-JUL-1996 (RFWS): +* Added support for attributes and a public interface. +* 11-SEP-1996 (RFWS): +* Added Gap (written by DSB). +* 24-SEP-1996 (RFWS): +* Added I/O facilities. +* 27-FEB-1997 (RFWS): +* Improved the public prologues. +* 27-MAY-1997 (RFWS): +* Modified to use a new public interface to the SlaMap class +* and to use the astSimplify method to remove redundant +* conversions. +* 16-JUN-1997 (RFWS): +* Fixed bug in axis associations returned by astMatch if axes +* were swapped. +* 16-JUL-1997 (RFWS): +* Added Projection attribute. +* 14-NOV-1997 (RFWS): +* Corrected the omission of axis permutations from astNorm. +* 21-JAN-1998 (RFWS): +* Ensure that Title and Domain values appropriate to a SkyFrame +* are preserved if a Frame result is generated by SubFrame. +* 26-FEB-1998 (RFWS): +* Over-ride the astUnformat method. +* 3-APR-2001 (DSB): +* Added "Unknown" option for the System attribute. Added read-only +* attributes LatAxis and LonAxis. +* 21-JUN-2001 (DSB): +* Added astAngle and astOffset2. +* 4-SEP-2001 (DSB): +* Added NegLon attribute, and astResolve method. +* 9-SEP-2001 (DSB): +* Added astBear method. +* 21-SEP-2001 (DSB): +* Removed astBear method. +* 10-OCT-2002 (DSB): +* Moved definitions of macros for SkyFrame system values from +* this file into skyframe.h. +* 24-OCT-2002 (DSB): +* Modified MakeSkyMapping so that any two SkyFrames with system=unknown +* are assumed to be related by a UnitMap. previously, they were +* considered to be unrelated, resulting in no ability to convert from +* one to the other. This could result for instance in astConvert +* being unable to find a maping from a SkyFrame to itself. +* 15-NOV-2002 (DSB): +* Moved System and Epoch attributes to the Frame class. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitSkyFrameVtab +* method. +* 11-JUN-2003 (DSB): +* Added ICRS option for System attribute, and made it the default +* in place of FK5. +* 27-SEP-2003 (DSB): +* Added HELIOECLIPTIC option for System attribute. +* 19-APR-2004 (DSB): +* Added SkyRef, SkyRefIs, SkyRefP and AlignOffset attributes. +* 8-SEP-2004 (DSB): +* Added astResolvePoints method. +* 2-DEC-2004 (DSB): +* Added System "J2000" +* 27-JAN-2005 (DSB): +* Fix memory leaks in astLoadSkyFrame_ and Match. +* 7-APR-2005 (DSB): +* Allow SkyRefIs to be set to "Ignored". +* 12-MAY-2005 (DSB): +* Override astNormBox method. +* 15-AUG-2005 (DSB): +* Added AZEL system. +* 13-SEP-2005 (DSB): +* Override astClearSystem so that SkyRef/SkyRefPcan be converted +* from the original System to the default System. +* 19-SEP-2005 (DSB): +* Changed default for SkyRefIs from ORIGIN to IGNORED. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 22-FEB-2006 (DSB): +* Store the Local Apparent Sidereal Time in the SkyFrame structure +* in order to avoid expensive re-computations. +* 22-AUG-2006 (DSB): +* Ensure the cached Local Apparent Siderial Time is initialised +* when initialising or loading a SkyFrame. +* 22-SEP-2006 (DSB): +* Report an error in SetSystem if it is not possible to convert +* from old to new systems. +* 3-OCT-2006 (DSB): +* Added Equation of Equinoxes to the SkyFrame structure. +* 6-OCT-2006 (DSB): +* - Guard against annulling null pointers in subFrame. +* - Add Dut1 attribute +* - Use linear approximation for LAST over short periods (less +* than 0.001 of a day) +* - Remove Equation of Equinoxes from the SkyFrame structure. +* 10-OCT-2006 (DSB): +* Use "AlOff" instead of "AlignOffset" as the external channel name +* for the AlignOffset attribute. The longer form exceeded the +* limit that can be used by the Channel class. +* 14-OCT-2006 (DSB): +* - Move Dut1 attribute to the Frame class. +* - Use the TimeFrame class to do the TDB->LAST conversions. +* 17-JAN-2007 (DSB): +* - Use a UnitMap to align offset coordinate systems in two +* SkyFrames, regardless of other attribute values. +* - Only align in offset coordinates if both target and template +* have a non-zero value for AlignOffset. +* 23-JAN-2007 (DSB): +* Modified so that a SkyFrame can be used as a template to find a +* SkyFrame contained within a CmpFrame. This involves changes in +* Match and the removal of the local versions of SetMaxAxes and +* SetMinAxes. +* 4-JUL-2007 (DSB): +* Modified GetLast to use the correct solar to sidereal conversion +* factor. As a consequence the largest acceptable epoch gap before +* the LAST needs to be recalculated has been increased. +* 11-JUL-2007 (DSB): +* Override astSetEpoch and astClearEpoch by implementations which +* update the LAST value stored in the SkyFrame. +* 7-AUG-2007 (DSB): +* - Set a value for the CentreZero attribute when extracting a +* SkyAxis from a SkyFrame in function SubFrame. +* - In SubFrame, clear extended attributes such as System after +* all axis attributes have been "fixated. +* 30-AUG-2007 (DSB): +* Override astSetDut1 and astClearDut1 by implementations which +* update the LAST value stored in the SkyFrame. +* 31-AUG-2007 (DSB): +* - Cache the magnitude of the diurnal aberration vector in the +* SkyFrame structure for use when correcting for diurnal aberration. +* - Modify the azel conversions to include correction for diurnal +* aberration. +* - Override astClearObsLat and astSetObsLat by implementations which +* reset the magnitude of the diurnal aberration vector. +* 3-SEP-2007 (DSB): +* In SubFrame, since AlignSystem is extended by the SkyFrame class +* it needs to be cleared before invoking the parent SubFrame +* method in cases where the result Frame is not a SkyFrame. +* 2-OCT-2007 (DSB): +* In Overlay, clear AlignSystem as well as System before calling +* the parent overlay method. +* 10-OCT-2007 (DSB): +* In MakeSkyMapping, correct the usage of variables "system" and +* "align_sys" when aligning in AZEL. +* 18-OCT-2007 (DSB): +* Compare target and template AlignSystem values in Match, rather +* than comparing target and result AlignSystem values in MakeSkyMapping +* (since result is basically a copy of target). +* 27-NOV-2007 (DSB): +* - Modify SetSystem to ensure that SkyRef and SkyRefP position are +* always transformed as absolute values, rather than as offset +* values. +* - Modify SubMatch so that a value of zero is assumed for +* AlignOffset when restoring thre integrity of a FrameSet. +* 15-DEC-2008 (DSB): +* Improve calculation of approximate Local Apparent Sidereal time +* by finding and using the ratio of solar to sidereal time +* independently for each approximation period. +* 14-JAN-2009 (DSB): +* Override the astIntersect method. +* 21-JAN-2009 (DSB): +* Fix mis-use of results buffers for GetFormat and GetAttrib. +* 16-JUN-2009 (DSB): +* All sky coordinate systems currently supported by SkyFrame are +* left handed. So fix GetDirection method to return zero for all +* longitude axes and 1 for all latitude axes. +* 18-JUN-2009 (DSB): +* Incorporate the new ObsAlt attribute. +* 23-SEP-2009 (DSB): +* Allow some rounding error when checking for changes in SetObsLon +* and SetDut1. This reduces the number of times the expensive +* calculation of LAST is performed. +* 24-SEP-2009 (DSB); +* Create a static cache of LAST values stored in the class virtual +* function table. These are used in preference to calculating a new +* value from scratch. +* 25-SEP-2009 (DSB); +* Do not calculate LAST until it is needed. +* 12-OCT-2009 (DSB); +* - Handle 2.PI->0 discontinuity in cached LAST values. +* 12-OCT-2009 (BEC); +* - Fix bug in caching LAST value. +* 31-OCT-2009 (DSB); +* Correct SetCachedLAST to handle cases where the epoch to be +* stored is smaller than any epoch already in the table. +* 24-NOV-2009 (DSB): +* - In CalcLast, only use end values form the table of stored +* LAST values if the corresponding epochs are within 0.001 of +* a second of the required epoch (this tolerance used to be +* 0.1 seconds). +* - Do not clear the cached LAST value in SetEpoch and ClearEpoch. +* 8-MAR-2010 (DSB): +* Add astSkyOffsetMap method. +* 7-APR-2010 (DSB): +* Add IsLatAxis and IsLonAxis attributes. +* 11-MAY-2010 (DSB): +* In SetSystem, clear SkyRefP as well as SkyRef. +* 22-MAR-2011 (DSB): +* Override astFrameGrid method. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +* 23-MAY-2011 (DSB): +* Truncate returned PointSet in function FrameGrid to exclude unused points. +* 24-MAY-2011 (DSB): +* When clearing or setting the System attribute, clear SkyRef rather +* than reporting an error if the Mapping from the old System to the +* new System is unknown. +* 30-NOV-2011 (DSB): +* When aligning two SkyFrames in the system specified by AlignSystem, +* do not assume inappropriate default equinox values for systems +* that are not referred to the equinox specified by the Equinox attribute. +* 26-APR-2012 (DSB): +* - Correct Dump function so that any axis permutation is taken into +* account when dumping SkyFrame attributes that have a separate value +* for each axis (e.g. SkyRef and SkyRefP). +* - Take axis permutation into account when setting a new value +* for attributes that have a separate value for each axis (e.g. +* SkyRef and SkyRefP). +* - Remove the code that overrides ClearEpoch and SetEpoch (these +* overrides have not been needed since the changes made on +* 24/11/2009). +* 27-APR-2012 (DSB): +* - Correct astLoadSkyFrame function so that any axis permutation is +* taken into account when loading SkyFrame attributes that have a +* separate value for each axis. +* 25-JUL-2013 (DSB): +* Use a single table of cached LAST values for all threads, rather +* than a separate table for each thread. The problem with a table per +* thread is that if you have N threads, each table contains only +* one N'th of the total number of cached values, resulting in +* poorer accuracy, and small variations in interpolated LAST value +* depending on the way the cached values are distributed amongst the +* N threads. +* 6-AST-2013 (DSB): +* Fix the use of the read-write lock that is used to serialise +* access to the table of cached LAST values. This bug could +* cause occasional problems where an AST pointer would became +* invalid for no apparent reason. +* 21-FEB-2014 (DSB): +* Rounding errors in the SkyLineDef constructor could result in the line +* between coincident points being given a non-zero length. +* 6-JUL-2015 (DSB): +* Added SkyTol attribute. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SkyFrame + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__FK4 +#define LAST_SYSTEM AST__AZEL + +/* Speed of light (AU per day) (from SLA_AOPPA) */ +#define C 173.14463331 + +/* Ratio between solar and sidereal time (from SLA_AOPPA) */ +#define SOLSID 1.00273790935 + +/* Define values for the different values of the SkyRefIs attribute. */ +#define POLE_STRING "Pole" +#define ORIGIN_STRING "Origin" +#define IGNORED_STRING "Ignored" + +/* Define other numerical constants for use in this module. */ +#define GETATTRIB_BUFF_LEN 200 +#define GETFORMAT_BUFF_LEN 50 +#define GETLABEL_BUFF_LEN 40 +#define GETSYMBOL_BUFF_LEN 20 +#define GETTITLE_BUFF_LEN 200 + +/* A macro which returns a flag indicating if the supplied system is + references to the equinox specified by the Equinox attribute. */ +#define EQREF(system) \ +((system==AST__FK4||system==AST__FK4_NO_E||system==AST__FK5||system==AST__ECLIPTIC)?1:0) + +/* +* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_CLEAR(attr,component,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstSkyFrame *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear_( AstSkyFrame *this, int axis ) +* +* which implement a method for clearing a single value in a specified +* multi-valued attribute for an axis of a SkyFrame. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. Label in "astClearLabelAt"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attr,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstSkyFrame *this, int axis, int *status ) { \ +\ + int axis_p; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astClear" #attr ); \ +\ +/* Assign the "clear" value. */ \ + if( astOK ) { \ + this->component[ axis_p ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attr##_( AstSkyFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,SkyFrame,Clear##attr))( this, axis, status ); \ +} + + +/* +* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_GET(attr,type,bad_value,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstSkyFrame *this, int axis ) +* +* and an external interface function of the form: +* +* astGet_( AstSkyFrame *this, int axis ) +* +* which implement a method for getting a single value from a specified +* multi-valued attribute for an axis of a SkyFrame. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET(attr,type,bad_value,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstSkyFrame *this, int axis, int *status ) { \ + int axis_p; /* Permuted axis index */ \ + type result; /* Result to be returned */ \ +\ +/* Initialise */\ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astGet" #attr ); \ +\ +/* Assign the result value. */ \ + if( astOK ) { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attr##_( AstSkyFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,SkyFrame,Get##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute +* for a SkyFrame. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_SET(attr,type,component,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstSkyFrame *this, int axis, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstSkyFrame *this, int axis, value ) +* +* which implement a method for setting a single value in a specified +* multi-valued attribute for a SkyFrame. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. Label in "astSetLabelAt"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET(attr,type,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstSkyFrame *this, int axis, type value, int *status ) { \ +\ + int axis_p; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astSet" #attr ); \ +\ +/* Store the new value in the structure component. */ \ + if( astOK ) { \ + this->component[ axis_p ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attr##_( AstSkyFrame *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,SkyFrame,Set##attr))( this, axis, value, status ); \ +} + +/* +* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a method to test if a single value has been set in a +* multi-valued attribute for a class. + +* Type: +* Private macro. + +* Synopsis: +* #include "skyframe.h" +* MAKE_TEST(attr,assign,nval) + +* Class Membership: +* Defined by the SkyFrame class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( AstSkyFrame *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest_( AstSkyFrame *this, int axis ) +* +* which implement a method for testing if a single value in a specified +* multi-valued attribute has been set for a class. + +* Parameters: +* attr +* The name of the attribute to be tested, as it appears in the function +* name (e.g. Label in "astTestLabelAt"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. This can use the string "axis" to represent the zero-based +* index of the value within the attribute. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_TEST(attr,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attr( AstSkyFrame *this, int axis, int *status ) { \ + int result; /* Value to return */ \ + int axis_p; /* Permuted axis index */ \ +\ +/* Initialise */ \ + result =0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate and permute the axis index. */ \ + axis_p = astValidateAxis( this, axis, 1, "astTest" #attr ); \ +\ +/* Assign the result value. */ \ + if( astOK ) { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attr##_( AstSkyFrame *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,SkyFrame,Test##attr))( this, axis, status ); \ +} + + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points (for AST__BAD) */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Coordinate permutations */ +#include "cmpmap.h" /* Compound Mappings */ +#include "slamap.h" /* SLALIB sky coordinate Mappings */ +#include "timemap.h" /* Time conversions */ +#include "skyaxis.h" /* Sky axes */ +#include "frame.h" /* Parent Frame class */ +#include "matrixmap.h" /* Matrix multiplication */ +#include "sphmap.h" /* Cartesian<->Spherical transformations */ +#include "skyframe.h" /* Interface definition for this class */ +#include "pal.h" /* SLALIB library interface */ +#include "wcsmap.h" /* Factors of PI */ +#include "timeframe.h" /* Time system transformations */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Type Definitions. */ +/* ================= */ + +/* Cached Line structure. */ +/* ---------------------- */ +/* This structure contains information describing a line segment within a + SkyFrame. It differs from the AstLineDef defined in frame.h because + positions are represented by 3D (x,y,z) cartesian coords rather than + 2D (long,lat) coords. */ + +typedef struct SkyLineDef { + AstFrame *frame; /* Pointer to Frame in which the line is defined */ + double length; /* Line length */ + int infinite; /* Disregard the start and end of the line? */ + double start[3]; /* Unit vector defining start of line */ + double end[3]; /* Unit vector defining end of line */ + double dir[3]; /* Unit vector defining line direction */ + double q[3]; /* Unit vector perpendicular to line */ + double start_2d[2]; + double end_2d[2]; +} SkyLineDef; + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are used or extended by this + class. */ +static AstSystemType (* parent_getalignsystem)( AstFrame *, int * ); +static AstSystemType (* parent_getsystem)( AstFrame *, int * ); +static const char *(* parent_format)( AstFrame *, int, double, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getdomain)( AstFrame *, int * ); +static const char *(* parent_getformat)( AstFrame *, int, int * ); +static const char *(* parent_getlabel)( AstFrame *, int, int * ); +static const char *(* parent_getsymbol)( AstFrame *, int, int * ); +static const char *(* parent_gettitle)( AstFrame *, int * ); +static const char *(* parent_getunit)( AstFrame *, int, int * ); +static double (* parent_gap)( AstFrame *, int, double, int *, int * ); +static double (* parent_getbottom)( AstFrame *, int, int * ); +static double (* parent_getepoch)( AstFrame *, int * ); +static double (* parent_gettop)( AstFrame *, int, int * ); +static int (* parent_getdirection)( AstFrame *, int, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static int (* parent_testformat)( AstFrame *, int, int * ); +static int (* parent_unformat)( AstFrame *, int, const char *, double *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_cleardut1)( AstFrame *, int * ); +static void (* parent_clearformat)( AstFrame *, int, int * ); +static void (* parent_clearobsalt)( AstFrame *, int * ); +static void (* parent_clearobslat)( AstFrame *, int * ); +static void (* parent_clearobslon)( AstFrame *, int * ); +static void (* parent_clearsystem)( AstFrame *, int * ); +static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_setdut1)( AstFrame *, double, int * ); +static void (* parent_setformat)( AstFrame *, int, const char *, int * ); +static void (* parent_setobsalt)( AstFrame *, double, int * ); +static void (* parent_setobslat)( AstFrame *, double, int * ); +static void (* parent_setobslon)( AstFrame *, double, int * ); +static void (* parent_setsystem)( AstFrame *, AstSystemType, int * ); + +/* Factors for converting between hours, degrees and radians. */ +static double hr2rad; +static double deg2rad; +static double pi; +static double piby2; + +/* Table of cached Local Apparent Sidereal Time values and corresponding + epochs. */ +static int nlast_tables = 0; +static AstSkyLastTable **last_tables = NULL; + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetFormat_Buff[ 0 ] = 0; \ + globals->GetLabel_Buff[ 0 ] = 0; \ + globals->GetSymbol_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff2[ 0 ] = 0; \ + globals->TDBFrame = NULL; \ + globals->LASTFrame = NULL; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SkyFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SkyFrame,Class_Init) +#define class_vtab astGLOBAL(SkyFrame,Class_Vtab) +#define getattrib_buff astGLOBAL(SkyFrame,GetAttrib_Buff) +#define getformat_buff astGLOBAL(SkyFrame,GetFormat_Buff) +#define getlabel_buff astGLOBAL(SkyFrame,GetLabel_Buff) +#define getsymbol_buff astGLOBAL(SkyFrame,GetSymbol_Buff) +#define gettitle_buff astGLOBAL(SkyFrame,GetTitle_Buff) +#define gettitle_buff2 astGLOBAL(SkyFrame,GetTitle_Buff2) +#define tdbframe astGLOBAL(SkyFrame,TDBFrame) +#define lastframe astGLOBAL(SkyFrame,LASTFrame) + + + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* A read-write lock used to protect the table of cached LAST values so + that multiple threads can read simultaneously so long as no threads are + writing to the table. */ +static pthread_rwlock_t rwlock1=PTHREAD_RWLOCK_INITIALIZER; +#define LOCK_WLOCK1 pthread_rwlock_wrlock( &rwlock1 ); +#define LOCK_RLOCK1 pthread_rwlock_rdlock( &rwlock1 ); +#define UNLOCK_RWLOCK1 pthread_rwlock_unlock( &rwlock1 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ]; + +/* Buffer returned by GetFormat. */ +static char getformat_buff[ GETFORMAT_BUFF_LEN + 1 ]; + +/* Default GetLabel string buffer */ +static char getlabel_buff[ GETLABEL_BUFF_LEN + 1 ]; + +/* Default GetSymbol buffer */ +static char getsymbol_buff[ GETSYMBOL_BUFF_LEN + 1 ]; + +/* Default Title string buffer */ +static char gettitle_buff[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ]; +static char gettitle_buff2[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ]; + +/* TimeFrames for doing TDB<->LAST conversions. */ +static AstTimeFrame *tdbframe = NULL; +static AstTimeFrame *lastframe = NULL; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSkyFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#define LOCK_WLOCK1 +#define LOCK_RLOCK1 +#define UNLOCK_RWLOCK1 + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstLineDef *LineDef( AstFrame *, const double[2], const double[2], int * ); +static AstMapping *SkyOffsetMap( AstSkyFrame *, int * ); +static AstPointSet *FrameGrid( AstFrame *, int, const double *, const double *, int * ); +static AstPointSet *ResolvePoints( AstFrame *, const double [], const double [], AstPointSet *, AstPointSet *, int * ); +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static AstSystemType GetSystem( AstFrame *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static const char *Format( AstFrame *, int, double, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetFormat( AstFrame *, int, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetProjection( AstSkyFrame *, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static double Angle( AstFrame *, const double[], const double[], const double[], int * ); +static double CalcLAST( AstSkyFrame *, double, double, double, double, double, int * ); +static double Distance( AstFrame *, const double[], const double[], int * ); +static double Gap( AstFrame *, int, double, int *, int * ); +static double GetBottom( AstFrame *, int, int * ); +static double GetCachedLAST( AstSkyFrame *, double, double, double, double, double, int * ); +static double GetEpoch( AstFrame *, int * ); +static double GetEquinox( AstSkyFrame *, int * ); +static void SetCachedLAST( AstSkyFrame *, double, double, double, double, double, double, int * ); +static void SetLast( AstSkyFrame *, int * ); +static double GetTop( AstFrame *, int, int * ); +static double Offset2( AstFrame *, const double[2], double, double, double[2], int * ); +static double GetDiurab( AstSkyFrame *, int * ); +static double GetLAST( AstSkyFrame *, int * ); +static int GetActiveUnit( AstFrame *, int * ); +static int GetAsTime( AstSkyFrame *, int, int * ); +static int GetDirection( AstFrame *, int, int * ); +static int GetIsLatAxis( AstSkyFrame *, int, int * ); +static int GetIsLonAxis( AstSkyFrame *, int, int * ); +static int GetLatAxis( AstSkyFrame *, int * ); +static int GetLonAxis( AstSkyFrame *, int * ); +static int GetNegLon( AstSkyFrame *, int * ); +static int GetObjSize( AstObject *, int * ); +static int IsEquatorial( AstSystemType, int * ); +static int LineContains( AstFrame *, AstLineDef *, int, double *, int * ); +static int LineCrossing( AstFrame *, AstLineDef *, AstLineDef *, double **, int * ); +static int LineIncludes( SkyLineDef *, double[3], int * ); +static int MakeSkyMapping( AstSkyFrame *, AstSkyFrame *, AstSystemType, AstMapping **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static int TestAsTime( AstSkyFrame *, int, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestEquinox( AstSkyFrame *, int * ); +static int TestNegLon( AstSkyFrame *, int * ); +static int TestProjection( AstSkyFrame *, int * ); +static int TestSlaUnit( AstSkyFrame *, AstSkyFrame *, AstSlaMap *, int * ); +static int Unformat( AstFrame *, int, const char *, double *, int * ); +static void ClearAsTime( AstSkyFrame *, int, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearDut1( AstFrame *, int * ); +static void ClearEquinox( AstSkyFrame *, int * ); +static void ClearNegLon( AstSkyFrame *, int * ); +static void ClearObsAlt( AstFrame *, int * ); +static void ClearObsLat( AstFrame *, int * ); +static void ClearObsLon( AstFrame *, int * ); +static void ClearProjection( AstSkyFrame *, int * ); +static void ClearSystem( AstFrame *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Intersect( AstFrame *, const double[2], const double[2], const double[2], const double[2], double[2], int * ); +static void LineOffset( AstFrame *, AstLineDef *, double, double, double[2], int * ); +static void MatchAxesX( AstFrame *, AstFrame *, int *, int * ); +static void Norm( AstFrame *, double[], int * ); +static void NormBox( AstFrame *, double[], double[], AstMapping *, int * ); +static void Offset( AstFrame *, const double[], const double[], double, double[], int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void Resolve( AstFrame *, const double [], const double [], const double [], double [], double *, double *, int * ); +static void SetAsTime( AstSkyFrame *, int, int, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetDut1( AstFrame *, double, int * ); +static void SetEquinox( AstSkyFrame *, double, int * ); +static void SetNegLon( AstSkyFrame *, int, int * ); +static void SetObsAlt( AstFrame *, double, int * ); +static void SetObsLat( AstFrame *, double, int * ); +static void SetObsLon( AstFrame *, double, int * ); +static void SetProjection( AstSkyFrame *, const char *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); +static void Shapp( double, double *, double *, double, double *, int * ); +static void Shcal( double, double, double, double *, double *, int * ); +static void VerifyMSMAttrs( AstSkyFrame *, AstSkyFrame *, int, const char *, const char *, int * ); + +static double GetSkyRef( AstSkyFrame *, int, int * ); +static int TestSkyRef( AstSkyFrame *, int, int * ); +static void SetSkyRef( AstSkyFrame *, int, double, int * ); +static void ClearSkyRef( AstSkyFrame *, int, int * ); + +static double GetSkyRefP( AstSkyFrame *, int, int * ); +static int TestSkyRefP( AstSkyFrame *, int, int * ); +static void SetSkyRefP( AstSkyFrame *, int, double, int * ); +static void ClearSkyRefP( AstSkyFrame *, int, int * ); + +static int GetSkyRefIs( AstSkyFrame *, int * ); +static int TestSkyRefIs( AstSkyFrame *, int * ); +static void SetSkyRefIs( AstSkyFrame *, int, int * ); +static void ClearSkyRefIs( AstSkyFrame *, int * ); + +static int GetAlignOffset( AstSkyFrame *, int * ); +static int TestAlignOffset( AstSkyFrame *, int * ); +static void SetAlignOffset( AstSkyFrame *, int, int * ); +static void ClearAlignOffset( AstSkyFrame *, int * ); + +static double GetSkyTol( AstSkyFrame *, int * ); +static int TestSkyTol( AstSkyFrame *, int * ); +static void SetSkyTol( AstSkyFrame *, double, int * ); +static void ClearSkyTol( AstSkyFrame *, int * ); + +/* Member functions. */ +/* ================= */ +static double Angle( AstFrame *this_frame, const double a[], + const double b[], const double c[], int *status ) { +/* +* Name: +* Angle + +* Purpose: +* Calculate the angle subtended by two points at a third point. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Angle( AstFrame *this_frame, const double a[], +* const double b[], const double c[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astAngle method +* inherited from the Frame class). + +* Description: +* This function finds the angle at point B between the line +* joining points A and B, and the line joining points C +* and B. These lines will in fact be geodesic curves (great circles). + +* Parameters: +* this +* Pointer to the SkyFrame. +* a +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the first point. +* b +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the second point. +* c +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the third point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The angle in radians, from the line AB to the line CB, in +* the range $\pm \pi$ with positive rotation is in the same sense +* as rotation from axis 2 to axis 1. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if points A and B are +* co-incident, or if points B and C are co-incident. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double aa[ 2 ]; /* Permuted a coordinates */ + double anga; /* Angle from north to the line BA */ + double angc; /* Angle from north to the line BC */ + double bb[ 2 ]; /* Permuted b coordinates */ + double cc[ 2 ]; /* Permuted c coordinates */ + double result; /* Value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied coordinates are OK. */ + if ( ( a[ 0 ] != AST__BAD ) && ( a[ 1 ] != AST__BAD ) && + ( b[ 0 ] != AST__BAD ) && ( b[ 1 ] != AST__BAD ) && + ( c[ 0 ] != AST__BAD ) && ( c[ 1 ] != AST__BAD ) ) { + +/* Apply the axis permutation array to obtain the coordinates of the + three points in the required (longitude,latitude) order. */ + aa[ perm[ 0 ] ] = a[ 0 ]; + aa[ perm[ 1 ] ] = a[ 1 ]; + bb[ perm[ 0 ] ] = b[ 0 ]; + bb[ perm[ 1 ] ] = b[ 1 ]; + cc[ perm[ 0 ] ] = c[ 0 ]; + cc[ perm[ 1 ] ] = c[ 1 ]; + +/* Check that A and B are not co-incident. */ + if( aa[ 0 ] != bb[ 0 ] || aa[ 1 ] != bb[ 1 ] ) { + +/* Check that C and B are not co-incident. */ + if( cc[ 0 ] != bb[ 0 ] || cc[ 1 ] != bb[ 1 ] ) { + +/* Find the angle from north to the line BA. */ + anga = palDbear( bb[ 0 ], bb[ 1 ], aa[ 0 ], aa[ 1 ] ); + +/* Find the angle from north to the line BC. */ + angc = palDbear( bb[ 0 ], bb[ 1 ], cc[ 0 ], cc[ 1 ] ); + +/* Find the difference. */ + result = angc - anga; + +/* This value is the angle from north, but we want the angle from axis 2. + If the axes have been swapped so that axis 2 is actually the longitude + axis, then we need to correct this result. */ + if( perm[ 0 ] != 0 ) result = piby2 - result; + +/* Fold the result into the range +/- PI. */ + result = palDrange( result ); + } + } + } + } + +/* Return the result. */ + return result; +} + +static double CalcLAST( AstSkyFrame *this, double epoch, double obslon, + double obslat, double obsalt, double dut1, + int *status ) { +/* +* Name: +* CalcLAST + +* Purpose: +* Calculate the Local Appearent Sidereal Time for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double CalcLAST( AstSkyFrame *this, double epoch, double obslon, +* double obslat, double obsalt, double dut1, +* int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function calculates and returns the Local Apparent Sidereal Time +* at the given epoch, etc. + +* Parameters: +* this +* Pointer to the SkyFrame. +* epoch +* The epoch (MJD). +* obslon +* Observatory geodetic longitude (radians) +* obslat +* Observatory geodetic latitude (radians) +* obsalt +* Observatory geodetic altitude (metres) +* dut1 +* The UT1-UTC correction, in seconds. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Local Apparent Sidereal Time, in radians. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstFrameSet *fs; /* Mapping from TDB offset to LAST offset */ + double epoch0; /* Supplied epoch value */ + double result; /* Returned LAST value */ + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* See if the required LAST value can be determined from the cached LAST + values in the SkyFrame virtual function table. */ + result = GetCachedLAST( this, epoch, obslon, obslat, obsalt, dut1, + status ); + +/* If not, we do an exact calculation from scratch. */ + if( result == AST__BAD ) { + +/* If not yet done, create two TimeFrames. Note, this is done here + rather than in astInitSkyFrameVtab in order to avoid infinite vtab + initialisation loops (caused by the TimeFrame class containing a + static SkyFrame). */ + if( ! tdbframe ) { + astBeginPM; + tdbframe = astTimeFrame( "system=mjd,timescale=tdb", status ); + lastframe = astTimeFrame( "system=mjd,timescale=last", status ); + astEndPM; + } + +/* For better accuracy, use this integer part of the epoch as the origin of + the two TimeFrames. */ + astSetTimeOrigin( tdbframe, (int) epoch ); + astSetTimeOrigin( lastframe, (int) epoch ); + +/* Convert the absolute Epoch value to an offset from the above origin. */ + epoch0 = epoch; + epoch -= (int) epoch; + +/* Store the observers position in the two TimeFrames. */ + astSetObsLon( tdbframe, obslon ); + astSetObsLon( lastframe, obslon ); + + astSetObsLat( tdbframe, obslat ); + astSetObsLat( lastframe, obslat ); + + astSetObsAlt( tdbframe, obsalt ); + astSetObsAlt( lastframe, obsalt ); + +/* Store the DUT1 value. */ + astSetDut1( tdbframe, dut1 ); + astSetDut1( lastframe, dut1 ); + +/* Get the conversion from tdb mjd offset to last mjd offset. */ + fs = astConvert( tdbframe, lastframe, "" ); + +/* Use it to transform the SkyFrame Epoch from TDB offset to LAST offset. */ + astTran1( fs, 1, &epoch, 1, &epoch ); + fs = astAnnul( fs ); + +/* Convert the LAST offset from days to radians. */ + result = ( epoch - (int) epoch )*2*AST__DPI; + +/* Cache the new LAST value in the SkyFrame virtual function table. */ + SetCachedLAST( this, result, epoch0, obslon, obslat, obsalt, dut1, + status ); + } + +/* Return the required LAST value. */ + return result; +} + +static void ClearAsTime( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* ClearAsTime + +* Purpose: +* Clear the value of the AsTime attribute for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearAsTime( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function clears any value that has been set for the AsTime +* attribute for a specified axis of a SkyFrame. This attribute indicates +* whether axis values should be formatted as times (as opposed to angles) +* by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which the value is to be cleared (zero based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astClearAsTime" ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis ); + +/* If the Axis is a SkyAxis, clear the AsTime attribute (if it is not a + SkyAxis, it will not have this attribute anyway). */ + if ( astIsASkyAxis( ax ) ) astClearAxisAsTime( ax ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* SkyFrame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the SkyFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis; /* SkyFrame axis number */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearAsTime( this, axis - 1 ); + +/* Equinox. */ +/* -------- */ + } else if ( !strcmp( attrib, "equinox" ) ) { + astClearEquinox( this ); + +/* NegLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "neglon" ) ) { + astClearNegLon( this ); + +/* Projection. */ +/* ----------- */ + } else if ( !strcmp( attrib, "projection" ) ) { + astClearProjection( this ); + +/* SkyRef. */ +/* ------- */ + } else if ( !strcmp( attrib, "skyref" ) ) { + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + +/* SkyTol. */ +/* ------- */ + } else if ( !strcmp( attrib, "skytol" ) ) { + astClearSkyTol( this ); + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearSkyRef( this, axis - 1 ); + +/* SkyRefP. */ +/* -------- */ + } else if ( !strcmp( attrib, "skyrefp" ) ) { + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + +/* SkyRefP(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + astClearSkyRefP( this, axis - 1 ); + +/* SkyRefIs. */ +/* --------- */ + } else if ( !strcmp( attrib, "skyrefis" ) ) { + astClearSkyRefIs( this ); + +/* AlignOffset. */ +/* ------------ */ + } else if ( !strcmp( attrib, "alignoffset" ) ) { + astClearAlignOffset( this ); + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class. If it does, then report an + error. */ + } else if ( !strncmp( attrib, "islataxis", 9 ) || + !strncmp( attrib, "islonaxis", 9 ) || + !strcmp( attrib, "lataxis" ) || + !strcmp( attrib, "lonaxis" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearDut1( AstFrame *this, int *status ) { +/* +* Name: +* ClearDut1 + +* Purpose: +* Clear the value of the Dut1 attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearDut1( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearDut1 method +* inherited from the Frame class). + +* Description: +* This function clears the Dut1 value and updates the LAST value +* stored in the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetDut1( this ); + +/* Invoke the parent method to clear the Frame Dut1 */ + (*parent_cleardut1)( this, status ); + +/* If the DUT1 value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - astGetDut1( this ) ) > 1.0E-6 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +static void ClearObsAlt( AstFrame *this, int *status ) { +/* +* Name: +* ClearObsAlt + +* Purpose: +* Clear the value of the ObsAlt attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearObsAlt( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearObsAlt method +* inherited from the Frame class). + +* Description: +* This function clears the ObsAlt value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetObsAlt( this ); + +/* Invoke the parent method to clear the Frame ObsAlt. */ + (*parent_clearobsalt)( this, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - astGetObsAlt( this ) ) > 0.001 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void ClearObsLat( AstFrame *this, int *status ) { +/* +* Name: +* ClearObsLat + +* Purpose: +* Clear the value of the ObsLat attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearObsLat( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearObsLat method +* inherited from the Frame class). + +* Description: +* This function clears the ObsLat value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetObsLat( this ); + +/* Invoke the parent method to clear the Frame ObsLat. */ + (*parent_clearobslat)( this, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - astGetObsLat( this ) ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void ClearObsLon( AstFrame *this, int *status ) { +/* +* Name: +* ClearObsLon + +* Purpose: +* Clear the value of the ObsLon attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearObsLon( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearObsLon method +* inherited from the Frame class). + +* Description: +* This function clears the ObsLon value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original value */ + orig = astGetObsLon( this ); + +/* Invoke the parent method to clear the Frame ObsLon. */ + (*parent_clearobslon)( this, status ); + +/* If the longitude has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - astGetObsLon( this ) ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +static void ClearSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearSystem + +* Purpose: +* Clear the System attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void ClearSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astClearSystem protected +* method inherited from the Frame class). + +* Description: +* This function clears the System attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* FrameSet to be used as the Mapping */ + AstSkyFrame *sfrm; /* Copy of original SkyFrame */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + double xin[ 2 ]; /* Axis 0 values */ + double yin[ 2 ]; /* Axis 1 values */ + double xout[ 2 ]; /* Axis 0 values */ + double yout[ 2 ]; /* Axis 1 values */ + int skyref_set; /* Is either SkyRef attribute set? */ + int skyrefp_set; /* Is either SkyRefP attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* See if either the SkyRef or SkyRefP attribute is set. */ + skyref_set = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ); + skyrefp_set = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 ); + +/* If so, we will need to transform their values into the new coordinate + system. Save a copy of the SkyFrame with its original System value. */ + sfrm = ( skyref_set || skyrefp_set )?astCopy( this ):NULL; + +/* Use the parent method to clear the System value. */ + (*parent_clearsystem)( this_frame, status ); + +/* Now modify the SkyRef and SkyRefP attributes if necessary. */ + if( sfrm ) { + +/* Save the SkyRef and SkyRefP values. */ + xin[ 0 ] = astGetSkyRef( sfrm, 0 ); + xin[ 1 ] = astGetSkyRefP( sfrm, 0 ); + yin[ 0 ] = astGetSkyRef( sfrm, 1 ); + yin[ 1 ] = astGetSkyRefP( sfrm, 1 ); + +/* Clear the SkyRef values to avoid infinite recursion in the following + call to astConvert. */ + if( skyref_set ) { + astClearSkyRef( sfrm, 0 ); + astClearSkyRef( sfrm, 1 ); + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + +/* Get the Mapping from the original System to the default System. Invoking + astConvert will recursively invoke ClearSystem again. This is why we need + to be careful to ensure that SkyRef is cleared above - doing so ensure + we do not end up with infinite recursion. */ + fs = astConvert( sfrm, this, "" ); + +/* Check the Mapping was found. */ + if( fs ) { + +/* Use the Mapping to find the SkyRef and SkyRefP positions in the default + coordinate system. */ + astTran2( fs, 2, xin, yin, 1, xout, yout ); + +/* Store the values as required. */ + if( skyref_set ) { + astSetSkyRef( this, 0, xout[ 0 ] ); + astSetSkyRef( this, 1, yout[ 0 ] ); + } + + if( skyrefp_set ) { + astSetSkyRefP( this, 0, xout[ 1 ] ); + astSetSkyRefP( this, 1, yout[ 1 ] ); + } + +/* Free resources. */ + fs = astAnnul( fs ); + +/* If the Mapping is not defined, we cannot convert the SkyRef or SkyRefP + positions in the new Frame so clear them. */ + } else { + if( skyref_set ) { + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + if( skyrefp_set ) { + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + } + } + +/* Free resources. */ + sfrm = astAnnul( sfrm ); + } +} + +static double Distance( AstFrame *this_frame, + const double point1[], const double point2[], int *status ) { +/* +* Name: +* Distance + +* Purpose: +* Calculate the distance between two points. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Distance( AstFrame *this, +* const double point1[], const double point2[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astDistance method +* inherited from the Frame class). + +* Description: +* This function finds the distance between two points whose +* SkyFrame coordinates are given. The distance calculated is that +* along the geodesic curve (i.e. great circle) that joins the two +* points. + +* Parameters: +* this +* Pointer to the SkyFrame. +* point1 +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the first point. +* point2 +* An array of double, with one element for each SkyFrame axis, +* containing the coordinates of the second point. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The distance between the two points, in radians. + +* Notes: +* - This function will return a "bad" result value (AST__BAD) if +* any of the input coordinates has this value. +* - A "bad" value will also be returned if this function is +* invoked with the AST error status set or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double p1[ 2 ]; /* Permuted point1 coordinates */ + double p2[ 2 ]; /* Permuted point2 coordinates */ + double result; /* Value to return */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied coordinates are OK. */ + if ( ( point1[ 0 ] != AST__BAD ) && ( point1[ 1 ] != AST__BAD ) && + ( point2[ 0 ] != AST__BAD ) && ( point2[ 1 ] != AST__BAD ) ) { + +/* Apply the axis permutation array to obtain the coordinates of the + two points in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + +/* Calculate the great circle distance between the points in radians. */ + result = palDsep( p1[ 0 ], p1[ 1 ], p2[ 0 ], p2[ 1 ] ); + } + } + +/* Return the result. */ + return result; +} + +static const char *Format( AstFrame *this_frame, int axis, double value, int *status ) { +/* +* Name: +* Format + +* Purpose: +* Format a coordinate value for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *Format( AstFrame *this, int axis, double value, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astFormat method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to a string containing the formatted +* (character) version of a coordinate value for a SkyFrame axis. The +* formatting applied is that specified by a previous invocation of the +* astSetFormat method. A suitable default format is applied if necessary, +* and this may depend on which sky coordinate system the SkyFrame +* describes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the axis (zero-based) for which formatting is to be +* performed. +* value +* The coordinate value to be formatted, in radians. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *result; /* Pointer value to return */ + int format_set; /* Format attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astFormat" ); + +/* Determine if a Format value has been set for the axis and set a temporary + value if it has not. Use the GetFormat member function for this class + together with member functions inherited from the parent class (rather than + using the object's methods directly) because if any of these methods have + been over-ridden by a derived class the Format string syntax may no longer + be compatible with this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the Format member function inherited from the parent class to format the + value and return a pointer to the resulting string. */ + result = (*parent_format)( this_frame, axis, value, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static AstPointSet *FrameGrid( AstFrame *this_object, int size, const double *lbnd, + const double *ubnd, int *status ){ +/* +* Name: +* FrameGrid + +* Purpose: +* Return a grid of points covering a rectangular area of a Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstPointSet *FrameGrid( AstFrame *this_frame, int size, +* const double *lbnd, const double *ubnd, +* int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astFrameGrid +* method inherited from the Frame class). + +* Description: +* This function returns a PointSet containing positions spread +* approximately evenly throughtout a specified rectangular area of +* the Frame. + +* Parameters: +* this +* Pointer to the Frame. +* size +* The preferred number of points in the returned PointSet. The +* actual number of points in the returned PointSet may be +* different, but an attempt is made to stick reasonably closely to +* the supplied value. +* lbnd +* Pointer to an array holding the lower bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. +* ubnd +* Pointer to an array holding the upper bound of the rectangular +* area on each Frame axis. The array should have one element for +* each Frame axis. + +* Returned Value: +* A pointer to a new PointSet holding the grid of points. + +* Notes: +* - A NULL pointer is returned if an error occurs. +*/ + +/* Local Variables: */ + AstPointSet *result; + AstSkyFrame *this; + double **ptr; + double box_area; + double cl; + double dlon; + double hilat; + double hilon; + double inclon; + double lat_size; + double lat; + double lon; + double lolon; + double lon_size; + double lolat; + double totlen; + int ilat; + int ilon; + int imer; + int ip; + int ipar; + int ipmax; + int nmer; + int npar; + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Get the zero-based indices of the longitude and latitude axes. */ + ilon = astGetLonAxis( this ); + ilat = 1 - ilon; + +/* The latitude bounds may not be the right way round so check for it. */ + if( lbnd[ ilat ] <= ubnd[ ilat ] ) { + lolat = lbnd[ ilat ]; + hilat = ubnd[ ilat ]; + } else { + lolat = ubnd[ ilat ]; + hilat = lbnd[ ilat ]; + } + +/* Check all bounds are good. Also check the size is positive. */ + lolon = lbnd[ ilon ]; + hilon = ubnd[ ilon ]; + if( size > 0 && lolat != AST__BAD && hilat != AST__BAD && + lolon != AST__BAD && hilon != AST__BAD ) { + +/* Ensure the longitude bounds are in the range 0-2PI. */ + lolon = palDranrm( lolon ); + hilon = palDranrm( hilon ); + +/* If the upper longitude limit is less than the lower limit, add 2.PI */ + if( hilon <= lolon && + ubnd[ ilon ] != lbnd[ ilon ] ) hilon += 2*AST__DPI; + +/* Get the total area of the box in steradians. */ + dlon = hilon - lolon; + box_area = fabs( dlon*( sin( hilat ) - sin( lolat ) ) ); + +/* Get the nominal size of a square grid cell, in radians. */ + lat_size = sqrt( box_area/size ); + +/* How many parallels should we use to cover the box? Ensure we use at + least two. These parallels pass through the centre of the grid cells. */ + npar = (int)( 0.5 + ( hilat - lolat )/lat_size ); + if( npar < 2 ) npar = 2; + +/* Find the actual sample size implied by this number of parallels. */ + lat_size = ( hilat - lolat )/npar; + +/* Find the total arc length of the parallels. */ + totlen = 0.0; + lat = lolat + 0.5*lat_size; + for( ipar = 0; ipar < npar; ipar++ ) { + totlen += dlon*cos( lat ); + lat += lat_size; + } + +/* If we space "size" samples evenly over this total arc-length, what is + the arc-distance between samples? */ + lon_size = totlen/size; + +/* Create a PointSet in which to store the grid. Make it bigger than + necessary in order to leave room for extra samples caused by integer + truncation. */ + ipmax = 2*size; + result = astPointSet( ipmax, 2, " ", status ); + ptr = astGetPoints( result ); + if( astOK ) { + +/* Loop over all the parallels. */ + ip = 0; + lat = lolat + 0.5*lat_size; + for( ipar = 0; ipar < npar; ipar++ ) { + +/* Get the longitude increment between samples on this parallel. */ + cl = cos( lat ); + inclon = ( cl != 0.0 ) ? lon_size/cl : 0.0; + +/* Get the number of longitude samples for this parallel. Reduce it if + it would extend beyond the end of the PointSet. */ + nmer = dlon/inclon; + if( ip + nmer >= ipmax ) nmer = ipmax - ip; + +/* Adjust the longitude increment to take up any slack caused by the + above integer division. */ + inclon = dlon/nmer; + +/* Produce the samples for the current parallel. */ + lon = lolon + 0.5*inclon; + for( imer = 0; imer < nmer; imer++ ) { + ptr[ ilon ][ ip ] = lon; + ptr[ ilat ][ ip ] = lat; + + lon += inclon; + ip++; + } + +/* Get the latitude on the next parallel. */ + lat += lat_size; + } + +/* Truncate the PointSet to exclude unused elements at the end. */ + astSetNpoint( result, ip ); + } + +/* Report error if supplied values were bad. */ + } else if( astOK ) { + if( size < 1 ) { + astError( AST__ATTIN, "astFrameGrid(%s): The supplied grid " + "size (%d) is invalid (programming error).", + status, astGetClass( this ), size ); + } else { + astError( AST__ATTIN, "astFrameGrid(%s): One of more of the " + "supplied bounds is AST__BAD (programming error).", + status, astGetClass( this ) ); + } + } + +/* Annul the returned PointSet if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the PointSet holding the grid. */ + return result; +} + +static double Gap( AstFrame *this_frame, int axis, double gap, int *ntick, int *status ) { +/* +* Name: +* Gap + +* Purpose: +* Find a "nice" gap for tabulating SkyFrame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astGap method +* inherited from the Frame class). + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted values for a SkyFrame axis, the returned gap +* size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + double result; /* Gap value to return */ + int format_set; /* Format attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGap" ); + +/* Determine if a Format value has been set for the axis and set a + temporary value if it has not. Use the GetFormat member function + for this class together with member functions inherited from the + parent class (rather than using the object's methods directly) + because if any of these methods have been over-ridden by a derived + class the Format string syntax may no longer be compatible with + this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the Gap member function inherited from the parent class to find + the gap size. */ + result = (*parent_gap)( this_frame, axis, gap, ntick, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SkyFrame, +* in bytes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->projection ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetActiveUnit + +* Purpose: +* Obtain the value of the ActiveUnit flag for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function returns the value of the ActiveUnit flag for a +* SkyFrame, which is always 0. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to use for the ActiveUnit flag (0). + +*/ + return 0; +} + +static int GetAsTime( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetAsTime + +* Purpose: +* Obtain the value of the AsTime attribute for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetAsTime( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the boolean value of the AsTime attribute for a +* specified axis of a SkyFrame. This value indicates whether axis values +* should be formatted as times (as opposed to angles) by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which information is required (zero based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero or one, according to the setting of the AsTime attribute (if no +* value has previously been set, a suitable default is returned). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + int axis_p; /* Permuted axis index */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetAsTime" ); + +/* Obtain a pointer to the required Axis object. */ + ax = astGetAxis( this, axis ); + +/* Determine if the AsTime attribute has been set for the axis (this can only + be the case if the object is a SkyAxis). If the attribute is set, obtain its + value. */ + if ( astIsASkyAxis( ax ) && astTestAxisAsTime( ax ) ) { + result = astGetAxisAsTime( ax ); + +/* Otherwise, check which (permuted) axis is involved. Only the first + (longitude) axis may be displayed as a time by default. */ + } else if ( axis_p == 0 ) { + +/* Test for those coordinate systems which normally have their longitude axes + displayed as times (basically, those that involve the Earth's equator) and + set the returned value appropriately. */ + result = IsEquatorial( astGetSystem( this ), status ); + } + +/* Annul the Axis object pointer. */ + ax = astAnnul( ax ); + +/* Return the result. */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a SkyFrame, formatted as a character string. + +* Parameters: +* this +* Pointer to the SkyFrame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the SkyFrame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the SkyFrame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *cval; /* Pointer to character attribute value */ + const char *result; /* Pointer value to return */ + double dval; /* Floating point attribute value */ + double equinox; /* Equinox attribute value (as MJD) */ + int as_time; /* AsTime attribute value */ + int axis; /* SkyFrame axis number */ + int ival; /* Integer attribute value */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + int neglon; /* Display long. values as [-pi,pi]? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + as_time = astGetAsTime( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", as_time ); + result = getattrib_buff; + } + +/* Equinox. */ +/* -------- */ + } else if ( !strcmp( attrib, "equinox" ) ) { + equinox = astGetEquinox( this ); + if ( astOK ) { + +/* Format the Equinox as decimal years. Use a Besselian epoch if it + will be less than 1984.0, otherwise use a Julian epoch. */ + result = astFmtDecimalYr( ( equinox < palEpj2d( 1984.0 ) ) ? + palEpb( equinox ) : palEpj( equinox ), + DBL_DIG ); + } + +/* IsLatAxis(axis) */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "islataxis(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = astGetIsLatAxis( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* IsLonAxis(axis) */ +/* --------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "islonaxis(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + ival = astGetIsLonAxis( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* LatAxis */ +/* -------- */ + } else if ( !strcmp( attrib, "lataxis" ) ) { + axis = astGetLatAxis( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", axis + 1 ); + result = getattrib_buff; + } + +/* LonAxis */ +/* -------- */ + } else if ( !strcmp( attrib, "lonaxis" ) ) { + axis = astGetLonAxis( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", axis + 1 ); + result = getattrib_buff; + } + +/* NegLon */ +/* ------ */ + } else if ( !strcmp( attrib, "neglon" ) ) { + neglon = astGetNegLon( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", neglon ); + result = getattrib_buff; + } + +/* SkyTol */ +/* ------ */ + } else if ( !strcmp( attrib, "skytol" ) ) { + dval = astGetSkyTol( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* Projection. */ +/* ----------- */ + } else if ( !strcmp( attrib, "projection" ) ) { + result = astGetProjection( this ); + +/* SkyRef. */ +/* ------- */ + } else if ( !strcmp( attrib, "skyref" ) ) { + cval = astFormat( this, 0, astGetSkyRef( this, 0 ) ); + if ( astOK ) { + nc = sprintf( getattrib_buff, "%s, ", cval ); + cval = astFormat( this, 1, astGetSkyRef( this, 1 ) ); + if ( astOK ) { + (void) sprintf( getattrib_buff + nc, "%s", cval ); + result = getattrib_buff; + } + } + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetSkyRef( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* SkyRefP. */ +/* -------- */ + } else if ( !strcmp( attrib, "skyrefp" ) ) { + cval = astFormat( this, 0, astGetSkyRefP( this, 0 ) ); + if ( astOK ) { + nc = sprintf( getattrib_buff, "%s, ", cval ); + cval = astFormat( this, 1, astGetSkyRefP( this, 1 ) ); + if ( astOK ) { + (void) sprintf( getattrib_buff + nc, "%s", cval ); + result = getattrib_buff; + } + } + +/* SkyRefP(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + dval = astGetSkyRefP( this, axis - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* SkyRefIs. */ +/* --------- */ + } else if ( !strcmp( attrib, "skyrefis" ) ) { + ival = astGetSkyRefIs( this ); + if ( astOK ) { + if( ival == AST__POLE_REF ){ + result = POLE_STRING; + } else if( ival == AST__IGNORED_REF ){ + result = IGNORED_STRING; + } else { + result = ORIGIN_STRING; + } + } + +/* AlignOffset */ +/* ----------- */ + } else if ( !strcmp( attrib, "alignoffset" ) ) { + ival = astGetAlignOffset( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetDirection( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetDirection + +* Purpose: +* Obtain the value of the Direction attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetDirection( AstFrame *this_frame, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetDirection method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Direction attribute for a +* specified axis of a SkyFrame. A suitable default value is returned if no +* Direction value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero or one, depending on the Direction attribute value. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis_p; /* Permuted axis index */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetDirection" ); + +/* Check if a value has been set for the axis Direction attribute. If so, + obtain its value. */ + if ( astTestDirection( this, axis ) ) { + result = (*parent_getdirection)( this_frame, axis, status ); + +/* Otherwise, we will generate a default Direction value. Currently all + systems supported by SkyFrame are left handed, so all longitude axes + are reversed and all latitude axes are not reversed. */ + } else if( axis_p == 0 ) { + result = 0; + } else { + result = 1; + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static double GetBottom( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetBottom + +* Purpose: +* Obtain the value of the Bottom attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetBottom( AstFrame *this_frame, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetBottom method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Bottom attribute for a +* specified axis of a SkyFrame. A suitable default value is returned if no +* value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Bottom value to use. + +* Notes: +* - A value of -DBL_MAX will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis_p; /* Permuted axis index */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return -DBL_MAX; + +/* Initialise. */ + result = -DBL_MAX; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetBottom" ); + +/* Check if a value has been set for the axis Bottom attribute. If so, + obtain its value. */ + if ( astTestBottom( this, axis ) ) { + result = (*parent_getbottom)( this_frame, axis, status ); + +/* Otherwise, we will return a default Bottom value appropriate to the + SkyFrame class. */ + } else { + +/* If it is a latitude axis return -pi/2. */ + if( axis_p == 1 ) { + result = -piby2; + +/* If it is a longitude value return -DBL_MAX (i.e. no lower limit). */ + } else { + result = -DBL_MAX; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -DBL_MAX; + +/* Return the result. */ + return result; +} + +static double GetCachedLAST( AstSkyFrame *this, double epoch, double obslon, + double obslat, double obsalt, double dut1, + int *status ) { +/* +* Name: +* GetCachedLAST + +* Purpose: +* Attempt to get a LAST value from the cache in the SkyFrame vtab. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetCachedLAST( AstSkyFrame *this, double epoch, double obslon, +* double obslat, double obsalt, double dut1, +* int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function searches the static cache of LAST values held in the +* SkyFrame virtual function table for a value that corresponds to the +* supplied parameter values. If one is found, it is returned. +* Otherwise AST__BAD is found. + +* Parameters: +* this +* Pointer to the SkyFrame. +* epoch +* The epoch (MJD). +* obslon +* Observatory geodetic longitude (radians) +* obslat +* Observatory geodetic latitude (radians) +* obsalt +* Observatory geodetic altitude (metres) +* dut1 +* The UT1-UTC correction, in seconds. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Local Apparent Sidereal Time, in radians. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstSkyLastTable *table; + double *ep; + double *lp; + double dep; + double result; + int ihi; + int ilo; + int itable; + int itest; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Wait until the table is not being written to by any thread. This also + prevents a thread from writing to the table whilst we are reading it. */ + LOCK_RLOCK1 + +/* Loop round every LAST table held in the vtab. Each table refers to a + different observatory position and/or DUT1 value. */ + for( itable = 0; itable < nlast_tables; itable++ ) { + table = last_tables[ itable ]; + +/* See if the table refers to the given position and dut1 value, allowing + some small tolerance. */ + if( fabs( table->obslat - obslat ) < 2.0E-7 && + fabs( table->obslon - obslon ) < 2.0E-7 && + fabs( table->obsalt - obsalt ) < 1.0 && + fabs( table->dut1 - dut1 ) < 1.0E-5 ) { + +/* Get pointers to the array of epoch and corresponding LAST values in + the table. */ + ep = table->epoch; + lp = table->last; + +/* The values in the epoch array are monotonic increasing. Do a binary chop + within the table's epoch array to find the earliest entry that has a + value equal to or greater than the supplied epoch value. */ + ilo = 0; + ihi = table->nentry - 1; + while( ihi > ilo ) { + itest = ( ilo + ihi )/2; + if( ep[ itest ] >= epoch ) { + ihi = itest; + } else { + ilo = itest + 1; + } + } + +/* Get the difference between the epoch at the entry selected above and + the requested epoch. */ + dep = ep[ ilo ] - epoch; + +/* If the entry selected above is the first entry in the table, it can + only be used if it is within 0.001 second of the requested epoch. */ + if( ilo == 0 ) { + if( fabs( dep ) < 0.001/86400.0 ) { + result = lp[ 0 ]; + } + +/* If the list of epoch values contained no value that was greater than + the supplied epoch value, then we can use the last entry if + it is no more than 0.001 second away from the requested epoch. */ + } else if( dep <= 0.0 ) { + if( fabs( dep ) < 0.001/86400.0 ) { + result = lp[ ilo ]; + } + + +/* Otherwise, see if the entry selected above is sufficiently close to + its lower neighbour (i.e. closer than 0.4 days) to allow a reasonably + accurate LAST value to be determined by interpolation. */ + } else if( ep[ ilo ] - ep[ ilo - 1 ] < 0.4 ) { + ep += ilo - 1; + lp += ilo - 1; + result = *lp + ( epoch - *ep )*( lp[ 1 ] - *lp )/( ep[ 1 ] - *ep ); + +/* If the neighbouring point is too far away for interpolation to be + reliable, then we can only use the point if it is within 0.001 seconds of + the requested epoch. */ + } else if( fabs( dep ) < 0.001/86400.0 ) { + result = lp[ ilo ]; + } + +/* If we have found the right table, we do not need to look at any other + tables, so leave the table loop. */ + break; + } + } + +/* Indicate that threads may now write to the table. */ + UNLOCK_RWLOCK1 + +/* Ensure the returned value is within the range 0 - 2.PI. */ + if( result != AST__BAD ) { + while( result > 2*AST__DPI ) result -= 2*AST__DPI; + while( result < 0.0 ) result += 2*AST__DPI; + } + +/* Return the required LAST value. */ + return result; +} + +static double GetEpoch( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetEpoch + +* Purpose: +* Obtain the value of the Epoch attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetEpoch( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetEpoch method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Epoch attribute for a +* SkyFrame. A suitable default value is returned if no value has +* previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Epoch value to use. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + AstSystemType system; /* System attribute */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Initialise. */ + result = AST__BAD; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Check if a value has been set for the Epoch attribute. If so, obtain its + value. */ + if ( astTestEpoch( this ) ) { + result = (*parent_getepoch)( this_frame, status ); + +/* Otherwise, we will return a default Epoch value appropriate to the + SkyFrame class. */ + } else { + +/* Provide a default value of B1950.0 or J2000.0 depending on the System + setting. */ + system = astGetSystem( this ); + if( system == AST__FK4 || system == AST__FK4_NO_E ) { + result = palEpb2d( 1950.0 ); + } else { + result = palEpj2d( 2000.0 ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static double GetTop( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetTop + +* Purpose: +* Obtain the value of the Top attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetTop( AstFrame *this_frame, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetTop method inherited +* from the Frame class). + +* Description: +* This function returns the value of the Top attribute for a +* specified axis of a SkyFrame. A suitable default value is returned if no +* value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Top value to use. + +* Notes: +* - A value of DBL_MAX will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis_p; /* Permuted axis index */ + double result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return DBL_MAX; + +/* Initialise. */ + result = DBL_MAX; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetTop" ); + +/* Check if a value has been set for the axis Top attribute. If so, + obtain its value. */ + if ( astTestTop( this, axis ) ) { + result = (*parent_gettop)( this_frame, axis, status ); + +/* Otherwise, we will return a default Top value appropriate to the + SkyFrame class. */ + } else { + +/* If this is a latitude axis return pi/2. */ + if( axis_p == 1 ) { + result = piby2; + +/* If it is a longitude value return DBL_MAX (i.e. no upper limit). */ + } else { + result = DBL_MAX; + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = DBL_MAX; + +/* Return the result. */ + return result; +} + +static const char *GetDomain( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDomain + +* Purpose: +* Obtain a pointer to the Domain attribute string for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetDomain( AstFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetDomain protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the Domain attribute string +* for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Domain value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the SkyFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* If a Domain attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestDomain( this ) ) { + result = (*parent_getdomain)( this_frame, status ); + +/* Otherwise, provide a pointer to a suitable default string. */ + } else { + result = "SKY"; + } + +/* Return the result. */ + return result; +} + +static const char *GetFormat( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetFormat + +* Purpose: +* Access the Format string for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetFormat( AstFrame *this, int axis ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetFormat method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Format string for a specified axis +* of a SkyFrame. A pointer to a suitable default string is returned if no +* Format value has previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstAxis *ax; /* Pointer to Axis object */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *result; /* Pointer value to return */ + int as_time; /* Value of AsTime attribute */ + int as_time_set; /* AsTime attribute set? */ + int axis_p; /* Permuted axis index */ + int digits; /* Number of digits of precision */ + int is_latitude; /* Value of IsLatitude attribute */ + int is_latitude_set; /* IsLatitude attribute set? */ + int parent; /* Use parent method? */ + int skyaxis; /* Is the Axis a SkyAxis? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + as_time_set = 0; + is_latitude = 0; + is_latitude_set = 0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetFormat" ); + +/* Obtain a pointer to the Axis structure. */ + ax = astGetAxis( this, axis ); + +/* Decide whether the parent astGetFormat method is able to provide the format + string we require. We must use the parent method if the Axis is not a + SkyAxis, because the syntax of the Format string would become unsuitable + for use with the Axis astFormat method if it was over-ridden here. We also + use the parent method to return a Format pointer if an explicit Format + string has already been set. */ + skyaxis = astIsASkyAxis( ax ); + parent = ( !skyaxis || (*parent_testformat)( this_frame, axis, status ) ); + +/* If neither of the above conditions apply, we may still be able to use the + parent method if the Axis (actually a SkyAxis) is required to behave as a + normal RA or DEC axis, as this is the standard behaviour provided by the + SkyAxis class. Examine the SkyFrame's System attribute to determine if its + axes should behave in this way. */ + if ( !parent ) parent = IsEquatorial( astGetSystem( this ), status ); + +/* If using the parent method and dealing with a SkyAxis, determine the + settings of any attributes that may affect the Format string. */ + if ( astOK ) { + if ( parent ) { + if ( skyaxis ) { + as_time_set = astTestAsTime( this, axis ); + is_latitude_set = astTestAxisIsLatitude( ax ); + is_latitude = astGetAxisIsLatitude( ax ); + +/* If no AsTime value is set for the axis, set a temporary value as determined + by the astGetAsTime method, which supplies suitable defaults for the axes of + a SkyFrame. */ + if ( !as_time_set ) { + astSetAsTime( this, axis, astGetAsTime( this, axis ) ); + } + +/* Temporarly over-ride the SkyAxis IsLatitude attribute, regardless of its + setting, as the second axis of a SkyFrame is always the latitude axis. */ + astSetAxisIsLatitude( ax, axis_p == 1 ); + } + +/* Invoke the parent method to obtain a pointer to the Format string. */ + result = (*parent_getformat)( this_frame, axis, status ); + +/* Now restore the attributes that were temporarily over-ridden above to their + previous states. */ + if ( skyaxis ) { + if ( !as_time_set ) astClearAsTime( this, axis ); + if ( !is_latitude_set ) { + astClearAxisIsLatitude( ax ); + } else { + astSetAxisIsLatitude( ax, is_latitude ); + } + } + +/* If the parent method is unsuitable, we must construct a new Format string + here. This affects only those coordinate systems whose axes do not behave + like standard RA/DEC axes (e.g. typically ecliptic, galactic and + supergalactic coordinates). For these, we format values as decimal degrees + (or decimal hours if the AsTime attribute is set). Obtain the AsTime + value. */ + } else { + as_time = astGetAsTime( this, axis ); + +/* Determine how many digits of precision to use. This is obtained from the + SkyAxis Digits attribute (if set), otherwise from the Digits attribute of + the enclosing SkyFrame. */ + if ( astTestAxisDigits( ax ) ) { + digits = astGetAxisDigits( ax ); + } else { + digits = astGetDigits( this ); + } + +/* If a time format is required, generate a Format string using decimal + hours. */ + if ( astOK ) { + if ( as_time ) { + if ( digits <= 2 ) { + result = "h"; + } else { + (void) sprintf( getformat_buff, "h.%d", digits - 2 ); + result = getformat_buff; + } + +/* Otherwise use decimal degrees. */ + } else { + if ( digits <= 3 ) { + result = "d"; + } else { + (void) sprintf( getformat_buff, "d.%d", digits - 3 ); + result = getformat_buff; + } + } + } + } + } + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetLabel( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetLabel + +* Purpose: +* Access the Label string for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetLabel( AstFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetLabel method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Label string for a specified axis +* of a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSystemType system; /* Code identifying type of sky coordinates */ + const char *result; /* Pointer to label string */ + int axis_p; /* Permuted axis index */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetLabel" ); + +/* Check if a value has been set for the required axis label string. If so, + invoke the parent astGetLabel method to obtain a pointer to it. */ + if ( astTestLabel( this, axis ) ) { + result = (*parent_getlabel)( this, axis, status ); + +/* Otherwise, identify the sky coordinate system described by the SkyFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default label string. */ + if ( astOK ) { + +/* Equatorial coordinate systems. */ + if ( IsEquatorial( system, status ) ) { + result = ( axis_p == 0 ) ? "Right ascension" : + "Declination"; + +/* Ecliptic coordinates. */ + } else if ( system == AST__ECLIPTIC ) { + result = ( axis_p == 0 ) ? "Ecliptic longitude" : + "Ecliptic latitude"; + +/* Helio-ecliptic coordinates. */ + } else if ( system == AST__HELIOECLIPTIC ) { + result = ( axis_p == 0 ) ? "Helio-ecliptic longitude" : + "Helio-ecliptic latitude"; + +/* AzEl coordinates. */ + } else if ( system == AST__AZEL ) { + result = ( axis_p == 0 ) ? "Azimuth" : + "Elevation"; + +/* Galactic coordinates. */ + } else if ( system == AST__GALACTIC ) { + result = ( axis_p == 0 ) ? "Galactic longitude" : + "Galactic latitude"; + +/* Supergalactic coordinates. */ + } else if ( system == AST__SUPERGALACTIC ) { + result = ( axis_p == 0 ) ? "Supergalactic longitude" : + "Supergalactic latitude"; + +/* Unknown spherical coordinates. */ + } else if ( system == AST__UNKNOWN ) { + result = ( axis_p == 0 ) ? "Longitude" : + "Latitude"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "astGetLabel(%s): Corrupt %s contains " + "invalid sky coordinate system identification code " + "(%d).", status, astGetClass( this ), astGetClass( this ), + (int) system ); + } + +/* If the SkyRef attribute has a set value, append " offset" to the label. */ + if( astGetSkyRefIs( this ) != AST__IGNORED_REF && + ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) ) { + sprintf( getlabel_buff, "%s offset", result ); + result = getlabel_buff; + } + } + } + +/* Return the result. */ + return result; +} + +static double GetDiurab( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetDiurab + +* Purpose: +* Return the magnitude of the diurnal aberration vector. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetDiurab( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function + +* Description: +* This function returns the magnitude of the diurnal aberration +* vector. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The magnitude of the diurnal aberration vector. + +*/ + +/* Local Variables: */ + double uau; + double vau; + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* If the magnitude of the diurnal aberration vector has not yet been + found, find it now, and cache it in the SkyFrame structure. The cached + value will be reset to AST__BAD if the ObsLat attribute value is + changed. This code is transliterated from SLA_AOPPA. */ + if( this->diurab == AST__BAD ) { + palGeoc( astGetObsLat( this ), astGetObsAlt( this ), &uau, &vau ); + this->diurab = 2*AST__DPI*uau*SOLSID/C; + } + +/* Return the result, */ + return this->diurab; +} + +static double GetLAST( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetLAST + +* Purpose: +* Return the Local Apparent Sidereal Time for the SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetLAST( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function + +* Description: +* This function returns the Local Apparent Sidereal Time (LAST) +* at the moment intime given by the Epoch attribute of the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The LAST value. + +*/ + +/* Local Variables: */ + double dlast; /* Change in LAST */ + double epoch; /* Epoch (TDB MJD) */ + double last1; /* LAST at end of current interval */ + double result; /* Result value to return */ + double delta_epoch; /* Change in Epoch */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The "last" component of the SkyFrame structure holds the accurate + LAST at the moment in time given by the "eplast" (a TDB MJD) component + of the SkyFrame structure. If the current value of the SkyFrame's + Epoch attribute is not much different to "eplast" (within 0.4 of a day), + then the returned LAST value is the "last" value plus the difference + between Epoch and "eplast", converted from solar to sidereal time, + then converted to radians. This approximation seems to be good to less + than a tenth of an arcsecond. If this approximation cannot be used, + invoke SetLast to recalculate the accurate LAST and update the "eplast" + and "last" values. */ + if( this->eplast != AST__BAD ) { + epoch = astGetEpoch( this ); + delta_epoch = epoch - this->eplast; + +/* Return the current LAST value if the epoch has not changed. */ + if( delta_epoch == 0.0 ) { + result = this->last; + +/* If the previous full calculation of LAST was less than 0.4 days ago, + use a linear approximation to LAST. */ + } else if( fabs( delta_epoch ) < 0.4 ) { + +/* If we do not know the ratio of sidereal to solar time at the current + epoch, calculate it now. This involves a full calculation of LAST at + the end of the current linear approximation period. */ + if( this->klast == AST__BAD ) { + last1 = CalcLAST( this, this->eplast + 0.4, astGetObsLon( this ), + astGetObsLat( this ), astGetObsAlt( this ), + astGetDut1( this ), status ); + +/* Ensure the change in LAST is positive so that we get a positive ratio. */ + dlast = last1 - this->last; + if( dlast < 0.0 ) dlast += 2*AST__DPI; + this->klast = 2*AST__DPI*0.4/dlast; + } + +/* Now use the ratio of solar to sidereal time to calculate the linear + approximation to LAST. */ + result = this->last + 2*AST__DPI*delta_epoch/this->klast; + +/* If the last accurate calculation of LAST was more than 0.4 days ago, + do a full accurate calculation. */ + } else { + SetLast( this, status ); + result = this->last; + } + +/* If we have not yet done an accurate calculation of LAST, do one now. */ + } else { + SetLast( this, status ); + result = this->last; + } + +/* Return the result, */ + return result; +} + +static int GetIsLatAxis( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetIsLatAxis + +* Purpose: +* Test an axis to see if it is a latitude axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetIsLatAxis( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function tests if a SkyFrame axis is a celestial latitude axis. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the supplied axis is a celestial latitude axis, and zero +* otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get the index of the latitude axis and compare to the supplied axis + index. */ + result = ( axis == astGetLatAxis( this ) ); + +/* Return the result. */ + return astOK ? result : 0; + +} + +static int GetIsLonAxis( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetIsLonAxis + +* Purpose: +* Test an axis to see if it is a longitude axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetIsLonAxis( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function tests if a SkyFrame axis is a celestial longitude axis. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the supplied axis is a celestial longitude axis, and zero +* otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get the index of the longitude axis and compare to the supplied axis + index. */ + result = ( axis == astGetLonAxis( this ) ); + +/* Return the result. */ + return astOK ? result : 0; + +} + +static int GetLatAxis( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetLatAxis + +* Purpose: +* Obtain the index of the latitude axis of a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetLatAxis( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the zero-based index of the latitude axis of +* a SkyFrame, taking into account any current axis permutation. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The zero based axis index (0 or 1) of the latitude axis. + +* Notes: +* - A value of one will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + const int *perm; /* Axis permutation array */ + +/* Check the global error status. */ + if ( !astOK ) return 1; + +/* Initialise. */ + result = 1; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Identify the latitude axis. */ + if( perm[ 0 ] == 1 ) { + result = 0; + } else { + result = 1; + } + + } + +/* Return the result. */ + return result; + +} + +static int GetLonAxis( AstSkyFrame *this, int *status ) { +/* +* Name: +* GetLonAxis + +* Purpose: +* Obtain the index of the longitude axis of a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int GetLonAxis( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the zero-based index of the longitude axis of +* a SkyFrame, taking into account any current axis permutation. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The zero based axis index (0 or 1) of the longitude axis. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result to be returned */ + const int *perm; /* Axis permutation array */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 0; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Identify the longitude axis. */ + if( perm[ 0 ] == 0 ) { + result = 0; + } else { + result = 1; + } + + } + +/* Return the result. */ + return result; + +} + +static double GetSkyRefP( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* GetSkyRefP + +* Purpose: +* Obtain the value of the SkyRefP attribute for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double GetSkyRefP( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns the value of the SkyRefP attribute for a +* SkyFrame axis, providing suitable defaults. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The SkyRefP value to be used. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + double result; /* Returned value */ + int axis_p; /* Permuted axis index */ + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetSkyRefP" ); + +/* Check if a value has been set for the required axis. If so, return it. */ + if( this->skyrefp[ axis_p ] != AST__BAD ) { + result = this->skyrefp[ axis_p ]; + +/* Otherwise, return the default value */ + } else { + +/* The default longitude value is always zero. */ + if( axis_p == 0 ) { + result= 0.0; + +/* The default latitude value depends on SkyRef. The usual default is the + north pole. The exception to this is if the SkyRef attribute identifies + either the north or the south pole, in which case the origin is used as + the default. Allow some tolerance. */ + } else if( fabs( cos( this->skyref[ 1 ] ) ) > 1.0E-10 ) { + result = pi/2; + + } else { + result = 0.0; + } + } + +/* Return the result. */ + return result; +} + +static const char *GetSymbol( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetSymbol + +* Purpose: +* Obtain a pointer to the Symbol string for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetSymbol( AstFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetSymbol method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Symbol string for a specified axis +* of a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSystemType system; /* Code identifying type of sky coordinates */ + const char *result; /* Pointer to symbol string */ + int axis_p; /* Permuted axis index */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate and permute the axis index. */ + axis_p = astValidateAxis( this, axis, 1, "astGetSymbol" ); + +/* Check if a value has been set for the required axis symbol string. If so, + invoke the parent astGetSymbol method to obtain a pointer to it. */ + if ( astTestSymbol( this, axis ) ) { + result = (*parent_getsymbol)( this, axis, status ); + +/* Otherwise, identify the sky coordinate system described by the SkyFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default Symbol string. */ + if ( astOK ) { + +/* Equatorial coordinate systems. */ + if ( IsEquatorial( system, status ) ) { + result = ( axis_p == 0 ) ? "RA" : "Dec"; + +/* Ecliptic coordinates. */ + } else if ( system == AST__ECLIPTIC ) { + result = ( axis_p == 0 ) ? "Lambda" : "Beta"; + +/* Helio-ecliptic coordinates. */ + } else if ( system == AST__HELIOECLIPTIC ) { + result = ( axis_p == 0 ) ? "Lambda" : "Beta"; + +/* AzEl coordinates. */ + } else if ( system == AST__AZEL ) { + result = ( axis_p == 0 ) ? "Az" : "El"; + +/* Galactic coordinates. */ + } else if ( system == AST__GALACTIC ) { + result = ( axis_p == 0 ) ? "l" : "b"; + +/* Supergalactic coordinates. */ + } else if ( system == AST__SUPERGALACTIC ) { + result = ( axis_p == 0 ) ? "SGL" : "SGB"; + +/* Unknown spherical coordinates. */ + } else if ( system == AST__UNKNOWN ) { + result = ( axis_p == 0 ) ? "Lon" : "Lat"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "astGetSymbol(%s): Corrupt %s contains " + "invalid sky coordinate system identification code " + "(%d).", status, astGetClass( this ), astGetClass( this ), + (int) system ); + } + +/* If the SkyRef attribute had a set value, prepend "D" (for "delta") to the + Symbol. */ + if( astGetSkyRefIs( this ) != AST__IGNORED_REF && + ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) ) { + sprintf( getsymbol_buff, "D%s", result ); + result = getsymbol_buff; + } + } + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetAlignSystem + +* Purpose: +* Obtain the AlignSystem attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetAlignSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the AlignSystem attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The AlignSystem value. + +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* If a AlignSystem attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestAlignSystem( this ) ) { + result = (*parent_getalignsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__ICRS; + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetSystem + +* Purpose: +* Obtain the System attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstSystemType GetSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the System attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* If a System attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestSystem( this ) ) { + result = (*parent_getsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__ICRS; + } + +/* Return the result. */ + return result; +} + +static const char *GetTitle( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetTitle + +* Purpose: +* Obtain a pointer to the Title string for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetTitle( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetTitle method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Title string for a SkyFrame. +* A pointer to a suitable default string is returned if no Title value has +* previously been set. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + AstSystemType system; /* Code identifying type of sky coordinates */ + const char *extra; /* Pointer to extra information */ + const char *p; /* Character pointer */ + const char *projection; /* Pointer to sky projection description */ + const char *result; /* Pointer to result string */ + const char *word; /* Pointer to critical word */ + double epoch; /* Value of Epoch attribute */ + double equinox; /* Value of Equinox attribute */ + int lextra; /* Length of extra information */ + int offset; /* Using offset coordinate system? */ + int pos; /* Buffer position to enter text */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + pos = 0; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* See if a Title string has been set. If so, use the parent astGetTitle + method to obtain a pointer to it. */ + if ( astTestTitle( this ) ) { + result = (*parent_gettitle)( this_frame, status ); + +/* Otherwise, we will generate a default Title string. Obtain the values of the + SkyFrame's attributes that determine what this string will be. */ + } else { + epoch = astGetEpoch( this ); + equinox = astGetEquinox( this ); + projection = astGetProjection( this ); + system = astGetSystem( this ); + +/* See if an offset coordinate system is being used.*/ + offset = ( astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ) ) + && ( astGetSkyRefIs( this ) != AST__IGNORED_REF ); + +/* Use this to determine if the word "coordinates" or "offsets" should be + used.*/ + word = offset ? "offsets" : "coordinates"; + +/* Classify the coordinate system type and create an appropriate Title + string. (Note that when invoking the astFmtDecimalYr function we must + use a separate sprintf on each occasion so as not to over-write its + internal buffer before the result string has been used.) */ + if ( astOK ) { + result = gettitle_buff; + switch ( system ) { + +/* FK4 equatorial coordinates. */ +/* --------------------------- */ +/* Display the Equinox and Epoch values. */ + case AST__FK4: + pos = sprintf( gettitle_buff, "FK4 equatorial %s", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox B%s", + astFmtDecimalYr( palEpb( equinox ), 9 ) ); + } + if( astTestEpoch( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, + "; epoch B%s", astFmtDecimalYr( palEpb( epoch ), 9 ) ); + } + break; + +/* FK4 coordinates with no E-terms of aberration. */ +/* ---------------------------------------------- */ +/* Display the Equinox and Epoch values. */ + case AST__FK4_NO_E: + pos = sprintf( gettitle_buff, "FK4 equatorial %s; no E-terms", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox B%s", + astFmtDecimalYr( palEpb( equinox ), 9 ) ); + } + if( astTestEpoch( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, + "; epoch B%s", astFmtDecimalYr( palEpb( epoch ), 9 ) ); + } + break; + +/* FK5 equatorial coordinates. */ +/* --------------------------- */ +/* Display only the Equinox value. */ + case AST__FK5: + pos = sprintf( gettitle_buff, "FK5 equatorial %s", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox J%s", + astFmtDecimalYr( palEpj( equinox ), 9 ) ); + } + break; + +/* J2000 equatorial coordinates. */ +/* ----------------------------- */ +/* Based on the dynamically determined mean equator and equinox of J2000, + rather than on a model such as FK4 or FK5 */ + case AST__J2000: + pos = sprintf( gettitle_buff, "J2000 equatorial %s", word ); + break; + +/* ICRS coordinates. */ +/* ----------------- */ +/* ICRS is only like RA/Dec by co-incidence, it is not really an + equatorial system by definition. */ + case AST__ICRS: + pos = sprintf( gettitle_buff, "ICRS %s", word ); + break; + +/* AzEl coordinates. */ +/* ----------------- */ + case AST__AZEL: + pos = sprintf( gettitle_buff, "Horizon (Azimuth/Elevation) %s", word ); + break; + +/* Geocentric apparent equatorial coordinates. */ +/* ------------------------------------------ */ +/* Display only the Epoch value. */ + case AST__GAPPT: + pos = sprintf( gettitle_buff, + "Geocentric apparent equatorial %s; " + "; epoch J%s", word, astFmtDecimalYr( palEpj( epoch ), 9 ) ); + break; + +/* Ecliptic coordinates. */ +/* --------------------- */ +/* Display only the Equinox value. */ + case AST__ECLIPTIC: + pos = sprintf( gettitle_buff, "Ecliptic %s", word ); + if( astTestEquinox( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; mean equinox J%s", + astFmtDecimalYr( palEpj( equinox ), 9 ) ); + } + break; + +/* Helio-ecliptic coordinates. */ +/* --------------------------- */ +/* Display only the Epoch value (equinox is fixed). */ + case AST__HELIOECLIPTIC: + pos = sprintf( gettitle_buff, "Helio-ecliptic %s; mean equinox J2000", word ); + if( astTestEpoch( this ) || astGetUseDefs( this ) ) { + pos += sprintf( gettitle_buff + pos, "; epoch J%s", + astFmtDecimalYr( palEpj( epoch ), 9 ) ); + } + break; + +/* Galactic coordinates. */ +/* --------------------- */ +/* Do not display an Equinox or Epoch value. */ + case AST__GALACTIC: + pos = sprintf( gettitle_buff, "IAU (1958) galactic %s", word ); + break; + +/* Supergalactic coordinates. */ +/* -------------------------- */ +/* Do not display an Equinox or Epoch value. */ + case AST__SUPERGALACTIC: + pos = sprintf( gettitle_buff, + "De Vaucouleurs supergalactic %s", word ); + break; + +/* Unknown coordinates. */ +/* -------------------------- */ + case AST__UNKNOWN: + pos = sprintf( gettitle_buff, + "Spherical %s", word ); + break; + +/* Report an error if the coordinate system was not recognised. */ + default: + astError( AST__SCSIN, "astGetTitle(%s): Corrupt %s contains " + "invalid sky coordinate system identification code " + "(%d).", status, astGetClass( this ), astGetClass( this ), + (int) system ); + break; + } + +/* If OK, we add either a description of the sky projection, or (if used) + a description of the origin or pole of the offset coordinate system. + We include only one of these two strings in order to keep the length + of the title down to a reasonable value.*/ + if ( astOK ) { + +/* If the SkyRef attribute has set values, create a description of the offset + coordinate system. */ + if( offset ){ + word = ( astGetSkyRefIs( this ) == AST__POLE_REF )?"pole":"origin"; + lextra = sprintf( gettitle_buff2, "%s at %s ", word, + astFormat( this, 0, astGetSkyRef( this, 0 ) ) ); + lextra += sprintf( gettitle_buff2 + lextra, "%s", + astFormat( this, 1, astGetSkyRef( this, 1 ) ) ); + extra = gettitle_buff2; + +/* Otherwise, get the sky projection description. */ + } else { + extra = projection; + +/* Determine the length of the extra information, after removing trailing + white space. */ + for ( lextra = (int) strlen( extra ); lextra > 0; lextra-- ) { + if ( !isspace( extra[ lextra - 1 ] ) ) break; + } + } + +/* If non-blank extra information is available, append it to the title string, + checking that the end of the buffer is not over-run. */ + if ( lextra ) { + p = "; "; + while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && *p ) gettitle_buff[ pos++ ] = *p++; + p = extra; + while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && + ( p < ( extra + lextra ) ) ) gettitle_buff[ pos++ ] = *p++; + if( extra == projection ) { + p = " projection"; + while ( ( pos < AST__SKYFRAME_GETTITLE_BUFF_LEN ) && *p ) gettitle_buff[ pos++ ] = *p++; + } + gettitle_buff[ pos ] = '\0'; + } + } + } + } + +/* If an error occurred, clear the returned pointer value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetUnit( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetUnit + +* Purpose: +* Obtain a pointer to the Unit string for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *GetUnit( AstFrame *this_frame, int axis ) + +* Class Membership: +* SkyFrame member function (over-rides the astGetUnit method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Unit string for a specified axis +* of a SkyFrame. If the Unit attribute has not been set for the axis, a +* pointer to a suitable default string is returned instead. This string may +* depend on the value of the Format attribute for the axis and, in turn, on +* the type of sky coordinate system that the SkyFrame describes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the axis (zero-based) for which information is required. + +* Returned Value: +* A pointer to a null-terminated string containing the Unit value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const char *result; /* Pointer value to return */ + int format_set; /* Format attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astGetUnit" ); + +/* The Unit value may depend on the value of the Format attribute, so + determine if a Format value has been set for the axis and set a + temporary value if it has not. Use the GetFormat member function + for this class together with member functions inherited from the + parent class (rather than using the object's methods directly) + because if any of these methods have been over-ridden by a derived + class the Format string syntax may no longer be compatible with + this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the parent GetUnit method to return a pointer to the required Unit + string. */ + result = (*parent_getunit)( this_frame, axis, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +void astInitSkyFrameVtab_( AstSkyFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSkyFrameVtab + +* Purpose: +* Initialise a virtual function table for a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* void astInitSkyFrameVtab( AstSkyFrameVtab *vtab, const char *name ) + +* Class Membership: +* SkyFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SkyFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + int stat; /* SLALIB status */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASkyFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->ClearAsTime = ClearAsTime; + vtab->ClearEquinox = ClearEquinox; + vtab->ClearNegLon = ClearNegLon; + vtab->ClearProjection = ClearProjection; + vtab->GetAsTime = GetAsTime; + vtab->GetEquinox = GetEquinox; + vtab->GetNegLon = GetNegLon; + vtab->GetIsLatAxis = GetIsLatAxis; + vtab->GetIsLonAxis = GetIsLonAxis; + vtab->GetLatAxis = GetLatAxis; + vtab->GetLonAxis = GetLonAxis; + vtab->GetProjection = GetProjection; + vtab->SetAsTime = SetAsTime; + vtab->SetEquinox = SetEquinox; + vtab->SetNegLon = SetNegLon; + vtab->SetProjection = SetProjection; + vtab->SkyOffsetMap = SkyOffsetMap; + vtab->TestAsTime = TestAsTime; + vtab->TestEquinox = TestEquinox; + vtab->TestNegLon = TestNegLon; + vtab->TestProjection = TestProjection; + + vtab->TestSkyTol = TestSkyTol; + vtab->SetSkyTol = SetSkyTol; + vtab->GetSkyTol = GetSkyTol; + vtab->ClearSkyTol = ClearSkyTol; + + vtab->TestSkyRef = TestSkyRef; + vtab->SetSkyRef = SetSkyRef; + vtab->GetSkyRef = GetSkyRef; + vtab->ClearSkyRef = ClearSkyRef; + + vtab->TestSkyRefP = TestSkyRefP; + vtab->SetSkyRefP = SetSkyRefP; + vtab->GetSkyRefP = GetSkyRefP; + vtab->ClearSkyRefP = ClearSkyRefP; + + vtab->TestSkyRefIs = TestSkyRefIs; + vtab->SetSkyRefIs = SetSkyRefIs; + vtab->GetSkyRefIs = GetSkyRefIs; + vtab->ClearSkyRefIs = ClearSkyRefIs; + + vtab->TestAlignOffset = TestAlignOffset; + vtab->SetAlignOffset = SetAlignOffset; + vtab->GetAlignOffset = GetAlignOffset; + vtab->ClearAlignOffset = ClearAlignOffset; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_gettop = frame->GetTop; + frame->GetTop = GetTop; + + parent_setobsalt = frame->SetObsAlt; + frame->SetObsAlt = SetObsAlt; + + parent_setobslat = frame->SetObsLat; + frame->SetObsLat = SetObsLat; + + parent_setobslon = frame->SetObsLon; + frame->SetObsLon = SetObsLon; + + parent_clearobslon = frame->ClearObsLon; + frame->ClearObsLon = ClearObsLon; + + parent_clearobsalt = frame->ClearObsAlt; + frame->ClearObsAlt = ClearObsAlt; + + parent_clearobslat = frame->ClearObsLat; + frame->ClearObsLat = ClearObsLat; + + parent_getbottom = frame->GetBottom; + frame->GetBottom = GetBottom; + + parent_getepoch = frame->GetEpoch; + frame->GetEpoch = GetEpoch; + + parent_format = frame->Format; + frame->Format = Format; + parent_gap = frame->Gap; + frame->Gap = Gap; + parent_getdirection = frame->GetDirection; + frame->GetDirection = GetDirection; + parent_getdomain = frame->GetDomain; + frame->GetDomain = GetDomain; + parent_getsystem = frame->GetSystem; + frame->GetSystem = GetSystem; + parent_setsystem = frame->SetSystem; + frame->SetSystem = SetSystem; + parent_clearsystem = frame->ClearSystem; + frame->ClearSystem = ClearSystem; + parent_getalignsystem = frame->GetAlignSystem; + frame->GetAlignSystem = GetAlignSystem; + parent_getformat = frame->GetFormat; + frame->GetFormat = GetFormat; + parent_getlabel = frame->GetLabel; + frame->GetLabel = GetLabel; + parent_getsymbol = frame->GetSymbol; + frame->GetSymbol = GetSymbol; + parent_gettitle = frame->GetTitle; + frame->GetTitle = GetTitle; + parent_getunit = frame->GetUnit; + frame->GetUnit = GetUnit; + parent_match = frame->Match; + frame->Match = Match; + parent_overlay = frame->Overlay; + frame->Overlay = Overlay; + parent_subframe = frame->SubFrame; + frame->SubFrame = SubFrame; + parent_unformat = frame->Unformat; + frame->Unformat = Unformat; + + parent_setdut1 = frame->SetDut1; + frame->SetDut1 = SetDut1; + + parent_cleardut1 = frame->ClearDut1; + frame->ClearDut1 = ClearDut1; + +/* Store replacement pointers for methods which will be over-ridden by new + member functions implemented here. */ + frame->Angle = Angle; + frame->Distance = Distance; + frame->FrameGrid = FrameGrid; + frame->Intersect = Intersect; + frame->Norm = Norm; + frame->NormBox = NormBox; + frame->Resolve = Resolve; + frame->ResolvePoints = ResolvePoints; + frame->Offset = Offset; + frame->Offset2 = Offset2; + frame->ValidateSystem = ValidateSystem; + frame->SystemString = SystemString; + frame->SystemCode = SystemCode; + frame->LineDef = LineDef; + frame->LineContains = LineContains; + frame->LineCrossing = LineCrossing; + frame->LineOffset = LineOffset; + frame->GetActiveUnit = GetActiveUnit; + frame->TestActiveUnit = TestActiveUnit; + frame->MatchAxesX = MatchAxesX; + +/* Store pointers to inherited methods that will be invoked explicitly + by this class. */ + parent_clearformat = frame->ClearFormat; + parent_setformat = frame->SetFormat; + parent_testformat = frame->TestFormat; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "SkyFrame", + "Description of celestial coordinate system" ); + +/* Initialize constants for converting between hours, degrees and + radians, etc.. */ + LOCK_MUTEX2 + palDtf2r( 1, 0, 0.0, &hr2rad, &stat ); + palDaf2r( 1, 0, 0.0, °2rad, &stat ); + palDaf2r( 180, 0, 0.0, &pi, &stat ); + piby2 = 0.5*pi; + UNLOCK_MUTEX2 + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void Intersect( AstFrame *this_frame, const double a1[2], + const double a2[2], const double b1[2], + const double b2[2], double cross[2], + int *status ) { +/* +* Name: +* Intersect + +* Purpose: +* Find the point of intersection between two geodesic curves. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Intersect( AstFrame *this_frame, const double a1[2], +* const double a2[2], const double b1[2], +* const double b2[2], double cross[2], +* int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astIntersect method +* inherited from the Frame class). + +* Description: +* This function finds the coordinate values at the point of +* intersection between two geodesic curves. Each curve is specified +* by two points on the curve. + +* Parameters: +* this +* Pointer to the SkyFrame. +* a1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the first +* geodesic curve. +* a2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on the +* first geodesic curve. +* b1 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a point on the second +* geodesic curve. +* b2 +* An array of double, with one element for each Frame axis. +* This should contain the coordinates of a second point on +* the second geodesic curve. +* cross +* An array of double, with one element for each Frame axis +* in which the coordinates of the required intersection +* point will be returned. These will be AST__BAD if the curves do +* not intersect. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - For SkyFrames each curve will be a great circle, and in general +* each pair of curves will intersect at two diametrically opposite +* points on the sky. The returned position is the one which is +* closest to point "a1". +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double aa1[ 2 ]; /* Permuted coordinates for a1 */ + double aa2[ 2 ]; /* Permuted coordinates for a2 */ + double bb1[ 2 ]; /* Permuted coordinates for b1 */ + double bb2[ 2 ]; /* Permuted coordinates for b2 */ + double cc[ 2 ]; /* Permuted coords at intersection */ + double d1; /* Cos(distance from a1 to vp) */ + double d2; /* Cos(distance from a1 to -vp) */ + double na[ 3 ]; /* Normal to the a1/a2 great circle */ + double nb[ 3 ]; /* Normal to the b1/b2 great circle */ + double va1[ 3 ]; /* Vector pointing at a1 */ + double va2[ 3 ]; /* Vector pointing at a2 */ + double vb1[ 3 ]; /* Vector pointing at b1 */ + double vb2[ 3 ]; /* Vector pointing at b2 */ + double vmod; /* Length of "vp" */ + double vp[ 3 ]; /* Vector pointing at the intersection */ + double vpn[ 3 ]; /* Normalised vp */ + int iaxis; /* Axis index */ + +/* Initialise. */ + cross[ 0 ] = AST__BAD; + cross[ 1 ] = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Check that all supplied values are OK. */ + if ( ( a1[ 0 ] != AST__BAD ) && ( a1[ 1 ] != AST__BAD ) && + ( a2[ 0 ] != AST__BAD ) && ( a2[ 1 ] != AST__BAD ) && + ( b1[ 0 ] != AST__BAD ) && ( b1[ 1 ] != AST__BAD ) && + ( b2[ 0 ] != AST__BAD ) && ( b2[ 1 ] != AST__BAD ) ) { + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Apply the axis permutation array to obtain the coordinates of + the points in the required (longitude,latitude) order. */ + for( iaxis = 0; iaxis < 2; iaxis++ ) { + aa1[ perm[ iaxis ] ] = a1[ iaxis ]; + aa2[ perm[ iaxis ] ] = a2[ iaxis ]; + bb1[ perm[ iaxis ] ] = b1[ iaxis ]; + bb2[ perm[ iaxis ] ] = b2[ iaxis ]; + } + +/* Convert each (lon,lat) pair into a unit length 3-vector. */ + palDcs2c( aa1[ 0 ], aa1[ 1 ], va1 ); + palDcs2c( aa2[ 0 ], aa2[ 1 ], va2 ); + palDcs2c( bb1[ 0 ], bb1[ 1 ], vb1 ); + palDcs2c( bb2[ 0 ], bb2[ 1 ], vb2 ); + +/* Find the normal vectors to the two great cicles. */ + palDvxv( va1, va2, na ); + palDvxv( vb1, vb2, nb ); + +/* The cross product of the two normal vectors points to one of the + two diametrically opposite intersections. */ + palDvxv( na, nb, vp ); + +/* Normalise the "vp" vector, also obtaining its original modulus. */ + palDvn( vp, vpn, &vmod ); + if( vmod != 0.0 ) { + +/* We want the intersection which is closest to "a1". The dot product + gives the cos(distance) between two positions. So find the dot + product between "a1" and "vpn", and then between "a1" and the point + diametrically opposite "vpn". */ + d1 = palDvdv( vpn, va1 ); + vpn[ 0 ] = -vpn[ 0 ]; + vpn[ 1 ] = -vpn[ 1 ]; + vpn[ 2 ] = -vpn[ 2 ]; + d2 = palDvdv( vpn, va1 ); + +/* Revert to "vpn" if it is closer to "a1". */ + if( d1 > d2 ) { + vpn[ 0 ] = -vpn[ 0 ]; + vpn[ 1 ] = -vpn[ 1 ]; + vpn[ 2 ] = -vpn[ 2 ]; + } + +/* Convert the vector back into a (lon,lat) pair, and put the longitude + into the range 0 to 2.pi. */ + palDcc2s( vpn, cc, cc + 1 ); + *cc = palDranrm( *cc ); + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + cross[ 0 ] = cc[ perm[ 0 ] ]; + cross[ 1 ] = cc[ perm[ 1 ] ]; + } + } + } +} + +static int IsEquatorial( AstSystemType system, int *status ) { +/* +* Name: +* IsEquatorial + +* Purpose: +* Test for an equatorial sky coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int IsEquatorial( AstSystemType system, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns a boolean value to indicate if a sky coordinate +* system is equatorial. + +* Parameters: +* system +* Code to identify the sky coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the sky coordinate system is equatorial, otherwise zero. + +* Notes: +* - A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Determine if the sky coordinate system is an equatorial one. Note, + ICRS is not equatorial by definition, but is included here because it + is normally treated as an equatorial system in terms of the axis + labels, formats, etc. */ + result = ( ( system == AST__FK4 ) || + ( system == AST__FK4_NO_E ) || + ( system == AST__ICRS ) || + ( system == AST__FK5 ) || + ( system == AST__J2000 ) || + ( system == AST__GAPPT ) ); + +/* Return the result. */ + return result; +} + +static int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) { +/* +* Name: +* LineContains + +* Purpose: +* Determine if a line contains a point. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int LineContains( AstFrame *this, AstLineDef *l, int def, double *point, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineContains +* method inherited from the Frame class). + +* Description: +* This function determines if the supplied point is on the supplied +* line within the supplied Frame. The start point of the line is +* considered to be within the line, but the end point is not. The tests +* are that the point of closest approach of the line to the point should +* be between the start and end, and that the distance from the point to +* the point of closest aproach should be less than 1.0E-7 of the length +* of the line. + +* Parameters: +* this +* Pointer to the Frame. +* l +* Pointer to the structure defining the line. +* def +* Should be set non-zero if the "point" array was created by a +* call to astLineCrossing (in which case it may contain extra +* information following the axis values),and zero otherwise. +* point +* Point to an array containing the axis values of the point to be +* tested, possibly followed by extra cached information (see "def"). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the line contains the point. + +* Notes: +* - The pointer supplied for "l" should have been created using the +* astLineDef method. These structures contained cached information about +* the lines which improve the efficiency of this method when many +* repeated calls are made. An error will be reported if the structure +* does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + SkyLineDef *sl; /* SkyLine information */ + const int *perm; /* Pointer to axis permutation array */ + double *b; /* Pointer to Cartesian coords array */ + double bb[3]; /* Buffer for Cartesian coords */ + double p1[2]; /* Buffer for Spherical coords */ + double t1, t2; + int result; /* Returned value */ + +/* Initialise */ + result =0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check that the line refers to the supplied Frame. */ + if( l->frame != this ) { + astError( AST__INTER, "astLineContains(%s): The supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* Check the axis values are good */ + } else if( point[ 0 ] != AST__BAD && point[ 1 ] != AST__BAD ){ + +/* Get a pointer to an array holding the corresponding Cartesian coords. */ + if( def ) { + b = point + 2; + + } else { + perm = astGetPerm( this ); + if ( perm ) { + p1[ perm[ 0 ] ] = point[ 0 ]; + p1[ perm[ 1 ] ] = point[ 1 ]; + palDcs2c( p1[ 0 ], p1[ 1 ], bb ); + b = bb; + } else { + b = NULL; + } + } + +/* Recast the supplied AstLineDef into a SkyLineDef to get the different + structure (we know from the above check on the Frame that it is safe to + do this). */ + sl = (SkyLineDef *) l; + +/* Check that the point of closest approach of the line to the point is + within the limits of the line. */ + if( LineIncludes( sl, b, status ) ){ + +/* Check that the point is 90 degrees away from the pole of the great + circle containing the line. */ + t1 = palDvdv( sl->q, b ); + t2 = 1.0E-7*sl->length; + if( t2 < 1.0E-10 ) t2 = 1.0E-10; + if( fabs( t1 ) <= t2 ) result = 1; + } + } + +/* Return the result. */ + return result; +} + +static int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, + double **cross, int *status ) { +/* +* Name: +* LineCrossing + +* Purpose: +* Determine if two lines cross. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int LineCrossing( AstFrame *this, AstLineDef *l1, AstLineDef *l2, +* double **cross, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineCrossing +* method inherited from the Frame class). + +* Description: +* This function determines if the two suplied line segments cross, +* and if so returns the axis values at the point where they cross. +* A flag is also returned indicating if the crossing point occurs +* within the length of both line segments, or outside one or both of +* the line segments. + +* Parameters: +* this +* Pointer to the Frame. +* l1 +* Pointer to the structure defining the first line. +* l2 +* Pointer to the structure defining the second line. +* cross +* Pointer to a location at which to put a pointer to a dynamically +* alocated array containing the axis values at the crossing. If +* NULL is supplied no such array is returned. Otherwise, the returned +* array should be freed using astFree when no longer needed. If the +* lines are parallel (i.e. do not cross) then AST__BAD is returned for +* all axis values. Note usable axis values are returned even if the +* lines cross outside the segment defined by the start and end points +* of the lines. The order of axes in the returned array will take +* account of the current axis permutation array if appropriate. Note, +* sub-classes such as SkyFrame may append extra values to the end +* of the basic frame axis values. A NULL pointer is returned if an +* error occurs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the lines cross at a point which is +* within the [start,end) segment of both lines. If the crossing point +* is outside this segment on either line, or if the lines are parallel, +* zero is returned. Note, the start point is considered to be inside +* the length of the segment, but the end point is outside. + +* Notes: +* - The pointers supplied for "l1" and "l2" should have been created +* using the astLineDef method. These structures contained cached +* information about the lines which improve the efficiency of this method +* when many repeated calls are made. An error will be reported if +* either structure does not refer to the Frame specified by "this". +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + SkyLineDef *sl1; /* SkyLine information for line 1 */ + SkyLineDef *sl2; /* SkyLine information for line 2 */ + const int *perm; /* Pointer to axis permutation array */ + double *crossing; /* Pointer to returned array */ + double *b; /* Pointer to Cartesian coords */ + double len; /* Vector length */ + double p[ 2 ]; /* Temporary (lon,lat) pair */ + double temp[ 3 ]; /* Temporary vector */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + if( cross ) *cross = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate returned array (2 elements for the lon and lat values, plus 3 + for the corresponding (x,y,z) coords). */ + crossing = astMalloc( sizeof(double)*5 ); + +/* Check that both lines refer to the supplied Frame. */ + if( l1->frame != this ) { + astError( AST__INTER, "astLineCrossing(%s): First supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + + } else if( l2->frame != this ) { + astError( AST__INTER, "astLineCrossing(%s): Second supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* Recast the supplied AstLineDefs into a SkyLineDefs to get the different + structure (we know from the above check on the Frame that it is safe to + do this). */ + } else if( crossing ){ + sl1 = (SkyLineDef *) l1; + sl2 = (SkyLineDef *) l2; + +/* Point of intersection of the two great circles is perpendicular to the + pole vectors of both great circles. Put the Cartesian coords in elements + 2 to 4 of the returned array. */ + palDvxv( sl1->q, sl2->q, temp ); + b = crossing + 2; + palDvn( temp, b, &len ); + +/* See if this point is within the length of both arcs. If so return it. */ + if( LineIncludes( sl2, b, status ) && LineIncludes( sl1, b, status ) ) { + result = 1; + +/* If not, see if the negated b vector is within the length of both arcs. + If so return it. Otherwise, we return zero. */ + } else { + b[ 0 ] *= -1.0; + b[ 1 ] *= -1.0; + b[ 2 ] *= -1.0; + if( LineIncludes( sl2, b, status ) && LineIncludes( sl1, b, status ) ) result = 1; + } + +/* Store the spherical coords in elements 0 and 1 of the returned array. */ + palDcc2s( b, p, p + 1 ); + +/* Permute the spherical axis value into the order used by the SkyFrame. */ + perm = astGetPerm( this ); + if( perm ){ + crossing[ 0 ] = p[ perm[ 0 ] ]; + crossing[ 1 ] = p[ perm[ 1 ] ]; + } + } + +/* If an error occurred, return 0. */ + if( !astOK ) { + result = 0; + crossing = astFree( crossing ); + } + +/* Return the array */ + if( cross ) { + *cross = crossing; + } else { + crossing = astFree( crossing ); + } + +/* Return the result. */ + return result; +} + +static AstLineDef *LineDef( AstFrame *this, const double start[2], + const double end[2], int *status ) { +/* +* Name: +* LineDef + +* Purpose: +* Creates a structure describing a line segment in a 2D Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstLineDef *LineDef( AstFrame *this, const double start[2], +* const double end[2], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineDef +* method inherited from the Frame class). + +* Description: +* This function creates a structure containing information describing a +* given line segment within the supplied 2D Frame. This may include +* information which allows other methods such as astLineCrossing to +* function more efficiently. Thus the returned structure acts as a +* cache to store intermediate values used by these other methods. + +* Parameters: +* this +* Pointer to the Frame. Must have 2 axes. +* start +* An array of 2 doubles marking the start of the line segment. +* end +* An array of 2 doubles marking the end of the line segment. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the memory structure containing the description of the +* line. This structure should be freed using astFree when no longer +* needed. A NULL pointer is returned (without error) if any of the +* supplied axis values are AST__BAD. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + SkyLineDef *result; /* Returned value */ + const int *perm; /* Axis permutation array */ + double le; /* Length of end vector */ + double len; /* Permuted point1 coordinates */ + double ls; /* Length of start vector */ + double p1[ 2 ]; /* Permuted point1 coordinates */ + double p2[ 2 ]; /* Permuted point2 coordinates */ + double temp[3]; /* Cartesian coords at offset position */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Check the axis values are good */ + if( start[ 0 ] != AST__BAD && start[ 1 ] != AST__BAD && + end[ 0 ] != AST__BAD && end[ 1 ] != AST__BAD ) { + +/* Allocate memory for the returned structure. */ + result = astMalloc( sizeof( SkyLineDef ) ); + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( perm ) { + +/* Apply the axis permutation array to obtain the coordinates of the two + input points in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = start[ 0 ]; + p1[ perm[ 1 ] ] = start[ 1 ]; + p2[ perm[ 0 ] ] = end[ 0 ]; + p2[ perm[ 1 ] ] = end[ 1 ]; + +/* Convert each point into a 3-vector of unit length and store in the + returned structure. */ + palDcs2c( p1[ 0 ], p1[ 1 ], result->start ); + palDcs2c( p2[ 0 ], p2[ 1 ], result->end ); + +/* Calculate the great circle distance between the points in radians and + store in the result structure. Correct for rounding errors in palDcs2c + that can result in the vectors not having exactly unit length. */ + result->length = palDvdv( result->start, result->end ); + ls = result->start[0]*result->start[0] + + result->start[1]*result->start[1] + + result->start[2]*result->start[2]; + le = result->end[0]*result->end[0] + + result->end[1]*result->end[1] + + result->end[2]*result->end[2]; + result->length = acos( result->length/sqrt( ls*le ) ); + +/* Find a unit vector representing the pole of the system in which the + equator is given by the great circle. This is such that going the + short way from the start to the end, the pole is to the left of the + line as seen by the observer (i.e. from the centre of the sphere). + If the line has zero length, or 180 degrees length, the pole is + undefined, so we use an arbitrary value. */ + if( result->length == 0.0 || result->length > pi - 5.0E-11 ) { + palDcs2c( p1[ 0 ] + 0.01, p1[ 1 ] + 0.01, temp ); + palDvxv( temp, result->start, result->dir ); + } else { + palDvxv( result->end, result->start, result->dir ); + } + palDvn( result->dir, result->q, &len ); + +/* Also store a point which is 90 degrees along the great circle from the + start. */ + palDvxv( result->start, result->q, result->dir ); + +/* Store a pointer to the defining SkyFrame. */ + result->frame = this; + +/* Indicate that the line is considered to be terminated at the start and + end points. */ + result->infinite = 0; + +/* Normalise the spherical start and end positions stored in the returned + structure. */ + result->start_2d[ 0 ] = start[ 0 ]; + result->start_2d[ 1 ] = start[ 1 ]; + result->end_2d[ 0 ] = end[ 0 ]; + result->end_2d[ 1 ] = end[ 1 ]; + + astNorm( this, result->start_2d ); + astNorm( this, result->end_2d ); + } + } + +/* Free the returned pointer if an error occurred. */ + if( !astOK ) result = astFree( result ); + +/* Return a pointer to the output structure. */ + return (AstLineDef *) result; +} + +static int LineIncludes( SkyLineDef *l, double point[3], int *status ) { +/* +* Name: +* LineIncludes + +* Purpose: +* Determine if a line includes a point which is known to be in the +* great circle. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int LineIncludes( SkyLineDef *l, double point[3], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineIncludes +* method inherited from the Frame class). + +* Description: +* The supplied point is assumed to be a point on the great circle of +* which the supplied line is a segment. This function returns true if +* "point" is within the bounds of the segment (the end point of the +* line is assumed * not to be part of the segment). + +* Parameters: +* l +* Pointer to the structure defining the line. +* point +* An array holding the Cartesian coords of the point to be tested. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the line includes the point. + +* Notes: +* - Zero will be returned if this function is invoked with the global +* error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + double t1, t2, t3; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* If the line is of infite length, it is assumed to include the supplied + point. */ + if( l->infinite ) return 1; + +/* Otherwise, get the unsigned distance of the point from the start of the + line in the range 0 - 180 degs. Check it is less than the line length. + Then check that the point is not more than 90 degs away from the quarter + point. */ + t1 = palDvdv( l->start, point ); + t2 = acos( t1 ); + t3 = palDvdv( l->dir, point ); + return ( ((l->length > 0) ? t2 < l->length : t2 == 0.0 ) && t3 >= -1.0E-8 ); +} + +static void LineOffset( AstFrame *this, AstLineDef *line, double par, + double prp, double point[2], int *status ){ +/* +* Name: +* LineOffset + +* Purpose: +* Find a position close to a line. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void LineOffset( AstFrame *this, AstLineDef *line, double par, +* double prp, double point[2], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astLineOffset +* method inherited from the Frame class). + +* Description: +* This function returns a position formed by moving a given distance along +* the supplied line, and then a given distance away from the supplied line. + +* Parameters: +* this +* Pointer to the Frame. +* line +* Pointer to the structure defining the line. +* par +* The distance to move along the line from the start towards the end. +* prp +* The distance to move at right angles to the line. Positive +* values result in movement to the left of the line, as seen from +* the observer, when moving from start towards the end. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The pointer supplied for "line" should have been created using the +* astLineDef method. This structure contains cached information about the +* line which improves the efficiency of this method when many repeated +* calls are made. An error will be reported if the structure does not +* refer to the Frame specified by "this". +*- +*/ + +/* Local Variables; */ + SkyLineDef *sl; + const int *perm; + double c; + double nx; + double ny; + double nz; + double p[2]; + double s; + double v[3]; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check that the line refers to the supplied Frame. */ + if( line->frame != this ) { + astError( AST__INTER, "astLineOffset(%s): The supplied line does " + "not relate to the supplied %s (AST internal programming " + "error).", status, astGetClass( this ), astGetClass( this ) ); + +/* This implementation uses spherical geometry. */ + } else { + +/* Get a pointer to the SkyLineDef structure. */ + sl = (SkyLineDef *) line; + +/* Move a distance par from start to end. */ + c = cos( par ); + s = sin( par ); + nx = c * sl->start[ 0 ] + s * sl->dir[ 0 ]; + ny = c * sl->start[ 1 ] + s * sl->dir[ 1 ]; + nz = c * sl->start[ 2 ] + s * sl->dir[ 2 ]; + +/* Move a distance prp from this point towards the pole point. */ + if( prp != 0.0 ) { + c = cos( prp ); + s = sin( prp ); + v[ 0 ] = c * nx + s * sl->q[ 0 ]; + v[ 1 ] = c * ny + s * sl->q[ 1 ]; + v[ 2 ] = c * nz + s * sl->q[ 2 ]; + } else { + v[ 0 ] = nx; + v[ 1 ] = ny; + v[ 2 ] = nz; + } + +/* Convert to lon/lat */ + palDcc2s( v, p, p + 1 ); + +/* Permute the spherical axis value into the order used by the SkyFrame. */ + perm = astGetPerm( this ); + if( perm ){ + point[ 0 ] = p[ perm[ 0 ] ]; + point[ 1 ] = p[ perm[ 1 ] ]; + } + } +} + +static int MakeSkyMapping( AstSkyFrame *target, AstSkyFrame *result, + AstSystemType align_sys, AstMapping **map, int *status ) { +/* +* Name: +* MakeSkyMapping + +* Purpose: +* Generate a Mapping between two SkyFrames. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int MakeSkyMapping( AstSkyFrame *target, AstSkyFrame *result, +* AstSystemType align_sys, AstMapping **map, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function takes two SkyFrames and generates a Mapping that +* converts between them, taking account of differences in their +* coordinate systems, equinox value, epoch, etc. (but not allowing +* for any axis permutations). + +* Parameters: +* target +* Pointer to the first SkyFrame. +* result +* Pointer to the second SkyFrame. +* align_sys +* The system in which to align the two SkyFrames. +* map +* Pointer to a location which is to receive a pointer to the +* returned Mapping. The forward transformation of this Mapping +* will convert from "target" coordinates to "result" +* coordinates, and the inverse transformation will convert in +* the opposite direction (all coordinate values in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Mapping could be generated, or zero if the two +* SkyFrames are sufficiently un-related that no meaningful Mapping +* can be produced. + +* Notes: +* A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Constants: */ +#define MAX_ARGS 4 /* Max arguments for an SlaMap conversion */ + +/* Local Variables: */ + AstMapping *omap; /* Mapping from coorinates to offsets */ + AstMapping *tmap2; /* Temporary Mapping */ + AstMapping *tmap; /* Temporary Mapping */ + AstSlaMap *slamap; /* Pointer to SlaMap */ + AstSystemType result_system; /* Code to identify result coordinate system */ + AstSystemType system; /* Code to identify coordinate system */ + AstSystemType target_system; /* Code to identify target coordinate system */ + double args[ MAX_ARGS ]; /* Conversion argument array */ + double epoch; /* Epoch as Modified Julian Date */ + double epoch_B; /* Besselian epoch as decimal years */ + double epoch_J; /* Julian epoch as decimal years */ + double equinox; /* Equinox as Modified Julian Date */ + double equinox_B; /* Besselian equinox as decimal years */ + double equinox_J; /* Julian equinox as decimal years */ + double diurab; /* Magnitude of diurnal aberration vector */ + double last; /* Local Apparent Sidereal Time */ + double lat; /* Observers latitude */ + double result_epoch; /* Result frame Epoch */ + double result_equinox; /* Result frame Epoch */ + double target_epoch; /* Target frame Epoch */ + double target_equinox; /* Target frame Epoch */ + int isunit; /* Is the SlaMap effectively a unit mapping? */ + int match; /* Mapping can be generated? */ + int step1; /* Convert target to FK5 J2000? */ + int step2; /* Convert FK5 J2000 to align sys? */ + int step3; /* Convert align sys to FK5 J2000? */ + int step4; /* Convert FK5 J2000 to result? */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise the returned values. */ + match = 1; + *map = NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + epoch_B = 0.0; + epoch_J = 0.0; + equinox_B = 0.0; + equinox_J = 0.0; + +/* Get the two epoch values. */ + result_epoch = astGetEpoch( result ); + target_epoch = astGetEpoch( target ); + +/* Get the two equinox values. */ + result_equinox = astGetEquinox( result ); + target_equinox = astGetEquinox( target ); + +/* Get the two system values. */ + result_system = astGetSystem( result ); + target_system = astGetSystem( target ); + +/* If either system is not references to the equinox given by the Equinox + attribute, then use the equinox of the other system rather than + adopting the arbitrary default of J2000. */ + if( !EQREF(result_system) ) result_equinox = target_equinox; + if( !EQREF(target_system) ) target_equinox = result_equinox; + +/* If both systems are unknown, assume they are the same. Return a UnitMap. + We need to do this, otherwise a simple change of Title (for instance) + will result in a FrameSet whose current Frame has System=AST__UNKNOWN + loosing its integrity. */ + if( target_system == AST__UNKNOWN && result_system == AST__UNKNOWN ) { + *map = (AstMapping *) astUnitMap( 2, "", status ); + return 1; + } + +/* The total Mapping is divided into two parts in series; the first part + converts from the target SkyFrame to the alignment system, using the + Epoch and Equinox of the target Frame, the second part converts from + the alignment system to the result SkyFrame, using the Epoch and Equinox + of the result Frame. Each of these parts has an arbitrary input and an + output system, and therefore could be implemented using a collection + of NxN conversions. To reduce the complexity, each part is implement + by converting from the input system to FK5 J2000, and then from FK5 + J2000 to the output system. This scheme required only N conversions + rather than NxN. Thus overall the total Mapping is made up of 4 steps + in series. Some of these steps may be ommitted if they are effectively + a UnitMap. Determine which steps need to be included. Assume all need + to be done to begin with. */ + step1 = 1; + step2 = 1; + step3 = 1; + step4 = 1; + +/* If the target system is the same as the alignment system, neither of the + first 2 steps need be done. */ + if( target_system == align_sys ) { + step1 = 0; + step2 = 0; + } + +/* If the result system is the same as the alignment system, neither of the + last 2 steps need be done. */ + if( result_system == align_sys ) { + step3 = 0; + step4 = 0; + } + +/* If the two epochs are the same, or if the alignment system is FK5 J2000, + steps 2 and 3 are not needed. */ + if( step2 && step3 ) { + if( align_sys == AST__FK5 || result_epoch == target_epoch ) { + step2 = 0; + step3 = 0; + } + } + +/* None are needed if the target and result SkyFrames have the same + System, Epoch and Equinox. */ + if( result_system == target_system && + result_epoch == target_epoch && + result_equinox == target_equinox ) { + step1 = 0; + step2 = 0; + step3 = 0; + step4 = 0; + } + +/* Create an initial (null) SlaMap. */ + slamap = astSlaMap( 0, "", status ); + +/* Define local macros as shorthand for adding sky coordinate + conversions to this SlaMap. Each macro simply stores details of + the additional arguments in the "args" array and then calls + astSlaAdd. The macros differ in the number of additional argument + values. */ + #define TRANSFORM_0(cvt) \ + astSlaAdd( slamap, cvt, NULL ); + + #define TRANSFORM_1(cvt,arg0) \ + args[ 0 ] = arg0; \ + astSlaAdd( slamap, cvt, args ); + + #define TRANSFORM_2(cvt,arg0,arg1) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + astSlaAdd( slamap, cvt, args ); + + #define TRANSFORM_3(cvt,arg0,arg1,arg2) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + astSlaAdd( slamap, cvt, args ); + + #define TRANSFORM_4(cvt,arg0,arg1,arg2,arg3) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + args[ 3 ] = arg3; \ + astSlaAdd( slamap, cvt, args ); + +/* Convert _to_ FK5 J2000.0 coordinates. */ +/* ===================================== */ +/* The overall conversion is formulated in four phases. In this first + phase, we convert from the target coordinate system to intermediate sky + coordinates expressed using the FK5 system, mean equinox J2000.0. */ + +/* Obtain the sky coordinate system, equinox, epoch, etc, of the target + SkyFrame. */ + system = target_system; + equinox = target_equinox; + epoch = target_epoch; + last = GetLAST( target, status ); + diurab = GetDiurab( target, status ); + lat = astGetObsLat( target ); + if( astOK && step1 ) { + +/* Convert the equinox and epoch values (stored as Modified Julian + Dates) into the equivalent Besselian and Julian epochs (as decimal + years). */ + equinox_B = palEpb( equinox ); + equinox_J = palEpj( equinox ); + epoch_B = palEpb( epoch ); + epoch_J = palEpj( epoch ); + +/* Formulate the conversion... */ + +/* From FK4. */ +/* --------- */ +/* If necessary, apply the old-style FK4 precession model to bring the + equinox to B1950.0, with rigorous handling of the E-terms of + aberration. Then convert directly to FK5 J2000.0 coordinates. */ + if ( system == AST__FK4 ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", equinox_B ) + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + TRANSFORM_1( "ADDET", 1950.0 ) + } + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK4 with no E-terms. */ +/* ------------------------- */ +/* This is the same as above, except that we do not need to subtract + the E-terms initially as they are already absent. */ + } else if ( system == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + } + TRANSFORM_1( "ADDET", 1950.0 ) + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK5. */ +/* --------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the equinox is already J2000.0. */ + } else if ( system == AST__FK5 ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", equinox_J, 2000.0 ); + } + +/* From J2000. */ +/* ----------- */ +/* Convert from J2000 to ICRS, then from ICRS to FK5. */ + } else if ( system == AST__J2000 ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_0( "J2000H" ) + TRANSFORM_1( "HFK5Z", epoch_J ); + +/* From geocentric apparent. */ +/* ------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From ecliptic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + TRANSFORM_1( "ECLEQ", equinox ) + +/* From helio-ecliptic coordinates. */ +/* -------------------------------- */ + } else if ( system == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "HEEQ", epoch ) + +/* From galactic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GALACTIC ) { + TRANSFORM_0( "GALEQ" ) + +/* From ICRS. */ +/* ---------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ICRS ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "HFK5Z", epoch_J ); + +/* From supergalactic coordinates. */ +/* ------------------------------- */ +/* Convert to galactic coordinates and then to FK5 J2000.0 + equatorial. */ + } else if ( system == AST__SUPERGALACTIC ) { + TRANSFORM_0( "SUPGAL" ) + TRANSFORM_0( "GALEQ" ) + +/* From AzEl. */ +/* ---------- */ +/* Rotate from horizon to equator (H2E), shift hour angle into RA (H2R), + go from geocentric apparent to FK5 J2000. */ + } else if ( system == AST__AZEL ) { + VerifyMSMAttrs( target, result, 1, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "H2E", lat, diurab ) + TRANSFORM_1( "H2R", last ) + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From unknown coordinates. */ +/* ------------------------- */ +/* No conversion is possible. */ + } else if ( system == AST__UNKNOWN ) { + match = 0; + } + } + +/* Convert _from_ FK5 J2000.0 coordinates _to_ the alignment system. */ +/* ============================================================ */ +/* In this second phase, we convert to the system given by the align_sys + argument (if required), still using the properties of the target Frame. */ + if ( astOK && match && step2 ) { + +/* Align in FK4. */ +/* --------------- */ +/* Convert directly from FK5 J2000.0 to FK4 B1950.0 coordinates at the + appropriate epoch. Then, if necessary, apply the old-style FK4 + precession model to bring the equinox to that required, with + rigorous handling of the E-terms of aberration. */ + if ( align_sys == AST__FK4 ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", 1950.0 ) + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + TRANSFORM_1( "ADDET", equinox_B ) + } + +/* Align in FK4 with no E-terms. */ +/* ------------------------------- */ +/* This is the same as above, except that we do not need to add the + E-terms at the end. */ + } else if ( align_sys == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 1, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + TRANSFORM_1( "SUBET", 1950.0 ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + } + +/* Align in FK5. */ +/* ------------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the required equinox is J2000.0. */ + } else if ( align_sys == AST__FK5 ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", 2000.0, equinox_J ) + } + +/* Align in J2000. */ +/* --------------- */ +/* Mov from FK5 to ICRS, and from ICRS to J2000. */ + } else if ( align_sys == AST__J2000 ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + TRANSFORM_0( "HJ2000" ) + +/* Align in geocentric apparent. */ +/* ------------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + +/* Align in ecliptic coordinates. */ +/* -------------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Equinox", "astMatch", status ); + TRANSFORM_1( "EQECL", equinox ) + +/* Align in helio-ecliptic coordinates. */ +/* ------------------------------------ */ + } else if ( align_sys == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "EQHE", epoch ) + +/* Align in galactic coordinates. */ +/* -------------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GALACTIC ) { + TRANSFORM_0( "EQGAL" ) + +/* Align in ICRS. */ +/* -------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ICRS ) { + VerifyMSMAttrs( target, result, 1, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + +/* Align in supergalactic coordinates. */ +/* ------------------------------------- */ +/* Convert to galactic coordinates and then to supergalactic. */ + } else if ( align_sys == AST__SUPERGALACTIC ) { + TRANSFORM_0( "EQGAL" ) + TRANSFORM_0( "GALSUP" ) + +/* Align in AzEl coordinates. */ +/* -------------------------- */ +/* Go from FK5 J2000 to geocentric apparent (MAP), shift RA into hour angle + (R2H), rotate from equator to horizon (E2H). */ + } else if ( align_sys == AST__AZEL ) { + VerifyMSMAttrs( target, result, 1, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + TRANSFORM_1( "R2H", last ) + TRANSFORM_2( "E2H", lat, diurab ) + +/* Align in unknown coordinates. */ +/* ------------------------------- */ +/* No conversion is possible. */ + } else if ( align_sys == AST__UNKNOWN ) { + match = 0; + } + } + +/* Convert _from_ the alignment system _to_ FK5 J2000.0 coordinates */ +/* =========================================================== */ +/* In this third phase, we convert from the alignment system (if required) + to the intermediate FK5 J2000 system, using the properties of the + result SkyFrame. */ + +/* Obtain the sky coordinate system, equinox, epoch, etc, of the result + SkyFrame. */ + system = result_system; + equinox = result_equinox; + epoch = result_epoch; + diurab = GetDiurab( result, status ); + last = GetLAST( result, status ); + lat = astGetObsLat( result ); + +/* Convert the equinox and epoch values (stored as Modified Julian + Dates) into the equivalent Besselian and Julian epochs (as decimal + years). */ + if( astOK ) { + equinox_B = palEpb( equinox ); + equinox_J = palEpj( equinox ); + epoch_B = palEpb( epoch ); + epoch_J = palEpj( epoch ); + } + +/* Check we need to do the conversion. */ + if ( astOK && match && step3 ) { + +/* Formulate the conversion... */ + +/* From FK4. */ +/* --------- */ +/* If necessary, apply the old-style FK4 precession model to bring the + equinox to B1950.0, with rigorous handling of the E-terms of + aberration. Then convert directly to FK5 J2000.0 coordinates. */ + if ( align_sys == AST__FK4 ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", equinox_B ) + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + TRANSFORM_1( "ADDET", 1950.0 ) + } + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK4 with no E-terms. */ +/* ------------------------- */ +/* This is the same as above, except that we do not need to subtract + the E-terms initially as they are already absent. */ + } else if ( align_sys == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", equinox_B, 1950.0 ) + } + TRANSFORM_1( "ADDET", 1950.0 ) + TRANSFORM_1( "FK45Z", epoch_B ) + +/* From FK5. */ +/* --------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the equinox is already J2000.0. */ + } else if ( align_sys == AST__FK5 ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", equinox_J, 2000.0 ); + } + +/* From geocentric apparent. */ +/* ------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From ecliptic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + TRANSFORM_1( "ECLEQ", equinox ) + +/* From helio-ecliptic coordinates. */ +/* -------------------------------- */ + } else if ( align_sys == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "HEEQ", epoch ) + +/* From galactic coordinates. */ +/* -------------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__GALACTIC ) { + TRANSFORM_0( "GALEQ" ) + +/* From ICRS. */ +/* ---------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( align_sys == AST__ICRS ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "HFK5Z", epoch_J ) + +/* From J2000. */ +/* ----------- */ +/* From J2000 to ICRS, and from ICRS to FK5. */ + } else if ( align_sys == AST__J2000 ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_0( "J2000H" ) + TRANSFORM_1( "HFK5Z", epoch_J ) + +/* From supergalactic coordinates. */ +/* ------------------------------- */ +/* Convert to galactic coordinates and then to FK5 J2000.0 + equatorial. */ + } else if ( align_sys == AST__SUPERGALACTIC ) { + TRANSFORM_0( "SUPGAL" ) + TRANSFORM_0( "GALEQ" ) + +/* From AzEl. */ +/* ---------- */ +/* Rotate from horizon to equator (H2E), shift hour angle into RA (H2R), + go from geocentric apparent to FK5 J2000. */ + } else if ( align_sys == AST__AZEL ) { + VerifyMSMAttrs( target, result, 3, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "H2E", lat, diurab ) + TRANSFORM_1( "H2R", last ) + TRANSFORM_2( "AMP", epoch, 2000.0 ) + +/* From unknown coordinates. */ +/* ------------------------------- */ +/* No conversion is possible. */ + } else if ( align_sys == AST__UNKNOWN ) { + match = 0; + } + } + +/* Convert _from_ FK5 J2000.0 coordinates. */ +/* ======================================= */ +/* In this fourth and final phase, we convert to the result coordinate + system from the intermediate FK5 J2000 sky coordinates generated above. */ + if ( astOK && match && step4 ) { + +/* To FK4. */ +/* ------- */ +/* Convert directly from FK5 J2000.0 to FK4 B1950.0 coordinates at the + appropriate epoch. Then, if necessary, apply the old-style FK4 + precession model to bring the equinox to that required, with + rigorous handling of the E-terms of aberration. */ + if ( system == AST__FK4 ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_1( "SUBET", 1950.0 ) + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + TRANSFORM_1( "ADDET", equinox_B ) + } + +/* To FK4 with no E-terms. */ +/* ----------------------- */ +/* This is the same as above, except that we do not need to add the + E-terms at the end. */ + } else if ( system == AST__FK4_NO_E ) { + VerifyMSMAttrs( target, result, 3, "Equinox Epoch", "astMatch", status ); + TRANSFORM_1( "FK54Z", epoch_B ) + TRANSFORM_1( "SUBET", 1950.0 ) + if ( equinox_B != 1950.0 ) { + TRANSFORM_2( "PREBN", 1950.0, equinox_B ) + } + +/* To FK5. */ +/* ------- */ +/* We simply need to apply a precession correction for the change of + equinox. Omit even this if the required equinox is J2000.0. */ + } else if ( system == AST__FK5 ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + if ( equinox_J != 2000.0 ) { + TRANSFORM_2( "PREC", 2000.0, equinox_J ) + } + +/* To geocentric apparent. */ +/* ----------------------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GAPPT ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + +/* To ecliptic coordinates. */ +/* ------------------------ */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Equinox", "astMatch", status ); + TRANSFORM_1( "EQECL", equinox ) + +/* To helio-ecliptic coordinates. */ +/* ------------------------------ */ + } else if ( system == AST__HELIOECLIPTIC ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "EQHE", epoch ) + +/* To galactic coordinates. */ +/* ------------------------ */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__GALACTIC ) { + TRANSFORM_0( "EQGAL" ) + +/* To ICRS. */ +/* -------- */ +/* This conversion is supported directly by SLALIB. */ + } else if ( system == AST__ICRS ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + +/* To J2000. */ +/* --------- */ +/* From FK5 to ICRS, then from ICRS to J2000. */ + } else if ( system == AST__J2000 ) { + VerifyMSMAttrs( target, result, 3, "Epoch", "astMatch", status ); + TRANSFORM_1( "FK5HZ", epoch_J ) + TRANSFORM_0( "HJ2000" ) + +/* To supergalactic coordinates. */ +/* ----------------------------- */ +/* Convert to galactic coordinates and then to supergalactic. */ + } else if ( system == AST__SUPERGALACTIC ) { + TRANSFORM_0( "EQGAL" ) + TRANSFORM_0( "GALSUP" ) + +/* To AzEl */ +/* ------- */ +/* Go from FK5 J2000 to geocentric apparent (MAP), shift RA into hour angle + (R2H), rotate from equator to horizon (E2H). */ + } else if ( system == AST__AZEL ) { + VerifyMSMAttrs( target, result, 3, "ObsLon ObsLat Epoch", "astMatch", status ); + TRANSFORM_2( "MAP", 2000.0, epoch ) + TRANSFORM_1( "R2H", last ) + TRANSFORM_2( "E2H", lat, diurab ) + +/* To unknown coordinates. */ +/* ----------------------------- */ +/* No conversion is possible. */ + } else if ( system == AST__UNKNOWN ) { + match = 0; + } + } + +/* See of the slamap created above is effectively a unit mapping to + within the tolerance of the more accurate SkyFrame (target or result). */ + isunit = TestSlaUnit( target, result, slamap, status ); + +/* Now need to take account of the possibility that the input or output + SkyFrame may represent an offset system rather than a coordinate system. + Form the Mapping from the target coordinate system to the associated + offset system. A UnitMap is returned if the target does not use an + offset system. */ + omap = SkyOffsetMap( target, status ); + +/* Invert it to get the Mapping from the actual used system (whther + offsets or coordinates) to the coordinate system. */ + astInvert( omap ); + +/* Combine it with the slamap created earlier, so that its coordinate + outputs feed the inputs of the slamap. We only do this if the slamap + is not effectively a unit mapping. Annul redundant pointers afterwards. */ + if( ! isunit ) { + tmap = (AstMapping *) astCmpMap( omap, slamap, 1, "", status ); + } else { + tmap = astClone( omap ); + } + omap = astAnnul( omap ); + slamap =astAnnul( slamap ); + +/* Now form the Mapping from the result coordinate system to the associated + offset system. A UnitMap is returned if the result does not use an + offset system. */ + omap = SkyOffsetMap( result, status ); + +/* Combine it with the above CmpMap, so that the CmpMap outputs feed the + new Mapping inputs. Annul redundant pointers afterwards. */ + tmap2 = (AstMapping *) astCmpMap( tmap, omap, 1, "", status ); + omap =astAnnul( omap ); + tmap =astAnnul( tmap ); + +/* Simplify the Mapping produced above (this eliminates any redundant + conversions) and annul the original pointer. */ + *map = astSimplify( tmap2 ); + tmap2 = astAnnul( tmap2 ); + +/* If an error occurred, annul the returned Mapping and clear the + returned values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + match = -1; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef MAX_ARGS +#undef TRANSFORM_0 +#undef TRANSFORM_1 +#undef TRANSFORM_2 +#undef TRANSFORM_3 +} + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astMatch method +* inherited from the Frame class). + +* Description: +* This function matches a "template" SkyFrame to a "target" Frame and +* determines whether it is possible to convert coordinates between them. +* If it is, a mapping that performs the transformation is returned along +* with a new Frame that describes the coordinate system that results when +* this mapping is applied to the "target" coordinate system. In addition, +* information is returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" and "template" +* Frames from which they are derived. + +* Parameters: +* template +* Pointer to the template SkyFrame. This describes the coordinate system +* (or set of possible coordinate systems) into which we wish to convert +* our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate system in +* which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the template SkyFrame axis from which +* it is derived. If it is not derived from any template SkyFrame axis, +* a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the target Frame axis from which it +* is derived. If it is not derived from any target Frame axis, a value +* of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will be +* returned if the requested coordinate conversion is possible. If +* returned, the forward transformation of this Mapping may be used to +* convert coordinates between the "target" Frame and the "result" +* Frame (see below) and the inverse transformation will convert in the +* opposite direction. +* result +* Address of a location where a pointer to a new Frame will be returned +* if the requested coordinate conversion is possible. If returned, this +* Frame describes the coordinate system that results from applying the +* returned Mapping (above) to the "target" coordinate system. In +* general, this Frame will combine attributes from (and will therefore +* be more specific than) both the target and the template Frames. In +* particular, when the template allows the possibility of transformaing +* to any one of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate conversion is +* possible. Otherwise zero is returned (this will not in itself result in +* an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* This implementation addresses the matching of a SkyFrame class object to +* any other class of Frame. A SkyFrame will match any class of SkyFrame +* (i.e. possibly from a derived class) but will not match a less +* specialised class of Frame. +*/ + +/* Local Variables: */ + AstFrame *frame0; /* Pointer to Frame underlying axis 0 */ + AstFrame *frame1; /* Pointer to Frame underlying axis 1 */ + AstSkyFrame *template; /* Pointer to template SkyFrame structure */ + int iaxis; /* Axis index */ + int iaxis0; /* Axis index underlying axis 0 */ + int iaxis1; /* Axis index underlying axis 1 */ + int match; /* Coordinate conversion possible? */ + int swap1; /* Template axes swapped? */ + int swap2; /* Target axes swapped? */ + int swap; /* Additional axis swap needed? */ + int target_axis0; /* Index of 1st SkyFrame axis in the target */ + int target_axis1; /* Index of 2nd SkyFrame axis in the target */ + int target_naxes; /* Number of target axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + swap = 0; + target_axis0 = -1; + target_axis1 = -1; + +/* Obtain a pointer to the template SkyFrame structure. */ + template = (AstSkyFrame *) template_frame; + +/* Obtain the number of axes in the target Frame. */ + target_naxes = astGetNaxes( target ); + +/* The first criterion for a match is that the template matches as a + Frame class object. This ensures that the number of axes (2) and + domain, etc. of the target Frame are suitable. Invoke the parent + "astMatch" method to verify this. */ + match = (*parent_match)( template_frame, target, matchsub, + template_axes, target_axes, map, result, status ); + +/* If a match was found, annul the returned objects, which are not + needed, but keep the memory allocated for the axis association + arrays, which we will re-use. */ + if ( astOK && match ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + } + +/* If OK so far, obtain pointers to the primary Frames which underlie + all target axes. Stop when a SkyFrame axis is found. */ + if ( match && astOK ) { + + match = 0; + for( iaxis = 0; iaxis < target_naxes; iaxis++ ) { + astPrimaryFrame( target, iaxis, &frame0, &iaxis0 ); + if( astIsASkyFrame( frame0 ) ) { + target_axis0 = iaxis; + match = 1; + break; + } else { + frame0 = astAnnul( frame0 ); + } + } + +/* Check at least one SkyFrame axis was found it the target. */ + if( match ) { + +/* If so, search the remaining target axes for another axis that is + derived from the same SkyFrame. */ + match = 0; + for( iaxis++ ; iaxis < target_naxes; iaxis++ ) { + astPrimaryFrame( target, iaxis, &frame1, &iaxis1 ); + if( frame1 == frame0 ) { + target_axis1 = iaxis; + frame1 = astAnnul( frame1 ); + match = 1; + break; + } else { + frame1 = astAnnul( frame1 ); + } + } + +/* Annul the remaining Frame pointer used in the above tests. */ + frame0 = astAnnul( frame0 ); + } + +/* If this test is passed, we can now test that the underlying axis indices + are 0 and 1, in either order. This then ensures that we have a + single SkyFrame (not a compound Frame) with both axes present. */ + if ( match && astOK ) { + match = ( ( ( iaxis0 == 0 ) && ( iaxis1 == 1 ) ) || + ( ( iaxis1 == 0 ) && ( iaxis0 == 1 ) ) ); + } + + } + +/* If a possible match has been detected, we must now decide how the + order of the axes in the result Frame relates to the order of axes + in the target Frame. There are two factors involved. The first + depends on whether the axis permutation array for the template + SkyFrame (whose method we are executing) causes an axis + reversal. Determine this by permuting axis index zero. */ + if ( astOK && match ) { + swap1 = ( astValidateAxis( template, 0, 1, "astMatch" ) != 0 ); + +/* The second factor depends on whether the axes of the underlying + primary SkyFrame are reversed when seen in the target Frame. */ + swap2 = ( iaxis0 != 0 ); + +/* Combine these to determine if an additional axis swap will be + needed. */ + swap = ( swap1 != swap2 ); + +/* Now check to see if this additional swap is permitted by the + template's Permute attribute. */ + match = ( !swap || astGetPermute( template ) ); + } + +/* If the Frames still match, we next set up the axis association + arrays. */ + if ( astOK && match ) { + +/* If the target axis order is to be preserved, then the target axis + association involves no permutation but the template axis + association may involve an axis swap. */ + if ( astGetPreserveAxes( template ) ) { + (*template_axes)[ 0 ] = swap; + (*template_axes)[ 1 ] = !swap; + (*target_axes)[ 0 ] = target_axis0; + (*target_axes)[ 1 ] = target_axis1; + +/* Otherwise, any swap applies to the target axis association + instead. */ + } else { + (*template_axes)[ 0 ] = 0; + (*template_axes)[ 1 ] = 1; + (*target_axes)[ 0 ] = swap ? target_axis1 : target_axis0; + (*target_axes)[ 1 ] = swap ? target_axis0 : target_axis1; + } + +/* Use the target's "astSubFrame" method to create a new Frame (the + result Frame) with copies of the target axes in the required + order. This process also overlays the template attributes on to the + target Frame and returns a Mapping between the target and result + Frames which effects the required coordinate conversion. */ + match = astSubFrame( target, template, 2, *target_axes, *template_axes, + map, result ); + } + +/* If an error occurred, or conversion to the result Frame's + coordinate system was not possible, then free all memory, annul the + returned objects, and reset the returned value. */ + if ( !astOK || !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void MatchAxesX( AstFrame *frm2_frame, AstFrame *frm1, int *axes, + int *status ) { +/* +* Name: +* MatchAxesX + +* Purpose: +* Find any corresponding axes in two Frames. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void MatchAxesX( AstFrame *frm2, AstFrame *frm1, int *axes ) +* int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astMatchAxesX +* method inherited from the Frame class). + +* This function looks for corresponding axes within two supplied +* Frames. An array of integers is returned that contains an element +* for each axis in the second supplied Frame. An element in this array +* will be set to zero if the associated axis within the second Frame +* has no corresponding axis within the first Frame. Otherwise, it +* will be set to the index (a non-zero positive integer) of the +* corresponding axis within the first supplied Frame. + +* Parameters: +* frm2 +* Pointer to the second Frame. +* frm1 +* Pointer to the first Frame. +* axes +* Pointer to an integer array in which to return the indices of +* the axes (within the first Frame) that correspond to each axis +* within the second Frame. Axis indices start at 1. A value of zero +* will be stored in the returned array for each axis in the second +* Frame that has no corresponding axis in the first Frame. +* +* The number of elements in this array must be greater than or +* equal to the number of axes in the second Frame. +* status +* Pointer to inherited status value. + +* Notes: +* - Corresponding axes are identified by the fact that a Mapping +* can be found between them using astFindFrame or astConvert. Thus, +* "corresponding axes" are not necessarily identical. For instance, +* SkyFrame axes in two Frames will match even if they describe +* different celestial coordinate systems +*/ + +/* Local Variables: */ + AstFrame *resfrm; + AstMapping *resmap; + AstSkyFrame *frm2; + int *frm2_axes; + int *frm1_axes; + int max_axes; + int min_axes; + int preserve_axes; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the SkyFrame. */ + frm2 = (AstSkyFrame *) frm2_frame; + +/* Temporarily ensure that the PreserveAxes attribute is non-zero in + the first supplied Frame. This means thte result Frame returned by + astMatch below will have the axis count and order of the target Frame + (i.e. "pfrm"). */ + if( astTestPreserveAxes( frm1 ) ) { + preserve_axes = astGetPreserveAxes( frm1 ) ? 1 : 0; + } else { + preserve_axes = -1; + } + astSetPreserveAxes( frm1, 1 ); + +/* Temporarily ensure that the MaxAxes and MinAxes attributes in the + first supplied Frame are set so the Frame can be used as a template + in astMatch for matching any number of axes. */ + if( astTestMaxAxes( frm1 ) ) { + max_axes = astGetMaxAxes( frm1 ); + } else { + max_axes = -1; + } + astSetMaxAxes( frm1, 10000 ); + + if( astTestMinAxes( frm1 ) ) { + min_axes = astGetMinAxes( frm1 ); + } else { + min_axes = -1; + } + astSetMinAxes( frm1, 1 ); + +/* Attempt to find a sub-frame within the first supplied Frame that + corresponds to the supplied SkyFrame. */ + if( astMatch( frm1, frm2, 1, &frm1_axes, &frm2_axes, &resmap, &resfrm ) ) { + +/* If successfull, Store the one-based index within "frm1" of the + corresponding axes. */ + axes[ 0 ] = frm1_axes[ 0 ] + 1; + axes[ 1 ] = frm1_axes[ 1 ] + 1; + +/* Free resources */ + frm1_axes = astFree( frm1_axes ); + frm2_axes = astFree( frm2_axes ); + resmap = astAnnul( resmap ); + resfrm = astAnnul( resfrm ); + +/* If no corresponding SkyFrame was found store zeros in the returned array. */ + } else { + axes[ 0 ] = 0; + axes[ 1 ] = 0; + } + +/* Re-instate the original attribute values in the first supplied Frame. */ + if( preserve_axes == -1 ) { + astClearPreserveAxes( frm1 ); + } else { + astSetPreserveAxes( frm1, preserve_axes ); + } + + if( max_axes == -1 ) { + astClearMaxAxes( frm1 ); + } else { + astSetMaxAxes( frm1, max_axes ); + } + + if( min_axes == -1 ) { + astClearMinAxes( frm1 ); + } else { + astSetMinAxes( frm1, min_axes ); + } +} + +static void Norm( AstFrame *this_frame, double value[], int *status ) { +/* +* Name: +* Norm + +* Purpose: +* Normalise a set of SkyFrame coordinates. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Norm( AstAxis *this, double value[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astNorm method inherited +* from the Frame class). + +* Description: +* This function converts a set of SkyFrame coordinate values, +* which might potentially be unsuitable for display to a user (for +* instance, may lie outside the expected range of values) into a +* set of acceptable alternative values suitable for display. +* +* This is done by wrapping coordinates so that the latitude lies +* in the range (-pi/2.0) <= latitude <= (pi/2.0). If the NegLon +* attribute is zero (the default), then the wrapped longitude value +* lies in the range 0.0 <= longitude < (2.0*pi). Otherwise, it lies +* in the range -pi <= longitude < pi. + +* Parameters: +* this +* Pointer to the SkyFrame. +* value +* An array of double, with one element for each SkyFrame axis. +* This should contain the initial set of coordinate values, +* which will be modified in place. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double sky_lat; /* Sky latitude value */ + double sky_long; /* Sky longitude value */ + double v[ 2 ]; /* Permuted value coordinates */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Obtain the sky longitude and latitude values, allowing for any axis + permutation. */ + v[ perm[ 0 ] ] = value[ 0 ]; + v[ perm[ 1 ] ] = value[ 1 ]; + sky_long = v[ 0 ]; + sky_lat = v[ 1 ]; + +/* Test if both values are OK (i.e. not "bad"). */ + if ( ( sky_long != AST__BAD ) && ( sky_lat != AST__BAD ) ) { + +/* Fold the longitude value into the range 0 to 2*pi and the latitude into + the range -pi to +pi. */ + sky_long = palDranrm( sky_long ); + sky_lat = palDrange( sky_lat ); + +/* If the latitude now exceeds pi/2, shift the longitude by pi in whichever + direction will keep it in the range 0 to 2*pi. */ + if ( sky_lat > ( pi / 2.0 ) ) { + sky_long += ( sky_long < pi ) ? pi : -pi; + +/* Reflect the latitude value through the pole, so it lies in the range 0 to + pi/2. */ + sky_lat = pi - sky_lat; + +/* If the latitude is less than -pi/2, shift the longitude in the same way + as above. */ + } else if ( sky_lat < -( pi / 2.0 ) ) { + sky_long += ( sky_long < pi ) ? pi : -pi; + +/* But reflect the latitude through the other pole, so it lies in the range + -pi/2 to 0. */ + sky_lat = -pi - sky_lat; + } + +/* If only the longitude value is valid, wrap it into the range 0 to 2*pi. */ + } else if ( sky_long != AST__BAD ) { + sky_long = palDranrm( sky_long ); + +/* If only the latitude value is valid, wrap it into the range -pi to +pi. */ + } else if ( sky_lat != AST__BAD ) { + sky_lat = palDrange( sky_lat ); + +/* Then refect through one of the poles (as above), if necessary, to move it + into the range -pi/2 to +pi/2. */ + if ( sky_lat > ( pi / 2.0 ) ) { + sky_lat = pi - sky_lat; + } else if ( sky_lat < -( pi / 2.0 ) ) { + sky_lat = -pi - sky_lat; + } + } + +/* Convert 2*pi longitude into zero. Allow for a small error. */ + if ( fabs( sky_long - ( 2.0 * pi ) ) <= + ( 2.0 * pi ) * ( DBL_EPSILON * (double) FLT_RADIX ) ) sky_long = 0.0; + +/* If the NegLon attribute is set, and the longitude value is good, + convert it into the range -pi to +pi. */ + if( sky_long != AST__BAD && astGetNegLon( this ) ) { + sky_long = palDrange( sky_long ); + } + +/* Return the new values, allowing for any axis permutation. */ + v[ 0 ] = sky_long; + v[ 1 ] = sky_lat; + value[ 0 ] = v[ perm[ 0 ] ]; + value[ 1 ] = v[ perm[ 1 ] ]; + } +} + +static void NormBox( AstFrame *this_frame, double lbnd[], double ubnd[], + AstMapping *reg, int *status ) { +/* +* Name: +* NormBox + +* Purpose: +* Extend a box to include effect of any singularities in the Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void astNormBox( AstFrame *this, double lbnd[], double ubnd[], +* AstMapping *reg, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astNormBox method inherited +* from the Frame class). + +* Description: +* This function modifies a supplied box to include the effect of any +* singularities in the co-ordinate system represented by the Frame. +* For a normal Cartesian coordinate system, the box will be returned +* unchanged. Other classes of Frame may do other things. For instance, +* a SkyFrame will check to see if the box contains either the north +* or south pole and extend the box appropriately. + +* Parameters: +* this +* Pointer to the Frame. +* lbnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* lower axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* ubnd +* An array of double, with one element for each Frame axis +* (Naxes attribute). Initially, this should contain a set of +* upper axis bounds for the box. They will be modified on exit +* to include the effect of any singularities within the box. +* reg +* A Mapping which should be used to test if any singular points are +* inside or outside the box. The Mapping should leave an input +* position unchanged if the point is inside the box, and should +* set all bad if the point is outside the box. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Axis permutation array */ + double lb[ 2 ]; /* Permuted lower bounds */ + double t; /* Temporary storage */ + double t2; /* Temporary storage */ + double ub[ 2 ]; /* Permuted upper bounds */ + double x[2]; /* 1st axis values at poles */ + double xo[2]; /* Tested 1st axis values at poles */ + double y[2]; /* 2nd axis values at poles */ + double yo[2]; /* Tested 2nd axis values at poles */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if( perm ) { + +/* Obtain the sky longitude and latitude limits, allowing for any axis + permutation. */ + lb[ perm[ 0 ] ] = lbnd[ 0 ]; + lb[ perm[ 1 ] ] = lbnd[ 1 ]; + ub[ perm[ 0 ] ] = ubnd[ 0 ]; + ub[ perm[ 1 ] ] = ubnd[ 1 ]; + +/* Use the supplied Mapping to test if box includes either pole. */ + if( perm[ 0 ] == 0 ) { + x[ 0 ] = 0.0; + y[ 0 ] = AST__DPIBY2; + x[ 1 ] = 0.0; + y[ 1 ] = -AST__DPIBY2; + } else { + x[ 0 ] = AST__DPIBY2; + y[ 0 ] = 0.0; + x[ 1 ] = -AST__DPIBY2; + y[ 1 ] = 0.0; + } + astTran2( reg, 2, x, y, 1, xo, yo ); + +/* If the box includes the north pole... */ + if( xo[ 0 ] != AST__BAD ) { + +/* Find the lowest latitude after normalisation. */ + if( ub[ 1 ] != AST__BAD && lb[ 1 ] != AST__BAD ){ + t = palDrange( ub[ 1 ] ); + t2 = palDrange( lb[ 1 ] ); + if( t2 < t ) t = t2; + } else { + t = AST__BAD; + } + +/* Set the lower returned limit to this value and the upper returned limit + to +90 degs */ + lb[ 1 ] = t; + ub[ 1 ] = AST__DPIBY2; + +/* Set the longitude range to 0 to 2PI */ + lb[ 0 ] = 0; + ub[ 0 ] = 2*AST__DPI; + + } + +/* If the box includes the south pole... */ + if( xo[ 1 ] != AST__BAD ) { + +/* Find the highest latitude after normalisation. */ + if( ub[ 1 ] != AST__BAD && lb[ 1 ] != AST__BAD ){ + t = palDrange( ub[ 1 ] ); + t2 = palDrange( lb[ 1 ] ); + if( t2 > t ) t = t2; + } else { + t = AST__BAD; + } + +/* Set the upper returned limit to this value and the lower returned limit + to -90 degs */ + lb[ 1 ] = -AST__DPIBY2; + ub[ 1 ] = t; + +/* Set the longitude range to 0 to 2PI */ + lb[ 0 ] = 0; + ub[ 0 ] = 2*AST__DPI; + } + +/* Return the modified limits. */ + lbnd[ 0 ] = lb[ perm[ 0 ] ]; + lbnd[ 1 ] = lb[ perm[ 1 ] ]; + ubnd[ 0 ] = ub[ perm[ 0 ] ]; + ubnd[ 1 ] = ub[ perm[ 1 ] ]; + } +} + +static void Offset( AstFrame *this_frame, const double point1[], + const double point2[], double offset, double point3[], int *status ) { +/* +* Name: +* Offset + +* Purpose: +* Calculate an offset along a geodesic curve. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Offset( AstFrame *this, +* const double point1[], const double point2[], +* double offset, double point3[], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astOffset method +* inherited from the Frame class). + +* Description: +* This function finds the SkyFrame coordinate values of a point +* which is offset a specified distance along the geodesic curve +* (i.e. great circle) between two other points. + +* Parameters: +* this +* Pointer to the SkyFrame. +* point1 +* An array of double, with one element for each SkyFrame axis. +* This should contain the coordinates of the point marking the +* start of the geodesic curve. +* point2 +* An array of double, with one element for each SkyFrame axis. +* This should contain the coordinates of the point marking the +* end of the geodesic curve. +* offset +* The required offset from the first point along the geodesic +* curve, in radians. If this is positive, it will be towards +* the second point. If it is negative, it will be in the +* opposite direction. This offset need not imply a position +* lying between the two points given, as the curve will be +* extrapolated if necessary. +* point3 +* An array of double, with one element for each SkyFrame axis +* in which the coordinates of the required point will be +* returned. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +* - "Bad" coordinate values will also be returned if the two +* points supplied are coincident (or otherwise fail to uniquely +* specify a geodesic curve) but the requested offset is non-zero. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double mrot[ 3 ][ 3 ]; /* Rotation matrix */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double p3[ 2 ]; /* Permuted coordinates for point3 */ + double scale; /* Scale factor */ + double v1[ 3 ]; /* 3-vector for p1 */ + double v2[ 3 ]; /* 3-vector for p2 */ + double v3[ 3 ]; /* 3-vector for p3 */ + double vmod; /* Modulus of vector */ + double vrot[ 3 ]; /* Vector along rotation axis */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied coordinates are OK. If not, generate "bad" + output coordinates. */ + if ( ( point1[ 0 ] == AST__BAD ) || ( point1[ 1 ] == AST__BAD ) || + ( point2[ 0 ] == AST__BAD ) || ( point2[ 1 ] == AST__BAD ) ) { + point3[ 0 ] = AST__BAD; + point3[ 1 ] = AST__BAD; + +/* Otherwise, apply the axis permutation array to obtain the + coordinates of the two input points in the required + (longitude,latitude) order. */ + } else { + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + +/* Convert each point into a 3-vector of unit length. */ + palDcs2c( p1[ 0 ], p1[ 1 ], v1 ); + palDcs2c( p2[ 0 ], p2[ 1 ], v2 ); + +/* Find the cross product between these two vectors (the vector order + is reversed here to compensate for the sense of rotation introduced + by palDav2m and palDmxv below). */ + palDvxv( v2, v1, v3 ); + +/* Normalise the cross product vector, also obtaining its original + modulus. */ + palDvn( v3, vrot, &vmod ); + +/* If the original modulus was zero, the input points are either + coincident or diametrically opposite, so do not uniquely define a + great circle. In either case, we can only generate output + coordinates if the offset required is an exact multiple of pi. If + it is, generate the 3-vector that results from rotating the first + input point through this angle. */ + if ( vmod == 0.0 ) { + if ( sin( offset ) == 0.0 ) { + scale = cos( offset ); + v3[ 0 ] = v1[ 0 ] * scale; + v3[ 1 ] = v1[ 1 ] * scale; + v3[ 2 ] = v1[ 2 ] * scale; + +/* Convert the 3-vector back into spherical cooordinates and then + constrain the longitude result to lie in the range 0 to 2*pi + (palDcc2s doesn't do this itself). */ + palDcc2s( v3, &p3[ 0 ], &p3[ 1 ] ); + p3[ 0 ] = palDranrm( p3[ 0 ] ); + +/* If the offset was not a multiple of pi, generate "bad" output + coordinates. */ + } else { + p3[ 0 ] = AST__BAD; + p3[ 1 ] = AST__BAD; + } + +/* If the two input points define a great circle, scale the normalised + cross product vector to make its length equal to the required + offset (angle) between the first input point and the result. */ + } else { + vrot[ 0 ] *= offset; + vrot[ 1 ] *= offset; + vrot[ 2 ] *= offset; + +/* Generate the rotation matrix that implements this rotation and use + it to rotate the first input point (3-vector) to give the required + result (3-vector). */ + palDav2m( vrot, mrot ); + palDmxv( mrot, v1, v3 ); + +/* Convert the 3-vector back into spherical cooordinates and then + constrain the longitude result to lie in the range 0 to 2*pi. */ + palDcc2s( v3, &p3[ 0 ], &p3[ 1 ] ); + p3[ 0 ] = palDranrm( p3[ 0 ] ); + } + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + point3[ 0 ] = p3[ perm[ 0 ] ]; + point3[ 1 ] = p3[ perm[ 1 ] ]; + } + } +} + +static AstMapping *SkyOffsetMap( AstSkyFrame *this, int *status ){ +/* +*++ +* Name: +c astSkyOffsetMap +f AST_SKYOFFSETMAP + +* Purpose: +* Returns a Mapping which goes from absolute coordinates to offset +* coordinates. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "skyframe.h" +c AstMapping *astSkyOffsetMap( AstSkyFrame *this ) +f RESULT = AST_SKYOFFSETMAP( THIS, STATUS ) + +* Class Membership: +* SkyFrame method. + +* Description: +* This function returns a Mapping in which the forward transformation +* transforms a position in the coordinate system given by the System +* attribute of the supplied SkyFrame, into the offset coordinate system +* specified by the SkyRef, SkyRefP and SkyRefIs attributes of the +* supplied SkyFrame. +* +* A UnitMap is returned if the SkyFrame does not define an offset +* coordinate system. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the SkyFrame. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSkyOffsetMap() +f AST_SKYOFFSETMAP = INTEGER +* Pointer to the returned Mapping. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstCmpMap *map3; /* Partial Mapping. */ + AstMapping *result; /* The returned Mapping. */ + AstMatrixMap *map1; /* Spherical rotation in 3D cartesian space */ + AstSphMap *map2; /* 3D Cartesian to 2D spherical Mapping */ + double *vx; /* Pointer to x unit vector. */ + double *vy; /* Pointer to y unit vector. */ + double *vz; /* Pointer to z unit vector. */ + double mat[ 9 ]; /* Spherical rotation matrix */ + double vmod; /* Length of vector (+ve) */ + double vp[ 3 ]; /* Unit vector representin SkyRefP position. */ + int lataxis; /* Index of the latitude axis */ + int lonaxis; /* Index of the longitude axis */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Return a UnitMap if the offset coordinate system is not defined. */ + if( astGetSkyRefIs( this ) == AST__IGNORED_REF || + ( !astTestSkyRef( this, 0 ) && !astTestSkyRef( this, 1 ) ) ) { + result = (AstMapping *) astUnitMap( 2, "", status ); + +/* Otherwise... */ + } else { + +/* Get the longitude and latitude at the reference point and at a point + on the primary meridian. */ + lataxis = astGetLatAxis( this ); + lonaxis = 1 - lataxis; + +/* Initialise pointers to the rows of the 3x3 matrix. Each row will be + used to store a unit vector. */ + vx = mat; + vy = mat + 3; + vz = mat + 6; + +/* The following trig converts between (longitude,latitude) and (x,y,z) + on a unit sphere, in which (0,0) is at (1,0,0), (0,pi/2) is (0,0,1) + and (pi/2,0) is at (0,1,0). */ + +/* First deal with cases where the SkyRef attribute holds the standard + coords at the origin of the offset coordinate system. */ + if( astGetSkyRefIs( this ) == AST__ORIGIN_REF ) { + +/* Convert each point into a 3-vector of unit length. The SkyRef position + defines the X axis in the offset coord system. */ + palDcs2c( astGetSkyRef( this, lonaxis ), astGetSkyRef( this, lataxis ), vx ); + palDcs2c( astGetSkyRefP( this, lonaxis ), astGetSkyRefP( this, lataxis ), vp ); + +/* The Y axis is perpendicular to both the X axis and the skyrefp + position. That is, it is parallel to the cross product of the 2 above + vectors.*/ + palDvxv( vp, vx, vy ); + +/* Normalize the y vector. */ + palDvn( vy, vy, &vmod ); + +/* Report an error if the modulus of the vector is zero.*/ + if( vmod == 0.0 ) { + astError( AST__BADOC, "astConvert(%s): The position specified by the SkyRefP " + "attribute is either coincident, with or opposite to, the " + "position specified by the SkyRef attribute.", status, astGetClass( this ) ); + +/* If OK, form the Z axis as the cross product of the x and y axes. */ + } else { + palDvxv( vx, vy, vz ); + + } + +/* Now deal with cases where the SkyRef attribute holds the standard + coords at the north pole of the offset coordinate system. */ + } else { + +/* Convert each point into a 3-vector of unit length. The SkyRef position + defines the Z axis in the offset coord system. */ + palDcs2c( astGetSkyRef( this, lonaxis ), astGetSkyRef( this, lataxis ), vz ); + palDcs2c( astGetSkyRefP( this, lonaxis ), astGetSkyRefP( this, lataxis ), vp ); + +/* The Y axis is perpendicular to both the Z axis and the skyrefp + position. That is, it is parallel to the cross product of the 2 above + vectors.*/ + palDvxv( vz, vp, vy ); + +/* Normalize the y vector. */ + palDvn( vy, vy, &vmod ); + +/* Report an error if the modulus of the vector is zero.*/ + if( vmod == 0.0 ) { + astError( AST__BADOC, "astConvert(%s): The position specified by the SkyRefP " + "attribute is either coincident, with or opposite to, the " + "position specified by the SkyRef attribute.", status, astGetClass( this ) ); + +/* If OK, form the X axis as the cross product of the y and z axes. */ + } else { + palDvxv( vy, vz, vx ); + } + } + +/* Create a MatrixMap which implements the above spherical rotation. Each + row in this matrix represents one of the unit axis vectors found above. */ + map1 = astMatrixMap( 3, 3, 0, mat, "", status ); + +/* Create a 3D cartesian to 2D spherical Mapping. */ + map2 = astSphMap( "UnitRadius=1", status ); + +/* Form a series CmpMap which converts from 2D (long,lat) in the base + System to 2D (long,lat) in the offset coordinate system. */ + map3 = astCmpMap( map1, map2, 1, "", status ); + astInvert( map2 ); + result = (AstMapping *) astCmpMap( map2, map3, 1, "", status ); + +/* Free resources. */ + map1 = astAnnul( map1 ); + map2 = astAnnul( map2 ); + map3 = astAnnul( map3 ); + } + +/* Annul the returned Mapping if anything has gone wrong. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; + +} + +static double Offset2( AstFrame *this_frame, const double point1[2], + double angle, double offset, double point2[2], int *status ) { +/* +* Name: +* Offset2 + +* Purpose: +* Calculate an offset along a geodesic curve at a given bearing. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* double Offset2( AstFrame *this_frame, const double point1[2], +* double angle, double offset, double point2[2], int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astOffset2 method +* inherited from the Frame class). + +* Description: +* This function finds the SkyFrame coordinate values of a point +* which is offset a specified distance along the geodesic curve +* (i.e. great circle) at a given angle from a given starting point. + +* Parameters: +* this +* Pointer to the SkyFrame. +* point1 +* An array of double, with one element for each SkyFrame axis. +* This should contain the coordinates of the point marking the +* start of the geodesic curve. +* angle +* The angle (in radians) from the positive direction of the second +* axis, to the direction of the required position, as seen from +* the starting position. Positive rotation is in the sense of +* rotation from the positive direction of axis 2 to the positive +* direction of axis 1. +* offset +* The required offset from the first point along the geodesic +* curve, in radians. If this is positive, it will be towards +* the given angle. If it is negative, it will be in the +* opposite direction. +* point2 +* An array of double, with one element for each SkyFrame axis +* in which the coordinates of the required point will be +* returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The direction of the geodesic curve at the end point. That is, the +* angle (in radians) between the positive direction of the second +* axis and the continuation of the geodesic curve at the requested +* end point. Positive rotation is in the sense of rotation from +* the positive direction of axis 2 to the positive direction of axis +* 1. + +* Notes: +* - The geodesic curve used by this function is the path of +* shortest distance between two points, as defined by the +* astDistance function. +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double result; /* The returned answer */ + double cosoff; /* Cosine of offset */ + double cosa1; /* Cosine of longitude at start */ + double cosb1; /* Cosine of latitude at start */ + double pa; /* A position angle measured from north */ + double q1[ 3 ]; /* Vector PI/2 away from R4 in meridian of R4 */ + double q2[ 3 ]; /* Vector PI/2 away from R4 on equator */ + double q3[ 3 ]; /* Vector PI/2 away from R4 on great circle */ + double r0[ 3 ]; /* Reference position vector */ + double r3[ 3 ]; /* Vector PI/2 away from R0 on great circle */ + double sinoff; /* Sine of offset */ + double sina1; /* Sine of longitude at start */ + double sinb1; /* Sine of latitude at start */ + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Check that all supplied values are OK. If not, generate "bad" + output coordinates. */ + if ( ( point1[ 0 ] == AST__BAD ) || ( point1[ 1 ] == AST__BAD ) || + ( angle == AST__BAD ) || ( offset == AST__BAD ) ) { + point2[ 0 ] = AST__BAD; + point2[ 1 ] = AST__BAD; + +/* Otherwise, apply the axis permutation array to obtain the + coordinates of the starting point in the required (longitude,latitude) + order. */ + } else { + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + +/* If the axes are permuted, convert the supplied angle into a position + angle. */ + pa = ( perm[ 0 ] == 0 )? angle: piby2 - angle; + +/* Use Shcal to calculate the required vectors R0 (representing + the reference point) and R3 (representing the point which is 90 + degrees away from the reference point, along the required great + circle). The XY plane defines zero latitude, Z is in the direction + of increasing latitude, X is towards zero longitude, and Y is + towards longitude 90 degrees. */ + Shcal( p1[ 0 ], p1[ 1 ], pa, r0, r3, status ); + +/* Use Shapp to use R0 and R3 to calculate the new position. */ + Shapp( offset, r0, r3, p1[ 0 ], p2, status ); + +/* Normalize the result. */ + astNorm( this, p2 ); + +/* Create the vector Q1 representing the point in the meridian of the + required point which has latitude 90 degrees greater than the + required point. */ + sina1 = sin( p2[ 0 ] ); + cosa1 = cos( p2[ 0 ] ); + sinb1 = sin( p2[ 1 ] ); + cosb1 = cos( p2[ 1 ] ); + + q1[ 0 ] = -sinb1*cosa1; + q1[ 1 ] = -sinb1*sina1; + q1[ 2 ] = cosb1; + +/* Create the vector Q2 representing the point on the equator (i.e. a + latitude of zero), which has a longitude 90 degrees to the west of + the required point. */ + q2[ 0 ] = -sina1; + q2[ 1 ] = cosa1; + q2[ 2 ] = 0.0; + +/* Create the vector Q3 representing the point which is 90 degrees away + from the required point, along the required great circle. */ + cosoff = cos( offset ); + sinoff = sin( offset ); + + q3[ 0 ] = -sinoff*r0[ 0 ] + cosoff*r3[ 0 ]; + q3[ 1 ] = -sinoff*r0[ 1 ] + cosoff*r3[ 1 ]; + q3[ 2 ] = -sinoff*r0[ 2 ] + cosoff*r3[ 2 ]; + +/* Calculate the position angle of the great circle at the required + point. */ + pa = atan2( palDvdv( q3, q2 ), palDvdv( q3, q1 ) ); + +/* Convert this from a pa into the required angle. */ + result = ( perm[ 0 ] == 0 )? pa: piby2 - pa; + +/* Ensure that the end angle is in the range 0 to 2*pi. */ + result = palDranrm( result ); + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + point2[ 0 ] = p2[ perm[ 0 ] ]; + point2[ 1 ] = p2[ perm[ 1 ] ]; + } + } + +/* Return the result. */ + return result; + +} + +static void Overlay( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template SkyFrame on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astOverlay method +* inherited from the Frame class). + +* Description: +* This function overlays attributes of a SkyFrame (the "template") on to +* another Frame, so as to over-ride selected attributes of that second +* Frame. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which a Frame acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. +* +* Note that if the result Frame is a SkyFrame and a change of sky +* coordinate system occurs as a result of overlaying its System +* attribute, then some of its original attribute values may no +* longer be appropriate (e.g. the Title, or attributes describing +* its axes). In this case, these will be cleared before overlaying +* any new values. + +* Parameters: +* template +* Pointer to the template SkyFrame, for which values should have been +* explicitly set for any attribute which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of the +* "result" Frame (see below). For each axis in the result frame, the +* corresponding element of this array should contain the (zero-based) +* index of the template axis to which it corresponds. This array is used +* to establish from which template axis any axis-dependent attributes +* should be obtained. +* +* If any axis in the result Frame is not associated with a template +* axis, the corresponding element of this array should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - In general, if the result Frame is not from the same class as the +* template SkyFrame, or from a class derived from it, then attributes may +* exist in the template SkyFrame which do not exist in the result Frame. In +* this case, these attributes will not be transferred. +*/ + + +/* Local Variables: */ + AstSystemType new_alignsystem;/* Code identifying new alignment coords */ + AstSystemType new_system; /* Code identifying new sky cordinates */ + AstSystemType old_system; /* Code identifying old sky coordinates */ + int axis; /* Loop counter for result SkyFrame axes */ + int skyref_changed; /* Has the SkyRef attribute changed? */ + int reset_system; /* Was the template System value cleared? */ + int skyframe; /* Result Frame is a SkyFrame? */ + int tax0; /* Template axis for result axis 0 */ + int tax1; /* Template axis for result axis 1 */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Indicate that we do not need to reset the System attribute of the + template. */ + reset_system = 0; + new_system = AST__UNKNOWN; + +/* If the result Frame is a SkyFrame, we must test to see if overlaying its + System attribute will change the type of sky coordinate system it + describes. Determine the value of this attribute for the result and template + SkyFrames. We also need to do this if either SkyRef attribute would + change. */ + skyframe = astIsASkyFrame( result ); + if ( skyframe ) { + old_system = astGetSystem( result ); + new_system = astGetSystem( template ); + skyref_changed = ( astGetSkyRef( result, 0 ) != + astGetSkyRef( template, 0 ) ) || + ( astGetSkyRef( result, 1 ) != + astGetSkyRef( template, 1 ) ); + +/* If the coordinate system will change, any value already set for the result + SkyFrame's Title will no longer be appropriate, so clear it. */ + if ( new_system != old_system || skyref_changed ) { + astClearTitle( result ); + +/* Test if the old and new sky coordinate systems are similar enough to make + use of the same axis attribute values (e.g. if they are both equatorial + systems, then they can both use the same axis labels, etc.,so long as + the SKyRefIs value has not changed). */ + if ( IsEquatorial( new_system, status ) != IsEquatorial( old_system, status ) || + skyref_changed ) { + +/* If necessary, clear inappropriate values for all those axis attributes + whose access functions are over-ridden by this class (these access functions + will then provide suitable defaults appropriate to the new coordinate system + instead). */ + for ( axis = 0; axis < 2; axis++ ) { + astClearAsTime( result, axis ); + astClearDirection( result, axis ); + astClearFormat( result, axis ); + astClearLabel( result, axis ); + astClearSymbol( result, axis ); + astClearUnit( result, axis ); + } + } + } + +/* If the result Frame is not a SkyFrame, we must temporarily clear the + System and AlignSystem values since the values used by this class are only + appropriate to this class. */ + } else { + if( astTestSystem( template ) ) { + new_system = astGetSystem( template ); + astClearSystem( template ); + new_alignsystem = astGetAlignSystem( template ); + astClearAlignSystem( template ); + reset_system = 1; + } + } + +/* Invoke the parent class astOverlay method to transfer attributes inherited + from the parent class. */ + (*parent_overlay)( template, template_axes, result, status ); + +/* Reset the System and AlignSystem values if necessary */ + if( reset_system ) { + astSetSystem( template, new_system ); + astSetAlignSystem( template, new_alignsystem ); + } + +/* Check if the result Frame is a SkyFrame or from a class derived from + SkyFrame. If not, we cannot transfer SkyFrame attributes to it as it is + insufficiently specialised. In this case simply omit these attributes. */ + if ( skyframe && astOK ) { + +/* Define a macro that tests whether an attribute is set in the template and, + if so, transfers its value to the result. */ +#define OVERLAY(attr) \ + if ( astTest##attr( template ) ) { \ + astSet##attr( result, astGet##attr( template ) ); \ + } + +/* Store template axis indices */ + if( template_axes ) { + tax0 = template_axes[ 0 ]; + tax1 = template_axes[ 1 ]; + } else { + tax0 = 0; + tax1 = 1; + } + +/* Define a similar macro that does the same for SkyFrame specific axis + attributes. */ +#define OVERLAY2(attr) \ + if( astTest##attr( template, tax0 ) ) { \ + astSet##attr( result, 0, astGet##attr( template, tax0 ) ); \ + } \ + if( astTest##attr( template, tax1 ) ) { \ + astSet##attr( result, 1, astGet##attr( template, tax1 ) ); \ + } + +/* Use the macro to transfer each SkyFrame attribute in turn. */ + OVERLAY(Equinox); + OVERLAY(Projection); + OVERLAY(NegLon); + OVERLAY(SkyTol); + OVERLAY(AlignOffset); + OVERLAY(SkyRefIs); + OVERLAY2(SkyRef); + OVERLAY2(SkyRefP); + } + +/* Undefine macros local to this function. */ +#undef OVERLAY +#undef OVERLAY2 +} + +static void Resolve( AstFrame *this_frame, const double point1[], + const double point2[], const double point3[], + double point4[], double *d1, double *d2, int *status ){ +/* +* Name: +* Resolve + +* Purpose: +* Resolve a vector into two orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Resolve( AstFrame *this, const double point1[], +* const double point2[], const double point3[], +* double point4[], double *d1, double *d2, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astResolve method +* inherited from the Frame class). + +* Description: +* This function resolves a vector into two perpendicular components. +* The vector from point 1 to point 2 is used as the basis vector. +* The vector from point 1 to point 3 is resolved into components +* parallel and perpendicular to this basis vector. The lengths of the +* two components are returned, together with the position of closest +* aproach of the basis vector to point 3. +* +* Each vector is a geodesic curve. For a SkyFrame, these are great +* circles on the celestial sphere. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vector to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* point3 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the vector to be +* resolved. +* point4 +* An array of double, with one element for each Frame axis +* in which the coordinates of the point of closest approach of the +* basis vector to point 3 will be returned. +* d1 +* The address of a location at which to return the distance from +* point 1 to point 4 (that is, the length of the component parallel +* to the basis vector). Positive values are in the same sense as +* movement from point 1 to point 2. +* d2 +* The address of a location at which to return the distance from +* point 4 to point 3 (that is, the length of the component +* perpendicular to the basis vector). The returned value is always +* positive. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function will return "bad" coordinate values (AST__BAD) +* if any of the input coordinates has this value, or if the required +* output values are undefined. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double n1[ 3 ]; /* Unit normal to grt crcl thru p1 and p2 */ + double n2[ 3 ]; /* Unit normal to grt crcl thru p3 and p4 */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double p3[ 2 ]; /* Permuted coordinates for point3 */ + double p4[ 2 ]; /* Permuted coordinates for point4 */ + double v1[ 3 ]; /* 3-vector for p1 */ + double v2[ 3 ]; /* 3-vector for p2 */ + double v3[ 3 ]; /* 3-vector for p3 */ + double v4[ 3 ]; /* 3-vector for p4 */ + double v5[ 3 ]; /* 3-vector 90 degs away from p1 */ + double vmod; /* Modulus of vector */ + double vtemp[ 3 ]; /* Temporary vector workspace */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Store initial bad output values. */ + point4[ 0 ] = AST__BAD; + point4[ 1 ] = AST__BAD; + *d1 = AST__BAD; + *d2 = AST__BAD; + +/* Check that all supplied values are OK. */ + if ( ( point1[ 0 ] != AST__BAD ) && ( point1[ 1 ] != AST__BAD ) && + ( point2[ 0 ] != AST__BAD ) && ( point2[ 1 ] != AST__BAD ) && + ( point3[ 0 ] != AST__BAD ) && ( point3[ 1 ] != AST__BAD ) ) { + +/* If so, obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + if ( astOK ) { + +/* Apply the axis permutation array to obtain the coordinates of the + three supplied point in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + p3[ perm[ 0 ] ] = point3[ 0 ]; + p3[ perm[ 1 ] ] = point3[ 1 ]; + +/* Convert each point into a 3-vector of unit length. */ + palDcs2c( p1[ 0 ], p1[ 1 ], v1 ); + palDcs2c( p2[ 0 ], p2[ 1 ], v2 ); + palDcs2c( p3[ 0 ], p3[ 1 ], v3 ); + +/* Find the cross product between the first two vectors, and normalize is. + This is the unit normal to the great circle plane defining parallel + distance. */ + palDvxv( v2, v1, vtemp ); + palDvn( vtemp, n1, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the first two + vectors are identical or diametrically opposite). */ + if( vmod > 0.0 ) { + +/* Now take the cross product of the normal vector and v1. This gives a + point, v5, on the great circle which is 90 degrees away from v1, in the + direction of v2. */ + palDvxv( v1, n1, v5 ); + +/* Find the cross product of the outlying point (point 3), and the vector + n1 found above, and normalize it. This is the unit normal to the great + circle plane defining perpendicular distance. */ + palDvxv( v3, n1, vtemp ); + palDvn( vtemp, n2, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the + outlying point is normal to the great circle defining the basis + vector). */ + if( vmod > 0.0 ) { + +/* The point of closest approach, point 4, is the point which is normal + to both normal vectors (i.e. the intersection of the two great circles). + This is the cross product of n1 and n2. No need to normalize this time + since both n1 and n2 are unit vectors, and so v4 will already be a + unit vector. */ + palDvxv( n1, n2, v4 ); + +/* The dot product of v4 and v1 is the cos of the parallel distance, + d1, whilst the dot product of v4 and v5 is the sin of the parallel + distance. Use these to get the parallel distance with the correct + sign, in the range -PI to +PI. */ + *d1 = atan2( palDvdv( v4, v5 ), palDvdv( v4, v1 ) ); + +/* The dot product of v4 and v3 is the cos of the perpendicular distance, + d2, whilst the dot product of n1 and v3 is the sin of the perpendicular + distance. Use these to get the perpendicular distance. */ + *d2 = fabs( atan2( palDvdv( v3, n1 ), palDvdv( v3, v4 ) ) ); + +/* Convert the 3-vector representing the intersection of the two planes + back into spherical cooordinates and then constrain the longitude result + to lie in the range 0 to 2*pi. */ + palDcc2s( v4, &p4[ 0 ], &p4[ 1 ] ); + p4[ 0 ] = palDranrm( p4[ 0 ] ); + +/* Permute the result coordinates to undo the effect of the SkyFrame + axis permutation array. */ + point4[ 0 ] = p4[ perm[ 0 ] ]; + point4[ 1 ] = p4[ perm[ 1 ] ]; + } + } + } + } + + return; + +} + +static AstPointSet *ResolvePoints( AstFrame *this_frame, const double point1[], + const double point2[], AstPointSet *in, + AstPointSet *out, int *status ) { +/* +* Name: +* ResolvePoints + +* Purpose: +* Resolve a set of vectors into orthogonal components + +* Type: +* Private function. + +* Synopsis: +* #include "frame.h" +* AstPointSet *astResolvePoints( AstFrame *this, const double point1[], +* const double point2[], AstPointSet *in, +* AstPointSet *out ) + +* Class Membership: +* SkyFrame member function (over-rides the astResolvePoints method +* inherited from the Frame class). + +* Description: +* This function takes a Frame and a set of vectors encapsulated +* in a PointSet, and resolves each one into two orthogonal components, +* returning these two components in another PointSet. +* +* This is exactly the same as the public astResolve method, except +* that this method allows many vectors to be processed in a single call, +* thus reducing the computational cost of overheads of many +* individual calls to astResolve. + +* Parameters: +* this +* Pointer to the Frame. +* point1 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the start of the basis vector, +* and of the vectors to be resolved. +* point2 +* An array of double, with one element for each Frame axis +* (Naxes attribute). This marks the end of the basis vector. +* in +* Pointer to the PointSet holding the ends of the vectors to be +* resolved. +* out +* Pointer to a PointSet which will hold the length of the two +* resolved components. A NULL value may also be given, in which +* case a new PointSet will be created by this function. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. The first axis will +* hold the lengths of the vector components parallel to the basis vector. +* These values will be signed (positive values are in the same sense as +* movement from point 1 to point 2. The second axis will hold the lengths +* of the vector components perpendicular to the basis vector. These +* values will be signed only if the Frame is 2-dimensional, in which +* case a positive value indicates that rotation from the basis vector +* to the tested vector is in the same sense as rotation from the first +* to the second axis of the Frame. + +* Notes: +* - The number of coordinate values per point in the input +* PointSet must match the number of axes in the supplied Frame. +* - If an output PointSet is supplied, it must have space for +* sufficient number of points and 2 coordinate values per point. +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +* - We assume spherical geometry throughout this function. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + const int *perm; /* Pointer to axis permutation array */ + double **ptr_in; /* Pointers to input axis values */ + double **ptr_out; /* Pointers to returned axis values */ + double *d1; /* Pointer to next parallel component value */ + double *d2; /* Pointer to next perpendicular component value */ + double *point3x; /* Pointer to next first axis value */ + double *point3y; /* Pointer to next second axis value */ + double n1[ 3 ]; /* Unit normal to grt crcl thru p1 and p2 */ + double n2[ 3 ]; /* Unit normal to grt crcl thru p3 and p4 */ + double p1[ 2 ]; /* Permuted coordinates for point1 */ + double p2[ 2 ]; /* Permuted coordinates for point2 */ + double p3[ 2 ]; /* Permuted coordinates for point3 */ + double sign; /* Sign for perpendicular distances */ + double v1[ 3 ]; /* 3-vector for p1 */ + double v2[ 3 ]; /* 3-vector for p2 */ + double v3[ 3 ]; /* 3-vector for p3 */ + double v4[ 3 ]; /* 3-vector for p4 */ + double v5[ 3 ]; /* 3-vector 90 degs away from p1 */ + double vmod; /* Modulus of vector */ + double vtemp[ 3 ]; /* Temporary vector workspace */ + int ipoint; /* Index of next point */ + int ncoord_in; /* Number of input PointSet coordinates */ + int ncoord_out; /* Number of coordinates in output PointSet */ + int npoint; /* Number of points to transform */ + int npoint_out; /* Number of points in output PointSet */ + int ok; /* OK to proceed? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Obtain the number of input vectors to resolve and the number of coordinate + values per vector. */ + npoint = astGetNpoint( in ); + ncoord_in = astGetNcoord( in ); + +/* If OK, check that the number of input coordinates matches the number + required by the Frame. Report an error if these numbers do not match. */ + if ( astOK && ( ncoord_in != 2 ) ) { + astError( AST__NCPIN, "astResolvePoints(%s): Bad number of coordinate " + "values (%d) in input %s.", status, astGetClass( this ), ncoord_in, + astGetClass( in ) ); + astError( AST__NCPIN, "The %s given requires 2 coordinate values for " + "each input point.", status, astGetClass( this ) ); + } + +/* If still OK, and a non-NULL pointer has been given for the output PointSet, + then obtain the number of points and number of coordinates per point for + this PointSet. */ + if ( astOK && out ) { + npoint_out = astGetNpoint( out ); + ncoord_out = astGetNcoord( out ); + +/* Check that the dimensions of this PointSet are adequate to accommodate the + output coordinate values and report an error if they are not. */ + if ( astOK ) { + if ( npoint_out < npoint ) { + astError( AST__NOPTS, "astResolvePoints(%s): Too few points (%d) in " + "output %s.", status, astGetClass( this ), npoint_out, + astGetClass( out ) ); + astError( AST__NOPTS, "The %s needs space to hold %d transformed " + "point(s).", status, astGetClass( this ), npoint ); + } else if ( ncoord_out < 2 ) { + astError( AST__NOCTS, "astResolvePoints(%s): Too few coordinate " + "values per point (%d) in output %s.", status, + astGetClass( this ), ncoord_out, astGetClass( out ) ); + astError( AST__NOCTS, "The %s supplied needs space to store 2 " + "coordinate value(s) per transformed point.", status, + astGetClass( this ) ); + } + } + } + +/* If all the validation stages are passed successfully, and a NULL output + pointer was given, then create a new PointSet to encapsulate the output + coordinate data. */ + if ( astOK ) { + if ( !out ) { + result = astPointSet( npoint, 2, "", status ); + +/* Otherwise, use the PointSet supplied. */ + } else { + result = out; + } + } + +/* Get pointers to the input and output axis values */ + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Obtain a pointer to the SkyFrame's axis permutation array. */ + perm = astGetPerm( this ); + +/* If the axes have been swapped we need to swap the sign of the returned + perpendicular distances. */ + sign = ( perm[ 0 ] == 0 ) ? -1.0 : 1.0; + +/* Check pointers can be used safely */ + if( astOK ) { + +/* Apply the axis permutation array to obtain the coordinates of the + two supplied points in the required (longitude,latitude) order. */ + p1[ perm[ 0 ] ] = point1[ 0 ]; + p1[ perm[ 1 ] ] = point1[ 1 ]; + p2[ perm[ 0 ] ] = point2[ 0 ]; + p2[ perm[ 1 ] ] = point2[ 1 ]; + +/* Convert these points into 3-vectors of unit length. */ + palDcs2c( p1[ 0 ], p1[ 1 ], v1 ); + palDcs2c( p2[ 0 ], p2[ 1 ], v2 ); + +/* Find the cross product between the vectors, and normalize it. This is the + unit normal to the great circle plane defining parallel distance. */ + palDvxv( v2, v1, vtemp ); + palDvn( vtemp, n1, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the first two + vectors are identical or diametrically opposite). */ + ok = 0; + if( vmod > 0.0 ) { + ok = 1; + +/* Now take the cross product of the normal vector and v1. This gives a + point, v5, on the great circle which is 90 degrees away from v1, in the + direction of v2. */ + palDvxv( v1, n1, v5 ); + } + +/* Store pointers to the first two axis arrays in the returned PointSet. */ + d1 = ptr_out[ 0 ]; + d2 = ptr_out[ 1 ]; + +/* Store pointers to the axis values in the supplied PointSet. */ + point3x = ptr_in[ 0 ]; + point3y = ptr_in[ 1 ]; + +/* Check supplied values can be used */ + if( ok ) { + +/* Loop round each supplied vector. */ + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++, + point3x++, point3y++ ) { + +/* Store bad output values if either input axis value is bad. */ + if( *point3x == AST__BAD || *point3y == AST__BAD ){ + *d1 = AST__BAD; + *d2 = AST__BAD; + +/* If both are good... */ + } else { + +/* Apply the axis permutation array to obtain the coordinates in the + required (longitude,latitude) order. */ + p3[ perm[ 0 ] ] = *point3x; + p3[ perm[ 1 ] ] = *point3y; + +/* Convert into a 3-vector of unit length. */ + palDcs2c( p3[ 0 ], p3[ 1 ], v3 ); + +/* Find the cross product of the outlying point (point 3), and the vector + n1 found above, and normalize it. This is the unit normal to the great + circle plane defining perpendicular distance. */ + palDvxv( v3, n1, vtemp ); + palDvn( vtemp, n2, &vmod ); + +/* Return with bad values if the normal is undefined (i.e. if the + outlying point is normal to the great circle defining the basis + vector). */ + if( vmod <= 0.0 ) { + *d1 = AST__BAD; + *d2 = AST__BAD; + } else { + +/* The point of closest approach, point 4, is the point which is normal + to both normal vectors (i.e. the intersection of the two great circles). + This is the cross product of n1 and n2. No need to normalize this time + since both n1 and n2 are unit vectors, and so v4 will already be a + unit vector. */ + palDvxv( n1, n2, v4 ); + +/* The dot product of v4 and v1 is the cos of the parallel distance, + d1, whilst the dot product of v4 and v5 is the sin of the parallel + distance. Use these to get the parallel distance with the correct + sign, in the range -PI to +PI. */ + *d1 = atan2( palDvdv( v4, v5 ), palDvdv( v4, v1 ) ); + +/* The dot product of v4 and v3 is the cos of the perpendicular distance, + d2, whilst the dot product of n1 and v3 is the sin of the perpendicular + distance. Use these to get the perpendicular distance. */ + *d2 = sign*atan2( palDvdv( v3, n1 ), palDvdv( v3, v4 ) ); + } + } + } + +/* If supplied values cannot be used, fill the returned PointSet with bad + values */ + } else { + for( ipoint = 0; ipoint < npoint; ipoint++, d1++, d2++ ) { + *d1 = AST__BAD; + *d2 = AST__BAD; + } + } + } + +/* Annul the returned PointSet if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void SetAsTime( AstSkyFrame *this, int axis, int value, int *status ) { +/* +* Name: +* SetAsTime + +* Purpose: +* Set a value for the AsTime attribute for a SkyFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetAsTime( AstSkyFrame *this, int axis, int value, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function sets the boolean value of the AsTime attribute for a +* specified axis of a SkyFrame. This value indicates whether axis values +* should be formatted as times (as opposed to angles) by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which a value is to be set (zero based). +* value +* The boolean value to be set. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to Axis object */ + AstSkyAxis *new_ax; /* Pointer to new SkyAxis object */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astSetAsTime" ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis ); + +/* Check if the Axis object is a SkyAxis. If not, we will replace it with + one. */ + if ( !astIsASkyAxis( ax ) ) { + +/* Create a new SkyAxis and overlay the attributes of the original Axis. */ + new_ax = astSkyAxis( "", status ); + astAxisOverlay( ax, new_ax ); + +/* Modify the SkyFrame to use the new Skyaxis and annul the original Axis + pointer. Retain a pointer to the new SkyAxis. */ + astSetAxis( this, axis, new_ax ); + ax = astAnnul( ax ); + ax = (AstAxis *) new_ax; + } + +/* Set a value for the Axis AsTime attribute. */ + astSetAxisAsTime( ax, value ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* SkyFrame member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a SkyFrame, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Attributes: +* As well as those attributes inherited from the parent class, this +* function also accepts values for the following additional attributes: +* +* Equinox (double, read as a string) + +* Notes: +* This protected method is intended to be invoked by the Object astSet +* method and makes additional attributes accessible to it. +*/ + +/* Local Vaiables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + double dval; /* Floating point attribute value */ + double dval1; /* Floating point attribute value */ + double dval2; /* Floating point attribute value */ + double mjd; /* Modified Julian Date */ + int astime; /* Value of AsTime attribute */ + int axis; /* Axis index */ + int equinox; /* Offset of Equinox attribute value */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + int neglon; /* Display -ve longitudes? */ + int ok; /* Can string be used? */ + int offset; /* Offset of start of attribute value */ + int projection; /* Offset of projection attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 2 == astSscanf( setting, "astime(%d)= %d %n", &axis, &astime, &nc ) ) + && ( nc >= len ) ) { + astSetAsTime( this, axis - 1, astime ); + +/* Equinox. */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "equinox=%n%*[^\n]%n", + &equinox, &nc ) ) && ( nc >= len ) ) { + +/* Convert the Equinox value to a Modified Julian Date before use. */ + mjd = astReadDateTime( setting + equinox ); + if ( astOK ) { + astSetEquinox( this, mjd ); + +/* Report contextual information if the conversion failed. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid equinox value " + "\"%s\" given for sky coordinate system.", status, + astGetClass( this ), setting + equinox ); + } + +/* NegLon. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "neglon= %d %n", &neglon, &nc ) ) + && ( nc >= len ) ) { + astSetNegLon( this, neglon ); + +/* SkyTol. */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "skytol= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetSkyTol( this, dval ); + +/* Projection. */ +/* ----------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "projection=%n%*[^\n]%n", + &projection, &nc ) ) + && ( nc >= len ) ) { + astSetProjection( this, setting + projection ); + +/* SkyRef. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "skyref=%n%*[^\n]%n", + &offset, &nc ) ) + && ( nc >= len ) ) { + ok = 0; + nc = astUnformat( this, 0, setting + offset, &dval1 ); + if( setting[ offset + nc ] == ',' ) { + nc++; + nc += astUnformat( this, 1, setting + offset + nc, &dval2 ); + if( nc == strlen( setting + offset ) ) { + astSetSkyRef( this, 0, dval1 ); + astSetSkyRef( this, 1, dval2 ); + ok = 1; + } + } + + if( !ok && astOK ) { + astError( AST__BADOC, "astSetAttrib(%s): Invalid axis values string " + "\"%.*s\" given for SkyRef attribute.", status, astGetClass( this ), + (int) astChrLen( setting + offset ), setting + offset ); + } + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "skyref(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetSkyRef( this, axis - 1, dval ); + +/* SkyRefIs. */ +/* --------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "skyrefis=%n%*[^\n]%n", + &offset, &nc ) ) + && ( nc >= len ) ) { + + if( astChrMatch( setting + offset, POLE_STRING ) ) { + astSetSkyRefIs( this, AST__POLE_REF ); + + } else if( astChrMatch( setting + offset, ORIGIN_STRING ) ) { + astSetSkyRefIs( this, AST__ORIGIN_REF ); + + } else if( astChrMatch( setting + offset, IGNORED_STRING ) ) { + astSetSkyRefIs( this, AST__IGNORED_REF ); + + } else if( astOK ) { + astError( AST__OPT, "astSet(%s): option '%s' is unknown in '%s'.", status, + astGetClass( this ), setting+offset, setting ); + } + +/* SkyRefP. */ +/* -------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "skyrefp=%n%*[^\n]%n", + &offset, &nc ) ) + && ( nc >= len ) ) { + + ok = 0; + nc = astUnformat( this, 0, setting + offset, &dval1 ); + if( setting[ offset + nc ] == ',' ) { + nc++; + nc += astUnformat( this, 1, setting + offset + nc, &dval2 ); + if( nc == strlen( setting + offset ) ) { + astSetSkyRefP( this, 0, dval1 ); + astSetSkyRefP( this, 1, dval2 ); + ok = 1; + } + } + + if( !ok && astOK ) { + astError( AST__BADOC, "astSetAttrib(%s): Invalid axis values string " + "\"%.*s\" given for SkyRefP attribute.", status, astGetClass( this ), + (int) astChrLen( setting + offset ), setting + offset ); + } + + +/* SkyRefP(axis). */ +/* -------------- */ + } else if ( nc = 0, + ( 2 == astSscanf( setting, "skyrefp(%d)= %lg %n", + &axis, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetSkyRefP( this, axis - 1, dval ); + +/* AlignOffset. */ +/* ------------ */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "alignoffset= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetAlignOffset( this, ival ); + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + } else if ( !strncmp( setting, "islataxis", 9 ) || + !strncmp( setting, "islonaxis", 9 ) || + MATCH( "lataxis" ) || + MATCH( "lonaxis" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetCachedLAST( AstSkyFrame *this, double last, double epoch, + double obslon, double obslat, double obsalt, + double dut1, int *status ) { +/* +* Name: +* SetCachedLAST + +* Purpose: +* Store a LAST value in the cache in the SkyFrame vtab. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetCachedLAST( AstSkyFrame *this, double last, double epoch, +* double obslon, double obslat, double obsalt, +* double dut1, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function stores the supplied LAST value in a cache in the +* SkyFrame virtual function table for later use by GetCachedLAST. + +* Parameters: +* this +* Pointer to the SkyFrame. +* last +* The Local Apparent Sidereal Time (radians). +* epoch +* The epoch (MJD). +* obslon +* Observatory geodetic longitude (radians) +* obslat +* Observatory geodetic latitude (radians) +* obsalt +* Observatory geodetic altitude (metres) +* dut1 +* The UT1-UTC correction, in seconds. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstSkyLastTable *table; + double *ep; + double *lp; + double lp_ref; + int i; + int itable; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise */ + table = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Ensure no threads are allowed to read the table whilst we are writing + to it. */ + LOCK_WLOCK1 + +/* Loop round every LAST table held in the vtab. Each table refers to a + different observatory position and/or DUT1 value. */ + for( itable = 0; itable < nlast_tables; itable++ ) { + table = last_tables[ itable ]; + +/* See if the table refers to the given position and dut1 value, allowing + some small tolerance. If it does, leave the loop. */ + if( fabs( table->obslat - obslat ) < 2.0E-7 && + fabs( table->obslon - obslon ) < 2.0E-7 && + fabs( table->obsalt - obsalt ) < 1.0 && + fabs( table->dut1 - dut1 ) < 1.0E-5 ) break; + +/* Ensure "table" ends up NULL if no suitable table is found. */ + table = NULL; + } + +/* If no table was found, create one now, and add it into the vtab cache. */ + if( !table ) { + + astBeginPM; + table = astMalloc( sizeof( AstSkyLastTable ) ); + itable = nlast_tables++; + last_tables = astGrow( last_tables, nlast_tables, + sizeof( AstSkyLastTable * ) ); + astEndPM; + + if( astOK ) { + last_tables[ itable ] = table; + table->obslat = obslat; + table->obslon = obslon; + table->obsalt = obsalt; + table->dut1 = dut1; + table->nentry = 1; + + astBeginPM; + table->epoch = astMalloc( sizeof( double ) ); + table->last = astMalloc( sizeof( double ) ); + astEndPM; + + if( astOK ) { + table->epoch[ 0 ] = epoch; + table->last[ 0 ] = last; + } + } + + +/* If we have a table, add the new point into it. */ + } else { + +/* Extend the epoch and last arrays. */ + astBeginPM; + table->epoch = astGrow( table->epoch, ++(table->nentry), sizeof( double ) ); + table->last = astGrow( table->last, table->nentry, sizeof( double ) ); + astEndPM; + +/* Check memory allocation was successful. */ + if( astOK ) { + +/* Get pointers to the last original elements in the arrays of epoch and + corresponding LAST values in the table. */ + ep = table->epoch + table->nentry - 2; + lp = table->last + table->nentry - 2; + +/* Starting from the end of the arrays, shuffle all entries up one + element until an element is found which is less than the supplied epoch + value. This maintains the epoch array in monotonic increasing order. */ + for( i = table->nentry - 2; i >= 0; i--,ep--,lp-- ) { + if( *ep <= epoch ) break; + ep[ 1 ] = *ep; + lp[ 1 ] = *lp; + } + +/* Store the new epoch and LAST value. Add or subtract 2.PI as needed + from the new LAST value to ensure it is continuous with an adjacent + LAST value. This is needed for interpolation between the two values + to be meaningful. */ + ep[ 1 ] = epoch; + +/* For most cases, compare with the previous LAST value. If the new epoch + value is smaller than any epoch already in the table, there will be no + previous LAST value. So compare with the next value instead. */ + if( i >= 0 ) { + lp_ref = lp[ 0 ]; + } else { + lp_ref = lp[ 2 ]; + } + + if( last > lp_ref + AST__DPI ) { + lp[ 1 ] = last - 2*AST__DPI; + + } else if( last < lp_ref - AST__DPI ) { + lp[ 1 ] = last + 2*AST__DPI; + + } else { + lp[ 1 ] = last; + } + } + } + +/* Indicate other threads are now allowed to read the table. */ + UNLOCK_RWLOCK1 + +} + +static void SetDut1( AstFrame *this_frame, double val, int *status ) { +/* +* Name: +* SetDut1 + +* Purpose: +* Set the value of the Dut1 attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetDut1( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetDut1 method +* inherited from the Frame class). + +* Description: +* This function clears the Dut1 value and updates the LAST value +* stored in the SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New Dut1 value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstSkyFrame *this; + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Note the original Dut1 value. */ + orig = astGetDut1( this ); + +/* Invoke the parent method to set the Frame Dut1 value. */ + (*parent_setdut1)( this_frame, val, status ); + +/* If the DUT1 value has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - val ) > 1.0E-6 ) { + this->last = AST__BAD; + this->eplast = AST__BAD; + this->klast = AST__BAD; + } +} + +static void SetLast( AstSkyFrame *this, int *status ) { +/* +* Name: +* SetLast + +* Purpose: +* Set the Local Appearent Sidereal Time for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetLast( AstSkyFrame *this, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function sets the Local Apparent Sidereal Time at the epoch +* and geographical longitude given by the current values of the Epoch +* and ObsLon attributes associated with the supplied SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + double epoch; /* Epoch as a TDB MJD */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the SkyFrame Epoch as a TDB MJD. */ + epoch = astGetEpoch( this ); + +/* Calculate the LAST value (in rads) and store in the SkyFrame structure. */ + this->last = CalcLAST( this, epoch, astGetObsLon( this ), + astGetObsLat( this ), astGetObsAlt( this ), + astGetDut1( this ), status ); + +/* Save the TDB MJD to which this LAST corresponds. */ + this->eplast = epoch; + +/* The ratio between solar and sidereal time is a slowly varying function + of epoch. The GetLAST function returns a fast approximation to LAST + by using the ratio between solar and sidereal time. Indicate that + GetLAST should re-calculate the ratio by setting the ratio value bad. */ + this->klast = AST__BAD; +} + +static void SetObsAlt( AstFrame *this, double val, int *status ) { +/* +* Name: +* SetObsAlt + +* Purpose: +* Set the value of the ObsAlt attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetObsAlt( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetObsAlt method +* inherited from the Frame class). + +* Description: +* This function sets the ObsAlt value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New ObsAlt value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original ObsAlt value. */ + orig = astGetObsAlt( this ); + +/* Invoke the parent method to set the Frame ObsAlt. */ + (*parent_setobsalt)( this, val, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - val ) > 0.001 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void SetObsLat( AstFrame *this, double val, int *status ) { +/* +* Name: +* SetObsLat + +* Purpose: +* Set the value of the ObsLat attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetObsLat( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetObsLat method +* inherited from the Frame class). + +* Description: +* This function sets the ObsLat value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New ObsLat value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original ObsLat value. */ + orig = astGetObsLat( this ); + +/* Invoke the parent method to set the Frame ObsLat. */ + (*parent_setobslat)( this, val, status ); + +/* If the altitude has changed significantly, indicate that the LAST value + and magnitude of the diurnal aberration vector will need to be + re-calculated when next needed. */ + if( fabs( orig - val ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + ( (AstSkyFrame *) this )->diurab = AST__BAD; + } +} + +static void SetObsLon( AstFrame *this, double val, int *status ) { +/* +* Name: +* SetObsLon + +* Purpose: +* Set the value of the ObsLon attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetObsLon( AstFrame *this, double val, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetObsLon method +* inherited from the Frame class). + +* Description: +* This function sets the ObsLon value. + +* Parameters: +* this +* Pointer to the SkyFrame. +* val +* New ObsLon value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double orig; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Note the original ObsLon value. */ + orig = astGetObsLon( this ); + +/* Invoke the parent method to set the Frame ObsLon. */ + (*parent_setobslon)( this, val, status ); + +/* If the longitude has changed significantly, indicate that the LAST value + will need to be re-calculated when it is next needed. */ + if( fabs( orig - val ) > 1.0E-8 ) { + ( (AstSkyFrame *) this )->last = AST__BAD; + ( (AstSkyFrame *) this )->eplast = AST__BAD; + ( (AstSkyFrame *) this )->klast = AST__BAD; + } +} + +static void SetSystem( AstFrame *this_frame, AstSystemType system, int *status ) { +/* +* Name: +* SetSystem + +* Purpose: +* Set the System attribute for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void SetSystem( AstFrame *this_frame, AstSystemType system, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSetSystem protected +* method inherited from the Frame class). + +* Description: +* This function assigns a new value to the System attribute for a SkyFrame. + +* Parameters: +* this +* Pointer to the SkyFrame. +* system +* The new System value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* FrameSet to be used as the Mapping */ + AstSkyFrame *sfrm; /* Copy of original SkyFrame */ + AstSkyFrame *this; /* Pointer to SkyFrame structure */ + double xin[ 2 ]; /* Axis 0 values */ + double xout[ 2 ]; /* Axis 0 values */ + double yin[ 2 ]; /* Axis 1 values */ + double yout[ 2 ]; /* Axis 1 values */ + int aloff; /* The AlignOffset attribute value */ + int aloff_set; /* Is the AlignOffset attribute set? */ + int skyref_set; /* Is either SkyRef attribute set? */ + int skyrefis; /* The SkyRefIs attribute value */ + int skyrefis_set; /* Is the SkyRefIs attribute set? */ + int skyrefp_set; /* Is either SkyRefP attribute set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* See if either the SkyRef or SkyRefP attribute is set. */ + skyref_set = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ); + skyrefp_set = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 ); + +/* If so, we will need to transform their values into the new coordinate + system. Save a copy of the SkyFrame with its original System value. */ + sfrm = ( skyref_set || skyrefp_set )?astCopy( this ):NULL; + +/* Use the parent method to set the new System value. */ + (*parent_setsystem)( this_frame, system, status ); + +/* Now modify the SkyRef and SkyRefP attributes if necessary. */ + if( sfrm ) { + +/* Save the AlignOffset, SkyRefIs, SkyRef and SkyRefP values. */ + aloff_set = astTestAlignOffset( sfrm ); + aloff = astGetAlignOffset( sfrm ); + skyrefis_set = astTestSkyRefIs( sfrm ); + skyrefis = astGetSkyRefIs( sfrm ); + + xin[ 0 ] = astGetSkyRef( sfrm, 0 ); + xin[ 1 ] = astGetSkyRefP( sfrm, 0 ); + yin[ 0 ] = astGetSkyRef( sfrm, 1 ); + yin[ 1 ] = astGetSkyRefP( sfrm, 1 ); + +/* Clear the SkyRef and SkyRefP values to avoid infinite recursion in the + following call to astConvert. */ + if( skyref_set ) { + astClearSkyRef( sfrm, 0 ); + astClearSkyRef( sfrm, 1 ); + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + + if( skyrefp_set ) { + astClearSkyRefP( sfrm, 0 ); + astClearSkyRefP( sfrm, 1 ); + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + } + +/* Also set AlignOffset and SkyRefIs so that the following call to + astConvert does not align in offset coords. */ + astSetAlignOffset( sfrm, 0 ); + astSetSkyRefIs( sfrm, AST__IGNORED_REF ); + +/* Get the Mapping from the original System to the new System. Invoking + astConvert will recursively invoke SetSystem again. This is why we need + to be careful to ensure that SkyRef and SKyRefP are cleared above - doing + so ensure we do not end up with infinite recursion. */ + fs = astConvert( sfrm, this, "" ); + +/* If the conversion is not possible, clear the SkyRef and SkyRefP + values. */ + if( !fs ) { + if( skyref_set ) { + astClearSkyRef( this, 0 ); + astClearSkyRef( this, 1 ); + } + if( skyrefp_set ) { + astClearSkyRefP( this, 0 ); + astClearSkyRefP( this, 1 ); + } + +/* Use the Mapping to find the SkyRef and SkyRefP positions in the new + coordinate system. */ + } else { + astTran2( fs, 2, xin, yin, 1, xout, yout ); + +/* Store the values as required. */ + if( skyref_set ) { + astSetSkyRef( this, 0, xout[ 0 ] ); + astSetSkyRef( this, 1, yout[ 0 ] ); + } + + if( skyrefp_set ) { + astSetSkyRefP( this, 0, xout[ 1 ] ); + astSetSkyRefP( this, 1, yout[ 1 ] ); + } + +/* Restore the original SkyRefIs and AlignOffset values. */ + if( aloff_set ) { + astSetAlignOffset( this, aloff ); + } else { + astClearAlignOffset( this ); + } + + if( skyrefis_set ) { + astSetSkyRefIs( this, skyrefis ); + } else { + astClearSkyRefIs( this ); + } + +/* Free resources. */ + fs = astAnnul( fs ); + } + sfrm = astAnnul( sfrm ); + } +} + +static void Shapp( double dist, double *r0, double *r3, double a0, + double *p4, int *status ){ +/* +* Name: +* Shapp + +* Purpose: +* Use the vectors calculated by Shcal to find a sky position +* which is offset along a given position angle. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Shapp( double dist, double *r0, double *r3, double a0, +* double *p4, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function uses the vectors R0 and R3 calculated previously by +* Shcal to find the sky position which is offset away from the +* "reference" position (see function Offset2) by a given arc +* distance, along a given great circle. +* +* No checks are made for AST__BAD values. + +* Parameters: +* dist +* The arc distance to move away from the reference position +* in the given direction, in radians. +* r0 +* Pointer to an array holding the 3-vector representing the reference +* position. +* r3 +* Pointer to an array holding the 3-vector representing the +* point which is 90 degrees away from the reference point, along +* the required great circle. +* a0 +* The sky longitude of the reference position, in radians. +* p4 +* Pointer to an array of 2 doubles in which to put the sky longitude +* and latitude of the required point, in radians. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double cosdst; /* Cosine of DIST */ + double r4[ 3 ]; /* Required position vector */ + double sindst; /* Sine of DIST */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store commonly used values. */ + sindst = sin( dist ); + cosdst = cos( dist ); + +/* The vector R4 representing the required point is produced as a + linear sum of R0 and R3. */ + r4[ 0 ] = cosdst*r0[ 0 ] + sindst*r3[ 0 ]; + r4[ 1 ] = cosdst*r0[ 1 ] + sindst*r3[ 1 ]; + r4[ 2 ] = cosdst*r0[ 2 ] + sindst*r3[ 2 ]; + +/* Create the longitude of the required point. If this point is at + a pole it is assigned the same longitude as the reference point. */ + if( r4[ 0 ] != 0.0 || r4[ 1 ] != 0.0 ) { + p4[ 0 ] = atan2( r4[ 1 ], r4[ 0 ] ); + } else { + p4[ 0 ] = a0; + } + +/* Create the latitude of the required point. */ + if( r4[ 2 ] > 1.0 ) { + r4[ 2 ] = 1.0; + } else if( r4[ 2 ] < -1.0 ) { + r4[ 2 ] = -1.0; + } + p4[ 1 ] = asin( r4[ 2 ] ); + +} + +static void Shcal( double a0, double b0, double angle, double *r0, + double *r3, int *status ) { +/* +* Name: +* Shcal + +* Purpose: +* Calculate vectors required by Offset2. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void Shcal( double a0, double b0, double angle, double *r0, +* double *r3, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function calculates the 3-vector R0, representing the given +* sky position (A0,B0), and the 3-vector R3, representing the sky +* position which is 90 degrees away from R0, along a great circle +* passing through R0 at a position angle given by ANGLE. Each +* 3-vector holds Cartesian (X,Y,Z) values with origin at the centre +* of the celestial sphere. The XY plane is the "equator", the Z +* axis is in the direction of the "north pole", X is towards zero +* longitude (A=0), and Y is towards longitude 90 degrees. +* +* No checks are made for AST__BAD input values. + +* Parameters: +* a0 +* The sky longitude of the given position, in radians. +* b0 +* The sky latitude of the given position, in radians. +* angle +* The position angle of a great circle passing through the given +* position. That is, the angle from north to the required +* direction, in radians. Positive angles are in the sense of +* rotation from north to east. +* r0 +* A pointer to an array to receive 3-vector R0. See above. +* r3 +* A pointer to an array to receive 3-vector R3. See above. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double cosa0; /* Cosine of A0 */ + double cosb0; /* Cosine of B0 */ + double cospa; /* Cosine of ANGLE */ + double r1[ 3 ]; /* Vector PI/2 away from R0 in meridian of R0 */ + double r2[ 3 ]; /* Vector PI/2 away from R0 on equator */ + double sinpa; /* Sine of ANGLE */ + double sina0; /* Sine of A0 */ + double sinb0; /* Sine of B0 */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store commonly used values. */ + sina0 = sin( a0 ); + cosa0 = cos( a0 ); + sinb0 = sin( b0 ); + cosb0 = cos( b0 ); + sinpa = sin( angle ); + cospa = cos( angle ); + +/* Create the vector R0 representing the given point. The XY plane + defines zero latitude, Z is in the direction of increasing latitude, + X is towards zero longitude, and Y is towards longitude 90 degrees. */ + r0[ 0 ] = cosb0*cosa0; + r0[ 1 ] = cosb0*sina0; + r0[ 2 ] = sinb0; + +/* Create the vector R1 representing the point in the meridian of the + given point which has latitude 90 degrees greater than the + given point. */ + r1[ 0 ] = -sinb0*cosa0; + r1[ 1 ] = -sinb0*sina0; + r1[ 2 ] = cosb0; + +/* Create the vector R2 representing the point on the equator (i.e. a + latitude of zero), which has a longitude 90 degrees to the west of + the given point. */ + r2[ 0 ] = -sina0; + r2[ 1 ] = cosa0; + r2[ 2 ] = 0.0; + +/* Create the vector R3 representing the point which is 90 degrees away + from the given point, along the required great circle. */ + r3[ 0 ] = cospa*r1[ 0 ] + sinpa*r2[ 0 ]; + r3[ 1 ] = cospa*r1[ 1 ] + sinpa*r2[ 1 ]; + r3[ 2 ] = cospa*r1[ 2 ] + sinpa*r2[ 2 ]; + +/* Return */ + return; +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a SkyFrame and convert to the new coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the protected astSubFrame method +* inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the axes from +* a "target" SkyFrame and creates a new Frame with copies of the selected +* axes assembled in the requested order. It then optionally overlays the +* attributes of a "template" Frame on to the result. It returns both the +* resulting Frame and a Mapping that describes how to convert between the +* coordinate systems described by the target and result Frames. If +* necessary, this Mapping takes account of any differences in the Frames' +* attributes due to the influence of the template. + +* Parameters: +* target +* Pointer to the target SkyFrame, from which axes are to be selected. +* template +* Pointer to the template Frame, from which new attributes for the +* result Frame are to be obtained. Optionally, this may be NULL, in +* which case no overlaying of template attributes will be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This number may +* be greater than or less than the number of axes in this Frame (or +* equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving a list +* of the (zero-based) axis indices of the axes to be selected from the +* target SkyFrame. The order in which these are given determines the +* order in which the axes appear in the result Frame. If any of the +* values in this array is set to -1, the corresponding result axis will +* not be derived from the target Frame, but will be assigned default +* attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This should +* contain a list of the template axes (given as zero-based axis indices) +* with which the axes of the result Frame are to be associated. This +* array determines which axes are used when overlaying axis-dependent +* attributes of the template on to the result. If any element of this +* array is set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not used and +* a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned Mapping. +* The forward transformation of this Mapping will describe how to +* convert coordinates from the coordinate system described by the target +* SkyFrame to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is possible +* between the target and the result Frame. Otherwise zero is returned and +* *map and *result are returned as NULL (but this will not in itself +* result in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not always +* be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* - This implementation addresses the selection of axes from a SkyFrame +* object. This results in another object of the same class only if both +* axes of the SkyFrame are selected, once each. Otherwise, the result is a +* Frame class object which inherits the SkyFrame's axis information (if +* appropriate) but none of the other properties of a SkyFrame. +* - In the event that a SkyFrame results, the returned Mapping will take +* proper account of the relationship between the target and result sky +* coordinate systems. +* - In the event that a Frame class object results, the returned Mapping +* will only represent a selection/permutation of axes. + +* Implementation Deficiencies: +* - Any axis selection is currently permitted. Probably this should be +* restricted so that each axis can only be selected once. The +* astValidateAxisSelection method will do this but currently there are bugs +* in the CmpFrame class that cause axis selections which will not pass this +* test. Install the validation when these are fixed. +*/ + +/* Local Variables: */ + AstAxis *ax; /* Pointer to result Frame Axis object */ + AstMapping *tmpmap; /* Temporary Mapping pointer */ + AstPermMap *permmap; /* Pointer to PermMap */ + AstSkyFrame *target; /* Pointer to the SkyFrame structure */ + AstSkyFrame *temp; /* Pointer to copy of target SkyFrame */ + AstSystemType align_sys; /* System in which to align the SkyFrames */ + int match; /* Coordinate conversion is possible? */ + int perm[ 2 ]; /* Permutation array for axis swap */ + int result_swap; /* Swap result SkyFrame coordinates? */ + int set_usedefs; /* Set the returned UseDefs attribute zero?*/ + int target_axis; /* Target SkyFrame axis index */ + int target_swap; /* Swap target SkyFrame coordinates? */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the target SkyFrame structure. */ + target = (AstSkyFrame *) target_frame; + +/* Result is a SkyFrame. */ +/* --------------------- */ +/* Check if the result Frame is to have two axes obtained by selecting + both of the target SkyFrame axes, in either order. If so, the + result will also be a SkyFrame. */ + if ( ( result_naxes == 2 ) && + ( ( ( target_axes[ 0 ] == 0 ) && ( target_axes[ 1 ] == 1 ) ) || + ( ( target_axes[ 0 ] == 1 ) && ( target_axes[ 1 ] == 0 ) ) ) ) { + +/* If a template has not been supplied, or is the same object as the + target, we are simply extracting axes from the supplied SkyFrame. In + this case we temporarily force the UseDefs attribute to 1 so that (for + instance) the astPickAxes method can function correctly. E.g. if you + have a SkyFrame with no set Epoch and UseDefs set zero, and you try to + swap the axes, the attempt would fail because MakeSkyMapping would be + unable to determine the Mapping from original to swapped SkyFrame, + because of the lack of an Epoch value. */ + set_usedefs = 0; + if( !template || template == target_frame ) { + if( !astGetUseDefs( target ) ) { + astClearUseDefs( target ); + set_usedefs = 1; + } + } + +/* Form the result from a copy of the target and then permute its axes + into the order required. */ + *result = astCopy( target ); + astPermAxes( *result, target_axes ); + +/* If required, overlay the template attributes on to the result SkyFrame. + Also get the system in which to align the two SkyFrames. This is the + value of the AlignSystem attribute from the template (if there is a + template). */ + if ( template ) { + astOverlay( template, template_axes, *result ); + align_sys = astGetAlignSystem( template ); + + } else { + align_sys = astGetAlignSystem( target ); + } + +/* See whether alignment occurs in offset coordinates or absolute + coordinates. If the current call to this function is part of the + process of restoring a FrameSet's integrity following changes to + the FrameSet's current Frame, then we ignore the setting of the + AlignOffset attributes and use 0. This ensures that when the System + attribute (for instance) is changed via a FrameSet pointer, the + Mappings within the FrameSet are modified to produce offsets in the + new System. If we are not currently restoring a FrameSet's integrity, + then we align in offsets if the template is a SkyFrame and both template + and target want alignment to occur in the offset coordinate system. In + this case we use a UnitMap to connect them. */ + if( ( astGetFrameFlags( target_frame ) & AST__INTFLAG ) == 0 ) { + if( astGetAlignOffset( target ) && + astGetSkyRefIs( target ) != AST__IGNORED_REF && + template && astIsASkyFrame( template ) ){ + if( astGetAlignOffset( (AstSkyFrame *) template ) && + astGetSkyRefIs( (AstSkyFrame *) template ) != AST__IGNORED_REF ) { + match = 1; + *map = (AstMapping *) astUnitMap( 2, "", status ); + } + } + } + +/* Otherwise, generate a Mapping that takes account of changes in the sky + coordinate system (equinox, epoch, etc.) between the target SkyFrame and + the result SkyFrame. If this Mapping can be generated, set "match" to + indicate that coordinate conversion is possible. */ + if( ! *map ) { + match = ( MakeSkyMapping( target, (AstSkyFrame *) *result, + align_sys, map, status ) != 0 ); + } + +/* If required, re-instate the original zero value of UseDefs. */ + if( set_usedefs ) { + astSetUseDefs( target, 0 ); + astSetUseDefs( *result, 0 ); + } + +/* If a Mapping has been obtained, it will expect coordinate values to be + supplied in (longitude,latitude) pairs. Test whether we need to swap the + order of the target SkyFrame coordinates to conform with this. */ + if ( astOK && match ) { + target_swap = ( astValidateAxis( target, 0, 1, "astSubFrame" ) != 0 ); + +/* Coordinates will also be delivered in (longitude,latitude) pairs, so check + to see whether the result SkyFrame coordinate order should be swapped. */ + result_swap = ( target_swap != ( target_axes[ 0 ] != 0 ) ); + +/* If either set of coordinates needs swapping, create a PermMap that + will swap a pair of coordinates. */ + permmap = NULL; + if ( target_swap || result_swap ) { + perm[ 0 ] = 1; + perm[ 1 ] = 0; + permmap = astPermMap( 2, perm, 2, perm, NULL, "", status ); + } + +/* If necessary, prefix this PermMap to the main Mapping. */ + if ( target_swap ) { + tmpmap = (AstMapping *) astCmpMap( permmap, *map, 1, "", status ); + *map = astAnnul( *map ); + *map = tmpmap; + } + +/* Also, if necessary, append it to the main Mapping. */ + if ( result_swap ) { + tmpmap = (AstMapping *) astCmpMap( *map, permmap, 1, "", status ); + *map = astAnnul( *map ); + *map = tmpmap; + } + +/* Annul the pointer to the PermMap (if created). */ + if ( permmap ) permmap = astAnnul( permmap ); + } + +/* Result is not a SkyFrame. */ +/* ------------------------- */ +/* In this case, we select axes as if the target were from the Frame + class. However, since the resulting data will then be separated + from their enclosing SkyFrame, default attribute values may differ + if the methods for obtaining them were over-ridden by the SkyFrame + class. To overcome this, we ensure that these values are explicitly + set for the result Frame (rather than relying on their + defaults). */ + } else { + +/* Make a temporary copy of the target SkyFrame. We will explicitly + set the attribute values in this copy so as not to modify the + original. */ + temp = astCopy( target ); + +/* Define a macro to test if an attribute is set. If not, set it + explicitly to its default value. */ +#define SET(attribute) \ + if ( !astTest##attribute( temp ) ) { \ + astSet##attribute( temp, astGet##attribute( temp ) ); \ + } + +/* Set attribute values which apply to the Frame as a whole and which + we want to retain, but whose defaults are over-ridden by the + SkyFrame class. */ + SET(Domain) + SET(Title) + +/* Now loop to set explicit attribute values for each axis. */ + for ( target_axis = 0; target_axis < 2; target_axis++ ) { + +/* Define a macro to test if an axis attribute is set. If not, set it + explicitly to its default value. */ +#define SET_AXIS(attribute) \ + if ( !astTest##attribute( temp, target_axis ) ) { \ + astSet##attribute( temp, target_axis, \ + astGet##attribute( temp, target_axis ) ); \ + } + +/* Use this macro to set explicit values for all the axis attributes + for which the SkyFrame class over-rides the default value. */ + SET_AXIS(AsTime) + SET_AXIS(Format) + SET_AXIS(Label) + SET_AXIS(Symbol) + SET_AXIS(Unit) + +/* Now handle axis attributes for which there are no SkyFrame access + methods. For these we require a pointer to the temporary + SkyFrame's Axis object. */ + ax = astGetAxis( temp, target_axis ); + +/* Set an explicit value for the IsLatitude and CentreZero attributes. */ + if( astValidateAxis( temp, target_axis, 1, "astSubFrame" ) == 1 ) { + astSetAxisIsLatitude( ax, 1 ); + astSetAxisCentreZero( ax, 1 ); + + } else { + astSetAxisIsLatitude( ax, 0 ); + astSetAxisCentreZero( ax, astGetNegLon( temp ) ); + } + +/* Annul the Axis object pointer. */ + ax = astAnnul( ax ); + } + +/* Clear attributes which have an extended range of values allowed by + this class. */ + astClearSystem( temp ); + astClearAlignSystem( temp ); + +/* Invoke the astSubFrame method inherited from the Frame class to + produce the result Frame by selecting the required set of axes and + overlaying the template Frame's attributes. */ + match = (*parent_subframe)( (AstFrame *) temp, template, + result_naxes, target_axes, template_axes, + map, result, status ); + +/* Delete the temporary copy of the target SkyFrame. */ + temp = astDelete( temp ); + } + +/* Ensure the returned Frame does not have active units. */ + astSetActiveUnit( *result, 0 ); + +/* If an error occurred or no match was found, annul the returned + objects and reset the returned result. */ + if ( !astOK || !match ) { + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef SET +#undef SET_AXIS +} + +static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSystemCode method +* inherited from the Frame class). + +* Description: +* This function converts a string used for the external +* description of a sky coordinate system into a SkyFrame +* coordinate system type code (System attribute value). It is the +* inverse of the astSystemString function. + +* Parameters: +* this +* The Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the sky coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the sky coordinate +* system description was not recognised. This does not produce an +* error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" string against each possibility and assign the + result. */ + if ( astChrMatch( "FK4", system ) ) { + result = AST__FK4; + + } else if ( astChrMatch( "FK4_NO_E", system ) || + astChrMatch( "FK4-NO-E", system ) ) { + result = AST__FK4_NO_E; + + } else if ( astChrMatch( "FK5", system ) || + astChrMatch( "Equatorial", system ) ) { + result = AST__FK5; + + } else if ( astChrMatch( "J2000", system ) ) { + result = AST__J2000; + + } else if ( astChrMatch( "ICRS", system ) ) { + result = AST__ICRS; + + } else if ( astChrMatch( "AZEL", system ) ) { + result = AST__AZEL; + + } else if ( astChrMatch( "GAPPT", system ) || + astChrMatch( "GEOCENTRIC", system ) || + astChrMatch( "APPARENT", system ) ) { + result = AST__GAPPT; + + } else if ( astChrMatch( "ECLIPTIC", system ) ) { + result = AST__ECLIPTIC; + + } else if ( astChrMatch( "HELIOECLIPTIC", system ) ) { + result = AST__HELIOECLIPTIC; + + } else if ( astChrMatch( "GALACTIC", system ) ) { + result = AST__GALACTIC; + + } else if ( astChrMatch( "SUPERGALACTIC", system ) ) { + result = AST__SUPERGALACTIC; + + } else if ( astChrMatch( "UNKNOWN", system ) ) { + result = AST__UNKNOWN; + } + +/* Return the result. */ + return result; +} + +static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astSystemString method +* inherited from the Frame class). + +* Description: +* This function converts a SkyFrame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* The Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the sky coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. (Where possible, return the same string as would be + used in the FITS WCS representation of the coordinate system). */ + switch ( system ) { + case AST__FK4: + result = "FK4"; + break; + + case AST__FK4_NO_E: + result = "FK4-NO-E"; + break; + + case AST__FK5: + result = "FK5"; + break; + + case AST__J2000: + result = "J2000"; + break; + + case AST__ICRS: + result = "ICRS"; + break; + + case AST__GAPPT: + result = "GAPPT"; + break; + + case AST__AZEL: + result = "AZEL"; + break; + + case AST__ECLIPTIC: + result = "ECLIPTIC"; + break; + + case AST__HELIOECLIPTIC: + result = "HELIOECLIPTIC"; + break; + + case AST__GALACTIC: + result = "GALACTIC"; + break; + + case AST__SUPERGALACTIC: + result = "SUPERGALACTIC"; + break; + + case AST__UNKNOWN: + result = "Unknown"; + break; + } + +/* Return the result pointer. */ + return result; +} + +static int TestActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* TestActiveUnit + +* Purpose: +* Test the ActiveUnit flag for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astTestActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function test the value of the ActiveUnit flag for a SkyFrame, +* which is always "unset". + +* Parameters: +* this +* Pointer to the SkyFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The result of the test (0). + +*/ + return 0; +} + +static int TestAsTime( AstSkyFrame *this, int axis, int *status ) { +/* +* Name: +* TestAsTime + +* Purpose: +* Determine if a value has been set for a SkyFrame's AsTime attribute. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestAsTime( AstSkyFrame *this, int axis, int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function returns a boolean value to indicate if a value has +* previously been set for the AsTime attribute for a specified axis of a +* SkyFrame. This attribute indicates whether axis values should be +* formatted as times (as opposed to angles) by default. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* Index of the axis for which information is required (zero based). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero or one, according to whether the AsTime attribute has been set. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables. */ + AstAxis *ax; /* Pointer to Axis object */ + int result; /* Result to be returned */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astTestAsTime" ); + +/* Obtain a pointer to the Axis object. */ + ax = astGetAxis( this, axis ); + +/* Determine if the AsTime attribute has been set for it (it cannot have been + set unless the object is a SkyAxis). */ + result = ( astIsASkyAxis( ax ) && astTestAxisAsTime( ax ) ); + +/* Annul the Axis pointer. */ + ax = astAnnul( ax ); + +/* Return the result. */ + return result; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a SkyFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a SkyFrame's attributes. + +* Parameters: +* this +* Pointer to the SkyFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + int axis; /* SkyFrame axis number */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* AsTime(axis). */ +/* ------------- */ + if ( nc = 0, + ( 1 == astSscanf( attrib, "astime(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestAsTime( this, axis - 1 ); + +/* Equinox. */ +/* -------- */ + } else if ( !strcmp( attrib, "equinox" ) ) { + result = astTestEquinox( this ); + +/* NegLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "neglon" ) ) { + result = astTestNegLon( this ); + +/* SkyTol. */ +/* ------- */ + } else if ( !strcmp( attrib, "skytol" ) ) { + result = astTestSkyTol( this ); + +/* Projection. */ +/* ----------- */ + } else if ( !strcmp( attrib, "projection" ) ) { + result = astTestProjection( this ); + +/* SkyRefIs. */ +/* --------- */ + } else if ( !strcmp( attrib, "skyrefis" ) ) { + result = astTestSkyRefIs( this ); + +/* SkyRef. */ +/* ------- */ + } else if ( !strcmp( attrib, "skyref" ) ) { + result = astTestSkyRef( this, 0 ) || astTestSkyRef( this, 1 ); + +/* SkyRef(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyref(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestSkyRef( this, axis - 1 ); + +/* SkyRefP. */ +/* -------- */ + } else if ( !strcmp( attrib, "skyrefp" ) ) { + result = astTestSkyRefP( this, 0 ) || astTestSkyRefP( this, 1 ); + +/* SkyRefP(axis). */ +/* ------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, "skyrefp(%d)%n", &axis, &nc ) ) + && ( nc >= len ) ) { + result = astTestSkyRefP( this, axis - 1 ); + +/* AlignOffset */ +/* ----------- */ + } else if ( !strcmp( attrib, "alignoffset" ) ) { + result = astTestAlignOffset( this ); + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + } else if ( !strncmp( attrib, "islataxis", 9 ) || + !strncmp( attrib, "islonaxis", 9 ) || + !strcmp( attrib, "lataxis" ) || + !strcmp( attrib, "lonaxis" ) ) { + result = 0; + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int TestSlaUnit( AstSkyFrame *sf1, AstSkyFrame *sf2, AstSlaMap *slamap, + int *status ){ +/* +* Name: +* Unformat + +* Purpose: +* See if a slamap is effectively a unit mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int TestSlaUnit( AstSkyFrame *sf1, AstSkyFrame *sf2, AstSlaMap *slamap, +* int *status ) + +* Class Membership: +* SkyFrame member function. + +* Description: +* This function tests a SlaMap to see if it is effectively a unit +* transformatuon to within a tolerance given by the smaller tolerance +* of the two supplied SkyFrames. + +* Parameters: +* sf1 +* Pointer to the first SkyFrame. +* sf2 +* Pointer to the second SkyFrame (may be NULL) +* slamap +* Pointer to the SlaMap to test. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the SlaMap is effectively a unit mapping, and zero +* otherwise. + +*/ + +/* Number of test points. */ +#define NTEST 14 + +/* Local Variables: */ + double maxshift; /* Max. shift produced by slamap (rads) */ + double olat[NTEST]; /* Transformed latitudes */ + double olon[NTEST]; /* Transformed longitudes */ + double shift; /* Shift produced by slamap (rads) */ + double tol2; /* Second tolerance (in radians) */ + double tol; /* Used tolerance (in radians) */ + int i; /* Loop count */ + int result; /* Returned flag */ + +/* A grid of lon/lat points covering the sphere. */ + double lat[ NTEST ] = { 0.0, 0.0, 0.0, 0.0, + 0.8, 0.8, 0.8, 0.8, + -0.8, -0.8, -0.8, -0.8, + 1.570796, -1.570796 }; + double lon[ NTEST ] = { 0.0, 1.57, 3.14, 4.71, + 0.8, 2.37, 3.94, 5.51, + 0.8, 2.37, 3.94, 5.51, + 0.0, 0.0 }; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the SlaMap is empty (i.e. has no conversions in it), then is it a + UnitMap. So save time by not transforming the test values. */ + if( astSlaIsEmpty( slamap ) ) { + result = 1; + +/* Otherwise, get the smaller of the tolerances associated with the + supplied SkyFrames, in radians. */ + } else { + tol = astGetSkyTol( sf1 ); + if( sf2 ) { + tol2 = astGetSkyTol( sf2 ); + if( tol2 < tol ) tol = tol2; + } + +/* If the tolerance is zero, there is no need to do the test. */ + if( tol > 0.0 ) { + +/* Transform the test point using the SlaMap. */ + astTran2( slamap, NTEST, lon, lat, 1, olon, olat ); + +/* Find the maximum shift produced by the SlaMap at any of the test + positions. Again, to avoid the slow-down produced by checking for + axis permutation, use palDsep rather than astDistance. */ + maxshift = 0.0; + for( i = 0; i < NTEST; i++ ) { + shift = palDsep( lon[ i ], lat[ i ], olon[ i ], olat[ i ] ); + if( shift > maxshift ) maxshift = shift; + } + +/* Convert the max shift to arc-seconds and do the check. */ + result = ( maxshift*AST__DR2D*3600 < tol ); + } + } + + return result; +} + +static int Unformat( AstFrame *this_frame, int axis, const char *string, + double *value, int *status ) { +/* +* Name: +* Unformat + +* Purpose: +* Read a formatted coordinate value for a SkyFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* int Unformat( AstFrame *this, int axis, const char *string, +* double *value, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the public astUnformat +* method inherited from the Frame class). + +* Description: +* This function reads a formatted coordinate value for a SkyFrame +* axis (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the SkyFrame. +* axis +* The number of the SkyFrame axis for which the coordinate +* value is to be read (axis numbering starts at zero for the +* first axis). +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will +* be returned (in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + double coord; /* Coordinate value read */ + int format_set; /* Format attribute set? */ + int nc; /* Number of characters read */ + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astUnformat" ); + +/* Determine if a Format value has been set for the axis and set a + temporary value if it has not. Use the GetFormat member function + for this class together with member functions inherited from the + parent class (rather than using the object's methods directly) + because if any of these methods have been over-ridden by a derived + class the Format string syntax may no longer be compatible with + this class. */ + format_set = (*parent_testformat)( this_frame, axis, status ); + if ( !format_set ) { + (*parent_setformat)( this_frame, axis, GetFormat( this_frame, axis, status ), status ); + } + +/* Use the Unformat member function inherited from the parent class to + read the coordinate value. */ + nc = (*parent_unformat)( this_frame, axis, string, &coord, status ); + +/* If necessary, clear any temporary Format value that was set above. */ + if ( !format_set ) (*parent_clearformat)( this_frame, axis, status ); + +/* If an error occurred, clear the number of characters read. */ + if ( !astOK ) { + nc = 0; + +/* Otherwise, if characters were read, return the coordinate value. */ + } else if ( nc ) { + *value = coord; + } + +/* Return the number of characters read. */ + return nc; +} + +static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) { +/* +* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* SkyFrame member function (over-rides the astValidateSystem method +* inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST__BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the value is out of bounds, report an error. */ + if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) { + astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System " + "or AlignSystem attribute of a %s.", status, method, + astGetClass( this ), (int) system, astGetClass( this ) ); + +/* Otherwise, return the supplied value. */ + } else { + result = system; + } + +/* Return the result. */ + return result; +} + +static void VerifyMSMAttrs( AstSkyFrame *target, AstSkyFrame *result, + int which, const char *attrs, const char *method, int *status ) { +/* +* Name: +* VerifyMSMAttrs + +* Purpose: +* Verify that usable attribute values are available. + +* Type: +* Private function. + +* Synopsis: +* #include "skyframe.h" +* void VerifyMSMAttrs( AstSkyFrame *target, AstSkyFrame *result, +* int which, const char *attrs, const char *method, int *status ) + +* Class Membership: +* SkyFrame member function + +* Description: +* This function tests each attribute listed in "attrs". It returns +* without action if 1) an explicit value has been set for each attribute +* in the SkyFrame indicated by "which" or 2) the UseDefs attribute of the +* "which" SkyFrame is non-zero. +* +* If UseDefs is zero (indicating that default values should not be +* used for attributes), and any of the named attributes does not have +* an explicitly set value, then an error is reported. +* +* The displayed error message assumes that tjis function was called +* as part of the process of producing a Mapping from "target" to "result". + +* Parameters: +* target +* Pointer to the target SkyFrame. +* result +* Pointer to the result SkyFrame. +* which +* If 2, both the target and result SkyFrames are checked for the +* supplied attributes. If less than 2, only the target SkyFrame is +* checked. If greater than 2, only the result SkyFrame is checked. +* attrs +* A string holding a space separated list of attribute names. +* method +* A string holding the name of the calling method for use in error +* messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + const char *a; + const char *p; + const char *desc; + int len; + int set1; + int set2; + int state; + int usedef1; + int usedef2; + +/* Check inherited status */ + if( !astOK ) return; + +/* Get the UseDefs attributes of the two SkyFrames. */ + usedef1 = astGetUseDefs( target ); + usedef2 = astGetUseDefs( result ); + +/* If both SkyFrames have a non-zero value for its UseDefs attribute, then + all attributes are assumed to have usable values, since the defaults + will be used if no explicit value has been set. So we only need to do + any checks if UseDefs is zero for either SkyFrame. */ + if( !usedef1 || !usedef2 ) { + +/* Stop compiler warnings about uninitialised variables */ + a = NULL; + desc = NULL; + len = 0; + set1 = 0; + set2 = 0; + +/* Loop round the "attrs" string identifying the start and length of each + non-blank word in the string. */ + state = 0; + p = attrs; + while( 1 ) { + if( state == 0 ) { + if( !isspace( *p ) ) { + a = p; + len = 1; + state = 1; + } + } else { + if( isspace( *p ) || !*p ) { + +/* The end of a word has just been reached. Compare it to each known + attribute value. Get a flag indicating if the attribute has a set + value, and a string describing the attribute.*/ + if( len > 0 ) { + + if( !strncmp( "Equinox", a, len ) ) { + set1 = astTestEquinox( target ); + set2 = astTestEquinox( result ); + desc = "reference equinox"; + + } else if( !strncmp( "Dut1", a, len ) ) { + set1 = astTestDut1( target ); + set2 = astTestDut1( result ); + desc = "UT1-UTC correction"; + + } else if( !strncmp( "Epoch", a, len ) ) { + set1 = astTestEpoch( target ); + set2 = astTestEpoch( result ); + desc = "epoch of observation"; + + } else if( !strncmp( "ObsLon", a, len ) ) { + set1 = astTestObsLon( target ); + set2 = astTestObsLon( result ); + desc = "longitude of observer"; + + } else if( !strncmp( "ObsLat", a, len ) ) { + set1 = astTestObsLat( target ); + set2 = astTestObsLat( result ); + desc = "latitude of observer"; + + } else if( !strncmp( "ObsAlt", a, len ) ) { + set1 = astTestObsAlt( target ); + set2 = astTestObsAlt( result ); + desc = "altitude of observer"; + + } else { + astError( AST__INTER, "VerifyMSMAttrs(SkyFrame): " + "Unknown attribute name \"%.*s\" supplied (AST " + "internal programming error).", status, len, a ); + } + +/* If the attribute is not set in the target but should be, report an + error. */ + if( !usedef1 && !set1 && which < 3 ) { + astClearTitle( target ); + astClearTitle( result ); + astError( AST__NOVAL, "%s(%s): Cannot convert " + "celestial coordinates from %s to %s.", status, + method, astGetClass( target ), + astGetC( target, "Title" ), + astGetC( result, "Title" ) ); + astError( AST__NOVAL, "No value has been set for " + "the \"%.*s\" attribute (%s) in the input %s.", status, + len, a, desc, astGetClass( target ) ); + break; + } + +/* If the attribute is not set in the result but should be, report an + error. */ + if( !usedef2 && !set2 && which > 1 ) { + astClearTitle( target ); + astClearTitle( result ); + astError( AST__NOVAL, "%s(%s): Cannot convert " + "celestial coordinates from %s to %s.", status, + method, astGetClass( result ), + astGetC( target, "Title" ), + astGetC( result, "Title" ) ); + astError( AST__NOVAL, "No value has been set for " + "the \"%.*s\" attribute (%s) in the output %s.", status, + len, a, desc, astGetClass( result ) ); + break; + } + +/* Continue the word search algorithm. */ + } + len = 0; + state = 0; + } else { + len++; + } + } + if( !*(p++) ) break; + } + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* +*att++ +* Name: +* AlignOffset + +* Purpose: +* Align SkyFrames using the offset coordinate system? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how a SkyFrame +* behaves when it is used (by +c astFindFrame or astConvert) as a template to match another (target) +f AST_FINDFRAME or AST_CONVERT) as a template to match another (target) +* SkyFrame. It determines the coordinate system in which the two +* SkyFrames are aligned if a match occurs. +* +* If the template and target SkyFrames both have defined offset coordinate +* systems (i.e. the SkyRefIs attribute is set to either "Origin" or " +* Pole"), and they both have a non-zero value for AlignOffset, then +* alignment occurs within the offset coordinate systems (that is, a +* UnitMap will always be used to align the two SkyFrames). If either +* the template or target SkyFrame has zero (the default value) for +* AlignOffset, or if either SkyFrame has SkyRefIs set to "Ignored", then +* alignment occurring within the coordinate system specified by the +* AlignSystem attribute. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +astMAKE_CLEAR(SkyFrame,AlignOffset,alignoffset,-INT_MAX) +astMAKE_GET(SkyFrame,AlignOffset,int,0,( ( this->alignoffset != -INT_MAX ) ? + this->alignoffset : 0 )) +astMAKE_SET(SkyFrame,AlignOffset,int,alignoffset,( value != 0 )) +astMAKE_TEST(SkyFrame,AlignOffset,( this->alignoffset != -INT_MAX )) + +/* +*att++ +* Name: +* AsTime(axis) + +* Purpose: +* Format celestal coordinates as times? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute specifies the default style of formatting to be +c used (e.g. by astFormat) for the celestial coordinate values +f used (e.g. by AST_FORMAT) for the celestial coordinate values +* described by a SkyFrame. It takes a separate boolean value for +* each SkyFrame axis so that, for instance, the setting +* "AsTime(2)=0" specifies the default formatting style for +* celestial latitude values. +* +* If the AsTime attribute for a SkyFrame axis is zero, then +* coordinates on that axis will be formatted as angles by default +* (using degrees, minutes and seconds), otherwise they will be +* formatted as times (using hours, minutes and seconds). +* +* The default value of AsTime is chosen according to the sky +* coordinate system being represented, as determined by the +* SkyFrame's System attribute. This ensures, for example, that +* right ascension values will be formatted as times by default, +* following normal conventions. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - The AsTime attribute operates by changing the default value of +* the corresponding Format(axis) attribute. This, in turn, may +* also affect the value of the Unit(axis) attribute. +* - Only the default style of formatting is affected by the AsTime +* value. If an explicit Format(axis) value is set, it will +* over-ride any effect from the AsTime attribute. +*att-- +*/ + +/* +*att++ +* Name: +* Equinox + +* Purpose: +* Epoch of the mean equinox. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute is used to qualify those celestial coordinate +* systems described by a SkyFrame which are notionally based on +* the ecliptic (the plane of the Earth's orbit around the Sun) +* and/or the Earth's equator. +* +* Both of these planes are in motion and their positions are +* difficult to specify precisely. In practice, therefore, a model +* ecliptic and/or equator are used instead. These, together with +* the point on the sky that defines the coordinate origin (the +* intersection of the two planes termed the "mean equinox") move +* with time according to some model which removes the more rapid +* fluctuations. The SkyFrame class supports both the FK4 and +* FK5 models. +* +* The position of a fixed source expressed in any of these +* coordinate systems will appear to change with time due to +* movement of the coordinate system itself (rather than motion of +* the source). Such coordinate systems must therefore be +* qualified by a moment in time (the "epoch of the mean equinox" +* or "equinox" for short) which allows the position of the model +* coordinate system on the sky to be determined. This is the role +* of the Equinox attribute. +* +* The Equinox attribute is stored as a Modified Julian Date, but +* when setting or getting its value you may use the same formats +* as for the Epoch attribute (q.v.). +* +* The default Equinox value is B1950.0 (Besselian) for the old +* FK4-based coordinate systems (see the System attribute) and +* J2000.0 (Julian) for all others. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - Care must be taken to distinguish the Equinox value, which +* relates to the definition of a time-dependent coordinate system +* (based on solar system reference planes which are in motion), +* from the superficially similar Epoch value. The latter is used +* to qualify coordinate systems where the positions of sources +* change with time (or appear to do so) for a variety of other +* reasons, such as aberration of light caused by the observer's +* motion, etc. +* - See the description of the System attribute for details of +* which qualifying attributes apply to each celestial coordinate +* system. +*att-- +*/ +/* Clear the Equinox value by setting it to AST__BAD. */ +astMAKE_CLEAR(SkyFrame,Equinox,equinox,AST__BAD) + +/* Provide a default value of B1950.0 or J2000.0 depending on the System + setting. */ +astMAKE_GET(SkyFrame,Equinox,double,AST__BAD,( + ( this->equinox != AST__BAD ) ? this->equinox : + ( ( ( astGetSystem( this ) == AST__FK4 ) || + ( astGetSystem( this ) == AST__FK4_NO_E ) ) ? + palEpb2d( 1950.0 ) : palEpj2d( 2000.0 ) ) )) + +/* Allow any Equinox value to be set, unless the System is Helio-ecliptic + (in which case clear the value so that J2000 is used). */ +astMAKE_SET(SkyFrame,Equinox,double,equinox,((astGetSystem(this)!=AST__HELIOECLIPTIC)?value:AST__BAD)) + +/* An Equinox value is set if it is not equal to AST__BAD. */ +astMAKE_TEST(SkyFrame,Equinox,( this->equinox != AST__BAD )) + + +/* +*att++ +* Name: +* IsLatAxis(axis) + +* Purpose: +* Is the specified celestial axis a latitude axis? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This is a read-only boolean attribute that indicates the nature of +* the specified axis. The attribute has a non-zero value if the +* specified axis is a celestial latitude axis (Declination, Galactic +* latitude, etc), and is zero otherwise. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the SkyFrame axis to be tested. +*att-- +*/ + +/* +*att++ +* Name: +* IsLonAxis(axis) + +* Purpose: +* Is the specified celestial axis a longitude axis? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean), read-only. + +* Description: +* This is a read-only boolean attribute that indicates the nature of +* the specified axis. The attribute has a non-zero value if the +* specified axis is a celestial longitude axis (Right Ascension, Galactic +* longitude, etc), and is zero otherwise. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - When specifying this attribute by name, it should be +* subscripted with the number of the SkyFrame axis to be tested. +*att-- +*/ + +/* +*att++ +* Name: +* LatAxis + +* Purpose: +* Index of the latitude axis. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This read-only attribute gives the index (1 or 2) of the latitude +* axis within the SkyFrame (taking into account any current axis +* permutations). + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* LonAxis + +* Purpose: +* Index of the longitude axis. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This read-only attribute gives the index (1 or 2) of the longitude +* axis within the SkyFrame (taking into account any current axis +* permutations). + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* NegLon + +* Purpose: +* Display negative longitude values? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how longitude values +c are normalized for display by astNorm. +f are normalized for display by AST_NORM. +* +* If the NegLon attribute is zero, then normalized +* longitude values will be in the range zero to 2.pi. If NegLon is +* non-zero, then normalized longitude values will be in the range -pi +* to pi. +* +* The default value depends on the current value of the SkyRefIs +* attribute, If SkyRefIs has a value of "Origin", then the default for +* NegLon is one, otherwise the default is zero. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +/* Clear the NegLon value by setting it to -INT_MAX. */ +astMAKE_CLEAR(SkyFrame,NegLon,neglon,-INT_MAX) + +/* Supply a default of 0 for absolute coords and 1 for offset coords if + no NegLon value has been set. */ +astMAKE_GET(SkyFrame,NegLon,int,0,( ( this->neglon != -INT_MAX ) ? +this->neglon : (( astGetSkyRefIs( this ) == AST__ORIGIN_REF )? 1 : 0))) + +/* Set a NegLon value of 1 if any non-zero value is supplied. */ +astMAKE_SET(SkyFrame,NegLon,int,neglon,( value != 0 )) + +/* The NegLon value is set if it is not -INT_MAX. */ +astMAKE_TEST(SkyFrame,NegLon,( this->neglon != -INT_MAX )) + +/* +*att++ +* Name: +* SkyTol + +* Purpose: +* The smallest significant shift in sky coordinates. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute indicates the accuracy of the axis values that will +* be represented by the SkyFrame. If the arc-distance between two +* positions within the SkyFrame is smaller than the value of SkyTol, +* then the two positions will (for the puposes indicated below) be +* considered to be co-incident. +* +* This value is used only when constructing the Mapping between +* two different SkyFrames (for instance, when calling +c astConvert or astFindFrame). +f AST_CONVERT or AST_FINDFRAME). +* If the transformation between the two SkyFrames causes positions to +* shift by less than SkyTol arc-seconds, then the transformation is +* replaced by a UnitMap. This could in certain circumatances allow +* major simplifications to be made to the transformation between +* any pixel grids associated with the two SkyFrames (for instance, if +* each SkyFrame is part of the WCS FrameSet associated with an image). +* +* A common case is when two SkyFrames use the FK5 system, but have +* slightly different Epoch values. If the AlignSystem attribute has +* its default value of "ICRS", then the transformation between the +* two SkyFrames will include a very small rotation (FK5 rotates with +* respect to ICRS as a rate of about 0.0005 arc-seconds per year). In +* most circumstances such a small rotation is insignificant. Setting +* SkyTol to some suitably small non-zero value will cause this +* rotation to be ignored, allowing much simpler transformations to +* be used. +* +* The test to determine the shift introduced by transforming between +* the two SkyFrames is performed by transforming a set of 14 position +* spread evenly over the whole sky. The largest shift produced at any +* of these 14 positions is compared to the value of SkyTol. +* +* The SkyTol value is in units of arc-seconds, and the default value +* is 0.001. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +astMAKE_CLEAR(SkyFrame,SkyTol,skytol,AST__BAD) +astMAKE_GET(SkyFrame,SkyTol,double,0.001,((this->skytol!=AST__BAD)?this->skytol:0.001)) +astMAKE_SET(SkyFrame,SkyTol,double,skytol,fabs(value)) +astMAKE_TEST(SkyFrame,SkyTol,(this->skytol!=AST__BAD)) + +/* +*att++ +* Name: +* Projection + +* Purpose: +* Sky projection description. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute provides a place to store a description of the +* type of sky projection used when a SkyFrame is attached to a +* 2-dimensional object, such as an image or plotting surface. For +* example, typical values might be "orthographic", "Hammer-Aitoff" +* or "cylindrical equal area". +* +* The Projection value is purely descriptive and does not affect +* the celestial coordinate system represented by the SkyFrame in +* any way. If it is set to a non-blank string, the description +* provided may be used when forming the default value for the +* SkyFrame's Title attribute (so that typically it will appear in +* graphical output, for instance). The default value is an empty +* string. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. +*att-- +*/ +/* Clear the Projection value by freeing the allocated memory and + assigning a NULL pointer. */ +astMAKE_CLEAR(SkyFrame,Projection,projection,astFree( this->projection )) + +/* If the Projection value is not set, return a pointer to an empty + string. */ +astMAKE_GET(SkyFrame,Projection,const char *,NULL,( this->projection ? + this->projection : "" )) + +/* Set a Projection value by freeing any previously allocated memory, + allocating new memory, storing the string and saving the pointer to + the copy. */ +astMAKE_SET(SkyFrame,Projection,const char *,projection,astStore( + this->projection, value, strlen( value ) + (size_t) 1 )) + +/* The Projection value is set if the pointer to it is not NULL. */ +astMAKE_TEST(SkyFrame,Projection,( this->projection != NULL )) + +/* +*att++ +* Name: +* SkyRefIs + +* Purpose: +* Selects the nature of the offset coordinate system. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls how the values supplied for the SkyRef and +* SkyRefP attributes are used. These three attributes together allow +* a SkyFrame to represent offsets relative to some specified origin +* or pole within the coordinate system specified by the System attribute, +* rather than absolute axis values. SkyRefIs can take one of the +* case-insensitive values "Origin", "Pole" or "Ignored". +* +* If SkyRefIs is set to "Origin", then the coordinate system +* represented by the SkyFrame is modified to put the origin of longitude +* and latitude at the position specified by the SkyRef attribute. +* +* If SkyRefIs is set to "Pole", then the coordinate system represented +* by the SkyFrame is modified to put the north pole at the position +* specified by the SkyRef attribute. +* +* If SkyRefIs is set to "Ignored" (the default), then any value set for the +* SkyRef attribute is ignored, and the SkyFrame represents the coordinate +* system specified by the System attribute directly without any rotation. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +*att-- +*/ +astMAKE_CLEAR(SkyFrame,SkyRefIs,skyrefis,AST__BAD_REF) +astMAKE_SET(SkyFrame,SkyRefIs,int,skyrefis,value) +astMAKE_TEST(SkyFrame,SkyRefIs,( this->skyrefis != AST__BAD_REF )) +astMAKE_GET(SkyFrame,SkyRefIs,int,AST__IGNORED_REF,(this->skyrefis == AST__BAD_REF ? AST__IGNORED_REF : this->skyrefis)) + +/* +*att++ +* Name: +* SkyRef(axis) + +* Purpose: +* Position defining the offset coordinate system. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute allows a SkyFrame to represent offsets, rather than +* absolute axis values, within the coordinate system specified by the +* System attribute. If supplied, SkyRef should be set to hold the +* longitude and latitude of a point within the coordinate system +* specified by the System attribute. The coordinate system represented +* by the SkyFrame will then be rotated in order to put the specified +* position at either the pole or the origin of the new coordinate system +* (as indicated by the SkyRefIs attribute). The orientation of the +* modified coordinate system is then controlled using the SkyRefP +* attribute. +* +* If an integer axis index is included in the attribute name (e.g. +* "SkyRef(1)") then the attribute value should be supplied as a single +* floating point axis value, in radians, when setting a value for the +* attribute, and will be returned in the same form when getting the value +* of the attribute. In this case the integer axis index should be "1" +* or "2" (the values to use for longitude and latitude axes are +* given by the LonAxis and LatAxis attributes). +* +* If no axis index is included in the attribute name (e.g. "SkyRef") then +* the attribute value should be supplied as a character string +* containing two formatted axis values (an axis 1 value followed by a +* comma, followed by an axis 2 value). The same form +* will be used when getting the value of the attribute. +* +* The default values for SkyRef are zero longitude and zero latitude. + +* Aligning SkyFrames with Offset Coordinate Systems: +* The offset coordinate system within a SkyFrame should normally be +* considered as a superficial "re-badging" of the axes of the coordinate +* system specified by the System attribute - it merely provides an +* alternative numerical "label" for each position in the System coordinate +* system. The SkyFrame retains full knowledge of the celestial coordinate +* system on which the offset coordinate system is based (given by the +* System attribute). For instance, the SkyFrame retains knowledge of the +* way that one celestial coordinate system may "drift" with respect to +* another over time. Normally, if you attempt to align two SkyFrames (e.g. +f using the AST_CONVERT or AST_FINDFRAME routine), +c using the astConvert or astFindFrame routine), +* the effect of any offset coordinate system defined in either SkyFrame +* will be removed, resulting in alignment being performed in the +* celestial coordinate system given by the AlignSystem attribute. +* However, by setting the AlignOffset attribute ot a non-zero value, it +* is possible to change this behaviour so that the effect of the offset +* coordinate system is not removed when aligning two SkyFrames. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - If the System attribute of the SkyFrame is changed, any position +* given for SkyRef is transformed into the new System. +* - If a value has been assigned to SkyRef attribute, then +* the default values for certain attributes are changed as follows: +* the default axis Labels for the SkyFrame are modified by appending +* " offset" to the end, the default axis Symbols for the SkyFrame are +* modified by prepending the character "D" to the start, and the +* default title is modified by replacing the projection information by the +* origin information. + +*att-- +*/ +MAKE_CLEAR(SkyRef,skyref,AST__BAD,2) +MAKE_SET(SkyRef,double,skyref,value,2) +MAKE_TEST(SkyRef,( this->skyref[axis_p] != AST__BAD ),2) +MAKE_GET(SkyRef,double,0.0,((this->skyref[axis_p]!=AST__BAD)?this->skyref[axis_p]:0.0),2) + +/* +*att++ +* Name: +* SkyRefP(axis) + +* Purpose: +* Position on primary meridian of offset coordinate system. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute is used to control the orientation of the offset +* coordinate system defined by attributes SkyRef and SkyRefIs. If used, +* it should be set to hold the longitude and latitude of a point within +* the coordinate system specified by the System attribute. The offset +* coordinate system represented by the SkyFrame will then be rotated in +* order to put the position supplied for SkyRefP on the zero longitude +* meridian. This rotation is about an axis from the centre of the +* celestial sphere to the point specified by the SkyRef attribute. +* The default value for SkyRefP is usually the north pole (that is, a +* latitude of +90 degrees in the coordinate system specified by the System +* attribute). The exception to this is if the SkyRef attribute is +* itself set to either the north or south pole. In these cases the +* default for SkyRefP is the origin (that is, a (0,0) in the coordinate +* system specified by the System attribute). +* +* If an integer axis index is included in the attribute name (e.g. +* "SkyRefP(1)") then the attribute value should be supplied as a single +* floating point axis value, in radians, when setting a value for the +* attribute, and will be returned in the same form when getting the value +* of the attribute. In this case the integer axis index should be "1" +* or "2" (the values to use for longitude and latitude axes are +* given by the LonAxis and LatAxis attributes). +* +* If no axis index is included in the attribute name (e.g. "SkyRefP") then +* the attribute value should be supplied as a character string +* containing two formatted axis values (an axis 1 value followed by a +* comma, followed by an axis 2 value). The same form +* will be used when getting the value of the attribute. + +* Applicability: +* SkyFrame +* All SkyFrames have this attribute. + +* Notes: +* - If the position given by the SkyRef attribute defines the origin +* of the offset coordinate system (that is, if the SkyRefIs attribute +* is set to "origin"), then there will in general be two orientations +* which will put the supplied SkyRefP position on the zero longitude +* meridian. The orientation which is actually used is the one which +* gives the SkyRefP position a positive latitude in the offset coordinate +* system (the other possible orientation would give the SkyRefP position +* a negative latitude). +* - An error will be reported if an attempt is made to use a +* SkyRefP value which is co-incident with SkyRef or with the point +* diametrically opposite to SkyRef on the celestial sphere. The +* reporting of this error is deferred until the SkyRef and SkyRefP +* attribute values are used within a calculation. +* - If the System attribute of the SkyFrame is changed, any position +* given for SkyRefP is transformed into the new System. + +*att-- +*/ +MAKE_CLEAR(SkyRefP,skyrefp,AST__BAD,2) +MAKE_SET(SkyRefP,double,skyrefp,value,2) +MAKE_TEST(SkyRefP,( this->skyrefp[axis_p] != AST__BAD ),2) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SkyFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SkyFrame objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstSkyFrame *in; /* Pointer to input SkyFrame */ + AstSkyFrame *out; /* Pointer to output SkyFrame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SkyFrames. */ + in = (AstSkyFrame *) objin; + out = (AstSkyFrame *) objout; + +/* For safety, first clear any references to the input memory from + the output SkyFrame. */ + out->projection = NULL; + +/* If necessary, allocate memory in the output SkyFrame and store a + copy of the input Projection string. */ + if ( in->projection ) out->projection = astStore( NULL, in->projection, + strlen( in->projection ) + (size_t) 1 ); + +/* If an error occurred, free any allocated memory. */ + if ( !astOK ) { + out->projection = astFree( out->projection ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SkyFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SkyFrame objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to SkyFrame */ + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) obj; + +/* Free the memory used for the Projection string if necessary. */ + this->projection = astFree( this->projection ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SkyFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SkyFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the SkyFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSkyFrame *this; /* Pointer to the SkyFrame structure */ + AstSystemType system; /* System attribute value */ + char buf[ 100 ]; /* Comment buffer */ + char key[ 10 ]; /* Buffer for keywords */ + const char *sval; /* Pointer to string value */ + const int *perm; /* Pointer to axis permutation array */ + double dval; /* Double value */ + int bessyr; /* Use a Besselian year value ?*/ + int helpful; /* Helpful to display un-set value? */ + int invperm[ 2 ]; /* Inverse permutation array */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + int axis; /* External (i.e. permuted) zero-based axis index */ + int axis_p; /* Internal zero-based axis index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SkyFrame structure. */ + this = (AstSkyFrame *) this_object; + +/* Get a pointer to the SkyFrame's axis permutation array (using a method, + to allow for any over-ride by a derived class). */ + perm = astGetPerm( this ); + +/* Generate an inverse axis permutation array from the forward permutation + values. This will be used to determine which axis should be enquired + about (using possibly over-ridden methods) to obtain data to + correspond with a particular internal value (i.e. instance variable) + relating to an axis. This step is needed so that the effect of any + axis permutation can be un-done before values are written out, as + output values are written by this function in un-permuted order. */ + for ( axis = 0; axis < 2; axis++ ) invperm[ perm[ axis ] ] = axis; + +/* Write out values representing the instance variables for the + SkyFrame class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Projection. */ +/* ----------- */ + set = TestProjection( this, status ); + sval = set ? GetProjection( this, status ) : astGetProjection( this ); + astWriteString( channel, "Proj", set, 0, sval, + "Description of sky projection" ); + +/* NegLon. */ +/* ------- */ + set = TestNegLon( this, status ); + ival = set ? GetNegLon( this, status ) : astGetNegLon( this ); + astWriteInt( channel, "NegLon", set, 0, ival, + ival ? "Display negative longitude values" : + "Display positive longitude values" ); + +/* SkyTol. */ +/* ------- */ + set = TestSkyTol( this, status ); + dval = set ? GetSkyTol( this, status ) : astGetSkyTol( this ); + astWriteDouble( channel, "SkyTol", set, 1, dval, + "Smallest significant separation [arc-sec]"); + +/* Equinox. */ +/* -------- */ + set = TestEquinox( this, status ); + dval = set ? GetEquinox( this, status ) : astGetEquinox( this ); + +/* Decide whether the Equinox value is relevant to the current + coordinate system. */ + system = astGetSystem( this ); + helpful = ( ( system == AST__FK4 ) || + ( system == AST__FK4_NO_E ) || + ( system == AST__FK5 ) || + ( system == AST__ECLIPTIC ) ); + +/* Convert MJD to Besselian or Julian years, depending on the value. */ + bessyr = ( dval < palEpj2d( 1984.0 ) ); + dval = bessyr ? palEpb( dval ) : palEpj( dval ); + astWriteDouble( channel, "Eqnox", set, helpful, dval, + bessyr ? "Besselian epoch of mean equinox" : + "Julian epoch of mean equinox" ); + +/* SkyRefIs. */ +/* --------- */ + set = TestSkyRefIs( this, status ); + ival = set ? GetSkyRefIs( this, status ) : astGetSkyRefIs( this ); + if( ival == AST__POLE_REF ) { + astWriteString( channel, "SRefIs", set, 0, POLE_STRING, + "Rotated to put pole at ref. pos." ); + + } else if( ival == AST__IGNORED_REF ) { + astWriteString( channel, "SRefIs", set, 0, IGNORED_STRING, + "Not rotated (ref. pos. is ignored)" ); + + } else { + astWriteString( channel, "SRefIs", set, 0, ORIGIN_STRING, + "Rotated to put origin at ref. pos." ); + } + +/* SkyRef. */ +/* ------- */ +/* The inverse axis permutation array is used to obtain the axis index + to use when accessing the SkyRef attribute. This reverses the effect + of the SkyFrame's axis permutation array and yields a value appropriate + to the axis with internal index "axis". */ + for ( axis_p = 0; axis_p < 2; axis_p++ ) { + axis = invperm[ axis_p ]; + + set = TestSkyRef( this, axis, status ); + dval = set ? GetSkyRef( this, axis, status ) : astGetSkyRef( this, axis ); + sprintf( buf, "Ref. pos. %s %s", astGetSymbol( this, axis ), + astFormat( this, axis, dval ) ); + sprintf( key, "SRef%d", axis_p + 1 ); + astWriteDouble( channel, key, set, 0, dval, buf ); + } + +/* SkyRefP. */ +/* -------- */ + for ( axis_p = 0; axis_p < 2; axis_p++ ) { + axis = invperm[ axis_p ]; + + set = TestSkyRefP( this, axis, status ); + dval = set ? GetSkyRefP( this, axis, status ) : astGetSkyRefP( this, axis ); + sprintf( buf, "Ref. north %s %s", astGetSymbol( this, axis ), + astFormat( this, axis, dval ) ); + sprintf( key, "SRefP%d", axis_p + 1 ); + astWriteDouble( channel, key, set, 0, dval, buf ); + } + +/* AlignOffset. */ +/* ------------ */ + set = TestAlignOffset( this, status ); + ival = set ? GetAlignOffset( this, status ) : astGetAlignOffset( this ); + astWriteInt( channel, "AlOff", set, 0, ival, + ival ? "Align in offset coords" : + "Align in system coords" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASkyFrame and astCheckSkyFrame functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SkyFrame,Frame) +astMAKE_CHECK(SkyFrame) + +AstSkyFrame *astSkyFrame_( const char *options, int *status, ...) { +/* +*+ +* Name: +* astSkyFrame + +* Purpose: +* Create a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* AstSkyFrame *astSkyFrame( const char *options, int *status, ... ) + +* Class Membership: +* SkyFrame constructor. + +* Description: +* This function creates a new SkyFrame and optionally initialises its +* attributes. + +* Parameters: +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new SkyFrame. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new SkyFrame. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic SkyFrame constructor which +* is available via the protected interface to the SkyFrame class. +* A public interface is provided by the astSkyFrameId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyFrame *new; /* Pointer to new SkyFrame */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SkyFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSkyFrame( NULL, sizeof( AstSkyFrame ), !class_init, &class_vtab, + "SkyFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SkyFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SkyFrame. */ + return new; +} + +AstSkyFrame *astInitSkyFrame_( void *mem, size_t size, int init, + AstSkyFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSkyFrame + +* Purpose: +* Initialise a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* AstSkyFrame *astInitSkyFrame( void *mem, size_t size, int init, +* AstFrameVtab *vtab, const char *name ) + +* Class Membership: +* SkyFrame initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SkyFrame object. It allocates memory (if necessary) to accommodate +* the SkyFrame plus any additional data associated with the derived class. +* It then initialises a SkyFrame structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a SkyFrame at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SkyFrame is to be created. This +* must be of sufficient size to accommodate the SkyFrame data +* (sizeof(SkyFrame)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the SkyFrame (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the SkyFrame +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the SkyFrame's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SkyFrame. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). + +* Returned Value: +* A pointer to the new SkyFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSkyAxis *ax; /* Pointer to SkyAxis object */ + AstSkyFrame *new; /* Pointer to the new SkyFrame */ + int axis; /* Loop counter for axes */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSkyFrameVtab( vtab, name ); + +/* Initialise a Frame structure (the parent class) as the first component + within the SkyFrame structure, allocating memory if necessary. */ + new = (AstSkyFrame *) astInitFrame( mem, size, 0, + (AstFrameVtab *) vtab, name, 2 ); + + if ( astOK ) { + +/* Initialise the SkyFrame data. */ +/* ----------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->equinox = AST__BAD; + new->projection = NULL; + new->neglon = -INT_MAX; + new->skytol = AST__BAD; + new->alignoffset = -INT_MAX; + new->skyrefis = AST__BAD_REF; + new->skyref[ 0 ] = AST__BAD; + new->skyref[ 1 ] = AST__BAD; + new->skyrefp[ 0 ] = AST__BAD; + new->skyrefp[ 1 ] = AST__BAD; + new->last = AST__BAD; + new->eplast = AST__BAD; + new->klast = AST__BAD; + new->diurab = AST__BAD; + +/* Loop to replace the Axis object associated with each SkyFrame axis with + a SkyAxis object instead. */ + for ( axis = 0; axis < 2; axis++ ) { + +/* Create the new SkyAxis, assign it to the required SkyFrame axis and then + annul the SkyAxis pointer. */ + ax = astSkyAxis( "", status ); + astSetAxis( new, axis, ax ); + ax = astAnnul( ax ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSkyFrame *astLoadSkyFrame_( void *mem, size_t size, + AstSkyFrameVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSkyFrame + +* Purpose: +* Load a SkyFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "skyframe.h" +* AstSkyFrame *astLoadSkyFrame( void *mem, size_t size, +* AstSkyFrameVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SkyFrame loader. + +* Description: +* This function is provided to load a new SkyFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SkyFrame structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the SkyFrame is to be +* loaded. This must be of sufficient size to accommodate the +* SkyFrame data (sizeof(SkyFrame)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SkyFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SkyFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSkyFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SkyFrame. If this is NULL, a pointer +* to the (static) virtual function table for the SkyFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SkyFrame" is used instead. + +* Returned Value: +* A pointer to the new SkyFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstSkyFrame *new; /* Pointer to the new SkyFrame */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *sval; /* Pointer to string value */ + const int *perm; /* Pointer to axis permutation array */ + double dval; /* Floating point attribute value */ + int axis; /* External axis index */ + int invperm[ 2 ]; /* Inverse permutation array */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SkyFrame. In this case the + SkyFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSkyFrame ); + vtab = &class_vtab; + name = "SkyFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSkyFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SkyFrame. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Get a pointer to the SkyFrame's axis permutation array (using a method, + to allow for any over-ride by a derived class). */ + perm = astGetPerm( new ); + +/* Generate an inverse axis permutation array from the forward permutation + values. This will be used to determine which axis should be enquired + about (using possibly over-ridden methods) to obtain data to + correspond with a particular internal value (i.e. instance variable) + relating to an axis. This step is needed so that the effect of any + axis permutation can be un-done before values are written out, as + output values are written by this function in un-permuted order. */ + for( axis = 0; axis < 2; axis++ ) invperm[ perm[ axis ] ] = axis; + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SkyFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* The attributes defining the offset coordinate system must be loaded + before the System attrivbute, since SetSystem uses them. */ + +/* AlignOffset */ +/* ----------- */ + new->alignoffset = astReadInt( channel, "aloff", -INT_MAX ); + if ( TestAlignOffset( new, status ) ) SetAlignOffset( new, new->alignoffset, status ); + +/* SkyRefIs. */ +/* --------- */ + sval = astReadString( channel, "srefis", " " ); + if( sval ){ + new->skyrefis = AST__BAD_REF; + if( astChrMatch( sval, POLE_STRING ) ) { + new->skyrefis = AST__POLE_REF; + } else if( astChrMatch( sval, ORIGIN_STRING ) ) { + new->skyrefis = AST__ORIGIN_REF; + } else if( astChrMatch( sval, IGNORED_STRING ) ) { + new->skyrefis = AST__IGNORED_REF; + } else if( !astChrMatch( sval, " " ) && astOK ){ + astError( AST__INTER, "astRead(SkyFrame): Corrupt SkyFrame contains " + "invalid SkyRefIs attribute value (%s).", status, sval ); + } + if( TestSkyRefIs( new, status ) ) SetSkyRefIs( new, new->skyrefis, status ); + sval = astFree( sval ); + } + +/* SkyRef. */ +/* ------- */ + new->skyref[ 0 ] = astReadDouble( channel, "sref1", AST__BAD ); + axis = invperm[ 0 ]; + if ( TestSkyRef( new, axis, status ) ) SetSkyRef( new, axis, new->skyref[ 0 ], status ); + + new->skyref[ 1 ] = astReadDouble( channel, "sref2", AST__BAD ); + axis = invperm[ 1 ]; + if ( TestSkyRef( new, axis, status ) ) SetSkyRef( new, axis, new->skyref[ 1 ], status ); + +/* SkyRefP. */ +/* -------- */ + new->skyrefp[ 0 ] = astReadDouble( channel, "srefp1", AST__BAD ); + axis = invperm[ 0 ]; + if ( TestSkyRefP( new, axis, status ) ) SetSkyRefP( new, axis, new->skyrefp[ 0 ], status ); + + new->skyrefp[ 1 ] = astReadDouble( channel, "srefp2", AST__BAD ); + axis = invperm[ 1 ]; + if ( TestSkyRefP( new, axis, status ) ) SetSkyRefP( new, axis, new->skyrefp[ 1 ], status ); + +/* System. */ +/* ------- */ +/* The System attribute is now part of the Frame class, but this code is + retained to allow this version of AST to read SkyFrames dumped by + previous versions. */ + +/* Check a value has not already been assigned to the Frames System + attribute. */ + if( !astTestSystem( new ) ){ + +/* Read the external representation as a string. */ + sval = astReadString( channel, "system", NULL ); + +/* If a value was read, use the SetAttrib method to validate and store the + new value in the correct place, then free the string. */ + if ( sval ) { + astSet( new, "System=%s", status, sval); + sval = astFree( sval ); + } + } + +/* Epoch. */ +/* ------ */ +/* The Epoch attribute is now part of the Frame class, but this code is + retained to allow this version of AST to read SkyFrames dumped by + previous versions. */ + +/* Check a value has not already been assigned to the Frames Epoch + attribute. */ + if( !astTestEpoch( new ) ){ + +/* Get the value. */ + dval = astReadDouble( channel, "epoch", AST__BAD ); + +/* If a value was read, use the SetAttrib method to validate and store the + new value in the correct place. */ + if( dval != AST__BAD ) { + if( dval < 1984.0 ) { + astSet( new, "Epoch=B%.*g", status, DBL_DIG, dval); + } else { + astSet( new, "Epoch=J%.*g", status, DBL_DIG, dval); + } + } + } + +/* Projection. */ +/* ----------- */ + new->projection = astReadString( channel, "proj", NULL ); + +/* Equinox. */ +/* -------- */ +/* Interpret this as Besselian or Julian depending on its value. */ + new->equinox = astReadDouble( channel, "eqnox", AST__BAD ); + if ( TestEquinox( new, status ) ) { + SetEquinox( new, ( new->equinox < 1984.0 ) ? palEpb2d( new->equinox ) : + palEpj2d( new->equinox ), status ); + } + +/* NegLon. */ +/* ------- */ + new->neglon = astReadInt( channel, "neglon", -INT_MAX ); + if ( TestNegLon( new, status ) ) SetNegLon( new, new->neglon, status ); + +/* SkyTol. */ +/* ------- */ + new->skytol = astReadDouble( channel, "skytol", AST__BAD ); + if ( TestSkyTol( new, status ) ) SetSkyTol( new, new->skytol, status ); + +/* Other values */ +/* ------------ */ + new->last = AST__BAD; + new->eplast = AST__BAD; + new->klast = AST__BAD; + new->diurab = AST__BAD; + +/* If an error occurred, clean up by deleting the new SkyFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SkyFrame pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astClearAsTime_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SkyFrame,ClearAsTime))( this, axis, status ); +} +int astGetAsTime_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetAsTime))( this, axis, status ); +} +void astSetAsTime_( AstSkyFrame *this, int axis, int value, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SkyFrame,SetAsTime))( this, axis, value, status ); +} +int astTestAsTime_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,TestAsTime))( this, axis, status ); +} +int astGetIsLatAxis_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetIsLatAxis))( this, axis, status ); +} +int astGetIsLonAxis_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetIsLonAxis))( this, axis, status ); +} +int astGetLatAxis_( AstSkyFrame *this, int *status ) { + if ( !astOK ) return 1; + return (**astMEMBER(this,SkyFrame,GetLatAxis))( this, status ); +} +int astGetLonAxis_( AstSkyFrame *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,SkyFrame,GetLonAxis))( this, status ); +} +double astGetSkyRefP_( AstSkyFrame *this, int axis, int *status ) { + if ( !astOK ) return 0.0; + return (**astMEMBER(this,SkyFrame,GetSkyRefP))( this, axis, status ); +} +AstMapping *astSkyOffsetMap_( AstSkyFrame *this, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,SkyFrame,SkyOffsetMap))( this, status ); +} + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSkyFrame *astSkyFrameId_( const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstSkyFrame *astSkyFrameId_( const char *options, ... ) { +/* +*++ +* Name: +c astSkyFrame +f AST_SKYFRAME + +* Purpose: +* Create a SkyFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "skyframe.h" +c AstSkyFrame *astSkyFrame( const char *options, ... ) +f RESULT = AST_SKYFRAME( OPTIONS, STATUS ) + +* Class Membership: +* SkyFrame constructor. + +* Description: +* This function creates a new SkyFrame and optionally initialises +* its attributes. +* +* A SkyFrame is a specialised form of Frame which describes +* celestial longitude/latitude coordinate systems. The particular +* celestial coordinate system to be represented is specified by +* setting the SkyFrame's System attribute (currently, the default +* is ICRS) qualified, as necessary, by a mean Equinox value and/or +* an Epoch. +* +* For each of the supported celestial coordinate systems, a SkyFrame +* can apply an optional shift of origin to create a coordinate system +* representing offsets within the celestial coordinate system from some +* specified point. This offset coordinate system can also be rotated to +* define new longitude and latitude axes. See attributes SkyRef, SkyRefIs +* and SkyRefP +* +* All the coordinate values used by a SkyFrame are in +* radians. These may be formatted in more conventional ways for +c display by using astFormat. +f display by using AST_FORMAT. + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SkyFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SkyFrame. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSkyFrame() +f AST_SKYFRAME = INTEGER +* A pointer to the new SkyFrame. + +* Examples: +c frame = astSkyFrame( "" ); +c Creates a SkyFrame to describe the default ICRS celestial +c coordinate system. +c frame = astSkyFrame( "System = FK5, Equinox = J2005, Digits = 10" ); +c Creates a SkyFrame to describe the FK5 celestial +c coordinate system, with a mean Equinox of J2005.0. +c Because especially accurate coordinates will be used, +c additional precision (10 digits) has been requested. This will +c be used when coordinate values are formatted for display. +c frame = astSkyFrame( "System = FK4, Equinox = 1955-sep-2" ); +c Creates a SkyFrame to describe the old FK4 celestial +c coordinate system. A default Epoch value (B1950.0) is used, +c but the mean Equinox value is given explicitly as "1955-sep-2". +c frame = astSkyFrame( "System = GAPPT, Epoch = %s", date ); +c Creates a SkyFrame to describe the Geocentric Apparent +c celestial coordinate system. The Epoch value, which specifies +c the date of observation, is obtained from a date/time string +c supplied via the string pointer "date". +f FRAME = AST_SKYFRAME( ' ', STATUS ) +f Creates a SkyFrame to describe the default ICRS celestial +f coordinate system. +f FRAME = AST_SKYFRAME( 'System = FK5, Equinox = J2005, Digits = 10', STATUS ) +f Creates a SkyFrame to describe the FK5 celestial +f coordinate system, with a mean Equinox of J2005.0. +f Because especially accurate coordinates will be used, +f additional precision (10 digits) has been requested. This will +f be used when coordinate values are formatted for display. +f FRAME = AST_SKYFRAME( 'System = FK4, Equinox = 1955-SEP-2', STATUS ) +f Creates a SkyFrame to describe the old FK4 celestial +f coordinate system. A default Epoch value (B1950.0) is used, +f but the mean Equinox value is given explicitly as "1955-SEP-2". +f FRAME = AST_SKYFRAME( 'System = GAPPT, Epoch = ' // DATE, STATUS ) +f Creates a SkyFrame to describe the Geocentric Apparent +f celestial coordinate system. The Epoch value, which specifies +f the date of observation, is obtained from a date/time string +f contained in the character variable DATE. + +* Notes: +* - Currently, the default celestial coordinate system is +* ICRS. However, this default may change in future as new +* astrometric standards evolve. The intention is to track the most +* modern appropriate standard. For this reason, you should use the +* default only if this is what you intend (and can tolerate any +* associated slight change in behaviour with future versions of +* this function). If you intend to use the ICRS system +* indefinitely, then you should specify it explicitly using an +c "options" value of "System=ICRS". +f OPTIONS value of "System=ICRS". +* - Whichever celestial coordinate system is represented, it will +* have two axes. The first of these will be the longitude axis +* and the second will be the latitude axis. This order can be +c changed using astPermAxes if required. +f changed using AST_PERMAXES if required. +* - When conversion between two SkyFrames is requested (as when +c supplying SkyFrames to astConvert), +f supplying SkyFrames AST_CONVERT), +* account will be taken of the nature of the celestial coordinate +* systems they represent, together with any qualifying mean Equinox or +* Epoch values, etc. The AlignSystem attribute will also be taken into +* account. The results will therefore fully reflect the +* relationship between positions on the sky measured in the two +* systems. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astSkyFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astSkyFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astSkyFrame_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSkyFrame *new; /* Pointer to new SkyFrame */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SkyFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSkyFrame( NULL, sizeof( AstSkyFrame ), !class_init, &class_vtab, + "SkyFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SkyFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new SkyFrame. */ + return astMakeId( new ); +} + + + + + + + diff --git a/skyframe.h b/skyframe.h new file mode 100644 index 0000000..41d8f22 --- /dev/null +++ b/skyframe.h @@ -0,0 +1,505 @@ +#if !defined( SKYFRAME_INCLUDED ) /* Include this file only once */ +#define SKYFRAME_INCLUDED +/* +*+ +* Name: +* skyframe.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SkyFrame class. + +* Invocation: +* #include "skyframe.h" + +* Description: +* This include file defines the interface to the SkyFrame class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. + +* Inheritance: +* The SkyFrame class inherits from the Frame class. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstSkyFrame +* SkyFrame object type. + +* Protected: +* AstSkyFrameVtab +* SkyFrame virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 4-MAR-1996 (RFWS): +* Original version. +* 24-MAY-1996 (RFWS): +* Tidied up, etc. +* 24-SEP-1996 (RFWS): +* Added I/O facilities. +* 16-JUL-1997 (RFWS): +* Added Projection attribute. +* 26-FEB-1998 (RFWS): +* Over-ride the astUnformat method. +* 3-APR-2001 (DSB): +* Added "Unknown" option for the System attribute. Added read-only +* attributes LatAxis and LonAxis. +* 10-OCT-2002 (DSB): +* Moved definitions of macros for SkyFrame system values into +* this file from skyframe.c. +* 15-NOV-2002 (DSB): +* Move the System attribute from this class to the parent (Frame) +* class. +* 8-JAN-2003 (DSB): +* Added protected astInitSkyFrameVtab method. +* 19-APR-2004 (DSB): +* Added SkyRef, SkyRefIs, SkyRefP and AlignOffset attributes. +* Simplified prologue. +* 2-DEC-2004 (DSB): +* Added System "J2000" +* 22-FEB-2006 (DSB): +* Added Local Apparent Sidereal Time to the SkyFrame structure. +* 3-OCT-2006 (DSB): +* Added Equation of Equinoxes to the SkyFrame structure. +* 6-OCT-2006 (DSB): +* Removed Equation of Equinoxes from the SkyFrame structure. +* Added dut1 to the SkyFrame structure. +* Added Dut1 accessor methods. +* 14-OCT-2006 (DSB): +* Moved dut1 to the Frame class. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "frame.h" /* Parent Frame class */ + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) /* Protected */ + +/* Define values for the different values of the SkyRefIs attribute. */ +#define AST__BAD_REF 0 +#define AST__POLE_REF 1 +#define AST__ORIGIN_REF 2 +#define AST__IGNORED_REF 3 + +/* Values used to represent different System attribute values. */ +#define AST__FK4 1 +#define AST__FK4_NO_E 2 +#define AST__FK5 3 +#define AST__GAPPT 4 +#define AST__ECLIPTIC 5 +#define AST__GALACTIC 6 +#define AST__SUPERGALACTIC 7 +#define AST__ICRS 8 +#define AST__HELIOECLIPTIC 9 +#define AST__J2000 10 +#define AST__UNKNOWN 11 +#define AST__AZEL 12 + +/* Define constants used to size global arrays in this module. */ +/* Define other numerical constants for use in this module. */ +#define AST__SKYFRAME_GETATTRIB_BUFF_LEN 200 +#define AST__SKYFRAME_GETFORMAT_BUFF_LEN 50 +#define AST__SKYFRAME_GETLABEL_BUFF_LEN 40 +#define AST__SKYFRAME_GETSYMBOL_BUFF_LEN 20 +#define AST__SKYFRAME_GETTITLE_BUFF_LEN 200 + +#endif + +/* Type Definitions. */ +/* ================= */ + +/* Cached LAST look-up table. */ +/* -------------------------- */ +/* Holds a list of epoch values and the corresponding Local Apparent + Sidereal Time values. Also holds the observatory position and DUT1 + value used when calculating the LAST values. */ +typedef struct AstSkyLastTable { + double obslat; /* ObsLat at which LAST values were calculated */ + double obslon; /* ObsLon at which LAST values were calculated */ + double obsalt; /* ObsAlt at which LAST values were calculated */ + double dut1; /* Dut1 values at which LAST values were calculated */ + int nentry; /* Number of entries in the epoch and last arrays */ + double *epoch; /* Array of epoch values */ + double *last; /* Array of LAST values */ +} AstSkyLastTable; + +/* SkyFrame structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstSkyFrame { + +/* Attributes inherited from the parent class. */ + AstFrame frame; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + char *projection; /* Description of sky projection */ + double equinox; /* Modified Julian Date of mean equinox */ + int neglon; /* Display negative longitude values? */ + double skytol; /* Smallest significant distance */ + int alignoffset; /* Align SkyFrame in offset coords? */ + int skyrefis; /* Nature of offset coord system */ + double skyref[ 2 ]; /* Origin or pole of offset coord system */ + double skyrefp[ 2 ]; /* Point on primary meridian of offset coord system */ + double last; /* Local Apparent Sidereal Time */ + double eplast; /* Epoch used to calculate "last" */ + double klast; /* Ratio of solar to sidereal time */ + double diurab; /* Magnitude of diurnal aberration vector */ +} AstSkyFrame; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSkyFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstMapping *(* SkyOffsetMap)( AstSkyFrame *, int * ); + const char *(* GetProjection)( AstSkyFrame *, int * ); + double (* GetEquinox)( AstSkyFrame *, int * ); + int (* GetNegLon)( AstSkyFrame *, int * ); + int (* GetAsTime)( AstSkyFrame *, int, int * ); + int (* GetIsLatAxis)( AstSkyFrame *, int, int * ); + int (* GetIsLonAxis)( AstSkyFrame *, int, int * ); + int (* GetLatAxis)( AstSkyFrame *, int * ); + int (* GetLonAxis)( AstSkyFrame *, int * ); + int (* TestAsTime)( AstSkyFrame *, int, int * ); + int (* TestEquinox)( AstSkyFrame *, int * ); + int (* TestNegLon)( AstSkyFrame *, int * ); + int (* TestProjection)( AstSkyFrame *, int * ); + void (* ClearAsTime)( AstSkyFrame *, int, int * ); + void (* ClearEquinox)( AstSkyFrame *, int * ); + void (* ClearNegLon)( AstSkyFrame *, int * ); + void (* ClearProjection)( AstSkyFrame *, int * ); + void (* SetAsTime)( AstSkyFrame *, int, int, int * ); + void (* SetEquinox)( AstSkyFrame *, double, int * ); + void (* SetNegLon)( AstSkyFrame *, int, int * ); + void (* SetProjection)( AstSkyFrame *, const char *, int * ); + + int (* GetSkyRefIs)( AstSkyFrame *, int * ); + int (* TestSkyRefIs)( AstSkyFrame *, int * ); + void (* ClearSkyRefIs)( AstSkyFrame *, int * ); + void (* SetSkyRefIs)( AstSkyFrame *, int, int * ); + + double (* GetSkyTol)( AstSkyFrame *, int * ); + int (* TestSkyTol)( AstSkyFrame *, int * ); + void (* ClearSkyTol)( AstSkyFrame *, int * ); + void (* SetSkyTol)( AstSkyFrame *, double, int * ); + + double (* GetSkyRef)( AstSkyFrame *, int, int * ); + int (* TestSkyRef)( AstSkyFrame *, int, int * ); + void (* ClearSkyRef)( AstSkyFrame *, int, int * ); + void (* SetSkyRef)( AstSkyFrame *, int, double, int * ); + + double (* GetSkyRefP)( AstSkyFrame *, int, int * ); + int (* TestSkyRefP)( AstSkyFrame *, int, int * ); + void (* ClearSkyRefP)( AstSkyFrame *, int, int * ); + void (* SetSkyRefP)( AstSkyFrame *, int, double, int * ); + + int (* GetAlignOffset)( AstSkyFrame *, int * ); + int (* TestAlignOffset)( AstSkyFrame *, int * ); + void (* ClearAlignOffset)( AstSkyFrame *, int * ); + void (* SetAlignOffset)( AstSkyFrame *, int, int * ); + +} AstSkyFrameVtab; + +#if defined(THREAD_SAFE) + +/* The AstSkyFrameGlobals structure makes a forward reference to the + AstTimeFrame structure which is not defined here (since the + timeframe.h file includes skyframe.h). Hence make a preliminary + definition available now. */ +struct AstTimeFrame; + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstSkyFrameGlobals { + AstSkyFrameVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__SKYFRAME_GETATTRIB_BUFF_LEN + 1 ]; + char GetFormat_Buff[ AST__SKYFRAME_GETFORMAT_BUFF_LEN + 1 ]; + char GetLabel_Buff[ AST__SKYFRAME_GETLABEL_BUFF_LEN + 1 ]; + char GetSymbol_Buff[ AST__SKYFRAME_GETSYMBOL_BUFF_LEN + 1 ]; + char GetTitle_Buff[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ]; + char GetTitle_Buff2[ AST__SKYFRAME_GETTITLE_BUFF_LEN + 1 ]; + struct AstTimeFrame *TDBFrame; + struct AstTimeFrame *LASTFrame; +} AstSkyFrameGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SkyFrame) /* Check class membership */ +astPROTO_ISA(SkyFrame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstSkyFrame *astSkyFrame_( const char *, int *, ...); +#else +AstSkyFrame *astSkyFrameId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSkyFrame *astInitSkyFrame_( void *, size_t, int, AstSkyFrameVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitSkyFrameVtab_( AstSkyFrameVtab *, const char *, int * ); + +/* Loader. */ +AstSkyFrame *astLoadSkyFrame_( void *, size_t, AstSkyFrameVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitSkyFrameGlobals_( AstSkyFrameGlobals * ); +#endif +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstMapping *astSkyOffsetMap_( AstSkyFrame *, int * ); + +#if defined(astCLASS) /* Protected */ +const char *astGetProjection_( AstSkyFrame *, int * ); +double astGetEquinox_( AstSkyFrame *, int * ); +int astGetNegLon_( AstSkyFrame *, int * ); +int astGetAsTime_( AstSkyFrame *, int, int * ); +int astGetIsLatAxis_( AstSkyFrame *, int, int * ); +int astGetIsLonAxis_( AstSkyFrame *, int, int * ); +int astGetLatAxis_( AstSkyFrame *, int * ); +int astGetLonAxis_( AstSkyFrame *, int * ); +int astTestAsTime_( AstSkyFrame *, int, int * ); +int astTestEquinox_( AstSkyFrame *, int * ); +int astTestNegLon_( AstSkyFrame *, int * ); +int astTestProjection_( AstSkyFrame *, int * ); +void astClearAsTime_( AstSkyFrame *, int, int * ); +void astClearEquinox_( AstSkyFrame *, int * ); +void astClearNegLon_( AstSkyFrame *, int * ); +void astClearProjection_( AstSkyFrame *, int * ); +void astSetAsTime_( AstSkyFrame *, int, int, int * ); +void astSetEquinox_( AstSkyFrame *, double, int * ); +void astSetNegLon_( AstSkyFrame *, int, int * ); +void astSetProjection_( AstSkyFrame *, const char *, int * ); + +int astGetAlignOffset_( AstSkyFrame *, int * ); +int astTestAlignOffset_( AstSkyFrame *, int * ); +void astClearAlignOffset_( AstSkyFrame *, int * ); +void astSetAlignOffset_( AstSkyFrame *, int, int * ); + +int astGetSkyRefIs_( AstSkyFrame *, int * ); +int astTestSkyRefIs_( AstSkyFrame *, int * ); +void astClearSkyRefIs_( AstSkyFrame *, int * ); +void astSetSkyRefIs_( AstSkyFrame *, int, int * ); + +double astGetSkyRef_( AstSkyFrame *, int, int * ); +int astTestSkyRef_( AstSkyFrame *, int, int * ); +void astClearSkyRef_( AstSkyFrame *, int, int * ); +void astSetSkyRef_( AstSkyFrame *, int, double, int * ); + +double astGetSkyRefP_( AstSkyFrame *, int, int * ); +int astTestSkyRefP_( AstSkyFrame *, int, int * ); +void astClearSkyRefP_( AstSkyFrame *, int, int * ); +void astSetSkyRefP_( AstSkyFrame *, int, double, int * ); + +double astGetSkyTol_( AstSkyFrame *, int * ); +int astTestSkyTol_( AstSkyFrame *, int * ); +void astClearSkyTol_( AstSkyFrame *, int * ); +void astSetSkyTol_( AstSkyFrame *, double, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSkyFrame(this) astINVOKE_CHECK(SkyFrame,this,0) +#define astVerifySkyFrame(this) astINVOKE_CHECK(SkyFrame,this,1) + +/* Test class membership. */ +#define astIsASkyFrame(this) astINVOKE_ISA(SkyFrame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astSkyFrame astINVOKE(F,astSkyFrame_) +#else +#define astSkyFrame astINVOKE(F,astSkyFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSkyFrame(mem,size,init,vtab,name) \ +astINVOKE(O,astInitSkyFrame_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSkyFrameVtab(vtab,name) astINVOKE(V,astInitSkyFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSkyFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSkyFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) + +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ + +#define astSkyOffsetMap(this) \ +astINVOKE(O,astSkyOffsetMap_(astCheckSkyFrame(this),STATUS_PTR)) + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +/* Here we make use of astCheckSkyFrame to validate SkyFrame pointers + before use. This provides a contextual error report if a pointer to + the wrong sort of object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astClearAsTime(this,axis) \ +astINVOKE(V,astClearAsTime_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astClearEquinox(this) \ +astINVOKE(V,astClearEquinox_(astCheckSkyFrame(this),STATUS_PTR)) +#define astClearNegLon(this) \ +astINVOKE(V,astClearNegLon_(astCheckSkyFrame(this),STATUS_PTR)) +#define astClearProjection(this) \ +astINVOKE(V,astClearProjection_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetAsTime(this,axis) \ +astINVOKE(V,astGetAsTime_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astGetEquinox(this) \ +astINVOKE(V,astGetEquinox_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetNegLon(this) \ +astINVOKE(V,astGetNegLon_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetIsLatAxis(this,axis) \ +astINVOKE(V,astGetIsLatAxis_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astGetIsLonAxis(this,axis) \ +astINVOKE(V,astGetIsLonAxis_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astGetLatAxis(this) \ +astINVOKE(V,astGetLatAxis_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetLonAxis(this) \ +astINVOKE(V,astGetLonAxis_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetProjection(this) \ +astINVOKE(V,astGetProjection_(astCheckSkyFrame(this),STATUS_PTR)) +#define astSetAsTime(this,axis,value) \ +astINVOKE(V,astSetAsTime_(astCheckSkyFrame(this),axis,value,STATUS_PTR)) +#define astSetEquinox(this,value) \ +astINVOKE(V,astSetEquinox_(astCheckSkyFrame(this),value,STATUS_PTR)) +#define astSetNegLon(this,value) \ +astINVOKE(V,astSetNegLon_(astCheckSkyFrame(this),value,STATUS_PTR)) +#define astSetProjection(this,value) \ +astINVOKE(V,astSetProjection_(astCheckSkyFrame(this),value,STATUS_PTR)) +#define astTestAsTime(this,axis) \ +astINVOKE(V,astTestAsTime_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astTestEquinox(this) \ +astINVOKE(V,astTestEquinox_(astCheckSkyFrame(this),STATUS_PTR)) +#define astTestNegLon(this) \ +astINVOKE(V,astTestNegLon_(astCheckSkyFrame(this),STATUS_PTR)) +#define astTestProjection(this) \ +astINVOKE(V,astTestProjection_(astCheckSkyFrame(this),STATUS_PTR)) + +#define astClearAlignOffset(this) astINVOKE(V,astClearAlignOffset_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetAlignOffset(this) astINVOKE(V,astGetAlignOffset_(astCheckSkyFrame(this),STATUS_PTR)) +#define astSetAlignOffset(this,value) astINVOKE(V,astSetAlignOffset_(astCheckSkyFrame(this),value,STATUS_PTR)) +#define astTestAlignOffset(this) astINVOKE(V,astTestAlignOffset_(astCheckSkyFrame(this),STATUS_PTR)) + +#define astClearSkyRefIs(this) astINVOKE(V,astClearSkyRefIs_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetSkyRefIs(this) astINVOKE(V,astGetSkyRefIs_(astCheckSkyFrame(this),STATUS_PTR)) +#define astSetSkyRefIs(this,value) astINVOKE(V,astSetSkyRefIs_(astCheckSkyFrame(this),value,STATUS_PTR)) +#define astTestSkyRefIs(this) astINVOKE(V,astTestSkyRefIs_(astCheckSkyFrame(this),STATUS_PTR)) + +#define astClearSkyRef(this,axis) astINVOKE(V,astClearSkyRef_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astGetSkyRef(this,axis) astINVOKE(V,astGetSkyRef_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astSetSkyRef(this,axis,value) astINVOKE(V,astSetSkyRef_(astCheckSkyFrame(this),axis,value,STATUS_PTR)) +#define astTestSkyRef(this,axis) astINVOKE(V,astTestSkyRef_(astCheckSkyFrame(this),axis,STATUS_PTR)) + +#define astClearSkyRefP(this,axis) astINVOKE(V,astClearSkyRefP_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astGetSkyRefP(this,axis) astINVOKE(V,astGetSkyRefP_(astCheckSkyFrame(this),axis,STATUS_PTR)) +#define astSetSkyRefP(this,axis,value) astINVOKE(V,astSetSkyRefP_(astCheckSkyFrame(this),axis,value,STATUS_PTR)) +#define astTestSkyRefP(this,axis) astINVOKE(V,astTestSkyRefP_(astCheckSkyFrame(this),axis,STATUS_PTR)) + +#define astClearSkyTol(this) astINVOKE(V,astClearSkyTol_(astCheckSkyFrame(this),STATUS_PTR)) +#define astGetSkyTol(this) astINVOKE(V,astGetSkyTol_(astCheckSkyFrame(this),STATUS_PTR)) +#define astSetSkyTol(this,value) astINVOKE(V,astSetSkyTol_(astCheckSkyFrame(this),value,STATUS_PTR)) +#define astTestSkyTol(this) astINVOKE(V,astTestSkyTol_(astCheckSkyFrame(this),STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/slamap.c b/slamap.c new file mode 100644 index 0000000..aa606c3 --- /dev/null +++ b/slamap.c @@ -0,0 +1,5007 @@ +/* +*class++ +* Name: +* SlaMap + +* Purpose: +* Sequence of celestial coordinate conversions. + +* Constructor Function: +c astSlaMap (also see astSlaAdd) +f AST_SLAMAP (also see AST_SLAADD) + +* Description: +* An SlaMap is a specialised form of Mapping which can be used to +* represent a sequence of conversions between standard celestial +* (longitude, latitude) coordinate systems. +* +* When an SlaMap is first created, it simply performs a unit +c (null) Mapping on a pair of coordinates. Using the astSlaAdd +f (null) Mapping on a pair of coordinates. Using the AST_SLAADD +c function, a series of coordinate conversion steps may then be +f routine, a series of coordinate conversion steps may then be +* added, selected from those provided by the SLALIB Positional +* Astronomy Library (Starlink User Note SUN/67). This allows +* multi-step conversions between a variety of celestial coordinate +* systems to be assembled out of the building blocks provided by +* SLALIB. +* +* For details of the individual coordinate conversions available, +c see the description of the astSlaAdd function. +f see the description of the AST_SLAADD routine. + +* Inheritance: +* The SlaMap class inherits from the Mapping class. + +* Attributes: +* The SlaMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c In addition to those functions applicable to all Mappings, the +c following function may also be applied to all SlaMaps: +f In addition to those routines applicable to all Mappings, the +f following routine may also be applied to all SlaMaps: +* +c - astSlaAdd: Add a celestial coordinate conversion to an SlaMap +f - AST_SLAADD: Add a celestial coordinate conversion to an SlaMap + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2013 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 25-APR-1996 (RFWS): +* Original version. +* 28-MAY-1996 (RFWS): +* Fixed bug in argument order to palMappa for AST__SLA_AMP case. +* 26-SEP-1996 (RFWS): +* Added external interface and I/O facilities. +* 23-MAY-1997 (RFWS): +* Over-ride the astMapMerge method. +* 28-MAY-1997 (RFWS): +* Use strings to specify conversions for the public interface +* and convert to macros (from an enumerated type) for the +* internal representation. Tidy the public prologues. +* 8-JAN-2003 (DSB): +* - Changed private InitVtab method to protected astInitSlaMapVtab +* method. +* - Included STP conversion functions. +* 11-JUN-2003 (DSB): +* - Added HFK5Z and FK5HZ conversion functions. +* 28-SEP-2003 (DSB): +* - Added HEEQ and EQHE conversion functions. +* 2-DEC-2004 (DSB): +* - Added J2000H and HJ2000 conversion functions. +* 15-AUG-2005 (DSB): +* - Added H2E and E2H conversion functions. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 22-FEB-2006 (DSB): +* Cache results returned by palMappa in order to increase speed. +* 10-MAY-2006 (DSB): +* Override astEqual. +* 31-AUG-2007 (DSB): +* - Modify H2E and E2H conversion functions so that they convert to +* and from apparent (HA,Dec) rather than topocentric (HA,Dec) (i.e. +* include a correction for diurnal aberration). This requires an +* extra conversion argument holding the magnitude of the diurnal +* aberration vector. +* - Correct bug in the simplification of adjacent AMP and MAP +* conversions. +* 15-NOV-2013 (DSB): +* Fix bug in merging of adjacent AMP and MAP conversions (MapMerge +* did not take account of the fact that the arguments for these +* two conversions are stored in swapped order). +* 6-JUL-2015 (DSB): +* Added method astSlaIsEmpty. + +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SlaMap + +/* Codes to identify SLALIB sky coordinate conversions. */ +#define AST__SLA_NULL 0 /* Null value */ +#define AST__SLA_ADDET 1 /* Add E-terms of aberration */ +#define AST__SLA_SUBET 2 /* Subtract E-terms of aberration */ +#define AST__SLA_PREBN 3 /* Bessel-Newcomb (FK4) precession */ +#define AST__SLA_PREC 4 /* Apply IAU 1975 (FK5) precession model */ +#define AST__SLA_FK45Z 5 /* FK4 to FK5, no proper motion or parallax */ +#define AST__SLA_FK54Z 6 /* FK5 to FK4, no proper motion or parallax */ +#define AST__SLA_AMP 7 /* Geocentric apparent to mean place */ +#define AST__SLA_MAP 8 /* Mean place to geocentric apparent */ +#define AST__SLA_ECLEQ 9 /* Ecliptic to J2000.0 equatorial */ +#define AST__SLA_EQECL 10 /* Equatorial J2000.0 to ecliptic */ +#define AST__SLA_GALEQ 11 /* Galactic to J2000.0 equatorial */ +#define AST__SLA_EQGAL 12 /* J2000.0 equatorial to galactic */ +#define AST__SLA_GALSUP 13 /* Galactic to supergalactic */ +#define AST__SLA_SUPGAL 14 /* Supergalactic to galactic */ +#define AST__HPCEQ 15 /* Helioprojective-Cartesian to J2000.0 equatorial */ +#define AST__EQHPC 16 /* J2000.0 equatorial to Helioprojective-Cartesian */ +#define AST__HPREQ 17 /* Helioprojective-Radial to J2000.0 equatorial */ +#define AST__EQHPR 18 /* J2000.0 equatorial to Helioprojective-Radial */ +#define AST__SLA_HFK5Z 19 /* ICRS to FK5 J2000.0, no pm or parallax */ +#define AST__SLA_FK5HZ 20 /* FK5 J2000.0 to ICRS, no pm or parallax */ +#define AST__HEEQ 21 /* Helio-ecliptic to equatorial */ +#define AST__EQHE 22 /* Equatorial to helio-ecliptic */ +#define AST__J2000H 23 /* Dynamical J2000 to ICRS */ +#define AST__HJ2000 24 /* ICRS to dynamical J2000 */ +#define AST__SLA_DH2E 25 /* Horizon to equatorial coordinates */ +#define AST__SLA_DE2H 26 /* Equatorial coordinates to horizon */ +#define AST__R2H 27 /* RA to hour angle */ +#define AST__H2R 28 /* Hour to RA angle */ + +/* Maximum number of arguments required by an SLALIB conversion. */ +#define MAX_SLA_ARGS 4 + +/* The alphabet (used for generating keywords for arguments). */ +#define ALPHABET "abcdefghijklmnopqrstuvwxyz" + +/* Angle conversion (PI is from the SLALIB slamac.h file) */ +#define PI 3.1415926535897932384626433832795028841971693993751 +#define PIBY2 (PI/2.0) +#define D2R (PI/180.0) +#define R2D (180.0/PI) +#define AS2R (PI/648000.0) + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "pal.h" /* SLALIB interface */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "wcsmap.h" /* Required for AST__DPI */ +#include "unitmap.h" /* Unit (null) Mappings */ +#include "slamap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->Eq_Cache = AST__BAD; \ + globals->Ep_Cache = AST__BAD; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SlaMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SlaMap,Class_Init) +#define class_vtab astGLOBAL(SlaMap,Class_Vtab) +#define eq_cache astGLOBAL(SlaMap,Eq_Cache) +#define ep_cache astGLOBAL(SlaMap,Ep_Cache) +#define amprms_cache astGLOBAL(SlaMap,Amprms_Cache) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* A cache used to store the most recent results from palMappa in order + to avoid continuously recalculating the same values. */ +static double eq_cache = AST__BAD; +static double ep_cache = AST__BAD; +static double amprms_cache[ 21 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSlaMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSlaMap *astSlaMapId_( int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *CvtString( int, const char **, int *, const char *[ MAX_SLA_ARGS ], int * ); +static int CvtCode( const char *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int SlaIsEmpty( AstSlaMap *, int * ); +static void AddSlaCvt( AstSlaMap *, int, const double *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void De2h( double, double, double, double, double *, double *, int * ); +static void Dh2e( double, double, double, double, double *, double *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Earth( double, double[3], int * ); +static void SlaAdd( AstSlaMap *, const char *, const double[], int * ); +static void SolarPole( double, double[3], int * ); +static void Hpcc( double, double[3], double[3][3], double[3], int * ); +static void Hprc( double, double[3], double[3][3], double[3], int * ); +static void Hgc( double, double[3][3], double[3], int * ); +static void Haec( double, double[3][3], double[3], int * ); +static void Haqc( double, double[3][3], double[3], int * ); +static void Gsec( double, double[3][3], double[3], int * ); +static void STPConv( double, int, int, int, double[3], double *[3], int, double[3], double *[3], int * ); +static void J2000H( int, int, double *, double *, int * ); + +static int GetObjSize( AstObject *, int * ); + +/* Member functions. */ +/* ================= */ + +static void De2h( double ha, double dec, double phi, double diurab, + double *az, double *el, int *status ){ + +/* Not quite like slaDe2h since it converts from apparent (ha,dec) to + topocentric (az,el). This includes a correction for diurnal + aberration. The magnitude of the diurnal aberration vector should be + supplied in parameter "diurab". The extra code is taken from the + Fortran routine SLA_AOPQK. */ + +/* Local Variables: */ + double a; + double cd; + double ch; + double cp; + double f; + double r; + double sd; + double sh; + double sp; + double x; + double xhd; + double xhdt; + double y; + double yhd; + double yhdt; + double z; + double zhd; + double zhdt; + +/* Check inherited status */ + if( !astOK ) return; + +/* Pre-compute common values */ + sh = sin( ha ); + ch = cos( ha ); + sd = sin( dec ); + cd = cos( dec ); + sp = sin( phi ); + cp = cos( phi ); + +/* Components of cartesian (-ha,dec) vector. */ + xhd = ch*cd; + yhd = -sh*cd; + zhd = sd; + +/* Modify the above vector to apply diurnal aberration. */ + f = ( 1.0 - diurab*yhd ); + xhdt = f*xhd; + yhdt = f*( yhd + diurab ); + zhdt = f*zhd; + +/* Convert to cartesian (az,el). */ + x = -xhdt*sp + zhdt*cp; + y = yhdt; + z = xhdt*cp + zhdt*sp; + +/* Convert to spherical (az,el). */ + r = sqrt( x*x + y*y ); + if( r == 0.0 ) { + a = 0.0; + } else { + a = atan2( y, x ); + } + + while( a < 0.0 ) a += 2*AST__DPI; + + *az = a; + *el = atan2( z, r ); +} + +static void Dh2e( double az, double el, double phi, double diurab, double *ha, + double *dec, int *status ){ + +/* Not quite like slaDh2e since it converts from topocentric (az,el) to + apparent (ha,dec). This includes a correction for diurnal aberration. + The magnitude of the diurnal aberration vector should be supplied in + parameter "diurab". The extra code is taken from the Fortran routine + SLA_OAPQK. */ + +/* Local Variables: */ + double ca; + double ce; + double cp; + double f; + double r; + double sa; + double se; + double sp; + double x; + double xmhda; + double y; + double ymhda; + double z; + double zmhda; + +/* Check inherited status */ + if( !astOK ) return; + +/* Pre-compute common values. */ + sa = sin( az ); + ca = cos( az ); + se = sin( el ); + ce = cos( el ); + sp = sin( phi ); + cp = cos( phi ); + +/* Cartesian (az,el) to Cartesian (ha,dec) - note, +ha, not -ha. */ + xmhda = -ca*ce*sp + se*cp; + ymhda = -sa*ce; + zmhda = ca*ce*cp + se*sp; + +/* Correct this vector for diurnal aberration. Since the above + expressions produce +ha rather than -ha, we do not negate "diurab" + before using it. Compare this to SLA_AOPQK. */ + f = ( 1 - diurab*ymhda ); + x = f*xmhda; + y = f*( ymhda + diurab ); + z = f*zmhda; + +/* Cartesian (ha,dec) to spherical (ha,dec). */ + r = sqrt( x*x + y*y ); + if( r == 0.0 ) { + *ha = 0.0; + } else { + *ha = atan2( y, x ); + } + *dec = atan2( z, r ); +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two SlaMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* SlaMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two SlaMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a SlaMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the SlaMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSlaMap *that; + AstSlaMap *this; + const char *argdesc[ MAX_SLA_ARGS ]; + const char *comment; + int i, j; + int nargs; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two SlaMap structures. */ + this = (AstSlaMap *) this_object; + that = (AstSlaMap *) that_object; + +/* Check the second object is a SlaMap. We know the first is a + SlaMap since we have arrived at this implementation of the virtual + function. */ + if( astIsASlaMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two SlaMaps differ, it may still be possible + for them to be equivalent. First compare the SlaMaps if their Invert + flags are the same. In this case all the attributes of the two SlaMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + if( this->ncvt == that->ncvt ) { + result = 1; + for( i = 0; i < this->ncvt && result; i++ ) { + if( this->cvttype[ i ] != that->cvttype[ i ] ) { + result = 0; + } else { + CvtString( this->cvttype[ i ], &comment, &nargs, + argdesc, status ); + for( j = 0; j < nargs; j++ ) { + if( !astEQUAL( this->cvtargs[ i ][ j ], + that->cvtargs[ i ][ j ] ) ){ + result = 0; + break; + } + } + } + } + } + +/* If the Invert flags for the two SlaMaps differ, the attributes of the two + SlaMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a SlaMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SlaMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SlaMap, +* in bytes. + +* Parameters: +* this +* Pointer to the SlaMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSlaMap *this; /* Pointer to SlaMap structure */ + int result; /* Result value to return */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SlaMap structure. */ + this = (AstSlaMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + for ( cvt = 0; cvt < this->ncvt; cvt++ ) { + result += astTSizeOf( this->cvtargs[ cvt ] ); + result += astTSizeOf( this->cvtextra[ cvt ] ); + } + + result += astTSizeOf( this->cvtargs ); + result += astTSizeOf( this->cvtextra ); + result += astTSizeOf( this->cvttype ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void AddSlaCvt( AstSlaMap *this, int cvttype, const double *args, int *status ) { +/* +* Name: +* AddSlaCvt + +* Purpose: +* Add a coordinate conversion step to an SlaMap. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* void AddSlaCvt( AstSlaMap *this, int cvttype, const double *args ) + +* Class Membership: +* SlaMap member function. + +* Description: +* This function allows one of the sky coordinate conversions +* supported by SLALIB to be appended to an SlaMap. When an SlaMap +* is first created (using astSlaMap), it simply performs a unit +* mapping. By using AddSlaCvt repeatedly, a series of sky +* coordinate conversions may then be specified which the SlaMap +* will subsequently perform in sequence. This allows a complex +* coordinate conversion to be assembled out of the basic building +* blocks provided by SLALIB. The SlaMap will also perform the +* inverse coordinate conversion (applying the individual +* conversion steps in reverse) if required. + +* Parameters: +* this +* Pointer to the SlaMap. +* cvttype +* A code to identify which sky coordinate conversion is to be +* appended. See the "SLALIB Coordinate Conversions" section +* for details of those available. +* args +* Pointer to an array of double containing the argument values +* required to fully specify the required coordinate +* conversion. The number of arguments depends on the conversion +* (see the "SLALIB Coordinate Conversions" section for +* details). This value is ignored and may be NULL if no +* arguments are required. + +* Returned Value: +* void. + +* SLALIB Coordinate Conversions: +* The following values may be supplied for the "cvttype" parameter +* in order to specify the sky coordinate conversion to be +* performed. In each case the value is named after the SLALIB +* routine that performs the conversion, and the relevant SLALIB +* documentation should be consulted for full details. +* +* The argument(s) required to fully specify each conversion are +* indicated in parentheses after each value. Values for these +* should be given in the array pointed at by "args". The argument +* names given match the corresponding SLALIB function arguments +* (in the Fortran 77 documentation - SUN/67) and their values +* should be given using the same units, time scale, calendar, +* etc. as in SLALIB. +* +* AST__SLA_ADDET( EQ ) +* Add E-terms of aberration. +* AST__SLA_SUBET( EQ ) +* Subtract E-terms of aberration. +* AST__SLA_PREBN( BEP0, BEP1 ) +* Apply Bessel-Newcomb pre-IAU 1976 (FK4) precession model. +* AST__SLA_PREC( EP0, EP1 ) +* Apply IAU 1975 (FK5) precession model. +* AST__SLA_FK45Z( BEPOCH ) +* Convert FK4 to FK5 (no proper motion or parallax). +* AST__SLA_FK54Z( BEPOCH ) +* Convert FK5 to FK4 (no proper motion or parallax). +* AST__SLA_AMP( DATE, EQ ) +* Convert geocentric apparent to mean place. +* AST__SLA_MAP( EQ, DATE ) +* Convert mean place to geocentric apparent. +* AST__SLA_ECLEQ( DATE ) +* Convert ecliptic coordinates to J2000.0 equatorial. +* AST__SLA_EQECL( DATE ) +* Convert equatorial J2000.0 to ecliptic coordinates. +* AST__SLA_GALEQ( ) +* Convert galactic coordinates to J2000.0 equatorial. +* AST__SLA_EQGAL( ) +* Convert J2000.0 equatorial to galactic coordinates. +* AST__SLA_HFK5Z( JEPOCH ) +* Convert ICRS coordinates to J2000.0 equatorial (no proper +* motion or parallax). +* AST__SLA_FK5HZ( JEPOCH ) +* Convert J2000.0 equatorial to ICRS coordinates (no proper +* motion or parallax). +* AST__SLA_GALSUP( ) +* Convert galactic to supergalactic coordinates. +* AST__SLA_SUPGAL( ) +* Convert supergalactic coordinates to galactic. +* AST__HPCEQ( DATE, OBSX, OBSY, OBSZ ) +* Convert Helioprojective-Cartesian coordinates to J2000.0 +* equatorial. This is not a native SLALIB conversion, but is +* implemented by functions within this module. The DATE argument +* is the MJD defining the HPC coordinate system. The OBSX, OBSY +* and OBSZ arguments are the AST__HAEC coordinates of the observer. +* AST__EQHPC( DATE, OBSX, OBSY, OBSZ ) +* Convert J2000.0 equatorial coordinates to Helioprojective-Cartesian. +* AST__HPREQ( DATE, OBSX, OBSY, OBSZ ) +* Convert Helioprojective-Radial coordinates to J2000.0 equatorial. +* AST__EQHPR( DATE, OBSX, OBSY, OBSZ ) +* Convert J2000.0 equatorial coordinates to Helioprojective-Radial. +* AST__HEEQ( DATE ) +* Convert helio-ecliptic to ecliptic coordinates. +* AST__EQHE( DATE ) +* Convert ecliptic to helio-ecliptic coordinates. +* AST__J2000H( ) +* Convert dynamical J2000 to ICRS. +* AST__HJ2000( ) +* Convert ICRS to dynamical J2000. +* AST__SLA_DH2E( LAT, DIURAB ) +* Convert horizon to equatorial coordinates +* AST__SLA_DE2H( LAT, DIURAB ) +* Convert equatorial to horizon coordinates +* AST__R2H( LAST ) +* Convert RA to Hour Angle. +* AST__H2R( LAST ) +* Convert Hour Angle to RA. + +* Notes: +* - The specified conversion is appended only if the SlaMap's +* Invert attribute is zero. If it is non-zero, this function +* effectively prefixes the inverse of the conversion specified +* instead. +* - Sky coordinate values are in radians (as for SLALIB) and all +* conversions are performed using double arithmetic. +*/ + +/* Local Variables: */ + const char *argdesc[ MAX_SLA_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + const char *cvt_string; /* Pointer to conversion type string */ + int nargs; /* Number of arguments */ + int ncvt; /* Number of coordinate conversions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the coordinate conversion type and obtain the number of + required arguments. */ + cvt_string = CvtString( cvttype, &comment, &nargs, argdesc, status ); + +/* If the sky coordinate conversion type was not valid, then report an + error. */ + if ( astOK && !cvt_string ) { + astError( AST__SLAIN, "AddSlaCvt(%s): Invalid SLALIB sky coordinate " + "conversion type (%d).", status, astGetClass( this ), + (int) cvttype ); + } + +/* Note the number of coordinate conversions already stored in the SlaMap. */ + if ( astOK ) { + ncvt = this->ncvt; + +/* Extend the array of conversion types and the array of pointers to + their argument lists to accommodate the new one. */ + this->cvttype = (int *) astGrow( this->cvttype, ncvt + 1, + sizeof( int ) ); + this->cvtargs = (double **) astGrow( this->cvtargs, ncvt + 1, + sizeof( double * ) ); + this->cvtextra = (double **) astGrow( this->cvtextra, ncvt + 1, + sizeof( double * ) ); + +/* If OK, allocate memory and store a copy of the argument list, + putting a pointer to the copy into the SlaMap. */ + if ( astOK ) { + this->cvtargs[ ncvt ] = astStore( NULL, args, + sizeof( double ) * (size_t) nargs ); + this->cvtextra[ ncvt ] = NULL; + } + +/* Store the conversion type and increment the conversion count. */ + if ( astOK ) { + this->cvttype[ ncvt ] = cvttype; + this->ncvt++; + } + } +} + +static int CvtCode( const char *cvt_string, int *status ) { +/* +* Name: +* CvtCode + +* Purpose: +* Convert a conversion type from a string representation to a code value. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* int CvtCode( const char *cvt_string, int *status ) + +* Class Membership: +* SlaMap member function. + +* Description: +* This function accepts a string used to repersent one of the +* SLALIB sky coordinate conversions and converts it into a code +* value for internal use. + +* Parameters: +* cvt_string +* Pointer to a constant null-terminated string representing a +* sky coordinate conversion. This is case sensitive and should +* contain no unnecessary white space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The equivalent conversion code. If the string was not +* recognised, the code AST__SLA_NULL is returned, without error. + +* Notes: +* - A value of AST__SLA_NULL will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Initialise. */ + result = AST__SLA_NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Test the string against each recognised value in turn and assign + the result. */ + if ( astChrMatch( cvt_string, "ADDET" ) ) { + result = AST__SLA_ADDET; + + } else if ( astChrMatch( cvt_string, "SUBET" ) ) { + result = AST__SLA_SUBET; + + } else if ( astChrMatch( cvt_string, "PREBN" ) ) { + result = AST__SLA_PREBN; + + } else if ( astChrMatch( cvt_string, "PREC" ) ) { + result = AST__SLA_PREC; + + } else if ( astChrMatch( cvt_string, "FK45Z" ) ) { + result = AST__SLA_FK45Z; + + } else if ( astChrMatch( cvt_string, "FK54Z" ) ) { + result = AST__SLA_FK54Z; + + } else if ( astChrMatch( cvt_string, "AMP" ) ) { + result = AST__SLA_AMP; + + } else if ( astChrMatch( cvt_string, "MAP" ) ) { + result = AST__SLA_MAP; + + } else if ( astChrMatch( cvt_string, "ECLEQ" ) ) { + result = AST__SLA_ECLEQ; + + } else if ( astChrMatch( cvt_string, "EQECL" ) ) { + result = AST__SLA_EQECL; + + } else if ( astChrMatch( cvt_string, "GALEQ" ) ) { + result = AST__SLA_GALEQ; + + } else if ( astChrMatch( cvt_string, "EQGAL" ) ) { + result = AST__SLA_EQGAL; + + } else if ( astChrMatch( cvt_string, "FK5HZ" ) ) { + result = AST__SLA_FK5HZ; + + } else if ( astChrMatch( cvt_string, "HFK5Z" ) ) { + result = AST__SLA_HFK5Z; + + } else if ( astChrMatch( cvt_string, "GALSUP" ) ) { + result = AST__SLA_GALSUP; + + } else if ( astChrMatch( cvt_string, "SUPGAL" ) ) { + result = AST__SLA_SUPGAL; + + } else if ( astChrMatch( cvt_string, "HPCEQ" ) ) { + result = AST__HPCEQ; + + } else if ( astChrMatch( cvt_string, "EQHPC" ) ) { + result = AST__EQHPC; + + } else if ( astChrMatch( cvt_string, "HPREQ" ) ) { + result = AST__HPREQ; + + } else if ( astChrMatch( cvt_string, "EQHPR" ) ) { + result = AST__EQHPR; + + } else if ( astChrMatch( cvt_string, "HEEQ" ) ) { + result = AST__HEEQ; + + } else if ( astChrMatch( cvt_string, "EQHE" ) ) { + result = AST__EQHE; + + } else if ( astChrMatch( cvt_string, "J2000H" ) ) { + result = AST__J2000H; + + } else if ( astChrMatch( cvt_string, "HJ2000" ) ) { + result = AST__HJ2000; + + } else if ( astChrMatch( cvt_string, "H2E" ) ) { + result = AST__SLA_DH2E; + + } else if ( astChrMatch( cvt_string, "E2H" ) ) { + result = AST__SLA_DE2H; + + } else if ( astChrMatch( cvt_string, "R2H" ) ) { + result = AST__R2H; + + } else if ( astChrMatch( cvt_string, "H2R" ) ) { + result = AST__H2R; + + } + +/* Return the result. */ + return result; +} + +static const char *CvtString( int cvt_code, const char **comment, + int *nargs, const char *arg[ MAX_SLA_ARGS ], int *status ) { +/* +* Name: +* CvtString + +* Purpose: +* Convert a conversion type from a code value to a string representation. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* const char *CvtString( int cvt_code, const char **comment, +* int *nargs, const char *arg[ MAX_SLA_ARGS ], int *status ) + +* Class Membership: +* SlaMap member function. + +* Description: +* This function accepts a code value used to represent one of the +* SLALIB sky coordinate conversions and converts it into an +* equivalent string representation. It also returns a descriptive +* comment and information about the arguments required in order to +* perform the conversion. + +* Parameters: +* cvt_code +* The conversion code. +* comment +* Address of a location to return a pointer to a constant +* null-terminated string containing a description of the +* conversion. +* nargs +* Address of an int in which to return the number of arguments +* required in order to perform the conversion (may be zero). +* arg +* An array in which to return a pointer to a constant +* null-terminated string for each argument (above) containing a +* description of what each argument represents. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string representation of +* the conversion code value supplied. If the code supplied is not +* valid, a NULL pointer will be returned, without error. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Result pointer to return */ + +/* Initialise the returned values. */ + *comment = NULL; + *nargs = 0; + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Test for each valid code value in turn and assign the appropriate + return values. */ + switch ( cvt_code ) { + + case AST__SLA_ADDET: + result = "ADDET"; + *comment = "Add E-terms of aberration"; + *nargs = 1; + arg[ 0 ] = "Besselian epoch of mean equinox (FK4)"; + break; + + case AST__SLA_SUBET: + result = "SUBET"; + *comment = "Subtract E-terms of aberration"; + *nargs = 1; + arg[ 0 ] = "Besselian epoch of mean equinox (FK4)"; + break; + + case AST__SLA_PREBN: + result = "PREBN"; + *comment = "Apply Bessel-Newcomb (FK4) precession"; + *nargs = 2; + arg[ 0 ] = "From Besselian epoch"; + arg[ 1 ] = "To Besselian epoch"; + break; + + case AST__SLA_PREC: + result = "PREC"; + *comment = "Apply IAU 1975 (FK5) precession"; + *nargs = 2; + arg[ 0 ] = "From Julian epoch"; + arg[ 1 ] = "To Julian epoch"; + break; + + case AST__SLA_FK45Z: + result = "FK45Z"; + *comment = "FK4 to FK5 J2000.0 (no PM or parallax)"; + arg[ 0 ] = "Besselian epoch of FK4 coordinates"; + *nargs = 1; + break; + + case AST__SLA_FK54Z: + result = "FK54Z"; + *comment = "FK5 J2000.0 to FK4 (no PM or parallax)"; + *nargs = 1; + arg[ 0 ] = "Besselian epoch of FK4 system"; + break; + + case AST__SLA_AMP: + result = "AMP"; + *comment = "Geocentric apparent to mean place (FK5)"; + *nargs = 2; + arg[ 0 ] = "TDB of apparent place (as MJD)"; + arg[ 1 ] = "Julian epoch of mean equinox (FK5)"; + break; + + case AST__SLA_MAP: + result = "MAP"; + *comment = "Mean place (FK5) to geocentric apparent"; + *nargs = 2; + arg[ 0 ] = "Julian epoch of mean equinox (FK5)"; + arg[ 1 ] = "TDB of apparent place (as MJD)"; + break; + + case AST__SLA_ECLEQ: + result = "ECLEQ"; + *comment = "Ecliptic (IAU 1980) to J2000.0 equatorial (FK5)"; + *nargs = 1; + arg[ 0 ] = "TDB of mean ecliptic (as MJD)"; + break; + + case AST__SLA_EQECL: + result = "EQECL"; + *comment = "Equatorial J2000.0 (FK5) to ecliptic (IAU 1980)"; + *nargs = 1; + arg[ 0 ] = "TDB of mean ecliptic (as MJD)"; + break; + + case AST__SLA_GALEQ: + result = "GALEQ"; + *comment = "Galactic (IAU 1958) to J2000.0 equatorial (FK5)"; + *nargs = 0; + break; + + case AST__SLA_EQGAL: + result = "EQGAL"; + *comment = "J2000.0 equatorial (FK5) to galactic (IAU 1958)"; + *nargs = 0; + break; + + case AST__SLA_FK5HZ: + result = "FK5HZ"; + *comment = "J2000.0 FK5 to ICRS (no PM or parallax)"; + arg[ 0 ] = "Julian epoch of FK5 coordinates"; + *nargs = 1; + break; + + case AST__SLA_HFK5Z: + result = "HFK5Z"; + *comment = "ICRS to J2000.0 FK5 (no PM or parallax)"; + arg[ 0 ] = "Julian epoch of FK5 coordinates"; + *nargs = 1; + break; + + case AST__SLA_GALSUP: + result = "GALSUP"; + *comment = "Galactic (IAU 1958) to supergalactic"; + *nargs = 0; + break; + + case AST__SLA_SUPGAL: + result = "SUPGAL"; + *comment = "Supergalactic to galactic (IAU 1958)"; + *nargs = 0; + break; + + case AST__HPCEQ: + result = "HPCEQ"; + *comment = "Helioprojective-Cartesian to J2000.0 equatorial (FK5)"; + *nargs = 4; + arg[ 0 ] = "Modified Julian Date of observation"; + arg[ 1 ] = "Heliocentric-Aries-Ecliptic X value at observer"; + arg[ 2 ] = "Heliocentric-Aries-Ecliptic Y value at observer"; + arg[ 3 ] = "Heliocentric-Aries-Ecliptic Z value at observer"; + break; + + case AST__EQHPC: + result = "EQHPC"; + *comment = "J2000.0 equatorial (FK5) to Helioprojective-Cartesian"; + *nargs = 4; + arg[ 0 ] = "Modified Julian Date of observation"; + arg[ 1 ] = "Heliocentric-Aries-Ecliptic X value at observer"; + arg[ 2 ] = "Heliocentric-Aries-Ecliptic Y value at observer"; + arg[ 3 ] = "Heliocentric-Aries-Ecliptic Z value at observer"; + break; + + case AST__HPREQ: + result = "HPREQ"; + *comment = "Helioprojective-Radial to J2000.0 equatorial (FK5)"; + *nargs = 4; + arg[ 0 ] = "Modified Julian Date of observation"; + arg[ 1 ] = "Heliocentric-Aries-Ecliptic X value at observer"; + arg[ 2 ] = "Heliocentric-Aries-Ecliptic Y value at observer"; + arg[ 3 ] = "Heliocentric-Aries-Ecliptic Z value at observer"; + break; + + case AST__EQHPR: + result = "EQHPR"; + *comment = "J2000.0 equatorial (FK5) to Helioprojective-Radial"; + *nargs = 4; + arg[ 0 ] = "Modified Julian Date of observation"; + arg[ 1 ] = "Heliocentric-Aries-Ecliptic X value at observer"; + arg[ 2 ] = "Heliocentric-Aries-Ecliptic Y value at observer"; + arg[ 3 ] = "Heliocentric-Aries-Ecliptic Z value at observer"; + break; + + case AST__HEEQ: + result = "HEEQ"; + *comment = "Helio-ecliptic to equatorial"; + *nargs = 1; + arg[ 0 ] = "Modified Julian Date of observation"; + break; + + case AST__EQHE: + result = "EQHE"; + *comment = "Equatorial to helio-ecliptic"; + *nargs = 1; + arg[ 0 ] = "Modified Julian Date of observation"; + break; + + case AST__J2000H: + result = "J2000H"; + *comment = "J2000 equatorial (dynamical) to ICRS"; + *nargs = 0; + break; + + case AST__HJ2000: + result = "HJ2000"; + *comment = "ICRS to J2000 equatorial (dynamical)"; + *nargs = 0; + break; + + case AST__SLA_DH2E: + result = "H2E"; + *comment = "Horizon to equatorial"; + *nargs = 2; + arg[ 0 ] = "Geodetic latitude of observer"; + arg[ 1 ] = "Magnitude of diurnal aberration vector"; + break; + + case AST__SLA_DE2H: + result = "E2H"; + *comment = "Equatorial to horizon"; + *nargs = 2; + arg[ 0 ] = "Geodetic latitude of observer"; + arg[ 1 ] = "Magnitude of diurnal aberration vector"; + break; + + case AST__R2H: + result = "R2H"; + *comment = "RA to Hour Angle"; + *nargs = 1; + arg[ 0 ] = "Local apparent sidereal time (radians)"; + break; + + case AST__H2R: + result = "H2R"; + *comment = "Hour Angle to RA"; + *nargs = 1; + arg[ 0 ] = "Local apparent sidereal time (radians)"; + break; + + } + +/* Return the result. */ + return result; +} + +static void Earth( double mjd, double earth[3], int *status ) { +/* +*+ +* Name: +* Earth + +* Purpose: +* Returns the AST__HAEC position of the earth at the specified time. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void Earth( double mjd, double earth[3], int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns the AST__HAEC position of the earth at the +* specified time. See astSTPConv for a description of the AST__HAEC +* coordinate systems. + +* Parameters: +* mjd +* Modified Julian date. +* earth +* The AST__HAEC position of the earth at the given date. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + double dpb[3]; /* Earth position (barycentric) */ + double dph[3]; /* Earth position (heliocentric) */ + double dvb[3]; /* Earth velocity (barycentric) */ + double dvh[3]; /* Earth velocity (heliocentric, AST__HAQC) */ + double ecmat[3][3];/* Equatorial to ecliptic matrix */ + int i; /* Loop count */ + +/* Initialize. */ + for( i = 0; i < 3; i++ ) earth[ i ] = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the position of the earth at the given date in the AST__HAQC coord + system (dph). */ + palEvp( mjd, 2000.0, dvb, dpb, dvh, dph ); + +/* Now rotate the earths position vector into AST__HAEC coords. */ + palEcmat( palEpj2d( 2000.0 ), ecmat ); + palDmxv( ecmat, dph, earth ); + +/* Convert from AU to metres. */ + earth[0] *= AST__AU; + earth[1] *= AST__AU; + earth[2] *= AST__AU; + +} + +static void Hgc( double mjd, double mat[3][3], double offset[3], int *status ) { +/* +*+ +* Name: +* Hgc + +* Purpose: +* Returns matrix and offset for converting AST__HGC positions to AST__HAEC. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void Hgc( double mjd, double mat[3][3], double offset[3], int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns a 3x3 matrix which rotates direction vectors +* given in the AST__HGC system to the AST__HAEC system at the +* specified date. It also returns the position of the origin of the +* AST__HGC system as an AST__HAEC position. See astSTPConv for a +* description of these coordinate systems. + +* Parameters: +* mjd +* Modified Julian date defining the coordinate systems. +* mat +* Matrix which rotates from AST__HGC to AST__HAEC. +* offset +* The origin of the AST__HGC system within the AST__HAEC system. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + double earth[3]; /* Earth position (heliocentric, AST__HAEC) */ + double len; /* Vector length */ + double xhg[3]; /* Unix X vector of AST__HGC system in AST__HAEC */ + double yhg[3]; /* Unix Y vector of AST__HGC system in AST__HAEC */ + double ytemp[3]; /* Un-normalized Y vector */ + double zhg[3]; /* Unix Z vector of AST__HGC system in AST__HAEC */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Initialize. */ + for( i = 0; i < 3; i++ ) { + for( j = 0; j < 3; j++ ) { + mat[i][j] = (i==j)?1.0:0.0; + } + offset[ i ] = 0.0; + } + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a unit vector parallel to the solar north pole at the given date. + This vector is expressed in AST__HAEC coords. This is the Z axis of the + AST__HGC system. */ + SolarPole( mjd, zhg, status ); + +/* Get the position of the earth at the given date in the AST__HAEC coord + system. */ + Earth( mjd, earth, status ); + +/* The HG Y axis is perpendicular to both the polar axis and the + sun-earth line. Obtain a Y vector by taking the cross product of the + two vectors, and then normalize it into a unit vector. */ + palDvxv( zhg, earth, ytemp ); + palDvn( ytemp, yhg, &len ); + +/* The HG X axis is perpendicular to both Z and Y, */ + palDvxv( yhg, zhg, xhg ); + +/* The HG X, Y and Z unit vectors form the columns of the required matrix. + The origins of the two systems are co-incident, so return the zero offset + vector initialised earlier. */ + for( i = 0; i < 3; i++ ) { + mat[ i ][ 0 ] = xhg[ i ]; + mat[ i ][ 1 ] = yhg[ i ]; + mat[ i ][ 2 ] = zhg[ i ]; + } + +} + +static void Gsec( double mjd, double mat[3][3], double offset[3], int *status ) { +/* +*+ +* Name: +* Gsec + +* Purpose: +* Returns matrix and offset for converting AST__GSEC positions to AST__HAEC. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void Gsec( double mjd, double mat[3][3], double offset[3], int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns a 3x3 matrix which rotates direction vectors +* given in the AST__GSEC system to the AST__HAEC system at the +* specified date. It also returns the position of the origin of the +* AST__GSEC system as an AST__HAEC position. See astSTPConv for a +* description of these coordinate systems. + +* Parameters: +* mjd +* Modified Julian date defining the coordinate systems. +* mat +* Matrix which rotates from AST__GSEC to AST__HAEC. +* offset +* The origin of the AST__GSEC system within the AST__HAEC system. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + double earth[3]; /* Earth position (heliocentric, AST__HAEC) */ + double pole[3]; /* Solar pole (AST__HAEC) */ + double len; /* Vector length */ + double xgs[3]; /* Unix X vector of AST__GSEC system in AST__HAEC */ + double ygs[3]; /* Unix Y vector of AST__GSEC system in AST__HAEC */ + double ytemp[3]; /* Un-normalized Y vector */ + double zgs[3]; /* Unix Z vector of AST__GSEC system in AST__HAEC */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Initialize. */ + for( i = 0; i < 3; i++ ) { + for( j = 0; j < 3; j++ ) { + mat[i][j] = (i==j)?1.0:0.0; + } + offset[ i ] = 0.0; + } + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the position of the earth at the given date in the AST__HAEC coord + system. */ + Earth( mjd, earth, status ); + +/* We need to find unit vectors parallel to the GSEC (X,Y,Z) axes, expressed + in terms of the AST__HAEC (X,Y,Z) axes. The GSEC X axis starts at the + earth and passes through the centre of the sun. This is just the + normalized opposite of the earth's position vector. */ + palDvn( earth, xgs, &len ); + xgs[0] *= -1.0; + xgs[1] *= -1.0; + xgs[2] *= -1.0; + +/* The GSEC Y axis is perpendicular to both the X axis and the ecliptic north + pole vector. So find the ecliptic north pole vector in AST__HAEC coords. */ + pole[ 0 ] = 0.0; + pole[ 1 ] = 0.0; + pole[ 2 ] = 1.0; + +/* Find the GSEC Y axis by taking the vector product of the X axis and + the ecliptic north pole vector, and then normalize it into a unit + vector. */ + palDvxv( pole, xgs, ytemp ); + palDvn( ytemp, ygs, &len ); + +/* The GSEC Z axis is perpendicular to both X and Y axis, and forms a + right-handed system. The resulting vector will be of unit length + since the x and y vectors are both of unit length, and are + perpendicular to each other. It therefore does not need to be + normalized.*/ + palDvxv( xgs, ygs, zgs ); + +/* The GSEC X, Y and Z unit vectors form the columns of the required matrix. */ + for( i = 0; i < 3; i++ ) { + mat[ i ][ 0 ] = xgs[ i ]; + mat[ i ][ 1 ] = ygs[ i ]; + mat[ i ][ 2 ] = zgs[ i ]; + offset[i] = earth[ i ]; + } + +} + +static void Haec( double mjd, double mat[3][3], double offset[3], int *status ) { +/* +*+ +* Name: +* Haec + +* Purpose: +* Returns matrix and offset for converting AST__HAEC positions to AST__HAEC. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void Haec( double mjd, double mat[3][3], double offset[3], int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns a 3x3 matrix which rotates direction vectors +* given in the AST__HAEC system to the AST__HAEC system at the +* specified date. It also returns the position of the origin of the +* AST__HAEC system as an AST__HAEC position. See astSTPConv for a +* description of these coordinate systems. + +* Parameters: +* mjd +* Modified Julian date defining the coordinate systems. +* mat +* Matrix which rotates from AST__HAEC to AST__HAEC. +* offset +* The origin of the AST__HAEC system within the AST__HAEC system. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Return an identity matrix and a zero offset vector. */ + for( i = 0; i < 3; i++ ) { + for( j = 0; j < 3; j++ ) { + mat[i][j] = (i==j)?1.0:0.0; + } + offset[ i ] = 0.0; + } + +} + +static void Haqc( double mjd, double mat[3][3], double offset[3], int *status ) { +/* +*+ +* Name: +* Haqc + +* Purpose: +* Returns matrix and offset for converting AST__HAQC positions to AST__HAEC. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void Haqc( double mjd, double mat[3][3], double offset[3], int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns a 3x3 matrix which rotates direction vectors +* given in the AST__HAQC system to the AST__HAEC system at the +* specified date. It also returns the position of the origin of the +* AST__HAQC system as an AST__HAEC position. See astSTPConv for a +* description of these coordinate systems. + +* Parameters: +* mjd +* Modified Julian date defining the coordinate systems. +* mat +* Matrix which rotates from AST__HAQC to AST__HAEC. +* offset +* The origin of the AST__HAQC system within the AST__HAEC system. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Initialise an identity matrix and a zero offset vector. */ + for( i = 0; i < 3; i++ ) { + for( j = 0; j < 3; j++ ) { + mat[i][j] = (i==j)?1.0:0.0; + } + offset[ i ] = 0.0; + } + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Return the required matrix. */ + palEcmat( palEpj2d( 2000.0 ), mat ); + return; +} + +static void Hpcc( double mjd, double obs[3], double mat[3][3], double offset[3], int *status ) { +/* +*+ +* Name: +* Hpcc + +* Purpose: +* Returns matrix and offset for converting AST__HPCC positions to +* AST__HAEC. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void Hpcc( double mjd, double obs[3], double mat[3][3], double offset[3], int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns a 3x3 matrix which rotates direction vectors +* given in the AST__HPCC system to the AST__HAEC system at the +* specified date. It also returns the position of the origin of the +* AST__HPCC system as an AST__HAEC position. See astSTPConv for a +* description of these coordinate systems. + +* Parameters: +* mjd +* Modified Julian date defining the coordinate systems. +* obs +* The observers position, in AST__HAEC, or NULL if the observer is +* at the centre of the earth. +* mat +* Matrix which rotates from AST__HPCC to AST__HAEC. +* offset +* The origin of the AST__HPCC system within the AST__HAEC system. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + double earth[3]; /* Earth position (heliocentric, AST__HAEC) */ + double pole[3]; /* Solar pole vector (AST__HAEC) */ + double len; /* Vector length */ + double xhpc[3]; /* Unix X vector of AST__HPCC system in AST__HAEC */ + double yhpc[3]; /* Unix Y vector of AST__HPCC system in AST__HAEC */ + double ytemp[3]; /* Un-normalized Y vector */ + double zhpc[3]; /* Unix Z vector of AST__HPCC system in AST__HAEC */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Initialize. */ + for( i = 0; i < 3; i++ ) { + for( j = 0; j < 3; j++ ) { + mat[i][j] = (i==j)?1.0:0.0; + } + offset[i] = 0.0; + } + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If no observers position was supplied, use the position of the earth + at the specified date in AST__HAEC coords. */ + if( !obs ) { + Earth( mjd, earth, status ); + obs = earth; + } + +/* We need to find unit vectors parallel to the HPCC (X,Y,Z) axes, expressed + in terms of the AST__HAEC (X,Y,Z) axes. The HPCC X axis starts at the + observer and passes through the centre of the sun. This is just the + normalized opposite of the supplied observer's position vector. */ + palDvn( obs, xhpc, &len ); + xhpc[0] *= -1.0; + xhpc[1] *= -1.0; + xhpc[2] *= -1.0; + +/* The HPC Y axis is perpendicular to both the X axis and the solar north + pole vector. So find the solar north pole vector in AST__HAEC coords. */ + SolarPole( mjd, pole, status ); + +/* Find the HPC Y axis by taking the vector product of the X axis and + the solar north pole vector, and then normalize it into a unit vector. + Note, HPC (X,Y,Z) axes form a left-handed system! */ + palDvxv( xhpc, pole, ytemp ); + palDvn( ytemp, yhpc, &len ); + +/* The HPC Z axis is perpendicular to both X and Y axis, and forms a + left-handed system. The resulting vector will be of unit length + since the x and y vectors are both of unit length, and are + perpendicular to each other. It therefore does not need to be + normalized.*/ + palDvxv( yhpc, xhpc, zhpc ); + +/* The HPC X, Y and Z unit vectors form the columns of the required matrix. */ + for( i = 0; i < 3; i++ ) { + mat[ i ][ 0 ] = xhpc[ i ]; + mat[ i ][ 1 ] = yhpc[ i ]; + mat[ i ][ 2 ] = zhpc[ i ]; + offset[i] = obs[ i ]; + } + +} + +static void Hprc( double mjd, double obs[3], double mat[3][3], double offset[3], int *status ) { +/* +*+ +* Name: +* Hprc + +* Purpose: +* Returns matrix and offset for converting AST__HPRC positions to +* AST__HAEC. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void Hprc( double mjd, double obs[3], double mat[3][3], double offset[3], int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns a 3x3 matrix which rotates direction vectors +* given in the AST__HPRC system to the AST__HAEC system at the +* specified date. It also returns the position of the origin of the +* AST__HPRC system as an AST__HAEC position. See astSTPConv for a +* description of these coordinate systems. + +* Parameters: +* mjd +* Modified Julian date defining the coordinate systems. +* obs +* The observers position, in AST__HAEC, or NULL if the observer is +* at the centre of the earth. +* mat +* Matrix which rotates from AST__HPRC to AST__HAEC. +* offset +* The origin of the AST__HPRC system within the AST__HAEC system. +*- +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + double pole[3]; /* Solar pole (AST__HAEC) */ + double earth[3]; /* Earth position (heliocentric, AST__HAEC) */ + double len; /* Vector length */ + double xhpr[3]; /* Unix X vector of AST__HPRC system in AST__HAEC */ + double yhpr[3]; /* Unix Y vector of AST__HPRC system in AST__HAEC */ + double ytemp[3]; /* Un-normalized Y vector */ + double zhpr[3]; /* Unix Z vector of AST__HPRC system in AST__HAEC */ + int i; /* Loop count */ + int j; /* Loop count */ + +/* Initialize. */ + for( i = 0; i < 3; i++ ) { + for( j = 0; j < 3; j++ ) { + mat[i][j] = (i==j)?1.0:0.0; + } + offset[i] = 0.0; + } + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If no observers position was supplied, use the position of the earth + at the specified date in AST__HAEC coords. */ + if( !obs ) { + Earth( mjd, earth, status ); + obs = earth; + } + +/* We need to find unit vectors parallel to the HPRC (X,Y,Z) axes, expressed + in terms of the AST__HAEC (X,Y,Z) axes. The HPRC Z axis starts at the + observer and passes through the centre of the sun. This is just the + normalized opposite of the supplied observer's position vector. */ + palDvn( obs, zhpr, &len ); + zhpr[0] *= -1.0; + zhpr[1] *= -1.0; + zhpr[2] *= -1.0; + +/* The HPR Y axis is perpendicular to both the Z axis and the solar north + pole vector. So find the solar north pole vector in AST__HAEC coords. */ + SolarPole( mjd, pole, status ); + +/* Find the HPR Y axis by taking the vector product of the Z axis and + the solar north pole vector, and then normalize it into a unit vector. + Note, HPR (X,Y,Z) axes form a left-handed system! */ + palDvxv( pole, zhpr, ytemp ); + palDvn( ytemp, yhpr, &len ); + +/* The HPRC X axis is perpendicular to both Y and Z axis, and forms a + left-handed system. The resulting vector will be of unit length + since the y and z vectors are both of unit length, and are + perpendicular to each other. It therefore does not need to be + normalized.*/ + palDvxv( zhpr, yhpr, xhpr ); + +/* The HPRC X, Y and Z unit vectors form the columns of the required matrix. */ + for( i = 0; i < 3; i++ ) { + mat[ i ][ 0 ] = xhpr[ i ]; + mat[ i ][ 1 ] = yhpr[ i ]; + mat[ i ][ 2 ] = zhpr[ i ]; + offset[ i ] = obs[ i ]; + } +} + +static void J2000H( int forward, int npoint, double *alpha, double *delta, int *status ){ +/* +* Name: +* J2000H + +* Purpose: +* Convert dynamical J2000 equatorial coords to ICRS. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void J2000H( int forward, int npoint, double *alpha, double *delta, int *status ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function converts the supplied dynamical J2000 equatorial coords +* to ICRS (or vice-versa). + +* Parameters: +* forward +* Do forward transformation? +* npoint +* Number of points to transform. +* alpha +* Pointer to longitude values. +* delta +* Pointer to latitude values. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + int i; /* Loop count */ + double rmat[3][3]; /* J2000 -> ICRS rotation matrix */ + double v1[3]; /* J2000 vector */ + double v2[3]; /* ICRS vector */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the J2000 to ICRS rotation matrix (supplied by P.T. Wallace) */ + palDeuler( "XYZ", -0.0068192*AS2R, 0.0166172*AS2R, 0.0146000*AS2R, + rmat ); + +/* Loop round all points. */ + for( i = 0; i < npoint; i++ ) { + +/* Convert from (alpha,delta) to 3-vector */ + palDcs2c( alpha[ i ], delta[ i ], v1 ); + +/* Rotate the 3-vector */ + if( forward ) { + palDmxv( rmat, v1, v2 ); + } else { + palDimxv( rmat, v1, v2 ); + } + +/* Convert from 3-vector to (alpha,delta) */ + palDcc2s( v2, alpha + i, delta + i ); + } +} + +void astSTPConv1_( double mjd, int in_sys, double in_obs[3], double in[3], + int out_sys, double out_obs[3], double out[3], int *status ){ +/* +*+ +* Name: +* astSTPConv1 + +* Purpose: +* Converts a 3D solar system position between specified STP coordinate +* systems. + +* Type: +* Protected function. + +* Synopsis: +* #include "slamap.h" +* void astSTPConv1( double mjd, int in_sys, double in_obs[3], +* double in[3], int out_sys, double out_obs[3], +* double out[3] ) + +* Class Membership: +* Frame method. + +* Description: +* This function converts a single 3D solar-system position from the +* specified input coordinate system to the specified output coordinate +* system. See astSTPConv for a list of supported coordinate systems. + +* Parameters: +* mjd +* The Modified Julian Date to which the coordinate systems refer. +* in_sys +* The coordinate system in which the input positions are supplied. +* in_obs +* The position of the observer in AST__HAEC coordinates. This is only +* needed if the input system is an observer-centric system. If this +* is not the case, a NULL pointer can be supplied. A NULL pointer +* can also be supplied to indicate that he observer is at the centre of +* the earth at the specified date. +* in +* A 3-element array holding the input position. +* out_sys +* The coordinate system in which the input positions are supplied. +* out_obs +* The position of the observer in AST__HAEC coordinates. This is only +* needed if the output system is an observer-centric system. If this +* is not the case, a NULL pointer can be supplied. A NULL pointer +* can also be supplied to indicate that he observer is at the centre of +* the earth at the specified date. +* out +* A 3-element array holding the output position. + +* Notes: +* - The "in" and "out" arrays may safely point to the same memory. +* - Output longitude values are always in the range 0 - 2.PI. + +*- +*/ + +/* Local Variables: */ + double *ins[ 3 ]; /* The input position */ + double *outs[ 3 ]; /* The output position */ + +/* Store pointers to the supplied arrays suitable for passing to STPConv. */ + ins[ 0 ] = in; + ins[ 1 ] = in + 1; + ins[ 2 ] = in + 2; + outs[ 0 ] = out; + outs[ 1 ] = out + 1; + outs[ 2 ] = out + 2; + +/* Convert the position. */ + STPConv( mjd, 0, 1, in_sys, in_obs, ins, out_sys, out_obs, outs, status ); + +} + +void astSTPConv_( double mjd, int n, int in_sys, double in_obs[3], + double *in[3], int out_sys, double out_obs[3], + double *out[3], int *status ){ +/* +*+ +* Name: +* astSTPConv + +* Purpose: +* Converts a set of 3D solar system positions between specified STP +* coordinate systems. + +* Type: +* Protected function. + +* Synopsis: +* #include "slamap.h" +* void astSTPConv( double mjd, int n, int in_sys, double in_obs[3], +* double *in[3], int out_sys, double out_obs[3], +* double *out[3] ) + +* Class Membership: +* Frame method. + +* Description: +* This function converts a set of 3D solar-system positions from +* the specified input coordinate system to the specified output +* coordinate system. + +* Parameters: +* mjd +* The Modified Julian Date to which the coordinate systems refer. +* in_sys +* The coordinate system in which the input positions are supplied +* (see below). +* in_obs +* The position of the observer in AST__HAEC coordinates. This is only +* needed if the input system is an observer-centric system. If this +* is not the case, a NULL pointer can be supplied. A NULL pointer +* can also be supplied to indicate that he observer is at the centre of +* the earth at the specified date. +* in +* A 3-element array holding the input positions. Each of the 3 +* elements should point to an array of "n" axis values. For spherical +* input systems, in[3] can be supplied as NULL, in which case a +* constant value of 1 AU will be used. +* out_sys +* The coordinate system in which the input positions are supplied +* (see below). +* out_obs +* The position of the observer in AST__HAEC coordinates. This is only +* needed if the output system is an observer-centric system. If this +* is not the case, a NULL pointer can be supplied. A NULL pointer +* can also be supplied to indicate that he observer is at the centre of +* the earth at the specified date. +* out +* A 3-element array holding the output positions. Each of the 3 +* elements should point to an array of "n" axis values. If in[3] is +* NULL, no values will be assigned to out[3]. + +* Notes: +* - The "in" and "out" arrays may safely point to the same memory. +* - Output longitude values are always in the range 0 - 2.PI. + +* Supported Coordinate Systems: +* Coordinate systems are either spherical or Cartesian, and are right +* handed (unless otherwise indicated). Spherical systems use axis 0 for +* longitude, axis 1 for latitude, and axis 2 for radius. Cartesian systems +* use 3 mutually perpendicular axes; X is axis 0 and points towards the +* intersection of the equator and the zero longitude meridian of the +* corresponding spherical system, Y is axis 1 and points towards longitude +* of +90 degrees, Z is axis 2 and points twowards the north pole. All +* angles are in radians and all distances are in metres. The following +* systems are supported: +* +* - AST__HAE: Heliocentric-aries-ecliptic spherical coordinates. Centred +* at the centre of the sun. The north pole points towards the J2000 +* ecliptic north pole, and meridian of zero longitude includes the +* J2000 equinox. +* +* - AST__HAEC: Heliocentric-aries-ecliptic cartesian coordinates. Origin +* at the centre of the sun. The Z axis points towards the J2000 ecliptic +* north pole, and the X axis points towards the J2000 equinox. +* +* - AST__HAQ: Heliocentric-aries-equatorial spherical coordinates. Centred +* at the centre of the sun. The north pole points towards the FK5 J2000 +* equatorial north pole, and meridian of zero longitude includes the +* FK5 J2000 equinox. +* +* - AST__HAQC: Heliocentric-aries-equatorial cartesian coordinates. Origin +* at the centre of the sun. The Z axis points towards the FK5 J2000 +* equatorial north pole, and the X axis points towards the FK5 J2000 +* equinox. +* +* - AST__HG: Heliographic spherical coordinates. Centred at the centre of +* the sun. North pole points towards the solar north pole at the given +* date. The meridian of zero longitude includes the sun-earth line at +* the given date. +* +* - AST__HGC: Heliographic cartesian coordinates. Origin at the centre of +* the sun. The Z axis points towards the solar north pole at the given +* date. The X axis is in the plane spanned by the Z axis, and the +* sun-earth line at the given date. +* +* - AST__HPC: Helioprojective-cartesian spherical coordinates. A +* left-handed system (that is, longitude increases westwards), centred +* at the specified observer position. The intersection of the +* zero-longitude meridian and the equator coincides with the centre of +* the sun as seen from the observers position. The zero longitude +* meridian includes the solar north pole at the specified date. +* +* - AST__HPCC: Helioprojective-cartesian cartesian coordinates. A +* left-handed system with origin at the specified observer position. The +* X axis points towards the centre of the sun as seen from the observers +* position. The X-Z plane includes the solar north pole at the specified +* date. +* +* - AST__HPR: Helioprojective-radial spherical coordinates. A left-handed +* system (that is, longitude increases westwards), centred at the +* specified observer position. The north pole points towards the centre +* of the sun as seen from the observers position. The zero longitude +* meridian includes the solar north pole at the specified date. +* +* - AST__HPRC: Helioprojective-radial cartesian coordinates. A left-handed +* system with origin at the specified observer position. The Z axis points +* towards the centre of the sun as seen from the observers position. The +* X-Z plane includes the solar north pole at the specified date. +* +* - AST__GSE: Geocentric-solar-ecliptic spherical coordinates. Centred at +* the centre of the earth at the given date. The north pole points towards +* the J2000 ecliptic north pole, and the meridian of zero longitude +* includes the Sun. +* +* - AST__GSEC: Geocentric-solar-ecliptic cartesian coordinates. Origin at +* the centre of the earth at the given date. The X axis points towards the +* centre of sun, and the X-Z plane contains the J2000 ecliptic north +* pole. Since the earth may not be exactly in the mean ecliptic of +* J2000, the Z axis will not in general correspond exactly to the +* ecliptic north pole. +*- +*/ + STPConv( mjd, 0, n, in_sys, in_obs, in, out_sys, out_obs, out, status ); +} + +static void STPConv( double mjd, int ignore_origins, int n, int in_sys, + double in_obs[3], double *in[3], int out_sys, + double out_obs[3], double *out[3], int *status ){ +/* +* Name: +* STPConv + +* Purpose: +* Convert a set of 3D solar system positions between specified STP +* coordinate systems. + +* Type: +* Private member function. + +* Synopsis: +* #include "slamap.h" +* void STPConv( double mjd, int ignore_origins, int n, int in_sys, +* double in_obs[3], double *in[3], int out_sys, +* double out_obs[3], double *out[3], int *status ){ + +* Class Membership: +* Frame method. + +* Description: +* This function converts a set of 3D solar-system positions from +* the specified input coordinate system to the specified output +* coordinate system. See astSTPConv for a list of the available +* coordinate systems. + +* Parameters: +* mjd +* The Modified Julian Date to which the coordinate systems refer. +* ignore_origins +* If non-zero, then the coordinate system definitions are modified so +* that all cartesian systems have the origin at the centre of the +* Sun. If zero, the correct origins are used for each individual +* system. +* n +* The number of positions to transform. +* in_sys +* The coordinate system in which the input positions are supplied +* in_obs +* The position of the observer in AST__HAEC coordinates. This is only +* needed if the input system is an observer-centric system. If this +* is not the case, a NULL pointer can be supplied. A NULL pointer +* can also be supplied to indicate that he observer is at the centre of +* the earth at the specified date. +* in +* A 3-element array holding the input positions. Each of the 3 +* elements should point to an array of "n" axis values. For spherical +* input systems, in[3] can be supplied as NULL, in which case a +* constant value of 1 AU will be used. +* out_sys +* The coordinate system in which the input positions are supplied +* (see "Supported Coordinate Systems" below). +* out_obs +* The position of the observer in AST__HAEC coordinates. This is only +* needed if the output system is an observer-centric system. If this +* is not the case, a NULL pointer can be supplied. A NULL pointer +* can also be supplied to indicate that he observer is at the centre of +* the earth at the specified date. +* out +* A 3-element array holding the input positions. Each of the 3 +* elements should point to an array of "n" axis values. For spherical +* output coordinates, out[2] may be NULL, in which case the output +* radius values are thrown away. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Output longitude values are always in the range 0 - 2.PI. +* - The "in" and "out" arrays may safely point to the same memory. +* - The contents of the output array is left unchanged if an error +* has already occurred. +*/ + +/* Local Variables: */ + double *out2; /* Pointer to output third axis values */ + double *px; /* Pointer to next X axis value */ + double *py; /* Pointer to next Y axis value */ + double *pz; /* Pointer to next Z axis value */ + double lat; /* Latitude value */ + double lng; /* Longitude value */ + double mat1[3][3]; /* Input->HAEC rotation matrix */ + double mat2[3][3]; /* Output->HAEC rotation matrix */ + double mat3[3][3]; /* HAEC->output rotation matrix */ + double mat4[3][3]; /* Input->output rotation matrix */ + double off1[3]; /* Origin of input system in HAEC coords */ + double off2[3]; /* Origin of output system in HAEC coords */ + double off3[3]; /* HAEC vector from output origin to input origin */ + double off4[3]; /* Position of input origin within output system */ + double p[3]; /* Current position */ + double q[3]; /* New position */ + double radius; /* Radius value */ + int cur_sys; /* Current system for output values */ + int i; /* Loop count */ + int j; /* Loop count */ + int inCsys; /* Input cartesian system */ + int outCsys; /* Output cartesian system */ + size_t nbyte; /* Amount of memory to copy */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If out[2] was supplied as null, allocate memory to hold third axis + values. Otherwise, use the supplied array. */ + nbyte = n*sizeof( double ); + if( !out[2] ) { + out2 = (double *) astMalloc( nbyte ); + } else { + out2 = out[2]; + } + +/* Copy the input data to the output data and note that the output values + are currently in the same system as the input values. */ + memcpy ( out[ 0 ], in[ 0 ], nbyte ); + memcpy ( out[ 1 ], in[ 1 ], nbyte ); + if( in[2] ) { + memcpy ( out2, in[ 2 ], nbyte ); + } else { + for( i = 0; i < n; i++ ) out2[ i ] = AST__AU; + } + cur_sys = in_sys; + +/* Skip the next bit if the output values are now in the required system. */ + if( cur_sys != out_sys ) { + +/* If the current system is spherical note the corresponding cartesian + system. If the current system is cartesian, use it. */ + if( cur_sys == AST__HG ){ + inCsys = AST__HGC; + } else if( cur_sys == AST__HAQ ){ + inCsys = AST__HAQC; + } else if( cur_sys == AST__HAE ){ + inCsys = AST__HAEC; + } else if( cur_sys == AST__GSE ){ + inCsys = AST__GSEC; + } else if( cur_sys == AST__HPC ){ + inCsys = AST__HPCC; + } else if( cur_sys == AST__HPR ){ + inCsys = AST__HPRC; + } else { + inCsys = cur_sys; + } + +/* Convert input spherical positions into the corresponding cartesian system, + putting the results in the "out" arrays. Modify the input system + accordingly. */ + if( cur_sys != inCsys ) { + px = out[ 0 ]; + py = out[ 1 ]; + pz = out2; + for( i = 0; i < n; i++ ) { + p[ 0 ] = *px; + p[ 1 ] = *py; + p[ 2 ] = *pz; + if( p[ 0 ] != AST__BAD && + p[ 1 ] != AST__BAD && + p[ 2 ] != AST__BAD ) { + palDcs2c( p[ 0 ], p[ 1 ], q ); + *(px++) = q[ 0 ]*p[ 2 ]; + *(py++) = q[ 1 ]*p[ 2 ]; + *(pz++) = q[ 2 ]*p[ 2 ]; + } else { + *(px++) = AST__BAD; + *(py++) = AST__BAD; + *(pz++) = AST__BAD; + } + } + + cur_sys = inCsys; + + } + } + +/* Skip the next bit if the output values are now in the required system. */ + if( cur_sys != out_sys ) { + +/* If the required output system is spherical, note the corresponding + cartesian system. If the required output system is cartesian, use it.*/ + if( out_sys == AST__HG ){ + outCsys = AST__HGC; + } else if( out_sys == AST__HAQ ){ + outCsys = AST__HAQC; + } else if( out_sys == AST__HAE ){ + outCsys = AST__HAEC; + } else if( out_sys == AST__GSE ){ + outCsys = AST__GSEC; + } else if( out_sys == AST__HPC ){ + outCsys = AST__HPCC; + } else if( out_sys == AST__HPR ){ + outCsys = AST__HPRC; + } else { + outCsys = out_sys; + } + +/* Skip the next bit if the output values are already in the required + output cartesian system. */ + if( cur_sys != outCsys ) { + +/* Obtain an offset vector and a rotation matrix which moves positions from + the current (Cartesian) system to the AST__HAEC system. The offset vector + returned by these functions is the AST__HAEC coordinates of the origin of + the current system. The matrix rotates direction vectors from the current + system to the AST__HAEC system. */ + if( cur_sys == AST__HGC ) { + Hgc( mjd, mat1, off1, status ); + + } else if( cur_sys == AST__HAEC ) { + Haec( mjd, mat1, off1, status ); + + } else if( cur_sys == AST__HAQC ) { + Haqc( mjd, mat1, off1, status ); + + } else if( cur_sys == AST__GSEC ) { + Gsec( mjd, mat1, off1, status ); + + } else if( cur_sys == AST__HPCC ) { + Hpcc( mjd, in_obs, mat1, off1, status ); + + } else if( cur_sys == AST__HPRC ) { + Hprc( mjd, in_obs, mat1, off1, status ); + + } else { + astError( AST__INTER, "astSTPConv(SlaMap): Unsupported input " + "cartesian coordinate system type %d (internal AST " + "programming error).", status, cur_sys ); + } + +/* Obtain an offset vector and a rotation matrix which moves positions from + the required output Cartesian system to the AST__HAEC system. */ + if( outCsys == AST__HGC ) { + Hgc( mjd, mat2, off2, status ); + + } else if( outCsys == AST__HAEC ) { + Haec( mjd, mat2, off2, status ); + + } else if( outCsys == AST__HAQC ) { + Haqc( mjd, mat2, off2, status ); + + } else if( outCsys == AST__GSEC ) { + Gsec( mjd, mat2, off2, status ); + + } else if( outCsys == AST__HPCC ) { + Hpcc( mjd, out_obs, mat2, off2, status ); + + } else if( outCsys == AST__HPRC ) { + Hprc( mjd, out_obs, mat2, off2, status ); + + } else { + astError( AST__INTER, "astSTPConv(SlaMap): Unsupported output " + "cartesian coordinate system type %d (internal AST " + "programming error).", status, outCsys ); + } + +/* Invert the second matrix to get the matrix which rotates AST__HAEC coords + to the output cartesian system. This an be done simply by transposing it + since all the matrices are 3D rotations. */ + for( i = 0; i < 3; i++ ) { + for( j = 0; j < 3; j++ ) mat3[ i ][ j ] = mat2[ j ][ i ]; + +/* Find the offset in AST__HAEC coords from the origin of the output + cartesian system to the origin of the current system. */ + off3[ i ] = off1[ i ] - off2[ i ]; + } + +/* Unless the origins are being ignored, use the above matrix to rotate the + above AST__HAEC offset into the output cartesian system. If origins are + being ignored, use an offset of zero. */ + if( ignore_origins ) { + off4[ 0 ] = 0.0; + off4[ 1 ] = 0.0; + off4[ 2 ] = 0.0; + } else { + palDmxv( mat3, off3, off4 ); + } + +/* Concatentate the two matrices to get the matrix which rotates from the + current system to the output cartesian system. */ + palDmxm( mat3, mat1, mat4 ); + +/* Use the matrix and offset to convert current positions to output + cartesian positions. */ + px = out[ 0 ]; + py = out[ 1 ]; + pz = out2; + + for( i = 0; i < n; i++ ) { + p[ 0 ] = *px; + p[ 1 ] = *py; + p[ 2 ] = *pz; + + if( p[ 0 ] != AST__BAD && + p[ 1 ] != AST__BAD && + p[ 2 ] != AST__BAD ) { + palDmxv( mat4, p, q ); + *(px++) = q[ 0 ] + off4[ 0 ]; + *(py++) = q[ 1 ] + off4[ 1 ]; + *(pz++) = q[ 2 ] + off4[ 2 ]; + } else { + *(px++) = AST__BAD; + *(py++) = AST__BAD; + *(pz++) = AST__BAD; + } + } + +/* Indicate that the output values are now in the required output + cartesian system. */ + cur_sys = outCsys; + + } + } + +/* Skip the next bit if the output values are now in the required system. */ + if( cur_sys != out_sys ) { + +/* The only reason why the output values may not be in the required output + system is because the output system is spherical. Convert output Cartesian + positions to output spherical positions. */ + px = out[ 0 ]; + py = out[ 1 ]; + pz = out2; + for( i = 0; i < n; i++ ) { + p[ 0 ] = *px; + p[ 1 ] = *py; + p[ 2 ] = *pz; + if( p[ 0 ] != AST__BAD && + p[ 1 ] != AST__BAD && + p[ 2 ] != AST__BAD ) { + palDvn( p, q, &radius ); + palDcc2s( q, &lng, &lat ); + *(px++) = palDranrm( lng ); + *(py++) = lat; + *(pz++) = radius; + } else { + *(px++) = AST__BAD; + *(py++) = AST__BAD; + *(pz++) = AST__BAD; + } + } + } + +/* If out[2] was supplied as null, free the memory used to hold third axis + values. */ + if( !out[2] ) out2 = (double *) astFree( (void *) out2 ); +} + +void astInitSlaMapVtab_( AstSlaMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSlaMapVtab + +* Purpose: +* Initialise a virtual function table for a SlaMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "slamap.h" +* void astInitSlaMapVtab( AstSlaMapVtab *vtab, const char *name ) + +* Class Membership: +* SlaMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SlaMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASlaMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->SlaAdd = SlaAdd; + vtab->SlaIsEmpty = SlaIsEmpty; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "SlaMap", + "Conversion between sky coordinate systems" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing an SlaMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* SlaMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated SlaMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated SlaMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated SlaMap which is to be merged with +* its neighbours. This should be a cloned copy of the SlaMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* SlaMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated SlaMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *new; /* Pointer to replacement Mapping */ + AstSlaMap *slamap; /* Pointer to SlaMap */ + const char *argdesc[ MAX_SLA_ARGS ]; /* Argument descriptions (junk) */ + const char *class; /* Pointer to Mapping class string */ + const char *comment; /* Pointer to comment string (junk) */ + double (*cvtargs)[ MAX_SLA_ARGS ]; /* Pointer to argument arrays */ + int *cvttype; /* Pointer to transformation type codes */ + int *narg; /* Pointer to argument count array */ + int done; /* Finished (no further simplification)? */ + int iarg; /* Loop counter for arguments */ + int icvt1; /* Loop initial value */ + int icvt2; /* Loop final value */ + int icvt; /* Loop counter for transformation steps */ + int ikeep; /* Index to store step being kept */ + int imap1; /* Index of first SlaMap to merge */ + int imap2; /* Index of last SlaMap to merge */ + int imap; /* Loop counter for Mappings */ + int inc; /* Increment for transformation step loop */ + int invert; /* SlaMap applied in inverse direction? */ + int istep; /* Loop counter for transformation steps */ + int keep; /* Keep transformation step? */ + int ngone; /* Number of Mappings eliminated */ + int nstep0; /* Original number of transformation steps */ + int nstep; /* Total number of transformation steps */ + int result; /* Result value to return */ + int simpler; /* Simplification possible? */ + int unit; /* Replacement Mapping is a UnitMap? */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* SlaMaps can only be merged if they are in series (or if there is + only one Mapping present, in which case it makes no difference), so + do nothing if they are not. */ + if ( series || ( *nmap == 1 ) ) { + +/* Initialise the number of transformation steps to be merged to equal + the number in the nominated SlaMap. */ + nstep = ( (AstSlaMap *) ( *map_list )[ where ] )->ncvt; + +/* Search adjacent lower-numbered Mappings until one is found which is + not an SlaMap. Accumulate the number of transformation steps + involved in any SlaMaps found. */ + imap1 = where; + while ( ( imap1 - 1 >= 0 ) && astOK ) { + class = astGetClass( ( *map_list )[ imap1 - 1 ] ); + if ( !astOK || strcmp( class, "SlaMap" ) ) break; + nstep += ( (AstSlaMap *) ( *map_list )[ imap1 - 1 ] )->ncvt; + imap1--; + } + +/* Similarly search adjacent higher-numbered Mappings. */ + imap2 = where; + while ( ( imap2 + 1 < *nmap ) && astOK ) { + class = astGetClass( ( *map_list )[ imap2 + 1 ] ); + if ( !astOK || strcmp( class, "SlaMap" ) ) break; + nstep += ( (AstSlaMap *) ( *map_list )[ imap2 + 1 ] )->ncvt; + imap2++; + } + +/* Remember the initial number of transformation steps. */ + nstep0 = nstep; + +/* Allocate memory for accumulating a list of all the transformation + steps involved in all the SlaMaps found. */ + cvttype = astMalloc( sizeof( int ) * (size_t) nstep ); + cvtargs = astMalloc( sizeof( double[ MAX_SLA_ARGS ] ) * (size_t) nstep ); + narg = astMalloc( sizeof( int ) * (size_t) nstep ); + +/* Loop to obtain the transformation data for each SlaMap being merged. */ + nstep = 0; + for ( imap = imap1; astOK && ( imap <= imap2 ); imap++ ) { + +/* Obtain a pointer to the SlaMap and note if it is being applied in + its inverse direction. */ + slamap = (AstSlaMap *) ( *map_list )[ imap ]; + invert = ( *invert_list )[ imap ]; + +/* Set up loop limits and an increment to scan the transformation + steps in each SlaMap in either the forward or reverse direction, as + dictated by the associated "invert" value. */ + icvt1 = invert ? slamap->ncvt - 1 : 0; + icvt2 = invert ? -1 : slamap->ncvt; + inc = invert ? -1 : 1; + +/* Loop through each transformation step in the SlaMap. */ + for ( icvt = icvt1; icvt != icvt2; icvt += inc ) { + +/* For simplicity, free any extra information stored with the conversion + step (it will be recreated as and when necessary). */ + slamap->cvtextra[ icvt ] = astFree( slamap->cvtextra[ icvt ] ); + +/* Store the transformation type code and use "CvtString" to determine + the associated number of arguments. Then store these arguments. */ + cvttype[ nstep ] = slamap->cvttype[ icvt ]; + (void) CvtString( cvttype[ nstep ], &comment, narg + nstep, + argdesc, status ); + if ( !astOK ) break; + for ( iarg = 0; iarg < narg[ nstep ]; iarg++ ) { + cvtargs[ nstep ][ iarg ] = slamap->cvtargs[ icvt ][ iarg ]; + } + +/* If the SlaMap is inverted, we must not only accumulate its + transformation steps in reverse, but also apply them in + reverse. For some steps this means swapping arguments, for some it + means changing the transformation type code to a complementary + value, and for others it means both. Define macros to perform each + of these changes. */ + +/* Macro to swap the values of two nominated arguments if the + transformation type code matches "code". */ +#define SWAP_ARGS( code, arg1, arg2 ) \ + if ( cvttype[ nstep ] == code ) { \ + double tmp = cvtargs[ nstep ][ arg1 ]; \ + cvtargs[ nstep ][ arg1 ] = cvtargs[ nstep ][ arg2 ]; \ + cvtargs[ nstep ][ arg2 ] = tmp; \ + } + +/* Macro to exchange a transformation type code for its inverse (and + vice versa). */ +#define SWAP_CODES( code1, code2 ) \ + if ( cvttype[ nstep ] == code1 ) { \ + cvttype[ nstep ] = code2; \ + } else if ( cvttype[ nstep ] == code2 ) { \ + cvttype[ nstep ] = code1; \ + } + +/* Use these macros to apply the changes where needed. */ + if ( invert ) { + +/* E-terms of aberration. */ +/* ---------------------- */ +/* Exchange addition and subtraction of E-terms. */ + SWAP_CODES( AST__SLA_ADDET, AST__SLA_SUBET ) + +/* Bessel-Newcomb pre-IAU 1976 (FK4) precession model. */ +/* --------------------------------------------------- */ +/* Exchange the starting and ending Besselian epochs. */ + SWAP_ARGS( AST__SLA_PREBN, 0, 1 ) + +/* IAU 1975 (FK5) precession model. */ +/* -------------------------------- */ +/* Exchange the starting and ending epochs. */ + SWAP_ARGS( AST__SLA_PREC, 0, 1 ) + +/* FK4 to FK5 (no proper motion or parallax). */ +/* ------------------------------------------ */ +/* Exchange FK5 to FK4 conversion for its inverse, and vice versa. */ + SWAP_CODES( AST__SLA_FK54Z, AST__SLA_FK45Z ) + +/* Geocentric apparent to mean place. */ +/* ---------------------------------- */ +/* Exchange the transformation code for its inverse and also exchange + the order of the date and equinox arguments. */ + SWAP_CODES( AST__SLA_AMP, AST__SLA_MAP ) + SWAP_ARGS( AST__SLA_AMP, 0, 1 ) + SWAP_ARGS( AST__SLA_MAP, 0, 1 ) + +/* Ecliptic coordinates to FK5 J2000.0 equatorial. */ +/* ------------------------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__SLA_ECLEQ, AST__SLA_EQECL ) + +/* Horizon to equatorial. */ +/* ---------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__SLA_DH2E, AST__SLA_DE2H ) + +/* Galactic coordinates to FK5 J2000.0 equatorial. */ +/* ------------------------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__SLA_GALEQ, AST__SLA_EQGAL ) + +/* ICRS coordinates to FK5 J2000.0 equatorial. */ +/* ------------------------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__SLA_HFK5Z, AST__SLA_FK5HZ ) + +/* Galactic to supergalactic coordinates. */ +/* -------------------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__SLA_GALSUP, AST__SLA_SUPGAL ) + +/* FK5 J2000 equatorial coordinates to Helioprojective-Cartesian. */ +/* -------------------------------------------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__EQHPC, AST__HPCEQ ) + +/* FK5 J2000 equatorial coordinates to Helioprojective-Radial. */ +/* ----------------------------------------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__EQHPR, AST__HPREQ ) + +/* FK5 J2000 equatorial coordinates to Helio-ecliptic. */ +/* --------------------------------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__EQHE, AST__HEEQ ) + +/* Dynamical J2000.0 to ICRS. */ +/* -------------------------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__J2000H, AST__HJ2000 ) + +/* HA to RA */ +/* -------- */ +/* Exchange the transformation code for its inverse. */ + SWAP_CODES( AST__H2R, AST__R2H ) + + } + +/* Undefine the local macros. */ +#undef SWAP_ARGS +#undef SWAP_CODES + +/* Count the transformation steps. */ + nstep++; + } + } + +/* Loop to simplify the sequence of transformation steps until no + further improvement is possible. */ + done = 0; + while ( astOK && !done ) { + +/* Examine each remaining transformation step in turn. */ + ikeep = -1; + for ( istep = 0; istep < nstep; istep++ ) { + +/* Initially assume we will retain the current step. */ + keep = 1; + +/* Eliminate redundant precession corrections. */ +/* ------------------------------------------- */ +/* First check if this is a redundant precession transformation + (i.e. the starting and ending epochs are the same). If so, then + note that it should not be kept. */ + if ( ( ( cvttype[ istep ] == AST__SLA_PREBN ) || + ( cvttype[ istep ] == AST__SLA_PREC ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], cvtargs[ istep ][ 1 ] ) ) { + keep = 0; + +/* The remaining simplifications act to combine adjacent + transformation steps, so only apply them while there are at least 2 + steps left. */ + } else if ( istep < ( nstep - 1 ) ) { + +/* Define a macro to test if two adjacent transformation type codes + have specified values. */ +#define PAIR_CVT( code1, code2 ) \ + ( ( cvttype[ istep ] == code1 ) && \ + ( cvttype[ istep + 1 ] == code2 ) ) + +/* Combine adjacent precession corrections. */ +/* ---------------------------------------- */ +/* If two precession corrections are adjacent, and have an equinox + value in common, then they may be combined into a single correction + by eliminating the common equinox. */ + if ( ( PAIR_CVT( AST__SLA_PREBN, AST__SLA_PREBN ) || + PAIR_CVT( AST__SLA_PREC, AST__SLA_PREC ) ) && + astEQUAL( cvtargs[ istep ][ 1 ], cvtargs[ istep + 1 ][ 0 ] ) ) { + +/* Retain the second correction, changing its first argument, and + eliminate the first correction. */ + cvtargs[ istep + 1 ][ 0 ] = cvtargs[ istep ][ 0 ]; + istep++; + +/* Eliminate redundant E-term handling. */ +/* ------------------------------------ */ +/* Check if adjacent steps implement a matching pair of corrections + for the E-terms of aberration with the same argument value. If so, + they will cancel, so eliminate them both. */ + } else if ( ( PAIR_CVT( AST__SLA_SUBET, AST__SLA_ADDET ) || + PAIR_CVT( AST__SLA_ADDET, AST__SLA_SUBET ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant FK4/FK5 conversions. */ +/* ---------------------------------------- */ +/* Similarly, check for a matching pair of FK4/FK5 conversions with + the same argument value and eliminate them both if possible. */ + } else if ( ( PAIR_CVT( AST__SLA_FK45Z, AST__SLA_FK54Z ) || + PAIR_CVT( AST__SLA_FK54Z, AST__SLA_FK45Z ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant ICRS/FK5 conversions. */ +/* ----------------------------------------- */ +/* Similarly, check for a matching pair of ICRS/FK5 conversions with + the same argument value and eliminate them both if possible. */ + } else if ( ( PAIR_CVT( AST__SLA_HFK5Z, AST__SLA_FK5HZ ) || + PAIR_CVT( AST__SLA_FK5HZ, AST__SLA_HFK5Z ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant geocentric apparent conversions. */ +/* ---------------------------------------------------- */ +/* As above, check for a matching pair of conversions with matching + argument values (note the argument order reverses for the two + directions) and eliminate them if possible. */ + } else if ( ( PAIR_CVT( AST__SLA_AMP, AST__SLA_MAP ) || + PAIR_CVT( AST__SLA_MAP, AST__SLA_AMP ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant ecliptic coordinate conversions. */ +/* ---------------------------------------------------- */ +/* This is handled in the same way as the FK4/FK5 case. */ + } else if ( ( PAIR_CVT( AST__SLA_ECLEQ, AST__SLA_EQECL ) || + PAIR_CVT( AST__SLA_EQECL, AST__SLA_ECLEQ ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant AzEl coordinate conversions. */ +/* ------------------------------------------------ */ + } else if ( ( PAIR_CVT( AST__SLA_DH2E, AST__SLA_DE2H ) || + PAIR_CVT( AST__SLA_DE2H, AST__SLA_DH2E ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant galactic coordinate conversions. */ +/* ---------------------------------------------------- */ +/* This is handled as above, except that there are no arguments to + check. */ + } else if ( PAIR_CVT( AST__SLA_GALEQ, AST__SLA_EQGAL ) || + PAIR_CVT( AST__SLA_EQGAL, AST__SLA_GALEQ ) ) { + istep++; + keep = 0; + +/* Eliminate redundant supergalactic coordinate conversions. */ +/* --------------------------------------------------------- */ +/* This is handled as above. */ + } else if ( PAIR_CVT( AST__SLA_GALSUP, AST__SLA_SUPGAL ) || + PAIR_CVT( AST__SLA_SUPGAL, AST__SLA_GALSUP ) ) { + istep++; + keep = 0; + +/* Eliminate redundant helioprojective-Cartesian coordinate conversions. */ +/* --------------------------------------------------------------------- */ + } else if ( ( PAIR_CVT( AST__HPCEQ, AST__EQHPC ) || + PAIR_CVT( AST__EQHPC, AST__HPCEQ ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 2 ], + cvtargs[ istep + 1 ][ 2 ] ) && + astEQUAL( cvtargs[ istep ][ 3 ], + cvtargs[ istep + 1 ][ 3 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant helioprojective-Radial coordinate conversions. */ +/* --------------------------------------------------------------------- */ + } else if ( ( PAIR_CVT( AST__HPREQ, AST__EQHPR ) || + PAIR_CVT( AST__EQHPR, AST__HPREQ ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 2 ], + cvtargs[ istep + 1 ][ 2 ] ) && + astEQUAL( cvtargs[ istep ][ 3 ], + cvtargs[ istep + 1 ][ 3 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant helio-ecliptic coordinate conversions. */ +/* ---------------------------------------------------------- */ + } else if ( ( PAIR_CVT( AST__EQHE, AST__HEEQ ) || + PAIR_CVT( AST__HEEQ, AST__EQHE ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Eliminate redundant dynamical J2000 coordinate conversions. */ +/* ----------------------------------------------------------- */ + } else if ( PAIR_CVT( AST__J2000H, AST__HJ2000 ) || + PAIR_CVT( AST__HJ2000, AST__J2000H ) ) { + istep++; + keep = 0; + +/* Eliminate redundant Hour Angle conversions. */ +/* ------------------------------------------- */ + } else if ( ( PAIR_CVT( AST__R2H, AST__H2R ) || + PAIR_CVT( AST__H2R, AST__R2H ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + + } + +/* Undefine the local macro. */ +#undef PAIR_CVT + } + +/* If the current transformation (possibly modified above) is being + kept, then increment the index that identifies its new location in + the list of transformation steps. */ + if ( keep ) { + ikeep++; + +/* If the new location is different to its current location, copy the + transformation data into the new location. */ + if ( ikeep != istep ) { + cvttype[ ikeep ] = cvttype[ istep ]; + for ( iarg = 0; iarg < narg[ istep ]; iarg++ ) { + cvtargs[ ikeep ][ iarg ] = cvtargs[ istep ][ iarg ]; + } + narg[ ikeep ] = narg[ istep ]; + } + } + } + +/* Note if no simplification was achieved on this iteration (i.e. the + number of transformation steps was not reduced). This is the signal + to quit. */ + done = ( ( ikeep + 1 ) >= nstep ); + +/* Note how many transformation steps now remain. */ + nstep = ikeep + 1; + } + +/* Determine how many Mappings can be eliminated by condensing all + those considered above into a single Mapping. */ + if ( astOK ) { + ngone = imap2 - imap1; + +/* Determine if the replacement Mapping can be a UnitMap (a null + Mapping). This will only be the case if all the transformation + steps were eliminated above. */ + unit = ( nstep == 0 ); + +/* Determine if simplification is possible. This will be the case if + (a) Mappings were eliminated ("ngone" is non-zero), or (b) the + number of transformation steps was reduced, or (c) the SlaMap(s) + can be replaced by a UnitMap, or (d) if there was initially only + one SlaMap present, its invert flag was set (this flag will always + be cleared in the replacement Mapping). */ + simpler = ngone || ( nstep < nstep0 ) || unit || + ( *invert_list )[ where ]; + +/* Do nothing more unless simplification is possible. */ + if ( simpler ) { + +/* If the replacement Mapping is a UnitMap, then create it. */ + if ( unit ) { + new = (AstMapping *) + astUnitMap( astGetNin( ( *map_list )[ where ] ), "", status ); + +/* Otherwise, create a replacement SlaMap and add each of the + remaining transformation steps to it. */ + } else { + new = (AstMapping *) astSlaMap( 0, "", status ); + for ( istep = 0; istep < nstep; istep++ ) { + AddSlaCvt( (AstSlaMap *) new, cvttype[ istep ], + cvtargs[ istep ], status ); + } + } + +/* Annul the pointers to the Mappings being eliminated. */ + if ( astOK ) { + for ( imap = imap1; imap <= imap2; imap++ ) { + ( *map_list )[ imap ] = astAnnul( ( *map_list )[ imap ] ); + } + +/* Insert the pointer and invert value for the new Mapping. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Move any subsequent Mapping information down to close the gap. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - ngone ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - ngone ] = ( *invert_list )[ imap ]; + } + +/* Blank out any information remaining at the end of the arrays. */ + for ( imap = ( *nmap - ngone ); imap < *nmap; imap++ ) { + ( *map_list )[ imap ] = NULL; + ( *invert_list )[ imap ] = 0; + } + +/* Decrement the Mapping count and return the index of the first + Mapping which was eliminated. */ + ( *nmap ) -= ngone; + result = imap1; + +/* If an error occurred, annul the new Mapping pointer. */ + } else { + new = astAnnul( new ); + } + } + } + +/* Free the memory used for the transformation steps. */ + cvttype = astFree( cvttype ); + cvtargs = astFree( cvtargs ); + narg = astFree( narg ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static void SlaAdd( AstSlaMap *this, const char *cvt, const double args[], int *status ) { +/* +*++ +* Name: +c astSlaAdd +f AST_SLAADD + +* Purpose: +* Add a celestial coordinate conversion to an SlaMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "slamap.h" +c void astSlaAdd( AstSlaMap *this, const char *cvt, const double args[] ) +f CALL AST_SLAADD( THIS, CVT, ARGS, STATUS ) + +* Class Membership: +* SlaMap method. + +* Description: +c This function adds one of the standard celestial coordinate +f This routine adds one of the standard celestial coordinate +* system conversions provided by the SLALIB Positional Astronomy +* Library (Starlink User Note SUN/67) to an existing SlaMap. +* +c When an SlaMap is first created (using astSlaMap), it simply +f When an SlaMap is first created (using AST_SLAMAP), it simply +c performs a unit (null) Mapping. By using astSlaAdd (repeatedly +f performs a unit (null) Mapping. By using AST_SLAADD (repeatedly +* if necessary), one or more coordinate conversion steps may then +* be added, which the SlaMap will perform in sequence. This allows +* multi-step conversions between a variety of celestial coordinate +* systems to be assembled out of the building blocks provided by +* SLALIB. +* +* Normally, if an SlaMap's Invert attribute is zero (the default), +* then its forward transformation is performed by carrying out +* each of the individual coordinate conversions specified by +c astSlaAdd in the order given (i.e. with the most recently added +f AST_SLAADD in the order given (i.e. with the most recently added +* conversion applied last). +* +* This order is reversed if the SlaMap's Invert attribute is +* non-zero (or if the inverse transformation is requested by any +* other means) and each individual coordinate conversion is also +* replaced by its own inverse. This process inverts the overall +* effect of the SlaMap. In this case, the first conversion to be +* applied would be the inverse of the one most recently added. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the SlaMap. +c cvt +f CVT = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string which identifies the +f A character string which identifies the +* celestial coordinate conversion to be added to the +* SlaMap. See the "SLALIB Conversions" section for details of +* those available. +c args +f ARGS( * ) = DOUBLE PRECISION (Given) +* An array containing argument values for the celestial +* coordinate conversion. The number of arguments required, and +* hence the number of array elements used, depends on the +* conversion specified (see the "SLALIB Conversions" +* section). This array is ignored +c and a NULL pointer may be supplied +* if no arguments are needed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - All coordinate values processed by an SlaMap are in +* radians. The first coordinate is the celestial longitude and the +* second coordinate is the celestial latitude. +* - When assembling a multi-stage conversion, it can sometimes be +* difficult to determine the most economical conversion path. For +* example, converting to the standard FK5 coordinate system as an +* intermediate stage is often sensible in formulating the problem, +* but may introduce unnecessary extra conversion steps. A solution +* to this is to include all the steps which are (logically) +c necessary, but then to use astSimplify to simplify the resulting +f necessary, but then to use AST_SIMPLIFY to simplify the resulting +* SlaMap. The simplification process will eliminate any steps +* which turn out not to be needed. +c - This function does not check to ensure that the sequence of +f - This routine does not check to ensure that the sequence of +* coordinate conversions added to an SlaMap is physically +* meaningful. + +* SLALIB Conversions: +* The following strings (which are case-insensitive) may be supplied +c via the "cvt" parameter to indicate which celestial coordinate +f via the CVT argument to indicate which celestial coordinate +* conversion is to be added to the SlaMap. Each string is derived +* from the name of the SLALIB routine that performs the +* conversion and the relevant documentation (SUN/67) should be +* consulted for details. Where arguments are needed by +* the conversion, they are listed in parentheses. Values for +c these arguments should be given, via the "args" array, in the +f these arguments should be given, via the ARGS array, in the +* order indicated. The argument names match the corresponding +* SLALIB routine arguments and their values should be given using +* exactly the same units, time scale, calendar, etc. as described +* in SUN/67: +* +* - "ADDET" (EQ): Add E-terms of aberration. +* - "SUBET" (EQ): Subtract E-terms of aberration. +* - "PREBN" (BEP0,BEP1): Apply Bessel-Newcomb pre-IAU 1976 (FK4) +* precession model. +* - "PREC" (EP0,EP1): Apply IAU 1975 (FK5) precession model. +* - "FK45Z" (BEPOCH): Convert FK4 to FK5 (no proper motion or parallax). +* - "FK54Z" (BEPOCH): Convert FK5 to FK4 (no proper motion or parallax). +* - "AMP" (DATE,EQ): Convert geocentric apparent to mean place. +* - "MAP" (EQ,DATE): Convert mean place to geocentric apparent. +* - "ECLEQ" (DATE): Convert ecliptic coordinates to FK5 J2000.0 equatorial. +* - "EQECL" (DATE): Convert equatorial FK5 J2000.0 to ecliptic coordinates. +* - "GALEQ": Convert galactic coordinates to FK5 J2000.0 equatorial. +* - "EQGAL": Convert FK5 J2000.0 equatorial to galactic coordinates. +* - "HFK5Z" (JEPOCH): Convert ICRS coordinates to FK5 J2000.0 equatorial. +* - "FK5HZ" (JEPOCH): Convert FK5 J2000.0 equatorial coordinates to ICRS. +* - "GALSUP": Convert galactic to supergalactic coordinates. +* - "SUPGAL": Convert supergalactic coordinates to galactic. +* - "J2000H": Convert dynamical J2000.0 to ICRS. +* - "HJ2000": Convert ICRS to dynamical J2000.0. +* - "R2H" (LAST): Convert RA to Hour Angle. +* - "H2R" (LAST): Convert Hour Angle to RA. +* +* For example, to use the "ADDET" conversion, which takes a single +* argument EQ, you should consult the documentation for the SLALIB +* routine SLA_ADDET. This describes the conversion in detail and +* shows that EQ is the Besselian epoch of the mean equator and +* equinox. +c This value should then be supplied to astSlaAdd in args[0]. +f This value should then be supplied to AST_SLAADD in ARGS(1). +* +* In addition the following strings may be supplied for more complex +* conversions which do not correspond to any one single SLALIB routine +* (DIURAB is the magnitude of the diurnal aberration vector in units +* of "day/(2.PI)", DATE is the Modified Julian Date of the observation, +* and (OBSX,OBSY,OBZ) are the Heliocentric-Aries-Ecliptic cartesian +* coordinates, in metres, of the observer): +* +* - "HPCEQ" (DATE,OBSX,OBSY,OBSZ): Convert Helioprojective-Cartesian coordinates to J2000.0 equatorial. +* - "EQHPC" (DATE,OBSX,OBSY,OBSZ): Convert J2000.0 equatorial coordinates to Helioprojective-Cartesian. +* - "HPREQ" (DATE,OBSX,OBSY,OBSZ): Convert Helioprojective-Radial coordinates to J2000.0 equatorial. +* - "EQHPR" (DATE,OBSX,OBSY,OBSZ): Convert J2000.0 equatorial coordinates to Helioprojective-Radial. +* - "HEEQ" (DATE): Convert helio-ecliptic coordinates to J2000.0 equatorial. +* - "EQHE" (DATE): Convert J2000.0 equatorial coordinates to helio-ecliptic. +* - "H2E" (LAT,DIRUAB): Convert horizon coordinates to equatorial. +* - "E2H" (LAT,DIURAB): Convert equatorial coordinates to horizon. +* +* Note, the "H2E" and "E2H" conversions convert between topocentric +* horizon coordinates (azimuth,elevation), and apparent local equatorial +* coordinates (hour angle,declination). Thus, the effects of diurnal +* aberration are taken into account in the conversions but the effects +* of atmospheric refraction are not. + +*-- +*/ + +/* Local Variables: */ + int cvttype; /* Conversion type code */ + +/* Check the inherited status. */ + if ( !astOK ) return; + +/* Validate the type string supplied and obtain the equivalent + conversion type code. */ + cvttype = CvtCode( cvt, status ); + +/* If the string was not recognised, then report an error. */ + if ( astOK && ( cvttype == AST__SLA_NULL ) ) { + astError( AST__SLAIN, + "astSlaAdd(%s): Invalid SLALIB sky coordinate conversion " + "type \"%s\".", status, astGetClass( this ), cvt ); + } + +/* Add the new conversion to the SlaMap. */ + AddSlaCvt( this, cvttype, args, status ); +} + +static int SlaIsEmpty( AstSlaMap *this, int *status ){ +/* +*+ +* Name: +* astSlaIsEmpty + +* Purpose: +* Indicates if a SlaMap is empty (i.e. has no conversions). + +* Type: +* Protected function. + +* Synopsis: +* #include "slamap.h" +* result = astSlaIsEmpty( AstSlaMap *this ) + +* Class Membership: +* SlaMap method. + +* Description: +* This function returns a flag indicating if the SlaMap is empty +* (i.e. has not yet had any conversions added to it using astSlaAdd). + +* Parameters: +* this +* The SlaMap. +*- +*/ + if( !astOK ) return 1; + return ( this->ncvt == 0 ); +} + + +static void SolarPole( double mjd, double pole[3], int *status ) { +/* +* Name: +* SolarPole + +* Purpose: +* Returns a unit vector along the solar north pole at the given date. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* void SolarPole( double mjd, double pole[3], int *status ) + +* Class Membership: +* SlaMap member function. + +* Description: +* This function returns a unit vector along the solar north pole at +* the given date, in the AST__HAEC coordinate system. + +* Parameters: +* mjd +* The date at which the solar north pole vector is required. +* pole +* An array holding the (X,Y,Z) components of the vector, in the +* AST__HAEC system. +* status +* Pointer to the inherited status variable. + +* Notes: +* - AST__BAD will be returned for all components of the vector if this +* function is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + double omega; + double sproj; + double inc; + double t1; + +/* Initialize. */ + pole[0] = AST__BAD; + pole[1] = AST__BAD; + pole[2] = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* First, we find the ecliptic longitude of the ascending node of the solar + equator on the ecliptic at the required date. This is based on the + equation in the "Explanatory Supplement to the Astronomical Alamanac", + section "Physical Ephemeris of the Sun": + + Omega = 75.76 + 0.01397*T degrees + + Note, the text at the start of the chapter says that "T" is measured in + centuries since J2000, but the equivalent expression in Table 15.4 is + only consistent with the above equation if "T" is measured in days since + J2000. We assume T is in days. The text does not explicitly say so, + but we assume that this longitude value (Omega) is with respect to the + mean equinox of J2000.0. */ + omega = 75.76 + 0.01397*( palEpj(mjd) - 2000.0 ); + +/* Convert this to the ecliptic longitude of the projection of the sun's + north pole onto the ecliptic, in radians. */ + sproj = ( omega - 90.0 )*D2R; + +/* Obtain a unit vector parallel to the sun's north pole, in terms of + the required ecliptic (X,Y,Z) axes, in which X points towards ecliptic + longitude/latitude ( 0, 0 ), Y axis points towards ecliptic + longitude/latitude ( 90, 0 ) degrees, and Z axis points towards the + ecliptic north pole. The inclination of the solar axis to the ecliptic + axis (7.25 degrees) is taken from the "Explanatory Supplement" section + "The Physical Ephemeris of the Sun". */ + inc = 7.25*D2R; + t1 = sin( inc ); + pole[ 0 ]= t1*cos( sproj ); + pole[ 1 ] = t1*sin( sproj ); + pole[ 2 ] = cos( inc ); + +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply an SlaMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* SlaMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes an SlaMap and a set of points encapsulated +* in a PointSet and transforms the points so as to perform the +* sequence of SLALIB sky coordinate conversions specified by +* previous invocations of astSlaAdd. + +* Parameters: +* this +* Pointer to the SlaMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the SlaMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstPointSet *result; /* Pointer to output PointSet */ + AstSlaMap *map; /* Pointer to SlaMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *alpha; /* Pointer to longitude array */ + double *args; /* Pointer to argument list for conversion */ + double *extra; /* Pointer to intermediate values */ + double *delta; /* Pointer to latitude array */ + double *p[3]; /* Pointers to arrays to be transformed */ + double *obs; /* Pointer to array holding observers position */ + int cvt; /* Loop counter for conversions */ + int ct; /* Conversion type */ + int end; /* Termination index for conversion loop */ + int inc; /* Increment for conversion loop */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + int start; /* Starting index for conversion loop */ + int sys; /* STP coordinate system code */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this); + +/* Obtain a pointer to the SlaMap. */ + map = (AstSlaMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + coordinate conversions needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse transformation, according + to the direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( this ) ) forward = !forward; + +/* Transform the coordinate values. */ +/* -------------------------------- */ +/* Use "alpha" and "delta" as synonyms for the arrays of longitude and latitude + coordinate values stored in the output PointSet. */ + if ( astOK ) { + alpha = ptr_out[ 0 ]; + delta = ptr_out[ 1 ]; + +/* Initialise the output coordinate values by copying the input ones. */ + (void) memcpy( alpha, ptr_in[ 0 ], sizeof( double ) * (size_t) npoint ); + (void) memcpy( delta, ptr_in[ 1 ], sizeof( double ) * (size_t) npoint ); + +/* We will loop to apply each SLALIB sky coordinate conversion in turn to the + (alpha,delta) arrays. However, if the inverse transformation was requested, + we must loop through these transformations in reverse order, so set up + appropriate limits and an increment to control this loop. */ + start = forward ? 0 : map->ncvt - 1; + end = forward ? map->ncvt : -1; + inc = forward ? 1 : -1; + +/* Loop through the coordinate conversions in the required order and obtain a + pointer to the argument list for the current conversion. */ + for ( cvt = start; cvt != end; cvt += inc ) { + args = map->cvtargs[ cvt ]; + extra = map->cvtextra[ cvt ]; + +/* Define a local macro as a shorthand to apply the code given as "function" + (the macro argument) to each element of the (alpha,delta) arrays in turn. + Before applying this conversion function, each element is first checked for + "bad" coordinates (indicated by the value AST__BAD) and appropriate "bad" + result values are assigned if necessary. */ +#define TRAN_ARRAY(function) \ + for ( point = 0; point < npoint; point++ ) { \ + if ( ( alpha[ point ] == AST__BAD ) || \ + ( delta[ point ] == AST__BAD ) ) { \ + alpha[ point ] = AST__BAD; \ + delta[ point ] = AST__BAD; \ + } else { \ + function \ + } \ + } + +/* Classify the SLALIB sky coordinate conversion to be applied. */ + ct = map->cvttype[ cvt ]; + switch ( ct ) { + +/* Add E-terms of aberration. */ +/* -------------------------- */ +/* Add or subtract (for the inverse) the E-terms from each coordinate pair + in turn, returning the results to the same arrays. */ + case AST__SLA_ADDET: + if ( forward ) { + TRAN_ARRAY(palAddet( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + } else { + TRAN_ARRAY(palSubet( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + } + break; + +/* Subtract E-terms of aberration. */ +/* ------------------------------- */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_SUBET: + if ( forward ) { + TRAN_ARRAY(palSubet( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + } else { + TRAN_ARRAY(palAddet( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + } + break; + +/* Apply Bessel-Newcomb pre-IAU 1976 (FK4) precession model. */ +/* --------------------------------------------------------- */ +/* Since we are transforming a sequence of points, first set up the required + precession matrix, swapping the argument order to get the inverse matrix + if required. */ + case AST__SLA_PREBN: + { + double epoch1 = forward ? args[ 0 ] : args[ 1 ]; + double epoch2 = forward ? args[ 1 ] : args[ 0 ]; + double precess_matrix[ 3 ][ 3 ]; + double vec1[ 3 ]; + double vec2[ 3 ]; + palPrebn( epoch1, epoch2, precess_matrix ); + +/* For each point in the (alpha,delta) arrays, convert to Cartesian + coordinates, apply the precession matrix, convert back to polar coordinates + and then constrain the longitude result to lie in the range 0 to 2*pi + (palDcc2s doesn't do this itself). */ + TRAN_ARRAY(palDcs2c( alpha[ point ], delta[ point ], vec1 ); + palDmxv( precess_matrix, vec1, vec2 ); + palDcc2s( vec2, alpha + point, delta + point ); + alpha[ point ] = palDranrm( alpha[ point ] );) + } + break; + +/* Apply IAU 1975 (FK5) precession model. */ +/* -------------------------------------- */ +/* This is handled in the same way as above, but using the appropriate FK5 + precession matrix. */ + case AST__SLA_PREC: + { + double epoch1 = forward ? args[ 0 ] : args[ 1 ]; + double epoch2 = forward ? args[ 1 ] : args[ 0 ]; + double precess_matrix[ 3 ][ 3 ]; + double vec1[ 3 ]; + double vec2[ 3 ]; + palPrec( epoch1, epoch2, precess_matrix ); + TRAN_ARRAY(palDcs2c( alpha[ point ], delta[ point ], vec1 ); + palDmxv( precess_matrix, vec1, vec2 ); + palDcc2s( vec2, alpha + point, delta + point ); + alpha[ point ] = palDranrm( alpha[ point ] );) + } + break; + +/* Convert FK4 to FK5 (no proper motion or parallax). */ +/* -------------------------------------------------- */ +/* Apply the conversion to each point. */ + case AST__SLA_FK45Z: + if ( forward ) { + TRAN_ARRAY(palFk45z( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + +/* The inverse transformation is also straightforward, except that we need a + couple of dummy variables as function arguments. */ + } else { + double dr1950; + double dd1950; + TRAN_ARRAY(palFk54z( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point, + &dr1950, &dd1950 );) + } + break; + +/* Convert FK5 to FK4 (no proper motion or parallax). */ +/* -------------------------------------------------- */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_FK54Z: + if ( forward ) { + double dr1950; + double dd1950; + TRAN_ARRAY(palFk54z( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point, + &dr1950, &dd1950 );) + } else { + TRAN_ARRAY(palFk45z( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + } + break; + +/* Convert geocentric apparent to mean place. */ +/* ------------------------------------------ */ +/* Since we are transforming a sequence of points, first set up the required + parameter array. Than apply this to each point in turn. */ + case AST__SLA_AMP: + { + + if( !extra ) { + + if( args[ 1 ] != eq_cache || + args[ 0 ] != ep_cache ) { + eq_cache = args[ 1 ]; + ep_cache = args[ 0 ]; + palMappa( eq_cache, ep_cache, amprms_cache ); + } + + extra = astStore( NULL, amprms_cache, + sizeof( double )*21 ); + map->cvtextra[ cvt ] = extra; + } + + if ( forward ) { + TRAN_ARRAY(palAmpqk( alpha[ point ], delta[ point ], + extra, + alpha + point, delta + point );) + +/* The inverse uses the same parameter array but converts from mean place + to geocentric apparent. */ + } else { + TRAN_ARRAY(palMapqkz( alpha[ point ], delta[ point ], + extra, + alpha + point, delta + point );) + } + } + break; + +/* Convert mean place to geocentric apparent. */ +/* ------------------------------------------ */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_MAP: + { + if( !extra ) { + + if( args[ 0 ] != eq_cache || + args[ 1 ] != ep_cache ) { + eq_cache = args[ 0 ]; + ep_cache = args[ 1 ]; + palMappa( eq_cache, ep_cache, amprms_cache ); + } + + extra = astStore( NULL, amprms_cache, + sizeof( double )*21 ); + map->cvtextra[ cvt ] = extra; + } + + if ( forward ) { + TRAN_ARRAY(palMapqkz( alpha[ point ], delta[ point ], + extra, + alpha + point, delta + point );) + } else { + TRAN_ARRAY(palAmpqk( alpha[ point ], delta[ point ], + extra, + alpha + point, delta + point );) + } + } + break; + +/* Convert ecliptic coordinates to J2000.0 equatorial. */ +/* --------------------------------------------------- */ +/* Since we are transforming a sequence of points, first set up the required + conversion matrix (the conversion is a rotation). */ + case AST__SLA_ECLEQ: + { + double convert_matrix[ 3 ][ 3 ]; + double precess_matrix[ 3 ][ 3 ]; + double rotate_matrix[ 3 ][ 3 ]; + double vec1[ 3 ]; + double vec2[ 3 ]; + +/* Obtain the matrix that precesses equatorial coordinates from J2000.0 to the + required date. Also obtain the rotation matrix that converts from + equatorial to ecliptic coordinates. */ + palPrec( 2000.0, palEpj( args[ 0 ] ), precess_matrix ); + palEcmat( args[ 0 ], rotate_matrix ); + +/* Multiply these matrices to give the overall matrix that converts from + equatorial J2000.0 coordinates to ecliptic coordinates for the required + date. */ + palDmxm( rotate_matrix, precess_matrix, convert_matrix ); + +/* Apply the conversion by transforming from polar to Cartesian coordinates, + multiplying by the inverse conversion matrix and converting back to polar + coordinates. Then constrain the longitude result to lie in the range + 0 to 2*pi (palDcc2s doesn't do this itself). */ + if ( forward ) { + TRAN_ARRAY(palDcs2c( alpha[ point ], delta[ point ], + vec1 ); + palDimxv( convert_matrix, vec1, vec2 ); + palDcc2s( vec2, alpha + point, delta + point ); + alpha[ point ] = palDranrm ( alpha[ point ] );) + +/* The inverse conversion is the same except that we multiply by the forward + conversion matrix (palDmxv instead of palDimxv). */ + } else { + TRAN_ARRAY(palDcs2c( alpha[ point ], delta[ point ], + vec1 ); + palDmxv( convert_matrix, vec1, vec2 ); + palDcc2s( vec2, alpha + point, delta + point ); + alpha[ point ] = palDranrm ( alpha[ point ] );) + } + } + break; + +/* Convert equatorial J2000.0 to ecliptic coordinates. */ +/* --------------------------------------------------- */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_EQECL: + { + double convert_matrix[ 3 ][ 3 ]; + double precess_matrix[ 3 ][ 3 ]; + double rotate_matrix[ 3 ][ 3 ]; + double vec1[ 3 ]; + double vec2[ 3 ]; + +/* Create the conversion matrix. */ + palPrec( 2000.0, palEpj( args[ 0 ] ), precess_matrix ); + palEcmat( args[ 0 ], rotate_matrix ); + palDmxm( rotate_matrix, precess_matrix, convert_matrix ); + +/* Apply it. */ + if ( forward ) { + TRAN_ARRAY(palDcs2c( alpha[ point ], delta[ point ], + vec1 ); + palDmxv( convert_matrix, vec1, vec2 ); + palDcc2s( vec2, alpha + point, delta + point ); + alpha[ point ] = palDranrm ( alpha[ point ] );) + } else { + TRAN_ARRAY(palDcs2c( alpha[ point ], delta[ point ], + vec1 ); + palDimxv( convert_matrix, vec1, vec2 ); + palDcc2s( vec2, alpha + point, delta + point ); + alpha[ point ] = palDranrm ( alpha[ point ] );) + } + } + break; + +/* Convert ICRS to J2000.0 equatorial. */ +/* ----------------------------------- */ +/* Apply the conversion to each point. */ + case AST__SLA_HFK5Z: + if ( forward ) { + double dr5; + double dd5; + TRAN_ARRAY(palHfk5z( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point, + &dr5, &dd5 );) + +/* The inverse simply uses the inverse SLALIB function. */ + } else { + TRAN_ARRAY(palFk5hz( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + } + break; + +/* Convert J2000.0 to ICRS equatorial. */ +/* ----------------------------------- */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_FK5HZ: + if ( forward ) { + TRAN_ARRAY(palFk5hz( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point );) + +/* The inverse simply uses the inverse SLALIB function. */ + } else { + double dr5; + double dd5; + TRAN_ARRAY(palHfk5z( alpha[ point ], delta[ point ], + args[ 0 ], + alpha + point, delta + point, + &dr5, &dd5 );) + } + break; + +/* Convert horizon to equatorial. */ +/* ------------------------------ */ +/* Apply the conversion to each point. */ + case AST__SLA_DH2E: + if ( forward ) { + TRAN_ARRAY(Dh2e( alpha[ point ], delta[ point ], + args[ 0 ], args[ 1 ], + alpha + point, delta + point, status );) + +/* The inverse simply uses the inverse SLALIB function. */ + } else { + TRAN_ARRAY(De2h( alpha[ point ], delta[ point ], + args[ 0 ], args[ 1 ], + alpha + point, delta + point, status );) + } + break; + +/* Convert equatorial to horizon. */ +/* ------------------------------ */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_DE2H: + if ( forward ) { + TRAN_ARRAY(De2h( alpha[ point ], delta[ point ], + args[ 0 ], args[ 1 ], + alpha + point, delta + point, status );) + +/* The inverse simply uses the inverse SLALIB function. */ + } else { + TRAN_ARRAY(Dh2e( alpha[ point ], delta[ point ], + args[ 0 ], args[ 1 ], + alpha + point, delta + point, status );) + } + break; + +/* Convert galactic coordinates to J2000.0 equatorial. */ +/* --------------------------------------------------- */ +/* Apply the conversion to each point. */ + case AST__SLA_GALEQ: + if ( forward ) { + TRAN_ARRAY(palGaleq( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + +/* The inverse simply uses the inverse SLALIB function. */ + } else { + TRAN_ARRAY(palEqgal( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + } + break; + +/* Convert J2000.0 equatorial to galactic coordinates. */ +/* --------------------------------------------------- */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_EQGAL: + if ( forward ) { + TRAN_ARRAY(palEqgal( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + } else { + TRAN_ARRAY(palGaleq( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + } + break; + +/* Convert galactic to supergalactic coordinates. */ +/* ---------------------------------------------- */ +/* Apply the conversion to each point. */ + case AST__SLA_GALSUP: + if ( forward ) { + TRAN_ARRAY(palGalsup( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + +/* The inverse simply uses the inverse SLALIB function. */ + } else { + TRAN_ARRAY(palSupgal( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + } + break; + +/* Convert supergalactic coordinates to galactic. */ +/* ---------------------------------------------- */ +/* This is the same as above, but with the forward and inverse cases + transposed. */ + case AST__SLA_SUPGAL: + if ( forward ) { + TRAN_ARRAY(palSupgal( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + } else { + TRAN_ARRAY(palGalsup( alpha[ point ], delta[ point ], + alpha + point, delta + point );) + } + break; + +/* If the conversion type was not recognised, then report an error + (this should not happen unless validation in astSlaAdd has failed + to detect a bad value previously). */ + default: + astError( AST__SLAIN, "astTransform(%s): Corrupt %s contains " + "invalid SLALIB sky coordinate conversion code (%d).", status, + astGetClass( this ), astGetClass( this ), + (int) ct ); + break; + +/* Convert any STP coordinates to J2000 equatorial. */ +/* ------------------------------------------------ */ + case AST__HPCEQ: + case AST__HPREQ: + case AST__HEEQ: + { + +/* Get the code for the appropriate 3D STP coordinate system to use. + Also, get a point to the observer position, if needed. */ + if( ct == AST__HPCEQ ) { + sys = AST__HPC; + obs = args + 1; + + } else if( ct == AST__HPREQ ) { + sys = AST__HPR; + obs = args + 1; + + } else { + sys = AST__GSE; + obs = NULL; + + } + +/* Store the 3D positions to be transformed. The supplied arrays are used + for the longitude and latitude values. No radius values are supplied. + (a value of 1AU will be used in the transformation). */ + p[0] = alpha; + p[1] = delta; + p[2] = NULL; + +/* Convert the supplied positions to (or from) AST__HEQ, ignoring the + distinction between the origin of the input and output systems (which + is appropriate since we are considering points at an infinite distance + from the observer). */ + if( forward ) { + STPConv( args[ 0 ], 1, npoint, sys, obs, p, + AST__HAQ, NULL, p, status ); + } else { + STPConv( args[ 0 ], 1, npoint, AST__HAQ, NULL, p, + sys, obs, p, status ); + } + } + break; + + +/* Convert J2000 equatorial to any STP coordinates. */ +/* ------------------------------------------------ */ +/* Same as above, but with forward and inverse cases transposed. */ + case AST__EQHPC: + case AST__EQHPR: + case AST__EQHE: + { + +/* Get the code for the appropriate 3D STP coordinate system to use. + Also, get a point to the observer position, if needed. */ + if( ct == AST__EQHPC ) { + sys = AST__HPC; + obs = args + 1; + + } else if( ct == AST__EQHPR ) { + sys = AST__HPR; + obs = args + 1; + + } else { + sys = AST__GSE; + obs = NULL; + + } + +/* Store the 3D positions to be transformed. The supplied arrays are used + for the longitude and latitude values. No radius values are supplied. + (a value of 1AU will be used in the transformation). */ + p[0] = alpha; + p[1] = delta; + p[2] = NULL; + +/* Convert the supplied positions from (or to) AST__HEQ, ignoring the + distinction between the origin of the input and output systems (which + is appropriate since we are considering points at an infinite distance + from the observer). */ + if( forward ) { + STPConv( args[ 0 ], 1, npoint, AST__HAQ, NULL, p, + sys, obs, p, status ); + } else { + STPConv( args[ 0 ], 1, npoint, sys, obs, p, + AST__HAQ, NULL, p, status ); + } + } + break; + +/* Convert dynamical J2000.0 to ICRS. */ +/* ---------------------------------- */ +/* Apply the conversion to each point. */ + case AST__J2000H: + J2000H( forward, npoint, alpha, delta, status ); + break; + +/* Convert ICRS to dynamical J2000.0 */ +/* ---------------------------------- */ + case AST__HJ2000: + J2000H( !(forward), npoint, alpha, delta, status ); + break; + +/* Convert HA to RA, or RA to HA */ +/* ----------------------------- */ +/* The forward and inverse transformations are the same. */ + case AST__H2R: + case AST__R2H: + TRAN_ARRAY( alpha[ point ] = args[ 0 ] - alpha[ point ]; ) + break; + + } + } + } + +/* If an error has occurred and a new PointSet may have been created, then + clean up by annulling it. In any case, ensure that a NULL result is + returned.*/ + if ( !astOK ) { + if ( !out ) result = astAnnul( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; + +/* Undefine macros local to this function. */ +#undef TRAN_ARRAY +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SlaMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SlaMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstSlaMap *in; /* Pointer to input SlaMap */ + AstSlaMap *out; /* Pointer to output SlaMap */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SlaMap structures. */ + in = (AstSlaMap *) objin; + out = (AstSlaMap *) objout; + +/* For safety, first clear any references to the input memory from the output + SlaMap. */ + out->cvtargs = NULL; + out->cvtextra = NULL; + out->cvttype = NULL; + +/* Allocate memory for the output array of argument list pointers. */ + out->cvtargs = astMalloc( sizeof( double * ) * (size_t) in->ncvt ); + +/* Allocate memory for the output array of extra (intermediate) values. */ + out->cvtextra = astMalloc( sizeof( double * ) * (size_t) in->ncvt ); + +/* If necessary, allocate memory and make a copy of the input array of sky + coordinate conversion codes. */ + if ( in->cvttype ) out->cvttype = astStore( NULL, in->cvttype, + sizeof( int ) + * (size_t) in->ncvt ); + +/* If OK, loop through each conversion in the input SlaMap and make a copy of + its argument list, storing the new pointer in the output argument list + array. */ + if ( astOK ) { + for ( cvt = 0; cvt < in->ncvt; cvt++ ) { + out->cvtargs[ cvt ] = astStore( NULL, in->cvtargs[ cvt ], + astSizeOf( in->cvtargs[ cvt ] ) ); + out->cvtextra[ cvt ] = astStore( NULL, in->cvtextra[ cvt ], + astSizeOf( in->cvtextra[ cvt ] ) ); + } + +/* If an error occurred while copying the argument lists, loop through the + conversions again and clean up by ensuring that the new memory allocated for + each argument list is freed. */ + if ( !astOK ) { + for ( cvt = 0; cvt < in->ncvt; cvt++ ) { + out->cvtargs[ cvt ] = astFree( out->cvtargs[ cvt ] ); + } + } + } + +/* If an error occurred, free all other memory allocated above. */ + if ( !astOK ) { + out->cvtargs = astFree( out->cvtargs ); + out->cvtextra = astFree( out->cvtextra ); + out->cvttype = astFree( out->cvttype ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SlaMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SlaMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSlaMap *this; /* Pointer to SlaMap */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Obtain a pointer to the SlaMap structure. */ + this = (AstSlaMap *) obj; + +/* Loop to free the memory containing the argument list for each sky coordinate + conversion. */ + for ( cvt = 0; cvt < this->ncvt; cvt++ ) { + this->cvtargs[ cvt ] = astFree( this->cvtargs[ cvt ] ); + this->cvtextra[ cvt ] = astFree( this->cvtextra[ cvt ] ); + } + +/* Free the memory holding the array of conversion types and the array of + argument list pointers. */ + this->cvtargs = astFree( this->cvtargs ); + this->cvtextra = astFree( this->cvtextra ); + this->cvttype = astFree( this->cvttype ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SlaMap objects. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SlaMap class to an output Channel. + +* Parameters: +* this +* Pointer to the SlaMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstSlaMap *this; /* Pointer to the SlaMap structure */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *argdesc[ MAX_SLA_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + const char *sval; /* Pointer to string value */ + int iarg; /* Loop counter for arguments */ + int icvt; /* Loop counter for conversion steps */ + int ival; /* Integer value */ + int nargs; /* Number of conversion arguments */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SlaMap structure. */ + this = (AstSlaMap *) this_object; + +/* Write out values representing the instance variables for the SlaMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Number of conversion steps. */ +/* --------------------------- */ +/* Regard this as "set" if it is non-zero. */ + ival = this->ncvt; + set = ( ival != 0 ); + astWriteInt( channel, "Nsla", set, 0, ival, "Number of conversion steps" ); + +/* Write out data for each conversion step... */ + for ( icvt = 0; icvt < this->ncvt; icvt++ ) { + +/* Conversion type. */ +/* ---------------- */ +/* Change each conversion type code into an equivalent string and + obtain associated descriptive information. If the conversion code + was not recognised, report an error and give up. */ + if ( astOK ) { + sval = CvtString( this->cvttype[ icvt ], &comment, &nargs, argdesc, status ); + if ( astOK && !sval ) { + astError( AST__SLAIN, + "astWrite(%s): Corrupt %s contains invalid SLALIB " + "sky coordinate conversion code (%d).", status, + astGetClass( channel ), astGetClass( this ), + (int) this->cvttype[ icvt ] ); + break; + } + +/* Create an appropriate keyword and write out the conversion code + information. */ + (void) sprintf( key, "Sla%d", icvt + 1 ); + astWriteString( channel, key, 1, 1, sval, comment ); + +/* Write out data for each conversion argument... */ + for ( iarg = 0; iarg < nargs; iarg++ ) { + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and write out the argument value, + accompanied by the descriptive comment obtained above. */ + (void) sprintf( key, "Sla%d%c", icvt + 1, ALPHABET[ iarg ] ); + astWriteDouble( channel, key, 1, 1, this->cvtargs[ icvt ][ iarg ], + argdesc[ iarg ] ); + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASlaMap and astCheckSlaMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SlaMap,Mapping) +astMAKE_CHECK(SlaMap) + +AstSlaMap *astSlaMap_( int flags, const char *options, int *status, ...) { +/* +*++ +* Name: +c astSlaMap +f AST_SLAMAP + +* Purpose: +* Create an SlaMap. + +* Type: +* Public function. + +* Synopsis: +c #include "slamap.h" +c AstSlaMap *astSlaMap( int flags, const char *options, ... ) +f RESULT = AST_SLAMAP( FLAGS, OPTIONS, STATUS ) + +* Class Membership: +* SlaMap constructor. + +* Description: +* This function creates a new SlaMap and optionally initialises +* its attributes. +* +* An SlaMap is a specialised form of Mapping which can be used to +* represent a sequence of conversions between standard celestial +* (longitude, latitude) coordinate systems. +* +* When an SlaMap is first created, it simply performs a unit +c (null) Mapping on a pair of coordinates. Using the astSlaAdd +f (null) Mapping on a pair of coordinates. Using the AST_SLAADD +c function, a series of coordinate conversion steps may then be +f routine, a series of coordinate conversion steps may then be +* added, selected from those provided by the SLALIB Positional +* Astronomy Library (Starlink User Note SUN/67). This allows +* multi-step conversions between a variety of celestial coordinate +* systems to be assembled out of the building blocks provided by +* SLALIB. +* +* For details of the individual coordinate conversions available, +c see the description of the astSlaAdd function. +f see the description of the AST_SLAADD routine. + +* Parameters: +c flags +f FLAGS = INTEGER (Given) +c This parameter is reserved for future use and should currently +f This argument is reserved for future use and should currently +* always be set to zero. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SlaMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SlaMap. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSlaMap() +f AST_SLAMAP = INTEGER +* A pointer to the new SlaMap. + +* Notes: +* - The Nin and Nout attributes (number of input and output +* coordinates) for an SlaMap are both equal to 2. The first +* coordinate is the celestial longitude and the second coordinate +* is the celestial latitude. All coordinate values are in radians. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSlaMap *new; /* Pointer to the new SlaMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SlaMap, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSlaMap( NULL, sizeof( AstSlaMap ), !class_init, &class_vtab, + "SlaMap", flags ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SlaMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SlaMap. */ + return new; +} + +AstSlaMap *astSlaMapId_( int flags, const char *options, ... ) { +/* +* Name: +* astSlaMapId_ + +* Purpose: +* Create an SlaMap. + +* Type: +* Private function. + +* Synopsis: +* #include "slamap.h" +* AstSlaMap *astSlaMapId_( int flags, const char *options, ... ) + +* Class Membership: +* SlaMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astSlaMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astSlaMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astSlaMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astSlaMap_. + +* Returned Value: +* The ID value associated with the new SlaMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSlaMap *new; /* Pointer to the new SlaMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SlaMap, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSlaMap( NULL, sizeof( AstSlaMap ), !class_init, &class_vtab, + "SlaMap", flags ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SlaMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new SlaMap. */ + return astMakeId( new ); +} + +AstSlaMap *astInitSlaMap_( void *mem, size_t size, int init, + AstSlaMapVtab *vtab, const char *name, + int flags, int *status ) { +/* +*+ +* Name: +* astInitSlaMap + +* Purpose: +* Initialise an SlaMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "slamap.h" +* AstSlaMap *astInitSlaMap( void *mem, size_t size, int init, +* AstSlaMapVtab *vtab, const char *name, +* int flags ) + +* Class Membership: +* SlaMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SlaMap object. It allocates memory (if necessary) to accommodate +* the SlaMap plus any additional data associated with the derived class. +* It then initialises an SlaMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for an SlaMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SlaMap is to be initialised. +* This must be of sufficient size to accommodate the SlaMap data +* (sizeof(SlaMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the SlaMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the SlaMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the SlaMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SlaMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astClass +* method). +* flags +* This parameter is reserved for future use. It is currently ignored. + +* Returned Value: +* A pointer to the new SlaMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSlaMap *new; /* Pointer to the new SlaMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSlaMapVtab( vtab, name ); + +/* Initialise a Mapping structure (the parent class) as the first component + within the SlaMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstSlaMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 2, 2, 1, 1 ); + + if ( astOK ) { + +/* Initialise the SlaMap data. */ +/* --------------------------- */ +/* The initial state is with no SLALIB conversions set, in which condition the + SlaMap simply implements a unit mapping. */ + new->ncvt = 0; + new->cvtargs = NULL; + new->cvtextra = NULL; + new->cvttype = NULL; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSlaMap *astLoadSlaMap_( void *mem, size_t size, + AstSlaMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSlaMap + +* Purpose: +* Load a SlaMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "slamap.h" +* AstSlaMap *astLoadSlaMap( void *mem, size_t size, +* AstSlaMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SlaMap loader. + +* Description: +* This function is provided to load a new SlaMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SlaMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a SlaMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the SlaMap is to be +* loaded. This must be of sufficient size to accommodate the +* SlaMap data (sizeof(SlaMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SlaMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SlaMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSlaMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SlaMap. If this is NULL, a pointer to +* the (static) virtual function table for the SlaMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SlaMap" is used instead. + +* Returned Value: +* A pointer to the new SlaMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstSlaMap *new; /* Pointer to the new SlaMap */ + char *sval; /* Pointer to string value */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *argdesc[ MAX_SLA_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + int iarg; /* Loop counter for arguments */ + int icvt; /* Loop counter for conversion steps */ + int nargs; /* Number of conversion arguments */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SlaMap. In this case the + SlaMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSlaMap ); + vtab = &class_vtab; + name = "SlaMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSlaMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SlaMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SlaMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Number of conversion steps. */ +/* --------------------------- */ +/* Read the number of conversion steps and allocate memory to hold + data for each step. */ + new->ncvt = astReadInt( channel, "nsla", 0 ); + if ( new->ncvt < 0 ) new->ncvt = 0; + new->cvttype = astMalloc( sizeof( int ) * (size_t) new->ncvt ); + new->cvtargs = astMalloc( sizeof( double * ) * (size_t) new->ncvt ); + new->cvtextra = astMalloc( sizeof( double * ) * (size_t) new->ncvt ); + +/* If an error occurred, ensure that all allocated memory is freed. */ + if ( !astOK ) { + new->cvttype = astFree( new->cvttype ); + new->cvtargs = astFree( new->cvtargs ); + new->cvtextra = astFree( new->cvtextra ); + +/* Otherwise, initialise the argument pointer array. */ + } else { + for ( icvt = 0; icvt < new->ncvt; icvt++ ) { + new->cvtargs[ icvt ] = NULL; + new->cvtextra[ icvt ] = NULL; + } + +/* Read in data for each conversion step... */ + for ( icvt = 0; icvt < new->ncvt; icvt++ ) { + +/* Conversion type. */ +/* ---------------- */ +/* Create an appropriate keyword and read the string representation of + the conversion type. */ + (void) sprintf( key, "sla%d", icvt + 1 ); + sval = astReadString( channel, key, NULL ); + +/* If no value was read, report an error. */ + if ( astOK ) { + if ( !sval ) { + astError( AST__BADIN, + "astRead(%s): An SLALIB sky coordinate conversion " + "type is missing from the input SlaMap data.", status, + astGetClass( channel ) ); + +/* Otherwise, convert the string representation into the required + conversion type code. */ + } else { + new->cvttype[ icvt ] = CvtCode( sval, status ); + +/* If the string was not recognised, report an error. */ + if ( new->cvttype[ icvt ] == AST__SLA_NULL ) { + astError( AST__BADIN, + "astRead(%s): Invalid SLALIB sky conversion " + "type \"%s\" in SlaMap data.", status, + astGetClass( channel ), sval ); + } + } + +/* Free the memory holding the string value. */ + sval = astFree( sval ); + } + +/* Obtain the number of arguments associated with the conversion and + allocate memory to hold them. */ + (void) CvtString( new->cvttype[ icvt ], &comment, &nargs, + argdesc, status ); + new->cvtargs[ icvt ] = astMalloc( sizeof( double ) * + (size_t) nargs ); + +/* Read in data for each argument... */ + if ( astOK ) { + for ( iarg = 0; iarg < nargs; iarg++ ) { + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and read each argument value. */ + (void) sprintf( key, "sla%d%c", icvt + 1, ALPHABET[ iarg ] ); + new->cvtargs[ icvt ][ iarg ] = astReadDouble( channel, key, + AST__BAD ); + } + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + +/* If an error occurred, clean up by deleting the new SlaMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SlaMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astSlaAdd_( AstSlaMap *this, const char *cvt, const double args[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SlaMap,SlaAdd))( this, cvt, args, status ); +} + +int astSlaIsEmpty_( AstSlaMap *this, int *status ) { + if ( !astOK ) return 1; + return (**astMEMBER(this,SlaMap,SlaIsEmpty))( this, status ); +} + diff --git a/slamap.h b/slamap.h new file mode 100644 index 0000000..57ed420 --- /dev/null +++ b/slamap.h @@ -0,0 +1,330 @@ +#if !defined( SLAMAP_INCLUDED ) /* Include this file only once */ +#define SLAMAP_INCLUDED +/* +*+ +* Name: +* slamap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SlaMap class. + +* Invocation: +* #include "slamap.h" + +* Description: +* This include file defines the interface to the SlaMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The SlaMap class encapsulates the conversions provided by the +* SLALIB library (SUN/67) for converting between different sky +* coordinate systems. Since, typically, a sequence of these +* SLALIB conversions is required, an SlaMap can be used to +* accumulate a series of conversions which it then applies in +* sequence. + +* Inheritance: +* The SlaMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* astTransform +* Use an SlaMap to transform a set of points. + +* Protected: +* astMapMerge +* Simplify a sequence of Mappings containing an SlaMap. + +* New Methods Defined: +* Public: +* astSlaAdd +* Add a coordinate conversion step to an SlaMap. + +* Private: +* None. + +* Other Class Functions: +* Public: +* astIsASlaMap +* Test class membership. +* astSlaMap +* Create an SlaMap. + +* Protected: +* astCheckSlaMap +* Validate class membership. +* astInitSlaMap +* Initialise an SlaMap. +* astLoadSlaMap +* Load an SlaMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstSlaMap +* SlaMap object type. + +* Protected: +* AstSlaMapVtab +* SlaMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 26-APR-1996 (RFWS): +* Original version. +* 26-SEP-1996 (RFWS): +* Added external interface and I/O facilities. +* 23-MAY-1997 (RFWS): +* Over-ride the astMapMerge method. +* 15-OCT-2002 (DSB): +* Added astSTPConv, astSTPConv1, and STP coordinate system macros. +* 8-JAN-2003 (DSB): +* Added protected astInitSlaMapVtab method. +* 22-FEB-2006 (DSB): +* Added cvtextra to the AstSlaMap structure. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + + +/* Macros */ +/* ====== */ +#if defined(astCLASS) /* Protected */ +#define AST__NOSTP -1 /* An invalid value for an STP coordinate system */ +#define AST__HAE 0 /* Heliocentric-aries-ecliptic spherical coordinates */ +#define AST__HAEC 1 /* Heliocentric-aries-ecliptic cartesian coordinates */ +#define AST__HAQ 2 /* Heliocentric-aries-equatorial spherical coordinates */ +#define AST__HAQC 3 /* Heliocentric-aries-equatorial cartesian coordinates */ +#define AST__HG 4 /* Heliographic spherical coordinates */ +#define AST__HGC 5 /* Heliographic cartesian coordinates */ +#define AST__HPC 6 /* Helioprojective-cartesian spherical coordinates */ +#define AST__HPCC 7 /* Helioprojective-cartesian cartesian coordinates */ +#define AST__HPR 8 /* Helioprojective-radial spherical coordinates */ +#define AST__HPRC 9 /* Helioprojective-radial cartesian coordinates */ +#define AST__GSE 10 /* Geocentric-solar-ecliptic spherical coordinates */ +#define AST__GSEC 11 /* Geocentric-solar-ecliptic cartesian coordinates */ +#endif + +/* One IAU astronomical unit, in metres. */ +#define AST__AU 1.49597870E11 + +/* One solar radius (top of photosphere?), in metres (from "The Explanatory + Supplement to the Astronomical Almanac"). */ +#define AST__SOLRAD 6.96E8 + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* SlaMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstSlaMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int *cvttype; /* Pointer to array of conversion types */ + double **cvtargs; /* Pointer to argument list pointer array */ + double **cvtextra; /* Pointer to intermediate values pointer array */ + int ncvt; /* Number of conversions to perform */ +} AstSlaMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSlaMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* SlaAdd)( AstSlaMap *, const char *, const double[], int * ); + int (* SlaIsEmpty)( AstSlaMap *, int * ); +} AstSlaMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstSlaMapGlobals { + AstSlaMapVtab Class_Vtab; + int Class_Init; + double Eq_Cache; + double Ep_Cache; + double Amprms_Cache[ 21 ]; +} AstSlaMapGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SlaMap) /* Check class membership */ +astPROTO_ISA(SlaMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstSlaMap *astSlaMap_( int, const char *, int *, ...); +#else +AstSlaMap *astSlaMapId_( int, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSlaMap *astInitSlaMap_( void *, size_t, int, AstSlaMapVtab *, + const char *, int, int * ); + +/* Vtab initialiser. */ +void astInitSlaMapVtab_( AstSlaMapVtab *, const char *, int * ); + +/* Loader. */ +AstSlaMap *astLoadSlaMap_( void *, size_t, AstSlaMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitSlaMapGlobals_( AstSlaMapGlobals * ); +#endif + +/* Other functions. */ +void astSTPConv1_( double, int, double[3], double[3], int, double[3], double[3], int * ); +void astSTPConv_( double, int, int, double[3], double *[3], int, double[3], double *[3], int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astSlaAdd_( AstSlaMap *, const char *, const double[], int * ); +int astSlaIsEmpty_( AstSlaMap *, int * ); + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSlaMap(this) astINVOKE_CHECK(SlaMap,this,0) +#define astVerifySlaMap(this) astINVOKE_CHECK(SlaMap,this,1) + +/* Test class membership. */ +#define astIsASlaMap(this) astINVOKE_ISA(SlaMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astSlaMap astINVOKE(F,astSlaMap_) +#else +#define astSlaMap astINVOKE(F,astSlaMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSlaMap(mem,size,init,vtab,name,flags) \ +astINVOKE(O,astInitSlaMap_(mem,size,init,vtab,name,flags,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSlaMapVtab(vtab,name) astINVOKE(V,astInitSlaMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSlaMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSlaMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckSlaMap to validate SlaMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +#define astSlaAdd(this,cvt,args) \ +astINVOKE(V,astSlaAdd_(astCheckSlaMap(this),cvt,args,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astSTPConv astSTPConv_ +#define astSTPConv1 astSTPConv1_ +#define astSlaIsEmpty(this) astINVOKE(V,astSlaIsEmpty_(astCheckSlaMap(this),STATUS_PTR)) +#endif + +#endif + + + + + diff --git a/specfluxframe.c b/specfluxframe.c new file mode 100644 index 0000000..ea00d49 --- /dev/null +++ b/specfluxframe.c @@ -0,0 +1,2189 @@ +/* +*class++ +* Name: +* SpecFluxFrame + +* Purpose: +* Compound spectrum/flux Frame. + +* Constructor Function: +c astSpecFluxFrame +f AST_SPECFLUXFRAME + +* Description: +* A SpecFluxFrame combines a SpecFrame and a FluxFrame into a single +* 2-dimensional compound Frame. Such a Frame can for instance be used +* to describe a Plot of a spectrum in which the first axis represents +* spectral position and the second axis represents flux. + +* Inheritance: +* The SpecFluxFrame class inherits from the CmpFrame class. + +* Attributes: +* The SpecFluxFrame class does not define any new attributes beyond +* those which are applicable to all CmpFrames. However, the attributes +* of the component Frames can be accessed as if they were attributes +* of the SpecFluxFrame. For instance, the SpecFluxFrame will recognise +* the "StdOfRest" attribute and forward access requests to the component +* SpecFrame. An axis index can optionally be appended to the end of any +* attribute name, in which case the request to access the attribute will +* be forwarded to the primary Frame defining the specified axis. + +* Functions: +c The SpecFluxFrame class does not define any new functions beyond those +f The SpecFluxFrame class does not define any new routines beyond those +* which are applicable to all CmpFrames. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 8-DEC-2004 (DSB): +* Original version. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SpecFluxFrame + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__COMP +#define LAST_SYSTEM AST__COMP + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "mapping.h" /* Coordinate Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "permmap.h" /* Coordinate permutation Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "axis.h" /* Coordinate axes */ +#include "cmpframe.h" /* Parent CmpFrame class */ +#include "tranmap.h" /* Separated transformation Mappings */ +#include "mathmap.h" /* Algebraic Mappings */ +#include "ratemap.h" /* Differential Mappings */ +#include "specframe.h" /* SpecFrame class */ +#include "fluxframe.h" /* FluxFrame class */ +#include "specfluxframe.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static const char *(* parent_gettitle)( AstFrame *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetTitle_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SpecFluxFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SpecFluxFrame,Class_Init) +#define class_vtab astGLOBAL(SpecFluxFrame,Class_Vtab) +#define gettitle_buff astGLOBAL(SpecFluxFrame,GetTitle_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char gettitle_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSpecFluxFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSpecFluxFrame *astSpecFluxFrameId_( void *, void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstFluxFrame *GetFluxFrame( AstSpecFluxFrame *, int, int * ); +static AstMapping *MakeMap2( AstSpecFluxFrame *, int * ); +static AstMapping *MakeMap3( AstSpecFluxFrame *, AstSpecFluxFrame *, int * ); +static AstMapping *MakeMapF( AstFluxFrame *, AstSpecFrame *, AstFluxFrame *, AstSpecFrame *, int * ); +static AstMapping *MakeMapI( AstFluxFrame *, AstSpecFrame *, AstFluxFrame *, AstSpecFrame *, int * ); +static AstSpecFrame *GetSpecFrame( AstSpecFluxFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static int MakeSFMapping( AstSpecFluxFrame *, AstSpecFluxFrame *, AstMapping **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static void Dump( AstObject *, AstChannel *, int * ); + +/* Member functions. */ +/* ================= */ + +static AstFluxFrame *GetFluxFrame( AstSpecFluxFrame *this, int std, int *status ){ +/* +* Name: +* GetFluxFrame + +* Purpose: +* Return a pointer to the FluxFrame in a FluxSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* AstFluxFrame *GetFluxFrame( AstSpecFluxFrame *this, int std, int *status ) + +* Class Membership: +* SpecFluxFrame member function. + +* Description: +* Returns a pointer to the FluxFrame in a SpecFluxFrame. + +* Parameters: +* this +* Pointer to the SpecFluxFrame. +* std +* If non zero, then the returned FluxFrame is a standardised copy of +* the FluxFrame in the supplied SpecFluxFrame, in which the System has +* been set explicitly (rather than potentially being defaulted), and +* the Units have been cleared to use default units appropriate to +* the flux System. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the FluxFrame. Should be freed using astAnnul when no +* longer needed. + +* Notes: +* NULL is returned if this function is invoked with the global error +* status set or if it should fail for any reason. +*/ + +/* Local Variables; */ + AstFluxFrame *ff; + AstFluxFrame *ret; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* The FluxFrame is always the second Frame in the parent CmpFrame. */ + ff = (AstFluxFrame *) ((AstCmpFrame *)this)->frame2; + +/* Produce a standardised copy of the FluxFrame if required, or clone the + above pointer otherwise. */ + if( std ) { + ret = astCopy( ff ); + astSetSystem( ret, astGetSystem( ff ) ); + astClearUnit( ret, 0 ); + } else { + ret = astClone( ff ); + } + +/* Annul the returned pointer if anything went wrong. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result. */ + return ret; +} + +static AstSpecFrame *GetSpecFrame( AstSpecFluxFrame *this, int std, int *status ){ +/* +* Name: +* GetSpecFrame + +* Purpose: +* Return a pointer to the SpecFrame in a FluxSpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* AstSpecFrame *GetSpecFrame( AstSpecFluxFrame *this, int std, int *status ) + +* Class Membership: +* SpecFluxFrame member function. + +* Description: +* Returns a pointer to the SpecFrame in a SpecFluxFrame. + +* Parameters: +* this +* Pointer to the SpecFluxFrame. +* std +* If non zero, then the returned SpecFrame is a standardised copy of +* the SpecFrame in the supplied SpecFluxFrame, in which the System +* and Units have been set explicitly to the values appropriate to the +* flux system in use in the FluxFrame in the supplied SpecFluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the FluxFrame. Should be freed using astAnnul when no +* longer needed. + +* Notes: +* NULL is returned if this function is invoked with the global error +* status set or if it should fail for any reason. +*/ + +/* Local Variables; */ + AstFluxFrame *ff; + AstSpecFrame *ret; + AstSpecFrame *sf; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the SpecFrame (the first Frame in the parent CmpFrame). */ + sf = (AstSpecFrame *) ((AstCmpFrame *)this)->frame1; + +/* If we want a standardised version of the SpecFrame... */ + if( std ) { + +/* The FluxFrame is always the second Frame in the parent CmpFrame. */ + ff = (AstFluxFrame *) ((AstCmpFrame *)this)->frame2; + +/* Produce a copy of the SpecFrame and set its System and Units + appropriate to the flux system (expressed in default units). */ + ret = astCopy( sf ); + astSetSystem( ret, astGetDensitySystem( ff ) ); + astSetUnit( ret, 0, astGetDensityUnit( ff ) ); + +/* If we are not standardising the SpecFrame, just return a clone of the + pointer in the parent CmpFrame. */ + } else { + ret = astClone( sf ); + } + +/* Annul the returned pointer if anything went wrong. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result. */ + return ret; +} + +static const char *GetTitle( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetTitle + +* Purpose: +* Obtain a pointer to the Title string for a SpecFluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* const char *GetTitle( AstFrame *this_frame, int *status ) + +* Class Membership: +* SpecFluxFrame member function (over-rides the astGetTitle method +* inherited from the CmpFrame class). + +* Description: +* This function returns a pointer to the Title string for a SpecFluxFrame. +* A pointer to a suitable default string is returned if no Title value has +* previously been set. + +* Parameters: +* this +* Pointer to the SpecFluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstSpecFluxFrame *this; + AstSpecFrame *sf; + AstFluxFrame *ff; + const char *result; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the SpecFluxFrame structure. */ + this = (AstSpecFluxFrame *) this_frame; + +/* See if a Title string has been set. If so, use the parent astGetTitle + method to obtain a pointer to it. */ + if ( astTestTitle( this ) ) { + result = (*parent_gettitle)( this_frame, status ); + +/* Otherwise, we will generate a default Title string. Obtain the values of the + SpecFrame's attributes that determine what this string will be. */ + } else { + ff = GetFluxFrame( this, 0, status ); + sf = GetSpecFrame( this, 0, status ); + + if( astOK ) { + sprintf( gettitle_buff, "%s versus %s", astGetLabel( ff, 0 ), + astGetLabel( sf, 0 ) ); + gettitle_buff[ 0 ] = toupper( gettitle_buff[ 0 ] ); + result = gettitle_buff; + } + + ff = astAnnul( ff ); + sf = astAnnul( sf ); + + } + +/* If an error occurred, clear the returned pointer value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +void astInitSpecFluxFrameVtab_( AstSpecFluxFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSpecFluxFrameVtab + +* Purpose: +* Initialise a virtual function table for a SpecFluxFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specfluxframe.h" +* void astInitSpecFluxFrameVtab( AstSpecFluxFrameVtab *vtab, const char *name ) + +* Class Membership: +* SpecFluxFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SpecFluxFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitCmpFrameVtab( (AstCmpFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASpecFluxFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstCmpFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + + parent_match = frame->Match; + frame->Match = Match; + + parent_subframe = frame->SubFrame; + frame->SubFrame = SubFrame; + + parent_gettitle = frame->GetTitle; + frame->GetTitle = GetTitle; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetDump( vtab, Dump, "SpecFluxFrame", + "Compound spectral/flux coordinate system description" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static AstMapping *MakeMap2( AstSpecFluxFrame *this, int *status ){ +/* +* Name: +* MakeMap2 + +* Purpose: +* Generate the second Mapping required by MakeSFMapping + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* AstMapping *MakeMap2( AstSpecFluxFrame *this, int *status ) + +* Class Membership: +* SpecFluxFrame member function. + +* Description: +* The second Mapping used by MakeSFMapping contains three Mappings in +* parallel which converts v1 (flux value) and x1 (spectral position) into +* default units, and passes the third axis (a copy of flux value) +* unchanged. + +* Parameters: +* this +* Pointer to the SpecFluxFrame to use. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the required Mapping, or NULL if the Mapping cannot be +* created. The Mapping will have 3 inputs and 3 outputs. + +* Notes: +* NULL is returned if this function is invoked with the global error +* status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFrame *f1; + AstFrame *f2; + AstFrameSet *fs; + AstMapping *ax1_map; + AstMapping *ax2_map; + AstMapping *ax3_map; + AstMapping *ret; + AstMapping *tmap; + +/* Initialise. */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Input 0 is the supplied FluxFrame value and output 0 is the corresponding + value in the default units for the FluxFrame system. Take a copy of the + supplied FluxFrame, and fix its System value (which may be a default value + based on the Units string), and then clear the Units so that it represents + default units for the System. */ + f1 = (AstFrame *) GetFluxFrame( this, 0, status ); + f2 = (AstFrame *) GetFluxFrame( this, 1, status ); + +/* Now, if conversion was possible, get the Mapping from the supplied + FluxFrame to the default units FluxFrame. */ + fs = astConvert( f1, f2, "" ); + f1 = astAnnul( f1 ); + f2 = astAnnul( f2 ); + + if( fs ) { + ax1_map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + +/* Input 1 is the supplied SpecFrame value and output 1 is the corresponding + value in the spectral system used by the flux system (wavelength or + frequency). Take a copy of the supplied SpecFrame, and fix its System + value to wavelength or frequency (depending on the System value of the + FluxFrame), and set up units of Hz or Angstrom (these are the spectral + position units used within the default flux units for a FluxFrame). */ + f1 = (AstFrame *) GetSpecFrame( this, 0, status ); + f2 = (AstFrame *) GetSpecFrame( this, 1, status ); + +/* Now, if conversion was possible, get the Mapping from the supplied + SpecFrame to the required SpecFrame. */ + fs = astConvert( f1, f2, "" ); + f1 = astAnnul( f1 ); + f2 = astAnnul( f2 ); + + if( fs ) { + ax2_map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + +/* Create a UnitMap for the 3rd axis. */ + ax3_map = (AstMapping *) astUnitMap( 1, "", status ); + +/* Create a parallel CmpMap containing the three Mappings. */ + tmap = (AstMapping *) astCmpMap( ax1_map, ax2_map, 0, "", status ); + ret = (AstMapping *) astCmpMap( tmap, ax3_map, 0, "", status ); + +/* Free remaining resources. */ + tmap = astAnnul( tmap ); + ax2_map = astAnnul( ax2_map ); + ax3_map = astAnnul( ax3_map ); + + } + ax1_map = astAnnul( ax1_map ); + } + +/* If an error has occurred, return NULL. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result */ + return ret; +} + +static AstMapping *MakeMap3( AstSpecFluxFrame *target, AstSpecFluxFrame *result, int *status ){ +/* +* Name: +* MakeMap3 + +* Purpose: +* Generate the third Mapping required by MakeSFMapping + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* AstMapping *MakeMap3( AstSpecFluxFrame *target, AstSpecFluxFrame *result, int *status ) + +* Class Membership: +* SpecFluxFrame member function. + +* Description: +* The third Mapping used by MakeSFMapping converts input (v1,x1) in +* default units to output (v2,x2) in default units. The third axis (x1) +* in original units is converted to x2 in original units. + +* Parameters: +* target +* Pointer to the first SpecFluxFrame. +* result +* Pointer to the second SpecFluxFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the required Mapping, or NULL if the Mapping cannot be +* created. The Mapping will have 3 inputs and 3 outputs. + +* Notes: +* NULL is returned if this function is invoked with the global error +* status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstFluxFrame *ff2; + AstFluxFrame *ff1; + AstFrameSet *fs; + AstMapping *fmap; + AstMapping *imap; + AstMapping *mapa; + AstMapping *mapb; + AstMapping *ret; + AstSpecFrame *sf2; + AstSpecFrame *sf1; + +/* Initialise */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* The first two inputs and outputs are related by a TranMap which + converts between standardised (v1,x1) and standardised (v2,x2). Get + pointers to the standardised SpecFrames and FluxFrames in the two + supplied SpecFluxFrames. */ + ff1 = GetFluxFrame( target, 1, status ); + sf1 = GetSpecFrame( target, 1, status ); + ff2 = GetFluxFrame( result, 1, status ); + sf2 = GetSpecFrame( result, 1, status ); + +/* Create the Mapping which defines the forward transformation of the + required TranMap. The forward transformation of this Mapping goes from + (v1,x1) to (v2,x2). */ + fmap = MakeMapF( ff1, sf1, ff2, sf2, status ); + +/* Create the Mapping which defines the inverse transformation of the + required TranMap. The inverse transformation of this Mapping goes from + (v2,x2) to (v1,x1). */ + imap = MakeMapI( ff1, sf1, ff2, sf2, status ); + +/* Combine these into a TranMap */ + if( fmap && imap ) { + mapa = (AstMapping *) astTranMap( fmap, imap, "", status ); + } else { + mapa = NULL; + } + +/* Free resources. */ + ff1 = astAnnul( ff1 ); + sf1 = astAnnul( sf1 ); + ff2 = astAnnul( ff2 ); + sf2 = astAnnul( sf2 ); + if( fmap ) fmap = astAnnul( fmap ); + if( imap ) imap = astAnnul( imap ); + +/* The third input and output are related by a Mapping which converts + between supplied (x1) and supplied (x2). Get pointers to the original + unmodified SpecFrames in the two supplied SpecFluxFrames. */ + sf1 = GetSpecFrame( target, 0, status ); + sf2 = GetSpecFrame( result, 0, status ); + +/* Find the Mapping from the first to the second. */ + fs = astConvert( sf1, sf2, "" ); + if( fs ) { + mapb = astGetMapping( fs, AST__BASE, AST__CURRENT ); + fs = astAnnul( fs ); + } else { + mapb = NULL; + } + +/* Free resources. */ + sf1 = astAnnul( sf1 ); + sf2 = astAnnul( sf2 ); + +/* Combine the two Mappings in parallel. */ + if( mapa && mapb ) ret = (AstMapping *) astCmpMap( mapa, mapb, 0, "", status ); + if( mapa ) mapa = astAnnul( mapa ); + if( mapb ) mapb = astAnnul( mapb ); + +/* If an error has occurred, return NULL. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result */ + return ret; +} + +static AstMapping *MakeMapF( AstFluxFrame *v1, AstSpecFrame *x1, + AstFluxFrame *v2, AstSpecFrame *x2, int *status ){ +/* +* Name: +* MakeMapF + +* Purpose: +* Generate the forward part of the third Mapping required by MakeSFMapping + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* AstMapping *MakeMapF( AstFluxFrame *v1, AstSpecFrame *x1, +* AstFluxFrame *v2, AstSpecFrame *x2, int *status ) + +* Class Membership: +* SpecFluxFrame member function. + +* Description: +* Theis creates a 2-input 2-output Mapping which transforms +* input (v1,x1) in default units to output (v2,x2) in default units. + +* Parameters: +* v1 +* Pointer to the standardised input FluxFrame. +* x1 +* Pointer to the standardised input SpecFrame. +* v2 +* Pointer to the standardised output FluxFrame. +* x2 +* Pointer to the standardised output SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the required Mapping, or NULL if the Mapping cannot be +* created. + +* Notes: +* NULL is returned if this function is invoked with the global error +* status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpMap *cmap1; + AstCmpMap *cmap2; + AstCmpMap *cmap3; + AstFrameSet *fs; + AstMapping *m; + AstMapping *ret; + AstMathMap *div; + AstPermMap *perm; + AstRateMap *rate; + AstUnitMap *unit; + const char *fwd[1]; + const char *inv[2]; + int inperm[ 2 ]; + int outperm[ 3 ]; + +/* Initialise */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* First create the required component Mappings. + --------------------------------------------- */ + +/* A Mapping which maps input spectral position (x1) into output spectral + position (x2). */ + fs = astConvert( x1, x2, "" ); + if( fs ) { + m = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* A 1-input 1-output Mapping in which the input is spectral position (x1) + and the output is the rate of change of output spectral position (x2) + with respect to input spectral position (x1). */ + rate = astRateMap( m, 0, 0, "", status ); + +/* A MathMap which is used to divide the flux value (v1) by the absolute rate + of change of x2 wrt x1 */ + fwd[ 0 ] = "out=in0/abs(in1)"; + inv[ 0 ] = "in0"; + inv[ 1 ] = "in1"; + div = astMathMap( 2, 1, 1, fwd, 2, inv, "", status ); + +/* A 1D UnitMap used to copy v1. */ + unit = astUnitMap( 1, "", status ); + +/* A PermMap which is used to produce an extra output copy of x1. */ + inperm[ 0 ] = 0; + inperm[ 1 ] = 2; + outperm[ 0 ] = 0; + outperm[ 1 ] = 1; + outperm[ 2 ] = 1; + perm = astPermMap( 2, inperm, 3, outperm, NULL, "", status ); + +/* Now combine these component Mappings together. + --------------------------------------------- */ + +/* First put the UnitMap and the RateMap in parallel. This produces a 2-in + 2-out Mapping in which the inputs are (v1,x1) and the outputs are + (v1,dx2/dx1). */ + cmap1 = astCmpMap( unit, rate, 0, "", status ); + +/* Now put this in series with the dividing MathMap. This results in a + 2-in, 1-out Mapping in which the inputs are v1 and x1 and the single + output is v2. */ + cmap2 = astCmpMap( cmap1, div, 1, "", status ); + +/* Now put this in parallel with the x1->x2 Mapping. This results in a + 3-in, 2-out Mapping in which the inputs are (v1,x1,x1) and the outputs + are (v2,x2). */ + cmap3 = astCmpMap( cmap2, m, 0, "", status ); + +/* Finally put this in series with the PermMap. This results in a 2-in, + 2-out Mapping in which the inputs are (v1,x1) and the outputs are + (v2,x2). */ + ret = (AstMapping *) astCmpMap( perm, cmap3, 1, "", status ); + +/* Free resources. */ + fs = astAnnul( fs ); + m = astAnnul( m ); + rate = astAnnul( rate ); + div= astAnnul( div ); + unit = astAnnul( unit ); + perm = astAnnul( perm ); + cmap1 = astAnnul( cmap1 ); + cmap2 = astAnnul( cmap2 ); + cmap3 = astAnnul( cmap3 ); + } + +/* If an error has occurred, return NULL. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result */ + return ret; +} + +static AstMapping *MakeMapI( AstFluxFrame *v1, AstSpecFrame *x1, + AstFluxFrame *v2, AstSpecFrame *x2, int *status ){ +/* +* Name: +* MakeMapI + +* Purpose: +* Generate the inverse part of the third Mapping required by MakeSFMapping + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* AstMapping *MakeMapI( AstFluxFrame *v1, AstSpecFrame *x1, +* AstFluxFrame *v2, AstSpecFrame *x2 ) + +* Class Membership: +* SpecFluxFrame member function. + +* Description: +* This creates a 2-input 2-output Mapping in which the inverse +* transformation transforms "outputs" representing (v2,x2) into +* "inputs" representing (v1,x1). + +* Parameters: +* v1 +* Pointer to the standardised input FluxFrame. +* x1 +* Pointer to the standardised input SpecFrame. +* v2 +* Pointer to the standardised output FluxFrame. +* x2 +* Pointer to the standardised output SpecFrame. + +* Returned Value: +* A pointer to the required Mapping, or NULL if the Mapping cannot be +* created. + +* Notes: +* NULL is returned if this function is invoked with the global error +* status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpMap *cmap1; + AstCmpMap *cmap2; + AstCmpMap *cmap3; + AstCmpMap *cmap4; + AstCmpMap *cmap5; + AstFrameSet *fs; + AstMapping *m; + AstMapping *ret; + AstMathMap *mult; + AstPermMap *perm; + AstRateMap *rate; + AstUnitMap *unit; + const char *fwd[1]; + const char *inv[2]; + int inperm[ 2 ]; + int outperm[ 3 ]; + +/* Initialise */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* We create a CmpMap in which the forward transformation foes from + (v2,x2) to (v1,x1) and we finally invert this Mapping to get the + required Mapping in which the *inverse* transformation goes from + (v2,x2) to (v1,x1). + + First create the required component Mappings. + --------------------------------------------- */ + +/* A Mapping which maps spectral position x1 into spectral position x2. */ + fs = astConvert( x1, x2, "" ); + if( fs ) { + m = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* A 1-input 1-output Mapping in which the input is spectral position x1 + and the output is the rate of change of spectral position x2 with + respect to spectral position x1. */ + rate = astRateMap( m, 0, 0, "", status ); + +/* Now invert "m" so that its forward transformation goes from x2 to x1. + The RateMap created above retains a copy of the original Invert flag + for "m" and uses it in preference to the current value when transforming + points. */ + astInvert( m ); + +/* A MathMap which is used to multiple the flux value v2 by the + absolute rate of change of x2 wrt x1 */ + fwd[ 0 ] = "out=in0*abs(in1)"; + inv[ 0 ] = "in0"; + inv[ 1 ] = "in1"; + mult = astMathMap( 2, 1, 1, fwd, 2, inv, "", status ); + +/* A 1D UnitMap used to copy various values. */ + unit = astUnitMap( 1, "", status ); + +/* A PermMap which is used to produce an extra copy of x1. */ + inperm[ 0 ] = 0; + inperm[ 1 ] = 2; + outperm[ 0 ] = 0; + outperm[ 1 ] = 1; + outperm[ 2 ] = 1; + perm = astPermMap( 2, inperm, 3, outperm, NULL, "", status ); + +/* Now combine these component Mappings together. + --------------------------------------------- */ + +/* First put the UnitMap and the RateMap in parallel. This produces a 2-in + 2-out Mapping in which the inputs are (v2,x1) and the outputs are + (v2,dx2/dx1). */ + cmap1 = astCmpMap( unit, rate, 0, "", status ); + +/* Now put this in series with the multiplying MathMap. This results in a + 2-in, 1-out Mapping in which the inputs are (v2,x1) and the single + output is v1. */ + cmap2 = astCmpMap( cmap1, mult, 1, "", status ); + +/* Now put this in parallel with the UnitMap to get a 3-in, 2-out Mapping + in which the inputs are (v2,x1,x1) and the outputs are (v1,x1). */ + cmap3 = astCmpMap( cmap2, unit, 0, "", status ); + +/* Now put this in series with the PermMap to get a 2-in, 2-out Mapping + in which the inputs are (v2,x1) and the outputs are (v1,x1). */ + cmap4 = astCmpMap( perm, cmap3, 1, "", status ); + +/* Now put the UnitMap in parallel with the (x2->x1 Mapping to get a + 2-in, 2-out Mapping in which the inputs are (v2,x2) and the outputs are + (v2,x1). */ + cmap5 = astCmpMap( unit, m, 0, "", status ); + +/* Finally put this in series with "cmap4" to get a 2-in 2-out Mapping + from (v2,x2) to (v1,x1). */ + ret = (AstMapping *) astCmpMap( cmap5, cmap4, 1, "", status ); + +/* Invert this so that the inverse transformation goes from (v2,x2) to + (v1,x1). */ + astInvert( ret ); + +/* Free resources. */ + fs = astAnnul( fs ); + m = astAnnul( m ); + rate = astAnnul( rate ); + mult = astAnnul( mult ); + unit = astAnnul( unit ); + perm = astAnnul( perm ); + cmap1 = astAnnul( cmap1 ); + cmap2 = astAnnul( cmap2 ); + cmap3 = astAnnul( cmap3 ); + cmap4 = astAnnul( cmap4 ); + cmap5 = astAnnul( cmap5 ); + } + +/* If an error has occurred, return NULL. */ + if( !astOK ) ret = astAnnul( ret ); + +/* Return the result */ + return ret; +} + +static int MakeSFMapping( AstSpecFluxFrame *target, AstSpecFluxFrame *result, + AstMapping **map, int *status ){ +/* +* Name: +* MakeSFMapping + +* Purpose: +* Generate a Mapping between two SpecFluxFrames. + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* int MakeSFMapping( AstSpecFluxFrame *target, AstSpecFluxFrame *result, +* AstMapping **map, int *status ) + +* Class Membership: +* SpecFluxFrame member function. + +* Description: +* This function takes two SpecFluxFrames and generates a Mapping that +* converts between them, taking account of differences in their +* coordinate systems, systems, units, etc. (but not allowing for any +* axis permutations). + +* Parameters: +* target +* Pointer to the first SpecFluxFrame. +* result +* Pointer to the second SpecFluxFrame. +* map +* Pointer to a location which is to receive a pointer to the +* returned Mapping. The forward transformation of this Mapping +* will convert from "target" coordinates to "result" +* coordinates, and the inverse transformation will convert in +* the opposite direction (all coordinate values in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Mapping could be generated, or zero if the two +* SpecFluxFrames are sufficiently un-related that no meaningful Mapping +* can be produced. + +* Notes: +* A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *map1; + AstMapping *map2; + AstMapping *map3; + AstMapping *map4; + AstMapping *map5; + AstMapping *tmap1; + AstMapping *tmap2; + AstMapping *tmap3; + AstMapping *tmap4; + int inperm[2]; + int match; + int outperm[3]; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise the returned values. */ + match = 0; + *map = NULL; + +/* Initialise other things. */ + map1 = NULL; + map2 = NULL; + map3 = NULL; + map4 = NULL; + map5 = NULL; + tmap1 = NULL; + tmap2 = NULL; + tmap3 = NULL; + tmap4 = NULL; + +/* At the top level, the required Mapping consists of five Mappings in + series. Inputs 0 and 1 of the total Mapping correspond to the SpecFrame + and FluxFrame in the target SpecFluxFrame. These are referred to as X1 + and V1. Outputs 0 and 1 of the total Mapping correspond to the SpecFrame + and FluxFrame in the result SpecFluxFrame. These are referred to as X2 + and V2. */ + +/* Map1 is a PermMap which copies v1 to its first output and x1 to its + second and third outputs. The inverse transformation copies v1 from + its first output and x1 from its third output. */ + inperm[ 0 ] = 2; + inperm[ 1 ] = 0; + outperm[ 0 ] = 1; + outperm[ 1 ] = 0; + outperm[ 2 ] = 0; + map1 = (AstMapping *) astPermMap( 2, inperm, 3, outperm, NULL, "", status ); + +/* Map2 contains three Mappings in parallel which converts v1 and x1 into + default units, and passes the third axis unchanged. */ + map2 = MakeMap2( target, status ); + +/* Map3 converts ( v1,x1) in default units to (v2,x2) in default units. + The third axis (x1) in original units is convert to x2 in original + units. */ + map3 = map2 ? MakeMap3( target, result, status ) : NULL; + +/* Map4 converts (v2,x2) in default units to (v2,x2) in original units + and passes the third axis unchanged. This is similar to Map2 but based + on the result ratherthan the target, and in the opposite direction. */ + if( map3 ) { + map4 = MakeMap2( result, status ); + if( map4 ) astInvert( map4 ); + } else { + map4 = NULL; + } + +/* Map5 is a PermMap which is the inverse of Map1. */ + map5 = map4 ? astCopy( map1 ) : NULL; + if( map5 ) astInvert( map5 ); + +/* Combine all 6 Mappings in series. */ + if( map5 ) { + tmap1 = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + tmap2 = (AstMapping *) astCmpMap( tmap1, map3, 1, "", status ); + tmap3 = (AstMapping *) astCmpMap( tmap2, map4, 1, "", status ); + tmap4 = (AstMapping *) astCmpMap( tmap3, map5, 1, "", status ); + +/* Return the simplified total Mapping. */ + *map = astSimplify( tmap4 ); + match = 1; + } + +/* Free resources. */ + if( map1 ) map1 = astAnnul( map1 ); + if( map2 ) map2 = astAnnul( map2 ); + if( map3 ) map3 = astAnnul( map3 ); + if( map4 ) map4 = astAnnul( map4 ); + if( map5 ) map5 = astAnnul( map5 ); + if( tmap1 ) tmap1 = astAnnul( tmap1 ); + if( tmap2 ) tmap2 = astAnnul( tmap2 ); + if( tmap3 ) tmap3 = astAnnul( tmap3 ); + if( tmap4 ) tmap4 = astAnnul( tmap4 ); + +/* If an error occurred, annul the returned Mapping and clear the + returned values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, + AstMapping **map, AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* SpecFluxFrame member function (over-rides the protected astMatch +* method inherited from the Frame class). + +* Description: +* This function matches a "template" SpecFluxFrame to a "target" Frame +* and determines whether it is possible to convert coordinates +* between them. If it is, a Mapping that performs the +* transformation is returned along with a new Frame that describes +* the coordinate system that results when this Mapping is applied +* to the "target" coordinate system. In addition, information is +* returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" Frame and +* "template" SpecFluxFrame from which they are derived. + +* Parameters: +* template +* Pointer to the template SpecFluxFrame. This describes the +* coordinate system (or set of possible coordinate systems) +* into which we wish to convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate +* system in which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* template SpecFluxFrame axis from which it is derived. If it is not +* derived from any template axis, a value of -1 will be +* returned instead. +* target_axes +* Address of a location where a pointer to int will be returned +* if the requested coordinate conversion is possible. This +* pointer will point at a dynamically allocated array of +* integers with one element for each axis of the "result" Frame +* (see below). It must be freed by the caller (using astFree) +* when no longer required. +* +* For each axis in the result Frame, the corresponding element +* of this array will return the (zero-based) index of the +* target Frame axis from which it is derived. If it is not +* derived from any target axis, a value of -1 will be returned +* instead. +* map +* Address of a location where a pointer to a new Mapping will +* be returned if the requested coordinate conversion is +* possible. If returned, the forward transformation of this +* Mapping may be used to convert coordinates between the +* "target" Frame and the "result" Frame (see below) and the +* inverse transformation will convert in the opposite +* direction. +* result +* Address of a location where a pointer to a new Frame will be +* returned if the requested coordinate conversion is +* possible. If returned, this Frame describes the coordinate +* system that results from applying the returned Mapping +* (above) to the "target" coordinate system. In general, this +* Frame will combine attributes from (and will therefore be +* more specific than) both the target Frame and the template +* SpecFluxFrame. In particular, when the template allows the +* possibility of transformaing to any one of a set of +* alternative coordinate systems, the "result" Frame will +* indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate +* conversion is possible. Otherwise zero is returned (this will +* not in itself result in an error condition). + +* Notes: +* - By default, the "result" Frame will have its number of axes +* and axis order determined by the "template" SpecFluxFrame. However, +* if the PreserveAxes attribute of the template SpecFluxFrame is +* non-zero, then the axis count and axis order of the "target" +* Frame will be used instead. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSpecFluxFrame *template; /* Pointer to template SpecFluxFrame structure */ + int match; /* Coordinate conversion possible? */ + int swap1; /* Template axes swapped? */ + int swap2; /* Target axes swapped? */ + int swap; /* Additional axis swap needed? */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the template SpecFluxFrame structure. */ + template = (AstSpecFluxFrame *) template_frame; + +/* If the target is not a SpecFluxFrame, use the results returned by the + parent Match method inherited from the CmpFrame class. */ + if( !astIsASpecFluxFrame( target ) ) { + match = (*parent_match)( template_frame, target, matchsub, template_axes, + target_axes, map, result, status ); + + +/* If the target is a SpecFluxFrame, see if we can convert between target + and template */ + } else { + +/* We must now decide how the order of the axes in the result Frame relates to + the order of axes in the target Frame. There are two factors involved. The + first depends on whether the axis permutation array for the template + SpecFluxFrame (whose method we are executing) causes an axis + reversal. Determine this by permuting axis index zero. */ + swap1 = ( astValidateAxis( template, 0, 1, "astMatch" ) != 0 ); + +/* The second factor depends on whether the axes of the target SpecFluxFrame + causes an axis reversal. Determine this by permuting axis index zero. */ + swap2 = ( astValidateAxis( target, 0, 1, "astMatch" ) != 0 ); + +/* Combine these to determine if an additional axis swap will be + needed. */ + swap = ( swap1 != swap2 ); + +/* Now check to see if this additional swap is permitted by the template's + Permute attribute. */ + match = ( !swap || astGetPermute( template ) ); + +/* Allocate the target and template axes arrays. */ + *template_axes = astMalloc( sizeof(int)*2 ); + *target_axes = astMalloc( sizeof(int)*2 ); + +/* If the Frames still match, we next set up the axis association + arrays. */ + if ( astOK && match ) { + +/* If the target axis order is to be preserved, then the target axis + association involves no permutation but the template axis + association may involve an axis swap. */ + if ( astGetPreserveAxes( template ) ) { + (*template_axes)[ 0 ] = swap; + (*template_axes)[ 1 ] = !swap; + (*target_axes)[ 0 ] = 0; + (*target_axes)[ 1 ] = 1; + +/* Otherwise, any swap applies to the target axis association + instead. */ + } else { + (*template_axes)[ 0 ] = 0; + (*template_axes)[ 1 ] = 1; + (*target_axes)[ 0 ] = swap; + (*target_axes)[ 1 ] = !swap; + } + +/* Use the target's "astSubFrame" method to create a new Frame (the + result Frame) with copies of the target axes in the required + order. This process also overlays the template attributes on to the + target Frame and returns a Mapping between the target and result + Frames which effects the required coordinate conversion. */ + match = astSubFrame( target, template, 2, *target_axes, *template_axes, + map, result ); + +/* If an error occurred, or conversion to the result Frame's + coordinate system was not possible, then free all memory, annul the + returned objects, and reset the returned value. */ + if ( !astOK || !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + } + } + +/* Return the result. */ + return match; +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a SpecFluxFrame and convert to the new coordinate system. + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* SpecFluxFrame member function (over-rides the protected astSubFrame +* method inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the +* axes from a "target" SpecFluxFrame and creates a new Frame with +* copies of the selected axes assembled in the requested order. It +* then optionally overlays the attributes of a "template" Frame on +* to the result. It returns both the resulting Frame and a Mapping +* that describes how to convert between the coordinate systems +* described by the target and result Frames. If necessary, this +* Mapping takes account of any differences in the Frames' +* attributes due to the influence of the template. + +* Parameters: +* target +* Pointer to the target SpecFluxFrame, from which axes are to be selected. +* template +* Pointer to the template Frame, from which new attributes for +* the result Frame are to be obtained. Optionally, this may be +* NULL, in which case no overlaying of template attributes will +* be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This +* number may be greater than or less than the number of axes in +* this Frame (or equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving +* a list of the (zero-based) axis indices of the axes to be +* selected from the target SpecFluxFrame. The order in which these +* are given determines the order in which the axes appear in +* the result Frame. If any of the values in this array is set +* to -1, the corresponding result axis will not be derived from +* the target Frame, but will be assigned default attributes +* instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This +* should contain a list of the template axes (given as +* zero-based axis indices) with which the axes of the result +* Frame are to be associated. This array determines which axes +* are used when overlaying axis-dependent attributes of the +* template on to the result. If any element of this array is +* set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not +* used and a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned +* Mapping. The forward transformation of this Mapping will +* describe how to convert coordinates from the coordinate +* system described by the target SpecFluxFrame to that described by +* the result Frame. The inverse transformation will convert in +* the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is +* possible between the target and the result Frame. Otherwise zero +* is returned and *map and *result are returned as NULL (but this +* will not in itself result in an error condition). In general, +* coordinate conversion should always be possible if no template +* Frame is supplied but may not always be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. + +* Implementation Deficiencies: +* - It is not clear that the method of handling "extra" axes is +* the best one, nor is the method of setting the "following" flag +* necessarily correct. However, it is also not obvious that this +* feature will ever be needed, so improvements have been left +* until the requirement is clearer. +*/ + +/* Local Variables: */ + AstMapping *tmpmap; /* Temporary Mapping pointer */ + AstPermMap *permmap; /* Pointer to PermMap */ + AstSpecFluxFrame *target; /* Pointer to target SpecFluxFrame structure */ + int match; /* Coordinate conversion is possible? */ + int perm[ 2 ]; /* Permutation array for axis swap */ + int result_swap; /* Swap result SpecFluxFrame coordinates? */ + int target_swap; /* Swap target SpecFluxFrame coordinates? */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* If the template is not a SpecFluxFrame we use the parent SubFrame + method inherited form the CmpFrame class. */ + if( !template || !astIsASpecFluxFrame( template ) || result_naxes != 2 ) { + match = (*parent_subframe)( target_frame, template, result_naxes, + target_axes, template_axes, map, result, status ); + +/* Otherwise... */ + } else { + +/* Obtain a pointer to the target SpecFluxFrame structure. */ + target = (AstSpecFluxFrame *) target_frame; + +/* Form the result from a copy of the target and then permute its axes + into the order required. */ + *result = astCopy( target ); + astPermAxes( *result, target_axes ); + +/* Overlay the template attributes on to the result SpecFrame. */ + astOverlay( template, template_axes, *result ); + +/* Generate a Mapping that takes account of changes in the coordinate + system (system, units, etc.) between the target SpecFluxFrame and the + result SpecFluxFrame. If this Mapping can be generated, set "match" to + indicate that coordinate conversion is possible. */ + match = MakeSFMapping( target, (AstSpecFluxFrame *) *result, map, status ); + +/* If a Mapping has been obtained, it will expect coordinate values to be + supplied in (flux,spec) pairs. Test whether we need to swap the + order of the target SpecFluxFrame coordinates to conform with this. */ + if ( astOK && match ) { + target_swap = ( astValidateAxis( target, 0, 1, "astSubFrame" ) != 0 ); + +/* Coordinates will also be delivered in (flux,spec) pairs, so check + to see whether the result SpecFluxFrame coordinate order should be + swapped. */ + result_swap = ( target_swap != ( target_axes[ 0 ] != 0 ) ); + +/* If either set of coordinates needs swapping, create a PermMap that + will swap a pair of coordinates. */ + permmap = NULL; + if ( target_swap || result_swap ) { + perm[ 0 ] = 1; + perm[ 1 ] = 0; + permmap = astPermMap( 2, perm, 2, perm, NULL, "", status ); + } + +/* If necessary, prefix this PermMap to the main Mapping. */ + if ( target_swap ) { + tmpmap = (AstMapping *) astCmpMap( permmap, *map, 1, "", status ); + *map = astAnnul( *map ); + *map = tmpmap; + } + +/* Also, if necessary, append it to the main Mapping. */ + if ( result_swap ) { + tmpmap = (AstMapping *) astCmpMap( *map, permmap, 1, "", status ); + *map = astAnnul( *map ); + *map = tmpmap; + } + +/* Annul the pointer to the PermMap (if created). */ + if ( permmap ) permmap = astAnnul( permmap ); + } + } + +/* If an error occurred, clean up by annulling the result pointers and + returning appropriate null values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + the axes of a SpecFluxFrame using the private macros defined for this + purpose at the start of this file. */ + +/* Copy constructor. */ +/* ----------------- */ + +/* Destructor. */ +/* ----------- */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SpecFluxFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SpecFluxFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the SpecFluxFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSpecFluxFrame *this; /* Pointer to the SpecFluxFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFluxFrame structure. */ + this = (AstSpecFluxFrame *) this_object; + +/* Write out values representing the instance variables for the + SpecFluxFrame class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASpecFluxFrame and astCheckSpecFluxFrame functions using + the macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SpecFluxFrame,CmpFrame) +astMAKE_CHECK(SpecFluxFrame) + +AstSpecFluxFrame *astSpecFluxFrame_( void *frame1_void, void *frame2_void, + const char *options, int *status, ...) { +/* +*++ +* Name: +c astSpecFluxFrame +f AST_SPECFLUXFRAME + +* Purpose: +* Create a SpecFluxFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "specfluxframe.h" +c AstSpecFluxFrame *astSpecFluxFrame( AstSpecFrame *frame1, AstFluxFrame *frame2, +c const char *options, ... ) +f RESULT = AST_SPECFLUXFRAME( FRAME1, FRAME2, OPTIONS, STATUS ) + +* Class Membership: +* SpecFluxFrame constructor. + +* Description: +* This function creates a new SpecFluxFrame and optionally initialises +* its attributes. +* +* A SpecFluxFrame combines a SpecFrame and a FluxFrame into a single +* 2-dimensional compound Frame. Such a Frame can for instance be used +* to describe a Plot of a spectrum in which the first axis represents +* spectral position and the second axis represents flux. + +* Parameters: +c frame1 +f FRAME1 = INTEGER (Given) +* Pointer to the SpecFrame. This will form the first axis in the +* new SpecFluxFrame. +c frame2 +f FRAME2 = INTEGER (Given) +* Pointer to the FluxFrame. This will form the second axis in the +* new SpecFluxFrame. The "SpecVal" attribute of this FluxFrame is +* not used by the SpecFluxFrame class and so may be set to AST__BAD +* when the FluxFrame is created. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SpecFluxFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SpecFluxFrame. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSpecFluxFrame() +f AST_SPECFLUXFRAME = INTEGER +* A pointer to the new SpecFluxFrame. + +* Notes: +* - The supplied Frame pointers are stored directly, rather than +* being used to create deep copies of the supplied Frames. This means +* that any subsequent changes made to the Frames via the supplied +* pointers will result in equivalent changes being visible in the +* SpecFluxFrame. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- + +* Implementation Notes: +* - This function implements the basic SpecFluxFrame constructor which +* is available via the protected interface to the SpecFluxFrame class. +* A public interface is provided by the astSpecFluxFrameId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "frame1" and "frame2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSpecFluxFrame *new; /* Pointer to new SpecFluxFrame */ + AstFluxFrame *frame2; /* Pointer to FluxFrame structure */ + AstSpecFrame *frame1; /* Pointer to SpecFrame structure */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + new = NULL; + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Frame structures provided. */ + frame1 = astCheckSpecFrame( frame1_void ); + frame2 = astCheckFluxFrame( frame2_void ); + if ( astOK ) { + +/* Initialise the SpecFluxFrame, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSpecFluxFrame( NULL, sizeof( AstSpecFluxFrame ), !class_init, + &class_vtab, "SpecFluxFrame", frame1, frame2 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + SpecFluxFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new SpecFluxFrame. */ + return new; +} + +AstSpecFluxFrame *astSpecFluxFrameId_( void *frame1_void, void *frame2_void, + const char *options, ... ) { +/* +* Name: +* astSpecFluxFrameId_ + +* Purpose: +* Create a SpecFluxFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specfluxframe.h" +* AstSpecFluxFrame *astSpecFluxFrameId_( void *frame1_void, void *frame2_void, +* const char *options, ... ) + +* Class Membership: +* SpecFluxFrame constructor. + +* Description: +* This function implements the external (public) interface to the +* astSpecFluxFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astSpecFluxFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). For the same reason, the "frame1" and "frame2" +* parameters are of type (void *) and are converted and validated +* within the function itself. +* +* The variable argument list also prevents this function from +* invoking astSpecFluxFrame_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. + +* Parameters: +* As for astSpecFluxFrame_. + +* Returned Value: +* The ID value associated with the new SpecFluxFrame. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSpecFluxFrame *new; /* Pointer to new SpecFluxFrame */ + AstSpecFrame *frame1; /* Pointer to first Frame structure */ + AstFluxFrame *frame2; /* Pointer to second Frame structure */ + va_list args; /* Variable argument list */ + + int *status; /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + new = NULL; + if ( !astOK ) return new; + +/* Obtain the Frame pointers from the ID's supplied and validate the + pointers to ensure they identify valid Frames. */ + frame1 = astVerifySpecFrame( astMakePointer( frame1_void ) ); + frame2 = astVerifyFluxFrame( astMakePointer( frame2_void ) ); + if ( astOK ) { + +/* Initialise the SpecFluxFrame, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSpecFluxFrame( NULL, sizeof( AstSpecFluxFrame ), !class_init, + &class_vtab, "SpecFluxFrame", frame1, frame2 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + SpecFluxFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new SpecFluxFrame. */ + return astMakeId( new ); +} + +AstSpecFluxFrame *astInitSpecFluxFrame_( void *mem, size_t size, int init, + AstSpecFluxFrameVtab *vtab, const char *name, + AstSpecFrame *frame1, AstFluxFrame *frame2, int *status ) { +/* +*+ +* Name: +* astInitSpecFluxFrame + +* Purpose: +* Initialise a SpecFluxFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specfluxframe.h" +* AstSpecFluxFrame *astInitSpecFluxFrame( void *mem, size_t size, int init, +* AstSpecFluxFrameVtab *vtab, const char *name, +* AstSpecFrame *frame1, AstFluxFrame *frame2 ) + +* Class Membership: +* SpecFluxFrame initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new SpecFluxFrame object. It allocates memory (if +* necessary) to accommodate the SpecFluxFrame plus any additional data +* associated with the derived class. It then initialises a +* SpecFluxFrame structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for a SpecFluxFrame at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SpecFluxFrame is to be +* created. This must be of sufficient size to accommodate the +* SpecFluxFrame data (sizeof(SpecFluxFrame)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SpecFluxFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SpecFluxFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A logical flag indicating if the SpecFluxFrame's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SpecFluxFrame. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the Object astClass function). +* frame1 +* Pointer to the SpecFrame +* frame2 +* Pointer to the FluxFrame + +* Returned Value: +* A pointer to the new SpecFluxFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstSpecFluxFrame *new; /* Pointer to new SpecFluxFrame */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSpecFluxFrameVtab( vtab, name ); + +/* Initialise a Frame structure (the parent class) as the first + component within the SpecFluxFrame structure, allocating memory if + necessary. Set the number of Frame axes to zero, since all axis + information is stored within the component Frames. */ + new = astInitCmpFrame( mem, size, 0, (AstCmpFrameVtab *) vtab, name, + frame1, frame2 ); + if ( astOK ) { + + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSpecFluxFrame *astLoadSpecFluxFrame_( void *mem, size_t size, + AstSpecFluxFrameVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSpecFluxFrame + +* Purpose: +* Load a SpecFluxFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specfluxframe.h" +* AstSpecFluxFrame *astLoadSpecFluxFrame( void *mem, size_t size, +* AstSpecFluxFrameVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SpecFluxFrame loader. + +* Description: +* This function is provided to load a new SpecFluxFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SpecFluxFrame structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the SpecFluxFrame is to be +* loaded. This must be of sufficient size to accommodate the +* SpecFluxFrame data (sizeof(SpecFluxFrame)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SpecFluxFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SpecFluxFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSpecFluxFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SpecFluxFrame. If this is NULL, a pointer +* to the (static) virtual function table for the SpecFluxFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SpecFluxFrame" is used instead. + +* Returned Value: +* A pointer to the new SpecFluxFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstSpecFluxFrame *new; /* Pointer to the new SpecFluxFrame */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SpecFluxFrame. In this case the + SpecFluxFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSpecFluxFrame ); + vtab = &class_vtab; + name = "SpecFluxFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSpecFluxFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SpecFluxFrame. */ + new = astLoadCmpFrame( mem, size, (AstCmpFrameVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SpecFluxFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ +/* (none) */ + +/* If an error occurred, clean up by deleting the new SpecFluxFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SpecFluxFrame pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + diff --git a/specfluxframe.h b/specfluxframe.h new file mode 100644 index 0000000..ff8a5e1 --- /dev/null +++ b/specfluxframe.h @@ -0,0 +1,215 @@ +#if !defined( SPECFLUXFRAME_INCLUDED ) /* Include this file only once */ +#define SPECFLUXFRAME_INCLUDED +/* +*+ +* Name: +* specfluxframe.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SpecFluxFrame class. + +* Invocation: +* #include "specfluxframe.h" + +* Description: +* This include file defines the interface to the SpecFluxFrame class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. + +* Inheritance: +* The SpecFluxFrame class inherits from the Frame class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 8-DEC-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "cmpframe.h" /* Parent Frame class */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ------- */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* SpecFluxFrame structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstSpecFluxFrame { + +/* Attributes inherited from the parent class. */ + AstCmpFrame cmpframe; /* Parent class structure */ + +} AstSpecFluxFrame; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSpecFluxFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstCmpFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + +} AstSpecFluxFrameVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstSpecFluxFrameGlobals { + AstSpecFluxFrameVtab Class_Vtab; + int Class_Init; + char GetTitle_Buff[ 201 ]; +} AstSpecFluxFrameGlobals; + +#endif +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SpecFluxFrame) /* Check class membership */ +astPROTO_ISA(SpecFluxFrame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstSpecFluxFrame *astSpecFluxFrame_( void *, void *, const char *, int *, ...); +#else +AstSpecFluxFrame *astSpecFluxFrameId_( void *, void *, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSpecFluxFrame *astInitSpecFluxFrame_( void *, size_t, int, AstSpecFluxFrameVtab *, + const char *, AstSpecFrame *, AstFluxFrame *, int * ); + +/* Vtab initialiser. */ +void astInitSpecFluxFrameVtab_( AstSpecFluxFrameVtab *, const char *, int * ); + +/* Loader. */ +AstSpecFluxFrame *astLoadSpecFluxFrame_( void *, size_t, AstSpecFluxFrameVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitSpecFluxFrameGlobals_( AstSpecFluxFrameGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSpecFluxFrame(this) astINVOKE_CHECK(SpecFluxFrame,this,0) +#define astVerifySpecFluxFrame(this) astINVOKE_CHECK(SpecFluxFrame,this,1) + +/* Test class membership. */ +#define astIsASpecFluxFrame(this) astINVOKE_ISA(SpecFluxFrame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astSpecFluxFrame astINVOKE(F,astSpecFluxFrame_) +#else +#define astSpecFluxFrame astINVOKE(F,astSpecFluxFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSpecFluxFrame(mem,size,init,vtab,name,frame1,frame2) \ +astINVOKE(O,astInitSpecFluxFrame_(mem,size,init,vtab,name,astCheckSpecFrame(frame1),astCheckFluxFrame(frame2),STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSpecFluxFrameVtab(vtab,name) astINVOKE(V,astInitSpecFluxFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSpecFluxFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSpecFluxFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckSpecFluxFrame to validate SpecFluxFrame pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#endif + + + + + diff --git a/specframe.c b/specframe.c new file mode 100644 index 0000000..137a69a --- /dev/null +++ b/specframe.c @@ -0,0 +1,7437 @@ +/* +*class++ +* Name: +* SpecFrame + +* Purpose: +* Spectral coordinate system description. + +* Constructor Function: +c astSpecFrame +f AST_SPECFRAME + +* Description: +* A SpecFrame is a specialised form of one-dimensional Frame which +* represents various coordinate systems used to describe positions within +* an electro-magnetic spectrum. The particular coordinate system to be +* used is specified by setting the SpecFrame's System attribute (the +* default is wavelength) qualified, as necessary, by other attributes +* such as the rest frequency, the standard of rest, the epoch of +* observation, units, etc (see the description of the System attribute +* for details). +* +* By setting a value for thr SpecOrigin attribute, a SpecFrame can be made +* to represent offsets from a given spectral position, rather than absolute +* spectral values. + +* Inheritance: +* The SpecFrame class inherits from the Frame class. + +* Attributes: +* In addition to those attributes common to all Frames, every +* SpecFrame also has the following attributes: +* +* - AlignSpecOffset: Align SpecFrames using the offset coordinate system? +* - AlignStdOfRest: Standard of rest in which to align SpecFrames +* - RefDec: Declination of the source (FK5 J2000) +* - RefRA: Right ascension of the source (FK5 J2000) +* - RestFreq: Rest frequency +* - SourceSys: Source velocity spectral system +* - SourceVel: Source velocity +* - SourceVRF: Source velocity rest frame +* - SpecOrigin: The zero point for SpecFrame axis values +* - StdOfRest: Standard of rest +* +* Several of the Frame attributes inherited by the SpecFrame class +* refer to a specific axis of the Frame (for instance Unit(axis), +* Label(axis), etc). Since a SpecFrame is strictly one-dimensional, +* it allows these attributes to be specified without an axis index. +* So for instance, "Unit" is allowed in place of "Unit(1)". + +* Functions: +c In addition to those functions applicable to all Frames, the +c following functions may also be applied to all SpecFrames: +f In addition to those routines applicable to all Frames, the +f following routines may also be applied to all SpecFrames: +* +c - astSetRefPos: Set reference position in any celestial system +f - AST_SETREFPOS: Set reference position in any celestial system +c - astGetRefPos: Get reference position in any celestial system +f - AST_GETREFPOS: Get reference position in any celestial system + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 4-NOV-2002 (DSB): +* Original version. +* 2-FEB-2005 (DSB): +* - Avoid using astStore to allocate more storage than is supplied +* in the "data" pointer. This can cause access violations since +* astStore will then read beyond the end of the "data" area. +* 22-MAR-2005 (DSB): +* - Re-structure MakeSpecMapping in order to avoid unnecessary +* access to SpecFrame attributes which may not be set, and to +* check that all required attributes have been set if UseDefs is +* zero. +* 23-MAR-2005 (DSB): +* - Added missing rest frames to SorEqual. +* 12-AUG-2005 (DSB): +* - Remove GeoLon and GeoLat attributes. Use the new ObsLon and +* ObsLat attributes in the parent Frame class instead. Note, for +* backward compatibility the public attribute accessors and the +* astLoadSpecFrame functions still recogonise GeoLon and GeoLat, +* but use the ObsLat/ObsLon attributes internally. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 6-OCT-2006 (DSB): +* Guard against annulling null pointers in subFrame. +* 18-OCT-2006 (DSB): +* Added SpecOrigin and AlignSpecOffset attributes. +* 23-OCT-2006 (DSB): +* Fix memory leak caused by addition of SpecOrigin and AlignSpecOffset +* attributes. +* 15-NOV-2006 (DSB): +* Only write out SpecOrigin if it is not bad. +* 8-JAN-2006 (DSB): +* - SubFrame: Copy the SourceSystem and SourceStdOfRest attributes +* to the System and StdOfRest attributes of the "align_frm" +* SpecFrame before calling MakeSpecMapping. Previously, the +* values assigned to SourceSystem and SourceStdOfRest were +* ignored, and alignment was always performed in the templates System +* and StdOfRest. +* - MakeSpecMapping: Correct logic used to decide if steps 2 and 7 +* can be cancelled. +* - OriginSystem: Clear the AlignSpecOffset attributes before +* finding the Mapping between the old and new Systems. +* 16-JAN-2006 (DSB): +* Fix bug in Dump that caused SrcVRF not to be written out. +* 31-JAN-2007 (DSB): +* Modified so that a SpecFrame can be used as a template to find a +* SpecFrame contained within a CmpFrame. This involves changes in +* Match and the removal of the local versions of SetMaxAxes and +* SetMinAxes. +* 8-AUG-2007 (DSB): +* Changed Overlay to avoid the possibility of making permanent +* changes to the supplied template Frame. +* 3-SEP-2007 (DSB): +* In SubFrame, since AlignSystem is extended by the SpecFrame class +* it needs to be cleared before invoking the parent SubFrame +* method in cases where the result Frame is not a SkyFrame. +* 2-OCT-2007 (DSB): +* In Overlay, clear AlignSystem as well as System before calling +* the parent overlay method. +* 4-SEP-2009 (DSB): +* In MakeSpecMapping, in order to produce alignment that is not +* affected by the epoch or reference position, make the alignment +* frame adapt to the epoch and reference position of the target +* and result Frames. +* 14-SEP-2009 (DSB): +* In MakeSpecMapping, extend the 4-SEP-2009 fix to cover other +* attributes that define the available rest frames (e.g. +* SourceVRF, SourceVel, ObsLat, ObsLon, ObsAlt). +* 16-SEP-2009 (DSB): +* In MakeSpecMapping, retain the original alignment frame attribute +* values if we are restoring the integrity of a FrameSet. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SpecFrame + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__FREQ +#define LAST_SYSTEM AST__VREL + +/* Define the first and last acceptable StdOfRest values. */ +#define FIRST_SOR AST__TPSOR +#define LAST_SOR AST__SCSOR + +/* The supported spectral coordinate systems fall into two groups; + "relative", and "absolute". The relative systems define each axis + value with respect to the rest frequency, whereas the absolute systems + have axis values which do not depend on the rest frequency. Define a + macro which returns one if the specified system is absolute, and zero + otherwise. */ +#define ABS_SYSTEM(sys) \ + ( ( sys == AST__ENERGY || \ + sys == AST__WAVENUM || \ + sys == AST__WAVELEN || \ + sys == AST__AIRWAVE || \ + sys == AST__FREQ ) ? 1 : 0 ) + +/* Define other numerical constants for use in this module. */ +#define GETATTRIB_BUFF_LEN 50 +#define GETLABEL_BUFF_LEN 200 +#define GETSYMBOL_BUFF_LEN 20 +#define GETTITLE_BUFF_LEN 200 + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "unit.h" /* Units management facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "specmap.h" /* Spectral coordinate Mappings */ +#include "frame.h" /* Parent Frame class */ +#include "skyframe.h" /* Celestial coordinate frames */ +#include "specframe.h" /* Interface definition for this class */ +#include "mapping.h" /* Coordinate Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "pal.h" /* SlaLib interface */ +#include "shiftmap.h" /* Change of origin */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are used or extended by this + class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstSystemType (* parent_getalignsystem)( AstFrame *, int * ); +static AstSystemType (* parent_getsystem)( AstFrame *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getdomain)( AstFrame *, int * ); +static const char *(* parent_getlabel)( AstFrame *, int, int * ); +static const char *(* parent_getsymbol)( AstFrame *, int, int * ); +static const char *(* parent_gettitle)( AstFrame *, int * ); +static const char *(* parent_getunit)( AstFrame *, int, int * ); +static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_setunit)( AstFrame *, int, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_setsystem)( AstFrame *, AstSystemType, int * ); +static void (* parent_clearsystem)( AstFrame *, int * ); +static void (* parent_clearunit)( AstFrame *, int, int * ); + +/* Define a variable to hold a SkyFrame which will be used for formatting + and unformatting sky positions, etc. */ +static AstSkyFrame *skyframe; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetLabel_Buff[ 0 ] = 0; \ + globals->GetSymbol_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff[ 0 ] = 0; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SpecFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SpecFrame,Class_Init) +#define class_vtab astGLOBAL(SpecFrame,Class_Vtab) +#define getattrib_buff astGLOBAL(SpecFrame,GetAttrib_Buff) +#define getlabel_buff astGLOBAL(SpecFrame,GetLabel_Buff) +#define getsymbol_buff astGLOBAL(SpecFrame,GetSymbol_Buff) +#define gettitle_buff astGLOBAL(SpecFrame,GetTitle_Buff) + + + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ 51 ]; + +/* Default GetLabel string buffer */ +static char getlabel_buff[ 201 ]; + +/* Default GetSymbol buffer */ +static char getsymbol_buff[ 21 ]; + +/* Default Title string buffer */ +static char gettitle_buff[ 201 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSpecFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstStdOfRestType StdOfRestCode( const char *, int * ); +static int GetObjSize( AstObject *, int * ); +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static const char *DefUnit( AstSystemType, const char *, const char *, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const char *SpecMapUnit( AstSystemType, const char *, const char *, int * ); +static const char *StdOfRestString( AstStdOfRestType, int * ); +static const char *SystemLabel( AstSystemType, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static double ConvertSourceVel( AstSpecFrame *, AstStdOfRestType, AstSystemType, int * ); +static int EqualSor( AstSpecFrame *, AstSpecFrame *, int * ); +static int GetActiveUnit( AstFrame *, int * ); +static int MakeSpecMapping( AstSpecFrame *, AstSpecFrame *, AstSpecFrame *, int, AstMapping **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SorConvert( AstSpecFrame *, AstSpecFrame *, AstSpecMap *, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static void ClearUnit( AstFrame *, int, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GetRefPos( AstSpecFrame *, AstSkyFrame *, double *, double *, int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void SetRefPos( AstSpecFrame *, AstSkyFrame *, double, double, int * ); +static void SetUnit( AstFrame *, int, const char *, int * ); +static void VerifyAttrs( AstSpecFrame *, const char *, const char *, const char *, int * ); +static double ToUnits( AstSpecFrame *, const char *, double, const char *, int * ); +static void OriginStdOfRest( AstSpecFrame *, AstStdOfRestType, const char *, int * ); +static void OriginSystem( AstSpecFrame *, AstSystemType, const char *, int * ); + +static AstSystemType GetSystem( AstFrame *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); +static void ClearSystem( AstFrame *, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static AstStdOfRestType GetAlignStdOfRest( AstSpecFrame *, int * ); +static int TestAlignStdOfRest( AstSpecFrame *, int * ); +static void ClearAlignStdOfRest( AstSpecFrame *, int * ); +static void SetAlignStdOfRest( AstSpecFrame *, AstStdOfRestType, int * ); + +static AstStdOfRestType GetStdOfRest( AstSpecFrame *, int * ); +static int TestStdOfRest( AstSpecFrame *, int * ); +static void ClearStdOfRest( AstSpecFrame *, int * ); +static void SetStdOfRest( AstSpecFrame *, AstStdOfRestType, int * ); + +static double GetRestFreq( AstSpecFrame *, int * ); +static int TestRestFreq( AstSpecFrame *, int * ); +static void ClearRestFreq( AstSpecFrame *, int * ); +static void SetRestFreq( AstSpecFrame *, double, int * ); + +static double GetSourceVel( AstSpecFrame *, int * ); +static int TestSourceVel( AstSpecFrame *, int * ); +static void ClearSourceVel( AstSpecFrame *, int * ); +static void SetSourceVel( AstSpecFrame *, double, int * ); + +static double GetRefRA( AstSpecFrame *, int * ); +static int TestRefRA( AstSpecFrame *, int * ); +static void ClearRefRA( AstSpecFrame *, int * ); +static void SetRefRA( AstSpecFrame *, double, int * ); + +static double GetRefDec( AstSpecFrame *, int * ); +static int TestRefDec( AstSpecFrame *, int * ); +static void ClearRefDec( AstSpecFrame *, int * ); +static void SetRefDec( AstSpecFrame *, double, int * ); + +static AstStdOfRestType GetSourceVRF( AstSpecFrame *, int * ); +static int TestSourceVRF( AstSpecFrame *, int * ); +static void ClearSourceVRF( AstSpecFrame *, int * ); +static void SetSourceVRF( AstSpecFrame *, AstStdOfRestType, int * ); + +static AstSystemType GetSourceSys( AstSpecFrame *, int * ); +static int TestSourceSys( AstSpecFrame *, int * ); +static void ClearSourceSys( AstSpecFrame *, int * ); +static void SetSourceSys( AstSpecFrame *, AstSystemType, int * ); + +static double GetSpecOrigin( AstSpecFrame *, int * ); +static int TestSpecOrigin( AstSpecFrame *, int * ); +static void ClearSpecOrigin( AstSpecFrame *, int * ); +static void SetSpecOrigin( AstSpecFrame *, double, int * ); +static double GetSpecOriginCur( AstSpecFrame *, int * ); + +static int GetAlignSpecOffset( AstSpecFrame *, int * ); +static int TestAlignSpecOffset( AstSpecFrame *, int * ); +static void SetAlignSpecOffset( AstSpecFrame *, int, int * ); +static void ClearAlignSpecOffset( AstSpecFrame *, int * ); + +/* Member functions. */ +/* ================= */ + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* SpecFrame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the SpecFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + char *new_attrib; /* Pointer value to new attribute name */ + int len; /* Length of attrib string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* First look for axis attributes defined by the Frame class. Since a + SpecFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strcmp( attrib, "direction" ) || + !strcmp( attrib, "bottom" ) || + !strcmp( attrib, "top" ) || + !strcmp( attrib, "format" ) || + !strcmp( attrib, "label" ) || + !strcmp( attrib, "symbol" ) || + !strcmp( attrib, "unit" ) ) { + +/* Create a new attribute name from the original by appending the string + "(1)" and then use the parent ClearAttrib method. */ + new_attrib = astMalloc( len + 4 ); + if( new_attrib ) { + memcpy( new_attrib, attrib, len ); + memcpy( new_attrib + len, "(1)", 4 ); + (*parent_clearattrib)( this_object, new_attrib, status ); + new_attrib = astFree( new_attrib ); + } + +/* AlignStdOfRest. */ +/* --------------- */ + } else if ( !strcmp( attrib, "alignstdofrest" ) ) { + astClearAlignStdOfRest( this ); + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in which + SpecFrame had GeoLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( !strcmp( attrib, "geolat" ) ) { + astClearAttrib( this, "obslat" ); + +/* GeoLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "geolon" ) ) { + astClearAttrib( this, "obslon" ); + +/* RefDec. */ +/* ---------- */ + } else if ( !strcmp( attrib, "refdec" ) ) { + astClearRefDec( this ); + +/* RefRA. */ +/* --------- */ + } else if ( !strcmp( attrib, "refra" ) ) { + astClearRefRA( this ); + +/* RestFreq. */ +/* --------- */ + } else if ( !strcmp( attrib, "restfreq" ) ) { + astClearRestFreq( this ); + +/* SourceVel. */ +/* ---------- */ + } else if ( !strcmp( attrib, "sourcevel" ) ) { + astClearSourceVel( this ); + +/* SpecOrigin. */ +/* ---------- */ + } else if ( !strcmp( attrib, "specorigin" ) ) { + astClearSpecOrigin( this ); + +/* AlignSpecOffset. */ +/* ---------------- */ + } else if ( !strcmp( attrib, "alignspecoffset" ) ) { + astClearAlignSpecOffset( this ); + +/* SourceVRF */ +/* --------- */ + } else if ( !strcmp( attrib, "sourcevrf" ) ) { + astClearSourceVRF( this ); + +/* SourceSys */ +/* --------- */ + } else if ( !strcmp( attrib, "sourcesys" ) ) { + astClearSourceSys( this ); + +/* StdOfRest. */ +/* ---------- */ + } else if ( !strcmp( attrib, "stdofrest" ) ) { + astClearStdOfRest( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearSystem + +* Purpose: +* Clear the System attribute for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void ClearSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astClearSystem protected +* method inherited from the Frame class). + +* Description: +* This function clears the System attribute for a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to SpecFrame structure */ + AstSystemType newsys; /* System after clearing */ + AstSystemType oldsys; /* System before clearing */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* Save the original system */ + oldsys = astGetSystem( this_frame ); + +/* Use the parent ClearSystem method to clear the System value. */ + (*parent_clearsystem)( this_frame, status ); + +/* Get the default System. */ + newsys = astGetSystem( this_frame ); + +/* If the system has actually changed. */ + if( newsys != oldsys ) { + +/* Changing the System value will in general require the Units to change + as well. If the used has previously specified the units to be used with + the new system, then re-instate them (they are stored in the "usedunits" + array in the SpecFrame structure). Otherwise, clear the units so that + the default units will eb used with the new System. */ + if( (int) newsys < this->nuunits && this->usedunits && + this->usedunits[ (int) newsys ] ) { + astSetUnit( this, 0, this->usedunits[ (int) newsys ] ); + } else { + astClearUnit( this, 0 ); + } + +/* Also, clear all attributes which have system-specific defaults. */ + astClearLabel( this_frame, 0 ); + astClearSymbol( this_frame, 0 ); + astClearTitle( this_frame ); + +/* Modify the SpecOrigin value to use the new System */ + OriginSystem( this, oldsys, "astClearSystem", status ); + + } + +} + +static void ClearStdOfRest( AstSpecFrame *this, int *status ) { +/* +*+ +* Name: +* astClearStdOfRest + +* Purpose: +* Clear the StdOfRest attribute for a SpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* void astClearStdOfRest( AstSpecFrame *this ) + +* Class Membership: +* SpecFrame virtual function + +* Description: +* This function clears the StdOfRest attribute for a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Modify the SpecOrigin value stored in the SpecFrame structure to refer to the + default rest frame (heliocentric). */ + OriginStdOfRest( this, AST__HLSOR, "astClearStdOfRest", status ); + +/* Store a bad value for the standard of rest in the SpecFrame structure. */ + this->stdofrest = AST__BADSOR; +} + + +static void ClearUnit( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* ClearUnit + +* Purpose: +* Clear the value of the Unit string for a SpecFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void ClearUnit( AstFrame *this_frame, int axis ) + +* Class Membership: +* SpecFrame member function (over-rides the astClearUnit method inherited +* from the Frame class). + +* Description: +* This function clears the Unit string for a specified axis of a +* SpecFrame. It also clears the UsedUnit item in the SpecFrame +* structure corresponding to the current System. + +* Parameters: +* this +* Pointer to the SpecFrame. +* axis +* The number of the axis (zero-based). +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + int system; /* The SpecFrame's System value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astClearUnit" ); + +/* Clear the UsedUnit item for the current System, if current set. */ + system = (int) astGetSystem( this ); + if( system < this->nuunits && this->usedunits ) { + this->usedunits[ system ] = astFree( this->usedunits[ system ] ); + } + +/* Use the parent method to clear the Unit attribute of the axis. */ + (*parent_clearunit)( this_frame, axis, status ); +} + +static double ConvertSourceVel( AstSpecFrame *this, AstStdOfRestType newsor, + AstSystemType newsys, int *status ) { +/* +* Name: +* ConvertSourceVel + +* Purpose: +* Convert the SourceVel value to a specified rest frame and spectral +* system. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* double ConvertSourceVel( AstSpecFrame *this, AstStdOfRestType newsor, +* AstSystemType newsys, int *status ) + +* Class Membership: +* SpecFrame member function + +* Description: +* This function convert the SourceVel value to a specified rest frame +* and spectral system, and returns the new value. + +* Parameters: +* this +* Pointer to the SpecFrame. +* newsor +* The rest frame in which the source velocity is required. +* newsys +* The spectral system (AST__VREL or AST__REDSHIFT) in which the +* source velocity is required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The converted source velocity (m/s), or redshift. + +* Notes: +* - This function returns zero if an error occurs. +*/ + +/* Local Variables: */ + AstSpecFrame *from; /* Pointer to a source SpecFrame */ + AstSpecFrame *to; /* Pointer to a destination SpecFrame */ + AstSpecMap *specmap; /* Pointer to a SpecMap */ + AstStdOfRestType sor; /* Standard of rest in which SourceVel is defined */ + AstSystemType sys; /* Spectral system in which SourceVel is defined */ + double ret; /* The returned value */ + double rf; /* Rest frequency (Hz) */ + double temp; /* Temporary storage */ + +/* Initialise */ + ret = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Get the value of the SourceVel attribute. This will be a velocity in m/s + (relativistic, radio or optical), or unitless redshift or beta factor, + depending on the current value of SourceSys. */ + ret = astGetSourceVel( this ); + +/* Check it can be used (depends on whether a value has been set and + whether the UseDefs attribute is zero). */ + VerifyAttrs( this, "convert source velocity to a new standard of rest", + "SourceVel", "astMatch", status ); + +/* Get the rest frame and spectral system to which value refers. */ + sor = astGetSourceVRF( this ); + sys = astGetSourceSys( this ); + +/* If necessary, convert to the requested rest frame and spectral system. */ + if( sor != newsor || sys != newsys ) { + +/* Verify that usable value is available for the RestFreq attribute. An + error is reported if not. */ + VerifyAttrs( this, "convert source velocity to a new standard of rest", + "RestFreq", "astMatch", status ); + +/* Take two copies of the supplied SpecFrame and set their StdOfRest + attributes to the required values. */ + from = astCopy( this ); + astSetStdOfRest( from, sor ); + + to = astCopy( this ); + astSetStdOfRest( to, newsor ); + +/* Initialise a new SpecMap to describe the conversion. The new SpecMap + initially represents a UnitMap. */ + specmap = astSpecMap( 1, 0, "", status ); + +/* Add a conversion from the spectral system in which the SourceVEl value + is stored, to relativistic velocity. */ + if( sys == AST__VRADIO ) { + astSpecAdd( specmap, "VRTOVL", NULL ); + + } else if( sys == AST__VOPTICAL ) { + astSpecAdd( specmap, "VOTOVL", NULL ); + + } else if( sys == AST__REDSHIFT ) { + astSpecAdd( specmap, "ZOTOVL", NULL ); + + } else if( sys == AST__BETA ) { + astSpecAdd( specmap, "BTTOVL", NULL ); + } + +/* Add a conversion from velocity to frequency since SorConvert converts + frequencies. */ + rf = astGetRestFreq( this ); + astSpecAdd( specmap, "VLTOFR", &rf ); + +/* Now add a conversion from frequency in the SourveVRF standard of rest to + frequency in the required rest frame. */ + SorConvert( from, to, specmap, status ); + +/* Add a conversion from frequency back to velocity. Note, the value of the + rest frequency does not affect the overall conversion. */ + astSpecAdd( specmap, "FRTOVL", &rf ); + +/* Add a conversion from relativistic velocity to the required spectral + system, if needed. */ + if( newsys == AST__VRADIO ) { + astSpecAdd( specmap, "VLTOVR", NULL ); + + } else if( newsys == AST__VOPTICAL ) { + astSpecAdd( specmap, "VLTOVO", NULL ); + + } else if( newsys == AST__REDSHIFT ) { + astSpecAdd( specmap, "VLTOZO", NULL ); + + } else if( newsys == AST__BETA ) { + astSpecAdd( specmap, "VLTOBT", NULL ); + } + +/* Use the SpecMap to convert the source velocity in the SourceVRF + standard of rest and SourceSys spectral system to the required rest + frame and spectral system. */ + temp = ret; + astTran1( specmap, 1, &temp, 1, &ret ); + +/* Free resources */ + specmap = astAnnul( specmap ); + to = astAnnul( to ); + from = astAnnul( from ); + } + +/* Return zero if an error has occurred. */ + if( !astOK ) ret = 0.0; + +/* Return the answer. */ + return ret; + +} + +static const char *DefUnit( AstSystemType system, const char *method, + const char *class, int *status ){ +/* +* Name: +* DefUnit + +* Purpose: +* Return the default units for a spectral coordinate system type. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *DefUnit( AstSystemType system, const char *method, +* const char *class, int *status ) + +* Class Membership: +* SpecFrame member function. + +* Description: +* This function returns a textual representation of the default +* units associated with the specified spectral coordinate system. + +* Parameters: +* system +* The spectral coordinate system. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* As tring describing the default units. This string follows the +* units syntax described in FITS WCS paper I "Representations of world +* coordinates in FITS" (Greisen & Calabretta). + +* Notes: +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Value to return */ + +/* Initialize */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get an identifier for the default units. */ + if( system == AST__FREQ ) { + result = "GHz"; + } else if( system == AST__ENERGY ) { + result = "J"; + } else if( system == AST__WAVENUM ) { + result = "1/m"; + } else if( system == AST__WAVELEN ) { + result = "Angstrom"; + } else if( system == AST__AIRWAVE ) { + result = "Angstrom"; + } else if( system == AST__VRADIO ) { + result = "km/s"; + } else if( system == AST__VOPTICAL ) { + result = "km/s"; + } else if( system == AST__REDSHIFT ) { + result = ""; + } else if( system == AST__BETA ) { + result = ""; + } else if( system == AST__VREL ) { + result = "km/s"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "%s(%s): Corrupt %s contains illegal System " + "identification code (%d).", status, method, class, class, + (int) system ); + } + +/* Return the result. */ + return result; +} + +static int EqualSor( AstSpecFrame *this, AstSpecFrame *that, int *status ) { +/* +* Name: +* EqualSor + +* Purpose: +* Do two SpecFrames use the same standard of rest? + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int EqualSor( AstSpecFrame *this, AstSpecFrame *that, int *status ) + +* Class Membership: +* SpecFrame member function + +* Description: +* This function returns non-zero if the two supplied SpecFrames use +* the same standard of rest. + +* Parameters: +* this +* Pointer to the first SpecFrame. +* that +* Pointer to the second SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the two SpecFrames use the same standard of rest. Zero +* otherwise. + +*/ + +/* Local Variables: */ + AstStdOfRestType sor; /* Standard of rest */ + int result; /* Value to return */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise. */ + result = 1; + +/* Compare StdOfRest attributes. */ + sor = astGetStdOfRest( this ); + if( astGetStdOfRest( that ) != sor ) { + result = 0; + +/* If the standards of rest are equal we need to check the the attributes + which specify the precise rest frame. */ + } else { + +/* The reference RA and Dec need to be equal */ + if( !astEQUAL( astGetRefRA( this ), astGetRefRA( that ) ) || + !astEQUAL( astGetRefDec( this ), astGetRefDec( that ) ) ) { + result = 0; + +/* For source rest frame, the source velocities, rest frames and systems must + be equal */ + } else if( sor == AST__SCSOR ){ + if( !astEQUAL( astGetSourceVel( this ), astGetSourceVel( that ) ) || + astGetSourceVRF( this ) != astGetSourceVRF( that ) || + astGetSourceSys( this ) != astGetSourceSys( that ) ) { + result = 0; + } + +/* For geocentric, barycentric and heliocentric rest frames, the epochs must + be the same */ + } else if( sor == AST__GESOR || sor == AST__BYSOR || sor == AST__HLSOR ){ + if( !astEQUAL( astGetEpoch( this ), astGetEpoch( that ) ) ) result = 0; + +/* For topocentric rest frame, the epoch and position of the observer must be + the same */ + } else if( sor == AST__TPSOR ){ + if( !astEQUAL( astGetEpoch( this ), astGetEpoch( that ) ) || + !astEQUAL( astGetObsAlt( this ), astGetObsAlt( that ) ) || + !astEQUAL( astGetObsLon( this ), astGetObsLon( that ) ) || + !astEQUAL( astGetObsLat( this ), astGetObsLat( that ) ) ) result = 0; + + } else if( sor != AST__LKSOR && sor != AST__LDSOR && + sor != AST__GLSOR && sor != AST__LGSOR && astOK ) { + astError( AST__INTER, "SorEqual(SpecFrame): Function SorEqual " + "does not yet support rest frame %d (AST internal " + "programming error)", status, sor ); + } + } + +/* Return the result */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SpecFrame, +* in bytes. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to SpecFrame structure */ + int result; /* Result value to return */ + int i; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SpecFrame structure. */ + this = (AstSpecFrame *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + if( this->usedunits ) { + for( i = 0; i < this->nuunits; i++ ) { + result += astTSizeOf( this->usedunits[ i ] ); + } + result += astTSizeOf( this->usedunits ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetActiveUnit + +* Purpose: +* Obtain the value of the ActiveUnit flag for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int GetActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function returns the value of the ActiveUnit flag for a +* SpecFrame, which is always 1. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to use for the ActiveUnit flag (1). + +*/ + return 1; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a SpecFrame, formatted as a character string. + +* Parameters: +* this +* Pointer to the SpecFrame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the SpecFrame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the SpecFrame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + AstStdOfRestType sor; /* Standard of rest */ + AstSystemType sys; /* Spectral system */ + char *new_attrib; /* Pointer value to new attribute name */ + const char *result; /* Pointer value to return */ + double dval; /* Attribute value */ + int ival; /* Attribute value */ + int len; /* Length of attrib string */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_object; + +/* Create an FK5 J2000 SkyFrame which will be used for formatting and + unformatting sky positions, etc. */ + LOCK_MUTEX2 + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame( "system=FK5,equinox=J2000", status ); + astEndPM; + } + UNLOCK_MUTEX2 + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* First look for axis attributes defined by the Frame class. Since a + SpecFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strcmp( attrib, "direction" ) || + !strcmp( attrib, "bottom" ) || + !strcmp( attrib, "top" ) || + !strcmp( attrib, "format" ) || + !strcmp( attrib, "label" ) || + !strcmp( attrib, "symbol" ) || + !strcmp( attrib, "unit" ) ) { + +/* Create a new attribute name from the original by appending the string + "(1)" and then use the parent GetAttrib method. */ + new_attrib = astMalloc( len + 4 ); + if( new_attrib ) { + memcpy( new_attrib, attrib, len ); + memcpy( new_attrib + len, "(1)", 4 ); + result = (*parent_getattrib)( this_object, new_attrib, status ); + new_attrib = astFree( new_attrib ); + } + +/* AlignStdOfRest. */ +/* --------------- */ +/* Obtain the AlignStdOfRest code and convert to a string. */ + } else if ( !strcmp( attrib, "alignstdofrest" ) ) { + sor = astGetAlignStdOfRest( this ); + if ( astOK ) { + result = StdOfRestString( sor, status ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid AlignStdOfRest " + "identification code (%d).", status, astGetClass( this ), + astGetClass( this ), (int) sor ); + } + } + +/* AlignSpecOffset */ +/* --------------- */ + } else if ( !strcmp( attrib, "alignspecoffset" ) ) { + ival = astGetAlignSpecOffset( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in which + SpecFrame had GeoLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( !strcmp( attrib, "geolat" ) ) { + result = astGetAttrib( this, "obslat" ); + +/* GeoLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "geolon" ) ) { + result = astGetAttrib( this, "obslon" ); + +/* RefDec. */ +/* ------- */ +/* Convert to a string using the SkyFrame Format method. */ + } else if ( !strcmp( attrib, "refdec" ) ) { + dval = astGetRefDec( this ); + if ( astOK ) { + result = astFormat( skyframe, 1, dval ); + } + +/* RefRA. */ +/* ------ */ +/* Convert to a string using the SkyFrame Format method. */ + } else if ( !strcmp( attrib, "refra" ) ) { + dval = astGetRefRA( this ); + if ( astOK ) { + result = astFormat( skyframe, 0, dval ); + } + +/* RestFreq. */ +/* --------- */ + } else if ( !strcmp( attrib, "restfreq" ) ) { + dval = astGetRestFreq( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval*1.0E-9 ); + result = getattrib_buff; + } + +/* SourceVel */ +/* --------- */ + } else if ( !strcmp( attrib, "sourcevel" ) ) { + dval = astGetSourceVel( this ); + if ( astOK ) { + +/* Convert from m/s to km/s if the SourceVel value is a velocity. . */ + if( astGetSourceSys( this ) == AST__VREL || + astGetSourceSys( this ) == AST__VRADIO || + astGetSourceSys( this ) == AST__VOPTICAL ) dval *= 1.0E-3; + +/* Format */ + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + + } + +/* SpecOrigin. */ +/* ----------- */ + } else if ( !strcmp( attrib, "specorigin" ) ) { + dval = GetSpecOriginCur( this, status ); + if( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + +/* SourceVRF */ +/* ----------*/ + } else if ( !strcmp( attrib, "sourcevrf" ) ) { + sor = astGetSourceVRF( this ); + if ( astOK ) { + result = StdOfRestString( sor, status ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid SourceVRF " + "identification code (%d).", status, astGetClass( this ), + astGetClass( this ), (int) sor ); + } + } + +/* SourceSys */ +/* ----------*/ + } else if ( !strcmp( attrib, "sourcesys" ) ) { + sys = astGetSourceSys( this ); + if ( astOK ) { + result = SystemString( (AstFrame *) this, sys, status ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid SourceSys " + "identification code (%d).", status, astGetClass( this ), + astGetClass( this ), (int) sys ); + } + } + +/* StdOfRest. */ +/* ---------- */ +/* Obtain the StdOfRest code and convert to a string. */ + } else if ( !strcmp( attrib, "stdofrest" ) ) { + sor = astGetStdOfRest( this ); + if ( astOK ) { + result = StdOfRestString( sor, status ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid StdOfRest " + "identification code (%d).", status, astGetClass( this ), + astGetClass( this ), (int) sor ); + } + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static const char *GetDomain( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDomain + +* Purpose: +* Obtain a pointer to the Domain attribute string for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *GetDomain( AstFrame *this, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetDomain protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the Domain attribute string +* for a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Domain value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the SpecFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to SpecFrame structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* If a Domain attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestDomain( this ) ) { + result = (*parent_getdomain)( this_frame, status ); + +/* Otherwise, provide a pointer to a suitable default string. */ + } else { + result = "SPECTRUM"; + } + +/* Return the result. */ + return result; +} + +static const char *GetLabel( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetLabel + +* Purpose: +* Access the Label string for a SpecFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *GetLabel( AstFrame *this, int axis, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetLabel method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Label string for a specified axis +* of a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstMapping *map; /* Mapping between units */ + AstSystemType system; /* Code identifying type of spectral coordinates */ + char *new_lab; /* Modified label string */ + const char *result; /* Pointer to label string */ + double orig; /* Spec origin */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetLabel" ); + +/* Check if a value has been set for the required axis label string. If so, + invoke the parent astGetLabel method to obtain a pointer to it. */ + if ( astTestLabel( this, axis ) ) { + result = (*parent_getlabel)( this, axis, status ); + +/* Otherwise, identify the spectral coordinate system described by the + SpecFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default label string. */ + if ( astOK ) { + result = strcpy( getlabel_buff, SystemLabel( system, status ) ); + getlabel_buff[ 0 ] = toupper( getlabel_buff[ 0 ] ); + +/* If a non-zero SpecOrigin has been specified, include the offset now. */ + orig = GetSpecOriginCur( (AstSpecFrame *) this, status ); + if( orig != 0.0 ) { + sprintf( getlabel_buff + strlen( getlabel_buff ), " offset from %s", + astFormat( this, 0, orig ) ); + } + +/* Modify this default to take account of the current value of the Unit + attribute, if set. */ + if( astTestUnit( this, axis ) ) { + +/* Find a Mapping from the default Units for the current System, to the + units indicated by the Unit attribute. This Mapping is used to modify + the existing default label appropriately. For instance, if the default + units is "Hz" and the actual units is "log(Hz)", then the default label + of "Frequency" is changed to "log( frequency )". */ + map = astUnitMapper( DefUnit( system, "astGetLabel", + astGetClass( this ), status ), + astGetUnit( this, axis ), result, + &new_lab ); + if( new_lab ) { + result = strcpy( getlabel_buff, new_lab ); + new_lab = astFree( new_lab ); + } + +/* Annul the unused Mapping. */ + if( map ) map = astAnnul( map ); + + } + } + } + +/* Return the result. */ + return result; +} + +static void GetRefPos( AstSpecFrame *this, AstSkyFrame *frm, double *lon, + double *lat, int *status ){ +/* +*++ +* Name: +c astGetRefPos +f AST_GETREFPOS + +* Purpose: +* Return the reference position in a specified celestial coordinate system. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "specframe.h" +c void astGetRefPos( AstSpecFrame *this, AstSkyFrame *frm, double *lon, +c double *lat ) +f CALL AST_GETREFPOS( THIS, FRM, LON, LAT, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* returns the reference position (specified by attributes RefRA and +* RefDec) converted to the celestial coordinate system represented by +* a supplied SkyFrame. The celestial longitude and latitude values +* are returned in radians. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the SpecFrame. +c frm +f FRM = INTEGER (Given) +* Pointer to the SkyFrame which defines the required celestial +* coordinate system. +c If NULL +f If AST__NULL +* is supplied, then the longitude and latitude values are returned +* as FK5 J2000 RA and Dec values. +c lon +f LON = DOUBLE PRECISION (Returned) +c A pointer to a double in which to store the +f The +* longitude of the reference point, in the coordinate system +* represented by the supplied SkyFrame (radians). +c lat +f LAT = DOUBLE PRECISION (Returned) +c A pointer to a double in which to store the +f The +* latitude of the reference point, in the coordinate system +* represented by the supplied SkyFrame (radians). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Values of AST__BAD will be returned if this function is +c invoked with the AST error status set, or if it should fail for +f invoked with STATUS set to an error value, or if it should fail for +* any reason. +*-- +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* Conversion FrameSet */ + AstFrame *fb; /* Base Frame */ + AstFrame *fc; /* Current Frame */ + double xin[ 1 ]; /* Axis 1 values */ + double yin[ 1 ]; /* Axis 2 values */ + double xout[ 1 ]; /* Axis 1 values */ + double yout[ 1 ]; /* Axis 2 values */ + +/* Initialise. */ + if( lon ) *lon = AST__BAD; + if( lat ) *lat = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If no SkyFrame was supplied, just return the stored RefRA and RefDec + values. */ + if( !frm ) { + if( lon ) *lon = astGetRefRA( this ); + if( lat ) *lat = astGetRefDec( this ); + +/* Otherwise, convert the stored values to the requested system. */ + } else { + +/* Create an FK5 J2000 SkyFrame which will be used for formatting and + unformatting sky positions, etc. */ + LOCK_MUTEX2 + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame( "system=FK5,equinox=J2000", status ); + astEndPM; + } + UNLOCK_MUTEX2 + +/* Find the Mapping from the SkyFrame which describes the internal format + in which the RefRA and RefDec attribute values are stored, to the + supplied Frame. */ + fs = astFindFrame( skyframe, frm, "" ); + +/* If alignment was possible, use the Mapping to transform the internal + RefRA and RefDec values. Check for axis permutatuion. */ + if( fs ) { + fb = astGetFrame( fs, AST__BASE ); + if( astGetLonAxis( fb ) == 0 ) { + xin[ 0 ] = astGetRefRA( this ); + yin[ 0 ] = astGetRefDec( this ); + } else { + yin[ 0 ] = astGetRefRA( this ); + xin[ 0 ] = astGetRefDec( this ); + } + astTran2( fs, 1, xin, yin, 1, xout, yout ); + +/* Store the returned values, checking to see if the axes of the supplied + SkyFrame have been permuted. */ + fc = astGetFrame( fs, AST__CURRENT ); + if( astGetLonAxis( fc ) == 0 ) { + if( lon ) *lon = xout[ 0 ]; + if( lat ) *lat = yout[ 0 ]; + } else { + if( lon ) *lon = yout[ 0 ]; + if( lat ) *lat = xout[ 0 ]; + } + +/* Annul object references. */ + fc = astAnnul( fc ); + fb = astAnnul( fb ); + fs = astAnnul( fs ); + } + } +} + +static const char *GetSymbol( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetSymbol + +* Purpose: +* Obtain a pointer to the Symbol string for a SpecFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *GetSymbol( AstFrame *this, int axis, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetSymbol method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Symbol string for a specified axis +* of a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstMapping *map; /* Mapping between units */ + AstSystemType system; /* Code identifying type of sky coordinates */ + char *new_sym; /* Modified symbol string */ + const char *result; /* Pointer to symbol string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetSymbol" ); + +/* Check if a value has been set for the required axis symbol string. If so, + invoke the parent astGetSymbol method to obtain a pointer to it. */ + if ( astTestSymbol( this, axis ) ) { + result = (*parent_getsymbol)( this, axis, status ); + +/* Otherwise, identify the sky coordinate system described by the SpecFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default Symbol string. */ + if ( astOK ) { + + if( system == AST__FREQ ) { + result = "FREQ"; + } else if( system == AST__ENERGY ) { + result = "ENER"; + } else if( system == AST__WAVENUM ) { + result = "WAVN"; + } else if( system == AST__WAVELEN ) { + result = "WAVE"; + } else if( system == AST__AIRWAVE ) { + result = "AWAV"; + } else if( system == AST__VRADIO ) { + result = "VRAD"; + } else if( system == AST__VOPTICAL ) { + result = "VOPT"; + } else if( system == AST__REDSHIFT ) { + result = "ZOPT"; + } else if( system == AST__BETA ) { + result = "BETA"; + } else if( system == AST__VREL ) { + result = "VELO"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "astGetSymbol(%s): Corrupt %s contains " + "invalid System identification code (%d).", status, + astGetClass( this ), astGetClass( this ), (int) system ); + } + +/* Modify this default to take account of the current value of the Unit + attribute, if set. */ + if( astTestUnit( this, axis ) ) { + +/* Find a Mapping from the default Units for the current System, to the + units indicated by the Unit attribute. This Mapping is used to modify + the existing default symbol appropriately. For instance, if the default + units is "Hz" and the actual units is "log(Hz)", then the default symbol + of "nu" is changed to "log( nu )". */ + map = astUnitMapper( DefUnit( system, "astGetSymbol", + astGetClass( this ), status ), + astGetUnit( this, axis ), result, + &new_sym ); + if( new_sym ) { + result = strcpy( getsymbol_buff, new_sym ); + new_sym = astFree( new_sym ); + } + +/* Annul the unused Mapping. */ + if( map ) map = astAnnul( map ); + + } + } + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetAlignSystem + +* Purpose: +* Obtain the AlignSystem attribute for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "Specframe.h" +* AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetAlignSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the AlignSystem attribute for a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The AlignSystem value. + +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to SpecFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* If a AlignSystem attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestAlignSystem( this ) ) { + result = (*parent_getalignsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__WAVELEN; + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetSystem + +* Purpose: +* Obtain the System attribute for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* AstSystemType GetSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the System attribute for a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to SpecFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* If a System attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestSystem( this ) ) { + result = (*parent_getsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__WAVELEN; + } + +/* Return the result. */ + return result; +} + +static const char *GetTitle( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetTitle + +* Purpose: +* Obtain a pointer to the Title string for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *GetTitle( AstFrame *this_frame, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetTitle method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Title string for a SpecFrame. +* A pointer to a suitable default string is returned if no Title value has +* previously been set. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSpecFrame *this; /* Pointer to SpecFrame structure */ + AstStdOfRestType sor; /* Code identifying standard of rest */ + AstSystemType system; /* Code identifying type of coordinates */ + const char *sor_string; /* Pointer to SOR description */ + const char *result; /* Pointer to result string */ + double rf; /* Rest frequency */ + int nc; /* No. of characters added */ + int pos; /* Buffer position to enter text */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* See if a Title string has been set. If so, use the parent astGetTitle + method to obtain a pointer to it. */ + if ( astTestTitle( this ) ) { + result = (*parent_gettitle)( this_frame, status ); + +/* Otherwise, we will generate a default Title string. Obtain the values of the + SpecFrame's attributes that determine what this string will be. */ + } else { + system = astGetSystem( this ); + sor = astGetStdOfRest( this ); + sor_string = StdOfRestString( sor, status ); + rf = astGetRestFreq( this ); + +/* Classify the coordinate system type and create an appropriate Title + string. (Note that when invoking the astFmtDecimalYr function we must + use a separate sprintf on each occasion so as not to over-write its + internal buffer before the result string has been used.) */ + if ( astOK ) { + result = gettitle_buff; + +/* Begin with the system's default label. */ + pos = sprintf( gettitle_buff, "%s", SystemLabel( system, status ) ); + gettitle_buff[ 0 ] = toupper( gettitle_buff[ 0 ] ); + +/* Append the standard of rest in parentheses, if set. */ + if( astTestStdOfRest( this ) ) { + nc = sprintf( gettitle_buff+pos, " (%s)", sor_string ); + pos += nc; + } + +/* Append the rest frequency if relevant. */ + if( !ABS_SYSTEM(system) && ( astTestRestFreq( this ) || + astGetUseDefs( this ) ) ) { + pos += sprintf( gettitle_buff+pos, ", rest frequency = %g GHz", rf*1.0E-9 ); + } + } + } + +/* If an error occurred, clear the returned pointer value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static double GetSpecOriginCur( AstSpecFrame *this, int *status ) { +/* +* Name: +* GetSpecOriginCur + +* Purpose: +* Obtain the SpecOrigin attribute for a SpecFrame in current units. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double GetSpecOriginCur( AstSpecFrame *this, int *status ) + +* Class Membership: +* SpecFrame virtual function + +* Description: +* This function returns the SpecOrigin attribute for a SpecFrame, in +* the current units of the SpecFrame. The protected astGetSpecOrigin +* method can be used to obtain the time origin in the default units of +* the SpecFrame's System. + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The SpecOrigin value, in the units, system and rest frame specified +* by the current values of the Unit, System and StdOfRest attributes +* within "this". + +* Notes: +* - AST__BAD is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *map; + const char *cur; + const char *def; + double result; + double defval; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the value in the default units */ + result = astGetSpecOrigin( this ); + +/* If SpecOrigin is non-zero and non-BAD we convert it to the current units.*/ + if( result != 0.0 && result != AST__BAD ) { + +/* Get the default units for the SpecFrame's System. */ + def = DefUnit( astGetSystem( this ), "astGetSpecOrigin", "SpecFrame", status ); + +/* Get the current units from the SpecFrame. */ + cur = astGetUnit( this, 0 ); + +/* If the units differ, get a Mapping from default to current units. */ + if( cur && def ){ + if( strcmp( cur, def ) ) { + map = astUnitMapper( def, cur, NULL, NULL ); + +/* Report an error if the units are incompatible. */ + if( !map ) { + astError( AST__BADUN, "%s(%s): The current units (%s) are not suitable " + "for a SpecFrame.", status, "astGetSpecOrigin", astGetClass( this ), + cur ); + +/* Otherwise, transform the stored origin value.*/ + } else { + defval = result; + astTran1( map, 1, &defval, 1, &result ); + map = astAnnul( map ); + } + } + } + } + +/* Return the result. */ + return result; +} + + +static const char *GetUnit( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetUnit + +* Purpose: +* Obtain a pointer to the Unit string for a SpecFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *GetUnit( AstFrame *this_frame, int axis ) + +* Class Membership: +* SpecFrame member function (over-rides the astGetUnit method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Unit string for a specified axis +* of a SpecFrame. If the Unit attribute has not been set for the axis, a +* pointer to a suitable default string is returned instead. + +* Parameters: +* this +* Pointer to the SpecFrame. +* axis +* The number of the axis (zero-based) for which information is required. + +* Returned Value: +* A pointer to a null-terminated string containing the Unit value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + AstSystemType system; /* The SpecFrame's System value */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetUnit" ); + +/* If a value has been set for the Unit attribute, use the parent + GetUnit method to return a pointer to the required Unit string. */ + if( astTestUnit( this, axis ) ){ + result = (*parent_getunit)( this_frame, axis, status ); + +/* Otherwise, identify the spectral coordinate system described by the + SpecFrame. */ + } else { + system = astGetSystem( this ); + +/* Return a string describing the default units. */ + result = DefUnit( system, "astGetUnit", astGetClass( this ), status ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +void astInitSpecFrameVtab_( AstSpecFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSpecFrameVtab + +* Purpose: +* Initialise a virtual function table for a SpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specframe.h" +* void astInitSpecFrameVtab( AstSpecFrameVtab *vtab, const char *name ) + +* Class Membership: +* SpecFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SpecFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASpecFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->GetRefPos = GetRefPos; + vtab->SetRefPos = SetRefPos; + + vtab->ClearAlignStdOfRest = ClearAlignStdOfRest; + vtab->TestAlignStdOfRest = TestAlignStdOfRest; + vtab->GetAlignStdOfRest = GetAlignStdOfRest; + vtab->SetAlignStdOfRest = SetAlignStdOfRest; + + vtab->ClearSourceVRF = ClearSourceVRF; + vtab->TestSourceVRF = TestSourceVRF; + vtab->GetSourceVRF = GetSourceVRF; + vtab->SetSourceVRF = SetSourceVRF; + + vtab->ClearSourceSys = ClearSourceSys; + vtab->TestSourceSys = TestSourceSys; + vtab->GetSourceSys = GetSourceSys; + vtab->SetSourceSys = SetSourceSys; + + vtab->ClearRefDec = ClearRefDec; + vtab->TestRefDec = TestRefDec; + vtab->GetRefDec = GetRefDec; + vtab->SetRefDec = SetRefDec; + + vtab->ClearRefRA = ClearRefRA; + vtab->TestRefRA = TestRefRA; + vtab->GetRefRA = GetRefRA; + vtab->SetRefRA = SetRefRA; + + vtab->ClearRestFreq = ClearRestFreq; + vtab->TestRestFreq = TestRestFreq; + vtab->GetRestFreq = GetRestFreq; + vtab->SetRestFreq = SetRestFreq; + + vtab->ClearStdOfRest = ClearStdOfRest; + vtab->TestStdOfRest = TestStdOfRest; + vtab->GetStdOfRest = GetStdOfRest; + vtab->SetStdOfRest = SetStdOfRest; + + vtab->ClearSourceVel = ClearSourceVel; + vtab->TestSourceVel = TestSourceVel; + vtab->GetSourceVel = GetSourceVel; + vtab->SetSourceVel = SetSourceVel; + + vtab->ClearSpecOrigin = ClearSpecOrigin; + vtab->TestSpecOrigin = TestSpecOrigin; + vtab->GetSpecOrigin = GetSpecOrigin; + vtab->SetSpecOrigin = SetSpecOrigin; + + vtab->TestAlignSpecOffset = TestAlignSpecOffset; + vtab->SetAlignSpecOffset = SetAlignSpecOffset; + vtab->GetAlignSpecOffset = GetAlignSpecOffset; + vtab->ClearAlignSpecOffset = ClearAlignSpecOffset; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_getdomain = frame->GetDomain; + frame->GetDomain = GetDomain; + + parent_getsystem = frame->GetSystem; + frame->GetSystem = GetSystem; + parent_setsystem = frame->SetSystem; + frame->SetSystem = SetSystem; + parent_clearsystem = frame->ClearSystem; + frame->ClearSystem = ClearSystem; + + parent_getalignsystem = frame->GetAlignSystem; + frame->GetAlignSystem = GetAlignSystem; + + parent_getlabel = frame->GetLabel; + frame->GetLabel = GetLabel; + + parent_getsymbol = frame->GetSymbol; + frame->GetSymbol = GetSymbol; + + parent_gettitle = frame->GetTitle; + frame->GetTitle = GetTitle; + + parent_clearunit = frame->ClearUnit; + frame->ClearUnit = ClearUnit; + + parent_getunit = frame->GetUnit; + frame->GetUnit = GetUnit; + + parent_setunit = frame->SetUnit; + frame->SetUnit = SetUnit; + + parent_match = frame->Match; + frame->Match = Match; + + parent_overlay = frame->Overlay; + frame->Overlay = Overlay; + + parent_subframe = frame->SubFrame; + frame->SubFrame = SubFrame; + +/* Store replacement pointers for methods which will be over-ridden by new + member functions implemented here. */ + frame->GetActiveUnit = GetActiveUnit; + frame->TestActiveUnit = TestActiveUnit; + frame->ValidateSystem = ValidateSystem; + frame->SystemString = SystemString; + frame->SystemCode = SystemCode; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "SpecFrame", + "Description of spectral coordinate system" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MakeSpecMapping( AstSpecFrame *target, AstSpecFrame *result, + AstSpecFrame *align_frm, int report, + AstMapping **map, int *status ) { +/* +* Name: +* MakeSpecMapping + +* Purpose: +* Generate a Mapping between two SpecFrames. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int MakeSpecMapping( AstSpecFrame *target, AstSpecFrame *result, +* AstSpecFrame *align_frm, int report, +* AstMapping **map, int *status ) { + +* Class Membership: +* SpecFrame member function. + +* Description: +* This function takes two SpecFrames and generates a Mapping that +* converts between them, taking account of differences in their +* coordinate systems, rest frequency, standard of rest, etc. +* +* In order to cut down the number of transformations to be considered, +* the scheme works by first converting from the target frame to an +* "alignment" Frame, using the attributes of the target to define the +* transformation. A transformation is then found from the alignment +* frame to the required result Frame, using the attributes of the +* result to define the transformation. The alignment Frame is +* described by the AlignSystem and AlignStdOfRest attributes of the +* "align_frm" SpecFrame. +* +* Thus, different forms of alignment can be obtained by suitable +* choice of the attributes of "align_frm". For instance, to compare the +* radio velocity dispersion of two lines at different rest frequencies, +* you would set "system=radio velocity" and (probably) "stdofrest=local +* group" in "align_frm". On the other hand if you wanted to re-calibrate +* an existing radio velocity Frame within a FrameSet to use a different +* rest frequency, you would make the SpecFrame the current Frame and then +* set the rest frequency attribute for the FrameSet. The "integrity +* checking" system in the FrameSet class would then get the Mapping +* between the original and the modified SpecFrames. In this case, the +* "alignment system" needs to be "frequency" since you want the original +* and modified SpecFrames to be aligned in frequency, not radio velocity. + +* Parameters: +* target +* Pointer to the first SpecFrame. +* result +* Pointer to the second SpecFrame. +* align_frm +* A SpecFrame defining the system and standard of rest in which to +* align the target and result SpecFrames. +* report +* Should errors be reported if no match is possible? These reports +* will describe why no match was possible. +* map +* Pointer to a location which is to receive a pointer to the +* returned Mapping. The forward transformation of this Mapping +* will convert from "target" coordinates to "result" +* coordinates, and the inverse transformation will convert in +* the opposite direction (all coordinate values in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Mapping could be generated, or zero if the two +* SpecFrames are sufficiently un-related that no meaningful Mapping +* can be produced (albeit an "unmeaningful" Mapping will be returned +* in this case, which will need to be annulled). + +* Notes: +* A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Constants: */ +#define MAX_ARGS 1 /* Max arguments for an SpecMap conversion */ + +/* Local Variables: */ + AstMapping *map1; /* Intermediate Mapping */ + AstMapping *map2; /* Intermediate Mapping */ + AstMapping *umap1; /* First Units Mapping */ + AstMapping *umap2; /* Second Units Mapping */ + AstSpecMap *specmap; /* Pointer to SpecMap */ + AstShiftMap *sm; /* ShiftMap pointer */ + AstSpecFrame *align_target; /* Alignment Frame with target properties */ + AstSpecFrame *align_result; /* Alignment Frame with result properties */ + AstSystemType serr; /* Erroneous system */ + AstSystemType align_system; /* Code to identify alignment system */ + AstSystemType target_system; /* Code to identify target system */ + AstSystemType result_system; /* Code to identify result system */ + const char *uerr; /* Erroneous units */ + const char *ures; /* Results units */ + const char *utarg; /* Target units */ + const char *vmess; /* Text for use in error messages */ + double args[ MAX_ARGS ]; /* Conversion argument array */ + double target_rf; /* Target rest frequency (Hz) */ + double result_rf; /* Result rest frequency (Hz) */ + double target_origin; /* Target origin */ + double result_origin; /* Result origin */ + int match; /* Mapping can be generated? */ + int step2; /* Perform the 2nd step in the Mapping? */ + int step3; /* Perform the 3rd step in the Mapping? */ + int step4; /* Perform the 4th step in the Mapping? */ + int step5; /* Perform the 5th step in the Mapping? */ + int step6; /* Perform the 6th step in the Mapping? */ + int step7; /* Perform the 7th step in the Mapping? */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise the returned values. */ + match = 1; + *map = NULL; + +/* Create an initial (null) SpecMap. This is a 1D Mapping which converts + spectral axis values between different systems and standard of rest. + The axis units used by the SpecMap class match the default units used + by this class. Any discrepancy between units is taken into account at + the end of this function, once the total SpecMap has been created. */ + specmap = astSpecMap( 1, 0, "", status ); + +/* Define local macros as shorthand for adding spectral coordinate + conversions to this SpecMap. Each macro simply stores details of + the additional arguments in the "args" array and then calls + astSpecAdd. The macros differ in the number of additional argument + values. */ +#define TRANSFORM_0(cvt) \ + astSpecAdd( specmap, cvt, NULL ); + +#define TRANSFORM_1(cvt,arg0) \ + args[ 0 ] = arg0; \ + astSpecAdd( specmap, cvt, args ); + +/* Get all the necessary attributes from the result, target and alignment + Frames. */ + target_rf = astGetRestFreq( target ); + result_rf = astGetRestFreq( result ); + + target_system = astGetSystem( target ); + result_system = astGetSystem( result ); + align_system = astGetSystem( align_frm ); + +/* Define text for error messages.*/ + vmess = "convert between spectral systems"; + +/* Verify that values for the standard of rest have been set if required + (i.e if the UseDefs attribute of either SpecFrame is false). */ + VerifyAttrs( result, vmess, "StdOfRest", "astMatch", status ); + VerifyAttrs( target, vmess, "StdOfRest", "astMatch", status ); + +/* There are two different strategies for alignment. I'll use the Source + rest frame as an example, although the same argument applies to other + rest frames. In the first strategy, all "Source" rest frames are + considered equal. That is, if two SpecFrames respresent (for example) + frequencies in the source frame, then the SpecFrames are aligned using + a UnitMap even if the details of the two source rest frames differ. + This is usually what users want to see when (for instance) aligning + plots of two spectra which both represent source frequencies but where + the source frames details differ. In the second strategy, "Source" + rest frames are aligned using a SpecMap that takes into account any + differences in the properties of the source rest frames. This is what + should happen when changes are made to the properties of a SpecFrame + within a FrameSet. For instance, if the user changes the SourceVel + attribute of the current Frame (assumed here to be a SpecFrame) in a + FrameSet, then the process of restoring the integrity of the FrameSet + (see frameset.c for details of integrity restoration) should cause the + base->current Mapping in the FrameSet to be modified to reflect the + new SourceVel value. + + So if the current call to this function is part of the process of + restoring a FrameSet's integrity following changes to the FrameSet's + current Frame, then we want to retain the properties of the supplied + alignment Frame. So we use clones of the supplied alignment Frame. */ + if( astGetFrameFlags( target ) & AST__INTFLAG ) { + align_target = astClone( align_frm ); + align_result = astClone( align_frm ); + +/* Buf if we are not restoring the integrity of a FrameSet, we want + to ignore any differences in the properties that define the available + rest frames. So create copies of the alignment Frame in which the + properies defining the available rest frames are the same as in the + target and result Frames. */ + } else { + align_target = astCopy( align_frm ); + astSetEpoch( align_target, astGetEpoch( target ) ); + astSetRefRA( align_target, astGetRefRA( target ) ); + astSetRefDec( align_target, astGetRefDec( target ) ); + astSetSourceVRF( align_target, astGetSourceVRF( target ) ); + astSetSourceVel( align_target, astGetSourceVel( target ) ); + astSetObsLat( align_target, astGetObsLat( target ) ); + astSetObsLon( align_target, astGetObsLon( target ) ); + astSetObsAlt( align_target, astGetObsAlt( target ) ); + + align_result = astCopy( align_frm ); + astSetEpoch( align_result, astGetEpoch( result ) ); + astSetRefRA( align_result, astGetRefRA( result ) ); + astSetRefDec( align_result, astGetRefDec( result ) ); + astSetSourceVRF( align_result, astGetSourceVRF( result ) ); + astSetSourceVel( align_result, astGetSourceVel( result ) ); + astSetObsLat( align_result, astGetObsLat( result ) ); + astSetObsLon( align_result, astGetObsLon( result ) ); + astSetObsAlt( align_result, astGetObsAlt( result ) ); + } + +/* The supported spectral coordinate systems fall into two groups; + "relative", and "absolute". The relative systems define each axis + value with respect to the rest frequency, whereas the absolute systems + have axis values which do not depend on the rest frequency. In order + to convert an axis value from a system in one group to a system in the + other group, the rest frequency must be known. However, the rest + frequency is not necessary in order to convert axis values between two + systems belonging to the same group. Determine if the alignment system + is absolute or relative. If absolute, we ignore the system of the supplied + "align_frm" and align in frequency, since aligning in any absolute system + will automatically ensure that all the other absolute systems are aligned. + Similarly, aligning in any relative system will automatically ensure that + all the other relative systems are aligned. Doing this cuts down the + complexity of the conversion process since we do not need to check every + possible alignment system. */ + align_system = ( ABS_SYSTEM( align_system ) ) ? AST__FREQ : AST__VREL; + +/* The total Mapping is made up of the following steps in series: + + 0) Convert from an offset value to an absolute value (if SpecOrigin set) + 1) Convert target units to default units for the targets system + 2) Convert from target system in target SOR to frequency in target SOR + 3) Convert from freq in target SOR to freq in alignment SOR + 4) Convert from freq in alignment SOR to alignment system in alignment SOR + 5) Convert from alignment system in alignment SOR to freq in alignment SOR + 6) Convert from freq in alignment SOR to freq in result SOR + 7) Convert from freq in result SOR to result system in result SOR + 8) Convert default units for the result system to results unit + 9) Convert from an absolute value to an offset value (if SpecOrigin set) + + Steps 1,2,3,4 are performed using the attributes of the target (rest + frequency, reference farem, etc), whilst steps 5,6,7,8 are performed + using the attributes of the target (rest frequency, reference frame, + etc). It is necessary to go from target system to alignment system + via frequency because SOR conversion can only be performed in the + frequency domain. + + Some of these steps may not be necessary. Initially assume all steps + are necessary (we leave steps 0, 1, 8 and 9 out of this process and + implement them once all other steps have been done). */ + step2 = 1; + step3 = 1; + step4 = 1; + step5 = 1; + step6 = 1; + step7 = 1; + +/* Step 2 is not necessary if the target system is frequency. */ + if( target_system == AST__FREQ ) step2 = 0; + +/* Step 3 is not necessary if the alignment SOR is the same as the target + SOR. */ + if( EqualSor( target, align_target, status ) ) step3 = 0; + +/* Step 6 is not necessary if the alignment SOR is the same as the result + SOR. */ + if( EqualSor( result, align_result, status ) ) step6 = 0; + +/* Step 7 is not necessary if the result system is frequency. */ + if( result_system == AST__FREQ ) step7 = 0; + +/* Steps 4 and 5 are not necessary if the alignment system is frequency, + or if the target and result rest frequencies are equal. */ + if( align_system == AST__FREQ || result_rf == target_rf ) step4 = step5 = 0; + +/* Steps 3 and 6 are not necessary if steps 4 and 5 are not necessary, and + the target sor equals the result sor. */ + if( !step4 && !step5 && EqualSor( target, result, status ) ) step3 = step6 = 0; + +/* Steps 2 and 7 are not necessary if steps 3, 4, 5 and 6 are not necessary, + and the target sor equals the result sor, and the target and results + systems are equal (if the systems are relative they must also have equal + rest frequencies). */ + if( !step3 && !step4 && !step5 && !step6 && EqualSor( target, result, status ) && + target_system == result_system ) { + if( !ABS_SYSTEM( target_system ) && result_rf == target_rf ) step2 = step7 = 0; + } + + +/* Now we know which steps are needed, let's do them (we delay unit + conversion to the end)... */ + +/* Step 2: target system in target rest frame to frequency in target rest + frame. */ + if( step2 ) { + if( target_system != AST__FREQ ) { + +/* If the target system is absolute, we can convert directly to frequency. */ + if ( target_system == AST__ENERGY ) { + TRANSFORM_0( "ENTOFR" ) + + } else if ( target_system == AST__WAVENUM ) { + TRANSFORM_0( "WNTOFR" ) + + } else if ( target_system == AST__WAVELEN ) { + TRANSFORM_0( "WVTOFR" ) + + } else if ( target_system == AST__AIRWAVE ) { + TRANSFORM_0( "AWTOFR" ) + +/* If the target target_system is relative, we first need to convert to + apparent radial velocity, and then to frequency using the rest frequency. */ + } else { + + if ( target_system == AST__VRADIO ) { + TRANSFORM_0( "VRTOVL" ) + + } else if ( target_system == AST__VOPTICAL ) { + TRANSFORM_0( "VOTOVL" ) + + } else if ( target_system == AST__REDSHIFT ) { + TRANSFORM_0( "ZOTOVL" ) + + } else if ( target_system == AST__BETA ) { + TRANSFORM_0( "BTTOVL" ) + } + + VerifyAttrs( target, vmess, "RestFreq", "astMatch", status ); + TRANSFORM_1( "VLTOFR", target_rf ) + } + } + } + +/* Step 3: frequency in target rest frame to frequency in alignment rest + frame. */ + if( step3 ) match = SorConvert( target, align_target, specmap, status ); + +/* Step 4: frequency in alignment rest frame to alignment system in alignment + rest frame. The alignment will be either relativistic velocity or + frequency. */ + if( step4 ) { + if( align_system == AST__VREL ) { + VerifyAttrs( target, vmess, "RestFreq", "astMatch", status ); + TRANSFORM_1( "FRTOVL", target_rf ) + } + } + +/* Step 5: Alignment system in alignment rest frame to frequency in alignment + rest frame (from now on use the attributes of the result SpecFrame to + define the conversion parameters). */ + if( step5 ) { + if( align_system == AST__VREL ) { + VerifyAttrs( result, vmess, "RestFreq", "astMatch", status ); + TRANSFORM_1( "VLTOFR", result_rf ) + } + } + +/* Step 6: frequency in alignment rest frame to frequency in result rest + frame. */ + if( step6 ) match = SorConvert( align_result, result, specmap, status ); + +/* Step 7: frequency in result rest frame to result system in result rest + frame. */ + if( step7 ) { + if( result_system != AST__FREQ ) { + +/* If the results system is absolute, we can convert directly. */ + if ( result_system == AST__ENERGY ) { + TRANSFORM_0( "FRTOEN" ) + + } else if ( result_system == AST__WAVENUM ) { + TRANSFORM_0( "FRTOWN" ) + + } else if ( result_system == AST__WAVELEN ) { + TRANSFORM_0( "FRTOWV" ) + + } else if ( result_system == AST__AIRWAVE ) { + TRANSFORM_0( "FRTOAW" ) + +/* If the result system is relative, we first need to convert to apparent + radial velocity from frequency using the rest frequency. Report an error + if the rest frequency is undefined. */ + } else { + VerifyAttrs( result, vmess, "RestFreq", "astMatch", status ); + TRANSFORM_1( "FRTOVL", result_rf ) + +/* Now convert from apparent radial velocity to the required result system. */ + if ( result_system == AST__VRADIO ) { + TRANSFORM_0( "VLTOVR" ) + + } else if ( result_system == AST__VOPTICAL ) { + TRANSFORM_0( "VLTOVO" ) + + } else if ( result_system == AST__REDSHIFT ) { + TRANSFORM_0( "VLTOZO" ) + + } else if ( result_system == AST__BETA ) { + TRANSFORM_0( "VLTOBT" ) + } + } + } + } + +/* The SpecMap created above class assumes that the axis values supplied to + its Transform method are in units which correspond to the default units + for its class (the returned values also use these units). However, + the Unit attributes of the supplied Frames may have been set to some + non-default value, and so we need to add Mappings before and after the + SpecMap which convert to and from the default units. Find the Mapping + from the target Frame Units to the default Units for the target's system. */ + utarg = astGetUnit( target, 0 ); + umap1 = astUnitMapper( utarg, SpecMapUnit( target_system, "MakeSpecMap", + "SpecFrame", status ), NULL, NULL ); + +/* Find the Mapping from the default Units for the result's system to the + Units of the result Frame. */ + ures = astGetUnit( result, 0 ); + umap2 = astUnitMapper( SpecMapUnit( result_system, "MakeSpecMap", + "SpecFrame", status ), ures, NULL, NULL ); + +/* If both units Mappings were created OK, sandwich the SpecMap between + them. */ + if( umap1 && umap2 ) { + map1 = (AstMapping *) astCmpMap( umap1, specmap, 1, "", status ); + map2 = (AstMapping *) astCmpMap( map1, umap2, 1, "", status ); + map1 = astAnnul( map1 ); + +/* If the simplified SpecMap is a UnitMap, and the target and result + units are the same, we do not need to know the mapping between units. + Otherwise, report an error and indicate that we cannot convert between + the Frames. */ + } else { + map2 = astSimplify( specmap ); + if( !astIsAUnitMap( map2 ) || strcmp( ures, utarg ) ) { + match = 0; + if( astOK && report ) { + if( !umap1 ) { + uerr = utarg; + serr = astGetSystem( target ); + } else { + uerr = ures; + serr = astGetSystem( result ); + } + astError( AST__BADUN, "astMatch(SpecFrame): Inappropriate units (%s) " + "specified for a %s axis.", status, uerr, SystemLabel( serr, status ) ); + } + } + } + +/* Step 0: offset to absolute value in target system. Prepend the Maping created + above with a ShiftMap that does the required shift of origin. */ + target_origin = GetSpecOriginCur( target, status ); + if( target_origin != 0.0 ) { + sm = astShiftMap( 1, &target_origin, "", status ); + map1 = (AstMapping *) astCmpMap( sm, map2, 1, "", status ); + sm = astAnnul( sm ); + } else { + map1 = astClone( map2 ); + } + map2 = astAnnul( map2 ); + +/* Step 9: absolute value to offset in result system. If we are aligning in the + offset system, use the transformed target origin as the new zero point. + Otherwise use the origin from the result frame. First get the origin for the + result system. */ + if( astGetAlignSpecOffset( target ) && astGetAlignSpecOffset( result ) ) { + result_origin = 0.0; + astTran1( map1, 1, &result_origin, 1, &result_origin ); + } else { + result_origin = GetSpecOriginCur( result, status ); + } + +/* Now create the ShiftMap and apend it to the end of the Maping. */ + if( result_origin != 0.0 ) { + result_origin = -result_origin; + sm = astShiftMap( 1, &result_origin, "", status ); + map2 = (AstMapping *) astCmpMap( map1, sm, 1, "", status ); + sm = astAnnul( sm ); + } else { + map2 = astClone( map1 ); + } + map1 = astAnnul( map1 ); + +/* Return the simplified Mapping. */ + *map = astSimplify( map2 ); + +/* Annul remaining resources. */ + map2 = astAnnul( map2 ); + specmap = astAnnul( specmap ); + if( umap1 ) umap1 = astAnnul( umap1 ); + if( umap2 ) umap2 = astAnnul( umap2 ); + align_result = astAnnul( align_result ); + align_target = astAnnul( align_target ); + +/* If an error occurred, annul the returned Mapping and clear the returned + values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + match = 0; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef MAX_ARGS +#undef TRANSFORM_0 +#undef TRANSFORM_1 +} + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the protected astMatch method +* inherited from the Frame class). + +* Description: +* This function matches a "template" SpecFrame to a "target" Frame and +* determines whether it is possible to convert coordinates between them. +* If it is, a mapping that performs the transformation is returned along +* with a new Frame that describes the coordinate system that results when +* this mapping is applied to the "target" coordinate system. In addition, +* information is returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" and "template" +* Frames from which they are derived. + +* Parameters: +* template +* Pointer to the template SpecFrame. This describes the coordinate +* system (or set of possible coordinate systems) into which we wish to +* convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate system in +* which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the template SpecFrame axis from +* which it is derived. If it is not derived from any template +* SpecFrame axis, a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the target Frame axis from which it +* is derived. If it is not derived from any target Frame axis, a value +* of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will be +* returned if the requested coordinate conversion is possible. If +* returned, the forward transformation of this Mapping may be used to +* convert coordinates between the "target" Frame and the "result" +* Frame (see below) and the inverse transformation will convert in the +* opposite direction. +* result +* Address of a location where a pointer to a new Frame will be returned +* if the requested coordinate conversion is possible. If returned, this +* Frame describes the coordinate system that results from applying the +* returned Mapping (above) to the "target" coordinate system. In +* general, this Frame will combine attributes from (and will therefore +* be more specific than) both the target and the template Frames. In +* particular, when the template allows the possibility of transformaing +* to any one of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate conversion is +* possible. Otherwise zero is returned (this will not in itself result in +* an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* This implementation addresses the matching of a SpecFrame class +* object to any other class of Frame. A SpecFrame will match any class +* of SpecFrame (i.e. possibly from a derived class) but will not match +* a less specialised class of Frame. +*/ + +/* Local Variables: */ + AstFrame *frame0; /* Pointer to Frame underlying axis 0 */ + AstSpecFrame *template; /* Pointer to template SpecFrame structure */ + int iaxis0; /* Axis index underlying axis 0 */ + int iaxis; /* Axis index */ + int match; /* Coordinate conversion possible? */ + int target_axis0; /* Index of SpecFrame axis in the target */ + int target_naxes; /* Number of target axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the template SpecFrame structure. */ + template = (AstSpecFrame *) template_frame; + +/* Obtain the number of axes in the target Frame. */ + target_naxes = astGetNaxes( target ); + +/* The first criterion for a match is that the template matches as a + Frame class object. This ensures that the number of axes (1) and + domain, etc. of the target Frame are suitable. Invoke the parent + "astMatch" method to verify this. */ + match = (*parent_match)( template_frame, target, matchsub, + template_axes, target_axes, map, result, status ); + +/* If a match was found, annul the returned objects, which are not + needed, but keep the memory allocated for the axis association + arrays, which we will re-use. */ + if ( astOK && match ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + } + +/* If OK so far, obtain pointers to the primary Frames which underlie + all target axes. Stop when a SpecFrame axis is found. */ + if ( match && astOK ) { + match = 0; + for( iaxis = 0; iaxis < target_naxes; iaxis++ ) { + astPrimaryFrame( target, iaxis, &frame0, &iaxis0 ); + if( astIsASpecFrame( frame0 ) ) { + frame0 = astAnnul( frame0 ); + target_axis0 = iaxis; + match = 1; + break; + } else { + frame0 = astAnnul( frame0 ); + } + } + + } + +/* Check at least one SpecFrame axis was found it the target. Store the + axis associataions. */ + if( match && astOK ) { + (*template_axes)[ 0 ] = 0; + (*target_axes)[ 0 ] = target_axis0; + +/* Use the target's "astSubFrame" method to create a new Frame (the + result Frame) with copies of the target axes in the required + order. This process also overlays the template attributes on to the + target Frame and returns a Mapping between the target and result + Frames which effects the required coordinate conversion. */ + match = astSubFrame( target, template, 1, *target_axes, *template_axes, + map, result ); + + } + +/* If an error occurred, or conversion to the result Frame's + coordinate system was not possible, then free all memory, annul the + returned objects, and reset the returned value. */ + if ( !astOK || !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + + +/* Return the result. */ + return match; +} + +static void OriginStdOfRest( AstSpecFrame *this, AstStdOfRestType newsor, + const char *method, int *status ){ +/* +* Name: +* OriginStdOfRest + +* Purpose: +* Convert the SpecOrigin in a SpecFrame to a new rest frame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void OriginStdOfRest( AstSpecFrame *this, AstStdOfRestType newsor, +* const char *method, int *status ) + +* Class Membership: +* SpecFrame member function + +* Description: +* This function converts the value of the SpecOrigin attribute stored +* within a supplied SpecFrame from the rest frame currently associated +* with the SpecFrame, to the new rest frame indicated by "newsor". + +* Parameters: +* this +* Point to the SpecFrame. On entry, the SpecOrigin value is +* assumed to refer to the re st frame given by the astGetStdOfRest +* method. On exit, the SpecOrigin value refers to the rest frame +* supplied in "newsor". The StdOfRest attribute of the SpecFrame +* should then be modified in order to keep things consistent. +* newsor +* The rest frame to which the SpecOrigin value stored within "this" +* should refer on exit. +* method +* Pointer to a string holding the name of the method to be +* included in any error messages. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Local Variables: */ + AstSpecFrame *sf; + AstFrameSet *fs; + double origin; + double neworigin; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do nothing if the SpecOrigin attribute has not been assigned a value. */ + if( astTestSpecOrigin( this ) ) { + +/* Do nothing if the rest frame will not change. */ + if( newsor != astGetStdOfRest( this ) ) { + +/* Save the original SpecOrigin value (in the current SpecFrame units) and then + clear it. */ + origin = GetSpecOriginCur( this, status ); + astClearSpecOrigin( this ); + +/* Take a copy of the SpecFrame and set the new StdOfRest. */ + sf = astCopy( this ); + astSetStdOfRest( sf, newsor ); + +/* Create a Mapping to perform the rest frame change, then use it to convert + the value to the new rest frame. */ + fs = astConvert( this, sf, "" ); + neworigin = AST__BAD; + if( fs ) { + astTran1( fs, 1, &origin, 1, &neworigin ); + fs = astAnnul( fs ); + } + +/* If succesful, convert from the current units to the default units, and store + in "this". */ + if( neworigin != AST__BAD ) { + astSetSpecOrigin( this, ToUnits( this, astGetUnit( this, 0 ), neworigin, + method, status ) ); + + } else if( astOK ) { + astError( AST__ATSER, "%s(%s): Cannot convert the SpecOrigin " + "value to a different rest frame.", status, method, + astGetClass( this ) ); + } + } + } +} + +static void OriginSystem( AstSpecFrame *this, AstSystemType oldsys, + const char *method, int *status ){ +/* +* Name: +* OriginSystem + +* Purpose: +* Convert the SpecOrigin in a SpecFrame to a new System. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void OriginSystem( AstSpecFrame *this, AstSystemType oldsys, +* const char *method, int *status ) + +* Class Membership: +* SpecFrame member function + +* Description: +* This function converts the value of the SpecOrigin attribute stored +* within a supplied SpecFrame from its original System, etc, to the +* System, etc, currently associated with the SpecFrame. + +* Parameters: +* this +* Point to the SpecFrame. On entry, the SpecOrigin value is +* assumed to refer to the System given by "oldsys", etc. On exit, the +* SpecOrigin value refers to the System returned by the astGetSystem +* method, etc. +* oldsys +* The System to which the SpecOrigin value stored within "this" +* refers on entry. +* method +* A string containing the method name for error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstSpecFrame *sf1; + AstSpecFrame *sf2; + AstFrameSet *fs; + double origin; + double neworigin; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do nothing if the SpecOrigin attribute has not been assigned a value. */ + if( astTestSpecOrigin( this ) ) { + +/* Do nothing if the System will not change. */ + if( oldsys != astGetSystem( this ) ) { + +/* Note the original SpecOrigin value, in the SpecFrame's default units. */ + origin = astGetSpecOrigin( this ); + +/* Take a copy of the original SpecFrame and ensure the Units, SpecOrigin and + AlignSpecOffset attributes are cleared. */ + sf1 = astCopy( this ); + astClearUnit( sf1, 0 ); + astClearSpecOrigin( sf1 ); + astClearAlignSpecOffset( sf1 ); + +/* Take another copy of the SpecFrame and set the old system. */ + sf2 = astCopy( sf1 ); + astSetSystem( sf2, oldsys ); + +/* Create a Mapping to perform the rest frame change, then use it to convert + the value to the current system. */ + fs = astConvert( sf2, sf1, "" ); + neworigin = AST__BAD; + if( fs ) { + astTran1( fs, 1, &origin, 1, &neworigin ); + fs = astAnnul( fs ); + } + +/* Free resources */ + sf1 = astAnnul( sf1 ); + sf2 = astAnnul( sf2 ); + +/* If succesful, store it in "this". */ + if( neworigin != AST__BAD ) { + astSetSpecOrigin( this, neworigin ); + + } else if( astOK ) { + astError( AST__ATSER, "%s(%s): Cannot convert the SpecOrigin " + "value to a different spectral system.", status, method, + astGetClass( this ) ); + } + } + } +} + + +static void Overlay( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template SpecFrame on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the protected astOverlay method +* inherited from the Frame class). + +* Description: +* This function overlays attributes of a SpecFrame (the "template") on to +* another Frame, so as to over-ride selected attributes of that second +* Frame. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which a Frame acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. +* +* Note that if the result Frame is a SpecFrame and a change of spectral +* coordinate system occurs as a result of overlaying its System +* attribute, then some of its original attribute values may no +* longer be appropriate (e.g. the Title, or attributes describing +* its axes). In this case, these will be cleared before overlaying +* any new values. + +* Parameters: +* template +* Pointer to the template SpecFrame, for which values should have been +* explicitly set for any attribute which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of the +* "result" Frame (see below). For each axis in the result frame, the +* corresponding element of this array should contain the (zero-based) +* index of the template axis to which it corresponds. This array is used +* to establish from which template axis any axis-dependent attributes +* should be obtained. +* +* If any axis in the result Frame is not associated with a template +* axis, the corresponding element of this array should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - In general, if the result Frame is not from the same class as the +* template SpecFrame, or from a class derived from it, then attributes may +* exist in the template SpecFrame which do not exist in the result Frame. +* In this case, these attributes will not be transferred. +*/ + +/* Local Variables: */ + AstFrame *templt; /* Copy of supplied template Frame */ + AstSystemType new_system; /* Code identifying new cordinates */ + AstSystemType old_system; /* Code identifying old coordinates */ + const char *method; /* Pointer to method string */ + const char *new_class; /* Pointer to template class string */ + const char *old_class; /* Pointer to result class string */ + int specframe; /* Result Frame is a SpecFrame? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise strings used in error messages. */ + new_class = astGetClass( template ); + old_class = astGetClass( result ); + method = "astOverlay"; + +/* Get the old and new systems. */ + old_system = astGetSystem( result ); + new_system = astGetSystem( template ); + +/* It may be necessary to make temporary changes to the template Frame + below. In order to ensure that we make no permanent changes to the + supplied frame, we will, if necessary, take a deep copy of the + supplied Frame, storing a pointer to the copy in "templt". If it is + not necessary to make any changes to the template, we still want + "templt" to hold a usable pointer, so we initialise it now to hold a + clone of the supplied pointer. This pointer will be replaced by a + pointer to a deep copy (if required) below. */ + templt = astClone( template ); + +/* If the result Frame is a SpecFrame, we must test to see if overlaying its + System attribute will change the type of coordinate system it describes. + Determine the value of this attribute for the result and template + SpecFrames. */ + specframe = astIsASpecFrame( result ); + if( specframe ) { + +/* If the coordinate system will change, any value already set for the result + SpecFrame's Title will no longer be appropriate, so clear it. */ + if ( new_system != old_system ) { + astClearTitle( result ); + +/* If the systems have the same default units, we can retain the current + Unit value. */ + if( strcmp( DefUnit( new_system, method, new_class, status ), + DefUnit( old_system, method, old_class, status ) ) ) { + astClearUnit( result, 0 ); + } + +/* If necessary, clear inappropriate values for all those axis attributes + whose access functions are over-ridden by this class (these access functions + will then provide suitable defaults appropriate to the new coordinate system + instead). */ + astClearLabel( result, 0 ); + astClearSymbol( result, 0 ); + } + +/* If the result Frame is not a SpecFrame, we must temporarily clear the + System and AlignSystem values since the values used by this class + are only appropriate to this class. Use a deep copy to avoid the danger + of making any permanent changes to the suppied Frame. */ + } else { + if( astTestSystem( template ) ) { + templt = astAnnul( templt ); + templt = astCopy( template ); + astClearSystem( templt ); + astClearAlignSystem( templt ); + } + } + +/* Invoke the parent class astOverlay method to transfer attributes inherited + from the parent class. */ + (*parent_overlay)( templt, template_axes, result, status ); + +/* Check if the result Frame is a SpecFrame or from a class derived from + SpecFrame. If not, we cannot transfer SpecFrame attributes to it as it is + insufficiently specialised. In this case simply omit these attributes. */ + if ( specframe && astOK ) { + +/* Define macros that test whether an attribute is set in the template and, + if so, transfers its value to the result. */ +#define OVERLAY(attribute) \ + if ( astTest##attribute( template ) ) { \ + astSet##attribute( result, astGet##attribute( template ) ); \ + } + +/* Use the macro to transfer each SpecFrame attribute in turn. Note, + SourceVRF must be overlayed before SourceVel. Otherwise the stored value + for SourceVel would be changed from the default SourceVRF to the specified + SourceVRF when SourceVRF was overlayed. */ + OVERLAY(AlignStdOfRest) + OVERLAY(AlignSpecOffset); + OVERLAY(RefDec) + OVERLAY(RefRA) + OVERLAY(RestFreq) + OVERLAY(SourceSys) + OVERLAY(SourceVRF) + OVERLAY(SourceVel) + OVERLAY(StdOfRest) + OVERLAY(SpecOrigin) + } + +/* Free resources */ + templt = astAnnul( templt ); + +/* Undefine macros local to this function. */ +#undef OVERLAY +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* SpecFrame member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a SpecFrame, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the SpecFrame. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This protected method is intended to be invoked by the Object astSet +* method and makes additional attributes accessible to it. +*/ + +/* Local Vaiables: */ + AstMapping *umap; /* Mapping between units */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + AstStdOfRestType sor; /* Standard of rest type code */ + AstSystemType sys; /* Spectral system type code */ + char *a; /* Pointer to next character */ + char *new_setting; /* Pointer value to new attribute setting */ + double dval; /* Double atribute value */ + double dtemp; /* Temporary double atribute value */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int ulen; /* Used length of setting string */ + int namelen; /* Length of attribute name in setting */ + int nc; /* Number of characters read by astSscanf */ + int off; /* Offset of attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_object; + +/* Create an FK5 J2000 SkyFrame which will be used for formatting and + unformatting sky positions, etc. */ + LOCK_MUTEX2 + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame( "system=FK5,equinox=J2000", status ); + astEndPM; + } + UNLOCK_MUTEX2 + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Obtain the used length of the setting string. */ + ulen = astChrLen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* First look for axis attributes defined by the Frame class. Since a + SpecFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strncmp( setting, "direction=", 10 ) || + !strncmp( setting, "bottom=", 7 ) || + !strncmp( setting, "top=", 4 ) || + !strncmp( setting, "format=", 7 ) || + !strncmp( setting, "label=", 6 ) || + !strncmp( setting, "symbol=", 7 ) || + !strncmp( setting, "unit=", 5 ) ) { + +/* Create a new setting string from the original by appending the string + "(1)" to the end of the attribute name and then use the parent SetAttrib + method. */ + new_setting = astMalloc( len + 4 ); + if( new_setting ) { + memcpy( new_setting, setting, len + 1 ); + a = strchr( new_setting, '=' ); + namelen = a - new_setting; + memcpy( a, "(1)", 4 ); + a += 3; + strcpy( a, setting + namelen ); + (*parent_setattrib)( this_object, new_setting, status ); + new_setting = astFree( new_setting ); + } + +/* AlignStdOfRest. */ +/* --------------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "alignstdofrest=%n%*s %n", &off, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a StdOfRest code before use. */ + sor = StdOfRestCode( setting + off, status ); + if ( sor != AST__BADSOR ) { + astSetAlignStdOfRest( this, sor ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid standard of rest " + "description \"%s\".", status, astGetClass( this ), setting+off ); + } + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in which + SpecFrame had GeoLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "geolat=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + new_setting = astStore( NULL, setting, len + 1 ); + new_setting[ 0 ] = 'o'; + new_setting[ 1 ] = 'b'; + new_setting[ 2 ] = 's'; + astSetAttrib( this, new_setting ); + new_setting = astFree( new_setting ); + +/* GeoLon. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "geolon=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + new_setting = astStore( NULL, setting, len + 1 ); + new_setting[ 0 ] = 'o'; + new_setting[ 1 ] = 'b'; + new_setting[ 2 ] = 's'; + astSetAttrib( this, new_setting ); + new_setting = astFree( new_setting ); + +/* RefDec. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "refdec=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + +/* Convert the string to a radians value before use. */ + ival = astUnformat( skyframe, 1, setting + off, &dval ); + if ( ival == ulen - off ) { + astSetRefDec( this, dval ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid reference " + "declination \"%s\".", status, astGetClass( this ), setting + off ); + } + +/* RefRA. */ +/* ------ */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "refra=%n%*s %n", &off, &nc ) ) + && ( nc >= 6 ) ) { + +/* Convert the string to a radians value before use. */ + ival = astUnformat( skyframe, 0, setting + off, &dval ); + if ( ival == ulen - off ) { + astSetRefRA( this, dval ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid reference right " + "ascension \"%s\".", status, astGetClass( this ), setting + off ); + } + +/* AlignSpecOffset. */ +/* ---------------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "alignspecoffset= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetAlignSpecOffset( this, ival ); + +/* RestFreq. */ +/* --------- */ +/* Without any units indication - assume GHz. Convert to Hz for storage. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "restfreq= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetRestFreq( this, dval*1.0E9 ); + +/* With units indication. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "restfreq= %lg %n%*s %n", &dval, &off, &nc ) ) + && ( nc >= len ) ) { + dtemp = AST__BAD; + +/* Is there a Mapping from the supplied units to Hz? If so, use the + Mapping to convert the supplied value to Hz. */ + if( ( umap = astUnitMapper( setting + off, "Hz", NULL, NULL ) ) ) { + astTran1( umap, 1, &dval, 1, &dtemp ); + umap = astAnnul( umap ); + +/* Otherwise, if there is a Mapping from the supplied units to metre, + assume the supplied unit is a vacuum wavelength. */ + } else if( ( umap = astUnitMapper( setting + off, "m", NULL, NULL ) ) ) { + +/* Convert the supplied wavelength to metres. */ + astTran1( umap, 1, &dval, 1, &dtemp ); + umap = astAnnul( umap ); + +/* Convert the wavelength (m) to frequency (Hz). */ + if( dtemp != AST__BAD && dtemp != 0.0 ) { + dtemp = AST__C/dtemp; + } else if( astOK ) { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid rest wavelength " + "\"%g %s\" supplied.", status, astGetClass( this ), dval, setting + off ); + } + +/* Otherwise, if there is a Mapping from the supplied units to Joule, + assume the supplied unit is an energy. */ + } else if( ( umap = astUnitMapper( setting + off, "J", NULL, NULL ) ) ) { + +/* Convert the supplied energy to Joules. */ + astTran1( umap, 1, &dval, 1, &dtemp ); + umap = astAnnul( umap ); + +/* Convert the energy (J) to frequency (Hz). */ + if( dtemp != AST__BAD ) { + dtemp *= 1.0/AST__H; + } else if( astOK ) { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid rest energy " + "\"%g %s\" supplied.", status, astGetClass( this ), dval, setting + off ); + } + +/* Otherwise report an error. */ + } else if( astOK ) { + astError( AST__ATTIN, "astSetAttrib(%s): Rest frequency given in an " + "unsupported system of units \"%g %s\".", status, + astGetClass( this ), dval, setting + off ); + } + +/* Set the rest frequency. */ + astSetRestFreq( this, dtemp ); + +/* SourceVel. */ +/* ---------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "sourcevel= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + +/* Convert from km/s to m/s if the SourceVel value is a velocity. */ + if( astGetSourceSys( this ) == AST__VREL || + astGetSourceSys( this ) == AST__VRADIO || + astGetSourceSys( this ) == AST__VOPTICAL ) dval *= 1.0E3; + +/* Store the value */ + astSetSourceVel( this, dval ); + +/* SourceVRF */ +/* --------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "sourcevrf=%n%*s %n", &off, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a StdOfRest code before use. */ + sor = StdOfRestCode( setting + off, status ); + if ( sor != AST__BADSOR ) { + astSetSourceVRF( this, sor ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid standard of rest " + "description \"%s\".", status, astGetClass( this ), setting+off ); + } + +/* SourceSys */ +/* --------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "sourcesys=%n%*s %n", &off, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a System code before use. */ + sys = SystemCode( (AstFrame *) this, setting + off, status ); + astSetSourceSys( this, sys ); + +/* StdOfRest. */ +/* ---------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "stdofrest=%n%*s %n", &off, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a StdOfRest code before use. */ + sor = StdOfRestCode( setting + off, status ); + if ( sor != AST__BADSOR ) { + astSetStdOfRest( this, sor ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid standard of rest " + "description \"%s\".", status, astGetClass( this ), setting + off ); + } + +/* SpecOrigin */ +/* ---------- */ + +/* Floating-point without any units indication - assume the current Unit + value. Convert from current units to default units for current system. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "specorigin= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + + astSetSpecOrigin( this, ToUnits( this, astGetUnit( this, 0 ), dval, + "astSetSpecOrigin", status ) ); + +/* Floating-point with units. Convert the supplied value to the default units + for the SpecFrame's System. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "specorigin= %lg %n%*s %n", &dval, &off, &nc ) ) + && ( nc >= len ) ) { + astSetSpecOrigin( this, ToUnits( this, setting + off, dval, "astSetSpecOrigin", status ) ); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetRefPos( AstSpecFrame *this, AstSkyFrame *frm, double lon, + double lat, int *status ){ +/* +*++ +* Name: +c astSetRefPos +f AST_SETREFPOS + +* Purpose: +* Set the reference position in a specified celestial coordinate system. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "specframe.h" +c void astSetRefPos( AstSpecFrame *this, AstSkyFrame *frm, double lon, +c double lat ) +f CALL AST_SETREFPOS( THIS, FRM, LON, LAT, STATUS ) + +* Class Membership: +* Frame method. + +* Description: +c This function +f This routine +* sets the reference position (see attributes RefRA and RefDec) using +* axis values (in radians) supplied within the celestial coordinate +* system represented by a supplied SkyFrame. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the SpecFrame. +c frm +f FRM = INTEGER (Given) +* Pointer to the SkyFrame which defines the celestial coordinate +* system in which the longitude and latitude values are supplied. +c If NULL +f If AST__NULL +* is supplied, then the supplied longitude and latitude values are +* assumed to be FK5 J2000 RA and Dec values. +c lon +f LON = DOUBLE PRECISION (Given) +* The longitude of the reference point, in the coordinate system +* represented by the supplied SkyFrame (radians). +c lat +f LAT = DOUBLE PRECISION (Given) +* The latitude of the reference point, in the coordinate system +* represented by the supplied SkyFrame (radians). +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + AstFrameSet *fs; /* Conversion FrameSet */ + AstFrame *fb; /* Base Frame */ + AstFrame *fc; /* Current Frame */ + double xin[ 1 ]; /* Axis 1 values */ + double yin[ 1 ]; /* Axis 2 values */ + double xout[ 1 ]; /* Axis 1 values */ + double yout[ 1 ]; /* Axis 2 values */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If no SkyFrame was supplied, just store the supplied RefRA and RefDec + values. */ + if( !frm ) { + astSetRefRA( this, lon ); + astSetRefDec( this, lat ); + +/* Otherwise, convert the supplied values from the requested system. */ + } else { + +/* Create an FK5 J2000 SkyFrame which will be used for formatting and + unformatting sky positions, etc. */ + LOCK_MUTEX2 + if( !skyframe ) { + astBeginPM; + skyframe = astSkyFrame( "system=FK5,equinox=J2000", status ); + astEndPM; + } + UNLOCK_MUTEX2 + +/* Find the Mapping from the supplied SkyFrame, to the SkyFrame which + describes the internal format in which the RefRA and RefDec attribute + values are stored. */ + fs = astFindFrame( frm, skyframe, "" ); + +/* If alignment was possible, use the Mapping to transform the supplied + axis values, checking to see if the axes of the supplied SkyFrame have + been permuted. */ + if( fs ) { + +/* Find the longitude axis in the Base Frame, and store the supplied + longitude and latitude values. */ + fb = astGetFrame( fs, AST__BASE ); + if( astGetLonAxis( fb ) == 0 ) { + xin[ 0 ] = lon; + yin[ 0 ] = lat; + } else { + xin[ 0 ] = lat; + yin[ 0 ] = lon; + } + astTran2( fs, 1, xin, yin, 1, xout, yout ); + +/* Store the corresponding RefRA and RefDec values. */ + fc = astGetFrame( fs, AST__CURRENT ); + if( astGetLonAxis( fc ) == 0 ) { + astSetRefRA( this, xout[ 0 ] ); + astSetRefDec( this, yout[ 0 ] ); + } else { + astSetRefRA( this, yout[ 0 ] ); + astSetRefDec( this, xout[ 0 ] ); + } + +/* Annul object references. */ + fc = astAnnul( fc ); + fb = astAnnul( fb ); + fs = astAnnul( fs ); + } + } +} + +static void SetStdOfRest( AstSpecFrame *this, AstStdOfRestType value, int *status ) { +/* +*+ +* Name: +* astSetStdOfRest + +* Purpose: +* Set the StdOfRest attribute for a SpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specframe.h" +* void astSetStdOfRest( AstSpecFrame *this, AstStdOfRestType value ) + +* Class Membership: +* SpecFrame virtual function + +* Description: +* This function set a new value for the StdOfRest attribute for a +* SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* value +* The new value. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + + +/* Validate the StdOfRest value being set and report an error if necessary. */ + if( value < FIRST_SOR || value > LAST_SOR ) { + astError( AST__ATTIN, "%s(%s): Bad value (%d) given for StdOfRest attribute.", status, + "astSetStdOfRest", astGetClass( this ), (int) value ); + +/* Otherwise set the new StdOfRest */ + } else { + +/* Modify the SpecOrigin value stored in the SpecFrame structure to refer + to the new rest frame. */ + OriginStdOfRest( this, value, "astSetStdOfRest", status ); + +/* Store the new value for the rest frame in the SpecFrame structure. */ + this->stdofrest = value; + + } +} + +static void SetSystem( AstFrame *this_frame, AstSystemType newsys, int *status ) { +/* +* Name: +* SetSystem + +* Purpose: +* Set the System attribute for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void SetSystem( AstFrame *this_frame, AstSystemType newsys, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astSetSystem protected +* method inherited from the Frame class). + +* Description: +* This function sets the System attribute for a SpecFrame. + +* Parameters: +* this +* Pointer to the SpecFrame. +* newsys +* The new System value to be stored. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to SpecFrame structure */ + AstSystemType oldsys; /* Original System value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* Save the original System value */ + oldsys = astGetSystem( this_frame ); + +/* Use the parent SetSystem method to store the new System value. */ + (*parent_setsystem)( this_frame, newsys, status ); + +/* If the system has changed... */ + if( oldsys != newsys ) { + +/* Changing the System value will in general require the Units to change + as well. If the user has previously specified the units to be used with + the new system, then re-instate them (they are stored in the "usedunits" + array in the SpecFrame structure). Otherwise, clear the units so that + the default units will eb used with the new System. */ + if( (int) newsys < this->nuunits && this->usedunits && + this->usedunits[ (int) newsys ] ) { + astSetUnit( this, 0, this->usedunits[ (int) newsys ] ); + } else { + astClearUnit( this, 0 ); + } + +/* Modify the stored SpecOrigin. */ + OriginSystem( this, oldsys, "astSetSystem", status ); + +/* Also, clear all attributes which have system-specific defaults. */ + astClearLabel( this_frame, 0 ); + astClearSymbol( this_frame, 0 ); + astClearTitle( this_frame ); + } +} + +static void SetUnit( AstFrame *this_frame, int axis, const char *value, int *status ) { +/* +* Name: +* SetUnit + +* Purpose: +* Set a pointer to the Unit string for a SpecFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void SetUnit( AstFrame *this_frame, int axis, const char *value ) + +* Class Membership: +* SpecFrame member function (over-rides the astSetUnit method inherited +* from the Frame class). + +* Description: +* This function stores a pointer to the Unit string for a specified axis +* of a SpecFrame. It also stores the string in the "usedunits" array +* in the SpecFrame structure, in the element associated with the +* current System. + +* Parameters: +* this +* Pointer to the SpecFrame. +* axis +* The number of the axis (zero-based) for which information is required. +* unit +* The new string to store. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + int i; /* Loop counter */ + int system; /* The SpecFrame's System value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astSetUnit" ); + +/* Store the supplied value as the UsedUnit for the current System. First + ensure the array is big enough. Free any previous value stored for the + current system. */ + system = (int) astGetSystem( this ); + if( system >= this->nuunits ) { + this->usedunits = astGrow( this->usedunits, system + 1, + sizeof(char *) ); + if( astOK ) { + for( i = this->nuunits; i < system + 1; i++ ) this->usedunits[ i ] = NULL; + this->nuunits = system + 1; + } + } + +/* Now store a copy of the value, if it is different to the stored string. */ + if( astOK && ( !this->usedunits[ system ] || + strcmp( this->usedunits[ system ], value ) ) ) { + this->usedunits[ system ] = astStore( this->usedunits[ system ], + value, strlen( value ) + 1 ); + } + +/* Now use the parent SetUnit method to store the value in the Axis + structure */ + (*parent_setunit)( this_frame, axis, value, status ); + +} + +static int SorConvert( AstSpecFrame *this, AstSpecFrame *that, + AstSpecMap *specmap, int *status ) { +/* +* Name: +* SorConvert + +* Purpose: +* Add a conversion to a SpecMap which transforms between two +* standards of rest. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int SorConvert( AstSpecFrame *this, AstSpecFrame *that, +* AstSpecMap *specmap, int *status ) + +* Class Membership: +* SpecFrame member function. + +* Description: +* This function adds a conversion to a SpecMap which transforms +* frequencies from the standard of rest specified by "this" to +* the standard of rest specified by "that". Note the conversion is +* always between frequency in the two rest frames no matter what the +* System attributes of the two SpecFrames may be (which are ignored). + +* Parameters: +* this +* The SpecFrame which defines the input rest frame. +* that +* The SpecFrame which defines the output rest frame. +* specmap +* The SpecMap to which the conversion is to be added. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero is returned if the conversion could not be performed. One is +* returned otherwise. + +*/ + +/* Local Constants: */ +#define MAX_ARGS 7 /* Max arguments for an SpecMap conversion */ + +/* Local Variables: */ + AstStdOfRestType from; /* Input standard of rest */ + AstStdOfRestType to; /* Output standard of rest */ + const char *vmess; /* Text for use in error messages */ + double args[ MAX_ARGS ]; /* Conversion argument array */ + double dec; /* DEC of source (radians, FK5 J2000) */ + double epoch; /* Epoch of observation (MJD) */ + double alt; /* Observers geodetic altitude (radians) */ + double lat; /* Observers geodetic latitude (radians) */ + double lon; /* Observers geodetic longitude (radians) */ + double ra; /* RA of source (radians, FK5 J2000) */ + int result; /* Returned value */ + +/* Initialise */ + result = 1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* No conversion is required if the rest frames are equal. */ + if( !EqualSor( this, that, status ) ) { + +/* Define local macros as shorthand for adding spectral coordinate + conversions to the SpecMap. Each macro simply stores details of + the additional arguments in the "args" array and then calls + astSpecAdd. The macros differ in the number of additional argument + values. */ +#define TRANSFORM_2(cvt,arg0,arg1) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + astSpecAdd( specmap, cvt, args ); + +#define TRANSFORM_3(cvt,arg0,arg1,arg2) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + astSpecAdd( specmap, cvt, args ); + +#define TRANSFORM_6(cvt,arg0,arg1,arg2,arg3,arg4,arg5) \ + args[ 0 ] = arg0; \ + args[ 1 ] = arg1; \ + args[ 2 ] = arg2; \ + args[ 3 ] = arg3; \ + args[ 4 ] = arg4; \ + args[ 5 ] = arg5; \ + astSpecAdd( specmap, cvt, args ); + +/* A string for use in error messages. */ + vmess = "convert between different standards of rest"; + +/* Get the required values from "this". */ + from = astGetStdOfRest( this ); + ra = astGetRefRA( this ); + dec = astGetRefDec( this ); + lon = astGetObsLon( this ); + lat = astGetObsLat( this ); + alt = astGetObsAlt( this ); + epoch = astGetEpoch( this ); + +/* Verify that the reference RA and DEC can be used (they are needed by all + the conversions used below). */ + VerifyAttrs( this, vmess, "RefRA RefDec", "astMatch", status ); + +/* Convert from the "this" rest frame to heliographic. */ + if( from == AST__TPSOR ) { + VerifyAttrs( this, vmess, "ObsLon ObsLat ObsAlt Epoch", "astMatch", status ); + TRANSFORM_6( "TPF2HL", lon, lat, alt, epoch, ra, dec ) + + } else if( from == AST__GESOR ) { + VerifyAttrs( this, vmess, "Epoch", "astMatch", status ); + TRANSFORM_3( "GEF2HL", epoch, ra, dec ) + + } else if( from == AST__BYSOR ) { + VerifyAttrs( this, vmess, "Epoch", "astMatch", status ); + TRANSFORM_3( "BYF2HL", epoch, ra, dec ) + + } else if( from == AST__LKSOR ) { + TRANSFORM_2( "LKF2HL", ra, dec ) + + } else if( from == AST__LDSOR ) { + TRANSFORM_2( "LDF2HL", ra, dec ) + + } else if( from == AST__LGSOR ) { + TRANSFORM_2( "LGF2HL", ra, dec ) + + } else if( from == AST__GLSOR ) { + TRANSFORM_2( "GLF2HL", ra, dec ) + + } else if( from == AST__SCSOR ) { + TRANSFORM_3( "USF2HL", ConvertSourceVel( this, AST__HLSOR, AST__VREL, status ), + ra, dec ) + } + +/* Now go from heliocentric to the "to" frame. */ + to = astGetStdOfRest( that ); + ra = astGetRefRA( that ); + dec = astGetRefDec( that ); + lon = astGetObsLon( that ); + lat = astGetObsLat( that ); + alt = astGetObsAlt( that ); + epoch = astGetEpoch( that ); + VerifyAttrs( that, vmess, "RefRA RefDec", "astMatch", status ); + + if( to == AST__TPSOR ) { + VerifyAttrs( that, vmess, "ObsLon ObsLat ObsAlt Epoch", "astMatch", status ); + TRANSFORM_6( "HLF2TP", lon, lat, alt, epoch, ra, dec ) + + } else if( to == AST__GESOR ) { + VerifyAttrs( that, vmess, "Epoch", "astMatch", status ); + TRANSFORM_3( "HLF2GE", epoch, ra, dec ) + + } else if( to == AST__BYSOR ) { + VerifyAttrs( that, vmess, "Epoch", "astMatch", status ); + TRANSFORM_3( "HLF2BY", epoch, ra, dec ) + + } else if( to == AST__LKSOR ) { + TRANSFORM_2( "HLF2LK", ra, dec ) + + } else if( to == AST__LDSOR ) { + TRANSFORM_2( "HLF2LD", ra, dec ) + + } else if( to == AST__LGSOR ) { + TRANSFORM_2( "HLF2LG", ra, dec ) + + } else if( to == AST__GLSOR ) { + TRANSFORM_2( "HLF2GL", ra, dec ) + + } else if( to == AST__SCSOR ) { + TRANSFORM_3( "HLF2US", ConvertSourceVel( that, AST__HLSOR, AST__VREL, status ), + ra, dec ) + } + } + +/* Return the result. */ + return result; + +/* Undefine macros local to this function. */ +#undef MAX_ARGS +#undef TRANSFORM_2 +#undef TRANSFORM_3 +#undef TRANSFORM_6 +} + +static const char *SpecMapUnit( AstSystemType system, const char *method, + const char *class, int *status ){ +/* +* Name: +* SpecMapUnit + +* Purpose: +* Return the default units for a spectral coordinate system type used +* by the SpecMap class. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *SpecMapUnit( AstSystemType system, const char *method, +* const char *class, int *status ) + +* Class Membership: +* SpecFrame member function. + +* Description: +* This function returns a textual representation of the +* units used by the SpecMap class for the specified spectral +* coordinate system. In general, the SpecMap class uses SI units +* (m/s, Hz, m, etc), but this class (SpecFrame) has default units +* more appropriate to astronomers (km/s, GHz, Angstroms, etc). + +* Parameters: +* system +* The spectral coordinate system. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A string describing the default units. This string follows the +* units syntax described in FITS WCS paper I "Representations of world +* coordinates in FITS" (Greisen & Calabretta). + +* Notes: +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Value to return */ + +/* Initialize */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get an identifier for the default units. */ + if( system == AST__FREQ ) { + result = "Hz"; + } else if( system == AST__ENERGY ) { + result = "J"; + } else if( system == AST__WAVENUM ) { + result = "1/m"; + } else if( system == AST__WAVELEN ) { + result = "m"; + } else if( system == AST__AIRWAVE ) { + result = "m"; + } else if( system == AST__VRADIO ) { + result = "m/s"; + } else if( system == AST__VOPTICAL ) { + result = "m/s"; + } else if( system == AST__REDSHIFT ) { + result = ""; + } else if( system == AST__BETA ) { + result = ""; + } else if( system == AST__VREL ) { + result = "m/s"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "%s(%s): Corrupt %s contains illegal System " + "identification code (%d).", status, method, class, class, + (int) system ); + } + +/* Return the result. */ + return result; +} + +static AstStdOfRestType StdOfRestCode( const char *sor, int *status ) { +/* +* Name: +* StdOfRestCode + +* Purpose: +* Convert a string into a standard of rest type code. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* AstStdOfRestType StdOfRestCode( const char *sor ) + +* Class Membership: +* SpecFrame member function. + +* Description: +* This function converts a string used for the external description of +* a standard of rest into a SpecFrame standard of rest type code +* (StdOfRest attribute value). It is the inverse of the +* StdOfRestString function. + +* Parameters: +* sor +* Pointer to a constant null-terminated string containing the +* external description of the standard of rest. + +* Returned Value: +* The StdOfRest type code. + +* Notes: +* - A value of AST__BADSOR is returned if the standard of rest +* description was not recognised. This does not produce an error. +* - A value of AST__BADSOR is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstStdOfRestType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSOR; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "sor" string against each possibility and assign the + result. */ + if ( astChrMatch( "TOPO", sor ) || astChrMatch( "TOPOCENT", sor ) || astChrMatch( "TOPOCENTRIC", sor ) ) { + result = AST__TPSOR; + + } else if ( astChrMatch( "GEO", sor ) || astChrMatch( "GEOCENTR", sor ) || astChrMatch( "GEOCENTRIC", sor ) ) { + result = AST__GESOR; + + } else if ( astChrMatch( "BARY", sor ) || astChrMatch( "BARYCENT", sor ) || astChrMatch( "BARYCENTRIC", sor ) ) { + result = AST__BYSOR; + + } else if ( astChrMatch( "HELIO", sor ) || astChrMatch( "HELIOCEN", sor ) || astChrMatch( "HELIOCENTRIC", sor ) ) { + result = AST__HLSOR; + + } else if ( astChrMatch( "LSRK", sor ) || astChrMatch( "LSR", sor ) ) { + result = AST__LKSOR; + + } else if ( astChrMatch( "LSRD", sor ) ) { + result = AST__LDSOR; + + } else if ( astChrMatch( "GAL", sor ) || astChrMatch( "GALACTOC", sor ) || astChrMatch( "GALACTIC", sor ) ) { + result = AST__GLSOR; + + } else if ( astChrMatch( "LG", sor ) || astChrMatch( "LOCALGRP", sor ) || + astChrMatch( "LOCAL_GROUP", sor ) || astChrMatch( "LOCAL-GROUP", sor ) ) { + result = AST__LGSOR; + + } else if ( astChrMatch( "SOURCE", sor ) || astChrMatch( "SRC", sor ) ) { + result = AST__SCSOR; + + } + +/* Return the result. */ + return result; +} + +static const char *StdOfRestString( AstStdOfRestType sor, int *status ) { +/* +* Name: +* StdOfRestString + +* Purpose: +* Convert a standard of rest type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *StdOfRestString( AstStdOfRestType sor, int *status ) + +* Class Membership: +* SpecFrame member function. + +* Description: +* This function converts a SpecFrame standard of rest type code +* (StdOfRest attribute value) into a string suitable for use as an +* external representation of the standard of rest type. + +* Parameters: +* sor +* The standard of rest type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the standard of rest +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "sor" value against each possibility and convert to a + string pointer. (Where possible, return the same string as would be + used in the FITS WCS representation of the standard of rest). */ + switch ( sor ) { + + case AST__TPSOR: + result = "Topocentric"; + break; + + case AST__GESOR: + result = "Geocentric"; + break; + + case AST__BYSOR: + result = "Barycentric"; + break; + + case AST__HLSOR: + result = "Heliocentric"; + break; + + case AST__LDSOR: + result = "LSRD"; + break; + + case AST__LKSOR: + result = "LSRK"; + break; + + case AST__LGSOR: + result = "Local_group"; + break; + + case AST__GLSOR: + result = "Galactic"; + break; + + case AST__SCSOR: + result = "Source"; + break; + + } + +/* Return the result pointer. */ + return result; +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a SpecFrame and convert to the new coordinate +* system. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the protected astSubFrame +* method inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the axes +* from a "target" SpecFrame and creates a new Frame with copies of +* the selected axes assembled in the requested order. It then +* optionally overlays the attributes of a "template" Frame on to the +* result. It returns both the resulting Frame and a Mapping that +* describes how to convert between the coordinate systems described by +* the target and result Frames. If necessary, this Mapping takes +* account of any differences in the Frames' attributes due to the +* influence of the template. + +* Parameters: +* target +* Pointer to the target SpecFrame, from which axes are to be +* selected. +* template +* Pointer to the template Frame, from which new attributes for the +* result Frame are to be obtained. Optionally, this may be NULL, in +* which case no overlaying of template attributes will be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This number may +* be greater than or less than the number of axes in this Frame (or +* equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving a list +* of the (zero-based) axis indices of the axes to be selected from the +* target SpecFrame. The order in which these are given determines +* the order in which the axes appear in the result Frame. If any of the +* values in this array is set to -1, the corresponding result axis will +* not be derived from the target Frame, but will be assigned default +* attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This should +* contain a list of the template axes (given as zero-based axis indices) +* with which the axes of the result Frame are to be associated. This +* array determines which axes are used when overlaying axis-dependent +* attributes of the template on to the result. If any element of this +* array is set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not used and +* a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned Mapping. +* The forward transformation of this Mapping will describe how to +* convert coordinates from the coordinate system described by the target +* SpecFrame to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is possible +* between the target and the result Frame. Otherwise zero is returned and +* *map and *result are returned as NULL (but this will not in itself +* result in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not always +* be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* - This implementation addresses the selection of axes from a +* SpecFrame object. This results in another object of the same class +* only if the single SpecFrame axis is selected exactly once. +* Otherwise, the result is a Frame class object which inherits the +* SpecFrame's axis information (if appropriate) but none of the other +* properties of a SpecFrame. +* - In the event that a SpecFrame results, the returned Mapping will +* take proper account of the relationship between the target and result +* coordinate systems. +* - In the event that a Frame class object results, the returned Mapping +* will only represent a selection/permutation of axes. + +* Implementation Deficiencies: +* - Any axis selection is currently permitted. Probably this should be +* restricted so that each axis can only be selected once. The +* astValidateAxisSelection method will do this but currently there are bugs +* in the CmpFrame class that cause axis selections which will not pass this +* test. Install the validation when these are fixed. +*/ + +/* Local Variables: */ + AstSpecFrame *target; /* Pointer to the SpecFrame structure */ + AstSpecFrame *temp; /* Pointer to copy of target SpecFrame */ + AstSpecFrame *align_frm; /* Frame in which to align the SpecFrames */ + int match; /* Coordinate conversion is possible? */ + int report; /* Report errors if SpecFrames cannot be aligned? */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the target SpecFrame structure. */ + target = (AstSpecFrame *) target_frame; + +/* Result is a SpecFrame. */ +/* -------------------------- */ +/* Check if the result Frame is to have one axis obtained by selecting + the single target SpecFrame axis. If so, the result will also be + a SpecFrame. */ + if ( ( result_naxes == 1 ) && ( target_axes[ 0 ] == 0 ) ) { + +/* Form the result from a copy of the target. */ + *result = astCopy( target ); + +/* Initialise a flag to indicate that MakeSpecMapping should not report + errors if no Mapping can be created. */ + report = 0; + +/* If required, overlay the template attributes on to the result SpecFrame. + Also get the system and standard of rest in which to align the two + SpecFrames. These are the values from the template (if there is a + template). */ + if ( template ) { + astOverlay( template, template_axes, *result ); + if( astIsASpecFrame( template ) ) { + align_frm = astCopy( template ); + +/* Since we now know that both the template and target are SpecFrames, it + should usually be possible to convert betwen them. If conversion is + *not* possible (fpr instance if no rest frequency is availalbe, etc) + then the user will probably be interested in knowing the reason why + conversion is not possible. Therefore, indicate that MakeSpecMapping + should report errors if no Mapping can be created. */ + report = 1; + + } else { + align_frm = astCopy( target ); + } + +/* If no template was supplied, align in the System and StdOfRest of the + target. */ + } else { + VerifyAttrs( target, "convert between different spectral systems", + "StdOfRest", "astMatch", status ); + align_frm = astCopy( target ); + } + +/* The MakeSpecMapping function uses the System and StdOfRest attributes to + define the alignment frame. But the AlignSystem and AlignStdOfRest + attributes should be used for this purpose. Therefore, copy the values + of the AlignSystem and AlignStdOfRest attributes to the System and + StdOfRest attribute. */ + astSetSystem( align_frm, astGetAlignSystem( align_frm ) ); + astSetStdOfRest( align_frm, astGetAlignStdOfRest( align_frm ) ); + +/* Generate a Mapping that takes account of changes in the sky coordinate + system (equinox, epoch, etc.) between the target SpecFrame and the result + SpecFrame. If this Mapping can be generated, set "match" to indicate that + coordinate conversion is possible. If the template is a specframe, + report errors if a match is not possible. */ + match = ( MakeSpecMapping( target, (AstSpecFrame *) *result, + align_frm, report, map, status ) != 0 ); + +/* Free resources. */ + align_frm = astAnnul( align_frm ); + +/* Result is not a SpecFrame. */ +/* ------------------------------ */ +/* In this case, we select axes as if the target were from the Frame + class. However, since the resulting data will then be separated + from their enclosing SpecFrame, default attribute values may differ + if the methods for obtaining them were over-ridden by the SpecFrame + class. To overcome this, we ensure that these values are explicitly + set for the result Frame (rather than relying on their defaults). */ + } else { + +/* Make a temporary copy of the target SpecFrame. We will explicitly + set the attribute values in this copy so as not to modify the original. */ + temp = astCopy( target ); + +/* Define a macro to test if an attribute is set. If not, set it + explicitly to its default value. */ +#define SET(attribute) \ + if ( !astTest##attribute( temp ) ) { \ + astSet##attribute( temp, astGet##attribute( temp ) ); \ + } + +/* Set attribute values which apply to the Frame as a whole and which + we want to retain, but whose defaults are over-ridden by the + SpecFrame class. */ + SET(Domain) + SET(Title) + +/* Define a macro to test if an attribute is set for axis zero (the only + axis of a SpecFrame). If not, set it explicitly to its default value. */ +#define SET_AXIS(attribute) \ + if ( !astTest##attribute( temp, 0 ) ) { \ + astSet##attribute( temp, 0, \ + astGet##attribute( temp, 0 ) ); \ + } + +/* Use this macro to set explicit values for all the axis attributes + for which the SpecFrame class over-rides the default value. */ + SET_AXIS(Label) + SET_AXIS(Symbol) + SET_AXIS(Unit) + +/* Clear attributes which have an extended range of values allowed by + this class. */ + astClearSystem( temp ); + astClearAlignSystem( temp ); + +/* Invoke the astSubFrame method inherited from the Frame class to + produce the result Frame by selecting the required set of axes and + overlaying the template Frame's attributes. */ + match = (*parent_subframe)( (AstFrame *) temp, template, + result_naxes, target_axes, template_axes, + map, result, status ); + +/* Delete the temporary copy of the target SpecFrame. */ + temp = astDelete( temp ); + } + +/* If an error occurred or no match was found, annul the returned + objects and reset the returned result. */ + if ( !astOK || !match ) { + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef SET +#undef SET_AXIS +} + +static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astSystemCode method +* inherited from the Frame class). + +* Description: +* This function converts a string used for the external +* description of a coordinate system into a SpecFrame +* coordinate system type code (System attribute value). It is the +* inverse of the astSystemString function. + +* Parameters: +* this +* The Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the sky coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the sky coordinate +* system description was not recognised. This does not produce an +* error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" string against each possibility and assign the + result. */ + if ( astChrMatch( "FREQ", system ) ) { + result = AST__FREQ; + + } else if ( astChrMatch( "ENER", system ) || astChrMatch( "ENERGY", system ) ) { + result = AST__ENERGY; + + } else if ( astChrMatch( "WAVN", system ) || astChrMatch( "WAVENUM", system ) ) { + result = AST__WAVENUM; + + } else if ( astChrMatch( "WAVE", system ) || astChrMatch( "WAVELEN", system ) ) { + result = AST__WAVELEN; + + } else if ( astChrMatch( "AWAV", system ) || astChrMatch( "AIRWAVE", system ) ) { + result = AST__AIRWAVE; + + } else if ( astChrMatch( "VRAD", system ) || astChrMatch( "VRADIO", system ) ) { + result = AST__VRADIO; + + } else if ( astChrMatch( "VOPT", system ) || astChrMatch( "VOPTICAL", system ) ) { + result = AST__VOPTICAL; + + } else if ( astChrMatch( "ZOPT", system ) || astChrMatch( "REDSHIFT", system ) ) { + result = AST__REDSHIFT; + + } else if ( astChrMatch( "BETA", system ) ) { + result = AST__BETA; + + } else if ( astChrMatch( "VELO", system ) || astChrMatch( "VREL", system ) ) { + result = AST__VREL; + + } + +/* Return the result. */ + return result; +} + +static const char *SystemLabel( AstSystemType system, int *status ) { +/* +* Name: +* SystemLabel + +* Purpose: +* Return a label for a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *SystemLabel( AstSystemType system, int *status ) + +* Class Membership: +* SpecFrame member function. + +* Description: +* This function converts a SpecFrame coordinate system type code +* (System attribute value) into a descriptive string for human readers. + +* Parameters: +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the sky coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. */ + switch ( system ) { + + case AST__FREQ: + result = "frequency"; + break; + + case AST__ENERGY: + result = "energy"; + break; + + case AST__WAVENUM: + result = "wave-number"; + break; + + case AST__WAVELEN: + result = "wavelength"; + break; + + case AST__AIRWAVE: + result = "wavelength in air"; + break; + + case AST__VRADIO: + result = "radio velocity"; + break; + + case AST__VOPTICAL: + result = "optical velocity"; + break; + + case AST__REDSHIFT: + result = "redshift"; + break; + + case AST__BETA: + result = "beta factor"; + break; + + case AST__VREL: + result = "apparent radial velocity"; + break; + } + +/* Return the result pointer. */ + return result; +} + +static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astSystemString method +* inherited from the Frame class). + +* Description: +* This function converts a SpecFrame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* The Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the sky coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. (Where possible, return the same string as would be + used in the FITS WCS representation of the coordinate system). */ + switch ( system ) { + + case AST__FREQ: + result = "FREQ"; + break; + + case AST__ENERGY: + result = "ENER"; + break; + + case AST__WAVENUM: + result = "WAVN"; + break; + + case AST__WAVELEN: + result = "WAVE"; + break; + + case AST__AIRWAVE: + result = "AWAV"; + break; + + case AST__VRADIO: + result = "VRAD"; + break; + + case AST__VOPTICAL: + result = "VOPT"; + break; + + case AST__REDSHIFT: + result = "ZOPT"; + break; + + case AST__BETA: + result = "BETA"; + break; + + case AST__VREL: + result = "VELO"; + break; + } + +/* Return the result pointer. */ + return result; +} + +static int TestActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* TestActiveUnit + +* Purpose: +* Test the ActiveUnit flag for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int TestActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astTestActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function test the value of the ActiveUnit flag for a SpecFrame, +* which is always "unset". + +* Parameters: +* this +* Pointer to the SpecFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The result of the test (0). + +*/ + return 0; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a SpecFrame's attributes. + +* Parameters: +* this +* Pointer to the SpecFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + char *new_attrib; /* Pointer value to new attribute name */ + int len; /* Length of attrib string */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* First look for axis attributes defined by the Frame class. Since a + SpecFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strcmp( attrib, "direction" ) || + !strcmp( attrib, "bottom" ) || + !strcmp( attrib, "top" ) || + !strcmp( attrib, "format" ) || + !strcmp( attrib, "label" ) || + !strcmp( attrib, "symbol" ) || + !strcmp( attrib, "unit" ) ) { + +/* Create a new attribute name from the original by appending the string + "(1)" and then use the parent TestAttrib method. */ + new_attrib = astMalloc( len + 4 ); + if( new_attrib ) { + memcpy( new_attrib, attrib, len ); + memcpy( new_attrib + len, "(1)", 4 ); + result = (*parent_testattrib)( this_object, new_attrib, status ); + new_attrib = astFree( new_attrib ); + } + +/* AlignStdOfRest. */ +/* --------------- */ + } else if ( !strcmp( attrib, "alignstdofrest" ) ) { + result = astTestAlignStdOfRest( this ); + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in which + SpecFrame had GeoLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( !strcmp( attrib, "geolat" ) ) { + result = astTestAttrib( this, "obslat" ); + +/* GeoLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "geolon" ) ) { + result = astTestAttrib( this, "obslon" ); + +/* RefDec. */ +/* ------- */ + } else if ( !strcmp( attrib, "refdec" ) ) { + result = astTestRefDec( this ); + +/* RefRA. */ +/* ------ */ + } else if ( !strcmp( attrib, "refra" ) ) { + result = astTestRefRA( this ); + +/* RestFreq. */ +/* --------- */ + } else if ( !strcmp( attrib, "restfreq" ) ) { + result = astTestRestFreq( this ); + +/* SourceVel. */ +/* ---------- */ + } else if ( !strcmp( attrib, "sourcevel" ) ) { + result = astTestSourceVel( this ); + +/* SourceVRF */ +/* --------- */ + } else if ( !strcmp( attrib, "sourcevrf" ) ) { + result = astTestSourceVRF( this ); + +/* SourceSys */ +/* --------- */ + } else if ( !strcmp( attrib, "sourcesys" ) ) { + result = astTestSourceSys( this ); + +/* StdOfRest. */ +/* ---------- */ + } else if ( !strcmp( attrib, "stdofrest" ) ) { + result = astTestStdOfRest( this ); + +/* SpecOrigin. */ +/* --------- */ + } else if ( !strcmp( attrib, "specorigin" ) ) { + result = astTestSpecOrigin( this ); + +/* AlignSpecOffset */ +/* --------------- */ + } else if ( !strcmp( attrib, "alignspecoffset" ) ) { + result = astTestAlignSpecOffset( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static double ToUnits( AstSpecFrame *this, const char *oldunit, double oldval, + const char *method, int *status ){ +/* +* +* Name: +* ToUnits + +* Purpose: +* Convert a supplied spectral value to the default units of the supplied +* SpecFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double ToUnits( AstSpecFrame *this, const char *oldunit, double oldval, +* const char *method, int *status ) + +* Class Membership: +* SpecFrame member function + +* Description: +* This function converts the supplied value from the supplied units to +* the default units associated with the supplied SpecFrame's System. + +* Parameters: +* this +* Pointer to the SpecFrame. +* oldunit +* The units in which "oldval" is supplied. +* oldval +* The value to be converted. +* method +* Pointer to a string holding the name of the method to be +* included in any error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The converted value. + +*/ + +/* Local Variables: */ + AstMapping *map; + const char *defunit; + double result; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get default units associated with the System attribute of the supplied + SpecFrame, and find a Mapping from the old units to the default. */ + defunit = DefUnit( astGetSystem( this ), method, "SpecFrame", status ); + map = astUnitMapper( oldunit, defunit, NULL, NULL ); + if( map ) { + +/* Use the Mapping to convert the supplied value. */ + astTran1( map, 1, &oldval, 1, &result ); + +/* Free resources. */ + map = astAnnul( map ); + +/* Report an error if no conversion is possible. */ + } else if( astOK ){ + astError( AST__BADUN, "%s(%s): Cannot convert the supplied attribute " + "value from units of %s to %s.", status, method, astGetClass( this ), + oldunit, defunit ); + } + +/* Return the result */ + return result; +} + + +static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) { +/* +* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "specframe.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* SpecFrame member function (over-rides the astValidateSystem method +* inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST__BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the value is out of bounds, report an error. */ + if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) { + astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System " + "or AlignSystem attribute of a %s.", status, method, + astGetClass( this ), (int) system, astGetClass( this ) ); + +/* Otherwise, return the supplied value. */ + } else { + result = system; + } + +/* Return the result. */ + return result; +} + +static void VerifyAttrs( AstSpecFrame *this, const char *purp, + const char *attrs, const char *method, int *status ) { +/* +* Name: +* VerifyAttrs + +* Purpose: +* Verify that usable attribute values are available. + +* Type: +* Private function. + +* Synopsis: +* #include "specframe.h" +* void VerifyAttrs( AstSpecFrame *this, const char *purp, +* const char *attrs, const char *method, int *status ) + +* Class Membership: +* SpecFrame member function + +* Description: +* This function tests each attribute listed in "attrs". It returns +* without action if 1) an explicit value has been set for each attribute +* or 2) the UseDefs attribute of the supplied SpecFrame is non-zero. +* +* If UseDefs is zero (indicating that default values should not be +* used for attributes), and any of the named attributes does not have +* an explicitly set value, then an error is reported. + +* Parameters: +* this +* Pointer to the SpecFrame. +* purp +* Pointer to a text string containing a message which will be +* included in any error report. This shouldindicate the purpose +* for which the attribute value is required. +* attrs +* A string holding a space separated list of attribute names. +* method +* A string holding the name of the calling method for use in error +* messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + const char *a; + const char *desc; + const char *p; + int len; + int set; + int state; + +/* Check inherited status */ + if( !astOK ) return; + +/* If the SpecFrame has a non-zero value for its UseDefs attribute, then + all attributes are assumed to have usable values, since the defaults + will be used if no explicit value has been set. So we only need to do + any checks if UseDefs is zero. */ + if( !astGetUseDefs( this ) ) { + +/* Stop compiler warnings about uninitialised variables */ + a = NULL; + desc = NULL; + len = 0; + set = 0; + +/* Loop round the "attrs" string identifying the start and length of each + non-blank word in the string. */ + state = 0; + p = attrs; + while( 1 ) { + if( state == 0 ) { + if( !isspace( *p ) ) { + a = p; + len = 1; + state = 1; + } + } else { + if( isspace( *p ) || !*p ) { + +/* The end of a word has just been reached. Compare it to each known + attribute value. Get a flag indicating if the attribute has a set + value, and a string describing the attribute.*/ + if( len > 0 ) { + + if( !strncmp( "ObsLat", a, len ) ) { + set = astTestObsLat( this ); + desc = "observer's latitude"; + + } else if( !strncmp( "ObsLon", a, len ) ) { + set = astTestObsLon( this ); + desc = "observer's longitude"; + + } else if( !strncmp( "ObsAlt", a, len ) ) { + set = astTestObsAlt( this ); + desc = "observer's altitude"; + + } else if( !strncmp( "RefRA", a, len ) ) { + set = astTestRefRA( this ); + desc = "source RA"; + + } else if( !strncmp( "RefDec", a, len ) ) { + set = astTestRefDec( this ); + desc = "source Dec"; + + } else if( !strncmp( "RestFreq", a, len ) ) { + set = astTestRestFreq( this ); + desc = "rest frequency"; + + } else if( !strncmp( "SourceVel", a, len ) ) { + set = astTestSourceVel( this ); + desc = "source velocity"; + + } else if( !strncmp( "StdOfRest", a, len ) ) { + set = astTestStdOfRest( this ); + desc = "spectral standard of rest"; + + } else if( !strncmp( "Epoch", a, len ) ) { + set = astTestEpoch( this ); + desc = "epoch of observation"; + + } else { + astError( AST__INTER, "VerifyAttrs(SpecFrame): " + "Unknown attribute name \"%.*s\" supplied (AST " + "internal programming error).", status, len, a ); + } + +/* If the attribute does not have a set value, report an error. */ + if( !set && astOK ) { + astError( AST__NOVAL, "%s(%s): Cannot %s.", status, method, + astGetClass( this ), purp ); + astError( AST__NOVAL, "No value has been set for " + "the AST \"%.*s\" attribute (%s).", status, len, a, + desc ); + } + +/* Continue the word search algorithm. */ + } + len = 0; + state = 0; + } else { + len++; + } + } + if( !*(p++) ) break; + } + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* +*att++ +* Name: +* AlignSpecOffset + +* Purpose: +* Align SpecFrames using the offset coordinate system? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how a SpecFrame +* behaves when it is used (by +c astFindFrame or astConvert) as a template to match another (target) +f AST_FINDFRAME or AST_CONVERT) as a template to match another (target) +* SpecFrame. It determines whether alignment occurs between the offset +* values defined by the current value of the SpecOffset attribute, or +* between the corresponding absolute spectral values. +* +* The default value of zero results in the two SpecFrames being aligned +* so that a given absolute spectral value in one is mapped to the same +* absolute value in the other. A non-zero value results in the SpecFrames +* being aligned so that a given offset value in one is mapped to the same +* offset value in the other. + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. +*att-- +*/ +astMAKE_CLEAR(SpecFrame,AlignSpecOffset,alignspecoffset,-INT_MAX) +astMAKE_GET(SpecFrame,AlignSpecOffset,int,0,( ( this->alignspecoffset != -INT_MAX ) ? + this->alignspecoffset : 0 )) +astMAKE_SET(SpecFrame,AlignSpecOffset,int,alignspecoffset,( value != 0 )) +astMAKE_TEST(SpecFrame,AlignSpecOffset,( this->alignspecoffset != -INT_MAX )) + + + +/* +*att++ +* Name: +* AlignStdOfRest + +* Purpose: +* Standard of rest to use when aligning SpecFrames. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls how a SpecFrame behaves when it is used (by +c astFindFrame or astConvert) as a template to match another (target) +f AST_FINDFRAME or AST_CONVERT) as a template to match another (target) +* SpecFrame. It identifies the standard of rest in which alignment is +* to occur. See the StdOfRest attribute for a desription of the values +* which may be assigned to this attribute. The default AlignStdOfRest +* value is "Helio" (heliographic). +* +c When astFindFrame or astConvert is used on two SpecFrames (potentially +f When AST_FindFrame or AST_CONVERT is used on two SpecFrames (potentially +* describing different spectral coordinate systems), it returns a Mapping +* which can be used to transform a position in one SpecFrame into the +* corresponding position in the other. The Mapping is made up of the +* following steps in the indicated order: +* +* - Map values from the system used by the target (wavelength, +* apparent radial velocity, etc) to the system specified by the +* AlignSystem attribute, using the target's rest frequency if necessary. +* +* - Map these values from the target's standard of rest to the standard of +* rest specified by the AlignStdOfRest attribute, using the Epoch, ObsLat, +* ObsLon, ObsAlt, RefDec and RefRA attributes of the target to define the +* two standards of rest. +* +* - Map these values from the standard of rest specified by the +* AlignStdOfRest attribute, to the template's standard of rest, using the +* Epoch, ObsLat, ObsLon, ObsAlt, RefDec and RefRA attributes of the +* template to define the two standards of rest. +* +* - Map these values from the system specified by the AlignSystem +* attribute, to the system used by the template, using the template's +* rest frequency if necessary. + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +*att-- +*/ +/* The AlignStdOfRest value has a value of AST__BADSOR when not set yielding + a default of AST__HLSOR. */ +astMAKE_TEST(SpecFrame,AlignStdOfRest,( this->alignstdofrest != AST__BADSOR )) +astMAKE_CLEAR(SpecFrame,AlignStdOfRest,alignstdofrest,AST__BADSOR) +astMAKE_GET(SpecFrame,AlignStdOfRest,AstStdOfRestType,AST__BADSOR,( + ( this->alignstdofrest == AST__BADSOR ) ? AST__HLSOR : this->alignstdofrest ) ) + +/* Validate the AlignStdOfRest value being set and report an error if necessary. */ +astMAKE_SET(SpecFrame,AlignStdOfRest,AstStdOfRestType,alignstdofrest,( + ( ( value >= FIRST_SOR ) && ( value <= LAST_SOR ) ) ? + value : + ( astError( AST__ATTIN, "%s(%s): Bad value (%d) " + "given for AlignStdOfRest attribute.", status, + "astSetAlignStdOfRest", astGetClass( this ), (int) value ), + +/* Leave the value unchanged on error. */ + this->alignstdofrest ) ) ) + +/* +*att++ +* Name: +* RefDec + +* Purpose: +* The declination of the reference point + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute specifies the FK5 J2000.0 declination of a reference +* point on the sky. See the description of attribute RefRA for details. +* The default RefDec is "0:0:0". + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +*att-- +*/ +/* The reference declination (FK5 J2000, radians). Clear the RefDec value by + setting it to AST__BAD, which results in a default value of zero. Any + value is acceptable. */ +astMAKE_CLEAR(SpecFrame,RefDec,refdec,AST__BAD) +astMAKE_GET(SpecFrame,RefDec,double,0.0,((this->refdec!=AST__BAD)?this->refdec:0.0)) +astMAKE_SET(SpecFrame,RefDec,double,refdec,value) +astMAKE_TEST(SpecFrame,RefDec,( this->refdec != AST__BAD )) + +/* +*att++ +* Name: +* RefRA + +* Purpose: +* The right ascension of the reference point + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute, together with the RefDec attribute, specifies the FK5 +* J2000.0 coordinates of a reference point on the sky. For 1-dimensional +* spectra, this should normally be the position of the source. For +* spectral data with spatial coverage (spectral cubes, etc), this should +* be close to centre of the spatial coverage. It is used to define the +* correction for Doppler shift to be applied when using the +c astFindFrame or astConvert +f AST_FINDFRAME or AST_CONVERT +* method to convert between different standards of rest. +* +* The SpecFrame class assumes this velocity correction is spatially +* invariant. If a single SpecFrame is used (for instance, as a +* component of a CmpFrame) to describe spectral values at different +* points on the sky, then it is assumes that the doppler shift at any +* spatial position is the same as at the reference position. The +* maximum velocity error introduced by this assumption is of the order +* of V*SIN(FOV), where FOV is the angular field of view, and V is the +* relative velocity of the two standards of rest. As an example, when +* correcting from the observers rest frame (i.e. the topocentric rest +* frame) to the kinematic local standard of rest the maximum value of V +* is about 20 km/s, so for 5 arc-minute field of view the maximum velocity +* error introduced by the correction will be about 0.03 km/s. As another +* example, the maximum error when correcting from the observers rest frame +* to the local group is about 5 km/s over a 1 degree field of view. +* +* The RefRA and RefDec attributes are stored internally in radians, but +* are converted to and from a string for access. The format "hh:mm:ss.ss" +* is used for RefRA, and "dd:mm:ss.s" is used for RefDec. The methods +c astSetRefPos and astGetRefPos may be used to access the values of +f AST_SETREFPOS and AST_GETREFPOS may be used to access the value of +* these attributes directly as unformatted values in radians. +* +* The default for RefRA is "0:0:0". + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +*att-- +*/ +/* The reference right ascension (FK5 J2000, radians). Clear the RefRA value + by setting it to AST__BAD, which gives a default value of 0.0. Any + value is acceptable. */ +astMAKE_CLEAR(SpecFrame,RefRA,refra,AST__BAD) +astMAKE_GET(SpecFrame,RefRA,double,0.0,((this->refra!=AST__BAD)?this->refra:0.0)) +astMAKE_SET(SpecFrame,RefRA,double,refra,value) +astMAKE_TEST(SpecFrame,RefRA,( this->refra != AST__BAD )) + + +/* +*att++ +* Name: +* RestFreq + +* Purpose: +* The rest frequency. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the frequency corresponding to zero +* velocity. It is used when converting between between velocity-based +* coordinate systems and and other coordinate systems (such as frequency, +* wavelength, energy, etc). The default value is 1.0E5 GHz. +* +* When setting a new value for this attribute, the new value can be +* supplied either directly as a frequency, or indirectly as a wavelength +* or energy, in which case the supplied value is converted to a frequency +* before being stored. The nature of the supplied value is indicated by +* appending text to the end of the numerical value indicating the units in +* which the value is supplied. If the units are not specified, then the +* supplied value is assumed to be a frequency in units of GHz. If the +* supplied unit is a unit of frequency, the supplied value is assumed to +* be a frequency in the given units. If the supplied unit is a unit of +* length, the supplied value is assumed to be a (vacuum) wavelength. If +* the supplied unit is a unit of energy, the supplied value is assumed to +* be an energy. For instance, the following strings all result in +* a rest frequency of around 1.4E14 Hz being used: "1.4E5", "1.4E14 Hz", +* "1.4E14 s**-1", "1.4E5 GHz", "2.14E-6 m", "21400 Angstrom", "9.28E-20 J", +* "9.28E-13 erg", "0.58 eV", etc. +* +* When getting the value of this attribute, the returned value is +* always a frequency in units of GHz. + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +*att-- +*/ +/* The rest frequency (Hz). Clear the RestFreq value by setting it to AST__BAD, + which gives 1.0E14 as the default value. Any value is acceptable. */ +astMAKE_CLEAR(SpecFrame,RestFreq,restfreq,AST__BAD) +astMAKE_GET(SpecFrame,RestFreq,double,1.0E14,((this->restfreq!=AST__BAD)?this->restfreq:1.0E14)) +astMAKE_SET(SpecFrame,RestFreq,double,restfreq,value) +astMAKE_TEST(SpecFrame,RestFreq,( this->restfreq != AST__BAD )) + +/* +*att++ +* Name: +* SourceVel + +* Purpose: +* The source velocity. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute (together with SourceSys, SourceVRF, RefRA and RefDec) +* defines the "Source" standard of rest (see attribute StdOfRest). This is +* a rest frame which is moving towards the position given by RefRA and +* RefDec at a velocity given by SourceVel. A positive value means +* the source is moving away from the observer. When a new value is +* assigned to this attribute, the supplied value is assumed to refer +* to the spectral system specified by the SourceSys attribute. For +* instance, the SourceVel value may be supplied as a radio velocity, a +* redshift, a beta factor, etc. Similarly, when the current value of +* the SourceVel attribute is obtained, the returned value will refer +* to the spectral system specified by the SourceSys value. If the +* SourceSys value is changed, any value previously stored for the SourceVel +* attribute will be changed automatically from the old spectral system +* to the new spectral system. +* +* When setting a value for SourceVel, the value should be supplied in the +* rest frame specified by the SourceVRF attribute. Likewise, when getting +* the value of SourceVel, it will be returned in the rest frame specified +* by the SourceVRF attribute. +* +* The default SourceVel value is zero. + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +* Notes: +* - It is important to set an appropriate value for SourceVRF and +* SourceSys before setting a value for SourceVel. If a new value is later +* set for SourceVRF or SourceSys, the value stored for SourceVel will +* simultaneously be changed to the new standard of rest or spectral +* system. + +*att-- +*/ +/* The source velocity (velocities are stored internally in m/s). Clear it + by setting it to AST__BAD, which returns a default value of zero. Any + value is acceptable. */ +astMAKE_CLEAR(SpecFrame,SourceVel,sourcevel,AST__BAD) +astMAKE_SET(SpecFrame,SourceVel,double,sourcevel,value) +astMAKE_TEST(SpecFrame,SourceVel,( this->sourcevel != AST__BAD )) +astMAKE_GET(SpecFrame,SourceVel,double,0.0,((this->sourcevel!=AST__BAD)?this->sourcevel:0.0)) + +/* +*att++ +* Name: +* SourceVRF + +* Purpose: +* Rest frame in which the source velocity is stored. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute identifies the rest frame in which the source +* velocity or redshift is stored (the source velocity or redshift is +* accessed using attribute SourceVel). When setting a new value for the +* SourceVel attribute, the source velocity or redshift should be supplied +* in the rest frame indicated by this attribute. Likewise, when getting +* the value of the SourceVel attribute, the velocity or redshift will be +* returned in this rest frame. +* +* If the value of SourceVRF is changed, the value stored for SourceVel +* will be converted from the old to the new rest frame. +* +* The values which can be supplied are the same as for the StdOfRest +* attribute (except that SourceVRF cannot be set to "Source"). The +* default value is "Helio". + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +*att-- +*/ +/* The SourceVRF value has a value of AST__BADSOR when not set yielding + a default of AST__HLSOR. */ +astMAKE_TEST(SpecFrame,SourceVRF,( this->sourcevrf != AST__BADSOR )) +astMAKE_GET(SpecFrame,SourceVRF,AstStdOfRestType,AST__BADSOR,( + ( this->sourcevrf == AST__BADSOR ) ? AST__HLSOR : this->sourcevrf ) ) + +/* When clearing SourceVRF, convert the SourceVel value to heliocentric + (but only if set)*/ +astMAKE_CLEAR(SpecFrame,SourceVRF,sourcevrf,((astTestSourceVel( this )? + (void)(astSetSourceVel( this, ConvertSourceVel( this, AST__HLSOR, + astGetSourceSys( this ), status ) ),NULL): + NULL),AST__BADSOR)) + +/* Validate the SourceVRF value being set and report an error if necessary. + If OK, convert the stored SourceVel value into the new rest frame (but +only if set)*/ +astMAKE_SET(SpecFrame,SourceVRF,AstStdOfRestType,sourcevrf,( + ( ( value >= FIRST_SOR ) && ( value <= LAST_SOR ) && value != AST__SCSOR ) ? + (astTestSourceVel( this )? + (void)(astSetSourceVel( this,ConvertSourceVel(this,value,astGetSourceSys( this ), status )),NULL): + NULL), value:( astError( AST__ATTIN, "%s(%s): Bad value (%d) " + "given for SourceVRF attribute.", status, + "astSetSourceVRF", astGetClass( this ), (int) value ), + +/* Leave the value unchanged on error. */ + this->sourcevrf ) ) ) + +/* +*att++ +* Name: +* SourceSys + +* Purpose: +* Spectral system in which the source velocity is stored. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute identifies the spectral system in which the +* SourceVel attribute value (the source velocity) is supplied and +* returned. It can be one of the following: +* +* - "VRAD" or "VRADIO": Radio velocity (km/s) +* - "VOPT" or "VOPTICAL": Optical velocity (km/s) +* - "ZOPT" or "REDSHIFT": Redshift (dimensionless) +* - "BETA": Beta factor (dimensionless) +* - "VELO" or "VREL": Apparent radial ("relativistic") velocity (km/s) +* +* When setting a new value for the SourceVel attribute, the source +* velocity should be supplied in the spectral system indicated +* by this attribute. Likewise, when getting the value of the SourceVel +* attribute, the velocity will be returned in this spectral system. +* +* If the value of SourceSys is changed, the value stored for SourceVel +* will be converted from the old to the new spectral systems. +* +* The default value is "VELO" (apparent radial velocity). + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +*att-- +*/ +/* The SourceSys value has a value of AST__BADSYS when not set yielding + a default of AST__VREL. */ +astMAKE_TEST(SpecFrame,SourceSys,( this->sourcesys != AST__BADSYSTEM )) +astMAKE_GET(SpecFrame,SourceSys,AstSystemType,AST__BADSYSTEM,( + ( this->sourcesys == AST__BADSYSTEM ) ? AST__VREL : this->sourcesys ) ) + +/* When clearing SourceSys, convert the SourceVel value to relativistic + velocity (but only if set) */ +astMAKE_CLEAR(SpecFrame,SourceSys,sourcesys,((astTestSourceVel( this )? +(void)(astSetSourceVel( this, ConvertSourceVel( this, astGetSourceVRF( this ), + AST__VREL, status ) ),NULL):NULL),AST__BADSYSTEM)) + +/* Validate the SourceSys value being set and report an error if necessary. + If OK, convert the stored SourceVel value into the new rest frame (but + only if set)*/ +astMAKE_SET(SpecFrame,SourceSys,AstSystemType,sourcesys,( + ( ( value == AST__VREL ) || ( value == AST__BETA ) || + ( value == AST__VRADIO ) || ( value == AST__REDSHIFT ) || + ( value == AST__VOPTICAL ) ) ? + (astTestSourceVel( this )? + (void)(astSetSourceVel( this, ConvertSourceVel( this, astGetSourceVRF( this ), + value, status )),NULL):NULL), + value: + ( astError( AST__ATTIN, "%s(%s): Bad value (%d) " + "given for SourceSys attribute.", status, + "astSetSourceSys", astGetClass( this ), (int) value ), + +/* Leave the value unchanged on error. */ + this->sourcesys ) ) ) + +/* +*att++ +* Name: +* StdOfRest + +* Purpose: +* Standard of rest. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute identifies the standard of rest to which the spectral +* axis values of a SpecFrame refer, and may take any of the values +* listed in the "Standards of Rest" section (below). +* +* The default StdOfRest value is "Helio". + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +* Standards of Rest: +* The SpecFrame class supports the following StdOfRest values (all are +* case-insensitive): +* +* - "Topocentric", "Topocent" or "Topo": The observers rest-frame (assumed +* to be on the surface of the earth). Spectra recorded in this standard of +* rest suffer a Doppler shift which varies over the course of a day +* because of the rotation of the observer around the axis of the earth. +* This standard of rest must be qualified using the ObsLat, ObsLon, +* ObsAlt, Epoch, RefRA and RefDec attributes. +* +* - "Geocentric", "Geocentr" or "Geo": The rest-frame of the earth centre. +* Spectra recorded in this standard of rest suffer a Doppler shift which +* varies over the course of a year because of the rotation of the earth +* around the Sun. This standard of rest must be qualified using the Epoch, +* RefRA and RefDec attributes. +* +* - "Barycentric", "Barycent" or "Bary": The rest-frame of the solar-system +* barycentre. Spectra recorded in this standard of rest suffer a Doppler +* shift which depends both on the velocity of the Sun through the Local +* Standard of Rest, and on the movement of the planets through the solar +* system. This standard of rest must be qualified using the Epoch, RefRA +* and RefDec attributes. +* +* - "Heliocentric", "Heliocen" or "Helio": The rest-frame of the Sun. +* Spectra recorded in this standard of rest suffer a Doppler shift which +* depends on the velocity of the Sun through the Local Standard of Rest. +* This standard of rest must be qualified using the RefRA and RefDec +* attributes. +* +* - "LSRK", "LSR": The rest-frame of the kinematical Local Standard of +* Rest. Spectra recorded in this standard of rest suffer a Doppler shift +* which depends on the velocity of the kinematical Local Standard of Rest +* through the galaxy. This standard of rest must be qualified using the +* RefRA and RefDec attributes. +* +* - "LSRD": The rest-frame of the dynamical Local Standard of Rest. Spectra +* recorded in this standard of rest suffer a Doppler shift which depends +* on the velocity of the dynamical Local Standard of Rest through the +* galaxy. This standard of rest must be qualified using the RefRA and +* RefDec attributes. +* +* - "Galactic", "Galactoc" or "Gal": The rest-frame of the galactic centre. +* Spectra recorded in this standard of rest suffer a Doppler shift which +* depends on the velocity of the galactic centre through the local group. +* This standard of rest must be qualified using the RefRA and RefDec +* attributes. +* +* - "Local_group", "Localgrp" or "LG": The rest-frame of the local group. +* This standard of rest must be qualified using the RefRA and RefDec +* attributes. +* +* - "Source", or "src": The rest-frame of the source. This standard of +* rest must be qualified using the RefRA, RefDec and SourceVel attributes. +* +* Where more than one alternative System value is shown above, the +* first of these will be returned when an enquiry is made. +*att-- +*/ +/* The StdOfRest value has a value of AST__BADSOR when not set yielding + a default of AST__HLSOR. */ +astMAKE_TEST(SpecFrame,StdOfRest,( this->stdofrest != AST__BADSOR )) +astMAKE_GET(SpecFrame,StdOfRest,AstStdOfRestType,AST__BADSOR,( + ( this->stdofrest == AST__BADSOR ) ? AST__HLSOR : this->stdofrest ) ) + +/* +*att++ +* Name: +* SpecOrigin + +* Purpose: +* The zero point for SpecFrame axis values + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This specifies the origin from which all spectral values are measured. +* The default value (zero) results in the SpecFrame describing +* absolute spectral values in the system given by the System attribute +* (e.g. frequency, velocity, etc). If a SpecFrame is to be used to +* describe offset from some origin, the SpecOrigin attribute +* should be set to hold the required origin value. The SpecOrigin value +* stored inside the SpecFrame structure is modified whenever SpecFrame +* attribute values are changed so that it refers to the original spectral +* position. +* +* When setting a new value for this attribute, the supplied value is assumed +* to be in the system, units and standard of rest described by the SpecFrame. +* Likewise, when getting the value of this attribute, the value is returned +* in the system, units and standard of rest described by the SpecFrame. If +* any of these attributes are changed, then any previously stored SpecOrigin +* value will also be changed so that refers to the new system, units or +* standard of rest. + +* Applicability: +* SpecFrame +* All SpecFrames have this attribute. + +*att-- +*/ +/* The spec origin, stored internally in the default units associated + with the current System value. Clear the SpecOrigin value by setting it + to AST__BAD, which gives 0.0 as the default value. Any value is acceptable. */ +astMAKE_CLEAR(SpecFrame,SpecOrigin,specorigin,AST__BAD) +astMAKE_GET(SpecFrame,SpecOrigin,double,0.0,((this->specorigin!=AST__BAD)?this->specorigin:0.0)) +astMAKE_SET(SpecFrame,SpecOrigin,double,specorigin,value) +astMAKE_TEST(SpecFrame,SpecOrigin,( this->specorigin != AST__BAD )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SpecFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SpecFrame objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstSpecFrame *in; /* Pointer to input SpecFrame */ + AstSpecFrame *out; /* Pointer to output SpecFrame */ + char *usedunit; /* Pointer to an element of usedunits array */ + int i; /* Loop count */ + int nused; /* Size of "usedunits" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SpecFrames. */ + in = (AstSpecFrame *) objin; + out = (AstSpecFrame *) objout; + +/* Nullify the pointers stored in the output object since these will + currently be pointing at the input data (since the output is a simple + byte-for-byte copy of the input). Otherwise, the input data could be + freed by accidient if the output object is deleted due to an error + occuring in this function. */ + out->usedunits = NULL; + +/* Store the last used units in the output SpecMap. */ + if( in && in->usedunits ) { + nused = in->nuunits; + out->usedunits = astMalloc( nused*sizeof( char * ) ); + if( out->usedunits ) { + for( i = 0; i < nused; i++ ) { + usedunit = in->usedunits[ i ]; + if( usedunit ) { + out->usedunits[ i ] = astStore( NULL, usedunit, + strlen( usedunit ) + 1 ); + } else { + out->usedunits[ i ] = NULL; + } + } + } + } + +/* If an error has occurred, free the output resources. */ + if( !astOK ) Delete( (AstObject *) out, status ); + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SpecFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SpecFrame objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSpecFrame *this; + int i; + +/* Release the memory referred to in the SpecFrame structure. */ + this = (AstSpecFrame *) obj; + if( this && this->usedunits ) { + for( i = 0; i < this->nuunits; i++ ) { + this->usedunits[ i ] = astFree( this->usedunits[ i ] ); + } + this->usedunits = astFree( this->usedunits ); + } +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SpecFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SpecFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the SpecFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSpecFrame *this; /* Pointer to the SpecFrame structure */ + AstStdOfRestType sor; /* StdOfRest attribute value */ + AstSystemType sys; /* Spectral system value */ + char buff[ 20 ]; /* Buffer for item name */ + char comm[ 50 ]; /* Buffer for comment */ + const char *sval; /* Pointer to string value */ + double dval; /* Double value */ + int i; /* Loop count */ + int ival; /* int value */ + int j; /* Loop count */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecFrame structure. */ + this = (AstSpecFrame *) this_object; + +/* Write out values representing the instance variables for the + SpecFrame class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* StdOfRest. */ +/* ---------- */ + set = TestStdOfRest( this, status ); + sor = set ? GetStdOfRest( this, status ) : astGetStdOfRest( this ); + +/* If set, convert explicitly to a string for the external + representation. */ + sval = ""; + if ( set ) { + if ( astOK ) { + sval = StdOfRestString( sor, status ); + +/* Report an error if the StdOfRest value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "%s(%s): Corrupt %s contains invalid standard of rest " + "identification code (%d).", status, "astWrite", + astGetClass( channel ), astGetClass( this ), (int) sor ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "stdofrest" ); + } + +/* Write out the value. */ + astWriteString( channel, "SoR", set, 1, sval, "Standard of rest" ); + +/* AlignStdOfRest. */ +/* --------------- */ + set = TestAlignStdOfRest( this, status ); + sor = set ? GetAlignStdOfRest( this, status ) : astGetAlignStdOfRest( this ); + +/* If set, convert explicitly to a string for the external representation. */ + if ( set ) { + if ( astOK ) { + sval = StdOfRestString( sor, status ); + +/* Report an error if the StdOfRest value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "%s(%s): Corrupt %s contains invalid alignment standard " + "of rest identification code (%d).", status, "astWrite", + astGetClass( channel ), astGetClass( this ), (int) sor ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "alignstdofrest" ); + } + +/* Write out the value. */ + astWriteString( channel, "AlSoR", set, 0, sval, "Alignment standard of rest" ); + +/* RefRA. */ +/* ------ */ + set = TestRefRA( this, status ); + dval = set ? GetRefRA( this, status ) : astGetRefRA( this ); + astWriteDouble( channel, "RefRA", set, 0, dval, "Reference RA (rads, FK5 J2000)" ); + +/* RefDec. */ +/* ------- */ + set = TestRefDec( this, status ); + dval = set ? GetRefDec( this, status ) : astGetRefDec( this ); + astWriteDouble( channel, "RefDec", set, 0, dval, "Reference Dec (rads, FK5 J2000)" ); + +/* RestFreq. */ +/* --------- */ + set = TestRestFreq( this, status ); + dval = set ? GetRestFreq( this, status ) : astGetRestFreq( this ); + astWriteDouble( channel, "RstFrq", set, 0, dval, "Rest frequency (Hz)" ); + +/* SourceVel. */ +/* ---------- */ + set = TestSourceVel( this, status ); + dval = set ? GetSourceVel( this, status ) : astGetSourceVel( this ); + astWriteDouble( channel, "SrcVel", set, 0, dval, "Source velocity (m/s)" ); + +/* SourceVRF. */ +/* ---------- */ + set = TestSourceVRF( this, status ); + sor = set ? GetSourceVRF( this, status ) : astGetSourceVRF( this ); + +/* If set, convert explicitly to a string for the external representation. */ + if ( set ) { + if ( astOK ) { + sval = StdOfRestString( sor, status ); + +/* Report an error if the value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "%s(%s): Corrupt %s contains invalid source velocity " + "rest frame identification code (%d).", status, "astWrite", + astGetClass( channel ), astGetClass( this ), (int) sor ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "sourcevrf" ); + } + +/* Write out the value. */ + astWriteString( channel, "SrcVRF", set, 0, sval, "Source velocity rest frame" ); + +/* SourceSys. */ +/* ---------- */ + set = TestSourceSys( this, status ); + sys = set ? GetSourceSys( this, status ) : astGetSourceSys( this ); + +/* If set, convert explicitly to a string for the external representation. */ + if ( set ) { + if ( astOK ) { + sval = SystemString( (AstFrame *) this, sys, status ); + +/* Report an error if the value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "%s(%s): Corrupt %s contains invalid source velocity " + "spectral system identification code (%d).", status, "astWrite", + astGetClass( channel ), astGetClass( this ), (int) sys ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "sourcesys" ); + } + +/* Write out the value. */ + astWriteString( channel, "SrcSys", set, 0, sval, "Source velocity spectral system" ); + +/* AlignSpecOffset. */ +/* ---------------- */ + set = TestAlignSpecOffset( this, status ); + ival = set ? GetAlignSpecOffset( this, status ) : astGetAlignSpecOffset( this ); + astWriteInt( channel, "AlSpOf", set, 0, ival, + ival ? "Align in offset coords" : + "Align in system coords" ); + +/* UsedUnits */ +/* --------- */ + if( this->usedunits ) { + for( i = 0; i < this->nuunits; i++ ) { + if( this->usedunits[ i ] ) { + sprintf( buff, "U%s", astSystemString( this, (AstSystemType) i )); + for( j = 2; j < strlen( buff ); j++ ) buff[ j ] = tolower( buff[ j ] ); + sprintf( comm, "Preferred units for %s", SystemLabel( (AstSystemType) i, status ) ); + astWriteString( channel, buff, 1, 0, this->usedunits[ i ], comm ); + } + } + } + +/* SpecOrigin. */ +/* ----------- */ + set = TestSpecOrigin( this, status ); + dval = set ? GetSpecOrigin( this, status ) : astGetSpecOrigin( this ); + if( dval != AST__BAD ) { + astWriteDouble( channel, "SpOrg", set, 0, dval, "Spec offset" ); + } + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASpecFrame and astCheckSpecFrame functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SpecFrame,Frame) +astMAKE_CHECK(SpecFrame) + +AstSpecFrame *astSpecFrame_( const char *options, int *status, ...) { +/* +*+ +* Name: +* astSpecFrame + +* Purpose: +* Create a SpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specframe.h" +* AstSpecFrame *astSpecFrame( const char *options, int *status, ... ) + +* Class Membership: +* SpecFrame constructor. + +* Description: +* This function creates a new SpecFrame and optionally initialises its +* attributes. + +* Parameters: +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new SpecFrame. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new SpecFrame. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic SpecFrame constructor which +* is available via the protected interface to the SpecFrame class. +* A public interface is provided by the astSpecFrameId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *um; /* Mapping from default to actual units */ + AstSpecFrame *new; /* Pointer to new SpecFrame */ + AstSystemType s; /* System */ + const char *u; /* Units string */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SpecFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSpecFrame( NULL, sizeof( AstSpecFrame ), !class_init, + &class_vtab, "SpecFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SpecFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* Check the Units are appropriate for the System. */ + u = astGetUnit( new, 0 ); + s = astGetSystem( new ); + um = astUnitMapper( DefUnit( s, "astSpecFrame", "SpecFrame", status ), + u, NULL, NULL ); + if( um ) { + um = astAnnul( um ); + } else { + astError( AST__BADUN, "astSpecFrame: Inappropriate units (%s) " + "specified for a %s axis.", status, u, SystemLabel( s, status ) ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SpecFrame. */ + return new; +} + +AstSpecFrame *astInitSpecFrame_( void *mem, size_t size, int init, + AstSpecFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSpecFrame + +* Purpose: +* Initialise a SpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specframe.h" +* AstSpecFrame *astInitSpecFrame( void *mem, size_t size, int init, +* AstFrameVtab *vtab, const char *name ) + +* Class Membership: +* SpecFrame initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new SpecFrame object. It allocates memory (if +* necessary) to accommodate the SpecFrame plus any additional data +* associated with the derived class. It then initialises a +* SpecFrame structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual function +* table for a SpecFrame at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SpecFrame is to be +* created. This must be of sufficient size to accommodate the +* SpecFrame data (sizeof(SpecFrame)) plus any data used by +* the derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SpecFrame (plus derived +* class data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also stored +* in the SpecFrame structure, so a valid value must be supplied +* even if not required for allocating memory. +* init +* A logical flag indicating if the SpecFrame's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SpecFrame. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object belongs +* (it is this pointer value that will subsequently be returned by +* the astGetClass method). + +* Returned Value: +* A pointer to the new SpecFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSpecFrame *new; /* Pointer to the new SpecFrame */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSpecFrameVtab( vtab, name ); + +/* Initialise a 1D Frame structure (the parent class) as the first component + within the SpecFrame structure, allocating memory if necessary. */ + new = (AstSpecFrame *) astInitFrame( mem, size, 0, + (AstFrameVtab *) vtab, name, 1 ); + + if ( astOK ) { + +/* Initialise the SpecFrame data. */ +/* ----------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->alignstdofrest = AST__BADSOR; + new->refdec = AST__BAD; + new->refra = AST__BAD; + new->restfreq = AST__BAD; + new->sourcevel = AST__BAD; + new->sourcevrf = AST__BADSOR; + new->sourcesys = AST__BADSYSTEM; + new->stdofrest = AST__BADSOR; + new->nuunits = 0; + new->usedunits = NULL; + new->specorigin = AST__BAD; + new->alignspecoffset = -INT_MAX; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSpecFrame *astLoadSpecFrame_( void *mem, size_t size, + AstSpecFrameVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSpecFrame + +* Purpose: +* Load a SpecFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "specframe.h" +* AstSpecFrame *astLoadSpecFrame( void *mem, size_t size, +* AstSpecFrameVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* SpecFrame loader. + +* Description: +* This function is provided to load a new SpecFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SpecFrame structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the SpecFrame is to be +* loaded. This must be of sufficient size to accommodate the +* SpecFrame data (sizeof(SpecFrame)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SpecFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SpecFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSpecFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SpecFrame. If this is NULL, a pointer +* to the (static) virtual function table for the SpecFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SpecFrame" is used instead. + +* Returned Value: +* A pointer to the new SpecFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSpecFrame *new; /* Pointer to the new SpecFrame */ + char buff[ 20 ]; /* Buffer for item name */ + char *sval; /* Pointer to string value */ + double obslat; /* Value for ObsLat attribute */ + double obslon; /* Get a pointer to the thread specific global data structure. */ + +/* Value for ObsLon attribute */ + int i; /* Loop count */ + int j; /* Loop count */ + int nc; /* String length */ + int sys; /* System value */ + + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SpecFrame. In this case the + SpecFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSpecFrame ); + vtab = &class_vtab; + name = "SpecFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSpecFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SpecFrame. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SpecFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* StdOfRest. */ +/* ---------- */ +/* Set the default and read the external representation as a string. */ + new->stdofrest = AST__BADSOR; + sval = astReadString( channel, "sor", NULL ); + +/* If a value was read, convert from a string to a StdOfRest code. */ + if ( sval ) { + if ( astOK ) { + new->stdofrest = StdOfRestCode( sval, status ); + +/* Report an error if the value wasn't recognised. */ + if ( new->stdofrest == AST__BADSOR ) { + astError( AST__ATTIN, + "astRead(%s): Invalid standard of rest description " + "\"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* AlignStdOfRest. */ +/* --------------- */ +/* Set the default and read the external representation as a string. */ + new->alignstdofrest = AST__BADSOR; + sval = astReadString( channel, "alsor", NULL ); + +/* If a value was read, convert from a string to a StdOfRest code. */ + if ( sval ) { + if ( astOK ) { + new->alignstdofrest = StdOfRestCode( sval, status ); + +/* Report an error if the value wasn't recognised. */ + if ( new->alignstdofrest == AST__BADSOR ) { + astError( AST__ATTIN, + "astRead(%s): Invalid alignment standard of rest " + "description \"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in + which SpecFrame had a GeoLat attribute (now ObsLat is used instead). */ + if( !astTestObsLat( new ) ) { + obslat = astReadDouble( channel, "geolat", AST__BAD ); + if ( obslat != AST__BAD ) astSetObsLat( new, obslat ); + } + +/* GeoLon. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in + which SpecFrame had a GeoLon attribute (now ObsLon is used instead). */ + if( !astTestObsLon( new ) ) { + obslon = astReadDouble( channel, "geolon", AST__BAD ); + if ( obslon != AST__BAD ) astSetObsLon( new, obslon ); + } + +/* RefRA. */ +/* ------ */ + new->refra = astReadDouble( channel, "refra", AST__BAD ); + if ( TestRefRA( new, status ) ) SetRefRA( new, new->refra, status ); + +/* RefDec. */ +/* ------- */ + new->refdec = astReadDouble( channel, "refdec", AST__BAD ); + if ( TestRefDec( new, status ) ) SetRefDec( new, new->refdec, status ); + +/* RestFreq. */ +/* --------- */ + new->restfreq = astReadDouble( channel, "rstfrq", AST__BAD ); + if ( TestRestFreq( new, status ) ) SetRestFreq( new, new->restfreq, status ); + +/* AlignSpecOffset */ +/* --------------- */ + new->alignspecoffset = astReadInt( channel, "alspof", -INT_MAX ); + if ( TestAlignSpecOffset( new, status ) ) SetAlignSpecOffset( new, new->alignspecoffset, status ); + +/* SourceVel. */ +/* ---------- */ + new->sourcevel = astReadDouble( channel, "srcvel", AST__BAD ); + if ( TestSourceVel( new, status ) ) SetSourceVel( new, new->sourcevel, status ); + +/* SourceVRF */ +/* --------- */ +/* Set the default and read the external representation as a string. */ + new->sourcevrf = AST__BADSOR; + sval = astReadString( channel, "srcvrf", NULL ); + +/* If a value was read, convert from a string to a StdOfRest code. */ + if ( sval ) { + if ( astOK ) { + new->sourcevrf = StdOfRestCode( sval, status ); + +/* Report an error if the value wasn't recognised. */ + if ( new->sourcevrf == AST__BADSOR ) { + astError( AST__ATTIN, + "astRead(%s): Invalid source velocity rest frame " + "description \"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* SourceSys */ +/* --------- */ +/* Set the default and read the external representation as a string. */ + new->sourcesys = AST__BADSYSTEM; + sval = astReadString( channel, "srcsys", NULL ); + +/* If a value was read, convert from a string to a System code. */ + if ( sval ) { + if ( astOK ) { + new->sourcesys = SystemCode( (AstFrame *) new, sval, status ); + +/* Report an error if the value wasn't recognised. */ + if ( new->sourcesys == AST__BADSYSTEM ) { + astError( AST__ATTIN, + "astRead(%s): Invalid source velocity spectral system " + "description \"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* UsedUnits */ +/* --------- */ + new->nuunits = 0; + new->usedunits = NULL; + for( sys = FIRST_SYSTEM; sys <= LAST_SYSTEM; sys++ ) { + nc = sprintf( buff, "u%s", astSystemString( new, (AstSystemType) sys )); + for( j = 0; j < nc; j++ ) buff[ j ] = tolower( buff[ j ] ); + sval = astReadString( channel, buff, NULL ); + if( sval ) { + if( (int) sys >= new->nuunits ) { + new->usedunits = astGrow( new->usedunits, sys + 1, + sizeof(char *) ); + if( astOK ) { + for( i = new->nuunits; i < sys + 1; i++ ) new->usedunits[ i ] = NULL; + new->nuunits = sys + 1; + } + } else { + new->usedunits[ sys ] = astFree( new->usedunits[ sys ] ); + } + if( astOK ) { + new->usedunits[ sys ] = astStore( new->usedunits[ sys ], + sval, strlen( sval ) + 1 ); + } + sval = astFree( sval); + } + } + +/* SpecOrigin. */ +/* --------- */ + new->specorigin = astReadDouble( channel, "sporg", AST__BAD ); + if ( TestSpecOrigin( new, status ) ) SetSpecOrigin( new, new->specorigin, status ); + + +/* If an error occurred, clean up by deleting the new SpecFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SpecFrame pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astGetRefPos_( AstSpecFrame *this, AstSkyFrame *frm, double *lon, + double *lat, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,SpecFrame,GetRefPos))(this,frm,lon,lat, status ); +} +void astSetRefPos_( AstSpecFrame *this, AstSkyFrame *frm, double lon, + double lat, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,SpecFrame,SetRefPos))(this,frm,lon,lat, status ); +} + +void astSetStdOfRest_( AstSpecFrame *this, AstStdOfRestType value, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,SpecFrame,SetStdOfRest))(this,value, status ); +} + +void astClearStdOfRest_( AstSpecFrame *this, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,SpecFrame,ClearStdOfRest))(this, status ); +} + + + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSpecFrame *astSpecFrameId_( const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstSpecFrame *astSpecFrameId_( const char *options, ... ) { +/* +*++ +* Name: +c astSpecFrame +f AST_SPECFRAME + +* Purpose: +* Create a SpecFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "specframe.h" +c AstSpecFrame *astSpecFrame( const char *options, ... ) +f RESULT = AST_SPECFRAME( OPTIONS, STATUS ) + +* Class Membership: +* SpecFrame constructor. + +* Description: +* This function creates a new SpecFrame and optionally initialises +* its attributes. +* +* A SpecFrame is a specialised form of one-dimensional Frame which +* represents various coordinate systems used to describe positions within +* an electro-magnetic spectrum. The particular coordinate system to be +* used is specified by setting the SpecFrame's System attribute (the +* default is wavelength) qualified, as necessary, by other attributes +* such as the rest frequency, the standard of rest, the epoch of +* observation, etc (see the description of the System attribute for +* details). +* +* By setting a value for thr SpecOrigin attribute, a SpecFrame can be made +* to represent offsets from a given spectral position, rather than absolute + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SpecFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SpecFrame. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSpecFrame() +f AST_SPECFRAME = INTEGER +* A pointer to the new SpecFrame. + +* Examples: +c frame = astSpecFrame( "" ); +f FRAME = AST_SPECFRAME( ' ', STATUS ) +* Creates a SpecFrame to describe the default wavelength spectral +* coordinate system. The RestFreq attribute (rest frequency) is +* unspecified, so it will not be possible to align this SpecFrame +* with another SpecFrame on the basis of a velocity-based system. The +* standard of rest is also unspecified. This means that alignment +* will be possible with other SpecFrames, but no correction will be +* made for Doppler shift caused by change of rest frame during the +* alignment. +c frame = astSpecFrame( "System=VELO, RestFreq=1.0E15, StdOfRest=LSRK" ); +f FRAME = AST_SPECFRAME( 'System=VELO, RestFreq=1.0E15, StdOfRest=LSRK', STATUS ) +* Creates a SpecFrame describing a apparent radial velocity ("VELO") axis +* with rest frequency 1.0E15 Hz (about 3000 Angstroms), measured +* in the kinematic Local Standard of Rest ("LSRK"). Since the +* source position has not been specified (using attributes RefRA and +* RefDec), it will only be possible to align this SpecFrame with +* other SpecFrames which are also measured in the LSRK standard of +* rest. + +* Notes: +* - When conversion between two SpecFrames is requested (as when +c supplying SpecFrames to astConvert), +f supplying SpecFrames AST_CONVERT), +* account will be taken of the nature of the spectral coordinate systems +* they represent, together with any qualifying rest frequency, standard +* of rest, epoch values, etc. The AlignSystem and AlignStdOfRest +* attributes will also be taken into account. The results will therefore +* fully reflect the relationship between positions measured in the two +* systems. In addition, any difference in the Unit attributes of the two +* systems will also be taken into account. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astSpecFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astSpecFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astSpecFrame_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *um; /* Mapping from default to actual units */ + AstSpecFrame *new; /* Pointer to new SpecFrame */ + AstSystemType s; /* System */ + const char *u; /* Units string */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + astGET_GLOBALS(NULL); /* Get a pointer to the thread specific global data structure. */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SpecFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSpecFrame( NULL, sizeof( AstSpecFrame ), !class_init, + &class_vtab, "SpecFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SpecFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* Check the Units are appropriate for the System. */ + u = astGetUnit( new, 0 ); + s = astGetSystem( new ); + um = astUnitMapper( DefUnit( s, "astSpecFrame", "SpecFrame", status ), + u, NULL, NULL ); + if( um ) { + um = astAnnul( um ); + } else { + astError( AST__BADUN, "astSpecFrame: Inappropriate units (%s) " + "specified for a %s axis.", status, u, SystemLabel( s, status ) ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new SpecFrame. */ + return astMakeId( new ); +} + diff --git a/specframe.h b/specframe.h new file mode 100644 index 0000000..34d8eac --- /dev/null +++ b/specframe.h @@ -0,0 +1,430 @@ +#if !defined( SPECFRAME_INCLUDED ) /* Include this file only once */ +#define SPECFRAME_INCLUDED +/* +*+ +* Name: +* specframe.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SpecFrame class. + +* Invocation: +* #include "specframe.h" + +* Description: +* This include file defines the interface to the SpecFrame class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 12-NOV-2002 (DSB): +* Original version. +* 18-OCT-2006 (DSB): +* Added SpecOrigin. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "frame.h" /* Parent Frame class */ +#include "skyframe.h" /* Celestial coordinate systems */ + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + + +#if defined(astCLASS) /* Protected */ + +/* Values used to represent different System attribute values. */ +#define AST__FREQ 1 +#define AST__ENERGY 2 +#define AST__WAVENUM 3 +#define AST__WAVELEN 4 +#define AST__AIRWAVE 5 +#define AST__VRADIO 6 +#define AST__VOPTICAL 7 +#define AST__REDSHIFT 8 +#define AST__BETA 9 +#define AST__VREL 10 + +/* Values used to represent different StdOfRest attribute values. */ +#define AST__BADSOR 0 +#define AST__TPSOR 1 +#define AST__GESOR 2 +#define AST__BYSOR 3 +#define AST__HLSOR 4 +#define AST__LDSOR 5 +#define AST__LKSOR 6 +#define AST__LGSOR 7 +#define AST__GLSOR 8 +#define AST__SCSOR 9 +#endif + +/* Type Definitions. */ +/* ================= */ + +/* Integer type used to store the spectral StdOfRest attribute. */ +typedef int AstStdOfRestType; + +/* SpecFrame structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstSpecFrame { + +/* Attributes inherited from the parent class. */ + AstFrame frame; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstStdOfRestType alignstdofrest;/* Code identifying alignment StdOfRest */ + AstStdOfRestType stdofrest; /* Standard of rest */ + double refdec; /* Dec (FK5 J2000) of source */ + double refra; /* RA (FK5 J2000) of source */ + double restfreq; /* Rest frequency (Hz)*/ + double sourcevel; /* Source velocity (heliocentric, m/s) */ + AstStdOfRestType sourcevrf; /* Code identifying source vel. StdOfRest */ + AstSystemType sourcesys; /* Code identifying source vel. system */ + int nuunits; /* Size of usedunits array */ + char **usedunits; /* Last used units for each system */ + double specorigin; /* Origin for sectral values */ + int alignspecoffset; /* Align SpecFrame in offset coords? */ +} AstSpecFrame; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSpecFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* GetRefPos)( AstSpecFrame *, AstSkyFrame *, double *, double *, int * ); + void (* SetRefPos)( AstSpecFrame *, AstSkyFrame *, double, double, int * ); + + AstStdOfRestType (* GetStdOfRest)( AstSpecFrame *, int * ); + int (* TestStdOfRest)( AstSpecFrame *, int * ); + void (* ClearStdOfRest)( AstSpecFrame *, int * ); + void (* SetStdOfRest)( AstSpecFrame *, AstStdOfRestType, int * ); + + AstStdOfRestType (* GetAlignStdOfRest)( AstSpecFrame *, int * ); + int (* TestAlignStdOfRest)( AstSpecFrame *, int * ); + void (* ClearAlignStdOfRest)( AstSpecFrame *, int * ); + void (* SetAlignStdOfRest)( AstSpecFrame *, AstStdOfRestType, int * ); + + AstStdOfRestType (* GetSourceVRF)( AstSpecFrame *, int * ); + int (* TestSourceVRF)( AstSpecFrame *, int * ); + void (* ClearSourceVRF)( AstSpecFrame *, int * ); + void (* SetSourceVRF)( AstSpecFrame *, AstStdOfRestType, int * ); + + AstSystemType (* GetSourceSys)( AstSpecFrame *, int * ); + int (* TestSourceSys)( AstSpecFrame *, int * ); + void (* ClearSourceSys)( AstSpecFrame *, int * ); + void (* SetSourceSys)( AstSpecFrame *, AstSystemType, int * ); + + double (* GetRestFreq)( AstSpecFrame *, int * ); + int (* TestRestFreq)( AstSpecFrame *, int * ); + void (* ClearRestFreq)( AstSpecFrame *, int * ); + void (* SetRestFreq)( AstSpecFrame *, double, int * ); + + double (* GetRefRA)( AstSpecFrame *, int * ); + int (* TestRefRA)( AstSpecFrame *, int * ); + void (* ClearRefRA)( AstSpecFrame *, int * ); + void (* SetRefRA)( AstSpecFrame *, double, int * ); + + double (* GetRefDec)( AstSpecFrame *, int * ); + int (* TestRefDec)( AstSpecFrame *, int * ); + void (* ClearRefDec)( AstSpecFrame *, int * ); + void (* SetRefDec)( AstSpecFrame *, double, int * ); + + double (* GetSourceVel)( AstSpecFrame *, int * ); + int (* TestSourceVel)( AstSpecFrame *, int * ); + void (* ClearSourceVel)( AstSpecFrame *, int * ); + void (* SetSourceVel)( AstSpecFrame *, double, int * ); + + double (* GetSpecOrigin)( AstSpecFrame *, int * ); + int (* TestSpecOrigin)( AstSpecFrame *, int * ); + void (* ClearSpecOrigin)( AstSpecFrame *, int * ); + void (* SetSpecOrigin)( AstSpecFrame *, double, int * ); + + int (* GetAlignSpecOffset)( AstSpecFrame *, int * ); + int (* TestAlignSpecOffset)( AstSpecFrame *, int * ); + void (* ClearAlignSpecOffset)( AstSpecFrame *, int * ); + void (* SetAlignSpecOffset)( AstSpecFrame *, int, int * ); + + +} AstSpecFrameVtab; + + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstSpecFrameGlobals { + AstSpecFrameVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 51 ]; + char GetLabel_Buff[ 201 ]; + char GetSymbol_Buff[ 21 ]; + char GetTitle_Buff[ 201 ]; +} AstSpecFrameGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SpecFrame) /* Check class membership */ +astPROTO_ISA(SpecFrame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstSpecFrame *astSpecFrame_( const char *, int *, ...); +#else +AstSpecFrame *astSpecFrameId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSpecFrame *astInitSpecFrame_( void *, size_t, int, + AstSpecFrameVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitSpecFrameVtab_( AstSpecFrameVtab *, const char *, int * ); + +/* Loader. */ +AstSpecFrame *astLoadSpecFrame_( void *, size_t, + AstSpecFrameVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitSpecFrameGlobals_( AstSpecFrameGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astGetRefPos_( AstSpecFrame *, AstSkyFrame *, double *, double *, int * ); +void astSetRefPos_( AstSpecFrame *, AstSkyFrame *, double, double, int * ); + +#if defined(astCLASS) /* Protected */ + +AstStdOfRestType astGetStdOfRest_( AstSpecFrame *, int * ); +int astTestStdOfRest_( AstSpecFrame *, int * ); +void astClearStdOfRest_( AstSpecFrame *, int * ); +void astSetStdOfRest_( AstSpecFrame *, AstStdOfRestType, int * ); + +AstStdOfRestType astGetAlignStdOfRest_( AstSpecFrame *, int * ); +int astTestAlignStdOfRest_( AstSpecFrame *, int * ); +void astClearAlignStdOfRest_( AstSpecFrame *, int * ); +void astSetAlignStdOfRest_( AstSpecFrame *, AstStdOfRestType, int * ); + +AstStdOfRestType astGetSourceVRF_( AstSpecFrame *, int * ); +int astTestSourceVRF_( AstSpecFrame *, int * ); +void astClearSourceVRF_( AstSpecFrame *, int * ); +void astSetSourceVRF_( AstSpecFrame *, AstStdOfRestType, int * ); + +AstSystemType astGetSourceSys_( AstSpecFrame *, int * ); +int astTestSourceSys_( AstSpecFrame *, int * ); +void astClearSourceSys_( AstSpecFrame *, int * ); +void astSetSourceSys_( AstSpecFrame *, AstSystemType, int * ); + +double astGetRestFreq_( AstSpecFrame *, int * ); +int astTestRestFreq_( AstSpecFrame *, int * ); +void astClearRestFreq_( AstSpecFrame *, int * ); +void astSetRestFreq_( AstSpecFrame *, double, int * ); + +double astGetRefRA_( AstSpecFrame *, int * ); +int astTestRefRA_( AstSpecFrame *, int * ); +void astClearRefRA_( AstSpecFrame *, int * ); +void astSetRefRA_( AstSpecFrame *, double, int * ); + +double astGetRefDec_( AstSpecFrame *, int * ); +int astTestRefDec_( AstSpecFrame *, int * ); +void astClearRefDec_( AstSpecFrame *, int * ); +void astSetRefDec_( AstSpecFrame *, double, int * ); + +double astGetSourceVel_( AstSpecFrame *, int * ); +int astTestSourceVel_( AstSpecFrame *, int * ); +void astClearSourceVel_( AstSpecFrame *, int * ); +void astSetSourceVel_( AstSpecFrame *, double, int * ); + +double astGetSpecOrigin_( AstSpecFrame *, int * ); +int astTestSpecOrigin_( AstSpecFrame *, int * ); +void astClearSpecOrigin_( AstSpecFrame *, int * ); +void astSetSpecOrigin_( AstSpecFrame *, double, int * ); + +int astGetAlignSpecOffset_( AstSpecFrame *, int * ); +int astTestAlignSpecOffset_( AstSpecFrame *, int * ); +void astClearAlignSpecOffset_( AstSpecFrame *, int * ); +void astSetAlignSpecOffset_( AstSpecFrame *, int, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSpecFrame(this) astINVOKE_CHECK(SpecFrame,this,0) +#define astVerifySpecFrame(this) astINVOKE_CHECK(SpecFrame,this,1) + +/* Test class membership. */ +#define astIsASpecFrame(this) astINVOKE_ISA(SpecFrame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astSpecFrame astINVOKE(F,astSpecFrame_) +#else +#define astSpecFrame astINVOKE(F,astSpecFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSpecFrame(mem,size,init,vtab,name) \ +astINVOKE(O,astInitSpecFrame_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSpecFrameVtab(vtab,name) astINVOKE(V,astInitSpecFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSpecFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSpecFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) + +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ + +/* None. */ + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +/* Here we make use of astCheckSpecFrame to validate SpecFrame pointers + before use. This provides a contextual error report if a pointer to + the wrong sort of object is supplied. */ + +#define astGetRefPos(this,frm,lon,lat) astINVOKE(V,astGetRefPos_(astCheckSpecFrame(this),(frm==NULL?NULL:astCheckSkyFrame(frm)),lon,lat,STATUS_PTR)) +#define astSetRefPos(this,frm,lon,lat) astINVOKE(V,astSetRefPos_(astCheckSpecFrame(this),(frm==NULL?NULL:astCheckSkyFrame(frm)),lon,lat,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ + +#define astGetStdOfRest(this) astINVOKE(V,astGetStdOfRest_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestStdOfRest(this) astINVOKE(V,astTestStdOfRest_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearStdOfRest(this) astINVOKE(V,astClearStdOfRest_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetStdOfRest(this,value) astINVOKE(V,astSetStdOfRest_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetAlignStdOfRest(this) astINVOKE(V,astGetAlignStdOfRest_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestAlignStdOfRest(this) astINVOKE(V,astTestAlignStdOfRest_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearAlignStdOfRest(this) astINVOKE(V,astClearAlignStdOfRest_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetAlignStdOfRest(this,value) astINVOKE(V,astSetAlignStdOfRest_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetSourceVRF(this) astINVOKE(V,astGetSourceVRF_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestSourceVRF(this) astINVOKE(V,astTestSourceVRF_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearSourceVRF(this) astINVOKE(V,astClearSourceVRF_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetSourceVRF(this,value) astINVOKE(V,astSetSourceVRF_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetSourceSys(this) astINVOKE(V,astGetSourceSys_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestSourceSys(this) astINVOKE(V,astTestSourceSys_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearSourceSys(this) astINVOKE(V,astClearSourceSys_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetSourceSys(this,value) astINVOKE(V,astSetSourceSys_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetRestFreq(this) astINVOKE(V,astGetRestFreq_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestRestFreq(this) astINVOKE(V,astTestRestFreq_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearRestFreq(this) astINVOKE(V,astClearRestFreq_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetRestFreq(this,value) astINVOKE(V,astSetRestFreq_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetRefRA(this) astINVOKE(V,astGetRefRA_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestRefRA(this) astINVOKE(V,astTestRefRA_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearRefRA(this) astINVOKE(V,astClearRefRA_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetRefRA(this,value) astINVOKE(V,astSetRefRA_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetRefDec(this) astINVOKE(V,astGetRefDec_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestRefDec(this) astINVOKE(V,astTestRefDec_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearRefDec(this) astINVOKE(V,astClearRefDec_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetRefDec(this,value) astINVOKE(V,astSetRefDec_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetSourceVel(this) astINVOKE(V,astGetSourceVel_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestSourceVel(this) astINVOKE(V,astTestSourceVel_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearSourceVel(this) astINVOKE(V,astClearSourceVel_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetSourceVel(this,value) astINVOKE(V,astSetSourceVel_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astGetSpecOrigin(this) astINVOKE(V,astGetSpecOrigin_(astCheckSpecFrame(this),STATUS_PTR)) +#define astTestSpecOrigin(this) astINVOKE(V,astTestSpecOrigin_(astCheckSpecFrame(this),STATUS_PTR)) +#define astClearSpecOrigin(this) astINVOKE(V,astClearSpecOrigin_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetSpecOrigin(this,value) astINVOKE(V,astSetSpecOrigin_(astCheckSpecFrame(this),value,STATUS_PTR)) + +#define astClearAlignSpecOffset(this) astINVOKE(V,astClearAlignSpecOffset_(astCheckSpecFrame(this),STATUS_PTR)) +#define astGetAlignSpecOffset(this) astINVOKE(V,astGetAlignSpecOffset_(astCheckSpecFrame(this),STATUS_PTR)) +#define astSetAlignSpecOffset(this,value) astINVOKE(V,astSetAlignSpecOffset_(astCheckSpecFrame(this),value,STATUS_PTR)) +#define astTestAlignSpecOffset(this) astINVOKE(V,astTestAlignSpecOffset_(astCheckSpecFrame(this),STATUS_PTR)) + + + +#endif +#endif + + + + + diff --git a/specmap.c b/specmap.c new file mode 100644 index 0000000..164179a --- /dev/null +++ b/specmap.c @@ -0,0 +1,4671 @@ +/* +*class++ +* Name: +* SpecMap + +* Purpose: +* Sequence of spectral coordinate conversions. + +* Constructor Function: +c astSpecMap (also see astSpecAdd) +f AST_SPECMAP (also see AST_SPECADD) + +* Description: +* A SpecMap is a specialised form of Mapping which can be used to +* represent a sequence of conversions between standard spectral +* coordinate systems. +* +* When an SpecMap is first created, it simply performs a unit +c (null) Mapping. Using the astSpecAdd +f (null) Mapping. Using the AST_SPECADD +c function, a series of coordinate conversion steps may then be +f routine, a series of coordinate conversion steps may then be +* added. This allows multi-step conversions between a variety of +* spectral coordinate systems to be assembled out of a set of building +* blocks. +* +* Conversions are available to transform between standards of rest. +* Such conversions need to know the source position as an RA and DEC. +* This information can be supplied in the form of parameters for +* the relevant conversions, in which case the SpecMap is 1-dimensional, +* simply transforming the spectral axis values. This means that the +* same source position will always be used by the SpecMap. However, this +* may not be appropriate for an accurate description of a 3-D spectral +* cube, where changes of spatial position can produce significant +* changes in the Doppler shift introduced when transforming between +* standards of rest. For this situation, a 3-dimensional SpecMap can +* be created in which axes 2 and 3 correspond to the source RA and DEC +* The SpecMap simply copies values for axes 2 and 3 from input to +* output), but modifies axis 1 values (the spectral axis) appropriately. +* +* For details of the individual coordinate conversions available, +c see the description of the astSpecAdd function. +f see the description of the AST_SPECADD routine. + +* Inheritance: +* The SpecMap class inherits from the Mapping class. + +* Attributes: +* The SpecMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c In addition to those functions applicable to all Mappings, the +c following function may also be applied to all SpecMaps: +f In addition to those routines applicable to all Mappings, the +f following routine may also be applied to all SpecMaps: +* +c - astSpecAdd: Add a spectral coordinate conversion to an SpecMap +f - AST_SPECADD: Add a spectral coordinate conversion to an SpecMap + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 6-NOV-2002 (DSB): +* Original version. +* 14-JUL-2003 (DSB): +* Added checks for NAN values produced by transformation functions. +* 17-SEP-2003 (DSB): +* - Improve FRTOAW accuracy by iterating. +* - Changed Refrac to use algorithm given in FITS-WCS paper 3 +* version dated 21/9/03. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 10-MAY-2006 (DSB): +* Override astEqual. +* 15-NOV-2006 (DSB): +* Guard against division by zero when converting freq to wave in +* SystemChange. +* 18-JUN-2009 (DSB): +* Add OBSALT argument to TPF2HL and HLF2TP conversions. +* Change GEOLON/LAT to OBSLON/LAT for consistency with other +* classes. +* 2-OCT-2012 (DSB): +* Check for Infs as well as NaNs. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SpecMap + +/* Codes to identify spectral coordinate conversions. */ +#define AST__SPEC_NULL 0 /* Null value */ +#define AST__FRTOVL 1 /* Frequency to relativistic velocity */ +#define AST__VLTOFR 2 /* Relativistic velocity to Frequency */ +#define AST__ENTOFR 3 /* Energy to frequency */ +#define AST__FRTOEN 4 /* Frequency to energy */ +#define AST__WNTOFR 5 /* Wave number to frequency */ +#define AST__FRTOWN 6 /* Frequency to wave number */ +#define AST__WVTOFR 7 /* Wavelength (vacuum) to frequency */ +#define AST__FRTOWV 8 /* Frequency to wavelength (vacuum) */ +#define AST__AWTOFR 9 /* Wavelength (air) to frequency */ +#define AST__FRTOAW 10 /* Frequency to wavelength (air) */ +#define AST__VRTOVL 11 /* Radio to relativistic velocity */ +#define AST__VLTOVR 12 /* Relativistic to radio velocity */ +#define AST__VOTOVL 13 /* Optical to relativistic velocity */ +#define AST__VLTOVO 14 /* Relativistic to optical velocity */ +#define AST__ZOTOVL 15 /* Redshift to relativistic velocity */ +#define AST__VLTOZO 16 /* Relativistic velocity to redshift */ +#define AST__BTTOVL 17 /* Beta factor to relativistic velocity */ +#define AST__VLTOBT 18 /* Relativistic velocity to beta factor */ +#define AST__USF2HL 19 /* User-defined to heliocentric frequency */ +#define AST__HLF2US 20 /* Heliocentric to user-defined frequency */ +#define AST__TPF2HL 21 /* Topocentric to heliocentric frequency */ +#define AST__HLF2TP 22 /* Heliocentric to topocentric frequency */ +#define AST__GEF2HL 23 /* Geocentric to heliocentric frequency */ +#define AST__HLF2GE 24 /* Heliocentric to geocentric frequency */ +#define AST__BYF2HL 25 /* Barycentric to heliocentric frequency */ +#define AST__HLF2BY 26 /* Heliocentric to barycentric frequency */ +#define AST__LKF2HL 27 /* LSRK to heliocentric frequency */ +#define AST__HLF2LK 28 /* Heliocentric to LSRK frequency */ +#define AST__LDF2HL 29 /* LSRD to heliocentric frequency */ +#define AST__HLF2LD 30 /* Heliocentric to LSRD frequency */ +#define AST__LGF2HL 31 /* Local group to heliocentric frequency */ +#define AST__HLF2LG 32 /* Heliocentric to local group frequency */ +#define AST__GLF2HL 33 /* Galactic to heliocentric frequency */ +#define AST__HLF2GL 34 /* Heliocentric to galactic frequency */ + +/* Maximum number of arguments required by a conversion. */ +#define MAX_ARGS 7 + +/* The alphabet (used for generating keywords for arguments). */ +#define ALPHABET "abcdefghijklmnopqrstuvwxyz" + +/* Angle conversion */ +#define PI 3.141592653589793238462643 +#define PIBY2 (PI/2.0) +#define D2R (PI/180.0) +#define R2D (180.0/PI) + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "pal.h" /* SLALIB interface */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "unitmap.h" /* Unit (null) Mappings */ +#include "specmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double (* parent_rate)( AstMapping *, double *, int, int, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SpecMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SpecMap,Class_Init) +#define class_vtab astGLOBAL(SpecMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSpecMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* Structure to hold parameters and intermediate values describing a + reference frame */ +typedef struct FrameDef { + double obsalt; /* Observers geodetic altitude (m) */ + double obslat; /* Observers geodetic latitude (rads) */ + double obslon; /* Observers geodetic longitude (rads, +ve east) */ + double epoch; /* Julian epoch of observation */ + double refdec; /* RA of reference point (FK5 J2000) */ + double refra; /* DEC of reference point (FK5 J2000) */ + double veluser; /* Heliocentric velocity of user-defined system (m/s) */ + double last; /* Local apparent sideral time */ + double amprms[21]; /* Mean to apparent parameters */ + double vuser[3]; /* Used-defined velocity as a FK5 J2000 vector */ + double dvh[3]; /* Earth-sun velocity */ + double dvb[3]; /* Barycentre-sun velocity */ +} FrameDef; + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSpecMap *astSpecMapId_( int, int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *CvtString( int, const char **, int *, int *, int *, int *, const char *[ MAX_ARGS ], int * ); +static double BaryVel( double, double, FrameDef *, int * ); +static double GalVel( double, double, FrameDef *, int * ); +static double GeoVel( double, double, FrameDef *, int * ); +static double LgVel( double, double, FrameDef *, int * ); +static double LsrdVel( double, double, FrameDef *, int * ); +static double LsrkVel( double, double, FrameDef *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static double Refrac( double, int * ); +static double Rverot( double, double, double, double, double, int * ); +static double TopoVel( double, double, FrameDef *, int * ); +static double UserVel( double, double, FrameDef *, int * ); +static int CvtCode( const char *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int FrameChange( int, int, double *, double *, double *, double *, int, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int SystemChange( int, int, double *, double *, int, int * ); +static void AddSpecCvt( AstSpecMap *, int, const double *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void SpecAdd( AstSpecMap *, const char *, const double[], int * ); + +static int GetObjSize( AstObject *, int * ); +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two SpecMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* SpecMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two SpecMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a SpecMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the SpecMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSpecMap *that; + AstSpecMap *this; + const char *argdesc[ MAX_ARGS ]; + const char *comment; + int argdec; + int argra; + int i, j; + int nargs; + int nin; + int nout; + int result; + int szargs; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two SpecMap structures. */ + this = (AstSpecMap *) this_object; + that = (AstSpecMap *) that_object; + +/* Check the second object is a SpecMap. We know the first is a + SpecMap since we have arrived at this implementation of the virtual + function. */ + if( astIsASpecMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two SpecMaps differ, it may still be possible + for them to be equivalent. First compare the SpecMaps if their Invert + flags are the same. In this case all the attributes of the two SpecMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + if( this->ncvt == that->ncvt ) { + result = 1; + for( i = 0; i < this->ncvt && result; i++ ) { + if( this->cvttype[ i ] != that->cvttype[ i ] ) { + result = 0; + } else { + CvtString( this->cvttype[ i ], &comment, &argra, + &argdec, &nargs, &szargs, argdesc, status ); + for( j = 0; j < nargs; j++ ) { + if( !astEQUAL( this->cvtargs[ i ][ j ], + that->cvtargs[ i ][ j ] ) ){ + result = 0; + break; + } + } + } + } + } + +/* If the Invert flags for the two SpecMaps differ, the attributes of the two + SpecMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a SpecMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SpecMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SpecMap, +* in bytes. + +* Parameters: +* this +* Pointer to the SpecMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSpecMap *this; /* Pointer to SpecMap structure */ + int result; /* Result value to return */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SpecMap structure. */ + this = (AstSpecMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + for ( cvt = 0; cvt < this->ncvt; cvt++ ) { + result += astTSizeOf( this->cvtargs[ cvt ] ); + } + + result += astTSizeOf( this->cvtargs ); + result += astTSizeOf( this->cvttype ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void AddSpecCvt( AstSpecMap *this, int cvttype, const double *args, int *status ) { +/* +* Name: +* AddSpecCvt + +* Purpose: +* Add a coordinate conversion step to an SpecMap. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* void AddSpecCvt( AstSpecMap *this, int cvttype, const double *args ) + +* Class Membership: +* SpecMap member function. + +* Description: +* This function allows one of the supported spectral coordinate +* conversions to be appended to a SpecMap. When a SpecMap is first +* created (using astSpecMap), it simply performs a unit mapping. By +* using AddSpecCvt repeatedly, a series of coordinate conversions may +* then be specified which the SpecMap will subsequently perform in +* sequence. This allows a complex coordinate conversion to be +* assembled out of the basic building blocks. The SpecMap will also +* perform the inverse coordinate conversion (applying the individual +* conversion steps in reverse) if required. + +* Parameters: +* this +* Pointer to the SpecMap. +* cvttype +* A code to identify which spectral coordinate conversion is to be +* appended. See the "Coordinate Conversions" section for details +* of those available. +* args +* Pointer to an array of double containing the argument values +* required to fully specify the required coordinate +* conversion. The number of arguments depends on the conversion +* (see the "Coordinate Conversions" section for details). This +* value is ignored and may be NULL if no arguments are required. + +* Returned Value: +* void. + +* Coordinate Conversions: +* The following values may be supplied for the "cvttype" parameter +* in order to specify the coordinate conversion to be performed. +* The argument(s) required to fully specify each conversion are +* indicated in parentheses after each value, and described at the end +* of the list. Values for these should be given in the array pointed +* at by "args". +* +* AST__FRTOVL( RF ) +* Convert frequency to relativistic velocity. +* AST__VLTOFR( RF ) +* Convert relativistic velocity to Frequency. +* AST__ENTOFR +* Convert energy to frequency. +* AST__FRTOEN +* Convert frequency to energy. +* AST__WNTOFR +* Convert wave number to frequency. +* AST__FRTOWN +* Convert frequency to wave number. +* AST__WVTOFR +* Convert wavelength (vacuum) to frequency. +* AST__FRTOWV +* Convert frequency to wavelength (vacuum). +* AST__AWTOFR +* Convert wavelength (air) to frequency. +* AST__FRTOAW +* Convert frequency to wavelength (air). +* AST__VRTOVL +* Convert radio to relativistic velocity. +* AST__VLTOVR +* Convert relativistic to radio velocity. +* AST__VOTOVL +* Convert optical to relativistic velocity. +* AST__VLTOVO +* Convert relativistic to optical velocity. +* AST__ZOTOVL +* Convert redshift to relativistic velocity. +* AST__VLTOZO +* Convert relativistic velocity to redshift. +* AST__BTTOVL +* Convert beta factor to relativistic velocity. +* AST__VLTOBT +* Convert relativistic velocity to beta factor. +* AST_USF2HL( VOFF, RA, DEC ) +* Convert frequency from a user-defined reference frame to +* heliocentric. +* AST__HLF2US( VOFF, RA, DEC ) +* Convert frequency from heliocentric reference frame to +* user-defined. +* AST__TPF2HL( OBSLON, OBSLAT, OBSALT, EPOCH, RA, DEC ) +* Convert from Topocentric to heliocentric frequency +* AST__HLF2TP( OBSLON, OBSLAT, OBSALT, EPOCH, RA, DEC ) +* Convert from Heliocentric to topocentric frequency. +* AST__GEF2HL( EPOCH, RA, DEC ) +* Convert from Geocentric to heliocentric frequency. +* AST__HLF2GE( EPOCH, RA, DEC ) +* Convert from Heliocentric to geocentric frequency. +* AST__BYF2HL( EPOCH, RA, DEC ) +* Convert from Barycentric to heliocentric frequency. +* AST__HLF2BY( EPOCH, RA, DEC ) +* Convert from Heliocentric to barycentric frequency. +* AST__LKF2HL( RA, DEC ) +* Convert from LSRK to heliocentric frequency. +* AST__HLF2LK( RA, DEC ) +* Convert from Heliocentric to LSRK frequency. +* AST__LDF2HL( RA, DEC ) +* Convert from LSRD to heliocentric frequency. +* AST__HLF2LD( RA, DEC ) +* Convert from Heliocentric to LSRD frequency. +* AST__LGF2HL( RA, DEC ) +* Convert from Local group to heliocentric frequency. +* AST__HLF2LG( RA, DEC ) +* Convert from Heliocentric to local group frequency. +* AST__GLF2HL( RA, DEC ) +* Convert from Galactic to heliocentric frequency. +* AST__HLF2GL( RA, DEC ) +* Convert from Heliocentric to galactic frequency. +* +* The units for the values processed by the above conversions are as +* follows: +* +* - all velocities: metres per second. +* - frequency: Hertz. +* - all wavelengths: metres. +* - energy: Joules. +* - wave number: cycles per metre. +* +* The arguments used in the above conversions are as follows: +* +* - RF: Rest frequency (Hz). +* - OBSALT: Geodetic altitude of observer (IAU 1975, metres). +* - OBSLAT: Geodetic latitude of observer (IAU 1975, radians). +* - OBSLON: Longitude of observer (radians, positive eastwards). +* - EPOCH: Epoch of observation (UT1 expressed as a Modified Julian Date). +* - RA: Right Ascension of source (radians, FK5 J2000). +* - DEC: Declination of source (radians, FK5 J2000). +* - VOFF: Velocity of the user-defined reference frame, towards the +* position given by RA and DEC, measured in the heliocentric +* reference frame. +* +* If the SpecMap is 3-dimensional, source positions are provided by the +* values supplied to inputs 2 and 3 of the SpecMap (which are simply +* copied to outputs 2 and 3). Note, usable values are still required +* for the RA and DEC arguments in order to define the "user-defined" +* reference frame used by USF2HL and HLF2US. However, AST__BAD can be +* supplied for RA and DEC if the user-defined reference frame is not +* required. + +* Notes: +* - The specified conversion is appended only if the SpecMap's +* Invert attribute is zero. If it is non-zero, this function +* effectively prefixes the inverse of the conversion specified +* instead. +*/ + +/* Local Variables: */ + const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + const char *cvt_string; /* Pointer to conversion type string */ + int argdec; /* Index of DEC argument */ + int argra; /* Index of RA argument */ + int i; /* Argument index */ + int nargs; /* Number of user-supplied arguments */ + int ncvt; /* Number of coordinate conversions */ + int szargs; /* Size of arguments array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the coordinate conversion type and obtain the number of + required user-supplied arguments, and the size of the array in which + to put the user-supplied arguments (the array meay leave room after + the user-supplied arguments for various useful pre-calculated values). */ + cvt_string = CvtString( cvttype, &comment, &argra, &argdec, &nargs, + &szargs, argdesc, status ); + +/* If the coordinate conversion type was not valid, then report an + error. */ + if ( astOK && !cvt_string ) { + astError( AST__SPCIN, "AddSpecCvt(%s): Invalid spectral coordinate " + "conversion type (%d).", status, astGetClass( this ), + (int) cvttype ); + } + +/* Note the number of coordinate conversions already stored in the SpecMap. */ + if ( astOK ) { + ncvt = this->ncvt; + +/* Extend the array of conversion types and the array of pointers to + their argument lists to accommodate the new one. */ + this->cvttype = (int *) astGrow( this->cvttype, ncvt + 1, + sizeof( int ) ); + this->cvtargs = (double **) astGrow( this->cvtargs, ncvt + 1, + sizeof( double * ) ); + +/* If OK, allocate memory and store a copy of the argument list, + putting a pointer to the copy into the SpecMap. */ + if ( astOK ) { + this->cvtargs[ ncvt ] = astStore( NULL, args, + sizeof( double ) * (size_t) szargs ); + } + +/* Store the conversion type and increment the conversion count. Also put + AST__BAD in any elements of the argument array which are beyond the + end of the user-supplied arguments. These will be used to hold + intermediate values calculated on the basis of the user-supplied + arguments. */ + if ( astOK ) { + this->cvttype[ ncvt ] = cvttype; + this->ncvt++; + for( i = nargs; i < szargs; i++ ) this->cvtargs[ ncvt ][ i ] = AST__BAD; + } + } +} + +static double BaryVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* BaryVel + +* Purpose: +* Find the velocity of the earth-sun barycentre away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double BaryVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the velocity of the earth-sun +* barycentre away from a specified source position, at a given epoch, in +* the frame of rest of the centre of the Sun. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +*/ + +/* Local Variables: */ + double dpb[ 3 ]; /* Barycentric earth position vector */ + double dph[ 3 ]; /* Heliocentric earth position vector */ + double dvh[ 3 ]; /* Heliocentric earth velocity vector */ + double v[ 3 ]; /* Source direction vector */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Get the Cartesian vector towards the source, in the Cartesian FK5 + J2000 system. */ + palDcs2c( ra, dec, v ); + +/* If not already done so, get the Earth/Sun velocity and position vectors in + the same system. Speed is returned in units of AU/s. Store in the supplied + frame definition structure. */ + if( def->dvb[ 0 ] == AST__BAD ) { + palEvp( def->epoch, 2000.0, def->dvb, dpb, dvh, dph ); + +/* Change the barycentric velocity of the earth into the heliocentric + velocity of the barycentre. */ + def->dvb[ 0 ] = dvh[ 0 ] - def->dvb[ 0 ]; + def->dvb[ 1 ] = dvh[ 1 ] - def->dvb[ 1 ]; + def->dvb[ 2 ] = dvh[ 2 ] - def->dvb[ 2 ]; + } + +/* Return the component away from the source, of the velocity of the + barycentre relative to the sun (in m/s). */ + return -palDvdv( v, def->dvb )*149.597870E9; + +} + +static int CvtCode( const char *cvt_string, int *status ) { +/* +* Name: +* CvtCode + +* Purpose: +* Convert a conversion type from a string representation to a code value. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* int CvtCode( const char *cvt_string, int *status ) + +* Class Membership: +* SpecMap member function. + +* Description: +* This function accepts a string used to repersent one of the +* SpecMap coordinate conversions and converts it into a code +* value for internal use. + +* Parameters: +* cvt_string +* Pointer to a constant null-terminated string representing a +* spectral coordinate conversion. This is case sensitive and should +* contain no unnecessary white space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The equivalent conversion code. If the string was not +* recognised, the code AST__SPEC_NULL is returned, without error. + +* Notes: +* - A value of AST__SPEC_NULL will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Initialise. */ + result = AST__SPEC_NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Test the string against each recognised value in turn and assign + the result. */ + if ( astChrMatch( cvt_string, "FRTOVL" ) ) { + result = AST__FRTOVL; + + } else if ( astChrMatch( cvt_string, "VLTOFR" ) ) { + result = AST__VLTOFR; + + } else if ( astChrMatch( cvt_string, "VLTOFR" ) ) { + result = AST__VLTOFR; + + } else if ( astChrMatch( cvt_string, "ENTOFR" ) ) { + result = AST__ENTOFR; + + } else if ( astChrMatch( cvt_string, "FRTOEN" ) ) { + result = AST__FRTOEN; + + } else if ( astChrMatch( cvt_string, "WNTOFR" ) ) { + result = AST__WNTOFR; + + } else if ( astChrMatch( cvt_string, "FRTOWN" ) ) { + result = AST__FRTOWN; + + } else if ( astChrMatch( cvt_string, "WVTOFR" ) ) { + result = AST__WVTOFR; + + } else if ( astChrMatch( cvt_string, "FRTOWV" ) ) { + result = AST__FRTOWV; + + } else if ( astChrMatch( cvt_string, "AWTOFR" ) ) { + result = AST__AWTOFR; + + } else if ( astChrMatch( cvt_string, "FRTOAW" ) ) { + result = AST__FRTOAW; + + } else if ( astChrMatch( cvt_string, "VRTOVL" ) ) { + result = AST__VRTOVL; + + } else if ( astChrMatch( cvt_string, "VLTOVR" ) ) { + result = AST__VLTOVR; + + } else if ( astChrMatch( cvt_string, "VOTOVL" ) ) { + result = AST__VOTOVL; + + } else if ( astChrMatch( cvt_string, "VLTOVO" ) ) { + result = AST__VLTOVO; + + } else if ( astChrMatch( cvt_string, "ZOTOVL" ) ) { + result = AST__ZOTOVL; + + } else if ( astChrMatch( cvt_string, "VLTOZO" ) ) { + result = AST__VLTOZO; + + } else if ( astChrMatch( cvt_string, "BTTOVL" ) ) { + result = AST__BTTOVL; + + } else if ( astChrMatch( cvt_string, "VLTOBT" ) ) { + result = AST__VLTOBT; + + } else if ( astChrMatch( cvt_string, "USF2HL" ) ) { + result = AST__USF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2US" ) ) { + result = AST__HLF2US; + + } else if ( astChrMatch( cvt_string, "TPF2HL" ) ) { + result = AST__TPF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2TP" ) ) { + result = AST__HLF2TP; + + } else if ( astChrMatch( cvt_string, "GEF2HL" ) ) { + result = AST__GEF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2GE" ) ) { + result = AST__HLF2GE; + + } else if ( astChrMatch( cvt_string, "BYF2HL" ) ) { + result = AST__BYF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2BY" ) ) { + result = AST__HLF2BY; + + } else if ( astChrMatch( cvt_string, "LKF2HL" ) ) { + result = AST__LKF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2LK" ) ) { + result = AST__HLF2LK; + + } else if ( astChrMatch( cvt_string, "LDF2HL" ) ) { + result = AST__LDF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2LD" ) ) { + result = AST__HLF2LD; + + } else if ( astChrMatch( cvt_string, "LGF2HL" ) ) { + result = AST__LGF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2LG" ) ) { + result = AST__HLF2LG; + + } else if ( astChrMatch( cvt_string, "GLF2HL" ) ) { + result = AST__GLF2HL; + + } else if ( astChrMatch( cvt_string, "HLF2GL" ) ) { + result = AST__HLF2GL; + + } + +/* Return the result. */ + return result; +} + +static const char *CvtString( int cvt_code, const char **comment, + int *argra, int *argdec, int *nargs, int *szargs, + const char *arg[ MAX_ARGS ], int *status ) { +/* +* Name: +* CvtString + +* Purpose: +* Convert a conversion type from a code value to a string representation. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* const char *CvtString( int cvt_code, const char **comment, +* int *argra, int *argdec, int *nargs, +* int *szargs, const char *arg[ MAX_ARGS ], int *status ) + +* Class Membership: +* SpecMap member function. + +* Description: +* This function accepts a code value used to represent one of the +* SpecMap coordinate conversions and converts it into an +* equivalent string representation. It also returns a descriptive +* comment and information about the arguments required in order to +* perform the conversion. + +* Parameters: +* cvt_code +* The conversion code. +* comment +* Address of a location to return a pointer to a constant +* null-terminated string containing a description of the +* conversion. +* argra +* Address of an int in which to return the index of the argument +* corresponding to the source RA. Returned equal to -1 if the +* conversion does not have a source RA argument. +* argdec +* Address of an int in which to return the index of the argument +* corresponding to the source DEC. Returned equal to -1 if the +* conversion does not have a source DEC argument. +* nargs +* Address of an int in which to return the number of arguments +* required from the user in order to perform the conversion (may +* be zero). +* szargs +* Address of an int in which to return the number of arguments +* associated with the conversion. This may be bigger than "nargs" +* if the conversion can pre-calculate useful values on the basis +* of the user-supplied values. Such precalculated values are +* stored after the last user-supplied argument. +* arg +* An array in which to return a pointer to a constant +* null-terminated string for each argument (above) containing a +* description of what each argument represents. This includes both +* user-supplied arguments and pre-calculated values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string representation of +* the conversion code value supplied. If the code supplied is not +* valid, a NULL pointer will be returned, without error. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Result pointer to return */ + +/* Initialise the returned values. */ + *comment = NULL; + *nargs = 0; + *argra = -1; + *argdec = -1; + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Test for each valid code value in turn and assign the appropriate + return values. */ + switch ( cvt_code ) { + + case AST__FRTOVL: + *comment = "Convert frequency to rel. velocity"; + result = "FRTOVL"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "Rest frequency (Hz)"; + break; + + case AST__VLTOFR: + *comment = "Convert rel. velocity to frequency"; + result = "VLTOFR"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "Rest frequency (Hz)"; + break; + + case AST__ENTOFR: + *comment = "Convert energy to frequency"; + result = "ENTOFR"; + *nargs = 0; + *szargs = 0; + break; + + case AST__FRTOEN: + *comment = "Convert frequency to energy"; + result = "FRTOEN"; + *nargs = 0; + *szargs = 0; + break; + + case AST__WNTOFR: + *comment = "Convert wave number to frequency"; + result = "WNTOFR"; + *nargs = 0; + *szargs = 0; + break; + + case AST__FRTOWN: + *comment = "Convert frequency to wave number"; + result = "FRTOWN"; + *nargs = 0; + *szargs = 0; + break; + + case AST__WVTOFR: + *comment = "Convert wavelength (vacuum) to frequency"; + result = "WVTOFR"; + *nargs = 0; + *szargs = 0; + break; + + case AST__FRTOWV: + *comment = "Convert frequency to wavelength (vacuum)"; + result = "FRTOWV"; + *nargs = 0; + *szargs = 0; + break; + + case AST__AWTOFR: + *comment = "Convert wavelength (air) to frequency"; + result = "AWTOFR"; + *nargs = 0; + *szargs = 0; + break; + + case AST__FRTOAW: + *comment = "Convert frequency to wavelength (air)"; + result = "FRTOAW"; + *nargs = 0; + *szargs = 0; + break; + + case AST__VRTOVL: + *comment = "Convert radio to rel. velocity"; + result = "VRTOVL"; + *nargs = 0; + *szargs = 0; + break; + + case AST__VLTOVR: + *comment = "Convert relativistic to radio velocity"; + result = "VLTOVR"; + *nargs = 0; + *szargs = 0; + break; + + case AST__VOTOVL: + *comment = "Convert optical to rel. velocity"; + result = "VOTOVL"; + *nargs = 0; + *szargs = 0; + break; + + case AST__VLTOVO: + *comment = "Convert relativistic to optical velocity"; + result = "VLTOVO"; + *nargs = 0; + *szargs = 0; + break; + + case AST__ZOTOVL: + *comment = "Convert redshift to rel. velocity"; + result = "ZOTOVL"; + *nargs = 0; + *szargs = 0; + break; + + case AST__VLTOZO: + *comment = "Convert rel. velocity to redshift"; + result = "VLTOZO"; + *nargs = 0; + *szargs = 0; + break; + + case AST__BTTOVL: + *comment = "Convert beta factor to rel. velocity"; + result = "BTTOVL"; + *nargs = 0; + *szargs = 0; + break; + + case AST__VLTOBT: + *comment = "Convert rel. velocity to beta factor"; + result = "VLTOBT"; + *nargs = 0; + *szargs = 0; + break; + + case AST__USF2HL: + *comment = "Convert from user-defined to heliocentric frequency"; + result = "USF2HL"; + *argra = 1; + *argdec = 2; + *nargs = 3; + *szargs = 4; + arg[ 0 ] = "Velocity offset (m/s)"; + arg[ 1 ] = "RA of source (FK5 J2000, radians)"; + arg[ 2 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 3 ] = "Frequency correction factor"; + break; + + case AST__HLF2US: + *comment = "Convert from heliocentric to user-defined frequency"; + result = "HLF2US"; + *argra = 1; + *argdec = 2; + *nargs = 3; + *szargs = 4; + arg[ 0 ] = "Velocity offset (m/s)"; + arg[ 1 ] = "RA of source (FK5 J2000, radians)"; + arg[ 2 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 3 ] = "Frequency correction factor"; + break; + + case AST__TPF2HL: + *comment = "Convert from Topocentric to heliocentric frequency"; + result = "TPF2HL"; + *argra = 4; + *argdec = 5; + *nargs = 6; + *szargs = 7; + arg[ 0 ] = "Longitude (positive eastwards, radians)"; + arg[ 1 ] = "Latitude (geodetic, radians)"; + arg[ 2 ] = "Altitude (geodetic, metres)"; + arg[ 3 ] = "UT1 epoch of observaton (Modified Julian Date)"; + arg[ 4 ] = "RA of source (FK5 J2000, radians)"; + arg[ 5 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 6 ] = "Frequency correction factor"; + break; + + case AST__HLF2TP: + *comment = "Convert from Heliocentric to topocentric frequency"; + result = "HLF2TP"; + *argra = 4; + *argdec = 5; + *nargs = 6; + *szargs = 7; + arg[ 0 ] = "Longitude (positive eastwards, radians)"; + arg[ 1 ] = "Latitude (geodetic, radians)"; + arg[ 2 ] = "Altitude (geodetic, metres)"; + arg[ 3 ] = "UT1 epoch of observaton (Modified Julian Date)"; + arg[ 4 ] = "RA of source (FK5 J2000, radians)"; + arg[ 5 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 6 ] = "Frequency correction factor"; + break; + + case AST__GEF2HL: + *comment = "Convert from Geocentric to heliocentric frequency"; + result = "GEF2HL"; + *argra = 1; + *argdec = 2; + *nargs = 3; + *szargs = 4; + arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)"; + arg[ 1 ] = "RA of source (FK5 J2000, radians)"; + arg[ 2 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 3 ] = "Frequency correction factor"; + break; + + case AST__HLF2GE: + *comment = "Convert from Heliocentric to geocentric frequency"; + result = "HLF2GE"; + *argra = 1; + *argdec = 2; + *nargs = 3; + *szargs = 4; + arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)"; + arg[ 1 ] = "RA of source (FK5 J2000, radians)"; + arg[ 2 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 3 ] = "Frequency correction factor"; + break; + + case AST__BYF2HL: + *comment = "Convert from Barycentric to heliocentric frequency"; + result = "BYF2HL"; + *argra = 1; + *argdec = 2; + *nargs = 3; + *szargs = 4; + arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)"; + arg[ 1 ] = "RA of source (FK5 J2000, radians)"; + arg[ 2 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 3 ] = "Frequency correction factor"; + break; + + case AST__HLF2BY: + *comment = "Convert from Heliocentric to barycentric frequency"; + result = "HLF2BY"; + *argra = 1; + *argdec = 2; + *nargs = 3; + *szargs = 4; + arg[ 0 ] = "UT1 epoch of observaton (Modified Julian Date)"; + arg[ 1 ] = "RA of source (FK5 J2000, radians)"; + arg[ 2 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 3 ] = "Frequency correction factor"; + break; + + case AST__LKF2HL: + *comment = "Convert from LSRK to heliocentric frequency"; + result = "LKF2HL"; + *argra = 0; + *argdec = 1; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + case AST__HLF2LK: + *comment = "Convert from Heliocentric to LSRK frequency"; + result = "HLF2LK"; + *argra = 0; + *argdec = 1; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + case AST__LDF2HL: + *comment = "Convert from LSRD to heliocentric frequency"; + result = "LDF2HL"; + *argra = 0; + *argdec = 1; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + case AST__HLF2LD: + *comment = "Convert from Heliocentric to LSRD frequency"; + result = "HLF2LD"; + *argra = 0; + *argdec = 1; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + case AST__LGF2HL: + *comment = "Convert from Local group to heliocentric frequency"; + result = "LGF2HL"; + *argra = 0; + *argdec = 1; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + case AST__HLF2LG: + *comment = "Convert from Heliocentric to local group frequency"; + result = "HLF2LG"; + *argra = 0; + *argdec = 1; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + case AST__GLF2HL: + *comment = "Convert from Galactic to heliocentric frequency"; + result = "GLF2HL"; + *argra = 0; + *argdec = 1; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + case AST__HLF2GL: + *comment = "Convert from Heliocentric to galactic frequency"; + *argra = 0; + *argdec = 1; + result = "HLF2GL"; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "RA of source (FK5 J2000, radians)"; + arg[ 1 ] = "DEC of source (FK5 J2000, radians)"; + arg[ 2 ] = "Frequency correction factor"; + break; + + } + +/* Return the result. */ + return result; +} + +static int FrameChange( int cvt_code, int np, double *ra, double *dec, double *freq, + double *args, int forward, int *status ){ +/* +* Name: +* FrameChange + +* Purpose: +* Apply a doppler shift caused by a change of reference frame. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* int FrameChange( int cvt_code, int np, double *ra, double *dec, +* double *freq, double *args, int forward, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function modifies the supplied frequency values in order to +* apply a doppler shift caused by a change of the observers rest-frame. + +* Parameters: +* cvt_code +* A code indicating the conversion to be applied. If the code does +* not correspond to a change of rest-frame, then the supplied +* frequencies are left unchanged and zero is returned as the +* function value. +* np +* The number of frequency values to transform. +* ra +* Pointer to an array of "np" RA (J2000 FK5) values at which the +* "np" frequencies are observed. These are unchanged on exit. If a +* NULL pointer is supplied, then all frequencies are assumed to be +* observed at the single RA value given by "refra" +* dec +* Pointer to an array of "np" Dec (J2000 FK5) values at which the +* "np" frequencies are observed. These are unchanged on exit. If a +* NULL pointer is supplied, then all frequencies are assumed to be +* observed at the single Dec value given by "refdec" +* freq +* Pointer to an array of "np" frequency values, measured in the +* input rest-frame. These are modified on return to hold the +* corresponding values measured in the output rest-frame. +* args +* Pointer to an array holding the conversion arguments. The number +* of arguments expected depends on the particular conversion being +* used. +* forward +* Should the conversion be applied in the forward or inverse +* direction? Non-zero for forward, zero for inverse. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the supplied conversion code corresponds to a change of +* reference frame. Zoer otherwise (in which case the upplied values +* will not have been changed). + +* Notes: +* - The "args" array contains RA and DEC values which give the "source" +* position (FK5 J2000). If a NULL value is supplied for the "ra" +* parameter, then these args define the position of all the frequency +* values. In addition they also define the direction of motion of +* the "user-defined" rest-frame (see "veluser"). Thus they should still +* be supplied even if "ra" is NULL. + +*/ + +/* Local Variables: */ + FrameDef def; /* Structure holding frame parameters */ + double (* cvtFunc)( double, double, FrameDef *, int * ); /* Pointer to conversion function */ + double *fcorr; /* Pointer to frequency correction factor */ + double *pdec; /* Pointer to next Dec value */ + double *pf; /* Pointer to next frequency value */ + double *pra; /* Pointer to next RA value */ + double factor; /* Frequency correction factor */ + double s; /* Velocity correction (m/s) */ + int i; /* Loop index */ + int result; /* Returned value */ + int sign; /* Sign for velocity correction */ + +/* Check inherited status. */ + if( !astOK ) return 0; + +/* Initialise */ + cvtFunc = NULL; + fcorr = NULL; + sign = 0; + +/* Set the return value to indicate that the supplied conversion code + represents a change of rest-frame. */ + result = 1; + +/* Initialise a structure which stores parameters which define the + transformation. */ + def.obsalt = AST__BAD; + def.obslat = AST__BAD; + def.obslon = AST__BAD; + def.epoch = AST__BAD; + def.refdec = AST__BAD; + def.refra = AST__BAD; + def.veluser = AST__BAD; + def.last = AST__BAD; + def.amprms[ 0 ] = AST__BAD; + def.vuser[ 0 ] = AST__BAD; + def.dvh[ 0 ] = AST__BAD; + def.dvb[ 0 ] = AST__BAD; + +/* Test for each rest-frame code value in turn and assign the appropriate + values. */ + switch ( cvt_code ) { + + case AST__USF2HL: + cvtFunc = UserVel; + def.veluser = args[ 0 ]; + def.refra = args[ 1 ]; + def.refdec = args[ 2 ]; + fcorr = args + 3; + sign = -1; + break; + + case AST__HLF2US: + cvtFunc = UserVel; + def.veluser = args[ 0 ]; + def.refra = args[ 1 ]; + def.refdec = args[ 2 ]; + fcorr = args + 3; + sign = +1; + break; + + case AST__TPF2HL: + cvtFunc = TopoVel; + def.obslon = args[ 0 ]; + def.obslat = args[ 1 ]; + def.obsalt = args[ 2 ]; + def.epoch = args[ 3 ]; + def.refra = args[ 4 ]; + def.refdec = args[ 5 ]; + fcorr = args + 6; + sign = -1; + break; + + case AST__HLF2TP: + cvtFunc = TopoVel; + def.obslon = args[ 0 ]; + def.obslat = args[ 1 ]; + def.obsalt = args[ 2 ]; + def.epoch = args[ 3 ]; + def.refra = args[ 4 ]; + def.refdec = args[ 5 ]; + fcorr = args + 6; + sign = +1; + break; + + case AST__GEF2HL: + cvtFunc = GeoVel; + def.epoch = args[ 0 ]; + def.refra = args[ 1 ]; + def.refdec = args[ 2 ]; + fcorr = args + 3; + sign = -1; + break; + + case AST__HLF2GE: + cvtFunc = GeoVel; + def.epoch = args[ 0 ]; + def.refra = args[ 1 ]; + def.refdec = args[ 2 ]; + fcorr = args + 3; + sign = +1; + break; + + case AST__BYF2HL: + cvtFunc = BaryVel; + def.epoch = args[ 0 ]; + def.refra = args[ 1 ]; + def.refdec = args[ 2 ]; + fcorr = args + 3; + sign = -1; + break; + + case AST__HLF2BY: + cvtFunc = BaryVel; + def.epoch = args[ 0 ]; + def.refra = args[ 1 ]; + def.refdec = args[ 2 ]; + fcorr = args + 3; + sign = +1; + break; + + case AST__LKF2HL: + cvtFunc = LsrkVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = -1; + break; + + case AST__HLF2LK: + cvtFunc = LsrkVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = +1; + break; + + case AST__LDF2HL: + cvtFunc = LsrdVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = -1; + break; + + case AST__HLF2LD: + cvtFunc = LsrdVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = +1; + break; + + case AST__LGF2HL: + cvtFunc = LgVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = -1; + break; + + case AST__HLF2LG: + cvtFunc = LgVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = +1; + break; + + case AST__GLF2HL: + cvtFunc = GalVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = -1; + break; + + case AST__HLF2GL: + cvtFunc = GalVel; + def.refra = args[ 0 ]; + def.refdec = args[ 1 ]; + fcorr = args + 2; + sign = +1; + break; + +/* If the supplied code does not represent a change of rest-frame, clear + the returned flag. */ + default: + result = 0; + } + +/* Check we have a rest-frame code. */ + if( result ) { + +/* First deal with cases where we have a single source position (given by + refra and refdec). */ + if( !ra ) { + +/* If the frequency correction factor has not been found, find it now. */ + if( *fcorr == AST__BAD ) { + +/* Get the velocity correction. This is the component of the velocity of the + output system, away from the source, as measured in the input system. */ + s = sign*cvtFunc( def.refra, def.refdec, &def, status ); + +/* Find the factor by which to correct supplied frequencies. If the + velocity correction is positive, the output frequency wil be lower than + the input frequency. */ + if( s < AST__C && s > -AST__C ) { + *fcorr = sqrt( ( AST__C - s )/( AST__C + s ) ); + } + } + +/* Correct each supplied frequency. */ + if( *fcorr != AST__BAD && *fcorr != 0.0 ) { + factor = forward ? *fcorr : 1.0 / ( *fcorr ); + pf = freq; + for( i = 0; i < np; i++, pf++ ) { + if( *pf != AST__BAD ) *pf *= factor; + } + +/* Set returned values bad if the velocity correction is un-physical. */ + } else { + pf = freq; + for( i = 0; i < np; i++ ) *(pf++) = AST__BAD; + } + +/* Now deal with cases where each frequency value has its own source + position. */ + } else { + +/* Invert the sign if we are doing a inverse transformation. */ + if( !forward ) sign = -sign; + +/* Loop round each value. */ + pf = freq; + pra = ra; + pdec = dec; + for( i = 0; i < np; i++ ) { + +/* If the ra or dec is bad, store a bad frequency. */ + if( *pra == AST__BAD || *pdec == AST__BAD || *pf == AST__BAD ) { + *pf = AST__BAD; + +/* Otherwise, produce a corrected frequency. */ + } else { + +/* Get the velocity correction. */ + s = sign*cvtFunc( *pra, *pdec, &def, status ); + +/* Correct this frequency, if possible. Otherwise set bad. */ + if( s < AST__C && s > -AST__C ) { + *pf *= sqrt( ( AST__C - s )/( AST__C + s ) ); + } else { + *pf = AST__BAD; + } + } + +/* Move on to the next position. */ + pf++; + pra++; + pdec++; + } + } + } + +/* Return the result. */ + return result; +} + +static double GalVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* GalVel + +* Purpose: +* Find the velocity of the galactic centre away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double GalVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the velocity of the galactic +* centre away from a specified source position, in the frame of rest +* of the Sun. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +*/ + +/* Local Variables: */ + double s1, s2; + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Get the component away from the source, of the velocity of the sun + relative to the dynamic LSR (in km/s). */ + s1 = (double) palRvlsrd( (float) ra, (float) dec ); + +/* Get the component away from the source, of the velocity of the + dynamic LSR relative to the galactic centre (in km/s). */ + s2 = (double) palRvgalc( (float) ra, (float) dec ); + +/* Return the total velocity of the galactic centre away from the source, + relative to the sun, in m/s. */ + return -1000.0*( s1 + s2 ); +} + +static double GeoVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* GeoVel + +* Purpose: +* Find the velocity of the earth away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double GeoVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the velocity of the earth away +* from a specified source position, at a given epoch, in the frame of +* rest of the Sun. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +*/ + +/* Local Variables: */ + double dpb[ 3 ]; /* Barycentric earth position vector */ + double dph[ 3 ]; /* Heliocentric earth position vector */ + double dvb[ 3 ]; /* Barycentric earth velocity vector */ + double v[ 3 ]; /* Source direction vector */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Get the Cartesian vector towards the source, in the Cartesian FK5 + J2000 system. */ + palDcs2c( ra, dec, v ); + +/* If not already done so, get the Earth/Sun velocity and position vectors in + the same system. Speed is returned in units of AU/s. Store in the supplied + frame definition structure. */ + if( def->dvh[ 0 ] == AST__BAD ) palEvp( def->epoch, 2000.0, dvb, dpb, + def->dvh, dph ); + +/* Return the component away from the source, of the velocity of the earths + centre relative to the sun (in m/s). */ + return -palDvdv( v, def->dvh )*149.597870E9; +} + +void astInitSpecMapVtab_( AstSpecMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSpecMapVtab + +* Purpose: +* Initialise a virtual function table for a SpecMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "specmap.h" +* void astInitSpecMapVtab( AstSpecMapVtab *vtab, const char *name ) + +* Class Membership: +* SpecMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SpecMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASpecMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->SpecAdd = SpecAdd; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_rate = mapping->Rate; + mapping->Rate = Rate; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "SpecMap", + "Conversion between spectral coordinate systems" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static double LgVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* LgVel + +* Purpose: +* Find the velocity of the Local Group away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double LgVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the Local Group velocity away +* from a specified source position, in the frame of rest of the Sun. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +*/ + +/* Return the component away from the source, of the velocity of the + local group relative to the sun (in m/s). */ + return -1000.0*palRvlg( (float) ra, (float) dec ); +} + +static double LsrdVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* LsrdVel + +* Purpose: +* Find the velocity of the Dynamical LSR away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double LsrdVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the velocity of the Dynamical +* LSR away from a specified source position, in the frame of rest of +* the Sun. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +*/ +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Get the component away from the source, of the velocity of the sun + relative to the dynamical LSR (in m/s). This can also be thought of as the + velocity of the LSR towards the source relative to the sun. Return the + negated value (i.e. velocity of lsrd *away from* the source. */ + return -1000.0*palRvlsrd( (float) ra, (float) dec ); +} + +static double LsrkVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* LsrkVel + +* Purpose: +* Find the velocity of the Kinematic LSR away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double LsrkVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the velocity of the Kinematic +* LSR away from a specified source position, in the frame of rest of +* the Sun. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +*/ +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Get the component away from the source, of the velocity of the sun + relative to the kinematic LSR (in m/s). This can also be thought of as the + velocity of the LSR towards the source relative to the sun. Return the + negated value (i.e. velocity of lsrk *away from* the source. */ + return -1000.0*palRvlsrk( (float) ra, (float) dec ); +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a SpecMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* SpecMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated SpecMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated SpecMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated SpecMap which is to be merged with +* its neighbours. This should be a cloned copy of the SpecMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* SpecMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated SpecMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *new; /* Pointer to replacement Mapping */ + AstSpecMap *specmap; /* Pointer to SpecMap */ + const char *argdesc[ MAX_ARGS ]; /* Argument descriptions (junk) */ + const char *class; /* Pointer to Mapping class string */ + const char *comment; /* Pointer to comment string (junk) */ + double (*cvtargs)[ MAX_ARGS ]; /* Pointer to argument arrays */ + double tmp; /* Temporary storage */ + int *cvttype; /* Pointer to transformation type codes */ + int *szarg; /* Pointer to argument count array */ + int argdec; /* Index of DEC argument */ + int argra; /* Index of RA argument */ + int done; /* Finished (no further simplification)? */ + int iarg; /* Loop counter for arguments */ + int icvt1; /* Loop initial value */ + int icvt2; /* Loop final value */ + int icvt; /* Loop counter for transformation steps */ + int ikeep; /* Index to store step being kept */ + int imap1; /* Index of first SpecMap to merge */ + int imap2; /* Index of last SpecMap to merge */ + int imap; /* Loop counter for Mappings */ + int inc; /* Increment for transformation step loop */ + int invert; /* SpecMap applied in inverse direction? */ + int istep; /* Loop counter for transformation steps */ + int keep; /* Keep transformation step? */ + int narg; /* Number of user-supplied arguments */ + int ngone; /* Number of Mappings eliminated */ + int nin; /* Numbr of axes for SpecMaps being merged */ + int nstep0; /* Original number of transformation steps */ + int nstep; /* Total number of transformation steps */ + int result; /* Result value to return */ + int simpler; /* Simplification possible? */ + int unit; /* Replacement Mapping is a UnitMap? */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* SpecMaps can only be merged if they are in series (or if there is + only one Mapping present, in which case it makes no difference), so + do nothing if they are not. */ + if ( series || ( *nmap == 1 ) ) { + +/* Save the number of inputs for the SpecMap. */ + nin = astGetNin( this ); + +/* Initialise the number of transformation steps to be merged to equal + the number in the nominated SpecMap. */ + nstep = ( (AstSpecMap *) ( *map_list )[ where ] )->ncvt; + +/* Search adjacent lower-numbered Mappings until one is found which is + not a SpecMap, or is a SpecMap with a different number of axes. Accumulate + the number of transformation steps involved in any SpecMaps found. */ + imap1 = where; + while ( ( imap1 - 1 >= 0 ) && astOK ) { + class = astGetClass( ( *map_list )[ imap1 - 1 ] ); + if ( !astOK || strcmp( class, "SpecMap" ) || + astGetNin( ( *map_list )[ imap1 - 1 ] ) != nin ) break; + nstep += ( (AstSpecMap *) ( *map_list )[ imap1 - 1 ] )->ncvt; + imap1--; + } + +/* Similarly search adjacent higher-numbered Mappings. */ + imap2 = where; + while ( ( imap2 + 1 < *nmap ) && astOK ) { + class = astGetClass( ( *map_list )[ imap2 + 1 ] ); + if ( !astOK || strcmp( class, "SpecMap" ) || + astGetNin( ( *map_list )[ imap2 + 1 ] ) != nin ) break; + nstep += ( (AstSpecMap *) ( *map_list )[ imap2 + 1 ] )->ncvt; + imap2++; + } + +/* Remember the initial number of transformation steps. */ + nstep0 = nstep; + +/* Allocate memory for accumulating a list of all the transformation + steps involved in all the SpecMaps found. */ + cvttype = astMalloc( sizeof( int ) * (size_t) nstep ); + cvtargs = astMalloc( sizeof( double[ MAX_ARGS ] ) * (size_t) nstep ); + szarg = astMalloc( sizeof( int ) * (size_t) nstep ); + +/* Loop to obtain the transformation data for each SpecMap being merged. */ + nstep = 0; + for ( imap = imap1; astOK && ( imap <= imap2 ); imap++ ) { + +/* Obtain a pointer to the SpecMap and note if it is being applied in + its inverse direction. */ + specmap = (AstSpecMap *) ( *map_list )[ imap ]; + invert = ( *invert_list )[ imap ]; + +/* Set up loop limits and an increment to scan the transformation + steps in each SpecMap in either the forward or reverse direction, as + dictated by the associated "invert" value. */ + icvt1 = invert ? specmap->ncvt - 1 : 0; + icvt2 = invert ? -1 : specmap->ncvt; + inc = invert ? -1 : 1; + +/* Loop through each transformation step in the SpecMap. */ + for ( icvt = icvt1; icvt != icvt2; icvt += inc ) { + +/* Store the transformation type code and use "CvtString" to determine + the associated number of arguments. Then store these arguments. */ + cvttype[ nstep ] = specmap->cvttype[ icvt ]; + (void) CvtString( cvttype[ nstep ], &comment, &argra, &argdec, + &narg, szarg + nstep, argdesc, status ); + if ( !astOK ) break; + for ( iarg = 0; iarg < szarg[ nstep ]; iarg++ ) { + cvtargs[ nstep ][ iarg ] = specmap->cvtargs[ icvt ][ iarg ]; + } + +/* If the SpecMap is inverted, we must not only accumulate its + transformation steps in reverse, but also apply them in + reverse. For some steps this means changing arguments, for some it + means changing the transformation type code to a complementary + value, and for others it means both. Define macros to perform each + of the required changes. */ + +/* Macro to exchange a transformation type code for its inverse (and + vice versa). */ +#define SWAP_CODES( code1, code2 ) \ + if ( cvttype[ nstep ] == code1 ) { \ + cvttype[ nstep ] = code2; \ + } else if ( cvttype[ nstep ] == code2 ) { \ + cvttype[ nstep ] = code1; \ + } + +/* Macro to exchange a transformation type code for its inverse (and + vice versa), and reciprocate a specified argument. */ +#define SWAP_CODES2( code1, code2, jarg ) \ + if ( cvttype[ nstep ] == code1 ) { \ + cvttype[ nstep ] = code2; \ + tmp = cvtargs[ nstep ][ jarg ]; \ + if( tmp != AST__BAD && tmp != 0.0 ) { \ + cvtargs[ nstep ][ jarg ] = 1.0/tmp; \ + } else { \ + cvtargs[ nstep ][ jarg ] = AST__BAD; \ + } \ + } else if ( cvttype[ nstep ] == code2 ) { \ + cvttype[ nstep ] = code1; \ + tmp = cvtargs[ nstep ][ jarg ]; \ + if( tmp != AST__BAD && tmp != 0.0 ) { \ + cvtargs[ nstep ][ jarg ] = 1.0/tmp; \ + } else { \ + cvtargs[ nstep ][ jarg ] = AST__BAD; \ + } \ + } + +/* Macro to exchange a transformation type code for its inverse (and + vice versa), and negate a specified argument. */ +#define SWAP_CODES3( code1, code2, jarg ) \ + if ( cvttype[ nstep ] == code1 ) { \ + cvttype[ nstep ] = code2; \ + tmp = cvtargs[ nstep ][ jarg ]; \ + if( tmp != AST__BAD ) { \ + cvtargs[ nstep ][ jarg ] = -tmp; \ + } \ + } else if ( cvttype[ nstep ] == code2 ) { \ + cvttype[ nstep ] = code1; \ + tmp = cvtargs[ nstep ][ jarg ]; \ + if( tmp != AST__BAD ) { \ + cvtargs[ nstep ][ jarg ] = -tmp; \ + } \ + } + +/* Use these macros to apply the changes where needed. */ + if ( invert ) { + +/* Exchange transformation codes for their inverses. */ + SWAP_CODES( AST__FRTOVL, AST__VLTOFR ) + SWAP_CODES( AST__ENTOFR, AST__FRTOEN ) + SWAP_CODES( AST__WNTOFR, AST__FRTOWN ) + SWAP_CODES( AST__WVTOFR, AST__FRTOWV ) + SWAP_CODES( AST__AWTOFR, AST__FRTOAW ) + SWAP_CODES( AST__VRTOVL, AST__VLTOVR ) + SWAP_CODES( AST__VOTOVL, AST__VLTOVO ) + SWAP_CODES( AST__ZOTOVL, AST__VLTOZO ) + SWAP_CODES( AST__BTTOVL, AST__VLTOBT ) + +/* Exchange transformation codes for their inverses, and reciprocate the + frequency correction factor. */ + SWAP_CODES2( AST__TPF2HL, AST__HLF2TP, 6 ) + SWAP_CODES2( AST__USF2HL, AST__HLF2US, 3 ) + SWAP_CODES2( AST__GEF2HL, AST__HLF2GE, 3 ) + SWAP_CODES2( AST__BYF2HL, AST__HLF2BY, 3 ) + SWAP_CODES2( AST__LKF2HL, AST__HLF2LK, 2 ) + SWAP_CODES2( AST__LDF2HL, AST__HLF2LD, 2 ) + SWAP_CODES2( AST__LGF2HL, AST__HLF2LG, 2 ) + SWAP_CODES2( AST__GLF2HL, AST__HLF2GL, 2 ) + + } + +/* Undefine the local macros. */ +#undef SWAP_CODES +#undef SWAP_CODES2 +#undef SWAP_CODES3 + +/* Count the transformation steps. */ + nstep++; + } + } + +/* Loop to simplify the sequence of transformation steps until no + further improvement is possible. */ + done = 0; + while ( astOK && !done ) { + +/* Examine each remaining transformation step in turn. */ + ikeep = -1; + for ( istep = 0; istep < nstep; istep++ ) { + +/* Initially assume we will retain the current step. */ + keep = 1; + +/* The only simplifications for the conversions currently in this class act + to combine adjacent transformation steps, so only apply them while there + are at least 2 steps left. */ + if ( istep < ( nstep - 1 ) ) { + +/* Define a macro to test if two adjacent transformation type codes + have specified values. */ +#define PAIR_CVT( code1, code2 ) \ + ( ( cvttype[ istep ] == code1 ) && \ + ( cvttype[ istep + 1 ] == code2 ) ) + +/* Define a macro to test if two adjacent transformation type codes + have specified values, either way round. */ +#define PAIR_CVT2( code1, code2 ) \ + ( ( PAIR_CVT( code1, code2 ) ) || \ + ( PAIR_CVT( code2, code1 ) ) ) + +/* If a correction is followed by its inverse, and the user-supplied argument + values are unchanged (we do not need to test values stored in the + argument array which were not supplied by the user), we can eliminate them. + First check for conversions which have no user-supplied arguments. */ + if ( PAIR_CVT2( AST__ENTOFR, AST__FRTOEN ) || + PAIR_CVT2( AST__WNTOFR, AST__FRTOWN ) || + PAIR_CVT2( AST__WVTOFR, AST__FRTOWV ) || + PAIR_CVT2( AST__AWTOFR, AST__FRTOAW ) || + PAIR_CVT2( AST__VRTOVL, AST__VLTOVR ) || + PAIR_CVT2( AST__VOTOVL, AST__VLTOVO ) || + PAIR_CVT2( AST__ZOTOVL, AST__VLTOZO ) || + PAIR_CVT2( AST__BTTOVL, AST__VLTOBT ) ) { + istep++; + keep = 0; + +/* Now check for conversions which have a single user-supplied argument. */ + } else if( PAIR_CVT2( AST__FRTOVL, AST__VLTOFR ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Now check for conversions which have two user-supplied arguments. */ + } else if( ( PAIR_CVT2( AST__LKF2HL, AST__HLF2LK ) || + PAIR_CVT2( AST__LDF2HL, AST__HLF2LD ) || + PAIR_CVT2( AST__LGF2HL, AST__HLF2LG ) || + PAIR_CVT2( AST__GLF2HL, AST__HLF2GL ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) ) { + istep++; + keep = 0; + +/* Now check for conversions which have three user-supplied arguments. */ + } else if( ( PAIR_CVT2( AST__GEF2HL, AST__HLF2GE ) || + PAIR_CVT2( AST__BYF2HL, AST__HLF2BY ) || + PAIR_CVT2( AST__USF2HL, AST__HLF2US ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 2 ], + cvtargs[ istep + 1 ][ 2 ] ) ) { + istep++; + keep = 0; + +/* Now check for conversions which have six user-supplied arguments (currently + no conversions have four or five user-supplied arguments). */ + } else if( ( PAIR_CVT2( AST__TPF2HL, AST__HLF2TP ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 2 ], + cvtargs[ istep + 1 ][ 2 ] ) && + astEQUAL( cvtargs[ istep ][ 3 ], + cvtargs[ istep + 1 ][ 3 ] ) && + astEQUAL( cvtargs[ istep ][ 4 ], + cvtargs[ istep + 1 ][ 4 ] ) && + astEQUAL( cvtargs[ istep ][ 5 ], + cvtargs[ istep + 1 ][ 5 ] ) ) { + istep++; + keep = 0; + + } + +/* Undefine the local macros. */ +#undef PAIR_CVT +#undef PAIR_CVT2 + } + +/* If the current transformation (possibly modified above) is being + kept, then increment the index that identifies its new location in + the list of transformation steps. */ + if ( keep ) { + ikeep++; + +/* If the new location is different to its current location, copy the + transformation data into the new location. */ + if ( ikeep != istep ) { + cvttype[ ikeep ] = cvttype[ istep ]; + for ( iarg = 0; iarg < szarg[ istep ]; iarg++ ) { + cvtargs[ ikeep ][ iarg ] = cvtargs[ istep ][ iarg ]; + } + szarg[ ikeep ] = szarg[ istep ]; + } + } + } + +/* Note if no simplification was achieved on this iteration (i.e. the + number of transformation steps was not reduced). This is the signal + to quit. */ + done = ( ( ikeep + 1 ) >= nstep ); + +/* Note how many transformation steps now remain. */ + nstep = ikeep + 1; + } + +/* Determine how many Mappings can be eliminated by condensing all + those considered above into a single Mapping. */ + if ( astOK ) { + ngone = imap2 - imap1; + +/* Determine if the replacement Mapping can be a UnitMap (a null + Mapping). This will only be the case if all the transformation + steps were eliminated above. */ + unit = ( nstep == 0 ); + +/* Determine if simplification is possible. This will be the case if + (a) Mappings were eliminated ("ngone" is non-zero), or (b) the + number of transformation steps was reduced, or (c) the SpecMap(s) + can be replaced by a UnitMap, or (d) if there was initially only + one SpecMap present, its invert flag was set (this flag will always + be cleared in the replacement Mapping). */ + simpler = ngone || ( nstep < nstep0 ) || unit || + ( *invert_list )[ where ]; + +/* Do nothing more unless simplification is possible. */ + if ( simpler ) { + +/* If the replacement Mapping is a UnitMap, then create it. */ + if ( unit ) { + new = (AstMapping *) + astUnitMap( astGetNin( ( *map_list )[ where ] ), "", status ); + +/* Otherwise, create a replacement SpecMap and add each of the + remaining transformation steps to it. */ + } else { + new = (AstMapping *) astSpecMap( nin, 0, "", status ); + for ( istep = 0; istep < nstep; istep++ ) { + AddSpecCvt( (AstSpecMap *) new, cvttype[ istep ], + cvtargs[ istep ], status ); + } + } + +/* Annul the pointers to the Mappings being eliminated. */ + if ( astOK ) { + for ( imap = imap1; imap <= imap2; imap++ ) { + ( *map_list )[ imap ] = astAnnul( ( *map_list )[ imap ] ); + } + +/* Insert the pointer and invert value for the new Mapping. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Move any subsequent Mapping information down to close the gap. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - ngone ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - ngone ] = ( *invert_list )[ imap ]; + } + +/* Blank out any information remaining at the end of the arrays. */ + for ( imap = ( *nmap - ngone ); imap < *nmap; imap++ ) { + ( *map_list )[ imap ] = NULL; + ( *invert_list )[ imap ] = 0; + } + +/* Decrement the Mapping count and return the index of the first + Mapping which was eliminated. */ + ( *nmap ) -= ngone; + result = imap1; + +/* If an error occurred, annul the new Mapping pointer. */ + } else { + new = astAnnul( new ); + } + } + } + +/* Free the memory used for the transformation steps. */ + cvttype = astFree( cvttype ); + cvtargs = astFree( cvtargs ); + szarg = astFree( szarg ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* SpecMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +* Implementation Deficiencies: +* The initial version of this implementation only deals with +* frequency->wavelength conversions. This is because the slowness of +* the numerical differentiation implemented by the astRate method in +* the parent Mapping class is cripples conversion between SpecFluxFrames. +* Such conversions only rely on rate of change of wavelength with +* respect to frequency. This implementation should be extended when +* needed. + +*/ + +/* Local Variables: */ + AstSpecMap *map; + double result; + int cvt; + +/* Check inherited status */ + if( !astOK ) return AST__BAD; + +/* Get a pointer to the SpecMap structure. */ + map = (AstSpecMap *) this; + +/* Return 1.0 if the SpecMap has no conversions. */ + if( map->ncvt == 0 ) return 1.0; + +/* Store the type of the first conversion.*/ + cvt = map->cvttype[ 0 ]; + +/* If this is a 3D SpecMap or if it has more than one component, or if + that conversion is not between frequency and wavelength, use the + astRate method inherited form the parent Mapping class. */ + if( astGetNin( map ) != 1 || map->ncvt != 1 || + ( cvt != AST__WVTOFR && cvt != AST__FRTOWV ) ) { + result = (*parent_rate)( this, at, ax1, ax2, status ); + +/* Otherwise, evaluate the known analytical expressions for the rate of + change of frequency with respect to wavelength or wavelength with + respect to frequency. */ + } else { + result = ( *at != AST__BAD ) ? -AST__C/((*at)*(*at)) : AST__BAD; + } + +/* Return the result. */ + return result; +} + +static double Refrac( double wavelen, int *status ){ +/* +* Name: +* Refrac + +* Purpose: +* Returns the refractive index of dry air at a given wavelength. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double Refrac( double wavelen, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function returns the refractive index of dry air at standard +* temperature and pressure, at a given wavelength. The formula is +* taken from the paper "Representation of Spectral Coordinates in FITS" +* (Greisen et al). + +* Parameters: +* wavelen +* The wavelength, in metres. This should be the air wavelength, +* but supplying the vacuum wavelength will make no significant +* difference. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The refractive index. A value of 1.0 is returned if an error +* occurs, or has already occurred. + +*/ + +/* Local Variables: */ + double w2; /* Wavenumber squared */ + +/* Check the global error status. */ + if ( !astOK || wavelen == 0.0 ) return 1.0; + +/* Find the squared wave number in units of "(per um)**2". */ + w2 = 1.0E-12/( wavelen * wavelen ); + +/* Apply the rest of the algorithm as described in the FITS WCS + paper III. */ + return 1.0 + 1.0E-6*( 287.6155 + 1.62887*w2 + 0.01360*w2*w2 ); +} + +static double Rverot( double phi, double h, double ra, double da, + double st, int *status ) { +/* +* Name: +* Rverot + +* Purpose: +* Find the velocity component in a given direction due to Earth rotation. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double Rverot( double phi, double h, double ra, double da, +* double st, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function is like slaRverot, except that it takes account of the +* observers height (h), and does all calculations in double precision. + +* Parameters: +* phi +* The geodetic latitude of the observer (radians, IAU 1976). +* h +* The geodetic height above the reference spheroid of the observer +* (metres, IAU 1976). +* ra +* The geocentric apparent RA (rads) of the source. +* da +* The geocentric apparent Dec (rads) of the source. +* st +* The local apparent sidereal time (radians). +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the Earth rotation in direction [RA,DA] (km/s). +* The result is positive when the observer is receding from the +* given point on the sky. Zero is returned if an error has already +* occurred. + +*/ + +/* Local Variables: */ + double pv[ 6 ]; /* Observer position and velocity */ + double v[ 3 ]; /* Source direction vector */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* Get the Cartesian coordinates of the unit vector pointing towards the + given sky position. */ + palDcs2c( ra, da, v ); + +/* Get velocity and position of the observer. */ + palPvobs( phi, h, st, pv ); + +/* Return the component of the observer's velocity away from the sky + position, and convert from AU/s to km/s. */ + return -palDvdv( v, pv + 3 )*149.597870E6; +} + +static void SpecAdd( AstSpecMap *this, const char *cvt, const double args[], int *status ) { +/* +*++ +* Name: +c astSpecAdd +f AST_SPECADD + +* Purpose: +* Add a spectral coordinate conversion to a SpecMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "specmap.h" +c void astSpecAdd( AstSpecMap *this, const char *cvt, const double args[] ) +f CALL AST_SPECADD( THIS, CVT, ARGS, STATUS ) + +* Class Membership: +* SpecMap method. + +* Description: +c This function adds one of the standard spectral coordinate +f This routine adds one of the standard spectral coordinate +* system conversions listed below to an existing SpecMap. +* +c When a SpecMap is first created (using astSpecMap), it simply +f When a SpecMap is first created (using AST_SPECMAP), it simply +c performs a unit (null) Mapping. By using astSpecAdd (repeatedly +f performs a unit (null) Mapping. By using AST_SPECADD (repeatedly +* if necessary), one or more coordinate conversion steps may then +* be added, which the SpecMap will perform in sequence. This allows +* multi-step conversions between a variety of spectral coordinate +* systems to be assembled out of the building blocks provided by +* this class. +* +* Normally, if a SpecMap's Invert attribute is zero (the default), +* then its forward transformation is performed by carrying out +* each of the individual coordinate conversions specified by +c astSpecAdd in the order given (i.e. with the most recently added +f AST_SPECADD in the order given (i.e. with the most recently added +* conversion applied last). +* +* This order is reversed if the SpecMap's Invert attribute is +* non-zero (or if the inverse transformation is requested by any +* other means) and each individual coordinate conversion is also +* replaced by its own inverse. This process inverts the overall +* effect of the SpecMap. In this case, the first conversion to be +* applied would be the inverse of the one most recently added. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the SpecMap. +c cvt +f CVT = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string which identifies the +f A character string which identifies the +* spectral coordinate conversion to be added to the +* SpecMap. See the "Available Conversions" section for details of +* those available. +c args +f ARGS( * ) = DOUBLE PRECISION (Given) +* An array containing argument values for the spectral +* coordinate conversion. The number of arguments required, and +* hence the number of array elements used, depends on the +* conversion specified (see the "Available Conversions" +* section). This array is ignored +c and a NULL pointer may be supplied +* if no arguments are needed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - When assembling a multi-stage conversion, it can sometimes be +* difficult to determine the most economical conversion path. For +* example, when converting between reference frames, converting first +* to the heliographic reference frame as an intermediate stage is often +* sensible in formulating the problem, but may introduce unnecessary +* extra conversion steps. A solution to this is to include all the steps +* which are (logically) necessary, but then to use +c astSimplify to simplify the resulting +f AST_SIMPLIFY to simplify the resulting +* SpecMap. The simplification process will eliminate any steps +* which turn out not to be needed. +c - This function does not check to ensure that the sequence of +f - This routine does not check to ensure that the sequence of +* coordinate conversions added to a SpecMap is physically +* meaningful. + +* Available Conversions: +* The following strings (which are case-insensitive) may be supplied +c via the "cvt" parameter to indicate which spectral coordinate +f via the CVT argument to indicate which spectral coordinate +* conversion is to be added to the SpecMap. Where arguments are needed by +* the conversion, they are listed in parentheses. Values for +c these arguments should be given, via the "args" array, in the +f these arguments should be given, via the ARGS array, in the +* order indicated. Units and argument names are described at the end of +* the list of conversions. + +* - "FRTOVL" (RF): Convert frequency to relativistic velocity. +* - "VLTOFR" (RF): Convert relativistic velocity to Frequency. +* - "ENTOFR": Convert energy to frequency. +* - "FRTOEN": Convert frequency to energy. +* - "WNTOFR": Convert wave number to frequency. +* - "FRTOWN": Convert frequency to wave number. +* - "WVTOFR": Convert wavelength (vacuum) to frequency. +* - "FRTOWV": Convert frequency to wavelength (vacuum). +* - "AWTOFR": Convert wavelength (air) to frequency. +* - "FRTOAW": Convert frequency to wavelength (air). +* - "VRTOVL": Convert radio to relativistic velocity. +* - "VLTOVR": Convert relativistic to radio velocity. +* - "VOTOVL": Convert optical to relativistic velocity. +* - "VLTOVO": Convert relativistic to optical velocity. +* - "ZOTOVL": Convert redshift to relativistic velocity. +* - "VLTOZO": Convert relativistic velocity to redshift. +* - "BTTOVL": Convert beta factor to relativistic velocity. +* - "VLTOBT": Convert relativistic velocity to beta factor. +* - "USF2HL" (VOFF,RA,DEC): Convert frequency from a user-defined +* reference frame to heliocentric. +* - "HLF2US" (VOFF,RA,DEC): Convert frequency from heliocentric +* reference frame to user-defined. +* - "TPF2HL" (OBSLON,OBSLAT,OBSALT,EPOCH,RA,DEC): Convert frequency from +* topocentric reference frame to heliocentric. +* - "HLF2TP" (OBSLON,OBSLAT,OBSALT,EPOCH,RA,DEC): Convert frequency from +* heliocentric reference frame to topocentric. +* - "GEF2HL" (EPOCH,RA,DEC): Convert frequency from geocentric +* reference frame to heliocentric. +* - "HLF2GE" (EPOCH,RA,DEC): Convert frequency from +* heliocentric reference frame to geocentric. +* - "BYF2HL" (EPOCH,RA,DEC): Convert frequency from +* barycentric reference frame to heliocentric. +* - "HLF2BY" (EPOCH,RA,DEC): Convert frequency from +* heliocentric reference frame to barycentric. +* - "LKF2HL" (RA,DEC): Convert frequency from kinematic LSR +* reference frame to heliocentric. +* - "HLF2LK" (RA,DEC): Convert frequency from heliocentric +* reference frame to kinematic LSR. +* - "LDF2HL" (RA,DEC): Convert frequency from dynamical LSR +* reference frame to heliocentric. +* - "HLF2LD" (RA,DEC): Convert frequency from heliocentric +* reference frame to dynamical LSR. +* - "LGF2HL" (RA,DEC): Convert frequency from local group +* reference frame to heliocentric. +* - "HLF2LG" (RA,DEC): Convert frequency from heliocentric +* reference frame to local group. +* - "GLF2HL" (RA,DEC): Convert frequency from galactic +* reference frame to heliocentric. +* - "HLF2GL" (RA,DEC): Convert frequency from heliocentric +* reference frame to galactic. + +* The units for the values processed by the above conversions are as +* follows: +* +* - all velocities: metres per second (positive if the source receeds from +* the observer). +* - frequency: Hertz. +* - all wavelengths: metres. +* - energy: Joules. +* - wave number: cycles per metre. +* +* The arguments used in the above conversions are as follows: +* +* - RF: Rest frequency (Hz). +* - OBSALT: Geodetic altitude of observer (IAU 1975, metres). +* - OBSLAT: Geodetic latitude of observer (IAU 1975, radians). +* - OBSLON: Longitude of observer (radians - positive eastwards). +* - EPOCH: Epoch of observation (UT1 expressed as a Modified Julian Date). +* - RA: Right Ascension of source (radians, FK5 J2000). +* - DEC: Declination of source (radians, FK5 J2000). +* - VOFF: Velocity of the user-defined reference frame, towards the +* position given by RA and DEC, measured in the heliocentric +* reference frame. +* +* If the SpecMap is 3-dimensional, source positions are provided by the +* values supplied to inputs 2 and 3 of the SpecMap (which are simply +* copied to outputs 2 and 3). Note, usable values are still required +* for the RA and DEC arguments in order to define the "user-defined" +* reference frame used by USF2HL and HLF2US. However, AST__BAD can be +* supplied for RA and DEC if the user-defined reference frame is not +* required. +* +*-- +*/ + +/* Local Variables: */ + int cvttype; /* Conversion type code */ + +/* Check the inherited status. */ + if ( !astOK ) return; + +/* Validate the type string supplied and obtain the equivalent + conversion type code. */ + cvttype = CvtCode( cvt, status ); + +/* If the string was not recognised, then report an error. */ + if ( astOK && ( cvttype == AST__SPEC_NULL ) ) { + astError( AST__SPCIN, + "%s(%s): Invalid SpecMap spectral coordinate " + "conversion type \"%s\".", status, "astAddSpec", astGetClass( this ), cvt ); + } + +/* Add the new conversion to the SpecMap. */ + AddSpecCvt( this, cvttype, args, status ); +} + +static int SystemChange( int cvt_code, int np, double *values, double *args, + int forward, int *status ){ +/* +* Name: +* SystemChange + +* Purpose: +* Change values between two spectral systems. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* int SystemChange( int cvt_code, int np, double *values, double *args, +* int forward, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function modifies the supplied values in order to change the +* spectral co-ordinate system (frequency, wavelength, etc) to which +* they refer. + +* Parameters: +* cvt_code +* A code indicating the conversion to be applied. If the code does +* not correspond to a change of system, then the supplied values +* are left unchanged and zero is returned as the function value. +* np +* The number of frequency values to transform. +* values +* Pointer to an array of "np" spectral values. These are modified on +* return to hold the corresponding values measured in the output +* system. +* args +* Pointer to an array holding the conversion arguments. The number +* of arguments expected depends on the particular conversion being +* used. +* forward +* Should the conversion be applied in the forward or inverse +* direction? Non-zero for forward, zero for inverse. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the supplied conversion code corresponds to a change of +* system. Zero otherwise (in which case the upplied values will not +* have been changed). + +*/ + +/* Local Variables: */ + double *pv; /* Pointer to next value */ + double d; /* Intermediate value */ + double f2; /* Squared frequency */ + double temp; /* Intermediate value */ + int i; /* Loop index */ + int iter; /* Iteration count */ + int result; /* Returned value */ + +/* Check inherited status. */ + if( !astOK ) return 0; + +/* Set the return value to indicate that the supplied conversion code + represents a change of system. */ + result = 1; + +/* Test for each code value in turn and assign the appropriate values. */ + switch ( cvt_code ) { + +/* Frequency to relativistic velocity. */ + case AST__FRTOVL: + if( forward ) { + if( args[ 0 ] != AST__BAD ) { + temp = args[ 0 ] * args[ 0 ]; + pv = values - 1; + for( i = 0; i < np; i++ ){ + pv++; + if( *pv != AST__BAD ) { + f2 = ( *pv ) * ( *pv ); + d = temp + f2; + if( d > 0.0 ) { + *pv = AST__C*( ( temp - f2 )/d ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } + } + } else { + pv = values; + for( i = 0; i < np; i++ ) *( pv++ ) = AST__BAD; + } + } else { + SystemChange( AST__VLTOFR, np, values, args, 1, status ); + } + break; + +/* Relativistic velocity to frequency. */ + case AST__VLTOFR: + if( forward ) { + if( args[ 0 ] != AST__BAD ) { + temp = args[ 0 ]; + pv = values - 1; + for( i = 0; i < np; i++ ){ + pv++; + if( *pv != AST__BAD ) { + d = AST__C + ( *pv ); + if( d != 0.0 ) { + d = ( AST__C - ( *pv ) )/d; + if( d >= 0.0 ) { + *pv = temp*sqrt( d ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } else { + *pv = AST__BAD; + } + } + } + } else { + pv = values; + for( i = 0; i < np; i++ ) *( pv++ ) = AST__BAD; + } + } else { + SystemChange( AST__FRTOVL, np, values, args, 1, status ); + } + break; + +/* Energy to frequency */ + case AST__ENTOFR: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + *pv /= AST__H; + } + } + } else { + SystemChange( AST__FRTOEN, np, values, args, 1, status ); + } + break; + +/* Frequency to energy */ + case AST__FRTOEN: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + *pv *= AST__H; + } + } + } else { + SystemChange( AST__ENTOFR, np, values, args, 1, status ); + } + break; + +/* Wave number to frequency */ + case AST__WNTOFR: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + *pv *= AST__C; + } + } + } else { + SystemChange( AST__FRTOWN, np, values, args, 1, status ); + } + break; + +/* Wave number to frequency */ + case AST__FRTOWN: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + *pv /= AST__C; + } + } + } else { + SystemChange( AST__WNTOFR, np, values, args, 1, status ); + } + break; + +/* Wavelength to frequency */ + case AST__WVTOFR: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD && *pv != 0.0 ) { + *pv = AST__C/( *pv ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } + } else { + SystemChange( AST__FRTOWV, np, values, args, 1, status ); + } + break; + +/* Frequency to wavelength. */ + case AST__FRTOWV: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD && *pv != 0.0 ) { + *pv = AST__C/( *pv ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } + } else { + SystemChange( AST__WVTOFR, np, values, args, 1, status ); + } + break; + +/* Wavelength in air to frequency. */ + case AST__AWTOFR: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD && *pv != 0.0 ) { + *pv = AST__C/( ( *pv )*Refrac( *pv, status ) ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } + } else { + SystemChange( AST__FRTOAW, np, values, args, 1, status ); + } + break; + +/* Frequency to wavelength in air. */ + case AST__FRTOAW: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD && *pv != 0.0 ) { + +/* Form the vacuum wavelength. */ + temp = AST__C/( *pv ); + +/* The refractive index function "Refrac" requires the wavelength in air + as its parameter. Initially assume that the wavelength in air is equal + to the vacuum wavelength to get he first estimate of the wavelength in + air. Then use this estimate to get a better refractive index in order to + form a better estimate of the air wavelength, etc. Iterate in this way a + few times. */ + *pv = temp; + for( iter = 0; iter < 3; iter++ ) { + *pv = temp/Refrac( *pv, status ); + if( !astISFINITE( *pv ) ) { + *pv = AST__BAD; + break; + } + } + + } else { + *pv = AST__BAD; + } + } + } else { + SystemChange( AST__AWTOFR, np, values, args, 1, status ); + } + break; + +/* Radio velocity to relativistic velocity */ + case AST__VRTOVL: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + temp = 1.0 - ( *pv )/AST__C; + temp *= temp; + *pv = AST__C*( 1.0 - temp )/( 1.0 + temp ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } + } + } else { + SystemChange( AST__VLTOVR, np, values, args, 1, status ); + } + break; + +/* Relativistic velocity to radio velocity. */ + case AST__VLTOVR: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + temp = AST__C + ( *pv ); + if( temp != 0.0 ) { + temp = (AST__C - *pv )/temp; + if( temp >= 0.0 ) { + *pv = AST__C*( 1.0 - sqrt( temp ) ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } else { + *pv = AST__BAD; + } + } + } + } else { + SystemChange( AST__VRTOVL, np, values, args, 1, status ); + } + break; + +/* Optical velocity to relativistic velocity */ + case AST__VOTOVL: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + temp = 1.0 + ( *pv )/AST__C; + temp *= temp; + *pv = AST__C*( temp - 1.0 )/( temp + 1.0 ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } + } + } else { + SystemChange( AST__VLTOVO, np, values, args, 1, status ); + } + break; + +/* Relativistic velocity to optical velocity. */ + case AST__VLTOVO: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + temp = AST__C - *pv; + if( temp != 0.0 ) { + temp = (AST__C + *pv )/temp; + if( temp >= 0.0 ) { + *pv = AST__C*( sqrt( temp ) - 1.0 ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } else { + *pv = AST__BAD; + } + } + } + } else { + SystemChange( AST__VOTOVL, np, values, args, 1, status ); + } + break; + +/* Redshift to relativistic velocity */ + case AST__ZOTOVL: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + temp = 1.0 + ( *pv ); + temp *= temp; + *pv = AST__C*( temp - 1.0 )/( temp + 1.0 ); + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } + } + } else { + SystemChange( AST__VLTOZO, np, values, args, 1, status ); + } + break; + +/* Relativistic velocity to redshift. */ + case AST__VLTOZO: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + temp = AST__C - *pv; + if( temp != 0.0 ) { + temp = (AST__C + *pv )/temp; + if( temp >= 0.0 ) { + *pv = sqrt( temp ) - 1.0; + if( !astISFINITE( *pv ) ) *pv = AST__BAD; + } else { + *pv = AST__BAD; + } + } else { + *pv = AST__BAD; + } + } + } + } else { + SystemChange( AST__ZOTOVL, np, values, args, 1, status ); + } + break; + +/* Beta factor to relativistic velocity */ + case AST__BTTOVL: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + *pv *= AST__C; + } + } + } else { + SystemChange( AST__VLTOBT, np, values, args, 1, status ); + } + break; + +/* Relativistic velocity to beta factor. */ + case AST__VLTOBT: + if( forward ) { + pv = values - 1; + for( i = 0; i < np; i++ ) { + pv++; + if( *pv != AST__BAD ) { + *pv /= AST__C; + } + } + } else { + SystemChange( AST__BTTOVL, np, values, args, 1, status ); + } + break; + +/* If the supplied code does not represent a change of system, clear + the returned flag. */ + default: + result = 0; + } + +/* Return the result. */ + return result; +} + +static double TopoVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* TopoVel + +* Purpose: +* Find the velocity of the observer away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double TopoVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the velocity of the observer away +* from a specified source position, at a given epoch, in the frame of +* rest of the Sun. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +*/ + +/* Local Variables: */ + double deca; /* Apparent DEC */ + double raa; /* Apparent RA */ + double vobs; /* Velocity of observer relative to earth */ + double vearth; /* Velocity of earth realtive to sun */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* If not already done so, get the parameters defining the transformation + of mean ra and dec to apparent ra and dec, and store in the supplied frame + definition structure. */ + if( def->amprms[ 0 ] == AST__BAD ) palMappa( 2000.0, def->epoch, + def->amprms ); + +/* Convert the source position from mean ra and dec to apparent ra and dec. */ + palMapqkz( ra, dec, def->amprms, &raa, &deca ); + +/* If not already done so, get the local apparent siderial time (in radians) + and store in the supplied frame definition structure. */ + if( def->last == AST__BAD ) def->last = palGmst( def->epoch ) + + palEqeqx( def->epoch ) + + def->obslon; + +/* Get the component away from the source, of the velocity of the observer + relative to the centre of the earth (in m/s). */ + vobs = 1000.0*Rverot( def->obslat, def->obsalt, raa, deca, def->last, + status ); + +/* Get the component away from the source, of the velocity of the earth's + centre relative to the Sun, in m/s. */ + vearth = GeoVel( ra, dec, def, status ); + +/* Return the total velocity of the observer away from the source in the + frame of the sun. */ + return vobs + vearth; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a SpecMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* SpecMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a SpecMap and a set of points encapsulated +* in a PointSet and transforms the points so as to perform the +* sequence of spectral coordinate conversions specified by +* previous invocations of astSpecAdd. + +* Parameters: +* this +* Pointer to the SpecMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the SpecMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstSpecMap *map; /* Pointer to SpecMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *spec; /* Pointer to output spectral axis value array */ + double *alpha; /* Pointer to output RA axis value array */ + double *beta; /* Pointer to output DEC axis value array */ + int cvt; /* Loop counter for conversions */ + int end; /* Termination index for conversion loop */ + int inc; /* Increment for conversion loop */ + int map3d; /* Is the SpecMap 3-dimensional? */ + int ncoord_in; /* Number of coordinates per input point */ + int npoint; /* Number of points */ + int start; /* Starting index for conversion loop */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SpecMap. */ + map = (AstSpecMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + coordinate conversions needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + ncoord_in = astGetNcoord( in ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse transformation, according + to the direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( this ) ) forward = !forward; + +/* Transform the coordinate values. */ +/* -------------------------------- */ +/* Use "spec" as a synonym for the array of spectral axis values stored in + the output PointSet. */ + if ( astOK ) { + spec = ptr_out[ 0 ]; + +/* If this is a 3D SpecMap use "alpha" as a synonym for the array of RA axis + values and "beta" as a synonym for the array of DEC axis values stored + in the output PointSet. */ + map3d = ( ncoord_in == 3 ); + if( map3d ) { + alpha = ptr_out[ 1 ]; + beta = ptr_out[ 2 ]; + } else { + alpha = NULL; + beta = NULL; + } + +/* Initialise the output coordinate values by copying the input ones. */ + (void) memcpy( spec, ptr_in[ 0 ], sizeof( double ) * (size_t) npoint ); + if( map3d ) { + (void) memcpy( alpha, ptr_in[ 1 ], sizeof( double ) * (size_t) npoint ); + (void) memcpy( beta, ptr_in[ 2 ], sizeof( double ) * (size_t) npoint ); + } + +/* We will loop to apply each spectral coordinate conversion in turn to the + (spec) array. However, if the inverse transformation was requested, + we must loop through these transformations in reverse order, so set up + appropriate limits and an increment to control this loop. */ + start = forward ? 0 : map->ncvt - 1; + end = forward ? map->ncvt : -1; + inc = forward ? 1 : -1; + +/* Loop through the coordinate conversions in the required order. */ + for ( cvt = start; cvt != end; cvt += inc ) { + +/* Process conversions which correspond to changes of reference frames. */ + if( !FrameChange( map->cvttype[ cvt ], npoint, alpha, beta, spec, + map->cvtargs[ cvt ], forward, status ) ) { + +/* If this conversion was not a change of reference frame, it must be a + change of system. */ + SystemChange( map->cvttype[ cvt ], npoint, spec, + map->cvtargs[ cvt ], forward, status ); + } + } + } + +/* If an error has occurred and a new PointSet may have been created, then + clean up by annulling it. In any case, ensure that a NULL result is + returned.*/ + if ( !astOK ) { + if ( !out ) result = astAnnul( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; + +} + +static double UserVel( double ra, double dec, FrameDef *def, int *status ) { +/* +* Name: +* UserVel + +* Purpose: +* Find the component of the velocity of the user-defined rest-frame +* away from the source. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* double UserVel( double ra, double dec, FrameDef *def, int *status ) + +* Class Membership: +* SpecMap method. + +* Description: +* This function finds the component of the velocity of the user-defined +* rest-frame away from a specified position. The magnitude and direction +* of the rest-frames velocity are defined within the supplied "def" +* structure. The user-defined rest-frame is typically used to represent +* the velocity of the source within the heliocentric rest-frame. + +* Parameters: +* ra +* The RA (rads, FK5 J2000) of the source. +* dec +* The Dec (rads, FK5 J2000) of the source. +* def +* Pointer to a FrameDef structure which holds the parameters which +* define the frame, together with cached intermediate results. +* status +* Pointer to the inherited status variable. + +* Returns: +* The component of the frame's velocity away from the position given by +* "ra" and "dec", in m/s, measured within the Heliographic frame of +* rest. Zero is returned if an error has already occurred. + +* Notes: +* - The direction of the user velocity is given by def->refra and +* def->refdec (an FK5 J2000 position). The maginitude of the velocity +* is given by def->veluser, in m/s, positive when the source is moving +* away from the observer towards def->refra, def->refdec, and given +* with respect to the heliocentric rest-frame. + +*/ + +/* Local Variables: */ + double vb[ 3 ]; /* Source position vector */ + +/* Check the global error status. */ + if ( !astOK ) return 0.0; + +/* If not already done so, express the user velocity in the form of a + J2000.0 x,y,z vector. */ + if( def->vuser[ 0 ] == AST__BAD ) { + def->vuser[ 0 ] = def->veluser*cos( def->refra )*cos( def->refdec ); + def->vuser[ 1 ] = def->veluser*sin( def->refra )*cos( def->refdec ); + def->vuser[ 2 ] = def->veluser*sin( def->refdec ); + } + +/* Convert given J2000 RA,Dec to x,y,z. */ + palDcs2c( ra, dec, vb ); + +/* Return the dot product with the user velocity. Invert it to get the + velocity towards the observer (the def->veluser value is supposed to be + positive if the source is moving away from the observer). */ + return -palDvdv( def->vuser, vb ); +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SpecMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SpecMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstSpecMap *in; /* Pointer to input SpecMap */ + AstSpecMap *out; /* Pointer to output SpecMap */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SpecMap structures. */ + in = (AstSpecMap *) objin; + out = (AstSpecMap *) objout; + +/* For safety, first clear any references to the input memory from the output + SpecMap. */ + out->cvtargs = NULL; + out->cvttype = NULL; + +/* Allocate memory for the output array of argument list pointers. */ + out->cvtargs = astMalloc( sizeof( double * ) * (size_t) in->ncvt ); + +/* If necessary, allocate memory and make a copy of the input array of + coordinate conversion codes. */ + if ( in->cvttype ) out->cvttype = astStore( NULL, in->cvttype, + sizeof( int ) + * (size_t) in->ncvt ); + +/* If OK, loop through each conversion in the input SpecMap and make a copy of + its argument list, storing the new pointer in the output argument list + array. */ + if ( astOK ) { + for ( cvt = 0; cvt < in->ncvt; cvt++ ) { + out->cvtargs[ cvt ] = astStore( NULL, in->cvtargs[ cvt ], + astSizeOf( in->cvtargs[ cvt ] ) ); + } + +/* If an error occurred while copying the argument lists, loop through the + conversions again and clean up by ensuring that the new memory allocated for + each argument list is freed. */ + if ( !astOK ) { + for ( cvt = 0; cvt < in->ncvt; cvt++ ) { + out->cvtargs[ cvt ] = astFree( out->cvtargs[ cvt ] ); + } + } + } + +/* If an error occurred, free all other memory allocated above. */ + if ( !astOK ) { + out->cvtargs = astFree( out->cvtargs ); + out->cvttype = astFree( out->cvttype ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SpecMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SpecMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSpecMap *this; /* Pointer to SpecMap */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Obtain a pointer to the SpecMap structure. */ + this = (AstSpecMap *) obj; + +/* Loop to free the memory containing the argument list for each coordinate + conversion. */ + for ( cvt = 0; cvt < this->ncvt; cvt++ ) { + this->cvtargs[ cvt ] = astFree( this->cvtargs[ cvt ] ); + } + +/* Free the memory holding the array of conversion types and the array of + argument list pointers. */ + this->cvtargs = astFree( this->cvtargs ); + this->cvttype = astFree( this->cvttype ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SpecMap objects. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SpecMap class to an output Channel. + +* Parameters: +* this +* Pointer to the SpecMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstSpecMap *this; /* Pointer to the SpecMap structure */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + const char *sval; /* Pointer to string value */ + int argdec; /* Index of DEC argument */ + int argra; /* Index of RA argument */ + int iarg; /* Loop counter for arguments */ + int icvt; /* Loop counter for conversion steps */ + int ival; /* Integer value */ + int nargs; /* Number of user-supplied arguments */ + int szargs; /* Number of stored arguments */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SpecMap structure. */ + this = (AstSpecMap *) this_object; + +/* Write out values representing the instance variables for the SpecMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Number of conversion steps. */ +/* --------------------------- */ +/* Regard this as "set" if it is non-zero. */ + ival = this->ncvt; + set = ( ival != 0 ); + astWriteInt( channel, "Nspec", set, 0, ival, "Number of conversion steps" ); + +/* Write out data for each conversion step... */ + for ( icvt = 0; icvt < this->ncvt; icvt++ ) { + +/* Conversion type. */ +/* ---------------- */ +/* Change each conversion type code into an equivalent string and + obtain associated descriptive information. If the conversion code + was not recognised, report an error and give up. */ + if ( astOK ) { + sval = CvtString( this->cvttype[ icvt ], &comment, &argra, &argdec, + &nargs, &szargs, argdesc, status ); + if ( astOK && !sval ) { + astError( AST__SPCIN, + "astWrite(%s): Corrupt %s contains invalid SpecMap " + "spectral coordinate conversion code (%d).", status, + astGetClass( channel ), astGetClass( this ), + (int) this->cvttype[ icvt ] ); + break; + } + +/* Create an appropriate keyword and write out the conversion code + information. */ + (void) sprintf( key, "Spec%d", icvt + 1 ); + astWriteString( channel, key, 1, 1, sval, comment ); + +/* Write out data for each conversion argument... */ + for ( iarg = 0; iarg < szargs; iarg++ ) { + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and write out the argument value, + accompanied by the descriptive comment obtained above. */ + if( this->cvtargs[ icvt ][ iarg ] != AST__BAD ) { + (void) sprintf( key, "Spec%d%c", icvt + 1, ALPHABET[ iarg ] ); + astWriteDouble( channel, key, 1, 1, this->cvtargs[ icvt ][ iarg ], + argdesc[ iarg ] ); + } + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASpecMap and astCheckSpecMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SpecMap,Mapping) +astMAKE_CHECK(SpecMap) + +AstSpecMap *astSpecMap_( int nin, int flags, const char *options, int *status, ...) { +/* +*++ +* Name: +c astSpecMap +f AST_SPECMAP + +* Purpose: +* Create a SpecMap. + +* Type: +* Public function. + +* Synopsis: +c #include "specmap.h" +c AstSpecMap *astSpecMap( int nin, int flags, const char *options, ... ) +f RESULT = AST_SPECMAP( NIN, FLAGS, OPTIONS, STATUS ) + +* Class Membership: +* SpecMap constructor. + +* Description: +* This function creates a new SpecMap and optionally initialises +* its attributes. +* +* An SpecMap is a specialised form of Mapping which can be used to +* represent a sequence of conversions between standard spectral +* coordinate systems. This includes conversions between frequency, +* wavelength, and various forms of velocity, as well as conversions +* between different standards of rest. +* +* When a SpecMap is first created, it simply performs a unit +c (null) Mapping. Using the astSpecAdd function, +f (null) Mapping. Using the AST_SPECADD routine, +* a series of coordinate conversion steps may then be added, selected +* from the list of supported conversions. This allows multi-step +* conversions between a variety of spectral coordinate systems to +* be assembled out of the building blocks provided by this class. +* +* For details of the individual coordinate conversions available, +c see the description of the astSpecAdd function. +f see the description of the AST_SPECADD routine. +* +* Conversions are available to transform between standards of rest. +* Such conversions need to know the source position as an RA and DEC. +* This information can be supplied in the form of parameters for +* the relevant conversions, in which case the SpecMap is 1-dimensional, +* simply transforming the spectral axis values. This means that the +* same source position will always be used by the SpecMap. However, this +* may not be appropriate for an accurate description of a 3-D spectral +* cube, where changes of spatial position can produce significant +* changes in the Doppler shift introduced when transforming between +* standards of rest. For this situation, a 3-dimensional SpecMap can +* be created in which axes 2 and 3 correspond to the source RA and DEC +* The SpecMap simply copies values for axes 2 and 3 from input to +* output). + +* Parameters: +c nin +f NIN = INTEGER (Given) +* The number of inputs to the Mapping (this will also equal the +* number of outputs). This value must be either 1 or 3. In either +* case, the first input and output correspoindis the spectral axis. +* For a 3-axis SpecMap, the second and third axes give the RA and +* DEC (J2000 FK5) of the source. This positional information is +* used by conversions which transform between standards of rest, +* and replaces the "RA" and "DEC" arguments for the individual +* conversions listed in description of the "SpecAdd" +c function. +f routine. +c flags +f FLAGS = INTEGER (Given) +c This parameter is reserved for future use and should currently +f This argument is reserved for future use and should currently +* always be set to zero. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SpecMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SpecMap. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSpecMap() +f AST_SPECMAP = INTEGER +* A pointer to the new SpecMap. + +* Notes: +* - The nature and units of the coordinate values supplied for the +* first input (i.e. the spectral input) of a SpecMap must be appropriate +* to the first conversion step applied by the SpecMap. For instance, if +* the first conversion step is "FRTOVL" (frequency to relativistic +* velocity), then the coordinate values for the first input should +* be frequency in units of Hz. Similarly, the nature and units of the +* coordinate values returned by a SpecMap will be determined by the +* last conversion step applied by the SpecMap. For instance, if the +* last conversion step is "VLTOVO" (relativistic velocity to optical +* velocity), then the coordinate values for the first output will be optical +* velocity in units of metres per second. See the description of the +c astSpecAdd function for the units expected and returned by each +f AST_SPECADD routine for the units expected and returned by each +* conversion. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSpecMap *new; /* Pointer to the new SpecMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SpecMap, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSpecMap( NULL, sizeof( AstSpecMap ), !class_init, &class_vtab, + "SpecMap", nin, flags ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SpecMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SpecMap. */ + return new; +} + +AstSpecMap *astSpecMapId_( int nin, int flags, const char *options, ... ) { +/* +* Name: +* astSpecMapId_ + +* Purpose: +* Create a SpecMap. + +* Type: +* Private function. + +* Synopsis: +* #include "specmap.h" +* AstSpecMap *astSpecMapId_( int nin, int flags, const char *options, ... ) + +* Class Membership: +* SpecMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astSpecMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astSpecMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astSpecMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astSpecMap_. + +* Returned Value: +* The ID value associated with the new SpecMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSpecMap *new; /* Pointer to the new SpecMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SpecMap, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitSpecMap( NULL, sizeof( AstSpecMap ), !class_init, &class_vtab, + "SpecMap", nin, flags ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SpecMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new SpecMap. */ + return astMakeId( new ); +} + +AstSpecMap *astInitSpecMap_( void *mem, size_t size, int init, + AstSpecMapVtab *vtab, const char *name, + int nin, int flags, int *status ) { +/* +*+ +* Name: +* astInitSpecMap + +* Purpose: +* Initialise a SpecMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "specmap.h" +* AstSpecMap *astInitSpecMap( void *mem, size_t size, int init, +* AstSpecMapVtab *vtab, const char *name, +* int nin, int flags ) + +* Class Membership: +* SpecMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SpecMap object. It allocates memory (if necessary) to accommodate +* the SpecMap plus any additional data associated with the derived class. +* It then initialises a SpecMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a SpecMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SpecMap is to be initialised. +* This must be of sufficient size to accommodate the SpecMap data +* (sizeof(SpecMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the SpecMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the SpecMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the SpecMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SpecMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astClass +* method). +* nin +* The number of inputs and outputs for the SpecMap (either 1 or 3). +* flags +* This parameter is reserved for future use. It is currently ignored. + +* Returned Value: +* A pointer to the new SpecMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSpecMap *new; /* Pointer to the new SpecMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Check nin is OK (1 or 3). */ + if( nin != 1 && nin != 3 ) { + astError( AST__BADNI, "astInitSpecMap(SpecMap): Supplied number of " + "SpecMap axes (%d) is illegal; it should be 1 or 2. ", status, + nin ); + } + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSpecMapVtab( vtab, name ); + +/* Initialise a 1D Mapping structure (the parent class) as the first component + within the SpecMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstSpecMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nin, 1, 1 ); + + if ( astOK ) { + +/* Initialise the SpecMap data. */ +/* --------------------------- */ +/* The initial state is with no conversions set, in which condition the + SpecMap simply implements a unit mapping. */ + new->ncvt = 0; + new->cvtargs = NULL; + new->cvttype = NULL; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSpecMap *astLoadSpecMap_( void *mem, size_t size, + AstSpecMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSpecMap + +* Purpose: +* Load a SpecMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "specmap.h" +* AstSpecMap *astLoadSpecMap( void *mem, size_t size, +* AstSpecMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SpecMap loader. + +* Description: +* This function is provided to load a new SpecMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SpecMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a SpecMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the SpecMap is to be +* loaded. This must be of sufficient size to accommodate the +* SpecMap data (sizeof(SpecMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SpecMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SpecMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSpecMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SpecMap. If this is NULL, a pointer to +* the (static) virtual function table for the SpecMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SpecMap" is used instead. + +* Returned Value: +* A pointer to the new SpecMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstSpecMap *new; /* Pointer to the new SpecMap */ + char *sval; /* Pointer to string value */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + int argdec; /* Index of DEC argument */ + int argra; /* Index of RA argument */ + int iarg; /* Loop counter for arguments */ + int icvt; /* Loop counter for conversion steps */ + int nargs; /* Number of user-supplied arguments */ + int szargs; /* Number of stored arguments */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SpecMap. In this case the + SpecMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSpecMap ); + vtab = &class_vtab; + name = "SpecMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSpecMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SpecMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SpecMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Number of conversion steps. */ +/* --------------------------- */ +/* Read the number of conversion steps and allocate memory to hold + data for each step. */ + new->ncvt = astReadInt( channel, "nspec", 0 ); + if ( new->ncvt < 0 ) new->ncvt = 0; + new->cvttype = astMalloc( sizeof( int ) * (size_t) new->ncvt ); + new->cvtargs = astMalloc( sizeof( double * ) * (size_t) new->ncvt ); + +/* If an error occurred, ensure that all allocated memory is freed. */ + if ( !astOK ) { + new->cvttype = astFree( new->cvttype ); + new->cvtargs = astFree( new->cvtargs ); + +/* Otherwise, initialise the argument pointer array. */ + } else { + for ( icvt = 0; icvt < new->ncvt; icvt++ ) { + new->cvtargs[ icvt ] = NULL; + } + +/* Read in data for each conversion step... */ + for ( icvt = 0; icvt < new->ncvt; icvt++ ) { + +/* Conversion type. */ +/* ---------------- */ +/* Create an appropriate keyword and read the string representation of + the conversion type. */ + (void) sprintf( key, "spec%d", icvt + 1 ); + sval = astReadString( channel, key, NULL ); + +/* If no value was read, report an error. */ + if ( astOK ) { + if ( !sval ) { + astError( AST__BADIN, + "astRead(%s): A spectral coordinate conversion " + "type is missing from the input SpecMap data.", status, + astGetClass( channel ) ); + +/* Otherwise, convert the string representation into the required + conversion type code. */ + } else { + new->cvttype[ icvt ] = CvtCode( sval, status ); + +/* If the string was not recognised, report an error. */ + if ( new->cvttype[ icvt ] == AST__SPEC_NULL ) { + astError( AST__BADIN, + "astRead(%s): Invalid spectral conversion " + "type \"%s\" in SpecMap data.", status, + astGetClass( channel ), sval ); + } + } + +/* Free the memory holding the string value. */ + sval = astFree( sval ); + } + +/* Obtain the number of arguments associated with the conversion and + allocate memory to hold them. */ + (void) CvtString( new->cvttype[ icvt ], &comment, &argra, + &argdec, &nargs, &szargs, argdesc, status ); + new->cvtargs[ icvt ] = astMalloc( sizeof( double ) * + (size_t) szargs ); + +/* Read in data for each argument... */ + if ( astOK ) { + for ( iarg = 0; iarg < szargs; iarg++ ) { + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and read each argument value. */ + (void) sprintf( key, "spec%d%c", icvt + 1, ALPHABET[ iarg ] ); + new->cvtargs[ icvt ][ iarg ] = astReadDouble( channel, key, + AST__BAD ); + } + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + +/* If an error occurred, clean up by deleting the new SpecMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SpecMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astSpecAdd_( AstSpecMap *this, const char *cvt, const double args[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,SpecMap,SpecAdd))( this, cvt, args, status ); +} + + + + diff --git a/specmap.h b/specmap.h new file mode 100644 index 0000000..ac2319a --- /dev/null +++ b/specmap.h @@ -0,0 +1,282 @@ +#if !defined( SPECMAP_INCLUDED ) /* Include this file only once */ +#define SPECMAP_INCLUDED +/* +*+ +* Name: +* specmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SpecMap class. + +* Invocation: +* #include "specmap.h" + +* Description: +* This include file defines the interface to the SpecMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The SpecMap class encapsulates various ecptral coordinate +* conversions. Since, typically, a sequence of these conversions is +* required, a SpecMap can be used to accumulate a series of conversions +* which it then applies in sequence. + +* Inheritance: +* The SpecMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* astTransform +* Use an SpecMap to transform a set of points. + +* Protected: +* astMapMerge +* Simplify a sequence of Mappings containing an SpecMap. + +* New Methods Defined: +* Public: +* astSpecAdd +* Add a coordinate conversion step to an SpecMap. + +* Private: +* None. + +* Other Class Functions: +* Public: +* astIsASpecMap +* Test class membership. +* astSpecMap +* Create an SpecMap. + +* Protected: +* astCheckSpecMap +* Validate class membership. +* astInitSpecMap +* Initialise an SpecMap. +* astLoadSpecMap +* Load an SpecMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstSpecMap +* SpecMap object type. + +* Protected: +* AstSpecMapVtab +* SpecMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 8-NOV-2002 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ------ */ +/* Physical constants taken from Chapter 15 of the "Explanatory Supplement + to the Astronomical Ephemeris". */ +#define AST__C 2.99792458E8 /* Speed of light (metres per second) */ +#define AST__H 6.6260755E-34 /* Plank constant (Joule.seconds) */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* SpecMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstSpecMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int *cvttype; /* Pointer to array of conversion types */ + double **cvtargs; /* Pointer to argument list pointer array */ + int ncvt; /* Number of conversions to perform */ +} AstSpecMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSpecMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* SpecAdd)( AstSpecMap *, const char *, const double[], int * ); +} AstSpecMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstSpecMapGlobals { + AstSpecMapVtab Class_Vtab; + int Class_Init; +} AstSpecMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitSpecMapGlobals_( AstSpecMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SpecMap) /* Check class membership */ +astPROTO_ISA(SpecMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstSpecMap *astSpecMap_( int, int, const char *, int *, ...); +#else +AstSpecMap *astSpecMapId_( int, int, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSpecMap *astInitSpecMap_( void *, size_t, int, AstSpecMapVtab *, + const char *, int, int, int * ); + +/* Vtab initialiser. */ +void astInitSpecMapVtab_( AstSpecMapVtab *, const char *, int * ); + +/* Loader. */ +AstSpecMap *astLoadSpecMap_( void *, size_t, AstSpecMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astSpecAdd_( AstSpecMap *, const char *, const double[], int * ); + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSpecMap(this) astINVOKE_CHECK(SpecMap,this,0) +#define astVerifySpecMap(this) astINVOKE_CHECK(SpecMap,this,1) + +/* Test class membership. */ +#define astIsASpecMap(this) astINVOKE_ISA(SpecMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astSpecMap astINVOKE(F,astSpecMap_) +#else +#define astSpecMap astINVOKE(F,astSpecMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSpecMap(mem,size,init,vtab,name,nin,flags) \ +astINVOKE(O,astInitSpecMap_(mem,size,init,vtab,name,nin,flags,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSpecMapVtab(vtab,name) astINVOKE(V,astInitSpecMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSpecMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSpecMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckSpecMap to validate SpecMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +#define astSpecAdd(this,cvt,args) \ +astINVOKE(V,astSpecAdd_(astCheckSpecMap(this),cvt,args,STATUS_PTR)) + +#endif + + + + + diff --git a/sphmap.c b/sphmap.c new file mode 100644 index 0000000..b73cf7a --- /dev/null +++ b/sphmap.c @@ -0,0 +1,2052 @@ +/* +*class++ +* Name: +* SphMap + +* Purpose: +* Map 3-d Cartesian to 2-d spherical coordinates + +* Constructor Function: +c astSphMap +f AST_SPHMAP + +* Description: +* A SphMap is a Mapping which transforms points from a +* 3-dimensional Cartesian coordinate system into a 2-dimensional +* spherical coordinate system (longitude and latitude on a unit +* sphere centred at the origin). It works by regarding the input +* coordinates as position vectors and finding their intersection +* with the sphere surface. The inverse transformation always +* produces points which are a unit distance from the origin +* (i.e. unit vectors). + +* Inheritance: +* The SphMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* SphMap also has the following attributes: +* +* - UnitRadius: SphMap input vectors lie on a unit sphere? +* - PolarLong: The longitude value to assign to either pole + +* Functions: +c The SphMap class does not define any new functions beyond those +f The SphMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 24-OCT-1996 (DSB): +* Original version. +* 5-MAR-1997 (RFWS): +* Tidied public prologues. +* 24-MAR-1998 (RFWS): +* Override the astMapMerge method. +* 4-SEP-1998 (DSB): +* Added UnitRadius attribute. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitSphMapVtab +* method. +* 11-JUN-2003 (DSB): +* Added PolarLong attribute. +* 10-MAY-2006 (DSB): +* Override astEqual. +* 5-NOV-2013 (DSB): +* Modify MapMerge so that it can spot and simplify an +* (inverted SphMap,MatrixMap,SphMap) sequence in which the +* MatrixMap just magnifies or reflects the radius vector. +* 25-MAR-2014 (DSB): +* Correct 5-NOV-2013 MapMerge change. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SphMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "unitmap.h" /* Unit (identity) Mappings */ +#include "sphmap.h" /* Interface definition for this class */ +#include "pal.h" /* SLA transformations */ +#include "wcsmap.h" /* For the AST__DPIBY2 (etc) constants */ +#include "matrixmap.h" /* Matrix mappings */ +#include "winmap.h" /* Shift and scale mappings */ +#include "zoommap.h" /* Scale mappings */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SphMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SphMap,Class_Init) +#define class_vtab astGLOBAL(SphMap,Class_Vtab) +#define getattrib_buff astGLOBAL(SphMap,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSphMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSphMap *astSphMapId_( const char *, ...); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static int GetUnitRadius( AstSphMap *, int * ); +static int TestUnitRadius( AstSphMap *, int * ); +static void ClearUnitRadius( AstSphMap *, int * ); +static void SetUnitRadius( AstSphMap *, int, int * ); + +static double GetPolarLong( AstSphMap *, int * ); +static int TestPolarLong( AstSphMap *, int * ); +static void ClearPolarLong( AstSphMap *, int * ); +static void SetPolarLong( AstSphMap *, double, int * ); + +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +/* Member functions. */ +/* ================= */ +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a SphMap. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status, int *status ) + +* Class Membership: +* SphMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* SphMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the SphMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSphMap *this; /* Pointer to the SphMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SphMap structure. */ + this = (AstSphMap *) this_object; + +/* UnitRadius */ +/* ---------- */ + if ( !strcmp( attrib, "unitradius" ) ) { + astClearUnitRadius( this ); + +/* PolarLong */ +/* --------- */ + } else if ( !strcmp( attrib, "polarlong" ) ) { + astClearPolarLong( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two SphMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* int Equal( AstObject *this, AstObject *that, int *status, int *status ) + +* Class Membership: +* SphMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two SphMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a SphMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the SphMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSphMap *that; + AstSphMap *this; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two SphMap structures. */ + this = (AstSphMap *) this_object; + that = (AstSphMap *) that_object; + +/* Check the second object is a SphMap. We know the first is a + SphMap since we have arrived at this implementation of the virtual + function. */ + if( astIsASphMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two SphMaps differ, it may still be possible + for them to be equivalent. First compare the SphMaps if their Invert + flags are the same. In this case all the attributes of the two SphMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + + if( astEQUAL( this->polarlong, that->polarlong ) && + this->unitradius == that->unitradius ){ + result = 1; + } + +/* If the Invert flags for the two SphMaps differ, the attributes of the two + SphMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a SphMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a SphMap. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status, int *status ) + +* Class Membership: +* SphMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a SphMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the SphMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the SphMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the SphMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSphMap *this; /* Pointer to the SphMap structure */ + const char *result; /* Pointer value to return */ + double dval; /* Double precision attribute value */ + int ival; /* Int attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the SphMap structure. */ + this = (AstSphMap *) this_object; + +/* UnitRadius. */ +/* ----------- */ + if ( !strcmp( attrib, "unitradius" ) ) { + ival = astGetUnitRadius( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* PolarLong */ +/* --------- */ + } else if ( !strcmp( attrib, "polarlong" ) ) { + dval = astGetPolarLong( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +void astInitSphMapVtab_( AstSphMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSphMapVtab + +* Purpose: +* Initialise a virtual function table for a SphMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "sphmap.h" +* void astInitSphMapVtab( AstSphMapVtab *vtab, const char *name ) + +* Class Membership: +* SphMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SphMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASphMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->ClearUnitRadius = ClearUnitRadius; + vtab->SetUnitRadius = SetUnitRadius; + vtab->GetUnitRadius = GetUnitRadius; + vtab->TestUnitRadius = TestUnitRadius; + + vtab->ClearPolarLong = ClearPolarLong; + vtab->SetPolarLong = SetPolarLong; + vtab->GetPolarLong = GetPolarLong; + vtab->TestPolarLong = TestPolarLong; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the class dump, copy and delete functions.*/ + astSetDump( vtab, Dump, "SphMap", "Cartesian to Spherical mapping" ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + astSetDelete( (AstObjectVtab *) vtab, Delete ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a SphMap. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status, int *status ) + +* Class Membership: +* SphMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated SphMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated SphMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated SphMap which is to be merged with +* its neighbours. This should be a cloned copy of the SphMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* SphMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated SphMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *new; /* Pointer to replacement Mapping */ + AstMatrixMap *mm; /* Pointer to MatrixMap */ + AstWinMap *wm; /* The new WinMap */ + const char *class; /* Pointer to Mapping class string */ + double absval; /* Absolute value fo each diagonal element */ + double diag[ 3 ]; /* The diagonal matrix elements */ + double polarlong; /* Value of PolarLong attribute */ + int imap1; /* Index of first SphMap */ + int imap2; /* Index of second SphMap */ + int imap; /* Loop counter for Mappings */ + int result; /* Result value to return */ + int simpler; /* Mappings simplified? */ + +/* Initialise the returned result. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Further initialisation. */ + new = NULL; + simpler = 0; + +/* We will only handle the case of SphMaps in series and will consider + merging the nominated SphMap with the Mapping which follows + it. Check that there is such a Mapping. */ + if ( series && ( ( where + 1 ) < *nmap ) ) { + +/* Obtain the indices of the two potential SphMaps to be merged. */ + imap1 = where; + imap2 = where + 1; + +/* Obtain the Class string of the second Mapping and determine if it + is a SphMap. */ + class = astGetClass( ( *map_list )[ imap2 ] ); + if ( astOK && !strcmp( class, "SphMap" ) ) { + +/* Check if the first SphMap is applied in the inverse direction and + the second in the forward direction. This combination can be + simplified if the PolarLongitude attributes are equal.. */ + if( ( *invert_list )[ imap1 ] && !( *invert_list )[ imap2 ] ) { + simpler = astEQUAL( astGetPolarLong( ( *map_list )[ imap1 ] ), + astGetPolarLong( ( *map_list )[ imap2 ] ) ); + +/* If the first SphMap is applied in the forward direction and the second in + the inverse direction, the combination can only be simplified if the + input vectors to the first SphMap all have unit length (as indicated by + the UnitRadius attribute). */ + } else if( !( *invert_list )[ imap1 ] && ( *invert_list )[ imap2 ] ) { + simpler = astGetUnitRadius( ( *map_list )[ imap1 ] ); + } + } + +/* If the two SphMaps can be simplified, create a UnitMap to replace + them. */ + if ( simpler ) { + new = (AstMapping *) astUnitMap( 2, "", status ); + +/* Annul the pointers to the SphMaps. */ + if ( astOK ) { + ( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] ); + ( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] ); + +/* Insert the pointer to the replacement Mapping and initialise its + invert flag. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Loop to close the resulting gap by moving subsequent elements down + in the arrays. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - 1 ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - 1 ] = ( *invert_list )[ imap ]; + } + +/* Clear the vacated elements at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = imap1; + } + } + } + +/* Another possible simplification is if the nominated Mapping is an inverted + SphMap followed in series by a ZoomMap or diagonal MatrixMap that has + diagonal elements of equal magnitude, which is then followed by a + non-inverted SphMap. This is equivalent to a 3D rotation of a pair of + (longitude,latitude) angles. The MatrixMap/ZoomMap may magnify the + radius vector, but this will not alter the angles. Any difference in + signs amongst the diagonal elements will cause a reflection or reversal + of the corresponbding angles, which can be represented by a WinMap. We + do not need to consider the other possibility (that the nominated + SphMap is the *last* Mapping in such a sequence of three), since we + will already have discovered such a sequence on an earlier invocation + of this function. */ + if( series && !simpler && ( *invert_list )[ where ] && + where + 2 < *nmap ) { + +/* Check the third Mapping is a non-inverted SphMap. */ + class = astGetClass( ( *map_list )[ where + 2 ] ); + if( astOK && !strcmp( class, "SphMap" ) && + !( *invert_list )[ where + 2 ] ) { + +/* Check the second Mapping is a ZoomMap, or a diagonal MatrixMap that + has diagonal elements of equal magnitude. Since the Mapping is + sandwiched between the two SphMaps, we know it must have 3 inputs and + 3 outputs. Record the corresponding diagonal values. The state of the + Invert flag does not matter since it will only affect the degree to + which the radius vector is magnified - it will not change the signs of + any diagonal elements. */ + class = astGetClass( ( *map_list )[ where + 1 ] ); + if( astOK && !strcmp( class, "ZoomMap" ) ) { + diag[ 0 ] = astGetZoom( ( *map_list )[ where + 1 ] ); + if( diag[ 0 ] != 0.0 ) { + diag[ 1 ] = diag[ 0 ]; + diag[ 2 ] = diag[ 0 ]; + } else { + class = NULL; + } + + } else if( astOK && !strcmp( class, "MatrixMap" ) ) { + mm = (AstMatrixMap *) ( *map_list )[ where + 1 ]; + if( mm->form == 1 && mm->f_matrix ) { + diag[ 0 ] = mm->f_matrix[ 0 ]; + if( diag[ 0 ] != 0.0 ) { + diag[ 1 ] = mm->f_matrix[ 1 ]; + diag[ 2 ] = mm->f_matrix[ 2 ]; + + absval = fabs( diag[ 0 ] ); + if( !astEQUAL( fabs( diag[ 1 ] ), absval ) || + !astEQUAL( fabs( diag[ 2 ] ), absval ) ) { + class = NULL; + } + + } else { + class = NULL; + } + + } else { + class = NULL; + } + + } else { + class = NULL; + } + + } else { + class = NULL; + } + +/* We can only make changes if above conditions were met. */ + if( class ) { + +/* Create a WinMap that modifies the (longitude,latitude) values, initially + with undefined corners. */ + wm = astWinMap( 2, NULL, NULL, NULL, NULL, "", status ); + +/* Store appropriate scales and offsets in the WinMap. These just depend on + the signs of the matrix diagonal elements since we know the magnitudes of + these elements are all equal. */ + if( diag[ 0 ] < 0.0 ) { + if( diag[ 1 ] < 0.0 ) { + wm->a[ 0 ] = AST__DPI; + wm->b[ 0 ] = 1.0; + } else { + wm->a[ 0 ] = AST__DPI; + wm->b[ 0 ] = -1.0; + } + + } else { + if( diag[ 1 ] < 0.0 ) { + wm->a[ 0 ] = 0.0; + wm->b[ 0 ] = -1.0; + } else { + wm->a[ 0 ] = 0.0; + wm->b[ 0 ] = 1.0; + } + } + + if( diag[ 2 ] < 0.0 ) { + wm->a[ 1 ] = 0.0; + wm->b[ 1 ] = -1.0; + } else { + wm->a[ 1 ] = 0.0; + wm->b[ 1 ] = 1.0; + } + +/* We are aiming to replace the supplied (SphMap,MatrixMap,SphMap) + combination with (WinMap,SphMap,SphMap), leaving us with an inverted + and non-inverted SphMap side by side. This is on the understanding + that a subsequent call to this function will combine these two + adjacent SphMaps into a UnitMap. But this will only happen if the + adjacent SphMaps have equal values for their PolarLong attributes. The + change of (SphMap,MatrixMap) to (WinMap,SphMap) will change the value + of the PolarLong attribute in the first SphMap, so we need to work out + this changed value and check that it is the same as the PolarLong + value of the second SphMap. If they are different, there is no point + making any changes since the two SphMaps cannot be merged into a + UnitMap. So get the PolarLong value from the supplied first SphMap. */ + polarlong = astGetPolarLong( ( *map_list )[ where ] ); + +/* Modified the PolarLong value to take account of the change from + (SphMap,MatrixMap) to (WinMap,SphMap). */ + polarlong = wm->a[ 0 ] + wm->b[ 0 ]*polarlong; + +/* Check this is the same as the PolarLong value in the second SphMap. */ + if( astEQUAL( polarlong, astGetPolarLong( ( *map_list )[ where + 2 ] ) ) ) { + +/* All is good, so we can now change the supplied Mappings list. First + change the PolarLong value in the first SphMap. */ + astSetPolarLong( ( *map_list )[ where ], polarlong ); + +/* Annul The MatrixMap or ZoomMap. */ + (void) astAnnul( ( *map_list )[ where + 1 ] ); + +/* Move the first SphMap to the slot left vacant by the annulled + MatrixMap or ZoomMap. */ + ( *map_list )[ where + 1 ] = ( *map_list )[ where ]; + ( *invert_list )[ where + 1 ] = ( *invert_list )[ where ]; + +/* Store the new WinMap in the place of the SphMap. */ + ( *map_list )[ where ] = astClone( wm ); + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + } + +/* Free resources. */ + wm = astAnnul( wm ); + } + } + +/* If an error occurred, clear the returned result. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a SphMap. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* SphMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a SphMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the SphMap. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstSphMap *this; /* Pointer to the SphMap structure */ + double dval; /* Double precision attribute value */ + int len; /* Length of setting string */ + int ival; /* Int attribute value */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SphMap structure. */ + this = (AstSphMap *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* UnitRadius */ +/* ---------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "unitradius= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetUnitRadius( this, ival ); + +/* PolarLong */ +/* --------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "polarlong= %lf %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetPolarLong( this, dval ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a SphMap. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status, int *status ) + +* Class Membership: +* SphMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a SphMap's attributes. + +* Parameters: +* this +* Pointer to the SphMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSphMap *this; /* Pointer to the SphMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the SphMap structure. */ + this = (AstSphMap *) this_object; + +/* UnitRadius */ +/* ---------- */ + if ( !strcmp( attrib, "unitradius" ) ) { + result = astTestUnitRadius( this ); + +/* PolarLong */ +/* --------- */ + } else if ( !strcmp( attrib, "polarlong" ) ) { + result = astTestPolarLong( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a SphMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status, int *status ) + +* Class Membership: +* SphMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a SphMap and a set of points encapsulated in a +* PointSet and transforms the points from Cartesian coordinates to +* spherical coordinates. + +* Parameters: +* this +* Pointer to the SphMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the SphMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstSphMap *map; /* Pointer to SphMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + double *p0; /* Pointer to x axis value */ + double *p1; /* Pointer to y axis value */ + double *p2; /* Pointer to z axis value */ + double *q0; /* Pointer to longitude value */ + double *q1; /* Pointer to latitude value */ + double mxerr; /* Largest value which is effectively zero */ + double polarlong; /* Longitude at either pole */ + double v[3]; /* Vector for a single point */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SphMap. */ + map = (AstSphMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if( astOK ){ + +/* First deal with forward mappings from Cartesian to Spherical. */ + if( forward ){ + +/* Get the longitude to return at either pole. */ + polarlong = astGetPolarLong( this ); + +/* Store pointers to the input Cartesian axes. */ + p0 = ptr_in[ 0 ]; + p1 = ptr_in[ 1 ]; + p2 = ptr_in[ 2 ]; + +/* Store pointers to the output Spherical axes. */ + q0 = ptr_out[ 0 ]; + q1 = ptr_out[ 1 ]; + +/* Apply the mapping to every point. */ + for( point = 0; point < npoint; point++ ){ + if( *p0 != AST__BAD && *p1 != AST__BAD && *p2 != AST__BAD ){ + v[0] = *p0; + v[1] = *p1; + v[2] = *p2; + +/* At either pole, return the longitude equal to PolarLong attribute. */ + mxerr = fabs( 1000.0*v[ 2 ] )*DBL_EPSILON; + if( fabs( v[ 0 ] ) < mxerr && fabs( v[ 1 ] ) < mxerr ) { + if( v[ 2 ] < 0.0 ) { + *(q0++) = polarlong; + *(q1++) = -AST__DPIBY2; + } else if( v[ 2 ] > 0.0 ) { + *(q0++) = polarlong; + *(q1++) = AST__DPIBY2; + } else { + *(q0++) = AST__BAD; + *(q1++) = AST__BAD; + } + +/* Otherwise use a SLALIB function to do the conversion (SLALIB always + returns zero at either pole which is why we make the above check). */ + } else { + palDcc2s( v, q0++, q1++ ); + } + + } else { + *(q0++) = AST__BAD; + *(q1++) = AST__BAD; + } + p0++; + p1++; + p2++; + } + +/* Now deal with inverse mappings from Spherical to Cartesian. */ + } else { + +/* Store pointers to the input Spherical axes. */ + q0 = ptr_in[ 0 ]; + q1 = ptr_in[ 1 ]; + +/* Store pointers to the output Cartesian axes. */ + p0 = ptr_out[ 0 ]; + p1 = ptr_out[ 1 ]; + p2 = ptr_out[ 2 ]; + +/* Apply the mapping to every point. */ + for( point = 0; point < npoint; point++ ){ + if( *q0 != AST__BAD && *q1 != AST__BAD ){ + palDcs2c( *q0, *q1, v ); + *(p0++) = v[ 0 ]; + *(p1++) = v[ 1 ]; + *(p2++) = v[ 2 ]; + } else { + *(p0++) = AST__BAD; + *(p1++) = AST__BAD; + *(p2++) = AST__BAD; + + } + q0++; + q1++; + } + + } + + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* UnitRadius */ +/* ---------- */ +/* +*att++ +* Name: +* UnitRadius + +* Purpose: +* SphMap input vectors lie on a unit sphere? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This is a boolean attribute which indicates whether the +* 3-dimensional vectors which are supplied as input to a SphMap +* are known to always have unit length, so that they lie on a unit +* sphere centred on the origin. +* +c If this condition is true (indicated by setting UnitRadius +c non-zero), it implies that a CmpMap which is composed of a +c SphMap applied in the forward direction followed by a similar +c SphMap applied in the inverse direction may be simplified +c (e.g. by astSimplify) to become a UnitMap. This is because the +c input and output vectors will both have unit length and will +c therefore have the same coordinate values. +f If this condition is true (indicated by setting UnitRadius +f non-zero), it implies that a CmpMap which is composed of a +f SphMap applied in the forward direction followed by a similar +f SphMap applied in the inverse direction may be simplified +f (e.g. by AST_SIMPLIFY) to become a UnitMap. This is because the +f input and output vectors will both have unit length and will +f therefore have the same coordinate values. +* +* If UnitRadius is zero (the default), then although the output +* vector produced by the CmpMap (above) will still have unit +* length, the input vector may not have. This will, in general, +* change the coordinate values, so it prevents the pair of SphMaps +* being simplified. + +* Notes: +* - This attribute is intended mainly for use when SphMaps are +* involved in a sequence of Mappings which project (e.g.) a +* dataset on to the celestial sphere. By regarding the celestial +* sphere as a unit sphere (and setting UnitRadius to be non-zero) +* it becomes possible to cancel the SphMaps present, along with +* associated sky projections, when two datasets are aligned using +* celestial coordinates. This often considerably improves +* performance. +* - Such a situations often arises when interpreting FITS data and +* is handled automatically by the FitsChan class. +* - The value of the UnitRadius attribute is used only to control +* the simplification of Mappings and has no effect on the value of +* the coordinates transformed by a SphMap. The lengths of the +* input 3-dimensional Cartesian vectors supplied are always +* ignored, even if UnitRadius is non-zero. +* - The value of this attribute may changed only if the SphMap +* has no more than one reference. That is, an error is reported if the +* SphMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* SphMap +* All SphMaps have this attribute. +*att-- +*/ +astMAKE_CLEAR1(SphMap,UnitRadius,unitradius,-1) +astMAKE_GET(SphMap,UnitRadius,int,0,(this->unitradius == -1 ? 0 : this->unitradius)) +astMAKE_SET1(SphMap,UnitRadius,int,unitradius,( value ? 1 : 0 )) +astMAKE_TEST(SphMap,UnitRadius,( this->unitradius != -1 )) + +/* PolarLong */ +/* --------- */ +/* +*att++ +* Name: +* PolarLong + +* Purpose: +* The longitude value to assign to either pole + +* Type: +* Public attribute. + +* Synopsis: +* Double precision. + +* Description: +* This attribute holds the longitude value, in radians, to be +* returned when a Cartesian position corresponding to either the north +* or south pole is transformed into spherical coordinates. The +* default value is zero. +* +* Note, the value of this attribute may changed only if the SphMap +* has no more than one reference. That is, an error is reported if the +* SphMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* SphMap +* All SphMaps have this attribute. +*att-- +*/ +astMAKE_CLEAR1(SphMap,PolarLong,polarlong,AST__BAD) +astMAKE_GET(SphMap,PolarLong,double,0.0,(this->polarlong == AST__BAD ? 0.0 : this->polarlong)) +astMAKE_SET1(SphMap,PolarLong,double,polarlong,value) +astMAKE_TEST(SphMap,PolarLong,( this->polarlong != AST__BAD )) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SphMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status, int *status, int *status ) + +* Description: +* This function implements the copy constructor for SphMap objects. + +* Parameters: +* objin +* Pointer to the SphMap to be copied. +* objout +* Pointer to the SphMap being constructed. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. + +*/ + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SphMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status, int *status ) + +* Description: +* This function implements the destructor for SphMap objects. + +* Parameters: +* obj +* Pointer to the SphMap to be deleted. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This destructor does nothing and exists only to maintain a +* one-to-one correspondence between destructors and copy +* constructors. +*/ + + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SphMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status, int *status, int *status, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SphMap class to an output Channel. + +* Parameters: +* this +* Pointer to the SphMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSphMap *this; /* Pointer to the SphMap structure */ + double dval; /* Double precision attribute value */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SphMap structure. */ + this = (AstSphMap *) this_object; + +/* Write out values representing the instance variables for the + SphMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* UnitRadius. */ +/* ------- */ + set = TestUnitRadius( this, status ); + ival = set ? GetUnitRadius( this, status ) : astGetUnitRadius( this ); + if( ival ) { + astWriteInt( channel, "UntRd", set, 0, ival, "All input vectors have unit length" ); + } else { + astWriteInt( channel, "UntRd", set, 0, ival, "Input vectors do not all have unit length" ); + } + +/* PolarLong. */ +/* ---------- */ + set = TestPolarLong( this, status ); + dval = set ? GetPolarLong( this, status ) : astGetPolarLong( this ); + astWriteDouble( channel, "PlrLg", set, 1, dval, "Polar longitude (rad.s)" ); + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASphMap and astCheckSphMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SphMap,Mapping) +astMAKE_CHECK(SphMap) + +AstSphMap *astSphMap_( const char *options, int *status, ...) { +/* +*++ +* Name: +c astSphMap +f AST_SPHMAP + +* Purpose: +* Create a SphMap. + +* Type: +* Public function. + +* Synopsis: +c #include "sphmap.h" +c AstSphMap *astSphMap( const char *options, ... ) +f RESULT = AST_SPHMAP( OPTIONS, STATUS ) + +* Class Membership: +* SphMap constructor. + +* Description: +* This function creates a new SphMap and optionally initialises +* its attributes. +* +* A SphMap is a Mapping which transforms points from a +* 3-dimensional Cartesian coordinate system into a 2-dimensional +* spherical coordinate system (longitude and latitude on a unit +* sphere centred at the origin). It works by regarding the input +* coordinates as position vectors and finding their intersection +* with the sphere surface. The inverse transformation always +* produces points which are a unit distance from the origin +* (i.e. unit vectors). + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SphMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SphMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSphMap() +f AST_SPHMAP = INTEGER +* A pointer to the new SphMap. + +* Notes: +* - The spherical coordinates are longitude (positive +* anti-clockwise looking from the positive latitude pole) and +* latitude. The Cartesian coordinates are right-handed, with the x +* axis (axis 1) at zero longitude and latitude, and the z axis +* (axis 3) at the positive latitude pole. +* - At either pole, the longitude is set to the value of the +* PolarLong attribute. +* - If the Cartesian coordinates are all zero, then the longitude +* and latitude are set to the value AST__BAD. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSphMap *new; /* Pointer to new SphMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SphMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSphMap( NULL, sizeof( AstSphMap ), !class_init, &class_vtab, + "SphMap" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SphMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new SphMap. */ + return new; +} + +AstSphMap *astSphMapId_( const char *options, ...) { +/* +* Name: +* astSphMapId_ + +* Purpose: +* Create a SphMap. + +* Type: +* Private function. + +* Synopsis: +* #include "sphmap.h" +* AstSphMap *astSphMapId_( const char *options, ... ) + +* Class Membership: +* SphMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astSphMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astSphMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astSphMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astSphMap_. + +* Returned Value: +* The ID value associated with the new SphMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSphMap *new; /* Pointer to new SphMap */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the SphMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSphMap( NULL, sizeof( AstSphMap ), !class_init, &class_vtab, + "SphMap" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new SphMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new SphMap. */ + return astMakeId( new ); +} + +AstSphMap *astInitSphMap_( void *mem, size_t size, int init, + AstSphMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSphMap + +* Purpose: +* Initialise a SphMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "sphmap.h" +* AstSphMap *astInitSphMap( void *mem, size_t size, int init, +* AstSphMapVtab *vtab, const char *name ) + +* Class Membership: +* SphMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SphMap object. It allocates memory (if necessary) to accommodate +* the SphMap plus any additional data associated with the derived class. +* It then initialises a SphMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a SphMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SphMap is to be initialised. +* This must be of sufficient size to accommodate the SphMap data +* (sizeof(SphMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the SphMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the SphMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the SphMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SphMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). + +* Returned Value: +* A pointer to the new SphMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSphMap *new; /* Pointer to new SphMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSphMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Mapping structure (the parent class) as the first component + within the SphMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstSphMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 3, 2, 1, 1 ); + + if ( astOK ) { + +/* Initialise the SphMap data. */ +/* --------------------------- */ +/* Are all input vectors of unit length? Store a value of -1 to indicate that + no value has yet been set. This will cause a default value of 0 (no, i.e. + input vectors are not all of unit length) to be used. */ + new->unitradius = -1; + new->polarlong = AST__BAD; + + } + +/* Return a pointer to the new SphMap. */ + return new; +} + +AstSphMap *astLoadSphMap_( void *mem, size_t size, + AstSphMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSphMap + +* Purpose: +* Load a SphMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "sphmap.h" +* AstSphMap *astLoadSphMap( void *mem, size_t size, +* AstSphMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SphMap loader. + +* Description: +* This function is provided to load a new SphMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SphMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a SphMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the SphMap is to be +* loaded. This must be of sufficient size to accommodate the +* SphMap data (sizeof(SphMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SphMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SphMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSphMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SphMap. If this is NULL, a pointer +* to the (static) virtual function table for the SphMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SphMap" is used instead. + +* Returned Value: +* A pointer to the new SphMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSphMap *new; /* Pointer to the new SphMap */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SphMap. In this case the + SphMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSphMap ); + vtab = &class_vtab; + name = "SphMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSphMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SphMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SphMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* UnitRadius. */ +/* ----------- */ + new->unitradius = astReadInt( channel, "untrd", -1 ); + if ( TestUnitRadius( new, status ) ) SetUnitRadius( new, new->unitradius, status ); + +/* PolarLong. */ +/* ---------- */ + new->polarlong = astReadDouble( channel, "plrlg", AST__BAD ); + if ( TestPolarLong( new, status ) ) SetPolarLong( new, new->polarlong, status ); + + } + +/* If an error occurred, clean up by deleting the new SphMap. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the new SphMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + diff --git a/sphmap.h b/sphmap.h new file mode 100644 index 0000000..905703b --- /dev/null +++ b/sphmap.h @@ -0,0 +1,374 @@ +#if !defined( SPHMAP_INCLUDED ) /* Include this file only once */ +#define SPHMAP_INCLUDED +/* +*+ +* Name: +* sphmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SphMap class. + +* Invocation: +* #include "sphmap.h" + +* Description: +* This include file defines the interface to the SphMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The SphMap class implements Mappings which maps positions from +* 3-dimensional Cartesian coordinates into 2-dimensional spherical +* coordinates (i.e. longitude and latitude on a unit sphere). The +* inverse Mapping always produces vectors of unit length. +* +* The spherical coordinates are longitude (positive anti-clockwise +* looking from the positive latitude pole) and latitude. The +* Cartesian coordinates are right-handed, with the x-axis (axis 1) +* at zero longitude and latitude, and the z-axis (axis 3) at the +* positive latitude pole. +* +* At either pole, the longitude is set to the value of the PolarLong +* attribute. If the Cartesian coordinates are all zero, then the +* longitude and latitude values are set to AST__BAD. + +* Inheritance: +* The SphMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* PolarLong (double) +* This attribute holds the longitude value, in radians, to be +* returned when a Cartesian position corresponding to either the north +* or south pole is transformed into spherical coordinates. The +* default value is zero. +* UnitRadius (integer) +* This is a boolean attribute which indicates whether the +* 3-dimensional vectors which are supplied as input to a SphMap +* are known to always have unit length, so that they lie on a +* unit sphere centred on the origin. +* +* If this condition is true (indicated by setting UnitRadius +* non-zero), it implies that a CmpMap which is composed of a +* SphMap applied in the forward direction followed by a similar +* SphMap applied in the inverse direction may be simplified +* (e.g. by astSimplify) to become a UnitMap. This is because +* the input and output vectors will both have unit length and +* will therefore have the same coordinate values. +* +* If UnitRadius is zero (the default), then although the output +* vector produced by the CmpMap (above) will still have unit +* length, the input vector may not have. This will, in general, +* change the coordinate values, so it prevents the pair of +* SphMaps being simplified. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astClearAttrib +* Clear an attribute value for a SphMap. +* astGetAttrib +* Get an attribute value for a SphMap. +* astMapMerge +* Simplify a sequence of Mappings containing a SphMap. +* astSetAttrib +* Set an attribute value for a SphMap. +* astTestAttrib +* Test if an attribute value has been set for a SphMap. +* astTransform +* Apply a SphMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astClearUnitRadius +* Clear the UnitRadius attribute value for a SphMap. +* astGetUnitRadius +* Get the UnitRadius attribute value for a SphMap. +* astSetUnitRadius +* Set the UnitRadius attribute value for a SphMap. +* astTestUnitRadius +* Test if a UnitRadius attribute value has been set for a SphMap. +* astClearPolarLong +* Clear the PolarLong attribute value for a SphMap. +* astGetPolarLong +* Get the PolarLong attribute value for a SphMap. +* astSetPolarLong +* Set the PolarLong attribute value for a SphMap. +* astTestPolarLong +* Test if a PolarLong attribute value has been set for a SphMap. + +* Other Class Functions: +* Public: +* astIsASphMap +* Test class membership. +* astSphMap +* Create a SphMap. +* +* Protected: +* astCheckSphMap +* Validate class membership. +* astInitSphMap +* Initialise a SphMap. +* astInitSphMapVtab +* Initialise the virtual function table for the SphMap class. +* astLoadSphMap +* Load a SphMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstSphMap +* SphMap object type. +* +* Protected: +* AstSphMapVtab +* SphMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 25-OCT-1996 (DSB): +* Original version. +* 24-MAR-1998 (RFWS): +* Override the astMapMerge method. +* 4-SEP-1998 (DSB): +* Added UnitRadius attribute. +* 8-JAN-2003 (DSB): +* Added protected astInitSphMapVtab method. +* 11-JUN-2003 (DSB): +* Added PolarLong attribute. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* SphMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstSphMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double polarlong; /* Longitude to assign to either pole */ + int unitradius; /* Are input vectors always of unit length? */ +} AstSphMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSphMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* GetUnitRadius)( AstSphMap *, int * ); + int (* TestUnitRadius)( AstSphMap *, int * ); + void (* ClearUnitRadius)( AstSphMap *, int * ); + void (* SetUnitRadius)( AstSphMap *, int, int * ); + + double (* GetPolarLong)( AstSphMap *, int * ); + int (* TestPolarLong)( AstSphMap *, int * ); + void (* ClearPolarLong)( AstSphMap *, int * ); + void (* SetPolarLong)( AstSphMap *, double, int * ); +} AstSphMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstSphMapGlobals { + AstSphMapVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 51 ]; +} AstSphMapGlobals; +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SphMap) /* Check class membership */ +astPROTO_ISA(SphMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstSphMap *astSphMap_( const char *, int *, ...); +#else +AstSphMap *astSphMapId_( const char *, ...)__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSphMap *astInitSphMap_( void *, size_t, int, AstSphMapVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitSphMapVtab_( AstSphMapVtab *, const char *, int * ); + +/* Loader. */ +AstSphMap *astLoadSphMap_( void *, size_t, AstSphMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitSphMapGlobals_( AstSphMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +int astGetUnitRadius_( AstSphMap *, int * ); +int astTestUnitRadius_( AstSphMap *, int * ); +void astClearUnitRadius_( AstSphMap *, int * ); +void astSetUnitRadius_( AstSphMap *, int, int * ); + +double astGetPolarLong_( AstSphMap *, int * ); +int astTestPolarLong_( AstSphMap *, int * ); +void astClearPolarLong_( AstSphMap *, int * ); +void astSetPolarLong_( AstSphMap *, double, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSphMap(this) astINVOKE_CHECK(SphMap,this,0) +#define astVerifySphMap(this) astINVOKE_CHECK(SphMap,this,1) + +/* Test class membership. */ +#define astIsASphMap(this) astINVOKE_ISA(SphMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astSphMap astINVOKE(F,astSphMap_) +#else +#define astSphMap astINVOKE(F,astSphMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define \ +astInitSphMap(mem,size,init,vtab,name) \ +astINVOKE(O,astInitSphMap_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSphMapVtab(vtab,name) astINVOKE(V,astInitSphMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSphMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSphMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckSphMap to validate SphMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astClearUnitRadius(this) astINVOKE(V,astClearUnitRadius_(astCheckSphMap(this),STATUS_PTR)) +#define astGetUnitRadius(this) astINVOKE(V,astGetUnitRadius_(astCheckSphMap(this),STATUS_PTR)) +#define astSetUnitRadius(this,value) astINVOKE(V,astSetUnitRadius_(astCheckSphMap(this),value,STATUS_PTR)) +#define astTestUnitRadius(this) astINVOKE(V,astTestUnitRadius_(astCheckSphMap(this),STATUS_PTR)) + +#define astClearPolarLong(this) astINVOKE(V,astClearPolarLong_(astCheckSphMap(this),STATUS_PTR)) +#define astGetPolarLong(this) astINVOKE(V,astGetPolarLong_(astCheckSphMap(this),STATUS_PTR)) +#define astSetPolarLong(this,value) astINVOKE(V,astSetPolarLong_(astCheckSphMap(this),value,STATUS_PTR)) +#define astTestPolarLong(this) astINVOKE(V,astTestPolarLong_(astCheckSphMap(this),STATUS_PTR)) +#endif + +#endif + + + + + diff --git a/stc.c b/stc.c new file mode 100644 index 0000000..08e4392 --- /dev/null +++ b/stc.c @@ -0,0 +1,3703 @@ +/* +*class++ +* Name: +* Stc + +* Purpose: +* Represents an instance of the IVOA STC class. + +* Constructor Function: +c astStc +f AST_STC + +* Description: +* The Stc class is an implementation of the IVOA STC class which forms +* part of the IVOA Space-Time Coordinate Metadata system. See: +* +* http://hea-www.harvard.edu/~arots/nvometa/STC.html +* +* The Stc class does not have a constructor function of its own, as it +* is simply a container class for a family of specialised sub-classes +* including StcCatalogEntryLocation, StcResourceProfile, StcSearchLocation +* and StcObsDataLocation. + +* Inheritance: +* The Stc class inherits from the Region class. + +* Attributes: +* In addition to those attributes common to all Regions, every +* Stc also has the following attributes: +* +* - RegionClass: The class name of the encapsulated Region. + +* Functions: +c In addition to those functions applicable to all Regions, the +c following functions may also be applied to all Stc's: +f In addition to those routines applicable to all Regions, the +f following routines may also be applied to all Stc's: +* +c - astGetStcRegion: Get a pointer to the encapsulated Region +f - AST_GETSTCREGION: Get a pointer to the encapsulated Region +c - astGetStcCoord: Get information about an AstroCoords element +f - AST_GETSTCCOORD: Get information about an AstroCoords element +c - astGetStcNCoord: Returns the number of AstroCoords elements in an Stc +f - AST_GETSTCNCOORD: Returns the number of AstroCoords elements in an Stc + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2008 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-NOV-2004 (DSB): +* Original version. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 13-MAR-2009 (DSB): +* Over-ride astRegBasePick. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Stc + +/* The number of components in an AstroCoords element which are described + using a Region within a KeyMap. */ +#define NREG 5 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "unitmap.h" /* Unit Mappings */ +#include "region.h" /* Regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "stc.h" /* Interface definition for this class */ +#include "keymap.h" /* Lists of value/key pairs */ +#include "pointlist.h" /* Individual points in a Frame */ +#include "ellipse.h" /* Ellipses within a Frame */ +#include "interval.h" /* Axis intervals within a Frame */ +#include "prism.h" /* Extrusions into higher dimensions */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *(* parent_getdefunc)( AstRegion *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_getusedefs)( AstObject *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static void (*parent_regclearattrib)( AstRegion *, const char *, char **, int * ); +static void (*parent_regsetattrib)( AstRegion *, const char *, char **, int * ); + +static void (* parent_clearnegated)( AstRegion *, int * ); +static void (* parent_clearclosed)( AstRegion *, int * ); +static void (* parent_clearfillfactor)( AstRegion *, int * ); +static void (* parent_clearmeshsize)( AstRegion *, int * ); + +static void (* parent_setclosed)( AstRegion *, int, int * ); +static void (* parent_setfillfactor)( AstRegion *, double, int * ); +static void (* parent_setmeshsize)( AstRegion *, int, int * ); +static void (* parent_setnegated)( AstRegion *, int, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + +/* The keys associated with each component of an AstroCoords element + within KeyMap */ +static const char *regkey[ NREG ] = { AST__STCERROR, + AST__STCRES, + AST__STCSIZE, + AST__STCPIXSZ, + AST__STCVALUE }; + +/* The comments associated with each component of an AstroCoords element + within KeyMap */ +static const char *regcom[ NREG ] = { "AstroCoords error region", + "AstroCoords resolution region", + "AstroCoords size region", + "AstroCoords pixel size region", + "AstroCoords value region" }; + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Stc) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Stc,Class_Init) +#define class_vtab astGLOBAL(Stc,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstStcVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstKeyMap *GetStcCoord( AstStc *, int, int * ); +static AstKeyMap *MakeAstroCoordsKeyMap( AstRegion *, AstKeyMap *, const char *, int * ); +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *GetDefUnc( AstRegion *, int * ); +static AstRegion *GetStcRegion( AstStc *, int * ); +static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * ); +static const char *GetRegionClass( AstStc *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetBounded( AstRegion *, int * ); +static int GetObjSize( AstObject *, int * ); +static int GetStcNCoord( AstStc *, int * ); +static int GetUseDefs( AstObject *, int * ); +static int Overlap( AstRegion *, AstRegion *, int * ); +static int OverlapX( AstRegion *, AstRegion *, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GetRegion( AstStc *, AstRegion **, int *, int * ); +static void RegBaseBox( AstRegion *, double *, double *, int * ); +static void RegClearAttrib( AstRegion *, const char *, char **, int * ); +static void RegSetAttrib( AstRegion *, const char *, char **, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); + +static void ClearAttrib( AstObject *, const char *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); + +static void ClearClosed( AstRegion *, int * ); +static int GetClosed( AstRegion *, int * ); +static void SetClosed( AstRegion *, int, int * ); +static int TestClosed( AstRegion *, int * ); + +static void ClearMeshSize( AstRegion *, int * ); +static int GetMeshSize( AstRegion *, int * ); +static void SetMeshSize( AstRegion *, int, int * ); +static int TestMeshSize( AstRegion *, int * ); + +static void ClearFillFactor( AstRegion *, int * ); +static double GetFillFactor( AstRegion *, int * ); +static void SetFillFactor( AstRegion *, double, int * ); +static int TestFillFactor( AstRegion *, int * ); + +static void ClearNegated( AstRegion *, int * ); +static int GetNegated( AstRegion *, int * ); +static void SetNegated( AstRegion *, int, int * ); +static int TestNegated( AstRegion *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Stc. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Stc member function (over-rides the astClearAttrib protected +* method inherited from the Region class). + +* Description: +* This function clears the value of a specified attribute for a +* Stc, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Stc. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to the Stc structure */ + int len; /* Length of attrib string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Stc structure. */ + this = (AstStc *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* (none as yet) */ +/* ------------- */ + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute name matches any of the read-only attributes + of this class. If it does, then report an error. */ + if ( !strcmp( attrib, "regionclass" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Objects are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int Equal( AstObject *this_object, AstObject *that_object, int *status ) + +* Class Membership: +* Stc member function (over-rides the astEqual protected +* method inherited from the Region class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two Stcs are equivalent. + +* Parameters: +* this +* Pointer to the first Stc. +* that +* Pointer to the second Stc. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the Stcs are equivalent, zero otherwise. + +* Notes: +* - The Stcs are equivalent if their encapsulated Region are +* equivalent, and if they have the same boolean operation, negation +* and closed flags. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstStc *that; + AstStc *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent Region class. This checks + that the Objects are both of the same class, and have the same Negated + and Closed flags (amongst other things). */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Obtain pointers to the two Stc structures. */ + this = (AstStc *) this_object; + that = (AstStc *) that_object; + +/* Test their encapsulated Region for equality. */ + result = astEqual( this->region, that->region ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Define a function to set an attribute value for a Stc. + +* Type: +* Private macro. + +* Synopsis: +* #include "stc.h" +* MAKE_SET(attribute,lattribute,type) + +* Class Membership: +* Defined by the Stc class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set( AstRegion *this, value ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the encapsulated Region. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,lattribute,type) \ +static void Set##attribute( AstRegion *this_region, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstStc *this; /* Pointer to the Stc structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to set the value in the parent Region structure. */ \ + (*parent_set##lattribute)( this_region, value, status ); \ +\ +/* Also set the value in the encapsulated Region. */ \ + this = (AstStc *) this_region; \ + astSet##attribute( this->region, value ); \ +} + +/* Use the above macro to create accessors for the MeshSize, Closed and + FillFactor attributes. */ +MAKE_SET(FillFactor,fillfactor,double) +MAKE_SET(MeshSize,meshsize,int) +MAKE_SET(Closed,closed,int) +MAKE_SET(Negated,negated,int) + +/* Undefine the macro. */ +#undef MAKE_SET + +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Define a function to clear an attribute value for a Stc. + +* Type: +* Private macro. + +* Synopsis: +* #include "stc.h" +* MAKE_CLEAR(attribute,lattribute) + +* Class Membership: +* Defined by the Stc class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear( AstRegion *this ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the encapsulated Region. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute,lattribute) \ +static void Clear##attribute( AstRegion *this_region, int *status ) { \ +\ +/* Local Variables: */ \ + AstStc *this; /* Pointer to the Stc structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to clear the value in the parent Region structure. */ \ + (*parent_clear##lattribute)( this_region, status ); \ +\ +/* Also clear the value in the encapsulated Region. */ \ + this = (AstStc *) this_region; \ + astClear##attribute( this->region ); \ +} + +/* Use the above macro to create accessors for the MeshSize, Closed and + FillFactor attributes. */ +MAKE_CLEAR(FillFactor,fillfactor) +MAKE_CLEAR(MeshSize,meshsize) +MAKE_CLEAR(Closed,closed) +MAKE_CLEAR(Negated,negated) + +/* Undefine the macro. */ +#undef MAKE_CLEAR + + +/* +* Name: +* MAKE_GET + +* Purpose: +* Define a function to get an attribute value for a Stc. + +* Type: +* Private macro. + +* Synopsis: +* #include "stc.h" +* MAKE_GET(attribute,type,bad) + +* Class Membership: +* Defined by the Stc class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static Get( AstRegion *this ) +* +* that gets the value of a specified Region attribute from the encapsulated +* Region. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +* bad +* Value to return in caseof error. +*/ + +/* Define the macro. */ +#define MAKE_GET(attribute,type,bad) \ +static type Get##attribute( AstRegion *this_region, int *status ) { \ +\ +/* Local Variables: */ \ + AstStc *this; /* Pointer to the Stc structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad); \ +\ +/* Get the value from the encapsulated Region. */ \ + this = (AstStc *) this_region; \ + return astGet##attribute( this->region ); \ +} + +/* Use the above macro to create accessors for the MeshSize, Closed and + FillFactor attributes. */ +MAKE_GET(FillFactor,double,AST__BAD) +MAKE_GET(MeshSize,int,100) +MAKE_GET(Closed,int,1) +MAKE_GET(Negated,int,0) + +/* Undefine the macro. */ +#undef MAKE_GET + +/* +* Name: +* MAKE_TEST + +* Purpose: +* Define a function to test an attribute value for a Stc. + +* Type: +* Private macro. + +* Synopsis: +* #include "stc.h" +* MAKE_TEST(attribute) + +* Class Membership: +* Defined by the Stc class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static int Test( AstRegion *this ) +* +* that test the value of a specified Region attribute from the encapsulated +* Region. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_TEST(attribute) \ +static int Test##attribute( AstRegion *this_region, int *status ) { \ +\ +/* Local Variables: */ \ + AstStc *this; /* Pointer to the Stc structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Test the value from the encapsulated Region. */ \ + this = (AstStc *) this_region; \ + return astTest##attribute( this->region ); \ +} + +/* Use the above macro to create accessors for the MeshSize, Closed and + FillFactor attributes. */ +MAKE_TEST(FillFactor) +MAKE_TEST(MeshSize) +MAKE_TEST(Closed) +MAKE_TEST(Negated) + +/* Undefine the macro. */ +#undef MAKE_TEST + + + + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Stc member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Stc, +* in bytes. + +* Parameters: +* this +* Pointer to the Stc. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to Stc structure */ + int result; /* Result value to return */ + int i; /* AstroCoords index */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the Stc structure. */ + this = (AstStc *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astGetObjSize( this->region ); + + if( this->coord ) { + for( i = 0; i < this->ncoord; i++ ) { + result += astGetObjSize( this->coord[ i ] ); + } + result += astTSizeOf( this->coord ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Stc. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Stc member function (over-rides the protected astGetAttrib +* method inherited from the Region class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Stc, formatted as a character string. + +* Parameters: +* this +* Pointer to the Stc. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Stc, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Stc. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to the Stc structure */ + const char *result; /* Pointer value to return */ + int len; /* Length of attrib string */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Stc structure. */ + this = (AstStc *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* RegionClass. */ +/* ------------ */ + if ( !strcmp( attrib, "regionclass" ) ) { + result = astGetClass( this->region ); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetBounded( AstRegion *this_region, int *status ) { +/* +* Name: +* GetBounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int GetBounded( AstRegion *this, int *status ) + +* Class Membership: +* Stc method (over-rides the astGetBounded method inherited from +* the Region class). + +* Description: +* This function returns a flag indicating if the Region is bounded. +* The implementation provided by the base Region class is suitable +* for Region sub-classes representing the inside of a single closed +* curve (e.g. Circle, Ellipse, Box, etc). Other sub-classes (such as +* Stc, PointList, etc ) may need to provide their own implementations. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Region is bounded. Zero otherwise. + +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to Stc structure */ + AstRegion *reg; /* Pointer to the encapsulated Region */ + int neg; /* Negated flag to use */ + int neg_old; /* Original Negated flag */ + int result; /* Returned result */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Stc structure. */ + this = (AstStc *) this_region; + +/* Get the encapsulated Region, and the Negated value which should be used + with it. The returned values take account of whether the supplied Stc has + itself been Negated or not. The returned Region represent a region within + the base Frame of the FrameSet encapsulated by the parent Region + structure. */ + GetRegion( this, ®, &neg, status ); + +/* Temporarily set the Negated attribute to the required value.*/ + neg_old = astGetNegated( reg ); + astSetNegated( reg, neg ); + +/* See if the encapsulated Region is bounded. */ + result = astGetBounded( reg ); + +/* Re-instate the original value for the Negated attribute of the + encapsulated Region. */ + if( reg ) astSetNegated( reg, neg_old ); + +/* Free resources. */ + reg = astAnnul( reg ); + +/* Return zero if an error occurred. */ + if( !astOK ) result = 0; + +/* Return the required pointer. */ + return result; +} + +static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) { +/* +* Name: +* GetDefUnc + +* Purpose: +* Obtain a pointer to the default uncertainty Region for a given Region. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* AstRegion *GetDefUnc( AstRegion *this ) + +* Class Membership: +* Stc method (over-rides the astGetDefUnc method inherited from +* the Region class). + +* This function returns a pointer to a Region which represents the +* default uncertainty associated with a position on the boundary of the +* given Region. The returned Region refers to the base Frame within the +* FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to the Stc structure */ + AstRegion *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Stc structure. */ + this = (AstStc *) this_region; + +/* If the encapsulated region has non-default uncertainty, use it as + the default uncertainty for the Cmpregion. Note, the current Frame of + an uncertainty Region is assumed to be the same as the base Frame in the + Stc. */ + if( astTestUnc( this->region ) ) { + result = astGetUncFrm( this->region, AST__CURRENT ); + +/* Otherwise, use the parent method to determine the default uncertainty. */ + } else { + result = (* parent_getdefunc)( this_region, status ); + } + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +static void GetRegion( AstStc *this, AstRegion **reg, int *neg, int *status ) { +/* +* +* Name: +* GetRegion + +* Purpose: +* Get the encapsulated Region of a Stc. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void GetRegion( AstStc *this, AstRegion **reg, int *neg, int *status ) + +* Class Membership: +* Stc member function + +* Description: +* This function returns a pointer to a Region which is equivalent to +* the supplied Stc. If the Stc has been negated, then the returned +* "negated" flag will be set such that it represents the negated Stc. +* +* The current Frames in returned encapsulated Region will be equivalent +* to the base Frame in the FrameSet encapsulated by the parent Region +* structure. + +* Parameters: +* this +* Pointer to the Stc. +* reg +* Address of a location to receive a pointer to the encapsulated +* Region. The current Frame in this region will be equivalent to +* the base Frame in the FrameSet +* neg +* The value of the Negated attribute to be used with reg. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the encapsulated Region using the returned +* pointer will be reflected in the supplied Stc. + +*/ + +/* Initialise */ + if( reg ) *reg = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Return the component Region pointers. */ + if( reg ) *reg = astClone( this->region ); + +/* Initialise the other returned items. Note, the Stc initialiser stored a + deep copy of the supplied encapsulated Region, and so we do not + need to worry about attributes of the Region having been changed + after the creation of the Stc. This is different to the CmpMap + class which merely clones its supplied component pointers and so has + to save copies of the original Invert settings within the CmpMap + structure. */ + if( neg ) *neg = astGetNegated( this->region ); + +/* If the Stc has been inverted, we modify the boolean operator and + negation flags so that they reflect the inverted Stc. */ + if( astGetNegated( this ) && neg ) *neg = *neg ? 0 : 1; +} + +static const char *GetRegionClass( AstStc *this, int *status ){ +/* +*+ +* Name: +* astGetRegionClass + +* Purpose: +* Get the value of a RegionClass attribute for a Stc. + +* Type: +* Protected function. + +* Synopsis: +* #include "stc.h" +* const char *astGetRegionClass( AstStc *this ) + +* Class Membership: +* Stc virtual function + +* Description: +* This function returns a pointer to the value of the RegionClass +* attribute for a Stc. + +* Parameters: +* this +* Pointer to the Stc. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Stc, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Stc. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain and return the class of the encapsulated Region. */ + return astGetClass( ((AstStc *) this)->region ); +} + + +static AstKeyMap *GetStcCoord( AstStc *this, int icoord, int *status ){ +/* +*++ +* Name: +c astGetStcCoord +f AST_GETSTCCOORD + +* Purpose: +* Return information about an AstroCoords element stored in an Stc. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "specframe.h" +c AstKeyMap *astGetStcCoord( AstStc *this, int icoord ) +f RESULT = AST_GETSTCCOORD( THIS, ICOORD, STATUS ) + +* Class Membership: +* Stc method. + +* Description: +* When any sub-class of Stc is created, the constructor function +* allows one or more AstroCoords elements to be stored within the Stc. +* This function allows any one of these AstroCoords elements to be +* retrieved. The format of the returned information is the same as +* that used to pass the original information to the Stc constructor. +* That is, the information is returned in a KeyMap structure +* containing elements with one or more of the keys given by symbolic +* constants AST__STCNAME, AST__STCVALUE, AST__STCERROR, AST__STCRES, +* AST__STCSIZE and AST__STCPIXSZ. +* +* If the coordinate system represented by the Stc has been changed +* since it was created (for instance, by changing its System +* attribute), then the sizes and positions in the returned KeyMap +* will reflect the change in coordinate system. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Stc. +c icoord +f ICOORD = INTEGER (Given) +* The index of the AstroCoords element required. The first has index +* one. The number of AstroCoords elements in the Stc can be found using +c function astGetStcNcoord. +f function AST_GETSTCNCOORD. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetStcCoord() +f AST_GETSTCCOORD = INTEGER +* A pointer to a new KeyMap containing the required information. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstFrame *frm; + AstKeyMap *result; + AstMapping *map; + AstMapping *smap; + AstObject *obj; + AstRegion *reg; + AstRegion *rereg; + AstRegion *srereg; + int ikey; + int nc; + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the supplied index. */ + nc = astGetStcNCoord( this ); + if( icoord < 1 || icoord > nc ) { + astError( AST__STCIND, "astGetStcCoord(%s): Supplied AstroCoords " + "index (%d) is invalid.", status, astGetClass( this ), icoord ); + + if( icoord < 1 ) { + astError( AST__STCIND, "The index of the first AstroCoord " + "element is one, not zero." , status); + } else if( nc == 0 ) { + astError( AST__STCIND, "There are no AstroCoords elements in " + "the supplied %s.", status, astGetClass( this ) ); + } else if( nc == 1 ) { + astError( AST__STCIND, "There is 1 AstroCoords element in " + "the supplied %s.", status, astGetClass( this ) ); + } else { + astError( AST__STCIND, "There are %d AstroCoords elements in " + "the supplied %s.", status, nc, astGetClass( this ) ); + } + +/* If the index is OK, initialise the returned KeyMap to be a copy of the + KeyMap holding information about the required AstroCoords element.*/ + } else { + result = astCopy( this->coord[ icoord - 1 ] ); + +/* The Regions stored within this KeyMap describe regions within the base + Frame of the parent Region structure. If the Mapping from base to current + Frame in the parent Region structure is not a UnitMap, we need to + change these to represent regions within the current Frame of the + parent Region structure. */ + map = astGetMapping( ((AstRegion *)this)->frameset, + AST__BASE, AST__CURRENT ); + smap = astSimplify( map ); + frm = astGetFrame( ((AstRegion *)this)->frameset, AST__CURRENT ); + +/* If the Frame represented by the Region has changed, erase the Names + element since they may no longer be correct. */ + if( !astIsAUnitMap( smap ) ) astMapRemove( result, AST__STCNAME ); + +/* Loop round keys for which a Region may be stored in the KeyMap. */ + for( ikey = 0; ikey < NREG; ikey++ ) { + +/* If the KeyMap contains a Region for this key, get a pointer to it. */ + if( astMapGet0A( result, regkey[ ikey ], &obj ) ){ + reg = (AstRegion *) obj; + +/* Sets its RegionFS attribute so that the encapsulated FrameSet will be + included in any dump of the Region. This is needed since the returned + Region pointer will have no parent Region from which the FrameSet can + be determined. */ + astSetRegionFS( reg, 1 ); + +/* If necessary, remap the Region into the current Frame, and simplify. */ + if( !astIsAUnitMap( smap ) ) { + rereg = astMapRegion( reg, smap, frm ); + srereg = astSimplify( rereg ); + rereg = astAnnul( rereg ); + } else { + srereg = astClone( reg ); + } + +/* Replace the Region in the KeyMap with the remapped Region. */ + astMapPut0A( result, regkey[ ikey ], srereg, NULL ); + +/* Free resources */ + reg = astAnnul( reg ); + srereg = astAnnul( srereg ); + } + } + + frm = astAnnul( frm ); + map = astAnnul( map ); + smap = astAnnul( smap ); + +/* Annul the returned KeyMap if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + } + +/* Return the pointer */ + return result; + +} + +static int GetStcNCoord( AstStc *this, int *status ){ +/* +*++ +* Name: +c astGetStcNCoord +f AST_GETSTCNCOORD + +* Purpose: +* Return the number of AstroCoords elements stored in an Stc. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "stc.h" +c int astGetStcNCoord( AstStc *this ) +f RESULT = AST_GETSTCNCOORD( THIS, STATUS ) + +* Class Membership: +* Stc method. + +* Description: +* This function returns the number of AstroCoords elements stored in +* an Stc. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Stc. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetStcNCoord() +f AST_GETSTCNCOORD = INTEGER +* The number of AstroCoords elements stored in the Stc. + +* Notes: +* - Zero will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Return the required value. */ + return astOK ? this->ncoord : 0; + +} + +static AstRegion *GetStcRegion( AstStc *this, int *status ) { +/* +*++ +* Name: +c astGetStcRegion +f AST_GETSTCREGION + +* Purpose: +* Obtain a copy of the encapsulated Region within a Stc. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "stc.h" +c AstRegion *astGetStcRegion( AstStc *this ) +f RESULT = AST_GETSTCREGION( THIS, STATUS ) + +* Class Membership: +* Region method. + +* Description: +* This function returns a pointer to a deep copy of the Region +* supplied when the Stc was created. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Stc. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGetStcRegion() +f AST_GETSTCREGION = INTEGER +* A pointer to a deep copy of the Region encapsulated within the +* supplied Stc. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return a pointer to a copy of the encapsulated Region. */ + return astCopy( this->region ); +} + +static int GetUseDefs( AstObject *this_object, int *status ) { +/* +* Name: +* GetUseDefs + +* Purpose: +* Get the value of the UseDefs attribute for a Stc. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int GetUseDefs( AstObject *this_object, int *status ) { + +* Class Membership: +* Stc member function (over-rides the protected astGetUseDefs +* method inherited from the Region class). + +* Description: +* This function returns the value of the UseDefs attribute for a +* Stc, supplying a suitable default. + +* Parameters: +* this +* Pointer to the Stc. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - The USeDefs value. +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to the Stc structure */ + int result; /* Value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Stc structure. */ + this = (AstStc *) this_object; + +/* If the UseDefs value for the Stc has been set explicitly, use the + Get method inherited from the parent Region class to get its value. */ + if( astTestUseDefs( this ) ) { + result = (*parent_getusedefs)( this_object, status ); + +/* Otherwise, supply a default value equal to the UseDefs value of the + encapsulated Region. */ + } else { + result = astGetUseDefs( this->region ); + } + +/* Return the result. */ + return result; +} + +void astInitStcVtab_( AstStcVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitStcVtab + +* Purpose: +* Initialise a virtual function table for a Stc. + +* Type: +* Protected function. + +* Synopsis: +* #include "stc.h" +* void astInitStcVtab( AstStcVtab *vtab, const char *name ) + +* Class Membership: +* Stc vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Stc class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstRegionVtab *region; /* Pointer to Region component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitRegionVtab( (AstRegionVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAStc) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstRegionVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + + vtab->GetRegionClass = GetRegionClass; + vtab->GetStcRegion = GetStcRegion; + vtab->GetStcCoord = GetStcCoord; + vtab->GetStcNCoord = GetStcNCoord; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + region = (AstRegionVtab *) vtab; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_simplify = mapping->Simplify; + mapping->Simplify = Simplify; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_clearclosed = region->ClearClosed; + region->ClearClosed = ClearClosed; + + parent_setclosed = region->SetClosed; + region->SetClosed = SetClosed; + + region->TestClosed = TestClosed; + region->GetClosed = GetClosed; + + parent_regsetattrib = region->RegSetAttrib; + region->RegSetAttrib = RegSetAttrib; + + parent_regclearattrib = region->RegClearAttrib; + region->RegClearAttrib = RegClearAttrib; + + parent_clearnegated = region->ClearNegated; + region->ClearNegated = ClearNegated; + + parent_setnegated = region->SetNegated; + region->SetNegated = SetNegated; + + region->TestNegated = TestNegated; + region->GetNegated = GetNegated; + + parent_setmeshsize = region->SetMeshSize; + region->SetMeshSize = SetMeshSize; + + parent_clearmeshsize = region->ClearMeshSize; + region->ClearMeshSize = ClearMeshSize; + + region->TestMeshSize = TestMeshSize; + region->GetMeshSize = GetMeshSize; + + parent_setfillfactor = region->SetFillFactor; + region->SetFillFactor = SetFillFactor; + + parent_clearfillfactor = region->ClearFillFactor; + region->ClearFillFactor = ClearFillFactor; + + region->TestFillFactor = TestFillFactor; + region->GetFillFactor = GetFillFactor; + + parent_getusedefs = object->GetUseDefs; + object->GetUseDefs = GetUseDefs; + + parent_getdefunc = region->GetDefUnc; + region->GetDefUnc = GetDefUnc; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + region->Overlap = Overlap; + region->OverlapX = OverlapX; + region->RegBaseBox = RegBaseBox; + region->RegBaseMesh = RegBaseMesh; + region->RegBasePick = RegBasePick; + region->RegPins = RegPins; + region->GetBounded = GetBounded; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "Stc", "An IVOA Space-Time-Coords object" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static AstKeyMap *MakeAstroCoordsKeyMap( AstRegion *reg, AstKeyMap *coord, + const char *class, int *status ){ +/* +* Name: +* MakeAstroCoordsKeyMap + +* Purpose: +* Create a new KeyMap holding Regions describing a supplied +* AstroCoords element. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* AstKeyMap *MakeAstroCoordsKeyMap( AstRegion *reg, AstKeyMap *coord, +* const char *class, int *status ) + +* Class Membership: +* Stc member function + +* Description: +* This function returns a pointer to a new KeyMap containing elements +* which correspond to the components of an STC AstroCoords element. +* The element with key AST__STCNAME holds a vector of character +* strings containing the names associated with each of the axies. +* The other elements of the returned KeyMap such as AST__STCERROR, +* AST__STCRES, etc, hold pointers to Regions describing the error +* box, resolution, etc, in the Frame of the supplied Region "reg". + +* Parameters: +* reg +* Pointer to the Region in which the AstroCoords is defined. +* coordId +* An ID (not a pointer) to a KeyMap defining a single +* element, having elements with keys given by constants AST__STCNAME, +* AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. Any of these elements may be omitted, but no other +* elements should be included. If supplied, the AST__STCNAME element +* should be a vector of character string pointers holding the "Name" +* item for each axis. Any other supplied elements should be scalar +* elements, each holding a pointer to a Region describing the +* associated item of ancillary information (error, resolution, size, +* pixel size or value). These Regions should refer to the coordinate +* system represented by "region". +* class +* Pointer to a string holding the STC class name. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to the new KeyMap. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Pointer to current Frame */ + AstFrameSet *fs; /* Pointer to conversion FrameSet */ + AstKeyMap *result; /* Pointer value to return */ + AstMapping *map; /* Pointer to conversion Mapping */ + AstObject *obj; /* Pointer to Object stored in supplied KeyMap */ + AstRegion *areg; /* Pointer to remapped Region */ + AstRegion *sareg; /* Pointer to simplified remapped Region */ + const char *key; /* Current key */ + int j; /* Index of key within KeyMap */ + int naxes; /* Number of axes in region */ + int nkey; /* Number of keys in supplied KeyMap */ + int nv; /* Number of values in KeyMap element */ + int type; /* Data type of entry */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Confirm it is a genuine KeyMap pointer. */ + if( !astIsAKeyMap( coord ) && astOK ) { + astError( AST__STCKEY, "astInitStc(%s): Supplied pointer is for " + "a %s, not a KeyMap.", status, class, astGetClass( coord ) ); + } + +/* Initialise the new KeyMap to be a copy of the supplied KeyMap. */ + result = astCopy( coord ); + +/* Check the supplied KeyMap is usable. */ + naxes = astGetNaxes( reg ); + nkey = astMapSize( result ); + for( j = 0; j < nkey; j++ ) { + key = astMapKey( result, j ); + if( key ) { + nv = astMapLength( result, key ); + type = astMapType( result, key ); + +/* Check no unknown keys are present in the KeyMap. */ + if( strcmp( key, AST__STCNAME ) && + strcmp( key, AST__STCVALUE ) && + strcmp( key, AST__STCERROR ) && + strcmp( key, AST__STCRES ) && + strcmp( key, AST__STCSIZE ) && + strcmp( key, AST__STCPIXSZ ) ) { + astError( AST__STCKEY, "astInitStc(%s): Unknown key " + "\"%s\" supplied in an AstroCoords list.", status, + class, key ); + break; + +/* Check that the "Name" element is a vector of "naxes" strings. */ + } else if( !strcmp( key, AST__STCNAME ) ) { + if( nv != naxes ) { + astError( AST__STCKEY, "astInitStc(%s): %d \"%s\" " + "values supplied in an AstroCoords list, but " + "the Stc has %d axes. ", status, class, nv, key, + naxes ); + break; + + } else if( type != AST__STRINGTYPE ) { + astError( AST__STCKEY, "astInitStc(%s): The \"%s\" " + "values supplied in an AstroCoords list are " + "not character strings. ", status, class, key ); + break; + } + +/* Check that all other elements are scalar. */ + } else if( nv != 1 ) { + astError( AST__STCKEY, "astInitStc(%s): %d \"%s\" " + "values supplied in an AstroCoords list, but " + "only one is allowed. ", status, class, nv, key ); + break; + +/* Check that all other elements are AST Object pointers. */ + } else if( type != AST__OBJECTTYPE ) { + astError( AST__STCKEY, "astInitStc(%s): The \"%s\" " + "value supplied in an AstroCoords list is " + "not an AST Object pointer. ", status, class, key ); + break; + +/* Check that the Object pointers are not NULL. */ + } else { + astMapGet0A( result, key, &obj ); + if( astOK ) { + if( !obj ) { + astError( AST__STCKEY, "astInitStc(%s): The \"%s\" " + "value supplied in an AstroCoords list is " + "a NULL pointer. ", status, class, key ); + break; + +/* Check that the Object pointers are Region pointers. */ + } else if( !astIsARegion( obj ) ){ + astError( AST__STCKEY, "astInitStc(%s): The \"%s\" " + "value supplied in an AstroCoords list is " + "a %s, not a Region. ", status, class, key, + astGetClass(obj) ); + obj = astAnnul( obj ); + break; + +/* Check that the Region pointers can be converted to the coordinate + system represented by the supplied Region. */ + } else { + fs = astConvert( obj, reg, "" ); + if( !fs ) { + obj = astAnnul( obj ); + astError( AST__STCKEY, "astInitStc(%s): The \"%s\" " + "value supplied in an AstroCoords list " + "cannot be converted to the coordinate " + "system of its parent Stc object.", status, class, + key ); + break; + +/* If necessary, map the Region into the same frame as the supplied + Region, and replace the Region in the returned KeyMap with the + remapped Region. Also set the RegionFS attribute to indicate that the + FrameSet in the Region does not need to be dumped if it contains a + UnitMap. */ + } else { + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + if( !astIsAUnitMap( map ) ) { + frm = astGetFrame( fs, AST__CURRENT ); + areg = astMapRegion( (AstRegion *) obj, map, frm ); + sareg = astSimplify( areg ); + astSetRegionFS( sareg, 0 ); + astMapPut0A( result, key, sareg, NULL ); + areg = astAnnul( areg ); + sareg = astAnnul( sareg ); + frm = astAnnul( frm ); + } else { + astSetRegionFS( (AstRegion *) obj, 0 ); + } + map = astAnnul( map ); + fs = astAnnul( fs ); + + } + obj = astAnnul( obj ); + } + } + } + } + } + +/* Free the returned KeyMap if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result */ + return result; + +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* Stc member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to STC structure */ + int i; /* Loop count */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the STC structure. */ + this = (AstStc *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->region, mode, extra, fail ); + for( i = 0; i < this->ncoord; i++ ) { + if( !result ) result = astManageLock( this->coord[ i ], mode, + extra, fail ); + } + + return result; + +} +#endif + +static int Overlap( AstRegion *this, AstRegion *that, int *status ){ +/* +* Name: +* Overlap + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int Overlap( AstRegion *this, AstRegion *that, int *status ) + +* Class Membership: +* Stc member function (over-rides the astOverlap method inherited +* from the Region class). + +* Description: +* This function returns an integer value indicating if the two +* supplied Regions overlap. The two Regions are converted to a commnon +* coordinate system before performing the check. If this conversion is +* not possible (for instance because the two Regions represent areas in +* different domains), then the check cannot be performed and a zero value +* is returned to indicate this. + +* Parameters: +* this +* Pointer to the first Region. +* that +* Pointer to the second Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* astOverlap() +* A value indicating if there is any overlap between the two Regions. +* Possible values are: +* +* 0 - The check could not be performed because the second Region +* could not be mapped into the coordinate system of the first +* Region. +* +* 1 - There is no overlap between the two Regions. +* +* 2 - The first Region is completely inside the second Region. +* +* 3 - The second Region is completely inside the first Region. +* +* 4 - There is partial overlap between the two Regions. +* +* 5 - The Regions are identical. +* +* 6 - The second Region is the negation of the first Region. + +* Notes: +* - The returned values 5 and 6 do not check the value of the Closed +* attribute in the two Regions. +* - A value of zero will be returned if this function is invoked with the +* AST error status set, or if it should fail for any reason. + +*/ + +/* Check the inherited status. */ + if ( !astOK ) return 0; + +/* Invoke the "astOverlap" method on the encapsulated Region. */ + return astOverlap( ((AstStc *)this)->region, that ); +} + +static int OverlapX( AstRegion *that, AstRegion *this, int *status ){ +/* +* Name: +* OverlapX + +* Purpose: +* Test if two regions overlap each other. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int OverlapX( AstRegion *that, AstRegion *this ) + +* Class Membership: +* Stc member function (over-rides the astOverlapX method inherited +* from the Region class). + +* Description: +* This function performs the processing for the public astOverlap +* method and has exactly the same interface except that the order +* of the two arguments is swapped. This is a trick to allow +* the astOverlap method to be over-ridden by derived classes on +* the basis of the class of either of its two arguments. +* +* See the astOverlap method for details of the interface. + +*/ + +/* Local Variables: */ + int result; + +/* Check the inherited status. */ + if ( !astOK ) return 0; + +/* Invoke the "astOverlapX" method on the encapsulated Region. */ + result = astOverlap( ((AstStc *)that)->region, this ); + +/* Swap the returned values 2 and 3 to take account of the swapping of + the regions.*/ + if( result == 2 ) { + result = 3; + } else if( result == 3 ) { + result = 2; + } + +/* Return the result. */ + return result; +} + +static void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* Stc member function (over-rides the astRegBaseBox protected +* method inherited from the Region class). + +* Description: +* This function returns the upper and lower axis bounds of a Region in +* the base Frame of the encapsulated FrameSet, assuming the Region +* has not been negated. That is, the value of the Negated attribute +* is ignored. + +* Parameters: +* this +* Pointer to the Region. +* lbnd +* Pointer to an array in which to return the lower axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* ubnd +* Pointer to an array in which to return the upper axis bounds +* covered by the Region in the base Frame of the encapsulated +* FrameSet. It should have at least as many elements as there are +* axes in the base Frame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Invoke the method on the encapsulated Region. */ + astRegBaseBox( ((AstStc *)this)->region, lbnd, ubnd ); +} + +static AstPointSet *RegBaseMesh( AstRegion *this, int *status ){ +/* +* Name: +* RegBaseMesh + +* Purpose: +* Create a new PointSet containing a mesh of points on the boundary of a +* Region in its base Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* Stc member function (over-rides the astRegBaseMesh protected +* method inherited from the Region class). + +* Description: +* This function creates a new PointSet containing a mesh of points on the +* boundary of the Region. The points refer to the base Frame of +* the encapsulated FrameSet. + +* Parameters: +* this +* Pointer to the Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the PointSet. Annul the pointer using astAnnul when it +* is no longer needed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. + +*/ + +/* Check the global error status. */ + if( !astOK ) return NULL; + +/* Invoke the astRegMesh method on the encapsulated Region. This returns + a mesh in the current Frame of the encapsulated Region which is the same + as the base Frame of the Stc Region. */ + return astRegMesh( ((AstStc *)this)->region ); +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, + const int *axes, int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* Stc member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Invoke the astRegBaePick method on the encapsulated Region. */ + return astRegBasePick( ((AstStc *)this_region)->region, naxes, axes ); +} + +static void RegClearAttrib( AstRegion *this_region, const char *attrib, + char **base_attrib, int *status ) { +/* +* Name: +* RegClearAttrib + +* Purpose: +* Clear an attribute value for a Region. + +* Type: +* Protected function. + +* Synopsis: +* #include "stc.h" +* void RegClearAttrib( AstRegion *this, const char *attrib, +* char **base_attrib, int *status ) + +* Class Membership: +* Stc method (over-rides the astRegClearAttrib method inherited from +* the Region class). + +* Description: +* This function clears the value of an attribute in both the base and +* current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* attrib +* Pointer to a null terminated string containing an attribute name. +* NOTE, IT SHOULD BE ENTIRELY LOWER CASE. +* base_attrib +* Address of a location at which to return a pointer to the null +* terminated string holding the name of the attribute which was +* cleared in the base Frame of the encapsulated FrameSet. This may +* differ from the supplied name if the supplied name contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstStc *this; + char *batt; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Stc structure. */ + this = (AstStc *) this_region; + +/* Use the RegClearAttrib method inherited from the parent class to clear + the attribute in the current and base Frames in the FrameSet encapsulated + by the parent Region structure. */ + (*parent_regclearattrib)( this_region, attrib, &batt, status ); + +/* Now clear the base Frame attribute in the encapsulated Region (the current + Frame within the encapsulated Region is equivalent to the base Frame in the + parent Region structure). Annul any "attribute unknown" error that results + from attempting to do this. */ + if( astOK ) { + rep = astReporting( 0 ); + astRegClearAttrib( this->region, batt, NULL ); + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + } + +/* If required, return the base Frame attribute name, otherwise free it. */ + if( base_attrib ) { + *base_attrib = batt; + } else { + batt = astFree( batt ); + } +} + +static int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, + int **mask, int *status ){ +/* +* Name: +* RegPins + +* Purpose: +* Check if a set of points fall on the boundary of a given Stc. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* Stc member function (over-rides the astRegPins protected +* method inherited from the Region class). + +* Description: +* This function returns a flag indicating if the supplied set of +* points all fall on the boundary of the given Stc. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied Stc "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the Stc. +* pset +* Pointer to the PointSet. The points are assumed to refer to the +* base Frame of the FrameSet encapsulated by "this". +* unc +* Pointer to a Region representing the uncertainties in the points +* given by "pset". The Region is assumed to represent the base Frame +* of the FrameSet encapsulated by "this". Zero uncertainity is assumed +* if NULL is supplied. +* mask +* Pointer to location at which to return a pointer to a newly +* allocated dynamic array of ints. The number of elements in this +* array is equal to the value of the Npoint attribute of "pset". +* Each element in the returned array is set to 1 if the +* corresponding position in "pset" is on the boundary of the Region +* and is set to zero otherwise. A NULL value may be supplied +* in which case no array is created. If created, the array should +* be freed using astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the points all fall on the boundary of the given +* Region, to within the tolerance specified. Zero otherwise. + +*/ + +/* Check the global error status. */ + if( !astOK ) return 0; + +/* Invoke the method on the encapsulated Region. */ + return astRegPins( ((AstStc *)this)->region, pset, unc, mask ); +} + +static void RegSetAttrib( AstRegion *this_region, const char *setting, + char **base_setting, int *status ) { +/* +* Name: +* RegSetAttrib + +* Purpose: +* Set an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* void RegSetAttrib( AstRegion *this, const char *setting, +* char **base_setting, int *status ) + +* Class Membership: +* Stc method (over-rides the astRegSetAttrib method inherited from +* the Region class). + +* Description: +* This function assigns an attribute value to both the base and +* current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* setting +* Pointer to a null terminated attribute setting string. NOTE, IT +* SHOULD BE ENTIRELY LOWER CASE. The supplied string will be +* interpreted using the public interpretation implemented by +* astSetAttrib. This can be different to the interpretation of the +* protected accessor functions. For instance, the public +* interpretation of an unqualified floating point value for the +* Epoch attribute is to interpet the value as a gregorian year, +* but the protected interpretation is to interpret the value as an +* MJD. +* base_setting +* Address of a location at which to return a pointer to the null +* terminated attribute setting string which was applied to the +* base Frame of the encapsulated FrameSet. This may differ from +* the supplied setting if the supplied setting contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstKeyMap *keymap; + AstObject *obj; + AstRegion *reg; + AstStc *this; + char *bset; + int i; + int ikey; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Stc structure. */ + this = (AstStc *) this_region; + +/* Use the RegSetAttrib method inherited from the parent class to apply the + setting to the current and base Frames in the FrameSet encapsulated by the + parent Region structure. */ + (*parent_regsetattrib)( this_region, setting, &bset, status ); + +/* Now apply the base Frame setting to the encapsulated Region (the current + Frame within the encapsulated Region is equivalent to the base Frame in the + parent Region structure). Annul any "attribute unknown" error that results + from attempting to do this. Also do any AstroCoords in the Stc. */ + if( astOK ) { + rep = astReporting( 0 ); + astRegSetAttrib( this->region, bset, NULL ); + if( astStatus == AST__BADAT ) astClearStatus; + +/* Loop round all AstroCoords elements. */ + for( i = 0; i < this->ncoord; i++ ) { + +/* Get a pointer to the KeyMap holding a description of the current + AstroCoords element. */ + keymap = this->coord[ i ]; + +/* Loop round all the elements of this KeyMap which may hold a Region + pointer. */ + for( ikey = 0; ikey < NREG; ikey++ ) { + +/* If the KeyMap contains a Region for this key, get a pointer to it. */ + if( astMapGet0A( keymap, regkey[ ikey ], &obj ) ){ + reg = (AstRegion *) obj; + +/* Modify it by applying the attribute setting. */ + astRegSetAttrib( reg, bset, NULL ); + if( astStatus == AST__BADAT ) astClearStatus; + +/* Annul the pointer. */ + reg = astAnnul( reg ); + } + } + } + + astReporting( rep ); + } + +/* If required, return the base Frame setting string, otherwise free it. */ + if( base_setting ) { + *base_setting = bset; + } else { + bset = astFree( bset ); + } +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Stc. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Stc member function (over-rides the astSetAttrib method inherited +* from the Region class). + +* Description: +* This function assigns an attribute value for a Stc, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Stc. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Vaiables: */ + AstStc *this; /* Pointer to the Stc structure */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Stc structure. */ + this = (AstStc *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* (none as yet) */ + +/* Read-only attributes. */ +/* --------------------- */ +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* Use this macro to report an error if a read-only attribute has been + specified. */ + if ( MATCH( "regionclass" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) { +/* +* Name: +* SetRegFS + +* Purpose: +* Stores a new FrameSet in a Region + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* Stc method (over-rides the astSetRegFS method inherited from +* the Region class). + +* Description: +* This function creates a new FrameSet and stores it in the supplied +* Region. The new FrameSet contains two copies of the supplied +* Frame, connected by a UnitMap. + +* Parameters: +* this +* Pointer to the Region. +* frm +* The Frame to use. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstRegion *creg; /* Pointer to encapsulated Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* If the encapsulated Region has a dummy FrameSet use this method + recursively to give it the same FrameSet. */ + creg = ((AstStc *) this_region )->region; + if( creg && !astGetRegionFS( creg ) ) astSetRegFS( creg, frm ); + +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* Stc method (over-rides the astSimplify method inherited from +* the Region class). + +* Description: +* This function simplifies a Stc to eliminate redundant +* computational steps, or to merge separate steps which can be +* performed more efficiently in a single operation. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new pointer to the (possibly simplified) Region. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* Current Frame */ + AstKeyMap *keymap; /* KeyMap holding stroCoords element */ + AstMapping *map; /* Base->current Mapping */ + AstObject *obj; /* Pointer to object retrieved from keymap */ + AstRegion *newreg; /* New encapsulated Region */ + AstRegion *reg; /* AstroCoords Region pointer */ + AstRegion *treg; /* Temporary Region pointer */ + AstStc *stc; /* Returned Stc Structure. */ + AstStc *temp; /* Temporary Stc pointer */ + int i; /* Index of current AstroCoords element */ + int ikey; /* Index of key to be tested */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Invoke the Simplify method of the parent Region class. This simplifies + the FrameSet and uncertainty Region in the parent Region structure. */ + stc = (AstStc *) (AstRegion *) (* parent_simplify)( this_mapping, status ); + +/* If the Stc is negated, we can perform a simplication by transferring + the negated state from the Stc itself to the encapsulated Region. */ + if( astGetNegated( stc ) ) { + +/* Ensure that modifying "stc" will not modify the supplied Stc, by + creating a copy of the supplied Stc, if this has not already been done. */ + if( stc == (AstStc *) this_mapping ) { + temp = (AstStc *) astCopy( stc ); + (void) astAnnul( stc ); + stc = temp; + } + +/* Modify "temp" by negating both the Stc structure and its encapsulated + Region. */ + astNegate( stc ); + astNegate( stc->region ); + + } + +/* Get the base->current Mapping from the parent Region structure, and + the current Frame. */ + map = astGetMapping( ((AstRegion *) stc)->frameset, AST__BASE, AST__CURRENT ); + frm = astGetFrame( ((AstRegion *) stc)->frameset, AST__CURRENT ); + +/* We may be able to perform some more simplication on the encapsulated + Region itself. If the above mapping is not a unit map, remap the + encapsulated Region into the current Frame of the parent Region structure + and simplify it. This transfers complication from the Mapping in the + parent Region structure to the encapsulated Region. */ + if( !astIsAUnitMap( map ) ) { + treg = astMapRegion( stc->region, map, frm ); + newreg = astSimplify( treg ); + treg = astAnnul( treg ); + +/* If the base->current Mapping in the parent Region structure is a unit + map, simplification of the whole Stc is possible if the encapsulated + Region (without any remapping) can be simplied. */ + } else { + newreg = astSimplify( stc->region ); + } + +/* If the encapsulated Region has been changed, store it in the returned + Stc. */ + if( newreg != stc->region ) { + +/* Ensure that modifying "stc" will not modify the supplied Stc, by + creating a copy of the supplied Stc, if this has not already been done. */ + if( stc == (AstStc *) this_mapping ) { + temp = (AstStc *) astCopy( stc ); + (void) astAnnul( stc ); + stc = temp; + } + +/* Store the new region in "stc", annulling the existing Region. */ + if( stc ) { + (void) astAnnul( stc->region ); + stc->region = astClone( newreg ); + } + +/* The encapsulated Region now represents an area in the current Frame + represented by the supplied Stc. Since the encapsulated Region is + defined as being in the base Frame of the FrameSet in the parent + Region structure, the parent FrameSet should just be a UnitMap. Modify + it appropriately (if it not already a UnitMap). */ + if( !astIsAUnitMap( map ) ) astSetRegFS( stc, frm ); + } + +/* Free resources */ + newreg = astAnnul( newreg ); + +/* Now we do a similar process on any Regions held within an AstroCoords + elements. Loop round all AstroCoords elements. */ + if( stc ) { + for( i = 0; i < stc->ncoord; i++ ) { + +/* Get a pointewr to the KeyMap holding a description of the current + AstroCoords element. */ + keymap = stc->coord[ i ]; + +/* Loop round all the elements of this KeyMap which may hold a Region + pointer. */ + for( ikey = 0; ikey < NREG; ikey++ ) { + +/* If the KeyMap contains a Region for this key, get a pointer to it. */ + if( astMapGet0A( keymap, regkey[ ikey ], &obj ) ){ + reg = (AstRegion *) obj; + +/* We have two tasks now, firstly to ensure that this AstroCoords Region + describes an area in the base Frame of the FrameSet in the parent + Region structure (which may have been changed by the earlier + simplications performed by this function), and secondly, to attempt to + simplify the Region. + + The Stc structure addressed by the "stc" pointer will have a current + Frame given by "frm". This will also be its base Frame, and the + base->current Mapping will consequently be a UnitMap. The Mapping from + the original base Frame to the new base Frame is given by "map". Unless + this is a UnitMap, we need to remap the Region.*/ + if( !astIsAUnitMap( map ) ) { + treg = astMapRegion( reg, map, frm ); + } else { + treg = astClone( reg ); + } + +/* Now attempt to simplify the Region.*/ + newreg = astSimplify( treg ); + +/* If the Region has been changed by either of these steps, we need to + store the modified Region back in the "stc" structure which is being + returned. But we need to be careful we do not modify the supplied Stc + structure. */ + if( newreg != reg ) { + + if( stc == (AstStc *) this_mapping ) { + temp = astCopy( stc ); + (void) astAnnul( stc ); + stc = temp; + keymap = temp->coord[ i ]; + } + + astMapPut0A( keymap, regkey[ ikey ], newreg, regcom[ ikey ] ); + + } + +/* Free resources */ + reg = astAnnul( reg ); + treg = astAnnul( treg ); + newreg = astAnnul( newreg ); + + } + } + } + } + +/* Free resources */ + map = astAnnul( map ); + frm = astAnnul( frm ); + +/* If an error occurred, annul the returned Mapping. */ + if ( !astOK ) stc = astAnnul( stc ); + +/* Return the result. */ + return (AstMapping *) stc; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Stc. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Stc member function (over-rides the astTestAttrib protected +* method inherited from the Region class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Stc's attributes. + +* Parameters: +* this +* Pointer to the Stc. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to the Stc structure */ + int len; /* Length of attrib string */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the Stc structure. */ + this = (AstStc *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute name matches any of the read-only attributes + of this class. If it does, then return zero. */ + if ( !strcmp( attrib, "regionclass" ) ) { + result = 0; + +/* Not recognised. */ +/* --------------- */ +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a Stc to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "stc.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* Stc member function (over-rides the astTransform method inherited +* from the Region class). + +* Description: +* This function takes a Stc and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Region. +* This implies applying each of the Stc's encapsulated Region in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the Stc. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the Stc being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *ps; /* Pointer to PointSet */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + AstRegion *reg; /* Pointer to encapsulated Region */ + AstStc *this; /* Pointer to the Stc structure */ + double **ptr; /* Pointer to axis values */ + double **ptr_out; /* Pointer to output coordinate data */ + int coord; /* Zero-based index for coordinates */ + int good; /* Is the point inside the Stc? */ + int ncoord_out; /* No. of coordinates per output point */ + int ncoord_tmp; /* No. of coordinates per base Frame point */ + int neg; /* Negated value for encapsulated Region */ + int neg_old; /* Original Negated flag */ + int npoint; /* No. of points */ + int point; /* Loop counter for points */ + int rep; /* Original error reporting status */ + int status_value; /* AST status value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a Pointer to the Stc structure */ + this = (AstStc *) this_mapping; + +/* Get the encapsulated Region, and the Negated value which should be used + with it. The returned values take account of whether the supplied Stc has + itself been Negated or not. The returned Region represent a region within + the base Frame of the FrameSet encapsulated by the parent Region + structure. */ + GetRegion( this, ®, &neg, status ); + +/* Temporarily set the Negated attribute to the required value.*/ + neg_old = astGetNegated( reg ); + astSetNegated( reg, neg ); + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Region class. This function validates + all arguments and generates an output PointSet if necessary, containing + a copy of the input PointSet. */ + result = (*parent_transform)( this_mapping, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet in the parent Region structure to + transform the supplied positions from the current Frame in the + encapsulated FrameSet (the Frame represented by the Stc), to the + base Frame (the Frame in which the encapsulated Region are defined). Note, + the returned pointer may be a clone of the "in" pointer, and so we + must be carefull not to modify the contents of the returned PointSet. */ + pset_tmp = astRegTransform( this, in, 0, NULL, NULL ); + +/* Now transform this PointSet using the encapsulated Region. */ + ps = astTransform( reg, pset_tmp, 0, NULL ); + +/* Determine the numbers of points and coordinates per point for these base + Frame PointSets and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( pset_tmp ); + ncoord_tmp = astGetNcoord( pset_tmp ); + ptr = astGetPoints( ps ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + + for ( point = 0; point < npoint; point++ ) { + good = 0; + + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + if( ptr[ coord ][ point ] != AST__BAD ) { + good = 1; + break; + } + } + + if( !good ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + } + +/* Re-instate the original value for the Negated attribute of the + encapsulated Region. Do this even if an error has occurred. */ + status_value = astStatus; + astClearStatus; + rep = astReporting( 0 ); + if( reg ) astSetNegated( reg, neg_old ); + astReporting( rep ); + astSetStatus( status_value ); + +/* Free resources. */ + reg = astAnnul( reg ); + ps = astAnnul( ps ); + pset_tmp = astAnnul( pset_tmp ); + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + + +/* Stc Attributes: */ +/* =============== */ + +/* +*att++ +* Name: +* RegionClass + +* Purpose: +* The AST class name of the Region encapsulated within an Stc + +* Type: +* Public attribute. + +* Synopsis: +* String, read-only. + +* Description: +* This is a read-only attribute giving the AST class name of the +* Region encapsulated within an Stc (that is, the class of the Region +* which was supplied when the Stc was created). + +* Applicability: +* Stc +* All Stc objects this attribute. +*att-- +*/ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Stc objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Stc objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Regions within the Stc. +*/ + +/* Local Variables: */ + AstStc *in; /* Pointer to input Stc */ + AstStc *out; /* Pointer to output Stc */ + int i; /* AstroCoords index */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Stcs. */ + in = (AstStc *) objin; + out = (AstStc *) objout; + +/* For safety, start by clearing any references to the input component + Regions, etc, from the output Stc. */ + out->region = NULL; + out->coord = NULL; + out->ncoord = 0; + +/* Make a copy of the Region and store a pointer to it in the output Stc + structure. */ + out->region = astCopy( in->region ); + +/* Copy any memory holding AstroCoords values */ + if( in->coord && in->ncoord ) { + out->ncoord = in->ncoord; + out->coord = astMalloc( sizeof(AstKeyMap *) * (size_t)in->ncoord ); + if( out->coord ) { + for( i = 0; i < in->ncoord; i++ ) { + out->coord[ i ] = astCopy( in->coord[ i ] ); + } + } + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Stc objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Stc objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstStc *this; /* Pointer to Stc */ + int i; /* AstroCoords index */ + +/* Obtain a pointer to the Stc structure. */ + this = (AstStc *) obj; + +/* Annul the pointer to the encapsulated Region. */ + this->region = astAnnul( this->region ); + +/* Free any memory holding AstroCoords values */ + if( this->coord ) { + for( i = 0; i < this->ncoord; i++ ) { + this->coord[ i ] = astAnnul( this->coord[ i ] ); + } + this->coord = astFree( this->coord ); + } +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Stc objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Stc class to an output Channel. + +* Parameters: +* this +* Pointer to the Stc whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 150 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstStc *this; /* Pointer to the Stc structure */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment string */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int ico; /* Loop counter for KeyMaps */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Stc structure. */ + this = (AstStc *) this_object; + +/* Write out values representing the instance variables for the Stc + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Encapsulated Region. */ +/* -------------------- */ + astWriteObject( channel, "Region", 1, 1, this->region, + "STC Region" ); + +/* AstroCoords info */ +/* ---------------- */ + astWriteInt( channel, "Ncoord", ( this->ncoord != 0 ), 0, this->ncoord, + "Number of AstroCoords elements" ); + + for ( ico = 1; ico <= this->ncoord; ico++ ) { + (void) sprintf( key, "Coord%d", ico ); + (void) sprintf( comment, "AstroCoords number %d", ico ); + astWriteObject( channel, key, 1, 1, this->coord[ ico - 1 ], + comment ); + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAStc and astCheckStc functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Stc,Region) +astMAKE_CHECK(Stc) + +AstStc *astInitStc_( void *mem, size_t size, int init, AstStcVtab *vtab, + const char *name, AstRegion *region, int ncoords, + AstKeyMap **coords, int *status ) { +/* +*+ +* Name: +* astInitStc + +* Purpose: +* Initialise a Stc. + +* Type: +* Protected function. + +* Synopsis: +* #include "stc.h" +* AstStc *astInitStc( void *mem, size_t size, int init, AstStcVtab *vtab, +* const char *name, AstRegion *region, int ncoords, +* AstKeyMap **coords ) + +* Class Membership: +* Stc initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Stc object. It allocates memory (if necessary) to +* accommodate the Stc plus any additional data associated with the +* derived class. It then initialises a Stc structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a Stc at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Stc is to be initialised. +* This must be of sufficient size to accommodate the Stc data +* (sizeof(Stc)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the Stc (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* Stc structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the Stc's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Stc. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* region +* Pointer to the Region represented by the Stc. +* ncoords +* Number of KeyMap pointers supplied in "coords". Can be zero. +* Ignored if "coords" is NULL. +* coords +* Pointer to an array of "ncoords" KeyMap pointers, or NULL if +* "ncoords" is zero. Each KeyMap defines defines a single +* element, and should have elements with keys given by constants +* AST__STCNAME, AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. Any of these elements may be omitted, but no other +* elements should be included. If supplied, the AST__STCNAME element +* should be a vector of character string pointers holding the "Name" +* item for each axis. Any other supplied elements should be scalar +* elements, each holding a pointer to a Region describing the +* associated item of ancillary information (error, resolution, size, +* pixel size or value). These Regions should describe a volume within +* the coordinate system represented by "region". + +* Returned Value: +* A pointer to the new Stc. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstMapping *frm; /* Current Frame in supplied Stc */ + AstMapping *map; /* Base -> Current Mapping in supplied Stc */ + AstRegion *reg; /* Copy of supplied Region */ + AstStc *new; /* Pointer to new Stc */ + int i; /* AstroCoords index */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitStcVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* If the supplied Region is an Stc, create a new Region by mapping the + encapsulated Region within the supplied Stc into the current Frame of the + Stc. */ + if( astIsAStc( region ) ) { + map = astGetMapping( region->frameset, AST__BASE, AST__CURRENT ); + frm = astGetFrame( region->frameset, AST__CURRENT ); + reg = astMapRegion( ((AstStc *) region)->region, map, frm ); + frm = astAnnul( frm ); + map = astAnnul( map ); + +/* Otherwise, just take a copy of the supplied Region. */ + } else { + reg = astCopy( region ); + } + +/* Initialise a Region structure (the parent class) as the first component + within the Stc structure, allocating memory if necessary. A NULL + PointSet is suppled as the encapsulated Region will perform the function + of defining the Region shape. The base Frame of the FrameSet in the + parent Region structure will be the same as the current Frames of the + FrameSets in the two encapsulated Region. */ + if ( astOK ) { + new = (AstStc *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab, + name, reg, NULL, NULL ); + +/* Initialise the Stc data. */ +/* --------------------------- */ +/* Store a pointer to the encapsulated Region. */ + new->region = astClone( reg ); + +/* No AstroCoords info as yet. */ + new->ncoord = 0; + new->coord = NULL; + +/* Transfer attributes from the encapsulated region to the parent region. */ + astRegOverlay( new, reg, 1 ); + if( astTestIdent( reg ) ) astSetIdent( new, astGetIdent( reg ) ); + +/* If the base->current Mapping in the FrameSet within the encapsulated Region + is a UnitMap, then the FrameSet does not need to be included in the + Dump of the new Stc. Set the RegionFS attribute of the encapsulated + Region to zero to flag this. Note, we do this after the previous class + to astRegOverlay because we do not want this zero value for RegionFS to + be copied into the new Stc object. */ + astSetRegionFS( reg, 0 ); + +/* For each supplied AstroCoords, create a new KeyMap holding Regions + representing the various elements of the AstroCoords, and store the + new KeyMap in the Stc structure. */ + if( coords && ncoords > 0 ) { + new->ncoord = ncoords; + new->coord = astMalloc( sizeof( AstKeyMap *)*(size_t) ncoords ); + if( new->coord ) { + for( i = 0; i < ncoords; i++ ) { + new->coord[ i ] = MakeAstroCoordsKeyMap( reg, coords[ i ], + name, status ); + } + } + } + +/* If an error occurred, clean up deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Free resources */ + reg = astAnnul( reg ); + +/* Return a pointer to the new object. */ + return new; +} + +AstStc *astLoadStc_( void *mem, size_t size, AstStcVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadStc + +* Purpose: +* Load a Stc. + +* Type: +* Protected function. + +* Synopsis: +* #include "stc.h" +* AstStc *astLoadStc( void *mem, size_t size, AstStcVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Stc loader. + +* Description: +* This function is provided to load a new Stc using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Stc structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Stc at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Stc is to be +* loaded. This must be of sufficient size to accommodate the +* Stc data (sizeof(Stc)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Stc (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Stc structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstStc) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Stc. If this is NULL, a pointer to +* the (static) virtual function table for the Stc class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Stc" is used instead. + +* Returned Value: +* A pointer to the new Stc. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstFrame *f1; /* Base Frame in parent Region */ + AstObject *obj; /* Pointer to Object retrieved from KeyMap */ + AstRegion *creg; /* Pointer to encapsulated Region */ + AstStc *new; /* Pointer to the new Stc */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int ico; /* Loop counter for AstroCoords */ + int ikey; /* Index of KeyMap */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Stc. In this case the + Stc belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstStc ); + vtab = &class_vtab; + name = "Stc"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitStcVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Stc. */ + new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Stc" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Encapsulated Region. */ +/* -------------------- */ + new->region = astReadObject( channel, "region", NULL ); + +/* Get a pointer to the base Frame in the FrameSet encapsulated by the + parent Region structure. */ + f1 = astGetFrame( ((AstRegion *) new)->frameset, AST__BASE ); + +/* If the encapsulated Region has a dummy FrameSet rather than the correct + FrameSet, the correct FrameSet will have copies of the base Frame of the + new Stc as both its current and base Frames, connected by a UnitMap (this + is equivalent to a FrameSet containing a single Frame). However if the new + Stc being loaded has itself got a dummy FrameSet, then we do not do this + since we do not yet know what the correct FrameSet is. In this case we + wait until the parent Region invokes the astSetRegFS method on the new + Stc. */ + if( !astRegDummyFS( new ) ) { + creg = new->region; + if( astRegDummyFS( creg ) ) astSetRegFS( creg, f1 ); + } + +/* AstroCoords info */ +/* ---------------- */ +/* The number of AstroCoords described in the new Stc. */ + new->ncoord = astReadInt( channel, "ncoord", 0 ); + if( new->ncoord < 0 ) new->ncoord = 0; + +/* Read back each KeyMap describing these AstroCoords. */ + new->coord = astMalloc( sizeof( AstKeyMap *) * (size_t) new->ncoord ); + for( ico = 1; ico <= new->ncoord; ico++ ) { + (void) sprintf( key, "coord%d", ico ); + new->coord[ ico - 1 ] = astReadObject( channel, key, NULL ); + +/* Ensure the Regions within the KeyMap do not have dummy FrameSets. */ + if( new->coord[ ico - 1 ] && !astRegDummyFS( new ) ) { + for( ikey = 0; ikey < NREG; ikey++ ) { + if( astMapGet0A( new->coord[ ico - 1 ], regkey[ ikey ], &obj ) ){ + creg = (AstRegion *) obj; + if( astRegDummyFS( creg ) ) { + astSetRegFS( creg, f1 ); + astMapPut0A( new->coord[ ico - 1 ], regkey[ ikey ], creg, + regcom[ ikey ] ); + } + creg = astAnnul( creg ); + } + } + } + } + +/* Free resources */ + f1 = astAnnul( f1 ); + +/* If an error occurred, clean up by deleting the new Stc. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Stc pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +const char *astGetRegionClass_( AstStc *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Stc,GetRegionClass))( this, status ); +} + +AstRegion *astGetStcRegion_( AstStc *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Stc,GetStcRegion))( this, status ); +} + +AstKeyMap *astGetStcCoord_( AstStc *this, int icoord, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Stc,GetStcCoord))( this, icoord, status ); +} + +int astGetStcNCoord_( AstStc *this, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Stc,GetStcNCoord))( this, status ); +} + + + + + + + + + + + + + diff --git a/stc.h b/stc.h new file mode 100644 index 0000000..1fe0b1b --- /dev/null +++ b/stc.h @@ -0,0 +1,240 @@ +#if !defined( STC_INCLUDED ) /* Include this file only once */ +#define STC_INCLUDED +/* +*+ +* Name: +* stc.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Stc class. + +* Invocation: +* #include "stc.h" + +* Description: +* This include file defines the interface to the Stc class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The Stc class is an implementation of the IVOA STC class which forms +* part of the IVOA Space-Time Coordinate Metadata system. See: +* +* http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The Stc class inherits from the Region class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-NOV-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "region.h" /* Coordinate regions (parent class) */ +#include "keymap.h" /* Lists of value/key pairs */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ======= */ + + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST__STCNAME "Name" +#define AST__STCVALUE "Value" +#define AST__STCERROR "Error" +#define AST__STCRES "Resolution" +#define AST__STCSIZE "Size" +#define AST__STCPIXSZ "PixSize" + +/* Type Definitions. */ +/* ================= */ +/* Stc structure. */ +/* ------------------ */ + +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstStc { + +/* Attributes inherited from the parent class. */ + AstRegion parent_region; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstRegion *region; /* Encapsulated Region */ + AstKeyMap **coord; /* STC AstroCoords info */ + int ncoord; /* Number of AstroCoords in "coords" */ +} AstStc; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstStcVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstRegionVtab region_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + const char *(* GetRegionClass)( AstStc *, int * ); + AstRegion *(* GetStcRegion)( AstStc *, int * ); + AstKeyMap *(* GetStcCoord)( AstStc *, int, int * ); + int (* GetStcNCoord)( AstStc *, int * ); + +} AstStcVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstStcGlobals { + AstStcVtab Class_Vtab; + int Class_Init; +} AstStcGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitStcGlobals_( AstStcGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Stc) /* Check class membership */ +astPROTO_ISA(Stc) /* Test class membership */ + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstStc *astInitStc_( void *, size_t, int, AstStcVtab *, const char *, + AstRegion *, int, AstKeyMap **, int * ); + +/* Vtab initialiser. */ +void astInitStcVtab_( AstStcVtab *, const char *, int * ); + +/* Loader. */ +AstStc *astLoadStc_( void *, size_t, AstStcVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +AstRegion *astGetStcRegion_( AstStc *, int * ); +AstKeyMap *astGetStcCoord_( AstStc *, int, int * ); +int astGetStcNCoord_( AstStc *, int * ); + +# if defined(astCLASS) /* Protected */ +const char *astGetRegionClass_( AstStc *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckStc(this) astINVOKE_CHECK(Stc,this,0) +#define astVerifyStc(this) astINVOKE_CHECK(Stc,this,1) + +/* Test class membership. */ +#define astIsAStc(this) astINVOKE_ISA(Stc,this) + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitStc(mem,size,init,vtab,name,reg,ncoords,coords) \ +astINVOKE(O,astInitStc_(mem,size,init,vtab,name,reg,ncoords,coords,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitStcVtab(vtab,name) astINVOKE(V,astInitStcVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadStc(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadStc_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckStc to validate Stc pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astGetStcRegion(this) astINVOKE(O,astGetStcRegion_(astCheckStc(this),STATUS_PTR)) +#define astGetStcCoord(this,icoord) astINVOKE(O,astGetStcCoord_(astCheckStc(this),icoord,STATUS_PTR)) +#define astGetStcNCoord(this) astINVOKE(V,astGetStcNCoord_(astCheckStc(this),STATUS_PTR)) +#if defined(astCLASS) /* Protected */ +#define astGetRegionClass(this) astINVOKE(V,astGetRegionClass_(astCheckStc(this),STATUS_PTR)) +#endif +#endif + + + + + diff --git a/stccatalogentrylocation.c b/stccatalogentrylocation.c new file mode 100644 index 0000000..a1ea16f --- /dev/null +++ b/stccatalogentrylocation.c @@ -0,0 +1,804 @@ +/* +*class++ +* Name: +* StcCatalogEntryLocation + +* Purpose: +* Correspond to the IVOA STCCatalogEntryLocation class. + +* Constructor Function: +c astStcCatalogEntryLocation +f AST_STCCATALOGENTRYLOCATION + +* Description: +* The StcCatalogEntryLocation class is a sub-class of Stc used to describe +* the coverage of the datasets contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The StcCatalogEntryLocation class inherits from the Stc class. + +* Attributes: +* The StcCatalogEntryLocation class does not define any new attributes beyond +* those which are applicable to all Stcs. + +* Functions: +c The StcCatalogEntryLocation class does not define any new functions beyond those +f The StcCatalogEntryLocation class does not define any new routines beyond those +* which are applicable to all Stcs. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-NOV-2004 (DSB): +* Original version. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS StcCatalogEntryLocation + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "stc.h" /* Coordinate stcs (parent class) */ +#include "channel.h" /* I/O channels */ +#include "region.h" /* Regions within coordinate systems */ +#include "stccatalogentrylocation.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(StcCatalogEntryLocation) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(StcCatalogEntryLocation,Class_Init) +#define class_vtab astGLOBAL(StcCatalogEntryLocation,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstStcCatalogEntryLocationVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstStcCatalogEntryLocation *astStcCatalogEntryLocationId_( void *, int, AstKeyMap **, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static void Dump( AstObject *, AstChannel *, int * ); + +/* Member functions. */ +/* ================= */ + +void astInitStcCatalogEntryLocationVtab_( AstStcCatalogEntryLocationVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitStcCatalogEntryLocationVtab + +* Purpose: +* Initialise a virtual function table for a StcCatalogEntryLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stccatalogentrylocation.h" +* void astInitStcCatalogEntryLocationVtab( AstStcCatalogEntryLocationVtab *vtab, const char *name ) + +* Class Membership: +* StcCatalogEntryLocation vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the StcCatalogEntryLocation class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstStcVtab *stc; /* Pointer to Stc component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitStcVtab( (AstStcVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAStcCatalogEntryLocation) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstStcVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + mapping = (AstMappingVtab *) vtab; + stc = (AstStcVtab *) vtab; + + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDump( vtab, Dump, "StcCatalogEntryLocation", "Resource coverage" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +/* None */ + +/* Destructor. */ +/* ----------- */ +/* None */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for StcCatalogEntryLocation objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the StcCatalogEntryLocation class to an output Channel. + +* Parameters: +* this +* Pointer to the StcCatalogEntryLocation whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstStcCatalogEntryLocation *this; /* Pointer to the StcCatalogEntryLocation structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the StcCatalogEntryLocation structure. */ + this = (AstStcCatalogEntryLocation *) this_object; + +/* Write out values representing the instance variables for the + StcCatalogEntryLocation class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAStcCatalogEntryLocation and astCheckStcCatalogEntryLocation functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(StcCatalogEntryLocation,Stc) +astMAKE_CHECK(StcCatalogEntryLocation) + + +AstStcCatalogEntryLocation *astStcCatalogEntryLocation_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, int *status, ...) { +/* +*++ +* Name: +c astStcCatalogEntryLocation +f AST_STCCATALOGENTRYLOCATION + +* Purpose: +* Create a StcCatalogEntryLocation. + +* Type: +* Public function. + +* Synopsis: +c #include "stccatalogentrylocation.h" +c AstStcCatalogEntryLocation *astStcCatalogEntryLocation( AstRegion *region, +c int ncoords, AstKeyMap *coords[], const char *options, ... ) +f RESULT = AST_STCCATALOGENTRYLOCATION( REGION, NCOORDS, COORDS, OPTIONS, STATUS ) + +* Class Membership: +* StcCatalogEntryLocation constructor. + +* Description: +* This function creates a new StcCatalogEntryLocation and optionally initialises its +* attributes. +* +* The StcCatalogEntryLocation class is a sub-class of Stc used to describe +* the coverage of the datasets contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Parameters: +c region +f REGION = INTEGER (Given) +* Pointer to the encapsulated Region. +c ncoords +f NCOORDS = INTEGER (Given) +c The length of the "coords" array. Supply zero if "coords" is NULL. +f The length of the COORDS array. Supply zero if COORDS should be +f ignored. +c coords +f COORDS( NCOORDS ) = INTEGER (Given) +c Pointer to an array holding "ncoords" AstKeyMap pointers (if "ncoords" +f An array holding NCOORDS AstKeyMap pointers (if NCOORDS +* is zero, the supplied value is ignored). Each supplied KeyMap +* describes the contents of a single STC element, and +* should have elements with keys given by constants AST__STCNAME, +* AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. Any of these elements may be omitted, but no other +* elements should be included. If supplied, the AST__STCNAME element +* should be a vector of character string pointers holding the "Name" +* item for each axis in the coordinate system represented by +c "region". +f REGION. +* Any other supplied elements should be scalar elements, each holding +* a pointer to a Region describing the associated item of ancillary +* information (error, resolution, size, pixel size or value). These +* Regions should describe a volume within the coordinate system +c represented by "region". +f represented by REGION. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new StcCatalogEntryLocation. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new StcCatalogEntryLocation. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astStcCatalogEntryLocation() +f AST_STCCATALOGENTRYLOCATION = INTEGER +* A pointer to the new StcCatalogEntryLocation. + +* Notes: +* - A deep copy is taken of the supplied Region. This means that +* any subsequent changes made to the encapsulated Region using the +* supplied pointer will have no effect on the Stc. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRegion *region; /* Pointer to Region structure */ + AstStcCatalogEntryLocation *new; /* Pointer to new StcCatalogEntryLocation */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the Region structure provided. */ + region = astCheckRegion( region_void ); + +/* Initialise the StcCatalogEntryLocation, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcCatalogEntryLocation( NULL, sizeof( AstStcCatalogEntryLocation ), !class_init, + &class_vtab, "StcCatalogEntryLocation", region, + ncoords, coords ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcCatalogEntryLocation's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new StcCatalogEntryLocation. */ + return new; +} + +AstStcCatalogEntryLocation *astStcCatalogEntryLocationId_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, ... ) { +/* +* Name: +* astStcCatalogEntryLocationId_ + +* Purpose: +* Create a StcCatalogEntryLocation. + +* Type: +* Private function. + +* Synopsis: +* #include "stccatalogentrylocation.h" +* AstStcCatalogEntryLocation *astStcCatalogEntryLocationId( AstRegion *region, +* int ncoords, AstKeyMap *coords[], const char *options, ..., int *status ) + +* Class Membership: +* StcCatalogEntryLocation constructor. + +* Description: +* This function implements the external (public) interface to the +* astStcCatalogEntryLocation constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astStcCatalogEntryLocation_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astStcCatalogEntryLocation_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astStcCatalogEntryLocation_. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The ID value associated with the new StcCatalogEntryLocation. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstKeyMap **keymaps; /* Pointer to array of KeyMap pointers */ + AstRegion *region; /* Pointer to Region structure */ + AstStcCatalogEntryLocation *new;/* Pointer to new StcCatalogEntryLocation */ + int icoord; /* Keymap index */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Region pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Region. */ + region = astVerifyRegion( astMakePointer( region_void ) ); + +/* Obtain pointer from the supplied KeyMap ID's and validate the + pointers to ensure it identifies a valid KeyMap. */ + keymaps = astMalloc( sizeof( AstKeyMap * )*(size_t) ncoords ); + if( keymaps ) { + for( icoord = 0; icoord < ncoords; icoord++ ) { + keymaps[ icoord ] = astVerifyKeyMap( astMakePointer( coords[ icoord ] ) ); + } + } + +/* Initialise the StcCatalogEntryLocation, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcCatalogEntryLocation( NULL, sizeof( AstStcCatalogEntryLocation ), !class_init, + &class_vtab, "StcCatalogEntryLocation", region, + ncoords, keymaps ); + +/* Free resources. */ + keymaps = astFree( keymaps ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcCatalogEntryLocation's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new StcCatalogEntryLocation. */ + return astMakeId( new ); +} + +AstStcCatalogEntryLocation *astInitStcCatalogEntryLocation_( void *mem, size_t size, + int init, AstStcCatalogEntryLocationVtab *vtab, + const char *name, AstRegion *region, + int ncoords, AstKeyMap **coords, int *status ) { +/* +*+ +* Name: +* astInitStcCatalogEntryLocation + +* Purpose: +* Initialise a StcCatalogEntryLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stccatalogentrylocation.h" +* AstStcCatalogEntryLocation *astInitStcCatalogEntryLocation_( void *mem, size_t size, +* int init, AstStcCatalogEntryLocationVtab *vtab, +* const char *name, AstRegion *region, +* int ncoords, AstKeyMap **coords ) + +* Class Membership: +* StcCatalogEntryLocation initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new StcCatalogEntryLocation object. It allocates memory (if necessary) to accommodate +* the StcCatalogEntryLocation plus any additional data associated with the derived class. +* It then initialises a StcCatalogEntryLocation structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a StcCatalogEntryLocation at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the StcCatalogEntryLocation is to be initialised. +* This must be of sufficient size to accommodate the StcCatalogEntryLocation data +* (sizeof(StcCatalogEntryLocation)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the StcCatalogEntryLocation (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the StcCatalogEntryLocation +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the StcCatalogEntryLocation's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new StcCatalogEntryLocation. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* region +* A pointer to the Region encapsulated by the StcCatalogEntryLocation. +* ncoords +* Number of KeyMap pointers supplied in "coords". Can be zero. +* Ignored if "coords" is NULL. +* coords +* Pointer to an array of "ncoords" KeyMap pointers, or NULL if +* "ncoords" is zero. Each KeyMap defines defines a single +* element, and should have elements with keys given by constants +* AST__STCNAME, AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. These elements hold values for the corresponding +* components of the STC AstroCoords element. Any of these elements may +* be omitted, but no other elements should be included. All supplied +* elements should be vector elements, with vector length less than or +* equal to the number of axes in the supplied Region. The data type of +* all elements should be "double", except for AST__STCNAME which should +* be "character string". If no value is available for a given axis, then +* AST__BAD (or NULL for the AST__STCNAME element) should be stored in +* the vector at the index corresponding to the axis (trailing axes +* can be omitted completely from the KeyMap). + +* Returned Value: +* A pointer to the new StcCatalogEntryLocation. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstStcCatalogEntryLocation *new; /* Pointer to new StcCatalogEntryLocation */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitStcCatalogEntryLocationVtab( vtab, name ); + +/* Initialise a Stc structure (the parent class) as the first component + within the StcCatalogEntryLocation structure, allocating memory if necessary. */ + new = (AstStcCatalogEntryLocation *) astInitStc( mem, size, 0, (AstStcVtab *) vtab, + name, region, ncoords, coords ); + +/* If an error occurred, clean up by deleting the new StcCatalogEntryLocation. */ + if ( !astOK ) new = astDelete( new ); + +/* Return a pointer to the new StcCatalogEntryLocation. */ + return new; +} + +AstStcCatalogEntryLocation *astLoadStcCatalogEntryLocation_( void *mem, size_t size, AstStcCatalogEntryLocationVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadStcCatalogEntryLocation + +* Purpose: +* Load a StcCatalogEntryLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stccatalogentrylocation.h" +* AstStcCatalogEntryLocation *astLoadStcCatalogEntryLocation( void *mem, size_t size, AstStcCatalogEntryLocationVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* StcCatalogEntryLocation loader. + +* Description: +* This function is provided to load a new StcCatalogEntryLocation using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* StcCatalogEntryLocation structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a StcCatalogEntryLocation at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the StcCatalogEntryLocation is to be +* loaded. This must be of sufficient size to accommodate the +* StcCatalogEntryLocation data (sizeof(StcCatalogEntryLocation)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the StcCatalogEntryLocation (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the StcCatalogEntryLocation structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstStcCatalogEntryLocation) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new StcCatalogEntryLocation. If this is NULL, a pointer +* to the (static) virtual function table for the StcCatalogEntryLocation class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "StcCatalogEntryLocation" is used instead. + +* Returned Value: +* A pointer to the new StcCatalogEntryLocation. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcCatalogEntryLocation *new; /* Pointer to the new StcCatalogEntryLocation */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this StcCatalogEntryLocation. In this case the + StcCatalogEntryLocation belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstStcCatalogEntryLocation ); + vtab = &class_vtab; + name = "StcCatalogEntryLocation"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitStcCatalogEntryLocationVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built StcCatalogEntryLocation. */ + new = astLoadStc( mem, size, (AstStcVtab *) vtab, name, channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "StcCatalogEntryLocation" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* If an error occurred, clean up by deleting the new StcCatalogEntryLocation. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new StcCatalogEntryLocation pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + + diff --git a/stccatalogentrylocation.h b/stccatalogentrylocation.h new file mode 100644 index 0000000..f999e6a --- /dev/null +++ b/stccatalogentrylocation.h @@ -0,0 +1,223 @@ +#if !defined( STCCATALOGENTRYLOCATION_INCLUDED ) /* Include this file only once */ +#define STCCATALOGENTRYLOCATION_INCLUDED +/* +*+ +* Name: +* stccatalogentrylocation.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the StcCatalogEntryLocation class. + +* Invocation: +* #include "stccatalogentrylocation.h" + +* Description: +* This include file defines the interface to the StcCatalogEntryLocation class +* and provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The StcCatalogEntryLocation class is a sub-class of Stc used to describe +* the coverage of the datasets contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The StcCatalogEntryLocation class inherits from the Stc class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-NOV-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "stc.h" /* Coordinate stcs (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* StcCatalogEntryLocation structure. */ +/* ----------------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstStcCatalogEntryLocation { + +/* Attributes inherited from the parent class. */ + AstStc stc; /* Parent class structure */ + +} AstStcCatalogEntryLocation; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstStcCatalogEntryLocationVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstStcVtab stc_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +} AstStcCatalogEntryLocationVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstStcCatalogEntryLocationGlobals { + AstStcCatalogEntryLocationVtab Class_Vtab; + int Class_Init; +} AstStcCatalogEntryLocationGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitStcCatalogEntryLocationGlobals_( AstStcCatalogEntryLocationGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(StcCatalogEntryLocation) /* Check class membership */ +astPROTO_ISA(StcCatalogEntryLocation) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstStcCatalogEntryLocation *astStcCatalogEntryLocation_( void *, int, AstKeyMap **, const char *, int *, ...); +#else +AstStcCatalogEntryLocation *astStcCatalogEntryLocationId_( void *, int, AstKeyMap **, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstStcCatalogEntryLocation *astInitStcCatalogEntryLocation_( void *, size_t, int, AstStcCatalogEntryLocationVtab *, const char *, AstRegion *, int, AstKeyMap **, int * ); + +/* Vtab initialiser. */ +void astInitStcCatalogEntryLocationVtab_( AstStcCatalogEntryLocationVtab *, const char *, int * ); + +/* Loader. */ +AstStcCatalogEntryLocation *astLoadStcCatalogEntryLocation_( void *, size_t, AstStcCatalogEntryLocationVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckStcCatalogEntryLocation(this) astINVOKE_CHECK(StcCatalogEntryLocation,this,0) +#define astVerifyStcCatalogEntryLocation(this) astINVOKE_CHECK(StcCatalogEntryLocation,this,1) + +/* Test class membership. */ +#define astIsAStcCatalogEntryLocation(this) astINVOKE_ISA(StcCatalogEntryLocation,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astStcCatalogEntryLocation astINVOKE(F,astStcCatalogEntryLocation_) +#else +#define astStcCatalogEntryLocation astINVOKE(F,astStcCatalogEntryLocationId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitStcCatalogEntryLocation(mem,size,init,vtab,name,region,ncoords,coords) \ +astINVOKE(O,astInitStcCatalogEntryLocation_(mem,size,init,vtab,name,region,ncoords,coords,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitStcCatalogEntryLocationVtab(vtab,name) astINVOKE(V,astInitStcCatalogEntryLocationVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadStcCatalogEntryLocation(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadStcCatalogEntryLocation_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckStcCatalogEntryLocation to validate StcCatalogEntryLocation pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#endif +#endif + + + + + diff --git a/stcobsdatalocation.c b/stcobsdatalocation.c new file mode 100644 index 0000000..777d29e --- /dev/null +++ b/stcobsdatalocation.c @@ -0,0 +1,1051 @@ +/* +*class++ +* Name: +* StcObsDataLocation + +* Purpose: +* Correspond to the IVOA ObsDataLocation class. + +* Constructor Function: +c astStcObsDataLocation +f AST_STCOBSDATALOCATION + +* Description: +* The StcObsDataLocation class is a sub-class of Stc used to describe +* the coordinate space occupied by a particular observational dataset. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html +* +* An STC ObsDataLocation element specifies the extent of the +* observation within a specified coordinate system, and also specifies +* the observatory location within a second coordinate system. +* +* The AST StcObsDataLocation class inherits from Stc, and therefore +* an StcObsDataLocation can be used directly as an Stc. When used +* in this way, the StcObsDataLocation describes the location of the +* observation (not the observatory). +* +* Eventually, this class will have a method for returning an Stc +* describing the observatory location. However, AST currently does not +* include any classes of Frame for describing terrestrial or solar +* system positions. Therefore, the provision for returning observatory +* location as an Stc is not yet available. However, for terrestrial +* observations, the position of the observatory can still be recorded +* using the ObsLon and ObsLat attributes of the Frame encapsulated +* within the Stc representing the observation location (this assumes +* the observatory is located at sea level). + +* Inheritance: +* The StcObsDataLocation class inherits from the Stc class. + +* Attributes: +* The StcObsDataLocation class does not define any new attributes beyond +* those which are applicable to all Stcs. + +* Functions: +c The StcObsDataLocation class does not define any new functions beyond those +f The StcObsDataLocation class does not define any new routines beyond those +* which are applicable to all Stcs. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 25-APR-2005 (DSB): +* Original version. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS StcObsDataLocation + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "stc.h" /* Coordinate stcs (parent class) */ +#include "channel.h" /* I/O channels */ +#include "region.h" /* Regions within coordinate systems */ +#include "pointlist.h" /* Points within coordinate systems */ +#include "stcobsdatalocation.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); + + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(StcObsDataLocation) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(StcObsDataLocation,Class_Init) +#define class_vtab astGLOBAL(StcObsDataLocation,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstStcObsDataLocationVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstStcObsDataLocation *astStcObsDataLocationId_( void *, int, AstKeyMap **, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void StcSetObs( AstStcObsDataLocation *, AstPointList *, int * ); + +static int GetObjSize( AstObject *, int * ); +/* Member functions. */ +/* ================= */ +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "stcobsdatalocation.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* StcObsDataLocation member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied StcObsDataLocation, +* in bytes. + +* Parameters: +* this +* Pointer to the StcObsDataLocation. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstStcObsDataLocation *this; /* Pointer to StcObsDataLocation structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the StcObsDataLocation structure. */ + this = (AstStcObsDataLocation *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astGetObjSize( this->obs ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + + +void astInitStcObsDataLocationVtab_( AstStcObsDataLocationVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitStcObsDataLocationVtab + +* Purpose: +* Initialise a virtual function table for a StcObsDataLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcobsdatalocation.h" +* void astInitStcObsDataLocationVtab( AstStcObsDataLocationVtab *vtab, const char *name ) + +* Class Membership: +* StcObsDataLocation vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the StcObsDataLocation class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstStcVtab *stc; /* Pointer to Stc component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitStcVtab( (AstStcVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAStcObsDataLocation) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstStcVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + stc = (AstStcVtab *) vtab; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + vtab->StcSetObs = StcSetObs; + +/* Declare the copy constructor, destructor and class dump functions. */ + astSetDump( vtab, Dump, "StcObsDataLocation", "Observation coverage" ); + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static void StcSetObs( AstStcObsDataLocation *this, AstPointList *obs, int *status ) { +/* +*+ +* Name: +* astStcSetObs + +* Purpose: +* Set the observatory position within an StcObsDataLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "region.h" +* void astStcSetObs( AstStcObsDataLocation *this, AstPointList *obs ) + +* Class Membership: +* StcObsDataLocation virtual function + +* Description: +* This function stores a clone of the supplied PointList pointer +* within the supplied StcObsDataLocation, first annulling any +* pointer already stored in the StcObsDataLocation. + +* Parameters: +* this +* Pointer to the StcObsDataLocation. +* obs +* Pointer to a PointList defining the observatory position. NULL +* may be supplied in which case any existing observatory position +* is removed. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Free any existing obseravtory position PointList. */ + if( this->obs ) this->obs = astAnnul( this->obs ); + +/* Store any supplied pointer. */ + if( obs ) this->obs = astClone( obs ); + +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for StcObsDataLocation objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for StcObsDataLocation +* objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Regions within the StcObsDataLocation. +*/ + +/* Local Variables: */ + AstStcObsDataLocation *in; /* Pointer to input StcObsDataLocation */ + AstStcObsDataLocation *out; /* Pointer to output StcObsDataLocation */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output StcObsDataLocations. */ + in = (AstStcObsDataLocation *) objin; + out = (AstStcObsDataLocation *) objout; + +/* For safety, start by clearing any references to the input component + Regions, etc, from the output StcObsDataLocation. */ + out->obs = NULL; + +/* Make a copy of the Observatory location */ + if( in->obs ) out->obs = astCopy( in->obs ); + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for StcObsDataLocation objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for StcObsDataLocation objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstStcObsDataLocation *this; /* Pointer to StcObsDataLocation */ + +/* Obtain a pointer to the StcObsDataLocation structure. */ + this = (AstStcObsDataLocation *) obj; + +/* Annul the pointer to the observatory location Region. */ + if( this->obs ) this->obs = astAnnul( this->obs ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for StcObsDataLocation objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the StcObsDataLocation class to an output Channel. + +* Parameters: +* this +* Pointer to the StcObsDataLocation whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstStcObsDataLocation *this; /* Pointer to the StcObsDataLocation structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the StcObsDataLocation structure. */ + this = (AstStcObsDataLocation *) this_object; + +/* Write out values representing the instance variables for the + StcObsDataLocation class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Observatory position. */ +/* --------------------- */ + astWriteObject( channel, "ObsLoc", 1, 1, this->obs, "Observatory position" ); + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAStcObsDataLocation and astCheckStcObsDataLocation functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(StcObsDataLocation,Stc) +astMAKE_CHECK(StcObsDataLocation) + + +AstStcObsDataLocation *astStcObsDataLocation_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, int *status, ...) { +/* +*++ +* Name: +c astStcObsDataLocation +f AST_STCOBSDATALOCATION + +* Purpose: +* Create a StcObsDataLocation. + +* Type: +* Public function. + +* Synopsis: +c #include "stcobsdatalocation.h" +c AstStcObsDataLocation *astStcObsDataLocation( AstRegion *region, +c int ncoords, AstKeyMap *coords[], const char *options, ... ) +f RESULT = AST_STCOBSDATALOCATION( REGION, NCOORDS, COORDS, OPTIONS, STATUS ) + +* Class Membership: +* StcObsDataLocation constructor. + +* Description: +* This function creates a new StcObsDataLocation and optionally initialises its +* attributes. +* +* The StcObsDataLocation class is a sub-class of Stc used to describe +* the coverage of the datasets contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Parameters: +c region +f REGION = INTEGER (Given) +* Pointer to the encapsulated Region. +c ncoords +f NCOORDS = INTEGER (Given) +c The length of the "coords" array. Supply zero if "coords" is NULL. +f The length of the COORDS array. Supply zero if COORDS should be +f ignored. +c coords +f COORDS( NCOORDS ) = INTEGER (Given) +c Pointer to an array holding "ncoords" AstKeyMap pointers (if "ncoords" +f An array holding NCOORDS AstKeyMap pointers (if NCOORDS +* is zero, the supplied value is ignored). Each supplied KeyMap +* describes the contents of a single STC element, and +* should have elements with keys given by constants AST__STCNAME, +* AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. Any of these elements may be omitted, but no other +* elements should be included. If supplied, the AST__STCNAME element +* should be a vector of character string pointers holding the "Name" +* item for each axis in the coordinate system represented by +c "region". +f REGION. +* Any other supplied elements should be scalar elements, each holding +* a pointer to a Region describing the associated item of ancillary +* information (error, resolution, size, pixel size or value). These +* Regions should describe a volume within the coordinate system +c represented by "region". +f represented by REGION. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new StcObsDataLocation. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new StcObsDataLocation. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astStcObsDataLocation() +f AST_STCOBSDATALOCATION = INTEGER +* A pointer to the new StcObsDataLocation. + +* Notes: +* - A deep copy is taken of the supplied Region. This means that +* any subsequent changes made to the encapsulated Region using the +* supplied pointer will have no effect on the Stc. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRegion *region; /* Pointer to Region structure */ + AstStcObsDataLocation *new; /* Pointer to new StcObsDataLocation */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the Region structure provided. */ + region = astCheckRegion( region_void ); + +/* Initialise the StcObsDataLocation, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcObsDataLocation( NULL, sizeof( AstStcObsDataLocation ), !class_init, + &class_vtab, "StcObsDataLocation", region, + ncoords, coords ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcObsDataLocation's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new StcObsDataLocation. */ + return new; +} + +AstStcObsDataLocation *astStcObsDataLocationId_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, ... ) { +/* +* Name: +* astStcObsDataLocationId_ + +* Purpose: +* Create a StcObsDataLocation. + +* Type: +* Private function. + +* Synopsis: +* #include "stcobsdatalocation.h" +* AstStcObsDataLocation *astStcObsDataLocationId( AstRegion *region, +* int ncoords, AstKeyMap *coords[], const char *options, ..., int *status ) + +* Class Membership: +* StcObsDataLocation constructor. + +* Description: +* This function implements the external (public) interface to the +* astStcObsDataLocation constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astStcObsDataLocation_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astStcObsDataLocation_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astStcObsDataLocation_. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The ID value associated with the new StcObsDataLocation. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstKeyMap **keymaps; /* Pointer to array of KeyMap pointers */ + AstRegion *region; /* Pointer to Region structure */ + AstStcObsDataLocation *new; /* Pointer to new StcObsDataLocation */ + int icoord; /* Keymap index */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Region pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Region. */ + region = astVerifyRegion( astMakePointer( region_void ) ); + +/* Obtain pointer from the supplied KeyMap ID's and validate the + pointers to ensure it identifies a valid KeyMap. */ + keymaps = astMalloc( sizeof( AstKeyMap * )*(size_t) ncoords ); + if( keymaps ) { + for( icoord = 0; icoord < ncoords; icoord++ ) { + keymaps[ icoord ] = astVerifyKeyMap( astMakePointer( coords[ icoord ] ) ); + } + } + +/* Initialise the StcObsDataLocation, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcObsDataLocation( NULL, sizeof( AstStcObsDataLocation ), !class_init, + &class_vtab, "StcObsDataLocation", region, + ncoords, keymaps ); + +/* Free resources. */ + keymaps = astFree( keymaps ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcObsDataLocation's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new StcObsDataLocation. */ + return astMakeId( new ); +} + +AstStcObsDataLocation *astInitStcObsDataLocation_( void *mem, size_t size, + int init, AstStcObsDataLocationVtab *vtab, + const char *name, AstRegion *region, + int ncoords, AstKeyMap **coords, int *status ) { +/* +*+ +* Name: +* astInitStcObsDataLocation + +* Purpose: +* Initialise a StcObsDataLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcobsdatalocation.h" +* AstStcObsDataLocation *astInitStcObsDataLocation_( void *mem, size_t size, +* int init, AstStcObsDataLocationVtab *vtab, +* const char *name, AstRegion *region, +* int ncoords, AstKeyMap **coords ) + +* Class Membership: +* StcObsDataLocation initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new StcObsDataLocation object. It allocates memory (if necessary) to accommodate +* the StcObsDataLocation plus any additional data associated with the derived class. +* It then initialises a StcObsDataLocation structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a StcObsDataLocation at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the StcObsDataLocation is to be initialised. +* This must be of sufficient size to accommodate the StcObsDataLocation data +* (sizeof(StcObsDataLocation)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the StcObsDataLocation (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the StcObsDataLocation +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the StcObsDataLocation's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new StcObsDataLocation. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* region +* A pointer to the Region encapsulated by the StcObsDataLocation. +* ncoords +* Number of KeyMap pointers supplied in "coords". Can be zero. +* Ignored if "coords" is NULL. +* coords +* Pointer to an array of "ncoords" KeyMap pointers, or NULL if +* "ncoords" is zero. Each KeyMap defines defines a single +* element, and should have elements with keys given by constants +* AST__STCNAME, AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. These elements hold values for the corresponding +* components of the STC AstroCoords element. Any of these elements may +* be omitted, but no other elements should be included. All supplied +* elements should be vector elements, with vector length less than or +* equal to the number of axes in the supplied Region. The data type of +* all elements should be "double", except for AST__STCNAME which should +* be "character string". If no value is available for a given axis, then +* AST__BAD (or NULL for the AST__STCNAME element) should be stored in +* the vector at the index corresponding to the axis (trailing axes +* can be omitted completely from the KeyMap). + +* Returned Value: +* A pointer to the new StcObsDataLocation. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstStcObsDataLocation *new; /* Pointer to new StcObsDataLocation */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitStcObsDataLocationVtab( vtab, name ); + +/* Initialise a Stc structure (the parent class) as the first component + within the StcObsDataLocation structure, allocating memory if necessary. */ + new = (AstStcObsDataLocation *) astInitStc( mem, size, 0, (AstStcVtab *) vtab, + name, region, ncoords, coords ); + +/* If succesful, initialise properties of the StcObsDataLocation. */ + if( new ) { + new->obs = NULL; + } + +/* If an error occurred, clean up by deleting the new StcObsDataLocation. */ + if ( !astOK ) new = astDelete( new ); + +/* Return a pointer to the new StcObsDataLocation. */ + return new; +} + +AstStcObsDataLocation *astLoadStcObsDataLocation_( void *mem, size_t size, AstStcObsDataLocationVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadStcObsDataLocation + +* Purpose: +* Load a StcObsDataLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcobsdatalocation.h" +* AstStcObsDataLocation *astLoadStcObsDataLocation( void *mem, size_t size, AstStcObsDataLocationVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* StcObsDataLocation loader. + +* Description: +* This function is provided to load a new StcObsDataLocation using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* StcObsDataLocation structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a StcObsDataLocation at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the StcObsDataLocation is to be +* loaded. This must be of sufficient size to accommodate the +* StcObsDataLocation data (sizeof(StcObsDataLocation)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the StcObsDataLocation (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the StcObsDataLocation structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstStcObsDataLocation) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new StcObsDataLocation. If this is NULL, a pointer +* to the (static) virtual function table for the StcObsDataLocation class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "StcObsDataLocation" is used instead. + +* Returned Value: +* A pointer to the new StcObsDataLocation. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcObsDataLocation *new; /* Pointer to the new StcObsDataLocation */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this StcObsDataLocation. In this case the + StcObsDataLocation belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstStcObsDataLocation ); + vtab = &class_vtab; + name = "StcObsDataLocation"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitStcObsDataLocationVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built StcObsDataLocation. */ + new = astLoadStc( mem, size, (AstStcVtab *) vtab, name, channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "StcObsDataLocation" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Observatory position. */ +/* --------------------- */ + new->obs = astReadObject( channel, "obsloc", NULL ); + +/* If an error occurred, clean up by deleting the new StcObsDataLocation. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new StcObsDataLocation pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astStcSetObs_( AstStcObsDataLocation *this, AstPointList *obs, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,StcObsDataLocation,StcSetObs))( this, obs, status ); +} + + + + diff --git a/stcobsdatalocation.h b/stcobsdatalocation.h new file mode 100644 index 0000000..ad3b793 --- /dev/null +++ b/stcobsdatalocation.h @@ -0,0 +1,236 @@ +#if !defined( STCOBSDATALOCATION_INCLUDED ) /* Include this file only once */ +#define STCOBSDATALOCATION_INCLUDED +/* +*+ +* Name: +* stcobsdatalocation.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the StcObsDataLocation class. + +* Invocation: +* #include "stcobsdatalocation.h" + +* Description: +* This include file defines the interface to the StcObsDataLocation class +* and provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The StcObsDataLocation class is a sub-class of Stc used to describe +* the an observation contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The StcObsDataLocation class inherits from the Stc class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 25-APR-2005 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "stc.h" /* Coordinate stcs (parent class) */ +#include "pointlist.h" /* Points within coordinate systems */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* StcObsDataLocation structure. */ +/* ----------------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstStcObsDataLocation { + +/* Attributes inherited from the parent class. */ + AstStc stc; /* Parent class structure */ + +/* Attributes specific to the StcObsDataLOcation class. */ + AstPointList *obs; /* Observatory position */ + +} AstStcObsDataLocation; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstStcObsDataLocationVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstStcVtab stc_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* StcSetObs)( AstStcObsDataLocation *, AstPointList *, int * ); + +} AstStcObsDataLocationVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstStcObsDataLocationGlobals { + AstStcObsDataLocationVtab Class_Vtab; + int Class_Init; +} AstStcObsDataLocationGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitStcObsDataLocationGlobals_( AstStcObsDataLocationGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(StcObsDataLocation) /* Check class membership */ +astPROTO_ISA(StcObsDataLocation) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstStcObsDataLocation *astStcObsDataLocation_( void *, int, AstKeyMap **, const char *, int *, ...); +#else +AstStcObsDataLocation *astStcObsDataLocationId_( void *, int, AstKeyMap **, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstStcObsDataLocation *astInitStcObsDataLocation_( void *, size_t, int, AstStcObsDataLocationVtab *, const char *, AstRegion *, int, AstKeyMap **, int * ); + +/* Vtab initialiser. */ +void astInitStcObsDataLocationVtab_( AstStcObsDataLocationVtab *, const char *, int * ); + +/* Loader. */ +AstStcObsDataLocation *astLoadStcObsDataLocation_( void *, size_t, AstStcObsDataLocationVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + + +#if defined(astCLASS) /* Protected */ +void astStcSetObs_( AstStcObsDataLocation *, AstPointList *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckStcObsDataLocation(this) astINVOKE_CHECK(StcObsDataLocation,this,0) +#define astVerifyStcObsDataLocation(this) astINVOKE_CHECK(StcObsDataLocation,this,1) + +/* Test class membership. */ +#define astIsAStcObsDataLocation(this) astINVOKE_ISA(StcObsDataLocation,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astStcObsDataLocation astINVOKE(F,astStcObsDataLocation_) +#else +#define astStcObsDataLocation astINVOKE(F,astStcObsDataLocationId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitStcObsDataLocation(mem,size,init,vtab,name,region,ncoords,coords) \ +astINVOKE(O,astInitStcObsDataLocation_(mem,size,init,vtab,name,region,ncoords,coords,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitStcObsDataLocationVtab(vtab,name) astINVOKE(V,astInitStcObsDataLocationVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadStcObsDataLocation(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadStcObsDataLocation_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckStcObsDataLocation to validate StcObsDataLocation pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astStcSetObs(this,obs) \ +astINVOKE(V,astStcSetObs_(astCheckStcObsDataLocation(this),obs?astCheckPointList(obs):NULL,STATUS_PTR)) +#endif +#endif + + + + + diff --git a/stcresourceprofile.c b/stcresourceprofile.c new file mode 100644 index 0000000..4ae6cc0 --- /dev/null +++ b/stcresourceprofile.c @@ -0,0 +1,807 @@ +/* +*class++ +* Name: +* StcResourceProfile + +* Purpose: +* Correspond to the IVOA STCResourceProfile class. + +* Constructor Function: +c astStcResourceProfile +f AST_STCRESOURCEPROFILE + +* Description: +* The StcResourceProfile class is a sub-class of Stc used to describe +* the coverage of the datasets contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The StcResourceProfile class inherits from the Stc class. + +* Attributes: +* The StcResourceProfile class does not define any new attributes beyond +* those which are applicable to all Stcs. + +* Functions: +c The StcResourceProfile class does not define any new functions beyond those +f The StcResourceProfile class does not define any new routines beyond those +* which are applicable to all Stcs. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-NOV-2004 (DSB): +* Original version. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS StcResourceProfile + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "stc.h" /* Coordinate stcs (parent class) */ +#include "channel.h" /* I/O channels */ +#include "region.h" /* Regions within coordinate systems */ +#include "stcresourceprofile.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(StcResourceProfile) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(StcResourceProfile,Class_Init) +#define class_vtab astGLOBAL(StcResourceProfile,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstStcResourceProfileVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstStcResourceProfile *astStcResourceProfileId_( void *, int, AstKeyMap **, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static void Dump( AstObject *, AstChannel *, int * ); + +/* Member functions. */ +/* ================= */ + +void astInitStcResourceProfileVtab_( AstStcResourceProfileVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitStcResourceProfileVtab + +* Purpose: +* Initialise a virtual function table for a StcResourceProfile. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcresourceprofile.h" +* void astInitStcResourceProfileVtab( AstStcResourceProfileVtab *vtab, const char *name ) + +* Class Membership: +* StcResourceProfile vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the StcResourceProfile class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstStcVtab *stc; /* Pointer to Stc component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitStcVtab( (AstStcVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAStcResourceProfile) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstStcVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + mapping = (AstMappingVtab *) vtab; + stc = (AstStcVtab *) vtab; + + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDump( vtab, Dump, "StcResourceProfile", "Resource coverage" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +/* None */ + +/* Destructor. */ +/* ----------- */ +/* None */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for StcResourceProfile objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the StcResourceProfile class to an output Channel. + +* Parameters: +* this +* Pointer to the StcResourceProfile whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstStcResourceProfile *this; /* Pointer to the StcResourceProfile structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the StcResourceProfile structure. */ + this = (AstStcResourceProfile *) this_object; + +/* Write out values representing the instance variables for the + StcResourceProfile class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAStcResourceProfile and astCheckStcResourceProfile functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(StcResourceProfile,Stc) +astMAKE_CHECK(StcResourceProfile) + + +AstStcResourceProfile *astStcResourceProfile_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, int *status, ...) { +/* +*++ +* Name: +c astStcResourceProfile +f AST_STCRESOURCEPROFILE + +* Purpose: +* Create a StcResourceProfile. + +* Type: +* Public function. + +* Synopsis: +c #include "stcresourceprofile.h" +c AstStcResourceProfile *astStcResourceProfile( AstRegion *region, +c int ncoords, AstKeyMap *coords[], const char *options, ... ) +f RESULT = AST_STCRESOURCEPROFILE( REGION, NCOORDS, COORDS, OPTIONS, STATUS ) + +* Class Membership: +* StcResourceProfile constructor. + +* Description: +* This function creates a new StcResourceProfile and optionally initialises its +* attributes. +* +* The StcResourceProfile class is a sub-class of Stc used to describe +* the coverage of the datasets contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Parameters: +c region +f REGION = INTEGER (Given) +* Pointer to the encapsulated Region. +c ncoords +f NCOORDS = INTEGER (Given) +c The length of the "coords" array. Supply zero if "coords" is NULL. +f The length of the COORDS array. Supply zero if COORDS should be +f ignored. +c coords +f COORDS( NCOORDS ) = INTEGER (Given) +c Pointer to an array holding "ncoords" AstKeyMap pointers (if "ncoords" +f An array holding NCOORDS AstKeyMap pointers (if NCOORDS +* is zero, the supplied value is ignored). Each supplied KeyMap +* describes the contents of a single STC element, and +* should have elements with keys given by constants AST__STCNAME, +* AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. Any of these elements may be omitted, but no other +* elements should be included. If supplied, the AST__STCNAME element +* should be a vector of character string pointers holding the "Name" +* item for each axis in the coordinate system represented by +c "region". +f REGION. +* Any other supplied elements should be scalar elements, each holding +* a pointer to a Region describing the associated item of ancillary +* information (error, resolution, size, pixel size or value). These +* Regions should describe a volume within the coordinate system +c represented by "region". +f represented by REGION. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new StcResourceProfile. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new StcResourceProfile. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astStcResourceProfile() +f AST_STCRESOURCEPROFILE = INTEGER +* A pointer to the new StcResourceProfile. + +* Notes: +* - A deep copy is taken of the supplied Region. This means that +* any subsequent changes made to the encapsulated Region using the +* supplied pointer will have no effect on the Stc. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRegion *region; /* Pointer to Region structure */ + AstStcResourceProfile *new; /* Pointer to new StcResourceProfile */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the Region structure provided. */ + region = astCheckRegion( region_void ); + +/* Initialise the StcResourceProfile, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcResourceProfile( NULL, sizeof( AstStcResourceProfile ), !class_init, + &class_vtab, "StcResourceProfile", region, + ncoords, coords ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcResourceProfile's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new StcResourceProfile. */ + return new; +} + +AstStcResourceProfile *astStcResourceProfileId_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, ... ) { +/* +* Name: +* astStcResourceProfileId_ + +* Purpose: +* Create a StcResourceProfile. + +* Type: +* Private function. + +* Synopsis: +* #include "stcresourceprofile.h" +* AstStcResourceProfile *astStcResourceProfileId( AstRegion *region, +* int ncoords, AstKeyMap *coords[], const char *options, ... ) + +* Class Membership: +* StcResourceProfile constructor. + +* Description: +* This function implements the external (public) interface to the +* astStcResourceProfile constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astStcResourceProfile_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astStcResourceProfile_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astStcResourceProfile_. + +* Returned Value: +* The ID value associated with the new StcResourceProfile. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstKeyMap **keymaps; /* Pointer to array of KeyMap pointers */ + AstRegion *region; /* Pointer to Region structure */ + AstStcResourceProfile *new; /* Pointer to new StcResourceProfile */ + int icoord; /* Keymap index */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + astGET_GLOBALS(NULL); /* Get a pointer to the thread specific global data structure. */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Region pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Region. */ + region = astVerifyRegion( astMakePointer( region_void ) ); + +/* Obtain pointer from the supplied KeyMap ID's and validate the + pointers to ensure it identifies a valid KeyMap. */ + keymaps = astMalloc( sizeof( AstKeyMap * )*(size_t) ncoords ); + if( keymaps ) { + for( icoord = 0; icoord < ncoords; icoord++ ) { + keymaps[ icoord ] = astVerifyKeyMap( astMakePointer( coords[ icoord ] ) ); + } + } + +/* Initialise the StcResourceProfile, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcResourceProfile( NULL, sizeof( AstStcResourceProfile ), !class_init, + &class_vtab, "StcResourceProfile", region, + ncoords, keymaps ); + +/* Free resources. */ + keymaps = astFree( keymaps ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcResourceProfile's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new StcResourceProfile. */ + return astMakeId( new ); +} + +AstStcResourceProfile *astInitStcResourceProfile_( void *mem, size_t size, + int init, AstStcResourceProfileVtab *vtab, + const char *name, AstRegion *region, + int ncoords, AstKeyMap **coords, int *status ) { +/* +*+ +* Name: +* astInitStcResourceProfile + +* Purpose: +* Initialise a StcResourceProfile. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcresourceprofile.h" +* AstStcResourceProfile *astInitStcResourceProfile_( void *mem, size_t size, +* int init, AstStcResourceProfileVtab *vtab, +* const char *name, AstRegion *region, +* int ncoords, AstKeyMap **coords ) + +* Class Membership: +* StcResourceProfile initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new StcResourceProfile object. It allocates memory (if necessary) to accommodate +* the StcResourceProfile plus any additional data associated with the derived class. +* It then initialises a StcResourceProfile structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a StcResourceProfile at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the StcResourceProfile is to be initialised. +* This must be of sufficient size to accommodate the StcResourceProfile data +* (sizeof(StcResourceProfile)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the StcResourceProfile (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the StcResourceProfile +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the StcResourceProfile's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new StcResourceProfile. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* region +* A pointer to the Region encapsulated by the StcResourceProfile. +* ncoords +* Number of KeyMap pointers supplied in "coords". Can be zero. +* Ignored if "coords" is NULL. +* coords +* Pointer to an array of "ncoords" KeyMap pointers, or NULL if +* "ncoords" is zero. Each KeyMap defines defines a single +* element, and should have elements with keys given by constants +* AST__STCNAME, AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. These elements hold values for the corresponding +* components of the STC AstroCoords element. Any of these elements may +* be omitted, but no other elements should be included. All supplied +* elements should be vector elements, with vector length less than or +* equal to the number of axes in the supplied Region. The data type of +* all elements should be "double", except for AST__STCNAME which should +* be "character string". If no value is available for a given axis, then +* AST__BAD (or NULL for the AST__STCNAME element) should be stored in +* the vector at the index corresponding to the axis (trailing axes +* can be omitted completely from the KeyMap). + +* Returned Value: +* A pointer to the new StcResourceProfile. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstStcResourceProfile *new; /* Pointer to new StcResourceProfile */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitStcResourceProfileVtab( vtab, name ); + +/* Initialise a Stc structure (the parent class) as the first component + within the StcResourceProfile structure, allocating memory if necessary. */ + new = (AstStcResourceProfile *) astInitStc( mem, size, 0, (AstStcVtab *) vtab, + name, region, ncoords, coords ); + +/* If an error occurred, clean up by deleting the new StcResourceProfile. */ + if ( !astOK ) new = astDelete( new ); + +/* Return a pointer to the new StcResourceProfile. */ + return new; +} + +AstStcResourceProfile *astLoadStcResourceProfile_( void *mem, size_t size, AstStcResourceProfileVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadStcResourceProfile + +* Purpose: +* Load a StcResourceProfile. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcresourceprofile.h" +* AstStcResourceProfile *astLoadStcResourceProfile( void *mem, size_t size, AstStcResourceProfileVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* StcResourceProfile loader. + +* Description: +* This function is provided to load a new StcResourceProfile using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* StcResourceProfile structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a StcResourceProfile at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the StcResourceProfile is to be +* loaded. This must be of sufficient size to accommodate the +* StcResourceProfile data (sizeof(StcResourceProfile)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the StcResourceProfile (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the StcResourceProfile structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstStcResourceProfile) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new StcResourceProfile. If this is NULL, a pointer +* to the (static) virtual function table for the StcResourceProfile class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "StcResourceProfile" is used instead. + +* Returned Value: +* A pointer to the new StcResourceProfile. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcResourceProfile *new; /* Pointer to the new StcResourceProfile */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this StcResourceProfile. In this case the + StcResourceProfile belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstStcResourceProfile ); + vtab = &class_vtab; + name = "StcResourceProfile"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitStcResourceProfileVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built StcResourceProfile. */ + new = astLoadStc( mem, size, (AstStcVtab *) vtab, name, channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "StcResourceProfile" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* If an error occurred, clean up by deleting the new StcResourceProfile. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new StcResourceProfile pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + + diff --git a/stcresourceprofile.h b/stcresourceprofile.h new file mode 100644 index 0000000..8157dbb --- /dev/null +++ b/stcresourceprofile.h @@ -0,0 +1,223 @@ +#if !defined( STCRESOURCEPROFILE_INCLUDED ) /* Include this file only once */ +#define STCRESOURCEPROFILE_INCLUDED +/* +*+ +* Name: +* stcresourceprofile.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the StcResourceProfile class. + +* Invocation: +* #include "stcresourceprofile.h" + +* Description: +* This include file defines the interface to the StcResourceProfile class +* and provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The StcResourceProfile class is a sub-class of Stc used to describe +* the coverage of the datasets contained in some VO resource. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The StcResourceProfile class inherits from the Stc class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-NOV-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "stc.h" /* Coordinate stcs (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* StcResourceProfile structure. */ +/* ----------------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstStcResourceProfile { + +/* Attributes inherited from the parent class. */ + AstStc stc; /* Parent class structure */ + +} AstStcResourceProfile; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstStcResourceProfileVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstStcVtab stc_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +} AstStcResourceProfileVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstStcResourceProfileGlobals { + AstStcResourceProfileVtab Class_Vtab; + int Class_Init; +} AstStcResourceProfileGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitStcResourceProfileGlobals_( AstStcResourceProfileGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(StcResourceProfile) /* Check class membership */ +astPROTO_ISA(StcResourceProfile) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstStcResourceProfile *astStcResourceProfile_( void *, int, AstKeyMap **, const char *, int *, ...); +#else +AstStcResourceProfile *astStcResourceProfileId_( void *, int, AstKeyMap **, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstStcResourceProfile *astInitStcResourceProfile_( void *, size_t, int, AstStcResourceProfileVtab *, const char *, AstRegion *, int, AstKeyMap **, int * ); + +/* Vtab initialiser. */ +void astInitStcResourceProfileVtab_( AstStcResourceProfileVtab *, const char *, int * ); + +/* Loader. */ +AstStcResourceProfile *astLoadStcResourceProfile_( void *, size_t, AstStcResourceProfileVtab *, + const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckStcResourceProfile(this) astINVOKE_CHECK(StcResourceProfile,this,0) +#define astVerifyStcResourceProfile(this) astINVOKE_CHECK(StcResourceProfile,this,1) + +/* Test class membership. */ +#define astIsAStcResourceProfile(this) astINVOKE_ISA(StcResourceProfile,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astStcResourceProfile astINVOKE(F,astStcResourceProfile_) +#else +#define astStcResourceProfile astINVOKE(F,astStcResourceProfileId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitStcResourceProfile(mem,size,init,vtab,name,region,ncoords,coords) \ +astINVOKE(O,astInitStcResourceProfile_(mem,size,init,vtab,name,region,ncoords,coords,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitStcResourceProfileVtab(vtab,name) astINVOKE(V,astInitStcResourceProfileVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadStcResourceProfile(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadStcResourceProfile_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckStcResourceProfile to validate StcResourceProfile pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#endif +#endif + + + + + diff --git a/stcs-ex1.txt b/stcs-ex1.txt new file mode 100644 index 0000000..f46fb6a --- /dev/null +++ b/stcs-ex1.txt @@ -0,0 +1,13 @@ +TimeInterval TT GEOCENTER +1996-01-01T00:00:00 1996-01-01T00:30:00 +Time MJD 50814.0 Error 1.2 +Resolution 0.8 PixSize 1024.0 +Circle ICRS GEOCENTER 179.0 -11.5 0.5 +Position 179.0 -11.5 Error 0.000889 +Resolution 0.001778 Size 0.000333 0.000278 +PixSize 0.000083 0.000083 +Spectral BARYCENTER 1420.4 unit MHz +Resolution 10.0 +RedshiftInterval BARYCENTER VELOCITY OPTICAL +200.0 2300.0 Redshift 300.0 +Resolution 0.7 PixSize 0.3 diff --git a/stcschan-demo1.c b/stcschan-demo1.c new file mode 100644 index 0000000..171e78c --- /dev/null +++ b/stcschan-demo1.c @@ -0,0 +1,288 @@ +/* Name: + stcschan-demo1.c + + Purpose: + A demonstration of the facilities provided by the AST library + for reading STC metadata encoded using the STC-S linear string + format. + + Description: + This program reads an STC-S description from a disk file, and + tests a given position to see if it is inside or outside the + AstroCoordsArea specified by the STC-S description. + + Usage: + % stcschan-demo1 ... + + : The path to the disk file containing the STC-S + description. + + ...: The axis values at the position to be tested. + If insufficient values are supplied, a message describing the + required values is displayed (label, units, etc). + + Example: + % stcschan-demo1 stcs-ex1.txt 1996-01-01T00:00:15 11:56:00 -11:30:00 \ + 1420.4 1000 + + To compile and link: + Assuming your starlink distribution is in "/star": + + % gcc -o stcschan-demo1 stcschan-demo1.c -L/star/lib \ + -I/star/include `ast_link` +*/ + +/* Include system headers. */ +#include +#include + +/* Include the AST library header. */ +#include "ast.h" + +/* Maximum number of axes in an STC-S AstroCoordSystem. */ +#define MAX_AXES 5 + +/* Maximum allowed length for a single line of text form the disk file. */ +#define MAX_LINE_LEN 500 + +/* Prototype for the function that reads text from the disk file. */ +const char *source( void ); + + +int main( int argc, char **argv ){ + +/* Local variables: */ + AstKeyMap *warnings; + AstObject *object; + AstRegion *region; + AstStcsChan *channel; + FILE *fd; + char attrib[ 9 ]; + char key[ 15 ]; + const char *message; + double inpos[ MAX_AXES ]; + double outpos[ MAX_AXES ]; + int axis; + int iwarn; + int naxis; + int nc; + int status; + +/* Initialised the returned system status to indicate success. */ + status = 0; + +/* Check a file was specified on the command line, and attempt to open it + for read access. */ + if( argc < 2 ) { + printf( "Usage: stcschan-demo1 ...\n" ); + status = 1; + } else { + fd = fopen( argv[ 1 ], "r" ); + if( !fd ) { + printf("Failed to open input file '%s'.\n", argv[ 1 ] ); + status = 1; + } + } + +/* If a disk file was opened successfully... */ + if( !status ) { + +/* Start an AST object context. This means we do not need to annull + each AST Object individually. Instead, all Objects created within + this context will be annulled automatically by the corresponding + invocation of astEnd. */ + astBegin; + +/* Create an StcsChan. This is the object that converts external STC-S + descriptions into corresponding AST Objects. Tell it to use the + "source" function for obtaining lines of text from the disk file. Also + tell it to store all warnings generated by the conversion for later + use. Other attributes of the StcsChan class retain their default + values. */ + channel = astStcsChan( source, NULL, "ReportLevel=3" ); + +/* Associate the descriptor for the input disk file with the StcsChan. + This makes it available to the "source" function. Since this + application is single threaded, we could instead have made "fd" a + global variable, but the ChannelData facility is used here to illustrate + how to pass data to a source or sink function safely in a multi-threaded + application. */ + astPutChannelData( channel, fd ); + +/* The default behaviour of the astRead function when used on an StcsChan is + to read and return the AstroCoordArea as an AST Region. This behaviour + can be changed by assigning appropriate values to the StcsChan attributes + "StcsArea", "StcsCoords" and "StcsProps". Options exist to return the + AstroCoords as an AST PointList, and/or to return the individual + property values read from the STC-S text in the form of an AST KeyMap + (a sort of hashmap). For now, just take the default action of reading the + AstroCoordsArea. */ + object = astRead( channel ); + +/* The astRead function is a generic function and so returns a generic + AstObject pointer. Check an Object was created successfully. */ + if( !object ) { + printf( "Failed to read an AST Object from file '%s'.\n", + argv[ 1 ] ); + status = 1; + +/* Now check that the object read is actually an AST Region, rather than + some other class of AST Object. */ + } else if( !astIsARegion( object ) ) { + printf( "Expected a Region but read a %s from file '%s'.\n", + astGetC( object, "Class" ), argv[ 1 ] ); + status = 1; + +/* We now now know we have a Region so it is safe to use the pointer + returned by astRead as a Region pointer. Do the cast now to avoid + repeated casting in future. */ + } else { + region = (AstRegion *) object; + +/* Get the number of axes in the AstroCoordSystem, and check it is not + larger than expected. */ + naxis = astGetI( region, "Naxes" ); + if( naxis > MAX_AXES ) { + printf( "The coordinate system read from file '%s' has " + "too many axes (%d). Up to %d axes are allowed.\n", + argv[ 1 ], naxis, MAX_AXES ); + status = 1; + +/* Now check that the correct number of axis values were supplied on the + command line. If not, issue a warning message and give details of the + label and units for each axis. Note, AST axis indices are one-based, + in the range 1 to "Naxes". */ + } else if( argc != 2 + naxis ) { + printf( "The coordinate system read from file '%s' has " + "%d axes, but %d axis values were supplied on the " + "command line. ", argv[ 1 ], naxis, argc - 2 ); + + printf( "Values are required for the following axes:\n"); + + for( axis = 1; axis <= naxis; axis++ ) { + sprintf( attrib, "Label(%d)", axis ); + printf( " Axis %d: %s ", axis, astGetC( region, attrib ) ); + sprintf( attrib, "Unit(%d)", axis ); + printf( "(%s)\n", astGetC( region, attrib ) ); + } + + status = 1; + +/* If the correct number of axis values was supplied on the command line, + convert the supplied strings into floating point axis values. Each + class of axis has its own formatting and unformatting rules that are + controlled by various attributes such as "Format" and "Digits". Values + for these attributes could be stored in the Region if different + unformatting conventions were preferred. */ + } else { + + for( axis = 1; axis <= naxis; axis++ ) { + + nc = astUnformat( region, axis, argv[ axis + 1 ], + inpos + axis - 1 ); + + if( nc != strlen( argv[ axis + 1 ] ) ) { + sprintf( attrib, "Label(%d)", axis ); + printf( "Failed to interpret '%s' as a value for axis " + "%d (%s).\n", argv[ axis + 1 ], axis, + astGetC( region, attrib ) ); + status = 1; + break; + } else { + printf("%g ", inpos[ axis - 1 ] ); + } + } + printf("\n"); + } + +/* If we have obtained a full set of floating point axis values, use the + Region as a Mapping to transform the supplied position. When a Region + is used as a Mapping, the transformation leaves all axis values + unchanged for interior positions, but assigns the magic value AST__BAD + to all axes for exterior positions. */ + if( !status ) { + astTranN( region, 1, naxis, 1, inpos, 1, naxis, 1, outpos ); + +/* Issue a message describing the position tested and indicating if it is + inside or outside the AstroCoordsArea. */ + printf( "\nThe position ( %s=%s", astGetC( region, "Symbol(1)" ), + argv[ 2 ] ); + + for( axis = 2; axis <= naxis; axis++ ) { + sprintf( attrib, "Symbol(%d)", axis ); + printf(", %s=%s", astGetC( region, attrib ), argv[ axis + 1 ] ); + } + + printf( " ) is " ); + + if( outpos[ 0 ] == AST__BAD ) { + printf( "OUTSIDE" ); + } else { + printf( "INSIDE" ); + } + + printf( " the region read from file '%s'.\n\n", argv[ 1 ] ); + + } + } + +/* We asked the StcsChan to record any warnings that were generated + whilst converting the STC-S description into a corresponding AST + Object (a Region in this case). We now see if any such warnings were + generated by the earlier call to astRead. */ + warnings = astWarnings( channel ); + +/* If any warnings were generated, and if no other error has occurred so + far, display the warnings. */ + if( warnings && !status && astOK ) { + printf( "\nThe following warnings were issued reading file " + "'%s':\n", argv[ 1 ] ); + +/* The warnings are stored in an AST KeyMap (a sort of hashmap). Each + warning message is associated with a key of the form "Warning_1", + "Warning_2", etc. Loop round successive keys, obtaining a value for + each key from the warnings KeyMap, and displaying it. */ + iwarn = 1; + while( astOK ) { + sprintf( key, "Warning_%d", iwarn++ ); + if( astMapGet0C( warnings, key, &message ) ) { + printf( "\n- %s\n", message ); + } else { + break; + } + } + } + +/* End the AST Object context. All Objects created since the + corresponding invocation of astbegin will be annulled automatically. */ + astEnd; + +/* Close the disk file. */ + (void) fclose( fd ); + } + +/* If an error occurred in the AST library, set the retiurns system + status non-zero. */ + if( !astOK ) status = 1; + return status; +} + + + + + + + +/* This is a function that reads a line of text from the disk file and + returns it to the AST library. It is called from within the astRead + function. */ +const char *source( void ){ + static char buffer[ MAX_LINE_LEN + 2 ]; + FILE *fd = astChannelData; + return fgets( buffer, MAX_LINE_LEN + 2, fd ); +} + + + + + diff --git a/stcschan-demo2.c b/stcschan-demo2.c new file mode 100644 index 0000000..1967e0b --- /dev/null +++ b/stcschan-demo2.c @@ -0,0 +1,263 @@ +/* Name: + stcschan-demo2.c + + Purpose: + A demonstration of the facilities provided by the AST library + for reading STC metadata encoded using the STC-S linear string + format. + + Description: + This program reads a set of FITS-WCS headers from a text file, and + writes an STC-S description of the region covered by the FITS + file to standard output. + + Notes: + - The simple approach used in this demonstration will report an + error if the FITS headers describe a WCS in which some pixels have + invalid WCS axis values (such as most all sky maps, for instance). + + Usage: + % stcschan-demo2 + + : The path to a text file containing a set of FITS-WCS + headers. + + Example: + % stcschan-demo2 m31.head + + To compile and link: + Assuming your starlink distribution is in "/star": + + % gcc -o stcschan-demo2 stcschan-demo2.c -L/star/lib \ + -I/star/include `ast_link` + +*/ + +/* Include system headers. */ +#include +#include + +/* Include the AST library header. */ +#include "ast.h" + +/* Maximum number of axes in an STC-S AstroCoordSystem. */ +#define MAX_AXES 5 + +/* Maximum allowed length for a single line of text from the disk file. */ +#define MAX_LINE_LEN 100 + +/* Prototype for the function that reads text from the disk file. */ +const char *source( void ); + + + +int main( int argc, char **argv ){ + +/* Local variables: */ + AstBox *pixbox; + AstFitsChan *fchan; + AstFrame *pixfrm; + AstFrame *wcsfrm; + AstFrameSet *frameset; + AstKeyMap *warnings; + AstMapping *pix2wcs; + AstObject *object; + AstRegion *wcsbox; + AstStcsChan *schan; + FILE *fd; + char key[ 15 ]; + char keyword[ 9 ]; + const char *message; + double p1[ MAX_AXES ]; + double p2[ MAX_AXES ]; + int axis; + int iwarn; + int naxis; + int status; + +/* Initialised the returned system status to indicate success. */ + status = 0; + +/* Check a file was specified on the command line, and attempt to open it + for read access. */ + if( argc < 2 ) { + printf( "Usage: stcschan-demo2 \n" ); + status = 1; + } else { + fd = fopen( argv[ 1 ], "r" ); + if( !fd ) { + printf("Failed to open input file '%s'.\n", argv[ 1 ] ); + status = 1; + } + } + +/* If a disk file was opened successfully... */ + if( !status ) { + +/* Start an AST object context. This means we do not need to annull + each AST Object individually. Instead, all Objects created within + this context will be annulled automatically by the corresponding + invocation of astEnd. */ + astBegin; + +/* Create a FitsChan. This is the object that converts external FITS + headers into corresponding AST Objects. Tell it to use the "source" + function for obtaining lines of text from the disk file. */ + fchan = astFitsChan( source, NULL, " " ); + +/* Associate the descriptor for the input disk file with the StcsChan. + This makes it available to the "source" function. Since this + application is single threaded, we could instead have made "fd" a + global variable, but the ChannelData facility is used here to illustrate + how to pass data to a source or sink function safely in a multi-threaded + application. */ + astPutChannelData( fchan, fd ); + +/* Attempt to read the FITS heades and convert them into an AST FrameSet. */ + object = astRead( fchan ); + +/* The astRead function is a generic function and so returns a generic + AstObject pointer. Check an Object was created successfully. */ + if( !object ) { + printf( "Failed to read an AST Object from file '%s'.\n", + argv[ 1 ] ); + status = 1; + +/* Now check that the object read is actually an AST FrameSet, rather than + some other class of AST Object. */ + } else if( !astIsAFrameSet( object ) ) { + printf( "Expected a FrameSet but read a %s from file '%s'.\n", + astGetC( object, "Class" ), argv[ 1 ] ); + status = 1; + +/* We now know we have a FrameSet so it is safe to use the pointer + returned by astRead as a FrameSet pointer. Do the cast now to avoid + repeated casting in future. */ + } else { + frameset = (AstFrameSet *) object; + +/* Get a pointer to the Frame that describes the attributes of the FITS + world coordinate system. This is the current Frame in the FrameSet + read from the FITS headers. */ + wcsfrm = astGetFrame( frameset, AST__CURRENT ); + +/* Get a pointer to the Frame that describes the attributes of the FITS + pixel coordinate system. This is the base Frame in the FrameSet + read from the FITS headers. */ + pixfrm = astGetFrame( frameset, AST__BASE ); + +/* Get the Mapping that transforms pixel positions into WCS positions. + The is the Mapping from base to current Frame in the FrameSet read + from the FITS headers. */ + pix2wcs = astGetMapping( frameset, AST__BASE, AST__CURRENT ); + +/* Get the number of axes in ther pixel Frame. */ + naxis = astGetI( pixfrm, "Naxes" ); + +/* For each pixel axis, form the name of the corresponding NAXISi + keyword. */ + for( axis = 0; axis < naxis; axis++ ) { + sprintf( keyword, "NAXIS%d", axis + 1 ); + +/* Store the pixel coordinate on the current axis at the lower left corner + of the first pixel. */ + p1[ axis ] = 0.5; + +/* Get the NAXISi value for the current axis from the FITS header, and + store it in array "p2". Report an error if NAXISi is not found. */ + if( !astGetFitsF( fchan, keyword, p2 + axis ) ){ + printf("Keyword '%s' not found in header\n", keyword ); + status = 1; + break; + +/* If it is found, modify "p2" so that it holds the pixel coordinate on + the current axis at the upper right corner of the last pixel. */ + } else { + p2[ axis ] += 0.5; + } + } + } + +/* If all has gone well, create an AST Region (a Box) describing the + rectangular region of pixel coordinates covered by the pixel array. */ + if( !status ) { + pixbox = astBox( pixfrm, 1, p1, p2, NULL, " " ); + +/* Map this box into the FITS world coordinate system. The Mapping is + specified by "pix2wcs", and the attributes of the resulting axes is + described by "wcsfrm". */ + wcsbox = astMapRegion( pixbox, pix2wcs, wcsfrm ); + +/* Create an StcsChan. This is the object that converts (either way) + between external STC-S descriptions and their corresponding AST Objects. + Tell it to use the "source" function for obtaining lines of text from + the disk file. Also tell it to store all warnings generated by the + conversion for later use. Other attributes of the StcsChan class retain + their default values. */ + schan = astStcsChan( NULL, NULL, "ReportLevel=3" ); + +/* Attempt to write out the Region describing the pixel array (in WCS) + as an STC-S description. Report an error if this fails. */ + if( ! astWrite( schan, wcsbox ) && astOK ) { + printf( "Failed to convert the Region into an STC-S " + "description.\n" ); + } + } + +/* We asked the StcsChan to record any warnings that were generated + whilst converting the AST Region into a corresponding STC-S description. + We now see if any such warnings were generated by the earlier call to + astWrite. */ + warnings = astWarnings( schan ); + +/* If any warnings were generated, and if no other error has occurred so + far, display the warnings. */ + if( warnings && !status && astOK ) { + printf( "\nThe following warnings were issued:\n" ); + +/* The warnings are stored in an AST KeyMap (a sort of hashmap). Each + warning message is associated with a key of the form "Warning_1", + "Warning_2", etc. Loop round successive keys, obtaining a value for + each key from the warnings KeyMap, and displaying it. */ + iwarn = 1; + while( astOK ) { + sprintf( key, "Warning_%d", iwarn++ ); + if( astMapGet0C( warnings, key, &message ) ) { + printf( "\n- %s\n", message ); + } else { + break; + } + } + } + +/* End the AST Object context. All Objects created since the + corresponding invocation of astbegin will be annulled automatically. */ + astEnd; + +/* Close the disk file. */ + (void) fclose( fd ); + } + +/* If an error occurred in the AST library, set the retiurns system + status non-zero. */ + if( !astOK ) status = 1; + return status; +} + + + + + +/* This is a function that reads a line of text from the disk file and + returns it to the AST library. It is called from within the astRead + function. */ +const char *source( void ){ + static char buffer[ MAX_LINE_LEN + 2 ]; + FILE *fd = astChannelData; + return fgets( buffer, MAX_LINE_LEN + 2, fd ); +} + + + + + diff --git a/stcschan-demo3.c b/stcschan-demo3.c new file mode 100644 index 0000000..b7f4e6f --- /dev/null +++ b/stcschan-demo3.c @@ -0,0 +1,435 @@ +/* Name: + stcschan-demo3.c + + Purpose: + A demonstration of the facilities provided by the AST library + for reading STC metadata encoded using the STC-S linear string + format. + + Description: + This program reads an STC-S description from a text file, and also + reads a set of 2-D spatial FITS-WCS headers from another (text) file. + It then opens a specified graphics device, and displays an annotated + coordinate grid covering the region described by the FITS headers. + Finally, it draws the outline of the spatial extent of the STC-S + description over the top of the annotated coordinate grid. + + Usage: + % stcschan-demo3 + + : The path to a text file containing the STC-S + description. + + : The path to a text file containing a set of FITS-WCS + headers. + + : The name of an available PGPLOT graphics device. If not + supplied, the available device names will be listed and the program + will then exit. + + Example: + % stcschan-demo3 m31.stcs andromeda.head /xserve + + To compile and link: + Assuming your starlink distribution is in "/star": + + % gcc -o stcschan-demo3 stcschan-demo3.c -g -L/star/lib \ + -I/star/include `ast_link -pgplot` -lcpgplot + +*/ + +/* Include system headers. */ +#include +#include + +/* Include the PGPLOT header. */ +#include "cpgplot.h" + +/* Include the AST library header. */ +#include "ast.h" + +/* Maximum allowed length for a single line of text from the disk file. */ +#define MAX_LINE_LEN 100 + +/* Prototypes */ +const char *source( void ); +AstFrameSet *ReadFitsHeaders( const char *, int *, int * ); +AstRegion *ReadStcs( const char * ); + + +int main( int argc, char **argv ){ + +/* Local variables: */ + AstBox *pixbox; + AstFrame *pixfrm; + AstFrameSet *align_fs = NULL; + AstFrameSet *fset = NULL; + AstPlot *plot; + AstRegion *reg = NULL; + AstRegion *wcsbox = NULL; + AstRegion *wcsreg = NULL; + const char *dev; + double bbox[ 4 ]; + float gbox[ 4 ]; + int overlap_flag, status, naxis1, naxis2, ibase; + +/* Initialise the returned system status to indicate failure. */ + status = 1; + +/* Start an AST object context. This means we do not need to annull + each AST Object individually. Instead, all Objects created within + this context will be annulled automatically by the corresponding + invocation of astEnd. */ + astBegin; + +/* Check there are enough command line arguments. */ + if( argc < 3 ) { + printf( "Usage: stcschan-demo3 \n" ); + +/* If so, attempt to read the STC-S description, creating a corresponding + AST Region. If this is successful, attempt to read the FITS header file + and create an equivalent AST FrameSet. */ + } else { + reg = ReadStcs( argv[ 1 ] ); + if( reg ) fset = ReadFitsHeaders( argv[ 2 ], &naxis1, &naxis2 ); + } + +/* Check we obtained a Region and a FrameSet successfully. */ + if( reg && fset ){ + +/* Check that we can align the FITS WCS with the spatial axes in the + STC-S description. AST contains various built-in conversions between + standard celestial coordinate system. The necessary conversion will be + identified and used automatically if necessary to achieve alignment. + The returned object "align_fs" is a FrameSet that encapsulates the + the FITS WCS coordinate system, the STC-S spatial coordinate system and + the Mapping between them. A NULL pointer is returned if alignment is + not possible. Note, the astConvert method changes the base Frame in + any supplied FrameSet to indicate which coordinate frame was used for + alignment. So we first note the original base Frame index and then + re-instate it afterwards. */ + ibase = astGetI( fset, "Base" ); + align_fs = astConvert( reg, fset, " " ); + astSetI( fset, "Base", ibase ); + + if( !align_fs ) { + printf( "Could not align the FITS WCS with the spatial axes " + "in the STC-S\n" ); + +/* If alignment was possible, use the alignment FrameSet to create a new + Region representing the same region as the supplied STC-S, but + expressed in the coordinate system of the FITS WCS. The FrameSet class + inherits from both Frame and Mapping, and so the "align_fs" FrameSet can + be used both as the Mapping in this call, and as the Frame. When used + as a Mapping, a FrameSet represents the transformation between its base + and current Frame. When used as a Frame, a FrameSet represents its + current Frame. */ + } else { + wcsreg = astMapRegion( reg, align_fs, align_fs ); + +/* It would be nice to warn the user if the STC-S AstroCoordsArea does + not overlap the FITS grid. To do so we need a Region representing the + FITS grid. Create one now (an AST Box). First store the bounds of the + FITS grid, in pixel coordinates (i.e. a system in which the centre of + the bottom left pixel is at (1.0,1.0) ). */ + bbox[ 0 ] = 0.5; + bbox[ 1 ] = 0.5; + bbox[ 2 ] = (double) naxis1 + 0.5; + bbox[ 3 ] = (double) naxis2 + 0.5; + +/* Get a pointer to the Frame describing the FITS pixel coordinate system + (the base Frame in the FITS FrameSet). */ + pixfrm = astGetFrame( fset, AST__BASE ); + +/* Create a Box that encompasses the required range of axis values within + the pixel coordinate Frame. */ + pixbox = astBox( pixfrm, 1, bbox, bbox + 2, NULL, " " ); + +/* Create another Region that represents the same area, but in the FITS + WCS. */ + wcsbox = astMapRegion( pixbox, fset, fset ); + +/* If the previous step failed, it probably means the FITS header covers + the entire sky, resulting the corners of the pixel grid having invalid + sky positions. So cancel the error and omit the overlap test. */ + if( !wcsbox ) { + astClearStatus; + printf("\nContinuing, but omitting overlap test...\n\n"); + +/* Now see if the Region representing the FITS grid overlaps the region + read from the STC-S description.*/ + } else { + overlap_flag = astOverlap( wcsreg, wcsbox ); + + if( overlap_flag == 1 || overlap_flag == 6 ) { + printf( "\nThere is no overlap between the FITS grid and the " + "STC-S AstroCoordsArea\n\n" ); + + } else if( overlap_flag == 3 || overlap_flag == 5 ) { + printf( "\nThe FITS grid is completely contained within the " + "STC-S AstroCoordsArea\n\n" ); + } + + } + } + } + +/* Check we obtained a FITS-WCS to STC-S Mapping, and that no error has + occurred in AST. */ + if( wcsreg && astOK ){ + +/* Open PGPLOT using the specified device. Prompt the user for a device if + none was supplied on the command line. */ + if( argc < 4 ) { + dev = "?"; + } else { + dev = argv[ 3 ]; + } + if( cpgbeg( 0, dev, 1, 1 ) == 1 ) { + +/* Clear the screen. */ + cpgpage(); + +/* Ensure the graphics window has equal scales on both axes. */ + cpgwnad( 0.0f, 1.0f, 0.0f, 1.0f ); + +/* Find the extent of the graphics window, and store in an array suitable + for passing to the astPlot function. */ + cpgqwin( gbox, gbox + 2, gbox + 1, gbox + 3 ); + +/* Create an AST Plot. This is a special sort of FrameSet in which the + base Frame corresponds to graphics coordinates. All the coordinate + Frames and Mappings read from the FITS-WCS headers are added into the + Plot so that graphics can be drawn in any coordinate system. The extent + of the FITS array in pixel coordinates is mapped onto the extent of the + graphics device as returned above by cpgqwin. The AST library comes with + a driver module that provides primitive drawing functions by calling + appropriate PGFPLOT functions. It is simple to write driver modules + for other graphics systems such as Tcl/Tk, Java/Swing, etc. Set a few + graphics attributes to show the sort of thing that can be done. */ + plot = astPlot( fset, gbox, bbox, "Colour(border)=2,Colour(ticks)=2," + "Colour(axes)=2,Grid=1,Colour(grid)=3," + "Style(grid)=4" ); + +/* Draw a set of annotated coordinate axes labelling the FITS WCS axes. */ + astGrid( plot ); + +/* If there is any overlap (or if the overlap test could not be performed), + add the STC-S Region into the Plot. We use a UnitMap to connect it to + the current Frame (the FITS WCS frame). */ + if( 1 || overlap_flag == 2 || overlap_flag == 4 || !wcsbox ) { + astAddFrame( plot, AST__CURRENT, astUnitMap( 2, " " ), wcsreg ); + +/* Now draw the border round the STC-S Region. A Region is a sub-class of + Mapping and so can be used to transform positions. When a Region is used + as a Mapping, positions that are inside the Region are left unchanged + by the transformation, and positions that are outside the Region are + transformed into "bad" positions (i.e. every axis value has the nmagic + value AST__BAD indicating that the axis value is undefined). The + astBorder method is a generic function that will outline the areas + within the current coordinate Frame of the Plot that correspond to + valid (i.e. non-bad) positions. Set the colour and line thickness first + to emphasise the border. */ + astSet( plot, "Colour(border)=4,Width(border)=8" ); + (void) astBorder( plot ); + } + +/* Set the returned system status to indicate success. */ + status = 0; + +/* Close down PGPLOT. */ + cpgend(); + } + } + +/* End the AST Object context. All Objects created since the + corresponding invocation of astbegin will be annulled automatically. */ + astEnd; + +/* If an error occurred in the AST library, set the returned system + status non-zero. */ + if( !astOK ) status = 1; + return status; +} + + + + + + + +/* ------------------------------------------------------------------- + * This is a function that reads a line of text from the disk file and + * returns it to the AST library. It is called from within the astRead + * function. +*/ + +const char *source( void ){ + static char buffer[ MAX_LINE_LEN + 2 ]; + FILE *fd = astChannelData; + return fgets( buffer, MAX_LINE_LEN + 2, fd ); +} + + + +/* ------------------------------------------------------------------- + * This function reads a set of FITS-WCS headers from a given text file, + * and attempts to convert them into an AST FrameSet. If successful, a + * pointer to the FrameSet is returned. A NULL pointer is returned if + * anything goes wrong, or if the WCS is not 2-dimensional. The values of + * the NAXIS1 and NAXIS2 headers are returned in "*naxis1" and "*naxis2". +*/ + +AstFrameSet *ReadFitsHeaders( const char *file, int *naxis1, int *naxis2 ){ + AstFitsChan *chan; + AstFrameSet *result; + AstObject *object; + FILE *fd; + +/* Initialise the returned pointer to indicate that no FrameSet has yet + been read. */ + result = NULL; + +/* Attempt to open the FITS header file */ + fd = fopen( file, "r" ); + if( !fd ) { + printf("Failed to open FITS header file '%s'.\n", file ); + +/* If successful, create a FitsChan. This is the object that converts + external FITS headers into corresponding AST Objects. Tell it to use + the "source" function for obtaining lines of text from the disk file. */ + } else { + chan = astFitsChan( source, NULL, " " ); + +/* Associate the descriptor for the input disk file with the StcsChan. + This makes it available to the "source" function. Since this + application is single threaded, we could instead have made "fd" a + global variable, but the ChannelData facility is used here to illustrate + how to pass data to a source or sink function safely in a multi-threaded + application. */ + astPutChannelData( chan, fd ); + +/* Attempt to read the FITS heades and convert them into an AST FrameSet. */ + object = astRead( chan ); + +/* The astRead function is a generic function and so returns a generic + AstObject pointer. Check an Object was created successfully. */ + if( !object ) { + printf( "Failed to read an AST Object from FITS header file '%s'.\n", + file ); + +/* Now check that the object read is actually an AST FrameSet, rather than + some other class of AST Object. */ + } else if( !astIsAFrameSet( object ) ) { + printf( "Expected a FrameSet but read a %s from FITS header " + "file '%s'.\n", astGetC( object, "Class" ), file ); + +/* If the Object is a FrameSet, return the FrameSet pointer. */ + } else { + result = (AstFrameSet *) object; + +/* Check the WCS is 2-dimensional. If not, report an error and set the + returned pointer to NULL. The memory used to store the FrameSet will + be released when the current AST object context is ended (by calling + astEnd). */ + if( astGetI( result, "Naxes" ) != 2 ) { + printf( "The FITS WCS is not 2-dimensional.\n"); + result = NULL; + +/* If it is 2-dimensional, get the NAXIS1 and NAXIS2 keyword values. */ + } else { + + if( !astGetFitsI( chan, "NAXIS1", naxis1 ) ){ + printf("Keyword 'NAXIS1' not found in header\n" ); + result = NULL; + } + + if( !astGetFitsI( chan, "NAXIS2", naxis2 ) ){ + printf("Keyword 'NAXIS2' not found in header\n" ); + result = NULL; + } + + } + } + +/* Close the file. */ + fclose( fd ); + } + + return result; +} + + +/* ------------------------------------------------------------------- + * This function reads an STC-S description from a given text file, + * and attempts to convert them into an AST Region. If successful, a + * pointer to the Region is returned. A NULL pointer is returned if + * anything goes wrong. +*/ + +AstRegion *ReadStcs( const char *file ){ + AstStcsChan *chan; + AstRegion *result; + AstObject *object; + FILE *fd; + +/* Initialise the returned pointer to indicate that no Region has yet + been read. */ + result = NULL; + +/* Attempt to open the STC-S file */ + fd = fopen( file, "r" ); + if( !fd ) { + printf("Failed to open STC-S descrption file '%s'.\n", file ); + +/* If successful, create an StcsChan. This is the object that converts + external STC-S descriptions into corresponding AST Objects. Tell it to + use the "source" function for obtaining lines of text from the disk + file. */ + } else { + chan = astStcsChan( source, NULL, " " ); + +/* Associate the descriptor for the input disk file with the StcsChan. + This makes it available to the "source" function. Since this + application is single threaded, we could instead have made "fd" a + global variable, but the ChannelData facility is used here to illustrate + how to pass data to a source or sink function safely in a multi-threaded + application. */ + astPutChannelData( chan, fd ); + +/* The default behaviour of the astRead function when used on an StcsChan is + to read and return the AstroCoordArea as an AST Region. This behaviour + can be changed by assigning appropriate values to the StcsChan attributes + "StcsArea", "StcsCoords" and "StcsProps". Options exist to return the + AstroCoords as an AST PointList, and/or to return the individual + property values read from the STC-S text in the form of an AST KeyMap + (a sort of hashmap). For now, just take the default action of reading the + AstroCoordsArea. */ + object = astRead( chan ); + +/* The astRead function is a generic function and so returns a generic + AstObject pointer. Check an Object was created successfully. */ + if( !object ) { + printf( "Failed to read an AST Object from STC-S description " + "file '%s'.\n", file ); + +/* Now check that the object read is actually an AST Region, rather than + some other class of AST Object. */ + } else if( !astIsARegion( object ) ) { + printf( "Expected a Region but read a %s from STC-S description " + "file '%s'.\n", astGetC( object, "Class" ), file ); + +/* If the Object is a Region, return the Region pointer. */ + } else { + result = (AstRegion *) object; + } + +/* Close the file. */ + fclose( fd ); + } + + return result; +} + + + diff --git a/stcschan-demo4.c b/stcschan-demo4.c new file mode 100644 index 0000000..e45035f --- /dev/null +++ b/stcschan-demo4.c @@ -0,0 +1,262 @@ +/* Name: + stcschan-demo4.c + + Purpose: + A demonstration of the facilities provided by the AST library + for reading STC metadata encoded using the STC-S linear string + format. + + Description: + This program reads two STC-S descriptions from two disk files, + and tests them for overlap. The two descriptions need not refer + to the same coordinate system. Built-in conversions within AST + will be used to align them if the coordinate systems differ. + + Usage: + % stcschan-demo4 + + : The path to the disk file containing the first STC-S + description. + + : The path to the disk file containing the second STC-S + description. + + Example: + % stcschan-demo4 stcs-ex1.txt stcs-ex2.txt + + To compile and link: + Assuming your starlink distribution is in "/star": + + % gcc -o stcschan-demo4 stcschan-demo4.c -L/star/lib \ + -I/star/include `ast_link` +*/ + +/* Include system headers. */ +#include +#include + +/* Include the AST library header. */ +#include "ast.h" + +/* Maximum number of axes in an STC-S AstroCoordSystem. */ +#define MAX_AXES 5 + +/* Maximum allowed length for a single line of text form the disk file. */ +#define MAX_LINE_LEN 500 + +/* Prototypes: */ +const char *source( void ); +AstRegion *ReadStcs( AstStcsChan *chan, const char *file, int *status ); + +int main( int argc, char **argv ){ + +/* Local variables: */ + AstRegion *region1; + AstRegion *region2; + AstStcsChan *channel; + int status; + +/* Initialised the returned system status to indicate success. */ + status = 0; + +/* Check two files were specified on the command line. */ + if( argc < 3 ) { + printf( "Usage: stcschan-demo4 \n" ); + status = 1; + } + +/* Start an AST object context. This means we do not need to annull + each AST Object individually. Instead, all Objects created within + this context will be annulled automatically by the corresponding + invocation of astEnd. */ + astBegin; + +/* Create an StcsChan. This is the object that converts between external + STC-S descriptions and the corresponding AST Objects. Tell it to use the + "source" function for obtaining lines of text from the disk file. Also + tell it to store all warnings generated by the conversion for later + use. Other attributes of the StcsChan class retain their default + values. */ + channel = astStcsChan( source, NULL, "ReportLevel=3" ); + +/* Attempt to read the STC-S description from the first file, and produce + a corresponding AST Region object. The conversion is performed by the + StcsChan created above. */ + region1 = ReadStcs( channel, argv[ 1 ], &status ); + +/* Now attempt to read the STC-S description from the second file, + producing a corresponding AST Region object. We re-use the StcsChan + created above. */ + region2 = ReadStcs( channel, argv[ 2 ], &status ); + +/* If we have two Regions, test them for overlap and tell the user the + result of the test. */ + if( region1 && region2 ) { + switch( astOverlap( region1, region2 ) ) { + + case 1: + printf( "\n There is no overlap between the two Regions.\n\n"); + break; + + case 2: + printf( "\n The first Region is completely inside the second Region.\n\n"); + break; + + case 3: + printf( "\n The second Region is completely inside the first Region.\n\n"); + break; + + case 4: + printf( "\n There is partial overlap between the two Regions.\n\n"); + break; + + case 5: + printf( "\n The Regions are identical to within their uncertainties.\n\n"); + break; + + case 6: + printf( "\n The second Region is the exact negation of the first " + "Region to within their uncertainties. \n\n"); + break; + + default: + if( astOK ) printf( "\n Unexpected value returned by astOverlap\n\n" ); + } + } + +/* End the AST Object context. All Objects created since the + corresponding invocation of astbegin will be annulled automatically. */ + astEnd; + +/* If an error occurred in the AST library, set the retiurns system + status non-zero. */ + if( !astOK ) status = 1; + return status; +} + + + + + + + +/* This is a function that reads a line of text from the disk file and + returns it to the AST library. It is called from within the astRead + function. */ +const char *source( void ){ + static char buffer[ MAX_LINE_LEN + 2 ]; + FILE *fd = astChannelData; + return fgets( buffer, MAX_LINE_LEN + 2, fd ); +} + + + + + +/* ------------------------------------------------------------------- + * This function reads an STC-S description from a given text file, + * and attempts to convert them into an AST Region. If successful, a + * pointer to the Region is returned. A NULL pointer is returned if + * anything goes wrong. +*/ + +AstRegion *ReadStcs( AstStcsChan *chan, const char *file, int *status ){ + AstKeyMap *warnings; + AstObject *object; + AstRegion *result; + FILE *fd; + char key[ 15 ]; + const char *message; + int iwarn; + +/* Initialise the returned pointer to indicate that no Region has yet + been read. */ + result = NULL; + +/* If an error has already occurred, return without action. */ + if( *status != 0 ) return result; + +/* Attempt to open the STC-S file */ + fd = fopen( file, "r" ); + if( !fd ) { + printf("Failed to open STC-S descrption file '%s'.\n", file ); + +/* If successful... */ + } else { + +/* Associate the descriptor for the input disk file with the StcsChan. + This makes it available to the "source" function. Since this + application is single threaded, we could instead have made "fd" a + global variable, but the ChannelData facility is used here to illustrate + how to pass data to a source or sink function safely in a multi-threaded + application. */ + astPutChannelData( chan, fd ); + +/* The default behaviour of the astRead function when used on an StcsChan is + to read and return the AstroCoordArea as an AST Region. This behaviour + can be changed by assigning appropriate values to the StcsChan attributes + "StcsArea", "StcsCoords" and "StcsProps". Options exist to return the + AstroCoords as an AST PointList, and/or to return the individual + property values read from the STC-S text in the form of an AST KeyMap + (a sort of hashmap). For now, just take the default action of reading the + AstroCoordsArea. */ + object = astRead( chan ); + +/* The astRead function is a generic function and so returns a generic + AstObject pointer. Check an Object was created successfully. */ + if( !object ) { + printf( "Failed to read an AST Object from STC-S description " + "file '%s'.\n", file ); + +/* Now check that the object read is actually an AST Region, rather than + some other class of AST Object. */ + } else if( !astIsARegion( object ) ) { + printf( "Expected a Region but read a %s from STC-S description " + "file '%s'.\n", astGetC( object, "Class" ), file ); + +/* If the Object is a Region, return the Region pointer. */ + } else { + result = (AstRegion *) object; + } + +/* Close the file. */ + fclose( fd ); + +/* If the StcsChan recorded any warnings that were generated whilst + converting the STC-S description into a corresponding AST Object, + we now display them. First test the ReportLevel attribute value to see + if warnings were recored. */ + if( astGetI( chan, "ReportLevel" ) > 0 ) { + +/* Any warnings recorded during the conversion performed by astRead above + are returned by the astWarnings method, in the form of an AST "KeyMap" + (a type of hash map ). */ + warnings = astWarnings( chan ); + +/* If any warnings were generated, and if no other error has occurred so + far, display the warnings. */ + if( warnings && !status && astOK ) { + printf( "\nThe following warnings were issued reading file " + "'%s':\n", file ); + +/* The warnings are stored in an AST KeyMap (a sort of hashmap). Each + warning message is associated with a key of the form "Warning_1", + "Warning_2", etc. Loop round successive keys, obtaining a value for + each key from the warnings KeyMap, and displaying it. */ + iwarn = 1; + while( astOK ) { + sprintf( key, "Warning_%d", iwarn++ ); + if( astMapGet0C( warnings, key, &message ) ) { + printf( "\n- %s\n", message ); + } else { + break; + } + } + } + } + } + + return result; +} + + diff --git a/stcschan-demo5.c b/stcschan-demo5.c new file mode 100644 index 0000000..787c9e6 --- /dev/null +++ b/stcschan-demo5.c @@ -0,0 +1,300 @@ +/* Name: + stcschan-demo5.c + + Purpose: + A demonstration of the facilities provided by the AST library + for reading STC metadata encoded using the STC-S linear string + format. + + Description: + This program reads two STC-S descriptions from two disk files, and + writes a new STC-S description to standard output. The new STC-S + covers the same region as the first STC-S, but expressed in the + coordinate system of the second STC-S. + + Usage: + % stcschan-demo5 + + : The path to the disk file containing the first STC-S + description. + + : The path to the disk file containing the second STC-S + description. + + Example: + % stcschan-demo5 stcs-ex1.txt stcs-ex2.txt + + To compile and link: + Assuming your starlink distribution is in "/star": + + % gcc -o stcschan-demo5 stcschan-demo5.c -L/star/lib \ + -I/star/include `ast_link` +*/ + +/* Include system headers. */ +#include +#include + +/* Include the AST library header. */ +#include "ast.h" + +/* Maximum number of axes in an STC-S AstroCoordSystem. */ +#define MAX_AXES 5 + +/* Maximum allowed length for a single line of text form the disk file. */ +#define MAX_LINE_LEN 500 + +/* Prototypes: */ +const char *source( void ); +AstRegion *ReadStcs( AstStcsChan *chan, const char *file, int *status ); +void ReportWarnings( AstStcsChan *chan, const char *file, int *status ); + +int main( int argc, char **argv ){ + +/* Local variables: */ + AstFrameSet *fs; + AstFrame *frame; + AstMapping *map; + AstRegion *region1; + AstRegion *region2; + AstRegion *region3; + AstStcsChan *channel; + int status; + +/* Initialised the returned system status to indicate success. */ + status = 0; + +/* Check two files were specified on the command line. */ + if( argc < 3 ) { + printf( "Usage: stcschan-demo5 \n" ); + status = 1; + } + +/* Start an AST object context. This means we do not need to annull + each AST Object individually. Instead, all Objects created within + this context will be annulled automatically by the corresponding + invocation of astEnd. */ + astBegin; + +/* Create an StcsChan. This is the object that converts between external + STC-S descriptions and the corresponding AST Objects. Tell it to use the + "source" function for obtaining lines of text from the disk file. We + supply a NULL pointer for the "sink" function so that objects written out + through the channel will appear on standard output. Also tell it to + store all warnings generated by the conversion for later use. Other + attributes of the StcsChan class retain their default values. */ + channel = astStcsChan( source, NULL, "ReportLevel=3" ); + +/* Attempt to read the STC-S description from the first file, and produce + a corresponding AST Region object. The conversion is performed by the + StcsChan created above. */ + region1 = ReadStcs( channel, argv[ 1 ], &status ); + +/* Now attempt to read the STC-S description from the second file, + producing a corresponding AST Region object. We re-use the StcsChan + created above. */ + region2 = ReadStcs( channel, argv[ 2 ], &status ); + +/* If we have two Regions, get the mapping from the coordinate system of + the first Region to the coordinate system of the second Region. If + successful, this returns a FrameSet object that encapsulates both the + Mapping and the two coordinate Frames. */ + if( region1 && region2 ) { + fs = astConvert( region1, region2, "" ); + +/* Issue a warning if the conversion cannot be done. */ + if( ! fs ) { + printf( "Cannot determine the transformation from the first " + "coordinate system to the second coordinate system\n"); + status = 1; + +/* Otherwise, extract the Mapping from the FrameSet, and then extract the + Frame that describes the required coordinate system (it will be the + current Frame in the FrameSet). */ + } else { + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + frame = astGetFrame( fs, AST__CURRENT ); + +/* Create a new Region my mapping the first supplied Region using the + above Mapping. Also supply the above Frame to indicate the nature of + the axis values produced by the Mapping (the new Region will be + defined within this Frame). */ + region3 = astMapRegion( region1, map, frame ); + +/* Attempt to write out this new Region as an STC-S description. Report an + error if this fails. The channel writes text to standard output + because no "sink" function was supplied when it was created above. */ + if( ! astWrite( channel, region3 ) && astOK ) { + printf( "Failed to convert the new Region into an STC-S " + "description.\n" ); + +/* Report any warnings that were generated during the conversion to + STC-S. */ + } else { + ReportWarnings( channel, NULL, &status ); + } + } + } + +/* End the AST Object context. All Objects created since the + corresponding invocation of astbegin will be annulled automatically. */ + astEnd; + +/* If an error occurred in the AST library, set the retiurns system + status non-zero. */ + if( !astOK ) status = 1; + return status; +} + + + + + + + +/* This is a function that reads a line of text from the disk file and + returns it to the AST library. It is called from within the astRead + function. */ +const char *source( void ){ + static char buffer[ MAX_LINE_LEN + 2 ]; + FILE *fd = astChannelData; + return fgets( buffer, MAX_LINE_LEN + 2, fd ); +} + + + + + +/* ------------------------------------------------------------------- + * This function reads an STC-S description from a given text file, + * and attempts to convert them into an AST Region. If successful, a + * pointer to the Region is returned. A NULL pointer is returned if + * anything goes wrong. +*/ + +AstRegion *ReadStcs( AstStcsChan *chan, const char *file, int *status ){ + AstObject *object; + AstRegion *result; + FILE *fd; + +/* Initialise the returned pointer to indicate that no Region has yet + been read. */ + result = NULL; + +/* If an error has already occurred, return without action. */ + if( *status != 0 ) return result; + +/* Attempt to open the STC-S file */ + fd = fopen( file, "r" ); + if( !fd ) { + printf("Failed to open STC-S descrption file '%s'.\n", file ); + +/* If successful... */ + } else { + +/* Associate the descriptor for the input disk file with the StcsChan. + This makes it available to the "source" function. Since this + application is single threaded, we could instead have made "fd" a + global variable, but the ChannelData facility is used here to illustrate + how to pass data to a source or sink function safely in a multi-threaded + application. */ + astPutChannelData( chan, fd ); + +/* The default behaviour of the astRead function when used on an StcsChan is + to read and return the AstroCoordArea as an AST Region. This behaviour + can be changed by assigning appropriate values to the StcsChan attributes + "StcsArea", "StcsCoords" and "StcsProps". Options exist to return the + AstroCoords as an AST PointList, and/or to return the individual + property values read from the STC-S text in the form of an AST KeyMap + (a sort of hashmap). For now, just take the default action of reading the + AstroCoordsArea. */ + object = astRead( chan ); + +/* The astRead function is a generic function and so returns a generic + AstObject pointer. Check an Object was created successfully. */ + if( !object ) { + printf( "Failed to read an AST Object from STC-S description " + "file '%s'.\n", file ); + +/* Now check that the object read is actually an AST Region, rather than + some other class of AST Object. */ + } else if( !astIsARegion( object ) ) { + printf( "Expected a Region but read a %s from STC-S description " + "file '%s'.\n", astGetC( object, "Class" ), file ); + +/* If the Object is a Region, return the Region pointer. */ + } else { + result = (AstRegion *) object; + } + +/* Close the file. */ + fclose( fd ); + +/* If the StcsChan recorded any warnings that were generated whilst + converting the STC-S description into a corresponding AST Object, + we now display them. */ + ReportWarnings( chan, file, status ); + } + + return result; +} + +/* ------------------------------------------------------------------- + * This function extracts any warnings stored in the supplied Channel as + * a result of he previous read or write operation, and displays them on + * standard output. +*/ + +void ReportWarnings( AstStcsChan *chan, const char *file, int *status ){ + AstKeyMap *warnings; + char key[ 15 ]; + const char *message; + int iwarn; + +/* If an error has already occurred, return without action. */ + if( *status != 0 ) return; + +/* If the StcsChan records any warnings that are generated whilst + converting between STC-S descriptions and corresponding AST Objects, + display them. First test the ReportLevel attribute value to see + if warnings were recored. */ + if( astGetI( chan, "ReportLevel" ) > 0 ) { + +/* Any warnings recorded during the conversion performed by the previous + invocation of astRead or astWrite are returned by the astWarnings method, + in the form of an AST "KeyMap" (a type of hash map ). */ + warnings = astWarnings( chan ); + +/* If any warnings were generated, and if no other error has occurred so + far, display the warnings. */ + if( warnings && !status && astOK ) { + +/* Indicate the context to the user. Assume we were reading an STC-S + description if "file" was supplied, and writing an STC-S desciprion + otherwise. */ + if( file ) { + printf( "\nThe following warnings were issued reading file " + "'%s':\n", file ); + } else { + printf( "\nThe following warnings were issued converting an " + "AST Region into an STC-S description:\n" ); + } + +/* The warnings are stored in an AST KeyMap (a sort of hashmap). Each + warning message is associated with a key of the form "Warning_1", + "Warning_2", etc. Loop round successive keys, obtaining a value for + each key from the warnings KeyMap, and displaying it. */ + iwarn = 1; + while( astOK ) { + sprintf( key, "Warning_%d", iwarn++ ); + if( astMapGet0C( warnings, key, &message ) ) { + printf( "\n- %s\n", message ); + } else { + break; + } + } + } + } +} + + diff --git a/stcschan.c b/stcschan.c new file mode 100644 index 0000000..4b43524 --- /dev/null +++ b/stcschan.c @@ -0,0 +1,8732 @@ +/* +*class++ +* Name: +* StcsChan + +* Purpose: +* I/O Channel using STC-S to represent Objects. + +* Constructor Function: +c astStcsChan +f AST_STCSCHAN + +* Description: +* A StcsChan is a specialised form of Channel which supports STC-S +* I/O operations. Writing an Object to an StcsChan (using +c astWrite) will, if the Object is suitable, generate an +f AST_WRITE) will, if the Object is suitable, generate an +* STC-S description of that Object, and reading from an StcsChan will +* create a new Object from its STC-S description. +* +* When an STC-S description is read using +c astRead, +f AST_READ, +* the returned AST Object may be 1) a PointList describing the STC +* AstroCoords (i.e. a single point of interest within the coordinate frame +* described by the STC-S description), or 2) a Region describing the STC +* AstrCoordsArea (i.e. an area or volume of interest within the coordinate +* frame described by the STC-S description), or 3) a KeyMap +* containing the uninterpreted property values read form the STC-S +* description, or 4) a KeyMap containing any combination of the first +* 3 options. The attributes StcsArea, StcsCoords and StcsProps +* control which of the above is returned by +c astRead. +f AST_READ. +* +* When an STC-S description is created from an AST Object using +c astWrite, +f AST_WRITE, +* the AST Object must be either a Region or a KeyMap. If it is a +* Region, it is assumed to define the AstroCoordsArea or (if the +* Region is a single point) the AstroCoords to write to the STC-S +* description. If the Object is a KeyMap, it may contain an entry +* with the key "AREA", holding a Region to be used to define the +* AstroCoordsArea. It may also contain an entry with the key "COORDS", +* holding a Region (a PointList) to be used to create the +* AstroCoords. It may also contain an entry with key "PROPS", holding +* a KeyMap that contains uninterpreted property values to be used as +* defaults for any STC-S properties that are not determined by the +* other supplied Regions. In addition, a KeyMap supplied to +c astWrite +f AST_WRITE +* may itself hold the default STC-S properties (rather than defaults +* being held in a secondary KeyMap, stored as the "PROPS" entry in the +* supplied KeyMap). +* +* The +c astRead and astWrite +f AST_READ and AST_WRITE +* functions work together so that any Object returned by +c astRead can immediately be re-written using astWrite. +f AST_READ can immediately be re-written using AST_WRITE. +* +* Normally, when you use an StcsChan, you should provide "source" +c and "sink" functions which connect it to an external data store +c by reading and writing the resulting text. These functions +f and "sink" routines which connect it to an external data store +f by reading and writing the resulting text. These routines +* should perform any conversions needed between external character +c encodings and the internal ASCII encoding. If no such functions +f encodings and the internal ASCII encoding. If no such routines +* are supplied, a Channel will read from standard input and write +* to standard output. +* +* Alternatively, an XmlChan can be told to read or write from +* specific text files using the SinkFile and SourceFile attributes, +* in which case no sink or source function need be supplied. +* +* Support for STC-S is currently based on the IVOA document "STC-S: +* Space-Time Coordinate (STC) Metadata Linear String Implementation", +* version 1.30 (dated 5th December 2007), available at +* http://www.ivoa.net/Documents/latest/STC-S.html. Note, this +* document is a recommednation only and does not constitute an accepted +* IVOA standard. +* +* The full text of version 1.30 is supported by the StcsChan class, +* with the following exceptions and provisos: +* +* - When reading an STC-S phrase, case is ignored except when reading +* units strings. +* - There is no support for multiple intervals specified within a +* TimeInterval, PositionInterval, SpectralInterval or RedshiftInterval. +* - If the ET timescale is specified, TT is used instead. +* - If the TEB timescale is specified, TDB is used instead. +* - The LOCAL timescale is not supported. +* - The AST TimeFrame and SkyFrame classes do not currently allow a +* reference position to be specified. Consequently, any +* specified within the Time or Space sub-phrase of an STC-S document +* is ignored. +* - The Convex identifier for the space sub-phrase is not supported. +* - The GEO_C and GEO_D space frames are not supported. +* - The UNITSPHERE and SPHER3 space flavours are not supported. +* - If any Error values are supplied in a space sub-phrase, then the +* number of values supplied should equal the number of spatial axes, +* and the values are assumed to specify an error box (i.e. error +* circles, ellipses, etc, are not supported). +* - The spectral and redshift sub-phrases do not support the +* following values: LOCAL_GROUP_CENTER, UNKNOWNRefPos, +* EMBARYCENTER, MOON, MERCURY, VENUS, MARS, JUPITER, SATURN, URANUS, +* NEPTUNE, PLUTO. +* - Error values are supported but error ranges are not. +* - Resolution, PixSize and Size values are ignored. +* - Space velocity sub-phrases are ignored. + +* Inheritance: +* The StcsChan class inherits from the Channel class. + +* Attributes: +* In addition to those attributes common to all Channels, every +* StcsChan also has the following attributes: +* +* - StcsArea: Return the CoordinateArea component after reading an STC-S? +* - StcsCoords: Return the Coordinates component after reading an STC-S? +* - StcsLength: Controls output buffer length +* - StcsProps: Return the STC-S properties after reading an STC-S? + +* Functions: +c The StcsChan class does not define any new functions beyond those +f The StcsChan class does not define any new routines beyond those +* which are applicable to all Channels. + +* Copyright: +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) + +* History: +* 18-DEC-2008 (DSB): +* Original version. +* 22-MAY-2008 (DSB): +* Retain default Equinox values in SkyFrame when reading an STC-S. +* 30-OCT-2009 (DSB): +* Make case insensitive (except for units strings). +* 21-FEB-2014 (DSB): +* Split long properties up into words when writing out an STC-S +* description. +* 26-MAR-2015 (DSB): +* Guard against seg faults if an error has already occured. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS StcsChan + +/* Values identifying particular forms of CoordArea */ +#define NULL_ID 1 +#define TIME_INTERVAL_ID 2 +#define START_TIME_ID 3 +#define STOP_TIME_ID 4 +#define POSITION_INTERVAL_ID 5 +#define ALLSKY_ID 6 +#define CIRCLE_ID 7 +#define ELLIPSE_ID 8 +#define BOX_ID 9 +#define POLYGON_ID 10 +#define CONVEX_ID 11 +#define POSITION_ID 12 +#define TIME_ID 13 +#define SPECTRAL_INTERVAL_ID 14 +#define SPECTRAL_ID 15 +#define REDSHIFT_INTERVAL_ID 16 +#define REDSHIFT_ID 17 +#define VELOCITY_INTERVAL_ID 18 +#define UNION_ID 19 +#define INTERSECTION_ID 20 +#define DIFFERENCE_ID 21 +#define NOT_ID 22 +#define VELOCITY_ID 23 + +/* The number of words used to form an extract from an STC-S description + for use in an error message. */ +#define NEWORD 10 + +/* Max length of string returned by GetAttrib */ +#define GETATTRIB_BUFF_LEN 50 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "frame.h" /* Generic cartesian coordinate systems */ +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "channel.h" /* Interface for parent class */ +#include "stcschan.h" /* Interface definition for this class */ +#include "loader.h" /* Interface to the global loader */ +#include "skyframe.h" /* Celestial coordinate systems */ +#include "timeframe.h" /* Time coordinate systems */ +#include "specframe.h" /* Spectral coordinate systems */ +#include "wcsmap.h" /* PI-related constants */ +#include "region.h" /* Abstract regions */ +#include "interval.h" /* Axis intervals */ +#include "unitmap.h" /* Unit mappings */ +#include "nullregion.h" /* Boundless regions */ +#include "cmpregion.h" /* Compound regions */ +#include "box.h" /* Box regions */ +#include "prism.h" /* Prism regions */ +#include "circle.h" /* Circle regions */ +#include "ellipse.h" /* Ellipse regions */ +#include "polygon.h" /* Polygon regions */ +#include "pointlist.h" /* Lists of points */ +#include "keymap.h" /* KeyMap interface */ + + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module Types. */ +/* ============= */ +typedef struct WordContext { + char *line; + char *wnext; + char *e; + char f; + int done; + char *words[ NEWORD ]; + int next; + int close; + int open; +} WordContext; + +/* Module Variables. */ +/* ================= */ + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static int (* parent_getindent)( AstChannel *, int * ); + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(StcsChan) + +/* Define macros for accessing each item of thread specific global data. */ +#define getattrib_buff astGLOBAL(StcsChan,GetAttrib_Buff) +#define class_init astGLOBAL(StcsChan,Class_Init) +#define class_vtab astGLOBAL(StcsChan,Class_Vtab) + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstStcsChanVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ]; + +#endif + + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstStcsChan *astStcsChanForId_( const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), const char *, int * ), + const char *, ... ); +AstStcsChan *astStcsChanId_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstKeyMap *ReadProps( AstStcsChan *, int * ); +static AstObject *Read( AstChannel *, int * ); +static AstPointList *SinglePointList( AstFrame *, double *, AstRegion *, int *); +static AstRegion *MakeSpaceRegion( AstKeyMap *, AstFrame *, double, int * ); +static char *AddItem( AstStcsChan *, AstKeyMap *, const char *, const char *, char *, int *, int *, int, int * ); +static char *ContextFragment( WordContext *, char **, int * ); +static char *PutRegionProps( AstStcsChan *, AstKeyMap *, const char *, int, char *, int *, int *, int, int * ); +static char *SourceWrap( const char *(*)( void ), int * ); +static const char *GetNextWord( AstStcsChan *, WordContext *, int * ); +static const char *ReadSpaceArgs( AstStcsChan *, const char *, int, int, WordContext *, AstKeyMap *, int * ); +static double *BoxCorners( AstFrame *, const double[2], const double[2], int * ); +static int GetIndent( AstChannel *, int * ); +static int GetRegionProps( AstStcsChan *, AstRegion *, AstKeyMap *, int, int, double, int, int * ); +static int SpaceId( const char *, int * ); +static int Write( AstChannel *, AstObject *, int * ); +static int WriteRegion( AstStcsChan *, AstRegion *, AstKeyMap *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void FreeContext( WordContext *, int * ); +static void GetFmt( const char *, AstKeyMap *, int, int, char *, int * ); +static void MapPut0C( AstKeyMap *, const char *, const char *, const char *, int, int * ); +static void MapPut0D( AstKeyMap *, const char *, double, double, int, int * ); +static void SetUnc( AstRegion *, AstRegion *, AstFrame *, int, double, double *, int, int * ); +static void SinkWrap( void (*)( const char * ), const char *, int * ); +static void WriteProps( AstStcsChan *, AstKeyMap *, int * ); + +static int GetStcsArea( AstStcsChan *, int * ); +static int TestStcsArea( AstStcsChan *, int * ); +static void ClearStcsArea( AstStcsChan *, int * ); +static void SetStcsArea( AstStcsChan *, int, int * ); + +static int GetStcsCoords( AstStcsChan *, int * ); +static int TestStcsCoords( AstStcsChan *, int * ); +static void ClearStcsCoords( AstStcsChan *, int * ); +static void SetStcsCoords( AstStcsChan *, int, int * ); + +static int GetStcsProps( AstStcsChan *, int * ); +static int TestStcsProps( AstStcsChan *, int * ); +static void ClearStcsProps( AstStcsChan *, int * ); +static void SetStcsProps( AstStcsChan *, int, int * ); + +static void ClearAttrib( AstObject *, const char *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); + +static int TestStcsLength( AstStcsChan *, int * ); +static void ClearStcsLength( AstStcsChan *, int * ); +static void SetStcsLength( AstStcsChan *, int, int * ); +static int GetStcsLength( AstStcsChan *, int * ); + +/* Member functions. */ +/* ================= */ + +static char *AddItem( AstStcsChan *this, AstKeyMap *km, const char *key, + const char *prefix, char *line, int *nc, int *crem, + int linelen, int *status ){ +/* +* Name: +* AddItem + +* Purpose: +* Add an STC-S property item to a buffer. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* char *AddItem( AstStcsChan *this, AstKeyMap *km, const char *key, +* const char *prefix, char *line, int *nc, int *crem, +* int linelen, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function appends text describing a singlke STC-S property to +* a supplied text buffer, handling the splitting of text into lines. + +* Parameters: +* this +* The StcsChan. +* km +* Pointer to a KeyMap containing the STC-S properties. +* key +* The key name associated with the property to be checked. +* prefix +* if not NULL, this is a string that is to be written out before +* the property value. It should usually include a trailing space. +* line +* Pointer to the buffer to recieve the prefix and property value. +* nc +* Pointer to an int in which to store the number of characters in +* the buffer. Updated on exit. +* crem +* Pointer to an int in which to store the maximum number of +* characters before a new line. Ignored if linelen is zero. Updated +* on exit. +* linelen +* The maximum number of character per line, or zero if all text is +* to be included in a single line. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the buffer. This will usually be "line", but may be +* different to "line" if it was necessary to expand the memory to make +* room for the new property. + +*/ + +/* Local Variables: */ + char *result; /* Returned pointer */ + char **words; /* All words */ + const char *text; /* Property value */ + const char *word; /* Single word */ + int iw; /* Word index */ + int len; /* Length of new text */ + int nw; /* Number of words in property */ + +/* Initialise */ + result = line; + len = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the KeyMap contains the required property... */ + if( astMapGet0C( km, key, &text ) ) { + +/* Add any supplied prefix to the returned buffer. */ + if( prefix ) { + len = strlen( prefix ); + if( len > *crem && len < linelen ) { + astPutNextText( this, result ); + *nc = 0; + result = astAppendString( result, nc, " " ); + *crem = linelen - 3; + } + result = astAppendString( result, nc, prefix ); + *crem -= len; + } + +/* Split the property into words. */ + words = astChrSplit( text, &nw ); + +/* Append each word to the buffer. */ + for( iw = 0; iw < nw; iw++ ) { + word = words[ iw ]; + +/* If required, get the number of characters to be added to the buffer. */ + if( linelen ) { + len = strlen( word ); + +/* If there is insufficient room left, write out the text through the + Channel sink function, and start a new line with three spaces. Then + reset the number of character remaining in the line. */ + if( len > *crem && len < linelen ) { + astPutNextText( this, result ); + *nc = 0; + result = astAppendString( result, nc, " " ); + *crem = linelen - 3; + } + +/* Reduce crem to account for the text that is about to be added to the + line. */ + *crem -= len; + } + +/* Add the property value to the returned buffer. */ + result = astAppendString( result, nc, word ); + +/* Add a traling space to the returned buffer, if there is room. */ + if( !linelen || *crem > 0 ) { + result = astAppendString( result, nc, " " ); + (*crem)--; + } + } + +/* Free the words buffer. */ + if( words ) { + for( iw = 0; iw < nw; iw++ ) words[ iw ] = astFree( words[ iw ] ); + words = astFree( words ); + } + } + +/* Return the buffer pointer. */ + return result; +} + +static double *BoxCorners( AstFrame *frm, const double centre[2], + const double bsize[2], int *status ) { +/* +* Name: +* BoxCorners + +* Purpose: +* Determine the positions of the corners of an STC Box. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* double *BoxCorners( AstFrame *frm, const double centre[2], +* const double bsize[2], int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function returns a pointer to a dynamically allocated array +* holding the positions of the corners of the STC Box defined by the +* supplied "centre" and "bsize" arrays. + +* Parameters: +* frm +* Pointer to the Frame in which the Box is defined. Must be 2-D. +* centre +* Two element array holding the Frame co-ordinates at the centre +* of the Box. +* bsize +* Two element array holding the full width and height of the Box. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array holding the axis values +* at the four corners, in a form suitable for passing to the +* astPolygon constructor function. NULL is returned if an error has +* already occurred, of if this function fails for any reason. +*/ + +/* Local Variables: */ + double *result; /* Returned pointer. */ + double bh1[ 2 ]; /* A first point on the bottom horizontal edge */ + double bh2[ 2 ]; /* A second point on the bottom horizontal edge */ + double blc[ 2 ]; /* Position of bottom left corner */ + double brc[ 2 ]; /* Position of bottom right corner */ + double lv1[ 2 ]; /* A first point on the left vertical edge */ + double lv2[ 2 ]; /* A second point on the left vertical edge */ + double pa; /* Position angle of great circle/straight line */ + double rv1[ 2 ]; /* A first point on the right vertical edge */ + double rv2[ 2 ]; /* A second point on the right vertical edge */ + double th1[ 2 ]; /* A first point on the top horizontal edge */ + double th2[ 2 ]; /* A second point on the top horizontal edge */ + double tlc[ 2 ]; /* Position of top left corner */ + double trc[ 2 ]; /* Position of top right corner */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check the Frame is 2-dimensional. */ + if( astGetNaxes( frm ) != 2 ) { + astError( AST__BADIN, "astRead(StcsChan): Supplied space frame has " + "%d axes.", status, astGetNaxes( frm ) ); + astError( AST__BADIN, "astRead(StcsChan): Can only use STC Box regions " + "with 2-dimensional space frames.", status ); + } + +/* Offset away from the centre by half the Box width along a great circle + initially parallel to the positive first frame axis (i.e. position + angle +pi/2). The end position goes in "rv1" and the position angle of + the great circle (or straight line) at that point is returned as the + function value. NOTE, the use of the words "left" and "right" below is + vague because it depends on whether we are using a SkyFrame (which has + a reversed first axis) or a basic Frame. In general, the choice of "left" + and "right" below is appropriate for a basic Frame. */ + pa = astOffset2( frm, centre, AST__DPIBY2, bsize[ 0 ]/2, rv1 ); + +/* Turn by 90 degrees and offset away by half the box height. This is done + so that we have a second point (rv2) to define the great circle (or + straight line) that forms the first vertical edge of the Box (i.e. the + great circle or straight line through rv1 and rv2). Note, for spherical + Frames (i.e. SkyFrames) "rv2" is not necessarily a corner of the box. */ + (void) astOffset2( frm, rv1, pa + AST__DPIBY2, bsize[ 1 ]/2, rv2 ); + +/* In the same way, get two points on the second vertical Box edge. */ + pa = astOffset2( frm, centre, -AST__DPIBY2, bsize[ 0 ]/2, lv1 ); + (void) astOffset2( frm, lv1, pa + AST__DPIBY2, bsize[ 1 ]/2, lv2 ); + +/* In the same way, get two points on the top horizontal Box edge. */ + pa = astOffset2( frm, centre, 0.0, bsize[ 1 ]/2, th1 ); + (void) astOffset2( frm, th1, pa + AST__DPIBY2, bsize[ 0 ]/2, th2 ); + +/* In the same way, get two points on the bottom horizontal Box edge. */ + pa = astOffset2( frm, centre, AST__DPI, bsize[ 1 ]/2, bh1 ); + (void) astOffset2( frm, bh1, pa + AST__DPIBY2, bsize[ 0 ]/2, bh2 ); + +/* The first corner of the Box is at the intersection of the first + vertical and top horizontal edges. */ + astIntersect( frm, lv1, lv2, th1, th2, tlc ); + +/* The top right corner of the Box is at the intersection of the right + vertical and top horizontal edges. */ + astIntersect( frm, rv1, rv2, th1, th2, trc ); + +/* The bottom left corner of the Box is at the intersection of the left + vertical and bottom horizontal edges. */ + astIntersect( frm, lv1, lv2, bh1, bh2, blc ); + +/* The bottom right corner of the Box is at the intersection of the right + vertical and bottom horizontal edges. */ + astIntersect( frm, rv1, rv2, bh1, bh2, brc ); + +/* Gather the corners together into an array suitable for use with + astPolygon. Make sure the vertices are traversed in an ant-clockwise + sense whether in a SkyFrame or a basic Frame. */ + result = astMalloc( 8*sizeof( *result ) ); + if( result ) { + if( astIsASkyFrame( frm ) ) { + result[ 0 ] = tlc[ 0 ]; + result[ 1 ] = trc[ 0 ]; + result[ 2 ] = brc[ 0 ]; + result[ 3 ] = blc[ 0 ]; + result[ 4 ] = tlc[ 1 ]; + result[ 5 ] = trc[ 1 ]; + result[ 6 ] = brc[ 1 ]; + result[ 7 ] = blc[ 1 ]; + } else { + result[ 3 ] = tlc[ 0 ]; + result[ 2 ] = trc[ 0 ]; + result[ 1 ] = brc[ 0 ]; + result[ 0 ] = blc[ 0 ]; + result[ 7 ] = tlc[ 1 ]; + result[ 6 ] = trc[ 1 ]; + result[ 5 ] = brc[ 1 ]; + result[ 4 ] = blc[ 1 ]; + } + + } + +/* Return the pointer. */ + return result; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a StcsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* StcsChan member function (over-rides the astClearAttrib protected +* method inherited from the Channel class). + +* Description: +* This function clears the value of a specified attribute for a +* StcsChan, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the StcsChan. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstStcsChan *this; /* Pointer to the StcsChan structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the StcsChan structure. */ + this = (AstStcsChan *) this_object; + +/* Check the attribute name and clear the appropriate attribute. */ + + if ( !strcmp( attrib, "stcsarea" ) ) { + astClearStcsArea( this ); + + } else if ( !strcmp( attrib, "stcscoords" ) ) { + astClearStcsCoords( this ); + + } else if ( !strcmp( attrib, "stcsprop" ) ) { + astClearStcsProps( this ); + + } else if ( !strcmp( attrib, "stcslength" ) ) { + astClearStcsLength( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static char *ContextFragment( WordContext *con, char **buf, int *status ){ +/* +* Name: +* ContextFragment + +* Purpose: +* Returns a string holding a fragment of the document being read. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* char *ContextFragment( WordContext *con, char **buf, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function returns a pointer to a string that holds a fragment +* of the STC-S document currently being read. The fragment ends at +* the last word read by function GetNextWord, and starts a certain +* number of words earlier in the document, as specified by the NEWORD +* macro. + +* Parameters: +* con +* Pointer to the context structure, managed by GetNextWord. +* buf +* Address of a pointer to a dynamically allocated buffer. This +* pointer should be NULL on the first call to this function, and +* will be updated by this function. The pointer should be freed +* using astFree when no longer needed. +* status +* Address of the inherited status value. + +* Returned Value: +* A pointer to the buffer. +*/ + +/* Local Variables: */ + int i; /* Word count */ + int j; /* Word index */ + int nc; /* Text length */ + +/* Initialise the number of characters written to the buffer. */ + nc = 0; + +/* Get the index of the first word to add to the buffer. The "next" + component of the context structure holds the index at which the word + returned by the next call to GetNextWord will be stored. So at the + moment, this is the index of the oldest word in the cyclic list. */ + j = con->next; + +/* Loop round all non-NULL words in the cyclic list. */ + for( i = 0; i < NEWORD; i++ ) { + if( con->words[ j ] ) { + +/* Append this word to the buffer, extending the buffer size as + necessary. */ + *buf = astAppendString( *buf, &nc, con->words[ j ] ); + +/* Append a trailingh space. */ + *buf = astAppendString( *buf, &nc, " " ); + } + +/* Increment the index of the next word to use in the cyclic list. Wrap + back to zerp when the end of the list is reached. */ + if( ++j == NEWORD ) j = 0; + } + +/* Remove the final trailing space. */ + if( nc ) (*buf)[ nc - 1 ] = 0; + +/* Return a pointer to the supplied buffer. */ + return *buf; +} + +static void FreeContext( WordContext *con, int *status ){ +/* +* Name: +* FreeContext + +* Purpose: +* Free the resources used by a word-reading context structure. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* voidFreeContext( WordContext *con, int *status ); + +* Class Membership: +* StcsChan member function + +* Description: +* This function frees the resources used by the supplied WordContext +* structure. This structure is used by GetNextWord to keep track of +* which word to return next. +* +* This function frees the dynamic memory pointers stored within the +* WordContext structure, but does not free the memory holding the +* WordContext structure itself. + +* Parameters: +* con +* Pointer to a structure holding the context. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; /* Word index */ + +/* Check the supplied pointer. */ + if ( !con ) return; + +/* Free the resources. */ + con->line = astFree( con->line ); + + for( i = 0; i < NEWORD; i++ ) { + con->words[ i ] = astFree( con->words[ i ] ); + } + +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a StcsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* StcsChan member function (over-rides the protected astGetAttrib +* method inherited from the Channel class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a StcsChan, formatted as a character string. + +* Parameters: +* this +* Pointer to the StcsChan. +* attrib +* Pointer to a null terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the StcsChan, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the StcsChan. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstStcsChan *this; /* Pointer to the StcsChan structure */ + const char *result; /* Pointer value to return */ + int ival; /* Integer attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the StcsChan structure. */ + this = (AstStcsChan *) this_object; + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* StcsArea. */ +/* --------- */ + if ( !strcmp( attrib, "stcsarea" ) ) { + ival = astGetStcsArea( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* StcsCoords. */ +/* ----------- */ + } else if ( !strcmp( attrib, "stcscoords" ) ) { + ival = astGetStcsCoords( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + + +/* StcsProps. */ +/* ---------- */ + } else if ( !strcmp( attrib, "stcsprops" ) ) { + ival = astGetStcsProps( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* StcsLength */ +/* --------- */ + } else if ( !strcmp( attrib, "stcslength" ) ) { + ival = astGetStcsLength( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; + +} + +static void GetFmt( const char *key, AstKeyMap *props, int i, int defdigs, + char *fmt, int *status ){ +/* +* Name: +* GetFmt + +* Purpose: +* Decide how many digits to use when formatting a numerical STC-S +* property value. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void GetFmt( const char *key, AstKeyMap *props, int i, +* int defdigs, char *fmt, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function locates the named property in the supplied KeyMap. If +* it is found, a printf format specifier is generated that matches +* the value is determined and returned. Otherwise, a default format +* specified based on the supplied default number of digits is returned. + +* Parameters: +* key +* The key name associated with the property. +* km +* Pointer to a KeyMap containing the STC-S properties. +* i +* For vector values, this is the index of the vector element to be +* checked. Should be zero for scalar values. If "i" is greater +* than the number of values in the vector, then the number of digits +* in the first element is found and returned. +* defdigs +* The value to return if the KeyMap does not contain an entry with +* the supplied key. +* fmt +* Pointer to a string in which to return the format specifier. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + const char *dot; /* Pointer to decimal point */ + const char *p; /* Pointer to next character */ + const char *word; /* Property value */ + int after0; /* Digits after the decimal point in first word */ + int after; /* Digits after the decimal point in current word */ + int before0; /* Digits before the decimal point in first word */ + int before; /* Digits before the decimal point in current word */ + int exp0; /* Was an exponent found in first word? */ + int exp; /* Was an exponent found in current word? */ + int j; /* Index of current word */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise. */ + exp = 1; + before = defdigs; + after = 0; + exp0 = 0; + before0 = 0; + after0 = 0; + +/* If the KeyMap contains the required property... */ + if( astMapGet0C( props, key, &word ) ) { + +/* Skip over the words in the string. */ + p = word; + for( j = 0; j <= i; j++ ) { + +/* Find the next space or terminating null at the end of the current word. + Also count the number of digits before and after the decimal point and + see if the word includes an exponent. */ + exp = 0; + before = 0; + after = 0; + dot = NULL; + + while( *p != 0 && *p != ' ' ) { + if( ! exp ) { + if( isdigit( *p ) ) { + if( dot ) { + after++; + } else { + before++; + } + + } else if( *p == '.' ) { + dot = p; + + } else if( *p == 'e' || *p == 'E' ) { + exp = 1; + } + } + p++; + } + +/* Note the values for the first word. */ + if( j == 0 ) { + exp0 = exp; + before0 = before; + after0 = after; + } + +/* Find the following non-space marking the start of the next word, + or the terminating null. */ + while( *p != 0 && *p == ' ' ) p++; + +/* If we find the terminating null before we have found the i'th word, + break out of the loop using the first word instead of the i'th word. */ + if( *p == 0 ) { + exp = exp0; + before = before0; + after = after0; + break; + } + } + } + + if( exp ) { + sprintf( fmt, "%%.%dg", before + after ); + } else { + sprintf( fmt, "%%.%df", after ); + } +} + +static int GetIndent( AstChannel *this, int *status ) { +/* +* Name: +* GetIndent + +* Purpose: +* Get the value of the Indent attribute for a StcsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* int GetIndent( AstChannel *this, int *status ) + +* Class Membership: +* StcsChan member function (over-rides the protected astGetIndent +* method inherited from the Channel class). + +* Description: +* This function returns the value of the Indent attribute, supplying +* a default value appropriate to an StcsChan. + +* Parameters: +* this +* Pointer to the StcsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - The Indent value to use. + +*/ + +/* If the attribute is set, return its value. Otherwise return a value of + zero. */ + return astTestIndent( this ) ? (*parent_getindent)( this, status ) : 0; +} + +static const char *GetNextWord( AstStcsChan *this, WordContext *con, + int *status ){ +/* +* Name: +* GetNextWord + +* Purpose: +* Get a pointer to the next input word read from an STC-S source. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* const char *GetNextWord( AstStcsChan *this, WordContext *con, +* int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function returns a pointer to the next word of an STC-S +* description. + +* Parameters: +* this +* Pointer to the StcsChan, or NULL (to initialise "con"). +* con +* Pointer to a structure holding context. The structure should be +* initialised by calling this function with a NULL "this" pointer +* before making further use of this function. When finished, it +* should be released using FreeContext. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new word. NULL is returned if an error has already +* occurred, of if "this" is NULL. +*/ + +/* Local Variables: */ + const char *result; /* Returned pointer. */ + int i; /* Word index */ + size_t len; /* Word length */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If no StcChan was supplied, initialise the supplied WordContext. */ + if( !this ) { + con->e = NULL; + con->line = NULL; + con->done = 0; + con->next = 0; + con->wnext = NULL; + con->close = 0; + con->open = 0; + for( i = 0; i < NEWORD; i++ ) con->words[ i ] = NULL; + +/* Words that end with an opening parenthesis are treated as two words. If the + previous word ended in an opening parenthesis, it will have been removed by + the previous call to this function and the "con->open" flag set. In + this case, we just return a pointer to the second of the two words - a + single "(" character - and clear the "con->open" flag. */ + } else if( con->open && ! con->done ) { + con->open = 0; + result = "("; + +/* Likewise deal with words that end with a closing parenthesis. */ + } else if( con->close && ! con->done ) { + con->close = 0; + result = ")"; + +/* Words that begin with an opening parenthesis are treated as two words. If + the previous word was such an opening parenthesis, the rest of the word + will have been removed by the previous call to this function and the + "con->wnext" pointer set to the start of the remaining word. In + this case, re-instate the original character that was replaced by a + terminating null when the previous word was returned, return the + "con->wnext" pointer, and then clear the pointer. */ + } else if( con->wnext && ! con->done ) { + *(con->wnext) = con->f; + result = con->wnext; + con->wnext = NULL; + +/* Otherwise... */ + } else { + +/* If the previous invocation of this function converted a space + character into a null character, change it back again. */ + if( con->e ) *(con->e) = ' '; + +/* Get a pointer to the next non-white character in the current line of + input text. */ + result = con->e; + if( result ) { + while( *result && isspace( *result ) ) result++; + } + +/* If we have exhausted the current line, get the next line by invoking + the source function. We loop until we read a line that is not entirely + blank. */ + while( ( !result || ! *result ) && astOK ) { + +/* First free the memory holding the previous line. */ + if( con->line ) con->line = astFree( con->line ); + con->e = NULL; + +/* Get the next line of text from the source function. */ + con->line = astGetNextText( this ); + result = con->line; + +/* Break when we reach the end of the input text. */ + if( !result ) break; + +/* Get a pointer to the first non-white character in the new line. */ + while( *result && isspace( *result ) ) result++; + } + +/* Find the end of the word. */ + if( result && *result ) { + con->e = (char *) result + 1; + while( *(con->e) && !isspace( *(con->e) ) ) (con->e)++; + +/* If the word is already null-terminated, nullify the "e" pointer to + indicate this. Otherwise, change the white-space character into a + null. */ + if( *(con->e) ) { + *(con->e) = 0; + len = con->e - result; + } else { + con->e = NULL; + len = strlen( result ); + } + +/* Add the word into the cyclic list of words used to form a document + fragment to include in error and warning messages. */ + con->words[ con->next ] = astStore( con->words[ con->next ], + result, len + 1 ); + if( ++(con->next) == NEWORD ) con->next = 0; + +/* Deal with words that include an opening or closing parenthesis at + start or end. These words must have 2 or more characters. */ + if( len > 1 ) { + +/* If the word ends with an opening parenthesis, replace the parenthesis + with a null character and set a flag indicating that the next word + returned should consist of just an opening parenthesis. */ + if( result[ len - 1 ] == '(' ) { + ((char *) result)[ len - 1 ] = 0; + con->open = 1; + +/* If the word ends with a closing parenthesis, replace the parenthesis + with a null character and set a flag indicating that the next word + returned should consist of just a closing parenthesis. */ + } else if( result[ len - 1 ] == ')' ) { + ((char *) result)[ len - 1 ] = 0; + con->close = 1; + +/* If the word starts with an opening parenthesis, replace the parenthesis + with a null character and set a flag indicating that the next word + returned should consist of just a closing parenthesis. */ + } else if( result[ 0 ] == '(' ) { + con->wnext = ( (char *) result ) + 1; + con->f = *(con->wnext); + *(con->wnext) = 0; + } + } + +/* If we have run out of input words, but we have not yet finished + interpreting the previous word returned, return a null string, rather + than a null pointer in order to allow further interpretation of the + previous word. */ + } else if( ! con->done ) { + result = ""; + } + } + +/* Return the pointer to the next word. */ + return result; +} + +static int GetRegionProps( AstStcsChan *this, AstRegion *spreg, + AstKeyMap *spprops, int nspace, int defdigs, + double scale, int issky, int *status ) { +/* +* Name: +* GetRegionProps + +* Purpose: +* Create STC-S properties to describe a given Region and store in a +* KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* int GetRegionProps( AstStcsChan *this, AstRegion *spreg, +* AstKeyMap *spprops, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function creates a set of STC-S properties to describe the +* supplied spatial (2D) Region, and stores them in the supplied KeyMap. + +* Parameters: +* this +* The StcsChan being used. +* spreg +* The 2-D spatial Region to be described. +* spprops +* A KeyMap in which to store the created properties. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Returns the integer code for the spatial region, or NULL_ID if the +* properties could not be created for any reason. + +*/ + + +/* Local Variables: */ + AstKeyMap *new_props; /* KeyMap holding component Region properties */ + AstMapping *sreg; /* Simplified Region */ + AstRegion **reg_list; /* Array of component Regioon pointers */ + char *prop; /* Formatted property string */ + char buf[ 100 ]; /* Buffer for formatted values */ + char fmt[ 10 ]; /* Buffer for format specifier */ + double *p; /* Pointer to next axis value */ + double *points; /* Pointer to array of Region axis values */ + double a; /* Circle or ellipse radius */ + double angle; /* Ellipse position angle */ + double b; /* Ellipse radius */ + double centre[ 3 ]; /* Circle or ellipse centre */ + double lbnd[ 3 ]; /* Region lower bounds */ + double ubnd[ 3 ]; /* Region upper bounds */ + int i; /* Loop index */ + int j; /* Loop index */ + int nc; /* Number of characters in "prop" string */ + int np; /* Number of points defining the Region */ + int nreg; /* Number of component Regions */ + int ok; /* Can the Region be written out? */ + int oper; /* Code for CmpRegion boolean operator */ + int spaceid; /* Identifier for STC-S spatial region type */ + +/* Check inherited status */ + if( !astOK ) return NULL_ID; + +/* Initialise */ + spaceid = NULL_ID; + ok = 1; + prop = NULL; + +/* If the Region has been negated, temporarily negate the Region, and + write its properties into a new KeyMap by calling this function + recursively. Then store the new KeyMap in the supplied KeyMap. */ + if( astGetNegated( spreg ) ) { + spaceid = NOT_ID; + astNegate( spreg ); + new_props = astKeyMap( " ", status ); + + if( GetRegionProps( this, spreg, new_props, nspace, defdigs, + scale, issky, status ) == NULL_ID ) ok = 0; + + astMapPut0C( spprops, "ID", "Not", NULL ); + astMapPut0A( spprops, "REGION1", new_props, NULL ); + astMapPut0I( spprops, "NREG", 1, NULL ); + astNegate( spreg ); + +/* Store properties that are specific to AllSky sub-phrases (i.e. none)... */ + } else if( astIsANullRegion( spreg ) && astGetNegated( spreg ) ) { + spaceid = ALLSKY_ID; + astMapPut0C( spprops, "ID", "AllSky", NULL ); + +/* Store properties that are specific to Circle sub-phrases... */ + } else if( astIsACircle( spreg ) ) { + spaceid = CIRCLE_ID; + astMapPut0C( spprops, "ID", "Circle", NULL ); + +/* Get the geometric parameters of the Circle. */ + astCirclePars( spreg, centre, &a, NULL ); + +/* Create a string holding the formatted centre axis values, scaling + to the required units. Use the Frame's Digits attribute to specify + how many digits to use when formatting the axis values. */ + nc = 0; + for( i = 0; i < nspace; i++ ) { + if( centre[ i ] != AST__BAD ) { + GetFmt( "CENTRE", spprops, i, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*centre[ i ] ); + prop = astAppendString( prop, &nc, buf ); + prop = astAppendString( prop, &nc, " " ); + + } else { + ok = 0; + astAddWarning( this, 1, "The supplied Circle contains " + "one or more bad centre axis values.", + "astWrite", status ); + break; + } + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "CENTRE", prop, NULL ); + +/* Scale, format and store the radius. */ + if( a != AST__BAD ) { + GetFmt( "RADIUS", spprops, 0, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*a ); + astMapPut0C( spprops, "RADIUS", buf, NULL ); + } else { + ok = 0; + astAddWarning( this, 1, "The supplied Circle has an " + "undefined radius.", "astWrite", status ); + } + +/* Store properties that are specific to PositionInterval sub-phrases... */ + } else if( astIsAInterval( spreg ) || astIsABox( spreg ) ) { + spaceid = POSITION_INTERVAL_ID; + astMapPut0C( spprops, "ID", "PositionInterval", NULL ); + +/* Get the bounds of the Region. */ + astGetRegionBounds( spreg, lbnd, ubnd ); + +/* Create a string holding the formatted low limits, scaling to the + required units. Use the Frame's Digits attribute to specify how + many digits to use when formatting the axis values. */ + nc = 0; + for( i = 0; i < nspace; i++ ) { + if( lbnd[ i ] == AST__BAD || lbnd[ i ] == DBL_MAX || + lbnd[ i ] == -DBL_MAX ) { + astAddWarning( this, 1, "Spatial axis %d has an undefined " + "lower limit.", "astWrite", status, i + 1 ); + ok = 0; + break; + } else { + GetFmt( "LOLIMIT", spprops, i, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*lbnd[ i ] ); + prop = astAppendString( prop, &nc, buf ); + prop = astAppendString( prop, &nc, " " ); + } + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "LOLIMIT", prop, NULL ); + +/* Do the same for the upper limits. */ + nc = 0; + for( i = 0; i < nspace; i++ ) { + if( ubnd[ i ] == AST__BAD || ubnd[ i ] == DBL_MAX || + ubnd[ i ] == -DBL_MAX ) { + astAddWarning( this, 1, "Spatial axis %d has an undefined " + "upper limit.", "astWrite", status, i + 1 ); + ok = 0; + break; + } else { + GetFmt( "HILIMIT", spprops, i, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*ubnd[ i ] ); + prop = astAppendString( prop, &nc, buf ); + prop = astAppendString( prop, &nc, " " ); + } + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "HILIMIT", prop, NULL ); + +/* Store properties that are specific to Ellipse sub-phrases... */ + } else if( astIsAEllipse( spreg ) ) { + spaceid = ELLIPSE_ID; + astMapPut0C( spprops, "ID", "Ellipse", NULL ); + +/* Get the geometric parameters of the Ellipse. */ + astEllipsePars( spreg, centre, &a, &b, &angle, NULL, NULL ); + +/* Create a string holding the formatted centre axis values, scaling + to the required units. Use the Frame's Digits attribute to specify + how many digits to use when formatting the axis values. */ + nc = 0; + for( i = 0; i < nspace; i++ ) { + if( centre[ i ] != AST__BAD ) { + GetFmt( "CENTRE", spprops, i, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*centre[ i ] ); + prop = astAppendString( prop, &nc, buf ); + prop = astAppendString( prop, &nc, " " ); + + } else { + ok = 0; + astAddWarning( this, 1, "The supplied Ellipse contains " + "one or more bad centre axis values.", + "astWrite", status ); + break; + } + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "CENTRE", prop, NULL ); + +/* Scale, format and store the two radii. */ + if( a != AST__BAD && b != AST__BAD && angle != AST__BAD ) { + GetFmt( "RADIUS1", spprops, 0, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*a ); + astMapPut0C( spprops, "RADIUS1", buf, NULL ); + + GetFmt( "RADIUS2", spprops, 0, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*b ); + astMapPut0C( spprops, "RADIUS2", buf, NULL ); + +/* Convert the angle to degrees in the direction required by STC-S, + format and store. */ + angle *= AST__DR2D; + if( !issky ) angle = 90 - angle; + while( angle < 0.0 ) angle += 360.0; + while( angle >= 360.0 ) angle -= 360.0; + + GetFmt( "POSANGLE", spprops, 0, defdigs, fmt, status ); + (void) sprintf( buf, fmt, angle ); + astMapPut0C( spprops, "POSANGLE", buf, NULL ); + + } else { + astAddWarning( this, 1, "The gemeotric parameters of the " + "supplied Ellipse are undefined.", + "astWrite", status ); + ok = 0; + } + +/* Store properties that are specific to Polygon sub-phrases... */ + } else if( astIsAPolygon( spreg ) ) { + spaceid = POLYGON_ID; + astMapPut0C( spprops, "ID", "Polygon", NULL ); + +/* Get an array holding the axis values at the polygon vertices. */ + astGetRegionPoints( spreg, 0, 0, &np, NULL ); + points = astMalloc( sizeof( double )*np*nspace ); + astGetRegionPoints( spreg, np, nspace, &np, points ); + +/* Create a string holding the formatted vertex axis values, scaling + to the required units. Use the Frame's Digits attribute to specify + how many digits to use when formatting the axis values. */ + GetFmt( "VERTICES", spprops, 0, defdigs, fmt, status ); + nc = 0; + for( j = 0; j < np; j++ ) { + p = points + j; + for( i = 0; i < nspace; i++ ) { + if( *p != AST__BAD ) { + (void) sprintf( buf, fmt, scale*(*p) ); + prop = astAppendString( prop, &nc, buf ); + prop = astAppendString( prop, &nc, " " ); + p += np; + } else { + astAddWarning( this, 1, "The supplied Polygon contains " + "one or more bad axis values.", "astWrite", + status ); + ok = 0; + break; + } + } + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "VERTICES", prop, NULL ); + +/* Free resources. */ + points = astFree( points ); + +/* Store properties that are specific to Position sub-phrases... */ + } else if( astIsAPointList( spreg ) ) { + spaceid = POSITION_ID; + astMapPut0C( spprops, "ID", "Position", NULL ); + +/* Check the PointList contains only a single point. */ + astGetRegionPoints( spreg, 0, 0, &np, NULL ); + if( np > 1 ) { + astAddWarning( this, 1, "The supplied PointList contains " + "more than one position.", "astWrite", status ); + ok = 0; + +/* If so, get the axis values at the point. */ + } else { + astGetRegionPoints( spreg, 1, nspace, &np, centre ); + +/* Create a string holding the formatted axis values, scaling to the + required units. Use the Frame's Digits attribute to specify how many + digits to use when formatting the axis values. */ + nc = 0; + for( i = 0; i < nspace; i++ ) { + if( centre[ i ] != AST__BAD ) { + GetFmt( "POSITION", spprops, i, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*centre[ i ] ); + prop = astAppendString( prop, &nc, buf ); + prop = astAppendString( prop, &nc, " " ); + + } else { + astAddWarning( this, 1, "The supplied PointList contains " + "one or more bad axis values.", "astWrite", + status ); + ok = 0; + break; + } + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "POSITION", prop, NULL ); + } + +/* Store properties that are specific to compound Position sub-phrases... */ + } else { + +/* If the Region is not a CmpRegion (e.g. a Prism?) see if simplifying it + produces a CmpRegion. */ + if( !astIsACmpRegion( spreg ) ) { + sreg = astSimplify( spreg ); + } else { + sreg = astClone( spreg ); + } + +/* If we now have a CmpRegion, write its properties into a new KeyMap by + calling this function recursively. Then store the new KeyMap in the + supplied KeyMap. */ + if( astIsACmpRegion( sreg ) ) { + +/* Get the list of Regions that the CmpRegion combines together. This + also returns the boolean operator with which they are combined. */ + nreg = 0; + reg_list = NULL; + oper = astCmpRegionList( (AstCmpRegion *) sreg, &nreg, ®_list ); + +/* Store compound region type in the supplied KeyMap. */ + if( oper == AST__AND ) { + spaceid = INTERSECTION_ID; + astMapPut0C( spprops, "ID", "Intersection", NULL ); + } else if( oper == AST__OR ) { + spaceid = UNION_ID; + astMapPut0C( spprops, "ID", "Union", NULL ); + } else { + spaceid = DIFFERENCE_ID; + astMapPut0C( spprops, "ID", "Difference", NULL ); + } + +/* Loop round each of the combined Regions. */ + for( i = 0; i < nreg; i++ ) { + +/* Create a new KeyMap, and then call this function recursively to store + the properties of the i'th component Region in the new KeyMap. */ + if( ok ) { + new_props = astKeyMap( " ", status ); + if( GetRegionProps( this, reg_list[ i ], new_props, nspace, + defdigs, scale, issky, status ) + == NULL_ID ) ok = 0; + +/* Store the new KeyMap in the supplied KeyMap. */ + sprintf( buf, "REGION%d", i + 1 ); + astMapPut0A( spprops, buf, new_props, NULL ); + +/* Free resources. */ + new_props = astAnnul( new_props ); + } + reg_list[ i ] = astAnnul( reg_list[ i ] ); + } + reg_list = astFree( reg_list ); + astMapPut0I( spprops, "NREG", nreg, NULL ); + +/* All other classes of Region are unsupported. */ + } else { + astAddWarning( this, 1, "The supplied %s cannot be written " + "out since STC-S does not support %s regions.", + "astWrite", status, astGetClass( spreg ), + astGetClass( spreg ) ); + ok = 0; + } + +/* Free resources. */ + sreg = astAnnul( sreg ); + } + + if( prop ) prop = astFree( prop ); + +/* If an error has occurred, return NULL_ID. */ + if( !ok || !astOK ) spaceid = NULL_ID; + +/* Return the identifier for the STC-S spatial region type. */ + return spaceid; +} + +void astInitStcsChanVtab_( AstStcsChanVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitStcsChanVtab + +* Purpose: +* Initialise a virtual function table for an StcsChan. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcschan.h" +* void astInitStcsChanVtab( AstStcsChanVtab *vtab, const char *name ) + +* Class Membership: +* StcsChan vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the StcsChan class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstChannelVtab *channel; /* Pointer to Channel component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitChannelVtab( (AstChannelVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAStcsChan) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstChannelVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ + + vtab->ClearStcsArea = ClearStcsArea; + vtab->GetStcsArea = GetStcsArea; + vtab->SetStcsArea = SetStcsArea; + vtab->TestStcsArea = TestStcsArea; + + vtab->ClearStcsCoords = ClearStcsCoords; + vtab->GetStcsCoords = GetStcsCoords; + vtab->SetStcsCoords = SetStcsCoords; + vtab->TestStcsCoords = TestStcsCoords; + + vtab->ClearStcsProps = ClearStcsProps; + vtab->GetStcsProps = GetStcsProps; + vtab->SetStcsProps = SetStcsProps; + vtab->TestStcsProps = TestStcsProps; + + vtab->SetStcsLength = SetStcsLength; + vtab->ClearStcsLength = ClearStcsLength; + vtab->TestStcsLength = TestStcsLength; + vtab->GetStcsLength = GetStcsLength; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + channel = (AstChannelVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + channel->Write = Write; + channel->Read = Read; + + parent_getindent = channel->GetIndent; + channel->GetIndent = GetIndent; + +/* Declare the Dump function for this class. There is no destructor or + copy constructor. */ + astSetDump( vtab, Dump, "StcsChan", "STC-S I/O Channel" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static AstRegion *MakeSpaceRegion( AstKeyMap *props, AstFrame *frm, + double scale, int *status ){ +/* +* Name: +* MakeSpaceRegion + +* Purpose: +* Create a Region to describe the space coverage of the STC-S +* description being read. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* AstRegion *MakeSpaceRegion( AstKeyMap *props, AstFrame *frm, +* double scale, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function returns a pointer to a new Region that describes the +* spatial coverage of an STC-S description. + +* Parameters: +* props +* A KeyMap holding properties read from the STC-S space sub-phrase. +* frm +* The Frame in which the Region is to be defined. +* scale +* A factor that must be applied to the raw axis values read from the +* STC-S description in order to convert them into the units used by +* the supplied Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Region pointer. + +*/ + + +/* Local Variables: */ + AstKeyMap *reg_props; /* KeyMap holding argument properties */ + AstRegion *reg; /* Current argument Region */ + AstRegion *result; /* Returned Region */ + AstRegion *tmp; /* Temporary Region pointer */ + char key[ 20 ]; /* Key for argument region */ + const char *id; /* Sub-phrase identifier */ + double *p; /* Pointer to next axis value */ + double *temp; /* Pointer to array of reordered polygon vertex axis values */ + double *vertices; /* Pointer to array of polygon vertex axis values */ + double val1; /* Scalar value read from KeyMap */ + double val2; /* Scalar value read from KeyMap */ + double val3; /* Scalar value read from KeyMap */ + double vec1[ 10 ]; /* Vector read from KeyMap */ + double vec2[ 10 ]; /* Vector read from KeyMap */ + int iaxis; /* Axis index */ + int ireg; /* Index of argument regions */ + int ivert; /* Vertex index */ + int naxes; /* Number of spatial axes */ + int nreg; /* Number of argument regions */ + int nval; /* Number of values read from KeyMap */ + int nvert; /* Number of vertices */ + int spaceid; /* Integer identifier for spatial shape */ + int oper; /* Boolean operator code for CmpRegion */ + +/* Initialise */ + result = NULL; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Temporarily ensure that an error is reported if an attempt is made to + access a non-existent KeyMap entry. */ + astSetKeyError( props, 1 ); + +/* Get the space sub-phrase identifier from the properties KeyMap, and + find the corresponding integer identifier. */ + + astMapGet0C( props, "ID", &id ); + spaceid = SpaceId( id, status ); + +/* Get the number of axes in the Frame. */ + naxes = astGetNaxes( frm ); + +/* Create a suitable Region to enclose the space positions. This + includes scaling the supplied axis values to the units used by + the Frame. */ + if( spaceid == POSITION_INTERVAL_ID ) { + astMapGet1D( props, "DLOLIMIT", naxes, &nval, vec1 ); + astMapGet1D( props, "DHILIMIT", naxes, &nval, vec2 ); + + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vec1[ iaxis ] *= scale; + vec2[ iaxis ] *= scale; + } + + result = (AstRegion *) astBox( frm, 1, vec1, vec2, NULL, " ", status ); + + } else if( spaceid == ALLSKY_ID ) { + result = (AstRegion *) astNullRegion( frm, NULL, "Negated=1", status ); + + } else if( spaceid == CIRCLE_ID ) { + astMapGet1D( props, "DCENTRE", naxes, &nval, vec1 ); + astMapGet0D( props, "RADIUS", &val1 ); + for( iaxis = 0; iaxis < naxes; iaxis++ ) vec1[ iaxis ] *= scale; + val1 *= scale; + result = (AstRegion *) astCircle( frm, 1, vec1, &val1, NULL, " ", + status ); + + } else if( spaceid == ELLIPSE_ID ) { + astMapGet1D( props, "DCENTRE", naxes, &nval, vec1 ); + astMapGet0D( props, "RADIUS1", &val1 ); + astMapGet0D( props, "RADIUS2", &val2 ); + astMapGet0D( props, "POSANGLE", &val3 ); + for( iaxis = 0; iaxis < naxes; iaxis++ ) vec1[ iaxis ] *= scale; + vec2[ 0 ] = val1*scale; + vec2[ 1 ] = val2*scale; + if( !astIsASkyFrame( frm ) ) val3 = 90.0 - val3; + val3 *= AST__DD2R; + result = (AstRegion *) astEllipse( frm, 1, vec1, vec2, &val3, NULL, " ", + status ); + + } else if( spaceid == BOX_ID ) { + astMapGet1D( props, "DCENTRE", naxes, &nval, vec1 ); + astMapGet1D( props, "DBSIZE", naxes, &nval, vec2 ); + + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vec1[ iaxis ] *= scale; + vec2[ iaxis ] *= scale; + } + + vertices = BoxCorners( frm, vec1, vec2, status ); + result = (AstRegion *) astPolygon( frm, 4, 4, vertices, NULL, " ", + status ); + vertices = astFree( vertices ); + + } else if( spaceid == POLYGON_ID ) { + nval = astMapLength( props, "DVERTICES" ); + temp = astMalloc( sizeof( double )*nval ); + astMapGet1D( props, "DVERTICES", nval, &nval, temp ); + +/* An STC-S polygon description holds the vertex axis values in the wrong + order for the AstPolygon constructor. Therefore, transpose the temp + array (scale them at the same time). */ + vertices = astMalloc( sizeof( double )*nval ); + if( astOK ) { + nvert = nval/naxes; + p = temp; + for( ivert = 0; ivert < nvert; ivert++ ) { + for( iaxis = 0; iaxis < naxes; iaxis++,p++ ) { + vertices[ iaxis*nvert + ivert ] = *p*scale; + } + } + + result = (AstRegion *) astPolygon( frm, nvert, nvert, vertices, NULL, + " ", status ); + } + + vertices = astFree( vertices ); + temp = astFree( temp ); + + } else if( spaceid == POSITION_ID ) { + astMapGet1D( props, "DPOSITION", naxes, &nval, vec1 ); + for( iaxis = 0; iaxis < naxes; iaxis++ ) vec1[ iaxis ] *= scale; + result = (AstRegion *) SinglePointList( frm, vec1, NULL, status ); + + } else if( spaceid == CONVEX_ID ) { + astError( AST__INTER, "astRead(StcsChan): No support for Convex in " + "MakeSpaceRegion (internal AST programming error).", status ); + +/* All remaining valid space id values are compound - their arguments are held + within separate KeyMaps nested inside the supplied KeyMap. */ + } else if( spaceid != NULL_ID ) { + +/* The number of arguments is defined in the NREG entry. */ + astMapGet0I( props, "NREG", &nreg ); + +/* Get the CmpRegion operator code. */ + if( spaceid == UNION_ID ) { + oper = AST__OR; + } else if( spaceid == INTERSECTION_ID ) { + oper = AST__AND; + } else if( spaceid == DIFFERENCE_ID ) { + oper = AST__XOR; + } else { + oper = 0; /* To avoid compiler warnings */ + } + +/* Loop over all argument Regions. */ + for( ireg = 0; ireg < nreg; ireg++ ) { + +/* Get the KeyMap holding the STC-S properties of the current argument + region. */ + sprintf( key, "REGION%d", ireg + 1 ); + astMapGet0A( props, key, ®_props ); + +/* Construct an AST Region from this list of STC-S properties. */ + reg = MakeSpaceRegion( reg_props, frm, scale, status ); + +/* If we are creating a "Not" element, just negate the argument region + and return it. */ + if( spaceid == NOT_ID ) { + astNegate( reg ); + result = astClone( reg ); + +/* If we are creating a "Union", "Difference" or "Intersection" element, + combine the first two arguments into a CmpRegion, and then add in each + subsequent argument. */ + } else { + if( ireg == 0 ) { + result = astClone( reg ); + } else { + tmp = (AstRegion *) astCmpRegion( result, reg, oper, " ", + status ); + (void) astAnnul( result ); + result = tmp; + } + } + +/* Free resources */ + reg = astAnnul( reg ); + reg_props = astAnnul( reg_props ); + } + } + +/* Ensure that no error is reported if an attempt is made to access a + non-existent KeyMap entry. */ + astSetKeyError( props, 0 ); + +/* Return the Region. */ + return result; +} + +static void MapPut0C( AstKeyMap *km, const char *key, const char *value, + const char *def, int defs, int *status ){ +/* +* Name: +* MapPut0C + +* Purpose: +* Store a text STC-S property in the supplied keymap. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void MapPut0C( AstKeyMap *km, const char *key, const char *value, +* const char *def, int defs, int *status ) + +* Class Membership: +* StcsChan member function. + +* Description: +* This function stors the supplied value in the given KeyMap, +* handling default values. + +* Parameters: +* km +* Pointer to the KeyMap in which to store the value. +* key +* Pointer to a string holding the property name associated with +* the value. +* value +* The property value. If this is NULL then the function +* returns without action. +* def +* The default property value. +* defs +* If zero, then the value is not stored in the KeyMap if the value +* is equal to the default value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Check the inherited status */ + if( !astOK ) return; + +/* If the value is NULL, ignore the entry. */ + if( value ) { + +/* If the value is equal to the default value, and we are NOT storing + default values, ensure the KeyMap has no entry for the given key. */ + if( astChrMatch( value, def ) && !defs ) { + astMapRemove( km, key ); + +/* Otherwise, store the value. */ + } else { + astMapPut0C( km, key, value, NULL ); + } + } +} + +static void MapPut0D( AstKeyMap *km, const char *key, double value, double def, + int defs, int *status ){ +/* +* Name: +* MapPut0D + +* Purpose: +* Store a floating point STC-S property in the supplied keymap. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void MapPut0D( AstKeyMap *km, const char *key, double value, double def, +* int defs, int *status ) + +* Class Membership: +* StcsChan member function. + +* Description: +* This function stors the supplied value in the given KeyMap, +* handling default values. + +* Parameters: +* km +* Pointer to the KeyMap in which to store the value. +* key +* Pointer to a string holding the property name associated with +* the value. +* value +* The property value. If this is AST__BAD then the function +* returns without action. +* def +* The default property value. +* defs +* If zero, then the value is not stored in the KeyMap if the value +* is equal to the default value. +* status +* Pointer to the inherited status variable. + +*/ + +/* Check the inherited status */ + if( !astOK ) return; + +/* If the value is bad, ignore the entry. */ + if( value != AST__BAD ) { + +/* If the value is equal to the default value, and we are NOT storing + default values, ensure the KeyMap has no entry for the given key. */ + if( value == def && !defs ) { + astMapRemove( km, key ); + +/* Otherwise, store the value. */ + } else { + astMapPut0D( km, key, value, NULL ); + } + } +} + +static char *PutRegionProps( AstStcsChan *this, AstKeyMap *km, const char *id, + int indent, char *line, int *nc, int *crem, + int linelen, int *status ){ +/* +* Name: +* PutRegionProps + +* Purpose: +* Append STC-S space sub-phrase properties to the end of a string. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* char *PutRegionProps( AstStcsChan *this, AstKeyMap *km, const char *id, +* int indent, char *line, int *nc, int *crem, +* int linelen, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function converts the STC-S properties for the space sub-phrase +* supplied in a KeyMap into text, and appends them to the supplied +* line of text in the order required by STC-S. +* +* It is assumed that the sub-phrase identifier has already been put +* into the string. + +* Parameters: +* this +* The StcsChan. +* km +* Pointer to a KeyMap containing the STC-S properties. +* id +* Pointer to the sub-phrase identifier. +* indent +* If greater than or equal to zero, then it gives the number of +* spaces indentation to place before the first word (also indicates +* that a new-line should follow the last word of the argument). If +* negative, never use indentation. +* line +* Pointer to the buffer to receive the property values. +* nc +* Pointer to an int in which to store the number of characaters in +* the buffer. Updated on exit. +* crem +* Pointer to an int in which to store the maximum number of +* characters before a new line. Ignored if zero. Updated on exit. +* linelen +* The maximum number of character per line, or zero if all text is +* to be included in a single line. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the buffer. This will usually be "line", but may be +* different to "line" if it was necessary to expand the memory to make +* room for new properties. + +*/ + +/* Local Variables: */ + AstKeyMap *reg_props; + char *result; + char key[ 20 ]; + int i; + int ireg; + int nreg; + int spaceid; + +/* Initialise */ + result = line; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Temporarily ensure that an error is reported if an attempt is made to + access a non-existent KeyMap entry. */ + astSetKeyError( km, 1 ); + +/* Get the integer code for the space sub-phrase identifier. */ + spaceid = SpaceId( id, status ); + +/* Do each type of space sub-phrase. */ + if( spaceid == NULL_ID ) { + astError( AST__INTER, "astWrite(StcsChan): Illegal 'spaceid' value " + "in function PutRegionProps (internal AST programming " + "error).", status ); + + } else if( spaceid == POSITION_INTERVAL_ID ) { + result = AddItem( this, km, "LOLIMIT", NULL, result, nc, crem, linelen, status ); + result = AddItem( this, km, "HILIMIT", NULL, result, nc, crem, linelen, status ); + + } else if( spaceid == ALLSKY_ID ) { + + } else if( spaceid == CIRCLE_ID ) { + result = AddItem( this, km, "CENTRE", NULL, result, nc, crem, linelen, status ); + result = AddItem( this, km, "RADIUS", NULL, result, nc, crem, linelen, status ); + + } else if( spaceid == ELLIPSE_ID ) { + result = AddItem( this, km, "CENTRE", NULL, result, nc, crem, linelen, status ); + result = AddItem( this, km, "RADIUS1", NULL, result, nc, crem, linelen, status ); + result = AddItem( this, km, "RADIUS2", NULL, result, nc, crem, linelen, status ); + result = AddItem( this, km, "POSANGLE", NULL, result, nc, crem, linelen, status ); + + } else if( spaceid == BOX_ID ) { + result = AddItem( this, km, "CENTRE", NULL, result, nc, crem, linelen, status ); + result = AddItem( this, km, "BSIZE", NULL, result, nc, crem, linelen, status ); + + } else if( spaceid == POLYGON_ID ) { + result = AddItem( this, km, "VERTICES", NULL, result, nc, crem, linelen, status ); + + } else if( spaceid == CONVEX_ID ) { + astError( AST__INTER, "astWrite(StcsChan): No Convex support yet " + "(internal AST programming error).", status ); + + } else if( spaceid == POSITION_ID ) { + result = AddItem( this, km, "POSITION", NULL, result, nc, crem, linelen, status ); + +/* All remaining space id values are compound regions. */ + } else { + +/* Append an opening parenthesis. */ + result = astAppendString( result, nc, "( " ); + +/* If required, write out the text through the Channel sink function, + and start a new line. */ + if( indent >= 0 ) { + astPutNextText( this, result ); + *nc = 0; + *crem = linelen; + } + +/* Set the indentation for the next level down. */ + if( indent == 0 ) { + indent = 6; + } else if( indent > 0 ){ + indent += 3; + } + +/* Loop round all argument Regions. */ + astMapGet0I( km, "NREG", &nreg ); + for( ireg = 0; ireg < nreg; ireg++ ) { + sprintf( key, "REGION%d", ireg + 1 ); + astMapGet0A( km, key, ®_props ); + +/* Put any required indentation at the start of the line. */ + if( indent > 0 ) { + for( i = 0; i < indent; i++ ) { + result = astAppendString( result, nc, " " ); + } + *crem -= indent; + } + +/* Append the identifier for the next argument to the string. */ + result = AddItem( this, reg_props, "ID", NULL, result, nc, crem, + linelen, status ); + +/* Append the arguments to the string. */ + astMapGet0C( reg_props, "ID", &id ); + result = PutRegionProps( this, reg_props, id, indent, result, nc, + crem, linelen, status ); + +/* Write the text out to the sink function, and start a new line. */ + if( indent > 0 ) { + astPutNextText( this, result ); + *nc = 0; + *crem = linelen; + } + +/* Free resources. */ + reg_props = astAnnul( reg_props ); + } + +/* Decrease any indentation, and then append a closing parenthesis. */ + if( indent > 2 ) { + indent -= 3; + for( i = 0; i < indent; i++ ) { + result = astAppendString( result, nc, " " ); + } + } + result = astAppendString( result, nc, ") " ); + +/* If we are about to return fomr the top-level, start a new line. */ + if( indent > 0 && indent < 6 ) { + astPutNextText( this, result ); + *nc = 0; + for( i = 0; i < indent; i++ ) { + result = astAppendString( result, nc, " " ); + } + *crem = linelen - indent; + } + } + +/* Ensure that no error is reported if an attempt is made to access a + non-existent KeyMap entry. */ + astSetKeyError( km, 0 ); + +/* Return the buffer pointer. */ + return result; +} + +static AstObject *Read( AstChannel *this_channel, int *status ) { +/* +* Name: +* Read + +* Purpose: +* Read an Object from a Channel. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* AstObject *Read( AstChannel *this_channel, int *status ) + +* Class Membership: +* StcsChan member function (over-rides the astRead method +* inherited from the Channel class). + +* Description: +* This function reads an Object from an StcsChan. + +* Parameters: +* this +* Pointer to the StcsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new Object. +*/ + +/* Local Variables: */ + AstFrame *spacefrm; /* Pointer to SpaceFrame for space sub-phrase */ + AstFrameSet *fs; /* Temporary FrameSet */ + AstKeyMap *full_props; /* KeyMap holding all sub-phrase properties */ + AstKeyMap *props; /* KeyMap holding current sub-phrase properties */ + AstObject *new; /* Pointer to returned Object */ + AstObject *obj; /* Pointer to Object extracted from a KeyMap */ + AstPrism *tr; /* Temporary Region pointer */ + AstRegion *full_co; /* Region describing full coord position */ + AstRegion *full_enc; /* Region describing full enclosure */ + AstRegion *red_co; /* Region describing red-shift coord */ + AstRegion *red_enc; /* Region describing red-shift enclosure */ + AstRegion *space_co; /* Region describing space coord */ + AstRegion *space_enc; /* Region describing space enclosure */ + char **words; /* Array of pointers to individual words */ + int nword; /* Number of words returned */ + AstRegion *spec_co; /* Region describing spectral coord */ + AstRegion *spec_enc; /* Region describing spectral enclosure */ + AstRegion *time_co; /* Region describing time coord */ + AstRegion *time_enc; /* Region describing time enclosure */ + AstSpecFrame *redfrm; /* Pointer to SpecFrame for redshift sub-phrase */ + AstSpecFrame *specfrm; /* Pointer to SpecFrame for spectral sub-phrase */ + AstStcsChan *this; /* Pointer to the StcsChan structure */ + AstStdOfRestType sor; /* Standard of rest */ + AstSystemType sys; /* Frame System attribute value */ + AstTimeFrame *tf1; /* Temporary TimeFrame */ + AstTimeFrame *timefrm; /* Pointer to TimeFrame for time sub-phrase */ + AstTimeScaleType ts; /* TimeFrame TimeScale attribute value */ + WordContext con; /* Context for finding next source word */ + char *fbuf; /* Pointer to buffer holding document fragment */ + const char *new_ts; /* Time scale string */ + double epoch; /* Value to use for the Epoch attribue */ + double fill; /* Filling factor */ + double hilim; /* Axis upper limit */ + double lolim; /* Axis lower limit */ + double scale; /* Units scaling factor */ + int nval; /* No. of values read from KeyMap */ + double vals[ 10 ]; /* Values read from KeyMap */ + double start; /* Start time */ + double stop; /* Stop time */ + double time; /* Time value */ + double time_origin; /* Value to use as TimeFrame TimeOrigin*/ + double value; /* Axis value */ + int iaxis; /* Axis index */ + int is_skyframe; /* Is the space frame a SkyFrame? */ + int level; /* Warning reporting level */ + int naxes; /* No. of space Frame axes */ + int nwant; /* Number of objects to return */ + int use_co; /* Do we have a full coordinate position? */ + int use_enc; /* Do we have a full enclosure? */ + int want_co; /* Is the Coordinates component wanted? */ + int want_enc; /* Is the enclosure region wanted? */ + int want_props; /* Are the STC-S properties wanted? */ + const char *cval; /* Pointer to property value */ + const char *type; /* Type of redshift axis */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Obtain a pointer to the StcsChan structure. */ + this = (AstStcsChan *) this_channel; + +/* Initialise. */ + epoch = AST__BAD; + start = AST__BAD; + stop = AST__BAD; + time = AST__BAD; + time_co = NULL; + time_enc = NULL; + space_co = NULL; + space_enc = NULL; + spacefrm = NULL; + spec_co = NULL; + spec_enc = NULL; + red_co = NULL; + red_enc = NULL; + use_co = 1; + use_enc = 0; + scale = 1.0; + +/* Read the STC-S description from the external source, parse it, and + create a KeyMap containing the parsed property values. */ + full_props = ReadProps( this, status ); + +/* If the STC-S description contained a time sub-phrase, get the KeyMap + containing the proprties of the time sub-phrase, and then create AST + Regions describing the time coordinate value and its enclosing Region. */ + if( astMapGet0A( full_props, "TIME_PROPS", &obj ) ) { + props = (AstKeyMap *) obj; + +/* Create the default TimeFrame */ + timefrm = astTimeFrame( " ", status ); + +/* Get the TIMESCALE property from the KeyMap, and identify the corresponding + AST TimeScale. */ + ts = AST__BADTS; + new_ts = NULL; + level = 3; + + if( astMapGet0C( props, "TIMESCALE", &cval ) ) { + + if( astChrMatch( cval, "TT" ) ) { + ts = AST__TT; + + } else if( astChrMatch( cval, "TDT" ) ) { + ts = AST__TT; + new_ts = "TT"; + + } else if( astChrMatch( cval, "ET" ) ) { + ts = AST__TT; + new_ts = "TT"; + + } else if( astChrMatch( cval, "TAI" ) ) { + ts = AST__TAI; + + } else if( astChrMatch( cval, "IAT" ) ) { + ts = AST__TAI; + new_ts = "TAI"; + + } else if( astChrMatch( cval, "UTC" ) ) { + ts = AST__UTC; + + } else if( astChrMatch( cval, "TEB" ) ) { + ts = AST__TDB; + new_ts = "TDB"; + level = 1; + + } else if( astChrMatch( cval, "TDB" ) ) { + ts = AST__TDB; + + } else if( astChrMatch( cval, "TCG" ) ) { + ts = AST__TCG; + + } else if( astChrMatch( cval, "TCB" ) ) { + ts = AST__TCB; + + } else if( astChrMatch( cval, "LST" ) ) { + ts = AST__LMST; + + } else if( astChrMatch( cval, "nil" ) ) { + astAddWarning( this, 2, "Time scale defaulting to 'TAI'.", + "astRead", status ); + + } else if( astOK ){ + astError( AST__BADIN, "astRead(StcsChan): Unknown time scale '%s'.", + status, cval ); + } + + } else { + astAddWarning( this, 2, "Time scale defaulting to 'TAI'.", + "astRead", status ); + } + +/* Issue a warning if a different time-scale was substituted for the supplied + time-scale. */ + if( new_ts ) { + astAddWarning( this, level, "AST does not support the '%s' time " + "scale. The '%s' timescale is being used instead.", + "astRead", status, cval, new_ts ); + } + +/* If we got a time scale, set the TimeScale attribute in the TimeFrame + to the same value. */ + if( ts != AST__BADTS ) astSetTimeScale( timefrm, ts ); + +/* The AST TimeFrame class has no reference position, so allow any reference + position but issue a warning for anything other than "TOPOCENTER" and + "UNKNOWNRefPos". */ + if( !astMapGet0C( props, "REFPOS", &cval ) ) cval = "UNKNOWNRefPos"; + if( !astChrMatch( cval, "TOPOCENTER" ) ) { + astAddWarning( this, 1, "AST only supports topocentric time frames, " + "so 'TOPOCENTER' will be used in place of '%s'.", + "astRead", status, cval ); + } + +/* Get the times describes by the time sub-phrase as MJD values. */ + astMapGet0D( props, "MJDSTART", &start ); + astMapGet0D( props, "MJDTIME", &time ); + astMapGet0D( props, "MJDSTOP", &stop ); + +/* Get the earliest time represented by the time sub-phrase. We use this + as the TimeOrigin for the TimeFrame, and also as the Epoch for all + frames. */ + time_origin = start; + if( time_origin == AST__BAD ) time_origin = time; + if( time_origin == AST__BAD ) time_origin = stop; + epoch = time_origin; + +/* Store the TimeOrigin value in the TimeFrame, modifying the time values + accordingly. */ + if( time_origin != AST__BAD ) { + astSetTimeOrigin( timefrm, time_origin ); + if( start != AST__BAD ) start -= time_origin; + if( stop != AST__BAD ) stop -= time_origin; + if( time != AST__BAD ) time -= time_origin; + } + +/* Convert the epoch to TDB. */ + if( epoch != AST__BAD && ts != AST__TDB ) { + tf1 = astCopy( timefrm ); + astSetTimeScale( tf1, AST__TDB ); + fs = astConvert( timefrm, tf1, "" ); + astTran1( fs, 1, &epoch, 1, &epoch ); + fs = astAnnul( fs ); + tf1 = astAnnul( tf1 ); + } + +/* Store the epoch value in the TimeFrame. */ + if( epoch != AST__BAD ) astSetEpoch( timefrm, epoch ); + +/* Create a suitable Region to describe the enclosure for the time coords */ + if( start != AST__BAD || stop != AST__BAD ) { + time_enc = (AstRegion *) astInterval( timefrm, &start, &stop, + NULL, "", status ); + use_enc = 1; + } + +/* Create a suitable Region to describe the time coords contained within + the above enclosure. */ + if( time != AST__BAD ) { + time_co = (AstRegion *) SinglePointList( (AstFrame *) timefrm, + &time, NULL, status); + } else { + use_co = 0; + } + +/* If no enclosure Region was created for the time sub-phrase, use a + copy of any coordinate region. This is because each sub-phrase needs + to have an enclosure of some sort if they are to be combined in parallel + into an enclose for the whole CmpFrame. */ + if( ! time_enc && time_co ) time_enc = astCopy( time_co ); + +/* Set the filling factor. */ + if( time_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) { + astSetFillFactor( time_enc, fill ); + } + +/* Get the units in which the time error values are given, and get the + scaling factor that converts them into days. */ + if( astMapGet0C( props, "UNIT", &cval ) ) { + if( !strcmp( cval, "s" ) ) { + scale = 1.0/86400.0; + + } else if( !strcmp( cval, "d" ) ) { + scale = 1.0; + + } else if( !strcmp( cval, "a" ) ) { + scale = 365.25; + + } else if( !strcmp( cval, "yr" ) ) { + scale = 365.25; + + } else if( !strcmp( cval, "cy" ) ) { + scale = 36525.0; + + } else if( astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Unsupported " + "units (%s) for the time axis within an " + "STC-S description.", status, cval ); + } + + } else { + scale = 1.0/86400.0; + } + +/* Associate an uncertainty with the two Regions. */ + if( astMapGet1D( props, "DERROR", 2, &nval, vals ) ) { + if( nval > 1 ) { + astAddWarning( this, 1, "An STC-S time sub-phrase contains an " + "Error range. AST does not support error ranges " + "so the mid value will be used as the error.", + "astRead", status ); + vals[ 0 ] = 0.5*( vals[ 0 ] + vals[ 1 ] ); + } + + SetUnc( time_enc, time_co, (AstFrame *) timefrm, 0, scale, vals, 1, + status ); + } + +/* Free resources */ + props = astAnnul( props ); + timefrm = astAnnul( timefrm ); + } + +/* If the STC-S description contained a space sub-phrase, get the KeyMap + containing the proprties of the space sub-phrase, and then create AST + Regions describing the spatial position and its enclosing Region. */ + if( astMapGet0A( full_props, "SPACE_PROPS", &obj ) ) { + props = (AstKeyMap *) obj; + +/* The class of Frame (SkyFrame or basic Frame) is determined by the + "FLAVOR". */ + is_skyframe = 0; + if( astMapGet0C( props, "FLAVOUR", &cval ) ) { + + if( astChrMatch( cval, "SPHER2" ) ) { + spacefrm = (AstFrame *) astSkyFrame( "", status ); + is_skyframe = 1; + + } else if( astChrMatch( cval, "CART1" ) ) { + spacefrm = astFrame( 1, "", status ); + + } else if( astChrMatch( cval, "CART2" ) ) { + spacefrm = astFrame( 2, "", status ); + + } else if( astChrMatch( cval, "CART3" ) ) { + spacefrm = astFrame( 3, "", status ); + + } else { + astError( AST__BADIN, "astRead(StcsChan): Unsupported " + "space 'Flavor' (%s) found in STC-S description.", + status, cval ); + } + + } else { + spacefrm = (AstFrame *) astSkyFrame( "", status ); + is_skyframe = 1; + } + +/* Consider each supported space frame. Report an error for frames + not supported by AST. */ + if( astMapGet0C( props, "FRAME", &cval ) ) { + if( astChrMatch( cval, "ICRS" ) ) { + sys = AST__ICRS; + + } else if( astChrMatch( cval, "FK5" ) ) { + sys = AST__FK5; + + } else if( astChrMatch( cval, "FK4" ) ) { + sys = AST__FK4; + + } else if( astChrMatch( cval, "J2000" ) ) { + sys = AST__FK5; + + } else if( astChrMatch( cval, "B1950" ) ) { + sys = AST__FK4; + + } else if( astChrMatch( cval, "ECLIPTIC" ) ) { + sys = AST__ECLIPTIC; + + } else if( astChrMatch( cval, "GALACTIC" ) ) { + sys = AST__GALACTIC; + + } else if( astChrMatch( cval, "GALACTIC_II" ) ) { + sys = AST__GALACTIC; + + } else if( astChrMatch( cval, "SUPER_GALACTIC" ) ) { + sys = AST__SUPERGALACTIC; + + } else if( astChrMatch( cval, "UNKNOWNFrame" ) ) { + sys = AST__UNKNOWN; + + } else { + sys = AST__UNKNOWN; + astAddWarning( this, 1, "'UNKNOWNFrame' being used in place of " + "unsupported frame '%s' in an STC-S description.", + "astRead", status, cval ); + } + + } else { + cval = "UNKNOWNFrame"; + sys = AST__UNKNOWN; + astAddWarning( this, 1, "Space frame defaulting to 'UNKNOWNFrame' " + "in an STC-S description.", "astRead", status ); + } + +/* We can set the System (only needed for SkyFrames). */ + if( is_skyframe ) { + astSetSystem( spacefrm, sys ); + +/* If we have a basic Frame, set the Domain equal to the STC-S frame value. */ + } else { + astSetDomain( spacefrm, cval ); + } + +/* Set the epoch of the space frame. */ + if( epoch != AST__BAD ) astSetEpoch( spacefrm, epoch ); + +/* The AST Frame and SkyFrame class has no reference position, so for + SkyFrames we consider "TOPOCENTER" and "UNKNOWN" acceptable and all + other unsupported. For other Frames we allow any reference position. */ + if( !astMapGet0C( props, "REFPOS", &cval ) ) cval = "UNKNOWNRefPos"; + if( is_skyframe && !astChrMatch( cval, "TOPOCENTER" ) ) { + astAddWarning( this, 1, "AST only supports topocentric sky frames, " + "so 'TOPOCENTER' will be used in place of '%s'.", + "astRead", status, cval ); + } + +/* Get the number of spatial axes. */ + naxes = astGetNaxes( spacefrm ); + +/* Get the units strings. */ + if( !astMapGet0C( props, "UNIT", &cval ) ) { + if( is_skyframe ) { + cval = "deg"; + } else { + cval = "m"; + } + } + +/* In AST, SkyFrames always use radians, so set up a scaling factor to + convert supplied axis values into radians. */ + if( is_skyframe ) { + + if( !strcmp( cval, "deg" ) || !strcmp( cval, "deg deg" ) ) { + scale = AST__DD2R; + + } else if( !strcmp( cval, "arcmin" ) || !strcmp( cval, "arcmin arcmin" ) ) { + scale = AST__DD2R/60.0; + + } else if( !strcmp( cval, "arcsec" ) || !strcmp( cval, "arcsec arcsec" ) ) { + scale = AST__DD2R/3600.0; + + } else if( astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Unsupported " + "units (%s) for a spherical co-ordinate system " + "within an STC-S description: '%s'.", status, + cval, ContextFragment( &con, &fbuf, status ) ); + } + +/* Basic Frames can use any of the allowed units, so use a scale factor of + 1.0. Also set the active unit flag in the space frame to enable intelligent + units conversion by astConvert etc. */ + } else { + scale = 1.0; + astSetActiveUnit( spacefrm, 1 ); + +/* Basic Frames can have different units on different axes. So split the + units property up into separate words. */ + words = astChrSplit( cval, &nword ); + +/* Set values for the Unit attributes of the Frame. Replicate the last + supplied unit string for any extra axes. */ + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + if( iaxis < nword ) { + astSetUnit( spacefrm, iaxis, words[ iaxis ] ); + } else { + astSetUnit( spacefrm, iaxis, words[ nword - 1 ] ); + } + } + +/* Free resources. */ + for( iaxis = 0; iaxis < nword; iaxis++ ) { + words[ iaxis ] = astFree( words[ iaxis ] ); + } + words = astFree( words ); + } + +/* Create a suitable Region to enclose the space positions. This + includes scaling the supplied axis values to the units used by + the Frame. */ + space_enc = MakeSpaceRegion( props, spacefrm, scale, status ); + if( space_enc ) use_enc = 1; + +/* Create a suitable Region to describe the space coords contained within + the above enclosure. If any sub-phrase has no coordinate value, then + we cannot produce a PointList describing the complete coordinate set. */ + if( astMapGet1D( props, "DPOSITION", naxes, &nval, vals ) ) { + for( iaxis = 0; iaxis < nval; iaxis++ ) vals[ iaxis ] *= scale; + space_co = (AstRegion *) SinglePointList( spacefrm, vals, NULL, + status); + } else { + use_co = 0; + } + +/* If no enclosure Region was created for the space sub-phrase, use a + copy of any coordinate region. This is because each sub-phrase needs + to have an enclosure of some sort if they are to be combined in parallel + into an enclose for the whole CmpFrame. */ + if( ! space_enc && space_co ) space_enc = astCopy( space_co ); + +/* Set the filling factor. */ + if( space_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) { + astSetFillFactor( space_enc, fill ); + } + +/* Associate an uncertainty with the two Regions. */ + if( astMapGet1D( props, "DERROR", 2*naxes, &nval, vals ) ) { + if( nval > naxes ) { + astAddWarning( this, 1, "An STC-S space sub-phrase contains an " + "Error range. AST does not support error ranges " + "so the mid value will be used as the error.", + "astRead", status ); + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = 0.5*( vals[ iaxis ] + vals[ iaxis + naxes ] ); + } + +/* If insufficient error values have been supplied, replicate the last + one. */ + } else { + for( iaxis = nval; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = vals[ nval - 1 ]; + } + } + +/* Set the uncertainty in the two space regions. */ + SetUnc( space_enc, space_co, (AstFrame *) spacefrm, is_skyframe, + scale, vals, naxes, status ); + } + +/* Free resources */ + props = astAnnul( props ); + spacefrm = astAnnul( spacefrm ); + } + + + +/* If the STC-S description contained a velocity sub-phrase, issue a + warning. */ + if( astMapGet0A( full_props, "VELOCITY_PROPS", &obj ) ) { + astAddWarning( this, 1, "Ignoring a velocity sub-phrase found in " + "an STC-S description.", "astRead", status ); + obj = astAnnul( obj ); + } + + +/* If the STC-S description contained a spectral sub-phrase, get the KeyMap + containing the proprties of the spectral sub-phrase, and then create AST + Regions describing the spectral coordinate value and its enclosing Region. */ + if( astMapGet0A( full_props, "SPECTRAL_PROPS", &obj ) ) { + props = (AstKeyMap *) obj; + +/* Create the default SpecFrame */ + specfrm = astSpecFrame( " ", status ); + +/* Get the REFPOS property from the KeyMap, and identify the corresponding + AST StdOfRest. */ + sor = AST__BADSOR; + if( astMapGet0C( props, "REFPOS", &cval ) ) { + + if( astChrMatch( cval, "GEOCENTER" ) ) { + sor = AST__GESOR; + + } else if( astChrMatch( cval, "BARYCENTER" ) ) { + sor = AST__BYSOR; + + } else if( astChrMatch( cval, "HELIOCENTER" ) ) { + sor = AST__HLSOR; + + } else if( astChrMatch( cval, "TOPOCENTER" ) ) { + sor = AST__TPSOR; + + } else if( astChrMatch( cval, "LSR" ) || + astChrMatch( cval, "LSRK" ) ) { + sor = AST__LKSOR; + + } else if( astChrMatch( cval, "LSRD" ) ) { + sor = AST__LDSOR; + + } else if( astChrMatch( cval, "GALACTIC_CENTER" ) ) { + sor = AST__GLSOR; + + } else { + astAddWarning( this, 1, "Using 'HELIOCENTER' in place of " + "unsupported spectral reference position '%s' " + "found in an STC-S description.", "astRead", + status, cval ); + } + + } else { + astAddWarning( this, 2, "Spectral reference position defaulting to " + "'HELIOCENTER' in an STC-S description.", "astRead", + status ); + } + +/* If we got a ref pos, set the StdOfRest attribute in the SpecFrame. */ + if( sor != AST__BADSOR ) astSetStdOfRest( specfrm, sor ); + +/* Get the units. */ + if( !astMapGet0C( props, "UNIT", &cval ) ) cval = "Hz"; + + +/* Set the spectral system implied by the unit string. */ + if( !cval || !strcmp( cval, "Hz" ) || !strcmp( cval, "MHz" ) || + !strcmp( cval, "GHz" ) ) { + astSetSystem( specfrm, AST__FREQ ); + + } else if( !strcmp( cval, "m" ) || !strcmp( cval, "mm" ) || + !strcmp( cval, "um" ) || !strcmp( cval, "nm" ) || + !strcmp( cval, "Angstrom" ) ) { + astSetSystem( specfrm, AST__WAVELEN ); + + } else if( !strcmp( cval, "eV" ) || !strcmp( cval, "keV" ) || + !strcmp( cval, "MeV" ) ) { + astSetSystem( specfrm, AST__ENERGY ); + + } else if( astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Unsupported spectral " + "units (%s) found within an STC-S description.", + status, cval ); + } + +/* Set the units. */ + astSetUnit( specfrm, 0, cval ); + +/* Set the epoch */ + if( epoch != AST__BAD ) astSetEpoch( specfrm, epoch ); + +/* Create a suitable Region to describe the enclosure for the spectral + coords */ + if( astMapGet0D( props, "LOLIMIT", &lolim ) ) { + astMapGet0D( props, "HILIMIT", &hilim ); + spec_enc = (AstRegion *) astInterval( specfrm, &lolim, &hilim, + NULL, "", status ); + use_enc = 1; + } + +/* Create a suitable Region to describe the spectral coords contained within + the above enclosure. If any sub-phrase has no coordinate value, then + we cannot produce a PointList describing the complete coordinate set. */ + if( astMapGet0D( props, "SPECTRAL", &value ) ) { + spec_co = (AstRegion *) SinglePointList( (AstFrame *) specfrm, + &value, NULL, status); + } else { + use_co = 0; + } + +/* If no enclosure Region was created for the spectral sub-phrase, use a + copy of any coordinate region. This is because each sub-phrase needs + to have an enclosure of some sort if they are to be combined in parallel + into an enclose for the whole CmpFrame. */ + if( ! spec_enc && spec_co ) spec_enc = astCopy( spec_co ); + +/* Set the filling factor. */ + if( spec_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) { + astSetFillFactor( spec_enc, fill ); + } + + +/* Associate an uncertainty with the two Regions. */ + if( astMapGet1D( props, "DERROR", 2, &nval, vals ) ) { + if( nval > 1 ) { + astAddWarning( this, 1, "An STC-S spectral sub-phrase contains an " + "Error range. AST does not support error ranges " + "so the mid value will be used as the error.", + "astRead", status ); + vals[ 0 ] = 0.5*( vals[ 0 ] + vals[ 1 ] ); + } + + SetUnc( spec_enc, spec_co, (AstFrame *) specfrm, 0, 1.0, vals, 1, + status ); + } + +/* Free resources */ + props = astAnnul( props ); + specfrm = astAnnul( specfrm ); + } + + + + +/* If the STC-S description contained a redshift sub-phrase, get the KeyMap + containing the properties of the redshift sub-phrase, and then create AST + Regions describing the redshift coordinate value and its enclosing Region. */ + if( astMapGet0A( full_props, "REDSHIFT_PROPS", &obj ) ) { + props = (AstKeyMap *) obj; + +/* Create the default SpecFrame */ + redfrm = astSpecFrame( "Domain=REDSHIFT", status ); + +/* Get the REFPOS property from the KeyMap, and identify the corresponding + AST StdOfRest. */ + sor = AST__BADSOR; + if( astMapGet0C( props, "REFPOS", &cval ) ) { + + if( astChrMatch( cval, "GEOCENTER" ) ) { + sor = AST__GESOR; + + } else if( astChrMatch( cval, "BARYCENTER" ) ) { + sor = AST__BYSOR; + + } else if( astChrMatch( cval, "HELIOCENTER" ) ) { + sor = AST__HLSOR; + + } else if( astChrMatch( cval, "TOPOCENTER" ) ) { + sor = AST__TPSOR; + + } else if( astChrMatch( cval, "LSR" ) || + astChrMatch( cval, "LSRK" ) ) { + sor = AST__LKSOR; + + } else if( astChrMatch( cval, "LSRD" ) ) { + sor = AST__LDSOR; + + } else if( astChrMatch( cval, "GALACTIC_CENTER" ) ) { + sor = AST__GLSOR; + + } else { + astAddWarning( this, 1, "Using 'HELIOCENTER' in place of " + "unsupported redshift reference position '%s' " + "found in an STC-S description.", "astRead", + status, cval ); + } + + } else { + astAddWarning( this, 2, "Redshift reference position defaulting to " + "'HELIOCENTER' in an STC-S description.", "astRead", + status ); + } + +/* If we got a ref pos, set the StdOfRest attribute in the SpecFrame. */ + if( sor != AST__BADSOR ) astSetStdOfRest( redfrm, sor ); + +/* Get the redshift type. */ + if( !astMapGet0C( props, "TYPE", &type ) ) type = "REDSHIFT"; + +/* Now get the velocity definition, and set the equivalent SpecFrame + System value. AST only supports optical redshift, so report an error + or a warning for unsupported combinations. */ + if( astMapGet0C( props, "DOPPLERDEF", &cval ) ){ + + if( astChrMatch( cval, "OPTICAL" ) ) { + if( astChrMatch( type, "VELOCITY" ) ){ + astSetSystem( redfrm, AST__VOPTICAL ); + } else { + astSetSystem( redfrm, AST__REDSHIFT ); + } + + } else if( astChrMatch( cval, "RADIO" ) ) { + if( astChrMatch( type, "VELOCITY" ) ){ + astSetSystem( redfrm, AST__VRADIO ); + } else { + astSetSystem( redfrm, AST__REDSHIFT ); + astAddWarning( this, 1, "STC-S RADIO redshift not supported. " + "Assuming OPTICAL redshift instead.", "astRead", + status ); + } + + } else if( astChrMatch( cval, "RELATIVISTIC" ) ) { + if( astChrMatch( type, "VELOCITY" ) ){ + astSetSystem( redfrm, AST__VREL ); + } else { + astSetSystem( redfrm, AST__REDSHIFT ); + astAddWarning( this, 1, "STC-S RELATIVISTIC redshift not supported. " + "Assuming OPTICAL redshift instead.", "astRead", + status ); + } + + } else { + if( astChrMatch( type, "VELOCITY" ) ){ + astSetSystem( redfrm, AST__VOPTICAL ); + astAddWarning( this, 1, "Doppler velocity definition defaulting" + " to 'OPTICAL' in an STC-S description.", + "astRead", status ); + + } else { + astSetSystem( redfrm, AST__REDSHIFT ); + } + } + } + +/* Set the units. */ + if( astChrMatch( type, "VELOCITY" ) ){ + if( astMapGet0C( props, "UNIT", &cval ) ) { + astSetUnit( redfrm, 0, cval ); + } else { + astSetUnit( redfrm, 0, "km/s" ); + } + + } else if( astMapGet0C( props, "UNIT", &cval ) ) { + astAddWarning( this, 1, "Ignoring units (%s) specified for REDSHIFT " + "in an STC-S description.", "astRead", status, cval ); + } + +/* Set the epoch */ + if( epoch != AST__BAD ) astSetEpoch( redfrm, epoch ); + +/* Create a suitable Region to describe the enclosure for the redshift + coords */ + if( astMapGet0D( props, "LOLIMIT", &lolim ) ) { + astMapGet0D( props, "HILIMIT", &hilim ); + red_enc = (AstRegion *) astInterval( redfrm, &lolim, &hilim, + NULL, "", status ); + use_enc = 1; + } + +/* Create a suitable Region to describe the redshift coords contained within + the above enclosure. If any sub-phrase has no coordinate value, then + we cannot produce a PointList describing the complete coordinate set. */ + if( astMapGet0D( props, "REDSHIFT", &value ) ) { + red_co = (AstRegion *) SinglePointList( (AstFrame *) redfrm, + &value, NULL, status); + } else { + use_co = 0; + } + +/* If no enclosure Region was created for the redshift sub-phrase, use a + copy of any coordinate region. This is because each sub-phrase needs + to have an enclosure of some sort if they are to be combined in parallel + into an enclose for the whole CmpFrame. */ + if( ! red_enc && red_co ) red_enc = astCopy( red_co ); + +/* Set the filling factor. */ + if( red_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) { + astSetFillFactor( red_enc, fill ); + } + +/* Associate an uncertainty with the two Regions. */ + if( astMapGet1D( props, "DERROR", 2, &nval, vals ) ) { + if( nval > 1 ) { + astAddWarning( this, 1, "An STC-S redshift sub-phrase contains an " + "Error range. AST does not support error ranges " + "so the mid value will be used as the error.", + "astRead", status ); + vals[ 0 ] = 0.5*( vals[ 0 ] + vals[ 1 ] ); + } + + SetUnc( red_enc, red_co, (AstFrame *) redfrm, 0, 1.0, vals, 1, + status ); + } + +/* Free resources */ + props = astAnnul( props ); + redfrm = astAnnul( redfrm ); + } + +/* If a particular position was specified by the STC_S document, create the + full position from the individual sub-phrase position */ + if( use_co ) { + new = time_co ? astClone( time_co ) : NULL; + + if( space_co ) { + if( new ) { + tr = astPrism( new, space_co, "", status ); + (void) astAnnul( new ); + new = (AstObject *) tr; + } else { + new = astClone( space_co ); + } + } + + if( spec_co ) { + if( new ) { + tr = astPrism( new, spec_co, "", status ); + (void) astAnnul( new ); + new = (AstObject *) tr; + } else { + new = astClone( spec_co ); + } + } + + if( red_co ) { + if( new ) { + tr = astPrism( new, red_co, "", status ); + (void) astAnnul( new ); + new = (AstObject *) tr; + } else { + new = astClone( red_co ); + } + } + + if( new ) { + full_co = astSimplify( new ); + new = astAnnul( new ); + } else { + full_co = NULL; + } + + } else { + full_co = NULL; + } + +/* If an enclosing volume was specified by the STC_S document, create the + full enclosure Region from the individual sub-phrase enclosure Regions. */ + if( use_enc ) { + new = time_enc ? astClone( time_enc ) : NULL; + + if( space_enc ) { + if( new ) { + tr = astPrism( new, space_enc, "", status ); + (void) astAnnul( new ); + new = (AstObject *) tr; + } else { + new = astClone( space_enc ); + } + } + + if( spec_enc ) { + if( new ) { + tr = astPrism( new, spec_enc, "", status ); + (void) astAnnul( new ); + new = (AstObject *) tr; + } else { + new = astClone( spec_enc ); + } + } + + if( red_enc ) { + if( new ) { + tr = astPrism( new, red_enc, "", status ); + (void) astAnnul( new ); + new = (AstObject *) tr; + } else { + new = astClone( red_enc ); + } + } + full_enc = astSimplify( new ); + new = astAnnul( new ); + + } else { + full_enc = NULL; + } + +/* See which, and how many, items are to be returned. */ + nwant = 0; + if( ( want_enc = astGetStcsArea( this ) ) ) nwant++; + if( ( want_co = astGetStcsCoords( this ) ) ) nwant++; + if( ( want_props = astGetStcsProps( this ) ) ) nwant++; + +/* If one, and only one, of the three items is to be returned, return it. */ + new = NULL; + if( nwant == 1 ) { + if( want_enc && full_enc ) { + new = astClone( full_enc ); + } else if( want_co && full_co ) { + new = astClone( full_co ); + } else if( want_props && full_props ){ + new = astClone( full_props ); + } + +/* If more than one item is to be returned, put them into a KeyMap and + return the KeyMap. */ + } else if( nwant > 1 ) { + new = (AstObject *) astKeyMap( " ", status ); + if( want_enc && full_enc ) astMapPut0A( new, "AREA", full_enc, NULL ); + if( want_co && full_co ) astMapPut0A( new, "COORDS", full_co, NULL ); + if( want_props && full_props ) astMapPut0A( new, "PROPS", full_props, NULL ); + +/* Report an error if nothing is to be returned. */ + } else if( astOK ){ + astError( AST__ATTIN, "astRead(StcsChan): The StcsArea, StcsCoords " + "and StcsProps attributes indicate that nothing is to be " + "returned (possible programming error).", status ); + } + +/* Free resources */ + if( space_enc ) space_enc = astAnnul( space_enc ); + if( spec_enc ) spec_enc = astAnnul( spec_enc ); + if( time_enc ) time_enc = astAnnul( time_enc ); + if( red_enc ) red_enc = astAnnul( red_enc ); + if( space_co ) space_co = astAnnul( space_co ); + if( spec_co ) spec_co = astAnnul( spec_co ); + if( time_co ) time_co = astAnnul( time_co ); + if( red_co ) red_co = astAnnul( red_co ); + if( full_enc ) full_enc = astAnnul( full_enc ); + if( full_co ) full_co = astAnnul( full_co ); + if( full_props ) full_props = astAnnul( full_props ); + +/* If an error occurred, clean up by deleting the new Object and + return a NULL pointer. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the pointer to the new Object. */ + return new; +} + +static AstKeyMap *ReadProps( AstStcsChan *this, int *status ) { +/* +* Name: +* ReadProps + +* Purpose: +* Read STC-S properties from the source and store in a KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* AstKeyMap *ReadProps( AstStcsChan *this, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function parses the list of space-separated words read from the +* source function, identifies the purpose of each word within the STC-S +* description, and stores the words in a returned KeyMap. + +* Parameters: +* this +* Pointer to the StcsChan. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new KeyMap. This will contain up to five entries +* with any or all of the following keys: TIME_PROPS, SPACE_PROPS, +* VELOCITY_PROPS, SPECTRAL_PROPS, REDSHIFT_PROPS. If an entry is absent, +* it means the STC-S description did not contain the corresponding +* sub-phrase. The value associated with each of these entries will be a +* KeyMap. These will contain values for the sub-phrase proprties read +* from the STC-S description. Properties that are not specified in +* the STC-S description will not be present in the KeyMap. The values +* stored in the KeyMap are the words read form the STC-S description +* without any conversion or other processing. +*/ + +/* Local Constants: */ +#define MAXVAL 6 + +/* Local Variables: */ + AstKeyMap *props; /* KeyMap holding current sub-phrase properties */ + AstKeyMap *result; /* Returned KeyMap holding all properties */ + AstTimeFrame *timefrm; /* Used for unformatting ISO date-times */ + WordContext con; /* Context for finding next source word */ + char *fbuf; /* Pointer to buffer holding document fragment */ + char *prop; /* String holding complete property value */ + const char *subphrase; /* Name of current sub phrase */ + const char *t; /* Temporary character string pointer */ + const char *word; /* Pointer to next source word */ + double val[ MAXVAL ]; /* Array of numerical property values */ + double start; /* Start time (MJD) */ + double stop; /* Stop time (MJD) */ + double time; /* Time value (MJD) */ + double value; /* Axis value */ + int iaxis; /* Axis index */ + int is_jd; /* Is time value a JD rather than an MJD? */ + int nunit; /* Number of units strings supplied */ + int nval; /* Number of numerical values read */ + int naxes; /* No. of space Frame axes */ + int nc; /* Number of characters written to string */ + int new_word; /* Get a new word at the end of the pass? */ + int redid; /* Redshift sub-phrase component identifier */ + int spaceid; /* Space sub-phrase component identifier */ + int specid; /* Spectral sub-phrase component identifier */ + int timeid; /* Time sub-phrase component identifier */ + int velid; /* Velocity sub-phrase component identifier */ + +/* The stage reached in the parsing of the STC-S description is indicated + by the "look_for" variable. This variable is allowed the following + values, indicating the item that is to be checked for next. */ + enum look_for_type { + ERROR, + FILL_FACTOR, + FLAVOUR, + FRAME, + LIMITS, + PIX_SIZE, + POSITION, + POSITION_INTERVAL, + REDSHIFT_IDENTIFIER, + RED_SPEC_LABEL, + RED_SPEC_VALUE, + REFPOS, + RESOLUTION, + SIZE, + SPACE_IDENTIFIER, + SPECTRAL_IDENTIFIER, + START, + STOP, + TIME, + TIME_IDENTIFIER, + TIME_LABEL, + TIME_SCALE, + TYPE_DOPPLER, + UNIT, + VELOCITY_IDENTIFIER, + VELOCITY + } look_for; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Create the returned KeyMap. */ + result = astKeyMap( " ", status ); + +/* Initialise the word search context. */ + (void) GetNextWord( NULL, &con, status ); + +/* Get a pointer to the first word in the STC-S description. */ + word = GetNextWord( this, &con, status ); + +/* Indicate we are currently looking for the time sub-phrase (the first + item in an STC-S description). */ + look_for = TIME_IDENTIFIER; + +/* Initialise everything else. */ + fbuf = NULL; + naxes = 0; + prop = NULL; + props = NULL; + redid = NULL_ID; + spaceid = NULL_ID; + specid = NULL_ID; + subphrase = NULL; + t = NULL; + timeid = NULL_ID; + velid = NULL_ID; + timefrm = NULL; + +/* Loop until all words in the STC-S description have been interpreted or + an error has occurred. */ + while( word && astOK ) { + +/* Initialise a flag to indicate that we have interpreted the current word + sucesfully and so will need to get a new word before the next pass through + this loop. If it turns out that we cannot interpret the current word + in this pass, then this flag will be set to zero at some point, thus + preventing a new word from being acquired and causing another attempt to + re-interpret the current word in a different context. */ + new_word = 1; + +/* If we are currently looking for the time sub-phrase, see if the current + word is any of the known time sub-phrase identifiers. Is so, move on + to read the associated sub-phrase component. */ + if( look_for == TIME_IDENTIFIER ) { +/* ------------------------------------------------------------------ */ + +/* Assume that we will be moving on to read the fill factor (most time + sub-phrases start with the fill factor ). */ + look_for = FILL_FACTOR; + +/* Now check the word to see if it a known time sub-phrase identifier. */ + if( astChrMatch( word, "TimeInterval" ) ) { + timeid = TIME_INTERVAL_ID; + + } else if( astChrMatch( word, "StartTime" ) ) { + timeid = START_TIME_ID; + + } else if( astChrMatch( word, "StopTime" ) ) { + timeid = STOP_TIME_ID; + + } else if( astChrMatch( word, "Time" ) ) { + look_for = TIME_SCALE; /* After "Time", we move on to find the + timeid = TIME_ID; time-scale, not the fill factor */ + +/* If the word is not a known time sub-phrase identifier, indicate that we + should attempt to re-interpret the current word as a space sub-phrase + identifier, rather than getting a new word. */ + } else { + look_for = SPACE_IDENTIFIER; + new_word = 0; + } + +/* If we have found a time sub-phrase identifier, create a KeyMap to hold + the properties of the time sub-phrase, and store the time sub-phrase + identifier in the new KeyMap. */ + if( timeid != NULL_ID ) { + subphrase = "time"; + props = astKeyMap( " ", status ); + astMapPut0A( result, "TIME_PROPS", props, NULL ); + astMapPut0C( props, "ID", word, NULL ); + naxes = 1; + } + + + +/* If we are currently looking for the space sub-phrase, see if the current + word is any of the known space sub-phrase identifiers. Is so, move on + to read the associated sub-phrase component. */ + } else if( look_for == SPACE_IDENTIFIER ) { +/* ------------------------------------------------------------------ */ + +/* Indicate we have finished any preceding time sub-phrase. */ + timeid = NULL_ID; + +/* Now check the word to see if it a known space sub-phrase identifier. */ + spaceid = SpaceId( word, status ); + +/* Decide what to look for next. */ + if( spaceid == POSITION_ID ) { + look_for = FRAME; + + } else if( spaceid != NULL_ID ) { + look_for = FILL_FACTOR; + +/* If the word is not a known space sub-phrase identifier, move on to + re-interpret it as a Spectral sub-phrase identifier. */ + } else { + look_for = SPECTRAL_IDENTIFIER; + new_word = 0; + } + +/* If we have found a space sub-phrase identifier, create a KeyMap to hold + the properties of the space sub-phrase, and store the space sub-phrase + identifier in the new KeyMap. */ + if( spaceid != NULL_ID ) { + subphrase = "space"; + if( props ) props = astAnnul( props ); + props = astKeyMap( " ", status ); + astMapPut0A( result, "SPACE_PROPS", props, NULL ); + astMapPut0C( props, "ID", word, NULL ); + } + + + +/* If we are currently looking for the velocity sub-phrase, see if the current + word is any of the known velocity sub-phrase identifiers. Is so, move on + to read the associated sub-phrase component. */ + } else if( look_for == VELOCITY_IDENTIFIER ) { +/* ------------------------------------------------------------------ */ + +/* Indicate we have finished any preceding space sub-phrase. */ + spaceid = NULL_ID; + +/* Now check the word to see if it a known velocity sub-phrase identifier. */ + if( astChrMatch( word, "VelocityInterval" ) ) { + velid = VELOCITY_INTERVAL_ID; + look_for = FILL_FACTOR; + + } else if( astChrMatch( word, "Velocity" ) ) { + velid = VELOCITY_ID; + look_for = VELOCITY; + +/* If the word is not a known velocity sub-phrase identifier, move on to + re-interpret it as a Spectral sub-phrase identifier. */ + } else { + look_for = SPECTRAL_IDENTIFIER; + new_word = 0; + } + +/* If we have found a velocity sub-phrase identifier, create a KeyMap to + hold the properties of the velocity sub-phrase, and store the velocity + sub-phrase identifier in the new KeyMap. */ + if( velid != NULL_ID ) { + subphrase = "velocity"; + if( props ) props = astAnnul( props ); + props = astKeyMap( " ", status ); + astMapPut0A( result, "VELOCITY_PROPS", props, NULL ); + astMapPut0C( props, "ID", word, NULL ); + } + + + +/* If we are currently looking for the spectral sub-phrase, see if the + word is any of the known spectral sub-phrase identifiers. Is so, move + on to read the associated sub-phrase component. */ + } else if( look_for == SPECTRAL_IDENTIFIER ) { +/* ------------------------------------------------------------------ */ + +/* Indicate we have finished any preceding velocity sub-phrase. */ + velid = NULL_ID; + +/* Now check the word to see if it a known spectral sub-phrase identifier. */ + if( astChrMatch( word, "SpectralInterval" ) ) { + look_for = FILL_FACTOR; /* Move on to find the fill factor */ + specid = SPECTRAL_INTERVAL_ID; + + } else if( astChrMatch( word, "Spectral" ) ) { + look_for = REFPOS; /* Move on to find the refpos */ + specid = SPECTRAL_ID; + +/* If the word is not a known spectral sub-phrase identifier, move on to + look for the Redshift sub-phrase. */ + } else { + look_for = REDSHIFT_IDENTIFIER; + new_word = 0; + } + +/* If we have found a spectral sub-phrase identifier, create a KeyMap to + hold the properties of the spectral sub-phrase, and store the spectral + sub-phrase identifier in the new KeyMap. */ + if( specid != NULL_ID ) { + subphrase = "spectral"; + if( props ) props = astAnnul( props ); + props = astKeyMap( " ", status ); + astMapPut0A( result, "SPECTRAL_PROPS", props, NULL ); + astMapPut0C( props, "ID", word, NULL ); + naxes = 1; + } + + + +/* If we are currently looking for the redshift sub-phrase, see if the + word is any of the known redshift sub-phrase identifiers. Is so, move + on to read the associated sub-phrase component. */ + } else if( look_for == REDSHIFT_IDENTIFIER ) { +/* ------------------------------------------------------------------ */ + +/* Indicate we have finished any preceding spectral sub-phrase. */ + specid = NULL_ID; + +/* Now check the word to see if it a known spectral sub-phrase identifier. */ + if( astChrMatch( word, "RedshiftInterval" ) ) { + look_for = FILL_FACTOR; /* Move on to find the fill factor */ + redid = REDSHIFT_INTERVAL_ID; + + } else if( astChrMatch( word, "Redshift" ) ) { + look_for = REFPOS; /* Move on to find the refpos */ + redid = REDSHIFT_ID; + +/* If the word is not a known redshift sub-phrase identifier, report a + warning. */ + } else if( word[ 0 ] && astOK ) { + astError( AST__BADIN, "astRead(%s): Unsupported or irrelevant " + "word '%s' found in STC-S %s sub-phrase: '%s'.", status, + astGetClass( this ), word, subphrase, + ContextFragment( &con, &fbuf, status ) ); + new_word = 0; + } + +/* If we have found a redshift sub-phrase identifier, create a KeyMap to + hold the properties of the redshift sub-phrase, and store the redshift + sub-phrase identifier in the new KeyMap. */ + if( redid != NULL_ID ) { + subphrase = "redshift"; + if( props ) props = astAnnul( props ); + props = astKeyMap( " ", status ); + astMapPut0A( result, "REDSHIFT_PROPS", props, NULL ); + astMapPut0C( props, "ID", word, NULL ); + naxes = 1; + } + +/* Indicate we can now end when we run out of input words. */ + con.done = 1; + + + +/* If we are currently looking for a fill factor... */ + } else if( look_for == FILL_FACTOR ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is "fillfactor" attempt to read the numerical filling + factor from the next word. If this fails, or if the current word is + not "fillfactor", indicate that we will be re-interpreting the current + word in a new context and so do not need a new word. */ + if( astChrMatch( word, "fillfactor" ) ) { + word = GetNextWord( this, &con, status ); + if( astChr2Double( word ) == AST__BAD ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a numerical " + "filling factor, but found '%s' in the %s " + "sub-phrase of STC-S description: '%s'.", status, + word, subphrase, ContextFragment( &con, &fbuf, + status ) ); + new_word = 0; + } + } else { + new_word = 0; + } + +/* If we are reading a time sub-phrase, move on to read the timescale. */ + if( timeid != NULL_ID ) { + look_for = TIME_SCALE; + +/* If we are reading a space sub-phrase, move on to read the frame. */ + } else if( spaceid != NULL_ID ) { + look_for = FRAME; + +/* If we are reading a velocity sub-phrase, move on to read the limits. */ + } else if( velid != NULL_ID ) { + look_for = LIMITS; + +/* Otherwise (i.e. for spectral and redshift sub-phrases) move on to read + the refpos. */ + } else { + look_for = REFPOS; + } + +/* If the word was usable, record it as the fillfactor property. */ + if( new_word ) astMapPut0C( props, "FILLFACTOR", word, NULL ); + + + +/* If we are currently looking for a time scale... */ + } else if( look_for == TIME_SCALE ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is a recognised STC-S timescale, store it in the + props KeyMap. Otherwise, indicate that the word can be re-used in the + next context. */ + if( astChrMatch( word, "TT" ) || + astChrMatch( word, "TDT" ) || + astChrMatch( word, "ET" ) || + astChrMatch( word, "TAI" ) || + astChrMatch( word, "IAT" ) || + astChrMatch( word, "UTC" ) || + astChrMatch( word, "TEB" ) || + astChrMatch( word, "TDB" ) || + astChrMatch( word, "TCG" ) || + astChrMatch( word, "TCB" ) || + astChrMatch( word, "LST" ) || + astChrMatch( word, "nil" ) ) { + + astMapPut0C( props, "TIMESCALE", word, NULL ); + + } else { + new_word = 0; + } + +/* Move on to look for a refpos */ + look_for = REFPOS; + + + +/* If we are currently looking for a space frame... */ + } else if( look_for == FRAME ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is a recognised STC-S spatial frame, store it in + the props KeyMap. Otherwise, indicate that the word can be re-used. */ + if( astChrMatch( word, "ICRS" ) || + astChrMatch( word, "FK5" ) || + astChrMatch( word, "FK4" ) || + astChrMatch( word, "J2000" ) || + astChrMatch( word, "B1950" ) || + astChrMatch( word, "ECLIPTIC" ) || + astChrMatch( word, "GALACTIC" ) || + astChrMatch( word, "GALACTIC_II" ) || + astChrMatch( word, "SUPER_GALACTIC" ) || + astChrMatch( word, "GEO_C" ) || + astChrMatch( word, "GEO_D" ) || + astChrMatch( word, "UNKNOWNFrame" ) ) { + + astMapPut0C( props, "FRAME", word, NULL ); + + } else { + new_word = 0; + } + +/* Move on to look for a refpos */ + look_for = REFPOS; + + + +/* If we are currently looking for a refpos... */ + } else if( look_for == REFPOS ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is a recognised STC-S reference position, store it in + the props KeyMap. Otherwise, indicate that the word can be re-used. The + first group of reference positions apply to all sub-phrases. */ + if( astChrMatch( word, "GEOCENTER" ) || + astChrMatch( word, "BARYCENTER" ) || + astChrMatch( word, "HELIOCENTER" ) || + astChrMatch( word, "TOPOCENTER" ) || + astChrMatch( word, "GALACTIC_CENTER" ) || + astChrMatch( word, "EMBARYCENTER" ) || + astChrMatch( word, "MOON" ) || + astChrMatch( word, "MERCURY" ) || + astChrMatch( word, "VENUS" ) || + astChrMatch( word, "MARS" ) || + astChrMatch( word, "JUPITER" ) || + astChrMatch( word, "SATURN" ) || + astChrMatch( word, "URANUS" ) || + astChrMatch( word, "NEPTUNE" ) || + astChrMatch( word, "PLUTO" ) || + astChrMatch( word, "UNKNOWNRefPos" ) ) { + + astMapPut0C( props, "REFPOS", word, NULL ); + +/* This group of reference positions apply only to spectral and redshift + sub-phrases. */ + } else if( astChrMatch( word, "LSR" ) || + astChrMatch( word, "LSRK" ) || + astChrMatch( word, "LSRD" ) || + astChrMatch( word, "LOCAL_GROUP_CENTER" ) ) { + + if( specid != NULL_ID || redid != NULL_ID ) { + astMapPut0C( props, "REFPOS", word, NULL ); + + } else if( astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Illegal reference " + "position '%s' found in the %s sub-phrase of " + "STC-S description: '%s'.", status, word, + subphrase, ContextFragment( &con, &fbuf, status ) ); + new_word = 0; + } + + } else { + new_word = 0; + } + +/* Choose what to look for next on the basis of the type of sub-phrase + currently being interpreted. */ + if( timeid == TIME_INTERVAL_ID ){ + look_for = START; /* Move on to find the start time */ + + } else if( timeid == START_TIME_ID ){ + look_for = START; /* Move on to find the start time */ + + } else if( timeid == STOP_TIME_ID ){ + look_for = STOP; /* Move on to find the stop time */ + + } else if( timeid == TIME_ID ){ + look_for = TIME; /* Move on to find the time */ + + } else if( spaceid != NULL_ID ){ + look_for = FLAVOUR; /* Move on to find the spatial flavour */ + + } else if( specid == SPECTRAL_INTERVAL_ID ) { + look_for = LIMITS; /* Move on to find the spectral limits */ + + } else if( specid == SPECTRAL_ID ) { + look_for = RED_SPEC_VALUE; /* Move on to find the spectral value */ + + } else if( redid == REDSHIFT_INTERVAL_ID ) { + look_for = TYPE_DOPPLER; /* Move on to find the redshift type */ + + } else if( redid == REDSHIFT_ID ) { + look_for = TYPE_DOPPLER; /* Move on to find the redshift type */ + + } else if( astOK ) { /* Should never happen */ + astError( AST__INTER, "astRead(StcsChan): Sanity check 1 fails in " + "function ReadProps (AST internal programming error).", + status ); + new_word = 0; + } + + + + + +/* If we are currently looking for a start time... */ + } else if( look_for == START ) { +/* ------------------------------------------------------------------ */ + +/* Save the current word as the start of the START value. */ + nc = 0; + prop = astAppendString( prop, &nc, word ); + +/* If the current word is "JD" or "MJD", the following word should be + numerical. */ + is_jd = astChrMatch( word, "JD" ); + if( is_jd || astChrMatch( word, "MJD" ) ) { + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + if( value == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected numerical " + "value in Start time, but found '%s %s' in STC-S " + "description: '%s'.", status, prop, word, + ContextFragment( &con, &fbuf, status ) ); + +/* Append the second word to the first word. */ + } else { + prop = astAppendString( prop, &nc, " " ); + prop = astAppendString( prop, &nc, word ); + } + +/* Convert JD to MJD if required. */ + start = is_jd ? value - 2400000.5 : value; + +/* Otherwise, the current word should be an ISO date. Use a TimeFrame + to check the string. */ + } else { + if( !timefrm ) timefrm = astTimeFrame( " ", status ); + if( !astUnformat( timefrm, 0, word, &start ) && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected ISO date " + "string Start time, but found '%s' in an STC-S " + "description: '%s'.", status, word, + ContextFragment( &con, &fbuf, status ) ); + } + } + +/* Record the START property. */ + astMapPut0C( props, "START", prop, NULL ); + astMapPut0D( props, "MJDSTART", start, NULL ); + +/* Decide what to do next. */ + if( timeid == TIME_INTERVAL_ID ){ + look_for = STOP; /* Move on to find the stop time */ + + } else if( timeid == START_TIME_ID ){ + look_for = TIME_LABEL; /* Move on to find the "coord" time */ + + } + + + +/* If we are currently looking for a stop time... */ + } else if( look_for == STOP ) { +/* ------------------------------------------------------------------ */ + +/* Save the current word as the start of the STOP value. */ + nc = 0; + prop = astAppendString( prop, &nc, word ); + +/* If the current word is "JD" or "MJD", the following word should be + numerical. */ + is_jd = astChrMatch( word, "JD" ); + if( is_jd || astChrMatch( word, "MJD" ) ) { + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + if( value == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected numerical " + "value in Stop time, but found '%s %s' in STC-S " + "description: '%s'.", status, prop, word, + ContextFragment( &con, &fbuf, status ) ); + +/* Append the second word to the first word. */ + } else { + prop = astAppendString( prop, &nc, " " ); + prop = astAppendString( prop, &nc, word ); + } + +/* Convert JD to MJD if required. */ + stop = is_jd ? value - 2400000.5 : value; + +/* Otherwise, the current word should be an ISO date. Use a TimeFrame + to check the string. */ + } else { + if( !timefrm ) timefrm = astTimeFrame( " ", status ); + if( !astUnformat( timefrm, 0, word, &stop ) && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected ISO date " + "string Stop time, but found '%s' in an STC-S " + "description: '%s'.", status, word, + ContextFragment( &con, &fbuf, status ) ); + } + } + +/* Record the STOP property. */ + astMapPut0C( props, "STOP", prop, NULL ); + astMapPut0D( props, "MJDSTOP", stop, NULL ); + +/* Move on to find the "coord" time. */ + look_for = TIME_LABEL; + + + +/* If we are currently looking for the label before a time coord value... */ + } else if( look_for == TIME_LABEL ) { +/* ------------------------------------------------------------------ */ + if( astChrMatch( word, "Time" ) ) { + look_for = TIME; + } else { + new_word = 0; + look_for = UNIT; + } + + + +/* If we are currently looking for a time... */ + } else if( look_for == TIME ) { +/* ------------------------------------------------------------------ */ + +/* Save the current word as the start of the TIME value. */ + nc = 0; + prop = astAppendString( prop, &nc, word ); + +/* If the current word is "JD" or "MJD", the following word should be + numerical. */ + is_jd = astChrMatch( word, "JD" ); + if( is_jd || astChrMatch( word, "MJD" ) ) { + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + if( value == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected numerical " + "value in Time value, but found '%s %s' in STC-S " + "description: '%s'.", status, prop, word, + ContextFragment( &con, &fbuf, status ) ); + +/* Append the second word to the first word. */ + } else { + prop = astAppendString( prop, &nc, " " ); + prop = astAppendString( prop, &nc, word ); + } + +/* Convert JD to MJD if required. */ + time = is_jd ? value - 2400000.5 : value; + +/* Otherwise, the current word should be an ISO date. Use a TimeFrame + to check the string. */ + } else { + if( !timefrm ) timefrm = astTimeFrame( " ", status ); + if( !astUnformat( timefrm, 0, word, &time ) && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected ISO date " + "string Time value, but found '%s' in an STC-S " + "description: '%s'.", status, word, + ContextFragment( &con, &fbuf, status ) ); + } + } + +/* Record the TIME property. */ + astMapPut0C( props, "TIME", prop, NULL ); + astMapPut0D( props, "MJDTIME", time, NULL ); + +/* Move on to look for the units. */ + look_for = UNIT; + + + +/* If we are currently looking for a space "flavor"... */ + } else if( look_for == FLAVOUR ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is a recognised flavour value, note how many axis + values are required to specify a position. Otherwise, indicate that + the word can be re-used. */ + if( astChrMatch( word, "SPHER2" ) ) { + naxes = 2; + + } else if( astChrMatch( word, "UNITSPHER" ) ) { + naxes = 2; + + } else if( astChrMatch( word, "CART1" ) ) { + naxes = 1; + + } else if( astChrMatch( word, "CART2" ) ) { + naxes = 2; + + } else if( astChrMatch( word, "CART3" ) ) { + naxes = 3; + + } else if( astChrMatch( word, "SPHER3" ) ) { + naxes = 3; + + } else { + naxes = 2; + new_word = 0; + } + +/* If the word was recognised as a flavour, store it in the porperties + KeyMap. */ + if( new_word ) { + astMapPut0C( props, "FLAVOR", word, NULL ); + astMapPut0C( props, "FLAVOUR", word, NULL ); + } + +/* The next set of words to be read from the source function will specify + the arguments of the region enclosing the spatial positions. This may + contain nested regions, so use a recursive function to read the + arguments and store them in the properties KeyMap. */ + if( new_word ) word = GetNextWord( this, &con, status ); + word = ReadSpaceArgs( this, word, spaceid, naxes, &con, props, + status ); + new_word = 0; + +/* Move on to the next look_for (following the region argument list read + by ReadSpaceArgs). */ + if( spaceid == POSITION_ID ) { + look_for = UNIT; + } else { + look_for = POSITION; + } + + + +/* If we are currently looking for interval "lolimit"and "hilimit" ... */ + } else if( look_for == LIMITS ) { +/* ------------------------------------------------------------------ */ + if( velid != NULL_ID ) { + t = "velocity"; + look_for = VELOCITY; + + } else if( specid != NULL_ID ) { + t = "spectral"; + look_for = RED_SPEC_LABEL; + + } else { + t = "redshift"; + look_for = RED_SPEC_LABEL; + } + +/* The current word should be a numerical value (the low limit ). */ + if( astChr2Double( word ) == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a numerical " + "value for a %s lolimit, but found '%s' in an STC-S " + "description: '%s'.", status, t, word, + ContextFragment( &con, &fbuf, status ) ); + } else { + astMapPut0C( props, "LOLIMIT", word, NULL ); + } + +/* The next word should be a numerical value (the high limit ). */ + word = GetNextWord( this, &con, status ); + if( astChr2Double( word ) == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a numerical " + "value for a %s hilimit, but found '%s' in an STC-S " + "description: '%s'.", status, t, word, + ContextFragment( &con, &fbuf, status ) ); + } else { + astMapPut0C( props, "HILIMIT", word, NULL ); + } + + + +/* If we are currently looking for the label before a spectral or redshift + value... */ + } else if( look_for == RED_SPEC_LABEL ) { +/* ------------------------------------------------------------------ */ + if( specid != NULL_ID && astChrMatch( word, "Spectral" ) ) { + look_for = RED_SPEC_VALUE; + + } else if( redid != NULL_ID && astChrMatch( word, "Redshift" ) ) { + look_for = RED_SPEC_VALUE; + + } else { + new_word = 0; + look_for = UNIT; + } + + + +/* If we are currently looking for an spectral or redshift value. */ + } else if( look_for == RED_SPEC_VALUE ) { +/* ------------------------------------------------------------------ */ + + t = ( specid != NULL_ID ) ? "spectral" : "redshift"; + if( astChr2Double( word ) == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a numerical " + "%s value, but found '%s' in an STC-S " + "description: '%s'.", status, t, word, + ContextFragment( &con, &fbuf, status ) ); + } else { + astMapPut0C( props, ( specid != NULL_ID ) ? "SPECTRAL" : "REDSHIFT", + word, NULL ); + } + +/* Decide what to do next. */ + look_for = UNIT; + + + +/* If we are currently looking for information needed to create a spatial + Position ... */ + } else if( look_for == POSITION ) { +/* ------------------------------------------------------------------ */ + +/* Check the current word is "Position". If so, get the next word. */ + if( astChrMatch( word, "Position" ) ) { + word = GetNextWord( this, &con, status ); + +/* Get a value for every space axis. */ + nc = 0; + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + val[ iaxis ] = astChr2Double( word ); + if( val[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "axis value for a space Position, but found " + "'%s' in an STC-S description: '%s'.", status, + word, ContextFragment( &con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, &con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + prop[ nc - 1 ] = 0; + astMapPut0C( props, "POSITION", prop, NULL ); + astMapPut1D( props, "DPOSITION", naxes, val, NULL ); + } + +/* Move on to read the "unit" item. */ + new_word = 0; + look_for = UNIT; + + + +/* If we are currently looking for the redshift type and doppler + definition ... */ + } else if( look_for == TYPE_DOPPLER ) { +/* ------------------------------------------------------------------ */ + + if( astChrMatch( word, "VELOCITY" ) || + astChrMatch( word, "REDSHIFT" ) ) { + astMapPut0C( props, "TYPE", word, NULL ); + word = GetNextWord( this, &con, status ); + } + + if( astChrMatch( word, "OPTICAL" ) || + astChrMatch( word, "RADIO" ) || + astChrMatch( word, "RELATIVISTIC" ) ) { + astMapPut0C( props, "DOPPLERDEF", word, NULL ); + } else { + new_word = 0; + } + +/* Decide what to do next. */ + look_for = ( redid == REDSHIFT_INTERVAL_ID ) ? LIMITS : RED_SPEC_VALUE; + + + +/* If we are currently looking for a velocity label and value... */ + } else if( look_for == VELOCITY ) { +/* ------------------------------------------------------------------ */ + + if( astChrMatch( word, "Velocity" ) ) { + word = GetNextWord( this, &con, status ); + if( astChr2Double( word ) == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a " + "numerical Velocity value but found 'Velocity %s' " + "in an STC-S description: '%s'.", status, word, + ContextFragment( &con, &fbuf, status ) ); + } + + } else { + new_word = 0; + } + + look_for = UNIT; + + + +/* If we are currently looking for a "unit" string... */ + } else if( look_for == UNIT ) { +/* ------------------------------------------------------------------ */ + +/* See if the current word is "unit". If so, read the next word (which + will be the unit string itself). Otherwise, indicate the current word + can be re-used. */ + if( astChrMatch( word, "unit" ) ) { + word = GetNextWord( this, &con, status ); + } else { + new_word = 0; + } + +/* If we have a unit string... */ + if( new_word ) { + +/* Check that the unit string is one of the allowed values (different + values are allowed for different sub-phrases). Space frames can have + multiple units strings (one for each axis) so loop round until a string + is found which is not a valid unit string. */ + nc = 0; + nunit = 0; + while( ( timeid != NULL_ID && ( !strcmp( word, "s" ) || + !strcmp( word, "d" ) || + !strcmp( word, "a" ) || + !strcmp( word, "yr" ) || + !strcmp( word, "cy" ) ) ) || + + ( spaceid != NULL_ID && ( !strcmp( word, "deg" ) || + !strcmp( word, "arcmin" ) || + !strcmp( word, "arcsec" ) || + !strcmp( word, "m" ) || + !strcmp( word, "mm" ) || + !strcmp( word, "m" ) || + !strcmp( word, "km" ) || + !strcmp( word, "AU" ) || + !strcmp( word, "pc" ) || + !strcmp( word, "kpc" ) || + !strcmp( word, "Mpc" ) ) ) || + + ( velid != NULL_ID && ( !strcmp( word, "deg" ) || + !strcmp( word, "arcmin" ) || + !strcmp( word, "arcsec" ) || + !strcmp( word, "m" ) || + !strcmp( word, "mm" ) || + !strcmp( word, "km" ) || + !strcmp( word, "AU" ) || + !strcmp( word, "pc" ) || + !strcmp( word, "kpc" ) || + !strcmp( word, "Mpc" ) ) ) || + + ( !strcmp( word, "Hz" ) || + !strcmp( word, "MHz" ) || + !strcmp( word, "GHz" ) || + !strcmp( word, "m" ) || + !strcmp( word, "mm" ) || + !strcmp( word, "um" ) || + !strcmp( word, "nm" ) || + !strcmp( word, "Angstrom" ) || + !strcmp( word, "eV" ) || + !strcmp( word, "keV" ) || + !strcmp( word, "MeV" ) ) ) { + + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + nunit++; + word = GetNextWord( this, &con, status ); + } + +/* Report an error if an inappropriate number of valid unit strings was + found. */ + if( nunit == 0 && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Unsupported " + "units (%s) for the %s sub-phrase within an " + "STC-S description: '%s'.", status, word, subphrase, + ContextFragment( &con, &fbuf, status ) ); + + } else if( nunit != 1 && nunit != naxes && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Incorrect number of " + "units string (%d) supplied for the %s sub-phrase within an " + "STC-S description: '%s'.", status, nunit, subphrase, + ContextFragment( &con, &fbuf, status ) ); + +/* Otherwise, remove the trailing space, and store the property value in the + KeyMap. */ + } else { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "UNIT", prop, NULL ); + } + +/* The current word is the first word that was not a valid unit string, + and so can be re-used. */ + new_word = 0; + } + +/* Move on to find the errors. */ + look_for = ERROR; + + + +/* If we are currently looking for an "Error" string... */ + } else if( look_for == ERROR ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is "Error" read all subsequent words until the first + non-numerical value is encountered. */ + if( astChrMatch( word, "Error" ) ) { + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + + nc = 0; + nval = 0; + while( value != AST__BAD ) { + if( nval < MAXVAL ) { + val[ nval++ ] = value; + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + } else { + astError( AST__BADIN, "astRead(StcsChan): Too many (more " + "than %d) numerical values found for the Error " + "property of the %s sub-phrase within an STC-S " + "description: '%s'.", status, MAXVAL, subphrase, + ContextFragment( &con, &fbuf, status ) ); + break; + } + } + +/* Report an error if no numerical error values were found. */ + if( nval == 0 && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a " + "numerical error value but found 'Error %s' " + "for the %s sub-phrase within an " + "STC-S description: '%s'.", status, word, subphrase, + ContextFragment( &con, &fbuf, status ) ); + +/* Otherwise, remove the trailing space and store the concatenated + string of formatted values in the properties KeyMap. Also store a + corresponding vector of floating point values in the KeyMap. */ + } else { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "ERROR", prop, NULL ); + astMapPut1D( props, "DERROR", nval, val, NULL ); + } + } + +/* Indicate that we do not need to get a new word (we can re-use the last + one that turned out not to be a numerical value above). */ + new_word = 0; + +/* Next look for Resolution */ + look_for = RESOLUTION; + + + +/* If we are currently looking for a "Resolution" string... */ + } else if( look_for == RESOLUTION ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is "Resolution" read all subsequent words until the + first non-numerical value is encountered. */ + if( astChrMatch( word, "Resolution" ) ) { + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + + nc = 0; + nval = 0; + while( value != AST__BAD ) { + if( nval < MAXVAL ) { + val[ nval++ ] = value; + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + } else { + astError( AST__BADIN, "astRead(StcsChan): Too many (more " + "than %d) numerical values found for the Resolution " + "property of the %s sub-phrase within an STC-S " + "description: '%s'.", status, MAXVAL, subphrase, + ContextFragment( &con, &fbuf, status ) ); + break; + } + } + +/* Report an error if no numerical values were found. */ + if( nval == 0 && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a " + "numerical resolution value but found 'Resolution %s' " + "for the %s sub-phrase within an STC-S description:" + " '%s'.", status, word, subphrase, + ContextFragment( &con, &fbuf, status ) ); + +/* Otherwise, remove the trailing space and store the concatenated + string of formatted values in the properties KeyMap. Also store a + corresponding vector of floating point values in the KeyMap. */ + } else { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "RESOLUTION", prop, NULL ); + astMapPut1D( props, "DRESOLUTION", nval, val, NULL ); + } + } + +/* Indicate that we do not need to get a new word (we can re-use the last + one that turned out not to be a numerical value above). */ + new_word = 0; + +/* Next look for Size. */ + look_for = SIZE; + + + +/* If we are currently looking for a spatial "Size" string... */ + } else if( look_for == SIZE ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is "Size" read all subsequent words until the + first non-numerical value is encountered. */ + if( astChrMatch( word, "Size" ) ) { + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + + nc = 0; + nval = 0; + while( value != AST__BAD ) { + if( nval < MAXVAL ) { + val[ nval++ ] = value; + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + } else { + astError( AST__BADIN, "astRead(StcsChan): Too many (more " + "than %d) numerical values found for the Size " + "property of the %s sub-phrase within an STC-S " + "description: '%s'.", status, MAXVAL, subphrase, + ContextFragment( &con, &fbuf, status ) ); + break; + } + } + +/* Report an error if no numerical values were found. */ + if( nval == 0 && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a " + "numerical size value but found 'Size %s' " + "for the %s sub-phrase within an STC-S description:" + " '%s'.", status, word, subphrase, + ContextFragment( &con, &fbuf, status ) ); + +/* Otherwise, remove the trailing space and store the concatenated + string of formatted values in the properties KeyMap. Also store a + corresponding vector of floating point values in the KeyMap. */ + } else { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "SIZE", prop, NULL ); + astMapPut1D( props, "DSIZE", nval, val, NULL ); + } + } + +/* Indicate that we do not need to get a new word (we can re-use the last + one that turned out not to be a numerical value above). */ + new_word = 0; + +/* Next look for PixSize. */ + look_for = PIX_SIZE; + + + +/* If we are currently looking for a "PixSize" string... */ + } else if( look_for == PIX_SIZE ) { +/* ------------------------------------------------------------------ */ + +/* If the current word is "PixSize" read all subsequent words until the + first non-numerical value is encountered. */ + if( astChrMatch( word, "PixSize" ) ) { + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + + nc = 0; + nval = 0; + while( value != AST__BAD ) { + if( nval < MAXVAL ) { + val[ nval++ ] = value; + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, &con, status ); + value = astChr2Double( word ); + } else { + astError( AST__BADIN, "astRead(StcsChan): Too many (more " + "than %d) numerical values found for the PixSize " + "property of the %s sub-phrase within an STC-S " + "description: '%s'.", status, MAXVAL, subphrase, + ContextFragment( &con, &fbuf, status ) ); + break; + } + } + +/* Report an error if no numerical values were found. */ + if( nval == 0 && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a " + "numerical pixel size but found 'PixSize %s' " + "for the %s sub-phrase within an STC-S description:" + " '%s'.", status, word, subphrase, + ContextFragment( &con, &fbuf, status ) ); + +/* Otherwise, remove the trailing space and store the concatenated + string of formatted values in the properties KeyMap. Also store a + corresponding vector of floating point values in the KeyMap. */ + } else { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "PIXSIZE", prop, NULL ); + astMapPut1D( props, "DPIXSIZE", nval, val, NULL ); + } + } + +/* Indicate that we do not need to get a new word (we can re-use the last + one that turned out not to be a numerical value above). */ + new_word = 0; + +/* Next look for the next sub-phrase. */ + if( timeid != NULL_ID ) { + look_for = SPACE_IDENTIFIER; + + } else if( spaceid != NULL_ID ) { + look_for = VELOCITY_IDENTIFIER; + + } else if( velid != NULL_ID ) { + look_for = SPECTRAL_IDENTIFIER; + + } else if( specid != NULL_ID ) { + look_for = REDSHIFT_IDENTIFIER; + + } else { + break; + } + + + + +/* Report an error for any unknown look_for. */ +/* ------------------------------------------------------------------ */ + } else if( astOK ) { + astError( AST__INTER, "astRead(StcsChan): Illegal look_for value " + "(%d) encountered (internal AST programming error).", + status, look_for ); + } + +/* If required, get the next word in the STC-S description. */ + if( new_word ) word = GetNextWord( this, &con, status ); + } + +/* Free resources stored in the GetNextWord context structure. */ + con.done = 1; + (void) GetNextWord( this, &con, status ); + FreeContext( &con, status ); + +/* Free other resources */ + if( fbuf ) fbuf = astFree( fbuf ); + if( prop ) prop = astFree( prop ); + if( props ) props = astAnnul( props ); + if( timefrm ) timefrm = astAnnul( timefrm ); + +/* If an error occurred, clean up by deleting the new Object and + return a NULL pointer. */ + if ( !astOK ) result = astDelete( result ); + +/* Return the pointer to the properties KeyMap. */ + return result; + +/* Undefine Local Constants: */ +#undef MAXVAL +} + +static const char *ReadSpaceArgs( AstStcsChan *this, const char *word, + int spaceid, int naxes, WordContext *con, + AstKeyMap *props, int *status ){ +/* +* Name: +* ReadSpaceArgs + +* Purpose: +* Read space region arguments from an STC-S description. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* const char *ReadSpaceArgs( AstStcsChan *this, const char *word, +* int spaceid, int naxes, WordContext *con, +* AstKeyMap *props, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function parses the list of space-separated words that form +* the argument list of a spatial region. These words are read from the +* source function, and stored in the supplied KeyMap using keys that +* identify their purpose. +* +* This function calls itself recursively to handle compound regions. + +* Parameters: +* this +* Pointer to the StcsChan. +* word +* The first word of the argument list. +* spaceid +* An integer identifier for the type of spatial region for which +* arguments are being read. +* naxes +* Number of axes in the space frame. +* con +* Pointer to a structure holding context for use with the +* GetNextWord function. On exit, the next word returned by the +* GetNextWord function will be the first word following the +* argument list. +* props +* Pointer to the KeyMap in which the argument values should be +* stored. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the next wpord to be interpreted. + +*/ + + +/* Local Variables: */ + AstKeyMap *new_props; /* KeyMap holding properties of an argument region */ + char *fbuf; /* Pointer to buffer holding document fragment */ + char *prop; /* String property value */ + char key[ 20 ]; /* Key for argument region */ + double *p; /* Pointer to next polygon vertex axis value */ + double *temp; /* Array of polygon vertex axis values */ + double val; /* Single numerical value */ + double vals[ 6 ]; /* List of numerical values */ + int iaxis; /* Axis index */ + int nc; /* Used length of string */ + int new_spaceid; /* Type of next argument region */ + int nreg; /* Number of argument regions found */ + int nvert; /* Number of vertices in polygon */ + +/* Check inherited status */ + if( !astOK ) return word; + +/* Initialise. */ + fbuf = NULL; + prop = NULL; + nc = 0; + +/* If we are looking for information needed to create a spatial + Interval... */ + if( spaceid == POSITION_INTERVAL_ID ) { + +/* Get a lolimit value for every space axis. */ + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = astChr2Double( word ); + if( vals[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "'lolimit' value for a PositionInterval, but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "LOLIMIT", prop, NULL ); + astMapPut1D( props, "DLOLIMIT", naxes, vals, NULL ); + } + +/* Get a hilimit value for every space axis. */ + nc = 0; + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = astChr2Double( word ); + if( vals[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "'hilimit' value for a PositionInterval, but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "HILIMIT", prop, NULL ); + astMapPut1D( props, "DLOLIMIT", naxes, vals, NULL ); + } + +/* If we are currently looking for information needed to create a spatial + AllSky ... */ + } else if( spaceid == ALLSKY_ID ) { + + + +/* If we are currently looking for information needed to create a spatial + Circle ... */ + } else if( spaceid == CIRCLE_ID ) { + +/* Get a centre value for every space axis. */ + nc = 0; + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = astChr2Double( word ); + if( vals[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "'centre' value for a Circle, but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "CENTRE", prop, NULL ); + astMapPut1D( props, "DCENTRE", naxes, vals, NULL ); + } + +/* Get the radius value. */ + val = astChr2Double( word ); + if( val == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a radius " + "value for a Circle, but found '%s' in an STC-S " + "description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + +/* Store the property value in the KeyMap. */ + astMapPut0C( props, "RADIUS", word, NULL ); + +/* Get the next word. */ + word = GetNextWord( this, con, status ); + + + +/* If we are currently looking for information needed to create a spatial + Ellipse ... */ + } else if( spaceid == ELLIPSE_ID ) { + +/* Get a centre value for every space axis. */ + nc = 0; + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = astChr2Double( word ); + if( vals[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "centre value for an Ellipse, but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "CENTRE", prop, NULL ); + astMapPut1D( props, "DCENTRE", naxes, vals, NULL ); + } + +/* Get the first radius value . */ + val = astChr2Double( word ); + if( val == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected the first " + "radius value for an Ellipse, but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + +/* Store the property value in the KeyMap. */ + astMapPut0C( props, "RADIUS1", word, NULL ); + +/* Get the second radius value . */ + word = GetNextWord( this, con, status ); + val = astChr2Double( word ); + if( val == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected the second " + "radius value for an Ellipse, but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + +/* Store the property value in the KeyMap. */ + astMapPut0C( props, "RADIUS2", word, NULL ); + +/* Get the position angle value. */ + word = GetNextWord( this, con, status ); + val = astChr2Double( word ); + if( val == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected the position " + "angle value for an Ellipse, but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + +/* Store the property value in the KeyMap. */ + astMapPut0C( props, "POSANGLE", word, NULL ); + +/* Get the next word. */ + word = GetNextWord( this, con, status ); + + + +/* If we are currently looking for information needed to create a spatial + Box ... */ + } else if( spaceid == BOX_ID ) { + +/* Get a centre value for every space axis. */ + nc = 0; + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = astChr2Double( word ); + if( vals[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "centre value for a Box, but found " + "'%s' in an STC-S description: '%s'.", status, + word, ContextFragment( con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "CENTRE", prop, NULL ); + astMapPut1D( props, "DCENTRE", naxes, vals, NULL ); + } + +/* Get bsize value for every space axis. */ + nc = 0; + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = astChr2Double( word ); + if( vals[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "'bsize' value for a Box, but found " + "'%s' in an STC-S description: '%s'.", status, + word, ContextFragment( con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "BSIZE", prop, NULL ); + astMapPut1D( props, "DBSIZE", naxes, vals, NULL ); + } + + +/* If we are currently looking for information needed to create a spatial + Polygon ... */ + } else if( spaceid == POLYGON_ID ) { + +/* Read the first vertex into a dynamically allocated array. */ + temp = astMalloc( sizeof( *temp )*naxes ); + if( temp ) { + nc = 0; + p = temp; + for( iaxis = 0; iaxis < naxes; iaxis++,p++ ) { + val = astChr2Double( word ); + if( val == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "vertex value for a Polygon, but found " + "'%s' in an STC-S description: '%s'.", status, + word, ContextFragment( con, &fbuf, status ) ); + } else { + *p = val; + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Loop round reading remaining vertices, expanding the array as needed. */ + nvert = 1; + val = astChr2Double( word ); + while( val != AST__BAD && astOK ) { + + temp = astGrow( temp, naxes*( nvert + 1 ), sizeof( *temp ) ); + if( astOK ) { + p = temp + naxes*nvert; + + for( iaxis = 0; iaxis < naxes; iaxis++, p++ ) { + if( val == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected " + "another vertex value for a Polygon, but " + "found '%s' in an STC-S description: '%s'.", + status, word, ContextFragment( con, &fbuf, + status ) ); + } else { + *p = val; + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + val = astChr2Double( word ); + } + nvert++; + } + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "VERTICES", prop, NULL ); + astMapPut1D( props, "DVERTICES", naxes*nvert, temp, NULL ); + } + temp = astFree( temp ); + } + + + +/* If we are currently looking for information needed to create a spatial + Convex ... */ + } else if( spaceid == CONVEX_ID ) { + astError( AST__BADIN, "astRead(StcsChan): A Convex was found " + "within an STC-S description ('Convex' regions " + "are not yet supported by AST): %s", status, + ContextFragment( con, &fbuf, status ) ); + + + +/* If we are currently looking for information needed to create a spatial + Position ... */ + } else if( spaceid == POSITION_ID ) { + +/* Get a value for every space axis. */ + nc = 0; + for( iaxis = 0; iaxis < naxes; iaxis++ ) { + vals[ iaxis ] = astChr2Double( word ); + if( vals[ iaxis ] == AST__BAD && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected another " + "axis value for a space Position, but found " + "'%s' in an STC-S description: '%s'.", status, + word, ContextFragment( con, &fbuf, status ) ); + } + prop = astAppendString( prop, &nc, word ); + prop = astAppendString( prop, &nc, " " ); + word = GetNextWord( this, con, status ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( props, "POSITION", prop, NULL ); + astMapPut1D( props, "DPOSITION", naxes, vals, NULL ); + } + + +/* All remaining space id values require the argument list to be enclosed + in parentheses. Report an error if the current word does not start + with an opening parenthesis. */ + } else if( *word != '(' && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected an opening " + "parenthesis but found '%s' in an STC-S description: '%s'.", + status, word, ContextFragment( con, &fbuf, status ) ); + +/* Skip over the opening parenthesis. If the first word consists of just the + opening parenthesis, get the next word. */ + } else { + if( *(++word) == 0 ) word = GetNextWord( this, con, status ); + +/* Loop round all regions included in the compound region. */ + nreg = 0; + while( astOK ) { + +/* If the next word starts with a closing parenthesis, we have reached + the end of the argument list. */ + if( *word == ')' ) { + +/* Skip over the closing parenthesis. If the word consists of just the + closing parenthesis, get the next word. */ + if( *(++word) == 0 ) word = GetNextWord( this, con, status ); + +/* Leave the loop. */ + break; + } + +/* Identify the region type from the current word. */ + new_spaceid = SpaceId( word, status ); + if( new_spaceid == NULL_ID && astOK ) { + astError( AST__BADIN, "astRead(StcsChan): Expected a " + "CoordinateArea or a closing parenthesis but found " + "'%s' in an STC-S description: '%s'.", status, word, + ContextFragment( con, &fbuf, status ) ); + } + +/* Create a new KeyMap to store the properties of the new region. Store + this new KeyMap in the supplied KeyMap using a key of the form + "REGION". */ + new_props = astKeyMap( " ", status ); + astMapPut0C( new_props, "ID", word, NULL ); + sprintf( key, "REGION%d", ++nreg ); + astMapPut0A( props, key, new_props, NULL ); + +/* Get the next word (i.e. the first word of the argument list for the + region). */ + word = GetNextWord( this, con, status ); + +/* Call this function recursively to read the argument list. */ + word = ReadSpaceArgs( this, word, new_spaceid, naxes, con, + new_props, status ); + +/* Free resources. */ + new_props = astAnnul( new_props ); + } + +/* Store the number of regions in the supplied KeyMap. */ + astMapPut0I( props, "NREG", nreg, NULL ); + +/* Report an error if an in appropriate number of argument Regions were + supplied. */ + if( spaceid == UNION_ID ) { + if( nreg < 2 && astOK ){ + astError( AST__BADIN, "astRead(StcsChan): Less than two " + "CoordinateAreas found within a 'Union' element in an " + "STC-S description: '%s'.", status, + ContextFragment( con, &fbuf, status ) ); + } + + } else if( spaceid == INTERSECTION_ID ) { + if( nreg < 2 && astOK ){ + astError( AST__BADIN, "astRead(StcsChan): Less than two " + "CoordinateAreas found within an 'Intersection' element " + "in an STC-S description: '%s'.", status, + ContextFragment( con, &fbuf, status ) ); + } + + } else if( spaceid == DIFFERENCE_ID ) { + if( nreg != 2 && astOK ){ + astError( AST__BADIN, "astRead(StcsChan): %d CoordinateArea(s) " + "found within a 'Difference' element in an STC-S " + "description: '%s'.", status, nreg, + ContextFragment( con, &fbuf, status ) ); + } + + + } else if( spaceid == NOT_ID ) { + if( nreg != 1 && astOK ){ + astError( AST__BADIN, "astRead(StcsChan): %d CoordinateAreas " + "found within a 'Not' element in an STC-S description: " + "'%s'.", status, nreg, + ContextFragment( con, &fbuf, status ) ); + } + +/* Report an error for unknown spaceid values */ + } else if( astOK ) { + astError( AST__INTER, "astRead(StcsChan): Illegal 'spaceid' value " + "passed to function ReadSpaceArgs (internal AST " + "programming error).", status ); + } + } + +/* Free resources */ + if( prop ) prop = astFree( prop ); + +/* Return a pointer to the next word to be interpreted. */ + return word; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a StcsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* StcsChan member function (over-rides the astSetAttrib protected +* method inherited from the Channel class). + +* Description: +* This function assigns an attribute value for a StcsChan, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the StcsChan. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstStcsChan *this; /* Pointer to the StcsChan structure */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by "astSscanf" */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the StcsChan structure. */ + this = (AstStcsChan *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* StcsArea. */ +/* --------- */ + if ( nc = 0, + ( 1 == astSscanf( setting, "stcsarea= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetStcsArea( this, ival ); + +/* StcsCoords. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "stcscoords= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetStcsCoords( this, ival ); + +/* StcsProps. */ +/* ----------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "stcsprops= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetStcsProps( this, ival ); + +/* StcsLength */ +/* ----------*/ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "stcslength= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetStcsLength( this, ival ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetUnc( AstRegion *reg1, AstRegion *reg2, AstFrame *frm, + int is_skyframe, double scale, double *error, int nax, + int *status ){ +/* +* Name: +* SetUnc + +* Purpose: +* Store an uncertainty Box with a supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void SetUnc( AstRegion *reg1, AstRegion *reg2, AstFrame *frm, +* int is_skyframe, double scale, double *error, int nax, +* int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function creates a new Box with dimensions specified by the +* values in the "error" array, centred on a representative position +* within one of the supplied Regions, and then stores the Box as the +* uncertainty Region within both the supplied Regions. + +* Parameters: +* reg1 +* Pointer to a Region to which the error values relate. +* reg2 +* Pointer to another Region to which the error values relate. +* frm +* Pointer to the Frame encapsulated by both Regions. +* is_skyframe +* Should be non-zero if "frm" is a SkyFrame. +* scale +* A scale factor to apply to the error values before using them. +* error +* Pointer to an array of RMS error values, one for each axis in +* "frm". These are modified on exit. For a SkyFrame, both values +* (including the longitude axis value) should be given as an +* arc-distance. This function will convert the arc-distance to +* a longitude increment using a representative latitude for the +* region. +* nax +* The numner of axes in "frm". +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstBox *unc; /* Uncertainty box */ + double dist; /* Diagonal length of Region bounding box */ + double lbnd[ 6 ]; /* Lower bounds of Region bounding box */ + double spos1[ 6 ]; /* A representative position in the Region */ + double spos2[ 6 ]; /* A second position in the Region */ + double ubnd[ 6 ]; /* Upper bounds of Region bounding box */ + int i; /* Axis index */ + +/* Check the global error status. Also check an error value was supplied, + and at least one of the Region pointers is not NULL. */ + if ( !astOK || error[ 0 ] == AST__BAD || ( !reg1 && !reg2 ) ) return; + +/* We need a representative position within the region. First get the + coordinates at opposite corners of the region bounding box. */ + astRegBaseBox( reg1 ? reg1 : reg2, lbnd, ubnd ); + +/* Find the diagonal length of the bounding box. */ + dist = astDistance( frm, lbnd, ubnd ); + +/* Offset away from one corner towards the other by half the diagonal + length. The resulting position returned in spos1 is our representative + position for the region. */ + astOffset( frm, lbnd, ubnd, dist/2, spos1 ); + +/* Scale the error values */ + for( i = 0; i < nax; i++ ) error[ i ] *= scale; + +/* If the region is defined within a SkyFrame, the supplied longitude + error value will be an arc-distance value. But we need a longitude + increment to create an uncertainty Region, so do the conversion. */ + if( is_skyframe ) { + +/* Offset away from the representative position found above along the + first (i.e. longitude) axis by an arc-distance given by the Error + value. */ + (void) astOffset2( frm, spos1, AST__DPIBY2, error[ 0 ], spos2 ); + +/* Find the positive axis increment along the first axis. */ + error[ 0 ] = astAxDistance( frm, 1, spos1[ 0 ], spos2[ 0 ] ); + if( error[ 0 ] != AST__BAD ) error[ 0 ] = fabs( error[ 0 ] ); + } + +/* The uncertainty Region will be a Box centred at the representative + position found above. Modify the "error" array to hold the corner + axis values. */ + for( i = 0; i < nax; i++ ) error[ i ] += spos1[ i ]; + +/* Create the box, and store it as the uncertainty Region in the supplied + Region. */ + unc = astBox( frm, 0, spos1, error, NULL, " ", status ); + if( reg1 ) astSetUnc( reg1, unc ); + if( reg2 ) astSetUnc( reg2, unc ); + +/* Free resources. */ + unc = astAnnul( unc ); +} + +static AstPointList *SinglePointList( AstFrame *frm, double *pos, + AstRegion *unc, int *status){ +/* +* Name: +* SinglePointList + +* Purpose: +* Create a PointList holding a single point. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* AstPointList *SinglePointList( AstFrame *frm, double *pos, +* AstRegion *unc, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function creates a new PointList holding a single supplied +* position. + +* Parameters: +* frm +* Pointer to the Frame in which the PointList is defined. +* pos +* Array holding the position. The length of this array must equal +* the number of axes in "frm". +* unc +* Pointer to an uncertainty Region to associate with the new +* PointList, or NULL. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new PointList. NULL is returned if an error has +* already occurred, of if this function fails for any reason. +*/ + +/* Local Variables: */ + AstPointList *result; /* Returned pointer. */ + AstPointSet *pset; /* PointSet holding axis values */ + double **ptr; /* Pointer to PointSet data arrays */ + int i; /* Axis index */ + int nax; /* Number of axes */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get he number of axes. */ + nax = astGetNaxes( frm ); + +/* Create a PointSet to hold the supplied point, and get a pointer to its + data arrays. */ + pset = astPointSet( 1, nax, "", status ); + ptr = astGetPoints( pset ); + if( astOK ) { + +/* Copy the supplied axis values into the PointSet data arrays. */ + for( i = 0; i < nax; i++ ) ptr[ i ][ 0 ] = pos[ i ]; + +/* Create the PointList. */ + result = astPointList( frm, pset, unc, "", status ); + } + +/* Free resources */ + pset = astAnnul( pset ); + +/* Return the result. */ + return result; +} + +static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) { +/* +* Name: +* SinkWrap + +* Purpose: +* Wrapper function to invoke a C StcsChan sink function. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) + +* Class Membership: +* StcsChan member function. + +* Description: +* This function invokes the sink function whose pointer is +* supplied in order to write an output line to an external data +* store. + +* Parameters: +* sink +* Pointer to a sink function, whose single parameter is a +* pointer to a const, null-terminated string containing the +* text to be written, and which returns void. This is the form +* of StcsChan sink function employed by the C language interface +* to the AST library. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the sink function. */ + ( *sink )( line ); +} + +static char *SourceWrap( const char *(* source)( void ), int *status ) { +/* +* Name: +* SourceWrap + +* Purpose: +* Wrapper function to invoke a C StcsChan source function. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* char *SourceWrap( const char *(* source)( void ), int *status ) + +* Class Membership: +* StcsChan member function. + +* Description: +* This function invokes the source function whose pointer is +* supplied in order to read the next input line from an external +* data store. It then returns a pointer to a dynamic string +* containing a copy of the text that was read. + +* Parameters: +* source +* Pointer to a source function, with no parameters, that +* returns a pointer to a const, null-terminated string +* containing the text that it read. This is the form of StcsChan +* source function employed by the C language interface to the +* AST library. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated, null terminated string +* containing a copy of the text that was read. This string must be +* freed by the caller (using astFree) when no longer required. +* +* A NULL pointer will be returned if there is no more input text +* to read. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + char *result; /* Pointer value to return */ + const char *line; /* Pointer to input line */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the source function to read the next input line and return a + pointer to the resulting string. */ + line = ( *source )(); + +/* If a string was obtained, make a dynamic copy of it and save the + resulting pointer. */ + if ( line ) result = astString( line, (int) strlen( line ) ); + +/* Return the result. */ + return result; +} + +static int SpaceId( const char *word, int *status ){ +/* +* Name: +* SpaceId + +* Purpose: +* Return the integer identifier for a given textual space identifier. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* int SpaceId( const char *word, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function returns an integer identifier for the given space +* identifier. + +* Parameters: +* word +* The word holding the textual space identifier. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The integer space identifier, or NULL_ID if the supplied word was +* not a known space identifier. + +*/ + + +/* Local Variables: */ + int spaceid; /* Returned identifier */ + +/* Check inherited status */ + if( !astOK ) return NULL_ID; + + if( astChrMatch( word, "PositionInterval" ) ) { + spaceid = POSITION_INTERVAL_ID; + + } else if( astChrMatch( word, "AllSky" ) ) { + spaceid = ALLSKY_ID; + + } else if( astChrMatch( word, "Circle" ) ) { + spaceid = CIRCLE_ID; + + } else if( astChrMatch( word, "Ellipse" ) ) { + spaceid = ELLIPSE_ID; + + } else if( astChrMatch( word, "Box" ) ) { + spaceid = BOX_ID; + + } else if( astChrMatch( word, "Polygon" ) ) { + spaceid = POLYGON_ID; + + } else if( astChrMatch( word, "Convex" ) ) { + spaceid = CONVEX_ID; + + } else if( astChrMatch( word, "Union" ) ) { + spaceid = UNION_ID; + + } else if( astChrMatch( word, "Intersection" ) ) { + spaceid = INTERSECTION_ID; + + } else if( astChrMatch( word, "Difference" ) ) { + spaceid = DIFFERENCE_ID; + + } else if( astChrMatch( word, "Not" ) ) { + spaceid = NOT_ID; + + } else if( astChrMatch( word, "Position" ) ) { + spaceid = POSITION_ID; + + } else { + spaceid = NULL_ID; + } + +/* Return the integer space identifier. */ + return spaceid; +} + +static void StoreTimeProp( AstKeyMap *props, AstTimeFrame *frm, + const char *key, double value, int *status ){ +/* +* Name: +* StoreTimeProp + +* Purpose: +* Store a time value as an STC-S property, using the existing format. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void StoreTimeProp( AstKeyMap *props, AstTimeFrame *frm, +* const char *key, double value, int *status ) + +* Class Membership: +* StcsChan member function. + +* Description: +* This function formats the supplied time value and stores it in +* the "props" KeyMap, using the supplied key name. If the KeyMap +* already contains an entry for the given key, the new value is +* written using the same format. Otherwise, the new value is written +* as an ISO date and time string. + +* Parameters: +* props +* Pointer to the KeyMap in which to store the time value. +* frm +* Pointer to a TimeFrame that can be used to format the time value. +* key +* Pointer to a string holding the property name associated with +* the time value. +* value +* The time value, in the system described by "frm". +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstFrame *fmtfrm; /* Frame defining Format/System for formatted value */ + AstFrame *fs; /* FrameSet connecting Frames */ + const char *fmttxt; /* Formatted text */ + const char *oldval; /* Pointer to old formatted time value */ + const char *p; /* Pointer to next character in formatted value */ + double fmtval; /* The time value in the formatting system */ + int ndp; /* Number of decimal places in formatted value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* We want a TimeFrame (fmtfrm) that describes how to format the time + value. If the Format attribute of the supplied TimeFrame has been + set, use it (and the current System). So just take a clone of the + supplied frame pointer. */ + if( astTestFormat( frm, 0 ) ) { + fmtfrm = astClone( frm ); + +/* If the Format attribute has not been set, we create a copy of the + supplied TimeFrame, and set its System and Format attributes to + produce the required format. */ + } else { + fmtfrm = astCopy( frm ); + +/* If the KeyMap contains an entry for the specified key, determine the + format of the time string it contains. */ + if( astMapGet0C( props, key, &oldval ) && oldval ) { + +/* See how many digits there are after the decimal place */ + p = strchr( oldval, '.' ); + ndp = 0; + if( p ) { + while( *(++p) ) { + if( isdigit( *p ) ) { + ndp++; + } else { + break; + } + } + } + +/* If the string starts with "JD", the time is formatted as a numerical + Julian date. */ + if( !strncmp( oldval, "JD", 2 ) ) { + astSetSystem( fmtfrm, AST__JD ); + if( ndp > 0 ) { + astSet( fmtfrm, "Format=JD %%.%df", status, ndp ); + } else { + astSetFormat( fmtfrm, 0, "JD %d" ); + } + +/* If the string starts with "MJD", the time is formatted as a numerical + Modified Julian date. */ + } else if( !strncmp( oldval, "MJD", 3 ) ) { + astSetSystem( fmtfrm, AST__MJD ); + if( ndp > 0 ) { + astSet( fmtfrm, "Format=MJD %%.%df", status, ndp ); + } else { + astSetFormat( fmtfrm, 0, "MJD %d" ); + } + +/* Otherwise, the current word should be an ISO date. See how many + decimal paces in the seconds field there are (if any). */ + } else { + astSet( fmtfrm, "Format=iso.%dT", status, ndp ); + } + +/* If the KeyMap does not contain an entry for the specified key, an + ISO date/time string with 1 decimal place in the seconds field + is used. */ + } else { + astSetFormat( fmtfrm, 0, "iso.1T" ); + } + } + +/* Ensure the displayed value is an abolute value. */ + astClearTimeOrigin( fmtfrm ); + +/* Convert the supplied time value into the required system. */ + fs = astConvert( frm, fmtfrm, "" ); + astTran1( fs, 1, &value, 1, &fmtval ); + +/* Format the value. */ + fmttxt = astFormat( fmtfrm, 0, fmtval ); + +/* Store it in the KeyMap. */ + astMapPut0C( props, key, fmttxt, NULL ); + +/* Free resources. */ + fs = astAnnul( fs ); + fmtfrm = astAnnul( fmtfrm ); +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a StcsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* StcsChan member function (over-rides the astTestAttrib protected +* method inherited from the Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a StcsChan's attributes. + +* Parameters: +* this +* Pointer to the StcsChan. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstStcsChan *this; /* Pointer to the StcsChan structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the StcsChan structure. */ + this = (AstStcsChan *) this_object; + +/* Check the attribute name and test the appropriate attribute. */ + + if ( !strcmp( attrib, "stcsarea" ) ) { + result = astTestStcsArea( this ); + + } else if ( !strcmp( attrib, "stcscoords" ) ) { + result = astTestStcsCoords( this ); + + } else if ( !strcmp( attrib, "stcsprops" ) ) { + result = astTestStcsProps( this ); + + } else if ( !strcmp( attrib, "stcslength" ) ) { + result = astTestStcsLength( this ); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int Write( AstChannel *this_channel, AstObject *object, int *status ) { +/* +* Name: +* Write + +* Purpose: +* Write an Object to a StcsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* int Write( AstChannel *this, AstObject *object, int *status ) + +* Class Membership: +* StcsChan member function (over-rides the astWrite method +* inherited from the Channel class). + +* Description: +* This function writes an Object to a StcsChan. + +* Parameters: +* this +* Pointer to the StcsChan. +* object +* Pointer to the Object which is to be written. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of Objects written to the StcsChan by this invocation of +* astWrite. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the AST error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstFrame *frm; /* AREA Frame */ + AstFrameSet *fs; /* FrameSet connecting AREA and COORDS */ + AstKeyMap *props; /* A KeyMap holding the STC-S properties list */ + AstMapping *map; /* Mapping connecting AREA and COORDS */ + AstObject *obj; /* A temporary Object pointer */ + AstRegion *area; /* The Region representing the STC CoordArea */ + AstRegion *coords; /* The Region representing the STC Coords */ + AstRegion *new_coords; /* COORDS Region mapped into frame of AREA */ + AstStcsChan *this; /* Pointer to the StcsChan structure */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + const char *class; /* Pointer to string holding object class */ + const char *errclass; /* Type of the failed entry */ + const char *errname; /* Name of the failed entry */ + const char *method; /* Pointer to string holding calling method */ + const char *wantclass; /* The expected type */ + int ret; /* Number of objects read */ + +/* Initialise. */ + ret = 0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_channel); + +/* Obtain a pointer to the StcsChan structure. */ + this = (AstStcsChan *) this_channel; + +/* Store the calling method, and object class. */ + method = "astWrite"; + class = astGetClass( this ); + +/* Initialise */ + area = NULL; + coords = NULL; + props = NULL; + +/* If the supplied Object is a Region, we will use it to define the AREA + properties. */ + if( astIsARegion( object ) ) { + area = (AstRegion *) astClone( object ); + +/* If the supplied Object is a KeyMap... */ + } else if( astIsAKeyMap( object ) ) { + errname = NULL; + wantclass = NULL; + errclass = NULL; + +/* If the supplied KeyMap contains an entry with key "AREA", and if it is + a Region, use it to define the AREA properties. */ + if( astMapGet0A( (AstKeyMap *) object, "AREA", &obj ) ) { + if( astIsARegion( obj ) ) { + area = (AstRegion *) obj; + } else { + wantclass = "Region"; + errclass = astGetClass( obj ); + errname = "AREA"; + obj = astAnnul( obj ); + } + } + +/* If the supplied KeyMap contains an entry with key "COORDS", and if it is + a Region, use it to define the COORDS properties. */ + if( astMapGet0A( (AstKeyMap *) object, "COORDS", &obj ) ) { + if( astIsARegion( obj ) ) { + coords = (AstRegion *) obj; + } else { + wantclass = "Region"; + errclass = astGetClass( obj ); + errname = "COORDS"; + obj = astAnnul( obj ); + } + } + +/* If the supplied KeyMap contains an entry with key "PROPS", and if it is + a KeyMap, use it to define values for the properties that cannot be + determined from the supplied Regions (Resolution, PixSize, etc). */ + if( astMapGet0A( (AstKeyMap *) object, "PROPS", &obj ) ) { + if( astIsAKeyMap( obj ) ) { + props = (AstKeyMap *) obj; + } else { + wantclass = "KeyMap"; + errclass = astGetClass( obj ); + errname = "PROPS"; + obj = astAnnul( obj ); + } + } + +/* If the supplied KeyMap contains an entry with any of the keys + "TIME_PROPS", "SPACE_PROPS", "SPECTRAL_PROPS" or "REDSHIFT_PROPS", + use the supplied KeyMap to define values for all properties. */ + if( astMapGet0A( (AstKeyMap *) object, "TIME_PROPS", &obj ) || + astMapGet0A( (AstKeyMap *) object, "SPACE_PROPS", &obj ) || + astMapGet0A( (AstKeyMap *) object, "SPECTRAL_PROPS", &obj ) || + astMapGet0A( (AstKeyMap *) object, "REDSHIFT_PROPS", &obj ) ) { + props = astClone( object ); + } + +/* Report an error if the Object in the keymap has the wrong type. */ + if( errname && astOK ) { + astAddWarning( this, 1, "The supplied KeyMap contains a %s " + "called '%s'. But '%s' should be a %s " + "(programming error).", method, status, + errclass, errname, errname, wantclass ); + } + +/* Report an error if the keymap contains none of the above. */ + if( !area && !coords && !props && astOK ) { + astAddWarning( this, 1, "The supplied KeyMap does not " + "contains anything that can be written out " + "through a %s.", method, status, class ); + } + +/* If both COORDS and AREA were supplied, ensure they are in the same + Frame by mapping the COORDS Region into the Frame of the AREA Region. */ + if( area && coords ) { + fs = astConvert( coords, area, " " ); + if( fs ) { + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + frm = astGetFrame( fs, AST__CURRENT ); + + new_coords = astMapRegion( coords, map, frm ); + + map = astAnnul( map ); + frm = astAnnul( frm ); + coords = astAnnul( coords ); + fs = astAnnul( fs ); + + coords = new_coords; + + } else if( astOK ){ + astAddWarning( this, 1, "Cannot convert between the co-ordinate " + "frame of the COORDS Region and the co-ordinate " + "frame of the AREA Region.", method, status ); + } + } + +/* Report an error if the supplied object is neither a KeyMap nor a + Region. */ + } else if( astOK ) { + astAddWarning( this, 1, "Failed to write out a %s through a %s. " + "The %s class cannot be used to write out a %s.", + method, status, astGetClass( object ), class, class, + astGetClass( object ) ); + } + + +/* If we do not have a KeyMap in which to store the STC-S properties, + create one now. */ + if( astOK ) { + if( ! props ) props = astKeyMap ( " ", status ); + +/* Determine the set of STC-S properties that describe the COORDS Region, + and add them into the properties keymap, over-writing any values for the + same properties that are already in the props keymap. */ + ret = coords ? WriteRegion( this, coords, props, status ) : 1; + +/* Determine the set of STC-S properties that describe the AREA Region, + and add them into the properties keymap, over-writing any values for the + same properties that are already in the props keymap. NB, we need to + do AREA after COORDS so that the sub-phrase identifier implied by the + AREA is used in preference to that implied by the COORDS. */ + if( area && ret ) ret = WriteRegion( this, area, props, status ); + +/* Convert the properties list into text and write it out through the + parent Channel's sink function. */ + if( ret ) WriteProps( this, props, status ); + } + +/* Free resources. */ + if( area ) area = astAnnul( area ); + if( coords ) coords = astAnnul( coords ); + if( props ) props = astAnnul( props ); + +/* If an error has occurred, return zero. */ + if( !astOK ) ret = 0; + +/* Return the answer. */ + return ret; +} + +static void WriteProps( AstStcsChan *this, AstKeyMap *props, int *status ){ +/* +* Name: +* WriteProps + +* Purpose: +* Write out a set of STC-S properties to the sink function. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* void WriteProps( AstStcsChan *this, AstKeyMap *props, int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function converts the STC-S properties supplied in a KeyMap +* into text, and writes the text out through the sink function associated +* with the parent Channel. + +* Parameters: +* this +* Pointer to the StcsChan. +* props +* Pointer to the KeyMap holding the STC-S properties. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstKeyMap *spprops; /* Sub-phrase properties */ + AstObject *obj; /* Generic Object pointer */ + char *line; /* Dynamically allocated buffer for output text */ + const char *id; /* Sub-phrase identifier */ + const char *prefix; /* Prefix for property value */ + int nc; /* Number of characters in "line" */ + int pretty; /* Include new-lines and indentation in returned text? */ + int crem; /* Character remaining on current output line */ + int linelen; /* Line length */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise things. */ + nc = 0; + line = NULL; + +/* See if indentation and new-lines are to be added to the output text to + make it look pretty. */ + pretty = astGetIndent( this ); + +/* If so, get the line length to use, and initialise the number of + remaining characters in the current output line. */ + if( pretty ) { + linelen = astGetStcsLength( this ); + } else { + linelen = 0; + } + crem = linelen; + +/* Add each word in the time sub-phrase into the output buffer, in the + order defined by the STC-S standard. */ + if( astMapGet0A( props, "TIME_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + + line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status ); + astMapGet0C( spprops, "ID", &id ); + + line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "TIMESCALE", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "START", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "STOP", NULL, line, &nc, &crem, linelen, status ); + + prefix = !astChrMatch( id, "Time" ) ? "Time " : NULL; + line = AddItem( this, spprops, "TIME", prefix, line, &nc, &crem, linelen, status ); + + line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status ); + + spprops = astAnnul( spprops ); + +/* Write out the time sub-phrase text through the Channel sink function. */ + if( pretty && astChrLen( line ) ) { + astPutNextText( this, line ); + nc = 0; + crem = linelen; + } + } + +/* Add each word in the space sub-phrase into the output buffer, in the + order defined by the STC-S standard. */ + if( astMapGet0A( props, "SPACE_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + + line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status ); + astMapGet0C( spprops, "ID", &id ); + + line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "FRAME", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "FLAVOUR", NULL, line, &nc, &crem, linelen, status ); + + line = PutRegionProps( this, spprops, id, (pretty ? 0 : -1), line, &nc, + &crem, linelen, status ); + + prefix = !astChrMatch( id, "Position" ) ? "Position " : NULL; + line = AddItem( this, spprops, "POSITION", prefix, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "SIZE", "Size ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status ); + + spprops = astAnnul( spprops ); + +/* Write out the spatial sub-phrase text through the Channel sink function. */ + if( pretty && astChrLen( line ) ) { + astPutNextText( this, line ); + nc = 0; + crem = linelen; + } + } + +/* Add each word in the spectral sub-phrase into the output buffer, in the + order defined by the STC-S standard. */ + if( astMapGet0A( props, "SPECTRAL_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + + line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status ); + astMapGet0C( spprops, "ID", &id ); + + line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "LOLIMIT", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "HILIMIT", NULL, line, &nc, &crem, linelen, status ); + + prefix = !astChrMatch( id, "Spectral" ) ? "Spectral " : NULL; + line = AddItem( this, spprops, "SPECTRAL", prefix, line, &nc, &crem, linelen, status ); + + line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status ); + + spprops = astAnnul( spprops ); + +/* Write out the spectral sub-phrase text through the Channel sink function. */ + if( pretty && astChrLen( line ) ) { + astPutNextText( this, line ); + nc = 0; + crem = linelen; + } + } + +/* Add each word in the redshift sub-phrase into the output buffer, in the + order defined by the STC-S standard. */ + if( astMapGet0A( props, "REDSHIFT_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + + line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status ); + astMapGet0C( spprops, "ID", &id ); + + line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "TYPE", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "DOPPLERDEF", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "LOLIMIT", NULL, line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "HILIMIT", NULL, line, &nc, &crem, linelen, status ); + + prefix = !astChrMatch( id, "Redshift" ) ? "Redshift " : NULL; + line = AddItem( this, spprops, "REDSHIFT", prefix, line, &nc, &crem, linelen, status ); + + line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status ); + line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status ); + + spprops = astAnnul( spprops ); + +/* Write out the redshift sub-phrase text through the Channel sink function. */ + if( pretty && astChrLen( line ) ) { + astPutNextText( this, line ); + nc = 0; + crem = linelen; + } + } + +/* Write out any remaining text through the Channel sink function. */ + if( nc && astChrLen( line ) ) astPutNextText( this, line ); + +/* Free resources. */ + line = astFree( line ); + +} + +static int WriteRegion( AstStcsChan *this, AstRegion *reg, AstKeyMap *props, + int *status ){ +/* +* Name: +* WriteRegion + +* Purpose: +* Convert a Region into a set of STC-S properties and store them in a +* KeyMap. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* int WriteRegion( AstStcsChan *this, AstRegion *reg, AstKeyMap *props, +* int *status ) + +* Class Membership: +* StcsChan member function + +* Description: +* This function attempts to convert the supplied Region nto a set of +* STC-S properties, and stores them in the supplied KeyMap. + +* Parameters: +* this +* Pointer to the StcsChan being used. +* reg +* Pointer to the region to be converted. +* props +* Pointer to the KeyMap in which to store the STC-S properties. +* On exit, each STC-S sub-phrase has an entry in this KeyMap, +* and each of these entries has a value that is another KeyMap +* holding the properties for the sub-phrase. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the conversion was succesful, and +* zero is returned otherwise. +*/ + +/* Local Variables: */ + AstFrame *efrm; /* Pointer to encapsulated Frame */ + AstFrame *pfrm; /* Pointer to primary Frame cntaining an axis */ + AstFrame *spfrm; /* The sub-phrase Frame */ + AstKeyMap *spprops; /* Sub-phrase properties */ + AstMapping *map; /* Base->current Region Mapping */ + AstMapping *sreg; /* Simplified Region */ + AstObject *obj; /* Generic object pointer */ + AstRegion *spreg; /* The sub-phrase Region */ + AstRegion *treg; /* Temporary Region pointer */ + AstRegion *unc; /* Uncertainty region */ + AstRegion *unca; /* Adaptive uncertainty region */ + AstStdOfRestType sor; /* StdOfRest attribute value */ + AstSystemType sys; /* System attribute value */ + char *prop; /* Formatted property string */ + char *unit1; /* Pointer to string holding first axis unit */ + char buf[ 100 ]; /* Buffer for formatted values */ + char fmt[ 10 ]; /* Buffer for format specifier */ + const char *class; /* Class name */ + const char *dom; /* Domain name */ + const char *dopdef; /* DopplerDef value */ + const char *flavour; /* The STC-S flavour for the space frame */ + const char *q; /* Pointer to next character */ + const char *tfrm; /* STC-S string for Frame */ + const char *tsor; /* STC-S string for RefPos */ + const char *tts; /* Time scale label */ + const char *type; /* Redshift Type value */ + const char *unit; /* Unit string */ + double *pcen; /* Pointer to Circle or ellipse centre */ + double equinox; /* The required equinox value */ + double error; /* Axis error value */ + double fill; /* Fill factor */ + double lbnd[ 3 ]; /* Region lower bounds */ + double lim; /* Unlimited bounds value */ + double p1[ 2 ]; /* End point of error line */ + double scale; /* Factor for scaling Region values into required units */ + double ubnd[ 3 ]; /* Region upper bounds */ + int allthesame; /* Do all axes have the same units? */ + int defdigs; /* Default number of digits */ + int defs; /* Include default values in output STC-S? */ + int i; /* Loop index */ + int issky; /* Do the space axes form a SkyFrame? */ + int nax; /* The number of axes */ + int nc; /* Number of characters in "prop" string */ + int nspace; /* Number of space axes */ + int ok; /* Can the Region be written out? */ + int pax; /* Index of axis in primary Frame */ + int redax; /* The index of the redshift axis */ + int retain_units; /* Retain the units/system in properties KeyMap? */ + int spaceax[ 3 ]; /* Indicies of the space axes */ + int spaceid; /* Code for space sub-phrase identifier */ + int specax; /* The index of the spectral axis */ + int timeax; /* Index of time axis */ + int ts; /* Time scale identifier */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise things to avoid comiler warnings. */ + sys = AST__BADSYSTEM; + +/* Assume we can do the conversion. */ + ok = 1; + +/* See if default values are to be included in the output. */ + defs = ( astGetFull( this ) > 0 ); + +/* STC-S requires that the spatial shape (circle, box. etc) refers to + the coordinate system described by the STC-S. This is not quite like + AST, in that the AST class type (Circle, Box, etc) defines the + shape of the region in the base Frame, rather than the current Frame. + So we can only write the Region out using STC-S if the shape in the + current Frame is the same as the shape in the base Frame. This is the + case if the simplified Mapping connecting base and current Frames is + a UnitMap. Get the base->current Mapping from the Region. */ + map = astRegMapping( reg ); + +/* If it is not UnitMap, see if simplifying the whole Region results in + the base->current Mapping in the simplified Region being a UnitMap. */ + if( !astIsAUnitMap( map ) ) { + map = astAnnul( map ); + sreg = astSimplify( reg ); + map = astRegMapping( sreg ); + +/* If it is still not UnitMap, we cannot write out the region. */ + if( !astIsAUnitMap( map ) ) { + astAddWarning( this, 1, "The supplied Region does not have a " + "supported shape within its current coordinate " + "system.", "astWrite", status ); + ok = 0; + } + + } else { + sreg = astClone( reg ); + } + map = astAnnul( map ); + +/* Store a safe value that can be used to test unbounded axes. */ + lim = sqrt( DBL_MAX ); + +/* First job is to identify the Time, Space, Spectral and Redshift axes + in the supplied Region. + ------------------------------------------------------------------- */ + +/* Initialise things. */ + timeax = -1; + nspace = 0; + issky = 0; + specax = -1; + redax = -1; + prop = NULL; + +/* Get a pointer to the Frame encapsulated by the Region. */ + efrm = astRegFrame( sreg ); + +/* Loop round all axes. */ + nax = astGetNaxes( sreg ); + for( i = 0; i < nax; i++ ) { + +/* Get the primary Frame that defines the current axis of the Region. */ + astPrimaryFrame( efrm, i, &pfrm, &pax ); + +/* Get its class and domain. */ + class = astGetClass( pfrm ); + dom = astGetDomain( pfrm ); + if( astOK ) { + +/* The time axis is described by a TimeFrame with any domain. */ + if( !strcmp( class, "TimeFrame" ) ) { + if( timeax == -1 ) { + timeax = i; + } else { + astAddWarning( this, 1, "More than one time axis found. " + "Extra axis (axis %d) will be ignored.", + "astWrite", status, i + 1 ); + } + +/* The space axes are described by a SkyFrame or a basic Frame. If a + mixture of both types are found, report a warning and ignore the later + axes. */ + } else if( !strcmp( class, "SkyFrame" ) ) { + if( issky || nspace == 0 ) { + if( nspace < 2 ) { + spaceax[ nspace++ ] = i; + issky = 1; + } else { + astAddWarning( this, 1, "More than two sky frame axes " + "found. Extra axis (axis %d) will be ignored.", + "astWrite", status, i + 1 ); + } + + } else { + astAddWarning( this, 1, "Mixture of basic and sky frame " + "axes found. Sky frame axis %d will be " + "ignored.", "astWrite", status, i + 1 ); + } + + } else if( !strcmp( class, "Frame" ) ) { + if( !issky ) { + if( nspace < 3 ) { + spaceax[ nspace++ ] = i; + } else { + astAddWarning( this, 1, "More than three basic space frame axes " + "found. Extra axis (axis %d) will be ignored.", + "astWrite", status, i + 1 ); + } + + } else { + astAddWarning( this, 1, "Mixture of basic and sky frame " + "axes found. Basic frame axis %d will be " + "ignored.", "astWrite", status, i + 1 ); + } + +/* The spectral axis is described by a SpecFrame with domain SPECTRUM. */ + } else if( !strcmp( class, "SpecFrame" ) && + !strcmp( dom, "SPECTRUM" ) ) { + if( specax == -1 ) { + specax = i; + } else { + astAddWarning( this, 1, "More than one spectral axis found. " + "Extra axis (axis %d) will be ignored.", + "astWrite", status, i + 1 ); + } + +/* The redshift axis is described by a SpecFrame with domain REDSHIFT. */ + } else if( !strcmp( class, "SpecFrame" ) && + !strcmp( dom, "REDSHIFT" ) ) { + if( redax == -1 ) { + redax = i; + } else { + astAddWarning( this, 1, "More than one redshift axis found. " + "Extra axis (axis %d) will be ignored.", + "astWrite", status, i + 1 ); + } + +/* Warn about unused axes. */ + } else { + astAddWarning( this, 1, "Could not classify axis %d (class=%s " + "domain=%s). It will be ignored.", "astWrite", status, + i + 1, class, dom ); + } + } + +/* Free resources. */ + pfrm = astAnnul( pfrm ); + } + efrm = astAnnul( efrm ); + +/* Set a flag indicating if there is anything to convert. */ + ok = ok && ( timeax != -1 || nspace > 0 || specax != -1 || redax != -1 ); + + +/* Now we have identified the axes, we convert each available STC-S + sub-phrase, starting with the time sub-phrase. + ---------------------------------------------------------------- */ + if( timeax != -1 ) { + +/* Create a Region by picking the time axis from the supplied Region. */ + spreg = astPickAxes( sreg, 1, &timeax, NULL ); + +/* Check it is a Region. If not, we cannot convert anything. */ + if( !astIsARegion( spreg ) ) { + astAddWarning( this, 1, "Cannot determine the region covered by " + "the time axis.", "astWrite", status ); + ok = 0; + +/* Otherwise we add a description of the time sub-phrase to the + properties keymap. */ + } else { + +/* Get a pointer to the Region's time phrase property KeyMap, creating + one if necessary. */ + if( astMapGet0A( props, "TIME_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + } else { + spprops = astKeyMap( " ", status ); + astMapPut0A( props, "TIME_PROPS", spprops, NULL ); + } + +/* Get the Region's fill factor. */ + fill = astGetFillFactor( spreg ); + +/* Ensure the TimeFrame represents MJD. If not, take a deep copy (to + avoid changing the supplied Region), and set its system to MJD. */ + if( astGetSystem( spreg ) != AST__MJD ) { + treg = astCopy( spreg ); + (void) astAnnul( spreg ); + spreg = treg; + astSetAdaptive( spreg, 1 ); + astSetSystem( spreg, AST__MJD ); + } + +/* Get the bounds of the Region (i.e. the time axis coverage). */ + astGetRegionBounds( spreg, lbnd, ubnd ); + +/* Get a pointer to the time Region's encapsulated Frame. */ + spfrm = astRegFrame( spreg ); + +/* Report a warning if the sub-phrase Frame is not a TimeFrame */ + if( !astIsATimeFrame( spfrm ) ) { + ok = 0; + astAddWarning( this, 1, "The time sub-phrase in the supplied " + "KeyMap is not described using an AST TimeFrame.", + "astWrite", status ); + +/* Store properties that are specific to Time moments... */ + } else if( lbnd[ 0 ] == ubnd[ 0 ] ) { + astMapPut0C( spprops, "ID", "Time", NULL ); + StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "TIME", lbnd[ 0 ], status ); + fill = AST__BAD; + +/* Store properties that are specific to Time intervals... */ + } else if( lbnd[ 0 ] > -lim && ubnd[ 0 ] < lim ) { + astMapPut0C( spprops, "ID", "TimeInterval", NULL ); + StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "START", lbnd[ 0 ], status ); + StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "STOP", ubnd[ 0 ], status ); + +/* Store properties that are specific to Start times... */ + } else if( lbnd[ 0 ] > -lim ) { + astMapPut0C( spprops, "ID", "StartTime", NULL ); + StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "START", lbnd[ 0 ], status ); + +/* Store properties that are specific to Stop times... */ + } else { + astMapPut0C( spprops, "ID", "StopTime", NULL ); + StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "STOP", ubnd[ 0 ], status ); + + } + +/* Store properties that are common to all time sub-phrase types. First the + fill factor. */ + MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status ); + +/* Now the time scale. */ + ts = astGetTimeScale( spfrm ); + if( ts == AST__TT ) { + tts = "TT"; + + } else if( ts == AST__TAI ) { + tts = "TAI"; + + } else if( ts == AST__UTC ) { + tts = "UTC"; + + } else if( ts == AST__TDB ) { + tts = "TDB"; + + } else if( ts == AST__TCG ) { + tts = "TCG"; + + } else if( ts == AST__TCB ) { + tts = "TCB"; + + } else if( ts == AST__LMST ) { + tts = "LST"; + + } else { + tts = "nil"; + astAddWarning( this, 1, "Timescale '%s' is unsupported by " + "STC-S.", "astWrite", status, + astGetC( spfrm, "TimeScale" ) ); + ok = 0; + } + + MapPut0C( spprops, "TIMESCALE", tts, "nil", defs, status ); + +/* RefPos. The AST TimeFrame class has no reference position, we leave + unchanged any refpos already in the keymap. If there is no refpos in the + keymap, we use "TOPOCENTER". */ + if( !astMapHasKey( spprops, "REFPOS" ) ) { + astMapPut0C( spprops, "REFPOS", "TOPOCENTER", NULL ); + } + +/* That's it for the time sub-phrase, unless the supplied Region has an + explicit (non-default) uncertainty. */ + unc = astGetUnc( spreg, 0 ); + if( unc ) { + +/* See if the supplied properties KeyMap contains any item that refers to + the Unit included in the STC-S description, but which is not updated by + this function. If it does, we need to retain any units specified + within the KeyMap. */ + retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) || + astMapHasKey( spprops, "PIXSIZE" ) || + astMapHasKey( spprops, "SIZE" ) ); + + if( retain_units ) { + if( !astMapGet0C( spprops, "UNIT", &unit ) ) unit = "s"; + } else { + unit = "s"; + } + +/* Store the units string */ + MapPut0C( spprops, "UNIT", unit, "s", defs, status ); + +/* If necessary, map the uncertainty region into the requied units. Take + a deep copy to avoid changing the supplied Region. */ + if( strcmp( unit, astGetUnit( unc, 0 ) ) ) { + unca = astCopy( unc ); + astSetAdaptive( unca, 0 ); + astSetUnit( unca, 0, unit ); + } else { + unca = astClone( unc ); + } + +/* Get the bounds of the uncertainty. */ + astGetRegionBounds( unca, lbnd, ubnd ); + +/* The error is half the width of the bounding box. */ + astMapPut0D( spprops, "ERROR", 0.5*( ubnd[ 0 ] - lbnd[ 0 ] ), NULL ); + +/* Free resources. */ + unca = astAnnul( unca ); + unc = astAnnul( unc ); + } + +/* Free resources. */ + spfrm = astAnnul( spfrm ); + spprops = astAnnul( spprops ); + } + +/* Free resources. */ + spreg = astAnnul( spreg ); + + } + + +/* Now convert the space sub-phrase. + ---------------------------------------------------------------- */ + if( nspace > 0 && ok ) { + +/* Create a Region by picking the space axes from the supplied Region. */ + spreg = astPickAxes( sreg, nspace, spaceax, NULL ); + +/* Check it is a Region. If not, we cannot convert anything. */ + if( ! astIsARegion( spreg ) ) { + astAddWarning( this, 1, "Cannot determine the region covered by " + "the space axes.", "astWrite", status ); + ok = 0; + +/* Otherwise we add a description of the space sub-phrase to the + properties keymap. */ + } else { + +/* Get a pointer to the Region's space phrase property KeyMap, creating + one if necessary. */ + if( astMapGet0A( props, "SPACE_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + } else { + spprops = astKeyMap( " ", status ); + astMapPut0A( props, "SPACE_PROPS", spprops, NULL ); + } + +/* If the space frame is a SkyFrame, ensure it refers to a coodinate + system that is supported by STC-S. Take a deep copy before changing + anything. */ + if( issky ) { + sys = astGetSystem( spreg ); + if( sys != AST__FK4 && + sys != AST__FK5 && + sys != AST__ICRS && + sys != AST__ECLIPTIC && + sys != AST__GALACTIC && + sys != AST__SUPERGALACTIC && + sys != AST__UNKNOWN ) { + treg = astCopy( spreg ); + (void) astAnnul( spreg ); + spreg = treg; + astSetAdaptive( spreg, 1 ); + astSetSystem( spreg, AST__ICRS ); + } + } + +/* Get a pointer to the Region's encapsulated Frame. */ + spfrm = astRegFrame( spreg ); + +/* If the supplied Region is defined in a SkyFrame, choose the units to + use when storing radius, error, etc in the KeyMap. If the props KeyMap + already contains a unit specification, we use it. Otherwise we use the + default (degrees). AST uses radians internally, so find the scaling + factor. */ + if( issky ) { + if( astMapGet0C( spprops, "UNIT", &unit ) ) { + if( !strcmp( unit, "arcmin" ) ) { + scale = AST__DR2D*60.0; + } else if( !strcmp( unit, "arcsec" ) ) { + scale = AST__DR2D*3600.0; + } else { + unit = "deg"; + scale = AST__DR2D; + } + } else { + unit = "deg"; + scale = AST__DR2D; + } + +/* Store the units string */ + MapPut0C( spprops, "UNIT", unit, "deg", defs, status ); + +/* If the supplied Region is not defined in a SkyFrame, we will arrange + that the Region and the KeyMap use the same units, so set a scale + factor of 1.0. */ + } else { + scale = 1.0; + +/* See if the supplied properties KeyMap contains any item that refers to + the Unit included in the STC-S description, but which is not updated by + this function. If it does, we need to retain any units specified + within the KeyMap. */ + retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) || + astMapHasKey( spprops, "PIXSIZE" ) || + astMapHasKey( spprops, "SIZE" ) ); + +/* If so, and if the properties KeyMap already contains a Unit + specification, we convert the Region to the same units. Take a deep + copy of the Region first to avoid modifying the supplied Region. */ + if( retain_units ) { + if( !astMapGet0C( spprops, "UNIT", &unit ) ) unit = "deg"; + + treg = astCopy( spreg ); + (void) astAnnul( spreg ); + spreg = treg; + + for( i = 0; i < nspace; i++ ) { + astSetUnit( spreg, i, unit ); + +/* Space frames can have different units on different axes. So look for + the start of the next word in the Unit propert. This will be the unit + for the next axis. If there are no more words in the Unit property, + re-use the last unit value. */ + q = unit; + while( *q && !isspace( *q ) ) q++; + while( *q && isspace( *q ) ) q++; + if( *q ) unit = q; + } + +/* If we are not retaining the units specified in the properties KeyMap, we + retain the existing Region units instead, and store these units in the + properties KeyMap. We also check that these units are supported by + STC-S. */ + } else { + + nc = 0; + allthesame = 1; + unit1 = NULL; + + for( i = 0; i < nspace; i++ ) { + unit = astGetUnit( spreg, i ); + + if( !unit1 ) { + unit1 = astStore( NULL, unit, strlen( unit ) + 1 ); + } else { + if( strcmp( unit, unit1 ) ) allthesame = 0; + } + + if( strcmp( unit, "deg" ) && + strcmp( unit, "arcmin" ) && + strcmp( unit, "arcsec" ) && + strcmp( unit, "m" ) && + strcmp( unit, "mm" ) && + strcmp( unit, "km" ) && + strcmp( unit, "AU" ) && + strcmp( unit, "pc" ) && + strcmp( unit, "kpc" ) && + strcmp( unit, "Mpc" ) ) { + astAddWarning( this, 1, "Cannot use spatial units '%s'.", + "astWrite", status, unit ); + ok = 0; + break; + } + prop = astAppendString( prop, &nc, unit ); + prop = astAppendString( prop, &nc, " " ); + } + +/* Remove the trailing space, and store the property value in the KeyMap. */ + if( ! allthesame ) { + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "UNIT", prop, NULL ); + } + } else { + astMapPut0C( spprops, "UNIT", unit1, NULL ); + } + + unit1 = astFree( unit1 ); + + } + } + +/* Get the fill factor. */ + fill = astGetFillFactor( spreg ); + +/* Get the default number of digits. This is only used if the supplied + properties KeyMap does not have a value for the item being stored. If + it does, the number of digits is inherited form the value int he KeyMap. */ + defdigs = astGetDigits( spfrm ); + +/* Store properties that are specific to the particular type of Region. */ + spaceid = GetRegionProps( this, spreg, spprops, nspace, defdigs, + scale, issky, status ); + if( spaceid == NULL_ID ) ok = 0; + +/* If the above went OK, store values for the properties that are common + to all types of space sub-phrase. */ + if( ok ) { + +/* First the fill factor. */ + if( spaceid != POSITION_ID ) { + MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status ); + } + +/* Now the coordinate frame. */ + tfrm = NULL; + sys = astGetSystem( spfrm ); + if( issky ) { + if( sys == AST__FK4 ){ + tfrm = "B1950"; + equinox = 1950.0; + + } else if( sys == AST__FK5 ){ + tfrm = "J2000"; + equinox = 2000.0; + + } else if( sys == AST__ICRS ){ + tfrm = "ICRS"; + equinox = AST__BAD; + + } else if( sys == AST__ECLIPTIC ){ + tfrm = "ECLIPTIC"; + equinox = 2000.0; + + } else if( sys == AST__GALACTIC ){ + tfrm = "GALACTIC"; + equinox = AST__BAD; + + } else if( sys == AST__SUPERGALACTIC ){ + tfrm = "SUPER_GALACTIC"; + equinox = AST__BAD; + + } else if( sys == AST__UNKNOWN ){ + tfrm = NULL; + equinox = AST__BAD; + + } else { + tfrm = NULL; + astAddWarning( this, 1, "Sky system '%s' is " + "unsupported by STC-S.", "astWrite", + status, astGetC( spfrm, "System" ) ); + ok = 0; + } + + if( tfrm && equinox != AST__BAD ) { + if( astGetD( spfrm, "Equinox" ) != equinox ) { + astAddWarning( this, 1, "STC-S requires an equinox " + "of %g for the %s frame, but the " + "supplied %s equinox is %g.", "astWrite", + status, equinox, tfrm, + astGetClass( spfrm ), + astGetD( spfrm, "Equinox" ) ); + ok = 0; + tfrm = NULL; + } + } + } + +/* If we do not yet have a Frame, use the Domain value if it is set (and + is a legal STC-S Frame). */ + if( ! tfrm ) { + if( astTestDomain( spfrm ) ) { + tfrm = astGetDomain( spfrm ); + if( strcmp( tfrm, "ICRS" ) && + strcmp( tfrm, "FK5" ) && + strcmp( tfrm, "FK4" ) && + strcmp( tfrm, "J2000" ) && + strcmp( tfrm, "B1950" ) && + strcmp( tfrm, "ECLIPTIC" ) && + strcmp( tfrm, "GALACTIC" ) && + strcmp( tfrm, "GALACTIC_II" ) && + strcmp( tfrm, "SUPER_GALACTIC" ) && + strcmp( tfrm, "GEO_C" ) && + strcmp( tfrm, "GEO_D" ) ){ + astAddWarning( this, 1, "'UNKNOWNFrame' being used in " + "place of unsupported frame '%s'.", + "astWrite", status, tfrm ); + tfrm = NULL; + } + } + } + +/* Store the Frame name in the props keymap. */ + if( !tfrm ) tfrm = "UNKNOWNFrame"; + astMapPut0C( spprops, "FRAME", tfrm, NULL ); + +/* RefPos. The AST SkyFrame and Frame classes have no reference position, so + we leave unchanged any refpos already in the props keymap. If there is + no refpos in the keymap, we use "TOPOCENTER". */ + if( !astMapHasKey( spprops, "REFPOS" ) ) { + astMapPut0C( spprops, "REFPOS", "TOPOCENTER", NULL ); + } + +/* Flavour. */ + if( issky ) { + flavour = "SPHER2"; + } else if( nspace == 1 ){ + flavour = "CART1"; + } else if( nspace == 2 ){ + flavour = "CART2"; + } else { + flavour = "CART3"; + } + MapPut0C( spprops, "FLAVOUR", flavour, "SPHER2", defs, status ); + +/* That's it for the space sub-phrase, unless the supplied Region has an + explicit (non-default) uncertainty. */ + unc = astGetUnc( spreg, 0 ); + if( unc ) { + +/* Get the bounds of the uncertainty. */ + astGetRegionBounds( unc, lbnd, ubnd ); + +/* If its a sky frame, find the position of the centre of the uncertainty + region. */ + pcen = issky ? astRegCentre( unc, NULL, NULL, 0, + AST__CURRENT ) : NULL; + +/* Find the half-width of the bounding box for each space axis, and + concatenate their formatted values into a string. If any bound is + undefined, quit the axis loop with nc=0. We need to convert longitude + axis values from lingitude increments to arc-distance. */ + nc = 0; + defdigs = astGetDigits( unc ); + + for( i = 0; i < nspace; i++ ) { + if( ubnd[ i ] != AST__BAD && lbnd[ i ] != AST__BAD ){ + + if( ! issky ) { + error = 0.5*( ubnd[ i ] - lbnd[ i ] ); + } else { + if( i == 0 ) { + p1[ 0 ] = ubnd[ 0 ]; + p1[ 1 ] = pcen[ 1 ]; + } else { + p1[ 0 ] = pcen[ 0 ]; + p1[ 1 ] = ubnd[ 1 ]; + } + error = astDistance( spfrm, pcen, p1 ); + } + + GetFmt( "ERROR", spprops, i, defdigs, fmt, status ); + (void) sprintf( buf, fmt, scale*error ); + prop = astAppendString( prop, &nc, buf ); + prop = astAppendString( prop, &nc, " " ); + + } else { + nc = 0; + break; + } + } + +/* If the bounds were all good, store the string holding the formatted + error values in the properties KeyMap. */ + if( prop && nc > 0 ) { + prop[ nc - 1 ] = 0; + astMapPut0C( spprops, "ERROR", prop, NULL ); + } + +/* Free resources. */ + pcen = astFree( pcen ); + unc = astAnnul( unc ); + } + } + +/* Free resources. */ + spfrm = astAnnul( spfrm ); + spprops = astAnnul( spprops ); + } + +/* Free resources. */ + spreg = astAnnul( spreg ); + + } + + + +/* Convert the spectral sub-phrase. + ---------------------------------------------------------------- */ + if( specax != -1 ) { + +/* Create a Region by picking the spectral axis from the supplied Region. */ + spreg = astPickAxes( sreg, 1, &specax, NULL ); + +/* Check it is a Region. If not, we cannot convert anything. */ + if( !astIsARegion( spreg ) ) { + astAddWarning( this, 1, "Cannot determine the region covered by " + "the spectral axis.", "astWrite", status ); + ok = 0; + +/* Otherwise we add a description of the spectral sub-phrase to the + properties keymap. */ + } else { + +/* Get a pointer to the Region's spectral phrase property KeyMap, creating + one if necessary. */ + if( astMapGet0A( props, "SPECTRAL_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + } else { + spprops = astKeyMap( " ", status ); + astMapPut0A( props, "SPECTRAL_PROPS", spprops, NULL ); + } + +/* See if the supplied properties KeyMap contains any item that refers to + the Unit included in the STC-S description, but which is not updated by + this function. If it does, we need to retain any units specified + within the KeyMap. */ + retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) || + astMapHasKey( spprops, "PIXSIZE" ) || + astMapHasKey( spprops, "SIZE" ) ); + +/* If so, and if the properties KeyMap already contains a Unit specification, + we convert the Region to the same units and system. Determine the + required system and units. */ + if( retain_units ) { + if( !astMapGet0C( spprops, "UNIT", &unit ) ) unit = "Hz"; + + if( !strcmp( unit, "Hz" ) || + !strcmp( unit, "MHz" ) || + !strcmp( unit, "GHz" ) ) { + sys = AST__FREQ; + + } else if( !strcmp( unit, "m" ) || + !strcmp( unit, "mm" ) || + !strcmp( unit, "um" ) || + !strcmp( unit, "nm" ) || + !strcmp( unit, "Angstrom" ) ) { + sys = AST__WAVELEN; + + } else if( !strcmp( unit, "eV" ) || + !strcmp( unit, "keV" ) || + !strcmp( unit, "MeV" ) ) { + sys = AST__ENERGY; + + } else { + astAddWarning( this, 1, "Illegal STC-S units '%s' found in " + "supplied KeyMap", "astWrite", status, unit ); + ok = 0; + } + +/* If we do not need to retain the units implied by the supplied KeyMap, + use the Units and system in the supplied Region so long as they are + supported by STC-S. If not, use a related supported system instead. */ + } else { + sys = astGetSystem( spreg ); + unit = astGetUnit( spreg, 0 ); + + if( sys == AST__ENERGY ) { + sys = AST__ENERGY; + if( strcmp( unit, "eV" ) && + strcmp( unit, "keV" ) && + strcmp( unit, "MeV" ) ) unit = "eV"; + + } else if( sys == AST__WAVELEN || sys == AST__AIRWAVE || + sys == AST__VOPTICAL || sys == AST__REDSHIFT ){ + sys = AST__WAVELEN; + if( strcmp( unit, "m" ) && + strcmp( unit, "mm" ) && + strcmp( unit, "um" ) && + strcmp( unit, "nm" ) && + strcmp( unit, "Angstrom" ) ) unit = "m"; + + } else { + sys = AST__FREQ; + if( strcmp( unit, "Hz" ) && + strcmp( unit, "MHz" ) && + strcmp( unit, "GHz" ) ) unit = "Hz"; + + } + } + +/* Store the units string */ + MapPut0C( spprops, "UNIT", unit, "Hz", defs, status ); + +/* If either the System or Unit needs to be changed in the Region, take a + deep copy first in order to avoid changing the supplied Region. */ + if( sys != astGetSystem( spreg ) || + ( unit && strcmp( unit, astGetUnit( spreg, 0 ) ) ) ) { + treg = astCopy( spreg ); + (void) astAnnul( spreg ); + spreg = treg; + astSetAdaptive( spreg, 1 ); + astSetSystem( spreg, sys ); + astSetUnit( spreg, 0, unit ); + } + +/* Get the Region's fill factor. */ + fill = astGetFillFactor( spreg ); + +/* Get the bounds of the Region (i.e. the spectral axis coverage). */ + astGetRegionBounds( spreg, lbnd, ubnd ); + +/* Get a pointer to the spectral Region's encapsulated Frame. */ + spfrm = astRegFrame( spreg ); + +/* Report a warning if the sub-phrase Frame is not a SpecFrame */ + if( !astIsASpecFrame( spfrm ) ) { + ok = 0; + astAddWarning( this, 1, "The spectral sub-phrase in the supplied " + "KeyMap is not described using an AST SpecFrame.", + "astWrite", status ); + +/* Store properties that are specific to spectral positions... */ + } else if( lbnd[ 0 ] == ubnd[ 0 ] ) { + astMapPut0C( spprops, "ID", "Spectral", NULL ); + astMapPut0D( spprops, "SPECTRAL", lbnd[ 0 ], NULL ); + fill = AST__BAD; + +/* Store properties that are specific to Spectral intervals... */ + } else if( lbnd[ 0 ] > -lim && ubnd[ 0 ] < lim ) { + astMapPut0C( spprops, "ID", "SpectralInterval", NULL ); + astMapPut0D( spprops, "LOLIMIT", lbnd[ 0 ], NULL ); + astMapPut0D( spprops, "HILIMIT", ubnd[ 0 ], NULL ); + + } else { + ok = 0; + astAddWarning( this, 1, "Cannot write out an unbounded " + "spectral interval.", "astWrite", status ); + } + +/* Store properties that are common to all spectral sub-phrase types. First the + fill factor. */ + MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status ); + +/* Now the reference position. */ + sor = astGetStdOfRest( spfrm ); + if( sor == AST__GESOR ) { + tsor = "GEOCENTER"; + + } else if( sor == AST__BYSOR ) { + tsor = "BARYCENTER"; + + } else if( sor == AST__HLSOR ) { + tsor = "HELIOCENTER"; + + } else if( sor == AST__TPSOR ) { + tsor = "TOPOCENTER"; + + } else if( sor == AST__LKSOR ) { + tsor = "LSRK"; + + } else if( sor == AST__LDSOR ) { + tsor = "LSRD"; + + } else if( sor == AST__GLSOR ) { + tsor = "GALACTIC_CENTER"; + + } else { + tsor = NULL; + } + + if( !tsor ) tsor = "UNKNOWNRefPos"; + MapPut0C( spprops, "REFPOS", tsor, "UNKNOWNRefPos", defs, + status ); + +/* Now the unit string. */ + MapPut0C( spprops, "UNIT", unit, "Hz", defs, status ); + +/* That's it for the spectral sub-phrase, unless the supplied Region has an + explicit (non-default) uncertainty. */ + unc = astGetUnc( spreg, 0 ); + if( unc ) { + +/* Get the bounds of the uncertainty. */ + astGetRegionBounds( unc, lbnd, ubnd ); + +/* The error is half the width of the bounding box. */ + astMapPut0D( spprops, "ERROR", 0.5*( ubnd[ 0 ] - lbnd[ 0 ] ), NULL ); + +/* Free resources. */ + unc = astAnnul( unc ); + } + +/* Free resources. */ + spfrm = astAnnul( spfrm ); + spprops = astAnnul( spprops ); + } + +/* Free resources. */ + spreg = astAnnul( spreg ); + + } + + + +/* Convert the redshift sub-phrase. + ---------------------------------------------------------------- */ + if( redax != -1 ) { + +/* Create a Region by picking the redshift axis from the supplied Region. */ + spreg = astPickAxes( sreg, 1, &redax, NULL ); + +/* Check it is a Region. If not, we cannot convert anything. */ + if( !astIsARegion( spreg ) ) { + astAddWarning( this, 1, "Cannot determine the region covered by " + "the redshift axis.", "astWrite", status ); + ok = 0; + +/* Otherwise we add a description of the redshift sub-phrase to the + properties keymap. */ + } else { + +/* Get a pointer to the Region's redshift phrase property KeyMap, creating + one if necessary. */ + if( astMapGet0A( props, "REDSHIFT_PROPS", &obj ) ) { + spprops = (AstKeyMap *) obj; + } else { + spprops = astKeyMap( " ", status ); + astMapPut0A( props, "REDSHIFT_PROPS", spprops, NULL ); + } + +/* See if the supplied properties KeyMap contains any item that refers to + the system included in the STC-S description, but which is not updated by + this function. If it does, we need to retain any system specified + within the KeyMap. */ + retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) || + astMapHasKey( spprops, "PIXSIZE" ) || + astMapHasKey( spprops, "SIZE" ) ); + +/* If so, and if the properties KeyMap already contains a DopplerDef or + Type specification, we convert the Region to the same system. */ + if( retain_units ){ + if( !astMapGet0C( spprops, "DOPPLERDEF", &dopdef ) ) dopdef = "OPTICAL"; + if( !astMapGet0C( spprops, "TYPE", &type ) ) type = "VELOCITY"; + + if( astChrMatch( type, "VELOCITY" ) ) { + if( astChrMatch( dopdef, "OPTICAL" ) ) { + sys = AST__VOPTICAL; + } else if( astChrMatch( dopdef, "RADIO" ) ) { + sys = AST__VRADIO; + } else if( astChrMatch( dopdef, "RELATIVISTIC" ) ) { + sys = AST__VREL; + } else { + astAddWarning( this, 1, "Illegal STC-S DopplerDef '%s' " + "found in supplied KeyMap", "astWrite", status, + dopdef ); + ok = 0; + } + + } else if( astChrMatch( type, "REDSHIFT" ) ) { + if( astChrMatch( dopdef, "OPTICAL" ) ) { + sys = AST__REDSHIFT; + } else { + astAddWarning( this, 1, "Unsupported combination of " + "DopplerDef='%s' and Type='%s' found in " + "supplied KeyMap", "astWrite", status, dopdef, + type ); + ok = 0; + } + + } else { + astAddWarning( this, 1, "Illegal STC-S Redshift Type '%s' " + "found in supplied KeyMap", "astWrite", status, + type ); + ok = 0; + } + +/* If the supplied KeyMap does not imply the required system, use the + system in the supplied Region. */ + } else { + sys = astGetSystem( spreg ); + } + +/* Choose the requied units. */ + unit = ( sys == AST__REDSHIFT ) ? "": "km/s"; + +/* Store the units string */ + MapPut0C( spprops, "UNIT", unit, unit, defs, status ); + +/* If either the System or Unit needs to be changed in the Region, take a + deep copy first in order to avoid changing the supplied Region. */ + if( sys != astGetSystem( spreg ) || + ( unit && strcmp( unit, astGetUnit( spreg, 0 ) ) ) ) { + treg = astCopy( spreg ); + (void) astAnnul( spreg ); + spreg = treg; + astSetAdaptive( spreg, 1 ); + astSetSystem( spreg, sys ); + astSetUnit( spreg, 0, unit ); + } + +/* Get the Region's fill factor. */ + fill = astGetFillFactor( spreg ); + +/* Get the bounds of the Region (i.e. the redshift axis coverage). */ + astGetRegionBounds( spreg, lbnd, ubnd ); + +/* Get a pointer to the spectral Region's encapsulated Frame. */ + spfrm = astRegFrame( spreg ); + +/* Report a warning if the sub-phrase Frame is not a SpecFrame */ + if( !astIsASpecFrame( spfrm ) ) { + ok = 0; + astAddWarning( this, 1, "The redshift sub-phrase in the supplied " + "KeyMap is not described using an AST SpecFrame.", + "astWrite", status ); + +/* Store properties that are specific to redshift positions... */ + } else if( lbnd[ 0 ] == ubnd[ 0 ] ) { + astMapPut0C( spprops, "ID", "Redshift", NULL ); + astMapPut0D( spprops, "REDSHIFT", lbnd[ 0 ], NULL ); + fill = AST__BAD; + +/* Store properties that are specific to Redshift intervals... */ + } else if( lbnd[ 0 ] > -lim && ubnd[ 0 ] < lim ) { + astMapPut0C( spprops, "ID", "RedshiftInterval", NULL ); + astMapPut0D( spprops, "LOLIMIT", lbnd[ 0 ], NULL ); + astMapPut0D( spprops, "HILIMIT", ubnd[ 0 ], NULL ); + + } else { + ok = 0; + astAddWarning( this, 1, "Cannot write out an unbounded " + "redshift interval.", "astWrite", status ); + } + +/* Store properties that are common to all redshift sub-phrase types. First the + fill factor. */ + MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status ); + +/* Now the reference position. */ + sor = astGetStdOfRest( spfrm ); + + if( sor == AST__GESOR ) { + tsor = "GEOCENTER"; + + } else if( sor == AST__BYSOR ) { + tsor = "BARYCENTER"; + + } else if( sor == AST__HLSOR ) { + tsor = "HELIOCENTER"; + + } else if( sor == AST__TPSOR ) { + tsor = "TOPOCENTER"; + + } else if( sor == AST__LKSOR ) { + tsor = "LSRK"; + + } else if( sor == AST__LDSOR ) { + tsor = "LSRD"; + + } else if( sor == AST__GLSOR ) { + tsor = "GALACTIC_CENTER"; + + } else { + tsor = NULL; + } + + if( !tsor ) tsor = "UNKNOWNRefPos"; + MapPut0C( spprops, "REFPOS", tsor, "UNKNOWNRefPos", defs, + status ); + +/* Type and DopplerDef. */ + if( sys == AST__VOPTICAL ) { + type = "VELOCITY"; + dopdef = "OPTICAL"; + + } else if( sys == AST__VRADIO ) { + type = "VELOCITY"; + dopdef = "RADIO"; + + } else if( sys == AST__VREL ) { + type = "VELOCITY"; + dopdef = "RELATIVISTIC"; + + } else { + type = "REDSHIFT"; + dopdef = "OPTICAL"; + } + astMapPut0C( spprops, "DOPPLERDEF", dopdef, NULL ); + MapPut0C( spprops, "TYPE", type, "REDSHIFT", defs, status ); + +/* Now the unit string. */ + MapPut0C( spprops, "UNIT", unit, unit, defs, status ); + +/* That's it for the redshift sub-phrase, unless the supplied Region has an + explicit (non-default) uncertainty. */ + unc = astGetUnc( spreg, 0 ); + if( unc ) { + +/* Get the bounds of the uncertainty. */ + astGetRegionBounds( unc, lbnd, ubnd ); + +/* The error is half the width of the bounding box. */ + astMapPut0D( spprops, "ERROR", 0.5*( ubnd[ 0 ] - lbnd[ 0 ] ), NULL ); + +/* Free resources. */ + unc = astAnnul( unc ); + } + +/* Free resources. */ + spfrm = astAnnul( spfrm ); + spprops = astAnnul( spprops ); + } + +/* Free resources. */ + spreg = astAnnul( spreg ); + + } + +/* Free resources */ + if( sreg ) sreg = astAnnul( sreg ); + if( prop ) prop = astFree( prop ); + +/* Return the result. */ + return ok; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* StcsArea + +* Purpose: +* Return the CoordinateArea component when reading an STC-S document? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This is a boolean attribute which controls what is returned +* by the +c astRead +f AST_READ +* function when it is used to read from an StcsChan. +* If StcsArea is set non-zero (the default), then a Region +* representing the STC CoordinateArea will be returned by +c astRead. +f AST_READ. +* If StcsArea is set to zero, then the STC CoordinateArea +* will not be returned. + +* Notes: +* - Other attributes such as StcsCoords and StcsProps can be used to +* specify other Objects to be returned by +c astRead. +f AST_READ. +* If more than one of these attributes is set non-zero, then the +* actual Object returned by +c astRead +f AST_READ +* will be a KeyMap, containing the requested Objects. In this +* case, the Region representing the STC CoordinateArea will be +* stored in the returned KeyMap using the key "AREA". If StcsArea +* is the only attribute to be set non-zero, then the Object returned by +c astRead +f AST_READ +* will be the CoordinateArea Region itself. +* - The class of Region used to represent the CoordinateArea for each +* STC-S sub-phrase is determined by the first word in the +* sub-phrase (the "sub-phrase identifier"). The individual sub-phrase +* Regions are combined into a single Prism, which is then simplified +c using astSimplify +f using AST_SIMPLIFY +* to form the returned region. +* - Sub-phrases that represent a single value ( that is, have +* identifiers "Time", "Position", "Spectral" or "Redshift" ) are +* considered to be be part of the STC CoordinateArea component. +* - The TimeFrame used to represent a time STC-S sub-phrase will have +* its TimeOrigin attribute set to the sub-phrase start time. If no +* start time is specified by the sub-phrase, then the stop time will be +* used instead. If no stop time is specified by the sub-phrase, then +* the single time value specified in the sub-phrase will be used +* instead. Subsequently clearing the TimeOrigin attribute (or setting +* its value to zero) will cause the TimeFrame to reprsent absolute times. +* - The Epoch attribute for the returned Region is set in the same +* way as the TimeOrigin attribute (see above). + +* Applicability: +* StcsChan +* All StcsChans have this attribute. +*att-- +*/ + +/* This ia a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of 1. */ +astMAKE_CLEAR(StcsChan,StcsArea,stcsarea,-INT_MAX) +astMAKE_GET(StcsChan,StcsArea,int,1,( this->stcsarea != -INT_MAX ? this->stcsarea : 1 )) +astMAKE_SET(StcsChan,StcsArea,int,stcsarea,( value != 0 )) +astMAKE_TEST(StcsChan,StcsArea,( this->stcsarea != -INT_MAX )) + +/* +*att++ +* Name: +* StcsCoords + +* Purpose: +* Return the Coordinates component when reading an STC-S document? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This is a boolean attribute which controls what is returned +* by the +c astRead +f AST_READ +* function when it is used to read from an StcsChan. +* If StcsCoords is set non-zero, then a PointList +* representing the STC Coordinates will be returned by +c astRead. +f AST_READ. +* If StcsCoords is set to zero (the default), then the STC +* Coordinates will not be returned. + +* Notes: +* - Other attributes such as StcsArea and StcsProps can be used to +* specify other Objects to be returned by +c astRead. +f AST_READ. +* If more than one of these attributes is set non-zero, then the +* actual Object returned by +c astRead +f AST_READ +* will be a KeyMap, containing the requested Objects. In this +* case, the PointList representing the STC Coordinates will be +* stored in the returned KeyMap using the key "COORDS". If StcsCoords +* is the only attribute to be set non-zero, then the Object returned by +c astRead +f AST_READ +* will be the Coordinates PointList itself. +* - The Coordinates component is specified by the additional axis +* values embedded within the body of each STC-S sub-phrase that +* represents an extended area. Sub-phrases that represent a single +* value ( that is, have identifiers "Time", "Position", "Spectral" +* or "Redshift" ) are not considered to be be part of the STC +* Coordinates component. +* - If the STC-S documents does not contain a Coordinates component, +* then a NULL object pointer +f (AST__NULL) +* will be returned by +c astRead +f AST_READ +* if the Coordinates component is the only object being returned. If +* other objects are also being returned (see attributes StcsProps and +* StcsArea), then the returned KeyMap will contain a "COORDS" key +* only if the Coordinates component is read succesfully. +* - The TimeFrame used to represent a time STC-S sub-phrase will have +* its TimeOrigin attribute set to the sub-phrase start time. If no +* start time is specified by the sub-phrase, then the stop time will be +* used instead. If no stop time is specified by the sub-phrase, then +* the single time value specified in the sub-phrase will be used +* instead. Subsequently clearing the TimeOrigin attribute (or setting +* its value to zero) will cause the TimeFrame to reprsent absolute times. +* - The Epoch attribute for the returned Region is set in the same +* way as the TimeOrigin attribute (see above). + +* Applicability: +* StcsChan +* All StcsChans have this attribute. +*att-- +*/ + +/* This ia a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(StcsChan,StcsCoords,stcscoords,-INT_MAX) +astMAKE_GET(StcsChan,StcsCoords,int,0,( this->stcscoords != -INT_MAX ? this->stcscoords : 0 )) +astMAKE_SET(StcsChan,StcsCoords,int,stcscoords,( value != 0 )) +astMAKE_TEST(StcsChan,StcsCoords,( this->stcscoords != -INT_MAX )) + +/* +*att++ +* Name: +* StcsProps + +* Purpose: +* Return all properties when reading an STC-S document? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This is a boolean attribute which controls what is returned +* by the +c astRead +f AST_READ +* function when it is used to read from an StcsChan. +* If StcsProps is set non-zero, then a KeyMap containing all the +* properties read from the STC-S document will be returned by +c astRead. +f AST_READ. +* If StcsProps is set to zero (the default), then the properties +* will not be returned. + +* Notes: +* - Other attributes such as StcsCoords and StcsArea can be used to +* specify other Objects to be returned by +c astRead. +f AST_READ. +* If more than one of these attributes is set non-zero, then the +* actual Object returned by +c astRead +f AST_READ +* will be a KeyMap containing the requested Objects. In this +* case, the properties KeyMap will be stored in the returned KeyMap +* using the key "PROPS". If StcsProps is the only attribute to be +* set non-zero, then the Object returned by +c astRead +f AST_READ +* will be the properties KeyMap itself. +* - The KeyMap containing the properties will have entries for one or +* more of the following keys: "TIME_PROPS", "SPACE_PROPS", "SPECTRAL_PROPS" +* and "REDSHIFT_PROPS". Each of these entries will be another KeyMap +* containing the properties of the corresponding STC-S sub-phrase. + +* Applicability: +* StcsChan +* All StcsChans have this attribute. +*att-- +*/ + +/* This ia a boolean value (0 or 1) with a value of -INT_MAX when + undefined but yielding a default of zero. */ +astMAKE_CLEAR(StcsChan,StcsProps,stcsprops,-INT_MAX) +astMAKE_GET(StcsChan,StcsProps,int,0,( this->stcsprops != -INT_MAX ? this->stcsprops : 0 )) +astMAKE_SET(StcsChan,StcsProps,int,stcsprops,( value != 0 )) +astMAKE_TEST(StcsChan,StcsProps,( this->stcsprops != -INT_MAX )) + +/* +*att++ +* Name: +* StcsLength + +* Purpose: +* Controls output line length. + +* Type: +* Public attribute. + +* Synopsis: +* Integer. + +* Description: +* This attribute specifies the maximum length to use when writing out +* text through the sink function supplied when the StcsChan was created. +* It is ignored if the Indent attribute is zero (in which case the text +* supplied to the sink function can be of any length). The default value +* is 70. +* +* The number of characters in each string written out through the sink +* function will not usually be greater than the value of this attribute +* (but may be less). However, if any single word in the STC-S +* description exceeds the specified length, then the word will be +* written out as a single line. +* +f Note, the default value of zero is unlikely to be appropriate when +f an StcsChan is used within Fortran code. In this case, StcsLength +f should usually be set to the size of the CHARACTER variable used to +f receive the text returned by AST_GETLINE within the sink function. +f In addition, the Indent attribute should be set non-zero. This +f avoids the possibility of long lines being truncated invisibly +f within AST_GETLINE. + +* Applicability: +* StcsChan +* All StcsChans have this attribute. +*att-- +*/ +astMAKE_CLEAR(StcsChan,StcsLength,stcslength,-INT_MAX) +astMAKE_GET(StcsChan,StcsLength,int,70,( ( this->stcslength != -INT_MAX ) ? this->stcslength : 70 )) +astMAKE_SET(StcsChan,StcsLength,int,stcslength,(value<0?0:value)) +astMAKE_TEST(StcsChan,StcsLength,( this->stcslength != -INT_MAX )) + +/* Copy constructor. */ +/* ----------------- */ + +/* Destructor. */ +/* ----------- */ + +/* Dump function. */ +/* -------------- */ + +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for StcsChan objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the StcsChan class to an output Channel. + +* Parameters: +* this +* Pointer to the Object (an StcsChan) whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstStcsChan *this; /* Pointer to the StcsChan structure */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the StcsChan structure. */ + this = (AstStcsChan *) this_object; + +/* Write out values representing the instance variables for the + StcsChan class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* StcsArea. */ +/* --------- */ + set = TestStcsArea( this, status ); + ival = set ? GetStcsArea( this, status ) : astGetStcsArea( this ); + astWriteInt( channel, "StcsArea", set, 0, ival, + ival ? "Read the STC CoordinatesArea component" : + "Do not read the STC CoordinatesArea component" ); + +/* StcsCoords. */ +/* ----------- */ + set = TestStcsCoords( this, status ); + ival = set ? GetStcsCoords( this, status ) : astGetStcsCoords( this ); + astWriteInt( channel, "StcsCoords", set, 0, ival, + ival ? "Read the STC Coordinates component" : + "Do not read the STC Coordinates component" ); + +/* StcsProps. */ +/* ---------- */ + set = TestStcsProps( this, status ); + ival = set ? GetStcsProps( this, status ) : astGetStcsProps( this ); + astWriteInt( channel, "StcsProps", set, 0, ival, + ival ? "Read the STC-S properties" : + "Do not read the STC-S properties" ); + +/* StcsLength */ +/* ---------- */ + set = TestStcsLength( this, status ); + ival = set ? GetStcsLength( this, status ) : astGetStcsLength( this ); + astWriteInt( channel, "StcsLen", set, 0, ival, "STC-S buffer length" ); + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAStcsChan and astCheckStcsChan functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(StcsChan,Channel) +astMAKE_CHECK(StcsChan) + +AstStcsChan *astStcsChan_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, int *status, ...) { +/* +*++ +* Name: +c astStcsChan +f AST_STCSCHAN + +* Purpose: +* Create an StcsChan. + +* Type: +* Public function. + +* Synopsis: +c #include "stcschan.h" +c AstStcsChan *astStcsChan( const char *(* source)( void ), +c void (* sink)( const char * ), +c const char *options, ... ) +f RESULT = AST_STCSCHAN( SOURCE, SINK, OPTIONS, STATUS ) + +* Class Membership: +* StcsChan constructor. + +* Description: +* This function creates a new StcsChan and optionally initialises +* its attributes. +* +* A StcsChan is a specialised form of Channel which supports STC-S +* I/O operations. Writing an Object to an StcsChan (using +c astWrite) will, if the Object is suitable, generate an +f AST_WRITE) will, if the Object is suitable, generate an +* STC-S description of that Object, and reading from an StcsChan will +* create a new Object from its STC-S description. +* +* Normally, when you use an StcsChan, you should provide "source" +c and "sink" functions which connect it to an external data store +c by reading and writing the resulting text. These functions +f and "sink" routines which connect it to an external data store +f by reading and writing the resulting text. These routines +* should perform any conversions needed between external character +c encodings and the internal ASCII encoding. If no such functions +f encodings and the internal ASCII encoding. If no such routines +* are supplied, a Channel will read from standard input and write +* to standard output. +* +* Alternatively, an XmlChan can be told to read or write from +* specific text files using the SinkFile and SourceFile attributes, +* in which case no sink or source function need be supplied. + +* Parameters: +c source +f SOURCE = SUBROUTINE (Given) +c Pointer to a source function that takes no arguments and +c returns a pointer to a null-terminated string. If no value +c has been set for the SourceFile attribute, this function +c will be used by the StcsChan to obtain lines of input text. On +c each invocation, it should return a pointer to the next input +c line read from some external data store, and a NULL pointer +c when there are no more lines to read. +c +c If "source" is NULL and no value has been set for the SourceFile +c attribute, the StcsChan will read from standard input instead. +f A source routine, which is a subroutine which takes a single +f integer error status argument. If no value has been set +f for the SourceFile attribute, this routine will be used by +f the StcsChan to obtain lines of input text. On each +f invocation, it should read the next input line from some +f external data store, and then return the resulting text to +f the AST library by calling AST_PUTLINE. It should supply a +f negative line length when there are no more lines to read. +f If an error occurs, it should set its own error status +f argument to an error value before returning. +f +f If the null routine AST_NULL is suppied as the SOURCE value, +f and no value has been set for the SourceFile attribute, +f the StcsChan will read from standard input instead. +c sink +f SINK = SUBROUTINE (Given) +c Pointer to a sink function that takes a pointer to a +c null-terminated string as an argument and returns void. +c If no value has been set for the SinkFile attribute, this +c function will be used by the StcsChan to deliver lines of +c output text. On each invocation, it should deliver the +c contents of the string supplied to some external data store. +c +c If "sink" is NULL, and no value has been set for the SinkFile +c attribute, the StcsChan will write to standard output instead. +f A sink routine, which is a subroutine which takes a single +f integer error status argument. If no value has been set +f for the SinkFile attribute, this routine will be used by +f the StcsChan to deliver lines of output text. On each +f invocation, it should obtain the next output line from the +f AST library by calling AST_GETLINE, and then deliver the +f resulting text to some external data store. If an error +f occurs, it should set its own error status argument to an +f error value before returning. +f +f If the null routine AST_NULL is suppied as the SINK value, +f and no value has been set for the SinkFile attribute, +f the StcsChan will write to standard output instead. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new StcsChan. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new StcsChan. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astStcsChan() +f AST_STCSCHAN = INTEGER +* A pointer to the new StcsChan. + +* Notes: +f - The names of the routines supplied for the SOURCE and SINK +f arguments should appear in EXTERNAL statements in the Fortran +f routine which invokes AST_STCSCHAN. However, this is not generally +f necessary for the null routine AST_NULL (so long as the AST_PAR +f include file has been used). +* - If the external data source or sink uses a character encoding +* other than ASCII, the supplied source and sink functions should +* translate between the external character encoding and the internal +* ASCII encoding used by AST. +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the AST error status set, or if it +* should fail for any reason. +f - Note that the null routine AST_NULL (one underscore) is +f different to AST__NULL (two underscores), which is the null Object +f pointer. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcsChan *new; /* Pointer to new StcsChan */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the StcsChan, allocating memory and initialising the + virtual function table as well if necessary. This interface is for + use by other C functions within AST, and uses the standard "wrapper" + functions included in this class. */ + new = astInitStcsChan( NULL, sizeof( AstStcsChan ), !class_init, + &class_vtab, "StcsChan", source, SourceWrap, + sink, SinkWrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + StcsChan's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new StcsChan. */ + return new; +} + +AstStcsChan *astStcsChanId_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, ... ) { +/* +* Name: +* astStcsChanId_ + +* Purpose: +* Create an StcsChan. + +* Type: +* Private function. + +* Synopsis: +* #include "stcschan.h" +* AstStcsChan *astStcsChanId_( const char *(* source)( void ), +* void (* sink)( const char * ), +* const char *options, ... ) + +* Class Membership: +* StcsChan constructor. + +* Description: +* This function implements the external (public) C interface to the +* astStcsChan constructor function. Another function (astStcsChanForId) +* should be called to create an StcsChan for use within other languages. +* Both functions return an ID value (instead of a true C pointer) to +* external users, and must be provided because astStcsChan_ has a variable +* argument list which cannot be encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astStcsChan_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astStcsChan_. + +* Returned Value: +* The ID value associated with the new StcsChan. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcsChan *new; /* Pointer to new StcsChan */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the StcsChan, allocating memory and initialising the + virtual function table as well if necessary. This interface is for + use by external C functions and uses the standard "wrapper" + functions included in this class. */ + new = astInitStcsChan( NULL, sizeof( AstStcsChan ), !class_init, + &class_vtab, "StcsChan", source, SourceWrap, + sink, SinkWrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + StcsChan's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new StcsChan. */ + return astMakeId( new ); +} + +AstStcsChan *astStcsChanForId_( const char *(* source)( void ), + char *(* source_wrap)( const char *(*)( void ), int * ), + void (* sink)( const char * ), + void (* sink_wrap)( void (*)( const char * ), + const char *, int * ), + const char *options, ... ) { +/* +*+ +* Name: +* astStcsChanFor + +* Purpose: +* Initialise an StcsChan from a foreign language interface. + +* Type: +* Public function. + +* Synopsis: +* #include "stcschan.h" +* AstStcsChan *astStcsChanFor( const char *(* source)( void ), +* char *(* source_wrap)( const char *(*) +* ( void ), int * ), +* void (* sink)( const char * ), +* void (* sink_wrap)( void (*)( const char * ), +* const char *, int * ), +* const char *options, ... ) + +* Class Membership: +* StcsChan constructor. + +* Description: +* This function creates a new StcsChan from a foreign language +* interface and optionally initialises its attributes. +* +* A StcsChan is a specialised form of Channel which supports STC-S +* I/O operations. Writing an Object to an StcsChan (using +c astWrite) will, if the Object is suitable, generate an +f AST_WRITE) will, if the Object is suitable, generate an +* STC-S description of that Object, and reading from an StcsChan will +* create a new Object from its STC-S description. +* +* Normally, when you use an StcsChan, you should provide "source" +c and "sink" functions which connect it to an external data store +c by reading and writing the resulting text. These functions +f and "sink" routines which connect it to an external data store +f by reading and writing the resulting text. These routines +* should perform any conversions needed between external character +c encodings and the internal ASCII encoding. If no such functions +f encodings and the internal ASCII encoding. If no such routines +* are supplied, a Channel will read from standard input and write +* to standard output. + +* Parameters: +* source +* Pointer to a "source" function which will be used to obtain +* lines of input text. Generally, this will be obtained by +* casting a pointer to a source function which is compatible +* with the "source_wrap" wrapper function (below). The pointer +* should later be cast back to its original type by the +* "source_wrap" function before the function is invoked. +* +* If "source" is NULL, the StcsChan will read from standard +* input instead. +* source_wrap +* Pointer to a function which can be used to invoke the +* "source" function supplied (above). This wrapper function is +* necessary in order to hide variations in the nature of the +* source function, such as may arise when it is supplied by a +* foreign (non-C) language interface. +* +* The single parameter of the "source_wrap" function is a +* pointer to the "source" function, and it should cast this +* function pointer (as necessary) and invoke the function with +* appropriate arguments to obtain the next line of input +* text. The "source_wrap" function should then return a pointer +* to a dynamically allocated, null terminated string containing +* the text that was read. The string will be freed (using +* astFree) when no longer required and the "source_wrap" +* function need not concern itself with this. A NULL pointer +* should be returned if there is no more input to read. +* +* If "source_wrap" is NULL, the StcsChan will read from standard +* input instead. +* sink +* Pointer to a "sink" function which will be used to deliver +* lines of output text. Generally, this will be obtained by +* casting a pointer to a sink function which is compatible with +* the "sink_wrap" wrapper function (below). The pointer should +* later be cast back to its original type by the "sink_wrap" +* function before the function is invoked. +* +* If "sink" is NULL, the StcsChan will write to standard output +* instead. +* sink_wrap +* Pointer to a function which can be used to invoke the "sink" +* function supplied (above). This wrapper function is necessary +* in order to hide variations in the nature of the sink +* function, such as may arise when it is supplied by a foreign +* (non-C) language interface. +* +* The first parameter of the "sink_wrap" function is a pointer +* to the "sink" function, and the second parameter is a pointer +* to a const, null-terminated character string containing the +* text to be written. The "sink_wrap" function should cast the +* "sink" function pointer (as necessary) and invoke the +* function with appropriate arguments to deliver the line of +* output text. The "sink_wrap" function then returns void. +* +* If "sink_wrap" is NULL, the Channel will write to standard +* output instead. +* options +* Pointer to a null-terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new StcsChan. The syntax used is identical to +* that for the astSet function and may include "printf" format +* specifiers identified by "%" symbols in the normal way. +* ... +* If the "options" string contains "%" format specifiers, then +* an optional list of additional arguments may follow it in +* order to supply values to be substituted for these +* specifiers. The rules for supplying these are identical to +* those for the astSet function (and for the C "printf" +* function). + +* Returned Value: +* astStcsChanFor() +* A pointer to the new StcsChan. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +* - This function is only available through the public interface +* to the StcsChan class (not the protected interface) and is +* intended solely for use in implementing foreign language +* interfaces to this class. +*- + +* Implememtation Notes: +* - This function behaves exactly like astStcsChanId_, in that it +* returns ID values and not true C pointers, but it has two +* additional arguments. These are pointers to the "wrapper +* functions" which are needed to accommodate foreign language +* interfaces. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcsChan *new; /* Pointer to new StcsChan */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise the StcsChan, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcsChan( NULL, sizeof( AstStcsChan ), !class_init, + &class_vtab, "StcsChan", source, source_wrap, + sink, sink_wrap ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + StcsChan's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new StcsChan. */ + return astMakeId( new ); +} + +AstStcsChan *astInitStcsChan_( void *mem, size_t size, int init, + AstStcsChanVtab *vtab, const char *name, + const char *(* source)( void ), + char *(* source_wrap)( const char *(*)( void ), int * ), + void (* sink)( const char * ), + void (* sink_wrap)( void (*)( const char * ), + const char *, int * ), int *status ) { +/* +*+ +* Name: +* astInitStcsChan + +* Purpose: +* Initialise an StcsChan. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcschan.h" +* AstStcsChan *astInitStcsChan( void *mem, size_t size, int init, +* AstStcsChanVtab *vtab, const char *name, +* const char *(* source)( void ), +* char *(* source_wrap)( const char *(*)( void ), int * ), +* void (* sink)( const char * ), +* void (* sink_wrap)( void (*)( const char * ), +* const char *, int * ) ) + +* Class Membership: +* StcsChan initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new StcsChan object. It allocates memory (if +* necessary) to accommodate the StcsChan plus any additional data +* associated with the derived class. It then initialises a +* StcsChan structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual +* function table for an StcsChan at the start of the memory passed +* via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the StcsChan is to be +* initialised. This must be of sufficient size to accommodate +* the StcsChan data (sizeof(StcsChan)) plus any data used by the +* derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the StcsChan (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the StcsChan structure, so a valid value must be +* supplied even if not required for allocating memory. +* init +* A boolean flag indicating if the StcsChan's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new StcsChan. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* source +* Pointer to a "source" function which will be used to obtain +* lines of text. Generally, this will be obtained by +* casting a pointer to a source function which is compatible +* with the "source_wrap" wrapper function (below). The pointer +* should later be cast back to its original type by the +* "source_wrap" function before the function is invoked. +* +* If "source" is NULL, the Channel will read from standard +* input instead. +* source_wrap +* Pointer to a function which can be used to invoke the +* "source" function supplied (above). This wrapper function is +* necessary in order to hide variations in the nature of the +* source function, such as may arise when it is supplied by a +* foreign (non-C) language interface. +* +* The single parameter of the "source_wrap" function is a +* pointer to the "source" function, and it should cast this +* function pointer (as necessary) and invoke the function with +* appropriate arguments to obtain the next line of input +* text. The "source_wrap" function should then return a pointer +* to a dynamically allocated, null terminated string containing +* the text that was read. The string will be freed (using +* astFree) when no longer required and the "source_wrap" +* function need not concern itself with this. A NULL pointer +* should be returned if there is no more input to read. +* +* If "source_wrap" is NULL, the Channel will read from standard +* input instead. +* sink +* Pointer to a "sink" function which will be used to deliver +* lines of text. Generally, this will be obtained by +* casting a pointer to a sink function which is compatible with +* the "sink_wrap" wrapper function (below). The pointer should +* later be cast back to its original type by the "sink_wrap" +* function before the function is invoked. +* +* If "sink" is NULL, the contents of the StcsChan will not be +* written out before being deleted. +* sink_wrap +* Pointer to a function which can be used to invoke the "sink" +* function supplied (above). This wrapper function is necessary +* in order to hide variations in the nature of the sink +* function, such as may arise when it is supplied by a foreign +* (non-C) language interface. +* +* The first parameter of the "sink_wrap" function is a pointer +* to the "sink" function, and the second parameter is a pointer +* to a const, null-terminated character string containing the +* text to be written. The "sink_wrap" function should cast the +* "sink" function pointer (as necessary) and invoke the +* function with appropriate arguments to deliver the line of +* output text. The "sink_wrap" function then returns void. +* +* If "sink_wrap" is NULL, the Channel will write to standard +* output instead. + +* Returned Value: +* A pointer to the new StcsChan. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstStcsChan *new; /* Pointer to new StcsChan */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitStcsChanVtab( vtab, name ); + +/* Initialise a Channel structure (the parent class) as the first + component within the StcsChan structure, allocating memory if + necessary. */ + new = (AstStcsChan *) astInitChannel( mem, size, 0, + (AstChannelVtab *) vtab, name, + source, source_wrap, sink, + sink_wrap ); + + if ( astOK ) { + +/* Initialise the StcsChan data. */ +/* ---------------------------- */ + new->stcsarea = -INT_MAX; + new->stcscoords = -INT_MAX; + new->stcsprops = -INT_MAX; + new->stcslength = -INT_MAX; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstStcsChan *astLoadStcsChan_( void *mem, size_t size, + AstStcsChanVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadStcsChan + +* Purpose: +* Load an StcsChan. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcschan.h" +* AstStcsChan *astLoadStcsChan( void *mem, size_t size, +* AstStcsChanVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* StcsChan loader. + +* Description: +* This function is provided to load a new StcsChan using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* StcsChan structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for an StcsChan at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the StcsChan is to be +* loaded. This must be of sufficient size to accommodate the +* StcsChan data (sizeof(StcsChan)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the StcsChan (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the StcsChan structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstStcsChan) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new StcsChan. If this is NULL, a pointer +* to the (static) virtual function table for the StcsChan class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "StcsChan" is used instead. + +* Returned Value: +* A pointer to the new StcsChan. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcsChan *new; /* Pointer to the new StcsChan */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this StcsChan. In this case the + StcsChan belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstStcsChan ); + vtab = &class_vtab; + name = "StcsChan"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitStcsChanVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built StcsChan. */ + new = astLoadChannel( mem, size, (AstChannelVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "StcsChan" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* StcsArea. */ +/* --------- */ + new->stcsarea = astReadInt( channel, "stcsarea", -INT_MAX ); + if ( TestStcsArea( new, status ) ) SetStcsArea( new, new->stcsarea, status ); + +/* StcsCoords. */ +/* ----------- */ + new->stcscoords = astReadInt( channel, "stcscoords", -INT_MAX ); + if ( TestStcsCoords( new, status ) ) SetStcsCoords( new, new->stcscoords, status ); + +/* StcsProps. */ +/* ---------- */ + new->stcsprops = astReadInt( channel, "stcsprops", -INT_MAX ); + if ( TestStcsProps( new, status ) ) SetStcsProps( new, new->stcsprops, status ); + +/* StcsLength */ +/* ---------- */ + new->stcslength = astReadInt( channel, "stcslen", -INT_MAX ); + + } + +/* If an error occurred, clean up by deleting the new StcsChan. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the new StcsChan pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + + + + diff --git a/stcschan.h b/stcschan.h new file mode 100644 index 0000000..11de827 --- /dev/null +++ b/stcschan.h @@ -0,0 +1,308 @@ +#if !defined( STCSCHAN_INCLUDED ) /* Include this file only once */ +#define STCSCHAN_INCLUDED +/* +*+ +* Name: +* stcschan.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the StcsChan class. + +* Invocation: +* #include "stcschan.h" + +* Description: +* This include file defines the interface to the StcsChan class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The StcsChan class provides facilities for reading and writing AST +* Objects in the form of STC-S text. + +* Inheritance: +* The StcsChan class inherits from the Channel class. + +* Copyright: +* Copyright (C) 2008 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (JAC, UCLan) + +* History: +* 18-DEC-2008 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "channel.h" /* I/O channels (parent class) */ + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ------- */ + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define constants used to size global arrays in this module. */ +/* Define other numerical constants for use in this module. */ +#define AST__STCSCHAN_GETATTRIB_BUFF_LEN 200 + +/* Type Definitions. */ +/* ================= */ + +/* StcsChan structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstStcsChan { + +/* Attributes inherited from the parent class. */ + AstChannel channel; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int stcsarea; /* Read the STC CoordinatesArea? */ + int stcscoords; /* Read the STC Coordinates? */ + int stcsprops; /* Read the STC-S properties? */ + int stcslength; /* Line length */ +} AstStcsChan; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstStcsChanVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstChannelVtab channel_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* GetStcsArea)( AstStcsChan *, int * ); + int (* TestStcsArea)( AstStcsChan *, int * ); + void (* ClearStcsArea)( AstStcsChan *, int * ); + void (* SetStcsArea)( AstStcsChan *, int, int * ); + + int (* GetStcsCoords)( AstStcsChan *, int * ); + int (* TestStcsCoords)( AstStcsChan *, int * ); + void (* ClearStcsCoords)( AstStcsChan *, int * ); + void (* SetStcsCoords)( AstStcsChan *, int, int * ); + + int (* GetStcsProps)( AstStcsChan *, int * ); + int (* TestStcsProps)( AstStcsChan *, int * ); + void (* ClearStcsProps)( AstStcsChan *, int * ); + void (* SetStcsProps)( AstStcsChan *, int, int * ); + + int (* GetStcsLength)( AstStcsChan *, int * ); + int (* TestStcsLength)( AstStcsChan *, int * ); + void (* ClearStcsLength)( AstStcsChan *, int * ); + void (* SetStcsLength)( AstStcsChan *, int, int * ); + +} AstStcsChanVtab; + +#if defined(THREAD_SAFE) +typedef struct AstStcsChanGlobals { + AstStcsChanVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ AST__STCSCHAN_GETATTRIB_BUFF_LEN + 1 ]; +} AstStcsChanGlobals; + +#endif +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(StcsChan) /* Check class membership */ +astPROTO_ISA(StcsChan) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstStcsChan *astStcsChan_( const char *(*)( void ), void (*)( const char * ), + const char *, int *, ...); +#else +AstStcsChan *astStcsChanId_( const char *(*)( void ), void (*)( const char * ), + const char *, ... ); +AstStcsChan *astStcsChanForId_( const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), + const char *, int * ), + const char *, ... ); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstStcsChan *astInitStcsChan_( void *, size_t, int, AstStcsChanVtab *, + const char *, const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), + const char *, int * ), int * ); + +/* Vtab initialiser. */ +void astInitStcsChanVtab_( AstStcsChanVtab *, const char *, int * ); + + + +/* Loader. */ +AstStcsChan *astLoadStcsChan_( void *, size_t, AstStcsChanVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitStcsChanGlobals_( AstStcsChanGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +# if defined(astCLASS) /* Protected */ +int astGetStcsArea_( AstStcsChan *, int * ); +int astTestStcsArea_( AstStcsChan *, int * ); +void astClearStcsArea_( AstStcsChan *, int * ); +void astSetStcsArea_( AstStcsChan *, int, int * ); + +int astGetStcsCoords_( AstStcsChan *, int * ); +int astTestStcsCoords_( AstStcsChan *, int * ); +void astClearStcsCoords_( AstStcsChan *, int * ); +void astSetStcsCoords_( AstStcsChan *, int, int * ); + +int astGetStcsProps_( AstStcsChan *, int * ); +int astTestStcsProps_( AstStcsChan *, int * ); +void astClearStcsProps_( AstStcsChan *, int * ); +void astSetStcsProps_( AstStcsChan *, int, int * ); + +int astGetStcsLength_( AstStcsChan *, int * ); +int astTestStcsLength_( AstStcsChan *, int * ); +void astClearStcsLength_( AstStcsChan *, int * ); +void astSetStcsLength_( AstStcsChan *, int, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckStcsChan(this) astINVOKE_CHECK(StcsChan,this,0) +#define astVerifyStcsChan(this) astINVOKE_CHECK(StcsChan,this,1) + +/* Test class membership. */ +#define astIsAStcsChan(this) astINVOKE_ISA(StcsChan,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astStcsChan astINVOKE(F,astStcsChan_) +#else +#define astStcsChan astINVOKE(F,astStcsChanId_) +#define astStcsChanFor astINVOKE(F,astStcsChanForId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitStcsChan(mem,size,init,vtab,name,source,source_wrap,sink,sink_wrap) \ +astINVOKE(O,astInitStcsChan_(mem,size,init,vtab,name,source,source_wrap,sink,sink_wrap,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitStcsChanVtab(vtab,name) astINVOKE(V,astInitStcsChanVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadStcsChan(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadStcsChan_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to member functions. */ +/* ------------------------------- */ +/* Here we make use of astCheckStcsChan to validate StcsChan pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + + +#if defined(astCLASS) /* Protected */ + +#define astClearStcsArea(this) \ +astINVOKE(V,astClearStcsArea_(astCheckStcsChan(this),STATUS_PTR)) +#define astGetStcsArea(this) \ +astINVOKE(V,astGetStcsArea_(astCheckStcsChan(this),STATUS_PTR)) +#define astSetStcsArea(this,value) \ +astINVOKE(V,astSetStcsArea_(astCheckStcsChan(this),value,STATUS_PTR)) +#define astTestStcsArea(this) \ +astINVOKE(V,astTestStcsArea_(astCheckStcsChan(this),STATUS_PTR)) + +#define astClearStcsCoords(this) \ +astINVOKE(V,astClearStcsCoords_(astCheckStcsChan(this),STATUS_PTR)) +#define astGetStcsCoords(this) \ +astINVOKE(V,astGetStcsCoords_(astCheckStcsChan(this),STATUS_PTR)) +#define astSetStcsCoords(this,value) \ +astINVOKE(V,astSetStcsCoords_(astCheckStcsChan(this),value,STATUS_PTR)) +#define astTestStcsCoords(this) \ +astINVOKE(V,astTestStcsCoords_(astCheckStcsChan(this),STATUS_PTR)) + +#define astClearStcsProps(this) \ +astINVOKE(V,astClearStcsProps_(astCheckStcsChan(this),STATUS_PTR)) +#define astGetStcsProps(this) \ +astINVOKE(V,astGetStcsProps_(astCheckStcsChan(this),STATUS_PTR)) +#define astSetStcsProps(this,value) \ +astINVOKE(V,astSetStcsProps_(astCheckStcsChan(this),value,STATUS_PTR)) +#define astTestStcsProps(this) \ +astINVOKE(V,astTestStcsProps_(astCheckStcsChan(this),STATUS_PTR)) + +#define astClearStcsLength(this) astINVOKE(V,astClearStcsLength_(astCheckStcsChan(this),STATUS_PTR)) +#define astGetStcsLength(this) astINVOKE(V,astGetStcsLength_(astCheckStcsChan(this),STATUS_PTR)) +#define astSetStcsLength(this,stcslength) astINVOKE(V,astSetStcsLength_(astCheckStcsChan(this),stcslength,STATUS_PTR)) +#define astTestStcsLength(this) astINVOKE(V,astTestStcsLength_(astCheckStcsChan(this),STATUS_PTR)) + +#endif +#endif + + + + diff --git a/stcsearchlocation.c b/stcsearchlocation.c new file mode 100644 index 0000000..aa6bd4e --- /dev/null +++ b/stcsearchlocation.c @@ -0,0 +1,806 @@ +/* +*class++ +* Name: +* StcSearchLocation + +* Purpose: +* Correspond to the IVOA SearchLocation class. + +* Constructor Function: +c astStcSearchLocation +f AST_STCSEARCHLOCATION + +* Description: +* The StcSearchLocation class is a sub-class of Stc used to describe +* the coverage of a query. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The StcSearchLocation class inherits from the Stc class. + +* Attributes: +* The StcSearchLocation class does not define any new attributes beyond +* those which are applicable to all Stcs. + +* Functions: +c The StcSearchLocation class does not define any new functions beyond those +f The StcSearchLocation class does not define any new routines beyond those +* which are applicable to all Stcs. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-NOV-2004 (DSB): +* Original version. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS StcSearchLocation + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "stc.h" /* Coordinate stcs (parent class) */ +#include "channel.h" /* I/O channels */ +#include "region.h" /* Regions within coordinate systems */ +#include "stcsearchlocation.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(StcSearchLocation) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(StcSearchLocation,Class_Init) +#define class_vtab astGLOBAL(StcSearchLocation,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstStcSearchLocationVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstStcSearchLocation *astStcSearchLocationId_( void *, int, AstKeyMap **, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static void Dump( AstObject *, AstChannel *, int * ); + +/* Member functions. */ +/* ================= */ + +void astInitStcSearchLocationVtab_( AstStcSearchLocationVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitStcSearchLocationVtab + +* Purpose: +* Initialise a virtual function table for a StcSearchLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcsearchlocation.h" +* void astInitStcSearchLocationVtab( AstStcSearchLocationVtab *vtab, const char *name ) + +* Class Membership: +* StcSearchLocation vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the StcSearchLocation class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstStcVtab *stc; /* Pointer to Stc component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitStcVtab( (AstStcVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAStcSearchLocation) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstStcVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + mapping = (AstMappingVtab *) vtab; + stc = (AstStcVtab *) vtab; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + +/* Declare the copy constructor, destructor and class dump + functions. */ + astSetDump( vtab, Dump, "StcSearchLocation", "Query coverage" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +/* None */ + +/* Destructor. */ +/* ----------- */ +/* None */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for StcSearchLocation objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the StcSearchLocation class to an output Channel. + +* Parameters: +* this +* Pointer to the StcSearchLocation whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstStcSearchLocation *this; /* Pointer to the StcSearchLocation structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the StcSearchLocation structure. */ + this = (AstStcSearchLocation *) this_object; + +/* Write out values representing the instance variables for the + StcSearchLocation class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAStcSearchLocation and astCheckStcSearchLocation functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(StcSearchLocation,Stc) +astMAKE_CHECK(StcSearchLocation) + +AstStcSearchLocation *astStcSearchLocation_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, int *status, ...) { +/* +*++ +* Name: +c astStcSearchLocation +f AST_STCSEARCHLOCATION + +* Purpose: +* Create a StcSearchLocation. + +* Type: +* Public function. + +* Synopsis: +c #include "stcsearchlocation.h" +c AstStcResourceProfile *astStcSearchLocation( AstRegion *region, +c int ncoords, AstKeyMap *coords[], const char *options, ... ) +f RESULT = AST_STCSEARCHLOCATION( REGION, NCOORDS, COORDS, OPTIONS, STATUS ) + +* Class Membership: +* StcSearchLocation constructor. + +* Description: +* This function creates a new StcSearchLocation and optionally initialises its +* attributes. +* +* The StcSearchLocation class is a sub-class of Stc used to describe +* the coverage of a VO query. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Parameters: +c region +f REGION = INTEGER (Given) +* Pointer to the encapsulated Region. +c ncoords +f NCOORDS = INTEGER (Given) +c The length of the "coords" array. Supply zero if "coords" is NULL. +f The length of the COORDS array. Supply zero if COORDS should be +f ignored. +c coords +f COORDS( NCOORDS ) = INTEGER (Given) +c Pointer to an array holding "ncoords" AstKeyMap pointers (if "ncoords" +f An array holding NCOORDS AstKeyMap pointers (if NCOORDS +* is zero, the supplied value is ignored). Each supplied KeyMap +* describes the contents of a single STC element, and +* should have elements with keys given by constants AST__STCNAME, +* AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. Any of these elements may be omitted, but no other +* elements should be included. If supplied, the AST__STCNAME element +* should be a vector of character string pointers holding the "Name" +* item for each axis in the coordinate system represented by +c "region". +f REGION. +* Any other supplied elements should be scalar elements, each holding +* a pointer to a Region describing the associated item of ancillary +* information (error, resolution, size, pixel size or value). These +* Regions should describe a volume within the coordinate system +c represented by "region". +f represented by REGION. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new StcSearchLocation. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new StcSearchLocation. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astStcSearchLocation() +f AST_STCSEARCHLOCATION = INTEGER +* A pointer to the new StcSearchLocation. + +* Notes: +* - A deep copy is taken of the supplied Region. This means that +* any subsequent changes made to the encapsulated Region using the +* supplied pointer will have no effect on the Stc. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstRegion *region; /* Pointer to Region structure */ + AstStcSearchLocation *new; /* Pointer to new StcSearchLocation */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain and validate a pointer to the Region structure provided. */ + region = astCheckRegion( region_void ); + +/* Initialise the StcSearchLocation, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcSearchLocation( NULL, sizeof( AstStcSearchLocation ), !class_init, + &class_vtab, "StcSearchLocation", region, + ncoords, coords ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcSearchLocation's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new StcSearchLocation. */ + return new; +} + +AstStcSearchLocation *astStcSearchLocationId_( void *region_void, int ncoords, + AstKeyMap **coords, const char *options, ... ) { +/* +* Name: +* astStcSearchLocationId_ + +* Purpose: +* Create a StcSearchLocation. + +* Type: +* Private function. + +* Synopsis: +* #include "stcsearchlocation.h" +* AstStcSearchLocation *astStcSearchLocationId_( AstRegion *region, +* int ncoords, AstKeyMap *coords[], const char *options, ... ) + +* Class Membership: +* StcSearchLocation constructor. + +* Description: +* This function implements the external (public) interface to the +* astStcSearchLocation constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astStcSearchLocation_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astStcSearchLocation_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astStcSearchLocation_. + +* Returned Value: +* The ID value associated with the new StcSearchLocation. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstKeyMap **keymaps; /* Pointer to array of KeyMap pointers */ + AstRegion *region; /* Pointer to Region structure */ + AstStcSearchLocation *new; /* Pointer to new StcSearchLocation */ + int icoord; /* Keymap index */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + + /* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Obtain a Region pointer from the supplied ID and validate the + pointer to ensure it identifies a valid Region. */ + region = astVerifyRegion( astMakePointer( region_void ) ); + +/* Obtain pointer from the supplied KeyMap ID's and validate the + pointers to ensure it identifies a valid KeyMap. */ + keymaps = astMalloc( sizeof( AstKeyMap * )*(size_t) ncoords ); + if( keymaps ) { + for( icoord = 0; icoord < ncoords; icoord++ ) { + keymaps[ icoord ] = astVerifyKeyMap( astMakePointer( coords[ icoord ] ) ); + } + } + +/* Initialise the StcSearchLocation, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitStcSearchLocation( NULL, sizeof( AstStcSearchLocation ), !class_init, + &class_vtab, "StcSearchLocation", region, + ncoords, keymaps ); + +/* Free resources. */ + keymaps = astFree( keymaps ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new StcSearchLocation's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new StcSearchLocation. */ + return astMakeId( new ); +} + +AstStcSearchLocation *astInitStcSearchLocation_( void *mem, size_t size, + int init, AstStcSearchLocationVtab *vtab, + const char *name, AstRegion *region, + int ncoords, AstKeyMap **coords, int *status ) { +/* +*+ +* Name: +* astInitStcSearchLocation + +* Purpose: +* Initialise a StcSearchLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcsearchlocation.h" +* AstStcSearchLocation *astInitStcSearchLocation_( void *mem, size_t size, +* int init, AstStcSearchLocationVtab *vtab, +* const char *name, AstRegion *region, +* int ncoords, AstKeyMap **coords ) + +* Class Membership: +* StcSearchLocation initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new StcSearchLocation object. It allocates memory (if necessary) to accommodate +* the StcSearchLocation plus any additional data associated with the derived class. +* It then initialises a StcSearchLocation structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a StcSearchLocation at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the StcSearchLocation is to be initialised. +* This must be of sufficient size to accommodate the StcSearchLocation data +* (sizeof(StcSearchLocation)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the StcSearchLocation (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the StcSearchLocation +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the StcSearchLocation's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new StcSearchLocation. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* region +* A pointer to the Region encapsulated by the StcSearchLocation. +* ncoords +* Number of KeyMap pointers supplied in "coords". Can be zero. +* Ignored if "coords" is NULL. +* coords +* Pointer to an array of "ncoords" KeyMap pointers, or NULL if +* "ncoords" is zero. Each KeyMap defines defines a single +* element, and should have elements with keys given by constants +* AST__STCNAME, AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE, +* AST__STCPIXSZ. These elements hold values for the corresponding +* components of the STC AstroCoords element. Any of these elements may +* be omitted, but no other elements should be included. All supplied +* elements should be vector elements, with vector length less than or +* equal to the number of axes in the supplied Region. The data type of +* all elements should be "double", except for AST__STCNAME which should +* be "character string". If no value is available for a given axis, then +* AST__BAD (or NULL for the AST__STCNAME element) should be stored in +* the vector at the index corresponding to the axis (trailing axes +* can be omitted completely from the KeyMap). + +* Returned Value: +* A pointer to the new StcSearchLocation. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstStcSearchLocation *new; /* Pointer to new StcSearchLocation */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitStcSearchLocationVtab( vtab, name ); + +/* Initialise a Stc structure (the parent class) as the first component + within the StcSearchLocation structure, allocating memory if necessary. */ + new = (AstStcSearchLocation *) astInitStc( mem, size, 0, (AstStcVtab *) vtab, + name, region, ncoords, coords ); + +/* If an error occurred, clean up by deleting the new StcSearchLocation. */ + if ( !astOK ) new = astDelete( new ); + +/* Return a pointer to the new StcSearchLocation. */ + return new; +} + +AstStcSearchLocation *astLoadStcSearchLocation_( void *mem, size_t size, AstStcSearchLocationVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadStcSearchLocation + +* Purpose: +* Load a StcSearchLocation. + +* Type: +* Protected function. + +* Synopsis: +* #include "stcsearchlocation.h" +* AstStcSearchLocation *astLoadStcSearchLocation( void *mem, size_t size, AstStcSearchLocationVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* StcSearchLocation loader. + +* Description: +* This function is provided to load a new StcSearchLocation using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* StcSearchLocation structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a StcSearchLocation at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the StcSearchLocation is to be +* loaded. This must be of sufficient size to accommodate the +* StcSearchLocation data (sizeof(StcSearchLocation)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the StcSearchLocation (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the StcSearchLocation structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstStcSearchLocation) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new StcSearchLocation. If this is NULL, a pointer +* to the (static) virtual function table for the StcSearchLocation class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "StcSearchLocation" is used instead. + +* Returned Value: +* A pointer to the new StcSearchLocation. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstStcSearchLocation *new; /* Pointer to the new StcSearchLocation */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this StcSearchLocation. In this case the + StcSearchLocation belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstStcSearchLocation ); + vtab = &class_vtab; + name = "StcSearchLocation"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitStcSearchLocationVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built StcSearchLocation. */ + new = astLoadStc( mem, size, (AstStcVtab *) vtab, name, channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "StcSearchLocation" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* If an error occurred, clean up by deleting the new StcSearchLocation. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new StcSearchLocation pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + + + + + + diff --git a/stcsearchlocation.h b/stcsearchlocation.h new file mode 100644 index 0000000..c359711 --- /dev/null +++ b/stcsearchlocation.h @@ -0,0 +1,222 @@ +#if !defined( STCSEARCHLOCATION_INCLUDED ) /* Include this file only once */ +#define STCSEARCHLOCATION_INCLUDED +/* +*+ +* Name: +* stcsearchlocation.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the StcSearchLocation class. + +* Invocation: +* #include "stcsearchlocation.h" + +* Description: +* This include file defines the interface to the StcSearchLocation class +* and provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The StcSearchLocation class is a sub-class of Stc used to describe +* the coverage of a VO query. +* +* See http://hea-www.harvard.edu/~arots/nvometa/STC.html + +* Inheritance: +* The StcSearchLocation class inherits from the Stc class. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 26-NOV-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "stc.h" /* Coordinate stcs (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* StcSearchLocation structure. */ +/* ----------------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstStcSearchLocation { + +/* Attributes inherited from the parent class. */ + AstStc stc; /* Parent class structure */ + +} AstStcSearchLocation; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstStcSearchLocationVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstStcVtab stc_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +} AstStcSearchLocationVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstStcSearchLocationGlobals { + AstStcSearchLocationVtab Class_Vtab; + int Class_Init; +} AstStcSearchLocationGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitStcSearchLocationGlobals_( AstStcSearchLocationGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(StcSearchLocation) /* Check class membership */ +astPROTO_ISA(StcSearchLocation) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstStcSearchLocation *astStcSearchLocation_( void *, int, AstKeyMap **, const char *, int *, ...); +#else +AstStcSearchLocation *astStcSearchLocationId_( void *, int, AstKeyMap **, const char *, ... )__attribute__((format(printf,4,5))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstStcSearchLocation *astInitStcSearchLocation_( void *, size_t, int, AstStcSearchLocationVtab *, const char *, AstRegion *, int, AstKeyMap **, int * ); + +/* Vtab initialiser. */ +void astInitStcSearchLocationVtab_( AstStcSearchLocationVtab *, const char *, int * ); + +/* Loader. */ +AstStcSearchLocation *astLoadStcSearchLocation_( void *, size_t, AstStcSearchLocationVtab *, const char *, AstChannel *, int * ); + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckStcSearchLocation(this) astINVOKE_CHECK(StcSearchLocation,this,0) +#define astVerifyStcSearchLocation(this) astINVOKE_CHECK(StcSearchLocation,this,1) + +/* Test class membership. */ +#define astIsAStcSearchLocation(this) astINVOKE_ISA(StcSearchLocation,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astStcSearchLocation astINVOKE(F,astStcSearchLocation_) +#else +#define astStcSearchLocation astINVOKE(F,astStcSearchLocationId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitStcSearchLocation(mem,size,init,vtab,name,region,ncoords,coords) \ +astINVOKE(O,astInitStcSearchLocation_(mem,size,init,vtab,name,region,ncoords,coords,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitStcSearchLocationVtab(vtab,name) astINVOKE(V,astInitStcSearchLocationVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadStcSearchLocation(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadStcSearchLocation_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckStcSearchLocation to validate StcSearchLocation pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#endif +#endif + + + + + diff --git a/sun210_figures/cmpframe.pdf b/sun210_figures/cmpframe.pdf new file mode 100644 index 0000000..fd82293 Binary files /dev/null and b/sun210_figures/cmpframe.pdf differ diff --git a/sun210_figures/complex.pdf b/sun210_figures/complex.pdf new file mode 100644 index 0000000..b743453 Binary files /dev/null and b/sun210_figures/complex.pdf differ diff --git a/sun210_figures/frames.pdf b/sun210_figures/frames.pdf new file mode 100644 index 0000000..66d2b2f Binary files /dev/null and b/sun210_figures/frames.pdf differ diff --git a/sun210_figures/frameset.pdf b/sun210_figures/frameset.pdf new file mode 100644 index 0000000..d5d9a1a Binary files /dev/null and b/sun210_figures/frameset.pdf differ diff --git a/sun210_figures/fronta.pdf b/sun210_figures/fronta.pdf new file mode 100644 index 0000000..3433a3d Binary files /dev/null and b/sun210_figures/fronta.pdf differ diff --git a/sun210_figures/fronta_bw.pdf b/sun210_figures/fronta_bw.pdf new file mode 100644 index 0000000..2a5ff7e Binary files /dev/null and b/sun210_figures/fronta_bw.pdf differ diff --git a/sun210_figures/frontb.pdf b/sun210_figures/frontb.pdf new file mode 100644 index 0000000..16aeef3 Binary files /dev/null and b/sun210_figures/frontb.pdf differ diff --git a/sun210_figures/frontb_bw.pdf b/sun210_figures/frontb_bw.pdf new file mode 100644 index 0000000..78fa1db Binary files /dev/null and b/sun210_figures/frontb_bw.pdf differ diff --git a/sun210_figures/frontc.pdf b/sun210_figures/frontc.pdf new file mode 100644 index 0000000..69ab837 Binary files /dev/null and b/sun210_figures/frontc.pdf differ diff --git a/sun210_figures/frontc_bw.pdf b/sun210_figures/frontc_bw.pdf new file mode 100644 index 0000000..17fce95 Binary files /dev/null and b/sun210_figures/frontc_bw.pdf differ diff --git a/sun210_figures/fsalign.pdf b/sun210_figures/fsalign.pdf new file mode 100644 index 0000000..7d28fb7 Binary files /dev/null and b/sun210_figures/fsalign.pdf differ diff --git a/sun210_figures/fsconvert.pdf b/sun210_figures/fsconvert.pdf new file mode 100644 index 0000000..5b07041 Binary files /dev/null and b/sun210_figures/fsconvert.pdf differ diff --git a/sun210_figures/fsexample.pdf b/sun210_figures/fsexample.pdf new file mode 100644 index 0000000..1ba073a Binary files /dev/null and b/sun210_figures/fsexample.pdf differ diff --git a/sun210_figures/fsmerge.pdf b/sun210_figures/fsmerge.pdf new file mode 100644 index 0000000..2098b15 Binary files /dev/null and b/sun210_figures/fsmerge.pdf differ diff --git a/sun210_figures/fsremap.pdf b/sun210_figures/fsremap.pdf new file mode 100644 index 0000000..2967186 Binary files /dev/null and b/sun210_figures/fsremap.pdf differ diff --git a/sun210_figures/gridplot.pdf b/sun210_figures/gridplot.pdf new file mode 100644 index 0000000..65953d1 Binary files /dev/null and b/sun210_figures/gridplot.pdf differ diff --git a/sun210_figures/gridplot_bw.pdf b/sun210_figures/gridplot_bw.pdf new file mode 100644 index 0000000..67353bc Binary files /dev/null and b/sun210_figures/gridplot_bw.pdf differ diff --git a/sun210_figures/mapping.pdf b/sun210_figures/mapping.pdf new file mode 100644 index 0000000..78c12b7 Binary files /dev/null and b/sun210_figures/mapping.pdf differ diff --git a/sun210_figures/overgrid.pdf b/sun210_figures/overgrid.pdf new file mode 100644 index 0000000..82c65cf Binary files /dev/null and b/sun210_figures/overgrid.pdf differ diff --git a/sun210_figures/overgrid_bw.pdf b/sun210_figures/overgrid_bw.pdf new file mode 100644 index 0000000..10d65ab Binary files /dev/null and b/sun210_figures/overgrid_bw.pdf differ diff --git a/sun210_figures/parallel.pdf b/sun210_figures/parallel.pdf new file mode 100644 index 0000000..42ff646 Binary files /dev/null and b/sun210_figures/parallel.pdf differ diff --git a/sun210_figures/series.pdf b/sun210_figures/series.pdf new file mode 100644 index 0000000..df2a23b Binary files /dev/null and b/sun210_figures/series.pdf differ diff --git a/sun210_figures/simpexamp.pdf b/sun210_figures/simpexamp.pdf new file mode 100644 index 0000000..d63a5af Binary files /dev/null and b/sun210_figures/simpexamp.pdf differ diff --git a/sun211_figures/cmpframe.pdf b/sun211_figures/cmpframe.pdf new file mode 100644 index 0000000..63ca4e1 Binary files /dev/null and b/sun211_figures/cmpframe.pdf differ diff --git a/sun211_figures/complex.pdf b/sun211_figures/complex.pdf new file mode 100644 index 0000000..8e5bbb9 Binary files /dev/null and b/sun211_figures/complex.pdf differ diff --git a/sun211_figures/frames.pdf b/sun211_figures/frames.pdf new file mode 100644 index 0000000..dbf5070 Binary files /dev/null and b/sun211_figures/frames.pdf differ diff --git a/sun211_figures/frameset.pdf b/sun211_figures/frameset.pdf new file mode 100644 index 0000000..697fe38 Binary files /dev/null and b/sun211_figures/frameset.pdf differ diff --git a/sun211_figures/fronta.pdf b/sun211_figures/fronta.pdf new file mode 100644 index 0000000..3433a3d Binary files /dev/null and b/sun211_figures/fronta.pdf differ diff --git a/sun211_figures/fronta_bw.pdf b/sun211_figures/fronta_bw.pdf new file mode 100644 index 0000000..54cf906 Binary files /dev/null and b/sun211_figures/fronta_bw.pdf differ diff --git a/sun211_figures/frontb.pdf b/sun211_figures/frontb.pdf new file mode 100644 index 0000000..16aeef3 Binary files /dev/null and b/sun211_figures/frontb.pdf differ diff --git a/sun211_figures/frontb_bw.pdf b/sun211_figures/frontb_bw.pdf new file mode 100644 index 0000000..03df1c2 Binary files /dev/null and b/sun211_figures/frontb_bw.pdf differ diff --git a/sun211_figures/frontc.pdf b/sun211_figures/frontc.pdf new file mode 100644 index 0000000..69ab837 Binary files /dev/null and b/sun211_figures/frontc.pdf differ diff --git a/sun211_figures/frontc_bw.pdf b/sun211_figures/frontc_bw.pdf new file mode 100644 index 0000000..29fa5e1 Binary files /dev/null and b/sun211_figures/frontc_bw.pdf differ diff --git a/sun211_figures/fsalign.pdf b/sun211_figures/fsalign.pdf new file mode 100644 index 0000000..e0b8c15 Binary files /dev/null and b/sun211_figures/fsalign.pdf differ diff --git a/sun211_figures/fsconvert.pdf b/sun211_figures/fsconvert.pdf new file mode 100644 index 0000000..3950b3b Binary files /dev/null and b/sun211_figures/fsconvert.pdf differ diff --git a/sun211_figures/fsexample.pdf b/sun211_figures/fsexample.pdf new file mode 100644 index 0000000..097114d Binary files /dev/null and b/sun211_figures/fsexample.pdf differ diff --git a/sun211_figures/fsmerge.pdf b/sun211_figures/fsmerge.pdf new file mode 100644 index 0000000..5d6c198 Binary files /dev/null and b/sun211_figures/fsmerge.pdf differ diff --git a/sun211_figures/fsremap.pdf b/sun211_figures/fsremap.pdf new file mode 100644 index 0000000..31772d3 Binary files /dev/null and b/sun211_figures/fsremap.pdf differ diff --git a/sun211_figures/gridplot.pdf b/sun211_figures/gridplot.pdf new file mode 100644 index 0000000..c06b370 Binary files /dev/null and b/sun211_figures/gridplot.pdf differ diff --git a/sun211_figures/gridplot_bw.pdf b/sun211_figures/gridplot_bw.pdf new file mode 100644 index 0000000..67353bc Binary files /dev/null and b/sun211_figures/gridplot_bw.pdf differ diff --git a/sun211_figures/mapping.pdf b/sun211_figures/mapping.pdf new file mode 100644 index 0000000..8228188 Binary files /dev/null and b/sun211_figures/mapping.pdf differ diff --git a/sun211_figures/overgrid.pdf b/sun211_figures/overgrid.pdf new file mode 100644 index 0000000..f78a965 Binary files /dev/null and b/sun211_figures/overgrid.pdf differ diff --git a/sun211_figures/overgrid_bw.pdf b/sun211_figures/overgrid_bw.pdf new file mode 100644 index 0000000..0d972d4 Binary files /dev/null and b/sun211_figures/overgrid_bw.pdf differ diff --git a/sun211_figures/parallel.pdf b/sun211_figures/parallel.pdf new file mode 100644 index 0000000..18f9911 Binary files /dev/null and b/sun211_figures/parallel.pdf differ diff --git a/sun211_figures/series.pdf b/sun211_figures/series.pdf new file mode 100644 index 0000000..11656da Binary files /dev/null and b/sun211_figures/series.pdf differ diff --git a/sun211_figures/simpexamp.pdf b/sun211_figures/simpexamp.pdf new file mode 100644 index 0000000..a685837 Binary files /dev/null and b/sun211_figures/simpexamp.pdf differ diff --git a/sun_master.tex b/sun_master.tex new file mode 100644 index 0000000..bcb55c8 --- /dev/null +++ b/sun_master.tex @@ -0,0 +1,21676 @@ +\documentclass[twoside,11pt]{starlink} + +% ? Specify used packages +% ? End of specify used packages + +% ----------------------------------------------------------------------------- +% ? Document identification +% Fixed part +\stardoccategory {Starlink User Note} +\stardocinitials {SUN} +\stardocsource {sun\stardocnumber} + +% Variable part - replace [xxx] as appropriate. +c+ +\stardocnumber {211.27} +c- +f+ +\stardocnumber {210.27} +f- +\stardocauthors {R.F. Warren-Smith \& D.S. Berry} +\stardocdate {25th February 2013} +\stardoctitle {AST\linebreak% + A Library for Handling\linebreak% + World Coordinate Systems\linebreak% + in Astronomy} +\stardoccopyright {Copyright (C) 2014 Science \& Technology Facilities Council} +\stardocversion {V} +c+ +\stardocmanual {Programmer's Guide\\(C Version)} +\startitlepic{ + \includegraphics[width=0.25\textwidth]{sun211_figures/fronta}~~~~~\hfill + \includegraphics[width=0.25\textwidth]{sun211_figures/frontb}~~~~~\hfill + \includegraphics[width=0.25\textwidth]{sun211_figures/frontc} +} +c- +f+ +\stardocmanual {Programmer's Guide\\(Fortran Version)} +\startitlepic{ + \includegraphics[width=0.25\textwidth]{sun210_figures/fronta}~~~~~\hfill + \includegraphics[width=0.25\textwidth]{sun210_figures/frontb}~~~~~\hfill + \includegraphics[width=0.25\textwidth]{sun210_figures/frontc} +} +f- +\stardocabstract { +The AST library provides a comprehensive range of facilities for +attaching world coordinate systems to astronomical data, for +retrieving and interpreting that information in a variety of formats, +including FITS-WCS, and for generating graphical output based on it. + +This programmer's manual should be of interest to anyone writing +astronomical applications which need to manipulate coordinate system +data, especially celestial or spectral coordinate systems. AST is portable and +environment-independent. +} +% ? End of document identification +% ----------------------------------------------------------------------------- +% ? Document specific \providecommand or \newenvironment commands. + +\providecommand{\appref}[1]{Appendix~\ref{#1}} +\providecommand{\secref}[1]{\S\ref{#1}} + +\providecommand{\fitskey}[3]{{#1}&{#2}&{#3}\\} + +% Use {\tt ... } as \texttt{...} does not work if there are new lines in #1 +\providecommand{\sstsynopsis}[1]{\sstdiytopic{Synopsis}{\tt #1}} + +% Format the constructor section. +\providecommand{\sstconstructor}[1]{\sstdiytopic{Constructor Function}{#1}} + +% ? End of document specific commands +% ----------------------------------------------------------------------------- +% Title Page. +% =========== +\begin{document} +\scfrontmatter + +\begin{center} +c+ +\emph{This is the C version of this document.\\ + For the Fortran version, please see \xref{SUN/210}{sun210}{}.} +c- +f+ +\emph{This is the Fortran version of this document.\\ + For the C version, please see \xref{SUN/211}{sun211}{}.} +f- +\end{center} + +% Main text of document. +\vspace{7mm} +\section{Introduction} + +Welcome to the AST library. If you are writing software for astronomy +and need to use celestial coordinates (\emph{e.g.}\ RA and Dec), spectral +coordinates (\emph{e.g.}\ wavelength, frequency, \emph{etc.}), or +other coordinate system information, then this library should be of +interest. It provides solutions for most of the problems you will meet +and allows you to write robust and flexible software. It is able to read +and write WCS information in a variety of formats, including +\htmladdnormallink{FITS-WCS}{http://fits.gsfc.nasa.gov/fits_wcs.html}. + +%\subsection{TBW---What is a World Coordinate System?} + +\subsection{What Problems Does AST Tackle?} + +Here are some of the main problems you may face when handling world +coordinate system (WCS) information and the solutions that AST +provides: + +\begin{description} +\item[1. The Variety of Coordinate Systems]\mbox{}\\ +Astronomers use a wide range of differing coordinate systems to describe +positions within a variety of physical domains. For instance, there are a +large number of celestial coordinate systems in use within astronomy to +describe positions on the sky. Understanding these, and knowing how to +convert coordinates between them, can require considerable expertise. It +can also be difficult to decide which of them your software should support. +The same applies to coordinate systems describing other domains, such as +position within an electro-magnetic spectrum. + +\textbf{Solution.} AST has built-in knowledge of many coordinate systems +and allows you to convert freely between them without specialist +knowledge. This avoids the need to embed details of specific +coordinate systems in your software. You also benefit automatically +when new coordinate systems are added to AST. + +\item[2. Storing and Retrieving WCS Information]\mbox{}\\ +Storing coordinate system information in astronomical datasets and +retrieving it later can present a considerable challenge. Typically, +it requires knowledge of rather complex conventions +(\emph{e.g.}\ FITS) which are low-level, often mis-interpreted and may +be subject to change. Exchanging information with other software +systems is further complicated by the number of different conventions +in use. + +\textbf{Solution.} AST combines a unifying high-level description of WCS +information with the ability to save and restore this using a variety +of formats. Details of the formats, which include FITS, are handled +internally by AST. This frees you from the need to understand them or +embed the details in your software. Again, you benefit automatically +when new formats are added to AST. + +\item[3. Generating Graphical Output]\mbox{}\\ +Producing graphical displays involving curvilinear coordinate systems, +such as celestial coordinate grids, can be complicated. Particular +difficulties arise when handling large areas of sky, the polar regions +and discontinuous (\emph{e.g.}\ segmented) sky projections. Even just +numbering and labelling curvilinear axes is rarely straightforward. + +\textbf{Solution.} AST provides plotting facilities especially designed +for use with curvilinear coordinate systems. These include the +plotting of axes and complete labelled coordinate grids. A large +number of options are provided for tailoring the output to your +specific needs. Three dimensional coordinate grids can also be produced. + +\item[4. Aligning Data from Different Sources]\mbox{}\\ +One of the main uses of coordinate systems is to facilitate the +inter-comparison of data from different sources. A typical use might +be to plot (say) radio contours over an optical image. In practice, +however, different celestial coordinate systems may have been used, +making accurate alignment far from simple. + +\textbf{Solution} AST provides a one-step method of aligning datasets, +searching for all possible intermediate coordinate systems. This +makes it simple to directly inter-relate the pixel coordinates of +different datasets. + +\item[5. Handling Different Types of Coordinate System]\mbox{}\\ +Not all coordinate systems used in astronomy are celestial ones, so if +you are writing general-purpose software such as (say) a display tool, +you may also need to handle axes representing wavelength, distance, +time or whatever else comes along. Obviously, you would prefer not to +handle each one as a special case. + +\textbf{Solution} AST uses the same flexible high-level model to +describe all types of coordinate system. This allows you to write +software that handles different kinds of coordinate axis without +introducing special cases. +\end{description} + +\subsection{Other Design Objectives} + +As well as its scientific objectives, the AST library's design +includes a number of technical criteria intended to make it applicable +to as wide a range of projects as possible. The main considerations +are described here: + +\begin{enumerate} +\item {\bf{Minimum Software Dependencies.}} +The AST library depends on no other other software\footnote{It comes with +bundled copies of the ERFA and +\xref{Starlink PAL libraries}{sun268}{} which are built +at the same time as the other AST internal libraries. Alternatively, external +PAL and ERFA libraries may be used by specifying the ``\texttt{--with-external\_pal}'' option when configuring AST}. + +\item {\bf{Environment Independence.}} +AST is designed so that it can operate in a variety of ``programming +environments'' and is not tied to any particular one. To allow this, +it uses simple, flexible interfaces to obtain the following services: + +\begin{itemize} +\item {\bf{Data Storage.}} Data I/O operations are based on text +and/or FITS headers. This makes it easy to interface to a wide variety +of astronomical data formats in a machine-independent way. + +\item {\bf{Graphics.}} Graphical output is produced \emph{via} a +simple generic graphics interface, which may easily be re-implemented +over different graphics systems. AST provides a default implementation +based on the widely-used PGPLOT graphics system +(\xref{SUN/15}{sun15}{}). + +\item {\bf{Error Handling.}} Error messages are written to standard +error by default, but go through a simple generic interface similar to +that used for graphics (above). This permits error message delivery +\emph{via} other routes when necessary (\emph{e.g.} in a graphical +interface). +\end{itemize} + +\item {\bf{Multiple Language Support.}} +AST has been designed to be called from more than one language. +c+ +Both C and Fortran interfaces are available (see +\xref{SUN/210}{sun210}{} for the Fortran version) +c- +f+ +Both Fortran and C interfaces are available (see +\xref{SUN/211}{sun211}{} for the C version) +f- +and use from C$++$ is also straightforward if the C interface is +included using: + +\begin{small} +\begin{terminalv} +extern "C" { +#include "ast.h" +} +\end{terminalv} +\end{small} + +A JNI interface (known as ``JNIAST'' - see +\url{http://www.starlink.ac.uk/jniast/}) has also been developed by Starlink +which allows AST to be used from Java. + +\item {\bf{Object Oriented Design.}} +AST uses ``object oriented'' techniques internally in order to provide +a flexible and easily-extended programming model. A fairly +traditional calling interface is provided, however, so that the +library's facilities are easily accessible to programmers using +c+ +C and Fortran. +c- +f+ +Fortran and C. +f- + +\item {\bf{Portability.}} +AST is implemented entirely in ANSI standard C and, when called +\emph{via} its C interface, makes no explicit use of any +machine-dependent facilities. + +The Fortran interface is, unavoidably, machine dependent. However, the +potential for problems has been minimised by encapsulating the +interface layer in a compact set of C macros which facilitate its +transfer to other platforms. No Fortran compiler is needed to build +the library. + +Currently, AST is supported by Starlink on PC~Linux, Sun~Solaris and +Tru64~Unix (formerly DEC~UNIX) platforms. +\end{enumerate} + +\subsection{What Does ``AST'' Stand For?} + +The library name ``AST'' stands for ``ASTrometry Library''. The name +arose when it was thought that knowledge of ``astrometry'' +(\emph{i.e.}\ celestial coordinate systems) would form the bulk of the +library. In fact, it turns out that astrometry forms only a minor +component, but the name AST has stuck. + +\cleardoublepage +\section{Overview of AST Concepts} + +This section presents a brief overview of AST concepts. It is intended +as a basic orientation course before you move on to the more technical +considerations in subsequent sections. + +\subsection{\label{ss:mappingoverview}Relationships Between Coordinate Systems} + +The relationships between coordinate systems are represented in AST by +Objects called Mappings. A Mapping does not represent a coordinate +system itself, but merely the process by which you move from one +coordinate system to another related one. + + A convenient picture of a Mapping is as a ``black box'' + (Figure~\ref{fig:mapping}) into which you can feed sets of + coordinates. + \begin{figure}[bhtp] + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/mapping} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/mapping} +f- + \caption{A Mapping viewed as a ``black box'' for transforming coordinates.} + \label{fig:mapping} + \end{center} + \end{figure} + +For each set you feed in, the Mapping returns a corresponding set of +transformed coordinates. Since each set of coordinates represents a +point in a coordinate space, the Mapping acts to inter-relate +corresponding positions in the two spaces, although what these spaces +represent is unspecified. Notice that a Mapping need not have the +same number of input and output coordinates. That is, the two +coordinate spaces which it inter-relates need not have the same number +of dimensions. + +In many cases, the transformation can, in principle, be performed in +either direction: either from the \emph{input} coordinate space to the +\emph{output}, or \emph{vice versa}. The first of these is termed the +\emph{forward} transformation and the other the \emph{inverse} +transformation. + + +\textbf{Further reading:} For a more complete discussion of Mappings, +see~\secref{ss:mappings}. + +\subsection{\label{ss:mappingselection}Mappings Available} + +The basic concept of a Mapping (\secref{ss:mappingoverview}) is rather +generic and obviously it is necessary to have specific Mappings that +implement specific relationships between coordinate systems. AST +provides a range of these, to perform transformations such as the +following and, where appropriate, their inverses: + +\begin{itemize} +\item Conversions between various celestial coordinate systems (the +SlaMap). + +\item Conversions between various spectral coordinate systems (the +SpecMap and GrismMap). + +\item Conversions between various time systems (the TimeMap). + +\item Conversion between 2-dimensional spherical celestial coordinates +(longitude and latitude) and a 3-dimensional vectorial positions (the SphMap). + +\item Various projections of the celestial sphere on to 2-dimensional +coordinate spaces---\emph{i.e.}\ map projections (the DssMap and WcsMap). + +\item Permutation, introduction and elimination of coordinates (the +PermMap). + +\item Various linear coordinate transformations (the MatrixMap, WinMap, +ShiftMap and ZoomMap). + +\item General N-dimensional polynomial transformations (the PolyMap). + +\item Lookup tables (the LutMap). + +c+ +\item General-purpose transformations expressed using arithmetic +operations and functions similar to those available in C (the +MathMap). +c- +f+ +\item General-purpose transformations expressed using arithmetic +operations and functions similar to those available in Fortran (the +MathMap). +f- + +c+ +\item Transformations for internal use within a program, based on +private transformation functions which you write yourself in C (the +IntraMap). +c- +f+ +\item Transformations for internal use within a program, based on +private transformation routines which you write yourself in Fortran +(the IntraMap). +f- +\end{itemize} + +\textbf{Further reading:} For a more complete description of each of the +Mappings mentioned above, see its entry in +\appref{ss:classdescriptions}. In addition, see the discussion of the +PermMap in \secref{ss:permmapexample}, the UnitMap in +\secref{ss:unitmapexample} and the IntraMap in +\secref{ss:intramaps}. The ZoomMap is used as an example throughout +\secref{ss:primer}. + +\subsection{\label{ss:cmpmapoverview}Compound Mappings} + +The Mappings described in \secref{ss:mappingselection} provide a set +of basic building blocks from which more complex Mappings may be +constructed. The key to doing this is a type of Mapping called a +CmpMap, or compound Mapping. A CmpMap's role is, in principle, very +simple: it allows any other pair of Mappings to be joined together +into a single entity which behaves as if it were a single Mapping. A +CmpMap is therefore a container for another pair of Mappings. + + A pair of Mappings may be combined using a CmpMap in either of two + ways. The first of these, \emph{in series}, is illustrated in + Figure~\ref{fig:seriescmpmap}. + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/series} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/series} +f- + \caption[A CmpMap composed of two component Mappings joined in series]{A CmpMap (compound Mapping) composed of two component + Mappings joined in series. The output coordinates of the first Mapping + feed into the input coordinates of the second one, so that the whole + entity behaves like a single Mapping.} + \label{fig:seriescmpmap} + \end{center} + \end{figure} + + + Here, the transformations implemented by each component Mapping are + performed one after the other, with the output from the first Mapping + feeding into the second. The second way, \emph{in parallel}, is shown in + Figure~\ref{fig:parallelcmpmap}. + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.5\textwidth]{sun211_figures/parallel} +c- +f+ + \includegraphics[width=0.5\textwidth]{sun210_figures/parallel} +f- + \caption[A CmpMap composed of two Mappings joined in parallel.]{A CmpMap composed of two Mappings joined in parallel. Each + component Mapping acts on a complementary subset of the input and + output coordinates.} + \label{fig:parallelcmpmap} + \end{center} + \end{figure} + +In this case, each Mapping acts on a complementary subset of the +input and output coordinates.\footnote{A pair of Mappings can be combined +in a third way using a TranMap. A TranMap allows the forward +transformation of one Mapping to be combined with the inverse +transformation of another to produce a single Mapping.} + + The CmpMap forms the key to building arbitrarily complex Mappings + because it is itself a form of Mapping. This means that a CmpMap may + contain other CmpMaps as components + (\emph{e.g.}\ Figure~\ref{fig:complexcmpmap}). This nesting of CmpMaps + can be repeated indefinitely, so that complex Mappings may be built in + a hierarchical manner out of simper ones. + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.65\textwidth]{sun211_figures/complex} +c- +f+ + \includegraphics[width=0.65\textwidth]{sun210_figures/complex} +f- + \caption[CmpMaps may be nested in order to + construct complex Mappings out of simpler building blocks.]{CmpMaps + (compound Mappings) may be nested in order to + construct complex Mappings out of simpler building blocks.} + \label{fig:complexcmpmap} + \end{center} + \end{figure} + This gives AST great flexibility in the coordinate transformations it + can describe. + +\textbf{Further reading:} For a more complete description of CmpMaps, +see \secref{ss:cmpmaps}. Also see the CmpMap entry in +\appref{ss:classdescriptions}. + +\subsection{Representing Coordinate Systems} + + While Mappings (\secref{ss:mappingoverview}) represent the + relationships between coordinate systems in AST, the coordinate + systems themselves are represented by Objects called Frames + (Figure~\ref{fig:frames}). + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.55\textwidth]{sun211_figures/frames} +c- +f+ + \includegraphics[width=0.55\textwidth]{sun210_figures/frames} +f- + \caption[Representing coordinate systems as Frames.]{(a) A basic Frame is used to represent a Cartesian coordinate + system, here 2-dimensional. (b) A SkyFrame represents a (spherical) + celestial coordinate system. (c) The axis order of any Frame may be + permuted to match the coordinate space it describes.} + \label{fig:frames} + \end{center} + \end{figure} + +A Frame is similar in concept to the frame you might draw around a +graph. It contains information about the labels which appear on the +axes, the axis units, a title, knowledge of how to format the +coordinate values on each axis, \emph{etc.} An AST Frame is not, +however, restricted to two dimensions and may have any number of axes. + +c+ +A basic Frame may be used to represent a Cartesian coordinate system +by setting values for its \emph{attributes} (all AST Objects have +values associated with them called attributes, which may be set and +enquired). Usually, this would involve setting appropriate axis +labels and units, for example. Functions are provided for use with +Frames to perform operations such as formatting coordinate values as +text, calculating distances between points, interchanging axes, +\emph{etc.} +c- +f+ +A basic Frame may be used to represent a Cartesian coordinate system +by setting values for its \emph{attributes} (all AST Objects have +values associated with them called attributes, which may be set and +enquired). Usually, this would involve setting appropriate axis +labels and units, for example. Routines are provided for use with +Frames to perform operations such as formatting coordinate values as +text, calculating distances between points, interchanging axes, +\emph{etc.} +f- + +There are several more specialised forms of Frame, which provide the +additional functionality required when handling coordinates within some +specific physical domain. This ranges from tasks such as formatting axis +values, to complex tasks such as determining the transformation between +any pair of related coordinate systems. For instance, the SkyFrame +(Figure~\ref{fig:frames}b,c), represents celestial coordinate systems, +the SpecFrame represents spectral coordinate systems, and the TimeFrame +represents time coordinate systems. All these provide a wide range of +different systems for describing positions within their associated physical +domain, and these may be selected by setting appropriate attributes. + + As with compound Mappings (\secref{ss:cmpmapoverview}), it is possible + to merge two Frames together to form a compound Frame, or CmpFrame, in + which both sets of axes are combined. One could, for example, have + celestial coordinates on two axes and an unrelated coordinate + (wavelength, perhaps) on a third (Figure~\ref{fig:cmpframe}). + Knowledge of the relationships between the axes is preserved + internally by the process of constructing the CmpFrame which + represents them. + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.4\textwidth]{sun211_figures/cmpframe} +c- +f+ + \includegraphics[width=0.4\textwidth]{sun210_figures/cmpframe} +f- + \caption[A CmpFrame (compound Frame) formed by combining two simpler + Frames.]{A CmpFrame (compound Frame) formed by combining two simpler + Frames. Note how the special relationship which exists between the RA + and Dec axes is preserved within this data structure. As with compound + Mappings (Figure~\ref{fig:complexcmpmap}), CmpFrames may be nested in + order to build more complex Frames.} + \label{fig:cmpframe} + \end{center} + \end{figure} + +\textbf{Further reading:} For a more complete description of Frames see +\secref{ss:frames}, for SkyFrames see \secref{ss:skyframes} and for +SpecFrames see \secref{ss:specframes}. Also see the Frame, SkyFrame, +SpecFrame, TimeFrame and CmpFrame entries in \appref{ss:classdescriptions}. + +\subsection{Networks of Coordinate Systems} + + Mappings and Frames may be connected together to form networks called + FrameSets, which are used to represent sets of inter-related + coordinate systems (Figure~\ref{fig:frameset}). + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.65\textwidth]{sun211_figures/frameset} +c- +f+ + \includegraphics[width=0.65\textwidth]{sun210_figures/frameset} +f- + \caption[A FrameSet is a network of Frames.]{A FrameSet is a network of Frames inter-connected by Mappings + such that there is exactly one conversion path, \emph{via} Mappings, + between any pair of Frames.} + \label{fig:frameset} + \end{center} + \end{figure} + + +A FrameSet may be extended by adding a new Frame to it, together with +an associated Mapping which relates the new coordinate system to one +which is already present. This process ensures that there is always +exactly one path, \emph{via} Mappings, between any pair of Frames. A +function is provided for identifying this path and returning the +complete Mapping. + +One of the Frames in a FrameSet is termed its \emph{base} Frame. This +underlies the FrameSet's purpose, which is to calibrate datasets and +other entities by attaching coordinate systems to them. In this +context, the base Frame represents the ``native'' coordinate system +(for example, the pixel coordinates of an image). Similarly, one +Frame is termed the \emph{current} Frame and represents the +``currently-selected'' coordinates. It might, typically, be a +celestial or spectral coordinate system and would be used during +interactions with +a user, as when plotting axes on a graph or producing a table of +results. Other Frames within the FrameSet represent a library of +alternative coordinate systems which a software user can select by +making them current. + +\textbf{Further reading:} For a more complete description of +FrameSets, see \secref{ss:framesets} and \secref{ss:fshigher}. Also +see the FrameSet entry in \appref{ss:classdescriptions}. + +\subsection{Input/Output Facilities} + +AST allows you to convert any kind of Object into a stream of text +which contains a full description of that Object. This text may be +written out by one program and read back in by another, thus allowing +the original Object to be reconstructed. + +The filter which converts Objects into text and back again is itself a +kind of Object, called a Channel. A Channel provides a number of +options for controlling the information content of the text, such as +the addition of comments for human interpretation. It is also +possible to intercept the text being processed by a Channel so that it +may be redirected to/from any chosen external data store, such as a +text file, an astronomical dataset, or a network connection. + +The text format used by the basic Channel class is peculiar to the AST +library - no other software will understand it. However, more specialised +forms of Channel are provided which use text formats more widely +understood. + +To further facilitate the storage of coordinate system information in +astronomical datasets, a more specialised form of Channel called a +FitsChan is provided. Instead of using free-format text, a FitsChan +converts AST Objects to and from FITS header cards. It also allows the +information to be encoded in the FITS cards in a number of ways +(called \emph{encodings}), so that WCS information from a variety of +sources can be handled. + +Another sub-class of Channel, called XmlChan, is a specialised form of +Channel that stores the text in the form of XML markup. Currently, two +markup formats are provided by the XmlChan class, one is closely related +to the text format produced by the basic Channel class (currently, no +schema or DTD is available describing this format). The other is a subset +of an early draft of the IVOA Space-Time-Coordinates XML (STC-X) schema +(V1.20) described at +\url{http://www.ivoa.net/Documents/WD/STC/STC-20050225.html +}\footnote{XML documents which use only the subset of the STC schema +supported by AST can be read by the XmlChan class to produce +corresponding AST objects (subclasses of the Stc class). However, the +reverse is not possible. That is, AST objects can not currently be +written out in the form of STC documents.}. The version of STC-X that has +been adopted by the IVOA differs in several significant respects from +V1.20, and therefore this XmlChan format is of historical interest only. + +Finally, the StcsChan class provides facilities for reading and writing +IVOA STC-S region descriptions. STC-S (see +\url{http://www.ivoa.net/Documents/latest/STC-S.html}) is a linear string +syntax that allows simple specification of STC metadata. AST supports a +subset of the STC-S specification, allowing an STC-S description of a +region within an AST-supported astronomical coordinate system to be converted +into an equivalent AST Region object, and vice-versa. + +\textbf{Further reading:} For a more complete description of Channels +see \secref{ss:channels} and for FitsChans see \secref{ss:nativefits} +and \secref{ss:foreignfits}. Also see the Channel and FitsChan entries +in \appref{ss:classdescriptions} and the Encoding entry in +\appref{ss:attributedescriptions}. + +\subsection{Producing Graphical Output} + +Two dimensional graphical output is supported by a specialised form of +FrameSet called +a Plot, whose base Frame corresponds with the native coordinates of +the underlying graphics system. Plotting operations are specified in +\emph{physical coordinates} which correspond with the Plot's current +Frame. Typically, this might be a celestial coordinate system. + +Three dimensional plotting is also supported, via the Plot3D class - +sub-class of Plot. + +Operations, such as drawing lines, are automatically transformed from +physical to graphical coordinates before plotting, using an adaptive +algorithm which ensures smooth curves (because the transformation is +usually non-linear). ``Missing'' coordinates (\emph{e.g.}\ graphical +coordinates which do not project on to the celestial sphere), +discontinuities and generalised clipping are all consistently handled. +It is possible, for example, to plot in equatorial coordinates and +clip in galactic coordinates. The usual plotting operations are +provided (text, markers), but a geodesic curve replaces the primitive +straight line element. There is also a separate function for drawing +axis lines, since these are normally not geodesics. + +In addition to drawing coordinate grids over an area of the sky, another +common use of the Plot class is to produce line plots such as flux +against wavelength, displacement again time, \emph{etc}. For these +situations the current Frame of the Plot would be a compound Frame +(CmpFrame) containing a pair of 1-dimensional Frames - the first +representing the X axis quantity (wavelength, time, etc), and the second +representing the Y axis quantity (flux, displacement, etc). The Plot +class includes an option for axes to be plotted logarithmically. + + Perhaps the most useful graphics function available is for drawing + fully annotated coordinate grids (\emph{e.g.}\ Figure~\ref{fig:gridplot}). + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.6\textwidth]{sun211_figures/gridplot_bw} +c- +f+ + \includegraphics[width=0.6\textwidth]{sun210_figures/gridplot_bw} +f- + \caption[A labelled coordinate grid for an all-sky zenithal equal area + projection in ecliptic coordinates.]{A labelled coordinate grid for an all-sky zenithal equal area + projection in ecliptic coordinates. This was composed and drawn + \emph{via} a Plot using a +c+ + single function call.} +c- +f+ + single subroutine call.} +f- + \label{fig:gridplot} + \end{center} + \end{figure} + +This uses a general algorithm which does not depend on knowledge of +the coordinates being represented, so can also handle +programmer-defined coordinate systems. Grids for all-sky projections, +including polar regions, can be drawn and most aspects of the output +(colour, line style, \emph{etc.}) can be adjusted by setting +appropriate Plot attributes. + +\textbf{Further reading:} For a more complete description of +Plots and how to produce graphical output, see \secref{ss:plots}. Also +see the Plot entry in \appref{ss:classdescriptions}. + +\cleardoublepage +\section{\label{ss:howto}How To\ldots} + +For those of you with a plane to catch, this section provides some +instant templates and recipes for performing the most +commonly-required operations using AST, but without going into +detail. The examples given (sort of) follow on from each other, so you +should be able to construct a variety of programs by piecing them +together. Note that some of them appear longer than they actually +are, because we have included plenty of comments and a few options +that you probably won't need. + +If any of this material has you completely baffled, then you may want +to read the introduction to AST programming concepts in +\secref{ss:primer} first. Otherwise, references to more detailed +reading are given after each example, just in case they don't quite do +what you want. + +\subsection{\ldots Obtain and Install AST} +The AST library is available both as a stand-alone package and also as +part of the Starlink Software Collection\footnote{The Starlink Software +Collection can be downloaded from +\url{http://www.starlink.ac.uk/Download/}.}. If your site has the Starlink +Software Collection installed then AST should already be available. + +If not, you can download the AST library by itself from +\url{http://www.starlink.ac.uk/ast/}. + +\subsection{\ldots Structure an AST Program} + +An AST program normally has the following structure: + +c+ +\begin{small} +\begin{terminalv} +/* Include the interface to the AST library. */ +#include "ast.h" + +/* Main program (or could be any function). */ +main () { + + +/* Enclose the parts which use AST between the astBegin and astEnd macros. */ + astBegin; + + astEnd; + + +} +\end{terminalv} +\end{small} +c- +f+ +\small +\begin{terminalv} +* Include the interface to the AST library. + INCLUDE 'AST_PAR' + +* Declare an integer status variable. + INTEGER STATUS + + +* Initialise the status to zero. + STATUS = 0 + + +* Enclose the parts which use AST between AST_BEGIN and AST_END calls. + CALL AST_BEGIN( STATUS ) + + CALL AST_END( STATUS ) + + + END +\end{terminalv} +\normalsize +f- + +c+ +The use of astBegin and astEnd is optional, but has the effect of +tidying up after you have finished using AST, so is normally +recommended. For more details of this, see \secref{ss:contexts}. For +details of how to access the ``ast.h'' header file, see +\secref{ss:accessingheaderfile}. +c- +f+ +The use of AST\_BEGIN and AST\_END is optional, but has the effect of +tidying up after you have finished using AST, so is normally +recommended. For more details of this, see \secref{ss:contexts}. For +details of how to access the AST\_PAR include file, see +\secref{ss:accessingheaderfile}. +f- + +\subsection{\label{ss:howtobuild}\ldots Build an AST Program} + +To build a simple AST program that doesn't use graphics, use: + +c+ +\begin{small} +\begin{terminalv} +cc program.c -L/star/lib -I/star/include `ast_link` -o program +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} +f77 program.f -L/star/lib -I/star/include `ast_link` -o program +\end{terminalv} +\end{small} + +On Linux systems you should usually use \verb+g77 -fno-second-underscore+ in +place of \verb+f77+ - see \xref{``Software development on Linux''}{sun212} +{software_development_on_linux} in \xref{SUN/212}{sun212}{}. +f- + +To build a program which uses PGPLOT for graphics, use: + +c+ +\begin{small} +\begin{terminalv} +cc program.c -L/star/lib `ast_link -pgplot` -o program +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} +f77 program.f -L/star/lib `ast_link -pgplot` -o program +\end{terminalv} +\end{small} + +again using \verb+g77 -fno-second-underscore+ in place of \verb+f77+ +on Linux systems. +f- + +c+ +For more details about accessing the ``ast.h'' header file, see +\secref{ss:accessingheaderfile}. For more +details about linking programs, see \secref{ss:linking} and the +description of the ``ast\_link'' command in +\appref{ss:commanddescriptions}. +c- +f+ +For more details about accessing AST include files, see +\secref{ss:accessingheaderfile}. For more +details about linking programs, see \secref{ss:linking} and the +description of the ``ast\_link'' command in +\appref{ss:commanddescriptions}. +f- + +\subsection{\label{ss:howtoreadwcs}\ldots Read a WCS Calibration from a Dataset} + +c+ +Precisely how you extract world coordinate system (WCS) information +from a dataset obviously depends on what type of dataset it +is. Usually, however, you should be able to obtain a set of FITS +header cards which contain the WCS information (and probably much more +besides). Suppose that ``cards'' is a pointer to a string +containing a complete set of concatenated FITS header cards (such as +produced by the CFITSIO function fits\_hdr2str). Then proceed as follows: + +\begin{small} +\begin{terminalv} +fitsfile *fptr; +AstFitsChan *fitschan; +AstFrameSet *wcsinfo; +char *header; +int nkeys, status; + +... + +/* Obtain all the cards in the header concatenated into a single dynamically + allocated null-terminated character string. Note, we do not exclude + any cards since we may later modify the WCS information within the + header and consequently want to write the entire header out again. */ + if( fits_hdr2str( fptr, 0, NULL, 0, &header, &nkeys, &status ) ) + printf(" Error getting header\n"); + ... + +/* Header obtained succesfully... */ + } else { + +/* Create a FitsChan and fill it with FITS header cards. */ + fitschan = astFitsChan( NULL, NULL, "" ); + astPutCards( fitschan, header ); + +/* Free the memory holding the concatenated header cards. */ + header = free( header ); + +/* Read WCS information from the FitsChan. */ + wcsinfo = astRead( fitschan ); + + ... + +\end{terminalv} +\end{small} +c- + +f+ +Precisely how you extract world coordinate system (WCS) information +from a dataset obviously depends on what type of dataset it +is. Usually, however, you should be able to obtain a set of FITS +header cards which contain the WCS information (and probably much more +besides). Suppose that CARDS is an array of character strings +containing a complete set of FITS header cards and NCARD is the number +of cards. Then proceed as follows: + +\small +\begin{terminalv} + INTEGER FITSCHAN, ICARD, NCARD, WCSINFO + CHARACTER * ( 80 ) CARDS( NCARD ) + + ... + +* Create a FitsChan and fill it with FITS header cards. + FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) + DO 1 ICARD = 1, NCARD + CALL AST_PUTFITS( FITSCHAN, CARDS( ICARD ), .FALSE., STATUS ) + 1 CONTINUE + +* Rewind the FitsChan and read WCS information from it. + CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) + WCSINFO = AST_READ( FITSCHAN, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The result should be a pointer, ``wcsinfo'', to a FrameSet which +contains the WCS information. This pointer can now be used to perform +many useful tasks, some of which are illustrated in the following +recipes. +c- +f+ +The result should be a pointer, WCSINFO, to a FrameSet which contains +the WCS information. This pointer can now be used to perform many +useful tasks, some of which are illustrated in the following recipes. +f- + +c+ +Some datasets which do not easily yield FITS header cards may require +a different approach, possibly involving use of a Channel or XmlChan +(\secref{ss:channels}) rather than a FitsChan. In the case of the +Starlink NDF data format, for example, all the above may be replaced +by a single call to the function +\xref{ndfGtwcs}{sun33}{ndfGtwcs}---see \xref{SUN/33}{sun33}{}. The +whole process can probably be encapsulated in a similar way for +most data systems, whether they use FITS header cards or not. +c- +f+ +Some datasets which do not easily yield FITS header cards may require +a different approach, possibly involving use of a Channel or XmlChan +(\secref{ss:channels}) rather than a FitsChan. In the case of the +Starlink NDF data format, for example, all the above may be replaced +by a single call to the routine +\xref{NDF\_GTWCS}{sun33}{NDF_GTWCS}---see \xref{SUN/33}{sun33}{}. The +whole process can probably be encapsulated in a similar way for most +other data systems, whether they use FITS header cards or not. +f- + +For more details about reading WCS information from datasets, see +\secref{ss:identifyingfitsencoding} and +\secref{ss:readingforeignfits}. For a more general description of +FitsChans and their use with FITS header cards, see +\secref{ss:nativefits} and \secref{ss:foreignfits}. For more details +about FrameSets, see \secref{ss:framesets} and \secref{ss:fshigher}. + +\subsection{\ldots Validate WCS Information} + +Once you have read WCS information from a dataset, as in +\secref{ss:howtoreadwcs}, you may wish to check that you have been +successful. The following will detect and classify the things that +might possibly go wrong: + +c+ +\begin{small} +\begin{terminalv} +#include + +... + +if ( !astOK ) { + +} else if ( wcsinfo == AST__NULL ) { + +} else if ( strcmp( astGetC( wcsinfo, "Class" ), "FrameSet" ) ) { + +} else { + +} +\end{terminalv} +\end{small} +c- +f+ +\small +\begin{terminalv} + IF ( STATUS .NE. 0 ) THEN + + ELSE IF ( WCSINFO .EQ. AST__NULL ) THEN + + ELSE IF ( AST_GETC( WCSINFO, 'Class', STATUS ) .NE. 'FrameSet' ) THEN + + ELSE + + END IF +\end{terminalv} +\normalsize +f- + +c+ +For more information about detecting errors in AST functions, see +\secref{ss:errordetection}. For details of how to validate input data +read by AST, see \secref{ss:validatinginput} and +\secref{ss:readingforeignfits}. +c- +f+ +For more information about detecting errors in AST routines, see +\secref{ss:errordetection}. For details of how to validate input data +read by AST, see \secref{ss:validatinginput} and +\secref{ss:readingforeignfits}. +f- + +\subsection{\ldots Display AST Data} + +If you have a pointer to any AST Object, you can display the data +stored in that Object in textual form as follows: + +c+ +\begin{small} +\begin{terminalv} +astShow( wcsinfo ); +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + CALL AST_SHOW( WCSINFO, STATUS ) +\end{terminalv} +\end{small} +f- + +Here, we have used a pointer to the FrameSet which we read earlier +(\secref{ss:howtoreadwcs}). The result is written to the program's +standard output stream. This can be very useful during debugging. + +c+ +For more details about using astShow, see +\secref{ss:displayingobjects}. For information about interpreting the +output, also see \secref{ss:textualoutputformat}. +c- +f+ +For more details about using AST\_SHOW, see +\secref{ss:displayingobjects}. For information about interpreting the +output, also see \secref{ss:textualoutputformat}. +f- + +\subsection{\label{ss:howtotransform}\ldots Convert Between Pixel and World Coordinates} + +You may use a pointer to a FrameSet, such as we read in +\secref{ss:howtoreadwcs}, to transform a set of points between the +pixel coordinates of an image and the associated world coordinates. If +you are working in two dimensions, proceed as follows: + +c+ +\begin{small} +\begin{terminalv} +double xpixel[ N ], ypixel[ N ]; +double xworld[ N ], yworld[ N ]; + +... + +astTran2( wcsinfo, N, xpixel, ypixel, 1, xworld, yworld ); +\end{terminalv} +\end{small} +c- +f+ +\small +\begin{terminalv} + INTEGER N + DOUBLE PRECISION XPIXEL( N ), YPIXEL( N ) + DOUBLE PRECISION XWORLD( N ), YWORLD( N ) + + ... + + CALL AST_TRAN2( WCSINFO, N, XPIXEL, YPIXEL, .TRUE., + : XWORLD, YWORLD, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, N is the number of points to be transformed, ``xpixel'' and +``ypixel'' hold the pixel coordinates, and ``xworld'' and ``yworld'' +receive the returned world coordinates.\footnote{By pixel coordinates, +we mean a coordinate system in which the first pixel in the image is +centred on (1,1) and each pixel is a unit square. Note that the world +coordinates will not necessarily be celestial coordinates, but if they +are, then they will be in radians.} To transform in the opposite +direction, interchange the two pairs of arrays (so that the world +coordinates are given as input) and change the fifth argument of +astTran2 to zero. +c- +f+ +Here, N is the number of points to be transformed, XPIXEL and YPIXEL +hold the pixel coordinates, and XWORLD and YWORLD receive the returned +world coordinates.\footnote{By pixel coordinates, we mean a coordinate +system in which the first pixel in the image is centred on (1,1) and +each pixel is a unit square. Note that the world coordinates will not +necessarily be celestial coordinates, but if they are, then they will +be in radians.} To transform in the opposite direction, interchange +the two pairs of arrays (so that the world coordinates are given as +input) and change the fifth argument of AST\_TRAN2 to .FALSE.. +f- + +c+ +To transform points in one dimension, use astTran1. In any other +number of dimensions (or if the number of dimensions is initially +unknown), use astTranN or astTranP. These functions are described in +\appref{ss:functiondescriptions}. +c- +f+ +To transform points in one dimension, use AST\_TRAN1. In any other +number of dimensions (or if the number of dimensions is initially +unknown), use AST\_TRANN. These routines are described in +\appref{ss:functiondescriptions}. +f- + +For more information about transforming coordinates, see +\secref{ss:transforming} and \secref{ss:framesetasmapping}. For +details of how to handle missing coordinates, see +\secref{ss:badcoordinates}. + +\subsection{\label{ss:howtotestforcelestial}\ldots Test if a WCS is a Celestial Coordinate System} + +The world coordinate system (WCS) currently associated with an image +may often be a celestial coordinate system, but this need not +necessarily be the case. For instance, instead of right ascension and +declination, an image might have a WCS with axes representing +wavelength and slit position, or maybe just plain old pixels. + +c+ +If you have obtained a WCS calibration for an image, as in +\secref{ss:howtoreadwcs}, in the form of a pointer ``wcsinfo'' to a +FrameSet, then you may determine if the current coordinate system is a +celestial one or not, as follows: +c- +f+ +If you have obtained a WCS calibration for an image, as in +\secref{ss:howtoreadwcs}, in the form of a pointer WCSINFO to a +FrameSet, then you may determine if the current coordinate system is a +celestial one or not, as follows: +f- + +c+ +\begin{small} +\begin{terminalv} +AstFrame *frame; +int issky; + +... + +/* Obtain a pointer to the current Frame and determine if it is a + SkyFrame. */ +frame = astGetFrame( wcsinfo, AST__CURRENT ); +issky = astIsASkyFrame( frame ); +frame = astAnnul( frame ); +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + INTEGER FRAME + LOGICAL ISSKY + + ... + +* Obtain a pointer to the current Frame and determine if it is a +* SkyFrame. + FRAME = AST_GETFRAME( WCSINFO, AST__CURRENT, STATUS ) + ISSKY = AST_ISASKYFRAME( FRAME, STATUS ) + CALL AST_ANNUL( FRAME, STATUS ) +\end{terminalv} +\end{small} +f- + +c+ +This will set ``issky'' to 1 if the WCS is a celestial coordinate +system, and to zero otherwise. +c- +f+ +This will set ISSKY to .TRUE.\ if the WCS is a celestial coordinate +system, and to .FALSE.\ otherwise. +f- + +\subsection{\label{ss:howtotestforspectral}\ldots Test if a WCS is a Spectral Coordinate System} +Testing for a spectral coordinate system is basically the same as testing +for a celestial coordinate system (see the previous section). The one +difference is that you use the +c+ +astIsASpecFrame function +c- +f+ +AST\_ISASPECFRAME routine +f- +in place of the +c+ +astIsASkyFrame function. +c- +f+ +AST\_ISASKYFRAME routine. +f- + +\subsection{\label{ss:howtoformatcoordinates}\ldots Format Coordinates for Display} + +c+ +Once you have converted pixel coordinates into world coordinates +(\secref{ss:howtotransform}), you may want to format them as text +before displaying them. Typically, this would convert from (say) +radians into something more comprehensible. Using the FrameSet pointer +``wcsinfo'' obtained in \secref{ss:howtoreadwcs} and a pair of world +coordinates ``xw'' and ``yw'' (\emph{e.g.}\ see +\secref{ss:howtotransform}), you could proceed as follows: +c- +f+ +Once you have converted pixel coordinates into world coordinates +(\secref{ss:howtotransform}), you may want to format them as text +before displaying them. Typically, this would convert from (say) +radians into something more comprehensible. Using the FrameSet pointer +WCSINFO obtained in \secref{ss:howtoreadwcs} and a pair of world +coordinates XW and YW (\emph{e.g.}\ see \secref{ss:howtotransform}), +you could proceed as follows: +f- + +c+ +\begin{small} +\begin{terminalv} +#include +const char *xtext, *ytext; +double xw, yw; + +... + +xtext = astFormat( wcsinfo, 1, xw ); +ytext = astFormat( wcsinfo, 2, yw ); + +(void) printf( "Position = %s, %s\n", xtext, ytext ); +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + CHARACTER * ( 20 ) XTEXT, YTEXT + DOUBLE PRECISION XW, YW + + ... + + XTEXT = AST_FORMAT( WCSINFO, 1, XW, STATUS ) + YTEXT = AST_FORMAT( WCSINFO, 2, YW, STATUS ) + + WRITE ( *, 199 ) XTEXT, YTEXT + 199 FORMAT( 'Position = ', A, ', ', A ) +\end{terminalv} +\end{small} +f- + +c+ +Here, the second argument to astFormat is the axis number. +c- +f+ +Here, the second argument to AST\_FORMAT is the axis number. +f- + +With celestial coordinates, this will usually result in sexagesimal +notation, such as ``12:34:56.7''. However, the same method may be +applied to any type of coordinates and appropriate formatting will be +employed. + +For more information about formatting coordinate values and how to +control the style of formatting used, see +\secref{ss:formattingaxisvalues} and +\secref{ss:formattingskyaxisvalues}. If necessary, also see +\secref{ss:normalising} for details of how to ``normalise'' a set of +coordinates so that they lie within the standard range (\emph{e.g.}\ 0 +to 24 hours for right ascension and $\pm 90^\circ$ for +declination). + +\subsection{\ldots Display Coordinates as they are Transformed} + +c+ +In addition to formatting coordinates as part of a program's output, +you may also want to examine coordinate values while debugging your +program. To save time, you can ``eavesdrop'' on the coordinate values +being processed every time they are transformed. For example, when +using the FrameSet pointer ``wcsinfo'' obtained in +\secref{ss:howtoreadwcs} to transform coordinates +(\secref{ss:howtotransform}), you could inspect the coordinate values +as follows: +c- +f+ +In addition to formatting coordinates as part of a program's output, +you may also want to examine coordinate values while debugging your +program. To save time, you can ``eavesdrop'' on the coordinate values +being processed every time they are transformed. For example, when +using the FrameSet pointer WCSINFO obtained in +\secref{ss:howtoreadwcs} to transform coordinates +(\secref{ss:howtotransform}), you could inspect the coordinate values +as follows: +f- + +c+ +\begin{small} +\begin{terminalv} +astSet( wcsinfo, "Report=1" ); +astTran2( wcsinfo, N, xpixel, ypixel, 1, xworld, yworld ); +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + CALL AST_SET( WCSINFO, 'Report=1', STATUS ) + CALL AST_TRAN2( WCSINFO, N, XPIXEL, YPIXEL, .TRUE., + : XWORLD, YWORLD, STATUS ) +\end{terminalv} +\end{small} +f- + +By setting the FrameSet's Report attribute to 1, coordinate +transformations are automatically displayed on the program's standard +output stream, appropriately formatted, for example: + +\begin{terminalv} +(42.1087, 20.2717) --> (2:06:03.0, 34:22:39) +(43.0197, 21.1705) --> (2:08:20.6, 35:31:24) +(43.9295, 22.0716) --> (2:10:38.1, 36:40:09) +(44.8382, 22.9753) --> (2:12:55.6, 37:48:55) +(45.7459, 23.8814) --> (2:15:13.1, 38:57:40) +(46.6528, 24.7901) --> (2:17:30.6, 40:06:25) +(47.5589, 25.7013) --> (2:19:48.1, 41:15:11) +(48.4644, 26.6149) --> (2:22:05.6, 42:23:56) +(49.3695, 27.5311) --> (2:24:23.1, 43:32:41) +(50.2742, 28.4499) --> (2:26:40.6, 44:41:27) +\end{terminalv} + +For a complete description of the Report attribute, see its entry in +\appref{ss:attributedescriptions}. For further details of how to set +and enquire attribute values, see \secref{ss:settingattributes} and +\secref{ss:gettingattributes}. + +\subsection{\ldots Read Coordinates Entered by a User} + +In addition to writing out coordinate values generated by your program +(\secref{ss:howtoformatcoordinates}), you may also need to accept +coordinates entered by a user, or perhaps read from a file. In this +case, you will probably want to allow ``free-format'' input, so that +the user has some flexibility in the format that can be used. You will +probably also want to detect any typing errors. + +c+ +Let's assume that you want to read a number of lines of text, each +containing the world coordinates of a single point, and to split each +line into individual numerical coordinate values. Using the FrameSet +pointer ``wcsinfo'' obtained earlier (\secref{ss:howtoreadwcs}), you +could proceed as follows: +c- +f+ +Let's assume that you want to read a number of lines of text, each +containing the world coordinates of a single point, and to split each +line into individual numerical coordinate values. Using the FrameSet +pointer WCSINFO obtained earlier (\secref{ss:howtoreadwcs}), you could +proceed as follows: +f- + +c+ +\begin{small} +\begin{terminalv} +#include +char *t; +char text[ MAXCHARS + 2 ]; +double coord[ 10 ]; +int iaxis, n, naxes; + +... + +/* Obtain the number of coordinate axes (if not already known). */ +naxes = astGetI( wcsinfo, "Naxes" ); + +/* Loop to read each line of input text, in this case from the + standard input stream (your programming environment will probably + provide a better way of reading text than this). Set the pointer + "t" to the start of each line read. */ +while ( t = fgets( text, MAXCHARS + 2, stdin ) ) { + +/* Attempt to read a coordinate for each axis. */ + for ( iaxis = 1; iaxis <= naxes; iaxis++ ) { + n = astUnformat( wcsinfo, iaxis, t, &coord[ iaxis - 1 ] ); + +/* If nothing was read and this is not the first axis or the + end-of-string, try stepping over a separator and reading again. */ + if ( !n && ( iaxis > 1 ) && *t ) + n = astUnformat( wcsinfo, iaxis, ++t, &coord[ iaxis - 1 ] ); + +/* Quit if nothing was read, otherwise move on to the next coordinate. */ + if ( !n ) break; + t += n; + } + +/* Test for the possible errors that may occur... */ + +/* Error detected by AST (a message will have been issued). */ + if ( !astOK ) { + break; + +/* Error in input data at character t[n]. */ + } else if ( *t || !n ) { + + break; + + } else { + + } +} +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + CHARACTER TEXT * ( 80 ) + DOUBLE PRECISION COORD( 10 ) + INTEGER IAXIS, N, NAXES, T + + ... + +* Obtain the number of coordinate axes (if not already known). + NAXES = AST_GETI( WCSINFO, 'Naxes', STATUS ) + +* Loop to read each line of input text, in this case from the +* standard input channel (your programming environment will probably +* provide a better way of reading text than this). Set the index T to +* the start of each line read. + 2 CONTINUE + READ( *, '(A)', END=99 ) TEXT + T = 1 + +* Attempt to read a coordinate for each axis. + DO 3 IAXIS = 1, NAXES + N = AST_UNFORMAT( WCSINFO, IAXIS, TEXT( T : ), COORD( IAXIS ), + : STATUS ) + +* If nothing was read and this is not the first axis and the end of +* the text has not been reached, try stepping over a separator and +* reading again. + IF ( ( N .EQ. 0 ) .AND. ( IAXIS .GT. 1 ) .AND. + : ( T .LT. LEN( STRING ) ) ) THEN + T = T + 1 + N = AST_UNFORMAT( WCSINFO, IAXIS, TEXT( T : ), + COORD( IAXIS ), STATUS ) + END IF + +* Quit if nothing was read, otherwise move on to the next coordinate. + IF ( N .EQ. 0 ) GO TO 4 + T = T + N + 3 CONTINUE + 4 CONTINUE + +* Test for the possible errors that may occur... + +* Error detected by AST (a message will have been issued). + IF ( STATUS .NE. 0 ) THEN + GO TO 99 + +* Error in input data at character TEXT( T + N : T + N ). + ELSE IF ( ( T .LT. LEN( STRING ) ) .OR. ( N .EQ. 0 ) ) THEN + + GO TO 99 + + ELSE + + END IF + +* Return to read the next input line. + GO TO 2 + 99 CONTINUE +\end{terminalv} +\end{small} +f- + +This algorithm has the advantage of accepting free-format input in +whatever style is appropriate for the world coordinates in use (under +the control of the FrameSet whose pointer you provide). For example, +wavelength values might be read as floating point numbers +(\emph{e.g.}\ ``1.047'' or ``4787''), whereas celestial positions +could be given in sexagesimal format (\emph{e.g.}\ ``12:34:56'' or +``12~34.5'') and would be converted into radians. Individual +coordinate values may be separated by white space and/or any +non-ambiguous separator character, such as a comma. + +c+ +For more information on reading coordinate values using the +astUnformat function, see \secref{ss:unformattingaxisvalues}. For +details of how sexagesimal formats are handled, and the forms of input +that may be used for celestial coordinates, see +\secref{ss:unformattingskyaxisvalues}. +c- +f+ +For more information on reading coordinate values using the +AST\_UNFORMAT function, see \secref{ss:unformattingaxisvalues}. For +details of how sexagesimal formats are handled, and the forms of input +that may be used for for celestial coordinates, see +\secref{ss:unformattingskyaxisvalues}. +f- + +\subsection{\label{ss:howtocreatenewwcs}\ldots Create a New WCS Calibration} + +This section describes how to add a WCS calibration to a data set which you +are creating from scratch, rather than modifying an existing data set. + +In most common cases, the simplest way to create a new WCS calibration +from scratch is probably to create a set of strings describing the +required calibration in terms of the keywords used by the FITS WCS +standard, and then convert these strings into an AST FrameSet describing +the calibration. This FrameSet can then be used for many other purposes, or +simply stored in the data set. + +The full FITS-WCS standard is quite involved, currently running to four +separate papers, but the basic kernel is quite simple, involving the +following keywords (all of which end with an integer axis index, +indicated below by $$): + +\begin{description} +\item[CRPIX]\mbox{}\\ +hold the pixel coordinates at a reference point +\item[CRVAL]\mbox{}\\ +hold the corresponding WCS coordinates at the reference point +\item[CTYPE]\mbox{}\\ +name the quantity represented by the WCS axes, together with the +projection algorithm used to convert the scaled and rotated pixel coordinates +to WCS coordinates. +\item[CD\_]\mbox{}\\ +a set of keywords which specify the elements of a matrix. This matrix scales +pixel offsets from the reference point into the offsets required as input +by the projection algorithm specified by the CTYPE keywords. This matrix +specifies the scale and rotation of the image. If there is no rotation +the off-diagonal elements of the matrix (\emph{e.g.} CD1\_2 and +CD2\_1) can be omitted. +\end{description} + +As an example consider the common case of a simple 2D image of the sky in +which north is parallel to the second pixel axis and east parallel to the +(negative) first pixel axis. The image scale is 1.2 arc-seconds per pixel +on both axes, and the image is presumed to have been obtained with a +tangent plane projection. Furthermore, it is known that pixel coordinates +(100.5,98.4) correspond to an RA of 11:00:10 and a Dec. of -23:26:02. +A suitable set of FITS-WCS header cards could be: + +\begin{small} +\begin{terminalv} +CTYPE1 = 'RA---TAN' / Axis 1 represents RA with a tan projection +CTYPE2 = 'DEC--TAN' / Axis 2 represents Dec with a tan projection +CRPIX1 = 100.5 / Pixel coordinates of reference point +CRPIX2 = 98.4 / Pixel coordinates of reference point +CRVAL1 = 165.04167 / Degrees equivalent of "11:00:10" hours +CRVAL2 = -23.433889 / Decimal equivalent of "-23:26:02" degrees +CD1_1 = -0.0003333333 / Decimal degrees equivalent of -1.2 arc-seconds +CD2_2 = 0.0003333333 / Decimal degrees equivalent of 1.2 arc-seconds +\end{terminalv} +\end{small} + +Notes: +\begin{itemize} +\item a FITS header card begins with the keyword name starting at column 1, +has an equals sign in column 9, and the keyword value in columns 11 to 80. +\item string values must be enclosed in single quotes. +\item celestial longitude and latitude must both be specified in decimal degrees. +\item the CD1\_1 value is negative to indicate that RA increases as the +first pixel axis decreases. +\item the (RA,Dec) coordinates will be taken as ICRS coordinates. For FK5 +you should add: + +\begin{small} +\begin{terminalv} +RADESYS = 'FK5' +EQUINOX = 2005.6 +\end{terminalv} +\end{small} + +The EQUINOX value defaults to J2000.0 if omitted. FK4 can also be used in +place of FK5, in which case EQUINOX defaults to B1950.0. + +\end{itemize} + +Once you have created these FITS-WCS header card strings, you should +store them in a FitsChan and then read the corresponding FrameSet from the +FitsChan. How to do this is described in \secref{ss:howtoreadwcs}. + +Having created the WCS calibration, you may want to store it in a data +file. How to do this is described in \secref{ss:howtowritewcs}).\footnote{If +you are writing the WCS calibration to a FITS file you obviously +have the choice of storing the FITS-WCS cards directly.} + +If the required WCS calibration cannot be described as a set of FITS-WCS +headers, then a different approach is necessary. In this case, you should +first create a Frame describing pixel coordinates, and store this Frame +in a new FrameSet. You should then create a new Frame describing the +world coordinate system. This Frame may be a specific subclass of Frame such +as a SkyFrame for celestial coordinates, a SpecFrame for spectral +coordinates, a Timeframe for time coordinates, or a CmpFrame for a combination +of different coordinates. +You also need to create a suitable Mapping which transforms pixel +coordinates into world coordinates. AST provides many different types of +Mappings, all of which can be combined together in arbitrary fashions to +create more complicated Mappings. The WCS Frame should then be added into +the FrameSet, using the Mapping to connect the WCS Frame with the pixel +Frame. + +\subsection{\label{ss:howtomodifywcs}\ldots Modify a WCS Calibration} + +The usual reason for wishing to modify the WCS calibration associated +with a dataset is that the data have been geometrically transformed in +some way (here, we will assume a 2-dimensional image dataset). This +causes the image features (stars, galaxies, \emph{etc.}) to move with +respect to the grid of pixels which they occupy, so that any +coordinate systems previously associated with the image become +invalid. + +To correct for this, it is necessary to set up a Mapping which +expresses the positions of image features in the new data grid in +terms of their positions in the old grid. In both cases, the grid +coordinates we use will have the first pixel centred at (1,1) with +each pixel being a unit square. + +c+ +AST allows you to correct for any type of geometrical transformation +in this way, so long as a suitable Mapping to describe it can be +constructed. For purposes of illustration, we will assume here that +the new image coordinates ``xnew'' and ``ynew'' can be expressed in +terms of the old coordinates ``xold'' and ``yold'' as follows: +c- +f+ +AST allows you to correct for any type of geometrical transformation +in this way, so long as a suitable Mapping to describe it can be +constructed. For purposes of illustration, we will assume here that +the new image coordinates XNEW and YNEW can be expressed in terms of +the old coordinates XOLD and YOLD as follows: +f- + +c+ +\begin{small} +\begin{terminalv} +double xnew, xold, ynew, yold; +double m[ 4 ], z[ 2 ]; + +... + +xnew = xold * m[ 0 ] + yold * m[ 1 ] + z[ 0 ]; +ynew = xold * m[ 2 ] + yold * m[ 3 ] + z[ 1 ]; +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + DOUBLE PRECISION XNEW, XOLD, YNEW, YOLD + DOUBLE PRECISION M( 4 ), Z( 2 ) + + ... + + XNEW = XOLD * M( 1 ) + YOLD * M( 2 ) + Z( 1 ) + YNEW = XOLD * M( 3 ) + YOLD * M( 4 ) + Z( 2 ) +\end{terminalv} +\end{small} +f- + +c+ +where ``m'' is a 2$\times$2 transformation matrix and ``z'' represents +a shift of origin. This is therefore a general linear coordinate +transformation which can represent displacement, rotation, +magnification and shear. +c- +f+ +where M is a 2$\times$2 transformation matrix and Z represents a shift +of origin. This is therefore a general linear coordinate +transformation which can represent displacement, rotation, +magnification and shear. +f- + +In AST, it can be represented by concatenating two Mappings. The first +is a MatrixMap, which implements the matrix multiplication. The second +is a WinMap, which linearly transforms one coordinate window on to +another, but will be used here simply to implement the shift of +origin (alternatively, a ShiftMap could have been used in place of a +WinMap). These Mappings may be constructed and concatenated as follows: + +c+ +\begin{small} +\begin{terminalv} +AstCmpMap *newmap; +AstMatrixMap *matrixmap; +AstWinMap *winmap; + +... + +/* The MatrixMap may be constructed directly from the matrix "m". */ +matrixmap = astMatrixMap( 2, 2, 0, m, "" ); + +/* For the WinMap, we set up the coordinates of the corners of a unit + square (window) and then the same square shifted by the required + amount. */ +{ + double ina[] = { 0.0, 0.0 }; + double inb[] = { 1.0, 1.0 }; + double outa[] = { z[ 0 ], z[ 1 ] }; + double outb[] = { 1.0 + z[ 0 ], 1.0 + z[ 1 ] }; + +/* The WinMap will then implement this shift. */ + winmap = astWinMap( 2, ina, inb, outa, outb, "" ); +} + +/* Join the two Mappings together, so that they are applied one after + the other. */ +newmap = astCmpMap( matrixmap, winmap, 1, "" ); +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + DOUBLE PRECISION INA( 2 ), INB( 2 ), OUTA( 2 ), OUTB( 2 ) + INTEGER MATRIXMAP, WINMAP + + ... + +* Set up the corners of a unit square. + DATA INA / 2 * 0.0D0 / + DATA INB / 2 * 1.0D0 / + +* The MatrixMap may be constructed directly from the matrix M. + MATRIXMAP = AST_MATRIXMAP( 2, 2, 0, M, ' ', STATUS ) + +* For the WinMap, we take the coordinates of the corners of a unit +* square (window) and then shift them by the required amounts. + OUTA( 1 ) = INA( 1 ) + Z( 1 ) + OUTA( 2 ) = INA( 2 ) + Z( 2 ) + OUTB( 1 ) = INB( 1 ) + Z( 1 ) + OUTB( 2 ) = INB( 2 ) + Z( 2 ) + +* The WinMap will then implement this shift. + WINMAP = AST_WINMAP( 2, INA, INB, OUTA, OUTB, ' ', STATUS ) + +* Join the two Mappings together, so that they are applied one after +* the other. + NEWMAP = AST_CMPMAP( MATRIXMAP, WINMAP, 1, ' ', STATUS ) +\end{terminalv} +\end{small} +f- + +You might, of course, create any other form of Mapping depending on +the type of geometrical transformation involved. For an overview of +the Mappings provided by AST, see \secref{ss:mappingselection}, and +for a description of the capabilities of each class of Mapping, see +its entry in \appref{ss:classdescriptions}. For an overview of how +individual Mappings may be combined, see \secref{ss:cmpmapoverview} +(\secref{ss:cmpmaps} gives more details). + +c+ +Assuming you have obtained a WCS calibration for your original image +in the form of a pointer to a FrameSet, ``wcsinfo1'' +(\secref{ss:howtoreadwcs}), the Mapping created above may be used to +produce a calibration for the new image as follows: +c- +f+ +Assuming you have obtained a WCS calibration for your original image +in the form of a pointer to a FrameSet, WCSINFO1 +(\secref{ss:howtoreadwcs}), the Mapping created above may be used to +produce a calibration for the new image as follows: +f- + +c+ +\begin{small} +\begin{terminalv} +AstFrameSet *wcsinfo1, *wcsinfo2; + +... + +/* If necessary, make a copy of the WCS calibration, since we are + about to alter it. */ +wcsinfo2 = astCopy( wcsinfo1 ); + +/* Re-map the base Frame so that it refers to the new data grid + instead of the old one. */ +astRemapFrame( wcsinfo2, AST__BASE, newmap ); +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + INTEGER WCSINFO1, WCSINFO2 + + ... + +* If necessary, make a copy of the WCS calibration, since we are +* about to alter it. + WCSINFO2 = AST_COPY( WCSINFO1, STATUS ) + +* Re-map the base Frame so that it refers to the new data grid +* instead of the old one. + CALL AST_REMAPFRAME( WCSINFO2, AST__BASE, NEWMAP, STATUS ) +\end{terminalv} +\end{small} +f- + +c+ +This will produce a pointer, ``wcsinfo2'', to a new FrameSet in which +all the coordinate systems associated with your original image are +modified so that they are correctly registered with the new image +instead. +c- +f+ +This will produce a pointer, WCSINFO2, to a new FrameSet in which all +the coordinate systems associated with the original image are modified +so that they are correctly registered with your new image instead. +f- + +For more information about re-mapping the Frames within a FrameSet, +see \secref{ss:remapframe}. Also see \secref{ss:wcsprocessingexample} +for a similar example to the above, applicable to the case of reducing +the size of an image by binning. + +\subsection{\label{ss:howtowritewcs}\ldots Write a Modified WCS Calibration to a Dataset} + +If you have modified the WCS calibration associated with a dataset, +such as in the example above (\secref{ss:howtomodifywcs}), then you +will need to write the modified version out along with any new data. + +In the same way as when reading a WCS calibration +(\secref{ss:howtoreadwcs}), how you do this will depend on your data +system, but we will assume that you wish to generate a set of FITS +header cards that can be stored with the data. You should usually make +preparations for doing this when you first read the WCS calibration +from your input dataset by modifying the example given in +\secref{ss:howtoreadwcs} as follows: + +c+ +\begin{small} +\begin{terminalv} +AstFitsChan *fitschan1; +AstFrameSet *wcsinfo1; +const char *encode; + +... + +/* Create an input FitsChan and fill it with FITS header cards. Note, + if you have all the header cards in a single string, use astPutCards in + place of astPutFits. */ +fitschan1 = astFitsChan( NULL, NULL, "" ); +for ( icard = 0; icard < ncard; icard++ ) astPutFits( fitschan1, cards[ icard ], 0 ); + +/* Note which encoding has been used for the WCS information. */ +encode = astGetC( fitschan1, "Encoding" ); + +/* Rewind the input FitsChan and read the WCS information from it. */ +astClear( fitschan1, "Card" ); +wcsinfo1 = astRead( fitschan1 ); +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} + INTEGER FITSCHAN1, WCSINFO1 + CHARACTER * ( 20 ) ENCODE + + ... + +* Create an input FitsChan and fill it with FITS header cards. Note, +* if you have all the header cards in a single string, use AST_PUTCARDS in +* place of AST_PUTFITS. + FITSCHAN1 = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) + DO 1 ICARD = 1, NCARD + CALL AST_PUTFITS( FITSCHAN1, CARDS( ICARD ), .FALSE., STATUS ) + 1 CONTINUE + +* Note which encoding has been used for the WCS information. + ENCODE = AST_GETC( FITSCHAN1, 'Encoding', STATUS ); + +* Rewind the input FitsChan and read the WCS information from it. + CALL AST_CLEAR( FITSCHAN1, 'Card', STATUS ) + WCSINFO1 = AST_READ( FITSCHAN1, STATUS ) +\end{terminalv} +\end{small} +f- + +c+ +Note how we have added an enquiry to determine how the WCS information +is encoded in the input FITS cards, storing a pointer to the resulting +string in the ``encode'' variable. This must be done \textbf{before} +actually reading the WCS calibration. +c- +f+ +Note how we have added an enquiry to determine how the WCS information +is encoded in the input FITS cards, storing the resulting string in +the ENCODE variable. This must be done \textbf{before} actually reading +the WCS calibration. +f- + +c+ +\emph{(\textbf{N.B.}\ If you will be making extensive use of astGetC in +your program, then you should allocate a buffer and make a copy of +this string, because the pointer returned by astGetC will only remain +valid for 50 invocations of the function, and you will need to use the +Encoding value again later on.)} +c- + +c+ +Once you have produced a modified WCS calibration for the output +dataset (\emph{e.g.}\ \secref{ss:howtomodifywcs}), in the form of a +FrameSet identified by the pointer ``wcsinfo2'', you can produce a new +FitsChan containing the output FITS header cards as follows: +c- +f+ +Once you have produced a modified WCS calibration for the output +dataset (\emph{e.g.}\ \secref{ss:howtomodifywcs}), in the form of a +FrameSet identified by the pointer WCSINFO2, you can produce a new +FitsChan containing the output FITS header cards as follows: +f- + +c+ +\small +\begin{terminalv} +AstFitsChan *fitschan2; +AstFrameSet *wcsinfo2; + +... + +/* Make a copy of the input FitsChan, AFTER the WCS information has + been read from it. This will propagate all the input FITS header + cards, apart from those describing the input WCS calibration. */ +fitschan2 = astCopy( fitschan1 ); + +/* If necessary, make modifications to the cards in "fitschan2" + (e.g. you might need to change NAXIS1, NAXIS2, etc., to account for + a change in image size). You probably only need to do this if your + data system does not provide these facilities itself. */ +
    + +/* Alternatively, if your data system handles the propagation of FITS + header cards to the output dataset for you, then simply create an + empty FitsChan to contain the output WCS information alone. +fitschan2 = astFitsChan( NULL, NULL, "" ); +*/ + +/* Rewind the new FitsChan (if necessary) and attempt to write the + output WCS information to it using the same encoding method as the + input dataset. */ +astSet( fitschan2, "Card=1, Encoding=%s", encode ); +if ( !astWrite( fitschan2, wcsinfo2 ) ) { + +/* If this didn't work (the WCS FrameSet has become too complex), then + use the native AST encoding instead. */ + astSet( fitschan2, "Encoding=NATIVE" ); + (void) astWrite( fitschan2, wcsinfo2 ); +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FITSCHAN2, JUNK, WCSINFO2 + + ... + +* Make a copy of the input FitsChan, AFTER the WCS information has +* been read from it. This will propagate all the input FITS header +* cards, apart from those describing the WCS calibration. + FITSCHAN2 = AST_COPY( FITSCHAN1, STATUS ) + +* If necessary, make modifications to the cards in FITSCHAN2 +* (e.g. you might need to change NAXIS1, NAXIS2, etc., to account for +* a change in image size). You probably only need to do this if your +* data system does not provide these facilities itself. +
    + +* Alternatively, if your data system handles the propagation of FITS +* header cards to the output dataset for you, then simply create an +* empty FitsChan to contain the output WCS information alone. +* FITSCHAN2 = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) + +* Rewind the new FitsChan (if necessary) and attempt to write the +* output WCS information to it using the same encoding method as the +* input dataset. + CALL AST_SET( FITSCHAN2, 'Card=1, Encoding=' // ENCODE, STATUS ) + IF ( AST_WRITE( FITSCHAN2, WCSINFO2, STATUS ) .EQ. 0 ) THEN + +* If this didn't work (the WCS FrameSet has become too complex), then +* use the native AST encoding instead. + CALL AST_SETC( FITSCHAN2, 'Encoding', 'NATIVE', STATUS ); + JUNK = AST_WRITE( FITSCHAN2, WCSINFO2, STATUS ); + END IF +\end{terminalv} +\normalsize +f- + +For details of how to modify the contents of the output FitsChan in +other ways, such as by adding, over-writing or deleting header cards, +see \secref{ss:addressingfitscards}, \secref{ss:addingmulticards}, \secref{ss:addingfitscards} and +\secref{ss:findingandchangingfits}. + +Once you have assembled the output FITS cards, you may retrieve them +from the FitsChan that contains them as follows: + +c+ +\small +\begin{terminalv} +#include +char card[ 81 ]; + +... + +astClear( fitschan2, "Card" ); +while ( astFindFits( fitschan2, "%f", card, 1 ) ) (void) printf( "%s\n", card ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 80 ) CARD + + ... + + CALL AST_CLEAR( FITSCHAN2, 'Card', STATUS ) + 5 CONTINUE + IF ( AST_FINDFITS( FITSCHAN2, '%f', CARD, .TRUE., STATUS ) ) THEN + WRITE ( *, '(A)' ) CARD + GO TO 5 + END IF +\end{terminalv} +\normalsize +f- + +c+ +Here, we have simply written each card to the standard output stream, +but you would obviously replace this with a function invocation to +store the cards in your output dataset. +c- +f+ +Here, we have simply written each card to the standard output unit, +but you would obviously replace this with a subroutine call to store +the cards in your output dataset. +f- + +c+ +For data systems that do not use FITS header cards, a different +approach may be needed, possibly involving use of a Channel or XmlChan +(\secref{ss:channels}) rather than a FitsChan. In the case of the +Starlink NDF data format, for example, all of the above may be +replaced by a single call to the function +\xref{ndfPtwcs}{sun33}{ndfPtwcs}---see \xref{SUN/33}{sun33}{}. The +whole process can probably be encapsulated in a similar way for most +data systems, whether they use FITS header cards or not. +c- +f+ +For data systems that do not use FITS header cards, a different +approach may be needed, possibly involving use of a Channel or XmlChan +(\secref{ss:channels}) rather than a FitsChan. In the case of the +Starlink NDF data format, for example, all of the above may be +replaced by a single call to the routine +\xref{NDF\_PTWCS}{sun33}{NDF_PTWCS}---see \xref{SUN/33}{sun33}{}. The +whole process can probably be encapsulated in a similar way for most +other data systems, whether they use FITS header cards or not. +f- + +For an overview of how to propagate WCS information through data +processing steps, see \secref{ss:propagatingwcsinformation}. For more +information about writing WCS information to FitsChans, see +\secref{ss:writingnativefits} and \secref{ss:writingforeignfits}. For +information about the options for encoding WCS information in FITS +header cards, see \secref{ss:nativeencoding}, +\secref{ss:foreignencodings}, and the description of the Encoding +attribute in \appref{ss:attributedescriptions}. For a complete +understanding of FitsChans and their use with FITS header cards, you +should read \secref{ss:nativefits} and \secref{ss:foreignfits}. + +\subsection{\label{ss:howtoplotgrid}\ldots Display a Graphical Coordinate Grid} + + A common requirement when displaying image data is to plot an + associated coordinate grid (\emph{e.g.}\ Figure~\ref{fig:overgrid}) + over the displayed image. + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/overgrid_bw} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/overgrid_bw} +f- + \caption[An example of a displayed image with a coordinate grid + plotted over it.]{An example of a displayed image with a coordinate grid + plotted over it.} + \label{fig:overgrid} + \end{center} + \end{figure} + +c+ +The use of AST in such circumstances is independent of the underlying +graphics system, so starting up the graphics system, setting up a +coordinate system, displaying the image, and closing down afterwards +can all be done using the graphics functions you would normally use. +c- +f+ +The use of AST in such circumstances is independent of the underlying +graphics system, so starting up the graphics system, setting up a +coordinate system, displaying the image, and closing down afterwards +can all be done using the graphics routines you would normally use. +f- + +c+ +However, displaying an image at a precise location can be a little +fiddly with some graphics systems, and obviously the grid drawn by AST +will not be accurately registered with the image unless this is done +correctly. In the following template, we therefore illustrate both +steps, basing the image display on the C interface to the PGPLOT +graphics package.\footnote{An interface is provided with AST that +allows it to use PGPLOT (\xref{SUN/15}{sun15}{}) for its graphics, +although interfaces to other graphics systems may also be written.} +Plotting a coordinate grid with AST then becomes a relatively minor +part of what is almost a complete graphics program. +c- +f+ +However, displaying an image at a precise location can be a little +fiddly with some graphics systems, and obviously the grid drawn by AST +will not be accurately registered with the image unless this is done +correctly. In the following template, we therefore illustrate both +steps, basing the image display on the PGPLOT graphics +package.\footnote{An interface is provided with AST that allows it to +use PGPLOT (\xref{SUN/15}{sun15}{}) for its graphics, although +interfaces to other graphics systems may also be written.} Plotting a +coordinate grid with AST then becomes a relatively minor part of what +is almost a complete graphics program. +f- + +c+ +Once again, we assume that a pointer, ``wcsinfo'', to a suitable +FrameSet associated with the image has already been obtained +(\secref{ss:howtoreadwcs}). +c- +f+ +Once again, we assume that a pointer, WCSINFO, to a suitable FrameSet +associated with the image has already been obtained +(\secref{ss:howtoreadwcs}). +f- + +c+ +\small +\begin{terminalv} +#include "cpgplot.h" +AstPlot *plot; +const float *data; +float hi, lo, scale, x1, x2, xleft, xright, xscale; +float y1, y2, ybottom, yscale, ytop; +int nx, ny; + +... + +/* Access the image data, which we assume has dimension sizes "nx" and + "ny", and will be accessed via the "data" pointer. Also derive + limits for scaling it, which we assign to the variables "hi" and + "lo". */ + + +/* Open PGPLOT using the device given by environment variable + PGPLOT_DEV and check for success. */ +if( cpgbeg( 0, " ", 1, 1 ) == 1 ) { + +/* Clear the screen and ensure equal scales on both axes. */ + cpgpage(); + cpgwnad( 0.0f, 1.0f, 0.0f, 1.0f ); + +/* Obtain the extent of the plotting area (not strictly necessary for + PGPLOT, but possibly for other graphics systems). From this, derive + the display scale in graphics units per pixel so that the image + will fit within the display area. */ + cpgqwin( &x1, &x2, &y1, &y2 ); + xscale = ( x2 - x1 ) / nx; + yscale = ( y2 - y1 ) / ny; + scale = ( xscale < yscale ) ? xscale : yscale; + +/* Calculate the extent of the area in graphics units that the image + will occupy, so as to centre it within the display area. */ + xleft = 0.5f * ( x1 + x2 - nx * scale ); + xright = 0.5f * ( x1 + x2 + nx * scale ); + ybottom = 0.5f * ( y1 + y2 - ny * scale ); + ytop = 0.5f * ( y1 + y2 + ny * scale ); + +/* Set up a PGPLOT coordinate transformation matrix and display the + image data as a grey scale map (these details are specific to + PGPLOT). */ + { + float tr[] = { xleft - 0.5f * scale, scale, 0.0f, + ybottom - 0.5f * scale, 0.0f, scale }; + cpggray( data, nx, ny, 1, nx, 1, ny, hi, lo, tr ); + } + +/* BEGINNING OF AST BIT */ +/* ==================== */ +/* Store the locations of the bottom left and top right corners of the + region used to display the image, in graphics coordinates. */ + { + float gbox[] = { xleft, ybottom, xright, ytop }; + +/* Similarly, store the locations of the image's bottom left and top + right corners, in pixel coordinates -- with the first pixel centred + at (1,1). */ + double pbox[] = { 0.5, 0.5, nx + 0.5, ny + 0.5 }; + +/* Create a Plot, based on the FrameSet associated with the + image. This attaches the Plot to the graphics surface so that it + matches the displayed image. Specify that a complete set of grid + lines should be drawn (rather than just coordinate axes). */ + plot = astPlot( wcsinfo, gbox, pbox, "Grid=1" ); + } + +/* Optionally, we can now set other Plot attributes to control the + appearance of the grid. The values assigned here use the + colour/font indices defined by the underlying graphics system. */ + astSet( plot, "Colour(grid)=2, Font(textlab)=3" ); + +/* Use the Plot to draw the coordinate grid. */ + astGrid( plot ); + + + +/* Annul the Plot when finished (or use the astBegin/astEnd technique + shown earlier). */ + plot = astAnnul( plot ); + +/* END OF AST BIT */ +/* ============== */ + +/* Close down the graphics system. */ + cpgend(); +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION BBOX( 4 ) + INTEGER NX, NY, PGBEG, PLOT + REAL DATA( NX, NY ), GBOX( 4 ), HI, LO, SCALE, TR( 6 ) + REAL X1, X2, XLEFT, XRIGHT, Y1, Y2, YBOTTOM, YTOP + + ... + +* Access the image data, which we assume will be stored in the real +* 2-dimensional array DATA with dimension sizes NX and NY. Also +* derive limits for scaling it, which we assign to the variables HI +* and LO. + + +* Open PGPLOT using the device given by environment variable +* PGPLOT_DEV and check for success. + IF ( PGBEG( 0, ' ', 1, 1 ) .EQ. 1 ) THEN + +* Clear the screen and ensure equal scales on both axes. + CALL PGPAGE + CALL PGWNAD( 0.0, 1.0, 0.0, 1.0 ) + +* Obtain the extent of the plotting area (not strictly necessary for +* PGPLOT, but possibly for other graphics systems). From this, derive +* the display scale in graphics units per pixel so that the image +* will fit within the display area. + CALL PGQWIN( X1, X2, Y1, Y2 ) + SCALE = MIN( ( X2 - X1 ) / NX, ( Y2 - Y1 ) / NY ) + +* Calculate the extent of the area in graphics units that the image +* will occupy, so as to centre it within the display area. + XLEFT = 0.5 * ( X1 + X2 - NX * SCALE ) + XRIGHT = 0.5 * ( X1 + X2 + NX * SCALE ) + YBOTTOM = 0.5 * ( Y1 + Y2 - NY * SCALE ) + YTOP = 0.5 * ( Y1 + Y2 + NY * SCALE ) + +* Set up a PGPLOT coordinate transformation matrix and display the +* image data as a grey scale map (these details are specific to +* PGPLOT). + TR( 1 ) = XLEFT - 0.5 * SCALE + TR( 2 ) = SCALE + TR( 3 ) = 0.0 + TR( 4 ) = YBOTTOM - 0.5 * SCALE + TR( 5 ) = 0.0 + TR( 6 ) = SCALE + CALL PGGRAY( DATA, NX, NY, 1, NX, 1, NY, HI, LO, TR ) + +* BEGINNING OF AST BIT +* ==================== +* Store the locations of the bottom left and top right corners of the +* region used to display the image, in graphics coordinates. + GBOX( 1 ) = XLEFT + GBOX( 2 ) = YBOTTOM + GBOX( 3 ) = XRIGHT + GBOX( 4 ) = YTOP + +* Similarly, store the locations of the image's bottom left and top +* right corners, in pixel coordinates -- with the first pixel centred +* at (1,1). + BBOX( 1 ) = 0.5D0 + BBOX( 2 ) = 0.5D0 + BBOX( 3 ) = NX + 0.5D0 + BBOX( 4 ) = NY + 0.5D0 + +* Create a Plot, based on the FrameSet associated with the +* image. This attaches the Plot to the graphics surface so that it +* matches the displayed image. Specify that a complete set of grid +* lines should be drawn (rather than just coordinate axes). + PLOT = AST_PLOT( WCSINFO, GBOX, BBOX, 'Grid=1', STATUS ) + +* Optionally, we can now set other Plot attributes to control the +* appearance of the grid. The values assigned here use the +* colour/font indices defined by the underlying graphics system. + CALL AST_SET( PLOT, 'Colour(grid)=2, Font(textlab)=3', STATUS ) + +* Use the Plot to draw the coordinate grid. + CALL AST_GRID( PLOT, STATUS ) + + + +* Annul the Plot when finished (or use the AST_BEGIN/AST_END +* technique shown earlier). + CALL AST_ANNUL( PLOT, STATUS ) + +* END OF AST BIT +* ============== + +* Close down the graphics system. + CALL PGEND + END IF +\end{terminalv} +\normalsize +f- + +Note that once you have set up a Plot which is aligned with a +displayed image, you may also use it to generate further graphical +output of your own, specified in the image's world coordinate system +(such as markers to represent astronomical objects, annotation, +\emph{etc.}). There is also a range of Plot attributes which gives +control over most aspects of the output's appearance. For details of +the facilities available, see \secref{ss:plots} and the description of +the Plot class in \appref{ss:classdescriptions}. + +For details of how to build a graphics program which uses PGPLOT, see +\secref{ss:howtobuild} and the description of the ast\_link command in +\appref{ss:commanddescriptions}. + +\subsection{\label{ss:howtoswitchgrid}\ldots Switch to Plot a Different Celestial Coordinate Grid} + +c+ +Once you have set up a Plot to draw a coordinate grid +(\secref{ss:howtoplotgrid}), it is a simple matter to change things so +that the grid represents a different celestial coordinate system. For +example, after creating the Plot with astPlot, you could use: +c- +f+ +Once you have set up a Plot to draw a coordinate grid +(\secref{ss:howtoplotgrid}), it is a simple matter to change things so +that the grid represents a different celestial coordinate system. For +example, after creating the Plot with AST\_PLOT, you could use: +f- + +c+ +\small +\begin{terminalv} +astSet( plot, "System=Galactic" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( PLOT, 'System=Galactic', STATUS ) +\end{terminalv} +\normalsize +f- +or: +c+ +\small +\begin{terminalv} +astSet( plot, "System=FK5, Equinox=J2010" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( PLOT, 'System=FK5, Equinox=J2010', STATUS ) +\end{terminalv} +\normalsize +f- + +and any axes and/or grid drawn subsequently would represent the new +celestial coordinate system you specified. Note, however, that this +will only work if the original grid represented celestial coordinates +of some kind (see \secref{ss:howtotestforcelestial} for how to +determine if this is the case\footnote{Note that the methods applied +to a FrameSet may be used equally well with a Plot.}). If it did not, +you will get an error message. + +For more information about the celestial coordinate systems available, +see the descriptions of the System, Equinox and Epoch attributes in +\appref{ss:attributedescriptions}. + +\subsection{\ldots Give a User Control Over the Appearance of a Plot} + +The idea of using a Plot's attributes to control the appearance of the +graphical output it produces (\secref{ss:howtoplotgrid} and +\secref{ss:howtoswitchgrid}) can easily be extended to allow the user +of a program complete control over such matters. + +For instance, if the file ``plot.config'' contains a series of +plotting options in the form of Plot attribute assignments (see below +for an example), then we could create a Plot and implement these +assignments before producing the graphical output as follows: + +c+ +\small +\begin{terminalv} +#include +#define MAXCHARS 120 +FILE *stream; +char line[ MAXCHARS + 2 ]; +int base; + +... + +/* Create a Plot and define the default appearance of the graphical + output it will produce. */ +plot = astPlot( wcsinfo, gbox, pbox, + "Grid=1, Colour(grid)=2, Font(textlab)=3" ); + +/* Obtain the value of any Plot attributes we want to preserve. */ +base = astGetI( plot, "Base" ); + +/* Open the plot configuration file, if it exists. Read each line of + text and use it to set new Plot attribute values. Close the file + when done. */ +if ( stream = fopen( "plot.config", "r" ) ) { + while ( fgets( line, MAXCHARS + 2, stream ) ) astSet( plot, "%s", line ); + close( stream ); +} + +/* Restore any attribute values we are preserving. */ +astSetI( plot, "Base", base ); + +/* Produce the graphical output (e.g.). */ +astGrid( plot ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER LINE( 120 ) + INTEGER BASE + + ... + +* Create a Plot and define the default appearance of the graphical +* output it will produce. + PLOT = AST_PLOT( WCSINFO, GBOX, PBOX, + : 'Grid=1, Colour(grid)=2, Font(textlab)=3', + : STATUS ) + +* Obtain the value of any Plot attributes we want to preserve. + BASE = AST_GETI( PLOT, 'Base', STATUS ) + +* Open the plot configuration file, if it exists. + OPEN ( 1, FILE = 'plot.config', STATUS = 'OLD', ERR = 8 ) + +* Read each line of text and use it to set new Plot attribute +* values. Close the file when done. + 6 CONTINUE + READ ( 1, '(A)', END = 7 ) LINE + CALL AST_SET( PLOT, LINE, STATUS ) + GO TO 6 + 7 CLOSE ( 1 ) + 8 CONTINUE + +* Restore any attribute values we are preserving. + CALL AST_SETI( PLOT, 'Base', BASE, STATUS ) + +* Produce the graphical output (e.g.). + CALL AST_GRID( PLOT, STATUS ) +\end{terminalv} +\normalsize +f- + +Notice that we take care that the Plot's Base attribute is preserved +so that the user cannot change it. This is because graphical output +will not be produced successfully if the base Frame does not describe +the plotting surface to which we attached the Plot when we created it. + +The arrangement shown above allows the contents of the ``plot.config'' +file to control most aspects of the graphical output produced +(including the coordinate system used; the colour, line style, +thickness and font used for each component; the positioning of axes +and tick marks; the precision, format and positioning of labels; +\emph{etc.}) \emph{via} assignments of the form: + +\small +\begin{terminalv} +System=Galactic, Equinox = 2001 +Border = 1, Colour( border ) = 1 +Colour( grid ) = 2 +DrawAxes = 1 +Colour( axes ) = 3 +Digits = 8 +Labelling = Interior +\end{terminalv} +\normalsize + +For a more sophisticated interface, you could obviously perform +pre-processing on this input---for example, to translate words like +``red'', ``green'' and ``blue'' into colour indices, to permit +comments and blank lines, \emph{etc.} + +For a full list of the attributes that may be used to control the +appearance of graphical output, see the description of the Plot class +in \appref{ss:classdescriptions}. For a complete description of each +individual attribute (\emph{e.g.}\ those above), see the attribute's +entry in \appref{ss:attributedescriptions}. + +\cleardoublepage +\section{\label{ss:primer}An AST Object Primer} + +c+ +The AST library deals throughout with entities called Objects and a +basic understanding of how to handle these is needed before you can +use the library effectively. If you are already familiar with an +object-oriented language, such as C$++$, few of the concepts should +seem new to you. Be aware, however, that AST is designed to be used +\emph{via} fairly conventional C and Fortran interfaces, so some +things have to be done a little differently. +c- +f+ +The AST library deals throughout with entities called Objects and a +basic understanding of how to handle these is needed before you can +use the library effectively. If you are already familiar with an +object-oriented language, such as C$++$, few of the concepts should +seem new to you. Be aware, however, that AST is designed to be used +\emph{via} fairly conventional Fortran and C interfaces, so some +things have to be done a little differently. +f- + +c+ +If you are not already familiar with object-oriented programming, then +don't worry---we will not emphasise this aspect more than is necessary +and will not assume any background knowledge. Instead, this section +concentrates on presenting all the fundamental information you will +need, explaining how AST Objects behave and how to manipulate them +from conventional C programs. +c- +f+ +If you are not already familiar with object-oriented programming, then +don't worry---we will not emphasise this aspect more than is necessary +and will not assume any background knowledge. Instead, this section +concentrates on presenting all the fundamental information you will +need, explaining how AST Objects behave and how to manipulate them +from conventional Fortran programs. +f- + +If you like to read documents from cover to cover, then you can +consider this section as an introduction to the programming techniques +used in the rest of the document. Otherwise, you may prefer to skim +through it on a first reading and return to it later as reference +material. + +\subsection{AST Objects} + +An AST Object is an entity which is used to store information and +Objects come in various kinds, called \emph{classes}, according to the +sort of information they hold. Throughout this section, we will make +use of a simple Object belonging to the ``ZoomMap'' class to +illustrate many of the basic concepts. + +A ZoomMap is an Object that contains a recipe for converting +coordinates between two hypothetical coordinate systems. It does this +by multiplying all the coordinate values by a constant called the +\emph{Zoom factor}. A ZoomMap is a very simple Object which exists +mainly for use in examples. It allows us to illustrate the ways in +which Objects are manipulated and to introduce the concept of a +Mapping---a recipe for converting coordinates---which is fundamental +to the way the AST library works. + +\subsection{\label{ss:objectcreation}Object Creation and Pointers} + +Let us first consider how to create a ZoomMap. This is done very +simply as follows: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstZoomMap *zoommap; + +... + +zoommap = astZoomMap( 2, 5.0, "" ) +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER STATUS, ZOOMMAP + + STATUS = 0 + + ... + + ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The first step is to include the header file ``ast.h'' which declares +the interface to the AST library. We then declare a pointer of type +AstZoomMap$*$ to receive the result and invoke the function astZoomMap +to create the ZoomMap. The pattern is the same for all other classes +of AST Object---you simply prefix ``ast'' to the class name to obtain +the function that creates the Object and prefix ``Ast'' to obtain the +type of the returned pointer. +c- +f+ +The first step is to include the file AST\_PAR which defines the +interface to the AST library and, amongst other things, declares +AST\_ZOOMMAP to be an integer function. We then declare an integer +variable ZOOMMAP to receive the result and an integer STATUS variable +to hold the error status, which we initialise to zero. Next, we invoke +AST\_ZOOMMAP to create the ZoomMap. The pattern is the same for all +other classes of AST Object---you simply prefix ``AST\_'' to the class +name to obtain the function that creates the Object. +f- + +These functions are called \emph{constructor functions}, or simply +\emph{constructors} (you can find an individual description of all AST +functions in \appref{ss:functiondescriptions}) and the arguments +passed to the constructor are used to initialise the new Object. In +this case, we specify 2 as the number of coordinates (\emph{i.e.}\ we +are going to work in a 2-dimensional +c+ +space) and 5.0 as the Zoom factor to be applied. Note that this is a C +double value. We will return to the final argument, an empty string, +shortly (\secref{ss:attributeinitialisation}). +c- +f+ +space) and 5.0D0 as the Zoom factor to be applied. Note that this is a +Fortran double precision value. We will return to the final two +arguments, a blank string and the error status, shortly +(\secref{ss:attributeinitialisation} and \secref{ss:errordetection}). +f- + +c+ +The value returned by the constructor is termed an \emph{Object pointer} +or, in this case, a \emph{ZoomMap pointer} and is used to refer to the +Object. You perform all subsequent operations on the Object by +passing this pointer to other AST functions. +c- +f+ +The integer value returned by the constructor is termed an \emph{Object +pointer} or, in this case, a \emph{ZoomMap pointer}. This pointer is not +an Object itself, but is a value used to refer to the Object. You +should be careful not to modify any Object pointer yourself, as this +may render it invalid. Instead, you perform all subsequent operations +on the Object by passing this pointer to other AST routines. +f- + +\subsection{\label{ss:objecthierarchy}The Object Hierarchy} + +Now that we have created our first ZoomMap, let us examine how it +relates to other kinds of Object before investigating what we can do +with it. + +We have so far indicated that a ZoomMap is a kind of Object and have +also mentioned that it is a kind of Mapping as well. These statements +can be represented very simply using the following hierarchy: + +\small +\begin{terminalv} +Object + Mapping + ZoomMap +\end{terminalv} +\normalsize + +which is a way of stating that a ZoomMap is a special class of +Mapping, while a Mapping, in turn, is a special class of Object. This +is exactly like saying that an Oak is a special form of Tree, while a +Tree, in turn, is a special form of Plant. This may seem almost +trivial, but before you turn to read something less dull, be assured +that it is a very important idea to keep in mind in what follows. + +If we look at some of the other Objects used by the AST library, we +can see how these are all related in a similar way (don't worry about +what they do at this stage): +\label{ss:mappinghierarchy} + +\small +\begin{terminalv} +Object + Mapping + Frame + FrameSet + Plot + UnitMap + ZoomMap + Channel + FitsChan + XmlChan +\end{terminalv} +\normalsize + +Notice that there are several different types of Mapping available +(\emph{i.e.}\ there are classes of Object indented beneath the +``Mapping'' heading) and, in addition, other types of Object which are +not Mappings---Channels for instance (which are at the same +hierarchical level as Mappings). + +The most specialised Object we have shown here is the Plot (which we +will not discuss in detail until \secref{ss:plots}). As you can see, a +Plot is a FrameSet\ldots\ and a Frame\ldots\ and a Mapping\ldots\ and, +like everything else, ultimately an Object. + +What this means is that you can use a Plot not only for its own +specialised behaviour, but also whenever any of these other +less-specialised classes of Object is called for. The general rule is +that an Object of a particular class may substitute for any of the +classes appearing above it in this hierarchy. The Object is then said +to \emph{inherit} the behaviour of these higher classes. We can +therefore use our ZoomMap whenever a ZoomMap, a Mapping or an Object +is called for. + +Sometimes, this can lead to some spectacular short-cuts by avoiding +the need to break large Objects down in order to access their +components. With some practice and a little lateral thinking you +should soon be able to spot opportunities for this. + +You can find the full \emph{class hierarchy}, as this is called, for +the AST library in \appref{ss:classhierarchy} and you may need to +refer to it occasionally until you are familiar with the classes you +need to use. + +\subsection{\label{ss:displayingobjects}Displaying Objects} + +Let us now return to the ZoomMap that we created earlier +(\secref{ss:objectcreation}) and examine what it's made of. +c+ +There is a function for doing this, called astShow, which is provided +mainly for looking at Objects while you are debugging programs. +c- +f+ +There is a routine for doing this, called AST\_SHOW, which is provided +mainly for looking at Objects while you are debugging programs. +f- + +c+ +If you consult the description of astShow in +\appref{ss:functiondescriptions}, you will find that it takes a +pointer to an Object (of type AstObject$*$) as its argument. Although +we have only a ZoomMap pointer available, this is not a problem. If +you refer to the brief class hierarchy described above +(\secref{ss:mappinghierarchy}), you will see that a ZoomMap is an +Object, albeit a specialised one, so it inherits the properties of all +Objects and can be substituted wherever an Object is required. We can +therefore pass our ZoomMap pointer directly to astShow, as follows: +c- +f+ +If you consult the description of AST\_SHOW in +\appref{ss:functiondescriptions}, you will find that it takes a +pointer to an Object as its argument (in addition to the usual STATUS +argument). Although we have only a ZoomMap pointer available, +fortunately this is not a problem. If you refer to the brief class +hierarchy described above (\secref{ss:mappinghierarchy}), you will see +that a ZoomMap is an Object, albeit a specialised one, so it inherits +the properties of all Objects and can be substituted wherever an +Object is required. We can therefore pass our ZoomMap pointer +directly to AST\_SHOW, as follows: +f- + +c+ +\small +\begin{terminalv} +astShow( zoommap ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SHOW( ZOOMMAP, STATUS ) +\end{terminalv} +\normalsize +f- + +The output from this will appear on the standard output stream and +should look like the following: + +\small +\begin{terminalv} +Begin ZoomMap + Nin = 2 +IsA Mapping + Zoom = 5 +End ZoomMap +\end{terminalv} +\normalsize + +Here, the ``Begin'' and ``End'' lines mark the beginning and end of +the ZoomMap, while the values 2 and 5 are simply the values we +supplied to initialise it (\secref{ss:objectcreation}). These have +been given simple names to make them easy to refer to. + +The line in the middle which says ``IsA~Mapping'' is a dividing line +between the two values. It indicates that the ``Nin'' value is a +property shared by all Mappings, so the ZoomMap has inherited this +from its \emph{parent class} (Mapping). The ``Zoom'' value, however, +is specific to a ZoomMap and isn't shared by other kinds of Mappings. + +\subsection{\label{ss:gettingattributes}Getting Attribute Values} + +We saw above (\secref{ss:displayingobjects}) how to display the +internal values of an Object, but what about accessing these values +from a program? Not all internal Object values are accessible in this +way, but many are. Those that are, are called \emph{attributes}. A +description of all the attributes used by the AST library can be found +in \appref{ss:attributedescriptions}. + +c+ +Attributes come in several data types (character string, integer, +boolean and floating point) and there is a standard way of obtaining +their values. As an example, consider obtaining the value of the Nin +attribute for the ZoomMap created earlier. This could be done as +follows: +c- +f+ +Attributes come in several data types (character string, integer, +boolean and floating point) and there is a standard way of obtaining +their values. As an example, consider obtaining the value of the Nin +attribute for the ZoomMap created earlier. This could be done as +follows: +f- + +c+ +\small +\begin{terminalv} +int nin; + +... + +nin = astGetI( zoommap, "Nin" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER NIN + + ... + + NIN = AST_GETI( ZOOMMAP, 'Nin', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, the function astGetI is used to extract the attribute value by +giving it the ZoomMap pointer and the attribute name (attribute names +are not case sensitive, but we have used consistent capitalisation in +this document in order to identify them). Remember to use the +``ast.h'' header file to include the function prototype. +c- +f+ +Here, the integer function AST\_GETI is used to extract the attribute +value by giving it the ZoomMap pointer and the attribute name +(attribute names are not case sensitive, but we have used consistent +capitalisation in this document in order to identify them). Remember +to use the AST\_PAR include file to save having to declare AST\_GETI +as integer yourself. +f- + +c+ +If we had wanted the value of the Zoom attribute, we would probably +have used astGetD instead, this being a double version of the same +function, for example: +c- +f+ +If we had wanted the value of the Zoom attribute, we would probably +have used AST\_GETD instead, this being a double precision version of +the same function, for example: +f- + +c+ +\small +\begin{terminalv} +double zoom; + +... + +zoom = astGetD( zoommap, "Zoom" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION ZOOM + + ... + + ZOOM = AST_GETD( ZOOMMAP, 'Zoom', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +However, we could equally well have read the Nin value as double, or +the Zoom value as an integer, or whatever we wanted. +c- +f+ +However, we could equally well have read the Nin value as double +precision, or the Zoom value as an integer, or whatever we wanted. +f- + +c+ +The data type you want returned is specified simply by replacing the +final character of the astGetX function name with C~(character +string), D~(double), F~(float), I~(int) or L~(long). If possible, the +value is converted to the type you want. If not, an error message will +result. Note that all floating point values are stored internally as +double, and all integer values as int. Boolean values are also stored +as integers, but only take the values 1 and 0 (for true/false). +c- +f+ +The data type you want returned is specified simply by replacing the +final character of the AST\_GETx function name with C~(character), +D~(double precision), I~(integer), L~(logical) or R~(real). If +possible, the value is converted to the type you want. If not, an +error message will result. In converting from integer to logical, zero +is regarded as .FALSE.\ and non-zero as .TRUE.. Note that all floating +point values are stored internally as double precision. Boolean values +are stored as integers, but only take the values 1 and 0 (for +true/false). +f- + +\subsection{\label{ss:settingattributes}Setting Attribute Values} + +Some attribute values are read-only and cannot be altered after an +Object has been created. The Nin attribute of a ZoomMap (describing +the number of coordinates) is like this. It is defined when the +ZoomMap is created, but cannot then be altered. + +Other attributes, however, can be modified whenever you want. A +ZoomMap's Zoom attribute is like this. If we wanted to change it, this +could be done simply as follows: + +c+ +\small +\begin{terminalv} +astSetD( zoommap, "Zoom", 99.6 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SETD( ZOOMMAP, 'Zoom', 99.6D0, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +which sets the value to 99.6. As when getting an attribute value +(\secref{ss:gettingattributes}), you have a choice of which data type +you will use to supply the new value. For instance, you could use an +integer value, as in: +c- +f+ +which sets the value to 99.6 (double precision). As when getting an +attribute value (\secref{ss:gettingattributes}), you have a choice of +which data type you will use to supply the new value. For instance, +you could use an integer value, as in: +f- + +c+ +\small +\begin{terminalv} +astSetI( zoommap, "Zoom", 99 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SETI( ZOOMMAP, 'Zoom', 99, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +and the necessary data conversion would occur. You specify the data +type you want to supply simply by replacing the final character of the +astSetX function name with C~(character string), D~(double), +F~(float), I~(int) or L~(long). Setting a boolean attribute to any +non-zero integer causes it to take the value 1. +c- +f+ +and the necessary data conversion would occur. You specify the data +type you want to supply simply by replacing the final character of the +AST\_SETx routine name with C~(character), D~(double precision), +I~(integer), L~(logical) or R~(real). Setting a boolean attribute to +any non-zero integer causes it to take the value 1. +f- + +c+ +An alternative way of setting attribute values for Objects is to use +the astSet function (\emph{i.e.}\ with no final character specifying a +data type). In this case, you supply the attribute values in a +character string. The big advantage of this method is that you can +assign values to several attributes at once, separating them with +commas. This also reads more naturally in programs. For example: +c- +f+ +An alternative way of setting attribute values for Objects is to use +the AST\_SET routine (\emph{i.e.}\ with no final character specifying +a data type). In this case, you supply the attribute values in a +character string. The big advantage of this method is that you can +assign values to several attributes at once, separating them with +commas. This also reads more naturally in programs. For example: +f- + +c+ +\small +\begin{terminalv} +astSet( zoommap, "Zoom=99.6, Report=1" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( ZOOMMAP, 'Zoom=99.6, Report=1', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +would set values for both the Zoom attribute and the Report attribute +(about which more shortly---\secref{ss:transforming}). You don't really +have to worry about data types with this method, as any character +representation will do. Note, when using astSet, a +literal comma may be included in an attribute value by enclosed the value in +quotation marks: +\small +\begin{terminalv} + astSet( skyframe, 'SkyRef="12:13:32,-23:12:44"' ); +\end{terminalv} +\normalsize +c- +f+ +would set values for both the Zoom attribute and the Report attribute +(about which more shortly---\secref{ss:transforming}). You don't really +have to worry about data types with this method, as any character +representation will do (although you must use 0/1 instead of +.TRUE./.FALSE., which are not supported). Note, when using AST\_SET, a +literal comma may be included in an attribute value by enclosed the value in +quotation marks: +\small +\begin{terminalv} + CALL AST_SET( SKYFRAME, 'SkyRef="12:13:32,-23:12:44"', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Another attractive feature of astSet is that you can build the +character string which contains the attribute settings in the same way +as when using the C run time library ``printf'' function. This is most +useful when the values you want to set are held in other +variables. For example: + +\small +\begin{terminalv} +double zoom = 99.6; +int report = 1; + +... + +astSet( zoommap, "Zoom=%g, Report=%d", zoom, report ); +\end{terminalv} +\normalsize + +would replace the ``\%'' conversion specifications by the values +supplied as additional arguments. Any number of additional arguments +may be supplied and the formatting rules are exactly the same as for +the C ``printf'' family of functions. This is a very flexible +technique, but does contain one pitfall: + +\begin{quote} +\textbf{Pitfall.} The default precision used by ``printf'' (and astSet) +for floating point values is only 6 decimal digits, corresponding +approximately to float on most machines, whereas the AST library +stores such values internally as doubles. You should be careful to +specify a larger precision (such as DBL\_DIG, as defined in +$<$float.h$>$) when necessary. For example: + +\small +\begin{terminalv} +#include + +... + +astSet( zoommap, "Zoom=%.*g", DBL_DIG, double_value ); +\end{terminalv} +\normalsize +\end{quote} + +Substituted strings may contain commas and this is a useful way of +assigning such strings as attribute values without the comma being +interpreted as an assignment separator, for example: + +\small +\begin{terminalv} +astSet( object, "Attribute=%s", "A string, containing a comma" ); +\end{terminalv} +\normalsize + +This is equivalent to using astSetC and one of these two methods +should always be used when assigning string attribute values which +might potentially contain a comma (\emph{e.g.}\ strings obtained from +an external source). However, you should not attempt to use astSet to +substitute strings that contain newline characters, since these are +used internally as separators between adjacent attribute assignments. +c- +\label{ss:attributeinitialisation} + +c+ +Finally, a very convenient way of setting attribute values is to do so +at the same time as you create an Object. Every Object constructor +function has a final character string argument which allows you to do +this. Although you can simply supply an empty string, it is an ideal +opportunity to initialise the Object to have just the attributes you +want. For example, we might have created our original ZoomMap with: +c- +f+ +Finally, a very convenient way of setting attribute values is to do so +at the same time as you create an Object. Every Object constructor +function has a penultimate character argument which allows you to do +this. Although you can simply leave this blank, it is an ideal +opportunity to initialise the Object to have just the attributes you +want. For example, we might have created our original ZoomMap with: +f- + +c+ +\small +\begin{terminalv} +zoommap = astZoomMap( 2, 5.0, "Report=1" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, 'Report=1', STATUS ) +\end{terminalv} +\normalsize +f- + +and it would then start life with its Report attribute set to 1. +c+ +The ``printf''-style substitution described above may also be used +here. +c- + +\subsection{\label{ss:defaultingattributes}Testing, Clearing and Defaulting Attributes} + +c+ +You can use the astGetX family of functions +(\secref{ss:gettingattributes}) to get a value for any Object attribute +at any time, regardless of whether a value has previously been set for +it. If no value has been set, the AST library will generate a suitable +default value. +c- +f+ +You can use the AST\_GETx family of routines +(\secref{ss:gettingattributes}) to get a value for any Object attribute +at any time, regardless of whether a value has previously been set for +it. If no value has been set, the AST library will generate a suitable +default value. +f- + +Often, the default value of an attribute will not simply be trivial +(zero or blank) but may involve considerable processing to +calculate. Wherever possible, defaults are designed to be real-life, +sensible values that convey information about the state of the +Object. In particular, they may often be based on the values of other +attributes, so their values may change in response to changes in these +other attributes. The ZoomMap class that we have studied so far is a +little too simple to show this behaviour, but we will meet it later +on. + +An attribute that returns a default value in this way is said to be +\emph{un-set}. Conversely, once an explicit value has been assigned to +an attribute, it becomes \emph{set} and will always return precisely +that value, never a default. + +c+ +The distinction between set and un-set attributes is important and +affects the behaviour of several key routines in the AST library. You +can test if an attribute is set using the function astTest, which +returns a boolean (integer) result, as in: +c- +f+ +The distinction between set and un-set attributes is important and +affects the behaviour of several key routines in the AST library. You +can test if an attribute is set using the logical function AST\_TEST, +as in: +f- + +c+ +\small +\begin{terminalv} +if ( astTest( zoommap, "Report" ) ) { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + IF ( AST_TEST( ZOOMMAP, 'Report', STATUS ) ) THEN + + END IF +\end{terminalv} +\normalsize +f- + +f+ +(as usual, remember to include the AST\_PAR file to declare the +function as LOGICAL, or make this declaration yourself). +f- + +c+ +Once an attribute is set, you can return it to its un-set state using +astClear. The effect is as if it had never been set in the first +place. For example: +c- +f+ +Once an attribute is set, you can return it to its un-set state using +AST\_CLEAR. The effect is as if it had never been set in the first +place. For example: +f- + +c+ +\small +\begin{terminalv} +astClear( zoommap, "Report" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_CLEAR( ZOOMMAP, 'Report', STATUS ) +\end{terminalv} +\normalsize +f- + +would ensure that the default value of the Report attribute is used +subsequently. + +%\subsection{TBW--Handling Character Attributes} + +\subsection{\label{ss:transforming}Transforming Coordinates} + +We now have the necessary apparatus to start using our ZoomMap to show +what it is really for. Here, we will also encounter a routine that is +a little more fussy about the type of pointer it will accept. + +The purpose of a ZoomMap is to multiply coordinates by a constant zoom +factor. To witness this in action, we will first set the Report +attribute for our ZoomMap to a non-zero value: + +c+ +\small +\begin{terminalv} +astSet( zoommap, "Report=1" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( ZOOMMAP, 'Report=1', STATUS ) +\end{terminalv} +\normalsize +f- + +This boolean (integer) attribute, which is present in all Mappings +(and a ZoomMap is a Mapping), causes the automatic display of all +coordinate values that the Mapping converts. It is not a good idea to +leave this feature turned on in a finished program, but it can save a +lot of work during debugging. + +c+ +Our next step is to set up some coordinates for the ZoomMap to work +on, using two arrays ``xin'' and ``yin'', and two arrays to receive +the transformed coordinates, ``xout'' and ``yout''. Note that these +are arrays of double, as are all coordinate data processed by the AST +library: +c- +f+ +Our next step is to set up some coordinates for the ZoomMap to work +on, using two arrays XIN and YIN, and two arrays to receive the +transformed coordinates, XOUT and YOUT. Note that these arrays are +double precision, as are all coordinate data processed by the AST +library: +f- + +c+ +\small +\begin{terminalv} +double xin[ 10 ] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 }; +double yin[ 10 ] = { 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0 }; +double xout[ 10 ]; +double yout[ 10 ]; +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION XIN( 10 ), YIN( 10 ), XOUT( 10 ), YOUT( 10 ) + DATA XIN / 0D0, 1D0, 2D0, 3D0, 4D0, 5D0, 6D0, 7D0, 8D0, 9D0 / + DATA YIN / 0D0, 2D0, 4D0, 6D0, 8D0, 10D0, 12D0, 14D0, 16D0, 18D0 / +\end{terminalv} +\normalsize +f- + +c+ +We will now use the function astTran2 to transform the input +coordinates. This is the most commonly-used (2-dimensional) coordinate +transformation function. If you look at its description in +\appref{ss:functiondescriptions}, you will see that it requires a +pointer to a Mapping, so we cannot supply just any old Object pointer, +as we could with the functions discussed previously. If we passed it a +pointer to an inappropriate Object, an error message would result. +c- +f+ +We will now use the routine AST\_TRAN2 to transform the input +coordinates. This is the most commonly-used (2-dimensional) coordinate +transformation routine. If you look at its description in +\appref{ss:functiondescriptions}, you will see that it requires a +pointer to a Mapping, so we cannot supply just any old Object pointer, +as we could with the routines discussed previously. If we passed it a +pointer to an inappropriate Object, an error message would result. +f- + +c+ +Fortunately, a ZoomMap is a Mapping (\appref{ss:classhierarchy}), so we +can use it with astTran2 to transform our coordinates, as follows: +c- +f+ +Fortunately, a ZoomMap is a Mapping (\appref{ss:classhierarchy}), so we +can use it with AST\_TRAN2 to transform our coordinates, as follows: +f- + +c+ +\small +\begin{terminalv} +astTran2( zoommap, 10, xin, yin, 1, xout, yout ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_TRAN2( ZOOMMAP, 10, XIN, YIN, .TRUE., XOUT, YOUT, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, 10 is the number of points we want to transform and the fifth +argument value of 1 indicates that we want to transform in the +\emph{forward} direction (from input to output). +c- +f+ +Here, 10 is the number of points we want to transform and the fifth +argument value of .TRUE.\ indicates that we want to transform in the +\emph{forward} direction (from input to output). +f- + +Because our ZoomMap's Report attribute is set to 1, this will cause +the effects of the ZoomMap on the coordinates to be displayed on the +standard output stream: + +\small +\begin{terminalv} +(0, 0) --> (0, 0) +(1, 2) --> (5, 10) +(2, 4) --> (10, 20) +(3, 6) --> (15, 30) +(4, 8) --> (20, 40) +(5, 10) --> (25, 50) +(6, 12) --> (30, 60) +(7, 14) --> (35, 70) +(8, 16) --> (40, 80) +(9, 18) --> (45, 90) +\end{terminalv} +\normalsize + +c+ +This shows the coordinate values of each point both before and after +the ZoomMap is applied. You can see that each coordinate value has +been multiplied by the factor 5 determined by the Zoom attribute +value. The transformed coordinates are now stored in the ``xout'' and +``yout'' arrays. +c- +f+ +This shows the coordinate values of each point both before and after +the ZoomMap is applied. You can see that each coordinate value has +been multiplied by the factor 5 determined by the Zoom attribute +value. The transformed coordinates are now stored in the XOUT and YOUT +arrays. +f- + +c+ +If we wanted to transform in the opposite direction, we need simply +change the fifth argument of astTran2 from 1 to 0. We can also feed +the output coordinates from the above back into the function: +c- +f+ +If we wanted to transform in the opposite direction, we need simply +change the fifth argument of AST\_TRAN2 from .TRUE. to .FALSE.. We can +also feed the output coordinates from the above back into the routine: +f- + +c+ +\small +\begin{terminalv} +astTran2( zoommap, 10, xout, yout, 0, xin, yin ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_TRAN2( ZOOMMAP, 10, XOUT, YOUT, .FALSE., XIN, YIN, STATUS ) +\end{terminalv} +\normalsize +f- + +The output would then look like: + +\small +\begin{terminalv} +(0, 0) --> (0, 0) +(5, 10) --> (1, 2) +(10, 20) --> (2, 4) +(15, 30) --> (3, 6) +(20, 40) --> (4, 8) +(25, 50) --> (5, 10) +(30, 60) --> (6, 12) +(35, 70) --> (7, 14) +(40, 80) --> (8, 16) +(45, 90) --> (9, 18) +\end{terminalv} +\normalsize + +This is termed the \emph{inverse} transformation (we have converted +from output to input) and you can see that the original coordinates +have been recovered by dividing by the Zoom factor. + +\subsection{\label{ss:annullingpointers}Managing Object Pointers} + +So far, we have looked at creating Objects and using them in various +simple ways but have not yet considered how to get rid of them again. + +c+ +Every Object consumes various computer resources (principally memory) +and should be disposed of when it is no longer required, so as to free +up these resources. One way of doing this (not necessarily the +best---\secref{ss:contexts}) is to \emph{annul} each Object pointer once +you have finished with it, using astAnnul. For example: +c- +f+ +Every Object consumes various computer resources (principally memory) +and should be disposed of when it is no longer required, so as to free +up these resources. One way of doing this (not necessarily the +best---\secref{ss:contexts}) is to \emph{annul} each Object pointer once +you have finished with it, using AST\_ANNUL. For example: +f- + +c+ +\small +\begin{terminalv} +zoommap = astAnnul( zoommap ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_ANNUL( ZOOMMAP, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +This indicates that you have finished with the pointer. Since astAnnul +always returns the null value AST\_\_NULL (as defined in ``ast.h''), +the recommended way of using it, as here, is to assign the returned +value to the pointer being annulled. This ensures that any attempt to +use the pointer again will generate an error message. +c- +f+ +This indicates that you have finished with the pointer and sets it to +the null value AST\_\_NULL (as defined in the AST\_PAR include file), +so that any attempt to use it again will generate an error message. +f- + +c+ +In general, this process may not delete the Object, because there may +still be other pointers associated with it. However, each Object +maintains a count of the number of pointers associated with it and +will be deleted if you annul the final pointer. Using astAnnul +consistently will therefore ensure that all Objects are disposed of at +the correct time. You can determine how many pointers are associated +with an Object by examining its (read-only) RefCount attribute. +c- +f+ +In general, this process may not delete the Object, because there may +still be other pointers associated with it. However, each Object +maintains a count of the number of pointers associated with it and +will be deleted if you annul the final pointer. Using AST\_ANNUL +consistently will therefore ensure that all Objects are disposed of at +the correct time. You can determine how many pointers are associated +with an Object by examining its (read-only) RefCount attribute. +f- + +c+ +\subsection{\label{ss:contexts}AST Pointer Contexts---Begin and End} +c- +f+ +\subsection{\label{ss:contexts}AST Pointer Contexts---Begin and End} +f- + +c+ +The use of astAnnul (\secref{ss:annullingpointers}) is not completely +foolproof, however. Consider the following: +c- +f+ +The use of AST\_ANNUL (\secref{ss:annullingpointers}) is not completely +foolproof, however. Consider the following: +f- + +c+ +\small +\begin{terminalv} +astShow( astZoomMap( 2, 5.0, "" ) ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SHOW( AST_ZOOMMAP( 2, 5.ODO, ' ', STATUS ), STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +This creates a ZoomMap and displays it on standard output +(\secref{ss:displayingobjects}). Using function invocations as +arguments to other functions in this way is very convenient because it +avoids the need for intermediate pointer variables. However, the +pointer generated by astZoomMap is still active, and since we have not +stored its value, we cannot use astAnnul to annul it. The ZoomMap will +therefore stay around until the end of the program. +c- +f+ +This creates a ZoomMap and displays it on standard output +(\secref{ss:displayingobjects}). Using function invocations as +arguments to other routines in this way is very convenient because it +avoids the need for intermediate pointer variables. However, the +pointer generated by AST\_ZOOMMAP is still active, and since we have +not stored its value, we cannot use AST\_ANNUL to annul it. The +ZoomMap will therefore stay around until the end of the program. +f- + +c+ +A simple way to avoid this problem is to enclose all use of AST +functions between invocations of astBegin and astEnd, for example: +c- +f+ +A simple way to avoid this problem is to enclose all use of AST +routines between calls to AST\_BEGIN and AST\_END, for example: +f- + +c+ +\small +\begin{terminalv} +astBegin; +astShow( astZoomMap( 2, 5.0, "" ) ); +astEnd; +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_BEGIN( STATUS ) + CALL AST_SHOW( AST_ZOOMMAP( 2, 5.ODO, ' ', STATUS ), STATUS ) + CALL AST_END( STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +When the expansion of astEnd (which is a macro) executes, every Object +pointer created since the previous use of astBegin (also a macro) is +automatically annulled and any Objects left without pointers are +deleted. This provides a simple solution to managing Objects and their +pointers, and allows you to create Objects very freely without needing +to keep detailed track of each one. Because this is so convenient, we +implicitly assume that astBegin and astEnd are used in most of the +examples given in this document. Pointer management is not generally +shown explicitly unless it is particularly relevant to the point being +illustrated. +c- +f+ +When the AST\_END call executes, every Object pointer created since +the previous AST\_BEGIN call is automatically annulled and any Objects +left without pointers are deleted. This provides a simple solution to +managing Objects and their pointers, and allows you to create Objects +very freely without needing to keep detailed track of each one. +Because this is so convenient, we implicitly assume that AST\_BEGIN +and AST\_END are used in most of the examples given in this document. +Pointer management is not generally shown explicitly unless it is +particularly relevant to the point being illustrated. +f- + +c+ +If necessary, astBegin and astEnd may be nested, like blocks delimited +by ``\{\ldots\}'' in C, to define a series of AST pointer +contexts. Each use of astEnd will then annul only those Object +pointers created since the matching use of astBegin. +c- +f+ +If necessary, calls to AST\_BEGIN and AST\_END may be nested, like +IF\ldots ENDIF blocks in Fortran, to define a series of AST pointer +contexts. Each call to AST\_END will then annul only those Object +pointers created since the matching call to AST\_BEGIN. +f- + +\subsection{Exporting, Importing and Exempting AST Pointers} +c+ +The astExport function allows you to export particular pointers from +one AST context (\secref{ss:contexts}) to the next outer one, as +follows: +c- +f+ +The AST\_EXPORT routine allows you to export particular pointers from +one AST context (\secref{ss:contexts}) to the next outer one, as +follows: +f- + +c+ +\small +\begin{terminalv} +astExport( zoommap ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_EXPORT( ZOOMMAP, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +This would identify the pointer stored in ``zoommap'' as being required +after the end of the current AST context. It causes any pointers +nominated in this way to survive the next use of astEnd (but only one +such use) unscathed, so that they are available to the next outer +context. This facility is not needed often, but is invaluable when +the purpose of your astBegin\ldots astEnd block is basically to +generate an Object pointer. Without this, there is no way of getting +that pointer out. +c- +f+ +This would identify the pointer stored in ZOOMMAP as being required after +the end of the current AST context. It causes any pointers nominated +in this way to survive the next call to AST\_END (but only one such +call) unscathed, so that they are available to the next outer context. +This facility is not needed often, but is invaluable when the purpose +of your AST\_BEGIN\ldots AST\_END block is basically to generate an +Object pointer. Without this, there is no way of getting that pointer +out. +f- + +f+ +The AST\_IMPORT routine can be used in a similar manner to import a +pointer into the current context, so that it is deleted when the current +context is closed using AST\_END. +f- + +c+ +The astImport routine can be used in a similar manner to import a +pointer into the current context, so that it is deleted when the current +context is closed using astEnd. +c- + +c+ +Sometimes, you may also want to exempt a pointer from all the effects +of AST contexts. You should not need to do this often, but it will +prove essential if you ever need to write a library of functions that +stores AST pointers as part of its own internal data. Without some +form of exemption, the caller of your routines could cause the +pointers you have stored to be annulled---thus corrupting your +internal data---simply by using astEnd. To avoid this, you should use +astExempt on each pointer that you store, for example: +c- +f+ +Sometimes, you may also want to exempt a pointer from all the effects +of AST contexts. You should not need to do this often, but it will +prove essential if you ever need to write a library of routines that +stores AST pointers as part of its own internal data. Without some +form of exemption, the caller of your routines could cause the +pointers you have stored to be annulled---thus corrupting your +internal data---simply by using AST\_END. To avoid this, you should +use AST\_EXEMPT on each pointer that you store, for example: +f- + +c+ +\small +\begin{terminalv} +astExempt( zoommap ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_EXEMPT( ZOOMMAP, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +This will prevent the pointer being affected by any subsequent use of +astEnd. Of course, it then becomes your responsibility to annul this +pointer (using astAnnul) when it is no longer required. +c- +f+ +This will prevent the pointer being affected by any subsequent use of +AST\_END. Of course, it then becomes your responsibility to annul this +pointer (using AST\_ANNUL) when it is no longer required. +f- + + +c+ +\subsection{AST Objects within Multi-threaded Applications} + +When the AST library is built from source, the build process checks to +see if the POSIX threads library (``\texttt{pthreads}'') is available. If so, +appropriate \texttt{pthreads} calls are inserted into the AST source code to +ensure that AST is thread-safe, and the AST\_\_THREADSAFE macro (defined +in the ``ast.h'' header file) is set to ``\texttt{1}''. If the \texttt{pthreads} +library cannot be found when AST is built, a working version of the AST +library will still be created, but it will not be thread-safe. In this +case the AST\_\_THREADSAFE macro will be set to ``\texttt{0}'' in ast.h. The +rest of this section assumes that the thread-safe version of AST is being +used. + +Note, some AST functions call externally specified functions (\emph{e.g.} +the source and sink functions used by the Channel class or the graphics +primitives functions used by the Plot class). AST does not know whether +such functions are thread-safe or not. For this reason, invocations of these +functions within a multi-threaded environment are serialised using a mutex +in order to avoid two or more threads executing an external function +simultaneously. + +If an application uses more than one thread, the possibility arises that +an Object created by one thread may be accessed by another thread, potentially +simultaneously. If any of the threads modifies any aspect of the Object, +this could lead to serious problems within the other threads. For this +reason, some restrictions are placed on how Objects can be used in a +multi-threaded application. + +\subsubsection{Locking AST Objects for Exclusive Use} +The basic restriction is that a thread can only access Objects that it +has previously locked for its own exclusive use. If a thread attempts to +access any Object that it has not locked, an error is reported. + +The astAnnul function is the one exception to this restriction. Pointers +for Objects not currently locked by the calling thread can be annulled +succesfully using astAnnul. This means that a thread that has finished +with an Object pointer can unlock the Object by passing the pointer to +astUnlock (so that other threads can use the Object via their own cloned +pointers), and can then annul the pointer using astAnnul. Note, however, +that an error will be reported by astAnnul if the supplied pointer has +been locked by another thread using astLock. + +When an Object is created, it is initially locked by the calling thread. +Therefore a thread does not need to lock an Object explicitly if it was +created in the same thread. + +If the Object pointer is then passed to another thread, the first thread +must unlock the Object using astUnlock and the second thread must then lock +it using astLock. + +If a thread attempts to lock an Object that is already locked by another +thread, it can choose to report an error immediately or to wait until the +Object is available. + +The astThread function can be used to determine whether an Object is +locked by the running thread, locked by another thread, or unlocked. + +If two or more threads need simultaneous access to an Object, a deep copy +of the Object should be taken for each thread, using astCopy, and then +the copies should be unlocked and passed to the othe threads, which +should then lock them. Note, if a thread modifies the Object, the +modification will have no effect on the other threads, because the Object +copies are independent of each other. + +\subsubsection{AST Pointer Contexts} + +Each thread maintains its own set of nested AST contexts, so when astEnd +is called, only Objects that are locked by the current thread will +be annulled. + +If an Object is unlocked by a thread using astUnlock, it is exempted from +context handling so that subsequent invocations of astEnd will not cause it +to be annulled (this is similar to using astExempt on the Object). When the +Object is subsequently locked by another thread using astLock, it will be +imported into the context that was active when astLock was called. + +c- + + +\subsection{\label{ss:copyingobjects}Copying Objects} + +The AST library makes extensive use of pointers, not only for +accessing Objects directly, but also as a means of storing Objects +inside other Objects (a number of classes of Object are designed to +hold collections of other Objects). Rather than copy an Object in its +entirety, a pointer to the interior Object is simply stored in the +enclosing Object. + +This means that Objects may frequently not be completely independent +of each other because, for instance, they both contain pointers to the +same sub-Object. In this situation, changing one Object (say assigning +an attribute value) may affect the other one \emph{via} the common +Object. + +c+ +It is difficult to describe all cases where this may happen, so you +should always be alert to the possibility. Fortunately, there is a +simple solution. If you require two Objects to be independent, then +simply use astCopy to make a copy of one, \emph{e.g.}: +c- +f+ +It is difficult to describe all cases where this may happen, so you +should always be alert to the possibility. Fortunately, there is a +simple solution. If you require two Objects to be independent, then +simply use AST\_COPY to make a copy of one, \emph{e.g.}: +f- + +c+ +\small +\begin{terminalv} +AstZoomMap *zoommap1, *zoommap2; + +... + +zoommap2 = astCopy( zoommap1 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER ZOOMMAP1, ZOOMMAP2 + + ... + + ZOOMMAP2 = AST_COPY( ZOOMMAP1, STATUS ) +\end{terminalv} +\normalsize +f- + +This process will create a true copy of any Object and return a +pointer to the copy. This copy will not contain any pointers to any +component of the original Object (everything is duplicated), so you +can then modify it safely, without fear of affecting either the +original or any other Object. + +%\subsection{TBW - Inheritance} + +c+ +\subsection{C Pointer Types} + +At this point it is necessary to confess to a small amount of +deception. So far, we have been passing Object pointers to AST +functions in order to perform operations on those Objects. In fact, +however, what we were using were not true C functions at all, but +merely macros which invoke a related set of hidden functions with +essentially the same arguments. In practical terms, this makes very +little difference to how you use the functions, as we will continue to +call them.\footnote{About the only difference is that you cannot store +a pointer to an AST ``function'' in a variable and use the variable's +value to invoke that function again later.} + +The reason for this deception has to do with the rules for data typing +in C. Recall that most AST functions can be used to process Objects +from a range of different classes (\secref{ss:objecthierarchy}). In C, +this means passing different pointer types to the same function and +most C compilers will not permit this (at least, not without +grumbling) because it usually indicates a programming error. In AST, +however, it is perfectly safe if done properly. Some way is therefore +needed of circumventing the normal compiler checking. + +The normal way of doing this in C is with a cast. This approach +quickly becomes cumbersome, however, so we have adopted the strategy +of wrapping each function in a macro which applies the appropriate +cast for you. This means that you can pass pointers of any type to any +AST function. For example, in passing a ZoomMap pointer to astShow: + +\small +\begin{terminalv} +AstZoomMap *zoommap; + +... + +zoommap = astZoomMap( 2, 5.0, "" ); +astShow( zoommap ); +\end{terminalv} +\normalsize + +we are exploiting this mechanism to avoid a compiler warning, because +the notional type of astShow's parameter is AstObject$*$ (not +AstZoomMap$*$). + +We must still guard against programming errors, however, so every +pointer's type is checked by the enclosing macro immediately before +any AST function executes. This allows pointer mis-matches (in the +more liberal AST sense---\emph{i.e.}\ taking account of the class +hierarchy, rather than the stricter C sense) to be detected at +run-time and a suitable error message will be reported. This message +should also identify the line where the error occurs. + +A similar strategy is used when pointers are returned by AST functions +(\emph{i.e.}\ as the function result). In this case the pointer is +cast to void$*$, although we retain the notional pointer type in the +function's documentation +(\emph{e.g.}\ \appref{ss:functiondescriptions}). This allows you to +assign function results to pointer variables without using an explicit +cast. For example, the astRead function returns an Object pointer, but +might be used to read (say) a ZoomMap as follows: + +\small +\begin{terminalv} +AstChannel *channel; +AstZoomMap *zoommap; + +... + +zoommap = astRead( channel ); +\end{terminalv} +\normalsize + +Strictly, there is a C pointer mis-match here, but it is ignored +because the operation makes perfect sense to AST. + +\textbf{There is an important exception to this, however, in that +constructor functions always return strongly-typed pointers.} What +we mean by this is that the returned pointer is never implicitly cast +to void$*$. You must therefore match pointer types when you initially +create an Object using its constructor, such as in the following: + +\small +\begin{terminalv} +AstZoomMap *zoommap; + +... + +zoommap = astZoomMap( 2, 5.0, "" ); +\end{terminalv} +\normalsize + +If the variable receiving the pointer is of a different type, an +appropriate cast should be used, as in: + +\small +\begin{terminalv} +AstMapping *mapping; + +... + +mapping = (AstMapping *) astZoomMap( 2, 5.0, "" ); +\end{terminalv} +\normalsize + +This is an encouragement for you to declare your pointer types +consistently, since this is of great benefit to anyone trying to +understand your software. + +Finally, we should also make one more small confession---AST pointers +are not really pointers at all. Although they behave like pointers, +the actual ``values'' stored are not the addresses of C data +structures. This means that you cannot de-reference an AST pointer to +examine the data within (although you can use astShow +instead---\secref{ss:displayingobjects}). This is necessary so that AST +pointers can be made unique even although several of them might +reference the same Object. +c- + +\subsection{\label{ss:errordetection}Error Detection} + +c+ +If an error occurs in an AST function (for example, if you supply an +invalid argument, such as a pointer to the wrong class of Object), an +error message will be written to the standard error stream and the +function will immediately return. +c- +f+ +If an error occurs in an AST routine (for example, if you supply an +invalid argument, such as a pointer to the wrong class of Object), an +error message will be written to the standard error stream and the +function will immediately return. +f- + +c+ +To indicate than an error has occurred, an AST \emph{error status} +value is used. This integer value is stored internally by AST and is +initially clear (\emph{i.e.}\ set to zero\footnote{We will assume +throughout that the ``OK'' value is zero, as it currently is. However, +a different value could, in principle, be used if the environment in +which AST is running requires it. This is why a simple interface is +provided to isolate you from the actual value of the error status.} +to indicate no error). If an error occurs, it becomes set to a +different \emph{error value}, which allows you to detect the error, as +follows: +c- +f+ +To indicate that an error has occurred, each AST routine that can +potentially fail has a final integer \emph{error status} argument +called STATUS. This is both an input and an output argument. +Normally, you should declare a single error status variable and pass +it as the STATUS argument to every AST routine you invoke. This +variable must initially be cleared (\emph{i.e.}\ set to +zero\footnote{We will assume throughout that the ``OK'' value is zero, +as it currently is. However, a different value could, in principle, be +used if the environment in which AST is running requires it. To allow +for this possibility, you might prefer to use a parameter constant to +represent the value zero when testing for errors.} to indicate no +error). If an error occurs, the STATUS argument is returned set to a +different \emph{error value}, which allows you to detect the error, as +follows: +f- + +c+ +\small +\begin{terminalv} +zoommap = astZoomMap( 2, 5.0, "Title=My ZoomMap" ); +if ( !astOK ) { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + STATUS = 0 + + ... + + ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, 'Title=My ZoomMap', STATUS ) + IF ( STATUS .NE. 0 ) THEN + + END IF +\end{terminalv} +\normalsize +f- + +c+ +The macro astOK is used to test whether the AST error status is still +OK. In this example it would not be, because we have attempted to set +a value for the Title attribute of a ZoomMap and a ZoomMap does not +have such an attribute. The actual value of the AST error status can +be obtained using the astStatus macro, as follows: + +\small +\begin{terminalv} +int status; + +... + + +status = astStatus; +\end{terminalv} +\normalsize +c- +f+ +In this example, an error would be detected because we have attempted +to set a value for the Title attribute of a ZoomMap and a ZoomMap does +not have such an attribute. +f- + +c+ +A consequence of the AST error status being set is that almost all AST +functions will subsequently cease to function and will instead simply +return without action. This means that you do not need to use astOK +to check for errors very frequently. Instead, you can usually simply +invoke a succession of AST functions. If an error occurs in any of +them, the following ones will do nothing and you can check for the +error at the end, for example: +c- +f+ +A consequence of the error status variable STATUS being set to an +error value is that almost all AST routines will subsequently cease to +function and will instead simply return without action. This means +that you do not need to check for errors very frequently. Instead, you +can usually simply invoke a succession of AST routines. If an error +occurs in any of them, the following ones will do nothing and you can +check for the error at the end, for example: +f- + +c+ +\small +\begin{terminalv} +astFunctionA( ... ); +astFunctionB( ... ); +astFunctionC( ... ); +if ( !astOK ) { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + STATUS = 0 + + ... + + CALL AST_ROUTINEA( ... , STATUS ) + CALL AST_ROUTINEB( ... , STATUS ) + CALL AST_ROUTINEC( ... , STATUS ) + IF ( STATUS .NE. 0 ) THEN + + END IF +\end{terminalv} +\normalsize +f- + +c+ +There are, however, a few functions which do not adhere to this +general rule and which will attempt to execute if the AST error status +is set. These functions, such as astAnnul, are concerned with cleaning +up and recovering resources. For example, in the following: +c- +f+ +There are, however, a few routines which do not adhere to this general +rule and which will attempt to execute if their STATUS argument is +initially set. These routines, such as AST\_ANNUL, are concerned with +cleaning up and recovering resources. For example, in the following: +f- + +c+ +\small +\begin{terminalv} +zoommap = astZoomMap( 2, 5.0, "" ); + +astFunctionX( ... ); +astFunctionY( ... ); +astFunctionZ( ... ); + +zoommap = astAnnul( zoommap ); +if ( !astOK ) { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + STATUS = 0 + + ... + + ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ) + + CALL AST_ROUTINEX( ... , STATUS ) + CALL AST_ROUTINEY( ... , STATUS ) + CALL AST_ROUTINEZ( ... , STATUS ) + + CALL AST_ANNUL( ZOOMMAP, STATUS ) + IF ( STATUS .NE. 0 ) THEN + + END IF +\end{terminalv} +\normalsize +f- + +c+ +astAnnul will execute normally in order to recover the resources +associated with the ZoomMap that was created earlier, regardless of +whether an error has occurred in any of the intermediate functions. +Functions which behave in this way are noted in the relevant +descriptions in \appref{ss:functiondescriptions}. +c- +f+ +AST\_ANNUL will execute normally in order to recover the resources +associated with the ZoomMap that was created earlier, regardless of +whether an error has occurred in any of the intermediate routines. +Routines which behave in this way are noted in the relevant +descriptions in \appref{ss:functiondescriptions}. +f- + +c+ +If a serious error occurs, you will probably want to abort your +program, but sometimes you may want to recover and carry on. Because +very few AST functions will execute once the AST error status has been +set, you must first clear this status by using the astClearStatus +macro, as follows: +c- +f+ +If a serious error occurs, you will probably want to abort your +program, but sometimes you may want to recover and carry on. This is +simply done by resetting your error status variable to zero, whereupon +the AST routines you pass it to will execute normally again. +f- + +c+ +\small +\begin{terminalv} +astClearStatus; +\end{terminalv} +\normalsize +c- + +c+ +This will restore the AST error status to its OK value, so that AST +functions execute normally again. +c- + +c+ +Occasionally, you may also need to set the AST error status to an +explicit error value (see \secref{ss:channelsink} for an +example). This is done using astSetStatus and can be used to +communicate to AST that an error has occurred in some other item of +software, for example: +c- + +c+ +\small +\begin{terminalv} +int new_status; + +... + +astSetStatus( new_status ); +\end{terminalv} +\normalsize +c- + +c+ +The effect is that most AST routines will subsequently return without +action, just as if an error had occurred within the AST library +itself. +c- + +c+ +\subsection{Sharing the Error Status} + +In some software, it is usual to maintain a single integer error +status variable which is accessed by each function as it executes. If +an error occurs, this status variable is set and other functions can +detect this and take appropriate action. + +If you use AST in such a situation, it can be awkward to have a +separate internal error status used by AST functions alone. To remedy +this, AST is capable of sharing the error status variable used by any +other software, so long as they use the same conventions +(\emph{i.e.}\ a C int with the same ``OK'' value). To enable this +facility, you should pass the address of your status variable to +astWatch, as follows: + +\small +\begin{terminalv} +int my_status; +int *old_address; + +... + +old_address = astWatch( &my_status ); +\end{terminalv} +\normalsize + +Henceforth, instead of using its own internal error status variable, +AST will use the one you supply, so that it can detect errors flagged +by other parts of your software. The address of the original error +status variable is returned by astWatch, so you can restore the +original behaviour later if necessary. + +Note that this facility is not available \emph{via} the Fortran +interface to the AST library. +c- + +\cleardoublepage +\section{\label{ss:mappings}Inter-Relating Coordinate Systems (Mappings)} + +In \secref{ss:primer} we used the ZoomMap as an example of a +Mapping. We saw how it could be used to transform coordinates from its +input to its output and back again (\secref{ss:transforming}). We also +saw how its behaviour could be controlled by setting various +attributes, such as the Zoom factor and the Report attribute that made +it display coordinate values as it transformed them. + +In this section, we will look at Mappings a bit more thoroughly and +explore the behaviour which is common to all the Mappings provided by +AST. This is good background for what follows, because many of the +Objects we discuss later will also turn out to be Mappings in various +disguises. + +\subsection{\label{ss:mappingclass}The Mapping Class} + +Before we start, it is worth taking a quick look at the Mapping class +as a whole and some of the sub-classes it contains: + +\begin{terminalv} + Mapping + CmpMap + DssMap + GrismMap + IntraMap + LutMap + MathMap + MatrixMap + PermMap + PolyMap + SlaMap + SpecMap + TimeMap + UnitMap + WcsMap + ZoomMap + + Frame + +\end{terminalv} + +The Frame sub-class has been separated out here because it is covered +in detail in \secref{ss:frames}. We start by looking at the parent +class, Mapping. + +c+ +AST does not provide a function to create a basic Mapping +(\emph{i.e.}\ the astMapping constructor does not exist). This is +because the Mapping class itself is ``virtual'' and basic Mappings are +of no use in themselves. The Mapping class serves simply to contain +the various specialised Mappings that exist. +c- +f+ +AST does not provide a function to create a basic Mapping +(\emph{i.e.}\ the AST\_MAPPING constructor does not exist). This is +because the Mapping class itself is ``virtual'' and basic Mappings are +of no use in themselves. The Mapping class serves simply to contain +the various specialised Mappings that exist. +f- +However, it provides more than just a convenient heading for them +because it bestows all classes of Mapping with common properties +(\emph{e.g.}\ attributes) and behaviour. By examining the Mapping +class, we are therefore examining the things that all other Mappings +have in common. + +\subsection{The Mapping Model} + +The concept of a Mapping was illustrated in Figure~\ref{fig:mapping}. +It is a black box which you can supply with a set of coordinate values +in return for a set of transformed coordinates. The two sets are +termed \emph{input} and \emph{output} coordinates. You can also go +back the other way and transform output coordinates back into input +coordinates, as we saw in \secref{ss:transforming}. + +\subsection{Changing Attributes of a Mapping} + +Many classes of Mapping have attributes that provide values for parameter +used within the transformation. For instance, the ZoomMap class has an +attribute called ``Zoom'' that gives the scalar value by which each +coordinate is to be multiplied. These attribute values should be set when +the Mapping is created and should not be changed afterwards. Indeed, the +AST library will report an error if an attempt is made to change the +value of a Mapping attribute. This is because, once created, Mappings are +often later included within other objects such as FrameSets and CmpMaps. +This means that in general there could be many active references to a single +Mapping object within a program. Changing an attribute of the Mapping +via one particular reference (i.e pointer) would cause all the other +references to change too, with often undesirable or unpredictable +consequences. To avoid this, Mappings are considered \emph{immutable} in +most situations. The one exception is if the Mapping has not yet been +cloned or included in another Object (\emph{i.e.} it has a reference +couint of one) - changing the attributes of such a Mapping is allowed, +and will not generate an error. + +Note, the Invert attribute of a Mapping is not subject to this rule and +can be changed at any time. + +\subsection{Input and Output Coordinate Numbers} + +In general, the number of coordinates you feed into a Mapping to +represent a single point need not be the same as the number that comes +out. Often these numbers will be the same, and often they will both +equal 2 (because 2-dimensional coordinate systems are common), but +this needn't necessarily be the case. + +The number of coordinates required to specify an input point is +represented by the integer attribute Nin and the number required to +specify an output point is represented by Nout. These are read-only +attributes common to all Mappings. Generally, their values are fixed +when a Mapping is created. + +c+ +In \secref{ss:objectcreation}, we saw how the Nin attribute for a +ZoomMap was initialised by the call to the constructor function +astZoomMap which created it. In this case, the Nout attribute was not +needed and it implicitly took the same value as Nin, but we could +have enquired about its value had we wanted, as follows: +c- +f+ +In \secref{ss:objectcreation}, we saw how the Nin attribute for a +ZoomMap was initialised by the call to the constructor function +AST\_ZOOMMAP which created it. In this case, the Nout attribute was +not needed and it implicitly took the same value as Nout, but we could +have enquired about its value had we wanted, as follows: +f- + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstZoomMap *zoommap; +int nout; + +... + +nout = astGetI( zoommap, "Nout" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER NOUT, STATUS, ZOOMMAP + + STATUS = 0 + + ... + + NOUT = AST_GETI( ZOOMMAP, 'Nout', STATUS ) +\end{terminalv} +\normalsize +f- + +\subsection{Forward and Inverse Transformations} + +We stated earlier that a Mapping may be used to transform coordinates +either from input to output, or \emph{vice versa}. These are termed +its \emph{forward} and \emph{inverse} transformations. + +This statement was not quite accurate, however, because in general +Mappings are only \textbf{potentially} capable of working in both +directions. In practice, coordinate transformation may only be +feasible in one direction or the other because some functions are not +easily inverted (they may be multi-valued, for instance). Allowance +must be made for this, so each Mapping has two read-only boolean +(integer) attributes, TranForward and TranInverse, which indicate +whether each transformation is available. + +A transformation is available if the corresponding attribute is +non-zero, otherwise it is not.\footnote{Most of the Mappings provided +by the AST library work in both directions, although the LutMap can +behave otherwise.} If you enquire about the value of these attributes, +a value of 0 or 1 is returned. Attempting to use a Mapping to apply a +transformation which is not available will result in an error. + +\subsection{\label{ss:invertingmappings}Inverting Mappings} + +An important attribute, common to all Mappings, is the Invert +flag. This is a boolean (integer) attribute that can be assigned a new +value at any time. If it is non-zero, it has the effect of +interchanging the Mapping's input and output coordinates and the +Mapping is then said to be \emph{inverted}. By default, the Invert +attribute is zero. + +There is no magic in this. There is no fancy arithmetic involved in +inverting mathematical functions, for instance. The Invert flag is +simply a switch that interchanges a Mapping's input and output +ports. If it is non-zero, the Mapping's Nin and Nout attributes are +swapped, its TranForward and TranInverse attributes are swapped, and +when you ask for what was once the forward transformation you get the +inverse transformation instead (and \emph{vice versa}). When you +return the Invert attribute to zero, or clear it, the Mapping returns +to its original behaviour. + +c+ +Often, the actual value of the Invert attribute is unimportant and you +simply wish to invert its boolean sense, so that what was the +Mapping's input becomes its output and \emph{vice versa}. This is most +easily accomplished using astInvert, as follows: +c- +f+ +Often, the actual value of the Invert attribute is unimportant and you +simply wish to invert its boolean sense, so that what was the +Mapping's input becomes its output and \emph{vice versa}. This is most +easily accomplished using AST\_INVERT, as follows: +f- + +c+ +\small +\begin{terminalv} +AstMapping *mapping; + +... + +astInvert( mapping ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER MAPPING + + ... + + CALL AST_INVERT( MAPPING, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +If the Mapping you have happens to be the wrong way around, astInvert +allows you to correct the problem. +c- +f+ +If the Mapping you have happens to be the wrong way around, +AST\_INVERT allows you to correct the problem. +f- + +\subsection{Finding the Rate of Change of a Mapping Output} +The +c+ +astRate +c- +f+ +AST\_RATE +f- +function can be used to find the rate of change of any Mapping output +with respect to any Mapping input, at a given input position. The method +used produces good accuracy (typically a relative error of 10E-10 or +less) but may require the Mapping to be evaluated 100 or more times. +An estimate of the second derivative is also produced by this function. + + +\subsection{Reporting Coordinate Transformations} + +We have already seen (\secref{ss:transforming}) how the boolean +(integer) Report attribute of a Mapping works. If it is non-zero, the +operation of transforming a set of coordinates will result in a report +being written to standard output. This will display the coordinate +values before and after transformation. It can save considerable time +during program development by eliminating the need to add loops and +output statements to your program. + +c+ +In a finished program, however, you should be careful that the Report +attribute is not set to a non-zero value unless you want to see the +output (there may often be rather a lot of this!). To help prevent +unwanted output being produced by accident, the Report attribute is +unusual in that its value is not preserved when a Mapping is copied +using astCopy (\secref{ss:copyingobjects}). Instead, it reverts to its +default of zero (\emph{i.e.}\ un-set) in the copy. It also reverts to +zero when a Mapping is written out, \emph{e.g.}\ to a file using a +Channel (\secref{ss:channels}). +c- +f+ +In a finished program, however, you should be careful that the Report +attribute is not set to a non-zero value unless you want to see the +output (there may often be rather a lot of this!). To help prevent +unwanted output being produced by accident, the Report attribute is +unusual in that its value is not preserved when a Mapping is copied +using AST\_COPY (\secref{ss:copyingobjects}). Instead, it reverts to +its default of zero (\emph{i.e.}\ un-set) in the copy. It also reverts +to zero when a Mapping is written out, \emph{e.g.}\ to a file using a +Channel (\secref{ss:channels}). +f- + +%\subsection{TBW---More on Transforming Coordinates} + +\subsection{\label{ss:badcoordinates}Handling Missing (Bad) Coordinate Values} + +c+ +Even when coordinates can, in principle, be transformed in either +direction by a Mapping, there may still be instances where specific +coordinate values cannot be handled. For example, the Mapping may be +mathematically intractable (\emph{e.g.}\ singular) in certain places, +or it may map a subset of one space on to another, so that some points +in one space are not represented in the other. Sky projections often +show this behaviour, since it is quite common to project only half of +the celestial sphere on to two dimensions, omitting points on the +opposite side of the sky. There are many other examples. +c- +f+ +Even when coordinates can, in principle, be transformed in either +direction by a Mapping, there may still be instances where specific +coordinate values cannot be handled. For example, the Mapping may be +mathematically intractable (\emph{e.g.}\ singular) in certain places, +or it may map a subset of one space on to another, so that some points +in one space are not represented in the other. Sky projections often +show this behaviour, since it is quite common to project only half of +the celestial sphere on to two dimensions, omitting points on the +opposite side of the sky. There are many other examples. +f- + +c+ +To indicate when coordinates cannot be transformed, for whatever +reason, AST substitutes a special output coordinate value given by the +macro AST\_\_BAD (as defined in the ``ast.h'' header file). Before +making use of coordinates generated by any of the AST transformation +functions, therefore, you may need to check for the presence of this +value. +c- +f+ +To indicate when coordinates cannot be transformed, for whatever +reason, AST substitutes a special output coordinate value given by the +parameter constant AST\_\_BAD (as defined in the AST\_PAR include +file). Before making use of coordinates generated by any of the AST +transformation routines, therefore, you may need to check for the +presence of this value. +f- + +c+ +Because coordinates with the value AST\_\_BAD can be generated in this +way, all other AST functions are also capable of recognising this +value and handling it appropriately. The coordinate transformation +functions do this by propagating any missing input coordinate +information through to their output. This means that if you supply +coordinates with the value AST\_\_BAD, the returned coordinates are +also likely to contain this value. Here, for example, is what happens +if you use a ZoomMap (with Zoom factor 5) to transform such a set of +coordinates: +c- +f+ +Because coordinates with the value AST\_\_BAD can be generated in this +way, all other AST routines are also capable of recognising this value +and handling it appropriately. The coordinate transformation routines +do this by propagating any missing input coordinate information +through to their output. This means that if you supply coordinates +with the value AST\_\_BAD, the returned coordinates are also likely to +contain this value. Here, for example, is what happens if you use a +ZoomMap (with Zoom factor 5) to transform such a set of coordinates: +f- + +\small +\begin{terminalv} +(0, 0) --> (0, 0) +(, 2) --> (, 10) +(2, 4) --> (10, 20) +(3, 6) --> (15, 30) +(4, ) --> (20, ) +(5, 10) --> (25, 50) +(, ) --> (, ) +(7, 14) --> (35, 70) +(8, 16) --> (40, 80) +(9, 18) --> (45, 90) +\end{terminalv} +\normalsize + +The AST\_\_BAD value is represented by the string ``$<$bad$>$''. This +is a case of ``garbage in, garbage out'' but at least it's consistent +garbage that you can recognise! + +Note how the presence of the AST\_\_BAD value in one input dimension +does not necessarily result in the loss of information for all output +dimensions. Sometimes, such loss will be unavoidable, but in general +an attempt is made to preserve information as far as possible. The +exact behaviour will depend on the Mapping involved. + +\subsection{\label{ss:unitmapexample}Example---the UnitMap} + +The UnitMap is the simplest of Mappings. It is a null Mapping. Its +purpose is simply to copy coordinate values, unaltered, from its input +to its output and \emph{vice versa}. + +A UnitMap has no additional attributes beyond those of a basic +Mapping. Its Nin and Nout attributes are always equal and are +specified by the first argument supplied to its constructor. For +example: + +c+ +\small +\begin{terminalv} +AstUnitMap *unitmap; + +... + +unitmap = astUnitMap( 2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER UNITMAP + + ... + + UNITMAP = AST_UNITMAP( 2, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +will create a UnitMap that copies 2-dimensional coordinates. Inverting +a UnitMap has no effect beyond changing the value of its Invert +attribute. + +c+ +The main use of a UnitMap is to allow a Mapping to be supplied when one +is required (as an argument to a function, for example) but you wish +it to leave coordinate values unchanged. +c- +f+ +The main use of a UnitMap is to allow a Mapping to be supplied when one +is required (as an argument to a routine, for example) but you wish +it to leave coordinate values unchanged. +f- + +\subsection{\label{ss:permmapexample}Example---the PermMap} + +The PermMap is a rather more complicated Mapping than we have met +previously. Its purpose is to change the order, or number, of +coordinates. It is also able to substitute fixed values for +coordinates. + +To illustrate its action, suppose our input coordinates are denoted by +($x_1,x_2,x_3,x_4$) in a 4-dimensional space and suppose our output +coordinates are to be ($x_4,x_1,x_2,x_3$). Our PermMap, therefore, +should rotate the coordinate values by one position. + +c+ +To create such a PermMap, we first set up two integer arrays. One of +these, ``outperm'', controls the selection of input coordinates for +use in the output and the other, ``inperm'', controls selection of +output coordinates for use in the input: +c- +f+ +To create such a PermMap, we first set up two integer arrays. One of +these, OUTPERM, controls the selection of input coordinates for use in +the output and the other, INPERM, controls selection of output +coordinates for use in the input: +f- + +c+ +\small +\begin{terminalv} +int outperm[ 4 ] = { 4, 1, 2, 3 }; +int inperm[ 4 ] = { 2, 3, 4, 1 }; +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER OUTPERM( 4 ), INPERM( 4 ) + DATA OUTPERM / 4, 1, 2, 3 / + DATA INPERM / 2, 3, 4, 1 / +\end{terminalv} +\normalsize +f- + +Note that the numbers we store in these arrays are the indices of the +coordinates that we want to select. We have chosen these so that the +forward and inverse transformations will perform complementary +permutations on the coordinates. + +The PermMap is then created by passing these arrays to its +constructor, as follows: + +c+ +\small +\begin{terminalv} +AstPermMap *permmap; + +... + +permmap = astPermMap( 4, inperm, 4, outperm, NULL, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER PERMMAP + DOUBLE PRECISION DUMMY( 1 ) + + ... + + PERMMAP = AST_PERMMAP( 4, INPERM, 4, OUTPERM, DUMMY, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +f+ +(the fifth argument is not being used, so a dummy array has been supplied). +f- +Note that we specify the number of input and output coordinates +separately, but set both to 4 in this example. The resulting PermMap +would have the following effect when used to transform coordinates: + +\begin{terminalv} +Forward: + (1, 2, 3, 4) --> (4, 1, 2, 3) + (2, 4, 6, 8) --> (8, 2, 4, 6) + (3, 6, 9, 12) --> (12, 3, 6, 9) + (4, 8, 12, 16) --> (16, 4, 8, 12) + (5, 10, 15, 20) --> (20, 5, 10, 15) + +Inverse: + (4, 1, 2, 3) --> (1, 2, 3, 4) + (8, 2, 4, 6) --> (2, 4, 6, 8) + (12, 3, 6, 9) --> (3, 6, 9, 12) + (16, 4, 8, 12) --> (4, 8, 12, 16) + (20, 5, 10, 15) --> (5, 10, 15, 20) +\end{terminalv} + +c+ +If the number of input and output coordinates are unequal so, also, +will be the size of the ``outperm'' and ``inperm'' arrays. This means, +however, that we cannot fill them with coordinate indices so that they +perform complementary permutations, because one transformation will +lose information (discard a coordinate) that the other cannot recover. +To give an example, consider the following: +c- +f+ +If the number of input and output coordinates are unequal so, also, +will be the size of the OUTPERM and INPERM arrays. This means, +however, that we cannot fill them with coordinate indices so that they +perform complementary permutations, because one transformation will +lose information (discard a coordinate) that the other cannot recover. +To give an example, consider the following: +f- + +c+ +\small +\begin{terminalv} +int outperm[ 3 ] = { 4, 3, 2 }; +int inperm[ 4 ] = { -1, 3, 2, 1 }; +double con[ 1 ] = { 99.004 }; +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER OUTPERM( 3 ), INPERM( 4 ) + DOUBLE PRECISION CONST( 1 ) + DATA OUTPERM / 4, 3, 2 / + DATA INPERM / -1, 3, 2, 1 / + DATA CONST / 99.004D0 / +\end{terminalv} +\normalsize +f- + +c+ +In this case, the forward transformation will change +($x_1,x_2,x_3,x_4$) into ($x_4,x_3,x_2$) and will discard $x_1$. The +inverse transformation restores the original coordinate order, but has +no value to assign to the first coordinate. In this case, the number +entered in the ``inperm'' array is $-$1. +c- +f+ +In this case, the forward transformation will change +($x_1,x_2,x_3,x_4$) into ($x_4,x_3,x_2$) and will discard $x_1$. The +inverse transformation restores the original coordinate order, but has +no value to assign to the first coordinate. In this case, the number +entered in the INPERM array is $-$1. +f- + +c+ +This negative value indicates that the coordinate value should be +obtained by addressing the first element of the ``con'' array +(\emph{i.e.}\ element zero). This array, ignored in the previous +example, may then be used to supply a value for the missing +coordinate. +c- +f+ +This negative value indicates that the coordinate value should be +obtained by addressing the CONST array using an index of 1 (the +absolute value). This array, ignored in the previous example, may then +be used to supply a value for the missing coordinate. +f- + +The constructor function: + +c+ +\small +\begin{terminalv} +permmap = astPermMap( 4, inperm, 3, outperm, con, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + PERMMAP = AST_PERMMAP( 4, INPERM, 3, OUTPERM, CONST, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +will then create a PermMap with the following effect when used to +transform coordinates: + +\begin{terminalv} +Forward: + (1, 2, 3, 4) --> (4, 3, 2) + (2, 4, 6, 8) --> (8, 6, 4) + (3, 6, 9, 12) --> (12, 9, 6) + (4, 8, 12, 16) --> (16, 12, 8) + (5, 10, 15, 20) --> (20, 15, 10) + +Inverse: + (4, 3, 2) --> (99.004, 2, 3, 4) + (8, 6, 4) --> (99.004, 4, 6, 8) + (12, 9, 6) --> (99.004, 6, 9, 12) + (16, 12, 8) --> (99.004, 8, 12, 16) + (20, 15, 10) --> (99.004, 10, 15, 20) +\end{terminalv} + +c+ +The ``con'' array may contain more than one value if necessary and may +be addressed by both the ``inperm'' and ``outperm'' arrays using +coordinate indices $-$1, $-$2, $-$3,~\emph{etc.}\ to refer to the +first, second, third,~\emph{etc.}\ elements. +c- +f+ +The CONST array may contain more than one value if necessary and may +be addressed by both the INPERM and OUTPERM arrays using coordinate +indices $-$1, $-$2, $-$3,~\emph{etc.}\ to refer to the first, second, +third,~\emph{etc.}\ elements. +f- + +c+ +If there is no suitable replacement value that can be supplied +\emph{via} the ``con'' array, a value of zero may be entered into the +``outperm'' and/or ``inperm'' arrays. This causes the value AST\_\_BAD +to be used for the affected coordinate (as defined in the ``ast.h'' +header file), thus indicating a missing coordinate value +(\secref{ss:badcoordinates}). +c- +f+ +If there is no suitable replacement value that can be supplied +\emph{via} the CONST array, a value of zero may be entered into the +OUTPERM and/or INPERM arrays. This causes the value AST\_\_BAD to be +used for the affected coordinate (as defined in the AST\_PAR include +file), thus indicating a missing coordinate value +(\secref{ss:badcoordinates}). +f- + +The principle use for a PermMap lies in matching a coordinate system +to a data array where there is a choice of storage order for the data. +PermMaps are also useful for discarding unwanted coordinates so as to +reduce the number of dimensions, such as when selecting a ``slice'' +from a multi-dimensional array. + +\cleardoublepage +\section{\label{ss:cmpmaps}Compound Mappings (CmpMaps)} + +We now turn to a rather special form of Mapping, the CmpMap. The +Mappings we have considered so far have been atomic, in the sense that +they perform pre-defined elementary transformations. A CmpMap, +however, is a compound Mapping. In essence, it is a framework for +containing other Mappings and its purpose is to allow those Mappings +to work together in various combinations while appearing as a single +Object. A CmpMap's behaviour is therefore not pre-defined, but is +determined by the other Mappings it contains. + +\subsection{\label{ss:seriescmpmap}Combining Mappings in Series} + +Consider a simple example based on two 2-dimensional coordinate +systems. Suppose that to convert from one to the other we must swap +the coordinate order and multiply both coordinates by 5, so that the +coordinates ($x_1,x_2$) transform into ($5x_2,5x_1$). This can be done +in two stages: + +\begin{enumerate} +\item Apply a PermMap (\secref{ss:permmapexample}) to swap the +coordinate order. + +\item Apply a ZoomMap (\secref{ss:transforming}) to multiply both +coordinate values by the constant 5. +\end{enumerate} + +The PermMap and ZoomMap are then said to operate \emph{in series}, +because they are applied sequentially +(\emph{c.f.}\ Figure~\ref{fig:seriescmpmap}). We can create a CmpMap +that applies these Mappings in series as follows: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstCmpMap *cmpmap; +AstPermMap *permmap; +AstZoomMap *zoommap; + +... + +/* Create the individual Mappings. */ +{ + int inperm[ 2 ] = { 2, 1 }; + int outperm[ 2 ] = { 2, 1 }; + permmap = astPermMap( 2, inperm, 2, outperm, NULL, "" ); +} +zoommap = astZoomMap( 2, 5.0, "" ) + +/* Combine them in series. */ +cmpmap = astCmpMap( permmap, zoommap, 1, "" ); + +/* Annul the individual Mapping pointers. */ +permmap = astAnnul( permmap ); +zoommap = astAnnul( zoommap ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER CMPMAP, PERMMAP, STATUS, ZOOMMAP + INTEGER INPERM( 2 ), OUTPERM( 2 ), CONST( 1 ) + DATA INPERM / 1, 2 / + DATA OUTPERM / 1, 2 / + + STATUS = 0 + + ... + +* Create the individual Mappings. + PERMMAP = AST_PERMMAP( 2, INPERM, 2, OUTPERM, CONST, ' ', STATUS ) + ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ) + +* Combine them in series. + CMPMAP = AST_CMPMAP( PERMMAP, ZOOMMAP, .TRUE., ' ', STATUS ) + +* Annul the individual Mapping pointers. + CALL AST_ANNUL( PERMMAP, STATUS ) + CALL AST_ANNUL( ZOOMMAP, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, the third argument (1) of the constructor function astCmpMap +indicates ``in series''. +c- +f+ +Here, the third argument (.TRUE.) of the constructor function +AST\_CMPMAP indicates ``in series''. +f- + +When used to transform coordinates in the forward direction, the +resulting CmpMap will apply the first component Mapping (the PermMap) +and then the second one (the ZoomMap). When transforming in the +inverse direction, it will apply the second one (in the inverse +direction) and then the first one (also in the inverse direction). In +general, although not in this particular example, the order in which +the two component Mappings are supplied is significant. Clearly, also, +the Nout attribute (number of output coordinates) for the first +Mapping must equal the Nin attribute (number of input coordinates) for +the second one. + +\subsection{Combining Mappings in Parallel} + +Connecting two Mappings in series (\secref{ss:seriescmpmap}) is not the +only way of combining them. The alternative, \emph{in parallel}, +involves applying the two Mappings at once but on different subsets of +the coordinate values. + +Consider, for example, a set of 3-dimensional coordinates and suppose +we wish to transform them by swapping the first two coordinate values +and multiplying the final one by 5, so that ($x_1,x_2,x_3$) transforms +into ($x_2,x_1,5x_3$). Again, we can perform each of these steps +individually using Mappings similar to the PermMap and ZoomMap used +earlier (\secref{ss:seriescmpmap}). In this case, however, the ZoomMap is +1-dimensional and the individual Mappings are applied in parallel +(\emph{c.f.}\ Figure~\ref{fig:parallelcmpmap}). + +Creating a CmpMap for this purpose is also very simple: + +c+ +\small +\begin{terminalv} +cmpmap = astCmpMap( permmap, zoommap, 0, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CMPMAP = AST_CMPMAP( PERMMAP, ZOOMMAP, .FALSE., ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The only difference is that the third argument of astCmpMap is now +zero, meaning ``in parallel''. +c- +f+ +The only difference is that the third argument of AST\_CMPMAP is now +.FALSE., meaning ``in parallel''. +f- + +As before, the order in which the two component Mappings are supplied +is significant. The first one acts on the lower-numbered input +coordinate values (however many it needs) and produces the +lower-numbered output coordinates, while the second Mapping acts on +the higher-numbered input coordinates (however many remain) and +generates the remaining higher-numbered output coordinates. When the +CmpMap transforms coordinates in the inverse direction, both component +Mappings are applied to the same coordinates, but in the inverse +direction. + +Note that the Nin and Nout attributes of the component Mappings +(\emph{i.e.}\ the numbers of input and output coordinates) will sum to +give the Nin and Nout attributes of the overall CmpMap. + +\subsection{\label{ss:cmpmapcomponents}The Component Mappings} + +c+ +A CmpMap does not store copies of its component Mappings, but simply +holds pointers to them. In the example above +(\secref{ss:seriescmpmap}), we were free to annul the individual +Mapping pointers after creating the CmpMap because the pointers held +internally by the CmpMap increased the reference count (RefCount +attribute) of each component Mapping by one. The individual components +are therefore not deleted by astAnnul, but retained until the CmpMap +itself is deleted and annuls the pointers it holds. Consistent use of +astAnnul (\secref{ss:annullingpointers}) and/or pointer contexts +(\secref{ss:contexts}) will therefore ensure that all Objects are +deleted at the appropriate time. +c- +f+ +A CmpMap does not store copies of its component Mappings, but simply +holds pointers to them. In th example above (\secref{ss:seriescmpmap}), +we were free to annul the individual Mapping pointers after creating +the CmpMap because the pointers held internally by the CmpMap +increased the reference count (RefCount attribute) of each component +Mapping by one. The individual components are therefore not deleted by +AST\_ANNUL, but retained until the CmpMap itself is deleted and annuls +the pointers it holds. Consistent use of AST\_ANNUL +(\secref{ss:annullingpointers}) and/or pointer contexts +(\secref{ss:contexts}) will therefore ensure that all Objects are +deleted at the appropriate time. +f- + +Note that access to a CmpMap's component Mappings is not generally +available unless pointers to them are retained when the CmpMap is +created. If such pointers are retained, then subsequent modifications +to the individual components can be used to indirectly modify the +behaviour of the overall CmpMap. + +There is an important exception to this, however, because a CmpMap +retains a copy of the initial Invert flag settings of each of its +components and uses these in order to ignore any subsequent external +changes. This means that you may invert either component Mapping +before inserting it into a CmpMap and need not worry if you un-invert +it again later. The CmpMap's behaviour will not be affected by the +later action. + +\subsection{\label{ss:complexcmpmap}Creating More Complex Mappings} + +c+ +Because a CmpMap is itself a Mapping, any existing CmpMap can +substitute (\secref{ss:objecthierarchy}) as a component Mapping when +constructing a new CmpMap using astCmpMap. This has the effect of +nesting one CmpMap inside another and opens up many new possibilities. +For example, combining three Mappings in series can be accomplished as +follows: +c- +f+ +Because a CmpMap is itself a Mapping, any existing CmpMap can +substitute (\secref{ss:objecthierarchy}) as a component Mapping when +constructing a new CmpMap using AST\_CMPMAP. This has the effect of +nesting one CmpMap inside another and opens up many new possibilities. +For example, combining three Mappings in series can be accomplished as +follows: +f- + +c+ +\small +\begin{terminalv} +AstMapping *map1, *map2, *map3; + +... + +cmpmap = astCmpMap( map1, astCmpMap( map2, map3, 1, "" ), 1, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER MAP1, MAP2, MAP3 + + ... + + CMPMAP = AST_CMPMAP( MAP1, AST_CMPMAP( MAP2, MAP3, .TRUE., ' ', STATUS ), + : .TRUE., ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +The way in which the individual component Mappings are grouped within +the nested CmpMaps is not usually important. + +A similar technique can be used to combine multiple Mappings in +parallel and, of course, mixed series and parallel combinations are +also possible (Figure~\ref{fig:complexcmpmap}). There is no built-in +limit to how many CmpMaps may be nested in this way, so this mechanism +provides an indefinitely extensible method of building complex +Mappings out of the elemental building blocks provided by AST. + +In practice, you might not need to construct such complex CmpMaps +yourself very frequently, but they will often be returned by AST +routines. Nested CmpMaps underlie the library's entire ability to +represent a wide range of different coordinate transformations. + +\subsection{\label{ss:cmpmapexample}Example---Transforming Between Two Calibrated Images} + +Consider, as a practical example of CmpMaps, two images of the +sky. Suppose that for each image we have a Mapping which converts from +pixel coordinates to a standard celestial coordinate system, say +FK5~(J2000.0). If we wish to inter-compare these images, we can do so +by using this celestial coordinate system to align them. That is, we +first convert from pixel coordinates in the first image into FK5 +coordinates and we then convert from FK5 coordinates into pixel +coordinates in the second image. + +c+ +If ``mapa'' and ``mapb'' are pointers to our two original Mappings, we +could form a CmpMap which transforms directly between the pixel +coordinates of the first and second images by combining these +Mappings, as follows: +c- +f+ +If MAPA and MAPB are pointers to our two original Mappings, we could +form a CmpMap which transforms directly between the pixel coordinates +of the first and second images by combining these Mappings, as +follows: +f- + +c+ +\small +\begin{terminalv} +AstCmpMap *alignmap; +AstMapping *mapa, *mapb; + +... + +astInvert( mapb ); +alignmap = astCmpMap( mapa, mapb, 1, "" ); +astInvert( mapb ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER ALIGNMAP, MAPA, MAPB + + ... + + CALL AST_INVERT( MAPB, STATUS ) + ALIGNMAP = AST_CMPMAP( MAPA, MAPB, .TRUE., ' ', STATUS ) + CALL AST_INVERT( MAPB, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, we have used astInvert (\secref{ss:invertingmappings}) to invert +``mapb'' before inserting it into the CmpMap because, as supplied, it +converted in the wrong direction. Afterwards, we invert it again to +return it to its original state. The CmpMap, however, will ignore this +subsequent change (\secref{ss:cmpmapcomponents}). +c- +f+ +Here, we have used AST\_INVERT (\secref{ss:invertingmappings}) to +invert MAPB before inserting it into the CmpMap because, as supplied, +it converted in the wrong direction. Afterwards, we invert it again to +return it to its original state. The CmpMap, however, will ignore this +subsequent change (\secref{ss:cmpmapcomponents}). +f- + +The forward transformation of the resulting CmpMap will now transform +from pixel coordinates in the first image to pixel coordinates in the +second image, while its inverse transformation will convert in the +opposite direction. + +\subsection{\label{ss:overcomplexcmpmaps}Over-Complex Compound Mappings} + +While a CmpMap provides a very flexible way of constructing +arbitrarily complex Mappings (\secref{ss:complexcmpmap}), it +unfortunately also provides an opportunity for representing simple +Mappings in complex ways. Sometimes, unnecessary complexity can be +difficult to avoid but can obscure important simplifications. + +Consider the example above (\secref{ss:cmpmapexample}), in which we +inter-related two images of the sky \emph{via} a CmpMap. If the two +images turned out to be simply offset from each other by a shift along +each pixel axis, then this approach would align them correctly, but it +would be inefficient. This is because it would introduce unnecessary +and expensive transformations to and from an intermediate celestial +coordinate system, whereas a simple shift of pixel origin would +suffice. + +Recognising that a simpler and more efficient solution exists +obviously requires a little more than simply joining two Mappings +end-to-end. We must also determine whether the resulting CmpMap is +more complex than it needs to be, \emph{i.e.}\ contains redundant +information. If it is, we then need a way to simplify it. + +The problem is not always just one of efficiency, however. Sometimes +we may also need to know something about the actual form a Mapping +takes---\emph{i.e.}\ the nature of the operations it performs. +Unnecessary complexity can obscure this, but such complexity can +easily accumulate during normal data processing. + +For example, a Mapping that transforms pixel coordinates into +positions on the sky might be repeatedly modified as changes are made +to the shape and size of the image. Typically, on each occasion, +another Mapping will be concatenated to reflect what has happened to +the image. This could soon make it difficult to discern the overall +nature of the transformation from the complex CmpMap that +accumulates. If only shifts of origin were involved on each occasion, +however, they could be combined into a single shift which could be +represented much more simply. + +Suppose we now wanted to represent our image's celestial coordinate +calibration using FITS conventions (\secref{ss:foreignfits}). This +requires AST to determine whether the Mapping which relates pixel +coordinate to sky positions conforms to the FITS model (for example, +whether it is equivalent to applying a single set of shifts and scale +factors followed by a map projection). Clearly, there is an important +use here for some means of simplifying the internal structure of a +CmpMap. + +\subsection{\label{ss:simplifyingcmpmaps}Simplifying Compound Mappings} + +c+ +The ability to simplify compound Mappings is provided by the +astSimplify function. This function encapsulates a number of +heuristics for converting Mappings, or combinations of Mappings within +a CmpMap, into simpler, equivalent ones. When applied to a CmpMap, +astSimplify tries to reduce the number of individual Mappings within +it by merging neighbouring component Mappings together. It will do +this with both series and parallel combinations of Mappings, or both, +and will handle CmpMaps nested to any depth +(\secref{ss:complexcmpmap}). +c- +f+ +The ability to simplify compound Mappings is provided by the +AST\_SIMPLIFY function. This function encapsulates a number of +heuristics for converting Mappings, or combinations of Mappings within +a CmpMap, into simpler, equivalent ones. When applied to a CmpMap, +AST\_SIMPLIFY tries to reduce the number of individual Mappings within +it by merging neighbouring component Mappings together. It will do +this with both series and parallel combinations of Mappings, or both, +and will handle CmpMaps nested to any depth +(\secref{ss:complexcmpmap}). +f- + +c+ + To illustrate how astSimplify works, consider the combination of + Mappings shown in Figure~\ref{fig:simplifyexample}. +c- +f+ + To illustrate how AST\_SIMPLIFY works, consider the combination of + Mappings shown in Figure~\ref{fig:simplifyexample}. +f- + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/simpexamp} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/simpexamp} +f- + \caption[An over-complex compound Mapping.]{An over-complex compound Mapping, consisting of PermMaps, + ZoomMaps and a UnitMap, which can be simplified to become a single + UnitMap. The enclosing nested CmpMaps have been omitted for clarity.} + \label{fig:simplifyexample} + \end{center} + \end{figure} + +If this were contained in a CmpMap, it could be simplified as follows: + +c+ +\small +\begin{terminalv} +AstMapping *simpler; + +... + +simpler = astSimplify( cmpmap ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER SIMPLER + + ... + + SIMPLER = AST_SIMPLIFY( CMPMAP, STATUS ); +\end{terminalv} +\normalsize +f- + +c+ +In this case, the result would be a simple 3-dimensional UnitMap (the +identity Mapping). To reach this conclusion, astSimplify will have +made a number of deductions, roughly as follows: +c- +f+ +In this case, the result would be a simple 3-dimensional UnitMap (the +identity Mapping). To reach this conclusion, AST\_SIMPLIFY will have +made a number of deductions, roughly as follows: +f- + +\begin{enumerate} +\item The two 2-dimensional ZoomMaps in series are equivalent to a +single ZoomMap with a combined Zoom factor of unity. This, in turn, is +equivalent to a 2-dimensional UnitMap. + +\item This UnitMap in parallel with the other 1-dimensional UnitMap is +equivalent to a single 3-dimensional UnitMap. This UnitMap, sandwiched +between any other pair of Mappings, can then be eliminated. + +\item The remaining two PermMaps in series are equivalent to a single +3-dimensional PermMap. When these are combined, the resulting PermMap +is found to be equivalent to a 3-dimensional UnitMap. +\end{enumerate} + +c+ +This example is a little contrived, but illustrates how astSimplify +can deal with even quite complicated compound Mappings through a +series of incremental simplifications. Where possible, this will +result in either a simpler compound Mapping or, if feasible, an atomic +(non-compound) Mapping, as here. If no simplification is possible, +astSimplify will just return a pointer to the original Mapping. +c- +f+ +This example is a little contrived, but illustrates how AST\_SIMPLIFY +can deal with even quite complicated compound Mappings through a +series of incremental simplifications. Where possible, this will +result in either a simpler compound Mapping or, if feasible, an atomic +(non-compound) Mapping, as here. If no simplification is possible, +AST\_SIMPLIFY will just return a pointer to the original Mapping. +f- + +c+ +Although astSimplify cannot identify every simplification that is +theoretically possible, sufficient rules are included to deal with the +most common and important cases. +c- +f+ +Although AST\_SIMPLIFY cannot identify every simplification that is +theoretically possible, sufficient rules are included to deal with the +most common and important cases. +f- + +\cleardoublepage +\section{\label{ss:frames}Representing Coordinate Systems (Frames)} + +An AST Frame is an Object that is used to represent a coordinate +system. Contrast this with a Mapping (\secref{ss:mappings}), which is +used to describe how to convert between coordinate systems. The two +concepts are complementary and we will see how they work together in +\secref{ss:framesets}. + +In this section we will discuss only basic Frames, which represent +Cartesian coordinate systems. More specialised types of Frame +(\emph{e.g.}\ the SkyFrame, which represents celestial coordinate +systems, and the SpecFrame, which represents spectral coordinate +systems) are covered later (\secref{ss:skyframes} and \secref{ss:specframes}) +and, naturally, inherit the properties and behaviour of the simple Frames +discussed here. + +\subsection{The Frame Model} + +The best way to think about a Frame is like the frame that you would +plot around a graph. In two dimensions, you would have an ``$x$'' and +a ``$y$'' axis, a title on the graph and labels on the axes, together +with an indication of the physical units being plotted. The values +marked along each axis would be formatted in a human-readable way. The +frame around a graph therefore defines a coordinate space within which +you can locate points, draw lines, calculate distances, \emph{etc.} + +An AST Frame works in much the same way, embodying all of these +concepts and a few more. It also allows any number of axes, which +means that a Frame can represent coordinate systems with any number of +dimensions. You specify how many when you create it. + +Remember that the basic Frame we are considering here is completely +general. It knows nothing of celestial coordinates, for example, and +all its axes are equivalent. It can be adapted to describe any general +purpose Cartesian coordinate system by setting its attributes, such as +its Title and axis Labels, \emph{etc.}\ to appropriate values. + +\subsection{\label{ss:creatingframes}Creating a Frame} + +Creating a Frame is straightforward and follows the usual pattern: + +c+ +\small +\begin{terminalv} +#include "ast.h" +astFrame *frame; + +... + +frame = astFrame( 2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER FRAME, STATUS + + STATUS = 0 + + ... + + FRAME = AST_FRAME( 2, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The first argument of the astFrame constructor function specifies the +number of axes which the Frame should have. +c- +f+ +The first argument of the AST\_FRAME constructor function specifies +the number of axes which the Frame should have. +f- + +\subsection{\label{ss:frameasmapping}Using a Frame as a Mapping} + +We should briefly point out that the Frame we created above +(\secref{ss:creatingframes}) is also a Mapping +(\secref{ss:mappingclass}) and therefore inherits the properties and +behaviour common to other Mappings. + +c+ +One way to see this is to set the Frame's Report attribute (inherited +from the Mapping class) to a non-zero value and pass the Frame pointer +to a coordinate transformation function, such as astTran2. +c- +f+ +One way to see this is to set the Frame's Report attribute (inherited +from the Mapping class) to a non-zero value and pass the Frame pointer +to a coordinate transformation routine, such as AST\_TRAN2. +f- + +c+ +\small +\begin{terminalv} +double xin[ 5 ] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 }; +double yin[ 5 ] = { 0.0, 2.0, 4.0, 6.0, 8.0, 10.0 }; +double xout[ 5 ]; +double yout[ 5 ]; + +... + +astSet( frame, "Report=1" ); +astTran2( frame, 5, xin, yin, 1, xout, yout ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION XIN( 5 ), YIN( 5 ), XOUT( 5 ), YOUT( 5 ) + DATA XIN / 0D0, 1D0, 2D0, 3D0, 4D0, 5D0 / + DATA YIN / 0D0, 2D0, 4D0, 6D0, 8D0, 10D0 / + + CALL AST_SET( FRAME, 'Report=1', STATUS ) + CALL AST_TRAN2( FRAME, 5, XIN, YIN, .TRUE., XOUT, YOUT, STATUS ) +\end{terminalv} +\normalsize +f- + +The resulting output might then look like this: + +\begin{terminalv} +(1, 2) --> (1, 2) +(2, 4) --> (2, 4) +(3, 6) --> (3, 6) +(4, 8) --> (4, 8) +(5, 10) --> (5, 10) +\end{terminalv} + +This is not very exciting because a Frame implements an identity +transformation just like a UnitMap +(\secref{ss:unitmapexample}). However, it illustrates that a Frame can +be used as a Mapping and that its Nin and Nout attributes are both +equal to the number of Frame axes. + +When we consider more specialised Frames +(\emph{e.g.}~\secref{ss:framesets}), we will see that using them as +Mappings can be very useful indeed. + +\subsection{\label{ss:frameaxisattributes}Frame Axis Attributes} + +Frames have a number of attributes which can take multiple values, one +for each axis. These separate values are identified by appending the +axis number in parentheses to the attribute name. For example, the +Label(1) attribute is a character string containing the label which +appears on the first axis. + +Axis attributes are accessed in the same way as all other attributes +(\secref{ss:gettingattributes}, \secref{ss:settingattributes} and +\secref{ss:defaultingattributes}). For example, the Label on the second +axis might be obtained as follows: + +c+ +\small +\begin{terminalv} +const char *label; + +... + +label = astGetC( frame, "Label(2)" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 70 ) LABEL + + ... + + LABEL = AST_GETC( FRAME, 'Label(2)', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Other attribute access functions (astSetX, astTest and astClear) may +also be applied to axis attributes in the same way. +c- +f+ +Other attribute access routines (AST\_SETx, AST\_TEST and AST\_CLEAR) +may also be applied to axis attributes in the same way. +f- + +If the axis number is stored in a program variable, then its value +must be formatted to generate a suitable attribute name before using +this to access the attribute itself. For example, the following will +print out the Label value for each axis of a Frame: + +c+ +\small +\begin{terminalv} +#include +char name[ 18 ]; +int iaxis, naxes; + +... + +naxes = astGetI( frame, "Naxes" ); +for ( iaxis = 1; iaxis <= naxes; iaxis++ ) { + (void) sprintf( name, "Label(%d)", iaxis ); + label = astGetC( frame, name ); + (void) printf( "Label %2d: %s\n", iaxis, label ); +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 10 ) AXIS + INTEGER IAXIS + + ... + + DO 1 IAXIS = 1, AST_GETI( FRAME, 'Naxes', STATUS ) + WRITE ( AXIS, '( I10 )' ) IAXIS + LABEL = AST_GETC( FRAME, 'Label(' // AXIS // ')', STATUS ) + WRITE ( *, 199 ) IAXIS, LABEL + 199 FORMAT ( 'Label ', I2, ': ', A ) + 1 CONTINUE +\end{terminalv} +\normalsize +f- + +Note the use of the Naxes attribute to determine the number of Frame +axes. + +The output from this might look like the following: + +\begin{terminalv} +Label 1: Axis 1 +Label 2: Axis 2 +\end{terminalv} + +In this case, the Frame's default axis Labels have been revealed as +rather un-exciting. Normally, you would set much more useful values, +typically when you create the Frame---perhaps something like: + +c+ +\small +\begin{terminalv} +frame = astFrame( 2, "Label(1)=Offset from centre of field," + "Unit(1) =mm," + "Label(2)=Transmission coefficient," + "Unit(2) =%" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + FRAME = AST_FRAME( 2, 'Label(1)=Offset from centre of field,' // + 'Unit(1) =mm,' // + 'Label(2)=Transmission coefficient,' // + 'Unit(2) =%', STATUS ) +\end{terminalv} +\normalsize +f- + +Here, we have also set the (character string) Unit attribute for each +axis to describe the physical units represented on that axis. All the +attribute assignments have been combined into a single string, +separated by commas. + +\subsection{\label{ss:frameattributes}Frame Attributes} + +We will now briefly outline the various attributes associated with a +Frame (this is, of course, in addition to those inherited from the +Mapping class). We will not delve too deeply into the details of each +attribute, for which you should consult the appropriate description in +\appref{ss:attributedescriptions}. Instead, we aim simply to sketch +the range of facilities available: + +\begin{quote} +\begin{description} +\item[Naxes]\mbox{}\\ +A read-only integer giving the number of Frame axes. + +\item[Title]\mbox{}\\ +A string describing the coordinate system which the Frame represents. + +\item[Label(axis)]\mbox{}\\ +A label string for each axis. + +\item[Unit(axis)]\mbox{}\\ +A string describing the physical units on each axis. You can choose +whether to make this attribute ``active'' or ``passive'' (using +c+ +astSetActiveUnit +c- +f+ +AST\_SETACTIVEUNIT +f- +). If active, its value will be taken into account when finding the +Mapping between two Frames (\emph{e.g.} a scaling of 0.001 would be used +to connect two axis with units of ``km'' and ``m''). If passive, its value +is ignored. Its use is described in more detail in \secref{ss:frameunits}. + +\item[Symbol(axis)]\mbox{}\\ +A string containing a ``short form'' symbol (\emph{e.g.}\ like ``X'' +or ``Y'') used to represent the quantity plotted on each axis. + +\item[Digits/Digits(axis)]\mbox{}\\ +The preferred number of digits of precision to be used when formatting +values for display on each axis. + +\item[Format(axis)]\mbox{}\\ +A string containing a \emph{format specifier} which determines exactly +how values should be formatted for display on each axis +(\secref{ss:formattingaxisvalues}). If this attribute is un-set, the +formatting is based on the Digits value, otherwise the Format string +over-rides the Digits value. + +\item[Direction(axis)]\mbox{}\\ +A boolean (integer) value which indicates in which direction each axis +should be plotted. If it is non-zero (the default), the axis should be +plotted in the conventional direction---\emph{i.e.}\ increasing to the +right for the abscissa and increasing upwards for the ordinate. If it +is zero, the axis should be plotted in reverse. This attribute is +provided as a hint only and programs are free to ignore it if they +wish. + +\item[Domain]\mbox{}\\ +A character string which identifies the \emph{physical domain} to +which the Frame's coordinate system applies. The primary purpose of +this attribute is to prevent unwanted conversions from occurring +between coordinate systems which are not related. Its use is described +in more detail in \secref{ss:framedomains}. + +\item[System]\mbox{}\\ +A character string which identifies the specific coordinate system used +to describe positions within the physical domain represented by the Frame. +For a simple Frame, this attribute currently has a fixed value of +``Cartesian'', but could in principle be extended to include options such +as ``Polar'', ``Cylindrical'', \emph{etc}. More specialised Frames such +as the SkyFrame, TimeFrame and SpecFrame, re-define the allowed values to be +appropriate to the domain which they describe. For instance, the SkyFrame +allows values such as ``FK4'' and ``Galactic'', and the SpecFrame allows +values such as ``frequency'' and ``wavelength''. + +\item[Epoch]\mbox{}\\ +This value is used to qualify a coordinate system by giving the moment in +time when the coordinates are correct. Usually, this will be the date of +observation. The Epoch value is important in cases where coordinates +systems move with respect to each other over time. An example of two such +coordinate systems are the FK4 and FK5 celestial coordinate systems. + +\item[ObsLon]\mbox{}\\ +Specifies the longitude of the observer (assumed to be on the surface of +the earth). The basic Frame class does not use this value, but +specialised sub-classes may. For instance, the SpecFrame class uses it to +calculate the relative velocity of the observer and the centre of the +earth for use in converting between standards of rest. + +\item[ObsLat]\mbox{}\\ +Specifies the latitude of the observer. Use in conjunction with ObsLon. + +\end{description} +\end{quote} + +There are also some further Frame attributes, not described above, +which are important when Frames are used as templates to search for +other Frames. Their use goes beyond the present discussion. +%TBW---Add reference here. + +\subsection{\label{ss:formattingaxisvalues}Formatting Axis Values} + +c+ +The coordinate values associated with each axis of a Frame are stored +(\emph{e.g.}\ within your program) as double values. The Frame class +therefore provides a function, astFormat, to convert these values into +formatted strings for display: +c- +f+ +The coordinate values associated with each axis of a Frame are stored +(\emph{e.g.}\ within your program) as double precision values. The +Frame class therefore provides a function, AST\_FORMAT, to convert +these values into formatted strings for display: +f- + +c+ +\small +\begin{terminalv} +const char *string +double value; + +... + +string = astFormat( frame, iaxis, value ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 50 ) STRING + DOUBLE PRECISION VALUE + + ... + + STRING = AST_FORMAT( FRAME, IAXIS, VALUE, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, the astFormat function is passed a Frame pointer, the number of +an axis (``iaxis'') and a double precision value to format +(``value''). It returns a pointer to character string containing the +formatted value. +c- +f+ +Here, the AST\_FORMAT character function is passed a Frame pointer, +the number of an axis (IAXIS) and a double precision value to format +(VALUE). It returns a character string containing the formatted value. +f- +\label{ss:formattingwithdigits} + +c+ +By default, the formatting applied will be determined by the Frame's +Digits attribute and will normally display results with seven digits +of precision (corresponding approximately to the C ``float'' data type +on many machines). Setting a different Digits value, however, allows +you to adjust the precision as necessary to suit the accuracy of the +coordinate data you are processing. If finer control is needed, it is +also possible to set a Digits value for each individual axis by +appending an axis number to the attribute name +(\emph{e.g.}\ ``Digits(2)''). If this is done, it over-rides the +effect of the Frame's main Digits value for that axis. +c- +f+ +By default, the formatting applied will be determined by the Frame's +Digits attribute and will normally display results with seven digits +of precision (corresponding approximately to the Fortran REAL data +type on many machines). Setting a different Digits value, however, +allows you to adjust the precision as necessary to suit the accuracy +of the coordinate data you are processing. If finer control is +needed, it is also possible to set a Digits value for each individual +axis by appending an axis number to the attribute name +(\emph{e.g.}\ ``Digits(2)''). If this is done, it over-rides the +effect of the Frame's main Digits value for that axis. +f- + +c+ +Even finer control is possible by setting the (character string) Format +attribute for a Frame axis. The string given should contain a C +\emph{format specifier} which explicitly determines how the values on +that axis should be formatted. This will over-ride the effects of any +Digits value\footnote{The exception to this rule is that if the Format +value includes a precision of ``$.*$'', then Digits will be used to +determine the actual precision used.}. Any valid ``printf'' format +specifier may be used so long as it consumes exactly one double value. +c- +f+ +Even finer control is possible by setting the (character string) +Format attribute for a Frame axis. The string given should contain a +\emph{format specifier} which explicitly determines how the values on +that axis should be formatted. This will over-ride the effects of any +Digits value\footnote{The exception to this rule is that if the Format value +includes a precision of ``$.*$'', then Digits will be used to determine +the actual precision used.}. Unfortunately for Fortran programmers, this must +be a C language format specifier,\footnote{This is a consequence of +implementing the AST library in C.} so you might find the Digits +approach preferable. +f- + +c+ +When setting Format values, remember that the ``\%'' which appears in +the format specifier may need to be doubled to ``\%\%'' if you are +using a function (such as astSet) which interprets ``printf'' format +specifiers itself. +c- +f+ +The simplest type of format specifier takes the form ``\%m.nG'', where +``m'' and ``n'' are integers giving the minimum field width in characters +and the number of significant digits to display (\emph{e.g.}\ +``\%10.5G''). The ''n'' value may be replaced by an asterisk, in which +case the value of the Digits attribute is used to determine the number of +significant digits to display. Other formatting options are also possible +and if you need to use them you may wish to consult a book on C (see the +``printf'' function), remembering that you want to format a double +precision (C double) value. +f- + +c+ +It is recommended that you use astFormat whenever you display +formatted coordinate values, even although you could format them +yourself using ``sprintf''. This is because it puts the Frame in +control of formatting. When you start to handle more elaborate Frames +(representing, say, celestial coordinates), you will need different +formatting methods. This approach delivers them without any change to +your software. +c- +f+ +It is recommended that you use AST\_FORMAT whenever you display +formatted coordinate values, even although you could format them +yourself using a WRITE statement. This is because it puts the Frame in +control of formatting. When you start to handle more elaborate Frames +(representing, say, celestial coordinates), you will need different +formatting methods. This approach delivers them without any change to +your software. +f- + +c+ +You should also consider regularly using the astNorm function, +described below (\secref{ss:normalising}), for any values that will be +made visible to the user of your software. +c- +f+ +You should also consider regularly using the AST\_NORM routine, +described below (\secref{ss:normalising}), for any values that will be +made visible to the user of your software. +f- + +\subsection{\label{ss:normalising}Normalising Frame Coordinates} + +c+ +The function astNorm is provided to cope with the fact that some +coordinate systems do not extend indefinitely in all directions. Some +may have boundaries, outside which coordinates are meaningless, while +others wrap around on themselves, so that after a certain distance you +return to the beginning again (coordinate systems based on circles and +spheres, for instance). A basic Frame has no such complications, but +other more specialised Frames (such as SkyFrames, representing the +celestial sphere---\secref{ss:skyframes}) do. +c- +f+ +The routine AST\_NORM is provided to cope with the fact that some +coordinate systems do not extend indefinitely in all directions. Some +may have boundaries, outside which coordinates are meaningless, while +others wrap around on themselves, so that after a certain distance you +return to the beginning again (coordinate systems based on circles and +spheres, for instance). A basic Frame has no such complications, but +other more specialised Frames (such as SkyFrames, representing the +celestial sphere---\secref{ss:skyframes}) do. +f- + +c+ +The role played by astNorm is to \emph{normalise} any arbitrary set of +coordinates by converting them into a set which is ``within bounds'', +interpreted according to the particular Frame in question. For +example, on the celestial sphere, a right ascension value of 24~hours +or more can have a suitable multiple of 24~hours subtracted without +affecting its meaning and astNorm would perform this task. Similarly, +negative values of right ascension would have a multiple of 24~hours +added, so that the result lies in the range zero to 24~hours. The +coordinates in question are modified in place by astNorm, as follows: +c- +f+ +The role played by AST\_NORM is to \emph{normalise} any arbitrary set +of coordinates by converting them into a set which is ``within +bounds'', interpreted according to the particular Frame in +question. For example, on the celestial sphere, a right ascension +value of 24~hours or more can have a suitable multiple of 24~hours +subtracted without affecting its meaning and AST\_NORM would perform +this task. Similarly, negative values of right ascension would have a +multiple of 24~hours added, so that the result lies in the range zero +to 24~hours. The coordinates in question are modified in place by +AST\_NORM, as follows: +f- + +c+ +\small +\begin{terminalv} +double point[ 2 ]; + +... + +astNorm( frame, point ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION POINT( 2 ) + + ... + + CALL AST_NORM( FRAME, POINT, STATUS ) +\end{terminalv} +\normalsize +f- + +If the coordinates supplied are initially OK, as they would always be +with a basic Frame, then they are returned unchanged. + +c+ +Because the main purpose of astNorm is to convert coordinates into the +preferred range for human consumption, its use is almost always +appropriate immediately before formatting coordinate values for +display using astFormat (\secref{ss:formattingaxisvalues}). Even if +the Frame in question does not restrict the range of coordinates, so +that astNorm does nothing, using it will allow you to process other +more specialised Frames, where normalisation is important, without +changing your software. +c- +f+ +Because the main purpose of AST\_NORM is to convert coordinates into +the preferred range for human consumption, its use is almost always +appropriate immediately before formatting coordinate values for +display using AST\_FORMAT (\secref{ss:formattingaxisvalues}). Even if +the Frame in question does not restrict the range of coordinates, so +that AST\_NORM does nothing, using it will allow you to process other +more specialised Frames, where normalisation is important, without +changing your software. +f- + +\subsection{\label{ss:unformattingaxisvalues}Reading Formatted Axis Values} + +c+ +The process of converting a formatted coordinate value for a Frame +axis, such as might be produced by astFormat +(\secref{ss:formattingaxisvalues}), back into a numerical (double) +value ready for processing is performed by astUnformat. However, +although this process is essentially the inverse of that performed by +astFormat, there are a number of additional difficulties that must be +addressed in practice. +c- +f+ +The process of converting a formatted coordinate value for a Frame +axis, such as might be produced by AST\_FORMAT +(\secref{ss:formattingaxisvalues}), back into a numerical (double +precision) value ready for processing is performed by AST\_UNFORMAT. +However, although this process is essentially the inverse of that +performed by AST\_FORMAT, there are a number of additional difficulties +that must be addressed in practice. +f- + +c+ +The main use for astUnformat is in reading formatted coordinate values +which have been entered by the user of a program, or read from a +file. As such, we can rarely assume that the values are neatly +formatted in the way that astFormat would produce. Instead, it is +usually desirable to allow considerable flexibility in the form of +input that can be accommodated, so as to permit ``free-format'' data +input by the user. In addition, we may need to extract individual +coordinate values embedded in other textual data. +c- +f+ +The main use for AST\_UNFORMAT is in reading formatted coordinate +values which have been entered by the user of a program, or read from +a file. As such, we can rarely assume that the values are neatly +formatted in the way that AST\_FORMAT would produce. Instead, it is +usually desirable to allow considerable flexibility in the form of +input that can be accommodated, so as to permit ``free-format'' data +input by the user. In addition, we may need to extract individual +coordinate values embedded in other textual data. +f- + +c+ +Underlying these requirements is the root difficulty that the textual +format used to represent a coordinate value will depend on the class +of Frame we are considering. For example, for a basic Frame, +astUnformat may have to read a value like ``1.25e-6'', whereas for a +more specialised Frame representing celestial coordinates it may have +to handle a value like ``-07d~49m~13s''. Of course, the format might +also depend on which axis is being considered. +c- +f+ +Underlying these requirements is the root difficulty that the textual +format used to represent a coordinate value will depend on the class +of Frame we are considering. For example, for a basic Frame, +AST\_UNFORMAT may have to read a value like ``1.25E-6'', whereas a +more specialised Frame representing celestial coordinates may have to +handle a value like ``-07d~49m~13s''. Of course, the format might also +depend on which axis is being considered. +f- + +Ideally, we would like to write software that can handle any kind of +Frame. However, this makes it a little more difficult to analyse +textual input data to extract individual coordinate values, since we +cannot make assumptions about how the values are formatted. It would +not be safe, for example, simply to assume that the values being read +are separated by white space. This is not just because they might be +separated by some other character, but also because celestial +coordinate values might themselves contain spaces. In fact, to be +completely safe, we cannot make any assumptions about how a formatted +coordinate value is separated from the surrounding text, except that +it should be separated in some way which is not ambiguous. + +c+ +This is the very basic assumption upon which astUnformat works. It is +invoked as follows: +c- +f+ +This is the very basic assumption upon which AST\_UNFORMAT works. It is +invoked as follows: +f- + +c+ +\small +\begin{terminalv} +int n; + +... + +n = astUnformat( frame, iaxis, string, &value ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER N + + ... + + N = AST_UNFORMAT( FRAME, IAXIS, STRING, VALUE, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +It is supplied with a Frame pointer (``frame''), the number of an axis +(``iaxis'') and a character string to be read (``string''). If it +succeeds in reading a value, astUnformat returns the resulting +coordinate to the address supplied \emph{via} the final argument +(``\&value''). The returned function value indicates how many +characters were read from the string in order to obtain this result. +c- +f+ +It is supplied with a Frame pointer (FRAME), the number of an axis +(IAXIS) and a character string to be read (STRING). If it succeeds in +reading a value, AST\_UNFORMAT returns the resulting coordinate +\emph{via} its penultimate argument (VALUE). The returned function +value indicates how many characters were read from the string in order +to obtain this result. +f- + +The string is read as follows: + +\begin{enumerate} +\item Any white space at the start is skipped over. + +\item Further characters are considered, one at a time, until the next +character no longer matches any of the acceptable forms of input +(given the characters that precede it). The longest sequence of +characters which matches is then considered ``read''. + +\item If a suitable sequence of characters was read successfully, it +is converted into a coordinate value which is returned. Any white +space following this sequence is then skipped over and the total +number of characters consumed is returned as the function value. + +c+ +\item If the sequence of characters read is empty, or insufficient to +define a coordinate value, then the string does not contain a value to +read. In this case, the read is aborted and astUnformat returns a +function value of zero and no coordinate value. However, it returns +without error. +c- +f+ +\item If the sequence of characters read is empty, or insufficient to +define a coordinate value, then the string does not contain a value to +read. In this case, the read is aborted and AST\_UNFORMAT returns a +function value of zero and no coordinate value. However, it returns +without error. +f- +\end{enumerate} + +c+ +Note that failing to read a coordinate value does not constitute an +error, at least so far as astUnformat is concerned. However, an error +can occur if the sequence of characters read appears to have the +correct form but cannot be converted into a valid coordinate +value. Typically, this will be because it violates some constraint, +such as a limit on the value of one of its fields. The resulting error +message will give details. +c- +f+ +Note that failing to read a coordinate value does not constitute an +error, at least so far as AST\_UNFORMAT is concerned. However, an +error can occur if the sequence of characters read appears to have the +correct form but cannot be converted into a valid coordinate +value. Typically, this will be because it violates some constraint, +such as a limit on the value of one of its fields. The resulting error +message will give details. +f- + +c+ +For any given Frame axis, astUnformat does not necessarily always use +the same algorithm for converting the sequence of characters it reads +into a coordinate value. This is because some forms of input +(particularly free-format input) can be ambiguous and might be +interpreted in several ways depending on the context. For example, the +celestial longitude ``12:34:56.7'' could represent an angle in degrees +or a right ascension in hours. To decide which to use, astUnformat may +examine the Frame's attributes and, in particular, the appropriate +Format(axis) string which is used by astFormat when formatting +coordinate values (\secref{ss:formattingaxisvalues}). This is done in +order that astFormat and astUnformat should complement each other---so +that formatting a value and then un-formatting it will yield the +original value, subject to any rounding error. +c- +f+ +For any given Frame axis, AST\_UNFORMAT does not necessarily always +use the same algorithm for converting the sequence of characters it +reads into a coordinate value. This is because some forms of input +(particularly free-format input) can be ambiguous and might be +interpreted in several ways depending on the context. For example, the +celestial longitude ``12:34:56.7'' could represent an angle in degrees +or a right ascension in hours. To decide which to use, AST\_UNFORMAT +may examine the Frame's attributes and, in particular, the appropriate +Format(axis) string which is used by AST\_FORMAT when formatting +coordinate values (\secref{ss:formattingaxisvalues}). This is done in +order that AST\_FORMAT and AST\_UNFORMAT should complement each +other---so that formatting a value and then un-formatting it will +yield the original value, subject to any rounding error. +f- + +To give a simple (but crucially incomplete!) example, consider reading +a value for the axis of a basic Frame, as follows: + +c+ +\small +\begin{terminalv} +n = astUnformat( frame, iaxis, " 1.5e6 -99.0", &value ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + N = AST_UNFORMAT( FRAME, IAXIS, ' 1.5E6 -99.0', VALUE, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +astUnformat will skip over the initial space in the string supplied +and then examine each successive character. It will accept the +sequence ``1.5e6'' as input, but reject the space which follows +because it does not form part of the format of a floating point +number. It will then convert the characters ``1.5e6'' into a +coordinate value and skip over the three spaces which follow them. The +returned function value will therefore be 9, equal to the total number +of characters consumed. This result may be used to address the string +during a subsequent read, so as to commence reading at the start of +``-99.0''. +c- +f+ +AST\_UNFORMAT will skip over the initial space in the string supplied +and then examine each successive character. It will accept the +sequence ``1.5E6'' as input, but reject the space which follows +because it does not form part of the format of a floating point +number. It will then convert the characters ``1.5E6'' into a +coordinate value and skip over the three spaces which follow them. The +returned function value will therefore be 9, equal to the total number +of characters consumed. This result may be used to address the string +during a subsequent read, so as to commence reading at the start of +``-99.0''. +f- + +c+ +Most importantly, however, note that if the user of a program +mistakenly enters the string ``~1.5r6\ldots'' instead of +``~1.5e6\ldots'', a coordinate value of 1.5 and a function result of 4 +will be returned, because the ``r'' would prematurely terminate the +attempt to read the value. Because this sort of mistake does not +automatically result in an error but can produce incorrect results, it +is \textbf{vital} to check the returned function value to ensure that +the expected number of characters have been read.\footnote{Anyone who +seriously uses the C run time library ``scanf'' function will know +about the need for this check!} For example, if the string is +expected to contain exactly one value, and nothing else, then the +following would suffice: +c- +f+ +Most importantly, however, note that if the user of a program +mistakenly enters the string ``~1.5R6\ldots'' instead of +``~1.5E6\ldots'', a coordinate value of 1.5 and a function result of 4 +will be returned, because the ``R'' would prematurely terminate the +attempt to read the value. Because this sort of mistake does not +automatically result in an error but can produce incorrect results, it +is \textbf{vital} to check the returned function value to ensure that +the expected number of characters have been read. For example, if the +string is expected to contain exactly one value, and nothing else, +then the following would suffice: +f- + +c+ +\small +\begin{terminalv} +n = astUnformat( frame, iaxis, string, &value ); +if ( astOK ) { + if ( string[ n ] || !n ) { + + } else { + + } +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + N = AST_UNFORMAT( FRAME, IAXIS, STRING, VALUE, STATUS ) + IF ( STATUS .EQ. 0 ) THEN + IF ( N .LT. LEN( STRING ) ) THEN + + ELSE + + END IF + END IF +\end{terminalv} +\normalsize +f- + +c+ +If astUnformat does not detect an error itself, we check that it has +read to the end-of-string and consumed at least one character (which +traps the case of a zero-length input string). If this reveals an +error, the value of ``n'' indicates where it occurred. +c- +f+ +If AST\_UNFORMAT does not detect an error itself, we check that it has +read to the end of the string. If this reveals an error, the value of +N indicates where it occurred. +f- + +Another common requirement is to obtain a position by reading a list +of coordinates from a string which contains one value for each axis of +a Frame. We assume that the values are separated in some unambiguous +manner, perhaps using white space and/or some unspecified +single-character separator. The choice of separator is up to the data +supplier, who must choose it so as not to conflict with the format of +the coordinate values, but our software does not need to know what it +is. The following is a template algorithm for reading data in this +form: + +c+ +\small +\begin{terminalv} +const char *s; +double values[ 10 ]; + +... + +/* Initialise a string pointer. */ +s = string; + +/* Obtain the number of Frame axes and loop through them. */ +naxes = astGetI( frame, "Naxes" ); +for ( iaxis = 1; iaxis <= naxes; iaxis++ ) { + +/* Attempt to read a value for this axis. */ + n = astUnformat( frame, iaxis, s, &values[ iaxis - 1 ] ); + +/* If nothing was read and this is not the first axis or the + end-of-string, try stepping over a separator and reading again. */ + if ( !n && ( iaxis > 1 ) && *s ) + n = astUnformat( frame, iaxis, ++s, &values[ iaxis - 1 ] ); + +/* Quit if nothing was read, otherwise move on to the next value. */ + if ( !n ) break; + s += n; +} + +/* Check for possible errors. */ +if ( astOK ) { + if ( *s || !n ) { + + } else { + + } +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER I + DOUBLE PRECISION VALUES( 10 ) + + ... + +* Initialise the string index. + I = 1 + +* Obtain the number of Frame axes and loop through them. + DO 1 IAXIS = 1, AST_GETI( FRAME, 'Naxes', STATUS ) + +* Attempt to read a value for this axis. + N = AST_UNFORMAT( FRAME, IAXIS, STRING( I : ), + : VALUES( IAXIS ), STATUS ) + +* If nothing was read and this is not the first axis and the end of +* the string has not been reached, try stepping over a separator and +* reading again. + IF ( ( N .EQ. 0 ) .AND. ( IAXIS .GT. 1 ) .AND. + : ( I .LT. LEN( STRING ) ) ) THEN + I = I + 1 + N = AST_UNFORMAT( FRAME, IAXIS, STRING( I : ), + : VALUES( IAXIS ), STATUS ) + END IF + +* Quit if nothing was read, otherwise move on to the next value. + IF ( N .EQ. 0 ) GO TO 2 + I = I + N + 1 CONTINUE + 2 CONTINUE + +* Check for possible errors. + IF ( STATUS .EQ. 0 ) THEN + IF ( ( I .LT. LEN( STRING ) ) .OR. ( N .EQ. 0 ) ) THEN + + ELSE + + END IF + END IF +\end{terminalv} +\normalsize +f- + +c+ +In this case, ``s'' will point to the location of any input error. +c- +f+ +In this case, the value of I will indicate the location of any input error. +f- + +Note that this algorithm is insensitive to the precise format of the +data and will therefore work with any class of Frame and any +reasonably unambiguous input data. For example, here is a range of +suitable input data for a 3-dimensional basic Frame: + +\small +\begin{terminalv} +1 2.5 3 +3.1,3.2,3.3 +1.5, 2.6, -9.9e2 +-1.1+0.4-1.8 + .1/.2/.3 + 44.0 ; 55.1 -14 +\end{terminalv} +\normalsize + +\subsection{\label{ss:permutingaxes}Permuting Frame Axes} + +Once a Frame has been created, it is not possible to change the number +of axes it contains, but it is possible to change the order in which +these axes occur. To do so, an integer \emph{permutation array} is +filled with the numbers of the axes so as to specify the new order, +\emph{e.g.:} + +c+ +\small +\begin{terminalv} +int perm[ 2 ] = { 2, 1 }; +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER PERM( 2 ) + DATA PERM / 2, 1 / +\end{terminalv} +\normalsize +f- + +c+ +In this case, the axes of a 2-dimensional Frame could be interchanged +by passing this permutation array to the astPermAxes function. That +is, an ($x_1,x_2$) coordinate system would be changed into an +($x_2,x_1$) coordinate system by: +c- +f+ +In this case, the axes of a 2-dimensional Frame could be interchanged +by passing this permutation array to the AST\_PERMAXES function. That +is, an ($x_1,x_2$) coordinate system would be changed into an +($x_2,x_1$) coordinate system by: +f- + +c+ +\small +\begin{terminalv} +astPermAxes( frame, perm ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_PERMAXES( FRAME, PERM, STATUS ) +\end{terminalv} +\normalsize +f- + +If the axes are permuted more than once, the effects are cumulative. +You are, of course, not restricted to Frames with only two axes. + +\subsection{Selecting Frame Axes} + +c+ +An alternative to changing the number of Frame axes, which is not +allowed, is to create a new Frame by selecting axes from an existing +one. The method of doing this is very similar to the way astPermAxes +is used (\secref{ss:permutingaxes}), in that we supply an integer +array filled with the numbers of the axes we want, in their new +order. In this case, however, the number of array elements need not +equal the number of Frame axes. +c- +f+ +An alternative to changing the number of Frame axes, which is not +allowed, is to create a new Frame by selecting axes from an existing +one. The method of doing this is very similar to the way AST\_PERMAXES +is used (\secref{ss:permutingaxes}), in that we supply an integer +array filled with the numbers of the axes we want, in their new +order. In this case, however, the number of array elements need not +equal the number of Frame axes. +f- + +For example, we could select axes 3 and 2 (in that order) from a +3-dimensional Frame as follows: + +c+ +\small +\begin{terminalv} +astFrame *frame1, *frame2; +astMapping *mapping; +int pick[ 2 ] = { 3, 2 }; + +... + +frame2 = astPickAxes( frame1, 2, pick, &mapping ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAME1, FRAME2, MAPPING, PICK( 2 ) + DATA PICK / 3, 2 / + + ... + + FRAME2 = AST_PICKAXES( FRAME1, 2, PICK, MAPPING, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +This would return a pointer to a 2-dimensional Frame (``frame2'') +which contains the information associated with axes 3 and 2, in that +order, from the original Frame (``frame1''). The original Frame is not +altered by this process. Beware, however, that the axis information +may still be shared by both Frames, so if you wish to alter either of +them independently you may first need to use astCopy +(\secref{ss:copyingobjects}) to make an independent copy. +c- +f+ +This would return a pointer to a 2-dimensional Frame (FRAME2) which +contains the information associated with axes 3 and 2, in that order, +from the original Frame (FRAME1). The original Frame is not altered by +this process. Beware, however, that the axis information may still be +shared by both Frames, so if you wish to alter either of them +independently you may first need to use AST\_COPY +(\secref{ss:copyingobjects}) to make an independent copy. +f- + +c+ +In addition to the new Frame pointer, astPickAxes will also return a +pointer to a new Mapping \emph{via} its fourth argument (you may supply a +NULL pointer as an argument if you do not want this Mapping). This +Mapping will inter-relate the two Frames. By this we mean that its +forward transformation will convert coordinates originally in the +coordinate system represented by ``frame1'' into that represented by +``frame2'', while its inverse transformation will convert in the +opposite direction. In this particular case, the Mapping would be a +PermMap (\secref{ss:permmapexample}) and would implement the following +transformations: +c- +f+ +In addition to the new Frame pointer, AST\_PICKAXES will also return a +pointer to a new Mapping \emph{via} its fourth argument. This Mapping will +inter-relate the two Frames. By this we mean that its forward +transformation will convert coordinates originally in the coordinate +system represented by FRAME1 into that represented by FRAME2, while +its inverse transformation will convert in the opposite direction. In +this particular case, the Mapping would be a PermMap +(\secref{ss:permmapexample}) and would implement the following +transformations: +f- + +\begin{terminalv} +Forward: + (1, 2, 3) --> (3, 2) + (2, 4, 6) --> (6, 4) + (3, 6, 9) --> (9, 6) + (4, 8, 12) --> (12, 8) + (5, 10, 15) --> (15, 10) + +Inverse: + (3, 2) --> (, 2, 3) + (6, 4) --> (, 4, 6) + (9, 6) --> (, 6, 9) + (12, 8) --> (, 8, 12) + (15, 10) --> (, 10, 15) +\end{terminalv} + +This is our first introduction to the idea of inter-relating pairs of +Frames \emph{via} a Mapping, but this will assume a central role later on. + +c+ +Note that when using astPickAxes, it is also possible to request more +axes than there were in the original Frame. This will involve +selecting axes from the original Frame that do not exist. To do this, +the corresponding axis number (in the ``pick'' array) should be set to +zero and the effect is to introduce an additional new axis which is +not derived from the original Frame. This axis will have default +values for all its attributes. You will need to do this because +astPickAxes does not allow you to select any of the original axes more +than once.\footnote{It will probably not be obvious why this +restriction is necessary, but consider creating a Frame with one +longitude axis and two latitude axes. Which latitude axis should be +associated with the longitude axis?} +c- +f+ +Note that when using AST\_PICKAXES, it is also possible to request +more axes than there were in the original Frame. This will involve +selecting axes from the original Frame that do not exist. To do this, +the corresponding axis number (in the PICK array) should be set to +zero and the effect is to introduce an additional new axis which is +not derived from the original Frame. This axis will have default +values for all its attributes. You will need to do this because +AST\_PICKAXES does not allow you to select any of the original axes +more than once.\footnote{It will probably not be obvious why this +restriction is necessary, but consider creating a Frame with one +longitude axis and two latitude axes. Which latitude axis should be +associated with the longitude axis?} +f- + +\subsection{\label{ss:distanceandoffset}Calculating Distances, Angles and Offsets} +Some complementary +c+ +functions +c- +f+ +routines +f- +are provided for use with Frames to allow you to perform geometric +operations without needing to know the nature of the coordinate system +represented by the Frame. + +c+ +Functions +c- +f+ +Routines +f- +can be used to find the distance between two points, and to offset a +specified distance along a line joining two points, \emph{etc.} In essence, +these define the metric of the coordinate space which the Frame represents. In +the case of a basic Frame, this is a Cartesian metric. + +c+ +The first of these functions, astDistance, returns a double distance +value when supplied with the Frame coordinates of two points. For +example: +c- +f+ +The first of these routines, AST\_DISTANCE, returns a double precision +distance value when supplied with the Frame coordinates of two +points. For example: +f- + +c+ +\small +\begin{terminalv} +double dist; +double point1[ 2 ] = { 0.0, 0.0 }; +double point2[ 2 ] = { 1.0, 1.0 }; + +... + +dist = astDistance( frame, point1, point2 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION DIST, POINT1( 2 ), POINT2( 2 ) + DATA POINT1 / 0D0, 0D0 / + DATA POINT2 / 1D0, 1D0 / + + ... + + DIST = AST_DISTANCE( FRAME, POINT1, POINT2, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +This calculates the distance between the origin (0,0) and a point at +position (1,1). In this case, the result, as you would expect, is +$\surd{2}$. However, this is only true for the Cartesian coordinate +system which a basic Frame represents. In general, astDistance will +calculate the geodesic distance between the two points, so that with a +more specialised Frame (such as a SkyFrame, representing the celestial +sphere) a great-circle distance might be returned. +c- +f+ +This calculates the distance between the origin (0,0) and a point at +position (1,1). In this case, the result, as you would expect, is +$\surd{2}$. However, this is only true for the Cartesian coordinate +system which a basic Frame represents. In general, AST\_DISTANCE will +calculate the geodesic distance between the two points, so that with a +more specialised Frame (such as a SkyFrame, representing the celestial +sphere) a great-circle distance might be returned. +f- + +c+ +The astOffset function is really the inverse of astDistance. Given two +points in a Frame, it calculates the coordinates of a third point +which is offset a specified distance away from the first point along +the geodesic joining it to the second one. For example: +c- +f+ +The AST\_OFFSET routine is really the inverse of AST\_DISTANCE. Given +two points in a Frame, it calculates the coordinates of a third point +which is offset a specified distance away from the first point along +the geodesic joining it to the second one. For example: +f- + +c+ +\small +\begin{terminalv} +double point1[ 2 ] = { 0.0, 0.0 }; +double point2[ 2 ] = { 1.0, 1.0 }; +double point3[ 2 ]; + +... + +astOffset( frame, point1. point2, 0.5, point3 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION POINT1( 2 ), POINT2( 2 ), POINT3( 2 ) + DATA POINT1 / 0D0, 0D0 / + DATA POINT2 / 1D0, 1D0 / + + ... + + CALL AST_OFFSET( FRAME, POINT1, POINT2, 0.5D0, POINT3, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +This would fill the ``point3'' array with the coordinates of a point +which is offset 0.5 units away from the origin (0,0) in the direction +of the position (1,1). Again, this is a simple result in a Cartesian +Frame, as varying the offset will trace out a straight line. On the +celestial sphere, however (\emph{e.g.}\ using a SkyFrame), it would +trace out a great circle. +c- +f+ +This would fill the POINT3 array with the coordinates of a point which +is offset 0.5 units away from the origin (0,0) in the direction of the +position (1,1). Again, this is a simple result in a Cartesian Frame, +as varying the offset will trace out a straight line. On the celestial +sphere, however (\emph{e.g.}\ using a SkyFrame), it would trace out a +great circle. +f- + +c+ +The functions astAxDistance and astAxOffset are similar to astDistance +and astOffset, except that the curves which they use as ``straight +lines'' are not geodesics, but curves parallel to a specified axis\footnote +{For instance, a line of constant Declination is not a geodesic}. One +reason for using these functions is to deal with the cyclic ambiguity of +longitude and latitude axes. +c- +f+ +The routines AST\_AXDISTANCE and AST\_AXOFFSET are similar to AST\_DISTANCE +and AST\_OFFSET, except that the curves which they use as ``straight +lines'' are not geodesics, but curves parallel to a specified axis\footnote +{For instance, a line of constant Declination is not a geodesic}. One +reason for using these routines is to deal with the cyclic ambiguity of +longitude and latitude axes. +f- + +c+ +The astOffset2 function is similar to astOffset, but instead of using the +c- +f+ +The AST\_OFFSET2 routine is similar to AST\_OFFSET, but instead of using the +f- +geodesic which passes through two positions, it uses the geodesic which +passes at a given position angle through the starting position. + +Position angles are always measured from the positive direction of the +second Frame axis to the required line, with positive angles being in the +same sense as rotation from the positive direction of the second axis to +the positive direction of the first Frame axis. This definition applies +to all classes of Frame, including SkyFrame. The default ordering of axes +in a SkyFrame makes the second axis equivalent to north, and so the +definition of position angle given above corresponds to the normal +astronomical usage, ``from north, through east''. However, it should be +remembered that it is possible to permute the axes of a SkyFrame (or +indeed any Frame), so that north becomes axis 1. In this case, an AST +``position angle'' would be the angle ``from east, through north''. +Always take the axis ordering into account when deriving an astronomical +position angle from an AST position angle. + +Within a Cartesian coordinate system, the position angle of a geodesic +(\emph{i.e.}\ a straight line) is constant along its entire length, but +this is not necessarily true of other coordinate systems. Within a +spherical coordinate system, for instance, the position angle of a geodesic +will vary along its length (except for the special cases of a meridian and +the equator). In addition to returning the required offset position, the +c+ +astOffset2 function +c- +f+ +AST\_OFFSET2 routine +f- +returns the position angle of the geodesic at the +offset position. This is useful if you want to trace out a path which +involves turning through specified angles. For instance, tracing out a +rectangle in which each side is a geodesic involves turning through 90 +c+ +degrees at the corners. To do this, use astOffset2 to calculate the +position of each corner, and then add (or subtract) 90 degrees from the +position angle returned by astOffset2. +c- +f+ +degrees at the corners. To do this, use AST\_OFFSET2 to calculate the +position of each corner, and then add (or subtract) 90 degrees from the +position angle returned by AST\_OFFSET2. +f- + +c+ +The astAngle function +c- +f+ +The AST\_ANGLE routine +f- +calculates the angle subtended by two points, at a third point. +If used with a 2-dimensional Frame the returned angle +is signed to indicate the sense of rotation (clockwise or anti-clockwise) +in taking the ``shortest route'' from the first point to the second. +If the Frame has more than 2 axes, the result is un-signed and is always +in the range zero to $\pi$. + +c+ +The astAxAngle function is similar to astAngle, +c- +f+ +The AST\_AXANGLE routine is similar to AST\_AXANGLE, +f- +but the ``reference direction'', from which angles are measured, is +a specified axis. + +c+ +The astResolve function +c- +f+ +The AST\_RESOLVE routine +f- +resolves a given displacement within a Frame into two components, parallel and +perpendicular to a given reference direction. + +The displacement is specified by two positions within the Frame; the +starting and ending positions. The reference direction is defined by the +geodesic curve passing through the starting position and a third specified +position. The lengths of the two components are returned, together with +the position on the reference geodesic which is closest to the third +supplied point. + +\subsection{\label{ss:framedomains}The Domain Attribute} + +The Domain attribute is one of the most important properties of a +Frame, although the concept it expresses can sometimes seem a little +subtle. We will introduce it here, but its true value will probably +not become apparent until later (\secref{ss:framesetconverting}). + +To understand the need for the Domain attribute, consider using +different Frames to represent the following different coordinate +systems associated with a CCD image: + +\begin{enumerate} +\item A coordinate system based on pixel numbers. + +\item Positions on the CCD chip, measured in $\mu$m. + +\item Positions in the focal plane of the telescope, measured in mm. + +\item A celestial coordinate system, measured in radians. +\end{enumerate} + +If we had two such CCD images, we might legitimately want to align +them pixel-for-pixel (\emph{i.e.}\ using the coordinate system based +on pixel numbers) in order to, say, divide by a flat-field exposure. +We might similarly consider aligning them using any of the other +coordinate systems so as to achieve different results. For example, we +might consider merging separate images from a CCD mosaic by using +focal plane positions. + +It would obviously not be legitimate, however, to directly compare +positions in one image measured in pixels with positions in the other +measured in mm, nor to equate chip positions in $\mu$m with sky +coordinates in radians. If we wanted to inter-compare these +coordinates, we would need to do it indirectly, using other +information based on the experimental set-up. For instance, we might +need to know the size of the pixels expressed in mm and the +orientation of the CCD chip in the focal plane. + +Note that it is not simply the difference in physical units which +prevents certain coordinates from being directly inter-compared +(because the appropriate unit scaling factors could be included +without any additional information). Neither is it the fact that +different coordinate systems are in use (because we could legitimately +inter-compare two different celestial coordinate systems without any +extra information). Instead, it is the different nature of the +coordinate spaces to which these coordinate systems have been applied. + +We normally express this by saying that the coordinate systems apply +to different \emph{physical domains}. Although we may establish +\emph{ad hoc} relationships between coordinates in different physical +domains, they are not intrinsically related to each other and we need +to supply extra information before we can convert coordinates between +them. + +In AST, the role of the (character string) Domain attribute is to +assign Frames to their respective physical domains. The way it +operates is as follows: + +\begin{itemize} +\item Coordinate systems which apply to the same physical domain +(\emph{i.e.}\ whose Frames have the same Domain value) can be directly +inter-compared. + +If the domain has several coordinate systems associated with it +(\emph{e.g.}\ the celestial sphere), then a coordinate conversion may +be involved. Otherwise, coordinate values may simply be equated. + +\item Coordinate systems which apply to different physical domains +(\emph{i.e.}\ whose Frames have different Domain values) cannot be +directly inter-compared. + +If any relationship does exist between such coordinate systems---and +it need not---then additional information must be supplied in order to +establish the relationship between them in any particular case. We +will see later (\secref{ss:framesets}) how to establish such +relationships between Frames in different domains. +\end{itemize} + +With the basic Frames we are considering here, each physical domain only +has a single (Cartesian) coordinate system associated with it, so that if +two such Frames have the same Domain value, their coordinate systems will +be identical and may simply be equated. With more specialised Frames, +however, more than one coordinate system may apply to each domain. In +such cases, a coordinate conversion may need to be performed. + +c+ +When a basic Frame is created, its Domain attribute defaults to an +empty string. This means that all such Frames belong to the same +(null) domain by default and therefore describe the same unspecified +physical coordinate space. In order to assign a Frame to a different +domain, you simply need to set its Domain value. This is normally most +conveniently done when it is created, as follows: +c- +f+ +When a basic Frame is created, its Domain attribute defaults to a +blank string. This means that all such Frames belong to the same +(null) domain by default and therefore describe the same unspecified +physical coordinate space. In order to assign a Frame to a different +domain, you simply need to set its Domain value. This is normally most +conveniently done when it is created, as follows: +f- + +c+ +\small +\begin{terminalv} +frame1 = astFrame( 2, "Domain=CCD_CHIP," + "Unit(1)=micron," + "Unit(2)=micron" ); +frame2 = astFrame( 2, "Domain=FOCAL_PLANE," + "Unit(1)=mm," + "Unit(2)=mm" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + FRAME1 = AST_FRAME( 2, 'Domain=CCD_CHIP,' // + 'Unit(1)=micron,' // + 'Unit(2)=micron', STATUS ) + FRAME2 = AST_FRAME( 2, 'Domain=FOCAL_PLANE,' // + 'Unit(1)=mm,' // + 'Unit(2)=mm', STATUS ) +\end{terminalv} +\normalsize +f- + +Here, we have created two Frames in different physical +domains. Although their coordinate values all have units of length, +they cannot be directly inter-compared (because their axes may be +rotated with respect to each other, for instance). + +All Domain values are automatically converted to upper case and white +space is removed, but there are no other restrictions on the names you +may use to label different physical domains. From a practical point of +view, however, it is worth following a few conventions +(\secref{ss:domainconventions}). + +\subsection{\label{ss:domainconventions}Conventions for Domain Names} + +When choosing a value for the Domain attribute of a Frame, it +obviously makes sense to avoid generic names which might clash with +those used for similar (but subtly different!) purposes by other +programmers. If you are developing software for an instrument, for +example, and want to identify an instrumental coordinate system, then +it is sensible to add a distinguishing prefix. For instance, you might +use $<$INST$>$\_FOCAL\_PLANE, where $<$INST$>$ (\emph{e.g.}\ an +acronym) identifies your instrument. + +For some purposes, however, a standard choice of Domain name is +desirable so that different items of software can communicate. For +this purpose, the following Domain names are reserved by AST and the +use recommended below should be carefully observed: + +\begin{quote} +\begin{description} +\item[GRAPHICS]\mbox{}\\ +Identifies the coordinate space used by an underlying computer +graphics system to specify plotting operations. Typically, when +performing graphical operations, AST is used to define additional +coordinate systems which are related to these ``native'' graphical +coordinates. Plotting may be carried out in any of these coordinate +systems, but the GRAPHICS domain identifies the native coordinates +through which AST communicates with the underlying graphics system. + +\item[GRID]\mbox{}\\ +Identifies the instantaneous \emph{data grid} used to store and handle +data, together with an associated coordinate system. In this +coordinate system, the first element stored in an array of data always +has a coordinate value of unity at its centre and all elements have +unit extent. This applies to all dimensions. + +If data are copied or transformed to a new data grid (by whatever +means), or a subset of the original grid is extracted, then the same +rules apply to the copy or subset. Its first element therefore has +GRID coordinate values of unity at its centre. Note that this means +that GRID coordinates remain attached to the first element of the data +grid and not to its data content (\emph{e.g.}\ the features in an +image). + +\item[PIXEL]\mbox{}\\ +Identifies an array of pixels and an associated \emph{pixel-based} +coordinate system which is related to the GRID coordinate system +(above) simply by a shift of origin along each axis. This shift may be +integral, fractional, positive, negative or zero. The data elements +retain their unit extent along each axis. + +Because the amount of shift is unspecified, the PIXEL domain is +distinct from the GRID domain. The relationship between them contains +a degree of uncertainty, such as typically arises from the different +conventions used by different software systems. For instance, in some +software the first pixel is regarded as being centred at (1,1), while +in other software it is at (0.5,0.5). In addition, some software +packages implement a ``pixel origin'' which allows pixel coordinates +to start at an arbitrary value. + +The GRID domain (which corresponds with the pixel-numbering convention +used by FITS) is a special case of the PIXEL domain and avoids this +uncertainty. In general, additional information is required in order +to convert from one to the other. + +\item[SKY]\mbox{}\\ +Identifies the domain which contains all equivalent celestial +coordinate systems. Because these are represented in AST by SkyFrames +(\secref{ss:skyframes}), it should be no surprise that the default +Domain value for a SkyFrame is SKY. Since there is only one sky, you +probably won't need to change this very often. + +\item[SPECTRUM]\mbox{}\\ +Identifies the domain used to describe positions within an +electro-magnetic spectrum. The AST SpecFrame (\secref{ss:specframes}) +class describes positions within this domain, allowing a wide range of +different coordinate systems to be used (frequency, wavelength, +\emph{etc}). The default Domain value for a SpecFrame is SPECTRUM. + +\item[TIME]\mbox{}\\ +Identifies the domain used to describe moments in time. The AST TimeFrame +class describes positions within this domain, allowing a wide range of +different coordinate systems and timescales to be used. The default Domain +value for a TimeFrame is TIME. + +\end{description} +\end{quote} + +Although we have drawn a necessary distinction here between the GRID +and PIXEL domains, we will continue to refer in general terms to image +``pixels'' and ``pixel coordinates'' whenever this distinction is not +important. This should not be taken to imply that the GRID convention +for numbering pixels is excluded---in fact, it is usually to be +preferred (at the level of data handling being discussed in this +document) and we recommend it. + +\subsection{\label{ss:frameunits}The Unit Attribute} +Each axis of a Frame has a Unit attribute which holds the physical units used +to describe positions on the axis. The index of the axis to which the +attribute refers should normally be placed in parentheses following the +attribute name (``Unit(2)'' for instance). However, if the Frame has only +a single axis, then the axis index can be omitted. + +In versions of AST prior to version 2.0, the Unit attribute was nothing +more than a descriptive string intended purely for human readers---no +part of the AST system used the Unit string for any purpose (other than +inclusion in axis labels produced by the Plot class). In particular, no +account was taken of the Unit attribute when finding the Mapping between +two Frames. Thus if the conversion between a pair of 1-dimensional Frames +representing velocity was found (using +c+ +astConvert +c- +f+ +AST\_CONVERT +f- +) the returned Mapping would always be a UnitMap, even if the Unit +attributes of the two Frames were ``km/h'' and ``m/s''. This behaviour is +referred to below as a \emph{passive} Unit attribute. + +As of AST version 2.0, a facility exists which allows the Unit attribute +to be \emph{active}; that is, differences in the +Unit attribute may be taken into account when finding the Mapping between +two Frames. In order to minimise the risk of breaking older software, the +\emph{default} behaviour of simple Frames and SkyFrames is unchanged from +previous versions (\emph{i.e.} they have passive Unit attributes). However, +the new +c+ +functions astSetActiveUnit and astGetActiveUnit +c- +f+ +routines AST\_SETACTIVEUNIT and AST\_GETACTIVEUNIT +f- +allow this default behaviour to be changed. The SpecFrame and TimeFrame +classes \emph{always} have an active Unit attribute (attempts to change this +are ignored). + +For instance, consider the above example of two 1-dimensional Frames +describing velocity. These Frames can be created as follows: + +c+ +\small +\begin{terminalv} +AstFrame *frame1, *frame2; +frame1 = astFrame( 1, "Domain=VELOCITY,Unit=km/h" ); +frame2 = astFrame( 1, "Domain=VELOCITY,Unit=m/s" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAME1, FRAME2 + + FRAME1 = AST_FRAME( 1, 'Domain=VELOCITY,Unit=km/h' ) + FRAME2 = AST_FRAME( 1, 'Domain=VELOCITY,Unit=m/s' ) + +\end{terminalv} +\normalsize +f- + +By default, these Frames have passive Unit attributes, and so an attempt +to find a Mapping between them would ignore the difference in their Unit +attributes and return a unit Mapping. To avoid this, we indicate that we +want these Frames to have \emph{active} Unit attributes, as follows: + +c+ +\small +\begin{terminalv} +astSetActiveUnit( frame1, 1 ); +astSetActiveUnit( frame2, 1 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SETACTIVEUNIT( FRAME1, .TRUE., STATUS ) + CALL AST_SETACTIVEUNIT( FRAME2, .TRUE., STATUS ) +\end{terminalv} +\normalsize +f- + +If we then find the Mapping between them as follows: + +c+ +\small +\begin{terminalv} +AstFrameSet *cvt; +... +cvt = astConvert( frame1, frame2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER CVT + ... + CVT = AST_CONVERT( FRAME1, FRAME2, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +the Mapping contained within the FrameSet returned by +c+ +astConvert +c- +f+ +AST\_CONVERT +f- +will be a one-dimensional ZoomMap which simply scales its input (a +velocity in $km/h$) by a factor of 0.278 to create its output (a velocity +in $m/s$). + +c+ +In fact we need not have set the Unit attribute active in ``frame1'' +since the behaviour of astConvert is determined by its ``to'' Frame +(the second Frame parameter). +c- +f+ +In fact we need not have set the Unit attribute active in FRAME1 +since the behaviour of AST\_CONVERT is determined by its TO Frame +(the second Frame argument). +f- + +\subsubsection{\label{ss:unitsyntax}The Syntax for Unit Strings} +Conversion between units systems relies on the use of a specific syntax +for the Unit attribute. If the value of the Unit attribute does not +conform to this syntax, then an error will be reported if an attempt is +made to use it to determine an inter-unit Mapping (this will never happen +if the Unit attribute is \emph{passive}). + +The adopted syntax is that described in FITS-WCS paper I "Representation +of World Coordinate in FITS" by Greisen \& Calabretta. We distinguish +here between ``basic'' units and ``derived'' units: derived units are +defined in terms of other units (either derived or basic), whereas basic +units have no such definitions. Derived units may be represented by their +own \emph{symbol} (\emph{e.g.} ``Jy''---the Jansky) or by a +\emph{mathematical expression} which combines other symbols and constants +to form a definition of the unit (\emph{e.g.} ``km/s''---kilometres per +second). Unit symbols may be prefixed by a string representing a standard +multiple or sub-multiple. + +In addition to the unit symbols listed in FITS-WCS Paper I, any other +arbitrary unit symbol may be used, with the proviso that it will not be +possible to convert between Frames using such units. The exception to +this is if both Frames refer to the same unknown unit string. For instance, +an axis with unknown unit symbol "flop" \emph{could} be converted to an axis +with unit "Mflop" (Mega-flop). + +Unit symbols (optionally prefixed with a multiple or sub-multiple) can be +combined together using a limited range of mathematical operators and +functions, to produce new units. Such expressions may also contain +parentheses and numerical constants (these may optionally use +``scientific'' notation including an ``E'' character to represent the +power of 10). + +The following tables list the symbols for the basic and derived units which +may be included in a units string, the standard prefixes for multiples +and sub-multiples, and the strings which may be used to represent +mathematical operators and functions. + +\begin{table}[htbp] +\begin{center} +\begin{tabular}{|l|l|l|} +\hline +\multicolumn{3}{|c|}{{\large Basic units}} \\ \hline +\multicolumn{1}{|c|}{Quantity} & \multicolumn{1}{|c|}{Symbol} & +\multicolumn{1}{c|}{Full Name} \\ \hline +length & m & metre \\ +mass & g & gram \\ +time & s & second \\ +plane angle & rad & radian \\ +solid angle & sr & steradian \\ +temperature & K & Kelvin \\ +electric current & A & Ampere \\ +amount of substance & mol & mole \\ +luminous intensity & cd & candela \\ +\hline +\end{tabular} +\end{center} +\end{table} + +\begin{table}[htbp] +\begin{center} +\begin{small} +\begin{tabular}{|l|l|l|l|} +\hline +\multicolumn{4}{|c|}{{\large Derived units}} \\ \hline +\multicolumn{1}{|c|}{Quantity} & \multicolumn{1}{|c|}{Symbol} & +\multicolumn{1}{c|}{Full Name} & \multicolumn{1}{c|}{Definition} \\ \hline +area & barn & barn & 1.0E-28 m**2 \\ +area & pix & pixel & \\ +area & pixel & pixel & \\ +electric capacitance & F & Farad & C/V \\ +electric charge & C & Coulomb & A s \\ +electric conductance & S & Siemens & A/V \\ +electric potential & V & Volt & J/C \\ +electric resistance & Ohm & Ohm & V/A \\ +energy & J & Joule & N m \\ +energy & Ry & Rydberg & 13.605692 eV \\ +energy & eV & electron-Volt & 1.60217733E-19 J \\ +energy & erg & erg & 1.0E-7 J \\ +events & count & count & \\ +events & ct & count & \\ +events & ph & photon & \\ +events & photon & photon & \\ +flux density & Jy & Jansky & 1.0E-26 W /m**2 /Hz \\ +flux density & R & Rayleigh & 1.0E10/(4*PI) photon.m**-2 /s/sr \\ +flux density & mag & magnitude & \\ +force & N & Newton & kg m/s**2 \\ +frequency & Hz & Hertz & 1/s \\ +illuminance & lx & lux & lm/m**2 \\ +inductance & H & Henry & Wb/A \\ +length & AU & astronomical unit & 1.49598E11 m \\ +length & Angstrom & Angstrom & 1.0E-10 m \\ +length & lyr & light year & 9.460730E15 m \\ +length & pc & parsec & 3.0867E16 m \\ +length & solRad & solar radius & 6.9599E8 m \\ +luminosity & solLum & solar luminosity & 3.8268E26 W \\ +luminous flux & lm & lumen & cd sr \\ +magnetic field & G & Gauss & 1.0E-4 T \\ +magnetic flux & Wb & Weber & V s \\ +mass & solMass & solar mass & 1.9891E30 kg \\ +mass & u & unified atomic mass unit & 1.6605387E-27 kg \\ +magnetic flux density & T & Tesla & Wb/m**2 \\ +plane angle & arcmin & arc-minute & 1/60 deg \\ +plane angle & arcsec & arc-second & 1/3600 deg \\ +plane angle & mas & milli-arcsecond & 1/3600000 deg \\ +plane angle & deg & degree & pi/180 rad \\ +power & W & Watt & J/s \\ +pressure, stress & Pa & Pascal & N/m**2 \\ +time & a & year & 31557600 s \\ +time & d & day & 86400 s \\ +time & h & hour & 3600 s \\ +time & yr & year & 31557600 s \\ +time & min & minute & 60 s \\ + & D & Debye & 1.0E-29/3 C.m \\ +\hline +\end{tabular} +\end{small} +\end{center} +\end{table} + +\begin{table}[htbp] +\begin{center} +\begin{tabular}{|lll|lll|} +\hline +\multicolumn{6}{|c|}{{\large Prefixes for multiples \& +sub-multiples}} \\ \hline +\multicolumn{1}{|c}{Sub-multiple} & \multicolumn{1}{c}{Name} & +\multicolumn{1}{c|}{Prefix} & +\multicolumn{1}{|c}{Sub-multiple} & \multicolumn{1}{c}{Name} & +\multicolumn{1}{c|}{Prefix} \\ \hline +$10^{-1}$ & deci & d & $10$ & deca & da \\ +$10^{-2}$ & centi & c & $10^{2}$ & hecto & h \\ +$10^{-3}$ & milli & m & $10^{3}$ & kilo & k \\ +$10^{-6}$ & micro & u & $10^{6}$ & mega & M \\ +$10^{-9}$ & nano & n & $10^{9}$ & giga & G \\ +$10^{-12}$ & pico & p & $10^{12}$ & tera & T \\ +$10^{-15}$ & femto & f & $10^{15}$ & peta & P \\ +$10^{-18}$ & atto & a & $10^{18}$ & exa & E \\ +$10^{-21}$ & zepto & z & $10^{21}$ & zetta & Z \\ +$10^{-24}$ & yocto & y & $10^{24}$ & yotta & Y \\ +\hline +\end{tabular} +\end{center} +\end{table} + +\begin{table}[htbp] +\begin{center} +\begin{tabular}{|l|l|} +\hline +\multicolumn{2}{|c|}{{\large Mathematical operators \& functions}} \\ +\hline +\multicolumn{1}{|c|}{String} & \multicolumn{1}{|c|}{Meaning} \\ \hline +sym1 sym2 & multiplication (a space) \\ +sym1*sym2 & multiplication (an asterisk) \\ +sym1.sym2 & multiplication (a dot) \\ +sym1/sym2 & division \\ +sym1**y & exponentiation ($y$ must be a numerical constant)\\ +sym1\verb+^+y & exponentiation ($y$ must be a numerical constant)\\ +log(sym1) & common logarithm \\ +ln(sym1) & natural logarithm \\ +exp(sym1) & exponential \\ +sqrt(sym1) & square root \\ +\hline +\end{tabular} +\end{center} +\end{table} + +\subsubsection{Side-effects of Changing the Unit attribute} +If an Axis has an active Unit attribute, changing its value (either by +setting a new value or by clearing it so that the default value is +re-instated) may cause the Label and Symbol attributes to be changed +accordingly. For instance, if an Axis has Unit, Label and Symbol of ``Hz'', +``Frequency'' and ``nu'', then changing its Unit attribute to ``log(Hz)'' +will cause AST to change its Label and Symbol to ``log(Frequency)'' and +``Log(nu)''. These changes are only made if the Unit attribute is active, +and a Mapping can be found from the old units to the new units. On the other + hand, changing the Unit from ``Hz'' to ``MHz'' would not cause any change +to the Label or Symbol attributes. + +\cleardoublepage +\section{\label{ss:skyframes}Celestial Coordinate Systems (SkyFrames)} + +A Frame which is specialised for representing coordinate systems on +the celestial sphere is obviously of great importance in +astronomy. The SkyFrame is such a Frame. In this section we examine +the additional properties and behaviour of a SkyFrame that distinguish +it from a basic Frame (\secref{ss:frames}). + +\subsection{The SkyFrame Model} + +A SkyFrame is, of course, a Frame (\secref{ss:frames}) and also a +Mapping (\secref{ss:mappings}), so it inherits all the properties and +behaviour of these two ancestral classes. When used as a Mapping, a +SkyFrame implements a unit transformation, exactly like a basic Frame +(\secref{ss:frameasmapping}) or a UnitMap, so this aspect of its +behaviour is not of great importance. + +When used as a Frame, however, a SkyFrame represents a 2-dimensional +\emph{spherical} coordinate system, in which the shortest distance +between two points is a great circle. A SkyFrame therefore always has +exactly two axes which represent the longitude and latitude of a +coordinate system residing on the celestial sphere. Many such +coordinate systems can be represented by a SkyFrame, as we will see +shortly. + +A SkyFrame can represent any of the commonly used celestial coordinate +systems. Optionally, the origin of the longitude/latitude system can be +moved to any specified point in the standard celestial system, allowing +a SkyFrame to represent offsets from a specified sky position. + +c+ +When it is first created, a SkyFrame's axes are always in the order +(longitude,~latitude) but this can be changed, if required, by using the +astPermAxes function (\secref{ss:permutingaxes}). The order of the axes +can be determined at any time using the LatAxis and LonAxis attributes. A +SkyFrame's coordinate values are always stored as angles in (double +precision) radians, regardless of the setting of the Unit attribute +c- +f+ +When it is first created, a SkyFrame's axes are always in the order +(longitude,~latitude) but this can be changed, if required, by using the +AST\_PERMAXES routine (\secref{ss:permutingaxes}). The order of the axes +can be determined at any time using the LatAxis and LonAxis attributes. A +SkyFrame's coordinate values are always stored as angles in (double +precision) radians, regardless of the setting of the Unit attribute +f- +\footnote{The units used for the internal floating-point representation of an +axis value can be determined by examining the InternalUnit attribute of +the Frame. For most Frames, the Unit and InternalUnit attributes will be +equal, but InternalUnit is always set to ``\texttt{rad}'' for SkyFrames.}. + +\subsection{Creating a SkyFrame} + +The SkyFrame constructor function is particularly simple and a +SkyFrame with default attributes is created as follows: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstSkyFrame *skyframe; + +... + +skyframe = astSkyFrame( "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER SKYFRAME, STATUS + + STATUS = 0 + + ... + + SKYFRAME = AST_SKYFRAME( ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +Such a SkyFrame would represent the default celestial coordinate +system which, at present, is the ICRS system (the default was "FK5(J2000)" +in versions of AST prior to 3.0). + +\subsection{Specifying a Particular Celestial Coordinate System} + +For many purposes, the ICRS coordinate system is perfectly +adequate. In order to support conversion between a variety of +celestial coordinate systems, however, you can create SkyFrames that +represent any of these. + +Selection of a particular coordinate system is performed simply by +setting a value for the SkyFrame's (character string) System +attribute. This setting is most conveniently done when the SkyFrame is +created. For example, a SkyFrame representing the old FK4~(B1950.0) +coordinate system would be created by: + +c+ +\small +\begin{terminalv} +skyframe = astSkyFrame( "System=FK4" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SKYFRAME = AST_SKYFRAME( 'System=FK4', STATUS ) +\end{terminalv} +\normalsize +f- + +Note that specifying ``System$=$FK4'' also changes the associated +equinox (from J2000.0 to B1950.0). This is because the default value +of the SkyFrame's Equinox attribute (\secref{ss:equinoxitem}) depends +on the System attribute setting. + +You may change the System value at any time, although this is not +usually needed. The values supported are set out in the attribute's +description in \appref{ss:attributedescriptions} and include a variety +of equatorial coordinate systems, together with ecliptic and galactic +coordinates. + +General spherical coordinates are supported by specifying +``System$=$unknown''. You should note, though, that no Mapping can be +created to convert between ``unknown'' coordinates and any of the other +celestial coordinate systems (see \secref{ss:introducingconversion} ). + +\subsection{Attributes which Qualify Celestial Coordinate Systems} + +Many celestial coordinate systems have some additional free parameters +which serve to identify a particular coordinate system from amongst a +broader class of related coordinate systems. For example, the +FK5~(J2010.0) system is distinguished from the FK5~(J2000.0) +system by a different equinox---and the coordinates of a fixed +astronomical source would have different values when expressed in +these two systems. + +In AST, these free parameters are represented by additional SkyFrame +attributes, each of which has a default appropriate to +(\emph{i.e.}\ defined by) the setting of the main System +attribute. Each of these \emph{qualifying attributes} may, however, be +assigned an explicit value so as to select a particular coordinate +system. Note, it is usually best to assign explicit +values whenever possible rather than relying on defaults. Attribute +should only be left at their default value if you ``don't care'' what +value is used. In certain circumstances (particularly, when aligning two +Frames), a default value for an attribute may be replaced by the value +from another similar Frame. Such value replacement can be prevented by +assigning an explicit value to the attribute, rather than simply relying on +the default. + + +The main SkyFrame attributes which qualify the System attribute are: + +\begin{quote} +\begin{description} + +\item[\label{ss:epochitem}Epoch]\mbox{}\\ +This attribute is inherited from the Frame class. It gives the moment in +time when the coordinates are correct for the astronomical source +under study (usually the date of observation). + +\item[\label{ss:equinoxitem}Equinox]\mbox{}\\ +This value is used to qualify celestial coordinate systems that are +notionally based on the Earth's equator and/or the ecliptic (the plane +of the Earth's orbit around the Sun). The position of either of these +planes is difficult to specify precisely, so in practice a model +\emph{mean} equator and/or ecliptic are used instead. These, together +with the point on the sky that defines the coordinate origin (termed +the \emph{mean equinox}) move with time according to some model which +smoothes out the more rapid fluctuations. The SkyFrame class supports +both the old FK4 model and the newer FK5 one. + +Coordinates expressed in any of these systems vary with time due to +movement (by definition) of the coordinate system itself, and must +therefore be qualified by a moment in time (the \emph{epoch of the mean +equinox}, or ``equinox'' for short) which specifies the position of +the model coordinate system on the sky. This is the role of the +Equinox attribute. + +Note that it is quite valid and common to relate the position of a +source to an equinox other than the date of observation. Usually a +standard equinox such as J2000.0 is used, meaning that the coordinates +are referred to axes defined by where the model mean equator and +ecliptic would lie on the sky at the Julian epoch J2000.0. +\end{description} +\end{quote} + +For further details of these attributes you should consult their +descriptions in \appref{ss:attributedescriptions} and for details of +the System settings for which they are relevant, see the description +of the System attribute (also in \appref{ss:attributedescriptions}). +For the interested reader, an excellent overview of celestial +coordinate systems can also be found in the documentation for the +SLALIB library (\xref{SUN/67}{sun67}{}). + +The value of these qualifying attributes is most conveniently set at +the same time as the System value, \emph{e.g.}\ when a SkyFrame is +created. For instance: + +c+ +\small +\begin{terminalv} +skyframe = astSkyFrame( "System=Ecliptic, Equinox=J2005.5" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SKYFRAME = AST_SKYFRAME( 'System=Ecliptic, Equinox=J2005.5', STATUS ) +\end{terminalv} +\normalsize +f- + +would create a SkyFrame representing an ecliptic coordinate system +referred to the mean equinox and ecliptic of Julian epoch J2005.5. + +Note that it does no harm to assign values to qualifying attributes +which are not relevant to the main System value. Any such values are +stored, but are not used unless the System value is later set so that +they become relevant. + +\subsection{Using Default SkyFrame Attributes} + +c+ +The default values supplied for many SkyFrame attributes will depend +on the value of the SkyFrame's System attribute. In practice, this +means that there is usually little need to specify many of these +attributes explicitly unless you have some special requirement. This +can be illustrated by using astShow to examine a SkyFrame, as follows: +c- +f+ +The default values supplied for many SkyFrame attributes will depend +on the value of the SkyFrame's System attribute. In practice, this +means that there is usually little need to specify many of these +attributes explicitly unless you have some special requirement. This +can be illustrated by using AST\_SHOW to examine a SkyFrame, as +follows: +f- + +c+ +\small +\begin{terminalv} +astShow( astSkyFrame( "System=FK4-NO-E, Epoch=1958" ) ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SHOW( AST_SKYFRAME( 'System=FK4-NO-E, Epoch=1958', STATUS ), STATUS ) +\end{terminalv} +\normalsize +f- + +The output from this might look like the following: + +\begin{terminalv} + Begin SkyFrame # Description of celestial coordinate system +# Title = "FK4 equatorial coordinates; no E-terms; mean equinox B1950.0; +epoch B1958.0" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain + Epoch = 1958 # Besselian epoch of observation +# Lbl1 = "Right ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 + System = "FK4-NO-E" # Coordinate system type +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction +# Bot2 = -1.5707963267949 # Lowest legal axis value +# Top2 = 1.5707963267949 # Highest legal axis value + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description +# Eqnox = 1950 # Besselian epoch of mean equinox + End SkyFrame +\end{terminalv} + +Note that the defaults (indicated by the ``\verb?#?'' comment +character at the start of the line) for attributes such as the Title, +axis Labels and Format specifiers are all set to values appropriate +for the particular equatorial coordinate system that the SkyFrame +represents. + +c+ +This means, for example, that if we were to use this SkyFrame to +format a right ascension value stored in radians using astFormat +(\secref{ss:formattingaxisvalues}), it would automatically result in a +string in sexagesimal notation (such as ``12:14:35.7'') suitable for +display. If we changed the value of the SkyFrame's Digits attribute +(which is inherited from the Frame class), the number of digits +appearing would also change accordingly. +c- +f+ +This means, for example, that if we were to use this SkyFrame to +format a right ascension value stored in radians using AST\_FORMAT +(\secref{ss:formattingaxisvalues}), it would automatically result in a +string in sexagesimal notation (such as ``12:14:35.7'') suitable for +display. If we changed the value of the SkyFrame's Digits attribute +(which is inherited from the Frame class), the number of digits +appearing would also change accordingly. +f- + +These choices would be appropriate for a System value of ``FK4-NO-E'', +but if a different System value were set, the defaults would be +correspondingly different. For example, ecliptic longitude is +traditionally expressed in degrees, so setting ``System=ecliptic'' +would result in coordinate values being formatted as degrees by +default. + +Of course, if you do not like any of these defaults, you may always +over-ride them by setting explicit attribute values yourself. + +\subsection{\label{ss:formattingskyaxisvalues}Formatting Celestial Coordinates} + +c+ +SkyFrames use astFormat for formatting coordinate values in the same +way as other Frames (\secref{ss:formattingaxisvalues}). However, they +offer a different set of formatting options more appropriate to +celestial coordinates. +c- +f+ +SkyFrames use AST\_FORMAT for formatting coordinate values in the same +way as other Frames (\secref{ss:formattingaxisvalues}). However, they +offer a different set of formatting options more appropriate to +celestial coordinates. +f- + +The Digits attribute of a SkyFrame behaves in essentially the same way +as for a basic Frame (\secref{ss:formattingwithdigits}), so the +precision with which celestial coordinates are displayed can also be +adjusted in this way. However, the range of format specifiers that can +be given for the Format(axis) attribute, and the default format +resulting from any particular Digits value, is different. + +The syntax of SkyFrame format specifiers is detailed under the +description of the Format(axis) attribute in +\appref{ss:attributedescriptions}. Briefly, however, it allows +celestial coordinates to be expressed either as angles or times and to +include one or more of the fields: + +\begin{quote} +\begin{itemize} +\item degrees or hours +\item arc-minutes or minutes +\item arc-seconds or seconds +\end{itemize} +\end{quote} + +with a specified number of decimal places for the final field. A range +of field separators is also available, as the following examples show: + +\begin{quote} +\begin{center} +\begin{tabular}{|l|l|} +\hline +\textbf{Format Specifier} & \textbf{Example Formatted Value}\\ +\hline \hline +{\tt{d}} & {\tt{219}}\\ +{\tt{d.3}} & {\tt{219.123}}\\ +{\tt{dm}} & {\tt{219:05}}\\ +{\tt{dm.2}} & {\tt{219:05.44}}\\ +{\tt{dms}} & {\tt{219:05:42}}\\ +{\tt{hms.1}} & {\tt{15:44:13.8}}\\ +{\tt{bdms.2}} & {\tt{219 05 42.81}}\\ +{\tt{lhms.3}} & {\tt{15h44m13.88s}}\\ +{\tt{+zlhms}} & {\tt{+06h10m44s}}\\ +{\tt{ms.1}} & {\tt{13145:42.8}}\\ +{\tt{lmst.3}} & {\tt{876m22.854s}}\\ +{\tt{s.2}} & {\tt{788742.81}}\\ +\hline +\end{tabular} +\end{center} +\end{quote} + +Note the following key points: + +\begin{itemize} +\item The required fields are specified using characters chosen from +either ``dms'' or ``hms'' according to whether the value is to be +formatted as an angle (in degrees) or a time (in hours). + +\item If no degrees or hours field is required, the distinction +between angle and time may be made by including ``t'' to request time. + +\item The number of decimal places (for the final field) is indicated +using ``\texttt{.}'' followed by an integer. An asterisk can be used in +place of an integer, in which case the number of decimal places is +chosen so that the total number of digits in the formatted value is equal +to the value of the Digits attribute. + +\item ``b'' causes fields to be separated by blanks, while ``l'' +causes them to be separated by the appropriate letters (the default +being a colon). + +\item ``z'' causes padding with leading zeros. + +\item ``+'' cause a plus sign to be prefixed to positive values +(negative values always have a minus sign). +\end{itemize} + +The formatting performed by a SkyFrame is also influenced by the +AsTime(axis) attribute, which has a boolean (integer) value for each +SkyFrame axis. It determines whether the default format specifier for +an axis will present values as angles (\emph{e.g.}\ in degrees) if it +is zero, or as times (\emph{e.g.}\ in hours) if it is non-zero. + +The default AsTime value depends on the celestial coordinate system +which the SkyFrame represents which, in turn, depends on its System +attribute value. For example, equatorial longitude values (right +ascension) are normally expressed in hours, whereas ecliptic +longitudes are normally expressed in degrees, so their default AsTime +values will reflect this difference. + +The value of the AsTime attribute may be set explicitly to over-ride +these defaults if required, with the formatting precision being +determined by the Digits/Digits(axis) value. Alternatively, the +Format(axis) attribute may be set explicitly to specify both the +format and precision required. Setting an explicit Format value always +over-rides the effects of both the Digits and AsTime attributes (unless +the Format value does not specify the required number of decimal places, +in which case Digits is used to determine the default number of decimal +places) + +\subsection{\label{ss:unformattingskyaxisvalues}Reading Formatted Celestial Coordinates} + +c+ +The process of converting formatted celestial coordinates, such as +might be produced by the astFormat function +(\secref{ss:formattingskyaxisvalues}), into numerical (double) +coordinate values is performed by using astUnformat +(\secref{ss:unformattingaxisvalues}) and passing it a pointer to a +SkyFrame. The use of a SkyFrame means that the range of input formats +accepted is appropriate to positions on the sky expressed as angles +and/or times, while the returned value is in radians. +c- +f+ +The process of converting formatted celestial coordinates, such as +might be produced by the AST\_FORMAT function +(\secref{ss:formattingskyaxisvalues}), into numerical (double +precision) coordinate values is performed by using AST\_UNFORMAT +(\secref{ss:unformattingaxisvalues}) and passing it a pointer to a +SkyFrame. The use of a SkyFrame means that the range of input formats +accepted is appropriate to positions on the sky expressed as angles +and/or times, while the returned value is in radians. +f- + +The following describes the forms of celestial coordinate which are +supported: + +\begin{itemize} +\item You may supply an optional sign, followed by between one and +three fields representing either degrees, arc-minutes, arc-seconds or +hours, minutes, seconds (\emph{e.g.}\ ``$-$12~42~03''). + +\item Each field should consist of a sequence of one or more digits, +which may include leading zeros. At most one field may contain a +decimal point, in which case it is taken to be the final field +(\emph{e.g.}\ decimal degrees might be given as ``124.707'', while +degrees and decimal arc-minutes might be given as ``$-$13~33.8''). + +\item The first field given may take any value, allowing angles and +times outside the conventional ranges to be represented. However, +subsequent fields must have values of less than 60 (\emph{e.g.} +``720~45~31'' is valid, whereas ``11~45~61'' is not). + +\item Fields may be separated by white space or by ``:'' (colon), but +the choice of separator must be used consistently throughout the +value. Additional white space may be present around fields and +separators (\emph{e.g.}\ ``$-$~2:~04~:~7.1''). + +\item The following field identification characters may be used as +separators to replace those above (or may be appended to the final +field), in order to identify the field to which they are appended: + +\begin{quote} +\begin{tabular}{lll} +d & -- & degrees \\ +h & -- & hours \\ +m & -- & minutes (of arc or time) \\ +s & -- & seconds (of arc or time) \\ +\texttt{'} & -- & arc-minutes \\ +\texttt{"} & -- & arc-seconds +\end{tabular} +\end{quote} + +Either lower or upper case may be used. Fields must be given in order +of decreasing significance +(\emph{e.g.}\ ``$-$11D~3\texttt{'}~14.4\texttt{"}'' or ``22h14m11.2s''). + +\item The presence of certain field identification characters +indicates whether the value is to be interpreted as an angle or a time +(with 24 hours corresponding to 360 degrees), as follows: + +\begin{quote} +\begin{tabular}{lll} +d & -- & angle \\ +\texttt{'} & -- & angle \\ +\texttt{"} & -- & angle \\ +h & -- & time +\end{tabular} +\end{quote} + +Incompatible angle/time identification characters may not be mixed +(\emph{e.g.}\ ``10h14\texttt{'}3\texttt{"}'' is not valid). The remaining +field identification characters and separators do not specify a +preference for an angle or a time and may be used with either. + +c+ +\item If no preference for an angle or a time is expressed anywhere +within the value, then it is interpreted as an angle if the Format +attribute string associated with the SkyFrame axis generates an angle +and as a time otherwise. This ensures that values produced by +astFormat (\secref{ss:formattingskyaxisvalues}) are correctly +interpreted by astUnformat. +c- +f+ +\item If no preference for an angle or a time is expressed anywhere +within the value, then it is interpreted as an angle if the Format +attribute string associated with the SkyFrame axis generates an angle +and as a time otherwise. This ensures that values produced by +AST\_FORMAT (\secref{ss:formattingskyaxisvalues}) are correctly +interpreted by AST\_UNFORMAT. +f- + +\item Fields may be omitted, in which case they default to zero. The +remaining fields may be identified by using appropriate field +identification characters (see above) and/or by adding extra colon +separators (e.g. ``$-$05m13s'' is equivalent to ``$-$:05:13''). If a field +is not identified explicitly, it is assumed that adjacent fields have +been given, after taking account of any extra separator +characters. For example: + +\begin{quote} +\begin{tabular}{lll} +10d & -- & degrees \\ +10d12 & -- & degrees and arc-minutes \\ +11:14\texttt{"} & -- & arc-minutes and arc-seconds \\ +9h13s & -- & hours and seconds of time \\ +:45:33 & -- & minutes and seconds (of arc or time) \\ +:55: & -- & minutes (of arc or time) \\ +::13 & -- & seconds (of arc or time) \\ +$-$6::2.5 & -- & degrees/hours and seconds (of arc or time) \\ +07m14 & -- & minutes and seconds (of arc or time) \\ +$-$8:14\texttt{'} & -- & degrees and arc-minutes \\ +$-$h3:14 & -- & minutes and seconds of time \\ +h:2.1 & -- & seconds of time +\end{tabular} +\end{quote} + +c+ +\item If fields are omitted in such a way that the remaining ones +cannot be identified uniquely (e.g. ``01:02''), then the first field +(either given explicitly or implied by an extra leading colon +separator) is taken to be the most significant field that astFormat +would produce when formatting a value (using the Format attribute +associated with the SkyFrame axis). By default, this means that the +first field will normally be interpreted as degrees or hours. However, +if this does not result in consistent field identification, then the +last field (either given explicitly or implied by an extra trailing +colon separator) is taken to to be the least significant field that +astFormat would produce. +c- +f+ +\item If fields are omitted in such a way that the remaining ones +cannot be identified uniquely (e.g. ``01:02''), then the first field +(either given explicitly or implied by an extra leading colon +separator) is taken to be the most significant field that AST\_FORMAT +would produce when formatting a value (using the Format attribute +associated with the SkyFrame axis). By default, this means that the +first field will normally be interpreted as degrees or hours. However, +if this does not result in consistent field identification, then the +last field (either given explicitly or implied by an extra trailing +colon separator) is taken to to be the least significant field that +AST\_FORMAT would produce. +f- + +\end{itemize} + +c+ +This final convention is intended to ensure that values formatted by +astFormat which contain less than three fields will be correctly +interpreted if read back using astUnformat, even if they do not +contain field identification characters. However, it also affects +other forms of input. For example, if the Format(axis) string were set +to ``mst.1'' (producing two fields representing minutes and seconds of +time), then formatted input would be interpreted by astUnformat as +follows: +c- +f+ +This final convention is intended to ensure that values formatted by +AST\_FORMAT which contain less than three fields will be correctly +interpreted if read back using AST\_UNFORMAT, even if they do not +contain field identification characters. However, it also affects +other forms of input. For example, if the Format(axis) string were set +to ``mst.1'' (producing two fields representing minutes and seconds of +time), then formatted input would be interpreted by AST\_UNFORMAT as +follows: +f- + +\begin{quote} +\begin{tabular}{lll} +12 13 & -- & minutes and seconds \\ +12 & -- & minutes \\ +:13 & -- & seconds \\ +$-$18: & -- & minutes \\ +12.8 & -- & minutes \\ +1 2 3 & -- & hours, minutes and seconds \\ +& & \\ +4\texttt{'} & -- & arc-minutes \\ +60::\texttt{"} & -- & degrees \\ +$-$23:\texttt{"} & -- & arc-minutes \\ +$-$33h & -- & hours +\end{tabular} +\end{quote} + +(in the last four cases, explicit field identification has been given +which overrides the implicit identification). + +c+ +Alternatively, if the Format(axis) string were set to ``s.3'' +(producing only an arc-seconds field), then formatted input would be +interpreted by astUnformat as follows: +c- +f+ +Alternatively, if the Format(axis) string were set to ``s.3'' +(producing only an arc-seconds field), then formatted input would be +interpreted by AST\_UNFORMAT as follows: +f- + +\begin{quote} +\begin{tabular}{lll} +12.8 & -- & arc-seconds \\ +12 13 & -- & arc-minutes and arc-seconds \\ +:12 & -- & arc-seconds \\ +13: & -- & arc-minutes \\ +1 2 3 & -- & degrees, arc-minutes and arc-seconds +\end{tabular} +\end{quote} + +In general, if you are preparing formatted input data containing +celestial coordinates and wish to omit certain fields, then you are +advised to identify clearly those that you do provide by using the +appropriate field identification characters and/or extra colon +separators. This prevents you depending on the implicit field +identification described above which, in turn, depends on an +appropriate Format(axis) string having been set. + +When writing software, it is also a good idea to set the Format(axis) +string so that data input will be as simple as possible for the +user. Unless some special effect is desired, this normally means that +it should contain ``d'' or ``h'' to ensure that the first field +entered by the user will be interpreted as degrees or hours, unless +otherwise identified. This is the normal behaviour unless an explicit +Format(axis) value has been set to override the default. + +\subsection{Representing Offsets from a Specified Sky Position} +A SkyFrame can be modified so that its longitude and latitude axes are +referred to an origin at any specified sky position. Such a coordinate +system is referred to as an ``offset'' coordinate system. First, the System +attribute should be set to represent the celestial coordinate system in +which the origin is to be specified. Then the SkyRef attribute should be +set to hold the coordinates of the origin within the selected celestial +coordinate system. + +By default, ``north'' in the new offset coordinate system is parallel to +north in the original celestial coordinate system. However, the direction +of north in the offset system can be controlled by assigning a value to +the SkyRefP attribute. This attribute should be assigned the celestial +coordinates of a point which is on the zero longitude meridian and which +has non-zero latitude. + +By default, the position given by the SkyRef attribute is used as the +origin of the new longitude/latitude system, but an option exists to use +it as the north pole of the system instead. This option is controlled by +the SkyRefIs attribute. The choice of value for SkyRefIs depends on what +sort of offset coordinate system you want. Setting SkyRefIs to +``Origin'' (the default) produces an offset coordinate system which is +approximately Cartesian close to the specified position. Setting SkyRefIs +to +``Pole'' produces an offset coordinate system which is approximately Polar +close to the specified position. + +\cleardoublepage +\section{\xlabel{ss_specframes}\label{ss:specframes}Spectral Coordinate Systems (SpecFrames)} + +The SpecFrame is a Frame which is specialised for representing coordinate +systems which describe a position within an electro-magnetic spectrum. +In this section we examine the additional properties and behaviour of a +SpecFrame that distinguish it from a basic Frame (\secref{ss:frames}). + +\subsection{The SpecFrame Model} + +As for a SkyFrame, a SpecFrame is a Frame (\secref{ss:frames}) and also a +Mapping (\secref{ss:mappings}), so it inherits all the properties and +behaviour of these two ancestral classes. When used as a Mapping, a +SpecFrame implements a unit transformation, exactly like a basic Frame +(\secref{ss:frameasmapping}) or a UnitMap, so this aspect of its +behaviour is not of great importance. + +When used as a Frame, however, a SpecFrame represents a wide range of +different 1-dimensional coordinate system which can be used to describe +positions within a spectrum. The options available largely mirror those +described in the FITS-WCS paper III \emph{Representations of spectral +coordinates in FITS} (Greisen, Valdes, Calabretta \& Allen). + +\subsection{Creating a SpecFrame} + +The SpecFrame constructor function is particularly simple and a +SpecFrame with default attributes is created as follows: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstSpecFrame *specframe; + +... + +specframe = astSpecFrame( "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER SPECFRAME, STATUS + + STATUS = 0 + + ... + + SPECFRAME = AST_SPECFRAME( ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +Such a SpecFrame would represent the default coordinate system which is +heliocentric wavelength in metres (i.e. wavelength corrected to take into +account the Doppler shift caused by the velocity of the observer around the +sun). + +\subsection{Specifying a Particular Spectral Coordinate System} + +Selection of a particular coordinate system is performed simply by +setting a value for the SpecFrame's (character string) System +attribute. This setting is most conveniently done when the SpecFrame is +created. For example, a SpecFrame representing Energy would be created by: + +c+ +\small +\begin{terminalv} +specframe = astSpecFrame( "System=Energy" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SPECFRAME = AST_SPECFRAME( 'System=Energy', STATUS ) +\end{terminalv} +\normalsize +f- + +Note that specifying ``System$=$Energy'' also changes the associated +Unit (from metres to Joules). This is because the default value +of the SpecFrame's Unit attribute depends on the System attribute setting. + +You may change the System value at any time, although this is not +usually needed. The values supported are set out in the attribute's +description in \appref{ss:attributedescriptions} and include a variety +of velocity systems, together with frequency, wavelength, energy, +wave-number, \emph{etc}. + +\subsection{Attributes which Qualify Spectral Coordinate Systems} + +Many spectral coordinate systems have some additional free parameters +which serve to identify a particular coordinate system from amongst a +broader class of related coordinate systems. For example, the +velocity systems are all parameterised by a rest frequency---the +frequency which defines zero velocity, and all coordinate systems +are qualified by a `standard of rest'' which indicates the rest frame to +which the values refer. + +In AST, these free parameters are represented by additional SpecFrame +attributes, each of which has a default appropriate to +(\emph{i.e.}\ defined by) the setting of the main System +attribute. Each of these \emph{qualifying attributes} may, however, be +assigned an explicit value so as to select a particular coordinate +system. Note, it is usually best to assign explicit +values whenever possible rather than relying on defaults. Attribute +should only be left at their default value if you ``don't care'' what +value is used. In certain circumstances (particularly, when aligning two +Frames), a default value for an attribute may be replaced by the value +from another similar Frame. Such value replacement can be prevented by +assigning an explicit value to the attribute, rather than simply relying on +the default. + + +The main SpecFrame attributes which qualify the System attribute are: + +\begin{quote} +\begin{description} + +\item[Epoch]\mbox{}\\ +This attribute is inherited from the Frame class. It gives the moment in +time when the coordinates are correct for the astronomical source +under study (usually the date of observation). It is needed in order to +calculate the Doppler shift produced by the velocity of the observer +relative to the centre of the earth, and of the earth relative to the sun. + +\item[StdOfRest]\mbox{}\\ +This specifies the rest frame in which the coordinates are correct. +Transforming between different standards of rest involves taking account +of the Doppler shift introduced by the relative motion of the two +standards of rest. + +\item[RestFreq]\mbox{}\\ +Specifies the frequency which correspond to zero velocity. When setting a +value for this attribute, the value may be supplied as a wavelength +(including an indication of the units being used, ``nm'' ``Angstrom'', +\emph{etc.}), which will be automatically be converted to a frequency. + +\item[RefRA]\mbox{}\\ +Specifies the RA (FK5 J2000) of the source. This is used when converting +between standards of rest. It specifies the direction along which the +component of the relative velocity of the two standards of rest is taken. + +\item[RefDec]\mbox{}\\ +Specifies the Dec (FK5 J2000) of the source. Used in conjunction with +REFRA. + +\item[SourceVel]\mbox{}\\ +This defines the ``source'' standard of rest. This is a rest frame which +is moving towards the position given by RefRA and RefDec, at a velocity +given by SourceVel. The velocity is stored internally as a heliocentric +velocity, but can be given in any of the other supported standards of rest. + +\end{description} +\end{quote} + +For further details of these attributes you should consult their +descriptions in \appref{ss:attributedescriptions} and for details of +the System settings for which they are relevant, see the description +of the System attribute (also in \appref{ss:attributedescriptions}). + +Note that it does no harm to assign values to qualifying attributes +which are not relevant to the main System value. Any such values are +stored, but are not used unless the System value is later set so that +they become relevant. + +\subsection{Using Default SpecFrame Attributes} + +c+ +The default values supplied for many SpecFrame attributes will depend +on the value of the SpecFrame's System attribute. In practice, this +means that there is usually little need to specify many of these +attributes explicitly unless you have some special requirement. This +can be illustrated by using astShow to examine a SpecFrame, as follows: +c- +f+ +The default values supplied for many SpecFrame attributes will depend +on the value of the SpecFrame's System attribute. In practice, this +means that there is usually little need to specify many of these +attributes explicitly unless you have some special requirement. This +can be illustrated by using AST\_SHOW to examine a SpecFrame, as +follows: +f- + +c+ +\small +\begin{terminalv} +astShow( astSpecFrame( "System=Vopt, RestFreq=250 GHz" ) ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SHOW( AST_SPECFRAME( 'System=Vopt, RestFreq=250 GHz', STATUS ), + : STATUS ) +\end{terminalv} +\normalsize +f- + +The output from this might look like the following: + +\begin{terminalv} + Begin SpecFrame # Description of spectral coordinate system +# Title = "Optical velocity, rest frequency = 250 GHz" # Title +of coordinate system + Naxes = 1 # Number of coordinate axes +# Domain = "SPECTRUM" # Coordinate system domain +# Epoch = 2000 # Julian epoch of observation +# Lbl1 = "Optical velocity" # Label for axis 1 + System = "VOPT" # Coordinate system type +# Uni1 = "km/s" # Units for axis 1 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + End Axis + IsA Frame # Coordinate system description +# SoR = "Heliocentric" # Standard of rest + RstFrq = 250000000000 # Rest frequency (Hz) + End SpecFrame +\end{terminalv} + +Note that the defaults (indicated by the ``\verb?#?'' comment +character at the start of the line) for attributes such as the Title, +axis Labels and Unit specifiers are all set to values appropriate +for the particular velocity system that the SpecFrame represents. + +These choices would be appropriate for a System value of ``Vopt'', +but if a different System value were set, the defaults would be +correspondingly different. For example, by default frequency is measured in +units of GHz, not $km/s$, so setting ``System=freq'' +would change the appropriate line above from: + +\begin{terminalv} +# Uni1 = "km/s" # Units for axis 1 +\end{terminalv} + +to + +\begin{terminalv} +# Uni1 = "GHz" # Units for axis 1 +\end{terminalv} + +Of course, if you do not like any of these defaults, you may always +over-ride them by setting explicit attribute values yourself. For +instance, you may choose to have your frequency axis expressed in ``kHz'' +rather than ``GHz''. To do this simply set the attribute value as follows: + +c+ +\small +\begin{terminalv} +astSetC( specframe, "Unit", "kHz" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SETC( SPECFRAME, 'Unit', 'kHz', STATUS ) +\end{terminalv} +\normalsize +f- + +No error will be reported if you accidentally set an inappropriate Unit value +(say "J" - Joules)---after all, AST cannot tell what you are about to do, +and you \emph{may} be about to change the System value to ``Energy''. +However, an error \emph{will} be reported if you attempt to find a +conversion between two SpecFrames (for instance using +c+ +astConvert +c- +f+ +AST\_CONVERT +f- +) if either SpecFrame has a Unit value which is inappropriate for its +System value. + +SpecFrame attributes, like all other attributes, all have default +value. However, be aware that for some attributes these default values +can never be more than ``a legal numerical value'' and have no +astronomical significance. For instance, the RefRA and RefDec attributes +(which give the source position) both have a default value of zero. So +unless your source happens to be at that point (highly unlikely!) you will +need to set new values. Likewise, the RestFreq (rest frequency) attribute +has an arbitrary default value of 1.0E5 GHz. Some operations are not +affected by inappropriate values for these attributes (for instance, +converting from frequency to wavelength, changing axis units, \emph{etc}), +but some are. For instance, converting from frequency to velocity +requires a correct rest frequency, moving between different standards of +rest requires a correct source position. The moral is, always set explicit +values for as many attributes as possible. + +\subsection{\label{ss:creatingspectralcubes}Creating Spectral Cubes} +You can use a SpecFrame to describe the spectral axis in a data cube +containing two spatial axes and a spectral axis. To do this you would +create an appropriate SpecFrame, together with a 2-dimensional Frame +(often a SkyFrame) to describe the spatial axes. You would then combine +these two Frames together into a single CmpFrame. + +c+ +\small +\begin{terminalv} +AstSkyFrame *skyframe; +AstSpecFrame *specframe; +AstCmpFrame *cmpframe; +... +skyframe = astSkyFrame( "Epoch=J2002" ); +specframe = astSpecFrame( "System=Freq,StdOfRest=LSRK" ); +cmpframe = astCmpFrame( skyframe, specframe, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER SKYFRAME + INTEGER SPECFRAME + INTEGER CMPFRAME + ... + SKYFRAME = AST_SKYFRAME( 'Epoch=J2002', STATUS ) + SPECFRAME = AST_SPECFRAME( 'System=Freq,StdOfRest=LSRK', + : STATUS ) + CMPFRAME = AST_CMPFRAME( SKYFRAME, SPECFRAME, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +In the resulting CmpFrame, axis 1 will be RA, axis 2 will be Dec and axis +3 will be Frequency. If this is not the order you want, you can permute +the axes using +c+ +astPermAxes. +c- +f+ +AST\_PERMAXES. +f- + +There is one potential problem with this approach if you are interested in +unusually high accuracy. Conversion between different standards of rest +involves taking account of the Doppler shift caused by the relative +motion of the two standards of rest. At some point this involves finding +the component of the relative velocity in the direction of interest. +For a SpecFrame, this direction is always given by the RefRA and RefDec +attributes, even if the SpecFrame is embedded within a CmpFrame as above. +It would be more appropriate if this ``direction of interest'' was +specified by the values passed into the CmpFrame on the RA and DEC axes, +allowing each pixel within a data cube to have a slightly different +correction for Doppler shift. + +Unfortunately, the SpecFrame class cannot do this (since it is purely a +1-dimensional Frame), and so some small degree of error will be +introduced when converting between standards of rest, the size of the +error varying from pixel to pixel. It is hoped that at some point in the +future a sub-class of CmpFrame (a SpecCubeFrame) will be added to AST which +allows for this spatial variation in Doppler shift. + +The maximum velocity error introduced by this problem is of the order of +$V*SIN(FOV)$, where $FOV$ is the angular field of view, and $V$ is the +relative velocity of the two standards of rest. As an example, when +correcting from the observers rest frame (i.e. the topocentric rest +frame) to the kinematic local standard of rest the maximum value of $V$ +is about 20 $km/s$, so for 5 arc-minute field of view the maximum +velocity error introduced by the correction will be about 0.03 $km/s$. As +another example, the maximum error when correcting from the observers +rest frame to the local group is about 5 $km/s$ over a 1 degree field of +view. + +\subsection{\label{ss:handlingdualsidebandspectra}Handling Dual-Sideband Spectra} +Dual sideband super-heterodyne receivers produce spectra in which each channel +contains contributions from two different frequencies, referred to as the +``upper sideband frequency'' and the ``lower sideband frequency''. In the +rest frame of the observer (topocentric), these are related to each other as +follows: + +\begin{quote} +\begin{small} +\begin{equation} +\label{eqn:dsb} + f_{lsb} = 2.f_{LO} - f_{usb} +\end{equation} +\end{small} +\end{quote} + +where $f_{LO}$ is a fixed frequency known as the ``local oscillator +frequency''. In other words, the local oscillator frequency is always +mid-way between any pair of corresponding upper and lower sideband +frequencies\footnote{Note, this simple relationship only applies if all +frequencies are topocentric.}. If you want to describe the spectral axis +of such a spectrum using a SpecFrame you must choose whether you want the +SpecFrame to describe $f_{lsb}$ or $f_{usb}$ - a basic SpecFrame cannot +describe both sidebands simultaneously. However, there is a sub-class of +SpecFrame, called DSBSpecFrame, which overcomes this difficulty. + +A DSBSpecFrame has a SideBand attribute which indicates if the +DSBSpecFrame is currently being used to describe the upper or lower +sideband spectral axis. The value of this attribute can be changed at any +time. If you use the +c+ +astConvert +c- +f+ +AST\_CONVERT +f- +function to find the Mapping between two DSBSpecFrames, the setting for +the two SideBand attributes will be taken into account. Thus, if you take +a copy of a DSBSpecFrame, toggle its SideBand attribute, and then use +c+ +astConvert +c- +f+ +AST\_CONVERT +f- +to find a Mapping from the original to the modified copy, the resulting +Mapping will be of the form of equation \ref{eqn:dsb} (if the +DSBSpecFrame has its StdOfRest attribute set to ``Topocentric''). + +In general, when finding a Mapping between two arbitrary DSBSpecFrames, +the total Mapping is made of of three parts in series: + +\begin{enumerate} +\item A Mapping which converts the first DSBSpecFrame into its upper +sideband representation. If the DSBSpecFrame already represents its upper +sideband, this Mapping will be a UnitMap. +\item A Mapping which converts from the first to the second DSBSpecFrame, +treating them as if they were both basic SpecFrames. This takes account of +any difference in units, standard of rest, system, \emph{etc} between the +two DSBSpecFrames. +\item A Mapping which converts the second DSBSpecFrame from its upper +sideband representation to its current sideband. If the DSBSpecFrame +currently represents its upper sideband, this Mapping will be a UnitMap. +\end{enumerate} + +If an attempt is made to find the Mapping between a DSBSpecFrame and a +basic SpecFrame, then the DSBSpecFrame will be treated like a basic +SpecFrame. In other words, the returned Mapping will not be affected by +the setting of the SideBand attribute (or any of the other attributes +specific to the DSBSpecFrame class). + +In practice, the local oscillator frequency for a dual sideband +instrument may not be easily available to an observer. Instead, it is +common practice to specify the spectral position of some central feature +in the observation (commonly the centre of the instrument passband), +together with an ``intermediate frequency''. Together, these two values +allow the local oscillator frequency to be determined. The intermediate +frequency is the difference between the topocentric frequency at the +central spectral position and the topocentric frequency of the local +oscillator. So: + +\begin{quote} +\begin{small} +\begin{equation} +\label{eqn:dsb2} + f_{LO} = f_{central} + f_{if} +\end{equation} +\end{small} +\end{quote} + +The DSBSpecFrame class uses the DSBCentre attribute to specify the central +spectral position ($f_{central}$), and the IF attribute to specify the +intermediate frequency ($f_{if}$). The DSBCentre value is given and returned +in the spectral system described by the DSBSpecFrame (thus you do not need to +calculate the corresponding topocentric frequency yourself - this will be +done automatically by the DSBSpecFrame when you assign a new value to the +DSBCentre attribute). The value assigned to the IF attribute should +always be a topocentric frequency in units of Hz, however a negative +value may be given to indicate that the DSBCentre value is in the upper +sideband (that is, if $IF < 0$ then $f_{central} > f_{LO}$). A positive +value for IF indicates that the DSBCentre value is in the lower sideband +(that is, if $IF > 0$ then $f_{central} < f_{LO}$). + + +\cleardoublepage +\section{\xlabel{ss_timeframes}\label{ss:timeframes}Time Systems (TimeFrames)} + +The TimeFrame is a Frame which is specialised for representing moments in +time. In this section we examine the additional properties and behaviour of a +TimeFrame that distinguish it from a basic Frame (\secref{ss:frames}). + +\subsection{The TimeFrame Model} + +As for a SkyFrame, a TimeFrame is a Frame (\secref{ss:frames}) and also a +Mapping (\secref{ss:mappings}), so it inherits all the properties and +behaviour of these two ancestral classes. When used as a Mapping, a +TimeFrame implements a unit transformation, exactly like a basic Frame +(\secref{ss:frameasmapping}) or a UnitMap, so this aspect of its +behaviour is not of great importance. + +When used as a Frame, however, a TimeFrame represents a wide range of +different 1-dimensional coordinate system which can be used to describe +moments in time. Absolute times and relative (i.e. elapsed) times are +supported (attribute TimeOrigin), as are a range of different time scales +(attribute TimeScale). An absolute or relative value in any time scale can +be represented in different forms such as Modified Julian Date, Julian Epoch, +\emph{etc} (attribute System). AST extends the definition of these systems to +allow them to be used with any unit of time (attribute Unit). The TimeFrame +class also allows times to formatted as either a simple floating point value +or as a Gregorian date and time of day (attribute Format). + +\subsection{Creating a TimeFrame} + +The TimeFrame constructor function is particularly simple and a +TimeFrame with default attributes is created as follows: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstTimeFrame *timeframe; + +... + +timeframe = astTimeFrame( "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER TIMEFRAME, STATUS + + STATUS = 0 + + ... + + TIMEFRAME = AST_TIMEFRAME( ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +Such a TimeFrame would represent the default coordinate system which is +Modified Julian Date (with the usual units of days) in the International +Atomic Time (TAI) time scale. + +\subsection{Specifying a Particular Time System} +By setting the System attribute appropriately, the TimeFrame can represent +Julian Date, Modified Julian Date, Julian Epoch or Besselian Epoch (the +time scale is specified by a separate attribute called TimeScale). + +Selection of a particular coordinate system is performed simply by +setting a value for the TimeFrame's (character string) System +attribute. This setting is most conveniently done when the TimeFrame is +created. For example, a TimeFrame representing Julian Epoch would be created +by: + +c+ +\small +\begin{terminalv} +timeframe = astTimeFrame( "System=JEPOCH" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + TIMEFRAME = AST_TIMEFRAME( 'System=JEPOCH', STATUS ) +\end{terminalv} +\normalsize +f- + +Note that specifying ``System$=$JEPOCH'' also changes the associated +default Unit (from days to years). This is because the default value +of the TimeFrame's Unit attribute depends on the System attribute setting. + +You may change the System value at any time, although this is not +usually needed. The values supported are set out in the attribute's +description in \appref{ss:attributedescriptions}. + +\subsection{Attributes which Qualify Time Coordinate Systems} + +Time coordinate systems require some additional free parameters to identify +a particular coordinate system from amongst a broader class of related +coordinate systems. For example, all TimeFrames are qualified by the time +scale (that is, the physical process used to define the flow of time), +and some require the position of the observer's clock. + +In AST, these free parameters are represented by additional TimeFrame +attributes, each of which has a default appropriate to (\emph{i.e.}\ defined +by) the setting of the main System attribute. Each of these \emph{qualifying +attributes} may, however, be assigned an explicit value so as to select a +particular coordinate system. Note, it is usually best to assign explicit +values whenever possible rather than relying on defaults. Attribute +should only be left at their default value if you ``don't care'' what +value is used. In certain circumstances (particularly, when aligning two +Frames), a default value for an attribute may be replaced by the value +from another similar Frame. Such value replacement can be prevented by +assigning an explicit value to the attribute, rather than simply relying on +the default. + +The main TimeFrame attributes which qualify the System attribute are: + +\begin{quote} +\begin{description} + +\item[TimeScale]\mbox{}\\ +This specifies the time scale. + +\item[LTOffset]\mbox{}\\ +This specifies the offset from Local Time to UTC in hours (time zones +east of Greenwich have positive values). Note, AST uses the value as +supplied without making any correction for daylight saving. + +\item[TimeOrigin]\mbox{}\\ +This specifies the zero point from which time values are measured, within +the system specified by the System attribute. Thus, a value of zero (the +default) indicates that time values represent absolute times. Non-zero +values may be used to indicate that the TimeFrame represents elapsed time +since the specified origin. + +\end{description} +\end{quote} + +For further details of these attributes you should consult their +descriptions in \appref{ss:attributedescriptions} and for details of +the System settings for which they are relevant, see the description +of the System attribute (also in \appref{ss:attributedescriptions}). + +Note that it does no harm to assign values to qualifying attributes +which are not relevant to the main System or TimeScale value. Any such +values are stored, but are not used unless the System and/or TimeScale +value is later set so that they become relevant. + +\cleardoublepage +\section{\label{ss:cmpframes}Compound Frames (CmpFrames)} + +We now turn to a rather special form of Mapping, the CmpFrame. The +Frames we have considered so far have been atomic, in the sense that +they represent pre-defined elementary physical domains. A CmpFrame, +however, is a compound Frame. In essence, it is a structure for +containing other Frames and its purpose is to allow those Frames +to work together in various combinations while appearing as a single +Object. A CmpFrame's behaviour is therefore not pre-defined, but is +determined by the other Frames it contains (its ``component'' Frames). + +As with compound Mappings, compound Frames can be nested within each +other, forming arbitrarily complex Frames. + +\subsection{Creating a CmpFrame} +A very common use for a CmpFrame within astronomy is to represent a +``spectral cube''. This is a 3-dimensional Frame in which one of the axes +represents position within a spectrum, and the other two axes represent +position on the sky (or some other spatial domain such as the focal plane +of a telescope). As an example, we create such a CmpFrame in which axes +1 and 2 represent Right Ascension and Declination (ICRS), and axis 3 +represents wavelength (these are the default coordinate Systems +represented by a SkyFrame and a SpecFrame respectively): + +c+ +\small +\begin{terminalv} +AstSkyFrame *skyframe; +AstSpecFrame *specframe; +AstCmpFrame *cmpframe; +... +skyframe = astSkyFrame( "" ); +specframe = astSpecFrame( "" ); +cmpframe = astCmpFrame( skyframe, specframe, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER SKYFRAME + INTEGER SPECFRAME + INTEGER CMPFRAME + ... + SKYFRAME = AST_SKYFRAME( ' ', STATUS ) + SPECFRAME = AST_SPECFRAME( ' ', STATUS ) + CMPFRAME = AST_CMPFRAME( SKYFRAME, SPECFRAME, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +If it was desired to make RA and Dec correspond to axes 1 and 3, with +axis 2 being the spectral axis, then the axes of the CmpFrame created +above would need to be permuted as follows: + +c+ +\small +\begin{terminalv} +int perm[ 3 ]; +... + +perm[ 0 ] = 0; +perm[ 1 ] = 2; +perm[ 2 ] = 1; +astPermAxes( cmpframe, perm ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER PERM(3) + ... + + PERM( 1 ) = 1 + PERM( 2 ) = 3 + PERM( 3 ) = 2 + CALL AST_PERMAXES( CMPFRAME, PERM, STATUS ) +\end{terminalv} +\normalsize +f- + +\subsection{The Attributes of a CmpFrame} + +A CmpFrame \emph{is a} Frame and so has all the attributes of a Frame. +The default value for the Domain attribute for a CmpFrame is formed by +concatenating the Domains of the two component Frames, separated by a +minus sign (``-'').\footnote{If both component Frames have blank Domains, +then the default Domain for the CmpFrame is the string ``CMP''.} The (fixed) +value for its System attribute is ``Compound''.\footnote{Any attempt to +change the System value of a CmpFrame is ignored.} A CmpFrame has no +further attributes over and above those common to all Frames. However, +attributes of the two component Frames can be accessed as if they were +attributes of the CmpFrame, as described below. + +Frame attributes which are specific to individual axes (such as Label(2), +Format(1), \emph{etc}) simply mirror the corresponding axes of the +relevant component Frame. That is, if the ``Label(2)'' attribute of a +CmpFrame is accessed, the CmpFrame will forward the access request to the +component Frame which contains axis 2. Thus, default values for axis +attributes will be the same as those provided by the component Frames. + +An axis index can optionally be appended to the name of Frames attributes +which do not normally have such an index (System, Domain, Epoch, Title, +\emph{etc}). If this is done, the access request is forwarded to the +component Frame containing the indicated axis. For instance, if a +CmpFrame contains a SpecFrame and a SkyFrame in that order, and the axes +have not been permuted, then getting the value of attribute ``System'' will +return ``Compound'' as mentioned above (that is, the System value of the +CmpFrame as a whole), whereas getting the value of attribute +``System(1)'' will return ``Spectral''(that is, the System value of the +component Frame containing axis 1 --- the SpecFrame). + +This technique is not limited to attributes common to all Frames. For +instance, the SkyFrame class defines an attribute called Equinox which is +not held by other classes of Frames. To set a value for the Equinox +attribute of the SkyFrame contained within the above CmpFrame, assign the +value to the ``Equinox(2)'' attribute of the CmpFrame. Since the SkyFrame +defines both axes 2 and 3 of the CmpFrame, we could equivalently have set +a value for ``Equinox(3)'' since this would also result in the attribute +access being forwarded to the SkyFrame. + +Finally, if an attribute is not qualified by a axis index, attempts will +be made to access it using each of the CmpFrame axes in turn. Using the +above example of the spectral cube, if an attempt was made to get the +value of attribute ``Equinox'' (with no axis index), each axis in turn +would be used. Since axis 1 is contained within a SpecFrame, the first +attempt would fail since the SpecFrame class does not have an Equinox +attribute. However, the second attempt would succeed because axis 2 is +contained within a SkyFrame which \emph{does} have an Equinox attribute. Thus +the returned attribute value would be that obtained from the SkyFrame +containing axis 2. When getting or testing an attribute value, the +returned value is determined by the \emph{first} axis which recognises +the attribute. When setting an attribute value, \emph{all} axes +which recognises the attribute have the attribute value set to the given +value. Likewise, when clearing an attribute value, all axes +which recognises the attribute have the attribute value cleared. + +\cleardoublepage +\section{\label{ss:introducingconversion}An Introduction to Coordinate System Conversions} + +In this section, we start to look at techniques for converting between +different coordinate systems. At this stage, the tools we have available +are Frames (\secref{ss:frames}), SkyFrames (\secref{ss:skyframes}), +SpecFrames (\secref{ss:specframes}), TimeFrames (\secref{ss:timeframes}) and +various Mappings (\secref{ss:mappings}). These are sufficient to allow us to +begin examining the problem, but more sophisticated approaches will also emerge +later (\secref{ss:framesetconverting}). + +\subsection{\label{ss:convertingskyframes}Converting between Celestial Coordinate Systems} + +We begin by examining how to convert between two celestial coordinate +systems represented by SkyFrames, as this is both an illuminating and +practical example. Consider the problem of converting celestial +coordinates between: + +\begin{enumerate} +\item The old FK4 system, with no E terms, a Besselian epoch of +1958.0 and a Besselian equinox of 1960.0. + +\item An ecliptic coordinate system based on the mean equinox and +ecliptic of Julian epoch 2010.5. +\end{enumerate} + +This example is arbitrary but not completely unrealistic. Unless you +already have expertise with such conversions, you are unlikely to find +it straightforward. + +Using AST, we begin by creating two SkyFrames to represent these +coordinate systems, as follows: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstSkyFrame *skyframe1, *skyframe2; + +... + +skyframe1 = astSkyFrame( "System=FK4-NO-E, Epoch=B1958, Equinox=B1960" ); +skyframe2 = astSkyFrame( "System=Ecliptic, Equinox=J2010.5" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER SKYFRAME1, SKYFRAME2, STATUS + + STATUS = 0 + + ... + + SKYFRAME1 = AST_SKYFRAME( 'System=FK4-NO-E, Epoch=B1958, Equinox=B1960', STATUS ) + SKYFRAME2 = AST_SKYFRAME( 'System=Ecliptic, Equinox=J2010.5', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Note how specifying the coordinate systems consists simply of +initialising the attributes of each SkyFrame appropriately. The next +step is to find a way of converting between these SkyFrames. This is +done using astConvert, as follows: +c- +f+ +Note how specifying the coordinate systems consists simply of +initialising the attributes of each SkyFrame appropriately. The next +step is to find a way of converting between these SkyFrames. This is +done using AST\_CONVERT, as follows: +f- + +c+ +\small +\begin{terminalv} +AstFrameSet *cvt; + +... + +cvt = astConvert( skyframe1, skyframe2, "" ); +if ( cvt == AST__NULL ) { + +} else { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER CVT + + ... + + CVT = AST_CONVERT( SKYFRAME1, SKYFRAME2, ' ', STATUS ) + IF ( CVT .EQ. AST__NULL ) THEN + + ELSE + + END IF +\end{terminalv} +\normalsize +f- + +c+ +The third argument of astConvert is not used here and should be an +empty string. +c- +f+ +The third argument of AST\_CONVERT is not used here and should be a +blank string. +f- + +c+ +astConvert will return a null result, AST\_\_NULL (as defined in the +``ast.h'' header file), if conversion is not possible. In this +example, conversion is possible, so it will return a pointer to a new +Object that describes the conversion. +c- +f+ +AST\_CONVERT will return a null result, AST\_\_NULL (as defined in the +AST\_PAR include file), if conversion is not possible. In this +example, conversion is possible, so it will return a pointer to a new +Object that describes the conversion. +f- + +The Object returned is called a FrameSet. We have not discussed +FrameSets yet (\secref{ss:framesets}), but for the present purposes we +can consider them simply as Objects that can behave both as Mappings +and as Frames. It is the FrameSet's behaviour as a Mapping in which we +are mainly interested here, because the Mapping it implements is the +one we require---\emph{i.e.}\ it converts between the two celestial +coordinate systems (\secref{ss:framesetsfromconvert}). + +c+ +For example, if ``alpha1'' and ``delta1'' are two arrays containing +the longitude and latitude, in radians, of N points on the sky in the +original coordinate system (corresponding to ``skyframe1''), then they +could be converted into the new coordinate system (represented by +``skyframe2'') as follows: +c- +f+ +For example, if ALPHA1 and DELTA1 are two arrays containing the +longitude and latitude, in radians, of N points on the sky in the +original coordinate system (corresponding to SKYFRAME1), then they +could be converted into the new coordinate system (represented by +SKYFRAME2) as follows: +f- + +c+ +\small +\begin{terminalv} +#define N 10 +double alpha1[ N ], delta1[ N ]; +double alpha2[ N ], delta2[ N ]; + +... + +astTran2( cvt, N, alpha1, delta1, 1, alpha2, delta2 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER N + DOUBLE PRECISION ALPHA1( N ), DELTA1( N ) + DOUBLE PRECISION ALPHA2( N ), DELTA2( N ) + + ... + + CALL AST_TRAN2( CVT, N, ALPHA1, DELTA1, .TRUE., ALPHA2, DELTA2, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The new coordinates are returned \emph{via} the ``alpha2'' and +``delta2'' arrays. To transform coordinates in the opposite +direction, we simply invert the 5th (boolean int) argument to +astTran2, as follows: +c- +f+ +The new coordinates are returned \emph{via} the ALPHA2 and DELTA2 +arrays. To transform coordinates in the opposite direction, we simply +invert the 5th (logical) argument to AST\_TRAN2, as follows: +f- + +c+ +\small +\begin{terminalv} +astTran2( cvt, N, alpha2, delta2, 0, alpha1, delta1 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_TRAN2( CVT, N, ALPHA2, DELTA2, .FALSE., ALPHA1, DELTA1, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The FrameSet returned by astConvert also contains information about +the SkyFrames used in the conversion +(\secref{ss:framesetsfromconvert}). As we mentioned above, a FrameSet +may be used as a Frame and in this case it behaves like the +``destination'' Frame used in the conversion (\emph{i.e.}\ like +``skyframe2''). We could therefore use the ``cvt'' FrameSet to +calculate the distance between two points (with coordinates in +radians) in the destination coordinate system, using astDistance: +c- +f+ +The FrameSet returned by AST\_CONVERT also contains information about +the SkyFrames used in the conversion +(\secref{ss:framesetsfromconvert}). As we mentioned above, a FrameSet +may be used as a Frame and in this case it behaves like the +``destination'' Frame used in the conversion (\emph{i.e.}\ like +SKYFRAME2). We could therefore use the CVT FrameSet to calculate the +distance between two points (with coordinates in radians) in the +destination coordinate system, using AST\_DISTANCE: +f- + +c+ +\small +\begin{terminalv} +double distance, point1[ 2 ], point2[ 2 ]; + +... + +distance = astDistance( cvt, point1, point2 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION DISTANCE, POINT1( 2 ), POINT2( 2 ) + + ... + + DISTANCE = AST_DISTANCE( CVT, POINT1, POINT2, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +and the result would be the same as if the ``skyframe2'' SkyFrame had +been used. +c- +f+ +and the result would be the same as if the SKYFRAME2 SkyFrame had been +used. +f- + +Another way to see how the FrameSet produced by astConvert retains +information about the coordinate systems involved is to set its Report +attribute (inherited from the Mapping class) so that it displays the +coordinates before and after conversion (\secref{ss:transforming}): + +c+ +\small +\begin{terminalv} +astSet( cvt, "Report=1" ); +astTran2( cvt, N, alpha1, delta1, 1, alpha2, delta2 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( CVT, 'Report=1', STATUS ) + CALL AST_TRAN2( CVT, N, ALPHA1, DELTA1, .TRUE., ALPHA2, DELTA2, STATUS ) +\end{terminalv} +\normalsize +f- + +The output from this might look like the following: + +\begin{terminalv} +(2:06:03.0, 34:22:39) --> (42.1087, 20.2717) +(2:08:20.6, 35:31:24) --> (43.0197, 21.1705) +(2:10:38.1, 36:40:09) --> (43.9295, 22.0716) +(2:12:55.6, 37:48:55) --> (44.8382, 22.9753) +(2:15:13.1, 38:57:40) --> (45.7459, 23.8814) +(2:17:30.6, 40:06:25) --> (46.6528, 24.7901) +(2:19:48.1, 41:15:11) --> (47.5589, 25.7013) +(2:22:05.6, 42:23:56) --> (48.4644, 26.6149) +(2:24:23.1, 43:32:41) --> (49.3695, 27.5311) +(2:26:40.6, 44:41:27) --> (50.2742, 28.4499) +\end{terminalv} + +Here, we see that the input FK4 equatorial coordinate values (given in +radians) have been formatted automatically in sexagesimal notation +using the conventional hours for right ascension and degrees for +declination. Conversely, the output ecliptic coordinates are shown in +decimal degrees, as is conventional for ecliptic coordinates. Both are +displayed using the default precision of 7 digits.\footnote{The +leading digit is zero and is therefore not seen in this particular +example.} + +c+ +In fact, the ``cvt'' FrameSet has access to all the information in the +original SkyFrames which were passed to astConvert. If you had set a +new Digits attribute value for either of these, the formatting above +would reflect the different precision you requested by displaying a +greater or smaller number of digits. +c- +f+ +In fact, the CVT FrameSet has access to all the information in the +original SkyFrames which were passed to AST\_CONVERT. If you had set a +new Digits attribute value for either of these, the formatting above +would reflect the different precision you requested by displaying a +greater or smaller number of digits. +f- + + +\subsection{\label{ss:convertingspecframes}Converting between Spectral Coordinate Systems} +The principles described in the previous section for converting between +celestial coordinate systems also apply to the task of converting between +spectral coordinate systems. As an example, let's look at how we might +convert between frequency measured in $GHz$ as measured in the rest frame +of the telescope, and radio velocity measured in $km/s$ measured with +respect the kinematic Local Standard of Rest. + +First we create a default SpecFrame, and then set its attributes to +describe the required radio velocity system (this is slightly more +convenient, given the relatively large number of attributes, than +specifying the attribute values in a single string such as would be +passed to the SpecFrame constructor). We then take a copy of this +SpecFrame, and change the attribute values so that the copy describes the +original frequency system (modifying a copy, rather than creating a new +SpecFrame from scratch, avoids the need to specify the epoch, reference +position, \emph{etc} a second time since they are all inherited by the copy): + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstSpecFrame *specframe1, *specframe2; + +... + +specframe1 = astSpecFrame( "" ); +astSet( specframe1, "System=vradio" ); +astSet( specframe1, "Unit=km/s" ); +astSet( specframe1, "Epoch=1996-Oct-2 12:13:56.985" ); +astSet( specframe1, "ObsLon=W155:28:18" ); +astSet( specframe1, "ObsLat=N19:49:34" ); +astSet( specframe1, "RefRA=18:14:50.6" ); +astSet( specframe1, "RefDec=-4:40:49" ); +astSet( specframe1, "RestFreq=230.538 GHz" ); +astSet( specframe1, "StdOfRest=LSRK" ); + +specframe2 = astCopy( specframe1 ); +astSet( specframe1, "System=freq" ); +astSet( specframe1, "Unit=GHz" ); +astSet( specframe1, "StdOfRest=Topocentric" ); + +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER SPECFRAME1, SPECFRAME2, STATUS + + STATUS = 0 + + ... + + SPECFRAME1 = AST_SPECFRAME( ' ', STATUS ) + CALL AST_SETC( SPECFRAME1, 'System=vradio', STATUS ) + CALL AST_SETC( SPECFRAME1, 'Unit=km/s', STATUS ) + CALL AST_SETC( SPECFRAME1, 'Epoch=1996-Oct-2 12:13:56.985', + : STATUS ) + CALL AST_SETC( SPECFRAME1, 'ObsLon=W155:28:18', STATUS ) + CALL AST_SETC( SPECFRAME1, 'ObsLat=N19:49:34', STATUS ) + CALL AST_SETC( SPECFRAME1, 'RefRA=18:14:50.6', STATUS ) + CALL AST_SETC( SPECFRAME1, 'RefDec=-4:40:49', STATUS ) + CALL AST_SETC( SPECFRAME1, 'RestFreq=230.538 GHz', STATUS ) + CALL AST_SETC( SPECFRAME1, 'StdOfRest=LSRK', STATUS ) + + SPECFRAME2 = AST_COPY( SPECFRAME1, STATUS ) + CALL AST_SETC( SPECFRAME1, 'System=freq', STATUS ) + CALL AST_SETC( SPECFRAME1, 'Unit=GHz', STATUS ) + CALL AST_SETC( SPECFRAME1, 'StdOfRest=Topocentric', STATUS ) + +\end{terminalv} +\normalsize +f- + +Note, the fact that a SpecFrame has only a single axis means that we were +able to refer to the Unit attribute without an axis index. The other +attributes are: the time of of observation (Epoch), the geographical +position of the telescope (ObsLat \& ObsLon), the position of the source +on the sky (RefRA \& RefDec), the rest frequency (RestFreq) and the +standard of rest (StdOfRest). + +The next step is to find a way of converting between these SpecFrames. We +use exactly the same code that we did in the previous section where we were +converting between celestial coordinate systems: + +c+ +\small +\begin{terminalv} +AstFrameSet *cvt; + +... + +cvt = astConvert( specframe1, specframe2, "" ); +if ( cvt == AST__NULL ) { + +} else { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER CVT + + ... + + CVT = AST_CONVERT( SPECFRAME1, SPECFRAME2, ' ', STATUS ) + IF ( CVT .EQ. AST__NULL ) THEN + + ELSE + + END IF +\end{terminalv} +\normalsize +f- + +A before, this will give us a FrameSet (assuming conversion is possible, +which should always be the case for our example), and we can use the +FrameSet to convert between the two spectral coordinate systems. We use +c+ +astTran1 in place of astTran2 +c- +f+ +AST\_TRAN1 in place of AST\_TRAN2 +f- +since a SpecFrame has only one axis (unlike a SkyFrame which has two). + +c+ +For example, if ``frq'' is an array containing the observed frequency, in +GHz, of N spectral channels (describe by ``specframe1''), then they +could be converted into the new coordinate system (represented by +``specframe2'') as follows: +c- +f+ +For example, if FRQ is an array containing the observed frequency, in +GHz, of N spectral channels (describe by SPECFRAME1), then they +could be converted into the new coordinate system (represented by +SPECFRAME2) as follows: +f- + +c+ +\small +\begin{terminalv} +#define N 10 +double frq[ N ]; +double vel[ N ]; + +... + +astTran1( cvt, N, frq, 1, vel ); +\end{terminalv} +\normalsize + +The radio velocity values are returned in the ``vel'' array. + +c- +f+ +\small +\begin{terminalv} + INTEGER N + DOUBLE PRECISION FRQ( N ) + DOUBLE PRECISION VEL( N ) + + ... + + CALL AST_TRAN1( CVT, N, FRQ, .TRUE., VEL, STATUS ) +\end{terminalv} +\normalsize + +The radio velocity values are returned in the VEL array. +f- + +\subsection{Converting between Time Coordinate Systems} +All the principles outlined in the previous section about aligning +spectral cocordinate systems (SpecFrames) can be applied directly to the +problem of aligning time coordinate systems (TimeFrames). + +\subsection{\label{ss:convertingpermutedaxes}Handling SkyFrame Axis Permutations} + +c+ +We can illustrate an important point if we swap the axis order of +either SkyFrame in the example above (\secref{ss:convertingskyframes}) +before identifying the conversion. Let's assume we use astPermAxes +(\secref{ss:permutingaxes}) to do this to the second SkyFrame, before +applying astConvert, as follows: +c- +f+ +We can illustrate an important point if we swap the axis order of +either SkyFrame in the example above (\secref{ss:convertingskyframes}) +before identifying the conversion. Let's assume we use AST\_PERMAXES +(\secref{ss:permutingaxes}) to do this to the second SkyFrame, before +applying AST\_CONVERT, as follows: +f- + +c+ +\small +\begin{terminalv} +int perm[ 2 ] = { 2, 1 }; + +... + +astPermAxes( skyframe2, perm ); +cvt = astConvert( skyframe1, skyframe2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER PERM( 2 ) + DATA PERM / 2, 1 / + + ... + + CALL AST_PERMAXES( SKYFRAME2, PERM, STATUS ) + CVT = AST_CONVERT( SKYFRAME1, SKYFRAME2, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +Now, the destination SkyFrame system no longer represents the +coordinate system: + +\begin{quote} +(ecliptic~longitude, ecliptic~latitude) +\end{quote} + +but instead represents the transposed system: + +\begin{quote} +(ecliptic~latitude, ecliptic~longitude) +\end{quote} + +c+ +As a consequence, when we use the FrameSet returned by astConvert to +apply a coordinate transformation, we obtain something like the +following: +c- +f+ +As a consequence, when we use the FrameSet returned by AST\_CONVERT to +apply a coordinate transformation, we obtain something like the +following: +f- + +\begin{terminalv} +(2:06:03.0, 34:22:39) --> (20.2717, 42.1087) +(2:08:20.6, 35:31:24) --> (21.1705, 43.0197) +(2:10:38.1, 36:40:09) --> (22.0716, 43.9295) +(2:12:55.6, 37:48:55) --> (22.9753, 44.8382) +(2:15:13.1, 38:57:40) --> (23.8814, 45.7459) +(2:17:30.6, 40:06:25) --> (24.7901, 46.6528) +(2:19:48.1, 41:15:11) --> (25.7013, 47.5589) +(2:22:05.6, 42:23:56) --> (26.6149, 48.4644) +(2:24:23.1, 43:32:41) --> (27.5311, 49.3695) +(2:26:40.6, 44:41:27) --> (28.4499, 50.2742) +\end{terminalv} + +When compared to the original (\secref{ss:convertingskyframes}), the +output coordinate order has been swapped to compensate for the +different destination SkyFrame axis order. + +c+ +In all, there are four possible axis combinations, corresponding to two +possible axis orders for each of the source and destination SkyFrames, +and astConvert will convert correctly between any of these. +c- +f+ +In all, there are four possible axis combinations, corresponding to two +possible axis orders for each of the source and destination SkyFrames, +and AST\_CONVERT will convert correctly between any of these. +f- +The point to note is that a SkyFrame contains knowledge about how to +convert to and from other SkyFrames. Since its two axes (longitude and +latitude) are distinguishable, the conversion is able to take account +of the axis order. + +If you need to identify the axes of a SkyFrame explicitly, taking into +account any axis permutations, the LatAxis and LonAxis attributes can be +used. These are read-only attributes which give the indices of the +latitude and longitude axes respectively. + +\subsection{\label{ss:convertingframes}Converting Between Frames} + +c+ +Having seen how clever SkyFrames are (\secref{ss:convertingskyframes} +and \secref{ss:convertingpermutedaxes}), we will next examine how dumb +a basic Frame can be in comparison. For example, if we create two +2-dimensional Frames and use astConvert to derive a conversion between +them, as follows: +c- +f+ +Having seen how clever SkyFrames are (\secref{ss:convertingskyframes} +and \secref{ss:convertingpermutedaxes}), we will next examine how dumb +a basic Frame can be in comparison. For example, if we create two +2-dimensional Frames and use AST\_CONVERT to derive a conversion +between them, as follows: +f- + +c+ +\small +\begin{terminalv} +AstFrame *frame1, *frame2; + +... + +frame1 = astFrame( 2, "" ); +frame2 = astFrame( 2, "" ); +cvt = astConvert( frame1, frame2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAME1, FRAME2 + + ... + + FRAME1 = AST_FRAME( 2, ' ', STATUS ) + FRAME2 = AST_FRAME( 2, ' ', STATUS ) + CVT = AST_CONVERT( FRAME1, FRAME2, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +then the coordinate transformation which the ``cvt'' FrameSet performs +will be as follows: +c- +f+ +then the coordinate transformation which the ``cvt'' FrameSet performs +will be as follows: +f- + +\begin{terminalv} +(1, 2) --> (1, 2) +(2, 4) --> (2, 4) +(3, 6) --> (3, 6) +(4, 8) --> (4, 8) +(5, 10) --> (5, 10) +\end{terminalv} + +This is an identity transformation, exactly the same as a UnitMap +(\secref{ss:unitmapexample}). Even if we permute the axis order of our +Frames, as we did above (\secref{ss:convertingpermutedaxes}), we will +fare no better. The conversion between our two basic Frames will +always be an identity transformation. + +The reason for this is that, unlike a SkyFrame, all basic Frames start +life the same and have axes that are indistinguishable. Therefore, +permuting their axes doesn't make them look any different---they still +represent the same coordinate system. +%Actually, this behaviour isn't as dumb as it seems and can actually be +%very useful, as the following example illustrates. +% +%\subsection{Distinguishable and Indistinguishable Axes} +% +%c+ +%Imagine you have two Frames which represent the pixel coordinates of +%two 2-dimensional images. Let's call their axes ``X'' and ``Y''. +%Suppose you now transpose the second image and swap its Frame axes +%(with astPermAxes) to take account of this. +%c- +%f+ +%Imagine you have two Frames which represent the pixel coordinates of +%two 2-dimensional images. Let's call their axes ``X'' and ``Y''. +%Suppose you now transpose the second image and swap its Frame axes +%(with astPermAxes) to take account of this. +%f- +% +%Next, consider what happens if you want to subtract one image from the +%other. If you have a ``subtract'' program that is intelligent and +%tries to align the two images for you, one of two things could happen: +% +%\begin{enumerate} +%c+ +%\item If the axes are distinguishable, when your program invokes +%astConvert it will derive a transformation between the two images +%which swaps the X and Y coordinates (corresponding to the transposition +%you applied to the second image). However, in aligning X-with-X and +%Y-with-Y, this will completely undo the effects of your transposition! +%c- +%f+ +%\item If the axes are distinguishable, when your program invokes +%AST\_CONVERT it will derive a transformation between the two images +%which swaps the X and Y coordinates (corresponding to the transposition +%you applied to the second image). However, in aligning X-with-X and +%Y-with-Y, this will completely undo the effects of your transposition! +%f- +% +%\item If the axes are indistinguishable, the transformation between +%the two images will always be an identity +%(\secref{ss:convertingframes}). Therefore, your program will align +%X-with-Y and Y-with-X, so that you see the effects of your earlier +%transposition of the second image. +%\end{enumerate} +% +%Clearly, if we are considering pixel coordinates, the latter behaviour +%is preferable, since there would be no point in implementing an image +%transposition program if we could never see the effects of it. This +%indicates that a basic Frame, with is indistinguishable axes, is the +%correct type of Object to represent a pixel coordinate system, where +%this behaviour is necessary. +% +%Conversely, the former behaviour would be more useful if the axes we +%were considering were, say, wavelength (in nm) and slit position (in +%mm). In this case, we would expect our ``subtract'' program to +%subtract data at corresponding wavelengths and slit positions, not +%just at corresponding pixels. This case requires distinguishable axes, +%so that corresponding axes in the two images can be matched up, just +%as happens with a SkyFrame (\secref{ss:convertingpermutedaxes}). +% +%Of course, there may also be intermediate cases, where some axes are +%distinguishable and others aren't. + +\subsection{\label{ss:alignmentsystem}The Choice of Alignment System} + +In practice, when AST is asked to find a conversion between two Frames +describing two different coordinate systems on a given physical domain, +it uses an intermediate ``alignment'' system. Thus, when finding a +conversion from system A to system B, AST first finds the Mapping from +system A to some alignment system, system C, and then finds the Mapping +from this system C to the required system B. It finally concatenates +these two Mappings to get the Mapping from system A to system B. + +One advantage of this is that it cuts down the number of conversion +algorithms required. If there are $N$ different Systems which may be used +to describe positions within the Domain, then this approach requires +about $2*N$ conversion algorithms to be written. The alternative approach +of going directly from system A to system B would require about $N*N$ +conversion algorithms. + +In addition, the use of an intermediate alignment system highlights the +nature of the conversion process. What do we mean by saying that a +Mapping ``converts a position in one coordinate system into the +corresponding position in another''? In practice, it means that the input +and output coordinates correspond to the same coordinates \emph{in some +third coordinate system}. The choice of this third coordinate system, the +``alignment'' system, can completely alter the nature of the Mapping. The +Frame class has an attribute called AlignSystem which can be used to +specify the alignment system. + +As an example, consider the case of aligning two spectra calibrated in +radio velocity, but each with a different rest frequency (each spectrum +will be described by a SpecFrame). Since the rest frequencies differ, a +given velocity will correspond to different frequencies in the two +spectra. So when we come to ``align'' these two spectra (that is, find a +Mapping which converts positions in one SpecFrame to the corresponding +positions in the other), we have the choice of aligning the frequencies +or aligning the velocities. Different Mappings will be required to +describe these two forms of alignment. If we set AlignSystem to ``Freq'' +then the returned Mapping will align the frequencies described by the two +SpecFrames. On the other hand, if we set AlignSystem to ``Vradio'' +then the returned Mapping will align the velocities. + +Some choices of alignment system are redundant. For instance, in the +above example, changing the alignment system from frequency to wavelength +has no effect on the returned Mapping: if two spectra are aligned in +frequency they will also be aligned in wavelength (assuming the speed of +light doesn't change). + +The default value for AlignSystem depends on the class of Frame. For a +SpecFrame, the default is wavelength (or equivalently, frequency) +since this is the system in which observations are usually made. The +SpecFrame class also has an attribute called AlignStdOfRest which +allows the standard of rest of the alignment system to be specified. +Similarly, the TimeFrame class has an attribute called AlignTimeScale +which allows the time scale of the alignment system to be specified. +Currently, the SkyFrame uses ICRS as the default for AlignSystem, since +this is a close approximation to an inertial frame of rest. + +\cleardoublepage +\section{\label{ss:framesets}Coordinate System Networks (FrameSets)} + +c+ +We saw in \secref{ss:introducingconversion} how astConvert could be +used to find a Mapping that inter-relates a pair of coordinate systems +represented by Frames. There is a limitation to this, however, in that +it can only be applied to coordinate systems that are inter-related by +suitable conventions. In the case of celestial coordinates, the +relevant conventions are standards set out by the International +Astronomical Union, and others, that define what these coordinate +systems mean. In practice, however, the relationships between many +other coordinate systems are also of practical importance. +c- +f+ +We saw in \secref{ss:introducingconversion} how AST\_CONVERT could be +used to find a Mapping that inter-relates a pair of coordinate systems +represented by Frames. There is a limitation to this, however, in that +it can only be applied to coordinate systems that are inter-related by +suitable conventions. In the case of celestial coordinates, the +relevant conventions are standards set out by the International +Astronomical Union, and others, that define what these coordinate +systems mean. In practice, however, the relationships between many +other coordinate systems are also of practical importance. +f- + +Consider, for example, the focal plane of a telescope upon which an +image of the sky is falling. We could measure positions in this focal +plane in millimetres or, if there were a detector system such as a CCD +present, we could count pixels. We could also use celestial +coordinates of many different kinds. All of these systems are +equivalent in their effectiveness at specifying positions in the focal +plane, but some are more convenient than others for particular +purposes. + +Although we could, in principle, convert between all of these focal +plane coordinate systems, there is no pre-defined convention for doing +so. This is because the conversions required depend on where the +telescope is pointing and how the CCD is mounted in the focal +plane. Clearly, knowledge about this cannot be built into the AST +library and must be supplied in some other way. Note that this is +exactly the same problem as we met in \secref{ss:framedomains} when +discussing the Domain attribute---\emph{i.e.}\ coordinate systems that +apply to different physical domains require that extra information be +supplied before we can convert between them. + +What we need, therefore, is a general way to describe how coordinate +systems are inter-related, so that when there is no convention already +in place, we can define our own. We can then look forward to +converting, say, from pixels into galactic coordinates and {\emph{vice +versa.} In AST, the FrameSet class provides this capability. + +\subsection{The FrameSet Model} + +Consider a coordinate system (call it number 1) which is represented +by a Frame of some kind. Now consider a Mapping which, when applied to +the coordinates in system 1 yields coordinates in another system, +number 2. The Mapping therefore inter-relates coordinate systems 1 and +2. + +Now consider a second Mapping which inter-relates system 1 and a +further coordinate system, number 3. If we wanted to convert +coordinates between systems 2 and 3, we could do so by: + +\begin{enumerate} +\item Applying our first Mapping in reverse, so as to convert between +systems 2 and 1. + +\item Applying the second Mapping, as given, to convert between +systems 1 and 3. +\end{enumerate} + +We are not limited to three coordinate systems, of course. In fact, we +could continue to introduce any number of further coordinate systems, +so long as we have a suitable Mapping for each one which relates it to +one of the Frames already present. Continuing in this way, we can +build up a network in which Frames are inter-related by Mappings in +such a way that there is always a way of converting between any pair +of coordinate systems. + +The FrameSet (Figure~\ref{fig:frameset}) encapsulates these ideas. It +is a network composed of Frames and associated Mappings, in which +there is always exactly one path, \emph{via} Mappings, between any +pair of Frames. Since we assemble FrameSets ourselves, they can be +used to represent any coordinate systems we choose and to set up the +particular relationships between them that we want. + +\subsection{\label{ss:creatingaframeset}Creating a FrameSet} + +Before we can create a FrameSet, we must have a Frame of some kind to +put into it, so let's create a simple one: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstFrame *frame1; + +... + +frame1 = astFrame( 2, "Domain=A" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER FRAME1, STATUS + + STATUS = 0 + + ... + + FRAME1 = AST_FRAME( 2, 'Domain=A', STATUS ) +\end{terminalv} +\normalsize +f- + +We have set this Frame's Domain attribute (\secref{ss:framedomains}) to +A so that it will be distinct from the others we will be using. We can +now create a new FrameSet containing just this Frame, as follows: + +c+ +\small +\begin{terminalv} +AstFrameSet *frameset; + +... + +frameset = astFrameSet( frame1, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAMESET + + ... + + FRAMESET = AST_FRAMESET( FRAME1, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +So far, however, this Frame isn't related to any others. + +\subsection{\label{ss:addingframes}Adding New Frames to a FrameSet} + +We can now add further Frames to the FrameSet created above +(\secref{ss:creatingaframeset}). To do so, we must supply a new Frame +and an associated Mapping that relates it to any of the Frames that +are already present (there is only one present so far). To keep the +example simple, we will just use a ZoomMap that multiplies coordinates +by 10. The required Objects are created as follows: + +c+ +\small +\begin{terminalv} +AstFrame *frame2; +AstMapping *mapping12; + +... + +frame2 = astFrame( 2, "Domain=B" ); +mapping12 = astZoomMap( 2, 10.0, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAME2, MAPPING12 + + ... + + FRAME2 = AST_FRAME( 2, 'Domain=B', STATUS ) + MAPPING12 = AST_ZOOMMAP( 2, 10.0D0, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +To add the new Frame into our FrameSet, we use the astAddFrame +function: +c- +f+ +To add the new Frame into our FrameSet, we use the AST\_ADDFRAME +routine: +f- + +c+ +\small +\begin{terminalv} +astAddFrame( frameset, 1, mapping12, frame2 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_ADDFRAME( FRAMESET, 1, MAPPING12, FRAME2, STATUS ) +\end{terminalv} +\normalsize +f- + +Whenever a Frame is added to a FrameSet, it is assigned an integer +index. This index starts with 1 for the initial Frame used to create +the FrameSet (\secref{ss:creatingaframeset}) and increments by one +every time a new Frame is added. This index is the primary way of +identifying the Frames within a FrameSet. + +When a Frame is added, we also have to specify which of the existing +ones the new Frame is related to. Here, we chose number 1, the only +one present so far, and the new one we added became number 2. + +c+ +Note that a FrameSet does not make copies of the Frames and Mappings +that you insert into it. Instead, it holds pointers to them. This +means that if you retain the original pointers to these Objects and +alter them, you will indirectly be altering the FrameSet's +contents. You can, of course, always use astCopy +(\secref{ss:copyingobjects}) to make a separate copy of any Object if +you need to ensure its independence. +c- +f+ +Note that a FrameSet does not make copies of the Frames and Mappings +that you insert into it. Instead, it holds pointers to them. This +means that if you retain the original pointers to these Objects and +alter them, you will indirectly be altering the FrameSet's +contents. You can, of course, always use AST\_COPY +(\secref{ss:copyingobjects}) to make a separate copy of any Object if +you need to ensure its independence. +f- + +c+ +We could also add a third Frame into our FrameSet, this time defining +a coordinate system which is reached by multiplying the original +coordinates (of ``frame1'') by 5: +c- +f+ +We could also add a third Frame into our FrameSet, this time defining +a coordinate system which is reached by multiplying the original +coordinates (of FRAME1) by 5: +f- + +c+ +\small +\begin{terminalv} +astAddFrame( frameset, 1, astZoomMap( 2, 5.0, "" ), astFrame( 2, "Domain=C" ) ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_ADDFRAME( FRAMESET, 1, + : AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ), + : AST_FRAME( 2, 'Domain=C', STATUS ), + : STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, we have avoided storing unnecessary pointer values by using +function invocations directly as arguments for astAddFrame. This +assumes that we are using astBegin and astEnd (\secref{ss:contexts}) to +ensure that Objects are correctly deleted when no longer required. +c- +f+ +Here, we have avoided storing unnecessary pointer values by using +function invocations directly as arguments for AST\_ADDFRAME. This +assumes that we are using AST\_BEGIN and AST\_END +(\secref{ss:contexts}) to ensure that Objects are correctly deleted +when no longer required. +f- + + Our example FrameSet now contains three Frames and two Mappings with + the arrangement shown in Figure~\ref{fig:fsexample}. + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/fsexample} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/fsexample} +f- + \caption[An example FrameSet.]{An example FrameSet, in which Frames~2 and 3 are related to + Frame~1 by multiplying its coordinates by factors of 10 and 5 + respectively. The FrameSet's Base attribute has the value 1 and its + Current attribute has the value 3. The transformation performed when + the FrameSet is used as a Mapping (\emph{i.e.}\ from its base to + its current Frame) is shown in bold.} + \label{fig:fsexample} + \end{center} + \end{figure} + The total number of Frames is given by its read-only Nframe attribute. + +\subsection{\label{ss:baseandcurrent}The Base and Current Frames} + +At all times, one of the Frames in a FrameSet is designated to be its +\emph{base} Frame and one to be its \emph{current} Frame +(Figure~\ref{fig:fsexample}). These Frames are identified by two +integer FrameSet attributes, Base and Current, which hold the indices +of the nominated Frames within the FrameSet. + +The existence of the base and current Frames reflects an important +application of FrameSets, which is to attach coordinate systems to +entities such as data arrays, data files, plotting surfaces (for +graphics), \emph{etc.} In this context, the base Frame represents the +``native'' coordinate system of the attached entity---for example, the +pixel coordinates of an image or the intrinsic coordinates of a +plotting surface. The other Frames within the FrameSet represent +alternative coordinate systems which may also be used to refer to +positions within that entity. The current Frame represents the +particular coordinate system which is currently selected for use. For +instance, if an image were being displayed, you would aim to label it +with coordinates corresponding to the current Frame. In order to see a +different coordinate system, a software user would arrange for a +different Frame to be made current. + +The choice of base and current Frames may be changed at any time, +simply by assigning new values to the FrameSet's Base and Current +attributes. For example, to make the Frame with index 3 become the +current Frame, you could use: + +c+ +\small +\begin{terminalv} +astSetI( frameset, "Current", 3 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SETI( FRAMESET, 'Current', 3, STATUS ) +\end{terminalv} +\normalsize +f- + +You can nominate the same Frame to be both the base and current Frame +if you wish. +\label{ss:baseandcurrentdefault} + +By default (\emph{i.e.}\ if the Base or Current attribute is un-set), +the first Frame added to a FrameSet becomes its base Frame and the +last one added becomes its current Frame.\footnote{Although this is +reversed if the FrameSet's Invert attribute is non-zero.} Whenever a +new Frame is added to a FrameSet, the Current attribute is modified so +that the new Frame becomes the current one. This behaviour is +reflected in the state of the example FrameSet in +Figure~\ref{fig:fsexample}. + +\subsection{\label{ss:astbaseandastcurrent}Referring to the Base and Current Frames} + +c+ +It is often necessary to refer to the base and current Frames +(\secref{ss:baseandcurrent}) within a FrameSet, but it can be +cumbersome having to obtain their indices from the Base and Current +attributes on each occasion. To make this easier, two macros, +AST\_\_BASE and AST\_\_CURRENT, are defined in the ``ast.h'' header +file and may be used to represent the indices of the base and current +Frames respectively. They may be used whenever a Frame index is +required. +c- +f+ +It is often necessary to refer to the base and current Frames +(\secref{ss:baseandcurrent}) within a FrameSet, but it can be +cumbersome having to obtain their indices from the Base and Current +attributes on each occasion. To make this easier, two parameter +constants, AST\_\_BASE and AST\_\_CURRENT, are defined in the AST\_PAR +include file and may be used to represent the indices of the base and +current Frames respectively. They may be used whenever a Frame index +is required. +f- + +For example, when adding a new Frame to a FrameSet +(\secref{ss:addingframes}), you could use the following to indicate +that the new Frame is related to the existing current Frame, whatever +its index happens to be: + +c+ +\small +\begin{terminalv} +AstFrame *frame; +AstMapping *mapping; + +... + +astAddFrame( frameset, AST__CURRENT, mapping, frame ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAME, MAPPING + + ... + + CALL AST_ADDFRAME( FRAMESET, AST__CURRENT, MAPPING, FRAME, STATUS ) +\end{terminalv} +\normalsize +f- + +Of course, the Frame you added would then become the new current +Frame. + +\subsection{\label{ss:framesetasmapping}Using a FrameSet as a Mapping} + +The FrameSet class inherits properties and behaviour from the Frame +class (\secref{ss:frames}) and, in turn, from the Mapping class +(\secref{ss:mappings}). Its behaviour when used as a Mapping is +particularly important. + +c+ +Consider, for instance, passing a FrameSet pointer to a coordinate +transformation function such as astTran2: +c- +f+ +Consider, for instance, passing a FrameSet pointer to a coordinate +transformation routine such as AST\_TRAN2: +f- + +c+ +\small +\begin{terminalv} +#define N 10 +double xin[ N ], yin[ N ], xout[ N ], yout[ N ]; + +... + +astTran2( frameset, N, xin, yin, 1, xout, yout ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER N + DOUBLE PRECISION XIN( N ), YIN( N ) + DOUBLE PRECISION XOUT( N ), YOUT( N ) + + ... + + CALL AST_TRAN2( FRAMESET, N, XIN, YIN, .TRUE., XOUT, YOUT, STATUS ) +\end{terminalv} +\normalsize +f- + +The coordinate transformation applied by this FrameSet would be the +one which converts between its base and current Frames. Using the +FrameSet in Figure~\ref{fig:fsexample}, for example, the coordinates +would be multiplied by a factor of 5. If we instead requested the +FrameSet's inverse transformation, we would be transforming from its +current Frame to its base Frame, so our example FrameSet would then +multiply by a factor of 0.2. + +Whenever the choice of base and current Frames changes, the +transformations which a FrameSet performs when used as a Mapping also +change to reflect this. The Nin and Nout attributes may also change in +consequence, because they are determined by the numbers of axes in the +FrameSet's base and current Frames respectively. These numbers need +not necessarily be equal, of course. + +c+ +Like any Mapping, a FrameSet may also be inverted by changing the +boolean sense of its Invert attribute, \emph{e.g.}\ using astInvert +(\secref{ss:invertingmappings}). If this is happens, the values of the +FrameSet's Base and Current attributes are interchanged, along with +its Nin and Nout attributes, so that its base and current Frames swap +places. When used as a Mapping, the FrameSet will therefore perform +the inverse transformation to that which it performed previously. +c- +f+ +Like any Mapping, a FrameSet may also be inverted by changing the +boolean sense of its Invert attribute, \emph{e.g.}\ using AST\_INVERT +(\secref{ss:invertingmappings}). If this is happens, the values of the +FrameSet's Base and Current attributes are interchanged, along with +its Nin and Nout attributes, so that its base and current Frames swap +places. When used as a Mapping, the FrameSet will therefore perform +the inverse transformation to that which it performed previously. +f- + +To summarise, a FrameSet may be used exactly like any other Mapping +which inter-relates the coordinate systems described by its base and +current Frames. + +\subsection{\label{ss:extractingamapping}Extracting a Mapping from a FrameSet} + +Although it is very convenient to use a FrameSet when a Mapping is +required (\secref{ss:framesetasmapping}), a FrameSet necessarily +contains additional information and sometimes this might cause +inefficiency or confusion. For example, if you wanted to use a +Mapping contained in one FrameSet and insert it into another, it would +probably not be efficient to insert the whole of the first FrameSet +into the second one, although it would work. + +c+ +In such a situation, the astGetMapping function allows you to extract +a Mapping from a FrameSet. You do this by specifying the two Frames +which the Mapping should inter-relate using their indices within the +FrameSet. For example: +c- +f+ +In such a situation, the AST\_GETMAPPING function allows you to +extract a Mapping from a FrameSet. You do this by specifying the two +Frames which the Mapping should inter-relate using their indices +within the FrameSet. For example: +f- + +c+ +\small +\begin{terminalv} +map = astGetMapping( frameset, 2, 3 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + MAP = AST_GETMAPPING( FRAMESET, 2, 3, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +would return a pointer to a Mapping that converted between Frames~2 +and 3 in the FrameSet. Its inverse transformation would then convert +in the opposite direction, \emph{i.e.}\ between Frames~3 and 2. Note +that this Mapping might not be independent of the Mappings contained +within the FrameSet---\emph{i.e.}\ they may share sub-Objects---so +astCopy should be used to make a copy if you need to guarantee +independence (\secref{ss:copyingobjects}). +c- +f+ +would return a pointer to a Mapping that converted between Frames~2 +and 3 in the FrameSet. Its inverse transformation would then convert +in the opposite direction, \emph{i.e.}\ between Frames~3 and 2. Note +that this Mapping might not be independent of the Mappings contained +within the FrameSet---\emph{i.e.}\ they may share sub-Objects---so +AST\_COPY should be used to make a copy if you need to guarantee +independence (\secref{ss:copyingobjects}). +f- + +c+ +Very often, the Mapping returned by astGetMapping will be a compound +Mapping, or CmpMap (\secref{ss:cmpmaps}). This reflects the fact that +conversion between the two Frames may need to be done \emph{via} an +intermediate coordinate system so that several stages may be involved. +You can, however, easily simplify this Mapping (where this is possible) +by using the astSimplify function (\secref{ss:simplifyingcmpmaps}) and +this is recommended if you plan to use it for transforming a large +amount of data. +c- +f+ +Very often, the Mapping returned by AST\_GETMAPPING will be a compound +Mapping, or CmpMap (\secref{ss:cmpmaps}). This reflects the fact that +conversion between the two Frames may need to be done \emph{via} an +intermediate coordinate system so that several stages may be involved. +You can, however, easily simplify this Mapping (where this is possible) +by using the AST\_SIMPLIFY function (\secref{ss:simplifyingcmpmaps}) +and this is recommended if you plan to use it for transforming a large +amount of data. +f- + +\subsection{\label{ss:framesetasframe}Using a FrameSet as a Frame} + +A FrameSet can also be used as a Frame, in which capacity it almost +always behaves as if its current Frame had been used instead. For +example, if you request the Title attribute of a FrameSet using: + +c+ +\small +\begin{terminalv} +const char *title; + +... + +title = astGetC( frameset, "Title" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 80 ) TITLE + + ... + + TITLE = AST_GETC( FRAMESET, 'Title', STATUS ) +\end{terminalv} +\normalsize +f- + +the result will be the Title of the current Frame, or a suitable +default if the current Frame's Title attribute is un-set. The same +also applies to other attribute operations---\emph{i.e.}\ setting, +clearing and testing attributes. Most attributes shared by both +Frames and FrameSets behave in this way, such as Naxes, Label(axis), +Format(axis), \emph{etc.} There are, however, a few exceptions: + +\begin{quote} +\begin{description} +\item[Class]\mbox{}\\ +Has the value ``FrameSet''. + +\item[ID]\mbox{}\\ +Identifies the particular FrameSet (not its current Frame). + +\item[Nin]\mbox{}\\ +Equals the number of axes in the FrameSet's base Frame. + +\item[Invert]\mbox{}\\ +Is independent of any of the Objects within the FrameSet. + +\item[Nobject]\mbox{}\\ +Counts the number of active FrameSets. + +\item[RefCount]\mbox{}\\ +Counts the number of active pointers to the FrameSet (not to its +current Frame). +\end{description} +\end{quote} + +Note that the set of attributes possessed by a FrameSet can vary, +depending on the nature of its current Frame. For example, if the +current Frame is a SkyFrame (\secref{ss:skyframes}), then the FrameSet +will acquire an Equinox attribute from it which can be set, enquired, +\emph{etc.} However, if the current Frame is changed to be a basic +Frame, which does not have an Equinox attribute, then this attribute +will be absent from the FrameSet as well. Any attempt to reference it +will then result in an error. + +\subsection{Extracting a Frame from a FrameSet} + +c+ +Although a FrameSet may be used in place of its current Frame in most +situations, it is sometimes convenient to have direct access to a +specified Frame within it. This may be obtained using the astGetFrame +function, as follows: +c- +f+ +Although a FrameSet may be used in place of its current Frame in most +situations, it is sometimes convenient to have direct access to a +specified Frame within it. This may be obtained using the +AST\_GETFRAME function, as follows: +f- + +c+ +\small +\begin{terminalv} +frame = astGetFrame( frameset, AST__BASE ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + FRAME = AST_GETFRAME( FRAMESET, AST__BASE, STATUS ) +\end{terminalv} +\normalsize +f- + +This would return a pointer (not a copy) to the base Frame within the +FrameSet. Note the use of AST\_\_BASE +(\secref{ss:astbaseandastcurrent}) as shorthand for the value of the +FrameSet's Base attribute, which gives the base Frame's index. + +\subsection{Removing a Frame from a FrameSet} + +c+ +Removing a Frame from a FrameSet is straightforward and is performed +using the astRemoveFrame function. You identify the Frame you wish to +remove in the usual way, by giving its index within the FrameSet. For +example, the following would remove the Frame with index 1: +c- +f+ +Removing a Frame from a FrameSet is straightforward and is performed +using the AST\_REMOVEFRAME routine. You identify the Frame you wish to +remove in the usual way, by giving its index within the FrameSet. For +example, the following would remove the Frame with index 1: +f- + +c+ +\small +\begin{terminalv} +astRemoveFrame( frameset, 1 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_REMOVEFRAME( FRAMESET, 1, STATUS ); +\end{terminalv} +\normalsize +f- + +The only restriction is that you cannot remove the last remaining +Frame because a FrameSet must always contain at least one Frame. When +a Frame is removed, the Frames which follow it are re-numbered +(\emph{i.e.}\ their indices are reduced by one) so as to preserve the +sequence of consecutive Frame indices. The FrameSet's Nframe +attribute is also decremented. + +c+ +If appropriate, astRemoveFrame will modify the FrameSet's Base and/or +Current attributes so that they continue to identify the same Frames +as previously. If either the base or current Frame is removed, +however, the corresponding attribute will become un-set, so that it +reverts to its default value (\secref{ss:baseandcurrentdefault}) and +therefore identifies an alternative Frame. +c- +f+ +If appropriate, AST\_REMOVEFRAME will modify the FrameSet's Base +and/or Current attributes so that they continue to identify the same +Frames as previously. If either the base or current Frame is removed, +however, the corresponding attribute will become un-set, so that it +reverts to its default value (\secref{ss:baseandcurrentdefault}) and +therefore identifies an alternative Frame. +f- + +Note that it is quite permissible to remove any Frame from a FrameSet, +even although other Frames may appear to depend on it. For example, in +Figure~\ref{fig:fsexample}, if Frame~1 were removed, the correct +relationship between Frames~2 and 3 would still be preserved, although +they would be re-numbered as Frames~1 and 2. + +\cleardoublepage +\section{\label{ss:fshigher}Higher Level Operations on FrameSets} + +c+ +\subsection{\label{ss:framesetsfromconvert}Creating FrameSets with astConvert} +c- +f+ +\subsection{\label{ss:framesetsfromconvert}Creating FrameSets with AST\_CONVERT} +f- + +c+ +Before considering the important subject of using FrameSets to convert +between coordinate systems (\secref{ss:framesetconverting}), let us +return briefly to reconsider the output generated by astConvert. We +used this function earlier (\secref{ss:introducingconversion}), when +converting between the coordinate systems represented by various kinds +of Frame, and indicated that it returns a FrameSet to represent the +coordinate conversion it identifies. We are now in a position to +examine the structure of this FrameSet. +c- +f+ +Before considering the important subject of using FrameSets to convert +between coordinate systems (\secref{ss:framesetconverting}), let us +return briefly to reconsider the output generated by AST\_CONVERT. We +used this function earlier (\secref{ss:introducingconversion}), when +converting between the coordinate systems represented by various kinds +of Frame, and indicated that it returns a FrameSet to represent the +coordinate conversion it identifies. We are now in a position to +examine the structure of this FrameSet. +f- + +Take our earlier example (\secref{ss:convertingskyframes}) of +converting between the celestial coordinate systems represented by two +SkyFrames: + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstFrameSet *cvt; +AstSkyFrame *skyframe1, *skyframe2; + +... + +skyframe1 = astSkyFrame( "System=FK4-NO-E, Epoch=B1958, Equinox=B1960" ); +skyframe2 = astSkyFrame( "System=Ecliptic, Equinox=J2010.5" ); + +cvt = astConvert( skyframe1, skyframe2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER SKYFRAME1, SKYFRAME2, STATUS + + STATUS = 0 + + ... + + SKYFRAME1 = AST_SKYFRAME( 'System=FK4-NO-E, Epoch=B1958, Equinox=B1960', STATUS ) + SKYFRAME2 = AST_SKYFRAME( 'System=Ecliptic, Equinox=J2010.5', STATUS ) + + CVT = AST_CONVERT( SKYFRAME1, SKYFRAME2, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ + This will produce a pointer, ``cvt'', to the FrameSet shown in + Figure~\ref{fig:fsconvert}. +c- +f+ + This will produce a pointer, CVT, to the FrameSet shown in + Figure~\ref{fig:fsconvert}. +f- + \begin{figure}[bhtp] + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/fsconvert} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/fsconvert} +f- +c+ + \caption[FrameSet produced when converting between two SkyFrames.]{The FrameSet produced when astConvert is used to convert +c- +f+ + \caption[FrameSet produced when converting between two SkyFrames.]{The FrameSet produced when AST\_CONVERT is used to convert +f- + between the coordinate systems represented by two SkyFrames. The + source SkyFrame becomes the base Frame, while the destination SkyFrame + becomes the current Frame. The Mapping between them implements the + required conversion.} + \label{fig:fsconvert} + \end{center} + \end{figure} + +c+ +As can be seen, this FrameSet contains just two Frames. The source +Frame supplied to astConvert becomes its base Frame, while the +destination Frame becomes its current Frame. (The FrameSet, of course, +simply holds pointers to these Frames, rather than making copies.) The +Mapping which relates the base Frame to the current Frame is the one +which implements the required conversion. +c- +f+ +As can be seen, this FrameSet contains just two Frames. The source +Frame supplied to AST\_CONVERT becomes its base Frame, while the +destination Frame becomes its current Frame. (The FrameSet, of course, +simply holds pointers to these Frames, rather than making copies.) The +Mapping which relates the base Frame to the current Frame is the one +which implements the required conversion. +f- + +c+ +As we noted earlier (\secref{ss:convertingskyframes}), the FrameSet +returned by astConvert may be used both as a Mapping and as a Frame to +perform most of the functions you are likely to need. However, the +Mapping may be extracted for use on its own if necessary, using +astGetMapping (\secref{ss:extractingamapping}), for example: +c- +f+ +As we noted earlier (\secref{ss:convertingskyframes}), the FrameSet +returned by AST\_CONVERT may be used both as a Mapping and as a Frame +to perform most of the functions you are likely to need. However, the +Mapping may be extracted for use on its own if necessary, using +AST\_GETMAPPING (\secref{ss:extractingamapping}), for example: +f- + +c+ +\small +\begin{terminalv} +AstMapping *mapping; + +... + +mapping = astGetMapping( cvt, AST__BASE, AST__CURRENT ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER MAPPING + + ... + + MAPPING = AST_GETMAPPING( CVT, AST__BASE, AST__CURRENT, STATUS ) +\end{terminalv} +\normalsize +f- + +\subsection{\label{ss:framesetconverting}Converting between FrameSet Coordinate Systems} + + We now consider the process of converting between the coordinate + systems represented by two FrameSets. This is a most important + operation, as a subsequent example (\secref{ss:registeringimages}) + will show, and is illustrated in Figure~\ref{fig:fsalign}. + \begin{figure} + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/fsalign} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/fsalign} +f- + \caption[Conversion between two FrameSets is performed by establishin a link between a pair of Frames, one from each FrameSet.]{Conversion + between two FrameSets is performed by establishing + a link between a pair of Frames, one from each FrameSet. If conversion + between these two Frames is possible, then a route for converting + between the current Frames of both FrameSets can also be found. In + practice, there may be many ways of pairing Frames to find the + ``missing link'', so the Frames' Domain attribute may be used to + narrow the choice.} + \label{fig:fsalign} + \end{center} + \end{figure} + +c+ +Recalling (\secref{ss:framesetasframe}) that a FrameSet will behave +like its current Frame when necessary, conversion between two +FrameSets is performed using astConvert +(\secref{ss:convertingskyframes}), but supplying pointers to FrameSets +instead of Frames. The effect of this is to convert between the +coordinate systems represented by the current Frames of each FrameSet: +c- +f+ +Recalling (\secref{ss:framesetasframe}) that a FrameSet will behave +like its current Frame when necessary, conversion between two +FrameSets is performed using AST\_CONVERT +(\secref{ss:convertingskyframes}), but supplying pointers to FrameSets +instead of Frames. The effect of this is to convert between the +coordinate systems represented by the current Frames of each FrameSet: +f- + +c+ +\small +\begin{terminalv} +AstFrameSet *frameseta, *framesetb; + +... + +cvt = astConvert( frameseta, framesetb, "SKY" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAMESETA, FRAMESETB + + ... + + CVT = AST_CONVERT( FRAMESETA, FRAMESETB, 'SKY', STATUS ) +\end{terminalv} +\normalsize +f- + +When using FrameSets, we are presented with considerably more +conversion options than when using Frames alone. This is because each +current Frame is related to all the other Frames in its respective +FrameSet. Therefore, if we can establish a link between any pair of +Frames, one from each FrameSet, we can form a complete conversion path +between the two current Frames (Figure~\ref{fig:fsalign}). + +This expanded range of options is, of course, precisely the +intention. By connecting Frames together within a FrameSet, we have +extended the range of coordinate systems that can be reached from any +one of them. We are therefore no longer restricted to converting +between Frames with the same Domain value (\secref{ss:framedomains}), +but can go \emph{via} a range of intermediate coordinate systems in +order to make the connection we require. Transformation between +different domains has therefore become possible because, in assembling +the FrameSets, we provided the additional information needed to +inter-relate them. + +It is important to appreciate, however, that the choice of ``missing +link'' is crucial in determining the conversion that results. +Although each FrameSet may be perfectly self-consistent internally, +this does not mean that all conversion paths through the combined +network of Mappings are equivalent. Quite the contrary in fact: +everything depends on where the inter-connecting link between the two +FrameSets is made. In practice, there may be a large number of +possible pairings of Frames and hence of possible links. Other factors +must therefore be used to restrict the choice. These are: + +\begin{enumerate} +\item Not every possible pairing of Frames is legitimate. For example, +you cannot convert directly between a basic Frame and a SkyFrame which +belong to different classes, so such pairings will be ignored. + +\item In a similar way, you cannot convert directly between Frames +with different Domain values (\secref{ss:framedomains}). If the Domain +attribute is used consistently (typically only one Frame in each +FrameSet will have a particular Domain value), then this further +restricts the choice. + +c+ +\item The third argument of astConvert may then be used to specify +c- +f+ +\item The third argument of AST\_CONVERT may then be used to specify +f- +explicitly which Domain value the paired Frames should have. You may +also supply a comma-separated list of preferences here (see below). + +\item If the above steps fail to uniquely identify the link, then the +first suitable pairing of Frames is used, so that any ambiguity is +resolved by the order in which Frames are considered for pairing (see +c+ +the description of the astConvert function in +c- +f+ +the description of the AST\_CONVERT function in +f- +\appref{ss:functiondescriptions} for details of the search +order).\footnote{If you find that how this ambiguity is resolved +actually makes a difference to the conversion that results, then you +have probably constructed a FrameSet which lacks internal +self-consistency. For example, you might have two Frames representing +indistinguishable coordinate systems but inter-related by a non-null +Mapping.} +\end{enumerate} + +In the example above we supplied the string ``SKY'' as the third +c+ +argument of astConvert. This constitutes a request that a pair of +Frames with +c- +f+ +argument of AST\_CONVERT. This constitutes a request that a pair of +Frames with +f- +the Domain value SKY (\emph{i.e.}\ representing celestial coordinate +systems) should be used to inter-relate the two FrameSets. Note that +this does not specify which celestial coordinate system to use, but is +a general request that the two FrameSets be inter-related using +coordinates on the celestial sphere. + +Of course, it may be that this request cannot be met because there may +not be a celestial coordinate system in both FrameSets. If this is +likely to happen, we can supply a list of preferences, or a +\emph{domain search path}, +c+ +as the third argument to astConvert, such as +c- +f+ +as the third argument to AST\_CONVERT, such as +f- +the following: + +c+ +\small +\begin{terminalv} +cvt = astConvert( frameseta, framesetb, "SKY,PIXEL,GRID," ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CVT = AST_CONVERT( FRAMESETA, FRAMESETB, 'SKY,PIXEL,GRID,', STATUS ) +\end{terminalv} +\normalsize +f- + +Now, if the two FrameSets cannot be inter-related using the SKY domain, +c+ +astConvert will attempt to use the PIXEL domain instead. If this +c- +f+ +AST\_CONVERT will attempt to use the PIXEL domain instead. If this +f- +also fails, it will try the GRID domain. A blank field in the domain +search path (here indicated by the final comma) allows any Domain +value to be used. This can be employed as a last resort when all else +has failed. + +c+ +If astConvert succeeds in identifying a conversion, it will return a +c- +f+ +If astConvert succeeds in identifying a conversion, it will return a +f- +pointer to a FrameSet (\secref{ss:framesetsfromconvert}) in which the +source and destination Frames are inter-connected by the required +Mapping. In this case, of course, these Frames will be the current +Frames of the two FrameSets, but in all other respects the returned +FrameSet is the same as when converting between Frames. + +c+ +Very importantly, however, astConvert may modify the FrameSets you are +converting between. It does this, in order to indicate which pairing +of Frames was used to inter-relate them, by changing the Base +attribute for each FrameSet so that the Frame used in the pairing +becomes its base Frame (\secref{ss:baseandcurrent}). +c- +f+ +Very importantly, however, AST\_CONVERT may modify the FrameSets you +are converting between. It does this, in order to indicate which +pairing of Frames was used to inter-relate them, by changing the Base +attribute for each FrameSet so that the Frame used in the pairing +becomes its base Frame (\secref{ss:baseandcurrent}). +f- + +c+ +Finally, note that astConvert may also be used to convert between a +FrameSet and a Frame, or \emph{vice versa}. If a pointer to a Frame is +supplied for either the first or second argument, it will behave like +a FrameSet containing only a single Frame. +c- +f+ +Finally, note that AST\_CONVERT may also be used to convert between a +FrameSet and a Frame, or \emph{vice versa}. If a pointer to a Frame is +supplied for either the first or second argument, it will behave like +a FrameSet containing only a single Frame. +f- + +\subsection{\label{ss:registeringimages}Example---Registering Two Images} + +Consider two images which have been calibrated by attaching FrameSets +to them, such that the base Frame of each FrameSet corresponds to the +raw data grid coordinates of each image (the GRID domain of +\secref{ss:domainconventions}). Suppose, also, that these FrameSets +contain an unknown number of other Frames, representing alternative +world coordinate systems. What we wish to do is register these two +images, such that we can transform from a position in the data grid of +one into the corresponding position in the data grid of the other. +This is a very practical example because images will typically be +calibrated using FrameSets in precisely this way. + +c+ +The first step will probably involve making a copy of both FrameSets +(using astCopy---\secref{ss:copyingobjects}), since we will be +modifying them. Let ``frameseta'' and ``framesetb'' be pointers to +these copies. Since we want to convert between the base Frames of +these FrameSets (\emph{i.e.}\ their data grid coordinates), the next +step is to make these Frames current. This is simply done by inverting +both FrameSets, which interchanges their base and current +Frames. astInvert will perform this task: +c- +f+ +The first step will probably involve making a copy of both FrameSets +(using AST\_COPY---\secref{ss:copyingobjects}), since we will be +modifying them. Let ``frameseta'' and ``framesetb'' be pointers to +these copies. Since we want to convert between the base Frames of +these FrameSets (\emph{i.e.}\ their data grid coordinates), the next +step is to make these Frames current. This is simply done by inverting +both FrameSets, which interchanges their base and current +Frames. astInvert will perform this task: +f- + +c+ +\small +\begin{terminalv} +astInvert( frameseta ); +astInvert( framesetb ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_INVERT( FRAMESETA, STATUS ) + CALL AST_INVERT( FRAMESETB, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +To identify the required conversion, we now use astConvert, supplying +a suitable domain search path with which we would like our two images +to be registered: +c- +f+ +To identify the required conversion, we now use AST\_CONVERT, +supplying a suitable domain search path with which we would like our +two images to be registered: +f- + +c+ +\small +\begin{terminalv} +cvt = astConvert( frameseta, framesetb, "SKY,PIXEL,GRID" ); +if ( cvt == AST__NULL ) { + +} else { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CVT = AST_CONVERT( FRAMESETA, FRAMESETB, 'SKY,PIXEL,GRID', STATUS ) + IF ( CVT .EQ. AST__NULL ) THEN + + ELSE + + END IF +\end{terminalv} +\normalsize +f- + +The effects of this are: + +\begin{enumerate} +c+ +\item astConvert first attempts to register the two images on the +c- +f+ +\item AST\_CONVERT first attempts to register the two images on the +f- +celestial sphere (\emph{i.e.}\ using the SKY domain). To do this, it +searches for a celestial coordinate system, although not necessarily +the same one, attached to each image. If it finds a suitable pair of +coordinate systems, it then registers the images by matching +corresponding positions on the sky. + +c+ +\item If this fails, astConvert next tries to match positions in the +c- +f+ +\item If this fails, AST\_CONVERT next tries to match positions in the +f- +PIXEL domain (\secref{ss:framedomains}). If it succeeds, the two +images will then be registered so that their corresponding pixel +positions correspond. If the PIXEL domain is offset from the data grid +(as typically happens in data reduction systems which implement a +``pixel origin''), then this will be correctly accounted for. + +\item If this also fails, the GRID domain is finally used. This will +result in image registration by matching corresponding points in the +data grids used by both images. This means they will be +aligned so that the first element their data arrays correspond. + +c+ +\item If all of the above fail, astConvert will return the value +c- +f+ +\item If all of the above fail, AST\_CONVERT will return the value +f- +AST\_\_NULL. Otherwise a pointer to a FrameSet will be returned. +\end{enumerate} + +c+ +The resulting ``cvt'' FrameSet may then be used directly +(\secref{ss:convertingskyframes}) to convert between positions in the +data grid of the first image and corresponding positions in the data +grid of the second image. +c- +f+ +The resulting CVT FrameSet may then be used directly +(\secref{ss:convertingskyframes}) to convert between positions in the +data grid of the first image and corresponding positions in the data +grid of the second image. +f- + +To determine which domain was used to achieve registration, +we can use the fact that the Base attribute of each FrameSet is set by +c+ +astConvert to indicate which intermediate Frames were used. We +c- +f+ +AST\_CONVERT to indicate which intermediate Frames were used. We +f- +can therefore simply invert either FrameSet (to make its base Frame +become the current one) and then enquire the Domain value: + +c+ +\small +\begin{terminalv} +const char *domain; + +... + +astInvert( frameseta ); +domain = astGetC( frameseta, "Domain" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 20 ) DOMAIN + + ... + + + CALL AST_INVERT( FRAMESETA, STATUS ) + DOMAIN = AST_GETC( FRAMESETA, 'Domain', STATUS ) +\end{terminalv} +\normalsize +f- + +If conversion was successful, the result will be one of the strings +``SKY'', ``PIXEL'' or ``GRID''. + +\subsection{\label{ss:remapframe}Re-Defining a FrameSet Coordinate System} + +As discussed earlier (\secref{ss:baseandcurrent}), an important +application of a FrameSet is to allow coordinate system information to +be attached to entities such as images in order to calibrate them. In +addition, one of the main objectives of AST is to simplify the +propagation of such information through successive stages of data +processing, so that it remains consistent with the associated image +data. + +In such a situation, the FrameSet's base Frame would correspond with +the image's data grid coordinates and its other Frames (if any) with +the various alternative world coordinate systems associated with the +image. If the data processing being performed does not change the +relationship between the image's data grid coordinates and any of the +associated world coordinate systems, then propagation of the WCS +information is straightforward and simply involves copying the +FrameSet associated with the image. + +If any of these relationships change, however, then corresponding +changes must be made to the way Frames within the FrameSet are +inter-related. By far the most common case occurs when the image +undergoes some geometrical transformation resulting in ``re-gridding'' +on to another data grid, but the same principles can be applied to any +re-definition of a coordinate system. + +To pursue the re-gridding example, we would need to modify our +FrameSet to account for the fact that the image's data grid coordinate +system (corresponding to the FrameSet's base Frame) has +changed. Looking at the steps needed in detail, we might proceed as +follows: + +\begin{enumerate} +\item Create a Mapping which represents the relationship between the +original data grid coordinate system and the new one. + +c+ +\item Obtain a Frame to represent the new data grid coordinate system +(we could re-use the original base Frame here, using astGetFrame to +obtain a pointer to it). +c- +f+ +\item Obtain a Frame to represent the new data grid coordinate system +(we could re-use the original base Frame here, using AST\_GETFRAME to +obtain a pointer to it). +f- + +\item Add the new Frame to the FrameSet, related to the original base +Frame by the new Mapping. This Frame now represents the new data grid +coordinate system and is correctly related to all the other Frames +present.\footnote{This is because any transformation to or from this +new Frame must go \emph{via} the base Frame representing the original +data grid coordinate system, which we assume was correctly related to +all the other Frames present.} + +\item Remove the original base Frame (representing the old data grid +coordinate system). + +\item Make the new Frame the base Frame and restore the original +current Frame. +\end{enumerate} + + The effect of these steps is to change the relationship between the + base Frame and all the other Frames present. It is as if a new Mapping + has been interposed between the Frame we want to alter and all the + other Frames within the FrameSet (Figure~\ref{fig:fsremap}). + \begin{figure}[hbtp] + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/fsremap} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/fsremap} +f- +\caption[Interposing a Mapping into a FrameSet]{The effect +c+ + of astRemapFrame is to interpose a Mapping between +c- +f+ + of AST\_REMAPFRAME is to interpose a Mapping between +f- + a nominated Frame within a FrameSet and the remaining contents of the + FrameSet. This effectively ``re-defines'' the coordinate system + represented by the affected Frame. It may be used to compensate (say) + for geometrical changes made to an associated image. The + inter-relationships between all the other Frames within the FrameSet + remain unchanged.} + \label{fig:fsremap} + \end{center} + \end{figure} + +c+ +Performing the steps above is rather lengthy, however, so the +astRemapFrame function is provided to perform all of these operations +in one go. A practical example of its use is given below +(\secref{ss:wcsprocessingexample}). +c- +f+ +Performing the steps above is rather lengthy, however, so the +AST\_REMAPFRAME function is provided to perform all of these +operations in one go. A practical example of its use is given below +(\secref{ss:wcsprocessingexample}). +f- + +\subsection{\label{ss:wcsprocessingexample}Example---Binning an Image} + +c+ +As an example of using astRemapFrame, consider a case where the pixels +of a 2-dimensional image have been binned 2$\times$2, so as to reduce +the image size by a factor of two in each dimension. We must now +modify the associated FrameSet to reflect this change to the +image. Much the same process would be needed for any other geometrical +change the image might undergo. +c- +f+ +As an example of using AST\_REMAPFRAME, consider a case where the +pixels of a 2-dimensional image have been binned 2$\times$2, so as to +reduce the image size by a factor of two in each dimension. We must +now modify the associated FrameSet to reflect this change to the +image. Much the same process would be needed for any other geometrical +change the image might undergo. +f- + +We first set up a Mapping (a WinMap in this case) which relates the +data grid coordinates in the original image to those in the new one: + +c+ +\small +\begin{terminalv} +AstWinMap *winmap; +double ina[ 2 ] = { 0.5, 0.5 }; +double inb[ 2 ] = { 2.5, 2.5 }; +double outa[ 2 ] = { 0.5, 0.5 }; +double outb[ 2 ] = { 1.5, 1.5 }; + +... + +winmap = astWinMap( 2, ina, inb, outa, outb, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER WINMAP + DOUBLE PRECISION INA( 2 ), INB( 2 ), OUTA( 2 ), OUTB( 2 ) + DATA INA / 0.5D0, 0.5D0 / + DATA INB / 2.5D0, 2.5D0 / + DATA OUTA / 0.5D0, 0.5D0 / + DATA OUTB / 1.5DO, 1.5DO / + + ... + + WINMAP = AST_WINMAP( 2, INA, INB, OUTA, OUTB, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, we have simply set up arrays containing the data grid +coordinates of the bottom left and top right corners of the first +element in the output image (``outa'' and ``outb'') and the +corresponding coordinates in the input image (``ina'' and +``inb''). astWinMap then creates a WinMap which performs the required +transformation. We do not need to know the size of the image. +c- +f+ +Here, we have simply set up arrays containing the data grid +coordinates of the bottom left and top right corners of the first +element in the output image (OUTA and OUTB) and the corresponding +coordinates in the input image (INA and INB). AST\_WINMAP then creates +a WinMap which performs the required transformation. We do not need to +know the size of the image. +f- + +c+ +We can then pass this WinMap to astRemapFrame. This modifies the +relationship between our FrameSet's base Frame and the other Frames in +the FrameSet, so that the base Frame represents the data grid +coordinate system of the new image rather than the old one: +c- +f+ +We can then pass this WinMap to AST\_REMAPFRAME. This modifies the +relationship between our FrameSet's base Frame and the other Frames in +the FrameSet, so that the base Frame represents the data grid +coordinate system of the new image rather than the old one: +f- + +c+ +\small +\begin{terminalv} +AstFrameSet *frameset; + +... + +astRemapFrame( frameset, AST__BASE, winmap ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER FRAMESET + + ... + + CALL AST_REMAPFRAME( FRAMESET, AST__BASE, WINMAP, STATUS ) +\end{terminalv} +\normalsize +f- + +Any other coordinate systems described by the FrameSet, no matter how +many of these there might be, are now correctly associated with the +new image. + +\subsection{\label{ss:framesetintegrity}Maintaining the Integrity of FrameSets} + +When constructing a FrameSet, you are provided with a framework into +which you can place any combination of Frames and Mappings that you +wish. There are relatively few constraints on this process and no +checks are performed to see whether the FrameSet you construct makes +physical sense. It is quite possible, for example, to construct a +FrameSet containing two identical SkyFrames which are inter-related by +a non-unit Mapping. AST will not object if you do this, but it makes +no sense, because applying a non-unit Mapping to any set of celestial +coordinates cannot yield positions that are still in the original +coordinate system. If you use such a FrameSet to perform coordinate +conversions, you are likely to get unpredictable results because the +information in the FrameSet is corrupt. + +It is, of course, your responsibility as a programmer to ensure the +validity of any information which you insert into a +FrameSet. Normally, this is straightforward and simply consists of +formulating your problem correctly (a diagram can often help to +clarify how coordinate systems are inter-related) and writing the +appropriate bug-free code to construct the FrameSet. However, once you +start to modify an existing FrameSet, there are new opportunities for +corrupting it! + +c+ +Consider, for example, a FrameSet whose current Frame is a +SkyFrame. We can set a new value for this SkyFrame's Equinox attribute +simply by using astSet on the FrameSet, as follows: +c- +f+ +Consider, for example, a FrameSet whose current Frame is a +SkyFrame. We can set a new value for this SkyFrame's Equinox attribute +simply by using AST\_SET on the FrameSet, as follows: +f- + +c+ +\small +\begin{terminalv} +astSet( frameset, "Equinox=J2010" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( FRAMESET, 'Equinox=J2010', STATUS ) +\end{terminalv} +\normalsize +f- + +The effect of this will be to change the celestial coordinate system +which the current Frame represents. You can see, however, that this +has the potential to make the FrameSet corrupt unless corresponding +changes are also made to the Mapping which relates this SkyFrame to +the other Frames within the FrameSet. In fact, it is a general rule +that any change to a FrameSet which affects its current Frame can +potentially require corresponding changes to the FrameSet's Mappings +in order to maintain its overall integrity. + +c+ +Fortunately, once you have stored valid information in a FrameSet, AST +will look after these details for you automatically, so that the +FrameSet's integrity is maintained. In the example above, it would do +this by appropriately re-mapping the current Frame (as if +astRemapFrame had been used---\secref{ss:remapframe}) in response to +the use of astSet. One way of illustrating this process is as follows: +c- +f+ +Fortunately, once you have stored valid information in a FrameSet, AST +will look after these details for you automatically, so that the +FrameSet's integrity is maintained. In the example above, it would do +this by appropriately re-mapping the current Frame (as if +AST\_REMAPFRAME had been used---\secref{ss:remapframe}) in response to +the use of AST\_SET. One way of illustrating this process is as +follows: +f- + +c+ +\small +\begin{terminalv} +AstSkyFrame *skyframe; + +... + +skyframe = astSkyFrame( "" ); +frameSet = astFrameSet( skyframe ); +astAddFrame( frameset, 1, astUnitMap( 2, "" ), skyframe ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER SKYFRAME + + ... + + SKYFRAME = AST_SKYFRAME( ' ', STATUS ) + FRAMESET = AST_FRAMESET( SKYFRAME, STATUS ) + CALL AST_ADDFRAME( FRAMESET, 1, AST_UNITMAP( 2, ' ', STATUS ) + : SKYFRAME, STATUS ) +\end{terminalv} +\normalsize +f- + +This constructs a trivial FrameSet whose base and current Frames are +both the same SkyFrame connected by a UnitMap. You can think of this +as a ``pipe'' connecting two coordinate systems. At present, these two +systems represent identical ICRS coordinates, so the FrameSet +implements a unit Mapping. We can change the coordinate system on the +current end of this pipe as follows: + +c+ +\small +\begin{terminalv} +astSet( frameset, "System=Ecliptic, Equinox=J2010" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( FRAMESET, 'System=Ecliptic, Equinox=J2010', STATUS ) +\end{terminalv} +\normalsize +f- + +and the Mapping which the FrameSet implements would change +accordingly. To change the coordinate system on the base end of the +pipe, we might use: + +c+ +\small +\begin{terminalv} +astInvert( frameset ); +astSet( frameset, "System=Galactic" ); +astInvert( frameset ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_INVERT( FRAMESET ) + CALL AST_SET( FRAMESET, 'System=Galactic', STATUS ) + CALL AST_INVERT( FRAMESET ) +\end{terminalv} +\normalsize +f- + +The FrameSet would then convert between galactic and ecliptic +coordinates. + +c+ +Note that astSet is not the only function which has this effect: +astClear behaves similarly, as also does astPermAxes +(\secref{ss:permutingaxes}). If you need to circumvent this mechanism +for any reason, this can be done by going behind the scenes and +obtaining a pointer directly to the Frame you wish to modify. Consider +the following, for example: +c- +f+ +Note that AST\_SET is not the only function which has this effect: +AST\_CLEAR behaves similarly, as also does AST\_PERMAXES +(\secref{ss:permutingaxes}). If you need to circumvent this mechanism +for any reason, this can be done by going behind the scenes and +obtaining a pointer directly to the Frame you wish to modify. Consider +the following, for example: +f- + +c+ +\small +\begin{terminalv} +skyframe = astGetFrame( frameset, AST__CURRENT ); +astSet( skyframe, "Equinox=J2010" ); +skyframe = astAnnul( skyframe ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SKYFRAME = AST_GETFRAME( FRAMESET, AST__CURRENT, STATUS ) + CALL AST_SET( SKYFRAME, 'Equinox=J2010', STATUS ) + CALL AST_ANNUL( SKYFRAME, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, astSet is applied to the SkyFrame pointer rather than the +FrameSet pointer, so the usual checks on FrameSet integrity do not +occur. The SkyFrame's Equinox attribute will therefore be modified +without any corresponding change to the FrameSet's Mappings. In this +case you must take responsibility yourself for maintaining the +FrameSet's integrity, perhaps through appropriate use of +astRemapFrame. +c- +f+ +Here, AST\_SET is applied to the SkyFrame pointer rather than the +FrameSet pointer, so the usual checks on FrameSet integrity do not +occur. The SkyFrame's Equinox attribute will therefore be modified +without any corresponding change to the FrameSet's Mappings. In this +case you must take responsibility yourself for maintaining the +FrameSet's integrity, perhaps through appropriate use of +AST\_REMAPFRAME. +f- + +\subsection{Merging FrameSets} + + As well as adding individual Frames to a FrameSet + (\secref{ss:addingframes}), it is also possible to add complete sets of + inter-related Frames which are contained within another + FrameSet. This, of course, corresponds to the process of merging two + FrameSets (Figure~\ref{fig:fsmerge}). + \begin{figure}[hbtp] + \begin{center} +c+ + \includegraphics[width=0.7\textwidth]{sun211_figures/fsmerge} +c- +f+ + \includegraphics[width=0.7\textwidth]{sun210_figures/fsmerge} +f- + \caption[Two FrameSets in the process of being merged.]{Two FrameSets in the process of being merged using +c+ + astAddFrame. FrameSet~B is being added to FrameSet~A by supplying a +c- +f+ + AST\_ADDFRAME. FrameSet~B is being added to FrameSet~A by supplying a +f- + new Mapping which inter-relates a nominated Frame in A (here number~1) + and the current Frame of B. In the merged FrameSet, the Frames + contributed by B will be re-numbered to become Frames~4, 5 and 6. The + base Frame will remain unchanged, but the current Frame of B becomes + the new current Frame. Note that FrameSet~B itself is not + altered by this process.} + \label{fig:fsmerge} + \end{center} + \end{figure} + + + +c+ +This process is performed by adding one FrameSet to another using +astAddFrame, in much the same manner as when adding a new Frame to an +existing FrameSet (\secref{ss:addingframes}). It is simply a matter of +providing a FrameSet pointer, instead of a Frame pointer, for the 4th +argument. In performing the merger you must, as usual, supply a +Mapping, but in this case the Mapping should relate the current Frame +of the FrameSet being added to one of the Frames already present. For +example, you might perform the merger shown in +Figure~\ref{fig:fsmerge} as follows: +c- +f+ +This process is performed by adding one FrameSet to another using +AST\_ADDFRAME, in much the same manner as when adding a new Frame to +an existing FrameSet (\secref{ss:addingframes}). It is simply a matter +of providing a FrameSet pointer, instead of a Frame pointer, for the +4th argument. In performing the merger you must, as usual, supply a +Mapping, but in this case the Mapping should relate the current Frame +of the FrameSet being added to one of the Frames already present. For +example, you might perform the merger shown in +Figure~\ref{fig:fsmerge} as follows: +f- + +c+ +\small +\begin{terminalv} +AstMapping *mapping; + +... + +astAddFrame( frameseta, 1, mapping, framesetb ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER MAPPING + + ... + + CALL AST_ADDFRAME( FRAMESETA, 1, MAPPING, FRAMESETB, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The Frames acquired by ``frameseta'' from the FrameSet being added +(``framesetb'') are re-numbered so that they retain their original +order and follow on consecutively after the Frames that were already +present, whose indices remain unchanged. The base Frame of +``frameseta'' remains unchanged, but the current Frame of +``framesetb'' becomes its new current Frame. All the +inter-relationships between Frames in both FrameSets remain in place +and are preserved in the merged FrameSet. +c- +f+ +The Frames acquired by FRAMESETA from the FrameSet being added +(FRAMESETB) are re-numbered so that they retain their original order +and follow on consecutively after the Frames that were already +present, whose indices remain unchanged. The base Frame of FRAMESETA +remains unchanged, but the current Frame of FRAMESETB becomes its new +current Frame. All the inter-relationships between Frames in both +FrameSets remain in place and are preserved in the merged FrameSet. +f- + +c+ +Note that while this process modifies the first FrameSet +(``frameseta''), it leaves the original contents of the one being +added (``framesetb'') unchanged. +c- +f+ +Note that while this process modifies the first FrameSet (FRAMESETA), +it leaves the original contents of the one being added (FRAMESETB) +unchanged. +f- + +%\cleardoublepage +%\section{\label{ss:searching}TBW - Searching for Coordinate Systems} + +\cleardoublepage +\section{\label{ss:channels}Saving and Restoring Objects (Channels)} + +Facilities are provided by the AST library for performing input and +output (I/O) with any kind of Object. This means it is possible +to write any Object into various external representations for +storage, and then to read these representations back in, so as to +restore the original Object. Typically, an Object would be written by +one program and read back in by another. + +We refer to ``external representations'' in the plural because AST is +designed to function independently of any particular data storage +system. This means that Objects may need converting into a number of +different external representations in order to be compatible with +(say) the astronomical data storage system in which they will reside. + +In this section, we discuss the basic I/O facilities which support +external representations based on a textual format referred to as the AST +``native format''. These are implemented using a new kind of Object---a +Channel. We will examine later how to use other representations, based on +an XML format or on the use of FITS headers, for storing Objects. These +are implemented using more specialised forms of Channel called XmlChan +(\secref{ss:xmlchan}) and FitsChan (\secref{ss:nativefits}). + +\subsection{The Channel Model} + +c+ +The best way to start thinking about a Channel is like a C file +stream, and to think of the process of creating a Channel as that +of opening a file and obtaining a FILE pointer. Subsequently, you can +read and write Objects \emph{via} the Channel. +c- +f+ +The best way to start thinking about a Channel is like a Fortran I/O +unit (also represented by an integer, as it happens) and to think of +the process of creating a Channel as the combined process of +allocating a unit number and attaching it to a file by opening the +file on that unit. Subsequently, you can read and write Objects +\emph{via} the Channel. +f- + +c+ +This analogy is not quite perfect, however, because a Channel has, in +principle, two ``files'' attached to it. One is used when reading, and +the other when writing. These are termed the Channel's \emph{source} +and \emph{sink} respectively. In practice, the source and sink may +both be the same, in which case the analogy with the C file stream is +correct, but this need not always be so. It is not necessarily so with +the basic Channel, as we will now see (\secref{ss:creatingachannel}). +c- +f+ +This analogy is not quite perfect, however, because a Channel has, in +principle, two ``files'' attached to it. One is used when reading, and +the other when writing. These are termed the Channel's \emph{source} +and \emph{sink} respectively. In practice, the source and sink may +both be the same, in which case the analogy with the Fortran I/O unit +is correct, but this need not always be so. It is not necessarily so +with the basic Channel, as we will now see +(\secref{ss:creatingachannel}). +f- + +\subsection{\label{ss:creatingachannel}Creating a Channel} + +c+ +The process of creating a Channel is straightforward. As you +might expect, it uses the constructor function astChannel: + +\small +\begin{terminalv} +#include "ast.h" +AstChannel *channel; + +... + +channel = astChannel( NULL, NULL, "" ); +\end{terminalv} +\normalsize + +The first two arguments to astChannel specify the external source and +sink that the Channel is to use. There arguments are pointers to C +functions and we will examine their use in more detail later +(\secref{ss:channelsource} and \secref{ss:channelsink}). +c- +f+ +The process of creating a Channel is straightforward. As you +might expect, it uses the constructor function AST\_CHANNEL: + +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER CHANNEL, STATUS + + STATUS = 0 + + ... + + CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, ' ', STATUS ) +\end{terminalv} +\normalsize + +The first two arguments to AST\_CHANNEL specify the external source +and sink that the Channel is to use. There arguments are the names of +Fortran subroutines and we will examine their use in more detail later +(\secref{ss:channelsource} and \secref{ss:channelsink}). +f- + +c+ +In this very simple example we have supplied NULL pointers for both +the source and sink functions. This requests the default behaviour, +which means that textual input will be read from the program's +standard input stream (typically, this means your keyboard) while +textual output will go to the standard output stream (typically +appearing on your screen). On UNIX systems, of course, either of these +streams can easily be redirected to files. This default behaviour can be +changed by assigning values to the Channel's SinkFile and/or SourceFile +attributes. These attributes specify the paths to text files that are to +be used in place of the standard input and output streams. +c- +f+ +In this very simple example we have supplied the name of the null +routine AST\_NULL\footnote{Note that AST\_NULL (one underscore) is a +routine name and is distinct from AST\_\_NULL (two underscores) which +is a null Object pointer. Since we are passing the name of one +routine to another routine, AST\_NULL would normally have to appear in +a Fortran EXTERNAL statement. In this example, however, a suitable +statement is already present in the AST\_PAR include file.} for both +the source and sink routines. This requests the default behaviour, +which means that textual input will be read from the program's +standard input stream (typically, this means your keyboard) while +textual output will go to the standard output stream (typically +appearing on your screen). On UNIX systems, of course, either of these +streams can easily be redirected to files. +f- + +\subsection{\label{ss:writingtoachannel}Writing Objects to a Channel} + +c+ +The process of saving Objects is very straightforward. You can +simply write any Object to a Channel using the astWrite +function, as follows: + +\small +\begin{terminalv} +int nobj; +AstObject *object; + +... + +nobj = astWrite( channel, object ); +\end{terminalv} +\normalsize +c- +f+ +The process of saving Objects is very straightforward. You can +simply write any Object to a Channel using the AST\_WRITE +function, as follows: + +\small +\begin{terminalv} + INTEGER NOBJ, OBJECT + + ... + + NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) +\end{terminalv} +\normalsize +f- + +The effect of this will be to produce a textual description of the +Object which will appear, by default, on your program's standard +output stream. Any class of Object may be converted into text in this +way. + +c+ +astWrite returns a count of the number of Objects written. Usually, +this will be one, unless the Object supplied cannot be +represented. With a basic Channel all Objects can be represented, so a +value of one will always be returned unless there has been an +error. We will see later, however, that more specialised forms of +Channel may impose restrictions on the kind of Object you can write +(\secref{ss:foreignfitslimitations}). In such cases, astWrite may +return zero to indicate that the Object was not acceptable. +c- +f+ +AST\_WRITE returns a count of the number of Objects written. Usually, +this will be one, unless the Object supplied cannot be +represented. With a basic Channel all Objects can be represented, so a +value of one will always be returned unless there has been an +error. We will see later, however, that more specialised forms of +Channel may impose restrictions on the kind of Object you can write +(\secref{ss:foreignfitslimitations}). In such cases, AST\_WRITE may +return zero to indicate that the Object was not acceptable. +f- + +\subsection{\label{ss:readingfromachannel}Reading Objects from a Channel} + +Before discussing the format of the output produced above +(\secref{ss:writingtoachannel}), let us consider how to read it back, +so as to reconstruct the original Object. Naturally, we would first +need to save the output in a file. We can do that either by using the +SinkFile attribute, or (on UNIX systems), by redirecting standard output +to a file using a shell command like: + +\small +\begin{terminalv} +program1 >file +\end{terminalv} +\normalsize + +c+ +Within a subsequent program, we can read this Object back in by +using the astRead function, having first created a suitable +Channel: + +\small +\begin{terminalv} +object = astRead( channel ); +\end{terminalv} +\normalsize +c- +f+ +Within a subsequent program, we can read this Object back in by +using the AST\_READ function, having first created a suitable +Channel: + +\small +\begin{terminalv} + OBJECT = AST_READ( CHANNEL, STATUS ) +\end{terminalv} +\normalsize +f- + +By default, this function will read from the standard input stream +(the default source for a basic Channel), so we would need to ensure +that our second program reads its input from the file in which the +Object description is stored. On UNIX systems, we could again use a +shell redirection command such as: + +\small +\begin{terminalv} +program2 $ family of functions: + +\small +\begin{terminalv} +int ok; + +... + +ok = astIsAFrame( object ); +\end{terminalv} +\normalsize +c- +f+ +The pointer returned by AST\_READ (\secref{ss:readingfromachannel}) +could identify any class of Object---this is determined entirely by +the external data being read. If it is necessary to test for a +particular class (say a Frame), this may be done as follows using the +appropriate member of the AST\_ISA$<$CLASS$>$ family of functions: + +\small +\begin{terminalv} + LOGICAL OK + + ... + + OK = AST_ISAFRAME( OBJECT, STATUS ) +\end{terminalv} +\normalsize +f- + +Note, however, that this will accept any Frame, so would be equally +happy with a basic Frame or a SkyFrame. An alternative validation +strategy would be to obtain the value of the Object's Class attribute +and then test this character string, as follows: + +c+ +\small +\begin{terminalv} +#include + +... + +ok = !strcmp( astGetC( object, "Class" ), "Frame" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + OK = AST_GETC( OBJECT, 'Class', STATUS ) .EQ. 'Frame' +\end{terminalv} +\normalsize +f- + +This would only accept a basic Frame and would reject a SkyFrame. + +\subsection{Storing an ID String with an Object} + +Occasionally, you may want to store a number of Objects and later +retrieve them and use each for a different purpose. If the Objects are +of the same class, you cannot use the Class attribute to distinguish +them when you read them back +(\emph{c.f.}~\secref{ss:validatinginput}). Although relying on the +order in which they are stored is a possible solution, this becomes +complicated if some of the Objects are optional and may not always be +present. It also makes extending your data format in future more +difficult. + +To help with this, every AST Object has an ID attribute and an Ident +attribute, both of which allows you, in effect, to attach a textual +identification label to it. You simply set the ID or Ident attribute before +writing the Object: + +c+ +\small +\begin{terminalv} +astSet( object, "ID=Calibration" ); +nobj = astWrite( channel, object ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( OBJECT, 'ID=Calibration', STATUS ) + NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) +\end{terminalv} +\normalsize +f- + +You can then test its value after you read the Object back: + +c+ +\small +\begin{terminalv} +object = astRead( channel ); +if ( !strcmp( astGetC( object, "ID" ), "Calibration" ) ) { + +} else { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + OBJECT = AST_READ( CHANNEL, STATUS ) + IF ( AST_GETC( OBJECT, 'ID', STATUS ) .EQ. 'Calibration' ) THEN + + ELSE + + END IF +\end{terminalv} +\normalsize +f- + +The only difference between the ID and Ident attributes is that the ID +attribute is unique to a particular Object and is lost if, for example, +you make a copy of the Object. The Ident attrubute, on the other hand, is +transferred to the new Object when a copy is made. Consequently, it is +safest to set the value of the ID attribute immediately before you +perform the write. + +\subsection{\label{ss:textualoutputformat}The Textual Output Format} + +Let us now examine the format of the textual output produced by +writing an Object to a basic Channel +(\secref{ss:writingtoachannel}). To give a concrete example, suppose +the Object in question is a SkyFrame, written out as follows: + +c+ +\small +\begin{terminalv} +AstSkyFrame *skyframe; + +... + +nobj = astWrite( channel, skyframe ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER SKYFRAME + + ... + + NOBJ = AST_WRITE( CHANNEL, SKYFRAME, STATUS ) +\end{terminalv} +\normalsize +f- + +The output should then look like the following: + +\small +\begin{terminalv} + Begin SkyFrame # Description of celestial coordinate system +# Title = "FK4 Equatorial Coordinates, no E-terms, Mean Equinox B1950.0, Epoch B1958.0" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Lbl1 = "Right Ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction (hint) + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + System = "FK4-NO-E" # Celestial coordinate system type + Epoch = 1958 # Besselian epoch of observation +# Eqnox = 1950 # Besselian epoch of mean equinox + End SkyFrame +\end{terminalv} +\normalsize + +c+ +You will notice that this output is designed both for a human reader, +in that it is formatted, and also to be read back by a computer in +order to reconstruct the SkyFrame. In fact, this is precisely the way +that astShow works (\secref{ss:displayingobjects}), this function being +roughly equivalent to the following use of a Channel: + +\small +\begin{terminalv} +channel = astChannel( NULL, NULL, "" ); +(void) astWrite( channel, object ); +channel = astAnnul( channel ); +\end{terminalv} +\normalsize +c- +f+ +You will notice that this output is designed both for a human reader, +in that it is formatted, and also to be read back by a computer in +order to reconstruct the SkyFrame. In fact, this is precisely the way +that AST\_SHOW works (\secref{ss:displayingobjects}), this routine +being roughly equivalent to the following use of a Channel: + +\small +\begin{terminalv} + CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, ' ', STATUS ) + NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) + CALL AST_ANNUL( CHANNEL, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Some lines of the output start with a ``\verb?#?'' comment character, +which turns the rest of the line into a comment. These lines will be +ignored when read back in by astRead. They typically contain default +values, or values that can be derived in some way from the other data +present, so that they do not actually need to be stored in order to +reconstruct the original Object. They are provided purely for human +information. The same comment character is also used to append +explanatory comments to most output lines. +c- +f+ +Some lines of the output start with a ``\verb?#?'' comment character, +which turns the rest of the line into a comment. These lines will be +ignored when read back in by AST\_READ. They typically contain +default values, or values that can be derived in some way from the +other data present, so that they do not actually need to be stored in +order to reconstruct the original Object. They are provided purely for +human information. The same comment character is also used to append +explanatory comments to most output lines. +f- + +It is not sensible to attempt a complete description of this output +format because every class of Object is potentially different and each +can define how its own data should be represented. However, there are +some basic rules, which mean that the following common features will +usually be present: + +\begin{enumerate} +\item Each Object is delimited by matching ``Begin'' and ``End'' +lines, which also identify the class of Object involved. + +\item Within each Object description, data values are represented +by a simple ``keyword~$=$~value'' syntax, with one value to a line. + +\item Lines beginning ``IsA'' are used to mark the divisions between +data belonging to different levels in the class hierarchy +(\appref{ss:classhierarchy}). Thus, ``IsA~Frame'' marks the end of data +associated with the Frame class and the start of data associated with +some derived class (a SkyFrame in the above example). ``IsA'' lines +may be omitted if associated data values are absent and no confusion +arises. + +\item Objects may contain other Objects as data. This is +indicated by an absent value, with the description of the data +Object following on subsequent lines. + +\item Indentation is used to clarify the overall structure. +\end{enumerate} + +Beyond these general principles, the best guide to what a particular +line of output represents will generally be the comment which +accompanies it together with a general knowledge of the class of +Object being described. + +\subsection{\label{ss:controllingchanneloutput}Controlling the Amount of Output} + +c+ +It is not always necessary for the output from astWrite +(\secref{ss:writingtoachannel}) to be human-readable, so a Channel has +attributes that allow the amount of detail in the output to be +controlled. +c- +f+ +It is not always necessary for the output from AST\_WRITE +(\secref{ss:writingtoachannel}) to be human-readable, so a Channel has +attributes that allow the amount of detail in the output to be +controlled. +f- + +The first of these is the integer attribute Full, which controls the +extent to which optional, commented out, output lines are produced. By +default, Full is zero, and this results in the standard style of +output (\secref{ss:textualoutputformat}) where default values that may +be helpful to humans are included. To suppress these optional lines, +Full should be set to $-$1. This is most conveniently done when the +Channel is created, so that: + +c+ +\small +\begin{terminalv} +channel = astChannel( NULL, NULL, "Full=-1" ); +(void) astWrite( channel, skyframe ); +channel = astAnnul( channel ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, 'Full=-1', STATUS ) + NOBJ = AST_WRITE( CHANNEL, SKYFRAME, STATUS ) + CALL AST_ANNUL( CHANNEL, STATUS ) +\end{terminalv} +\normalsize +f- + +would result in output containing only the essential information, such +as: + +\small +\begin{terminalv} + Begin SkyFrame # Description of celestial coordinate system + Naxes = 2 # Number of coordinate axes + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis + End SkyAxis + IsA Frame # Coordinate system description + System = "FK4-NO-E" # Celestial coordinate system type + Epoch = 1958 # Besselian epoch of observation + End SkyFrame +\end{terminalv} +\normalsize + +In contrast, setting Full to $+$1 will result in additional output +lines which will reveal every last detail of the Object's +construction. Often this will be rather more than you want, especially +for more complex Objects, but it can sometimes help when debugging +programs. This is how a SkyFrame appears at this level of detail: + +\small +\begin{terminalv} + Begin SkyFrame # Description of celestial coordinate system +# RefCnt = 1 # Count of active Object pointers +# Nobj = 1 # Count of active Objects in same class + IsA Object # Astrometry Object +# Nin = 2 # Number of input coordinates +# Nout = 2 # Number of output coordinates +# Invert = 0 # Mapping not inverted +# Fwd = 1 # Forward transformation defined +# Inv = 1 # Inverse transformation defined +# Report = 0 # Don't report coordinate transformations + IsA Mapping # Mapping between coordinate systems +# Title = "FK4 Equatorial Coordinates, no E-terms, Mean Equinox B1950.0, Epoch B1958.0" # Title of coordinate system + Naxes = 2 # Number of coordinate axes +# Domain = "SKY" # Coordinate system domain +# Lbl1 = "Right Ascension" # Label for axis 1 +# Lbl2 = "Declination" # Label for axis 2 +# Sym1 = "RA" # Symbol for axis 1 +# Sym2 = "Dec" # Symbol for axis 2 +# Uni1 = "hh:mm:ss.s" # Units for axis 1 +# Uni2 = "ddd:mm:ss" # Units for axis 2 +# Dig1 = 7 # Individual precision for axis 1 +# Dig2 = 7 # Individual precision for axis 2 +# Digits = 7 # Default formatting precision +# Fmt1 = "hms.1" # Format specifier for axis 1 +# Fmt2 = "dms" # Format specifier for axis 2 +# Dir1 = 0 # Plot axis 1 in reverse direction (hint) +# Dir2 = 1 # Plot axis 2 in conventional direction (hint) +# Presrv = 0 # Don't preserve target axes +# Permut = 1 # Axes may be permuted to match +# MinAx = 2 # Minimum number of axes to match +# MaxAx = 2 # Maximum number of axes to match +# MchEnd = 0 # Match initial target axes +# Prm1 = 1 # Axis 1 not permuted +# Prm2 = 2 # Axis 2 not permuted + Ax1 = # Axis number 1 + Begin SkyAxis # Celestial coordinate axis +# RefCnt = 1 # Count of active Object pointers +# Nobj = 2 # Count of active Objects in same class + IsA Object # Astrometry Object +# Label = "Angle on Sky" # Axis Label +# Symbol = "delta" # Axis symbol +# Unit = "ddd:mm:ss" # Axis units +# Digits = 7 # Default formatting precision +# Format = "dms" # Format specifier +# Dirn = 1 # Plot in conventional direction + IsA Axis # Coordinate axis +# Format = "dms" # Format specifier +# IsLat = 0 # Longitude axis (not latitude) +# AsTime = 0 # Display values as angles (not times) + End SkyAxis + Ax2 = # Axis number 2 + Begin SkyAxis # Celestial coordinate axis +# RefCnt = 1 # Count of active Object pointers +# Nobj = 2 # Count of active Objects in same class + IsA Object # Astrometry Object +# Label = "Angle on Sky" # Axis Label +# Symbol = "delta" # Axis symbol +# Unit = "ddd:mm:ss" # Axis units +# Digits = 7 # Default formatting precision +# Format = "dms" # Format specifier +# Dirn = 1 # Plot in conventional direction + IsA Axis # Coordinate axis +# Format = "dms" # Format specifier +# IsLat = 0 # Longitude axis (not latitude) +# AsTime = 0 # Display values as angles (not times) + End SkyAxis + IsA Frame # Coordinate system description + System = "FK4-NO-E" # Celestial coordinate system type + Epoch = 1958 # Besselian epoch of observation +# Eqnox = 1950 # Besselian epoch of mean equinox + End SkyFrame +\end{terminalv} +\normalsize + +\subsection{\label{ss:channelcommenting}Controlling Commenting} + +Another way of controlling output from a Channel is \emph{via} the +boolean (integer) Comment attribute, which controls whether comments +are appended to describe the purpose of each value. Comment has the +value 1 by default but, if set to zero, will suppress these +comments. This is normally appropriate only if you wish to minimise +the amount of output, for example: + +c+ +\small +\begin{terminalv} +astSet( channel, "Full=-1, Comment=0" ); +nobj = astWrite( channel, skyframe ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( CHANNEL, 'Full=-1, Comment=0', STATUS ) + NOBJ = AST_WRITE( CHANNEL, SKYFRAME, STATUS ) +\end{terminalv} +\normalsize +f- + +might result in the following more compact output: + +\small +\begin{terminalv} + Begin SkyFrame + Naxes = 2 + Ax1 = + Begin SkyAxis + End SkyAxis + Ax2 = + Begin SkyAxis + End SkyAxis + IsA Frame + System = "FK4-NO-E" + Epoch = 1958 + End SkyFrame +\end{terminalv} +\normalsize + +\subsection{Editing Textual Output} + +c+ +The safest advice about editing the textual output from astWrite (or +astShow) is ``don't!''---unless you know what you are doing. +c+ +f+ +The safest advice about editing the textual output from AST\_WRITE (or +AST\_SHOW) is ``don't!''---unless you know what you are doing. +f- + +Having given that warning, however, it is sometimes possible to make +changes to the text, or even to write entire Object descriptions from +scratch, and to read the results back in to construct new +Objects. Normally, simple changes to numerical values are safest, but +be aware that this is a back door method of creating Objects, so +you are on your own! There are a number of potential pitfalls. In +particular: + +\begin{itemize} +c+ +\item astRead is intended for retrieving data written by astWrite and +not for reading data input by humans. As such, the data validation +provided is very limited and is certainly not foolproof. This makes it +quite easy to construct Objects that are internally inconsistent by +this means. In contrast, the normal programming interface incorporates +numerous checks designed to make it impossible to construct invalid +Objects. You should not necessarily think you have found a bug if your +changes to an Object's textual description fail to produce the results +you expected! +c- +f+ +\item AST\_READ is intended for retrieving data written by AST\_WRITE +and not for reading data input by humans. As such, the data validation +provided is very limited and is certainly not foolproof. This makes it +quite easy to construct Objects that are internally inconsistent by +this means. In contrast, the normal programming interface incorporates +numerous checks designed to make it impossible to construct invalid +Objects. You should not necessarily think you have found a bug if your +changes to an Object's textual description fail to produce the results +you expected! +f- + +\item In many instances the names associated with values in textual +output will correspond with Object attributes. Sometimes, however, +these names may differ from the attribute name. This is mainly because +of length restrictions imposed by other common external formats, such +as FITS headers. Some of the names used do not correspond with +attributes at all. + +\item It is safest to change single numerical or string values. +Beware of changing the size or shape of Objects (\emph{e.g.}\ the +number of axes in a Frame). Often, these values must match others +stored elsewhere within the Object and changing them in a haphazard +fashion will not produce useful results. + +\item Be wary about un-commenting default values. Sometimes this will +work, but often these values are derived from other Objects stored +more deeply in the structure and the proper place to insert a new +value is not where the default itself appears. +\end{itemize} + +\subsection{\label{ss:mixingchanneltext}Mixing Objects with other Text} + +c+ +By default, when you use astRead to read from a basic Channel +(\secref{ss:readingfromachannel}), it is assumed that you are reading a +stream of text containing only AST Objects, which follow each other +end-to-end. If any extraneous input data are encountered which do not +appear to form part of the textual description of an Object, then an +error will result. In particular, the first input line must identify +the start of an Object description, so you cannot start reading half +way through an Object. +c- +f+ +By default, when you use AST\_READ to read from a basic Channel +(\secref{ss:readingfromachannel}), it is assumed that you are reading a +stream of text containing only AST Objects, which follow each other +end-to-end. If any extraneous input data are encountered which do not +appear to form part of the textual description of an Object, then an +error will result. In particular, the first input line must identify +the start of an Object description, so you cannot start reading half +way through an Object. +f- + +Sometimes, however, you may want to store AST Object descriptions +intermixed with other textual data. You can do this by setting the +Channel's boolean (integer) Skip attribute to 1. This will cause every +read to skip over extraneous data until the start of a new AST Object +description, if any, is found. So long as your other data do not mimic +the appearance of an AST Object description, the two sets of data can +co-exist. + +c+ +For example, by setting Skip to 1, the following complete C program +will read all the AST Objects whose descriptions appear in the source +of this document, ignoring the other text. astShow is used to display +those found: + +\small +\begin{terminalv} +#include "ast.h" +main() { + AstChannel *channel; + AstObject *object; + + channel = astChannel( NULL, NULL, "Skip=1" ); + while ( ( object = astRead( channel ) ) != AST__NULL ) { + astShow( object ); + object = astAnnul( object ); + } + channel = astAnnul( channel ); +} +\end{terminalv} +\normalsize +c- +f+ +For example, by setting Skip to 1, the following complete Fortran +program will read all the AST Objects whose descriptions appear in the +source of this document, ignoring the other text. AST\_SHOW is used to +display those found: + +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER CHANNEL, OBJECT, STATUS + + STATUS = 0 + CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, 'Skip=1', STATUS ) + 1 OBJECT = AST_READ( CHANNEL, STATUS ) + IF ( OBJECT .NE. AST__NULL ) THEN + CALL AST_SHOW( OBJECT, STATUS ) + CALL AST_ANNUL( OBJECT, STATUS ) + GO TO 1 + END IF + CALL AST_ANNUL( CHANNEL, STATUS ) + END +\end{terminalv} +\normalsize +f- + +\subsection{\label{ss:channelsource}Reading Objects from Files} + +Thus far, we have only considered the default behaviour of a Channel +in reading and writing Objects through a program's standard input and +output streams. We will now consider how to access Objects stored in +files more directly. + +The simple approach is to use the SinkFile and SourceFile attributes of +the Channel. For instance, the following will read a pair of Objects from +a text file called ``fred.txt'': + +c+ +\small +\begin{terminalv} +astSet( channel, "SourceFile=fred.txt" ); +obj1 = astRead( channel ); +obj2 = astRead( channel ); +astClear( channel, "SourceFile" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( CHANNEL, 'SourceFile=fred.txt', STATUS ) + OBJ1 = AST_READ( CHANNEL, STATUS ) + OBJ2 = AST_READ( CHANNEL, STATUS ) + CALL AST_CLEAR( CHANNEL, 'SourceFile', STATUS ) +\end{terminalv} +\normalsize +f- + +Note, the act of clearing the attribute tells AST that no more Objects +are to be read from the file and so the file is then closed. If the +attribute is not cleared, the file will remain open and further Objects +can be read from it. The file will always be closed when the Channel is +deleted. + +This simple approach will normally be sufficient. However, because the +AST library is designed to be used from more than one language, it has +to be a little careful about reading and writing to files. This is due +to incompatibilities that may exist between the file I/O facilities +provided by different languages. If such incompatibilities prevent the +above simple system being used, we need to adopt a system that off-loads +all file I/O to external code. + +c+ +What this means in practice is that if the above simple approach cannot +be used, you must instead provide some simple C +functions that perform the actual transfer of data to and from files +and similar external data stores. The functions you provide are +supplied as the source and/or sink function arguments to astChannel +when you create a Channel (\secref{ss:creatingachannel}). An example is +the best way to illustrate this. +c- +f+ +What this means in practice is that if the above simple approach cannot +be used, you must instead provide some simple +Fortran routines that perform the actual transfer of data to and from +files and similar external data stores. The routines you provide are +supplied as the source and/or sink routine arguments to AST\_CHANNEL +when you create a Channel (\secref{ss:creatingachannel}). An example is +the best way to illustrate this. +f- + +c+ +Consider the following simple function called Source. It reads a +single line of text from a C input stream and returns a pointer to it, +or NULL if there is no more input: + +\small +\begin{terminalv} +#include +#define LEN 200 +static FILE *input_stream; + +const char *Source( void ) { + static char buffer[ LEN + 2 ]; + return fgets( buffer, LEN + 2, input_stream ); +} +\end{terminalv} +\normalsize +c- +f+ +Consider the following simple subroutine called SOURCE. It reads a +single line of text from a Fortran I/O unit and then calls +AST\_PUTLINE to pass it to the AST library, together with its +length. It sets this length to be negative if there is no more input: + +\small +\begin{terminalv} + SUBROUTINE SOURCE( STATUS ) + INTEGER STATUS + CHARACTER * ( 200 ) BUFFER + + READ( 1, '(A)', END = 99 ) BUFFER + CALL AST_PUTLINE( BUFFER, LEN( BUFFER ), STATUS ) + RETURN + + 99 CALL AST_PUTLINE( BUFFER, -1, STATUS ) + END +\end{terminalv} +\normalsize +f- + +c+ +Note that the input stream is a static variable which we will also +access from our main program. This might look something like this +(omitting error checking for brevity): + +\small +\begin{terminalv} +/* Open the input file. */ +input_stream = fopen( "infile.ast", "r" ); + +/* Create a Channel and read an Object from it. */ +channel = astChannel( Source, NULL, "" ); +object = astRead( channel ); + +... + +/* Annul the Channel and close the file when done. */ +channel = astAnnul( channel ); +(void) fclose( input_stream ); +\end{terminalv} +\normalsize +c- +f+ +Our main program might then look something like this (omitting error +checking for brevity): + +\small +\begin{terminalv} + EXTERNAL SOURCE + + ... + +* Open the input file. + OPEN( UNIT = 1, FILE = 'infile.ast', STATUS = 'OLD' ) + +* Create the Channel and read an Object from it. + CHANNEL = AST_CHANNEL( SOURCE, AST_NULL, ' ', STATUS ) + OBJECT = AST_READ( CHANNEL, STATUS ) + + ... + +* Annul the Channel and close the file when done. + CALL AST_ANNUL( CHANNEL, STATUS ) + CLOSE( 1 ) +\end{terminalv} +\normalsize +f- + +c+ +Here, we first open the required input file, saving the resulting FILE +pointer. We then pass a pointer to our Source function as the first +argument to astChannel when creating a new Channel. When we read +an Object from this Channel with astRead, the Source +function will be called to obtain the textual data from the file, the +end-of-file being detected when this function returns NULL. +c- +f+ +Here, we first open the required input file. We then pass the name of +our SOURCE routine as the first argument to AST\_CHANNEL when creating +a new Channel (ensuring that SOURCE also appears in an EXTERNAL +statement). When we read an Object from this Channel using +AST\_READ, the SOURCE routine will be called to obtain the textual +data from the file, the end-of-file being detected when it yields a +negative line length. +f- + +Note, if a value is set for the SourceFile attribute, +c+ +the astRead function will ignore any source function +c- +f+ +the AST\_READ function will ignore any source routine +f- +specified when the Channel was created. + +\subsection{\label{ss:channelsink}Writing Objects to Files} + +As for reading, writing Objects to files can be done in two different ways. +Again, the simple approach is to use the SinkFile attribute of the Channel. +For instance, the following will write a pair of Objects to a text file +called ``fred.txt'': + +c+ +\small +\begin{terminalv} +astSet( channel, "SinkFile=fred.txt" ); +nobj = astWrite( channel, object1 ); +nobj = astWrite( channel, object2 ); +astClear( channel, "SinkFile" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_SET( CHANNEL, 'SinkFile=fred.txt', STATUS ) + NOBJ = AST_WRITE( CHANNEL, OBJECT1, STATUS ) + NOBJ = AST_WRITE( CHANNEL, OBJECT2, STATUS ) + CALL AST_CLEAR( CHANNEL, 'SinkFile', STATUS ) +\end{terminalv} +\normalsize +f- + +Note, the act of clearing the attribute tells AST that no more output +will be written to the file and so the file is then closed. If the +attribute is not cleared, the file will remain open and further Objects +can be written to it. The file will always be closed when the Channel is +deleted. + +c+ +If the details of the language's I/O system on the computer you are using +means that the above approach cannot be used, then we can write a Sink function, +that writes a line of output text to a file, and use it in basically the same +way as the Source function in the previous section (\secref{ss:channelsource}): + +\small +\begin{terminalv} +static FILE *output_stream; + +void Sink( const char *line ) { + (void) fprintf( output_stream, "%s\n", line ); +} +\end{terminalv} +\normalsize + +Note that we must supply the final newline character ourselves. +c- +f+ +If the details of the language's I/O system on the computer you are using +means that the above approach cannot be used, then we can write a SINK routine, +that obtains a line of output text from the AST library by calling AST\_GETLINE +and then writes it to a file. We can use this in basically the same way as +the SOURCE routine in the previous section (\secref{ss:channelsource}): + +\small +\begin{terminalv} + SUBROUTINE SINK( STATUS ) + INTEGER L, STATUS + CHARACTER * ( 200 ) BUFFER + + CALL AST_GETLINE( BUFFER, L, STATUS ) + IF ( L .GT. 0 ) WRITE( 2, '(A)' ) BUFFER( : L ) + + END +\end{terminalv} +\normalsize +f- + +c+ +In this case, our main program would supply a pointer to this Sink +function as the second argument to astChannel, as follows: + +\small +\begin{terminalv} +/* Open the output file. */ +output_stream = fopen( "outfile.ast", "w" ); + +/* Create a Channel and write an Object to it. */ +channel = astChannel( Source, Sink, "" ); +nobj = astWrite( channel, object ); + + ... + +/* Annul the Channel and close the file when done. */ +channel = astAnnul( channel ); +(void) fclose( output_stream ); +\end{terminalv} +\normalsize +c- +f+ +In this case, our main program would supply the name of this SINK +routine as the second argument to AST\_CHANNEL (ensuring that it also +appears in an EXTERNAL statement), as follows: + +\small +\begin{terminalv} + EXTERNAL SINK + + ... + +* Open the output file. + OPEN( UNIT = 2, FILE = 'outfile.ast', STATUS = 'NEW' ) + +* Create a Channel and write an Object to it. + CHANNEL = AST_CHANNEL( SOURCE, SINK, ' ', STATUS ) + NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) + + ... + +* Annul the Channel and close the file when done. + CALL AST_ANNUL( CHANNEL, STATUS ) + CLOSE( 2 ) +\end{terminalv} +\normalsize +f- + +c+ +Note that we can specify a source and/or a sink function for the +Channel, and that these may use either the same file, or different +files according to whether we are reading or writing. AST has no +knowledge of the underlying file system, nor of file positioning. It +just reads and writes sequentially. If you wish, for example, to +reposition a file at the beginning in between reads and writes, then +this can be done directly (and completely independently of AST) using +standard C functions. +c- +f+ +Note that we can specify a source and/or a sink routine for the +Channel, and that these may use either the same file, or different +files according to whether we are reading or writing. AST has no +knowledge of the underlying file system, nor of file positioning. It +just reads and writes sequentially. If you wish, for example, to +reposition a file at the beginning in between reads and writes, then +this can be done directly (and completely independently of AST) using +standard Fortran statements. +f- + +c+ +If an error occurs in your source or sink function, you can +communicate this to the AST library by setting its error status to any +error value using astSetStatus (\secref{ss:errordetection}). This will +immediately terminate the read or write operation. +c- +f+ +If an error occurs in your source or sink routine, you can communicate +this to the AST library by setting the STATUS argument to any error +value. This will immediately terminate the read or write operation. +f- + +Note, if a value is set for the SinkFile attribute, +c+ +the astWrite function will ignore any sink function +c- +f+ +the AST\_WRITE function will ignore any sink routine +f- +specified when the Channel was created. + +\subsection{\label{ss:otherplaces}Reading and Writing Objects to other Places} + +c+ +It should be obvious from the above (\secref{ss:channelsource} and +\secref{ss:channelsink}) that a Channel's source and sink functions +provide a flexible means of intercepting textual data that describes +AST Objects as it flows in and out of your program. In fact, you might +like to regard a Channel simply as a filter for converting AST Objects +to and from a stream of text which is then handled by your source and +sink functions, where the real I/O occurs. +c- +f+ +It should be obvious from the above (\secref{ss:channelsource} and +\secref{ss:channelsink}) that a Channel's source and sink routines +provide a flexible means of intercepting textual data that describes +AST Objects as it flows in and out of your program. In fact, you might +like to regard a Channel simply as a filter for converting AST Objects +to and from a stream of text which is then handled by your source and +sink routines, where the real I/O occurs. +f- + +This gives you the ability to store AST Objects in virtually any data +system, so long as you can convert a stream of text into something +that can be stored (it need no longer be text) and retrieve it +again. There is generally no need to retain comments. Other +possibilities, such as inter-process and network communication, could +also be implemented \emph{via} source and sink functions in basically +the same way. + +\cleardoublepage +\section{\label{ss:nativefits}Storing AST Objects in FITS Headers (FitsChans)} + +A FITS header is a sequence of 80-character strings, formatted +according to particular rules defined by the Flexible Image Transport +System +(FITS). \htmladdnormallinkfoot{FITS}{http://fits.gsfc.nasa.gov/} +is a widely-used standard for data interchange in astronomy and has +also been adopted as a data processing format in some astronomical +data reduction systems. The individual 80-character strings in a FITS +header are usually called \emph{cards} or \emph{header cards} (for +entirely anachronistic reasons). + +A sequence of FITS cards appears as a header at the start of every +FITS data file, and sometimes also at other points within it, and is +used to provide ancillary information which qualifies or describes the +main array of data stored in the file. As such, FITS headers are prime +territory for storing information about the coordinate systems +associated with data held in FITS files. + +In this section, we will examine how to store information in FITS +headers directly in the form of AST Objects---a process which is +supported by a specialised class of Channel called a FitsChan. Our +discussion here will turn out to be a transitional step that +emphasises the similarities between a FitsChan and a Channel +(\secref{ss:channels}). At the same time, it will prepare us for the +next section (\secref{ss:foreignfits}), where we will examine how to +use a FitsChan to tackle some of the more difficult problems that FITS +headers can present. + +\subsection{\label{ss:nativeencoding}The Native FITS Encoding} + +As it turns out, we are not the first to have thought of storing WCS +information in FITS headers. In fact, the original FITS standard (1981 +vintage) defined a set of header keywords for this purpose which have +been widely used, although they have proved too limited for many +practical purposes. + +At the time of writing, a number of different ways of using FITS +headers for storing WCS information are in use, most (although not +all) based on the original standard. We will refer to these +alternative ways of storing the information as FITS \emph{encodings} +but will defer a discussion of their advantages and limitations until +the next section (\secref{ss:foreignfits}). + +Here, we will examine how to store AST Objects directly in FITS +headers. In effect, this defines a new encoding, which we will term +the \emph{native encoding}. This is a special kind of encoding, +because not only does it allow us to associate conventional +WCS calibration information with FITS data, but it also allows any other +information that can be expressed in terms of AST Objects to be stored +as well. In fact, the native encoding provides us with facilities +roughly analogous to those of the Channel +(\secref{ss:channels})---\emph{i.e.}\ a lossless way of +transferring AST Objects from program to program---but based on FITS +headers instead of free-format text. + +\subsection{The FitsChan Model} + +I/O between AST Objects and FITS headers is supported by a specialised +form of Channel called a FitsChan. A FitsChan contains a buffer which +may hold any number, including zero, of FITS header cards. This buffer +forms a workspace in which you can assemble FITS cards and manipulate +them before writing them out to a file. + +By default, when a FitsChan is first created, it contains no cards and +there are five ways of inserting cards into it: + +\begin{enumerate} +c+ +\item You may add cards yourself, one at a time, using astPutFits +(\secref{ss:addingfitscards}). +c- +f+ +\item You may add cards yourself, one at a time, using AST\_PUTFITS +(\secref{ss:addingfitscards}). +f- + +c+ +\item You may add cards yourself, supplying all cards concatenated into a +single string, using astPutCards +(\secref{ss:addingmulticards}). +c- +f+ +\item You may add cards yourself, supplying all cards concatenated into a +single string, using AST\_PUTCARDS. +(\secref{ss:addingmulticards}). +f- + +c+ +\item You may write an AST Object to the FitsChan (using astWrite), +which will have the effect of creating new cards within the FitsChan +which describe the Object (\secref{ss:writingnativefits}). +c- +f+ +\item You may write an AST Object to the FitsChan (using AST\_WRITE), +which will have the effect of creating new cards within the FitsChan +which describe the Object (\secref{ss:writingnativefits}). +f- + +\item You may assign a value to the SourceFile attribute of the FitsChan. +The value should be the path to a text file holding a set of FITS header +cards, one per line. When the SourceFile value is set (using +c+ +astSetC or astSet), +c- +f+ +AST\_SETC or AST\_SET). +f- +the file is opened and the headers copied from it into the FitsChan. +The file is then immediately closed. + +c+ +\item You may specify a source function which reads data from some +external store of FITS cards, just like the source associated with a +basic Channel (\secref{ss:channelsource}). If you supply a source +function, it will be called when the FitsChan is created in order to +fill it with an initial set of cards (\secref{ss:fitssourceandsink}). +c- +f+ +\item You may specify a source routine which reads data from some +external store of FITS cards, just like the source associated with a +basic Channel (\secref{ss:channelsource}). If you supply a source +routine, it will be called when the FitsChan is created in order to +fill it with an initial set of cards (\secref{ss:fitssourceandsink}). +f- +\end{enumerate} + +There are also four ways of removing cards from a FitsChan: + +\begin{enumerate} +c+ +\item You may delete cards yourself, one at a time, using astDelFits +(\secref{ss:findingandchangingfits}). +c- +f+ +\item You may delete cards yourself, one at a time, using AST\_DELFITS +(\secref{ss:findingandchangingfits}). +f- + +c+ +\item You may read an AST Object from the FitsChan (using astRead), +which will have the effect of removing those cards from the FitsChan +which describe the Object (\secref{ss:readingnativefits}). +c- +f+ +\item You may read an AST Object from the FitsChan (using AST\_READ), +which will have the effect of removing those cards from the FitsChan +which describe the Object (\secref{ss:readingnativefits}). +f- + +\item You may assign a value to the FitsChan's SinkFile attribute. When +the FitsChan is deleted, any remaining headers are written out to a text +file with path equal to the value of the SinkFile attribute. + +c+ +\item Alternatively, you may specify a sink function which writes data to some +external store of FITS cards, just like the sink associated with a +basic Channel (\secref{ss:channelsink}). If you supply a sink function, +it will be called when the FitsChan is deleted in order to write out +any FITS cards that remain in it (\secref{ss:fitssourceandsink}). Note, +the sink function is not called if the SinkFile attribute has been set. +c- +f+ +\item Alternatively, You may specify a sink routine which writes data to some +external store of FITS cards, just like the sink associated with a +basic Channel (\secref{ss:channelsink}). If you supply a sink routine, +it will be called when the FitsChan is deleted in order to write out +any FITS cards that remain in it (\secref{ss:fitssourceandsink}). Note, +the sink routine is not called if the SinkFile attribute has been set. +f- +\end{enumerate} + +Note, in particular, that reading an AST Object from a FitsChan is +\emph{destructive}. That is, it deletes the FITS cards that describe the +Object. The reason for this is explained in +\secref{ss:destructiveread}. + +c+ +In addition to the above, you may also read individual cards from a +FitsChan using the function astFindFits (which is not +destructive). This is the main means of writing out FITS cards if you +have not supplied a sink function. astFindFits also provides a means +of searching for particular FITS cards (by keyword, for example) and +there are other facilities for overwriting cards when required +(\secref{ss:findingandchangingfits}). +c- +f+ +In addition to the above, you may also read individual cards from a +FitsChan using the function AST\_FINDFITS (which is not +destructive). This is the main means of writing out FITS cards if you +have not supplied a sink routine. AST\_FINDFITS also provides a means +of searching for particular FITS cards (by keyword, for example) and +there are other facilities for overwriting cards when required +(\secref{ss:findingandchangingfits}). +f- + +\subsection{\label{ss:creatingafitschan}Creating a FitsChan} + +c+ +The FitsChan constructor function, astFitsChan, is straightforward to +use: +c- +f+ +The FitsChan constructor function, AST\_FITSCHAN, is straightforward +to use: +f- + +c+ +\small +\begin{terminalv} +#include "ast.h" +AstFitsChan *fitschan; + +... + +fitschan = astFitsChan( NULL, NULL, "Encoding=NATIVE" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER FITSCHAN, STATUS + + STATUS = 0 + + ... + + FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, 'Encoding=NATIVE', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, we have omitted any source or sink functions by supplying NULL +pointers for the first two arguments. +c- +f+ +Here, we have omitted any source or sink functions by supplying the +AST\_NULL routine for the first two arguments (remember to include the +AST\_PAR include file which contains the required EXTERNAL statement +for this routine). +f- +We have also initialised the FitsChan's Encoding attribute to +NATIVE. This indicates that we will be using the native encoding +(\secref{ss:nativeencoding}) to store and retrieve Objects. If this +was left unspecified, the default would depend on the FitsChan's +contents. An attempt is made to use whatever encoding appears to have +been used previously. For an empty FitsChan, the default is NATIVE, +but it does no harm to be sure. + +\subsection{\label{ss:addressingfitscards}Addressing Cards in a FitsChan} + +Because a FitsChan contains an ordered sequence of header cards, a +mechanism is needed for addressing them. This allows you to specify +where new cards are to be added, for example, or which card is to be +deleted. + +This role is filled by the FitsChan's integer Card attribute, which +gives the index of the \emph{current card} in the FitsChan. You can +nominate any card you like to be current, simply by setting a new +value for the Card attribute, for example: + +c+ +\small +\begin{terminalv} +int icard; + +... + +astSetI( fitschan, "Card", icard ) +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER ICARD + + ... + + CALL AST_SETI( FITSCHAN, 'Card', ICARD, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +where ``icard'' contains the index of the card on which you wish to +operate next. Some functions will update the Card attribute as a +means of advancing through the sequence of cards, when reading them +for example, or to indicate which card matches a search criterion. +c- +f+ +where ICARD contains the index of the card on which you wish to +operate next. Some functions will update the Card attribute as a +means of advancing through the sequence of cards, when reading them +for example, or to indicate which card matches a search criterion. +f- + +The default value for Card is one, which is the index of the first +card. This means that you can ``rewind'' a FitsChan to access its +first card by clearing the Card attribute: + +c+ +\small +\begin{terminalv} +astClear( fitschan, "Card" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) +\end{terminalv} +\normalsize +f- + +The total number of cards in a FitsChan is given by the integer Ncard +attribute. This is a read-only attribute whose value is automatically +updated as you add or remove cards. It means you can address all the +cards in sequence using a loop such as the following: + +c+ +\small +\begin{terminalv} +int ncard; + +... + +ncard = astGetI( fitschan, "Ncard" ); +for ( icard = 1; icard <= ncard; icard++ ) { + astSetI( fitschan, "Card", icard ); + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DO 1 ICARD = 1, AST_GETI( FITSCHAN, 'Ncard', STATUS ) + CALL AST_SETI( FITSCHAN, 'Card', ICARD, STATUS ) + + 1 CONTINUE +\end{terminalv} +\normalsize +f- + +c+ +However, it is usually possible to write slightly tidier loops based +on the astFindFits function described later +(\secref{ss:extractingfitscards} and +\secref{ss:findingandchangingfits}). +c- +f+ +However, it is usually possible to write slightly tidier loops based +on the AST\_FINDFITS function described later +(\secref{ss:extractingfitscards} and +\secref{ss:findingandchangingfits}). +f- + +If you set the Card attribute to a value larger than Ncard, the +FitsChan is regarded as being positioned at its \emph{end-of-file}. In +this case there is no current card and an attempt to obtain a value +for the Card attribute will always return the value Ncard~$+$~1. When +a FitsChan is empty, it is always at the end-of-file. + +\subsection{\label{ss:writingnativefits}Writing Native Objects to a FitsChan} + +c+ +Having created an empty FitsChan (\secref{ss:creatingafitschan}), you +can write any AST Object to it in the native encoding using the +astWrite function. Let us assume we are writing a +SkyFrame,\footnote{More probably, you would want to write a FrameSet, +but for purposes of illustration a SkyFrame contains a more manageable +amount of data.} as follows: +c- +f+ +Having created an empty FitsChan (\secref{ss:creatingafitschan}), you +can write any AST Object to it in the native encoding using the +AST\_WRITE function. Let us assume we are writing a +SkyFrame,\footnote{More probably, you would want to write a FrameSet, +but for purposes of illustration a SkyFrame contains a more manageable +amount of data.} as follows: +f- + +c+ +\small +\begin{terminalv} +AstSkyFrame *skyframe; +int nobj; + +... + +nobj = astWrite( fitschan, skyframe ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER NOBJ, SKYFRAME + + ... + + NOBJ = AST_WRITE( FITSCHAN, SKYFRAME, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Since we have selected the native encoding +(\secref{ss:nativeencoding}), there are no restrictions on the class +of Object we may write, so astWrite should always return a value of +one, unless an error occurs. Unlike a basic Channel +(\secref{ss:writingtoachannel}), this write operation will not produce +any output from our program. The FITS headers produced are simply +stored inside the FitsChan. +c- +f+ +Since we have selected the native encoding +(\secref{ss:nativeencoding}), there are no restrictions on the class +of Object we may write, so AST\_WRITE should always return a value of +one, unless an error occurs. Unlike a basic Channel +(\secref{ss:writingtoachannel}), this write operation will not produce +any output from our program. The FITS headers produced are simply +stored inside the FitsChan. +f- + +After this write operation, the Ncard attribute will be updated to +reflect the number of new cards added to the FitsChan and the Card +attribute will point at the card immediately after the last one +written. Since our FitsChan was initially empty, the Card attribute +will, in this example, point at the end-of-file +(\secref{ss:addressingfitscards}). + +The FITS standard imposes a limit of 68 characters on the length of +strings which may be stored in a single header card. Sometimes, a +description of an AST Object involves the use of strings which exceed +this limit (\emph{e.g.}\ a Frame title can be of arbitrary length). If +this occurs, the long string will be split over two or more header cards. +Each ``continuation'' card will have the keyword \texttt{CONTINUE} in +columns 1 to 8, and will contain a space in column 9 (instead of the +usual equals sign). An ampersand (``\texttt{\&}'') is appended to the end of +each of the strings (except the last one) to indicate that the string is +continued on the next card. + +c+ +Note, this splitting of long strings over several cards only occurs when +writing AST Objects to a FitsChan using the astWrite function and the +\emph{native} encoding. If a long string is stored in a FitsChan using +(for instance) the astPutFits or astPutCards function, it will simply be truncated. +c- + +f+ +Note, this splitting of long strings over several cards only occurs when +writing AST Objects to a FitsChan using the AST\_WRITE routine and the +\emph{native} encoding. If a long string is stored in a FitsChan using +(for instance) the AST\_PUTFITS or AST\_PUTCARDS routine, it will simply be truncated. +f- + +\subsection{\label{ss:extractingfitscards}Extracting Individual Cards from a FitsChan} + +c+ +To examine the contents of the FitsChan after writing the SkyFrame +above (\secref{ss:writingnativefits}), we must write a simple loop to +extract each card in turn and print it out. We must also remember to +rewind the FitsChan first, \emph{e.g.}\ using astClear. The following +loop would do: +c- +f+ +To examine the contents of the FitsChan after writing the SkyFrame +above (\secref{ss:writingnativefits}), we must write a simple loop to +extract each card in turn and print it out. We must also remember to +rewind the FitsChan first, \emph{e.g.}\ using AST\_CLEAR. The +following loop would do: +f- + +c+ +\small +\begin{terminalv} +#include +char card[ 81 ]; + +... + +astClear( fitschan, "Card" ); +while ( astFindFits( fitschan, "%f", card, 1 ) ) (void) printf( "%s\n", card ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 80 ) CARD + + ... + + CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) + + 2 CONTINUE + IF ( AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS ) ) THEN + WRITE ( *, '(A)' ) CARD + GO TO 2 + END IF +\end{terminalv} +\normalsize +f- + +c+ +Here, we have used the astFindFits function to find a FITS card by +keyword. It is given a keyword template of ``\%f'', which matches any +FITS keyword, so it always finds the current card, which it +returns. Its fourth argument is set to 1, to indicate that the Card +attribute should be incremented afterwards so that the following card +will be found the next time around the loop. astFindFits returns zero +when it reaches the end-of-file and this terminates the loop. +c- +f+ +Here, we have used the AST\_FINDFITS function to find a FITS card by +keyword. It is given a keyword template of ``\%f'', which matches any +FITS keyword, so it always finds the current card, which it +returns. Its fourth argument is set to .TRUE., to indicate that the +Card attribute should be incremented afterwards so that the following +card will be found the next time around the loop. AST\_FINDFITS +returns .FALSE.\ when it reaches the end-of-file and this terminates +the loop. +f- + +c+ +If we were storing the FITS headers in an output FITS file instead of +printing them out, we might use a loop like this but replace +``printf'' with a suitable data storage operation. This would only be +necessary if we had not provided a sink function for the FitsChan +(\secref{ss:fitssourceandsink}). +c- +f+ +If we were storing the FITS headers in an output FITS file instead of +printing them out, we might use a loop like this but replace the WRITE +statement with a call to a suitable data access routine to store the +header card. This would only be necessary if we had not provided a +sink routine for the FitsChan (\secref{ss:fitssourceandsink}). +f- + +\subsection{The Native FitsChan Output Format} + +If we print out the FITS header cards describing the SkyFrame we wrote +earlier (\secref{ss:writingnativefits}), we should obtain something +like the following: + +\small +\begin{terminalv} +COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST +COMMENT AST Beginning of AST data for SkyFrame object AST +COMMENT AST ................................................................ AST +BEGAST_A= 'SkyFrame' / Description of celestial coordinate system +NAXES_A = 2 / Number of coordinate axes +AX1_A = ' ' / Axis number 1 +BEGAST_B= 'SkyAxis ' / Celestial coordinate axis +ENDAST_A= 'SkyAxis ' / End of object definition +AX2_A = ' ' / Axis number 2 +BEGAST_C= 'SkyAxis ' / Celestial coordinate axis +ENDAST_B= 'SkyAxis ' / End of object definition +ISA_A = 'Frame ' / Coordinate system description +SYSTEM_A= 'FK4-NO-E' / Celestial coordinate system type +EPOCH_A = 1958.0 / Besselian epoch of observation +ENDAST_C= 'SkyFrame' / End of object definition +COMMENT AST ................................................................ AST +COMMENT AST End of AST data for SkyFrame object AST +COMMENT AST ---------------------------------------------------------------- AST +\end{terminalv} +\normalsize + +As you can see, this resembles the information that would be written +to a basic Channel to describe the same SkyFrame +(\secref{ss:textualoutputformat}), except that it has been formatted +into 80-character header cards according to FITS conventions. + +There are also a number of other differences worth noting: + +\begin{enumerate} +\item There is no unnecessary information about default values +provided for the benefit of the human reader. This is because the Full +attribute for a FitsChan defaults to $-$1, thus suppressing this +information (\emph{c.f.}~\secref{ss:controllingchanneloutput}). You +can restore the information if you wish by setting Full to 0 or $+$1, +in which case additional COMMENT cards will be generated to hold it. + +\item The information is not indented, because FITS does not allow +this. However, if you change the Full attribute to 0 or $+$1, comments +will be included that are intended to help break up the sequence of +headers and highlight its structure. This will probably only be of use +if you are attempting to track down a problem by examining the FITS +cards produced in detail. + +\item The FITS keywords which appear to the left of the ``$=$'' signs +have additional characters (``\_A'', ``\_B'', \emph{etc.}) appended to +them. This is done in order to make each keyword unique. +\end{enumerate} + +c+ +This last point is worth further comment and is necessary because the +FITS standard only allows for certain keywords (such as COMMENT and +HISTORY) to appear more than once. astWrite therefore appends an +arbitrary sequence of two characters to each new keyword it generates +in order to ensure that it does not duplicate any already present in +the FitsChan. +c- +f+ +This last point is worth further comment and is necessary because the +FITS standard only allows for certain keywords (such as COMMENT and +HISTORY) to appear more than once. AST\_WRITE therefore appends an +arbitrary sequence of two characters to each new keyword it generates +in order to ensure that it does not duplicate any already present in +the FitsChan. +f- + +c+ +The main risk from not following this convention is that some software +might ignore (say) all but the last occurrence of a keyword before +passing the FITS headers on. Such an event is unlikely, but would +obviously destroy the information present, so astWrite enforces the +uniqueness of the keywords it uses. The extra characters added are +ignored when the information is read back. +c- +f+ +The main risk from not following this convention is that some software +might ignore (say) all but the last occurrence of a keyword before +passing the FITS headers on. Such an event is unlikely, but would +obviously destroy the information present, so AST\_WRITE enforces the +uniqueness of the keywords it uses. The extra characters added are +ignored when the information is read back. +f- + +As with a basic Channel, you can also suppress the comments produced +in a FitsChan by setting the boolean (integer) Comment attribute to +zero (\secref{ss:channelcommenting}). However, FITS headers are +traditionally generously commented, so this is not recommended. + +\subsection{\label{ss:addingfitscards}Adding Individual Cards to a FitsChan} + +c+ +To insert individual cards into a FitsChan, prior to reading them back +as Objects for example, you should use the astPutFits function. You +can insert a card in front of the current one as follows: +c- +f+ +To insert individual cards into a FitsChan, prior to reading them back +as Objects for example, you should use the AST\_PUTFITS routine. You +can insert a card in front of the current one as follows: +f- + +c+ +\small +\begin{terminalv} +astPutFits( fitschan, card, 0 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_PUTFITS( FITSCHAN, CARD, .FALSE., STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +where the third argument of zero indicates that the current card +should not be overwritten. Note that facilities are not provided by +AST for formatting the card contents. +c- +f+ +where the third argument of .FALSE.\ indicates that the current card +should not be overwritten. Note that facilities are not provided by +AST for formatting the card contents. +f- + +c+ +After inserting a card, the FitsChan's Card attribute points at the +original Card, or at the end-of-file if the FitsChan was originally +empty. Entering a sequence of cards is therefore straightforward. If +``cards'' is an array of pointers to strings containing FITS header +cards and ``ncards'' is the number of cards, then a loop such as the +following will insert the cards in sequence into a FitsChan: +c- +f+ +After inserting a card, the FitsChan's Card attribute points at the +original Card, or at the end-of-file if the FitsChan was originally +empty. Entering a sequence of cards is therefore straightforward. If +CARDS is an array of character strings containing FITS header cards +and NCARDS is the number of cards, then a loop such as the following +will insert the cards in sequence into a FitsChan: +f- + +c+ +\small +\begin{terminalv} +#define MAXCARD 100 +char *cards[ MAXCARD ]; +int ncard; + +... + +for ( icard = 0; icard < ncard; icard++ ) astPutFits( fitschan, cards[ icard ], 0 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER NCARD + CHARACTER * ( 80 ) CARDS( NCARD ) + + ... + + DO 3 ICARD = 1, NCARD + CALL AST_PUTFITS( FITSCHAN, CARDS( ICARD ), .FALSE., STATUS ) + 3 CONTINUE +\end{terminalv} +\normalsize +f- + +c+ +The string containing a card need not be null terminated if it is at +least 80 characters long (we have not allocated space for the strings +themselves in this brief example). +c- + +c+ +Note that astPutFits enforces the validity of a FitsChan by rejecting +any cards which do not adhere to the FITS standard. If any such cards +are detected, an error will result. +c- +f+ +Note that AST\_PUTFITS enforces the validity of a FitsChan by +rejecting any cards which do not adhere to the FITS standard. If any +such cards are detected, an error will result. +f- + +\subsection{\label{ss:addingmulticards}Adding Concatenated Cards to a FitsChan} + +If you have all your cards concatenated together into a single long string, +each occupying 80 characters (with no delimiters), you can insert them +into a FitsChan in a single call using +c+ +astPutCards. +c- +f+ +AST\_PUTCARDS. +f- +This call first empties the supplied FitsChan of any existing cards, then +inserts the new cards, and finally rewinds the FitsChan so that a +subsequent call to +c+ +astRead +c- +f+ +AST\_READ +f- +will start reading from the first supplied card. The +c+ +astPutCards function uses astPutFits +c- +f+ +AST\_PUTCARDS routine uses AST\_PUTFITS +f- +internally to interpret and store each individual card, and so the +caveats in \secref{ss:addingfitscards} should be read. + +c+ +For instance, if you are using the CFITSIO library for access to FITS +files, you can use the CFITSIO fits\_hdr2str function to obtain a string suitable +for passing to astPutCards: + +\small +\begin{terminalv} + + +if( !fits_hdr2str( fptr, 0, NULL, 0, &header, &nkeys, &status ) ) + fitschan = astFitsChan( NULL, NULL, "" ); + astPutCards( fitschan, header ); + header = free( header ); + wcsinfo = astRead( fitschan ); + + ... +} +\end{terminalv} +\normalsize + + +c- + +\subsection{\label{ss:readingnativefits}Reading Native Objects From a FitsChan} + +c+ +Once you have stored a FITS header description of an Object in a +FitsChan using the native encoding (\secref{ss:writingnativefits}), +you can read it back using astRead in much the same way as with a +basic Channel (\secref{ss:readingfromachannel}). Similar comments +about validating the Object you read also apply +(\secref{ss:validatinginput}). If you have just written to the +FitsChan, you must remember to rewind it first: +c- +f+ +Once you have stored a FITS header description of an Object in a +FitsChan using the native encoding (\secref{ss:writingnativefits}), +you can read it back using AST\_READ in much the same way as with a +basic Channel (\secref{ss:readingfromachannel}). Similar comments +about validating the Object you read also apply +(\secref{ss:validatinginput}). If you have just written to the +FitsChan, you must remember to rewind it first: +f- + +c+ +\small +\begin{terminalv} +AstObject *object; + +... + +astClear( fitschan, "Card" ); +object = astRead( fitschan ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER OBJECT + + ... + + CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) + OBJECT = AST_READ( FITSCHAN, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +An important feature of a FitsChan is that read operations are +destructive. This means that if an Object description is found, it +will be consumed by astRead which will remove all the cards involved, +including associated COMMENT cards, from the FitsChan. Thus, if you +write an Object to a FitsChan, rewind, and read the same Object back, +you should end up with the original FitsChan contents. If you need to +circumvent this behaviour for any reason, it is a simple matter to +make a copy of a FitsChan using astCopy +(\secref{ss:copyingobjects}). If you then read from the copy, the +original FitsChan will remain untouched. +c- +f+ +An important feature of a FitsChan is that read operations are +destructive. This means that if an Object description is found, it +will be consumed by AST\_READ which will remove all the cards +involved, including associated COMMENT cards, from the FitsChan. Thus, +if you write an Object to a FitsChan, rewind, and read the same Object +back, you should end up with the original FitsChan contents. If you +need to circumvent this behaviour for any reason, it is a simple +matter to make a copy of a FitsChan using AST\_COPY +(\secref{ss:copyingobjects}). If you then read from the copy, the +original FitsChan will remain untouched. +f- + +After a read completes, the FitsChan's Card attribute identifies the +card immediately following the last card read, or the end-of-file of +there are no more cards. + +c+ +Since the \emph{native} encoding is being used, any long strings involved +in the object description will have been split into two or more adjacent +contuation cards when the Object was stored in the header using function +astWrite. The astRead function reverses this process by concatenating any +such adjacent continuation cards to re-create the original long string. +c- + +f+ +Since the \emph{native} encoding is being used, any long strings involved +in the object description will have been split into two or more adjacent +contuation cards when the Object was stored in the header using routine +AST\_WRITE. The AST\_READ routine reverses this process by concatenating +any such adjacent continuation cards to re-create the original long +string. +f- + +\subsection{Saving and Restoring Multiple Objects in a FitsChan} + +When using the native FITS encoding, multiple Objects may be stored +and all I/O operations are sequential. This means that you can simply +write a sequence of Objects to a FitsChan. After each write operation, +the Card attribute will be updated so that the next write appends the +next Object description to the previous one. + +If you then rewind the FitsChan, you can read the Objects back in the +original order. Reading them back will, of course, remove their +descriptions from the FitsChan (\secref{ss:readingnativefits}) but the +behaviour of the Card attribute is such that successive reads will +simply return each Object in sequence. + +The only thing that may require care, given that a FitsChan can always +be addressed randomly by setting its Card attribute, is to avoid +writing one Object on top of another. For obvious reasons, the Object +descriptions in a FitsChan must remain separate if they are to make +sense when read back. + +\subsection{Mixing Native Objects with Other FITS Cards} + +Of course, any real FITS header will contain other information besides +AST Objects, if only the mandatory FITS cards that must accompany all +FITS data. When FITS headers are read in from a real dataset, +therefore, any native AST Object descriptions will be inter-mixed with +many other cards. + +Because this is the normal state of affairs, the boolean (integer) +Skip attribute for a FitsChan defaults to one. This means that when +you read an Object From a FitsChan, any irrelevant cards will simply +be skipped over until the start of the next Object description, if +any, is found. If you start reading part way through an Object +description, no error will result. The remainder of the description +will simply be skipped. + +Setting Skip to zero will change this behaviour to resemble that of a +basic Channel (\secref{ss:mixingchanneltext}), where extraneous data +are not permitted by default, but this will probably rarely be useful. + +\subsection{\label{ss:findingandchangingfits}Finding and Changing Cards in a FitsChan} + +c+ +You can search for, and retrieve, particular cards in a FitsChan by +keyword, using the function astFindFits. This performs a search, +starting at the current card, until it finds a card whose keyword +matches the template you supply, or the end-of-file is reached. +c- +f+ +You can search for, and retrieve, particular cards in a FitsChan by +keyword, using the function AST\_FINDFITS. This performs a search, +starting at the current card, until it finds a card whose keyword +matches the template you supply, or the end-of-file is reached. +f- + +c+ +If a suitable card is found, astFindFits optionally returns the card's +contents and then sets the FitsChan's Card attribute either to +identify the card found, or the one following it. The way you want the +Card attribute to be set is indicated by the final boolean (int) +argument to astFindFits. A value of one is returned to indicate +success. If a suitable card cannot be found, astFindFits returns a +value of zero to indicate failure and sets the FitsChan's Card +attribute to the end-of-file. +c- +f+ +If a suitable card is found, AST\_FINDFITS returns the card's contents +and then sets the FitsChan's Card attribute either to identify the +card found, or the one following it. The way you want the Card +attribute to be set is indicated by the fourth (logical) argument to +AST\_FINDFITS. A value of .TRUE.\ is returned to indicate success. If +a suitable card cannot be found, AST\_FINDFITS returns a value of +.FALSE.\ to indicate failure and sets the FitsChan's Card attribute to +the end-of-file. +f- + +c+ +Requesting that the Card attribute be set to indicate the card that +astFindFits finds is useful if you want to replace that card with a +new one, as in this example: +c- +f+ +Requesting that the Card attribute be set to indicate the card that +AST\_FINDFITS finds is useful if you want to replace that card with a +new one, as in this example: +f- + +c+ +\small +\begin{terminalv} +char newcard[ 81 ]; + +... + +(void) astFindFits( fitschan, "AIRMASS", NULL, 0 ); +astPutFits( fitschan, newcard, 1 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 80 ) NEWCARD + LOGICAL JUNK + + ... + + JUNK = AST_FINDFITS( FITSCHAN, 'AIRMASS', CARD, .FALSE., STATUS ) + CALL AST_PUTFITS( FITSCHAN, NEWCARD, .TRUE., STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, astFindFits is used to search for a card with the keyword +AIRMASS, with a NULL pointer being given to indicate that we do not +want the card's contents returned. If the card is found, astPutFits +then overwrites it with a new card. Otherwise, the Card attribute +ends up pointing at the end-of-file and the new card is simply +appended to the end of the FitsChan. +c- +f+ +Here, AST\_FINDFITS is used to search for a card with the keyword +AIRMASS. If the card is found, AST\_PUTFITS then overwrites it with a +new card. Otherwise, the Card attribute ends up pointing at the +end-of-file and the new card is simply appended to the end of the +FitsChan. +f- + +c+ +A similar approach can be used to delete selected cards from a +FitsChan using astDelFits, which deletes the current card: +c- +f+ +A similar approach can be used to delete selected cards from a +FitsChan using AST\_DELFITS, which deletes the current card: +f- + +c+ +\small +\begin{terminalv} +if ( astFindFits( fitschan, "BSCALE", NULL, 0 ) ) astDelFits( fitschan ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + IF ( AST_FINDFITS( FITSCHAN, 'BSCALE', CARD, .FALSE., STATUS ) ) THEN + CALL AST_DELFITS( FITSCHAN, STATUS ) + END IF +\end{terminalv} +\normalsize +f- + +This deletes the first card, if any, with the BSCALE keyword. + +c+ +Requesting that astFindFits increments the Card attribute to identify +the card following the one found is more useful when writing loops. +For example, the following loop extracts each card whose keyword +matches the template ``CD\%6d'' (that is, ``CD'' followed by six +decimal digits): +c- +f+ +Requesting that AST\_FINDFITS increments the Card attribute to +identify the card following the one found is more useful when writing +loops. For example, the following loop extracts each card whose +keyword matches the template ``CD\%6d'' (that is, ``CD'' followed by +six decimal digits): +f- + +c+ +\small +\begin{terminalv} +while ( astFindFits( fitschan, "CD%6d", card, 1 ) { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + 4 CONTINUE + IF ( AST_FINDFITS( FITSCHAN, 'CD%6d', CARD, .TRUE., STATUS ) ) THEN + + GO TO 4 + END IF +\end{terminalv} +\normalsize +f- + +c+ +For further details of keyword templates, see the description of +astFindFits in \appref{ss:functiondescriptions}. +c- +f+ +For further details of keyword templates, see the description of +AST\_FINDFITS in \appref{ss:functiondescriptions}. +f- + +c+ +\subsection{\label{ss:fitssourceandsink}Source and Sink Functions for FitsChans} +c- +f+ +\subsection{\label{ss:fitssourceandsink}Source and Sink Routines for FitsChans} +f- + +c+ +The use of source and sink functions with a FitsChan is optional. This +is because you can always arrange to explicitly fill a FitsChan with +FITS cards (\secref{ss:addingfitscards} and \secref{ss:addingmulticards}) +and you can also extract any +cards that remain and write them out yourself +(\secref{ss:extractingfitscards}) before you delete the FitsChan. +c- +f+ +The use of source and sink routines with a FitsChan is optional. This +is because you can always arrange to explicitly fill a FitsChan with +FITS cards (\secref{ss:addingfitscards} and \secref{ss:addingmulticards}) +and you can also extract any +cards that remain and write them out yourself +(\secref{ss:extractingfitscards}) before you delete the FitsChan. +f- + +c+ +If you choose to use these functions, however, they behave in a very +similar manner to those used by a Channel (\secref{ss:channelsource} +and \secref{ss:channelsink}). You supply pointers to these functions, +as arguments to the constructor function astFitsChan when you create +the FitsChan (\secref{ss:creatingafitschan}). The source function is +invoked implicitly at this point to fill the FitsChan with FITS cards +and the FitsChan is then rewound, so that the first card becomes +current. The sink function is automatically invoked later, when the +FitsChan is deleted, in order to write out any cards that remain in +it. +c- +f+ +If you choose to use these routines, however, they behave in a very +similar manner to those used by a Channel (\secref{ss:channelsource} +and \secref{ss:channelsink}). You supply these routines, as arguments +to the constructor function AST\_FITSCHAN when you create the FitsChan +(\secref{ss:creatingafitschan}). The source routine is invoked +implicitly at this point to fill the FitsChan with FITS cards and the +FitsChan is then rewound, so that the first card becomes current. The +sink routine is automatically invoked later, when the FitsChan is +deleted, in order to write out any cards that remain in it. +f- + +c+ +The only real difference between the source and sink functions for a +FitsChan and a basic Channel is that FITS cards are limited in length +to 80~characters, so the choice of buffer size is simplified. The +``Source'' and ``Sink'' functions in \secref{ss:channelsource} and +\secref{ss:channelsink} could therefore be used to access FITS headers +stored in text files simply by changing LEN to be 80. If you were not +accessing a text file, however, appropriate changes to the I/O +statements would be needed since the separating newline characters +would be absent. The details obviously depend on the format of the +file you are handling, which need not necessarily be a true FITS file. +c- + +f+ +The only real difference between the source and sink routines for a +FitsChan and a basic Channel is that FITS cards are limited in length +to 80~characters, so the choice of buffer size is simplified. This +affects the way the card contents are passed, so the routines +themselves are slightly different. The following is therefore the +FitsChan equivalent of the Channel SOURCE routine given in +\secref{ss:channelsource}: + +\small +\begin{terminalv} + INTEGER FUNCTION FITSSOURCE( CARD, STATUS ) + CHARACTER * ( 80 ) CARD + INTEGER STATUS + + READ( 1, '(A)', END = 99 ) CARD + FITSSOURCE = 1 + RETURN + + 99 FITSSOURCE = 0 + END +\end{terminalv} +\normalsize + +Here, the FITS card contents are returned \emph{via} the CARD argument +(the AST\_PUTLINE routine should not be used) and the function returns +1 to indicate that a card has been read. A value of zero is returned +if there are no more cards to read. + +The sink routine for a FitsChan is also a little different +(\emph{c.f.}\ the SINK routine in~\secref{ss:channelsink}), as +follows: + +\small +\begin{terminalv} + SUBROUTINE FITSSINK( CARD, STATUS ) + CHARACTER * ( 80 ) CARD + INTEGER STATUS + + WRITE( 2, '(A)' ) CARD + + END +\end{terminalv} +\normalsize + +The contents of the FITS card being written are passed \emph{via}\ the +CARD argument (the AST\_GETLINE routine should not be used). + +Of course, both of these examples assume that you are accessing text +files. If this is not the case, then appropriate changes to the I/O +statements would be needed. The details obviously depend on the +format of the file you are handling, which need not necessarily be a +true FITS file. +f- + +\cleardoublepage +\section{\label{ss:foreignfits}Using Foreign FITS Encodings} + +We saw in the previous section (\secref{ss:nativefits}) how to store +and retrieve any kind of AST Object in a FITS header by using a +FitsChan. To achieve this, we set the FitsChan's Encoding attribute to +NATIVE. However, the Objects we wrote could then only be read back by +other programs that use AST. + +In practice, we will also encounter FITS headers containing WCS +information written by other software systems. We will probably also +need to write FITS headers in a format that can be understood by these +systems. Indeed, this interchange of data is one of the main reasons +for the existence of FITS, so in this section we will examine how to +accommodate these requirements. + +\subsection{\label{ss:foreignencodings}The Foreign FITS Encodings} + +As mentioned previously (\secref{ss:nativeencoding}), there are a +number of conventions currently in use for storing WCS information in +FITS headers, which we call \emph{encodings}. Here, we are concerned +with those encodings defined by software systems other than AST, which +we term \emph{foreign encodings}. + +Currently, AST supports six foreign encodings, which may be selected +by setting the Encoding attribute of a FitsChan to one of the +following (character string) values: + +\begin{quote} +\begin{description} +\item[DSS]\mbox{}\\ +This encoding stores WCS information using the convention developed at +the Space Telescope Science Institute for the Digitised Sky Survey +(DSS) astrometric plate calibrations. DSS images which use this +convention are widely available and it is understood by a number of +important and well-established astronomy applications. + +However, the calibration model used (based on a polynomial fit) is not +easily applicable to other types of data and creating the polynomial +coefficients needed to calibrate your own images can prove +difficult. For this reason, the DSS encoding is probably best viewed +as a ``read-only'' format. It is possible, however, to read in WCS +information using this encoding and then to write it back out again, +so long as only minor changes have been made. + +\item[FITS-WCS]\mbox{}\\ +This encoding is very important because it is based on a new FITS standard +which should, for the first time, address the problem of celestial coordinate +systems in a proper manner, by considerably extending the original FITS +standard. + +The conventions used are described in a series of papers by +E.W.\,Greisen, M.\,Calabretta, \emph{et. al.}, often referred to as the +``FITS-WCS papers''. They are described at +\url{http://fits.gsfc.nasa.gov/fits_wcs.html}. Now that the first two papers +in this series have been agreed, this encoding should be understood by any +FITS-WCS compliant software and it is likely to be adopted widely for FITS +data in future. For details of the coverage of these conventions provided +by the FitsChan class, see \appref{ss:fitswcscoverage}. + +\item[FITS-IRAF]\mbox{}\\ +This encoding is based on the conventions described in the document +``World Coordinate Systems Representations Within the FITS Format'' by R.J. +Hanisch and D.G. Wells, 1988.\footnote{Available by ftp from +fits.cv.nrao.edu /fits/documents/wcs/wcs88.ps.Z} It is employed +by the IRAF data analysis facility, so its use will facilitate data +exchange with IRAF. This encoding is in effect a sub-set of the current +FITS-WCS encoding. + +\item[FITS-PC]\mbox{}\\ +This encoding is based on a previous version of the proposed new FITS WCS +standard which used \texttt{PCjjjjiii} and \texttt{CDELTj} keywords to describe +axis rotation and scaling. Versions of AST prior to V1.5 used this scheme +for the FITS-WCS encoding. As of V1.5, FITS-WCS uses \texttt{CDi\_j} +keywords instead.\footnote{There are many other differences between the +previous and the current FITS-WCS encodings. The keywords to describe +axis rotation and scaling is used purely as a label to identify the +scheme.} The FITS-PC encoding is included in AST V1.5 only to allow +FITS-WCS data created with previous versions to be read. It should not, +in general, be used to create new data sets. + +\item[FITS-AIPS]\mbox{}\\ +This encoding is based on the conventions described in the document +``Non-linear Coordinate Systems in AIPS'' by Eric W. Greisen (revised 9th +September, 1994).\footnote{Available by ftp from fits.cv.nrao.edu +/fits/documents/wcs/aips27.ps.Z} It is currently employed by the AIPS +data analysis facility, so its use will facilitate data exchange with +AIPS. This encoding uses \texttt{CROTAi} and \texttt{CDELTi} keywords to +describe axis rotation and scaling. + +\item[FITS-AIPS++]\mbox{}\\ +Encodes coordinate system information in FITS +header cards using the conventions used by the AIPS++ project. +This is an extension of FITS-AIPS which includes some of the +features of FITS-PC and FITS-IRAF. +\end{description} +\end{quote} + +For more detail about the above encodings, see the description of the +Encoding attribute in \appref{ss:attributedescriptions}. + +\subsection{\label{ss:foreignfitslimitations}Limitations of Foreign Encodings} + +The foreign encodings available for storing WCS information in FITS +headers have a number of limitations when compared with the native +encoding of AST Objects (\secref{ss:nativefits}). The main ones are: + +\begin{enumerate} +\item Only one class of AST Object, the FrameSet, may be represented +using a foreign FITS encoding. This should not come as a surprise, +because the purpose of storing WCS information in FITS headers is to +attach coordinate systems to an associated array of data. Since the +FrameSet is the AST Object designed for the same purpose +(\secref{ss:baseandcurrent}), there is a natural correspondence. + +The way in which a FrameSet is translated to and from the foreign +encoding also follows from this correspondence. The FrameSet's base +Frame identifies the data grid coordinates of the associated FITS +data. These are the same as FITS pixel coordinates, in which the first +pixel (in 2 dimensions) has coordinates (1,1) at its +centre. Similarly, the current Frame of the FrameSet identifies the +FITS world coordinate system associated with the data. + +\item You may store a representation of only a single FrameSet in any +individual set of FITS header cards (\emph{i.e.}\ in a single +FitsChan) at one time. If you attempt to store more than one, you may +over-write the previous one or generate an invalid representation of +your WCS information. + +This is mainly a consequence of the use of fixed FITS keywords by +foreign encodings and the fact that you cannot, in general, have +multiple FITS cards with the same keyword. + +\item In general, it will not be possible to store every possible +FrameSet that you might construct. Depending on the encoding, only +certain FrameSets that conform to particular restrictions can be +represented and, even then, some of their information may be lost. See +the description of the Encoding attribute in +\appref{ss:attributedescriptions} for more details of these +limitations. +\end{enumerate} + +It should be understood that using foreign encodings to read and write +information held in AST Objects is essentially a process of converting +the data format. As such, it potentially suffers from the same +problems faced by all such processes, \emph{i.e.}\ differences between +the AST data model and that of the foreign encoding may cause some +information to be lost. Because the AST model is extremely flexible, +however, any data loss can largely be eliminated when reading. +Instead, this effect manifests itself in the form of the above +encoding-dependent restrictions on the kind of AST Objects which may +be written. + +One of the aims of the AST library, of course, is to insulate you from +the details of these foreign encodings and the restrictions they +impose. We will see shortly, therefore, how AST provides a mechanism +for determining whether your WCS information satisfies the necessary +conditions and allows you to make an automatic choice of which +encoding to use. + +\subsection{\label{ss:identifyingfitsencoding}Identifying Foreign Encodings on Input} + +Let us now examine the practicalities of extracting WCS information +from a set of FITS header cards which have been written by some other +software system. We will pretend that our program does not know which +encoding has been used for the WCS information and must discover this +for itself. In order to have a concrete example, however, we will use +the following set of cards. These use the FITS-AIPS encoding and +contain a typical mix of other FITS cards which are irrelevant to the +WCS information in which we are interested: + +\small +\begin{terminalv} +SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 +BITPIX = -32 / Bits per pixel. +NAXIS = 2 / Number of dimensions +NAXIS1 = 300 / Length of x axis. +NAXIS2 = 300 / Length of y axis. +CTYPE1 = 'GLON-ZEA' / X-axis type +CTYPE2 = 'GLAT-ZEA' / Y-axis type +CRVAL1 = -149.56866 / Reference pixel value +CRVAL2 = -19.758201 / Reference pixel value +CRPIX1 = 150.500 / Reference pixel +CRPIX2 = 150.500 / Reference pixel +CDELT1 = -1.20000 / Degrees/pixel +CDELT2 = 1.20000 / Degrees/pixel +CROTA1 = 0.00000 / Rotation in degrees. +SURVEY = 'COBE DIRBE' +BUNITS = 'MJy/sr ' / +ORIGIN = 'CDAC ' / Cosmology Data Analysis Center +TELESCOP= 'COBE ' / COsmic Background Explorer satellite +INSTRUME= 'DIRBE ' / COBE instrument [DIRBE, DMR, FIRAS] +PIXRESOL= 9 / Quad tree pixel resolution [6, 9] +DATE = '27/09/94' / FITS file creation date (dd/mm/yy) +DATE-MAP= '16/09/94' / Date of original file creation (dd/mm/yy) +COMMENT COBE specific keywords +DATE-BEG= '08/12/89' / date of initial data represented (dd/mm/yy) +DATE-END= '25/09/90' / date of final data represented (dd/mm/yy) +\end{terminalv} +\normalsize + +c+ +The first step is to create a FitsChan and insert these cards into +it. If ``cards'' is an array of pointers to character strings holding +the header cards and ``ncards'' is the number of cards, this could be +done as follows: +c- +f+ +The first step is to create a FitsChan and insert these cards into +it. If CARDS is an array of character strings holding the header cards +and NCARDS is the number of cards, this could be done as follows: +f- + +c+ +\small +\begin{terminalv} +#include "ast.h" +#define MAXCARD 100 +AstFitsChan *fitschan; +char *cards[ MAXCARD ]; +int icard, ncard; + +... + +fitschan = astFitsChan( NULL, NULL, "" ); +for ( icard = 0; icard < ncard; icard++ ) astPutFits( fitschan, cards[ icard ], 0 ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER FITSCHAN, ICARD, NCARD, STATUS + CHARACTER * ( 80 ) CARDS( NCARD ) + + STATUS = 0 + + ... + + FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) + DO 1 ICARD = 1, NCARD + CALL AST_PUTFITS( FITSCHAN, CARDS( ICARD ), .FALSE., STATUS ) + 1 CONTINUE +\end{terminalv} +\normalsize +f- + +Note that we have not initialised the Encoding attribute of the +FitsChan as we did in \secref{ss:creatingafitschan} when we wanted to +use the native encoding. This is because we are pretending not to know +which encoding to use and want AST to determine this for us. By +leaving the Encoding attribute un-set, its default value will adjust +to whichever encoding AST considers to be most appropriate, according +to the FITS header cards present. For details of how this choice is +made, see the description of the Encoding attribute in +\appref{ss:attributedescriptions}. + +This approach has the obvious advantages of making our program simpler +and more flexible and of freeing us from having to know about the +different encodings available. As a bonus, it also means that the +program will be able to read any new encodings that AST may support in +future, without needing to be changed. + +At this point, we could enquire the default value of the Encoding +attribute, which indicates which encoding AST intends to use, as +follows: + +c+ +\small +\begin{terminalv} +const char *encode; + +... + + +encode = astGetC( fitschan, "Encoding" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 20 ) ENCODE + + ... + + ENCODE = AST_GETC( FITSCHAN, 'Encoding', STATUS ) +\end{terminalv} +\normalsize +f- + +The result of this enquiry would be the string ``FITS-AIPS''. Note +that we could also have set the FitsChan's Encoding attribute +explicitly, such as when creating it: + +c+ +\small +\begin{terminalv} +fitschan = astFitsChan( NULL, NULL, "Encoding=FITS-AIPS" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, 'Encoding=FITS-AIPS', STATUS ) +\end{terminalv} +\normalsize +f- + +If we tried to read information using this encoding +(\secref{ss:readingforeignfits}), but failed, we could then change the +encoding and try again. This would allow our program to take control +of how the optimum choice of encoding is arrived at. However, it would +also involve using explicit knowledge of the encodings available and +this is best avoided if possible. + +\subsection{\label{ss:readingforeignfits}Reading Foreign WCS Information from a FITS Header} + +c+ +Having stored a set of FITS header cards in a FitsChan and determined +how the WCS information is encoded +(\secref{ss:identifyingfitsencoding}), the next step is to read an AST +Object from the FitsChan using astRead. We must also remember to +rewind the FitsChan first, if necessary, such as by clearing its Card +attribute, which defaults to 1: +c- +f+ +Having stored a set of FITS header cards in a FitsChan and determined +how the WCS information is encoded +(\secref{ss:identifyingfitsencoding}), the next step is to read an AST +Object from the FitsChan using AST\_READ. We must also remember to +rewind the FitsChan first, if necessary, such as by clearing its Card +attribute, which defaults to 1: +f- + +c+ +\small +\begin{terminalv} +AstObject *wcsinfo; + +... + +astClear( fitschan, "Card" ); +wcsinfo = astRead( fitschan ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER WCSINFO + + ... + + CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) + WCSINFO = AST_READ( FITSCHAN, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +If the pointer returned by astRead is not equal to AST\_\_NULL, then +an Object has been read successfully. Otherwise, there was either no +information to read or the choice of FITS encoding +(\secref{ss:identifyingfitsencoding}) was inappropriate. +c- +f+ +If the pointer returned by AST\_READ is not equal to AST\_\_NULL, then +an Object has been read successfully. Otherwise, there was either no +information to read or the choice of FITS encoding +(\secref{ss:identifyingfitsencoding}) was inappropriate. +f- + +At this point you might like to indulge in a little data validation +along the lines described in \secref{ss:validatinginput}, for example: + +c+ +\small +\begin{terminalv} +if ( !strcmp( astGetC( wcsinfo, "Class" ), "FrameSet" ) ) { + +} else { + +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + IF ( AST_GETC( WCSINFO, 'Class', STATUS ) .EQ. 'FrameSet' ) THEN + + ELSE + + END IF +\end{terminalv} +\normalsize +f- + +If a foreign encoding has definitely been used, then the Object will +automatically be a FrameSet (\secref{ss:foreignfitslimitations}), so +this stage can be omitted. However, if the native encoding +(\secref{ss:nativeencoding}) might have been employed, which is a +possibility if you accept the FitsChan's default Encoding value, then +any class of Object might have been read and a quick check would be +worthwhile. + +c+ +If you used astShow (\secref{ss:displayingobjects}) to examine the +FrameSet which results from reading our example FITS header +(\secref{ss:identifyingfitsencoding}), you would find that its base +Frame describes the image's pixel coordinate system and that its +current Frame is a SkyFrame representing galactic coordinates. These +two Frames are inter-related by a Mapping (actually a CmpMap) which +incorporates the effects of various rotations, scalings and a +``zenithal equal area'' sky projection, so that each pixel of the FITS +image is mapped on to a corresponding sky position in galactic +coordinates. +c- +f+ +If you used AST\_SHOW (\secref{ss:displayingobjects}) to examine the +FrameSet which results from reading our example FITS header +(\secref{ss:identifyingfitsencoding}), you would find that its base +Frame describes the image's pixel coordinate system and that its +current Frame is a SkyFrame representing galactic coordinates. These +two Frames are inter-related by a Mapping (actually a CmpMap) which +incorporates the effects of various rotations, scalings and a +``zenithal equal area'' sky projection, so that each pixel of the FITS +image is mapped on to a corresponding sky position in galactic +coordinates. +f- + +Because this FrameSet may be used both as a Mapping +(\secref{ss:framesetasmapping}) and as a Frame +(\secref{ss:framesetasframe}), it may be employed directly to perform +many useful operations without any need to decompose it into its +component parts. These include: + +\begin{itemize} +\item Transforming data grid (FITS pixel) coordinates into galactic +coordinates and \emph{vice versa} (\secref{ss:framesetasmapping}). + +\item Formatting coordinate values (either pixel or galactic +coordinates) ready for display to a user +(\secref{ss:formattingaxisvalues} and \secref{ss:normalising}). + +\item Enquiring about axis labels (or other axis +information---\secref{ss:frameattributes}) which might be used, for +example, to label columns of coordinates in a table +(\secref{ss:frameaxisattributes}). + +\item Aligning the image with another image from which a similar +FrameSet has been obtained (\secref{ss:registeringimages}). + +\item Creating a Plot (\secref{ss:plots}), which can be used to overlay +a variety of graphical information (including a coordinate +grid---Figure~\ref{fig:gridplot}) on the displayed image. + +\item Generating a new FrameSet which reflects any geometrical +processing you perform on the associated image data +(\secref{ss:wcsprocessingexample}). This new FrameSet could then be +written out as FITS headers to describe the modified image +(\secref{ss:writingforeignfits}). +\end{itemize} + +If the FrameSet contains other Frames (apart from the base and current +Frames), then you would also have access to information about other +coordinate systems associated with the image. + +\subsection{\label{ss:destructiveread}Removing WCS Information from FITS Headers---the Destructive Read} + +It is instructive at this point to examine the contents of a FitsChan +after we have read a FrameSet from it +(\secref{ss:readingforeignfits}). The following would rewind our +FitsChan and display its contents: + +c+ +\small +\begin{terminalv} +#include +char card[ 81 ]; + +... + +astClear( fitschan, "Card" ); +while ( astFindFits( fitschan, "%f", card, 1 ) ) (void) printf( "%s\n", card ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER CARD * ( 80 ) + + ... + + CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) + 2 CONTINUE + IF ( AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS ) ) THEN + WRITE ( *, '(A)' ) CARD + GO TO 2 + END IF +\end{terminalv} +\normalsize +f- + +The output, if we started with the example FITS header in +\secref{ss:identifyingfitsencoding}, might look like this: + +\small +\begin{terminalv} +SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 +BITPIX = -32 / Bits per pixel. +NAXIS = 2 / Number of dimensions +NAXIS1 = 300 / Length of x axis. +NAXIS2 = 300 / Length of y axis. +SURVEY = 'COBE DIRBE' +BUNITS = 'MJy/sr ' +ORIGIN = 'CDAC ' / Cosmology Data Analysis Center +TELESCOP= 'COBE ' / COsmic Background Explorer satellite +INSTRUME= 'DIRBE ' / COBE instrument [DIRBE, DMR, FIRAS] +PIXRESOL= 9 / Quad tree pixel resolution [6, 9] +DATE = '27/09/94' / FITS file creation date (dd/mm/yy) +DATE-MAP= '16/09/94' / Date of original file creation (dd/mm/yy) +COMMENT COBE specific keywords +DATE-BEG= '08/12/89' / date of initial data represented (dd/mm/yy) +DATE-END= '25/09/90' / date of final data represented (dd/mm/yy) +\end{terminalv} +\normalsize + +c+ +Comparing this with the original, you can see that all the FITS cards +that represent WCS information have been removed. They have +effectively been ``sucked out'' of the FitsChan by the destructive +read that astRead performs and converted into an equivalent +FrameSet. AST remembers where they were stored, however, so that if we +later write WCS information back into the FitsChan +(\secref{ss:writingforeignfits}) they will, as far as possible, go +back into their original locations. This helps to preserve the overall +layout of the FITS header. +c- +f+ +Comparing this with the original, you can see that all the FITS cards +that represent WCS information have been removed. They have +effectively been ``sucked out'' of the FitsChan by the destructive +read that AST\_READ performs and converted into an equivalent +FrameSet. AST remembers where they were stored, however, so that if we +later write WCS information back into the FitsChan +(\secref{ss:writingforeignfits}) they will, as far as possible, go +back into their original locations. This helps to preserve the +overall layout of the FITS header. +f- + +c+ +You can now see why astRead performs destructive reads. It is a +mechanism for removing WCS information from a FITS header while +insulating you, as a programmer, from the details of the encoding +being used. It means you can ensure that all relevant header cards +have been removed, giving you a clean slate, without having to know +which FITS keywords any particular encoding uses. +c- +f+ +You can now see why AST\_READ performs destructive reads. It is a +mechanism for removing WCS information from a FITS header while +insulating you, as a programmer, from the details of the encoding +being used. It means you can ensure that all relevant header cards +have been removed, giving you a clean slate, without having to know +which FITS keywords any particular encoding uses. +f- + +Clearing this WCS information out of a FITS header is particularly +important when considering how to write new WCS information back after +processing (\secref{ss:writingforeignfits}). If any relevant FITS +cards are left over from the input dataset and find their way into the +new processed header, they could interfere with the new information +being written.\footnote{This can happen if a particular keyword is +present in the input header but is not used in the output header +(whether particular keywords are used can depend on the WCS +information being stored). In such a case, the original value would +not be over-written by a new output value, so would remain erroneously +present.} The destructive read mechanism ensures that this doesn't +happen. + +\subsection{\label{ss:propagatingwcsinformation}Propagating WCS Information through Data Processing Steps} + +One of the purposes of AST is to make it feasible to propagate WCS +information through successive stages of data processing, so that it +remains consistent with the associated image data. As far as possible, +this should happen regardless of the FITS encoding used to store the +original WCS information. + +If the data processing being performed does not change the +relationship between image pixel and world coordinates (whatever these +may be), then propagation of the WCS information is +straightforward. You can simply copy the FITS header from input to +output. + +If this relationship changes, however, then the WCS information must +be processed alongside the image data and a new FITS header generated +to represent it. In this case, the sequence of operations within your +program would probably be as follows: + +\begin{enumerate} +\item Read the image data and associated FITS header from the input +dataset, putting the header cards into a FitsChan +(\secref{ss:identifyingfitsencoding}). + +\item Read an AST Object, a FrameSet, from the FitsChan (typically +using a foreign FITS encoding---\secref{ss:readingforeignfits}). + +\item Process the image data and modify the FrameSet accordingly +(\emph{e.g.}~\secref{ss:wcsprocessingexample}). + +\item Write the FrameSet back into the FitsChan +(\secref{ss:writingforeignfits}). + +\item Perform any other modification of FITS header cards your program +may require. + +\item Write the FitsChan contents (\emph{i.e.}\ processed header +cards) and image data to the output dataset. +\end{enumerate} + +In stage (2), the original WCS information will be removed from the +FitsChan by a destructive read. Later, in stage (4), new WCS +information is written to replace it. This is the process which we +consider next (\secref{ss:writingforeignfits}). + +\subsection{\label{ss:writingforeignfits}Writing Foreign WCS Information to a FITS Header} + +Before we can write processed WCS information held in a FrameSet back +into a FitsChan in preparation for output, we must select the FITS +encoding to use. Unfortunately, we cannot simply depend on the +default value of the Encoding attribute, as we did when reading the +input information (\secref{ss:identifyingfitsencoding}), because the +destructive action of reading the WCS data +(\secref{ss:destructiveread}) will have altered the FitsChan's +contents. This, in turn, will have changed the choice of default +encoding, probably causing it to revert to NATIVE. + +c+ +We will return to the question of the optimum choice of encoding +below. For now, let's assume that we want to use the same encoding +for output as we used for input. Since we enquired what that was +before we read the input WCS data from the FitsChan +(\secref{ss:identifyingfitsencoding}), we can now set that value +explicitly. We can also set the FitsChan's Card attribute back to 1 at +the same time (because the write will fail if the FitsChan is not +rewound). astWrite can then be used to write the output WCS +information into the FitsChan: +c- +f+ +We will return to the question of the optimum choice of encoding +below. For now, let's assume we want to use the same encoding for +output as we used for input. Since we enquired what that was before we +read the input WCS data from the FitsChan +(\secref{ss:identifyingfitsencoding}), we can now set that value +explicitly. We can also set the FitsChan's Card attribute back to 1 at +the same time (because the write will fail if the FitsChan is not +rewound). AST\_WRITE can then be used to write the output WCS +information into the FitsChan: +f- + +c+ +\small +\begin{terminalv} +int nobj; + +... + +astSet( fitschan, "Card=1, Encoding=%s", encode ); +nobj = astWrite( fitschan, wcsinfo ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER NOBJ + + ... + + + CALL AST_SET( FITSCHAN, 'Card=1, Encoding=' // ENCODE, STATUS ) + NOBJ = AST_WRITE( FITSCHAN, WCSINFO, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The value returned by astWrite (assigned to ``nobj'') indicates how +many Objects were written. This will either be 1 or zero. A value of +zero is used to indicate that the information could not be encoded in +the form you requested. If this happens, nothing will have been +written. +c- +f+ +The value returned by AST\_WRITE (assigned to NOBJ) indicates how many +Objects were written. This will either be 1 or zero. A value of zero +is used to indicate that the information could not be encoded in the +form you requested. If this happens, nothing will have been written. +f- + +If your choice of encoding proves inadequate, the probable reason is +that the changes you have made to the FrameSet have caused it to +depart from the data model which the encoding assumes. AST knows +about the data model used by each encoding and will attempt to +simplify the FrameSet you provide so as to fit into that model, thus +relieving you of the need to understand the details and limitations of +each encoding yourself.\footnote{Storing values in the FitsChan for +FITS headers NAXIS1, NAXIS2, \emph{etc.} (the grid dimensions in pixels), +before invoking +c+ +astWrite +c- +f+ +AST\_WRITE +f- +can sometimes help to produce a successful write.} When this attempt fails, +however, you must consider what alternative encoding to use. + +Ideally, you would probably want to try a sequence of alternative +encodings, using an approach such as the following: + +c+ +\small +\begin{terminalv} +/* 1. */ +astSet( fitschan, "Card=1, Encoding=FITS-IRAF" ); +if ( !astWrite( fitschan, wcsinfo ) ) { + +/* 2. */ + astSetC( fitschan, "Encoding", encode ); + if ( !astWrite( fitschan, wcsinfo ) ) { + +/* 3. */ + astSet( fitschan, "Encoding=NATIVE" ); + (void) astWrite( fitschan, wcsinfo ); + } +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} +* 1. + CALL AST_SET( FITSCHAN, 'Card=1, Encoding=FITS-WCS', STATUS ) + IF ( AST_WRITE( FITSCHAN, WCSINFO, STATUS ) .EQ. 0 ) THEN + +* 2. + CALL AST_SETC( FITSCHAN, 'Encoding', ENCODE, STATUS ) + IF ( AST_WRITE( FITSCHAN, WCSINFO, STATUS ) .EQ. 0 ) THEN + +* 3. + CALL AST_SET( FITSCHAN, 'Encoding=NATIVE', STATUS ) + NOBJ = AST_WRITE( FITSCHAN, WCSINFO, STATUS ) + END IF + END IF +\end{terminalv} +\normalsize +f- + +That is: + +\begin{enumerate} +\item Start by trying the FITS-WCS encoding, on the grounds that FITS +should provide a universal interchange standard in which all WCS +information should be expressed if possible. + +\item If that fails, then try the original encoding used for the input +WCS information, on the grounds that you are at least not making the +information any harder for others to read than it originally was. + +\item If that also fails, then you are probably trying to store fairly +complex information for which you need the native encoding. Only other +AST programs will then be able to read this information, but these are +probably the only programs that will be able to do anything sensible +with it anyway. +\end{enumerate} + +c+ +An alternative approach might be to encode the WCS information in several +ways, since this gives the maximum chance that other software will be +able to read it. This approach is only possible if there is no +significant conflict between the FITS keywords used by the different +encodings\footnote{In practice, this means you should avoid mixing +FITS-IRAF, FITS-WCS, FITS-AIPS, FITS-AIPS++ and FITS-PC encodings since they share +many keywords.}. Adopting this approach would simply require multiple +calls to astWrite, rewinding the FitsChan and changing its Encoding value +before each one. +c- +f+ +An alternative approach might be to encode the WCS information in several +ways, since this gives the maximum chance that other software will be +able to read it. This approach is only possible if there is no +significant conflict between the FITS keywords used by the different +encodings\footnote{In practice, this means you should avoid mixing +FITS-IRAF, FITS-WCS, FITS-AIPS, FITS-AIPS++ and FITS-PC encodings since they share +many keywords.}. Adopting this approach would simply require multiple +calls to AST\_WRITE, rewinding the FitsChan and changing its Encoding value +before each one. +f- + +Unfortunately, however, there is a drawback to duplicating WCS +information in the FITS header in this way, because any program which +modifies one version of this information and simply copies the +remainder of the header will risk producing two inconsistent sets of +information. This could obviously be confusing to subsequent +software. Whether you consider this a worthwhile risk probably depends +on the use to which you expect your data to be put. + +\cleardoublepage +\section{\label{ss:xmlchan}Storing AST Objects as XML (XmlChan)} + +\htmladdnormallinkfoot{XML}{http://www.w3.org/XML/} +is fast becoming the standard format for passing structured data around +the internet, and much general purpose software has been written for +tasks such as the parsing, editing, display and transformation of XML +data. The XmlChan class (a specialised form of Channel) provides +facilities for storing AST objects externally in the form of XML documents, +thus allowing such software to be used. + +The primary XML format used by the XmlChan class is a fairly close +transliteration of the AST native format produced by the basic Channel +class. Currently, there is no DTD or schema defining the structure of data +produced in this format by an XmlChan. The following is a native AST +representation of a simple 1-D Frame (including comments and with the Full +attribute set to zero so that some default attribute values are included +as extra comments): + +\small +\begin{terminalv} + Begin Frame # Coordinate system description +# Title = "1-d coordinate system" # Title of coordinate system + Naxes = 1 # Number of coordinate axes + Domain = "SCREEN" # Coordinate system domain +# Lbl1 = "Axis 1" # Label for axis 1 +# Uni1 = "cm" # Units for axis 1 + Ax1 = # Axis number 1 + Begin Axis # Coordinate axis + Unit = "cm" # Axis units + End Axis + End Frame +\end{terminalv} +\normalsize + +The corresponding XmlChan output would look like: + +\small +\begin{terminalv} + + <_attribute name="Title" quoted="true" value="1-d coordinate system" + desc="Title of coordinate system" default="true"/> + <_attribute name="Naxes" value="1" desc="Number of coordinate axes"/> + <_attribute name="Domain" quoted="true" value="SCREEN" + desc="Coordinate system domain"/> + <_attribute name="Lbl1" quoted="true" value="Axis 1" + desc="Label for axis 1" default="true"/> + <_attribute name="Uni1" quoted="true" value="cm" + desc="Units for axis 1" default="true"/> + + + <_attribute name="Unit" quoted="true" value="cm" desc="Axis units"/> + + +\end{terminalv} +\normalsize + + +Notes: + +\begin{enumerate} +\item The AST class name is used as the name for an XML element which contain +a description of an AST object. + +\item AST attributes are described by XML elements with the name +``\_attribute''. Unfortunately, the word ``attribute'' is also used by XML +to refer to a ``name=value'' pair within an element start tag. So for +instance, the ``Title'' attribute of the AST Frame object is described +within an XML element with name ``\_attribute'' in which the XML attribute +``name'' has the value ``Title'', and the XML attribute ``value'' has the +value ``1-d coordinate system''. The moral is always to be clear clear +about the context (AST or XML) in which the word \emph{attribute} is being +used! + +\item The XML includes comments both as XML attributes with the name ``desc'', +and as separate comment tags. + +\item Elements which describe default values are identified by the fact +that they have an XML attribute called ``default'' set to the value +``true''. These elements are ignored when being read back into an XmlChan. + +\item The outer-most XML element of an AST object will set the default +namespace to \verb+http://www.starlink.ac.uk/ast/xml/+ which will be +inherited by all nested elements. + +\end{enumerate} + + +The XmlChan class changes the default value for the Comment and Full +attributes (inherited from the base Channel class) to zero and -1, +resulting in terse output by default. With the default values for these +attributes, the above XML is reduced to the following: + +\small +\begin{terminalv} + + <_attribute name="Naxes" value="1"/> + <_attribute name="Domain" quoted="true" value="SCREEN"/> + + <_attribute name="Unit" quoted="true" value="cm"/> + + +\end{terminalv} +\normalsize + + +The XmlChan class uses the Skip attributes very similarly to the Channel +class. If Skip is zero (the default) then an error will be reported if the text +supplied by the source function does not begin with an AST Object. If +Skip is non-zero, then initial text is skipped over without error until +the start of an AST object is found. this allows an AST object to be +located within a larger XML document. + +\subsection{Reading IVOA Space-Time-Coordinates XML (STC-X) Descriptions} +The XmlChan class also provides support for reading (but not writing) XML +documents which use a restricted subset of an early draft (V1.20) of the +IVOA Space-Time-Coordinates XML (STC-X) system. The version of STC-X +finally adopted by the IVOA differs in several significant respects from +V1.20, and so the STC-X support currently provided by AST is mainly of +historical interest. Note, AST also supports the alternative ``STC-S'' +linear string description of the STC model (see \secref{ss:stcschans}). + +STC-X V1.20 is documented at +\url{http://www.ivoa.net/Documents/WD/STC/STC-20050225.html}, and the current +version is documented at +\url{http://www.ivoa.net/Documents/latest/STC-X.html}. + +When an STC-X document is read using an XmlChan, the read operation +produces an AST Object of the Stc class, which is itself a subclass of +Region. Specifically, each such Object will be an instance of +StcSearchLocation, StcResourceProfile, StcCatalogEntryLocation or +StcObsDataLocation. See the description of the XmlChan class and the +XmlFormat attribute for further details. + +\cleardoublepage +\section{\label{ss:stcschans}Reading and writing STC-S descriptions (StcsChans)} + +The StcsChan class provides facilities for reading and writing +IVOA ``STC-S'' descriptions. STC-S (see +\url{http://www.ivoa.net/Documents/latest/STC-S.html}) is a linear string +syntax that allows simple specification of the STC metadata describing a +region in an astronomical coordinate system. AST supports a +subset of the STC-S specification, allowing an STC-S description of a +region within an AST-supported astronomical coordinate system to be converted +into an equivalent AST Region object, and vice-versa. For further +details, see the full description of the StcsChan class in +\appref{ss:classdescriptions}. + + +\cleardoublepage +\section{\label{ss:intramaps}Creating Your Own Private Mappings (IntraMaps)} + +\subsection{The Need for Extensibility} + +However many Mapping classes are provided by AST, sooner or later you +will want to transform coordinates in some way that has not been +foreseen. You might want to plot a graph in some novel curvilinear +coordinate system (perhaps you already have a WCS system in your +software and just want to use AST for its graphical capabilities). +Alternatively, you might need to calibrate a complex dataset (like an +objective prism plate) where each position must be converted to world +coordinates with reference to calibration data under the control of an +elaborate algorithm. + +In such cases, it is clear that the basic pre-formed components +provided by AST for building Mappings are just not enough. What you +need is access to a programming language. However, if you write your +own software to transform coordinate values, then it must be made +available in the form of an AST class (from which you can create +Objects) before it can be used in conjunction with other AST +facilities. + +At this point you might consider writing your own AST class, but this +is not recommended. Not only would the internal conventions used by +AST take some time to master, but you might also find yourself having +to change your software whenever a new version of AST was +released. Fortunately, there is a much easier route provided by the +IntraMap class. + +\subsection{The IntraMap Model} + +c+ +To allow you to write your own Mappings, AST provides a special kind +of Mapping called an IntraMap. An IntraMap is a sort of ``wrapper'' +for a coordinate transformation function written in C. You write this +function yourself and then register it with AST. This, in effect, +creates a new class from which you can create Mappings +(\emph{i.e.}\ IntraMaps) which will transform coordinates in whatever +way your transformation function specifies. +c- +f+ +To allow you to write your own Mappings, AST provides a special kind +of Mapping called an IntraMap. An IntraMap is a sort of ``wrapper'' +for a coordinate transformation routine written in Fortran. You write +this routine yourself and then register it with AST. This, in effect, +creates a new class from which you can create Mappings +(\emph{i.e.}\ IntraMaps) which will transform coordinates in whatever +way your transformation routine specifies. +f- + +Because IntraMaps are Mappings, they may be used in the same way as +any other Mapping. For instance, they may be combined in series or +parallel with other Mappings using a CmpMap (\secref{ss:cmpmaps}), +they may be inverted (\secref{ss:invertingmappings}), you may enquire +about their attributes (\secref{ss:gettingattributes}), they may be +inserted into FrameSets (\secref{ss:framesets}), \emph{etc.} They do, +however, have some important limitations of which you should be aware +before we go on to consider how to create them. + +\subsection{\label{ss:intramaplimitations}Limitations of IntraMaps} + +c+ +By now, you might be wondering why any other kind of Mapping is +required at all. After all, why not simply write your own coordinate +transformation functions in C, wrap them up in IntraMaps and do away +with all the other Mapping classes in AST? +c- +f+ +By now, you might be wondering why any other kind of Mapping is +required at all. After all, why not simply write your own coordinate +transformation routines in Fortran, wrap them up in IntraMaps and do +away with all the other Mapping classes in AST? +f- + +c+ +The reason is not too hard to find. Any transformation function you +write is created solely by you, so it is a private extension which +does not form a permanent part of AST. If you use it to calibrate some +data and then pass that data to someone else, who has only the +standard version of AST, then they will not be able to interpret it. +c- +f+ +The reason is not too hard to find. Any transformation routine you +write is created solely by you, so it is a private extension which +does not form a permanent part of AST. If you use it to calibrate some +data and then pass that data to someone else, who has only the +standard version of AST, then they will not be able to interpret it. +f- + +c+ +Thus, while an IntraMap is fine for use by you and your collaborators +(who we assume have access to the same transformation functions), it +does not address the need for universal data exchange like other AST +Mappings do. This is where the ``Intra'' in the class name +``IntraMap'' comes from, implying private or internal usage. +c- +f+ +Thus, while an IntraMap is fine for use by you and your collaborators +(who we assume have access to the same transformation routines), it +does not address the need for universal data exchange like other AST +Mappings do. This is where the ``Intra'' in the class name +``IntraMap'' comes from, implying private or internal usage. +f- + +For this reason, it is unwise to store IntraMaps in datasets, unless +they will be used solely for communication between collaborating items +of software which share conventions about their use. A private +database describing coordinate systems on a graphics device might be +an example where IntraMaps would be suitable, because the data would +probably never be accessed by anyone else's software. Restricting +IntraMap usage to within a single program (\emph{i.e.} never writing +it out) is, of course, completely safe. + +c+ +If, by accident, an IntraMap should happen to escape as part of a +dataset, then the unsuspecting recipient is likely to receive an error +message when they attempt to read the data. However, AST will +associate details of the IntraMap's transformation function and its +author (if provided) with the data, so that the recipient can make an +intelligent enquiry to obtain the necessary software if this proves +essential. +c- +f+ +If, by accident, an IntraMap should happen to escape as part of a +dataset, then the unsuspecting recipient is likely to receive an error +message when they attempt to read the data. However, AST will +associate details of the IntraMap's transformation routine and its +author (if provided) with the data, so that the recipient can make an +intelligent enquiry to obtain the necessary software if this proves +essential. +f- + +c+ +\subsection{\label{ss:transformationfunctions}Writing a Transformation Function} +c- +f+ +\subsection{\label{ss:transformationfunctions}Writing a Transformation Routine} +f- + +c+ +The first stage in creating an IntraMap is to write the coordinate +transformation function. This should have a calling interface like the +astTranP function provided by AST (\emph{q.v.}). Here is a simple +example of a suitable transformation function which transforms +coordinates by squaring them: +c- +f+ +The first stage in creating an IntraMap is to write the coordinate +transformation routine. This should have a calling interface like the +AST\_TRANN function provided by AST (\emph{q.v.}). Here is a simple +example of a suitable transformation routine which transforms +coordinates by squaring them: +f- +\xlabel{SqrTran} + +c+ +\small +\begin{terminalv} +#include "ast.h" +#include + +void SqrTran( AstMapping *this, int npoint, int ncoord_in, + const double *ptr_in[], int forward, int ncoord_out, + double *ptr_out[] ) { + int point, coord; + double x; + +/* Forward transformation. */ + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + for ( coord = 0; coord < ncoord_in; coord++ ) { + x = ptr_in[ coord ][ point ]; + ptr_out[ coord ][ point ] = ( x == AST__BAD ) ? AST__BAD : x * x; + } + } + +/* Inverse transformation. */ + } else { + for ( point = 0; point < npoint; point++ ) { + for ( coord = 0; coord < ncoord_in; coord++ ) { + x = ptr_in[ coord ][ point ]; + ptr_out[ coord ][ point ] = + ( x < 0.0 || x == AST__BAD ) ? AST__BAD : sqrt( x ); + } + } + } +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SUBROUTINE SQRTRAN( THIS, NPOINT, NCOORD_IN, INDIM, IN, FORWARD, + : NCOORD_OUT, OUTDIM, OUT, STATUS ) + INTEGER THIS, NPOINT, NCOORD_IN, INDIM, NCOORD_OUT, OUTDIM, STATUS + DOUBLE PRECISION IN( INDIM, NCOORD_IN ), OUT( OUTDIM, NCOORD_OUT ) + LOGICAL FORWARD + + INCLUDE 'AST_PAR' + DOUBLE PRECISION X + INTEGER COORD, POINT + +* Forward transformation. + IF ( FORWARD ) THEN + DO 2 POINT = 1, NPOINT + DO 1 COORD = 1, NCOORD_IN + X = IN( POINT, COORD ) + IF ( X .EQ. AST__BAD ) THEN + OUT( POINT, COORD ) = AST__BAD + ELSE + OUT( POINT, COORD ) = X * X + ENDIF + 1 CONTINUE + 2 CONTINUE + +* Inverse transformation. + ELSE + DO 4 POINT = 1, NPOINT + DO 3 COORD = 1, NCOORD_IN + X = IN( POINT, COORD ) + IF ( X .LT. 0.0D0 .OR. X .EQ. AST__BAD ) THEN + OUT( POINT, COORD ) = AST__BAD + ELSE + OUT( POINT, COORD ) = SQRT( X ) + ENDIF + 3 CONTINUE + 4 CONTINUE + ENDIF + END +\end{terminalv} +\normalsize +f- + +c+ +As you can see, the function comes in two halves which implement the +forward and inverse coordinate transformations. The number of points +to be transformed (``npoint'') and the numbers of input and output +coordinates per point (``ncoord\_in'' and ``ncoord\_out''---in this +case both are assumed equal) are passed to the function. A pair of +loops then accesses all the coordinate values. Note that it is +legitimate to omit one or other of the forward/inverse transformations +and simply not to implement it, if it will not be required. It is also +permissible to require that the numbers of input and output +coordinates be fixed (\emph{e.g.}\ at 2), or to write the function so +that it can handle arbitrary dimensionality, as here. +c- +f+ +As you can see, the routine comes in two halves which implement the +forward and inverse coordinate transformations. The number of points +to be transformed (NPOINT) and the numbers of input and output +coordinates per point (NCOORD\_IN and NCOORD\_OUT---in this case both +are assumed equal) are passed to the routine. A pair of loops then +accesses all the coordinate values. Note that it is legitimate to +omit one or other of the forward/inverse transformations and simply +not to implement it, if it will not be required. It is also +permissible to require that the numbers of input and output +coordinates be fixed (\emph{e.g.}\ at 2), or to write the routine so +that it can handle arbitrary dimensionality, as here. +f- + +c+ +Before using an incoming coordinate, the function must first check +that it is not set to the value AST\_\_BAD, which indicates missing +data (\secref{ss:badcoordinates}). If it is, the same value is also +assigned to any affected output coordinates. The value AST\_\_BAD is +also generated if any coordinates cannot be transformed. In this +example, this can happen with the inverse transformation if negative +values are encountered, so that the square root cannot be taken. +c- +f+ +Before using an incoming coordinate, the routine must first check that +it is not set to the value AST\_\_BAD, which indicates missing data +(\secref{ss:badcoordinates}). If it is, the same value is also +assigned to any affected output coordinates. The value AST\_\_BAD is +also generated if any coordinates cannot be transformed. In this +example, this can happen with the inverse transformation if negative +values are encountered, so that the square root cannot be taken. +f- + +c+ +There are very few restrictions on what a coordinate transformation +function may do. For example, it may freely perform I/O to access any +external data needed, it may invoke other AST facilities (but beware +of unwanted recursion), \emph{etc.} Typically, you may also want to +pass information to it \emph{via}\ global variables. Remember, +however, that whatever facilities the transformation function requires +must be available in every program which uses it. +c- +f+ +There are very few restrictions on what a coordinate transformation +routine may do. For example, it may freely perform I/O to access any +external data needed, it may invoke other AST facilities (but beware +of unwanted recursion), \emph{etc.} Typically, you may also want to +pass information to it \emph{via}\ global variables held in common +blocks. Remember, however, that whatever facilities the +transformation routine requires must be available in every program +which uses it. +f- + +c+ +Generally, it is not a good idea to retain context information within +a transformation function. That is, it should transform each set of +coordinates as a single point and retain no memory of the points it +has transformed before. This is in order to conform with the AST model +of a Mapping. +c- +f+ +Generally, it is not a good idea to retain context information within +a transformation routine. That is, it should transform each set of +coordinates as a single point and retain no memory of the points it +has transformed before. This is in order to conform with the AST model +of a Mapping. +f- + +c+ +If an error occurs within a transformation function, it should use the +astSetStatus function (\secref{ss:errordetection}) to set the AST +status to an error value before returning. This will alert AST to the +error, causing it to abort the current operation. The error value +AST\_\_ITFER is available for this purpose, but other values may also +be used (\emph{e.g.}\ if you wish to distinguish different types of +error). +c- +f+ +If an error occurs within a transformation routine, it should set its +STATUS argument to an error value before returning. This will alert +AST to the error, causing it to abort the current operation. The error +value AST\_\_ITFER is available for this purpose, but other values may +also be used (\emph{e.g.}\ if you wish to distinguish different types +of error). The AST\_\_ITFER error value is defined in the AST\_ERR +include file. +f- + +c+ +\subsection{\label{ss:registeringintramaps}Registering a Transformation Function} +c- +f+ +\subsection{\label{ss:registeringintramaps}Registering a Transformation Routine} +f- + +c+ +Having written your coordinate transformation function, the next step +is to register it with AST. Registration is performed using +astIntraReg, as follows: +c- +f+ +Having written your coordinate transformation routine, the next step +is to register it with AST. Registration is performed using +AST\_INTRAREG, as follows: +f- + +c+ +\small +\begin{terminalv} +void SqrTran( AstMapping *, int, int, const double *[], int, int, double *[] ); + +const char *author, *contact, *purpose; + +... + +purpose = "Square each coordinate value"; +author = "R.F. Warren-Smith & D.S. Berry"; +contact = "http://www.starlink.ac.uk/cgi-bin/htxserver/sun211.htx/?xref_SqrTran"; + +astIntraReg( "SqrTran", 2, 2, SqrTran, 0, purpose, author, contact ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + EXTERNAL SQRTRAN + + CHARACTER * ( 80 ) AUTHOR, CONTACT, PURPOSE + + ... + + PURPOSE = 'Square each coordinate value' + AUTHOR = 'R.F. Warren-Smith & D.S. Berry' + CONTACT = 'http://www.starlink.ac.uk/cgi-bin/htxserver/' // + 'sun210.htx/?xref_SqrTran' + + CALL AST_INTRAREG( 'SqrTran', 2, 2, SQRTRAN, 0, + : PURPOSE, AUTHOR, CONTACT, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Note that you should also provide a function prototype to describe the +transformation function (the implementation of the function itself +would suffice, of course). +c- +f+ +Note that the transformation routine must also appear in a Fortran +EXTERNAL statement. +f- + +c+ +The first argument to astIntraReg is a name by which the +transformation function will be known. This will be used when we come +to create an IntraMap and is case sensitive. We recommend that you use +the actual function name here and make this sufficiently unusual that +it is unlikely to clash with any other functions in most people's +software. +c- +f+ +The first argument to AST\_INTRAREG is a name by which the +transformation routine will be known. This will be used when we come +to create an IntraMap and is case sensitive. We recommend that you +base this on the actual routine name and make this sufficiently +unusual that it is unlikely to clash with any other routines in most +people's software. +f- + +c+ +The next two arguments specify the number of input and output +coordinates which the transformation function will handle. These +correspond with the Nin and Nout attributes of the IntraMap we will +create. Here, we have set them both to 2, which means that we will +only be able to create IntraMaps with 2 input and 2 output coordinates +(despite the fact that the transformation function can actually handle +other dimensionalities). We will see later +(\secref{ss:variableintramapcoordinates}) how to remove this +restriction. +c- +f+ +The next two arguments specify the number of input and output +coordinates which the transformation routine will handle. These +correspond with the Nin and Nout attributes of the IntraMap we will +create. Here, we have set them both to 2, which means that we will +only be able to create IntraMaps with 2 input and 2 output coordinates +(despite the fact that the transformation routine can actually handle +other dimensionalities). We will see later +(\secref{ss:variableintramapcoordinates}) how to remove this +restriction. +f- + +c+ +The fourth argument should contain a set of flags which describe the +transformation function in a little more detail. We will return to +this shortly (\secref{ss:restrictedintramaps} \& +\secref{ss:simplifyingintramaps}). For now, we supply a value of zero. +c- +f+ +The fourth argument should contain a set of flags which describe the +transformation routine in a little more detail. We will return to this +shortly (\secref{ss:restrictedintramaps} \& +\secref{ss:simplifyingintramaps}). For now, we supply a value of zero. +f- + +c+ +The remaining arguments are character strings which document the +transformation function, mainly for the benefit of anyone who is +unfortunate enough to encounter a reference to it in their data which +they cannot interpret. As explained above +(\secref{ss:intramaplimitations}), you should try and avoid this, but +accidents will happen, so you should always provide strings containing +the following: +c- +f+ +The remaining arguments are character strings which document the +transformation routine, mainly for the benefit of anyone who is +unfortunate enough to encounter a reference to it in their data which +they cannot interpret. As explained above +(\secref{ss:intramaplimitations}), you should try and avoid this, but +accidents will happen, so you should always provide strings containing +the following: +f- + +\begin{enumerate} +c+ +\item A short description of what the transformation function is for. +c- +f+ +\item A short description of what the transformation routine is for. +f- +\item The name of the author. +\item Contact details, such as an e-mail or WWW address. +\end{enumerate} + +c+ +The idea is that anyone finding an IntraMap in their data, but lacking +the necessary transformation function, should be able to contact the +author and make a sensible enquiry in order to obtain it. If you +expect many enquiries, you may like to set up a World Wide Web page +and use that instead (in the example above, we use the WWW address of +the relevant part of this document). +c- +f+ +The idea is that anyone finding an IntraMap in their data, but lacking +the necessary transformation routine, should be able to contact the +author and make a sensible enquiry in order to obtain it. If you +expect many enquiries, you may like to set up a World Wide Web page +and use that instead (in the example above, we use the WWW address of +the relevant part of this document). +f- + +\subsection{Creating an IntraMap} + +c+ +Once a transformation function has been registered, creating an +IntraMap from it is simple: +c- +f+ +Once a transformation routine been registered, creating an IntraMap +from it is simple: +f- + +c+ +\small +\begin{terminalv} +AstIntraMap *intramap; + +... + +intramap = astIntraMap( "SqrTran", 2, 2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER INTRAMAP + + ... + + INTRAMAP = AST_INTRAMAP( 'SqrTran', 2, 2, ' ', STATUS ); +\end{terminalv} +\normalsize +f- + +c+ +We simply use the astIntraMap constructor function and pass it the +name of the transformation function to use. This name is the same +(case sensitive) one that we associated with the function when we +registered it using astIntraReg (\secref{ss:registeringintramaps}). +c- +f+ +We simply use the AST\_INTRAMAP constructor function and pass it the +name of the transformation routine to use. This name is the same (case +sensitive) one that we associated with the routine when we registered +it using AST\_INTRAREG (\secref{ss:registeringintramaps}). +f- + +c+ +You can, of course, register any number of transformation functions +and select which one to use whenever you create an IntraMap. You can +also create any number of independent IntraMaps using each +transformation function. In this sense, each transformation function +you register effectively creates a new ``sub-class'' of IntraMap, from +which you can create Objects just like any other class. However, an +error will occur if you attempt to use a transformation function that +has not yet been registered. +c- +f+ +You can, of course, register any number of transformation routines and +select which one to use whenever you create an IntraMap. You can also +create any number of independent IntraMaps using each transformation +routine. In this sense, each transformation routine you register +effectively creates a new ``sub-class'' of IntraMap, from which you +can create Objects just like any other class. However, an error will +occur if you attempt to use a transformation routine that has not yet +been registered. +f- + +c+ +The second and third arguments to astIntraMap are the numbers of input +and output coordinates. These define the Nin and Nout attributes for +the IntraMap that is created and they must match the corresponding +numbers given when the transformation function was registered. +c- +f+ +The second and third arguments to AST\_INTRAMAP are the numbers of +input and output coordinates. These define the Nin and Nout attributes +for the IntraMap that is created and they must match the corresponding +numbers given when the transformation routine was registered. +f- + +c+ +The final argument is the usual attribute initialisation string. You +may set attribute values for an IntraMap in exactly the same way as +for any other Mapping (\secref{ss:settingattributes}, and also see +\secref{ss:intraflag}). +c- +f+ +The penultimate argument is the usual attribute initialisation +string. You may set attribute values for an IntraMap in exactly the +same way as for any other Mapping (\secref{ss:settingattributes}, and +also see \secref{ss:intraflag}). +f- + +c+ +\subsection{\label{ss:restrictedintramaps}Restricted Implementations of Transformation Functions} +c- +f+ +\subsection{\label{ss:restrictedintramaps}Restricted Implementations of Transformation Routines} +f- + +c+ +You may not always want to use both the forward and inverse +transformations when you create an IntraMap, so it is possible to omit +either from the underlying coordinate transformation +function. Consider the following, for example: +c- +f+ +You may not always want to use both the forward and inverse +transformations when you create an IntraMap, so it is possible to omit +either from the underlying coordinate transformation routine. Consider +the following, for example: +f- + +c+ +\small +\begin{terminalv} +void Poly3Tran( AstMapping *this, int npoint, int ncoord_in, + const double *ptr_in[], int forward, int ncoord_out, + double *ptr_out[] ) { + double x; + int point; + +/* Forward transformation. */ + for ( point = 0; point < npoint; point++ ) { + x = ptr_in[ 0 ][ point ]; + ptr_out[ 0 ][ point ] = ( x == AST__BAD ) ? AST__BAD : + 6.18 + x * ( 0.12 + x * ( -0.003 + x * 0.0000101 ) ); + } +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SUBROUTINE POLY3TRAN( THIS, NPOINT, NCOORD_IN, INDIM, IN, FORWARD, + : NCOORD_OUT, OUTDIM, OUT, STATUS ) + INTEGER THIS, NPOINT, NCOORD_IN, INDIM, NCOORD_OUT, OUTDIM, STATUS + DOUBLE PRECISION IN( INDIM, NCOORD_IN ), OUT( OUTDIM, NCOORD_OUT ) + LOGICAL FORWARD + + INCLUDE 'AST_PAR' + DOUBLE PRECISION X + INTEGER POINT + +* Forward transformation. + DO 1 POINT = 1, NPOINT + X = IN( POINT, 1 ) + IF ( X .EQ. AST__BAD ) THEN + OUT( POINT, 1 ) = AST__BAD + ELSE + OUT( POINT, 1 ) = + : 6.18D0 + X * ( 0.12D0 + X * ( -0.003D0 + X * 0.0000101D0 ) ) + END IF + 1 CONTINUE + END +\end{terminalv} +\normalsize +f- + +c+ +This implements a 1-dimensional cubic polynomial transformation. Since +this is somewhat awkward to invert, however, we have only implemented +the forward transformation. When registering the function, this is +indicated via the ``flags'' argument to astIntraReg, as follows: +c- +f+ +This implements a 1-dimensional cubic polynomial transformation. Since +this is somewhat awkward to invert, however, we have only implemented +the forward transformation. When registering the routine, this is +indicated via the FLAGS argument to AST\_INTRAREG, as follows: +f- + +c+ +\small +\begin{terminalv} +void Poly3Tran( AstMapping *, int, int, const double *[], int, int, double *[] ); + +... + +astIntraReg( "Poly3Tran", 1, 1, Poly3Tran, AST__NOINV, + purpose, author, contact ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + EXTERNAL POLY3TRAN + + ... + + CALL AST_INTRAREG( 'Poly3Tran', 1, 1, POLY3TRAN, AST__NOINV, + : PURPOSE, AUTHOR, CONTACT, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, the fifth argument has been set to the flag value AST\_\_NOINV +to indicate the lack of an inverse. If the forward transformation were +absent, we would use AST\_\_NOFOR instead. Flag values for this +argument may be combined using a bitwise OR if necessary. +c- +f+ +Here, the fifth argument has been set to the flag value AST\_\_NOINV +to indicate the lack of an inverse. If the forward transformation were +absent, we would use AST\_\_NOFOR instead. Flag values for this +argument may be combined by summing them if necessary. +f- + +\subsection{\label{ss:variableintramapcoordinates}Variable Numbers of Coordinates} + +c+ +In our earlier examples, we have used a fixed number of input and +output coordinates when registering a coordinate transformation +function. It is not necessary to impose this restriction, however, if +the transformation function can cope with a variable number of +coordinates (as with the example in +\secref{ss:transformationfunctions}). We indicate the acceptability of +a variable number when registering the transformation function by +supplying the value AST\_\_ANY for the number of input and/or output +coordinates, as follows: +c- +f+ +In our earlier examples, we have used a fixed number of input and +output coordinates when registering a coordinate transformation +routine. It is not necessary to impose this restriction, however, if +the transformation routine can cope with a variable number of +coordinates (as with the example in +\secref{ss:transformationfunctions}). We indicate the acceptability of +a variable number when registering the transformation routine by +supplying the value AST\_\_ANY for the number of input and/or output +coordinates, as follows: +f- + +c+ +\small +\begin{terminalv} +astIntraReg( "SqrTran", AST__ANY, AST__ANY, SqrTran, 0, + purpose, author, contact ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_INTRAREG( 'SqrTran', AST__ANY, AST__ANY, SQRTRAN, 0, + : PURPOSE, AUTHOR, CONTACT, STATUS ) +\end{terminalv} +\normalsize +f- + +The result is that an IntraMap may now be created with any number of +input and output coordinates. For example: + +c+ +\small +\begin{terminalv} +AstIntraMap *intramap1, *intramap2; + +... + +intramap1 = astIntraMap( "SqrTran", 1, 1, "" ); +intramap2 = astIntraMap( "SqrTran", 3, 3, "Invert=1" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER INTRAMAP1, INTRAMAP2 + + ... + + INTRAMAP1 = AST_INTRAMAP( 'SqrTran', 1, 1, ' ', STATUS ) + INTRAMAP2 = AST_INTRAMAP( 'SqrTran', 3, 3, 'Invert=1', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +It is possible to fix either the number of input or output coordinates +(by supplying an explicit number to astIntraReg), but more subtle +restrictions on the number of coordinates, such as requiring that Nin +and Nout be equal, are not supported. This means that: +c- +f+ +It is possible to fix either the number of input or output coordinates +(by supplying an explicit number to AST\_INTRAREG), but more subtle +restrictions on the number of coordinates, such as requiring that Nin +and Nout be equal, are not supported. This means that: +f- + +c+ +\small +\begin{terminalv} +intramap = astIntraMap( "SqrTran", 1, 2, "" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTRAMAP = AST_INTRAMAP( 'SqrTran', 1, 2, ' ', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +will be accepted without error, although the transformation function +cannot actually handle such a combination sensibly. If this is +important, it would be worth adding a check within the transformation +function itself, so that the error would be detected when it came to +be used. +c- +f+ +will be accepted without error, although the transformation routine +cannot actually handle such a combination sensibly. If this is +important, it would be worth adding a check within the transformation +routine itself, so that the error would be detected when it came to be +used. +f- + +c+ +\subsection{\label{ss:intraflag}Adapting a Transformation Function to Individual IntraMaps} +c- +f+ +\subsection{\label{ss:intraflag}Adapting a Transformation Routine to Individual IntraMaps} +f- + +c+ +In the examples given so far, our coordinate transformation functions +have not made use of the ``this'' pointer passed to them (which +identifies the IntraMap whose transformation we are implementing). In +practice, this will often be the case. However, the presence of the +``this'' pointer allows the transformation function to invoke any +other AST function on the IntraMap, and this permits enquiries about +its attributes. The transformation function's behaviour can therefore +be modified according to any attribute values which are set. This +turns out to be a useful thing to do, so each IntraMap has a special +IntraFlag attribute reserved for exactly this purpose. +c- +f+ +In the examples given so far, our coordinate transformation routines +have not made use of the THIS pointer passed to them (which identifies +the IntraMap whose transformation we are implementing). In practice, +this will often be the case. However, the presence of the THIS pointer +allows the transformation routine to invoke any other AST routine on +the IntraMap, and this permits enquiries about its attributes. The +transformation routine's behaviour can therefore be modified according +to any attribute values which are set. This turns out to be a useful +thing to do, so each IntraMap has a special IntraFlag attribute reserved +for exactly this purpose. +f- + +c+ +Consider, for instance, the case where the transformation function has +access to several alternative sets of internally-stored data which it +may apply to perform its transformation. Rather than implement many +different versions of the transformation function, you may switch +between them by setting a value for the IntraFlag attribute when you +create an instance of an IntraMap, for example: +c- +f+ +Consider, for instance, the case where the transformation routine has +access to several alternative sets of internally-stored data which it +may apply to perform its transformation. Rather than implement many +different versions of the transformation routine, you may switch +between them by setting a value for the IntraFlag attribute when you +create an instance of an IntraMap, for example: +f- + +c+ +\small +\begin{terminalv} +intramap1 = astIntraMap( "MyTran", 2, 2, "IntraFlag=A" ); +intramap2 = astIntraMap( "MyTran", 2, 2, "IntraFlag=B" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTRAMAP1 = AST_INTRAMAP( 'MyTran', 2, 2, 'IntraFlag=A', STATUS ) + INTRAMAP2 = AST_INTRAMAP( 'MyTran', 2, 2, 'IntraFlag=B', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +The transformation function may then enquire the value of the IntraFlag +attribute (\emph{e.g.}\ using astGetC and passing it the ``this'' +pointer) and use whichever dataset is required for that particular +IntraMap. +c- +f+ +The transformation routine may then enquire the value of the IntraFlag +attribute (\emph{e.g.}\ using AST\_GETC and passing it the THIS +pointer) and use whichever dataset is required for that particular +IntraMap. +f- + +This approach is particularly useful when the number of possible +transformations is unbounded or not known in advance, in which case +the IntraFlag attribute may be used to hold numerical values encoded +as part of a character string (effectively using them as data for the +IntraMap). It is also superior to the use of a global switch for +communication (\emph{e.g.}\ setting an index to select the ``current'' +data before using the IntraMap), because it continues to work when +several IntraMaps are embedded within a more complex compound Mapping, +when you may have no control over the order in which they are used. + +\subsection{\xlabel{MaxTran}\label{ss:simplifyingintramaps}Simplifying IntraMaps} + +c+ +A notable disadvantage of IntraMaps is that they are ``black boxes'' +as far as AST is concerned. This means that they have limited ability +to participate in the simplification of compound Mappings performed, +\emph{e.g.}, by astSimplify (\secref{ss:simplifyingcmpmaps}), because +AST cannot know how they interact with other Mappings. In reality, of +course, they will often implement such specialised coordinate +transformations that the simplification possibilities will be rather +limited anyway. +c- +f+ +A notable disadvantage of IntraMaps is that they are ``black boxes'' +as far as AST is concerned. This means that they have limited ability +to participate in the simplification of compound Mappings performed, +\emph{e.g.}, by AST\_SIMPLIFY (\secref{ss:simplifyingcmpmaps}), +because AST cannot know how they interact with other Mappings. In +reality, of course, they will often implement such specialised +coordinate transformations that the simplification possibilities will +be rather limited anyway. +f- + +One important simplification, however, is the ability of a Mapping to +cancel with its own inverse to yield a unit Mapping (a UnitMap). This +is important because Mappings are frequently used to relate a dataset +to some external standard (a celestial coordinate system, for +example). When inter-relating two similar datasets calibrated using +the same standard, part of the Mapping often cancels, because it is +applied first in one direction and then the other, effectively +eliminating the reference to the standard. This is often a useful +simplification and can lead to greater efficiency. + +c+ +Many transformations have this property of cancelling with their own +inverse, but not necessarily all. Consider the following +transformation function, for example: +c- +f+ +Many transformations have this property of cancelling with their own +inverse, but not necessarily all. Consider the following +transformation routine, for example: +f- + +c+ +\small +\begin{terminalv} +void MaxTran( AstMapping *this, int npoint, int ncoord_in, + const double *ptr_in[], int forward, int ncoord_out, + double *ptr_out[] ) { + double hi, x; + int coord, point; + +/* Forward transformation. */ + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + hi = AST__BAD; + for ( coord = 0; coord < ncoord_in; coord++ ) { + x = ptr_in[ coord ][ point ]; + if ( x != AST__BAD ) { + if ( x > hi || hi == AST__BAD ) hi = x; + } + } + ptr_out[ 0 ][ point ] = hi; + } + +/* Inverse transformation. */ + } else { + for ( coord = 0; coord < ncoord_out; coord++ ) { + for ( point = 0; point < npoint; point++ ) { + ptr_out[ coord ][ point ] = ptr_in[ 0 ][ point ]; + } + } + } +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SUBROUTINE MAXTRAN( THIS, NPOINT, NCOORD_IN, INDIM, IN, FORWARD, + : NCOORD_OUT, OUTDIM, OUT, STATUS ) + INTEGER THIS, NPOINT, NCOORD_IN, INDIM, NCOORD_OUT, OUTDIM, STATUS + DOUBLE PRECISION IN( INDIM, NCOORD_IN ), OUT( OUTDIM, NCOORD_OUT ) + LOGICAL FORWARD + + INCLUDE 'AST_PAR' + DOUBLE PRECISION HI, X + INTEGER COORD, POINT + +* Forward transformation. + IF ( FORWARD ) THEN + DO 2 POINT = 1, NPOINT + HI = AST__BAD + DO 1 COORD = 1, NCOORD_IN + X = IN( POINT, COORD ) + IF ( X .NE. AST__BAD ) THEN + IF ( X .GT. HI .OR. HI .EQ. AST__BAD ) HI = X + END IF + 1 CONTINUE + 2 CONTINUE + +* Inverse transformation. + ELSE + DO 4 COORD = 1, NCOORD_OUT + DO 3 POINT = 1, NPOINT + OUT( POINT, COORD ) = IN( POINT, 1 ) + 3 CONTINUE + 4 CONTINUE + END IF + END +\end{terminalv} +\normalsize +f- + +c+ +This function takes any number of input coordinates and returns a +single output coordinate which is the maximum value of the input +coordinates. Its inverse (actually a ``pseudo-inverse'') sets all the +input coordinates to the value of the output +coordinate.\footnote{Remember that ``ptr\_in'' identifies the original +``output'' coordinates when applying the inverse transformation and +``ptr\_out'' identifies the original ``input'' coordinates.} +c- +f+ +This routine takes any number of input coordinates and returns a +single output coordinate which is the maximum value of the input +coordinates. Its inverse (actually a ``pseudo-inverse'') sets all the +input coordinates to the value of the output +coordinate.\footnote{Remember that IN holds the original ``output'' +coordinates when applying the inverse transformation and OUT holds the +original ``input'' coordinates.} +f- + +c+ +If this function is applied in the forward direction and then in the +inverse direction, it does \textbf{not} in general restore the original +coordinate values. However, if applied in the inverse direction and +then the forward direction, it does. Hence, replacing the sequence of +operations with an equivalent UnitMap is possible in the latter case, +but not in the former. +c- +f+ +If this routine is applied in the forward direction and then in the +inverse direction, it does \textbf{not} in general restore the original +coordinate values. However, if applied in the inverse direction and +then the forward direction, it does. Hence, replacing the sequence of +operations with an equivalent UnitMap is possible in the latter case, +but not in the former. +f- + +c+ +To distinguish these possibilities, two flag values are provided for +use with astIntraReg to indicate what simplification (if any) is +possible. For example, to register the above transformation function, +we might use: +c- +f+ +To distinguish these possibilities, two flag values are provided for +use with AST\_INTRAREG to indicate what simplification (if any) is +possible. For example, to register the above transformation routine, +we might use: +f- + +c+ +\small +\begin{terminalv} +void MaxTran( AstMapping *, int, int, const double *[], int, int, double *[] ); + +... + +astIntraReg( "MaxTran", AST__ANY, 1, MaxTran, AST__SIMPIF, + purpose, author, contact ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + EXTERNAL MAXTRAN + + ... + + CALL AST_INTRAREG( 'MaxTran', AST__ANY, 1, MAXTRAN, AST__SIMPIF, + : PURPOSE, AUTHOR, CONTACT, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, the flag value AST\_\_SIMPIF supplied for the fifth argument +indicates that simplification is possible if the transformation is +applied in the inverse direction followed by the forward direction. To +indicate the complementary case, the flag AST\_\_SIMPFI would be used +instead. If both simplifications are possible (as with the SqrTran +function in \secref{ss:transformationfunctions}), then we would use +the bitwise OR of both values. +c- +f+ +Here, the flag value AST\_\_SIMPIF supplied for the fifth argument +indicates that simplification is possible if the transformation is +applied in the inverse direction followed by the forward direction. To +indicate the complementary case, the flag AST\_\_SIMPFI would be used +instead. If both simplifications are possible (as with the SQRTRAN +function in \secref{ss:transformationfunctions}), then we would use +the sum of both values. +f- + +c+ +In practice, some judgement is usually necessary when deciding whether +to allow simplification. For example, seen in one light our SqrTran +function (\secref{ss:transformationfunctions}) does not cancel with +its own inverse, because squaring a coordinate value and then taking +its square root can change the original value, if this was +negative. Therefore, replacing this combination with a UnitMap will +change the behaviour of a compound Mapping and should not be +allowed. Seen in another light, however, where the coordinates being +processed are intrinsically all positive, it is a permissible and +probably useful simplification. +c- +f+ +In practice, some judgement is usually necessary when deciding whether +to allow simplification. For example, seen in one light our SQRTRAN +routine (\secref{ss:transformationfunctions}) does not cancel with its +own inverse, because squaring a coordinate value and then taking its +square root can change the original value, if this was +negative. Therefore, replacing this combination with a UnitMap will +change the behaviour of a compound Mapping and should not be +allowed. Seen in another light, however, where the coordinates being +processed are intrinsically all positive, it is a permissible and +probably useful simplification. +f- + +c+ +If such distinctions are ever important in practice, it is simple to +register the same transformation function twice with different flag +values (use a separate name for each) and then use whichever is +appropriate when creating an IntraMap. +c- +f+ +If such distinctions are ever important in practice, it is simple to +register the same transformation routine twice with different flag +values (use a separate name for each) and then use whichever is +appropriate when creating an IntraMap. +f- + +\subsection{\label{ss:readingandwritingintramaps}Writing and Reading IntraMaps} + +c+ +It is most important to realise that when you write an IntraMap to a +Channel (\secref{ss:writingtoachannel}), the transformation function +which it uses is not stored with it. To do so is impossible, because +the function has been compiled and loaded into memory ready for +execution before AST gets to see it. However, AST does store the name +associated with the transformation function and various details about +the IntraMap itself. + +c- +f+ +It is most important to realise that when you write an IntraMap to a +Channel (\secref{ss:writingtoachannel}), the transformation routine +which it uses is not stored with it. To do so is impossible, because +the routine has been compiled and loaded into memory ready for +execution before AST gets to see it. However, AST does store the name +associated with the transformation routine and various details about +the IntraMap itself. +f- + +c+ +This means that any program attempting to read the IntraMap +(\secref{ss:readingfromachannel}) cannot make use of it unless it also +has independent access to the original transformation function. If it +does not have access to this function, an error will occur at the +point where the IntraMap is read and the associated error message will +direct the user to the author of the transformation function for more +information. +c- +f+ +This means that any program attempting to read the IntraMap +(\secref{ss:readingfromachannel}) cannot make use of it unless it also +has independent access to the original transformation routine. If it +does not have access to this routine, an error will occur at the point +where the IntraMap is read and the associated error message will +direct the user to the author of the transformation routine for more +information. +f- + +c+ +However, if the necessary transformation function is available, and +has been registered before the read operation takes place, then AST is +able to re-create the original IntraMap and will do so. Registration +of the transformation function must, of course, use the same name +(and, in fact, be identical in most particulars) as was used in the +original program which wrote the data. +c- +f+ +However, if the necessary transformation routine is available, and +has been registered before the read operation takes place, then AST is +able to re-create the original IntraMap and will do so. Registration +of the transformation routine must, of course, use the same name +(and, in fact, be identical in most particulars) as was used in the +original program which wrote the data. +f- + +c+ +This means that a set of co-operating programs which all have access +to the same set of transformation functions and register them in +identical fashion (see \secref{ss:intramaplibrary} for how this can +best be achieved) can freely exchange data that contain IntraMaps. The +need to avoid exporting such data to unsuspecting third parties +(\secref{ss:intramaplimitations}) must, however, be re-iterated. +c- +f+ +This means that a set of co-operating programs which all have access +to the same set of transformation routines and register them in +identical fashion (see \secref{ss:intramaplibrary} for how this can +best be achieved) can freely exchange data that contain IntraMaps. The +need to avoid exporting such data to unsuspecting third parties +(\secref{ss:intramaplimitations}) must, however, be re-iterated. +f- + +c+ +\subsection{\label{ss:intramaplibrary}Managing Transformation Functions in Libraries} +c- +f+ +\subsection{\label{ss:intramaplibrary}Managing Transformation Routines in Libraries} +f- + +c+ +If you are developing a large suite of data reduction software, you +may have a need to use IntraMaps at various points within it. Very +probably this will occur in unrelated modules which are compiled +separately and then stored in a library. Since the transformation +functions required must be registered before they can be used, this +makes it difficult to decide where to perform this registration, +especially since any particular data reduction program may use an +arbitrary subset of the modules in your library. +c- +f+ +If you are developing a large suite of data reduction software, you +may have a need to use IntraMaps at various points within it. Very +probably this will occur in unrelated modules which are compiled +separately and then stored in a library. Since the transformation +routines required must be registered before they can be used, this +makes it difficult to decide where to perform this registration, +especially since any particular data reduction program may use an +arbitrary subset of the modules in your library. +f- + +c+ +To assist with this problem, AST allows you to perform the same +registration of a transformation function any number of times, so long +as it is performed using an identical invocation of astIntraReg on +each occasion (\emph{i.e.}\ all of its arguments must be +identical). This means you do not have to keep track of whether a +particular function has already been registered but could, in fact, +register it on each occasion immediately before it is required +(wherever that may be). In order that all registrations are identical, +however, it is recommended that you group them all together into a +single function, perhaps as follows: +c- +f+ +To assist with this problem, AST allows you to perform the same +registration of a transformation routine any number of times, so long +as it is performed using an identical invocation of AST\_INTRAREG on +each occasion (\emph{i.e.}\ all of its arguments must be +identical). This means you do not have to keep track of whether a +particular routine has already been registered but could, in fact, +register it on each occasion immediately before it is required +(wherever that may be). In order that all registrations are identical, +however, it is recommended that you group them all together into a +single routine, perhaps as follows: +f- + +c+ +\small +\begin{terminalv} +void MyTrans( void ) { + + ... + + astIntraReg( "MaxTran", AST__ANY, 1, MaxTran, AST__SIMPIF, + purpose, author, contact ); + + ... + + astIntraReg( "Poly3Tran", 1, 1, Poly3Tran, AST__NOINV, + purpose, author, contact ); + + ... + + astIntraReg( "SqrTran", 2, 2, SqrTran, 0, + purpose, author, contact ); +} +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + SUBROUTINE MYTRANS( STATUS ) + INTEGER STATUS + + INCLUDE 'AST_PAR' + EXTERNAL MAXTRAN, POLY3TRAN, SQRTRAN + + ... + + CALL AST_INTRAREG( 'MaxTran', AST__ANY, 1, MAXTRAN, AST__SIMPIF, + : PURPOSE, AUTHOR, CONTACT, STATUS ) + + ... + + CALL AST_INTRAREG( 'Poly3Tran', 1, 1, POLY3TRAN, AST__NOINV, + : PURPOSE, AUTHOR, CONTACT, STATUS ) + + ... + + CALL AST_INTRAREG( 'SqrTran, 2, 2, SQRTRAN, 0, + : PURPOSE, AUTHOR, CONTACT, STATUS ) + END +\end{terminalv} +\normalsize +f- + +c+ +You can then simply invoke this function wherever necessary. It is, in +fact, particularly important to register all relevant transformation +functions in this way before you attempt to read an Object that might +be (or contain) an IntraMap +(\secref{ss:readingandwritingintramaps}). This is because you may not +know in advance which of these transformation functions the IntraMap +will use, so they must all be available in order to avoid an error. +c- +f+ +You can then simply invoke this routine wherever necessary. It is, in +fact, particularly important to register all relevant transformation +routines in this way before you attempt to read an Object that might +be (or contain) an IntraMap +(\secref{ss:readingandwritingintramaps}). This is because you may not +know in advance which of these transformation routines the IntraMap +will use, so they must all be available in order to avoid an error. +f- + +\cleardoublepage +\section{\label{ss:plots}Producing Graphical Output (Plots)} + +Graphical output from AST is performed though an Object called a Plot, +which is a specialised form of FrameSet. A Plot does not represent the +graphical content itself, but is a route through which plotting +operations, such as drawing lines and curves, are conveyed on to a +plotting surface to appear as visible graphics. + +\subsection{The Plot Model} + +When a Plot is created, it is initialised by providing a FrameSet whose +base Frame (as specified by its Base attribute) is mapped linearly or +logarithmically (as specified by the LogPlot attribues) on to a +\emph{plotting area}. This is a rectangular region in the graphical +coordinate space of the underlying graphics system and becomes the new +base Frame of the Plot. In effect, the Plot becomes attached to the +plotting surface, in rather the same way that a basic FrameSet might be +attached to (say) an image. + +The current Frame of the Plot (derived from the current Frame of the +FrameSet supplied) is used to represent a \emph{physical coordinate +system}. This is the system in which plotting operations are +performed by your program. Every plotting operation is then +transformed through the Mapping which inter-relates the Plot's current +and base Frames in order to appear on the plotting surface. + +An example may help here. Suppose we start with a FrameSet whose base +Frame describes the pixel coordinates of an image and whose current +Frame describes a celestial (equatorial) coordinate system. Let us +assume that these two Frames are inter-related by a Mapping within the +FrameSet which represents a particular sky projection. + +When a Plot is created from this FrameSet, we specify how the pixel +coordinates (the base Frame) maps on to the plotting surface. This +simply corresponds to telling the Plot where we have previously +plotted the image data. If we now use the Plot to plot a line with +latitude zero in our physical coordinate system, as given by the +current Frame, this line would appear as a curve (the equator) on the +plotting surface, correctly registered with the image. + +There are a number of plotting functions provided, which all work in a +similar way. Plotting operations are transformed through the Mapping +which the Plot represents before they appear on the plotting +surface.\footnote{Like any FrameSet, a Plot can be used as a +Mapping. In this case it is the inverse transformation which is used +when plotting (\emph{i.e.}\ that which transforms between the current +and base Frames).} It is possible to draw symbols, lines, axes, +entire grids and more in this way. + +%\subsection{TBW---Creating a Plot} + +\subsection{Plotting Symbols} + +c+ +The simplest form of plotting is to draw symbols (termed +\emph{markers}) at a set of points. This is performed by astMark, +which is supplied with a set of physical coordinates at which to place +the markers: +c- +f+ +The simplest form of plotting is to draw symbols (termed +\emph{markers}) at a set of points. This is performed by AST\_MARK, +which is supplied with a set of physical coordinates at which to place +the markers: +f- + +c+ +\small +\begin{terminalv} +#include "ast.h" +#define NCOORD 2 +#define NMARK 10 +double in[ NCOORD ][ NMARK ]; +int type; + +... + +astMark( plot, NMARK, NCOORD, NMARK, in, type ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INCLUDE 'AST_PAR' + INTEGER NCOORD, NMARK, TYPE, STATUS + DOUBLE PRECISION IN( NMARK, NCOORD ) + + STATUS = 0 + + ... + + CALL AST_MARK( PLOT, NMARK, NCOORD, NMARK, IN, TYPE, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, NMARK specifies how many markers to plot and NCOORD specifies +how many coordinates are being supplied for each +point.\footnote{Remember, the physical coordinate space need not +necessarily be 2-dimensional, even if the plotting surface is.} The +array ``in'' supplies the coordinates and the integer ``type'' +specifies which type of marker to plot. +c- +f+ +Here, NMARK specifies how many markers to plot and NCOORD specifies +how many coordinates are being supplied for each +point.\footnote{Remember, the physical coordinate space need not +necessarily be 2-dimensional, even if the plotting surface is.} The +array IN supplies the coordinates and the integer TYPE specifies which +type of marker to plot. +f- + +\subsection{\label{ss:plottinggeodesics}Plotting Geodesic Curves} + +There is no Plot routine to draw a straight line, because any straight +line in physical coordinates can potentially turn into a curve in +graphical coordinates. We therefore start by considering how to draw +geodesic curves. These are curves which trace the path of shortest +distance between two points in physical coordinates + and are the basic drawing element in a Plot. + +c+ +In many instances, the geodesic will, in fact, be a straight line, but +this depends on the Plot's current Frame. If this represents a +celestial coordinate system, for instance, it will be a great circle +(corresponding with the behaviour of the astDistance function which +defines the metric of the physical coordinate space). The geodesic +will, of course, be transformed into graphics coordinates before being +plotted. A geodesic curve is plotted using astCurve as follows: +c- +f+ +In many instances, the geodesic will, in fact, be a straight line, but +this depends on the Plot's current Frame. If this represents a +celestial coordinate system, for instance, it will be a great circle +(corresponding with the behaviour of the AST\_DISTANCE function which +defines the metric of the physical coordinate space). The geodesic +will, of course, be transformed into graphics coordinates before being +plotted. A geodesic curve is plotted using AST\_CURVE as follows: +f- + +c+ +\small +\begin{terminalv} +double start[ NCOORD ], finish[ NCOORD ]; + +... + +astCurve( plot, start, finish ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + DOUBLE PRECISION START( NCOORD ), FINISH( NCOORD ) + + ... + + CALL AST_CURVE( PLOT, START, FINISH, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, ``start'' and ``finish'' are arrays containing the starting and +finishing coordinates of the curve. The astOffset and astDistance +functions can often be useful for computing these +(\secref{ss:distanceandoffset}). +c- +f+ +Here, START and FINISH are arrays containing the starting and +finishing coordinates of the curve. The AST\_OFFSET and AST\_DISTANCE +routines can often be useful for computing these +(\secref{ss:distanceandoffset}). +f- + +c+ +If you need to draw a series of curves end-to-end (when drawing a +contour line, for example), then a more efficient alternative is to +use astPolyCurve. This has the same effect as a sequence of +invocations of astCurve, but allows you to supply a whole set of +points at one time. astPolyCurve then joins them, in sequence, using +geodesic curves: +c- +f+ +If you need to draw a series of curves end-to-end (when drawing a +contour line, for example), then a more efficient alternative is to +use AST\_POLYCURVE. This has the same effect as a sequence of calls to +AST\_CURVE, but allows you to supply a whole set of points at the same +time. AST\_POLYLINE then joins them, in sequence, using geodesic +curves: +f- + +c+ +\small +\begin{terminalv} +#define NPOINT 100 +double coords[ NCOORD ][ NPOINT ]; + +... + +astPolyCurve( plot, NPOINT, NCOORD, NPOINT, coords ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER NPOINT + DOUBLE PRECISION COORDS( NPOINT, NCOORD ) + + ... + + CALL AST_POLYCURVE( PLOT, NPOINT, NCOORD, NPOINT, COORDS, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, NPOINT specifies how many points are to be joined and NCOORD +specifies how many coordinates are being supplied for each point. The +array ``coords'' supplies the coordinates of the points in the Plot's +physical coordinate system. +c- +f+ +Here, NPOINT specifies how many points are to be joined and NCOORD +specifies how many coordinates are being supplied for each point. The +array COORDS supplies the coordinates of the points in the Plot's +physical coordinate system. +f- + +\subsection{Plotting Curves Parallel to Axes} + +c+ +As there is no Plot function to draw a ``straight line'', drawing axes +and grid lines to represent coordinate systems requires a slightly +different approach. The problem is that for some coordinate systems, +these grid lines will not be geodesics, so astCurve and astPolyCurve +(\secref{ss:plottinggeodesics}) cannot easily be used (you would have +to resort to approximating grid lines by many small elements). Lines +of constant celestial latitude provide an example of this, with the +exception of the equator which is a geodesic. +c- +f+ +As there is no Plot routine to draw a ``straight line'', drawing axes +and grid lines to represent coordinate systems requires a slightly +different approach. The problem is that for some coordinate systems, +these grid lines will not be geodesics, so AST\_CURVE and +AST\_POLYCURVE (\secref{ss:plottinggeodesics}) cannot easily be used +(you would have to resort to approximating grid lines by many small +elements). Lines of constant celestial latitude provide an example of +this, with the exception of the equator which is a geodesic. +f- + +c+ +The astGridLine function allows these curves to be drawn, as follows: +c- +f+ +The AST\_GRIDLINE routine allows these curves to be drawn, as follows: +f- + +c+ +\small +\begin{terminalv} +int axis; +double length; + +... + +astGridLine( plot, axis, start, length ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER AXIS + DOUBLE PRECISION LENGTH + + ... + + CALL AST_GRIDLINE( PLOT, AXIS, START, LENGTH, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, ``axis'' specifies which physical coordinate axis we wish to +draw parallel to. The ``start'' array contains the coordinates of the +start of the curve and ``length'' specifies the distance to draw along +the axis in physical coordinate space. +c- +f+ +Here, AXIS specifies which physical coordinate axis we wish to draw +parallel to. The START array contains the coordinates of the start of +the curve and LENGTH specifies the distance to draw along the axis in +physical coordinate space. +f- + +\subsection{\label{ss:plottinggeneralizedcurves}Plotting Generalized Curves} +We have seen how geodesic curves and grid lines can be drawn. The Plot +class includes another method, +c+ +astGenCurve, +c- +f+ +AST\_GENCURVE, +f- +which allows curves of \emph{any} form to be drawn. The caller supplies a +Mapping which maps offset along the curve\footnote{normalized so that the +start of the curve is at offset 0.0 and the end of the curve is at offset +1.0 - offset need not be linearly related to distance.} into the +corresponding position in the current Frame of the Plot. +c+ +astGenCurve, +c- +f+ +AST\_GENCURVE, +f- +then takes care of Mapping these positions into graphics coordinates. The +choice of exactly which positions along the curve are to be used to +define the curve is also made by +c+ +astGenCurve, +c- +f+ +AST\_GENCURVE, +f- +using an adaptive algorithm which concentrates points around areas where +the curve is bending sharply or is discontinuous in graphics coordinates. + +The IntraMap class may be of particular use in this context since it allows +you to code your own Mappings to do any transformation you choose. + + +\subsection{\label{ss:clipping}Clipping} + +Like many graphics systems, a Plot allows you to \emph{clip} the graphics +you produce. This means that plotting is restricted to certain regions +of the plotting surface so that anything drawn outside these regions +will not appear. All Plots automatically clip at the edges of the +plotting area specified when the Plot is created. This means that +graphics are ultimately restricted to the rectangular region of +plotting space to which you have attached the Plot. + +In addition to this, you may also specify lower and upper limits on +each axis at which clipping should occur. This permits you to further +restrict the plotting region. Moreover, you may attach these clipping +limits to \emph{any} of the Frames in the Plot. This allows you to +place restrictions on where plotting will take place in either the +physical coordinate system, the graphical coordinate system, or in any +other coordinate system which is described by a Frame within the Plot. + +For example, you could plot using equatorial coordinates and set up +clipping limits in galactic coordinates. In general, you could set up +arbitrary clipping regions by adding a new Frame to a Plot (in which +clipping will be performed) and inter-relating this to the other +Frames in a suitable way. + +c+ +Clipping limits are defined using the astClip function, as follows: +c- +f+ +Clipping limits are defined using the AST\_CLIP routine, as follows: +f- + +c+ +\small +\begin{terminalv} +#define NAXES 2 +int iframe; +double lbnd[ NAXES ], ubnd[ NAXES ]; + +... +astClip( plot, iframe, lbnd, ubnd); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + INTEGER IFRAME, NAXES + DOUBLE PRECISION LBND( NAXES ), UBND( NAXES ) + + ... + + CALL AST_CLIP( PLOT, IFRAME, LBND, UBND, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, the ``iframe'' value gives the index of the Frame within the +Plot to which clipping is to be applied, while ``lbnd'' and ``ubnd'' +give the limits on each axis of the selected Frame (NAXES is the +number of axes in this Frame). +c- +f+ +Here, the IFRAME value gives the index of the Frame within the Plot to +which clipping is to be applied, while LBND and UBND give the limits +on each axis of the selected Frame (NAXES is the number of axes in +this Frame). +f- + +c+ +You can remove clipping by giving a value of AST\_\_NOFRAME for ``iframe''. +c- +f+ +You can remove clipping by giving a value of AST\_\_NOFRAME for IFRAME. +f- + +\subsection{Using a Plot as a Mapping} + +All Plots are also Mappings (just like the FrameSets from which they +are derived), so can be used to transform coordinates. + +Like FrameSets, the forward transformation of a Plot will convert +coordinates between the base and current Frames (\emph{i.e.}\ between +graphical and physical coordinates). This would be useful if you were +(say) reading a cursor position in graphical coordinates and needed to +convert this into physical coordinates for display. + +c+ +Conversely, a Plot's inverse transformation converts between its +current and base Frames (\emph{i.e.}\ from physical coordinates to +graphical coordinates). This transformation is applied automatically +whenever plotting operations are carried out by AST functions. It may +also be useful to apply it directly, however, if you wish to perform +additional plotting operations (\emph{e.g.}\ those provided by the +native graphics system) at positions specified in physical +coordinates. +c- +f+ +Conversely, a Plot's inverse transformation converts between its +current and base Frames (\emph{i.e.}\ from physical coordinates to +graphical coordinates). This transformation is applied automatically +whenever plotting operations are carried out by AST routines. It may +also be useful to apply it directly, however, if you wish to perform +additional plotting operations (\emph{e.g.}\ those provided by the +native graphics system) at positions specified in physical +coordinates. +f- + +c+ +There is, however, one important difference between using a FrameSet +and a Plot to transform coordinates, and this is that clipping may be +applied by a Plot (if it has been enabled using +astClip---\secref{ss:clipping}). Any point which lies within the +clipped region of a Plot will, when transformed, yield coordinates +with the value AST\_\_BAD. If you wish to avoid this clipping, you +should extract the relevant Mapping from the Plot (using +astGetMapping) and use this, instead of the Plot, to transform the +coordinates. +c- +f+ +There is, however. one important difference between using a FrameSet +and a Plot to transform coordinates, and this is that clipping may be +applied by a Plot (if it has been enabled using +AST\_CLIP---\secref{ss:clipping}). Any point which lies within the +clipped region of a Plot will, when transformed, yield coordinates +with the value AST\_\_BAD. If you wish to avoid this clipping, you +should extract the relevant Mapping from the Plot (using +AST\_GETMAPPING) and use this, instead of the Plot, to transform the +coordinates. +f- + +\subsection{Using a Plot as a Frame} + +Every Plot is also a Frame, so can be used to obtain the values of +Frame attributes such as a Title, axis Labels, axis Units, +\emph{etc.}, which are typically used when displaying data and/or +coordinates. These attributes are, as for any FrameSet, derived from +the current Frame of the Plot (\secref{ss:framesetasframe}). They are +also used automatically when using the Plot to plot coordinate axes +and coordinate grids (\emph{e.g.}\ for labelling +them---\secref{ss:plottingagrid}). + +c+ +Because the current Frame of a Plot represents physical coordinates, +any Frame operation applied to the Plot will effectively be working in +this coordinate system. For example, the astDistance and astOffset +functions will compute distances and offsets in physical coordinate +space, while astFormat and astNorm will format physical coordinates in +an appropriate way for display. +c- +f+ +Because the current Frame of a Plot represents physical coordinates, +any Frame operation applied to the Plot will effectively be working in +this coordinate system. For example, the AST\_DISTANCE and AST\_OFFSET +routines will compute distances and offsets in physical coordinate +space, and AST\_FORMAT will format physical coordinates in an +appropriate way for display. +f- + +\subsection{\label{ss:validphysicalcoordinates}Regions of Valid Physical Coordinates} + +When points in physical coordinate space are transformed by a Plot +into graphics coordinates for plotting, they may not always yield +valid coordinates, irrespective of any clipping being applied +(\secref{ss:clipping}). To indicate this, the resulting coordinate +values will be set to the value AST\_\_BAD +(\secref{ss:badcoordinates}). + +There are a number of reasons why this may occur, but typically it +will be because physical coordinates only map on to a subset of the +graphics coordinate space. This situation is commonly encountered with +all-sky projections where, typically, the celestial sphere appears, +when plotted, as a distorted shape (\emph{e.g.}\ an ellipse) which +does not entirely fill the graphics space. In some cases, there may +even be multiple regions of valid and invalid physical coordinates. + +When plotting is performed \emph{via} a Plot, graphical output will +only appear in the regions of valid physical coordinates. Nothing will +appear where invalid coordinates occur. Such output is effectively +clipped. If you wish to plot in these areas, you must change +coordinate system and use, say, graphical coordinates to address the +plotting surface directly. + +\subsection{Plotting Borders} + +c+ +The astBorder function is provided to draw a (line) border around your +graphical output. With most graphics systems, this would simply be a +rectangular box around the plotting area. With a Plot, however, this +boundary follows the edge of each region containing valid, unclipped +physical coordinates (\secref{ss:validphysicalcoordinates}). +c- +f+ +The AST\_BORDER routine is provided to draw a (line) border around +your graphical output. With most graphics systems, this would simply +be a rectangular box around the plotting area. With a Plot, however, +this boundary follows the edge of each region containing valid, +unclipped physical coordinates (\secref{ss:validphysicalcoordinates}). +f- + +c+ +This means, for example, that if you were plotting an all-sky +projection, this boundary would outline the perimeter of the celestial +sphere when projected on to your plotting surface. Of course, if there +is no clipping and all physical coordinates are valid, then you will +get the traditional rectangular box. astBorder requires only +a pointer to the Plot: +c- +f+ +This means, for example, that if you were plotting an all-sky +projection, this boundary would outline the perimeter of the celestial +sphere when projected on to your plotting surface. Of course, if there +is no clipping and all physical coordinates are valid, then you will +get the traditional rectangular box. AST\_BORDER requires only a +pointer to the Plot and the usual STATUS argument: +f- + +c+ +\small +\begin{terminalv} +int holes; + +... + +holes = astBorder( plot ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + LOGICAL HOLES + + ... + + HOLES = AST_BORDER( PLOT, STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +It returns a boolean (integer) value to indicate if any invalid or +clipped physical coordinates were found within the plotting area. If +they were, it will draw around the valid unclipped regions and return +a value of one. Otherwise, it will draw a simple rectangular border +and return zero. +c- +f+ +It returns a logical value to indicate if any invalid or clipped +physical coordinates were found within the plotting area. If they +were, it will draw around the valid unclipped regions and return +.TRUE.. Otherwise, it will draw a simple rectangular border and return +.FALSE.. +f- + +\subsection{Plotting Text} + +c+ +Using a Plot to draw text involves supplying a string of text to be +displayed and a position in physical coordinates where the text is to +appear. The position is transformed into graphical coordinates to +determine where the text should appear on the plotting surface. You +must also provide a 2-element ``up'' vector which gives the upward +direction of the text in graphical coordinates. This allows text to be +drawn at any angle. +c- +f+ +Using a Plot to draw text involves supplying a string of text to be +displayed and a position in physical coordinates where the text is to +appear. The position is transformed into graphical coordinates to +determine where the text should appear on the plotting surface. You +must also provide a 2-element UP vector which gives the upward +direction of the text in graphical coordinates. This allows text to be +drawn at any angle. +f- + +c+ +Plotting is performed by astText, for example: +c- +f+ +Plotting is performed by AST\_TEXT, for example: +f- + +c+ +\small +\begin{terminalv} +char text[ 21 ]; +double pos[ NCOORD ]; +float up[ 2 ] = { 0.0f, 1.0f }; + +... + +astText( plot, text, pos, up, "TL" ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CHARACTER * ( 20 ) TEXT + DOUBLE PRECISION POS( NCOORD ) + REAL UP( 2 ) + DATA UP / 0.0, 1.0 / + + ... + + CALL AST_TEXT( PLOT, TEXT, POS, UP, 'TL', STATUS ) +\end{terminalv} +\normalsize +f- + +c+ +Here, ``text'' contains the string to be drawn, ``pos'' is an array of +physical coordinates and ``up'' specifies the upward vector. In this +case, the text will be drawn horizontally. The final argument +specifies the text justification, here indicating that the top left +corner of the text should appear at the position given. +c- +f+ +Here, TEXT contains the string to be drawn, POS is an array of +physical coordinates and UP specifies the upward vector. In this case, +the text will be drawn horizontally. The penultimate argument +specifies the text justification, here indicating that the top left +corner of the text should appear at the position given. +f- + +Further control over the appearance of the text is possible by setting +values for various Plot attributes, for example Colour, Font and Size. +Sub-strings within the displayed text can be given different appearances, +or turned into super-scripts or sub-scripts, by the inclusion of escape +sequences (see section~\secref{ss:escapes}) within the supplied text string. + +\subsection{\label{ss:plottingagrid}Plotting a Grid} + +c+ +The most comprehensive plotting function available is astGrid, which +can be used to draw labelled coordinate axes and, optionally, to +overlay coordinate grids on the plotting area +(Figure~\ref{fig:gridplot}). The routine is straightforward to use, +simply requiring a pointer to the Plot: +c- +f+ +The most comprehensive plotting routine available is AST\_GRID, which +can be used to draw labelled coordinate axes and, optionally, to +overlay coordinate grids on the plotting area +(Figure~\ref{fig:gridplot}). The routine is straightforward to use, +simply requiring a pointer to the Plot and a STATUS argument: +f- + +c+ +\small +\begin{terminalv} +astGrid( plot ); +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} + CALL AST_GRID( PLOT, STATUS ) +\end{terminalv} +\normalsize +f- + +It will draw both linear and curvilinear axes and grids, as required +by the particular Plot. The appearance of the output can be modified +in a wide variety of ways by setting various Plot attributes. +The Label attributes of the current Frame are displayed as the axis +labels in the grid, and the Title attribute as the plot title. Sub-strings +within these strings can be given different appearances, or turned into +super-scripts or sub-scripts, by the inclusion of escape sequences (see +section~\secref{ss:escapes}) within the Label attributes. + +\subsection{\label{ss:escapes}Controlling the Appearance of Sub-strings} +Normally, each string of characters displayed using a Plot will be +plotted so that all characters in the string have the same font size, +colour, \emph{etc.}, specified by the appropriate attributes of the +Plot. However, it is possible to include \emph{escape sequences} within +the text to modify the appearance of sub-strings. Escape sequences can be +used to change, colour, font, size, width, to introduce extra horizontal +space between characters, and to change the base line of characters (thus +allowing super-scripts and sub-scripts to be created). See the entry for +the Escape attribute in \appref{ss:attributedescriptions} for details. + +As an example, if the character string ``\verb+10\%^50+\%s70+0.5+'' is +plotted, it will be displayed as ``$10^{0.5}$'' - that is, with a +super-scripted exponent. The exponent text will be 70\% of the size of +normal text (as determined by the Size attribute), and its baseline will +be raised by 50\% of the height of a normal character. + +Such escape sequences can be used in the strings assigned to textual +attributes of the Plot (such as the axis Labels), and may also be +included in strings plotted using +c+ +astText. +c- +f+ +AST\_TEXT. +f- + +The Format attribute for the SkyAxis class includes the ``g'' option +which will cause escape sequences to be included when formatting +celestial positions so that super-script characters are used as +delimiters for the various fields (a super-script ``h'' for hours, ``m'' +for minutes, \emph{etc}). + +Note, the facility for interpreting escape sequences is only available if +the graphics wrapper functions which provide the interface to the +underlying graphics system support all the functions included in the +\verb+grf.h+ file as of AST V3.2. Older grf interfaces may need to be +extended by the addition of new functions before escape sequences can be +interpretted. + +\subsection{\label{ss:logaxes}Producing Logarithmic Axes} +In certain situations you may wish for one or both of the plotted axes to +be displayed logarithmically rather than linearly. For instance, you may +wish to do this when using a Plot to represent a spectrum of, say, flux +against frequency. In this case, you can cause the frequency axis to be drawn +logarithmically simply by setting the boolean LogPlot attribute for the +frequency axis to a non-zero value. This causes several things to happen: + +\begin{enumerate} + +\item The Mapping between the base Frame of the Plot (which represents +the underlying graphics world coordinate system) and the base Frame of +the FrameSet supplied when the Plot was created, is modified. By +default, this mapping is linear on both axes, but setting LogPlot non-zero +for an axis causes the Mapping to be modified so that it is logarithmic +on the specified axis. This is only possible if the displayed section of +the axis does not include the value zero (otherwise the attempt to set +a new value for LogPlot is ignored,and it retains its default value of +zero). + +\item The major tick marks drawn as part of the annotated coordinate grid +are spaced logarithmically rather than linearly. That is, major axis +values are chosen so that there is a constant ratio between adjacent +tick mark values. This ratio is constrained to be a power of ten. The +minor tick marks are drawn at linearly distributed points between the +adjoining major tick values. Thus if a pair of adjacent major tick values +are drawn at axis values 10.0 and 100.0, minor ticks will be placed at +20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0 and 90.0 (note only 8 minor tick +marks are drawn). + +\item If possible, numerical axis labels are shown as powers of ten. +This depends on the facilities implemented by the graphics wrapper +functions (see the next section). Extra functions were introduced to this +set of wrapper functions at AST V3.2 which enable super-scripts and +sub-scripts to be produced. Some older wrappers may not yet have +implemented these functiosn and this will result in axis labels being +drawn in usual scientific or decimal notation. + +\end{enumerate} + +Whilst the LogPlot attribute can be used to control all three of the above +facilities, it is possible to control them individually as well. The +LogTicks and LogLabel attributes control the behaviour specified in items +2 and 3 above, but the default values for these attributes depend on the +setting of the LogPlot attribute. This means that setting LogPlot +non-zero will swicth all three facilites on, so long as zero values have +not been assigned explicitly to LogTicks or LogLabel. + + +\subsection{\label{ss:choosingagraphicspackage}Choosing a Graphics Package} +The Plot class itself does not include any code for actually drawing on a +graphics device. Instead, it requires a set of functions to be provided +which it uses to draw the required graphics. These include functions +to draw a straight line, draw a text string, \emph{etc}. You may choose +to provide functions from your favorite graphics package, or you can even +write your own! To accomodate variations in the calling interfaces of +different graphics packages, AST defines a standard interface for these +routines. If this interface differs from the interface provided by your +graphics package (which in general it will), then you must write a set of +\emph{wrapper functions}, which provide the interface expected by AST but +which then call functions from your graphics package to provide the +required functionality. AST comes with wrapper functions suitable for +the PGPLOT graphics package (see \xref{SUN/15}{sun15}{}). + +There are two ways of indicating which wrapper functions are to be used by +the Plot class: +\begin{enumerate} + +\item A file containing C functions with pre-defined names can be written +and linked with the application using options of the ast\_link command. +(see \secref{ss:howtobuild} and \appref{ss:commanddescriptions}). AST is +distributed with such a file (called \texttt{grf\_pgplot.c}) which calls PGPLOT +functions to implement the required functionality. This file can be used +as a template for writing your own. +f+ +Currently, it is not possible to write such ``grf modules'' in Fortran. +If you want to use wrapper functions written in Fortran, then you must +use the AST\_GRFSET method as described below. +f- + +\item The +c+ +astGrfSet +c- +f+ +AST\_GRFSET +f- +method of the Plot class can be used to ``register'' +wrapper functions at run-time. This allows an application to switch +between graphics systems if required. Graphics functions registered in +this way do not need to have the pre-defined names used in the link-time +method described above. + +\end{enumerate} + +For details of the interfaces of the wrapper routines, see +c+ +either the \texttt{grf\_pgplot.c} file included in the AST source +distribution, or the reference documentation for the astGrfSet method. +c- +f+ +the reference documentation for the AST\_GRFSET method. +f- + +\cleardoublepage +\section{Compiling and Linking Software that Uses AST} + +A small number of UNIX commands are provided by AST to assist with the +process of building software. A description of these can be found in +\appref{ss:commanddescriptions} and their use is discussed here. Note +that in order to access these commands, the appropriate directory +(normally ``/star/bin'') should be on your PATH.\footnote{If you have +not installed AST in the usual location, then substitute the +appropriate directory in place of ``/star'' wherever it occurs.} + +c+ +\subsection{\label{ss:accessingheaderfile}Accessing the ``ast.h'' Header File} +c- +f+ +\subsection{\label{ss:accessingheaderfile}Accessing AST Include Files} +f- + +c+ +The ``ast.h'' header file defines the external interface to the AST library, +including all constants, function prototypes, macros, \emph{etc.}. This file +should be located using the usual compiler options for finding C +include files, for instance: + +\small +\begin{terminalv} +cc prog.c -I/star/include -o prog +\end{terminalv} +\normalsize + +This is preferable to specifying the file's absolute name within your +software. +c- +f+ + +The include files provided for use with Fortran are: + +\begin{quote} +\begin{description} +\item[AST\_PAR]\mbox{}\\ +Declares the types of all AST functions and defines parameter +constants, except those that identify error values. + +\item[AST\_ERR]\mbox{}\\ +Defines parameter constants to represent the various error values to +which the AST error status may be set when an error occurs +(\secref{ss:errordetection}). +\end{description} +\end{quote} + +References to AST include files should be in upper case. Most modern +Fortran compilers allow the directory to be specified as a command line +option: + +\small +\begin{terminalv} +f77 prog.f -I/star/include -o prog +\end{terminalv} +\normalsize + +If you are using such a compiler then your Fortran source code should, +for instance, include: + +\small +\begin{terminalv} + INCLUDE 'AST_PAR' +\end{terminalv} +\normalsize + +(that is, there is no need to include the directory within the INCLUDE +statement). If your compiler does not provide such an option then your +source code must contain an absolute file name identifying the directory +where the include files reside, for instance: + +\small +\begin{terminalv} + INCLUDE '/star/include/AST_PAR' +\end{terminalv} +\normalsize + +f- + +\subsection{\label{ss:linking}Linking with AST Facilities} + +c+ +C programs which use AST facilities may be linked by including +execution of the command ``ast\_link'' on the compiler command +line. Thus, to compile and link a program called ``prog'', the +following might be used: +c- +f+ +Fortran programs may be linked with AST by including execution of the +command ``ast\_link'' on the compiler command line. Thus, to compile +and link a program called ``prog'', the following might be used: +f- + +c+ +\small +\begin{terminalv} +cc prog.c -L/star/lib `ast_link` -o prog +\end{terminalv} +\normalsize +c- +f+ +\small +\begin{terminalv} +f77 prog.f -L/star/lib `ast_link` -o prog +\end{terminalv} +\normalsize + +On Linux systems you should usually use \verb+g77 -fno-second-underscore+ in +place of \verb+f77+ - see \xref{``Software development on Linux''}{sun212} +{software_development_on_linux} in \xref{SUN/212}{sun212}{}. + +f- + +Note the use of backward quote characters, which cause the +``ast\_link'' command to be executed and its result substituted into +the compiler command. An alternative is to save the output from +``ast\_link'' in (say) a shell variable and use this instead. You may +find this a little faster if you are building software repeatedly +during development. + +Programs which use AST can also be linked in a number of other ways, +depending on the facilities they require. In the example above, we +have used the default method which assumes that the program will not +be generating graphical output, so that no graphics libraries need be +linked. If you need other facilities, then various switches can be +applied to the ``ast\_link'' command in order to control the linking +process. + +For example, if you were producing graphical output using the PGPLOT +graphics package, you could link with the AST/PGPLOT interface by +using the ``$-$pgplot'' switch with ``ast\_link'', as +follows:\footnote{Use the ``$-$pgp'' option instead if you wish to use +the Starlink version of PGPLOT which uses GKS to generate its output.} + +c+ +\small +\begin{terminalv} +cc prog.c -L/star/lib `ast_link -pgplot` -o prog +\end{terminalv} +\normalsize +c- +f+ +\begin{small} +\begin{terminalv} +f77 prog.f -L/star/lib `ast_link -pgplot` -o prog +\end{terminalv} +\end{small} + +again using \verb+g77 -fno-second-underscore+ in place of \verb+f77+ +on Linux systems. + +f- + +See the ``ast\_link'' command description in +\appref{ss:commanddescriptions} for details of the options available. + +\subsection{Building ADAM Applications that Use AST} + +Users of Starlink's \xref{ADAM}{sg4}{} programming environment +\latex{(SG/4)} on UNIX should use the +``\xref{alink}{sun144}{ADAM_link_scripts}'' command +(\xref{SUN/144}{sun144}{}) to compile and link applications and can +access the AST library by including execution of the command +``ast\_link\_adam'' on the command line, as follows: + +c+ +\begin{small} +\begin{terminalv} +alink adamprog.c `ast_link_adam` +\end{terminalv} +\end{small} +c- +f+ +\begin{small} +\begin{terminalv} +alink adamprog.f `ast_link_adam` +\end{terminalv} +\end{small} +f- + +Note the use of backward quote characters. + +By default, AST error messages produced by applications built in this +way will be delivered \emph{via} the Starlink EMS Error Message +Service (\xref{SSN/4}{ssn4}{}) so that error handling by AST is +consistent with the \xref{\emph{inherited +status}}{sun104}{inherited_status} error handling normally used in +Starlink software. + +Switches may be given to the ``ast\_link\_adam'' command (in a similar +way to ``ast\_link''---\secref{ss:linking}) in order to link with +additional AST-related facilities, such as a graphics interface. See +the ``ast\_link\_adam'' command description in +\appref{ss:commanddescriptions} for details of the options available. + +\appendix +\cleardoublepage +\section{\label{ss:classhierarchy}The AST Class Hierarchy} +The following table shows the hierarchy of classes in the AST library. +For a description of each class, you should consult +\appref{ss:classdescriptions}. + +\small +\begin{terminalv} +Object - Base class for all AST Objects + Axis - Store axis information + SkyAxis - Store celestial axis information + Channel - Basic (textual) I/O channel + FitsChan - I/O Channel using FITS header cards + XmlChan - I/O Channel using XML + StcsChan - I/O Channel using IVOA STC-S descriptions + KeyMap - Store a set of key/value pairs + Table - Store a 2-dimensional table of values + Mapping - Inter-relate two coordinate systems + CmpMap - Compound Mapping + DssMap - Map points using Digitised Sky Survey plate solution + Frame - Coordinate system description + CmpFrame - Compound Frame + SpecFluxFrame - Observed value versus spectral position + FluxFrame - Observed value at a given fixed spectral position + FrameSet - Set of inter-related coordinate systems + Plot - Provide facilities for 2D graphical output + Plot3D - Provide facilities for 3D graphical output + Region - Specify areas within a coordinate system + Box - A box region with sides parallel to the axes of a Frame + Circle - A circular or spherical region within a Frame + CmpRegion - A combination of two regions within a single Frame + Ellipse - An elliptical region within a 2-dimensional Frame + Interval - Intervals on one or more axes of a Frame. + NullRegion - A boundless region within a Frame + PointList - A collection of points in a Frame + Polygon - A polygonal region within a 2-dimensional Frame + Prism - An extrusion of a Region into orthogonal dimensions + Stc - Represents an generic instance of an IVOA STC-X description + StcResourceProfile - Represents an an IVOA STC-X ResourceProfile + StcSearchLocation - Represents an an IVOA STC-X SearchLocation + StcCatalogEntryLocation - Represents an an IVOA STC-X CatalogEntryLocation + StcObsDataLocation - Represents an an IVOA STC-X ObsDataLocation + SkyFrame - Celestial coordinate system description + SpecFrame - Spectral coordinate system description + DSBSpecFrame - Dual sideband spectral coordinate system description + TimeFrame - Time coordinate system description + GrismMap - Models the spectral dispersion produced by a grism + IntraMap - Map points using a private transformation function + LutMap - Transform 1-dimensional coordinates using a lookup table + MathMap - Transform coordinates using mathematical expressions + MatrixMap - Map positions by multiplying them by a matrix + NormMap - Normalise coordinates using a supplied Frame + PcdMap - Apply 2-dimensional pincushion/barrel distortion + PermMap - Coordinate permutation Mapping + PolyMap - General N-dimensional polynomial Mapping + RateMap - Calculates an element of a Mapping's Jacobian matrix + SelectorMap - Locates positions within a set of Regions + ShiftMap - Shifts each axis by a constant amount + SlaMap - Sequence of celestial coordinate conversions + SpecMap - Sequence of spectral coordinate conversions + SphMap - Map 3-d Cartesian to 2-d spherical coordinates + SwitchMap - Encapuslates a set of alternate Mappings + TimeMap - Sequence of time coordinate conversions + TranMap - Combine fwd. and inv. transformations from two Mappings + UnitMap - Unit (null) Mapping + WcsMap - Implement a FITS-WCS sky projection + WinMap - Match windows by scaling and shifting each axis + ZoomMap - Zoom coordinates about the origin +\end{terminalv} +\normalsize + +\cleardoublepage +c+ +\section{\label{ss:functiondescriptions}AST Function Descriptions} +\small +\include{c_routines} +\normalsize +c- +f+ +\section{\label{ss:functiondescriptions}AST Routine Descriptions} +\small +\include{f_routines} +\normalsize +f- + +\cleardoublepage +\section{\label{ss:attributedescriptions}AST Attribute Descriptions} +\small +c+ +\include{c_attribs} +c- +f+ +\include{f_attribs} +f- +\normalsize + +\cleardoublepage +\section{\label{ss:classdescriptions}AST Class Descriptions} +\small +c+ +\include{c_classes} +c- +f+ +\include{f_classes} +f- +\normalsize + +\cleardoublepage +\section{\label{ss:commanddescriptions}UNIX Command Descriptions} +The commands described here are provided for use from the UNIX shell +to assist with developing software which uses AST. To use these +commands, you should ensure that the directory +``/star/bin''\footnote{Or the equivalent directory if AST is installed +in a non-standard location.} is on your PATH. +\small +c+ +\include{c_commands} +c- +f+ +\include{f_commands} +f- +\normalsize + +c+ +\cleardoublepage +\section{\label{ss:memoryfunctions}AST Memory Management and Utility Functions} +AST provides a memory management layer that can be used in place of +system functions such as \texttt{malloc}, \texttt{free}, \texttt{realloc}, +\emph{etc.} The AST replacements for these functions ( \texttt{astMalloc}, +\texttt{astFree} and \texttt{astRealloc}) add extra information to each +allocated memory block that allows AST to check the validity of supplied +pointers. For example, this extra information allows \texttt{astFree} to +detect if the supplied pointer has already been freed, and if so to issue +an appropriate error message. The existence of this extra information is +invisible to outside callers, and stored in a header block located just +before the returned memory block. + +In addition to the standard functions, AST provides other memory management +functions, such as: + +\begin{description} +\item [\texttt{astStore}] - stores data in dynamically allocated memory, allocating +the memory (or adjusting the size of previously allocated memory) to match +the amount of data to be stored. +\item [\texttt{astGrow}] - allocates and expands memory to hold an adjustable-sized array. +\item [\texttt{astAppendString}] - allocates and expands memory to hold a +concatenated string. +\end{description} + +Theses are just a few of the available utilities functions in the AST +memory management layer. Prototypes for all AST memory management +functions are included in the header file ``\texttt{ast.h}''. + +An important restriction on these functions is that pointers created by +other memory management functions, such as the system version of \texttt{malloc} \emph{etc.}, should never supplied to an AST memory management +function. Only pointers created by AST should be used by these functions. + +In addition to memory management functions, AST provides various other +utility functions, such as a basic regular expression facility, and other +string manipulation functions. These are also documented in this appendix. + +The AST memory management layer is implemented on top of the usual \texttt{malloc}, {tt free} and \texttt{realloc} functions. By default these will be +the standard functions provided by . However, the facilities of +the STARMEM package (included in the Starlink Software Collection) can be +used to specify alternative functions to use. This requires that AST be +configured using the ``--with-starmem'' option when it is built. + +The STARMEM package provides a wrapper for the standard malloc +implementation that enables the user to switch malloc schemes at runtime +by setting the STARMEM\_MALLOC environment variable. Currently allowed +values for this variable are: + +\begin{description} +\item [SYSTEM] - standard system malloc/free - the default +\item [DL] - Doug Lea's malloc/free +\item [GC] - Hans-Boehm Garbage Collection +\end{description} + +\small +\include{memory_routines} +\normalsize +c- + +\newpage +\section{\xlabel{FitsWcsCoverage}\label{ss:fitswcscoverage}FITS-WCS Coverage} + +This appendix gives details of the FitsChan class +implementation of the conventions described in the FITS-WCS papers +available at +\url{http://fits.gsfc.nasa.gov/fits_wcs.html}. These conventions are +used only if the Encoding attribute of the FitsChan +has the value ``FITS-WCS'' (whether set explicitly or defaulted). It +should always be possible for a FrameSet to be read +(using the +c+ +astRead +c- +f+ +AST\_READ +f- +function) from a FitsChan containing a header which conforms to these +conventions. However, only those FrameSets which are compatible with the +FITS-WCS model can be \emph{written} to a FitsChan using the +c+ +astWrite +c- +f+ +AST\_WRITE +f- +function. For instance, if the current Frame of a +FrameSet is re-mapped using, say, an arbitrary MathMap +then the FrameSet will no longer be compatible with the FITS-WCS model, +and so will not be written out successfully to a FitsChan. + +The following sub-sections describe the details of the implementation of +each of the first four FITS-WCS papers. Here, the term ``pixel axes'' is +used to refer to the FITS pixel coordinates (i.e. the centre of the +first image pixel has a value 1.0 on each pixel axis); the term ``IWC +axes'' is used to refer to the axes of the Intermediate World Coordinate +system; and the term ``WCS axes'' is used to refer to the axes of the final +physical coordinate system described by the CTYPE\emph{i} keywords. + +\subsection{Paper I - General Linear Coordinates} +When reading a FrameSet from a FitsChan, these conventions are used if the CTYPE\emph{i} keyword +values within the FitsChan do not conform to the conventions described in +later papers, in which case the axes are assumed to be linear. When +writing a FrameSet to a FitsChan, these conventions are used for axes +which are described by a simple Frame (\emph{i.e.} not a +SkyFrame, SpecFrame, \emph{etc.}). + +Table \ref{tab:fitspaper1} describes the use made by AST of each keyword +defined by FITS-WCS paper I. + +\begin{table}[htbp] +\begin{tabular}{|l|p{2.5in}|p{2.5in}|} +\hline +\multicolumn{1}{|c|}{\textbf{Keyword}} & \multicolumn{1}{c|}{\textbf{Read}} +& \multicolumn{1}{c|}{\textbf{Write}} \\ \hline + +\fitskey{WCSAXES\emph{a}}{Ignored.}{Set to the number of axes in the WCS +Frame - only written if different to NAXIS.} + +\fitskey{CRVAL\emph{ia}}{Used to create the pixel to WCS +Mapping.}{Always written (see ``Choice of Reference +Point'' below).} + +\fitskey{CRPIX\emph{ja}}{Used to create the pixel to WCS Mapping.}{Always +written (see ``Choice of Reference Point'' below).} + +\fitskey{CDELT\emph{ia}}{Used to create the pixel to WCS Mapping.}{Only +written if the CDMatrix attribute of the FitsChan is +set to zero.} + +\fitskey{CROTA\emph{i}}{Used to create the pixel to WCS Mapping.}{Only +written in FITS-AIPS and FITS-AIPS++ encodings.} + +\fitskey{CTYPE\emph{ia}}{Used to choose the class and attributes of the +WCS Frame, and to create the pixel to WCS Mapping (note, ``STOKES'' and +``COMPLEX'' axes are treated as unknown linear axes).}{Always written +(see ``Use and Choice of CTYPE keywords'' below).} + +\fitskey{CUNIT\emph{ia}}{Used to set the Units attributes +of the WCS Frame.}{Only written if the Units attribute of the WCS Frame +has been set explicitly. If so, the Units value for each axis is used as +the CUNIT value.} + +\fitskey{PC\emph{i\_j}\emph{a}}{Used to create the pixel to WCS +Mapping.}{Only written if the CDMatrix attribute of the FitsChan is set to +zero.} + +\fitskey{CD\emph{i\_j}\emph{a}}{Used to create the pixel to WCS +Mapping.}{Only written if the CDMatrix attribute of the FitsChan is set to +a non-zero value.} + +\fitskey{PV\emph{i\_ma}}{Ignored for linear axes.}{Not written if the axes +are linear.} + +\fitskey{PS\emph{i\_ma}}{Ignored.}{Not used.} + +\fitskey{WCSNAME\emph{a}}{Used to set the Domain attribute +of the WCS Frame.}{Only written if the Domain attribute of the WCS Frame +has been set explicitly. If so, the Domain value is used as the WCSNAME +value.} + +\fitskey{CRDER\emph{ia}}{Ignored.}{Not used.} + +\fitskey{CSYER\emph{ia}}{Ignored.}{Not used.} + +\hline +\end{tabular} +\vspace{3.mm} +\caption{Use of FITS-WCS Paper I keywords} +\label{tab:fitspaper1} +\end{table} + +\subsubsection{Requirements for a Successful Write Operation} +When writing a FrameSet in which the WCS +Frame is a simple Frame to a FitsChan, +success depends on the Mapping from pixel coordinates +(the base Frame in the FrameSet) to the WCS Frame being linear. The write +operation will fail if this is not the case. + +\subsubsection{Use and Choice of CTYPE\emph{i} keywords} +When reading a FrameSet from a FitsChan the CTYPE\emph{i} values in the FitsChan are used to set the +Symbol attributes of the corresponding WCS Frame. The Label attributes of the WCS Frame are set from +the CNAME\emph{i} keywords, if present in the header. Otherwise they are set +from the CTYPE\emph{i} comments strings in the header, so long as each +axis has a unique non-blank comment. Otherwise, the Label attributes are +set to the CTYPE\emph{i} values. The above procedure is over-ridden if +the axis types conform to the conventions described in paper II or III, +as described below. + +When writing a FrameSet to a FitsChan, each CTYPE\emph{i} value is set to +the value of the Symbol attribute of the corresponding axis in the Frame +being written. If a value has been set explicitly for the axis Label +attribute, it is used as the axis comment (except that any existing +comments in the FitsChan take precedence if the keyword value has not +changed). The above procedure is over-ridden if the Frame is a +SkyFrame or a SpecFrame, in which +case the CTYPE\emph{i} value is derived from the System +attribute of the Frame and the nature of the pixel to WCS Mapping +according to the conventions of papers II and III, as described below. + +\subsubsection{Choice of Reference Point} +When writing a FrameSet to a +FitsChan, the pixel coordinates of the +reference point for linear axes (i.e. the CRPIX\emph{j} values) are +chosen as follows: + +\begin{itemize} +\item If the FrameSet is being written to a FitsChan which previously +contained a set of axis descriptions with the same identifying letter, +then the previous CRVAL\emph{j}values are converted into the coordinate system +of the Frame being written (if possible). These values are then +transformed into the pixel Frame, and the closest integer pixel values +are used as the CRPIX keywords. +\item If the above step could not be performed for any reason, the +central pixel is used as the reference point. This requires the image +dimensions to be present in the FitsChan in the form of a set of +NAXIS\emph{j} keyword values. +\item If both the above two steps failed for any axis, then the pixel +reference position is set to a value of 1.0 on the pixel axis. +\end{itemize} + +The pixel to WCS Mapping is then used to find the corresponding +CRVAL\emph{j}values. + +Again, the above procedure is over-ridden if the Frame is a +SkyFrame or a SpecFrame, in which +case the conventions of papers II and III are used as described below. + + +\subsubsection{Choice of Axis Ordering} +When reading a FrameSet from a +FitsChan, WCS axis $i$ in the current +Frame of the +resulting FrameSet corresponds to axis $i$ in the FITS header. + +When writing a FrameSet to a FitsChan, the axis ordering for the FITS +header is chosen to make the CD\emph{i\_j} or PC\emph{i\_j} matrix +predominately diagonal. This means that the axis numbering in the FITS +header will not necessarily be the same as that in the AST Frame. + +\subsubsection{Alternate Axis Descriptions} +When reading a FrameSet from a +FitsChan which contains alternate axis descriptions, +each complete set of axis descriptions results in a single Frame being added +to the final FrameSet, connected via an appropriate +Mapping to the base pixel Frame. The Ident attribute of the Frame is set to hold the single alphabetical +character which is used to identify the set of axis descriptions within +the FITS header (a single space is used for the primary axis descriptions). + +When writing a FrameSet to a FitsChan, it is assumed that the base Frame +represents pixel coordinates, and the current Frame represents the +primary axis descriptions. If there are any other Frames present in the +FrameSet, an attempt is made to create a complete set of ``alternate'' +set of keywords describing each additional Frame. The first character in +the Ident attribute of the Frame is used as the single character +descriptor to be appended to the keyword, with the proviso that a given +character can only be used once. If a second Frame is found with an Ident +attribute which has already been used, its Ident attribute is ignored and +the next free character is used instead. Note, failure to write a set of +alternate axis descriptions does not result in failure of the entire +write operation: the primary axis descriptions are still written, +together with any other alternate axis descriptions which can be produced +successfully. + +\subsection{Paper II - Celestial Coordinates} +These conventions are used when reading a FrameSet +from a FitsChan containing appropriate CTYPE\emph{i} +values, and when writing a FrameSet in which the WCS Frame +is a SkyFrame. + +Table \ref{tab:fitspaper2} describes the use made by AST of each keyword +whose meaning is defined or extended by FITS-WCS paper II. + +\begin{table}[htbp] +\begin{tabular}{|l|p{2.5in}|p{2.5in}|} +\hline +\multicolumn{1}{|c|}{\textbf{Keyword}} & \multicolumn{1}{c|}{\textbf{Read}} +& \multicolumn{1}{c|}{\textbf{Write}} \\ \hline + +\fitskey{CTYPE\emph{ia}}{All coordinate systems and projection types +listed in paper II are supported (note, ``CUBEFACE'' axes are treated as +unknown linear axes). In addition, "-HPX" (HEALPix) and "-XPH" (polar +HEALPix) are supported.}{Determined by the System attribute +of the SkyFrame and the WcsType attribute of the +WcsMap within the FrameSet.} + +\fitskey{CUNIT\emph{ia}}{Ignored (assumed to be 'degrees').}{Not written.} + +\fitskey{PV\emph{i\_ma}}{Used to create the pixel to WCS Mapping (values +are stored as attributes of a WcsMap within this Mapping).}{Values are +obtained from the WcsMap in the pixel to WCS Mapping.} + +\fitskey{LONPOLE\emph{a}}{Used to create the pixel to WCS Mapping. Also +stored as a PVi\_m attribute for the longitude axis of the WcsMap.}{Only +written if not equal to the default value defined in paper II (see +``Choice of LONPOLE/LATPOLE'' below).} + +\fitskey{LATPOLE\emph{a}}{Used to create the pixel to WCS Mapping. Also +stored as a PV attribute for the longitude axis of the WcsMap.}{Only +written if not equal to the default value defined in paper II (see +``Choice of LONPOLE/LATPOLE'' below).} + +\fitskey{RADESYS\emph{a}}{Used to set the attributes of the SkyFrame. All +values supported except that ecliptic coordinates are currently always +assumed to be FK5.}{Always written. Determined by the System attribute of +the SkyFrame.} + +\fitskey{EQUINOX\emph{a}}{Used to set the Equinox attribute +of the SkyFrame.}{Written if relevant. Determined by the Equinox attribute of +the SkyFrame.} + +\fitskey{EPOCH}{Used to set the Equinox attribute of the SkyFrame.}{Only +written if using FITS-AIPS and FITS-AIPS++ encodings. Determined by the Equinox attribute +of the SkyFrame.} + +\fitskey{MJD-OBS}{Used to set the Epoch attribute of the +SkyFrame. DATE-OBS is used if MJD-OBS is not present. A default value based on +RADESYS and EQUINOX is used if used if DATE-OBS is not present +either.}{Determined by the Epoch attribute of the SkyFrame. Only written +if this attribute has been set to an explicit value (in which case +DATE-OBS is also written).} + +\hline +\end{tabular} +\vspace{3.mm} +\caption{Use of FITS-WCS Paper II keywords} +\label{tab:fitspaper2} +\end{table} + +\subsubsection{Requirements for a Successful Write Operation} +When writing a FrameSet in which the WCS +Frame is a SkyFrame to a +FitsChan, success depends on the following conditions +being met: + +\begin{enumerate} +\item The Mapping from pixel coordinates (the base Frame +in the FrameSet) to the WCS SkyFrame includes a WcsMap. +\item The Mapping prior to the WcsMap (\emph{i.e.} from pixel to IWC) is linear. +\item The Mapping after the WcsMap (\emph{i.e.} from native spherical to +celestial coordinates) is a spherical rotation for the +celestial axes, and linear for any other axes. +\item The TabOK attribute is set to a non-zero positive value in the FitsChan, +and the longitude and latitude axes are separable. In this case the Mapping will +be described by a pair of 1-dimensional look-up tables, using the ``-TAB'' +algorithm described in FITS-WCS paper III. +\end{enumerate} + +If none of the above conditions hold, the write operation will be +unsuccessful. + +\subsubsection{Choice of LONPOLE/LATPOLE} +When writing a FrameSet to a FitsChan, +the choice of LONPOLE and LATPOLE values is determined as follows: + +\begin{enumerate} + +\item If the projection represented by the WcsMap is +azimuthal, then any values set for attributes ``PV\emph{i}\_3'' +and ``PV\emph{i}\_4'' (where ``\emph{i}'' is the index of the longitude axis) +within the WcsMap are used as the LONPOLE and LATPOLE values. Reading a +FrameSet from a FITS-WCS header +results in the original LONPOLE and LATPOLE values being stored within a +WcsMap within the FrameSet. Consequently, if a FrameSet is read from a +FITS-WCS header and it is subsequently written out to a new FITS-WCS +header, the original LONPOLE and LATPOLE values will usually be used in +the new header (the exception being if the WcsMap has been explicitly +modified before being written out again). Any extra rotation of the sky +is absorbed into the CD\emph{i\_j} or PC\emph{i\_j} matrix (this is +possible only if the projection is azimuthal). + +\item If the projection represented by the WcsMap is azimuthal but no +values have been set for the ``PV\emph{i}\_3'' and ``PV\emph{i}\_4'' +attributes within the WcsMap, then the default LONPOLE and LATPOLE values +are used. This results in no LONPOLE or LATPOLE keywords being stored in +the header since default values are never stored. Any extra rotation of +the sky is absorbed into the CD\emph{i\_j} or PC\emph{i\_j} matrix (this +is possible only if the projection is azimuthal). + +\item If the projection represented by the WcsMap is not azimuthal, +then the values of LONPOLE and LATPOLE are found by transforming the +coordinates of the celestial north pole (\emph{i.e} longitude zero, +latitude $+\pi/2$) into native spherical coordinates using the inverse of +the Mapping which follows the WcsMap. + +\end{enumerate} + +\subsubsection{User Defined Fiducial Points} +When reading a FrameSet from a FitsChan, projection parameters +PV\emph{i}\_0, PV\emph{i}\_1 and PV\emph{i}\_2 (for longitude axis +``\emph{i}'') are used to indicate a user-defined fiducial point as +described in section 2.5 of paper II. This results in a shift of IWC +origin being applied \emph{before} the WcsMap which converts +IWC into +native spherical coordinates. The values of these projection parameters, +if supplied, are stored as the corresponding PVi\_m attributes +of the WcsMap. + +When writing a FrameSet to a FitsChan, the PV attributes of the WcsMap +determine the native coordinates of the fiducial point (the fixed +defaults for each projection described in paper II are used if the PV +attributes of the WcsMap have not been assigned a value). The +corresponding celestial coordinates are used as the CRVAL\emph{i} +keywords and the corresponding pixel coordinates as the CRPIX\emph{j} +keywords. + +\subsubsection{Common Non-Standard Features} +A collection of common non-standard features are supported when reading a +FrameSet from a FitsChan, in addition +to those embodied within the +available encodings of the FitsChan class. These are translated into the +equivalent standard features before being used to create a FrameSet. +Note, the reverse operation is never performed: it is not possible to +produce non-standard features when writing a FrameSet to a FitsChan +(other than those embodied in the available encodings of the FitsChan +class). The supported non-standard features include: + +\begin{itemize} +\item EQUINOX keywords with string values equal to a date preceded +by the letter B or J (\emph{e.g.} ``B1995.0''). + +\item EQUINOX or EPOCH keywords with value zero (these are converted to +B1950). + +\item The IRAF ``ZPX'' projection is represented by a +WcsMap with type of +AST\_\_ZPN. Projection parameter values are read from any WAT\emph{i\_nnn} +keywords, and corresponding PVi\_m attributes are set in the +WcsMap. The WAT\emph{i\_nnn} keywords may specify corrections to the basic +ZPN projection by including ``lngcor'' or ``latcor'' terms. These are +supported if they use half cross-terms, in either simple or Chebyshev +representation. + +\item The IRAF ``TNX'' projection is represented by a WcsMap with type of +AST\_\_TPN (a distorted TAN projection retained within the WcsMap class +from an early draft of the FITS-WCS paper II). Projection parameter values +are read from any WAT\emph{i\_nnn} keywords, and corresponding PV +attributes are set in the WcsMap. If the TNX projection cannot be +converted exactly into an AST\_\_TPN projection, ASTWARN keywords are +added to the FitsChan containing a warning message (but only if the +Warnings attribute of the FitsChan is set appropriately). Currently, +TNX projections that use half cross-terms, in either simple or Chebyshev +representation, are supported. + +\item ``QV'' parameters for TAN projections (as produced by +\xref{AUTOASTROM}{sun242}{} +\footnote{\url{http://www.astro.gla.ac.uk/users/norman/star/autoastrom/}} +are renamed to the equivalent ``PV'' parameters. + +\item TAN projections that have associated ``PV'' parameters on the +latitude axis are converted to the corresponding TPN (distorted TAN) +projections. This conversion can be controlled using the PolyTan attribute +of the FitsChan class. + +\end{itemize} + +\subsection{Paper III - Spectral Coordinates} +These conventions are used when reading a FrameSet +from a FitsChan which includes appropriate +CTYPE\emph{i} values, and when writing a FrameSet in which +the WCS Frame is a SpecFrame. + +Table \ref{tab:fitspaper3} describes the use made by AST of each keyword +whose meaning is defined or extended by FITS-WCS paper III. + +\begin{table}[htbp] +\begin{footnotesize} +\begin{tabular}{|l|p{2.5in}|p{2.5in}|} +\hline +\multicolumn{1}{|c|}{\textbf{Keyword}} & \multicolumn{1}{c|}{\textbf{Read}} +& \multicolumn{1}{c|}{\textbf{Write}} \\ \hline + +\fitskey{CTYPE\emph{ia}}{All coordinate systems and projection types +listed in paper III are supported algorithm (the ``-LOG'' algorithm may +also be applied to non-spectral linear axes; the ``-TAB'' algorithm +requires the TabOK attribute to be set in the FitsChan).}{Determined by the System attribute of the +SpecFrame and the nature of the pixel to SpecFrame +Mapping.} + +\fitskey{CUNIT\emph{ia}}{Used to set the Units attribute of +the SpecFrame (note, SpecFrames always have an ``active'' Units attribute +(see astSetActiveUnit).}{Always written.} + +\fitskey{PV\emph{i\_ma}}{Used to create the pixel to WCS Mapping (values +are stored as attributes of a GrismMap).} +{Set from the attributes of the GrismMap, if present, and if set explicitly.} + +\fitskey{SPECSYS\emph{a}}{Used to set the StdOfRest +attribute of the SpecFrame (all systems are supported except CMBDIPOL).} +{Set from the StdOfRest attribute of the SpecFrame, but only if it has been +set explicitly.} + +\fitskey{SSYSOBS\emph{a}}{Ignored.}{Never written.} + +\fitskey{OBSGEO-X/Y/Z}{Used to set the ObsLon and +ObsLat attributes of the Frame (the observers +height above sea level is ignored).}{Set from the ObsLon and ObsLat +attributes of the Frame, if they have been set explicitly (it is +assumed that the observer is at sea level).} + +\fitskey{MJD-AVG}{Used to set the Epoch attributes of +the SpecFrame.}{Set from the Epoch attribute of the SpecFrame, if it has +been set explicitly.} + +\fitskey{SSYSSRC\emph{a}}{Used to set the SourceVRF attribute of the +SpecFrame +(all systems are supported except CMBDIPOL).} {Set from the SourceVRF +attribute of the SpecFrame.} + +\fitskey{ZSOURCE\emph{a}}{Used to set the SourceVel +attribute of the SpecFrame (the SourceVRF attribute +is first set to the system indicated by the SSYSSRC keyword, and the +ZSOURCE value is then converted to an apparent radial velocity and stored +as the SourceVel attribute).} +{Set from the SourceVel attribute of +the SpecFrame, if it has been set explicitly (the SourceVel value is +first converted from apparent radial velocity to redshift).} + +\fitskey{VELOSYS\emph{a}}{Ignored.}{Set from the attributes of the +SpecFrame that define the standard of rest and the observers position.} + +\fitskey{RESTFRQ\emph{a}}{Used to set the RestFreq +attribute of the SpecFrame.}{Set from the RestFreq attribute of the +SpecFrame, but only if the System attribute is not set to +``WAVE'', ``VOPT'', ``ZOPT'' or ``AWAV'', and only if RestFreq has been set +explicitly.} + +\fitskey{RESTWAV\emph{a}}{Used to set the RestFreq +attribute of the SpecFrame (after conversion from wavelength to frequency).} +{Set from the RestFreq attribute of the SpecFrame (after conversion), but only if the +System attribute is set to ``WAVE'', ``VOPT'', ``ZOPT'' or +``AWAV'', and only if RestFreq has been set explicitly.} + +\fitskey{CNAME\emph{ia}}{Used to set the Label attributes of +the WCS Frame keywords.}{Set from the Label attributes of the WCS Frame, +if they have been set explicitly.} +\hline +\end{tabular} +\end{footnotesize} +\vspace{3.mm} +\caption{Use of FITS-WCS Paper III keywords} +\label{tab:fitspaper3} +\end{table} + +\subsubsection{Requirements for a Successful Write Operation} +When writing a FrameSet in which the WCS Frame is a SpecFrame to a +FitsChan, the write operation is successful only if +the Mapping from pixel coordinates (the base Frame +in the FrameSet) to the SpecFrame satisfies one of the following conditions: + +\begin{enumerate} +\item It is linear. +\item It is logarithmic. +\item It is linear if the SpecFrame were to be re-mapped into one of the +other spectral systems supported by FITS-WCS paper III. +\item It contains a GrismMap, and the Mapping before the GrismMap (from +pixel coordinates to grism parameter) is linear, and the Mapping after the +GrismMap is either null or represents a change of spectral system from wavelength (air or +vacuum) to one of the supported spectral systems. +\item The TabOK attribute is set to a non-zero positive value in the FitsChan. +\end{enumerate} + +If none of the above conditions hold, the write operation will be +unsuccessful. Note, if the FitsChan's TabOK attribute is set to a positive +non-zero value then any Mapping that does not meet any of the earlier conditions +will be written out as a look-up table, using the ``-TAB'' algorithm described +in FITS-WCS paper III. If the TabOK attribute is to zero (the default) or +negative in the FitsChan, then the write operation will be unsuccessful unless +one of the eaerlier conditions is met.\footnote{If the -TAB algorithm is used, the +positive value of the TabOK attribute is used as the table version number +(the EXTVER header) in the associated FITS binary table.} + +\subsubsection{Common Non-Standard Features} +The following non-standard features are supported when reading spectral +axes from a FitsChan: + +\begin{itemize} +\item Conversion of ``-WAV'', ``-FRQ'' and ``-VEL'' algorithm codes +(specified in early drafts of paper III) to the corresponding +``-X2P'' form. +\item Conversion of ``RESTFREQ'' to ``RESTFRQ'' +\end{itemize} + +\subsection{Paper IV - Coordinate Distortions} + +This paper proposes that an additional 4 character code be appended to +the end of the CTYPE\emph{i} keyword to specify the nature of any +distortion away from the basic algorithm described by the first 8 +characters of the CTYPE\emph{i} value. Currently AST ignores all such +codes when reading a FrameSet from a FitsChan (except for the ``-SIP'' code +defined by the Spitzer Space Telescope project - see below). This means that +a FrameSet can still be read from such headers, but the Mapping which gives +the WCS position associated with a given pixel position will reflect only +the basic algorithm and will not include the effects of the distortion. + +If such a FrameSet is then written out to a FitsChan, the resulting +CTYPE\emph{i} keywords will include no distortion code. + +\subsubsection{The ``-SIP'' distortion code} + +The Spitzer Space Telescope project +(\url{http://www.spitzer.caltech.edu/}) +has developed its own system for encoding 2-dimensional image distortion +within a FITS header, based on the proposals of paper IV. A description +of this system is available in +\url{http://ssc.spitzer.caltech.edu/postbcd/doc/shupeADASS.pdf}. In this +system, the presence of distortion is indicated by appending the +distortion code ``-SIP'' to the CTYPE\emph{i} keyword values for the +celestial axes. The distortion takes the form of a polynomial function +which is applied to the pixel coordinates, after subtraction of the +CRPIX\emph{j} values. + +This system is a strictly 2 dimensional system. When reading a +FrameSet from a FitsChan which +includes the ``-SIP'' distortion code, AST assumes that it +is only applied to the first 2 WCS axes in a FITS header (i.e. +CTYPE1 and CTYPE2). If the ``-SIP'' distortion code is attached to other +axes, it will be ignored. The distortion itself is represented by a +PolyMap within the resulting FrameSet. + +If a FrameSet is read from a FitsChan which includes ``-SIP'' +distortion, and an attempt is then made to write this FrameSet out to a +FitsChan, the write operation will fail unless the distortion is +insignificant (\emph{i.e.} is so small that the tests for linearity built +into AST are passed). In this case, no distortion code will be appended to +the resulting CTYPE\emph{i} keyword values. + +\newpage +\section{\xlabel{changes_and_new_features}\label{ss:changes}Release Notes} + +\subsection{Changes Introduced in V1.1} + +The following describes the most significant changes which occurred in +the AST library between versions V1.0 and V1.1 (not the most recent +version): + +\begin{enumerate} + +\item A new ``How To\ldots'' section (\secref{ss:howto}) has been +added to this document. It contains simple recipies for performing +commonly-required operations using AST. + +c+ +\item A new astUnformat function has been provided to read formatted +coordinate values for the axes of a Frame +(\secref{ss:unformattingaxisvalues}). In essence, this function is the +inverse of astFormat. It may be used to decode user-supplied formatted +values representing coordinates, turning them into numerical values +for processing. Celestial coordinates may also be read using this +function (\secref{ss:unformattingskyaxisvalues}) and free-format input +is supported. +c- +f+ +\item A new AST\_UNFORMAT function has been provided to read formatted +coordinate values for the axes of a Frame +(\secref{ss:unformattingaxisvalues}). In essence, this function is the +inverse of AST\_FORMAT. It may be used to decode user-supplied +formatted values representing coordinates, turning them into numerical +values for processing. Celestial coordinates may also be read using +this function (\secref{ss:unformattingskyaxisvalues}) and free-format +input is supported. +f- + +\item The Format attribute string used by a SkyFrame when formatting +celestial coordinate values now allows the degrees/hours field to be +omitted, so that celestial coordinates may be given in (\emph{e.g.}) +arc-minutes and/or arc-seconds +(\secref{ss:formattingskyaxisvalues}). As a result, the degrees/hours +field is no longer included by default. A new ``t'' format specifier +has been introduced (see the Format attribute) to allow minutes and/or +seconds of time to be specified if required. + +c+ +\item A new function astMapBox has been introduced. This allows you to +find the extent of a ``bounding box'' which just encloses another box +after it has been transformed by a Mapping. A typical use might be to +calculate the size which an image would have if it were transformed by +the Mapping. +c- +f+ +\item A new routine AST\_MAPBOX has been introduced. This allows you +to find the extent of a ``bounding box'' which just encloses another +box after it has been transformed by a Mapping. A typical use might be +to calculate the size which an image would have if it were transformed +by the Mapping. +f- + +c+ +\item A new class of Object, the IntraMap, has been introduced +(\secref{ss:intramaps}). This is a specialised form of Mapping which +encapsulates a privately-defined coordinate transformation function +(\emph{e.g.}\ written in C) so that it may be used like any other AST +Mapping. This allows you to create Mappings that perform any +conceivable coordinate transformation. +c- +f+ +\item A new class of Object, the IntraMap, has been introduced +(\secref{ss:intramaps}). This is a specialised form of Mapping which +encapsulates a privately-defined coordinate transformation routine +(\emph{e.g.}\ written in Fortran) so that it may be used like any +other AST Mapping. This allows you to create Mappings that perform any +conceivable coordinate transformation. +f- + +\item The internal integrity of a FrameSet is now automatically +preserved whenever changes are made to any attributes which affect the +current Frame (either by setting or clearing their values). This is +accomplished by appropriately re-mapping the current Frame to account +for any change to the coordinate system which it represents +(\secref{ss:framesetintegrity}). + +\item The internal structure of a FrameSet is now automatically tidied +to eliminate redundant nodes whenever any of its Frames is removed or +re-mapped. Automatic simplification of any compound Mappings which +result may also occur. The effect of this change is to prevent the +accumulation of unnecessary structure in FrameSets which are +repeatedly modified. + +c+ +\item Some improvements have been made to the algorithms for +simplifying compound Mappings, as used by astSimplify. +c- +f+ +\item Some improvements have been made to the algorithms for +simplifying compound Mappings, as used by AST\_SIMPLIFY. +f- + +\item The textual representation used for some Objects +(\emph{i.e.}\ when they are written to a Channel) has changed +slightly, but remains compatible with earlier versions of AST. + +c+ +\item Interfaces to the internal functions and macros used by AST for +handling memory and error conditions are now provided \emph{via} the +``ast.h'' header file. This is for the benefit of those writing +(\emph{e.g.}) new graphics interfaces for AST. +c- + +c+ +\item A problem has been fixed which could result when using astRead +to read FITS headers in which the CDELT value is zero. Previously, +this could produce a Mapping whose inverse transformation was not +defined and this could unnecessarily restrict the use to which it +could be put. The problem has been overcome by supplying a suitable +small CDELT value for FITS axes which have only a single pixel. +c- +f+ +\item A problem has been fixed which could result when using AST\_READ +to read FITS headers in which the CDELT value is zero. Previously, +this could produce a Mapping whose inverse transformation was not +defined and this could unnecessarily restrict the use to which it +could be put. The problem has been overcome by supplying a suitable +small CDELT value for FITS axes which have only a single pixel. +f- + +\item A bug has been fixed which could occasionally cause a MatrixMap +to be used with the wrong Invert attribute value when it forms part of +c+ +a compound Mapping which is being simplified using astSimplify. +c- +f+ +a compound Mapping which is being simplified using AST\_SIMPLIFY. +f- + +f+ +\item A bug has been fixed which could cause the AST\_\_BAD parameter +to have an incorrect value on some platforms. +f- + +\item A problem has been fixed which could prevent tick marks being +drawn on a coordinate axis close to a singularity in the coordinate +system. +\end{enumerate} + +\subsection{Changes Introduced in V1.2} + +The following describes the most significant changes which occurred in +the AST library between versions V1.1 and V1.2 (not the most recent +version): + +\begin{enumerate} +c+ +\item A new function, astPolyCurve, has been introduced to allow more +efficient plotting of multiple geodesic curves +(\secref{ss:plottinggeodesics}). +c- +f+ +\item A new routine, AST\_POLYCURVE, has been introduced to allow more +efficient plotting of multiple geodesic curves +(\secref{ss:plottinggeodesics}). +f- + +c+ +\item A new set of functions, astResample$<$X$>$, has been introduced +to perform resampling of gridded data such as images +(\emph{i.e.}\ re-gridding) under the control of a geometrical +transformation specified by a Mapping. +c- +f+ +\item A new set of functions, AST\_RESAMPLE$<$X$>$, has been +introduced to perform resampling of gridded data such as images +(\emph{i.e.}\ re-gridding) under the control of a geometrical +transformation specified by a Mapping. +f- + +\item The command-line options ``$-$pgp'' and ``$-$pgplot'', which +were previously synonymous when used with the ``ast\_link'' and +``ast\_link\_adam'' commands, are no longer synonymous. The option +``$-$pgp'' now causes linking with the Starlink version of PGPLOT +(which uses GKS to generate its output), while ``$-$pgplot'' links +with the standard (or ``native'') version of PGPLOT. + +c+ +\item The function astMapBox has been changed to execute more quickly, +although this has been achieved at the cost of some loss of robustness +when used with difficult Mappings. +c- +f+ +\item The routine AST\_MAPBOX has been changed to execute more +quickly, although this has been achieved at the cost of some loss of +robustness when used with difficult Mappings. +f- + +\item A new value of ``FITS-IRAF'' has been introduced for the +Encoding attribute of a FitsChan. This new encoding provides an +interim solution to the problem of storing coordinate system +information in FITS headers, until the proposed new FITS-WCS standard +becomes stable. + +\item When a FrameSet is created from a set of FITS header cards (by +reading from a FitsChan using a ``foreign'' encoding), the base Frame +of the resulting FrameSet now has its Domain attribute set to +``GRID''. This reflects the fact that this Frame represents FITS data +grid coordinates (equivalent to FITS pixel coordinates---see +\secref{ss:domainconventions}). Previously, this Domain value was not +set. + +c+ +\item astFindFits now ignores trailing spaces in its keyword template. +c- +f+ +\item AST\_FINDFITS now ignores trailing spaces in its keyword template. +f- + +c+ +\item astPutFits now recognises ``D'' and ``d'' as valid exponent +characters in floating point numbers. +c- +f+ +\item AST\_PUTFITS now recognises ``D'' and ``d'' as valid exponent +characters in floating point numbers. +f- + +\item The FitsChan class is now more tolerant of common minor +violations of the FITS standard. + +\item The FitsChan class now incorporates an improved test for the +linearity of Mappings, allowing more reliable conversion of AST data +into FITS (using ``foreign'' FITS encodings). + +c+ +\item Some further improvements have been made to the algorithms for +simplifying compound Mappings, as used by astSimplify. +c- +f+ +\item Some further improvements have been made to the algorithms for +simplifying compound Mappings, as used by AST\_SIMPLIFY. +f- + +\item A new UnitRadius attribute has been added to the SphMap +class. This allows improved simplification of compound Mappings +(CmpMaps) involving SphMaps and typically improves performance when +handling FITS world coordinate information. + +\item A MatrixMap no longer propagates input coordinate values of +AST\_\_BAD automatically to all output coordinates. If certain output +coordinates do not depend on the affected input coordinate(s) because +the relevant matrix elements are zero, then they may now remain valid. + +\item A minor bug has been corrected which could cause certain +projections which involve half the celestial sphere to produce valid +coordinates for the other (unprojected) half of the sphere as well. + +c+ +\item A bug has been fixed which could occasionally cause astConvert +to think that conversion between a CmpFrame and another Frame was +possible when, in fact, it wasn't. +c- +f+ +\item A bug has been fixed which could occasionally cause AST\_CONVERT +to think that conversion between a CmpFrame and another Frame was +possible when, in fact, it wasn't. +f- +\end{enumerate} + +\subsection{Changes Introduced in V1.3} + +The following describes the most significant changes which occurred in +the AST library between versions V1.2 and V1.3 (not the most recent +version): + +\begin{enumerate} +c+ +\item A new set of functions, astResample$<$X$>$, has been introduced to +provide efficient resampling of gridded data, such as spectra and +images, under the control of a geometrical transformation specified by +a Mapping. A variety of sub-pixel interpolation schemes are supported. +c- +f+ +\item A new set of functions, AST\_RESAMPLE$<$X$>$, has been introduced to +provide efficient resampling of gridded data, such as spectra and +images, under the control of a geometrical transformation specified by +a Mapping. A variety of sub-pixel interpolation schemes are supported. +f- + +\item A new class, PcdMap, has been introduced. This is a specialised +form of Mapping which implements 2-dimensional pincushion or barrel +distortion. + +\item A bug has been fixed which could cause a FitsChan to produce too +many digits when formatting floating point values for inclusion in a +FITS header if the numerical value was in the range -0.00099999\ldots +to -0.0001. + +\item A bug has been fixed which could cause a FitsChan to lose the +comment associated with a string value in a FITS header. + +\item A FitsChan now reports an error if it reads a FITS header which +identifies a non-standard sky projection (previously, this was +accepted without error and a Cartesian projection used instead). + +\item A bug has been fixed which could prevent conversion between the +coordinate systems represented by two CmpFrames. This could only occur +if the CmpFrames contained a relatively large number of nested Frames. + +%\item A bug has been fixed which could cause a program to crash if +%FrameSets were nested inside each other (for example, if one FrameSet +%had another FrameSet added to it for use as a Frame or Mapping). The +%problem could only occur if the nested structure was loaded from a data +%c+ +%file (using astRead). +%c- +%f+ +%file (using AST\_READ). +%f- +% +\item Further improvements have been made to the simplification of +compound Mappings, including fixes for several bugs which could cause +indefinite looping or unwanted error messages. + +\item Some memory leaks have been fixed. + +\item A small number of documentation errors have been corrected. +\end{enumerate} + +\subsection{Changes Introduced in V1.4} + +The following describes the most significant changes which have occurred +in the AST library between versions V1.3 and V1.4 (not the most recent +version): + +\begin{enumerate} +c+ +\item A new MathMap class has been introduced. This is a form of +Mapping that allows you to define coordinate transformations in a +flexible and transportable way using arithmetic operations and +mathematical functions similar to those available in C. +c- +f+ +\item A new MathMap class has been introduced. This is a form of +Mapping that allows you to define coordinate transformations in a +flexible and transportable way using arithmetic operations and +mathematical functions similar to those available in Fortran. +f- + +c+ +\item {\bf{WARNING---INCOMPATIBLE CHANGE.}} Transformation functions +used with the IntraMap class (see, for example, astIntraReg) now +require a ``this'' pointer as their first parameter. \textbf{Existing +implementations will not continue to work correctly with this version +of AST unless this parameter is added.} There is no need for existing +software to make use of this pointer, but it must be present. +c- +f+ +\item {\bf{WARNING---INCOMPATIBLE CHANGE.}} Transformation routines +used with the IntraMap class (see, for example, AST\_INTRAREG) now +require a THIS pointer as their first argument. \textbf{Existing +implementations will not continue to work correctly with this version +of AST unless this argument is added.} There is no need for existing +software to make use of this pointer, but it must be present. +f- + +This change has been introduced so that transformation functions can gain +access to IntraMap attributes. + +c+ +\item A new IntraFlag attribute has been added to the IntraMap +class. This allows the transformation functions used by IntraMaps to +adapt to produce the required transformation on a per-IntraMap basis +(\secref{ss:intraflag}). +c- +f+ +\item A new IntraFlag attribute has been added to the IntraMap +class. This allows the transformation routines used by IntraMaps to +adapt to produce the required transformation on a per-IntraMap basis +(\secref{ss:intraflag}). +f- + +\item The Plot attributes MajTickLen and MinTickLen, which control the +length of major and minor tick marks on coordinate axes, may now be +subscripted using an axis number. This allows tick marks of different +lengths to be used on each axis. It also allows tick marks to be +suppressed on one axis only by setting the length to zero. + +\item The value of the Plot attribute NumLab, which controls the +plotting of numerical labels on coordinate axes, no longer has any +effect on whether labelling of a coordinate grid is interior or +exterior (as controlled by the Labelling attribute). + +\item The FitsChan class now provides some support for the +IRAF-specific ``ZPX'' sky projection, which is converted transparently +into the equivalent FITS ``ZPN'' projection (see the description of the +Encoding attribute for details). + +\item The FitsChan class now recognises the coordinate system ``ICRS'' +(International Celestial Reference System) as equivalent to +``FK5''. This is an interim measure and full support for the +(exceedingly small) difference between ICRS and FK5 will be added at a +future release. + +Note that ``ICRS'' is not yet recognised as a coordinate system by other +classes such as SkyFrame, so this change only facilitates the +importation of foreign data. + +\item A bug in the FitsChan class has been fixed which could result in +longitude values being incorrect by 180 degrees when using cylindrical +sky projections, such as the FITS ``CAR'' projection. + +\item A bug in the FitsChan class has been fixed which could result in +the FITS sky projection parameters ProjP(0) to ProjP(9) being +incorrectly named PROJP1 to PROJP10 when written out as FITS cards. + +\item A bug in the FitsChan class has been fixed which could cause +confusion between the FITS-IRAF and FITS-WCS encoding schemes if both +a CD matrix and a PC matrix are erroneously present in a FITS header. + +\item Some minor memory leaks have been fixed. + +\item A small number of documentation errors have been corrected. +\end{enumerate} + +\subsection{Changes Introduced in V1.5} + +The following describes the most significant changes which have +occurred in the AST library between versions V1.4 and V1.5 (not the most +recent version): + +\begin{enumerate} + +\item The FitsChan class has been modified to support the latest draft +FITS WCS standard, described in the two papers ``Representation of world +coordinates in FITS'' (E.W.\,Greisen and M.\,Calabretta, dated 30th +November, 1999), and ``Representation of celestial coordinates in FITS'' +(M.\,Calabretta and E.W.\,Greisen, dated 24th September, 1999). These are +available at +\url{http://www.cv.nrao.edu/fits/documents/wcs/wcs.html}. + +The FITS-WCS encoding now uses these updated conventions. The main +changes are: + +\begin{itemize} +\item Rotation and scaling of pixel axes is now represented by a matrix +of \texttt{CDj\_i} keywords instead of a combination of \texttt{PCjjjiii} and +\texttt{CDELTj} keywords. +\item Projection parameters are now associated with particular axes and +are represented by \texttt{PVi\_m} keywords instead of the \texttt{PROJPm} +keywords. +\item The tangent plane projection (``TAN'') can now include optional +polynomial correction terms. +\item An entire set of keywords must be supplied for each set of secondary +axis descriptions, and each such keyword must finish with a single +character indicating which set it belongs to. This means that keywords +which previously occupied eight characters have been shorten to seven to +leave room for this extra character. Thus \texttt{LONGPOLE} has become \texttt{LONPOLE} and \texttt{RADECSYS} has become \texttt{RADESYS}. +\end{itemize} + +\item Two new encodings have been added to the FitsChan class: +\begin{description} + +\item [FITS-PC] This encoding uses the conventions of the now superseded +FITS WCS paper by E.W.\,Greisen and M.\,Calabretta which used keywords +\texttt{CDELTj} and \texttt{PCjjjiii} to describe axis scaling and rotation. +These are the conventions which were used by the FITS-WCS encoding prior +to version 1.5 of AST. This encoding is provided to allow existing data +which use these conventions to be read. It should not in general be used +to create new data. + +\item [FITS-AIPS] This encoding is based on the conventions described in the +document ``Non-linear Coordinate Systems in AIPS'' by Eric W. Greisen +(revised 9th September, 1994 and available by ftp from fits.cv.nrao.edu +/fits/documents/wcs/aips27.ps.Z). This encoding uses \texttt{CROTAi} and +\texttt{CDELTi} keywords to describe axis rotation and scaling. + +\end{description} + +\item The FitsChan class now provides some support for the IRAF-specific +``TNX'' sky projection, which is converted transparently into the +equivalent FITS ``TAN'' projection (see the description of the Encoding +attribute for details). + +\item FrameSets originally read from a DSS encoded FITS header can now be +written out using the FITS-WCS encoding (a TAN projection with correction +terms will be used) in addition to the DSS encoding. The reverse is also +possible: FrameSets originally read from a FITS-WCS encoded FITS header +and which use a TAN projection can now be written out using the DSS +encoding. + +\item The algorithm used by the FitsChan class to verify that a FrameSet +conforms to the FITS-WCS model has been improved so that FrameSets +including more complex mixtures of parallel and serial Mappings +can be written out using the FITS-WCS encoding. + +\item The FitsChan class has been changed so that long strings included in +the description of an Object can be saved and restored without truncation +when using the NATIVE encoding. Previously, very long Frame titles, +mathematical expressions, \emph{etc.} were truncated if they exceeded the +capacity of a single FITS header card. They are now split over several +header cards so that they can be restored without truncation. Note, this +facility is only available when using NATIVE encoding. + +\item The FitsChan class has a new attribute called Warnings which +can be used to select potentially dangerous conditions under which +warnings should be issued. These conditions include (for instance) +unsupported features within non-standard projections, missing keywords +for which default values will be used, \emph{etc}. + +\item The WcsMap class has been changed to support the changes made to the +FITS-WCS encoding in the FitsChan class: +\begin{itemize} +\item Projection parameters are now associated with a particular axis and +are specified using a new set of attributes called PVj\_m. Here, ``j'' is +the index of an axis of WcsMap, and ``m'' is the index of the projection +parameter. +\item The old attributes ProjP(0) to ProjP(9) are still available but are +now deprecated in favour of the new PVj\_m attributes. They are interpreted +as aliases for PV(axlat)\_0 to PV(axlat)\_9, where ``axlat'' is the index of +the latitude axis. +\item The GLS projection projection has been renamed as SFL, but the +AST\_\_GLS type has been retained as an alias for AST\_\_SFL. +\end{itemize} + +\end{enumerate} + +\subsection{Changes Introduced in V1.6} + +The following describes the most significant changes which have +occurred in the AST library between versions V1.5 and V1.6: + +\begin{enumerate} + +c+ +\item The C interface to several methods (astTranN, astMark and +astPolyCurve) have been changed to make them easier to call from C++. +Parameters which previously had type ``double (*)[]'' have been changed +to the simpler ``double *''. Using the old types may result in non-fatal +compiler warnings, but should not change the behaviour of the methods. +c- + +\item A bug has been fixed in the Plot class which could cause groups +of tick marks to be skipped when using very small gaps. + +\item A bug has been fixed in the Plot class which could cause axes to be +labeled outside the visible window, resulting in no axes being visible. + +\item The FITS-WCS encoding used by the FitsChan class now includes the +WCSNAME keyword. When creating a FrameSet from FITS headers, the values of +the WCSNAME keywords are now used as the Domain names for the corresponding +Frames in the returned FrameSet. When writing a FrameSet to a FITS header +the Domain names of each Frame are stored in WCSNAME keywords in the +header. + +\item The FITS-WCS encoding used by the FitsChan class now attempts to +retain the identification letter associated with multiple axis +descriptions. When reading a FrameSet from a FITS header, the identification +letter is stored in the Ident attribute for each Frame. When writing a +FrameSet to a FITS header, the identification letter is read from the +Ident attribute of each Frame. The letter to associate with each Frame +can be changed by assigning a new value to the Frame's Ident attribute. + +\item The FITS-WCS, FITS-PC, FITS-IRAF and FITS-AIPS encodings used by the +FitsChan class now create a SkyFrame with the System attribute set to +``Unknown'' if the CTYPE keywords in the supplied header refers to an +unknown celestial coordinate system. Previously, a Frame was used instead +of a SkyFrame. + +\item The FITS-WCS, FITS-PC, FITS-IRAF and FITS-AIPS encodings used by the +FitsChan class no longer report an error if the FITS header contains no +CTYPE keywords. It is assumed that a missing CTYPE keyword implies that +the world coordinate system is linear and identically equal to +``intermediate world coordinates''. + +\item The new value ``noctype'' is now recognized by the Warnings attribute +of the FitsChan class. This value causes warnings to be issued if CTYPE +keywords are missing from foreign encodings. + +\item A new attribute called AllWarnings has been added to the FitsChan +class. This is a read-only, space separated list of all the known condition +names which can be specified in the Warnings attribute. + +\item The FitsChan class now attempts to assigns a Title to each Frame in +a FrameSet read using a foreign encoding. The Title is based on the Domain +name of the Frame. If the Frame has no Domain name, the default Title +supplied by the Frame class is retained. + +\item The FitsChan class uses the comments associated with CTYPE +keywords as axis labels when reading a foreign encoding. This behaviour +has been modified so that the default labels provided by the Frame class +are retained (instead of using the CTYPE comments) if any of the CTYPE +comments are identical. + +\item A new ``interpolation'' scheme identified by the symbolic constant +AST\_\_BLOCKAVE has been added to the AST\_RESAMPLE$<$X$>$ set of +functions. The new scheme calculates each output pixel value by finding +the mean of the input pixels in a box centred on the output pixel. + +\item The SkyFrame class can now be used to represent an arbitrary spherical +coordinate system by setting its System attribute to ``Unknown''. + +\item The indices of the latitude and longitude axes of a SkyFrame can +now be found using new read-only attributes LatAxis and LonAxis. The +effects of any axis permutation is taken into account. + +\item A new attribute called Ident has been added to the Object class. +This serves the same purpose as the existing ID attribute, but (unlike ID) +its value is transferred to the new Object when a copy is made. + +\item A bug has been fixed which could prevent complex CmpFrames +behaving correctly (for instance, resulting in the failure of attempts +to find a Mapping between a CmpFrame and itself). + +\end{enumerate} + +\subsection{Changes Introduced in V1.7} + +The following describes the most significant changes which have +occurred in the AST library between versions V1.6 and V1.7: + +\begin{enumerate} + +\item The Frame class has a new method called +f+ +AST\_ANGLE +f- +c+ +astAngle +c- +which returns the angle subtended by two points at a third point within a +2 or 3 dimensional Frame. + +\item The Frame class has a new method called +f+ +AST\_OFFSET2 +f- +c+ +astOffset2 +c- +which calculates a position which is offset away from a given starting +point by a specified distance along a geodesic curve which passes +through the starting point at a given position angle. It can only be used +with 2-dimensional Frames. + +\item The Frame class has a new method called +f+ +AST\_AXDISTANCE +f- +c+ +astAxDistance +c- +which returns the increment between two supplied axis values. For +axes belonging to SkyFrames, the returned value is normalized into +the range $\pm\pi$. + +\item The Frame class has a new method called +f+ +AST\_AXOFFSET +f- +c+ +astAxOffset +c- +which returns an axis value a given increment away from a specified axis +value. For axes belonging to SkyFrames, the returned value is normalized into +the range $\pm\pi$ (for latitude axes) or zero to $2\pi$ (for longitude +axes). + +\item The Plot class has a new method called +f+ +AST\_GENCURVE +f- +c+ +astGenCurve +c- +which allows generalised user-defined curves to be drawn. The curve is +defined by a user-supplied Mapping which maps distance along the curve +into the corresponding position in the current Frame of the Plot. The new +method then maps these current Frame position into graphics coordinates, +taking care of any non-linearities or discontinuities in the mapping. + +\item The Plot class has a new method called +f+ +AST\_GRFSET +f- +c+ +astGrfSet +c- +which allows the underlying primitive graphics functions to be selected +at run-time. Previously, the functions used by the Plot class to produce +graphics could only be selected at link-time, using the options of the +ast\_link command. The new Plot method allows an application to over-ride +the functions established at link-time, by specifying alternative +primitive graphics routines. In addition, the two new Plot methods +f+ +AST\_GRFPUSH and AST\_GRFPOP +f- +c+ +astGrfPush and astGrfPop +c- +allow the current graphics routines to be saved and restore on a +first-in-last-out stack, allowing temporary changes to be made to the set +of registered graphics routines. + +\item The DrawAxes attribute of the Plot class can now be specified +independantly for each axis, by appending the axis index to the +end of the attribute name. + +\item A bug has been fixed in the Plot class which could result in axis +labels being drawn on inappropriate edges of the plotting box when using +``interior'' labelling. + +\item A bug has been fixed in the IntraMap class which could cause IntraMaps +to be corrupted after transforming any points. + +\item Bugs have been fixed in the FitsChan class which could cause +inappropriate ordering of headers within a FitsChan when writing or +reading objects using NATIVE encodings. + +\item A bug has been fixed in the FitsChan class which could cause the +celestial longitude of a pixel to be estimated incorrectly by 180 degrees +if the reference point is at either the north or the south pole. + +\end{enumerate} + + +\subsection{Changes Introduced in V1.8-2} + +The following describes the most significant changes which have +occurred in the AST library between versions V1.7 and V1.8-2: + +\begin{enumerate} + +\item The SkyFrame class has a new attribute called NegLon which allows + longitude values to be displayed in the range $-\pi$ to $+\pi$, instead + of the usual range zero to $2.\pi$. + +\item Some new +c+ +functions (astAngle, astAxAngle, astResolve, astOffset2, astAxOffset, +astAxDistance) +c- +f+ +routines (AST\_ANGLE, AST\_AXANGLE, AST\_RESOLVE, AST\_OFFSET2, AST\_AXOFFSET, +AST\_AXDISTANCE) +f- +have been added to the Frame class to allow navigation of the coordinate space +to be performed without needing to know the underlying geometry +of the co-ordinate system (for instance, whether it is Cartesian or +spherical). + +Note, version 1.8-1 contained many of these facilities, but +some have been changed in version 1.8-2. Particularly, positions angles +are now referred to the second Frame axis for \emph{all} classes of Frames +(including SkyFrames), and the +c+ +astBear function has been replaced by astAxAngle. +c- +f+ +AST\_BEAR routine has been replaced by AST\_AXANGLE. +f- + +\end{enumerate} + +\subsection{Changes Introduced in V1.8-3} + +The following describes the most significant changes which +occurred in the AST library between versions V1.8-2 and V1.8-3: + +\begin{enumerate} + +\item A new method called astDecompose has been added to the Mapping class +which enables pointers to be obtained to the component parts of CmpMap and +CmpFrame objects. + +\item Functions within proj.c and wcstrig.c have been renamed to avoid name +clashes with functions in more recent versions of Mark Calabretta's wcslib +library. + +\end{enumerate} + +\subsection{Changes Introduced in V1.8-4} + +The following describes the most significant changes which +occurred in the AST library between versions V1.8-3 and V1.8-4: + +\begin{enumerate} + +\item The FitsChan class has a new attribute called DefB1950 which can be +used to select the default reference frame and equinox to be used if +a FitsChan with foreign encoding contains no indication of the +reference frame or equinox. + +\item A bug has been fixed in the FitsChan class which could prevent +astWrite from creating a set of FITS headers from an otherwise valid +FrameSet, when when using FITS-AIPS encoding. + +\item A bug has been fixed in the FitsChan class which could cause +astRead to mis-interpret the FITS CROTA keyword when using FITS-AIPS +encoding. + +\end{enumerate} + +\subsection{Changes Introduced in V1.8-5} + +The following describes the most significant changes which +occurred in the AST library between versions V1.8-4 and V1.8-5: + +\begin{enumerate} + +\item The Plot class defines new graphical elements Axis1, Axis2, +Grid1, Grid2, NumLabs1, NumLabs2, TextLab1, TextLab2, Ticks1 and Ticks2. +These allow graphical attributes (colour, width, etc) to be set for each +axis individually. Previously, graphical attributes could only be set for +both axes together, using graphical elements Axes, Grid, NumLabs, +TextLabs and Ticks. + +\end{enumerate} + + +\subsection{Changes Introduced in V1.8-7} + +The following describes the most significant changes which +occurred in the AST library between versions V1.8-5 and V1.8-7: + +\begin{enumerate} + +\item A new attribute called CarLin has been added to the FitsChan class +which controls the way CAR projections are handled when reading a +FrameSet from a non-native FITS header. Some FITS writers use a CAR +projection to represent a simple linear transformation between pixel +coordinates and celestial sky coordinates. This is not consistent with +the definition of the CAR projection in the draft FITS-WCS standard, which +requires the resultant Mapping to include a 3D rotation from native +spherical coordinates to celestial spherical coordinates, thus making the +Mapping non-linear. Setting CarLin to 1 forces +c+ +astRead +c- +f+ +AST\_READ +f- +to ignore the FITS-WCS standard and treat any CAR projections as simple +linear Mappings from pixel coordinates to celestial coordinates. + +\item A bug has been fixed which could result in axis Format attributes +set by the user being ignored under certain circumstances. + +\item A bug in the way tick marks positions are selected in the Plot class +has been fixed. This bug could result in extra ticks marks being displayed at +inappropriate positions. This bug manifested itself, for instance, if the +Mapping represented by the Plot was a simple Cartesian to Polar Mapping. +In this example, the bug caused tick marks to be drawn at negative radius +values. + +\item A bug has been fixed which could prevent attribute settings from +being read correctly by +c+ +astSet, +c- +f+ +AST\_SET, +f- +etc., on certain platforms (MacOS, for instance). + +\end{enumerate} + +\subsection{Changes Introduced in V1.8-8} + +The following describes the most significant changes which +occurred in the AST library between versions V1.8-7 and V1.8-8: + +\begin{enumerate} + +\item A bug has been fixed in the FitsChan class which could cause +problems when creating a FrameSet from a FITS header containing WCS +information stored in the form of Digitised Digitised Sky Survey (DSS) +keywords. These problems only occurred for DSS fields in the southern +hemisphere, and resulted in pixel positions being mapped to sky positions +close to the corresponding \emph{northern} hemispshere field. + +\item A new method called +c+ +astBoundingBox +c- +f+ +AST\_BOUNDINGBOX +f- +has been added to the Plot class. This method returns the bounding box of +the previous graphical output produced by a Plot method. + +\item A new attribute called Invisible has been added to the Plot class +which suppresses the graphical output normally produced by Plot methods. +All the calculations needed to produce the normal output are still +performed however, and so the bounding box returned by the new +c+ +astBoundingBox +c- +f+ +AST\_BOUNDINGBOX +f- +method is still usable. + +\item Bugs have been fixed related to the appearance of graphical output +produced by the Plot class. These bugs were to do with the way in which +graphical elements relating to a specific axis (e.g. \texttt{Colour(axis1)}, etc.) +interacted with the corresponding generic element (e.g. +\texttt{Colour(axes)}, etc.). + +\end{enumerate} + + +\subsection{Changes Introduced in V1.8-13} + +The following describes the most significant changes which occurred +in the AST library between versions V1.8-8 and V1.8-13: + +\begin{enumerate} + +\item The FitsChan class has been modified so that LONPOLE keywords +c+ +are only produced by astWrite when necessary. For zenithal projections such as +TAN, the LONPOLE keyword can always take its default value and so is +not included in the FITS header produced by astWrite. +c- +f+ +are only produced by AST\_WRITE when necessary. For zenithal projections such as +TAN, the LONPOLE keyword can always take its default value and so is +not included in the FITS header produced by AST\_WRITE +f- +Previously, the unnecessary production of a LONPOLE keyword could prevent +FrameSets being written out using encodings which do not support the +LONPOLE keyword (such as FITS-IRAF). + +\item The FitsChan class has been modified to retain leading and trailing +spaces within COMMENT cards. + +\item The FitsChan class has been modified to only use CTYPE comments as +axis labels if all non-celestial axes have unique non-blank comments +(otherwise the CTYPE keyword values are used as labels). + +\item The FitsChan class has been modified so that it does not append a +trailing ``Z'' character to the end of DATE-OBS keyword values. + +\item The FitsChan class has been modified to use latest list of FITS-WCS +projections, as described in the FITS-WCS paper II, ``Representations of +celestial coordinates in FITS'' (Calabretta \& Greisen, draft dated 23 +April 2002). Support has been retained for the polynomial correction +terms which previous drafts have allowed to be associated with TAN +projections. + +\item The WcsMap class has additional projection types of AST\_\_TPN +(which implements a distorted TAN projection) and AST\_\_SZP. The AST\_\_TAN +projection type now represents a simple TAN projection and has no +associated projection parameters. In addition, the usage of projection +parameters has been brought into line with the the FITS-WCS paper II. + +\item The WcsMap class has been modified so that a ``get'' operation on a +projection parameter attribute will return the default value defined in the +FITS-WCS paper II if no value has been set for the attribute. Previously, a +value of AST\_\_BAD was returned in such a situation. + +\item The Frame class has new attributes Top(axis) and Bottom(axis) which +allow a ``plottable range'' to be specified for each Frame axis. The grid +c+ +produced by the astGrid method will not extend beyond these limits. +c- +f+ +produced by the AST\_GRID routine will not extend beyond these limits. +f- + +\end{enumerate} + +\subsection{Changes Introduced in V2.0} + +Note, Frame descriptions created using AST V2.0 will not be readable by +applications linked with earlier versions of AST. This applies to Frame +descriptions created using: +\begin{itemize} +\item the Channel class +\item the FitsChan class if the NATIVE Encoding is used +c+ +\item the astShow function +c- +f+ +\item the AST\_SHOW routine. +f- +\end{itemize} + +Applications must be re-linked with AST V2.0 in order to be able to read +Frame descriptions created by AST v2.0. + +The following describes the most significant changes which have +occurred in the AST library between versions V1.8-13 and V2.0 (the +current version): + +\begin{enumerate} + +\item The default value for the Domain attribute provided by the CmpFrame +class has been changed from ``CMP'' to a string formed by concatenating +the Domain attributes of the two component Frames, separated by a minus +sign. If both component Domains are blank, then the old default of +``CMP'' is retained for the CmpFrame Domain. + +\item The implementation of the +c+ +astWrite function +c- +f+ +AST\_WRITE routine +f- +within the FitsChan class has been modified. It will now attempt to +produce a set of FITS header cards to describe a FrameSet even if the +number of axes in the Current Frames is greater than the number in the +Base Frame (that is, if there are more WCS axes than pixel axes). This +has always been possible with NATIVE encoding, but has not previously +been possible for foreign encodings. The WCSAXES keyword is used to store +the number of WCS axes in the FITS header. + +\item Another change to the +c+ +astWrite function +c- +f+ +AST\_WRITE routine +f- +within the FitsChan class is that the ordering of ``foreign'' axes +(\emph{i.e.} CTYPE keywords) is now chosen to make the CD (or PC) matrix +as diagonal as possible - any element of axis transposition is removed by +this re-ordering as recommended in FITS-WCS paper I. Previously the +ordering was determined by the order of the axes in the Current Frame of +the supplied FrameSet. This change does not affect NATIVE encoding. + +\item Support for spectral coordinate systems has been introduced +throught the addition of two new classes, SpecFrame and SpecMap. +The SpecFrame is a 1-dimensional Frame which can be used to describe +positions within an electromagnetic spectrum in various systems +(wavelength, frequency, various forms of velocity,~\emph{etc.}) and referred +to various standards of rest (topocentric, geocentric, heliocentric +LSRK,~\emph{etc.}). The SpecMap is a Mapping which can transform spectral +axis values between these various systems and standards of rest. Note, +FitsChans which have a foreign encoding (\emph{i.e.} any encoding other +than NATIVE) are not yet able to read or write these new classes. + +\item Facilities have been added to the Frame class which allow +differences in axis units to be taken into account when finding a Mapping +between two Frames. In previous versions of AST, the Unit attribute was a +purely descriptive item intended only for human readers - changing the +value of Unit made no difference to the behaviour of the Frame. As of +version 2.0, the Unit attribute can influence the nature of the Mappings +between Frames. For instance, if the +c+ +astFindrame or astConvert +c- +f+ +AST\_FINDRAME or AST\_CONVERT +f- +method is used to find the Mapping between an Axis with Unit set to ``m'' +and another Axis with Unit set to ``km'', then the method will return a +ZoomMap which introduces a scaling factor of 0.001 between the two axes. +These facilities assume that units are specified following the rules +included in FITS-WCS paper I (\emph{Representation of World +Coordinates in FITS}, Greisen \& Calabretta). + +In order to minimise the risk of breaking existing software, the default +behaviour for simple Frames is to ignore the Unit attribute (\emph{i.e.} +to retain the previous behaviour). However, the new Frame method +c+ +astSetActiveUnit +c- +f+ +AST\_SETACTIVEUNIT +f- +may be used to ``activate'' (or deactivate) the new facilities within a +specific Frame. Note, the new SpecFrame class is different to the simple +Frame class in that the new facilities for handling units are always active +within a SpecFrame. + +\item The System and Epoch attributes fo the SkyFrame class have been +moved to the parent Frame class. This enables all sub-classes of Frame +(such as the new SpecFrame class) to share these attributes, and to provide +suitable options for each class. + +\item The Frame class has a new attribute called AlignSystem, which allows +control over the alignment process performed by the methods +c+ +astFindFrame and astConvert. +c- +f+ +AST\_FINDFRAME and AST\_CONVERT. +f- + + +\item The CmpFrame class has been modified so that attributes of a +component Frame can be accessed without needing to extract the Frame first. +To do this, append an axis index to the end of the attribute name. For +instance, if a CmpFrame contains a SpecFrame and a SkyFrame (in that order), +then the StdOfRest attribute of the SpecFrame can be referred to as the +``StdOfRest(1)'' attribute of the CmpFrame. Likewise, the Equinox attribute +of the SkyFrame can be accessed as the ``Equinox(2)'' (or equivalently +``Equinox(3)'') attribute of the CmpFrame. The ``System(1)'' attribute of the +CmpFrame will refer to the System attribute of the SpecFrame, whereas the +``System(2)'' and ``System(3)'' attributes of the CmpFrame will refer to the +System attribute of the SkyFrame (the ``System'' attribute without an axis +specifier will refer to the System attribute of the CmpFrame as a whole, +since System is an attribute of all Frames, and a CmpFrame is a Frame and +so has its own System value which is independant of the System attributes +of its component Frames). + +\item The algorithms used by the Plot class for determining when to omit +overlapping axis labels, and the abbreviation of redundant leading fields +within sexagesimal axis labels, have been improved to avoid some anomolous +behaviour in previous versions. + +\item The curve drawing algorithm used by the Plot class has been +modified to reduce the chance of it ``missing'' small curve sections, +such as may be produced if a grid line cuts across the plot very close to +a corner. Previously, these missed sections could sometimes result in +axis labels being omitted. + +\item A new function +c+ +(astVersion) +c- +f+ +(AST\_VERSION) +f- +has been added to return the version of the AST library in use. + +\item Bugs have been fixed in the Plot class which caused serious problems +when plotting high precision data. These problems could range from the +omission of some tick marks to complete failure to produce a plot. + +\end{enumerate} + +Programs which are statically linked will need to be re-linked in +order to take advantage of these new facilities. + + +\subsection{Changes Introduced in V3.0} + +The following describes the most significant changes which +occurred in the AST library between versions V2.0 and V3.0: + +\begin{enumerate} + +\item Many changes have been made in the FitsChan class in order to bring +the FITS-WCS encoding into line with the current versions of the FITS-WCS +papers (see +\url{http://www.atnf.csiro.au/people/mcalabre/WCS/}): + +\begin{itemize} + +\item The rotation and scaling of the pixel axes may now be specified using +either CD\emph{i\_j} keywords, or PC\emph{i\_j} and CDELTj keywords. A new attribute +called CDMatrix has been added to the FitsChan class to indicate which +set of keywords should be used when writing a FrameSet to a FITS-WCS +header. + +\item The FITS-WCS encoding now supports most of the conventions +described in FITS-WCS paper III for the description of spectral +coordinates. The exceptions are that the SSYSOBS keyword is not +supported, and WCS stored in tabular form (as indicated by the ``-TAB'' +algorithm code) is not supported. + + +\item User-specified fiducial points for WCS projections are now +supported by FitsChans which use FITS-WCS encoding. This use keywords +PVi\_0, PVi\_1 and PVi\_2 for the longitude axis. + +\item When reading a FITS-WCS header, a FitsChan will now use keywords PVi\_3 +and PVi\_4 for the longitude axis (if present) in preference to any LONPOLE +and LATPOLE keywords which may be present. When writing a FITS-WCS header, +both forms are written out. + +\item The number of WCS axes is stored in the WCSAXES keyword if its value +would be different to that of the NAXIS keyword. + +\item Helio-ecliptic coordinates are now supported by FitsChans which use +FITS-WCS encoding. This uses CTYPE codes ``HLON'' and ``HLAT''. The +resulting SkyFrame will have a System value of ``HELIOECLIPTIC'', and all +the usual facilities, such as conversion to other celestial systems, are +available. + +\item The FITS-WCS encoding now supports most of the conventions +described in FITS-WCS paper III for the description of spectral +coordinates. The exceptions are that the SSYSOBS keyword is not +supported, and WCS stored in tabular form (as indicated by the ``-TAB'' +algorithm code) is not supported. + +\item When reading a FITS-WCS header, a FitsChan will now ignore any +distortion codes which are present in CTYPE keywords. Here, a ``distortion +code'' is the final group of four characters in a CTYPE value of the +form ``xxxx-yyy-zzz'', as described in FITS-WCS paper IV. The exception +to this is that the ``-SIP'' distortion code (as used by the Spitzer +Space Telescope project - see +\url{http://ssc.spitzer.caltech.edu/postbcd/doc/shupeADASS.pdf}) is +interpreted correctly and results in a PolyMap being used to represent +the distortion in the resulting FrameSet. Note, ``-SIP'' distortion codes +can only be read, not written. A FrameSet which uses a PolyMap will not +in general be able to be written out to a FitsChan using any foreign +encoding (although NATIVE encoding can of course be used). + +\item The Warnings attribute of the FitsChan class now accepts values +``BadVal'' (which gives warnings about conversion errors when reading +FITS keyword values), ``Distortion'' (which gives warnings about +unsupported distortion codes within CTYPE values), and ``BadMat'' (which +gives a warning if the rotation/scaling matrix cannot be inverted). + +\item When writing a FrameSet to a FitsChan which uses a non-Native +encoding, the comment associated with any card already in the FitsChan +will be retained if the keyword value being written is the same as the +keyword value already in the FitsChan. + +\item A FrameSet which uses the non-FITS projection type AST\_\_TPN (a TAN +projection with polynomial distortion terms) can now be written to a +FitsChan if the Encoding attribute is set to FITS-WCS. The standard +``-TAN'' code is used within the CTYPE values, and the distortion +coefficients are encoded in keywords of the form `` QVi\_ma'', which are +directly analogous to the standard ``PVi\_ma'' projection parameter keywords. +Thus a FITS reader which does not recognise the QV keywords will still +be able to read the header, but the distortion will be ignored. + +\item The default value for DefB1950 attribute now depends on the value +of the Encoding attribute. + +\item A new appendix has been added to SUN/210 and SUN/211 giving details +of the implementation provided by the FitsChan class of the +conventions contained in the first four FITS-WCS papers. +\end{itemize} + +\item The SkyFrame class now supports two new coordinate systems ``ICRS'' +and ``HELIOECLIPTIC''. The default for the System attribute for SkyFrames +has been changed from ``FK5'' to ``ICRS''. + +\item The +c+ +astRate +c- +f+ +AST\_RATE +f- +function has been added which allows an estimate to be made of the rate of +change of a Mapping output with respect to one of the Mapping inputs. + +\item All attribute names for Frames of any class may now include an optional +axis specifier. This includes those attributes which describe a property +of the whole Frame. For instance, the Domain attribute may now be +specified as ``Domain(1)'' in addition to the simpler ``Domain''. In cases +such as this, where the attribute describes a property of the whole +Frame, axis specifiers will usually be ignored. The exception is that a +CmpFrame will use the presence of an axis specifier to indicate that the +attribute name relates to the primary Frame containing the specified +axis, rather than to the CmpFrame as a whole. + +\item A new subclass of Mapping, the PolyMap, has been added which +performs a general N-dimensional polynomial mapping. + +\item A new subclass of Mapping, the GrismMap, has been added which +models the spectral dispersion produced by a grating, prism or grism. + +\item A new subclass of Mapping, the ShiftMap, has been added which adds +constant values onto all coordinates (this is equivalent to a WinMap +with unit scaling on all axes). + +\item Minor bugs have been fixed within the Plot class to do with the choice +and placement of numerical axis labels. + +\item The SphMap class has a new attribute called PolarLong which gives the +longitude value to be returned when a Cartesian position corresponding to +either the north or south pole is transformed into spherical coordinates. + +\item The WcsMap class now assigns a longitude of zero to output +celestial coordinates which have a latitude of plus or minus 90 degrees. + +\item The NatLat and NatLon attributes of the WcsMap class have been +changed so that they now return the fixed native coordinates of the +projection reference point, rather than the native coordinates of the +user-defined fiducial point. + +\item Notation has been changed in both the WcsMap and FitsChan classes to +reflect the convention used in the FITS-WCS papers that index ``i'' refers +to a world coordinate axis, and index ``j'' refers to a pixel axis. + +\item Changes have been made to several Mapping classes in order to allow +the +c+ +astSimplify +c- +f+ +AST\_SIMPLIFY +f- +function to make simplifications in a CmpMap which previously were not +possible. + +\item The SlaMap class has been extended by the addition of conversions +between FK5 and ICRS coordinates, and between FK5 and helio-ecliptic coordinates. + +\item The SpecMap class has been changed to use the equation for the +refractive index of air as given in the current version of FITS-WCS paper +III. Also, the forward and inverse transformations between frequency and +air-wavelength have been made more compatible by using an iterative +procedure to calculate the inverse. + +\end{enumerate} + +\subsection{Changes Introduced in V3.1} + +The following describes the most significant changes which have +occurred in the AST library between versions V3.0 and V3.1 (the +current version): + +\begin{enumerate} +\item Addition of a new class called XmlChan - a Channel which +reads and writes AST objects in the form of XML. +\item A bug has been fixed in the Plot class which could cause incorrect +graphical attributes to be used for various parts of the plot if either +axis has no tick marks (i.e. if both major and minor tick marks have zero +length). +\end{enumerate} + +Programs which are statically linked will need to be re-linked in +order to take advantage of these new facilities. + + +\subsection{Changes Introduced in V3.2} + +The following describes the most significant changes which have +occurred in the AST library between versions V3.1 and V3.2: + +\begin{enumerate} + +\item A new +c+ +function astPutCards +c- +f+ +routine AST\_PUTCARDS +f- +has been added to the FitsChan class. This allows multiple concatenated header +cards to be stored in a FitsChan in a single call, providing an alternative to +the existing +c+ +astPutCards function. +c- +f+ +AST\_PUTCARDS routine. +f- + +\item Some signficant changes have been made to the simplification of Mappings + which should resultin a greater degree of simplication taking place.Some + bugs have also been fixed which could result in an infinite loop being + entered when attempting to simplify certain Mappings. + +\item The FitsChan class now translates the spectral algorithm codes +``-WAV'', ``-FRQ'' and ``-VEL'' (specified in early drafts of paper III) to +the corresponding ``-X2P'' form when reading a spectral axis description +from a set of FITS header cards. + +\item A bug has been fixed in the FitsChan class which could cause +keywords associated with alternate axis descriptions to be mis-interpreted. + +\item The Plot class now provides facilities for modifying the appearance +of sub-strings within text strings such as axis labels, titles, \emph{etc}, +by producing super-scripts, sub-scripts, changing the font colour, size, +\emph{etc}. See attribute Escape. + +\item The default value of the Tol attribute of the Plot class has been +changed from 0.001 to 0.01. This should not usually cause any significant +visible change to the plot, but should make the plotting faster. You may +need to set a lower value for Tol if you are producing a particularly +large plot. + +\item The algorithm for finding the default value for the Gap attribute +has been changed. This attribute specifies the gap between major axis +values in an annotated grid drawn by the Plot class. The change in +algorithm may cause the default value to be different to previous versions +in cirtain circumstances. + +\item Some bugs have been fixed in the Plot class which could cause the +system to hang for a long time while drawing certain all-sky grids +(notable some of the FITS Quad-cube projections). + +\item The SkyAxis class has extended the Format attribute by the addition +of the ``g'' option. this option is similar to the older ``l'' option in that +it results in characters (``h'', ``m'', ``s'', \emph{etc}) being used as +delimiters between the sexagesimal fields of the celestial position. The +difference is that the ``g'' option includes graphics escape sequences +in the returned formatted string which result in the field delimiter +characters being drawn as super-scripts when plotted as numerical axis values +by a Plot. + +\item The Plot class has been extended to include facilities for producing +logarithmic axes. See attributes LogPlot, LogTicks, LogGap and LogLabel. + +\item New functions astGCap and astGScales have been added to the interface +defined by file \verb+grf.h+. The ast\_link command has been modified so +that the \verb+-mygrf+ switch loads dummy versions of the new grf +functions. This means that applications should continue to build without +any change. However, the facilities for interpreting escape sequences +within strings drawn by the Plot class will not be available unless the +new grf functions are implemented. If you choose to implement them, you +should modify your linking procedure to use the \verb+-grf+ switch in +place of the older \verb+-mygrf+ switch. See the description of the ast\_link +command for details of the new switches. Also note that the astGQch +function, whilst included in verb+grf.h+ in pervious versions of AST, was +not actually called. As of this version of AST, calls are made to the +astGQch function, and so any bugs in the implementation of astGQch may +cause spurious behaviour when plotting text strings. + +\item A new 'static' method called astEscapes has been added which is used +to control and enquire whether astGetC and astFormat will strip any graphical +escape sequences which may be present out of the returned value. + +\item New attribute XmlPrefix has been added to the XmlChan class. It +allows XML written by the XmlChan class to include an explicit namespace +prefix on each element. + +\item New attribute XmlFormat has been added to the XmlChan class. It +specifies the format in which AST objects should be written. + +\item A new class of Mapping, the TranMap, has been introduced. A TranMap +takes its forward transformation from an existing Mapping, and its inverse +transformation from another existing Mapping. + +\item A bug has been fixed in WcsMap which caused error reports to +include erroneous axis numbers when referring to missing parameter values. + +\end{enumerate} + +\subsection{Changes Introduced in V3.3} + +The following describes the most significant changes which have +occurred in the AST library between versions V3.2 and V3.3: + +\begin{enumerate} + +\item Options have been added to the SkyFrame class which allows the +origin +of celestial coordinates to be moved to any specified point. See the new +attributes SkyRef, SkyRefIs, SkyRefP and AlignOffset. + +\item An option has been added to the FitsChan class which allows extra +Frames representing cartesian projection plane coordinates (``intermediate +world coordinates'' in the parlance of FITS-WCS) to be created when +reading +WCS information from a foreign FITS header. This option is controlled by +a new attribute called Iwc. + +\item The FitsChan class which been modified to interpret FITS-WCS CAR +projection headers correctly if the longitude reference pixel (CRPIX) is +very large. + +\item The FITS-AIPS++ encoding in the FitsChan class now recognised +spectral axes if they conform to the AIPS convention in which the +spectral axis is descirbed by a CTYPE keyword od the form "AAAA-BBB" +where ``AAAA'' is one of FREQ, VELO or FELO, and ``BBB'' is one of LSR, LSD, +HEL or OBS. Such spectral axes can be both read and written. + +\item The FitsChan class now has a FITS-AIPS++ encoding which represents +WCS information using FITS header cards recognised by the AIPS++ project. +Support for spectral axes is identical to the FITS-AIPS encoding. + +\item The organisation of the AST distribution and the commands for +building it have been changed. Whereas AST used to be built and installed +with \verb+./mk build; ./mk install+, it now builds using the more standard +idiom \verb+./configure; make; make install+. The installation location is +controlled by the \verb+--prefix+ argument to ./configure (as is usual +for other packages which use this scheme). Note that the INSTALL environment +variable now has a \emph{different} meaning to that which it had +before, and it should generally be \emph{unset}. Also, there is no need to +set the SYSTEM variable. + +\item Shared libraries are now installed in the same directory as the +static libraries. In addition, links to sharable libraries are installed +with names which include version information, and ``libtool libraries'' +are also installed (see +\url{http://www.gnu.org/software/libtool/manual.html}). + +\item The \verb+ast_dev+ script has been removed. Instead, the location of +the AST include files should be specified using the -I option when +compiling. + +f+ +\item The names of the installed AST include files have been changed to +upper case. +f- + +\end{enumerate} + + +\subsection{Changes Introduced in V3.4} + +The following describes the most significant changes which have +occurred in the AST library between versions V3.3 and V3.4: + +\begin{enumerate} + +\item The Mapping class has a new method +c+ +(astLinearApprox) +c- +f+ +(AST\_LINEARAPPROX) +f- +which calculates the co-efficients of a linear approximation to a Mapping. + +\item The Format attribute for simple Frames and SkyFrames has been extended. +It has always been possible, in both classes, to specify a precision by +including a dot in the Format value followed by an integer (\emph{e.g.} +``\verb+dms.1+'' for a SkyFrame, or ``\verb+%.10g+'' for a simple Frame). +The precision can now also be specified using an asterisk in place of the +integer (\emph{e.g.} ``\verb+dms.*+'' or ``\verb+%.*g+''). This causes the +precision to be derived on the basis of the Digits attribute value. + +\item The Plot class has been changed so that the default value used for the +Digits attribute is chosen to be the smallest value which results in no +pair of adjacent labels being identical. For instance, if an annotated +grid is being drawn describing a SkyFrame, and the Format(1) value is set +to ``\verb+hms.*g+'' (the ``g'' causes field delimiters to be drawn as +superscripts), and the Digits(1) value is unset, then the seconds field +will have a number of decimal places which results in no pair of labels +being identical. + +\item Addition of a new class classed DSBSpecFrame. This is a +sub-class of SpecFrame which can be used to describe spectral axes +associated with dual sideband spectral data. + +\item The FitsChan class will now read headers which use the old ``-GLS'' +projection code, converting them to the corresponding modern ``-SFL'' code, +provided that the celestial axes are not rotated. + +\item The FitsChan class has a new Encoding, ``FITS-CLASS'', which allows +the reading and writing of FITS headers using the conventions of the CLASS +package - see +\url{http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/class.html}). + +\end{enumerate} + + +\subsection{Changes Introduced in V3.5} + +The following describes the most significant changes which have +occurred in the AST library between versions V3.4 and V3.5: + +\begin{enumerate} + +\item AST now provides facilities for representing regions of various +shapes within a coordinate system. The Region class provides general +facilities which are independent of the specific shape of region being +used. Various sub-classes of Region are also now available which provide +means of creating Regions of specific shape. Facilities provided by the +Region class include testing points to see if they are inside the +Region, testing two Regions for overlap, transforming Regions from one +coordinate system to another \emph{etc}. + +\item A new class of 1-dimensional Frame called FluxFrame has been added which +can be used to describe various systems for describing ovserved value at a +single fixed spectral position. + +\item A new class of 2-dimensional Frame called SpecFluxFrame has been added which +can be used to describe a 2-d frame spanned by a spectral position axis +and and an observed value axis. + +\item A new class of Mapping called RateMap has been added. A RateMap encapsulates +a previously created Mapping. The inputs of the RateMap correspond to the +inputs of the encapsulated Mapping. All RateMaps have just a single +output which correspond to the rate of change of a specified output of +the encapsulated Mapping with respect to a specified input. + +\item The SkyFrame class now supports a value of ``J2000'' for System. +This system is an equatorial system based on the mean dynamical equator and +equinox at J2000, and differs slightly from an FK5(J2000) system. + +\item A new class called KeyMap has been added. A KeyMap can be used to +store a collection of vector or scalar values or Objects, indexed by a +character string rather than an integer. + +\item The parameter list for the +c+ +astRate +c- +f+ +AST\_RATE +f- +method of the Mapping class has been modified. It no longer returns a second +derivative estimate. Existing code which uses this method will need to be +changed. + +\item Methods +c+ +(astSetFits) +c- +f+ +(AST\_SETFITS) +f- +have been added to the FitsChan class to allow values for named +keywords to be changed or added. + +\end{enumerate} + + +\subsection{Changes Introduced in V3.6} + +The following describes the most significant changes which +occurred in the AST library between versions V3.5 and V3.6: + +\begin{enumerate} + +\item If the Format attribute associated with an axis of a SkyFrame +starts with a percent character (``\verb+%+''), then axis values are +now formatted and unformatted as a decimal radians value, using the +Format syntax of a simple Frame. + +\item The Plot class has a new attribute called Clip which controls the +clipping performed by AST at the plot boundary. + +\item The keys used to label components of the PolyMap structure when a +PolyMap is written out through a Channel have been changed. The new keys +are shorter than the old keys and so can written succesfully to a FitsChan. +The new PolyMap class always writes new styles keys but can read either +old or new style keys. Consequently, PolyMap dumps written by this +version of AST cannot be read by older versions of AST. + +\item A mimimal cut down subset of the C version of SLALIB is now +included with the AST distribution and built as part of building AST. +This means that it is no longer necessary to have SLALIB installed +separately at your site. The SLALIB code included with AST is distrubuted +under the GPL. The default behaviour of the ast\_link script is now to +link with this internal slalib subset. However, the ``-csla'' option can +still be used to force linking with an external full C SLALIB library. +A new option ``-fsla'' has been introduced which forces linking with the +external full Fortran SLALIB library. + +\end{enumerate} + +\subsection{Changes Introduced in V3.7} + +The following describes the most significant changes which +occurred in the AST library between versions V3.6 and V3.7: + +\begin{enumerate} + +\item Support for time coordinate systems has been introduced +throught the addition of two new classes, TimeFrame and TimeMap. +The TimeFrame is a 1-dimensional Frame which can be used to describe +moments in time (either absolute or relative) in various systems (MJD, +Julian Epoch, \emph{etc.}) and referred to various time scales (TAI, UTC, +UT1, GMST, \emph{etc}). The TimeMap is a Mapping which can transform time +values between these various systems and time scales. Note, +FitsChans which have a foreign encoding (\emph{i.e.} any encoding other +than NATIVE) are not able to read or write these new classes. + +\end{enumerate} + + +\subsection{Changes Introduced in V4.0} + +The following describes the most significant changes which +occurred in the AST library between versions V3.7 and V4.0: + +\begin{enumerate} + +\item Experimental support for reading IVOA Space-Time-Coordinates (STC-X) +descriptions using the XmlChan class has been added. Support is included +for a subset of V1.20 of the draft STC specification. + +\item A new set of methods (AST\_REBIN/astRebin) has been added to +the Mapping class. These are flux-conserving alternatives to the existing +AST\_RESAMPLE/astResample methods. + +\end{enumerate} + + +\subsection{Changes Introduced in V4.1} + +The following describes the most significant changes which +occurred in the AST library between versions V4.0 and V4.1: + +\begin{enumerate} + +\item A new control flag has been added to the AST\_RESAMPLE/astResample +functions which produces approximate flux conservation. + +\item New constants AST\_\_SOMB and AST\_\_SOMBCOS have been added to +c+ +ast.h. These specify kernels for astResample and astRebin +c- +f+ +AST\_PAR. These specify kernels for AST\_RESAMPLE and AST\_REBIN +f- +based on the ``Sombrero'' function ( $2*J1(x)/x$ where $J1(x)$ is the +first order Bessel function of the first kind). + +\item The SkyFrame class now supports a System value of AZEL corresponding +to horizon (azimuth/elevation) coordinates. + +\item The FitsChan class allows the non-standard strings ``AZ--'' and +``EL--'' to be used as axis types in FITS-WCS CTYPE keyword values. + +\item The Frame class now has attributes ObsLon and ObsLat to specify +the geodetic longitude and latitude of the observer. + +\item The ClockLon and ClockLat attributes have been removed from the +TimeFrame class. Likewise, the GeoLon and GeoLat attributes have been +removed from the SpecFrame class. Both classes now use the ObsLon and +ObsLat attributes of the parent Frame class instead. However, the old +attribute names can be used as synonyms for ObsLat and ObsLon. Also, +dumps created using the old scheme can be read succesfully by AST V4.1 +and converted to the new form. + +\item A new +c+ +function astMapSplit +c- +f+ +routine AST\_MAPSPLIT +f- +has been added to the Mapping class. This splits a Mapping into two component +Mappings which, when combined in parallel, are equivalent to the original +Mapping. + +\item The default value for the SkyRefIs attribute has been changed from +``Origin'' to ``Ignored''. This means that if you want to use a SkyFrame +to represent offsets from some origin position, you must now set the +SkyRefIs attribute explicitly to either ``Pole'' or ``Origin'', in +addition to assigning the required origin position to the SkyRef attribute. + +\end{enumerate} + +\subsection{Changes Introduced in V4.2} + +The following describes the most significant changes which +occurred in the AST library between versions V4.1 and V4.2: + +\begin{enumerate} + +\item The SideBand attribute of the DSBSpecFrame class can now take the +option ``LO'' in addition to ``USB'' and ``LSB''. The new option causes the +DSBSpecFrame to represent the offset from the local oscillator frequency, +rather than either of the two sidebands. + +\item The FitsChan class has been changed so that it writes out a VELOSYS +keyword when creating a FITS-WCS encoding (VELOSYS indicates the topocentric +apparent velocity of the standard of rest). FitsChan also strips out VELOSYS +keywords when reading a FrameSet from a FITS-WCS encoding. + +\item The FitsChan class has a new method called +c+ +astRetainFits +c- +f+ +AST\_RETAINFITS +f- +that indicates that the current card in the FitsChan should not be +stripped out of the FitsChan when an AST Object is read from the FitsChan. +Unless this method is used, all cards that were involved in the creation +of the AST Object will be stripped from the FitsChan afte a read operation. + +\item A problem with unaligned memory access that could cause bus errors on +Solaris has been fixed. + +\item A new read-only attribute called ObjSize has been added to the base +Object Class. This gives the number of bytes of memory occupied by the +Object. Note, this is the size of the internal in-memory representation of +the Object, not the size of the textual representation produced by +writing the Object out through a Channel. + +\item A new function +c+ +astTune +c- +f+ +AST\_TUNE +f- +has been added which can be used to get and set global AST tuning +parameters. At the moment there are only two such parameter, both of +which are concerned with memory management within AST. + +\item A new method called +c+ +astTranGrid +c- +f+ +AST\_TRANGRID +f- +has been added to the Mapping class. This method creates a regular +grid of points covering a rectangular region within the input space of a +Mapping, and then transforms this set of points into the output space of the +Mapping, using a piecewise-continuous linear approximation to the Mapping +if appropriate in order to achive higher speed. + +\item A new subclass of Mapping has been added called SwitchMap. A +SwitchMap represents several alternate Mappings, each of which is used to +transforms input positions within a different region of the input +coordinate space. + +\item A new subclass of Mapping has been added called SelectorMap. A +SelectorMap tests each input position to see if it falls within one of +several Regions. If it does, the index of the Region containing the +input position is returned as the Mapping output. + +\item The behaviour of the +c+ +astConvert +c- +f+ +AST\_CONVERT +f- +method when trying to align a CmpFrame with another Frame has been +modified. If no conversion between positions in the Frame and CmpFrame +can be found, an attempt is now made to find a conversion between the +Frame and one of two component Frames contained within the CmpFrame. Thus +is should now be possible to align a SkyFrame with a CmpFrame containing a +SkyFrame and a SpecFrame (for instance). The returned Mapping produces bad +values for the extra axes (i.e. for the SpecFrame axis in the above example). + +\item The ``ast\_link\_adam'' and ``ast\_link'' scripts now ignore the +\verb+-fsla+ and \verb+-csla+ options, and always link against the +minimal cut-down version of SLALIB distributed as part of AST. + +\end{enumerate} + +\subsection{Changes Introduced in V4.3} + +The following describes the most significant changes which occurred in the +AST library between versions V4.2 and V4.3: + +\begin{enumerate} + +\item The +c+ +astGetFitsS +c- +f+ +AST\_GETFITSS +f- +function now strips trailing white space from the returned string, if the +original string contains 8 or fewer characters + +\item The SpecFrame class has a new attribute called SourceSys that specified +whether the SourceVel attribute (which specifies the rest frame of the +source) should be accessed as an apparent radial velocity or a redshift. +Note, any existing software that assumes that SourceVel always represents +a velocity in km/s should be changed to allow for the possibility of +SourceVel representing a redshift value. + +\end{enumerate} + + +\subsection{Changes Introduced in V4.4} + +The following describes the most significant changes which occurred in +the AST library between versions V4.3 and V4.4: + +\begin{enumerate} + +\item The +c+ +astFindFrame +c- +f+ +AST\_FINDFRAME +f- +function can now be used to search a CmpFrame for an instance of a more +specialised class of Frame (SkyFrame, TimeFrame, SpecFrame, DSBSpecFrame +or FluxFrame). That is, if an instance of one of these classes is used as +the ``template'' when calling +c+ +astFindFrame, +c- +f+ +AST\_FINDFRAME, +f- +and the ``target'' being searched is a CmpFrame (or a FrameSet in which the +current Frame is a CmpFrame), then the component Frames within the CmpFrame +will be searched for an instance of the supplied template Frame, and, if +found, a suitable Mapping (which will include a PermMap to select the +required axes from the CmpFrame) will be returned by +c+ +astFindFrame. +c- +f+ +AST\_FINDFRAME. +f- +Note, for this to work, the MaxAxes and MinAxes attributes of the template +Frame must be set so that they cover a range that includes the number of axes +in the target CmpFrame. + +\item The SkyFrame, SpecFrame, DSBSpecFrame, TimeFrame and FluxFrame classes +now allow the MaxAxes and MinAxes attributes to be set freely to any value. +In previous versions of AST, any attempt to change the value of MinAxes +or MaxAxes was ignored, resulting in them always taking the default values. + +\item The DSBSpecFrame class has a new attribute called AlignSB that +specifies whether or not to take account of the SideBand attributes when +aligning two DSBSpecFrames using +c+ +astConvert. +c- +f+ +AST\_CONVERT. +f- + +\item The Frame class has a new attribute called Dut1 that can be used to +store a value for the difference between the UT1 and UTC timescales at +the epoch referred to by the Frame. + +\item The number of digits used to format the Frame attributes ObsLat and +ObsLon has been increased. + +\item The use of the SkyFrame attribute AlignOffset has been changed. This +attribute is used to control how two SkyFrames are aligned by +c+ +astConvert. +c- +f+ +AST\_CONVERT. +f- +If the template and target SkyFrames both have a non-zero value for +AlignOffset, then alignment occurs between the offset coordinate systems +(that is, a UnitMap will always be used to align the two SkyFrames). + +\item The Plot class has a new attribute called ForceExterior that can be +used to force exterior (rather than interior) tick marks to be produced. +By default, exterior ticks are only produced if this would result in +more than 3 tick marks being drawn. + +\item The TimeFrame class now supports conversion between angle based +timescales such as UT1 and atomic based timescales such as UTC. + +\end{enumerate} + +\subsection{Changes Introduced in V4.5} + +The following describes the most significant changes that +occurred in the AST library between versions V4.4 and V4.5: + +\begin{enumerate} + + + +\item All FITS-CLASS headers are now created with a frequency axis. If the +FrameSet supplied to +c+ +astWrite +c- +f+ +AST\_WRITE +f- +contains a velocity axis (or any other form +of spectral axis) it will be converted to an equivalent frequency axis +before being used to create the FITS-CLASS header. + +\item The value stored in the FITS-CLASS keyword ``VELO-LSR'' has been changed +from the velocity of the source to the velocity of the reference channel. + +\item Addition of a new method call +c+ +astPurgeWCS +c- +f+ +AST\_PURGEWCS +f- +to the FitsChan +class. This method removes all WCS-related header cards from a FitsChan. + +\item The Plot class has a new attribute called GrfContext that can be used +to comminicate context information between an application and any +graphics functions registered with the Plot class via the +c+ +astGrfSet function. +c- +f+ +AST\_GRFSET routine. +f- +\item Functions registered with the Plot class using +c+ +astGrfSet +c- +f+ +AST\_GRFSET +f- +now take a new additional integer parameter, ``grfcon''. The Plot class +sets this parameter to the value of the Plot's GrfContext attribute before +calling the graphics function. NOTE, THIS CHANGE WILL REQUIRE EXISTING +CODE THAT USES +c+ +astGrfSet +c- +f+ +AST\_GRFSET +f- +TO BE MODIFIED TO INCLUDE THE NEW PARAMETER. +\item The +c+ +astRebinSeq functions +c- +f+ +AST\_REBINSEQ routines +f- +now have an extra parameter that is used to record the total number of input +data values added into the output array. This is necessary to correct a +flaw in the calculation of output variances based on the spread of input +values. NOTE, THIS CHANGE WILL REQUIRE EXISTING CODE TO BE MODIFIED TO +INCLUDE THE NEW PARAMETER (CALLED "NUSED"). +\item Support has been added for the FITS-WCS ``HPX'' (HEALPix) projection. +\item A new flag ``AST\_\_VARWGT'' can be supplied to +c+ +astRebinSeq. +c- +f+ +AST\_REBINSEQ. +f- +This causes the input data values to be weighted using the reciprocals of +the input variances (if supplied). + +\item The Frame class has a new read-only attribute called NormUnit that +returns the normalised value of the Unit attribute for an axis. Here, +``normalisation'' means cancelling redundant units, etc. So for instance, a +Unit value of ``s*(m/s)'' would result in a NormUnit value of ``m''. + +\item A new +c+ +function astShowMesh +c- +f+ +routine AST\_SHOWMESH +f- +has been added to the Region class. It displays a mesh of points covering +the surface of a Region by writing out a table of axis values to standard +output. + +\item The Plot class now honours the value of the LabelUp attribute even if +numerical labels are placed around the edge of the Plot. Previously +LabelUp was only used if the labels were drawn within the interior of +the plot. The LabelUp attribute controls whether numerical labels are +drawn horizontally or parallel to the axis they describe. + +\item A bug has been fixed that could segmentation violations when setting +attribute values. + +\end{enumerate} + +\subsection{Changes Introduced in V4.6} + +The following describes the most significant changes which have +occurred in the AST library between versions V4.5 and V4.6: + +\begin{enumerate} + +\item The TimeFrame class now support Local Time as a time scale. The offset +from UTC to Local Time is specified by a new TimeFrame attribute called +LTOffset. + +\item A new class called Plot3D has been added. The Plot3D class allows +the creation of 3-dimensional annotated coordinate grids. + +\item A correction for diurnal aberration is now included when +converting between AZEL and other celestial coordinate systems. The +correction is based on the value of the ObsLat Frame attribute (the +geodetic latitude of the observer). + +\item A bug has been fixed which caused the DUT1 attribute to be ignored +by the SkyFrame class when finding conversions between AZEL and other +celestial coordinate systems. + +\end{enumerate} + +\subsection{Changes Introduced in V5.0} + +The following describes the most significant changes which +occurred in the AST library between versions V4.6 and V5.0: + +\begin{enumerate} + + +\item The AST library is now thread-safe (assuming that the POSIX pthreads +library is available when AST is built). Many of the macros defined in +the ast.h header file have changed. It is therefore necessary to +re-compile all source code that includes ast.h. + +\item New methods astLock and astUnlock allow an AST Object to be locked +for exclusive use by a thread. + +\item The TimeFrame class now support Local Time as a time scale. The offset +from UTC to Local Time is specified by a new TimeFrame attribute called +LTOffset. + +\item The Channel class has a new attribute called Strict which controls +whether or not to report an error if unexpected data items are found +within an AST Object description read from an external data source. Note, +the default behaviour is now not to report such errors. This differs from +previous versions of AST which always reported an error is unexpected +input items were encountered. + +\end{enumerate} + +\subsection{Changes Introduced in V5.1} + +The following describes the most significant changes which occurred in the +AST library between versions V5.0 and V5.1: + +\begin{enumerate} + +c+ +\item The astUnlock function now has an extra parameter that controls whether +or not an error is reported if the Object is currently locked by another +thread. +c- + +\item The Prism class has been modified so that any class of Region can +be used to define the extrusion axes. Previously, only a Box or Interval +could be used for this purpose. + +c+ +\item The values of the AST\_\_THREADSAFE macro (defined in ast.h) have +been changed from ``yes'' and ``no'' to ``1'' and ``0''. +c- + +\item Improvements have been made to the way that Prisms are simplified +when +c+ +astSimplify +c- +f+ +AST\_SIMPLIFY +f- +is called. The changes mean that more types of Prism will now simplify +into a simpler class of Region. + +\item The PointList class has a new method, +c+ +astPoints, +c- +f+ +AST\_POINTS, +f- +that copies the axis values from the PointList into a supplied array. + +\item The PointList class has a new (read-only) attribute, ListSize, that +gives the number of points stored in the PointList. + +\item The handling of warnings within different classes of Channel has +been rationalised. The XmlStrict attribute and +c+ +astXmlWarnings +c- +f+ +AST\_XMLWARNINGS +f- +function have been removed. The same functionality is now available via +the existing Strict attribute (which has had its remit widened), a new +attribute called ReportLevel, and the new +c+ +astWarnings +c- +f+ +AST\_WARNINGS +f- +function. This new function can be used on any class of Channel. Teh +FitsChan class retains its long standing ability to store warnings as +header cards within the FitsChan, but it also now stores warnings in the +parent Channel structure, from where they can be retrieved using the +c+ +astWarnings +c- +f+ +AST\_WARNINGS +f- +function. + +\item A new function called +c+ +astIntercept +c- +f+ +AST\_INTERCEPT +f- +has been added to the Frame class. This function finds the point of +intersection beteeen two geodesic curves. + +\item A bug in the type-checking of Objects passed as arguments to constructor +functions has been fixed. This bug could lead to applications crashing or +showing strange behaviour if an inappropriate class of Object was +supplied as an argument to a constructor. + +\item The +c+ +astPickAxes +c- +f+ +AST\_PICKAXES +f- +function will now return a Region, if possible, when applied to a Region. If +this is not possible, a Frame will be returned as before. + +\item The choice of default tick-mark for time axes has been improved, to avoid +previous issues which could result in no suitable gap being found, or +inappropriate tick marks when using formatted dates. + +\item A new function called +c+ +astTestFits +c- +f+ +AST\_TESTFITS +f- +has been added to the FitsChan class. This function tests a FitsChan to +see if it contains a defined value for specified FITS keyword. + +\item The AST\_\_UNDEF parameters used to flag undefined FITS keyword values +have been removed. Use the new +c+ +astTestFits +c- +f+ +AST\_TESTFITS +f- +function instead. + +c+ +\item The astIsUndef functions used to test FITS keyword values +have been removed. Use the new astTestFits function instead. +c- + +\end{enumerate} + +\subsection{Changes Introduced in V5.2} + +The following describes the most significant changes which +occurred in the AST library between versions V5.1 and V5.2: + +\begin{enumerate} + +\item A new method called +c+ +astSetFitsCM +c- +f+ +AST\_SETFITSCM +f- +has been added to the FitsChan class. It stores a pure comment card in a +FitsChan (that is, a card with no keyword name or equals sign). + +\item A new attribute called ObsAlt has been added to the Frame class. It +records the geodetic altitude of the observer, in metres. It defaults to +zero. It is used when converting times to or from the TDB timescale, or +converting spectral positions to or from the topocentric rest frame, or +converting sky positions to or from horizon coordinates. The FitsChan +class will include its effect when creating a set of values for the +OBSGEO-X/Y/Z keywords, and will also assign a value to it when reading a +set of OBSGEO-X/Y/Z keyword values from a FITS header. + +\item The TimeMap conversions ``TTTOTDB'' and ``TDBTOTT'', and the SpecMap +conversions ``TPF2HL'' and ``HLF2TP'', now have an additional argument - +the observer's geodetic altitude. + +\item The Polygon class has been modified to make it consistent with the +IVOA STC definition of a Polygon. Specifically, the inside of a polygon +is now the area to the left of each edge as the vertices are traversed in +an anti-clockwise manner, as seen from the inside of the celestial sphere. +Previously, AST used the anti-clockwise convention, but viewed from the +outside of the celestial sphere instead of the inside. Any Polygon saved +using previous versions of AST will be identified and negated automatically +when read by AST V5.2. + +\item A new class of Channel, called StcsChan, has been added that allows +conversion of suitable AST Objects to and from IVOA STC-S format. + +\item A new method called +c+ +astRemoveRegions +c- +f+ +AST\_REMOVEREGIONS +f- +has been added to the Mapping class. It searches a (possibly compound) +Mapping (or Frame) for any instances of the AST Region class, and either +removes them, or replaces them with UnitMaps (or equivalent Frames). It +can be used to remove the masking effects of Regions from a compound +Mapping or Frame. + +\item A new method called +c+ +astDownsize +c- +f+ +AST\_DOWNSIZE +f- +has been added to the Polygon class. It produces a new Polygon that +contains a subset of the vertices in the supplied Polygon. The subset is +chosen to retain the main features of the supplied Polygion, in so far +as that is possible, within specified constraints. + +\item A new constructor called +c+ +astOutline +c- +f+ +AST\_OUTLINE +f- +has been added to the Polygon class. Given a 2D data array, it identifies +the boundary of a region within the array that holds pixels with +specified values. It then creates a new Polygon to describe this boundary +to a specified accuracy. + +\item A new set of methods, called +c+ +astMapGetElem +c- +f+ +AST\_MAPGETELEM +f- +has been added to the KeyMap class. They allow a single element of a vector +valued entry to be returned. + +\item A new attribute called KeyError has been added to the KeyMap Class. It +controls whether the +c+ +astMapGet... +c- +f+ +AST\_MAPGET... +f- +family of functions report an error if an entry with the requested key does +not exist in the KeyMap. + +\end{enumerate} + +\subsection{Changes Introduced in V5.3} + +The following describes the most significant changes which +occurred in the AST library between versions V5.2 and V5.3: + +\begin{enumerate} + +\item The details of how a Frame is aligned with another Frame by the +c+ +astFindFrame and astConvert +c- +f+ +AST\_FINDFRAME and AST\_CONVERT +f- +functions have been changed. The changes mean that a Frame can now be +aligned with an instance of a sub-class of Frame, so long as the number +of axes and the Domain values are consistent. For instance, a basic +2-dimensional Frame with Domain ``SKY'' will now align succesfully with +a SkyFrame, conversion between the two Frames being achieved using a +UnitMap. + +c+ +\item The arrays that supply input values for astMapPut1 are now +declared ``const''. +c- + +\item Added method +c+ +astMatchAxes +c- +f+ +AST\_MATCHAXES +f- +to the Frame class. This method allows corresponding axes within two +Frames to be identified. + +\item The +c+ +astAddFrame +c- +f+ +AST\_ADDFRAME +f- +method can now be used to append one or more axes to all Frames in a FrameSet. +\end{enumerate} + +\subsection{Changes Introduced in V5.3-1} + +The following describes the most significant changes which have +occurred in the AST library between versions V5.3 and V5.3-1: + +\begin{enumerate} + +c+ +\item The utility functions provided by the AST memory management layer +are now documented in an appendix. +c- + +\item The KeyMap class now supports entries that have undefined values. A +new method called +c+ +astMapPutU +c- +f+ +AST\_MAPPUTU +f- +will store an entry with undefined value in a keymap. Methods that +retrieve values from a KeyMap +c+ +(astMapGet0, etc.) +c- +f+ +(AST\_MAPGET0, etc.) +f- +ignore entries with undefined values when searching for an entry with a given +key. + +\item The KeyMap class has a new method called +c+ +astMapCopy +c- +f+ +AST\_MAPCOPY +f- +that copies entries from one KeyMap to another KeyMap. + +\item The KeyMap class has a new boolean attribute called MapLocked. If +c+ +non-zero, +c- +f+ +.TRUE., +f- +an error is reported if an attempt is made to add any new entries +to a KeyMap (the value associated with any old entry may still be changed +without error). The default is +c+ +zero. +c- +f+ +.FALSE. +f- + +\item The Object class has a new method called astHasAttribute/AST\_HASATTRIBUTE +that returns a boolean value indicating if a specified Object has a named +attribute. + +\item The SkyFrame class has two new read-only boolean attributes called +IsLatAxis and IsLonAxis that can be used to determine the nature of a +specified SkyFrame axis. + +\item A bug has been fixed in the +c+ +astRebin(Seq) +c- +f+ +AST\_REBIN(SEQ) +f- +methods that could cause flux to be lost from the edges of the supplied array. + +\item A bug has been fixed in the +c+ +astRebin(Seq) +c- +f+ +AST\_REBIN(SEQ) +f- +methods that caused the first user supplied parameter to be interpreted as the +full width of the spreading kernel, rather than the half-width. + +\item The StcsChan class now ignores case when reading STC-S phrases (except +that units strings are still case sensitive). + +\item A new Mapping method, +c+ +astQuadApprox, +c- +f+ +AST\_QUADAPPROX, +f- +produces a quadratic least-squares fit to a 2D Mapping. + +\item A new Mapping method, +c+ +astSkyOffsetMap, +c- +f+ +AST\_SKYOFFSETMAP, +f- +produces a Mapping from absolute SkyFrame coordinates to offset SkyFrame +coordinates. + +\item The Channel class now has an Indent attribute that controls indentation +in the text created by +c+ +astWrite. +c- +f+ +AST\_WRITE. +f- +The StcsIndent and XmlIndent attributes have been removed. + +\item All classes of Channel now use the string ``'' to represent the +floating point value AST\_\_BAD, rather than the literal formatted value +(typically ``-1.79769313486232e+308'' ). + +\item The KeyMap class now uses the string ``'' to represent the +floating point value AST\_\_BAD, rather than the literal formatted value +(typically ``-1.79769313486232e+308'' ). + +\item The KeyMap class has a new method called +c+ +astMapPutElem +c- +f+ +AST\_MAPPUTELEM +f- +that allows a value to be put into a single element of a vector entry in +a KeyMap. The vector entry is extended automatically to hold the new +element if required. + +\item The DSBSpecFrame class now reports an error if the local oscillator +frequency is less than the absoliute value of the intermediate frequency. + +\end{enumerate} + + +\subsection{Changes Introduced in V5.3-2} + +The following describes the most significant changes which +occurred in the AST library between versions V5.3-1 and V5.3-2: + +\begin{enumerate} + +\item A bug has been fixed in the FitsChan class that could cause wavelength +axes to be assigned the units ``m/s'' when reading WCS information from a +FITS header. + +\item The +c+ +astSet function +c- +f+ +AST\_SET routine +f- +now allows literal commas to be included in string attribute values. String +attribute values that include a literal comma should be enclosed in quotation +marks. + +\item A bug in FitsChan has been fixed that caused ``-SIN'' projection +codes within FITS-WCS headers to be mis-interpreted, resulting in no +FrameSet being read by astRead. + +\item The KeyMap class has a new attribute called ``SortBy''. It controls +the order in which keys are returned by the +c+ +astMapKey +c- +f+ +AST\_MAPKEY +f- +function. Keys can be sorted alphabetically or by age, or left unsorted. + +\item Access to KeyMaps holding thousands of entries is now significantly +faster. + +\item KeyMaps can now hold word (i.e. +c+ +short integer) +c- +f+ +INTEGER*2) +f- +values. + +\end{enumerate} + + +\subsection{Changes Introduced in V5.4-0} + +The following describes the most significant changes which +occurred in the AST library between versions V5.3-2 and V5.4-0: + +\begin{enumerate} + +\item the FitsChan class now has an option to support reading and writing +of FITS-WCS headers that use the -TAB algorithm described in FITS-WCS paper +III. This option is controlled by a new FitsChan attribute called TabOK. +See the documentation for TabOK for more information. + +\item A new class called ``Table'' has been added. A Table is a KeyMap in +which each entry represents a cell in a two-dimensional table. + +\item A new class called ``FitsTable'' has been added. A FitsTable is a +Table that has an associated FitsChan holding headers appropriate to a +FITS binary table. + +\item KeyMaps can now hold byte values. These are held in variables +of type +c+ +"unsigned char". +c- +f+ +BYTE. +f- + +\item KeyMaps have a new attribute called KeyCase that can be set to zero to +make the handling of keys case insensitive. + +\item a memory leak associated with the use of the +c+ +astMapPutElem +c- +f+ +AST\_MAPPUTELEM +f- +functions has been fixed. + +\item A new method called +c+ +astMapRename +c- +f+ +AST\_MAPRENAME +f- +has been added to rename existing entry in a KeyMap. +\end{enumerate} + +\subsection{Changes Introduced in V5.5-0} + +The following describes the most significant changes which +occurred in the AST library between versions V5.4-0 and V5.5-0: + +\begin{enumerate} + +\item The FitsChan ``TabOK'' attribute is now an integer value rather +than a boolean value. If TabOK is set to a non-zero positive integer +before invoking the +c+ +astWrite +c- +f+ +AST\_WRITE +f- +method, its value is used as the version number for any table that is +created as a consequence of the write operation. This is the value stored +in the PVi\_1a keyword in the IMAGE header, and the EXTVER keyword in the +binary table header. In previous versions of AST, the value used for these +headers could not be controlled and was fixed at 1. If TabOK is set to a +negative or zero value, the -TAB algorithm will not be supported by +either the +c+ +astWrite or astRead +c- +f+ +AST\_WRITE or AST\_READ +f- +methods. + +\end{enumerate} + + + +\subsection{Changes Introduced in V5.6-0} + +The following describes the most significant changes which +occurred in the AST library between versions V5.5-0 and V5.6-0: + +\begin{enumerate} + +\item +c+ +New functions astBBuf and astEBuf +c- +f+ +New routines AST\_BBUF and AST\_EBUF +f- +have been added to the Plot class. These control the buffering of graphical +output produced by other Plot methods. + +\item New functions astGBBuf and astGEBuf have been added to the interface +defined by file \verb+grf.h+. The ast\_link command has been modified so +that the \verb+-grf_v3.2+ switch loads dummy versions of the new grf +functions. This means that applications that use the \verb+-grf_v3.2+ +switch should continue to build without any change. However, the new public +c+ +functions astBBuf and astEBuf +c- +f+ +routines AST\_BBUF and AST\_EBUF +f- +will report an error unless the new grf functions are implemented. If you +choose to implement them, you should modify your linking procedure to +use the \verb+-grf+ (or \verb+-grf_v5.6+ ) switch in place of the older +\verb+-grf_v3.2+ switch. See the description of the ast\_link command for +details of these switches. + +\item New method +c+ +astGetRegionMesh +c- +f+ +AST\_GETREGIONMESH +f- +returns a set of positions covering the boundary, or volume, of a supplied +Region. + +\end{enumerate} + + +\subsection{ChangesIntroduced in V5.6-1} + +The following describes the most significant changes which +occurred in the AST library between versions V5.6-0 and V5.6-1: + +\begin{enumerate} + +\item Tables can now have any number of parameters describing the global +properties of the Table. + +\item Frames now interpret the unit string ``A'' as meaning ``Ampere'' +rather than ``Angstrom'', as specified by FITS-WCS paper I. + +\item A bug has been fixed in the +c+ +astFindFrame +c- +f+ +AST\_FINDFRAME +f- +method that allowed a template Frame of a more specialised class to match +a target frame of a less specialised class. For example, this bug would +allow a template SkyFrame to match a target Frame. This no longer +happens. + +\end{enumerate} + +\subsection{Changes Introduced in V5.7-0} + +The following describes the most significant changes which +occurred in the AST library between versions V5.6-1 and V5.7-0: + +\begin{enumerate} + +\item The FitsChan class support for the IRAF-specific ``TNX'' projection has +been extended to include reading TNX headers that use a Chebyshev +representation for the distortion polynomial. + +\item The FitsChan class support for the IRAF-specific ``ZPX'' projection has +been extended to include reading ZPX headers that use simple or Chebyshev +representation for the distortion polynomial. + +\item A bug has been fixed in the FitsChan class that caused headers +including the Spitzer ``-SIP'' distortion code to be read incorrectly if no +inverse polynomial was specified in the header. + +\item A new attribute called PolyTan has been added to the FitsChan class. It +can be used to indicate that FITS headers that specify a TAN projection +should be interpreted according to the ``distorted TAN'' convention +included in an early draft of FITS-WCS paper II. Such headers are created +by (for instance) the SCAMP tool (\url{http://www.astromatic.net/software/scamp}). + +\item The PolyMap class now provides a method called +c+ +astPolyTran +c- +f+ +AST\_POLYTRAN +f- +that adds an inverse transformation to a PolyMap by sampling the forward +transformation on a regular grid, and then fitting a polynomial function +from the resulting output values to the grid of input values. + +\end{enumerate} + +\subsection{Changes Introduced in V5.7-1} + +The following describes the most significant changes which +occurred in the AST library between versions V5.7-0 and V5.7-1: + +\begin{enumerate} + +\item - All classes of Channel can now read to and write from specified +text files, without the need to provide source and sink functions when +the Channel is created. The files to use are specified by the new +attributes SourceFile and SinkFile. + +\item - The FitsChan class now ignores trailing spaces in character-valued WCS +keywords when reading a FrameSet from a FITS header. + +\item - If the FitsChan astRead method reads a FITS header that uses the +-SIP (Spitzer) distortion code within the CTYPE values, but which does +not provide an inverse polynomial correction, the FitsChan class will now +use the PolyTran method of the PolyMap class to create an estimate of the +inverse polynomial correction. + +\end{enumerate} + + +\subsection{Changes Introduced in V5.7-2} + +The following describes the most significant changes which +occurred in the AST library between versions V5.7-1 and V5.7-2: + +\begin{enumerate} + +c+ +\item The Object class has a new function astToString (C only), which creates +an in-memory textual serialisation of a given AST Object. A corresponding +new function called astFromString re-creates the Object from its +serialisation. +c- + +\item The PolyMap class can now use an iterative Newton-Raphson method to +evaluate the inverse the inverse transformation if no inverse +transformation is defined when the PolyMap is created. + +\item The FitsChan class has a new method +c+ +astWriteFits +c- +f+ +AST\_WRITEFITS +f- +which writes out all cards currently in the FitsChan to the associated +external data sink (specified either by the SinkFile attribute or the +sink function supplied when the FitsChan was created), and then empties +the FitsChan. + +\item The FitsChan class has a new read-only attribute called ``Nkey'', which +holds the number of keywords for which values are held in a FitsChan. + +\item The FitsChan +c+ +astGetFits +c- +f+ +AST\_GETFITS +f- +methods can now be used to returned the value of the current card. + +\item The FitsChan class has a new read-only attribute called ``CardType'', which +holds the data type of the keyword value for the current card. + +\item The FitsChan class has a new method +c+ +astReadFits +c- +f+ +AST\_READFITS +f- +which forces the FitsChan to reads cards from the associated external +source and appends them to the end of the FitsChan. + +\item - If the FitsChan astRead method reads a FITS header that uses the +-SIP (Spitzer) distortion code within the CTYPE values, but which does +not provide an inverse polynomial correction, and for which the PolyTran +method of the PolyMap class fails to create an accurate estimate of the +inverse polynomial correction, then an iterative method will be used to +evaluate the inverse correction for each point transformed. + +\end{enumerate} + +\subsection{Changes Introduced in V6.0} + +The following describes the most significant changes which +occurred in the AST library between versions V5.7-2 and V6.0: + +\begin{enumerate} + +\item This version of AST is the first that can be used with the Python +AST wrapper module, starlink.Ast, available at \url{http://github.com/timj/starlink-pyast}. + +\item When reading a FITS-WCS header, the FitsChan class now recognises the +non-standard ``TPV'' projection code within a CTYPE keyword value. This +code is used by SCAMP (see www.astromatic.net/software/scamp) to +represent a distorted TAN projection. + +\item The Plot class has been changed to remove visual anomalies (such as +incorrectly rotated numerical axis labels) if the graphics coordinates have +unequal scales on the X and Y axes. + +- The graphics escape sequences used to produce graphical sky axis labels +can now be changed using the new +c+ +function astTuneC. +c- +f+ +routine AST\_TUNEC. +f- + +\end{enumerate} + +\subsection{Changes Introduced in V6.0-1} + +The following describes the most significant changes which +occurred in the AST library between versions V6.0 and V6.0-1: + +\begin{enumerate} + +\item The FitsChan class now recognises the Spitzer ``-SIP'' distortion +code within FITS headers that describe non-celestial axes, as well as +celestial axes. + +\item A bug has been fixed that could cause inappropriate equinox values to +be used when aligning SkyFrames if the AlignSystem attribute is set. + +\item The versioning string for AST has changed from +``$.-$'' to ``$..$''. + +\end{enumerate} + +\subsection{Changes Introduced in V7.0.0} + +The following describes the most significant changes which +occurred in the AST library between versions V6.0-1 and V7.0.0: + +\begin{enumerate} + +\item Fundamental positional astronomy calculations are now performed +using the IAU SOFA library where possible, and the Starlink PAL library \xref{SUN/268}{sun268}{} +otherwise (the PAL library contains a subset of the Fortran Starlink SLALIB +library re-written in C). Copies of these libraries are bundled with AST +and so do not need to be obtained or built separately, although external +copies of SOFA and PAL can be used if necessary by including the +``\texttt{--with-external\_pal}'' option when configuring AST. + +\end{enumerate} + +\subsection{Changes Introduced in V7.0.1} + +The following describes the most significant changes which +occurred in the AST library between versions V7.0.0 and V7.0.1: + +\begin{enumerate} + +\item The levmar and wcslib code distributed within AST is now stored in the +main AST library (libast.so) rather than in separate libraries. + +\end{enumerate} + +\subsection{Changes Introduced in V7.0.2} + +The following describes the most significant changes which +occurred in the AST library between versions V7.0.1 and V7.0.2: + +\begin{enumerate} + +\item The libast\_pal library is no longer built if the +``--with-external\_pal'' option is used when AST is configured. + +\end{enumerate} + +\subsection{Changes Introduced in V7.0.3} + +The following describes the most significant changes which +occurred in the AST library between versions V7.0.2 and V7.0.3: + +\begin{enumerate} + +\item A bug has been fixed which could cause an incorrect axis to be used when +accessing axis attributes within CmpFrames. This could happen if axes +within the CmpFrame have been permuted. + +\item A bug has been fixed in the SkyFrame class that could cause the two +values of the SkyRef and/or SkyRefP attributes to be reversed. + +\item Bugs have been fixed in the CmpRegion class that should allow the border +around a compound Region to be plotted more quickly, and more accurately. +Previously, component Regions nested deeply inside a CmpRegion may have +been completely or partially ignored. + +\item A bug has been fixed in the Plot3D class that caused a segmentation +violation if the MinTick attribute was set to zero. + +\item The astResampleX set of methods now includes astResampleK and +astResampleUK that handles 64 bit integer data. + +\end{enumerate} + + +\subsection{Changes Introduced in V7.0.4} + +The following describes the most significant changes which +occurred in the AST library between versions V7.0.3 and V7.0.4: + + +\begin{enumerate} + +\item The previously private grf3d.h header file is now installed into +prefix/include. + +\end{enumerate} + + +\subsection{Changes Introduced in V7.0.5} + +The following describes the most significant changes which +occurred in the AST library between versions V7.0.4 and V7.0.5: + +\begin{enumerate} + +\item The FitsChan class can now read FITS headers that use the SAO +convention for representing distorted TAN projections, based on the use +of ``COi\_m'' keywords to hold the coefficients of the distortion polynomial. + +\end{enumerate} + + +\subsection{Changes Introduced in V7.0.6} + +The following describes the most significant changes which +occurred in the AST library between versions V7.0.5 and V7.0.6: + +\begin{enumerate} + +\item A bug has been fixed in astRebinSeq which could result in +incorrect normalisation of the final binned data and variance values. + +\item When reading a FrameSet from a FITS-DSS header, the keywords CNPIX1 +and CNPIX2 now default to zero if absent. Previously an error was reported. + +\end{enumerate} + + +\subsection{Changes Introduced in V7.1.0} + +The following describes the most significant changes which occurred in the +AST library between versions V7.0.6 and V7.1.0: + +\begin{enumerate} + +\item IMPORTANT! The default behaviour of astRebinSeq is now NOT to conserve +flux. To conserve flux, the AST\_\_CONSERVEFLUX flag should be supplied +when calling +c+ +astRebinSeq. +c- +f+ +AST\_REBINSEQ. +f- +Without this flag, each output value is a weighted mean of the neighbouring +input values. + +\item A new flag AST\_\_NONORM can be used with astRebinSeq to indicate that +normalisation of the output arrays is not required. In this case no +weights array need be supplied. + +\item A bug has been fixed in +c+ +astAddFrame method +c- +f+ +AST\_ADDFRAME routine +f- +that could result in the incorrect inversion of Mappings within the FrameSet +when the AST\_\_ALLFRAMES flag is supplied for the +c+ +"iframe" parameter. +c- +f+ +IFRAME argument. +f- + +\item The +c+ +astRate method +c- +f+ +AST\_RATE function +f- +has been re-written to make it faster and more reliable. + +\end{enumerate} + +\subsection{Changes Introduced in V7.1.1} + +The following describes the most significant changes which +occurred in the AST library between versions V7.1.0 and V7.1.1: + +\begin{enumerate} + +\item When a FitsChan is used to write an ``offset'' SkyFrame (see attribute +SkyRefIs) to a FITS-WCS encoded header, two alternate axis descriptions +are now created - one for the offset coordinates and one for the absolute +coordinates. If such a header is subsequently read back into AST, the +original offset SkyFrame is recreated. + +\item A bug has been fixed in FitsChan that caused inappropriate CTYPE values +to be generated when writing a FrameSet to FITS-WCS headers if the +current Frame describes generalised spherical coordinates (i.e. a +SkyFrame with System=Unknown). + +\end{enumerate} + +\subsection{Changes Introduced in V7.2.0} + +The following describes the most significant changes which +occurred in the AST library between versions V7.1.1 and V7.2.0: + +\begin{enumerate} + +\item A new method call +c+ +astMapDefined +c- +f+ +AST\_MAPDEFINED +f- +has been added to the KeyMap class. It checks if a gtiven key name has +a defined value in a given KeyMap. + +\end{enumerate} + +\subsection{Changes Introduced in V7.3.0} + +The following describes the most significant changes which +occurred in the AST library between versions V7.2.0 and V7.3.0: + +\begin{enumerate} + +c+ +\item The interface for the astRebinSeq family of functions has +been changed in order to allow a greater number of pixels to be pasted +into the output array. The "nused" parameter is now a pointer to a +"int64\_t" variable, instead of an "int". APPLICATION CODE SHOULD BE +CHANGED ACCORDINGLY TO AVOID SEGMENTATION FAULTS AND OTHER ERRATIC +BEHAVIOUR. +c- +f+ +\item The interface for the AST\_REBINSEQ family of routines has +been changed in order to allow a greater number of pixels to be pasted +into the output array. The NUSED parameter is now an INTEGER*8 variable, +instead of an INTEGER. APPLICATION CODE SHOULD BE CHANGED ACCORDINGLY TO +AVOID SEGMENTATION FAULTS AND OTHER ERRATIC BEHAVIOUR. +f- + +\item Added a new facility to the FrameSet class to allow each Frame to be +associated with multiple Mappings, any one of which can be used to +connect the Frame to the other Frames in the FrameSet. The choice of +which Mapping to use is controlled by the new ``Variant'' attribute of the +FrameSet class. + +\item Mappings (but not Frames) that have a value set for their Ident +attribute are now left unchanged by the +c astSimplify function. +f AST\_SIMPLIFY routine. + +\end{enumerate} + +\subsection{Changes Introduced in V7.3.1} + +The following describes the most significant changes which +occurred in the AST library between versions V7.3.0 and V7.3.1: + +\begin{enumerate} + +\item Fix a bug that could cauise a segmentation violation when reading +certain FITS headers that use a TNX projection. + +\end{enumerate} + +\subsection{Changes Introduced in V7.3.2} + +The following describes the most significant changes which +occurred in the AST library between versions V7.3.1 and V7.3.2: + +\begin{enumerate} + +\item Fix support for reading FITS header that use a GLS projection. +Previously, an incorrect transformation was used for such projections if +any CRVAL or CROTA value was non-zero. + +\item The KeyMap class has new sorting options ``KeyAgeUp'' and +``KeyAgeDown'' that retain the position of an existing entry if its value +is changed. See the SortBy attribute. + +\item A bug has been fixed in the FitsChan class that caused CDELT keywords +for sky axes to be treated as radians rather than degrees when reading a +FITS header, if the corresponding CTYPE values included no projection code. + +\end{enumerate} + +\subsection{Changes Introduced in V7.3.3} + +The following describes the most significant changes which +occurred in the AST library between versions V7.3.2 and V7.3.3: + +\begin{enumerate} + +\item The FitsChan class has new attributes CardName and CardComm, which hold +the keyword name and comment of the current card. + +\item When using the FitsChan class to read FITS-WCS headers that include +polynomial distortion in the SIP format, any inverse transformation specified +in the header is now ignored and a new inverse is created to replace it based +on the supplied forward transformation. Previously, an inverse was created +only if the header did not include an inverse. The accuracy of the inverse +transformation has also been improved, although it may now be slower to +evaluate in some circumstances. + +\end{enumerate} + +\subsection{Changes Introduced in V7.3.4} + +The following describes the most significant changes which +occurred in the AST library between versions V7.3.3 and V7.3.4: + +\begin{enumerate} + +\item By default, the simplification of Polygons no longer checks that the +edges are not bent by the simplification. A new attribute, SimpVertices, +can be set to zero in order to re-instate this check. + +\item The Polygon class has a new mathod, +c+ +astConvex, +c- +f+ +AST\_CONVEX, +f- +that returns a Polygon representing the shortest polygon (i.e. convex +hull) enclosing a specified set of pixel values within a supplied array. + +\end{enumerate} + +\subsection{Changes Introduced in V8.0.0} + +The following describes the most significant changes which +occurred in the AST library between versions V7.3.4 and V8.0.0: + +\begin{enumerate} + +\item AST is now distributed under the Lesser GPL licence. + +\item The PolyMap class now uses files copied from the C/C++ Minpack +package (see \url{http://devernay.free.fr/hacks/cminpack/index.html}) to perform +least squares fitting of N-dimensional polynomials. + +\item Use of the IAU SOFA library has been replaced by ERFA library, which is +a re-badged copy of SOFA distributed under a less restrictive license. A +copy of ERFA is included within AST. + +\end{enumerate} + +\subsection{Changes Introduced in V8.0.1} + +The following describes the most significant changes which +occurred in the AST library between versions V8.0.0 and V8.0.1: + +\begin{enumerate} + +\item The Base and Current attributes of a FrameSet may now be set using the + Domain name or the index of the required Frame. +\item The order of WCS axes within new FITS-WCS headers created by astWrite + can now be controlled using a new attribute called FitsAxisOrder. +\item Supported added for FITS XPH (polar HEALPIX) projection. +c+ +\item The macro used to invoke the astAppendString utility function has + changed to allow printf-style converstions to be included in the + supplied text. Any code that uses this macro must be re-compiled. +\item The astRebin and astRebinSeq family of functions now include support + for arrays with char (byte) and unsigned char (unsigned byte) data types. +c- +f+ +\item The AST\_REBIN and AST\_REBINSEQ family of functions now include support + for arrays with \_BYTE (byte) and and \_UBYTE (unsigned byte) data types. +f- + +\end{enumerate} + +\subsection{Changes Introduced in V8.0.2} +c+ +The following describes the most significant changes which +occurred in the AST library between versions V8.0.1 and V8.0.2: + +\begin{enumerate} +\item For security reasons, the change introduced to astAppendString in + V8.0.1 has been moved to a new function called astAppendStringf, and + astAppendString itself has been reverted to its V8.0.0 version. + Any software that has been built against V8.0.1 will need to be + re-compiled and re-linked against V8.0.2. +\end{enumerate} + +c- +f+ +The changes that occurred in the AST library between versions V8.0.1 and +V8.0.2 only affect the C interface. The Fortran interface remains the +same as V8.0.1. +f- + +\subsection{Changes Introduced in V8.0.3} +The following describes the most significant changes which +occurred in the AST library between versions V8.0.2 and V8.0.3: + +\begin{enumerate} + +\item Methods +c+ +astRebin, astRebinSeq, astResample and astTranGrid +c- +f+ +AST\_REBIN, AST\_REBINSEQ, AST\_RESAMPLE and AST\_TRANGRID. +f- +now report an error if an array is specified that has more pixels than +can be counted by a 32 bit integer. +\item The hypertext documentation is now generated using Tex4HT rather +than latex2html. The format of the hypertext docs has changed significantly. +\item Another bug fix associated with reading CAR projections from +FITS-WCS headers. +f+ +\item Trailing spaces supplied within attribute setting strings are now ignored. +f- +c+ +\item Constructor options strings of the form ``\texttt{..., "\%s", text );}'' +can now be supplied. This avoids a security issue associated with the +alternative form ``\texttt{..., text );}''. +c- +\end{enumerate} + +\subsection{Changes Introduced in V8.0.4} +The following describes the most significant changes which +occurred in the AST library between versions V8.0.3 and V8.0.4: + +\begin{enumerate} + +\item The behaviour of the +c+ +astAddFrame method has been changed slightly. Previously, astAddFrame +c- +f+ +AST\_ADDFRAME method has been changed slightly. Previously, AST\_ADDFRAME +f- +modified the FrameSet by storing references to the supplied Mapping and +Frame objects within the FrameSet. This meant that any subsequent changes +to the current Frame of the modified FrameSet also affected the supplied +Frame object. Now, deep copies of the Mapping and Frame objects (rather +than references) are stored within the modified FrameSet. This means that +subsequent changes to the modified FrameSet will now have no effect on +the supplied Frame. + +\item The choice of default tick-mark gaps for time axes has been +improved, to avoid a previous issue which could result in no suitable gap +being found. + +- A new method called +c+ +astRegionOutline +c- +f+ +AST\_REGIONOUTLINE +f- +has been added to the Plot class. It draws the outline of a supplied AST +Region. + +\item A bug has been fixed that could cause astSimplfy to enter an infinite loop. + +\item Some improvements have been made to the Mapping simplification process +that allow more Mappings to be simplified. + +\item The Frame class has a new read-only attribute called InternalUnit, +which gives the units used for the unformatted (i.e. floating-point) axis +values used internally by application code. For most Frames, the +InternalUnit value is just the same as the Unit value (i.e. formatted and +unformatted axis values use the same units). However, the SkyFrame class +always returns ``\texttt{rad}'' for InternalUnit, regardless of the value of +Unit, indicating that floating-point SkyFrame axis values are always in units +of radians. + +\item The LutMap class has a new attribute called LutEpsilon, which specifies +the relative error of the values in the table. It is used to decide if +the LutMap can be simplified to a straight line. + +\end{enumerate} + + +\subsection{Changes Introduced in V8.0.5} +The following describes the most significant changes which +occurred in the AST library between versions V8.0.4 and V8.0.5: + +\begin{enumerate} + +\item The SkyFrame class has a new attribute called SkyTol, which specifies +the smallest significant distance within the SkyFrame. It is used to +decide if the Mapping between two SkyFrames can be considered a unit +transformation. The default value is 0.001 arc-seconds. + +\item A bug has been fixed in the FitsChan class that prevented illegal +characters within FITS keyword names (i.e. characters not allowed by the +FITS standard) being detected. This bug could under some circumstances +cause a subsequent segmentation violation to occur. + +- A ``BadKeyName'' warning is now issued by the FitsChan class if a FITS +keyword name is encountered that contains any illegal characters. See +attribute ``Warnings'' and +c+ +function ``astWarnings''. +c- +f+ +routine ``AST\_WARNINGS''. +f- + +\subsection{Changes Introduced in V8.1.0} +The following describes the most significant changes which +occurred in the AST library between versions V8.0.5 and V8.1.0: + +\begin{enumerate} + +\item The configure script has a new option ``--without-fortran'' that allows +AST to be built in situations where no Fortran compiler is available. The +resulting library has no Fortran interface and so cannot be used within +Fortran applications. Also, the link scripts do not attempt to include the +fortran runtime libraries. + +\end{enumerate} + +\subsection{\xlabel{changes}\xlabel{list_of_most_recent_changes}Changes +Introduced in V8.2} +The following describes the most significant changes which have +occurred in the AST library between versions V8.1.0 and V8.2.0 (the +current version): + +\begin{enumerate} + +\item The restriction that Mappings are immutable has been extended to all +Mapping classes. This means that attributes representing parameters of +a Mapping's forward or inverse transformation cannot be changed after +the Mapping has been created. In order to minimise the risk to existing +software, this rule does not apply to Mappings that have not yet been +included in other objects such as CmpMaps or FrameSets, or which have not +yet been cloned. In other words, an error is reported if an attempt is +made to change the nature of a Mapping's transformation, but only if the +reference count of the Mapping is greater than one. The Mapping classes +affected include: GrismMap, LutMap, PcdMap, SphMap, WcsMap and ZoomMap. + +\end{enumerate} + +% Programs which are statically linked will need to be re-linked in +% order to take advantage of these new facilities. + +\end{document} diff --git a/switchmap.c b/switchmap.c new file mode 100644 index 0000000..23dec4b --- /dev/null +++ b/switchmap.c @@ -0,0 +1,2875 @@ +/* +*class++ +* Name: +* SwitchMap + +* Purpose: +* A Mapping that encapsulates a set of alternate Mappings. + +* Constructor Function: +c astSwitchMap +f AST_SWITCHMAP + +* Description: +* A SwitchMap is a Mapping which represents a set of alternate +* Mappings, each of which is used to transform positions within a +* particular region of the input or output coordinate system of the +* SwitchMap. +* +* A SwitchMap can encapsulate any number of Mappings, but they must +* all have the same number of inputs (Nin attribute value) and the +* same number of outputs (Nout attribute value). The SwitchMap itself +* inherits these same values for its Nin and Nout attributes. Each of +* these Mappings represents a "route" through the switch, and are +* referred to as "route" Mappings below. Each route Mapping transforms +* positions between the input and output coordinate space of the entire +* SwitchMap, but only one Mapping will be used to transform any given +* position. The selection of the appropriate route Mapping to use with +* any given input position is made by another Mapping, called the +* "selector" Mapping. Each SwitchMap encapsulates two selector +* Mappings in addition to its route Mappings; one for use with the +* SwitchMap's forward transformation (called the "forward selector +* Mapping"), and one for use with the SwitchMap's inverse transformation +* (called the "inverse selector Mapping"). The forward selector Mapping +* must have the same number of inputs as the route Mappings, but +* should have only one output. Likewise, the inverse selector Mapping +* must have the same number of outputs as the route Mappings, but +* should have only one input. +* +* When the SwitchMap is used to transform a position in the forward +* direction (from input to output), each supplied input position is +* first transformed by the forward transformation of the forward selector +* Mapping. This produces a single output value for each input position +* referred to as the selector value. The nearest integer to the selector +* value is found, and is used to index the array of route Mappings (the +* first supplied route Mapping has index 1, the second route Mapping has +* index 2, etc). If the nearest integer to the selector value is less +* than 1 or greater than the number of route Mappings, then the SwitchMap +* output position is set to a value of AST__BAD on every axis. Otherwise, +* the forward transformation of the selected route Mapping is used to +* transform the supplied input position to produce the SwitchMap output +* position. +* +* When the SwitchMap is used to transform a position in the inverse +* direction (from "output" to "input"), each supplied "output" position +* is first transformed by the inverse transformation of the inverse +* selector Mapping. This produces a selector value for each "output" +* position. Again, the nearest integer to the selector value is found, +* and is used to index the array of route Mappings. If this selector +* index value is within the bounds of the array of route Mappings, then +* the inverse transformation of the selected route Mapping is used to +* transform the supplied "output" position to produce the SwitchMap +* "input" position. If the selector index value is outside the bounds +* of the array of route Mappings, then the SwitchMap "input" position is +* set to a value of AST__BAD on every axis. +* +* In practice, appropriate selector Mappings should be chosen to +* associate a different route Mapping with each region of coordinate +* space. Note that the SelectorMap class of Mapping is particularly +* appropriate for this purpose. +* +* If a compound Mapping contains a SwitchMap in series with its own +* inverse, the combination of the two adjacent SwitchMaps will be +* replaced by a UnitMap when the compound Mapping is simplified using +c astSimplify. +f AST_SIMPLIFY. + +* Inheritance: +* The SwitchMap class inherits from the Mapping class. + +* Attributes: +* The SwitchMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The SwitchMap class does not define any new functions beyond those +f The SwitchMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 13-MAR-2006 (DSB): +* Original version. +* 17-MAR-2006 (DSB): +* Guard against AST__BAD selector values. +* 9-MAY-2006 (DSB): +* Check selector Mapping pointers are not NULL before calling +* astEqual in Equal. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS SwitchMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "unitmap.h" /* Unit Mappings */ +#include "channel.h" /* I/O channels */ +#include "switchmap.h" /* Interface definition for this class */ +#include "frame.h" /* Frames */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(SwitchMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(SwitchMap,Class_Init) +#define class_vtab astGLOBAL(SwitchMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstSwitchMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstSwitchMap *astSwitchMapId_( void *, void *, int, void **, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static AstMapping *GetSelector( AstSwitchMap *, int, int *, int * ); +static AstMapping *GetRoute( AstSwitchMap *, double, int *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two SwitchMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "switchmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* SwitchMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two SwitchMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a SwitchMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the SwitchMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *fsmap1; + AstMapping *fsmap2; + AstMapping *ismap1; + AstMapping *ismap2; + AstMapping *rmap1; + AstMapping *rmap2; + AstSwitchMap *that; + AstSwitchMap *this; + int fsinv1; + int fsinv2; + int isinv1; + int i; + int isinv2; + int nroute; + int result; + int rinv1; + int rinv2; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two SwitchMap structures. */ + this = (AstSwitchMap *) this_object; + that = (AstSwitchMap *) that_object; + +/* Check the second object is a SwitchMap. We know the first is a + SwitchMap since we have arrived at this implementation of the virtual + function. */ + if( astIsASwitchMap( that ) ) { + +/* Check they have the same number of route mappings. */ + nroute = this->nroute; + if( that->nroute == nroute ) { + +/* Get the forward selector Mappings from the two SwitchMaps. */ + fsmap1 = GetSelector( this, 1, &fsinv1, status ); + fsmap2 = GetSelector( that, 1, &fsinv2, status ); + +/* Are they equal? */ + if( ( !fsmap1 && !fsmap2 ) || + ( fsmap1 && fsmap2 && astEqual( fsmap1, fsmap2 ) ) ) { + +/* Get the inverse selector Mappings from the two SwitchMaps. */ + ismap1 = GetSelector( this, 0, &isinv1, status ); + ismap2 = GetSelector( that, 0, &isinv2, status ); + +/* Are they equal? */ + if( ( !ismap1 && !ismap2 ) || + ( ismap1 && ismap2 && astEqual( ismap1, ismap2 ) ) ) { + +/* Loop over the route mappings, breaking as soon as two unequal route + Mappings are found. Re-instate the original values for the route + Mapping Invert flag after testing the route Mappings for equality. */ + result = 1; + for( i = 0; result && i < nroute; i++ ) { + rmap1 = GetRoute( this, (double) ( i + 1 ), &rinv1, status ); + rmap2 = GetRoute( that, (double) ( i + 1 ), &rinv2, status ); + if( !astEqual( rmap1, rmap2 ) ) result = 0; + astSetInvert( rmap2, rinv2 ); + astSetInvert( rmap1, rinv1 ); + } + } + +/* Reinstate the invert flags for the inverse selector Mappings. Ensure + this is done in the opposite order to which the selector Mappings were + obtained (in case they are in fact the same Mapping). */ + if( ismap2 ) astSetInvert( ismap2, isinv2 ); + if( ismap1 ) astSetInvert( ismap1, isinv1 ); + } + +/* Reinstate the invert flags for the forward selector Mappings. Ensure + this is done in the oppsote order to which the selector Mappings were + obtained (in case they are in fact the same Mapping). */ + if( fsmap2 ) astSetInvert( fsmap2, fsinv2 ); + if( fsmap1 ) astSetInvert( fsmap1, fsinv1 ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "switchmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* SwitchMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied SwitchMap, +* in bytes. + +* Parameters: +* this +* Pointer to the SwitchMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstSwitchMap *this; + int i; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the SwitchMap structure. */ + this = (AstSwitchMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->fsmap ); + result += astGetObjSize( this->ismap ); + + for( i = 0; i < this->nroute; i++ ) { + result += astGetObjSize( this->routemap[ i ] ); + } + + result += astGetObjSize( this->routeinv ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static AstMapping *GetRoute( AstSwitchMap *this, double sel, int *inv, int *status ){ +/* +* Name: +* GetRoute + +* Purpose: +* Return a pointer to a route Mapping, handling all Invert flags. + +* Type: +* Private function. + +* Synopsis: +* #include "switchmap.h" +* AstMapping *GetRoute( AstSwitchMap *this, double sel, int *inv, int *status ) + +* Class Membership: +* SwitchMap method. + +* Description: +* This function returns a pointer to a route Mapping (specified by a +* floating point selector value) for the given SwitchMap, taking account +* of the state of the Invert flag of both the route Mapping and the +* SwitchMap. + +* Parameters: +* this +* Pointer to the SwitchMap. +* sel +* The selector value. The nearest integer value (minus 1) is used +* to index the array of route Mappings stored in the SwitchMap. A +* NULL pointer is returned if the selector value is out of range. +* inv +* Pointer to an int in which to return the original value of the +* Invert flag of the returned Mapping. The astSetInvert method +* should be used to re-instate this value once all use of the Mapping +* has been completed. +* status +* Pointer to the inherited status variable. + +* Returns: +* A pointer to the route Mapping to use. Note, the returned pointer +* should NOT be annulled when no longer needed. NULL is returned +* (without error) if the SwitchMap does not have a route Mapping for the +* requested selector value. The forward transformation of the +* returned Mapping will implenment the forward transformation of the +* required route Mapping (and vice-versa). + +*/ + +/* Local Variables: */ + AstMapping *ret; + int rindex; + +/* Initialise */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Check selector value is good. */ + if( sel != AST__BAD ) { + +/* Convert the supplied floating point selector value into an integer + index into the array of route Mappings held in the supplied SwitchMap. */ + rindex = (int)( sel + 0.5 ) - 1; + +/* Return the null pointer if the index is out of range. */ + if( rindex >= 0 && rindex < this->nroute ) { + +/* Get the required route Mapping. */ + ret = ( this->routemap )[ rindex ]; + +/* Return its original invert flag. */ + *inv = astGetInvert( ret ); + +/* Set the Invert flag back to the value it had when the SwitchMap was + created. */ + astSetInvert( ret, this->routeinv[ rindex ] ); + +/* If the SwitchMap has since been inverted, also invert the returned + route Mapping, so that the forward transformation of the returned + Mapping implements the forward transformation of the supplied + SwitchMap (and vice-versa). */ + if( astGetInvert( this ) ) astInvert( ret ); + } + } + +/* Return the pointer. */ + return ret; + +} + +static AstMapping *GetSelector( AstSwitchMap *this, int fwd, int *inv, int *status ){ +/* +* Name: +* GetSelector + +* Purpose: +* Return a pointer to a selector Mapping, handling all Invert flags. + +* Type: +* Private function. + +* Synopsis: +* #include "switchmap.h" +* AstMapping *GetSelector( AstSwitchMap *this, int fwd, int *inv, int *status ) + +* Class Membership: +* SwitchMap method. + +* Description: +* This function returns a pointer to either the forward or inverse +* selector Mapping for the given SwitchMap, taking account of the +* state of the Invert flag of bothe the selector Mapping and the +* SwitchMap. + +* Parameters: +* this +* Pointer to the SwitchMap. +* fwd +* If non-zero, return the forward selector Mapping. Otherwise, +* return the inverse selector Mapping. +* inv +* Pointer to an int in which to return the original value of the +* Invert flag of the returned Mapping. The astSetInvert method +* should be used to re-instate this value once all use of the Mapping +* has been completed. +* status +* Pointer to the inherited status variable. + +* Returns: +* A pointer to the selector Mapping to use. Note, the returned pointer +* should NOT be annulled when no longer needed. NULL is returned +* (without error) if the SwitchMap does not have a Mapping for the +* requested selector. + +*/ + +/* Local Variables: */ + AstMapping *ret; + int swinv; + +/* Initialise */ + ret = NULL; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* See if the SwitchMap has been inverted. */ + swinv = astGetInvert( this ); + +/* If the SwitchMap has been inverted, the forward and inverse selector + Mappings should be reversed. */ + if( ( !swinv && !fwd ) || ( swinv && fwd ) ){ + ret = this->ismap; + if( ret ) { + *inv = astGetInvert( ret ); + astSetInvert( ret, this->isinv ); + } + + } else { + ret = this->fsmap; + if( ret ) { + *inv = astGetInvert( ret ); + astSetInvert( ret, this->fsinv ); + } + } + + if( ret && swinv ) astInvert( ret ); + +/* Return the pointer. */ + return ret; + +} + +void astInitSwitchMapVtab_( AstSwitchMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitSwitchMapVtab + +* Purpose: +* Initialise a virtual function table for a SwitchMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "switchmap.h" +* void astInitSwitchMapVtab( AstSwitchMapVtab *vtab, const char *name ) + +* Class Membership: +* SwitchMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the SwitchMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsASwitchMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* None. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + mapping->Rate = Rate; + mapping->RemoveRegions = RemoveRegions; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "SwitchMap", "Alternate regionalised Mapping" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* SwitchMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstSwitchMap *this; /* Pointer to SwitchMap structure */ + int i; /* Loop count */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the SwitchMap structure. */ + this = (AstSwitchMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->fsmap, mode, extra, fail ); + if( !result ) result = astManageLock( this->ismap, mode, extra, fail ); + for( i = 0; i < this->nroute; i++ ) { + if( !result ) result = astManageLock( this->routemap[ i ], mode, + extra, fail ); + } + + return result; + +} +#endif + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a SwitchMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* SwitchMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated SwitchMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated SwitchMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated SwitchMap which is to be merged with +* its neighbours. This should be a cloned copy of the SwitchMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* SwitchMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated SwitchMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSwitchMap *map; + AstMapping *new; + int i; + int nroute; + int result; + int fsinv_old; + int isinv_old; + int *rinv_old; + AstMapping *sfsmap; + AstMapping *sismap; + int simp; + AstMapping **srmap; + AstSwitchMap *swneb; + int ilo; + int equal; + +/* Initialise.*/ + result = -1; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Get a pointer to this SwitchMap, and note the number of route Mappings. */ + map = (AstSwitchMap *) this; + nroute = map->nroute; + +/* Temporarily put the Invert flag of all encapsulated Mappings (both + route and selector) back to the values they had when the SwitchMap was + created, noting their current values so that they can be re-instated + later. If the SwitchMap itself has been inverted, swap all the original + invert flags. */ + if( map->fsmap ) { + fsinv_old = astGetInvert( map->fsmap ); + astSetInvert( map->fsmap, map->fsinv ); + } else { + fsinv_old = 0; + } + + if( map->ismap ) { + isinv_old = astGetInvert( map->ismap ); + astSetInvert( map->ismap, map->isinv ); + } else { + isinv_old = 0; + } + + rinv_old = astMalloc( sizeof( int )*nroute ); + if( astOK ) { + for( i = 0; i < nroute; i++ ) { + rinv_old[ i ] = astGetInvert( map->routemap[ i ] ); + astSetInvert( map->routemap[ i ], map->routeinv[ i ] ); + } + } + +/* If possible, merge the SwitchMap with a neighbouring SwitchMap. */ +/* =============================================================== */ +/* Only do this if we are combining the Mappings in series. */ + if( series ) { + +/* Is the higher neighbour a SwitchMap? If so get a pointer to it, and + note the index of the lower of the two adjacent SwitchMaps. */ + if( where < ( *nmap - 1 ) && + astIsASwitchMap( ( *map_list )[ where + 1 ] ) ){ + swneb = (AstSwitchMap *) ( *map_list )[ where + 1 ]; + ilo = where; + +/* If not, is the lower neighbour a SwitchMap? If so get a pointer to it, and + note the index of the lower of the two adjacent SwitchMaps. */ + } else if( where > 0 && + astIsASwitchMap( ( *map_list )[ where - 1 ] ) ){ + swneb = (AstSwitchMap *) ( *map_list )[ where - 1 ]; + ilo = where - 1; + + } else { + swneb = NULL; + } + +/* If a neighbouring SwitchMap was found, we can replace the pair by a + UnitMap if the two SwitchMaps are equal but have opposite values for + their Invert flags. Temporarily invert the neighbour, then compare + the two SwitchMaps for equality, then re-invert the neighbour. */ + if( swneb ) { + astInvert( swneb ); + equal = astEqual( map, swneb ); + astInvert( swneb ); + +/* If the two SwitchMaps are equal but opposite, annul the first of the two + Mappings, and replace it with a UnitMap. Also set the invert flag. */ + if( equal ) { + new = (AstMapping *) astUnitMap( astGetNin( ( *map_list )[ ilo ] ), "", status ); + (void) astAnnul( ( *map_list )[ ilo ] ); + ( *map_list )[ ilo ] = new; + ( *invert_list )[ ilo ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ ilo + 1 ] ); + for ( i = ilo + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = where; + } + } + } + +/* Attempt to simplify the SwitchMap on its own. */ +/* ============================================= */ +/* Only do this if no change was made above. */ + if( result == -1 ) { + +/* If the SwitchMap is inverted, create an equal SwitchMap which is not + inverted. To do this, invert and swap the selector Mappings, and + invert all the route Mappings. We use astSetInvert rather than astInvert + because two or more more stored pointers may point to the same Mapping + in which case that Mapping would be inverted more than once with + unpredictable results. */ + if( ( *invert_list )[ where ] ) { + if( map->fsmap ) astSetInvert( map->fsmap, !(map->fsinv) ); + if( map->ismap ) astSetInvert( map->ismap, !(map->isinv) ); + for( i = 0; i < nroute; i++ ) { + astSetInvert( map->routemap[ i ], !(map->routeinv[ i ]) ); + } + + new = (AstMapping *) astSwitchMap( map->ismap, map->fsmap, nroute, (void **) map->routemap, "", status ); + + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) new; + ( *invert_list )[ where ] = 0; + result = where; + +/* Otherwise, try to simplify each of the encapsulated Mappings, noting + if any simplification takes place. */ + } else { + sfsmap = ( map->fsmap ) ? astSimplify( map->fsmap ) : NULL; + sismap = ( map->ismap ) ? astSimplify( map->ismap ) : NULL; + simp = ( sfsmap != map->fsmap ) || ( sismap != map->ismap ); + + srmap = astMalloc( sizeof( AstMapping * )*nroute ); + if( astOK ) { + for( i = 0; i < nroute; i++ ) { + srmap[ i ] = astSimplify( map->routemap[ i ] ); + simp = simp || ( srmap[ i ] != map->routemap[ i ] ); + } + } + +/* If any simplification took place, construct a new SwitchMap from these + simplified Mappings. */ + if( simp ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astSwitchMap( sfsmap, sismap, + nroute, (void **) srmap, "", status ); + result = where; + } + +/* Release resources. */ + if( sfsmap ) sfsmap = astAnnul( sfsmap ); + if( sismap ) sismap = astAnnul( sismap ); + if( srmap ) { + for( i = 0; i < nroute; i++ ) srmap[ i ] = astAnnul( srmap[ i ] ); + srmap = astFree( srmap ); + } + } + } + +/* Re-instate the original Invert values for the encapsulated Mappings. */ + if( map->fsmap ) astSetInvert( map->fsmap, fsinv_old ); + if( map->ismap ) astSetInvert( map->ismap, isinv_old ); + if( rinv_old ) { + for( i = 0; i < nroute; i++ ) { + astSetInvert( map->routemap[ i ], rinv_old[ i ] ); + } + rinv_old = astFree( rinv_old ); + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "switchmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* SwitchMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. Also evaluates the second derivative. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + +/* Local Variables: */ + AstSwitchMap *map; + AstMapping *smap; + AstMapping *rmap; + double result; + double sel; + int fsinv; + int rinv; + int nin; + +/* Initialise. */ + result = AST__BAD; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Get a pointer to the SwitchMap structure. */ + map = (AstSwitchMap *) this; + +/* Get a pointer to the effective foward selector Mapping, and its current + invert flag (this takes account of whether the SwtichMap has been + inverted or not). This call resets the selector's invert flag temporarily + back to the value it had when the SwitchMap was created. */ + smap = GetSelector( map, 1, &fsinv, status ); + +/* If the SwitchMap has no forward selector Mapping, return AST__BAD. */ + if( smap ) { + +/* Get the number of inputs */ + nin = astGetNin( smap ); + +/* Transform the supplied position using the selector Mapping. The output + value is the selector value that indicates which route Mapping to use. */ + astTranN( smap, 1, nin, 1, at, 1, 1, 1, &sel ); + +/* Get the index of the route Mapping to use, and check it is valid (if + not, return AST__BAD if not). This takes account of whether the + SwitchMap has been inverted, and also temporarily re-instates the + original value of the route Mapping's Invert flag . */ + rmap = GetRoute( map, sel, &rinv, status ); + if( rmap ) { + +/* Use the astRate method of the route Mapping. */ + result = astRate( rmap, at, ax1, ax2 ); + +/* Reset the Invert flag for the route Mapping. */ + astSetInvert( rmap, rinv ); + } + +/* Reset the Invert flag for the selector Mapping. */ + astSetInvert( smap, fsinv ); + } + +/* Return the result. */ + return result; +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "switchmap.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* SwitchMap method (over-rides the astRemoveRegions method inherited +* from the Mapping class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a SwitchMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel SwitchMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the SwitchMap class invokes the +* astRemoveRegions method on all the component Mappings, and joins +* the results together into a new SwitchMap. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstMapping **temp; /* Array of new route Mappings */ + AstMapping *newfsmap; /* New forward selector Mapping */ + AstMapping *newismap; /* New inverse selector Mapping */ + AstMapping *result; /* Result pointer to return */ + AstSwitchMap *new; /* Pointer to new SwitchMap */ + AstSwitchMap *this; /* Pointer to SwitchMap structure */ + int changed; /* Has any mapping been changed? */ + int i; /* Loop count */ + int nax; /* Number of Frame axes */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the SwitchMap. */ + this = (AstSwitchMap *) this_mapping; + +/* Allocate an array to hold the modified Mapping pointers. */ + temp = astMalloc( sizeof( AstMapping *)*( this->nroute ) ); + if( astOK ) { + +/* Invoke the astRemoveRegions method on all the component Mappings. */ + changed = 0; + for( i = 0; i < this->nroute; i++ ) { + temp[ i ] = astRemoveRegions( this->routemap[ i ] ); + +/* Note if any Mapping was changed. */ + if( temp[ i ] != this->routemap[ i ] ) { + changed = 1; + +/* The implementation of the astRemoveRegions method provided by the + Region class returns a Frame rather than a UnitMap. But we need + Mappings here, not Frames. So if the new Mapping is a Frame, replace + it with an equivalent UnitMap. */ + if( astIsAFrame( temp[ i ] ) ) { + nax = astGetNin( temp[ i ] ); + (void) astAnnul( temp[ i ] ); + temp[ i ] = (AstMapping *) astUnitMap( nax, " ", status ); + } + } + } + +/* And on the other ancillary Mappings */ + if( this->fsmap ) { + newfsmap = astRemoveRegions( this->fsmap ); + if( newfsmap != this->fsmap ) { + changed = 1; + if( astIsAFrame( newfsmap ) ) { + nax = astGetNin( newfsmap ); + (void) astAnnul( newfsmap ); + newfsmap = (AstMapping *) astUnitMap( nax, " ", status ); + } + } + + } else { + newfsmap = NULL; + } + + if( this->ismap ) { + newismap = astRemoveRegions( this->ismap ); + if( newismap != this->ismap ) { + changed = 1; + if( astIsAFrame( newismap ) ) { + nax = astGetNin( newismap ); + (void) astAnnul( newismap ); + newismap = (AstMapping *) astUnitMap( nax, " ", status ); + } + } + + } else { + newismap = NULL; + } + +/* If no component was modified, just return a clone of the supplied + pointer. */ + if( ! changed ) { + result = astClone( this ); + +/* Otherwise, we need to create a new Mapping to return. We take a deep + copy of the supplied SwitchMap and then modify the Mappings so that + we retain any extra information in the supplied SwitchMap. */ + } else { + new = astCopy( this ); + + for( i = 0; i < this->nroute; i++ ) { + (void) astAnnul( new->routemap[ i ] ); + new->routemap[ i ] = astClone( temp[ i ] ); + } + + if( newfsmap ) { + (void) astAnnul( new->fsmap ); + new->fsmap = astClone( newfsmap ); + } + + if( newismap ) { + (void) astAnnul( new->ismap ); + new->ismap = astClone( newismap ); + } + + result = (AstMapping *) new; + } + +/* Free resources. */ + for( i = 0; i < this->nroute; i++ ) { + temp[ i ] = astAnnul( temp[ i ] ); + } + + if( newfsmap ) newfsmap = astAnnul( newfsmap ); + if( newismap ) newismap = astAnnul( newismap ); + } + + temp = astFree( temp ); + +/* Annul the returned Mapping if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +int astSwitchList_( AstSwitchMap *this, int invert, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +*+ +* Name: +* astSwitchList + +* Purpose: +* Extract the selector and route Mappings from a SwitchMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "switchmap.h" +* int astSwitchList( AstSwitchMap *this, int invert, int *nmap, +* AstMapping ***map_list, int **invert_list ) + +* Class Membership: +* SwitchMap member function. + +* Description: +* This function extracts the route and selector Mappings form a +* SwitchMap. + +* Parameters: +* this +* Pointer to the SwitchMap to be decomposed (it is not actually +* modified by this function). +* invert +* The value to which the SwitchMap's Invert attribute is to be +* (notionally) set before performing the decomposition. Normally, +* the value supplied here will be the actual Invert value obtained +* from the SwitchMap (e.g. using astGetInvert). Sometimes, however, +* when a SwitchMap is encapsulated within another structure, that +* structure may retain an Invert value (in order to prevent external +* interference) which should be used instead. +* +* Note that the actual Invert value of the SwitchMap supplied is +* not used (or modified) by this function. +* nmap +* The address of an int in which to return a count of the number of +* individual Mappings in the decomposition. The supplied value is +* ignored. +* map_list +* Address of a pointer to an array of Mapping pointers. The value +* supplied on entry is ignored. On exit, it points at a dynamically +* allocated array containing Mapping pointers ("*nmap" in number) that +* result from the decomposition requested. +* +* The returned Mapping pointers returned will identify the following +* sequence of Mappings; forward selector mapping (or NULL if the +* SwitchMap has no forward selector Mapping), inverse selector +* mapping (or NULL if the SwitchMap has no inverse selector Mapping), +* the route Mappings in the order they were supplied when the +* SwitchMap was constructed. +* +* All the Mapping pointers returned by this function should be +* annulled by the caller, using astAnnul, when no longer +* required. The dynamic array holding these pointers should +* also be freed, using astFree. +* invert_list +* Address of a pointer to an array of int. The value supplied on +* entry is ignored. On exit, it points at a dynamically allocated +* array containing Invert attribute values ("*nmap" in number) that +* result from the decomposition requested. +* +* The returned Invert values returned identify the values which must +* be assigned to the Invert attributes of the corresponding +* Mappings (whose pointers are in the "*map_list" array) before +* they are applied. Note that these values may differ from the +* actual Invert attribute values of these Mappings, which are +* not relevant. +* +* The dynamic array holding these values should be freed by the +* caller, using astFree, when no longer required. + +* Returned Value: +* The number of route Mappings stored in the SwitchMap. + +* Notes: +* - It is unspecified to what extent the original SwitchMap and the +* individual (decomposed) Mappings are inter-dependent. Consequently, +* the individual Mappings cannot be modified without risking +* modification of the original SwitchMap. +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then the *nmap value, the +* list of Mapping pointers and the list of Invert values will all +* be returned unchanged. +*- +*/ + +/* Local Variables: */ + AstMapping *map; /* Pointer to Mapping to return */ + int inv; /* Original Invert flag for Mapping */ + int i; /* Route Mapping index */ + int oldinv; /* Original Invert flag for SwitchMap */ + int result; /* Returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Store the numbe of route Mappings */ + result = this->nroute; + *nmap = result + 2; + +/* Allocate the required arrays. */ + *map_list = astMalloc( sizeof( AstMapping * )*(size_t) *nmap ); + *invert_list = astMalloc( sizeof( int )*(size_t) *nmap ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Temporaily set the requested Invert flag for the SwitchMap. */ + oldinv = astGetInvert( this ); + astSetInvert( this, invert ); + +/* Get the forward selector Mapping. */ + map = GetSelector( this, 1, &inv, status ); + +/* If the SwitchMap has a forward selector Mapping, return a clone of the + Mapping pointer, and the invert flag to be used with it, then + re-instate the original invert flag value (which was modified by + GetSelector). */ + if( map ) { + ( *map_list )[ 0 ] = astClone( map ); + ( *invert_list )[ 0 ] = astGetInvert( map ); + astSetInvert( map, inv ); + +/* If the SwitchMap does not has a forward selector Mapping, return a + NULL pointer. */ + } else { + ( *map_list )[ 0 ] = NULL; + ( *invert_list )[ 0 ] = 0; + } + +/* Likewise, get and return the inverse selector Mapping.*/ + map = GetSelector( this, 0, &inv, status ); + if( map ) { + ( *map_list )[ 1 ] = astClone( map ); + ( *invert_list )[ 1 ] = astGetInvert( map ); + astSetInvert( map, inv ); + } else { + ( *map_list )[ 1 ] = NULL; + ( *invert_list )[ 1 ] = 0; + } + +/* Loop round all route Mappings. */ + for( i = 0; i < result; i++ ){ + +/* Get the next route Mapping. */ + map = GetRoute( this, (double) i + 1.0, &inv, status ); + +/* If the SwitchMap has a route Mapping for the current selector value, + return a clone of the Mapping pointer, and the invert flag to be used + with it, then re-instate the original invert flag value (which was + modified by GetRoute). */ + if( map ) { + ( *map_list )[ i + 2 ] = astClone( map ); + ( *invert_list )[ i + 2 ] = astGetInvert( map ); + astSetInvert( map, inv ); + +/* If the SwitchMap does not has a route Mapping for the current selector + value, return a NULL pointer. */ + } else { + ( *map_list )[ i + 2 ] = NULL; + ( *invert_list )[ i + 2 ] = 0; + } + + } + +/* Re-instate the original Ivert flag for the SwitchMap. */ + astSetInvert( this, oldinv ); + + } + +/* If an error has occurred, free the returned arrays. */ + if( !astOK ) { + *map_list = astFree( *map_list ); + *invert_list= astFree( *invert_list ); + result= 0; + *nmap = 0; + } + +/* Return the result */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a SwitchMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "switchmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* SwitchMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a SwitchMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Mapping. +* This implies applying each of the SwitchMap's component Mappings in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the SwitchMap. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the SwitchMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstMapping *rmap; + AstMapping *selmap; + AstPointSet *ps1; + AstPointSet *ps1a; + AstPointSet *ps2; + AstPointSet *ps2a; + AstPointSet *result; + AstPointSet *selps; + AstSwitchMap *map; + double **in_ptr; + double **out_ptr; + double **ptr1; + double **ptr2; + double **sel_ptr; + double *outv; + double *sel; + int *popmap; + int iroute; + int ipoint; + int j; + int k; + int maxpop; + int ncin; + int ncout; + int npoint; + int nroute; + int rindex; + int rinv; + int selinv; + int totpop; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the SwitchMap. */ + map = (AstSwitchMap *) this; + +/* Apply the parent Mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We now extend the parent astTransform method by applying the component + Mappings of the SwitchMap to generate the output coordinate values. */ + +/* Get the number of input and output coords. */ + if( forward ) { + ncin = astGetNin( this ); + ncout = astGetNout( this ); + } else { + ncin = astGetNout( this ); + ncout = astGetNin( this ); + } + +/* Get the appropriate selector Mapping. */ + selmap = GetSelector( map, forward, &selinv, status ); + +/* Transform the supplied positions using the above selector Mapping. */ + selps = astTransform( selmap, in, forward, NULL ); + +/* Get a pointer to the array holding the selector value. */ + sel_ptr = astGetPoints( selps ); + +/* Get a pointer to the array holding the input values. */ + in_ptr = astGetPoints( in ); + +/* Get a pointer to the array in which to store the results, and the total + number of points being transformed. */ + out_ptr = astGetPoints( result ); + npoint = astGetNpoint( result ); + +/* We now count how many positions are to be tranformed by each of the + route Mappings. */ + nroute = map->nroute; + popmap = astMalloc( sizeof( int )*nroute ); + if( astOK ) { + for( iroute = 0; iroute < nroute; iroute++ ) popmap[ iroute ] = 0; + + sel = sel_ptr[ 0 ]; + for( ipoint = 0; ipoint < npoint; ipoint++,sel++ ) { + if( *sel != AST__BAD ) { + rindex = (int)( *sel + 0.5 ) - 1; + if( rindex >= 0 && rindex < nroute ) ( popmap[ rindex ] )++; + } + } + +/* Find the number of points transformed by the most popular route Mapping. + Also find the total number of points transformed by any route Mapping. */ + totpop = 0; + maxpop = 0; + for( iroute = 0; iroute < nroute; iroute++ ) { + if( popmap[ iroute ] > maxpop ) maxpop = popmap[ iroute ]; + totpop += popmap[ iroute ]; + } + if( maxpop == 0 ) maxpop = 1; + +/* If some of the points are not transformed by any route Mapping. + Initialise the whole output array to hold AST__BAD at every point. */ + if( totpop < npoint ) { + for( j = 0; j < ncout; j++ ) { + outv = out_ptr[ j ]; + for( ipoint = 0; ipoint < npoint; ipoint++ ) *(outv++) = AST__BAD; + } + } + +/* Create a PointSet large enough to hold all the supplied positions + which are to be transformed by the most popular route Mapping. */ + ps1 = astPointSet( maxpop, ncin, "", status ); + ptr1 = astGetPoints( ps1 ); + +/* Create a PointSet large enough to hold all the output positions + created by the most popular route Mapping. */ + ps2 = astPointSet( maxpop, ncout, "", status ); + ptr2 = astGetPoints( ps2 ); + if( astOK ) { + +/* Loop round each route Mapping which is used by at least 1 point. */ + for( iroute = 0; iroute < nroute; iroute++ ) { + if( popmap[ iroute ] >0 ) { + rmap = GetRoute( map, (double)( iroute + 1 ), &rinv, status ); + +/* Construct two PointSets of the correct size to hold the input and + output points to be processed with the current route Mapping. We + re-use the memory allocated for the largest route Mapping's PointSet. */ + if( popmap[ iroute ] != maxpop ) { + ps1a = astPointSet( popmap[ iroute ], ncin, "", status ); + astSetPoints( ps1a, ptr1 ); + ps2a = astPointSet( popmap[ iroute ], ncout, "", status ); + astSetPoints( ps2a, ptr2 ); + } else { + ps1a = astClone( ps1 ); + ps2a = astClone( ps2 ); + } + +/* Fill the input PointSet with the input positions which are to be + transformed using the current route Mapping. */ + sel = sel_ptr[ 0 ]; + k = 0; + for( ipoint = 0; ipoint < npoint; ipoint++,sel++ ) { + if( *sel != AST__BAD ) { + rindex = (int)( *sel + 0.5 ) - 1; + if( rindex == iroute ) { + for( j = 0; j < ncin; j++ ) { + ptr1[ j ][ k ] = in_ptr[ j ][ ipoint ]; + } + k++; + } + } + } + +/* Use the route Mapping to transform this PointSet. */ + (void) astTransform( rmap, ps1a, forward, ps2a ); + +/* Copy the axis values from the resulting PointSet back into the results + array. */ + sel = sel_ptr[ 0 ]; + k = 0; + for( ipoint = 0; ipoint < npoint; ipoint++,sel++ ) { + if( *sel != AST__BAD ) { + rindex = (int)( *sel + 0.5 ) - 1; + if( rindex == iroute ) { + for( j = 0; j < ncout; j++ ) { + out_ptr[ j ][ ipoint ] = ptr2[ j ][ k ]; + } + k++; + } + } + } + +/* Free resources. */ + ps1a = astAnnul( ps1a ); + ps2a = astAnnul( ps2a ); + +/* Re-instate the Invert flag for the route Mapping. */ + astSetInvert( rmap, rinv ); + } + } + } + +/* Free resources. */ + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + } + + selps = astAnnul( selps ); + popmap = astFree( popmap ); + +/* Re-instate the Invert flag of the selector Mapping. */ + astSetInvert( selmap, selinv ); + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for SwitchMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for SwitchMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Mappings within the SwitchMap. +*/ + +/* Local Variables: */ + AstSwitchMap *in; /* Pointer to input SwitchMap */ + AstSwitchMap *out; /* Pointer to output SwitchMap */ + int i; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output SwitchMaps. */ + in = (AstSwitchMap *) objin; + out = (AstSwitchMap *) objout; + +/* For safety, start by clearing any references to the input component + Mappings,etc, from the output SwitchMap. */ + out->fsmap = NULL; + out->ismap = NULL; + out->routemap = NULL; + out->routeinv = NULL; + +/* Make copies of these Mappings, etc, and store pointers to them in the output + SwitchMap structure. */ + if( in->fsmap ) out->fsmap = astCopy( in->fsmap ); + if( in->ismap ) out->ismap = astCopy( in->ismap ); + + out->routemap = astMalloc( sizeof( AstMapping * )*( in->nroute ) ); + out->routeinv = astMalloc( sizeof( int )*( in->nroute ) ); + if( astOK ) { + for( i = 0; i < in->nroute; i++ ) { + out->routemap[ i ] = astCopy( in->routemap[ i ] ); + out->routeinv[ i ] = in->routeinv[ i ]; + } + } + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for SwitchMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for SwitchMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstSwitchMap *this; /* Pointer to SwitchMap */ + int i; + +/* Obtain a pointer to the SwitchMap structure. */ + this = (AstSwitchMap *) obj; + +/* Free dynamically allocated resources. */ + if( this->fsmap ) this->fsmap = astAnnul( this->fsmap ); + if( this->ismap ) this->ismap = astAnnul( this->ismap ); + for( i = 0; i < this->nroute; i++ ) { + this->routemap[ i ] = astAnnul( this->routemap[ i ] ); + } + this->routemap = astFree( this->routemap ); + this->routeinv = astFree( this->routeinv ); + +/* Clear the remaining SwitchMap variables. */ + this->nroute = 0; + this->fsinv = 0; + this->isinv = 0; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for SwitchMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the SwitchMap class to an output Channel. + +* Parameters: +* this +* Pointer to the SwitchMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstSwitchMap *this; + int ival; + int set; + int i; + char buf[ 20 ]; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the SwitchMap structure. */ + this = (AstSwitchMap *) this_object; + +/* Write out values representing the instance variables for the SwitchMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Forward selector Mapping */ +/* ------------------------ */ + if( this->fsmap ) { + astWriteObject( channel, "FSMap", 1, 1, this->fsmap, + "Forward selector Mapping" ); + +/* Forward selector Invert flag. */ +/* ----------------------------- */ + ival = this->fsinv; + set = ( ival != 0 ); + astWriteInt( channel, "FSInv", set, 0, ival, + ival ? "Fwd selector used in inverse direction" : + "Fwd selector used in forward direction" ); + } + + +/* Inverse selector Mapping */ +/* ------------------------ */ + if( this->ismap ) { + astWriteObject( channel, "ISMap", 1, 1, this->ismap, + "Inverse selector Mapping" ); + +/* Forward selector Invert flag. */ +/* ----------------------------- */ + ival = this->isinv; + set = ( ival != 0 ); + astWriteInt( channel, "ISInv", set, 0, ival, + ival ? "Inv selector used in inverse direction" : + "Inv selector used in forward direction" ); + } + +/* Loop to dump each route Mapping and its invert flag. */ +/* ---------------------------------------------------- */ + for( i = 0; i < this->nroute; i++ ) { + sprintf( buf, "RMap%d", i + 1 ); + astWriteObject( channel, buf, 1, 1, this->routemap[ i ], + "Route Mapping" ); + + ival = this->routeinv[ i ]; + set = ( ival != 0 ); + sprintf( buf, "RInv%d", i + 1 ); + astWriteInt( channel, buf, set, 0, ival, + ival ? "Route Mapping used in inverse direction" : + "Route Mapping used in forward direction" ); + } + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsASwitchMap and astCheckSwitchMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(SwitchMap,Mapping) +astMAKE_CHECK(SwitchMap) + +AstSwitchMap *astSwitchMap_( void *fsmap_void, void *ismap_void, int nroute, + void **routemaps_void, const char *options, int *status, ...) { +/* +*+ +* Name: +* astSwitchMap + +* Purpose: +* Create a SwitchMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "switchmap.h" +* AstSwitchMap *astSwitchMap( AstMapping *fsmap, AstMapping *ismap, +* int nroute, AstMapping **routemaps, +* const char *options, ... ) + +* Class Membership: +* SwitchMap constructor. + +* Description: +* This function creates a new SwitchMap and optionally initialises its +* attributes. + +* Parameters: +* fsmap +* Pointer to the forward selector Mapping +* ismap +* Pointer to the inverse selector Mapping +* nroute +* The number of route Mappings. +* routemaps +* An array of pointers to the route Mappings. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new SwitchMap. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new SwitchMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic SwitchMap constructor which is +* available via the protected interface to the SwitchMap class. A +* public interface is provided by the astSwitchMapId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "map1" and "map2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSwitchMap *new; /* Pointer to new SwitchMap */ + AstMapping *fsmap; /* Pointer to fwd selector Mapping */ + AstMapping *ismap; /* Pointer to inv selector Mapping */ + AstMapping **routemaps; /* Array of route Mapping pointers */ + int i; /* Route Mappings index */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Report an error if no route Mappings have been supplied. */ + if( nroute <= 0 ) astError( AST__BDPAR, "astSwitchMap(SwitchMap): " + "Bad number of route Mappings (%d) specified.", status, + nroute ); + +/* Otherwise create an array to hold the route Mapping pointers. */ + routemaps = astMalloc( sizeof( AstMapping * )*nroute ); + +/* Obtain and validate pointers to the Mapping structures provided. */ + if( astOK ) { + fsmap = fsmap_void ? astCheckMapping( fsmap_void ) : NULL; + ismap = ismap_void ? astCheckMapping( ismap_void ) : NULL; + for( i = 0; i < nroute; i++ ) { + routemaps[ i ] = astCheckMapping( routemaps_void[ i ] ); + } + } + + if ( astOK ) { + +/* Initialise the SwitchMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSwitchMap( NULL, sizeof( AstSwitchMap ), !class_init, &class_vtab, + "SwitchMap", fsmap, ismap, nroute, routemaps ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new SwitchMap's + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free memory used to hold the route Mapping pointers. */ + routemaps = astFree( routemaps ); + +/* Return a pointer to the new SwitchMap. */ + return new; +} + +AstSwitchMap *astSwitchMapId_( void *fsmap_void, void *ismap_void, int nroute, + void **routemaps_void, const char *options, ... ) { +/* +*++ +* Name: +c astSwitchMap +f AST_SWITCHMAP + +* Purpose: +* Create a SwitchMap. + +* Type: +* Public function. + +* Synopsis: +c #include "switchmap.h" +c AstSwitchMap *astSwitchMap( AstMapping *fsmap, AstMapping *ismap, +c int nroute, AstMapping *routemaps[], +c const char *options, ... ) +f RESULT = AST_SWITCHMAP( FSMAP, ISMAP, NROUTE, ROUTEMAPS, OPTIONS, +f STATUS ) + +* Class Membership: +* SwitchMap constructor. + +* Description: +* This function creates a new SwitchMap and optionally initialises +* its attributes. +* +* A SwitchMap is a Mapping which represents a set of alternate +* Mappings, each of which is used to transform positions within a +* particular region of the input or output coordinate system of the +* SwitchMap. +* +* A SwitchMap can encapsulate any number of Mappings, but they must +* all have the same number of inputs (Nin attribute value) and the +* same number of outputs (Nout attribute value). The SwitchMap itself +* inherits these same values for its Nin and Nout attributes. Each of +* these Mappings represents a "route" through the switch, and are +* referred to as "route" Mappings below. Each route Mapping transforms +* positions between the input and output coordinate space of the entire +* SwitchMap, but only one Mapping will be used to transform any given +* position. The selection of the appropriate route Mapping to use with +* any given input position is made by another Mapping, called the +* "selector" Mapping. Each SwitchMap encapsulates two selector +* Mappings in addition to its route Mappings; one for use with the +* SwitchMap's forward transformation (called the "forward selector +* Mapping"), and one for use with the SwitchMap's inverse transformation +* (called the "inverse selector Mapping"). The forward selector Mapping +* must have the same number of inputs as the route Mappings, but +* should have only one output. Likewise, the inverse selector Mapping +* must have the same number of outputs as the route Mappings, but +* should have only one input. +* +* When the SwitchMap is used to transform a position in the forward +* direction (from input to output), each supplied input position is +* first transformed by the forward transformation of the forward selector +* Mapping. This produces a single output value for each input position +* referred to as the selector value. The nearest integer to the selector +* value is found, and is used to index the array of route Mappings (the +* first supplied route Mapping has index 1, the second route Mapping has +* index 2, etc). If the nearest integer to the selector value is less +* than 1 or greater than the number of route Mappings, then the SwitchMap +* output position is set to a value of AST__BAD on every axis. Otherwise, +* the forward transformation of the selected route Mapping is used to +* transform the supplied input position to produce the SwitchMap output +* position. +* +* When the SwitchMap is used to transform a position in the inverse +* direction (from "output" to "input"), each supplied "output" position +* is first transformed by the inverse transformation of the inverse +* selector Mapping. This produces a selector value for each "output" +* position. Again, the nearest integer to the selector value is found, +* and is used to index the array of route Mappings. If this selector +* index value is within the bounds of the array of route Mappings, then +* the inverse transformation of the selected route Mapping is used to +* transform the supplied "output" position to produce the SwitchMap +* "input" position. If the selector index value is outside the bounds +* of the array of route Mappings, then the SwitchMap "input" position is +* set to a value of AST__BAD on every axis. +* +* In practice, appropriate selector Mappings should be chosen to +* associate a different route Mapping with each region of coordinate +* space. Note that the SelectorMap class of Mapping is particularly +* appropriate for this purpose. +* +* If a compound Mapping contains a SwitchMap in series with its own +* inverse, the combination of the two adjacent SwitchMaps will be +* replaced by a UnitMap when the compound Mapping is simplified using +c astSimplify. +f AST_SIMPLIFY. + +* Parameters: +c fsmap +f FSMAP = INTEGER (Given) +* Pointer to the forward selector Mapping. This must have a +* defined forward transformation, but need not have a defined +* inverse transformation. It must have one output, and the number of +* inputs must match the number of inputs of each of the supplied +* route Mappings. +c NULL +f AST__NULL +* may be supplied, in which case the SwitchMap will have an undefined +* forward Mapping. +c ismap +f ISMAP = INTEGER (Given) +* Pointer to the inverse selector Mapping. This must have a +* defined inverse transformation, but need not have a defined +* forward transformation. It must have one input, and the number of +* outputs must match the number of outputs of each of the supplied +* route Mappings. +c NULL +f AST__NULL +* may be supplied, in which case the SwitchMap will have an undefined +* inverse Mapping. +c nroute +f NROUTE = INTEGER (Given) +* The number of supplied route Mappings. +c routemaps +f ROUTEMAPS( NROUTE ) = INTEGER (Given) +* An array of pointers to the route Mappings. All the supplied +* route Mappings must have common values for the Nin and Nout +* attributes, and these values define the number of inputs and +* outputs of the SwitchMap. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new SwitchMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new SwitchMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSwitchMap() +f AST_SWITCHMAP = INTEGER +* A pointer to the new SwitchMap. + +* Notes: +c - Note that the component Mappings supplied are not copied by +c astSwitchMap (the new SwitchMap simply retains a reference to +c them). They may continue to be used for other purposes, but +c should not be deleted. If a SwitchMap containing a copy of its +c component Mappings is required, then a copy of the SwitchMap should +c be made using astCopy. +f - Note that the component Mappings supplied are not copied by +f AST_SWITCHMAP (the new SwitchMap simply retains a reference to +f them). They may continue to be used for other purposes, but +f should not be deleted. If a SwitchMap containing a copy of its +f component Mappings is required, then a copy of the SwitchMap should +f be made using AST_COPY. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astSwitchMap constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astSwitchMap_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "map1" and "map2" parameters +* are of type (void *) and are converted from an ID value to a +* pointer and validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astSwitchMap_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSwitchMap *new; /* Pointer to new SwitchMap */ + AstMapping *fsmap; /* Pointer to fwd selector Mapping */ + AstMapping *ismap; /* Pointer to inv selector Mapping */ + AstMapping **routemaps; /* Array of route Mapping pointers */ + int i; /* Route Mappings index */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Report an error if no route Mappings have been supplied. */ + if( nroute <= 0 ) astError( AST__BDPAR, "astSwitchMap(SwitchMap): " + " Bad number of route Mappings (%d) specified.", status, + nroute ); + +/* Otherwise create an array to hold the route Mapping pointers. */ + routemaps = astMalloc( sizeof( AstMapping * )*nroute ); + +/* Obtain and validate pointers to the Mapping structures provided. */ + if( astOK ) { + fsmap = fsmap_void ? astCheckMapping( astMakePointer(fsmap_void) ) : NULL; + ismap = ismap_void ? astCheckMapping( astMakePointer(ismap_void) ) : NULL; + for( i = 0; i < nroute; i++ ) { + routemaps[ i ] = astVerifyMapping( astMakePointer(routemaps_void[ i ]) ); + } + } + + if ( astOK ) { + +/* Initialise the SwitchMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitSwitchMap( NULL, sizeof( AstSwitchMap ), !class_init, &class_vtab, + "SwitchMap", fsmap, ismap, nroute, routemaps ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new SwitchMap's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Free memory used to hold the route Mapping pointers. */ + routemaps = astFree( routemaps ); + +/* Return an ID value for the new SwitchMap. */ + return astMakeId( new ); +} + +AstSwitchMap *astInitSwitchMap_( void *mem, size_t size, int init, + AstSwitchMapVtab *vtab, const char *name, + AstMapping *fsmap, AstMapping *ismap, + int nroute, AstMapping **routemaps, int *status ) { +/* +*+ +* Name: +* astInitSwitchMap + +* Purpose: +* Initialise a SwitchMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "switchmap.h" +* AstSwitchMap *astInitSwitchMap( void *mem, size_t size, int init, +* AstSwitchMapVtab *vtab, const char *name, +* AstMapping *fsmap, AstMapping *ismap, +* int nroute, AstMapping **routemaps ) + +* Class Membership: +* SwitchMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new SwitchMap object. It allocates memory (if necessary) to +* accommodate the SwitchMap plus any additional data associated with the +* derived class. It then initialises a SwitchMap structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a SwitchMap at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the SwitchMap is to be initialised. +* This must be of sufficient size to accommodate the SwitchMap data +* (sizeof(SwitchMap)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the SwitchMap (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* SwitchMap structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the SwitchMap's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new SwitchMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* fsmap +* Pointer to the forward selector Mapping. +* ismap +* Pointer to the inverse selector Mapping. +* nroute +* The number of route Mappings supplied. +* routemaps +* An array holdiong pointers to the route Mappings. + +* Returned Value: +* A pointer to the new SwitchMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstSwitchMap *new; /* Pointer to new SwitchMap */ + int i; /* Loop count */ + int nin; /* No. input coordinates for SwitchMap */ + int nout; /* No. output coordinates for SwitchMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitSwitchMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check that all route Mappings have common values for Nin and Nout.*/ + nin = astGetNin( routemaps[ 0 ] ); + nout = astGetNout( routemaps[ 0 ] ); + for( i = 1; i < nroute; i++ ) { + if( nin != astGetNin( routemaps[ i ] ) ){ + if( astOK ) { + astError( AST__BADNI, "astInitSwitchMap(%s): Route Mapping " + "number %d has %d input(s) but the first route " + "Mapping has %d input(s).", status, name, i + 1, + astGetNin( routemaps[ i ] ), nin ); + } + + } else if( nout != astGetNout( routemaps[ i ] ) ){ + if( astOK ) { + astError( AST__BADNO, "astInitSwitchMap(%s): Route Mapping " + "number %d has %d output(s) but the first route " + "Mapping has %d output(s).", status, name, i + 1, + astGetNin( routemaps[ i ] ), nin ); + } + } + } + +/* If supplied, report an error if fsmap has no forward transformation, + or if it has an incorrect number of inputs or output. */ + if( fsmap && astOK ) { + if( !astGetTranForward( fsmap ) ) { + astError( AST__INTRD, "astInitSwitchMap(%s): The forward selector Mapping " + "is not able to transform coordinates in the forward direction.", status, + name ); + + } else if( astGetNin( fsmap ) != nin ){ + astError( AST__BADNI, "astInitSwitchMap(%s): The forward selector " + "Mapping has %d input(s) but the SwitchMap has %d " + "input(s).", status, name, astGetNin( fsmap ), nin ); + + } else if( astGetNout( fsmap ) != 1 ){ + astError( AST__BADNO, "astInitSwitchMap(%s): The forward selector " + "Mapping has %d outputs but should only have 1.", status, name, + astGetNout( fsmap ) ); + } + } + +/* If supplied, report an error if ismap has no inverse transformation, + or if it has an incorrect number of inputs or outputs. */ + if( ismap && astOK ) { + if( !astGetTranInverse( ismap ) ) { + astError( AST__INTRD, "astInitSwitchMap(%s): The inverse selector Mapping " + "is not able to transform coordinates in the inverse direction.", status, + name ); + } else if( nout != astGetNout( ismap ) ){ + astError( AST__BADNO, "astInitSwitchMap(%s): The inverse selector " + "Mapping has %d output(s) but the SwitchMap has %d " + "output(s).", status, name, astGetNout( ismap ), nout ); + + } else if( astGetNin( ismap ) != 1 ){ + astError( AST__BADNI, "astInitSwitchMap(%s): The inverse selector " + "Mapping has %d inputs but should only have 1.", status, name, + astGetNin( ismap ) ); + + } + } + +/* Report an error if neither ismap nor fsmap were supplied. */ + if( !fsmap && !ismap && astOK ) { + astError( AST__INTRD, "astInitSwitchMap(%s): No selector Mappings " + "supplied.", status, name ); + } + +/* Initialise a Mapping structure (the parent class) as the first component + within the SwitchMap structure, allocating memory if necessary. Specify + the number of input and output coordinates and in which directions the + Mapping should be defined. */ + if ( astOK ) { + new = (AstSwitchMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nout, + ( fsmap != NULL ), + ( ismap != NULL ) ); + if ( astOK ) { + +/* Initialise the SwitchMap data. */ +/* --------------------------- */ +/* Store pointers to the selector Mappings. */ + new->fsmap = fsmap ? astClone( fsmap ) : NULL; + new->ismap = ismap ? astClone( ismap ) : NULL; + +/* Save the initial values of the inversion flags for these Mappings. */ + new->fsinv = fsmap ? astGetInvert( fsmap ) : 0; + new->isinv = ismap ? astGetInvert( ismap ) : 0; + +/* Create arrays for the route Mappings. */ + new->routemap = astMalloc( sizeof( AstMapping * )*nroute ); + new->routeinv = astMalloc( sizeof( int )*nroute ); + +/* Store pointers to the route Mappings and their invert flags. */ + if( astOK ) { + new->nroute = nroute; + for( i = 0; i < nroute; i++ ) { + new->routemap[ i ] = astClone( routemaps[ i ] ); + new->routeinv[ i ] = astGetInvert( routemaps[ i ] ); + } + } else { + new->nroute = 0; + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstSwitchMap *astLoadSwitchMap_( void *mem, size_t size, + AstSwitchMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadSwitchMap + +* Purpose: +* Load a SwitchMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "switchmap.h" +* AstSwitchMap *astLoadSwitchMap( void *mem, size_t size, +* AstSwitchMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* SwitchMap loader. + +* Description: +* This function is provided to load a new SwitchMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* SwitchMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a SwitchMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the SwitchMap is to be +* loaded. This must be of sufficient size to accommodate the +* SwitchMap data (sizeof(SwitchMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the SwitchMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the SwitchMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstSwitchMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new SwitchMap. If this is NULL, a pointer to +* the (static) virtual function table for the SwitchMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "SwitchMap" is used instead. + +* Returned Value: +* A pointer to the new SwitchMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstSwitchMap *new; + AstMapping *rmap; + int i; + char buf[ 20 ]; + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this SwitchMap. In this case the + SwitchMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstSwitchMap ); + vtab = &class_vtab; + name = "SwitchMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitSwitchMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built SwitchMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "SwitchMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Forward Selector Mapping and its Invert flag. */ +/* --------------------------------------------- */ + new->fsmap = astReadObject( channel, "fsmap", NULL ); + new->fsinv = astReadInt( channel, "fsinv", 0 ); + new->fsinv = ( new->fsinv != 0 ); + +/* Inverse Selector Mapping and its Invert flag. */ +/* --------------------------------------------- */ + new->ismap = astReadObject( channel, "ismap", NULL ); + new->isinv = astReadInt( channel, "isinv", new->fsinv ); + new->isinv = ( new->isinv != 0 ); + +/* Loop to load each route Mapping and its invert flag. */ +/* ---------------------------------------------------- */ + new->routemap = NULL; + new->routeinv = NULL; + i = 0; + while( astOK ) { + sprintf( buf, "rmap%d", i + 1 ); + rmap = astReadObject( channel, buf, NULL ); + if( rmap ) { + new->routemap = astGrow( new->routemap, i + 1, sizeof( AstMapping *) ); + new->routeinv = astGrow( new->routeinv, i + 1, sizeof( int ) ); + if( astOK ) { + new->routemap[ i ] = rmap; + sprintf( buf, "rinv%d", i + 1 ); + new->routeinv[ i ] = astReadInt( channel, buf, 0 ); + new->routeinv[ i ] = ( new->routeinv[ i ] != 0 ); + i++; + } + } else { + break; + } + } + +/* Number of route Mappings. */ + new->nroute = i; + +/* If an error occurred, clean up by deleting the new SwitchMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new SwitchMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* None. */ + + + + diff --git a/switchmap.h b/switchmap.h new file mode 100644 index 0000000..40c60a9 --- /dev/null +++ b/switchmap.h @@ -0,0 +1,289 @@ +#if !defined( SWITCHMAP_INCLUDED ) /* Include this file only once */ +#define SWITCHMAP_INCLUDED +/* +*+ +* Name: +* switchmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the SwitchMap class. + +* Invocation: +* #include "switchmap.h" + +* Description: +* This include file defines the interface to the SwitchMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The SwitchMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astMapMerge +* Merge a SwitchMap within a sequence of Mappings. +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsASwitchMap +* Test class membership. +* astSwitchMap +* Create a SwitchMap. +* +* Protected: +* astCheckSwitchMap +* Validate class membership. +* astInitSwitchMap +* Initialise a SwitchMap. +* astInitSwitchMapVtab +* Initialise the virtual function table for the SwitchMap class. +* astLoadSwitchMap +* Load a SwitchMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstSwitchMap +* SwitchMap object type. +* +* Protected: +* AstSwitchMapVtab +* SwitchMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 13-MAR-2006 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* SwitchMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstSwitchMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstMapping *fsmap; /* Pointer to forward selector Mapping */ + AstMapping *ismap; /* Pointer to inverse selector Mapping */ + int fsinv; /* Inversion flag for forward selector Mapping */ + int isinv; /* Inversion flag for inverse selector Mapping */ + int nroute; /* The number of route Mappings in the SwitchMap */ + AstMapping **routemap; /* Array of route Mapping pointers */ + int *routeinv; /* Array of inversion flags for route Mappings */ +} AstSwitchMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstSwitchMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +/* None. */ +} AstSwitchMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstSwitchMapGlobals { + AstSwitchMapVtab Class_Vtab; + int Class_Init; +} AstSwitchMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitSwitchMapGlobals_( AstSwitchMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(SwitchMap) /* Check class membership */ +astPROTO_ISA(SwitchMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstSwitchMap *astSwitchMap_( void *, void *, int, void **, const char *, int *, ...); +#else +AstSwitchMap *astSwitchMapId_( void *, void *, int, void **, const char *, ... )__attribute__((format(printf,5,6))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstSwitchMap *astInitSwitchMap_( void *, size_t, int, AstSwitchMapVtab *, + const char *, AstMapping *, AstMapping *, + int, AstMapping **, int * ); + +/* Vtab initialiser. */ +void astInitSwitchMapVtab_( AstSwitchMapVtab *, const char *, int * ); + +/* Loader. */ +AstSwitchMap *astLoadSwitchMap_( void *, size_t, AstSwitchMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +#if defined(astCLASS) /* Protected */ + +int astSwitchList_( AstSwitchMap *, int, int *, AstMapping ***, int **, int * ); + +#endif + + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckSwitchMap(this) astINVOKE_CHECK(SwitchMap,this,0) +#define astVerifySwitchMap(this) astINVOKE_CHECK(SwitchMap,this,1) + +/* Test class membership. */ +#define astIsASwitchMap(this) astINVOKE_ISA(SwitchMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astSwitchMap astINVOKE(F,astSwitchMap_) +#else +#define astSwitchMap astINVOKE(F,astSwitchMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitSwitchMap(mem,size,init,vtab,name,fsmap,ismap,nroute,routemaps) \ +astINVOKE(O,astInitSwitchMap_(mem,size,init,vtab,name,astCheckMapping(fsmap),\ + astCheckMapping(ismap),nroute,routemaps,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitSwitchMapVtab(vtab,name) astINVOKE(V,astInitSwitchMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadSwitchMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadSwitchMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) + +#define astSwitchList astSwitchList_ + +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckSwitchMap to validate SwitchMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +/* None. */ +#endif + + + + + diff --git a/table.c b/table.c new file mode 100644 index 0000000..0d283bc --- /dev/null +++ b/table.c @@ -0,0 +1,5246 @@ +/* +*class++ +* Name: +* Table + +* Purpose: +* A 2-dimensional table of values. + +* Constructor Function: +c astTable +f AST_TABLE + +* Description: +* The Table class is a type of KeyMap that represents a two-dimensional +* table of values. The +c astMapGet... and astMapPut... +f AST_MAPGET... and AST_MAPPUT... +* methods provided by the KeyMap class should be used for storing and +* retrieving values from individual cells within a Table. Each entry +* in the KeyMap represents a single cell of the table and has an +* associated key of the form "
    (i)" where "" is the +* upper-case name of a table column and "i" is the row index (the +* first row is row 1). Keys of this form should always be used when +* using KeyMap methods to access entries within a Table. +* +* Columns must be declared using the +c astAddColumn +f AST_ADDCOLUMN +* method before values can be stored within them. This also fixes the +* type and shape of the values that may be stored in any cell of the +* column. Cells may contain scalar or vector values of any data type +* supported by the KeyMap class. Multi-dimensional arrays may also be +* stored, but these must be vectorised when storing and retrieving +* them within a table cell. All cells within a single column must +* have the same type and shape, as specified when the column is added +* to the Table. +* +* Tables may have parameters that describe global properties of the +* entire table. These are stored as entries in the parent KeyMap and +* can be access using the get and set method of the KeyMap class. +* However, parameters must be declared using the +c astAddParameter +f AST_ADDPARAMETER +* method before being accessed. +* +* Note - since accessing entries within a KeyMap is a relatively slow +* process, it is not recommended to use the Table class to store +* very large tables. + +* Inheritance: +* The Table class inherits from the KeyMap class. + +* Attributes: +* In addition to those attributes common to all KeyMaps, every +* Table also has the following attributes: +* +* - ColumnLenC(column): The largest string length of any value in a column +* - ColumnLength(column): The number of elements in each value in a column +* - ColumnNdim(column): The number of axes spanned by each value in a column +* - ColumnType(column): The data type of each value in a column +* - ColumnUnit(column): The unit string describing each value in a column +* - Ncolumn: The number of columns currently in the Table +* - Nrow: The number of rows currently in the Table +* - Nparameter: The number of global parameters currently in the Table + +* Functions: +c In addition to those functions applicable to all KeyMaps, the +c following functions may also be applied to all Tables: +f In addition to those routines applicable to all KeyMaps, the +f following routines may also be applied to all Tables: +* +c - astAddColumn: Add a new column definition to a Table +c - astAddParameter: Add a new global parameter definition to a Table +c - astColumnName: Return the name of the column with a given index +c - astColumnShape: Return the shape of the values in a named column +c - astHasColumn: Checks if a column exists in a Table +c - astHasParameter: Checks if a global parameter exists in a Table +c - astParameterName: Return the name of the parameter with a given index +c - astPurgeRows: Remove all empty rows from a Table +c - astRemoveColumn: Remove a column from a Table +c - astRemoveParameter: Remove a global parameter from a Table +c - astRemoveRow: Remove a row from a Table +f - AST_ADDCOLUMN: Add a new column definition to a Table +f - AST_ADDPARAMETER: Add a new global parameter definition to a Table +f - AST_COLUMNNAME: Return the name of the column with a given index +f - AST_COLUMNSHAPE: Return the shape of the values in a named column +f - AST_HASCOLUMN: Checks if a column exists in a Table +f - AST_HASPARAMETER: Checks if a global parameter exists in a Table +f - AST_PARAMETERNAME: Return the name of the parameter with a given index +f - AST_PURGEROWS: Remove all empty rows from a Table +f - AST_REMOVECOLUMN: Remove a column from a Table +f - AST_REMOVEPARAMETER: Remove a global parameter from a Table +f - AST_REMOVEROW: Remove a row from a Table + +* Copyright: +* Copyright (C) 2010-2011 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-NOV-2010 (DSB): +* Original version. +* 13-MAY-2011 (DSB): +* Added support for table parameters. +* 16-NOV-2013 (DSB): +* Fix bug in forming keys in GetColumnLenC. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Table + +/* Fixed KeyMap entry names */ +#define LENGTH "Length" +#define NAME "Name" +#define NROW "Nrow" +#define SHAPE "Shape" +#define SIZE "Size" +#define TYPE "Type" +#define UNIT "Unit" + +/* A function macro that puts quotes around a value */ +#define STRING(w) #w + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "keymap.h" /* Coordinate keymaps (parent class) */ +#include "channel.h" /* I/O channels */ +#include "table.h" /* Interface definition for this class */ + + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static void (* parent_setkeycase)( AstKeyMap *, int, int * ); +static void (* parent_clearkeycase)( AstKeyMap *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); +static int (* parent_mapget0a)( AstKeyMap *, const char *, AstObject * *, int *); +static int (* parent_mapget0c)( AstKeyMap *, const char *, const char **, int *); +static int (* parent_mapget0d)( AstKeyMap *, const char *, double *, int *); +static int (* parent_mapget0f)( AstKeyMap *, const char *, float *, int *); +static int (* parent_mapget0i)( AstKeyMap *, const char *, int *, int *); +static int (* parent_mapget0p)( AstKeyMap *, const char *, void **, int *); +static int (* parent_mapget0b)( AstKeyMap *, const char *, unsigned char *, int *); +static int (* parent_mapget0s)( AstKeyMap *, const char *, short int *, int *); +static int (* parent_mapget1a)( AstKeyMap *, const char *, int, int *, AstObject **, int * ); +static int (* parent_mapget1c)( AstKeyMap *, const char *, int, int, int *, char *, int * ); +static int (* parent_mapget1d)( AstKeyMap *, const char *, int, int *, double *, int * ); +static int (* parent_mapget1f)( AstKeyMap *, const char *, int, int *, float *, int * ); +static int (* parent_mapget1i)( AstKeyMap *, const char *, int, int *, int *, int * ); +static int (* parent_mapget1p)( AstKeyMap *, const char *, int, int *, void **, int * ); +static int (* parent_mapget1s)( AstKeyMap *, const char *, int, int *, short int *, int * ); +static int (* parent_mapget1b)( AstKeyMap *, const char *, int, int *, unsigned char *, int * ); +static int (* parent_mapgetelema)( AstKeyMap *, const char *, int, AstObject **, int * ); +static int (* parent_mapgetelemc)( AstKeyMap *, const char *, int, int, char *, int * ); +static int (* parent_mapgetelemd)( AstKeyMap *, const char *, int, double *, int * ); +static int (* parent_mapgetelemf)( AstKeyMap *, const char *, int, float *, int * ); +static int (* parent_mapgetelemi)( AstKeyMap *, const char *, int, int *, int * ); +static int (* parent_mapgetelemp)( AstKeyMap *, const char *, int, void **, int * ); +static int (* parent_mapgetelems)( AstKeyMap *, const char *, int, short int *, int * ); +static int (* parent_mapgetelemb)( AstKeyMap *, const char *, int, unsigned char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_mapput0a)( AstKeyMap *, const char *, AstObject *, const char *, int *); +static void (* parent_mapput0c)( AstKeyMap *, const char *, const char *, const char *, int *); +static void (* parent_mapput0d)( AstKeyMap *, const char *, double, const char *, int *); +static void (* parent_mapput0f)( AstKeyMap *, const char *, float, const char *, int *); +static void (* parent_mapput0i)( AstKeyMap *, const char *, int, const char *, int *); +static void (* parent_mapput0p)( AstKeyMap *, const char *, void *, const char *, int *); +static void (* parent_mapput0b)( AstKeyMap *, const char *, unsigned char, const char *, int *); +static void (* parent_mapput0s)( AstKeyMap *, const char *, short int, const char *, int *); +static void (* parent_mapput1a)( AstKeyMap *, const char *, int, AstObject *const [], const char *, int * ); +static void (* parent_mapput1c)( AstKeyMap *, const char *, int, const char *const [], const char *, int * ); +static void (* parent_mapput1d)( AstKeyMap *, const char *, int, const double *, const char *, int * ); +static void (* parent_mapput1f)( AstKeyMap *, const char *, int, const float *, const char *, int * ); +static void (* parent_mapput1i)( AstKeyMap *, const char *, int, const int *, const char *, int * ); +static void (* parent_mapput1p)( AstKeyMap *, const char *, int, void *const [], const char *, int * ); +static void (* parent_mapput1b)( AstKeyMap *, const char *, int, const unsigned char *, const char *, int * ); +static void (* parent_mapput1s)( AstKeyMap *, const char *, int, const short int *, const char *, int * ); +static void (* parent_mapputelema)( AstKeyMap *, const char *, int, AstObject *, int * ); +static void (* parent_mapputelemc)( AstKeyMap *, const char *, int, const char *, int * ); +static void (* parent_mapputelemd)( AstKeyMap *, const char *, int, double, int * ); +static void (* parent_mapputelemf)( AstKeyMap *, const char *, int, float, int * ); +static void (* parent_mapputelemi)( AstKeyMap *, const char *, int, int, int * ); +static void (* parent_mapputelemp)( AstKeyMap *, const char *, int, void *, int * ); +static void (* parent_mapputelemb)( AstKeyMap *, const char *, int, unsigned char, int * ); +static void (* parent_mapputelems)( AstKeyMap *, const char *, int, short int, int * ); +static void (* parent_mapremove)( AstKeyMap *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_mapputu)( AstKeyMap *, const char *, const char *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Table) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(Table,Class_Init) +#define class_vtab astGLOBAL(Table,Class_Vtab) +#define getattrib_buff astGLOBAL(Table,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstTableVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstTable *astTableId_( const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstKeyMap *ColumnProps( AstTable *, int * ); +static AstKeyMap *ParameterProps( AstTable *, int * ); +static const char *ColumnName( AstTable *, int index, int * ); +static const char *ParameterName( AstTable *, int index, int * ); +static const char *GetColumnUnit( AstTable *, const char *, int * ); +static const char *TypeString( int ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetColumnLenC( AstTable *, const char *, int * ); +static int GetColumnLength( AstTable *, const char *, int * ); +static int GetColumnNdim( AstTable *, const char *, int * ); +static int GetColumnType( AstTable *, const char *, int * ); +static int GetNcolumn( AstTable *, int * ); +static int GetNparameter( AstTable *, int * ); +static int GetObjSize( AstObject *, int * ); +static int HasColumn( AstTable *, const char *, int *); +static int HasParameter( AstTable *, const char *, int *); +static int MapGet0A( AstKeyMap *, const char *, AstObject **, int * ); +static int MapGet0B( AstKeyMap *, const char *, unsigned char *, int * ); +static int MapGet0C( AstKeyMap *, const char *, const char **, int * ); +static int MapGet0D( AstKeyMap *, const char *, double *, int * ); +static int MapGet0F( AstKeyMap *, const char *, float *, int * ); +static int MapGet0I( AstKeyMap *, const char *, int *, int * ); +static int MapGet0P( AstKeyMap *, const char *, void **, int * ); +static int MapGet0S( AstKeyMap *, const char *, short int *, int * ); +static int MapGet1A( AstKeyMap *, const char *, int, int *, AstObject **, int * ); +static int MapGet1B( AstKeyMap *, const char *, int, int *, unsigned char *, int * ); +static int MapGet1C( AstKeyMap *, const char *, int, int, int *, char *, int * ); +static int MapGet1D( AstKeyMap *, const char *, int, int *, double *, int * ); +static int MapGet1F( AstKeyMap *, const char *, int, int *, float *, int * ); +static int MapGet1I( AstKeyMap *, const char *, int, int *, int *, int * ); +static int MapGet1P( AstKeyMap *, const char *, int, int *, void **, int * ); +static int MapGet1S( AstKeyMap *, const char *, int, int *, short int *, int * ); +static int MapGetElemA( AstKeyMap *, const char *, int, AstObject **, int * ); +static int MapGetElemB( AstKeyMap *, const char *, int, unsigned char *, int * ); +static int MapGetElemC( AstKeyMap *, const char *, int, int, char *, int * ); +static int MapGetElemD( AstKeyMap *, const char *, int, double *, int * ); +static int MapGetElemF( AstKeyMap *, const char *, int, float *, int * ); +static int MapGetElemI( AstKeyMap *, const char *, int, int *, int * ); +static int MapGetElemP( AstKeyMap *, const char *, int, void **, int * ); +static int MapGetElemS( AstKeyMap *, const char *, int, short int *, int * ); +static int ParseKey( AstTable *, const char *, int, char *, int *, AstKeyMap **, const char *, int * ); +static void AddColumn( AstTable *, const char *, int, int, int *, const char *, int * ); +static void AddParameter( AstTable *, const char *, int * ); +static void ColumnShape( AstTable *, const char *, int, int *, int *, int *); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void MapPut0A( AstKeyMap *, const char *, AstObject *, const char *, int * ); +static void MapPut0B( AstKeyMap *, const char *, unsigned char, const char *, int * ); +static void MapPut0C( AstKeyMap *, const char *, const char *, const char *, int * ); +static void MapPut0D( AstKeyMap *, const char *, double, const char *, int * ); +static void MapPut0F( AstKeyMap *, const char *, float, const char *, int * ); +static void MapPut0I( AstKeyMap *, const char *, int, const char *, int * ); +static void MapPut0P( AstKeyMap *, const char *, void *, const char *, int * ); +static void MapPut0S( AstKeyMap *, const char *, short int, const char *, int * ); +static void MapPut1A( AstKeyMap *, const char *, int, AstObject *const [], const char *, int * ); +static void MapPut1B( AstKeyMap *, const char *, int, const unsigned char *, const char *, int * ); +static void MapPut1C( AstKeyMap *, const char *, int, const char *const [], const char *, int * ); +static void MapPut1D( AstKeyMap *, const char *, int, const double *, const char *, int * ); +static void MapPut1F( AstKeyMap *, const char *, int, const float *, const char *, int * ); +static void MapPut1I( AstKeyMap *, const char *, int, const int *, const char *, int * ); +static void MapPut1P( AstKeyMap *, const char *, int, void *const [], const char *, int * ); +static void MapPut1S( AstKeyMap *, const char *, int, const short int *, const char *, int * ); +static void MapPutElemA( AstKeyMap *, const char *, int, AstObject *, int * ); +static void MapPutElemB( AstKeyMap *, const char *, int, unsigned char, int * ); +static void MapPutElemC( AstKeyMap *, const char *, int, const char *, int * ); +static void MapPutElemD( AstKeyMap *, const char *, int, double, int * ); +static void MapPutElemF( AstKeyMap *, const char *, int, float, int * ); +static void MapPutElemI( AstKeyMap *, const char *, int, int, int * ); +static void MapPutElemP( AstKeyMap *, const char *, int, void *, int * ); +static void MapPutElemS( AstKeyMap *, const char *, int, short int, int * ); +static void MapPutU( AstKeyMap *, const char *, const char *, int * ); +static void PurgeRows( AstTable *, int * ); +static void RemoveColumn( AstTable *, const char *, int * ); +static void RemoveParameter( AstTable *, const char *, int * ); +static void RemoveRow( AstTable *, int, int * ); +static void SetKeyCase( AstKeyMap *, int, int * ); +static void ClearKeyCase( AstKeyMap *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static int GetNrow( AstTable *, int * ); +static void SetNrow( AstTable *, int, int * ); + +static int GetNcolumn( AstTable *, int * ); +static int GetNparameter( AstTable *, int * ); + + +/* Member functions. */ +/* ================= */ +static void AddColumn( AstTable *this, const char *name, int type, + int ndim, int *dims, const char *unit, int *status ) { +/* +*++ +* Name: +c astAddColumn +f AST_ADDCOLUMN + +* Purpose: +* Add a new column definition to a table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c void astAddColumn( AstTable *this, const char *name, int type, int ndim, +c int *dims, const char *unit ) +f CALL AST_ADDCOLUMN( THIS, NAME, TYPE, NDIM, DIMS, UNIT, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* Adds the definition of a new column to the supplied table. Initially, +* the column is empty. Values may be added subsequently using the +* methods of the KeyMap class. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c name +f NAME = CHARACTER * ( * ) (Given) +* The column name. Trailing spaces are ignored (all other spaces +* are significant). The supplied string is converted to upper case. +c type +f TYPE = INTEGER (Given) +* The data type associated with the column. See "Applicability:" +* below. +c ndim +f NDIM = INTEGER (Given) +* The number of dimensions spanned by the values stored in a single +* cell of the column. Zero if the column holds scalar values. +c dims +f DIMS( NDIM ) = INTEGER (Given) +* An array holding the the lengths of each of the axes spanned by +* the values stored in a single cell of the column. Ignored if the +* column holds scalara values. +c unit +f UNIT = CHARACTER * ( * ) (Given) +* A string specifying the units of the column. Supply a blank +* string if the column is unitless. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Table +* Tables can hold columns with any of the following data types - +* AST__INTTYPE (for integer), AST__SINTTYPE (for +c short int), +f INTEGER*2), +* AST__BYTETYPE (for +c unsigned bytes - i.e. unsigned chars), +f bytes), +* AST__DOUBLETYPE (for double +* precision floating point), AST__FLOATTYPE (for single +* precision floating point), AST__STRINGTYPE (for character string), +* AST__OBJECTTYPE (for AST Object pointer), AST__POINTERTYPE (for +* arbitrary C pointer) or AST__UNDEFTYPE (for undefined values +* created by +c astMapPutU). +f AST_MAPPUTU). +* FitsTable +* FitsTables can hold columns with any of the following data types - +* AST__INTTYPE (for integer), AST__SINTTYPE (for +c short int), +f INTEGER*2), +* AST__BYTETYPE (for +c unsigned bytes - i.e. unsigned chars), +f bytes), +* AST__DOUBLETYPE (for double +* precision floating point), AST__FLOATTYPE (for single +* precision floating point), AST__STRINGTYPE (for character string). + +* Notes: +* - This +c function +f routine +* returns without action if a column already exists in the Table +* with the supplied name and properties. However an error is +* reported if any of the properties differ. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* KeyMap holding all column details */ + AstKeyMap *col_km; /* KeyMap holding new column details */ + const char *oldunit; /* Pointer to the old coumn unit string */ + int *olddims; /* Shape of pre-existing column */ + int idim; /* Axis index */ + int namlen; /* Used length of "name" */ + int nval; /* Number of values returned */ + int oldtype; /* Data type of pre-existing column */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Verify supplied values. */ + namlen = astChrLen( name ); + if( namlen == 0 ) { + astError( AST__BADKEY, "astAddColumn(%s): Illegal blank column name " + "supplied.", status, astGetClass( this ) ); + + } else if( namlen > AST__MXCOLNAMLEN ) { + astError( AST__BADKEY, "astAddColumn(%s): Column name '%s' is too " + "long (must be no more than %d characters).", status, + astGetClass( this ), name, AST__MXCOLNAMLEN ); + + } else if( ndim < 0 ) { + astError( AST__NAXIN, "astAddColumn(%s): No of axes (%d) for values in " + "new column %s is invalid.", status, astGetClass( this ), + ndim, name ); + + } else if( TypeString( type ) == NULL ) { + astError( AST__NAXIN, "astAddColumn(%s): Bad data type supplied (%d) " + "for new column %s.", status, astGetClass( this ), type, + name ); + + } else { + for( idim = 0; idim < ndim; idim++ ) { + if( dims[ idim ] < 1 ) { + astError( AST__DIMIN, "astAddColumn(%s): Length of axis %d (%d) " + "for new column %s is invalid.", status, + astGetClass( this ), idim + 1, dims[ idim ], name ); + break; + } + } + } + +/* If there is already a column with the given name, check its properties + match the supplied properties. */ + if( astOK ) { + cols = astColumnProps( this ); + if( astMapGet0A( cols, name, &col_km ) ) { + + astMapGet0I( col_km, TYPE, &oldtype ); + if( oldtype != type && astOK ) { + astError( AST__OLDCOL, "astAddColumn(%s): A column called " + "%s already exists in the table with a different " + "data type (%s).", status, astGetClass( this ), + name, TypeString( oldtype ) ); + } + + if( !astMapGet0C( col_km, UNIT, &oldunit ) ) oldunit = ""; + if( strcmp( oldunit, unit ) && astOK ) { + astError( AST__OLDCOL, "astAddColumn(%s): A column called " + "%s already exists in the table with a different " + "unit string ('%s').", status, astGetClass( this ), + name, oldunit ); + } + + if( ndim != astMapLength( col_km, SHAPE ) && astOK ) { + astError( AST__OLDCOL, "astAddColumn(%s): A column called " + "%s already exists in the table with a different " + "number of axes (%d).", status, astGetClass( this ), + name, astMapLength( col_km, SHAPE ) ); + } + + if( ndim > 0 && astOK ) { + olddims = astMalloc( sizeof( int )*ndim ); + (void) astMapGet1I( col_km, SHAPE, ndim, &nval, olddims ); + for( idim = 0; idim < ndim && astOK; idim++ ) { + if( dims[ idim ] != olddims[ idim ] ) { + astError( AST__OLDCOL, "astAddColumn(%s): A column called " + "%s already exists in the table with a different " + "shape.", status, astGetClass( this ), name ); + } + } + olddims = astFree( olddims ); + } + +/* Otherwise, add a new column to the table. */ + } else { + +/* Add a suitable entry describing the column to the Columns KeyMap. */ + col_km = astKeyMap( " ", status ); + astMapPut0C( col_km, NAME, name, NULL ); + astMapPut0I( col_km, TYPE, type, NULL ); + if( ndim ) astMapPut1I( col_km, SHAPE, ndim, dims, NULL ); + astMapPut0C( col_km, UNIT, unit, NULL ); + +/* Put the column KeyMap into the KeyMap holding details of all columns. + Use the column name as the key. */ + astMapPut0A( cols, name, col_km, NULL ); + } + +/* Annul the local KeyMap pointers. */ + col_km = astAnnul( col_km ); + cols = astAnnul( cols ); + } +} + +static void AddParameter( AstTable *this, const char *name, int *status ) { +/* +*++ +* Name: +c astAddParameter +f AST_ADDPARAMETER + +* Purpose: +* Add a new global parameter definition to a table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c void astAddParameter( AstTable *this, const char *name ) +f CALL AST_ADDPARAMETER( THIS, NAME, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* Adds the definition of a new global parameter to the supplied +* table. Note, this does not store a value for the parameter. To get +* or set the parameter value, the methods of the paremt KeyMap class +* should be used, using the name of the parameter as the key. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c name +f NAME = CHARACTER * ( * ) (Given) +* The parameter name. Trailing spaces are ignored (all other spaces +* are significant). The supplied string is converted to upper case. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - Unlike columns, the definition of a parameter does not specify its type, +* size or dimensionality. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *pars; /* KeyMap holding all parameter details */ + int namlen; /* Used length of "name" */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Verify supplied values. */ + namlen = astChrLen( name ); + if( namlen == 0 ) { + astError( AST__BADKEY, "astAddParameter(%s): Illegal blank parameter name " + "supplied.", status, astGetClass( this ) ); + + } else if( namlen > AST__MXCOLNAMLEN ) { + astError( AST__BADKEY, "astAddParameter(%s): Parameter name '%s' is too " + "long (must be no more than %d characters).", status, + astGetClass( this ), name, AST__MXCOLNAMLEN ); + } + +/* Do nothing if there is already a parameter with the given name. */ + if( astOK ) { + pars = astParameterProps( this ); + if( !astMapHasKey( pars, name ) ) { + +/* Add a suitable entry to the Parameters KeyMap. The value is arbitrary + and currently unused. */ + astMapPut0I( pars, name, 1, NULL ); + } + +/* Annul the local KeyMap pointer. */ + pars = astAnnul( pars ); + } +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void ClearAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Table member function (over-rides the astClearAttrib protected +* method inherited from the KeyMap class). + +* Description: +* This function clears the value of a specified attribute for a +* Table, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Table. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +*/ + +/* Local Variables: */ + AstTable *this; + int nc; + int len; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Table structure. */ + this = (AstTable *) this_object; + +/* Get the length of the attribute string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + /* None yet */ + +/* Define a macro to see if the attribute string matches any of the + read-only column attributes of this class. */ +#define MATCH(attr) \ + ( nc = 0, ( 0 == astSscanf( attrib, attr "(%*s)%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class. If it does, then report an + error. */ + if ( !strcmp( attrib, "nrow" ) || + !strcmp( attrib, "ncolumn" ) || + !strcmp( attrib, "nparameter" ) || + MATCH( "columnlenc" ) || + MATCH( "columnlength" ) || + MATCH( "columnndim" ) || + MATCH( "columntype" ) || + MATCH( "columnunit" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } + +#undef MATCH + +} + +static void ClearKeyCase( AstKeyMap *this, int *status ) { +/* +* Name: +* ClearKeyCase + +* Purpose: +* Clear the KeyCase attribute value for a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "keymape.h" +* void ClearKeyCase( AstKeyMap *this, int *status ) + +* Class Membership: +* Table member function (over-rides the astClearKeyCase protected +* method inherited from the KeyMap class). + +* Description: +* This function clears the value of the KeyCase attribute for a +* Table. For a Table, the KeyCase attribute cannot be changed so this +* function exits without action. + +* Parameters: +* this +* Pointer to the Table. +*/ + +} + +static const char *ColumnName( AstTable *this, int index, int *status ) { +/* +*++ +* Name: +c astColumnName +f AST_COLUMNNAME + +* Purpose: +* Get the name of the column at a given index within the Table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c const char *astColumnName( AstTable *this, int index ) +f RESULT = AST_COLUMNNAME( THIS, INDEX, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* This function returns a string holding the name of the column with +* the given index within the Table. +* +* This function is intended primarily as a means of iterating round all +* the columns in a Table. For this purpose, the number of columns in +* the Table is given by the Ncolumn attribute of the Table. This function +* could then be called in a loop, with the index value going from +c zero to one less than Ncolumn. +f one to Ncolumn. +* +* Note, the index associated with a column decreases monotonically with +* the age of the column: the oldest Column in the Table will have index +* one, and the Column added most recently to the Table will have the +* largest index. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c index +f INDEX = INTEGER (Given) +* The index into the list of columns. The first column has index +* one, and the last has index "Ncolumn". +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astColumnName() +c A pointer to a null-terminated string containing the +f AST_COLUMNNAME = CHARACTER * ( AST__SZCHR ) +f The +* upper case column name. + +* Notes: +c - The returned pointer is guaranteed to remain valid and the +c string to which it points will not be over-written for a total +c of 50 successive invocations of this function. After this, the +c memory containing the string may be re-used, so a copy of the +c string should be made if it is needed for longer than this. +c - A NULL pointer will be returned if this function is invoked +c with the AST error status set, or if it should fail for any +c reason. +f - A blank string will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any +f reason. +*-- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* KeyMap holding column definitions */ + const char *result; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get apointer to the KeyMap holding all column definitions. */ + cols = astColumnProps( this ); + +/* Issue a more useful error message than that issued by astMapKey if the + index is invalid. */ + if( index < 1 || index > astMapSize( cols ) ) { + astError( AST__MPIND, "astColumnName(%s): Cannot find column " + "%d (zero-based) of the %s - invalid index.", status, + astGetClass( this ), index, astGetClass( this ) ); + } + +/* Get the column name. */ + result = astMapKey( cols, index - 1 ); + +/* Free resources. */ + cols = astAnnul( cols ); + +/* Return a pointer to the required column name. */ + return result; +} + +static AstKeyMap *ColumnProps( AstTable *this, int *status ) { +/* +*+ +* Name: +* astColumnProps + +* Purpose: +* Returns a pointer to the KeyMap holding column properties. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* AstKeyMap *astColumnProps( AstTable *this ) + +* Class Membership: +* Table method. + +* Description: +* This function returns a pointer to the KeyMap that holds +* definitions of all the coumns added to the Table. + +* Parameters: +* this +* Pointer to the Table. + +* Returned Value: +* A pointer to the KeyMap. It shpould be annulled using astAnnul +* when no longer needed. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return a cloned pointer to the required KeyMap. */ + return astClone( this->columns ); +} + +static void ColumnShape( AstTable *this, const char *column, int mxdim, + int *ndim, int *dims, int *status ){ +/* +*++ +* Name: +c astColumnShape +f AST_COLUMNSHAPE + +* Purpose: +* Returns the shape of the values in a named column. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c void astColumnShape( AstTable *this, const char *column, int mxdim, +c int *ndim, int *dims ) +f CALL AST_COLUMNSHAPE( THIS, COLUMN, MXDIM, NDIM, DIMS, STATUS ) + +* Class Membership: +* Table method. + +* Description: +c This function +f This routine +* returns the number of dimensions spaned by each value in a named +* column of a Table, together with the length of each dimension. +* These are the values supplied when the column was created using +c astAddColumn. +f AST_ADDCOLUMN. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c column +f COLUMN = CHARACTER * ( * ) (Given) +* The character string holding the upper case name of the column. Trailing +* spaces are ignored. +c mxdim +f MXDIM = INTEGER (Given) +* The length of the +c "dims" array. +f DIMS array. +c ndim +f NDIM = INTEGER (Returned) +c Pointer to an int in which to return the +f The +* number of dimensions spanned by values in the named column. +* This will be zero if the column contains scalar values. +c dims +f DIMS( MXDIM ) = INTEGER (Returned) +c Pointer to an +f An +* array in which to return the length of each dimension. Any +* excess trailing elements will be filled with the value 1. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - No error is reported if the requested column cannot be found in the +* given Table. A value of zero is returned for +c "ndim" and the supplied values in "dims" +f NDIM and the supplied values in DIMS +* are left unchanged. +* - A value of zero is returned for +c "ndim" +f NDIM +* if an error occurs. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* Pointer to KeyMap holding all column info */ + AstKeyMap *col_km; /* Pointer to KeyMap holding requested column info */ + int idim; /* Axis index */ + +/* Initialise */ + *ndim = 0; + +/* Check the inherited status. */ + if( !astOK ) return; + + +/* Get the KeyMap holding information about the requested column. */ + cols = astColumnProps( this ); + if( astMapGet0A( cols, column, &col_km ) ) { + +/* Get the shape of the column values. */ + (void) astMapGet1I( col_km, SHAPE, mxdim, ndim, dims ); + +/* Fill excess array elements with 1. */ + for( idim = *ndim; idim < mxdim; idim++ ) dims[ idim ] = 1; + +/* Free resources. */ + col_km = astAnnul( col_km ); + } + cols = astAnnul( cols ); + +/* If an error has occurred, set ndim to zero. */ + if( !astOK ) *ndim = 0; + +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Tables are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* Table member function (over-rides the astEqual protected +* method inherited from the astKeyMap class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two Tables are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a Table). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the Tables are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstKeyMap *this_km; + AstKeyMap *that_km; + AstTable *that; + AstTable *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two Table structures. */ + this = (AstTable *) this_object; + that = (AstTable *) that_object; + +/* Check the second object is a Table. We know the first is a + Table since we have arrived at this implementation of the virtual + function. */ + if( astIsATable( that ) ) { + +/* Check the Tables are equal when compared as KeyMaps. */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Check the Columns KeyMaps are equal. */ + this_km = astColumnProps( this ); + that_km = astColumnProps( that ); + result = astEqual( this_km, that_km ); + this_km = astAnnul( this_km ); + that_km = astAnnul( that_km ); + +/* Check the Parameter KeyMaps are equal. */ + this_km = astParameterProps( this ); + that_km = astParameterProps( that ); + result = astEqual( this_km, that_km ); + this_km = astAnnul( this_km ); + that_km = astAnnul( that_km ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Table member function (over-rides the protected astGetAttrib +* method inherited from the KeyMap class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a Table, formatted as a character string. + +* Parameters: +* this +* Pointer to the Table. +* attrib +* Pointer to a null terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Table, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Table. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char cname[ AST__MXCOLNAMLEN + 1 ]; /* Column name */ + AstTable *this; /* Pointer to the Table structure */ + const char *result; /* Pointer value to return */ + int ival; /* Int attribute value */ + int len; /* Length of attrib string */ + int nc; /* No. characters read by astSscanf */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the Table structure. */ + this = (AstTable *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null terminated string in an + appropriate format. Set "result" to point at the result string. */ + +/* Table properties */ +/* ================ */ + +/* Ncolumn */ +/* ------- */ + if( !strcmp( attrib, "ncolumn" ) ) { + ival = astGetNcolumn( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Nrow */ +/* ---- */ + } else if( !strcmp( attrib, "nrow" ) ) { + ival = astGetNrow( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* Nparameter */ +/* ---------- */ + } else if( !strcmp( attrib, "nparameter" ) ) { + ival = astGetNparameter( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + + + +/* Column properties */ +/* ================= */ + +/* A macro that gives the scannf pattern to test for a given column + property. Needed since the buffer length is defined by a macro + (AST__MXCOLNAMLEN). */ +#define PATTERN(cnam,blen) #cnam "(%" STRING(blen) "[^()])%n" + +/* ColumnNdim */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, PATTERN(columnndim,AST__MXCOLNAMLEN), + cname, &nc ) ) && ( nc >= len ) ) { + ival = astGetColumnNdim( this, cname ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* ColumnLenC */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, PATTERN(columnlenc,AST__MXCOLNAMLEN), + cname, &nc ) ) && ( nc >= len ) ) { + ival = astGetColumnLenC( this, cname ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* ColumnType */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, PATTERN(columntype,AST__MXCOLNAMLEN), + cname, &nc ) ) && ( nc >= len ) ) { + ival = astGetColumnType( this, cname ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* ColumnLength */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, PATTERN(columnlength,AST__MXCOLNAMLEN), + cname, &nc ) ) && ( nc >= len ) ) { + ival = astGetColumnLength( this, cname ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* ColumnUnit */ + } else if ( nc = 0, + ( 1 == astSscanf( attrib, PATTERN(columnunit,AST__MXCOLNAMLEN), + cname, &nc ) ) && ( nc >= len ) ) { + result = astGetColumnUnit( this, cname ); + +#undef PATTERN + + +/* Unknown attributes */ +/* ================== */ + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static int GetColumnLenC( AstTable *this, const char *column, int *status ) { +/* +*+ +* Name: +* astGetColumnLenC + +* Purpose: +* Get the maximum formatted length of any value in a column. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* int astGetColumnLenC( AstTable *this, const char *column ) + +* Class Membership: +* Table method. + +* Description: +* This function returns the minimum length which a character variable +* must have in order to be able to store the longest value currently +* present (at any row) in a specified column of the supplied Table. If +* the named column holds vector values, then the returned value is +* the length of the longest element of the vector value. + +* Parameters: +* this +* Pointer to the Table. +* column +* The character string holding the upper-case name of the column. +* Trailing spaces are ignored. An error is reported if the supplied +* column is not found in the Table. + +* Returned Value: +* The length (i.e. number of characters) of the longest formatted +* value associated with the named column. This does not include the +* trailing null character. + +* Notes: +* - Automatic data type conversion occurs if the named column holds +* numerical values. +* - An error will be reported if named column does not exist or cannot +* be formatted as a character +* string. +* - A function value of zero will be returned if an error has already +* occurred, or if this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* KeyMap holding column definitions */ + char key[ AST__MXCOLKEYLEN ]; /* Current cell key string */ + int irow; /* Current row index */ + int len; /* Length needed to format current cell */ + int nrow; /* Number of rows in table */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the KeyMap holding information about all columns. */ + cols = astColumnProps( this ); + +/* Check the table contains the requested column. */ + if( astMapHasKey( cols, column ) ) { + +/* Loop round all rows in the table. */ + nrow = astGetNrow( this ); + for( irow = 1; irow <= nrow; irow++ ) { + +/* Format the cell name. */ + sprintf( key, "%.*s(%d)", (int) astChrLen(column), column, irow ); + +/* Get the maximum length needed to format a string in the current + row/column. */ + len = astMapLenC( this, key ); + +/* Return the largest value found for any row. */ + if( len > result ) result = len; + } + +/* Report an error if the column does not exist. */ + } else if( astOK ) { + astError( AST__BADCOL, "astGetColumnLenC(%s): No column named '%s' " + "exists in the table.", status, astGetClass( this ), column ); + } + +/* Free resources */ + cols = astAnnul( cols ); + +/* Return AST__BADTYPE if an error occurred. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetColumnLength( AstTable *this, const char *column, int *status ) { +/* +*+ +* Name: +* astGetColumnLength + +* Purpose: +* Get the number of elements in each value in a column. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* int astGetColumnLength( AstTable *this, const char *column ) + +* Class Membership: +* Table method. + +* Description: +* This function returns the number of elements in each value stored +* in a named column. Each value can be a scalar (in which case the +* ColumnLength attribute has a value of 1), or a multi-dimensional +* array ( in which case the ColumnLength value is equal to the +* product of the array dimensions). + +* Parameters: +* this +* Pointer to the Table. +* column +* The character string holding the upper-case name of the column. +* Trailing spaces are ignored. An error is reported if the supplied +* column is not found in the Table. + +* Returned Value: +* The number of elements in each column value. + +* Notes: +* - An error will be reported if named column does not exist or cannot +* be formatted as a character +* string. +* - A function value of zero will be returned if an error has already +* occurred, or if this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + AstKeyMap *col_km; /* KeyMap holding requested column definition */ + AstKeyMap *cols; /* KeyMap holding all column definitions */ + int *dims; /* Pointer to array holding dimensions */ + int idim; /* Index of dimension */ + int ndim; /* Number of dimensions */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the KeyMap holding information about all columns. */ + cols = astColumnProps( this ); + +/* Get the KeyMap holding information about the requested column. */ + if( astMapGet0A( cols, column, &col_km ) ) { + +/* If the Column properties includes the length, return it. Otherwise, + calculate the length and store it in the KeyMap as a column property. */ + if( ! astMapGet0I( col_km, LENGTH, &result ) ) { + +/* Get the number of axes spanned by each column value, and allocate an + array big enough to hold the dimensions of these axes. */ + ndim = astMapLength( col_km, SHAPE ); + dims = astMalloc( sizeof( int )*ndim ); + if( astOK ) { + +/* Get the dimensions. */ + astMapGet1I( col_km, SHAPE, ndim, &ndim, dims ); + +/* Find the number of elements. */ + result = 1; + for( idim = 0; idim < ndim; idim++ ) { + result *= dims[ idim ]; + } + +/* Store the result in the column KeyMap. */ + astMapPut0I( col_km, LENGTH, result, NULL ); + } + dims = astFree( dims ); + } + +/* Free resources */ + col_km = astAnnul( col_km ); + +/* Report an error if the column does not exist. */ + } else if( astOK ) { + astError( AST__BADCOL, "astGetColumnLength(%s): No column named '%s' " + "exists in the table.", status, astGetClass( this ), column ); + } + +/* Free resources */ + cols = astAnnul( cols ); + +/* Return AST__BADTYPE if an error occurred. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetColumnNdim( AstTable *this, const char *column, int *status ) { +/* +*+ +* Name: +* astGetColumnNdim + +* Purpose: +* Get the number of dimensions for a column in a Table. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* int astGetColumnNdim( AstTable *this, const char *column ) + +* Class Membership: +* Table method. + +* Description: +* This function attribute holds the number of axes spanned by each value +* in a column. If each cell in the column is a scalar, ColumnNdim will +* be zero. If each cell in the column is a 1D spectrum, ColumnNdim will +* be one. If each cell in the column is a 2D image, ColumnNdim will be +* two, etc. + +* Parameters: +* this +* Pointer to the Table. +* column +* The character string holding the upper-case name of the column. +* Trailing spaces are ignored. An error is reported if the supplied +* column is not found in the Table. + +* Returned Value: +* The number of dimensions - zero for a scalar. + +* Notes: +* - A function value of zero will be returned if an error has +* already occurred, or if this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* KeyMap holding column definitions */ + AstKeyMap *col_km; /* Pointer to KeyMap holding column info */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the KeyMap holding information about all columns. */ + cols = astColumnProps( this ); + +/* Get the KeyMap holding information about the requested column. */ + if( astMapGet0A( cols, column, &col_km ) ) { + +/* Get the number of dimensions. */ + result = astMapLength( col_km, SHAPE ); + +/* Free resources */ + col_km = astAnnul( col_km ); + +/* Report an error if the column does not exist. */ + } else if( astOK ) { + astError( AST__BADCOL, "astGetColumnNdim(%s): No column named '%s' " + "exists in the table.", status, astGetClass( this ), column ); + } + cols = astAnnul( cols ); + +/* Return AST__BADTYPE if an error occurred. */ + if( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int GetColumnType( AstTable *this, const char *column, int *status ) { +/* +*+ +* Name: +* astGetColumnType + +* Purpose: +* Get the data type of a column in a Table. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* int astGetColumnType( AstTable *this, const char *column ) + +* Class Membership: +* Table method. + +* Description: +* This function returns a value indicating the data type of a +* named column in a Table. This is the data type which was used +* when the column was added to the Table using astAddColumn. + +* Parameters: +* this +* Pointer to the Table. +* column +* The character string holding the upper-case name of the column. +* Trailing spaces are ignored. An error is reported if the supplied +* column is not found in the Table. + +* Returned Value: +* One of AST__INTTYPE (for integer), AST__SINTTYPE (for short int), +* AST__BYTETYPE (for unsigned bytes - i.e. unsigned chars), +* AST__DOUBLETYPE (for double precision floating point), +* AST__FLOATTYPE (for single precision floating point), AST__STRINGTYPE +* (for character string), AST__OBJECTTYPE (for AST Object pointer), +* AST__POINTERTYPE (for arbitrary C pointer) or AST__UNDEFTYPE (for +* undefined values created by astMapPutU). + +* Notes: +* - A function value of AST__BADTYPE will be returned if an error has +* already occurred, or if this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* Pointer to KeyMap holding all column info */ + AstKeyMap *col_km; /* Pointer to KeyMap holding requested column info */ + int result; /* Returned value */ + +/* Initialise */ + result = AST__BADTYPE; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the KeyMap holding information about the requested column. */ + cols = astColumnProps( this ); + if( astMapGet0A( cols, column, &col_km ) ) { + +/* Get the column data type. */ + (void) astMapGet0I( col_km, TYPE, &result ); + +/* Annul the KeyMap pointer. */ + col_km = astAnnul( col_km ); + +/* Report an error if the column does not exist. */ + } else if( astOK ) { + astError( AST__BADCOL, "astGetColumnType(%s): No column named '%s' " + "exists in the table.", status, astGetClass( this ), column ); + } + cols = astAnnul( cols ); + +/* Return AST__BADTYPE if an error occurred. */ + if( !astOK ) result = AST__BADTYPE; + +/* Return the result. */ + return result; +} + +static const char *GetColumnUnit( AstTable *this, const char *column, int *status ) { +/* +*+ +* Name: +* astGetColumnUnit + +* Purpose: +* Get the unit string for a column in a Table. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* const char *astGetColumnUnit( AstTable *this, const char *column ) + +* Class Membership: +* Table method. + +* Description: +* This function returns the unit string for a named column in a Table. +* This is the unit string that was provided when the column was added to +* the Table using astAddColumn. + +* Parameters: +* this +* Pointer to the Table. +* column +* The character string holding the upper-case name of the column. +* Trailing spaces are ignored. An error is reported if the supplied +* column is not found in the Table. + +* Returned Value: +* A pointer to a null-terminated string containing the column units. + +*- +*/ + +/* Local Variables: */ + AstKeyMap *col_km; /* Pointer to KeyMap holding requested column info */ + AstKeyMap *cols; /* Pointer to KeyMap holding all column info */ + const char *result; /* Returned value */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the KeyMap holding information about the requested column. */ + cols = astColumnProps( this ); + if( astMapGet0A( cols, column, &col_km ) ) { + +/* Get the column unit string. */ + (void) astMapGet0C( col_km, UNIT, &result ); + +/* Annul the KeyMap pointer. */ + col_km = astAnnul( col_km ); + +/* Report an error if the column does not exist. */ + } else if( astOK ) { + astError( AST__BADCOL, "astGetColumnUnit(%s): No column named '%s' " + "exists in the table.", status, astGetClass( this ), column ); + } + cols = astAnnul( cols ); + +/* Return NULL if an error occurred. */ + if( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static int GetNcolumn( AstTable *this, int *status ) { +/* +*+ +* Name: +* astGetNcolumn + +* Purpose: +* Get the number of columns in a Table. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* int astGetNcolumn( AstTable *this ) + +* Class Membership: +* Table method. + +* Description: +* This function returns the number of columns currently in the Table. + +* Parameters: +* this +* Pointer to the Table. + +* Returned Value: +* Number of columns. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstKeyMap *cols; + int result; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a pointer to the KeyMap holding the column definitions. */ + cols = astColumnProps( this ); + +/* Get the number of column definitions in the KeyMap. */ + result = astMapSize( cols ); + +/* Annul the KeyMap pointer. */ + cols = astAnnul( cols ); + +/* Return the result. */ + return result; +} + +static int GetNparameter( AstTable *this, int *status ) { +/* +*+ +* Name: +* astGetNparameter + +* Purpose: +* Get the number of global parameters in a Table. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* int astGetNparameter( AstTable *this ) + +* Class Membership: +* Table method. + +* Description: +* This function returns the number of global parameters currently in the Table. + +* Parameters: +* this +* Pointer to the Table. + +* Returned Value: +* Number of parameters. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstKeyMap *pars; + int result; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a pointer to the KeyMap holding the parameter definitions. */ + pars = astParameterProps( this ); + +/* Get the number of parameter definitions in the KeyMap. */ + result = astMapSize( pars ); + +/* Annul the KeyMap pointer. */ + pars = astAnnul( pars ); + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* Table member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied Tables, +* in bytes. + +* Parameters: +* this +* Pointer to the Table. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Table size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstKeyMap *km; /* KeyMap holding column/parameter definitions */ + AstTable *this; /* Pointer to Table structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the Table structure. */ + this = (AstTable *) this_object; + +/* Invoke the GetObjSize method inherited from the parent KeyMap class, and + then add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + km = astColumnProps( this ); + result += astGetObjSize( km ); + km = astAnnul( km ); + + km = astParameterProps( this ); + result += astGetObjSize( km ); + km = astAnnul( km ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int HasColumn( AstTable *this, const char *column, int *status ){ +/* +*++ +* Name: +c astHasColumn +f AST_HASCOLUMN + +* Purpose: +* Returns a flag indicating if a column is present in a Table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c int astHasColumn( AstTable *this, const char *column ) +f RESULT = AST_HASCOLUMN( THIS, COLUMN, STATUS ) + +* Class Membership: +* Table method. + +* Description: +c This function +f This routine +* returns a flag indicating if a named column exists in a Table, for +* instance, by having been added to to the Table using +c astAddColumn. +f AST_ADDCOLUMN. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c column +f COLUMN = CHARACTER * ( * ) (Given) +* The character string holding the upper case name of the column. Trailing +* spaces are ignored. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - A value of +c zero +f .FALSE. +* is returned for if an error occurs. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *cols; + int result; + +/* Initialise */ + result = 0; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get the KeyMap holding information about all columns. */ + cols = astColumnProps( this ); + +/* Seeif it contains an entry for the named column. */ + result = astMapHasKey( cols, column ); + +/* Free resources. */ + cols = astAnnul( cols ); + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + return result; +} + +static int HasParameter( AstTable *this, const char *parameter, int *status ){ +/* +*++ +* Name: +c astHasParameter +f AST_HASPARAMETER + +* Purpose: +* Returns a flag indicating if a named global parameter is present in a Table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c int astHasParameter( AstTable *this, const char *parameter ) +f RESULT = AST_HASPARAMETER( THIS, PARAMETER, STATUS ) + +* Class Membership: +* Table method. + +* Description: +c This function +f This routine +* returns a flag indicating if a named parameter exists in a Table, for +* instance, by having been added to to the Table using +c astAddParameter. +f AST_ADDPARAMETER. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c parameter +f PARAMETER = CHARACTER * ( * ) (Given) +* The character string holding the upper case name of the parameter. Trailing +* spaces are ignored. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - A value of +c zero +f .FALSE. +* is returned for if an error occurs. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *pars; + int result; + +/* Initialise */ + result = 0; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get the KeyMap holding information about all parameters. */ + pars = astParameterProps( this ); + +/* See if it contains an entry for the named parameter. */ + result = astMapHasKey( pars, parameter ); + +/* Free resources. */ + pars = astAnnul( pars ); + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + return result; +} + +void astInitTableVtab_( AstTableVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitTableVtab + +* Purpose: +* Initialise a virtual function table for a Table. + +* Type: +* Protected function. + +* Synopsis: +* #include "table.h" +* void astInitTableVtab( AstTableVtab *vtab, const char *name ) + +* Class Membership: +* Table vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Table class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstKeyMapVtab *keymap; /* Pointer to KeyMap component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitKeyMapVtab( (AstKeyMapVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsATable) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstKeyMapVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->AddColumn = AddColumn; + vtab->AddParameter = AddParameter; + vtab->ColumnName = ColumnName; + vtab->ParameterName = ParameterName; + vtab->ColumnProps = ColumnProps; + vtab->ColumnShape = ColumnShape; + vtab->GetColumnLenC = GetColumnLenC; + vtab->GetColumnLength = GetColumnLength; + vtab->GetColumnNdim = GetColumnNdim; + vtab->GetColumnType = GetColumnType; + vtab->GetColumnUnit = GetColumnUnit; + vtab->GetNcolumn = GetNcolumn; + vtab->GetNparameter = GetNparameter; + vtab->GetNrow = GetNrow; + vtab->HasColumn = HasColumn; + vtab->HasParameter = HasParameter; + vtab->ParameterProps = ParameterProps; + vtab->PurgeRows = PurgeRows; + vtab->RemoveColumn = RemoveColumn; + vtab->RemoveParameter = RemoveParameter; + vtab->RemoveRow = RemoveRow; + vtab->SetNrow = SetNrow; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + keymap = (AstKeyMapVtab *) vtab; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_mapremove = keymap->MapRemove; + +/* Define convenience macros for overriding methods inherited from the + parent KeyMap class using all data type supported by KeyMap. */ +#define OVERRIDE(method,code,methodlc,codelc) \ + parent_##methodlc##codelc = keymap->method##code; \ + keymap->method##code = method##code; \ + +#define OVERRIDE_METHOD(method,methodlc) \ + OVERRIDE(method,A,methodlc,a) \ + OVERRIDE(method,P,methodlc,p) \ + OVERRIDE(method,C,methodlc,c) \ + OVERRIDE(method,D,methodlc,d) \ + OVERRIDE(method,F,methodlc,f) \ + OVERRIDE(method,I,methodlc,i) \ + OVERRIDE(method,S,methodlc,s) \ + OVERRIDE(method,B,methodlc,b) + +/* Use these macros to override the required methods. */ + OVERRIDE_METHOD(MapPut0,mapput0) + OVERRIDE_METHOD(MapGet0,mapget0) + OVERRIDE_METHOD(MapPut1,mapput1) + OVERRIDE_METHOD(MapGet1,mapget1) + OVERRIDE_METHOD(MapPutElem,mapputelem) + OVERRIDE_METHOD(MapGetElem,mapgetelem) + OVERRIDE(MapPut,U,mapput,u) + OVERRIDE(SetKeyCase,,setkeycase,) + OVERRIDE(ClearKeyCase,,clearkeycase,) + +/* Remove the macros. */ +#undef OVERRIDE_METHOD +#undef OVERRIDE + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "Table", "Two-dimensional table of data values" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* Table member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstTable *this; /* Pointer to Table structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the Table structure. */ + this = (AstTable *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result && this->columns ) result = astManageLock( this->columns, mode, extra, + fail ); + + if( !result && this->parameters ) result = astManageLock( this->parameters, mode, extra, + fail ); + +/* Return the result. */ + return result; +} +#endif + +/* +* Name: +* MapGet0 + +* Purpose: +* Get a scalar value from a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int MapGet0( AstKeyMap *this, const char *key, type *value ); + +* Class Membership: +* Table member function (over-rides the astMapGet0 method inherited +* from the KeyMap class). + +* Description: +* This is a set of functions for retrieving a scalar value from a +* cell of a Table. You should replace in the generic function name +* MapGet0 by an appropriate 1-character type code (see the "Data +* Type Codes" section in the astMapGet0 docs). The stored value is +* converted to the data type indiced by before being returned (an +* error is reported if it is not possible to convert the stored value +* to the requested data type). + +* Parameters: +* this +* Pointer to the Table. +* key +* A character string identifying the cell from which the value is +* to be retrieved. It should have the form "COLNAME(irow)", where +* "COLNAME" is replaced by the name of a column that has been +* defined previously using the astAddColumn method, and "irow" is +* an integer row index (the first row is row 1). +* value +* A pointer to a buffer in which to return the requested value. +* If the requested cell is not found, or if it is found but has an +* undefined value (see astMapPutU), then the contents of the buffer +* on entry to this function will be unchanged on exit. For pointer +* types ("A" and "C"), the buffer should be a suitable pointer, and +* the address of this pointer should be supplied as the "value" +* parameter. +* status +* Pointer to inherited status value. + +* Returned Value: +* A non-zero value is returned if the requested key name was found, and +* does not have an undefined value (see astMapPutU). Zero is returned +* otherwise. + +* Notes: +* - No error is reported if the requested cell cannot be found in the +* given KeyMap, but a zero value will be returned as the function value. +* The supplied buffer will be returned unchanged. +* - Key names are case insensitive, and white space is considered +* significant. +* - If the stored value is a vector value, then the first value in +* the vector will be returned. +* - A string pointer returned by astMapGet0C is guaranteed to remain +* valid and the string to which it points will not be over-written for +* a total of 50 successive invocations of this function. After this, +* the memory containing the string may be re-used, so a copy of +* the string should be made if it is needed for longer than this. +* - If the returned value is an AST Object pointer, the Object's reference +* count is incremented by this call. Any subsequent changes made to +* the Object using the returned pointer will be reflected in any +* any other active pointers for the Object. The returned pointer +* should be annulled using astAnnul when it is no longer needed. +*/ + +/* Define a macro to implement the function for a specific data type. */ +#define MAKE_MAPGET0(X,Xlc,Xtype,Itype) \ +static int MapGet0##X( AstKeyMap *this_keymap, const char *key, Xtype *value, \ + int *status ) { \ +\ +/* Local Variables: */ \ + AstTable *this; /* Pointer to Table structure */ \ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ \ + int irow; /* Row index within key string */ \ + int result; /* Returned flag */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Get a pointer to the Table structure. */ \ + this = (AstTable *) this_keymap; \ +\ +/* If the key is the name of a global table parameter, use the parent \ + method to get the value of hte parameter. */ \ + if( astHasParameter( this, key ) ) { \ + result = (*parent_mapget0##Xlc)( this_keymap, key, value, status ); \ +\ +/* Check the supplied key looks like a table cell key, and get the \ + the column name and the row number. Also checks that the table \ + contains a column with the specified name. */ \ + } else if( ParseKey( this, key, astGetKeyError( this ), colname, &irow, \ + NULL, "astMapGet0" #X, status ) ) { \ +\ +/* If the row index is larger than the current number of rows in the \ + table, do nothing more. */ \ + if( irow <= astGetNrow( this ) ){ \ +\ +/* Use the astMapGet0 method in the parent keyMap class to get the \ + cell contents. */ \ + result = (*parent_mapget0##Xlc)( this_keymap, key, value, status ); \ + } \ + } \ +\ +/* If an error occurred, return zero. */ \ + if( !astOK ) result = 0; \ +\ +/* Return the result.*/ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPGET0(I,i,int,AST__INTTYPE) +MAKE_MAPGET0(D,d,double,AST__DOUBLETYPE) +MAKE_MAPGET0(F,f,float,AST__FLOATTYPE) +MAKE_MAPGET0(C,c,const char *,AST__STRINGTYPE) +MAKE_MAPGET0(A,a,AstObject *,AST__OBJECTTYPE) +MAKE_MAPGET0(P,p,void *,AST__POINTERTYPE) +MAKE_MAPGET0(S,s,short int,AST__SINTTYPE) +MAKE_MAPGET0(B,b,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPGET0 + +/* +* Name: +* MapGet1 + +* Purpose: +* Get a vector value from a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int MapGet1( AstKeyMap *this, const char *key, int mxval, +* int *nval, type *value ) +* int MapGet1C( AstKeyMap *this, const char *key, int l, int mxval, +* int *nval, const char *value ) + +* Class Membership: +* Table member function (over-rides the astMapGet1 method inherited +* from the KeyMap class). + +* Description: +* This is a set of functions for retrieving a vector value from a +* cell of a Table. You should replace in the generic function name +* MapGet1 by an appropriate 1-character type code (see the "Data +* Type Codes" section in the astMapGet1 docs). The stored value is +* converted to the data type indiced by before being returned (an +* error is reported if it is not possible to convert the stored value +* to the requested data type). +* +* Note, the MapGet1C function has an extra parameter "l" which +* specifies the maximum length of each string to be stored in the +* "value" buffer (see the "astMapGet1C" docs). + +* Parameters: +* this +* Pointer to the Table. +* key +* A character string identifying the cell from which the value is +* to be retrieved. It should have the form "COLNAME(irow)", where +* "COLNAME" is replaced by the name of a column that has been +* defined previously using the astAddColumn method, and "irow" is +* an integer row index (the first row is row 1). +* mxval +* The number of elements in the "value" array. +* nval +* The address of an integer in which to put the number of elements +* stored in the "value" array. Any unused elements of the array are +* left unchanged. +* value +* A pointer to an array in which to return the requested values. +* If the requested cell is not found, or if it is found but has an +* undefined value (see astMapPutU), then the contents of the buffer +* on entry to this function will be unchanged on exit. +* status +* Pointer to inherited status value. + +* Returned Value: +* A non-zero value is returned if the requested key name was found, and +* does not have an undefined value (see astMapPutU). Zero is returned +* otherwise. + +* MapGet1C: +* The "value" buffer supplied to the MapGet1C function should be a +* pointer to a character array with "mxval*l" elements, where "l" is +* the maximum length of a string to be returned. The value of "l" +* should be supplied as an extra parameter following "key" when +* invoking MapGet1C, and should include space for a terminating +* null character. + +* Notes: +* - No error is reported if the requested cell cannot be found in the +* given KeyMap, but a zero value will be returned as the function value. +* The supplied buffer will be returned unchanged. +* - Key names are case insensitive, and white space is considered +* significant. +* - If the stored value is a scalar value, then the value will be +* returned in the first element of the supplied array, and "nval" +* will be returned set to 1. +*/ + +/* Define a macro to implement the function for a specific data type + (excluding "C" since that needs an extra parameter). */ +#define MAKE_MAPGET1(X,Xlc,Xtype,Itype) \ +static int MapGet1##X( AstKeyMap *this_keymap, const char *key, int mxval, int *nval, \ + Xtype *value, int *status ) { \ +\ +/* Local Variables: */ \ + AstTable *this; /* Pointer to Table structure */ \ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ \ + int irow; /* Row index within key string */ \ + int result; /* Returned flag */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Get a pointer to the Table structure. */ \ + this = (AstTable *) this_keymap; \ +\ +/* If the key is the name of a global table parameter, use the parent \ + method to get the value of hte parameter. */ \ + if( astHasParameter( this, key ) ) { \ + result = (*parent_mapget1##Xlc)( this_keymap, key, mxval, nval, \ + value, status ); \ +\ +/* Check the supplied key looks like a table cell key, and get the \ + the column name and the row number. Also checks that the table \ + contains a column with the specified name. */ \ + } else if( ParseKey( this, key, astGetKeyError( this ), colname, &irow, \ + NULL, "astMapGet1" #X, status ) ) { \ +\ +/* If the row index is larger than the current number of rows in the \ + table, do nothing more. */ \ + if( irow <= astGetNrow( this ) ){ \ +\ +/* Use the astMapGet1 method in the parent keyMap class to get the \ + cell contents. */ \ + result = (*parent_mapget1##Xlc)( this_keymap, key, mxval, nval, \ + value, status ); \ + } \ + } \ +\ +/* If an error occurred, return zero. */ \ + if( !astOK ) result = 0; \ +\ +/* Return the result.*/ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPGET1(I,i,int,AST__INTTYPE) +MAKE_MAPGET1(D,d,double,AST__DOUBLETYPE) +MAKE_MAPGET1(F,f,float,AST__FLOATTYPE) +MAKE_MAPGET1(A,a,AstObject *,AST__OBJECTTYPE) +MAKE_MAPGET1(P,p,void *,AST__POINTERTYPE) +MAKE_MAPGET1(S,s,short int,AST__SINTTYPE) +MAKE_MAPGET1(B,b,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPGET1 + + +static int MapGet1C( AstKeyMap *this_keymap, const char *key, int l, int mxval, + int *nval, char *value, int *status ) { +/* +* Name: +* MapGet1C + +* Purpose: +* Get a vector value from a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int MapGet1C( AstKeyMap *this, const char *key, int l, int mxval, +* int *nval, const char *value ) + +* Class Membership: +* Table member function (over-rides the astMapGet1C method inherited +* from the KeyMap class). + +* Description: +* This is the implementation of MapGet1 for = "C". We +* cannot use the MAKE_MAPGET1 macro for this because the string +* version of this function has an extra parameter giving the maximum +* length of each string which can be stored in the supplied buffer. + +* Parameters: +* (see MapGet1) +*/ + +/* Local Variables: */ + AstTable *this; /* Pointer to Table structure */ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ + int irow; /* Row index within key string */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Table structure. */ + this = (AstTable *) this_keymap; + +/* If the key is the name of a global table parameter, use the parent + method to get the value of hte parameter. */ + if( astHasParameter( this, key ) ) { + result = (*parent_mapget1c)( this_keymap, key, l, mxval, nval, + value, status ); + +/* Check the supplied key looks like a table cell key, and get the + the column name and the row number. Also checks that the table + contains a column with the specified name. */ + } else if( ParseKey( this, key, astGetKeyError( this ), colname, &irow, + NULL, "astMapGet1C", status ) ) { + +/* If the row index is larger than the current number of rows in the + table, do nothing more. */ + if( irow <= astGetNrow( this ) ){ + +/* Use the astMapGet1 method in the parent keyMap class to get the + cell contents. */ + result = (*parent_mapget1c)( this_keymap, key, l, mxval, nval, + value, status ); + } + } + +/* If an error occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result.*/ + return result; +} + +/* +* Name: +* MapGetElem + +* Purpose: +* Get a single element of a vector value from a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int MapGetElem( AstKeyMap *this, const char *key, int elem, +* type *value, int *status ) +* int MapGetElemC( AstKeyMap *this, const char *key, int l, int elem, +* char *value, int *status ) + +* Class Membership: +* Table member function (over-rides the astMapGetElem method inherited +* from the KeyMap class). + +* Description: +* This is a set of functions for retrieving a single element of a vector +* value from a cell of a Table. You should replace in the generic +* function name MapGetElem by an appropriate 1-character type code +* (see the "Data Type Codes" section in the astMapGetElem docs). The +* stored value is converted to the data type indiced by before being +* returned (an error is reported if it is not possible to convert the +* stored value to the requested data type). +* +* Note, the MapGetElemC function has an extra parameter "l" which +* specifies the maximum length of each string to be stored in the +* "value" buffer (see the "MapGetElemC" docs). + +* Parameters: +* this +* Pointer to the Table. +* key +* A character string identifying the cell from which the value is +* to be retrieved. It should have the form "COLNAME(irow)", where +* "COLNAME" is replaced by the name of a column that has been +* defined previously using the astAddColumn method, and "irow" is +* an integer row index (the first row is row 1). +* elem +* The index of the vector element to modify, starting at zero. +* If the index is outside the range of the vector, an error will +* be reported. +* value +* A pointer to a buffer in which to return the requested values. +* If the requested cell is not found, or if it is found but has an +* undefined value (see astMapPutU), then the contents of the buffer +* on entry to this function will be unchanged on exit. +* status +* Pointer to inherited status value. + +* Returned Value: +* A non-zero value is returned if the requested key name was found, and +* does not have an undefined value (see astMapPutU). Zero is returned +* otherwise. + +* MapGetElemC: +* The "value" buffer supplied to the MapGetElemC function should be a +* pointer to a character array with "l" elements, where "l" is +* the maximum length of a string to be returned. The value of "l" +* should be supplied as an extra parameter following "key" when +* invoking MapGetElemC, and should include space for a terminating +* null character. + +* Notes: +* - No error is reported if the requested cell cannot be found in the +* given KeyMap, but a zero value will be returned as the function value. +* The supplied buffer will be returned unchanged. +* - Key names are case insensitive, and white space is considered +* significant. +* - If the stored value is a scalar value, then the value will be +* returned in the first element of the supplied array, and "nval" +* will be returned set to 1. +*/ + +/* Define a macro to implement the function for a specific data type +(excluding "C" since that needs an extra parameter). */ +#define MAKE_MAPGETELEM(X,Xlc,Xtype,Itype) \ +static int MapGetElem##X( AstKeyMap *this_keymap, const char *key, int elem, \ + Xtype *value, int *status ) { \ +\ +/* Local Variables: */ \ + AstTable *this; /* Pointer to Table structure */ \ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ \ + int irow; /* Row index within key string */ \ + int result; /* Returned flag */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Get a pointer to the Table structure. */ \ + this = (AstTable *) this_keymap; \ +\ +/* If the key is the name of a global table parameter, use the parent \ + method to get the value of hte parameter. */ \ + if( astHasParameter( this, key ) ) { \ + result = (*parent_mapgetelem##Xlc)( this_keymap, key, elem, \ + value, status ); \ +\ +/* Check the supplied key looks like a table cell key, and get the \ + the column name and the row number. Also checks that the table \ + contains a column with the specified name. */ \ + } else if( ParseKey( this, key, astGetKeyError( this ), colname, &irow, \ + NULL, "astMapGetElem" #X, status ) ) { \ +\ +/* If the row index is larger than the current number of rows in the \ + table, do nothing more. */ \ + if( irow <= astGetNrow( this ) ){ \ +\ +/* Use the astMapGetElem method in the parent keyMap class to get the \ + cell contents. */ \ + result = (*parent_mapgetelem##Xlc)( this_keymap, key, elem, \ + value, status ); \ + } \ + } \ +\ +/* If an error occurred, return zero. */ \ + if( !astOK ) result = 0; \ +\ +/* Return the result.*/ \ + return result; \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPGETELEM(I,i,int,AST__INTTYPE) +MAKE_MAPGETELEM(D,d,double,AST__DOUBLETYPE) +MAKE_MAPGETELEM(F,f,float,AST__FLOATTYPE) +MAKE_MAPGETELEM(A,a,AstObject *,AST__OBJECTTYPE) +MAKE_MAPGETELEM(P,p,void *,AST__POINTERTYPE) +MAKE_MAPGETELEM(S,s,short int,AST__SINTTYPE) +MAKE_MAPGETELEM(B,b,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPGETELEM + +static int MapGetElemC( AstKeyMap *this_keymap, const char *key, int l, + int elem, char *value, int *status ) { +/* +* Name: +* MapGetElemC + +* Purpose: +* Get a single element of a vector value from a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int MapGetElemC( AstKeyMap *this, const char *key, int l, int elem, +* char *value, int *status ) + +* Class Membership: +* Table member function (over-rides the astMapGetElemC method inherited +* from the KeyMap class). + +* Description: +* This is the implementation of MapGetElem for = "C". We +* cannot use the MAKE_MAPGETELEM macro for this because the string +* version of this function has an extra parameter giving the maximum +* length of each string which can be stored in the supplied buffer. + +* Parameters: +* (see MapGetElem) +*/ + +/* Local Variables: */ + AstTable *this; /* Pointer to Table structure */ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ + int irow; /* Row index within key string */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the Table structure. */ + this = (AstTable *) this_keymap; + +/* If the key is the name of a global table parameter, use the parent + method to get the value of hte parameter. */ + if( astHasParameter( this, key ) ) { + result = (*parent_mapgetelemc)( this_keymap, key, l, elem, + value, status ); + +/* Check the supplied key looks like a table cell key, and get the + the column name and the row number. Also checks that the table + contains a column with the specified name. */ + } else if( ParseKey( this, key, astGetKeyError( this ), colname, &irow, + NULL, "astMapGetElemC", status ) ) { + +/* If the row index is larger than the current number of rows in the + table, do nothing more. */ + if( irow <= astGetNrow( this ) ){ + +/* Use the astMapGetElem method in the parent keyMap class to get the + cell contents. */ + result = (*parent_mapgetelemc)( this_keymap, key, l, elem, + value, status ); + } + } + +/* If an error occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the result.*/ + return result; +} + +/* +* Name: +* MapPut0 + +* Purpose: +* Stores a scalar value in a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void MapPut0( AstKeyMap *this, const char *key, type value, +* const char *comment, int *status ) + +* Class Membership: +* Table member function (over-rides the astMapPut0 method inherited +* from the KeyMap class). + +* Description: +* This is a set of functions for storing a scalar value in a cell of +* a Table. You should use a function which matches the data type of the +* data you wish to add to the Table by replacing in the generic +* function name MapPut0 by an appropriate 1-character type code (see +* the "Data Type Codes" section in the astMapPut0 docs). + +* Parameters: +* this +* Pointer to the Table in which to store the supplied value. +* key +* A character string identifying the cell in which the value is +* to be stored. It should have the form "COLNAME(irow)", where +* "COLNAME" is replaced by the name of a column that has been +* defined previously using the astAddColumn method, and "irow" is +* an integer row index (the first row is row 1). +* value +* The value to be stored. The data type of this value should match +* the 1-character type code appended to the function name (e.g. if +* you are using astMapPut0A, the type of this value should be "pointer +* to AstObject"). An error will be reported if this data type is +* different to the data type assigned to the column when it was +* created via astAddColumn. +* comment +* A pointer to a null-terminated comment string to be stored with the +* value. A NULL pointer may be supplied, in which case no comment is +* stored. +* status +* Pointer to inherited status value. + +* Notes: +* - Key names are case insensitive, and white space is considered +* significant. +* - The new value will replace any old value already stored in the +* Table for the specified cell. +* - If the stored value is an AST Object pointer, the Object's reference +* count is incremented by this call. Any subsequent changes made to +* the Object using the returned pointer will be reflected in any +* any other active pointers for the Object, including any obtained +* later using astMapget0A. The reference count for the Object will be +* decremented when the KeyMap is destroyed, or the entry is removed or +* over-written with a different pointer. + +*/ +/* Define a macro to implement the function for a specific data type. */ +#define MAKE_MAPPUT0(X,Xlc,Xtype,Itype,ValExp) \ +static void MapPut0##X( AstKeyMap *this_keymap, const char *key, Xtype value, \ + const char *comment, int *status ) { \ +\ +/* Local Variables: */ \ + AstKeyMap *col_km; /* KeyMap holding details of the requested column */ \ + AstTable *this; /* Pointer to Table structure */ \ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ \ + int irow; /* Row index within key string */ \ + int type; /* Data type of the requested column */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Get a pointer to the Table structure. */ \ + this = (AstTable *) this_keymap; \ +\ +/* If the key is the name of a global table parameter, use the parent \ + method to put the value of the parameter. */ \ + if( astHasParameter( this, key ) ) { \ + (*parent_mapput0##Xlc)( this_keymap, key, value, comment, status ); \ +\ +/* Check the supplied key looks like a table cell key, and get the \ + the column name and the row number. Also checks that the table \ + contains a column with the specified name. */ \ + } else if( ParseKey( this, key, 1, colname, &irow, &col_km, "astMapPut0" #X, \ + status ) ) { \ +\ +/* Check the column holds scalar values of the type implied by the \ + code in the function name. */ \ + (void) astMapGet0I( col_km, TYPE, &type ); \ + if( type != Itype && astOK ) { \ + astError( AST__BADTYP, "astMapPut0" #X "(%s): Failed to store a " \ + #Xtype " value for cell \"%s\": column %s holds %s " \ + "values.", status, astGetClass( this ), key, colname, \ + TypeString( type ) ); \ + } \ +\ + if( astMapHasKey( col_km, SHAPE ) && astOK ) { \ + astError( AST__BADTYP, "astMapPut0" #X "(%s): Failed to store a " \ + "scalar value for cell \"%s\": column %s holds vector " \ + " values.", status, astGetClass( this ), key, colname ); \ + } \ +\ +/* If the row index is larger than the current number of rows in the \ + table, update the number of rows in the table. */ \ + if( irow > astGetNrow( this ) ) astSetNrow( this, irow ); \ +\ +/* Use the astMapPut0 method in the parent keyMap class to store the \ + new cell contents. */ \ + (*parent_mapput0##Xlc)( this_keymap, key, value, comment, status ); \ +\ +/* Free resources. */ \ + col_km = astAnnul( col_km ); \ + } \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPPUT0(I,i,int,AST__INTTYPE,value) +MAKE_MAPPUT0(D,d,double,AST__DOUBLETYPE,value) +MAKE_MAPPUT0(F,f,float,AST__FLOATTYPE,value) +MAKE_MAPPUT0(C,c,const char *,AST__STRINGTYPE,astStore(NULL,value,strlen(value)+1)) +MAKE_MAPPUT0(A,a,AstObject *,AST__OBJECTTYPE,(value?astClone(value):NULL)) +MAKE_MAPPUT0(P,p,void *,AST__POINTERTYPE,value) +MAKE_MAPPUT0(S,s,short int,AST__SINTTYPE,value) +MAKE_MAPPUT0(B,b,unsigned char,AST__BYTETYPE,value) + +/* Undefine the macro. */ +#undef MAKE_MAPPUT0 + +/* +* Name: +* MapPut1 + +* Purpose: +* Stores a vectorised value in a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void MapPut1( AstKeyMap *this, const char *key, int size, +* const type value[], const char *comment, +* int *status ); + +* Class Membership: +* Table member function (over-rides the astMapPut1 method inherited +* from the KeyMap class). + +* Description: +* This is a set of functions for storing a vectorised value in a cell of +* a Table. You should use a function which matches the data type of the +* data you wish to add to the Table by replacing in the generic +* function name MapPut1 by an appropriate 1-character type code (see +* the "Data Type Codes" section in the astMapPut1 docs). + +* Parameters: +* this +* Pointer to the Table in which to store the supplied value. +* key +* A character string identifying the cell in which the value is +* to be stored. It should have the form "COLNAME(irow)", where +* "COLNAME" is replaced by the name of a column that has been +* defined previously using the astAddColumn method, and "irow" is +* an integer row index (the first row is row 1). +* size +* The number of elements in the supplied array of values. +* value +* The value to be stored. The data type of this value should match +* the 1-character type code appended to the function name (e.g. if +* you are using astMapPut0A, the type of this value should be "pointer +* to AstObject"). An error will be reported if this data type is +* different to the data type assigned to the column when it was +* created via astAddColumn. +* comment +* A pointer to a null-terminated comment string to be stored with the +* value. A NULL pointer may be supplied, in which case no comment is +* stored. +* status +* Pointer to inherited status value. + +* Notes: +* - Key names are case insensitive, and white space is considered +* significant. +* - The new value will replace any old value already stored in the +* Table for the specified cell. + +*/ + +/* Define a macro to implement the function for a specific data type. */ +#define MAKE_MAPPUT1(X,Xlc,Xtype,Itype,ValExp) \ +static void MapPut1##X( AstKeyMap *this_keymap, const char *key, int size, \ + Xtype value[], const char *comment, \ + int *status ) { \ +\ +/* Local Variables: */ \ + AstTable *this; /* Pointer to Table structure */ \ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ \ + int irow; /* Row index within key string */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Get a pointer to the Table structure. */ \ + this = (AstTable *) this_keymap; \ +\ +/* If the key is the name of a global table parameter, use the parent \ + method to put the value of the parameter. */ \ + if( astHasParameter( this, key ) ) { \ + (*parent_mapput1##Xlc)( this_keymap, key, size, value, \ + comment, status ); \ +\ +/* Check the supplied key looks like a table cell key, and get the \ + the column name and the row number. Also checks that the table \ + contains a column with the specified name. */ \ + } else if( ParseKey( this, key, 1, colname, &irow, NULL, "astMapPut1" #X, \ + status ) ) { \ +\ +/* Check the column holds vector values of the type implied by the \ + code in the function name. */ \ + if( astGetColumnType( this, colname ) != Itype && astOK ) { \ + astError( AST__BADTYP, "astMapPut1" #X "(%s): Failed to store " \ + #Xtype " values for cell \"%s\": column %s holds %s values.", \ + status, astGetClass( this ), key, colname, \ + TypeString( astGetColumnType( this, colname ) ) ); \ + } \ +\ +/* Check the column holds vectors with length equal to the supplied vector. */ \ + if( astGetColumnLength( this, colname ) != size && astOK ) { \ + astError( AST__BADTYP, "astMapPut1" #X "(%s): Failed to " \ + "store a vector value for cell \"%s\": column " \ + "%s needs %d values per cell but %d were supplied.", \ + status, astGetClass( this ), key, colname, \ + astGetColumnLength( this, colname ), size ); \ + } \ +\ +/* If all is OK, update the number of rows in the table if required, and \ + store the vector in the parent KeyMap. */ \ + if( astOK ) { \ + if( irow > astGetNrow( this ) ) astSetNrow( this, irow ); \ + (*parent_mapput1##Xlc)( this_keymap, key, size, value, \ + comment, status ); \ + } \ +\ + } \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPPUT1(D,d,const double,AST__DOUBLETYPE,value[i]) +MAKE_MAPPUT1(F,f,const float,AST__FLOATTYPE,value[i]) +MAKE_MAPPUT1(I,i,const int,AST__INTTYPE,value[i]) +MAKE_MAPPUT1(C,c,const char *const,AST__STRINGTYPE,astStore(NULL,value[i],strlen(value[i])+1)) +MAKE_MAPPUT1(A,a,AstObject *const,AST__OBJECTTYPE,(value[i]?astClone(value[i]):NULL)) +MAKE_MAPPUT1(P,p,void *const,AST__POINTERTYPE,value[i]) +MAKE_MAPPUT1(S,s,const short int,AST__SINTTYPE,value[i]) +MAKE_MAPPUT1(B,b,const unsigned char,AST__BYTETYPE,value[i]) + +/* Undefine the macro. */ +#undef MAKE_MAPPUT1 + +/* +* Name: +* MapPutElem + +* Purpose: +* Put a value into an element of a vector value in a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void MapPutElem( AstKeyMap *this, const char *key, int elem, +* type *value, int *status ) + +* Class Membership: +* Table member function (over-rides the astMapPutElem method inherited +* from the KeyMap class). + +* Description: +* This is a set of functions for storing a value in a single element +* of a vector value stored in a cell of a Table. You should use a +* function which matches the data type of the data you wish to add to +* the Table by replacing in the generic function name MapPutElem +* by an appropriate 1-character type code (see the "Data Type Codes" +* section in the astMapPutElem docs). + +* Parameters: +* this +* Pointer to the Table in which to store the supplied value. +* key +* A character string identifying the cell in which the value is +* to be stored. It should have the form "COLNAME(irow)", where +* "COLNAME" is replaced by the name of a column that has been +* defined previously using the astAddColumn method, and "irow" is +* an integer row index (the first row is row 1). +* elem +* The index of the vector element to modify, starting at zero. +* If the index is outside the range of the vector, an error will +* be reported. +* value +* The value to be stored. The data type of this value should match +* the 1-character type code appended to the function name (e.g. if +* you are using astMapPut0A, the type of this value should be "pointer +* to AstObject"). An error will be reported if this data type is +* different to the data type assigned to the column when it was +* created via astAddColumn. +* status +* Pointer to inherited status value. + +* Notes: +* - Key names are case insensitive, and white space is considered +* significant. +* - The new value will replace any old value already stored in the +* Table for the specified cell. +* - If the stored value is an AST Object pointer, the Object's reference +* count is incremented by this call. Any subsequent changes made to +* the Object using the returned pointer will be reflected in any +* any other active pointers for the Object, including any obtained +* later using astMapget0A. The reference count for the Object will be +* decremented when the KeyMap is destroyed, or the entry is removed or +* over-written with a different pointer. + +*/ +/* Define a macro to implement the function for a specific data type. */ + +#define MAKE_MAPPUTELEM(X,Xlc,Xtype,Itype) \ +static void MapPutElem##X( AstKeyMap *this_keymap, const char *key, int elem, \ + Xtype value, int *status ) { \ +\ +/* Local Variables: */ \ + AstTable *this; /* Pointer to Table structure */ \ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ \ + int irow; /* Row index within key string */ \ + int type; /* Data type of the requested column */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Get a pointer to the Table structure. */ \ + this = (AstTable *) this_keymap; \ +\ +/* If the key is the name of a global table parameter, use the parent \ + method to put the value of the parameter. */ \ + if( astHasParameter( this, key ) ) { \ + (*parent_mapputelem##Xlc)( this_keymap, key, elem, value, \ + status ); \ +\ +/* Check the supplied key looks like a table cell key, and get the \ + the column name and the row number. Also checks that the table \ + contains a column with the specified name. */ \ + } else if( ParseKey( this, key, 1, colname, &irow, NULL, "astMapPutElem" #X, \ + status ) ) { \ +\ +/* Check the column holds vector values of the type implied by the \ + code in the function name. */ \ + type = astGetColumnType( this, colname ); \ + if( type != Itype && astOK ) { \ + astError( AST__BADTYP, "astMapPutElem" #X "(%s): Failed to store a " \ + #Xtype " value in cell \"%s\": column %s holds %s values.", \ + status, astGetClass( this ), key, colname, \ + TypeString( type ) ); \ + } \ +\ +/* Check the column holds vectors with length equal to the supplied vector. */ \ + if( astGetColumnLength( this, colname ) <= elem && astOK ) { \ + astError( AST__BADTYP, "astMapPutElem" #X "(%s): Failed to " \ + "store a value for element %d (zero-based) of " \ + "cell \"%s\": column %s has only %d values per " \ + "cell.", status, astGetClass( this ), elem, key, \ + colname, astGetColumnLength( this, colname ) ); \ + } \ +\ +/* If all is OK, update the number of rows in the table if required, and \ + store the value in the parent KeyMap. */ \ + if( astOK ) { \ + if( irow > astGetNrow( this ) ) astSetNrow( this, irow ); \ + (*parent_mapputelem##Xlc)( this_keymap, key, elem, value, \ + status ); \ + } \ + } \ +} + +/* Expand the above macro to generate a function for each required + data type. */ +MAKE_MAPPUTELEM(I,i,int,AST__INTTYPE) +MAKE_MAPPUTELEM(D,d,double,AST__DOUBLETYPE) +MAKE_MAPPUTELEM(F,f,float,AST__FLOATTYPE) +MAKE_MAPPUTELEM(A,a,AstObject *,AST__OBJECTTYPE) +MAKE_MAPPUTELEM(P,p,void *,AST__POINTERTYPE) +MAKE_MAPPUTELEM(C,c,const char *,AST__STRINGTYPE) +MAKE_MAPPUTELEM(S,s,short int,AST__SINTTYPE) +MAKE_MAPPUTELEM(B,b,unsigned char,AST__BYTETYPE) + +/* Undefine the macro. */ +#undef MAKE_MAPPUTELEM + +static void MapPutU( AstKeyMap *this_keymap, const char *key, const char *comment, + int *status ) { +/* +* Name: +* MapPutU + +* Purpose: +* Stores a undefined value in a cell of a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void MapPutU( AstKeyMap *this, const char *key, const char *comment, +* int *status ) + +* Class Membership: +* Table member function (over-rides the astMapPutU method inherited +* from the KeyMap class). + +* Description: +* This function adds a new cell to a Table, but no value is stored with +* the cell. The cell therefore has a special data type represented by +* symbolic constant AST__UNDEFTYPE. +* +* An example use is to add cells with undefined values to a Table +* prior to locking them with the MapLocked attribute. Such cells +* can act as placeholders for values that can be added to the KeyMap +* later. + +* Parameters: +* this +* Pointer to the Table in which to store the supplied value. +* key +* A character string identifying the cell in which the value is +* to be stored. It should have the form "COLNAME(irow)", where +* "COLNAME" is replaced by the name of a column that has been +* defined previously using the astAddColumn method, and "irow" is +* an integer row index (the first row is row 1). +* comment +* A pointer to a null-terminated comment string to be stored with the +* value. A NULL pointer may be supplied, in which case no comment is +* stored. +* status +* Pointer to inherited status value. + +* Notes: +* - Key names are case insensitive, and white space is considered +* significant. +* - The new undefined value will replace any old value already stored in +* the Table for the specified cell. + +*/ + +/* Local Variables: */ + AstTable *this; /* Pointer to Table structure */ + char colname[ AST__MXCOLNAMLEN + 1 ]; /* Column name read from string */ + int irow; /* Row index within key string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the Table structure. */ + this = (AstTable *) this_keymap; + +/* If the key is the name of a global table parameter, use the parent + method to put the value of the parameter. */ + if( astHasParameter( this, key ) ) { + (*parent_mapputu)( this_keymap, key, comment, status ); + +/* Check the supplied key looks like a table cell key, and get the + the column name and the row number. Also checks that the table + contains a column with the specified name. */ + } else if( ParseKey( this, key, 1, colname, &irow, NULL, "astMapPutU", + status ) ) { + +/* If the row index is larger than the current number of rows in the + table, update the number of rows in the table. */ + if( irow > astGetNrow( this ) ) astSetNrow( this, irow ); + +/* Use the astMapPutU method in the parent keyMap class to store the + new cell contents. */ + (*parent_mapputu)( this_keymap, key, comment, status ); + } +} + +static const char *ParameterName( AstTable *this, int index, int *status ) { +/* +*++ +* Name: +c astParameterName +f AST_PARAMETERNAME + +* Purpose: +* Get the name of the global parameter at a given index within the Table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c const char *astParameterName( AstTable *this, int index ) +f RESULT = AST_PARAMETERNAME( THIS, INDEX, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* This function returns a string holding the name of the global parameter with +* the given index within the Table. +* +* This function is intended primarily as a means of iterating round all +* the parameters in a Table. For this purpose, the number of parameters in +* the Table is given by the Nparameter attribute of the Table. This function +* could then be called in a loop, with the index value going from +c zero to one less than Nparameter. +f one to Nparameter. +* +* Note, the index associated with a parameter decreases monotonically with +* the age of the parameter: the oldest Parameter in the Table will have index +* one, and the Parameter added most recently to the Table will have the +* largest index. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c index +f INDEX = INTEGER (Given) +* The index into the list of parameters. The first parameter has index +* one, and the last has index "Nparameter". +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astParameterName() +c A pointer to a null-terminated string containing the +f AST_PARAMETERNAME = CHARACTER * ( AST__SZCHR ) +f The +* upper case parameter name. + +* Notes: +c - The returned pointer is guaranteed to remain valid and the +c string to which it points will not be over-written for a total +c of 50 successive invocations of this function. After this, the +c memory containing the string may be re-used, so a copy of the +c string should be made if it is needed for longer than this. +c - A NULL pointer will be returned if this function is invoked +c with the AST error status set, or if it should fail for any +c reason. +f - A blank string will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any +f reason. +*-- +*/ + +/* Local Variables: */ + AstKeyMap *pars; /* KeyMap holding parameter definitions */ + const char *result; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get apointer to the KeyMap holding all parameter definitions. */ + pars = astParameterProps( this ); + +/* Issue a more useful error message than that issued by astMapKey if the + index is invalid. */ + if( index < 1 || index > astMapSize( pars ) ) { + astError( AST__MPIND, "astParameterName(%s): Cannot find parameter " + "%d (zero-based) of the %s - invalid index.", status, + astGetClass( this ), index, astGetClass( this ) ); + } + +/* Get the parameter name. */ + result = astMapKey( pars, index - 1 ); + +/* Free resources. */ + pars = astAnnul( pars ); + +/* Return a pointer to the required parameter name. */ + return result; +} + +static AstKeyMap *ParameterProps( AstTable *this, int *status ) { +/* +*+ +* Name: +* astParameterProps + +* Purpose: +* Returns a pointer to the KeyMap holding parameter properties. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "table.h" +* AstKeyMap *astParameterProps( AstTable *this ) + +* Class Membership: +* Table method. + +* Description: +* This function returns a pointer to the KeyMap that holds +* definitions of all the parameters added to the Table. + +* Parameters: +* this +* Pointer to the Table. + +* Returned Value: +* A pointer to the KeyMap. It should be annulled using astAnnul +* when no longer needed. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Return a cloned pointer to the required KeyMap. */ + return astClone( this->parameters ); +} + +static int ParseKey( AstTable *this, const char *key, int report, + char colname[ AST__MXCOLNAMLEN + 1 ], int *irow, + AstKeyMap **col_km, const char *method, int *status ){ +/* +* Name: +* ParseKey + +* Purpose: +* Find the column name and row index in a KeyMap key. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int ParseKey( AstTable *this, const char *key, int report, +* char colname[ AST__MXCOLNAMLEN + 1 ], int *irow, +* AstKeyMap **col_km, const char *method, int *status ) + +* Class Membership: +* Table member function + +* Description: +* This function checks that the supplied KeyMap key conforms to the +* format expected for Table cells: i.e. "COLNAME(irow)", where +* "COLNAME" is the name of a column and "irow" is an integer row +* index (the first row is row 1), An error is reported if this is +* not the case. + +* Parameters: +* this +* Pointer to the table. +* key +* The key string to test. +* report +* If non-zero, an error will be reported if the key does not +* correspond to a cell of an existing column. Otherwise, no +* error will be reported. +* colname +* A buffer in which to return the column name. +* irow +* Address of an int in which to return the row index. +* col_km +* Address of a KeyMap pointer in which to return a pointer to the +* KeyMap holding the information about the column. The returned +* KeyMap pointer should be annulled using astAnnul when no lngerr +* needed. If "col_km" is NULL, no KeyMap pointer is returned. +* method +* Pointer to a string holding the name of the method to include in +* any error message. +* status +* Address of the inherited status value. + +* Returned Value: +* Zero is returned if the key is not a valid Table cell key, or if an +* error occurs. + +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* KeyMap holding column definitions */ + int result; /* Returned flag */ + int collen; /* Length of column name */ + int nctot; /* Number of characters read */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check the supplied key looks like a table cell key, and extract the + column name and row number. */ + nctot = 0; + if( 1 == astSscanf( key, "%*[^(]%n(%d) %n", &collen, irow, &nctot ) + && ( nctot >= strlen( key ) ) ) { + +/* Check the column name is not too long. */ + if( collen > AST__MXCOLNAMLEN ) { + if( report ) { + astError( AST__BADKEY, "%s(%s): Failed to store a value for cell " + "\"%s\": column name is too long.", status, method, + astGetClass( this ), key ); + } + +/* Check the row index is positive. */ + } else if( *irow < 1 ) { + if( report ) { + astError( AST__BADKEY, "%s(%s): Failed to store a value for cell " + "\"%s\": row index %d is invalid.", status, method, + astGetClass( this ), key, *irow ); + } + +/* If the key looks OK so far... */ + } else { + +/* Convert the column name to upper case and store in the returned buffer. */ + astChrCase( key, colname, 1, collen + 1 ); + colname[ collen ] = 0; + +/* check that the column exists in the Table, returning a pointer to the + column KeyMap is reequired. */ + cols = astColumnProps( this ); + if( col_km ) { + result = astMapGet0A( cols, colname, col_km ); + } else { + result = astMapHasKey( cols, colname ); + } + cols = astAnnul( cols ); + +/* Report an error if the table does not contain the specified column. */ + if( !result && astOK && report) { + astError( AST__BADKEY, "%s(%s): Failed to store a value for " + "cell \"%s\": the table does not contain a column " + "called '%s'.", status, method, astGetClass( this ), + key, colname ); + } + } + +/* Report an error if the cell key has the wrong format. */ + } else if( report ) { + astError( AST__BADKEY, "%s(%s): Failed to store a value for cell " + "\"%s\": the cell name is invalid.", status, method, + astGetClass( this ), key ); + } + +/* Return the result.*/ + return result; +} + +static void PurgeRows( AstTable *this, int *status ) { +/* +*++ +* Name: +c astPurgeRows +f AST_PURGEROWS + +* Purpose: +* Remove all empty rows from a table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c void astPurgeRows( AstTable *this ) +f CALL AST_PURGEROWS( THIS, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* This function removes all empty rows from the Table, renaming +* the key associated with each table cell accordingly. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + char newkey[ AST__MXCOLKEYLEN + 1 ]; /* New cell key string */ + char oldkey[ AST__MXCOLKEYLEN + 1 ]; /* Old cell key string */ + const char *col; /* Column name */ + const char *key; /* Pointer to key string */ + const char *op; /* Pointer to opening parenthesis */ + int *w1; /* Work space pointer */ + int icol; /* Column index */ + int inew; /* New row index */ + int iold; /* Old row index */ + int ncol; /* Number of columns in table */ + int nrow; /* Number of rows in table */ + int reset; /* Start a new pass through the KeyMap? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the number of rows in the table. */ + nrow = astGetNrow( this ); + +/* Create workspace to hold the number of defined values stored in each + row. Initialise every row to have zero defined values. */ + w1 = astCalloc( nrow, sizeof( int ) ); + if( astOK ) { + +/* Iterate round all keys in the KeyMap. */ + reset = 1; + while( ( key = astMapIterate( this, reset ) ) && astOK ) { + reset = 0; + +/* Extract the row number from the key. */ + op = strchr( key, '(' ); + if( !op || astSscanf( op + 1, "%d", &iold ) != 1 || + iold > nrow ) { + astError( AST__INTER, "astPurgeRows(%s): Illegal key '%s' " + "found in a %s (internal programming error).", + status, astGetClass( this ), key, astGetClass( this ) ); + +/* Increment the number of values in this row. Note row indices are + one-based. */ + } else { + w1[ iold - 1 ]++; + } + } + +/* Loop round all columns in the Table. */ + ncol = astGetNcolumn( this ); + inew = nrow; + for( icol = 1; icol <= ncol; icol++ ) { + +/* Get the column name */ + col = astColumnName( this, icol ); + +/* Loop round all the old row numbers. Skip empty rows.*/ + inew = 0; + for( iold = 0; iold < nrow; iold++ ) { + if( w1[ iold ] > 0 ) { + +/* Increment the row number to use in place of the old row number. If the + old and new row numbers are the same, we do not need to rename the cell. */ + if( iold != inew++ ) { + +/* For the old and new cell names */ + sprintf( oldkey, "%s(%d)", col, iold + 1 ); + sprintf( newkey, "%s(%d)", col, inew ); + +/* Rename the KeyMap entry. */ + astMapRename( this, oldkey, newkey ); + } + } + } + +/* If all rows were used, we do not need to check any more columns. */ + if( iold == inew ) break; + } + +/* Store the new number of rows. */ + astSetNrow( this, inew ); + } + +/* Free resources. */ + w1 = astFree( w1 ); + +} + +static void RemoveColumn( AstTable *this, const char *name, int *status ) { +/* +*++ +* Name: +c astRemoveColumn +f AST_REMOVECOLUMN + +* Purpose: +* Remove a column from a table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c void astRemoveColumn( AstTable *this, const char *name ) +f CALL AST_REMOVECOLUMN( THIS, NAME, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* This function removes a specified column from the supplied table. +* The +c function +f routine +* returns without action if the named column does not exist in the +* Table (no error is reported). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c name +f NAME = CHARACTER * ( * ) (Given) +* The column name. Trailing spaces are ignored (all other spaces +* are significant). Case is significant. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* KeyMap holding column definitions */ + char key[ AST__MXCOLKEYLEN + 1 ]; /* Cell key string */ + int irow; /* Row index */ + int namlen; /* Used length of "name" */ + int nrow; /* Number of rows in table */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Verify supplied values. */ + namlen = astChrLen( name ); + if( namlen == 0 ) { + astError( AST__BADKEY, "astRemoveColumn(%s): Illegal blank column name " + "supplied.", status, astGetClass( this ) ); + } + +/* Get the number of rows in the table. */ + nrow = astGetNrow( this ); + +/* If there is no column with the given name in the Table, do nothing more. */ + cols = astColumnProps( this ); + if( astOK && astMapHasKey( cols, name ) ) { + +/* Remove the column description from the columns keymap. */ + astMapRemove( cols, name ); + +/* Remove any column cells with defined values from the parent KeyMap. */ + for( irow = 1; irow <= nrow; irow++ ) { + sprintf( key, "%.*s(%d)", namlen, name, irow ); + (*parent_mapremove)( (AstKeyMap *) this, key, status ); + } + } + cols = astAnnul( cols ); +} + +static void RemoveParameter( AstTable *this, const char *name, int *status ) { +/* +*++ +* Name: +c astRemoveParameter +f AST_REMOVEPARAMETER + +* Purpose: +* Remove a global parameter from a table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c void astRemoveParameter( AstTable *this, const char *name ) +f CALL AST_REMOVEPARAMETER( THIS, NAME, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* This function removes a specified global parameter from the supplied table. +* The +c function +f routine +* returns without action if the named parameter does not exist in the +* Table (no error is reported). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c name +f NAME = CHARACTER * ( * ) (Given) +* The parameter name. Trailing spaces are ignored (all other spaces +* are significant). Case is significant. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *pars; /* KeyMap holding parameter definitions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Verify supplied values. */ + if( astChrLen( name ) == 0 ) { + astError( AST__BADKEY, "astRemoveParameter(%s): Illegal blank parameter name " + "supplied.", status, astGetClass( this ) ); + } + +/* If there is no parameter with the given name in the Table, do nothing more. */ + pars = astParameterProps( this ); + if( astOK && astMapHasKey( pars, name ) ) { + +/* Remove the parameter description from the parameters keymap. */ + astMapRemove( pars, name ); + +/* Remove any entry holding the parameter value from the parent KeyMap. */ + (*parent_mapremove)( (AstKeyMap *) this, name, status ); + } + pars = astAnnul( pars ); +} + +static void RemoveRow( AstTable *this, int index, int *status ) { +/* +*++ +* Name: +c astRemoveRow +f AST_REMOVEROW + +* Purpose: +* Remove a row from a table. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "table.h" +c void astRemoveRow( AstTable *this, int index ) +f CALL AST_REMOVEROW( THIS, INDEX, STATUS ) + +* Class Membership: +* Table method. + +* Description: +* This function removes a specified row from the supplied table. +* The +c function +f routine +* returns without action if the row does not exist in the +* Table (no error is reported). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Table. +c index +f INDEX = INTEGER (Given) +* The index of the row to be removed. The first row has index 1. +f STATUS = INTEGER (Given and Returned) +f The global status. + +*-- +*/ + +/* Local Variables: */ + AstKeyMap *cols; /* KeyMap holding column definitions */ + char key[ AST__MXCOLKEYLEN + 1 ]; /* Cell key string */ + const char *col; /* Column name */ + int icol; /* Column index */ + int ncol; /* Number of columns in table */ + int nrow; /* Number of rows in table */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the number of rows in the table. */ + nrow = astGetNrow( this ); + +/* Do nothing if the specified row is out of bounds. */ + if( index > 0 && index <= nrow ) { + +/* Loop round all columns in the table. */ + cols = astColumnProps( this ); + ncol = astMapSize( cols ); + for( icol = 0; icol < ncol; icol++ ) { + col = astMapKey( cols, icol ); + +/* Remove the cell of the current column at the requested row. */ + sprintf( key, "%s(%d)", col, index ); + (*parent_mapremove)( (AstKeyMap *) this, key, status ); + } + cols = astAnnul( cols ); + +/* If the removed row was the last row, reduce the number of rows in the + Table. */ + if( index == nrow ) astSetNrow( this, index - 1 ); + } +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* Table member function (over-rides the astSetAttrib protected +* method inherited from the KeyMap class). + +* Description: +* This function assigns an attribute value for a Table, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the Table. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstTable *this; /* Pointer to the Table structure */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Table structure. */ + this = (AstTable *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + /* None as yet */ + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +#define MATCH2(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "(%*s) =%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + if ( MATCH( "ncolumn" ) || + MATCH( "nparameter" ) || + MATCH( "nrow" ) || + MATCH2( "columnlenc" ) || + MATCH2( "columnlength" ) || + MATCH2( "columnndim" ) || + MATCH2( "columntype" ) || + MATCH2( "columnunit" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +#undef MATCH2 +} + +static void SetKeyCase( AstKeyMap *this, int keycase, int *status ) { +/* +* Name: +* SetKeyCase + +* Purpose: +* Set a value for the KeyCase attribute value for a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "keymape.h" +* void SetKeyCase( AstKeyMap *this, int keycase, int *status ) + +* Class Membership: +* Table member function (over-rides the astSetKeyCase protected +* method inherited from the KeyMap class). + +* Description: +* This function assigns a new valeu to the KeyCase attribute for a +* Table. For a Table, the KeyCase attribute cannot be changed so this +* function exits without action. + +* Parameters: +* this +* Pointer to the Table. +* keycase +* The new value to set. +*/ + +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Table member function (over-rides the astTestAttrib protected +* method inherited from the KeyMap class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a Table's attributes. + +* Parameters: +* this +* Pointer to the Table. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int len; /* Length of attribute string */ + int nc; /* Number of characters read by astSscanf */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the length of the attribute string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + /* None as yet */ + +/* Define a macro to see if the attribute string matches any of the + read-only column attributes of this class. */ +#define MATCH(attr) \ + ( nc = 0, ( 0 == astSscanf( attrib, attr "(%*s)%n", &nc ) ) && \ + ( nc >= len ) ) + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + if ( !strcmp( attrib, "ncolumn" ) || + !strcmp( attrib, "nparameter" ) || + !strcmp( attrib, "nrow" ) || + MATCH( "columnlenc" ) || + MATCH( "columnlength" ) || + MATCH( "columnndim" ) || + MATCH( "columntype" ) || + MATCH( "columnunit" ) ) { + result = 0; + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; + +#undef MATCH +} + +static const char *TypeString( int type ) { +/* +* Name: +* TypeString + +* Purpose: +* Return a pointer to a string describing a data type. + +* Type: +* Private function. + +* Synopsis: +* const char *TypeString( int type ); + +* Description: +* This function returns a pointer to a string describing a data type. + +* Parameters: +* type +* The integer data type code. + +* Returned Value: +* Pointer to a a string descirbing the data type (typically the C +* data type). + +*/ + +/* Local Variables: */ + const char *result; + +/* Compare the supplied type code against each supported value. */ + if( type == AST__INTTYPE ) { + result = "int"; + + } else if( type == AST__BYTETYPE ) { + result = "byte"; + + } else if( type == AST__DOUBLETYPE ) { + result = "double"; + + } else if( type == AST__STRINGTYPE ) { + result = "string"; + + } else if( type == AST__OBJECTTYPE ) { + result = "Object"; + + } else if( type == AST__FLOATTYPE ) { + result = "float"; + + } else if( type == AST__POINTERTYPE ) { + result = "pointer"; + + } else if( type == AST__SINTTYPE ) { + result = "short int"; + + } else if( type == AST__UNDEFTYPE ) { + result = "undefined"; + + } else { + result = NULL; + } + +/* Return the result. */ + return result; +} + + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* +*att++ +* Name: +* ColumnLenC(column) + +* Purpose: +* The largest string length of any value in a column + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute holds the minimum length which a character variable +* must have in order to be able to store the longest value currently +* present (at any row) in a specified column of the supplied Table. +c This does not include room for a trailing null character. +* The required column name should be placed inside the parentheses in +* the attribute name. If the named column holds vector values, then +* the attribute value is the length of the longest element of the +* vector value. + +* Applicability: +* Table +* All Tables have this attribute. + +* Notes: +* - If the named column holds numerical values, the length returned +* is the length of the largest string that would be generated if the +* column values were accessed as strings. + +*att-- +*/ + +/* +*att++ +* Name: +* ColumnLength(column) + +* Purpose: +* The number of elements in each value in a column + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute holds the number of elements in each value stored +* in a named column. Each value can be a scalar (in which case the +* ColumnLength attribute has a value of 1), or a multi-dimensional +* array ( in which case the ColumnLength value is equal to the +* product of the array dimensions). + +* Applicability: +* Table +* All Tables have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* ColumnNdim(column) + +* Purpose: +* The number of axes spanned by each value in a column + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute holds the number of axes spanned by each value in a +* column. If each cell in the column is a scalar, ColumnNdim will be +* zero. If each cell in the column is a 1D spectrum, ColumnNdim will +* be one. If each cell in the column is a 2D image, ColumnNdim will be +* two, etc. The required column name should be placed inside the +* parentheses in the attribute name. + +* Applicability: +* Table +* All Tables have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* ColumnType(column) + +* Purpose: +* The data type of each value in a column + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute holds a integer value indicating the data type of +* a named column in a Table. This is the data type which was used +* when the column was added to the Table using astAddColumn. The +* required column name should be placed inside the parentheses in +* the attribute name. +* +* The attribute value will be one of AST__INTTYPE (for integer), +* AST__SINTTYPE (for +c short int), +f INTEGER*2), +* AST__BYTETYPE (for +c unsigned bytes - i.e. unsigned chars), +f bytes), +* AST__DOUBLETYPE (for double +* precision floating point), AST__FLOATTYPE (for single +* precision floating point), AST__STRINGTYPE (for character string), +* AST__OBJECTTYPE (for AST Object pointer), AST__POINTERTYPE (for +* arbitrary C pointer) or AST__UNDEFTYPE (for undefined values +* created by +c astMapPutU). +f AST_MAPPUTU). + +* Applicability: +* Table +* All Tables have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* Ncolumn + +* Purpose: +* The number of columns in the table. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute holds the number of columns currently in the table. Columns +* are added and removed using the +c astAddColumn and astRemoveColumn +f AST_ADDCOLUMN and AST_REMOVECOLUMN +* functions. + +* Applicability: +* Table +* All Tables have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* Nparameter + +* Purpose: +* The number of global parameters in the table. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute holds the number of global parameters currently in the table. +* Parameters are added and removed using the +c astAddParameter and astRemoveParameter +f AST_ADDPARAMETER and AST_REMOVEPARAMETER +* functions. + +* Applicability: +* Table +* All Tables have this attribute. + +*att-- +*/ + +/* +*att++ +* Name: +* Nrow + +* Purpose: +* The number of rows in the table. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute holds the index of the last row to which any +* contents have been added using any of the +* astMapPut... +* AST_MAPPUT... +* functions. The first row has index 1. + +* Applicability: +* Table +* All Tables have this attribute. + +*att-- +*/ +astMAKE_GET(Table,Nrow,int,0,this->nrow) +astMAKE_SET(Table,Nrow,int,nrow,value) + + + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for Table objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for Table objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Mappings within the Table. +*/ + +/* Local Variables: */ + AstTable *in; /* Pointer to input Table */ + AstTable *out; /* Pointer to output Table */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output Tables. */ + in = (AstTable *) objin; + out = (AstTable *) objout; + +/* Make copies of the component KeyMaps and store pointers to them in the + output Table structure. */ + out->columns = in->columns ? astCopy( in->columns ) : NULL; + out->parameters = in->parameters ? astCopy( in->parameters ) : NULL; +} + + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for Table objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for Table objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstTable *this; /* Pointer to Table */ + +/* Obtain a pointer to the Table structure. */ + this = (AstTable *) obj; + +/* Annul the pointers to the component KeyMaps. */ + if( this->columns ) this->columns = astAnnul( this->columns ); + if( this->parameters ) this->parameters = astAnnul( this->parameters ); + +} + + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for Table objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the Table class to an output Channel. + +* Parameters: +* this +* Pointer to the Table whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstTable *this; /* Pointer to the Table structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the Table structure. */ + this = (AstTable *) this_object; + +/* Write out values representing the instance variables for the Table + class. Note, the primitive data in the Table will be written out + by the parent KeyMap Dump function. This function deals just with the + extra information held in the Table structure. */ + +/* Write out the number of rows in the table. */ + astWriteInt( channel, "Nrow", 1, 1, astGetNrow( this ), + "Number of rows in table" ); + +/* Write out the KeyMap holding definitions of each column. */ + if( this->columns ) { + astWriteObject( channel, "Columns", 1, 0, this->columns, "KeyMap holding " + "column definitions" ); + } + +/* Write out the KeyMap holding definitions of each global parameter. */ + if( this->parameters ) { + astWriteObject( channel, "Params", 1, 0, this->parameters, "KeyMap holding " + "parameter definitions" ); + } +} + + + + + + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsATable and astCheckTable functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(Table,KeyMap) +astMAKE_CHECK(Table) + +AstTable *astTable_( const char *options, int *status, ...) { +/* +*++ +* Name: +c astTable +f AST_TABLE + +* Purpose: +* Create a Table. + +* Type: +* Public function. + +* Synopsis: +c #include "table.h" +c AstTable *astTable( const char *options, ... ) +f RESULT = AST_TABLE( OPTIONS, STATUS ) + +* Class Membership: +* Table constructor. + +* Description: +* This function creates a new empty Table and optionally initialises +* its attributes. +* +* The Table class is a type of KeyMap that represents a two-dimensional +* table of values. The +c astMapGet... and astMapPut... +f AST_MAPGET... and AST_MAPPUT... +* methods provided by the KeyMap class should be used for storing and +* retrieving values from individual cells within a Table. Each entry +* in the KeyMap represents a single cell of the table and has an +* associated key of the form "(i)" where "" is the name of a +* table column and "i" is the row index (the first row is row 1). Keys +* of this form should always be used when using KeyMap methods to access +* entries within a Table. +* +* Columns must be declared using the +c astAddColumn +f AST_ADDCOLUMN +* method before values can be stored within them. This also fixes the +* type and shape of the values that may be stored in any cell of the +* column. Cells may contain scalar or vector values of any data type +* supported by the KeyMap class. Multi-dimensional arrays may also be +* stored, but these must be vectorised when storing and retrieving +* them within a table cell. All cells within a single column must +* have the same type and shape (specified when the column is declared). +* +* Tables may have parameters that describe global properties of the +* entire table. These are stored as entries in the parent KeyMap and +* can be access using the get and set method of the KeyMap class. +* However, parameters must be declared using the +c astAddParameter +f AST_ADDPARAMETER +* method before being accessed. +* +* Note - since accessing entries within a KeyMap is a relatively slow +* process, it is not recommended to use the Table class to store +* very large tables. + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new Table. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new Table. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTable() +f AST_TABLE = INTEGER +* A pointer to the new Table. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list described above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTable *new; /* Pointer to new Table */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the Table, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitTable( NULL, sizeof( AstTable ), !class_init, &class_vtab, + "Table" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Table's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Table. */ + return new; +} + +AstTable *astTableId_( const char *options, ... ) { +/* +* Name: +* astTableId_ + +* Purpose: +* Create a Table. + +* Type: +* Private function. + +* Synopsis: +* #include "table.h" +* AstTable *astTableId_( const char *options, ... ) + +* Class Membership: +* Table constructor. + +* Description: +* This function implements the external (public) interface to the +* astTable constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astTable_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astTable_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astTable_. + +* Returned Value: +* The ID value associated with the new Table. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTable *new; /* Pointer to new Table */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the Table, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitTable( NULL, sizeof( AstTable ), !class_init, &class_vtab, + "Table" ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new Table's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new Table. */ + return astMakeId( new ); +} + +AstTable *astInitTable_( void *mem, size_t size, int init, + AstTableVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitTable + +* Purpose: +* Initialise a Table. + +* Type: +* Protected function. + +* Synopsis: +* #include "table.h" +* AstTable *astInitTable( void *mem, size_t size, int init, +* AstTableVtab *vtab, const char *name ) + +* Class Membership: +* Table initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Table object. It allocates memory (if necessary) to accommodate +* the Table plus any additional data associated with the derived class. +* It then initialises a Table structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a Table at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Table is to be initialised. +* This must be of sufficient size to accommodate the Table data +* (sizeof(Table)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Table (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Table +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Table's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Table. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). + +* Returned Value: +* A pointer to the new Table. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstTable *new; /* Pointer to new Table */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitTableVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a KeyMap structure (the parent class) as the first component + within the Table structure, allocating memory if necessary. Specify that + the KeyMap should be defined in both the forward and inverse directions. */ + new = (AstTable *) astInitKeyMap( mem, size, 0, (AstKeyMapVtab *) vtab, + name ); + if ( astOK ) { + +/* Initialise the Table data. */ +/* ---------------------------- */ + new->nrow = 0; + new->columns = astKeyMap( "KeyCase=0,Sortby=AgeDown", status ); + new->parameters = astKeyMap( "KeyCase=0,Sortby=AgeDown", status ); + +/* Tables require the KeyCase attribute to be zero. */ + (*parent_setkeycase)( (AstKeyMap *) new, 0, status ); + +/* If an error occurred, clean up by deleting the new Table. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new Table. */ + return new; +} + +AstTable *astLoadTable_( void *mem, size_t size, AstTableVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadTable + +* Purpose: +* Load a Table. + +* Type: +* Protected function. + +* Synopsis: +* #include "table.h" +* AstTable *astLoadTable( void *mem, size_t size, AstTableVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* Table loader. + +* Description: +* This function is provided to load a new Table using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* Table structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a Table at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the Table is to be +* loaded. This must be of sufficient size to accommodate the +* Table data (sizeof(Table)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Table (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Table structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstTable) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Table. If this is NULL, a pointer +* to the (static) virtual function table for the Table class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Table" is used instead. + +* Returned Value: +* A pointer to the new Table. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTable *new; /* Pointer to the new Table */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Table. In this case the + Table belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstTable ); + vtab = &class_vtab; + name = "Table"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitTableVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built Table. */ + new = astLoadKeyMap( mem, size, (AstKeyMapVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Table" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* The number of rows. */ + new->nrow = astReadInt( channel, "nrow", 0 ); + +/* KeyMap holding columns definitions. */ + new->columns = astReadObject( channel, "columns", NULL ); + +/* KeyMap holding parameter definitions. */ + new->parameters = astReadObject( channel, "params", NULL ); + +/* If an error occurred, clean up by deleting the new Table. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Table pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astAddColumn_( AstTable *this, const char *name, int type, + int ndim, int *dims, const char *unit, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Table,AddColumn))(this,name,type,ndim,dims,unit,status); +} +void astAddParameter_( AstTable *this, const char *name, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Table,AddParameter))(this,name,status); +} +void astRemoveColumn_( AstTable *this, const char *name, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Table,RemoveColumn))(this,name,status); +} +void astRemoveParameter_( AstTable *this, const char *name, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Table,RemoveParameter))(this,name,status); +} +void astRemoveRow_( AstTable *this, int index, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Table,RemoveRow))(this,index,status); +} +void astPurgeRows_( AstTable *this, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Table,PurgeRows))(this,status); +} +int astGetNcolumn_( AstTable *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,GetNcolumn))( this, status ); +} +int astGetNparameter_( AstTable *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,GetNparameter))( this, status ); +} +const char *astColumnName_( AstTable *this, int index, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Table,ColumnName))(this,index,status); +} +const char *astParameterName_( AstTable *this, int index, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Table,ParameterName))(this,index,status); +} +int astGetColumnType_( AstTable *this, const char *column, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,GetColumnType))(this,column,status); +} +const char *astGetColumnUnit_( AstTable *this, const char *column, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Table,GetColumnUnit))(this,column,status); +} +int astGetColumnLenC_( AstTable *this, const char *column, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,GetColumnLenC))(this,column,status); +} +int astGetColumnLength_( AstTable *this, const char *column, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,GetColumnLength))(this,column,status); +} +int astGetColumnNdim_( AstTable *this, const char *column, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,GetColumnNdim))(this,column,status); +} +void astColumnShape_( AstTable *this, const char *column, int mxdim, + int *ndim, int *dims, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,Table,ColumnShape))( this, column, mxdim, ndim, + dims, status ); +} +AstKeyMap *astColumnProps_( AstTable *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Table,ColumnProps))(this,status); +} +AstKeyMap *astParameterProps_( AstTable *this, int *status ){ + if ( !astOK ) return NULL; + return (**astMEMBER(this,Table,ParameterProps))(this,status); +} +int astHasColumn_( AstTable *this, const char *column, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,HasColumn))(this,column,status); +} +int astHasParameter_( AstTable *this, const char *parameter, int *status ){ + if ( !astOK ) return 0; + return (**astMEMBER(this,Table,HasParameter))(this,parameter,status); +} diff --git a/table.h b/table.h new file mode 100644 index 0000000..f51690e --- /dev/null +++ b/table.h @@ -0,0 +1,309 @@ +#if !defined( TABLE_INCLUDED ) /* Include this file only once */ +#define TABLE_INCLUDED +/* +*+ +* Name: +* table.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the Table class. + +* Invocation: +* #include "table.h" + +* Description: +* This include file defines the interface to the Table class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The Table class inherits from the KeyMap class. + +* Copyright: +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-NOV-2010 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "keymap.h" /* Parent class */ + +#if defined(astCLASS) /* Protected */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) /* Protected */ + +/* Maximum length of a column name */ +#define AST__MXCOLNAMLEN 100 + +/* Maximum length of a key for a column cell */ +#define AST__MXCOLKEYLEN ( AST__MXCOLNAMLEN + 23 ) + +#endif + + +/* Type Definitions. */ +/* ================= */ +/* Table structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstTable { + +/* Attributes inherited from the parent class. */ + AstKeyMap keymap; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int nrow; /* Mo. of rows in table */ + AstKeyMap *columns; /* KeyMap holding column definitions */ + AstKeyMap *parameters; /* KeyMap holding parameter definitions */ +} AstTable; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstTableVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstKeyMapVtab keymap_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + AstKeyMap *(* ColumnProps)( AstTable *, int * ); + AstKeyMap *(* ParameterProps)( AstTable *, int * ); + const char *(* ColumnName)( AstTable *, int, int * ); + const char *(* ParameterName)( AstTable *, int, int * ); + const char *(* GetColumnUnit)( AstTable *, const char *, int * ); + int (* GetColumnLenC)( AstTable *, const char *, int * ); + int (* GetColumnLength)( AstTable *, const char *, int * ); + int (* GetColumnNdim)( AstTable *, const char *, int * ); + int (* GetColumnType)( AstTable *, const char *, int * ); + int (* GetNcolumn)( AstTable *, int * ); + int (* GetNparameter)( AstTable *, int * ); + int (* GetNrow)( AstTable *, int * ); + int (* HasColumn)( AstTable *, const char *, int * ); + int (* HasParameter)( AstTable *, const char *, int * ); + void (* AddColumn)( AstTable *, const char *, int, int, int *, const char *, int * ); + void (* AddParameter)( AstTable *, const char *, int * ); + void (* ColumnShape)( AstTable *, const char *, int, int *, int *, int * ); + void (* PurgeRows)( AstTable *, int * ); + void (* RemoveColumn)( AstTable *, const char *, int * ); + void (* RemoveParameter)( AstTable *, const char *, int * ); + void (* RemoveRow)( AstTable *, int, int * ); + void (* SetNrow)( AstTable *, int, int * ); +} AstTableVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstTableGlobals { + AstTableVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstTableGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitTableGlobals_( AstTableGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(Table) /* Check class membership */ +astPROTO_ISA(Table) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstTable *astTable_( const char *, int *, ...); +#else +AstTable *astTableId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstTable *astInitTable_( void *, size_t, int, AstTableVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitTableVtab_( AstTableVtab *, const char *, int * ); + +/* Loader. */ +AstTable *astLoadTable_( void *, size_t, AstTableVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astAddColumn_( AstTable *, const char *, int, int, int *, const char *, int * ); +void astAddParameter_( AstTable *, const char *, int * ); +void astRemoveColumn_( AstTable *, const char *, int * ); +void astRemoveParameter_( AstTable *, const char *, int * ); +void astRemoveRow_( AstTable *, int, int * ); +void astPurgeRows_( AstTable *, int * ); +const char *astColumnName_( AstTable *, int, int * ); +const char *astParameterName_( AstTable *, int, int * ); +void astColumnShape_( AstTable *, const char *, int, int *, int *, int * ); +int astHasColumn_( AstTable *, const char *, int * ); +int astHasParameter_( AstTable *, const char *, int * ); + +#if defined(astCLASS) /* Protected */ +AstKeyMap *astColumnProps_( AstTable *, int * ); +AstKeyMap *astParameterProps_( AstTable *, int * ); +const char *astGetColumnUnit_( AstTable *, const char *, int * ); +int astGetColumnLenC_( AstTable *, const char *, int * ); +int astGetColumnLength_( AstTable *, const char *, int * ); +int astGetColumnNdim_( AstTable *, const char *, int * ); +int astGetColumnType_( AstTable *, const char *, int * ); +int astGetNcolumn_( AstTable *, int * ); +int astGetNparameter_( AstTable *, int * ); +int astGetNrow_( AstTable *, int * ); +void astSetNrow_( AstTable *, int, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckTable(this) astINVOKE_CHECK(Table,this,0) +#define astVerifyTable(this) astINVOKE_CHECK(Table,this,1) + +/* Test class membership. */ +#define astIsATable(this) astINVOKE_ISA(Table,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astTable astINVOKE(F,astTable_) +#else +#define astTable astINVOKE(F,astTableId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitTable(mem,size,init,vtab,name) \ +astINVOKE(O,astInitTable_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitTableVtab(vtab,name) astINVOKE(V,astInitTableVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadTable(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadTable_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckTable to validate Table pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#define astAddColumn(this,name,type,ndim,dims,unit) astINVOKE(V,astAddColumn_(astCheckTable(this),name,type,ndim,dims,unit, STATUS_PTR)) +#define astAddParameter(this,name) astINVOKE(V,astAddParameter_(astCheckTable(this),name,STATUS_PTR)) +#define astRemoveColumn(this,name) astINVOKE(V,astRemoveColumn_(astCheckTable(this),name,STATUS_PTR)) +#define astRemoveParameter(this,name) astINVOKE(V,astRemoveParameter_(astCheckTable(this),name,STATUS_PTR)) +#define astRemoveRow(this,index) astINVOKE(V,astRemoveRow_(astCheckTable(this),index,STATUS_PTR)) +#define astPurgeRows(this) astINVOKE(V,astPurgeRows_(astCheckTable(this),STATUS_PTR)) +#define astColumnName(this,index) astINVOKE(V,astColumnName_(astCheckTable(this),index,STATUS_PTR)) +#define astParameterName(this,index) astINVOKE(V,astParameterName_(astCheckTable(this),index,STATUS_PTR)) +#define astColumnShape(this,column,mxdim,ndim,dims) astINVOKE(V,astColumnShape_(astCheckTable(this),column,mxdim,ndim,dims,STATUS_PTR)) +#define astHasColumn(this,column) astINVOKE(V,astHasColumn_(astCheckTable(this),column,STATUS_PTR)) +#define astHasParameter(this,param) astINVOKE(V,astHasParameter_(astCheckTable(this),param,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ + +#define astColumnProps(this) \ +astINVOKE(O,astColumnProps_(astCheckTable(this),STATUS_PTR)) +#define astParameterProps(this) \ +astINVOKE(O,astParameterProps_(astCheckTable(this),STATUS_PTR)) +#define astGetNcolumn(this) \ +astINVOKE(V,astGetNcolumn_(astCheckTable(this),STATUS_PTR)) +#define astGetNparameter(this) \ +astINVOKE(V,astGetNparameter_(astCheckTable(this),STATUS_PTR)) +#define astGetNrow(this) \ +astINVOKE(V,astGetNrow_(astCheckTable(this),STATUS_PTR)) +#define astSetNrow(this,value) \ +astINVOKE(V,astSetNrow_(astCheckTable(this),value,STATUS_PTR)) +#define astGetColumnLenC(this,column) \ +astINVOKE(V,astGetColumnLenC_(astCheckTable(this),column,STATUS_PTR)) +#define astGetColumnLength(this,column) \ +astINVOKE(V,astGetColumnLength_(astCheckTable(this),column,STATUS_PTR)) +#define astGetColumnNdim(this,column) \ +astINVOKE(V,astGetColumnNdim_(astCheckTable(this),column,STATUS_PTR)) +#define astGetColumnType(this,column) \ +astINVOKE(V,astGetColumnType_(astCheckTable(this),column,STATUS_PTR)) +#define astGetColumnUnit(this,column) \ +astINVOKE(V,astGetColumnUnit_(astCheckTable(this),column,STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/templateclass.README b/templateclass.README new file mode 100644 index 0000000..914834e --- /dev/null +++ b/templateclass.README @@ -0,0 +1,29 @@ +How to add a new public class to AST: + +1) Make copies of the three files templateclass.c templateclass.h and + ftemplateclass.c, replacing "templateclass" in the file name by the + lower case name of the new class. + +2) Edit each of the files created above and do the following: + - Replace TemplateClass with capitalised class name + - Replace templateclass with lower case class name + - Replace TEMPLATECLASS with upper case class name + - Replace TemplateParent with capitalised parent class name + - Replace templateparent with lower case parent class name + - Replace all occurrences of >>> with suitable text + - Add all the classes new functionality + +3) Add the three new files to CVS + +4) Edit the following files to include reference to the new class + - ast_par.source + - builddocs.in + - loader.c + - Makefile.am + - sun_master.tex + - ast.news + +5) Add a test program to the ast_tester script in the ast_tester directory + +6) Commit all changes to CVS. + diff --git a/templateclass.c b/templateclass.c new file mode 100644 index 0000000..395f63c --- /dev/null +++ b/templateclass.c @@ -0,0 +1,1483 @@ +1 - Replace TemplateClass with capitalised class name +2 - Replace templateclass with lower case class name +3 - Replace TEMPLATECLASS with upper case class name +4 - Replace TemplateParent with capitalised parent class name +5 - Replace templateparent with lower case parent class name +6 - Replace all occurrences of >>> with suitable text + +/* +*class++ +* Name: +* TemplateClass + +* Purpose: +* >>> purpose + +* Constructor Function: +c astTemplateClass +f AST_TEMPLATECLASS + +* Description: +* A TemplateClass is a >>> + +* Inheritance: +* The TemplateClass class inherits from the TemplateParent class. + +* Attributes: +* In addition to those attributes common to all TemplateParents, every +* TemplateClass also has the following attributes: +* +* >>> Describe new attributes +* - AlignStdOfRest: Standard of rest in which to align TemplateClasss + +* Functions: +c In addition to those functions applicable to all TemplateParents, the +c following functions may also be applied to all TemplateClasss: +f In addition to those routines applicable to all TemplateParents, the +f following routines may also be applied to all TemplateClasss: +* +* >>> Describe new functions +c - astSetRefPos: Set reference position in any celestial system +f - AST_SETREFPOS: Set reference position in any celestial system + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* >>> 4-NOV-2002 (DSB): +* Original version. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS TemplateClass + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "templateclass.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ +/* Define the class virtual function table and its initialisation flag as + static variables. */ +static AstTemplateClassVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +/* Pointers to parent class methods which are used or extended by this + class. */ +static int (* parent_getobjsize)( AstObject * ); +static const char *(* parent_getattrib)( AstObject *, const char * ); +static int (* parent_testattrib)( AstObject *, const char * ); +static void (* parent_clearattrib)( AstObject *, const char * ); +static void (* parent_setattrib)( AstObject *, const char * ); +static int (* parent_equal)( AstObject *, AstObject * ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static int Equal( AstObject *, AstObject * ); +static int GetObjSize( AstObject * ); +static void Copy( const AstObject *, AstObject * ); +static void Delete( AstObject * ); +static void Dump( AstObject *, AstChannel * ); + +static const char *GetAttrib( AstObject *, const char * ); +static int TestAttrib( AstObject *, const char * ); +static void ClearAttrib( AstObject *, const char * ); +static void SetAttrib( AstObject *, const char * ); + + +/* Member functions. */ +/* ================= */ + +static void ClearAttrib( AstObject *this_object, const char *attrib ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a TemplateClass. + +* Type: +* Private function. + +* Synopsis: +* #include "templateclass.h" +* void ClearAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* TemplateClass member function (over-rides the astClearAttrib protected +* method inherited from the TemplateParent class). + +* Description: +* This function clears the value of a specified attribute for a +* TemplateClass, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the TemplateClass. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstTemplateClass *this; /* Pointer to the TemplateClass structure */ + char *new_attrib; /* Pointer value to new attribute name */ + int len; /* Length of attrib string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TemplateClass structure. */ + this = (AstTemplateClass *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +>>> rEPLACE WITH NEW ATTRIBUTES + +/* AlignStdOfRest. */ +/* --------------- */ + if ( !strcmp( attrib, "alignstdofrest" ) ) { + astClearAlignStdOfRest( this ); + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in which + TemplateClass had GeoLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( !strcmp( attrib, "geolat" ) ) { + astClearAttrib( this, "obslat" ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two TemplateClasss are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "templateclass.h" +* int Equal( AstObject *this, AstObject *that ) + +* Class Membership: +* TemplateClass member function (over-rides the astEqual protected +* method inherited from the TemplateParent Object class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two TemplateClasss are equivalent. + +* Parameters: +* this +* Pointer to the first TemplateClass. +* that +* Pointer to the second TemplateClass. + +* Returned Value: +* One if the TemplateClasss are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTemplateClass *that; /* Pointer to the second TemplateClass structure */ + AstTemplateClass *this; /* Pointer to the first TemplateClass structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent TemplateParent class. This checks + that the TemplateParents are both of the same class (amongst other things). */ + if( (*parent_equal)( this_object, that_object ) ) { + +/* Obtain pointers to the two TemplateClass structures. */ + this = (AstTemplateClass *) this_object; + that = (AstTemplateClass *) that_object; +>>> + + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "templateclass.h" +* int GetObjSize( AstObject *this ) + +* Class Membership: +* TemplateClass member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied TemplateClass, +* in bytes. + +* Parameters: +* this +* Pointer to the TemplateClass. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTemplateClass *this; /* Pointer to TemplateClass structure */ + int result; /* Result value to return */ + int i; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the TemplateClass structure. */ + this = (AstTemplateClass *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object ); + +>>> + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a TemplateClass. + +* Type: +* Private function. + +* Synopsis: +* #include "templateclass.h" +* const char *GetAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* TemplateClass member function (over-rides the protected astGetAttrib +* method inherited from the TemplateParent class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a TemplateClass, formatted as a character string. + +* Parameters: +* this +* Pointer to the TemplateClass. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the TemplateClass, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the TemplateClass. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Constants: */ +#define BUFF_LEN 50 /* Max. characters in result buffer */ + +/* Local Variables: */ + AstTemplateClass *this; /* Pointer to the TemplateClass structure */ + char *new_attrib; /* Pointer value to new attribute name */ + const char *result; /* Pointer value to return */ + double dval; /* Attribute value */ + int ival; /* Attribute value */ + int len; /* Length of attrib string */ + static char buff[ BUFF_LEN + 1 ]; /* Buffer for string result */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TemplateClass structure. */ + this = (AstTemplateClass *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +>>> + +/* AlignStdOfRest. */ +/* --------------- */ +/* Obtain the AlignStdOfRest code and convert to a string. */ + if ( !strcmp( attrib, "alignstdofrest" ) ) { + sor = astGetAlignStdOfRest( this ); + if ( astOK ) { + result = StdOfRestString( sor ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid AlignStdOfRest " + "identification code (%d).", astGetClass( this ), + astGetClass( this ), (int) sor ); + } + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib ); + } + +/* Return the result. */ + return result; + +/* Undefine macros local to this function. */ +#undef BUFF_LEN +} + +void astInitTemplateClassVtab_( AstTemplateClassVtab *vtab, const char *name ) { +/* +*+ +* Name: +* astInitTemplateClassVtab + +* Purpose: +* Initialise a virtual function table for a TemplateClass. + +* Type: +* Protected function. + +* Synopsis: +* #include "templateclass.h" +* void astInitTemplateClassVtab( AstTemplateClassVtab *vtab, const char *name ) + +* Class Membership: +* TemplateClass vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the TemplateClass class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + AstTemplateParentVtab *templateparent; /* Pointer to TemplateParent component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitTemplateParentVtab( (AstTemplateParentVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsATemplateClass) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_init variable to generate this unique value. */ + vtab->check = &class_init; + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ +>>> + vtab->GetRefPos = GetRefPos; + vtab->SetRefPos = SetRefPos; + + vtab->ClearAlignStdOfRest = ClearAlignStdOfRest; + vtab->TestAlignStdOfRest = TestAlignStdOfRest; + vtab->GetAlignStdOfRest = GetAlignStdOfRest; + vtab->SetAlignStdOfRest = SetAlignStdOfRest; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + templateparent = (AstTemplateParentVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + +>>> + +/* Store replacement pointers for methods which will be over-ridden by new + member functions implemented here. */ +>>> + templateparent->GetActiveUnit = GetActiveUnit; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "TemplateClass", + ">>>Description of class" ); + +} + +static void SetAttrib( AstObject *this_object, const char *setting ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a TemplateClass. + +* Type: +* Private function. + +* Synopsis: +* #include "templateclass.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* TemplateClass member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a TemplateClass, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the TemplateClass. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. + +* Returned Value: +* void + +* Notes: +* This protected method is intended to be invoked by the Object astSet +* method and makes additional attributes accessible to it. +*/ + +/* Local Vaiables: */ + AstTemplateClass *this; /* Pointer to the TemplateClass structure */ + char *a; /* Pointer to next character */ + char *new_setting; /* Pointer value to new attribute setting */ + double dval; /* Double atribute value */ + double dtemp; /* Temporary double atribute value */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int ulen; /* Used length of setting string */ + int namelen; /* Length of attribute name in setting */ + int nc; /* Number of characters read by astSscanf */ + int off; /* Offset of attribute value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TemplateClass structure. */ + this = (AstTemplateClass *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Obtain the used length of the setting string. */ + ulen = astChrLen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +>>> + +/* AlignStdOfRest. */ +/* --------------- */ + if ( nc = 0, + ( 0 == astSscanf( setting, "alignstdofrest=%n%*s %n", &off, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a StdOfRest code before use. */ + sor = StdOfRestCode( setting + off ); + if ( sor != AST__BADSOR ) { + astSetAlignStdOfRest( this, sor ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid standard of rest " + "description \"%s\".", astGetClass( this ), setting+off ); + } + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in which + TemplateClass had GeoLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "geolat=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + new_setting = astStore( NULL, setting, len + 1 ); + new_setting[ 0 ] = 'o'; + new_setting[ 1 ] = 'b'; + new_setting[ 2 ] = 's'; + astSetAttrib( this, new_setting ); + new_setting = astFree( new_setting ); + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting ); + } +} + +static int TestAttrib( AstObject *this_object, const char *attrib ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a TemplateClass. + +* Type: +* Private function. + +* Synopsis: +* #include "templateclass.h" +* int TestAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* TemplateClass member function (over-rides the astTestAttrib protected +* method inherited from the TemplateParent class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a TemplateClass's attributes. + +* Parameters: +* this +* Pointer to the TemplateClass. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTemplateClass *this; /* Pointer to the TemplateClass structure */ + char *new_attrib; /* Pointer value to new attribute name */ + int len; /* Length of attrib string */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TemplateClass structure. */ + this = (AstTemplateClass *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +>>> + +/* AlignStdOfRest. */ +/* --------------- */ + if ( !strcmp( attrib, "alignstdofrest" ) ) { + result = astTestAlignStdOfRest( this ); + +/* GeoLat. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in which + TemplateClass had GeoLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( !strcmp( attrib, "geolat" ) ) { + result = astTestAttrib( this, "obslat" ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib ); + } + +/* Return the result, */ + return result; +} + + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* + +>>> Add descriptions and accessors for all new attributes + +*att++ +* Name: +* AlignSpecOffset + +* Purpose: +* Align TemplateClasss using the offset coordinate system? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute is a boolean value which controls how a TemplateClass +* behaves when it is used (by +c astFindTemplateParent or astConvert) as a template to match another (target) +f AST_FINDFRAME or AST_CONVERT) as a template to match another (target) +* TemplateClass. It determines whether alignment occurs between the offset +* values defined by the current value of the SpecOffset attribute, or +* between the corresponding absolute spectral values. +* +* The default value of zero results in the two TemplateClasss being aligned +* so that a given absolute spectral value in one is mapped to the same +* absolute value in the other. A non-zero value results in the TemplateClasss +* being aligned so that a given offset value in one is mapped to the same +* offset value in the other. + +* Applicability: +* TemplateClass +* All TemplateClasss have this attribute. +*att-- +*/ +astMAKE_CLEAR(TemplateClass,AlignSpecOffset,alignspecoffset,-INT_MAX) +astMAKE_GET(TemplateClass,AlignSpecOffset,int,0,( ( this->alignspecoffset != -INT_MAX ) ? + this->alignspecoffset : 0 )) +astMAKE_SET(TemplateClass,AlignSpecOffset,int,alignspecoffset,( value != 0 )) +astMAKE_TEST(TemplateClass,AlignSpecOffset,( this->alignspecoffset != -INT_MAX )) + + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for TemplateClass objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout ) + +* Description: +* This function implements the copy constructor for TemplateClass objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstTemplateClass *in; /* Pointer to input TemplateClass */ + AstTemplateClass *out; /* Pointer to output TemplateClass */ + char *usedunit; /* Pointer to an element of usedunits array */ + int i; /* Loop count */ + int nused; /* Size of "usedunits" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output TemplateClasss. */ + in = (AstTemplateClass *) objin; + out = (AstTemplateClass *) objout; + +/* Nullify the pointers stored in the output object since these will + currently be pointing at the input data (since the output is a simple + byte-for-byte copy of the input). Otherwise, the input data could be + freed by accidient if the output object is deleted due to an error + occuring in this function. */ + out->usedunits = NULL; + +>>> + +/* If an error has occurred, free the output resources. */ + if( !astOK ) Delete( (AstObject *) out ); + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for TemplateClass objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj ) + +* Description: +* This function implements the destructor for TemplateClass objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstTemplateClass *this; + int i; + +/* Release the memory referred to in the TemplateClass structure. */ + this = (AstTemplateClass *) obj; + if( this ) { +>>> + } +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for TemplateClass objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel ) + +* Description: +* This function implements the Dump function which writes out data +* for the TemplateClass class to an output Channel. + +* Parameters: +* this +* Pointer to the TemplateClass whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +*/ + +/* Local Variables: */ + AstTemplateClass *this; /* Pointer to the TemplateClass structure */ + char buff[ 20 ]; /* Buffer for item name */ + char comm[ 50 ]; /* Buffer for comment */ + const char *sval; /* Pointer to string value */ + double dval; /* Double value */ + int i; /* Loop count */ + int ival; /* int value */ + int j; /* Loop count */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TemplateClass structure. */ + this = (AstTemplateClass *) this_object; + +/* Write out values representing the instance variables for the + TemplateClass class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +>>> + +/* StdOfRest. */ +/* ---------- */ + set = TestStdOfRest( this ); + sor = set ? GetStdOfRest( this ) : astGetStdOfRest( this ); + +/* If set, convert explicitly to a string for the external + representation. */ + sval = ""; + if ( set ) { + if ( astOK ) { + sval = StdOfRestString( sor ); + +/* Report an error if the StdOfRest value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "%s(%s): Corrupt %s contains invalid standard of rest " + "identification code (%d).", "astWrite", + astGetClass( channel ), astGetClass( this ), (int) sor ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "stdofrest" ); + } + +/* Write out the value. */ + astWriteString( channel, "SoR", set, 1, sval, "Standard of rest" ); + + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsATemplateClass and astCheckTemplateClass functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(TemplateClass,TemplateParent,check,&class_init) +astMAKE_CHECK(TemplateClass) + + +>>> Change constructor argument list +AstTemplateClass *astTemplateClass_( >>> const char *options, ... ) { +/* +*+ +* Name: +* astTemplateClass + +* Purpose: +* Create a TemplateClass. + +* Type: +* Protected function. + +* Synopsis: +* #include "templateclass.h" +* AstTemplateClass *astTemplateClass( >>> const char *options, ... ) + +* Class Membership: +* TemplateClass constructor. + +* Description: +* This function creates a new TemplateClass and optionally initialises its +* attributes. + +* Parameters: +>>> +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new TemplateClass. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new TemplateClass. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic TemplateClass constructor which +* is available via the protected interface to the TemplateClass class. +* A public interface is provided by the astTemplateClassId_ function. +*/ + +/* Local Variables: */ + AstTemplateClass *new; /* Pointer to new TemplateClass */ + va_list args; /* Variable argument list */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the TemplateClass, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitTemplateClass( NULL, sizeof( AstTemplateClass ), !class_init, + &class_vtab, "TemplateClass" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new TemplateClass's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new TemplateClass. */ + return new; +} + +AstTemplateClass *astInitTemplateClass_( void *mem, size_t size, int init, + AstTemplateClassVtab *vtab, const char *name, + >>>) { +/* +*+ +* Name: +* astInitTemplateClass + +* Purpose: +* Initialise a TemplateClass. + +* Type: +* Protected function. + +* Synopsis: +* #include "templateclass.h" +* AstTemplateClass *astInitTemplateClass( void *mem, size_t size, int init, +* AstTemplateParentVtab *vtab, const char *name, +* >>> ) + +* Class Membership: +* TemplateClass initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new TemplateClass object. It allocates memory (if +* necessary) to accommodate the TemplateClass plus any additional data +* associated with the derived class. It then initialises a +* TemplateClass structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual function +* table for a TemplateClass at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the TemplateClass is to be +* created. This must be of sufficient size to accommodate the +* TemplateClass data (sizeof(TemplateClass)) plus any data used by +* the derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the TemplateClass (plus derived +* class data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also stored +* in the TemplateClass structure, so a valid value must be supplied +* even if not required for allocating memory. +* init +* A logical flag indicating if the TemplateClass's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new TemplateClass. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object belongs +* (it is this pointer value that will subsequently be returned by +* the astGetClass method). +>>> + +* Returned Value: +* A pointer to the new TemplateClass. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstTemplateClass *new; /* Pointer to the new TemplateClass */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitTemplateClassVtab( vtab, name ); + +/* Initialise a 1D TemplateParent structure (the parent class) as the first component + within the TemplateClass structure, allocating memory if necessary. */ + new = (AstTemplateClass *) astInitTemplateParent( mem, size, 0, + (AstTemplateParentVtab *) vtab, + name, >>> 1 ); + + if ( astOK ) { + +/* Initialise the TemplateClass data. */ +/* ----------------------------- */ +/* Initialise all attributes to their "undefined" values. */ +>>> + new->alignstdofrest = AST__BADSOR; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + + } + +/* Return a pointer to the new object. */ + return new; +} + +AstTemplateClass *astLoadTemplateClass_( void *mem, size_t size, + AstTemplateClassVtab *vtab, + const char *name, AstChannel *channel ) { +/* +*+ +* Name: +* astLoadTemplateClass + +* Purpose: +* Load a TemplateClass. + +* Type: +* Protected function. + +* Synopsis: +* #include "templateclass.h" +* AstTemplateClass *astLoadTemplateClass( void *mem, size_t size, +* AstTemplateClassVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* TemplateClass loader. + +* Description: +* This function is provided to load a new TemplateClass using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* TemplateClass structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the TemplateClass is to be +* loaded. This must be of sufficient size to accommodate the +* TemplateClass data (sizeof(TemplateClass)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the TemplateClass (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the TemplateClass structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstTemplateClass) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new TemplateClass. If this is NULL, a pointer +* to the (static) virtual function table for the TemplateClass class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "TemplateClass" is used instead. + +* Returned Value: +* A pointer to the new TemplateClass. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstTemplateClass *new; /* Pointer to the new TemplateClass */ + char buff[ 20 ]; /* Buffer for item name */ + char *sval; /* Pointer to string value */ + int i; /* Loop count */ + int j; /* Loop count */ + int nc; /* String length */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this TemplateClass. In this case the + TemplateClass belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstTemplateClass ); + vtab = &class_vtab; + name = "TemplateClass"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitTemplateClassVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built TemplateClass. */ + new = astLoadTemplateParent( mem, size, (AstTemplateParentVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "TemplateClass" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +>>> + +/* StdOfRest. */ +/* ---------- */ +/* Set the default and read the external representation as a string. */ + new->stdofrest = AST__BADSOR; + sval = astReadString( channel, "sor", NULL ); + +/* If a value was read, convert from a string to a StdOfRest code. */ + if ( sval ) { + if ( astOK ) { + new->stdofrest = StdOfRestCode( sval ); + +/* Report an error if the value wasn't recognised. */ + if ( new->stdofrest == AST__BADSOR ) { + astError( AST__ATTIN, + "astRead(%s): Invalid standard of rest description " + "\"%s\".", astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* If an error occurred, clean up by deleting the new TemplateClass. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new TemplateClass pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +>>> +void astGetRefPos_( AstTemplateClass *this, AstSkyTemplateParent *frm, double *lon, + double *lat ){ + if ( !astOK ) return; + (**astMEMBER(this,TemplateClass,GetRefPos))(this,frm,lon,lat); +} + + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstTemplateClass *astTemplateClassId_( >>> const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstTemplateClass *astTemplateClassId_( >>> const char *options, ... ) { +/* +*++ +* Name: +c astTemplateClass +f AST_TEMPLATECLASS + +* Purpose: +* Create a TemplateClass. + +* Type: +* Public function. + +* Synopsis: +c #include "templateclass.h" +c AstTemplateClass *astTemplateClass( >>> const char *options, ... ) +f RESULT = AST_TEMPLATECLASS( >>> OPTIONS, STATUS ) + +* Class Membership: +* TemplateClass constructor. + +* Description: +* This function creates a new TemplateClass and optionally initialises +* its attributes. +* +* A TemplateClass is a >>> (copy from class prologue at top of file) + +* Parameters: +>>> +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new TemplateClass. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new TemplateClass. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTemplateClass() +f AST_TEMPLATECLASS = INTEGER +* A pointer to the new TemplateClass. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astTemplateClass constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astTemplateClass_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astTemplateClass_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. +*/ + +/* Local Variables: */ + AstTemplateClass *new; /* Pointer to new TemplateClass */ + va_list args; /* Variable argument list */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the TemplateClass, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitTemplateClass( NULL, sizeof( AstTemplateClass ), !class_init, + &class_vtab, "TemplateClass" >>> ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new TemplateClass's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new TemplateClass. */ + return astMakeId( new ); +} + + + + diff --git a/templateclass.h b/templateclass.h new file mode 100644 index 0000000..070e84b --- /dev/null +++ b/templateclass.h @@ -0,0 +1,216 @@ +1 - Replace TemplateClass with capitalised class name +2 - Replace templateclass with lower case class name +3 - Replace TEMPLATECLASS with upper case class name +4 - Replace TemplateParent with capitalised parent class name +5 - Replace templateparent with lower case parent class name +6 - Replace all occurrences of >>> with suitable text + +#if !defined( TEMPLATECLASS_INCLUDED ) /* Include this file only once */ +#define TEMPLATECLASS_INCLUDED +/* +*+ +* Name: +* templateclass.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the TemplateClass class. + +* Invocation: +* #include "templateclass.h" + +* Description: +* This include file defines the interface to the TemplateClass class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. + +* Copyright: +* Copyright (C) 2007 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* >>> 12-NOV-2002 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "templateparent.h" /* Parent TemplateParent class */ + +/* Macros. */ +/* ======= */ + +#if defined(astCLASS) /* Protected */ + +#endif + +/* Type Definitions. */ +/* ================= */ + +/* TemplateClass structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstTemplateClass { + +/* Attributes inherited from the parent class. */ + AstTemplateParent templateparent; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ +>>> + +} AstTemplateClass; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstTemplateClassVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstTemplateParentVtab templateparent_vtab; /* Parent class virtual function table */ + +/* Unique flag value to determine class membership. */ + int *check; /* Check value */ + +/* Properties (e.g. methods) specific to this class. */ +>>> + double (* GetNewAttr)( AstTemplateClass * ); + int (* TestNewAttr)( AstTemplateClass * ); + void (* ClearNewAttr)( AstTemplateClass * ); + void (* SetNewAttr)( AstTemplateClass *, double ); + + +} AstTemplateClassVtab; +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(TemplateClass) /* Check class membership */ +astPROTO_ISA(TemplateClass) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstTemplateClass *astTemplateClass_( >>> const char *, ... ); +#else +AstTemplateClass *astTemplateClassId_( >>> const char *, ... ); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstTemplateClass *astInitTemplateClass_( void *, size_t, int, + AstTemplateClassVtab *, + const char * >>> ); + +/* Vtab initialiser. */ +void astInitTemplateClassVtab_( AstTemplateClassVtab *, const char * ); + +/* Loader. */ +AstTemplateClass *astLoadTemplateClass_( void *, size_t, + AstTemplateClassVtab *, + const char *, AstChannel *channel ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ + +#if defined(astCLASS) /* Protected */ + + +>>> +double astGetNewAttr_( AstTemplateClass * ); +int astTestNewAttr_( AstTemplateClass * ); +void astClearNewAttr_( AstTemplateClass * ); +void astSetNewAttr_( AstTemplateClass *, double ); + + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckTemplateClass(this) astINVOKE_CHECK(TemplateClass,this) + +/* Test class membership. */ +#define astIsATemplateClass(this) astINVOKE_ISA(TemplateClass,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astTemplateClass astINVOKE(F,astTemplateClass_) +#else +#define astTemplateClass astINVOKE(F,astTemplateClassId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitTemplateClass(mem,size,init,vtab,name>>>) \ +astINVOKE(O,astInitTemplateClass_(mem,size,init,vtab,name>>>)) + +/* Vtab Initialiser. */ +#define astInitTemplateClassVtab(vtab,name) astINVOKE(V,astInitTemplateClassVtab_(vtab,name)) +/* Loader. */ +#define astLoadTemplateClass(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadTemplateClass_(mem,size,vtab,name,astCheckChannel(channel))) + +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +>>> + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +/* Here we make use of astCheckTemplateClass to validate TemplateClass pointers + before use. This provides a contextual error report if a pointer to + the wrong sort of object is supplied. */ + +#if defined(astCLASS) /* Protected */ + +#define astGetNewAttr(this) astINVOKE(V,astGetNewAttr_(astCheckTemplateClass(this))) +#define astTestNewAttr(this) astINVOKE(V,astTestNewAttr_(astCheckTemplateClass(this))) +#define astClearNewAttr(this) astINVOKE(V,astClearNewAttr_(astCheckTemplateClass(this))) +#define astSetNewAttr(this,value) astINVOKE(V,astSetNewAttr_(astCheckTemplateClass(this),value)) + +#endif +#endif diff --git a/timeframe.c b/timeframe.c new file mode 100644 index 0000000..c3dae88 --- /dev/null +++ b/timeframe.c @@ -0,0 +1,7481 @@ +/* +*class++ +* Name: +* TimeFrame + +* Purpose: +* Time coordinate system description. + +* Constructor Function: +c astTimeFrame +f AST_TIMEFRAME + +* Description: +* A TimeFrame is a specialised form of one-dimensional Frame which +* represents various coordinate systems used to describe positions in +* time. +* +* A TimeFrame represents a moment in time as either an Modified Julian +* Date (MJD), a Julian Date (JD), a Besselian epoch or a Julian epoch, +* as determined by the System attribute. Optionally, a zero point can be +* specified (using attribute TimeOrigin) which results in the TimeFrame +* representing time offsets from the specified zero point. +* +* Even though JD and MJD are defined as being in units of days, the +* TimeFrame class allows other units to be used (via the Unit attribute) +* on the basis of simple scalings (60 seconds = 1 minute, 60 minutes = 1 +* hour, 24 hours = 1 day, 365.25 days = 1 year). Likewise, Julian epochs +* can be described in units other than the usual years. Besselian epoch +* are always represented in units of (tropical) years. +* +* The TimeScale attribute allows the time scale to be specified (that +* is, the physical process used to define the rate of flow of time). +* MJD, JD and Julian epoch can be used to represent a time in any +* supported time scale. However, Besselian epoch may only be used with the +* "TT" (Terrestrial Time) time scale. The list of supported time scales +* includes universal time and siderial time. Strictly, these represent +* angles rather than time scales, but are included in the list since +* they are in common use and are often thought of as time scales. +* +* When a time value is formatted it can be formated either as a simple +* floating point value, or as a Gregorian date (see the Format +* attribute). + +* Inheritance: +* The TimeFrame class inherits from the Frame class. + +* Attributes: +* In addition to those attributes common to all Frames, every +* TimeFrame also has the following attributes: +* +* - AlignTimeScale: Time scale in which to align TimeFrames +* - LTOffset: The offset of Local Time from UTC, in hours. +* - TimeOrigin: The zero point for TimeFrame axis values +* - TimeScale: The timescale used by the TimeFrame +* +* Several of the Frame attributes inherited by the TimeFrame class +* refer to a specific axis of the Frame (for instance Unit(axis), +* Label(axis), etc). Since a TimeFrame is strictly one-dimensional, +* it allows these attributes to be specified without an axis index. +* So for instance, "Unit" is allowed in place of "Unit(1)". + +* Functions: +c In addition to those functions applicable to all Frames, the +c following functions may also be applied to all TimeFrames: +f In addition to those routines applicable to all Frames, the +f following routines may also be applied to all TimeFrames: +* +c - astCurrentTime: Return the current system time +f - AST_CURRENTTIME: Return the current system time + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* NG: Norman Gray (Starlink) +* DSB: David Berry (Starlink) + +* History: +* XX-Aug-2003 (NG): +* Original version, drawing heavily on specframe.c. +* 20-MAY-2005 (NG): +* Merged into main AST system. +* 25-MAY-2005 (DSB): +* Extensive modifications to add extra timescales, unit support, +* support for relative times, etc, and to make it more like the +* other AST Frame classes. +* 12-AUG-2005 (DSB): +* Remove ClockLon and ClockLat attributes. Use the new ObsLon and +* ObsLat attributes in the parent Frame class instead. Note, for +* backward compatibility the public attribute accessors and the +* astLoadTimeFrame functions still recogonise ClockLon and ClockLat, +* but use the ObsLat/ObsLon attributes internally. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 29-JUN-2006 (DSB): +* - Activate astAbbrev function for abbreviating leading fields in +* plot labels. +* - Include TimeOrigin in default Label. +* 30-JUN-2006 (DSB): +* When splitting a date/time string into fields, allow each field +* to include a decimal point. +* 30-JUN-2006 (DSB): +* Allow astAbbrev to have a null "str1" value. +* 16-OCT-2006 (DSB): +* Allow conversions between UTC and UT1 (using the new Frame attribute +* 1-NOV-2006 (DSB): +* Correct sign of longitude passed to TimeMap contrutcorss in +* function MakeMap. +* 31-JAN-2007 (DSB): +* Modified so that a TimeFrame can be used as a template to find a +* TimeFrame contained within a CmpFrame. This involves changes in +* Match and the removal of the local versions of SetMaxAxes and +* SetMinAxes. +* 3-SEP-2007 (DSB): +* In SubFrame, since AlignSystem is extended by the TimeFrame class +* it needs to be cleared before invoking the parent SubFrame +* method in cases where the result Frame is not a TimeFrame. +* 2-OCT-2007 (DSB): +* In Overlay, clear AlignSystem as well as System before calling +* the parent overlay method. +* 2-OCT-2007 (DSB): +* Added "LT" (Local Time) time scale. +* 9-DEC-2008 (DSB): +* Ensure Format string pointer is used correctly. +* 19-JAN-2009 (DSB): +* Ensure "" is returned by astFormat if the axis value is bad. +* 31-MAR-2009 (DSB): +* Extend TimeFrame "iso" Format to allow it to specify the character to +* place between the time and date strings. +* 15-APR-2009 (DSB): +* Increase the number of nice calendar time axis gaps allowed by +* the Gap function. Previously, there was a jump from 1 day to 1 +* year making it difficult to plot calendar axes covering time +* ranges of the order of 0.5 to 2 years. Now, nice numbers of days +* are allowed as intermediate gaps. Since months do not all have +* the same number of days, this means that the day number at major +* ticks will bounce around a bit. +* 29-APR-2011 (DSB): +* Prevent astFindFrame from matching a subclass template against a +* superclass target. +* 16-APR-2015 (DSB): +* Add more choices when chosing gaps on time axes. +* 17-APR-2015 (DSB): +* - Added Centre. +* - Remove some "set but unused" variables. +* 21-APR-2016 (DSB): +* - Over-ride astFields. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS TimeFrame + +/* Define the first and last acceptable System values. */ +#define FIRST_SYSTEM AST__MJD +#define LAST_SYSTEM AST__BEPOCH + +/* Define the first and last acceptable TimeScale values. */ +#define FIRST_TS AST__TAI +#define LAST_TS AST__LT + +/* The supported time scales fall into two groups. Time scales in the + first group depend on the clock position. That is, transformation + between a time scale in one group and a timescale in the other group + requires the clock position, as does transformation between two time + scales within the first group. Define a macro which tests if a given + timescale belongs to the first group. */ +#define CLOCK_SCALE(ts) \ + ( ( ts == AST__LMST || \ + ts == AST__LAST || \ + ts == AST__TDB || \ + ts == AST__TCB ) ? 1 : 0 ) + + +/* Define a macro which tests if a given timescale requires a Dut1 value + in order to convert from the timescale to UTC. */ +#define DUT1_SCALE(ts) \ + ( ( ts == AST__LMST || \ + ts == AST__LAST || \ + ts == AST__GMST || \ + ts == AST__UT1 ) ? 1 : 0 ) + +/* Define a macro which tests if a given timescale requires a LTOffset value + in order to convert from the timescale to UTC. */ +#define LTOFFSET_SCALE(ts) \ + ( ( ts == AST__LT ) ? 1 : 0 ) + +/* The Unix epoch (00:00:00 UTC 1 January 1970 AD) as an absolute MJD in + the UTC timescale. */ +#define UNIX_EPOCH 40587.0 + +/* Header files. */ +/* ============= */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "unit.h" /* Units management facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "timemap.h" /* Time coordinate Mappings */ +#include "frame.h" /* Parent Frame class */ +#include "timeframe.h" /* Interface definition for this class */ +#include "mapping.h" /* Coordinate Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "shiftmap.h" /* Shift of origins */ +#include "pal.h" /* SlaLib interface */ + + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are used or extended by this + class. */ +static AstSystemType (* parent_getalignsystem)( AstFrame *, int * ); +static AstSystemType (* parent_getsystem)( AstFrame *, int * ); +static double (* parent_centre)( AstFrame *, int, double, double, int * ); +static double (* parent_gap)( AstFrame *, int, double, int *, int * ); +static const char *(* parent_abbrev)( AstFrame *, int, const char *, const char *, const char *, int * ); +static const char *(* parent_format)( AstFrame *, int, double, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static const char *(* parent_getdomain)( AstFrame *, int * ); +static const char *(* parent_getlabel)( AstFrame *, int, int * ); +static const char *(* parent_getsymbol)( AstFrame *, int, int * ); +static const char *(* parent_gettitle)( AstFrame *, int * ); +static const char *(* parent_getunit)( AstFrame *, int, int * ); +static double (* parent_getepoch)( AstFrame *, int * ); +static int (* parent_fields)( AstFrame *, int, const char *, const char *, int, char **, int *, double *, int * ); +static int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static int (* parent_unformat)( AstFrame *, int, const char *, double *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_clearsystem)( AstFrame *, int * ); +static void (* parent_overlay)( AstFrame *, const int *, AstFrame *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static void (* parent_setsystem)( AstFrame *, AstSystemType, int * ); +static void (* parent_setunit)( AstFrame *, int, const char *, int * ); + +/* The Unix epoch (00:00:00 UTC 1 January 1970 AD) as an absolute MJD in + the TAI timescale. */ +static double tai_epoch; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->Format_Buff[ 0 ] = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetLabel_Buff[ 0 ] = 0; \ + globals->GetSymbol_Buff[ 0 ] = 0; \ + globals->GetTitle_Buff[ 0 ] = 0; \ + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(TimeFrame) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(TimeFrame,Class_Init) +#define class_vtab astGLOBAL(TimeFrame,Class_Vtab) +#define format_buff astGLOBAL(TimeFrame,Format_Buff) +#define getattrib_buff astGLOBAL(TimeFrame,GetAttrib_Buff) +#define getlabel_buff astGLOBAL(TimeFrame,GetLabel_Buff) +#define getsymbol_buff astGLOBAL(TimeFrame,GetSymbol_Buff) +#define gettitle_buff astGLOBAL(TimeFrame,GetTitle_Buff) + + + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Buffers for strings returned by various functions. */ +static char getattrib_buff[ AST__TIMEFRAME_GETATTRIB_BUFF_LEN + 1 ]; +static char format_buff[ AST__TIMEFRAME_FORMAT_BUFF_LEN + 1 ]; +static char getlabel_buff[ AST__TIMEFRAME_GETLABEL_BUFF_LEN + 1 ]; +static char getsymbol_buff[ AST__TIMEFRAME_GETSYMBOL_BUFF_LEN + 1 ]; +static char gettitle_buff[ AST__TIMEFRAME_GETTITLE_BUFF_LEN + 1 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstTimeFrameVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *MakeMap( AstTimeFrame *, AstSystemType, AstSystemType, AstTimeScaleType, AstTimeScaleType, double, double, const char *, const char *, const char *, int * ); +static AstSystemType GetAlignSystem( AstFrame *, int * ); +static AstSystemType SystemCode( AstFrame *, const char *, int * ); +static AstSystemType ValidateSystem( AstFrame *, AstSystemType, const char *, int * ); +static AstTimeScaleType TimeScaleCode( const char *, int * ); +static const char *DefUnit( AstSystemType, const char *, const char *, int * ); +static const char *Format( AstFrame *, int, double, int * ); +static const char *GetDomain( AstFrame *, int * ); +static const char *GetLabel( AstFrame *, int, int * ); +static const char *GetSymbol( AstFrame *, int, int * ); +static const char *GetTitle( AstFrame *, int * ); +static const char *GetUnit( AstFrame *, int, int * ); +static const char *SystemLabel( AstSystemType, int * ); +static const char *SystemString( AstFrame *, AstSystemType, int * ); +static const char *TimeScaleString( AstTimeScaleType, int * ); +static double CurrentTime( AstTimeFrame *, int * ); +static double FromMJD( AstTimeFrame *, double, int * ); +static double GetEpoch( AstFrame *, int * ); +static double GetTimeOriginCur( AstTimeFrame *, int * ); +static double ToMJD( AstSystemType, double, int * ); +static double ToUnits( AstTimeFrame *, const char *, double, const char *, int * ); +static int DateFormat( const char *, int *, char *, int * ); +static int Fields( AstFrame *, int, const char *, const char *, int, char **, int *, double *, int * ); +static int GetActiveUnit( AstFrame *, int * ); +static int MakeTimeMapping( AstTimeFrame *, AstTimeFrame *, AstTimeFrame *, int, AstMapping **, int * ); +static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * ); +static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * ); +static int TestActiveUnit( AstFrame *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void OriginScale( AstTimeFrame *, AstTimeScaleType, const char *, int * ); +static void OriginSystem( AstTimeFrame *, AstSystemType, const char *, int * ); +static void Overlay( AstFrame *, const int *, AstFrame *, int * ); +static void SetUnit( AstFrame *, int, const char *, int * ); +static void VerifyAttrs( AstTimeFrame *, const char *, const char *, const char *, int * ); +static AstMapping *ToMJDMap( AstSystemType, double, int * ); +static int Unformat( AstFrame *, int, const char *, double *, int * ); +static const char *Abbrev( AstFrame *, int, const char *, const char *, const char *, int * ); +static double Centre( AstFrame *, int, double, double, int * ); +static double Gap( AstFrame *, int, double, int *, int * ); + +static AstSystemType GetSystem( AstFrame *, int * ); +static void SetSystem( AstFrame *, AstSystemType, int * ); +static void ClearSystem( AstFrame *, int * ); + +static double GetTimeOrigin( AstTimeFrame *, int * ); +static int TestTimeOrigin( AstTimeFrame *, int * ); +static void ClearTimeOrigin( AstTimeFrame *, int * ); +static void SetTimeOrigin( AstTimeFrame *, double, int * ); + +static double GetLTOffset( AstTimeFrame *, int * ); +static int TestLTOffset( AstTimeFrame *, int * ); +static void ClearLTOffset( AstTimeFrame *, int * ); +static void SetLTOffset( AstTimeFrame *, double, int * ); + +static const char *GetAttrib( AstObject *, const char *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); + +static AstTimeScaleType GetAlignTimeScale( AstTimeFrame *, int * ); +static int TestAlignTimeScale( AstTimeFrame *, int * ); +static void ClearAlignTimeScale( AstTimeFrame *, int * ); +static void SetAlignTimeScale( AstTimeFrame *, AstTimeScaleType, int * ); + +static AstTimeScaleType GetTimeScale( AstTimeFrame *, int * ); +static int TestTimeScale( AstTimeFrame *, int * ); +static void ClearTimeScale( AstTimeFrame *, int * ); +static void SetTimeScale( AstTimeFrame *, AstTimeScaleType, int * ); + +/* Member functions. */ +/* ================= */ +static const char *Abbrev( AstFrame *this_frame, int axis, const char *fmt, + const char *str1, const char *str2, int *status ) { +/* +* Name: +* Abbrev + +* Purpose: +* Abbreviate a formatted Frame axis value by skipping leading fields. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *Abbrev( AstFrame *this, int axis, const char *fmt, +* const char *str1, const char *str2, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astAbbrev protected +* method inherited from the Frame class). + +* Description: +* This function compares two Frame axis values that have been +* formatted (using astFormat) and determines if they have any +* redundant leading fields (i.e. leading fields in common which +* can be suppressed when tabulating the values or plotting them on +* the axis of a graph). + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the Frame axis for which the values have been +* formatted (axis numbering starts at zero for the first axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format specification used to format the two values. +* str1 +* Pointer to a constant null-terminated string containing the +* first formatted value. If this is null, the returned pointer +* points to the start of the final field in str2. +* str2 +* Pointer to a constant null-terminated string containing the +* second formatted value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer into the "str2" string which locates the first +* character in the first field that differs between the two +* formatted values. +* +* If the two values have no leading fields in common, the returned +* value will point at the start of string "str2". If the two +* values are equal, it will point at the terminating null at the +* end of this string. + +* Notes: +* - This function assumes that the format specification used was +* the same when both values were formatted and that they both +* apply to the same Frame axis. +* - A pointer to the start of "str2" will be returned if this +* function is invoked with the global error status set, or if it +* should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *p1; + const char *p2; + const char *result; + int df; + int nc1; + int nc2; + int ndp; + +/* Initialise. */ + result = str2; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index. */ + astValidateAxis( this_frame, axis, 1, "astAbbrev" ); + +/* Use the parent astAbbrev function unless the Format attribute indicates + that axis values are to be formatted as multi-field date/time strings. */ + df = DateFormat( fmt, &ndp, NULL, status ); + if( !df ) { + result = (*parent_abbrev)( this_frame, axis, fmt, str1, str2, status ); + +/* Otherwise, if no "str1" string was supplied find the start of the + last field in "str2". */ + } else if( !str1 ){ + +/* Initialise a pointer to the start of the next field in the "str2" string + (skip leading spaces). */ + p2 = str2; + while( *p2 && isspace( *p2 ) ) p2++; + +/* Check the entire string, saving the start of the next field as the + returned pointer. */ + while( *p2 ) { + result = p2; + +/* Each field in a date/time field consists of digits only (and maybe a + decimal point). Find the number of leading digits/dots in this field + and increment the point to the following character (the first delimiter + character). */ + p2 += strspn( p2, "0123456789." ); + +/* Skip inter-field (non-numeric) delimiters. */ + p2 += strcspn( p2, "0123456789." ); + } + +/* Otherwise, if an "str1" string was supplied find the start of the + first differing field in "str2". */ + } else { + +/* Initialise pointers to the start of the next field in each string + (skip leading spaces). */ + p1 = str1; + p2 = str2; + while( *p1 && isspace( *p1 ) ) p1++; + while( *p2 && isspace( *p2 ) ) p2++; + +/* Check the entire string */ + result = p2; + while( *p1 && *p2 ) { + +/* Each field in a date/time field consists of digits only (and maybe a + decimal point). Find the number of leading digits/dots in each string */ + nc1 = strspn( p1, "0123456789." ); + nc2 = strspn( p2, "0123456789." ); + +/* If the next field has different lengths in the two strings, or of the + content of the fields differ, break out of th eloop, leaving "result" + pointing to the start of the current field. */ + if( nc1 != nc2 || strncmp( p1, p2, nc1 ) ) { + break; + +/* If the next field is identical in the two strings, skip to the + character following the end of the field. */ + } else { + p1 += nc1; + p2 += nc2; + +/* Skip inter-field (non-numeric) delimiters. */ + p1 += strcspn( p1, "0123456789." ); + p2 += strcspn( p2, "0123456789." ); + } + +/* Prepare to check the next field. */ + result = p2; + } + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = str2; + +/* Return the result. */ + return result; +} + +static double Centre( AstFrame *this_frame, int axis, double value, + double gap, int *status ) { +/* +* Name: +* Centre + +* Purpose: +* Find a "nice" central value for tabulating Frame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double Centre( AstFrame *this_frame, int axis, double value, +* double gap, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the protected astCentre method +* inherited from the Frame class). + +* Description: +* This function returns an axis value which produces a nice formatted +* value suitable for a major tick mark on a plot axis, close to the +* supplied axis value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which a central value +* is to be found. +* value +* An arbitrary axis value in the section that is being plotted. +* gap +* The gap size. + +* Returned Value: +* The nice central axis value. + +* Notes: +* - The supplied axis value is returned if the supplied gap size is +* zero, or if this function is invoked with the global error status +* set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTimeFrame *this; + char *date1; + char *date2; + char *f1; + char *f2; + char *fres; + char *p1; + char *p2; + char *pres; + const char *fmt; + const char *date; + double result; + int df; + int fmod; + int nc1; + int nc2; + int ndp; + int nres; + int v1; + int v2; + +/* Initialise. */ + result = value; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index. */ + astValidateAxis( this_frame, axis, 1, "astCentre" ); + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Use the parent astCentre function unless the Format attribute indicates + that axis values are to be formatted as multi-field date/time strings. */ + fmt = astGetFormat( this, 0 ); + df = DateFormat( fmt, &ndp, NULL, status ); + if( !df ) { + result = (*parent_centre)( this_frame, axis, value, gap, status ); + +/* Otherwise. */ + } else { + +/* Format time values one gap above the supplied axis value and one gap below + it. Take copies of each string since the astFormat buffer will be + over-written by each call. */ + date = astFormat( this, 0, value - gap ); + if( date ) date1 = astStore( NULL, date, strlen( date ) + 1 ); + date = astFormat( this, 0, value + gap ); + if( date ) date2 = astStore( NULL, date, strlen( date ) + 1 ); + if( astOK ) { + +/* Initialise a formatted version of the returned central value to be + equal to "date1". */ + nres = strlen( date1 ); + fres = astStore( NULL, date1, nres + 1 ); + +/* Loop over all characters within the first date. */ + fmod = 0; + nc1 = 0; + f1 = NULL; + p1 = date1; + nc2 = 0; + f2 = NULL; + p2 = date2; + while( 1 ) { + +/* If we have not yet found the length of the next numerical field in + date1, continue looking for it. */ + if( !nc1 ) { + +/* If we are currently looking for the start of a numerical field, indicate + we have found one if the current character is a digit. */ + if( !f1 ) { + if( isdigit( *p1 ) ) f1 = p1; + +/* If we are currently looking for the end of a numeric field, we have + found the end if the current character is not a digit. */ + } else { + if( !isdigit( *p1 ) ) { + nc1 = p1 - f1; + } + } + +/* Look at the next character */ + p1++; + } + +/* If we have not yet found the length of the next numerical field in + date2, continue looking for it. */ + if( !nc2 ) { + +/* If we are currently looking for the start of a numerical field, indicate + we have found one if the current character is a digit. */ + if( !f2 ) { + if( isdigit( *p2 ) ) f2 = p2; + +/* If we are currently looking for the end of a numeric field, we have + found the end if the current character is not a digit. */ + } else { + if( !isdigit( *p2 ) ) { + nc2 = p2 - f2; + } + } + +/* Look at the next character */ + p2++; + } + +/* If we have found the next numerical field in both dates, convert them + to integers. */ + if( nc1 && nc2 ) { + v1 = atoi( f1 ); + v2 = atoi( f2 ); + +/* If the values are different, replace this field and all subsequent + fields with zeros in the formatted version of the returned central + value, and leave the loop. */ + if( v1 != v2 ) { + + pres = fres + ( f1 - date1 ) - 1; + while( *(++pres) ) { + if( isdigit( *pres ) ) *pres = '0'; + } + fmod = 1; + + break; + } + +/* Prepare to look for the next numerical field in both strings. */ + nc1 = nc2 = 0; + f1 = f2 = NULL; + +/* If either string has been exhausted, leave the loop. */ + if( !*p1 || !*p2 ) break; + } + } + +/* If the formatted "nice" value was changed, unformatted it to get the + returned axis value. Otherwise we rettina the returned value set + earlier. */ + if( fmod ) { + if( astUnformat( this, 0, fres, &result ) != nres && astOK ) { + astError( AST__INTER, "astCentre(%s): Error unformatting " + "the central time axis value '%s' (internal AST " + "programming error).", status, astClass( this ), fres ); + } + } + +/* Free resources. */ + fres = astFree( fres ); + } + date1 = astFree( date1 ); + date2 = astFree( date2 ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int DateFormat( const char *fmt, int *ndp, char *sep, int *status ){ +/* +* Name: +* DateFormat + +* Purpose: +* Determine if TimeFrame values should be formatted as a date. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int DateFormat( const char *fmt, int *ndp, char *sep, int *status ) + +* Class Membership: +* TimeFrame member function + +* Description: +* This function returns a flag indicating if the supplied Format string +* requires the TimeFrame value to be formatted as a date and/or time of +* day. + +* Parameters: +* fmt +* Pointer to Format string. +* ndp +* A pointer to an integer in which is returned a value indicating +* if a time is required as well as a date. A value of -1 will be +* returned in no time is required, otherwise the returned value will +* equal the number of decimal places required for the seconds field. +* sep +* A pointer to a char in which is returned the character that +* should be used to separate the date and time fields. Ignored if +* NULL. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the formatted TimeFrame value should include a date. + +*/ + +/* Local Variables: */ + const char *c; + int nc; + int result; + +/* Initialise */ + result = 0; + *ndp = -1; + +/* Check the Format string */ + if( fmt ) { + +/* Find the first non-white character */ + c = fmt; + while( *c && isspace( *c ) ) c++; + +/* If the first non-white character starts the string "iso" + assume a date is required. If so see if a time is also required + (indicated by 1 dot following) and how many seconds of precision are + required (the interegr following the dot). */ + if( !strncmp( c, "iso", 3 ) ) { + result = 1; + if( astSscanf( c, "iso.%d%n", ndp, &nc ) == 1 ) { + +/* Check the separate character (if any) at the end of the format string. + Only "T" is allowed. A space is used if no separator is given. */ + if( sep ) *sep = ( c[ nc ] == 'T' || c[ nc ] == 't' ) ? 'T' : ' '; + + } else { + *ndp = -1; + } + } + } + + return result; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astClearAttrib protected +* method inherited from the Frame class). + +* Description: +* This function clears the value of a specified attribute for a +* TimeFrame, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the TimeFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to the TimeFrame structure */ + char *new_attrib; /* Pointer value to new attribute name */ + int len; /* Length of attrib string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_object; + +/* Obtain the length of the "attrib" string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* First look for axis attributes defined by the Frame class. Since a + TimeFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strcmp( attrib, "direction" ) || + !strcmp( attrib, "bottom" ) || + !strcmp( attrib, "top" ) || + !strcmp( attrib, "format" ) || + !strcmp( attrib, "label" ) || + !strcmp( attrib, "symbol" ) || + !strcmp( attrib, "unit" ) ) { + +/* Create a new attribute name from the original by appending the string + "(1)" and then use the parent ClearAttrib method. */ + new_attrib = astMalloc( len + 4 ); + if( new_attrib ) { + memcpy( new_attrib, attrib, len ); + memcpy( new_attrib + len, "(1)", 4 ); + (*parent_clearattrib)( this_object, new_attrib, status ); + new_attrib = astFree( new_attrib ); + } + +/* AlignTimeScale. */ +/* --------------- */ + } else if ( !strcmp( attrib, "aligntimescale" ) ) { + astClearAlignTimeScale( this ); + +/* ClockLat. */ +/* --------- */ +/* Retained for backward compatibility with older versions of AST in which + TimeFrame had ClockLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( !strcmp( attrib, "clocklat" ) ) { + astClearAttrib( this, "obslat" ); + +/* ClockLon. */ +/* --------- */ +/* Retained for backward compatibility with older versions of AST in which + TimeFrame had ClockLon/Lat attributes (now ObsLon/Lat are used instead). */ + } else if ( !strcmp( attrib, "clocklon" ) ) { + astClearAttrib( this, "obslon" ); + +/* LTOffset. */ +/* --------- */ + } else if ( !strcmp( attrib, "ltoffset" ) ) { + astClearLTOffset( this ); + +/* TimeOrigin. */ +/* ---------- */ + } else if ( !strcmp( attrib, "timeorigin" ) ) { + astClearTimeOrigin( this ); + +/* TimeScale. */ +/* ---------- */ + } else if ( !strcmp( attrib, "timescale" ) ) { + astClearTimeScale( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* ClearSystem + +* Purpose: +* Clear the System attribute for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void ClearSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astClearSystem protected +* method inherited from the Frame class). + +* Description: +* This function clears the System attribute for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstSystemType oldsys; /* System before clearing */ + AstTimeFrame *this; /* Pointer to TimeFrame structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Save the original system */ + oldsys = astGetSystem( this_frame ); + +/* Use the parent ClearSystem method to clear the System value. */ + (*parent_clearsystem)( this_frame, status ); + +/* Do nothing more if the system has not actually changed. */ + if( astGetSystem( this_frame ) != oldsys ) { + +/* Modify the TimeOrigin value to use the new System */ + OriginSystem( this, oldsys, "astClearSystem", status ); + +/* Clear attributes which have system-specific defaults. */ + astClearLabel( this_frame, 0 ); + astClearSymbol( this_frame, 0 ); + astClearTitle( this_frame ); + +/* If the old system was BEPOCH also clear units and timescale. This is + because we need to ensure that TimeScale=TT and Unit=yr will be used + in future (these are the only acceptable values for System=BEPOCH). */ + if( oldsys == AST__BEPOCH ) { + astClearUnit( this_frame, 0 ); + astClearTimeScale( (AstTimeFrame *) this_frame ); + } + } +} + +static void ClearTimeScale( AstTimeFrame *this, int *status ) { +/* +*+ +* Name: +* astClearTimeScale + +* Purpose: +* Clear the TimeScale attribute for a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* void astClearTimeScale( AstTimeFrame *this ) + +* Class Membership: +* TimeFrame virtual function + +* Description: +* This function clears the TimeScale attribute for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If the System is currently set to BEPOCH, then the TimeScale will + either be set to TT or will be unset (since SetTimeScale will not + allow any other value than TT if the System is BEPOCH). Therefore, if + System is BEPOCH, we will not need to modify the TimeOrigin value, + since it will already be appropriate. Otherwise, we modify the + TimeOrigin value stored in the TimeFrame structure to refer to the + default timescale (TAI or TT). */ + if( astGetSystem( this ) != AST__BEPOCH ) OriginScale( this, AST__TAI, + "astClearTimeScale", status ); + +/* Store a bad value for the timescale in the TimeFrame structure. */ + this->timescale = AST__BADTS; +} + +static double CurrentTime( AstTimeFrame *this, int *status ){ +/* +*++ +* Name: +c astCurrentTime +f AST_CURRENTTIME + +* Purpose: +* Return the current system time. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "timeframe.h" +c double astCurrentTime( AstTimeFrame *this ) +f RESULT = AST_CURRENTTIME( THIS, STATUS ) + +* Class Membership: +* TimeFrame method. + +* Description: +c This function +f This routine +* returns the current system time, represented in the form specified +* by the supplied TimeFrame. That is, the returned floating point +* value should be interpreted using the attribute values of the +* TimeFrame. This includes System, TimeOrigin, LTOffset, TimeScale, +* and Unit. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the TimeFrame. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astCurrentTime() +f AST_CURRENTTIME = DOUBLE +c A TimeFrame axis value representing the current system time. + +* Notes: +* - Values of AST__BAD will be returned if this function is +c invoked with the AST error status set, or if it should fail for +f invoked with STATUS set to an error value, or if it should fail for +* any reason. +* - It is assumes that the system time (returned by the C time() +* function) follows the POSIX standard, representing a continuous +* monotonic increasing count of SI seconds since the epoch 00:00:00 +* UTC 1 January 1970 AD (equivalent to TAI with a constant offset). +* Resolution is one second. +* - An error will be reported if the TimeFrame has a TimeScale value +* which cannot be converted to TAI (e.g. "angular" systems such as +* UT1, GMST, LMST and LAST). +* - Any inaccuracy in the system clock will be reflected in the value +* returned by this function. +*-- +*/ + +/* Local Constants: */ + +/* Local Variables: */ + AstMapping *map; + double result; + double systime; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a Mapping from the system time (TAI seconds relative to "tai_epoch") + to the system represented by the supplied TimeFrame. */ + map = MakeMap( this, AST__MJD, astGetSystem( this ), + AST__TAI, astGetTimeScale( this ), + tai_epoch, astGetTimeOrigin( this ), + "s", astGetUnit( this, 0 ), "astCurrentTime", status ); + if( !map ) { + astError( AST__INCTS, "astCurrentTime(%s): Cannot convert the " + "current system time to the required timescale (%s).", status, + astGetClass( this ), + TimeScaleString( astGetTimeScale( this ), status ) ); + +/* Get the system time. The "time" function returns a "time_t" which may be + encoded in any way. We use "difftime" to convert this into a floating + point number of seconds by taking the difference between the current + time and zero time. This assumes nothing about the structure of a + "time_t" except that zero can be cast into a time_t representing + the epoch. */ + } else { + systime = difftime( time( NULL ), (time_t) 0 ); + +/* Use the Mapping to convert the time into the requied system. */ + astTran1( map, 1, &systime, 1, &result ); + +/* Free resources */ + map = astAnnul( map ); + } + +/* Set result to AST__BAD if an error occurred. */ + if( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static const char *DefUnit( AstSystemType system, const char *method, + const char *class, int *status ){ +/* +* Name: +* DefUnit + +* Purpose: +* Return the default units for a time coordinate system type. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *DefUnit( AstSystemType system, const char *method, +* const char *class, int *status ) + +* Class Membership: +* TimeFrame member function. + +* Description: +* This function returns a textual representation of the default +* units associated with the specified time coordinate system. + +* Parameters: +* system +* The time coordinate system. +* method +* Pointer to a string holding the name of the calling method. +* This is only for use in constructing error messages. +* class +* Pointer to a string holding the name of the supplied object class. +* This is only for use in constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A string describing the default units. This string follows the +* units syntax described in FITS WCS paper I "Representations of world +* coordinates in FITS" (Greisen & Calabretta). + +* Notes: +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Value to return */ + +/* Initialize */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get an identifier for the default units. */ + if( system == AST__MJD ) { + result = "d"; + } else if( system == AST__JD ) { + result = "d"; + } else if( system == AST__BEPOCH ) { + result = "yr"; + } else if( system == AST__JEPOCH ) { + result = "yr"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "%s(%s): Corrupt %s contains illegal System " + "identification code (%d).", status, method, class, class, + (int) system ); + } + +/* Return the result. */ + return result; +} + +static int Fields( AstFrame *this_frame, int axis, const char *fmt, + const char *str, int maxfld, char **fields, + int *nc, double *val, int *status ) { +/* +* Name: +* Fields + +* Purpose: +* Identify numerical fields within a formatted Axis value. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "frame.h" +* int Fields( AstFrame *this, int axis, const char *fmt, +* const char *str, int maxfld, char **fields, +* int *nc, double *val ) + +* Class Membership: +* TimeFrame member function (over-rides the astFields protected +* method inherited from the Frame class). + +* Description: +* This function identifies the numerical fields within a Frame axis +* value that has been formatted using astAxisFormat. It assumes that +* the value was formatted using the supplied format string. It also +* returns the equivalent floating point value. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the Frame axis for which the values have been +* formatted (axis numbering starts at zero for the first axis). +* fmt +* Pointer to a constant null-terminated string containing the +* format used when creating "str". +* str +* Pointer to a constant null-terminated string containing the +* formatted value. +* maxfld +* The maximum number of fields to identify within "str". +* fields +* A pointer to an array of at least "maxfld" character pointers. +* Each element is returned holding a pointer to the start of the +* corresponding field in "str" (in the order in which they occur +* within "str"), or NULL if no corresponding field can be found. +* nc +* A pointer to an array of at least "maxfld" integers. Each +* element is returned holding the number of characters in the +* corresponding field, or zero if no corresponding field can be +* found. +* val +* Pointer to a location at which to store the value +* equivalent to the returned field values. If this is NULL, +* it is ignored. + +* Returned Value: +* The number of fields succesfully identified and returned. + +* Notes: +* - Leading and trailing spaces are ignored. +* - If the formatted value is not consistent with the supplied format +* string, then a value of zero will be returned, "fields" will be +* returned holding NULLs, "nc" will be returned holding zeros, and +* "val" is returned holding VAL__BAD. +* - Fields are counted from the start of the formatted string. If the +* string contains more than "maxfld" fields, then trailing fields are +* ignored. +*/ + +/* Local Variables: */ + AstTimeFrame *this; + char *p; + int bad; + int df; + int ifld; + int ndp; + int result; + int state; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astFields" ); + +/* Call the method inherited from the parent Frame class, unless the + format string indicates date-time formatting. */ + df = DateFormat( fmt, &ndp, NULL, status ); + if( !df ) { + result = (*parent_fields)( this_frame, axis, fmt, str, maxfld, fields, + nc, val, status ); + +/* Now handle date/time formats.... */ + } else { + +/* Initialise. */ + for( ifld = 0; ifld < maxfld; ifld++ ) { + fields[ ifld ] = NULL; + nc[ ifld ] = 0; + } + if( val ) *val = AST__BAD; + +/* The formatted string should always include a date in ISO format - three + integer fields separated by dashes. Loop round each character until + all characters have been read, or the max number of fields have been + obtained, or it is shown that the string is badly formatted. */ + bad = 0; + state = 0; + ifld = 0; + p = (char *) str - 1; + while( *(++p) && ifld < maxfld && !bad ){ + +/* Looking for the start of the year field. */ + if( state == 0 ) { + if( isdigit( *p ) ) { + fields[ ifld ] = p; + nc[ ifld ] = 1; + state = 1; + } else if( !isspace( *p ) ) { + bad = 1; + } + +/* Looking for the end of the year field. */ + } else if( state == 1 ) { + if( isdigit( *p ) ) { + nc[ ifld ]++; + } else if( *p != '-' ){ + bad = 1; + } else { + state = 2; + ifld++; + } + +/* Looking for the start of the month field. */ + } else if( state == 2 ) { + if( isdigit( *p ) ) { + fields[ ifld ] = p; + nc[ ifld ] = 1; + state = 3; + } else { + bad = 1; + } + +/* Looking for the end of the month field. */ + } else if( state == 3 ) { + if( isdigit( *p ) ) { + nc[ ifld ]++; + } else if( *p != '-' ){ + bad = 1; + } else { + state = 4; + ifld++; + } + +/* Looking for the start of the day field. */ + } else if( state == 4 ) { + if( isdigit( *p ) ) { + fields[ ifld ] = p; + nc[ ifld ] = 1; + state = 5; + } else { + bad = 1; + } + +/* Looking for the end of the day field. */ + } else if( state == 5 ) { + if( isdigit( *p ) ) { + nc[ ifld ]++; + } else if( *p != ' ' && *p != 'T' ){ + bad = 1; + } else { + state = 6; + ifld++; + } + +/* Looking for the start of the hour field. */ + } else if( state == 6 ) { + if( isdigit( *p ) ) { + fields[ ifld ] = p; + nc[ ifld ] = 1; + state = 7; + } else { + bad = 1; + } + +/* Looking for the end of the hour field. */ + } else if( state == 7 ) { + if( isdigit( *p ) ) { + nc[ ifld ]++; + } else if( *p != ':' ){ + bad = 1; + } else { + state = 8; + ifld++; + } + +/* Looking for the start of the minute field. */ + } else if( state == 8 ) { + if( isdigit( *p ) ) { + fields[ ifld ] = p; + nc[ ifld ] = 1; + state = 9; + } else { + bad = 1; + } + +/* Looking for the end of the minute field. */ + } else if( state == 9 ) { + if( isdigit( *p ) ) { + nc[ ifld ]++; + } else if( *p != ':' ){ + bad = 1; + } else { + state = 10; + ifld++; + } + +/* Looking for the start of the integer part of the seconds field. */ + } else if( state == 10 ) { + if( isdigit( *p ) ) { + fields[ ifld ] = p; + nc[ ifld ] = 1; + state = 11; + } else { + bad = 1; + } + +/* Looking for the end of the integer part of the seconds field. */ + } else if( state == 11 ) { + if( isdigit( *p ) ) { + nc[ ifld ]++; + } else if( *p != '.' ){ + bad = 1; + } else { + state = 12; + ifld++; + } + +/* Looking for the start of the decimal part of the seconds field. */ + } else if( state == 12 ) { + if( isdigit( *p ) ) { + fields[ ifld ] = p; + nc[ ifld ] = 1; + state = 13; + } else { + bad = 1; + } + +/* Looking for the end of the decimal part of the seconds field. */ + } else if( state == 13 ) { + if( isdigit( *p ) ) { + nc[ ifld ]++; + } else if( !isspace( *p ) ){ + bad = 1; + } + + } else { + bad = 1; + } + } + +/* If he string is badly formatted, return null values. */ + if( bad ) { + result = 0; + for( ifld = 0; ifld < maxfld; ifld++ ) { + fields[ ifld ] = NULL; + nc[ ifld ] = 0; + } + +/* Otherwise, unformat the string if required. */ + } else if( val ) { + (void) astUnformat( this, axis, str, val ); + } + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static const char *Format( AstFrame *this_frame, int axis, double value, int *status ) { +/* +* Name: +* Format + +* Purpose: +* Format a coordinate value for a TimeFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *Format( AstFrame *this, int axis, double value, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astFormat method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to a string containing the formatted +* (character) version of a coordinate value for a TimeFrame axis. The +* formatting applied is that specified by a previous invocation of the +* astSetFormat method. A suitable default format is applied if necessary. + +* Parameters: +* this +* Pointer to the TimeFrame. +* axis +* The number of the axis (zero-based) for which formatting is to be +* performed. +* value +* The coordinate value to be formatted, in radians. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a null-terminated string containing the formatted value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + AstMapping *map; + AstSystemType sys; + AstTimeFrame *this; + AstTimeScaleType ts; + char *d; + char sep; + char tbuf[ 100 ]; + char sign[ 2 ]; + const char *fmt; + const char *result; + const char *u; + double fd; + double mjd; + double off; + int df; + int id; + int ihmsf[ 4 ]; + int im; + int iy; + int j; + int ndp; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astFormat" ); + +/* Check if a bad coordinate value was supplied and return a pointer to an + appropriate string if necessary. */ + if ( value == AST__BAD ) { + result = ""; + } else { + +/* If the format string does not indicate a date/time format, invoke the + parent Format method. */ + fmt = astGetFormat( this, 0 ); + df = DateFormat( fmt, &ndp, &sep, status ); + if( !df ) { + result = (*parent_format)( this_frame, axis, value, status ); + +/* Otherwise, format the value as a date/time */ + } else { + +/* Convert the value to an absolute MJD in units of days. */ + ts = astGetTimeScale( this ); + sys = astGetSystem( this ); + off = astGetTimeOrigin( this ); + u = astGetUnit( this, 0 ); + map = MakeMap( this, sys, AST__MJD, ts, ts, off, 0.0, u, "d", + "astFormat", status ); + if( map ) { + astTran1( map, 1, &value, 1, &mjd ); + map = astAnnul( map ); + +/* If no time fields will be produced, round to the nearest day. */ + if( ndp < 0 ) mjd = (int) ( mjd + 0.5 ); + +/* Convert the MJD into a set of numeric date fields, plus day fraction, + and format them. */ + palDjcl( mjd, &iy, &im, &id, &fd, &j ); + d = format_buff; + d += sprintf( d, "%4d-%2.2d-%2.2d", iy, im, id ); + +/* If required, convert the day fraction into a set of numerical time + fields. */ + if( ndp >= 0 ) { + palDd2tf( ndp, fd, sign, ihmsf ); + +/* Format the time fields. */ + if( ndp > 0 ) { + (void) sprintf( tbuf, "%c%2.2d:%2.2d:%2.2d.%*.*d", sep, + ihmsf[0], ihmsf[1], ihmsf[2], ndp, ndp, + ihmsf[3] ); + } else { + (void) sprintf( tbuf, "%c%2.2d:%2.2d:%2.2d", sep, ihmsf[0], + ihmsf[1], ihmsf[2] ); + } + +/* Add in the formatted time. */ + d += sprintf( d, "%s", tbuf ); + + } + result = format_buff; + } + } + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static double FromMJD( AstTimeFrame *this, double oldval, int *status ){ +/* +* +* Name: +* FromMJD + +* Purpose: +* Convert a supplied MJD value to the System of the supplied TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double FromMJD( AstTimeFrame *this, double oldval, int *status ) + +* Class Membership: +* TimeFrame member function + +* Description: +* This function converts the supplied time value from an MJD to +* the System of the supplied TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* oldval +* The value to be converted. It is assume to be an absolute MJD +* value (i.e. zero offset) in units of days. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The converted value (with zero offset), in the default units +* associated with the System of "this". + +*/ + +/* Local Variables: */ + AstTimeMap *timemap; + AstSystemType newsys; + double args[ 2 ]; + double result; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the System attribute from the supplied TimeFrame. */ + newsys = astGetSystem( this ); + +/* If this is MJD just return the value unchanged. */ + if( newsys == AST__MJD ) { + result = oldval; + +/* Otherwise create a TimeMap wich converts from the MJD to the required + system, and use it to transform the supplied value. */ + } else { + timemap = astTimeMap( 0, "", status ); + +/* The supplied and returned values are assumed to have zero offset.*/ + args[ 0 ] = 0.0; + args[ 1 ] = 0.0; + +/* If required, add a TimeMap conversion which converts from MJD to the + new system. */ + if( newsys == AST__JD ) { + astTimeAdd( timemap, "MJDTOJD", args ); + + } else if( newsys == AST__JEPOCH ) { + astTimeAdd( timemap, "MJDTOJEP", args ); + + } else if( newsys == AST__BEPOCH ) { + astTimeAdd( timemap, "MJDTOBEP", args ); + } + +/* Use the TimeMap to convert the supplied value. */ + astTran1( timemap, 1, &oldval, 1, &result ); + +/* Free resources */ + timemap = astAnnul( timemap ); + + } + +/* Return the result */ + return result; +} + + +static double Gap( AstFrame *this_frame, int axis, double gap, int *ntick, int *status ) { +/* +* Name: +* Gap + +* Purpose: +* Find a "nice" gap for tabulating Frame axis values. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double Gap( AstFrame *this, int axis, double gap, int *ntick, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGap protected +* method inherited from the Frame class). + +* Description: +* This function returns a gap size which produces a nicely spaced +* series of formatted values for a Frame axis, the returned gap +* size being as close as possible to the supplied target gap +* size. It also returns a convenient number of divisions into +* which the gap can be divided. + +* Parameters: +* this +* Pointer to the Frame. +* axis +* The number of the axis (zero-based) for which a gap is to be found. +* gap +* The target gap size. +* ntick +* Address of an int in which to return a convenient number of +* divisions into which the gap can be divided. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The nice gap size. + +* Notes: +* - A value of zero is returned if the target gap size is zero. +* - A negative gap size is returned if the supplied gap size is negative. +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *map; + AstTimeFrame *this; + AstTimeScaleType ts; + const char *fmt; + double mjdgap; + double result; + double xin[2]; + double xout[2]; + int df; + int ndp; + +/* Initialise. */ + result = 0.0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Validate the axis index. */ + astValidateAxis( this_frame, axis, 1, "astGap" ); + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Use the parent astGap function unless the Format attribute indicates + that axis values are to be formatted as multi-field date/time strings. */ + fmt = astGetFormat( this, 0 ); + df = DateFormat( fmt, &ndp, NULL, status ); + if( !df ) { + result = (*parent_gap)( this_frame, axis, gap, ntick, status ); + +/* Otherwise. */ + } else { + +/* Get a Mapping which converts TimeFrame values to MJD values. */ + ts = astGetTimeScale( this ); + map = MakeMap( this, astGetSystem( this ), AST__MJD, ts, ts, + astGetTimeOrigin( this ), 0.0, astGetUnit( this, 0 ), + "d", "astGap", status ); + if( map ) { + +/* Use it to transform two TimeFrame times to MJD. The first is the + current time, and the second is the current time plus the target gap. */ + xin[ 0 ] = astCurrentTime( this ); + xin[ 1 ] = xin[ 0 ] + gap; + astTran1( map, 2, xin, 1, xout ); + +/* Find the target MJD gap. */ + mjdgap = xout[ 1 ] - xout[ 0 ]; + +/* If it is 1 year or more, use the parent astGap method to find a nice + number of years, and convert back to days. */ + if( mjdgap >= 365.25 ) { + mjdgap = 365.25*(*parent_gap)( this_frame, axis, mjdgap/365.25, ntick, status ); + +/* If it is more than 270 days days use 1 year. */ + } else if( mjdgap > 270.0 ) { + mjdgap = 365.25; + *ntick = 4; + +/* If it is more than 150 days days use 180 days (roughly half a year). + Use 6 divisions (30 days each, or roughly 1 month). */ + } else if( mjdgap > 150.0 ) { + mjdgap = 180.0; + *ntick = 6; + +/* If it is more than 90 days days use 120 days (roughly 4 months). */ + } else if( mjdgap > 90.0 ) { + mjdgap = 120.0; + *ntick = 4; + +/* If it is more than 45 days days use 60 days (roughly 2 months). */ + } else if( mjdgap > 45.0 ) { + mjdgap = 60.0; + *ntick = 2; + +/* If it is more than 22 days days use 30 days (roughly one month). Use 3 + ten day divisions. */ + } else if( mjdgap > 22.0 ) { + mjdgap = 30.0; + *ntick = 3; + +/* If it is more than 12 days days use 15 days (roughly half a month). */ + } else if( mjdgap > 12.0 ) { + mjdgap = 15.0; + *ntick = 3; + +/* If it is more than 7.5 days days use 10 days, with 5 two-day divisions. */ + } else if( mjdgap > 7.5 ) { + mjdgap = 10.0; + *ntick = 5; + +/* If it is more than 4.5 days days use 5 days. */ + } else if( mjdgap > 4.5 ) { + mjdgap = 5.0; + *ntick = 5; + +/* If it is more than 3 days days use 4 days. */ + } else if( mjdgap > 3.0 ) { + mjdgap = 4.0; + *ntick = 4; + +/* If it is more than 1.5 days days use 2 days. */ + } else if( mjdgap > 1.5 ) { + mjdgap = 2.0; + *ntick = 2; + +/* If it is more than 0.5 of a day use 1 day. */ + } else if( mjdgap > 0.5 ) { + mjdgap = 1.0; + *ntick = 4; + +/* Otherwise, if the format indicates that no time field is allowed, + use 1 day. */ + } else if( ndp < 0 ) { + mjdgap = 1.0; + *ntick = 2; + +/* Otherwise (i.e. if the target gap is 0.5 day or less and the format + indicates that a time field is allowed), choose a value which looks + nice. */ + } else if( mjdgap >= 6.0/24.0 ) { /* 12 hours */ + mjdgap = 12.0/24.0; + *ntick = 4; + + } else if( mjdgap >= 3.0/24.0 ) { /* 6 hours */ + mjdgap = 6.0/24.0; + *ntick = 3; + + } else if( mjdgap >= 1.0/24.0 ) { /* 2 hours */ + mjdgap = 2.0/24.0; + *ntick = 4; + + } else if( mjdgap >= 30.0/1440.0 ) { /* 1 hour */ + mjdgap = 60.0/1440.0; + *ntick = 4; + + } else if( mjdgap >= 15.0/1440.0 ) { /* 30 minutes */ + mjdgap = 30.0/1440.0; + *ntick = 3; + + } else if( mjdgap >= 5.0/1440.0 ) { /* 10 minutes */ + mjdgap = 10.0/1440.0; + *ntick = 5; + + } else if( mjdgap >= 2.5/1440.0 ) { /* 5 minutes */ + mjdgap = 5.0/1440.0; + *ntick = 5; + + } else if( mjdgap >= 1.0/1440.0 ) { /* 2 minutes */ + mjdgap = 2.0/1440.0; + *ntick = 4; + + } else if( mjdgap >= 0.5/1440.0 ) { /* 1 minute */ + mjdgap = 1.0/1440.0; + *ntick = 4; + + } else if( mjdgap >= 15.0/86400.0 ) { /* 30 seconds */ + mjdgap = 30.0/86400.0; + *ntick = 3; + + } else if( mjdgap >= 5.0/86400.0 ) { /* 10 seconds */ + mjdgap = 10.0/86400.0; + *ntick = 5; + + } else if( mjdgap >= 2.5/86400.0 ) { /* 5 seconds */ + mjdgap = 5.0/86400.0; + *ntick = 5; + + } else if( mjdgap >= 1.0/86400.0 ) { /* 2 seconds */ + mjdgap = 2.0/86400.0; + *ntick = 4; + + } else if( mjdgap >= 0.5/86400.0 ) { /* 1 second */ + mjdgap = 1.0/86400.0; + *ntick = 4; + + } else { /* Less than 1 second */ + mjdgap = 86400.0*(*parent_gap)( this_frame, axis, mjdgap/86400.0, ntick, status ); + + } + +/* Convert the MJD gap back into the system of the supplied TimeFrame. */ + xout[ 1 ] = xout[ 0 ] + mjdgap; + astTran1( map, 2, xout, 0, xin ); + result = xin[ 1 ] - xin[ 0 ]; + +/* Free resources */ + map = astAnnul( map ); + +/* If no Mapping could be found, use the parent astGap method. */ + } else { + result = (*parent_gap)( this_frame, axis, gap, ntick, status ); + } + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = 0.0; + +/* Return the result. */ + return result; +} + +static int GetActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetActiveUnit + +* Purpose: +* Obtain the value of the ActiveUnit flag for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int GetActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function returns the value of the ActiveUnit flag for a +* TimeFrame, which is always 1. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The value to use for the ActiveUnit flag (1). + +*/ + return 1; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the protected astGetAttrib +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a TimeFrame, formatted as a character string. + +* Parameters: +* this +* Pointer to the TimeFrame. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - The returned string pointer may point at memory allocated +* within the TimeFrame, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the TimeFrame. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to the TimeFrame structure */ + AstTimeScaleType ts; /* Time scale */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + char *new_attrib; /* Pointer value to new attribute name */ + const char *result; /* Pointer value to return */ + double dval; /* Attribute value */ + int len; /* Length of attrib string */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* First look for axis attributes defined by the Frame class. Since a + TimeFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strcmp( attrib, "direction" ) || + !strcmp( attrib, "bottom" ) || + !strcmp( attrib, "top" ) || + !strcmp( attrib, "format" ) || + !strcmp( attrib, "label" ) || + !strcmp( attrib, "symbol" ) || + !strcmp( attrib, "unit" ) ) { + +/* Create a new attribute name from the original by appending the string + "(1)" and then use the parent GetAttrib method. */ + new_attrib = astMalloc( len + 4 ); + if( new_attrib ) { + memcpy( new_attrib, attrib, len ); + memcpy( new_attrib + len, "(1)", 4 ); + result = (*parent_getattrib)( this_object, new_attrib, status ); + new_attrib = astFree( new_attrib ); + } + +/* AlignTimeScale. */ +/* --------------- */ +/* Obtain the AlignTimeScale code and convert to a string. */ + } else if ( !strcmp( attrib, "aligntimescale" ) ) { + ts = astGetAlignTimeScale( this ); + if ( astOK ) { + result = TimeScaleString( ts, status ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid AlignTimeScale " + "identification code (%d).", status, astGetClass( this ), + astGetClass( this ), (int) ts ); + } + } + +/* ClockLat. */ +/* ------- */ + } else if ( !strcmp( attrib, "clocklat" ) ) { + result = astGetAttrib( this, "obslat" ); + +/* ClockLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "clocklon" ) ) { + result = astGetAttrib( this, "obslon" ); + +/* TimeOrigin. */ +/* ----------- */ + } else if ( !strcmp( attrib, "timeorigin" ) ) { + dval = GetTimeOriginCur( this, status ); + if( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* LTOffset. */ +/* --------- */ + } else if ( !strcmp( attrib, "ltoffset" ) ) { + dval = astGetLTOffset( this ); + if( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* TimeScale. */ +/* ---------- */ +/* Obtain the TimeScale code and convert to a string. */ + } else if ( !strcmp( attrib, "timescale" ) ) { + ts = astGetTimeScale( this ); + if ( astOK ) { + result = TimeScaleString( ts, status ); + +/* Report an error if the value was not recognised. */ + if ( !result ) { + astError( AST__SCSIN, + "astGetAttrib(%s): Corrupt %s contains invalid TimeScale " + "identification code (%d).", status, astGetClass( this ), + astGetClass( this ), (int) ts ); + } + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static double GetTimeOriginCur( AstTimeFrame *this, int *status ) { +/* +* Name: +* GetTimeOriginCur + +* Purpose: +* Obtain the TimeOrigin attribute for a TimeFrame in current units. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double GetTimeOriginCur( AstTimeFrame *this, int *status ) + +* Class Membership: +* TimeFrame virtual function + +* Description: +* This function returns the TimeOrigin attribute for a TimeFrame, in +* the current units of the TimeFrame. The protected astGetTimeOrigin +* method can be used to obtain the time origin in the default units of +* the TimeFrame's System. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The TimeOrigin value, in the units, system and timescale specified +* by the current values of the Unit, System and TimeScale attributes +* within "this". + +* Notes: +* - AST__BAD is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *map; + const char *cur; + const char *def; + double result; + double defval; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get the value in the default units */ + result = astGetTimeOrigin( this ); + +/* If non-zero we convert to the current units.*/ + if( result != 0.0 && result != AST__BAD ) { + +/* Get the default units for the TimeFrame's System. */ + def = DefUnit( astGetSystem( this ), "astGetTimeOrigin", "TimeFrame", status ); + +/* Get the current units from the TimeFrame. */ + cur = astGetUnit( this, 0 ); + +/* If the units differ, get a Mapping from default to current units. */ + if( cur && def ){ + if( strcmp( cur, def ) ) { + map = astUnitMapper( def, cur, NULL, NULL ); + +/* Report an error if the units are incompatible. */ + if( !map ) { + astError( AST__BADUN, "%s(%s): The current units (%s) are not suitable " + "for a TimeFrame.", status, "astGetTimeOrigin", astGetClass( this ), + cur ); + +/* Otherwise, transform the stored origin value.*/ + } else { + defval = result; + astTran1( map, 1, &defval, 1, &result ); + map = astAnnul( map ); + } + } + } + } + +/* Return the result. */ + return result; +} + +static const char *GetDomain( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetDomain + +* Purpose: +* Obtain a pointer to the Domain attribute string for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *GetDomain( AstFrame *this, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetDomain protected +* method inherited from the Frame class). + +* Description: +* This function returns a pointer to the Domain attribute string +* for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a constant null-terminated string containing the +* Domain value. + +* Notes: +* - The returned pointer or the string it refers to may become +* invalid following further invocation of this function or +* modification of the TimeFrame. +* - A NULL pointer is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to TimeFrame structure */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* If a Domain attribute string has been set, invoke the parent method + to obtain a pointer to it. */ + if ( astTestDomain( this ) ) { + result = (*parent_getdomain)( this_frame, status ); + +/* Otherwise, provide a pointer to a suitable default string. */ + } else { + result = "TIME"; + } + +/* Return the result. */ + return result; +} + +static double GetEpoch( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetEpoch + +* Purpose: +* Get a value for the Epoch attribute of a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double GetEpoch( AstFrame *this, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetEpoch method +* inherited from the Frame class). + +* Description: +* This function returns a value for the Epoch attribute of a +* TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Epoch attribute value. + +* Notes: +* - A value of AST__BAD will be returned if this function is invoked +* with the global error status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *map; + AstSystemType sys; + AstTimeFrame *this; + AstTimeScaleType ts; + const char *u; + double oldval; + double result; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* If an Epoch attribute value has been set, invoke the parent method + to obtain it. */ + if ( astTestEpoch( this ) ) { + result = (*parent_getepoch)( this_frame, status ); + +/* Otherwise, if the TimeOrigin value is set in the TimeFrame, + return it, converted to an absolute TDB MJD. */ + } else if( astTestTimeOrigin( this ) ){ + +/* Get the required properties of the TimeFrame. */ + oldval = astGetTimeOrigin( this ); + ts = astGetTimeScale( this ); + sys = astGetSystem( this ); + u = DefUnit( sys, "astGetEpoch", "TimeFrame", status ); + +/* Epoch is defined as a TDB value. If the timescale is stored in an angular + timescale such as UT1, then we would not normally be able to convert it + to TDB since knowledge of DUT1 is required (the difference between UTC + and UT1). Since the default Epoch value is not critical we assume a DUT1 + value of zero in this case. We first map the stored value to UT1 then + from UTC to TDB (using the approximation UT1 == UTC). */ + if( ts == AST__UT1 || ts == AST__GMST || + ts == AST__LAST || ts == AST__LMST ) { + map = MakeMap( this, sys, AST__MJD, ts, AST__UT1, 0.0, 0.0, u, + "d", "astGetEpoch", status ); + if( map ) { + astTran1( map, 1, &oldval, 1, &result ); + map = astAnnul( map ); + +/* Update the values to use when converting to TBD. */ + oldval = result; + ts = AST__UTC; + sys = AST__MJD; + u = "d"; + + } else if( astOK ) { + astError( AST__INTER, "astGetEpoch(%s): No Mapping from %s to " + "UT1 (AST internal programming error).", status, + astGetClass( this ), TimeScaleString( ts, status ) ); + } + } + +/* Now convert to TDB */ + map = MakeMap( this, sys, AST__MJD, ts, AST__TDB, 0.0, 0.0, u, + "d", "astGetEpoch", status ); + if( map ) { + oldval = astGetTimeOrigin( this ); + astTran1( map, 1, &oldval, 1, &result ); + map = astAnnul( map ); + + } else if( astOK ) { + astError( AST__INTER, "astGetEpoch(%s): No Mapping from %s to " + "TDB (AST internal programming error).", status, + astGetClass( this ), TimeScaleString( ts, status ) ); + } + +/* Otherwise, return the default Epoch value from the parent Frame. */ + } else { + result = (*parent_getepoch)( this_frame, status ); + } + +/* Return the result. */ + return result; +} + +static const char *GetLabel( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetLabel + +* Purpose: +* Access the Label string for a TimeFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *GetLabel( AstFrame *this, int axis, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetLabel method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Label string for a specified axis +* of a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstMapping *map; /* Mapping between units */ + AstSystemType system; /* Code identifying type of time coordinates */ + char *new_lab; /* Modified label string */ + const char *fmt; /* Pointer to original Format string */ + const char *result; /* Pointer to label string */ + double ltoff; /* Local Time offset from UTC (hours) */ + double orig; /* Time origin (seconds) */ + int fmtSet; /* Was Format attribute set? */ + int ndp; /* Number of decimal places for seconds field */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetLabel" ); + +/* Check if a value has been set for the required axis label string. If so, + invoke the parent astGetLabel method to obtain a pointer to it. */ + if ( astTestLabel( this, axis ) ) { + result = (*parent_getlabel)( this, axis, status ); + +/* Otherwise, provide a suitable default label. */ + } else { + +/* If the Format attribute indicates that time values will be formatted + as dates, then choose a suitable label. */ + fmt = astGetFormat( this, 0 ); + if( DateFormat( fmt, &ndp, NULL, status ) ) { + result = ( ndp >= 0 ) ? "Date/Time" : "Date"; + +/* Otherwise, identify the time coordinate system described by the + TimeFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default label string. */ + if ( astOK ) { + result = strcpy( getlabel_buff, SystemLabel( system, status ) ); + getlabel_buff[ 0 ] = toupper( getlabel_buff[ 0 ] ); + +/* If a non-zero TimeOrigin has been specified, include the offset now as a + date/time string. */ + orig = astGetTimeOrigin( this ); + if( orig != 0.0 ) { + +/* Save the Format attribute, and then temporarily set it to give a date/time + string. */ + fmt = astStore( NULL, fmt, strlen( fmt ) + 1 ); + fmtSet = astTestFormat( this, 0 ); + astSetFormat( this, 0, "iso.0" ); + +/* Format the origin value as an absolute time and append it to the + returned label string. Note, the origin always corresponds to a + TimeFrame axis value of zero. */ + sprintf( getlabel_buff + strlen( getlabel_buff ), " offset from %s", + astFormat( this, 0, 0.0 ) ); + +/* Re-instate the original Format value. */ + if( fmtSet ) { + astSetFormat( this, 0, fmt ); + } else { + astClearFormat( this, 0 ); + } + +/* Free the memory holding the copy of the format string. */ + fmt = astFree( (char *) fmt ); + +/* If the time of day is "00:00:00", remove it. */ + if( !strcmp( getlabel_buff + strlen( getlabel_buff ) - 8, "00:00:00" ) ) { + getlabel_buff[ strlen( getlabel_buff ) - 8 ] = 0; + } + } + +/* Modify this default to take account of the current value of the Unit + attribute, if set. */ + if( astTestUnit( this, axis ) ) { + +/* Find a Mapping from the default Units for the current System, to the + units indicated by the Unit attribute. This Mapping is used to modify + the existing default label appropriately. For instance, if the default + units is "yr" and the actual units is "log(yr)", then the default label + of "Julian epoch" is changed to "log( Julian epoch )". */ + map = astUnitMapper( DefUnit( system, "astGetLabel", + astGetClass( this ), status ), + astGetUnit( this, axis ), result, + &new_lab ); + if( new_lab ) { + result = strcpy( getlabel_buff, new_lab ); + new_lab = astFree( new_lab ); + } + +/* Annul the unused Mapping. */ + if( map ) map = astAnnul( map ); + } + } + } + +/* If the time is a Local Time, indicate the offset from UTC. */ + if( astGetTimeScale( this ) == AST__LT ) { + ltoff = astGetLTOffset( this ); + if( ltoff >= 0.0 ) { + sprintf( getlabel_buff, "%s (UTC+%g)", result, ltoff ); + } else { + sprintf( getlabel_buff, "%s (UTC-%g)", result, -ltoff ); + } + result = getlabel_buff; + } + } + +/* Return the result. */ + return result; +} + +static const char *GetSymbol( AstFrame *this, int axis, int *status ) { +/* +* Name: +* GetSymbol + +* Purpose: +* Obtain a pointer to the Symbol string for a TimeFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *GetSymbol( AstFrame *this, int axis, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetSymbol method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Symbol string for a specified axis +* of a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* axis +* Axis index (zero-based) identifying the axis for which information is +* required. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated character string containing the +* requested information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstMapping *map; /* Mapping between units */ + AstSystemType system; /* Code identifying type of sky coordinates */ + char *new_sym; /* Modified symbol string */ + const char *result; /* Pointer to symbol string */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this); + +/* Initialise. */ + result = NULL; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetSymbol" ); + +/* Check if a value has been set for the required axis symbol string. If so, + invoke the parent astGetSymbol method to obtain a pointer to it. */ + if ( astTestSymbol( this, axis ) ) { + result = (*parent_getsymbol)( this, axis, status ); + +/* Otherwise, identify the sky coordinate system described by the TimeFrame. */ + } else { + system = astGetSystem( this ); + +/* If OK, supply a pointer to a suitable default Symbol string. */ + if ( astOK ) { + + if( system == AST__MJD ) { + result = "MJD"; + } else if( system == AST__JD ) { + result = "JD"; + } else if( system == AST__BEPOCH ) { + result = "BEP"; + } else if( system == AST__JEPOCH ) { + result = "JEP"; + +/* Report an error if the coordinate system was not recognised. */ + } else { + astError( AST__SCSIN, "astGetSymbol(%s): Corrupt %s contains " + "invalid System identification code (%d).", status, + astGetClass( this ), astGetClass( this ), (int) system ); + } + +/* Modify this default to take account of the current value of the Unit + attribute, if set. */ + if( astTestUnit( this, axis ) ) { + +/* Find a Mapping from the default Units for the current System, to the + units indicated by the Unit attribute. This Mapping is used to modify + the existing default symbol appropriately. For instance, if the default + units is "yr" and the actual units is "log(yr)", then the default symbol + of "JEP" is changed to "log( JEP )". */ + map = astUnitMapper( DefUnit( system, "astGetSymbol", + astGetClass( this ), status ), + astGetUnit( this, axis ), result, + &new_sym ); + if( new_sym ) { + result = strcpy( getsymbol_buff, new_sym ); + new_sym = astFree( new_sym ); + } + +/* Annul the unused Mapping. */ + if( map ) map = astAnnul( map ); + + } + } + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetAlignSystem + +* Purpose: +* Obtain the AlignSystem attribute for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "Specframe.h" +* AstSystemType GetAlignSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetAlignSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the AlignSystem attribute for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The AlignSystem value. + +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to TimeFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* If a AlignSystem attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestAlignSystem( this ) ) { + result = (*parent_getalignsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__MJD; + } + +/* Return the result. */ + return result; +} + +static AstTimeScaleType GetAlignTimeScale( AstTimeFrame *this, int *status ) { +/* +*+ +* Name: +* astGetAlignTimeScale + +* Purpose: +* Obtain the AlignTimeScale attribute for a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* AstTimeScaleType GetAlignTimeScale( AstTimeFrame *this ) + +* Class Membership: +* TimeFrame virtual function + +* Description: +* This function returns the System attribute for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADTS is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstTimeScaleType result; + AstTimeScaleType ts; + +/* Initialise. */ + result = AST__BADTS; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If a value has been set, return it. */ + if( this->aligntimescale != AST__BADTS ) { + result = this->aligntimescale; + +/* Otherwise, return a default depending on the current TimeScale value */ + } else { + ts = astGetTimeScale( this ); + if ( ts == AST__UT1 || ts == AST__LAST || ts == AST__LMST || ts == AST__GMST ) { + result = AST__UT1; + } else { + result = AST__TAI; + } + + } + +/* Return the result. */ + return result; +} + +static AstSystemType GetSystem( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetSystem + +* Purpose: +* Obtain the System attribute for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* AstSystemType GetSystem( AstFrame *this_frame, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetSystem protected +* method inherited from the Frame class). + +* Description: +* This function returns the System attribute for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADSYSTEM is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to TimeFrame structure */ + AstSystemType result; /* Value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* If a System attribute has been set, invoke the parent method to obtain + it. */ + if ( astTestSystem( this ) ) { + result = (*parent_getsystem)( this_frame, status ); + +/* Otherwise, provide a suitable default. */ + } else { + result = AST__MJD; + } + +/* Return the result. */ + return result; +} + +static AstTimeScaleType GetTimeScale( AstTimeFrame *this, int *status ) { +/* +*+ +* Name: +* astGetTimeScale + +* Purpose: +* Obtain the TimeScale attribute for a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* AstTimeScaleType GetTimeScale( AstTimeFrame *this ) + +* Class Membership: +* TimeFrame virtual function + +* Description: +* This function returns the System attribute for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. + +* Returned Value: +* The System value. + +* Notes: +* - AST__BADTS is returned if this function is invoked with +* the global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstTimeScaleType result; + +/* Initialise. */ + result = AST__BADTS; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If a value has been set, return it. */ + if( this->timescale != AST__BADTS ) { + result = this->timescale; + +/* Otherwise, return a default depending on the current System value. */ + } else { + if ( astGetSystem( this ) == AST__BEPOCH ) { + result = AST__TT; + } else { + result = AST__TAI; + } + + } + +/* Return the result. */ + return result; +} + +static const char *GetTitle( AstFrame *this_frame, int *status ) { +/* +* Name: +* GetTitle + +* Purpose: +* Obtain a pointer to the Title string for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *GetTitle( AstFrame *this_frame, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetTitle method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Title string for a TimeFrame. +* A pointer to a suitable default string is returned if no Title value has +* previously been set. + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null-terminated character string containing the requested +* information. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Declare the thread specific global data */ + AstSystemType system; /* Code identifying type of coordinates */ + AstTimeScaleType ts; /* Time scale value */ + AstTimeFrame *this; /* Pointer to TimeFrame structure */ + const char *fmt; /* Pointer to original Format string */ + const char *result; /* Pointer to result string */ + double ltoff; /* Local Time offset from UTC (hours) */ + double orig; /* Time origin (seconds) */ + int fmtSet; /* Was Format attribute set? */ + int nc; /* No. of characters added */ + int ndp; /* Number of decimal places */ + int pos; /* Buffer position to enter text */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the structure holding thread-specific global data. */ + astGET_GLOBALS(this_frame); + +/* Initialise. */ + result = NULL; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* See if a Title string has been set. If so, use the parent astGetTitle + method to obtain a pointer to it. */ + if ( astTestTitle( this ) ) { + result = (*parent_gettitle)( this_frame, status ); + +/* Otherwise, we will generate a default Title string. Obtain the values of the + TimeFrame's attributes that determine what this string will be. */ + } else { + system = astGetSystem( this ); + orig = GetTimeOriginCur( this, status ); + ts = astGetTimeScale( this ); + if ( astOK ) { + result = gettitle_buff; + +/* Begin with the system's default label. */ + pos = sprintf( gettitle_buff, "%s", SystemLabel( system, status ) ); + gettitle_buff[ 0 ] = toupper( gettitle_buff[ 0 ] ); + +/* Append the time scale code, if a value has been set for the timescale. + Do not do this if the system is BEPOCH since BEPOCH can only be used + with the TT timescale. */ + if( system != AST__BEPOCH && astTestTimeScale( this ) ) { + nc = sprintf( gettitle_buff + pos, " [%s", TimeScaleString( ts, status ) ); + pos += nc; + +/* For Local Time, include the offset from UTC. */ + if( ts == AST__LT ) { + ltoff = astGetLTOffset( this ); + if( ltoff >= 0.0 ) { + nc = sprintf( gettitle_buff + pos, " (UTC+%g)", ltoff ); + } else { + nc = sprintf( gettitle_buff + pos, " (UTC-%g)", -ltoff ); + } + pos += nc; + } + +/* Close the brackets. */ + nc = sprintf( gettitle_buff + pos, "]" ); + pos += nc; + } + +/* If a non-zero offset has been specified, and the Format attribute does + not indicate a date string (which is always absolute), include the + offset now as a date/time string. */ + fmt = astGetFormat( this, 0 ); + if( orig != 0.0 && !DateFormat( fmt, &ndp, NULL, status ) ) { + +/* Save the Format attribute, and then temporarily set it to give a date/time + string. */ + fmt = astStore( NULL, fmt, strlen( fmt ) + 1 ); + fmtSet = astTestFormat( this, 0 ); + astSetFormat( this, 0, "iso.0" ); + +/* Format the origin value as an absolute time and append it to the + returned title string. Note, the origin always corresponds to a + TimeFrame axis value of zero. */ + nc = sprintf( gettitle_buff+pos, " offset from %s", + astFormat( this, 0, 0.0 ) ); + pos += nc; + +/* Re-instate the original Format value. */ + if( fmtSet ) { + astSetFormat( this, 0, fmt ); + } else { + astClearFormat( this, 0 ); + } + +/* Free the Format string copy. */ + fmt = astFree( (char *) fmt ); + + } + } + } + +/* If an error occurred, clear the returned pointer value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetUnit( AstFrame *this_frame, int axis, int *status ) { +/* +* Name: +* GetUnit + +* Purpose: +* Obtain a pointer to the Unit string for a TimeFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *GetUnit( AstFrame *this_frame, int axis ) + +* Class Membership: +* TimeFrame member function (over-rides the astGetUnit method inherited +* from the Frame class). + +* Description: +* This function returns a pointer to the Unit string for a specified axis +* of a TimeFrame. If the Unit attribute has not been set for the axis, a +* pointer to a suitable default string is returned instead. + +* Parameters: +* this +* Pointer to the TimeFrame. +* axis +* The number of the axis (zero-based) for which information is required. + +* Returned Value: +* A pointer to a null-terminated string containing the Unit value. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to the TimeFrame structure */ + AstSystemType system; /* The TimeFrame's System value */ + const char *result; /* Pointer value to return */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astGetUnit" ); + +/* If a value has been set for the Unit attribute, use the parent + GetUnit method to return a pointer to the required Unit string. */ + if( astTestUnit( this, axis ) ){ + result = (*parent_getunit)( this_frame, axis, status ); + +/* Otherwise, identify the time coordinate system described by the + TimeFrame. */ + } else { + system = astGetSystem( this ); + +/* Return a string describing the default units. */ + result = DefUnit( system, "astGetUnit", astGetClass( this ), status ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +void astInitTimeFrameVtab_( AstTimeFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitTimeFrameVtab + +* Purpose: +* Initialise a virtual function table for a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* void astInitTimeFrameVtab( AstTimeFrameVtab *vtab, const char *name ) + +* Class Membership: +* TimeFrame vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the TimeFrame class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstFrameVtab *frame; /* Pointer to Frame component of Vtab */ + AstMapping *map; /* Temporary Maping */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + double utc_epoch; /* Unix epoch as a UTC MJD */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitFrameVtab( (AstFrameVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsATimeFrame) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstFrameVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + + vtab->ClearAlignTimeScale = ClearAlignTimeScale; + vtab->TestAlignTimeScale = TestAlignTimeScale; + vtab->GetAlignTimeScale = GetAlignTimeScale; + vtab->SetAlignTimeScale = SetAlignTimeScale; + + vtab->ClearTimeOrigin = ClearTimeOrigin; + vtab->TestTimeOrigin = TestTimeOrigin; + vtab->GetTimeOrigin = GetTimeOrigin; + vtab->SetTimeOrigin = SetTimeOrigin; + + vtab->ClearLTOffset = ClearLTOffset; + vtab->TestLTOffset = TestLTOffset; + vtab->GetLTOffset = GetLTOffset; + vtab->SetLTOffset = SetLTOffset; + + vtab->ClearTimeScale = ClearTimeScale; + vtab->TestTimeScale = TestTimeScale; + vtab->GetTimeScale = GetTimeScale; + vtab->SetTimeScale = SetTimeScale; + + vtab->CurrentTime = CurrentTime; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + frame = (AstFrameVtab *) vtab; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_getdomain = frame->GetDomain; + frame->GetDomain = GetDomain; + + parent_getsystem = frame->GetSystem; + frame->GetSystem = GetSystem; + parent_setsystem = frame->SetSystem; + frame->SetSystem = SetSystem; + parent_clearsystem = frame->ClearSystem; + frame->ClearSystem = ClearSystem; + + parent_getalignsystem = frame->GetAlignSystem; + frame->GetAlignSystem = GetAlignSystem; + + parent_getlabel = frame->GetLabel; + frame->GetLabel = GetLabel; + + parent_getsymbol = frame->GetSymbol; + frame->GetSymbol = GetSymbol; + + parent_gettitle = frame->GetTitle; + frame->GetTitle = GetTitle; + + parent_getepoch = frame->GetEpoch; + frame->GetEpoch = GetEpoch; + + parent_getunit = frame->GetUnit; + frame->GetUnit = GetUnit; + + parent_setunit = frame->SetUnit; + frame->SetUnit = SetUnit; + + parent_match = frame->Match; + frame->Match = Match; + + parent_overlay = frame->Overlay; + frame->Overlay = Overlay; + + parent_subframe = frame->SubFrame; + frame->SubFrame = SubFrame; + + parent_format = frame->Format; + frame->Format = Format; + + parent_unformat = frame->Unformat; + frame->Unformat = Unformat; + + parent_abbrev = frame->Abbrev; + frame->Abbrev = Abbrev; + + parent_fields = frame->Fields; + frame->Fields = Fields; + + parent_gap = frame->Gap; + frame->Gap = Gap; + + parent_centre = frame->Centre; + frame->Centre = Centre; + +/* Store replacement pointers for methods which will be over-ridden by new + member functions implemented here. */ + frame->GetActiveUnit = GetActiveUnit; + frame->TestActiveUnit = TestActiveUnit; + frame->ValidateSystem = ValidateSystem; + frame->SystemString = SystemString; + frame->SystemCode = SystemCode; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetDump( vtab, Dump, "TimeFrame", + "Description of time coordinate system" ); + +/* Convert the Unix Epoch (00:00:00 UTC 1 January 1970 AD) from UTC to TAI. */ + LOCK_MUTEX2 + map = MakeMap( NULL, AST__MJD, AST__MJD, AST__UTC, AST__TAI, + 0.0, 0.0, "d", "d", "astInitTimeFrameVtab", status ); + utc_epoch = UNIX_EPOCH; + astTran1( map, 1, &utc_epoch, 1, &tai_epoch ); + map = astAnnul( map ); + UNLOCK_MUTEX2 + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static AstMapping *MakeMap( AstTimeFrame *this, AstSystemType sys1, + AstSystemType sys2, AstTimeScaleType ts1, + AstTimeScaleType ts2, double off1, double off2, + const char *unit1, const char *unit2, + const char *method, int *status ){ +/* +* Name: +* MakeMap + +* Purpose: +* Make a Mapping between stated timescales and systems. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* AstMapping *MakeMap( AstTimeFrame *this, AstSystemType sys1, +* AstSystemType sys2, AstTimeScaleType ts1, +* AstTimeScaleType ts2, double off1, double off2, +* const char *unit1, const char unit2, +* const char *method, int *status ) + +* Class Membership: +* TimeFrame member function + +* Description: +* This function creates a Mapping from a stated pair of System and +* TimeScale to another stated pair. + +* Parameters: +* this +* A TimeFrame which specifies extra attributes (the clock position, +* time zone, etc) for both input and output. +* sys1 +* The input System. +* sys2 +* The output System. +* ts1 +* The input System. +* ts2 +* The output System. +* off1 +* The axis offset used with the input, in the defaults units +* associated with "sys1". +* off2 +* The axis offset used with the output, in the defaults units +* associated with "sys2". +* unit1 +* The input units. +* unit2 +* The output units. +* method +* A string containing the method name to include in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new Mapping. NULL if the timescales were +* incompatible. + +*/ + + +/* Local Variables: */ + AstMapping *result; + AstMapping *tmap; + AstMapping *tmap2; + AstMapping *umap; + AstMapping *umap1; + AstMapping *umap2; + AstTimeMap *timemap; + const char *du; + double args[ 4 ]; + double args_lt[ 1 ]; + double args_ut[ 1 ]; + double shift; + +/* Check the global error status. */ + result = NULL; + if ( !astOK ) return result; + +/* If the timescales are equal... */ + if( ts1 == ts2 ) { + +/* and the time systems are equal... */ + if( sys1 == sys2 ) { + +/* and the time offsets are equal... */ + if( astEQUALS( off1, off2, 1.0E3 ) ) { + +/* and the units are equal, return a UnitMap. */ + if( !strcmp( unit1, unit2 ) ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + +/* If only the units differ, return the appropriate units Mapping. */ + } else { + result = astUnitMapper( unit1, unit2, NULL, NULL ); + } + +/* If the time offsets differ... */ + } else { + +/* Transform the difference in offsets from the default units associated + with the (common) system, to the units associated with the output. */ + shift = off1 - off2; + du = DefUnit( sys1, method, "TimeFrame", status ); + if( du && strcmp( du, unit2 ) && shift != 0.0 ) { + umap = astUnitMapper( DefUnit( sys1, method, "TimeFrame", status ), + unit2, NULL, NULL ); + astTran1( umap, 1, &shift, 1, &shift ); + umap = astAnnul( umap ); + } + +/* Create a ShiftMap to apply the shift. */ + result = (AstMapping *) astShiftMap( 1, &shift, "", status ); + +/* If the input and output units also differ, include the appropriate units + Mapping. */ + if( strcmp( unit1, unit2 ) ) { + umap = astUnitMapper( unit1, unit2, NULL, NULL ); + tmap = (AstMapping *) astCmpMap( umap, result, 1, "", status ); + umap = astAnnul( umap ); + (void) astAnnul( result ); + result = tmap; + } + } + } + } + +/* If the systems and/or timescales differ, we convert first from the + input frame to a common frame, then from the common frame to the output + frame. */ + if( !result ) { + +/* First, a Mapping from the input units to the default units for the + input System (these are the units expected by the TimeMap conversions). */ + umap1 = astUnitMapper( unit1, DefUnit( sys1, method, "TimeFrame", status ), + NULL, NULL ); + +/* Now create a null TimeMap. */ + timemap = astTimeMap( 0, "", status ); + +/* Store the input time offsets to use. They correspond to the same moment in + time (the second is the MJD equivalent of the first). */ + args[ 0 ] = off1; + args[ 1 ] = ToMJD( sys1, off1, status ); + +/* Add a conversion from the input System to MJD. */ + if( sys1 == AST__JD ) { + astTimeAdd( timemap, "JDTOMJD", args ); + + } else if( sys1 == AST__JEPOCH ) { + astTimeAdd( timemap, "JEPTOMJD", args ); + + } else if( sys1 == AST__BEPOCH ) { + astTimeAdd( timemap, "BEPTOMJD", args ); + } + +/* All timescale conversions except UTTOUTC and UTCTOUT require the input (MJD) + offset as the first argument. In general, the observers longitude, latitude + and altitude are also needed. The Frame class stores longitude values in a + +ve eastwards sense, but the TimeMap class needs +ve westwards, so negate + the longitude. */ + args[ 0 ] = args[ 1 ]; + args[ 1 ] = this ? -astGetObsLon( this ) : 0.0; + args[ 2 ] = this ? astGetObsLat( this ) : 0.0; + args[ 3 ] = this ? astGetObsAlt( this ) : 0.0; + +/* The UTTOUTC and UTCTOUT conversions required just the DUT1 value. */ + args_ut[ 0 ] = this ? astGetDut1( this ) : 0.0; + +/* The LTTOUTC and UTCTOLT conversions required just the time zone + correction. */ + args_lt[ 0 ] = this ? astGetLTOffset( this ) : 0.0; + +/* If the input and output timescales differ, now add a conversion from the + input timescale to TAI. */ + if( ts1 != ts2 ) { + if( ts1 == AST__TAI ) { + + } else if( ts1 == AST__UTC ) { + astTimeAdd( timemap, "UTCTOTAI", args ); + + } else if( ts1 == AST__TT ) { + astTimeAdd( timemap, "TTTOTAI", args ); + + } else if( ts1 == AST__TDB ) { + astTimeAdd( timemap, "TDBTOTT", args ); + astTimeAdd( timemap, "TTTOTAI", args ); + + } else if( ts1 == AST__TCG ) { + astTimeAdd( timemap, "TCGTOTT", args ); + astTimeAdd( timemap, "TTTOTAI", args ); + + } else if( ts1 == AST__LT ) { + astTimeAdd( timemap, "LTTOUTC", args_lt ); + astTimeAdd( timemap, "UTCTOTAI", args ); + + } else if( ts1 == AST__TCB ) { + astTimeAdd( timemap, "TCBTOTDB", args ); + astTimeAdd( timemap, "TDBTOTT", args ); + astTimeAdd( timemap, "TTTOTAI", args ); + + } else if( ts1 == AST__UT1 ) { + astTimeAdd( timemap, "UTTOUTC", args_ut ); + astTimeAdd( timemap, "UTCTOTAI", args ); + + } else if( ts1 == AST__GMST ) { + astTimeAdd( timemap, "GMSTTOUT", args ); + astTimeAdd( timemap, "UTTOUTC", args_ut ); + astTimeAdd( timemap, "UTCTOTAI", args ); + + } else if( ts1 == AST__LAST ) { + astTimeAdd( timemap, "LASTTOLMST", args ); + astTimeAdd( timemap, "LMSTTOGMST", args ); + astTimeAdd( timemap, "GMSTTOUT", args ); + astTimeAdd( timemap, "UTTOUTC", args_ut ); + astTimeAdd( timemap, "UTCTOTAI", args ); + + } else if( ts1 == AST__LMST ) { + astTimeAdd( timemap, "LMSTTOGMST", args ); + astTimeAdd( timemap, "GMSTTOUT", args ); + astTimeAdd( timemap, "UTTOUTC", args_ut ); + astTimeAdd( timemap, "UTCTOTAI", args ); + } + +/* Now add a conversion from TAI to the output timescale. */ + if( ts2 == AST__TAI ) { + + } else if( ts2 == AST__UTC ) { + astTimeAdd( timemap, "TAITOUTC", args ); + + } else if( ts2 == AST__TT ) { + astTimeAdd( timemap, "TAITOTT", args ); + + } else if( ts2 == AST__TDB ) { + astTimeAdd( timemap, "TAITOTT", args ); + astTimeAdd( timemap, "TTTOTDB", args ); + + } else if( ts2 == AST__TCG ) { + astTimeAdd( timemap, "TAITOTT", args ); + astTimeAdd( timemap, "TTTOTCG", args ); + + } else if( ts2 == AST__TCB ) { + astTimeAdd( timemap, "TAITOTT", args ); + astTimeAdd( timemap, "TTTOTDB", args ); + astTimeAdd( timemap, "TDBTOTCB", args ); + + } else if( ts2 == AST__UT1 ) { + astTimeAdd( timemap, "TAITOUTC", args ); + astTimeAdd( timemap, "UTCTOUT", args_ut ); + + } else if( ts2 == AST__GMST ) { + astTimeAdd( timemap, "TAITOUTC", args ); + astTimeAdd( timemap, "UTCTOUT", args_ut ); + astTimeAdd( timemap, "UTTOGMST", args ); + + } else if( ts2 == AST__LAST ) { + astTimeAdd( timemap, "TAITOUTC", args ); + astTimeAdd( timemap, "UTCTOUT", args_ut ); + astTimeAdd( timemap, "UTTOGMST", args ); + astTimeAdd( timemap, "GMSTTOLMST", args ); + astTimeAdd( timemap, "LMSTTOLAST", args ); + + } else if( ts2 == AST__LMST ) { + astTimeAdd( timemap, "TAITOUTC", args ); + astTimeAdd( timemap, "UTCTOUT", args_ut ); + astTimeAdd( timemap, "UTTOGMST", args ); + astTimeAdd( timemap, "GMSTTOLMST", args ); + + } else if( ts2 == AST__LT ) { + astTimeAdd( timemap, "TAITOUTC", args ); + astTimeAdd( timemap, "UTCTOLT", args_lt ); + + } + } + +/* Add a conversion from MJD to the output System, if needed. */ + args[ 1 ] = off2; + if( sys2 == AST__MJD ) { + if( args[ 0 ] != off2 ) astTimeAdd( timemap, "MJDTOMJD", args ); + + } else if( sys2 == AST__JD ) { + astTimeAdd( timemap, "MJDTOJD", args ); + + } else if( sys2 == AST__JEPOCH ) { + astTimeAdd( timemap, "MJDTOJEP", args ); + + } else if( sys2 == AST__BEPOCH ) { + astTimeAdd( timemap, "MJDTOBEP", args ); + } + +/* Now, create a Mapping from the default units for the output System (these + are the units produced by the TimeMap conversions) to the requested + output units. */ + umap2 = astUnitMapper( DefUnit( sys2, method, "TimeFrame", status ), unit2, + NULL, NULL ); + +/* If OK, combine the Mappings in series. Note, umap1 and umap2 should + always be non-NULL because the suitablity of units strings is checked + within OriginSystem - called from within SetSystem. */ + if( umap1 && umap2 ) { + tmap = (AstMapping *) astCmpMap( umap1, timemap, 1, "", status ); + tmap2 = (AstMapping *) astCmpMap( tmap, umap2, 1, "", status ); + tmap = astAnnul( tmap ); + result = astSimplify( tmap2 ); + tmap2 = astAnnul( tmap2 ); + } + +/* Free remaining resources */ + if( umap1 ) umap1 = astAnnul( umap1 ); + if( umap2 ) umap2 = astAnnul( umap2 ); + timemap = astAnnul( timemap ); + } + +/* Return NULL if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; + +} + +static int MakeTimeMapping( AstTimeFrame *target, AstTimeFrame *result, + AstTimeFrame *align_frm, int report, + AstMapping **map, int *status ) { +/* +* Name: +* MakeTimeMapping + +* Purpose: +* Generate a Mapping between two TimeFrames. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int MakeTimeMapping( AstTimeFrame *target, AstTimeFrame *result, +* AstTimeFrame *align_frm, int report, +* AstMapping **map, int *status ) { + +* Class Membership: +* TimeFrame member function. + +* Description: +* This function takes two TimeFrames and generates a Mapping that +* converts between them, taking account of differences in their +* coordinate systems, offsets, timescales, units, etc. + +* Parameters: +* target +* Pointer to the first TimeFrame. +* result +* Pointer to the second TimeFrame. +* align_frm +* A TimeFrame defining the system and time scale in which to +* align the target and result TimeFrames. The AlignSystem and +* AlignTimeScale attributes are used for this purpose. +* report +* Should errors be reported if no match is possible? These reports +* will describe why no match was possible. +* map +* Pointer to a location which is to receive a pointer to the +* returned Mapping. The forward transformation of this Mapping +* will convert from "target" coordinates to "result" +* coordinates, and the inverse transformation will convert in +* the opposite direction (all coordinate values in radians). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the Mapping could be generated, or zero if the two +* TimeFrames are sufficiently un-related that no meaningful Mapping +* can be produced (albeit an "unmeaningful" Mapping will be returned +* in this case, which will need to be annulled). + +* Notes: +* A value of zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *map1; /* Intermediate Mapping */ + AstMapping *map2; /* Intermediate Mapping */ + AstMapping *tmap; /* Intermediate Mapping */ + AstSystemType sys1; /* Code to identify input system */ + AstSystemType sys2; /* Code to identify output system */ + AstTimeScaleType align_ts; /* Alignment time scale */ + AstTimeScaleType ts1; /* Input time scale */ + AstTimeScaleType ts2; /* Output time scale */ + const char *align_unit; /* Units used for alignment */ + const char *u1; /* Input target units */ + const char *u2; /* Output target units */ + double align_off; /* Axis offset */ + double ltoff1; /* Input axis Local Time offset */ + double ltoff2; /* Output axis Local Time offset */ + double off1; /* Input axis offset */ + double off2; /* Output axis offset */ + int arclk; /* Align->result depends on clock position? */ + int ardut; /* Align->result depends on Dut1? */ + int arlto; /* Align->result depends on LT offset? */ + int clkdiff; /* Do target and result clock positions differ? */ + int dut1diff; /* Do target and result Dut1 values differ? */ + int ltodiff; /* Do target and result LTOffset values differ? */ + int match; /* Mapping can be generated? */ + int taclk; /* Target->align depends on clock position? */ + int tadut; /* Target->align depends on Dut1? */ + int talto; /* Target->align depends on LT offset? */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise the returned values. */ + match = 0; + *map = NULL; + +/* Get the required properties of the input (target) TimeFrame */ + sys1 = astGetSystem( target ); + ts1 = astGetTimeScale( target ); + off1 = astGetTimeOrigin( target ); + u1 = astGetUnit( target, 0 ); + ltoff1= astGetLTOffset( target ); + +/* Get the required properties of the output (result) TimeFrame */ + sys2 = astGetSystem( result ); + ts2 = astGetTimeScale( result ); + off2 = astGetTimeOrigin( result ); + u2 = astGetUnit( result, 0 ); + ltoff2= astGetLTOffset( result ); + +/* Get the timescale in which alignment is to be performed. The alignment + System does not matter since they all supported time systems are linearly + related, and so the choice of alignment System has no effect on the total + Mapping. We arbitrarily choose MJD as the alignment System (if needed). */ + align_ts = astGetAlignTimeScale( align_frm ); + +/* The main difference between this function and the MakeMap function is + that this function takes account of the requested alignment frame. But + the alignment Frame only makes a difference to the overall Mapping if + 1) the observer's positions are different in the target and result Frame, + and 2) one or both of the Mappings to or from the alignment frame depends + on the observer's position. If either of these 2 conditions is not met, + then the alignment frame can be ignored, and the simpler MakeMap function + can be called. See if the observer's positions differ. */ + clkdiff = ( astGetObsLon( target ) != astGetObsLon( result ) || + astGetObsLat( target ) != astGetObsLat( result ) || + astGetObsAlt( target ) != astGetObsAlt( result ) ); + +/* See if the Mapping from target to alignment frame depends on the + observer's position. */ + taclk = CLOCK_SCALE( ts1 ) || CLOCK_SCALE( align_ts ); + +/* See if the Mapping from alignment to result frame depends on the + observer's position. */ + arclk = CLOCK_SCALE( align_ts ) || CLOCK_SCALE( ts2 ); + +/* In addition, the alignment frame is significant if either of the Mappings + depends on DUT1 and the values of the DUT1 attribute are different for the + two TimeFrames. */ + dut1diff = ( astGetDut1( target ) != astGetDut1( result ) ); + tadut = DUT1_SCALE( ts1 ) != DUT1_SCALE( align_ts ); + ardut = DUT1_SCALE( align_ts ) != DUT1_SCALE( ts2 ); + +/* In addition, the alignment frame is significant if either of the Mappings + depends on LTOffset and the values of the LTOffset attribute are different + for the two TimeFrames. */ + ltodiff = ( ltoff1 != ltoff2 ); + talto = LTOFFSET_SCALE( ts1 ) != LTOFFSET_SCALE( align_ts ); + arlto = LTOFFSET_SCALE( align_ts ) != LTOFFSET_SCALE( ts2 ); + +/* If the alignment frame can be ignored, use MakeMap */ + if( ( !clkdiff || !( taclk || arclk ) ) && + ( !ltodiff || !( talto || arlto ) ) && + ( !dut1diff || !( tadut || ardut ) ) ) { + *map = MakeMap( target, sys1, sys2, ts1, ts2, off1, off2, u1, u2, + "astSubFrame", status ); + if( *map ) match = 1; + +/* Otherwise, we create the Mapping in two parts; first a Mapping from + the target Frame to the alignment Frame (using the target clock, dut1 + and ltoffset), then a Mapping from the alignment Frame to the results + Frame (using the result clock, dut1 and ltoffset). */ + } else { + +/* Create a Mapping from target units/system/timescale/offset to MJD in + the alignment timescale with default units and offset equal to the MJD + equivalent of the target offset. */ + align_off = ToMJD( sys1, off1, status ); + align_unit = DefUnit( AST__MJD, "MakeTimeMap", "TimeFrame", status ); + map1 = MakeMap( target, sys1, AST__MJD, ts1, align_ts, off1, align_off, + u1, align_unit, "MakeTimeMap", status ); + +/* Report an error if the timescales were incompatible. */ + if( !map1 ){ + match = 0; + if( report && astOK ) { + astError( AST__INCTS, "astMatch(%s): Alignment in requested " + "timescale (%s) is not possible since one or both of the " + "TimeFrames being aligned refer to the %s timescale.", status, + astGetClass( target ), TimeScaleString( align_ts, status ), + TimeScaleString( ts1, status ) ); + } + } + +/* We now create a Mapping that converts from the alignment System (MJD), + TimeScale and offset to the result coordinate system. */ + map2 = MakeMap( result, AST__MJD, sys2, align_ts, ts2, align_off, off2, + align_unit, u2, "MakeTimeMap", status ); + +/* Report an error if the timescales were incompatible. */ + if( !map2 ){ + match = 0; + if( report && astOK ) { + astError( AST__INCTS, "astMatch(%s): Alignment in requested " + "timescale (%s) is not possible since one or both of the " + "TimeFrames being aligned refer to the %s timescale.", status, + astGetClass( result ), TimeScaleString( align_ts, status ), + TimeScaleString( ts2, status ) ); + } + } + +/* Combine these two Mappings. */ + if( map1 && map2 ) { + match = 1; + tmap = (AstMapping *) astCmpMap( map1, map2, 1, "", status ); + *map = astSimplify( tmap ); + tmap = astAnnul( tmap ); + } + +/* Free resources. */ + if( map1 ) map1 = astAnnul( map1 ); + if( map2 ) map2 = astAnnul( map2 ); + } + +/* If an error occurred, annul the returned Mapping and clear the returned + values. */ + if ( !astOK ) { + *map = astAnnul( *map ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static int Match( AstFrame *template_frame, AstFrame *target, int matchsub, + int **template_axes, int **target_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* Match + +* Purpose: +* Determine if conversion is possible between two coordinate systems. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int Match( AstFrame *template, AstFrame *target, int matchsub, +* int **template_axes, int **target_axes, +* AstMapping **map, AstFrame **result, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the protected astMatch method +* inherited from the Frame class). + +* Description: +* This function matches a "template" TimeFrame to a "target" Frame and +* determines whether it is possible to convert coordinates between them. +* If it is, a mapping that performs the transformation is returned along +* with a new Frame that describes the coordinate system that results when +* this mapping is applied to the "target" coordinate system. In addition, +* information is returned to allow the axes in this "result" Frame to be +* associated with the corresponding axes in the "target" and "template" +* Frames from which they are derived. + +* Parameters: +* template +* Pointer to the template TimeFrame. This describes the coordinate +* system (or set of possible coordinate systems) into which we wish to +* convert our coordinates. +* target +* Pointer to the target Frame. This describes the coordinate system in +* which we already have coordinates. +* matchsub +* If zero then a match only occurs if the template is of the same +* class as the target, or of a more specialised class. If non-zero +* then a match can occur even if this is not the case. +* template_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the template TimeFrame axis from +* which it is derived. If it is not derived from any template +* TimeFrame axis, a value of -1 will be returned instead. +* target_axes +* Address of a location where a pointer to int will be returned if the +* requested coordinate conversion is possible. This pointer will point +* at a dynamically allocated array of integers with one element for each +* axis of the "result" Frame (see below). It must be freed by the caller +* (using astFree) when no longer required. +* +* For each axis in the result Frame, the corresponding element of this +* array will return the index of the target Frame axis from which it +* is derived. If it is not derived from any target Frame axis, a value +* of -1 will be returned instead. +* map +* Address of a location where a pointer to a new Mapping will be +* returned if the requested coordinate conversion is possible. If +* returned, the forward transformation of this Mapping may be used to +* convert coordinates between the "target" Frame and the "result" +* Frame (see below) and the inverse transformation will convert in the +* opposite direction. +* result +* Address of a location where a pointer to a new Frame will be returned +* if the requested coordinate conversion is possible. If returned, this +* Frame describes the coordinate system that results from applying the +* returned Mapping (above) to the "target" coordinate system. In +* general, this Frame will combine attributes from (and will therefore +* be more specific than) both the target and the template Frames. In +* particular, when the template allows the possibility of transformaing +* to any one of a set of alternative coordinate systems, the "result" +* Frame will indicate which of the alternatives was used. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if the requested coordinate conversion is +* possible. Otherwise zero is returned (this will not in itself result in +* an error condition). + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* This implementation addresses the matching of a TimeFrame class +* object to any other class of Frame. A TimeFrame will match any class +* of TimeFrame (i.e. possibly from a derived class) but will not match +* a less specialised class of Frame. +*/ + + AstFrame *frame0; /* Pointer to Frame underlying axis 0 */ + AstTimeFrame *template; /* Pointer to template TimeFrame structure */ + int iaxis0; /* Axis index underlying axis 0 */ + int iaxis; /* Axis index */ + int match; /* Coordinate conversion possible? */ + int target_axis0; /* Index of TimeFrame axis in the target */ + int target_naxes; /* Number of target axes */ + +/* Initialise the returned values. */ + *template_axes = NULL; + *target_axes = NULL; + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the template TimeFrame structure. */ + template = (AstTimeFrame *) template_frame; + +/* Obtain the number of axes in the target Frame. */ + target_naxes = astGetNaxes( target ); + +/* The first criterion for a match is that the template matches as a + Frame class object. This ensures that the number of axes (1) and + domain, etc. of the target Frame are suitable. Invoke the parent + "astMatch" method to verify this. */ + match = (*parent_match)( template_frame, target, matchsub, + template_axes, target_axes, map, result, status ); + +/* If a match was found, annul the returned objects, which are not + needed, but keep the memory allocated for the axis association + arrays, which we will re-use. */ + if ( astOK && match ) { + *map = astAnnul( *map ); + *result = astAnnul( *result ); + } + +/* If OK so far, obtain pointers to the primary Frames which underlie + all target axes. Stop when a TimeFrame axis is found. */ + if ( match && astOK ) { + match = 0; + for( iaxis = 0; iaxis < target_naxes; iaxis++ ) { + astPrimaryFrame( target, iaxis, &frame0, &iaxis0 ); + if( astIsATimeFrame( frame0 ) ) { + frame0 = astAnnul( frame0 ); + target_axis0 = iaxis; + match = 1; + break; + } else { + frame0 = astAnnul( frame0 ); + } + } + } + +/* Check at least one TimeFrame axis was found it the target. Store the + axis associataions. */ + if( match && astOK ) { + (*template_axes)[ 0 ] = 0; + (*target_axes)[ 0 ] = target_axis0; + +/* Use the target's "astSubFrame" method to create a new Frame (the + result Frame) with copies of the target axes in the required + order. This process also overlays the template attributes on to the + target Frame and returns a Mapping between the target and result + Frames which effects the required coordinate conversion. */ + match = astSubFrame( target, template, 1, *target_axes, *template_axes, + map, result ); + } + +/* If an error occurred, or conversion to the result Frame's coordinate + system was not possible, then free all memory, annul the returned + objects, and reset the returned value. */ + if ( !astOK || !match ) { + *template_axes = astFree( *template_axes ); + *target_axes = astFree( *target_axes ); + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; +} + +static void OriginScale( AstTimeFrame *this, AstTimeScaleType newts, + const char *method, int *status ){ +/* +* Name: +* OriginScale + +* Purpose: +* Convert the TimeOrigin in a TimeFrame to a new timescale. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void OriginScale( AstTimeFrame *this, AstTimeScaleType newts, +* const char *method, int *status ) + +* Class Membership: +* TimeFrame member function + +* Description: +* This function converts the value of the TimeOrigin attribute stored +* within a supplied TimeFrame from the timescale currently associated +* with the TimeFrame, to the new timescale indicated by "newts". + +* Parameters: +* this +* Point to the TimeFrame. On entry, the TimeOrigin value is +* assumed to refer to the timescale given by the astGetTimeScale +* method. On exit, the TimeOrigin value refers to the timescale +* supplied in "newts". The TimeScale attribute of the TimeFrame +* should then be modified in order to keep things consistent. +* newts +* The timescale to which the TimeOrigin value stored within "this" +* should refer on exit. +* method +* Pointer to a string holding the name of the method to be +* included in any error messages. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Local Variables: */ + AstMapping *map; + AstSystemType sys; + AstTimeScaleType oldts; + const char *u; + double newval; + double oldval; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do nothing if the TimeOrigin attribute has not been assigned a value. */ + if( astTestTimeOrigin( this ) ) { + +/* Do nothing if the Scale will not change. */ + oldts = astGetTimeScale( this ); + if( newts != oldts ) { + +/* Create a Mapping to perform the TimeScale change. */ + sys = astGetSystem( this ); + u = DefUnit( sys, method, "TimeFrame", status ), + map = MakeMap( this, sys, sys, oldts, newts, 0.0, 0.0, u, u, + method, status ); + +/* Use the Mapping to convert the stored TimeOrigin value. */ + if( map ) { + oldval = astGetTimeOrigin( this ); + astTran1( map, 1, &oldval, 1, &newval ); + +/* Store the new value */ + astSetTimeOrigin( this, newval ); + +/* Free resources */ + map = astAnnul( map ); + + } else if( astOK ) { + astError( AST__INCTS, "%s(%s): Cannot convert the TimeOrigin " + "value to a different timescale because of " + "incompatible time scales.", status, method, + astGetClass( this ) ); + } + } + } +} + +static void OriginSystem( AstTimeFrame *this, AstSystemType oldsys, + const char *method, int *status ){ +/* +* Name: +* OriginSystem + +* Purpose: +* Convert the TimeOrigin in a TimeFrame to a new System. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void OriginSystem( AstTimeFrame *this, AstSystemType oldsys, +* const char *method, int *status ) + +* Class Membership: +* TimeFrame member function + +* Description: +* This function converts the value of the TimeOrigin attribute stored +* within a supplied TimeFrame from its original System, etc, to the +* System, etc, currently associated with the TimeFrame. + +* Parameters: +* this +* Point to the TimeFrame. On entry, the TimeOrigin value is +* assumed to refer to the System given by "oldsys", etc. On exit, the +* TimeOrigin value refers to the System returned by the astGetSystem +* method, etc. +* oldsys +* The System to which the TimeOrigin value stored within "this" +* refers on entry. +* method +* A string containing the method name for error messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapping *map; + AstSystemType newsys; + AstTimeScaleType ts; + const char *oldu; + const char *newu; + double newval; + double oldval; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Do nothing if the TimeOrigin attribute has not been assigned a value. */ + if( astTestTimeOrigin( this ) ) { + +/* Do nothing if the System has not changed. */ + newsys = astGetSystem( this ); + if( oldsys != newsys ) { + +/* Create a Mapping to perform the System change. */ + ts = astGetTimeScale( this ); + oldu = DefUnit( oldsys, method, "TimeFrame", status ), + newu = DefUnit( newsys, method, "TimeFrame", status ), + map = MakeMap( this, oldsys, newsys, ts, ts, 0.0, 0.0, oldu, newu, + method, status ); + +/* Use the Mapping to convert the stored TimeOrigin value. */ + if( map ) { + oldval = astGetTimeOrigin( this ); + astTran1( map, 1, &oldval, 1, &newval ); + +/* Store the new value */ + astSetTimeOrigin( this, newval ); + +/* Free resources */ + map = astAnnul( map ); + + } else if( astOK ) { + astError( AST__INCTS, "%s(%s): Cannot convert the TimeOrigin " + "value to a different System because of incompatible " + "time scales.", status, method, astGetClass( this ) ); + } + } + } +} + +static void Overlay( AstFrame *template, const int *template_axes, + AstFrame *result, int *status ) { +/* +* Name: +* Overlay + +* Purpose: +* Overlay the attributes of a template TimeFrame on to another Frame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void Overlay( AstFrame *template, const int *template_axes, +* AstFrame *result, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the protected astOverlay method +* inherited from the Frame class). + +* Description: +* This function overlays attributes of a TimeFrame (the "template") on to +* another Frame, so as to over-ride selected attributes of that second +* Frame. Normally only those attributes which have been specifically set +* in the template will be transferred. This implements a form of +* defaulting, in which a Frame acquires attributes from the template, but +* retains its original attributes (as the default) if new values have not +* previously been explicitly set in the template. +* +* Note that if the result Frame is a TimeFrame and a change of time +* coordinate system occurs as a result of overlaying its System +* attribute, then some of its original attribute values may no +* longer be appropriate (e.g. the Title, or attributes describing +* its axes). In this case, these will be cleared before overlaying +* any new values. + +* Parameters: +* template +* Pointer to the template TimeFrame, for which values should have been +* explicitly set for any attribute which is to be transferred. +* template_axes +* Pointer to an array of int, with one element for each axis of the +* "result" Frame (see below). For each axis in the result frame, the +* corresponding element of this array should contain the (zero-based) +* index of the template axis to which it corresponds. This array is used +* to establish from which template axis any axis-dependent attributes +* should be obtained. +* +* If any axis in the result Frame is not associated with a template +* axis, the corresponding element of this array should be set to -1. +* +* If a NULL pointer is supplied, the template and result axis +* indices are assumed to be identical. +* result +* Pointer to the Frame which is to receive the new attribute values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - In general, if the result Frame is not from the same class as the +* template TimeFrame, or from a class derived from it, then attributes may +* exist in the template TimeFrame which do not exist in the result Frame. +* In this case, these attributes will not be transferred. +*/ + + +/* Local Variables: */ + AstSystemType new_alignsystem;/* Code identifying new alignment coords */ + AstSystemType new_system; /* Code identifying new cordinates */ + AstSystemType old_system; /* Code identifying old coordinates */ + int resetSystem; /* Was the template System value cleared? */ + int timeframe; /* Result Frame is a TimeFrame? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the old and new systems. */ + old_system = astGetSystem( result ); + new_system = astGetSystem( template ); + +/* If the result Frame is a TimeFrame, we must test to see if overlaying its + System attribute will change the type of coordinate system it describes. + Determine the value of this attribute for the result and template + TimeFrames. */ + resetSystem = 0; + timeframe = astIsATimeFrame( result ); + if( timeframe ) { + +/* If the coordinate system will change, any value already set for the result + TimeFrame's Title, etc, will no longer be appropriate, so clear it. */ + if ( new_system != old_system ) { + astClearTitle( result ); + astClearLabel( result, 0 ); + astClearSymbol( result, 0 ); + } + +/* If the result Frame is not a TimeFrame, we must temporarily clear the + System and AlignSystem values since the values used by this class are only + appropriate to this class. */ + } else { + if( astTestSystem( template ) ) { + astClearSystem( template ); + + new_alignsystem = astGetAlignSystem( template ); + astClearAlignSystem( template ); + + resetSystem = 1; + } + } + +/* Invoke the parent class astOverlay method to transfer attributes inherited + from the parent class. */ + (*parent_overlay)( template, template_axes, result, status ); + +/* Reset the System and AlignSystem values if necessary */ + if( resetSystem ) { + astSetSystem( template, new_system ); + astSetAlignSystem( template, new_alignsystem ); + } + +/* Check if the result Frame is a TimeFrame or from a class derived from + TimeFrame. If not, we cannot transfer TimeFrame attributes to it as it is + insufficiently specialised. In this case simply omit these attributes. */ + if ( timeframe && astOK ) { + +/* Define macros that test whether an attribute is set in the template and, + if so, transfers its value to the result. */ +#define OVERLAY(attribute) \ + if ( astTest##attribute( template ) ) { \ + astSet##attribute( result, astGet##attribute( template ) ); \ + } + +/* Use the macro to transfer each TimeFrame attribute in turn. Note, + SourceVRF must be overlayed before SourceVel. Otherwise the stored value + for SourceVel would be changed from the default SourceVRF to the specified + SourceVRF when SourceVRF was overlayed. */ + OVERLAY(AlignTimeScale) + OVERLAY(LTOffset) + OVERLAY(TimeOrigin) + OVERLAY(TimeScale) + } + +/* Undefine macros local to this function. */ +#undef OVERLAY +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void SetAttrib( AstObject *this, const char *setting, int *status ) + +* Class Membership: +* TimeFrame member function (extends the astSetAttrib method inherited from +* the Mapping class). + +* Description: +* This function assigns an attribute value for a TimeFrame, the attribute +* and its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the TimeFrame. +* setting +* Pointer to a null terminated string specifying the new attribute +* value. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This protected method is intended to be invoked by the Object astSet +* method and makes additional attributes accessible to it. +*/ + +/* Local Vaiables: */ + AstTimeFrame *this; /* Pointer to the TimeFrame structure */ + AstTimeScaleType ts; /* time scale type code */ + char *a; /* Pointer to next character */ + char *new_setting; /* Pointer value to new attribute setting */ + double dval; /* Double atribute value */ + double mjd; /* MJD read from setting */ + double origin; /* TimeOrigin value */ + int len; /* Length of setting string */ + int namelen; /* Length of attribute name in setting */ + int nc; /* Number of characters read by astSscanf */ + int off; /* Offset of attribute value */ + int rep; /* Original error reporting state */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_object; + +/* Obtain the length of the setting string. */ + len = strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse the + setting string and extract the attribute value (or an offset to it in the + case of string values). In each case, use the value set in "nc" to check + that the entire string was matched. Once a value has been obtained, use the + appropriate method to set it. */ + +/* First look for axis attributes defined by the Frame class. Since a + TimeFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strncmp( setting, "direction=", 10 ) || + !strncmp( setting, "bottom=", 7 ) || + !strncmp( setting, "top=", 4 ) || + !strncmp( setting, "format=", 7 ) || + !strncmp( setting, "label=", 6 ) || + !strncmp( setting, "symbol=", 7 ) || + !strncmp( setting, "unit=", 5 ) ) { + +/* Create a new setting string from the original by appending the string + "(1)" to the end of the attribute name and then use the parent SetAttrib + method. */ + new_setting = astMalloc( len + 4 ); + if( new_setting ) { + memcpy( new_setting, setting, len + 1 ); + a = strchr( new_setting, '=' ); + namelen = a - new_setting; + memcpy( a, "(1)", 4 ); + a += 3; + strcpy( a, setting + namelen ); + (*parent_setattrib)( this_object, new_setting, status ); + new_setting = astFree( new_setting ); + } + +/* AlignTimeScale. */ +/* --------------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "aligntimescale=%n%*s %n", &off, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a TimeScale code before use. */ + ts = TimeScaleCode( setting + off, status ); + if ( ts != AST__BADTS ) { + astSetAlignTimeScale( this, ts ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid time scale " + "description \"%s\".", status, astGetClass( this ), setting+off ); + } + +/* ClockLat. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "clocklat=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + new_setting = astMalloc( sizeof( char )*(size_t) len + 1 ); + new_setting[ 0 ] = 'o'; + new_setting[ 1 ] = 'b'; + new_setting[ 2 ] = 's'; + strcpy( new_setting + 3, setting + 5 ); + astSetAttrib( this, new_setting ); + new_setting = astFree( new_setting ); + +/* ClockLon. */ +/* ------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "clocklon=%n%*s %n", &off, &nc ) ) + && ( nc >= 7 ) ) { + new_setting = astMalloc( sizeof( char )*(size_t) len + 1 ); + new_setting[ 0 ] = 'o'; + new_setting[ 1 ] = 'b'; + new_setting[ 2 ] = 's'; + strcpy( new_setting + 3, setting + 5 ); + astSetAttrib( this, new_setting ); + new_setting = astFree( new_setting ); + +/* LTOffset */ +/* -------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "ltoffset= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + astSetLTOffset( this, dval ); + +/* TimeOrigin */ +/* ---------- */ + +/* Floating-point without any units indication - assume the current Unit + value. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "timeorigin= %lg %n", &dval, &nc ) ) + && ( nc >= len ) ) { + + astSetTimeOrigin( this, ToUnits( this, astGetUnit( this, 0 ), dval, + "astSetTimeOrigin", status ) ); + +/* Floating-point with units. */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "timeorigin= %lg %n%*s %n", &dval, &off, &nc ) ) + && ( nc >= len ) ) { + +/* Defer error reporting in case a date string was given which starts + with a floating point number, then convert the supplied value to the + default units for the TimeFrame's System. */ + rep = astReporting( 0 ); + origin = ToUnits( this, setting + off, dval, "astSetTimeOrigin", status ); + if( !astOK ) astClearStatus; + astReporting( rep ); + +/* If the origin was converted, store it. */ + if( origin != AST__BAD ) { + astSetTimeOrigin( this, origin ); + +/* Otherwise, interpret the string as a date. Convert first to MJD then to + default system. */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "timeorigin=%n%*[^\n]%n", &off, &nc ) ) + && ( nc >= len ) ) { + mjd = astReadDateTime( setting + off ); + if ( astOK ) { + astSetTimeOrigin( this, FromMJD( this, mjd, status ) ); + +/* Report contextual information if the conversion failed. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid TimeOrigin value " + "\"%s\" given.", status, astGetClass( this ), setting + off ); + } + } + +/* String (assumed to be a date). Convert first to MJD then to default + system. */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "timeorigin=%n%*[^\n]%n", &off, &nc ) ) + && ( nc >= len ) ) { + mjd = astReadDateTime( setting + off ); + if ( astOK ) { + astSetTimeOrigin( this, FromMJD( this, mjd, status ) ); + +/* Report contextual information if the conversion failed. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid TimeOrigin value " + "\"%s\" given.", status, astGetClass( this ), setting + off ); + } + +/* TimeScale. */ +/* ---------- */ + } else if ( nc = 0, + ( 0 == astSscanf( setting, "timescale=%n%*s %n", &off, &nc ) ) + && ( nc >= len ) ) { + +/* Convert the string to a TimeScale code before use. */ + ts = TimeScaleCode( setting + off, status ); + if ( ts != AST__BADTS ) { + astSetTimeScale( this, ts ); + +/* Report an error if the string value wasn't recognised. */ + } else { + astError( AST__ATTIN, "astSetAttrib(%s): Invalid time scale " + "description \"%s\".", status, astGetClass( this ), setting + off ); + } + +/* Pass any unrecognised setting to the parent method for further + interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } +} + +static void SetSystem( AstFrame *this_frame, AstSystemType newsys, int *status ) { +/* +* Name: +* SetSystem + +* Purpose: +* Set the System attribute for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void SetSystem( AstFrame *this_frame, AstSystemType newsys, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astSetSystem protected +* method inherited from the Frame class). + +* Description: +* This function sets the System attribute for a TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* newsys +* The new System value to be stored. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to TimeFrame structure */ + AstSystemType oldsys; /* Original System value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* If we are changing the System to BEPOCH, set the Unit attribute to + "yr" and TimeScale to "TT". */ + if( newsys == AST__BEPOCH ) { + astSetUnit( this_frame, 0, "yr" ); + astSetTimeScale( (AstTimeFrame *) this_frame, AST__TT ); + } + +/* Save the original System value */ + oldsys = astGetSystem( this_frame ); + +/* Use the parent SetSystem method to store the new System value. */ + (*parent_setsystem)( this_frame, newsys, status ); + +/* If the system has changed... */ + if( oldsys != newsys ) { + +/* Modify the stored TimeOrigin. */ + OriginSystem( this, oldsys, "astSetSystem", status ); + +/* Clear all attributes which have system-specific defaults. */ + astClearLabel( this_frame, 0 ); + astClearSymbol( this_frame, 0 ); + astClearTitle( this_frame ); + } +} + +static void SetTimeScale( AstTimeFrame *this, AstTimeScaleType value, int *status ) { +/* +*+ +* Name: +* astSetTimeScale + +* Purpose: +* Set the TimeScale attribute for a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* void astSetTimeScale( AstTimeFrame *this, AstTimeScaleType value ) + +* Class Membership: +* TimeFrame virtual function + +* Description: +* This function set a new value for the TimeScale attribute for a +* TimeFrame. + +* Parameters: +* this +* Pointer to the TimeFrame. +* value +* The new value. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Verify the supplied timescale value */ + if( value < FIRST_TS || value > LAST_TS ) { + astError( AST__ATTIN, "%s(%s): Bad value (%d) given for TimeScale " + "attribute.", status, "astSetTimeScale", astGetClass( this ), + (int) value ); + +/* Report an error if System is set to BEPOCH and an in appropriate + TimeScale was supplied. */ + } else if( astGetSystem( this ) == AST__BEPOCH && + value != AST__TT ) { + astError( AST__ATTIN, "%s(%s): Supplied TimeScale (%s) cannot be " + "used because the %s represents Besselian Epoch which " + "is defined in terms of TT.", status, "astSetTimeScale", + astGetClass( this ), TimeScaleString( value, status ), + astGetClass( this ) ); + +/* Otherwise set the new TimeScale */ + } else { + +/* Modify the TimeOrigin value stored in the TimeFrame structure to refer + to the new timescale. */ + OriginScale( this, value, "astSetTimeScale", status ); + +/* Store the new value for the timescale in the TimeFrame structure. */ + this->timescale = value; + + } +} + +static void SetUnit( AstFrame *this_frame, int axis, const char *value, int *status ) { +/* +* Name: +* SetUnit + +* Purpose: +* Set a pointer to the Unit string for a TimeFrame's axis. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void SetUnit( AstFrame *this_frame, int axis, const char *value ) + +* Class Membership: +* TimeFrame member function (over-rides the astSetUnit method inherited +* from the Frame class). + +* Description: +* This function stores a pointer to the Unit string for a specified axis +* of a TimeFrame. It also stores the string in the "usedunits" array +* in the TimeFrame structure, in the element associated with the +* current System. + +* Parameters: +* this +* Pointer to the TimeFrame. +* axis +* The number of the axis (zero-based) for which information is required. +* unit +* The new string to store. +*/ + +/* Local Variables: */ + AstTimeFrame *this; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Validate the axis index. */ + astValidateAxis( this, axis, 1, "astSetUnit" ); + +/* Report an error if System is set to BEPOCH and an in appropriate + Unit was supplied. */ + if( astGetSystem( this ) == AST__BEPOCH && strcmp( "yr", value ) ) { + astError( AST__ATTIN, "astSetUnit(%s): Supplied Unit (%s) cannot " + "be used because the %s represents Besselian Epoch which " + "is defined in units of years (yr).", status, astGetClass( this ), + value, astGetClass( this ) ); + +/* Otherwise use the parent SetUnit method to store the value in the Axis + structure */ + } else { + (*parent_setunit)( this_frame, axis, value, status ); + } +} + +static AstTimeScaleType TimeScaleCode( const char *ts, int *status ) { +/* +* Name: +* TimeScaleCode + +* Purpose: +* Convert a string into a time scale type code. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* AstTimeScaleType TimeScaleCode( const char *ts ) + +* Class Membership: +* TimeFrame member function. + +* Description: +* This function converts a string used for the external description of +* a time scale into a TimeFrame time scale type code (TimeScale attribute +* value). It is the inverse of the TimeScaleString function. + +* Parameters: +* ts +* Pointer to a constant null-terminated string containing the +* external description of the time scale. + +* Returned Value: +* The TimeScale type code. + +* Notes: +* - A value of AST__BADTS is returned if the time scale +* description was not recognised. This does not produce an error. +* - A value of AST__BADTS is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstTimeScaleType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADTS; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the timescale string against each possibility and assign the + result. */ + if ( astChrMatch( "TAI", ts ) ) { + result = AST__TAI; + + } else if ( astChrMatch( "UTC", ts ) ) { + result = AST__UTC; + + } else if ( astChrMatch( "UT1", ts ) ) { + result = AST__UT1; + + } else if ( astChrMatch( "GMST", ts ) ) { + result = AST__GMST; + + } else if ( astChrMatch( "LAST", ts ) ) { + result = AST__LAST; + + } else if ( astChrMatch( "LMST", ts ) ) { + result = AST__LMST; + + } else if ( astChrMatch( "TT", ts ) ) { + result = AST__TT; + + } else if ( astChrMatch( "TDB", ts ) ) { + result = AST__TDB; + + } else if ( astChrMatch( "TCG", ts ) ) { + result = AST__TCG; + + } else if ( astChrMatch( "TCB", ts ) ) { + result = AST__TCB; + + } else if ( astChrMatch( "LT", ts ) ) { + result = AST__LT; + + } + +/* Return the result. */ + return result; +} + +static const char *TimeScaleString( AstTimeScaleType ts, int *status ) { +/* +* Name: +* TimeScaleString + +* Purpose: +* Convert a time scale type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *TimeScaleString( AstTimeScaleType ts, int *status ) + +* Class Membership: +* TimeFrame member function. + +* Description: +* This function converts a TimeFrame time scale type code (TimeScale +* attribute value) into a string suitable for use as an external +* representation of the time scale type. + +* Parameters: +* ts +* The time scale type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the time scale +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the timescale value against each possibility and convert to a + string pointer. */ + switch ( ts ) { + + case AST__TAI: + result = "TAI"; + break; + + case AST__UTC: + result = "UTC"; + break; + + case AST__UT1: + result = "UT1"; + break; + + case AST__GMST: + result = "GMST"; + break; + + case AST__LAST: + result = "LAST"; + break; + + case AST__LMST: + result = "LMST"; + break; + + case AST__TT: + result = "TT"; + break; + + case AST__TDB: + result = "TDB"; + break; + + case AST__TCB: + result = "TCB"; + break; + + case AST__TCG: + result = "TCG"; + break; + + case AST__LT: + result = "LT"; + break; + + } + +/* Return the result pointer. */ + return result; +} + +static int SubFrame( AstFrame *target_frame, AstFrame *template, + int result_naxes, const int *target_axes, + const int *template_axes, AstMapping **map, + AstFrame **result, int *status ) { +/* +* Name: +* SubFrame + +* Purpose: +* Select axes from a TimeFrame and convert to the new coordinate +* system. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int SubFrame( AstFrame *target, AstFrame *template, +* int result_naxes, const int *target_axes, +* const int *template_axes, AstMapping **map, +* AstFrame **result, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the protected astSubFrame +* method inherited from the Frame class). + +* Description: +* This function selects a requested sub-set (or super-set) of the axes +* from a "target" TimeFrame and creates a new Frame with copies of +* the selected axes assembled in the requested order. It then +* optionally overlays the attributes of a "template" Frame on to the +* result. It returns both the resulting Frame and a Mapping that +* describes how to convert between the coordinate systems described by +* the target and result Frames. If necessary, this Mapping takes +* account of any differences in the Frames' attributes due to the +* influence of the template. + +* Parameters: +* target +* Pointer to the target TimeFrame, from which axes are to be +* selected. +* template +* Pointer to the template Frame, from which new attributes for the +* result Frame are to be obtained. Optionally, this may be NULL, in +* which case no overlaying of template attributes will be performed. +* result_naxes +* Number of axes to be selected from the target Frame. This number may +* be greater than or less than the number of axes in this Frame (or +* equal). +* target_axes +* Pointer to an array of int with result_naxes elements, giving a list +* of the (zero-based) axis indices of the axes to be selected from the +* target TimeFrame. The order in which these are given determines +* the order in which the axes appear in the result Frame. If any of the +* values in this array is set to -1, the corresponding result axis will +* not be derived from the target Frame, but will be assigned default +* attributes instead. +* template_axes +* Pointer to an array of int with result_naxes elements. This should +* contain a list of the template axes (given as zero-based axis indices) +* with which the axes of the result Frame are to be associated. This +* array determines which axes are used when overlaying axis-dependent +* attributes of the template on to the result. If any element of this +* array is set to -1, the corresponding result axis will not receive any +* template attributes. +* +* If the template argument is given as NULL, this array is not used and +* a NULL pointer may also be supplied here. +* map +* Address of a location to receive a pointer to the returned Mapping. +* The forward transformation of this Mapping will describe how to +* convert coordinates from the coordinate system described by the target +* TimeFrame to that described by the result Frame. The inverse +* transformation will convert in the opposite direction. +* result +* Address of a location to receive a pointer to the result Frame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value is returned if coordinate conversion is possible +* between the target and the result Frame. Otherwise zero is returned and +* *map and *result are returned as NULL (but this will not in itself +* result in an error condition). In general, coordinate conversion should +* always be possible if no template Frame is supplied but may not always +* be possible otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. + +* Implementation Notes: +* - This implementation addresses the selection of axes from a +* TimeFrame object. This results in another object of the same class +* only if the single TimeFrame axis is selected exactly once. +* Otherwise, the result is a Frame class object which inherits the +* TimeFrame's axis information (if appropriate) but none of the other +* properties of a TimeFrame. +* - In the event that a TimeFrame results, the returned Mapping will +* take proper account of the relationship between the target and result +* coordinate systems. +* - In the event that a Frame class object results, the returned Mapping +* will only represent a selection/permutation of axes. + +* Implementation Deficiencies: +* - Any axis selection is currently permitted. Probably this should be +* restricted so that each axis can only be selected once. The +* astValidateAxisSelection method will do this but currently there are bugs +* in the CmpFrame class that cause axis selections which will not pass this +* test. Install the validation when these are fixed. +*/ + +/* Local Variables: */ + AstTimeFrame *target; /* Pointer to the TimeFrame structure */ + AstTimeFrame *temp; /* Pointer to copy of target TimeFrame */ + AstTimeFrame *align_frm; /* Frame in which to align the TimeFrames */ + int match; /* Coordinate conversion is possible? */ + +/* Initialise the returned values. */ + *map = NULL; + *result = NULL; + match = 0; + +/* Check the global error status. */ + if ( !astOK ) return match; + +/* Obtain a pointer to the target TimeFrame structure. */ + target = (AstTimeFrame *) target_frame; + +/* Result is a TimeFrame. */ +/* -------------------------- */ +/* Check if the result Frame is to have one axis obtained by selecting + the single target TimeFrame axis. If so, the result will also be + a TimeFrame. */ + if ( ( result_naxes == 1 ) && ( target_axes[ 0 ] == 0 ) ) { + +/* Form the result from a copy of the target. */ + *result = astCopy( target ); + +/* If required, overlay the template attributes on to the result TimeFrame. + Also choose the Frame which defined the alignment system and time scale + (via its AlignSystem and AlignTimeScale attributes) in which to align the + two TimeFrames. This is the template (if there is a template). */ + if ( template ) { + astOverlay( template, template_axes, *result ); + if( astIsATimeFrame( template ) ) { + align_frm = astClone( template ); + } else { + align_frm = astClone( target ); + } + +/* If no template was supplied, align in the System and TimeScale of the + target. */ + } else { + VerifyAttrs( target, "convert between different time systems", + "TimeScale", "astMatch", status ); + align_frm = astClone( target ); + } + +/* Generate a Mapping that takes account of changes in the sky coordinate + system (equinox, epoch, etc.) between the target TimeFrame and the result + TimeFrame. If this Mapping can be generated, set "match" to indicate that + coordinate conversion is possible. */ + match = ( MakeTimeMapping( target, (AstTimeFrame *) *result, + align_frm, 0, map, status ) != 0 ); + +/* Free resources. */ + align_frm = astAnnul( align_frm ); + +/* Result is not a TimeFrame. */ +/* ------------------------------ */ +/* In this case, we select axes as if the target were from the Frame + class. However, since the resulting data will then be separated + from their enclosing TimeFrame, default attribute values may differ + if the methods for obtaining them were over-ridden by the TimeFrame + class. To overcome this, we ensure that these values are explicitly + set for the result Frame (rather than relying on their defaults). */ + } else { + +/* Make a temporary copy of the target TimeFrame. We will explicitly + set the attribute values in this copy so as not to modify the original. */ + temp = astCopy( target ); + +/* Define a macro to test if an attribute is set. If not, set it + explicitly to its default value. */ +#define SET(attribute) \ + if ( !astTest##attribute( temp ) ) { \ + astSet##attribute( temp, astGet##attribute( temp ) ); \ + } + +/* Set attribute values which apply to the Frame as a whole and which + we want to retain, but whose defaults are over-ridden by the + TimeFrame class. */ + SET(Domain) + SET(Title) + +/* Define a macro to test if an attribute is set for axis zero (the only + axis of a TimeFrame). If not, set it explicitly to its default value. */ +#define SET_AXIS(attribute) \ + if ( !astTest##attribute( temp, 0 ) ) { \ + astSet##attribute( temp, 0, \ + astGet##attribute( temp, 0 ) ); \ + } + +/* Use this macro to set explicit values for all the axis attributes + for which the TimeFrame class over-rides the default value. */ + SET_AXIS(Label) + SET_AXIS(Symbol) + SET_AXIS(Unit) + +/* Clear attributes which have an extended range of values allowed by + this class. */ + astClearSystem( temp ); + astClearAlignSystem( temp ); + +/* Invoke the astSubFrame method inherited from the Frame class to + produce the result Frame by selecting the required set of axes and + overlaying the template Frame's attributes. */ + match = (*parent_subframe)( (AstFrame *) temp, template, + result_naxes, target_axes, template_axes, + map, result, status ); + +/* Delete the temporary copy of the target TimeFrame. */ + temp = astDelete( temp ); + } + +/* If an error occurred or no match was found, annul the returned + objects and reset the returned result. */ + if ( !astOK || !match ) { + if( *map ) *map = astAnnul( *map ); + if( *result ) *result = astAnnul( *result ); + match = 0; + } + +/* Return the result. */ + return match; + +/* Undefine macros local to this function. */ +#undef SET +#undef SET_AXIS +} + +static AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) { +/* +* Name: +* SystemCode + +* Purpose: +* Convert a string into a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* AstSystemType SystemCode( AstFrame *this, const char *system, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astSystemCode method +* inherited from the Frame class). + +* Description: +* This function converts a string used for the external description of +* a coordinate system into a TimeFrame coordinate system type code +* (System attribute value). It is the inverse of the astSystemString +* function. + +* Parameters: +* this +* The Frame. +* system +* Pointer to a constant null-terminated string containing the +* external description of the sky coordinate system. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The System type code. + +* Notes: +* - A value of AST__BADSYSTEM is returned if the sky coordinate +* system description was not recognised. This does not produce an +* error. +* - A value of AST__BADSYSTEM is also returned if this function +* is invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Result value to return */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" string against each possibility and assign the + result. */ + if ( astChrMatch( "MJD", system ) || astChrMatch( "Modified Julian Date", system ) ) { + result = AST__MJD; + + } else if ( astChrMatch( "JD", system ) || astChrMatch( "Julian Date", system ) ) { + result = AST__JD; + + } else if ( astChrMatch( "BEPOCH", system ) || astChrMatch( "Besselian Epoch", system ) ) { + result = AST__BEPOCH; + + } else if ( astChrMatch( "JEPOCH", system ) || astChrMatch( "Julian Epoch", system ) ) { + result = AST__JEPOCH; + + } + +/* Return the result. */ + return result; +} + +static const char *SystemLabel( AstSystemType system, int *status ) { +/* +* Name: +* SystemLabel + +* Purpose: +* Return a label for a coordinate system type code. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *SystemLabel( AstSystemType system, int *status ) + +* Class Membership: +* TimeFrame member function. + +* Description: +* This function converts a TimeFrame coordinate system type code +* (System attribute value) into a descriptive string for human readers. + +* Parameters: +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the sky coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. */ + switch ( system ) { + + case AST__MJD: + result = "Modified Julian Date"; + break; + + case AST__JD: + result = "Julian Date"; + break; + + case AST__JEPOCH: + result = "Julian Epoch"; + break; + + case AST__BEPOCH: + result = "Besselian Epoch"; + break; + + } + +/* Return the result pointer. */ + return result; +} + +static const char *SystemString( AstFrame *this, AstSystemType system, int *status ) { +/* +* Name: +* SystemString + +* Purpose: +* Convert a coordinate system type code into a string. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* const char *SystemString( AstFrame *this, AstSystemType system, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astSystemString method +* inherited from the Frame class). + +* Description: +* This function converts a TimeFrame coordinate system type code +* (System attribute value) into a string suitable for use as an +* external representation of the coordinate system type. + +* Parameters: +* this +* The Frame. +* system +* The coordinate system type code. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string containing the +* textual equivalent of the type code supplied. + +* Notes: +* - A NULL pointer value is returned if the sky coordinate system +* code was not recognised. This does not produce an error. +* - A NULL pointer value is also returned if this function is +* invoked with the global error status set or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Match the "system" value against each possibility and convert to a + string pointer. (Where possible, return the same string as would be + used in the FITS WCS representation of the coordinate system). */ + switch ( system ) { + + case AST__MJD: + result = "MJD"; + break; + + case AST__JD: + result = "JD"; + break; + + case AST__JEPOCH: + result = "JEPOCH"; + break; + + case AST__BEPOCH: + result = "BEPOCH"; + break; + } + +/* Return the result pointer. */ + return result; +} + +static int TestActiveUnit( AstFrame *this_frame, int *status ) { +/* +* Name: +* TestActiveUnit + +* Purpose: +* Test the ActiveUnit flag for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int TestActiveUnit( AstFrame *this_frame, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astTestActiveUnit protected +* method inherited from the Frame class). + +* Description: +* This function test the value of the ActiveUnit flag for a TimeFrame, +* which is always "unset". + +* Parameters: +* this +* Pointer to the TimeFrame. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The result of the test (0). + +*/ + return 0; +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astTestAttrib protected +* method inherited from the Frame class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a TimeFrame's attributes. + +* Parameters: +* this +* Pointer to the TimeFrame. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - This function uses one-based axis numbering so that it is +* suitable for external (public) use. +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to the TimeFrame structure */ + char *new_attrib; /* Pointer value to new attribute name */ + int len; /* Length of attrib string */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + +/* First look for axis attributes defined by the Frame class. Since a + TimeFrame has only 1 axis, we allow these attributes to be specified + without a trailing "(axis)" string. */ + if ( !strcmp( attrib, "direction" ) || + !strcmp( attrib, "bottom" ) || + !strcmp( attrib, "top" ) || + !strcmp( attrib, "format" ) || + !strcmp( attrib, "label" ) || + !strcmp( attrib, "symbol" ) || + !strcmp( attrib, "unit" ) ) { + +/* Create a new attribute name from the original by appending the string + "(1)" and then use the parent TestAttrib method. */ + new_attrib = astMalloc( len + 4 ); + if( new_attrib ) { + memcpy( new_attrib, attrib, len ); + memcpy( new_attrib + len, "(1)", 4 ); + result = (*parent_testattrib)( this_object, new_attrib, status ); + new_attrib = astFree( new_attrib ); + } + +/* AlignTimeScale. */ +/* --------------- */ + } else if ( !strcmp( attrib, "aligntimescale" ) ) { + result = astTestAlignTimeScale( this ); + +/* ClockLat. */ +/* ------- */ + } else if ( !strcmp( attrib, "clocklat" ) ) { + result = astTestAttrib( this, "obslat" ); + +/* ClockLon. */ +/* ------- */ + } else if ( !strcmp( attrib, "clocklon" ) ) { + result = astTestAttrib( this, "obslon" ); + +/* LTOffset. */ +/* --------- */ + } else if ( !strcmp( attrib, "ltoffset" ) ) { + result = astTestLTOffset( this ); + +/* TimeOrigin. */ +/* --------- */ + } else if ( !strcmp( attrib, "timeorigin" ) ) { + result = astTestTimeOrigin( this ); + +/* TimeScale. */ +/* ---------- */ + } else if ( !strcmp( attrib, "timescale" ) ) { + result = astTestTimeScale( this ); + +/* If the attribute is not recognised, pass it on to the parent method + for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static double ToMJD( AstSystemType oldsys, double oldval, int *status ){ +/* +* Name: +* ToMJD + +* Purpose: +* Convert a time value from TimeFrame's System to MJD. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double ToMJD( AstSystemType oldsys, double oldval, int *status ){ + +* Class Membership: +* TimeFrame member function + +* Description: +* This function converts the supplied value from the supplied System +* to an MJD. + +* Parameters: +* oldsys +* The System in which the oldval is supplied. +* oldval +* The value to convert, assumed to be in the default units +* associated with "oldsys". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The MJD value corresponding to "oldval" + +* Notes: +* - Both old and new value are assumed to be absolute (i.e. have zero +* offset). + +*/ + +/* Local Variables; */ + AstMapping *map; + double result; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the old system is MJD just return the value unchanged. */ + if( oldsys == AST__MJD ) { + result = oldval; + +/* Otherwise create a TimeMap wich converts from the TimeFrame system to + MJD, and use it to transform the supplied value. */ + } else { + map = ToMJDMap( oldsys, 0.0, status ); + +/* Use the TimeMap to convert the supplied value. */ + astTran1( map, 1, &oldval, 1, &result ); + +/* Free resources */ + map = astAnnul( map ); + + } + +/* Return the result */ + return result; +} + +static AstMapping *ToMJDMap( AstSystemType oldsys, double off, int *status ){ +/* +* Name: +* ToMJDMap + +* Purpose: +* Create a Mapping from a specified System to MJD. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* AstMapping *ToMJDMap( AstSystemType oldsys, double off, int *status ){ + +* Class Membership: +* TimeFrame member function + +* Description: +* This function creates a Mapping which converts from the supplied +* system and offset to absolute MJD. + +* Parameters: +* oldsys +* The System in which the oldval is supplied. +* off +* The axis offset used with the old System, assumed to be in the +* default system associated with oldsys. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Mapping. + +*/ + +/* Local Variables; */ + AstTimeMap *timemap; + double args[ 2 ]; + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Create a null TimeMap */ + timemap = astTimeMap( 0, "", status ); + +/* Set the offsets for the supplied and returned values. */ + args[ 0 ] = off; + args[ 1 ] = 0.0; + +/* If required, add a TimeMap conversion which converts from the TimeFrame + system to MJD. */ + if( oldsys == AST__MJD ) { +/* if( off != 0.0 ) astTimeAdd( timemap, "MJDTOMJD", args ); */ + astTimeAdd( timemap, "MJDTOMJD", args ); + + } else if( oldsys == AST__JD ) { + astTimeAdd( timemap, "JDTOMJD", args ); + + } else if( oldsys == AST__JEPOCH ) { + astTimeAdd( timemap, "JEPTOMJD", args ); + + } else if( oldsys == AST__BEPOCH ) { + astTimeAdd( timemap, "BEPTOMJD", args ); + } + +/* Return the result */ + return (AstMapping *) timemap; +} + +static double ToUnits( AstTimeFrame *this, const char *oldunit, double oldval, + const char *method, int *status ){ +/* +* +* Name: +* ToUnits + +* Purpose: +* Convert a supplied time value to the default units of the supplied TimeFrame. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* double ToUnits( AstTimeFrame *this, const char *oldunit, double oldval, +* const char *method, int *status ) + +* Class Membership: +* TimeFrame member function + +* Description: +* This function converts the supplied time value from the supplied +* units to the default units associated with the supplied TimeFrame's +* System. + +* Parameters: +* this +* Pointer to the TimeFrame. +* oldunit +* The units in which "oldval" is supplied. +* oldval +* The value to be converted. +* method +* Pointer to a string holding the name of the method to be +* included in any error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The converted value. + +*/ + +/* Local Variables: */ + AstMapping *map; + const char *defunit; + double result; + +/* Initialise. */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get default units associated with the System attribute of the supplied + TimeFrame, and find a Mapping from the old units to the default. */ + defunit = DefUnit( astGetSystem( this ), method, "TimeFrame", status ); + map = astUnitMapper( oldunit, defunit, NULL, NULL ); + if( map ) { + +/* Use the Mapping to convert the supplied value. */ + astTran1( map, 1, &oldval, 1, &result ); + +/* Free resources. */ + map = astAnnul( map ); + +/* Report an error if no conversion is possible. */ + } else if( astOK ){ + astError( AST__BADUN, "%s(%s): Cannot convert the supplied attribute " + "value from units of %s to %s.", status, method, astGetClass( this ), + oldunit, defunit ); + } + +/* Return the result */ + return result; +} + +static int Unformat( AstFrame *this_frame, int axis, const char *string, + double *value, int *status ) { +/* +* Name: +* Unformat + +* Purpose: +* Read a formatted coordinate value for a TimeFrame axis. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* int Unformat( AstFrame *this, int axis, const char *string, +* double *value, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the public astUnformat +* method inherited from the Frame class). + +* Description: +* This function reads a formatted coordinate value for a TimeFrame +* axis (supplied as a string) and returns the equivalent numerical +* value as a double. It also returns the number of characters read +* from the string. + +* Parameters: +* this +* Pointer to the TimeFrame. +* axis +* The number of the TimeFrame axis for which the coordinate +* value is to be read (axis numbering starts at zero for the +* first axis). +* string +* Pointer to a constant null-terminated string containing the +* formatted coordinate value. +* value +* Pointer to a double in which the coordinate value read will +* be returned. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of characters read from the string to obtain the +* coordinate value. + +* Notes: +* - Any white space at the beginning of the string will be +* skipped, as also will any trailing white space following the +* coordinate value read. The function's return value will reflect +* this. +* - A function value of zero (and no coordinate value) will be +* returned, without error, if the string supplied does not contain +* a suitably formatted value. +* - The string "" is recognised as a special case and will +* generate the value AST__BAD, without error. The test for this +* string is case-insensitive and permits embedded white space. +* - A function result of zero will be returned and no coordinate +* value will be returned via the "value" pointer if this function +* is invoked with the global error status set, or if it should +* fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *map; + AstTimeFrame *this; + AstTimeScaleType ts1; + AstTimeScaleType ts2; + const char *c; + char *old_fmt; + char *str; + const char *txt; + double mjd; + double val1; + int l; + int lt; + int nc1; + int nc; + int ndp; + int rep; + +/* Initialise. */ + nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return nc; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_frame; + +/* Validate the axis index. */ + (void) astValidateAxis( this, axis, 1, "astUnformat" ); + +/* First attempt to read the value using the parent unformat method, and + note how many characters were used. We temporarily clear the Format + attribute if it has been set to a date format, since the parent Frame + class does not understand date format.*/ + txt = astGetFormat( this, axis ); + if( DateFormat( txt, &ndp, NULL, status ) ) { + old_fmt = astStore( NULL, txt, strlen( txt ) + 1 ); + astClearFormat( this, axis ); + } else { + old_fmt = NULL; + } + + nc1 = (*parent_unformat)( this_frame, axis, string, &val1, status ); + +/* Re-instate the original Format */ + if( old_fmt ) { + astSetFormat( this,axis, old_fmt ); + old_fmt = astFree( old_fmt ); + } + +/* The astReadDateTime function (defined within frame.c) does not allow + for any extra text to be appended to the end of the formatted date/time + (AST__BAD is returned if any such extra text is present). But astUnformat + is contracted to allow such text. So we need to make multiple attempts + at reading the date/time in order to find the longest leading string + which gives a non-bad value. First take a copy of the supplied string + si we can terminate it at any point we wish. */ + l = astChrLen( string ); + str = astStore( NULL, string, l + 1 ); + +/* Now attempt to read an ISO date from the start of the string. We + switch off error reporting to avoid reports of unsuitable syntax. */ + rep = astReporting( 0 ); + +/* Attempt to read a date/time from the whol string. If this fails + terminate the string in order to make it one character shorter and try + again. */ + for( lt = l; lt > 0; lt-- ) { + str[ lt ] = 0; + mjd = astReadDateTime( str ); + if( !astOK ) astClearStatus; + if( mjd != AST__BAD ) break; + } + +/* Re-instate error reporting. */ + astReporting( rep ); + +/* Free resources. */ + str = astFree( str ); + +/* If the whole non-blank start of the string was consumed, add on any + trailing white space. */ + if( lt >= l ) lt = strlen( string ); + +/* If no date/time could be read, or if reading the value as a + floating point value was at least as good, return the floating point + value (assumed to be in the system and units of the TimeFrame. */ + if( mjd == AST__BAD || nc1 >= l ) { + *value = val1; + nc = nc1; + +/* Otherwise, if a date/time was read convert it to the TimeFrame system, + etc. */ + } else if( mjd != AST__BAD ) { + +/* Save the number of character read from the supplied string. */ + nc = lt; + +/* We require a value in the timescale of the supplied TimeFrame. Get + this TimeScale. */ + ts2 = astGetTimeScale( this ); + +/* If the supplied string gave the date/time as a Besselian epoch, the + input timescale is TT, otherwise it is assumed to be the TimeScale of + the TimeFrame. Locate the first non-space character. */ + c = string; + while( *c && isspace( *c ) ) c++; + +/* If the first non-space is a "B", assuming a TT timescale. Otherwise + assume the timescale of the supplied TimeFrame. */ + ts1 = ( *c == 'B' || *c == 'b' ) ? AST__TT : ts2; + +/* Create the Mapping and use it to transform the mjd value. */ + map = MakeMap( this, AST__MJD, astGetSystem( this ), ts1, ts2, + 0.0, astGetTimeOrigin( this ), "d", + astGetUnit( this, 0 ), "astFormat", status ); + if( map ) { + astTran1( map, 1, &mjd, 1, value ); + map = astAnnul( map ); + } else { + astError( AST__INCTS, "astUnformat(%s): Cannot convert the " + "supplied date/time string (%s) to the required " + "timescale (%s).", status, astGetClass( this ), string, + TimeScaleString( ts2, status ) ); + } + } + +/* Return the number of characters read. */ + return nc; +} + +static int ValidateSystem( AstFrame *this, AstSystemType system, const char *method, int *status ) { +/* +* +* Name: +* ValidateSystem + +* Purpose: +* Validate a value for a Frame's System attribute. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "timeframe.h" +* int ValidateSystem( AstFrame *this, AstSystemType system, +* const char *method, int *status ) + +* Class Membership: +* TimeFrame member function (over-rides the astValidateSystem method +* inherited from the Frame class). + +* Description: +* This function checks the validity of the supplied system value. +* If the value is valid, it is returned unchanged. Otherwise, an +* error is reported and a value of AST__BADSYSTEM is returned. + +* Parameters: +* this +* Pointer to the Frame. +* system +* The system value to be checked. +* method +* Pointer to a constant null-terminated character string +* containing the name of the method that invoked this function +* to validate an axis index. This method name is used solely +* for constructing error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The validated system value. + +* Notes: +* - A value of AST__BADSYSTEM will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstSystemType result; /* Validated system value */ + +/* Initialise. */ + result = AST__BADSYSTEM; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the value is out of bounds, report an error. */ + if ( system < FIRST_SYSTEM || system > LAST_SYSTEM ) { + astError( AST__AXIIN, "%s(%s): Bad value (%d) given for the System " + "or AlignSystem attribute of a %s.", status, method, + astGetClass( this ), (int) system, astGetClass( this ) ); + +/* Otherwise, return the supplied value. */ + } else { + result = system; + } + +/* Return the result. */ + return result; +} + +static void VerifyAttrs( AstTimeFrame *this, const char *purp, + const char *attrs, const char *method, int *status ) { +/* +* Name: +* VerifyAttrs + +* Purpose: +* Verify that usable attribute values are available. + +* Type: +* Private function. + +* Synopsis: +* #include "timeframe.h" +* void VerifyAttrs( AstTimeFrame *this, const char *purp, +* const char *attrs, const char *method, int *status ) + +* Class Membership: +* TimeFrame member function + +* Description: +* This function tests each attribute listed in "attrs". It returns +* without action if 1) an explicit value has been set for each attribute +* or 2) the UseDefs attribute of the supplied TimeFrame is non-zero. +* +* If UseDefs is zero (indicating that default values should not be +* used for attributes), and any of the named attributes does not have +* an explicitly set value, then an error is reported. + +* Parameters: +* this +* Pointer to the TimeFrame. +* purp +* Pointer to a text string containing a message which will be +* included in any error report. This shouldindicate the purpose +* for which the attribute value is required. +* attrs +* A string holding a space separated list of attribute names. +* method +* A string holding the name of the calling method for use in error +* messages. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + const char *a; + const char *desc; + const char *p; + int len; + int set; + int state; + +/* Check inherited status */ + if( !astOK ) return; + +/* Stop compiler warnings about uninitialised variables */ + a = NULL; + desc = NULL; + len = 0; + set = 0; + +/* If the TimeFrame has a non-zero value for its UseDefs attribute, then + all attributes are assumed to have usable values, since the defaults + will be used if no explicit value has been set. So we only need to do + any checks if UseDefs is zero. */ + if( !astGetUseDefs( this ) ) { + +/* Loop round the "attrs" string identifying the start and length of each + non-blank word in the string. */ + state = 0; + p = attrs; + while( 1 ) { + if( state == 0 ) { + if( !isspace( *p ) ) { + a = p; + len = 1; + state = 1; + } + } else { + if( isspace( *p ) || !*p ) { + +/* The end of a word has just been reached. Compare it to each known + attribute value. Get a flag indicating if the attribute has a set + value, and a string describing the attribute.*/ + if( len > 0 ) { + + if( !strncmp( "ObsLat", a, len ) ) { + set = astTestObsLat( this ); + desc = "observer latitude"; + + } else if( !strncmp( "ObsLon", a, len ) ) { + set = astTestObsLon( this ); + desc = "observer longitude"; + + } else if( !strncmp( "ObsAlt", a, len ) ) { + set = astTestObsAlt( this ); + desc = "observer altitude"; + + } else if( !strncmp( "Dut1", a, len ) ) { + set = astTestDut1( this ); + desc = "UT1-UTC correction"; + + } else if( !strncmp( "TimeOrigin", a, len ) ) { + set = astTestTimeOrigin( this ); + desc = "time offset"; + + } else if( !strncmp( "LTOffset", a, len ) ) { + set = astTestLTOffset( this ); + desc = "local time offset"; + + } else if( !strncmp( "TimeScale", a, len ) ) { + set = astTestTimeScale( this ); + desc = "time scale"; + + } else { + astError( AST__INTER, "VerifyAttrs(TimeFrame): " + "Unknown attribute name \"%.*s\" supplied (AST " + "internal programming error).", status, len, a ); + } + +/* If the attribute does not have a set value, report an error. */ + if( !set && astOK ) { + astError( AST__NOVAL, "%s(%s): Cannot %s.", status, method, + astGetClass( this ), purp ); + astError( AST__NOVAL, "No value has been set for " + "the AST \"%.*s\" attribute (%s).", status, len, a, + desc ); + } + +/* Continue the word search algorithm. */ + } + len = 0; + state = 0; + } else { + len++; + } + } + if( !*(p++) ) break; + } + } +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ + +/* +*att++ +* Name: +* TimeOrigin + +* Purpose: +* The zero point for TimeFrame axis values + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This specifies the origin from which all time values are measured. +* The default value (zero) results in the TimeFrame describing +* absolute time values in the system given by the System attribute +* (e.g. MJD, Julian epoch, etc). If a TimeFrame is to be used to +* describe elapsed time since some origin, the TimeOrigin attribute +* should be set to hold the required origin value. The TimeOrigin value +* stored inside the TimeFrame structure is modified whenever TimeFrame +* attribute values are changed so that it refers to the original moment +* in time. +* +* Input Formats: +* The formats accepted when setting a TimeOrigin value are listed +* below. They are all case-insensitive and are generally tolerant +* of extra white space and alternative field delimiters: +* +* - Besselian Epoch: Expressed in decimal years, with or without +* decimal places ("B1950" or "B1976.13" for example). +* +* - Julian Epoch: Expressed in decimal years, with or without +* decimal places ("J2000" or "J2100.9" for example). +* +* - Units: An unqualified decimal value is interpreted as a value in +* the system specified by the TimeFrame's System attribute, in the +* units given by the TimeFrame's Unit attribute. Alternatively, an +* appropriate unit string can be appended to the end of the floating +* point value ("123.4 d" for example), in which case the supplied value +* is scaled into the units specified by the Unit attribute. +* +* - Julian Date: With or without decimal places ("JD 2454321.9" for +* example). +* +* - Modified Julian Date: With or without decimal places +* ("MJD 54321.4" for example). +* +* - Gregorian Calendar Date: With the month expressed either as an +* integer or a 3-character abbreviation, and with optional decimal +* places to represent a fraction of a day ("1996-10-2" or +* "1996-Oct-2.6" for example). If no fractional part of a day is +* given, the time refers to the start of the day (zero hours). +* +* - Gregorian Date and Time: Any calendar date (as above) but with +* a fraction of a day expressed as hours, minutes and seconds +* ("1996-Oct-2 12:13:56.985" for example). The date and time can be +* separated by a space or by a "T" (as used by ISO8601 format). + +* Output Format: +* When enquiring TimeOrigin values, the returned formatted floating +* point value represents a value in the TimeFrame's System, in the unit +* specified by the TimeFrame's Unit attribute. + +* Applicability: +* TimeFrame +* All TimeFrames have this attribute. + +*att-- +*/ +/* The time origin, stored internally in the default units associated + with the current System value. Clear the TimeOrigin value by setting it + to AST__BAD, which gives 0.0 as the default value. Any value is acceptable. */ +astMAKE_CLEAR(TimeFrame,TimeOrigin,timeorigin,AST__BAD) +astMAKE_GET(TimeFrame,TimeOrigin,double,0.0,((this->timeorigin!=AST__BAD)?this->timeorigin:0.0)) +astMAKE_SET(TimeFrame,TimeOrigin,double,timeorigin,value) +astMAKE_TEST(TimeFrame,TimeOrigin,( this->timeorigin != AST__BAD )) + +/* +*att++ +* Name: +* LTOffset + +* Purpose: +* The offset from UTC to Local Time, in hours. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This specifies the offset from UTC to Local Time, in hours (fractional +* hours can be supplied). It is positive for time zones east of Greenwich. +* AST uses the figure as given, without making any attempt to correct for +* daylight saving. The default value is zero. + +* Applicability: +* TimeFrame +* All TimeFrames have this attribute. + +*att-- +*/ +astMAKE_CLEAR(TimeFrame,LTOffset,ltoffset,AST__BAD) +astMAKE_GET(TimeFrame,LTOffset,double,0.0,((this->ltoffset!=AST__BAD)?this->ltoffset:0.0)) +astMAKE_SET(TimeFrame,LTOffset,double,ltoffset,value) +astMAKE_TEST(TimeFrame,LTOffset,( this->ltoffset != AST__BAD )) + +/* +*att++ +* Name: +* AlignTimeScale + +* Purpose: +* Time scale to use when aligning TimeFrames. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute controls how a TimeFrame behaves when it is used (by +c astFindFrame or astConvert) as a template to match another (target) +f AST_FINDFRAME or AST_CONVERT) as a template to match another (target) +* TimeFrame. It identifies the time scale in which alignment is +* to occur. See the TimeScale attribute for a desription of the values +* which may be assigned to this attribute. The default AlignTimeScale +* value depends on the current value of TimeScale: if TimeScale is +* UT1, GMST, LMST or LAST, the default for AlignTimeScale is UT1, for all +* other TimeScales the default is TAI. +* +c When astFindFrame or astConvert is used on two TimeFrames (potentially +f When AST_FindFrame or AST_CONVERT is used on two TimeFrames (potentially +* describing different time coordinate systems), it returns a Mapping +* which can be used to transform a position in one TimeFrame into the +* corresponding position in the other. The Mapping is made up of the +* following steps in the indicated order: +* +* - Map values from the system used by the target (MJD, JD, etc) to the +* system specified by the AlignSystem attribute. +* +* - Map these values from the target's time scale to the time scale +* specified by the AlignTimeScale attribute. +* +* - Map these values from the time scale specified by the AlignTimeScale +* attribute, to the template's time scale. +* +* - Map these values from the system specified by the AlignSystem +* attribute, to the system used by the template. + +* Applicability: +* TimeFrame +* All TimeFrames have this attribute. + +*att-- +*/ +astMAKE_TEST(TimeFrame,AlignTimeScale,( this->aligntimescale != AST__BADTS )) +astMAKE_CLEAR(TimeFrame,AlignTimeScale,aligntimescale,AST__BADTS) +astMAKE_SET(TimeFrame,AlignTimeScale,AstTimeScaleType,aligntimescale,( + ( ( value >= FIRST_TS ) && ( value <= LAST_TS ) ) ? + value : + ( astError( AST__ATTIN, "%s(%s): Bad value (%d) " + "given for AlignTimeScale attribute.", status, + "astSetAlignTimeScale", astGetClass( this ), (int) value ), + +/* Leave the value unchanged on error. */ + this->aligntimescale ) ) ) + +/* +*att++ +* Name: +* TimeScale + +* Purpose: +* Time scale. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute identifies the time scale to which the time axis values +* of a TimeFrame refer, and may take any of the values listed in the +* "Time Scales" section (below). +* +* The default TimeScale value depends on the current System value; if +* the current TimeFrame system is "Besselian epoch" the default is +* "TT", otherwise it is "TAI". Note, if the System attribute is set +* so that the TimeFrame represents Besselian Epoch, then an error +* will be reported if an attempt is made to set the TimeScale to +* anything other than TT. +* +* Note, the supported time scales fall into two groups. The first group +* containing UT1, GMST, LAST and LMST define time in terms of the +* orientation of the earth. The second group (containing all the remaining +* time scales) define time in terms of an atomic process. Since the rate of +* rotation of the earth varies in an unpredictable way, conversion between +* two timescales in different groups relies on a value being supplied for +* the Dut1 attribute (defined by the parent Frame class). This attribute +* specifies the difference between the UT1 and UTC time scales, in seconds, +* and defaults to zero. See the documentation for the Dut1 attribute for +* further details. + +* Applicability: +* TimeFrame +* All TimeFrames have this attribute. + +* Time Scales: +* The TimeFrame class supports the following TimeScale values (all are +* case-insensitive): +* +* - "TAI" - International Atomic Time +* - "UTC" - Coordinated Universal Time +* - "UT1" - Universal Time +* - "GMST" - Greenwich Mean Sidereal Time +* - "LAST" - Local Apparent Sidereal Time +* - "LMST" - Local Mean Sidereal Time +* - "TT" - Terrestrial Time +* - "TDB" - Barycentric Dynamical Time +* - "TCB" - Barycentric Coordinate Time +* - "TCG" - Geocentric Coordinate Time +* - "LT" - Local Time (the offset from UTC is given by attribute LTOffset) +* +* An very informative description of these and other time scales is +* available at http://www.ucolick.org/~sla/leapsecs/timescales.html. + +* UTC Warnings: +* UTC should ideally be expressed using separate hours, minutes and +* seconds fields (or at least in seconds for a given date) if leap seconds +* are to be taken into account. Since the TimeFrame class represents +* each moment in time using a single floating point number (the axis value) +* there will be an ambiguity during a leap second. Thus an error of up to +* 1 second can result when using AST to convert a UTC time to another +* time scale if the time occurs within a leap second. Leap seconds +* occur at most twice a year, and are introduced to take account of +* variation in the rotation of the earth. The most recent leap second +* occurred on 1st January 1999. Although in the vast majority of cases +* leap second ambiguities won't matter, there are potential problems in +* on-line data acquisition systems and in critical applications involving +* taking the difference between two times. + +*att-- +*/ +astMAKE_TEST(TimeFrame,TimeScale,( this->timescale != AST__BADTS )) + +/* Copy constructor. */ +/* ----------------- */ + +/* Destructor. */ +/* ----------- */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for TimeFrame objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the TimeFrame class to an output Channel. + +* Parameters: +* this +* Pointer to the TimeFrame whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstTimeFrame *this; /* Pointer to the TimeFrame structure */ + AstTimeScaleType ts; /* TimeScale attribute value */ + const char *sval; /* Pointer to string value */ + double dval; /* Double value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TimeFrame structure. */ + this = (AstTimeFrame *) this_object; + +/* Write out values representing the instance variables for the + TimeFrame class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* TimeScale. */ +/* ---------- */ + set = TestTimeScale( this, status ); + ts = set ? GetTimeScale( this, status ) : astGetTimeScale( this ); + +/* If set, convert explicitly to a string for the external + representation. */ + sval = ""; + if ( set ) { + if ( astOK ) { + sval = TimeScaleString( ts, status ); + +/* Report an error if the TimeScale value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "%s(%s): Corrupt %s contains invalid time scale " + "identification code (%d).", status, "astWrite", + astGetClass( channel ), astGetClass( this ), (int) ts ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "timescale" ); + } + +/* Write out the value. */ + astWriteString( channel, "TmScl", set, 1, sval, "Time scale" ); + +/* AlignTimeScale. */ +/* --------------- */ + set = TestAlignTimeScale( this, status ); + ts = set ? GetAlignTimeScale( this, status ) : astGetAlignTimeScale( this ); + +/* If set, convert explicitly to a string for the external representation. */ + if ( set ) { + if ( astOK ) { + sval = TimeScaleString( ts, status ); + +/* Report an error if the TimeScale value was not recognised. */ + if ( !sval ) { + astError( AST__SCSIN, + "%s(%s): Corrupt %s contains invalid alignment time " + "scale identification code (%d).", status, "astWrite", + astGetClass( channel ), astGetClass( this ), (int) ts ); + } + } + +/* If not set, use astGetAttrib which returns a string value using + (possibly over-ridden) methods. */ + } else { + sval = astGetAttrib( this_object, "aligntimescale" ); + } + +/* Write out the value. */ + astWriteString( channel, "ATmScl", set, 0, sval, "Alignment time scale" ); + +/* TimeOrigin. */ +/* ----------- */ + set = TestTimeOrigin( this, status ); + dval = set ? GetTimeOrigin( this, status ) : astGetTimeOrigin( this ); + astWriteDouble( channel, "TmOrg", set, 0, dval, "Time offset" ); + +/* LTOffset. */ +/* --------- */ + set = TestLTOffset( this, status ); + dval = set ? GetLTOffset( this, status ) : astGetLTOffset( this ); + astWriteDouble( channel, "LTOff", set, 0, dval, "Local Time offset from UTC" ); + +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsATimeFrame and astCheckTimeFrame functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(TimeFrame,Frame) +astMAKE_CHECK(TimeFrame) + +AstTimeFrame *astTimeFrame_( const char *options, int *status, ...) { +/* +*+ +* Name: +* astTimeFrame + +* Purpose: +* Create a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* AstTimeFrame *astTimeFrame( const char *options, int *status, ... ) + +* Class Membership: +* TimeFrame constructor. + +* Description: +* This function creates a new TimeFrame and optionally initialises its +* attributes. + +* Parameters: +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new TimeFrame. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new TimeFrame. + +* Notes: +* - A NULL pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- + +* Implementation Notes: +* - This function implements the basic TimeFrame constructor which +* is available via the protected interface to the TimeFrame class. +* A public interface is provided by the astTimeFrameId_ function. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *um; /* Mapping from default to actual units */ + AstTimeFrame *new; /* Pointer to new TimeFrame */ + AstSystemType s; /* System */ + const char *u; /* Units string */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the TimeFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitTimeFrame( NULL, sizeof( AstTimeFrame ), !class_init, + &class_vtab, "TimeFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new TimeFrame's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* Check the Units are appropriate for the System. */ + u = astGetUnit( new, 0 ); + s = astGetSystem( new ); + um = astUnitMapper( DefUnit( s, "astTimeFrame", "TimeFrame", status ), + u, NULL, NULL ); + if( um ) { + um = astAnnul( um ); + } else { + astError( AST__BADUN, "astTimeFrame: Inappropriate units (%s) " + "specified for a %s axis.", status, u, SystemLabel( s, status ) ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new TimeFrame. */ + return new; +} + +AstTimeFrame *astInitTimeFrame_( void *mem, size_t size, int init, + AstTimeFrameVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitTimeFrame + +* Purpose: +* Initialise a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* AstTimeFrame *astInitTimeFrame( void *mem, size_t size, int init, +* AstFrameVtab *vtab, const char *name ) + +* Class Membership: +* TimeFrame initialiser. + +* Description: +* This function is provided for use by class implementations to +* initialise a new TimeFrame object. It allocates memory (if +* necessary) to accommodate the TimeFrame plus any additional data +* associated with the derived class. It then initialises a +* TimeFrame structure at the start of this memory. If the "init" +* flag is set, it also initialises the contents of a virtual function +* table for a TimeFrame at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the TimeFrame is to be +* created. This must be of sufficient size to accommodate the +* TimeFrame data (sizeof(TimeFrame)) plus any data used by +* the derived class. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the TimeFrame (plus derived +* class data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also stored +* in the TimeFrame structure, so a valid value must be supplied +* even if not required for allocating memory. +* init +* A logical flag indicating if the TimeFrame's virtual function +* table is to be initialised. If this value is non-zero, the +* virtual function table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new TimeFrame. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object belongs +* (it is this pointer value that will subsequently be returned by +* the astGetClass method). + +* Returned Value: +* A pointer to the new TimeFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstTimeFrame *new; /* Pointer to the new TimeFrame */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitTimeFrameVtab( vtab, name ); + +/* Initialise a 1D Frame structure (the parent class) as the first component + within the TimeFrame structure, allocating memory if necessary. */ + new = (AstTimeFrame *) astInitFrame( mem, size, 0, + (AstFrameVtab *) vtab, name, 1 ); + + if ( astOK ) { + +/* Initialise the TimeFrame data. */ +/* ----------------------------- */ +/* Initialise all attributes to their "undefined" values. */ + new->timeorigin = AST__BAD; + new->ltoffset = AST__BAD; + new->timescale = AST__BADTS; + new->aligntimescale = AST__BADTS; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + + } + +/* Return a pointer to the new object. */ + return new; +} + +AstTimeFrame *astLoadTimeFrame_( void *mem, size_t size, + AstTimeFrameVtab *vtab, + const char *name, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadTimeFrame + +* Purpose: +* Load a TimeFrame. + +* Type: +* Protected function. + +* Synopsis: +* #include "timeframe.h" +* AstTimeFrame *astLoadTimeFrame( void *mem, size_t size, +* AstTimeFrameVtab *vtab, +* const char *name, AstChannel *channel ) + +* Class Membership: +* TimeFrame loader. + +* Description: +* This function is provided to load a new TimeFrame using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* TimeFrame structure in this memory, using data read from the +* input Channel. + +* Parameters: +* mem +* A pointer to the memory into which the TimeFrame is to be +* loaded. This must be of sufficient size to accommodate the +* TimeFrame data (sizeof(TimeFrame)) plus any data used by +* derived classes. If a value of NULL is given, this function +* will allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the TimeFrame (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the TimeFrame structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstTimeFrame) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new TimeFrame. If this is NULL, a pointer +* to the (static) virtual function table for the TimeFrame class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "TimeFrame" is used instead. + +* Returned Value: +* A pointer to the new TimeFrame. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTimeFrame *new; /* Pointer to the new TimeFrame */ + char *sval; /* Pointer to string value */ + double obslat; /* Value for ObsLat attribute */ + double obslon; /* Value for ObsLon attribute */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this TimeFrame. In this case the + TimeFrame belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstTimeFrame ); + vtab = &class_vtab; + name = "TimeFrame"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitTimeFrameVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built TimeFrame. */ + new = astLoadFrame( mem, size, (AstFrameVtab *) vtab, name, + channel ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "TimeFrame" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* TimeScale. */ +/* ---------- */ +/* Set the default and read the external representation as a string. */ + new->timescale = AST__BADTS; + sval = astReadString( channel, "tmscl", NULL ); + +/* If a value was read, convert from a string to a TimeScale code. */ + if ( sval ) { + if ( astOK ) { + new->timescale = TimeScaleCode( sval, status ); + +/* Report an error if the value wasn't recognised. */ + if ( new->timescale == AST__BADTS ) { + astError( AST__ATTIN, + "astRead(%s): Invalid time scale description " + "\"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* AlignTimeScale. */ +/* --------------- */ +/* Set the default and read the external representation as a string. */ + new->aligntimescale = AST__BADTS; + sval = astReadString( channel, "atmscl", NULL ); + +/* If a value was read, convert from a string to a TimeScale code. */ + if ( sval ) { + if ( astOK ) { + new->aligntimescale = TimeScaleCode( sval, status ); + +/* Report an error if the value wasn't recognised. */ + if ( new->aligntimescale == AST__BADTS ) { + astError( AST__ATTIN, + "astRead(%s): Invalid alignment time scale " + "description \"%s\".", status, astGetClass( channel ), sval ); + } + } + +/* Free the string value. */ + sval = astFree( sval ); + } + +/* ClockLat. */ +/* --------- */ +/* Retained for backward compatibility with older versions of AST in + which TimeFrame had a ClockLat attribute (now ObsLat is used instead). */ + if( !astTestObsLat( new ) ) { + obslat = astReadDouble( channel, "cllat", AST__BAD ); + if ( obslat != AST__BAD ) astSetObsLat( new, obslat ); + } + +/* ClockLon. */ +/* ------- */ +/* Retained for backward compatibility with older versions of AST in + which TimeFrame had a ClockLon attribute (now ObsLon is used instead). */ + if( !astTestObsLon( new ) ) { + obslon = astReadDouble( channel, "cllon", AST__BAD ); + if ( obslon != AST__BAD ) astSetObsLon( new, obslon ); + } + +/* TimeOrigin. */ +/* --------- */ + new->timeorigin = astReadDouble( channel, "tmorg", AST__BAD ); + if ( TestTimeOrigin( new, status ) ) SetTimeOrigin( new, new->timeorigin, status ); + +/* LTOffset. */ +/* --------- */ + new->ltoffset = astReadDouble( channel, "ltoff", AST__BAD ); + if ( TestLTOffset( new, status ) ) SetLTOffset( new, new->ltoffset, status ); + +/* If an error occurred, clean up by deleting the new TimeFrame. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new TimeFrame pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astSetTimeScale_( AstTimeFrame *this, AstTimeScaleType value, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,TimeFrame,SetTimeScale))(this,value, status ); +} + +void astClearTimeScale_( AstTimeFrame *this, int *status ){ + if ( !astOK ) return; + (**astMEMBER(this,TimeFrame,ClearTimeScale))(this, status ); +} + +AstTimeScaleType astGetAlignTimeScale_( AstTimeFrame *this, int *status ) { + if ( !astOK ) return AST__BADTS; + return (**astMEMBER(this,TimeFrame,GetAlignTimeScale))(this, status ); +} + +AstTimeScaleType astGetTimeScale_( AstTimeFrame *this, int *status ) { + if ( !astOK ) return AST__BADTS; + return (**astMEMBER(this,TimeFrame,GetTimeScale))(this, status ); +} + +double astCurrentTime_( AstTimeFrame *this, int *status ){ + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,TimeFrame,CurrentTime))(this, status ); +} + + + +/* Special public interface functions. */ +/* =================================== */ +/* These provide the public interface to certain special functions + whose public interface cannot be handled using macros (such as + astINVOKE) alone. In general, they are named after the + corresponding protected version of the function, but with "Id" + appended to the name. */ + +/* Public Interface Function Prototypes. */ +/* ------------------------------------- */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstTimeFrame *astTimeFrameId_( const char *, ... ); + +/* Special interface function implementations. */ +/* ------------------------------------------- */ +AstTimeFrame *astTimeFrameId_( const char *options, ... ) { +/* +*++ +* Name: +c astTimeFrame +f AST_TIMEFRAME + +* Purpose: +* Create a TimeFrame. + +* Type: +* Public function. + +* Synopsis: +c #include "timeframe.h" +c AstTimeFrame *astTimeFrame( const char *options, ... ) +f RESULT = AST_TIMEFRAME( OPTIONS, STATUS ) + +* Class Membership: +* TimeFrame constructor. + +* Description: +* This function creates a new TimeFrame and optionally initialises +* its attributes. +* +* A TimeFrame is a specialised form of one-dimensional Frame which +* represents various coordinate systems used to describe positions in +* time. +* +* A TimeFrame represents a moment in time as either an Modified Julian +* Date (MJD), a Julian Date (JD), a Besselian epoch or a Julian epoch, +* as determined by the System attribute. Optionally, a zero point can be +* specified (using attribute TimeOrigin) which results in the TimeFrame +* representing time offsets from the specified zero point. +* +* Even though JD and MJD are defined as being in units of days, the +* TimeFrame class allows other units to be used (via the Unit attribute) +* on the basis of simple scalings (60 seconds = 1 minute, 60 minutes = 1 +* hour, 24 hours = 1 day, 365.25 days = 1 year). Likewise, Julian epochs +* can be described in units other than the usual years. Besselian epoch +* are always represented in units of (tropical) years. +* +* The TimeScale attribute allows the time scale to be specified (that +* is, the physical proces used to define the rate of flow of time). +* MJD, JD and Julian epoch can be used to represent a time in any +* supported time scale. However, Besselian epoch may only be used with the +* "TT" (Terrestrial Time) time scale. The list of supported time scales +* includes universal time and siderial time. Strictly, these represent +* angles rather than time scales, but are included in the list since +* they are in common use and are often thought of as time scales. +* +* When a time value is formatted it can be formated either as a simple +* floating point value, or as a Gregorian date (see the Format +* attribute). + +* Parameters: +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new TimeFrame. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new TimeFrame. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTimeFrame() +f AST_TIMEFRAME = INTEGER +* A pointer to the new TimeFrame. + +* Notes: +* - When conversion between two TimeFrames is requested (as when +c supplying TimeFrames to astConvert), +f supplying TimeFrames AST_CONVERT), +* account will be taken of the nature of the time coordinate systems +* they represent, together with any qualifying time scale, offset, +* unit, etc. The AlignSystem and AlignTimeScale attributes will also be +* taken into account. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astTimeFrame constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astTimeFrame_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - The variable argument list also prevents this function from +* invoking astTimeFrame_ directly, so it must be a +* re-implementation of it in all respects, except for the final +* conversion of the result to an ID value. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMapping *um; /* Mapping from default to actual units */ + AstTimeFrame *new; /* Pointer to new TimeFrame */ + AstSystemType s; /* System */ + const char *u; /* Units string */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the TimeFrame, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitTimeFrame( NULL, sizeof( AstTimeFrame ), !class_init, + &class_vtab, "TimeFrame" ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new TimeFrame's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* Check the Units are appropriate for the System. */ + u = astGetUnit( new, 0 ); + s = astGetSystem( new ); + um = astUnitMapper( DefUnit( s, "astTimeFrame", "TimeFrame", status ), + u, NULL, NULL ); + if( um ) { + um = astAnnul( um ); + } else { + astError( AST__BADUN, "astTimeFrame: Inappropriate units (%s) " + "specified for a %s axis.", status, u, SystemLabel( s, status ) ); + } + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new TimeFrame. */ + return astMakeId( new ); +} + + + + + + + + + + + + + + + + + + + + diff --git a/timeframe.h b/timeframe.h new file mode 100644 index 0000000..4aae267 --- /dev/null +++ b/timeframe.h @@ -0,0 +1,324 @@ +#if !defined( TIMEFRAME_INCLUDED ) /* Include this file only once */ +#define TIMEFRAME_INCLUDED +/* +*+ +* Name: +* timeframe.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the TimeFrame class. + +* Invocation: +* #include "timeframe.h" + +* Description: +* This include file defines the interface to the TimeFrame class +* and provides the type definitions, function prototypes and +* macros, etc. needed to use this class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 20-MAY-2005 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "object.h" /* Base Object class */ +#include "frame.h" /* Parent Frame class */ +#include "skyframe.h" /* Celestial coordinate systems */ + +/* Macros. */ +/* ======= */ +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +#if defined(astCLASS) /* Protected */ + +/* Values used to represent different System attribute values. */ +#define AST__MJD 1 +#define AST__JD 2 +#define AST__JEPOCH 3 +#define AST__BEPOCH 4 + +/* Values used to represent different TimeScale attribute values. */ +#define AST__BADTS 0 +#define AST__TAI 1 +#define AST__UTC 2 +#define AST__UT1 3 +#define AST__GMST 4 +#define AST__LAST 5 +#define AST__LMST 6 +#define AST__TT 7 +#define AST__TDB 8 +#define AST__TCB 9 +#define AST__TCG 10 +#define AST__LT 11 + +/* Define constants used to size global arrays in this module. */ +#define AST__TIMEFRAME_FORMAT_BUFF_LEN 200 +#define AST__TIMEFRAME_GETATTRIB_BUFF_LEN 50 +#define AST__TIMEFRAME_GETLABEL_BUFF_LEN 200 +#define AST__TIMEFRAME_GETSYMBOL_BUFF_LEN 20 +#define AST__TIMEFRAME_GETTITLE_BUFF_LEN 200 + +#endif + +/* Type Definitions. */ +/* ================= */ + +/* Integer type used to store the TimeScale attribute. */ +typedef int AstTimeScaleType; + +/* TimeFrame structure. */ +/* ------------------- */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstTimeFrame { + +/* Attributes inherited from the parent class. */ + AstFrame frame; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double ltoffset; /* Offset from UTC to Local Time */ + double timeorigin; /* Zero point for time axis */ + AstTimeScaleType timescale; /* Time scale */ + AstTimeScaleType aligntimescale; /* Alignment time scale */ +} AstTimeFrame; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all objects in the + class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstTimeFrameVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstFrameVtab frame_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + double (* CurrentTime)( AstTimeFrame *, int * ); + + double (* GetLTOffset)( AstTimeFrame *, int * ); + int (* TestLTOffset)( AstTimeFrame *, int * ); + void (* ClearLTOffset)( AstTimeFrame *, int * ); + void (* SetLTOffset)( AstTimeFrame *, double, int * ); + + double (* GetTimeOrigin)( AstTimeFrame *, int * ); + int (* TestTimeOrigin)( AstTimeFrame *, int * ); + void (* ClearTimeOrigin)( AstTimeFrame *, int * ); + void (* SetTimeOrigin)( AstTimeFrame *, double, int * ); + + AstTimeScaleType (* GetTimeScale)( AstTimeFrame *, int * ); + int (* TestTimeScale)( AstTimeFrame *, int * ); + void (* ClearTimeScale)( AstTimeFrame *, int * ); + void (* SetTimeScale)( AstTimeFrame *, AstTimeScaleType, int * ); + + AstTimeScaleType (* GetAlignTimeScale)( AstTimeFrame *, int * ); + int (* TestAlignTimeScale)( AstTimeFrame *, int * ); + void (* ClearAlignTimeScale)( AstTimeFrame *, int * ); + void (* SetAlignTimeScale)( AstTimeFrame *, AstTimeScaleType, int * ); + +} AstTimeFrameVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstTimeFrameGlobals { + AstTimeFrameVtab Class_Vtab; + int Class_Init; + char Format_Buff[ AST__TIMEFRAME_FORMAT_BUFF_LEN + 1 ]; + char GetAttrib_Buff[ AST__TIMEFRAME_GETATTRIB_BUFF_LEN + 1 ]; + char GetLabel_Buff[ AST__TIMEFRAME_GETLABEL_BUFF_LEN + 1 ]; + char GetSymbol_Buff[ AST__TIMEFRAME_GETSYMBOL_BUFF_LEN + 1 ]; + char GetTitle_Buff[ AST__TIMEFRAME_GETTITLE_BUFF_LEN + 1 ]; +} AstTimeFrameGlobals; + +#endif + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(TimeFrame) /* Check class membership */ +astPROTO_ISA(TimeFrame) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +AstTimeFrame *astTimeFrame_( const char *, int *, ...); +#else +AstTimeFrame *astTimeFrameId_( const char *, ... )__attribute__((format(printf,1,2))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstTimeFrame *astInitTimeFrame_( void *, size_t, int, AstTimeFrameVtab *, + const char *, int * ); + +/* Vtab initialiser. */ +void astInitTimeFrameVtab_( AstTimeFrameVtab *, const char *, int * ); + +/* Loader. */ +AstTimeFrame *astLoadTimeFrame_( void *, size_t, AstTimeFrameVtab *, + const char *, AstChannel *channel, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitTimeFrameGlobals_( AstTimeFrameGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +double astCurrentTime_( AstTimeFrame *, int * ); + +#if defined(astCLASS) /* Protected */ + +double astGetLTOffset_( AstTimeFrame *, int * ); +int astTestLTOffset_( AstTimeFrame *, int * ); +void astClearLTOffset_( AstTimeFrame *, int * ); +void astSetLTOffset_( AstTimeFrame *, double, int * ); + +double astGetTimeOrigin_( AstTimeFrame *, int * ); +int astTestTimeOrigin_( AstTimeFrame *, int * ); +void astClearTimeOrigin_( AstTimeFrame *, int * ); +void astSetTimeOrigin_( AstTimeFrame *, double, int * ); + +AstTimeScaleType astGetTimeScale_( AstTimeFrame *, int * ); +int astTestTimeScale_( AstTimeFrame *, int * ); +void astClearTimeScale_( AstTimeFrame *, int * ); +void astSetTimeScale_( AstTimeFrame *, AstTimeScaleType, int * ); + +AstTimeScaleType astGetAlignTimeScale_( AstTimeFrame *, int * ); +int astTestAlignTimeScale_( AstTimeFrame *, int * ); +void astClearAlignTimeScale_( AstTimeFrame *, int * ); +void astSetAlignTimeScale_( AstTimeFrame *, AstTimeScaleType, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckTimeFrame(this) astINVOKE_CHECK(TimeFrame,this,0) +#define astVerifyTimeFrame(this) astINVOKE_CHECK(TimeFrame,this,1) + +/* Test class membership. */ +#define astIsATimeFrame(this) astINVOKE_ISA(TimeFrame,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected */ +#define astTimeFrame astINVOKE(F,astTimeFrame_) +#else +#define astTimeFrame astINVOKE(F,astTimeFrameId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitTimeFrame(mem,size,init,vtab,name) \ +astINVOKE(O,astInitTimeFrame_(mem,size,init,vtab,name,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitTimeFrameVtab(vtab,name) astINVOKE(V,astInitTimeFrameVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadTimeFrame(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadTimeFrame_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) + +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ + +/* None. */ + +/* Interfaces to protected member functions. */ +/* ----------------------------------------- */ +/* Here we make use of astCheckTimeFrame to validate TimeFrame pointers + before use. This provides a contextual error report if a pointer to + the wrong sort of object is supplied. */ + +#define astCurrentTime(this) astINVOKE(V,astCurrentTime_(astCheckTimeFrame(this),STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ + +#define astGetTimeOrigin(this) astINVOKE(V,astGetTimeOrigin_(astCheckTimeFrame(this),STATUS_PTR)) +#define astTestTimeOrigin(this) astINVOKE(V,astTestTimeOrigin_(astCheckTimeFrame(this),STATUS_PTR)) +#define astClearTimeOrigin(this) astINVOKE(V,astClearTimeOrigin_(astCheckTimeFrame(this),STATUS_PTR)) +#define astSetTimeOrigin(this,value) astINVOKE(V,astSetTimeOrigin_(astCheckTimeFrame(this),value,STATUS_PTR)) + +#define astGetLTOffset(this) astINVOKE(V,astGetLTOffset_(astCheckTimeFrame(this),STATUS_PTR)) +#define astTestLTOffset(this) astINVOKE(V,astTestLTOffset_(astCheckTimeFrame(this),STATUS_PTR)) +#define astClearLTOffset(this) astINVOKE(V,astClearLTOffset_(astCheckTimeFrame(this),STATUS_PTR)) +#define astSetLTOffset(this,value) astINVOKE(V,astSetLTOffset_(astCheckTimeFrame(this),value,STATUS_PTR)) + +#define astGetTimeScale(this) astINVOKE(V,astGetTimeScale_(astCheckTimeFrame(this),STATUS_PTR)) +#define astTestTimeScale(this) astINVOKE(V,astTestTimeScale_(astCheckTimeFrame(this),STATUS_PTR)) +#define astClearTimeScale(this) astINVOKE(V,astClearTimeScale_(astCheckTimeFrame(this),STATUS_PTR)) +#define astSetTimeScale(this,value) astINVOKE(V,astSetTimeScale_(astCheckTimeFrame(this),value,STATUS_PTR)) + +#define astGetAlignTimeScale(this) astINVOKE(V,astGetAlignTimeScale_(astCheckTimeFrame(this),STATUS_PTR)) +#define astTestAlignTimeScale(this) astINVOKE(V,astTestAlignTimeScale_(astCheckTimeFrame(this),STATUS_PTR)) +#define astClearAlignTimeScale(this) astINVOKE(V,astClearAlignTimeScale_(astCheckTimeFrame(this),STATUS_PTR)) +#define astSetAlignTimeScale(this,value) astINVOKE(V,astSetAlignTimeScale_(astCheckTimeFrame(this),value,STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/timemap.c b/timemap.c new file mode 100644 index 0000000..669ec85 --- /dev/null +++ b/timemap.c @@ -0,0 +1,5187 @@ +/* +*class++ +* Name: +* TimeMap + +* Purpose: +* Sequence of time coordinate conversions. + +* Constructor Function: +c astTimeMap (also see astTimeAdd) +f AST_TIMEMAP (also see AST_TIMEADD) + +* Description: +* A TimeMap is a specialised form of 1-dimensional Mapping which can be +* used to represent a sequence of conversions between standard time +* coordinate systems. +* +* When a TimeMap is first created, it simply performs a unit +c (null) Mapping. Using the astTimeAdd +f (null) Mapping. Using the AST_TIMEADD +c function, a series of coordinate conversion steps may then be +f routine, a series of coordinate conversion steps may then be +* added. This allows multi-step conversions between a variety of +* time coordinate systems to be assembled out of a set of building +* blocks. +* +* For details of the individual coordinate conversions available, +c see the description of the astTimeAdd function. +f see the description of the AST_TIMEADD routine. + +* Inheritance: +* The TimeMap class inherits from the Mapping class. + +* Attributes: +* The TimeMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c In addition to those functions applicable to all Mappings, the +c following function may also be applied to all TimeMaps: +f In addition to those routines applicable to all Mappings, the +f following routine may also be applied to all TimeMaps: +* +c - astTimeAdd: Add a time coordinate conversion to an TimeMap +f - AST_TIMEADD: Add a time coordinate conversion to an TimeMap + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* NG: Norman Gray (Starlink) +* DSB: David Berry (Starlink) + +* History: +* 5-Sep-2003 (NG): +* Original version (drawing heavily on specmap.c) +* 25-MAY-2005 (DSB): +* Extensive modifications to make it more AST-like. +* 10-AUG-2005 (DSB): +* Add 2006 leap second. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 10-MAY-2006 (DSB): +* Override astEqual. +* 15-OCT-2006 (DSB): +* Add conversions between UT1 and UTC (UTTOUTC and UTCTOUT). +* 3-APR-2008 (DSB): +* Only call memcpy if the source and destination pointers are +* different. +* 15-APR-2008 (DSB): +* Add missing "break;" statement to "case AST__LMSTTOGMST:" +* in Transform. +* 20-MAY-2008 (DSB): +* Add conversions between Local Time and UTC (LTTOUTC and UTCTOLT). +* 18-JUN-2009 (DSB): +* Add OBSALT to argument list for TTTOTDB and TDBTOTT. Change +* CLOCKLAT/LON to OBSLAT/LON for consistency with other classes. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS TimeMap + +/* Codes to identify time coordinate conversions. */ +#define AST__TIME_NULL 0 /* Null value */ +#define AST__MJDTOMJD 1 /* MJD to MJD */ +#define AST__MJDTOJD 2 /* MJD to JD */ +#define AST__JDTOMJD 3 /* JD to MJD */ +#define AST__MJDTOBEP 4 /* MJD to Besselian epoch */ +#define AST__BEPTOMJD 5 /* Besselian epoch to MJD */ +#define AST__MJDTOJEP 6 /* MJD to Julian epoch */ +#define AST__JEPTOMJD 7 /* Julian epoch to MJD */ +#define AST__TAITOUTC 8 /* TAI to UTC */ +#define AST__UTCTOTAI 9 /* UTC to TAI */ +#define AST__TTTOTAI 10 /* TT to TAI */ +#define AST__TAITOTT 11 /* TAI to TT */ +#define AST__TDBTOTT 12 /* TDB to TT */ +#define AST__TTTOTDB 13 /* TT to TDB */ +#define AST__TCGTOTT 14 /* TCG to TT */ +#define AST__TTTOTCG 15 /* TT to TCG */ +#define AST__TCBTOTDB 16 /* TCB to TDB */ +#define AST__TDBTOTCB 17 /* TDB to TCB */ +#define AST__UTTOGMST 18 /* UT to GMST */ +#define AST__GMSTTOUT 19 /* GMST to UT1 */ +#define AST__GMSTTOLMST 20 /* GMST to LMST */ +#define AST__LMSTTOGMST 21 /* LMST to GMST */ +#define AST__LASTTOLMST 22 /* LAST to LMST */ +#define AST__LMSTTOLAST 23 /* LMST to LAST */ +#define AST__UTTOUTC 24 /* UT1 to UTC */ +#define AST__UTCTOUT 25 /* UTC to UT1 */ +#define AST__LTTOUTC 26 /* Local Time to UTC */ +#define AST__UTCTOLT 27 /* UTC to Local Time */ + +/* Maximum number of arguments required by a conversion. */ +#define MAX_ARGS 6 + +/* The alphabet (used for generating keywords for arguments). */ +#define ALPHABET "abcdefghijklmnopqrstuvwxyz" + +/* Angle conversion */ +#define PI 3.1415926535897932384626433832795028841971693993751 +#define D2PI (2*PI) +#define PIBY2 (PI/2.0) +#define D2R (PI/180.0) +#define R2D (180.0/PI) + +/* Other constants */ +#define SPD 86400 +#define LG 6.969290134E-10 +#define LB 1.55051976772E-8 +#define P0 6.55E-5 +#define TTOFF 32.184 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "pal.h" /* SLALIB interface */ +#include "slamap.h" /* Spatial sla mappings */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "unitmap.h" /* Unit (null) Mappings */ +#include "timemap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double (* parent_rate)( AstMapping *, double *, int, int, int * ); + + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(TimeMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(TimeMap,Class_Init) +#define class_vtab astGLOBAL(TimeMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstTimeMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstTimeMap *astTimeMapId_( int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *CvtString( int, const char **, int *, int *, const char *[ MAX_ARGS ], int * ); +static double Gmsta( double, double, int, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static double Rcc( double, double, double, double, double, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int CvtCode( const char *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void AddArgs( int, double *, int * ); +static void AddTimeCvt( AstTimeMap *, int, const double *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void TimeAdd( AstTimeMap *, const char *, const double[], int * ); + +static int GetObjSize( AstObject *, int * ); +/* Member functions. */ +/* ================= */ + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two TimeMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* TimeMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two TimeMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a TimeMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the TimeMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTimeMap *that; + AstTimeMap *this; + const char *argdesc[ MAX_ARGS ]; + const char *comment; + int i, j; + int nargs; + int nin; + int nout; + int result; + int szargs; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two TimeMap structures. */ + this = (AstTimeMap *) this_object; + that = (AstTimeMap *) that_object; + +/* Check the second object is a TimeMap. We know the first is a + TimeMap since we have arrived at this implementation of the virtual + function. */ + if( astIsATimeMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two TimeMaps differ, it may still be possible + for them to be equivalent. First compare the TimeMaps if their Invert + flags are the same. In this case all the attributes of the two TimeMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + if( this->ncvt == that->ncvt ) { + result = 1; + for( i = 0; i < this->ncvt && result; i++ ) { + if( this->cvttype[ i ] != that->cvttype[ i ] ) { + result = 0; + } else { + CvtString( this->cvttype[ i ], &comment, &nargs, &szargs, argdesc, status ); + for( j = 0; j < nargs; j++ ) { + if( !astEQUAL( this->cvtargs[ i ][ j ], + that->cvtargs[ i ][ j ] ) ){ + result = 0; + break; + } + } + } + } + } + +/* If the Invert flags for the two TimeMaps differ, the attributes of the two + TimeMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a TimeMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* TimeMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied TimeMap, +* in bytes. + +* Parameters: +* this +* Pointer to the TimeMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTimeMap *this; /* Pointer to TimeMap structure */ + int result; /* Result value to return */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the TimeMap structure. */ + this = (AstTimeMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + for ( cvt = 0; cvt < this->ncvt; cvt++ ) { + result += astTSizeOf( this->cvtargs[ cvt ] ); + } + + result += astTSizeOf( this->cvtargs ); + result += astTSizeOf( this->cvttype ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + + +static void AddArgs( int cvttype, double *cvtargs, int *status ) { +/* +* Name: +* AddArgs + +* Purpose: +* Set values for addition conversion arguments. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* void AddArgs( int cvttype, double *cvtargs, int *status ) + +* Class Membership: +* TimeMap member function. + +* Description: +* This function stores value for additional conversion arguments, +* based on the values supplied for the user arguments. + +* Parameters: +* cvttype +* The conversion type. +* cvtargs +* The arguments for the conversion. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + double r; /* Distance from Earth axis (AU) */ + double z; /* Distance from plane of Earth equator (AU) */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Test for each valid code value in turn and assign the appropriate + extra values. */ + switch ( cvttype ) { + + case AST__MJDTOMJD: + cvtargs[ 2 ] = cvtargs[ 0 ] - cvtargs[ 1 ]; + break; + + case AST__MJDTOJD: + cvtargs[ 2 ] = cvtargs[ 0 ] - cvtargs[ 1 ] + 2400000.5; + break; + + case AST__JDTOMJD: + cvtargs[ 2 ] = cvtargs[ 0 ] - cvtargs[ 1 ] - 2400000.5; + break; + + case AST__MJDTOBEP: + cvtargs[ 2 ] = palEpb( cvtargs[ 0 ] ) - palEpb( 0.0 ) - cvtargs[ 1 ]; + cvtargs[ 3 ] = palEpb2d( cvtargs[ 1 ] ) - palEpb2d( 0.0 ) - cvtargs[ 0 ]; + break; + + case AST__BEPTOMJD: + cvtargs[ 2 ] = palEpb2d( cvtargs[ 0 ] ) - palEpb2d( 0.0 ) - cvtargs[ 1 ]; + cvtargs[ 3 ] = palEpb( cvtargs[ 1 ] ) - palEpb( 0.0 ) - cvtargs[ 0 ]; + break; + + case AST__MJDTOJEP: + cvtargs[ 2 ] = palEpj( cvtargs[ 0 ] ) - palEpj( 0.0 ) - cvtargs[ 1 ]; + cvtargs[ 3 ] = palEpj2d( cvtargs[ 1 ] ) - palEpj2d( 0.0 ) - cvtargs[ 0 ]; + break; + + case AST__JEPTOMJD: + cvtargs[ 2 ] = palEpj2d( cvtargs[ 0 ] ) - palEpj2d( 0.0 ) - cvtargs[ 1 ]; + cvtargs[ 3 ] = palEpj( cvtargs[ 1 ] ) - palEpj( 0.0 ) - cvtargs[ 0 ]; + break; + + case AST__TTTOTDB: + palGeoc( cvtargs[ 2 ], cvtargs[ 3 ], &r, &z ); + cvtargs[ 4 ] = 0.001*r*AST__AU; + cvtargs[ 5 ] = 0.001*z*AST__AU; + break; + + case AST__TDBTOTT: + palGeoc( cvtargs[ 2 ], cvtargs[ 3 ], &r, &z ); + cvtargs[ 4 ] = 0.001*r*AST__AU; + cvtargs[ 5 ] = 0.001*z*AST__AU; + break; + + case AST__TDBTOTCB: + cvtargs[ 1 ] = LB*( cvtargs[ 0 ] - (TTOFF/SPD) + - 43144.0 ) + P0/SPD; + break; + + case AST__TCBTOTDB: + cvtargs[ 1 ] = LB*( cvtargs[ 0 ] - (TTOFF/SPD) + - 43144.0 ) + P0/SPD; + break; + + case AST__TTTOTCG: + cvtargs[ 1 ] = LG*( cvtargs[ 0 ] - (TTOFF/SPD) - 43144.0 ); + break; + + case AST__TCGTOTT: + cvtargs[ 1 ] = LG*( cvtargs[ 0 ] - (TTOFF/SPD) - 43144.0 ); + break; + + } +} + +static void AddTimeCvt( AstTimeMap *this, int cvttype, const double *args, int *status ) { +/* +* Name: +* AddTimeCvt + +* Purpose: +* Add a coordinate conversion step to an TimeMap. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* void AddTimeCvt( AstTimeMap *this, int cvttype, const double *args ) + +* Class Membership: +* TimeMap member function. + +* Description: +* This function allows one of the supported time coordinate +* conversions to be appended to a TimeMap. When a TimeMap is first +* created (using astTimeMap), it simply performs a unit mapping. By +* using AddTimeCvt repeatedly, a series of coordinate conversions may +* then be specified which the TimeMap will subsequently perform in +* sequence. This allows a complex coordinate conversion to be +* assembled out of the basic building blocks. The TimeMap will also +* perform the inverse coordinate conversion (applying the individual +* conversion steps in reverse) if required. + +* Parameters: +* this +* Pointer to the TimeMap. +* cvttype +* A code to identify which time coordinate conversion is to be +* appended. See the "Coordinate Conversions" section for details +* of those available. +* args +* Pointer to an array of double containing the argument values +* required to fully specify the required coordinate +* conversion. The number of arguments depends on the conversion +* (see the "Coordinate Conversions" section for details). This +* value is ignored and may be NULL if no arguments are required. + +* Returned Value: +* void. + +* Coordinate Conversions: +* The following values may be supplied for the "cvttype" parameter +* in order to specify the coordinate conversion to be performed. +* The argument(s) required to fully specify each conversion are +* indicated in parentheses after each value, and described at the end +* of the list. Values for these should be given in the array pointed +* at by "args". +* +* AST__MJDTOMJD( MJDOFF1, MJDOFF2 ) +* Convert Modified Julian Date from one offset to another. +* AST__MJDTOJD( MJDOFF, JDOFF ) +* Convert Modified Julian Date to Julian Date. +* AST__JDTOMJD( JDOFF, MJDOFF ) +* Convert Julian Date to Modified Julian Date. +* AST__MJDTOBEP( MJDOFF, BEPOFF ) +* Convert Modified Julian Date to Besselian epoch. +* AST__BEPTOMJD( BEPOFF, MJDOFF ) +* Convert Besselian epoch to Modified Julian Date. +* AST__MJDTOJEP( MJDOFF, JEPOFF ) +* Convert Modified Julian Date to Julian epoch. +* AST__JEPTOMJD( JEPOFF, MJDOFF ) +* Convert Julian epoch to Modified Julian Date. +* AST__TAITOUTC( MJDOFF ) +* Convert a TAI MJD to a UTC MJD. +* AST__UTCTOTAI( MJDOFF ) +* Convert a UTC MJD to a TAI MJD. +* AST__TAITOTT( MJDOFF ) +* Convert a TAI MJD to a TT MJD. +* AST__TTTOTAI( MJDOFF ) +* Convert a TT MJD to a TAI MJD. +* AST__TTTOTDB( MJDOFF, OBSLON, OBSLAT, OBSALT ) +* Convert a TT MJD to a TDB MJD. +* AST__TDBTOTT( MJDOFF, OBSLON, OBSLAT, OBSALT ) +* Convert a TDB MJD to a TT MJD. +* AST__TTTOTCG( MJDOFF ) +* Convert a TT MJD to a TCG MJD. +* AST__TCGTOTT( MJDOFF ) +* Convert a TCG MJD to a TT MJD. +* AST__TDBTOTCB( MJDOFF) +* Convert a TAI MJD to a TCB MJD. +* AST__TCBTOTDB( MJDOFF) +* Convert a TCB MJD to a TDB MJD. +* AST__UTTOGMST( MJDOFF ) +* Convert a UT MJD to a GMST MJD. +* AST__GMSTTOUT( MJDOFF ) +* Convert a GMST MJD to a UT MJD. +* AST__GMSTTOLMST( MJDOFF, OBSLON, OBSLAT ) +* Convert a GMST MJD to a LMST MJD. +* AST__LMSTTOGMST( MJDOFF, OBSLON, OBSLAT ) +* Convert a LMST MJD to a GMST MJD. +* AST__LASTTOLMST( MJDOFF, OBSLON, OBSLAT ) +* Convert a LAST MJD to a LMST MJD. +* AST__LMSTTOLAST( MJDOFF, OBSLON, OBSLAT ) +* Convert a LMST MJD to a LAST MJD. +* AST__UTTOUTC( DUT1 ) +* Convert a UT1 MJD to a UTC MJD. +* AST__UTCTOUT( DUT1 ) +* Convert a UTC MJD to a UT1 MJD. +* AST__LTTOUTC( LTOFF ) +* Convert a local time MJD to a UTC MJD. +* AST__UTCTOLT( LTOFF ) +* Convert a UTC MJD to a local time MJD. +* +* The units for the values processed by the above conversions are as +* follows: +* +* - MJD, MJDOFF, JD, JDOFF: days +* - Julian epochs, BEPOFF: Tropical years +* - Besselian epochs, JEPOFF: Julian years +* +* The arguments used in the above conversions are as follows: +* +* - MJDOFF: Offset to be added to each MJD value +* - JDOFF: Offset to be added to each JD value +* - JEPOFF: Offset to be added to each Julian epoch value +* - BEPOFF: Offset to be added to each Besselian epoch value +* - OBSLON: Observer's longitude in radians (+ve westwards) +* - OBSLAT: Observer's geodetic latitude in radians (+ve northwards) +* - OBSALT: Observer's geodetic altitude in metres. +* - DUT1: The value of UT1-UTC +* - LTOFF: The offset between Local Time and UTC (in hours, positive +* for time zones east of Greenwich). + +* Notes: +* - The specified conversion is appended only if the TimeMap's +* Invert attribute is zero. If it is non-zero, this function +* effectively prefixes the inverse of the conversion specified +* instead. +*/ + +/* Local Variables: */ + const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + const char *cvt_string; /* Pointer to conversion type string */ + int i; /* Argument index */ + int nargs; /* Number of user-supplied arguments */ + int ncvt; /* Number of coordinate conversions */ + int szargs; /* Size of arguments array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Validate the coordinate conversion type and obtain the number of + required user-supplied arguments, and the size of the array in which + to put the user-supplied arguments (the array may leave room after + the user-supplied arguments for various useful pre-calculated values). */ + cvt_string = CvtString( cvttype, &comment, &nargs, &szargs, argdesc, status ); + +/* If the coordinate conversion type was not valid, then report an + error. */ + if ( astOK && !cvt_string ) { + astError( AST__TIMIN, "AddTimeCvt(%s): Invalid time coordinate " + "conversion type (%d).", status, astGetClass( this ), + (int) cvttype ); + } + +/* Note the number of coordinate conversions already stored in the TimeMap. */ + if ( astOK ) { + ncvt = this->ncvt; + +/* Extend the array of conversion types and the array of pointers to + their argument lists to accommodate the new one. */ + this->cvttype = (int *) astGrow( this->cvttype, ncvt + 1, + sizeof( int ) ); + this->cvtargs = (double **) astGrow( this->cvtargs, ncvt + 1, + sizeof( double * ) ); + +/* Allocate memory for the argument list, putting a pointer to it into + the TimeMap. */ + this->cvtargs[ ncvt ] = astMalloc( sizeof( double ) * (size_t) szargs ); + +/* Store the conversion type and increment the conversion count. Also + copy the supplied arguments into the memory allocated above and put + suitable values in any elements of the argument array which are beyond + the end of the user-supplied arguments. These are intermediate values + calculated on the basis of the user-supplied arguments. */ + if ( astOK ) { + this->cvttype[ ncvt ] = cvttype; + for( i = 0; i < nargs; i++ ) this->cvtargs[ ncvt ][ i ] = args[ i ]; + for( i = nargs; i < szargs; i++ ) this->cvtargs[ ncvt ][ i ] = AST__BAD; + this->ncvt++; + +/* Test for each valid code value in turn and assign the appropriate extra values. */ + AddArgs( cvttype, this->cvtargs[ ncvt ], status ); + } + } +} + +static int CvtCode( const char *cvt_string, int *status ) { +/* +* Name: +* CvtCode + +* Purpose: +* Convert a conversion type from a string representation to a code value. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* int CvtCode( const char *cvt_string, int *status ) + +* Class Membership: +* TimeMap member function. + +* Description: +* This function accepts a string used to repersent one of the +* TimeMap coordinate conversions and converts it into a code +* value for internal use. + +* Parameters: +* cvt_string +* Pointer to a constant null-terminated string representing a +* time coordinate conversion. This is case sensitive and should +* contain no unnecessary white space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The equivalent conversion code. If the string was not +* recognised, the code AST__TIME_NULL is returned, without error. + +* Notes: +* - A value of AST__TIME_NULL will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Initialise. */ + result = AST__TIME_NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Test the string against each recognised value in turn and assign + the result. */ + if ( astChrMatch( cvt_string, "MJDTOJD" ) ) { + result = AST__MJDTOJD; + + } else if ( astChrMatch( cvt_string, "MJDTOMJD" ) ) { + result = AST__MJDTOMJD; + + } else if ( astChrMatch( cvt_string, "JDTOMJD" ) ) { + result = AST__JDTOMJD; + + } else if ( astChrMatch( cvt_string, "JDTOMJD" ) ) { + result = AST__JDTOMJD; + + } else if ( astChrMatch( cvt_string, "MJDTOBEP" ) ) { + result = AST__MJDTOBEP; + + } else if ( astChrMatch( cvt_string, "BEPTOMJD" ) ) { + result = AST__BEPTOMJD; + + } else if ( astChrMatch( cvt_string, "MJDTOJEP" ) ) { + result = AST__MJDTOJEP; + + } else if ( astChrMatch( cvt_string, "JEPTOMJD" ) ) { + result = AST__JEPTOMJD; + + } else if ( astChrMatch( cvt_string, "TAITOUTC" ) ) { + result = AST__TAITOUTC; + + } else if ( astChrMatch( cvt_string, "UTCTOTAI" ) ) { + result = AST__UTCTOTAI; + + } else if ( astChrMatch( cvt_string, "TAITOTT" ) ) { + result = AST__TAITOTT; + + } else if ( astChrMatch( cvt_string, "TTTOTAI" ) ) { + result = AST__TTTOTAI; + + } else if ( astChrMatch( cvt_string, "TTTOTDB" ) ) { + result = AST__TTTOTDB; + + } else if ( astChrMatch( cvt_string, "TDBTOTT" ) ) { + result = AST__TDBTOTT; + + } else if ( astChrMatch( cvt_string, "TTTOTCG" ) ) { + result = AST__TTTOTCG; + + } else if ( astChrMatch( cvt_string, "TCGTOTT" ) ) { + result = AST__TCGTOTT; + + } else if ( astChrMatch( cvt_string, "TDBTOTCB" ) ) { + result = AST__TDBTOTCB; + + } else if ( astChrMatch( cvt_string, "TCBTOTDB" ) ) { + result = AST__TCBTOTDB; + + } else if ( astChrMatch( cvt_string, "UTTOGMST" ) ) { + result = AST__UTTOGMST; + + } else if ( astChrMatch( cvt_string, "GMSTTOUT" ) ) { + result = AST__GMSTTOUT; + + } else if ( astChrMatch( cvt_string, "GMSTTOLMST" ) ) { + result = AST__GMSTTOLMST; + + } else if ( astChrMatch( cvt_string, "LMSTTOGMST" ) ) { + result = AST__LMSTTOGMST; + + } else if ( astChrMatch( cvt_string, "LASTTOLMST" ) ) { + result = AST__LASTTOLMST; + + } else if ( astChrMatch( cvt_string, "LMSTTOLAST" ) ) { + result = AST__LMSTTOLAST; + + } else if ( astChrMatch( cvt_string, "UTTOUTC" ) ) { + result = AST__UTTOUTC; + + } else if ( astChrMatch( cvt_string, "UTCTOUT" ) ) { + result = AST__UTCTOUT; + + } else if ( astChrMatch( cvt_string, "LTTOUTC" ) ) { + result = AST__LTTOUTC; + + } else if ( astChrMatch( cvt_string, "UTCTOLT" ) ) { + result = AST__UTCTOLT; + } + +/* Return the result. */ + return result; +} + +static const char *CvtString( int cvt_code, const char **comment, + int *nargs, int *szargs, + const char *arg[ MAX_ARGS ], int *status ) { +/* +* Name: +* CvtString + +* Purpose: +* Convert a conversion type from a code value to a string representation. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* const char *CvtString( int cvt_code, const char **comment, int *nargs, +* int *szargs, const char *arg[ MAX_ARGS ], int *status ) + +* Class Membership: +* TimeMap member function. + +* Description: +* This function accepts a code value used to represent one of the +* TimeMap coordinate conversions and converts it into an +* equivalent string representation. It also returns a descriptive +* comment and information about the arguments required in order to +* perform the conversion. + +* Parameters: +* cvt_code +* The conversion code. +* comment +* Address of a location to return a pointer to a constant +* null-terminated string containing a description of the +* conversion. +* nargs +* Address of an int in which to return the number of arguments +* required from the user in order to perform the conversion (may +* be zero). +* szargs +* Address of an int in which to return the number of arguments +* associated with the conversion. This may be bigger than "nargs" +* if the conversion can pre-calculate useful values on the basis +* of the user-supplied values. Such precalculated values are +* stored after the last user-supplied argument. +* arg +* An array in which to return a pointer to a constant +* null-terminated string for each argument (above) containing a +* description of what each argument represents. This includes both +* user-supplied arguments and pre-calculated values. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a constant null-terminated string representation of +* the conversion code value supplied. If the code supplied is not +* valid, a NULL pointer will be returned, without error. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the global error status set, or if it should fail +* for any reason. +*/ + +/* Local Variables: */ + const char *result; /* Result pointer to return */ + +/* Initialise the returned values. */ + *comment = NULL; + *nargs = 0; + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Test for each valid code value in turn and assign the appropriate + return values. */ + switch ( cvt_code ) { + + case AST__MJDTOMJD: + *comment = "Convert MJD between offsets"; + result = "MJDTOMJD"; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "Input MJD offset"; + arg[ 1 ] = "Output MJD offset"; + arg[ 2 ] = "Combined offset"; + break; + + case AST__MJDTOJD: + *comment = "Convert MJD to JD"; + result = "MJDTOJD"; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "JD offset"; + arg[ 2 ] = "Combined offset"; + break; + + case AST__JDTOMJD: + *comment = "Convert JD to MJD"; + result = "JDTOMJD"; + *nargs = 2; + *szargs = 3; + arg[ 0 ] = "JD offset"; + arg[ 1 ] = "MJD offset"; + arg[ 2 ] = "Combined offset"; + break; + + case AST__MJDTOBEP: + *comment = "Convert MJD to Besselian epoch"; + result = "MJDTOBEP"; + *nargs = 2; + *szargs = 4; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Besselian epoch offset"; + arg[ 2 ] = "Combined forward offset"; + arg[ 3 ] = "Combined inverse offset"; + break; + + case AST__BEPTOMJD: + *comment = "Convert Besselian epoch to MJD"; + result = "BEPTOMJD"; + *nargs = 2; + *szargs = 4; + arg[ 0 ] = "Besselian epoch offset"; + arg[ 1 ] = "MJD offset"; + arg[ 2 ] = "Combined forward offset"; + arg[ 3 ] = "Combined inverse offset"; + break; + + case AST__MJDTOJEP: + *comment = "Convert MJD to Julian epoch"; + result = "MJDTOJEP"; + *nargs = 2; + *szargs = 4; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Julian epoch offset"; + arg[ 2 ] = "Combined forward offset"; + arg[ 3 ] = "Combined inverse offset"; + break; + + case AST__JEPTOMJD: + *comment = "Convert Julian epoch to MJD"; + result = "JEPTOMJD"; + *nargs = 2; + *szargs = 4; + arg[ 0 ] = "Julian epoch offset"; + arg[ 1 ] = "MJD offset"; + arg[ 2 ] = "Combined forward offset"; + arg[ 3 ] = "Combined inverse offset"; + break; + + case AST__TAITOUTC: + *comment = "Convert TAI to UTC"; + result = "TAITOUTC"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "MJD offset"; + break; + + case AST__UTCTOTAI: + *comment = "Convert UTC to TAI"; + result = "UTCTOTAI"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "MJD offset"; + break; + + case AST__TAITOTT: + *comment = "Convert TAI to TT"; + result = "TAITOTT"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "MJD offset"; + break; + + case AST__TTTOTAI: + *comment = "Convert TT to TAI"; + result = "TTTOTAI"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "MJD offset"; + break; + + case AST__TTTOTDB: + *comment = "Convert TT to TDB"; + result = "TTTOTDB"; + *nargs = 4; + *szargs = 6; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + arg[ 3 ] = "Observer altitude"; + arg[ 4 ] = "Distance from earth spin axis"; + arg[ 5 ] = "Distance north of equatorial plane"; + break; + + case AST__TDBTOTT: + *comment = "Convert TDB to TT"; + result = "TDBTOTT"; + *nargs = 4; + *szargs = 6; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + arg[ 3 ] = "Observer altitude"; + arg[ 4 ] = "Distance from earth spin axis"; + arg[ 5 ] = "Distance north of equatorial plane"; + break; + + case AST__TTTOTCG: + *comment = "Convert TT to TCG"; + result = "TTTOTCG"; + *nargs = 1; + *szargs = 2; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "TCG offset"; + break; + + case AST__TCGTOTT: + *comment = "Convert TCG to TT"; + result = "TCGTOTT"; + *nargs = 1; + *szargs = 2; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "TCG offset"; + break; + + case AST__TDBTOTCB: + *comment = "Convert TDB to TCB"; + result = "TDBTOTCB"; + *nargs = 1; + *szargs = 2; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "TCB offset"; + break; + + case AST__TCBTOTDB: + *comment = "Convert TCB to TDB"; + result = "TCBTOTDB"; + *nargs = 1; + *szargs = 2; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "TCB offset"; + break; + + case AST__UTTOGMST: + *comment = "Convert UT to GMST"; + result = "UTTOGMST"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "MJD offset"; + break; + + case AST__GMSTTOUT: + *comment = "Convert GMST to UT"; + result = "GMSTTOUT"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "MJD offset"; + break; + + case AST__GMSTTOLMST: + *comment = "Convert GMST to LMST"; + result = "GMSTTOLMST"; + *nargs = 3; + *szargs = 3; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + break; + + case AST__LMSTTOGMST: + *comment = "Convert LMST to GMST"; + result = "LMSTTOGMST"; + *nargs = 3; + *szargs = 3; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + break; + + case AST__LASTTOLMST: + *comment = "Convert LAST to LMST"; + result = "LASTTOLMST"; + *nargs = 3; + *szargs = 3; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + break; + + case AST__LMSTTOLAST: + *comment = "Convert LMST to LAST"; + result = "LMSTTOLAST"; + *nargs = 3; + *szargs = 3; + arg[ 0 ] = "MJD offset"; + arg[ 1 ] = "Observer longitude"; + arg[ 2 ] = "Observer latitude"; + break; + + case AST__UTTOUTC: + *comment = "Convert UT1 to UTC"; + result = "UTTOUTC"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "DUT1"; + break; + + case AST__UTCTOUT: + *comment = "Convert UTC to UT1"; + result = "UTCTOUT"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "DUT1"; + break; + + case AST__LTTOUTC: + *comment = "Convert Local Time to UTC"; + result = "LTTOUTC"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "LTOFF"; + break; + + case AST__UTCTOLT: + *comment = "Convert UTC to Local Time"; + result = "UTCTOLT"; + *nargs = 1; + *szargs = 1; + arg[ 0 ] = "LTOFF"; + break; + } + +/* Return the result. */ + return result; +} + +double astDat_( double in, int forward, int *status ){ +/* +*+ +* Name: +* Dat + +* Purpose: +* Convert between UTC and TAI. + +* Type: +* Protected function. + +* Synopsis: +* #include "timemap.h" +* double astDat( double in, int forward ) + +* Class Membership: +* TimeMap member function + +* Description: +* This function returns the difference between Coordinated Universal Time +* (UTC) and International Atomic Time (TAI), at a given epoch. + +* Parameters: +* in +* UTC date or TAI time (as selected by "forward"), as an absolute +* MJD. +* forward +* If non-zero, "in" should be a UTC value, and the returned value +* is TAI-UTC. If zero, "in" should be a TAI value, and the returned +* value is UTC-TAI. + +* Returned Value: +* Either UTC-TAI or TAI-UTC (as indicated by "forward") in units of +* seconds. + +* Notes: +* - The UTC is specified to be a date rather than a time to indicate +* that care needs to be taken not to specify an instant which lies +* within a leap second. Though in most cases UTC can include the +* fractional part, correct behaviour on the day of a leap second +* can only be guaranteed up to the end of the second 23:59:59. +* - For epochs from 1961 January 1 onwards, the expressions from the +* file ftp://maia.usno.navy.mil/ser7/tai-utc.dat are used. +* - The 5ms time step at 1961 January 1 is taken from 2.58.1 (p87) of +* the 1992 Explanatory Supplement. +* - UTC began at 1960 January 1.0 (JD 2436934.5) and it is improper +* to call the routine with an earlier epoch. However, if this +* is attempted, the TAI-UTC expression for the year 1960 is used. + +* Implementation Details: +* - This function is based on SLA_DAT by P.T.Wallace. +* - This routine must be updated on each occasion that a leap second is +* announced +* - Latest leap second: 2015 July 1 + +*- +*/ + +/* Local Variables: */ + double result; + +/* Initialise the returned value. */ + if( in == AST__BAD ) return AST__BAD; + +/* First do TAI-UTC at a given UTC + ------------------------------- */ + if( forward ) { + +/* 2015 July 1 */ + if ( in >= 57204.0 ) { + result = 36.0; + +/* 2012 July 1 */ + } else if ( in >= 56109.0 ) { + result = 35.0; + +/* 2009 January 1 */ + } else if ( in >= 54832.0 ) { + result = 34.0; + +/* 2006 January 1 */ + } else if( in >= 53736.0 ) { + result = 33.0; + +/* 1999 January 1 */ + } else if( in >= 51179.0 ){ + result = 32.0; + +/* 1997 July 1 */ + } else if( in >= 50630.0 ){ + result = 31.0; + +/* 1996 January 1 */ + } else if( in >= 50083.0 ){ + result = 30.0; + +/* 1994 July 1 */ + } else if( in >= 49534.0 ){ + result = 29.0; + +/* 1993 July 1 */ + } else if( in >= 49169.0 ){ + result = 28.0; + +/* 1992 July 1 */ + } else if( in >= 48804.0 ){ + result = 27.0; + +/* 1991 January 1 */ + } else if( in >= 48257.0 ){ + result = 26.0; + +/* 1990 January 1 */ + } else if( in >= 47892.0 ){ + result = 25.0; + +/* 1988 January 1 */ + } else if( in >= 47161.0 ){ + result = 24.0; + +/* 1985 July 1 */ + } else if( in >= 46247.0 ){ + result = 23.0; + +/* 1983 July 1 */ + } else if( in >= 45516.0 ){ + result = 22.0; + +/* 1982 July 1 */ + } else if( in >= 45151.0 ){ + result = 21.0; + +/* 1981 July 1 */ + } else if( in >= 44786.0 ){ + result = 20.0; + +/* 1980 January 1 */ + } else if( in >= 44239.0 ){ + result = 19.0; + +/* 1979 January 1 */ + } else if( in >= 43874.0 ){ + result = 18.0; + +/* 1978 January 1 */ + } else if( in >= 43509.0 ){ + result = 17.0; + +/* 1977 January 1 */ + } else if( in >= 43144.0 ){ + result = 16.0; + +/* 1976 January 1 */ + } else if( in >= 42778.0 ){ + result = 15.0; + +/* 1975 January 1 */ + } else if( in >= 42413.0 ){ + result = 14.0; + +/* 1974 January 1 */ + } else if( in >= 42048.0 ){ + result = 13.0; + +/* 1973 January 1 */ + } else if( in >= 41683.0 ){ + result = 12.0; + +/* 1972 July 1 */ + } else if( in >= 41499.0 ){ + result = 11.0; + +/* 1972 January 1 */ + } else if( in >= 41317.0 ){ + result = 10.0; + +/* 1968 February 1 */ + } else if( in >= 39887.0 ){ + result = 4.2131700 + ( in - 39126.0 )*0.002592; + +/* 1966 January 1 */ + } else if( in >= 39126.0 ){ + result = 4.3131700 + ( in - 39126.0 )*0.002592; + +/* 1965 September 1 */ + } else if( in >= 39004.0 ){ + result = 3.8401300 + ( in - 38761.0 )*0.001296; + +/* 1965 July 1 */ + } else if( in >= 38942.0 ){ + result = 3.7401300 + ( in - 38761.0 )*0.001296; + +/* 1965 March 1 */ + } else if( in >= 38820.0 ){ + result = 3.6401300 + ( in - 38761.0 )*0.001296; + +/* 1965 January 1 */ + } else if( in >= 38761.0 ){ + result = 3.5401300 + ( in - 38761.0 )*0.001296; + +/* 1964 September 1 */ + } else if( in >= 38639.0 ){ + result = 3.4401300 + ( in - 38761.0 )*0.001296; + +/* 1964 April 1 */ + } else if( in >= 38486.0 ){ + result = 3.3401300 + ( in - 38761.0 )*0.001296; + +/* 1964 January 1 */ + } else if( in >= 38395.0 ){ + result = 3.2401300 + ( in - 38761.0 )*0.001296; + +/* 1963 November 1 */ + } else if( in >= 38334.0 ){ + result = 1.9458580 + ( in - 37665.0 )*0.0011232; + +/* 1962 January 1 */ + } else if( in >= 37665.0 ){ + result = 1.8458580 + ( in - 37665.0 )*0.0011232; + +/* 1961 August 1 */ + } else if( in >= 37512.0 ){ + result = 1.3728180 + ( in - 37300.0 )*0.001296; + +/* 1961 January 1 */ + } else if( in >= 37300.0 ){ + result = 1.4228180 + ( in - 37300.0 )*0.001296; + +/* Before that */ + } else { + result = 1.4178180 + ( in - 37300.0 )*0.001296; + } + +/* Now do UTC-TAI at a given TAI. + ------------------------------ */ + } else { + + +/* 2015 July 1 */ + if ( in >= 57204.0 + 36.0/SPD ) { + result = -36.0; + +/* 2012 July 1 */ + } else if( in >= 56109.0 + 35.0/SPD ) { + result = -35.0; + +/* 2009 January 1 */ + } else if( in >= 54832.0 + 34.0/SPD ) { + result = -34.0; + +/* 2006 January 1 */ + } else if( in >= 53736.0 + 33.0/SPD ){ + result = -33.0; + +/* 1999 January 1 */ + } else if( in >= 51179.0 + 32.0/SPD ){ + result = -32.0; + +/* 1997 July 1 */ + } else if( in >= 50630.0 + 31.0/SPD ){ + result = -31.0; + +/* 1996 January 1 */ + } else if( in >= 50083.0 + 30.0/SPD ){ + result = -30.0; + +/* 1994 July 1 */ + } else if( in >= 49534.0 + 29.0/SPD ){ + result = -29.0; + +/* 1993 July 1 */ + } else if( in >= 49169.0 + 28.0/SPD ){ + result = -28.0; + +/* 1992 July 1 */ + } else if( in >= 48804.0 + 27.0/SPD ){ + result = -27.0; + +/* 1991 January 1 */ + } else if( in >= 48257.0 + 26.0/SPD ){ + result = -26.0; + +/* 1990 January 1 */ + } else if( in >= 47892.0 + 25.0/SPD ){ + result = -25.0; + +/* 1988 January 1 */ + } else if( in >= 47161.0 + 24.0/SPD ){ + result = -24.0; + +/* 1985 July 1 */ + } else if( in >= 46247.0 + 23.0/SPD ){ + result = -23.0; + +/* 1983 July 1 */ + } else if( in >= 45516.0 + 22.0/SPD ){ + result = -22.0; + +/* 1982 July 1 */ + } else if( in >= 45151.0 + 21.0/SPD ){ + result = -21.0; + +/* 1981 July 1 */ + } else if( in >= 44786.0 + 20.0/SPD ){ + result = -20.0; + +/* 1980 January 1 */ + } else if( in >= 44239.0 + 19.0/SPD ){ + result = -19.0; + +/* 1979 January 1 */ + } else if( in >= 43874.0 + 18.0/SPD ){ + result = -18.0; + +/* 1978 January 1 */ + } else if( in >= 43509.0 + 17.0/SPD ){ + result = -17.0; + +/* 1977 January 1 */ + } else if( in >= 43144.0 + 16.0/SPD ){ + result = -16.0; + +/* 1976 January 1 */ + } else if( in >= 42778.0 + 15.0/SPD ){ + result = -15.0; + +/* 1975 January 1 */ + } else if( in >= 42413.0 + 14.0/SPD ){ + result = -14.0; + +/* 1974 January 1 */ + } else if( in >= 42048.0 + 13.0/SPD ){ + result = -13.0; + +/* 1973 January 1 */ + } else if( in >= 41683.0 + 12.0/SPD ){ + result = -12.0; + +/* 1972 July 1 */ + } else if( in >= 41499.0 + 11.0/SPD ){ + result = -11.0; + +/* 1972 January 1 */ + } else if( in >= 41317.0 + 10.0/SPD ){ + result = -10.0; + +/* 1968 February 1 */ + } else if( in >= 39887.0 + ( 4.2131700 + + ( 39887.0 - 39126.0 )*0.002592 )/SPD ){ + result = -( 4.2131700 + ( in - 39126.0 )*0.002592 )/1.02592; + +/* 1966 January 1 */ + } else if( in >= 39126.0 + ( 4.3131700 + + ( 39126.0 - 39126.0 )*0.002592 )/SPD ){ + result = -( 4.2131700 + ( in - 39126.0 )*0.002592 )/1.02592; + +/* 1965 September 1 */ + } else if( in >= 39004.0 + ( 3.8401300 + + ( 39004.0 - 38761.0 )*0.001296 )/SPD ){ + result = -( 3.8401300 + ( in - 38761.0 )*0.001296 )/1.001296; + +/* 1965 July 1 */ + } else if( in >= 38942.0 + ( 3.7401300 + + ( 38942.0 - 38761.0 )*0.001296 )/SPD ){ + result = -( 3.7401300 + ( in - 38761.0 )*0.001296 )/1.01296; + +/* 1965 March 1 */ + } else if( in >= 38820.0 + ( 3.6401300 + + ( 38820.0 - 38761.0 )*0.001296 )/SPD ){ + result = -( 3.6401300 + ( in - 38761.0 )*0.001296 )/1.001296; + +/* 1965 January 1 */ + } else if( in >= 38761.0 + ( 3.5401300 + + ( 38761.0 - 38761.0 )*0.001296 )/SPD ){ + result = -( 3.5401300 + ( in - 38761.0 )*0.001296 )/1.001296; + +/* 1964 September 1 */ + } else if( in >= 38639.0 + ( 3.4401300 + + ( 38639.0 - 38761.0 )*0.001296 )/SPD ){ + result = -( 3.4401300 + ( in - 38761.0 )*0.001296 )/1.001296; + +/* 1964 April 1 */ + } else if( in >= 38486.0 + ( 3.3401300 + + ( 38486.0 - 38761.0 )*0.001296 )/SPD ){ + result = -( 3.3401300 + ( in - 38761.0 )*0.001296 )/1.001296; + +/* 1964 January 1 */ + } else if( in >= 38395.0 + ( 3.2401300 + + ( 38395.0 - 38761.0 )*0.001296 )/SPD ){ + result = -( 3.2401300 + ( in - 38761.0 )*0.001296 )/1.001296; + +/* 1963 November 1 */ + } else if( in >= 38334.0 + ( 1.9458580 + + ( 38334.0 - 37665.0 )*0.0011232 )/SPD ){ + result = -( 1.9458580 + ( in - 37665.0 )*0.0011232 )/1.0011232; + +/* 1962 January 1 */ + } else if( in >= 37665.0 + ( 1.8458580 + + ( 37665.0 - 37665.0 )*0.0011232 )/SPD ){ + result = -( 1.8458580 + ( in - 37665.0 )*0.0011232 )/1.0011232; + +/* 1961 August 1 */ + } else if( in >= 37512.0 + ( 1.3728180 + + ( 37512.0 - 37300.0 )*0.001296 )/SPD ){ + result = -( 1.3728180 + ( in - 37300.0 )*0.001296 )/1.001296; + +/* 1961 January 1 */ + } else if( in >= 37300.0 + ( 1.4228180 + + ( 37300.0 - 37300.0 )*0.001296 )/SPD ){ + result = -( 1.4228180 + ( in - 37300.0 )*0.001296 )/1.001296; + +/* Before that */ + } else { + result = -( 1.4178180 + ( in - 37300.0 )*0.001296 )/1.001296; + } + } + +/* Return the result */ + return result; +} + +static double Gmsta( double in, double off, int forward, int *status ){ +/* +* Name: +* Gmsta + +* Purpose: +* Convert between Universal Time (UT) and Greenwich Mean Sidereal Time (GMST). + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* double Gmsta( double in, double off, int forward, int *status ){ + +* Class Membership: +* TimeMap member function + +* Description: +* This functions converts between UT and GMST. Both timescales are +* represented by an MJD, rather than as an angle (as is done by SLALIB) +* in order to facilitate conversions from GMST to UT1. This means +* that whole days are retained. + +* Parameters: +* in +* The time to convert, represented as an offset in days from the MJD +* zero-point specified by "off". The time is either UT1 or GMST, as +* selected by "forward"). +* off +* The MJD value corresponding to a value of 0.0 for "in". +* forward +* If non-zero, "in" should be a UT1 value, and the returned value +* is GMST. If zero, "in" should be a GMST value, and the returned +* value is UT1. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* An offset in days from the MJD given by "off". When the returned +* value is added to "off" the sum is a GMST MJD (if "forward" is +* non-zero), or a UT1 MJD (if "forward" is zero), + +* Notes: +* - This function is based on SLA_GMST by P.T.Wallace. + +*/ + +/* Local Variables: */ + double dgdu; + double g; + double result; + double t; + double utl; + int nit; + +/* Initialise the returned value. */ + if( in == AST__BAD || off == AST__BAD ) return AST__BAD; + +/* First deal with UT1 -> GMST + --------------------------- */ + if( forward ) { + +/* Julian centuries since J2000. */ + t = ( off + in - 51544.5 )/36525.0; + +/* GMST at this UT1. */ + result = in + ( 24110.54841 + ( 8640184.812866 + ( 0.093104 - + 6.2E-6*t )*t )*t )/86400.0; + +/* Now deal with GMST -> UT1 + ----------------------- */ + } else { + +/* Form an initial guess at the UT1 value using the inverse of a linear + approximation to the UT1->GMST equation. */ + result = 0.996997348638869*in + 154.49194372222 - 0.00300265136113098*off; + +/* Loop round improving the guess, until the guess stops changing, or 10 + iterations have been performed. */ + utl = AST__BAD; + nit = 0; + while( result != utl && nit++ < 10 ){ + +/* Calculate the GMST at the current UT1 guess. */ + t = ( off + result - 51544.5 )/36525.0; + g = result + ( 24110.54841 + ( 8640184.812866 + ( 0.093104 - + 6.2E-6*t )*t )*t )/86400.0; + +/* Calculate the rate of change of GMST with respect to UT1 at the current + UT1 guess. */ + dgdu = 1.0 + ( 8640184.812866 + + ( 0.186208 - 12.4E-6*t )*t)/(36525.0*86400.0); + +/* Improve the UT1 guess. */ + utl = result; + result = result - ( g - in )/dgdu; + } + } + +/* Return the result */ + return result; +} + +void astInitTimeMapVtab_( AstTimeMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitTimeMapVtab + +* Purpose: +* Initialise a virtual function table for a TimeMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "timemap.h" +* void astInitTimeMapVtab( AstTimeMapVtab *vtab, const char *name ) + +* Class Membership: +* TimeMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the TimeMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsATimeMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + vtab->TimeAdd = TimeAdd; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_rate = mapping->Rate; + mapping->Rate = Rate; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the copy constructor, destructor and class dump + function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "TimeMap", + "Conversion between time coordinate systems" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a TimeMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* TimeMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated TimeMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated TimeMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated TimeMap which is to be merged with +* its neighbours. This should be a cloned copy of the TimeMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* TimeMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated TimeMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *new; /* Pointer to replacement Mapping */ + AstTimeMap *timemap; /* Pointer to TimeMap */ + const char *argdesc[ MAX_ARGS ]; /* Argument descriptions (junk) */ + const char *class; /* Pointer to Mapping class string */ + const char *comment; /* Pointer to comment string (junk) */ + double (*cvtargs)[ MAX_ARGS ]; /* Pointer to argument arrays */ + double tmp; /* Temporary storage */ + int *cvttype; /* Pointer to transformation type codes */ + int *szarg; /* Pointer to argument count array */ + int done; /* Finished (no further simplification)? */ + int iarg; /* Loop counter for arguments */ + int icvt1; /* Loop initial value */ + int icvt2; /* Loop final value */ + int icvt; /* Loop counter for transformation steps */ + int ikeep; /* Index to store step being kept */ + int imap1; /* Index of first TimeMap to merge */ + int imap2; /* Index of last TimeMap to merge */ + int imap; /* Loop counter for Mappings */ + int inc; /* Increment for transformation step loop */ + int invert; /* TimeMap applied in inverse direction? */ + int istep; /* Loop counter for transformation steps */ + int keep; /* Keep transformation step? */ + int narg; /* Number of user-supplied arguments */ + int ngone; /* Number of Mappings eliminated */ + int nstep0; /* Original number of transformation steps */ + int nstep; /* Total number of transformation steps */ + int result; /* Result value to return */ + int simpler; /* Simplification possible? */ + int unit; /* Replacement Mapping is a UnitMap? */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* TimeMaps can only be merged if they are in series (or if there is + only one Mapping present, in which case it makes no difference), so + do nothing if they are not. */ + if ( series || ( *nmap == 1 ) ) { + +/* Initialise the number of transformation steps to be merged to equal + the number in the nominated TimeMap. */ + nstep = ( (AstTimeMap *) ( *map_list )[ where ] )->ncvt; + +/* Search adjacent lower-numbered Mappings until one is found which is + not a TimeMap. Accumulate the number of transformation steps involved in + any TimeMaps found. */ + imap1 = where; + while ( ( imap1 - 1 >= 0 ) && astOK ) { + class = astGetClass( ( *map_list )[ imap1 - 1 ] ); + if ( !astOK || strcmp( class, "TimeMap" ) ) break; + nstep += ( (AstTimeMap *) ( *map_list )[ imap1 - 1 ] )->ncvt; + imap1--; + } + +/* Similarly search adjacent higher-numbered Mappings. */ + imap2 = where; + while ( ( imap2 + 1 < *nmap ) && astOK ) { + class = astGetClass( ( *map_list )[ imap2 + 1 ] ); + if ( !astOK || strcmp( class, "TimeMap" ) ) break; + nstep += ( (AstTimeMap *) ( *map_list )[ imap2 + 1 ] )->ncvt; + imap2++; + } + +/* Remember the initial number of transformation steps. */ + nstep0 = nstep; + +/* Allocate memory for accumulating a list of all the transformation + steps involved in all the TimeMaps found. */ + cvttype = astMalloc( sizeof( int ) * (size_t) nstep ); + cvtargs = astMalloc( sizeof( double[ MAX_ARGS ] ) * (size_t) nstep ); + szarg = astMalloc( sizeof( int ) * (size_t) nstep ); + +/* Loop to obtain the transformation data for each TimeMap being merged. */ + nstep = 0; + for ( imap = imap1; astOK && ( imap <= imap2 ); imap++ ) { + +/* Obtain a pointer to the TimeMap and note if it is being applied in + its inverse direction. */ + timemap = (AstTimeMap *) ( *map_list )[ imap ]; + invert = ( *invert_list )[ imap ]; + +/* Set up loop limits and an increment to scan the transformation + steps in each TimeMap in either the forward or reverse direction, as + dictated by the associated "invert" value. */ + icvt1 = invert ? timemap->ncvt - 1 : 0; + icvt2 = invert ? -1 : timemap->ncvt; + inc = invert ? -1 : 1; + +/* Loop through each transformation step in the TimeMap. */ + for ( icvt = icvt1; icvt != icvt2; icvt += inc ) { + +/* Store the transformation type code and use "CvtString" to determine + the associated number of arguments. Then store these arguments. */ + cvttype[ nstep ] = timemap->cvttype[ icvt ]; + (void) CvtString( cvttype[ nstep ], &comment, + &narg, szarg + nstep, argdesc, status ); + if ( !astOK ) break; + for ( iarg = 0; iarg < szarg[ nstep ]; iarg++ ) { + cvtargs[ nstep ][ iarg ] = timemap->cvtargs[ icvt ][ iarg ]; + } + +/* If the TimeMap is inverted, we must not only accumulate its + transformation steps in reverse, but also apply them in + reverse. For some steps this means changing arguments, for some it + means changing the transformation type code to a complementary + value, and for others it means both. Define macros to perform each + of the required changes. */ + +/* Macro to exchange a transformation type code for its inverse (and + vice versa). */ +#define SWAP_CODES( code1, code2 ) \ + if ( cvttype[ nstep ] == code1 ) { \ + cvttype[ nstep ] = code2; \ + AddArgs( code2, cvtargs[ nstep ], status ); \ + } else if ( cvttype[ nstep ] == code2 ) { \ + cvttype[ nstep ] = code1; \ + AddArgs( code1, cvtargs[ nstep ], status ); \ + } + +/* Macro to exchange a transformation type code for its inverse (and + vice versa), and swap the order of its 2 arguments. */ +#define SWAP_CODES2( code1, code2 ) \ + if ( cvttype[ nstep ] == code1 ) { \ + cvttype[ nstep ] = code2; \ + tmp = cvtargs[ nstep ][ 0 ]; \ + cvtargs[ nstep ][ 0 ] = cvtargs[ nstep ][ 1 ]; \ + cvtargs[ nstep ][ 1 ] = tmp; \ + AddArgs( cvttype[ nstep ], cvtargs[ nstep ], status ); \ + } else if ( cvttype[ nstep ] == code2 ) { \ + cvttype[ nstep ] = code1; \ + tmp = cvtargs[ nstep ][ 0 ]; \ + cvtargs[ nstep ][ 0 ] = cvtargs[ nstep ][ 1 ]; \ + cvtargs[ nstep ][ 1 ] = tmp; \ + AddArgs( cvttype[ nstep ], cvtargs[ nstep ], status ); \ + } + +/* Use these macros to apply the changes where needed. */ + if ( invert ) { + +/* Exchange transformation codes for their inverses. */ + SWAP_CODES( AST__TAITOUTC, AST__UTCTOTAI ) + SWAP_CODES( AST__TAITOTT, AST__TTTOTAI ) + SWAP_CODES( AST__TTTOTDB, AST__TDBTOTT ) + SWAP_CODES( AST__TDBTOTCB, AST__TCBTOTDB ) + SWAP_CODES( AST__TTTOTCG, AST__TCGTOTT ) + SWAP_CODES( AST__UTTOGMST, AST__GMSTTOUT ) + SWAP_CODES( AST__GMSTTOLMST, AST__LMSTTOGMST ) + SWAP_CODES( AST__LASTTOLMST, AST__LMSTTOLAST ) + SWAP_CODES( AST__UTTOUTC, AST__UTCTOUT ) + SWAP_CODES( AST__LTTOUTC, AST__UTCTOLT ) + +/* Exchange transformation codes for their inverses, and swap the offset + values. */ + SWAP_CODES2( AST__MJDTOMJD, AST__MJDTOMJD ) + SWAP_CODES2( AST__MJDTOJD, AST__JDTOMJD ) + SWAP_CODES2( AST__MJDTOBEP, AST__BEPTOMJD ) + SWAP_CODES2( AST__MJDTOJEP, AST__JEPTOMJD ) + + } + +/* Undefine the local macros. */ +#undef SWAP_CODES +#undef SWAP_CODES2 + +/* Count the transformation steps. */ + nstep++; + } + } + +/* Loop to simplify the sequence of transformation steps until no + further improvement is possible. */ + done = 0; + while ( astOK && !done ) { + +/* Examine each remaining transformation step in turn. */ + ikeep = -1; + for ( istep = 0; istep < nstep; istep++ ) { + +/* Initially assume we will retain the current step. */ + keep = 1; + +/* We can eliminate changes of system which have no effect. */ + if( ( cvttype[ istep ] == AST__MJDTOMJD || + cvttype[ istep ] == AST__MJDTOJD || + cvttype[ istep ] == AST__JDTOMJD ) && + cvtargs[ istep ][ 2 ] == 0.0 ) { + keep = 0; + +/* The only simplifications for the conversions currently in this class act + to combine adjacent transformation steps, so only apply them while there + are at least 2 steps left. */ + } else if ( istep < ( nstep - 1 ) ) { + +/* Define a macro to test if two adjacent transformation type codes + have specified values. */ +#define PAIR_CVT( code1, code2 ) \ + ( ( cvttype[ istep ] == code1 ) && \ + ( cvttype[ istep + 1 ] == code2 ) ) + +/* Define a macro to test if two adjacent transformation type codes + have specified values, either way round. */ +#define PAIR_CVT2( code1, code2 ) \ + ( ( PAIR_CVT( code1, code2 ) ) || \ + ( PAIR_CVT( code2, code1 ) ) ) + +/* If a correction is followed by its inverse, and the user-supplied argument + values are unchanged (we do not need to test values stored in the + argument array which were not supplied by the user), we can eliminate them. + First check for conversions which have a single user-supplied argument. */ + if( ( PAIR_CVT2( AST__TAITOUTC, AST__UTCTOTAI ) || + PAIR_CVT2( AST__TAITOTT, AST__TTTOTAI ) || + PAIR_CVT2( AST__UTTOGMST, AST__GMSTTOUT ) || + PAIR_CVT2( AST__TTTOTCG, AST__TCGTOTT ) || + PAIR_CVT2( AST__TTTOTCG, AST__TCGTOTT ) || + PAIR_CVT2( AST__UTTOUTC, AST__UTCTOUT ) || + PAIR_CVT2( AST__LTTOUTC, AST__UTCTOLT ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Now check for conversions which have two user-supplied arguments + (test they are swapped). */ + } else if( ( PAIR_CVT2( AST__MJDTOJD, AST__JDTOMJD ) || + PAIR_CVT2( AST__MJDTOMJD, AST__MJDTOMJD ) || + PAIR_CVT2( AST__MJDTOBEP, AST__BEPTOMJD ) || + PAIR_CVT2( AST__MJDTOJEP, AST__JEPTOMJD ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 0 ] ) ) { + istep++; + keep = 0; + +/* Now check for conversions which have three user-supplied arguments. */ + } else if( ( PAIR_CVT2( AST__TDBTOTCB, AST__TCBTOTDB ) || + PAIR_CVT2( AST__GMSTTOLMST, AST__LMSTTOGMST ) || + PAIR_CVT2( AST__LASTTOLMST, AST__LMSTTOLAST ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 2 ], + cvtargs[ istep + 1 ][ 2 ] ) ) { + istep++; + keep = 0; + +/* Now check for conversions which have four user-supplied arguments. */ + } else if( ( PAIR_CVT2( AST__TTTOTDB, AST__TDBTOTT ) ) && + astEQUAL( cvtargs[ istep ][ 0 ], + cvtargs[ istep + 1 ][ 0 ] ) && + astEQUAL( cvtargs[ istep ][ 1 ], + cvtargs[ istep + 1 ][ 1 ] ) && + astEQUAL( cvtargs[ istep ][ 2 ], + cvtargs[ istep + 1 ][ 2 ] ) && + astEQUAL( cvtargs[ istep ][ 3 ], + cvtargs[ istep + 1 ][ 3 ] ) ) { + istep++; + keep = 0; + } + +/* Undefine the local macros. */ +#undef PAIR_CVT +#undef PAIR_CVT2 + } + +/* If the current transformation (possibly modified above) is being + kept, then increment the index that identifies its new location in + the list of transformation steps. */ + if ( keep ) { + ikeep++; + +/* If the new location is different to its current location, copy the + transformation data into the new location. */ + if ( ikeep != istep ) { + cvttype[ ikeep ] = cvttype[ istep ]; + for ( iarg = 0; iarg < szarg[ istep ]; iarg++ ) { + cvtargs[ ikeep ][ iarg ] = cvtargs[ istep ][ iarg ]; + } + szarg[ ikeep ] = szarg[ istep ]; + } + } + } + +/* Note if no simplification was achieved on this iteration (i.e. the + number of transformation steps was not reduced). This is the signal + to quit. */ + done = ( ( ikeep + 1 ) >= nstep ); + +/* Note how many transformation steps now remain. */ + nstep = ikeep + 1; + } + +/* Determine how many Mappings can be eliminated by condensing all + those considered above into a single Mapping. */ + if ( astOK ) { + ngone = imap2 - imap1; + +/* Determine if the replacement Mapping can be a UnitMap (a null + Mapping). This will only be the case if all the transformation + steps were eliminated above. */ + unit = ( nstep == 0 ); + +/* Determine if simplification is possible. This will be the case if + (a) Mappings were eliminated ("ngone" is non-zero), or (b) the + number of transformation steps was reduced, or (c) the TimeMap(s) + can be replaced by a UnitMap, or (d) if there was initially only + one TimeMap present, its invert flag was set (this flag will always + be cleared in the replacement Mapping). */ + simpler = ngone || ( nstep < nstep0 ) || unit || + ( *invert_list )[ where ]; + +/* Do nothing more unless simplification is possible. */ + if ( simpler ) { + +/* If the replacement Mapping is a UnitMap, then create it. */ + if ( unit ) { + new = (AstMapping *) + astUnitMap( astGetNin( ( *map_list )[ where ] ), "", status ); + +/* Otherwise, create a replacement TimeMap and add each of the + remaining transformation steps to it. */ + } else { + new = (AstMapping *) astTimeMap( 0, "", status ); + for ( istep = 0; istep < nstep; istep++ ) { + AddTimeCvt( (AstTimeMap *) new, cvttype[ istep ], + cvtargs[ istep ], status ); + } + } + +/* Annul the pointers to the Mappings being eliminated. */ + if ( astOK ) { + for ( imap = imap1; imap <= imap2; imap++ ) { + ( *map_list )[ imap ] = astAnnul( ( *map_list )[ imap ] ); + } + +/* Insert the pointer and invert value for the new Mapping. */ + ( *map_list )[ imap1 ] = new; + ( *invert_list )[ imap1 ] = 0; + +/* Move any subsequent Mapping information down to close the gap. */ + for ( imap = imap2 + 1; imap < *nmap; imap++ ) { + ( *map_list )[ imap - ngone ] = ( *map_list )[ imap ]; + ( *invert_list )[ imap - ngone ] = ( *invert_list )[ imap ]; + } + +/* Blank out any information remaining at the end of the arrays. */ + for ( imap = ( *nmap - ngone ); imap < *nmap; imap++ ) { + ( *map_list )[ imap ] = NULL; + ( *invert_list )[ imap ] = 0; + } + +/* Decrement the Mapping count and return the index of the first + Mapping which was eliminated. */ + ( *nmap ) -= ngone; + result = imap1; + +/* If an error occurred, annul the new Mapping pointer. */ + } else { + new = astAnnul( new ); + } + } + } + +/* Free the memory used for the transformation steps. */ + cvttype = astFree( cvttype ); + cvtargs = astFree( cvtargs ); + szarg = astFree( szarg ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* TimeMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +* Implementation Deficiencies: +* The initial version of this implementation only deals with +* frequency->wavelength conversions. This is because the slowness of +* the numerical differentiation implemented by the astRate method in +* the parent Mapping class is cripples conversion between SpecFluxFrames. +* Such conversions only rely on rate of change of wavelength with +* respect to frequency. This implementation should be extended when +* needed. + +*/ + +/* Local Variables: */ + AstTimeMap *map; + double result; + int cvt; + int i; + +/* Check inherited status */ + if( !astOK ) return AST__BAD; + +/* Get a pointer to the TimeMap structure. */ + map = (AstTimeMap *) this; + +/* Initialise the returned value. */ + result = 1.0; + +/* Loop round each conversion. */ + for( i = 0; i < map->ncvt; i++ ) { + +/* Store the type of the current conversion.*/ + cvt = map->cvttype[ i ]; + +/* Many of the time conversions are linear. If this is the case, multiply + the total rate of change by the rate of change for this step. */ + if( cvt == AST__MJDTOBEP ) { + result *= 1.0/365.242198781; + + } else if( cvt == AST__BEPTOMJD ) { + result *= 365.242198781; + + } else if( cvt == AST__MJDTOJEP ) { + result *= 1.0/365.25; + + } else if( cvt == AST__JEPTOMJD ) { + result *= 365.25; + +/* The GMST scales is not linear, so break if we encounter it, and use the + (numerical) parent astRate method. The other time scale conversions are + assumed to have a slope of unity. In fact the slope will be ever so + slightly different to unity. */ + } else if( cvt == AST__UTTOGMST || cvt == AST__GMSTTOUT ) { + result = AST__BAD; + break; + } + } + +/* If this is non-linear TimeMap, use the astRate method inherited from the + parent Mapping class. */ + if( result == AST__BAD ) result = (*parent_rate)( this, at, ax1, ax2, status ); + +/* Return the result. */ + return result; +} + +static double Rcc( double tdb, double ut1, double wl, double u, double v, int *status ){ +/* +* Name: +* Rcc + +* Purpose: +* Find difference between TDB and TT. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* double Rcc( double tdb, double ut1, double wl, double u, double v, int *status ) + +* Class Membership: +* TimeMap member function + +* Description: +* Relativistic clock correction: the difference between proper time at +* a point on the surface of the Earth and coordinate time in the Solar +* System barycentric space-time frame of reference. +* +* The proper time is terrestrial time, TT; the coordinate time is an +* implementation of barycentric dynamical time, TDB. + +* Parameters: +* tdb +* TDB as an MJD. +* ut1 +* Universal time (only the fraction of the day is relevant) +* wl +* Observer longitude (radians west) +* u +* Observer distance from Earth spin axis (km) +* v +* Observer distance north of Earth equatorial plane (km) +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The clock correction, TDB-TT, in seconds. TDB is coordinate time in the +* solar system barycentre frame of reference, in units chosen to eliminate +* the scale difference with respect to terrestrial time. TT is the proper +* time for clocks at mean sea level on the Earth. + +* Notes: +* - This function is a translation of the fortran routine SLA_RCC +* written by by P.T.Wallace. +* +* - The argument TDB is, strictly, the barycentric coordinate time; +* however, the terrestrial time TT can in practice be used without +* any significant loss of accuracy. +* +* - The result returned by Rcc comprises a main (annual) +* sinusoidal term of amplitude approximately 0.00166 seconds, plus +* planetary and lunar terms up to about 20 microseconds, and diurnal +* terms up to 2 microseconds. The variation arises from the +* transverse Doppler effect and the gravitational red-shift as the +* observer varies in speed and moves through different gravitational +* potentials. +* +* - The geocentric model is that of Fairhead & Bretagnon (1990), in +* its full form. It was supplied by Fairhead (private +* communication) as a FORTRAN subroutine. The original Fairhead +* routine used explicit formulae, in such large numbers that +* problems were experienced with certain compilers (Microsoft +* Fortran on PC aborted with stack overflow, Convex compiled +* successfully but extremely slowly). The present implementation is +* a complete recoding, with the original Fairhead coefficients held +* in a table. To optimise arithmetic precision, the terms are +* accumulated in reverse order, smallest first. A number of other +* coding changes were made, in order to match the calling sequence +* of previous versions of the present routine, and to comply with +* Starlink programming standards. The numerical results compared +* with those from the Fairhead form are essentially unaffected by +* the changes, the differences being at the 10^-20 sec level. +* +* - The topocentric part of the model is from Moyer (1981) and +* Murray (1983). It is an approximation to the expression +* ( v / c ) . ( r / c ), where v is the barycentric velocity of +* the Earth, r is the geocentric position of the observer and +* c is the speed of light. +* +* - During the interval 1950-2050, the absolute accuracy of is better +* than +/- 3 nanoseconds relative to direct numerical integrations +* using the JPL DE200/LE200 solar system ephemeris. +* +* - The IAU definition of TDB was that it must differ from TT only by +* periodic terms. Though practical, this is an imprecise definition +* which ignores the existence of very long-period and secular +* effects in the dynamics of the solar system. As a consequence, +* different implementations of TDB will, in general, differ in zero- +* point and will drift linearly relative to one other. +* +* - TDB was, in principle, superseded by new coordinate timescales +* which the IAU introduced in 1991: geocentric coordinate time, +* TCG, and barycentric coordinate time, TCB. However, Rcc +* can be used to implement the periodic part of TCB-TCG. + +* References: +* - Fairhead, L., & Bretagnon, P., Astron.Astrophys., 229, 240-247 +* (1990). +* +* - Moyer, T.D., Cel.Mech., 23, 33 (1981). +* +* - Murray, C.A., Vectorial Astrometry, Adam Hilger (1983). +* +* - Seidelmann, P.K. et al, Explanatory Supplement to the +* Astronomical Almanac, Chapter 2, University Science Books +* (1992). +* +* - Simon J.L., Bretagnon P., Chapront J., Chapront-Touze M., +* Francou G. & Laskar J., Astron.Astrophys., 282, 663-683 (1994). +*/ + + + + + +/* ----------------------------------------------------------------------- +* +* Fairhead and Bretagnon canonical coefficients +* +* 787 sets of three coefficients. +* +* Each set is amplitude (microseconds) +* frequency (radians per Julian millennium since J2000), +* phase (radians). +* +* Sets 0-473 are the T**0 terms, +* " 474-678 " " T**1 " +* " 679-763 " " T**2 " +* " 764-783 " " T**3 " +* " 784-786 " " T**4 " . +*/ + static double fairhd[ 787 ][ 3 ] = { + + { 1656.674564E-6, 6283.075849991, 6.240054195}, + { 22.417471E-6, 5753.384884897, 4.296977442}, + { 13.839792E-6, 12566.151699983, 6.196904410}, + { 4.770086E-6, 529.690965095, 0.444401603}, + { 4.676740E-6, 6069.776754553, 4.021195093}, + { 2.256707E-6, 213.299095438, 5.543113262}, + { 1.694205E-6, -3.523118349, 5.025132748}, + { 1.554905E-6, 77713.771467920, 5.198467090}, + { 1.276839E-6, 7860.419392439, 5.988822341}, + { 1.193379E-6, 5223.693919802, 3.649823730}, + { 1.115322E-6, 3930.209696220, 1.422745069}, + { 0.794185E-6, 11506.769769794, 2.322313077}, + { 0.447061E-6, 26.298319800, 3.615796498}, + { 0.435206E-6, -398.149003408, 4.349338347}, + { 0.600309E-6, 1577.343542448, 2.678271909}, + { 0.496817E-6, 6208.294251424, 5.696701824}, + { 0.486306E-6, 5884.926846583, 0.520007179}, + { 0.432392E-6, 74.781598567, 2.435898309}, + { 0.468597E-6, 6244.942814354, 5.866398759}, + { 0.375510E-6, 5507.553238667, 4.103476804}, + { 0.243085E-6, -775.522611324, 3.651837925}, + { 0.173435E-6, 18849.227549974, 6.153743485}, + { 0.230685E-6, 5856.477659115, 4.773852582}, + { 0.203747E-6, 12036.460734888, 4.333987818}, + { 0.143935E-6, -796.298006816, 5.957517795}, + { 0.159080E-6, 10977.078804699, 1.890075226}, + { 0.119979E-6, 38.133035638, 4.551585768}, + { 0.118971E-6, 5486.777843175, 1.914547226}, + { 0.116120E-6, 1059.381930189, 0.873504123}, + { 0.137927E-6, 11790.629088659, 1.135934669}, + { 0.098358E-6, 2544.314419883, 0.092793886}, + { 0.101868E-6, -5573.142801634, 5.984503847}, + { 0.080164E-6, 206.185548437, 2.095377709}, + { 0.079645E-6, 4694.002954708, 2.949233637}, + { 0.062617E-6, 20.775395492, 2.654394814}, + { 0.075019E-6, 2942.463423292, 4.980931759}, + { 0.064397E-6, 5746.271337896, 1.280308748}, + { 0.063814E-6, 5760.498431898, 4.167901731}, + { 0.048042E-6, 2146.165416475, 1.495846011}, + { 0.048373E-6, 155.420399434, 2.251573730}, + { 0.058844E-6, 426.598190876, 4.839650148}, + { 0.046551E-6, -0.980321068, 0.921573539}, + { 0.054139E-6, 17260.154654690, 3.411091093}, + { 0.042411E-6, 6275.962302991, 2.869567043}, + { 0.040184E-6, -7.113547001, 3.565975565}, + { 0.036564E-6, 5088.628839767, 3.324679049}, + { 0.040759E-6, 12352.852604545, 3.981496998}, + { 0.036507E-6, 801.820931124, 6.248866009}, + { 0.036955E-6, 3154.687084896, 5.071801441}, + { 0.042732E-6, 632.783739313, 5.720622217}, + { 0.042560E-6, 161000.685737473, 1.270837679}, + { 0.040480E-6, 15720.838784878, 2.546610123}, + { 0.028244E-6, -6286.598968340, 5.069663519}, + { 0.033477E-6, 6062.663207553, 4.144987272}, + { 0.034867E-6, 522.577418094, 5.210064075}, + { 0.032438E-6, 6076.890301554, 0.749317412}, + { 0.030215E-6, 7084.896781115, 3.389610345}, + { 0.029247E-6, -71430.695617928, 4.183178762}, + { 0.033529E-6, 9437.762934887, 2.404714239}, + { 0.032423E-6, 8827.390269875, 5.541473556}, + { 0.027567E-6, 6279.552731642, 5.040846034}, + { 0.029862E-6, 12139.553509107, 1.770181024}, + { 0.022509E-6, 10447.387839604, 1.460726241}, + { 0.020937E-6, 8429.241266467, 0.652303414}, + { 0.020322E-6, 419.484643875, 3.735430632}, + { 0.024816E-6, -1194.447010225, 1.087136918}, + { 0.025196E-6, 1748.016413067, 2.901883301}, + { 0.021691E-6, 14143.495242431, 5.952658009}, + { 0.017673E-6, 6812.766815086, 3.186129845}, + { 0.022567E-6, 6133.512652857, 3.307984806}, + { 0.016155E-6, 10213.285546211, 1.331103168}, + { 0.014751E-6, 1349.867409659, 4.308933301}, + { 0.015949E-6, -220.412642439, 4.005298270}, + { 0.015974E-6, -2352.866153772, 6.145309371}, + { 0.014223E-6, 17789.845619785, 2.104551349}, + { 0.017806E-6, 73.297125859, 3.475975097}, + { 0.013671E-6, -536.804512095, 5.971672571}, + { 0.011942E-6, 8031.092263058, 2.053414715}, + { 0.014318E-6, 16730.463689596, 3.016058075}, + { 0.012462E-6, 103.092774219, 1.737438797}, + { 0.010962E-6, 3.590428652, 2.196567739}, + { 0.015078E-6, 19651.048481098, 3.969480770}, + { 0.010396E-6, 951.718406251, 5.717799605}, + { 0.011707E-6, -4705.732307544, 2.654125618}, + { 0.010453E-6, 5863.591206116, 1.913704550}, + { 0.012420E-6, 4690.479836359, 4.734090399}, + { 0.011847E-6, 5643.178563677, 5.489005403}, + { 0.008610E-6, 3340.612426700, 3.661698944}, + { 0.011622E-6, 5120.601145584, 4.863931876}, + { 0.010825E-6, 553.569402842, 0.842715011}, + { 0.008666E-6, -135.065080035, 3.293406547}, + { 0.009963E-6, 149.563197135, 4.870690598}, + { 0.009858E-6, 6309.374169791, 1.061816410}, + { 0.007959E-6, 316.391869657, 2.465042647}, + { 0.010099E-6, 283.859318865, 1.942176992}, + { 0.007147E-6, -242.728603974, 3.661486981}, + { 0.007505E-6, 5230.807466803, 4.920937029}, + { 0.008323E-6, 11769.853693166, 1.229392026}, + { 0.007490E-6, -6256.777530192, 3.658444681}, + { 0.009370E-6, 149854.400134205, 0.673880395}, + { 0.007117E-6, 38.027672636, 5.294249518}, + { 0.007857E-6, 12168.002696575, 0.525733528}, + { 0.007019E-6, 6206.809778716, 0.837688810}, + { 0.006056E-6, 955.599741609, 4.194535082}, + { 0.008107E-6, 13367.972631107, 3.793235253}, + { 0.006731E-6, 5650.292110678, 5.639906583}, + { 0.007332E-6, 36.648562930, 0.114858677}, + { 0.006366E-6, 4164.311989613, 2.262081818}, + { 0.006858E-6, 5216.580372801, 0.642063318}, + { 0.006919E-6, 6681.224853400, 6.018501522}, + { 0.006826E-6, 7632.943259650, 3.458654112}, + { 0.005308E-6, -1592.596013633, 2.500382359}, + { 0.005096E-6, 11371.704689758, 2.547107806}, + { 0.004841E-6, 5333.900241022, 0.437078094}, + { 0.005582E-6, 5966.683980335, 2.246174308}, + { 0.006304E-6, 11926.254413669, 2.512929171}, + { 0.006603E-6, 23581.258177318, 5.393136889}, + { 0.005123E-6, -1.484472708, 2.999641028}, + { 0.004648E-6, 1589.072895284, 1.275847090}, + { 0.005119E-6, 6438.496249426, 1.486539246}, + { 0.004521E-6, 4292.330832950, 6.140635794}, + { 0.005680E-6, 23013.539539587, 4.557814849}, + { 0.005488E-6, -3.455808046, 0.090675389}, + { 0.004193E-6, 7234.794256242, 4.869091389}, + { 0.003742E-6, 7238.675591600, 4.691976180}, + { 0.004148E-6, -110.206321219, 3.016173439}, + { 0.004553E-6, 11499.656222793, 5.554998314}, + { 0.004892E-6, 5436.993015240, 1.475415597}, + { 0.004044E-6, 4732.030627343, 1.398784824}, + { 0.004164E-6, 12491.370101415, 5.650931916}, + { 0.004349E-6, 11513.883316794, 2.181745369}, + { 0.003919E-6, 12528.018664345, 5.823319737}, + { 0.003129E-6, 6836.645252834, 0.003844094}, + { 0.004080E-6, -7058.598461315, 3.690360123}, + { 0.003270E-6, 76.266071276, 1.517189902}, + { 0.002954E-6, 6283.143160294, 4.447203799}, + { 0.002872E-6, 28.449187468, 1.158692983}, + { 0.002881E-6, 735.876513532, 0.349250250}, + { 0.003279E-6, 5849.364112115, 4.893384368}, + { 0.003625E-6, 6209.778724132, 1.473760578}, + { 0.003074E-6, 949.175608970, 5.185878737}, + { 0.002775E-6, 9917.696874510, 1.030026325}, + { 0.002646E-6, 10973.555686350, 3.918259169}, + { 0.002575E-6, 25132.303399966, 6.109659023}, + { 0.003500E-6, 263.083923373, 1.892100742}, + { 0.002740E-6, 18319.536584880, 4.320519510}, + { 0.002464E-6, 202.253395174, 4.698203059}, + { 0.002409E-6, 2.542797281, 5.325009315}, + { 0.003354E-6, -90955.551694697, 1.942656623}, + { 0.002296E-6, 6496.374945429, 5.061810696}, + { 0.003002E-6, 6172.869528772, 2.797822767}, + { 0.003202E-6, 27511.467873537, 0.531673101}, + { 0.002954E-6, -6283.008539689, 4.533471191}, + { 0.002353E-6, 639.897286314, 3.734548088}, + { 0.002401E-6, 16200.772724501, 2.605547070}, + { 0.003053E-6, 233141.314403759, 3.029030662}, + { 0.003024E-6, 83286.914269554, 2.355556099}, + { 0.002863E-6, 17298.182327326, 5.240963796}, + { 0.002103E-6, -7079.373856808, 5.756641637}, + { 0.002303E-6, 83996.847317911, 2.013686814}, + { 0.002303E-6, 18073.704938650, 1.089100410}, + { 0.002381E-6, 63.735898303, 0.759188178}, + { 0.002493E-6, 6386.168624210, 0.645026535}, + { 0.002366E-6, 3.932153263, 6.215885448}, + { 0.002169E-6, 11015.106477335, 4.845297676}, + { 0.002397E-6, 6243.458341645, 3.809290043}, + { 0.002183E-6, 1162.474704408, 6.179611691}, + { 0.002353E-6, 6246.427287062, 4.781719760}, + { 0.002199E-6, -245.831646229, 5.956152284}, + { 0.001729E-6, 3894.181829542, 1.264976635}, + { 0.001896E-6, -3128.388765096, 4.914231596}, + { 0.002085E-6, 35.164090221, 1.405158503}, + { 0.002024E-6, 14712.317116458, 2.752035928}, + { 0.001737E-6, 6290.189396992, 5.280820144}, + { 0.002229E-6, 491.557929457, 1.571007057}, + { 0.001602E-6, 14314.168113050, 4.203664806}, + { 0.002186E-6, 454.909366527, 1.402101526}, + { 0.001897E-6, 22483.848574493, 4.167932508}, + { 0.001825E-6, -3738.761430108, 0.545828785}, + { 0.001894E-6, 1052.268383188, 5.817167450}, + { 0.001421E-6, 20.355319399, 2.419886601}, + { 0.001408E-6, 10984.192351700, 2.732084787}, + { 0.001847E-6, 10873.986030480, 2.903477885}, + { 0.001391E-6, -8635.942003763, 0.593891500}, + { 0.001388E-6, -7.046236698, 1.166145902}, + { 0.001810E-6, -88860.057071188, 0.487355242}, + { 0.001288E-6, -1990.745017041, 3.913022880}, + { 0.001297E-6, 23543.230504682, 3.063805171}, + { 0.001335E-6, -266.607041722, 3.995764039}, + { 0.001376E-6, 10969.965257698, 5.152914309}, + { 0.001745E-6, 244287.600007027, 3.626395673}, + { 0.001649E-6, 31441.677569757, 1.952049260}, + { 0.001416E-6, 9225.539273283, 4.996408389}, + { 0.001238E-6, 4804.209275927, 5.503379738}, + { 0.001472E-6, 4590.910180489, 4.164913291}, + { 0.001169E-6, 6040.347246017, 5.841719038}, + { 0.001039E-6, 5540.085789459, 2.769753519}, + { 0.001004E-6, -170.672870619, 0.755008103}, + { 0.001284E-6, 10575.406682942, 5.306538209}, + { 0.001278E-6, 71.812653151, 4.713486491}, + { 0.001321E-6, 18209.330263660, 2.624866359}, + { 0.001297E-6, 21228.392023546, 0.382603541}, + { 0.000954E-6, 6282.095528923, 0.882213514}, + { 0.001145E-6, 6058.731054289, 1.169483931}, + { 0.000979E-6, 5547.199336460, 5.448375984}, + { 0.000987E-6, -6262.300454499, 2.656486959}, + { 0.001070E-6, -154717.609887482, 1.827624012}, + { 0.000991E-6, 4701.116501708, 4.387001801}, + { 0.001155E-6, -14.227094002, 3.042700750}, + { 0.001176E-6, 277.034993741, 3.335519004}, + { 0.000890E-6, 13916.019109642, 5.601498297}, + { 0.000884E-6, -1551.045222648, 1.088831705}, + { 0.000876E-6, 5017.508371365, 3.969902609}, + { 0.000806E-6, 15110.466119866, 5.142876744}, + { 0.000773E-6, -4136.910433516, 0.022067765}, + { 0.001077E-6, 175.166059800, 1.844913056}, + { 0.000954E-6, -6284.056171060, 0.968480906}, + { 0.000737E-6, 5326.786694021, 4.923831588}, + { 0.000845E-6, -433.711737877, 4.749245231}, + { 0.000819E-6, 8662.240323563, 5.991247817}, + { 0.000852E-6, 199.072001436, 2.189604979}, + { 0.000723E-6, 17256.631536341, 6.068719637}, + { 0.000940E-6, 6037.244203762, 6.197428148}, + { 0.000885E-6, 11712.955318231, 3.280414875}, + { 0.000706E-6, 12559.038152982, 2.824848947}, + { 0.000732E-6, 2379.164473572, 2.501813417}, + { 0.000764E-6, -6127.655450557, 2.236346329}, + { 0.000908E-6, 131.541961686, 2.521257490}, + { 0.000907E-6, 35371.887265976, 3.370195967}, + { 0.000673E-6, 1066.495477190, 3.876512374}, + { 0.000814E-6, 17654.780539750, 4.627122566}, + { 0.000630E-6, 36.027866677, 0.156368499}, + { 0.000798E-6, 515.463871093, 5.151962502}, + { 0.000798E-6, 148.078724426, 5.909225055}, + { 0.000806E-6, 309.278322656, 6.054064447}, + { 0.000607E-6, -39.617508346, 2.839021623}, + { 0.000601E-6, 412.371096874, 3.984225404}, + { 0.000646E-6, 11403.676995575, 3.852959484}, + { 0.000704E-6, 13521.751441591, 2.300991267}, + { 0.000603E-6, -65147.619767937, 4.140083146}, + { 0.000609E-6, 10177.257679534, 0.437122327}, + { 0.000631E-6, 5767.611978898, 4.026532329}, + { 0.000576E-6, 11087.285125918, 4.760293101}, + { 0.000674E-6, 14945.316173554, 6.270510511}, + { 0.000726E-6, 5429.879468239, 6.039606892}, + { 0.000710E-6, 28766.924424484, 5.672617711}, + { 0.000647E-6, 11856.218651625, 3.397132627}, + { 0.000678E-6, -5481.254918868, 6.249666675}, + { 0.000618E-6, 22003.914634870, 2.466427018}, + { 0.000738E-6, 6134.997125565, 2.242668890}, + { 0.000660E-6, 625.670192312, 5.864091907}, + { 0.000694E-6, 3496.032826134, 2.668309141}, + { 0.000531E-6, 6489.261398429, 1.681888780}, + { 0.000611E-6, -143571.324284214, 2.424978312}, + { 0.000575E-6, 12043.574281889, 4.216492400}, + { 0.000553E-6, 12416.588502848, 4.772158039}, + { 0.000689E-6, 4686.889407707, 6.224271088}, + { 0.000495E-6, 7342.457780181, 3.817285811}, + { 0.000567E-6, 3634.621024518, 1.649264690}, + { 0.000515E-6, 18635.928454536, 3.945345892}, + { 0.000486E-6, -323.505416657, 4.061673868}, + { 0.000662E-6, 25158.601719765, 1.794058369}, + { 0.000509E-6, 846.082834751, 3.053874588}, + { 0.000472E-6, -12569.674818332, 5.112133338}, + { 0.000461E-6, 6179.983075773, 0.513669325}, + { 0.000641E-6, 83467.156352816, 3.210727723}, + { 0.000520E-6, 10344.295065386, 2.445597761}, + { 0.000493E-6, 18422.629359098, 1.676939306}, + { 0.000478E-6, 1265.567478626, 5.487314569}, + { 0.000472E-6, -18.159247265, 1.999707589}, + { 0.000559E-6, 11190.377900137, 5.783236356}, + { 0.000494E-6, 9623.688276691, 3.022645053}, + { 0.000463E-6, 5739.157790895, 1.411223013}, + { 0.000432E-6, 16858.482532933, 1.179256434}, + { 0.000574E-6, 72140.628666286, 1.758191830}, + { 0.000484E-6, 17267.268201691, 3.290589143}, + { 0.000550E-6, 4907.302050146, 0.864024298}, + { 0.000399E-6, 14.977853527, 2.094441910}, + { 0.000491E-6, 224.344795702, 0.878372791}, + { 0.000432E-6, 20426.571092422, 6.003829241}, + { 0.000481E-6, 5749.452731634, 4.309591964}, + { 0.000480E-6, 5757.317038160, 1.142348571}, + { 0.000485E-6, 6702.560493867, 0.210580917}, + { 0.000426E-6, 6055.549660552, 4.274476529}, + { 0.000480E-6, 5959.570433334, 5.031351030}, + { 0.000466E-6, 12562.628581634, 4.959581597}, + { 0.000520E-6, 39302.096962196, 4.788002889}, + { 0.000458E-6, 12132.439962106, 1.880103788}, + { 0.000470E-6, 12029.347187887, 1.405611197}, + { 0.000416E-6, -7477.522860216, 1.082356330}, + { 0.000449E-6, 11609.862544012, 4.179989585}, + { 0.000465E-6, 17253.041107690, 0.353496295}, + { 0.000362E-6, -4535.059436924, 1.583849576}, + { 0.000383E-6, 21954.157609398, 3.747376371}, + { 0.000389E-6, 17.252277143, 1.395753179}, + { 0.000331E-6, 18052.929543158, 0.566790582}, + { 0.000430E-6, 13517.870106233, 0.685827538}, + { 0.000368E-6, -5756.908003246, 0.731374317}, + { 0.000330E-6, 10557.594160824, 3.710043680}, + { 0.000332E-6, 20199.094959633, 1.652901407}, + { 0.000384E-6, 11933.367960670, 5.827781531}, + { 0.000387E-6, 10454.501386605, 2.541182564}, + { 0.000325E-6, 15671.081759407, 2.178850542}, + { 0.000318E-6, 138.517496871, 2.253253037}, + { 0.000305E-6, 9388.005909415, 0.578340206}, + { 0.000352E-6, 5749.861766548, 3.000297967}, + { 0.000311E-6, 6915.859589305, 1.693574249}, + { 0.000297E-6, 24072.921469776, 1.997249392}, + { 0.000363E-6, -640.877607382, 5.071820966}, + { 0.000323E-6, 12592.450019783, 1.072262823}, + { 0.000341E-6, 12146.667056108, 4.700657997}, + { 0.000290E-6, 9779.108676125, 1.812320441}, + { 0.000342E-6, 6132.028180148, 4.322238614}, + { 0.000329E-6, 6268.848755990, 3.033827743}, + { 0.000374E-6, 17996.031168222, 3.388716544}, + { 0.000285E-6, -533.214083444, 4.687313233}, + { 0.000338E-6, 6065.844601290, 0.877776108}, + { 0.000276E-6, 24.298513841, 0.770299429}, + { 0.000336E-6, -2388.894020449, 5.353796034}, + { 0.000290E-6, 3097.883822726, 4.075291557}, + { 0.000318E-6, 709.933048357, 5.941207518}, + { 0.000271E-6, 13095.842665077, 3.208912203}, + { 0.000331E-6, 6073.708907816, 4.007881169}, + { 0.000292E-6, 742.990060533, 2.714333592}, + { 0.000362E-6, 29088.811415985, 3.215977013}, + { 0.000280E-6, 12359.966151546, 0.710872502}, + { 0.000267E-6, 10440.274292604, 4.730108488}, + { 0.000262E-6, 838.969287750, 1.327720272}, + { 0.000250E-6, 16496.361396202, 0.898769761}, + { 0.000325E-6, 20597.243963041, 0.180044365}, + { 0.000268E-6, 6148.010769956, 5.152666276}, + { 0.000284E-6, 5636.065016677, 5.655385808}, + { 0.000301E-6, 6080.822454817, 2.135396205}, + { 0.000294E-6, -377.373607916, 3.708784168}, + { 0.000236E-6, 2118.763860378, 1.733578756}, + { 0.000234E-6, 5867.523359379, 5.575209112}, + { 0.000268E-6, -226858.238553767, 0.069432392}, + { 0.000265E-6, 167283.761587465, 4.369302826}, + { 0.000280E-6, 28237.233459389, 5.304829118}, + { 0.000292E-6, 12345.739057544, 4.096094132}, + { 0.000223E-6, 19800.945956225, 3.069327406}, + { 0.000301E-6, 43232.306658416, 6.205311188}, + { 0.000264E-6, 18875.525869774, 1.417263408}, + { 0.000304E-6, -1823.175188677, 3.409035232}, + { 0.000301E-6, 109.945688789, 0.510922054}, + { 0.000260E-6, 813.550283960, 2.389438934}, + { 0.000299E-6, 316428.228673312, 5.384595078}, + { 0.000211E-6, 5756.566278634, 3.789392838}, + { 0.000209E-6, 5750.203491159, 1.661943545}, + { 0.000240E-6, 12489.885628707, 5.684549045}, + { 0.000216E-6, 6303.851245484, 3.862942261}, + { 0.000203E-6, 1581.959348283, 5.549853589}, + { 0.000200E-6, 5642.198242609, 1.016115785}, + { 0.000197E-6, -70.849445304, 4.690702525}, + { 0.000227E-6, 6287.008003254, 2.911891613}, + { 0.000197E-6, 533.623118358, 1.048982898}, + { 0.000205E-6, -6279.485421340, 1.829362730}, + { 0.000209E-6, -10988.808157535, 2.636140084}, + { 0.000208E-6, -227.526189440, 4.127883842}, + { 0.000191E-6, 415.552490612, 4.401165650}, + { 0.000190E-6, 29296.615389579, 4.175658539}, + { 0.000264E-6, 66567.485864652, 4.601102551}, + { 0.000256E-6, -3646.350377354, 0.506364778}, + { 0.000188E-6, 13119.721102825, 2.032195842}, + { 0.000185E-6, -209.366942175, 4.694756586}, + { 0.000198E-6, 25934.124331089, 3.832703118}, + { 0.000195E-6, 4061.219215394, 3.308463427}, + { 0.000234E-6, 5113.487598583, 1.716090661}, + { 0.000188E-6, 1478.866574064, 5.686865780}, + { 0.000222E-6, 11823.161639450, 1.942386641}, + { 0.000181E-6, 10770.893256262, 1.999482059}, + { 0.000171E-6, 6546.159773364, 1.182807992}, + { 0.000206E-6, 70.328180442, 5.934076062}, + { 0.000169E-6, 20995.392966449, 2.169080622}, + { 0.000191E-6, 10660.686935042, 5.405515999}, + { 0.000228E-6, 33019.021112205, 4.656985514}, + { 0.000184E-6, -4933.208440333, 3.327476868}, + { 0.000220E-6, -135.625325010, 1.765430262}, + { 0.000166E-6, 23141.558382925, 3.454132746}, + { 0.000191E-6, 6144.558353121, 5.020393445}, + { 0.000180E-6, 6084.003848555, 0.602182191}, + { 0.000163E-6, 17782.732072784, 4.960593133}, + { 0.000225E-6, 16460.333529525, 2.596451817}, + { 0.000222E-6, 5905.702242076, 3.731990323}, + { 0.000204E-6, 227.476132789, 5.636192701}, + { 0.000159E-6, 16737.577236597, 3.600691544}, + { 0.000200E-6, 6805.653268085, 0.868220961}, + { 0.000187E-6, 11919.140866668, 2.629456641}, + { 0.000161E-6, 127.471796607, 2.862574720}, + { 0.000205E-6, 6286.666278643, 1.742882331}, + { 0.000189E-6, 153.778810485, 4.812372643}, + { 0.000168E-6, 16723.350142595, 0.027860588}, + { 0.000149E-6, 11720.068865232, 0.659721876}, + { 0.000189E-6, 5237.921013804, 5.245313000}, + { 0.000143E-6, 6709.674040867, 4.317625647}, + { 0.000146E-6, 4487.817406270, 4.815297007}, + { 0.000144E-6, -664.756045130, 5.381366880}, + { 0.000175E-6, 5127.714692584, 4.728443327}, + { 0.000162E-6, 6254.626662524, 1.435132069}, + { 0.000187E-6, 47162.516354635, 1.354371923}, + { 0.000146E-6, 11080.171578918, 3.369695406}, + { 0.000180E-6, -348.924420448, 2.490902145}, + { 0.000148E-6, 151.047669843, 3.799109588}, + { 0.000157E-6, 6197.248551160, 1.284375887}, + { 0.000167E-6, 146.594251718, 0.759969109}, + { 0.000133E-6, -5331.357443741, 5.409701889}, + { 0.000154E-6, 95.979227218, 3.366890614}, + { 0.000148E-6, -6418.140930027, 3.384104996}, + { 0.000128E-6, -6525.804453965, 3.803419985}, + { 0.000130E-6, 11293.470674356, 0.939039445}, + { 0.000152E-6, -5729.506447149, 0.734117523}, + { 0.000138E-6, 210.117701700, 2.564216078}, + { 0.000123E-6, 6066.595360816, 4.517099537}, + { 0.000140E-6, 18451.078546566, 0.642049130}, + { 0.000126E-6, 11300.584221356, 3.485280663}, + { 0.000119E-6, 10027.903195729, 3.217431161}, + { 0.000151E-6, 4274.518310832, 4.404359108}, + { 0.000117E-6, 6072.958148291, 0.366324650}, + { 0.000165E-6, -7668.637425143, 4.298212528}, + { 0.000117E-6, -6245.048177356, 5.379518958}, + { 0.000130E-6, -5888.449964932, 4.527681115}, + { 0.000121E-6, -543.918059096, 6.109429504}, + { 0.000162E-6, 9683.594581116, 5.720092446}, + { 0.000141E-6, 6219.339951688, 0.679068671}, + { 0.000118E-6, 22743.409379516, 4.881123092}, + { 0.000129E-6, 1692.165669502, 0.351407289}, + { 0.000126E-6, 5657.405657679, 5.146592349}, + { 0.000114E-6, 728.762966531, 0.520791814}, + { 0.000120E-6, 52.596639600, 0.948516300}, + { 0.000115E-6, 65.220371012, 3.504914846}, + { 0.000126E-6, 5881.403728234, 5.577502482}, + { 0.000158E-6, 163096.180360983, 2.957128968}, + { 0.000134E-6, 12341.806904281, 2.598576764}, + { 0.000151E-6, 16627.370915377, 3.985702050}, + { 0.000109E-6, 1368.660252845, 0.014730471}, + { 0.000131E-6, 6211.263196841, 0.085077024}, + { 0.000146E-6, 5792.741760812, 0.708426604}, + { 0.000146E-6, -77.750543984, 3.121576600}, + { 0.000107E-6, 5341.013788022, 0.288231904}, + { 0.000138E-6, 6281.591377283, 2.797450317}, + { 0.000113E-6, -6277.552925684, 2.788904128}, + { 0.000115E-6, -525.758811831, 5.895222200}, + { 0.000138E-6, 6016.468808270, 6.096188999}, + { 0.000139E-6, 23539.707386333, 2.028195445}, + { 0.000146E-6, -4176.041342449, 4.660008502}, + { 0.000107E-6, 16062.184526117, 4.066520001}, + { 0.000142E-6, 83783.548222473, 2.936315115}, + { 0.000128E-6, 9380.959672717, 3.223844306}, + { 0.000135E-6, 6205.325306007, 1.638054048}, + { 0.000101E-6, 2699.734819318, 5.481603249}, + { 0.000104E-6, -568.821874027, 2.205734493}, + { 0.000103E-6, 6321.103522627, 2.440421099}, + { 0.000119E-6, 6321.208885629, 2.547496264}, + { 0.000138E-6, 1975.492545856, 2.314608466}, + { 0.000121E-6, 137.033024162, 4.539108237}, + { 0.000123E-6, 19402.796952817, 4.538074405}, + { 0.000119E-6, 22805.735565994, 2.869040566}, + { 0.000133E-6, 64471.991241142, 6.056405489}, + { 0.000129E-6, -85.827298831, 2.540635083}, + { 0.000131E-6, 13613.804277336, 4.005732868}, + { 0.000104E-6, 9814.604100291, 1.959967212}, + { 0.000112E-6, 16097.679950283, 3.589026260}, + { 0.000123E-6, 2107.034507542, 1.728627253}, + { 0.000121E-6, 36949.230808424, 6.072332087}, + { 0.000108E-6, -12539.853380183, 3.716133846}, + { 0.000113E-6, -7875.671863624, 2.725771122}, + { 0.000109E-6, 4171.425536614, 4.033338079}, + { 0.000101E-6, 6247.911759770, 3.441347021}, + { 0.000113E-6, 7330.728427345, 0.656372122}, + { 0.000113E-6, 51092.726050855, 2.791483066}, + { 0.000106E-6, 5621.842923210, 1.815323326}, + { 0.000101E-6, 111.430161497, 5.711033677}, + { 0.000103E-6, 909.818733055, 2.812745443}, + { 0.000101E-6, 1790.642637886, 1.965746028}, + { 102.156724E-6, 6283.075849991, 4.249032005}, + { 1.706807E-6, 12566.151699983, 4.205904248}, + { 0.269668E-6, 213.299095438, 3.400290479}, + { 0.265919E-6, 529.690965095, 5.836047367}, + { 0.210568E-6, -3.523118349, 6.262738348}, + { 0.077996E-6, 5223.693919802, 4.670344204}, + { 0.054764E-6, 1577.343542448, 4.534800170}, + { 0.059146E-6, 26.298319800, 1.083044735}, + { 0.034420E-6, -398.149003408, 5.980077351}, + { 0.032088E-6, 18849.227549974, 4.162913471}, + { 0.033595E-6, 5507.553238667, 5.980162321}, + { 0.029198E-6, 5856.477659115, 0.623811863}, + { 0.027764E-6, 155.420399434, 3.745318113}, + { 0.025190E-6, 5746.271337896, 2.980330535}, + { 0.022997E-6, -796.298006816, 1.174411803}, + { 0.024976E-6, 5760.498431898, 2.467913690}, + { 0.021774E-6, 206.185548437, 3.854787540}, + { 0.017925E-6, -775.522611324, 1.092065955}, + { 0.013794E-6, 426.598190876, 2.699831988}, + { 0.013276E-6, 6062.663207553, 5.845801920}, + { 0.011774E-6, 12036.460734888, 2.292832062}, + { 0.012869E-6, 6076.890301554, 5.333425680}, + { 0.012152E-6, 1059.381930189, 6.222874454}, + { 0.011081E-6, -7.113547001, 5.154724984}, + { 0.010143E-6, 4694.002954708, 4.044013795}, + { 0.009357E-6, 5486.777843175, 3.416081409}, + { 0.010084E-6, 522.577418094, 0.749320262}, + { 0.008587E-6, 10977.078804699, 2.777152598}, + { 0.008628E-6, 6275.962302991, 4.562060226}, + { 0.008158E-6, -220.412642439, 5.806891533}, + { 0.007746E-6, 2544.314419883, 1.603197066}, + { 0.007670E-6, 2146.165416475, 3.000200440}, + { 0.007098E-6, 74.781598567, 0.443725817}, + { 0.006180E-6, -536.804512095, 1.302642751}, + { 0.005818E-6, 5088.628839767, 4.827723531}, + { 0.004945E-6, -6286.598968340, 0.268305170}, + { 0.004774E-6, 1349.867409659, 5.808636673}, + { 0.004687E-6, -242.728603974, 5.154890570}, + { 0.006089E-6, 1748.016413067, 4.403765209}, + { 0.005975E-6, -1194.447010225, 2.583472591}, + { 0.004229E-6, 951.718406251, 0.931172179}, + { 0.005264E-6, 553.569402842, 2.336107252}, + { 0.003049E-6, 5643.178563677, 1.362634430}, + { 0.002974E-6, 6812.766815086, 1.583012668}, + { 0.003403E-6, -2352.866153772, 2.552189886}, + { 0.003030E-6, 419.484643875, 5.286473844}, + { 0.003210E-6, -7.046236698, 1.863796539}, + { 0.003058E-6, 9437.762934887, 4.226420633}, + { 0.002589E-6, 12352.852604545, 1.991935820}, + { 0.002927E-6, 5216.580372801, 2.319951253}, + { 0.002425E-6, 5230.807466803, 3.084752833}, + { 0.002656E-6, 3154.687084896, 2.487447866}, + { 0.002445E-6, 10447.387839604, 2.347139160}, + { 0.002990E-6, 4690.479836359, 6.235872050}, + { 0.002890E-6, 5863.591206116, 0.095197563}, + { 0.002498E-6, 6438.496249426, 2.994779800}, + { 0.001889E-6, 8031.092263058, 3.569003717}, + { 0.002567E-6, 801.820931124, 3.425611498}, + { 0.001803E-6, -71430.695617928, 2.192295512}, + { 0.001782E-6, 3.932153263, 5.180433689}, + { 0.001694E-6, -4705.732307544, 4.641779174}, + { 0.001704E-6, -1592.596013633, 3.997097652}, + { 0.001735E-6, 5849.364112115, 0.417558428}, + { 0.001643E-6, 8429.241266467, 2.180619584}, + { 0.001680E-6, 38.133035638, 4.164529426}, + { 0.002045E-6, 7084.896781115, 0.526323854}, + { 0.001458E-6, 4292.330832950, 1.356098141}, + { 0.001437E-6, 20.355319399, 3.895439360}, + { 0.001738E-6, 6279.552731642, 0.087484036}, + { 0.001367E-6, 14143.495242431, 3.987576591}, + { 0.001344E-6, 7234.794256242, 0.090454338}, + { 0.001438E-6, 11499.656222793, 0.974387904}, + { 0.001257E-6, 6836.645252834, 1.509069366}, + { 0.001358E-6, 11513.883316794, 0.495572260}, + { 0.001628E-6, 7632.943259650, 4.968445721}, + { 0.001169E-6, 103.092774219, 2.838496795}, + { 0.001162E-6, 4164.311989613, 3.408387778}, + { 0.001092E-6, 6069.776754553, 3.617942651}, + { 0.001008E-6, 17789.845619785, 0.286350174}, + { 0.001008E-6, 639.897286314, 1.610762073}, + { 0.000918E-6, 10213.285546211, 5.532798067}, + { 0.001011E-6, -6256.777530192, 0.661826484}, + { 0.000753E-6, 16730.463689596, 3.905030235}, + { 0.000737E-6, 11926.254413669, 4.641956361}, + { 0.000694E-6, 3340.612426700, 2.111120332}, + { 0.000701E-6, 3894.181829542, 2.760823491}, + { 0.000689E-6, -135.065080035, 4.768800780}, + { 0.000700E-6, 13367.972631107, 5.760439898}, + { 0.000664E-6, 6040.347246017, 1.051215840}, + { 0.000654E-6, 5650.292110678, 4.911332503}, + { 0.000788E-6, 6681.224853400, 4.699648011}, + { 0.000628E-6, 5333.900241022, 5.024608847}, + { 0.000755E-6, -110.206321219, 4.370971253}, + { 0.000628E-6, 6290.189396992, 3.660478857}, + { 0.000635E-6, 25132.303399966, 4.121051532}, + { 0.000534E-6, 5966.683980335, 1.173284524}, + { 0.000543E-6, -433.711737877, 0.345585464}, + { 0.000517E-6, -1990.745017041, 5.414571768}, + { 0.000504E-6, 5767.611978898, 2.328281115}, + { 0.000485E-6, 5753.384884897, 1.685874771}, + { 0.000463E-6, 7860.419392439, 5.297703006}, + { 0.000604E-6, 515.463871093, 0.591998446}, + { 0.000443E-6, 12168.002696575, 4.830881244}, + { 0.000570E-6, 199.072001436, 3.899190272}, + { 0.000465E-6, 10969.965257698, 0.476681802}, + { 0.000424E-6, -7079.373856808, 1.112242763}, + { 0.000427E-6, 735.876513532, 1.994214480}, + { 0.000478E-6, -6127.655450557, 3.778025483}, + { 0.000414E-6, 10973.555686350, 5.441088327}, + { 0.000512E-6, 1589.072895284, 0.107123853}, + { 0.000378E-6, 10984.192351700, 0.915087231}, + { 0.000402E-6, 11371.704689758, 4.107281715}, + { 0.000453E-6, 9917.696874510, 1.917490952}, + { 0.000395E-6, 149.563197135, 2.763124165}, + { 0.000371E-6, 5739.157790895, 3.112111866}, + { 0.000350E-6, 11790.629088659, 0.440639857}, + { 0.000356E-6, 6133.512652857, 5.444568842}, + { 0.000344E-6, 412.371096874, 5.676832684}, + { 0.000383E-6, 955.599741609, 5.559734846}, + { 0.000333E-6, 6496.374945429, 0.261537984}, + { 0.000340E-6, 6055.549660552, 5.975534987}, + { 0.000334E-6, 1066.495477190, 2.335063907}, + { 0.000399E-6, 11506.769769794, 5.321230910}, + { 0.000314E-6, 18319.536584880, 2.313312404}, + { 0.000424E-6, 1052.268383188, 1.211961766}, + { 0.000307E-6, 63.735898303, 3.169551388}, + { 0.000329E-6, 29.821438149, 6.106912080}, + { 0.000357E-6, 6309.374169791, 4.223760346}, + { 0.000312E-6, -3738.761430108, 2.180556645}, + { 0.000301E-6, 309.278322656, 1.499984572}, + { 0.000268E-6, 12043.574281889, 2.447520648}, + { 0.000257E-6, 12491.370101415, 3.662331761}, + { 0.000290E-6, 625.670192312, 1.272834584}, + { 0.000256E-6, 5429.879468239, 1.913426912}, + { 0.000339E-6, 3496.032826134, 4.165930011}, + { 0.000283E-6, 3930.209696220, 4.325565754}, + { 0.000241E-6, 12528.018664345, 3.832324536}, + { 0.000304E-6, 4686.889407707, 1.612348468}, + { 0.000259E-6, 16200.772724501, 3.470173146}, + { 0.000238E-6, 12139.553509107, 1.147977842}, + { 0.000236E-6, 6172.869528772, 3.776271728}, + { 0.000296E-6, -7058.598461315, 0.460368852}, + { 0.000306E-6, 10575.406682942, 0.554749016}, + { 0.000251E-6, 17298.182327326, 0.834332510}, + { 0.000290E-6, 4732.030627343, 4.759564091}, + { 0.000261E-6, 5884.926846583, 0.298259862}, + { 0.000249E-6, 5547.199336460, 3.749366406}, + { 0.000213E-6, 11712.955318231, 5.415666119}, + { 0.000223E-6, 4701.116501708, 2.703203558}, + { 0.000268E-6, -640.877607382, 0.283670793}, + { 0.000209E-6, 5636.065016677, 1.238477199}, + { 0.000193E-6, 10177.257679534, 1.943251340}, + { 0.000182E-6, 6283.143160294, 2.456157599}, + { 0.000184E-6, -227.526189440, 5.888038582}, + { 0.000182E-6, -6283.008539689, 0.241332086}, + { 0.000228E-6, -6284.056171060, 2.657323816}, + { 0.000166E-6, 7238.675591600, 5.930629110}, + { 0.000167E-6, 3097.883822726, 5.570955333}, + { 0.000159E-6, -323.505416657, 5.786670700}, + { 0.000154E-6, -4136.910433516, 1.517805532}, + { 0.000176E-6, 12029.347187887, 3.139266834}, + { 0.000167E-6, 12132.439962106, 3.556352289}, + { 0.000153E-6, 202.253395174, 1.463313961}, + { 0.000157E-6, 17267.268201691, 1.586837396}, + { 0.000142E-6, 83996.847317911, 0.022670115}, + { 0.000152E-6, 17260.154654690, 0.708528947}, + { 0.000144E-6, 6084.003848555, 5.187075177}, + { 0.000135E-6, 5756.566278634, 1.993229262}, + { 0.000134E-6, 5750.203491159, 3.457197134}, + { 0.000144E-6, 5326.786694021, 6.066193291}, + { 0.000160E-6, 11015.106477335, 1.710431974}, + { 0.000133E-6, 3634.621024518, 2.836451652}, + { 0.000134E-6, 18073.704938650, 5.453106665}, + { 0.000134E-6, 1162.474704408, 5.326898811}, + { 0.000128E-6, 5642.198242609, 2.511652591}, + { 0.000160E-6, 632.783739313, 5.628785365}, + { 0.000132E-6, 13916.019109642, 0.819294053}, + { 0.000122E-6, 14314.168113050, 5.677408071}, + { 0.000125E-6, 12359.966151546, 5.251984735}, + { 0.000121E-6, 5749.452731634, 2.210924603}, + { 0.000136E-6, -245.831646229, 1.646502367}, + { 0.000120E-6, 5757.317038160, 3.240883049}, + { 0.000134E-6, 12146.667056108, 3.059480037}, + { 0.000137E-6, 6206.809778716, 1.867105418}, + { 0.000141E-6, 17253.041107690, 2.069217456}, + { 0.000129E-6, -7477.522860216, 2.781469314}, + { 0.000116E-6, 5540.085789459, 4.281176991}, + { 0.000116E-6, 9779.108676125, 3.320925381}, + { 0.000129E-6, 5237.921013804, 3.497704076}, + { 0.000113E-6, 5959.570433334, 0.983210840}, + { 0.000122E-6, 6282.095528923, 2.674938860}, + { 0.000140E-6, -11.045700264, 4.957936982}, + { 0.000108E-6, 23543.230504682, 1.390113589}, + { 0.000106E-6, -12569.674818332, 0.429631317}, + { 0.000110E-6, -266.607041722, 5.501340197}, + { 0.000115E-6, 12559.038152982, 4.691456618}, + { 0.000134E-6, -2388.894020449, 0.577313584}, + { 0.000109E-6, 10440.274292604, 6.218148717}, + { 0.000102E-6, -543.918059096, 1.477842615}, + { 0.000108E-6, 21228.392023546, 2.237753948}, + { 0.000101E-6, -4535.059436924, 3.100492232}, + { 0.000103E-6, 76.266071276, 5.594294322}, + { 0.000104E-6, 949.175608970, 5.674287810}, + { 0.000101E-6, 13517.870106233, 2.196632348}, + { 0.000100E-6, 11933.367960670, 4.056084160}, + { 4.322990E-6, 6283.075849991, 2.642893748}, + { 0.406495E-6, 0.000000000, 4.712388980}, + { 0.122605E-6, 12566.151699983, 2.438140634}, + { 0.019476E-6, 213.299095438, 1.642186981}, + { 0.016916E-6, 529.690965095, 4.510959344}, + { 0.013374E-6, -3.523118349, 1.502210314}, + { 0.008042E-6, 26.298319800, 0.478549024}, + { 0.007824E-6, 155.420399434, 5.254710405}, + { 0.004894E-6, 5746.271337896, 4.683210850}, + { 0.004875E-6, 5760.498431898, 0.759507698}, + { 0.004416E-6, 5223.693919802, 6.028853166}, + { 0.004088E-6, -7.113547001, 0.060926389}, + { 0.004433E-6, 77713.771467920, 3.627734103}, + { 0.003277E-6, 18849.227549974, 2.327912542}, + { 0.002703E-6, 6062.663207553, 1.271941729}, + { 0.003435E-6, -775.522611324, 0.747446224}, + { 0.002618E-6, 6076.890301554, 3.633715689}, + { 0.003146E-6, 206.185548437, 5.647874613}, + { 0.002544E-6, 1577.343542448, 6.232904270}, + { 0.002218E-6, -220.412642439, 1.309509946}, + { 0.002197E-6, 5856.477659115, 2.407212349}, + { 0.002897E-6, 5753.384884897, 5.863842246}, + { 0.001766E-6, 426.598190876, 0.754113147}, + { 0.001738E-6, -796.298006816, 2.714942671}, + { 0.001695E-6, 522.577418094, 2.629369842}, + { 0.001584E-6, 5507.553238667, 1.341138229}, + { 0.001503E-6, -242.728603974, 0.377699736}, + { 0.001552E-6, -536.804512095, 2.904684667}, + { 0.001370E-6, -398.149003408, 1.265599125}, + { 0.001889E-6, -5573.142801634, 4.413514859}, + { 0.001722E-6, 6069.776754553, 2.445966339}, + { 0.001124E-6, 1059.381930189, 5.041799657}, + { 0.001258E-6, 553.569402842, 3.849557278}, + { 0.000831E-6, 951.718406251, 2.471094709}, + { 0.000767E-6, 4694.002954708, 5.363125422}, + { 0.000756E-6, 1349.867409659, 1.046195744}, + { 0.000775E-6, -11.045700264, 0.245548001}, + { 0.000597E-6, 2146.165416475, 4.543268798}, + { 0.000568E-6, 5216.580372801, 4.178853144}, + { 0.000711E-6, 1748.016413067, 5.934271972}, + { 0.000499E-6, 12036.460734888, 0.624434410}, + { 0.000671E-6, -1194.447010225, 4.136047594}, + { 0.000488E-6, 5849.364112115, 2.209679987}, + { 0.000621E-6, 6438.496249426, 4.518860804}, + { 0.000495E-6, -6286.598968340, 1.868201275}, + { 0.000456E-6, 5230.807466803, 1.271231591}, + { 0.000451E-6, 5088.628839767, 0.084060889}, + { 0.000435E-6, 5643.178563677, 3.324456609}, + { 0.000387E-6, 10977.078804699, 4.052488477}, + { 0.000547E-6, 161000.685737473, 2.841633844}, + { 0.000522E-6, 3154.687084896, 2.171979966}, + { 0.000375E-6, 5486.777843175, 4.983027306}, + { 0.000421E-6, 5863.591206116, 4.546432249}, + { 0.000439E-6, 7084.896781115, 0.522967921}, + { 0.000309E-6, 2544.314419883, 3.172606705}, + { 0.000347E-6, 4690.479836359, 1.479586566}, + { 0.000317E-6, 801.820931124, 3.553088096}, + { 0.000262E-6, 419.484643875, 0.606635550}, + { 0.000248E-6, 6836.645252834, 3.014082064}, + { 0.000245E-6, -1592.596013633, 5.519526220}, + { 0.000225E-6, 4292.330832950, 2.877956536}, + { 0.000214E-6, 7234.794256242, 1.605227587}, + { 0.000205E-6, 5767.611978898, 0.625804796}, + { 0.000180E-6, 10447.387839604, 3.499954526}, + { 0.000229E-6, 199.072001436, 5.632304604}, + { 0.000214E-6, 639.897286314, 5.960227667}, + { 0.000175E-6, -433.711737877, 2.162417992}, + { 0.000209E-6, 515.463871093, 2.322150893}, + { 0.000173E-6, 6040.347246017, 2.556183691}, + { 0.000184E-6, 6309.374169791, 4.732296790}, + { 0.000227E-6, 149854.400134205, 5.385812217}, + { 0.000154E-6, 8031.092263058, 5.120720920}, + { 0.000151E-6, 5739.157790895, 4.815000443}, + { 0.000197E-6, 7632.943259650, 0.222827271}, + { 0.000197E-6, 74.781598567, 3.910456770}, + { 0.000138E-6, 6055.549660552, 1.397484253}, + { 0.000149E-6, -6127.655450557, 5.333727496}, + { 0.000137E-6, 3894.181829542, 4.281749907}, + { 0.000135E-6, 9437.762934887, 5.979971885}, + { 0.000139E-6, -2352.866153772, 4.715630782}, + { 0.000142E-6, 6812.766815086, 0.513330157}, + { 0.000120E-6, -4705.732307544, 0.194160689}, + { 0.000131E-6, -71430.695617928, 0.000379226}, + { 0.000124E-6, 6279.552731642, 2.122264908}, + { 0.000108E-6, -6256.777530192, 0.883445696}, + { 0.143388E-6, 6283.075849991, 1.131453581}, + { 0.006671E-6, 12566.151699983, 0.775148887}, + { 0.001480E-6, 155.420399434, 0.480016880}, + { 0.000934E-6, 213.299095438, 6.144453084}, + { 0.000795E-6, 529.690965095, 2.941595619}, + { 0.000673E-6, 5746.271337896, 0.120415406}, + { 0.000672E-6, 5760.498431898, 5.317009738}, + { 0.000389E-6, -220.412642439, 3.090323467}, + { 0.000373E-6, 6062.663207553, 3.003551964}, + { 0.000360E-6, 6076.890301554, 1.918913041}, + { 0.000316E-6, -21.340641002, 5.545798121}, + { 0.000315E-6, -242.728603974, 1.884932563}, + { 0.000278E-6, 206.185548437, 1.266254859}, + { 0.000238E-6, -536.804512095, 4.532664830}, + { 0.000185E-6, 522.577418094, 4.578313856}, + { 0.000245E-6, 18849.227549974, 0.587467082}, + { 0.000180E-6, 426.598190876, 5.151178553}, + { 0.000200E-6, 553.569402842, 5.355983739}, + { 0.000141E-6, 5223.693919802, 1.336556009}, + { 0.000104E-6, 5856.477659115, 4.239842759}, + { 0.003826E-6, 6283.075849991, 5.705257275}, + { 0.000303E-6, 12566.151699983, 5.407132842}, + { 0.000209E-6, 155.420399434, 1.989815753} + }; + +/* -------------------------------------------------------------------- */ + +/* Local Variables: */ + double t, tsol, w, elsun, emsun, d, elj, els, wt, w0, w1, w2, w3, w4, + wf, wj; + int i; + + +/* Time since J2000.0 in Julian millennia. */ + t = ( tdb - 51544.5 )/365250; + + + +/* -------------------- Topocentric terms ----------------------------- */ + +/* Convert UT1 to local solar time in radians. */ + tsol = fmod( ut1, 1.0 )*D2PI - wl; + +/* FUNDAMENTAL ARGUMENTS: Simon et al 1994 */ + +/* Combine time argument (millennia ) with deg/arcsec factor. */ + w = t / 3600.0; + +/* Sun Mean Longitude. */ + elsun = fmod( 280.46645683 + 1296027711.03429*w, 360.0 )*D2R; + +/* Sun Mean Anomaly. */ + emsun = fmod( 357.52910918 + 1295965810.481*w, 360.0 )*D2R; + +/* Mean Elongation of Moon from Sun. */ + d = fmod( 297.85019547 + 16029616012.090*w, 360.0 )*D2R; + +/* Mean Longitude of Jupiter. */ + elj = fmod( 34.35151874 + 109306899.89453*w, 360.0 )*D2R; + +/* Mean Longitude of Saturn. */ + els = fmod( 50.07744430 + 44046398.47038*w, 360.0 )*D2R; + +/* TOPOCENTRIC TERMS: Moyer 1981 and Murray 1983. */ + wt = + 0.00029E-10*u*sin( tsol + elsun - els ) + + 0.00100E-10*u*sin( tsol - 2*emsun ) + + 0.00133E-10*u*sin( tsol - d ) + + 0.00133E-10*u*sin( tsol + elsun - elj ) + - 0.00229E-10*u*sin( tsol + 2*elsun + emsun ) + - 0.0220E-10*v*cos( elsun + emsun ) + + 0.05312E-10*u*sin( tsol - emsun ) + - 0.13677E-10*u*sin( tsol + 2*elsun ) + - 1.3184E-10*v*cos( elsun ) + + 3.17679E-10*u*sin( tsol ); + + + +/* --------------- Fairhead model --------------------------------------- */ + +/* t**0 */ + w0 = 0; + for( i = 473; i >= 0; i-- ) { + w0 = w0 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] ); + } + +/* t**1 */ + w1 = 0; + for( i = 678; i >= 474; i-- ) { + w1 = w1 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] ); + } + +/* t**2 */ + w2 = 0; + for( i = 763; i >= 679; i-- ) { + w2 = w2 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] ); + } + +/* t**3 */ + w3 = 0; + for( i = 783; i >= 764; i-- ) { + w3 = w3 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] ); + } + +/* t**4 */ + w4 = 0; + for( i = 786; i >= 784; i-- ) { + w4 = w4 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] ); + } + +/* Multiply by powers of T and combine. */ + wf = t*( t*( t*( t*w4 + w3 ) + w2 ) + w1 ) + w0; + +/* Adjustments to use JPL planetary masses instead of IAU. */ + wj = 0.00065E-6 * sin( 6069.776754 *t + 4.021194 ) + + 0.00033E-6 * sin( 213.299095 *t + 5.543132 ) + + ( -0.00196E-6 * sin( 6208.294251 *t + 5.696701 ) ) + + ( -0.00173E-6 * sin( 74.781599 *t + 2.435900 ) ) + + 0.03638E-6*t*t; + + + +/* -------------------------------------------------------------------- */ + +/* Final result: TDB-TT in seconds. */ + return wt + wf + wj; + +} + +static void TimeAdd( AstTimeMap *this, const char *cvt, const double args[], int *status ) { +/* +*++ +* Name: +c astTimeAdd +f AST_TIMEADD + +* Purpose: +* Add a time coordinate conversion to a TimeMap. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "timemap.h" +c void astTimeAdd( AstTimeMap *this, const char *cvt, const double args[] ) +f CALL AST_TIMEADD( THIS, CVT, ARGS, STATUS ) + +* Class Membership: +* TimeMap method. + +* Description: +c This function adds one of the standard time coordinate +f This routine adds one of the standard time coordinate +* system conversions listed below to an existing TimeMap. +* +c When a TimeMap is first created (using astTimeMap), it simply +f When a TimeMap is first created (using AST_TIMEMAP), it simply +c performs a unit (null) Mapping. By using astTimeAdd (repeatedly +f performs a unit (null) Mapping. By using AST_TIMEADD (repeatedly +* if necessary), one or more coordinate conversion steps may then +* be added, which the TimeMap will perform in sequence. This allows +* multi-step conversions between a variety of time coordinate +* systems to be assembled out of the building blocks provided by +* this class. +* +* Normally, if a TimeMap's Invert attribute is zero (the default), +* then its forward transformation is performed by carrying out +* each of the individual coordinate conversions specified by +c astTimeAdd in the order given (i.e. with the most recently added +f AST_TIMEADD in the order given (i.e. with the most recently added +* conversion applied last). +* +* This order is reversed if the TimeMap's Invert attribute is +* non-zero (or if the inverse transformation is requested by any +* other means) and each individual coordinate conversion is also +* replaced by its own inverse. This process inverts the overall +* effect of the TimeMap. In this case, the first conversion to be +* applied would be the inverse of the one most recently added. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the TimeMap. +c cvt +f CVT = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string which identifies the +f A character string which identifies the +* time coordinate conversion to be added to the +* TimeMap. See the "Available Conversions" section for details of +* those available. +c args +f ARGS( * ) = DOUBLE PRECISION (Given) +* An array containing argument values for the time +* coordinate conversion. The number of arguments required, and +* hence the number of array elements used, depends on the +* conversion specified (see the "Available Conversions" +* section). This array is ignored +c and a NULL pointer may be supplied +* if no arguments are needed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Notes: +* - When assembling a multi-stage conversion, it can sometimes be +* difficult to determine the most economical conversion path. A solution +* to this is to include all the steps which are (logically) necessary, +* but then to use +c astSimplify to simplify the resulting +f AST_SIMPLIFY to simplify the resulting +* TimeMap. The simplification process will eliminate any steps +* which turn out not to be needed. +c - This function does not check to ensure that the sequence of +f - This routine does not check to ensure that the sequence of +* coordinate conversions added to a TimeMap is physically +* meaningful. + +* Available Conversions: +* The following strings (which are case-insensitive) may be supplied +c via the "cvt" parameter to indicate which time coordinate +f via the CVT argument to indicate which time coordinate +* conversion is to be added to the TimeMap. Where arguments are needed by +* the conversion, they are listed in parentheses. Values for +c these arguments should be given, via the "args" array, in the +f these arguments should be given, via the ARGS array, in the +* order indicated. Units and argument names are described at the end of +* the list of conversions, and "MJD" means Modified Julian Date. +* +* - "MJDTOMJD" (MJDOFF1,MJDOFF2): Convert MJD from one offset to another. +* - "MJDTOJD" (MJDOFF,JDOFF): Convert MJD to Julian Date. +* - "JDTOMJD" (JDOFF,MJDOFF): Convert Julian Date to MJD. +* - "MJDTOBEP" (MJDOFF,BEPOFF): Convert MJD to Besselian epoch. +* - "BEPTOMJD" (BEPOFF,MJDOFF): Convert Besselian epoch to MJD. +* - "MJDTOJEP" (MJDOFF,JEPOFF): Convert MJD to Julian epoch. +* - "JEPTOMJD" (JEPOFF,MJDOFF): Convert Julian epoch to MJD. +* - "TAITOUTC" (MJDOFF): Convert a TAI MJD to a UTC MJD. +* - "UTCTOTAI" (MJDOFF): Convert a UTC MJD to a TAI MJD. +* - "TAITOTT" (MJDOFF): Convert a TAI MJD to a TT MJD. +* - "TTTOTAI" (MJDOFF): Convert a TT MJD to a TAI MJD. +* - "TTTOTDB" (MJDOFF, OBSLON, OBSLAT, OBSALT): Convert a TT MJD to a TDB MJD. +* - "TDBTOTT" (MJDOFF, OBSLON, OBSLAT, OBSALT): Convert a TDB MJD to a TT MJD. +* - "TTTOTCG" (MJDOFF): Convert a TT MJD to a TCG MJD. +* - "TCGTOTT" (MJDOFF): Convert a TCG MJD to a TT MJD. +* - "TDBTOTCB" (MJDOFF): Convert a TDB MJD to a TCB MJD. +* - "TCBTOTDB" (MJDOFF): Convert a TCB MJD to a TDB MJD. +* - "UTTOGMST" (MJDOFF): Convert a UT MJD to a GMST MJD. +* - "GMSTTOUT" (MJDOFF): Convert a GMST MJD to a UT MJD. +* - "GMSTTOLMST" (MJDOFF, OBSLON, OBSLAT): Convert a GMST MJD to a LMST MJD. +* - "LMSTTOGMST" (MJDOFF, OBSLON, OBSLAT): Convert a LMST MJD to a GMST MJD. +* - "LASTTOLMST" (MJDOFF, OBSLON, OBSLAT): Convert a GMST MJD to a LMST MJD. +* - "LMSTTOLAST" (MJDOFF, OBSLON, OBSLAT): Convert a LMST MJD to a GMST MJD. +* - "UTTOUTC" (DUT1): Convert a UT1 MJD to a UTC MJD. +* - "UTCTOUT" (DUT1): Convert a UTC MJD to a UT1 MJD. +* - "LTTOUTC" (LTOFF): Convert a Local Time MJD to a UTC MJD. +* - "UTCTOLT" (LTOFF): Convert a UTC MJD to a Local Time MJD. +* +* The units for the values processed by the above conversions are as +* follows: +* +* - Julian epochs and offsets: Julian years +* - Besselian epochs and offsets: Tropical years +* - Modified Julian Dates and offsets: days +* - Julian Dates and offsets: days +* +* The arguments used in the above conversions are the zero-points +* used by the +c astTransform function. +f AST_TRANSFORM routine. +* The axis values supplied and returned by +c astTransform +f AST_TRANSFORM +* are offsets away from these zero-points: +* +* - MJDOFF: The zero-point being used with MJD values. +* - JDOFF: The zero-point being used with Julian Date values. +* - BEPOFF: The zero-point being used with Besselian epoch values. +* - JEPOFF: The zero-point being used with Julian epoch values. +* - OBSLON: Observer longitude in radians (+ve westwards). +* - OBSLAT: Observer geodetic latitude (IAU 1975) in radians (+ve northwards). +* - OBSALT: Observer geodetic altitude (IAU 1975) in metres. +* - DUT1: The UT1-UTC value to use. +* - LTOFF: The offset between Local Time and UTC (in hours, positive +* for time zones east of Greenwich). +*-- +*/ + +/* Local Variables: */ + int cvttype; /* Conversion type code */ + +/* Check the inherited status. */ + if ( !astOK ) return; + +/* Validate the type string supplied and obtain the equivalent + conversion type code. */ + cvttype = CvtCode( cvt, status ); + +/* If the string was not recognised, then report an error. */ + if ( astOK && ( cvttype == AST__TIME_NULL ) ) { + astError( AST__TIMIN, + "%s(%s): Invalid TimeMap time coordinate " + "conversion type \"%s\".", status, "astAddTime", astGetClass( this ), cvt ); + } + +/* Add the new conversion to the TimeMap. */ + AddTimeCvt( this, cvttype, args, status ); +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a TimeMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* TimeMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a TimeMap and a set of points encapsulated +* in a PointSet and transforms the points so as to perform the +* sequence of time coordinate conversions specified by +* previous invocations of astTimeAdd. + +* Parameters: +* this +* Pointer to the TimeMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the TimeMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstTimeMap *map; /* Pointer to TimeMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *args; /* Pointer to argument list for conversion */ + double *time; /* Pointer to output time axis value array */ + double gmstx; /* GMST offset (in days) */ + double tai; /* Absolute TAI value (in days) */ + double tdb; /* Absolute TDB value (in days) */ + double tt; /* Absolute TT value (in days) */ + double utc; /* Absolute UTC value (in days) */ + int ct; /* Conversion type */ + int cvt; /* Loop counter for conversions */ + int end; /* Termination index for conversion loop */ + int inc; /* Increment for conversion loop */ + int ncoord_in; /* Number of coordinates per input point */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + int start; /* Starting index for conversion loop */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the TimeMap. */ + map = (AstTimeMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + coordinate conversions needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + ncoord_in = astGetNcoord( in ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse transformation, according + to the direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( this ) ) forward = !forward; + +/* Transform the coordinate values. */ +/* -------------------------------- */ +/* Use "time" as a synonym for the array of time axis values stored in + the output PointSet. */ + if ( astOK ) { + time = ptr_out[ 0 ]; + +/* Initialise the output coordinate values by copying the input ones. */ + if( time != ptr_in[ 0 ] ) { + (void) memcpy( time, ptr_in[ 0 ], sizeof( double ) * (size_t) npoint ); + } + +/* We will loop to apply each time coordinate conversion in turn to the + (time) array. However, if the inverse transformation was requested, + we must loop through these transformations in reverse order, so set up + appropriate limits and an increment to control this loop. */ + start = forward ? 0 : map->ncvt - 1; + end = forward ? map->ncvt : -1; + inc = forward ? 1 : -1; + +/* Loop through the coordinate conversions in the required order and obtain a + pointer to the argument list for the current conversion. */ + for ( cvt = start; cvt != end; cvt += inc ) { + args = map->cvtargs[ cvt ]; + +/* Classify the SLALIB sky coordinate conversion to be applied. */ + ct = map->cvttype[ cvt ]; + switch ( ct ) { + +/* MJD to MJD. */ +/* ---------- */ + case AST__MJDTOMJD: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 2 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 2 ]; + } + } + } + break; + +/* MJD to JD. */ +/* ---------- */ + case AST__MJDTOJD: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 2 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 2 ]; + } + } + } + break; + +/* JD to MJD. */ +/* ---------- */ + case AST__JDTOMJD: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 2 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 2 ]; + } + } + } + break; + +/* MJD to Besselian epoch. */ +/* ----------------------- */ + case AST__MJDTOBEP: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpb( time[ point ] ) + args[ 2 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpb2d( time[ point ] ) + args[ 3 ]; + } + } + } + break; + +/* Besselian epoch to MJD. */ +/* ----------------------- */ + case AST__BEPTOMJD: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpb2d( time[ point ] ) + args[ 2 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpb( time[ point ] ) + args[ 3 ]; + } + } + } + break; + +/* MJD to Julian epoch. */ +/* -------------------- */ + case AST__MJDTOJEP: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpj( time[ point ] ) + args[ 2 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpj2d( time[ point ] ) + args[ 3 ]; + } + } + } + break; + +/* Julian epoch to MJD. */ +/* -------------------- */ + case AST__JEPTOMJD: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpj2d( time[ point ] ) + args[ 2 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = palEpj( time[ point ] ) + args[ 3 ]; + } + } + } + break; + +/* TAI to UTC. */ +/* ----------- */ + case AST__TAITOUTC: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += astDat( time[ point ] + + args[ 0 ], 0 )/SPD; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += astDat( time[ point ] + + args[ 0 ], 1 )/SPD; + } + } + } + break; + +/* UTC to TAI. */ +/* ----------- */ + case AST__UTCTOTAI: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += astDat( time[ point ] + + args[ 0 ], 1 )/SPD; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += astDat( time[ point ] + + args[ 0 ], 0 )/SPD; + } + } + } + break; + +/* TAI to TT. */ +/* ---------- */ + case AST__TAITOTT: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += (TTOFF/SPD); + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= (TTOFF/SPD); + } + } + } + break; + +/* TT to TAI. */ +/* ---------- */ + case AST__TTTOTAI: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= (TTOFF/SPD); + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += (TTOFF/SPD); + } + } + } + break; + +/* TT to TDB. */ +/* ---------- */ +/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation + to UT1, and that TT is a good approximation to TDB. */ + case AST__TTTOTDB: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + tt = time[ point ] + args[ 0 ]; + tai = tt - (TTOFF/SPD); + utc = tai + astDat( tai, 0 )/SPD; + time[ point ] += Rcc( tt, utc, args[ 1 ], args[ 4 ], + args[ 5 ], status )/SPD; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + tdb = time[ point ] + args[ 0 ]; + tai = tdb - (TTOFF/SPD); + utc = tai + astDat( tai, 0 )/SPD; + time[ point ] -= Rcc( tdb, utc, args[ 1 ], args[ 4 ], + args[ 5 ], status )/SPD; + } + } + } + break; + +/* TDB to TT. */ +/* ---------- */ +/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation + to UT1, and that TT is a good approximation to TDB. */ + case AST__TDBTOTT: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + tdb = time[ point ] + args[ 0 ]; + tai = tdb - (TTOFF/SPD); + utc = tai + astDat( tai, 0 )/SPD; + time[ point ] -= Rcc( tdb, utc, args[ 1 ], args[ 4 ], + args[ 5 ], status )/SPD; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + tt = time[ point ] + args[ 0 ]; + tai = tt - (TTOFF/SPD); + utc = tai + astDat( tai, 0 )/SPD; + time[ point ] += Rcc( tt, utc, args[ 1 ], args[ 4 ], + args[ 5 ], status )/SPD; + } + } + } + break; + +/* TT to TCG. */ +/* ---------- */ + case AST__TTTOTCG: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += time[ point ]*LG + args[ 1 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = ( time[ point ] - args[ 1 ] ) / + ( 1.0 + LG ); + } + } + } + break; + +/* TCG to TT. */ +/* ---------- */ + case AST__TCGTOTT: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = ( time[ point ] - args[ 1 ] ) / + ( 1.0 + LG ); + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += time[ point ]*LG + args[ 1 ]; + } + } + } + break; + +/* TDB to TCB. */ +/* ----------- */ +/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation + to UT1, and that TT is a good approximation to both TDB and TCB. */ + case AST__TDBTOTCB: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += time[ point ]*LB + args[ 1 ]; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = ( time[ point ] - args[ 1 ] ) / + ( 1.0 + LB ); + } + } + } + break; + +/* TCB to TDB. */ +/* ----------- */ +/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation + to UT1, and that TT is a good approximation to TDB. */ + case AST__TCBTOTDB: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = ( time[ point ] - args[ 1 ] ) / + ( 1.0 + LB ); + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += time[ point ]*LB + args[ 1 ]; + } + } + } + break; + +/* UT to GMST . */ +/* ------------ */ + case AST__UTTOGMST: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = Gmsta( time[ point ], args[ 0 ], 1, status ); + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = Gmsta( time[ point ], args[ 0 ], 0, status ); + } + } + } + break; + +/* GMST to UT. */ +/* ----------- */ + case AST__GMSTTOUT: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = Gmsta( time[ point ], args[ 0 ], 0, status ); + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] = Gmsta( time[ point ], args[ 0 ], 1, status ); + } + } + } + break; + +/* GMST to LMST. */ +/* ------------- */ + case AST__GMSTTOLMST: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 1 ]/D2PI; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 1 ]/D2PI; + } + } + } + break; + +/* LMST to GMST. */ +/* ------------- */ + case AST__LMSTTOGMST: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 1 ]/D2PI; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 1 ]/D2PI; + } + } + } + break; + +/* UT1 to UTC. */ +/* ------------- */ + case AST__UTTOUTC: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 0 ]/86400.0; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 0 ]/86400.0; + } + } + } + break; + + +/* UTC to UT1. */ +/* ------------- */ + case AST__UTCTOUT: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 0 ]/86400.0; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 0 ]/86400.0; + } + } + } + break; + +/* LT to UTC. */ +/* ---------- */ + case AST__LTTOUTC: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 0 ]/24.0; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 0 ]/24.0; + } + } + } + break; + + +/* UTC to LT. */ +/* ---------- */ + case AST__UTCTOLT: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] += args[ 0 ]/24.0; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + time[ point ] -= args[ 0 ]/24.0; + } + } + } + break; + +/* LMST to LAST. */ +/* ------------- */ +/* Calculating the equation of the equinoxes required TDB. So we need to + convert the given LMST to TDB. We first convert LMST to UT1. UT1 is + equal to UTC to within 1 second. We then add on 32 seconds to get TAI + (this value is correct since 1999 - for earlier epochs an error of the + order of a minute will be introduced in the TAI value). We then add on + TTOFF seconds to get TT. This TT is then used as an approximation to + TDB. The total error in TDB is of the order of a few minutes, which + corresponds to an error of a few tens of microseconds in the equation of + the equinoxes. The sla precession-nutation model is accurate to around 3 + mas = 200 us, so the error in TDB will be insignificant. */ + case AST__LMSTTOLAST: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + gmstx = time[ point ] + args[ 1 ]/D2PI; + tdb = Gmsta( gmstx, args[ 0 ], 0, status ) + + args[ 0 ] + (32 + TTOFF)/SPD; + time[ point ] += palEqeqx( tdb )/D2PI; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + gmstx = time[ point ] + args[ 1 ]/D2PI; + tdb = Gmsta( gmstx, args[ 0 ], 0, status ) + + args[ 0 ] + (32+TTOFF)/SPD; + time[ point ] -= palEqeqx( tdb )/D2PI; + } + } + } + break; + +/* LAST to LMST. */ +/* ------------- */ + case AST__LASTTOLMST: + if ( forward ) { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + gmstx = time[ point ] + args[ 1 ]/D2PI; + tdb = Gmsta( gmstx, args[ 0 ], 0, status ) + + args[ 0 ] + (32+TTOFF)/SPD; + time[ point ] -= palEqeqx( tdb )/D2PI; + } + } + } else { + for ( point = 0; point < npoint; point++ ) { + if ( time[ point ] != AST__BAD ) { + gmstx = time[ point ] + args[ 1 ]/D2PI; + tdb = Gmsta( gmstx, args[ 0 ], 0, status ) + + args[ 0 ] + (32 + TTOFF)/SPD; + time[ point ] += palEqeqx( tdb )/D2PI; + } + } + } + + } + } + } + +/* If an error has occurred and a new PointSet may have been created, then + clean up by annulling it. In any case, ensure that a NULL result is + returned.*/ + if ( !astOK ) { + if ( !out ) result = astAnnul( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for TimeMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for TimeMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy. +*/ + +/* Local Variables: */ + AstTimeMap *in; /* Pointer to input TimeMap */ + AstTimeMap *out; /* Pointer to output TimeMap */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output TimeMap structures. */ + in = (AstTimeMap *) objin; + out = (AstTimeMap *) objout; + +/* For safety, first clear any references to the input memory from the output + TimeMap. */ + out->cvtargs = NULL; + out->cvttype = NULL; + +/* Allocate memory for the output array of argument list pointers. */ + out->cvtargs = astMalloc( sizeof( double * ) * (size_t) in->ncvt ); + +/* If necessary, allocate memory and make a copy of the input array of + coordinate conversion codes. */ + if ( in->cvttype ) out->cvttype = astStore( NULL, in->cvttype, + sizeof( int ) + * (size_t) in->ncvt ); + +/* If OK, loop through each conversion in the input TimeMap and make a copy of + its argument list, storing the new pointer in the output argument list + array. */ + if ( astOK ) { + for ( cvt = 0; cvt < in->ncvt; cvt++ ) { + out->cvtargs[ cvt ] = astStore( NULL, in->cvtargs[ cvt ], + astSizeOf( in->cvtargs[ cvt ] ) ); + } + +/* If an error occurred while copying the argument lists, loop through the + conversions again and clean up by ensuring that the new memory allocated for + each argument list is freed. */ + if ( !astOK ) { + for ( cvt = 0; cvt < in->ncvt; cvt++ ) { + out->cvtargs[ cvt ] = astFree( out->cvtargs[ cvt ] ); + } + } + } + +/* If an error occurred, free all other memory allocated above. */ + if ( !astOK ) { + out->cvtargs = astFree( out->cvtargs ); + out->cvttype = astFree( out->cvttype ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for TimeMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for TimeMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstTimeMap *this; /* Pointer to TimeMap */ + int cvt; /* Loop counter for coordinate conversions */ + +/* Obtain a pointer to the TimeMap structure. */ + this = (AstTimeMap *) obj; + +/* Loop to free the memory containing the argument list for each coordinate + conversion. */ + for ( cvt = 0; cvt < this->ncvt; cvt++ ) { + this->cvtargs[ cvt ] = astFree( this->cvtargs[ cvt ] ); + } + +/* Free the memory holding the array of conversion types and the array of + argument list pointers. */ + this->cvtargs = astFree( this->cvtargs ); + this->cvttype = astFree( this->cvttype ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for TimeMap objects. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the TimeMap class to an output Channel. + +* Parameters: +* this +* Pointer to the TimeMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstTimeMap *this; /* Pointer to the TimeMap structure */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + const char *sval; /* Pointer to string value */ + int iarg; /* Loop counter for arguments */ + int icvt; /* Loop counter for conversion steps */ + int ival; /* Integer value */ + int nargs; /* Number of user-supplied arguments */ + int szargs; /* Number of stored arguments */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TimeMap structure. */ + this = (AstTimeMap *) this_object; + +/* Write out values representing the instance variables for the TimeMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* Number of conversion steps. */ +/* --------------------------- */ +/* Regard this as "set" if it is non-zero. */ + ival = this->ncvt; + set = ( ival != 0 ); + astWriteInt( channel, "Ntime", set, 0, ival, "Number of conversion steps" ); + +/* Write out data for each conversion step... */ + for ( icvt = 0; icvt < this->ncvt; icvt++ ) { + +/* Conversion type. */ +/* ---------------- */ +/* Change each conversion type code into an equivalent string and + obtain associated descriptive information. If the conversion code + was not recognised, report an error and give up. */ + if ( astOK ) { + sval = CvtString( this->cvttype[ icvt ], &comment, + &nargs, &szargs, argdesc, status ); + if ( astOK && !sval ) { + astError( AST__TIMIN, + "astWrite(%s): Corrupt %s contains invalid TimeMap " + "time coordinate conversion code (%d).", status, + astGetClass( channel ), astGetClass( this ), + (int) this->cvttype[ icvt ] ); + break; + } + +/* Create an appropriate keyword and write out the conversion code + information. */ + (void) sprintf( key, "Time%d", icvt + 1 ); + astWriteString( channel, key, 1, 1, sval, comment ); + +/* Write out data for each conversion argument... */ + for ( iarg = 0; iarg < szargs; iarg++ ) { + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and write out the argument value, + accompanied by the descriptive comment obtained above. */ + if( this->cvtargs[ icvt ][ iarg ] != AST__BAD ) { + (void) sprintf( key, "Time%d%c", icvt + 1, ALPHABET[ iarg ] ); + astWriteDouble( channel, key, 1, 1, this->cvtargs[ icvt ][ iarg ], + argdesc[ iarg ] ); + } + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsATimeMap and astCheckTimeMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(TimeMap,Mapping) +astMAKE_CHECK(TimeMap) + +AstTimeMap *astTimeMap_( int flags, const char *options, int *status, ...) { +/* +*++ +* Name: +c astTimeMap +f AST_TIMEMAP + +* Purpose: +* Create a TimeMap. + +* Type: +* Public function. + +* Synopsis: +c #include "timemap.h" +c AstTimeMap *astTimeMap( int flags, const char *options, ... ) +f RESULT = AST_TIMEMAP( FLAGS, OPTIONS, STATUS ) + +* Class Membership: +* TimeMap constructor. + +* Description: +* This function creates a new TimeMap and optionally initialises +* its attributes. +* +* A TimeMap is a specialised form of 1-dimensional Mapping which can be +* used to represent a sequence of conversions between standard time +* coordinate systems. +* +* When a TimeMap is first created, it simply performs a unit +c (null) Mapping. Using the astTimeAdd +f (null) Mapping. Using the AST_TIMEADD +c function, a series of coordinate conversion steps may then be +f routine, a series of coordinate conversion steps may then be +* added. This allows multi-step conversions between a variety of +* time coordinate systems to be assembled out of a set of building +* blocks. +* +* For details of the individual coordinate conversions available, +c see the description of the astTimeAdd function. +f see the description of the AST_TIMEADD routine. + +* Parameters: +c flags +f FLAGS = INTEGER (Given) +c This parameter is reserved for future use and should currently +f This argument is reserved for future use and should currently +* always be set to zero. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new TimeMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +c If no initialisation is required, a zero-length string may be +c supplied. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new TimeMap. The syntax used is identical to that for the +f AST_SET routine. If no initialisation is required, a blank +f value may be supplied. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTimeMap() +f AST_TIMEMAP = INTEGER +* A pointer to the new TimeMap. + +* Notes: +* - The nature and units of the coordinate values supplied for the +* first input (i.e. the time input) of a TimeMap must be appropriate +* to the first conversion step applied by the TimeMap. For instance, if +* the first conversion step is "MJDTOBEP" (Modified Julian Date to +* Besselian epoch) then the coordinate values for the first input should +* be date in units of days. Similarly, the nature and units of the +* coordinate values returned by a TimeMap will be determined by the +* last conversion step applied by the TimeMap. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTimeMap *new; /* Pointer to the new TimeMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the TimeMap, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitTimeMap( NULL, sizeof( AstTimeMap ), !class_init, &class_vtab, + "TimeMap", flags ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new TimeMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new TimeMap. */ + return new; +} + +AstTimeMap *astTimeMapId_( int flags, const char *options, ... ) { +/* +* Name: +* astTimeMapId_ + +* Purpose: +* Create a TimeMap. + +* Type: +* Private function. + +* Synopsis: +* #include "timemap.h" +* AstTimeMap *astTimeMapId_( int flags, const char *options, ... ) + +* Class Membership: +* TimeMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astTimeMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astTimeMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astTimeMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astTimeMap_. + +* Returned Value: +* The ID value associated with the new TimeMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTimeMap *new; /* Pointer to the new TimeMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the TimeMap, allocating memory and initialising the virtual + function table as well if necessary. */ + new = astInitTimeMap( NULL, sizeof( AstTimeMap ), !class_init, &class_vtab, + "TimeMap", flags ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new TimeMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new TimeMap. */ + return astMakeId( new ); +} + +AstTimeMap *astInitTimeMap_( void *mem, size_t size, int init, + AstTimeMapVtab *vtab, const char *name, + int flags, int *status ) { +/* +*+ +* Name: +* astInitTimeMap + +* Purpose: +* Initialise a TimeMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "timemap.h" +* AstTimeMap *astInitTimeMap( void *mem, size_t size, int init, +* AstTimeMapVtab *vtab, const char *name, +* int flags ) + +* Class Membership: +* TimeMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new TimeMap object. It allocates memory (if necessary) to accommodate +* the TimeMap plus any additional data associated with the derived class. +* It then initialises a TimeMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a TimeMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the TimeMap is to be initialised. +* This must be of sufficient size to accommodate the TimeMap data +* (sizeof(TimeMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the TimeMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the TimeMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the TimeMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new TimeMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astClass +* method). +* flags +* This parameter is reserved for future use. It is currently ignored. + +* Returned Value: +* A pointer to the new TimeMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstTimeMap *new; /* Pointer to the new TimeMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitTimeMapVtab( vtab, name ); + +/* Initialise a 1D Mapping structure (the parent class) as the first component + within the TimeMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstTimeMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + 1, 1, 1, 1 ); + + if ( astOK ) { + +/* Initialise the TimeMap data. */ +/* --------------------------- */ +/* The initial state is with no conversions set, in which condition the + TimeMap simply implements a unit mapping. */ + new->ncvt = 0; + new->cvtargs = NULL; + new->cvttype = NULL; + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstTimeMap *astLoadTimeMap_( void *mem, size_t size, + AstTimeMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadTimeMap + +* Purpose: +* Load a TimeMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "timemap.h" +* AstTimeMap *astLoadTimeMap( void *mem, size_t size, +* AstTimeMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* TimeMap loader. + +* Description: +* This function is provided to load a new TimeMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* TimeMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a TimeMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the TimeMap is to be +* loaded. This must be of sufficient size to accommodate the +* TimeMap data (sizeof(TimeMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the TimeMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the TimeMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstTimeMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new TimeMap. If this is NULL, a pointer to +* the (static) virtual function table for the TimeMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "TimeMap" is used instead. + +* Returned Value: +* A pointer to the new TimeMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstTimeMap *new; /* Pointer to the new TimeMap */ + char *sval; /* Pointer to string value */ + char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */ + const char *comment; /* Pointer to comment string */ + int iarg; /* Loop counter for arguments */ + int icvt; /* Loop counter for conversion steps */ + int nargs; /* Number of user-supplied arguments */ + int szargs; /* Number of stored arguments */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this TimeMap. In this case the + TimeMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstTimeMap ); + vtab = &class_vtab; + name = "TimeMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitTimeMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built TimeMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "TimeMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* Number of conversion steps. */ +/* --------------------------- */ +/* Read the number of conversion steps and allocate memory to hold + data for each step. */ + new->ncvt = astReadInt( channel, "ntime", 0 ); + if ( new->ncvt < 0 ) new->ncvt = 0; + new->cvttype = astMalloc( sizeof( int ) * (size_t) new->ncvt ); + new->cvtargs = astMalloc( sizeof( double * ) * (size_t) new->ncvt ); + +/* If an error occurred, ensure that all allocated memory is freed. */ + if ( !astOK ) { + new->cvttype = astFree( new->cvttype ); + new->cvtargs = astFree( new->cvtargs ); + +/* Otherwise, initialise the argument pointer array. */ + } else { + for ( icvt = 0; icvt < new->ncvt; icvt++ ) { + new->cvtargs[ icvt ] = NULL; + } + +/* Read in data for each conversion step... */ + for ( icvt = 0; icvt < new->ncvt; icvt++ ) { + +/* Conversion type. */ +/* ---------------- */ +/* Create an appropriate keyword and read the string representation of + the conversion type. */ + (void) sprintf( key, "time%d", icvt + 1 ); + sval = astReadString( channel, key, NULL ); + +/* If no value was read, report an error. */ + if ( astOK ) { + if ( !sval ) { + astError( AST__BADIN, + "astRead(%s): A time coordinate conversion " + "type is missing from the input TimeMap data.", status, + astGetClass( channel ) ); + +/* Otherwise, convert the string representation into the required + conversion type code. */ + } else { + new->cvttype[ icvt ] = CvtCode( sval, status ); + +/* If the string was not recognised, report an error. */ + if ( new->cvttype[ icvt ] == AST__TIME_NULL ) { + astError( AST__BADIN, + "astRead(%s): Invalid time conversion " + "type \"%s\" in TimeMap data.", status, + astGetClass( channel ), sval ); + } + } + +/* Free the memory holding the string value. */ + sval = astFree( sval ); + } + +/* Obtain the number of arguments associated with the conversion and + allocate memory to hold them. */ + (void) CvtString( new->cvttype[ icvt ], &comment, + &nargs, &szargs, argdesc, status ); + new->cvtargs[ icvt ] = astMalloc( sizeof( double ) * + (size_t) szargs ); + +/* Read in data for each argument... */ + if ( astOK ) { + for ( iarg = 0; iarg < szargs; iarg++ ) { + +/* Arguments. */ +/* ---------- */ +/* Create an appropriate keyword and read each argument value. */ + (void) sprintf( key, "time%d%c", icvt + 1, ALPHABET[ iarg ] ); + new->cvtargs[ icvt ][ iarg ] = astReadDouble( channel, key, + AST__BAD ); + } + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + } + +/* If an error occurred, clean up by deleting the new TimeMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new TimeMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astTimeAdd_( AstTimeMap *this, const char *cvt, const double args[], int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,TimeMap,TimeAdd))( this, cvt, args, status ); +} + + + + diff --git a/timemap.h b/timemap.h new file mode 100644 index 0000000..68a18ab --- /dev/null +++ b/timemap.h @@ -0,0 +1,285 @@ +#if !defined( TIMEMAP_INCLUDED ) /* Include this file only once */ +#define TIMEMAP_INCLUDED +/* +*+ +* Name: +* timemap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the TimeMap class. + +* Invocation: +* #include "timemap.h" + +* Description: +* This include file defines the interface to the TimeMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The TimeMap class encapsulates various time coordinate +* conversions. Since, typically, a sequence of these conversions is +* required, a TimeMap can be used to accumulate a series of conversions +* which it then applies in sequence. + +* Inheritance: +* The TimeMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* astTransform +* Use an TimeMap to transform a set of points. + +* Protected: +* astMapMerge +* Simplify a sequence of Mappings containing an TimeMap. + +* New Methods Defined: +* Public: +* astTimeAdd +* Add a coordinate conversion step to an TimeMap. + +* Private: +* None. + +* Other Class Functions: +* Public: +* astIsATimeMap +* Test class membership. +* astTimeMap +* Create an TimeMap. + +* Protected: +* astCheckTimeMap +* Validate class membership. +* astInitTimeMap +* Initialise an TimeMap. +* astLoadTimeMap +* Load an TimeMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstTimeMap +* TimeMap object type. + +* Protected: +* AstTimeMapVtab +* TimeMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 24-MAY-2005 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* TimeMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstTimeMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int *cvttype; /* Pointer to array of conversion types */ + double **cvtargs; /* Pointer to argument list pointer array */ + int ncvt; /* Number of conversions to perform */ +} AstTimeMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstTimeMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + void (* TimeAdd)( AstTimeMap *, const char *, const double[], int * ); +} AstTimeMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstTimeMapGlobals { + AstTimeMapVtab Class_Vtab; + int Class_Init; +} AstTimeMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitTimeMapGlobals_( AstTimeMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(TimeMap) /* Check class membership */ +astPROTO_ISA(TimeMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstTimeMap *astTimeMap_( int, const char *, int *, ...); +#else +AstTimeMap *astTimeMapId_( int, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstTimeMap *astInitTimeMap_( void *, size_t, int, AstTimeMapVtab *, + const char *, int, int * ); + +/* Vtab initialiser. */ +void astInitTimeMapVtab_( AstTimeMapVtab *, const char *, int * ); + +/* Loader. */ +AstTimeMap *astLoadTimeMap_( void *, size_t, AstTimeMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +void astTimeAdd_( AstTimeMap *, const char *, const double[], int * ); + +#if defined(astCLASS) /* Protected. */ +double astDat_( double, int, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckTimeMap(this) astINVOKE_CHECK(TimeMap,this,0) +#define astVerifyTimeMap(this) astINVOKE_CHECK(TimeMap,this,1) + +/* Test class membership. */ +#define astIsATimeMap(this) astINVOKE_ISA(TimeMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astTimeMap astINVOKE(F,astTimeMap_) +#else +#define astTimeMap astINVOKE(F,astTimeMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitTimeMap(mem,size,init,vtab,name,flags) \ +astINVOKE(O,astInitTimeMap_(mem,size,init,vtab,name,flags,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitTimeMapVtab(vtab,name) astINVOKE(V,astInitTimeMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadTimeMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadTimeMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckTimeMap to validate TimeMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +#define astTimeAdd(this,cvt,args) \ +astINVOKE(V,astTimeAdd_(astCheckTimeMap(this),cvt,args,STATUS_PTR)) + +#if defined(astCLASS) /* Protected */ +#define astDat(in,forward) astDat_(in,forward,STATUS_PTR) +#endif +#endif + + + + + diff --git a/tpn.c b/tpn.c new file mode 100644 index 0000000..0d30503 --- /dev/null +++ b/tpn.c @@ -0,0 +1,393 @@ +#include +#include +#include "wcsmath.h" +#include "wcstrig.h" +#include "proj.h" + +#define icopysign(X, Y) ((Y) < 0.0 ? -abs(X) : abs(X)) +#define TPN 999 + +/*============================================================================ +* TAN: gnomonic projection, with correction terms. +* +* This projection is no longer part of the FITSWCS standard, but is +* retained here for use b the AST library as a means of implementing +* the IRAF TNX projection, and the DSS encoding. +* +* Given and/or returned: +* prj->p Array of latitude coefficients +* prj->p2 Array of longitude coefficients +* prj->flag TPN, or -TPN if prj->flag is given < 0. +* prj->r0 r0; reset to 180/pi if 0. +* prj->n If zero, only do poly part of transformation (i.e. omit +* the TAN projection). +* +* Returned: +* prj->code "TPN" +* prj->phi0 0.0 +* prj->theta0 90.0 +* prj->astPRJfwd Pointer to astTPNfwd(). +* prj->astPRJrev Pointer to astTPNrev(). +* prj->w[ 0 ] Set to 0.0 if a simple tan projection is required +* (with no polynomial correction). Otherwise, set to 1.0. +*===========================================================================*/ + +int astTPNset(prj) + +struct AstPrjPrm *prj; + +{ + int m; + + prj->flag = icopysign(TPN, prj->flag); + prj->phi0 = 0.0; + prj->theta0 = 90.0; + + if (prj->r0 == 0.0) prj->r0 = R2D; + + prj->astPRJfwd = astTPNfwd; + prj->astPRJrev = astTPNrev; + +/* If all co-efficients have their "unit" values, we do not need to + use the polynomial correction. */ + prj->w[ 0 ] = 0.0; + + if( prj->p[ 0 ] != 0.0 || prj->p2[ 0 ] != 0.0 ) { + prj->w[ 0 ] = 1.0; + + } else if( prj->p[ 1 ] != 1.0 || prj->p2[ 1 ] != 1.0 ) { + prj->w[ 0 ] = 1.0; + + } else { + for( m = 2; m < WCSLIB_MXPAR; m++ ){ + if( prj->p[ m ] != 0.0 || prj->p2[ m ] != 0.0 ){ + prj->w[ 0 ] = 1.0; + break; + } + } + } + + return 0; +} + +/*--------------------------------------------------------------------------*/ + +int astTPNfwd(phi, theta, prj, xx, yy) + +const double phi, theta; +double *xx, *yy; +struct AstPrjPrm *prj; + +{ + double r, xi, eta, x2, xy, y2, r2, x3, x2y, xy2, y3, r3, x4, x3y, + x2y2, xy3, y4, x5, x4y, x3y2, x2y3, xy4, y5, r5, x6, x5y, x4y2, + x3y3, x2y4, xy5, y6, x7, x6y, x5y2, x4y3, x3y4, x2y5, xy6, y7, + r7, tol, f, g, fx, fy, gx, gy, dx, dy, x, y, denom; + double *a, *b; + int i, ok; + + if (abs(prj->flag) != TPN ) { + if (astTPNset(prj)) return 1; + } + + if( prj->n ) { + double s = astSind(theta); + if (prj->flag > 0 && s < 0.0) { + return 2; + } + r = prj->r0*astCosd(theta)/s; + xi = r*astSind(phi); + eta = -r*astCosd(phi); + } else { + xi = phi; + eta = theta; + } + + /* Simple tan */ + if( prj->w[ 0 ] == 0.0 ){ + *xx = xi; + *yy = eta; + + /* Tan with polynomial corrections: Iterate using Newton's method to + get the (x,y) corresponding to the above (xi,eta). */ + } else { + a = prj->p2; + b = prj->p; + + /* Initial guess: linear solution assuming a3,... and b3,... are zero. */ + denom = a[1]*b[1] - a[2]*b[2]; + if( denom != 0.0 ) { + x = ( xi*b[1] - eta*a[2] - a[0]*b[1] + b[0]*a[2] )/denom; + y = -( xi*b[2] - eta*a[1] - a[0]*b[2] + b[0]*a[1] )/denom; + + } else { + if( a[1] != 0.0 ){ + x = ( xi - a[0] )/a[1]; + } else { + x = a[0]; + } + + if( b[1] != 0.0 ){ + y = ( eta - b[0] )/b[1]; + } else { + y = b[0]; + } + } + +/* Iterate up to 50 times, until the required relative accuracy is + achieved. */ + tol = 1.0E-5; + ok = 0; + for (i = 0; i < 50; i++) { + +/* Get required products of the current x and y values */ + x2 = x*x; + xy = x*y; + y2 = y*y; + + r2 = x2 + y2; + r = sqrt( r2 ); + + x3 = x2*x; + x2y = x2*y; + xy2 = x*y2; + y3 = y*y2; + + r3 = r*r2; + + x4 = x3*x; + x3y = x3*y; + x2y2 = x2*y2; + xy3 = x*y3; + y4 = y*y3; + + x5 = x4*x; + x4y = x4*y; + x3y2 = x3*y2; + x2y3 = x2*y3; + xy4 = x*y4; + y5 = y*y4; + + r5 = r3*r2; + + x6 = x5*x; + x5y = x5*y; + x4y2 = x4*y2; + x3y3 = x3*y3; + x2y4 = x2*y4; + xy5 = x*y5; + y6 = y*y5; + + x7 = x6*x; + x6y = x6*y; + x5y2 = x5*y2; + x4y3 = x4*y3; + x3y4 = x3*y4; + x2y5 = x2*y5; + xy6 = x*y6; + y7 = y*y6; + + r7 = r5*r2; + +/* Get the xi and eta models corresponding to the current x and y values */ + f = a[0] + a[1]*x + a[2]*y + a[3]*r + a[4]*x2 + + a[5]*xy + a[6]*y2 + a[7]*x3 + a[8]*x2y + a[9]*xy2 + + a[10]*y3 + a[11]*r3 + a[12]*x4 + a[13]*x3y + a[14]*x2y2 + + a[15]*xy3 + a[16]*y4 + a[17]*x5 + a[18]*x4y + a[19]*x3y2 + + a[20]*x2y3 + a[21]*xy4 + a[22]*y5 + a[23]*r5 + a[24]*x6 + + a[25]*x5y + a[26]*x4y2 + a[27]*x3y3 + a[28]*x2y4 + a[29]*xy5 + + a[30]*y6 + a[31]*x7 + a[32]*x6y + a[33]*x5y2 + a[34]*x4y3 + + a[35]*x3y4 + a[36]*x2y5 + a[37]*xy6 + a[38]*y7 + a[39]*r7; + + g = b[0] + b[1]*y + b[2]*x + b[3]*r + b[4]*y2 + + b[5]*xy + b[6]*x2 + b[7]*y3 + b[8]*xy2 + b[9]*x2y + + b[10]*x3 + b[11]*r3 + b[12]*y4 + b[13]*xy3 + b[14]*x2y2 + + b[15]*x3y + b[16]*x4 + b[17]*y5 + b[18]*xy4 + b[19]*x2y3 + + b[20]*x3y2 + b[21]*x4y + b[22]*x5 + b[23]*r5 + b[24]*y6 + + b[25]*xy5 + b[26]*x2y4 + b[27]*x3y3 + b[28]*x4y2 + b[29]*x5y + + b[30]*x6 + b[31]*y7 + b[32]*xy6 + b[33]*x2y5 + b[34]*x3y4 + + b[35]*x4y3 + b[36]*x5y2 + b[37]*x6y + b[38]*x7 + b[39]*r7; + +/* Partial derivative of xi wrt x... */ + fx = a[1] + a[3]*( (r!=0.0)?(x/r):0.0 ) + 2*a[4]*x + + a[5]*y + 3*a[7]*x2 + 2*a[8]*xy + a[9]*y2 + + 3*a[11]*r*x + 4*a[12]*x3 + 3*a[13]*x2y + 2*a[14]*xy2 + + a[15]*y3 + 5*a[17]*x4 + 4*a[18]*x3y + 3*a[19]*x2y2 + + 2*a[20]*xy3 + a[21]*y4 + 5*a[23]*r3*x + 6*a[24]*x5 + + 5*a[25]*x4y + 4*a[26]*x3y2 + 3*a[27]*x2y3 + 2*a[28]*xy4 + + a[29]*y5 + 7*a[31]*x6 + 6*a[32]*x5y + 5*a[33]*x4y2 + + 4*a[34]*x3y3 + 3*a[35]*x2y4 + 2*a[36]*xy5 + a[37]*y6 + + 7*a[39]*r5*x; + +/* Partial derivative of xi wrt y... */ + fy = a[2] + a[3]*( (r!=0.0)?(y/r):0.0 ) + a[5]*x + + 2*a[6]*y + a[8]*x2 + 2*a[9]*xy + 3*a[10]*y2 + + 3*a[11]*r*y + a[13]*x3 + 2*a[14]*x2y + 3*a[15]*xy2 + + 4*a[16]*y3 + a[18]*x4 + 2*a[19]*x3y + 3*a[20]*x2y2 + + 4*a[21]*xy3 + 5*a[22]*y4 + 5*a[23]*r3*y + a[25]*x5 + + 2*a[26]*x4y + 3*a[27]*x3y2 + 4*a[28]*x2y3 + 5*a[29]*xy4 + + 6*a[30]*y5 + a[32]*x6 + 2*a[33]*x5y + 3*a[34]*x4y2 + + 4*a[35]*x3y3 + 5*a[36]*x2y4 + 6*a[37]*xy5 + 7*a[38]*y6 + + 7*a[39]*r5*y; + +/* Partial derivative of eta wrt x... */ + gx = b[2] + b[3]*( (r!=0.0)?(x/r):0.0 ) + b[5]*y + + 2*b[6]*x + b[8]*y2 + 2*b[9]*xy + 3*b[10]*x2 + + 3*b[11]*r*x + b[13]*y3 + 2*b[14]*xy2 + 3*b[15]*x2y + + 4*b[16]*x3 + b[18]*y4 + 2*b[19]*xy3 + 3*b[20]*x2y2 + + 4*b[21]*x3y + 5*b[22]*x4 + 5*b[23]*r3*x + b[25]*y5 + + 2*b[26]*xy4 + 3*b[27]*x2y3 + 4*b[28]*x3y2 + 5*b[29]*x4y + + 6*b[30]*x5 + b[32]*y6 + 2*b[33]*xy5 + 3*b[34]*x2y4 + + 4*b[35]*x3y3 + 5*b[36]*x4y2 + 6*b[37]*x5y + 7*b[38]*x6 + + 7*b[39]*r5*x; + +/* Partial derivative of eta wrt y... */ + gy = b[1] + b[3]*( (r!=0.0)?(y/r):0.0 ) + 2*b[4]*y + + b[5]*x + 3*b[7]*y2 + 2*b[8]*xy + b[9]*x2 + + 3*b[11]*r*y + 4*b[12]*y3 + 3*b[13]*xy2 + 2*b[14]*x2y + + b[15]*x3 + 5*b[17]*y4 + 4*b[18]*xy3 + 3*b[19]*x2y2 + + 2*b[20]*x3y + b[21]*x4 + 5*b[23]*r3*y + 6*b[24]*y5 + + 5*b[25]*xy4 + 4*b[26]*x2y3 + 3*b[27]*x3y2 + 2*b[28]*x4y + + b[29]*x5 + 7*b[31]*y6 + 6*b[32]*xy5 + 5*b[33]*x2y4 + + 4*b[34]*x3y3 + 3*b[35]*x4y2 + 2*b[36]*x5y + b[37]*x6 + + 7*b[39]*r5*y; + +/* Calculate new x and y values. */ + f = f - xi; + g = g - eta; + dx = ( (-f*gy) + (g*fy) ) / ( (fx*gy) - (fy*gx) ); + dy = ( (-g*fx) + (f*gx) ) / ( (fx*gy) - (fy*gx) ); + x += dx; + y += dy; + +/* Check if convergence has been achieved. */ + if( fabs(dx) <= tol*fabs(x) && fabs(dy) <= tol*fabs(y) ) { + ok = 1; + break; + } + } + + *xx = x; + *yy = y; + + if( !ok ) return 2; + + } + + return 0; + +} + +/*--------------------------------------------------------------------------*/ + +int astTPNrev(x, y, prj, phi, theta) + +const double x, y; +double *phi, *theta; +struct AstPrjPrm *prj; + +{ + double r, xi, eta, x2, xy, y2, r2, x3, x2y, xy2, y3, r3, x4, x3y, + x2y2, xy3, y4, x5, x4y, x3y2, x2y3, xy4, y5, r5, x6, x5y, x4y2, + x3y3, x2y4, xy5, y6, x7, x6y, x5y2, x4y3, x3y4, x2y5, xy6, y7, + r7; + double *a, *b; + + if (abs(prj->flag) != TPN ) { + if (astTPNset(prj)) return 1; + } + + /* Simple tan */ + if( prj->w[ 0 ] == 0.0 ){ + xi = x; + eta = y; + + /* Tan with polynomial corrections. */ + } else { + x2 = x*x; + xy = x*y; + y2 = y*y; + + r2 = x2 + y2; + r = sqrt( r2 ); + + x3 = x2*x; + x2y = x2*y; + xy2 = x*y2; + y3 = y*y2; + + r3 = r*r2; + + x4 = x3*x; + x3y = x3*y; + x2y2 = x2*y2; + xy3 = x*y3; + y4 = y*y3; + + x5 = x4*x; + x4y = x4*y; + x3y2 = x3*y2; + x2y3 = x2*y3; + xy4 = x*y4; + y5 = y*y4; + + r5 = r3*r2; + + x6 = x5*x; + x5y = x5*y; + x4y2 = x4*y2; + x3y3 = x3*y3; + x2y4 = x2*y4; + xy5 = x*y5; + y6 = y*y5; + + x7 = x6*x; + x6y = x6*y; + x5y2 = x5*y2; + x4y3 = x4*y3; + x3y4 = x3*y4; + x2y5 = x2*y5; + xy6 = x*y6; + y7 = y*y6; + + r7 = r5*r2; + + a = prj->p2; + xi = a[0] + a[1]*x + a[2]*y + a[3]*r + a[4]*x2 + + a[5]*xy + a[6]*y2 + a[7]*x3 + a[8]*x2y + a[9]*xy2 + + a[10]*y3 + a[11]*r3 + a[12]*x4 + a[13]*x3y + a[14]*x2y2 + + a[15]*xy3 + a[16]*y4 + a[17]*x5 + a[18]*x4y + a[19]*x3y2 + + a[20]*x2y3 + a[21]*xy4 + a[22]*y5 + a[23]*r5 + a[24]*x6 + + a[25]*x5y + a[26]*x4y2 + a[27]*x3y3 + a[28]*x2y4 + a[29]*xy5 + + a[30]*y6 + a[31]*x7 + a[32]*x6y + a[33]*x5y2 + a[34]*x4y3 + + a[35]*x3y4 + a[36]*x2y5 + a[37]*xy6 + a[38]*y7 + a[39]*r7; + + b = prj->p; + eta = b[0] + b[1]*y + b[2]*x + b[3]*r + b[4]*y2 + + b[5]*xy + b[6]*x2 + b[7]*y3 + b[8]*xy2 + b[9]*x2y + + b[10]*x3 + b[11]*r3 + b[12]*y4 + b[13]*xy3 + b[14]*x2y2 + + b[15]*x3y + b[16]*x4 + b[17]*y5 + b[18]*xy4 + b[19]*x2y3 + + b[20]*x3y2 + b[21]*x4y + b[22]*x5 + b[23]*r5 + b[24]*y6 + + b[25]*xy5 + b[26]*x2y4 + b[27]*x3y3 + b[28]*x4y2 + b[29]*x5y + + b[30]*x6 + b[31]*y7 + b[32]*xy6 + b[33]*x2y5 + b[34]*x3y4 + + b[35]*x4y3 + b[36]*x5y2 + b[37]*x6y + b[38]*x7 + b[39]*r7; + + } + + /* Now do the tan projection */ + if( prj->n ) { + r = sqrt(xi*xi + eta*eta); + if (r == 0.0) { + *phi = 0.0; + } else { + *phi = astATan2d(xi, -eta); + } + *theta = astATan2d(prj->r0, r); + } else { + *phi = xi; + *theta = eta; + } + + return 0; +} + diff --git a/tranmap.c b/tranmap.c new file mode 100644 index 0000000..5abd75d --- /dev/null +++ b/tranmap.c @@ -0,0 +1,2327 @@ +/* +*class++ +* Name: +* TranMap + +* Purpose: +* Mapping with specified forward and inverse transformations. + +* Constructor Function: +c astTranMap +f AST_TRANMAP + +* Description: +* A TranMap is a Mapping which combines the forward transformation of +* a supplied Mapping with the inverse transformation of another +* supplied Mapping, ignoring the un-used transformation in each +* Mapping (indeed the un-used transformation need not exist). +* +* When the forward transformation of the TranMap is referred to, the +* transformation actually used is the forward transformation of the +* first Mapping supplied when the TranMap was constructed. Likewise, +* when the inverse transformation of the TranMap is referred to, the +* transformation actually used is the inverse transformation of the +* second Mapping supplied when the TranMap was constructed. + +* Inheritance: +* The TranMap class inherits from the Mapping class. + +* Attributes: +* The TranMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The TranMap class does not define any new functions beyond those +f The TranMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 10-FEB-2004 (DSB): +* Original version. +* 19-JAN-2005 (DSB): +* Fix memory leak. +* 14-FEB-2006 (DSB): +* - Over-ride the astDecompose method. +* - Fix bug in MapSplit related to use of invert flags. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 10-MAY-2006 (DSB): +* Override astEqual. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS TranMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "permmap.h" /* Coordinate permutation Mappings */ +#include "cmpmap.h" /* Compound Mappings */ +#include "unitmap.h" /* Unit Mappings */ +#include "tranmap.h" /* Interface definition for this class */ +#include "frame.h" /* Frames */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(TranMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(TranMap,Class_Init) +#define class_vtab astGLOBAL(TranMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstTranMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstTranMap *astTranMapId_( void *, void *, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *RemoveRegions( AstMapping *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +static int GetObjSize( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two TranMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "tranmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* TranMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two TranMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a TranMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the TranMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTranMap *that; + AstTranMap *this; + int nin; + int nout; + int result; + int that_inv1; + int that_inv2; + int this_inv1; + int this_inv2; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two TranMap structures. */ + this = (AstTranMap *) this_object; + that = (AstTranMap *) that_object; + +/* Check the second object is a TranMap. We know the first is a + TranMap since we have arrived at this implementation of the virtual + function. */ + if( astIsATranMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* Temporarily re-instate the original Invert flag values. */ + that_inv1 = astGetInvert( that->map1 ); + that_inv2 = astGetInvert( that->map2 ); + this_inv1 = astGetInvert( this->map1 ); + this_inv2 = astGetInvert( this->map2 ); + + astSetInvert( this->map1, this->invert1 ); + astSetInvert( this->map2, this->invert2 ); + astSetInvert( that->map1, that->invert1 ); + astSetInvert( that->map2, that->invert2 ); + +/* If the Invert flags for the two TranMaps differ, it may still be possible + for them to be equivalent. First compare the TranMaps if their Invert + flags are the same. In this case all the attributes of the two TranMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + if( astEqual( this->map1, that->map1 ) && + astEqual( this->map2, that->map2 ) ) { + result = 1; + } + +/* If the Invert flags for the two TranMaps differ, the attributes of the two + TranMaps must be inversely related to each other. */ + } else { + + astInvert( that->map1 ); + astInvert( that->map2 ); + + if( astEqual( this->map1, that->map2 ) && + astEqual( this->map2, that->map1 ) ) { + result = 1; + } + + } + +/* Restore the original Invert flag values. */ + astSetInvert( this->map1, this_inv1 ); + astSetInvert( this->map2, this_inv2 ); + astSetInvert( that->map1, that_inv1 ); + astSetInvert( that->map2, that_inv2 ); + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "tranmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* TranMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied TranMap, +* in bytes. + +* Parameters: +* this +* Pointer to the TranMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstTranMap *this; /* Pointer to TranMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the TranMap structure. */ + this = (AstTranMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astGetObjSize( this->map1 ); + result += astGetObjSize( this->map2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void Decompose( AstMapping *this_mapping, AstMapping **map1, + AstMapping **map2, int *series, int *invert1, + int *invert2, int *status ) { +/* +* +* Name: +* Decompose + +* Purpose: +* Decompose a Mapping into two component Mappings. + +* Type: +* Private function. + +* Synopsis: +* #include "tranmap.h" +* void Decompose( AstMapping *this, AstMapping **map1, +* AstMapping **map2, int *series, +* int *invert1, int *invert2, int *status ) + +* Class Membership: +* TranMap member function (over-rides the protected astDecompose +* method inherited from the Mapping class). + +* Description: +* This function returns pointers to the two Mappings encapsulated by +* a TranMap. + +* Parameters: +* this +* Pointer to the Mapping. +* map1 +* Address of a location to receive a pointer to first component +* Mapping (the forward Mapping). +* map2 +* Address of a location to receive a pointer to second component +* Mapping (the inverse Mapping). +* series +* Address of a location to receive a value indicating if the +* component Mappings are applied in series or parallel. A non-zero +* value means that the supplied Mapping is equivalent to applying map1 +* followed by map2 in series. A zero value means that the supplied +* Mapping is equivalent to applying map1 to the lower numbered axes +* and map2 to the higher numbered axes, in parallel. Zero is +* returned for a TranMap. +* invert1 +* The value of the Invert attribute to be used with map1. +* invert2 +* The value of the Invert attribute to be used with map2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component Mappings using the returned +* pointers will be reflected in the supplied Mapping. + +*- +*/ + + +/* Local Variables: */ + AstTranMap *this; /* Pointer to TranMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TranMap structure. */ + this = (AstTranMap *) this_mapping; + +/* If the TranMap has been inverted, return the Mappings in reverse + order with inverted Invert falgs. */ + if( astGetInvert( this ) ) { + if( map1 ) *map1 = astClone( this->map2 ); + if( map2 ) *map2 = astClone( this->map1 ); + if( invert1 ) *invert1 = this->invert2 ? 0 : 1; + if( invert2 ) *invert2 = this->invert1 ? 0 : 1; + +/* If the TranMap has not been inverted, return the Mappings in their + original order with their original Invert flags. */ + } else { + if( map1 ) *map1 = astClone( this->map1 ); + if( map2 ) *map2 = astClone( this->map2 ); + if( invert1 ) *invert1 = this->invert1; + if( invert2 ) *invert2 = this->invert2; + } +} + +void astInitTranMapVtab_( AstTranMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitTranMapVtab + +* Purpose: +* Initialise a virtual function table for a TranMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "tranmap.h" +* void astInitTranMapVtab( AstTranMapVtab *vtab, const char *name ) + +* Class Membership: +* TranMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the TranMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsATranMap) to determine if an object belongs to + this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* None. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + mapping->RemoveRegions = RemoveRegions; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_mapsplit = mapping->MapSplit; + mapping->MapSplit = MapSplit; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->Decompose = Decompose; + mapping->MapMerge = MapMerge; + mapping->Rate = Rate; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "TranMap", "Compound Transformation Mapping" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* TranMap member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstTranMap *this; /* Pointer to TranMap structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the TranMap structure. */ + this = (AstTranMap *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->map1, mode, extra, fail ); + if( !result ) result = astManageLock( this->map2, mode, extra, fail ); + + return result; + +} +#endif + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a TranMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* TranMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated TranMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated TranMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated TranMap which is to be merged with +* its neighbours. This should be a cloned copy of the TranMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* TranMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated TranMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpMap *cmap; /* Pointer to compound Mapping */ + AstMapping *cmap_f; /* Pointer to compound Mapping */ + AstMapping *cmap_i; /* Pointer to compound Mapping */ + AstMapping *hmap1; /* Pointer to 1st comp of higher TranMap */ + AstMapping *hmap2; /* Pointer to 2nd comp of higher TranMap */ + AstMapping *hmap_f; /* Pointer to fwd Mapping of higher TranMap */ + AstMapping *hmap_i; /* Pointer to inv Mapping of higher TranMap */ + AstMapping *map1; /* Pointer to 1st comp of nominated TranMap */ + AstMapping *map2; /* Pointer to 2nd comp of nominated TranMap */ + AstMapping *map_f; /* Pointer to fwd Mapping of nominated TranMap */ + AstMapping *map_i; /* Pointer to inv Mapping of nominated TranMap */ + AstMapping *smap; /* Pointer to simplified Mapping */ + AstMapping *smap_f; /* Pointer to simplified Mapping */ + AstMapping *smap_i; /* Pointer to simplified Mapping */ + AstTranMap *hmap; /* Pointer to higher TranMap */ + AstTranMap *map; /* Pointer to this TranMap */ + AstTranMap *new; /* Pointer to merged TranMap */ + int i; /* Loop count */ + int old_hinv1; /* Original Invert flag for hmap->map1 */ + int old_hinv2; /* Original Invert flag for hmap->map2 */ + int old_inv1; /* Original Invert flag for this->map1 */ + int old_inv2; /* Original Invert flag for this->map2 */ + int result; /* The value to return */ + +/* Initialise.*/ + result = -1; + +/* Check the inherited status. */ + if ( !astOK ) return result; + +/* Get a pointer to this TranMap. */ + map = (AstTranMap *) this; + +/* Get the two component Mappings,and temporarily set their Invert + attributes back to the values they had when the TranMap was created, + saving their current Invert values so that they can be re-instated later. */ + map1 = map->map1; + old_inv1 = astGetInvert( map1 ); + astSetInvert( map1, map->invert1 ); + + map2 = map->map2; + old_inv2 = astGetInvert( map2 ); + astSetInvert( map2, map->invert2 ); + +/* Simplify the TranMap on its own. */ +/* ================================ */ + +/* If the TranMap is inverted, creat an equal TranMap which is not inverted. + To do this, invert and swap the component Mappings. */ + if( ( *invert_list )[ where ] ) { + astInvert( map1 ); + astInvert( map2 ); + new = astTranMap( map2, map1, "", status ); + astInvert( map1 ); + astInvert( map2 ); + + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) new; + ( *invert_list )[ where ] = 0; + result = where; + +/* Otherwise, try to simplify each of the component Mappings. */ + } else { + smap_f = astSimplify( map1 ); + smap_i = astSimplify( map2 ); + +/* Assume some simplification took place if the pointers have changed. */ + if( smap_f != map1 || smap_i != map2 ) { + +/* Construct a new TranMap from these simplifgied Mappings. */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astTranMap( smap_f, smap_i, "", status ); + result = where; + +/* Otherwise, if the both component Mappings are defined in both directions... */ + } else if( astGetTranForward( map1 ) && astGetTranInverse( map1 ) && + astGetTranForward( map2 ) && astGetTranInverse( map2 ) ) { + +/* Form a series CmpMap from the two component Mappings, with the second + Mapping inverted. */ + astInvert( map2 ); + cmap = astCmpMap( map1, map2, 1, "", status ); + astInvert( map2 ); + +/* If this CmpMap simplifies to a UnitMap, then the two components of the + TranMap are equal, and so we can replace the entire TranMap with either + of its components. Note, we leave the supplied invert flag unchanged, + since the copycreated below refers to the Mapping as it was when the + TranMap was created. However, we invert the returned Mapping if + necessary. */ + smap = astSimplify( cmap ); + if( astIsAUnitMap( smap ) ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = astCopy( map1 ); + if( ( *invert_list )[ where ] ) astInvert( ( *map_list )[ where ] ); + result = where; + } + +/* Release resources. */ + smap = astAnnul( smap ); + cmap = astAnnul( cmap ); + } + +/* Release resources. */ + smap_f = astAnnul( smap_f ); + smap_i = astAnnul( smap_i ); + } + +/* Merge the TranMap with a neighbouring TranMap. */ +/* ============================================== */ +/* Only do this if no change was made above, and we are combining the + Mappings in series. */ + if( result == -1 && series ) { + +/* Is the higher neighbour a TranMap? */ + if( where < ( *nmap - 1 ) && + astIsATranMap( ( *map_list )[ where + 1 ] ) ){ + +/* Get the two component Mappings of the higher TranMap, and temporarily set + their Invert attributes back to the values they had when the TranMap was + created, saving their current Invert values so that they can be re-instated + later. */ + hmap = (AstTranMap *) ( *map_list )[ where + 1 ]; + + hmap1 = hmap->map1; + old_hinv1 = astGetInvert( hmap1 ); + astSetInvert( hmap1, hmap->invert1 ); + + hmap2 = hmap->map2; + old_hinv2 = astGetInvert( hmap2 ); + astSetInvert( hmap2, hmap->invert2 ); + +/* Get the Mappings which defines the forward and inverse transformation of + the lower TranMap ("this"). Then, map_f and map_i are pointers to + Mappings which could be used to construct a new TranMap which would be + equivalent to "this" with the supplied invert setting. */ + if( ( *invert_list )[ where ] ) { + map_f = map2; + map_i = map1; + astInvert( map_f ); + astInvert( map_i ); + } else { + map_f = map1; + map_i = map2; + } + +/* Likewise, get the Mappings which defines the forward and inverse + transformation of the higher TranMap. */ + if( ( *invert_list )[ where + 1 ] ) { + hmap_f = hmap2; + hmap_i = hmap1; + astInvert( hmap_f ); + astInvert( hmap_i ); + } else { + hmap_f = hmap1; + hmap_i = hmap2; + } + +/* Combine the two forward Mappings together into a series CmpMap, and + simplify it. */ + cmap_f = (AstMapping *) astCmpMap( map_f, hmap_f, 1, "", status ); + smap_f = astSimplify( cmap_f ); + +/* Do the same for the inverse Mappings */ + cmap_i = (AstMapping *) astCmpMap( map_i, hmap_i, 1, "", status ); + smap_i = astSimplify( cmap_i ); + +/* Was any simplification performed? We assume this is the case if the + either of the simplied pointer differs from the original pointer. */ + if( cmap_f != smap_f || cmap_i != smap_i ) { + +/* In which case,construct a new TranMap from the simplified Mappings. */ + new = astTranMap( smap_f, smap_i, "", status ); + + } else { + new = NULL; + } + +/* Free resources.*/ + cmap_f = astAnnul( cmap_f ); + smap_f = astAnnul( smap_f ); + cmap_i = astAnnul( cmap_i ); + smap_i = astAnnul( smap_i ); + +/* Re-instate the original Invert values for the component Mappings of + the higher TranMap. */ + astSetInvert( hmap1, old_hinv1 ); + astSetInvert( hmap2, old_hinv2 ); + +/* If we have a new TranMap, annul the first of the two Mappings, and replace + it with the merged TranMap. Also set the invert flag. */ + if( new ) { + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) new; + ( *invert_list )[ where ] = 0; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ where + 1 ] ); + for ( i = where + 2; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = where; + } + } + } + +/* Re-instate the original Invert values for the component Mappings. */ + astSetInvert( map1, old_inv1 ); + astSetInvert( map2, old_inv2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = -1; + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* TranMap. + +* Type: +* Private function. + +* Synopsis: +* #include "tranmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* TranMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing TranMap. This is only possible if the specified inputs +* correspond to some subset of the TranMap outputs. That is, there +* must exist a subset of the TranMap outputs for which each output +* depends only on the selected TranMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied TranMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the TranMap to be split (the TranMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied TranMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied TranMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied TranMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstMapping *fmap; /* Pointer to forward Mapping in supplied TranMap */ + AstMapping *imap; /* Pointer to inverse Mapping in supplied TranMap */ + AstMapping *rfmap; /* Pointer to split forward Mapping */ + AstMapping *rimap; /* Pointer to split inverse Mapping */ + AstTranMap *this; /* Pointer to TranMap structure */ + int *ires; /* I/ps of inv Mapping dependent on selected o/ps */ + int *out; /* O/ps of fwd Mapping dependent on selected i/ps */ + int *result; /* Pointer to returned array */ + int finv; /* Invert flag to use with fmap */ + int i; /* Loop count */ + int iinv; /* Invert flag to use with imap */ + int nout; /* No. of outputs dependent on selected inputs */ + int ok; /* Can required Mapping be created? */ + int old_finv; /* Original Invert flag for fmap */ + int old_iinv; /* Original Invert flag for imap */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the parent astMapSplit method to see if it can do the job. */ + result = (*parent_mapsplit)( this_map, nin, in, map, status ); + +/* If not, we provide a special implementation here. */ + if( !result ) { + +/* Get a pointer to the TranMap structure. */ + this = (AstTranMap *) this_map; + +/* Get pointers to the forward and inverse Mappings, taking into account + whether the TranMap has been inverted. */ + if( !astGetInvert( this ) ) { + fmap = this->map1; + finv = this->invert1; + imap = this->map2; + iinv = this->invert2; + } else { + imap = this->map1; + iinv = !( this->invert1 ); + fmap = this->map2; + finv = !( this->invert2 ); + } + +/* Temporarily set the Invert flag of both Mappings back to their + original values. */ + old_finv = astGetInvert( fmap ); + astSetInvert( fmap, finv ); + old_iinv = astGetInvert( imap ); + astSetInvert( imap, iinv ); + +/* Try to split the forward Mapping. */ + out = astMapSplit( fmap, nin, in, &rfmap ); + +/* Check the split could be done. */ + if( out ) { + +/* Get the number of outputs which are fed by the selected inputs. */ + nout = astGetNout( rfmap ); + +/* See if the inverse Mapping can be split using these outputs as inputs. */ + astInvert( imap ); + ires = astMapSplit( imap, nout, out, &rimap ); + astInvert( imap ); + if( ires ) { + astInvert( rimap ); + +/* Check that the resulting inputs are the same as the supplied inputs. */ + if( astGetNin( rimap ) == nin ) { + ok = 1; + for( i = 0; i < nin; i++ ) { + if( in[ i ] != ires[ i ] ) { + ok = 0; + break; + } + } + +/* If so create the required new TranMap. */ + if( ok ) { + *map = (AstMapping *) astTranMap( rfmap, rimap, "", status ); + result = out; + } + } + +/* Free resources. */ + ires = astFree( ires ); + rimap = astAnnul( rimap ); + } + + if( !result ) out = astFree( out ); + rfmap = astAnnul( rfmap ); + } + +/* Re-instate the Invert flags of the component Mappings. */ + astSetInvert( fmap, old_finv ); + astSetInvert( imap, old_iinv ); + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "tranmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* TranMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. Also evaluates the second derivative. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + +/* Local Variables: */ + AstTranMap *map; + AstMapping *cmap; + double result; + int cinv; + int old_inv; + +/* Check inherited status */ + if( !astOK ) return AST__BAD; + +/* Get a pointer to the TranMap structure. */ + map = (AstTranMap *) this; + +/* Choose the component Mapping to use, and get its original Invert + value. Invert this if the TranMap itself has been inverted (this is + because the astRate function has no "invert" argument so we need to + invert the Mapping before calling astRate). */ + if( astGetInvert( this ) ) { + cmap = map->map2; + cinv = !(map->invert2); + } else { + cmap = map->map1; + cinv = map->invert1; + } + +/* Temporarily set the Invert flag of the component Mapping back to its + original value. */ + old_inv = astGetInvert( cmap ); + astSetInvert( cmap, cinv ); + +/* Use the astRate method of the component Mapping. */ + result = astRate( cmap, at, ax1, ax2 ); + +/* Re-instate the Invert flag of the component Mapping. */ + astSetInvert( cmap, old_inv ); + +/* Return the result. */ + return result; +} + +static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) { +/* +* Name: +* RemoveRegions + +* Purpose: +* Remove any Regions from a Mapping. + +* Type: +* Private function. + +* Synopsis: +* #include "tranmap.h" +* AstMapping *RemoveRegions( AstMapping *this, int *status ) + +* Class Membership: +* TranMap method (over-rides the astRemoveRegions method inherited +* from the Mapping class). + +* Description: +* This function searches the supplied Mapping (which may be a +* compound Mapping such as a TranMap) for any component Mappings +* that are instances of the AST Region class. It then creates a new +* Mapping from which all Regions have been removed. If a Region +* cannot simply be removed (for instance, if it is a component of a +* parallel TranMap), then it is replaced with an equivalent UnitMap +* in the returned Mapping. +* +* The implementation provided by the TranMap class invokes the +* astRemoveRegions method on the two component Mappings, and joins +* the results together into a new TranMap. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the modified mapping. + +* Notes: +* - A NULL pointer value will be returned if this function is +* invoked with the AST error status set, or if it should fail for +* any reason. +*/ + +/* Local Variables: */ + AstTranMap *new; /* Pointer to new TranMap */ + AstTranMap *this; /* Pointer to TranMap structure */ + AstMapping *newmap1; /* New first component Mapping */ + AstMapping *newmap2; /* New second component Mapping */ + AstMapping *result; /* Result pointer to return */ + int nax; /* Number of Frame axes */ + int unit1; /* Is new first Mapping a UnitMap? */ + int unit2; /* Is new second Mapping a UnitMap? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the TranMap. */ + this = (AstTranMap *) this_mapping; + +/* Invoke the astRemoveRegions method on the two component Mappings. */ + newmap1 = astRemoveRegions( this->map1 ); + newmap2 = astRemoveRegions( this->map2 ); + +/* If neither component was modified, just return a clone of the supplied + pointer. */ + if( this->map1 == newmap1 && this->map2 == newmap2 ) { + result = astClone( this ); + +/* Otherwise, we need to create a new Mapping to return. */ + } else { + +/* The implementation of the astRemoveRegions method provided by the + Region class returns a Frame rather than a UnitMap. But we need + Mappings here, not Frames. So if either of these new Mappings is + a Frame, replace it with an equivalent UnitMap. Also, get flags + indicating if either Mapping is a UnitMap.*/ + if( astIsAFrame( newmap1 ) ) { + nax = astGetNin( newmap1 ); + (void) astAnnul( newmap1 ); + newmap1 = (AstMapping *) astUnitMap( nax, " ", status ); + unit1 = 1; + } else { + unit1 = astIsAUnitMap( newmap1 ); + } + + if( astIsAFrame( newmap2 ) ) { + nax = astGetNin( newmap2 ); + (void) astAnnul( newmap2 ); + newmap2 = (AstMapping *) astUnitMap( nax, " ", status ); + unit2 = 1; + } else { + unit2 = astIsAUnitMap( newmap2 ); + } + +/* If both new Mappings are UnitMaps, return an equivalent UnitMap. */ + if( unit1 && unit2 ) { + result = (AstMapping *) astUnitMap( astGetNin( newmap1 ) + + astGetNin( newmap2 ), " ", + status ); + +/* Otherwise, return a new TranMap containing the two new Mappings. */ + } else { + new = astCopy( this ); + (void) astAnnul( new->map1 ); + (void) astAnnul( new->map2 ); + new->map1 = astClone( newmap1 ); + new->map2 = astClone( newmap2 ); + result = (AstMapping *) new; + } + } + +/* Free resources. */ + newmap1 = astAnnul( newmap1 ); + newmap2 = astAnnul( newmap2 ); + +/* Annul the returned Mapping if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a TranMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "tranmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* TranMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a TranMap and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Mapping. +* This implies applying each of the TranMap's component Mappings in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the TranMap. +* in +* Pointer to the PointSet associated with the input coordinate values. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the TranMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstMapping *cmap; /* Mapping which defines the required transformation */ + AstPointSet *result; /* Pointer to output PointSet */ + AstTranMap *map; /* Pointer to TranMap to be applied */ + int cinv; /* Invert flag when TranMap was created */ + int old_inv; /* Invert flag on entry to this function */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the TranMap. */ + map = (AstTranMap *) this; + +/* Apply the parent Mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We now extend the parent astTransform method by applying the component + Mappings of the TranMap to generate the output coordinate values. */ + +/* Determine whether to apply the forward or inverse Mapping, according to the + direction specified and whether the Mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Choose the component Mapping to use, and get its original Invert value. */ + if( forward ) { + cmap = map->map1; + cinv = map->invert1; + }else { + cmap = map->map2; + cinv = map->invert2; + } + +/* Temporarily set the Invert flag of the component Mapping back to its + original value. */ + old_inv = astGetInvert( cmap ); + astSetInvert( cmap, cinv ); + +/* Use the Transform method of the component Mapping. */ + result = astTransform( cmap, in, forward, out ); + +/* Re-instate the Invert flag of the component Mapping. */ + astSetInvert( cmap, old_inv ); + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for TranMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for TranMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Mappings within the TranMap. +*/ + +/* Local Variables: */ + AstTranMap *in; /* Pointer to input TranMap */ + AstTranMap *out; /* Pointer to output TranMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output TranMaps. */ + in = (AstTranMap *) objin; + out = (AstTranMap *) objout; + +/* For safety, start by clearing any references to the input component + Mappings from the output TranMap. */ + out->map1 = NULL; + out->map2 = NULL; + +/* Make copies of these Mappings and store pointers to them in the output + TranMap structure. */ + out->map1 = astCopy( in->map1 ); + out->map2 = astCopy( in->map2 ); +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for TranMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for TranMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstTranMap *this; /* Pointer to TranMap */ + +/* Obtain a pointer to the TranMap structure. */ + this = (AstTranMap *) obj; + +/* Annul the pointers to the component Mappings. */ + this->map1 = astAnnul( this->map1 ); + this->map2 = astAnnul( this->map2 ); + +/* Clear the remaining TranMap variables. */ + this->invert1 = 0; + this->invert2 = 0; +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for TranMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the TranMap class to an output Channel. + +* Parameters: +* this +* Pointer to the TranMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstTranMap *this; /* Pointer to the TranMap structure */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the TranMap structure. */ + this = (AstTranMap *) this_object; + +/* Write out values representing the instance variables for the TranMap + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* First Invert flag. */ +/* ------------------ */ + ival = this->invert1; + set = ( ival != 0 ); + astWriteInt( channel, "InvA", set, 0, ival, + ival ? "First Mapping used in inverse direction" : + "First Mapping used in forward direction" ); + +/* Second Invert flag. */ +/* ------------------- */ + ival = this->invert2; + set = ( ival != 0 ); + astWriteInt( channel, "InvB", set, 0, ival, + ival ? "Second Mapping used in inverse direction" : + "Second Mapping used in forward direction" ); + +/* First Mapping. */ +/* -------------- */ + astWriteObject( channel, "MapA", 1, 1, this->map1, + "Mapping for forward transformation" ); + +/* Second Mapping. */ +/* --------------- */ + astWriteObject( channel, "MapB", 1, 1, this->map2, + "Mapping for inverse transformation" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsATranMap and astCheckTranMap functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(TranMap,Mapping) +astMAKE_CHECK(TranMap) + +AstTranMap *astTranMap_( void *map1_void, void *map2_void, const char *options, int *status, ...) { +/* +*+ +* Name: +* astTranMap + +* Purpose: +* Create a TranMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "tranmap.h" +* AstTranMap *astTranMap( AstMapping *map1, AstMapping *map2, const char *options, int *status, ... ) + +* Class Membership: +* TranMap constructor. + +* Description: +* This function creates a new TranMap and optionally initialises its +* attributes. + +* Parameters: +* map1 +* Pointer to the first Mapping (which deinfes the forward +* transformation). +* map2 +* Pointer to the second Mapping (which deinfes the inverse +* transformation). +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new TranMap. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new TranMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- + +* Implementation Notes: +* - This function implements the basic TranMap constructor which is +* available via the protected interface to the TranMap class. A +* public interface is provided by the astTranMapId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "map1" and "map2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTranMap *new; /* Pointer to new TranMap */ + AstMapping *map1; /* Pointer to first Mapping structure */ + AstMapping *map2; /* Pointer to second Mapping structure */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Mapping structures provided. */ + map1 = astCheckMapping( map1_void ); + map2 = astCheckMapping( map2_void ); + if ( astOK ) { + +/* Initialise the TranMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitTranMap( NULL, sizeof( AstTranMap ), !class_init, &class_vtab, + "TranMap", map1, map2 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new TranMap's + attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new TranMap. */ + return new; +} + +AstTranMap *astTranMapId_( void *map1_void, void *map2_void, + const char *options, ... ) { +/* +*++ +* Name: +c astTranMap +f AST_TRANMAP + +* Purpose: +* Create a TranMap. + +* Type: +* Public function. + +* Synopsis: +c #include "tranmap.h" +c AstTranMap *astTranMap( AstMapping *map1, AstMapping *map2, +c const char *options, ... ) +f RESULT = AST_TRANMAP( MAP1, MAP2, OPTIONS, STATUS ) + +* Class Membership: +* TranMap constructor. + +* Description: +* This function creates a new TranMap and optionally initialises +* its attributes. +* +* A TranMap is a Mapping which combines the forward transformation of +* a supplied Mapping with the inverse transformation of another +* supplied Mapping, ignoring the un-used transformation in each +* Mapping (indeed the un-used transformation need not exist). +* +* When the forward transformation of the TranMap is referred to, the +* transformation actually used is the forward transformation of the +* first Mapping supplied when the TranMap was constructed. Likewise, +* when the inverse transformation of the TranMap is referred to, the +* transformation actually used is the inverse transformation of the +* second Mapping supplied when the TranMap was constructed. + +* Parameters: +c map1 +f MAP1 = INTEGER (Given) +* Pointer to the first component Mapping, which defines the +* forward transformation. +c map2 +f MAP2 = INTEGER (Given) +* Pointer to the second component Mapping, which defines the +* inverse transformation. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new TranMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new TranMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTranMap() +f AST_TRANMAP = INTEGER +* A pointer to the new TranMap. + +* Notes: +* - The number of output coordinates generated by the two Mappings +* (their Nout attribute) must be equal, as must the number of input +* coordinates accepted by each Mapping (their Nin attribute). +* - The forward transformation of the first Mapping must exist. +* - The inverse transformation of the second Mapping must exist. +c - Note that the component Mappings supplied are not copied by +c astTranMap (the new TranMap simply retains a reference to +c them). They may continue to be used for other purposes, but +c should not be deleted. If a TranMap containing a copy of its +c component Mappings is required, then a copy of the TranMap should +c be made using astCopy. +f - Note that the component Mappings supplied are not copied by +f AST_TRANMAP (the new TranMap simply retains a reference to +f them). They may continue to be used for other purposes, but +f should not be deleted. If a TranMap containing a copy of its +f component Mappings is required, then a copy of the TranMap should +f be made using AST_COPY. +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astTranMap constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astTranMap_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "map1" and "map2" parameters +* are of type (void *) and are converted from an ID value to a +* pointer and validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astTranMap_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTranMap *new; /* Pointer to new TranMap */ + AstMapping *map1; /* Pointer to first Mapping structure */ + AstMapping *map2; /* Pointer to second Mapping structure */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain the Mapping pointers from the ID's supplied and validate the + pointers to ensure they identify valid Mappings. */ + map1 = astVerifyMapping( astMakePointer( map1_void ) ); + map2 = astVerifyMapping( astMakePointer( map2_void ) ); + if ( astOK ) { + +/* Initialise the TranMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitTranMap( NULL, sizeof( AstTranMap ), !class_init, &class_vtab, + "TranMap", map1, map2 ); + +/* If successful, note that the virtual function table has been initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new TranMap's + attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return an ID value for the new TranMap. */ + return astMakeId( new ); +} + +AstTranMap *astInitTranMap_( void *mem, size_t size, int init, + AstTranMapVtab *vtab, const char *name, + AstMapping *map1, AstMapping *map2, int *status ) { +/* +*+ +* Name: +* astInitTranMap + +* Purpose: +* Initialise a TranMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "tranmap.h" +* AstTranMap *astInitTranMap( void *mem, size_t size, int init, +* AstTranMapVtab *vtab, const char *name, +* AstMapping *map1, AstMapping *map2 ) + +* Class Membership: +* TranMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new TranMap object. It allocates memory (if necessary) to +* accommodate the TranMap plus any additional data associated with the +* derived class. It then initialises a TranMap structure at the start +* of this memory. If the "init" flag is set, it also initialises the +* contents of a virtual function table for a TranMap at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the TranMap is to be initialised. +* This must be of sufficient size to accommodate the TranMap data +* (sizeof(TranMap)) plus any data used by the derived class. If a +* value of NULL is given, this function will allocate the memory itself +* using the "size" parameter to determine its size. +* size +* The amount of memory used by the TranMap (plus derived class +* data). This will be used to allocate memory if a value of NULL is +* given for the "mem" parameter. This value is also stored in the +* TranMap structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the TranMap's virtual function table +* is to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new TranMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* map1 +* Pointer to the first Mapping. +* map2 +* Pointer to the second Mapping. + +* Returned Value: +* A pointer to the new TranMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstTranMap *new; /* Pointer to new TranMap */ + int nin; /* No. input coordinates for TranMap */ + int nout; /* No. output coordinates for TranMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitTranMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Report an error if map1 has no forward transformation. */ + if( !astGetTranForward( map1 ) && astOK ) { + astError( AST__INTRD, "astInitTranMap(%s): The first supplied Mapping " + "is not able to transform coordinates in the forward direction.", status, + name ); + } + +/* Report an error if map2 has no inverse transformation. */ + if( !astGetTranInverse( map2 ) && astOK ) { + astError( AST__INTRD, "astInitTranMap(%s): The second supplied Mapping " + "is not able to transform coordinates in the inverse direction.", status, + name ); + } + +/* Check that the number of coordinates are compatible and report an error if + they are not. */ + nout = astGetNout( map1 ); + if ( astGetNout( map2 ) != nout && astOK ) { + astError( AST__INNCO, "astInitTranMap(%s): The number of output " + "coordinates per point (%d) for the first Mapping " + "supplied does not match the number of output " + "coordinates (%d) for the second Mapping.", status, name, nout, + astGetNout( map2 ) ); + } + + nin = astGetNin( map1 ); + if ( astGetNin( map2 ) != nin && astOK ) { + astError( AST__INNCO, "astInitTranMap(%s): The number of input " + "coordinates per point (%d) for the first Mapping " + "supplied does not match the number of input " + "coordinates (%d) for the second Mapping.", status, name, nin, + astGetNin( map2 ) ); + } + +/* Initialise a Mapping structure (the parent class) as the first component + within the TranMap structure, allocating memory if necessary. Specify + the number of input and output coordinates and in which directions the + Mapping should be defined. */ + if ( astOK ) { + new = (AstTranMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + nin, nout, 1, 1 ); + + if ( astOK ) { + +/* Initialise the TranMap data. */ +/* --------------------------- */ +/* Store pointers to the component Mappings. */ + new->map1 = astClone( map1 ); + new->map2 = astClone( map2 ); + +/* Save the initial values of the inversion flags for these Mappings. */ + new->invert1 = astGetInvert( map1 ); + new->invert2 = astGetInvert( map2 ); + +/* If an error occurred, clean up by annulling the Mapping pointers and + deleting the new object. */ + if ( !astOK ) { + new->map1 = astAnnul( new->map1 ); + new->map2 = astAnnul( new->map2 ); + new = astDelete( new ); + } + } + } + +/* Return a pointer to the new object. */ + return new; +} + +AstTranMap *astLoadTranMap_( void *mem, size_t size, + AstTranMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadTranMap + +* Purpose: +* Load a TranMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "tranmap.h" +* AstTranMap *astLoadTranMap( void *mem, size_t size, +* AstTranMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* TranMap loader. + +* Description: +* This function is provided to load a new TranMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* TranMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a TranMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the TranMap is to be +* loaded. This must be of sufficient size to accommodate the +* TranMap data (sizeof(TranMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the TranMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the TranMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstTranMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new TranMap. If this is NULL, a pointer to +* the (static) virtual function table for the TranMap class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "TranMap" is used instead. + +* Returned Value: +* A pointer to the new TranMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstTranMap *new; /* Pointer to the new TranMap */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this TranMap. In this case the + TranMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstTranMap ); + vtab = &class_vtab; + name = "TranMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitTranMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built TranMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "TranMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* First Invert flag. */ +/* ------------------ */ + new->invert1 = astReadInt( channel, "inva", 0 ); + new->invert1 = ( new->invert1 != 0 ); + +/* Second Invert flag. */ +/* ------------------- */ + new->invert2 = astReadInt( channel, "invb", 0 ); + new->invert2 = ( new->invert2 != 0 ); + +/* First Mapping. */ +/* -------------- */ + new->map1 = astReadObject( channel, "mapa", NULL ); + +/* Second Mapping. */ +/* --------------- */ + new->map2 = astReadObject( channel, "mapb", NULL ); + +/* If an error occurred, clean up by deleting the new TranMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new TranMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* None. */ + + + + diff --git a/tranmap.h b/tranmap.h new file mode 100644 index 0000000..ae16f18 --- /dev/null +++ b/tranmap.h @@ -0,0 +1,276 @@ +#if !defined( TRANMAP_INCLUDED ) /* Include this file only once */ +#define TRANMAP_INCLUDED +/* +*+ +* Name: +* tranmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the TranMap class. + +* Invocation: +* #include "tranmap.h" + +* Description: +* This include file defines the interface to the TranMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. + +* Inheritance: +* The TranMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astMapMerge +* Merge a TranMap within a sequence of Mappings. +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsATranMap +* Test class membership. +* astTranMap +* Create a TranMap. +* +* Protected: +* astCheckTranMap +* Validate class membership. +* astInitTranMap +* Initialise a TranMap. +* astInitTranMapVtab +* Initialise the virtual function table for the TranMap class. +* astLoadTranMap +* Load a TranMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstTranMap +* TranMap object type. +* +* Protected: +* AstTranMapVtab +* TranMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 10-FEB-2004 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate Mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* TranMap structure. */ +/* ----------------- */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstTranMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + AstMapping *map1; /* Pointer to first Mapping */ + AstMapping *map2; /* Pointer to second Mapping */ + int invert1; /* Inversion flag for first Mapping */ + int invert2; /* Inversion flag for second Mapping */ +} AstTranMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstTranMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +/* None. */ +} AstTranMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstTranMapGlobals { + AstTranMapVtab Class_Vtab; + int Class_Init; +} AstTranMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitTranMapGlobals_( AstTranMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(TranMap) /* Check class membership */ +astPROTO_ISA(TranMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstTranMap *astTranMap_( void *, void *, const char *, int *, ...); +#else +AstTranMap *astTranMapId_( void *, void *, const char *, ... )__attribute__((format(printf,3,4))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstTranMap *astInitTranMap_( void *, size_t, int, AstTranMapVtab *, + const char *, AstMapping *, AstMapping *, int * ); + +/* Vtab initialiser. */ +void astInitTranMapVtab_( AstTranMapVtab *, const char *, int * ); + +/* Loader. */ +AstTranMap *astLoadTranMap_( void *, size_t, AstTranMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +/* None. */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckTranMap(this) astINVOKE_CHECK(TranMap,this,0) +#define astVerifyTranMap(this) astINVOKE_CHECK(TranMap,this,1) + +/* Test class membership. */ +#define astIsATranMap(this) astINVOKE_ISA(TranMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astTranMap astINVOKE(F,astTranMap_) +#else +#define astTranMap astINVOKE(F,astTranMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitTranMap(mem,size,init,vtab,name,map1,map2) \ +astINVOKE(O,astInitTranMap_(mem,size,init,vtab,name,astCheckMapping(map1),astCheckMapping(map2),STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitTranMapVtab(vtab,name) astINVOKE(V,astInitTranMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadTranMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadTranMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckTranMap to validate TranMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +/* None. */ +#endif + + + + + diff --git a/unit.c b/unit.c new file mode 100644 index 0000000..bec4b0a --- /dev/null +++ b/unit.c @@ -0,0 +1,6218 @@ +/* +* Name: +* unit.c + +* Purpose: +* Implement unit conversion functions. + +* Description: +* This file implements the Unit module which is used for identifying +* units and transforming between them. It follows the recommendations +* for unit handling contained within FITS WCS paper I (Greisen & +* Calabretta). All methods have protected access. + +* Methods: +* astUnitMapper: Create a Mapping between two systems of units. +* astUnitLabel: Returns a label for a given unit symbol. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 10-DEC-2002 (DSB): +* Original version. +* 10-FEB-2004 (DSB): +* Added debug conditional code to keep track of memory leaks. +* 15-JUL-2004 (DSB): +* In astUnitMapper: if no Mapping can be found from input to +* output units (e.g. because fo the less than perfect simplication +* algorithm in SimplifyTree), try finding a Mapping from output to +* input units and inverting the result. +* 14-DEC-2004 (DSB): +* In CreateTree, move the invocation of FixConstants from after +* InvertConstants to before InvertConstants. This is because +* InvertConstants ignores nodes which contain all constant +* arguments. This results in constants not being inverted in +* expressions such as "1/60 deg" (because all arguments are +* constant in the the "1/60" node). +* 18-JAN-2005 (DSB): +* Fix memory leaks. +* 2-FEB-2005 (DSB): +* - Avoid using astStore to allocate more storage than is supplied +* in the "data" pointer. This can cause access violations since +* astStore will then read beyond the end of the "data" area. +* 15-FEB-2005 (DSB): +* - Modified CleanExp to fix up some common units mistakes. +* 21-FEB-2005 (DSB): +* - Modified CleanExp to accept as equivalent to +* ^. +* - Modified MakeTree to do case insensitive checking if case +* sensitive checking failsto produce a match to a multiplier/unit +* symbol combination. If this still produces no match, do a case +* insensitive match for multiplier/unit label. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 6-APR-2006 (DSB): +* Modify CleanExp to convert "MJY/STER" to standard form ("MJy/sr"). +* 7-JUL-2006 (DSB): +* Correct initialisation of "word" flag in CleanExp. +* 17-MAY-2007 (DSB): +* Simplify the units string returned by astUnitNormaliser. +* - Fix indexing bug in CombineFactors. +* 26-MAY-2007 (DSB): +* - Correct error reporting in astUNitNormaliser. +* - Fix bug in CleanExp that caused (for example) "-2" to be +* converted to "^-2". +* 2-DEC-2008 (DSB): +* Correct memory allocation bug in CleanExp. +* 6-MAY-2011 (DSB): +* Include "adu" as basic unit. +* 9-MAY-2011 (DSB): +* Change "A" to be Ampere (as defined by FITS-WCS paper 1) rather +* than "Angstrom". +*/ + +/* Module Macros. */ +/* ============== */ +/* Define the astCLASS macro (even although this is not a class + implementation) to obtain access to the protected error and memory + handling functions. */ +#define astCLASS +#define PI 3.141592653589793 +#define E 2.718281828459045 + +/* Macro which returns the nearest integer to a given floating point + value. */ +#define NINT(x) (int)((x)+(((x)>0.0)?0.5:-0.5)) + +/* Macro identifying a character as lower or upper case letter, digit or ++ or -. */ +#define ISWORD(c) (isalnum(c)||((c)=='+')||((c)=='-')) + +/* The number of basic dimension quantities used for dimensional analysis. + In addition to the usual M, L and T, this includes pseudo-dimensions + describing strictly dimensionless quantities such as plane angle, + magnitude, etc. */ +#define NQUANT 10 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" +#include "memory.h" +#include "pointset.h" +#include "mapping.h" +#include "unitmap.h" +#include "zoommap.h" +#include "mathmap.h" +#include "unit.h" + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include + +#ifdef THREAD_SAFE +#include +#endif + +/* Module Type Definitions. */ +/* ======================== */ + +/* This declaration enumerates the codes for the operations which may + legally be included in a units expression. */ +typedef enum { + OP_LDCON, /* Load constant */ + OP_LDVAR, /* Load variable */ + OP_LOG, /* Common logarithm */ + OP_LN, /* Natural logarithm */ + OP_EXP, /* Natural exponential */ + OP_SQRT, /* Square root */ + OP_POW, /* Exponentiate */ + OP_DIV, /* Division */ + OP_MULT, /* Multiplication */ + OP_LDPI, /* Load constant PI */ + OP_LDE, /* Load constant E */ + OP_NULL /* Null operation */ +} Oper; + +/* A structure describing a standard multiplier prefix. */ +typedef struct Multiplier { + const char *label; /* Multipler label string (null terminated) */ + const char *sym; /* Multipler symbol string (null terminated) */ + int symlen; /* Length of symbol (without trailing null ) */ + int lablen; /* Length of label (without trailing null ) */ + double scale; /* The scale factor associated with the prefix */ + struct Multiplier *next; /* Next Multiplier in linked list */ +} Multiplier; + +/* A structure describing a single node in a tree representation of a + single units expression. */ +typedef struct UnitNode { + Oper opcode; /* Code for operation performed by this node */ + int narg; /* No. of arguments used by the operation */ + struct UnitNode **arg; /* Array of pointers to argument nodes */ + double con; /* Constant to be loaded by OP_LDCON operations */ + struct KnownUnit *unit; /* Known unit referred to by OP_LDVAR nodes */ + Multiplier *mult; /* Multiplier used by OP_LDVAR nodes */ + const char *name; /* User-defined unit referred to by OP_LDVAR + nodes (no multiplier prefix included) */ +} UnitNode; + +/* A structure describing a known unit. */ +typedef struct KnownUnit { + const char *sym; /* Unit symbol string (null terminated) */ + const char *label; /* Unit label string (null terminated) */ + int symlen; /* Length of symbol (without trailing null ) */ + int lablen; /* Length of label (without trailing null ) */ + struct UnitNode *head; /* Head of definition tree (NULL for basic units) */ + struct KnownUnit *next; /* Next KnownUnit in linked list */ + struct KnownUnit *use; /* KnownUnit to be used in place of this one */ +} KnownUnit; + +/* Module Variables. */ +/* ================= */ + +/* A pointer to the KnownUnit structure at the head of a linked list of + such structures containing definitions of all known units. */ +static KnownUnit *known_units = NULL; + +/* An array of pointers to KnownUnits which list the basic quantities +used in dimensional analysis. */ +static KnownUnit *quant_units[ NQUANT ]; + +/* A pointer to the Multiplier structure at the head of a linked list of + such structures containing definitions of all known multipliers. */ +static Multiplier *multipliers = NULL; + +/* Set up mutexes */ +#ifdef THREAD_SAFE + +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ); +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ); + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +#else + +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#endif + +/* Prototypes for Private Functions. */ +/* ================================= */ +static AstMapping *MakeMapping( UnitNode *, int * ); +static KnownUnit *GetKnownUnits( int, int * ); +static Multiplier *GetMultipliers( int * ); +static UnitNode *ConcatTree( UnitNode *, UnitNode *, int * ); +static UnitNode *CopyTree( UnitNode *, int * ); +static UnitNode *CreateTree( const char *, int, int, int * ); +static UnitNode *FixUnits( UnitNode *, UnitNode *, int * ); +static UnitNode *FreeTree( UnitNode *, int * ); +static UnitNode *MakeTree( const char *, int, int, int * ); +static UnitNode *MakeLabelTree( const char *, int, int * ); +static UnitNode *NewNode( UnitNode *, Oper, int * ); +static UnitNode *CombineFactors( UnitNode **, double *, int, double, int * ); +static const char *CleanExp( const char *, int * ); +static int EndsWith( const char *, int, const char *, int * ); +static int CmpTree( UnitNode *, UnitNode *, int, int * ); +static void FixConstants( UnitNode **, int, int * ); +static void InvertConstants( UnitNode **, int * ); +static UnitNode *InvertTree( UnitNode *, UnitNode *, int * ); +static void LocateUnits( UnitNode *, UnitNode ***, int *, int * ); +static void MakeKnownUnit( const char *, const char *, const char *, int * ); +static void MakeUnitAlias( const char *, const char *, int * ); +static void RemakeTree( UnitNode **, int * ); +static int SimplifyTree( UnitNode **, int, int * ); +static int ComplicateTree( UnitNode **, int * ); +static int ReplaceNode( UnitNode *, UnitNode *, UnitNode *, int * ); +static void FindFactors( UnitNode *, UnitNode ***, double **, int *, double *, int * ); +static const char *MakeExp( UnitNode *, int, int, int * ); +static int DimAnal( UnitNode *, double[NQUANT], double *, int * ); +static int Ustrcmp( const char *, const char *, int * ); +static int Ustrncmp( const char *, const char *, size_t, int * ); +static int SplitUnit( const char *, int, const char *, int, Multiplier **, int *, int * ); +static UnitNode *ModifyPrefix( UnitNode *, int * ); +static int ConStart( const char *, double *, int *, int * ); + +/* Debug functions... +static const char *DisplayTree( UnitNode *, int ); +static void OpSym( UnitNode *, char * ); +static const char *OpName( Oper ); +static const char *TreeExp( UnitNode * ); +*/ + +/* Function implementations. */ +/* ========================= */ +static const char *CleanExp( const char *exp, int *status ) { +/* +* Name: +* CleanExp + +* Purpose: +* Produce a clean copy of a units expression. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* const char *CleanExp( const char *exp ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to dynamic memory containing a +* cleaned copy of the supplied units expression. Cleaning consists of +* the following operations: +* - removal of leading and trailing white space. +* - replacement of multiple adjacent spaces by a single space +* - removal of spaces adjacent to a parenthesis +* - removal of spaces adjacent to a binary operator +* - translates various common non-standard units into equivalent +* standard units. +* +* Such carefull handling of spaces is necessary since a space is +* recognised by the MakeTree function as a multiplication operator. + +* Parameters: +* exp +* A pointer to the expression to be cleaned. + +* Returned Value: +* A pointer to the cleaned expression, which should be freed using +* astFree when no longer needed. + +* Notes: +* - This function returns NULL if it is invoked with the global error +* status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + char **tok; + char *p; + char *r; + char *result; + char *s; + char *t; + char *w; + const char *start; + int i; + int l; + int len; + char *tt; + int ntok; + int po; + int ps; + int word; + +/* Initialise */ + result = NULL; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Split the supplied string up into tokens. Each block of contiguous + alphanumeric characters is a token. Each contiguous block of + non-alphanumerical characters is also a token. The + and - signs are + counted as alphanumeric. */ + start = exp; + p = (char *) exp - 1; + word = ISWORD( *( p + 1 ) ); + ntok = 0; + tok = NULL; + while( *(++p) ){ + if( word ) { + if( !ISWORD( *p ) ) { + l = p - start; + t = astStore( NULL, start, l + 1 ); + if( t ) t[ l ] = 0; + tok = astGrow( tok, ntok + 1, sizeof( char * ) ); + if( tok ) tok[ ntok++ ] = t; + start = p; + word = 0; + } + } else { + if( ISWORD( *p ) ) { + l = p - start; + t = astStore( NULL, start, l + 1 ); + if( t ) t[ l ] = 0; + tok = astGrow( tok, ntok + 1, sizeof( char * ) ); + if( tok ) tok[ ntok++ ] = t; + start = p; + word = 1; + } + } + } + + l = p - start; + t = astStore( NULL, start, l + 1 ); + if( t ) t[ l ] = 0; + tok = astGrow( tok, ntok + 1, sizeof( char * ) ); + if( tok ) tok[ ntok++ ] = t; + +/* Check the tokens for known non-standard unit syntax, and replace with the + equivalent standard syntax. Starlink SPLAT has a class called UnitUtilities + which has more of these common units mistakes. AST has to be a bit + more conservative than SPLAT though because of its wider remit. */ + len = 0; + tt = NULL; + for( i = 0; i < ntok; i++ ) { + t = tok[ i ]; + l = strlen( t ); + tt = astStore( tt, t, l + 1 ); + +/* Any alphabetical word followed by a digit is taken as ^. + Any alphabetical word followed by a sign and a digit is taken as + ^. */ + if( l > 1 && *t != '-' && *t != '+' && + strcspn( t, "0123456789" ) == l - 1 ) { + tok[ i ] = astMalloc( l + 2 ); + if( tok[ i ] ) { + strcpy( tok[ i ], t ); + w = t + l - 2; + if( *w != '+' && *w != '-' ) { + tok[ i ][ l - 1 ] = '^'; + strcpy( tok[ i ] + l, t + l - 1 ); + } else { + tok[ i ][ l - 2 ] = '^'; + strcpy( tok[ i ] + l - 1, t + l - 2 ); + } + t = astFree( t ); + } + l++; + +/* If the word ends with "micron" change to "(m*1.0E-6)". Should be OK + for things like "Kmicron". */ + } else if( ( s = strstr( t, "micron" ) ) ) { + tok[ i ] = astMalloc( s - t + 11 ); + if( tok[ i ] ) { + w = tok[ i ]; + *(w++) = '('; + if( s > t ) { + strncpy( w, t, s - t ); + w += s - t; + } + strcpy( w, "m*1.0E-6)" ); + l = s - t + 11; + t = astFree( t ); + } + +/* Convert "STER" to "sr". */ + } else if( !Ustrcmp( t, "STER", status ) ) { + tok[ i ] = astStore( NULL, "sr", 3 ); + l = 2; + t = astFree( t ); + +/* If the word ends with "JY" and is preceded by a single character, change + to "Jy". Should be OK for things like "MJY". */ + } else if( l == 3 && !strcmp( t + 1, "JY" ) ) { + tok[ i ][ 2 ] = 'y'; + +/* If the word begins with "nano" (case-insensitive) change "nano" to + "n". Such changes are usually handled by SplitUnit, but we need to + handle this as a special case here since scanf seems to read "nan" as + a string representation of NaN. */ + } else if( !Ustrncmp( t, "nano", 4, status ) ) { + tok[ i ] = astStore( NULL, t + 3, l - 2 ); + if( tok[ i ] ) { + *(tok[ i ]) = 'n'; + t = astFree( t ); + } + l -= 3; + } + +/* Update the total length of the string. */ + len += l; + } + tt = astFree( tt ); + +/* Concatentate the tokens into a single string, freeing the individual + strings. */ + result = astMalloc( len + 1 ); + if( result ) { + p = result; + for( i = 0; i < ntok; i++ ) { + len = strlen( tok[ i ] ); + memcpy( p, tok[ i ], len ); + p += len; + tok[ i ] = astFree( tok[ i ] ); + } + *p = 0; + tok = astFree( tok ); + +/* Now do other cleaning. + ---------------------- */ + +/* Initialise a pointer to the previous character read from the string. */ + r = result - 1; + +/* Initialise a pointer to the next character to be written to the string. */ + w = result; + +/* Pretend the previous character written to the string was a space. */ + ps = 1; + +/* Read all the supplied string, copying it to earlier parts of the + string discarding leading spaces and multiple adjacent embedded spaces in + the process. */ + while( *(++r) ) { + +/* If the character read is a space, only write it to the string if the + previous character written was not a space (in which case set a flag + to indicate that the previous character written to the string is now a + space). */ + if( isspace( *r ) ) { + if( !ps ) { + *(w++) = *r; + ps = 1; + } + +/* Write all non-space characters to the string, and clear the flag which + indicates if the previous character written to the string was a space. */ + } else { + *(w++) = *r; + ps = 0; + } + } + +/* If the last character written to the string was a space, reduce the + length of the string by one since we do not want any trailing spaces. */ + if( ps ) w--; + +/* Terminate the string. */ + *w = 0; + +/* We now need to pass through the string again, this time removing any + spaces which are adjacent to a binary operator or a parenthesis. */ + r = result - 1; + w = result; + ps = 0; + po = 0; + while( *(++r) ) { + +/* If the current character is a space, only write it if the previous + written character was not an operator or parenthesis. */ + if( isspace( *r ) ) { + if( !po ) { + *(w++) = *r; + po = 1; + ps = 1; + } + +/* If the current character is an operator or parenthesis, back up one + character before writing it out if the previous written character was + a space. */ + } else if( *r == '*' || *r == '/' || *r == '^' || *r == '.' || + *r == ')' || *r == '(' ) { + if( ps ) w--; + *(w++) = *r; + po = 1; + ps = 0; + +/* If the current character is not a space and not an operator symbol, + just write it out. */ + } else { + *(w++) = *r; + po = 0; + ps = 0; + } + } + +/* Terminate the string. */ + if( ps ) w--; + *w = 0; + + } + +/* Return the result. */ + return (const char *) result; +} + +static int CmpTree( UnitNode *tree1, UnitNode *tree2, int exact, int *status ) { +/* +* Name: +* CmpTree + +* Purpose: +* Compares two trees of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int CmpTree( UnitNode *tree1, UnitNode *tree2, int exact, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a zero value if the two trees are +* equivalent. This requires the trees to have identical structure +* except that, if "exact" is zero, arguments for OP_MULT nodes can +* be swapped. +* +* If the trees are not equivalent then a value of +1 or -1 is returned +* depending on whether tree1 should be placed before or after tree2 +* in a sorted list of trees. + +* Parameters: +* tree1 +* A pointer to the UnitNode at the head of the first tree. +* tree2 +* A pointer to the UnitNode at the head of the second tree. +* exact +* If non-zero, then OP_MULT nodes must have their arguments the +* same way round in order for the OP_MULT nodes to match. Otherwise, +* OP_MULT nodes with equivalent arguments match even if the +* arguments are swapped. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the two trees are equal. +1 if tree1 should be placed before +* tree2 in a sorted list of trees. -1 if tree1 should be placed after +* tree2 in a sorted list of trees. + +* Notes: +* - Zero is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + int result; + int i; + Oper op; + +/* Initialise. */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* If the op codes differ, compare them as integers. */ + op = tree1->opcode; + if( op != tree2->opcode ) { + result = ( op > tree2->opcode ) ? 1: -1; + +/* If both supplied nodes are OP_LDVAR nodes, compare the associated names. */ + } else if( op == OP_LDVAR ){ + result = strcmp( tree1->name, tree2->name ); + +/* If both supplied nodes are constant nodes, compare the constant values. */ + } else if( tree1->con != AST__BAD ){ + result = astEQUAL( tree1->con, tree2->con ) ? 0 : ( + ( tree1->con > tree2->con ) ? 1 : -1 ); + +/* Otherwise, compare the arguments for the node. */ + } else { + for( i = 0; i < tree1->narg; i++ ) { + result = CmpTree( tree1->arg[ i ], tree2->arg[ i ], exact, status ); + if( result ) break; + } + +/* If the head nodes of the two trees are OP_MULT nodes, and the above + check determined they are different, this may be just because they + have their operands swapped. If "exact" si zero, this is considered an + insignificant difference between the two trees which we should ignore. + To check for this try comparing the arguments again, this time swapping + the arguments of tree2. */ + if( result && op == OP_MULT && !exact ) { + for( i = 0; i < tree1->narg; i++ ) { + result = CmpTree( tree1->arg[ i ], tree2->arg[ 1 - i ], 0, status ); + if( result ) break; + } + } + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the answer. */ + return result; +} + +static UnitNode *CombineFactors( UnitNode **factors, double *powers, + int nfactor, double coeff, int *status ) { +/* +* Name: +* CombineFactors + +* Purpose: +* Create a tree which represents the product of the supplied factors. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *CombineFactors( UnitNode **factors, double *powers, +* int nfactor, double coeff, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function createa a tree of UnitNodes which represents the +* product of the supplied factors, and the supplied coefficient. +* The factors are sorted before being combined, using the sort order +* implemented by the CmpTree function. + +* Parameters: +* factors +* A pointer to an array with "nfactor" elements, each element being +* a pointer to a UnitNode which is a factor of the required tree. +* On exit, the array is sorted. +* powers +* A pointer to an array with "nfactor" elements, each element being a +* double holding the power of the associated factor in "factors". +* On exit, the array reflects the sorting applied to "factors". +* nfactor +* The number of elements in the "factors" and "powers" arrays. +* coeff +* The overall coefficient to be applied to the product of the factors. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which is at the head of the new tree. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + UnitNode *result; + int i; + int j; + int jp; + int done; + UnitNode *ftmp; + UnitNode *node1; + UnitNode *node2; + UnitNode *pnode; + double ptmp; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Sort the supplied list of factors, modifying the powers array + correspondingly. A simple bubblesort algorithm is used since there + will only be a handfull of factors. */ + for( i = nfactor - 1; i > 0; i-- ) { + done = 1; + for( j = 0, jp = 1; j < i; j++, jp++ ) { + if( CmpTree( factors[ j ], factors[ jp ], 0, status ) > 0 ) { + ftmp = factors[ j ]; + factors[ j ] = factors[ jp ]; + factors[ jp ] = ftmp; + + ptmp = powers[ j ]; + powers[ j ] = powers[ jp ]; + powers[ jp ] = ptmp; + + done = 0; + } + } + if( done ) break; + } + +/* The first root term of the returned tree is the coefficient, unless the + coefficient is 1.0, in which case it will be the first factor. */ + if( coeff != 1.0 ) { + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) node1->con = coeff; + } else { + node1 = NULL; + } + +/* Loop through the factors. */ + for( i = 0; i < nfactor; i++ ) { + +/* If the power of this factor is zero, we ignore the factor. */ + if( powers[ i ] != 0.0 ) { + +/* If the power of this factor is one, we use the factor directly. */ + if( astEQUAL( powers[ i ], 1.0 ) ) { + node2 = CopyTree( factors[ i ], status ); + +/* Otherwise, for non-zero, non-unity powers, we create a POW node for + the factor. */ + } else { + node2 = NewNode( NULL, OP_POW, status ); + pnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + pnode->con = powers[ i ]; + node2->arg[ 0 ] = CopyTree( factors[ i ], status ); + node2->arg[ 1 ] = pnode; + } + } + +/* We now combine node1 and node2 using an OP_MULT node, which becomes + the "node1" for the next pass. On the first pass we may have no node1 (if + the supplied coefficient was 1.0), in which case we reserve the current + node2 as the node1 for the next pass. */ + if( node1 ) { + result = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + result->arg[ 0 ] = node1; + result->arg[ 1 ] = node2; + node1 = result; + } + } else { + node1 = node2; + } + } + } + +/* Ensure we have a node to return. */ + if( astOK ) { + if( !result ) result = node1; + if( !result ) { + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = 1.0; + } + } + +/* If an error has occurred, free any new tree. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static int ComplicateTree( UnitNode **node, int *status ) { +/* +* Name: +* ComplicateTree + +* Purpose: +* Removes standardisations introduced by SimplifyTree. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int ComplicateTree( UnitNode **node ) + +* Class Membership: +* Unit member function. + +* Description: +* This function modifies a tree of UnitNodes by removing standardisations +* introduced by SimplifyTree. The standardisations removed are ones +* which would make the corresponding algebraic expression (as produced +* by MakeExp) unnatural to a human reader. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be complicated. On exit the supplied tree is freed and +* a pointer to a new tree is placed at the given address. + +* Returned Value: +* Non-zero if any change was introduced into the tree. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + UnitNode *node1; + UnitNode *node2; + UnitNode *node3; + Oper op; + double con; + double fk; + int k; + int result; + double kcon; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Initiallially, we have no replacement node. */ + newnode = NULL; + node1 = NULL; + node3 = NULL; + +/* Complicate the sub-trees corresponding to the arguments of the node at + the head of the supplied tree. */ + for( i = 0; i < (*node)->narg; i++ ) { + if( ComplicateTree( &( (*node)->arg[ i ] ), status ) ) result = 1; + } + +/* Now undo specific simplifications appropriate to the nature of the node at + the head of the tree. */ + op = (*node)->opcode; + +/* If the head is an OP_MULT node with a constant first argument and + a "LN" second argument, rearrange the nodes to represent ln(x**k) instead + of k*ln(x). If k is an integer multiple of "0.1/ln(10)" convert the "ln" + function into a "log" (base 10) function. Check for "k==1" in which + case we do not need a POW node. */ + if( (*node)->opcode == OP_MULT ) { + + con = (*node)->arg[ 0 ]->con; + if( con != AST__BAD && (*node)->arg[ 1 ]->opcode == OP_LN ) { + fk = 10.0*con*log( 10.0 ); + k = NINT(fk); + if( astEQUAL(fk,((double)k)) ) { + newnode = NewNode( NULL, OP_LOG, status ); + con = k/10.0; + } else { + newnode = NewNode( NULL, OP_LN, status ); + } + + node2 = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + if( !astEQUAL( con, 1.0 ) ){ + node1 = CopyTree( (*node)->arg[ 0 ], status ); + node3 = NewNode( NULL, OP_POW, status ); + } + + if( astOK ) { + if( !astEQUAL( con, 1.0 ) ){ + node1->con = con; + node3->arg[ 0 ] = node2; + node3->arg[ 1 ] = node1; + newnode->arg[ 0 ] = node3; + } else { + newnode->arg[ 0 ] = node2; + } + } + +/* Replace "(A**-1)*B" with "B/A" */ + } else if( (*node)->arg[ 0 ]->opcode == OP_POW && + astEQUAL( (*node)->arg[ 0 ]->arg[ 1 ]->con, -1.0 )) { + newnode = NewNode( NULL, OP_DIV, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + } + +/* Replace "B*(A**-1)" with "B/A" */ + } else if( (*node)->arg[ 1 ]->opcode == OP_POW && + astEQUAL( (*node)->arg[ 1 ]->arg[ 1 ]->con, -1.0 )) { + newnode = NewNode( NULL, OP_DIV, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + } + +/* Convert (x**k)*(y**k) to (x*y)**k. */ + } else if( (*node)->arg[ 0 ]->opcode == OP_POW && + (*node)->arg[ 1 ]->opcode == OP_POW && + astEQUAL( (*node)->arg[ 0 ]->arg[ 1 ]->con, + (*node)->arg[ 1 ]->arg[ 1 ]->con )) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 1 ], status ); + } + +/* Convert c*sqrt(x) to sqrt((c**2)*x) (if c > 0). */ + } else if( (kcon=(*node)->arg[ 0 ]->con) != AST__BAD && + kcon > 0.0 && (*node)->arg[ 1 ]->opcode == OP_SQRT ) { + newnode = NewNode( NULL, OP_SQRT, status ); + node1 = NewNode( NULL, OP_MULT, status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node2->con = kcon*kcon; + node1->arg[ 0 ] = node2; + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + } + } + +/* If the head node is a POW node, replace "x**0.5" by sqrt(x) */ + } else if( (*node)->opcode == OP_POW ) { + if( astEQUAL( (*node)->arg[ 1 ]->con, 0.5 ) ) { + newnode = NewNode( NULL, OP_SQRT, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + } + } + } + +/* If we have produced a new node which is identical to the old node, + free it. Otherwise, indicate we have made some changes. */ + if( newnode ) { + if( !CmpTree( newnode, *node, 1, status ) ) { + newnode = FreeTree( newnode, status ); + } else { + result = 1; + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) { + newnode = FreeTree( newnode, status ); + result = 0; + } + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } + +/* If the above produced some change, try simplifying (without + re-introducing the standardisation we have just got rid of!) and + then re-complicating the tree. */ + if( result ) { + SimplifyTree( node, 0, status ); + ComplicateTree( node, status ); + } + +/* Return the result. */ + return result; +} + +static UnitNode *ConcatTree( UnitNode *tree1, UnitNode *tree2, int *status ) { +/* +* Name: +* ConcatTree + +* Purpose: +* Concatenate two trees together. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *ConcatTree( UnitNode *tree1, UnitNode *tree2, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function a pointer to the head of a new tree of UnitNodes which +* is formed by feeding the output of "tree1" (i.e. the quantity +* represented by the node at the head of tree1) into the (single) +* input of "tree2" (i.e. the single OP_LDVAR Node containined within +* tree2). + +* Parameters: +* tree1 +* A pointer to the first tree. +* tree2 +* A pointer to the second tree. This should have no more than one +* OP_LDVAR node. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which is at the head of the new tree. + +* Notes: +* - If "tree2" contains zero units, a NULL pointer is returned but no +* error is reported. +* - If "tree2" contains more than one unit, an error is reported +* error is reported. +* - A NULL pointer is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + UnitNode *result; + UnitNode **units; + int nunits; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Produce a copy of tree2. */ + result = CopyTree( tree2, status ); + +/* Locate the OP_LDVAR node in the copy of tree2. */ + units = NULL; + nunits = 0; + LocateUnits( result, &units, &nunits, status ); + +/* If no OP_LDVAR nodes were found in tree2, we cannot concatenate the + trees. */ + if( nunits > 0 ) { + +/* Report an error if the number of pointers returned is larger than 1. */ + if( nunits > 1 && astOK ) { + astError( AST__INTER, "ConcatTree(unit): tree2 uses %d units - " + "should be 1 (internal AST programming error).", status, nunits ); + } + +/* Replace the OP_LDVAR node in the copy of tree2 with a copy of tree1. */ + if( astOK ) { + +/* If the node at the head of the supplied tree2 is the node to be + replaced, just free the tree created earlier and return a copy of + tree1. */ + if( units[ 0 ] == result ) { + FreeTree( result, status ); + result = CopyTree( tree1, status ); + +/* Otherwise, search for the node to be replaced and do the substitution + within the tree created earlier. */ + } else { + ReplaceNode( result, units[ 0 ], CopyTree( tree1, status ), status ); + } + } + } + +/* Free resources. */ + units = astFree( units ); + +/* If an error has occurred, free any new tree. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static int ConStart( const char *text, double *val, int *nc, int *status ) { +/* +* Name: +* ConStart + +* Purpose: +* See if the supplied string starts with a literal numeric constant. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int ConStart( const char *text, double *val, int *nc, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function checks if the supplied string starts with a literal +* numeric constant and returns it if it does. It is a wrap-up for scanf +* since scanf has non-standard behaviour on some platforms (e.g. Cygwin +* scanf interprets the character "n" as a floating point number!). + +* Parameters: +* text +* The text to check. +* val +* Address of a double to receive any numerical constant read +* from the start of the string. Unity is returned if the string +* does not start with a numerical constant. +* nc +* Address of an int to receive the number of characters used to +* create the value returned in "val". Zero is returned if the +* string does not start with a numerical constant. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the text started with a numerical constant. + +*/ + +/* Local Variables: */ + int result; + const char *c; + +/* Initialise */ + *nc = 0; + *val = 1.0; + +/* Return zero if no text was supplied */ + if( !text ) return 0; + +/* Use sscanf to see if the string begin with a numerical constant */ + result = astSscanf( text, "%lf%n", val, nc ); + +/* If so, check that the first non-blank character in the string + is not "N" (interpreted by Cygwin as numerical zero!). */ + if( result ) { + c = text; + while( isspace( *c ) ) c++; + if( *c == 'n' || *c == 'N' ) { + result = 0; + *nc = 0; + *val = 1.0; + } + } + +/* Return the result. */ + return result; +} + +static UnitNode *CopyTree( UnitNode *tree, int *status ) { +/* +* Name: +* CopyTree + +* Purpose: +* Create a new tree of UnitNodes containing a copy of a given tree. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *CopyTree( UnitNode *tree, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a copy of the supplied tree of UnitNodes. + +* Parameters: +* tree +* The UnitNode at the head of the tree to be copied. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the UnitNode at the head of the new tree. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode **args; + UnitNode *result; + int i; + int narg; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK || !tree ) return result; + +/* Create a new node to represent the head of the supplied tree. */ + result = astMalloc( sizeof( UnitNode ) ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the fields of the supplied node. */ + narg = tree->narg; + + result->arg = NULL; + result->unit = tree->unit; + result->mult = tree->mult; + result->opcode = tree->opcode; + result->narg = narg; + result->con = tree->con; + result->name = tree->name ? astStore( NULL, tree->name, + strlen( tree->name ) + 1 ) : NULL; + +/* Create an array of UnitNode pointers for the arguments. */ + args = astMalloc( narg*sizeof( UnitNode * ) ); + if( astOK ) { + result->arg = args; + +/* Copy the sub-trees headed by the argument nodes. */ + for( i = 0; i < narg; i++ ) { + args[ i ] = CopyTree( tree->arg[ i ], status ); + } + } + } + +/* Free any result if an error occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static UnitNode *CreateTree( const char *exp, int basic, int lock, int *status ){ +/* +* Name: +* CreateTree + +* Purpose: +* Convert an algebraic units expression into a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *CreateTree( const char *exp, int basic, int lock, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function converts the supplied algebraic units expression into +* a tree of UnitNodes. The result tree can optionally be expanded to +* create a tree in which the "roots" (LDVAR nodes) all refer to +* basic units. + +* Parameters: +* exp +* The units expression. This should not include any leading or +* trailing spaces. +* basic +* Should the tree created from parsing "exp" be expanded so that +* the leaf nodes of the tree are all basic units? +* lock +* Use a mutex to guard access to the KnownUnits list? +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which forms the head of a tree of UnitNodes +* representing the supplied unit expression. + +* Notes: +* - A NULL value is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode *result; + const char *cleanex; + +/* Initialise */ + result = NULL; + +/* Check the global error status, and that we have a string. */ + if ( !astOK ) return result; + +/* Produce a clean copy of the supplied string. This has no leading + or trailing white space, and any spaces adjacent to operators within + the string are removed (this is needed because spaces are treated as + multiplication symbols). */ + cleanex = CleanExp( exp, status ); + +/* If the string is blank, return the NULL pointer. Otherwise, create a + tree of UnitNodes describing the units. The returned tree has LDVAR + nodes which refer to the unit symbols contained in the supplied string. */ + if( cleanex && (*cleanex) ) { + result = MakeTree( cleanex, strlen( cleanex ), lock, status ); + +/* Replace each subtree which simply combines constants (i.e. which has no + OP_LDVAR nodes) with a single OP_LDCON node. */ + FixConstants( &result, 0, status ); + +/* Invert literal constant unit multipliers. */ + InvertConstants( &result, status ); + +/* Now replace each LDVAR node which refers to a known derived unit with + a sub-tree which defines the derived unit in terms of known basic units. + The LDVAR nodes in the resulting tree all refer to basic units. */ + if( basic ) RemakeTree( &result, status ); + } + +/* Free resources. */ + cleanex = astFree( (void *) cleanex ); + +/* Free any returned tree if an error has occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; +} + +static int DimAnal( UnitNode *node, double powers[NQUANT], double *scale, int *status ) { +/* +* Name: +* DimAnal + +* Purpose: +* Perform a dimensional analysis of a unit tree. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* int DimAnal( UnitNode *node, double powers[NQUANT], double *scale, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a set of powers and a scaling factor which +* represent the units tree. + +* Parameters: +* node +* Pointer to the UnitNode at the head of the unit tree. +* powers +* An array in which are returned the powers for each of the following +* basic units (in the order shown): kilogramme, metre, second, radian, +* Kelvin, count, adu, photon, magnitude, pixel. If the supplied unit +* does not depend on a given basic unit a value of 0.0 will be returned +* in the array. The returns values represent a system of units which is +* a scaled form of the supplied units, expressed in the basic units of +* m, kg, s, rad, K, count, adu, photon, mag and pixel. For instance, a +* returned array of [1,0,-2,0,0,0,0,0,0,0] would represent "m/s**2". +* scale +* Pointer to a location at which to return a scaling factor for the +* supplied units. The is the value, in the units represented by the +* returned powers, which corresponds to a value of 1.0 in the supplied +* units. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the tree was analysed succesfully. Zero otherwise. + +* Notes: +* - Zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables; */ + Oper oper; + int result; + int i; + double p0[ NQUANT ]; + double p1[ NQUANT ]; + double s0; + double s1; + +/* Check inherited status */ + if( !astOK ) return 0; + +/* Initialise the powers of all dimensions to zero, and set the scaling + factor to unity. */ + result = 1; + *scale = 1.0; + for( i = 0; i < NQUANT; i++ ) powers[ i ] = 0.0; + +/* Load constant: constant is dimensionaless so leave powers unchanged, + and set the scaling factor. */ + oper = node->opcode; + if( oper == OP_LDCON ) { + *scale = 1.0/node->con; + +/* Load variable: check it is one of the basic known dimensional + quantities. If so, set the power of the quantity to unity and store + the scale factor. If the unit is "g" modify the scale factor so that + the analysis quantity is "kg". */ + } else if( oper == OP_LDVAR ) { + result = 0; + for( i = 0; i < NQUANT; i++ ) { + if( node->unit == quant_units[ i ] ) { + powers[ i ] = 1.0; + *scale = node->mult ? 1.0/node->mult->scale : 1.0; + if( !strcmp( node->unit->sym, "g" ) ) *scale *= 0.001; + result = 1; + break; + } + } + +/* How does dimensional analysis handle log or exp units?*/ + } else if( oper == OP_LOG ) { + result= 0; + + } else if( oper == OP_LN ) { + result= 0; + + } else if( oper == OP_EXP ) { + result= 0; + +/* Get the powers for the child unit and then multiply each by 0.5 and + take the square root of the scale factor. */ + } else if( oper == OP_SQRT ) { + result = DimAnal( node->arg[0], powers, scale, status ); + if( result ) { + for( i = 0; i < NQUANT; i++ ) powers[ i ]*= 0.5; + *scale = sqrt( *scale ); + } + +/* Similarly for pow nodes. */ + } else if( oper == OP_POW ) { + result = DimAnal( node->arg[0], powers, scale, status ); + if( result ) { + double power = node->arg[1]->con; + for( i = 0; i < NQUANT; i++ ) powers[ i ]*= power; + *scale = pow( *scale, power ); + } + +/* Binary operators. Analyses the operands dimensions and combine. */ + } else if( oper == OP_DIV ) { + if( DimAnal( node->arg[0], p0, &s0, status ) && + DimAnal( node->arg[1], p1, &s1, status ) ) { + for( i = 0; i < NQUANT; i++ ) powers[ i ] = p0[ i ] - p1[ i ]; + *scale = s0/s1; + } else { + result = 0; + } + + } else if( oper == OP_MULT ) { + if( DimAnal( node->arg[0], p0, &s0, status ) && + DimAnal( node->arg[1], p1, &s1, status ) ) { + for( i = 0; i < NQUANT; i++ ) powers[ i ] = p0[ i ] + p1[ i ]; + *scale = s0*s1; + } else { + result = 0; + } + +/* Named constants are dimensionless */ + } else if( oper == OP_LDPI ) { + *scale = 1.0/PI; + + } else if( oper == OP_LDE ) { + *scale = 1.0/E; + + } + + return result; + +} + +static int EndsWith( const char *c, int nc, const char *test, int *status ){ +/* +* Name: +* EndsWith + +* Purpose: +* See if a string ends with another string + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int EndsWith( const char *c, int nc, const char *test, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function sees if the string given by "c" ends with the string +* given by "test". The comparison is case-insensitive. + +* Parameters: +* c +* A pointer to the last character in the string to be tested. +* nc +* The number of characters in the string to be tested. +* test +* A pointer to the string to be tested for. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the string "c" ends with the string "test". + +*/ + +/* Local Variables: */ + const char *start; + int i; + int result; + int tlen; + +/* initialise. */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Check the string being tested for is not longer than the string being + tested. */ + tlen = strlen( test ); + if( tlen <= nc ){ + +/* Get a pointer to where the matching string would start if the string "c" + ends with the required string "test". */ + start = c - tlen + 1; + +/* Do the comparison. */ + result = 1; + for( i = 0; i < tlen; i++ ) { + if( tolower( start[ i ] ) != tolower( test[ i ] ) ) { + result = 0; + break; + } + } + } + +/* Return the result. */ + return result; + +} + +static void FindFactors( UnitNode *node, UnitNode ***factors, double **powers, + int *nfactor, double *coeff, int *status ){ +/* +* Name: +* FindFactors + +* Purpose: +* Find the factors within an expression given by a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void FindFactors( UnitNode *node, UnitNode ***factors, double **powers, +* int *nfactor, double *coeff, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function analyses the supplied tree of UnitNoes and returns +* an array of pointers to nodes within the supplied tree which form +* factors of the tree. The power associated with each factor is also +* returned, together with an overall coefficient for the tree. The +* expression represented by the tree is thus the product of the +* coefficient with each of the factors, each raised to the associated +* power. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree which is to be +* analysed. +* factors +* The address at which to return a pointer to an array with "*nfactor" +* elements, each element being a pointer to a UnitNode within the +* supplied tree which is a factor of the supplied tree. +* powers +* The address at which to return a pointer to an array with "*nfactor" +* elements, each element being a double holding the power of the +* associated factor in "*factors". +* nfactor +* The address of an int containing the number of elements in the +* returned "*factors" and "*powers" arrays. +* coeff +* The address of a double containing the overall coefficient to be +* applied to the product of the factors. +* status +* Pointer to the inherited status variable. + +* Notes: +* - If the supplied node is a constant node, then "*coeff" is +* returned holding the value of the constant and "*nfactor" is returned +* equal to zero ("*factors" and "*powers" are returned holding NULL). +* - If an error has already occurred, or if this function fails, then +* "*factors" and "*powers" are returned holding NULL, "*nfactor" is +* returned holding zero and "*coeff" is returned holding 1.0. + +*/ + +/* Local Variables: */ + int i; + int j; + int found; + UnitNode **fact1; + double *pow1; + double coeff1; + int nfac1; + double con; + +/* Initialise */ + *factors = NULL; + *powers = NULL; + *nfactor = 0; + *coeff = 1.0; + +/* Check inherited status. */ + if( !astOK ) return; + +/* If the node at the head of the supplied tree is an OP_MULT node... */ + if( node->opcode == OP_MULT ) { + +/* Find the factors of the two arguments of the OP_MULT node. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + FindFactors( node->arg[ 1 ], &fact1, &pow1, &nfac1, &coeff1, status ); + +/* Combine the two lists. Loop round the factors of the seocnd argument. */ + for( i = 0; i < nfac1; i++ ) { + +/* See if there is already an equivalent factor in the returned list of + factors. */ + found = 0; + for( j = 0; j < *nfactor; j++ ) { + if( !CmpTree( (*factors)[ j ], fact1[ i ], 0, status ) ){ + found = 1; + break; + } + } + +/* If so, increment the power of the factor. */ + if( found ) { + (*powers)[ j ] += pow1[ i ]; + +/* Otherwise, add the factor to the end of the returned list. */ + } else { + *factors = astGrow( *factors, *nfactor + 1, sizeof( UnitNode *) ); + *powers = astGrow( *powers, *nfactor + 1, sizeof( double ) ); + if( astOK ) { + (*factors)[ *nfactor ] = fact1[ i ]; + (*powers)[ (*nfactor)++ ] = pow1[ i ]; + } + } + } + +/* Modify the overall coefficient. */ + *coeff *= coeff1; + +/* Free resources */ + fact1 = astFree( fact1 ); + pow1 = astFree( pow1 ); + +/* If the node at the head of the supplied tree is an OP_POW node, */ + } else if( node->opcode == OP_POW ) { + +/* Find the factors of the first argument. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + +/* Multiply all the factor powers by the constant exponent of the POW + node. */ + con = node->arg[ 1 ]->con; + for( j = 0; j < *nfactor; j++ ) { + (*powers)[ j ] *= con; + } + +/* Exponentiate the coefficient. */ + if( *coeff >= 0.0 || (int) con == con ) { + *coeff = pow( *coeff, con ); + } else { + astError( AST__BADUN, "Simplifying a units expression requires a " + "negative value to be raised to a non-intergal power." , status); + } + +/* If the node at the head of the supplied tree is an OP_DIV node, */ + } else if( node->opcode == OP_DIV ) { + +/* Find the factors of the two arguments of the OP_DIV node. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + FindFactors( node->arg[ 1 ], &fact1, &pow1, &nfac1, &coeff1, status ); + +/* Combine the two lists. Loop round the factors of the second argument + (the denominator). */ + for( i = 0; i < nfac1; i++ ) { + +/* See if there is already an equivalent factor in the returned list of + factors. */ + found = 0; + for( j = 0; j < *nfactor; j++ ) { + if( !CmpTree( (*factors)[ j ], fact1[ i ], 0, status ) ){ + found = 1; + break; + } + } + +/* If so, decrement the power of the factor. */ + if( found ) { + (*powers)[ j ] -= pow1[ i ]; + +/* Otherwise, add the factor to the end of the returned list, with a + negated power. */ + } else { + *factors = astGrow( *factors, *nfactor + 1, sizeof( UnitNode *) ); + *powers = astGrow( *powers, *nfactor + 1, sizeof( double ) ); + if( astOK ) { + (*factors)[ *nfactor ] = fact1[ i ]; + (*powers)[ (*nfactor)++ ] = -pow1[ i ]; + } + } + } + +/* Modify the overall coefficient. */ + if( coeff1 != 0.0 ) { + *coeff /= coeff1; + } else { + astError( AST__BADUN, "Simplifying a units expression" + "requires a division by zero." , status); + } + +/* Free resources */ + fact1 = astFree( fact1 ); + pow1 = astFree( pow1 ); + +/* If the node at the head of the supplied tree is an OP_SQRT node, */ + } else if( node->opcode == OP_SQRT ) { + +/* Find the factors of the argument. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + +/* Multiply all the factor powers by 0.5. */ + for( j = 0; j < *nfactor; j++ ) { + (*powers)[ j ] *= 0.5; + } + +/* Square root the coefficient. */ + if( *coeff >= 0.0 ) { + *coeff = sqrt( *coeff ); + } else { + astError( AST__BADUN, "Simplifying a units expression requires " + "the square root of a negative value to be taken." , status); + } + +/* If the node at the head of the supplied tree is constant we have no + factors but we have a coeffcient. */ + } else if( node->con != AST__BAD ) { + *coeff = node->con; + +/* Other nodes have no factors other than themselves, so just return a + pointer to the supplied node. */ + } else { + *factors = astMalloc( sizeof( UnitNode *) ); + *powers = astMalloc( sizeof( double ) ); + if( astOK ) { + *nfactor = 1; + (*factors)[ 0 ] = node; + (*powers)[ 0 ] = 1.0; + *coeff = 1.0; + } + } + +/* If an error has occurred, free any returned resources. */ + if( !astOK ) { + *factors = astFree( *factors ); + *powers = astFree( *powers ); + *nfactor = 0; + *coeff = 1.0; + } +} + +static void FixConstants( UnitNode **node, int unity, int *status ) { +/* +* Name: +* FixConstants + +* Purpose: +* Take the reciprocal of all constants in a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void FixConstants( UnitNode **node, int unity, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function replaces sub-trees which have a constant value by +* a single OP_LDCON node which loads the appropriate constant. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be fixed. On exit the supplied tree is freed and a +* pointer to a new tree is palced at he given address. +* unity +* If non-zero, then all multiplicative constants are set to 1.0, and +* their original values are forgotten, but only if the other +* argument of the OP_MULT node is an OP_LDVAR, OP_POW or OP_SQRT Node. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + int allcon; + Oper op; + double newcon; + +/* Check inherited status and pointer. */ + if( !astOK || !node || !(*node) ) return; + +/* Initiallially, we have no replacement node */ + newnode = NULL; + newcon = AST__BAD; + +/* There is nothing to fix if the node has no arguments. */ + if( (*node)->narg > 0 ) { + +/* Note the op code for the node. */ + op = (*node)->opcode; + +/* Fix up the argument nodes. Also note if all the arguments are + constants. */ + allcon = 1; + for( i = 0; i < (*node)->narg; i++ ) { + FixConstants( &( (*node)->arg[ i ] ), unity, status ); + if( (*node)->arg[ i ]->con == AST__BAD ) allcon = 0; + } + +/* If an OP_MULT nodes within a simplified tree has a constant argument, + it will always be argument zero. If this is an OP_MULT node and arg[0] + is constant and "unity" is non-zero and arg[1] is an OP_LDVAR, OP_POW + or OP_SQRT node, replace the constant value by 1.0. */ + if( unity && op == OP_MULT && + (*node)->arg[ 0 ]->con != AST__BAD && + ( (*node)->arg[ 1 ]->opcode == OP_LDVAR || + (*node)->arg[ 1 ]->opcode == OP_SQRT || + (*node)->arg[ 1 ]->opcode == OP_POW ) ) { + (*node)->arg[ 0 ]->con = 1.0; + } + +/* If the arguments of this node are all constants, replace the node by + an OP_LDCON node which loads the resulting constant value. */ + if( allcon ) { + if( (*node)->narg > 0 ) { + newnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + if( op == OP_LOG ) { + if( (*node)->arg[ 0 ]->con > 0.0 ) { + newcon = log10( (*node)->arg[ 0 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative or zero constant " + "value '%g' encountered.", status, + (*node)->arg[ 0 ]->con ); + } + } else if( op == OP_LN ){ + if( (*node)->arg[ 0 ]->con > 0.0 ) { + newcon = log( (*node)->arg[ 0 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative or zero constant value " + "'%g' encountered.", status, (*node)->arg[ 0 ]->con ); + } + } else if( op == OP_EXP ){ + newcon = exp( (*node)->arg[ 0 ]->con ); + + } else if( op == OP_SQRT ){ + if( (*node)->arg[ 0 ]->con >= 0.0 ) { + newcon = sqrt( (*node)->arg[ 0 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative constant value " + "'%g' encountered.", status, (*node)->arg[ 0 ]->con ); + } + + } else if( op == OP_POW ){ + if( (*node)->arg[ 0 ]->con >= 0.0 || + (int) (*node)->arg[ 1 ]->con == (*node)->arg[ 1 ]->con ) { + newcon = pow( (*node)->arg[ 0 ]->con, + (*node)->arg[ 1 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative constant value " + "'%g' encountered.", status, (*node)->arg[ 0 ]->con ); + } + + } else if( op == OP_DIV ){ + if( (*node)->arg[ 1 ]->con != 0.0 ) { + newcon = (*node)->arg[ 0 ]->con / (*node)->arg[ 1 ]->con; + } else { + astError( AST__BADUN, "Illegal zero constant value encountered." , status); + } + + } else if( op == OP_MULT ){ + newcon = (*node)->arg[ 0 ]->con * (*node)->arg[ 1 ]->con; + + } + + + if( astOK ) newnode->con = newcon; + } + } + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } + +} + +static UnitNode *FixUnits( UnitNode *node, UnitNode *test, int *status ) { +/* +* Name: +* FixUnits + +* Purpose: +* Assign a constant value to all units except for one. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *FixUnits( UnitNode *node, UnitNode *test, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a copy of the supplied tree of UnitNodes. All +* OP_LDVAR nodes within the copy which refer to units which differ +* from those referred to by the supplied test node are replaced by +* OP_LDCON nodes which load the constant value 1.0. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree to be used. +* test +* A pointer to an OP_LDVAR node which defines the units which are +* *not* to be replaced by a constant value of 1.0. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which is at the head of a tree of UnitNodes +* which forms the required copy of th einput tree. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + int i; + UnitNode *result; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Create a complete copy of the supplied tree. */ + result = CopyTree( node, status ); + +/* Is the node at the head of the supplied tree an OP_LDVAR node? */ + if( node->opcode == OP_LDVAR ) { + +/* Does it refer to a unit which differs from that of the test node? If so + annul the copy created above and return a new OP_LDCON node which loads + the constant value 1.0. */ + if( strcmp( test->name, node->name ) ) { + FreeTree( result, status ); + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = 1.0; + } + +/* If the supplied node is not an OP_LDVAR node, check each argument of + the head node. */ + } else { + for( i = 0; i < node->narg; i++ ) { + +/* Free the resources used to hold this argument in the tree copy created + above. */ + FreeTree( result->arg[ i ], status ); + +/* Create a new argument tree by calling this function recursively to + fix units in the argument sub-trees. */ + result->arg[ i ] = FixUnits( node->arg[ i ], test, status ); + } + } + +/* If an error has occurred, free any new tree. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static UnitNode *FreeTree( UnitNode *node, int *status ) { +/* +* Name: +* FreeTree + +* Purpose: +* Free resources used by a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *FreeTree( UnitNode *node, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function frees the memory used to store a tree of UnitNodes. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree which is to be +* freed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A NULL pointer is returned. + +* Notes: +* - This function attempts to execute even if it is invoked with +* the global error status set. +*/ + +/* Local Variables: */ + int i; + +/* Check a node was supplied. */ + if( node ) { + +/* Recursively free any argument nodes. */ + if( node->arg ) { + for( i = 0; i < node->narg; i++ ) { + (node->arg)[ i ] = FreeTree( (node->arg)[ i ], status ); + } + node->arg = astFree( node->arg ); + } + +/* Nullify other pointers for safety. */ + node->unit = NULL; + node->mult = NULL; + +/* Free the copy of the symbol string (if any). */ + node->name = astFree( (char *) node->name ); + +/* Free the memory holding the node. */ + node = astFree( node ); + } + +/* Return a null pointer. */ + return NULL; +} + +static KnownUnit *GetKnownUnits( int lock, int *status ) { +/* +* Name: +* GetKnownUnits + +* Purpose: +* Get a pointer to the head of a linked list of known unit definitions. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* KnownUnit *GetKnownUnits( int lock, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to the head of a linked list of known +* unit definitions. The unit definitions are created as static module +* variables if they have not previously been created. + +* Parameters: +* lock +* If non-zero, then lock a mutex prior to accessing the list of +* known units. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the first known unit definition. + +* Notes: +* - A NULL pointer is returned if it is invoked with the global error +* status set, or if an error occurs. +*/ + +/* Local Variables: */ + int iq; + KnownUnit *result; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Ensure the known units list is only initialised once. */ + if( lock ) { + LOCK_MUTEX1 + } + +/* If the linked list of KnownUnit structures describing the known units + has not yet been created, create it now. A pointer to the head of the + linked list is put into the static variable "known_units". */ + if( !known_units ) { + +/* At the same time we store pointers to the units describing the basic + quantities used in dimensional analysis. Initialise th index of the + next such unit. */ + iq = 0; + +/* Create definitions for the known units. First do all IAU basic units. + We include "g" instead of "kg" because otherwise we would have to + refer to a gramme as a milli-kilogramme. */ + MakeKnownUnit( "g", "gram", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "m", "metre", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "s", "second", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "rad", "radian", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "K", "Kelvin", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "A", "Ampere", NULL, status ); + MakeKnownUnit( "mol", "mole", NULL, status ); + MakeKnownUnit( "cd", "candela", NULL, status ); + +/* Now do all IAU derived units. Unit definitions may only refer to units + which have already been defined. */ + MakeKnownUnit( "sr", "steradian", "rad rad", status ); + MakeKnownUnit( "Hz", "Hertz", "1/s", status ); + MakeKnownUnit( "N", "Newton", "kg m/s**2", status ); + MakeKnownUnit( "J", "Joule", "N m", status ); + MakeKnownUnit( "W", "Watt", "J/s", status ); + MakeKnownUnit( "C", "Coulomb", "A s", status ); + MakeKnownUnit( "V", "Volt", "J/C", status ); + MakeKnownUnit( "Pa", "Pascal", "N/m**2", status ); + MakeKnownUnit( "Ohm", "Ohm", "V/A", status ); + MakeKnownUnit( "S", "Siemens", "A/V", status ); + MakeKnownUnit( "F", "Farad", "C/V", status ); + MakeKnownUnit( "Wb", "Weber", "V s", status ); + MakeKnownUnit( "T", "Tesla", "Wb/m**2", status ); + MakeKnownUnit( "H", "Henry", "Wb/A", status ); + MakeKnownUnit( "lm", "lumen", "cd sr", status ); + MakeKnownUnit( "lx", "lux", "lm/m**2", status ); + +/* Now do additional derived and basic units listed in the FITS-WCS paper. */ + MakeKnownUnit( "deg", "degree", "pi/180 rad", status ); + MakeKnownUnit( "arcmin", "arc-minute", "1/60 deg", status ); + MakeKnownUnit( "arcsec", "arc-second", "1/3600 deg", status ); + MakeKnownUnit( "mas", "milli-arcsecond", "1/3600000 deg", status ); + MakeKnownUnit( "min", "minute", "60 s", status ); + MakeKnownUnit( "h", "hour", "3600 s", status ); + MakeKnownUnit( "d", "day", "86400 s", status ); + MakeKnownUnit( "yr", "year", "31557600 s", status ); + MakeKnownUnit( "a", "year", "31557600 s", status ); + MakeKnownUnit( "eV", "electron-Volt", "1.60217733E-19 J", status ); + MakeKnownUnit( "erg", "erg", "1.0E-7 J", status ); + MakeKnownUnit( "Ry", "Rydberg", "13.605692 eV", status ); + MakeKnownUnit( "solMass", "solar mass", "1.9891E30 kg", status ); + MakeKnownUnit( "u", "unified atomic mass unit", "1.6605387E-27 kg", status ); + MakeKnownUnit( "solLum", "solar luminosity", "3.8268E26 W", status ); + MakeKnownUnit( "Angstrom", "Angstrom", "1.0E-10 m", status ); + MakeKnownUnit( "micron", "micron", "1.0E-6 m", status ); + MakeKnownUnit( "solRad", "solar radius", "6.9599E8 m", status ); + MakeKnownUnit( "AU", "astronomical unit", "1.49598E11 m", status ); + MakeKnownUnit( "lyr", "light year", "9.460730E15 m", status ); + MakeKnownUnit( "pc", "parsec", "3.0867E16 m", status ); + MakeKnownUnit( "count", "count", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "adu", "analogue-to-digital unit", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "photon", "photon", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "Jy", "Jansky", "1.0E-26 W /m**2 /Hz", status ); + MakeKnownUnit( "mag", "magnitude", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "G", "Gauss", "1.0E-4 T", status ); + MakeKnownUnit( "pixel", "pixel", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "barn", "barn", "1.0E-28 m**2", status ); + MakeKnownUnit( "D", "Debye", "(1.0E-29/3) C.m", status ); + + if( iq != NQUANT && astOK ) { + astError( AST__INTER, "unit(GetKnownUnits): %d basic quantities " + "noted but this should be %d (internal AST programming " + "error).", status, iq, NQUANT ); + } + +/* Unit aliases... */ + MakeUnitAlias( "Angstrom", "Ang", status ); + MakeUnitAlias( "count", "ct", status ); + MakeUnitAlias( "photon", "ph", status ); + MakeUnitAlias( "Jy", "Jan", status ); + MakeUnitAlias( "pixel", "pix", status ); + MakeUnitAlias( "s", "sec", status ); + MakeUnitAlias( "m", "meter", status ); + } + +/* If succesful, return the pointer to the head of the list. */ + if( astOK ) result = known_units; + +/* Allow the next thread to proceed. */ + if( lock ) { + UNLOCK_MUTEX1 + } + +/* Return the result. */ + return result; +} + +static Multiplier *GetMultipliers( int *status ) { +/* +* Name: +* GetMultiplier + +* Purpose: +* Get a pointer to the head of a linked list of multiplier definitions. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* Multiplier *Multipliers( void ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to the head of a linked list of known +* multiplier definitions. The multiplier definitions are created as +* static module variables if they have not previously been created. + +* Returned Value: +* A pointer to the first known multiplier definition. + +* Notes: +* - A NULL pointer is returned if it is invoked with the global error +* status set, or if an error occurs. +*/ + +/* Local Variables: */ + Multiplier *result; + Multiplier *mult; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Ensure the list is only initialised by one thread. */ + LOCK_MUTEX2 + +/* If the linked list of Multiplier structures describing the known + multipliers has not yet been created, create it now. A pointer to the + head of the linked list is put into the static variable "multipliers". */ + if( !multipliers ) { + +/* Define a macro to create a multiplier struncture and add it to the + linked list of multiplier structures. */ +#define MAKEMULT(s,sl,sc,lab,ll) \ + mult = astMalloc( sizeof( Multiplier ) ); \ + if( astOK ) { \ + mult->sym = s; \ + mult->symlen = sl; \ + mult->lablen = ll; \ + mult->scale = sc; \ + mult->label = lab; \ + mult->next = multipliers; \ + multipliers = mult; \ + } + +/* Use the above macro to create all the standard multipliers listed in the + FITS WCS paper I. */ + MAKEMULT("d",1,1.0E-1,"deci",4) + MAKEMULT("c",1,1.0E-2,"centi",5) + MAKEMULT("m",1,1.0E-3,"milli",5) + MAKEMULT("u",1,1.0E-6,"micro",5) + MAKEMULT("n",1,1.0E-9,"nano",4) + MAKEMULT("p",1,1.0E-12,"pico",4) + MAKEMULT("f",1,1.0E-15,"femto",5) + MAKEMULT("a",1,1.0E-18,"atto",4) + MAKEMULT("z",1,1.0E-21,"zepto",5) + MAKEMULT("y",1,1.0E-24,"yocto",5) + MAKEMULT("da",2,1.0E1,"deca",4) + MAKEMULT("h",1,1.0E2,"hecto",5) + MAKEMULT("k",1,1.0E3,"kilo",4) + MAKEMULT("M",1,1.0E6,"mega",4) + MAKEMULT("G",1,1.0E9,"giga",4) + MAKEMULT("T",1,1.0E12,"tera",4) + MAKEMULT("P",1,1.0E15,"peta",4) + MAKEMULT("E",1,1.0E18,"exa",3) + MAKEMULT("Z",1,1.0E21,"zetta",5) + MAKEMULT("Y",1,1.0E24,"yotta",5) + +/* Undefine the macro. */ +#undef MAKEMULT + + } + +/* If succesful, return the pointer to the head of the list. */ + if( astOK ) result = multipliers; + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2 + +/* Return the result. */ + return result; +} + +static void InvertConstants( UnitNode **node, int *status ) { +/* +* Name: +* InvertConstants + +* Purpose: +* Take the reciprocal of all constants in a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void InvertConstants( UnitNode **node, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function replaces constant unit coefficients by their reciprocal. +* This is because a string such as "0.01 m" will be interpreted as +* meaning "multiply a value in metres by 0.01 to get the value in the +* required units", whereas what is actually meant is "use units of +* 0.01 of a metre" which requires us to divide the value in metres by +* 0.01, not multiply it. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree. +* On exit the supplied tree is freed and a pointer to a new tree is +* placed at the given address. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + int allcon; + Oper op; + +/* Check inherited status and pointer. */ + if( !astOK || !node || !(*node) ) return; + +/* Initiallially, we have no replacement node */ + newnode = NULL; + +/* There is nothing to fix if the node has no arguments. */ + if( (*node)->narg > 0 ) { + +/* Note the op code for the node. */ + op = (*node)->opcode; + +/* Fix up the argument nodes. Also note if all the arguments are + constants. */ + allcon = 1; + for( i = 0; i < (*node)->narg; i++ ) { + InvertConstants( &( (*node)->arg[ i ] ), status ); + if( (*node)->arg[ i ]->con == AST__BAD ) allcon = 0; + } + +/* If all nodes are constant, there are no co-efficients to invert. */ + if( !allcon ) { + +/* Iif this is a multiplication node, see if either of its arguments + is a constant. If so, invert the constant. This is because a string like + "0.01 m" means "each unit is 0.01 of a metre". Therefore, to transform + a value in metres into required units means multiplying the metres + value by 100.0 (i.e the reciprocal of 0.01), not 0.01. */ + if( op == OP_MULT ) { + for( i = 0; i < 2; i++ ) { + if( (*node)->arg[ i ]->con != AST__BAD ) { + if( (*node)->arg[ i ]->con != 0.0 ) { + + (*node)->arg[ i ]->con = 1.0/(*node)->arg[ i ]->con; + } else { + astError( AST__BADUN, "Illegal zero constant encountered." , status); + } + } + } + +/* Likewise, check for division nodes in which the denominator is + constant. */ + } else if( op == OP_DIV ) { + if( (*node)->arg[ 1 ]->con != AST__BAD ) { + if( (*node)->arg[ 1 ]->con != 0.0 ) { + (*node)->arg[ 1 ]->con = 1.0/(*node)->arg[ 1 ]->con; + } else { + astError( AST__BADUN, "Illegal zero constant encountered." , status); + } + } + +/* If this is a "pow" node check that the second argument is constant + (required by FITS WCS paper I). */ + } else if( op == OP_POW ) { + if( (*node)->arg[ 1 ]->con == AST__BAD ) { + astError( AST__BADUN, "Illegal variable exponent." , status); + } + } + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } +} + +static UnitNode *InvertTree( UnitNode *fwdnode, UnitNode *src, int *status ) { +/* +* Name: +* InvertTree + +* Purpose: +* Invert a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *InvertTree( UnitNode *fwdnode, UnitNode *src ) + +* Class Membership: +* Unit member function. + +* Description: +* This function inverts a tree of UnitNodes. The supplied tree should +* have exactly one OP_LDVAR node. This will be the quantity represented +* by the node at the head of the returned tree. + +* Parameters: +* fwdnode +* A pointer to the UnitNode at the head of the tree which is to be +* inverted. +* src +* A pointer to a UnitNode which is to be used as the root of the +* inverted tree. That is, the output from this node should form +* the (one and only) varying input to the inverted tree. If the +* supplied tree is succesfulyl inverted, the tree of which "src" +* is the head will be contained within the returned inverted tree. +* Therefore "src" only needs to be freed explicitly if this +* function fails to invert the supplied tree for any reason. If +* this function succeeds, then "src" will be freed as part of +* freeing the returned inverted tree. + +* Returned Value: +* A pointer to a UnitNode which forms the head of the inverted tree. + +* Algorithm: +* The algorithm works through the supplied forward tree, from the head +* to the roots. First, the supplied node at the head of the forward +* tree is inverted. To be invertable, the supplied head node must have +* exactly one varying argument (any other arguments must be fixed, +* i.e. not vary). This varying argument becomes the output of the +* inverted node. The other (fixed) arguments to the forward node are +* also used as arguments to the inverted node. The supplied "src" node +* is used as the single varying input to the inverted node. Having +* inverted the supplied forward head node, this function is called +* recursively to invert the lower parts of the forward tree (i.e. the +* part of the forward tree which provided the varying input to node +* which has just been inverted). + +* Notes: +* - It is assumed that he supplied forward tree has been simplified +* using SimplifyTree. This means that the tree contains no nodes with +* the following op codes: OP_LOG, OP_SQRT. OP_DIV (SimplifyTree +* converts these nodes into OP_LN, OP_POW and OP_MULT nodes). +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. + +*/ + +/* Local Variables: */ + UnitNode *newnode; + UnitNode *nextnode; + UnitNode *result; + UnitNode *node1; + Oper fop; + int varg; + +/* Initialise */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Initiallially, we have no replacement node */ + newnode = NULL; + nextnode = NULL; + +/* Save the op code at the head of the forward tree. */ + fop = fwdnode->opcode; + +/* If the head of the forward tree is a OP_EXP node. Inverse of + "exp(x)" is "ln(x)". */ + if( fop == OP_EXP ) { + newnode = NewNode( NULL, OP_LN, status ); + if( astOK ) { + newnode->arg[ 0 ] = src; + nextnode = fwdnode->arg[ 0 ]; + } + +/* If the head of the forward tree is a OP_LN node. Inverse of + "ln(x)" is "exp(x)". */ + } else if( fop == OP_LN ) { + newnode = NewNode( NULL, OP_EXP, status ); + if( astOK ) { + newnode->arg[ 0 ] = src; + nextnode = fwdnode->arg[ 0 ]; + } + +/* If the head of the forward tree is a OP_POW node. Inverse of + "x**k" is "x**(1/k)" */ + } else if( fop == OP_POW ) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = 1.0/fwdnode->arg[ 1 ]->con; + newnode->arg[ 0 ] = src; + newnode->arg[ 1 ] = node1; + nextnode = fwdnode->arg[ 0 ]; + } + +/* If the head of the forward tree is a OP_MULT node... */ + } else if( fop == OP_MULT ) { + +/* The node is only invertable if it has one constant node and one + non-constant node. Get the index of the varying argument. */ + if( fwdnode->arg[ 0 ]->con != AST__BAD && + fwdnode->arg[ 1 ]->con == AST__BAD ) { + varg = 1; + } else if( fwdnode->arg[ 0 ]->con == AST__BAD && + fwdnode->arg[ 1 ]->con != AST__BAD ) { + varg = 0; + } else { + varg = -1; + } + if( varg != -1 ) { + +/* The inverse of "k*x" is "(1/k)*x" (we use MULT nodes instead of DIV + nodes to maintain the standardisation implemented by SimplifyTree). */ + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = 1.0/fwdnode->arg[ 1 - varg ]->con; + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = src; + nextnode = fwdnode->arg[ varg ]; + } + } + +/* If the head of the forward tree is a OP_LDVAR node, there is nothing + left to invert. SO return a pointer to the suppleid source node. */ + } else if( fop == OP_LDVAR ) { + result = src; + nextnode = NULL; + +/* If the head of the forward tree is any other node (e.g. a OP_LDCON node), + the tree cannot be inverted. */ + } else { + nextnode = NULL; + } + +/* If we managed to invert the node at the head of the supplied tree, + continue to invert its varying argument node (if any). */ + if( nextnode && newnode ) result = InvertTree( nextnode, newnode, status ); + +/* If the tree could not be inverted, free the newnode. */ + if( !result ) newnode = FreeTree( newnode, status ); + +/* If an error has occurred, free any new node. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; + +} + +static void LocateUnits( UnitNode *node, UnitNode ***units, int *nunits, int *status ){ +/* +* Name: +* LocateUnits + +* Purpose: +* Locate the units used by a supplied tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void LocateUnits( UnitNode *node, UnitNode ***units, int *nunits, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function locates the units used by a supplied tree of +* UnitNodes. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree to be searched. +* units +* The address at which is stored a pointer to an array of "*nunits" +* elements. Each element of the array holds a pointer to a UnitNode. +* The array is extended on exit to hold pointers to the OP_LDVAR nodes +* within the supplied tree (i.e. nodes which represent named units, +* either known or unknown). A node is only included in the returned +* array if no other node for the same unit is already included in the +* array. A NULL pointer should be supplied on the first invocation of +* this function. +* nunits +* The address of an integer which holds the number of elements in +* the array given by "*units". Updated on exit to included any +* elements added to the array. Zero should be supplied on the first +* invocation of this function. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + int found; + +/* Check the global error status. */ + if( !astOK ) return; + +/* Is the node at the head of the supplied tree an OP_LDVAR node? */ + if( node->opcode == OP_LDVAR ) { + +/* If an array was supplied, see if it already contains a pointer to a node + which refers to the same units. */ + found = 0; + if( *units ) { + for( i = 0; i < *nunits; i++ ) { + if( !strcmp( (*units)[ i ]->name, node->name ) ) { + found = 1; + break; + } + } + } + +/* If not, ensure the array is big enough and add a pointer to the + supplied node to the array. */ + if( !found ) { + *units = astGrow( *units, *nunits + 1, sizeof( UnitNode * ) ); + if( astOK ) (*units)[ (*nunits)++ ] = node; + } + +/* If the supplied node is not an OP_LDVAR node, call this function + recursively to search the argument sub-trees. */ + } else { + for( i = 0; i < node->narg; i++ ) { + LocateUnits( node->arg[ i ], units, nunits, status ); + } + } +} + +static const char *MakeExp( UnitNode *tree, int mathmap, int top, int *status ) { +/* +* Name: +* MakeExp + +* Purpose: +* Make an algebraic expression from a supplied tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* const char *MakeExp( UnitNode *tree, int mathmap, int top, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function produces a string holding an algebraic expression +* corresponding to a supplied tree of UnitNodes. + +* Parameters: +* tree +* A pointer to the UnitNode at the head of the tree to be converted +* into an algebraic expression. +* mathmap +* If zero, format as an axis label expression. If 1, format as a +* MathMap expression. If 2, format as a FITS unit string. +* top +* Should be non-zero for a top-level entry to this function, and +* zero for a recursive entry. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the cleaned expression, which should be freed using +* astFree when no longer needed. + +* Notes: +* - This function returns NULL if it is invoked with the global error +* status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode *newtree; + UnitNode *sunit; + char *a; + char *result; + char buff[200]; + const char *arg0; + const char *arg1; + const char *mtxt; + int larg0; + int larg1; + int lbuff; + int mlen; + int par; + int tlen; + +/* Check inherited status. */ + result = NULL; + if( !astOK ) return result; + +/* Modify the tree to make the resulting transformation functions more + natural to human readers. */ + newtree = CopyTree( tree, status ); + ComplicateTree( &newtree, status ); + +/* If we are producing an axis label... */ + if( !mathmap ) { + +/* Fix all multiplicative constants to 1.0 if they multiply an OP_LDVAR + OP_SQRT or OP_POW node. This is on the assumption that the returned label + should not include any simple unit scaling (e.g. if the output label would + be "2.345*wavelength", we prefer simply to use "wavelength" since a scaled + wavelength is still a wavelength - i.e. simple scaling does not change + the dimensions of a quantity). */ + FixConstants( &newtree, 1, status ); + +/* Simplify the tree again to get rid of the 1.0 terms which may have + been introduced by the previous line (but do not re-introduce any + standardisations - removing them was the reason for calling ComplicateTree). + If this simplication introduces any changes, try fixing multiplicative + constants again, and so on, until no more changes occur. */ + while( SimplifyTree( &newtree, 0, status ) ) { + FixConstants( &newtree, 1, status ); + } + + } + +/* Produce a string describing the action performed by the UnitNode at + the head of the supplied tree, and then invoke this function recursively + to format any arguments of the head node. */ + +/* Constant valued nodes... just format the constant in a local buffer and + then copy the buffer. */ + if( newtree->con != AST__BAD ) { + lbuff = sprintf( buff, "%.*g", DBL_DIG, newtree->con ); + result = astStore( NULL, buff, lbuff + 1 ); + +/* "Load Variable Value" nodes - return the variable name. If this is a + recursive call to this function, and we are producing a label, append a + single space before and after the name. */ + } else if( newtree->opcode == OP_LDVAR ) { + tlen = strlen( newtree->name ); + + if( !mathmap && !top ){ + result = astMalloc( tlen + 3 ); + if( result ) { + result[ 0 ] = ' '; + memcpy( result + 1, newtree->name, tlen ); + memcpy( result + tlen + 1, " ", 2 ); + } + + } else if( mathmap == 2 ) { + + if( newtree->mult ) { + mlen = newtree->mult->symlen; + mtxt = newtree->mult->sym; + } else { + mlen = 0; + mtxt = NULL; + } + + result = astMalloc( tlen + 1 + mlen ); + if( result ) { + if( mtxt ) memcpy( result, mtxt, mlen ); + memcpy( result + mlen, newtree->name, tlen + 1 ); + } + + } else { + result = astStore( NULL, newtree->name, tlen + 1 ); + } + +/* Single argument functions... place the argument in parentheses after + the function name. */ + } else if( newtree->opcode == OP_LOG ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + if( mathmap == 1 ) { + result = astMalloc( larg0 + 8 ); + if( result ) memcpy( result, "log10(", 7 ); + a = result + 6; + } else { + result = astMalloc( larg0 + 6 ); + if( result ) memcpy( result, "log(", 5 ); + a = result + 4; + } + if( result ){ + memcpy( a, arg0, larg0 + 1 ); + memcpy( a + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + + } else if( newtree->opcode == OP_LN ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + if( mathmap == 1 ) { + result = astMalloc( larg0 + 6 ); + if( result ) memcpy( result, "log(", 5 ); + a = result + 4; + } else { + result = astMalloc( larg0 + 5 ); + if( result ) memcpy( result, "ln(", 4 ); + a = result + 3; + } + if( astOK ){ + memcpy( a, arg0, larg0 ); + memcpy( a + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + + } else if( newtree->opcode == OP_EXP ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + result = astMalloc( larg0 + 6 ); + if( result ){ + memcpy( result, "exp(", 5 ); + memcpy( result + 4, arg0, larg0 ); + memcpy( result + 4 + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + + } else if( newtree->opcode == OP_SQRT ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + result = astMalloc( larg0 + 7 ); + if( result ){ + memcpy( result, "sqrt(", 6 ); + memcpy( result + 5, arg0, larg0 ); + memcpy( result + 5 + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + +/* POW... the exponent (arg[1]) is always a constant and so does not need + to be placed in parentheses. The first argument only needs to be + placed in parentheses if it is a two arg node (except we also put it + in parentheses if it is an OP_LDVAR node and "mathmap" is zero - this is + because such OP_LDVAR nodes will correspond to axis labels which will + have spaces before and after them which would look odd if not encloses + in parentheses). */ + } else if( newtree->opcode == OP_POW ) { + + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + + arg1 = MakeExp( newtree->arg[ 1 ], mathmap, 0, status ); + larg1 = strlen( arg1 ); + + if( newtree->arg[ 0 ]->narg == 2 || + (newtree->arg[ 0 ]->opcode == OP_LDVAR && !mathmap) ) { + par = 1; + result = astMalloc( larg0 + larg1 + 7 ); + if( result ) memcpy( result, "(", 2 ); + a = result + 1; + } else { + par = 0; + result = astMalloc( larg0 + larg1 + 5 ); + a = result; + } + + if( result ) { + memcpy( a, arg0, larg0 ); + a += larg0; + if( par ) *(a++) = ')'; + memcpy( a, "**", 3 ); + a += 2; + memcpy( a, arg1, larg1 ); + a += larg1; + *a = 0; + } + + arg0 = astFree( (void *) arg0 ); + arg1 = astFree( (void *) arg1 ); + +/* DIV... the first argument (numerator) never needs to be in parentheses. + The second argument (denominator) only needs to be placed in parentheses + if it is a MULT node. */ + } else if( newtree->opcode == OP_DIV ) { + + if( mathmap == 2 && ( sunit = ModifyPrefix( newtree, status ) ) ) { + result = (char *) MakeExp( sunit, mathmap, 0, status ); + sunit = FreeTree( sunit, status ); + + } else { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + + arg1 = MakeExp( newtree->arg[ 1 ], mathmap, 0, status ); + larg1 = strlen( arg1 ); + + if( newtree->arg[ 1 ]->opcode == OP_MULT && + strchr( arg1, '*' ) ) { + par = 1; + result = astMalloc( larg0 + larg1 + 4 ); + } else { + par = 0; + result = astMalloc( larg0 + larg1 + 2 ); + } + + if( result ) { + memcpy( result, arg0, larg0 ); + a = result + larg0; + *(a++) = '/'; + if( par ) *(a++) = '('; + memcpy( a, arg1, larg1 ); + a += larg1; + if( par ) *(a++) = ')'; + *a = 0; + } + + arg0 = astFree( (void *) arg0 ); + arg1 = astFree( (void *) arg1 ); + } + +/* MULT... the second argument never needs to be in parentheses. The first + argument only needs to be placed in parentheses if it is a DIV or POW + node. */ + } else if( newtree->opcode == OP_MULT ) { + if( mathmap == 2 && ( sunit = ModifyPrefix( newtree, status ) ) ) { + result = (char *) MakeExp( sunit, mathmap, 0, status ); + sunit = FreeTree( sunit, status ); + + } else { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + + arg1 = MakeExp( newtree->arg[ 1 ], mathmap, 0, status ); + larg1 = strlen( arg1 ); + +/* If this is a top-level entry and we are producing an axis label, do + not include any constant multiplicative terms. */ + if( top && !mathmap ) { + if( newtree->arg[ 0 ]->con != AST__BAD ) arg0 = astFree( (void *) arg0 ); + if( newtree->arg[ 1 ]->con != AST__BAD ) arg1 = astFree( (void *) arg1 ); + } + +/* If we have two arguments, concatentate them, placing the operands in + parentheses if necessary. */ + if( arg0 && arg1 ) { + + if( ( newtree->arg[ 0 ]->opcode == OP_DIV && + strchr( arg0, '/' ) ) || + ( newtree->arg[ 0 ]->opcode == OP_POW && + strstr( arg0, "**" ) ) ) { + par = 1; + result = astMalloc( larg0 + larg1 + 4 ); + if( result ) result[ 0 ] = '('; + a = result + 1; + } else { + par = 0; + result = astMalloc( larg0 + larg1 + 2 ); + a = result; + } + + if( result ) { + memcpy( a, arg0, larg0 ); + a += larg0; + if( par ) *(a++) = ')'; + *(a++) = '*'; + memcpy( a, arg1, larg1 ); + a += larg1; + *a = 0; + } + + arg0 = astFree( (void *) arg0 ); + arg1 = astFree( (void *) arg1 ); + +/* If we do not have two arguments, just return the one we do have. */ + } else if( arg0 ){ + result = (char *) arg0; + + } else { + result = (char *) arg1; + } + } + } + +/* Free the complicated tree. */ + newtree = FreeTree( newtree, status ); + +/* Free the returned string if an error has occurred. */ + if( !astOK ) result = astFree( result ); + +/* Return the result. */ + return (const char *) result; +} + +static void MakeKnownUnit( const char *sym, const char *label, const char *exp, int *status ){ +/* +* Name: +* MakeKnownUnit + +* Purpose: +* Create a KnownUnit structure describing a known unit. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void MakeKnownUnit( const char *sym, const char *label, const char *exp, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a KnownUnit structure decribing a known unit, +* and adds it to the head of the linked list of known units stored in +* a module variable. + +* Parameters: +* sym +* A pointer to a string which can be used as a symbol to represent +* the new named unit. Once defined, this symbol can be included within +* the definition of other derived units. The string should contain +* only alphabetical characters (no digits, spaces, punctuation, +* etc). Symbols are case sensitive (e.g. "s" is second, but "S" is +* Siemens). The string should not include any multiplier prefix. +* label +* Pointer to a null terminated string containing the label for +* the required units. No restriction on content. +* exp +* This should be a pointer to a null terminated string containing +* a definition of the required unit. See the description of the +* "in" and "out" parameters for the astUnitMapper function. +* +* A NULL pointer or a blank string may supplied for "exp", which +* is interpreted as a request for a new basic unit to be created with +* the symbol and label given by the other parameters. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The supplied symbol and label strings are not copied. The +* supplied pointers are simply stored in the returned structure. +* Therefore the strings to which the pointers point should not be +* modified after this function returned (in fact this function is +* always called with literal strings for these arguments). +*/ + +/* Local Variables: */ + KnownUnit *result; + +/* Check the global error status. */ + if( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Allocate memory for the structure, and check the returned pointer can + be used safely. */ + result = astMalloc( sizeof( KnownUnit ) ); + if( astOK ) { + +/* In case of errors, first nullify the pointer to the next KnownUnit. */ + result->next = NULL; + +/* Store the supplied label and symbol pointers. */ + result->sym = sym; + result->label = label; + +/* Store the length of the symbol (without the trailing null character). */ + result->symlen = strlen( sym ); + +/* Store the length of the label (without the trailing null character). */ + result->lablen = strlen( label ); + +/* Create a tree of UnitNodes describing the unit if an expression was + supplied. */ + result->head = exp ? CreateTree( exp, 1, 0, status ) : NULL; + +/* Unit aliases are replaced in use by the KnownUnit pointed to by the + "use" component of the structure. Indicate this KnownUnitis not an + alias by setting its "use" component NULL. */ + result->use = NULL; + } + +/* Mark the end of the section in which memory allocations may never be + freed (other than by any AST exit handler). */ + astEndPM; + +/* If an error has occurred, free any returned structure. */ + if( !astOK ) { + result->head = FreeTree( result->head, status ); + result = astFree( result ) ; + +/* Otherwise, add the new KnownUnit to the head of the linked list of + known units. */ + } else { + result->next = known_units; + known_units = result; + } + +} + +static AstMapping *MakeMapping( UnitNode *tree, int *status ) { +/* +* Name: +* MakeMapping + +* Purpose: +* Create a new Mapping from a given tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* AstMapping *MakeMapping( UnitNode *tree ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a Mapping with a forward transformation equal +* to the transformation described by the tree of UnitNodes. The head +* node of the tree corresponds to the output of the Mapping. + +* Parameters: +* tree +* The UnitNode at the head of the tree to be used. It should have +* exactly one OP_LDVAR node, and should have been simplified using +* the SimplifyTree function. + +* Returned Value: +* A pointer to the Mapping. Its Nin and Nout attributes will both be 1. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *result; + UnitNode *inv; + UnitNode *src; + const char *fwdexp; + char *fwdfun; + const char *invexp; + char *invfun; + int lfwd; + int linv; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* First see if a UnitMap can be used to represent the Mapping from + input units to output units. This will be the case if the supplied tree + consists of a aingle OP_LDVAR node (corresponding to the input units). */ + if( tree->opcode == OP_LDVAR ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + +/* Now see if a UnitMap or ZoomMap can be used to represent the Mapping from + input units to output units. This will be the case if the supplied tree + consists of a OP_MULT node with one constant argument and on OP_LDVAR + argument (corresponding to the input units). The standardisation done by + SimplifyTree will have ensured that the constant will be argument 0 + (and will also have converted "x/k" trees into "(1/k)*x" trees). */ + } else if( tree->opcode == OP_MULT && + tree->arg[ 0 ]->con != AST__BAD && + tree->arg[ 1 ]->opcode == OP_LDVAR ) { + + if( tree->arg[ 0 ]->con == 1.0 ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + } else { + result = (AstMapping *) astZoomMap( 1, tree->arg[ 0 ]->con, "", status ); + } + +/* For other trees we need to create a MathMap. */ + } else { + +/* Format the supplied tree as an algebraic expression, and get its length. */ + fwdexp = MakeExp( tree, 1, 1, status ); + lfwd = strlen( fwdexp ); + +/* The MathMap constructor requires the forward and inverse + transformation functions to be specified as equations (i.e. including an + equals sign). We use the output variable name "output_units" (the + astUnitMapper function creates the supplied tree usign the variable + name "input_units" ). */ + lfwd += 13; + +/* Invert the supplied tree and create an algebraic expression from it. */ + src = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) src->name = astStore( NULL, "output_units", 13 ); + inv = InvertTree( tree, src, status ); + if( !inv ) { + src = FreeTree( src, status ); + astError( AST__BADUN, "MakeMapping(Unit): Failed to invert " + "supplied tree '%s' (internal AST programming error).", status, + fwdexp ); + +/* If inverted succesfully (which it should be since astUnitMapper should + have checked this)... */ + } else { + +/* Format the inverted tree as an algebraic expression, and get its + length, adding on extra characters for the variable name ("input_units") + and equals sign. */ + invexp = MakeExp( inv, 1, 1, status ); + linv = strlen( invexp ); + linv += 12; + +/* Allocate memory for the transformation functions, plus an extra + character for the trailing null. */ + fwdfun = astMalloc( lfwd + 1 ); + invfun = astMalloc( linv + 1 ); + if( invfun ) { + memcpy( fwdfun, "output_units=", 14 ); + memcpy( invfun, "input_units=", 13 ); + +/* Append the expressions following the equals signs. */ + strcpy( fwdfun + 13, fwdexp ); + strcpy( invfun + 12, invexp ); + +/* Create the MathMap. */ + result = (AstMapping *) astMathMap( 1, 1, 1, + (const char **) &fwdfun, 1, + (const char **) &invfun, + "SimpFI=1,SimpIF=1", status ); + } + +/* Free resources. */ + inv = FreeTree( inv, status ); + fwdfun = astFree( fwdfun ); + invfun = astFree( invfun ); + invexp = astFree( (void *) invexp ); + } + fwdexp = astFree( (void *) fwdexp ); + } + +/* Free any result if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the answer. */ + return result; +} + +static UnitNode *MakeLabelTree( const char *lab, int nc, int *status ){ +/* +* Name: +* MakeLabelTree + +* Purpose: +* Convert an axis label into a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *MakeLabelTree( const char *lab, int nc, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function converts an axis label into a tree of UnitNodes. +* It is assumed the supplied label represents some "basic" label +* modified by the application of one or more single function arguments +* and/or exponentiation operators. The (single) OP_LDVAR node in the +* returned tree refers to the basic label (it is stored as the "name" +* component of UnitNode structure). + +* Parameters: +* lab +* The label expression. +* nc +* The number of characters from "lab" to use. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which forms the head of a tree of UnitNodes +* representing the supplied label expression. + +* Notes: +* - A NULL value is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + Oper op; + UnitNode *result; + char buff[ 10 ]; + const char *c; + const char *exp; + int depth; + int i; + int oplen; + int n; + double con; + +/* Initialise */ + result = NULL; + oplen = 0; + +/* Check the global error status, and that we have a string. */ + if ( !astOK || !lab || !nc ) return result; + +/* Get a pointer to the first non-blank character, and store the number of + characters to examine (this excludes any trailing white space). */ + exp = lab; + while( isspace( *exp ) ) exp++; + c = lab + nc - 1; + while( c >= exp && isspace( *c ) ) c--; + nc = c - exp + 1; + +/* Scan through the supplied string looking for the first pow operator at + zero depth of nesting within parentheses. */ + depth = 0; + c = exp; + i = 0; + op = OP_NULL; + while( i < nc && *c ){ + +/* If this character is an opening parenthesis, increment the depth of + nesting. */ + if( *c == '(' ) { + depth++; + +/* If this character is an closing parenthesis, decrement the depth of + nesting. Report an error if it ever goes negative. */ + } else if( *c == ')' ) { + depth--; + if( depth < 0 && astOK ) { + astError( AST__BADUN, "Missing opening parenthesis." , status); + break; + } + +/* Ignore all other characters unless they are at zero depth of nesting. + Also ignore spaces. */ + } else if( depth == 0 && !isspace( *c ) ) { + +/* Compare the next part of the string with each of the "pow" operators. */ + if( !strncmp( c, "**", 2 ) ) { + op = OP_POW; + oplen = 2; + } else if( *c == '^' ) { + op = OP_POW; + oplen = 1; + } + +/* If an operator was found, break out of the loop. */ + if( op != OP_NULL ) break; + } + +/* Pass on to check the next character. */ + i++; + c++; + } + +/* If a "pow" operator was found, the strings on either side of it should be + valid unit expressions, in which case we use this routine recursively to + create corresponding trees of UnitNodes. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the operator. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which precedes the binary + operator. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeLabelTree( exp, i, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand before '%s'.", status, buff ); + } + +/* Create a tree of unit nodes from the string which follows the binary + operator. Report an error if it cannot be done. */ + result->arg[ 1 ] = MakeLabelTree( c + oplen, nc - i - oplen, status ); + if( !result->arg[ 1 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand after '%s'.", status, buff ); + } + } + +/* If no binary operator was found at depth zero, see if the supplied string + starts with a function name (the only legal place for a function name + given that the string has no binary operators at depth zero). */ + } else { + if( !strncmp( exp, "sqrt(", 5 ) || !strncmp( exp, "SQRT(", 5 ) ) { + op = OP_SQRT; + oplen = 4; + } else if( !strncmp( exp, "exp(", 4 ) || !strncmp( exp, "EXP(", 4 ) ) { + op = OP_EXP; + oplen = 3; + } else if( !strncmp( exp, "ln(", 3 ) || !strncmp( exp, "LN(", 3 ) ) { + op = OP_LN; + oplen = 2; + } else if( !strncmp( exp, "log(", 4 ) || !strncmp( exp, "LOG(", 4 ) ) { + op = OP_LOG; + oplen = 3; + } + +/* If a function was found, the string following the function name + (including the opening parenthesis) should form a legal units + expresssion (all the supported functions take a single argument and + so we do not need to worry about comma-separated lists of function + arguments). Use this routine recursively to create a tree of UnitNodes + from the string which forms the function argument. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the function. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which follows the function + name. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeLabelTree( exp + oplen, nc - oplen, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing argument for '%s'.", status, buff ); + } + } + +/* Arrive here if the supplied string does not contain a POW operator + or function at depth zero. Check to see if the whole string is contained + within parentheses, In which we interpret the contents of the + parentheses as a units expression. It is safe simply to check the + first and last characters (a string like "(fred)(Harry)" is not a + legal possibility since there should be an operator in the middle).*/ + } else if( nc > 0 && ( exp[ 0 ] == '(' && exp[ nc - 1 ] == ')' ) ) { + result = MakeLabelTree( exp + 1, nc - 2, status ); + +/* Does the string begin with a numerical constant? */ + } else if( ConStart( exp, &con, &n, status ) == 1 ) { + +/* If the entire string was a numerical constant, represent it by a LDCON + node. */ + if( n == nc ) { + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = con; + +/* If there was anything following the numerical constant, report an + error. */ + } else if( astOK ){ + astError( AST__BADUN, "Missing operator after " + "numerical string '%.*s'.", status, n, exp ); + } + +/* The only legal possibility left is that the string represents the basic + label. Create an OP_LDVAR node for it and store the basic label as + the node name, omitting any enclosing white space. */ + } else { + result = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) { + result->name = astStore( NULL, exp, nc + 1 ); + if( astOK ) ( (char *) result->name)[ nc ] = 0; + } + } + } + +/* Free any returned tree if an error has occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; +} + +static UnitNode *MakeTree( const char *exp, int nc, int lock, int *status ){ +/* +* Name: +* MakeTree + +* Purpose: +* Convert an algebraic units expression into a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *MakeTree( const char *exp, int nc, int lock, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function converts an algebraic units expression into a tree of +* UnitNodes. It is a service routine for CreateTree. The roots of the +* returned tree (i.e. the LDVAR nodes) refer to the unit symbols +* contained within the supplied expression (i.e. definitions of these +* units are not grafted onto the tree in place of the original nodes, +* as is done by CreateTree). + +* Parameters: +* exp +* The units expression. This should not include any leading or +* trailing spaces. +* nc +* The number of characters from "exp" to use. +* lock +* Use a mutex to guard access to the KnownUnits list? +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which forms the head of a tree of UnitNodes +* representing the supplied unit expression. + +* Notes: +* - A NULL value is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + KnownUnit *munit; + KnownUnit *unit; + Multiplier *mmult; + Multiplier *mult; + Oper op; + UnitNode *result; + char buff[ 10 ]; + char d; + const char *c; + double con; + int depth; + int i; + int l; + int maxlen; + int n; + int oplen; + int plural; + +/* Initialise */ + result = NULL; + +/* Check the global error status, and that we have a string. */ + if ( !astOK || !exp || nc <= 0 ) return result; + +/* Scan through the supplied string from the end to the start looking for + the last multiplication or division operator at zero depth of nesting + within parentheses. We go backwards through the string in order to + give the correct priority to multiple division operators (i.e. "a/b/c" + needs to be interpreted as "(a/b)/c", not "a/(b/c)"). */ + op = OP_NULL; + oplen = 1; + depth = 0; + c = exp + nc - 1; + i = nc - 1; + while( i >= 0 ){ + +/* If this character is an opening parenthesis, decrement the depth of + nesting. Report an error if it ever goes negative. */ + if( *c == '(' ) { + depth--; + if( depth < 0 && astOK ) { + astError( AST__BADUN, "Missing closing parenthesis." , status); + break; + } + +/* An opening parenthesis at level zero must always be either the first + character in the string, or be preceded by the name of a function, or + be preceded by an operator. If none of these are true, assume there is + an implicit multiplication operator before the parenthesis. */ + if( depth == 0 && i > 0 ) { + d = *( c - 1 ); + if( d != '*' && d != '/' && d != '^' && d != '.' && d != ' ' && + !EndsWith( c, i + 1, "sqrt(", status ) && !EndsWith( c, i + 1, "exp(", status ) && + !EndsWith( c, i + 1, "ln(", status ) && !EndsWith( c, i + 1, "log(", status ) ) { + op = OP_MULT; + oplen = 0; + break; + } + } + +/* If this character is an closing parenthesis, increment the depth of + nesting. */ + } else if( *c == ')' ) { + depth++; + +/* A closing parenthesis at level zero must always be either the last + character in the string, or be followed by an operator. If neither of + these are true, assume there is an implicit multiplication operator. */ + if( depth == 1 && i < nc - 1 ) { + d = *(c+1); + if( d != '*' && d != '/' && d != '^' && d != '.' && d != ' ') { + op = OP_MULT; + oplen = 0; + +/* Correct "i" so that it gives the length of the left hand operand of + the implicit MULT operator, correct "c" so that it points to the first + character in the right hand operand, and leave the loop. */ + i++; + c++; + break; + } + } + +/* Ignore all other characters unless they are at zero depth of nesting. */ + } else if( depth == 0 ) { + +/* Compare the next part of the string with each of the multiplication + and division operators. */ + if( *c == '/' ) { + op = OP_DIV; + + } else if( *c == ' ' ) { + op = OP_MULT; + +/* An asterisk is only treated as a multiplication symbol if it does not occur + before or after another asterisk. */ + } else if( *c == '*' ) { + if( c == exp ) { + if( *(c+1) != '*' ) op = OP_MULT; + } else if( i == nc - 1 ) { + if( *(c-1) != '*' ) op = OP_MULT; + } else { + if( *(c+1) != '*' && *(c-1) != '*' ) op = OP_MULT; + } + +/* A dot is only treated as a multiplication symbol if it does not occur + between two digits. */ + } else if( *c == '.' ) { + if( ( c == exp || !isdigit( *(c-1) ) ) && + ( i == nc - 1 || !isdigit( *(c+1) ) ) ) { + op = OP_MULT; + } + } + } + +/* If an operator was found, break out of the loop. */ + if( op != OP_NULL ) break; + +/* Pass on to check the next character. */ + i--; + c--; + } + +/* If a multiplication or division operator was found, the strings on either + side of it should be valid unit expressions, in which case we use this + routine recursively to create corresponding trees of UnitNodes. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the binary operator. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which precedes the binary + operator. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeTree( exp, i, lock, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand before '%s'.", status, buff ); + } + +/* Create a tree of unit nodes from the string which follows the binary + operator. Report an error if it cannot be done. */ + result->arg[ 1 ] = MakeTree( c + oplen, nc - i - oplen, lock, status ); + if( !result->arg[ 1 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand after '%s'.", status, buff ); + } + } + +/* If no multiplication or division operator was found at depth zero, check + that the final depth of nesting was zero. Report an error if not. */ + } else if( depth > 0 && astOK ) { + astError( AST__BADUN, "Missing opening parenthesis." , status); + +/* Otherwise check for a "Pow" operator at depth zero. */ + } else { + +/* Scan through the supplied string looking for the first pow operator at + zero depth of nesting within parentheses. */ + depth = 0; + c = exp; + i = 0; + while( i < nc && *c ){ + +/* If this character is an opening parenthesis, increment the depth of + nesting. */ + if( *c == '(' ) { + depth++; + +/* If this character is an closing parenthesis, decrement the depth of + nesting. Report an error if it ever goes negative. */ + } else if( *c == ')' ) { + depth--; + if( depth < 0 && astOK ) { + astError( AST__BADUN, "Missing opening parenthesis." , status); + break; + } + +/* Ignore all other characters unless they are at zero depth of nesting. */ + } else if( depth == 0 ) { + +/* Compare the next part of the string with each of the "pow" operators. */ + if( !strncmp( c, "**", 2 ) ) { + op = OP_POW; + oplen = 2; + } else if( *c == '^' ) { + op = OP_POW; + oplen = 1; + } + +/* If an operator was found, break out of the loop. */ + if( op != OP_NULL ) break; + } + +/* Pass on to check the next character. */ + i++; + c++; + } + +/* If a "pow" operator was found, the strings on either side of it should be + valid unit expressions, in which case we use this routine recursively to + create corresponding trees of UnitNodes. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the operator. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which precedes the binary + operator. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeTree( exp, i, lock, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand before '%s'.", status, buff ); + } + +/* Create a tree of unit nodes from the string which follows the binary + operator. Report an error if it cannot be done. */ + result->arg[ 1 ] = MakeTree( c + oplen, nc - i - oplen, lock, status ); + if( !result->arg[ 1 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand after '%s'.", status, buff ); + } + } + +/* If no binary operator was found at depth zero, see if the supplied string + starts with a function name (the only legal place for a function name + given that the string has no binary operators at depth zero). */ + } else { + if( !strncmp( exp, "sqrt(", 5 ) || !strncmp( exp, "SQRT(", 5 ) ) { + op = OP_SQRT; + oplen = 4; + } else if( !strncmp( exp, "exp(", 4 ) || !strncmp( exp, "EXP(", 4 ) ) { + op = OP_EXP; + oplen = 3; + } else if( !strncmp( exp, "ln(", 3 ) || !strncmp( exp, "LN(", 3 ) ) { + op = OP_LN; + oplen = 2; + } else if( !strncmp( exp, "log(", 4 ) || !strncmp( exp, "LOG(", 4 ) ) { + op = OP_LOG; + oplen = 3; + } + +/* If a function was found, the string following the function name + (including the opening parenthesis) should form a legal units + expresssion (all the supported functions take a single argument and + so we do not need to worry about comma-separated lists of function + arguments). Use this routine recursively to create a tree of UnitNodes + from the string which forms the function argument. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the function. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which follows the function + name. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeTree( exp + oplen, nc - oplen, lock, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing argument for '%s'.", status, buff ); + } + } + +/* Arrive here if the supplied string does not contain a binary operator + or function at depth zero. Check to see if the whole string is contained + within parentheses, In which we interpret the contents of the + parentheses as a units expression. It is safe simply to check the + first and last characters (a string like "(fred)(Harry)" is not a + legal possibility since there should be an operator in the middle).*/ + } else if( exp[ 0 ] == '(' && exp[ nc - 1 ] == ')' ) { + result = MakeTree( exp + 1, nc - 2, lock, status ); + +/* Does the string begin with a numerical constant? */ + } else if( ConStart( exp, &con, &n, status ) == 1 ) { + +/* If the entire string was a numerical constant, represent it by a LDCON + node. */ + if( n == nc ) { + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = con; + +/* If there was anything following the numerical constant, report an + error. */ + } else if( astOK ){ + astError( AST__BADUN, "Missing operator after " + "numerical string '%.*s'.", status, n, exp ); + } + +/* Does the string represent one of the named constants? If so represent it + by a an appropriate operator. */ + } else if( nc == 2 && ( !strncmp( exp, "pi", 2 ) || + !strncmp( exp, "PI", 2 ) ) ) { + result = NewNode( NULL, OP_LDPI, status ); + + } else if( nc == 1 && ( !strncmp( exp, "e", 1 ) || + !strncmp( exp, "E", 1 ) ) ) { + result = NewNode( NULL, OP_LDE, status ); + +/* The only legal possibility left is that the string represents the name + of a basic unit, possibly prefixed by a multiplier character. */ + } else { + +/* See if the string ends with the symbol for any of the known basic + units. If it matches more than one basic unit, choose the longest. + First ensure descriptions of the known units are available. */ + mmult = NULL; + plural = 0; + while( 1 ) { + unit = GetKnownUnits( lock, status ); + + maxlen = -1; + munit = NULL; + while( unit ) { + if( SplitUnit( exp, nc, unit->sym, 1, &mult, &l, status ) ) { + if( l > maxlen ) { + maxlen = l; + munit = unit; + mmult = mult; + } + } + unit = unit->next; + } + +/* If the above did not produce a match, try matching the unit symbol + case insensitive. */ + if( !munit ) { + unit = GetKnownUnits( lock, status ); + while( unit ) { + if( SplitUnit( exp, nc, unit->sym, 0, &mult, &l, status ) ) { + if( l > maxlen ) { + maxlen = l; + munit = unit; + mmult = mult; + } + } + unit = unit->next; + } + } + +/* If the above did not produce a match, try matching the unit label + case insensitive. */ + if( !munit ) { + unit = GetKnownUnits( lock, status ); + while( unit ) { + if( SplitUnit( exp, nc, unit->label, 0, &mult, &l, status ) ) { + if( l > maxlen ) { + maxlen = l; + munit = unit; + mmult = mult; + } + } + unit = unit->next; + } + } + +/* If we still do not have a match, and if the string ends with "s", try + removing the "s" (which could be a plural as in "Angstroms") and + trying again. */ + if( !munit && nc > 1 && !plural && + ( exp[ nc - 1 ] == 's' || exp[ nc - 1 ] == 'S' ) ) { + plural = 1; + nc--; + } else { + break; + } + } + if( plural ) nc++; + +/* If a known unit and multiplier combination was found, create an + OP_LDVAR node from it. */ + unit = munit; + mult = mmult; + if( unit ) { + +/* If the unit is an alias for another unit, it will have a non-NULL + value for its "use" component.In this case, use the unit for which the + identified unit is an alias. */ + result = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) { + result->unit = unit->use ? unit->use : unit; + result->mult = mult; + result->name = astStore( NULL, result->unit->sym, result->unit->symlen + 1 ); + } + +/* If no known unit and multiplier combination was found, we assume the + string represents a new user-defined basic unit, possibly preceded by a + standard multiplier prefix. */ + } else { + +/* Check the string to see if starts with a known multiplier prefix (but + do not allow the multiplier to account for the entire string). */ + mult = GetMultipliers( status ); + c = exp; + while( mult ) { + n = nc - mult->symlen; + if( n > 0 && !strncmp( exp, mult->sym, mult->symlen ) ) { + c += mult->symlen; + break; + } + mult = mult->next; + } + if( !mult ) n = nc; + +/* Check there are no illegal characters in the following string. */ + for( i = 0; i < n && astOK; i++ ) { + if( !isalpha( c[ i ] ) ) { + astError( AST__BADUN, "Illegal character '%c' found.", status, c[ i ] ); + break; + } + } + +/* If succesfull, create an OP_LDVAR node for th user-defined basic unit. */ + if( astOK ) { + result = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) { + result->mult = mult; + result->name = astStore( NULL, c, n + 1 ); + if( astOK ) ( (char *) result->name)[ n ] = 0; + } + } + } + } + } + } + +/* Free any returned tree if an error has occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; +} + +static void MakeUnitAlias( const char *sym, const char *alias, int *status ){ +/* +* Name: +* MakeUnitAlias + +* Purpose: +* Create a KnownUnit structure describing an alias for a known unit. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void MakeUnitAlias( const char *sym, const char *alias, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a KnownUnit structure decribing an alias for a +* known unit, and adds it to the head of the linked list of known units +* stored in a module variable. An alias is a KnownUnit which is +* identical to an existing known but which has a different symbol. + +* Parameters: +* sym +* A pointer to the symbol string of an existing KnwonUnit. The string +* should not include any multiplier prefix. +* alias +* A pointer to the symbol string to use as the alasi for the existing +* KnownUnit. The string should not include any multiplier prefix. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The supplied symbol and label strings are not copied. The +* supplied pointers are simply stored in the returned structure. +* Therefore the strings to which the pointers point should not be +* modified after this function returned (in fact this function is +* always called with literal strings for these arguments). +*/ + +/* Local Variables: */ + KnownUnit *unit; + +/* Check the global error status. */ + if( !astOK ) return; + +/* Search the existing list of KnownUnits for the specified symbol. */ + unit = known_units; + while( unit ) { + if( !strcmp( sym, unit->sym ) ) { + +/* Create a new KnownUnit for the alias. It will becomes the head of the + known units chain. */ + MakeKnownUnit( alias, unit->label, NULL, status ); + +/* Store a pointer to the KnownUnit which is to be used in place of the + alias. */ + known_units->use = unit; + +/* Leave the loop. */ + break; + } + +/* Move on to check the next existing KnownUnit. */ + unit = unit->next; + } + +/* Report an error if the supplied unit was not found. */ + if( !unit ) { + astError( AST__INTER, "MakeUnitAlias(Unit): Cannot find existing " + "units \"%s\" to associate with the alias \"%s\" (AST " + "internal programming error).", status, sym, alias ); + } +} + +static UnitNode *ModifyPrefix( UnitNode *old, int *status ) { +/* +* Name: +* ModifyPrefix + +* Purpose: +* Replace a MULT or DIV node with a LDVAR and suitable multiplier. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *ModifyPrefix( UnitNode *old, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function checks the supplied node. If it is a DIV or MULT node +* in which one argument is an LDVAR and the other is a constant, then +* its checks to see if the constant can be absorbed into the LDVAR by +* changing the multiplier in the LDVAR node. If so, it returns a new +* node which is an LDVAR with the modified multiplier. Otherwise it +* returns NULL. + +* Parameters: +* old +* Pointer to an existing UnitNode to be checked. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new UnitNode. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + Multiplier *mult; + Multiplier *mmult; + UnitNode *ldcon; + UnitNode *ldvar; + UnitNode *newtree; + UnitNode *result; + double con; + double cmult; + double r; + double rmin; + int recip; + int changed; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Indicate that we have not yet found any reason to return a changed + node. */ + changed = 0; + +/* Check the supplied node is a DIV or MULT node. */ + if( old->opcode == OP_DIV || old->opcode == OP_MULT ) { + +/* Get a copy of the supplied tree which we can modify safely. */ + newtree = CopyTree( old, status ); + +/* Identify the LDVAR argument (if any). */ + if( newtree->arg[ 0 ]->opcode == OP_LDVAR ) { + ldvar = newtree->arg[ 0 ]; + + } else if( newtree->arg[ 1 ]->opcode == OP_LDVAR ) { + ldvar = newtree->arg[ 1 ]; + + } else { + ldvar = NULL; + } + +/* Identify the LDCON argument (if any). */ + if( newtree->arg[ 0 ]->opcode == OP_LDCON ) { + ldcon = newtree->arg[ 0 ]; + + } else if( newtree->arg[ 1 ]->opcode == OP_LDCON ) { + ldcon = newtree->arg[ 1 ]; + + } else { + ldcon = NULL; + } + +/* If either was not found, return NULL. */ + if( !ldvar || !ldcon ) { + newtree = FreeTree( newtree, status ); + +/* Otherwise, extract the multiplier constant. If there is no multiplier, the + constant is 1.0. */ + } else { + cmult = ldvar->mult ? ldvar->mult->scale: 1.0; + +/* Extract the constant. */ + con = ldcon->con; + +/* Combine the multiplier and the constant. The resulting constant is a + factor which is used to multiply the LDVAR quantity. If the original + node is a DIV node in which the LDVAR is in the denominator, then + flag that we need to reciprocate the new MULT node which represents + "constant*LDVAR" before returning. */ + if( newtree->opcode == OP_MULT ) { + con = con*cmult; + recip = 0; + } else { + con = cmult/con; + recip = ( ldvar == newtree->arg[ 1 ] ); + } + +/* Find the closest known multiplier to the new constant. */ + rmin = ( con > 1 ) ? con : 1.0/con; + mmult = NULL; + mult = GetMultipliers( status ); + while( mult ) { + r = ( con > mult->scale) ? con/mult->scale : mult->scale/con; + if( r < rmin ) { + mmult = mult; + rmin = r; + } + mult = mult->next; + } + +/* Modify the constant to take account of the new multiplier chosen + above. "mmult" will be NULL if the best multiplier is unity. */ + if( mmult ) con = con/mmult->scale; + +/* If they have changed, associate the chosen multiplier with the LDVAR node, + and the constant with the LDCON node. */ + if( ldvar->mult != mmult ) { + ldvar->mult = mmult; + changed = 1; + } + + if( ldcon->con != con ) { + ldcon->con = con; + changed = 1; + } + +/* Unless the node is proportional to the reciprocal of the variable, the + new node should be a MULT node (it may originally have been a DIV). */ + if( !recip ) { + if( newtree->opcode != OP_MULT ){ + newtree->opcode = OP_MULT; + changed = 1; + } + +/* If the constant is 1.0 we can just return the LDVAR node by itself. */ + if( fabs( con - 1.0 ) < 1.0E-6 ) { + result = CopyTree( ldvar, status ); + newtree = FreeTree( newtree, status ); + changed = 1; + +/* Otherwise return the modified tree containing both LDVAR and LDCON nodes. */ + } else { + result = newtree; + } + +/* If the node is proportional to the reciprocal of the variable, the + new node will already be a DIV node and will have an LDCON as the first + argument (numerator) and an LDVAR as the second argument (denominator). */ + } else { + +/* The first argument (the numerator) should be the reciprocal of the constant + found above. */ + ldcon->con = 1.0/ldcon->con; + if( !astEQUAL( ldcon->con, old->arg[0]->con ) ) changed = 1; + +/* Return the modified tree containing both LDVAR and LDCON nodes. */ + result = newtree; + } + } + } + +/* If the new and old trees are equivalent, then we do not need to return + it. */ + if( !changed && result ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + + +static UnitNode *NewNode( UnitNode *old, Oper code, int *status ) { +/* +* Name: +* NewNode + +* Purpose: +* Create and initialise a new UnitNode. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *NewNode( UnitNode *old, Oper code, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates and initialises a new UnitNode, or +* re-initialises an existing UnitNode to use a different op code. + +* Parameters: +* old +* Pointer to an existing UnitNode to be modified, or NULL to create +* a new UnitNode. +* code +* The op code for the new UnitNode. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new UnitNode. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode **args; + UnitNode *result; + int i; + +/* Initialise. */ + result = NULL; + args = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* If an existig UnitNode was supplied, free any memory used to hold + pointers to its arguments. */ + if( old ) { + old->arg = astFree( old->arg ); + result = old; + +/* Otherwise, allocate memory for a new structure. */ + } else { + result = astMalloc( sizeof( UnitNode ) ); + } + +/* Check the pointer can be used safely. */ + if( astOK ) { + +/* Initialise the members of the UnitNode structure. */ + result->opcode = code; + result->arg = NULL; + result->con = AST__BAD; + result->name = NULL; + result->unit = NULL; + result->mult = NULL; + result->narg = 0; + + switch( code ){ + case OP_LDPI: + result->con = PI; + break; + + case OP_LDE: + result->con = E; + break; + + case OP_LOG: + case OP_LN: + case OP_EXP: + case OP_SQRT: + result->narg = 1; + break; + + case OP_POW: + case OP_DIV: + case OP_MULT: + result->narg = 2; + break; + + default: + ; + } + +/* Allocate memory for the UnitNode pointers which will locate the + nodes forming the arguments to the new node. */ + args = astMalloc( (result->narg)*sizeof( UnitNode * ) ); + if( astOK ) { + result->arg = args; + +/* Initialise the argument pointers to NULL. */ + for( i = 0; i < result->narg; i++ ) args[ i ] = NULL; + } + } + +/* Free any result if an error occurred. */ + if( !astOK ) { + args = astFree( args ); + result = astFree( result ); + } + +/* Return the answer. */ + return result; +} + +static void RemakeTree( UnitNode **node, int *status ) { +/* +* Name: +* RemakeTree + +* Purpose: +* Replace derived units within a tree of UnitNodes by basic units. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void RemakeTree( UnitNode **node, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function searches for LDVAR nodes (i.e. references to unit +* symbols) within the given tree, and replaces each such node which +* refers to known derived unit with a sub-tree of nodes which +* define the derived unit in terms of known basic units. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be simplified. On exit the supplied tree is freed and a +* pointer to a new tree is placed at the given address. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + KnownUnit *unit; + int i; + UnitNode *newnode; + +/* Check inherited status. */ + if( !astOK ) return; + +/* Initially, we have no replacement node */ + newnode = NULL; + +/* If this is an LDVAR node... */ + if( (*node)->opcode == OP_LDVAR ) { + +/* If the LDVAR node has a multiplier associated with it, we need to + introduce a OP_MULT node to perform the scaling. */ + if( (*node)->mult ) { + newnode = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + newnode->arg[0] = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + newnode->arg[0]->con = 1.0/(*node)->mult->scale; + +/* See if the node refers to a known unit. If not, or if the known unit + is a basic unit (i.e. not a derived unit) use the supplied node for + the second argument of the OP_MULT node (without the multiplier). + Otherwise, use a copy of the tree which defines the derived unit. */ + unit = (*node)->unit; + if( unit && unit->head ) { + newnode->arg[1] = CopyTree( unit->head, status ); + } else { + newnode->arg[1] = CopyTree( *node, status ); + if( astOK ) newnode->arg[1]->mult = NULL; + } + } + } + +/* If no multiplier is supplied, the replacement node is simply the tree + which defines the unscaled unit (if known), or the original node (if + unknown). */ + } else { + unit = (*node)->unit; + if( unit && unit->head ) newnode = CopyTree( unit->head, status ); + } + +/* If this is not an LDVAR Node, remake the sub-trees which form the + arguments of this node. */ + } else { + for( i = 0; i < (*node)->narg; i++ ) { + RemakeTree( &((*node)->arg[ i ]), status ); + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } +} + +static int ReplaceNode( UnitNode *target, UnitNode *old, UnitNode *new, int *status ) { +/* +* Name: +* ReplaceNode + +* Purpose: +* Replace a node within a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int ReplaceNode( UnitNode *target, UnitNode *old, UnitNode *new, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function replaces a specified node within a tree of UnitNodes +* with another given node. The original node is freed if found. + +* Parameters: +* target +* A pointer to the UnitNode at the head of the tree containing the +* node to be replaced. +* old +* A pointer to the UnitNode to be replaced. +* new +* A pointer to the UnitNode to replace "old". +* status +* Pointer to the inherited status variable. + +* Return Value: +* Non-zero if the "old" node was found and replaced (in which case +* the "old" node will have been freed). + +* Notes: +* - It is assumed that the "old" node occurs at most once within the +* target tree. +* - The node at the head of the target tree is not compared with the +* "old" node. It is assumed the called will already have done this. +* - A value of zero is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + int i; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Loop round the arguments of the node at the head of the target tree. + Break out of the loop as soone as the old node is found. */ + for( i = 0; i < target->narg; i++ ) { + +/* If this argument is the node to be replaced, free the old one and store + the new one, and then leave the loop. */ + if( target->arg[ i ] == old ) { + FreeTree( old, status ); + target->arg[ i ] = new; + result = 1; + break; + +/* Otherwise use this function recursively to search for the old node + within the current argument. */ + } else { + if( ReplaceNode( target->arg[ i ], old, new, status ) ) break; + } + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the answer. */ + return result; +} + +static int SimplifyTree( UnitNode **node, int std, int *status ) { +/* +* Name: +* SimplifyTree + +* Purpose: +* Simplify a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int SimplifyTree( UnitNode **node, int std, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function simplifies a tree of UnitNodes. It is assumed that +* all the OP_LDVAR nodes in the tree refer to the same basic unit. +* A primary purpose of this function is to standardise the tree so +* that trees which implement equivalent transformations but which +* have different structures can be compared (for instance, so that +* "2*x" and "x*2" are treated as equal trees). If "std" is non-zero, +* reducing the complexity of the tree is only of secondary importance. +* This explains why some "simplifications" actually produced trees which +* are more complicated. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be simplified. On exit the supplied tree is freed and a +* pointer to a new tree is placed at the given address. +* std +* If non-zero, perform standardisations. Otherwise only perform +* genuine simplifications. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if some change was made to the tree. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + UnitNode *node1; + UnitNode *node2; + Oper op; + UnitNode **factors; + double *powers; + int nfactor; + double coeff; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Initiallially, we have no replacement node. */ + newnode = NULL; + +/* First replace any complex constant expressions any corresponding + OP_LDCON nodes. */ + FixConstants( node, 0, status ); + +/* Simplify the sub-trees corresponding to the arguments of the node at + the head of the supplied tree. */ + for( i = 0; i < (*node)->narg; i++ ) { + if( SimplifyTree( &( (*node)->arg[ i ] ), std, status ) ) result = 1; + } + +/* Now do specific simplifications appropriate to the nature of the node at + the head of the tree. */ + op = (*node)->opcode; + +/* Natural log */ +/* =========== */ +/* We standardise argument powers into coefficients of the LN value. */ + if( op == OP_LN ) { + +/* If the argument is a OP_EXP node, they cancel out. Return a copy of the + argument of OP_EXP node. */ + if( (*node)->arg[ 0 ]->opcode == OP_EXP ) { + newnode = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + +/* If the argument is an OP_POW node, rearrange the nodes to represent + k*ln(x) instead of ln(x**k) (note pow nodes always have a constant + exponent - this is checked in InvertConstants). SQRT arguments will + not occur because they will have been changed into POW nodes when the + arguments of the supplied head node were simplified above. */ + } else if( std && (*node)->arg[ 0 ]->opcode == OP_POW ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = CopyTree( (*node)->arg[ 0 ]->arg[ 1 ], status ); + node2 = NewNode( NULL, OP_LN, status ); + if( astOK ) { + node2->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Common log */ +/* ========== */ +/* We standardise natural logs into common logs. */ + } else if( op == OP_LOG ) { + if( std ) { + newnode = NewNode( NULL, OP_DIV, status ); + node1 = NewNode( NULL, OP_LN, status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + node2->con = log( 10.0 ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Exponential */ +/* =========== */ +/* We prefer to minimise the number of EXP nodes, so, for instance, we do not + change "exp(x*y)" to "exp(x)+exp(y)" (and the code for ADD nodes does + the inverse conversion). */ + } else if( op == OP_EXP ) { + +/* If the argument is an OP_LN node, they cancel out. Return a copy of the + argument of the OP_LN node. Common log arguments will not occur because + they will have been changed into natural logs when the arguments of + the supplied head node were simplified above. */ + if( (*node)->arg[ 0 ]->opcode == OP_LN ) { + newnode = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + } + +/* Square root */ +/* =========== */ +/* We standardise sqrt nodes into pow nodes. */ + } else if( op == OP_SQRT ) { + if( std ) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = CopyTree( (*node)->arg[ 0 ], status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node2->con = 0.5; + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Exponentiation */ +/* ============== */ +/* We want to simplfy factors. So, for instance, (x*y)**k is converted to + (x**k)*(y**k). */ + } else if( op == OP_POW ) { + +/* If the first argument is an OP_EXP node, then change "(e**x)**k" into + "e**(k*x)" */ + if( (*node)->arg[ 0 ]->opcode == OP_EXP ) { + newnode = NewNode( NULL, OP_EXP, status ); + node1 = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + } + +/* "x**0" can be replaced by 1.0 */ + } else if( (*node)->arg[ 1 ]->con == 0.0 ) { + newnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) newnode->con = 1.0; + +/* "x**1" can be replaced by x */ + } else if( astEQUAL( (*node)->arg[ 1 ]->con, 1.0 ) ) { + newnode = CopyTree( (*node)->arg[ 0 ], status ); + +/* If the first argument is an OP_POW node, then change "(x**k1)**k2" into + "x**(k1*k2)" */ + } else if( (*node)->arg[ 0 ]->opcode == OP_POW ) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = ( (*node)->arg[ 0 ]->arg[ 1 ]->con )* + ( (*node)->arg[ 1 ]->con ); + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + newnode->arg[ 1 ] = node1; + } + +/* If the first argument is an OP_MULT node, then change "(x*y)**k" into + "(x**(k))*(y**(k))" */ + } else if( std && (*node)->arg[ 0 ]->opcode == OP_MULT ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_POW, status ); + if( astOK ) { + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ], status ); + node2 = CopyTree( node1, status ); + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + node2->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 1 ], status ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Division. */ +/* ========= */ +/* We standardise divisions into corresponding multiplications. */ + } else if( op == OP_DIV ) { + +/* Division by 1 is removed. */ + if( astEQUAL( (*node)->arg[ 1 ]->con, 1.0 ) ){ + newnode = CopyTree( (*node)->arg[ 0 ], status ); + +/* Division by any other constant (except zero) is turned into a + multiplication by the reciprocal constant. */ + } else if( (*node)->arg[ 1 ]->con != AST__BAD ) { + if( (*node)->arg[ 1 ]->con != 0.0 ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = 1.0/(*node)->arg[ 1 ]->con; + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ], status ); + } + } else { + astError( AST__BADUN, "Simplifying a units expression" + "requires a division by zero." , status); + } + +/* Other divisions "x/y" are turned into "x*(y**(-1))" */ + } else if( std ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_POW, status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node2->con = -1.0; + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + node1->arg[ 1 ] = node2; + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + newnode->arg[ 1 ] = node1; + } + } + +/* Multiplication */ +/* ============== */ + } else if( op == OP_MULT ) { + +/* If the right hand argument is constant, swap the arguments. */ + if( (*node)->arg[ 1 ]->con != AST__BAD ) { + newnode = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ], status ); + } + +/* Multiplication by zero produces a constant zero. */ + } else if( (*node)->arg[ 0 ]->con == 0.0 ){ + newnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) newnode->con = 0.0; + +/* Multiplication by 1 is removed. */ + } else if( astEQUAL( (*node)->arg[ 0 ]->con, 1.0 ) ){ + newnode = CopyTree( (*node)->arg[ 1 ], status ); + +/* For other MULT nodes, analyse the tree to find a list of all its + factors with an associated power for each one, and an overall constant + coefficient. */ + } else if( std ) { + FindFactors( (*node), &factors, &powers, &nfactor, &coeff, status ); + +/* Produce a new tree from these factors. The factors are standardised by + ordering them alphabetically (after conversion to a character string). */ + newnode = CombineFactors( factors, powers, nfactor, coeff, status ); + +/* Free resources */ + factors = astFree( factors ); + powers = astFree( powers ); + + } + } + +/* If we have produced a new node which is identical to the old node, + free it. Otherwise, indicate we have made some changes. */ + if( newnode ) { + if( !CmpTree( newnode, *node, 1, status ) ) { + newnode = FreeTree( newnode, status ); + } else { + result = 1; + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } + +/* If the above produced some change, try re-simplifying the tree. */ + if( result ) SimplifyTree( node, std, status ); + +/* Return the result. */ + return result; + +} + +static int SplitUnit( const char *str, int ls, const char *u, int cs, + Multiplier **mult, int *l, int *status ) { +/* +* Name: +* SplitUnit + +* Purpose: +* Split a given string into unit name and multiplier. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int SplitUnit( const char *str, int ls, const char *u, int cs, +* Multiplier **mult, int *l, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* Returns non-zer0 if the supplied string ends with the supplied unit +* name or label, and any leading string is a known multiplier. + +* Parameters: +* str +* The string to test, typically containing a multiplier and a unit +* symbol or label. +* ls +* Number of characters to use from "str" (not including trailing null) +* u +* Pointer to the unit label or symbol string to be searched for. +* cs +* If non-zero, the test for "u" is case insensitive. +* mult +* Address of a location at which to return the multiplier at the +* start of the supplied string. NULL is returned if the supplied +* string does not match the supplied unit, or if the string +* includes no multiplier. +* l +* Address of an int in which to return the length of "u". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if "str" ends with "u" and starts with a null string or a +* known multiplier string. + +*/ + +/* Local Variables: */ + int ret; + int lm; + int lu; + +/* Initialise */ + ret = 0; + *mult = NULL; + *l = 0; + +/* Check inherited status. */ + if( !astOK ) return ret; + +/* Find the number of characters in the supplied unit label or symbol. The + difference between lu and ls must be the length of the multiplier. */ + lu = strlen( u ); + lm = ls - lu; + +/* Make sure "str" is not shorter than "u" */ + if( lm >= 0 ) { + +/* Compare the end of "str" against "u" */ + if( cs ? !strncmp( str + lm, u, lu ) : + !Ustrncmp( str + lm, u, lu, status ) ) { + ret = 1; + +/* If "str" ends with "u", see if it starts with a known multiplier */ + if( lm > 0 ) { + ret = 0; + *mult = GetMultipliers( status ); + while( *mult ) { + if( (*mult)->symlen == lm && !strncmp( str, (*mult)->sym, lm ) ) { + ret = 1; + break; + } + *mult = (*mult)->next; + } + +/* If not, try again using case-insensitive matching. */ + if( !ret ) { + *mult = GetMultipliers( status ); + while( *mult ) { + if( (*mult)->symlen == lm && !Ustrncmp( str, (*mult)->sym, lm, status ) ) { + ret = 1; + break; + } + *mult = (*mult)->next; + } + } + +/* If not, try again using case-insensitive matching against the + multiplier label. */ + if( !ret ) { + *mult = GetMultipliers( status ); + while( *mult ) { + if( (*mult)->lablen == lm && !Ustrncmp( str, (*mult)->label, lm, status ) ) { + ret = 1; + break; + } + *mult = (*mult)->next; + } + } + + } + } + } + + *l = lu; + return ret; +} + +double astUnitAnalyser_( const char *in, double powers[9], int *status ){ +/* +*+ +* Name: +* astUnitAnalyser + +* Purpose: +* Perform a dimensional analysis of a unti string. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* double astUnitAnalyser_( const char *in, double powers[9] ) + +* Class Membership: +* Unit member function. + +* Description: +* This function parses the supplied units string if possible, and +* returns a set of pwoers and a scaling factor which represent the +* units string. + +* Parameters: +* in +* A string representation of the units, for instance "km/h". +* powers +* An array in which are returned the powers for each of the following +* basic units (in the order shown): kilogramme, metre, second, radian, +* Kelvin, count, adu, photon, magnitude, pixel. If the supplied unit +* does not depend on a given basic unit a value of 0.0 will be returned +* in the array. The returns values represent a system of units which is +* a scaled form of the supplied units, expressed in the basic units of +* m, kg, s, rad, K, count, adu, photon, mag and pixel. For instance, a +* returned array of [1,0,-2,0,0,0,0,0,0] would represent "m/s**2". + +* Returned Value: +* A scaling factor for the supplied units. The is the value, in the +* units represented by the returned powers, which corresponds to a +* value of 1.0 in the supplied units. + +* Notes: +* - An error will be reported if the units string cannot be parsed +* or refers to units or functions which cannot be analysed in this way. +* - AST__BAD is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + UnitNode *in_tree; + double result; + +/* Initialise */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Parse the input units string, producing a tree of UnitNodes which + represents the input units. A pointer to the UnitNode at the head of + the tree is returned if succesfull. Report a context message if this + fails. */ + in_tree = CreateTree( in, 1, 1, status ); + if( in_tree ) { + +/* Analyse the tree */ + if( !DimAnal( in_tree, powers, &result, status ) && astOK ) { + result = AST__BAD; + astError( AST__BADUN, "astUnitAnalyser: Error analysing input " + "units string '%s' (it may contain unsupported " + "functions or dimensionless units).", status, in ); + } + +/* Free the tree. */ + in_tree = FreeTree( in_tree, status ); + + } else if( astOK ) { + astError( AST__BADUN, "astUnitAnalyser: Error parsing input " + "units string '%s'.", status, in ); + } + +/* Return the result */ + return result; +} + +const char *astUnitLabel_( const char *sym, int *status ){ +/* +*+ +* Name: +* astUnitLabel + +* Purpose: +* Return a string label for a given unit symbol. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* const char *astUnitLabel( const char *sym ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to a constant string containing a +* descriptive label for the unit specified by the given unit symbol. + +* Parameters: +* sym +* A string holing a known unit symbol. + +* Returned Value: +* A pointer to constant string holding a descriptive label for the +* supplied unit. A NULL pointer is returned (without error) if the +* supplied unit is unknown. + +* Notes: +* - A NULL pointer is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *result; + KnownUnit *unit; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Ensure descriptions of the known units are available. */ + unit = GetKnownUnits( 1, status ); + +/* Loop through the chain of known units looking for a unit with a symbol + equal to the supplied string. If found, store a pointer to its label + and break out of the loop. */ + while( unit ) { + if( !strcmp( sym, unit->sym ) ) { + result = unit->label; + break; + } + + unit = unit->next; + } + +/* Return the answer. */ + return result; +} + +AstMapping *astUnitMapper_( const char *in, const char *out, + const char *in_lab, char **out_lab, int *status ){ +/* +*+ +* Name: +* astUnitMapper + +* Purpose: +* Create a Mapping between two system of units. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* AstMapping *astUnitMapper( const char *in, const char *out, +* const char *in_lab, char **out_lab ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a Mapping between two specified system of +* units. It also modifes a supplied label (which is typically +* the axis label associated with the input units) so that it includes +* any functional change implied by the supplied "in" and "out" units. + +* Parameters: +* in +* A string representation of the input units, for instance "km/h". +* See "Unit Representations:" below. +* out +* A string representation of the output units, for instance "m/s". +* See "Unit Representations:" below. +* in_lab +* A label describing the quantity associated with the input units. +* If the "in" string is the Units attribute of an Axis, then +* "in_lab" should be the Label of the same Axis. May be supplied +* NULL in which case "out_lab" is ignored. +* out_lab +* The address at which to return a pointer to a label describing the +* quantity associated with the output units. For instance, if the +* input and output units are "Hz" and "sqrt(Hz)", and the input +* label is "Frequency", then the returned output label will be +* "sqrt( Frequency )". The returned label is stored in dynamically +* allocated memory which should be freed (using astFree) when no longer +* needed. + +* Returned Value: +* A pointer to a Mapping which can be used to transform values in the +* "in" system of units into the "out" system of units. The Mapping +* will have 1 input and 1 output. + +* Unit Representations: +* The string supplied for "in" and "out" should represent a system of +* units following the recommendations of the FITS WCS paper I +* "Representation of World Coordinates in FITS" (Greisen & Calabretta). +* Various commonly used variants are also allowed. +* +* To summarise, a string describing a system of units should be an +* algebraic expression which combines one or more named units. The +* following functions and operators may be used within these algebraic +* expressions: +* +* - "*": multiplication. A period "." or space " " may also be used +* to represent multiplication (a period is only interpreted as a +* multiplication operator if it is not positioned between two digits, +* and a space is only interpreted as a multiplication operator if it +* occurs between two operands). +* - "/": division. +* - "**": exponentiation. The exponent (i.e. the operand following the +* exponentiation operator) must be a constant. The symbol "^" is also +* interpreted as an exponentiation operator. Exponentiation is also +* implied by an integer following a unit name without any separator +* (e.g. "cm2" is "cm^2"). +* - log(): Common logarithm. +* - ln(): Natural logarithm. +* - sqrt(): Square root. +* - exp(): Exponential. +* +* Function names are case insensitive. White space may be included +* within an expression (note that white space between two operands +* will be interpreted as a muiltiplication operator as described +* above). Parentheses may be used to indicate the order in which +* expressions are to be evaluated (normal mathematical precedence is +* used otherwise). The following symbols may be used to represent +* constants: +* +* - "pi" +* - "e" +* +* These symbols are also case in-sensitive. +* +* The above operators and functions are used to combine together one +* or more "unit symbols". The following base unit symbols are recognised: +* +* - "m": metre. +* - "g": gram. +* - "s": second. +* - "rad": radian. +* - "sr": steradian. +* - "K": Kelvin. +* - "mol": mole. +* - "cd": candela. +* +* The following symbols for units derived fro the above basic units are +* recognised: +* +* - "sec": second (1 s) +* - "Hz": Hertz (1/s). +* - "N": Newton (kg m/s**2). +* - "J": Joule (N m). +* - "W": Watt (J/s). +* - "C": Coulomb (A s). +* - "V": Volt (J/C). +* - "Pa": Pascal (N/m**2). +* - "Ohm": Ohm (V/A). +* - "S": Siemens (A/V). +* - "F": Farad (C/V). +* - "Wb": Weber (V s). +* - "T": Tesla (Wb/m**2). +* - "H": Henry (Wb/A). +* - "lm": lumen (cd sr). +* - "lx": lux (lm/m**2). +* - "deg": degree (pi/180 rad). +* - "arcmin": arc-minute (1/60 deg). +* - "arcsec": arc-second (1/3600 deg). +* - "mas": milli-arcsecond (1/3600000 deg). +* - "min": minute (60 s). +* - "h": hour (3600 s). +* - "d": day (86400 s). +* - "yr": year (31557600 s). +* - "a": year (31557600 s). +* - "eV": electron-Volt (1.60217733E-19 J). +* - "erg": erg (1.0E-7 J). +* - "Ry": Rydberg (13.605692 eV). +* - "solMass": solar mass (1.9891E30 kg). +* - "u": unified atomic mass unit (1.6605387E-27 kg). +* - "solLum": solar luminosity (3.8268E26 W). +* - "Angstrom": Angstrom (1.0E-10 m). +* - "Ang": Angstrom +* - "A": Ampere +* - "micron": micron (1.0E-6 m). +* - "solRad": solar radius (6.9599E8 m). +* - "AU": astronomical unit (1.49598E11 m). +* - "lyr": light year (9.460730E15 m). +* - "pc": parsec (3.0867E16 m). +* - "count": count. +* - "ct": count. +* - "adu": analogue-to-digital converter unit. +* - "photon": photon. +* - "ph": photon. +* - "Jy": Jansky (1.0E-26 W /m**2 /Hz). +* - "Jan": Jansky +* - "mag": magnitude. +* - "G": Gauss (1.0E-4 T). +* - "pixel": pixel. +* - "pix": pixel. +* - "barn": barn (1.0E-28 m**2). +* - "D": Debye (1.0E-29/3 C.m). +* +* In addition, any other unknown unit symbol may be used (but of course +* no mapping will be possible between unknown units). +* +* Unit symbols may be preceded with a numerical constant (for +* instance "1000 m") or a standard multiplier symbol (for instance "km") +* to represent some multiple of the unit. The following standard +* multipliers are recognised: +* +* - "d": deci (1.0E-1) +* - "c": centi (1.0E-2) +* - "m": milli (1.0E-3) +* - "u": micro (1.0E-6) +* - "n": nano (1.0E-9) +* - "p": pico (1.0E-12) +* - "f": femto (1.0E-15) +* - "a": atto (1.0E-18) +* - "z": zepto (1.0E-21) +* - "y": yocto (1.0E-24) +* - "da": deca (1.0E1) +* - "h": hecto (1.0E2) +* - "k": kilo (1.0E3) +* - "M": mega (1.0E6) +* - "G": giga (1.0E9) +* - "T": tera (1.0E12) +* - "P": peta (1.0E15) +* - "E": exa (1.0E18) +* - "Z": zetta (1.0E21) +* - "Y": yotta (1.0E24) + +* Notes: +* - NULL values are returned without error if the supplied units are +* incompatible (for instance, if the input and output units are "kg" +* and "m" ). +* - NULL values are returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstMapping *result; + UnitNode **units; + UnitNode *in_tree; + UnitNode *intemp; + UnitNode *inv; + UnitNode *labtree; + UnitNode *newtest; + UnitNode *out_tree; + UnitNode *outtemp; + UnitNode *src; + UnitNode *testtree; + UnitNode *tmp; + UnitNode *totaltree; + UnitNode *totlabtree; + const char *c; + const char *exp; + int i; + int nc; + int nunits; + int ipass; + +/* Initialise */ + result = NULL; + if( in_lab ) *out_lab = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* A quick check for a common simple case: if the two strings are + identical, return a UnitMap.*/ + if( !strcmp( in, out ) ) { + if( in_lab ) *out_lab = astStore( NULL, in_lab, strlen( in_lab ) + 1 ); + return (AstMapping *) astUnitMap( 1, "", status ); + } + +/* More initialisation. */ + in_tree = NULL; + out_tree = NULL; + units = NULL; + +/* Parse the input units string, producing a tree of UnitNodes which + represents the input units. A pointer to the UnitNode at the head of + the tree is returned if succesfull. Report a context message if this + fails. The returned tree contains branch nodes which correspond to + operators or functions, and leaf nodes which represent constant values + or named basic units (m, s, g, K, etc). Each branch node has one or more + "arguments" (i.e. child nodes) which are operated on or combined by + the branch node in some way to produce the nodes "value". This value + is then used as an argument for the node's parent node (if any). If + the string supplied by the user refers to any known derived units (e.g. "N", + Newton) then each such unit is represented in the returned tree by a + complete sub-tree in which the head node corresponds to the derived + unit (e.g. "N") and the leaf nodes correspond to the basic units needed + to define the derived unit ( for instance, "m", "s" and "g" - metres, + seconds and grammes), or numerical constants. Thus every leaf node in the + returned tree will be a basic unit (i.e. a unit which is not defined in + terms of other units), or a numerical constant. */ + in_tree = CreateTree( in, 1, 1, status ); + if( !astOK ) astError( AST__BADUN, "astUnitMapper: Error parsing input " + "units string '%s'.", status, in ); + +/* Do the same for the output units. */ + if( astOK ) { + out_tree = CreateTree( out, 1, 1, status ); + if( !astOK ) astError( AST__BADUN, "astUnitMapper: Error parsing output " + "units string '%s'.", status, out ); + } + +/* If a blank string is supplied for both input and output units, then + assume a UnitMap is the appropriate Mapping. */ + if( !in_tree && !out_tree && astOK ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + if( in_lab ) *out_lab = astStore( NULL, in_lab, strlen( in_lab ) + 1 ); + +/* Otherwise, if we have both input and output trees... */ + } else if( in_tree && out_tree && astOK ) { + +/* Locate all the basic units used within either of these two trees. An + array is formed in which each element is a pointer to a UnitNode + contained within one of the trees created above. Each basic unit + referred to in either tree will have a single entry in this array + (even if the unit is referred to more than once). */ + units = NULL; + nunits = 0; + LocateUnits( in_tree, &units, &nunits, status ); + LocateUnits( out_tree, &units, &nunits, status ); + +/* Due to the simple nature of the simplification process in SimplifyTree, + the following alogorithm sometimes fails to find a Mapping form input + to output units, but can find a Mapping from output to input units. + In this latter case, we can get the required Mapping from input to + output simply by inverting the Mapign from output to input. So try + first with the units in the original order. If this fails to find a + Mapping, try again with the units swapped, and note that the final + Mapping should be inverted before being used. */ + for( ipass = 0; ipass < 2; ipass++ ){ + if( ipass == 1 ) { + tmp = in_tree; + in_tree = out_tree; + out_tree = tmp; + } + +/* We are going to create a new tree of UnitNodes in which the head node + corresponds to the requested output units, and which has a single + non-constant leaf node corresponding to the input units. Initialise a + pointer to this new tree to indicate that it has not yet been created. */ + testtree = NULL; + +/* Loop round each basic unit used in the definition of either the input + or the output units (i.e. the elements of the array created above by + "LocateUnits"). The unit selected by this loop is referred to as the + "current" unit. On each pass through this loop, we create a tree which + is a candidate for the final required tree (the "test tree" pointed to + by the testtree pointer initialised above). In order for a mapping to + be possible between input and output units, the test tree created on + each pass through this loop must be equivalent to the test tree for the + previous pass (in other words, all the test trees must be equivalent). + We break out of the loop (and return a NULL Mapping) as soon as we find + a test tree which differs from the previous test tree. */ + for( i = 0; i < nunits; i++ ) { + +/* Create copies of the trees describing the input and output units, in which + all units other than the current unit are set to a constant value of 1. + This is done by replacing OP_LDVAR nodes (i.e. nodes which "load" the + value of a named basic unit) by OP_LDCON nodes (i.e. nodes which load + a specified constant value) in the tree copy. */ + intemp = FixUnits( in_tree, units[ i ], status ); + outtemp = FixUnits( out_tree, units[ i ], status ); + +/* Simplify these trees. An important side-effect of this simplification + is that trees are "standardised" which allows them to be compared for + equivalence. A single mathematical expression can often be represented + in many different ways (for instance "A/B" is equivalent to "(B**(-1))*A"). + Standardisation is a process of forcing all equivalent representations + into a single "standard" form. Without standardisation, trees representing + the above two expressions would not be considered to be equivalent + since thy would contain different nodes and have different structures. + As a consequence of this standardisation, the "simplification" performed + by SimplifyTree can sometimes actually make the tree more complicated + (in terms of the number of nodes in the tree). */ + SimplifyTree( &intemp, 1, status ); + SimplifyTree( &outtemp, 1, status ); + +/* If either of the simplified trees does not depend on the current unit, + then the node at the head of the simplified tree will have a constant + value (because all the units other than the current unit have been fixed + to a constant value of 1.0 above by FixUnits, leaving only the current + unit to vary in value). If both simplified trees are constants, then + neither tree depends on the current basic unit (i.e. references to the + current basic unit cancel out within each string expression - for + instance if converting from "m.s.Hz" to "km" and the current unit + is "s", then the "s.Hz" term will cause the "s" units to cancel out). In + this case ignore this basic unit and pass on to the next. */ + if( outtemp->con != AST__BAD && intemp->con != AST__BAD ) { + +/* If just one simplified tree is constant, then the two units cannot + match since one depends on the current basic unit and the other does + not. Free any test tree from previous passes and break out of the loop. */ + } else if( outtemp->con != AST__BAD || intemp->con != AST__BAD ) { + intemp = FreeTree( intemp, status ); + outtemp = FreeTree( outtemp, status ); + testtree = FreeTree( testtree, status ); + break; + +/* If neither simplified tree is constant, both depend on the current + basic unit and so we can continue to see if their dependencies are + equivalent. */ + } else { + +/* We are going to create a new tree which is the inverse of the above + simplified "intemp" tree. That is, the new tree will have a head node + corresponding to the current unit, and a single non-constant leaf node + corresponding to the input units. Create an OP_LDVAR node which can be + used as the leaf node for this inverted tree. If the input tree is + inverted successfully, this root node becomes part of the inverted tree, + and so does not need to be freed explicitly (it will be freed when the + inverted tree is freed). */ + src = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) src->name = astStore( NULL, "input_units", 12 ); + +/* Now produce the inverted input tree. If the tree cannot be inverted, a + null pointer is returned. Check for this. Otherwise a pointer to the + UnitNode at the head of the inverted tree is returned. */ + inv = InvertTree( intemp, src, status ); + if( inv ) { + +/* Concatenate this tree (which goes from "input units" to "current unit") + with the simplified output tree (which goes from "current unit" to + "output units"), to get a new tree which goes from input units to output + units. */ + totaltree = ConcatTree( inv, outtemp, status ); + +/* Simplify this tree. */ + SimplifyTree( &totaltree, 1, status ); + +/* Compare this simplified tree with the tree produced for the previous + unit (if any). If they differ, we cannot map between the supplied + units so annul the test tree and break out of the loop. If this is the + first unit to be tested, use the total tree as the test tree for the + next unit. */ + if( testtree ) { + if( CmpTree( totaltree, testtree, 0, status ) ) testtree = FreeTree( testtree, status ); + totaltree = FreeTree( totaltree, status ); + if( !testtree ) break; + } else { + testtree = totaltree; + } + } + +/* If the input tree was inverted, free the inverted tree. */ + if( inv ) { + inv = FreeTree( inv, status ); + +/* If the input tree could not be inverted, we cannot convert between input + and output units. Free the node which was created to be the root of the + inverted tree (and which has consequently not been incorporated into the + inverted tree), free any testtree and break out of the loop. */ + } else { + src = FreeTree( src, status ); + testtree = FreeTree( testtree, status ); + break; + } + } + +/* Free the other trees. */ + intemp = FreeTree( intemp, status ); + outtemp = FreeTree( outtemp, status ); + + } + +/* If all the basic units used by either of the supplied system of units + produced the same test tree, leave the "swap in and out units" loop. */ + if( testtree ) break; + + } + +/* If the input and output units have been swapped, swap them back to + their original order, and invert the test tree (if there is one). */ + if( ipass > 0 ) { + tmp = in_tree; + in_tree = out_tree; + out_tree = tmp; + if( testtree ) { + src = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) src->name = astStore( NULL, "input_units", 12 ); + newtest = InvertTree( testtree, src, status ); + FreeTree( testtree, status ); + testtree = newtest; + if( !newtest ) src = FreeTree( src, status ); + } + } + +/* If all the basic units used by either of the supplied system of units + produced the same test tree, create a Mapping which is equivalent to the + test tree and return it. */ + if( testtree ) { + result = MakeMapping( testtree, status ); + +/* We now go on to produce the output axis label from the supplied input + axis label. Get a tree of UnitNodes which describes the supplied label + associated with the input axis. The tree will have single OP_LDVAR node + corresponding to the basic label (i.e. the label without any single + argument functions or exponentiation operators applied). */ + if( in_lab && astOK ) { + +/* Get a pointer to the first non-blank character, and store the number of + characters to examine (this excludes any trailing white space). */ + exp = in_lab; + while( isspace( *exp ) ) exp++; + c = exp + strlen( exp ) - 1; + while( c >= exp && isspace( *c ) ) c--; + nc = c - exp + 1; + +/* Create the tree. */ + labtree = MakeLabelTree( exp, nc, status ); + if( astOK ) { + +/* Concatenate this tree (which goes from "basic label" to "input label") + with the test tree found above (which goes from "input units" to "output + units"), to get a tree which goes from basic label to output label. */ + totlabtree = ConcatTree( labtree, testtree, status ); + +/* Simplify this tree. */ + SimplifyTree( &totlabtree, 1, status ); + +/* Create the output label from this tree. */ + *out_lab = (char *) MakeExp( totlabtree, 0, 1, status ); + +/* Free the trees. */ + totlabtree = FreeTree( totlabtree, status ); + labtree = FreeTree( labtree, status ); + +/* Report a context error if the input label could not be parsed. */ + } else { + astError( AST__BADUN, "astUnitMapper: Error parsing axis " + "label '%s'.", status, in_lab ); + } + } + +/* Free the units tree. */ + testtree = FreeTree( testtree, status ); + + } + } + +/* Free resources. */ + in_tree = FreeTree( in_tree, status ); + out_tree = FreeTree( out_tree, status ); + units = astFree( units ); + +/* If an error has occurred, annul the returned Mapping. */ + if( !astOK ) { + result = astAnnul( result ); + if( in_lab ) *out_lab = astFree( *out_lab ); + } + +/* Return the result. */ + return result; + +} + +const char *astUnitNormaliser_( const char *in, int *status ){ +/* +*+ +* Name: +* astUnitNormalizer + +* Purpose: +* Normalise a unit string into FITS-WCS format. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* const char *astUnitNormaliser( const char *in ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a standard FITS-WCS form of the supplied unit +* string. + +* Parameters: +* in +* A string representation of the units, for instance "km/h". + +* Returned Value: +* A pointer to a dynamically allocated string holding the normalized +* unit string. It should be freed using astFree when no longer needed. + +* Notes: +* - An error will be reported if the units string cannot be parsed. +* - NULL is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + UnitNode *in_tree; + double dval; + const char *result; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Parse the input units string, producing a tree of UnitNodes which + represents the input units. A pointer to the UnitNode at the head of + the tree is returned if succesfull. Report a context message if this + fails. */ + in_tree = CreateTree( in, 0, 1, status ); + if( in_tree ) { + +/* Simplify the units expression, only doing genuine simplifications. */ + SimplifyTree( &in_tree, 1, status ); + +/* Invert literal constant unit multipliers. This is because a constant of + say 1000 for a unit of "m" means "multiply the value in metres by 1000", + but a unit string of "1000 m" means "value in units of 1000 m" (i.e. + *divide* the value in metres by 1000). */ + InvertConstants( &in_tree, status ); + +/* Convert the tree into string form. */ + result = MakeExp( in_tree, 2, 1, status ); + +/* If the result is a constant value, return a blank string. */ + if( 1 == astSscanf( result, "%lg", &dval ) ) { + *((char *) result) = 0; + } + +/* Free the tree. */ + in_tree = FreeTree( in_tree, status ); + + } else { + astError( AST__BADUN, "astUnitNormaliser: Error parsing input " + "units string '%s'.", status, in ); + } + +/* Return the result */ + return result; +} + +static int Ustrcmp( const char *a, const char *b, int *status ){ +/* +* Name: +* Ustrcmp + +* Purpose: +* A case blind version of strcmp. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int Ustrcmp( const char *a, const char *b, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* Returns 0 if there are no differences between the two strings, and 1 +* otherwise. Comparisons are case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strcmp" does. +* - This function attempts to execute even if an error has occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Loop round each character. */ + while( 1 ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + +static int Ustrncmp( const char *a, const char *b, size_t n, int *status ){ +/* +* Name: +* Ustrncmp + +* Purpose: +* A case blind version of strncmp. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int Ustrncmp( const char *a, const char *b, size_t n, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* Returns 0 if there are no differences between the first "n" +* characters of the two strings, and 1 otherwise. Comparisons are +* case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. +* n +* The maximum number of characters to compare. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strncmp" does. +* - This function attempts to execute even if an error has occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int i; /* Character index */ + int ret; /* Returned value */ + + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Check pointer have been supplied. */ + if( !a || !b ) return ret; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Compare up to "n" characters. */ + for( i = 0; i < (int) n; i++ ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + + + + + + + + + + + + +/* The rest of this file contains functions which are of use for debugging + this module. They are usually commented out. + +static const char *DisplayTree( UnitNode *node, int ind ) { + int i; + char buf[200]; + const char *result; + char *a; + const char *arg[ 2 ]; + int rl; + int slen; + const opsym[ 100 ]; + + result = ""; + + for( i = 0; i < ind; i++ ) buf[ i ] = ' '; + buf[ ind ] = 0; + + if( !node ) { + printf( "%s \n", buf ); + } else { + + printf( "%s Code: '%s' (%d)\n", buf, OpName( node->opcode ), node->opcode ); + printf( "%s Narg: %d\n", buf, node->narg ); + printf( "%s Constant: %g\n", buf, node->con ); + printf( "%s Name: %s\n", buf, node->name?node->name:"" ); + printf( "%s Unit: %s\n", buf, node->unit?node->unit->sym:"" ); + printf( "%s Mult: %s\n", buf, node->mult?node->mult->sym:"" ); + + OpSym( node, opsym ); + slen = strlen( opsym ); + rl = slen; + + if( node->narg == 0 ) { + result = astMalloc( rl + 1 ); + if( astOK ) strcpy( (char *) result, opsym ); + + } else if( node->narg == 1 ) { + rl += 2; + printf( "%s Arg 0:\n", buf ); + arg[ 0 ] = DisplayTree( (node->arg)[ 0 ], ind + 2 ); + rl += strlen( arg[ 0 ] ); + + result = astMalloc( rl + 1 ); + if( astOK ) { + a = (char *) result; + strcpy( a, opsym ); + a += slen; + *(a++) = '('; + strcpy( a, arg[0] ); + a += strlen( arg[ 0 ] ); + *(a++) = ')'; + } + + } else { + rl += 4; + for( i = 0; i < node->narg; i++ ) { + printf( "%s Arg %d:\n", buf, i ); + arg[ i ] = DisplayTree( (node->arg)[ i ], ind + 2 ); + rl += strlen( arg[ i ] ); + } + + result = astMalloc( rl + 1 ); + if( astOK ) { + a = (char *) result; + *(a++) = '('; + strcpy( a, arg[0] ); + a += strlen( arg[ 0 ] ); + *(a++) = ')'; + strcpy( a, opsym ); + a += slen; + *(a++) = '('; + strcpy( a, arg[1] ); + a += strlen( arg[ 1 ] ); + *(a++) = ')'; + } + } + } + + if( !astOK ) { + astFree( (void *) result ); + result = ""; + } + + return result; +} + +static const char *OpName( Oper op ) { + const char *name; + + if( op == OP_LDCON ) { + name = "LDCON"; + } else if( op == OP_LDVAR ) { + name = "LDVAR"; + } else if( op == OP_LOG ) { + name = "LOG"; + } else if( op == OP_LN ) { + name = "LN"; + } else if( op == OP_EXP ) { + name = "EXP"; + } else if( op == OP_SQRT ) { + name = "SQRT"; + } else if( op == OP_POW ) { + name = "POW"; + } else if( op == OP_DIV ) { + name = "DIV"; + } else if( op == OP_MULT ) { + name = "MULT"; + } else if( op == OP_LDPI ) { + name = "LDPI"; + } else if( op == OP_LDE ) { + name = "LDE"; + } else if( op == OP_NULL ) { + name = "NULL"; + } else { + name = ""; + } + + return name; +} + +static void OpSym( UnitNode *node, char *buff ) { + const char *sym = NULL; + + if( node->con != AST__BAD ) { + sprintf( buff, "%g", node->con ); + + } else if( node->opcode == OP_LDVAR ) { + sym = node->name; + + } else if( node->opcode == OP_LOG ) { + sym = "log"; + + } else if( node->opcode == OP_LN ) { + sym = "ln"; + + } else if( node->opcode == OP_EXP ) { + sym = "exp"; + + } else if( node->opcode == OP_SQRT ) { + sym = "sqrt"; + + } else if( node->opcode == OP_POW ) { + sym = "**"; + + } else if( node->opcode == OP_DIV ) { + sym = "/"; + + } else if( node->opcode == OP_MULT ) { + sym = "*"; + + } else if( node->opcode == OP_NULL ) { + sym = "NULL"; + + } else { + sym = ""; + } + + if( sym ) strcpy( buff, sym ); +} + +static const char *TreeExp( UnitNode *node ) { + char buff[ 100 ]; + char buff2[ 100 ]; + + if( node->narg == 0 ) { + OpSym( node, buff ); + + } else if( node->narg == 1 ) { + OpSym( node, buff2 ); + sprintf( buff, "%s(%s)", buff2, TreeExp( node->arg[ 0 ] ) ); + + } else if( node->narg == 2 ) { + OpSym( node, buff2 ); + sprintf( buff, "(%s)%s(%s)", TreeExp( node->arg[ 0 ] ), buff2, + TreeExp( node->arg[ 1 ] ) ); + } + + return astStore( NULL, buff, strlen( buff ) + 1 ); +} + +*/ + + + + diff --git a/unit.h b/unit.h new file mode 100644 index 0000000..3637f13 --- /dev/null +++ b/unit.h @@ -0,0 +1,83 @@ +#if !defined( UNIT_INCLUDED ) /* Include this file only once */ +#define UNIT_INCLUDED +/* +*+ +* Name: +* unit.h + +* Purpose: +* Define the interface to the Unit module. + +* Description: +* This module defines functions which identify units and transform +* between them. +* +* Note that this module is not a class implementation, although it +* resembles one. + +* Functions Defined: +* Public: +* None. +* +* Protected: + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 10-DEC-2002 (DSB): +* Original version. +*- +*/ + +/* Include files. */ +/* ============== */ +#include "mapping.h" /* Coordinate mappings */ + +/* C header files. */ +/* --------------- */ + +/* Function prototypes. */ +/* ==================== */ +#if defined(astCLASS) /* Protected */ +AstMapping *astUnitMapper_( const char *, const char *, const char *, + char **, int * ); +const char *astUnitLabel_( const char *, int * ); +double astUnitAnalyser_( const char *, double[9], int * ); +const char *astUnitNormaliser_( const char *, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These wrap up the functions defined by this module. */ + +#if defined(astCLASS) /* Protected */ +#define astUnitMapper(in,out,inlab,outlab) astINVOKE(O,astUnitMapper_(in,out,inlab,outlab,STATUS_PTR)) +#define astUnitAnalyser(in,powers) astUnitAnalyser_(in,powers,STATUS_PTR) +#define astUnitNormaliser(in) astUnitNormaliser_(in,STATUS_PTR) +#define astUnitLabel(sym) astINVOKE(O,astUnitLabel_(sym,STATUS_PTR)) +#endif +#endif + + + diff --git a/unitmap.c b/unitmap.c new file mode 100644 index 0000000..a371031 --- /dev/null +++ b/unitmap.c @@ -0,0 +1,1425 @@ +/* +*class++ +* Name: +* UnitMap + +* Purpose: +* Unit (null) Mapping. + +* Constructor Function: +c astUnitMap +f AST_UNITMAP + +* Description: +* A UnitMap is a unit (null) Mapping that has no effect on the +* coordinates supplied to it. They are simply copied. This can be +* useful if a Mapping is required (e.g. to pass to another +c function) but you do not want it to have any effect. +f routine) but you do not want it to have any effect. +* The Nin and Nout attributes of a UnitMap are always equal and +* are specified when it is created. + +* Inheritance: +* The UnitMap class inherits from the Mapping class. + +* Attributes: +* The UnitMap class does not define any new attributes beyond +* those which are applicable to all Mappings. + +* Functions: +c The UnitMap class does not define any new functions beyond those +f The UnitMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S Berry (JAC, Hawaii) + +* History: +* 7-FEB-1996 (RFWS): +* Original version. +* 13-DEC-1996 (RFWS): +* Over-ride the astMapMerge method. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitUnitMapVtab +* method. +* 10-MAY-2006 (DSB): +* Override astEqual. +* 17-FEB-2012 (DSB): +* In Transform, do not copy the coordinate values if the input and +* output array are the same. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS UnitMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "object.h" /* Base Object class */ +#include "memory.h" /* AST memory management */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "unitmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(UnitMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(UnitMap,Class_Init) +#define class_vtab astGLOBAL(UnitMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstUnitMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstUnitMap *astUnitMapId_( int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static void Dump( AstObject *, AstChannel *, int * ); + +/* Member functions. */ +/* ================= */ +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two UnitMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "unitmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* UnitMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two UnitMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a UnitMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the UnitMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstUnitMap *that; + AstUnitMap *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two UnitMap structures. */ + this = (AstUnitMap *) this_object; + that = (AstUnitMap *) that_object; + +/* Check the second object is a UnitMap. We know the first is a + UnitMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAUnitMap( that ) ) { + +/* Get the number of inputs check they are the same for both. */ + result = ( astGetNin( this ) == astGetNin( that ) ); + + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a UnitMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* UnitMap member function (over-rides the protected astGetIsLinear +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsLinear attribute for a +* Frame, which is always one. + +* Parameters: +* this +* Pointer to the UnitMap. +* status +* Pointer to the inherited status variable. +*/ + return 1; +} + +void astInitUnitMapVtab_( AstUnitMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitUnitMapVtab + +* Purpose: +* Initialise a virtual function table for a UnitMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "unitmap.h" +* void astInitUnitMapVtab( AstUnitMapVtab *vtab, const char *name ) + +* Class Membership: +* UnitMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the UnitMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAUnitMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that + provide virtual methods for this class. */ + +/* None. */ + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + mapping->MapSplit = MapSplit; + mapping->Rate = Rate; + mapping->GetIsLinear = GetIsLinear; + +/* Declare the class dump function. There is no copy constructor or + destructor. */ + astSetDump( vtab, Dump, "UnitMap", "Unit (null) Mapping" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a UnitMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* UnitMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated UnitMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated UnitMap with one which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated UnitMap which is to be merged with +* its neighbours. This should be a cloned copy of the UnitMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* UnitMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated UnitMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *new; /* Pointer to replacement UnitMap */ + const char *class; /* Pointer to Mapping class string */ + int i1; /* Index of first UnitMap to merge */ + int i2; /* Index of last UnitMap to merge */ + int i; /* Loop counter for Mappings */ + int ngone; /* Number of UnitMaps eliminated */ + int nin; /* Number of coordinates for UnitMap */ + int result; /* Result value to return */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* If the Mapping sequence consists of a single UnitMap, simply check + if the asociated Invert flag is set, and clear it if necessary. */ + if ( *nmap == 1 ) { + if ( ( *invert_list )[ 0 ] ) { + ( *invert_list )[ 0 ] = 0; + +/* Note if the Mapping sequence was modified. */ + result = 0; + } + +/* In series. */ +/* ========== */ +/* If a UnitMap occurs in series with any other Mapping, it can simply + be eliminated, so annul its Mapping pointer. */ + } else if ( *nmap > 1 ) { + if ( series ) { + ( *map_list )[ where ] = astAnnul( ( *map_list )[ where ] ); + +/* Loop to move any following pointers and their Invert flags down in + the array to fill the gap, and clear the vacated elements at the + end of the arrays. */ + for ( i = where + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = where; + +/* In parallel. */ +/* ============ */ +/* If a UnitMap occurs in parallel with other Mappings, it can be + merged with any UnitMaps on either side of itself. */ + } else { + +/* Initialise indices to identify any adjacent UnitMaps and the number + of coordinates to be handled by the replacement. */ + i1 = i2 = where; + nin = astGetNin( ( *map_list )[ where ] ); + +/* Loop to inspect earlier Mappings, obtaining the Mapping Class name + and checking it is "UnitMap". Quit looping as soon as any other + class of Mapping is found. */ + while ( i1 - 1 >= 0 ) { + class = astGetClass( ( *map_list )[ i1 - 1 ] ); + if ( !astOK || strcmp( class, "UnitMap" ) ) break; + +/* Update the index of the earliest UnitMap that is to be merged and + increment the total number of coordinates involved. */ + i1--; + nin += astGetNin( ( *map_list )[ i1 ] ); + } + +/* Repeat the above process to inspect any later Mappings in the + sequence. */ + while ( i2 + 1 < *nmap ) { + class = astGetClass( ( *map_list )[ i2 + 1 ] ); + if ( !astOK || strcmp( class, "UnitMap" ) ) break; + i2++; + nin += astGetNin( ( *map_list )[ i2 ] ); + } + +/* Calculate the net number of Mappings that can be removed from the + sequence and check if this is zero, meaning that there were no + adjacent UnitMaps to merge with. */ + if ( astOK ) { + ngone = i2 - i1; + if ( !ngone ) { + +/* If so, simply check if the nominated UnitMap's Invert flag is set, + and clear it if necessary. */ + if ( ( *invert_list )[ where ] ) { + ( *invert_list )[ where ] = 0; + +/* Note if the Mapping sequence was modified. */ + result = where; + } + +/* If UnitMaps can be merged, create the replacement UnitMap. */ + } else { + new = (AstMapping *) astUnitMap( nin, "", status ); + if ( astOK ) { + +/* Annul the pointers to all the UnitMaps that are being replaced. */ + for ( i = i1; i <= i2; i++ ) { + ( *map_list )[ i ] = astAnnul( ( *map_list )[ i ] ); + } + +/* Insert the new pointer and the associated Invert flag. */ + ( *map_list )[ i1 ] = new; + ( *invert_list )[ i1 ] = 0; + +/* Loop to close the resulting gap by moving subsequent elements down + in the arrays. */ + for ( i = i2 + 1; i < *nmap; i++ ) { + ( *map_list )[ i - ngone ] = ( *map_list )[ i ]; + ( *invert_list )[ i - ngone ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated elements at the end. */ + for ( i = *nmap - ngone; i < *nmap; i++ ) { + ( *map_list )[ i ] = NULL; + ( *invert_list )[ i ] = 0; + } + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap ) -= ngone; + result = i1; + } + } + } + } + } + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* UnitMap. + +* Type: +* Private function. + +* Synopsis: +* #include "unitmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* UnitMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing UnitMap. This is only possible if the specified inputs +* correspond to some subset of the UnitMap outputs. That is, there +* must exist a subset of the UnitMap outputs for which each output +* depends only on the selected UnitMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied UnitMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the UnitMap to be split (the UnitMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied UnitMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied UnitMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied UnitMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstUnitMap *this; /* Pointer to UnitMap structure */ + int *result; /* Pointer to returned array */ + int i; /* Loop count */ + int iin; /* Mapping input index */ + int mnin; /* No. of Mapping inputs */ + int ok; /* Are input indices OK? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the UnitMap structure. */ + this = (AstUnitMap *) this_map; + +/* Allocate memory for the returned array and create a UnitMap with the + required number of axes. */ + result = astMalloc( sizeof( int )*(size_t) nin ); + *map = (AstMapping *) astUnitMap( nin, "", status ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Store the required output axis indices. At the same time check that each + axis is valid. */ + mnin = astGetNin( this ); + ok = 1; + for( i = 0; i < nin; i++ ) { + iin = in[ i ]; + if( iin >= 0 && iin < mnin ) { + result[ i ] = iin; + } else { + ok = 0; + break; + } + } + +/* If the "in" array contained any invalid values, free the returned + resources. */ + if( !ok ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "unitmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* UnitMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + + return ( ax1 == ax2 ) ? 1.0 : 0.0; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a UnitMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "unitmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* UnitMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a UnitMap and a set of points encapsulated in a +* PointSet and transforms the points so as to perform the identity +* transformation (i.e. simply copies the coordinate values). + +* Parameters: +* this +* Pointer to the UnitMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. In this case, both transformations are equivalent. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the UnitMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstUnitMap *map; /* Pointer to UnitMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + int coord; /* Loop counter for coordinates */ + int ncoord_in; /* Number of coordinates per input point */ + int npoint; /* Number of points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the UnitMap. */ + map = (AstUnitMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + coordinate copy needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + ncoord_in = astGetNcoord( in ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* We need not determine whether to apply the forward or inverse + transformation, as they are both the same. */ + +/* Copy the coordinate values. */ +/* --------------------------- */ + if ( astOK ) { + +/* Loop to copy the values for each coordinate. Use a memory copy for speed. + Do not do the copy if the input and output arrays are the same. */ + for ( coord = 0; coord < ncoord_in; coord++ ) { + if( ptr_out[ coord ] != ptr_in[ coord ] ) { + (void) memcpy( (void *) ptr_out[ coord ], + (const void *) ptr_in[ coord ], + sizeof( double ) * (size_t) npoint ); + } + } + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +/* Copy constructor. */ +/* ----------------- */ +/* No copy constructor is needed, as a byte-by-byte copy suffices. */ + +/* Destructor. */ +/* ----------- */ +/* No destructor is needed as no memory, etc. needs freeing. */ + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for UnitMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the UnitMap class to an output Channel. + +* Parameters: +* this +* Pointer to the UnitMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstUnitMap *this; /* Pointer to the UnitMap structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the UnitMap structure. */ + this = (AstUnitMap *) this_object; + +/* Write out values representing the instance variables for the + UnitMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* There are no values to write, so return without further action. */ +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAUnitMap and astCheckUnitMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(UnitMap,Mapping) +astMAKE_CHECK(UnitMap) + +AstUnitMap *astUnitMap_( int ncoord, const char *options, int *status, ...) { +/* +*++ +* Name: +c astUnitMap +f AST_UNITMAP + +* Purpose: +* Create a UnitMap. + +* Type: +* Public function. + +* Synopsis: +c #include "unitmap.h" +c AstUnitMap *astUnitMap( int ncoord, const char *options, ... ) +f RESULT = AST_UNITMAP( NCOORD, OPTIONS, STATUS ) + +* Class Membership: +* UnitMap constructor. + +* Description: +* This function creates a new UnitMap and optionally initialises +* its attributes. +* +* A UnitMap is a unit (null) Mapping that has no effect on the +* coordinates supplied to it. They are simply copied. This can be +* useful if a Mapping is required (e.g. to pass to another +c function) but you do not want it to have any effect. +f routine) but you do not want it to have any effect. + +* Parameters: +c ncoord +f NCOORD = INTEGER (Given) +* The number of input and output coordinates (these numbers are +* necessarily the same). +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new UnitMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new UnitMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astUnitMap() +f AST_UNITMAP = INTEGER +* A pointer to the new UnitMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstUnitMap *new; /* Pointer to new UnitMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the UnitMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitUnitMap( NULL, sizeof( AstUnitMap ), !class_init, &class_vtab, + "UnitMap", ncoord ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + UnitMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new UnitMap. */ + return new; +} + +AstUnitMap *astUnitMapId_( int ncoord, const char *options, ... ) { +/* +* Name: +* astUnitMapId_ + +* Purpose: +* Create a UnitMap. + +* Type: +* Private function. + +* Synopsis: +* #include "unitmap.h" +* AstUnitMap *astUnitMapId_( int ncoord, const char *options, ... ) + +* Class Membership: +* UnitMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astUnitMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astUnitMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astUnitMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astUnitMap_. + +* Returned Value: +* The ID value associated with the new UnitMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstUnitMap *new; /* Pointer to new UnitMap */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the UnitMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitUnitMap( NULL, sizeof( AstUnitMap ), !class_init, &class_vtab, + "UnitMap", ncoord ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the + options string to the astVSet method to initialise the new + UnitMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new UnitMap. */ + return astMakeId( new ); +} + +AstUnitMap *astInitUnitMap_( void *mem, size_t size, int init, + AstUnitMapVtab *vtab, const char *name, + int ncoord, int *status ) { +/* +*+ +* Name: +* astInitUnitMap + +* Purpose: +* Initialise a UnitMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "unitmap.h" +* AstUnitMap *astInitUnitMap( void *mem, size_t size, int init, +* AstUnitMapVtab *vtab, const char *name, +* int ncoord ) + +* Class Membership: +* UnitMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new UnitMap object. It allocates memory (if necessary) to accommodate +* the UnitMap plus any additional data associated with the derived class. +* It then initialises a UnitMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a UnitMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the UnitMap is to be initialised. +* This must be of sufficient size to accommodate the UnitMap data +* (sizeof(UnitMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the UnitMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the UnitMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the UnitMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new UnitMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). +* ncoord +* The number of coordinate values per point. + +* Returned Value: +* A pointer to the new UnitMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstUnitMap *new; /* Pointer to new UnitMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitUnitMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Mapping structure (the parent class) as the first component + within the UnitMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstUnitMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + ncoord, ncoord, 1, 1 ); + + if ( astOK ) { + +/* Initialise the UnitMap data. */ +/* ---------------------------- */ +/* There is nothing else to store. */ + +/* If an error occurred, clean up by deleting the new object (if any other + resources had been allocated, we would free these first). */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new object. */ + return new; +} + +AstUnitMap *astLoadUnitMap_( void *mem, size_t size, + AstUnitMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadUnitMap + +* Purpose: +* Load a UnitMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "unitmap.h" +* AstUnitMap *astLoadUnitMap( void *mem, size_t size, +* AstUnitMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* UnitMap loader. + +* Description: +* This function is provided to load a new UnitMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* UnitMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a UnitMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the UnitMap is to be +* loaded. This must be of sufficient size to accommodate the +* UnitMap data (sizeof(UnitMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the UnitMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the UnitMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstUnitMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new UnitMap. If this is NULL, a pointer +* to the (static) virtual function table for the UnitMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "UnitMap" is used instead. + +* Returned Value: +* A pointer to the new UnitMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstUnitMap *new; /* Pointer to the new UnitMap */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this UnitMap. In this case the + UnitMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstUnitMap ); + vtab = &class_vtab; + name = "UnitMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitUnitMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built UnitMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "UnitMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. */ + +/* There are no values to read. */ +/* ---------------------------- */ + +/* If an error occurred, clean up by deleting the new UnitMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new UnitMap pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +/* There are none of these. */ + + + + diff --git a/unitmap.h b/unitmap.h new file mode 100644 index 0000000..c808ed1 --- /dev/null +++ b/unitmap.h @@ -0,0 +1,288 @@ +#if !defined( UNITMAP_INCLUDED ) /* Include this file only once */ +#define UNITMAP_INCLUDED +/* +*+ +* Name: +* unitmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the UnitMap class. + +* Invocation: +* #include "unitmap.h" + +* Description: +* This include file defines the interface to the UnitMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The UnitMap class implements Mappings that perform an identity +* (unit) coordinate transformation, simply by copying each +* coordinate value from input to output (and similarly for the +* inverse transformation). UnitMaps are therefore of little use +* for converting coordinates, but they serve a useful role as +* "null" Mappings in cases where a Mapping is needed (e.g. to +* pass to a function), but no coordinate transformation is wanted. + +* Inheritance: +* The UnitMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astMapMerge +* Simplify a sequence of Mappings containing a UnitMap. +* astTransform +* Transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* None. + +* Other Class Functions: +* Public: +* astIsAUnitMap +* Test class membership. +* astUnitMap +* Create a UnitMap. +* +* Protected: +* astCheckUnitMap +* Validate class membership. +* astInitUnitMap +* Initialise a UnitMap. +* astInitUnitMapVtab +* Initialise the virtual function table for the UnitMap class. +* astLoadUnitMap +* Load a UnitMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstUnitMap +* UnitMap object type. +* +* Protected: +* AstUnitMapVtab +* UnitMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 7-FEB-1996 (RFWS): +* Original version. +* 13-DEC-1996 (RFWS): +* Over-ride the astMapMerge method. +* 8-JAN-2003 (DSB): +* Added protected astInitUnitMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* UnitMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each + object in the class (e.g. its instance variables). */ +typedef struct AstUnitMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ +/* None. */ +} AstUnitMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstUnitMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ +/* None. */ +} AstUnitMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstUnitMapGlobals { + AstUnitMapVtab Class_Vtab; + int Class_Init; +} AstUnitMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitUnitMapGlobals_( AstUnitMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(UnitMap) /* Check class membership */ +astPROTO_ISA(UnitMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstUnitMap *astUnitMap_( int, const char *, int *, ...); +#else +AstUnitMap *astUnitMapId_( int, const char *, ... )__attribute__((format(printf,2,3))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstUnitMap *astInitUnitMap_( void *, size_t, int, AstUnitMapVtab *, + const char *, int, int * ); + +/* Vtab initialiser. */ +void astInitUnitMapVtab_( AstUnitMapVtab *, const char *, int * ); + +/* Loader. */ +AstUnitMap *astLoadUnitMap_( void *, size_t, AstUnitMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +/* None. */ + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckUnitMap(this) astINVOKE_CHECK(UnitMap,this,0) +#define astVerifyUnitMap(this) astINVOKE_CHECK(UnitMap,this,1) + +/* Test class membership. */ +#define astIsAUnitMap(this) astINVOKE_ISA(UnitMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astUnitMap astINVOKE(F,astUnitMap_) +#else +#define astUnitMap astINVOKE(F,astUnitMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitUnitMap(mem,size,init,vtab,name,ncoord) \ +astINVOKE(O,astInitUnitMap_(mem,size,init,vtab,name,ncoord,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitUnitMapVtab(vtab,name) astINVOKE(V,astInitUnitMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadUnitMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadUnitMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckUnitMap to validate UnitMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ +/* None. */ +#endif + + + + + diff --git a/version.h.in b/version.h.in new file mode 100644 index 0000000..49d6643 --- /dev/null +++ b/version.h.in @@ -0,0 +1,73 @@ +#if !defined( VERSION_INCLUDED ) +#define VERSION_INCLUDED 1 +/* +*+ +* Name: +* version.h + +* Purpose: +* Declare version numbers + +* Description: +* Defines macros which expand to the components of the AST version +* number, namely the major and minor version numbers, and the +* release number. The version number as a string is available by +* including the file config.h, which defines macros PACKAGE_STRING, +* PACKAGE_VERSION and (equivalently to the latter) VERSION. +* +* For example, the version string `3.2.1' corresponds to major version +* 3, minor version 2, release 1. + +* Macros defined: +* AST__VMAJOR +* The AST major version number +* AST__VMINOR +* The AST minor version number +* AST__RELEASE +* The AST release number +* +* For backwards compatibility, this module also declares macros +* AST_MAJOR_VERS, AST_MINOR_VERS and AST_RELEASE. The AST__* +* macros should be used in preference to these, since the latter +* use (non-standard) single underscores. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* NG: Norman Gray (Starlink) + +* History: +* 25-NOV-2003 (NG): +* Original version +*- +*/ + +/* The current version of AST is @PACKAGE_VERSION@ */ +#define AST__VMAJOR @PACKAGE_VERSION_MAJOR@ +#define AST__VMINOR @PACKAGE_VERSION_MINOR@ +#define AST__RELEASE @PACKAGE_VERSION_RELEASE@ + +/* Deprecated macros */ +#define AST_MAJOR_VERS @PACKAGE_VERSION_MAJOR@ +#define AST_MINOR_VERS @PACKAGE_VERSION_MINOR@ +#define AST_RELEASE @PACKAGE_VERSION_RELEASE@ + +#endif /* #if ! defined(VERSION_INCLUDED) */ diff --git a/wcsmap.c b/wcsmap.c new file mode 100644 index 0000000..3f90d4d --- /dev/null +++ b/wcsmap.c @@ -0,0 +1,6063 @@ +/* +*class++ +* Name: +* WcsMap + +* Purpose: +* Implement a FITS-WCS sky projection. + +* Constructor Function: +c astWcsMap +f AST_WCSMAP + +* Description: +* This class is used to represent sky coordinate projections as +* described in the FITS world coordinate system (FITS-WCS) paper II +* "Representations of Celestial Coordinates in FITS" by M. Calabretta +* and E.W. Griesen. This paper defines a set of functions, or sky +* projections, which transform longitude-latitude pairs representing +* spherical celestial coordinates into corresponding pairs of Cartesian +* coordinates (and vice versa). +* +* A WcsMap is a specialised form of Mapping which implements these +* sky projections and applies them to a specified pair of coordinates. +* All the projections in the FITS-WCS paper are supported, plus the now +* deprecated "TAN with polynomial correction terms" projection which +* is refered to here by the code "TPN". Using the FITS-WCS terminology, +* the transformation is between "native spherical" and "projection +* plane" coordinates (also called "intermediate world coordinates". +* These coordinates may, optionally, be embedded in a space with more +* than two dimensions, the remaining coordinates being copied unchanged. +* Note, however, that for consistency with other AST facilities, a +* WcsMap handles coordinates that represent angles in radians (rather +* than the degrees used by FITS-WCS). +* +* The type of FITS-WCS projection to be used and the coordinates +* (axes) to which it applies are specified when a WcsMap is first +* created. The projection type may subsequently be determined +* using the WcsType attribute and the coordinates on which it acts +* may be determined using the WcsAxis(lonlat) attribute. +* +* Each WcsMap also allows up to 100 "projection parameters" to be +* associated with each axis. These specify the precise form of the +* projection, and are accessed using PVi_m attribute, where "i" is +* the integer axis index (starting at 1), and m is an integer +* "parameter index" in the range 0 to 99. The number of projection +* parameters required by each projection, and their meanings, are +* dependent upon the projection type (most projections either do not +* use any projection parameters, or use parameters 1 and 2 associated +* with the latitude axis). Before creating a WcsMap you should consult +* the FITS-WCS paper for details of which projection parameters are +* required, and which have defaults. When creating the WcsMap, you must +* explicitly set values for all those required projection parameters +* which do not have defaults defined in this paper. + +* Inheritance: +* The WcsMap class inherits from the Mapping class. + +* Attributes: +* In addition to those attributes common to all Mappings, every +* WcsMap also has the following attributes: +* +* - NatLat: Native latitude of the reference point of a FITS-WCS projection +* - NatLon: Native longitude of the reference point of a FITS-WCS projection +* - PVi_m: FITS-WCS projection parameters +* - PVMax: Maximum number of FITS-WCS projection parameters +* - WcsAxis(lonlat): FITS-WCS projection axes +* - WcsType: FITS-WCS projection type + +* Functions: +c The WcsMap class does not define any new functions beyond those +f The WcsMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 15-FEB-1996 (DSB): +* Original version. +* 23-MAR-1996 (DSB): +* Added support for PointSets with more than 2 axes. +* 14-NOV-1996 (DSB): +* Added I/O facilities, external interface, attributes, etc. +* 16-JAN-1997 (DSB): +* Allowed WCSBAD as a projection type in astWcsMap. +* 24-APR-1997 (RFWS): +* Tidied prologues. +* 26-SEP-1997 (DSB): +* o Long descriptions of projections changed to include a textual +* description as well as the three letter acronym. +* o Added protected function astPrjDesc. +* o String values now used instead of integers to represent +* "choice" attributes externally (eg WcsType). +* 8-APR-1998 (DSB): +* Modified MapMerge so that a WcsMap can merge with its own +* inverse when astSimplify is used. +* 20-APR-1998 (DSB): +* Modified MapMerge to avoid the possibility of returning an +* empty mappings list. +* 4-SEP-1998 (DSB): +* Changed MapMerge to allow WcsMaps to swap with PermMaps in +* order to bring mergable WcsMaps closer together. +* 5-MAY-1999 (DSB): +* More corrections to MapMerge: Cleared up errors in the use of the +* supplied invert flags, and corrected logic for deciding which +* neighbouring Mapping to swap with. +* 12-JUL-1999 (DSB): +* - Report an error if too many or two few projection parameters are +* supplied in a WcsMap. +* - Corrected MapMerge to prevent unset projection parameters +* being copied to any new WcsMaps. +* - Correct handling of invert flags in WcsPerm. +* 16-JUL-1999 (DSB): +* Fixed memory leak in MapMerge. +* 11-FEB-2000 (DSB): +* Added PVj_m attributes. Attributes ProjP(0) to ProjP(9) are now +* aliases for PV(axlat)_0 to PV(axlat)_9. Renamed GLS projection +* as SFL (GLS retained as alias for SFL). +* 10-AUG-2000 (DSB): +* MapMerge no longer simplifies a CAR projection. Previously they +* were replaced by a UnitMap, but this removed the cylic nature of +* the mapping (i.e. 2.PI == 0 ). +* 6-OCT-2000 (DSB): +* Ignore leading and trailing spaces in astWCsPrjType (some +* CTYPE FITS keywords have appeared with trailing white space). +* 26-SEP-2001 (DSB): +* Changed names of all functions and structure to avoid name clashes +* with wcslib. +* 10-OCT-2002 (DSB): +* Added astIsZenithal. +* 22-OCT-2002 (DSB): +* - GetPV now returns the FITS default value instead of AST__BAD +* if a defaultable latitude projection parameter has not been set. +* - A number of changes needed to support WcsLib v2.9. +* - Added AST__TPN projection. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitWcsMapVtab +* method. +* 4-JUN-2003 (DSB): +* - Added attribute "NatLon". +* - Changed to allow a user-specified fiducial point to be stored +* in projection parameter PVi_1 and PVi_2 for the longitude axis. +* - Changed "PVj_m" to "PVi_m" for consistency with FITS-WCS paper II. +* 18-AUG-2003 (DSB): +* In function Map, assign zero longitude to output positions which +* are very close to a pole. +* 23-SEP-2003 (DSB): +* - Changed so that the NatLat and NatLon attributes refer to the +* fixed values for the projections defined in FITS-WCS paper II, rather +* than the user-defined values stored in projection parameter PVi_1 and +* PVi_2 for the longitude axis. +* 11-FEB-2004 (DSB): +* Corrected axis numbering when reporting missing keywords in +* Transform. +* 23-APR-2004 (DSB): +* Changes to simplification algorithm. +* 1-SEP-2004 (DSB): +* CopyPV rewritten to avoid assumption that the input and output +* WcsMaps have the same number of axes and that the lon/lat axes have +* the same indices. +* 7-DEC-2005 (DSB): +* Free memory allocated by calls to astReadString. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 15-FEB-2006 (DSB): +* Use dynamic rather than static memory for the parameter arrays in +* the AstPrjPrm structure.Override astGetObjSize. This is to +* reduce the in-memory size of a WcsMap. +* 10-MAY-2006 (DSB): +* Override astEqual. +* 10-AUG-2006 (DSB): +* Correct astLoadWcsMap to take acount of the different number of +* PVi_m values that can be associated with each axis. +* 4-JAN-2007 (DSB): +* Correct astLoadWcsMap to load the projection parameter with +* highest index correctly. +* 23-FEB-2007 (DSB): +* Added HPX projection. +* 1-MAR-2011 (DSB): +* In function Map, do not allow valid longitude range to include both +* the high limit and the low limt (since they are both the same point on +* the sky). +* 7-MAR-2011 (DSB): +* In function Map, only do the longitude check if the projection +* is not cyclic. +* 24-MAY-2011 (DSB): +* Added protected FITSProj and TPNTan attributes (they should be +* removed when the PolyMap class has an iterative inverse). +* 6-MAR-2014 (DSB): +* Revert the change made on 18-AUG-2003 since setting the +* longitude arbitrarily to zero for points close to the pole +* causes significant round trip errors when doing pixel->sky->pixel +* transformation for points very close to the pole, if the pixel +* size is very small. The longitude at the pole is indeterminate, +* but whatever random numerical value is returned by atan2 is +* no less useful (and no more useful) than a fixed value of zero. +* 12-JUN-2014 (DSB): +* Added XPH projection. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS WcsMap + +/* Macros which return the maximum and minimum of two values. */ +#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb)) +#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb)) + +/* Macros to check for equality of floating point values. We cannot + compare bad values directory because of the danger of floating point + exceptions, so bad values are dealt with explicitly. */ +#define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN)))) + +/* +* +* Name: +* MAKE_CLEAR + +* Purpose: +* Implement a method to clear a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "wcsmap.h" +* MAKE_CLEAR(attr,component,assign,nval) + +* Class Membership: +* Defined by the WcsMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Clear( AstWcsMap *this, int axis ) +* +* and an external interface function of the form: +* +* void astClear_( AstWcsMap *this, int axis ) +* +* which implement a method for clearing a single value in a specified +* multi-valued attribute for an axis of a WcsMap. + +* Parameters: +* attr +* The name of the attribute to be cleared, as it appears in the function +* name (e.g. Label in "astClearLabelAt"). +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to assign to the component +* to clear its value. The variable "axis" can be used to refer to +* the zero-based index of the attribute component being cleared. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attr,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Clear##attr( AstWcsMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astClear" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the "clear" value. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astClear##attr##_( AstWcsMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,WcsMap,Clear##attr))( this, axis, status ); \ +} + + +/* +* +* Name: +* MAKE_GET + +* Purpose: +* Implement a method to get a single value in a multi-valued attribute. + +* Type: +* Private macro. + +* Synopsis: +* #include "wcsmap.h" +* MAKE_GET(attr,type,bad_value,assign,nval) + +* Class Membership: +* Defined by the WcsMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static Get( AstWcsMap *this, int axis ) +* +* and an external interface function of the form: +* +* astGet_( AstWcsMap *this, int axis ) +* +* which implement a method for getting a single value from a specified +* multi-valued attribute for an axis of a WcsMap. + +* Parameters: +* attr +* The name of the attribute whose value is to be obtained, as it +* appears in the function name (e.g. Label in "astGetLabel"). +* type +* The C type of the attribute. +* bad_value +* A constant value to return if the global error status is set, or if +* the function fails. +* assign +* An expression that evaluates to the value to be returned. This can +* use the string "axis" to represent the zero-based value index. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +* +*/ + +/* Define the macro. */ +#define MAKE_GET(attr,type,bad_value,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static type Get##attr( AstWcsMap *this, int axis, int *status ) { \ + type result; /* Result to be returned */ \ +\ +/* Initialise */ \ + result = (bad_value); \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astGet" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = (bad_value); \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +type astGet##attr##_( AstWcsMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return (bad_value); \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,WcsMap,Get##attr))( this, axis, status ); \ +} + +/* +* +* Name: +* MAKE_SET + +* Purpose: +* Implement a method to set a single value in a multi-valued attribute +* for a WcsMap. + +* Type: +* Private macro. + +* Synopsis: +* #include "wcsmap.h" +* MAKE_SET(attr,type,component,assign,nval) + +* Class Membership: +* Defined by the WcsMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static void Set( AstWcsMap *this, int axis, value ) +* +* and an external interface function of the form: +* +* void astSet_( AstWcsMap *this, int axis, value ) +* +* which implement a method for setting a single value in a specified +* multi-valued attribute for a WcsMap. + +* Parameters: +* attr +* The name of the attribute to be set, as it appears in the function +* name (e.g. Label in "astSetLabelAt"). +* type +* The C type of the attribute. +* component +* The name of the class structure component that holds the attribute +* value. +* assign +* An expression that evaluates to the value to be assigned to the +* component. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_SET(attr,type,component,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static void Set##attr( AstWcsMap *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astSet" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Store the new value in the structure component. */ \ + } else { \ + this->component[ axis ] = (assign); \ + } \ +} \ +\ +/* External interface. */ \ +/* ------------------- */ \ +void astSet##attr##_( AstWcsMap *this, int axis, type value, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Invoke the required method via the virtual function table. */ \ + (**astMEMBER(this,WcsMap,Set##attr))( this, axis, value, status ); \ +} + +/* +* +* Name: +* MAKE_TEST + +* Purpose: +* Implement a method to test if a single value has been set in a +* multi-valued attribute for a class. + +* Type: +* Private macro. + +* Synopsis: +* #include "wcsmap.h" +* MAKE_TEST(attr,assign,nval) + +* Class Membership: +* Defined by the WcsMap class. + +* Description: +* This macro expands to an implementation of a private member function of +* the form: +* +* static int Test( AstWcsMap *this, int axis ) +* +* and an external interface function of the form: +* +* int astTest_( AstWcsMap *this, int axis ) +* +* which implement a method for testing if a single value in a specified +* multi-valued attribute has been set for a class. + +* Parameters: +* attr +* The name of the attribute to be tested, as it appears in the function +* name (e.g. Label in "astTestLabelAt"). +* assign +* An expression that evaluates to 0 or 1, to be used as the returned +* value. This can use the string "axis" to represent the zero-based +* index of the value within the attribute. +* nval +* Specifies the number of values in the multi-valued attribute. The +* "axis" values supplied to the created function should be in the +* range zero to (nval - 1). + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*- +*/ + +/* Define the macro. */ +#define MAKE_TEST(attr,assign,nval) \ +\ +/* Private member function. */ \ +/* ------------------------ */ \ +static int Test##attr( AstWcsMap *this, int axis, int *status ) { \ + int result; /* Value to return */ \ +\ +/* Initialise */ \ + result = 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +\ +/* Validate the axis index. */ \ + if( axis < 0 || axis >= nval ){ \ + astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \ + #attr " - it should be in the range 1 to %d.", status, \ + "astTest" #attr, astGetClass( this ), \ + axis + 1, nval ); \ +\ +/* Assign the result value. */ \ + } else { \ + result = (assign); \ + } \ +\ +/* Check for errors and clear the result if necessary. */ \ + if ( !astOK ) result = 0; \ +\ +/* Return the result. */ \ + return result; \ +} \ +/* External interface. */ \ +/* ------------------- */ \ +int astTest##attr##_( AstWcsMap *this, int axis, int *status ) { \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return 0; \ +\ +/* Invoke the required method via the virtual function table. */ \ + return (**astMEMBER(this,WcsMap,Test##attr))( this, axis, status ); \ +} + + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "globals.h" /* Thread-safe global data access */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "unitmap.h" /* Unit mappings */ +#include "permmap.h" /* Axis permutation mappings */ +#include "wcsmap.h" /* Interface definition for this class */ +#include "pal.h" /* SLALIB function prototypes */ +#include "channel.h" /* I/O channels */ +#include "proj.h" /* WCSLIB projections and WCSLIB_MXPAR */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Local Type Definitions. */ +/* ----------------------- */ +/* This structure is used to hold information describing a WCSLIB + projection. */ +typedef struct PrjData { + int prj; /* WCSLIB projection identifier value */ + int mxpar; /* Max index for a lat axis projection param */ + int mxpar2; /* Max index for a lon axis projection param */ + char desc[60]; /* Long projection description */ + char ctype[5]; /* FITS CTYPE identifying string */ + int (* WcsFwd)(double, double, struct AstPrjPrm *, double *, double *); + /* Pointer to forward projection function */ + int (* WcsRev)(double, double, struct AstPrjPrm *, double *, double *); + /* Pointer to reverse projection function */ + double theta0; /* Default native latitude of fiducial point */ +} PrjData; + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * ); + +/* The following array of PrjData structured describes each of the WCSLIB + projections. The last entry in the list should be for the AST__WCSBAD + projection. This marks the end of the list. */ +static PrjData PrjInfo[] = { + { AST__AZP, 2, 4, "zenithal perspective", "-AZP", astAZPfwd, astAZPrev, AST__DPIBY2 }, + { AST__SZP, 3, 4, "slant zenithal perspective", "-SZP", astSZPfwd, astSZPrev, AST__DPIBY2 }, + { AST__TAN, 0, 4, "gnomonic", "-TAN", astTANfwd, astTANrev, AST__DPIBY2 }, + { AST__STG, 0, 4, "stereographic", "-STG", astSTGfwd, astSTGrev, AST__DPIBY2 }, + { AST__SIN, 2, 4, "orthographic", "-SIN", astSINfwd, astSINrev, AST__DPIBY2 }, + { AST__ARC, 0, 4, "zenithal equidistant", "-ARC", astARCfwd, astARCrev, AST__DPIBY2 }, + { AST__ZPN, WCSLIB_MXPAR, 4, "zenithal polynomial", "-ZPN", astZPNfwd, astZPNrev, AST__DPIBY2 }, + { AST__ZEA, 0, 4, "zenithal equal area", "-ZEA", astZEAfwd, astZEArev, AST__DPIBY2 }, + { AST__AIR, 1, 4, "Airy", "-AIR", astAIRfwd, astAIRrev, AST__DPIBY2 }, + { AST__CYP, 2, 4, "cylindrical perspective", "-CYP", astCYPfwd, astCYPrev, 0.0 }, + { AST__CEA, 1, 4, "cylindrical equal area", "-CEA", astCEAfwd, astCEArev, 0.0 }, + { AST__CAR, 0, 4, "Cartesian", "-CAR", astCARfwd, astCARrev, 0.0 }, + { AST__MER, 0, 4, "Mercator", "-MER", astMERfwd, astMERrev, 0.0 }, + { AST__SFL, 0, 4, "Sanson-Flamsteed", "-SFL", astSFLfwd, astSFLrev, 0.0 }, + { AST__PAR, 0, 4, "parabolic", "-PAR", astPARfwd, astPARrev, 0.0 }, + { AST__MOL, 0, 4, "Mollweide", "-MOL", astMOLfwd, astMOLrev, 0.0 }, + { AST__AIT, 0, 4, "Hammer-Aitoff", "-AIT", astAITfwd, astAITrev, 0.0 }, + { AST__COP, 2, 4, "conical perspective", "-COP", astCOPfwd, astCOPrev, AST__BAD }, + { AST__COE, 2, 4, "conical equal area", "-COE", astCOEfwd, astCOErev, AST__BAD }, + { AST__COD, 2, 4, "conical equidistant", "-COD", astCODfwd, astCODrev, AST__BAD }, + { AST__COO, 2, 4, "conical orthomorphic", "-COO", astCOOfwd, astCOOrev, AST__BAD }, + { AST__BON, 1, 4, "Bonne's equal area", "-BON", astBONfwd, astBONrev, 0.0 }, + { AST__PCO, 0, 4, "polyconic", "-PCO", astPCOfwd, astPCOrev, 0.0 }, + { AST__TSC, 0, 4, "tangential spherical cube", "-TSC", astTSCfwd, astTSCrev, 0.0 }, + { AST__CSC, 0, 4, "cobe quadrilateralized spherical cube", "-CSC", astCSCfwd, astCSCrev, 0.0 }, + { AST__QSC, 0, 4, "quadrilateralized spherical cube", "-QSC", astQSCfwd, astQSCrev, 0.0 }, + { AST__NCP, 2, 4, "AIPS north celestial pole", "-NCP", NULL, NULL, 0.0 }, + { AST__GLS, 0, 4, "sinusoidal", "-GLS", astSFLfwd, astSFLrev, 0.0 }, + { AST__HPX, 2, 4, "HEALPix", "-HPX", astHPXfwd, astHPXrev, 0.0 }, + { AST__XPH, 0, 4, "polar HEALPix", "-XPH", astXPHfwd, astXPHrev, AST__DPIBY2 }, + { AST__TPN, WCSLIB_MXPAR, WCSLIB_MXPAR, "gnomonic polynomial", "-TPN", astTPNfwd, astTPNrev, AST__DPIBY2 }, + { AST__WCSBAD, 0, 4, "", " ", NULL, NULL, 0.0 } }; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(WcsMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(WcsMap,Class_Init) +#define class_vtab astGLOBAL(WcsMap,Class_Vtab) +#define getattrib_buff astGLOBAL(WcsMap,GetAttrib_Buff) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +static char getattrib_buff[ 101 ]; + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstWcsMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstWcsMap *astWcsMapId_( int, int, int, int, const char *options, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static int GetObjSize( AstObject *, int * ); +static double GetPV( AstWcsMap *, int, int, int * ); +static int TestPV( AstWcsMap *, int, int, int * ); +static void ClearPV( AstWcsMap *, int, int, int * ); +static void SetPV( AstWcsMap *, int, int, double, int * ); + +static int GetPVMax( AstWcsMap *, int, int * ); +static int GetWcsType( AstWcsMap *, int * ); +static double GetNatLat( AstWcsMap *, int * ); +static double GetNatLon( AstWcsMap *, int * ); +static int GetWcsAxis( AstWcsMap *, int, int * ); + +static int GetFITSProj( AstWcsMap *, int * ); +static int TestFITSProj( AstWcsMap *, int * ); +static void ClearFITSProj( AstWcsMap *, int * ); +static void SetFITSProj( AstWcsMap *, int, int * ); + +static int GetTPNTan( AstWcsMap *, int * ); +static int TestTPNTan( AstWcsMap *, int * ); +static void ClearTPNTan( AstWcsMap *, int * ); +static void SetTPNTan( AstWcsMap *, int, int * ); + +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const PrjData *FindPrjData( int, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static int CanMerge( AstMapping *, int, AstMapping *, int, int * ); +static int CanSwap( AstMapping *, AstMapping *, int, int, int *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetNP( AstWcsMap *, int, int * ); +static int IsZenithal( AstWcsMap *, int * ); +static int LongRange( const PrjData *, struct AstPrjPrm *, double *, double *, int * ); +static int Map( AstWcsMap *, int, int, double *, double *, double *, double *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void CopyPV( AstWcsMap *, AstWcsMap *, int * ); +static void Delete( AstObject *obj, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void FreePV( AstWcsMap *, int * ); +static void InitPrjPrm( AstWcsMap *, int * ); +static void PermGet( AstPermMap *, int **, int **, double **, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void WcsPerm( AstMapping **, int *, int, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); + +/* Member functions. */ +/* ================= */ +static int CanMerge( AstMapping *map1, int inv1, AstMapping *map2, int inv2, int *status ){ +/* +* +* Name: +* CanMerge + +* Purpose: +* Checks if two WcsMaps can be merged. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int CanMerge( AstMapping *map1, int inv1, AstMapping *map2, int inv2, int *status ) + +* Class Membership: +* WcsMap internal utility function. + +* Description: +* This function checks the two supplied Mappings to see if they are +* two WcsMaps which can be merged. This is only possible if they +* form an inverse pair. + +* Parameters: +* map1 +* A pointer to the first mapping. +* map2 +* A pointer to the second mapping. +* inv1 +* The invert flag to use with the first mapping. +* inv2 +* The invert flag to use with the second mapping. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if the WcsMaps can be merged, zero otherwise. + +*/ + +/* Local Variables: */ + AstWcsMap *wcs1; /* Pointer to first WcsMap */ + AstWcsMap *wcs2; /* Pointer to second WcsMap */ + int m; /* Projection parameter index */ + int ret; /* Can the Mappings be merged? */ + int i; /* Axis index */ + +/* Initialise the returned value. */ + ret = 0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Both Mappings must be WcsMaps to merge. */ + if( !strcmp( "WcsMap", astGetClass( map1 ) ) && + !strcmp( "WcsMap", astGetClass( map2 ) ) ) { + +/* Get pointers to the WcsMaps. */ + wcs1 = (AstWcsMap *) map1; + wcs2 = (AstWcsMap *) map2; + +/* Check that the two WcsMaps performs the same sort of projection, and + have the same number of axes. */ + if( astGetWcsType( wcs1 ) == astGetWcsType( wcs2 ) && + astGetNin( wcs1 ) == astGetNin( wcs2 ) ) { + +/* Check that the Mappings are applied in opposite senses. */ + if( inv1 != inv2 ) { + +/* Check that the latitude and longitude axes have the same indices in + both WcsMaps. */ + if( astGetWcsAxis( wcs1, 0 ) == astGetWcsAxis( wcs2, 0 ) && + astGetWcsAxis( wcs1, 1 ) == astGetWcsAxis( wcs2, 1 ) ){ + +/* We nopw check the projection parameters are equal. Assume they are for + the moment. */ + ret = 1; + +/* Check the parameters for each axis in turn. */ + for( i = 0; i < astGetNin( wcs1 ); i++ ){ + +/* If the two WcsMaps have a different number of parameters for this axes, + they cannot merge. */ + if( GetNP( wcs1, i, status ) != GetNP( wcs1, i, status ) ){ + ret = 0; + break; + +/* Otherwise, check each parameter value in turn. If any are found which + are not equal, the WcsMaps cannot merge. */ + } else { + for( m = 0; m < GetNP( wcs1, i, status ); m++ ){ + if( !EQUAL( astGetPV( wcs1, i, m ), + astGetPV( wcs2, i, m ) ) ){ + ret = 0; + break; + } + } + if( !ret ) break; + } + } + } + } + } + } + +/* Return the answer. */ + return ret; +} + +static int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, + int *simpler, int *status ){ +/* +* Name: + +* CanSwap + +* Purpose: +* Determine if two Mappings could be swapped. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, +* int *simpler ) + +* Class Membership: +* WcsMap member function + +* Description: +* This function returns a flag indicating if the pair of supplied +* Mappings could be replaced by an equivalent pair of Mappings from the +* same classes as the supplied pair, but in reversed order. Each pair +* of Mappings is considered to be compunded in series. The supplied +* Mapings are not changed in any way. + +* Parameters: +* map1 +* The Mapping to be applied first. +* map2 +* The Mapping to be applied second. +* inv1 +* The invert flag to use with map1. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* inv2 +* The invert flag to use with map2. +* simpler +* Addresss of a location at which to return a flag indicating if +* the swapped Mappings would be intrinsically simpler than the +* original Mappings. + +* Returned Value: +* 1 if the Mappings could be swapped, 0 otherwise. + +* Notes: +* - One of the supplied pair of Mappings must be a WcsMap. +* - A value of 0 is returned if the two Mappings could be merged into +* a single Mapping. +* - A value of 0 is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *nowcs; /* Pointer to non-WcsMap Mapping */ + AstWcsMap *wcs; /* Pointer to WcsMap Mapping */ + const char *class1; /* Pointer to map1 class string */ + const char *class2; /* Pointer to map2 class string */ + const char *nowcs_class; /* Pointer to non-WcsMap class string */ + double *consts; /* Pointer to constants array */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int i; /* Loop count */ + int invert[ 2 ]; /* Original invert flags */ + int latax; /* Index of latitude axis in WcsMap */ + int lonax; /* Index of longitude axis in WcsMap */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + int ret; /* Returned flag */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise */ + ret = 0; + *simpler = 0; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + invert[ 0 ] = astGetInvert( map1 ); + astSetInvert( map1, inv1 ); + + invert[ 1 ] = astGetInvert( map2 ); + astSetInvert( map2, inv2 ); + +/* Get the classes of the two mappings. */ + class1 = astGetClass( map1 ); + class2 = astGetClass( map2 ); + if( astOK ){ + +/* Get a pointer to the non-WcsMap Mapping. */ + if( !strcmp( class1, "WcsMap" ) ){ + wcs = (AstWcsMap *) map1; + nowcs = map2; + nowcs_class = class2; + } else { + nowcs = map1; + wcs = (AstWcsMap *) map2; + nowcs_class = class1; + } + +/* If it is a PermMap, the Mappings can be swapped so long as: + 1) all links between input and output axes in the PermMap are + bi-directional. This does not preclude the existence of unconnected axes, + which do not have links (bi-directional or otherwise). + 2) The PermMap passesd though both the longitude and latitude axes of + the WcsMap */ + if( !strcmp( nowcs_class, "PermMap" ) ){ + +/* Get the number of input and output coordinates. */ + nin = astGetNin( nowcs ); + nout = astGetNout( nowcs ); + +/* We need to know the axis permutation arrays and constants array for + the PermMap. */ + PermGet( (AstPermMap *) nowcs, &outperm, &inperm, &consts, status ); + if( astOK ) { + +/* Indicate we can swap with the PermMap. */ + ret = 1; + +/* Check each output axis. If any links between axes are found which are + not bi-directional, indicate that we cannot swap with the PermMap. */ + for( i = 0; i < nout; i++ ){ + if( outperm[ i ] >= 0 && outperm[ i ] < nin ) { + if( inperm[ outperm[ i ] ] != i ) { + ret = 0; + break; + } + } + } + +/* Check each input axis. If any links between axes are found which are + not bi-directional, indicate that we cannot swap with the PermMap. */ + for( i = 0; i < nin; i++ ){ + if( inperm[ i ] >= 0 && inperm[ i ] < nout ) { + if( outperm[ inperm[ i ] ] != i ) { + ret = 0; + break; + } + } + } + +/* Check that the longitude and latitude axes both have bi-directional + links in the PermMap, or are both unassigned. */ + if( ret ) { + +/* Get the indices of the longitude and latitude axes in the WcsMap */ + lonax = astGetWcsAxis( wcs, 0 ); + latax = astGetWcsAxis( wcs, 1 ); + +/* If the WcsMap is applied first... */ + if( wcs == (AstWcsMap *) map1 ) { + if( inperm[ lonax] < 0 && inperm[ latax ] < 0 ) { + ret = 1; + } else if( inperm[ lonax ] < 0 || inperm[ lonax ] >= nout || + inperm[ latax ] < 0 || inperm[ latax ] >= nout ) { + ret = 0; + } + +/* If the WcsMap is applied second ... */ + } else { + if( outperm[ lonax ] < 0 && outperm[ latax ] < 0 ) { + ret = 1; + } else if( outperm[ lonax ] < 0 || outperm[ lonax ] >= nin || + outperm[ latax ] < 0 || outperm[ latax ] >= nin ) { + ret = 0; + } + } + } + +/* If we can swap with the PermMap, the swapped Mappings may be + intrinsically simpler than the original mappings. */ + if( ret ) { + +/* If the PermMap precedes the WcsMap, this will be the case if the PermMap + has more outputs than inputs. If the WcsMap precedes the PermMap, this + will be the case if the PermMap has more inputs than outputs. */ + *simpler = ( nowcs == map1 ) ? nout > nin : nin > nout; + } + +/* Free the axis permutation and constants arrays. */ + outperm = (int *) astFree( (void *) outperm ); + inperm = (int *) astFree( (void *) inperm ); + consts = (double *) astFree( (void *) consts ); + } + } + } + +/* Re-instate the original settings of the Invert attributes for the + supplied MatrixMaps. */ + astSetInvert( map1, invert[ 0 ] ); + astSetInvert( map2, invert[ 1 ] ); + +/* Return the answer. */ + return astOK ? ret : 0; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* WcsMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* WcsMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the WcsMap. +* attrib +* Pointer to a null terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstWcsMap *this; /* Pointer to the WcsMap structure */ + int i; /* Axis index */ + int len; /* Length of the attribute name */ + int m; /* Projection parameter index */ + int nc; /* No. of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the WcsMap structure. */ + this = (AstWcsMap *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and clear the appropriate attribute. */ + +/* ProjP. */ +/* ------ */ + if ( nc = 0, ( 1 == astSscanf( attrib, "prpjp(%d)%n", &m, &nc ) ) + && ( nc >= len ) ) { + astClearPV( this, astGetWcsAxis( this, 1 ), m ); + +/* PV. */ +/* ------ */ + } else if ( nc = 0, ( 2 == astSscanf( attrib, "pv%d_%d%n", &i, &m, &nc ) ) + && ( nc >= len ) ) { + astClearPV( this, i - 1, m ); + +/* If the name was not recognised, test if it matches any of the + read-only attributes of this class. If it does, then report an + error. */ + } else if ( ( nc = 0, ( 1 == astSscanf( attrib, "wcsaxis(%d)%n", &i, &nc ) ) + && ( nc >= len ) ) || + !strcmp( attrib, "wcstype" ) || + !strcmp( attrib, "natlat" ) || + !strcmp( attrib, "natlon" ) ){ + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_clearattrib)( this_object, attrib, status ); + } +} + +static void ClearPV( AstWcsMap *this, int i, int m, int *status ) { +/* +*+ +* Name: +* astClearPV + +* Purpose: +* Clear a PVi_m attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* void astClearPV( AstWcsMap *this, int i, int m ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function clears a specified member of the PV attribute array, by +* resetting its value to AST__BAD. + +* Parameters: +* this +* A pointer to the WcsMap. +* i +* Zero based axis index. +* m +* Zero based parameter index. + +*- +*/ +/* Local Variables; */ + int npar; + int mxpar; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Report an error if the object has been cloned (i.e. has a reference + count that is greater than one). */ + if( astGetRefCount( this ) > 1 ) { + astError( AST__IMMUT, "astClear(%s): Projection parameter values " + "within the supplied %s cannot be cleared because the %s has " + "been cloned (programming error).", status, + astGetClass(this), astGetClass(this), astGetClass(this) ); + +/* Validate the axis index. */ + } else if( i < 0 || i >= astGetNin( this ) ){ + astError( AST__AXIIN, "astClearPV(%s): Axis index (%d) is invalid in " + "attribute PV%d_%d - it should be in the range 1 to %d.", + status, astGetClass( this ), i + 1, i + 1, m, + astGetNin( this ) ); + + } else { + +/* Find the maximum number of parameters allowed for the axis. */ + mxpar = astGetPVMax( this, i ); + +/* Ignore unused parameters. */ + if( m < 0 || m > mxpar ){ + +/* See if the parameter is currently set. Is so, set its value to + AST__BAD. */ + } else if( this->np && this->p ){ + npar = this->np[ i ]; + if( m < npar && this->p[ i ] ) this->p[ i ][ m ] = AST__BAD; + } + +/* Re-initialize the values stored in the "AstPrjPrm" structure. */ + InitPrjPrm( this, status ); + } +} + +static void ClearTPNTan( AstWcsMap *this, int *status ) { +/* +*+ +* Name: +* astClearTPNTan + +* Purpose: +* Clear the TPNTan attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* void ClearTPNTan( AstWcsMap *this, int *status ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function clears the TPNTan attribute, ensuring the projection +* parameters used by WCSLIB are adjusted accordingly. + +* Parameters: +* this +* A pointer to the WcsMap. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Clear the value. */ + this->tpn_tan = -INT_MAX; + +/* Re-initialize the values stored in the "AstPrjPrm" structure. */ + InitPrjPrm( this, status ); +} + +static void CopyPV( AstWcsMap *in, AstWcsMap *out, int *status ) { +/* +* Name: +* CopyPV + +* Purpose: +* Copy projection parameter information from one WcsMap to another. + +* Type: +* Private function. + +* Synopsis: +* void CopyPV( AstWcsMap *in, AstWcsMap *out ) + +* Description: +* This function copies projection parameter information from one +* WcsMap to another. + +* Parameters: +* in +* Pointer to the input WcsMap. +* out +* Pointer to the output WcsMap. + +*/ + + +/* Local Variables: */ + int i; /* Axis index */ + int latax_in; /* Index of input latitude axis */ + int latax_out; /* Index of output latitude axis */ + int lonax_in; /* Index of input longitude axis */ + int lonax_out; /* Index of output longitude axis */ + int nax_out; /* No. of axis in the output WcsMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Nullify the pointers stored in the output WcsMap since these may + currently be pointing at good data. Otherwise, the good data could be + freed by accident if the output object is deleted due to an error + occuring in this function. */ + out->np = NULL; + out->p = NULL; + +/* Do nothing more if either of the input pointers are null (i.e. if there + are no projection parameters. */ + if( in->np && in->p ){ + +/* Store the number of axes in the input and output WcsMaps */ + nax_out = astGetNin( out ); + +/* Allocate memory for the array holding the number of projection parameters + associated with each axis. */ + out->np = (int *) astMalloc( sizeof( int )*nax_out ); + +/* Allocate memory for the array of pointers which identify the arrays + holding the parameter values. */ + out->p = (double **) astMalloc( sizeof( double *)*nax_out ); + +/* Check pointers can be used */ + if( astOK ) { + +/* Initialise the above arrays. */ + for( i = 0; i < nax_out; i++ ) { + (out->np)[ i ] = 0; + (out->p)[ i ] = NULL; + } + +/* Copy the longitude and latitude values from in to out (other axes do + not have projection parameters). */ + lonax_in = astGetWcsAxis( in, 0 ); + latax_in = astGetWcsAxis( in, 1 ); + lonax_out = astGetWcsAxis( out, 0 ); + latax_out = astGetWcsAxis( out, 1 ); + + (out->np)[ lonax_out ] = (in->np)[ lonax_in ]; + (out->p)[ lonax_out ] = (double *) astStore( NULL, + (void *) (in->p)[ lonax_in ], + sizeof(double)*(in->np)[ lonax_in ] ); + + (out->np)[ latax_out ] = (in->np)[ latax_in ]; + (out->p)[ latax_out ] = (double *) astStore( NULL, + (void *) (in->p)[ latax_in ], + sizeof(double)*(in->np)[ latax_in ] ); + } + +/* If an error has occurred, free the output arrays. */ + if( !astOK ) FreePV( out, status ); + + } + +/* Re-initialize the values stored in the "AstPrjPrm" structure. */ + InitPrjPrm( out, status ); + +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two WcsMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* WcsMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two WcsMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a WcsMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the WcsMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWcsMap *that; + AstWcsMap *this; + int i, j; + int nin; + int nout; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two WcsMap structures. */ + this = (AstWcsMap *) this_object; + that = (AstWcsMap *) that_object; + +/* Check the second object is a WcsMap. We know the first is a + WcsMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAWcsMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + nout = astGetNout( this ); + if( astGetNin( that ) == nin && astGetNout( that ) == nout ) { + +/* If the Invert flags for the two WcsMaps differ, it may still be possible + for them to be equivalent. First compare the WcsMaps if their Invert + flags are the same. In this case all the attributes of the two WcsMaps + must be identical. */ + if( astGetInvert( this ) == astGetInvert( that ) ) { + + if( this->type == that->type && + this->wcsaxis[ 0 ] == that->wcsaxis[ 0 ] && + this->wcsaxis[ 1 ] == that->wcsaxis[ 1 ] ) { + + result = 1; + + if( this->np && that->np ){ + + for( i = 0; i < nout && result; i++ ) { + + if( (this->np)[ i ] != (that->np)[ i ] ) { + result = 0; + + } else if( (this->p)[ i ] && !(this->p)[ i ] ) { + result = 0; + + } else if( !(this->p)[ i ] && (this->p)[ i ] ) { + result = 0; + + } else if( (this->p)[ i ] && (this->p)[ i ] ) { + + for( j = 0; j < (this->np)[ i ]; j++ ) { + if( !astEQUAL( (this->p)[ i ][ j ], + (that->p)[ i ][ j ] ) ) { + result = 0; + break; + } + } + } + } + } + + } else if( this->np || that->np ){ + result = 0; + } + +/* If the Invert flags for the two WcsMaps differ, the attributes of the two + WcsMaps must be inversely related to each other. */ + } else { + +/* In the specific case of a WcsMap, Invert flags must be equal. */ + result = 0; + + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const PrjData *FindPrjData( int type, int *status ){ +/* +*+ +* Name: +* FindPrjData + +* Purpose: +* Get information about a WCSLIB projection given a projection type. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* const PrjData *FindPrjData( int type, int *status ) + +* Class Membership: +* WcsMap member function + +* Description: +* This function returns a pointer to an PrjData structure describing +* the WCSLIB projection with the supplied type. + +* Parameters: +* type +* The projection type. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the "const" PrjData structure describing the projection. + +* Notes: +* - The returned pointer points to an element in a static array and +* should not be freed. +* - This function attempts to execute even if an error has already +* occurred. A description of a "null" projection will be returned if +* this function subsequently fails (for instance if the projection is +* not recognised). +*- +*/ + + const PrjData *data; + data = PrjInfo; + while( data->prj != AST__WCSBAD && data->prj != type ) data++; + return data; +} + +static void FreePV( AstWcsMap *this, int *status ) { +/* +* +* Name: +* FreePV + +* Purpose: +* Free memory used to hold projection parameters + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* FreePV( AstWcsMap *this, int *status ) + +* Class Membership: +* WcsMap private function + +* Description: +* This function frees all the dynamic memory used to store projection +* parameter information in the supplied WcsMap. + +* Parameters: +* this +* A pointer to the WcsMap. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if an error has already occurred. + +* +*/ + int i; /* Axis index */ + + if( this->np ) this->np = (int *) astFree( (void *) this->np ); + if( this->p ){ + for( i = 0; i < astGetNin( this ); i++ ){ + this->p[ i ] = (double *) astFree( (void *) this->p[ i ] ); + } + this->p = (double **) astFree( (void *) this->p ); + } + +/* Re-initialize the values stored in the "AstPrjPrm" structure. */ + InitPrjPrm( this, status ); + + +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* WcsMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied WcsMap, +* in bytes. + +* Parameters: +* this +* Pointer to the WcsMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWcsMap *this; /* Pointer to WcsMap structure */ + int result; /* Result value to return */ + int i; /* Axis index */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the WcsMap structure. */ + this = (AstWcsMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astTSizeOf( this->np ); + if( this->p ){ + for( i = 0; i < astGetNin( this ); i++ ){ + result += astTSizeOf( (void *) this->p[ i ] ); + } + result += astTSizeOf( this->p ); + } + + result += astTSizeOf( this->params.p ); + result += astTSizeOf( this->params.p2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* WcsMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a WcsMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the WcsMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the WcsMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the WcsMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstWcsMap *this; /* Pointer to the WcsMap structure */ + const char *result; /* Pointer value to return */ + double dval; /* Floating point attribute value */ + int i; /* Axis index */ + int ival; /* Integer attribute value */ + int len; /* Length of attribute string */ + int m; /* Projection parameter index */ + int nc; /* No. of characters read by astSscanf */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(this_object); + +/* Obtain a pointer to the WcsMap structure. */ + this = (AstWcsMap *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an appropriate + format. Set "result" to point at the result string. */ + +/* ProjP. */ +/* ------ */ + if ( nc = 0, ( 1 == astSscanf( attrib, "projp(%d)%n", &m, &nc ) ) + && ( nc >= len ) ) { + dval = astGetPV( this, astGetWcsAxis( this, 1 ), m ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* PV. */ +/* --- */ + } else if ( nc = 0, ( 2 == astSscanf( attrib, "pv%d_%d%n", &i, &m, &nc ) ) + && ( nc >= len ) ) { + dval = astGetPV( this, i - 1, m ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* WcsType */ +/* ======= */ + } else if ( !strcmp( attrib, "wcstype" ) ) { + ival = astGetWcsType( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* PVMax */ +/* ===== */ + } else if ( nc = 0, ( 1 == astSscanf( attrib, "pvmax(%d)%n", &i, &nc ) ) + && ( nc >= len ) ) { + ival = astGetPVMax( this, i - 1 ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* NatLat */ +/* ====== */ + } else if ( !strcmp( attrib, "natlat" ) ) { + dval = astGetNatLat( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + +/* NatLon */ +/* ====== */ + } else if ( !strcmp( attrib, "natlon" ) ) { + dval = astGetNatLon( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval ); + result = getattrib_buff; + } + + +/* WcsAxis */ +/* ======= */ + } else if ( nc = 0, ( 1 == astSscanf( attrib, "wcsaxis(%d)%n", &i, &nc ) ) + && ( nc >= len ) ) { + ival = astGetWcsAxis( this, i - 1 ) + 1; + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ival ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_getattrib)( this_object, attrib, status ); + } + +/* Return the result. */ + return result; +} + +static double GetNatLat( AstWcsMap *this, int *status ) { +/* +*+ +* Name: +* GetNatLat + +* Purpose: +* Get the value of the NatLat attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* double GetNatLat( AstWcsMap *this, int *status ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns the value of the NatLat attribute. This is +* fixed value for most projection types , defined in the FITS-WCS paper +* II. For instance, all zenithal projections have NatLat = PI/2 (90 +* degrees). For some prjections (e.g. conics), the value is defined +* by a projection parameter. + +* Parameters: +* this +* A pointer to the WcsMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The attribute value, in radians. + +*- +*/ + double ret; /* Returned value */ + +/* The native latitude of the reference point of the projection is + constant for most projection, but for some (the conics) it is + specified by projection one on the latitude axis. */ + ret = FindPrjData( this->type, status )->theta0; + if( ret == AST__BAD ){ + ret = astGetPV( this, astGetWcsAxis( this, 1 ), 1 ); + if( ret != AST__BAD ) ret *= AST__DD2R; + } + +/* Return the result. */ + return ret; +} + +static double GetNatLon( AstWcsMap *this, int *status ) { +/* +*+ +* Name: +* GetNatLon + +* Purpose: +* Get the value of the NatLon attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* double GetNatLon( AstWcsMap *this, int *status ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns the value of the NatLon attribute. This is +* fixed value of zero for all projection types. + +* Parameters: +* this +* A pointer to the WcsMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The attribute value, in radians. + +*- +*/ + return 0.0; +} + +static int GetNP( AstWcsMap *this, int i, int *status ) { +/* +*+ +* Name: +* GetNP + +* Purpose: +* Get the number of projection parameters for a specified axis. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* int GetNP( AstWcsMap *this, int i, int *status ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns the current number of projection parameters +* associated with the speified axis. Some of these may be unset (i.e. +* equal to AST__BAD). The returned number is the size of the array +* holding the projection parameters. + +* Parameters: +* this +* A pointer to the WcsMap. +* i +* Zero based axis index. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The number of projection parameters for the specified axis. + +*- +*/ + double ret; + +/* Initialise */ + ret = 0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Validate the axis index, and get the count. */ + if( i >= 0 && this->np && i < astGetNin( this ) ) ret = this->np[ i ]; + + return ret; + +} + +static double GetPV( AstWcsMap *this, int i, int m, int *status ) { +/* +*+ +* Name: +* astGetPV + +* Purpose: +* Get the value of a PVi_m attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* double astGetPV( AstWcsMap *this, int i, int m ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns the current value of a specified member of the +* PV attribute array. A value of AST__BAD is returned if no value has +* been set for the parameter. + +* Parameters: +* this +* A pointer to the WcsMap. +* i +* Zero based axis index. +* m +* Zero based parameter index. + +* Returned Value: +* The value of the requested attribute, of AST__BAD if not set. + +*- +*/ + +/* Local Variables: */ + double ret; + int npar; + int mxpar; + +/* Initialise */ + ret = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Validate the axis index. */ + if( i < 0 || i >= astGetNin( this ) ){ + astError( AST__AXIIN, "astGetPV(%s): Axis index (%d) is invalid in " + "attribute PV%d_%d - it should be in the range 1 to %d.", + status, astGetClass( this ), i + 1, i + 1, m, astGetNin( this ) ); + +/* Find the maximum number of parameters allowed for the axis. */ + } else { + mxpar = astGetPVMax( this, i ); + +/* Validate the parameter index. */ + if( m < 0 || m > mxpar ){ + astError( AST__AXIIN, "astGetPV(%s): Parameter index (%d) is invalid " + "in attribute PV%d_%d for a \"%s\" projection - it should be " + "in the range 0 to %d.", status, astGetClass( this ), m, i + 1, m, + FindPrjData( this->type, status )->ctype, mxpar ); + +/* For latitude parameters use the values in the "params" structure which will + have been defaulted. */ + } else if( i == astGetWcsAxis( this, 1 ) ) { + ret = (this->params).p[ m ]; + +/* For other axes, see if the arrays stored in the WcsMap structure extend as + far as the requested parameter. If so, return the required attribute value. + Otherwise the AST__BAD value initialised above is retained. */ + } else if( this->np && this->p ){ + npar = this->np[ i ]; + if( m < npar && this->p[ i ] ) ret = this->p[ i ][ m ]; + } + +/* FITS-WCS paper II gives defaults for the first 3 longitude axis + parameters. The AST-specific TPN projection does not use this + convention since it needs all projection parameters to specify + correction terms. */ + if( ret == AST__BAD && i == astGetWcsAxis( this, 0 ) && + astGetWcsType( this ) != AST__TPN ) { + +/* Parameter zero has a default of zero. */ + if( m == 0 ) { + ret = 0.0; + +/* Parameter one has a default equal to the native longitude of the + reference point of the projection, in degrees. */ + } else if( m == 1 ) { + ret = astGetNatLon( this )*AST__DR2D; + +/* Parameter two has a default equal to the native latitude of the + reference point of the projection (in degrees). This is constant for + most projection, but for some (the conics) it is specified by + projection one on the latitude axis. */ + } else if( m == 2 ) { + ret = astGetNatLat( this )*AST__DR2D; + } + } + } + + return ret; + +} + +static int GetPVMax( AstWcsMap *this, int i, int *status ) { +/* +*+ +* Name: +* astGetPVMax + +* Purpose: +* Get the maximum projection parameter index for a WcsMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* int astGetPVMax( AstWcsMap *this, int i ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns the largest legal projection parameter index +* for a specified axis of the given WcsMap (i.e. the largest value of +* "m" in the attribute "PVi_m"). + +* Parameters: +* this +* A pointer to the WcsMap. +* i +* Zero based axis index. + +* Returned Value: +* The largest legal projection parameter index, or -1 if no +* projection parameters are allowed on the specified axis. + +*- +*/ + +/* Local Variables: */ + int mxpar; + +/* Initialise */ + mxpar = 0; + +/* Check the global error status. */ + if ( !astOK ) return -1; + +/* Validate the axis index. */ + if( i < 0 || i >= astGetNin( this ) ){ + astError( AST__AXIIN, "astGetPVMax(%s): Axis index (%d) is invalid in " + "attribute PVMax(%d) - it should be in the range 1 to %d.", + status, astGetClass( this ), i + 1, i + 1, astGetNin( this ) ); + +/* Find the maximum number of parameters allowed for the axis. */ + } else if( i == astGetWcsAxis( this, 0 ) ) { + mxpar = astSizeOf( this->params.p2 )/sizeof( double ); + + } else if( i == astGetWcsAxis( this, 1 ) ) { + mxpar = astSizeOf( this->params.p )/sizeof( double ); + + } + +/* The mxpar variable holds the max number of parameters. Return the the + largest legal parameter index (one less than the max number of + parameters). */ + return mxpar - 1; +} + +static void InitPrjPrm( AstWcsMap *this, int *status ) { +/* +* Name: +* InitPrjPrm + +* Purpose: +* Initialise the WcsLib PrjPrm structure, assigning default values for +* missing parameters. + +* Type: +* Private function. + +* Synopsis: +* void InitPrjPrm( AstWcsMap *this, int *status ) + +* Description: +* This function initializes the projection parameter information +* stored within the WcsLib AstPrjPrm structure associated with the +* supplied WcsMap. Default values are assigned to any unspecified +* parameter values. AST__BAD values are assigned if any parameters +* have not been supplied for which there is no default. + +* Parameters: +* this +* The WcsMap. +* status +* Pointer to the inherited status variable. + +*/ + + +/* Local Variables: */ + struct AstPrjPrm *params; /* The AstPrjPrm structure from the WcsMap */ + int i; /* Loop index */ + int latax; /* Index of latitude axis */ + int lonax; /* Index of longitude axis */ + int npar; /* No. of parameters supplied */ + int plen; /* Length of params array */ + int plen2; /* Length of latitude params array */ + int type; /* Projection type */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the AstPrjPrm structure*/ + params = &(this->params); + +/* Tell the routines within the WcsLib "proj.c" module to re-calculate + intermediate values. */ + params->flag = 0; + params->r0 = 0; + +/* If this is a TPN projection, indicate whether or not the + transformation should include the TAN projection or just the + polynomial transformation. */ + if( this->type == AST__TPN ) params->n = astGetTPNTan( this ); + +/* Find the max number of projection parameters associated with each + axis.*/ + plen2 = astSizeOf( params->p2 )/sizeof( double ); + plen = astSizeOf( params->p )/sizeof( double ); + +/* Initially set all parameter to AST__BAD. */ + for( i = 0; i < plen; i++ ) (params->p)[i] = AST__BAD; + for( i = 0; i < plen2; i++ ) (params->p2)[i] = AST__BAD; + +/* If the WcsMap contains any projection parameter values... */ + if( this->np && this->p ){ + +/* Get the index of the latitude axis. Currently, all projection + parameters are associated with the latitude axis (except for + the TPN projection, which is a hang-over from a earlier draft of the + FITS-WCS paper). */ + latax = astGetWcsAxis( this, 1 ); + +/* Find the number of projection parameters in the WcsMap for the + latitude axis. */ + npar = (this->np)[ latax ]; + if( npar > plen ) { + astError( AST__INTER, "InitPrjPrm(WcsMap): Too many projection " + "parameters on the latitude axis (%d > %d) (internal " + "AST programming error).", status, npar, plen ); + } + +/* Copy the parameters to the AstPrjPrm structure. Do not copy more than + can be stored in the AstPrjPrm structure. */ + for( i = 0; i < npar && i < plen; i++ ) { + (params->p)[ i ] = (this->p)[ latax ][ i ]; + } + +/* Do the same for the longitude axis (for the benefit of the TPN projection). */ + lonax = astGetWcsAxis( this, 0 ); + npar = (this->np)[ lonax ]; + + if( npar > plen2 ) { + astError( AST__INTER, "InitPrjPrm(WcsMap): Too many projection " + "parameters on the longitude axis (%d > %d) (internal " + "AST programming error).", status, npar, plen2 ); + } + + for( i = 0; i < npar && i < plen2; i++ ) { + (params->p2)[ i ] = (this->p)[ lonax ][ i ]; + } + + } + +/* Get the projection type. */ + type = astGetWcsType( this ); + +/* First supply default values for any missing projection parameters which + do not default to zero. */ + if( type == AST__SZP ){ + if( (params->p)[ 3 ] == AST__BAD ) (params->p)[ 3 ] = 90.0; + + } else if( type == AST__AIR ){ + if( (params->p)[ 1 ] == AST__BAD ) (params->p)[ 1 ] = 90.0; + + } else if( type == AST__CYP ){ + if( (params->p)[ 1 ] == AST__BAD ) (params->p)[ 1 ] = 1.0; + if( (params->p)[ 2 ] == AST__BAD ) (params->p)[ 2 ] = 1.0; + + } else if( type == AST__CEA ){ + if( (params->p)[ 1 ] == AST__BAD ) (params->p)[ 1 ] = 1.0; + + } else if( type == AST__TPN ){ + if( (params->p)[ 1 ] == AST__BAD ) (params->p)[ 1 ] = 1.0; + if( (params->p2)[ 1 ] == AST__BAD ) (params->p2)[ 1 ] = 1.0; + + } else if( type == AST__HPX ){ + if( (params->p)[ 1 ] == AST__BAD ) (params->p)[ 1 ] = 4.0; + if( (params->p)[ 2 ] == AST__BAD ) (params->p)[ 2 ] = 3.0; + + } + +/* Now use a default value of zero for any remaining unspecified values, + except for un-defaultable projection parameters. */ + for( i = 0; i < plen; i++ ){ + +/* Retain any AST__BAD value for these undefaultable parameters. */ + if( i == 1 && ( type == AST__BON || + type == AST__COP || type == AST__COE || + type == AST__COD || type == AST__COO ) ){ + +/* Use a default of zero for all other parameters. */ + } else { + if( (params->p)[ i ] == AST__BAD ) (params->p)[ i ] = 0.0; + } + } + +/* Do the same for the latitude projection parameters (if any) */ + for( i = 0; i < plen2; i++ ){ + if( i == 1 && ( type == AST__BON || + type == AST__COP || type == AST__COE || + type == AST__COD || type == AST__COO ) ){ + } else { + if( (params->p2)[ i ] == AST__BAD ) (params->p2)[ i ] = 0.0; + } + } +} + +void astInitWcsMapVtab_( AstWcsMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitWcsMapVtab + +* Purpose: +* Initialise a virtual function table for a WcsMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* void astInitWcsMapVtab( AstWcsMapVtab *vtab, const char *name ) + +* Class Membership: +* WcsMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the WcsMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAWcsMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->ClearPV = ClearPV; + vtab->GetNatLat = GetNatLat; + vtab->GetNatLon = GetNatLon; + vtab->GetPV = GetPV; + vtab->GetWcsAxis = GetWcsAxis; + vtab->GetPVMax = GetPVMax; + vtab->GetWcsType = GetWcsType; + vtab->SetPV = SetPV; + vtab->TestPV = TestPV; + vtab->IsZenithal = IsZenithal; + + vtab->ClearFITSProj = ClearFITSProj; + vtab->TestFITSProj = TestFITSProj; + vtab->GetFITSProj = GetFITSProj; + vtab->SetFITSProj = SetFITSProj; + + vtab->ClearTPNTan = ClearTPNTan; + vtab->TestTPNTan = TestTPNTan; + vtab->GetTPNTan = GetTPNTan; + vtab->SetTPNTan = SetTPNTan; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + + parent_mapsplit = mapping->MapSplit; + mapping->MapSplit = MapSplit; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + +/* Declare the destructor and copy constructor. */ + astSetDelete( (AstObjectVtab *) vtab, Delete ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + +/* Declare the class dump function. */ + astSetDump( vtab, Dump, "WcsMap", "FITS-WCS sky projection" ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int IsZenithal( AstWcsMap *this, int *status ){ +/* +*+ +* Name: +* IsZenithal + +* Purpose: +* Determine if this WcsMap represents a zenithal projection. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* int IsZenithal( AstWcsMap *this, int *status ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns a flag indicating if this WcsMap is a zenithal +* projection. Some projections which are classed as zenithal in the +* Calabretta and Greisen paper are only genuinely zenithal if the +* projection parameters have certain values. These projections are +* not considered to be zenithal unless the projection parameters have +* appropriate values. + +* Parameters: +* this +* The WcsMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A non-zero value if the WcsMap represents a zenithal projection. + +*- +*/ + +/* Local Variables: */ + double p1; /* PVi_1 */ + double p2; /* PVi_2 */ + double p3; /* PVi_3 */ + int latax; /* Index of latitude axis */ + int ret; /* Returned flag */ + int type; /* Projection type */ + +/* Initialise the returned value. */ + ret = 0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Get the projection type. */ + type = astGetWcsType( this ); + +/* Get the index of the latitude axis. */ + latax = astGetWcsAxis( this, 1 ); + +/* The following are always zenithal... */ + if( type == AST__TAN || type == AST__STG || type == AST__ARC || + type == AST__ZPN || type == AST__ZEA || type == AST__AIR || + type == AST__TPN ) { + ret = 1; + +/* The following are sometimes zenithal... */ + } else if( type == AST__AZP ) { + p2 = astGetPV( this, latax, 2 ); + if( p2 == AST__BAD || p2 == 0.0 ) ret = 1; + + } else if( type == AST__SIN ) { + p1 = astGetPV( this, latax, 1 ); + p2 = astGetPV( this, latax, 2 ); + if( p1 == AST__BAD ) p1 = 0.0; + if( p2 == AST__BAD ) p2 = 0.0; + if( p1 == 0.0 && p2 == 0.0 ) ret = 1; + + } else if( type == AST__SZP ) { + p3 = astGetPV( this, latax, 2 ); + if( p3 == AST__BAD ) p3 = 90.0; + if( p3 == 90.0 || p3 == -90.0 ) ret = 1; + + } + + return ret; +} + +static int LongRange( const PrjData *prjdata, struct AstPrjPrm *params, + double *high, double *low, int *status ){ +/* +* +* Name: +* LongRange + +* Purpose: +* See if primary range of longitude produced by a WCSLIB mapping is +* [0,360] or [-180,+180]. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* void LongRange( const PrjData *prjdata, struct AstPrjPrm *params, +* double *high, double *low, int *status ) + +* Class Membership: +* WcsMap internal utility function. + +* Description: +* This function uses the WCSLIB library to transform the supplied input +* positions. + +* Parameters: +* prjdata +* A pointer to information about the mapping. +* params +* Pointer to a WCSLIB "AstPrjPrm" structure containing the projection +* parameters, etc. +* high +* A pointer to a location at which is returned the upper bound of +* the primary longitude range. +* low +* A pointer to a location at which is returned the lower bound of +* the primary longitude range. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A flag indicating if the sky->xy transformation is cyclic (i.e. +* [a,0] gets mapped to the same (x,y) position as [a+2.PI,0]). +*/ + + +/* Local Variables: */ + int point; /* Loop counter for points */ + static double xx[ 4 ] = { -1.0E-6, 0.0, 1.0E-6, 0.0 }; + static double yy[ 4 ] = { 0.0, 1.0E-6, 0.0, -1.0E-6 }; + double aa; + double bb; + double xxx[ 2 ]; + double yyy[ 2 ]; + int cyclic; + +/* Initialise the returned values. */ + *high = 180.0; + *low = -180.0; + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Project each of the points. If any longitude value is found which is + greater than 180 degrees, return [0,360] as the longitude range. */ + for( point = 0; point < 4; point++ ){ + if( !prjdata->WcsRev( xx[ point ], yy[ point ], params, &aa, &bb ) ){ + if( aa > 180.0 ){ + *high = 360.0; + *low = 0.0; + break; + } + } + } + +/* See if the projection is cyclic. Transform the sky positions [90,bb] and + [450,bb] into cartesian positions and see if they are the same. */ + prjdata->WcsFwd( 90.0, bb, params, xxx, yyy ); + prjdata->WcsFwd( 450.0, bb, params, xxx + 1, yyy + 1 ); + cyclic = ( fabs( xxx[ 0 ] - xxx[ 1 ] ) < 1.0E-10 && + fabs( yyy[ 0 ] - yyy[ 1 ] ) < 1.0E-10 ); + +/* Return. */ + return cyclic; + +} + +static int Map( AstWcsMap *this, int forward, int npoint, double *in0, + double *in1, double *out0, double *out1, int *status ){ +/* +* +* Name: +* Map + +* Purpose: +* Transform a set of points using a function from the WCSLIB library. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int Map( AstWcsMap *this, int forward, int npoint, double *in0, +* double *in1, double *out0, double *out1 ) + +* Class Membership: +* WcsMap internal utility function. + +* Description: +* This function uses the WCSLIB library to transform the supplied input +* positions. + +* Parameters: +* this +* Pointer to the WcsMap. +* forward +* A non-zero value indicates that the forward projection from +* (long,lat) to (x,y) is required, while a zero value requests the +* reverse transformation. +* npoint +* The number of points to transform (i.e. the size of the +* in0, in1, out0 and out1 arrays). +* in0 +* A pointer to the input coordinate data for the 0th axis (i.e. +* longitude or X depending on "forward"). +* in1 +* A pointer to the input coordinate data for the 1st axis (i.e. +* latitude or Y depending on "forward"). +* out0 +* A pointer to the returned output coordinate data for the 0th axis +* (i.e. X or longitude depending on "forward"). +* out1 +* A pointer to the returned output coordinate data for the 1st axis +* (i.e. Y or latitude depending on "forward"). + +* Returned Value: +* The status value: 0 - Success +* 1 - Unrecognised projection type +* 2 - Invalid projection parameters values. +* 4 - Error existed on entry +* 100 - 399: Longitude axis projection parameter +* (status-100) not supplied. +* 400 - 699: Latitude axis projection parameter +* (status-400) not supplied. + + +* Notes: +* - This function does not report any errors. Reporting of suitable +* error messages is the responsibility of the calling function. +* - The value 4 will be returned if this function is invoked with the +* global error status set. +* +*/ + +/* Local Variables: */ + const PrjData *prjdata; /* Information about the projection */ + double factor; /* Factor that scales input into radians. */ + double latitude; /* Latitude value in degrees */ + double longhi; /* Upper longitude limit in degrees */ + double longitude; /* Longitude value in degrees */ + double longlo; /* Lower longitude limit in degrees */ + double x; /* X Cartesian coordinate in degrees */ + double y; /* Y Cartesian coordinate in degrees */ + int cyclic; /* Is sky->xy transformation cyclic? */ + int i; /* Loop count */ + int plen; /* Length of proj par array */ + int point; /* Loop counter for points */ + int type; /* Projection type */ + int wcs_status; /* Status from WCSLIB functions */ + struct AstPrjPrm *params; /* Pointer to structure holding WCSLIB info */ + +/* Check the global error status. */ + if ( !astOK ) return 4; + +/* Initialise variables to avoid compiler warnings. */ + longlo = AST__BAD; + longhi = AST__BAD; + +/* Store the projection type. */ + type = astGetWcsType( this ); + +/* Get information about the projection. */ + prjdata = FindPrjData( type, status ); + +/* Return if there are no WcsLib mapping functons associated with the + projection. */ + if( ( !prjdata->WcsFwd && forward ) || + ( !prjdata->WcsRev && !forward ) ) return 1; + +/* Check that all necessary projection parameters have been supplied, or + can be defaulted. */ + params = &(this->params); + plen = astSizeOf( params->p )/sizeof( double ); + for( i = 0; i < plen; i++ ) { + if( ( params->p)[ i ] == AST__BAD ) return 400+i; + } + +/* If we are doing a reverse mapping, get the acceptable range of longitude + values. */ + cyclic = forward ? 0 : LongRange( prjdata, params, &longhi, &longlo, + status ); + +/* The WcsMap input and output values are normally in radians, but if + the TPNTan attribute has been reset then they are in degrees. The + WCSLIB projection functions always expect and return degrees. Get + the factor that scales the WcsMap input into radians. */ + factor = astGetTPNTan( this ) ? 1.0 : AST__DD2R; + +/* Loop to apply the projection to each point in turn, checking for + (and propagating) bad values in the process. */ + for ( point = 0; point < npoint; point++ ) { + if ( in0[ point ] == AST__BAD || + in1[ point ] == AST__BAD ){ + out0[ point ] = AST__BAD; + out1[ point ] = AST__BAD; + } else { + +/* First deal with forward projection calls */ + if ( forward ){ + +/* The input coordinates are assumed to be longitude and latitude, in + radians or degrees (as specified by the TPNTan attribute). Convert them + to degrees ensuring that the longitude value is in the range [-180,180] + and the latitude is in the range [-90,90] (as required by the WCSLIB + library). Any point with a latitude outside the range [-90,90] is + converted to the equivalent point on the complementary meridian. */ + latitude = AST__DR2D*palDrange( factor*in1[ point ] ); + if ( latitude > 90.0 ){ + latitude = 180.0 - latitude; + longitude = AST__DR2D*palDrange( AST__DPI + factor*in0[ point ] ); + + } else if ( latitude < -90.0 ){ + latitude = -180.0 - latitude; + longitude = AST__DR2D*palDrange( AST__DPI + factor*in0[ point ] ); + + } else { + longitude = AST__DR2D*palDrange( factor*in0[ point ] ); + } + +/* Call the relevant WCSLIB forward projection function. */ + wcs_status = prjdata->WcsFwd( longitude, latitude, params, &x, &y ); + +/* Store the returned Cartesian coordinates, converting them from degrees + to radians. If the position could not be projected, use the value + AST__BAD. Abort for any other bad status. */ + if( wcs_status == 0 ){ + out0[ point ] = (AST__DD2R/factor)*x; + out1[ point ] = (AST__DD2R/factor)*y; + + } else if( wcs_status == 1 ){ + return 2; + + } else if( wcs_status == 2 ){ + out0[ point ] = AST__BAD; + out1[ point ] = AST__BAD; + + } else { + return wcs_status; + } + +/* Now deal with reverse projection calls */ + } else { + +/* Convert the supplied Cartesian coordinates from radians to degrees. */ + x = (AST__DR2D*factor)*in0[ point ]; + y = (AST__DR2D*factor)*in1[ point ]; + +/* Call the relevant WCSLIB reverse projection function. */ + wcs_status = prjdata->WcsRev( x, y, params, &longitude, &latitude ); + +/* Store the returned longitude and latitude, converting them from degrees + to radians. Many projections (ARC, AIT, ZPN, etc) are not cyclic (i.e. + [long,lat]=[0,0] does not get mapped to the same place as + [long,lat]=[360,0] ). Only accept values in the primary longitude or + latitude ranges. This avoids (x,y) points outside the physical domain + of the mapping being assigned valid (long,lat) values. */ + if( wcs_status == 0 ){ + if( ( cyclic || ( longitude < longhi && + longitude >= longlo ) ) && + fabs( latitude ) <= 90.0 ){ + + out0[ point ] = (AST__DD2R/factor)*longitude; + out1[ point ] = (AST__DD2R/factor)*latitude; + + } else { + out0[ point ] = AST__BAD; + out1[ point ] = AST__BAD; + } + +/* Abort if projection parameters were unusable. */ + } else if( wcs_status == 1 ){ + return 2; + +/* If the position could not be projected, use the value AST__BAD. */ + } else if( wcs_status == 2 ){ + out0[ point ] = AST__BAD; + out1[ point ] = AST__BAD; + +/* Abort if projection parameters were not supplied. */ + } else { + return wcs_status; + } + + } + + } + + } + + return 0; +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* WcsMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated WcsMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated WcsMap with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated WcsMap which is to be merged with +* its neighbours. This should be a cloned copy of the WcsMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* WcsMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated WcsMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstMapping *mc[2]; /* Copies of supplied Mappings to swap */ + AstMapping *smc0; /* Simplied Mapping */ + AstMapping *smc1; /* Simplied Mapping */ + const char *nclass; /* Pointer to neighbouring Mapping class */ + const char *class1; /* Pointer to first Mapping class string */ + const char *class2; /* Pointer to second Mapping class string */ + int do1; /* Would a backward swap make a simplification? */ + int do2; /* Would a forward swap make a simplification? */ + int i1; /* Lower index of the two WcsMaps being merged */ + int i2; /* Upper index of the two WcsMaps being merged */ + int i; /* Mapping index */ + int ic[2]; /* Copies of supplied invert flags to swap */ + int merge; /* Can WcsMap merge with a neighbour? */ + int nin; /* Number of coordinates for WcsMap */ + int nstep1; /* No. of Mappings backwards to next mergable Mapping */ + int nstep2; /* No. of Mappings forward to next mergable Mapping */ + int result; /* Result value to return */ + int swaphi; /* Can WcsMap be swapped with higher neighbour? */ + int swaplo; /* Can WcsMap be swapped with lower neighbour? */ + int type; /* Projection type */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + i1 = 0; + i2 = 0; + +/* Get the number of axes for the WcsMap. */ + nin = astGetNin( ( *map_list )[ where ] ); + +/* First of all, see if the WcsMap can be replaced by a simpler Mapping, + without reference to the neighbouring Mappings in the list. */ +/* ======================================================================*/ +/* WcsMaps with map type of AST__WCSBAD are equivalent to a UnitMap. */ + type = astGetWcsType( this ); + if( type == AST__WCSBAD ){ + +/* Annul the WcsMap pointer in the list and replace it with a UnitMap + pointer, and indicate that the forward transformation of the returned + UnitMap should be used. */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) astUnitMap( nin, "", status ); + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the WcsMap itself could not be simplified, see if it can be merged + with the Mappings on either side of it in the list. This can only be + done in series for a WcsMap. */ +/* ===================================================================== */ + } else if( series && *nmap > 1 ) { + +/* Store the classes of the neighbouring Mappings in the list. */ + class1 = ( where > 0 ) ? astGetClass( ( *map_list )[ where - 1 ] ) : NULL; + class2 = ( where < *nmap - 1 ) ? astGetClass( ( *map_list )[ where + 1 ] ) : NULL; + +/* A WcsMap can only combine with its own inverse. Set a flag indicating + that we have not yet found a neighbour with which the WcsMap can be + merged. */ + merge = 0; + +/* First check the lower neighbour (if any). */ + if( where > 0 ) { + i1 = where - 1; + i2 = where; + merge = CanMerge( ( *map_list )[ i1 ], (* invert_list)[ i1 ], + ( *map_list )[ i2 ], (* invert_list)[ i2 ], status ); + } + +/* If the WcsMap can not be merged with its lower neighbour, check its + upper neighbour (if any) in the same way. */ + if( !merge && where < *nmap - 1 ) { + i1 = where; + i2 = where + 1; + merge = CanMerge( ( *map_list )[ i1 ], (* invert_list)[ i1 ], + ( *map_list )[ i2 ], (* invert_list)[ i2 ], status ); + } + +/* If either neighbour has passed these checks, it is the inverse of the + WcsMap being checked. The pair of WcsMaps can be replaced by a single + UnitMap. */ + if( merge ) { + +/* Annul the two WcsMaps. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + (void) astAnnul( ( *map_list )[ i2 ] ); + +/* Create a UnitMap, and store a pointer for it in place of the first + WcsMap. */ + ( *map_list )[ i1 ] = (AstMapping *) astUnitMap( nin, "", status ); + ( *invert_list )[ i1 ] = 0; + +/* Shuffle down the remaining Mappings to fill the hole left by the + second WcsMap. */ + for ( i = i2 + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + (*nmap)--; + result = i1; + +/* If the WcsMap could not merge directly with either of its neighbours, + we consider whether it would be worthwhile to swap the WcsMap with + either of its neighbours. This can only be done for certain PermMaps, + and will usually require both Mappings to be modified (unless they are + commutative). The advantage of swapping the order of the Mappings is that + it may result in the WcsMap being adjacent to a Mapping with which it can + merge directly on the next invocation of this function, thus reducing the + number of Mappings in the list. */ + } else { + +/* Set a flag if we could swap the WcsMap with its higher neighbour. "do2" + is returned if swapping the Mappings would simplify either of the + Mappings. */ + if( where + 1 < *nmap ){ + swaphi = CanSwap( ( *map_list )[ where ], + ( *map_list )[ where + 1 ], + ( *invert_list )[ where ], + ( *invert_list )[ where + 1 ], &do2, status ); + } else { + do2 = 0; + swaphi = 0; + } + +/* If so, step through each of the Mappings which follow the WcsMap, + looking for a Mapping with which the WcsMap could merge directly. Stop + when such a Mapping is found, or if a Mapping is found with which the + WcsMap could definitely not swap. Note the number of Mappings which + separate the WcsMap from the Mapping with which it could merge (if + any). */ + nstep2 = -1; + if( swaphi ){ + for( i2 = where + 1; i2 < *nmap; i2++ ){ + +/* See if we can merge with this Mapping. If so, note the number of steps + between the two Mappings and leave the loop. */ + if( CanMerge( ( *map_list )[ i2 ], ( *invert_list )[ i2 ], + ( *map_list )[ where ], ( *invert_list )[ where ], status ) ) { + nstep2 = i2 - where - 1; + break; + } + +/* If there is no chance that we can swap with this Mapping, leave the loop + with -1 for the number of steps to indicate that no merging is possible. + WcsMaps can swap only with some PermMaps. */ + nclass = astGetClass( ( *map_list )[ i2 ] ); + if( strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Do the same working forward from the WcsMap towards the start of the map + list. */ + if( where > 0 ){ + swaplo = CanSwap( ( *map_list )[ where - 1 ], + ( *map_list )[ where ], + ( *invert_list )[ where - 1 ], + ( *invert_list )[ where ], &do1, status ); + } else { + do1 = 0; + swaplo = 0; + } + + nstep1 = -1; + if( swaplo ){ + for( i1 = where - 1; i1 >= 0; i1-- ){ + + if( CanMerge( ( *map_list )[ i1 ], ( *invert_list )[ i1 ], + ( *map_list )[ where ], ( *invert_list )[ where ], status ) ) { + nstep1 = where - 1 - i1; + break; + } + + nclass = astGetClass( ( *map_list )[ i1 ] ); + if( strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Choose which neighbour to swap with so that the WcsMap moves towards the + nearest Mapping with which it can merge. */ + if( do1 || ( + nstep1 != -1 && ( nstep2 == -1 || nstep2 > nstep1 ) ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + } else if( do2 || nstep2 != -1 ){ + nclass = class2; + i1 = where; + i2 = where + 1; + } else { + nclass = NULL; + } + +/* If there is a target Mapping in the list with which the WcsMap could + merge, replace the supplied Mappings with swapped Mappings to bring a + WcsMap closer to the target Mapping. */ + if( nclass ){ + + WcsPerm( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + +/* Store the index of the first modified Mapping. */ + result = i1; + +/* If there is no Mapping available for merging, it may still be + advantageous to swap with a neighbour because the swapped Mapping may + be simpler than the original Mappings. */ + } else if( swaphi || swaplo ) { + +/* Choose a neightbour to swap with. If both are suitable for swapping, + swap with the lower. */ + if( swaplo ){ + nclass = class1; + i1 = where - 1; + i2 = where; + } else { + nclass = class2; + i1 = where; + i2 = where + 1; + } + +/* Take copies of the Mapping and Invert flag arrays so we do not change + the supplied values. */ + mc[ 0 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[0] ); + mc[ 1 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[1] ); + ic[ 0 ] = ( (*invert_list) + i1 )[0]; + ic[ 1 ] = ( (*invert_list) + i1 )[1]; + +/* Swap these Mappings. */ + WcsPerm( mc, ic, where - i1, status ); + +/* If neither of the swapped Mappings can be simplified further, then there + is no point in swapping the Mappings, so just annul the map copies. */ + smc0 = astSimplify( mc[0] ); + smc1 = astSimplify( mc[1] ); + + if( astGetClass( smc0 ) == astGetClass( mc[0] ) && + astGetClass( smc1 ) == astGetClass( mc[1] ) ) { + + mc[ 0 ] = (AstMapping *) astAnnul( mc[ 0 ] ); + mc[ 1 ] = (AstMapping *) astAnnul( mc[ 1 ] ); + +/* If one or both of the swapped Mappings could be simplified, then annul + the supplied Mappings and return the swapped mappings, storing the index + of the first modified Mapping. */ + } else { + (void ) astAnnul( ( (*map_list) + i1 )[0] ); + (void ) astAnnul( ( (*map_list) + i1 )[1] ); + + ( (*map_list) + i1 )[0] = mc[ 0 ]; + ( (*map_list) + i1 )[1] = mc[ 1 ]; + + ( (*invert_list) + i1 )[0] = ic[ 0 ]; + ( (*invert_list) + i1 )[1] = ic[ 1 ]; + + result = i1; + + } + +/* Annul the simplied Mappings */ + smc0 = astAnnul( smc0 ); + smc1 = astAnnul( smc1 ); + + } + } + } + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* WcsMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing WcsMap. This is only possible if the specified inputs +* correspond to some subset of the WcsMap outputs. That is, there +* must exist a subset of the WcsMap outputs for which each output +* depends only on the selected WcsMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied WcsMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the WcsMap to be split (the WcsMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied WcsMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied WcsMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied WcsMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstWcsMap *newwcs; /* Pointer to returned WcsMap */ + AstWcsMap *this; /* Pointer to WcsMap structure */ + int *result; /* Pointer to returned array */ + int *inperm; /* Input axis permutation array */ + int *outperm; /* Output axis permutation array */ + int i; /* Loop count */ + int iin; /* Mapping input index */ + int ilat; /* Index of latitude axis in new WcsMap */ + int ilatlon; /* Index of last lat or lon axis */ + int ilon; /* Index of longitude axis in new WcsMap */ + int latax; /* Index of latitude axis in supplied WcsMap */ + int lonax; /* Index of longitude axis in supplied WcsMap */ + int mnin; /* No. of Mapping inputs */ + int ok; /* Are input indices OK? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the parent astMapSplit method to see if it can do the job. */ + result = (*parent_mapsplit)( this_map, nin, in, map, status ); + +/* If not, we provide a special implementation here. */ + if( !result ) { + +/* Get a pointer to the WcsMap structure. */ + this = (AstWcsMap *) this_map; + +/* Prevent compiler warnings. */ + ilatlon = -1; + +/* Allocate memory for the returned array. */ + result = astMalloc( sizeof( int )*(size_t) nin ); + if( astOK ) { + +/* Get the indices of the longitude and latitude axes in the WcsMap */ + lonax = astGetWcsAxis( this, 0 ); + latax = astGetWcsAxis( this, 1 ); + +/* See if the selected axes include the longitude and/or latitude axis. + At the same time check the axis indices are ok, and set up the output + axis array. */ + ilat = -1; + ilon = -1; + mnin = astGetNin( this ); + ok = 1; + for( i = 0; i < nin; i++ ) { + iin = in[ i ]; + if( iin < 0 || iin >= mnin ) { + ok = 0; + break; + } else if( iin == lonax ) { + ilon = i; + ilatlon = i; + } else if( iin == latax ) { + ilat = i; + ilatlon = i; + } + result[ i ] = iin; + } + +/* If any of the input indices were invalid, free the returned array. */ + if( !ok ) { + result = astFree( result ); + +/* If both longitude and latitude axes are selected, then the returned Mapping + is a WcsMap. Create one based on the supplied WcsMap. */ + } else if( ilat != -1 && ilon != -1 ) { + newwcs = astWcsMap( nin, astGetWcsType( this ), ilon + 1, ilat + 1, + "", status ); + CopyPV( this, newwcs, status ); + astSetInvert( newwcs, astGetInvert( this ) ); + *map = (AstMapping *) newwcs; + +/* If neither the longitude nor the latitude axis has been selected, then + the returned Mapping is a UnitMap. */ + } else if( ilat == -1 && ilon == -1 ) { + *map = (AstMapping *) astUnitMap( nin, "", status ); + +/* If only one of the latitude and longitude axes was selected we remove + it from the returned Mapping (a PermMap) and list of outputs */ + } else if( nin > 1 ) { + + for( i = ilatlon; i < nin - 1; i++ ) { + result[ i ] = result[ i + 1 ]; + } + result[ i ] = -1; + + inperm = astMalloc( sizeof( int )*(size_t) nin ); + outperm = astMalloc( sizeof( int )*(size_t) ( nin - 1 ) ); + if( outperm ) { + for( i = 0; i < ilatlon; i++ ) { + inperm[ i ] = i; + outperm[ i ] = i; + } + inperm[ ilatlon ] = INT_MAX; + for( i = ilatlon + 1; i < nin; i++ ) { + inperm[ i ] = i - 1; + outperm[ i - 1 ] = i; + } + + *map = (AstMapping *) astPermMap( nin, inperm, nin - 1, outperm, NULL, " ", status ); + + } + inperm = astFree( inperm ); + outperm = astFree( outperm ); + + } else { + result = astFree( result ); + } + } + } + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static void PermGet( AstPermMap *map, int **outperm, int **inperm, + double **consts, int *status ){ +/* +* Name: +* PermGet + +* Purpose: +* Get the axis permutation and constants array for a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* void PermGet( AstPermMap *map, int **outperm, int **inperm, +* double **const, int *status ) + +* Class Membership: +* WcsMap member function + +* Description: +* This function returns axis permutation and constants arrays which can +* be used to create a PermMap which is equivalent to the supplied PermMap. + +* Parameters: +* map +* The PermMap. +* outperm +* An address at which to return a popinter to an array of ints +* holding the output axis permutation array. The array should be +* released using astFree when no longer needed. +* inperm +* An address at which to return a popinter to an array of ints +* holding the input axis permutation array. The array should be +* released using astFree when no longer needed. +* consts +* An address at which to return a popinter to an array of doubles +* holding the constants array. The array should be released using +* astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - NULL pointers are returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding input positions for PermMap */ + AstPointSet *pset2; /* PointSet holding output positions for PermMap */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + double *cnst; /* Pointer to constants array */ + double cn; /* Potential new constant value */ + double ip; /* Potential output axis index */ + double op; /* Potential input axis index */ + int *inprm; /* Pointer to input axis permutation array */ + int *outprm; /* Pointer to output axis permutation array */ + int i; /* Axis count */ + int nc; /* Number of constants stored so far */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + +/* Initialise. */ + if( outperm ) *outperm = NULL; + if( inperm ) *inperm = NULL; + if( consts ) *consts = NULL; + +/* Check the global error status and the supplied pointers. */ + if ( !astOK || !outperm || !inperm || !consts ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + nc = 0; + +/* Get the number of input and output axes for the supplied PermMap. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* Allocate the memory for the returned arrays. */ + outprm = (int *) astMalloc( sizeof( int )* (size_t) nout ); + inprm = (int *) astMalloc( sizeof( int )* (size_t) nin ); + cnst = (double *) astMalloc( sizeof( double )* (size_t) ( nout + nin ) ); + +/* Returned the pointers to these arrays.*/ + *outperm = outprm; + *inperm = inprm; + *consts = cnst; + +/* Create two PointSets, each holding two points, which can be used for + input and output positions with the PermMap. */ + pset1 = astPointSet( 2, nin, "", status ); + pset2 = astPointSet( 2, nout, "", status ); + +/* Set up the two input positions to be [0,1,2...] and [-1,-1,-1,...]. The + first position is used to enumerate the axes, and the second is used to + check for constant axis values. */ + ptr1 = astGetPoints( pset1 ); + if( astOK ){ + for( i = 0; i < nin; i++ ){ + ptr1[ i ][ 0 ] = ( double ) i; + ptr1[ i ][ 1 ] = -1.0; + } + } + +/* Use the PermMap to transform these positions in the forward direction. */ + (void) astTransform( map, pset1, 1, pset2 ); + +/* Look at the mapped positions to determine the output axis permutation + array. */ + ptr2 = astGetPoints( pset2 ); + if( astOK ){ + +/* No constant axis valeus found yet. */ + nc = 0; + +/* Do each output axis. */ + for( i = 0; i < nout; i++ ){ + +/* If the output axis value is copied from an input axis value, the index + of the appropriate input axis will be in the mapped first position. */ + op = ptr2[ i ][ 0 ]; + +/* If the output axis value is assigned a constant value, the result of + mapping the two different input axis values will be the same. */ + cn = ptr2[ i ][ 1 ]; + if( op == cn ) { + +/* We have found another constant. Store it in the constants array, and + store the index of the constant in the output axis permutation array. */ + cnst[ nc ] = cn; + outprm[ i ] = -( nc + 1 ); + nc++; + +/* If the output axis values are different, then the output axis value + must be copied from the input axis value. */ + } else { + outprm[ i ] = (int) ( op + 0.5 ); + } + } + } + +/* Now do the same thing to determine the input permutation array. */ + if( astOK ){ + for( i = 0; i < nout; i++ ){ + ptr2[ i ][ 0 ] = ( double ) i; + ptr2[ i ][ 1 ] = -1.0; + } + } + + (void) astTransform( map, pset2, 0, pset1 ); + + if( astOK ){ + + for( i = 0; i < nin; i++ ){ + + ip = ptr1[ i ][ 0 ]; + cn = ptr1[ i ][ 1 ]; + if( ip == cn ) { + + cnst[ nc ] = cn; + inprm[ i ] = -( nc + 1 ); + nc++; + + } else { + inprm[ i ] = (int) ( ip + 0.5 ); + } + } + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* If an error has occurred, attempt to free the returned arrays. */ + if( !astOK ) { + *outperm = (int *) astFree( (void *) *outperm ); + *inperm = (int *) astFree( (void *) *inperm ); + *consts = (double *) astFree( (void *) *consts ); + } + +/* Return. */ + return; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* WcsMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a WcsMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the WcsMap. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Local Variables: */ + AstWcsMap *this; /* Pointer to the WcsMap structure */ + double dval; /* Attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + int i; /* Axis index */ + int m; /* Projection parameter number */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the WcsMap structure. */ + this = (AstWcsMap *) this_object; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* ProjP(i). */ +/* --------- */ + if ( nc = 0, ( 2 == astSscanf( setting, "projp(%d)= %lg %n", &m, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetPV( this, astGetWcsAxis( this, 1 ), m, dval ); + +/* PV. */ +/* --- */ + } else if ( nc = 0, ( 3 == astSscanf( setting, "pv%d_%d= %lg %n", &i, &m, &dval, &nc ) ) + && ( nc >= len ) ) { + astSetPV( this, i - 1, m, dval ); + +/* Define macros to see if the setting string matches any of the + read-only attributes of this class. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + +#define MATCH2(attrib) \ + ( nc = 0, ( 1 == astSscanf( setting, attrib "(%d)=%*[^\n]%n", &i, &nc ) ) && \ + ( nc >= len ) ) + +/* If the attribute was not recognised, use this macro to report an error + if a read-only attribute has been specified. */ + } else if ( MATCH( "wcstype" ) || + MATCH( "natlat" ) || + MATCH( "natlon" ) || + MATCH2( "wcsaxis" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + (*parent_setattrib)( this_object, setting, status ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +#undef MATCH2 +} + +static void SetPV( AstWcsMap *this, int i, int m, double val, int *status ) { +/* +*+ +* Name: +* astSetPV + +* Purpose: +* Set the value of a PVi_m attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* void astSetPV( AstWcsMap *this, int i, int m, double val ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function stores a value for the specified member of the PV +* attribute array. + +* Parameters: +* this +* A pointer to the WcsMap. +* i +* Zero based axis index. +* m +* Zero based parameter index. + +*- +*/ +/* Local Variables: */ + int naxis; /* No. of axes in WcsMap */ + int mm; /* Loop count */ + int mxpar; /* Max number of parameters allowed for the axis */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Find the number of axes in the WcsMap. */ + naxis = astGetNin( this ); + +/* Report an error if the object has been cloned (i.e. has a reference + count that is greater than one). */ + if( astGetRefCount( this ) > 1 ) { + astError( AST__IMMUT, "astSet(%s): Projection parameter values " + "within the supplied %s cannot be changed because the %s has " + "been cloned (programming error).", status, + astGetClass(this), astGetClass(this), astGetClass(this) ); + +/* Validate the axis index. */ + } else if( i < 0 || i >= naxis ){ + astError( AST__AXIIN, "astSetPV(%s): Axis index (%d) is invalid in " + "attribute PV%d_%d - it should be in the range 1 to %d.", + status, astGetClass( this ), i + 1, i + 1, m, naxis ); + +/* Validate the parameter index. */ + } else { + mxpar = astGetPVMax( this, i ); + if( m < 0 || m > mxpar ){ + astError( AST__AXIIN, "astSetPV(%s): Parameter index (%d) is invalid " + "in attribute PV%d_%d for a \"%s\" projection - it should be " + "in the range 0 to %d.", status, astGetClass( this ), m, i + 1, m, + FindPrjData( this->type, status )->ctype, mxpar ); + +/* If the dynamic arrays used to hold the parameters have not yet been + created, create them now, and store pointers to them in the WcsMap + structure. */ + } else { + if( !this->np || !this->p ) { + this->np = (int *) astMalloc( sizeof(int)*naxis ); + this->p = (double **) astMalloc( sizeof(double *)*naxis ); + if( astOK ) { + for( mm = 0; mm < naxis; mm++ ) { + this->np[ mm ] = 0; + this->p[ mm ] = NULL; + } + } + +/* Release the dynamic arrays if an error has occurred. */ + if( !astOK ) FreePV( this, status ); + + } + } + +/* Check we can use the arrays. */ + if( astOK ) { + +/* Ensure the dynamic array used to hold parameter values for the + specified axis is big enough to hold the specified parameter. */ + this->p[ i ] = (double *) astGrow( (void *) this->p[ i ], + m + 1, sizeof(double) ); + +/* Check we can use this array. */ + if( astOK ) { + +/* Store the supplied value in the relevant element of this array. */ + this->p[ i ][ m ] = val; + +/* If the array was extended to hold this parameter... */ + if( this->np[ i ] <= m ) { + +/* Fill any other new elements in this array with AST__BAD */ + for( mm = this->np[ i ]; mm < m; mm++ ) this->p[ i ][ mm ] = AST__BAD; + +/* Remember the new array size. */ + this->np[ i ] = m + 1; + } + } + } + } + +/* Re-initialize the values stored in the "AstPrjPrm" structure. */ + InitPrjPrm( this, status ); +} + +static void SetTPNTan( AstWcsMap *this, int val, int *status ) { +/* +*+ +* Name: +* astSetTPNTan + +* Purpose: +* Set the value of a TPNTan attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* void astSetTPNTan( AstWcsMap *this, int val ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function stores a value for the TPNTan attribute and updates +* the projection parameters used by WCSLIB accordingly. + +* Parameters: +* this +* A pointer to the WcsMap. +* val +* New attribute value. + +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store the new value. */ + this->tpn_tan = ( val != 0 ); + +/* Re-initialize the values stored in the "AstPrjPrm" structure. */ + InitPrjPrm( this, status ); +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* WcsMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a WcsMap's attributes. + +* Parameters: +* this +* Pointer to the WcsMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWcsMap *this; /* Pointer to the WcsMap structure */ + int i; /* Axis index */ + int m; /* Projection parameter index */ + int len; /* Length os supplied string */ + int nc; /* No. of characters read by astSscanf */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointer to the WcsMap structure. */ + this = (AstWcsMap *) this_object; + +/* Obtain the length of the attrib string. */ + len = strlen( attrib ); + +/* Check the attribute name and test the appropriate attribute. */ + + +/* ProjP(i). */ +/* --------- */ + if ( nc = 0, ( 1 == astSscanf( attrib, "projp(%d)%n", &m, &nc ) ) + && ( nc >= len ) ) { + result = astTestPV( this, astGetWcsAxis( this, 1 ), m ); + +/* PV. */ +/* --- */ + } else if ( nc = 0, ( 2 == astSscanf( attrib, "pv%d_%d%n", &i, &m, &nc ) ) + && ( nc >= len ) ) { + result = astTestPV( this, i - 1, m ); + +/* If the name is not recognised, test if it matches any of the + read-only attributes of this class. If it does, then return + zero. */ + } else if ( !strcmp( attrib, "wcstype" ) || + !strcmp( attrib, "natlat" ) || + !strcmp( attrib, "natlon" ) || + ( nc = 0, ( 1 == astSscanf( attrib, "wcsaxis(%d)%n", &i, &nc ) ) + && ( nc >= len ) ) ) { + result = 0; + +/* If the attribute is still not recognised, pass it on to the parent + method for further interpretation. */ + } else { + result = (*parent_testattrib)( this_object, attrib, status ); + } + +/* Return the result, */ + return result; +} + +static int TestPV( AstWcsMap *this, int i, int m, int *status ) { +/* +*+ +* Name: +* astTestPV + +* Purpose: +* Test a PVi_m attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* int astTestPV( AstWcsMap *this, int i, int m ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns 1 if a specified member of the PV attribute +* array is currently set, and 0 otherwise. + +* Parameters: +* this +* A pointer to the WcsMap. +* i +* Zero based axis index. +* m +* Zero based parameter index. + +* Returned Value: +* 1 if the attribute is set, 0 otherwise. + +*- +*/ +/* Local Variables: */ + int ret; + int npar; + int mxpar; + +/* Initialise */ + ret = 0; + +/* Check the global error status. */ + if ( !astOK ) return ret; + +/* Validate the axis index. */ + if( i < 0 || i >= astGetNin( this ) ){ + astError( AST__AXIIN, "astTestPV(%s): Axis index (%d) is invalid in " + "attribute PV%d_%d - it should be in the range 1 to %d.", + status, astGetClass( this ), i + 1, i + 1, m, astGetNin( this ) ); + +/* Find the maximum number of parameters allowed for the axis. */ + } else { + mxpar = astGetPVMax( this, i ); + +/* Ignore unused parameters. */ + if( m < 0 || m > mxpar ){ + +/* See if the parameter is currently set. */ + } else if( this->np && this->p ){ + npar = this->np[ i ]; + if( m < npar && this->p[ i ] ){ + ret = ( this->p[ i ][ m ] != AST__BAD ); + } + } + } + + return ret; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +*+ +* Name: +* Transform + +* Purpose: +* Apply a WcsMap to a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* WcsMap member function (over-rides the astTransform method inherited +* from the Mapping class). + +* Description: +* This function takes a WcsMap and a set of points encapsulated in a +* PointSet and transforms the points using the requested projection. + +* Parameters: +* this +* Pointer to the WcsMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - Assuming the WcsMap has not been inverted, the forward mapping +* transforms spherical (longitude,latitude) pairs into Cartesian (x,y) +* pairs. Longitude and latitude values are given and returned in radians, +* and no restrictions are imposed on their ranges. X and Y values are +* also given and returned in radians, with no restrictions imposed on +* their ranges. +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the WcsMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*- +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstWcsMap *map; /* Pointer to WcsMap to be applied */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + int i; /* Axis count */ + int latax; /* Latitude axis index */ + int lonax; /* Longitude axis index */ + int npoint; /* Number of points */ + int status_value; /* Status from Map function */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Obtain a pointer to the WcsMap. */ + map = (AstWcsMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points from the input PointSet and obtain pointers + for accessing the input and output coordinate values. */ + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* If a genuine FITS-WCS projection is being performed... */ + if( astGetWcsType( map ) != AST__WCSBAD ){ + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Do the coordinate transformation. */ + lonax = astGetWcsAxis( map, 0 ); + latax = astGetWcsAxis( map, 1 ); + status_value = Map( map, forward, npoint, ptr_in[ lonax ], ptr_in[ latax ], + ptr_out[ lonax ], ptr_out[ latax ], status ); + +/* Report an error if the projection type was unrecognised. */ + if ( status_value == 1 ) { + astError( AST__WCSTY, "astTransform(%s): The %s specifies an " + "illegal projection type ('%s').", status, astClass( this ), + astClass( this ), FindPrjData( map->type, status )->desc ); + +/* Report an error if the projection parameters were invalid. */ + } else if ( status_value == 2 ) { + astError( AST__WCSPA, "astTransform(%s): The %s projection " + "parameter values in this %s are unusable.", status, + astClass( this ), FindPrjData( map->type, status )->desc, + astClass( this ) ); + +/* Report an error if required projection parameters were not supplied. */ + } else if ( status_value >= 400 ) { + astError( AST__WCSPA, "astTransform(%s): Required projection " + "parameter PV%d_%d was not supplied for a %s " + "projection.", status, astClass( this ), latax+1, status_value - 400, + FindPrjData( map->type, status )->desc ); + + } else if ( status_value >= 100 ) { + astError( AST__WCSPA, "astTransform(%s): Required projection " + "parameter PV%d_%d was not supplied for a %s " + "projection.", status, astClass( this ), lonax+1, status_value - 100, + FindPrjData( map->type, status )->desc ); + } + +/* Copy the remaining axes (i.e. all axes except the longitude and latitude + axes) from the input to the output. */ + for( i = 0; i < astGetNcoord( in ); i++ ){ + if( ( i != lonax && i != latax ) ){ + (void) memcpy( ptr_out[ i ], ptr_in[ i ], sizeof( double )* + (size_t) npoint ); + } + + } + +/* If there is no FITS-WCS projection, just copy all the axes from input to + output. */ + } else { + for( i = 0; i < astGetNcoord( in ); i++ ){ + (void) memcpy( ptr_out[ i ], ptr_in[ i ], sizeof( double )* + (size_t) npoint ); + } + } + +/* If an error has occurred, attempt to delete the results PointSet. */ + if ( !astOK ) result = astDelete( result ); + +/* Return a pointer to the output PointSet. */ + return result; + +} + +int astWcsPrjType_( const char *ctype, int *status ){ +/* +*+ +* Name: +* astWcsPrjType + +* Purpose: +* Get the integer identifier for a WCSLIB projection given by a FITS +* CTYPE keyword value. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* int astWcsPrjType( const char *ctype ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns the integer identifier for the WCSLIB projection +* specified by the supplied FITS CTYPE keyword value. The returned +* value can be passed to astWcsMap to create a WcsMap which implements +* the corresponding projection. + +* Parameters: +* ctype +* A pointer to the last 4 characters of a FITS CTYPE1 (etc) keyword. + +* Returned Value: +* The integer identifier associated with the projection. + +* Notes: +* - A value of AST__WCSBAD is returned if the projection type is +* unknown, but no error is reported. +*- +*/ + + PrjData *data; + char buffer[81]; + const char *a; + char *b; + +/* Remove leading and trailing blanks from the supplied string. */ + a = ctype; + b = buffer; + while( *a && (b - buffer) < 80 ){ + if( !isspace( (int) *a ) ) { + *(b++) = *a; + } + a++; + } + *b = 0; + +/* Search for the projection in the list of available projectons. */ + data = PrjInfo; + while( data->prj != AST__WCSBAD && strcmp( data->ctype, buffer ) ) data ++; + + return data->prj; +} + +const char *astWcsPrjName_( int type, int *status ){ +/* +*+ +* Name: +* astWcsPrjName + +* Purpose: +* Get the name of a projection given its integer identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* const char *astWcsPrjName( int type ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns a string holding 4 characters which can be +* used as the last 4 characters of a FITS CTYPE keyword value +* describing the WCSLIB projection specified by the supplied type. + +* Parameters: +* type +* The projection type. + +* Returned Value: +* A pointer to a null-terminated const character string holding the +* last 4 CTYPE characters describing the projection. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*- +*/ + + PrjData *data; + data = PrjInfo; + while( data->prj != AST__WCSBAD && data->prj != type ) data ++; + return data->ctype; +} + +const char *astWcsPrjDesc_( int type, int *status ){ +/* +*+ +* Name: +* astWcsPrjDesc + +* Purpose: +* Get a textual description of a projection given its integer +* identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* const char *astWcsPrjDesc( int type ) + +* Class Membership: +* WcsMap protected function + +* Description: +* This function returns a pointer to a string string holding a +* textual description of the specified projection type. + +* Parameters: +* type +* The projection type. + +* Returned Value: +* A pointer to a null-terminated const character string holding the +* projection description. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*- +*/ + + PrjData *data; + data = PrjInfo; + while( data->prj != AST__WCSBAD && data->prj != type ) data ++; + return data->desc; +} + +static void WcsPerm( AstMapping **maps, int *inverts, int iwm, int *status ){ +/* +* Name: +* WcsPerm + +* Purpose: +* Swap a WcsMap and a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* void WcsPerm( AstMapping **maps, int *inverts, int iwm, int *status ) + +* Class Membership: +* WcsMap member function + +* Description: +* A list of two Mappings is supplied containing a WcsMap and a +* PermMap. These Mappings are annulled, and replaced with +* another pair of Mappings consisting of a WcsMap and a PermMap +* in the opposite order. These Mappings are chosen so that their +* combined effect is the same as the original pair of Mappings. + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* iwm +* The index within "maps" of the WcsMap. +* status +* Pointer to the inherited status variable. + +* Notes: +* - All links between input and output axes in the PermMap must +* be bi-directional, but there can be unconnected axes, and there +* need not be the same number of input and output axes. Both +* longitude and latitude axes must be passed by the PermMap. + +*/ + +/* Local Variables: */ + AstPermMap *pm; /* Pointer to the supplied PermMap */ + AstPermMap *newpm; /* Pointer to the returned PermMap */ + AstMapping *newwm; /* Pointer to the returned WcsMap */ + AstWcsMap *wm; /* Pointer to the supplied WcsMap */ + double *consts; /* Pointer to constants array */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int latax; /* Index of latitude axis */ + int lonax; /* Index of longitude axis */ + int npin; /* No. of input axes in supplied PermMap */ + int npout; /* No. of output axes in supplied PermMap */ + int old_pinv; /* Invert value for the supplied PermMap */ + int old_winv; /* Invert value for the supplied WcsMap */ + int type; /* Projection type */ + int done; /* Have Mappings been swapped? */ + int i; /* AXis index */ + double *p; /* Pointer to input position */ + double *q; /* Pointer to output position */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + newpm = NULL; + newwm = NULL; + +/* Store pointers to the supplied WcsMap and the PermMap. */ + wm = (AstWcsMap *) maps[ iwm ]; + pm = (AstPermMap *) maps[ 1 - iwm ]; + +/* Temporarily set the Invert attribute of the supplied PermMap to the + supplied values. */ + old_pinv = astGetInvert( pm ); + astSetInvert( pm, inverts[ 1 - iwm ] ); + old_winv = astGetInvert( wm ); + astSetInvert( wm, inverts[ iwm ] ); + +/* Get the projection type of the supplied WcsMap and the indices of the + longitude and latitude axes. */ + type = astGetWcsType( wm ); + lonax = astGetWcsAxis( wm, 0 ); + latax = astGetWcsAxis( wm, 1 ); + +/* Get the axis permutation and constants arrays representing the + PermMap. Note, no constants are used more than once in the returned + arrays (i.e. duplicate constants are returned in "consts" if more than + one axis uses a given constant). */ + PermGet( pm, &outperm, &inperm, &consts, status ); + + if( astOK ) { + +/* Get the number of input and output axes in the PermMap. */ + npin = astGetNin( pm ); + npout = astGetNout( pm ); + +/* If the lon and lat axes of the WcsMap are unassigned, we return a + UnitMap instead of a WcsMap. + ================================================================ */ + done = 0; + +/* If the PermMap comes after the WcsMap... */ + if( iwm == 0 ) { + if( inperm[ lonax ] < 0 && inperm[ latax ] < 0 ) { + done = 1; + +/* Transform the constant values, using AST__BAD for other axes. */ + p = (double *) astMalloc( sizeof( double )*(size_t) npin ); + q = (double *) astMalloc( sizeof( double )*(size_t) npin ); + if( astOK ) { + for( i = 0; i < npin; i++ ) { + if( inperm[ i ] < 0 ) { + p[ i ] = consts[ -inperm[ i ] - 1 ]; + } else { + p[ i ] = AST__BAD; + } + } + +/* Transform this position using the inverse WcsMap. */ + astTranN( wm, 1, npin, 1, p, 0, npin, 1, q ); + +/* The new PermMap has the same axis permutations as the original, but it + has different constants. */ + for( i = 0; i < npin; i++ ) { + if( inperm[ i ] < 0 ) { + consts[ -inperm[ i ] - 1 ] = q[ i ]; + } + } + + newpm = astPermMap( npin, inperm, npout, outperm, consts, "", status ); + +/* Use a UnitMap instead of the WcsMap. */ + newwm = (AstMapping *) astUnitMap( npout, "", status ); + + } + +/* Free memory */ + p = astFree( p ); + q = astFree( q ); + + } + +/* If the WcsMap comes after the PermMap... */ + } else { + if( outperm[ lonax ] < 0 && outperm[ latax ] < 0 ) { + done = 1; + +/* Transform the constant values, using AST__BAD for other axes. */ + p = (double *) astMalloc( sizeof( double )*(size_t) npout ); + q = (double *) astMalloc( sizeof( double )*(size_t) npout ); + if( astOK ) { + for( i = 0; i < npout; i++ ) { + if( outperm[ i ] < 0 ) { + p[ i ] = consts[ -outperm[ i ] - 1 ]; + } else { + p[ i ] = AST__BAD; + } + } + +/* Transform this position using the forward WcsMap. */ + astTranN( wm, 1, npout, 1, p, 1, npout, 1, q ); + +/* The new PermMap has the same axis permutations as the original, but it + has different constants. */ + for( i = 0; i < npout; i++ ) { + if( outperm[ i ] < 0 ) { + consts[ -outperm[ i ] - 1 ] = q[ i ]; + } + } + + newpm = astPermMap( npin, inperm, npout, outperm, consts, "", status ); + +/* Use a UnitMap instead ofhte WcsMap. */ + newwm = (AstMapping *) astUnitMap( npin, "", status ); + + } + +/* Free memory */ + p = astFree( p ); + q = astFree( q ); + + } + } + +/* If the lon and lat axes of the WcsMap are both assigned, we return a + WcsMap. + ================================================================ */ + if( !done ) { + +/* Create the new WcsMap with permuted longitude and latitude axes. Note, + the private interface to astWcsMap uses 1-based axis indices. */ + if( iwm == 0 ) { + newwm = (AstMapping *) astWcsMap( npout, type, inperm[ lonax ] + 1, + inperm[ latax ] + 1, "", status ); + } else { + newwm = (AstMapping *) astWcsMap( npin, type, outperm[ lonax ] + 1, + outperm[ latax ] + 1, "", status ); + } + +/* Copy any projection parameters which have been set. */ + CopyPV( wm, (AstWcsMap *) newwm, status ); + +/* Set the invert flag. */ + astSetInvert( newwm, inverts[ iwm ] ); + +/* The returned PermMap is a clone of the supplied PermMap */ + newpm = astClone( pm ); + } + +/* Free the axis permutation and constants arrays. */ + outperm = (int *) astFree( (void *) outperm ); + inperm = (int *) astFree( (void *) inperm ); + consts = (double *) astFree( (void *) consts ); + } + +/* Re-instate the original value of the Invert attributes. */ + astSetInvert( pm, old_pinv ); + astSetInvert( wm, old_winv ); + +/* Annul the supplied pointers */ + pm = astAnnul( pm ); + wm = astAnnul( wm ); + +/* Store the returned Mappings. */ + maps[ iwm ] = (AstMapping *) newpm; + inverts[ iwm ] = astGetInvert( newpm ); + maps[ 1 - iwm ] = newwm; + inverts[ 1 - iwm ] = astGetInvert( newwm ); + +/* Return. */ + return; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + + +/* +*att+ +* Name: +* FITSProj + +* Purpose: +* Is this WcsMap used as a FITS-WCS projection? + +* Type: +* Protected attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls how a WcsMap is used when creating a set +* of FITS headers. If the WcsMap is contained within a FrameSet +* that is to be converted into a set of FITS headers using the +c astWrite funtion, +f AST_WRITE routine, +* the WcsMap will be used to define the projection code appended to +* the FITS "CTYPEi" keywords if, and only if, the FITSProj attribute +* is set non-zero in the WcsMap. In order for the conversion to be +* successful, the compound Mapping connecting the base and current +* Frames in the FrameSet must contained one (and only one) WcsMap +* that has a non-zero value for its FITSProj attribute. +* +* The default value is one. + +* Applicability: +* WcsMap +* All Frames have this attribute. +*att- +*/ +astMAKE_CLEAR(WcsMap,FITSProj,fits_proj,-INT_MAX) +astMAKE_GET(WcsMap,FITSProj,int,1,( ( this->fits_proj != -INT_MAX ) ? + this->fits_proj : 1 )) +astMAKE_SET(WcsMap,FITSProj,int,fits_proj,( value != 0 )) +astMAKE_TEST(WcsMap,FITSProj,( this->fits_proj != -INT_MAX )) + +/* +*att+ +* Name: +* TPNTan + +* Purpose: +* Should the TPN projection include a TAN projection? + +* Type: +* Protected attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute controls how a WcsMap with a AST__TPN projection +* type behaves. If the attribute value is non-zero (the default), +* the complete projection is performed as described in the draft +* version of FITS-WCS paper II. If it is zero, then the TAN +* projection from (psi,theta) to (xi,eta) is replaced by a unit +* transformation, so that the WcsMap implements the polynomial +* transformation only, without any preceding TAN projection. +* +* In addition if the TPNTan value is zero, then the WcsMap +* assumes that the input and output values are in degrees rather +* than radians. + +* Applicability: +* WcsMap +* All Frames have this attribute. +*att- +*/ +astMAKE_GET(WcsMap,TPNTan,int,1,( ( this->tpn_tan != -INT_MAX ) ? + this->tpn_tan : 1 )) +astMAKE_TEST(WcsMap,TPNTan,( this->tpn_tan != -INT_MAX )) + +/* ProjP. */ +/* ------ */ +/* +*att++ +* Name: +* ProjP(m) + +* Purpose: +* FITS-WCS projection parameters. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute provides aliases for the PV attributes, which +* specifies the projection parameter values to be used by a WcsMap +* when implementing a FITS-WCS sky projection. ProjP is retained for +* compatibility with previous versions of FITS-WCS and AST. New +* applications should use the PV attibute instead. +* +* Attributes ProjP(0) to ProjP(9) correspond to attributes PV_0 +* to PV_9, where is replaced by the index of the +* latitude axis (given by attribute WcsAxis(2)). See PV for further +* details. +* +* Note, the value of this attribute may changed only if the WcsMap +* has no more than one reference. That is, an error is reported if the +* WcsMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. + +* Applicability: +* WcsMap +* All WcsMaps have this attribute. + +*att-- +*/ + +/* PV. */ +/* --- */ +/* +*att++ +* Name: +* PVi_m + +* Purpose: +* FITS-WCS projection parameters. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point. + +* Description: +* This attribute specifies the projection parameter values to be +* used by a WcsMap when implementing a FITS-WCS sky projection. +* Each PV attribute name should include two integers, i and m, +* separated by an underscore. The axis index is specified +* by i, and should be in the range 1 to 99. The parameter number +* is specified by m, and should be in the range 0 to 99. For +* example, "PV2_1=45.0" would specify a value for projection +* parameter 1 of axis 2 in a WcsMap. +* +* These projection parameters correspond exactly to the values +* stored using the FITS-WCS keywords "PV1_1", "PV1_2", etc. This +* means that projection parameters which correspond to angles must +* be given in degrees (despite the fact that the angular +* coordinates and other attributes used by a WcsMap are in +* radians). +* +* The set of projection parameters used by a WcsMap depends on the +* type of projection, which is determined by its WcsType +* parameter. Most projections either do not require projection +* parameters, or use parameters 1 and 2 associated with the latitude +* axis. You should consult the FITS-WCS paper for details. +* +* Some projection parameters have default values (as defined in +* the FITS-WCS paper) which apply if no explicit value is given. +* You may omit setting a value for these "optional" parameters and the +* default will apply. Some projection parameters, however, have no +* default and a value must be explicitly supplied. This is most +* conveniently +c done using the "options" argument of astWcsMap (q.v.) when a WcsMap +f done using the OPTIONS argument of AST_WCSMAP (q.v.) when a WcsMap +* is first created. An error will result when a WcsMap is used to +* transform coordinates if any of its required projection +* parameters has not been set and lacks a default value. + +* A "get" operation for a parameter which has not been assigned a value +* will return the default value defined in the FITS-WCS paper, or +* AST__BAD if the paper indicates that the parameter has no default. +* A default value of zero is returned for parameters which are not +* accessed by the projection. +* +* Note, the FITS-WCS paper reserves parameters 1 and 2 on the longitude +* axis to hold the native longitude and latitude of the fiducial +* point of the projection, in degrees. The default values for these +* parameters are determined by the projection type. The AST-specific +* TPN projection does not use this convention - all projection +* parameters for both axes are used to represent polynomical correction +* terms, and the native longitude and latitude at the fiducial point may +* not be changed from the default values of zero and 90 degrees. + +* Applicability: +* WcsMap +* All WcsMaps have this attribute. + +* Notes: +* - The value of this attribute may changed only if the WcsMap +* has no more than one reference. That is, an error is reported if the +* WcsMap has been cloned, either by including it within another object +* such as a CmpMap or FrameSet or by calling the +c astClone +f AST_CLONE +* function. +* - If the projection parameter values given for a WcsMap do not +* satisfy all the required constraints (as defined in the FITS-WCS +* paper), then an error will result when the WcsMap is used to +* transform coordinates. +*att-- +*/ + +/* PVMax. */ +/* ------ */ +/* +*att++ +* Name: +* PVMax(i) + +* Purpose: +* Maximum number of FITS-WCS projection parameters. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute specifies the largest legal index for a PV projection +* parameter attached to a specified axis of the WcsMap (i.e. the +* largest legal value for "m" when accessing the "PVi_m" attribute). +* The axis index is specified by i, and should be in the range 1 to 99. +* The value for each axis is determined by the projection type specified +* when the WcsMap +c is first created using astWcsMap and cannot subsequently be +f is first created using AST_WCSMAP and cannot subsequently be +* changed. + +* Applicability: +* WcsMap +* All WcsMaps have this attribute. +*att-- +*/ + +/* WcsType. */ +/* -------- */ +/* +*att++ +* Name: +* WcsType + +* Purpose: +* FITS-WCS projection type. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute specifies which type of FITS-WCS projection will +* be performed by a WcsMap. The value is specified when a WcsMap +c is first created using astWcsMap and cannot subsequently be +f is first created using AST_WCSMAP and cannot subsequently be +* changed. +* +c The values used are represented by macros with names of +f The values used are represented by symbolic constants with names of +* the form "AST__XXX", where "XXX" is the (upper case) 3-character +* code used by the FITS-WCS "CTYPEi" keyword to identify the +* projection. For example, possible values are AST__TAN (for the +* tangent plane or gnomonic projection) and AST__AIT (for the +* Hammer-Aitoff projection). AST__TPN is an exception in that it +* is not part of the FITS-WCS standard (it represents a TAN +* projection with polynomial correction terms as defined in an early +* draft of the FITS-WCS paper). + +* Applicability: +* WcsMap +* All WcsMaps have this attribute. + +* Notes: +* - For a list of available projections, see the FITS-WCS paper. +*att-- +*/ + +/* Type of FITS-WCS projection. Read only. */ +astMAKE_GET(WcsMap,WcsType,int,AST__WCSBAD,this->type) + +/* NatLat. */ +/* ------- */ +/* +*att++ +* Name: +* NatLat + +* Purpose: +* Native latitude of the reference point of a FITS-WCS projection. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point, read-only. + +* Description: +* This attribute gives the latitude of the reference point of the +* FITS-WCS projection implemented by a WcsMap. The value is in +* radians in the "native spherical" coordinate system. This value is +* fixed for most projections, for instance it is PI/2 (90 degrees) +* for all zenithal projections. For some projections (e.g. the conics) +* the value is not fixed, but is specified by parameter one on the +* latitude axis. +* +* FITS-WCS paper II introduces the concept of a "fiducial point" +* which is logical distinct from the projection reference point. +* It is easy to confuse the use of these two points. The fiducial +* point is the point which has celestial coordinates given by the +* CRVAL FITS keywords. The native spherical coordinates for this point +* default to the values of the NatLat and NatLon, but these defaults +* mey be over-ridden by values stored in the PVi_j keywords. Put +* another way, the CRVAL keywords will by default give the celestial +* coordinates of the projection reference point, but may refer to +* some other point if alternative native longitude and latitude values +* are provided through the PVi_j keywords. +* +* The NatLat attribute is read-only. + +* Applicability: +* WcsMap +* All WcsMaps have this attribute. + +* Notes: +* - A default value of AST__BAD is used if no latitude value is available. +*att-- +*/ + +/* +*att++ +* Name: +* NatLon + +* Purpose: +* Native longitude of the reference point of a FITS-WCS projection. + +* Type: +* Public attribute. + +* Synopsis: +* Floating point, read-only. + +* Description: +* This attribute gives the longitude of the reference point of the +* FITS-WCS projection implemented by a WcsMap. The value is in +* radians in the "native spherical" coordinate system, and will +* usually be zero. See the description of attribute NatLat for further +* information. +* +* The NatLon attribute is read-only. + +* Applicability: +* WcsMap +* All WcsMaps have this attribute. + +* Notes: +*att-- +*/ + +/* WcsAxis. */ +/* -------- */ +/* +*att++ +* Name: +* WcsAxis(lonlat) + +* Purpose: +* FITS-WCS projection axes. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the indices of the longitude and latitude +* coordinates of the FITS-WCS projection within the coordinate +* space used by a WcsMap. These indices are defined when the +c WcsMap is first created using astWcsMap and cannot +f WcsMap is first created using AST_WCSMAP and cannot +* subsequently be altered. +* +* If "lonlat" is 1, the index of the longitude axis is +* returned. Otherwise, if it is 2, the index of the latitude axis +* is returned. + +* Applicability: +* WcsMap +* All WcsMaps have this attribute. +*att-- +*/ + +/* Index of the latitude or longitude axis. */ +MAKE_GET(WcsAxis,int,0,this->wcsaxis[ axis ],2) + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for WcsMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for WcsMap objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the +* projection parameter values associated with the input WcsMap. +*/ + + +/* Local Variables: */ + AstWcsMap *in; /* Pointer to input WcsMap */ + AstWcsMap *out; /* Pointer to output WcsMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output WcsMaps. */ + in = (AstWcsMap *) objin; + out = (AstWcsMap *) objout; + +/* Allocate memory to hold the projection parameters within the AstPrjPrm + structure used by WCSLIB. */ + (out->params).p = astMalloc( astSizeOf( (in->params).p ) ); + (out->params).p2 = astMalloc( astSizeOf( (in->params).p2 ) ); + +/* Copy the projection parameter information. */ + CopyPV( in, out, status ); + + return; + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for WcsMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for WcsMap objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstWcsMap *this; /* Pointer to WcsMap */ + +/* Obtain a pointer to the WcsMap structure. */ + this = (AstWcsMap *) obj; + +/* Free the arrays used to store projection parameters. */ + FreePV( this, status ); + +/* Free memory used to hold the projection parameters within the AstPrjPrm + structure used by WCSLIB. */ + this->params.p = astFree( this->params.p ); + this->params.p2 = astFree( this->params.p2 ); + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for WcsMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the WcsMap class to an output Channel. + +* Parameters: +* this +* Pointer to the WcsMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +#define COMMENT_LEN 150 /* Maximum length of a keyword comment */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstWcsMap *this; /* Pointer to the WcsMap structure */ + char *comment; /* Pointer to comment string */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char comment_buff[ COMMENT_LEN + 1 ]; /* Buffer for keyword comment */ + const PrjData *prjdata; /* Information about the projection */ + double dval; /* Double precision value */ + int axis; /* Zero based axis index */ + int i; /* Axis index */ + int m; /* Parameter index */ + int ival; /* Integer value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the WcsMap structure. */ + this = (AstWcsMap *) this_object; + +/* Write out values representing the instance variables for the + WcsMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. Note, all read-only attributes are considered to be set. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* WcsType. */ +/* -------- */ + ival = GetWcsType( this, status ); + prjdata = FindPrjData( ival, status ); + (void) sprintf( comment_buff, "%s projection", prjdata->desc ); + comment_buff[ 0 ] = toupper( comment_buff[ 0 ] ); + astWriteString( channel, "Type", 1, 1, prjdata->ctype + 1, comment_buff ); + +/* FITSProj */ +/* -------- */ + set = TestFITSProj( this, status ); + ival = set ? GetFITSProj( this, status ) : astGetFITSProj( this ); + astWriteInt( channel, "FitsPrj", set, 0, ival, + ival ? "Defines the FITS-WCS projection" : + "Does not define the FITS-WCS projection" ); + +/* TPNTan */ +/* ------ */ + set = TestTPNTan( this, status ); + ival = set ? GetTPNTan( this, status ) : astGetTPNTan( this ); + astWriteInt( channel, "TpnTan", set, 0, ival, + ival ? "Include TAN projection in TPN mapping" : + "Exclude TAN projection from TPN mapping" ); + +/* PVi_m. */ +/* ------ */ + for( i = 0; i < astGetNin( this ); i++ ){ + if( this->np ) { + for( m = 0; m < this->np[ i ]; m++ ){ + set = TestPV( this, i, m, status ); + if( set ) { + dval = set ? GetPV( this, i, m, status ) : astGetPV( this, i, m ); + (void) sprintf( buff, "PV%d_%d", i + 1, m ); + (void) sprintf( comment_buff, "Projection parameter %d for axis %d", m, i + 1 ); + astWriteDouble( channel, buff, set, 0, dval, comment_buff ); + } + } + } + } + +/* WcsAxis(axis). */ +/* -------------- */ + for( axis = 0; axis < 2; axis++ ){ + ival = GetWcsAxis( this, axis, status ); + (void) sprintf( buff, "WcsAx%d", axis + 1 ); + if( axis == 0 ) { + comment = "Index of celestial longitude axis"; + } else { + comment = "Index of celestial latitude axis"; + } + astWriteInt( channel, buff, (axis!=ival), 0, ival + 1, comment ); + } + +/* Note, the "params" component of the AstWcsMap structure is not written out + because it can be re-generated from the other components. */ + +/* Return. */ + return; + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAWcsMap and astCheckWcsMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(WcsMap,Mapping) +astMAKE_CHECK(WcsMap) + +AstWcsMap *astWcsMap_( int ncoord, int type, int lonax, int latax, + const char *options, int *status, ...){ +/* +*++ +* Name: +c astWcsMap +f AST_WCSMAP + +* Purpose: +* Create a WcsMap. + +* Type: +* Public function. + +* Synopsis: +c #include "wcsmap.h" +c AstWcsMap *astWcsMap( int ncoord, int type, int lonax, int latax, +c const char *options, ... ) +f RESULT = AST_WCSMAP( NCOORD, TYPE, LONAX, LATAX, OPTIONS, STATUS ) + +* Class Membership: +* WcsMap constructor. + +* Description: +* This function creates a new WcsMap and optionally initialises its +* attributes. +* +* A WcsMap is used to represent sky coordinate projections as +* described in the (draft) FITS world coordinate system (FITS-WCS) +* paper by E.W. Griesen and M. Calabretta (A & A, in preparation). +* This paper defines a set of functions, or sky projections, which +* transform longitude-latitude pairs representing spherical +* celestial coordinates into corresponding pairs of Cartesian +* coordinates (and vice versa). +* +* A WcsMap is a specialised form of Mapping which implements these +* sky projections and applies them to a specified pair of coordinates. +* All the projections in the FITS-WCS paper are supported, plus the now +* deprecated "TAN with polynomial correction terms" projection which +* is refered to here by the code "TPN". Using the FITS-WCS terminology, +* the transformation is between "native spherical" and "projection +* plane" coordinates. These coordinates may, optionally, be embedded in +* a space with more than two dimensions, the remaining coordinates being +* copied unchanged. Note, however, that for consistency with other AST +* facilities, a WcsMap handles coordinates that represent angles +* in radians (rather than the degrees used by FITS-WCS). +* +* The type of FITS-WCS projection to be used and the coordinates +* (axes) to which it applies are specified when a WcsMap is first +* created. The projection type may subsequently be determined +* using the WcsType attribute and the coordinates on which it acts +* may be determined using the WcsAxis(lonlat) attribute. +* +* Each WcsMap also allows up to 100 "projection parameters" to be +* associated with each axis. These specify the precise form of the +* projection, and are accessed using PVi_m attribute, where "i" is +* the integer axis index (starting at 1), and m is an integer +* "parameter index" in the range 0 to 99. The number of projection +* parameters required by each projection, and their meanings, are +* dependent upon the projection type (most projections either do not +* use any projection parameters, or use parameters 1 and 2 associated +* with the latitude axis). Before creating a WcsMap you should consult +* the FITS-WCS paper for details of which projection parameters are +* required, and which have defaults. When creating the WcsMap, you must +* explicitly set values for all those required projection parameters +* which do not have defaults defined in this paper. + +* Parameters: +c ncoord +f NCOORD = INTEGER (Given) +* The number of coordinate values for each point to be +* transformed (i.e. the number of dimensions of the space in +* which the points will reside). This must be at least 2. The +* same number is applicable to both input and output points. +c type +f TYPE = INTEGER (Given) +* The type of FITS-WCS projection to apply. This should be +c given using a macro value such as AST__TAN (for a tangent +f given as a symbolic value such as AST__TAN (for a tangent +* plane projection), where the characters following the double +* underscore give the projection type code (in upper case) as +* used in the FITS-WCS "CTYPEi" keyword. You should consult the +* FITS-WCS paper for a list of the available projections. The +* additional code of AST__TPN can be supplied which represents a +* TAN projection with polynomial correction terms as defined in an +* early draft of the FITS-WCS paper. +c lonax +f LONAX = INTEGER (Given) +* The index of the longitude axis. This should lie in the range +c 1 to "ncoord". +f 1 to NCOORD. +c latax +f LATAX = INTEGER (Given) +* The index of the latitude axis. This should lie in the range +c 1 to "ncoord" and be distinct from "lonax". +f 1 to NCOORD and be distinct from LONAX. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new WcsMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new WcsMap. The syntax used is identical to that for the +f AST_SET routine. +* +* If the sky projection to be implemented requires projection +* parameter values to be set, then this should normally be done +* here via the PVi_m attribute (see the "Examples" +* section). Setting values for these parameters is mandatory if +* they do not have default values (as defined in the FITS-WCS +* paper). +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astWcsMap() +f AST_WCSMAP = INTEGER +* A pointer to the new WcsMap. + +* Examples: +c wcsmap = astWcsMap( 2, AST__MER, 1, 2, "" ); +f WCSMAP = AST_WCSMAP( 2, AST__MER, 1, 2, ' ', STATUS ) +* Creates a WcsMap that implements a FITS-WCS Mercator +* projection on pairs of coordinates, with coordinates 1 and 2 +* representing the longitude and latitude respectively. Note +* that the FITS-WCS Mercator projection does not require any +* projection parameters. +c wcsmap = astWcsMap( 3, AST__COE, 2, 3, "PV3_1=40.0" ); +f WCSMAP = AST_WCSMAP( 3, AST__COE, 2, 3, 'PV3_1=40.0', STATUS ) +* Creates a WcsMap that implements a FITS-WCS conical equal +* area projection. The WcsMap acts on points in a 3-dimensional +* space; coordinates 2 and 3 represent longitude and latitude +* respectively, while the values of coordinate 1 are copied +* unchanged. Projection parameter 1 associatyed with the latitude +* axis (corresponding to FITS keyword "PV3_1") is required and has +* no default, so is set explicitly to 40.0 degrees. Projection +* parameter 2 (corresponding to FITS keyword "PV3_2") is required +* but has a default of zero, so need not be specified. + +* Notes: +* - The forward transformation of a WcsMap converts between +* FITS-WCS "native spherical" and "relative physical" coordinates, +* while the inverse transformation converts in the opposite +* direction. This arrangement may be reversed, if required, by +c using astInvert or by setting the Invert attribute to a non-zero +f using AST_INVERT or by setting the Invert attribute to a non-zero +* value. +* - If any set of coordinates cannot be transformed (for example, +* many projections do not cover the entire celestial sphere), then +* a WcsMap will yield coordinate values of AST__BAD. +* - The validity of any projection parameters given via the PVi_m +c parameter in the "options" string is not checked by this +f parameter in the OPTIONS string is not checked by this +* function. However, their validity is checked when the resulting +* WcsMap is used to transform coordinates, and an error will +* result if the projection parameters do not satisfy all the +* required constraints (as defined in the FITS-WCS paper). +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstWcsMap *new; /* Pointer to new WcsMap */ + va_list args; /* Variable argument list */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise the WcsMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitWcsMap( NULL, sizeof( AstWcsMap ), !class_init, &class_vtab, + "WcsMap", ncoord, type, lonax - 1, latax - 1 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new WcsMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new WcsMap. */ + return new; +} + +AstWcsMap *astWcsMapId_( int ncoord, int type, int lonax, int latax, + const char *options, ... ){ +/* +* Name: +* astWcsMapId_ + +* Purpose: +* Create a WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "wcsmap.h" +* AstWcsMap *astWcsMap_( int ncoord, int type, int lonax, int latax, +* const char *options, ... ) + +* Class Membership: +* WcsMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astWcsMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astWcsMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astWcsMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astWcsMap_. + +* Returned Value: +* The ID value associated with the new WcsMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstWcsMap *new; /* Pointer to new WcsMap */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the WcsMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitWcsMap( NULL, sizeof( AstWcsMap ), !class_init, &class_vtab, + "WcsMap", ncoord, type, lonax - 1, latax - 1 ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new WcsMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new WcsMap. */ + return astMakeId( new ); +} + +AstWcsMap *astInitWcsMap_( void *mem, size_t size, int init, + AstWcsMapVtab *vtab, const char *name, + int ncin, int type, int lonax, int latax, int *status ) { +/* +*+ +* Name: +* astInitWcsMap + +* Purpose: +* Initialise a WcsMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* AstWcsMap *astInitWcsMap( void *mem, size_t size, int init, +* AstWcsMapVtab *vtab, const char *name, +* int ncin, int type, int lonax, int latax ) + +* Class Membership: +* WcsMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new WcsMap object. It allocates memory (if necessary) to accommodate +* the WcsMap plus any additional data associated with the derived class. +* It then initialises a WcsMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a WcsMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the WcsMap is to be initialised. +* This must be of sufficient size to accommodate the WcsMap data +* (sizeof(WcsMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the WcsMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the WcsMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the WcsMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new WcsMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* ncin +* The number of coordinate values per point. +* type +* The type of projection to use, choosen from the list available +* in the FITS celestial coordinates system. These are specified by +* symbolic values such as AST__TAN (specifying a tangent plane +* projection), where the characters following the double underscore +* gives the FITS projection type (in upper case) as used in the FITS +* "CTYPEn" keyword. +* lonax +* The index of the longitude axis. The first axis has index 0. +* latax +* The index of the latitude axis. The first axis has index 0. + +* Returned Value: +* A pointer to the new WcsMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + const PrjData *prjdata; /* Information about the projection */ + AstWcsMap *new; /* Pointer to new WcsMap */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitWcsMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* If a genuine FITS-WCS projection has been specified, check the initialisation + value(s) for validity, reporting an error if necessary. Prefix the error + report with the name of the function and the class of object being + processed. First check that at least two dimensions are to be mapped. */ + if( type != AST__WCSBAD ){ + if ( ncin < 2 ){ + astError( AST__WCSNC, "astInitWcsMap(%s): Too few axes (%d) " + "specified. Must be at least 2.", status, name, ncin ); + +/* Report an error if either the longitude or latitude axes are out of + bounds. */ + } else if ( lonax < 0 || lonax >= ncin ){ + astError( AST__WCSAX, "astInitWcsMap(%s): Specified longitude axis (%d) " + "does not exist within a %d dimensional coordinate system. ", status, + name, lonax + 1, ncin ); + + } else if ( latax < 0 || latax >= ncin ){ + astError( AST__WCSAX, "astInitWcsMap(%s): Specified latitude axis (%d) " + "does not exist within a %d dimensional coordinate system. ", status, + name, latax + 1, ncin ); + +/* Report an error if the longitude or latitude axes are the same. */ + } else if ( lonax == latax ){ + astError( AST__WCSAX, "astInitWcsMap(%s): The same axis (%d) has been " + "given for both the longitude and the latitude axis.", status, name, + lonax + 1 ); + +/* Report an error if projection type is unknown. */ + } else if ( type < 1 || type >= AST__WCSBAD ){ + astError( AST__WCSTY, "astInitWcsMap(%s): Projection type %d is " + "undefined. Projection types must be in the range 1 to %d.", status, + name, type, AST__WCSBAD - 1 ); + } + } + +/* Get a description of the requeste dprojection type. */ + prjdata = FindPrjData( type, status ); + +/* If all the above checks have been passed succesfully... */ + if( astOK ){ + +/* Initialise a Mapping structure (the parent class) as the first component + within the WcsMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstWcsMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + ncin, ncin, 1, 1 ); + + if ( astOK ) { + +/* Initialise the WcsMap data. */ +/* ---------------------------- */ +/* Store the projection type. */ + new->type = type; + +/* Store the "use as FITS-WCS projection" flag. */ + new->fits_proj = -INT_MAX; + +/* Store the "include TAN component in TPN Mapping" flag. */ + new->tpn_tan = -INT_MAX; + +/* Store the axes associated with longitude and latitude. */ + new->wcsaxis[0] = lonax; + new->wcsaxis[1] = latax; + +/* Store NULL pointers for the arrays holding projection parameters. */ + new->p = NULL; + new->np = NULL; + +/* Allocate memory of the right size to hold the maximum number of + projection parameters needed by the projection. */ + new->params.p = astMalloc( sizeof( double ) * (prjdata->mxpar + 1) ); + new->params.p2 = astMalloc( sizeof( double ) * (prjdata->mxpar2 + 1) ); + +/* Initialise the "AstPrjPrm" structure (defined in proj.h). */ + InitPrjPrm( new, status ); + +/* If an error occurred, clean up by deleting the new WcsMap. */ + if ( !astOK ) new = astDelete( new ); + } + } + +/* Return a pointer to the new WcsMap. */ + return new; +} + +AstWcsMap *astLoadWcsMap_( void *mem, size_t size, + AstWcsMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadWcsMap + +* Purpose: +* Load a WcsMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "wcsmap.h" +* AstWcsMap *astLoadWcsMap( void *mem, size_t size, +* AstWcsMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* WcsMap loader. + +* Description: +* This function is provided to load a new WcsMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* WcsMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a WcsMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the WcsMap is to be +* loaded. This must be of sufficient size to accommodate the +* WcsMap data (sizeof(WcsMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the WcsMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the WcsMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstWcsMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new WcsMap. If this is NULL, a pointer +* to the (static) virtual function table for the WcsMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "WcsMap" is used instead. + +* Returned Value: +* A pointer to the new WcsMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +#define KEY_LEN 50 /* Maximum length of a keyword */ + + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +/* Local Variables: */ + const PrjData *prjdata; /* Information about the projection */ + AstWcsMap *new; /* Pointer to the new WcsMap */ + char *text; /* Textual form of an integer value */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + double pv; /* Projection parameter */ + int axis; /* Axis index */ + int i; /* Axis index */ + int m; /* Parameter index */ + int mxpar; /* Maximum number of PVi_m values */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this WcsMap. In this case the + WcsMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstWcsMap ); + vtab = &class_vtab; + name = "WcsMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitWcsMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built WcsMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "WcsMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* In the case of attributes, we first read the "raw" input value, + supplying the "unset" value as the default. If a "set" value is + obtained, we then use the appropriate (private) Set... member + function to validate and set the value properly. Note, this is only + done for read/write attributes, not read-only ones. */ + +/* FITSProj */ +/* -------- */ + new->fits_proj = astReadInt( channel, "fitsprj", -INT_MAX ); + if ( TestFITSProj( new, status ) ) { + SetFITSProj( new, new->fits_proj, status ); + } + +/* TPNTan */ +/* -------- */ + new->tpn_tan = astReadInt( channel, "tpntan", -INT_MAX ); + if ( TestTPNTan( new, status ) ) { + SetTPNTan( new, new->tpn_tan, status ); + } + +/* WcsType */ +/* ------- */ + text = astReadString( channel, "type", " " ); + if( strcmp( text, " " ) ){ + char tmp[ 10 ]; + (void) sprintf( tmp, "-%.8s", text ); + new->type = astWcsPrjType( tmp ); + } else { + new->type = AST__WCSBAD; + } + text = astFree( text ); + prjdata = FindPrjData( new->type, status ); + +/* WcsAxis(axis). */ +/* -------------- */ + for( axis = 0; axis < 2; axis++ ){ + (void) sprintf( buff, "wcsax%d", axis + 1 ); + new->wcsaxis[ axis ] = astReadInt( channel, buff, axis + 1 ) - 1; + } + +/* Initialise the pointers to the projection parameter information. */ + new->p = NULL; + new->np = NULL; + new->params.p = astMalloc( sizeof( double ) * (prjdata->mxpar + 1) ); + new->params.p2 = astMalloc( sizeof( double ) * (prjdata->mxpar2 + 1) ); + +/* Initialise the structure used by WCSLIB to hold intermediate values, + so that the values will be re-calculated on the first invocation of a + mapping function. */ + InitPrjPrm( new, status ); + +/* ProjP(m). */ +/* --------- */ + for( m = 0; m < AST__WCSMX; m++ ){ + (void) sprintf( buff, "projp%d", m ); + pv = astReadDouble( channel, buff, AST__BAD ); + if( pv != AST__BAD ) SetPV( new, new->wcsaxis[ 1 ], m, pv, status ); + } + +/* PVi_m. */ +/* -------*/ + for( i = 0; i < astGetNin( new ); i++ ){ + + if( i == new->wcsaxis[ 0 ] ) { + mxpar = prjdata->mxpar2; + } else if( i == new->wcsaxis[ 1 ] ) { + mxpar = prjdata->mxpar; + } else { + mxpar = 0; + } + + for( m = 0; m <= mxpar; m++ ){ + (void) sprintf( buff, "pv%d_%d", i + 1, m ); + pv = astReadDouble( channel, buff, AST__BAD ); + if( pv != AST__BAD ) SetPV( new, i, m, pv, status ); + } + } + +/* If an error occurred, clean up by deleting the new WcsMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new WcsMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +void astClearPV_( AstWcsMap *this, int i, int m, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,WcsMap,ClearPV))( this, i, m, status ); +} + +void astClearTPNTan_( AstWcsMap *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,WcsMap,ClearTPNTan))( this, status ); +} + +double astGetPV_( AstWcsMap *this, int i, int m, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,WcsMap,GetPV))( this, i, m, status ); +} + +void astSetPV_( AstWcsMap *this, int i, int m, double val, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,WcsMap,SetPV))( this, i, m, val, status ); +} + +void astSetTPNTan_( AstWcsMap *this, int val, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,WcsMap,SetTPNTan))( this, val, status ); +} + +int astTestPV_( AstWcsMap *this, int i, int m, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,WcsMap,TestPV))( this, i, m, status ); +} + +int astIsZenithal_( AstWcsMap *this, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,WcsMap,IsZenithal))( this, status ); +} + +double astGetNatLat_( AstWcsMap *this, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,WcsMap,GetNatLat))( this, status ); +} + +double astGetNatLon_( AstWcsMap *this, int *status ) { + if ( !astOK ) return AST__BAD; + return (**astMEMBER(this,WcsMap,GetNatLon))( this, status ); +} + +int astGetPVMax_( AstWcsMap *this, int i, int *status ) { + if ( !astOK ) return -1; + return (**astMEMBER(this,WcsMap,GetPVMax))( this, i, status ); +} + + + + + + + diff --git a/wcsmap.h b/wcsmap.h new file mode 100644 index 0000000..16ec3a7 --- /dev/null +++ b/wcsmap.h @@ -0,0 +1,591 @@ +#if !defined( WCSMAP_INCLUDED ) /* Include this file only once */ +#define WCSMAP_INCLUDED +/* +*+ +* Name: +* wcsmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the WcsMap class. + +* Invocation: +* #include "wcsmap.h" + +* Description: +* This include file defines the interface to the WcsMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The WcsMap class implements Mappings that transform a pair of +* longitude/latitude values into a pair of projected Cartesian +* coordinates. All the projections included in FITS WCS are included. +* For more information about these projections, see the appropriate +* FITS document. + +* Inheritance: +* The WcsMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* NatLat (double) +* This attribute gives the latitude of the reference point of +* a FITS WCS projection, in the native coordinate system. The value +* returned is in radians. A value of AST__BAD is returned if +* no value is defined. This attribute is read only, and may change +* if new values are assigned to the projection parameters. +* NatLon (double) +* This attribute gives the longitude of the reference point of +* a FITS WCS projection, in the native coordinate system. The value +* returned is in radians. A value of AST__BAD is returned if +* no value is defined. This attribute is read only, and may change +* if new values are assigned to the projection parameters. +* ProjP(i) (double) +* This attribute provides aliases for the PV attributes, which +* specifies the projection parameter values to be used by a WcsMap +* when implementing a FITS-WCS sky projection. ProjP is retained for +* compatibility with previous versions of FITS-WCS and AST. New +* applications should use the PV attibute instead. +* PVj_m (double) +* This attribute gives the parameter values used by a FITS WCS +* projection. The index j is the axis index in the range 1 to 99, and +* the index m is the parameter index in the range 0 to 99. They will +* have the value AST__BAD if undefined. By default, no projection +* parameters are defined. These should be assigned appropriate values +* before using a WcsMap to transform points. +* WcsAxis(lonlat) (int) +* This attribute gives the indices of the longitude and latitude axes +* of a FITS WCS projection within the coordinate system used by a +* WcsMap. If "lonlat" is 1 then the index of the longitude axis is +* returned. If it is 2 the index of the latitude axis is returned. +* The first axis in the coordinate system is axis 1. This is a +* read-only attribute. +* WcsType (int) +* This attribute gives the FITS WCS projection type implemented by a +* WcsMap. Macros giving the integer value associated with supported +* projections are defined. They have the general form "AST__xxx" where +* "xxx" is the 3-character code used to represent the projection in the +* FITS CTYPE keyword. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* astClearAttrib +* Clear an attribute value for a WcsMap. +* astGetAttrib +* Get an attribute value for a WcsMap. +* astSetAttrib +* Set an attribute value for a WcsMap. +* astTestAttrib +* Test if an attribute value has been set for a WcsMap. +* astTransform +* Apply a WcsMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astClearPV +* Clear a PVi_j attribute value for a WcsMap. +* astGetNatLat +* Get the NatLat attribute value for a WcsMap. +* astGetNatLon +* Get the NatLon attribute value for a WcsMap. +* astGetPV +* Get a PVi_j attribute value for a WcsMap. +* astGetWcsAxis +* Get a WcsAxis attribute value for a WcsMap. +* astGetWcsType +* Get the WcsType attribute value for a WcsMap. +* astIsZenithal +* Is the projection zenithal? +* astSetPV +* Set a PVi_j attribute value for a WcsMap. +* astTestPV +* Test if a PVi_j attribute value has been set for a WcsMap. +* astWcsPrjName +* Return the FITS CTYPE keyword value for a given projection type. +* astWcsPrjDesc +* Return a textual description for a given projection type. +* astWcsPrjType +* Return the projection type given a FITS CTYPE keyword value. + +* Other Class Functions: +* Public: +* astIsAWcsMap +* Test class membership. +* astWcsMap +* Create a WcsMap. +* +* Protected: +* astCheckWcsMap +* Validate class membership. +* astInitWcsMap +* Initialise a WcsMap. +* astInitWcsMapVtab +* Initialise the virtual function table for the WcsMap class. +* astLoadWcsMap +* Load a WcsMap. + +* Macros: +* Public: +* AST__WCSMX +* Maximum number of parameters associated with a projection. +* AST__DPI +* 180 degrees in radians. +* AST__DPIBY2 +* 90 degrees in radians. +* AST__DD2R +* Factor for converting degrees to radians. +* AST__DR2D +* Factor for converting radians to degrees. +* AST__AZP +* An integer identifier for the FITS AZP projection. +* AST__TAN +* An integer identifier for the FITS TAN projection. +* AST__SIN +* An integer identifier for the FITS SIN projection. +* AST__STG +* An integer identifier for the FITS STG projection. +* AST__ARC +* An integer identifier for the FITS ARC projection. +* AST__ZPN +* An integer identifier for the FITS ZPN projection. +* AST__ZEA +* An integer identifier for the FITS ZEA projection. +* AST__AIR +* An integer identifier for the FITS AIR projection. +* AST__CYP +* An integer identifier for the FITS CYP projection. +* AST__CAR +* An integer identifier for the FITS CAR projection. +* AST__MER +* An integer identifier for the FITS MER projection. +* AST__CEA +* An integer identifier for the FITS CEA projection. +* AST__COP +* An integer identifier for the FITS COP projection. +* AST__COD +* An integer identifier for the FITS COD projection. +* AST__COE +* An integer identifier for the FITS COE projection. +* AST__COO +* An integer identifier for the FITS COO projection. +* AST__BON +* An integer identifier for the FITS BON projection. +* AST__PCO +* An integer identifier for the FITS PCO projection. +* AST__GLS +* A depracated integer identifier for the FITS SFL projection. +* AST__SFL +* An integer identifier for the FITS SFL projection. +* AST__PAR +* An integer identifier for the FITS PAR projection. +* AST__AIT +* An integer identifier for the FITS AIT projection. +* AST__MOL +* An integer identifier for the FITS MOL projection. +* AST__CSC +* An integer identifier for the FITS CSC projection. +* AST__QSC +* An integer identifier for the FITS QSC projection. +* AST__TSC +* An integer identifier for the FITS TSC projection +* AST__HPX +* An integer identifier for the FITS HPX projection. +* AST__XPH +* An integer identifier for the FITS XPH projection. +* AST__TPN +* An integer identifier for a "TAN with correction terms" projection. +* AST__WCSBAD +* An integer identifier for a "null" projection. +* +* Protected: +* None. + +* Type Definitions: +* Public: +* AstWcsMap +* WcsMap object type. +* +* Protected: +* AstWcsMapVtab +* WcsMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 15-Feb-1996 (DSB): +* Original version. +* 23-MAR-1996 (DSB): +* Support for PointSets with more than 2 axes added. +* 18-NOV-1996 (DSB): +* Updated to include attributes, etc. +* 26-SEP-1997 (DSB): +* Included new protected function, astPrjDesc. +* 11-FEB-2000 (DSB): +* Replaced wcsmap component projp by pointers p and np. +* 20-OCT-2002 (DSB): +* Added astIsZenithal +* 8-JAN-2003 (DSB): +* Added protected astInitWcsMapVtab method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "proj.h" /* Mark Calabretta's WCSLIB library header + file */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros. */ +/* ------- */ +/* Max. number of parameters for a WCS projection */ + +#if defined(astCLASS) || defined(astFORTRAN77) +#define STATUS_PTR status +#else +#define STATUS_PTR astGetStatusPtr +#endif +#define AST__WCSMX 10 + +/* pi: 180 degrees in radians - from SLALIB file slamac.h. */ +#define AST__DPI 3.1415926535897932384626433832795028841971693993751 + +/* pi/2: 90 degrees in radians - from SLALIB file slamac.h. */ +#define AST__DPIBY2 1.5707963267948966192313216916397514420985846996876 + +/* pi/180: degrees to radians - from SLALIB file slamac.h. */ +#define AST__DD2R 0.017453292519943295769236907684886127134428718885417 + +/* 180/pi: radians to degrees - from SLALIB file slamac.h. */ +#define AST__DR2D 57.295779513082320876798154814105170332405472466564 + +/* Projection Types: (note, WCSBAD must be the last in this list) */ + +#define AST__AZP 1 +#define AST__SZP 2 +#define AST__TAN 3 +#define AST__STG 4 +#define AST__SIN 5 +#define AST__ARC 6 +#define AST__ZPN 7 +#define AST__ZEA 8 +#define AST__AIR 9 +#define AST__CYP 10 +#define AST__CEA 11 +#define AST__CAR 12 +#define AST__MER 13 +#define AST__SFL 14 +#define AST__PAR 15 +#define AST__MOL 16 +#define AST__AIT 17 +#define AST__COP 18 +#define AST__COE 19 +#define AST__COD 20 +#define AST__COO 21 +#define AST__BON 22 +#define AST__PCO 23 +#define AST__TSC 24 +#define AST__CSC 25 +#define AST__QSC 26 +#define AST__NCP 27 +#define AST__GLS 28 +#define AST__TPN 29 +#define AST__HPX 30 +#define AST__XPH 31 +#define AST__WCSBAD 32 /* A bad projection type */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* WcsMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstWcsMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + int type; /* Projection type */ + int wcsaxis[2]; /* Indices of lon and lat. axes */ + double **p; /* Pointer to array of projection parameter arrays */ + int *np; /* Pointer to array of projection parameter counts */ + struct AstPrjPrm params; /* WCS structure holding projection + parameters, etc. Defined in proj.h */ + int fits_proj; /* Use as FITS-WCS projection? */ + int tpn_tan; /* Include TAN projection in TPN transformation? */ +} AstWcsMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstWcsMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + double (* GetNatLat)( AstWcsMap *, int * ); + double (* GetNatLon)( AstWcsMap *, int * ); + double (* GetPV)( AstWcsMap *, int, int, int * ); + int (* GetWcsAxis)( AstWcsMap *, int, int * ); + int (* GetWcsType)( AstWcsMap *, int * ); + int (* GetPVMax)( AstWcsMap *, int, int * ); + int (* TestPV)( AstWcsMap *, int, int, int * ); + void (* ClearPV)( AstWcsMap *, int, int, int * ); + void (* SetPV)( AstWcsMap *, int, int, double, int * ); + int (* IsZenithal)( AstWcsMap *, int * ); + + int (* GetFITSProj)( AstWcsMap *, int * ); + int (* TestFITSProj)( AstWcsMap *, int * ); + void (* ClearFITSProj)( AstWcsMap *, int * ); + void (* SetFITSProj)( AstWcsMap *, int, int * ); + + int (* GetTPNTan)( AstWcsMap *, int * ); + int (* TestTPNTan)( AstWcsMap *, int * ); + void (* ClearTPNTan)( AstWcsMap *, int * ); + void (* SetTPNTan)( AstWcsMap *, int, int * ); + +} AstWcsMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within this + class. */ +typedef struct AstWcsMapGlobals { + AstWcsMapVtab Class_Vtab; + int Class_Init; + char GetAttrib_Buff[ 101 ]; +} AstWcsMapGlobals; + +#endif +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(WcsMap) /* Check class membership */ +astPROTO_ISA(WcsMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstWcsMap *astWcsMap_( int, int, int, int, const char *, int *, ...); +#else +AstWcsMap *astWcsMapId_( int, int, int, int, const char *, ... )__attribute__((format(printf,5,6))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstWcsMap *astInitWcsMap_( void *, size_t, int, AstWcsMapVtab *, + const char *, int, int, int, int, int * ); + +/* Vtab initialiser. */ +void astInitWcsMapVtab_( AstWcsMapVtab *, const char *, int * ); + +/* Loader. */ +AstWcsMap *astLoadWcsMap_( void *, size_t, AstWcsMapVtab *, + const char *, AstChannel *, int * ); + +/* Thread-safe initialiser for all global data used by this module. */ +#if defined(THREAD_SAFE) +void astInitWcsMapGlobals_( AstWcsMapGlobals * ); +#endif + +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ + const char *astWcsPrjDesc_( int, int * ); + const char *astWcsPrjName_( int, int * ); + double astGetNatLat_( AstWcsMap *, int * ); + double astGetNatLon_( AstWcsMap *, int * ); + double astGetPV_( AstWcsMap *, int, int, int * ); + int astWcsPrjType_( const char *, int * ); + int astGetWcsAxis_( AstWcsMap *, int, int * ); + int astGetWcsType_( AstWcsMap *, int * ); + int astGetPVMax_( AstWcsMap *, int, int * ); + int astTestPV_( AstWcsMap *, int, int, int * ); + int astIsZenithal_( AstWcsMap *, int * ); + void astClearPV_( AstWcsMap *, int, int, int * ); + void astSetPV_( AstWcsMap *, int, int, double, int * ); + + int astGetFITSProj_( AstWcsMap *, int * ); + int astTestFITSProj_( AstWcsMap *, int * ); + void astClearFITSProj_( AstWcsMap *, int * ); + void astSetFITSProj_( AstWcsMap *, int, int * ); + + int astGetTPNTan_( AstWcsMap *, int * ); + int astTestTPNTan_( AstWcsMap *, int * ); + void astClearTPNTan_( AstWcsMap *, int * ); + void astSetTPNTan_( AstWcsMap *, int, int * ); + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckWcsMap(this) astINVOKE_CHECK(WcsMap,this,0) +#define astVerifyWcsMap(this) astINVOKE_CHECK(WcsMap,this,1) + +/* Test class membership. */ +#define astIsAWcsMap(this) astINVOKE_ISA(WcsMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astWcsMap astINVOKE(F,astWcsMap_) +#else +#define astWcsMap astINVOKE(F,astWcsMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define astInitWcsMap(mem,size,init,vtab,name,ncoord,type,lon,lat) \ +astINVOKE(O,astInitWcsMap_(mem,size,init,vtab,name,ncoord,type,lon,lat,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitWcsMapVtab(vtab,name) astINVOKE(V,astInitWcsMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadWcsMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadWcsMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckWcsMap to validate WcsMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ + +#define astWcsPrjType(ctype) astWcsPrjType_(ctype,STATUS_PTR) +#define astWcsPrjName(type) astWcsPrjName_(type,STATUS_PTR) +#define astWcsPrjDesc(type) astWcsPrjDesc_(type,STATUS_PTR) + +#define astClearPV(this,i,j) \ +astINVOKE(V,astClearPV_(astCheckWcsMap(this),i,j,STATUS_PTR)) +#define astGetPV(this,i,j) \ +astINVOKE(V,astGetPV_(astCheckWcsMap(this),i,j,STATUS_PTR)) +#define astSetPV(this,i,j,par) \ +astINVOKE(V,astSetPV_(astCheckWcsMap(this),i,j,par,STATUS_PTR)) +#define astTestPV(this,i,j) \ +astINVOKE(V,astTestPV_(astCheckWcsMap(this),i,j,STATUS_PTR)) + +#define astClearFITSProj(this) \ +astINVOKE(V,astClearFITSProj_(astCheckWcsMap(this),STATUS_PTR)) +#define astGetFITSProj(this) \ +astINVOKE(V,astGetFITSProj_(astCheckWcsMap(this),STATUS_PTR)) +#define astSetFITSProj(this,value) \ +astINVOKE(V,astSetFITSProj_(astCheckWcsMap(this),value,STATUS_PTR)) +#define astTestFITSProj(this) \ +astINVOKE(V,astTestFITSProj_(astCheckWcsMap(this),STATUS_PTR)) + +#define astClearTPNTan(this) \ +astINVOKE(V,astClearTPNTan_(astCheckWcsMap(this),STATUS_PTR)) +#define astGetTPNTan(this) \ +astINVOKE(V,astGetTPNTan_(astCheckWcsMap(this),STATUS_PTR)) +#define astSetTPNTan(this,value) \ +astINVOKE(V,astSetTPNTan_(astCheckWcsMap(this),value,STATUS_PTR)) +#define astTestTPNTan(this) \ +astINVOKE(V,astTestTPNTan_(astCheckWcsMap(this),STATUS_PTR)) + +#define astGetWcsType(this) \ +astINVOKE(V,astGetWcsType_(astCheckWcsMap(this),STATUS_PTR)) + +#define astGetPVMax(this,i) \ +astINVOKE(V,astGetPVMax_(astCheckWcsMap(this),i,STATUS_PTR)) + +#define astGetNatLat(this) \ +astINVOKE(V,astGetNatLat_(astCheckWcsMap(this),STATUS_PTR)) + +#define astGetNatLon(this) \ +astINVOKE(V,astGetNatLon_(astCheckWcsMap(this),STATUS_PTR)) + +#define astGetWcsAxis(this,index) \ +astINVOKE(V,astGetWcsAxis_(astCheckWcsMap(this),index,STATUS_PTR)) + +#define astIsZenithal(this) \ +astINVOKE(V,astIsZenithal_(astCheckWcsMap(this),STATUS_PTR)) + +#endif +#endif + + + + + diff --git a/wcsmath.h b/wcsmath.h new file mode 100644 index 0000000..1f8977a --- /dev/null +++ b/wcsmath.h @@ -0,0 +1,67 @@ +/*============================================================================= +* +* WCSLIB - an implementation of the FITS WCS proposal. +* Copyright (C) 1995-2002, Mark Calabretta +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Library General Public License as published +* by the Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library +* General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street,Fifth Floor, Boston, MA 02110-1301, USA +* +* Correspondence concerning WCSLIB may be directed to: +* Internet email: mcalabre@atnf.csiro.au +* Postal address: Dr. Mark Calabretta, +* Australia Telescope National Facility, +* P.O. Box 76, +* Epping, NSW, 2121, +* AUSTRALIA +* +* Author: Mark Calabretta, Australia Telescope National Facility +* $Id$ +*============================================================================= +* +* This version of wcstrig.h is based on the version in wcslib-2.9, but has +* been modified in the following ways by the Starlink project (e-mail: +* ussc@star.rl.ac.uk): +* - Changed the name of the WCSLIB_MATH macro to WCSLIB_MATH_INCLUDED +*===========================================================================*/ + +#ifndef WCSLIB_MATH_INCLUDED +#define WCSLIB_MATH_INCLUDED + +#ifdef PI +#undef PI +#endif + +#ifdef D2R +#undef D2R +#endif + +#ifdef R2D +#undef R2D +#endif + +#ifdef SQRT2 +#undef SQRT2 +#endif + +#ifdef SQRT2INV +#undef SQRT2INV +#endif + +#define PI 3.141592653589793238462643 +#define D2R PI/180.0 +#define R2D 180.0/PI +#define SQRT2 1.4142135623730950488 +#define SQRT2INV 1.0/SQRT2 + +#endif /* WCSLIB_MATH_INCLUDED */ diff --git a/wcstrig.c b/wcstrig.c new file mode 100644 index 0000000..d3ba400 --- /dev/null +++ b/wcstrig.c @@ -0,0 +1,189 @@ +/*============================================================================ +* +* WCSLIB - an implementation of the FITS WCS proposal. +* Copyright (C) 1995-2002, Mark Calabretta +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Library General Public License as published +* by the Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library +* General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street,Fifth Floor, Boston, MA 02110-1301, USA +* +* Correspondence concerning WCSLIB may be directed to: +* Internet email: mcalabre@atnf.csiro.au +* Postal address: Dr. Mark Calabretta, +* Australia Telescope National Facility, +* P.O. Box 76, +* Epping, NSW, 2121, +* AUSTRALIA +* +*============================================================================= +* +* This version of wcstrig.c is based on the version in wcslib-2.9, but has +* been modified in the following ways by the Starlink project (e-mail: +* ussc@star.rl.ac.uk): +* - Support for non-ANSI C "const" class removed +* - Changed names of projection functions and degrees trig functions +* to avoid clashes with wcslib. +*============================================================================= +* +* The functions defined herein are trigonometric or inverse trigonometric +* functions which take or return angular arguments in decimal degrees. +* +* $Id$ +*---------------------------------------------------------------------------*/ + +#include +#include "wcsmath.h" +#include "wcstrig.h" + +double astCosd(angle) + +const double angle; + +{ + double resid; + + resid = fabs(fmod(angle,360.0)); + if (resid == 0.0) { + return 1.0; + } else if (resid == 90.0) { + return 0.0; + } else if (resid == 180.0) { + return -1.0; + } else if (resid == 270.0) { + return 0.0; + } + + return cos(angle*D2R); +} + +/*--------------------------------------------------------------------------*/ + +double astSind(angle) + +const double angle; + +{ + double resid; + + resid = fmod(angle-90.0,360.0); + if (resid == 0.0) { + return 1.0; + } else if (resid == 90.0) { + return 0.0; + } else if (resid == 180.0) { + return -1.0; + } else if (resid == 270.0) { + return 0.0; + } + + return sin(angle*D2R); +} + +/*--------------------------------------------------------------------------*/ + +double astTand(angle) + +const double angle; + +{ + double resid; + + resid = fmod(angle,360.0); + if (resid == 0.0 || fabs(resid) == 180.0) { + return 0.0; + } else if (resid == 45.0 || resid == 225.0) { + return 1.0; + } else if (resid == -135.0 || resid == -315.0) { + return -1.0; + } + + return tan(angle*D2R); +} + +/*--------------------------------------------------------------------------*/ + +double astACosd(v) + +const double v; + +{ + if (v >= 1.0) { + if (v-1.0 < WCSTRIG_TOL) return 0.0; + } else if (v == 0.0) { + return 90.0; + } else if (v <= -1.0) { + if (v+1.0 > -WCSTRIG_TOL) return 180.0; + } + + return acos(v)*R2D; +} + +/*--------------------------------------------------------------------------*/ + +double astASind(v) + +const double v; + +{ + if (v <= -1.0) { + if (v+1.0 > -WCSTRIG_TOL) return -90.0; + } else if (v == 0.0) { + return 0.0; + } else if (v >= 1.0) { + if (v-1.0 < WCSTRIG_TOL) return 90.0; + } + + return asin(v)*R2D; +} + +/*--------------------------------------------------------------------------*/ + +double astATand(v) + +const double v; + +{ + if (v == -1.0) { + return -45.0; + } else if (v == 0.0) { + return 0.0; + } else if (v == 1.0) { + return 45.0; + } + + return atan(v)*R2D; +} + +/*--------------------------------------------------------------------------*/ + +double astATan2d(y, x) + +const double x, y; + +{ + if (y == 0.0) { + if (x >= 0.0) { + return 0.0; + } else if (x < 0.0) { + return 180.0; + } + } else if (x == 0.0) { + if (y > 0.0) { + return 90.0; + } else if (y < 0.0) { + return -90.0; + } + } + + return atan2(y,x)*R2D; +} diff --git a/wcstrig.h b/wcstrig.h new file mode 100644 index 0000000..36463bb --- /dev/null +++ b/wcstrig.h @@ -0,0 +1,63 @@ +/*============================================================================= +* +* WCSLIB - an implementation of the FITS WCS proposal. +* Copyright (C) 1995-2002, Mark Calabretta +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Library General Public License as published +* by the Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library +* General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street,Fifth Floor, Boston, MA 02110-1301, USA +* +* Correspondence concerning WCSLIB may be directed to: +* Internet email: mcalabre@atnf.csiro.au +* Postal address: Dr. Mark Calabretta, +* Australia Telescope National Facility, +* P.O. Box 76, +* Epping, NSW, 2121, +* AUSTRALIA +* +* Author: Mark Calabretta, Australia Telescope National Facility +* $Id$ +*============================================================================= +* +* This version of wcstrig.h is based on the version in wcslib-2.9, but has +* been modified in the following ways by the Starlink project (e-mail: +* ussc@star.rl.ac.uk): +* - Support for non-ANSI C prototypes removed +* - Changed the name of the WCSLIB_TRIG macro to WCSLIB_TRIG_INCLUDED +* - Changed names of degrees trig functions to avoid clashes with +* wcslib. +*===========================================================================*/ + +#ifndef WCSLIB_TRIG_INCLUDED +#define WCSLIB_TRIG_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +double astCosd(const double); +double astSind(const double); +double astTand(const double); +double astACosd(const double); +double astASind(const double); +double astATand(const double); +double astATan2d(const double, const double); + +/* Domain tolerance for asin and acos functions. */ +#define WCSTRIG_TOL 1e-10 + +#ifdef __cplusplus +}; +#endif + +#endif /* WCSLIB_TRIG_INCLUDED */ diff --git a/winmap.c b/winmap.c new file mode 100644 index 0000000..5ba97d5 --- /dev/null +++ b/winmap.c @@ -0,0 +1,4389 @@ +/* +*class++ +* Name: +* WinMap + +* Purpose: +* Map one window on to another by scaling and shifting each axis. + +* Constructor Function: +c astWinMap +f AST_WINMAP + +* Description: +* A Winmap is a linear Mapping which transforms a rectangular +* window in one coordinate system into a similar window in another +* coordinate system by scaling and shifting each axis (the window +* edges being parallel to the coordinate axes). +* +* A WinMap is specified by giving the coordinates of two opposite +* corners (A and B) of the window in both the input and output +* coordinate systems. + +* Inheritance: +* The WinMap class inherits from the Mapping class. + +* Attributes: +* The WinMap class does not define any new attributes beyond those +* which are applicable to all Mappings. + +* Functions: +c The WinMap class does not define any new functions beyond those +f The WinMap class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) +* RFWS: R.F. Warren-Smith (Starlink) + +* History: +* 23-OCT-1996 (DSB): +* Original version. +* 4-MAR-1997 (RFWS): +* Tidied public prologues. +* 11-MAR-1997 (DSB): +* Added MapMerge method and associated bits. +* 30-JUN-1997 (DSB): +* Bug fixed which caused the MapMerge method to generate a +* segmentation violation. +* 24-MAR-1998 (RFWS): +* Improved output format from Dump. +* 9-APR-1998 (DSB): +* MapMerge modified to allow merging of WinMaps with ZoomMaps and +* and UnitMaps in parallel. +* 4-SEP-1998 (DSB): +* Improved MapMerge so that WinMaps can change places with a wider +* range of PermMaps, allowing them to approach closer to a Mapping +* with which they can merge. +* 22-FEB-1999 (DSB): +* Corrected logic of MapMerge method to avoid infinite looping. +* 5-MAY-1999 (DSB): +* More corrections to MapMerge: Cleared up errors in the use of the +* supplied invert flags, and corrected logic for deciding which +* neighbouring Mapping to swap with. +* 16-JUL-1999 (DSB): +* Fixed memory leaks in WinMat and MapMerge. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitWinMapVtab +* method. +* 8-SEP-2003 (DSB): +* Allow WinMaps to swap with WcsMaps if possible. +* 10-NOV-2003 (DSB): +* Modified functions which swap a WinMap with another Mapping +* (e.g. WinPerm, etc), to simplify the returned Mappings. +* 23-APR-2004 (DSB): +* Changes to simplification algorithm. +* 1-SEP-2004 (DSB): +* Ensure do1 and do2 are initialised before use in MapMerge. +* 7-SEP-2005 (DSB): +* Take account of the Invert flag when using the soom factor from +* a ZoomMap. +* 14-FEB-2006 (DSB): +* Override astGetObjSize. +* 15-MAR-2006 (DSB): +* Override astEqual. +* 23-AUG-2006 (DSB): +* Correct initialisation of "result" in the Equal function. +* 19-JAN-2007 (DSB): +* Fix memory leak. +* 3-MAY-2013 (DSB): +* Improve simplification by adding check for inverse pairs of +* WinMaps in function WinWin. +* 23-APR-2015 (DSB): +* Improve MapMerge. If a WinMap can merge with its next-but-one +* neighbour, then swap the WinMap with its neighbour, so that +* it is then next its next-but-one neighbour, and then merge the +* two Mappings into a single Mapping. Previously, only the swap +* was performed - not the merger. And the swap was only performed +* if the intervening neighbour could not itself merge. This could +* result in an infinite simplification loop, which was detected by +* CmpMap and and aborted, resulting in no useful simplification. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS WinMap + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory management facilities */ +#include "object.h" /* Base Object class */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "matrixmap.h" /* Linear mappings */ +#include "unitmap.h" /* Unit mappings */ +#include "zoommap.h" /* Zoom mappings */ +#include "permmap.h" /* Axis permutations */ +#include "cmpmap.h" /* Compound mappings */ +#include "wcsmap.h" /* Celestial projections */ +#include "mapping.h" /* Coordinate mappings (parent class) */ +#include "channel.h" /* I/O channels */ +#include "winmap.h" /* Interface definition for this class */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static int (* parent_getobjsize)( AstObject *, int * ); +static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(WinMap) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(WinMap,Class_Init) +#define class_vtab astGLOBAL(WinMap,Class_Vtab) + + +#include + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstWinMapVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstWinMap *astWinMapId_( int, const double [], const double [], + const double [], const double [], const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ + +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstWinMap *WinUnit( AstWinMap *, AstUnitMap *, int, int, int * ); +static AstWinMap *WinWin( AstMapping *, AstMapping *, int, int, int, int * ); +static AstWinMap *WinZoom( AstWinMap *, AstZoomMap *, int, int, int, int, int * ); +static int GetObjSize( AstObject *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static double Rate( AstMapping *, double *, int, int, int * ); +static int CanSwap( AstMapping *, AstMapping *, int, int, int *, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetIsLinear( AstMapping *, int * ); +static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int WinTerms( AstWinMap *, double **, double **, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void PermGet( AstPermMap *, int **, int **, double **, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void WinMat( AstMapping **, int *, int, int * ); +static void WinPerm( AstMapping **, int *, int, int * ); +static void WinWcs( AstMapping **, int *, int, int * ); +static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); + +/* Member functions. */ +/* ================= */ +static int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, + int *simpler, int *status ){ +/* +* Name: +* CanSwap + +* Purpose: +* Determine if two Mappings could be swapped. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2, +* int *simpler, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* This function returns a flag indicating if the pair of supplied +* Mappings could be replaced by an equivalent pair of Mappings from the +* same classes as the supplied pair, but in reversed order. Each pair +* of Mappings is considered to be compunded in series. The supplied +* Mapings are not changed in any way. + +* Parameters: +* map1 +* The Mapping to be applied first. +* map2 +* The Mapping to be applied second. +* inv1 +* The invert flag to use with map1. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* inv2 +* The invert flag to use with map2. +* simpler +* Addresss of a location at which to return a flag indicating if +* the swapped Mappings would be intrinsically simpler than the +* original Mappings. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* 1 if the Mappings could be swapped, 0 otherwise. + +* Notes: +* - One of the supplied pair of Mappings must be a WinMap. +* - A value of 0 is returned if the two Mappings could be merged into +* a single Mapping. +* - A value of 0 is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *nowin; /* Pointer to non-WinMap Mapping */ + AstWinMap *win; /* Pointer to the WinMap */ + const char *class1; /* Pointer to map1 class string */ + const char *class2; /* Pointer to map2 class string */ + const char *nowin_class; /* Pointer to non-WinMap class string */ + double *consts; /* Pointer to constants array */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int axlat; /* Latitude axis in WcsMap */ + int axlon; /* Longitude axis in WcsMap */ + int i; /* Loop count */ + int invert[ 2 ]; /* Original invert flags */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + int ret; /* Returned flag */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Initialise */ + ret = 0; + *simpler = 0; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + invert[ 0 ] = astGetInvert( map1 ); + astSetInvert( map1, inv1 ); + + invert[ 1 ] = astGetInvert( map2 ); + astSetInvert( map2, inv2 ); + +/* Get the classes of the two mappings. */ + class1 = astGetClass( map1 ); + class2 = astGetClass( map2 ); + if( astOK ){ + +/* Get a pointer to the non-WinMap Mapping. */ + if( !strcmp( class1, "WinMap" ) ){ + nowin = map2; + nowin_class = class2; + win = (AstWinMap *) map1; + } else { + nowin = map1; + nowin_class = class1; + win = (AstWinMap *) map2; + } + +/* If it is a MatrixMap, the Mappings can be swapped. */ + if( !strcmp( nowin_class, "MatrixMap" ) ){ + ret = 1; + +/* If it is a WcsMap, the Mappings can be swapped if the WinMap is + equivalent to a unit transformation on the celestial axes of the + WcsMap. */ + } else if( !strcmp( nowin_class, "WcsMap" ) ){ + +/* Get the indices of the celestial coordinates inthe WcsMap. */ + axlat = astGetWcsAxis( (AstWcsMap *) nowin, 1 ); + axlon = astGetWcsAxis( (AstWcsMap *) nowin, 0 ); + +/* Check the shift and scale for these axes. */ + ret = ( win->a[ axlon ] == 0.0 && win->b[ axlon ] == 1.0 && + win->a[ axlat ] == 0.0 && win->b[ axlat ] == 1.0 ); + +/* If it is a PermMap, the Mappings can be swapped so long as all links + between input and output axes in the PermMap are bi-directional. This + does not preclude the existence of unconnected axes, which do not + have links (bi-directional or otherwise). */ + } else if( !strcmp( nowin_class, "PermMap" ) ){ + +/* Get the number of input and output coordinates. */ + nin = astGetNin( nowin ); + nout = astGetNout( nowin ); + +/* We need to know the axis permutation arrays and constants array for + the PermMap. */ + PermGet( (AstPermMap *) nowin, &outperm, &inperm, &consts, status ); + if( astOK ) { + +/* Indicate we can swap with the PermMap. */ + ret = 1; + +/* Check each output axis. If any links between axes are found which are + not bi-directional, indicate that we cannot swap with the PermMap. */ + for( i = 0; i < nout; i++ ){ + if( outperm[ i ] >= 0 && outperm[ i ] < nin ) { + if( inperm[ outperm[ i ] ] != i ) { + ret = 0; + break; + } + } + } + +/* Check each input axis. If any links between axes are found which are + not bi-directional, indicate that we cannot swap with the PermMap. */ + for( i = 0; i < nin; i++ ){ + if( inperm[ i ] >= 0 && inperm[ i ] < nout ) { + if( outperm[ inperm[ i ] ] != i ) { + ret = 0; + break; + } + } + } + +/* If we can swap with the PermMap, the swapped Mappings may be + intrinsically simpler than the original mappings. */ + if( ret ) { + +/* If the PermMap precedes the WinMap, this will be the case if the PermMap + has more outputs than inputs. If the WinMap precedes the PermMap, this + will be the case if the PermMap has more inputs than outputs. */ + *simpler = ( nowin == map1 ) ? nout > nin : nin > nout; + } + +/* Free the axis permutation and constants arrays. */ + outperm = (int *) astFree( (void *) outperm ); + inperm = (int *) astFree( (void *) inperm ); + consts = (double *) astFree( (void *) consts ); + } + } + } + +/* Re-instate the original settings of the Invert attributes for the + supplied MatrixMaps. */ + astSetInvert( map1, invert[ 0 ] ); + astSetInvert( map2, invert[ 1 ] ); + +/* Return the answer. */ + return astOK ? ret : 0; +} + +static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* ClearAttrib + +* Purpose: +* Clear an attribute value for a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* void ClearAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* WinMap member function (over-rides the astClearAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function clears the value of a specified attribute for a +* WinMap, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the WinMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* At the moment the WinMap class has no attributes, so pass it on to the + parent method for further interpretation. */ + (*parent_clearattrib)( this_object, attrib, status ); + +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two WinMaps are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* int Equal( AstObject *this, AstObject *that, int *status ) + +* Class Membership: +* WinMap member function (over-rides the astEqual protected +* method inherited from the astMapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two WinMaps are equivalent. + +* Parameters: +* this +* Pointer to the first Object (a WinMap). +* that +* Pointer to the second Object. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the WinMaps are equivalent, zero otherwise. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWinMap *that; + AstWinMap *this; + double *a_that; + double *a_this; + double *b_that; + double *b_this; + int i; + int nin; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain pointers to the two WinMap structures. */ + this = (AstWinMap *) this_object; + that = (AstWinMap *) that_object; + +/* Check the second object is a WinMap. We know the first is a + WinMap since we have arrived at this implementation of the virtual + function. */ + if( astIsAWinMap( that ) ) { + +/* Get the number of inputs and outputs and check they are the same for both. */ + nin = astGetNin( this ); + if( astGetNin( that ) == nin ) { + +/* Assume the WinMaps are equivalent. */ + result = 1; + +/* Compare the shift and scale terms from both WinMaps ignoring the + setting of the Invert flag for the moment. */ + for( i = 0; i < nin; i++ ) { + if( !astEQUAL( this->a[ i ], that->a[ i ] ) || + !astEQUAL( this->b[ i ], that->b[ i ] ) ) { + result = 0; + break; + } + } + +/* If the scale and shifts are equal, check the Invert flags are equal. */ + if( result ) { + result= ( astGetInvert( this ) == astGetInvert( that ) ); + +/* If the scale and shifts differ, there is still a chance that the + WinMaps may be equivalent if their Invert flags differ. */ + } else if( astGetInvert( this ) != astGetInvert( that ) ) { + +/* Create copies of the scale and shift terms from the two WinMaps, taking + into account the setting of the Invert attribute. Finding the inverted + terms involves arithmetic which introduces rounding errors, so this + test is not as reliable as the above direct comparison of terms. */ + astWinTerms( this, &a_this, &b_this ); + astWinTerms( that, &a_that, &b_that ); + result = 1; + + for( i = 0; i < nin; i++ ) { + if( !astEQUAL( a_this[ i ], a_that[ i ] ) || + !astEQUAL( b_this[ i ], b_that[ i ] ) ) { + result = 0; + break; + } + } + +/* Free resources */ + a_this = astFree( a_this ); + a_that = astFree( a_that ); + b_this = astFree( b_this ); + b_that = astFree( b_that ); + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static int GetIsLinear( AstMapping *this_mapping, int *status ){ +/* +* Name: +* GetIsLinear + +* Purpose: +* Return the value of the IsLinear attribute for a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* void GetIsLinear( AstMapping *this, int *status ) + +* Class Membership: +* WinMap member function (over-rides the protected astGetIsLinear +* method inherited from the Mapping class). + +* Description: +* This function returns the value of the IsLinear attribute for a +* Frame, which is always one. + +* Parameters: +* this +* Pointer to the WinMap. +* status +* Pointer to the inherited status variable. +*/ + return 1; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* WinMap member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied WinMap, +* in bytes. + +* Parameters: +* this +* Pointer to the WinMap. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWinMap *this; /* Pointer to WinMap structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the WinMap structure. */ + this = (AstWinMap *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by thsi class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + result += astTSizeOf( this->a ); + result += astTSizeOf( this->b ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* GetAttrib + +* Purpose: +* Get the value of a specified attribute for a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* const char *GetAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* WinMap member function (over-rides the protected astGetAttrib +* method inherited from the Mapping class). + +* Description: +* This function returns a pointer to the value of a specified +* attribute for a WinMap, formatted as a character string. + +* Parameters: +* this +* Pointer to the WinMap. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the WinMap, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the WinMap. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Constants: */ +#define BUFF_LEN 50 /* Max. characters in result buffer */ + +/* Local Variables: */ + const char *result; /* Pointer value to return */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* At the moment the WinMap class has no attributes, so pass it on to the + parent method for further interpretation. */ + result = (*parent_getattrib)( this_object, attrib, status ); + +/* Return the result. */ + return result; + +/* Undefine macros local to this function. */ +#undef BUFF_LEN +} + +void astInitWinMapVtab_( AstWinMapVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitWinMapVtab + +* Purpose: +* Initialise a virtual function table for a WinMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "winmap.h" +* void astInitWinMapVtab( AstWinMapVtab *vtab, const char *name ) + +* Class Membership: +* WinMap vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the WinMap class. + +* Parameters: +* vtab +* Pointer to the virtual function table. The components used by +* all ancestral classes will be initialised if they have not already +* been initialised. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialize the component of the virtual function table used by the + parent class. */ + astInitMappingVtab( (AstMappingVtab *) vtab, name ); + +/* Store a unique "magic" value in the virtual function table. This + will be used (by astIsAWinMap) to determine if an object belongs + to this class. We can conveniently use the address of the (static) + class_check variable to generate this unique value. */ + vtab->id.check = &class_check; + vtab->id.parent = &(((AstMappingVtab *) vtab)->id); + +/* Initialise member function pointers. */ +/* ------------------------------------ */ +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->WinTerms = WinTerms; + +/* Save the inherited pointers to methods that will be extended, and + replace them with pointers to the new member functions. */ + object = (AstObjectVtab *) vtab; + mapping = (AstMappingVtab *) vtab; + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + + parent_clearattrib = object->ClearAttrib; + object->ClearAttrib = ClearAttrib; + parent_getattrib = object->GetAttrib; + object->GetAttrib = GetAttrib; + parent_setattrib = object->SetAttrib; + object->SetAttrib = SetAttrib; + parent_testattrib = object->TestAttrib; + object->TestAttrib = TestAttrib; + + parent_transform = mapping->Transform; + mapping->Transform = Transform; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + object->Equal = Equal; + mapping->MapMerge = MapMerge; + mapping->MapSplit = MapSplit; + mapping->Rate = Rate; + mapping->GetIsLinear = GetIsLinear; + +/* Declare the class dump, copy and delete functions.*/ + astSetDump( vtab, Dump, "WinMap", "Map one window on to another" ); + astSetCopy( (AstObjectVtab *) vtab, Copy ); + astSetDelete( (AstObjectVtab *) vtab, Delete ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised, and store a pointer to the class + identifier in the base "object" level of the vtab. */ + if( vtab == &class_vtab ) { + class_init = 1; + astSetVtabClassIdentifier( vtab, &(vtab->id) ); + } +} + +static int MapMerge( AstMapping *this, int where, int series, int *nmap, + AstMapping ***map_list, int **invert_list, int *status ) { +/* +* Name: +* MapMerge + +* Purpose: +* Simplify a sequence of Mappings containing a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* int MapMerge( AstMapping *this, int where, int series, int *nmap, +* AstMapping ***map_list, int **invert_list, int *status ) + +* Class Membership: +* WinMap method (over-rides the protected astMapMerge method +* inherited from the Mapping class). + +* Description: +* This function attempts to simplify a sequence of Mappings by +* merging a nominated WinMap in the sequence with its neighbours, +* so as to shorten the sequence if possible. +* +* In many cases, simplification will not be possible and the +* function will return -1 to indicate this, without further +* action. +* +* In most cases of interest, however, this function will either +* attempt to replace the nominated WinMap with a Mapping which it +* considers simpler, or to merge it with the Mappings which +* immediately precede it or follow it in the sequence (both will +* normally be considered). This is sufficient to ensure the +* eventual simplification of most Mapping sequences by repeated +* application of this function. +* +* In some cases, the function may attempt more elaborate +* simplification, involving any number of other Mappings in the +* sequence. It is not restricted in the type or scope of +* simplification it may perform, but will normally only attempt +* elaborate simplification in cases where a more straightforward +* approach is not adequate. + +* Parameters: +* this +* Pointer to the nominated WinMap which is to be merged with +* its neighbours. This should be a cloned copy of the WinMap +* pointer contained in the array element "(*map_list)[where]" +* (see below). This pointer will not be annulled, and the +* WinMap it identifies will not be modified by this function. +* where +* Index in the "*map_list" array (below) at which the pointer +* to the nominated WinMap resides. +* series +* A non-zero value indicates that the sequence of Mappings to +* be simplified will be applied in series (i.e. one after the +* other), whereas a zero value indicates that they will be +* applied in parallel (i.e. on successive sub-sets of the +* input/output coordinates). +* nmap +* Address of an int which counts the number of Mappings in the +* sequence. On entry this should be set to the initial number +* of Mappings. On exit it will be updated to record the number +* of Mappings remaining after simplification. +* map_list +* Address of a pointer to a dynamically allocated array of +* Mapping pointers (produced, for example, by the astMapList +* method) which identifies the sequence of Mappings. On entry, +* the initial sequence of Mappings to be simplified should be +* supplied. +* +* On exit, the contents of this array will be modified to +* reflect any simplification carried out. Any form of +* simplification may be performed. This may involve any of: (a) +* removing Mappings by annulling any of the pointers supplied, +* (b) replacing them with pointers to new Mappings, (c) +* inserting additional Mappings and (d) changing their order. +* +* The intention is to reduce the number of Mappings in the +* sequence, if possible, and any reduction will be reflected in +* the value of "*nmap" returned. However, simplifications which +* do not reduce the length of the sequence (but improve its +* execution time, for example) may also be performed, and the +* sequence might conceivably increase in length (but normally +* only in order to split up a Mapping into pieces that can be +* more easily merged with their neighbours on subsequent +* invocations of this function). +* +* If Mappings are removed from the sequence, any gaps that +* remain will be closed up, by moving subsequent Mapping +* pointers along in the array, so that vacated elements occur +* at the end. If the sequence increases in length, the array +* will be extended (and its pointer updated) if necessary to +* accommodate any new elements. +* +* Note that any (or all) of the Mapping pointers supplied in +* this array may be annulled by this function, but the Mappings +* to which they refer are not modified in any way (although +* they may, of course, be deleted if the annulled pointer is +* the final one). +* invert_list +* Address of a pointer to a dynamically allocated array which, +* on entry, should contain values to be assigned to the Invert +* attributes of the Mappings identified in the "*map_list" +* array before they are applied (this array might have been +* produced, for example, by the astMapList method). These +* values will be used by this function instead of the actual +* Invert attributes of the Mappings supplied, which are +* ignored. +* +* On exit, the contents of this array will be updated to +* correspond with the possibly modified contents of the +* "*map_list" array. If the Mapping sequence increases in +* length, the "*invert_list" array will be extended (and its +* pointer updated) if necessary to accommodate any new +* elements. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* If simplification was possible, the function returns the index +* in the "map_list" array of the first element which was +* modified. Otherwise, it returns -1 (and makes no changes to the +* arrays supplied). + +* Notes: +* - A value of -1 will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstCmpMap *cm; /* Pointer to neighbouring CmpMap */ + AstMapping **maplt; /* New mappings list pointer */ + AstMapping *map2; /* Pointer to replacement Mapping */ + AstMapping *mc[2]; /* Copies of supplied Mappings to swap */ + AstMapping *nc[2]; /* Copies of neighbouring Mappings to merge */ + AstMapping *smc0; /* Simplified Mapping */ + AstMapping *smc1; /* Simplified Mapping */ + AstMapping *simp1; /* Simplified Mapping */ + AstMapping *simp2; /* Simplified Mapping */ + AstMatrixMap *mtr; /* Pointer to replacement MatrixMap */ + AstWinMap *newwm2; /* Second component WinMap */ + AstWinMap *newwm; /* Pointer to replacement WinMap */ + AstWinMap *oldwm; /* Pointer to supplied WinMap */ + const char *class1; /* Pointer to first Mapping class string */ + const char *class2; /* Pointer to second Mapping class string */ + const char *nclass; /* Pointer to neighbouring Mapping class */ + double *a; /* Pointer to zero terms */ + double *b; /* Pointer to scale terms */ + int *invlt; /* New invert flags list pointer */ + int cmlow; /* Is lower neighbour a CmpMap? */ + int diag; /* Is WinMap equivalent to a diagonal matrix? */ + int do1; /* Would a backward swap make a simplification? */ + int do2; /* Would a forward swap make a simplification? */ + int i1; /* Index of first WinMap to merge */ + int i2; /* Index of last WinMap to merge */ + int i; /* Loop counter */ + int ic[2]; /* Copies of supplied invert flags to swap */ + int inc[4]; /* Copies of supplied invert flags to merge */ + int invert; /* Should the inverted Mapping be used? */ + int nin2; /* No. of inputs for second component WinMap */ + int nin; /* Number of coordinates for WinMap */ + int nmapt; /* No. of Mappings in list */ + int nstep1; /* No. of Mappings backwards to next mergable Mapping */ + int nstep2; /* No. of Mappings forward to next mergable Mapping */ + int old_winv; /* original Invert value for supplied WinMap */ + int result; /* Result value to return */ + int ser; /* Are Mappings applied in series? */ + int simpler; /* Is the resulting Mapping simpler than original? */ + int swap; /* Is there an advantage in swapping mappings? */ + int swaphi; /* Can WinMap be swapped with higher neighbour? */ + int swaplo; /* Can WinMap be swapped with lower neighbour? */ + +/* Initialise. */ + result = -1; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + i1 = 0; + i2 = 0; + +/* Get the number of axes for the WinMap. */ + nin = astGetNin( ( *map_list )[ where ] ); + +/* Get a pointer to the WinMap. */ + oldwm = (AstWinMap *) this; + +/* First of all, see if the WinMap can be replaced by a simpler Mapping, + without reference to the neighbouring Mappings in the list. */ +/* ======================================================================*/ +/* If the shift terms in the WinMap are all zero, the WinMap can be + replaced by a diagonal MatrixMap (which is faster to compute). Check the + shift terms. */ + diag = 1; + newwm = (AstWinMap *) ( *map_list )[ where ]; + for( i = 0; i < nin; i++ ){ + if( !astEQUAL( ( newwm->a )[ i ], 0.0 ) ){ + diag = 0; + break; + } + } + +/* If all the shift terms are zero... */ + if( diag ){ + +/* Temporarily set the Invert attribute of the WinMap to the supplied + value. */ + old_winv = astGetInvert( newwm ); + astSetInvert( newwm, ( *invert_list )[ where ] ); + +/* Get a copy of the scale terms from the WinMap. */ + astWinTerms( newwm, NULL, &b ); + +/* Create a diagonal MatrixMap holding the scale terms. */ + mtr = astMatrixMap( nin, nin, 1, b, "", status ); + +/* Restore the Invert attribute of the supplied WinMap. */ + astSetInvert( newwm, old_winv ); + +/* Free the memory used to hold the scale terms. */ + b = (double *) astFree( (void *) b ); + +/* Annul the WinMap pointer in the list and replace it with the MatrixMap + pointer, and indicate that the forward transformation of the returned + MatrixMap should be used. */ + (void) astAnnul( ( *map_list )[ where ] ); + ( *map_list )[ where ] = (AstMapping *) mtr; + ( *invert_list )[ where ] = 0; + +/* Return the index of the first modified element. */ + result = where; + +/* If the WinMap itself could not be simplified, see if it can be merged + with the Mappings on either side of it in the list. */ + } else { + +/* Store the classes of the neighbouring Mappings in the list. */ + class1 = ( where > 0 ) ? astGetClass( ( *map_list )[ where - 1 ] ) : NULL; + class2 = ( where < *nmap - 1 ) ? astGetClass( ( *map_list )[ where + 1 ] ) : NULL; + +/* In series. */ +/* ========== */ + if ( series ) { + +/* We first look to see if the WinMap can be merged with one of its + neighbours, resulting in a reduction of one in the number of Mappings + in the list. WinMaps can only merge directly with another WinMap, a + ZoomMap, or a UnitMap. */ + if( class1 && ( !strcmp( class1, "WinMap" ) || + !strcmp( class1, "ZoomMap" ) || + !strcmp( class1, "UnitMap" ) ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( class2 && ( !strcmp( class2, "WinMap" ) || + !strcmp( class2, "ZoomMap" ) || + !strcmp( class2, "UnitMap" ) ) ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } else { + nclass = NULL; + } + +/* If the WinMap can merge with one of its neighbours, create the merged + Mapping. */ + if( nclass ){ + + if( !strcmp( nclass, "WinMap" ) ){ + newwm = WinWin( ( *map_list )[ i1 ], ( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], + 1, status ); + invert = 0; + + } else if( !strcmp( nclass, "ZoomMap" ) ){ + if( i1 == where ){ + newwm = WinZoom( (AstWinMap *)( *map_list )[ i1 ], + (AstZoomMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], 1, 1, status ); + } else { + newwm = WinZoom( (AstWinMap *)( *map_list )[ i2 ], + (AstZoomMap *)( *map_list )[ i1 ], + ( *invert_list )[ i2 ], ( *invert_list )[ i1 ], 0, 1, status ); + } + invert = 0; + + } else { + newwm = astClone( ( *map_list )[ where ] ); + invert = ( *invert_list )[ where ]; + } + +/* If succesfull... */ + if( astOK ){ + +/* Annul the first of the two Mappings, and replace it with the merged + WinMap. Also set the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = (AstMapping *) newwm; + ( *invert_list )[ i1 ] = invert; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i2 ] ); + for ( i = i2 + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + + } + +/* If one of the neighbours is a (parallel) CmpMap, we convert the WinMap + into an equivalent parallel CmpMap, and then merge this parallel + CmpMap with the neighbouring parallel CmpMap to create a parallel CmpMap + containing two series CmpMaps. */ + } else if( ( class1 && !strcmp( "CmpMap", class1 ) ) || + ( class2 && !strcmp( "CmpMap", class2 ) ) ) { + +/* Identify the WinMap and the CmpMap. */ + if( class1 && !strcmp( "CmpMap", class1 ) ) { + i1 = where - 1; + i2 = where; + cm = (AstCmpMap *) ( *map_list )[ where - 1 ]; + cmlow = 1; + + } else { + i1 = where; + i2 = where + 1; + cm = (AstCmpMap *) ( *map_list )[ where + 1 ]; + cmlow = 0; + + } + +/* Temporarily set the required Invert attributes in the two Mappings. */ + inc[ 0 ] = astGetInvert( ( *map_list )[ i1 ] ); + astSetInvert( ( *map_list )[ i1 ], ( *invert_list )[ i1 ] ); + + inc[ 1 ] = astGetInvert( ( *map_list )[ i2 ] ); + astSetInvert( ( *map_list )[ i2 ], ( *invert_list )[ i2 ] ); + +/* Now get pointers to the scale and zero terms of the nominated WinMap + (these describe the forward transformation, taking into account the + setting of the Invert flag). */ + (void) astWinTerms( oldwm , &a, &b ); + +/* Get pointers to the two components of the parallel CmpMap. */ + astDecompose( cm, mc, mc + 1, &ser, ic, ic + 1 ); + +/* Check component Mappings are combined in parallel. */ + map2 = NULL; + if( astOK && !ser ) { + +/* Temporarily set the required Invert attributes in the two component + Mappings to the indicated values. */ + inc[ 2 ] = astGetInvert( mc[ 0 ] ); + astSetInvert( mc[ 0 ], ic[ 0 ] ); + + inc[ 3 ] = astGetInvert( mc[ 1 ] ); + astSetInvert( mc[ 1 ], ic[ 1 ] ); + +/* Create the first of two corresponding WinMaps, initially with undefined + corners. These could be combined into a parallel CmpMap which would be + equivalent to the nominated WinMap. The number of inputs for each WinMap + is equal to either the number of outputs or inputs of the corresponding + component of the CmpMap, depending on whether the CmpMap is upper or lower + neighbour. */ + nin = cmlow ? astGetNout( mc[ 0 ] ):astGetNin( mc[ 0 ] ); + newwm = astWinMap( nin, NULL, NULL, NULL, NULL, "", status ); + if( astOK ) { + +/* Store the first "nin" scale and zero terms from the nominated WinMap + in the new WinMap. */ + for( i = 0; i < nin; i++ ) { + (newwm->a)[ i ] = a[ i ]; + (newwm->b)[ i ] = b[ i ]; + } + } + +/* Now create the second WinMap in the same way, which transforms the + remaining outputs of the CmpMap. */ + nin2 = cmlow ? astGetNout( mc[ 1 ] ):astGetNin( mc[ 1 ] ); + newwm2 = astWinMap( nin2, NULL, NULL, NULL, NULL, "", status ); + if( astOK ) { + +/* Store the remaining scale and zero terms from the nominated WinMap + in the new WinMap. */ + for( i = 0; i < nin2; i++ ) { + (newwm2->a)[ i ] = a[ i + nin ]; + (newwm2->b)[ i ] = b[ i + nin ]; + } + } + +/* Combine the two corresponding lower component Mappings into a series + CmpMap, and likewise combine the two corresponding upper component + Mappings into a series CmpMap. */ + if( cmlow ) { + nc[ 0 ] = (AstMapping *) astCmpMap( mc[ 0 ], newwm, 1, "", status ); + nc[ 1 ] = (AstMapping *) astCmpMap( mc[ 1 ], newwm2, 1, "", status ); + } else { + nc[ 0 ] = (AstMapping *) astCmpMap( newwm, mc[ 0 ], 1, "", status ); + nc[ 1 ] = (AstMapping *) astCmpMap( newwm2, mc[ 1 ], 1, "", status ); + } + newwm = astAnnul( newwm ); + newwm2 = astAnnul( newwm2 ); + +/* Attempt to simplify each of the two new series CmpMaps. If neither of + them simplify then there is no point in doing the current merger. In fact + it would be dangerous to do so since we may end up in an infinite loop + where the resulting parallel CmpMap gets converted back into the + existing series CmpMap by the CmpMap MapMerge method, and then back + again by this method, etc. */ + simp1 = astSimplify( nc[ 0 ] ); + simp2 = astSimplify( nc[ 1 ] ); + +/* Test if either could be simplified by checking if its pointer value + has changed. */ + simpler = ( simp1 != nc[ 0 ] ) || ( simp2 != nc[ 1 ] ); + +/* If either CmpMap was simplified, then combine the two series CmpMap into + a single parallel CmpMap. */ + if( simpler ) { + map2 = (AstMapping *) astCmpMap( simp1, simp2, 0, "", status ); + } + +/* Re-instate the original Invert attributes in the two component Mappings. */ + astSetInvert( mc[ 0 ], inc[ 2 ] ); + astSetInvert( mc[ 1 ], inc[ 3 ] ); + +/* Free resources. */ + simp1 = astAnnul( simp1 ); + simp2 = astAnnul( simp2 ); + nc[ 0 ] = astAnnul( nc[ 0 ] ); + nc[ 1 ] = astAnnul( nc[ 1 ] ); + + } + +/* Free resources. */ + mc[ 0 ] = astAnnul( mc[ 0 ] ); + mc[ 1 ] = astAnnul( mc[ 1 ] ); + a = astFree( a ); + b = astFree( b ); + +/* Re-instate the original Invert attributes. */ + astSetInvert( ( *map_list )[ i1 ], inc[ 0 ] ); + astSetInvert( ( *map_list )[ i2 ], inc[ 1 ] ); + +/* If the above produced a new Mapping, annul the supplied pointers for + the two merged Mappings, store the pointer for the new merged Mapping, + and shuffle the remaining Mappings down to fill the space left. Nullify + the end slot which is no longer used, reduce the number of Mappings in + the list by 1, and return the index of the first modified Mapping. */ + if( map2 ) { + (void) astAnnul( ( *map_list )[ i1 ] ); + (void) astAnnul( ( *map_list )[ i2 ] ); + ( *map_list )[ i1 ] = map2; + ( *invert_list )[ i1 ] = 0; + for( i = i2 + 1; i < *nmap; i++ ){ + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + ( *map_list )[ *nmap - 1 ] = NULL; + (*nmap)--; + result = i1; + } + +/* If the WinMap could not merge directly with either of its neighbours, + we consider whether it would be worthwhile to swap the WinMap with + either of its neighbours. This can only be done for certain classes + of Mapping (MatrixMap & some PermMaps & WcsMaps), and will usually require both + Mappings to be modified (unless they are commutative). The advantage of + swapping the order of the Mappings is that it may result in the WinMap + being adjacent to a Mapping with which it can merge directly on the next + invocation of this function, thus reducing the number of Mappings + in the list. */ + } else { + +/* Set a flag if we could swap the WinMap with its higher neighbour. "do2" + is returned if swapping the Mappings would simplify either of the + Mappings. */ + if( where + 1 < *nmap ){ + swaphi = CanSwap( ( *map_list )[ where ], + ( *map_list )[ where + 1 ], + ( *invert_list )[ where ], + ( *invert_list )[ where + 1 ], &do2, status ); + } else { + swaphi = 0; + do2 = 0; + } + +/* If so, step through each of the Mappings which follow the WinMap, + looking for a Mapping with which the WinMap could merge directly. Stop + when such a Mapping is found, or if a Mapping is found with which the + WinMap could definitely not swap. Note the number of Mappings which + separate the WinMap from the Mapping with which it could merge (if + any). */ + nstep2 = -1; + if( swaphi ){ + for( i2 = where + 1; i2 < *nmap; i2++ ){ + +/* See if we can merge with this Mapping. If so, note the number of steps + between the two Mappings and leave the loop. */ + nclass = astGetClass( ( *map_list )[ i2 ] ); + if( !strcmp( nclass, "WinMap" ) || + !strcmp( nclass, "ZoomMap" ) || + !strcmp( nclass, "UnitMap" ) ) { + nstep2 = i2 - where - 1; + break; + } + +/* If there is no chance that we can swap with this Mapping, leave the loop + with -1 for the number of steps to indicate that no merging is possible. + WinMaps can swap with MatrixMaps and some PermMaps. */ + if( strcmp( nclass, "MatrixMap" ) && + strcmp( nclass, "WcsMap" ) && + strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Do the same working forward from the WinMap towards the start of the map + list. */ + if( where > 0 ){ + swaplo = CanSwap( ( *map_list )[ where - 1 ], + ( *map_list )[ where ], + ( *invert_list )[ where - 1 ], + ( *invert_list )[ where ], &do1, status ); + } else { + swaplo = 0; + do1 = 0; + } + + nstep1 = -1; + if( swaplo ){ + for( i1 = where - 1; i1 >= 0; i1-- ){ + + nclass = astGetClass( ( *map_list )[ i1 ] ); + if( !strcmp( nclass, "WinMap" ) || + !strcmp( nclass, "ZoomMap" ) || + !strcmp( nclass, "UnitMap" ) ) { + nstep1 = where - 1 - i1; + break; + } + + if( strcmp( nclass, "MatrixMap" ) && + strcmp( nclass, "WcsMap" ) && + strcmp( nclass, "PermMap" ) ) { + break; + } + + } + + } + +/* Choose which neighbour to swap with so that the WinMap moves towards the + nearest Mapping with which it can merge. */ + if( do1 || ( + nstep1 != -1 && ( nstep2 == -1 || nstep2 > nstep1 ) ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + } else if( do2 || nstep2 != -1 ){ + nclass = class2; + i1 = where; + i2 = where + 1; + } else { + nclass = NULL; + } + +/* If there is a target Mapping in the list with which the WinMap could + merge, replace the supplied Mappings with swapped Mappings to bring a + WinMap closer to the target Mapping. */ + if( nclass ){ + +/* Swap the Mappings. */ + if( !strcmp( nclass, "MatrixMap" ) ){ + WinMat( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + + } else if( !strcmp( nclass, "PermMap" ) ){ + WinPerm( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + + } else if( !strcmp( nclass, "WcsMap" ) ){ + WinWcs( (*map_list) + i1, (*invert_list) + i1, where - i1, status ); + } + +/* And then merge them if possible. */ + if( where == i1 && where + 1 < *nmap ) { /* Merging upwards */ + map2 = astClone( (*map_list)[ where + 1 ] ); + nmapt = *nmap - where - 1; + maplt = *map_list + where + 1; + invlt = *invert_list + where + 1; + + (void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + *nmap = where + 1 + nmapt; + + } else if( where - 2 >= 0 ) { /* Merging downwards */ + map2 = astClone( (*map_list)[ where - 2 ] ); + nmapt = *nmap - where + 2; + maplt = *map_list + where - 2 ; + invlt = *invert_list + where - 2; + + (void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + *nmap = where - 2 + nmapt; + } + + result = i1; + +/* If there is no Mapping available for merging, it may still be + advantageous to swap with a neighbour because the swapped Mapping may + be simpler than the original Mappings. For instance, a PermMap may + strip axes of the WinMap leaving only a UnitMap. Also, the two neighbours + may be able to merge. */ + } else if( swaphi || swaplo ) { + +/* Try swapping with each possible neighbour in turn. */ + for( i = 0; i < 2; i++ ) { + +/* Set up the class and pointers for the mappings to be swapped, first + the lower neighbour, then the upper neighbour. */ + if( i == 0 && swaplo ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( i == 1 && swaphi ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } else { + nclass = NULL; + } + +/* If we have a Mapping to swap with... */ + if( nclass ) { + +/* Take copies of the Mapping and Invert flag arrays so we do not change + the supplied values. */ + mc[ 0 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[0] ); + mc[ 1 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[1] ); + ic[ 0 ] = ( (*invert_list) + i1 )[0]; + ic[ 1 ] = ( (*invert_list) + i1 )[1]; + +/* Swap these Mappings. */ + if( !strcmp( nclass, "MatrixMap" ) ){ + WinMat( mc, ic, where - i1, status ); + } else if( !strcmp( nclass, "PermMap" ) ){ + WinPerm( mc, ic, where - i1, status ); + } else if( !strcmp( nclass, "WcsMap" ) ){ + WinWcs( mc, ic, where - i1, status ); + } + +/* See if the two neighbouring Mappings can merge now that the nominated + Mapping is no longer in between them. First get a list of Mapping + pointers containing the two Mappings to be merged, and associated + invert flags. */ + if( i == 0 && where != *nmap - 1 ) { + nc[ 0 ] = astClone( mc[ 1 ] ); + nc[ 1 ] = astClone( (*map_list)[ where + 1 ] ); + inc[ 0 ] = ic[ 1 ]; + inc[ 1 ] = (*invert_list)[ where + 1 ]; + + } else if( i == 1 && where > 0 ) { + nc[ 0 ] = astClone( (*map_list)[ where - 1 ] ); + nc[ 1 ] = astClone( mc[ 0 ] ); + inc[ 0 ] = (*invert_list)[ where - 1 ]; + inc[ 1 ] = ic[ 0 ]; + + } else { + nc[ 0 ] = NULL; + nc[ 1 ] = NULL; + } + +/* If both neighbours are available, use astMapMerge to see if it is + possible to merge the two Mappings. */ + swap = 0; + if( nc[ 0 ] && nc[ 1 ] ) { + nmapt = 2; + maplt = nc; + invlt = inc; + map2 = astClone( nc[ 0 ] ); + swap = astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + if( swap == -1 ) { + map2 = astClone( nc[ 1 ] ); + swap = astMapMerge( map2, 1, series, &nmapt, &maplt, &invlt ); + map2 = astAnnul( map2 ); + } + swap = ( nmapt < 2 ) ? 1 : 0; + } + +/* Free resources. */ + if( nc[ 0 ] ) nc[ 0 ] = astAnnul( nc[ 0 ] ); + if( nc[ 1 ] ) nc[ 1 ] = astAnnul( nc[ 1 ] ); + +/* If the neighbours could not merge, see if either swapped Mapping can + be simplified. */ + if( !swap ) { + smc0 = astSimplify( mc[0] ); + if( smc0 != mc[0] ) { + swap = 1; + } else { + smc1 = astSimplify( mc[1] ); + swap = ( smc1 != mc[1] ); + smc1 = astAnnul( smc1 ); + } + smc0 = astAnnul( smc0 ); + } + +/* If there is some point in swapping the Mappings, swap them in the + supplied lists. Otherwise annul the swapped Mappings. */ + if( swap ) { + (*map_list)[ i1 ] = astAnnul( (*map_list)[ i1 ] ); + (*map_list)[ i2 ] = astAnnul( (*map_list)[ i2 ] ); + (*map_list)[ i1 ] = mc[ 0 ]; + (*map_list)[ i2 ] = mc[ 1 ]; + (*invert_list)[ i1 ] = ic[ 0 ]; + (*invert_list)[ i2 ] = ic[ 1 ]; + result = i1; + break; + + } else { + mc[ 0 ] = astAnnul( mc[ 0 ] ); + mc[ 1 ] = astAnnul( mc[ 1 ] ); + } + } + } + } + } + +/* In parallel. */ +/* ============ */ +/* WinMaps are combined in parallel with neighbouring WinMaps, ZoomMaps and + UnitMaps. */ + } else { + +/* We first look to see if the WinMap can be merged with one of its + neighbours, resulting in a reduction of one in the number of Mappings + in the list. WinMaps can only merge directly with another WinMap, a + ZoomMap, or a UnitMap. */ + if( class1 && ( !strcmp( class1, "WinMap" ) || + !strcmp( class1, "ZoomMap" ) || + !strcmp( class1, "UnitMap" ) ) ){ + nclass = class1; + i1 = where - 1; + i2 = where; + + } else if( class2 && ( !strcmp( class2, "WinMap" ) || + !strcmp( class2, "ZoomMap" ) || + !strcmp( class2, "UnitMap" ) ) ){ + nclass = class2; + i1 = where; + i2 = where + 1; + + } else { + nclass = NULL; + } + +/* If the WinMap can merge with one of its neighbours, create the merged + Mapping. */ + if( nclass ){ + + if( !strcmp( nclass, "WinMap" ) ){ + newwm = WinWin( ( *map_list )[ i1 ], ( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], + 0, status ); + invert = 0; + + } else if( !strcmp( nclass, "ZoomMap" ) ){ + if( i1 == where ){ + newwm = WinZoom( (AstWinMap *)( *map_list )[ i1 ], + (AstZoomMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], ( *invert_list )[ i2 ], 1, 0, status ); + } else { + newwm = WinZoom( (AstWinMap *)( *map_list )[ i2 ], + (AstZoomMap *)( *map_list )[ i1 ], + ( *invert_list )[ i2 ], ( *invert_list )[ i1 ], 0, 0, status ); + } + invert = 0; + + } else { + if( i1 == where ){ + newwm = WinUnit( (AstWinMap *)( *map_list )[ i1 ], + (AstUnitMap *)( *map_list )[ i2 ], + ( *invert_list )[ i1 ], 1, status ); + } else { + newwm = WinUnit( (AstWinMap *)( *map_list )[ i2 ], + (AstUnitMap *)( *map_list )[ i1 ], + ( *invert_list )[ i2 ], 0, status ); + } + invert = 0; + + } + +/* If succesfull... */ + if( astOK ){ + +/* Annul the first of the two Mappings, and replace it with the merged + WinMap. Also set the invert flag. */ + (void) astAnnul( ( *map_list )[ i1 ] ); + ( *map_list )[ i1 ] = (AstMapping *) newwm; + ( *invert_list )[ i1 ] = invert; + +/* Annul the second of the two Mappings, and shuffle down the rest of the + list to fill the gap. */ + (void) astAnnul( ( *map_list )[ i2 ] ); + for ( i = i2 + 1; i < *nmap; i++ ) { + ( *map_list )[ i - 1 ] = ( *map_list )[ i ]; + ( *invert_list )[ i - 1 ] = ( *invert_list )[ i ]; + } + +/* Clear the vacated element at the end. */ + ( *map_list )[ *nmap - 1 ] = NULL; + ( *invert_list )[ *nmap - 1 ] = 0; + +/* Decrement the Mapping count and return the index of the first + modified element. */ + ( *nmap )--; + result = i1; + + } + } + } + } + +/* Return the result. */ + return result; +} + +static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){ +/* +* Name: +* MapSplit + +* Purpose: +* Create a Mapping representing a subset of the inputs of an existing +* WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ) + +* Class Membership: +* WinMap method (over-rides the protected astMapSplit method +* inherited from the Mapping class). + +* Description: +* This function creates a new Mapping by picking specified inputs from +* an existing WinMap. This is only possible if the specified inputs +* correspond to some subset of the WinMap outputs. That is, there +* must exist a subset of the WinMap outputs for which each output +* depends only on the selected WinMap inputs, and not on any of the +* inputs which have not been selected. If this condition is not met +* by the supplied WinMap, then a NULL Mapping is returned. + +* Parameters: +* this +* Pointer to the WinMap to be split (the WinMap is not actually +* modified by this function). +* nin +* The number of inputs to pick from "this". +* in +* Pointer to an array of indices (zero based) for the inputs which +* are to be picked. This array should have "nin" elements. If "Nin" +* is the number of inputs of the supplied WinMap, then each element +* should have a value in the range zero to Nin-1. +* map +* Address of a location at which to return a pointer to the new +* Mapping. This Mapping will have "nin" inputs (the number of +* outputs may be different to "nin"). A NULL pointer will be +* returned if the supplied WinMap has no subset of outputs which +* depend only on the selected inputs. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated array of ints. The number of +* elements in this array will equal the number of outputs for the +* returned Mapping. Each element will hold the index of the +* corresponding output in the supplied WinMap. The array should be +* freed using astFree when no longer needed. A NULL pointer will +* be returned if no output Mapping can be created. + +* Notes: +* - If this function is invoked with the global error status set, +* or if it should fail for any reason, then NULL values will be +* returned as the function value and for the "map" pointer. +*/ + +/* Local Variables: */ + AstWinMap *newwm; /* Pointer to returned WinMap */ + AstWinMap *this; /* Pointer to WinMap structure */ + double *a; /* Pointer to zero terms */ + double *b; /* Pointer to scale terms */ + int *result; /* Pointer to returned array */ + int i; /* Loop count */ + int iin; /* Mapping input index */ + int mnin; /* No. of Mapping inputs */ + int ok; /* Are input indices OK? */ + +/* Initialise */ + result = NULL; + *map = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the WinMap structure. */ + this = (AstWinMap *) this_map; + +/* Allocate memory for the returned array and create a WinMap with the + required number of axes and undefined corners. */ + result = astMalloc( sizeof( int )*(size_t) nin ); + newwm = astWinMap( nin, NULL, NULL, NULL, NULL, "", status ); + *map = (AstMapping *) newwm; + +/* Now get pointers to the scale and zero terms of the supplied WinMap + (these describe the forward transformation, taking into account the + setting of the Invert flag). */ + (void) astWinTerms( this , &a, &b ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Store the required scale and zero terms from the supplied WinMap + in the new WinMap. At the same time check that each axis is valid. */ + mnin = astGetNin( this ); + ok = 1; + for( i = 0; i < nin; i++ ) { + iin = in[ i ]; + if( iin >= 0 && iin < mnin ) { + (newwm->a)[ i ] = a[ iin ]; + (newwm->b)[ i ] = b[ iin ]; + result[ i ] = iin; + } else { + ok = 0; + break; + } + } + +/* If the "in" array contained any invalid values, free the returned + resources. */ + if( !ok ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + } + +/* Free resources. */ + a = astFree( a ); + b = astFree( b ); + +/* Free returned resources if an error has occurred. */ + if( !astOK ) { + result = astFree( result ); + *map = astAnnul( *map ); + } + +/* Return the list of output indices. */ + return result; +} + +static void PermGet( AstPermMap *map, int **outperm, int **inperm, + double **consts, int *status ){ +/* +* Name: +* PermGet + +* Purpose: +* Get the axis permutation and constants array for a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* void PermGet( AstPermMap *map, int **outperm, int **inperm, +* double **const, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* This function returns axis permutation and constants arrays which can +* be used to create a PermMap which is equivalent to the supplied PermMap. + +* Parameters: +* map +* The PermMap. +* outperm +* An address at which to return a popinter to an array of ints +* holding the output axis permutation array. The array should be +* released using astFree when no longer needed. +* inperm +* An address at which to return a popinter to an array of ints +* holding the input axis permutation array. The array should be +* released using astFree when no longer needed. +* consts +* An address at which to return a popinter to an array of doubles +* holding the constants array. The array should be released using +* astFree when no longer needed. +* status +* Pointer to the inherited status variable. + +* Notes: +* - NULL pointers are returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstPointSet *pset1; /* PointSet holding input positions for PermMap */ + AstPointSet *pset2; /* PointSet holding output positions for PermMap */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + double *cnst; /* Pointer to constants array */ + double cn; /* Potential new constant value */ + double ip; /* Potential output axis index */ + double op; /* Potential input axis index */ + int *inprm; /* Pointer to input axis permutation array */ + int *outprm; /* Pointer to output axis permutation array */ + int i; /* Axis count */ + int nc; /* Number of constants stored so far */ + int nin; /* No. of input coordinates for the PermMap */ + int nout; /* No. of output coordinates for the PermMap */ + +/* Initialise. */ + if( outperm ) *outperm = NULL; + if( inperm ) *inperm = NULL; + if( consts ) *consts = NULL; + +/* Check the global error status and the supplied pointers. */ + if ( !astOK || !outperm || !inperm || !consts ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + nc = 0; + +/* Get the number of input and output axes for the supplied PermMap. */ + nin = astGetNin( map ); + nout = astGetNout( map ); + +/* Allocate the memory for the returned arrays. */ + outprm = (int *) astMalloc( sizeof( int )* (size_t) nout ); + inprm = (int *) astMalloc( sizeof( int )* (size_t) nin ); + cnst = (double *) astMalloc( sizeof( double )* (size_t) ( nout + nin ) ); + +/* Returned the pointers to these arrays.*/ + *outperm = outprm; + *inperm = inprm; + *consts = cnst; + +/* Create two PointSets, each holding two points, which can be used for + input and output positions with the PermMap. */ + pset1 = astPointSet( 2, nin, "", status ); + pset2 = astPointSet( 2, nout, "", status ); + +/* Set up the two input positions to be [0,1,2...] and [-1,-1,-1,...]. The + first position is used to enumerate the axes, and the second is used to + check for constant axis values. */ + ptr1 = astGetPoints( pset1 ); + if( astOK ){ + for( i = 0; i < nin; i++ ){ + ptr1[ i ][ 0 ] = ( double ) i; + ptr1[ i ][ 1 ] = -1.0; + } + } + +/* Use the PermMap to transform these positions in the forward direction. */ + (void) astTransform( map, pset1, 1, pset2 ); + +/* Look at the mapped positions to determine the output axis permutation + array. */ + ptr2 = astGetPoints( pset2 ); + if( astOK ){ + +/* No constant axis valeus found yet. */ + nc = 0; + +/* Do each output axis. */ + for( i = 0; i < nout; i++ ){ + +/* If the output axis value is copied from an input axis value, the index + of the appropriate input axis will be in the mapped first position. */ + op = ptr2[ i ][ 0 ]; + +/* If the output axis value is assigned a constant value, the result of + mapping the two different input axis values will be the same. */ + cn = ptr2[ i ][ 1 ]; + if( op == cn ) { + +/* We have found another constant. Store it in the constants array, and + store the index of the constant in the output axis permutation array. */ + cnst[ nc ] = cn; + outprm[ i ] = -( nc + 1 ); + nc++; + +/* If the output axis values are different, then the output axis value + must be copied from the input axis value. */ + } else { + outprm[ i ] = (int) ( op + 0.5 ); + } + } + } + +/* Now do the same thing to determine the input permutation array. */ + if( astOK ){ + for( i = 0; i < nout; i++ ){ + ptr2[ i ][ 0 ] = ( double ) i; + ptr2[ i ][ 1 ] = -1.0; + } + } + + (void) astTransform( map, pset2, 0, pset1 ); + + if( astOK ){ + + for( i = 0; i < nin; i++ ){ + + ip = ptr1[ i ][ 0 ]; + cn = ptr1[ i ][ 1 ]; + if( ip == cn ) { + + cnst[ nc ] = cn; + inprm[ i ] = -( nc + 1 ); + nc++; + + } else { + inprm[ i ] = (int) ( ip + 0.5 ); + } + } + } + +/* Annul the PointSets. */ + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* If an error has occurred, attempt to free the returned arrays. */ + if( !astOK ) { + *outperm = (int *) astFree( (void *) *outperm ); + *inperm = (int *) astFree( (void *) *inperm ); + *consts = (double *) astFree( (void *) *consts ); + } + +/* Return. */ + return; +} + +static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ +/* +* Name: +* Rate + +* Purpose: +* Calculate the rate of change of a Mapping output. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ) + +* Class Membership: +* WinMap member function (overrides the astRate method inherited +* from the Mapping class ). + +* Description: +* This function returns the rate of change of a specified output of +* the supplied Mapping with respect to a specified input, at a +* specified input position. + +* Parameters: +* this +* Pointer to the Mapping to be applied. +* at +* The address of an array holding the axis values at the position +* at which the rate of change is to be evaluated. The number of +* elements in this array should equal the number of inputs to the +* Mapping. +* ax1 +* The index of the Mapping output for which the rate of change is to +* be found (output numbering starts at 0 for the first output). +* ax2 +* The index of the Mapping input which is to be varied in order to +* find the rate of change (input numbering starts at 0 for the first +* input). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The rate of change of Mapping output "ax1" with respect to input +* "ax2", evaluated at "at", or AST__BAD if the value cannot be +* calculated. + +*/ + +/* Local Variables: */ + AstWinMap *map; + double result; + +/* Check inherited status */ + if( !astOK ) return AST__BAD; + +/* Get a pointer to the WinMap structure. */ + map = (AstWinMap *) this; + +/* If the input and output axes are not equal the result is zero. */ + if( ax1 != ax2 ) { + result = 0.0; + +/* Otherwise, return the scale factor for the axis, taking the reciprocal + if the WinMap has been inverted. */ + } else { + result = ( map->b )[ ax1 ]; + if( astGetInvert( map ) ) { + if( result != 0.0 && result != AST__BAD ) { + result = 1.0/result; + } else { + result = AST__BAD; + } + } + } + +/* Return the result. */ + return result; +} + +static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { +/* +* Name: +* SetAttrib + +* Purpose: +* Set an attribute value for a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* void SetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* WinMap member function (over-rides the astSetAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function assigns an attribute value for a WinMap, the +* attribute and its value being specified by means of a string of +* the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in +* lower case with no white space present. The value to the right +* of the "=" should be a suitable textual representation of the +* value to be assigned and this will be interpreted according to +* the attribute's data type. White space surrounding the value is +* only significant for string attributes. + +* Parameters: +* this +* Pointer to the WinMap. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* The WinMap class currently has no attributes, so pass it on to the parent + method for further interpretation. */ + (*parent_setattrib)( this_object, setting, status ); + +} + +static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { +/* +* Name: +* TestAttrib + +* Purpose: +* Test if a specified attribute value is set for a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* int TestAttrib( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* WinMap member function (over-rides the astTestAttrib protected +* method inherited from the Mapping class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of a WinMap's attributes. + +* Parameters: +* this +* Pointer to the WinMap. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* The WinMap class currently has no attributes, so pass it on to the parent + method for further interpretation. */ + result = (*parent_testattrib)( this_object, attrib, status ); + +/* Return the result, */ + return result; +} + +static AstPointSet *Transform( AstMapping *this, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a WinMap to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* WinMap member function (over-rides the astTransform protected +* method inherited from the Mapping class). + +* Description: +* This function takes a WinMap and a set of points encapsulated in a +* PointSet and transforms the points so as to map them into the +* required window. + +* Parameters: +* this +* Pointer to the WinMap. +* in +* Pointer to the PointSet holding the input coordinate data. +* forward +* A non-zero value indicates that the forward coordinate transformation +* should be applied, while a zero value requests the inverse +* transformation. +* out +* Pointer to a PointSet which will hold the transformed (output) +* coordinate values. A NULL value may also be given, in which case a +* new PointSet will be created by this function. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the output (possibly new) PointSet. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +* - The number of coordinate values per point in the input PointSet must +* match the number of coordinates for the WinMap being applied. +* - If an output PointSet is supplied, it must have space for sufficient +* number of points and coordinate values per point to accommodate the +* result. Any excess space will be ignored. +*/ + +/* Local Variables: */ + AstPointSet *result; /* Pointer to output PointSet */ + AstWinMap *map; /* Pointer to WinMap to be applied */ + const char *class; /* Object class */ + double **ptr_in; /* Pointer to input coordinate data */ + double **ptr_out; /* Pointer to output coordinate data */ + double *axin; /* Pointer to next input axis value */ + double *axout; /* Pointer to next output axis value */ + double *a; /* Pointer to next constant term */ + double *b; /* Pointer to next multiplicative term */ + double aa; /* Constant term */ + double bb; /* Multiplicative term */ + int coord; /* Loop counter for coordinates */ + int def; /* Is mapping defined? */ + int ncoord; /* Number of coordinates per point */ + int npoint; /* Number of points */ + int point; /* Loop counter for points */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + aa = 0.0; + bb = 0.0; + +/* Obtain a pointer to the WinMap. */ + map = (AstWinMap *) this; + +/* Apply the parent mapping using the stored pointer to the Transform member + function inherited from the parent Mapping class. This function validates + all arguments and generates an output PointSet if necessary, but does not + actually transform any coordinate values. */ + result = (*parent_transform)( this, in, forward, out, status ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* Determine the numbers of points and coordinates per point from the input + PointSet and obtain pointers for accessing the input and output coordinate + values. */ + ncoord = astGetNcoord( in ); + npoint = astGetNpoint( in ); + ptr_in = astGetPoints( in ); + ptr_out = astGetPoints( result ); + +/* Determine whether to apply the forward or inverse mapping, according to the + direction specified and whether the mapping has been inverted. */ + if ( astGetInvert( map ) ) forward = !forward; + +/* Report an error if the WinMap does not contain any scales or shifts. */ + if( !(map->a && map->b) && astOK ){ + class = astGetClass( this ); + astError( AST__BADWM, "astTransform(%s): The supplied %s does not " + "contain any window information.", status, class, class ); + } + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if( astOK ){ + +/* Store pointers to the shift and scale for the next axis. */ + a = map->a; + b = map->b; + +/* Apply the mapping to each axis. */ + for( coord = 0; coord < ncoord; coord++ ){ + +/* If either the scale or shift is bad indicate that the mapping is + not defined on this axis. */ + if( *a == AST__BAD || *b == AST__BAD ){ + def = 0; + +/* Otherwise, get the scale and offset factors for this axis, taking account of + whether the mapping is inverted or not. If the mapping is undefined, set + the "def" flag to indicate this. */ + } else { + aa = *a; + bb = *b; + + if( forward ){ + def = 1; + + } else if( bb != 0.0 ){ + bb = 1.0/bb; + aa = -aa*bb; + def = 1; + + } else { + def = 0; + } + + } + +/* Store pointers to the first inpout and output values on this axis. */ + axin = ptr_in[ coord ]; + axout = ptr_out[ coord ]; + +/* If the mapping is defined, apply it to the supplied points. */ + if( def ){ + + for( point = 0; point < npoint; point++ ){ + if( *axin != AST__BAD ){ + *(axout++) = aa + bb*(*axin); + } else { + *(axout++) = AST__BAD; + } + axin++; + } + +/* If the mapping is not defined, store bad values on this axis in the + returned points. */ + } else { + for( point = 0; point < npoint; point++ ) *(axout++) = AST__BAD; + } + +/* Point to the scale and shift for the next axis. */ + a++; + b++; + } + + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void WinMat( AstMapping **maps, int *inverts, int iwm, int *status ){ +/* +* Name: +* WinMat + +* Purpose: +* Swap a WinMap and a MatrixMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* void WinMat( AstMapping **maps, int *inverts, int iwm, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* A list of two Mappings is supplied containing a WinMap and a +* MatrixMap. These Mappings are annulled, and replaced with +* another pair of Mappings consisting of a WinMap and a MatrixMap +* in the opposite order. These Mappings are chosen so that their +* combined effect is the same as the original pair of Mappings. +* The scale factors in the returned WinMap are always unity (i.e. +* the differences in scaling get absorbed into the returned +* MatrixMap). + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* iwm +* The index within "maps" of the WinMap. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMatrixMap *m1; /* Pointer to Diagonal scale factor MatrixMap */ + AstMatrixMap *m2; /* Pointer to returned MatrixMap */ + AstMatrixMap *sm2; /* Pointer to simplified returned MatrixMap */ + AstMatrixMap *mm; /* Pointer to the supplied MatrixMap */ + AstPointSet *pset1; /* Shift terms from supplied WinMap */ + AstPointSet *pset2; /* Shift terms for returned WinMap */ + AstWinMap *w1; /* Pointer to the returned WinMap */ + AstWinMap *sw1; /* Pointer to the simplified returned WinMap */ + AstWinMap *wm; /* Pointer to the supplied WinMap */ + double **ptr1; /* Pointer to pset1 data */ + double **ptr2; /* Pointer to pset2 data */ + double *a; /* Array of shift terms from supplied WinMap */ + double *aa; /* Pointer to next shift term */ + double *b; /* Array of scale terms from supplied WinMap */ + double *bb; /* Pointer to next scale term */ + int i; /* Axis count */ + int nin; /* No. of axes in supplied WinMap */ + int nout; /* No. of axes in returned WinMap */ + int old_minv; /* Invert value for the supplied MatrixMap */ + int old_winv; /* Invert value for the supplied WinMap */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Store pointers to the supplied WinMap and the MatrixMap. */ + wm = (AstWinMap *) maps[ iwm ]; + mm = (AstMatrixMap *) maps[ 1 - iwm ]; + +/* Temporarily set the Invert attribute of the supplied Mappings to the + supplied values. */ + old_winv = astGetInvert( wm ); + astSetInvert( wm, inverts[ iwm ] ); + + old_minv = astGetInvert( mm ); + astSetInvert( mm, inverts[ 1 - iwm ] ); + +/* Get copies of the shift and scale terms used by the WinMap. This + also returns the number of axes in the WinMap. */ + nin = astWinTerms( wm, &a, &b ); + +/* Create a diagonal MatrixMap holding the scale factors from the + supplied WinMap. */ + m1 = astMatrixMap( nin, nin, 1, b, "", status ); + +/* Create a PointSet holding a single position given by the shift terms + in the supplied WinMap. */ + pset1 = astPointSet( 1, nin, "", status ); + ptr1 = astGetPoints( pset1 ); + if( astOK ){ + aa = a; + for( i = 0; i < nin; i++ ) ptr1[ i ][ 0 ] = *(aa++); + } + +/* First deal with cases when the WinMap is applied first, followed by + the MatrixMap. */ + if( iwm == 0 ){ + +/* Multiply the diagonal matrix holding the WinMap scale factors by the + supplied matrix. The resulting MatrixMap is the one to return in the + map list. */ + m2 = astMtrMult( m1, mm ); + +/* Transform the position given by the shift terms from the supplied + WinMap using the supplied MatrixMap to get the shift terms for + the returned WinMap. */ + pset2 = astTransform( mm, pset1, 1, NULL ); + +/* Now deal with cases when the MatrixMap is applied first, followed by + the WinMap. */ + } else { + +/* Multiply the supplied MatrixMap by the diagonal matrix holding scale + factors from the supplied WinMap. The resulting MatrixMap is the one to + return in the map list. */ + m2 = astMtrMult( mm, m1 ); + +/* Transform the position given by the shift terms from the supplied + WinMap using the inverse of the returned MatrixMap to get the shift + terms for the returned WinMap. */ + pset2 = astTransform( m2, pset1, 0, NULL ); + + } + +/* Re-instate the original value of the Invert attributes of the supplied + Mappings. */ + astSetInvert( wm, old_winv ); + astSetInvert( mm, old_minv ); + +/* Get pointers to the shift terms for the returned WinMap. */ + ptr2 = astGetPoints( pset2 ); + +/* Create the returned WinMap, initially with undefined corners. The number of + axes in the WinMap must equal the number of shift terms. */ + nout = astGetNcoord( pset2 ); + w1 = astWinMap( nout, NULL, NULL, NULL, NULL, "", status ); + +/* If succesful, store the scale and shift terms in the WinMap. The scale + terms are always unity. */ + if( astOK ){ + bb = w1->b; + aa = w1->a; + for( i = 0; i < nout; i++ ) { + *(bb++) = 1.0; + *(aa++) = ptr2[ i ][ 0 ]; + } + +/* Replace the supplied Mappings and invert flags with the ones found + above. Remember that the order of the Mappings is now swapped */ + (void) astAnnul( maps[ 0 ] ); + (void) astAnnul( maps[ 1 ] ); + + sw1 = astSimplify( w1 ); + w1 = astAnnul( w1 ); + + maps[ 1 - iwm ] = (AstMapping *) sw1; + inverts[ 1 - iwm ] = astGetInvert( sw1 ); + + sm2 = astSimplify( m2 ); + m2 = astAnnul( m2 ); + + maps[ iwm ] = (AstMapping *) sm2; + inverts[ iwm ] = astGetInvert( sm2 ); + + } + +/* Annul the MatrixMap and PointSet holding the scale and shift terms from the + supplied WinMap. */ + m1 = astAnnul( m1 ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + +/* Free the copies of the scale and shift terms from the supplied WinMap. */ + b = (double *) astFree( (void *) b ); + a = (double *) astFree( (void *) a ); + +/* Return. */ + return; +} + +static void WinWcs( AstMapping **maps, int *inverts, int iwm, int *status ){ +/* +* Name: +* WinWcs + +* Purpose: +* Swap a WinMap and a WcsMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* void WinWcs( AstMapping **maps, int *inverts, int iwm, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* A list of two Mappings is supplied containing a WinMap and a +* WcsMap. These Mappings are swapped. + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* iwm +* The index within "maps" of the WinMap. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstMapping *m1; /* Pointer to a Mapping */ + int inv; /* Invert value */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Simply swap the values (the CanSwap function will have checked that + the WcsMap and WinMap can simply be swapped). */ + m1 = maps[ 0 ]; + maps[ 0 ] = maps[ 1 ]; + maps[ 1 ] = m1; + + inv = inverts[ 0 ]; + inverts[ 0 ] = inverts[ 1 ]; + inverts[ 1 ] = inv; + +/* Return. */ + return; +} + +static void WinPerm( AstMapping **maps, int *inverts, int iwm, int *status ){ +/* +* Name: +* WinPerm + +* Purpose: +* Swap a WinMap and a PermMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* void WinPerm( AstMapping **maps, int *inverts, int iwm, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* A list of two Mappings is supplied containing a WinMap and a +* PermMap. These Mappings are annulled, and replaced with +* another pair of Mappings consisting of a WinMap and a PermMap +* in the opposite order. These Mappings are chosen so that their +* combined effect is the same as the original pair of Mappings. + +* Parameters: +* maps +* A pointer to an array of two Mapping pointers. +* inverts +* A pointer to an array of two invert flags. +* iwm +* The index within "maps" of the WinMap. +* status +* Pointer to the inherited status variable. + +* Notes: +* - All links between input and output axes in the PermMap must +* be bi-directional, but there can be unconnected axes, and there +* need not be the same number of input and output axes. + +*/ + +/* Local Variables: */ + AstPermMap *pm; /* Pointer to the supplied PermMap */ + AstPermMap *p1; /* Pointer to the returned PermMap */ + AstPermMap *sp1; /* Pointer to the simplified returned PermMap */ + AstWinMap *w1; /* Pointer to the returned WinMap */ + AstWinMap *sw1; /* Pointer to the simplified returned PermMap */ + AstWinMap *wm; /* Pointer to the supplied WinMap */ + double *a; /* Array of shift terms from supplied WinMap */ + double *aa; /* Pointer to next shift term */ + double *b; /* Array of scale terms from supplied WinMap */ + double *bb; /* Pointer to next scale term */ + double *consts; /* Pointer to constants array */ + double c; /* A constant value */ + int *inperm; /* Pointer to input axis permutation array */ + int *outperm; /* Pointer to output axis permutation array */ + int i; /* Axis count */ + int j; /* Axis index */ + int nin; /* No. of axes in supplied WinMap */ + int npin; /* No. of input axes in supplied PermMap */ + int npout; /* No. of output axes in supplied PermMap */ + int old_pinv; /* Invert value for the supplied PermMap */ + int old_winv; /* Invert value for the supplied WinMap */ + + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Initialise variables to avoid "used of uninitialised variable" + messages from dumb compilers. */ + p1 = NULL; + w1 = NULL; + +/* Store pointers to the supplied WinMap and the PermMap. */ + wm = (AstWinMap *) maps[ iwm ]; + pm = (AstPermMap *) maps[ 1 - iwm ]; + +/* Temporarily set the Invert attribute of the supplied Mappings to the + supplied values. */ + old_winv = astGetInvert( wm ); + astSetInvert( wm, inverts[ iwm ] ); + + old_pinv = astGetInvert( pm ); + astSetInvert( pm, inverts[ 1 - iwm ] ); + +/* Get copies of the shift and scale terms used by the WinMap. This + also returns the number of axes in the WinMap. */ + nin = astWinTerms( wm, &a, &b ); + +/* Get the axis permutation and constants arrays representing the + PermMap. Note, no constants are used more than once in the returned + arrays (i.e. duplicate constants are returned in "consts" if more than + one axis uses a given constant). */ + PermGet( pm, &outperm, &inperm, &consts, status ); + + if( astOK ) { + +/* Get the number of input and output axes in the PermMap. */ + npin = astGetNin( pm ); + npout = astGetNout( pm ); + +/* First consider cases where the WinMap is applied first, followed by the + PermMap. */ + if( iwm == 0 ) { + +/* Create the new WinMap, initially with undefined corners. Its number + of axes will equal the number of output axes of the PermMap. */ + w1 = astWinMap( npout, NULL, NULL, NULL, NULL, "", status ); + +/* Get pointers to the scale and shift terms for the new WinMap. */ + bb = w1->b; + aa = w1->a; + +/* Thinking of the forward CmpMap first, consider each of the output axes of + the PermMap. */ + for( i = 0; i < npout; i++ ){ + +/* If the value for this output axis is derived from an input axis, copy the + scale and shift terms from the corresponding input axis to the new + WinMap. */ + j = outperm[ i ]; + if( j >= 0 && j < nin ) { + aa[ i ] = a[ j ]; + bb[ i ] = b[ j ]; + +/* If this output axis is assigned a constant value, use zero and one for + the shift and scale in order to preserve the constant value produced + by the PermMap. */ + } else { + aa[ i ] = 0.0; + bb[ i ] = 1.0; + } + + } + +/* Now consider the inverse CmpMap. Any constants produced by the inverse + PermMap would previously have been scaled by the inverse WinMap. Since + there will be no inverse WinMap to perform this scaling in the returned + Mappings, we need to change the constant values to be the values after + the scaling which would have been applied by the WinMap. Consider each + of the input axes of the PermMap.*/ + for( i = 0; i < npin; i++ ){ + +/* Skip axes which are not assigned a constant value. */ + if( inperm[ i ] < 0 ) { + +/* Scale the constant term associated with this input axis using the + inverse WinMap unless it is AST__BAD. */ + c = consts[ -inperm[ i ] - 1 ]; + if( c != AST__BAD ) { + + if( a[ i ] != AST__BAD && b[ i ] != AST__BAD && + b[ i ] != 0.0 ) { + consts[ -inperm[ i ] - 1 ] = ( c - a[ i ] )/b[ i ]; + } else { + consts[ -inperm[ i ] - 1 ] = AST__BAD; + } + + } + + } + + } + +/* Now consider cases where the PermMap is applied first, followed by the + WinMap. */ + } else { + +/* Create the new WinMap, initially with undefined corners. Its number + of axes will equal the number of input axes of the PermMap. */ + w1 = astWinMap( npin, NULL, NULL, NULL, NULL, "", status ); + +/* Get pointers to the scale and shift terms for the new WinMap. */ + bb = w1->b; + aa = w1->a; + +/* Thinking first about the inverse WinMap, consider each of the input axes + of the PermMap. */ + for( i = 0; i < npin; i++ ){ + +/* If the value for this input axis is derived from an output axis, copy the + scale and shift terms from the corresponding output axis to the new + WinMap. */ + j = inperm[ i ]; + if( j >= 0 && j < nin ) { + aa[ i ] = a[ j ]; + bb[ i ] = b[ j ]; + +/* If this input axis is assigned a constant value, use zero and one for + the shift and scale in order to preserve the constant value produced + by the PermMap. */ + } else { + aa[ i ] = 0.0; + bb[ i ] = 1.0; + } + + } + +/* Now consider the forward WinMap. Any constants produced by the forward + PermMap would previously have been scaled by the forward WinMap. Since + there will be no forward WinMap to perform this scaling in the returned + Mappings, we need to change the constant values to be the values after + the scaling which would have been applied by the WinMap. Consider each + of the output axes of the PermMap.*/ + for( i = 0; i < npout; i++ ){ + +/* Skip axes which are not assigned a constant value. */ + if( outperm[ i ] < 0 ) { + +/* Scale the constant term associated with this input axis using the + forward WinMap unless it is AST__BAD. */ + c = consts[ -outperm[ i ] - 1 ]; + if( c != AST__BAD ) { + + if( a[ i ] != AST__BAD && b[ i ] != AST__BAD ) { + consts[ -outperm[ i ] - 1 ] = a[ i ] + c*b[ i ]; + } else { + consts[ -outperm[ i ] - 1 ] = AST__BAD; + } + + } + + } + + } + + } + +/* Create a new PermMap (since the constants may have changed). */ + p1 = astPermMap( npin, inperm, npout, outperm, consts, "", status ); + +/* Free the axis permutation and constants arrays. */ + outperm = (int *) astFree( (void *) outperm ); + inperm = (int *) astFree( (void *) inperm ); + consts = (double *) astFree( (void *) consts ); + } + +/* Re-instate the original value of the Invert attributes of the supplied + Mappings. */ + astSetInvert( wm, old_winv ); + astSetInvert( pm, old_pinv ); + +/* Replace the supplied Mappings with the ones created above, swapping the + order. */ + if( astOK ){ + (void) astAnnul( wm ); + (void) astAnnul( pm ); + + sp1 = astSimplify( p1 ); + p1 = astAnnul( p1 ); + + sw1 = astSimplify( w1 ); + w1 = astAnnul( w1 ); + + maps[ iwm ] = (AstMapping *) sp1; + inverts[ iwm ] = 0; + + maps[ 1 - iwm ] = (AstMapping *) sw1; + inverts[ 1 - iwm ] = astGetInvert( sw1 ); + } + +/* Free the copies of the scale and shift terms from the supplied WinMap. */ + b = (double *) astFree( (void *) b ); + a = (double *) astFree( (void *) a ); + +/* Return. */ + return; +} + +static int WinTerms( AstWinMap *this, double **shift, double **scale, int *status ){ +/* +*+ +* Name: +* astWinTerms + +* Purpose: +* Obtain the scale and shift terms used by a WinMap. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "winmap.h" +* int astWinTerms( AstWinMap *this, double **shift, double **scale ) + +* Class Membership: +* WinMap mewthod. + +* Description: +* This function returns copies of the scale and shift terms used by a +* WinMap when transforming points. Each axis of the WinMap has a scale +* term B, and a shift term A, and the transformation of a point is done +* by applying these to each input axis value X in turn, to get the +* output axis value B.X + A. The returned terms take into account the +* current setting of the Invert attribute of the WinMap. + +* Parameters: +* this +* Pointer to the WinMap. +* shift +* The address of a location at which to return a pointer to the +* start of a dynamically allocated array holding the shift terms +* for each axis. +* scale +* The address of a location at which to return a pointer to the +* start of a dynamically allocated array holding the scale terms +* for each axis. + +* Returned Value: +* The number of axes in the WinMap. This is the same as the number of +* elements in the returned arrays. + +* Notes: +* - The returned arrays should be released using astFree when no +* longer needed. +* - NULL pointers can be supplied for "scale" or "shift" if the +* corresponding arrays are not required. +* - A value of zero will be returned, together with NULL pointers +* for "scale" and "shift" if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + double *a; /* Pointer to a copy of the shift term array */ + double *aa; /* Pointer to the next shift term */ + double *b; /* Pointer to a copy of the scale term array */ + double *bb; /* Pointer to the next scale term */ + int i; /* Axis count */ + int result; /* The returned number of axes */ + size_t absize; /* Size of shift and scale arrays */ + +/* Initialise. */ + result = 0; + if( scale ) *scale = NULL; + if( shift ) *shift = NULL; + +/* Check the global status. */ + if ( !astOK ) return result; + +/* Get the number of axes in the WinMap. */ + result = astGetNin( this ); + +/* Create copies of the scale and shift terms from the WinMap. */ + absize = sizeof( double )*(size_t) result; + b = (double *) astStore( NULL, (void *) this->b, absize ); + a = (double *) astStore( NULL, (void *) this->a, absize ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* If the WinMap is inverted, replace the scale and shift terms + by the corresponding values for the inverted mapping. */ + if( astGetInvert( this ) ){ + bb = b; + aa = a; + + for( i = 0; i < result; i++ ){ + if( *aa != AST__BAD && *bb != 0.0 && *bb != AST__BAD ){ + *bb = 1.0/(*bb); + *aa *= -(*bb); + } else { + *bb = AST__BAD; + *aa = AST__BAD; + } + + aa++; + bb++; + + } + } + +/* Store the required pointers, and free arrays which are not required. */ + if( scale ){ + *scale = b; + } else { + b = (double *) astFree( (void *) b ); + } + + if( shift ){ + *shift = a; + } else { + a = (double *) astFree( (void *) a ); + } + + } + +/* If an error has occurred, free the arrays and return zero. */ + if( !astOK ){ + if( scale ) *scale = (double *) astFree( (void *) *scale ); + if( shift ) *shift = (double *) astFree( (void *) *shift ); + result = 0; + } + +/* Return the answer. */ + return result; + +} + +static AstWinMap *WinUnit( AstWinMap *wm, AstUnitMap *um, int winv, + int win1, int *status ){ +/* +* Name: +* WinUnit + +* Purpose: +* Create a WinMap by merging a WinMap and a UnitMap in parallel. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* AstWinMap *WinUnit( AstWinMap *wm, AstUnitMap *um, int winv, int win1, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* This function creates a new WinMap which performs a mapping +* equivalent to applying the two supplied Mappings in parallel in +* the directions specified by the "invert" flag (the Invert +* attribute of the supplied WinMap is ignored). + +* Parameters: +* wm +* A pointer to the WinMap. +* um +* A pointer to the UnitMap. +* winv +* The invert flag to use with wm. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* win1 +* Indicates the order in which the Mappings should be applied. +* +* If win1 is non-zero: +* "wm" applies to the lower axis indices and "um" to the upper +* axis indices. +* +* If win1 is zero: +* "um" applies to the lower axis indices and "wm" to the upper +* axis indices. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new WinMap. + +* Notes: +* - The forward direction of the returned WinMap is equivalent to the +* combined effect of the two supplied Mappings, operating in the +* directions specified by "winv". +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWinMap *result; /* Pointer to output WinMap */ + double *a; /* Pointer to shift term array */ + double *aa; /* Pointer to next shift term */ + double *ar; /* Pointer to next shift term in result */ + double *b; /* Pointer to scale term array */ + double *bb; /* Pointer to next scale term */ + double *br; /* Pointer to next scale term in result */ + int i; /* Axis index */ + int ninw; /* No. of axes in the WinMap */ + int ninu; /* No. of axes in the UnitMap */ + int old_winv; /* Original setting of WinMap Invert attribute */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned pointer. */ + result = NULL; + +/* Temporarily set the Invert attribute of the WinMap to the supplied + value. */ + old_winv = astGetInvert( wm ); + astSetInvert( wm, winv ); + +/* Create copies of the scale and shift terms from the WinMap, and store the + number of axes in it. */ + ninw = astWinTerms( wm, &a, &b ); + +/* Get the number of axes in the UnitMap. */ + ninu = astGetNin( um ); + +/* Create the merged WinMap with unspecified corners. */ + result = astWinMap( ninw + ninu, NULL, NULL, NULL, NULL, "", status ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* If the WinMap applies to the lower axis indices... */ + if( win1 ){ + +/* Use the scale and shift terms from the WinMap for the lower axes of + the new WinMap. */ + aa = a; + bb = b; + ar = result->a; + br = result->b; + + for( i = 0; i < ninw; i++ ){ + *(ar++) = *(aa++); + *(br++) = *(bb++); + } + +/* Use the scale factor to 1.0 and the shift term to zero for the upper axes + of the new WinMap. */ + for( i = 0; i < ninu; i++ ){ + *(ar++) = 0.0; + *(br++) = 1.0; + } + +/* If the WinMap applies to the upper axis indices... */ + } else { + +/* Use the scale factor to 1.0 and the shift term to zero for the lower axes + of the new WinMap. */ + ar = result->a; + br = result->b; + + for( i = 0; i < ninu; i++ ){ + *(ar++) = 0.0; + *(br++) = 1.0; + } + +/* Use the scale and shift terms from the WinMap for the upper axes of + the new WinMap. */ + aa = a; + bb = b; + + for( i = 0; i < ninw; i++ ){ + *(ar++) = *(aa++); + *(br++) = *(bb++); + } + } + } + +/* Free the copies of the scale and shift terms from the supplied WinMap. */ + b = (double *) astFree( (void *) b ); + a = (double *) astFree( (void *) a ); + +/* Re-instate the original setting of the Invert attribute for the + supplied WinMap. */ + astSetInvert( wm, old_winv ); + +/* If an error has occurred, annull the returned WinMap. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output WinMap. */ + return result; +} + +static AstWinMap *WinWin( AstMapping *map1, AstMapping *map2, int inv1, + int inv2, int series, int *status ){ +/* +* Name: +* WinWin + +* Purpose: +* Create a merged WinMap from two supplied WinMaps. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* AstWinMap *WinWin( AstMapping *map1, AstMapping *map2, int inv1, +* int inv2, int series, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* This function creates a new WinMap which performs a mapping +* equivalent to applying the two supplied WinMaps either in series +* or parallel in the directions specified by the "invert" flags +* (the Invert attributes of the supplied WinMaps are ignored). + +* Parameters: +* map1 +* A pointer to the WinMap to apply first (if in series), or to the +* lower axis indices (if in parallel) +* map2 +* A pointer to the WinMap to apply second (if in series), or to the +* upper axis indices (if in parallel) +* inv1 +* The invert flag to use with map1. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* inv2 +* The invert flag to use with map2. +* series +* If non-zero, then the supplied WinMaps are combined in series. +* Otherwise, they are combined in parallel. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new WinMap. + +* Notes: +* - The forward direction of the returned WinMap is equivalent to the +* combined effect of the two supplied WinMap, operating in the +* directions specified by "inv1" and "inv2". +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWinMap *result; /* Pointer to output WinMap */ + AstWinMap *wm1; /* Pointer to the first supplied WinMap */ + AstWinMap *wm2; /* Pointer to the second supplied WinMap */ + double *a[ 2 ]; /* Pointers to shift term arrays */ + double *a0; /* Pointer to next shift term from WinMap 1 */ + double *a1; /* Pointer to next shift term from WinMap 2 */ + double *ar; /* Pointer to next shift term in result */ + double *b[ 2 ]; /* Pointers to scale term arrays */ + double *b0; /* Pointer to next scale term from WinMap 1 */ + double *b1; /* Pointer to next scale term from WinMap 2 */ + double *br; /* Pointer to next scale term in result */ + double amean; /* Geometric mean of the offset terms */ + int cancel; /* Do the two WinMaps cancel out? */ + int i; /* Axis index */ + int invert[ 2 ]; /* Array of invert flags */ + int nin[ 2 ]; /* No. of axes in the two WinMaps */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned pointer. */ + result = NULL; + +/* Store pointers to the WinMaps. */ + wm1 = (AstWinMap *) map1; + wm2 = (AstWinMap *) map2; + +/* Temporarily set their Invert attributes to the supplied values. */ + invert[ 0 ] = astGetInvert( wm1 ); + astSetInvert( wm1, inv1 ); + + invert[ 1 ] = astGetInvert( wm2 ); + astSetInvert( wm2, inv2 ); + +/* Create copies of the scale and shift terms from the two WinMaps, + and store the number of axes in each WinMap. The scale and shift terms + returned take into account the setting of the Invert attribute. */ + nin[ 0 ] = astWinTerms( wm1, a, b ); + nin[ 1 ] = astWinTerms( wm2, a + 1, b + 1 ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* Series */ +/* ====== */ + if( series ){ + +/* Check for equal and opposite WinMaps. Do this explicitly using the + supplied Mappings rather than the values returned by astWinTerms to + avoid the affects of rounding errors in the inversions performed by + astWinTerms. */ + if( ( inv1 == 0 ) != ( inv2 == 0 ) ) { + cancel = 1; + for( i = 0; i < nin[ 0 ]; i++ ){ + if( !astEQUAL( (wm1->a)[ i ], (wm2->a)[ i ] ) || + !astEQUAL( (wm1->b)[ i ], (wm2->b)[ i ] ) ) { + cancel = 0; + break; + } + } + } else { + cancel = 0; + } + +/* If they cancel, just put unit values into the WinMap. */ + if( cancel ) { + a0 = a[ 0 ]; + b0 = b[ 0 ]; + for( i = 0; i < nin[ 0 ]; i++ ){ + *(a0++) = 0.0; + *(b0++) = 1.0; + } + +/* Otherwise, merge the scale and shift terms for the two WinMaps, overwriting + the terms for the first WinMap. To be merged in series, both WinMaps must + have the same number of axes, so it matters not whether we use nin[ 0 ] + or nin[ 1 ] to specify the number of axes. Include rounding checks for values + close to a unit mapping. */ + } else { + a0 = a[ 0 ]; + b0 = b[ 0 ]; + a1 = a[ 1 ]; + b1 = b[ 1 ]; + for( i = 0; i < nin[ 0 ]; i++ ){ + + if( *a0 != AST__BAD && *b0 != AST__BAD && + *a1 != AST__BAD && *b1 != AST__BAD ){ + + amean = sqrt(fabs((*a0)*(*a1))); + + *a0 *= (*b1); + *a0 += (*a1); + *b0 *= (*b1); + + if( fabs( *a0 ) < amean*1E-15 ) *a0 = 0.0; + if( fabs( *b0 - 1.0 ) < 1E-15 ) *b0 = 1.0; + + } else { + *a0 = AST__BAD; + *b0 = AST__BAD; + *a1 = AST__BAD; + *b1 = AST__BAD; + } + +/* Move on to the next axis. */ + a0++; + b0++; + a1++; + b1++; + } + } + +/* Create the merged WinMap with unspecified corners. */ + result = astWinMap( nin[ 0 ], NULL, NULL, NULL, NULL, "", status ); + +/* Store the merged scale and shift terms in the new WinMap. The forward + transformation of this WinMap then corresponds to the combination of the + two supplied WinMaps, taking into account their invert flags. */ + a0 = a[ 0 ]; + b0 = b[ 0 ]; + ar = result->a; + br = result->b; + for( i = 0; i < nin[ 0 ]; i++ ){ + *(ar++) = *(a0++); + *(br++) = *(b0++); + } + +/* Parallel */ +/* ======== */ + } else { + +/* Create the merged WinMap with unspecified corners. */ + result = astWinMap( nin[ 0 ] + nin[ 1 ], NULL, NULL, NULL, NULL, "", status ); + +/* Copy the scale and shift terms into the new WinMap. */ + a0 = a[ 0 ]; + b0 = b[ 0 ]; + a1 = a[ 1 ]; + b1 = b[ 1 ]; + ar = result->a; + br = result->b; + + for( i = 0; i < nin[ 0 ]; i++ ){ + *(ar++) = *(a0++); + *(br++) = *(b0++); + } + + for( i = 0; i < nin[ 1 ]; i++ ){ + *(ar++) = *(a1++); + *(br++) = *(b1++); + } + } + } + +/* Re-instate the original settings of the Invert attributes for the + supplied WinMaps. */ + astSetInvert( wm1, invert[ 0 ] ); + astSetInvert( wm2, invert[ 1 ] ); + +/* Free the memory. */ + a[ 0 ] = (double *) astFree( (void *) a[ 0 ] ); + b[ 0 ] = (double *) astFree( (void *) b[ 0 ] ); + a[ 1 ] = (double *) astFree( (void *) a[ 1 ] ); + b[ 1 ] = (double *) astFree( (void *) b[ 1 ] ); + +/* If an error has occurred, annull the returned WinMap. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output WinMap. */ + return result; +} + +static AstWinMap *WinZoom( AstWinMap *wm, AstZoomMap *zm, int winv, + int zinv, int win1, int series, int *status ){ +/* +* Name: +* WinZoom + +* Purpose: +* Create a WinMap by merging a WinMap and a ZoomMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* AstWinMap *WinZoom( AstWinMap *wm, AstZoomMap *zm, int winv, +* int zinv, int win1, int series, int *status ) + +* Class Membership: +* WinMap member function + +* Description: +* This function creates a new WinMap which performs a mapping +* equivalent to applying the two supplied Mappings in series or +* parallel in the directions specified by the "invert" flags (the +* Invert attributes of the supplied WinMaps are ignored). + +* Parameters: +* wm +* A pointer to the WinMap. +* zm +* A pointer to the ZoomMap. +* winv +* The invert flag to use with wm. A value of zero causes the forward +* mapping to be used, and a non-zero value causes the inverse +* mapping to be used. +* zinv +* The invert flag to use with zm. +* win1 +* Indicates the order in which the Mappings should be applied. +* +* If win1 is non-zero: +* If in series: +* "wm" is applied first followed by "zm". +* If in parallel: +* "wm" applies to the lower axis indices and "zm" to the upper +* axis indices. +* +* If win1 is zero: +* If in series: +* "zm" is applied first followed by "wm". +* If in parallel: +* "zm" applies to the lower axis indices and "wm" to the upper +* axis indices. +* series +* Should be supplied non-zero if the Mappings are to be combined in +* series. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the new WinMap. + +* Notes: +* - The forward direction of the returned WinMap is equivalent to the +* combined effect of the two supplied Mappings, operating in the +* directions specified by "zinv" and "winv". +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstWinMap *result; /* Pointer to output WinMap */ + double *a; /* Pointer to shift term array */ + double *aa; /* Pointer to next shift term */ + double *ar; /* Pointer to next shift term in result */ + double *b; /* Pointer to scale term array */ + double *bb; /* Pointer to next scale term */ + double *br; /* Pointer to next scale term in result */ + double zfac; /* Zoom factor */ + int i; /* Axis index */ + int ninw; /* No. of axes in the WinMap */ + int ninz; /* No. of axes in the ZoomMap */ + int old_winv; /* Original setting of WinMap Invert attribute */ + int old_zinv; /* Original setting of ZoomMap Invert attribute */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Initialise the returned pointer. */ + result = NULL; + +/* Temporarily set the Invert attributes of both Mappings to the supplied + values. */ + old_winv = astGetInvert( wm ); + astSetInvert( wm, winv ); + + old_zinv = astGetInvert( zm ); + astSetInvert( zm, zinv ); + +/* Get the zoom factor implemented by the ZoomMap. Invert it if necessary + since astGetZoom does not take account of the Invert setting. */ + zfac = astGetZoom( zm ); + if( zinv ) zfac = 1.0 / zfac; + +/* Create copies of the scale and shift terms from the WinMap, and store the + number of axes in it. */ + ninw = astWinTerms( wm, &a, &b ); + +/* Check the pointers can be used. */ + if( astOK ){ + +/* First do series mode... */ + if( series ) { + +/* Modify the WinMap scale and shift terms by the zoom factor. How this is + done depends on which way round the Mappings are applied. */ + bb = b; + aa = a; + + for( i = 0; i < ninw; i++ ){ + + if( *aa != AST__BAD && *bb != AST__BAD && zfac != AST__BAD ){ + *bb *= zfac; + if( win1 ) *aa *= zfac; + } else { + *bb = AST__BAD; + *aa = AST__BAD; + } + + aa++; + bb++; + } + +/* Create the merged WinMap with unspecified corners. */ + result = astWinMap( ninw, NULL, NULL, NULL, NULL, "", status ); + +/* Store the merged scale and shift terms in the new WinMap. The forward + transformation of this WinMap then corresponds to the combination of the + two supplied Mappings, taking into account their invert flags. */ + aa = a; + bb = b; + ar = result->a; + br = result->b; + for( i = 0; i < ninw; i++ ){ + *(ar++) = *(aa++); + *(br++) = *(bb++); + } + +/* Now do parallel mode... */ + } else { + +/* Get the number of axes in the ZoomMap. */ + ninz = astGetNin( zm ); + +/* Create the merged WinMap with unspecified corners. */ + result = astWinMap( ninw + ninz, NULL, NULL, NULL, NULL, "", status ); + +/* If the WinMap applies to the lower axis indices... */ + if( win1 ) { + +/* Use the scale and shift terms from the WinMap for the lower axes of + the new WinMap. */ + aa = a; + bb = b; + ar = result->a; + br = result->b; + + for( i = 0; i < ninw; i++ ){ + *(ar++) = *(aa++); + *(br++) = *(bb++); + } + +/* Use the scale factor (with zero shift) from the ZoomMap for the upper axes + of the new WinMap. */ + for( i = 0; i < ninz; i++ ){ + *(ar++) = 0.0; + *(br++) = zfac; + } + +/* If the WinMap applies to the upper axis indices... */ + } else { + +/* Use the scale factor (with zero shift) from the ZoomMap for the lower axes + of the new WinMap. */ + ar = result->a; + br = result->b; + + for( i = 0; i < ninz; i++ ){ + *(ar++) = 0.0; + *(br++) = zfac; + } + +/* Use the scale and shift terms from the WinMap for the upper axes of + the new WinMap. */ + aa = a; + bb = b; + + for( i = 0; i < ninw; i++ ){ + *(ar++) = *(aa++); + *(br++) = *(bb++); + } + } + } + } + +/* Free the copies of the scale and shift terms from the supplied WinMap. */ + b = (double *) astFree( (void *) b ); + a = (double *) astFree( (void *) a ); + +/* Re-instate the original settings of the Invert attribute for the + supplied Mappings. */ + astSetInvert( wm, old_winv ); + astSetInvert( zm, old_zinv ); + +/* If an error has occurred, annull the returned WinMap. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output WinMap. */ + return result; +} + +/* Functions which access class attributes. */ +/* ---------------------------------------- */ +/* Implement member functions to access the attributes associated with + this class using the macros defined for this purpose in the + "object.h" file. For a description of each attribute, see the class + interface (in the associated .h file). */ + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for WinMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for WinMap objects. + +* Parameters: +* objin +* Pointer to the WinMap to be copied. +* objout +* Pointer to the WinMap being constructed. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstWinMap *out; /* Pointer to output WinMap */ + AstWinMap *in; /* Pointer to input WinMap */ + int ncoord; /* No. of axes for the mapping */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the input and output WinMaps. */ + in= (AstWinMap *) objin; + out = (AstWinMap *) objout; + +/* Get the number of coordinates mapped by the WinMap. */ + ncoord = astGetNin( in ); + +/* Allocate memory holding copies of the scales and shifts window defining the + mapping. */ + out->a = (double *) astStore( NULL, (void *) in->a, + sizeof(double)*(size_t)ncoord ); + out->b = (double *) astStore( NULL, (void *) in->b, + sizeof(double)*(size_t)ncoord ); + +/* If an error occurred, free any allocated memory. */ + if ( !astOK ) { + out->a = (double *) astFree( (void *) out->a ); + out->b = (double *) astFree( (void *) out->b ); + } + +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for WinMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for WinMap objects. + +* Parameters: +* obj +* Pointer to the WinMap to be deleted. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This destructor does nothing and exists only to maintain a +* one-to-one correspondence between destructors and copy +* constructors. +*/ + +/* Local Variables: */ + AstWinMap *this; /* Pointer to WinMap */ + +/* Obtain a pointer to the WinMap structure. */ + this = (AstWinMap *) obj; + +/* Free the memory holding the scales and shifts. */ + this->a = (double *) astFree( (void *) this->a ); + this->b = (double *) astFree( (void *) this->b ); + +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for WinMap objects. + +* Type: +* Private function. + +* Synopsis: +* void Dump( AstObject *this, AstChannel *channel, int *status ) + +* Description: +* This function implements the Dump function which writes out data +* for the WinMap class to an output Channel. + +* Parameters: +* this +* Pointer to the WinMap whose data are being written. +* channel +* Pointer to the Channel to which the data are being written. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Constants: */ +#define COMMENT_LEN 50 /* Maximum length of a comment string */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstWinMap *this; /* Pointer to the WinMap structure */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + char comment[ COMMENT_LEN + 1 ]; /* Buffer for comment string */ + int axis; /* Axis index */ + int ncoord; /* No. of axes for mapping */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the WinMap structure. */ + this = (AstWinMap *) this_object; + +/* Get the number of coordinates to be mapped. */ + ncoord = astGetNin( this ); + +/* Write out values representing the instance variables for the + WinMap class. Accompany these with appropriate comment strings, + possibly depending on the values being written.*/ + +/* The scales and shifts. */ + for( axis = 0; axis < ncoord; axis++ ){ + (void) sprintf( buff, "Sft%d", axis + 1 ); + (void) sprintf( comment, "Shift for axis %d", axis + 1 ); + astWriteDouble( channel, buff, (this->a)[ axis ] != 0.0, 0, + (this->a)[ axis ], comment ); + (void) sprintf( buff, "Scl%d", axis + 1 ); + (void) sprintf( comment, "Scale factor for axis %d", axis + 1 ); + astWriteDouble( channel, buff, (this->b)[ axis ] != 1.0, 0, + (this->b)[ axis ], comment ); + } + +/* Undefine macros local to this function. */ +#undef COMMENT_LEN +#undef KEY_LEN +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsAWinMap and astCheckWinMap functions using the macros + defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(WinMap,Mapping) +astMAKE_CHECK(WinMap) + +AstWinMap *astWinMap_( int ncoord, const double c1_in[], const double c2_in[], + const double c1_out[], const double c2_out[], + const char *options, int *status, ...) { +/* +*++ +* Name: +c astWinMap +f AST_WINMAP + +* Purpose: +* Create a WinMap. + +* Type: +* Public function. + +* Synopsis: +c #include "winmap.h" +c AstWinMap *astWinMap( int ncoord, +c const double ina[], const double inb[], +c const double outa[], const double outb[], +c const char *options, ... ) +f RESULT = AST_WINMAP( NCOORD, INA, INB, OUTA, OUTB, OPTIONS, STATUS ) + +* Class Membership: +* WinMap constructor. + +* Description: +* This function creates a new WinMap and optionally initialises its +* attributes. +* +* A Winmap is a linear Mapping which transforms a rectangular +* window in one coordinate system into a similar window in another +* coordinate system by scaling and shifting each axis (the window +* edges being parallel to the coordinate axes). +* +* A WinMap is specified by giving the coordinates of two opposite +* corners (A and B) of the window in both the input and output +* coordinate systems. + +* Parameters: +c ncoord +f NCOORD = INTEGER (Given) +* The number of coordinate values for each point to be +* transformed (i.e. the number of dimensions of the space in +* which the points will reside). The same number is applicable +* to both input and output points. +c ina +f INA( NCOORD ) = DOUBLE PRECISION (Given) +c An array containing the "ncoord" +f An array containing the +* coordinates of corner A of the window in the input coordinate +* system. +c inb +f INB( NCOORD ) = DOUBLE PRECISION (Given) +c An array containing the "ncoord" +f An array containing the +* coordinates of corner B of the window in the input coordinate +* system. +c outa +f OUTA( NCOORD ) = DOUBLE PRECISION (Given) +c An array containing the "ncoord" +f An array containing the +* coordinates of corner A of the window in the output coordinate +* system. +c outb +f OUTB( NCOORD ) = DOUBLE PRECISION (Given) +c An array containing the "ncoord" +f An array containing the +* coordinates of corner B of the window in the output coordinate +* system. +c options +f OPTIONS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing an optional +c comma-separated list of attribute assignments to be used for +c initialising the new WinMap. The syntax used is identical to +c that for the astSet function and may include "printf" format +c specifiers identified by "%" symbols in the normal way. +f A character string containing an optional comma-separated +f list of attribute assignments to be used for initialising the +f new WinMap. The syntax used is identical to that for the +f AST_SET routine. +c ... +c If the "options" string contains "%" format specifiers, then +c an optional list of additional arguments may follow it in +c order to supply values to be substituted for these +c specifiers. The rules for supplying these are identical to +c those for the astSet function (and for the C "printf" +c function). +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astWinMap() +f AST_WINMAP = INTEGER +* A pointer to the new WinMap. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. + +* Status Handling: +* The protected interface to this function includes an extra +* parameter at the end of the parameter list descirbed above. This +* parameter is a pointer to the integer inherited status +* variable: "int *status". + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstWinMap *new; /* Pointer to new WinMap */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the WinMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitWinMap( NULL, sizeof( AstWinMap ), !class_init, &class_vtab, + "WinMap", ncoord, c1_in, c2_in, c1_out, c2_out ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new WinMap's attributes. */ + va_start( args, status ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new WinMap. */ + return new; +} + +AstWinMap *astWinMapId_( int ncoord, const double c1_in[], const double c2_in[], + const double c1_out[], const double c2_out[], + const char *options, ... ) { +/* +* Name: +* astWinMapId_ + +* Purpose: +* Create a WinMap. + +* Type: +* Private function. + +* Synopsis: +* #include "winmap.h" +* AstWinMap *astWinMapId_( int ncoord, const double c1_in[], +* const double c2_in[], const double c1_out[], +* const double c2_out[], +* const char *options, ... ) + +* Class Membership: +* WinMap constructor. + +* Description: +* This function implements the external (public) interface to the +* astWinMap constructor function. It returns an ID value (instead +* of a true C pointer) to external users, and must be provided +* because astWinMap_ has a variable argument list which cannot be +* encapsulated in a macro (where this conversion would otherwise +* occur). +* +* The variable argument list also prevents this function from +* invoking astWinMap_ directly, so it must be a re-implementation +* of it in all respects, except for the final conversion of the +* result to an ID value. + +* Parameters: +* As for astWinMap_. + +* Returned Value: +* The ID value associated with the new WinMap. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstWinMap *new; /* Pointer to new WinMap */ + va_list args; /* Variable argument list */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* Initialise the WinMap, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitWinMap( NULL, sizeof( AstWinMap ), !class_init, &class_vtab, + "WinMap", ncoord, c1_in, c2_in, c1_out, c2_out ); + +/* If successful, note that the virtual function table has been + initialised. */ + if ( astOK ) { + class_init = 1; + +/* Obtain the variable argument list and pass it along with the options string + to the astVSet method to initialise the new WinMap's attributes. */ + va_start( args, options ); + astVSet( new, options, NULL, args ); + va_end( args ); + +/* If an error occurred, clean up by deleting the new object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return an ID value for the new WinMap. */ + return astMakeId( new ); +} + +AstWinMap *astInitWinMap_( void *mem, size_t size, int init, + AstWinMapVtab *vtab, const char *name, + int ncoord, const double *c1_in, + const double *c2_in, const double *c1_out, + const double *c2_out, int *status ) { +/* +*+ +* Name: +* astInitWinMap + +* Purpose: +* Initialise a WinMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "winmap.h" +* AstWinMap *astInitWinMap( void *mem, size_t size, int init, +* AstWinMapVtab *vtab, const char *name, +* int ncoord, const double *c1_in, +* const double *c2_in, +* const double *c1_out, const double *c2_out ) + +* Class Membership: +* WinMap initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new WinMap object. It allocates memory (if necessary) to accommodate +* the WinMap plus any additional data associated with the derived class. +* It then initialises a WinMap structure at the start of this memory. If +* the "init" flag is set, it also initialises the contents of a virtual +* function table for a WinMap at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the WinMap is to be initialised. +* This must be of sufficient size to accommodate the WinMap data +* (sizeof(WinMap)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the WinMap (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the WinMap +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the WinMap's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new WinMap. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the astGetClass +* method). +* ncoord +* The number of coordinate values per point. +* c1_in +* The input coordinates of corner C1 of the window. +* c2_in +* The input coordinates of corner C2 of the window. +* c1_out +* The output coordinates of corner C1 of the window. +* c2_out +* The output coordinates of corner C2 of the window. + +* Returned Value: +* A pointer to the new WinMap. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstWinMap *new; /* Pointer to new WinMap */ + double denom; /* Denominotor */ + int axis; /* Axis index */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitWinMapVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Initialise a Mapping structure (the parent class) as the first component + within the WinMap structure, allocating memory if necessary. Specify that + the Mapping should be defined in both the forward and inverse directions. */ + new = (AstWinMap *) astInitMapping( mem, size, 0, + (AstMappingVtab *) vtab, name, + ncoord, ncoord, 1, 1 ); + + if ( astOK ) { + +/* Initialise the WinMap data. */ +/* ---------------------------- */ +/* Allocate memory to hold the shift and scale for each axis. */ + new->a = (double *) astMalloc( sizeof(double)*(size_t)ncoord ); + new->b = (double *) astMalloc( sizeof(double)*(size_t)ncoord ); + +/* Check the pointers can be used */ + if( astOK ){ + +/* Calculater and store the shift and scale for each axis. */ + for( axis = 0; axis < ncoord; axis++ ){ + +/* If any of the corners have not been provided, store bad values. */ + if( !c1_in || !c1_out || !c2_in || !c2_out ) { + (new->b)[ axis ] = AST__BAD; + (new->a)[ axis ] = AST__BAD; + +/* Otherwise, check the corners are good (not AST__BAD or NaN)... */ + } else if( astISGOOD(c2_in[ axis ]) && astISGOOD(c1_in[ axis ]) && + astISGOOD(c2_out[ axis ]) && astISGOOD(c1_out[ axis ]) ){ + + denom = c2_in[ axis ] - c1_in[ axis ]; + if( denom != 0.0 ){ + (new->b)[ axis ] = ( c2_out[ axis ] - c1_out[ axis ] )/denom; + (new->a)[ axis ] = c1_out[ axis ] - (new->b)[ axis ]*c1_in[ axis ]; + } else { + (new->b)[ axis ] = AST__BAD; + (new->a)[ axis ] = AST__BAD; + } + + } else { + (new->b)[ axis ] = AST__BAD; + (new->a)[ axis ] = AST__BAD; + } + + } + + } + +/* If an error occurred, clean up by deleting the new WinMap. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return a pointer to the new WinMap. */ + return new; +} + +AstWinMap *astLoadWinMap_( void *mem, size_t size, + AstWinMapVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadWinMap + +* Purpose: +* Load a WinMap. + +* Type: +* Protected function. + +* Synopsis: +* #include "winmap.h" +* AstWinMap *astLoadWinMap( void *mem, size_t size, +* AstWinMapVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* WinMap loader. + +* Description: +* This function is provided to load a new WinMap using data read +* from a Channel. It first loads the data used by the parent class +* (which allocates memory if necessary) and then initialises a +* WinMap structure in this memory, using data read from the input +* Channel. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for a WinMap at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the WinMap is to be +* loaded. This must be of sufficient size to accommodate the +* WinMap data (sizeof(WinMap)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the WinMap (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the WinMap structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstWinMap) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new WinMap. If this is NULL, a pointer +* to the (static) virtual function table for the WinMap class +* is used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "WinMap" is used instead. + +* Returned Value: +* A pointer to the new WinMap. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Constants. */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ +#define KEY_LEN 50 /* Maximum length of a keyword */ + +/* Local Variables: */ + AstWinMap *new; /* Pointer to the new WinMap */ + char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */ + int axis; /* Axis index */ + int ncoord; /* The number of coordinate axes */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this WinMap. In this case the + WinMap belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstWinMap ); + vtab = &class_vtab; + name = "WinMap"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitWinMapVtab( vtab, name ); + class_init = 1; + } + } + +/* Invoke the parent class loader to load data for all the ancestral + classes of the current one, returning a pointer to the resulting + partly-built WinMap. */ + new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name, + channel ); + + if ( astOK ) { + +/* Get the number of axis for the mapping. */ + ncoord = astGetNin( (AstMapping *) new ); + +/* Allocate memory to hold the scales and shifts. */ + new->a = (double *) astMalloc( sizeof(double)*(size_t)ncoord ); + new->b = (double *) astMalloc( sizeof(double)*(size_t)ncoord ); + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "WinMap" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + +/* The scales and shifts. */ + for( axis = 0; axis < ncoord; axis++ ){ + (void) sprintf( buff, "sft%d", axis + 1 ); + (new->a)[ axis ] = astReadDouble( channel, buff, 0.0 ); + (void) sprintf( buff, "scl%d", axis + 1 ); + (new->b)[ axis ] = astReadDouble( channel, buff, 1.0 ); + } + } + +/* If an error occurred, clean up by deleting the new WinMap. */ + if ( !astOK ) new = astDelete( new ); + +/* Return the new WinMap pointer. */ + return new; + +/* Undefine macros local to this function. */ +#undef KEY_LEN +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ + +int astWinTerms_( AstWinMap *this, double **scale, double **shift, int *status ){ + if( !astOK ) return 0; + return (**astMEMBER(this,WinMap,WinTerms))( this, scale, shift, status ); +} + + + + diff --git a/winmap.h b/winmap.h new file mode 100644 index 0000000..fd05066 --- /dev/null +++ b/winmap.h @@ -0,0 +1,300 @@ +#if !defined( WINMAP_INCLUDED ) /* Include this file only once */ +#define WINMAP_INCLUDED +/* +*+ +* Name: +* winmap.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the WinMap class. + +* Invocation: +* #include "winmap.h" + +* Description: +* This include file defines the interface to the WinMap class and +* provides the type definitions, function prototypes and macros, +* etc. needed to use this class. +* +* The WinMap class implements Mappings which maps one window onto +* another window by scaling and shifting the values on each axis. + +* Inheritance: +* The WinMap class inherits from the Mapping class. + +* Attributes Over-Ridden: +* None. + +* New Attributes Defined: +* None. + +* Methods Over-Ridden: +* Public: +* None. +* +* Protected: +* ClearAttrib +* Clear an attribute value for a WinMap. +* GetAttrib +* Get an attribute value for a WinMap. +* SetAttrib +* Set an attribute value for a WinMap. +* TestAttrib +* Test if an attribute value has been set for a WinMap. +* astMapMerge +* Simplify a sequence of Mappings containing a WinMap. +* astTransform +* Apply a WinMap to transform a set of points. + +* New Methods Defined: +* Public: +* None. +* +* Protected: +* astWinTerms +* Obtain copies of the shift and scale terms used by a WinMap. + +* Other Class Functions: +* Public: +* astIsAWinMap +* Test class membership. +* astWinMap +* Create a WinMap. +* +* Protected: +* astCheckWinMap +* Validate class membership. +* astInitWinMap +* Initialise a WinMap. +* astInitWinMapVtab +* Initialise the virtual function table for the WinMap class. +* astLoadWinMap +* Load a WinMap. + +* Macros: +* None. + +* Type Definitions: +* Public: +* AstWinMap +* WinMap object type. +* +* Protected: +* AstWinMapVtab +* WinMap virtual function table type. + +* Feature Test Macros: +* astCLASS +* If the astCLASS macro is undefined, only public symbols are +* made available, otherwise protected symbols (for use in other +* class implementations) are defined. This macro also affects +* the reporting of error context information, which is only +* provided for external calls to the AST library. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 23-OCT-1996 (DSB): +* Original version. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitWinMapVtab +* method. +*- +*/ + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "mapping.h" /* Coordinate mappings (parent class) */ + +#if defined(astCLASS) /* Protected */ +#include "pointset.h" /* Sets of points/coordinates */ +#include "channel.h" /* I/O channels */ +#endif + +/* C header files. */ +/* --------------- */ +#if defined(astCLASS) /* Protected */ +#include +#endif + +/* Macros */ +/* ====== */ + +/* Define a dummy __attribute__ macro for use on non-GNU compilers. */ +#ifndef __GNUC__ +# define __attribute__(x) /*NOTHING*/ +#endif + +/* Type Definitions. */ +/* ================= */ +/* WinMap structure. */ +/* ------------------ */ +/* This structure contains all information that is unique to each object in + the class (e.g. its instance variables). */ +typedef struct AstWinMap { + +/* Attributes inherited from the parent class. */ + AstMapping mapping; /* Parent class structure */ + +/* Attributes specific to objects in this class. */ + double *a; /* Pointer to array of shifts */ + double *b; /* Pointer to array of scale factors */ + +} AstWinMap; + +/* Virtual function table. */ +/* ----------------------- */ +/* This table contains all information that is the same for all + objects in the class (e.g. pointers to its virtual functions). */ +#if defined(astCLASS) /* Protected */ +typedef struct AstWinMapVtab { + +/* Properties (e.g. methods) inherited from the parent class. */ + AstMappingVtab mapping_vtab; /* Parent class virtual function table */ + +/* A Unique identifier to determine class membership. */ + AstClassIdentifier id; + +/* Properties (e.g. methods) specific to this class. */ + int (* WinTerms)( AstWinMap *, double **, double **, int * ); + +} AstWinMapVtab; + +#if defined(THREAD_SAFE) + +/* Define a structure holding all data items that are global within the + object.c file. */ + +typedef struct AstWinMapGlobals { + AstWinMapVtab Class_Vtab; + int Class_Init; +} AstWinMapGlobals; + + +/* Thread-safe initialiser for all global data used by this module. */ +void astInitWinMapGlobals_( AstWinMapGlobals * ); + +#endif + + +#endif + +/* Function prototypes. */ +/* ==================== */ +/* Prototypes for standard class functions. */ +/* ---------------------------------------- */ +astPROTO_CHECK(WinMap) /* Check class membership */ +astPROTO_ISA(WinMap) /* Test class membership */ + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +AstWinMap *astWinMap_( int, const double [], const double [], const double [], const double [], const char *, int *, ...); +#else +AstWinMap *astWinMapId_( int, const double [], const double [], const double [], const double [], const char *, ... )__attribute__((format(printf,6,7))); +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +AstWinMap *astInitWinMap_( void *, size_t, int, AstWinMapVtab *, + const char *, int, const double *, const double *, + const double *, const double *, int * ); + +/* Vtab initialiser. */ +void astInitWinMapVtab_( AstWinMapVtab *, const char *, int * ); + +/* Loader. */ +AstWinMap *astLoadWinMap_( void *, size_t, AstWinMapVtab *, + const char *, AstChannel *, int * ); +#endif + +/* Prototypes for member functions. */ +/* -------------------------------- */ +# if defined(astCLASS) /* Protected */ +int astWinTerms_( AstWinMap *, double **, double **, int * ); +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These macros are wrap-ups for the functions defined by this class + to make them easier to invoke (e.g. to avoid type mis-matches when + passing pointers to objects from derived classes). */ + +/* Interfaces to standard class functions. */ +/* --------------------------------------- */ +/* Some of these functions provide validation, so we cannot use them + to validate their own arguments. We must use a cast when passing + object pointers (so that they can accept objects from derived + classes). */ + +/* Check class membership. */ +#define astCheckWinMap(this) astINVOKE_CHECK(WinMap,this,0) +#define astVerifyWinMap(this) astINVOKE_CHECK(WinMap,this,1) + +/* Test class membership. */ +#define astIsAWinMap(this) astINVOKE_ISA(WinMap,this) + +/* Constructor. */ +#if defined(astCLASS) /* Protected. */ +#define astWinMap astINVOKE(F,astWinMap_) +#else +#define astWinMap astINVOKE(F,astWinMapId_) +#endif + +#if defined(astCLASS) /* Protected */ + +/* Initialiser. */ +#define \ +astInitWinMap(mem,size,init,vtab,name,ncoord,c1_in,c2_in,c1_out,c2_out) \ +astINVOKE(O,astInitWinMap_(mem,size,init,vtab,name,ncoord,c1_in,c2_in,c1_out,c2_out,STATUS_PTR)) + +/* Vtab Initialiser. */ +#define astInitWinMapVtab(vtab,name) astINVOKE(V,astInitWinMapVtab_(vtab,name,STATUS_PTR)) +/* Loader. */ +#define astLoadWinMap(mem,size,vtab,name,channel) \ +astINVOKE(O,astLoadWinMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR)) +#endif + +/* Interfaces to public member functions. */ +/* -------------------------------------- */ +/* Here we make use of astCheckWinMap to validate WinMap pointers + before use. This provides a contextual error report if a pointer + to the wrong sort of Object is supplied. */ + +#if defined(astCLASS) /* Protected */ +#define astWinTerms(this,scale,shift) \ +astINVOKE(V,astWinTerms_(astCheckWinMap(this),scale,shift,STATUS_PTR)) +#endif + +#endif + + + + + diff --git a/xml.c b/xml.c new file mode 100644 index 0000000..541132d --- /dev/null +++ b/xml.c @@ -0,0 +1,7119 @@ +/* +* Name: +* xml.c + +* Purpose: +* Implement XML functions for AST. + +* Description: +* This file implements the Xml module which provides generic XML +* reading and writing functions for the XmlChan class. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 22-OCT-2003 (DSB): +* Original version. +* 12-JAN-2004 (DSB): +* Major revisions. +* 10-FEB-2004 (DSB): +* - Added debug conditional code to keep track of memory leaks. +* - Other minor bug fixes. +* 6-FEB-2004 (DSB): +* DefaultURI and astXmlAddURI modified to allow a blank URI to be +* used to ignore a default namespace URI provided by an enclosing +* element. +* 29-NOV-2004 (DSB): +* Added astXmlGetType method. +* 27-JAN-2005 (DSB): +* - Move astXmlTrace and associated code into conditional +* compilation blokc (included if DEBUG macro is defined). This +* speeds up the create and destruction of XmlObjects in non-DEBUG code. +* - Renamed the private Delete function as astXmlDelete and gave +* it protected access. +* - Modify astXmlDelete so that it can succesfully annul objects +* which have no parent. +* - Include extra info in some error messages. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 10-DEC-2008 (DSB): +* Allow a prefix to be included with the attribute name in +* astXmlGetAttributeValue. +*/ + + +/* Module Constants. */ +/* ----------------- */ +/* Set the name of the module we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. NB, this module is not a proper AST + class, but it defines this macro sanyway in order to get the protected + symbols defined in memory.h */ +#define astCLASS Xml + +#define IND_INC 3 + + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "memory.h" /* Interface to the memory management module */ +#include "error.h" /* Interface to the error module */ +#include "xml.h" /* Interface to this module */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include + + +/* +* Name: +* MAKE_CHECK + +* Type: +* Private macro. + +* Purpose: +* Implement the astXmlCheck_ function for XML structures. + +* Synopsis: +* #include "xml.h" +* MAKE_CHECK(type,id) + +* Class Membership: +* Defined by the xml module. + +* Description: +* This macro expands to an implementation of the protected +* astXmlCheck_ function (q.v.) which validates membership of +* a specified XML data type. + +* Parameters: +* type +* The type whose membership is to be validated (e.g. "Element" not +* "XmlElement"). +* id +* The constant (e.g. "AST__XMLELEM") defining the data type. + +* Notes: +* - To avoid problems with some compilers, you should not leave any white +* space around the macro arguments. +*/ + +/* Define the macro. */ +#define MAKE_CHECK(type,id) \ +\ +/* Declare the function */ \ +AstXml##type *astXmlCheck##type##_( void *this, int nullok, int *status ) { \ +\ +/* Local Variables: */\ + AstXml##type *result; /* The returned pointer */\ +\ +/* Check the global error status. If an error has already occurred just\ + return the supplied pointer. This is so that functions such as\ + astXmlAnnul which do not check the inherited status receive the\ + supplied pointer. */\ + if( !astOK ) return this;\ +\ +/* Initialise */\ + result = NULL;\ +\ +/* If the pointer is NULL issue an error if nullok is zero. */\ + if( !this ) {\ + if( !nullok ) astError( AST__PTRIN, "astXmlCheck"#type": Invalid "\ + "NULL pointer supplied." , status);\ +\ +/* Otherwise get the "type" component which holds a magic value for each\ + different class of structure. Compare this value against all valid \ + classes of structure. If no match is found, the pointer does not \ + identify an suitable structure, and so report an error and return \ + NULL. */\ + } else {\ + if( !astXmlCheckType( ( AstXmlObject * ) this, id ) ) {\ + astError( AST__PTRIN, "astXmlCheck"#type": Invalid pointer "\ + "supplied; pointer to AstXml"#type" required." , status);\ + } else {\ + result = (AstXml##type *) this;\ + }\ + }\ +\ +/* Return the result. */\ + return result;\ +} + + +/* Module variables. */ +/* ================= */ + +/* Define macros for accessing all items of thread-safe global data + used by this module. */ +#ifdef THREAD_SAFE + +#define next_id astGLOBAL(Xml,Next_ID) +#define gettag_buff astGLOBAL(Xml,GetTag_Buff) +#define GLOBAL_inits globals->Next_ID = 0; +astMAKE_INITGLOBALS(Xml) + +/* Set up mutexes */ +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ); +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ); + +/* If thread safety is not needed, declare globals at static variables. */ +#else + +static int next_id = 0; +static char gettag_buff[ AST__XML_GETTAG_BUFF_LEN + 1 ]; + +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 + +#ifdef DEBUG /* Not available in thread-safe compilations */ +static int nobj = 0; +static AstXmlObject **existing_objects = NULL; +#endif + +#endif + + +/* Function prototypes. */ +/* ==================== */ + +/* Private member functions. */ +/* ------------------------- */ +static AstXmlAttribute *FindAttribute( AstXmlElement *, const char *, int * ); +static AstXmlAttribute *NewAttribute( const char *, const char *, const char *, int * ); +static AstXmlDocument *NewDocument( int * ); +static AstXmlPrologue *NewPrologue( AstXmlDocument *, int * ); +static AstXmlNamespace *NewNamespace( const char *, const char *, int * ); +static char *AppendChar( char *, int *, char, int * ); +static char *AppendLine( char *, int *, const char *, int, int * ); +static char *RemoveEscapes( const char *, int * ); +static char *CleanText( const char *, int * ); +static const char *AddEscapes( const char *, int * ); +static const char *DefaultURI( AstXmlElement *, int * ); +static const char *Format( AstXmlObject *, int, int * ); +static char *FormatTag( AstXmlObject *, int, int * ); +static const char *ResolvePrefix( const char *, AstXmlElement *, int * ); +static int CheckType( long int, long int, int * ); +static int MatchName( AstXmlElement *, const char *, int * ); +static int Ustrcmp( const char *, const char *, int * ); +static void AddContent( AstXmlParent *, int, AstXmlContentItem *, int * ); +static void CheckName( const char *, const char *, const char *, int, int * ); +static void CheckPrefName( char *, const char *, const char *, int * ); +static void CleanXml( AstXmlObject *, long int, int * ); +static void InitXmlAttribute( AstXmlAttribute *, int, const char *, const char *, const char *, int * ); +static void InitXmlCDataSection( AstXmlCDataSection *, int, const char *, int * ); +static void InitXmlWhite( AstXmlWhite *, int, const char *, int * ); +static void InitXmlBlack( AstXmlBlack *, int, const char *, int * ); +static void InitXmlComment( AstXmlComment *, int, const char *, int * ); +static void InitXmlDocument( AstXmlDocument *, int, int * ); +static void InitXmlPrologue( AstXmlPrologue *, int, int * ); +static void InitXmlDeclPI( AstXmlDeclPI *, int, const char *, int * ); +static void InitXmlDTDec( AstXmlDTDec *, int, const char *, const char *, const char *, int * ); +static void InitXmlElement( AstXmlElement *, int, const char *, const char *, int * ); +static void InitXmlNamespace( AstXmlNamespace *, int, const char *, const char *, int * ); +static void InitXmlObject( AstXmlObject *, long int, int * ); +static void InitXmlPI( AstXmlPI *, int, const char *, const char *, int * ); +static AstXmlElement *ReadContent( AstXmlDocument **, int, int (*)( AstXmlElement *, int * ), int, char (*)( void *, int * ), void *, int, int * ); + +#ifdef DEBUG +static void AddObjectToList( AstXmlObject * ); +static void RemoveObjectFromList( AstXmlObject * ); +#endif + +/* Function implementations. */ +/* ========================= */ + +/* Create the astXmlCheck... functiosn which check a pointer identifies + an XML structure of a given type. */ + +MAKE_CHECK(Document,AST__XMLDOC) +MAKE_CHECK(Object,AST__XMLOBJECT) +MAKE_CHECK(Element,AST__XMLELEM) +MAKE_CHECK(Attribute,AST__XMLATTR) +MAKE_CHECK(CDataSection,AST__XMLCDATA) +MAKE_CHECK(Comment,AST__XMLCOM) +MAKE_CHECK(PI,AST__XMLPI) +MAKE_CHECK(Namespace,AST__XMLNAME) +MAKE_CHECK(Prologue,AST__XMLPRO) +MAKE_CHECK(DeclPI,AST__XMLDEC) +MAKE_CHECK(DTDec,AST__XMLDTD) +MAKE_CHECK(White,AST__XMLWHITE) +MAKE_CHECK(Black,AST__XMLBLACK) +MAKE_CHECK(CharData,AST__XMLCHAR) +MAKE_CHECK(ContentItem,AST__XMLCONT) +MAKE_CHECK(MiscItem,AST__XMLMISC) +MAKE_CHECK(Parent,AST__XMLPAR) + + +static void AddContent( AstXmlParent *this, int where, AstXmlContentItem *item, int *status ){ +/* +* Name: +* AddContent + +* Purpose: +* Add a content item to an XmlElement. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* void AddContent( AstXmlParent *this, int where, AstXmlContentItem *item, int *status ) + +* Description: +* This function adds a supplied item to a specified XmlElement or +* XmlDocument. An error is reported if the item is not appropriate. + +* Parameters: +* this +* The pointer to the element or document to be modified. +* where +* Ignored if "this" is an XmlElement pointer. Otherwise, "where" +* indicates where the item should be added to the document: +* 1 - In the prologue, after the XML declaration but before the DTD. +* 2 - In the prologue, after the DTD but before the root element. +* 3 - In the epilogue, after the root element. +* item +* Pointer to the content item to be added to the element. If +* "this" is an XmlElement, this can be a pointer to any of the +* following types: AstXmlElement, AstXmlWhite, AstXmlBlack, +* AstXmlCDataSection, AstXmlComment, AstXmlPI. If "this" is a +* document, the list is restricted to: AstXmlWhite, AstXmlComment, +* AstXmlPI. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + AstXmlDocument *doc; /* Document pointer */ + AstXmlElement *elem; /* Element pointer */ + AstXmlPrologue *pro; /* Prologue pointer */ + int nitem; /* Number of items in the parent */ + +/* Check the global error status and the supplied pointers. */ + if( !astOK || !this || !item ) return; + +/* Split for the two forms of parent. */ + if( astXmlCheckType( this, AST__XMLELEM ) ) { + elem = (AstXmlElement *) this; + +/* Save the number of content items currently stored in the element. */ + nitem = ( elem->items ) ? elem->nitem : 0; + +/* Attempt to extend the array to hold an extra item. */ + elem->items = astGrow( elem->items, nitem + 1, + sizeof( AstXmlContentItem * ) ); + +/* Check the memory was allocated succesfully. */ + if( astOK ) { + +/* Store the supplied pointer in the array of content items. */ + elem->items[ nitem ] = item; + +/* Increment the number of content items in this element */ + elem->nitem = nitem + 1; + +/* Indicate that the item is owned by the element. */ + ( (AstXmlObject *) item )->parent = this; + } + +/* Now deal with cases where we are adding an item to the prologue or + epilogue of the document. */ + } else { + if( !astXmlCheckType( item, AST__XMLMISC ) ){ + astError( AST__INTER, "AddContent(xml): Inappropriate attempt to " + "add an item of type %ld to an XML document (internal " + "AST programming error).", status, ( (AstXmlObject *) item)->type ); + + } else if( !astXmlCheckType( this, AST__XMLDOC ) ){ + astError( AST__INTER, "AddContent(xml): Inappropriate attempt to " + "add an item of type %ld to an XML object of type %ld " + "(internal AST programming error).", status, + ( (AstXmlObject *) item)->type, + ( (AstXmlObject *) this)->type ); + + } else { + doc = (AstXmlDocument *) this; + +/* Create a prologue if necessary. */ + if( where < 3 && !doc->prolog ) doc->prolog = NewPrologue( doc, status ); + pro = doc->prolog; + + if( where < 2 ) { + nitem = ( pro->misc1 ) ? pro->nmisc1 : 0; + pro->misc1 = astGrow( pro->misc1, nitem + 1, sizeof( AstXmlMiscItem * ) ); + if( astOK ) { + pro->misc1[ nitem ] = item; + pro->nmisc1 = nitem + 1; + ( (AstXmlObject *) item )->parent = (AstXmlParent *) pro; + } + + } else if( where == 2 ) { + nitem = ( pro->misc2 ) ? pro->nmisc2 : 0; + pro->misc2 = astGrow( pro->misc2, nitem + 1, sizeof( AstXmlMiscItem * ) ); + if( astOK ) { + pro->misc2[ nitem ] = item; + pro->nmisc2 = nitem + 1; + ( (AstXmlObject *) item )->parent = (AstXmlParent *) pro; + } + + } else { + nitem = ( doc->epilog ) ? doc->nepi : 0; + doc->epilog = astGrow( doc->epilog, nitem + 1, sizeof( AstXmlMiscItem * ) ); + if( astOK ) { + doc->epilog[ nitem ] = item; + doc->nepi = nitem + 1; + ( (AstXmlObject *) item )->parent = this; + } + } + } + } +} + +static const char *AddEscapes( const char *text, int *status ){ +/* +* Name: +* AddEscapes + +* Purpose: +* Replaces characters by corresponding entity references. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* const char *AddEscapes( const char *text, int *status ) + +* Description: +* This function produces a dynamic copy of the supplied text in which +* occurrences of "&", "<", ">", and "\"" are replaced by the corresponding +* XML entity reference. +* +* The "&" character is only replaced by an entity reference if it is +* followed by a non-name character (i.e. anything except a letter +* underscore or colon). If it is followed by a name character, it is +* assumed to mark the start of an entity reference. + +* Parameters: +* text +* A pointer to a text string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated string containing the required +* copy. + +* Notes: +* - NULL is returned if this function is called with the global error +* status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + char *result; /* Returned pointer */ + const char *c; /* Pointer to next supplied character */ + char *d; /* Pointer to next returned character */ + +/* Initialise */ + result = NULL; + +/* Return if the pointer is NULL or if an error has occurred. */ + if( !astOK || !text ) return result; + +/* Allocate the maximum possible amount of memory that may be needed to + store the returned string. */ + result = astMalloc( 6*strlen( text ) + 1 ); + +/* Check the pointer can be used safely. */ + if( astOK ) { + +/* Loop round every character in the supplied text. */ + c = text - 1; + d = result; + while( *(++c) ) { + +/* We replace this character if it is a <, >, ', &, or ". */ + if( *c == '<' ) { + strcpy( d, "<" ); + d += 4; + + } else if( *c == '>' ) { + strcpy( d, ">" ); + d += 4; + + } else if( *c == '"' ) { + strcpy( d, """ ); + d += 6; + + } else if( *c == '\'' ) { + strcpy( d, "'" ); + d += 6; + + } else if( *c == '&' ) { + strcpy( d, "&" ); + d += 5; + +/* Otherwise just append the supplied character. */ + } else { + *(d++) = *c; + } + } + +/* Terminate the returned string. */ + *d = 0; + +/* Reallocate the string to free up any unused space. */ + result = astRealloc( result, d - result + 1 ); + } + +/* Return the result. */ + return (const char *) result; +} + + +#ifdef DEBUG +static void AddObjectToList( AstXmlObject *obj ){ +/* +* Name: +* AddObjectToList + +* Purpose: +* Adds an XmlObject to a static list of all currently active XmlObjects. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* void AddObjectToList( AstXmlObject *obj ) + +* Description: +* This function adds the supplied pointer to a static list of pointers, +* and increments the number of elements in the list. This list holds +* pointers to all the XmlObjects which currently exist. + +* Parameters: +* this +* A pointer to a new XmlObject. +*/ + +/* Return if the pointer is NULL or if an error has occurred. */ + if( !astOK || !obj ) return; + +/* Increment the number of objects in the list and increase the size of + the list. */ + astBeginPM; + existing_objects = astGrow( existing_objects, ++nobj, sizeof( AstXmlObject *) ); + astEndPM; + +/* Add the new pointer to the end of the list. */ + existing_objects[ nobj - 1 ] = obj; +} +#endif + +static char *AppendChar( char *str1, int *nc, char ch, int *status ) { +/* +* Name: +* AppendChar + +* Purpose: +* Append a character to a string which grows dynamically. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* char *AppendChar( char *str1, int *nc, char ch, int *status ) + +* Description: +* This function appends a character to a dynamically +* allocated string, extending the dynamic string as necessary to +* accommodate the new character (plus the final null). + +* Parameters: +* str1 +* Pointer to the null-terminated dynamic string, whose memory +* has been allocated using the AST memory allocation functions +* defined in "memory.h". If no space has yet been allocated for +* this string, a NULL pointer may be given and fresh space will +* be allocated by this function. +* nc +* Pointer to an integer containing the number of characters in +* the dynamic string (excluding the final null). This is used +* to save repeated searching of this string to determine its +* length and it defines the point where the new string will be +* appended. Its value is updated by this function to include +* the extra characters appended. +* +* If "str1" is NULL, the initial value supplied for "*nc" will +* be ignored and zero will be used. +* ch +* The character which is to be appended to "str1". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A possibly new pointer to the dynamic string with the new character +* appended (its location in memory may have to change if it has to +* be extended, in which case the original memory is automatically +* freed by this function). When the string is no longer required, +* its memory should be freed using astFree. + +* Notes: +* - If this function is invoked with the global error status set +* or if it should fail for any reason, then the returned pointer +* will be equal to "str1" and the dynamic string contents will be +* unchanged. +*/ + +/* Local Variables: */ + char *result; /* Pointer value to return */ + int len; /* Length of new string */ + +/* Initialise. */ + result = str1; + +/* If the first string pointer is NULL, also initialise the character + count to zero. */ + if ( !str1 ) *nc = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Calculate the total string length once the character has been added. */ + len = *nc + 1; + +/* Extend the dynamic string to the required length, including + a final null. Save the resulting pointer, which will be + returned. */ + result = astGrow( str1, len + 1, sizeof( char ) ); + +/* If OK, append the second string and update the total character + count. */ + if ( astOK ) { + result[ *nc ] = ch; + *nc = len; + result[ *nc ] = 0; + } + +/* Return the result pointer. */ + return result; +} + +static char *AppendLine( char *str1, int *nc, const char *str2, int ind, int *status ) { +/* +* Name: +* AppendLine + +* Purpose: +* Append an indented new line to another string which grows dynamically. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* char *AppendLine( char *str1, int *nc, const char *str2, int ind, int *status ) + +* Description: +* This function appends one string to another dynamically +* allocated string, extending the dynamic string as necessary to +* accommodate the new characters (plus the final null). +* +* A newline character is inserted if necessary to ensure that the "str2" +* string starts on a newline. If "ind" is positive, spaces are added +* as necessary to ensure that "str2" begins with the specified number of +* spaces. + +* Parameters: +* str1 +* Pointer to the null-terminated dynamic string, whose memory +* has been allocated using the AST memory allocation functions +* defined in "memory.h". If no space has yet been allocated for +* this string, a NULL pointer may be given and fresh space will +* be allocated by this function. +* nc +* Pointer to an integer containing the number of characters in +* the dynamic string (excluding the final null). This is used +* to save repeated searching of this string to determine its +* length and it defines the point where the new string will be +* appended. Its value is updated by this function to include +* the extra characters appended. +* +* If "str1" is NULL, the initial value supplied for "*nc" will +* be ignored and zero will be used. +* str2 +* Pointer to a constant null-terminated string, a copy of which +* is to be appended to "str1". +* ind +* The number of spaces to use as the indentation string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A possibly new pointer to the dynamic string with the new string +* appended (its location in memory may have to change if it has to +* be extended, in which case the original memory is automatically +* freed by this function). When the string is no longer required, +* its memory should be freed using astFree. + +* Notes: +* - If this function is invoked with the global error status set +* or if it should fail for any reason, then the returned pointer +* will be equal to "str1" and the dynamic string contents will be +* unchanged. +*/ + +/* Local Variables: */ + char *c; /* Point to next character */ + char *result; /* Pointer value to return */ + char *temp; /* Pointer to modified string */ + int j; /* Loop count */ + +/* Initialise. */ + result = str1; + +/* If the first string pointer is NULL, also initialise the character + count to zero. */ + if ( !str1 ) *nc = 0; + +/* Check the global error status. */ + if ( !astOK || !str2 ) return result; + +/* Remove any trailing white space (except for newlines) from the supplied + string. */ + if( *nc > 0 ) { + c = str1 + *nc - 1; + while( isspace( *c ) && *c != '\n' ) { + *(c--) = 0; + (*nc)--; + } + +/* If the last character in the returned string is not now a newline, + append a newline, so long as the new item does not start with a newline. */ + if( str1[ *nc - 1 ] != '\n' ) { + temp = AppendChar( str1, nc, '\n', status ); + } else { + temp = str1; + } + + } else { + temp = str1; + } + +/* If a fixed indentation is specified, skip over any leading spaces in + the second string. */ + if( str2 ) { + if( ind > 0 ) { + while( isspace( *str2 ) ) str2++; + } + +/* If the first character of the second string is a newline, ignore it. */ + if( str2[ 0 ] == '\n' ) str2++; + } + +/* Append the indentation string. */ + for( j = 0; j < ind; j++ ) temp = AppendChar( temp, nc, ' ', status ); + +/* Append the supplied string. */ + return astAppendString( temp, nc, str2 ); +} + +void astXmlAddAttr_( AstXmlElement *this, const char *name, const char *value, + const char *prefix, int *status ){ +/* +*+ +* Name: +* astXmlAddAttr + +* Purpose: +* Add an attribute to an XmlElement. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlAddAttr( AstXmlElement *this, const char *name, +* const char *value, const char *prefix ) + +* Description: +* This function adds an attribute to a specified XmlElement. If the +* element already contains an attribute with the given name amd prefix, +* then the value of the attribute is changed to be the supplied value. + +* Parameters: +* this +* The pointer to the element to be modified. +* name +* Pointer to a null terminated string containing the attribute name. +* value +* Pointer to a null terminated string containing the attribute value. +* prefix +* The namespace prefix for the attribute. May be NULL or blank, in +* which case any prefix at the start of "name" is used. +*- +*/ + +/* Local Variables: */ + AstXmlAttribute *attr; /* The new attribute. */ + AstXmlAttribute *oldattr; /* Pointer to existing attribute */ + int i; /* Loop index */ + int nattr; /* Number of attributes in the element */ + int oldi; /* Index of existing attribute */ + char *my_value; /* Cleaned value text */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Initialise */ + oldattr = NULL; + +/* Clean the value text. */ + my_value = CleanText( value, status ); + +/* Create a new XmlAttribute. */ + attr = NewAttribute( name, my_value, prefix, status ); + +/* Free the memory */ + my_value = astFree( my_value ); + +/* If OK, indicate that the attribute is owned by the element. */ + if( astOK ) { + ( (AstXmlObject *) attr )->parent = (AstXmlParent *) this; + +/* Save the number of attributes currently stored in the element. */ + nattr = ( this->attrs ) ? this->nattr : 0; + +/* Search the existing attributes to see if an attribute with the given + name and prefix already exists. */ + oldi = -1; + for( i = 0; i < nattr; i++ ) { + oldattr = this->attrs[ i ]; + if( !strcmp( oldattr->name, attr->name ) ) { + if( !oldattr->prefix && !attr->prefix ) { + oldi = i; + break; + } else if( oldattr->prefix && attr->prefix && + !strcmp( oldattr->prefix, attr->prefix ) ){ + oldi = i; + break; + } + } + } + +/* If there is an existing attribute with the same name and prefix, + replace the old attribute with the new one created above. */ + if( oldi > -1 ){ + ((AstXmlObject *)oldattr)->parent = NULL; + oldattr = astXmlAnnul( oldattr ); + this->attrs[ oldi ] = attr; + +/* Otherwise, attempt to extend the array to hold an extra attribute. */ + } else { + this->attrs = astGrow( this->attrs, nattr + 1, + sizeof( AstXmlAttribute * ) ); + +/* Check all has gone OK. */ + if( astOK ) { + +/* Store the attribute pointer in the array of attribute pointers. */ + this->attrs[ nattr ] = attr; + +/* Increment the number of content items in this element */ + this->nattr = nattr + 1; + + } + } + } +} + +void astXmlAddCDataSection_( AstXmlElement *this, const char *text, int *status ){ +/* +*+ +* Name: +* astXmlAddCDataSection + +* Purpose: +* Create a new XmlCDataSection and add it to an XmlElement. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlAddCDataSection( AstXmlElement *this, const char *text ) + +* Description: +* This function creates a new XmlCDataSection structure representing +* an unparsed character data (CDATA) section, and adds it into an +* existing element. + +* Parameters: +* this +* A pointer to the element to be modified. +* text +* Pointer to a null terminated string containing the character data. + +*- +*/ + +/* Local Variables: */ + AstXmlCDataSection *new; /* Pointer to new structure */ + char *my_text; /* Cleaned text */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Allocate space for the new structure. */ + new = (AstXmlCDataSection *) astMalloc( sizeof( AstXmlCDataSection ) ); + +/* Clean the text. */ + my_text = CleanText( text, status ); + +/* Initialise it. */ + InitXmlCDataSection( new, AST__XMLCDATA, my_text, status ); + +/* Free the memory */ + my_text = astFree( my_text ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) { + new = astXmlDelete( new ); + +/* Otherwise, add the content item to the element. */ + } else { + AddContent( (AstXmlParent *) this, 0, (AstXmlContentItem *) new, status ); + } +} + +void astXmlAddCharData_( AstXmlParent *this, int where, const char *text, int *status ){ +/* +*+ +* Name: +* astXmlAddCharData + +* Purpose: +* Create a new XmlCharData and add it to an XmlElement or XmlDocument. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlAddCharData( AstXmlParent *this, int where, const char *text ) + +* Description: +* This function creates a new XmlCharData structure representing +* parsed character data, and adds it into an existing element or +* document. + +* Parameters: +* this +* Pointer to the element or document to be modified. +* where +* Ignored if "this" is an XmlElement pointer. Otherwise, "where" +* indicates where the item should be added to the document: +* 1 - In the prologue, after the XML declaration but before the DTD. +* 2 - In the prologue, after the DTD but before the root element. +* 3 - In the epilogue, after the root element. +* text +* Pointer to a null terminated string containing the character data. +* If "this" is a document, the text must consist entirely of white +* space. + +*- +*/ + +/* Local Variables: */ + AstXmlCharData *new; /* Pointer to the new structure */ + char *my_text; /* Pointer to cleaned text */ + char *c; /* Pointer to next character */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Initialise */ + new = NULL; + +/* Clean the text by replacing "\r\n" by "\n". */ + my_text = CleanText( text, status ); + +/* See if the text is all white. */ + c = my_text - 1; + while( *(++c) && isspace( *c ) ); + +/* If the string contains a non-white character, allocate memory for + a XmlBlack structure, and initialise it to hold the supplied text. + Otherwise, allocate memory for a XmlWhite structure, and initialise it + to hold the supplied text. */ + if( *c ) { + if( astXmlCheckType( this, AST__XMLDOC ) ) { + astError( AST__XMLCM, "astXmlAddCharData(xml): Illegal attempt " + "to add non-white character data to the prologue or " + "epilogue of an XML document: \"%s\".", status, my_text ); + } else { + new = (AstXmlCharData *) astMalloc( sizeof( AstXmlBlack ) ); + InitXmlBlack( (AstXmlBlack *) new, AST__XMLBLACK, my_text, status ); + } + + } else { + new = (AstXmlCharData *) astMalloc( sizeof( AstXmlWhite ) ); + InitXmlWhite( (AstXmlWhite *) new, AST__XMLWHITE, my_text, status ); + } + +/* Free the memory holding the cleaned text */ + my_text = astFree( my_text ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) { + new = astXmlDelete( new ); + +/* Otherwise, add the content item to the element. */ + } else { + AddContent( this, where, (AstXmlContentItem *) new, status ); + } +} + +void astXmlAddComment_( AstXmlParent *this, int where, const char *text, int *status ){ +/* +*+ +* Name: +* astXmlAddComment + +* Purpose: +* Create a new XmlComment and add it to an XmlElement or XmlDocument. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlAddComment( AstXmlParent *this, int where, const char *text ) + +* Description: +* This function creates a new XmlComment structure representing +* an XML comment, and adds it into an existing element or document. + +* Parameters: +* this +* Pointer to the element or document to be modified. +* where +* Ignored if "this" is an XmlElement pointer. Otherwise, "where" +* indicates where the item should be added to the document: +* 1 - In the prologue, after the XML declaration but before the DTD. +* 2 - In the prologue, after the DTD but before the root element. +* 3 - In the epilogue, after the root element. +* text +* Pointer to a null terminated string containing the comment text. + +*- +*/ + +/* Local Variables: */ + AstXmlComment *new; /* Pointer to the new structure */ + char *my_text; /* Cleaned text */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Allocate space for the new structure. */ + new = (AstXmlComment *) astMalloc( sizeof( AstXmlComment ) ); + +/* Clean the text. */ + my_text = CleanText( text, status ); + +/* Initialise it. */ + InitXmlComment( new, AST__XMLCOM, my_text, status ); + +/* Free the memory */ + my_text = astFree( my_text ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) { + new = astXmlDelete( new ); + +/* Otherwise, add the content item to the element. */ + } else { + AddContent( this, where, (AstXmlContentItem *) new, status ); + } + +} + +AstXmlElement *astXmlAddElement_( AstXmlElement *this, const char *name, + const char *prefix, int *status ){ +/* +*+ +* Name: +* astXmlAddElement + +* Purpose: +* Create a new empty XmlElement and adds it to an XmlElement. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* AstXmlElement *astXmlAddElement( AstXmlElement *this, const char *name, +* const char *prefix ) + +* Description: +* This function creates a new XmlElement structure representing an +* empty XML element with the given name and namespace prefix, and +* adds it into an existing element. + +* Parameters: +* this +* A pointer to the element to be modified. This may be NULL. +* name +* The name for the element. +* prefix +* The namespace prefix for the element. May be NULL or blank, in +* which case any prefix at the start of "name" is used. + +* Returned Value: +* A pointer to the new structure is returned. This pointer should be +* freed using astXmlAnnul when no longer needed. + +* Notes: +* - A NULL pointer is returned if the inherited status value +* indicates an error has occurred on entry, or if this function +* should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstXmlElement *new; /* The returned pointer */ + +/* Initialise */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Allocate space for the new structure. */ + new = (AstXmlElement *) astMalloc( sizeof( AstXmlElement ) ); + +/* Initialise it. */ + InitXmlElement( new, AST__XMLELEM, name, prefix, status ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) { + new = astXmlDelete( new ); + +/* Otherwise, add the content item to the element. */ + } else { + AddContent( (AstXmlParent *) this, 0, (AstXmlContentItem *) new, status ); + } + +/* Return the result. */ + return new; + +} + +void astXmlAddPI_( AstXmlParent *this, int where, const char *target, const char *text, int *status ){ +/* +*+ +* Name: +* astXmlAddPI + +* Purpose: +* Create a new XmlPI and add it to an element or document. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlAddPI( AstXmlParent *this, int where, const char *target, +* const char *text ) + +* Description: +* This function creates a new XmlPI structure representing an +* XML "programming instruction", and adds it into an existing element +* or document. + +* Parameters: +* this +* Pointer to the element or document to be modified. This should +* be a pointer to an XmlElement or an XmlDocument. +* where +* Ignored if "this" is an XmlElement pointer. Otherwise, "where" +* indicates where the PI should be added to the document: +* 1 - In the prologue, after the XML declaration but before the DTD. +* 2 - In the prologue, after the DTD but before the root element. +* 3 - In the epilogue, after the root element. +* target +* Pointer to a null terminated string containing the PI target. +* text +* Pointer to a null terminated string containing the PI text. + +*- +*/ + +/* Local Variables: */ + AstXmlPI *new; /* Pointer to the new structure */ + char *my_text; /* Cleaned text */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Allocate space for the new structure. */ + new = (AstXmlPI *) astMalloc( sizeof( AstXmlPI ) ); + +/* Clean the text. */ + my_text = CleanText( text, status ); + +/* Initialise it. */ + InitXmlPI( new, AST__XMLPI, target, my_text, status ); + +/* Free the memory */ + my_text = astFree( my_text ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) { + new = astXmlDelete( new ); + +/* Otherwise, add the content item to the element. */ + } else { + AddContent( this, where, (AstXmlContentItem *) new, status ); + } +} + +void astXmlAddURI_( AstXmlElement *this, const char *prefix, const char *uri, int *status ){ +/* +*+ +* Name: +* astXmlAddURI + +* Purpose: +* Add a namespace prefix definition to an XmlElement, or change the +* default namespace. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlAddURI( AstXmlElement *this, const char *prefix, +* const char *uri ) + +* Description: +* This function adds a namespace prefix definition to a specified +* XmlElement, or changes the default namespace. If the suppliedprefix +* is already defined in the element, the associated URI is changed to +* the supplied URI. + +* Parameters: +* this +* The pointer to the element to be modified. +* prefix +* Pointer to a null terminated string containing the namespace +* prefix. If this is NULL or blank, then the supplied URI is used +* as the default namespace for this element and all child elements +* (except for child elements which define their own default +* namespace). +* uri +* Pointer to a null terminated string containing the namespace URI. +* If this is NULL or blank, and "prefix" is also NULL or blank, then +* this has the same effect of there being no default namespace within +* the supplied element. +*- +*/ + +/* Local Variables: */ + AstXmlNamespace *ns; /* The new namespace definition */ + AstXmlNamespace *oldns; /* The existing namespace definition */ + int i; /* Loop index */ + int nc; /* Length of namespace prefix */ + int nnspref; /* Number of namespace defintions in the element */ + int oldi; /* Index of existing attribute */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Initialise */ + oldns = NULL; + +/* Store the used length of the namespace prefix. */ + nc = prefix ? astChrLen( prefix ) : 0; + +/* If no namespace prefix has been supplied, just change the default + namespace URI. */ + if( !nc ) { + if( uri ) { + this->defns = astStore( this->defns, uri, strlen( uri ) + 1 ); + } else { + this->defns = astStore( this->defns, "", 1 ); + } + +/* Otherwise, add the namespace definition to the element. */ + } else { + +/* Create a new XmlNamespace. */ + ns = NewNamespace( prefix, uri, status ); + +/* If OK, indicate that the namespace is owned by the element. */ + if( astOK ) { + ( (AstXmlObject *) ns )->parent = (AstXmlParent *) this; + +/* Save the number of namespace definitions currently stored in the element. */ + nnspref = ( this->nsprefs ) ? this->nnspref : 0; + +/* Search the existing prefixes to see if a namespace with the given + prefix already exists. */ + oldi = -1; + for( i = 0; i < nnspref; i++ ) { + oldns = this->nsprefs[ i ]; + if( !strcmp( oldns->prefix, ns->prefix ) ) { + oldi = i; + break; + } + } + +/* If there is an existing namespace with the same prefix, replace the old + namespace with the new one created above. */ + if( oldi > -1 ){ + ((AstXmlObject *)oldns)->parent = NULL; + oldns = astXmlAnnul( oldns ); + this->nsprefs[ oldi ] = ns; + +/* Otherwise, attempt to extend the array to hold an extra namespace definition. */ + } else { + this->nsprefs = astGrow( this->nsprefs, nnspref + 1, + sizeof( AstXmlNamespace * ) ); + +/* Check all has gone OK. */ + if( astOK ) { + +/* Store the Namespace pointer in the array of Namespace pointers. */ + this->nsprefs[ nnspref ] = ns; + +/* Increment the number of namespaces in this element */ + this->nnspref = nnspref + 1; + } + } + } + } +} + +void *astXmlAnnul_( AstXmlObject *this, int *status ){ +/* +*+ +* Name: +* astXmlAnnul + +* Purpose: +* Free the resources used by an XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void *astXmlAnnul( AstXmlObject *this ) + +* Description: +* This function frees the resources used to hold the XmlObject, together +* with any child objects contained within the supplied XmlObject. A NULL +* pointer is always returned. If the supplied object is still in use +* (that is, if its parent XmlElement still exists) then the resources +* are not freed, and a copy of the supplied pointer is returned. + +* Parameters: +* this +* pointer to the XmlObject to be freed. + +* Returned Value: +* A NULL pointer, or the supplied pointer if the XmlObject is still +* in use. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*- +*/ + +/* Return if a NULL pointer has been suppplied. */ + if( !this ) return NULL; + +/* Return the supplied pointer if the objects parent still exists. */ + if( this->parent && + astXmlCheckType( this->parent, AST__XMLPAR ) ) return this; + +#ifdef DEBUG +/* Remove the supplied object from the list of currently active XmlObjects. */ + RemoveObjectFromList( this ); +#endif + +/* Clean the objects contents, and free the memory holding the XmlObject. */ + CleanXml( this, this->type, status ); + astFree( this ); + +/* Return a NULL pointer. */ + return NULL; +} + +void *astXmlAnnulTree_( AstXmlObject *this, int *status ){ +/* +*+ +* Name: +* astXmlAnnulTree + +* Purpose: +* Free the resources used by a tree of XmlObjects. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void *astXmlAnnulTree( AstXmlObject *this ) + +* Description: +* This function finds the head of the tree containing the supplied +* XmlObject (either an XmlElement or an XmlDocument), and frees the +* resources associated with all members of the tree. A NULL pointer +* is always returned. + +* Parameters: +* this +* Pointer to a member of the tree of XmlObjects to be freed. + +* Returned Value: +* A NULL pointer. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*- +*/ + +/* Return if a NULL pointer has been suppplied. */ + if( !this ) return NULL; + +/* Find the root and annull it. This will free all children (i.e. + the entire tree). */ + return astXmlAnnul( astXmlGetRoot( this ) ); +} + +AstXmlObject *astXmlCopy_( AstXmlObject *this, int *status ) { +/* +*+ +* Name: +* astXmlCopy + +* Purpose: +* Produce a deep copy of a supplied XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* AstXmlObject *astXmlCopy( AstXmlObject *this ) + +* Description: +* This function returns a pointer to a deep copy of the supplied +* XmlObject. + +* Parameters: +* this +* Pointer to the XmlObject to copy. + +* Returned Value: +* Pointer to the new copy. + +* Notes: +* - NULL is returned if NULL pointer is supplied. +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + + +/* Local Variables: */ + AstXmlAttribute *attr; + AstXmlBlack *black; + AstXmlCDataSection *cdata; + AstXmlComment *comm; + AstXmlDTDec *dtd; + AstXmlDeclPI *dec; + AstXmlDocument *doc, *newdoc; + AstXmlElement *elem, *newelem; + AstXmlNamespace *ns; + AstXmlObject *new; + AstXmlPI *pi; + AstXmlPrologue *pro, *newpro; + AstXmlWhite *white; + int i, type; + +/* Initialise */ + new = NULL; + +/* Check the global error status. */ + if( !astOK || !this ) return new; + +/* Initialise a new XmlObject of the required class, and copy any + sub-objects. */ + type = this->type; + if( type == AST__XMLELEM ){ + elem = (AstXmlElement *) this; + new = astMalloc( sizeof( AstXmlElement ) ); + InitXmlElement( (AstXmlElement *) new, AST__XMLELEM, + elem->name, elem->prefix, status ); + + newelem = (AstXmlElement *) new; + + newelem->attrs = astMalloc( sizeof( AstXmlAttribute *) * (size_t)elem->nattr ); + newelem->nattr = elem->nattr; + for( i = 0; i < elem->nattr; i++ ) { + newelem->attrs[ i ] = (AstXmlAttribute *) astXmlCopy( elem->attrs[ i ] ); + ((AstXmlObject *) newelem->attrs[ i ])->parent = (AstXmlParent *) newelem; + } + + newelem->items = astMalloc( sizeof( AstXmlContentItem *) * (size_t)elem->nitem ); + newelem->nitem = elem->nitem; + for( i = 0; i < elem->nitem; i++ ) { + newelem->items[ i ] = (AstXmlContentItem *) astXmlCopy( elem->items[ i ] ); + ((AstXmlObject *) newelem->items[ i ])->parent = (AstXmlParent *) newelem; + } + + newelem->nsprefs = astMalloc( sizeof( AstXmlNamespace *) * (size_t)elem->nnspref ); + newelem->nnspref = elem->nnspref; + for( i = 0; i < elem->nnspref; i++ ) { + newelem->nsprefs[ i ] = (AstXmlNamespace *) astXmlCopy( elem->nsprefs[ i ] ); + ((AstXmlObject *) newelem->nsprefs[ i ])->parent = (AstXmlParent *) newelem; + } + + if( elem->defns ) { + newelem->defns = astStore( NULL, elem->defns, + strlen( elem->defns ) + 1 ); + } + + newelem->complete = elem->complete; + + + } else if( type == AST__XMLATTR ){ + attr = (AstXmlAttribute *) this; + new = astMalloc( sizeof( AstXmlAttribute ) ); + InitXmlAttribute( (AstXmlAttribute *) new, AST__XMLATTR, + attr->name, attr->value, attr->prefix, status ); + + } else if( type == AST__XMLBLACK ){ + black = (AstXmlBlack *) this; + new = astMalloc( sizeof( AstXmlBlack ) ); + InitXmlBlack( (AstXmlBlack *) new, AST__XMLBLACK, + black->text, status ); + + } else if( type == AST__XMLWHITE ){ + white = (AstXmlWhite *) this; + new = astMalloc( sizeof( AstXmlWhite ) ); + InitXmlWhite( (AstXmlWhite *) new, AST__XMLWHITE, + white->text, status ); + + } else if( type == AST__XMLCDATA ){ + cdata = (AstXmlCDataSection *) this; + new = astMalloc( sizeof( AstXmlCDataSection ) ); + InitXmlCDataSection( (AstXmlCDataSection *) new, AST__XMLCDATA, + cdata->text, status ); + + } else if( type == AST__XMLCOM ){ + comm = (AstXmlComment *) this; + new = astMalloc( sizeof( AstXmlComment ) ); + InitXmlComment( (AstXmlComment *) new, AST__XMLCOM, + comm->text, status ); + + } else if( type == AST__XMLPI ){ + pi = (AstXmlPI *) this; + new = astMalloc( sizeof( AstXmlPI ) ); + InitXmlPI( (AstXmlPI *) new, AST__XMLPI, pi->target, pi->text, status ); + + } else if( type == AST__XMLNAME ){ + ns = (AstXmlNamespace *) this; + new = astMalloc( sizeof( AstXmlNamespace ) ); + InitXmlNamespace( (AstXmlNamespace *) new, AST__XMLNAME, ns->prefix, + ns->uri, status ); + + } else if( type == AST__XMLDOC ){ + doc = (AstXmlDocument *) this; + new = astMalloc( sizeof( AstXmlDocument ) ); + InitXmlDocument( (AstXmlDocument *) new, AST__XMLDOC, status ); + + newdoc = (AstXmlDocument *) new; + + if( doc->prolog ) { + newdoc->prolog = (AstXmlPrologue *) astXmlCopy( doc->prolog ); + ((AstXmlObject *) newdoc->prolog)->parent = (AstXmlParent *) newdoc; + } + + if( doc->root ) { + newdoc->root = (AstXmlElement *) astXmlCopy( doc->root ); + ((AstXmlObject *) newdoc->root)->parent = (AstXmlParent *) newdoc; + } + + newdoc->epilog = astMalloc( sizeof( AstXmlMiscItem *) * (size_t)doc->nepi ); + newdoc->nepi = doc->nepi; + for( i = 0; i < doc->nepi; i++ ) { + newdoc->epilog[ i ] = (AstXmlMiscItem *) astXmlCopy( doc->epilog[ i ] ); + ((AstXmlObject *) newdoc->epilog[ i ])->parent = (AstXmlParent *) newdoc; + } + + newdoc->current = NULL; + + } else if( type == AST__XMLPRO ){ + pro = (AstXmlPrologue *) this; + new = astMalloc( sizeof( AstXmlPrologue ) ); + InitXmlPrologue( (AstXmlPrologue *) new, AST__XMLPRO, status ); + + newpro = (AstXmlPrologue *) new; + + if( pro->xmldecl ) { + newpro->xmldecl = (AstXmlDeclPI *) astXmlCopy( pro->xmldecl ); + ((AstXmlObject *) newpro->xmldecl)->parent = (AstXmlParent *) newpro; + } + + if( pro->dtdec ) { + newpro->dtdec = (AstXmlDTDec *) astXmlCopy( pro->dtdec ); + ((AstXmlObject *) newpro->dtdec)->parent = (AstXmlParent *) newpro; + } + + newpro->misc1 = astMalloc( sizeof( AstXmlMiscItem *) * (size_t)pro->nmisc1 ); + newpro->nmisc1 = pro->nmisc1; + for( i = 0; i < pro->nmisc1; i++ ) { + newpro->misc1[ i ] = (AstXmlMiscItem *) astXmlCopy( pro->misc1[ i ] ); + ((AstXmlObject *) newpro->misc1[ i ])->parent = (AstXmlParent *) newpro; + } + + newpro->misc2 = astMalloc( sizeof( AstXmlMiscItem *) * (size_t)pro->nmisc2 ); + newpro->nmisc2 = pro->nmisc2; + for( i = 0; i < pro->nmisc2; i++ ) { + newpro->misc2[ i ] = (AstXmlMiscItem *) astXmlCopy( pro->misc2[ i ] ); + ((AstXmlObject *) newpro->misc2[ i ])->parent = (AstXmlParent *) newpro; + } + + } else if( type == AST__XMLDEC ){ + dec = (AstXmlDeclPI *) this; + new = astMalloc( sizeof( AstXmlDeclPI ) ); + InitXmlDeclPI( (AstXmlDeclPI *) new, AST__XMLDEC, dec->text, status ); + + } else if( type == AST__XMLDTD ){ + dtd = (AstXmlDTDec *) this; + new = astMalloc( sizeof( AstXmlDTDec ) ); + InitXmlDTDec( (AstXmlDTDec *) new, AST__XMLDTD, dtd->name, + dtd->external, dtd->internal, status ); + + } else if( astOK ) { + astError( AST__INTER, "CopyXml: Invalid object type (%d) supplied " + "(internal AST programming error).", status, type ); + } + +/* If an error occurred, delete the new structure. */ + if( !astOK ) new = astXmlDelete( new ); + +/* Return the result. */ + return new; +} + +const char *astXmlFormat_( AstXmlObject *this, int *status ) { +/* +*+ +* Name: +* astXmlFormat + +* Purpose: +* Converts an XmlObject into a character string. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlFormat( AstXmlObject *this ) + +* Description: +* This function returns a pointer to a dynamically allocated string +* containing a textual representation of the supplied XmlObject. + +* Parameters: +* this +* Pointer to the XmlObject to format. + +* Returned Value: +* Pointer to a null terminated string holding the formated XmlObject. +* This string should be freed when no longer needed using astFree. + +* Notes: +* - No newlines or indentation strings are added to the returned string. +* - NULL is returned if NULL pointer is supplied. +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + return Format( this, -1, status ); +} + +const char *astXmlGetAttributeValue_( AstXmlElement *this, const char *name, int *status ){ +/* +*+ +* Name: +* astXmlGetAttributeValue + +* Purpose: +* Return a pointer to a string holding the value of a named attribute. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlGetAttributeValue( AstXmlElement *this, const char *name ) + +* Description: +* This function returns a pointer to a constant string holding the +* value of a named attribute of a supplied element. If the element +* does not have the named attribute, a NULL pointer is returned but +* no error is reported. + +* Parameters: +* this +* The pointer to the XmlElement. +* name +* Pointer to a string holding the name of the attribute. The name +* may be preceded with a "prefix:" string, in which case the +* prefix will also be matched. If no prefix is included, the first +* attribute with the specified name is returned, regardless of +* its prefix. + +* Returned Value: +* Pointer to a string holding the value of the attribute within the +* supplied element, or NULL if the attribute was not found. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *result; /* Returned pointer */ + AstXmlAttribute *attr; /* Pointer to the attribute */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Find the attribute. */ + attr = FindAttribute( this, name, status ); + +/* Get its value. */ + if( attr ) result = attr->value; + +/* Return the result. */ + return result; +} + +AstXmlContentItem *astXmlGetItem_( AstXmlElement *this, int item, int *status ){ +/* +*+ +* Name: +* astXmlGetItem + +* Purpose: +* Return a specified item of the content of an element. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* AstXmlContentItem *astXmlGetItem( AstXmlElement *this, int item ) + +* Description: +* This function returns a pointer to an item of the content of the +* specified element. + +* Parameters: +* this +* The pointer to the XmlElement. +* item +* The index of the required item, in the range zero to "nitem-1", +* where "nitem" is the number of items in the element as returned +* by astXmlGetNitem. An error is reported if the specified index +* is out of bounds. + +* Returned Value: +* A pointer to the requested item. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstXmlContentItem *result; /* The returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Report an error if the supplie dindex is bad. */ + if( this->nitem == 0 ) { + astError( AST__XMLIT, "astXmlGetItem(xml): The supplied item index (%d) " + "is out of bounds. The supplied XmlObject has no content.", status, + item ); + + } else if( item < 0 || item >= this->nitem ) { + astError( AST__XMLIT, "astXmlGetItem(xml): The supplied item index (%d) " + "is out of bounds. Should be in the range 0 to %d.", status, + item, this->nitem-1 ); + } else { + result = this->items[ item ]; + } + +/* Return the result. */ + return result; +} + +const char *astXmlGetName_( AstXmlObject *this, int *status ){ +/* +*+ +* Name: +* astXmlGetName + +* Purpose: +* Return a pointer to a string holding the name of an XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlGetName( AstXmlObject *this ) + +* Description: +* This function returns a pointer to a constant string holding the +* name associated with an XmlObject. For elements and attributes, the +* "name" value is returned. For PI elements, the "target" value is +* returned. For namespace definitions, the "prefix" value is returned. +* An error is reported if the supplied XmlObject is of any other class. + +* Parameters: +* this +* The pointer to the XmlObject. + +* Returned Value: +* Pointer to the name string within the XML object. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *result; /* Returned pointer */ + int type; /* Object type */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Return the relevant component of the structure, depending on its type. */ + type = this->type; + if( type == AST__XMLELEM ){ + result = ( (AstXmlElement *) this )->name; + + } else if( type == AST__XMLATTR ){ + result = ( (AstXmlAttribute *) this )->name; + + } else if( type == AST__XMLPI ){ + result = ( (AstXmlPI *) this )->target; + + } else if( type == AST__XMLNAME ){ + result = ( (AstXmlNamespace *) this )->prefix; + + } else { + astError( AST__INTER, "astXmlGetName: Inappropriate object type (%d) supplied " + "(internal AST programming error).", status, type ); + } + +/* Return the result. */ + return result; +} + +int astXmlGetNattr_( AstXmlElement *this, int *status ){ +/* +*+ +* Name: +* astXmlGetNattr + +* Purpose: +* Return the number of attributes held by an element. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* int astXmlGetNattr( AstXmlElement *this ) + +* Description: +* This function returns the number of attributes held by an element. + +* Parameters: +* this +* The pointer to the XmlElement. + +* Returned Value: +* The number of attributes held by the supplied element. + +* Notes: +* - Zero is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if( !astOK ) return 0; + +/* Return the result. */ + return ( this->attrs ) ? this->nattr : 0; +} + +int astXmlGetNitem_( AstXmlElement *this, int *status ){ +/* +*+ +* Name: +* astXmlGetNitem + +* Purpose: +* Return the number of items within the content of an element. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* int astXmlGetNitem( AstXmlElement *this ) + +* Description: +* This function returns the number of items within the content of an +* XmlElement. + +* Parameters: +* this +* The pointer to the XmlElement. + +* Returned Value: +* The number of items in the content of the supplied element. + +* Notes: +* - Zero is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if( !astOK ) return 0; + +/* Return the result. */ + return this->nitem; +} + +AstXmlParent *astXmlGetParent_( AstXmlObject *this, int *status ){ +/* +*+ +* Name: +* astXmlGetParent + +* Purpose: +* Return a pointer to the object which contains the supplied XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* AstXmlParent *astXmlGetParent( AstXmlObject *this ) + +* Description: +* This function returns a pointer to the XmlParent object (either an +* XmlElement or an XmlDocument) which contains the specified XmlObject. +* The object can be a content item (an element, a comment, a CDATA +* section, a PI, or character data) in which case the enclosing +* XmlElement is returned, or an attribute or namespace definition in +* which case the XmlElement to which object refers is returned. +* If "this" is the root element of a document, a pointer to the +* XmlDocument is returned. + + +* Parameters: +* this +* The pointer to check. + +* Returned Value: +* Pointer to the parent, or NULL if the object does not have a parent. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Check the global error status. */ + if( !astOK ) return NULL; + +/* Return the result. */ + return this->parent; +} + +AstXmlObject *astXmlGetRoot_( AstXmlObject *this, int *status ){ +/* +*+ +* Name: +* astXmlGetRoot + +* Purpose: +* Return a pointer to the root XmlObject which contains the supplied +* XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* AstXmlObject *astXmlGetRoot( AstXmlObject *this ) + +* Description: +* This function returns a pointer to the XmlObject which is the root of +* the tree containing the specified XmlObject. A pointer to the +* supplied XmlObject is returned if it has no parent. + +* Parameters: +* this +* The pointer to check. + +* Returned Value: +* Pointer to the root XmlObject, or a copy of the supplied pointer if +* the supplied XmlObject is the root. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstXmlObject *result; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* If "this" is a document, check it has no parent. If not, return a + pointer ot it. */ + if( astXmlCheckType( this, AST__XMLDOC ) ) { + if( this->parent ) { + astError( AST__INTER, "astXmlGetRoot(xml): An XmlDocument has a " + "non-null parent of type %ld (internal AST programming " + "error).", status, this->type ); + } else { + result = (AstXmlObject *) this; + } + +/* Otherwise... */ + } else if( this->parent ) { + result = astXmlGetRoot( this->parent ); + + } else { + result = this; + } + +/* Return the result. */ + return result; +} + +const char *astXmlGetTag_( AstXmlObject *this, int opening, int *status ){ +/* +*+ +* Name: +* astXmlGetTag + +* Purpose: +* Returns a string holding an XML tag describing the given XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlGetTag( AstXmlObject *this, int opening ) + +* Description: +* This function returns a pointer to a static string containing an +* XML tag describing the given XmlObject. + +* Parameters: +* this +* Pointer to the XmlObject. +* opening +* Indicates which tag is to be returned; the start tag or the end +* tag. If non-zero the start tag is returned. Otherwise, the +* end tag is returned. If the supplied XmlObject has no end +* tag (i.e. if it is an empty element, or if it is not an element), +* then NULL is returned but no error is reported. + +* Returned Value: +* Pointer to a null terminated string holding the tag. If the tag +* exceeds 200 characters, only the first 197 characters are returned +* and "..." is appended to the end. + +* Notes: +* - Subsequent invocations of this function will over-write the +* buffer which used to hold the returned string. +* - Empty elements are represented as an start tag of the form <.../>, +* with no corresponding end tag. +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + char *result; /* The returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Get a dynamic string holding the formatted tag. */ + result = FormatTag( this, opening, status ); + +/* If OK, copy the result into the static buffer. */ + gettag_buff[ 0 ] = 0; + if( result ) { + if( astOK ) { + + if( strlen( result ) > AST__XML_GETTAG_BUFF_LEN ) { + strncpy( gettag_buff, result, AST__XML_GETTAG_BUFF_LEN -3 ); + strcpy( gettag_buff + AST__XML_GETTAG_BUFF_LEN - 3, "..." ); + } else { + strncpy( gettag_buff, result, AST__XML_GETTAG_BUFF_LEN ); + } + + gettag_buff[ AST__XML_GETTAG_BUFF_LEN ] = 0; + astFree( result ); + result = gettag_buff; + } else { + result = astFree( result ); + } + } + +/* Return the result. */ + return result; +} + +const char *astXmlGetType_( AstXmlObject *this, int *status ){ +/* +*+ +* Name: +* astXmlGetType + +* Purpose: +* Returns a string holding the type of the given XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlGetType( AstXmlObject *this ) + +* Description: +* This function returns a pointer to a static string containing the +* type of the given XmlObject. + +* Parameters: +* this +* Pointer to the XmlObject. + +* Returned Value: +* Pointer to a null terminated string holding the type string. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *result; /* The returned pointer */ + int type; /* Element type */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + + type = this->type; + if( type == AST__XMLELEM ) { + result = "element"; + + } else if( type == AST__XMLATTR ) { + result = "attribute"; + + } else if( type == AST__XMLCDATA ) { + result = "CDATA section"; + + } else if( type == AST__XMLCOM ) { + result = "comment"; + + } else if( type == AST__XMLPI ) { + result = "processing instruction"; + + } else if( type == AST__XMLNAME ) { + result = "namespace"; + + } else if( type == AST__XMLDOC ) { + result = "document"; + + } else if( type == AST__XMLPRO ) { + result = "prologue"; + + } else if( type == AST__XMLDEC ) { + result = "XML delaration PI"; + + } else if( type == AST__XMLDTD ) { + result = "DTD"; + + } else if( type == AST__XMLWHITE ) { + result = "white-space character data "; + + } else if( type == AST__XMLBLACK ) { + result = "non-blank character data"; + + } else { + result = "unknown XML object"; + } + +/* Return the result. */ + return result; +} + +const char *astXmlGetURI_( AstXmlObject *this, int *status ){ +/* +*+ +* Name: +* astXmlGetURI + +* Purpose: +* Return a pointer to a string holding the namespace URI of an XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlGetURI( AstXmlObject *this ) + +* Description: +* This function returns a pointer to a constant string holding the +* namespace URI associated with an XmlObject. Only attributes, +* elements and namespaces have associated URIs, so a NULL pointer is +* returned for any other class of XmlObject. A NULL pointer is also +* returned if XmlObject does not belong to any namespace, or if it +* belongs to a unknown namespace (i.e. one for which no URI is +* available). Any namespace prefix attached to the supplied object is +* resolved first using any "xmlns" attributes contained in the same +* element, then using any "xmlns" attributes contained in the parent +* element, etc. + +* Parameters: +* this +* The pointer to the XmlObject. + +* Returned Value: +* Pointer to a string holding the namespace URI, or NULL. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *prefix; /* Namespace prefix */ + const char *result; /* Returned pointer */ + int type; /* Object type */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Do each type of object separately. */ + type = this->type; + if( type == AST__XMLATTR ){ + prefix = ( (AstXmlAttribute *) this )->prefix; + +/* Attributes have no default name space. Therefore if there is no prefix, + return NULL. If there is a prefix, resolve it within the context of + the attributes parent element. */ + if( prefix ) { + result = ResolvePrefix( prefix, (AstXmlElement *) this->parent, status ); + } + + } else if( type == AST__XMLELEM ){ + prefix = ( (AstXmlElement *) this )->prefix; + +/* If there is a prefix, resolve it within the context of this element. */ + if( prefix ) { + result = ResolvePrefix( prefix, (AstXmlElement *) this, status ); + +/* Elements do have a default name space. Therefore if there is no prefix, + return the default name space within the context of this element. */ + } else { + result = DefaultURI( (AstXmlElement *) this, status ); + } + +/* If the supplied object is a namespace, just return the associated URI. */ + } else if( type == AST__XMLNAME ){ + result = ( (AstXmlNamespace *) this )->uri; + + } + +/* Return the result. */ + return result; +} + +const char *astXmlGetValue_( AstXmlObject *this, int report, int *status ){ +/* +*+ +* Name: +* astXmlGetValue + +* Purpose: +* Return a pointer to a string holding the value of an XmlObject. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlGetValue( AstXmlObject *this, int report ) + +* Description: +* This function returns a pointer to a constant string holding the +* value associated with an XmlObject. For attributes, the attribute value +* is returned. For PI elements, the "text" value is returned. For +* namespace definitions, the "URI" value is returned. For character +* data, the character data is returned. For CDATA sections the "text" +* value is returned. For comments, the "text" value is returned. +* If the XmlObject is an element, then a non-NULL value is returned +* only if the element contains a single content item holding character +* data. In this case a pointer to the character data is returned. +* A null value is returned in all other cases (but no error is +* reported unless "report" is non-zero). + +* Parameters: +* this +* The pointer to the XmlObject. +* report +* Report an error if the supplied XmlObject does not have a value? + +* Returned Value: +* Pointer to a string holding the value of the XML object. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstXmlContentItem *item;/* Element content */ + const char *result; /* Returned pointer */ + int type; /* Object type */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Return the relevant component of the structure, depending on its type. */ + type = this->type; + if( type == AST__XMLATTR ){ + result = ( (AstXmlAttribute *) this )->value; + + } else if( type == AST__XMLBLACK ){ + result = ( (AstXmlBlack *) this )->text; + + } else if( type == AST__XMLWHITE ){ + result = ( (AstXmlWhite *) this )->text; + + } else if( type == AST__XMLCDATA ){ + result = ( (AstXmlCDataSection *) this )->text; + + } else if( type == AST__XMLCOM ){ + result = ( (AstXmlComment *) this )->text; + + } else if( type == AST__XMLPI ){ + result = ( (AstXmlPI *) this )->text; + + } else if( type == AST__XMLNAME ){ + result = ( (AstXmlNamespace *) this )->uri; + + } else if( type == AST__XMLELEM ){ + if( astXmlGetNitem( (AstXmlElement *) this ) == 1 ) { + item = astXmlGetItem( (AstXmlElement *) this, 0 ); + if( astXmlCheckType( item, AST__XMLCHAR ) ) { + result = astXmlGetValue( item, report ); + } + } + + if( !result && astOK && report ) { + astError( AST__BADIN, "astRead(xml): Cannot get the value of " + "element \"<%s>\": its contents are not pure character " + "data.", status, astXmlGetName( this ) ); + } + + } else if( report ) { + astError( AST__INTER, "astXmlGetValue(xml): Cannot get the value of " + "an XmlObject of type %d (internal AST programming " + "error).", status, type ); + } + +/* Return the result. */ + return result; +} + +void astXmlInsertElement_( AstXmlElement *this, AstXmlElement *elem, int *status ){ +/* +*+ +* Name: +* astXmlInsertElement + +* Purpose: +* Inserts an existing XmlElement into another XmlElement. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlInsertElement( AstXmlElement *this, AstXmlElement *elem ) + +* Description: +* This function inserts a given XmlElement "elem" into another given +* XmlElement "this". An error is reported if "elem" already has a +* parent. + +* Parameters: +* this +* A pointer to the element to be modified. +* elem +* The element to be inserted into "this". + +*- +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Report AN error if "elem" has already been inserted into + another element. */ + if( ((AstXmlObject *) elem)->parent ) { + astError( AST__INTER, "astXmlInsertElement(xml): Cannot insert \"%s\" " + "into \"%s\" because it already has a parent (\"%s\") " + "(internal AST programming error).", status, + astXmlGetTag( elem, 1 ), astXmlGetTag( this, 1 ), + astXmlGetTag( ((AstXmlObject *) elem)->parent, 1 ) ); + +/* Otherwise, add the content item to the element. */ + } else { + AddContent( (AstXmlParent *) this, 0, (AstXmlContentItem *) elem, status ); + } +} + +void astXmlPurge_( AstXmlParent *this, int *status ) { +/* +*+ +* Name: +* astXmlPurge + +* Purpose: +* Remove blank content from a parent object. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlPurge( AstXmlParent *this ) + +* Description: +* This function removes all character data containing only whitespace +* from the supplied document or element. It is recursive, in that it also +* removes white space from all children elements. + +* Parameters: +* this +* Pointer to the document or element. + +*- +*/ + +/* Local Variables: */ + int i; /* Content item index */ + AstXmlContentItem *item; /* Next content item */ + AstXmlMiscItem *misc; /* Nest miscalleneous item */ + AstXmlDocument *doc; /* This document */ + AstXmlPrologue *pro; /* This document prologue */ + AstXmlElement *elem; /* This element */ + +/* Check the global error status. */ + if( !astOK || !this ) return; + +/* If this is a a document.. */ + if( astXmlCheckType( this, AST__XMLDOC ) ) { + doc = (AstXmlDocument *) this; + astXmlPurge( doc->prolog ); + astXmlPurge( doc->root ); + + i = -1; + while( ++i < doc->nepi ) { + misc = doc->epilog[ i ]; + if( astXmlCheckType( misc, AST__XMLWHITE ) ) { + misc = astXmlDelete( misc ); + i--; + } + } + +/* If this is a prologue.. */ + } else if( astXmlCheckType( this, AST__XMLPRO ) ) { + pro = (AstXmlPrologue *) this; + + i = -1; + while( ++i < pro->nmisc1 ) { + misc = pro->misc1[ i ]; + if( astXmlCheckType( misc, AST__XMLWHITE ) ) { + misc = astXmlDelete( misc ); + i--; + } + } + + i = -1; + while( ++i < pro->nmisc2 ) { + misc = pro->misc2[ i ]; + if( astXmlCheckType( misc, AST__XMLWHITE ) ) { + misc = astXmlDelete( misc ); + i--; + } + } + + +/* If this is an element */ + } else if( astXmlCheckType( this, AST__XMLELEM ) ) { + elem = (AstXmlElement *) this; + + i = -1; + while( ++i < elem->nitem ) { + item = elem->items[ i ]; + + if( astXmlCheckType( item, AST__XMLWHITE ) ) { + item = astXmlDelete( item ); + i--; + + } else if( astXmlCheckType( item, AST__XMLELEM ) ) { + astXmlPurge( (AstXmlParent *) item ); + } + } + } +} + +void astXmlRemoveAttr_( AstXmlElement *this, const char *name, + const char *prefix, int *status ){ +/* +*+ +* Name: +* astXmlRemoveAttr + +* Purpose: +* Removes an attribute from its parent element. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlRemoveAttr( AstXmlElement *this, const char *name, +* const char *prefix ) + +* Description: +* This function removes a named attribute from its parent element. + +* Parameters: +* this +* The pointer to the element containing the attribute to be removed. +* name +* Pointer to a null terminated string containing the attribute name. +* prefix +* The namespace prefix for the attribute. May be NULL or blank, in +* which case any prefix at the start of "name" is used. +*- +*/ + +/* Local Variables: */ + AstXmlAttribute *attr; /* Pointer to temporary attribute structure */ + AstXmlAttribute *oldattr; /* Pointer to existing attribute */ + int i; /* Attribute index */ + int nattr; /* Number of attributes in parent */ + int oldi; /* Indexof existing attribute */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Initialise */ + oldattr = NULL; + +/* Create a new XmlAttribute with blank value. */ + attr = NewAttribute( name, "", prefix, status ); + if( astOK ) { + +/* Get the number of attributes currently stored in the element. */ + nattr = ( this->attrs ) ? this->nattr : 0; + +/* Search the existing attributes to see if an attribute with the given + name and prefix already exists. */ + oldi = -1; + for( i = 0; i < nattr; i++ ) { + oldattr = this->attrs[ i ]; + if( !strcmp( oldattr->name, attr->name ) ) { + if( !oldattr->prefix && !attr->prefix ) { + oldi = i; + break; + } else if( oldattr->prefix && attr->prefix && + !strcmp( oldattr->prefix, attr->prefix ) ){ + oldi = i; + break; + } + } + } + +/* If there is an existing attribute with the same name and prefix, + delete it. */ + if( oldi > -1 ) astXmlDelete( oldattr ); + +/* Delete the temporary attribute structure. */ + attr = astXmlDelete( attr ); + + } +} + +void astXmlRemoveItem_( AstXmlContentItem *this, int *status ){ +/* +*+ +* Name: +* astXmlRemoveItem + +* Purpose: +* Removes an item of content from its parent element or document. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlRemoveItem( AstXmlContentItem *this ) + +* Description: +* This function removes an item of content from its parent element, +* or removes the root element from a document. The removed item is not +* annulled and may be subsequently added into another element. + +* Parameters: +* this +* The pointer to the item to be removed form its parent. +*- +*/ + +/* Local Variables: */ + AstXmlDocument *doc; /* Pointer to parent document */ + AstXmlElement *elem; /* Pointer to parent element */ + AstXmlParent *parent; /* Pointer to parent */ + int found; /* Was the item found within its parent? */ + int i; /* Item index */ + int j; /* Item index */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Get a pointer to the items parent element, and check it is not null. */ + parent = ( (AstXmlObject *) this )->parent; + if( parent && astXmlCheckType( parent, AST__XMLELEM ) ) { + elem = (AstXmlElement *) parent; + +/* Search through all the items within the parent element looking for the + supplied item. */ + found = 0; + for( i = 0; i < elem->nitem; i++ ) { + if( elem->items[ i ] == this ) { + +/* When found, decrement the number of items in the element, and shuffle + all the remaining item pointers down one slot to over-write it, then + nullify the parent pointer in the supplied object and leave the loop. */ + (elem->nitem)--; + for( j = i; j < elem->nitem; j++ ) { + elem->items[ j ] = elem->items[ j + 1 ]; + } + ( (AstXmlObject *) this )->parent = NULL; + found = 1; + break; + } + } + +/* Report an error if the item was not found. */ + if( !found ) { + astError( AST__INTER, "astXmlRemoveItem: The parent of the supplied " + "item does not contain the item (internal AST programming " + "error)." , status); + } + +/* If the parent is an XmlDocument, check the item being removed is the + root element. */ + } else if( parent && astXmlCheckType( parent, AST__XMLDOC ) ) { + doc = (AstXmlDocument *) parent; + if( (AstXmlElement *) this == doc->root ) { + ( (AstXmlObject *) this )->parent = NULL; + doc->root = NULL; + } + } +} + +void astXmlRemoveURI_( AstXmlElement *this, const char *prefix, int *status ){ +/* +*+ +* Name: +* astXmlRemoveURI + +* Purpose: +* Removes an namespace prefix from its parent element. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlRemoveURI( AstXmlElement *this, const char *prefix ) + +* Description: +* This function removes a named namespace prefix from its parent element. + +* Parameters: +* this +* The pointer to the element containing the namespace prefix to be +* removed. +* prefix +* The namespace prefix to remove. +*- +*/ + +/* Local Variables: */ + AstXmlNamespace *ns; /* Temporary namespace structure */ + AstXmlNamespace *oldns; /* Pointer to existing namespace */ + int oldi; /* Index of namespace within its parent */ + int i; /* Namespace index */ + int nns; /* Number of existing namespaces */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Initialise */ + oldns = NULL; + +/* Create a new XmlNamespace with blank URI. */ + ns = NewNamespace( prefix, "", status ); + if( astOK ) { + +/* Get the number of namespace prefixes currently stored in the element. */ + nns = ( this->nsprefs ) ? this->nnspref : 0; + +/* Search the list of existing namespace prefixes to see if the given prefix + is included. */ + oldi = -1; + for( i = 0; i < nns; i++ ) { + oldns = this->nsprefs[ i ]; + if( !strcmp( oldns->prefix, ns->prefix ) ){ + oldi = i; + break; + } + } + +/* If the supplied namespace prefix was found in the list, delete it. */ + if( oldi > -1 ) astXmlDelete( oldns ); + +/* Delete the temporary namespace structure. */ + ns = astXmlDelete( ns ); + + } +} + +void astXmlSetDTDec_( AstXmlDocument *this, const char *text1, + const char *text2, const char *text3, int *status ){ +/* +*+ +* Name: +* astXmlSetDTDec + +* Purpose: +* Set the Document Type declaration for a document. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlSetDTDEC( AstXmlDocument *this, const char *text1, +* const char *text2, const char *text3 ) + +* Description: +* This function stores an Document Type declaration of the form +* +* +* +* in the supplied document. Any previous DTD is removed. + +* Parameters: +* this +* The pointer to the document. +* text1 +* The document type name. +* text2 +* The text defining the external elements of the document type +* (may be NULL). +* text3 +* The text defining the internal elements of the document type +* (may be NULL). Do not include delimiting "[" and "]" characters. +*- +*/ + +/* Local Variables: */ + AstXmlDTDec *new; /* Pointer to new DT declaration */ + AstXmlPrologue *pro; /* Pointer to prologue */ + char *my_text2; /* Cleaned text2 */ + char *my_text3; /* Cleaned text3 */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Allocate space for the new structure. */ + new = (AstXmlDTDec *) astMalloc( sizeof( AstXmlDTDec ) ); + +/* Clean the text. */ + my_text2 = CleanText( text2, status ); + my_text3 = CleanText( text3, status ); + +/* Initialise it. */ + InitXmlDTDec( new, AST__XMLDTD, text1, my_text2, my_text3, status ); + +/* Free the memory */ + my_text2 = astFree( my_text2 ); + my_text3 = astFree( my_text3 ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) { + new = astXmlDelete( new ); + +/* Otherwise, store it in the document, deleting any existing declaration + first. */ + } else { + +/* Create a prologue if necessary. */ + if( !this->prolog ) this->prolog = NewPrologue( this, status ); + + pro = this->prolog; + if( pro->dtdec ) astXmlDelete( pro->dtdec ); + pro->dtdec = new; + } +} + +void astXmlSetXmlDec_( AstXmlDocument *this, const char *text, int *status ){ +/* +*+ +* Name: +* astXmlSetXmlDec + +* Purpose: +* Set the XML declaration for a document. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void astXmlSetXmlDec( AstXmlDocument *this, const char *text ) + +* Description: +* This function stores an XML declaration of the form +* +* +* +* in the supplied document. Any previous XML declaration is removed. + +* Parameters: +* this +* The pointer to the document. +* text +* The text to include in the XML declaration tag. +*- +*/ + +/* Local Variables: */ + AstXmlDeclPI *new; /* Pointer to new XML delcaration */ + AstXmlPrologue *pro; /* Pointer to prologue */ + char *my_text; /* Cleaned text */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Allocate space for the new structure. */ + new = (AstXmlDeclPI *) astMalloc( sizeof( AstXmlDeclPI ) ); + +/* Clean the text. */ + my_text = CleanText( text, status ); + +/* Initialise it. */ + InitXmlDeclPI( new, AST__XMLDEC, my_text, status ); + +/* Free the memory */ + my_text = astFree( my_text ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) { + new = astXmlDelete( new ); + +/* Otherwise, store it in the document, deleting any existing declaration + first. */ + } else { + +/* Create a prologue if necessary. */ + if( !this->prolog ) this->prolog = NewPrologue( this, status ); + + pro = this->prolog; + if( pro->xmldecl ) astXmlDelete( pro->xmldecl ); + pro->xmldecl = new; + } +} + +const char *astXmlShow_( AstXmlObject *this, int *status ) { +/* +*+ +* Name: +* astXmlShow + +* Purpose: +* Converts an XmlObject into a character string with indentation. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* const char *astXmlShow( AstXmlObject *this ) + +* Description: +* This function returns a pointer to a dynamically allocated string +* containing a textual representation of the supplied XmlObject. +* Newline characters are added to the string if needed to ensure that +* each item of content within an element starts on a new line, and all +* tags are preceded by an indentation string consisting of a number +* of spaces. + +* Parameters: +* this +* Pointer to the XmlObject to format. + +* Returned Value: +* Pointer to a null terminated string holding the formated XmlObject. +* This string should be freed when no longer needed using astFree. + +* Notes: +* - NULL is returned if a NULL pointer is supplied. +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + return Format( this, 0, status ); +} + +static void CheckName( const char *name, const char *noun, const char *method, + int nullok, int *status ){ +/* +* Name: +* CheckName + +* Purpose: +* Checks the supplied string is a valid XML name. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* void CheckName( const char *name, const char *noun, const char *method, +* int nullok, int *status ) + +* Description: +* This function checks that the supplied string is a valid XML name, +* and reports an error otherwise. + +* Parameters: +* name +* The name string to check +* noun +* A word to describe the object which the name applies to - for use in +* error messages only. +* method +* The name of the calling method - for use in error messages only. +* nullok +* If non-zero, then a null or empty name is assumed to be +* acceptable. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + const char *c; /* Pointer to next character to check */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the string is not null. */ + if( !name ) { + if( !nullok ) astError( AST__XMLNM, "%s: A NULL pointer was supplied " + "instead of an XML %s name.", status, method, noun ); + } else { + + c = name; + if( *c == 0 ) { + if( !nullok ) astError( AST__XMLNM, "%s: An empty string was supplied " + "instead of an XML %s name.", status, method, noun ); + } else { + + if( !isalpha( *c ) && *c != '_' ) { + astError( AST__XMLNM, "%s: The illegal XML %s name \"%s\" was " + "encountered.", status, method, noun, name ); + + } else { + while( *(++c) ) { + if( !isalnum( *c ) && *c != '_' && *c != '-' && *c != '.' ){ + astError( AST__XMLNM, "%s: The illegal XML %s name \"%s\" was " + "encountered.", status, method, noun, name ); + break; + } + } + } + } + } +} + +static void CheckPrefName( char *name, const char *noun, const char *method, int *status ){ +/* +* Name: +* CheckPrefName + +* Purpose: +* Checks the supplied string is a valid XML (prefix:)name. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* void CheckPrefName( char *name, const char *noun, const char *method, int *status ) + +* Description: +* This function checks that the supplied string is a valid XML +* (prefix:)name combination and reports an error otherwise. + +* Parameters: +* name +* The string to check +* noun +* A word to describe the object which the name applies to - for use in +* error messages only. +* method +* The name of the calling method - for use in error messages only. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + char *colon; /* Pointer to first colon */ + char *temp; /* Pointer to temporary string */ + int nc; /* Length of temporary string */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Search for a ":" character. */ + colon = strchr( name, ':' ); + +/* If found, temporarily convert the colon into a null so that it + terminates the prefix string. */ + if( colon ) { + *colon = 0; + +/* Check the string before the colon is a valid name. */ + temp = NULL; + temp = astAppendString( temp, &nc, noun ); + temp = astAppendString( temp, &nc, " prefix" ); + CheckName( name, temp, method, 0, status ); + temp = astFree( temp ); + +/* Restore the colon. */ + *colon = ':'; + +/* Check the string following the colon is a valid name. */ + CheckName( colon + 1, noun, method, 0, status ); + +/* If not found, the whole supplied string must be a name. */ + } else { + CheckName( name, noun, method, 0, status ); + } +} + +static int CheckType( long int given, long int want, int *status ){ +/* +* Name: +* CheckType + +* Purpose: +* Check that the supplied type identifies an object of a given class. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* int CheckType( long int given, long int want, int *status ) + +* Description: +* This function checks that the supplied type identifier identifies +* a specified class of XML object, or a derived class. A flag is +* returned indicating if the check succeeds. No error is reported if +* the check fails. + +* Parameters: +* given +* The type value to be checked. +* want +* The type of the required class. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the check is passed, zero if not of if an error has +* already occurred. + +* Notes: +* - This function attempts to execute even if the error status is set. +*/ + +/* Local Variables: */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the wanted type is recognised. Report an error if not. */ + if( want != AST__XMLOBJECT && + want != AST__XMLELEM && + want != AST__XMLATTR && + want != AST__XMLCHAR && + want != AST__XMLCDATA && + want != AST__XMLCOM && + want != AST__XMLPI && + want != AST__XMLNAME && + want != AST__XMLCONT && + want != AST__XMLPRO && + want != AST__XMLDEC && + want != AST__XMLDTD && + want != AST__XMLMISC && + want != AST__XMLBLACK && + want != AST__XMLWHITE && + want != AST__XMLPAR && + want != AST__XMLDOC ) { + if( astOK ) { + astError( AST__INTER, "CheckType(Xml): Unsupported XML object " + "type (%ld) supplied for parameter \"want\" (internal " + "AST programming error). ", status, want ); + } + +/* You should never be given a generic "interface" type since the + "wanted" value comes from the "type" component of an XmlObject (an explicit + class type should always be given). */ + } else if( given == AST__XMLPAR || + given == AST__XMLMISC || + given == AST__XMLCONT || + given == AST__XMLCHAR ) { + if( astOK ) { + astError( AST__INTER, "CheckType(Xml): Generic type (%ld) supplied for " + "parameter \"given\" (internal AST programming error).", status, + given ); + } + +/* If the above is OK, return a non-zero value if the type to be tested + equals the wanted type. */ + } else if( want == given ) { + result = 1; + +/* If any class of XmlObject is acceptable, check that he given class + type is a valid XML class type. */ + } else if( want == AST__XMLOBJECT ) { + result = ( given == AST__XMLELEM || + given == AST__XMLATTR || + given == AST__XMLCDATA || + given == AST__XMLCOM || + given == AST__XMLPI || + given == AST__XMLNAME || + given == AST__XMLPRO || + given == AST__XMLDEC || + given == AST__XMLDTD || + given == AST__XMLWHITE || + given == AST__XMLBLACK || + given == AST__XMLDOC ); + +/* Otherwise, for "interface" types, check if the given class "implements + the interface". */ + } else if( want == AST__XMLCONT ) { + result = ( given == AST__XMLELEM || + given == AST__XMLBLACK || + given == AST__XMLWHITE || + given == AST__XMLCDATA || + given == AST__XMLCOM || + given == AST__XMLPI ); + + } else if( want == AST__XMLMISC ) { + result = ( given == AST__XMLWHITE || + given == AST__XMLCOM || + given == AST__XMLPI ); + + } else if( want == AST__XMLCHAR ) { + result = ( given == AST__XMLWHITE || + given == AST__XMLBLACK ); + + } else if( want == AST__XMLPAR ) { + result = ( given == AST__XMLDOC || + given == AST__XMLPRO || + given == AST__XMLELEM ); + } + +/* Return the result. */ + return result; +} + +int astXmlCheckType_( void *this, long int want, int *status ){ +/* +*+ +* Name: +* astXmlCheckType + +* Purpose: +* Check that the supplied object is of a given class. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* int astXmlCheckType( void *this, long int want ) + +* Description: +* This function checks that the supplied XmlObject is of a specified +* class of XML object, or a derived class. A flag is returned indicating +* if the check succeeds. No error is reported if the check fails. + +* Parameters: +* this +* The object to check. +* want +* The type of the required class. + +* Returned Value: +* Non-zero if the check is passed, zero if not of if an error has +* already occurred. + +* Notes: +* - This function attempts to execute even if the error status is set. +*- +*/ + + if( this ) { + return CheckType( ((AstXmlObject *) this)->type, want, status ); + } else { + return 0; + } +} + +static char *CleanText( const char *text, int *status ){ +/* +* Name: +* CleanText + +* Purpose: +* Normalise end-of-lines in the supplied text. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* char *CleanText( const char *text, int *status ) + +* Description: +* This function returns a copy of "text in which "\r\n" has been +* replaced by "\n" and any remaining "\r" characters have been +* replaced by "\n". + +* Parameters: +* text +* A pointer to a text string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated string containing the required +* copy. + +* Notes: +* - NULL is returned if this function is called with the global error +* status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + char *d; /* Pointer to next returned character */ + char *result; /* Returned pointer */ + char *c; /* Pointer to next supplied character */ + char lc; /* Previous character */ + +/* Initialise */ + result = NULL; + +/* Return if the pointer is NULL or if an error has occurred. */ + if( !astOK || !text ) return result; + +/* Take a copy of the supplied text */ + result = astStore( NULL, text, strlen( text ) + 1 ); + +/* Clean the text by replacing "\r\n" by "\n". */ + c = result - 1; + d = c; + lc = 0; + while( *(++c) ) { + if( *c != '\n' || lc != '\r' ) d++; + *d = ( lc = *c ); + } + *(++d) = 0; + +/* Now further clean it by replacing "\r" by "\n". */ + c = result - 1; + while( *(++c) ) { + if( *c == '\r' ) *c = '\n'; + } + +/* Return the result. */ + return result; +} + +static void CleanXml( AstXmlObject *this, long int type, int *status ){ +/* +* Name: +* CleanXml + +* Purpose: +* Free the resources used within an XmlObject. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* void CleanXml( AstXmlObject *this, long int type, int *status ) + +* Description: +* This function frees the resources used internally within the +* supplied XmlObject. + +* Parameters: +* this +* pointer to the XmlObject to be cleaned. +* type +* The type of XmlObject being cleaned. +* status +* Pointer to the inherited status variable. + +* Notes: +* This function attempts to execute even if an error has already +* occurred. +*- +*/ + +/* Local Variables: */ + AstXmlAttribute *attr; + AstXmlBlack *black; + AstXmlCDataSection *cdatasec; + AstXmlComment *comm; + AstXmlDTDec *dtd; + AstXmlDeclPI *dec; + AstXmlDocument *doc; + AstXmlElement *elem; + AstXmlNamespace *ns; + AstXmlPI *pi; + AstXmlPrologue *pro; + AstXmlWhite *white; + +/* Return if a NULL pointer has been suppplied. */ + if( !this ) return; + +/* For the base XmlObject class, clear the object type, etc. */ + if( type == AST__XMLOBJECT ){ + this->type = AST__XMLBAD; + this->parent = NULL; + +/* For each derived class of XmlObject, first clean the parent component, + then clean any further resources. */ + } else if( type == AST__XMLELEM ){ + + elem = (AstXmlElement *) this; + + elem->name = astFree( elem->name ); + elem->defns = astFree( elem->defns ); + elem->prefix = astFree( elem->prefix ); + + while( elem->nattr > 0 ) astXmlDelete( elem->attrs[ 0 ] ); + elem->attrs = astFree( elem->attrs ); + + while( elem->nitem > 0 ) astXmlDelete( elem->items[ 0 ] ); + elem->items = astFree( elem->items ); + + while( elem->nnspref > 0 ) astXmlDelete( elem->nsprefs[ 0 ] ); + elem->nsprefs = astFree( elem->nsprefs ); + + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLATTR ){ + attr = (AstXmlAttribute *) this; + attr->name = astFree( attr->name ); + attr->value = astFree( attr->value ); + attr->prefix = astFree( attr->prefix ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLBLACK ){ + black = (AstXmlBlack *) this; + black->text = astFree( black->text ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLWHITE ){ + white = (AstXmlWhite *) this; + white->text = astFree( white->text ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLCDATA ){ + cdatasec = (AstXmlCDataSection *) this; + cdatasec->text = astFree( cdatasec->text ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLCOM ){ + comm = (AstXmlComment *) this; + comm->text = astFree( comm->text ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLPI ){ + pi = (AstXmlPI *) this; + pi->target = astFree( pi->target ); + pi->text = astFree( pi->text ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLNAME ){ + ns = (AstXmlNamespace *) this; + ns->prefix = astFree( ns->prefix ); + ns->uri = astFree( ns->uri ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLDOC ){ + doc = (AstXmlDocument *) this; + doc->prolog = astXmlDelete( doc->prolog ); + doc->root = astXmlDelete( doc->root ); + while( doc->nepi > 0 ) astXmlDelete( doc->epilog[ 0 ] ); + doc->epilog = astFree( doc->epilog ); + doc->current = NULL; + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLPRO ){ + pro = (AstXmlPrologue *) this; + pro->xmldecl = astXmlDelete( pro->xmldecl ); + while( pro->nmisc1 > 0 ) astXmlDelete( pro->misc1[ 0 ] ); + pro->misc1 = astFree( pro->misc1 ); + pro->dtdec = astXmlDelete( pro->dtdec ); + while( pro->nmisc2 > 0 ) astXmlDelete( pro->misc2[ 0 ] ); + pro->misc2 = astFree( pro->misc2 ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLDEC ){ + dec = (AstXmlDeclPI *) this; + dec->text = astFree( dec->text ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( type == AST__XMLDTD ){ + dtd = (AstXmlDTDec *) this; + dtd->name = astFree( dtd->name ); + dtd->external = astFree( dtd->external ); + dtd->internal = astFree( dtd->internal ); + CleanXml( this, AST__XMLOBJECT, status ); + + } else if( astOK ) { + astError( AST__INTER, "CleanXml: Invalid object type (%ld) supplied " + "(internal AST programming error).", status, type ); + } + +} + +static const char *DefaultURI( AstXmlElement *elem, int *status ){ +/* +* Name: +* DefaultURI + +* Purpose: +* Find the URI associated with the default namespace. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* const char *DefaultURI( AstXmlElement *elem, int *status ) + +* Description: +* This function returns the default namespace URI defined within the +* given element. If the element does not define a default namespace URI, +* then this function is called recursively on the parent element. If +* there is no parent element, NULL is returned. + +* Parameters: +* elem +* The pointer to the XmlElement. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a string holding the URI, or NULL if not found. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlParent *parent; /* Parent of "this" */ + const char *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status, and the supplied element. */ + if( !astOK || !elem ) return result; + +/* If the supplied element defines a default namespace URI, return it. + Otherwise, call this function to get the default namespace URI from the + parent element. */ + result = elem->defns; + if( !result ) { + parent = ( (AstXmlObject *) elem )->parent; + if( astXmlCheckType( parent, AST__XMLELEM ) ) { + result = DefaultURI( (AstXmlElement *) parent, status ); + } + } + +/* If the element has a blank default namespace URI, then return NULL + since the XML namespaces specification says that "The default + namespace can be set to the empty string. This has the same effect, + within the scope of the declaration, of there being no default + namespace". */ + if( result && astChrLen( result ) == 0 ) result = NULL; + +/* Return the result. */ + return result; +} + +void *astXmlDelete_( void *obj_ptr, int *status ){ +/* +*+ +* Name: +* astXmlDelete + +* Purpose: +* Remove the supplied XmlObject from its parent and delete it. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* void *astXmlDelete( void *obj ) + +* Description: +* This function removes the supplied XmlObject from its parent and +* deletes it using astXmlAnnul. + +* Parameters: +* obj +* The pointer to the XmlObject to be deleted. + +* Returned Value: +* NULL + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*- +*/ + +/* Local Variables: */ + AstXmlDocument *doc; /* Pointer to XM document */ + AstXmlElement *elem; /* Pointer to XML element */ + AstXmlObject *obj; /* Pointer to XmlObject */ + AstXmlParent *parent; /* Pointer to parent */ + AstXmlPrologue *pro; /* Pointer to XML prologue */ + int i; /* Loop counter */ + int j; /* Loop counter */ + int n; /* Number of values in list */ + int ok; /* Is obj a child of its parent? */ + void *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + ok = 0; + +/* Check we have an XmlObject. */ + if( !astXmlCheckType( obj_ptr, AST__XMLOBJECT ) ) return result; + +/* Get the parent of the supplied object. */ + obj = (AstXmlObject *) obj_ptr; + parent = obj->parent; + if( parent ) { + +/* First deal with cases where we are deleting items from a document. */ + if( astXmlCheckType( parent, AST__XMLDOC ) ) { + doc = (AstXmlDocument *) parent; + + if( astXmlCheckType( obj, AST__XMLPRO ) ) { + if( (AstXmlPrologue *) obj == doc->prolog ) { + doc->prolog = NULL; + ok = 1; + } + + } else if( astXmlCheckType( obj, AST__XMLELEM ) ) { + if( (AstXmlElement *) obj == doc->root ) { + doc->root = NULL; + ok = 1; + } + + } else if( astXmlCheckType( obj, AST__XMLMISC ) ) { + n = doc->nepi; + for( i = 0; i < n; i++ ) { + if( doc->epilog[ i ] == (AstXmlMiscItem *) obj ) { + for( j = i + 1; j < n; j++ ) { + doc->epilog[ j - 1 ] = doc->epilog[ j ]; + } + doc->epilog[ --doc->nepi ] = NULL; + ok = 1; + break; + } + } + + } else if( astOK ) { + astError( AST__INTER, "astXmlDelete(xml): XmlObject of type %ld has " + "inappropriate parent of type %ld (internal AST " + "programming error).", status, obj->type, parent->type ); + } + +/* Now deal with cases where we are deleting items from a prologue. */ + } else if( astXmlCheckType( parent, AST__XMLPRO ) ) { + pro = (AstXmlPrologue *) parent; + + if( astXmlCheckType( obj, AST__XMLDEC ) ) { + if( (AstXmlDeclPI *) obj == pro->xmldecl ) { + pro->xmldecl = NULL; + ok = 1; + } + + } else if( astXmlCheckType( obj, AST__XMLDTD ) ) { + if( (AstXmlDTDec *) obj == pro->dtdec ) { + pro->dtdec = NULL; + ok = 1; + } + + } else if( astXmlCheckType( obj, AST__XMLMISC ) ) { + n = pro->nmisc1; + for( i = 0; i < n; i++ ) { + if( pro->misc1[ i ] == (AstXmlMiscItem *) obj ) { + for( j = i + 1; j < n; j++ ) { + pro->misc1[ j - 1 ] = pro->misc1[ j ]; + } + pro->misc1[ --pro->nmisc1 ] = NULL; + ok = 1; + break; + } + } + + if( !ok ) { + n = pro->nmisc2; + for( i = 0; i < n; i++ ) { + if( pro->misc2[ i ] == (AstXmlMiscItem *) obj ) { + for( j = i + 1; j < n; j++ ) { + pro->misc2[ j - 1 ] = pro->misc2[ j ]; + } + pro->misc2[ --pro->nmisc2 ] = NULL; + ok = 1; + break; + } + } + } + + } else if( astOK ) { + astError( AST__INTER, "astXmlDelete(xml): XmlObject of type %ld has " + "inappropriate parent of type %ld (internal AST " + "programming error).", status, obj->type, parent->type ); + } + +/* Now deal with cases where we are deleting items from an element. */ + } else if( astXmlCheckType( parent, AST__XMLELEM ) ) { + elem = (AstXmlElement *) parent; + +/* Remove the object form the appropriate list in the parent, and + then shuffle down the remaining entries in the list and decrement the + size of the list. */ + if( astXmlCheckType( obj, AST__XMLATTR ) ) { + n = elem->nattr; + for( i = 0; i < n; i++ ) { + if( elem->attrs[ i ] == (AstXmlAttribute *) obj ) { + for( j = i + 1; j < n; j++ ) { + elem->attrs[ j - 1 ] = elem->attrs[ j ]; + } + elem->attrs[ --elem->nattr ] = NULL; + ok = 1; + break; + } + } + + } else if( astXmlCheckType( obj, AST__XMLNAME ) ) { + n = elem->nnspref; + for( i = 0; i < n; i++ ) { + if( elem->nsprefs[ i ] == (AstXmlNamespace *) obj ) { + for( j = i + 1; j < n; j++ ) { + elem->nsprefs[ j - 1 ] = elem->nsprefs[ j ]; + } + elem->nsprefs[ --elem->nnspref ] = NULL; + ok = 1; + break; + } + } + + } else if( astXmlCheckType( obj, AST__XMLCONT ) ) { + n = elem->nitem; + for( i = 0; i < n; i++ ) { + if( elem->items[ i ] == (AstXmlContentItem *) obj ) { + for( j = i + 1; j < n; j++ ) { + elem->items[ j - 1 ] = elem->items[ j ]; + } + elem->items[ --elem->nitem ] = NULL; + ok = 1; + break; + } + } + } + + } else if( astOK ) { + astError( AST__INTER, "astXmlDelete(xml): XmlObject of type %ld has " + "inappropriate parent of type %ld (internal AST " + "programming error).", status, obj->type, parent->type ); + } + +/* Nullify the parent pointer so that astXmlAnnul will delete the object. */ + obj->parent = NULL; + +/* If the supplied object has no parent, we can continue to annul it. */ + } else { + ok = 1; + } + +/* Report an error if required. */ + if( !ok && astOK ) { + astError( AST__INTER, "astXmlDelete(xml): Supplied XmlObject (type %ld) " + "is not owned by its own parent (internal AST " + "programming error).", status, obj->type ); + } + +/* Delete the object. */ + result = astXmlAnnul( obj ); + +/* Annul the object and return the resulting NULL pointer. */ + return result; +} + +static AstXmlAttribute *FindAttribute( AstXmlElement *this, const char *name0, + int *status ){ +/* +* Name: +* FindAttribute + +* Purpose: +* Search an XmlElement for a named attribute + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* AstXmlAttribute *FindAttribute( AstXmlElement *this, const char *name0, +* int *status ) + +* Description: +* This function searches the supplied XmlElement for an attribute +* with the given name. If found, a pointer to the XmlAttribute is +* returned. Otherwise NULL is returned. + +* Parameters: +* this +* The pointer to the XmlElement. +* name0 +* Pointer to a string holding the name of the attribute. The name +* may be preceded with a "prefix:" string, in which case the +* prefix will also be matched. If no prefix is included, the first +* attribute with the specified name is returned, regardless of +* its prefix. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the XmlAttribute, or NULL if not found. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlAttribute *result; /* Returned pointer */ + char name_buffer[ 50 ]; /* Buffer for name */ + char prefix_buffer[ 50 ]; /* Buffer for prefix */ + const char *colon; /* Pointer to colon in supplied string */ + const char *name1; /* Pointer to name to be checked */ + const char *name; /* Pointer to name to be searched for */ + const char *prefix1; /* Pointer to prefix to be checked */ + const char *prefix; /* Pointer to prefix to be searched for */ + int i; /* Loop count */ + size_t len; /* Length of string */ + +/* Initialise */ + result = NULL; + name = name0; + prefix = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* If the supplied name string contains a colon, split it up into prefix + and name. */ + if( ( colon = strchr( name0, ':' ) ) ) { + len = colon - name0; + + if( len > 49 ) { + astError( AST__XMLNM, "FindAttribute: The XML prefix in \"%s\" " + "is too long (> 49 characters).", status, name0 ); + } else { + strncpy( prefix_buffer, name0, len ); + prefix_buffer[ len ] = 0; + prefix = prefix_buffer; + len = strlen( colon + 1 ); + + if( len > 49 ) { + astError( AST__XMLNM, "FindAttribute: The XML attribute name " + "in \"%s\" is too long (> 49 characters).", status, name0 ); + } else { + strcpy( name_buffer, colon + 1 ); + name = name_buffer; + } + + } + + } + +/* Loop round all the attributes in the element. */ + for( i = 0; i < this->nattr; i++ ) { + name1 = this->attrs[ i ]->name; + prefix1 = this->attrs[ i ]->prefix; + +/* Compare the attribute name (and prefix) with the supplied name (and + prefix). Leave the loop if they match. */ + if( !strcmp( name1, name ) && + ( !prefix || ( prefix1 && !strcmp( prefix1, prefix ) ) ) ) { + result = this->attrs[ i ]; + break; + } + } + +/* Return the result. */ + return result; +} + +static const char *Format( AstXmlObject *this, int ind, int *status ){ +/* +* Name: +* Format + +* Purpose: +* Converts an XmlObject into a character string. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* const char *Format( AstXmlObject *this, int ind, int *status ) + +* Description: +* This function returns a pointer to a dynamically allocated string +* containing a textual representation of the supplied XmlObject. + +* Parameters: +* this +* Pointer to the XmlObject to format. +* ind +* If the XmlObject is an element, then each content item within +* the element will be prefixed by a string containing "ind" spaces +* (indenting the returned element itself is the responsibility of +* the caller and so "this" is not itself indented within this function). +* In addition, a newline character will be included at the start +* of the prefix if required, to ensure that each new item starts +* on a new line. If "ind" is less than zero, then no prefixes are +* added. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a null terminated string holding the formated XmlObject. +* This string should be freed when no longer needed using astFree. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlPrologue *pro; /* Pointer to XML prologue */ + AstXmlDocument *doc; /* Pointer to XML document */ + AstXmlAttribute *attrib; /* Pointer to XML attribute */ + AstXmlWhite *white; /* Pointer to character data */ + AstXmlBlack *black; /* Pointer to character data */ + AstXmlElement *elem; /* Pointer to XML element */ + AstXmlNamespace *ns; /* Pointer to XML namespace instruction */ + char *result; /* The returned pointer */ + const char *temp; /* A temporary string pointer */ + int i; /* Loop count */ + int nc; /* Length of returned string */ + int type; /* Object type */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK || !this ) return result; + +/* Get the object type */ + type = this->type; + +/* If this is an element... */ + if( this->type == AST__XMLELEM ) { + temp = FormatTag( this, 1, status ); + result = astAppendString( result, &nc, temp ); + temp = astFree( (void *) temp ); + + elem = (AstXmlElement *) this; + if( elem->nitem > 0 ) { + +/* Go round all the items of content. */ + for( i = 0; i < elem->nitem; i++ ) { + +/* Ignore whitespace elements unless we are not producing indentation. */ + if( !astXmlCheckType( elem->items[ i ], AST__XMLWHITE ) || + ind < 0 ) { + +/* Format the item */ + temp = Format( (AstXmlObject *) elem->items[ i ], ( ( ind > -1 ) ? ind + IND_INC : -1 ), status ); + if( temp ) { + +/* Now append the next item of content, and free its memory. */ + if( ind > -1 ) { + result = AppendLine( result, &nc, temp, + ( (ind > -1) ? ind + IND_INC : -1 ), status ); + } else { + result = astAppendString( result, &nc, temp ); + } + temp = astFree( (void *) temp ); + } + } + } + +/* Finally append the end tag. */ + temp = FormatTag( this, 0, status ); + if( ind > -1 ) { + result = AppendLine( result, &nc, temp, ind, status ); + } else { + result = astAppendString( result, &nc, temp ); + } + temp = astFree( (void *) temp ); + + } + +/* If this is an attribute... */ + } else if( type == AST__XMLATTR ){ + attrib = (AstXmlAttribute *) this; + + if( attrib->prefix ) { + result = astAppendString( result, &nc, attrib->prefix ); + result = astAppendString( result, &nc, ":" ); + } + + temp = AddEscapes( attrib->value, status ); + result = astAppendString( result, &nc, attrib->name ); + result = astAppendString( result, &nc, "=\"" ); + result = astAppendString( result, &nc, temp ); + result = astAppendString( result, &nc, "\"" ); + temp = astFree( (void *) temp ); + + } else if( type == AST__XMLWHITE ){ + white = (AstXmlWhite *) this; + temp = AddEscapes( white->text, status ); + result = astAppendString( result, &nc, temp ); + temp = astFree( (void *) temp ); + + } else if( type == AST__XMLBLACK ){ + black = (AstXmlBlack *) this; + temp = AddEscapes( black->text, status ); + result = astAppendString( result, &nc, temp ); + temp = astFree( (void *) temp ); + + } else if( type == AST__XMLCDATA || + type == AST__XMLCOM || + type == AST__XMLPI || + type == AST__XMLDEC || + type == AST__XMLDTD ){ + + temp = FormatTag( this, 1, status ); + result = astAppendString( result, &nc, temp ); + temp = astFree( (void *) temp ); + + } else if( type == AST__XMLNAME ){ + ns = (AstXmlNamespace *) this; + result = astAppendString( result, &nc, "xmlns:" ); + result = astAppendString( result, &nc, ns->prefix ); + result = astAppendString( result, &nc, "=\"" ); + result = astAppendString( result, &nc, ns->uri ); + result = astAppendString( result, &nc, "\"" ); + + } else if( type == AST__XMLPRO ){ + pro = (AstXmlPrologue *) this; + result = astAppendString( result, &nc, + Format( (AstXmlObject *) pro->xmldecl, ind, status ) ); + +/* Append all the miscalleneous items before the DTD. */ + for( i = 0; i < pro->nmisc1; i++ ) { + temp = Format( (AstXmlObject *) pro->misc1[ i ], ind, status ); + if( temp ) { + if( ind > -1 ) { + result = AppendLine( result, &nc, temp, ind, status ); + } else { + result = astAppendString( result, &nc, temp ); + } + temp = astFree( (void *) temp ); + } + } + +/* Append the DTD. */ + temp = Format( (AstXmlObject *) pro->dtdec, ind, status ); + if( temp ) { + if( ind > -1 ) { + result = AppendLine( result, &nc, temp, ind, status ); + } else { + result = astAppendString( result, &nc, temp ); + } + temp = astFree( (void *) temp ); + } + +/* Append all the miscalleneous items after the DTD. */ + for( i = 0; i < pro->nmisc2; i++ ) { + temp = Format( (AstXmlObject *) pro->misc2[ i ], ind, status ); + if( temp ) { + if( ind > -1 ) { + result = AppendLine( result, &nc, temp, ind, status ); + } else { + result = astAppendString( result, &nc, temp ); + } + temp = astFree( (void *) temp ); + } + } + + } else if( type == AST__XMLDOC ){ + doc = (AstXmlDocument *) this; + +/* Format the prologue. */ + result = astAppendString( result, &nc, + Format( (AstXmlObject *) doc->prolog, ind, status ) ); + +/* Append the root element. */ + temp = Format( (AstXmlObject *) doc->root, ind, status ); + if( temp ) { + if( ind > -1 ) { + result = AppendLine( result, &nc, temp, ind, status ); + } else { + result = astAppendString( result, &nc, temp ); + } + temp = astFree( (void *) temp ); + } + +/* Append all the miscalleneous items in the epilogue. */ + for( i = 0; i < doc->nepi; i++ ) { + temp = Format( (AstXmlObject *) doc->epilog[ i ], ind, status ); + if( temp ) { + if( ind > -1 ) { + result = AppendLine( result, &nc, temp, ind, status ); + } else { + result = astAppendString( result, &nc, temp ); + } + temp = astFree( (void *) temp ); + } + } + + } else if( astOK ) { + astError( AST__INTER, "Format(xml): Invalid object type (%d) supplied " + "(internal AST programming error).", status, type ); + } + +/* Free the returned string if an error has occurred. */ + if( !astOK ) result = astFree( result ); + +/* Return the result. */ + return result; +} + +static char *FormatTag( AstXmlObject *this, int opening, int *status ){ +/* +* Name: +* FormatTag + +* Purpose: +* Returns a string holding an XML tag describing the given XmlObject. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* char *FormatTag( AstXmlObject *this, int opening, int *status ) + +* Description: +* This function returns a pointer to a dynamic string containing an +* XML tag describing the given XmlObject. + +* Parameters: +* this +* Pointer to the XmlObject. +* opening +* Indicates which tag is to be returned; the start tag or the end +* tag. If non-zero the start tag is returned. Otherwise, the +* end tag is returned. If the supplied XmlObject has no end +* tag (i.e. if it is an empty element, or if it is not an element), +* then NULL is returned but no error is reported. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a dynamically allocated string holding the tag. + +* Notes: +* - Empty elements are represented as an start tag of the form <.../>, +* with no corresponding end tag. +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*- +*/ + + +/* Local Variables: */ + AstXmlCDataSection *cdata;/* Pointer to XML CDATA section */ + AstXmlElement *elem; /* Pointer to XML element */ + AstXmlComment *com; /* Pointer to XML comment */ + AstXmlPI *pi; /* Pointer to XML processing instruction */ + AstXmlDTDec *dtd; /* Pointer to XML data type declaration */ + AstXmlDeclPI *xmlpi; /* XML version declaration */ + char *result; /* The returned pointer */ + const char *temp; /* A temporary string pointer */ + int i; /* Loop count */ + int nc; /* Length of returned string */ + int type; /* Object type */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Get the object type */ + type = this->type; + +/* If this is an element... */ + if( this->type == AST__XMLELEM ) { + elem = (AstXmlElement *) this; + + if( opening ) { + result = astAppendString( result, &nc, "<" ); + if( elem->prefix ) { + result = astAppendString( result, &nc, elem->prefix ); + result = astAppendString( result, &nc, ":" ); + } + result = astAppendString( result, &nc, elem->name ); + + if( elem->defns ) { + result = astAppendString( result, &nc, " xmlns=\"" ); + result = astAppendString( result, &nc, elem->defns ); + result = astAppendString( result, &nc, "\"" ); + } + + for( i = 0; i < elem->nnspref; i++ ) { + temp = Format( (AstXmlObject *) elem->nsprefs[ i ], -1, status ); + if( temp ) { + result = AppendChar( result, &nc, ' ', status ); + result = astAppendString( result, &nc, temp ); + temp = astFree( (void *) temp ); + } + } + + for( i = 0; i < elem->nattr; i++ ) { + temp = Format( (AstXmlObject *) elem->attrs[ i ], -1, status ); + if( temp ){ + result = AppendChar( result, &nc, ' ', status ); + result = astAppendString( result, &nc, temp ); + temp = astFree( (void *) temp ); + } + } + + if( elem->nitem == 0 ) result = astAppendString( result, &nc, "/" ); + result = astAppendString( result, &nc, ">" ); + + } else if( elem->nitem > 0 ) { + result = astAppendString( result, &nc, "prefix ) { + result = astAppendString( result, &nc, elem->prefix ); + result = astAppendString( result, &nc, ":" ); + } + result = astAppendString( result, &nc, elem->name ); + result = astAppendString( result, &nc, ">" ); + } + + } else if( type == AST__XMLDTD ){ + dtd = (AstXmlDTDec *) this; + if( opening && dtd->name && dtd->name[0] ) { + result = astAppendString( result, &nc, "name ); + if( dtd->external && dtd->external[ 0 ] ) { + result = astAppendString( result, &nc, " " ); + result = astAppendString( result, &nc, dtd->external ); + } + if( dtd->internal && dtd->internal[ 0 ] ) { + result = astAppendString( result, &nc, " [" ); + result = astAppendString( result, &nc, dtd->internal ); + result = astAppendString( result, &nc, "]" ); + } + result = astAppendString( result, &nc, ">" ); + } + + } else if( type == AST__XMLCDATA ){ + if( opening ) { + cdata = (AstXmlCDataSection *) this; + result = astAppendString( result, &nc, "text ); + result = astAppendString( result, &nc, "]]>" ); + } + + } else if( type == AST__XMLCOM ){ + if( opening ) { + com = (AstXmlComment *) this; + result = astAppendString( result, &nc, "" ); + } + + } else if( type == AST__XMLPI ){ + pi = (AstXmlPI *) this; + if( opening ) { + result = astAppendString( result, &nc, "target ); + if( pi->text && pi->text[0] ) { + result = astAppendString( result, &nc, " " ); + result = astAppendString( result, &nc, pi->text ); + } + result = astAppendString( result, &nc, "?>" ); + } + + } else if( type == AST__XMLDEC ){ + xmlpi = (AstXmlDeclPI *) this; + if( opening && xmlpi->text && xmlpi->text[0] ) { + result = astAppendString( result, &nc, "text && xmlpi->text[0] ) { + result = astAppendString( result, &nc, " " ); + result = astAppendString( result, &nc, xmlpi->text ); + } + result = astAppendString( result, &nc, "?>" ); + } + } + +/* If notOK, free the rteurned string. */ + if( !astOK ) result = astFree( result ); + +/* Return the result. */ + return result; +} + +static void InitXmlAttribute( AstXmlAttribute *new, int type, const char *name, + const char *value, const char *prefix, int *status ){ +/* +* Name: +* InitXmlAttribute + +* Purpose: +* Initialise a new XmlAttribute. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlAttribute( AstXmlAttribute *new, int type, const char *name, +* const char *value, const char *prefix, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlAttribute +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* name +* The name for the attribute. +* value +* The value for the attribute +* prefix +* The namespace prefix for the attribute. May be NULL or blank, in +* which case any prefix at the start of "name" is used. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + const char *colon; /* Pointer to colon within supplied name */ + char *newname; /* Pointer to name string (no prefix) */ + char *newpref; /* Pointer to name string */ + int nc; /* Length of prefix string */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLATTR, status ) ){ + astError( AST__INTER, "InitXmlAttribute: Supplied object type (%d) " + "does not represent an XmlAttribute", status, type ); + } + +/* Ensure we have non-NULL pointers. */ + if( !name ) name = ""; + if( !value ) value = ""; + +/* If no prefix was supplied, extract any prefix from the start of the + supplied name. */ + newname = (char *) name; + newpref = (char *) prefix; + colon = NULL; + + if( !prefix || astChrLen( prefix ) == 0 ){ + colon = strchr( name, ':' ); + if( colon ) { + nc = colon - name; + newpref = astStore( NULL, name, nc + 1 ); + newpref[ nc ] = 0; + + nc = strlen( name ) - ( colon - name ) - 1; + newname = astStore( NULL, colon + 1, nc + 1 ); + newname[ nc ] = 0; + } + } + +/* Check the supplied name and prefix are valid XML 'names'. */ + CheckName( newname, "attribute", "InitXmlAttribute", 0, status ); + CheckName( newpref, "attribute", "InitXmlAttribute", 1, status ); + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Initialise the items specific to this class of structure. */ + new->name = astStore( NULL, newname, strlen( newname ) + 1 ); + new->value = astStore( NULL, value, strlen( value ) + 1 ); + new->prefix = NULL; + if( newpref ) { + nc = strlen( newpref ); + if( nc > 0 ) new->prefix = astStore( NULL, newpref, nc + 1 ); + } + +/* Free any name and prefix extracted from the supplied name string */ + if( colon ) { + newname = astFree( newname ); + newpref = astFree( newpref ); + } +} + +static void InitXmlCDataSection( AstXmlCDataSection *new, int type, + const char *text, int *status ){ +/* +* Name: +* InitXmlCDataSection + +* Purpose: +* Initialise a new XmlCDataSection. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlCDataSection( AstXmlCDataSection *new, int type, +* const char *text, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlCDataSection +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* text +* Pointer to a null terminated string holding the text. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLCDATA, status ) ){ + astError( AST__INTER, "InitXmlCDataSection: Supplied object type (%d) " + "does not represent an XmlCDataSection", status, type ); + } + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Ensure we have non-NULL pointers. */ + if( !text ) text = ""; + +/* Initialise the items specific to this class of structure. */ + new->text = astStore( NULL, text, strlen( text ) + 1 ); +} + +static void InitXmlWhite( AstXmlWhite *new, int type, const char *text, int *status ){ +/* +* Name: +* InitXmlWhite + +* Purpose: +* Initialise a new XmlWhite. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlWhite( AstXmlWhite *new, int type, const char *text, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlWhite +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* text +* Pointer to a null terminated string holding the text. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + const char *c; /* Pointer to next character */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLWHITE, status ) ){ + astError( AST__INTER, "InitXmlWhite: Supplied object type (%d) " + "does not represent an XmlWhite", status, type ); + } + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Ensure we have non-NULL pointers. */ + if( !text ) text = ""; + +/* Report an error if the text is not white. */ + c = text - 1; + while( *(++c) ) { + if( !isspace( *c ) ) { + astError( AST__XMLCM, "InitXmlWhite(xml): Illegal XML whitespace " + "string supplied \"%s\" - not all characters are white.", status, + text ); + break; + } + } + +/* Initialise the items specific to this class of structure. */ + new->text = astStore( NULL, text, strlen( text ) + 1 ); +} + +static void InitXmlBlack( AstXmlBlack *new, int type, const char *text, int *status ){ +/* +* Name: +* InitXmlBlack + +* Purpose: +* Initialise a new XmlBlack. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlBlack( AstXmlBlack *new, int type, const char *text, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlBlack +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* text +* Pointer to a null terminated string holding the text. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLBLACK, status ) ){ + astError( AST__INTER, "InitXmlBlack: Supplied object type (%d) " + "does not represent an XmlBlack", status, type ); + } + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Ensure we have non-NULL pointers. */ + if( !text ) text = ""; + +/* Initialise the items specific to this class of structure. */ + new->text = astStore( NULL, text, strlen( text ) + 1 ); +} + +static void InitXmlComment( AstXmlComment *new, int type, const char *text, int *status ){ +/* +* Name: +* InitXmlComment + +* Purpose: +* Initialise a new XmlComment. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlComment( AstXmlComment *new, int type, const char *text, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlComment +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* text +* Pointer to a null terminated string holding the text. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLCOM, status ) ){ + astError( AST__INTER, "InitXmlComment: Supplied object type (%d) " + "does not represent an XmlComment", status, type ); + } + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Ensure we have non-NULL pointers. */ + if( !text ) text = ""; + +/* Initialise the items specific to this class of structure. Report an error + if the comment is illegal. */ + if( strstr( text, "--" ) && astOK ) { + astError( AST__XMLCM, "InitXmlCom(xml): Illegal XML comment " + "supplied \"%s\" - comments may not contain the " + "string \"--\".", status, text ); + new->text = NULL; + } else { + new->text = astStore( NULL, text, strlen( text ) + 1 ); + } +} + +static void InitXmlDeclPI( AstXmlDeclPI *new, int type, const char *text, int *status ){ +/* +* Name: +* InitXmlDeclPI + +* Purpose: +* Initialise a new XmlDeclPI. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlDeclPI( AstXmlDeclPI *new, int type, const char *text, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlDeclPI +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* text +* Pointer to a null terminated string holding the text. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLDEC, status ) ){ + astError( AST__INTER, "InitXmlDeclPI: Supplied object type (%d) " + "does not represent an XmlDeclPI", status, type ); + } + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Ensure we have non-NULL pointers. */ + if( !text ) text = ""; + +/* Initialise the items specific to this class of structure. */ + new->text = astStore( NULL, text, strlen( text ) + 1 ); +} + +static void InitXmlDocument( AstXmlDocument *new, int type, int *status ){ +/* +* Name: +* InitXmlDocument + +* Purpose: +* Initialise a new XmlDocument. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlDocument( AstXmlDocument *new, int type, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlDocument +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLDOC, status ) ){ + astError( AST__INTER, "InitXmlDocument: Supplied object type (%d) " + "does not represent an XmlDocument", status, type ); + } + +/* Initialise the parent XmlObject */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Initialise the items specific to this class of structure. */ + new->prolog = NULL; + new->root = NULL; + new->epilog = NULL; + new->nepi = 0; + new->current = NULL; +} + +static void InitXmlDTDec( AstXmlDTDec *new, int type, const char *name, + const char *external, const char *internal, int *status ){ +/* +* Name: +* InitXmlDTDec + +* Purpose: +* Initialise a new XmlDTDec. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* void InitXmlDTDec( AstXmlDTDec *new, int type, const char *name, +* const char *external, const char *internal, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlDTDec +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* name +* The document type name +* external +* The external SYSTEM id. +* internal +* The internal declaration markup text (this is not checked or +* parsed). +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLDTD, status ) ){ + astError( AST__INTER, "InitXmlDTDec: Supplied object type (%d) " + "does not represent an XmlDTDec", status, type ); + } + +/* Initialise the parent XmlObject */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Ensure we have non-NULL pointers. */ + if( !name ) name = ""; + if( !external ) external = ""; + if( !internal ) internal = ""; + +/* Initialise the items specific to this class of structure. */ + new->name = astStore( NULL, name, strlen( name ) + 1 ); + new->external = astStore( NULL, external, strlen( external ) + 1 ); + new->internal = astStore( NULL, internal, strlen( internal ) + 1 ); +} + +static void InitXmlElement( AstXmlElement *new, int type, const char *name, + const char *prefix, int *status ){ +/* +* Name: +* InitXmlElement + +* Purpose: +* Initialise a new XmlElement. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlElement( AstXmlElement *new, int type, const char *name, +* const char *prefix, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlElement +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* name +* The name for the element. +* prefix +* The namespace prefix for the element. May be NULL or blank, in +* which case any prefix at the start of "name" is used. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + const char *colon; /* Pointer to colon within supplied name */ + char *newname; /* Pointer to name string (no prefix) */ + char *newpref; /* Pointer to name string */ + int nc; /* Length of prefix string */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLELEM, status ) ){ + astError( AST__INTER, "InitXmlElement: Supplied object type (%d) " + "does not represent an XmlElement", status, type ); + } + +/* Ensure we have non-NULL pointers. */ + if( !name ) name = ""; + +/* If no prefix was supplied, extract any prefix from the start of the + supplied name. */ + newname = (char *) name; + newpref = (char *) prefix; + colon = NULL; + + if( !prefix || astChrLen( prefix ) == 0 ){ + colon = strchr( name, ':' ); + if( colon ) { + nc = colon - name; + newpref = astStore( NULL, name, nc + 1 ); + newpref[ nc ] = 0; + + nc = strlen( name ) - ( colon - name ) - 1; + newname = astStore( NULL, colon + 1, nc + 1 ); + newname[ nc ] = 0; + } + } + +/* Check the supplied name and prefix are valid XML 'names'. */ + CheckName( newname, "element", "InitXmlElement", 0, status ); + CheckName( newpref, "element", "InitXmlElement", 1, status ); + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Initialise the items specific to this class of structure. */ + new->name = astStore( NULL, newname, strlen( newname ) + 1 ); + new->attrs = NULL; + new->nattr = 0; + new->items = NULL; + new->nitem = 0; + new->defns = NULL; + new->nsprefs = NULL; + new->nnspref = 0; + new->complete = 0; + + new->prefix = NULL; + if( newpref ) { + nc = strlen( newpref ); + if( nc > 0 ) new->prefix = astStore( NULL, newpref, nc + 1 ); + } + +/* Free any name and prefix extracted from the supplied name string */ + if( colon ) { + newname = astFree( newname ); + newpref = astFree( newpref ); + } +} + +static void InitXmlNamespace( AstXmlNamespace *new, int type, const char *prefix, + const char *uri, int *status ){ +/* +* Name: +* InitXmlNamespace + +* Purpose: +* Initialise a new XmlNamespace. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlNamespace( AstXmlNamespace *new, int type, const char *prefix, +* const char *uri, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlNamespace +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* prefix +* Pointer to a null terminated string holding the namespace prefix. +* uri +* Pointer to a null terminated string holding the namespace URI. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLNAME, status ) ){ + astError( AST__INTER, "InitXmlNamespace: Supplied object type (%d) " + "does not represent an XmlNamespace", status, type ); + } + +/* Ensure we have non-NULL pointers. */ + if( !prefix ) prefix = ""; + if( !uri ) uri = ""; + +/* Check the supplied prefix is a valid XML 'name'. */ + CheckName( prefix, "namespace prefix", "InitXmlNamespace", 0, status ); + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Initialise the items specific to this class of structure. */ + new->prefix = astStore( NULL, prefix, strlen( prefix ) + 1 ); + new->uri = astStore( NULL, uri, strlen( uri ) + 1 ); +} + +static void InitXmlObject( AstXmlObject *new, long int type, int *status ){ +/* +* Name: +* InitXmlObject + +* Purpose: +* Initialise a new XmlObject. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlObject( AstXmlObject *new, long int type, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlObject +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* status +* Pointer to the inherited status variable. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + +/* Check the global error status. */ + if( !astOK ) return; + +/* If needed, get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the supplied object type is OK. Report an error if not. */ + if( !CheckType( type, AST__XMLOBJECT, status ) ){ + astError( AST__INTER, "InitXmlObject: Supplied object type (%ld) " + "is not appropriate for an XmlObject", status, type ); + } + +/* This class of structure is the base class for XML objects so it has no + parent class to be initialised. So just initialise the items specific to + this class of structure. */ + new->parent = NULL; + new->type = type; + new->id = next_id++; + +#ifdef DEBUG +/* Add the new XmlObject to the list of all XmlObjects. */ + AddObjectToList( new ); +#endif + +} + +static void InitXmlPI( AstXmlPI *new, int type, const char *target, + const char *text, int *status ){ +/* +* Name: +* InitXmlPI + +* Purpose: +* Initialise a new XmlPI. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlPI( AstXmlPI *new, int type, const char *target, +* const char *text, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlPI +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* target +* Pointer to a null terminated string holding the PI target. +* text +* Pointer to a null terminated string holding the PI text. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLPI, status ) ){ + astError( AST__INTER, "InitXmlPI: Supplied object type (%d) " + "does not represent an XmlPI", status, type ); + } + +/* Initialise the parent XmlObject component. */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Ensure we have non-NULL pointers. */ + if( !target ) target = ""; + if( !text ) text = ""; + +/* Initialise the items specific to this class of structure. Report an error + if anything is illegal. */ + new->target = NULL; + new->text = NULL; + + if( !Ustrcmp( target, "XML", status ) && astOK ) { + astError( AST__XMLPT, "InitXmlPI(xml): Illegal XML PI target \"%s\"" + " supplied.", status, target ); + } else { + new->target = astStore( NULL, target, strlen( target ) + 1 ); + new->text = astStore( NULL, text, strlen( text ) + 1 ); + } +} + +static void InitXmlPrologue( AstXmlPrologue *new, int type, int *status ){ +/* +* Name: +* InitXmlPrologue + +* Purpose: +* Initialise a new XmlPrologue. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* InitXmlPrologue( AstXmlPrologue *new, int type, int *status ) + +* Description: +* This function initialises supplied memory to hold an XmlPrologue +* structure. + +* Parameters: +* new +* The memory in which to initialise the structure. +* type +* An identifier for the structure type. +* status +* Pointer to the inherited status variable. +*/ + +/* Check the global error status. */ + if( !astOK ) return; + +/* Check the supplied object type is appropriate for the class of + structure being initialised. If not report an error. */ + if( !CheckType( type, AST__XMLPRO, status ) ){ + astError( AST__INTER, "InitXmlPrologue: Supplied object type (%d) " + "does not represent an XmlPrologue", status, type ); + } + +/* Initialise the parent XmlObject */ + InitXmlObject( (AstXmlObject *) new, type, status ); + +/* Initialise the items specific to this class of structure. */ + new->xmldecl = NULL; + new->misc1 = NULL; + new->nmisc1 = 0; + new->dtdec = NULL; + new->misc2 = NULL; + new->nmisc2 = 0; +} + +static int MatchName( AstXmlElement *this, const char *name, int *status ){ +/* +* Name: +* MatchName + +* Purpose: +* Check that an element has a specified name and/or prefix. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* int MatchName( AstXmlElement *this, const char *name, int *status ) + +* Description: +* This function checks that an element has a specified name and/or prefix. + +* Parameters: +* this +* The XmlElement to check. +* name +* The name for the element (may include a namespace prefix). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the supplied element has the supplie dname/prefix. Zero +* otherwise. + +*/ + + +/* Local Variables: */ + const char *colon; /* Pointer to colon within supplied name */ + char *newname; /* Pointer to name string (no prefix) */ + char *newpref; /* Pointer to name string */ + int nc; /* Length of prefix string */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* Extract any prefix from the start of the supplied name. */ + newpref = NULL; + newname = (char *) name; + colon = strchr( name, ':' ); + if( colon ) { + nc = colon - name; + newpref = astStore( NULL, name, nc + 1 ); + newpref[ nc ] = 0; + + nc = strlen( name ) - ( colon - name ) - 1; + newname = astStore( NULL, colon + 1, nc + 1 ); + newname[ nc ] = 0; + } + +/* Compare the prefix. */ + if( newpref && this->prefix ) { + result = !strcmp( newpref, this->prefix ); + + } else if( !newpref && !this->prefix ) { + result = 1; + + } else { + result = 0; + } + +/* If the prefixes matches, compare the names */ + if( result ) { + if( newname && this->name ) { + result = !strcmp( newname, this->name ); + + } else if( !newname && !this->name ) { + result = 1; + + } else { + result = 0; + } + } + +/* Free any name and prefix extracted from the supplied name string */ + if( colon ) { + newname = astFree( newname ); + newpref = astFree( newpref ); + } + +/* Return the result. */ + return result; +} + +static AstXmlAttribute *NewAttribute( const char *name, const char *value, + const char *prefix, int *status ){ +/* +* Name: +* NewAttribute + +* Purpose: +* Create a new XmlAttribute. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* AstXmlAttribute *NewAttribute( const char *name, const char *value, +* const char *prefix, int *status ) + +* Description: +* This function creates a new XmlAttribute structure representing an +* XML attribute with the given name, value and namespace prefix. + +* Parameters: +* name +* Pointer to a null terminated string containing the attribute name. +* value +* Pointer to a null terminated string containing the attribute value. +* prefix +* Pointer to a null terminated string containing the attribute +* namespace prefix (may be NULL or blank). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new structure is returned. + +* Notes: +* - A NULL pointer is returned if the inherited status value +* indicates an error has occurred on entry, or if this function +* should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlAttribute *new; /* The returned pointer */ + +/* Initialise */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Allocate space for the new structure. */ + new = (AstXmlAttribute *) astMalloc( sizeof( AstXmlAttribute ) ); + +/* Initialise it. */ + InitXmlAttribute( new, AST__XMLATTR, name, value, prefix, status ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) new = astXmlDelete( new ); + +/* Return the result. */ + return new; + +} + +static AstXmlDocument *NewDocument( int *status ){ +/* +* Name: +* NewDocument + +* Purpose: +* Create a new empty XmlDocument. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* AstXmlDocument *NewDocument( int *status ) + +* Description: +* This function creates a new empty XmlDocument structure representing +* an entire XML Document. + +* Parameters: +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new structure is returned. + +* Notes: +* - A NULL pointer is returned if the inherited status value +* indicates an error has occurred on entry, or if this function +* should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlDocument *new; /* The returned pointer */ + +/* Initialise */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Allocate space for the new structure. */ + new = (AstXmlDocument *) astMalloc( sizeof( AstXmlDocument ) ); + +/* Initialise it. */ + InitXmlDocument( new, AST__XMLDOC, status ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) new = astXmlDelete( new ); + +/* Return the result. */ + return new; + +} + +static AstXmlNamespace *NewNamespace( const char *prefix, const char *uri, int *status ){ +/* +* Name: +* NewNamespace + +* Purpose: +* Create a new XmlNamespace. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* AstXmlNamespace *NewNamespace( const char *prefix, +* const char *uri, int *status ) + +* Description: +* This function creates a new XmlNamespace structure representing an +* XML namespace with the given prefix and uri. + +* Parameters: +* prefix +* Pointer to a null terminated string containing the namespace prefix. +* uri +* Pointer to a null terminated string containing the associated URI. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new structure is returned. + +* Notes: +* - A NULL pointer is returned if the inherited status value +* indicates an error has occurred on entry, or if this function +* should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlNamespace *new; /* The returned pointer */ + +/* Initialise */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Allocate space for the new structure. */ + new = (AstXmlNamespace *) astMalloc( sizeof( AstXmlNamespace ) ); + +/* Initialise it. */ + InitXmlNamespace( new, AST__XMLNAME, prefix, uri, status ); + +/* If an error occurred, delete the new structure. */ + if( !astOK ) new = astXmlDelete( new ); + +/* Return the result. */ + return new; + +} + +static AstXmlPrologue *NewPrologue( AstXmlDocument *doc, int *status ){ +/* +* Name: +* NewPrologue + +* Purpose: +* Create a new empty XmlPrologue. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* AstXmlPrologue *NewPrologue( AstXmlDocument *doc, int *status ) + +* Description: +* This function creates a new empty XmlPrologue structure representing +* an entire prologue. + +* Parameters: +* doc +* A pointer to the XmlDocument to add the XmlPrologue to, or NULL. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new structure is returned. + +* Notes: +* - A NULL pointer is returned if the inherited status value +* indicates an error has occurred on entry, or if this function +* should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlPrologue *new; /* The returned pointer */ + +/* Initialise */ + new = NULL; + +/* Check the global error status. */ + if( !astOK ) return new; + +/* Allocate space for the new structure. */ + new = (AstXmlPrologue *) astMalloc( sizeof( AstXmlPrologue ) ); + +/* Initialise it. */ + InitXmlPrologue( new, AST__XMLPRO, status ); + +/* Set its parent. */ + ((AstXmlObject *) new )->parent = (AstXmlParent *) doc; + +/* If an error occurred, delete the new structure. */ + if( !astOK ) new = astXmlDelete( new ); + +/* Return the result. */ + return new; + +} + +static char *RemoveEscapes( const char *text, int *status ){ +/* +* Name: +* RemoveEscapes + +* Purpose: +* Replaces entity references by corresponding ascii characters. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* char *RemoveEscapes( const char *text, int *status ) + +* Description: +* This function produces a dynamic copy of the supplied text in which +* occurrences of XML entity references are replaced by the corresponding +* ASCII text. + +* Parameters: +* text +* A pointer to a text string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a dynamically allocated string containing the required +* copy. + +* Notes: +* - NULL is returned if this function is called with the global error +* status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + char *d; /* Pointer to next returned character */ + char *result; /* Returned pointer */ + char rc; /* Replacement character */ + const char *c; /* Pointer to next supplied character */ + int nc; /* Number of characters to skip */ + +/* Initialise */ + result = NULL; + nc = 0; + +/* Return if the pointer is NULL or if an error has occurred. */ + if( !astOK || !text ) return result; + +/* Allocate memory to hold a copy of the supplied text. */ + result = astMalloc( strlen( text ) + 1 ); + +/* Check the pointer can be used safely. */ + if( astOK ) { + +/* Loop round every character in the supplied text. */ + c = text - 1; + d = result; + while( *(++c) ) { + +/* If this character marks the start of a entity reference, replace it by + the corresponding ascii character and shuffle the remaining text down. */ + if( !strncmp( c, "&", 5 ) ) { + rc = '&'; + nc= 4; + + } else if( !strncmp( c, "<", 4 ) ) { + rc = '<'; + nc= 3; + + } else if( !strncmp( c, ">", 4 ) ) { + rc = '>'; + nc= 3; + + } else if( !strncmp( c, "'", 6 ) ) { + rc = '\''; + nc= 5; + + } else if( !strncmp( c, """, 6 ) ) { + rc = '"'; + nc= 5; + + } else { + rc = 0; + } + + if( rc ) { + *(d++) = rc; + c += nc; + } else { + *(d++) = *c; + } + + } + +/* Terminate the returned string. */ + *d = 0; + +/* Reallocate the string to free up any unused space. */ + result = astRealloc( result, d - result + 1 ); + } + +/* Return the result. */ + return result; +} + +#ifdef DEBUG +static void RemoveObjectFromList( AstXmlObject *obj ){ +/* +* Name: +* RemoveObjectFromList + +* Purpose: +* Removes an XmlObject from a static list of all currently active XmlObjects. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* void RemoveObjectFromList( AstXmlObject *obj ) + +* Description: +* This function removes the supplied pointer from a static list of +* pointers, and decrements the number of elements in the list. This list +* holds pointers to all the XmlObjects which currently exist. If the +* supplied pointer is not found in the list, this function returns +* without action. + +* Parameters: +* this +* A pointer to the XmlObject. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variavles: */ + int i; + int ii; + +/* Locate the supplied pointer within the list of pointers to all + currently active XmlObjects. */ + ii = -1; + for( i = 0; i < nobj; i++ ){ + if( existing_objects[ i ]->id == obj->id ) { + ii = i; + break; + } + } + +/* Check the pointer was found. */ + if( ii != -1 ) { + +/* Shuffle all higher index pointers down one in the list in order to fill + the gap left by removing the supplied pointer. */ + for( ii++; ii < nobj; ii++ ){ + existing_objects[ ii - 1 ] = existing_objects[ ii ]; + } + +/* Decrement the number of pointers in the list. */ + nobj--; + +/* Nullify the pointer at the end of the list which is no longer used. */ + existing_objects[ nobj ] = NULL; + } +} +#endif + +static const char *ResolvePrefix( const char *prefix, AstXmlElement *elem, int *status ){ +/* +* Name: +* ResolvePrefix + +* Purpose: +* Find the URI associated with a namespace prefix. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* const char *ResolvePrefix( const char *prefix, AstXmlElement *elem, int *status) + +* Description: +* This function searches the namespaces defined within the supplied +* element for a prefix which matches the supplied prefix. If found, +* it returns a pointer to the URI string associated with the prefix. +* If not found, it calls this function recursively on the parent +* element. If there is no parent element, NULL is returned. + +* Parameters: +* prefix +* Pointer to a string holding the namespace prefix. +* elem +* The pointer to the XmlElement. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to a string holding the URI, or NULL if not found. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +*/ + +/* Local Variables: */ + AstXmlParent *parent; /* Parent object */ + AstXmlNamespace *ns; /* Next namespace */ + const char *result; /* Returned pointer */ + int i; /* Loop count */ + +/* Initialise */ + result = NULL; + +/* Check the global error status, and the supplied element. */ + if( !astOK || !elem ) return result; + +/* Loop round all the namespace definitions in the element. */ + for( i = 0; i < elem->nnspref; i++ ) { + ns = elem->nsprefs[ i ]; + +/* Compare the namespace prefix with the supplied prefix (case sensitive). + Store a pointer to the associated URI if they match, and leave the + loop. */ + if( !strcmp( ns->prefix, prefix ) ) { + result = ns->uri; + break; + } + } + +/* If no matching namespace was found, attempt to resolve the prefix + within the context of the parent element. */ + if( !result ) { + parent = ((AstXmlObject *) elem )->parent; + if( astXmlCheckType( parent, AST__XMLELEM ) ) { + result = ResolvePrefix( prefix, (AstXmlElement *) parent, status ); + } + } + +/* Return the result. */ + return result; +} + +static int Ustrcmp( const char *a, const char *b, int *status ){ +/* +* Name: +* Ustrncmp + +* Purpose: +* A case blind version of strcmp. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* int Ustrcmp( const char *a, const char *b ) + +* Description: +* Returns 0 if there are no differences between the two strings, and 1 +* otherwise. Comparisons are case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strcmp" does. +* - This function attempts to execute even if an error has occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Loop round each character. */ + while( 1 ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + +#ifdef DEBUG +int astXmlTrace( int show ){ +/* +*+ +* Name: +* astXmlTrace + +* Purpose: +* List details of XML objects currently in existence. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* int astXmlTrace( int show ) + +* Description: +* Lists details of XML objects currently in existence. Details are +* written to standard output. + +* Parameters: +* show +* - 0, the ID values of all currently active XmlObjects are +* listed. The objects themselves are unchanged. +* - 1, each object is displayed using astXmlShow unless it has +* already been included in the display of a previous object, and then +* annulled. Consequently, this mode is destructive, and none of the +* displayed XmlObjects will be acessable afterwards. +* - 2, each object is displayed using astXmlShow whether or not it +* has already been included in the display of a previous object. +* The objects are left unchanged. +* - 3, nothing is written to standard output, but the number of +* active XmlObjects is still returned. + +* Returned Value: +* The number of XMLObjects which are in existence on entry to this +* function. + +*- +*/ + +/* Local Variables: */ + AstXmlObject *root; + int i, result, old_status; + + old_status = astStatus; + astClearStatus; + + result = nobj; + + if( show == 0 ) { + printf( "Current list of active XmlObject identifiers: " ); + for( i = 0; i < nobj; i++ ) printf( "%d ", existing_objects[ i ]->id ); + printf("\n"); + + } else if( show ==1 ){ + while( nobj > 0 ) { + root = astXmlGetRoot( existing_objects[0] ); + printf( "ID = %d (type %ld)\n%s\n------------\n", + root->id, root->type, astXmlShow( root ) ); + root = astXmlAnnulTree( root ); + } + + } else if( show == 2 ) { + for( i = 0; i < nobj; i++ ) printf( "%d\n%s\n------------\n", + existing_objects[ i ]->id, + astXmlShow(existing_objects[ i ]) ); + printf("\n"); + } + + astSetStatus( old_status ); + + return result; +} +#endif + +AstXmlElement *astXmlReadDocument_( AstXmlDocument **doc, + int (*is_wanted)( AstXmlElement *, int * ), + int skip, char (*source)( void *, int * ), + void *data, int *status ){ +/* +*+ +* Name: +* astXmlReadDocument + +* Purpose: +* Read and parse an XML document. + +* Type: +* Protected function. + +* Synopsis: +* #include "xml.h" +* AstXmlElement *astXmlReadDocument( AstXmlDocument **doc, +* int (*is_wanted)( AstXmlElement *, int * ), +* int skip, char (*source)( void *, int * ), +* void *data ) + +* Description: +* This function reads and parses text from an XML source. The text is +* obtained by calling the supplied "source" function, which returns +* the next character read from the external source on each invocation. +* +* The reading scheme combines elements of the SAX and DOM schemes in +* an attempt to minimise memory requirements (a potential problem with +* DOM) whilst retaining a simple interface for accessing the XML +* elements of interest to the client (a potential problem for SAX). +* +* When an element start tag is encountered in the source, the client +* is asked to indicate whether the element is of interest. This is +* done by calling the supplied "is_wanted" function. If the client +* indicates that the element is of interest, its contents are read +* and a pointer to a corresponding XmlElement structure is returned. +* Reading stops when the element has been read. If the client +* indicates that the element is not of interest, then (if "skip" is +* non-zero) the contents of the element are skipped over, and reading +* continues following the element end tag. When the next element is +* encountered the client will again be asked to indicate its interest +* in the element. This continues until either the client indicates that +* an element is of interest, or the end of the source is reached. If +* "skip" is zero, then an error is reported if the first element in +* the document is not of interest. +* +* The client has an option to reply that an element is not itself of +* interest, but may possibly contain interesting elements. In this case, +* the sub-elements within the element are read and checked in the same +* way. +* +* This function returns, and no more characters are read from the +* source, once the contents of the first "interesting" element has been +* read. +* +* The function thus returns a pointer to an XmlElement containing the +* entire contents of the first interesting element encountered in the +* source. This function can then be invoked again to read further +* interesting elements from the source. In this case, the XmlDocument +* structure created by the initial invocation (see parameter "doc") +* must be supplied, as this indicates the point in the total document +* structure at which the previous "interesting" element was located. + +* Parameters: +* doc +* Address of a location holding a pointer to an AstXmlDocument +* structure. The AstXmlDocument pointer should be supplied as NULL +* when invoking this function for the first time on a document +* source (i.e. when reading from the beginning of the document). +* In this case a new AstXmlDocument structure will be created and a +* pointer to it stored at the supplied address. This structure +* holds the context which enables subsequent invocations of this +* function to determine the point in the document structure at +* which to store any further text read from the source. It also +* holds the document prologue, root element, and epilogue. +* is_wanted +* Pointer to a function which is called to decide if the client is +* interested in each element start tag which has just been read. +* It has a single argument which is a pointer to the (empty) XmlElement +* corresponding to the element start tag which has just been read. +* It returns an integer: +* -1 : the element is not itself of interest but it may contain +* an interesting element, so look through the content and +* ask the client again about any elements found inside it. +* 0 : the element definately contains nothing of interest to +* the client. kip its content and continue looking for new +* elements. +* 1 : the element is definately of interest to the client so +* read its contents and return a pointer to it. +* If NULL is supplied, a value of "+1" is assumed. +* skip +* Indicates if any uninteresting elements may proceed the first +* element of interest. If zero, then an error is reported if the +* first element read from the source is not of interest to the client. +* If non-zero, then any uninteresting elements are simply skipped +* over until an interesting element is found or the document ends. +* source +* Pointer to a function which is called to return the next +* character from the source. It has a single argument which is +* used to pass any supplied data to it. It should return zero when +* the end of the source is reached. +* data +* Pointer to a structure to pass to the source function. This +* structure may contain any data needed by the source function. + +* Returned Value: +* A pointer to the first element of interest, or NULL if there are no +* interesting elements in the source. The returned element will be a +* descendant of "*doc". For this reason, the returned pointer need not +* be annulled explicitly since it will be freed when the XmlDocument +* is annulled. However, if required (e.g. to save memory) it may be +* annulled before the document is annulled by using astXmlRemoveItem to +* remove it from its parent, and then using astXmlAnnul to annul it. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +* - It is assumed that the read commences outside any tag (i.e. +* in between tags or within character data). +*- +*/ + +/* Local Variables: */ + AstXmlElement *result; + +/* Check any supplied pointer is for an XmlDocument. */ + astXmlCheckDocument( *doc, 1 ); + +/* Read and parse the source text. Indicate that the element being read + *may* contain items of interest to the client. Surround with a mutex + since the supplied functions may not be thread-safe. */ + LOCK_MUTEX1; + result = ReadContent( doc, -1, is_wanted, skip, source, data, 0, status ); + UNLOCK_MUTEX1; + +/* Return the result. */ + return result; +} + + +static AstXmlElement *ReadContent( AstXmlDocument **doc, int wanted, + int (*is_wanted)( AstXmlElement *, int * ), + int skip, char (*source)( void *, int * ), + void *data, int depth, int *status ){ +/* +* Name: +* ReadContent + +* Purpose: +* Read and parse an XML document. + +* Type: +* Private function. + +* Synopsis: +* #include "xml.h" +* AstXmlElement *ReadContent( AstXmlDocument **doc, int wanted, +* int (*is_wanted)( AstXmlElement *, int * ), +* int skip, char (*source)( void *, int * ), +* void *data, int depth, int *status ) + +* Description: +* This function reads and parses text from an XML source. The text is +* obtained by calling the supplied "source" function, which returns +* the next character read from the external source on each invocation. +* +* See astXmlReadDocument for more details. + +* Parameters: +* doc +* Address of a location holding a pointer to an AstXmlDocument +* structure. The AstXmlDocument pointer should be supplied as NULL +* when invoking this function for the first time on a document +* source (i.e. when reading from the beginning of the document). +* In this case a new AstXmlDocument structure will be created and a +* pointer to it stored at the supplied address. This structure +* holds the context which enables subsequent invocations of this +* function to determine the point in the document structure at +* which to store any further text read from the source. It also +* holds the document prologue, root element, and epilogue. +* wanted +* Indicates if the content read from the XML source is of interest +* to the client. If a positive value is supplied, all content read +* from the source (up to the end tag which corresponds to the +* supplied "parent") is added to the "parent" element (if supplied). +* If zero is supplied, then all content read from the source is +* discarded. If a negative value is supplied, then all content up +* to the first element start tag is discarded. When the first +* element start tag is encountered, it is passed back to the client +* by invoking the supplied "is_wanted" function. If this function +* returns a non-zero value, then the contents of the new element +* is read (by calling this function recursively) and a pointer to +* the new element is returned as the function value (reading then +* stops and the function returns). If the "is_wanted" function returns +* zero, then the contents of the new element is skipped over, and +* reading continues until the next element start tag is encountered, +* when the "is_wanted" function is again invoked. +* is_wanted +* Pointer to a function which is called to decide if the client is +* interested in the element start tag which has just been read. +* It has a single argument which is a pointer to the (empty) XmlElement +* corresponding to the element start tag which has just been read. +* It returns an integer: +* -1 : the element is not itself of interest but it may contain +* an interesting element, so look through the content and +* ask the client again about any elements found inside it. +* 0 : the element definately contains nothing of interest to +* the client. kip its content and continue looking for new +* elements. +* 1 : the element is definately of interest to the client so +* read its contents and return a pointer to it. +* If NULL is supplied, a value of "+1" is assumed. +* skip +* Indicates if any uninteresting elements may proceed the first +* element of interest. If zero, then an error is reported if the +* first element read from the source is not of interest to the client. +* If non-zero, then any uninteresting elements are simply skipped +* over until an interesting element is found or the document ends. +* source +* Pointer to a function which is called to return the next +* character from the source. It has a single argument which is +* used to pass any supplied data to it. It should return zero when +* the end of the source is reached. +* data +* Pointer to a structure to pass to the source function. This +* structure may contain any data needed by the source function. +* depth +* Depth of nesting (i.e. zero if this function was invoked from +* astXmlReadDocument, and a positive value if it was invoked +* recursively from within itself). +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the first element of interest, or NULL if there are no +* interesting elements in the source. If the first element of +* interest has already been found (as indicated by "wanted" being +1) +* then NULL is returned. The returned element may be a child of a +* parent element containing namespace definitions (which may itself +* have a parent, etc). For this reason, the returned pointer should be +* freed using astXmlAnnulTree rather than astXmlAnnul. + +* Notes: +* - NULL is returned if an error has already occurred, or if this +* function should fail for any reason. +* - It is assumed that the read commences outside any tag (i.e. +* in between tags or within character data). +*/ + +/* Local Variables; */ + AstXmlElement *answer; /* Result of reading a sub-element */ + AstXmlElement *parent; /* Pointer to current parent element */ + AstXmlElement *elem; /* A new element to be read */ + AstXmlElement *result; /* The returned pointer */ + char *cc; /* Pointer to next character */ + char *msg; /* Pointer to message buffer */ + char *text1; /* Pointer to dynamic string */ + char *text2; /* Pointer to another dynamic string */ + char *text3; /* Pointer to another dynamic string */ + char *text4; /* Pointer to another dynamic string */ + char c; /* Current character read from source */ + char lc2; /* Last but one character read */ + char lc; /* Last character read */ + char quoted; /* Character which opened current quote */ + int nc1; /* No. of characters stored in text1 */ + int nc2; /* No. of characters stored in text2 */ + int nc3; /* No. of characters stored in text2 */ + int ncmsg; /* Length of "msg" */ + int newwanted; /* Is the new element wanted? */ + int state; /* Current action being performed */ + int prolog_ok; /* OK for source to start with a prolog? */ + int where; /* Where to add the item within the document */ + +/* Initialise */ + result = NULL; + elem = NULL; + +/* Check the global error status. */ + if( !astOK ) return result; + +/* If no XmlDocument was supplied, assume we are commencing to read a new + document from the begining. Create a new XmlDocument to store the + prologue, root element and epilogue, together with a pointer to the + "current" element, i.e. the element whose content is currently being + read. Also, since we have not yet asked the client if it is interested + in anything, ignore the supplied "wanted" value and use -1 to ensure + that we ask the client when the first element start tag is encountered. */ + if( !*doc ){ + prolog_ok = 1; + *doc = NewDocument( status ); + wanted = -1; + } else { + prolog_ok = 0; + } + +/* Any content read from the source (except for prologue and epilogue) + will be placed into the "parent" element. A pointer to this element is + stored in the XmlDocument structure. The parent element will always be + a descendant of the root element, or the root element itself. */ + parent = (*doc)->current; + +/* If the supplied parent has already been completed (typically because + it was read from an empty element tag), then just return without + action. */ + if( parent && parent->complete ) { + +/* If an error has occurred, or if this invocation of ReadContent was + made recursively (rather than by the client), or if we have something + to return, return. */ + if( !astOK || depth > 0 || result ) { + return result; + +/* Otherwise, returning would result in returning a null pointer to the + client even though the end of the document may not have been reached. + Revert to state 0 and search for further interesting elements. */ + } else { + if( parent != (*doc)->root ) { + (*doc)->current = (AstXmlElement *) ( (AstXmlObject *) parent )->parent; + } else { + (*doc)->current = NULL; + } + parent = (*doc)->current; + state = 0; + } + } + +/* Initialise the previous two characters read. */ + lc = 0; + lc2 = 0; + +/* Initialise pointer to dynamically allocated strings. */ + text1 = NULL; + text2 = NULL; + text3 = NULL; + msg = NULL; + +/* We are not in a quote. */ + quoted = 0; + +/* Initialise the "state" variable which indicates what we are currently + looking for. */ + state = 0; + +/* Loop round reading characters from the source. */ + while( 1 ) { + c = (*source)( data, status ); + +/* Leave the loop if an error occurred whilst reading the character. */ + if( !astOK ) break; + +/* If a parent element has been supplied, (i.e. if we are currently + reading the content of an element), or if we are not in state zero, + report an error and leave the loop if the end of the text has been + reached. If no parent was supplied, just leave the loop. */ + if( !c ) { + if( parent ) { + astError( AST__XMLWF, "astRead(XmlChan): End of XML input text " + "reached whilst reading the content of element %s.", status, + astXmlGetTag( parent, 1 ) ); + + } else if( state > 1 ) { + if( msg ) { + astError( AST__XMLWF, "astRead(XmlChan): End of XML input text " + "reached whilst reading the document epilogue " + "(\"%s\").", status, msg ); + } else { + astError( AST__XMLWF, "astRead(XmlChan): End of XML input text " + "reached whilst reading the document epilogue." , status); + } + } + break; + } + +/* Save text which is not character data for use in error messages. */ + if( state < 2 ) { + if( msg ) msg = astFree( msg ); + } else { + msg = AppendChar( msg, &ncmsg, c, status ); + } + +/* State 0: Use the first character to decide what sort of content item + follows (character data or a tag of some form). */ + if( state == 0 ) { + if( c != '<' ) { + state = 1; + text1 = AppendChar( text1, &nc1, c, status ); + } else { + msg = AppendChar( msg, &ncmsg, '<', status ); + state = 2; + } + +/* State 1: We are reading character data. The character data ends at the + first occurrence of "<", at which point the character data is added to + the parent if required and we continue to state 2.*/ + } else if( state == 1 ) { + if( c != '<' ) { + text1 = AppendChar( text1, &nc1, c, status ); + } else { + msg = AppendChar( msg, &ncmsg, '<', status ); + if( text1 ){ + +/* If we have a parent element, just add it to the element. */ + if( parent ) { + if( wanted > 0 ) { + text4 = RemoveEscapes( text1, status ); + astXmlAddCharData( (AstXmlParent *) parent, 0, text4 ); + text4 = astFree( text4 ); + + +/* If we are not allowed to skip over non-blank content, report an + error if the text is not blank. */ + } else if( !skip ) { + cc = text1 - 1; + while( *(++cc) ) { + if( !isspace( *cc ) ) { + if( parent ) { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data \"%s\" within element %s.", status, + text1, astXmlGetTag( parent, 1 ) ); + } else { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data: \"%s\".", status, text1 ); + } + break; + } + } + } + +/* Otherwise, add it to the document prologue or epilogue. */ + } else { + if( (*doc)->root ) { + where = 3; + } else if( (*doc)->prolog && (*doc)->prolog->dtdec ){ + where = 2; + } else { + where = 1; + } + + text4 = RemoveEscapes( text1, status ); + astXmlAddCharData( (AstXmlParent *) *doc, where, text4 ); + text4 = astFree( text4 ); + } + + text1 = astFree( text1 ); + } + state = 2; + } + +/* State 2: We are using the character following a "<" to determine what + type of tag is commencing. */ + } else if( state == 2 ) { + +/* If the character is a ">", report an error. */ + if( c == '>' ) { + if( parent ) { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"<>\" " + "encountered within element %s.", status, astXmlGetTag( parent, 1 ) ); + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"<>\" " + "encountered." , status); + } + break; + +/* If the character is a "?", this must be a PI tag. */ + } else if( c == '?' ) { + state = 3; + +/* If the character is a "!", it must be a comment or a CDATA section + or a DTD. */ + } else if( c == '!' ) { + state = 4; + +/* If the character is a "/", it must be an element end tag. */ + } else if( c == '/' ) { + state = 5; + +/* Otherwise, this must be an element start tag. Append the character + to "text1". */ + } else { + state = 6; + text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 3: We are reading the initial text following the opening "" string is the target text. */ + } else if( state == 3 ) { + if( c == '>' && lc == '?' ) { + if( text1 ) text1[ --nc1 ] = 0; + state = 100; + } else if( isspace( c ) ) { + state = 7; + } else { + text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 4: We are using the characters following the opening "' ) { + state = 101; + } else { + text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 6: We are looking for the (prefix:)name combination at the start of + an element start tag. */ + } else if( state == 6 ) { + if( c == '>' ) { + state = ( lc != '/' ) ? 102 : 103; + } else if( isspace( c ) ) { + state = 104; + } else if( c != '/' ){ + text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 7: We are reading the remaining text in a PI tag following the target + text. */ + } else if( state == 7 ) { + if( c == '>' && lc == '?' ) { + if( text2 ) text2[ --nc2 ] = 0; + state = 100; + } else if( text2 || !isspace( c ) ) { + text2 = AppendChar( text2, &nc2, c, status ); + } + +/* State 8: We are looking for the start of the text within a comment tag. */ + } else if( state == 8 ) { + if( c == '-' ) { + state = 10; + } else { + if( parent ) { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag " + "starting with \"" is reached, check the previous 2 characters are "--" and then terminate + the text1 string in order to remove these two characters from the comment + text. */ + } else if( state == 10 ) { + if( c == '>' && lc == '-' && lc2 == '-' ) { + text1[ nc1 - 2 ] = 0; + state = 105; + } else { + text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 11: We are reading the remaining text in a CDATA tag. */ + } else if( state == 11 ) { + if( c == '>' && lc == ']' && lc2 == ']' ) { + text1[ nc1 - 2 ] = 0; + state = 106; + } else { + text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 12: We are looking for an equals sign marking the end of an + attribute name within an element start tag. */ + } else if( state == 12 ) { + if( c == '=' ) { + state = 13; + + } else if( c == '>' ) { + if( text1 ) { + if( parent ) { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag " + " \"%s...\" encountered within element %s.", status, msg, + astXmlGetTag( parent, 1 ) ); + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s...\" " + "encountered.", status, msg ); + } + break; + } else { + if( lc == '/' ) { + state = 108; + } else { + state = 200; + } + } + + } else if( text1 || !isspace( c ) ) { + if( c != '/' ) text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 13: We are looking for a '"' or ''' marking the start of an attribute + value within an element start tag. */ + } else if( state == 13 ) { + if( c == '"' ) { + state = 14; + + } else if( c == '\'' ) { + state = 15; + + } else if( c == '>' ) { + astError( AST__XMLWF, "astRead(XmlChan): Illegal value for attribute " + "\"%s\" in XML tag \"%s...\".", status, text1, msg ); + break; + } + +/* State 14: We are looking for a '"' marking the end of an attribute value + within an element start tag. */ + } else if( state == 14 ) { + if( c == '"' ) { + state = 107; + + } else if( c == '>' ) { + astError( AST__XMLWF, "astRead(XmlChan): Illegal value for attribute " + "\"%s\" in XML tag \"%s...\".", status, text1, msg ); + break; + + } else { + text2 = AppendChar( text2, &nc2, c, status ); + } + +/* State 15: We are looking for a ''' marking the end of an attribute value + within an element start tag. */ + } else if( state == 15 ) { + if( c == '\'' ) { + state = 107; + + } else if( c == '>' ) { + astError( AST__XMLWF, "astRead(XmlChan): Illegal value for attribute " + "\"%s\" in XML tag \"%s...\".", status, text1, msg ); + break; + + } else { + text2 = AppendChar( text2, &nc2, c, status ); + } + +/* State 16: We are looking for the end of a DOCTYPE string. */ + } else if( state == 16 ) { + if( isspace( c ) ) { + if( !strcmp( text1, "' ) { + state = 109; + } else { + text1 = AppendChar( text1, &nc1, c, status ); + } + +/* State 19: We are looking for the start of a string following a DOCTYPE + name string. */ + } else if( state == 19 ) { + if( !isspace( c ) ) { + if( c == '[' ) { + state = 20; + } else if( c == '>' ) { + state = 109; + } else { + state = 21; + text2 = AppendChar( text2, &nc2, c, status ); + } + } + +/* State 20: We are looking for the "]" marking the end of the internal + markup of a DOCTYPE element. Avoid the contents of quoted strings (such + as #FIXED attribute values). */ + } else if( state == 20 ) { + text3 = AppendChar( text3, &nc3, c, status ); + if( c == '\'' ) { + if( quoted == '\'' ) { + quoted = 0; + } else if( !quoted ) { + quoted = '\''; + } + + } else if( c == '"' ) { + if( quoted == '"' ) { + quoted = 0; + } else if( !quoted ) { + quoted = '"'; + } + + } else if( !quoted && c == ']' ) { + text3[ --nc3 ] = 0; + state = 22; + } + +/* State 21: We are looking for the start of a DOCTYPE internal section. */ + } else if( state == 21 ) { + if( c == '[' ) { + state = 20; + } else if( c == '>' ) { + state = 109; + } else { + text2 = AppendChar( text2, &nc2, c, status ); + } + +/* State 22: We are looking for the ">" at the end of a DOCTYPE. */ + } else if( state == 22 ) { + if( !isspace( c ) ) { + if( c == '>' ) { + state = 109; + } else { + astError( AST__XMLWF, "astRead(XmlChan): Extra text found " + "at end of XML DOCTYPE tag \"%s\".", status, msg ); + } + } + + } else { + astError( AST__INTER, "ReadContent(xml): Illegal state (%d) encountered " + "(AST internal programming error).", status, state ); + } + +/* The following states perform actions consequent on the decisons made + above, but which must be performed before reading the next character. */ + +/* In most cases there will be no actions to perform. Therefore check for + this first (to avoid the time spent doing all the following usually + irrelevant checks). */ + if( state < 23 ) { + +/* State 100: We have just reached the end of a PI tag. Create a new XmlPI and + store it in the parent (if required). */ + } else if( state == 100 ) { + if( text1 ){ + +/* First deal with XML declaration PI's. These must be the first item in + the source. */ + if( !strcmp( text1, "xml" ) ) { + if( (*doc)->root || (*doc)->prolog || (*doc)->nepi > 0 ) { + astError( AST__XMLWF, "astRead(XmlChan): An XML " + "declaration \"%s\" was encountered within the " + "body of the document.", status, msg ); + } else { + astXmlSetXmlDec( *doc, text2 ); + } + +/* Now deal with other PI's. */ + } else { + +/* If we have a parent element, just add it to the element. */ + if( parent ) { + if( wanted > 0 ) { + astXmlAddPI( (AstXmlParent *) parent, 0, text1, text2 ); + } else if( !skip ) { + if( parent ) { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data \"%s\" within element %s.", status, + msg, astXmlGetTag( parent, 1 ) ); + } else { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data: \"%s\".", status, msg ); + } + break; + } + +/* Otherwise, add it to the document prologue or epilogue. */ + } else { + if( (*doc)->root ) { + where = 3; + } else if( (*doc)->prolog->dtdec ){ + where = 2; + } else { + where = 1; + } + astXmlAddPI( (AstXmlParent *) *doc, where, text1, text2 ); + + } + } + text1 = astFree( text1 ); + if( text2 ) text2 = astFree( text2 ); + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + break; + } + state = 0; + +/* State 101: We have just reached the end of an element end tag. Check that + the (prefix:)name is legal, and matches that of the current parent, + re-instate the parent's parent as the current element in the document, + and leave the loop if appropriate. */ + } else if( state == 101 ) { + if( text1 ){ + CheckPrefName( text1, "element", "astRead(XmlChan)", status ); + if( parent ) { + if( MatchName( parent, text1, status ) ) { + parent->complete = 1; + if( parent != (*doc)->root ) { + (*doc)->current = (AstXmlElement *) ( (AstXmlObject *) parent )->parent; + } else { + (*doc)->current = NULL; + } + } else { + astError( AST__XMLWF, "astRead(XmlChan): Start tag \"%s\" " + "closed by end tag \"%s\".", status, astXmlGetTag( parent, 1 ), + msg ); + } + + } else { + (*doc)->current = NULL; + astError( AST__XMLWF, "astRead(XmlChan): Unmatched end tag " + "\"%s\" encountered.", status, msg ); + } + + text1 = astFree( text1 ); + + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + } + +/* If an error has occurred, or if this invocation of ReadContent was + made recursively (rather tnan by the client), or if we have something + to return, break out of the loop. */ + if( !astOK || depth > 0 || result ) { + break; + +/* Otherwise, breaking would result in returning a null pointer to the + client even though the end of the document may not have been reached. + Revert to state 0 and search for further intersting elements. */ + } else { + parent = (*doc)->current; + state = 0; + } + +/* State 102: We have just got the (prefix:)name for an element start tag, and + the start tag contains no attributes, etc. Create a new XmlElement, adding + it to the supplied parent, and then proceed to state 200 to read the + content of the element. */ + } else if( state == 102 ) { + if( text1 ){ + elem = astXmlAddElement( parent, text1, NULL ); + text1 = astFree( text1 ); + state = 200; + + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + break; + } + +/* State 103: We have just got the (prefix:)name for an empty element tag, and + the tag does not contain further attributes, etc. Create a new XmlElement + and store it in the container (if any). Indicate that there is no + content to read, and then go on to state 200. */ + } else if( state == 103 ) { + if( text1 ){ + elem = astXmlAddElement( parent, text1, NULL ); + elem->complete = 1; + text1 = astFree( text1 ); + state = 200; + + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + break; + } + +/* State 104: We have just got the (prefix:)name for an element start tag, but + the start tag may contain further attributes, etc. Create a new XmlElement + and store it in the container (if any). Then go to state 12 in which we + look for further attributes, etc. */ + } else if( state == 104 ) { + if( text1 ){ + elem = astXmlAddElement( parent, text1, NULL ); + text1 = astFree( text1 ); + state = 12; + + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + break; + } + +/* State 105: We have just reached the end of a comment tag. Create a new + XmlComment and store it in the parent. */ + } else if( state == 105 ) { + if( text1 ){ + +/* If we have a parent element, just add it to the element. */ + if( parent ) { + if( wanted > 0 ) { + astXmlAddComment( (AstXmlParent *) parent, 0, text1 ); + } else if( !skip ) { + if( parent ) { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data \"%s\" within element %s.", status, + msg, astXmlGetTag( parent, 1 ) ); + } else { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data: \"%s\".", status, msg ); + } + break; + } + +/* Otherwise, add it to the document prologue or epilogue. */ + } else { + if( (*doc)->root ) { + where = 3; + } else if( (*doc)->prolog->dtdec ){ + where = 2; + } else { + where = 1; + } + astXmlAddComment( (AstXmlParent *) *doc, where, text1 ); + } + + text1 = astFree( text1 ); + + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + break; + } + state = 0; + +/* State 106: We have just reached the end of a CDATA tag. Create a new + XmlCDATASection and store it in the container (if any). */ + } else if( state == 106 ) { + if( text1 ){ + if( parent && wanted > 0 ) { + astXmlAddCDataSection( parent, text1 ); + } else if( !skip ) { + if( parent ) { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data \"%s\" within element %s.", status, + msg, astXmlGetTag( parent, 1 ) ); + } else { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data: \"%s\".", status, msg ); + } + break; + } + text1 = astFree( text1 ); + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + break; + } + state = 0; + +/* State 107: We have just reached the end of an attribute or namespace + setting. Create a new object and store it in the element created + earlier. */ + } else if( state == 107 ) { + if( text1 ){ + if( !elem ) { + astError( AST__INTER, "ReadContent(xml): Container lost at state " + "107 (AST internal programming error).", status ); + break; + } + + if( !strcmp( text1, "xmlns" ) ) { + astXmlAddURI( elem, NULL, text2 ); + + } else if( !strncmp( text1, "xmlns:", 6 ) ) { + astXmlAddURI( elem, text1+6, text2 ); + + } else { + text4 = RemoveEscapes( text2, status ); + astXmlAddAttr( elem, text1, text4, NULL ); + text4 = astFree( text4 ); + } + + text1 = astFree( text1 ); + text2 = astFree( text2 ); + + } else { + astError( AST__XMLWF, "astRead(XmlChan): Illegal XML tag \"%s\" " + "encountered.", status, msg ); + break; + } + state = 12; + +/* State 108: We have just reached the end of an empty element tag to which + we have been adding attributes, etc. */ + } else if( state == 108 ) { + if( elem ) { + elem->complete = 1; + state = 200; + } else { + astError( AST__INTER, "Parse(xml): No container in state 108 " + "(AST internal programming error).", status ); + break; + } + +/* State 109: We have just reached the end of a DOCTYPE tag. */ + } else if( state == 109 ) { + + if( (*doc)->root ){ + astError( AST__XMLWF, "astRead(XmlChan): An DOCTYPE tag " + "\"%s\" was encountered within the body of the " + "document.", status, msg ); + break; + + } else if( (*doc)->prolog->dtdec ){ + astError( AST__XMLWF, "astRead(XmlChan): Multiple DOCTYPE tags " + "encountered." , status); + break; + + } else { + astXmlSetDTDec( *doc, text1, text2, text3 ); + text1 = astFree( text1 ); + text2 = astFree( text2 ); + text3 = astFree( text3 ); + state = 0; + } + + } else if( state != 200 ) { + astError( AST__INTER, "ReadContent(xml): Illegal state (%d) encountered " + "(AST internal programming error).", status, state ); + } + + + +/* State 200: We now have now read a complete element start tag and have + a corresponding XmlElement ("elem"), with all attributes and namespaces, + etc (but no content). Call the "is_wanted" function to see if the client + is interested in the element. */ + if( state == 200 ) { + +/* If this element is found at the root level of the document, store a + pointer to it as the root element. Report an error if there is already + a root element. */ + if( !parent ) { + if( (*doc)->root ){ + if( astOK ) { + astError( AST__XMLWF, "astRead(XmlChan): Multiple root " + "elements encountered." , status); + elem = astXmlDelete( elem ); + } + break; + } else { + (*doc)->root = elem; + ((AstXmlObject *) elem )->parent = (AstXmlParent *) (*doc); + } + } + +/* If we do not already know, ask the caller if it is interested in this new + element. If no "is_wanted" function was supplied, assume all elements + are interesting. */ + if( wanted == -1 ) { + newwanted = is_wanted ? (*is_wanted)( elem, status ) : 1; + } else { + newwanted = wanted; + } + +/* If it is not interested, report an error if skip is zero. */ + if( newwanted != 1 && !skip ) { + if( parent ) { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data \"%s\" within element %s.", status, + msg, astXmlGetTag( parent, 1 ) ); + } else { + astError( AST__BADIN, "astRead(XmlChan): Cannot interpret " + "the input data: \"%s\".", status, msg ); + } + break; + } + +/* Make the new element the "current" element in the document. */ + (*doc)->current = elem; + +/* Read the contents of the new element from the source. If the client is + interested in the element, the read contents will be added to the + element, otherwise they will be discarded after being read. */ + answer = ReadContent( doc, newwanted, is_wanted, skip, source, + data, depth + 1, status ); + +/* If the first interesting element was found inside "elem", then + return it. If "elem" is not interesting and did not contain anything + of interest, delete it and return the initialised NULL pointer. */ + if( newwanted < 0 ) { + if( answer ) { + result = answer; + } else { + elem = astXmlDelete( elem ); + } + +/* If the elem is of no interest, delete it and return the initialised + NULL pointer. */ + } else if( newwanted == 0 ) { + elem = astXmlDelete( elem ); + +/* Otherwise, "elem" itself is definitely of interest. If "elem" is + the first item of interest, return it. */ + } else if( wanted < 0 ) { + result = elem; + } + +/* If we have an answer to return, leave the loop, otherwise re-instate the + original current element in the document and continue to read any text + following the element. */ + if( result ) { + break; + } else { + (*doc)->current = parent; + state = 0; + } + + } if( state > 22 ) { + astError( AST__INTER, "ReadContent(xml): Illegal state (%d) encountered " + "(AST internal programming error).", status, state ); + } + +/* Remember the previous two character */ + lc2 = lc; + lc = c; + } + +/* Free any dynamic strings */ + text1 = astFree( text1 ); + text2 = astFree( text2 ); + text3 = astFree( text3 ); + if( msg ) msg = astFree( msg ); + +/* Delete the returned object if an error occurred. */ + if( !astOK ) result = astXmlDelete( result ); + +/* Return the result. */ + return result; +} + + + diff --git a/xml.h b/xml.h new file mode 100644 index 0000000..bbd0c89 --- /dev/null +++ b/xml.h @@ -0,0 +1,392 @@ +#if !defined( XML_INCLUDED ) /* Include this file only once */ +#define XML_INCLUDED +/* +*+ +* Name: +* xml.h + +* Type: +* C include file. + +* Purpose: +* Define the interface to the AST xml module + +* Invocation: +* #include "xml.h" + +* Description: +* This include file defines the interface to the internal xml module +* used by the AST library and provides the type definitions, function +* prototypes and macros, etc. needed to use this module. + +* Inheritance: +* The xml module is not a class and does not inherit. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David S. Berry (Starlink) + +* History: +* 23-OCT-2003 (DSB): +* Original version. +* 12-JAN-2004 (DSB): +* Major revisions. +*/ + + + +/* Constant Values. */ +/* ================ */ + +/* These constants are used as identifiers for the different classes of + XML object defined in this file. They are purposefully obscure to reduce + the possibility of random integer values being incorrectly interpreted + as valid XML types */ +#define AST__XMLBAD 0 /* Id for an uninitialised XmlObject */ +#define AST__XMLOBJECT 198263577 /* Id for XmlObject structure */ +#define AST__XMLELEM 182874779 /* Id for XmlElement structure */ +#define AST__XMLATTR 837746634 /* Id for XmlAttribute structure */ +#define AST__XMLCDATA 293854662 /* Id for XmlCDdataSection structure */ +#define AST__XMLCOM 748737648 /* Id for XmlComment structure */ +#define AST__XMLPI 983763553 /* Id for XmlPI structure */ +#define AST__XMLNAME 236756469 /* Id for XmlNamespace structure */ +#define AST__XMLDOC 356274395 /* Id for XmlDocument structure */ +#define AST__XMLPRO 743682474 /* Id for XmlPrologue structure */ +#define AST__XMLDEC 987546328 /* Id for XmlDeclPI structure */ +#define AST__XMLDTD 874673747 /* Id for XmlDTDec structure */ +#define AST__XMLWHITE 675849952 /* Id for XmlWhite structure */ +#define AST__XMLBLACK 347657863 /* Id for XmlBlack structure */ + +/* The following constants refer to "interfaces", not "classes". */ + +#define AST__XMLCHAR 456739289 /* Id for XmlCharData structure */ +#define AST__XMLCONT 673882993 /* Id for XmlContentItem structure */ +#define AST__XMLMISC 358768954 /* Id for XmlMiscItem structure */ +#define AST__XMLPAR 874366235 /* Id for XmlParent structure */ + +/* Define constants used to size global arrays in this module. */ +#define AST__XML_GETTAG_BUFF_LEN 200 + +/* Type Definitions. */ +/* ================= */ + +/* Pre-define types so they can be used within structure definitions. */ +typedef struct AstXmlObject AstXmlObject; +typedef struct AstXmlAttribute AstXmlAttribute; +typedef struct AstXmlNamespace AstXmlNamespace; +typedef struct AstXmlElement AstXmlElement; +typedef struct AstXmlBlack AstXmlBlack; +typedef struct AstXmlWhite AstXmlWhite; +typedef struct AstXmlCDataSection AstXmlCDataSection; +typedef struct AstXmlComment AstXmlComment; +typedef struct AstXmlPI AstXmlPI; +typedef struct AstXmlDocument AstXmlDocument; +typedef struct AstXmlPrologue AstXmlPrologue; +typedef struct AstXmlDeclPI AstXmlDeclPI; +typedef struct AstXmlDTDec AstXmlDTDec; + +/* The following data types define "interfaces". That is each data type + corresponds to a subset of the above classes. */ + +/* Marks a class as "character data" */ +typedef AstXmlObject AstXmlCharData; + +/* Marks a class as a "content item" */ +typedef AstXmlObject AstXmlContentItem; + +/* Marks a class as a "miscalleneous item" */ +typedef AstXmlObject AstXmlMiscItem; + +/* Marks a class as being able to own a child */ +typedef AstXmlObject AstXmlParent; + +/* XmlObject structure. */ +/* -------------------- */ +/* Contains data common to all other structures */ +struct AstXmlObject { + AstXmlParent *parent; /* The parent which contains this XmlObject */ + long int type; /* An ID giving the type of structure */ + int id; /* A unique id for this object. */ +}; + +/* XmlAttribute structure. */ +/* ----------------------- */ +/* Describes an XML attribute */ +struct AstXmlAttribute { + AstXmlObject obj; /* General information for this XmlObject */ + char *name; /* The name of the attribute */ + char *value; /* Attribute value */ + char *prefix; /* Namespace prefix for this attribute */ +}; + +/* XmlNamespace structure. */ +/* ----------------------- */ +/* Describes an XML namespace definition */ +struct AstXmlNamespace { + AstXmlObject obj; /* General information for this XmlObject */ + char *prefix; /* Namespace prefix */ + char *uri; /* Namespace URI */ +}; + +/* XmlElement structure. */ +/* --------------------- */ +/* Describes an XML element */ +struct AstXmlElement { + AstXmlObject obj; /* General information for this XmlObject */ + char *name; /* The type (name) of the element */ + AstXmlAttribute **attrs; /* Ptr. to list of attributes of the element */ + int nattr; /* Number of attributes in the above list */ + AstXmlContentItem **items; /* Ptr. to list of items in the element's content */ + int nitem; /* Number of items in above list */ + char *defns; /* Default Namespace URI for element content */ + char *prefix; /* Namespace prefix for this element */ + AstXmlNamespace **nsprefs; /* Ptr. to list of new Namespaces defined by this element */ + int nnspref; /* Number of Namespaces in above list */ + int complete; /* Have the contents of the element been read? */ +}; + +/* XmlBlack structure. */ +/* ---------------------- */ +/* Describes character data containing at least one non-blank character. */ +struct AstXmlBlack { + AstXmlObject obj; /* General information for this XmlObject */ + char *text; /* The character data */ +}; + +/* XmlWhite structure. */ +/* ------------------- */ +/* Describes character data containing no one non-blank characters. */ +struct AstXmlWhite { + AstXmlObject obj; /* General information for this XmlObject */ + char *text; /* The white character data */ +}; + +/* XmlCDataSection structure. */ +/* ----------------------- */ +/* Describes an XML CDATA section */ +struct AstXmlCDataSection { + AstXmlObject obj; /* General information for this XmlObject */ + char *text; /* The text of the cdata section */ +}; + +/* XmlComment structure. */ +/* --------------------- */ +/* Describes an XML CDATA section */ +struct AstXmlComment { + AstXmlObject obj; /* General information for this XmlObject */ + char *text; /* The text of the comment */ +}; + +/* XmlPI structure. */ +/* ---------------- */ +/* Describes an XML processing instruction */ +struct AstXmlPI { + AstXmlObject obj; /* General information for this XmlObject */ + char *target; /* The target of the processing instruction */ + char *text; /* The text of the processing instruction */ +}; + +/* XmlDocument structure. */ +/* ---------------------- */ +/* Describes an entire XML document */ +struct AstXmlDocument { + AstXmlObject obj; /* General information for this XmlObject */ + AstXmlPrologue *prolog; /* Pointer to document prologue */ + AstXmlElement *root; /* Pointer to root element */ + AstXmlMiscItem **epilog; /* List of XmlObjects forming the document epilogue */ + int nepi; /* No of XmlObjects pointers in "epilogue" */ + AstXmlElement *current; /* Pointer to element being read */ +}; + +/* XmlPrologue structure. */ +/* ---------------------- */ +/* Describes an XML document prologue */ +struct AstXmlPrologue { + AstXmlObject obj; /* General information for this XmlObject */ + AstXmlDeclPI *xmldecl; /* Pointer to XML declaration PI */ + AstXmlMiscItem **misc1; /* Group of of miscalleneous XmlObjects pointers */ + int nmisc1; /* No of XmlObjects pointers in "misc1" */ + AstXmlDTDec *dtdec; /* Pointer to Document Type Declaration */ + AstXmlMiscItem **misc2; /* Group of of miscalleneous XmlObjects pointers */ + int nmisc2; /* No of XmlObjects pointers in "misc2" */ +}; + +/* XmlDecPI structure. */ +/* ------------------- */ +/* Describes an XML declaration PI */ +struct AstXmlDeclPI { + AstXmlObject obj; /* General information for this XmlObject */ + char *text; /* The text of the XML declaration */ +}; + +/* XmlDTDec structure. */ +/* ------------------- */ +/* Describes a data type declaration */ +struct AstXmlDTDec { + AstXmlObject obj; /* General information for this XmlObject */ + char *name; /* Document type name */ + char *external; /* External ID */ + char *internal; /* Internal declarations */ +}; + + +#if defined(THREAD_SAFE) && defined(astCLASS) + +/* Define a structure holding all data items that are global within the + xml.c file. */ +typedef struct AstXmlGlobals { + int Next_ID; + char GetTag_Buff[ AST__XML_GETTAG_BUFF_LEN + 1 ]; +} AstXmlGlobals; + +#endif + + + + +/* Function prototypes. */ +/* ==================== */ +AstXmlAttribute *astXmlCheckAttribute_( void *, int, int * ); +AstXmlBlack *astXmlCheckBlack_( void *, int, int * ); +AstXmlCDataSection *astXmlCheckCDataSection_( void *, int, int * ); +AstXmlComment *astXmlCheckComment_( void *, int, int * ); +AstXmlContentItem *astXmlGetItem_( AstXmlElement *, int, int * ); +AstXmlDTDec *astXmlCheckDTDec_( void *, int, int * ); +AstXmlDeclPI *astXmlCheckDeclPI_( void *, int, int * ); +AstXmlDocument *astXmlCheckDocument_( void *, int, int * ); +AstXmlElement *astXmlAddElement_( AstXmlElement *, const char *, const char *, int * ); +AstXmlElement *astXmlCheckElement_( void *, int, int * ); +AstXmlParent *astXmlGetParent_( AstXmlObject *, int * ); +AstXmlObject *astXmlGetRoot_( AstXmlObject *, int * ); +AstXmlElement *astXmlReadDocument_( AstXmlDocument **, int (*)( AstXmlElement *, int * ), int, char (*)( void *, int * ), void *, int * ); +AstXmlNamespace *astXmlCheckNamespace_( void *, int, int * ); +AstXmlObject *astXmlCopy_( AstXmlObject *, int * ); +AstXmlObject *astXmlCheckObject_( void *, int, int * ); +AstXmlPI *astXmlCheckPI_( void *, int, int * ); +AstXmlPrologue *astXmlCheckPrologue_( void *, int, int * ); +AstXmlWhite *astXmlCheckWhite_( void *, int, int * ); +AstXmlCharData *astXmlCheckCharData_( void *, int, int * ); +AstXmlContentItem *astXmlCheckContentItem_( void *, int, int * ); +AstXmlMiscItem *astXmlCheckMiscItem_( void *, int, int * ); +AstXmlParent *astXmlCheckParent_( void *, int, int * ); +const char *astXmlFormat_( AstXmlObject *, int * ); +const char *astXmlGetAttributeValue_( AstXmlElement *, const char *, int * ); +const char *astXmlGetName_( AstXmlObject *, int * ); +const char *astXmlGetTag_( AstXmlObject *, int, int * ); +const char *astXmlGetType_( AstXmlObject *, int * ); +const char *astXmlGetURI_( AstXmlObject *, int * ); +const char *astXmlGetValue_( AstXmlObject *, int, int * ); +const char *astXmlShow_( AstXmlObject *, int * ); +int astXmlCheckType_( void *, long int, int * ); +int astXmlGetNattr_( AstXmlElement *, int * ); +int astXmlGetNitem_( AstXmlElement *, int * ); +void *astXmlAnnulTree_( AstXmlObject *, int * ); +void *astXmlAnnul_( AstXmlObject *, int * ); +void *astXmlDelete_( void *, int * ); +void astXmlAddAttr_( AstXmlElement *, const char *, const char *, const char *, int * ); +void astXmlAddCDataSection_( AstXmlElement *, const char *, int * ); +void astXmlAddCharData_( AstXmlParent *, int, const char *, int * ); +void astXmlAddComment_( AstXmlParent *, int, const char *, int * ); +void astXmlAddPI_( AstXmlParent *, int, const char *, const char *, int * ); +void astXmlAddURI_( AstXmlElement *, const char *, const char *, int * ); +void astXmlInsertElement_( AstXmlElement *, AstXmlElement *, int * ); +void astXmlPurge_( AstXmlParent *, int * ); +void astXmlRemoveAttr_( AstXmlElement *, const char *, const char *, int * ); +void astXmlRemoveItem_( AstXmlContentItem *, int * ); +void astXmlRemoveURI_( AstXmlElement *, const char *, int * ); +void astXmlSetXmlDec_( AstXmlDocument *, const char *, int * ); +void astXmlSetDTDec_( AstXmlDocument *, const char *, const char *, const char *, int * ); + +#if defined(THREAD_SAFE) && defined(astCLASS) +void astInitXmlGlobals_( AstXmlGlobals * ); +#else + +#ifdef DEBUG +int astXmlTrace_( int ); +#endif + +#endif + +/* Function interfaces. */ +/* ==================== */ +/* These wrap up the functions defined by this module. */ +#define astXmlGetType(this) astXmlGetType_(this,STATUS_PTR) +#define astXmlCheckAttribute(this,nullok) astXmlCheckAttribute_(this,nullok,STATUS_PTR) +#define astXmlCheckBlack(this,nullok) astXmlCheckBlack_(this,nullok,STATUS_PTR) +#define astXmlCheckCDataSection(this,nullok) astXmlCheckCDataSection_(this,nullok,STATUS_PTR) +#define astXmlCheckCharData(this,nullok) astXmlCheckCharData_(this,nullok,STATUS_PTR) +#define astXmlCheckComment(this,nullok) astXmlCheckComment_(this,nullok,STATUS_PTR) +#define astXmlCheckContentItem(this,nullok) astXmlCheckContentItem_(this,nullok,STATUS_PTR) +#define astXmlCheckDTDec(this,nullok) astXmlCheckDTDec_(this,nullok,STATUS_PTR) +#define astXmlCheckDeclPI(this,nullok) astXmlCheckDeclPI_(this,nullok,STATUS_PTR) +#define astXmlCheckDocument(this,nullok) astXmlCheckDocument_(this,nullok,STATUS_PTR) +#define astXmlCheckElement(this,nullok) astXmlCheckElement_(this,nullok,STATUS_PTR) +#define astXmlCheckMiscItem(this,nullok) astXmlCheckMiscItem_(this,nullok,STATUS_PTR) +#define astXmlCheckNamespace(this,nullok) astXmlCheckNamespace_(this,nullok,STATUS_PTR) +#define astXmlCheckObject(this,nullok) astXmlCheckObject_(this,nullok,STATUS_PTR) +#define astXmlCheckPI(this,nullok) astXmlCheckPI_(this,nullok,STATUS_PTR) +#define astXmlCheckParent(this,nullok) astXmlCheckParent_(this,nullok,STATUS_PTR) +#define astXmlCheckPrologue(this,nullok) astXmlCheckPrologue_(this,nullok,STATUS_PTR) +#define astXmlCheckWhite(this,nullok) astXmlCheckWhite_(this,nullok,STATUS_PTR) + +#define astXmlAddAttr(elem,name,value,prefix) astXmlAddAttr_(astXmlCheckElement(elem,0),name,value,prefix,STATUS_PTR) +#define astXmlAddURI(elem,prefix,uri) astXmlAddURI_(astXmlCheckElement(elem,0),prefix,uri,STATUS_PTR) +#define astXmlAnnul(this) astXmlAnnul_(astXmlCheckObject(this,1),STATUS_PTR) +#define astXmlDelete(this) astXmlDelete_(this,STATUS_PTR) +#define astXmlAnnulTree(this) astXmlAnnulTree_(astXmlCheckObject(this,1),STATUS_PTR) +#define astXmlAddCDataSection(this,text) astXmlAddCDataSection_(astXmlCheckElement(this,0),text,STATUS_PTR) +#define astXmlAddCharData(this,where,text) astXmlAddCharData_(astXmlCheckParent(this,0),where,text,STATUS_PTR) +#define astXmlAddComment(this,where,text) astXmlAddComment_(astXmlCheckParent(this,0),where,text,STATUS_PTR) +#define astXmlAddElement(this,name,prefix) astXmlAddElement_(astXmlCheckElement(this,1),name,prefix,STATUS_PTR) +#define astXmlAddPI(this,where,target,text) astXmlAddPI_(astXmlCheckParent(this,0),where,target,text,STATUS_PTR) +#define astXmlGetParent(this) astXmlGetParent_(astXmlCheckObject(this,0),STATUS_PTR) +#define astXmlGetRoot(this) astXmlGetRoot_(astXmlCheckObject(this,0),STATUS_PTR) +#define astXmlGetName(this) astXmlGetName_(astXmlCheckObject(this,0),STATUS_PTR) +#define astXmlGetValue(this,report) astXmlGetValue_(astXmlCheckObject(this,0),report,STATUS_PTR) +#define astXmlGetAttributeValue(this,name) astXmlGetAttributeValue_(astXmlCheckElement(this,0),name,STATUS_PTR) +#define astXmlGetNattr(this) astXmlGetNattr_(astXmlCheckElement(this,0),STATUS_PTR) +#define astXmlGetNitem(this) astXmlGetNitem_(astXmlCheckElement(this,0),STATUS_PTR) +#define astXmlGetItem(this,item) astXmlGetItem_(astXmlCheckElement(this,0),item,STATUS_PTR) +#define astXmlGetAttributeValue(this,name) astXmlGetAttributeValue_(astXmlCheckElement(this,0),name,STATUS_PTR) +#define astXmlGetTag(this,opening) astXmlGetTag_(astXmlCheckObject(this,0),opening,STATUS_PTR) +#define astXmlGetURI(this) astXmlGetURI_(astXmlCheckObject(this,0),STATUS_PTR) +#define astXmlFormat(this) astXmlFormat_(astXmlCheckObject(this,0),STATUS_PTR) +#define astXmlShow(this) astXmlShow_(astXmlCheckObject(this,0),STATUS_PTR) +#define astXmlRemoveItem(this) astXmlRemoveItem_(astXmlCheckContentItem(this,0),STATUS_PTR) +#define astXmlRemoveAttr(this,name,prefix) astXmlRemoveAttr_(astXmlCheckElement(this,0),name,prefix,STATUS_PTR) +#define astXmlRemoveURI(this,prefix) astXmlRemoveURI_(astXmlCheckElement(this,0),prefix,STATUS_PTR) +#define astXmlReadDocument(doc,is_wanted,skip,source,data) astXmlReadDocument_(doc,is_wanted,skip,source,data,STATUS_PTR) +#define astXmlInsertElement(this,elem) astXmlInsertElement_(astXmlCheckElement(this,0),astXmlCheckElement(elem,0),STATUS_PTR) +#define astXmlPurge(this) astXmlPurge_(astXmlCheckParent(this,1),STATUS_PTR) +#define astXmlSetXmlDec(this,text) astXmlSetXmlDec_(astXmlCheckDocument(this,0),text,STATUS_PTR) +#define astXmlSetDTDec(this,text1,text2,text3) astXmlSetDTDec_(astXmlCheckDocument(this,0),text1,text2,text3,STATUS_PTR) +#define astXmlCheckType(this,type) astXmlCheckType_(this,type,STATUS_PTR) +#define astXmlCopy(this) astXmlCopy_(astXmlCheckObject(this,1),STATUS_PTR) + +#ifdef DEBUG +#define astXmlTrace(show) astXmlTrace_(show) +#endif + +#endif + + + diff --git a/xmlchan.c b/xmlchan.c new file mode 100644 index 0000000..10cd2b0 --- /dev/null +++ b/xmlchan.c @@ -0,0 +1,14128 @@ +/* +*class++ +* Name: +* XmlChan + +* Purpose: +* I/O Channel using XML to represent Objects. + +* Constructor Function: +c astXmlChan +f AST_XMLCHAN + +* Description: +* A XmlChan is a specialised form of Channel which supports XML I/O +* operations. Writing an Object to an XmlChan (using +c astWrite) will, if the Object is suitable, generate an +f AST_WRITE) will, if the Object is suitable, generate an +* XML description of that Object, and reading from an XmlChan will +* create a new Object from its XML description. +* +* Normally, when you use an XmlChan, you should provide "source" +c and "sink" functions which connect it to an external data store +c by reading and writing the resulting XML text. These functions +f and "sink" routines which connect it to an external data store +f by reading and writing the resulting XML text. These routines +* should perform any conversions needed between external character +c encodings and the internal ASCII encoding. If no such functions +f encodings and the internal ASCII encoding. If no such routines +* are supplied, a Channel will read from standard input and write +* to standard output. +* +* Alternatively, an XmlChan can be told to read or write from +* specific text files using the SinkFile and SourceFile attributes, +* in which case no sink or source function need be supplied. + +* Inheritance: +* The XmlChan class inherits from the Channel class. + +* Attributes: +* In addition to those attributes common to all Channels, every +* XmlChan also has the following attributes: +* +* - XmlFormat: System for formatting Objects as XML +* - XmlLength: Controls output buffer length +* - XmlPrefix: The namespace prefix to use when writing + +* Functions: +c The XmlChan class does not define any new functions beyond those +f The XmlChan class does not define any new routines beyond those +* which are applicable to all Mappings. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2009 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* . + +* Authors: +* DSB: David Berry (Starlink) + +* History: +* 10-OCT-2003 (DSB): +* Original version. +* 6-FEB-2004 (DSB): +* Added XmlPrefix and XmlFormat attributes. +* 10-FEB-2004 (DSB): +* - Added debug conditional code to keep track of memory leaks. +* - Fixed bug which prevented more than 1 object being read from +* an XmlChan. +* 7-DEC-2005 (DSB): +* Free memory allocated by calls to astReadString. +* 12-FEB-2010 (DSB): +* Represent AST__BAD externally using the string "". +*class-- + +* Further STC work: +* - Speed up general STC processing (a lot of time seems to be spent +* simplifying things) +* - Document (including a complete description of what is and is not +* supported in the reference docs for the XmlFormat attribute). +* - Produce a schema describing the format which can in fact be read by +* AST. +* - Look at Jonathan McDowell's mini-STC schema (also STC stuff in +* spectral data model) +* - Web services. Read only: test STCs for overlap, test points for +* inclusion/exclusion, plot a mask over an image, verification (can AST +* read it & does it generate warnings?). Read/Write: convert FITS to STC, +* transform STC into a new coord system. +* - Add support for writing as well as reading +* - Modify Stc... constructors to check that the supplied Frame is suitable. +* - What about multiple AstroCoordFrames and AstroCoordAreas in a STC? +* - Add support for generic CoordFrames +* - What should be done with pixel coords info within STC? +* - Extend coverage (e.g. to 3D space frames, etc) + +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS XmlChan + +/* The XML element name used to store an AST attribute setting */ +#define ATTR "_attribute" + +/* The XML element name used for an AST "isa" element */ +#define ISA "_isa" + +/* The XML attribute name which holds the name of the AST class which + defines the item contained in the element. */ +#define DEFINEDBY "definedby" + +/* The XML attribute name which holds the name of the AST attribute */ +#define NAME "name" + +/* The XML attribute name which holds the value of the AST attribute */ +#define VALUE "value" + +/* The XML attribute name which indicates if the AST attribute value is a + default value. */ +#define DEFAULT "default" + +/* The XML attribute name which indicates if the AST attribute value was + originally a string value. */ +#define QUOTED "quoted" + +/* The XML attribute name which holds a description of the AST attribute. */ +#define DESC "desc" + +/* The XML attribute name which holds the label associated with an AST + Object (if any). */ +#define LABEL "label" + +/* A string used to indicate atrue attribute value */ +#define TRUE "true" + +/* Format identifiers and strings */ +#define UNKNOWN_FORMAT -1 +#define NATIVE_FORMAT 0 +#define QUOTED_FORMAT 1 +#define IVOA_FORMAT 2 +#define MAX_FORMAT 2 +#define UNKNOWN_STRING "UNKNOWN" +#define NATIVE_STRING "NATIVE" +#define QUOTED_STRING "QUOTED" +#define IVOA_STRING "IVOA" + +/* Values representing message severities. */ +#define WARNING 0 +#define FAILURE 1 +#define RESET 2 + +/* Known IVOA namespaces. When a new name is added, update the FindIVOAClass + function. */ +#define STC_URI "urn:nvo-stc" + +/* Known IVOA Classes and attributes. When a new name is added, it may be + necessary to update the FindIVOAClass function. */ +#define STC_RESOURCE_PROFILE "STCResourceProfile" +#define SEARCH_LOCATION "SearchLocation" +#define OBSERVATION_LOCATION "ObservationLocation" +#define OBSERVATORY_LOCATION "ObservatoryLocation" +#define CATALOG_ENTRY_LOCATION "CatalogEntryLocation" +#define OBS_DATA_LOCATION "ObsDataLocation" +#define ASTRO_COORD_SYSTEM "AstroCoordSystem" +#define ASTRO_COORD_AREA "AstroCoordArea" +#define ASTRO_COORDS "AstroCoords" +#define TIME_FRAME "TimeFrame" +#define SPACE_FRAME "SpaceFrame" +#define SPECTRAL_FRAME "SpectralFrame" +#define REDSHIFT_FRAME "RedshiftFrame" +#define DOPPLER_DEFINITION "DopplerDefinition" + +/* Returns string "an" or "a" depending on whether the first character of + the supplied string is a vowel or not. */ +#define ANA(t) (t?(strchr("AaEeIiOoUu",t[0])?"an":"a"):"") + +/* String used to represent AST__BAD externally. */ +#define BAD_STRING "" + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ + +#include "globals.h" /* Thread-safe global data access */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "object.h" /* Base Object class */ +#include "frame.h" /* Coordinate Frames */ +#include "timeframe.h" /* Time coordinate Frames */ +#include "cmpframe.h" /* Coordinate Frames */ +#include "skyframe.h" /* Celestial coordinate Frames */ +#include "specframe.h" /* Spectral coordinate Frames */ +#include "region.h" /* Regions within coordinate Frames */ +#include "ellipse.h" /* Ellipses within coordinate Frames */ +#include "pointlist.h" /* Points within coordinate Frames */ +#include "polygon.h" /* Polygons within coordinate Frames */ +#include "circle.h" /* Circles within coordinate Frames */ +#include "keymap.h" /* Mapping of keys to values */ +#include "channel.h" /* Interface for parent class */ +#include "xmlchan.h" /* Interface definition for this class */ +#include "loader.h" /* Interface to the global loader */ +#include "object.h" /* Base Object class */ +#include "wcsmap.h" /* Angular conversion constants */ +#include "xml.h" /* AST XML facilities */ +#include "erfa.h" /* ERFA functions */ +#include "stcresourceprofile.h" /* IVOA StcResourceProfile class */ +#include "stcsearchlocation.h" /* IVOA SearchLocation class */ +#include "stccatalogentrylocation.h"/* IVOA CatalogEntryLocation class */ +#include "stcobsdatalocation.h" /* IVOA ObsDataLocation class */ +#include "nullregion.h" /* Null regions */ +#include "interval.h" /* Axis intervals */ +#include "box.h" /* Box regions */ +#include "cmpregion.h" /* Compound regions */ +#include "prism.h" /* Prism regions */ +#include "unitmap.h" /* Unit Mappings */ +#include "unit.h" /* Unit handling utilities */ +#include "pal.h" /* slalib functions */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include +#include +#include +#include +#include +#include +#include + +/* Type Definitions */ +/* ================ */ + +/* A type for functions which read an IVOA element and return a + corresponding AST Object. */ +typedef AstObject *(*IVOAReader)( AstXmlChan *, AstXmlElement *, int * ); + +/* A structure to hold the result of scanning the content of an IVOA + element.*/ +typedef struct IVOAScan { + int n; /* Number of element names described by this structure */ + int *count; /* Array holding number of each element name found */ + AstXmlElement ***el; /* Array holding pointers to each element found */ +} IVOAScan; + +/* Module Variables. */ +/* ================= */ + +/* Address of this static variable is used as a unique identifier for + member of this class. */ +static int class_check; + +/* Pointers to parent class methods which are extended by this class. */ +static const char *(* parent_getattrib)( AstObject *, const char *, int * ); +static int (* parent_testattrib)( AstObject *, const char *, int * ); +static void (* parent_clearattrib)( AstObject *, const char *, int * ); +static void (* parent_setattrib)( AstObject *, const char *, int * ); +static int (* parent_getfull)( AstChannel *, int * ); +static int (* parent_getcomment)( AstChannel *, int * ); +static int (* parent_getindent)( AstChannel *, int * ); + +/* Text values used to represent XmlFormat values externally. These + should be in the order defined by the associated constants above. */ +static const char *xformat[3] = { NATIVE_STRING, QUOTED_STRING, IVOA_STRING }; + +/* Define macros for accessing each item of thread specific global data. */ +#ifdef THREAD_SAFE + +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; \ + globals->IsUsable_This = NULL; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->GetNextChar_C = NULL; \ + globals->GetNextChar_Buf = NULL; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(XmlChan) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(XmlChan,Class_Init) +#define class_vtab astGLOBAL(XmlChan,Class_Vtab) +#define isusable_this astGLOBAL(XmlChan,IsUsable_This) +#define getattrib_buff astGLOBAL(XmlChan,GetAttrib_Buff) +#define getnextchar_c astGLOBAL(XmlChan,GetNextChar_C) +#define getnextchar_buf astGLOBAL(XmlChan,GetNextChar_Buf) + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* An XmlChan pointer use to communicate with the IsUsable function. */ +static AstXmlChan *isusable_this = NULL; + +/* Buffer returned by GetAttrib. */ +static char getattrib_buff[ 51 ]; + +/* Variables used in GetNextChar */ +static char *getnextchar_c = NULL; /* Pointer to next character to read */ +static char *getnextchar_buf = NULL; /* Pointer to previously read text */ + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstXmlChanVtab class_vtab; /* Virtual function table */ +static int class_init = 0; /* Virtual function table initialised? */ + +#endif + + +/* External Interface Function Prototypes. */ +/* ======================================= */ +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstXmlChan *astXmlChanForId_( const char *(*)( void ), + char *(*)( const char *(*)( void ), int * ), + void (*)( const char * ), + void (*)( void (*)( const char * ), const char *, int * ), + const char *, ... ); +AstXmlChan *astXmlChanId_( const char *(* source)( void ), + void (* sink)( const char * ), + const char *options, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstObject *AstroCoordSystemReader( AstXmlChan *, AstXmlElement *, int * ); +static AstObject *MakeAstFromXml( AstXmlChan *, AstXmlElement *, int * ); +static AstObject *ObsDataLocationReader( AstXmlChan *, AstXmlElement *, int * ); +static AstObject *Read( AstChannel *, int * ); +static AstObject *ReadObject( AstChannel *, const char *, AstObject *, int * ); +static AstObject *RedshiftFrameReader( AstXmlChan *, AstXmlElement *, int * ); +static AstObject *SpaceFrameReader( AstXmlChan *, AstXmlElement *, int * ); +static AstObject *SpectralFrameReader( AstXmlChan *, AstXmlElement *, int * ); +static AstObject *StcMetadataReader( AstXmlChan *, AstXmlElement *, int * ); +static AstObject *TimeFrameReader( AstXmlChan *, AstXmlElement *, int * ); +static AstPointList *ObservatoryLocationReader( AstXmlChan *, AstXmlElement *, AstStcObsDataLocation *, int * ); +static AstRegion *AllSkyReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *AstroCoordAreaReader( AstXmlChan *, AstXmlElement *, AstFrame *, AstRegion *[4], int, AstKeyMap **, int * ); +static AstRegion *BoxReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *CircleReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *ConstraintReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *ConvexReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *Coord2VecIntervalReader( AstXmlChan *, AstXmlElement *, const char *, AstFrame *, int * ); +static AstRegion *Coord3VecIntervalReader( AstXmlChan *, AstXmlElement *, const char *, AstFrame *, int * ); +static AstRegion *CoordScalarIntervalReader( AstXmlChan *, AstXmlElement *, const char *, AstFrame *, int * ); +static AstRegion *EllipseReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *IntersectionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *NegationReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *PolygonReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *Position2DReader( AstXmlChan *, AstXmlElement *, AstFrame *, double *, AstKeyMap **, int * ); +static AstRegion *PositionIntervalReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *RedshiftIntervalReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *RedshiftReader( AstXmlChan *, AstXmlElement *, AstFrame *, AstKeyMap **, int * ); +static AstRegion *StcRegionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *RegionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *SpectralIntervalReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *SpectralReader( AstXmlChan *, AstXmlElement *, AstFrame *, double *, AstKeyMap **, int * ); +static AstRegion *SphereReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstRegion *TimeIntervalReader( AstXmlChan *, AstXmlElement *, AstTimeFrame *, int * ); +static AstRegion *TimeReader( AstXmlChan *, AstXmlElement *, AstTimeFrame *, double *, AstKeyMap **, int * ); +static AstRegion *UnionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * ); +static AstSystemType RedshiftSys( AstXmlChan *, AstXmlElement *, char **, int, int * ); +static AstSystemType SpecSys( AstXmlChan *, AstXmlElement *, const char *, int, int * ); +static AstXmlElement *FindAttribute( AstXmlChan *, const char *, int * ); +static AstXmlElement *FindElement( AstXmlChan *, AstXmlElement *, const char *, int * ); +static AstXmlElement *FindObject( AstXmlChan *, const char *, int * ); +static AstXmlElement *MakePos2D( AstXmlChan *, AstXmlElement *, int * ); +static AstXmlElement *ReadXmlText( AstXmlChan *, int * ); +static AstXmlElement *Remove( AstXmlChan *, AstXmlElement *, int * ); +static IVOAReader FindIVOAClass( AstXmlElement *, int *, int * ); +static IVOAScan *FreeIVOAScan( IVOAScan *, int * ); +static IVOAScan *ScanIVOAElement( AstXmlChan *, AstXmlElement *, int, const char *[], int[], int[], int * ); +static char *ReadString( AstChannel *, const char *, const char *, int * ); +static char *SourceWrap( const char *(*)( void ), int * ); +static char GetNextChar( void *, int * ); +static const char *FindNextIsA( AstXmlElement *, int, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetTag( AstXmlObject *, int, int * ); +static double AstronTimeReader( AstXmlChan *, AstXmlElement *, AstTimeFrame *, int * ); +static double AttrValueD( AstXmlChan *, AstXmlElement *, const char *, double, int * ); +static double ElemValueD( AstXmlChan *, AstXmlElement *, double, int * ); +static double Error2PAReader( AstXmlChan *, AstXmlElement *, double *, int * ); +static double MakeMJD( AstTimeFrame *, double, int * ); +static double PosAngleReader( AstXmlChan *, AstXmlElement *, int * ); +static double ReadDouble( AstChannel *, const char *, double, int * ); +static int AstroCoordsReader( AstXmlChan *, AstXmlElement *, AstFrame *, AstRegion *[4], AstKeyMap **, int * ); +static int AttrValueB( AstXmlChan *, AstXmlElement *, const char *, int, int * ); +static int AttrValueI( AstXmlChan *, AstXmlElement *, const char *, int, int * ); +static int ElemListD( AstXmlChan *, AstXmlElement *, int, double *, int * ); +static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * ); +static int GetComment( AstChannel *, int * ); +static int GetFull( AstChannel *, int * ); +static int GetIndent( AstChannel *, int * ); +static int IsUsable( AstXmlElement *, int * ); +static int ReadInt( AstChannel *, const char *, int, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int Use( AstXmlChan *, int, int, int * ); +static int Ustrcmp( const char *, const char *, int * ); +static int Ustrncmp( const char *, const char *, size_t, int * ); +static int VertexReader( AstXmlChan *, AstXmlElement *, double *, double *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void FillAndLims( AstXmlChan *, AstXmlElement *, AstRegion *, int * ); +static void OutputText( AstXmlChan *, const char *, int, int * ); +static void ReCentreAnc( AstRegion *, int, AstKeyMap **, int * ); +static void ReadClassData( AstChannel *, const char *, int * ); +static void Report( AstXmlChan *, AstXmlElement *, int, const char *, int * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SinkWrap( void (*)( const char * ), const char *, int * ); +static void WriteBegin( AstChannel *, const char *, const char *, int * ); +static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * ); +static void WriteEnd( AstChannel *, const char *, int * ); +static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * ); +static void WriteIsA( AstChannel *, const char *, const char *, int * ); +static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * ); +static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * ); +static AstTimeScaleType TimeScaleReader( AstXmlChan *, AstXmlElement *, int * ); + +static int TestXmlLength( AstXmlChan *, int * ); +static void ClearXmlLength( AstXmlChan *, int * ); +static void SetXmlLength( AstXmlChan *, int, int * ); +static int GetXmlLength( AstXmlChan *, int * ); + +static int TestXmlFormat( AstXmlChan *, int * ); +static void ClearXmlFormat( AstXmlChan *, int * ); +static void SetXmlFormat( AstXmlChan *, int, int * ); +static int GetXmlFormat( AstXmlChan *, int * ); + +static int TestXmlPrefix( AstXmlChan *, int * ); +static void ClearXmlPrefix( AstXmlChan *, int * ); +static void SetXmlPrefix( AstXmlChan *, const char *, int * ); +static const char * GetXmlPrefix( AstXmlChan *, int * ); + +/* Member functions. */ +/* ================= */ + +static AstRegion *AllSkyReader( AstXmlChan *this, AstXmlElement *elem, + AstFrame *frm, int *status ){ +/* +* Name: +* AllSkyReader + +* Purpose: +* Make an AST Region from an IVOA AllSky element. + +* Type: +* Private function. + +* Synopsis: +* #include "xmlchan.h" +* AstRegion *AllSkyReader( AstXmlChan *this, AstXmlElement *elem, +* AstFrame *frm, int *status ) + +* Class Membership: +* XmlChan member function. + +* Description: +* This function makes a new AST Region from the supplied IVOA +* AllSky element. + +* Parameters: +* this +* Pointer to the XmlChan. +* elem +* Pointer to the IVOA AllSky element. +* frm +* Pointer to the 2D Frame in which the returned Region should be +* defined. If the Unit attribute is not set, this function will +* set it to the value supplied in "unit" before returning. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new Region. + +*/ + +/* Local Variables: */ + AstRegion *new; /* Pointer to returned Region */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Create a negated NullRegion (this is a boundless Region which includes + all points in the Frame). */ + new = (AstRegion *) astNullRegion( frm, NULL, "negated=1", status ); + +/* Get any fill factor from the element and assign to the returned Region. */ + FillAndLims( this, elem, new, status ); + +/* Annul any returned Frame if an error has occurred. */ + if( !astOK ) new = astAnnul( new ); + +/* Return the pointer to the new Region. */ + return new; +} + +static AstRegion *AstroCoordAreaReader( AstXmlChan *this, AstXmlElement *elem, + AstFrame *frm, AstRegion *uncs[4], + int nanc, AstKeyMap **ancs, int *status ) { +/* +* Name: +* AstroCoordAreaReader + +* Purpose: +* Make an AST Region from an IVOA AstroCoordArea element. + +* Type: +* Private function. + +* Synopsis: +* #include "xmlchan.h" +* AstRegion *AstroCoordAreaReader( AstXmlChan *this, AstXmlElement *elem, +* AstFrame *frm, AstRegion *uncs[4], +* int nanc, AstKeyMap **ancs, int *status ) + +* Class Membership: +* XmlChan member function. + +* Description: +* This function makes a new AST Region from the supplied IVOA +* AstroCoordArea element. + +* Parameters: +* this +* Pointer to the XmlChan. +* elem +* Pointer to the IVOA AstroCoordArea element. May be NULL, in +* which case a NullRegion is returned. +* frm +* The Frame in which the returned Region is to be defined. If +* Units or reference values (Epoch, RestFreq, RefRA, etc) are not set +* for any axes, then they will be set by this function if possible. +* uncs +* Array holding pointers to the uncertainty Regions to be associated +* with each of the four STC domains (space, time, spectral, redshift). +* NULL should be suppied in any element for which no uncertainty is +* available. +* nanc +* Number of KeyMap pointers stored in "ancs" +* ancs +* Pointer to an array of "nanc" elements, each being a pointer to +* a KeyMap. Each one describes the ancilary information in an +* AstroCoords element associated with the AstroCoordsArea decribed +* by "region". Each KeyMap has elements with keys AST__STCERROR, +* AST__STCRES, AST__STCSIZE, AST__STCPIXSZ, AST__STCVALUE each of +* which holds a pointer to a Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new Region. +*/ + +/* Local Variables: */ + AstRegion *r; + AstFrame *cfrm; + AstFrame *fr; + AstFrame *pfrm; + AstFrame *red_frame; + AstFrame *space_frame; + AstFrame *spec_frame; + AstFrameSet *fs; + AstMapping *map; + AstObject *o; + AstRegion **red_list; + AstRegion **spec_list; + AstRegion **space_list; + AstRegion **time_list; + AstRegion *new; + AstRegion *reg; + AstRegion *rred; + AstRegion *rspec; + AstRegion *rspace; + AstRegion *rtime; + AstRegion *sum; + AstRegion *tmp; + AstTimeFrame *time_frame; + IVOAScan *scan; + char *decset; + char *raset; + char buff[ DBL_DIG + 30 ]; + char setting[ 100 ]; + const char *dom; + const char *id; + const char *names[4]; + const char *name; + const char *old_units; + const char *text; + double decref; + double lbnd[2]; + double raref; + double space_val[2]; + double spec_val; + double time_val; + double ubnd[2]; + int i; + int ianc; + int ired; + int ispace; + int ispec; + int itime; + int k; + int l; + int max[4]; + int min[4]; + int nax; + int nred; + int nspace; + int nspec; + int ntime; + int paxis; + + static const char *key[ 5 ] = { AST__STCERROR, + AST__STCRES, + AST__STCSIZE, + AST__STCPIXSZ, + AST__STCVALUE }; + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If null AstroCoordArea element has been supplied, return a NullRegion. */ + if( !elem ) { + new = (AstRegion *) astNullRegion( frm, NULL, "", status ); + +/* Otherwise, create a Region of suitable class. */ + } else { + +/* First identify the individual Frames within the supplied Frame. Current + implementation for spatial axes is limited to celestial longitude and + latitude. */ + space_frame = NULL; + spec_frame = NULL; + red_frame = NULL; + time_frame = NULL; + + nax = astGetNaxes( frm ); + for( i = 0; i < nax; i++ ) { + astPrimaryFrame( frm, i, &pfrm, &paxis ); + dom = astGetDomain( pfrm ); + if( !strcmp( dom, "SKY" ) ) { + if( !space_frame ) { + space_frame = astClone( pfrm ); + } else if( pfrm != space_frame) { + Report( this, elem, FAILURE, "contains more than 2 spatial axes", status ); + } + + } else if( !strcmp( dom, "TIME" ) ) { + if( !time_frame ) { + if( astIsATimeFrame( pfrm ) ) { + time_frame = (AstTimeFrame *) astClone( pfrm ); + } else if( astOK ) { + astError( AST__INTER, "AstroCoordAreaReader(XmlChan): %s " + "supplied where TimeFrame expected (internal " + "AST programming error).", status, astGetClass( pfrm ) ); + } + } else { + Report( this, elem, FAILURE, "contains more than 1 time axis", status ); + } + + } else if( !strcmp( dom, "SPECTRUM" ) ) { + if( !spec_frame ) { + spec_frame = astClone( pfrm ); + } else { + Report( this, elem, FAILURE, "contains more than 1 spectral axis", status ); + } + + } else if( !strcmp( dom, "REDSHIFT" ) ) { + if( !red_frame ) { + red_frame = astClone( pfrm ); + } else { + Report( this, elem, FAILURE, "contains more than 1 redshift axis", status ); + } + + } else { + Report( this, elem, FAILURE, "contains axes for an unsupported domain", status ); + } + pfrm = astAnnul( pfrm ); + } + +/* Search the supplied element for the required sub-elements. */ + names[ 0 ] = "Sphere|PositionInterval|Region"; + names[ 1 ] = "TimeInterval"; + names[ 2 ] = "SpectralInterval"; + names[ 3 ] = "RedshiftInterval"; + min[ 0 ] = 0; + min[ 1 ] = 0; + min[ 2 ] = 0; + min[ 3 ] = 0; + max[ 0 ] = INT_MAX; + max[ 1 ] = INT_MAX; + max[ 2 ] = INT_MAX; + max[ 3 ] = INT_MAX; + scan = ScanIVOAElement( this, elem, 4, names, min, max, status ); + +/* If succesfull.. */ + if( scan ) { + +/* Create Regions for all the SpatialIntervals found in the supplied element. */ + space_val[ 0 ] = AST__BAD; + space_val[ 1 ] = AST__BAD; + nspace = scan->count[ 0 ]; + space_list = astMalloc( sizeof(AstRegion *)*(size_t)nspace ); + if( space_list ) { + for( ispace = 0; ispace < nspace; ispace++ ) { + name = astXmlGetName( scan->el[ 0 ][ ispace ] ); + if( !strcmp( name, "Sphere" ) ) { + space_list[ ispace ] = SphereReader( this, + scan->el[ 0 ][ ispace ], + space_frame, status ); + } else if( !strcmp( name, "PositionInterval" ) ) { + space_list[ ispace ] = PositionIntervalReader( this, + scan->el[ 0 ][ ispace ], + space_frame, status ); + } else if( !strcmp( name, "Region" ) ) { + space_list[ ispace ] = StcRegionReader( this, + scan->el[ 0 ][ ispace ], + space_frame, status ); + } else if( astOK ) { + astError( AST__INTER, "AstroCoordAreaReader(XmlChan): " + "SpatialInterval type %s not yet supported " + "(AST internal programming error).", status, name ); + break; + } + +/* Store any uncertainty region.*/ + if( uncs[ 0 ] ) astSetUnc( space_list[ ispace ], uncs[ 0 ] ); + + } + +/* If the spatial region is a single point we will use the point as the + reference position for any SpecFrames which are created. If there is + just one spatial interval, and if it is bounded. and if the bounds are + equal on both axes, note the mean position. */ + if( nspace == 1 ){ + if( astGetBounded( space_list[ 0 ] ) ) { + astGetRegionBounds( space_list[ 0 ], lbnd, ubnd ); + if( astEQUAL( lbnd[ 0 ], ubnd[ 0 ] ) && + astEQUAL( lbnd[ 1 ], ubnd[ 1 ] ) ) { + space_val[ 0 ] = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] ); + space_val[ 1 ] = 0.5*( lbnd[ 1 ] + ubnd[ 1 ] ); + } + } + } + } + +/* Create Regions for all the TimeIntervals found in the supplied element. */ + time_val = AST__BAD; + ntime = scan->count[ 1 ]; + time_list = astMalloc( sizeof(AstRegion *)*(size_t)ntime ); + if( time_list ) { + for( itime = 0; itime < ntime; itime++ ) { + time_list[ itime ] = TimeIntervalReader( this, + scan->el[ 1 ][ itime ], + time_frame, status ); + +/* Store any uncertainty region. Transfer the System and TimeOrigin + values from the time region to the time uncertainty, if set. */ + if( uncs[ 1 ] ) { + + if( astTestSystem( time_frame ) && + astTestTimeOrigin( time_frame ) ) { + + sprintf( setting, "System=%s", + astGetC( time_frame, "System" ) ); + astRegSetAttrib( uncs[ 1 ], setting, NULL ); + + + if( astTestUnit( time_frame, 0 ) ) { + old_units = astGetUnit( time_frame, 0 ); + old_units = astStore( NULL, old_units, + strlen( old_units ) + 1 ); + } else { + old_units = NULL; + } + + astSetUnit( time_frame, 0, astGetUnit( uncs[ 1 ], 0 ) ); + + sprintf( setting, "TimeOrigin=%s", + astGetC( time_frame, "TimeOrigin" ) ); + astRegSetAttrib( uncs[ 1 ], setting, NULL ); + + if( old_units ) { + astSetUnit( time_frame, 0, old_units ); + old_units = astFree( (void *) old_units ); + } else { + astClearUnit( time_frame, 0 ); + } + + } + + astSetUnc( time_list[ itime ], uncs[ 1 ] ); + } + } + +/* Use the mid point as the Epoch for all Frames which are created. If + either limit is not specified, use the specified limit. */ + if( ntime > 0 ){ + astGetRegionBounds( time_list[ 0 ], lbnd, ubnd ); + if( fabs( lbnd[ 0 ] ) != DBL_MAX && lbnd[ 0 ] != AST__BAD ){ + if( fabs( ubnd[ 0 ] ) != DBL_MAX && ubnd[ 0 ] != AST__BAD ){ + time_val = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] ); + } else { + time_val = lbnd[ 0 ]; + } + } else if( fabs( ubnd[ 0 ] ) != DBL_MAX && ubnd[ 0 ] != AST__BAD ){ + time_val = ubnd[ 0 ]; + } + } + } + +/* Create Regions for all the SpectralIntervals found in the supplied element. */ + spec_val = AST__BAD; + nspec = scan->count[ 2 ]; + spec_list = astMalloc( sizeof(AstRegion *)*(size_t)nspec ); + if( spec_list ) { + for( ispec = 0; ispec < nspec; ispec++ ) { + spec_list[ ispec ] = SpectralIntervalReader( this, + scan->el[ 2 ][ ispec ], + spec_frame, status ); +/* Store any uncertainty region.*/ + if( uncs[ 2 ] ) astSetUnc( spec_list[ ispec ], uncs[ 2 ] ); + } + +/* If the spectral region is a single point we will use the point as the + rest frequency for all RedShift Frames which are created. If there is just + one spectral interval, and if it is bounded. and if the bounds are equal, + note the mean spectral value. */ + if( nspec == 1 ){ + if( astGetBounded( spec_list[ 0 ] ) ) { + astGetRegionBounds( spec_list[ 0 ], lbnd, ubnd ); + if( astEQUAL( lbnd[ 0 ], ubnd[ 0 ] ) ) { + spec_val = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] ); + } + } + } + } + +/* Create Regions for all the RedshiftIntervals found in the supplied element. */ + nred = scan->count[ 3 ]; + red_list = astMalloc( sizeof(AstRegion *)*(size_t)nred ); + if( red_list ) { + for( ired = 0; ired < nred; ired++ ) { + red_list[ ired ] = RedshiftIntervalReader( this, + scan->el[ 3 ][ ired ], + red_frame, status ); +/* Store any uncertainty region.*/ + if( uncs[ 3 ] ) astSetUnc( red_list[ ired ], uncs[ 3 ] ); + } + } + +/* Free the can result structure.*/ + scan = FreeIVOAScan( scan, status ); + +/* If the spatial regions cover only a single point, convert it to FK5 + J2000 and use it as the reference position for any SpecFrames (spectral or + redshift) unless values were inherited from the supplied Frame. If the + supplied Frame did not contain set values for these attributes, set them + now. Use astRegSetAttrib which applies the attribute setting to both + base and current Frame of the Region's FrameSet, and avoids re-mapping + the current Frame. */ + if( astOK ) { + if( space_val[ 0 ] != AST__BAD && space_val[ 1 ] != AST__BAD ) { + +/* First need to convert to FK5 J2000 and format into a string for use with + astRegSetAttrib. Need to ensure that the Format and Digits attributes + are set to values which will result in no loss of precision in the + formatting and unformatting steps. */ + fr = astCopy( space_frame ); + astClear( fr, "Format(1),Format(2),Digits(1),Digits(2)" ); + astSet( fr, "digits=%d,system=FK5,equinox=J2000", status, DBL_DIG); + fs = astConvert( space_frame, fr, "" ); + fr = astAnnul( fr ); + if( fs ) { + astTran2( fs, 1, space_val, space_val + 1, 1, &raref, &decref ); + + text = astFormat( fs, raref, 0 ); + l = text ? strlen( text ) : 0; + raset = astMalloc( l + 10 ); + if( raset ) sprintf( raset, "refra=%s", text ); + + text = astFormat( fs, decref, 1 ); + l = text ? strlen( text ) : 0; + decset = astMalloc( l + 10 ); + if( decset ) sprintf( decset, "refdec=%s", text ); + + fs = astAnnul( fs ); + +/* Now set the FK5 J2000 values in the required Frames and Regions. */ + if( !spec_frame || !astTestRefRA( spec_frame ) || + !astTestRefDec( spec_frame ) ) { + for( ispec = 0; ispec < nspec; ispec++ ) { + astRegSetAttrib( spec_list[ ispec ], raset, NULL ); + astRegSetAttrib( spec_list[ ispec ], decset, NULL ); + } + + if( spec_frame ) { + astSetRefRA( (AstSpecFrame *) spec_frame, raref ); + astSetRefDec( (AstSpecFrame *) spec_frame, decref ); + } + } + + if( !red_frame || !astTestRefRA( red_frame ) || + !astTestRefDec( red_frame ) ) { + for( ired = 0; ired < nred; ired++ ) { + astRegSetAttrib( red_list[ ired ], raset, NULL ); + astRegSetAttrib( red_list[ ired ], decset, NULL ); + } + + if( red_frame ) { + astSetRefRA( (AstSpecFrame *) red_frame, raref ); + astSetRefDec( (AstSpecFrame *) red_frame, decref ); + } + } + + for( ianc = 0; ianc < nanc; ianc++ ) { + for( k = 0; k < 5; k++ ) { + if( astMapGet0A( ancs[ ianc ], key[ k ], &o ) ) { + r = (AstRegion *) o; + astRegSetAttrib( r, raset, NULL ); + astRegSetAttrib( r, decset, NULL ); + r = astAnnul( r ); + } + } + } + +/* Free resources. */ + if( raset ) raset = astFree( raset ); + if( decset ) decset = astFree( decset ); + + } else if( astOK ) { + astError( AST__INTER, "AstroCoordAreaReader(XmlChan):" + " Cannot convert spatial position to FK5 J2000" , status); + } + } + +/* If a time region was specified, use a typical value as the epoch for + all Frames. Call MakeMJD to convert "time_val" from the system of the + TimeFrame to an MJD (as required by the Frame Epoch attribute). Set + the value in both the returned Region and the supplied Frame. */ + if( time_val != AST__BAD ) { + fr = astRegFrame( time_list[ 0 ] ); + if( astIsATimeFrame( fr ) ) { + time_val = MakeMJD( (AstTimeFrame *) fr, time_val, status ); + } else if( astOK ) { + astError( AST__INTER, "AstroCoordAreaReader(XmlChan): %s " + "supplied where TimeFrame expected (internal " + "AST programming error).", status, astGetClass( fr ) ); + } + fr = astAnnul( fr ); + + sprintf( buff, "epoch= MJD %.*g", DBL_DIG, time_val ); + + if( !space_frame || !astTestEpoch( space_frame ) ) { + for( ispace = 0; ispace < nspace; ispace++ ) { + astRegSetAttrib( space_list[ ispace ], buff, NULL ); + } + if( space_frame ) astSetEpoch( space_frame, time_val ); + } + + if( !spec_frame || !astTestEpoch( spec_frame ) ) { + for( ispec = 0; ispec < nspec; ispec++ ) { + astRegSetAttrib( spec_list[ ispec ], buff, NULL ); + } + if( spec_frame ) astSetEpoch( spec_frame, time_val ); + } + + if( !red_frame || !astTestEpoch( red_frame ) ) { + for( ired = 0; ired < nred; ired++ ) { + astRegSetAttrib( red_list[ ired ], buff, NULL ); + } + if( red_frame ) astSetEpoch( red_frame, time_val ); + } + + for( ianc = 0; ianc < nanc; ianc++ ) { + for( k = 0; k < 5; k++ ) { + if( astMapGet0A( ancs[ ianc ], key[ k ], &o ) ) { + r = (AstRegion *) o; + astRegSetAttrib( r, buff, NULL ); + r = astAnnul( r ); + } + } + } + + } + +/* If the spectral regions cover only a single point, format it with its + units so that the astSetAttrib function can convert it to Hz and use + it as the rest frequency for any redshift Frames. */ + if( spec_val != AST__BAD && nred > 0 ) { + + text = astGetUnit( spec_frame, 0 ); + if( text ) sprintf( buff, "restfreq= %.*g %s", DBL_DIG, + spec_val, text ); + + if( !red_frame || !astTestRestFreq( red_frame ) ) { + for( ired = 0; ired < nred; ired++ ) { + astRegSetAttrib( red_list[ ired ], buff, NULL ); + } + if( red_frame ) astSetAttrib( red_frame, buff ); + } + + for( ianc = 0; ianc < nanc; ianc++ ) { + for( k = 0; k < 5; k++ ) { + if( astMapGet0A( ancs[ ianc ], key[ k ], &o ) ) { + r = (AstRegion *) o; + astRegSetAttrib( r, buff, NULL ); + r = astAnnul( r ); + } + } + } + } + +/* Create Regions corresponding to every possible combination of interval + on each axis type, and assemble the union of these into a CmpRegion (if + there is more than one). */ + sum = NULL; + +/* Initialise indices of the sub-Frame intervals to use. */ + ispace = 0; + itime = 0; + ispec = 0; + ired = 0; + +/* Loop over all possible combinations of time+space+spec+red intervals. */ + while( 1 ) { + rspace = ( ispace < nspace ) ? space_list[ ispace ] : NULL; + rtime = ( itime < ntime ) ? time_list[ itime ] : NULL; + rspec = ( ispec < nspec ) ? spec_list[ ispec ] : NULL; + rred = ( ired < nred ) ? red_list[ ired ] : NULL; + +/* Prism Regions extrude a Region into higher dimensions, and the + extrusion is defined by an Interval. Spatial Regions are not + restricted to Intervals and so any spatial Region must be the first + Region to be included in the Prism (all the other axis types *are* + restricted to Intervals and so can be used to extrude the spatial + region). */ + reg = rspace ? astClone( rspace ) : NULL; + +/* Now extrude this region (if any) into the time axis. */ + if( rtime ) { + if( reg ) { + tmp = (AstRegion *) astPrism( reg, rtime, "", status ); + (void) astAnnul( reg ); + reg = tmp; + } else { + reg = astClone( rtime ); + } + } + +/* Now extrude this region (if any) into the spectral axis. */ + if( rspec ) { + if( reg ) { + tmp = (AstRegion *) astPrism( reg, rspec, "", status ); + (void) astAnnul( reg ); + reg = tmp; + } else { + reg = astClone( rspec ); + } + } + +/* Now extrude this region (if any) into the redshift axis. */ + if( rred ) { + if( reg ) { + tmp = (AstRegion *) astPrism( reg, rred, "", status ); + (void) astAnnul( reg ); + reg = tmp; + } else { + reg = astClone( rred ); + } + } + + +/* If a Prism was created, add it into the CmpRegion which holds the + running sum of the union of all Prisms created so far. */ + if( reg ) { + if( !sum ) { + sum = astClone( reg ); + } else { + tmp = (AstRegion *) astCmpRegion( sum, reg, AST__OR, "", status ); + (void) astAnnul( sum ); + sum = tmp; + } + reg = astAnnul( reg ); + } + +/* Increment the indices of the next set of sub-Frame Intervals to use. + Leave the while loop when all combinations have been done. */ + if( ++ired >= nred ) { + ired = 0; + if( ++ispec >= nspec ) { + ispec = 0; + if( ++itime >= ntime ) { + itime = 0; + if( ++ispace >= nspace ) break; + } + } + } + } + +/* Simplify the total sum Region. */ + tmp = astSimplify( sum ); + (void) astAnnul( sum ); + sum = tmp; + +/* The axes in this sum Region may not be in the correct order or units (i.e + in the order and units specified in the supplied Frame). So use + astConvert to get a Mapping from the Frame represented by the sum + Region to the supplied Frame. */ + fs = astConvert( sum, frm, "" ); + if( fs ) { + +/* Unless the Mapping is a UnitMap, remap the sum Region into the + supplied Frame using this Mapping. */ + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + if( !astIsAUnitMap( map ) ) { + new = astMapRegion( sum, map, frm ); + } else { + new = astClone( sum ); + } + + map = astAnnul( map ); + fs = astAnnul( fs ); + + } else if( astOK ) { + astError( AST__INTER, "AstroCoordAreaReader(%s): Cannot " + "convert from supplied Frame to internal Frame (AST " + "internal programming error).", status, astGetClass( this ) ); + } + +/* Transfer selected properties from the supplied Frame to the current Frame + of the returned Region. */ + cfrm = astRegFrame( new ); + if( astTestIdent( frm ) ) astSetIdent( cfrm, astGetIdent( frm ) ); + if( astTestTitle( frm ) ) astSetTitle( cfrm, astGetTitle( frm ) ); + +/* Ensure the Epoch is set correctly in the Region */ + if( time_val != AST__BAD ) { + sprintf( buff, "epoch= MJD %.*g", DBL_DIG, time_val ); + astRegSetAttrib( new, buff, NULL ); + } + +/* Free resources. */ + cfrm = astAnnul( cfrm ); + sum = astAnnul( sum ); + } + + if( space_list ) { + for( i = 0; i < nspace; i++ ) space_list[ i ] = astAnnul( space_list[ i ] ); + space_list = astFree( space_list ); + } + + if( time_list ) { + for( i = 0; i < ntime; i++ ) time_list[ i ] = astAnnul( time_list[ i ] ); + time_list = astFree( time_list ); + } + + if( spec_list ) { + for( i = 0; i < nspec; i++ ) spec_list[ i ] = astAnnul( spec_list[ i ] ); + spec_list = astFree( spec_list ); + } + + if( red_list ) { + for( i = 0; i < nred; i++ ) red_list[ i ] = astAnnul( red_list[ i ] ); + red_list = astFree( red_list ); + } + + } + + if( space_frame ) space_frame = astAnnul( space_frame ); + if( time_frame ) time_frame = astAnnul( time_frame ); + if( spec_frame ) spec_frame = astAnnul( spec_frame ); + if( red_frame ) red_frame = astAnnul( red_frame ); + +/* Get the ID attribute from the AstroCoordArea element and store in the + returned Region. */ + id = astXmlGetAttributeValue( elem, "ID" ); + if( id ) astSetIdent( new, id ); + + } + +/* If an error has occurred,annul the returned pointer. */ + if( !astOK ) new = astAnnul( new ); + +/* Return the pointer to the new Region. */ + return new; +} + +static int AstroCoordsReader( AstXmlChan *this, AstXmlElement *elem, + AstFrame *frm, AstRegion *uncs[4], + AstKeyMap **anc, int *status ) { +/* +* Name: +* AstroCoordsReader + +* Purpose: +* Modify a Frame to take account of an IVOA AstroCoords element, and +* return an coordinate uncertainties. + +* Type: +* Private function. + +* Synopsis: +* #include "xmlchan.h" +* int AstroCoordsReader( AstXmlChan *this, AstXmlElement *elem, +* AstFrame *frm, AstRegion *uncs[4], +* AstKeyMap **anc, int *status ) + +* Class Membership: +* XmlChan member function. + +* Description: +* This function modifies the supplied Frame object to incorporate the +* effects of the supplied AstroCoords element. It may also return +* Regions representing the bounds of the uncertainties in the four +* component coordinate Frames, depending on the contents of the +* AstroCoords element. + +* Parameters: +* this +* Pointer to the XmlChan. +* elem +* Pointer to the IVOA AstroCoords element. +* frm +* The Frame object to modify. +* uncs +* Array in which to return pointers to the uncertainty Regions to +* be associated with each of the four STC domains (space, time, +* spectral, redshift). NULL is returned in any element for which +* no uncertainty is specified within the supplied AstroCoords element. +* anc +* Address of a location at which to store the pointer to a newly +* created KeyMap holding ancillary information describing the +* AstroCoords element in the form required by constructors of AST +* Stc objects. A NULL pointer is returned if no usable ancillary +* information is found in the AstroCoords. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if any non-NULL values have been returned in the "uncs" +* array. Zero otherwise. + +*/ + +/* Local Variables: */ + AstFrame *afrm; /* Pointer to axis Frame */ + AstFrame *gfrm; /* Pointer to generic Frame */ + AstFrame *pfrm; /* Pointer to position Frame */ + AstFrame *rfrm; /* Pointer to redshift Frame */ + AstFrame *sfrm; /* Pointer to spectral Frame */ + AstTimeFrame *tfrm; /* Pointer to time Frame */ + AstKeyMap *panc; /* KeyMap holding spatial ancillary data */ + AstKeyMap *ranc; /* KeyMap holding redshift ancillary data */ + AstKeyMap *sanc; /* KeyMap holding spectral ancillary data */ + AstKeyMap *tanc; /* KeyMap holding temporal ancillary data */ + AstObject *o; /* Pointer to object retrieved from KeyMap */ + AstRegion *r; /* Individual ancillary Region */ + AstRegion *t; /* Total extruded ancillary Region */ + AstRegion *tt; /* Temporary Region pointer */ + AstXmlElement *el; /* Pointer to Position2D element */ + IVOAScan *scan; /* Structure holding scan results */ + char **anames; /* Pointer to list of ancillary name pointers */ + const char *dom; /* Pointer to Domain attribute value */ + const char *nam; /* Pointer to ancillary Name string */ + const char *names[4]; /* Names of the subelements to be searched for */ + char buff[100]; /* Message buffer */ + double epoch; /* Epoch */ + double hi; /* High limit for zero-width interval */ + double lo; /* Low limit for zero-width interval */ + double pos[2]; /* Reference spatial position */ + double rf; /* Rest frequency */ + int axes[2]; /* Indices of position axes */ + int axis; /* Index of next axis to use */ + int empty; /* Is returned KeyMap empty? */ + int i; /* Loop count */ + int isearth; /* Does the SkyFrame represent terrestrial lon/lat? */ + int junk; /* Unused integer value */ + int max[4]; /* Max allowed occurrences of each name */ + int min[4]; /* Min allowed occurrences of each name */ + int nax; /* Number of axes in supplied Frame */ + int unc; /* Any uncertainty Regions found? */ + int use; /* Use ancillary information? */ + + static const char *key[ 5 ] = { AST__STCERROR, + AST__STCRES, + AST__STCSIZE, + AST__STCPIXSZ, + AST__STCVALUE }; +/* Initialise */ + unc = 0; + uncs[ 0 ] = NULL; + uncs[ 1 ] = NULL; + uncs[ 2 ] = NULL; + uncs[ 3 ] = NULL; + *anc = NULL; + +/* Check the global error status. */ + if ( !astOK ) return unc; + +/* Search the supplied element for the required sub-elements. */ + names[ 0 ] = "Position2D|Position3D"; + names[ 1 ] = "Time"; + names[ 2 ] = "Spectral"; + names[ 3 ] = "Redshift"; + min[ 0 ] = 0; + min[ 1 ] = 0; + min[ 2 ] = 0; + min[ 3 ] = 0; + max[ 0 ] = 1; + max[ 1 ] = 1; + max[ 2 ] = 1; + max[ 3 ] = 1; + scan = ScanIVOAElement( this, elem, 4, names, min, max, status ); + +/* If succesfull.. */ + if( scan ) { + +/* Initialise pointers to component Frames */ + pfrm = NULL; + tfrm = NULL; + sfrm = NULL; + rfrm = NULL; + +/* Initialise pointers to KeyMaps holding ancillary data. */ + panc = NULL; + tanc = NULL; + sanc = NULL; + ranc = NULL; + +/* Allocate storage for an array of pointers to strings holding the Name + value for each axis. Initialise them to a null string. */ + nax = astGetNaxes( frm ); + anames = astMalloc( sizeof( char * )*(size_t)nax ); + for( i = 0; i < nax; i++ ) anames[ i ] = NULL; + +/* Initialise the index of the next Frame axis to use. */ + axis = 0; + +/* Check to see if the next 2 axes describe positions on the sky or earth + (see SpaceFrameReader). */ + axes[ 0 ] = 0; + axes[ 1 ] = 1; + afrm = astPickAxes( frm, 2, axes, NULL ); + dom = astGetDomain( afrm ); + isearth = dom && ( !strcmp( dom, "GEO_D" ) || + !strcmp( dom, "GEO_C" ) ); + + if( isearth || ( dom && !strcmp( dom, "SKY" ) ) ){ + astPrimaryFrame( frm, axis, &pfrm, &junk ); + if( scan->count[ 0 ] ) { + +/* We currently also use SkyFrames to represent geographical long/lat used to + describe observatory positions. These may have 3D positions, in which + case we convert the 3D position to a 2D position by ignoring the 3rd axis + value (height). See SpaceFrameReader. */ + el = MakePos2D( this, scan->el[ 0 ][ 0 ], status ); + +/* Use the Position2D to create a Region describing the uncertainty in + the space axes of the Frame. Also create a KeyMap holding Regions + describing any ancillary information stored in the Position2D. */ + uncs[ 0 ] = Position2DReader( this, el, pfrm, pos, &panc, status ); + if( uncs[ 0 ] ) unc = 1; + el = astXmlDelete( el ); + +/* If ancillary information was returned, extract the Name element, and + store it twice (once for each axis) in the "names" array. */ + if( panc && astMapGet0C( panc, AST__STCNAME, &nam ) ) { + anames[ axis ] = astStore( NULL, nam, strlen( nam ) + 1 ); + anames[ axis + 1 ] = astStore( NULL, nam, strlen( nam ) + 1 ); + nam = astFree( (void *) nam ); + } + } + +/* Increment the axis index. */ + axis += 2; + +/* If the supplied Frame has no sky frame, but we found a Position2D, then + report a warning and ignore the Position2D. */ + } else if( scan->count[ 0 ] ) { + sprintf( buff, "contains a <%s> which is not being used.", + astXmlGetName( scan->el[ 0 ][ 0 ] ) ); + Report( this, elem, WARNING, buff, status ); + } + afrm = astAnnul( afrm ); + +/* Indicate we do not yet have an epoch to use. */ + epoch = AST__BAD; + +/* Check to see if the Frame contains a time frame. It will be the next + axis if it does. */ + afrm = astPickAxes( frm, 1, &axis, NULL ); + dom = astGetDomain( afrm ); + if( dom && !strcmp( dom, "TIME" ) ){ + astPrimaryFrame( frm, axis, &gfrm, &junk ); + +/* Report an error if it is not an AST TimeFrame. */ + if( !astIsATimeFrame( gfrm ) && astOK ) { + astError( AST__INTER, "AstroCoordAreaReader(XmlChan): %s " + "supplied where TimeFrame expected (internal " + "AST programming error).", status, astGetClass( pfrm ) ); + } else { + tfrm = (AstTimeFrame *) gfrm; + } + +/* Use any Time element to create a Region describing the uncertainty in the + time axis of the Frame. Also create a KeyMap holding Regions describing + any ancillary information stored in the Time element. */ + if( scan->count[ 1 ] ) { + uncs[ 1 ] = TimeReader( this, scan->el[ 1 ][ 0 ], tfrm, &epoch, + &tanc, status ); + if( uncs[ 1 ] ) unc = 1; + +/* If ancillary information was returned, extract the Name element, and + store it in the "names" array. */ + if( tanc && astMapGet0C( tanc, AST__STCNAME, &nam ) ) { + anames[ axis ] = astStore( NULL, nam, strlen( nam ) + 1 ); + nam = astFree( (void *) nam ); + } + } + +/* Increment the index of the next axis to use. */ + axis++; + +/* If the supplied Frame has no time frame, but we found a Time element, then + report a warning and ignore the Time element. */ + } else if( scan->count[ 1 ] ) { + Report( this, elem, WARNING, "contains a